summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/Makefile5
-rw-r--r--sys/alpha/include/console.h224
-rw-r--r--sys/alpha/include/pc/display.h45
-rw-r--r--sys/alpha/include/pc/msdos.h65
-rw-r--r--sys/amd64/Makefile25
-rw-r--r--sys/amd64/amd64/autoconf.c209
-rw-r--r--sys/amd64/amd64/cpu_switch.S458
-rw-r--r--sys/amd64/amd64/db_disasm.c1375
-rw-r--r--sys/amd64/amd64/db_interface.c240
-rw-r--r--sys/amd64/amd64/db_trace.c340
-rw-r--r--sys/amd64/amd64/exception.S275
-rw-r--r--sys/amd64/amd64/exception.s275
-rw-r--r--sys/amd64/amd64/fpu.c554
-rw-r--r--sys/amd64/amd64/genassym.c192
-rw-r--r--sys/amd64/amd64/locore.S518
-rw-r--r--sys/amd64/amd64/locore.s518
-rw-r--r--sys/amd64/amd64/machdep.c1556
-rw-r--r--sys/amd64/amd64/mem.c259
-rw-r--r--sys/amd64/amd64/pmap.c1944
-rw-r--r--sys/amd64/amd64/support.S1221
-rw-r--r--sys/amd64/amd64/support.s1221
-rw-r--r--sys/amd64/amd64/swtch.s458
-rw-r--r--sys/amd64/amd64/sys_machdep.c328
-rw-r--r--sys/amd64/amd64/trap.c692
-rw-r--r--sys/amd64/amd64/tsc.c442
-rw-r--r--sys/amd64/amd64/vm_machdep.c1246
-rw-r--r--sys/amd64/include/asmacros.h49
-rw-r--r--sys/amd64/include/cpu.h118
-rw-r--r--sys/amd64/include/cpufunc.h108
-rw-r--r--sys/amd64/include/cputypes.h53
-rw-r--r--sys/amd64/include/db_machdep.h120
-rw-r--r--sys/amd64/include/exec.h128
-rw-r--r--sys/amd64/include/float.h72
-rw-r--r--sys/amd64/include/floatingpoint.h109
-rw-r--r--sys/amd64/include/fpu.h141
-rw-r--r--sys/amd64/include/frame.h140
-rw-r--r--sys/amd64/include/npx.h141
-rw-r--r--sys/amd64/include/pc/display.h45
-rw-r--r--sys/amd64/include/pcb.h93
-rw-r--r--sys/amd64/include/pmap.h217
-rw-r--r--sys/amd64/include/proc.h50
-rw-r--r--sys/amd64/include/profile.h56
-rw-r--r--sys/amd64/include/psl.h65
-rw-r--r--sys/amd64/include/ptrace.h40
-rw-r--r--sys/amd64/include/reg.h106
-rw-r--r--sys/amd64/include/reloc.h44
-rw-r--r--sys/amd64/include/segments.h235
-rw-r--r--sys/amd64/include/signal.h75
-rw-r--r--sys/amd64/include/specialreg.h64
-rw-r--r--sys/amd64/include/sysarch.h24
-rw-r--r--sys/amd64/include/trap.h101
-rw-r--r--sys/amd64/include/tss.h82
-rw-r--r--sys/amd64/include/varargs.h62
-rw-r--r--sys/amd64/include/vmparam.h263
-rw-r--r--sys/amd64/isa/clock.c442
-rw-r--r--sys/amd64/isa/icu.h97
-rw-r--r--sys/amd64/isa/isa.c671
-rw-r--r--sys/amd64/isa/isa.h181
-rw-r--r--sys/amd64/isa/npx.c554
-rw-r--r--sys/amd64/isa/timerreg.h93
-rw-r--r--sys/amd64/isa/vector.S360
-rw-r--r--sys/amd64/isa/vector.s360
-rw-r--r--sys/conf/Makefile.i386190
-rw-r--r--sys/conf/Makefile.powerpc190
-rw-r--r--sys/conf/NOTES197
-rw-r--r--sys/conf/defines15
-rw-r--r--sys/conf/files302
-rw-r--r--sys/conf/files.i386117
-rw-r--r--sys/conf/files.newconf274
-rw-r--r--sys/conf/newvers.sh48
-rw-r--r--sys/conf/nfsswapvmunix.c152
-rw-r--r--sys/conf/param.c146
-rw-r--r--sys/conf/systags.sh72
-rw-r--r--sys/ddb/db_access.c103
-rw-r--r--sys/ddb/db_access.h47
-rw-r--r--sys/ddb/db_aout.c407
-rw-r--r--sys/ddb/db_break.c353
-rw-r--r--sys/ddb/db_break.h64
-rw-r--r--sys/ddb/db_command.c485
-rw-r--r--sys/ddb/db_command.h57
-rw-r--r--sys/ddb/db_examine.c340
-rw-r--r--sys/ddb/db_expr.c226
-rw-r--r--sys/ddb/db_input.c256
-rw-r--r--sys/ddb/db_lex.c279
-rw-r--r--sys/ddb/db_lex.h77
-rw-r--r--sys/ddb/db_output.c370
-rw-r--r--sys/ddb/db_output.h44
-rw-r--r--sys/ddb/db_print.c70
-rw-r--r--sys/ddb/db_run.c391
-rw-r--r--sys/ddb/db_sym.c333
-rw-r--r--sys/ddb/db_sym.h102
-rw-r--r--sys/ddb/db_trap.c79
-rw-r--r--sys/ddb/db_variables.c165
-rw-r--r--sys/ddb/db_variables.h57
-rw-r--r--sys/ddb/db_watch.c270
-rw-r--r--sys/ddb/db_watch.h60
-rw-r--r--sys/ddb/db_write_cmd.c99
-rw-r--r--sys/ddb/ddb.h110
-rw-r--r--sys/dev/ed/if_ed.c2487
-rw-r--r--sys/dev/ed/if_edreg.h962
-rw-r--r--sys/dev/ep/if_ep.c993
-rw-r--r--sys/dev/ep/if_epreg.h295
-rw-r--r--sys/dev/fdc/fdc.c1255
-rw-r--r--sys/dev/fdc/fdcreg.h65
-rw-r--r--sys/dev/ic/i8237.h11
-rw-r--r--sys/dev/ic/i82586.h325
-rw-r--r--sys/dev/ic/nec765.h72
-rw-r--r--sys/dev/ic/ns16550.h51
-rw-r--r--sys/dev/ie/if_ie.c1801
-rw-r--r--sys/dev/ie/if_iereg.h24
-rw-r--r--sys/dev/kbd/kbdtables.h859
-rw-r--r--sys/dev/mcd/mcd.c1335
-rw-r--r--sys/dev/mcd/mcdreg.h159
-rw-r--r--sys/dev/mse/mse.c499
-rw-r--r--sys/dev/ppbus/lptio.h24
-rw-r--r--sys/dev/sio/sio.c1920
-rw-r--r--sys/dev/sio/sioreg.h114
-rw-r--r--sys/dev/speaker/speaker.h30
-rw-r--r--sys/dev/speaker/spkr.c541
-rw-r--r--sys/dev/syscons/syscons.c2660
-rw-r--r--sys/fs/cd9660/TODO77
-rw-r--r--sys/fs/cd9660/TODO.hibler22
-rw-r--r--sys/fs/cd9660/cd9660_bmap.c102
-rw-r--r--sys/fs/cd9660/cd9660_lookup.c467
-rw-r--r--sys/fs/cd9660/cd9660_node.c658
-rw-r--r--sys/fs/cd9660/cd9660_node.h143
-rw-r--r--sys/fs/cd9660/cd9660_rrip.c685
-rw-r--r--sys/fs/cd9660/cd9660_rrip.h146
-rw-r--r--sys/fs/cd9660/cd9660_util.c236
-rw-r--r--sys/fs/cd9660/cd9660_vfsops.c688
-rw-r--r--sys/fs/cd9660/cd9660_vnops.c1041
-rw-r--r--sys/fs/cd9660/iso.h256
-rw-r--r--sys/fs/cd9660/iso_rrip.h83
-rw-r--r--sys/fs/deadfs/dead_vnops.c368
-rw-r--r--sys/fs/fdescfs/fdesc.h82
-rw-r--r--sys/fs/fdescfs/fdesc_vfsops.c288
-rw-r--r--sys/fs/fdescfs/fdesc_vnops.c977
-rw-r--r--sys/fs/fifofs/fifo.h85
-rw-r--r--sys/fs/fifofs/fifo_vnops.c512
-rw-r--r--sys/fs/nullfs/null.h75
-rw-r--r--sys/fs/nullfs/null_subr.c295
-rw-r--r--sys/fs/nullfs/null_vfsops.c366
-rw-r--r--sys/fs/nullfs/null_vnops.c462
-rw-r--r--sys/fs/portalfs/portal.h72
-rw-r--r--sys/fs/portalfs/portal_vfsops.c313
-rw-r--r--sys/fs/portalfs/portal_vnops.c708
-rw-r--r--sys/fs/procfs/README113
-rw-r--r--sys/fs/procfs/procfs.h186
-rw-r--r--sys/fs/procfs/procfs_ctl.c302
-rw-r--r--sys/fs/procfs/procfs_fpregs.c87
-rw-r--r--sys/fs/procfs/procfs_mem.c302
-rw-r--r--sys/fs/procfs/procfs_note.c73
-rw-r--r--sys/fs/procfs/procfs_regs.c87
-rw-r--r--sys/fs/procfs/procfs_status.c145
-rw-r--r--sys/fs/procfs/procfs_subr.c314
-rw-r--r--sys/fs/procfs/procfs_vfsops.c254
-rw-r--r--sys/fs/procfs/procfs_vnops.c830
-rw-r--r--sys/fs/specfs/spec_vnops.c705
-rw-r--r--sys/fs/umapfs/umap.h92
-rw-r--r--sys/fs/umapfs/umap_subr.c399
-rw-r--r--sys/fs/umapfs/umap_vfsops.c407
-rw-r--r--sys/fs/umapfs/umap_vnops.c488
-rw-r--r--sys/fs/unionfs/union.h117
-rw-r--r--sys/fs/unionfs/union_subr.c743
-rw-r--r--sys/fs/unionfs/union_vfsops.c550
-rw-r--r--sys/fs/unionfs/union_vnops.c1495
-rw-r--r--sys/gnu/ext2fs/ext2_bmap.c294
-rw-r--r--sys/gnu/ext2fs/ext2_ihash.c154
-rw-r--r--sys/gnu/ext2fs/ext2_mount.h83
-rw-r--r--sys/gnu/ext2fs/inode.h162
-rw-r--r--sys/gnu/fs/ext2fs/ext2_bmap.c294
-rw-r--r--sys/gnu/fs/ext2fs/ext2_mount.h83
-rw-r--r--sys/gnu/fs/ext2fs/inode.h162
-rw-r--r--sys/gnu/i386/fpemul/Changelog36
-rw-r--r--sys/gnu/i386/fpemul/README277
-rw-r--r--sys/gnu/i386/fpemul/bde_trapinfo.mail35
-rw-r--r--sys/gnu/i386/fpemul/control_w.h95
-rw-r--r--sys/gnu/i386/fpemul/div_small.s101
-rw-r--r--sys/gnu/i386/fpemul/errors.c612
-rw-r--r--sys/gnu/i386/fpemul/exception.h102
-rw-r--r--sys/gnu/i386/fpemul/fpu_arith.c235
-rw-r--r--sys/gnu/i386/fpemul/fpu_asm.h82
-rw-r--r--sys/gnu/i386/fpemul/fpu_aux.c233
-rw-r--r--sys/gnu/i386/fpemul/fpu_emu.h188
-rw-r--r--sys/gnu/i386/fpemul/fpu_entry.c483
-rw-r--r--sys/gnu/i386/fpemul/fpu_etc.c175
-rw-r--r--sys/gnu/i386/fpemul/fpu_proto.h115
-rw-r--r--sys/gnu/i386/fpemul/fpu_system.h97
-rw-r--r--sys/gnu/i386/fpemul/fpu_trig.c1367
-rw-r--r--sys/gnu/i386/fpemul/get_address.c203
-rw-r--r--sys/gnu/i386/fpemul/load_store.c269
-rw-r--r--sys/gnu/i386/fpemul/math_emu.h47
-rw-r--r--sys/gnu/i386/fpemul/poly_2xm1.c141
-rw-r--r--sys/gnu/i386/fpemul/poly_atan.c252
-rw-r--r--sys/gnu/i386/fpemul/poly_div.s144
-rw-r--r--sys/gnu/i386/fpemul/poly_l2.c318
-rw-r--r--sys/gnu/i386/fpemul/poly_mul64.s124
-rw-r--r--sys/gnu/i386/fpemul/poly_sin.c192
-rw-r--r--sys/gnu/i386/fpemul/poly_tan.c229
-rw-r--r--sys/gnu/i386/fpemul/polynomial.s192
-rw-r--r--sys/gnu/i386/fpemul/reg_add_sub.c303
-rw-r--r--sys/gnu/i386/fpemul/reg_compare.c384
-rw-r--r--sys/gnu/i386/fpemul/reg_constant.c175
-rw-r--r--sys/gnu/i386/fpemul/reg_constant.h82
-rw-r--r--sys/gnu/i386/fpemul/reg_div.s295
-rw-r--r--sys/gnu/i386/fpemul/reg_ld_str.c1387
-rw-r--r--sys/gnu/i386/fpemul/reg_mul.c162
-rw-r--r--sys/gnu/i386/fpemul/reg_norm.s182
-rw-r--r--sys/gnu/i386/fpemul/reg_round.s653
-rw-r--r--sys/gnu/i386/fpemul/reg_u_add.s244
-rw-r--r--sys/gnu/i386/fpemul/reg_u_div.s506
-rw-r--r--sys/gnu/i386/fpemul/reg_u_mul.s199
-rw-r--r--sys/gnu/i386/fpemul/reg_u_sub.s361
-rw-r--r--sys/gnu/i386/fpemul/status_w.h106
-rw-r--r--sys/gnu/i386/fpemul/version.h61
-rw-r--r--sys/gnu/i386/fpemul/wm_shrx.s261
-rw-r--r--sys/gnu/i386/fpemul/wm_sqrt.s496
-rw-r--r--sys/i386/Makefile25
-rw-r--r--sys/i386/boot/Makefile106
-rw-r--r--sys/i386/boot/README.386BSD151
-rw-r--r--sys/i386/boot/README.MACH210
-rw-r--r--sys/i386/boot/asm.S260
-rw-r--r--sys/i386/boot/asm.h144
-rw-r--r--sys/i386/boot/asm.s270
-rw-r--r--sys/i386/boot/bios.S329
-rw-r--r--sys/i386/boot/bios.s326
-rw-r--r--sys/i386/boot/biosboot/Makefile106
-rw-r--r--sys/i386/boot/biosboot/README.386BSD151
-rw-r--r--sys/i386/boot/biosboot/README.MACH210
-rw-r--r--sys/i386/boot/biosboot/asm.S260
-rw-r--r--sys/i386/boot/biosboot/asm.h144
-rw-r--r--sys/i386/boot/biosboot/bios.S329
-rw-r--r--sys/i386/boot/biosboot/boot.c308
-rw-r--r--sys/i386/boot/biosboot/boot.h40
-rw-r--r--sys/i386/boot/biosboot/boot2.S177
-rw-r--r--sys/i386/boot/biosboot/disk.c281
-rw-r--r--sys/i386/boot/biosboot/io.c214
-rw-r--r--sys/i386/boot/biosboot/start.S292
-rw-r--r--sys/i386/boot/biosboot/sys.c232
-rw-r--r--sys/i386/boot/biosboot/table.c125
-rw-r--r--sys/i386/boot/boot.c308
-rw-r--r--sys/i386/boot/boot.h40
-rw-r--r--sys/i386/boot/boot.sed3
-rw-r--r--sys/i386/boot/boot2.S177
-rw-r--r--sys/i386/boot/boot2.s73
-rw-r--r--sys/i386/boot/disk.c281
-rw-r--r--sys/i386/boot/io.c214
-rw-r--r--sys/i386/boot/rmaouthdr6
-rw-r--r--sys/i386/boot/start.S292
-rw-r--r--sys/i386/boot/start.s323
-rw-r--r--sys/i386/boot/sys.c232
-rw-r--r--sys/i386/boot/table.c125
-rw-r--r--sys/i386/conf/GENERICAH88
-rw-r--r--sys/i386/conf/GENERICBT88
-rw-r--r--sys/i386/conf/LINT197
-rw-r--r--sys/i386/conf/Makefile.i386190
-rw-r--r--sys/i386/conf/NOTES197
-rw-r--r--sys/i386/conf/SYSCONS87
-rw-r--r--sys/i386/conf/devices.i38613
-rw-r--r--sys/i386/conf/files.i386117
-rw-r--r--sys/i386/eisa/aha1742.c1244
-rw-r--r--sys/i386/i386/autoconf.c209
-rw-r--r--sys/i386/i386/conf.c756
-rw-r--r--sys/i386/i386/cons.c244
-rw-r--r--sys/i386/i386/cons.h86
-rw-r--r--sys/i386/i386/db_disasm.c1375
-rw-r--r--sys/i386/i386/db_interface.c240
-rw-r--r--sys/i386/i386/db_trace.c340
-rw-r--r--sys/i386/i386/dkbad.c69
-rw-r--r--sys/i386/i386/exception.s275
-rw-r--r--sys/i386/i386/genassym.c192
-rw-r--r--sys/i386/i386/in_cksum.c237
-rw-r--r--sys/i386/i386/locore.s518
-rw-r--r--sys/i386/i386/machdep.c1556
-rw-r--r--sys/i386/i386/math_emu.h156
-rw-r--r--sys/i386/i386/math_emulate.c1476
-rw-r--r--sys/i386/i386/mem.c259
-rw-r--r--sys/i386/i386/microtime.s137
-rw-r--r--sys/i386/i386/ns_cksum.c207
-rw-r--r--sys/i386/i386/pmap.c1944
-rw-r--r--sys/i386/i386/procfs_machdep.c156
-rw-r--r--sys/i386/i386/support.s1221
-rw-r--r--sys/i386/i386/swapgeneric.c169
-rw-r--r--sys/i386/i386/swtch.s458
-rw-r--r--sys/i386/i386/symbols.raw84
-rw-r--r--sys/i386/i386/sys_machdep.c328
-rw-r--r--sys/i386/i386/trap.c692
-rw-r--r--sys/i386/i386/tsc.c442
-rw-r--r--sys/i386/i386/vm_machdep.c1246
-rw-r--r--sys/i386/include/_limits.h89
-rw-r--r--sys/i386/include/ansi.h72
-rw-r--r--sys/i386/include/asmacros.h49
-rw-r--r--sys/i386/include/cons.h6
-rw-r--r--sys/i386/include/console.h224
-rw-r--r--sys/i386/include/cpu.h118
-rw-r--r--sys/i386/include/cpufunc.h108
-rw-r--r--sys/i386/include/cputypes.h53
-rw-r--r--sys/i386/include/db_machdep.h120
-rw-r--r--sys/i386/include/dkio.h53
-rw-r--r--sys/i386/include/eflags.h54
-rw-r--r--sys/i386/include/endian.h120
-rw-r--r--sys/i386/include/exec.h128
-rw-r--r--sys/i386/include/float.h72
-rw-r--r--sys/i386/include/floatingpoint.h109
-rw-r--r--sys/i386/include/frame.h140
-rw-r--r--sys/i386/include/ioctl_fd.h98
-rw-r--r--sys/i386/include/ioctl_pc.h797
-rw-r--r--sys/i386/include/ipl.h7
-rw-r--r--sys/i386/include/limits.h89
-rw-r--r--sys/i386/include/lpt.h24
-rw-r--r--sys/i386/include/mtpr.h4
-rw-r--r--sys/i386/include/npx.h141
-rw-r--r--sys/i386/include/param.h168
-rw-r--r--sys/i386/include/pc/display.h45
-rw-r--r--sys/i386/include/pc/msdos.h65
-rw-r--r--sys/i386/include/pcaudioio.h75
-rw-r--r--sys/i386/include/pcb.h93
-rw-r--r--sys/i386/include/pio.h48
-rw-r--r--sys/i386/include/pmap.h217
-rw-r--r--sys/i386/include/proc.h50
-rw-r--r--sys/i386/include/profile.h56
-rw-r--r--sys/i386/include/psl.h65
-rw-r--r--sys/i386/include/pte.h130
-rw-r--r--sys/i386/include/ptrace.h40
-rw-r--r--sys/i386/include/reg.h106
-rw-r--r--sys/i386/include/reloc.h44
-rw-r--r--sys/i386/include/segments.h235
-rw-r--r--sys/i386/include/signal.h75
-rw-r--r--sys/i386/include/soundcard.h763
-rw-r--r--sys/i386/include/speaker.h30
-rw-r--r--sys/i386/include/specialreg.h64
-rw-r--r--sys/i386/include/spl.h107
-rw-r--r--sys/i386/include/stdarg.h58
-rw-r--r--sys/i386/include/sysarch.h24
-rw-r--r--sys/i386/include/trap.h101
-rw-r--r--sys/i386/include/tss.h82
-rw-r--r--sys/i386/include/types.h65
-rw-r--r--sys/i386/include/ultrasound.h121
-rw-r--r--sys/i386/include/varargs.h62
-rw-r--r--sys/i386/include/vmparam.h263
-rw-r--r--sys/i386/isa/aha1542.c1479
-rw-r--r--sys/i386/isa/aha1742.c1244
-rw-r--r--sys/i386/isa/bt742a.c1533
-rw-r--r--sys/i386/isa/clock.c442
-rw-r--r--sys/i386/isa/fd.c1255
-rw-r--r--sys/i386/isa/fdc.h75
-rw-r--r--sys/i386/isa/fdreg.h65
-rw-r--r--sys/i386/isa/ft.c2129
-rw-r--r--sys/i386/isa/ftreg.h81
-rw-r--r--sys/i386/isa/ic/i8042.h27
-rw-r--r--sys/i386/isa/ic/i82365.h190
-rw-r--r--sys/i386/isa/ic/i8237.h11
-rw-r--r--sys/i386/isa/ic/i82586.h325
-rw-r--r--sys/i386/isa/ic/nec765.h72
-rw-r--r--sys/i386/isa/ic/ns16450.h50
-rw-r--r--sys/i386/isa/ic/ns16550.h51
-rw-r--r--sys/i386/isa/icu.h97
-rw-r--r--sys/i386/isa/icu.s322
-rw-r--r--sys/i386/isa/if_ed.c2487
-rw-r--r--sys/i386/isa/if_edreg.h962
-rw-r--r--sys/i386/isa/if_el.c800
-rw-r--r--sys/i386/isa/if_elreg.h76
-rw-r--r--sys/i386/isa/if_ep.c993
-rw-r--r--sys/i386/isa/if_epreg.h295
-rw-r--r--sys/i386/isa/if_ie.c1801
-rw-r--r--sys/i386/isa/if_iereg.h24
-rw-r--r--sys/i386/isa/if_is.c1148
-rw-r--r--sys/i386/isa/if_isreg.h129
-rw-r--r--sys/i386/isa/if_ze.c1951
-rw-r--r--sys/i386/isa/if_zereg.h859
-rw-r--r--sys/i386/isa/isa.c671
-rw-r--r--sys/i386/isa/isa.h181
-rw-r--r--sys/i386/isa/isa_device.h82
-rw-r--r--sys/i386/isa/iso8859.font1243
-rw-r--r--sys/i386/isa/kbd.h56
-rw-r--r--sys/i386/isa/kbdtables.h859
-rw-r--r--sys/i386/isa/lpt.c653
-rw-r--r--sys/i386/isa/lptreg.h33
-rw-r--r--sys/i386/isa/mcd.c1335
-rw-r--r--sys/i386/isa/mcdreg.h159
-rw-r--r--sys/i386/isa/mse.c499
-rw-r--r--sys/i386/isa/npx.c554
-rw-r--r--sys/i386/isa/pcaudio.c403
-rw-r--r--sys/i386/isa/rtc.h91
-rw-r--r--sys/i386/isa/sio.c1920
-rw-r--r--sys/i386/isa/sioreg.h114
-rw-r--r--sys/i386/isa/sound/CHANGELOG75
-rw-r--r--sys/i386/isa/sound/COPYING25
-rw-r--r--sys/i386/isa/sound/HOWTO_MIDI51
-rw-r--r--sys/i386/isa/sound/README17
-rw-r--r--sys/i386/isa/sound/RELNOTES38
-rw-r--r--sys/i386/isa/sound/RELNOTES.Linux255
-rw-r--r--sys/i386/isa/sound/adlib_card.c51
-rw-r--r--sys/i386/isa/sound/audio.c356
-rw-r--r--sys/i386/isa/sound/dev_table.c217
-rw-r--r--sys/i386/isa/sound/dev_table.h273
-rw-r--r--sys/i386/isa/sound/dmabuf.c902
-rw-r--r--sys/i386/isa/sound/finetune.h49
-rw-r--r--sys/i386/isa/sound/gus_card.c142
-rw-r--r--sys/i386/isa/sound/gus_hw.h50
-rw-r--r--sys/i386/isa/sound/gus_linearvol.h18
-rw-r--r--sys/i386/isa/sound/gus_midi.c283
-rw-r--r--sys/i386/isa/sound/gus_vol.c147
-rw-r--r--sys/i386/isa/sound/gus_wave.c3575
-rw-r--r--sys/i386/isa/sound/gustest/Makefile16
-rw-r--r--sys/i386/isa/sound/gustest/Readme67
-rw-r--r--sys/i386/isa/sound/gustest/gmidi.h131
-rw-r--r--sys/i386/isa/sound/gustest/gmod.c1588
-rw-r--r--sys/i386/isa/sound/gustest/gpatinfo.c175
-rw-r--r--sys/i386/isa/sound/gustest/gusload.c349
-rw-r--r--sys/i386/isa/sound/gustest/midithru.c325
-rw-r--r--sys/i386/isa/sound/gustest/pmtest.c409
-rw-r--r--sys/i386/isa/sound/ics2101.c265
-rw-r--r--sys/i386/isa/sound/local.h15
-rw-r--r--sys/i386/isa/sound/midi.c204
-rw-r--r--sys/i386/isa/sound/midibuf.c123
-rw-r--r--sys/i386/isa/sound/mpu401.c282
-rw-r--r--sys/i386/isa/sound/opl3.c960
-rw-r--r--sys/i386/isa/sound/opl3.h260
-rw-r--r--sys/i386/isa/sound/os.h319
-rw-r--r--sys/i386/isa/sound/pas.h250
-rw-r--r--sys/i386/isa/sound/pas2_card.c383
-rw-r--r--sys/i386/isa/sound/pas2_midi.c295
-rw-r--r--sys/i386/isa/sound/pas2_mixer.c492
-rw-r--r--sys/i386/isa/sound/pas2_pcm.c429
-rw-r--r--sys/i386/isa/sound/patmgr.c262
-rw-r--r--sys/i386/isa/sound/pro_midi.c187
-rw-r--r--sys/i386/isa/sound/sb.h28
-rw-r--r--sys/i386/isa/sound/sb16_dsp.c627
-rw-r--r--sys/i386/isa/sound/sb16_midi.c287
-rw-r--r--sys/i386/isa/sound/sb_card.c52
-rw-r--r--sys/i386/isa/sound/sb_dsp.c785
-rw-r--r--sys/i386/isa/sound/sb_midi.c224
-rw-r--r--sys/i386/isa/sound/sb_mixer.c422
-rw-r--r--sys/i386/isa/sound/sb_mixer.h212
-rw-r--r--sys/i386/isa/sound/sequencer.c1167
-rw-r--r--sys/i386/isa/sound/sound_calls.h208
-rw-r--r--sys/i386/isa/sound/sound_config.h241
-rw-r--r--sys/i386/isa/sound/sound_switch.c445
-rw-r--r--sys/i386/isa/sound/soundcard.c394
-rw-r--r--sys/i386/isa/sound/tuning.h29
-rw-r--r--sys/i386/isa/sound/ulaw.h69
-rw-r--r--sys/i386/isa/spkr.c541
-rw-r--r--sys/i386/isa/syscons.c2660
-rw-r--r--sys/i386/isa/timerreg.h93
-rw-r--r--sys/i386/isa/ultra14f.c1155
-rw-r--r--sys/i386/isa/vector.s360
-rw-r--r--sys/i386/isa/wd.c1827
-rw-r--r--sys/i386/isa/wdreg.h144
-rw-r--r--sys/i386/isa/wt.c902
-rw-r--r--sys/i386/isa/wtreg.h125
-rw-r--r--sys/isa/atrtc.c442
-rw-r--r--sys/isa/fd.c1255
-rw-r--r--sys/isa/fdc.h75
-rw-r--r--sys/isa/fdreg.h65
-rw-r--r--sys/isa/ic/nec765.h72
-rw-r--r--sys/isa/ic/ns16550.h51
-rw-r--r--sys/isa/kbdtables.h859
-rw-r--r--sys/isa/rtc.h91
-rw-r--r--sys/isa/sio.c1920
-rw-r--r--sys/isa/sioreg.h114
-rw-r--r--sys/isa/syscons.c2660
-rw-r--r--sys/isa/timerreg.h93
-rw-r--r--sys/isofs/cd9660/TODO77
-rw-r--r--sys/isofs/cd9660/TODO.hibler22
-rw-r--r--sys/isofs/cd9660/cd9660_bmap.c102
-rw-r--r--sys/isofs/cd9660/cd9660_lookup.c467
-rw-r--r--sys/isofs/cd9660/cd9660_node.c658
-rw-r--r--sys/isofs/cd9660/cd9660_node.h143
-rw-r--r--sys/isofs/cd9660/cd9660_rrip.c685
-rw-r--r--sys/isofs/cd9660/cd9660_rrip.h146
-rw-r--r--sys/isofs/cd9660/cd9660_util.c236
-rw-r--r--sys/isofs/cd9660/cd9660_vfsops.c688
-rw-r--r--sys/isofs/cd9660/cd9660_vnops.c1041
-rw-r--r--sys/isofs/cd9660/iso.h256
-rw-r--r--sys/isofs/cd9660/iso_rrip.h83
-rw-r--r--sys/kern/Make.tags.inc18
-rw-r--r--sys/kern/Makefile50
-rw-r--r--sys/kern/imgact_aout.c190
-rw-r--r--sys/kern/imgact_shell.c145
-rw-r--r--sys/kern/init_main.c423
-rw-r--r--sys/kern/init_sysent.c484
-rw-r--r--sys/kern/kern_acct.c118
-rw-r--r--sys/kern/kern_clock.c529
-rw-r--r--sys/kern/kern_descrip.c959
-rw-r--r--sys/kern/kern_exec.c529
-rw-r--r--sys/kern/kern_exit.c495
-rw-r--r--sys/kern/kern_fork.c318
-rw-r--r--sys/kern/kern_ktrace.c478
-rw-r--r--sys/kern/kern_malloc.c383
-rw-r--r--sys/kern/kern_physio.c171
-rw-r--r--sys/kern/kern_proc.c410
-rw-r--r--sys/kern/kern_prot.c588
-rw-r--r--sys/kern/kern_resource.c487
-rw-r--r--sys/kern/kern_sig.c1220
-rw-r--r--sys/kern/kern_subr.c213
-rw-r--r--sys/kern/kern_synch.c667
-rw-r--r--sys/kern/kern_sysctl.c798
-rw-r--r--sys/kern/kern_tc.c529
-rw-r--r--sys/kern/kern_time.c430
-rw-r--r--sys/kern/kern_timeout.c529
-rw-r--r--sys/kern/kern_xxx.c268
-rw-r--r--sys/kern/makesyscalls.sh171
-rw-r--r--sys/kern/subr_autoconf.c342
-rw-r--r--sys/kern/subr_clist.c567
-rw-r--r--sys/kern/subr_disklabel.c461
-rw-r--r--sys/kern/subr_log.c238
-rw-r--r--sys/kern/subr_param.c146
-rw-r--r--sys/kern/subr_prf.c607
-rw-r--r--sys/kern/subr_prof.c257
-rw-r--r--sys/kern/subr_rlist.c290
-rw-r--r--sys/kern/subr_rmap.c81
-rw-r--r--sys/kern/subr_trap.c692
-rw-r--r--sys/kern/subr_xxx.c106
-rw-r--r--sys/kern/sys_generic.c684
-rw-r--r--sys/kern/sys_process.c76
-rw-r--r--sys/kern/sys_socket.c203
-rw-r--r--sys/kern/syscalls.c251
-rw-r--r--sys/kern/syscalls.master276
-rw-r--r--sys/kern/tty.c1940
-rw-r--r--sys/kern/tty_compat.c418
-rw-r--r--sys/kern/tty_conf.c132
-rw-r--r--sys/kern/tty_cons.c244
-rw-r--r--sys/kern/tty_pty.c704
-rw-r--r--sys/kern/tty_subr.c567
-rw-r--r--sys/kern/tty_tb.c366
-rw-r--r--sys/kern/tty_tty.c152
-rw-r--r--sys/kern/uipc_domain.c223
-rw-r--r--sys/kern/uipc_mbuf.c663
-rw-r--r--sys/kern/uipc_proto.c72
-rw-r--r--sys/kern/uipc_sockbuf.c787
-rw-r--r--sys/kern/uipc_socket.c1035
-rw-r--r--sys/kern/uipc_socket2.c787
-rw-r--r--sys/kern/uipc_syscalls.c1245
-rw-r--r--sys/kern/uipc_usrreq.c847
-rw-r--r--sys/kern/vfs_bio.c710
-rw-r--r--sys/kern/vfs_cache.c332
-rw-r--r--sys/kern/vfs_cluster.c747
-rw-r--r--sys/kern/vfs_conf.c260
-rw-r--r--sys/kern/vfs_export.c1351
-rw-r--r--sys/kern/vfs_extattr.c2155
-rw-r--r--sys/kern/vfs_init.c247
-rw-r--r--sys/kern/vfs_lookup.c506
-rw-r--r--sys/kern/vfs_mount.c260
-rw-r--r--sys/kern/vfs_subr.c1351
-rw-r--r--sys/kern/vfs_syscalls.c2155
-rw-r--r--sys/kern/vfs_vnops.c432
-rw-r--r--sys/kern/vnode_if.pl433
-rw-r--r--sys/kern/vnode_if.sh433
-rw-r--r--sys/kern/vnode_if.src296
-rw-r--r--sys/libkern/Makefile20
-rw-r--r--sys/libkern/adddi3.c60
-rw-r--r--sys/libkern/anddi3.c58
-rw-r--r--sys/libkern/ashldi3.c66
-rw-r--r--sys/libkern/ashrdi3.c75
-rw-r--r--sys/libkern/bcmp.c59
-rw-r--r--sys/libkern/cmpdi2.c59
-rw-r--r--sys/libkern/divdi3.c65
-rw-r--r--sys/libkern/ffs.c54
-rw-r--r--sys/libkern/fnmatch.c171
-rw-r--r--sys/libkern/iordi3.c58
-rw-r--r--sys/libkern/libkern.h98
-rw-r--r--sys/libkern/locc.c50
-rw-r--r--sys/libkern/lshldi3.c66
-rw-r--r--sys/libkern/lshrdi3.c65
-rw-r--r--sys/libkern/mcount.c178
-rw-r--r--sys/libkern/moddi3.c67
-rw-r--r--sys/libkern/muldi3.c246
-rw-r--r--sys/libkern/negdi2.c57
-rw-r--r--sys/libkern/notdi2.c58
-rw-r--r--sys/libkern/qdivrem.c279
-rw-r--r--sys/libkern/quad.h110
-rw-r--r--sys/libkern/random.c63
-rw-r--r--sys/libkern/rindex.c59
-rw-r--r--sys/libkern/scanc.c50
-rw-r--r--sys/libkern/skpc.c50
-rw-r--r--sys/libkern/strcat.c50
-rw-r--r--sys/libkern/strcmp.c55
-rw-r--r--sys/libkern/strcpy.c50
-rw-r--r--sys/libkern/strlen.c50
-rw-r--r--sys/libkern/strncpy.c68
-rw-r--r--sys/libkern/strsep.c80
-rw-r--r--sys/libkern/subdi3.c59
-rw-r--r--sys/libkern/ucmpdi2.c58
-rw-r--r--sys/libkern/udivdi3.c53
-rw-r--r--sys/libkern/umoddi3.c55
-rw-r--r--sys/libkern/xordi3.c58
-rw-r--r--sys/miscfs/deadfs/dead_vnops.c368
-rw-r--r--sys/miscfs/fdesc/fdesc.h82
-rw-r--r--sys/miscfs/fdesc/fdesc_vfsops.c288
-rw-r--r--sys/miscfs/fdesc/fdesc_vnops.c977
-rw-r--r--sys/miscfs/fifofs/fifo.h85
-rw-r--r--sys/miscfs/fifofs/fifo_vnops.c512
-rw-r--r--sys/miscfs/kernfs/kernfs.h56
-rw-r--r--sys/miscfs/kernfs/kernfs_vfsops.c341
-rw-r--r--sys/miscfs/kernfs/kernfs_vnops.c772
-rw-r--r--sys/miscfs/nullfs/null.h75
-rw-r--r--sys/miscfs/nullfs/null_subr.c295
-rw-r--r--sys/miscfs/nullfs/null_vfsops.c366
-rw-r--r--sys/miscfs/nullfs/null_vnops.c462
-rw-r--r--sys/miscfs/portal/portal.h72
-rw-r--r--sys/miscfs/portal/portal_vfsops.c313
-rw-r--r--sys/miscfs/portal/portal_vnops.c708
-rw-r--r--sys/miscfs/procfs/README113
-rw-r--r--sys/miscfs/procfs/procfs.h186
-rw-r--r--sys/miscfs/procfs/procfs_ctl.c302
-rw-r--r--sys/miscfs/procfs/procfs_fpregs.c87
-rw-r--r--sys/miscfs/procfs/procfs_mem.c302
-rw-r--r--sys/miscfs/procfs/procfs_note.c73
-rw-r--r--sys/miscfs/procfs/procfs_regs.c87
-rw-r--r--sys/miscfs/procfs/procfs_status.c145
-rw-r--r--sys/miscfs/procfs/procfs_subr.c314
-rw-r--r--sys/miscfs/procfs/procfs_vfsops.c254
-rw-r--r--sys/miscfs/procfs/procfs_vnops.c830
-rw-r--r--sys/miscfs/specfs/spec_vnops.c705
-rw-r--r--sys/miscfs/specfs/specdev.h127
-rw-r--r--sys/miscfs/umapfs/umap.h92
-rw-r--r--sys/miscfs/umapfs/umap_subr.c399
-rw-r--r--sys/miscfs/umapfs/umap_vfsops.c407
-rw-r--r--sys/miscfs/umapfs/umap_vnops.c488
-rw-r--r--sys/miscfs/union/README7
-rw-r--r--sys/miscfs/union/libc.opendir.c225
-rw-r--r--sys/miscfs/union/union.h117
-rw-r--r--sys/miscfs/union/union_subr.c743
-rw-r--r--sys/miscfs/union/union_vfsops.c550
-rw-r--r--sys/miscfs/union/union_vnops.c1495
-rw-r--r--sys/net/bpf.c1316
-rw-r--r--sys/net/bpf.h252
-rw-r--r--sys/net/bpf_compat.h50
-rw-r--r--sys/net/bpf_filter.c545
-rw-r--r--sys/net/bpfdesc.h100
-rw-r--r--sys/net/if.c670
-rw-r--r--sys/net/if.h363
-rw-r--r--sys/net/if_arp.h85
-rw-r--r--sys/net/if_dl.h80
-rw-r--r--sys/net/if_ethersubr.c675
-rw-r--r--sys/net/if_llc.h145
-rw-r--r--sys/net/if_loop.c249
-rw-r--r--sys/net/if_sl.c843
-rw-r--r--sys/net/if_slvar.h80
-rw-r--r--sys/net/if_types.h95
-rw-r--r--sys/net/netisr.h68
-rw-r--r--sys/net/radix.c757
-rw-r--r--sys/net/radix.h153
-rw-r--r--sys/net/raw_cb.c144
-rw-r--r--sys/net/raw_cb.h69
-rw-r--r--sys/net/raw_usrreq.c306
-rw-r--r--sys/net/route.c538
-rw-r--r--sys/net/route.h261
-rw-r--r--sys/net/rtsock.c833
-rw-r--r--sys/net/slcompress.c535
-rw-r--r--sys/net/slcompress.h157
-rw-r--r--sys/net/slip.h51
-rw-r--r--sys/netccitt/README.hdlc50
-rw-r--r--sys/netccitt/README.packet36
-rw-r--r--sys/netccitt/ccitt_proto.c93
-rw-r--r--sys/netccitt/dll.h83
-rw-r--r--sys/netccitt/hd_debug.c212
-rw-r--r--sys/netccitt/hd_input.c669
-rw-r--r--sys/netccitt/hd_output.c247
-rw-r--r--sys/netccitt/hd_subr.c391
-rw-r--r--sys/netccitt/hd_timer.c147
-rw-r--r--sys/netccitt/hd_var.h107
-rw-r--r--sys/netccitt/hdlc.h156
-rw-r--r--sys/netccitt/if_x25subr.c801
-rw-r--r--sys/netccitt/llc_input.c468
-rw-r--r--sys/netccitt/llc_output.c304
-rw-r--r--sys/netccitt/llc_subr.c2356
-rw-r--r--sys/netccitt/llc_timer.c180
-rw-r--r--sys/netccitt/llc_var.h659
-rw-r--r--sys/netccitt/pk.h207
-rw-r--r--sys/netccitt/pk_acct.c145
-rw-r--r--sys/netccitt/pk_debug.c140
-rw-r--r--sys/netccitt/pk_input.c1119
-rw-r--r--sys/netccitt/pk_llcsubr.c369
-rw-r--r--sys/netccitt/pk_output.c216
-rw-r--r--sys/netccitt/pk_subr.c1192
-rw-r--r--sys/netccitt/pk_timer.c126
-rw-r--r--sys/netccitt/pk_usrreq.c604
-rw-r--r--sys/netccitt/pk_var.h231
-rw-r--r--sys/netccitt/x25.h157
-rw-r--r--sys/netccitt/x25acct.h70
-rw-r--r--sys/netccitt/x25err.h64
-rw-r--r--sys/netinet/icmp_var.h68
-rw-r--r--sys/netinet/if_ether.c554
-rw-r--r--sys/netinet/if_ether.h224
-rw-r--r--sys/netinet/igmp.c314
-rw-r--r--sys/netinet/igmp.h59
-rw-r--r--sys/netinet/igmp_var.h85
-rw-r--r--sys/netinet/in.c628
-rw-r--r--sys/netinet/in.h238
-rw-r--r--sys/netinet/in_cksum.c149
-rw-r--r--sys/netinet/in_pcb.c497
-rw-r--r--sys/netinet/in_pcb.h88
-rw-r--r--sys/netinet/in_proto.c185
-rw-r--r--sys/netinet/in_systm.h56
-rw-r--r--sys/netinet/in_var.h200
-rw-r--r--sys/netinet/ip.h168
-rw-r--r--sys/netinet/ip_icmp.c591
-rw-r--r--sys/netinet/ip_icmp.h160
-rw-r--r--sys/netinet/ip_input.c1166
-rw-r--r--sys/netinet/ip_mroute.c835
-rw-r--r--sys/netinet/ip_mroute.h173
-rw-r--r--sys/netinet/ip_output.c1075
-rw-r--r--sys/netinet/ip_var.h188
-rw-r--r--sys/netinet/raw_ip.c389
-rw-r--r--sys/netinet/tcp.h98
-rw-r--r--sys/netinet/tcp_debug.c159
-rw-r--r--sys/netinet/tcp_debug.h59
-rw-r--r--sys/netinet/tcp_fsm.h85
-rw-r--r--sys/netinet/tcp_input.c1665
-rw-r--r--sys/netinet/tcp_output.c599
-rw-r--r--sys/netinet/tcp_reass.c1665
-rw-r--r--sys/netinet/tcp_seq.h62
-rw-r--r--sys/netinet/tcp_subr.c445
-rw-r--r--sys/netinet/tcp_timer.c312
-rw-r--r--sys/netinet/tcp_timer.h128
-rw-r--r--sys/netinet/tcp_timewait.c445
-rw-r--r--sys/netinet/tcp_usrreq.c527
-rw-r--r--sys/netinet/tcp_var.h278
-rw-r--r--sys/netinet/tcpip.h59
-rw-r--r--sys/netinet/udp.h45
-rw-r--r--sys/netinet/udp_usrreq.c642
-rw-r--r--sys/netinet/udp_var.h92
-rw-r--r--sys/netiso/argo_debug.h296
-rw-r--r--sys/netiso/clnl.h64
-rw-r--r--sys/netiso/clnp.h463
-rw-r--r--sys/netiso/clnp_debug.c260
-rw-r--r--sys/netiso/clnp_er.c375
-rw-r--r--sys/netiso/clnp_frag.c859
-rw-r--r--sys/netiso/clnp_input.c551
-rw-r--r--sys/netiso/clnp_options.c532
-rw-r--r--sys/netiso/clnp_output.c561
-rw-r--r--sys/netiso/clnp_raw.c352
-rw-r--r--sys/netiso/clnp_stat.h103
-rw-r--r--sys/netiso/clnp_subr.c658
-rw-r--r--sys/netiso/clnp_timer.c180
-rw-r--r--sys/netiso/cltp_usrreq.c405
-rw-r--r--sys/netiso/cltp_var.h54
-rw-r--r--sys/netiso/cons.h92
-rw-r--r--sys/netiso/cons_pcb.h193
-rw-r--r--sys/netiso/eonvar.h170
-rw-r--r--sys/netiso/esis.c1063
-rw-r--r--sys/netiso/esis.h135
-rw-r--r--sys/netiso/idrp_usrreq.c177
-rw-r--r--sys/netiso/if_cons.c960
-rw-r--r--sys/netiso/if_eon.c609
-rw-r--r--sys/netiso/iso.c919
-rw-r--r--sys/netiso/iso.h195
-rw-r--r--sys/netiso/iso_chksum.c360
-rw-r--r--sys/netiso/iso_errno.h274
-rw-r--r--sys/netiso/iso_pcb.c617
-rw-r--r--sys/netiso/iso_pcb.h113
-rw-r--r--sys/netiso/iso_proto.c197
-rw-r--r--sys/netiso/iso_snpac.c736
-rw-r--r--sys/netiso/iso_snpac.h112
-rw-r--r--sys/netiso/iso_var.h137
-rw-r--r--sys/netiso/tp.trans1342
-rw-r--r--sys/netiso/tp_astring.c74
-rw-r--r--sys/netiso/tp_clnp.h94
-rw-r--r--sys/netiso/tp_cons.c308
-rw-r--r--sys/netiso/tp_driver.c999
-rw-r--r--sys/netiso/tp_emit.c996
-rw-r--r--sys/netiso/tp_events.h84
-rw-r--r--sys/netiso/tp_inet.c688
-rw-r--r--sys/netiso/tp_input.c1624
-rw-r--r--sys/netiso/tp_ip.h91
-rw-r--r--sys/netiso/tp_iso.c693
-rw-r--r--sys/netiso/tp_meas.c127
-rw-r--r--sys/netiso/tp_meas.h94
-rw-r--r--sys/netiso/tp_output.c712
-rw-r--r--sys/netiso/tp_param.h367
-rw-r--r--sys/netiso/tp_pcb.c999
-rw-r--r--sys/netiso/tp_pcb.h356
-rw-r--r--sys/netiso/tp_seq.h124
-rw-r--r--sys/netiso/tp_stat.h283
-rw-r--r--sys/netiso/tp_states.h13
-rw-r--r--sys/netiso/tp_states.init75
-rw-r--r--sys/netiso/tp_subr.c947
-rw-r--r--sys/netiso/tp_subr2.c880
-rw-r--r--sys/netiso/tp_timer.c377
-rw-r--r--sys/netiso/tp_timer.h93
-rw-r--r--sys/netiso/tp_tpdu.h296
-rw-r--r--sys/netiso/tp_trace.c175
-rw-r--r--sys/netiso/tp_trace.h198
-rw-r--r--sys/netiso/tp_user.h162
-rw-r--r--sys/netiso/tp_usrreq.c756
-rw-r--r--sys/netiso/tuba_subr.c348
-rw-r--r--sys/netiso/tuba_table.c143
-rw-r--r--sys/netiso/tuba_table.h59
-rw-r--r--sys/netiso/tuba_usrreq.c312
-rw-r--r--sys/netiso/xebec/Makefile8
-rw-r--r--sys/netiso/xebec/debug.h22
-rw-r--r--sys/netiso/xebec/llparse.c366
-rw-r--r--sys/netiso/xebec/llparse.h145
-rw-r--r--sys/netiso/xebec/llscan.c430
-rw-r--r--sys/netiso/xebec/main.c410
-rw-r--r--sys/netiso/xebec/main.h32
-rw-r--r--sys/netiso/xebec/malloc.c136
-rw-r--r--sys/netiso/xebec/malloc.h4
-rw-r--r--sys/netiso/xebec/procs.c437
-rw-r--r--sys/netiso/xebec/procs.h5
-rw-r--r--sys/netiso/xebec/putdriver.c244
-rw-r--r--sys/netiso/xebec/sets.c472
-rw-r--r--sys/netiso/xebec/sets.h36
-rw-r--r--sys/netiso/xebec/test.trans64
-rw-r--r--sys/netiso/xebec/test_def.h13
-rw-r--r--sys/netiso/xebec/xebec.bnf315
-rw-r--r--sys/netiso/xebec/xebec.c451
-rw-r--r--sys/netiso/xebec/xebec.h88
-rw-r--r--sys/netns/idp.h46
-rw-r--r--sys/netns/idp_usrreq.c566
-rw-r--r--sys/netns/idp_var.h49
-rw-r--r--sys/netns/ns.c368
-rw-r--r--sys/netns/ns.h151
-rw-r--r--sys/netns/ns_cksum.c204
-rw-r--r--sys/netns/ns_error.c323
-rw-r--r--sys/netns/ns_error.h90
-rw-r--r--sys/netns/ns_if.h82
-rw-r--r--sys/netns/ns_input.c485
-rw-r--r--sys/netns/ns_ip.c440
-rw-r--r--sys/netns/ns_output.c160
-rw-r--r--sys/netns/ns_pcb.c363
-rw-r--r--sys/netns/ns_pcb.h80
-rw-r--r--sys/netns/ns_proto.c96
-rw-r--r--sys/netns/sp.h52
-rw-r--r--sys/netns/spidp.h62
-rw-r--r--sys/netns/spp_debug.c170
-rw-r--r--sys/netns/spp_debug.h59
-rw-r--r--sys/netns/spp_timer.h122
-rw-r--r--sys/netns/spp_usrreq.c1804
-rw-r--r--sys/netns/spp_var.h215
-rw-r--r--sys/nfs/nfs.h297
-rw-r--r--sys/nfs/nfs_bio.c803
-rw-r--r--sys/nfs/nfs_common.c1143
-rw-r--r--sys/nfs/nfs_common.h269
-rw-r--r--sys/nfs/nfs_node.c301
-rw-r--r--sys/nfs/nfs_nqlease.c1236
-rw-r--r--sys/nfs/nfs_serv.c1929
-rw-r--r--sys/nfs/nfs_socket.c2005
-rw-r--r--sys/nfs/nfs_srvcache.c350
-rw-r--r--sys/nfs/nfs_subs.c1143
-rw-r--r--sys/nfs/nfs_syscalls.c883
-rw-r--r--sys/nfs/nfs_vfsops.c740
-rw-r--r--sys/nfs/nfs_vnops.c2541
-rw-r--r--sys/nfs/nfsdiskless.h66
-rw-r--r--sys/nfs/nfsm_subs.h269
-rw-r--r--sys/nfs/nfsmount.h127
-rw-r--r--sys/nfs/nfsnode.h166
-rw-r--r--sys/nfs/nfsrtt.h96
-rw-r--r--sys/nfs/nfsrvcache.h84
-rw-r--r--sys/nfs/nfsv2.h260
-rw-r--r--sys/nfs/nqnfs.h198
-rw-r--r--sys/nfs/rpcv2.h88
-rw-r--r--sys/nfs/xdr_subs.h78
-rw-r--r--sys/nfsclient/nfs.h297
-rw-r--r--sys/nfsclient/nfs_bio.c803
-rw-r--r--sys/nfsclient/nfs_nfsiod.c883
-rw-r--r--sys/nfsclient/nfs_node.c301
-rw-r--r--sys/nfsclient/nfs_socket.c2005
-rw-r--r--sys/nfsclient/nfs_subs.c1143
-rw-r--r--sys/nfsclient/nfs_vfsops.c740
-rw-r--r--sys/nfsclient/nfs_vnops.c2541
-rw-r--r--sys/nfsclient/nfsargs.h297
-rw-r--r--sys/nfsclient/nfsdiskless.h66
-rw-r--r--sys/nfsclient/nfsm_subs.h269
-rw-r--r--sys/nfsclient/nfsmount.h127
-rw-r--r--sys/nfsclient/nfsnode.h166
-rw-r--r--sys/nfsclient/nfsstats.h297
-rw-r--r--sys/nfsserver/nfs.h297
-rw-r--r--sys/nfsserver/nfs_serv.c1929
-rw-r--r--sys/nfsserver/nfs_srvcache.c350
-rw-r--r--sys/nfsserver/nfs_srvsock.c2005
-rw-r--r--sys/nfsserver/nfs_srvsubs.c1143
-rw-r--r--sys/nfsserver/nfs_syscalls.c883
-rw-r--r--sys/nfsserver/nfsm_subs.h269
-rw-r--r--sys/nfsserver/nfsrvcache.h84
-rw-r--r--sys/nfsserver/nfsrvstats.h297
-rw-r--r--sys/powerpc/include/_limits.h89
-rw-r--r--sys/powerpc/include/limits.h89
-rw-r--r--sys/powerpc/include/ptrace.h40
-rw-r--r--sys/scsi/README196
-rw-r--r--sys/scsi/cd.c1317
-rw-r--r--sys/scsi/ch.c487
-rw-r--r--sys/scsi/scsi_all.h340
-rw-r--r--sys/scsi/scsi_base.c896
-rw-r--r--sys/scsi/scsi_cd.h229
-rw-r--r--sys/scsi/scsi_changer.h98
-rw-r--r--sys/scsi/scsi_debug.h53
-rw-r--r--sys/scsi/scsi_disk.h216
-rw-r--r--sys/scsi/scsi_generic.h63
-rw-r--r--sys/scsi/scsi_ioctl.c332
-rw-r--r--sys/scsi/scsi_tape.h204
-rw-r--r--sys/scsi/scsiconf.c699
-rw-r--r--sys/scsi/scsiconf.h249
-rw-r--r--sys/scsi/sd.c1072
-rw-r--r--sys/scsi/st.c1936
-rw-r--r--sys/scsi/su.c4
-rw-r--r--sys/scsi/uk.c158
-rw-r--r--sys/sys/_sigset.h194
-rw-r--r--sys/sys/acct.h75
-rw-r--r--sys/sys/bio.h213
-rw-r--r--sys/sys/bitstring.h143
-rw-r--r--sys/sys/buf.h213
-rw-r--r--sys/sys/callout.h51
-rw-r--r--sys/sys/cdefs.h122
-rw-r--r--sys/sys/cdio.h170
-rw-r--r--sys/sys/chio.h92
-rw-r--r--sys/sys/clist.h45
-rw-r--r--sys/sys/conf.h123
-rw-r--r--sys/sys/cons.h86
-rw-r--r--sys/sys/device.h143
-rw-r--r--sys/sys/dir.h61
-rw-r--r--sys/sys/dirent.h76
-rw-r--r--sys/sys/disk.h112
-rw-r--r--sys/sys/disklabel.h364
-rw-r--r--sys/sys/diskmbr.h364
-rw-r--r--sys/sys/diskpc98.h364
-rw-r--r--sys/sys/dkbad.h68
-rw-r--r--sys/sys/dkstat.h64
-rw-r--r--sys/sys/dmap.h60
-rw-r--r--sys/sys/domain.h64
-rw-r--r--sys/sys/errno.h163
-rw-r--r--sys/sys/exec.h71
-rw-r--r--sys/sys/fbio.h186
-rw-r--r--sys/sys/fcntl.h190
-rw-r--r--sys/sys/fdcio.h98
-rw-r--r--sys/sys/file.h76
-rw-r--r--sys/sys/filedesc.h99
-rw-r--r--sys/sys/filio.h55
-rw-r--r--sys/sys/gmon.h159
-rw-r--r--sys/sys/imgact.h65
-rw-r--r--sys/sys/ioccom.h64
-rw-r--r--sys/sys/ioctl.h84
-rw-r--r--sys/sys/ioctl_compat.h167
-rw-r--r--sys/sys/ipc.h79
-rw-r--r--sys/sys/kernel.h83
-rw-r--r--sys/sys/ktrace.h156
-rw-r--r--sys/sys/libkern.h98
-rw-r--r--sys/sys/linedisc.h123
-rw-r--r--sys/sys/link_aout.h289
-rw-r--r--sys/sys/link_elf.h289
-rw-r--r--sys/sys/malloc.h308
-rw-r--r--sys/sys/map.h82
-rw-r--r--sys/sys/mbuf.h402
-rw-r--r--sys/sys/mman.h89
-rw-r--r--sys/sys/mount.h418
-rw-r--r--sys/sys/msgbuf.h46
-rw-r--r--sys/sys/mtio.h151
-rw-r--r--sys/sys/namei.h188
-rw-r--r--sys/sys/nlist_aout.h85
-rw-r--r--sys/sys/param.h216
-rw-r--r--sys/sys/proc.h264
-rw-r--r--sys/sys/protosw.h210
-rw-r--r--sys/sys/ptrace.h67
-rw-r--r--sys/sys/queue.h245
-rw-r--r--sys/sys/reboot.h88
-rw-r--r--sys/sys/resource.h125
-rw-r--r--sys/sys/resourcevar.h90
-rw-r--r--sys/sys/rlist.h41
-rw-r--r--sys/sys/scsiio.h63
-rw-r--r--sys/sys/select.h56
-rw-r--r--sys/sys/selinfo.h56
-rw-r--r--sys/sys/signal.h194
-rw-r--r--sys/sys/signalvar.h167
-rw-r--r--sys/sys/socket.h339
-rw-r--r--sys/sys/socketvar.h207
-rw-r--r--sys/sys/sockio.h77
-rw-r--r--sys/sys/soundcard.h763
-rw-r--r--sys/sys/stat.h193
-rw-r--r--sys/sys/syscall.h190
-rw-r--r--sys/sys/sysctl.h344
-rw-r--r--sys/sys/syslimits.h54
-rw-r--r--sys/sys/syslog.h187
-rw-r--r--sys/sys/systm.h168
-rw-r--r--sys/sys/tablet.h94
-rw-r--r--sys/sys/termios.h279
-rw-r--r--sys/sys/time.h126
-rw-r--r--sys/sys/timeb.h47
-rw-r--r--sys/sys/times.h65
-rw-r--r--sys/sys/timetc.h126
-rw-r--r--sys/sys/tprintf.h41
-rw-r--r--sys/sys/trace.h116
-rw-r--r--sys/sys/tty.h217
-rw-r--r--sys/sys/ttychars.h63
-rw-r--r--sys/sys/ttycom.h136
-rw-r--r--sys/sys/ttydefaults.h96
-rw-r--r--sys/sys/ttydev.h60
-rw-r--r--sys/sys/types.h162
-rw-r--r--sys/sys/ucred.h59
-rw-r--r--sys/sys/uio.h83
-rw-r--r--sys/sys/un.h49
-rw-r--r--sys/sys/unistd.h124
-rw-r--r--sys/sys/unpcb.h73
-rw-r--r--sys/sys/user.h91
-rw-r--r--sys/sys/utsname.h68
-rw-r--r--sys/sys/vadvise.h49
-rw-r--r--sys/sys/vcmd.h43
-rw-r--r--sys/sys/vlimit.h49
-rw-r--r--sys/sys/vmmeter.h149
-rw-r--r--sys/sys/vnode.h397
-rw-r--r--sys/sys/vsio.h153
-rw-r--r--sys/sys/wait.h156
-rw-r--r--sys/tools/vnode_if.awk433
-rw-r--r--sys/ufs/ffs/ffs_alloc.c1483
-rw-r--r--sys/ufs/ffs/ffs_balloc.c283
-rw-r--r--sys/ufs/ffs/ffs_extern.h101
-rw-r--r--sys/ufs/ffs/ffs_inode.c489
-rw-r--r--sys/ufs/ffs/ffs_subr.c238
-rw-r--r--sys/ufs/ffs/ffs_tables.c136
-rw-r--r--sys/ufs/ffs/ffs_vfsops.c848
-rw-r--r--sys/ufs/ffs/ffs_vnops.c288
-rw-r--r--sys/ufs/ffs/fs.h489
-rw-r--r--sys/ufs/lfs/README139
-rw-r--r--sys/ufs/lfs/TODO116
-rw-r--r--sys/ufs/lfs/lfs.h353
-rw-r--r--sys/ufs/lfs/lfs_alloc.c251
-rw-r--r--sys/ufs/lfs/lfs_balloc.c136
-rw-r--r--sys/ufs/lfs/lfs_bio.c188
-rw-r--r--sys/ufs/lfs/lfs_cksum.c58
-rw-r--r--sys/ufs/lfs/lfs_debug.c137
-rw-r--r--sys/ufs/lfs/lfs_extern.h106
-rw-r--r--sys/ufs/lfs/lfs_inode.c359
-rw-r--r--sys/ufs/lfs/lfs_segment.c1112
-rw-r--r--sys/ufs/lfs/lfs_subr.c185
-rw-r--r--sys/ufs/lfs/lfs_syscalls.c563
-rw-r--r--sys/ufs/lfs/lfs_vfsops.c578
-rw-r--r--sys/ufs/lfs/lfs_vnops.c488
-rw-r--r--sys/ufs/mfs/mfs_extern.h60
-rw-r--r--sys/ufs/mfs/mfs_vfsops.c310
-rw-r--r--sys/ufs/mfs/mfs_vnops.c434
-rw-r--r--sys/ufs/mfs/mfsiom.h37
-rw-r--r--sys/ufs/mfs/mfsnode.h86
-rw-r--r--sys/ufs/ufs/dinode.h108
-rw-r--r--sys/ufs/ufs/dir.h147
-rw-r--r--sys/ufs/ufs/inode.h162
-rw-r--r--sys/ufs/ufs/lockf.h79
-rw-r--r--sys/ufs/ufs/quota.h207
-rw-r--r--sys/ufs/ufs/ufs_bmap.c294
-rw-r--r--sys/ufs/ufs/ufs_disksubr.c461
-rw-r--r--sys/ufs/ufs/ufs_extern.h125
-rw-r--r--sys/ufs/ufs/ufs_ihash.c154
-rw-r--r--sys/ufs/ufs/ufs_inode.c188
-rw-r--r--sys/ufs/ufs/ufs_lockf.c707
-rw-r--r--sys/ufs/ufs/ufs_lookup.c970
-rw-r--r--sys/ufs/ufs/ufs_quota.c938
-rw-r--r--sys/ufs/ufs/ufs_readwrite.c297
-rw-r--r--sys/ufs/ufs/ufs_vfsops.c206
-rw-r--r--sys/ufs/ufs/ufs_vnops.c2162
-rw-r--r--sys/ufs/ufs/ufsmount.h83
-rw-r--r--sys/vm/device_pager.c366
-rw-r--r--sys/vm/device_pager.h53
-rw-r--r--sys/vm/kern_lock.c534
-rw-r--r--sys/vm/lock.h172
-rw-r--r--sys/vm/pmap.h122
-rw-r--r--sys/vm/swap_pager.c1833
-rw-r--r--sys/vm/swap_pager.h99
-rw-r--r--sys/vm/vm.h93
-rw-r--r--sys/vm/vm_extern.h135
-rw-r--r--sys/vm/vm_fault.c1305
-rw-r--r--sys/vm/vm_glue.c685
-rw-r--r--sys/vm/vm_inherit.h83
-rw-r--r--sys/vm/vm_init.c105
-rw-r--r--sys/vm/vm_kern.c454
-rw-r--r--sys/vm/vm_kern.h74
-rw-r--r--sys/vm/vm_map.c2681
-rw-r--r--sys/vm/vm_map.h228
-rw-r--r--sys/vm/vm_meter.c225
-rw-r--r--sys/vm/vm_mmap.c836
-rw-r--r--sys/vm/vm_object.c1645
-rw-r--r--sys/vm/vm_object.h173
-rw-r--r--sys/vm/vm_page.c879
-rw-r--r--sys/vm/vm_page.h268
-rw-r--r--sys/vm/vm_pageout.c790
-rw-r--r--sys/vm/vm_pageout.h112
-rw-r--r--sys/vm/vm_pager.c322
-rw-r--r--sys/vm/vm_pager.h154
-rw-r--r--sys/vm/vm_param.h189
-rw-r--r--sys/vm/vm_prot.h102
-rw-r--r--sys/vm/vm_swap.c439
-rw-r--r--sys/vm/vm_unix.c110
-rw-r--r--sys/vm/vm_user.c314
-rw-r--r--sys/vm/vnode_pager.c1424
-rw-r--r--sys/vm/vnode_pager.h56
1084 files changed, 393312 insertions, 0 deletions
diff --git a/sys/Makefile b/sys/Makefile
new file mode 100644
index 0000000..0e0ac3a
--- /dev/null
+++ b/sys/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= libkern
+
+.include <bsd.subdir.mk>
diff --git a/sys/alpha/include/console.h b/sys/alpha/include/console.h
new file mode 100644
index 0000000..e663dea
--- /dev/null
+++ b/sys/alpha/include/console.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 1992, 1993, 1994 Søren Schmidt
+ *
+ * This program is free software; you may redistribute it and/or
+ * modify it, provided that it retain the above copyright notice
+ * and the following disclaimer.
+ *
+ * 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.
+ *
+ * Søren Schmidt Email: sos@login.dkuug.dk
+ * Tritonvej 36 UUCP: ...uunet!dkuug!login!sos
+ * DK9210 Aalborg SO Phone: +45 9814 8076
+ *
+ * from:@(#)console.h 1.1 940105
+ * $Id: console.h,v 1.7 1994/02/04 10:35:29 chmr Exp $
+ */
+
+#ifndef _CONSOLE_H_
+#define _CONSOLE_H_
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#define KDGKBMODE _IOR('K', 6, int)
+#define KDSKBMODE _IO('K', 7)
+#define KDMKTONE _IO('K', 8)
+#define KDGETMODE _IOR('K', 9, int)
+#define KDSETMODE _IO('K', 10)
+#define KDSBORDER _IO('K', 13)
+#define KDGKBSTATE _IOR('K', 19, int)
+#define KDSKBSTATE _IO('K', 20)
+#define KDENABIO _IO('K', 60)
+#define KDDISABIO _IO('K', 61)
+#define KIOCSOUND _IO('K', 63)
+#define KDGKBTYPE _IOR('K', 64, int)
+#define KDGETLED _IOR('K', 65, int)
+#define KDSETLED _IO('K', 66)
+#define KDSETRAD _IO('K', 67)
+
+#define GETFKEY _IOWR('k', 0, fkeyarg_t)
+#define SETFKEY _IOWR('k', 1, fkeyarg_t)
+#define GIO_SCRNMAP _IOR('k', 2, scrmap_t)
+#define PIO_SCRNMAP _IOW('k', 3, scrmap_t)
+#define GIO_KEYMAP _IOR('k', 6, keymap_t)
+#define PIO_KEYMAP _IOW('k', 7, keymap_t)
+
+#define CONS_BLANKTIME _IOW('c', 4, long)
+#define CONS_SSAVER _IOW('c', 5, ssaver_t)
+#define CONS_GSAVER _IOWR('c', 6, ssaver_t)
+#define PIO_FONT8x8 _IOW('c', 64, fnt8_t)
+#define GIO_FONT8x8 _IOR('c', 65, fnt8_t)
+#define PIO_FONT8x14 _IOW('c', 66, fnt14_t)
+#define GIO_FONT8x14 _IOR('c', 67, fnt14_t)
+#define PIO_FONT8x16 _IOW('c', 68, fnt16_t)
+#define GIO_FONT8x16 _IOR('c', 69, fnt16_t)
+#define CONS_GETINFO _IOWR('c', 73, vid_info_t)
+#define CONS_GETVERS _IOR('c', 74, long)
+#define CONS_80x25TEXT _IO('c', 102)
+#define CONS_80x50TEXT _IO('c', 103)
+
+#define VT_OPENQRY _IOR('v', 1, int)
+#define VT_SETMODE _IOW('v', 2, vtmode_t)
+#define VT_GETMODE _IOR('v', 3, vtmode_t)
+#define VT_RELDISP _IO('v', 4)
+#define VT_ACTIVATE _IO('v', 5)
+#define VT_WAITACTIVE _IO('v', 6)
+#define VT_GETACTIVE _IOR('v', 7, int)
+
+#define VT_FALSE 0
+#define VT_TRUE 1
+#define VT_ACKACQ 2
+
+#define VT_AUTO 0 /* switching is automatic */
+#define VT_PROCESS 1 /* switching controlled by prog */
+
+/* compatibility to old pccons & X386 */
+#define CONSOLE_X_MODE_ON _IO('t', 121)
+#define CONSOLE_X_MODE_OFF _IO('t', 122)
+#define CONSOLE_X_BELL _IOW('t',123,int[2])
+
+struct vt_mode {
+ char mode;
+ char waitv; /* not implemented yet SOS */
+ short relsig;
+ short acqsig;
+ short frsig; /* not implemented yet SOS */
+};
+
+
+#define KD_MONO 1 /* monochrome adapter */
+#define KD_HERCULES 2 /* hercules adapter */
+#define KD_CGA 3 /* color graphics adapter */
+#define KD_EGA 4 /* enhanced graphics adapter */
+#define KD_VGA 5 /* video graohics adapter */
+
+#define KD_TEXT 0 /* set text mode restore fonts */
+#define KD_TEXT0 0 /* ditto */
+#define KD_TEXT1 2 /* set text mode !restore fonts */
+#define KD_GRAPHICS 1 /* set graphics mode */
+
+#define K_RAW 0 /* keyboard returns scancodes */
+#define K_XLATE 1 /* keyboard returns ascii */
+
+#define KB_84 1 /* 'old' 84 key AT-keyboard */
+#define KB_101 2 /* MF-101 or MF-102 keyboard */
+#define KB_OTHER 3 /* keyboard not known */
+
+#define CLKED 1 /* Caps locked */
+#define NLKED 2 /* Num locked */
+#define SLKED 4 /* Scroll locked */
+#define ALKED 8 /* AltGr locked */
+#define LED_CAP 1 /* Caps lock LED */
+#define LED_NUM 2 /* Num lock LED */
+#define LED_SCR 4 /* Scroll lock LED */
+
+/* possible flag values */
+#define FLAG_LOCK_O 0
+#define FLAG_LOCK_C 1
+#define FLAG_LOCK_N 2
+
+#define NUM_KEYS 256 /* number of keys in table */
+#define NUM_STATES 8 /* states per key */
+#define ALTGR_OFFSET 128 /* offset for altlock keys */
+
+struct keymap {
+ u_short n_keys;
+ struct key_t {
+ u_char map[NUM_STATES];
+ u_char spcl;
+ u_char flgs;
+ } key[NUM_KEYS];
+};
+
+#define MAXFK 16
+
+struct fkeytab {
+ u_char str[MAXFK];
+ u_char len;
+};
+
+struct fkeyarg {
+ u_short keynum;
+ char keydef[MAXFK];
+ char flen;
+};
+
+struct colors {
+ char fore;
+ char back;
+};
+
+struct vid_info {
+ short size;
+ short m_num;
+ u_short mv_row, mv_col;
+ u_short mv_rsz, mv_csz;
+ struct colors mv_norm,
+ mv_rev,
+ mv_grfc;
+ u_char mv_ovscan;
+ u_char mk_keylock;
+};
+
+#define MAXSSAVER 16
+
+struct ssaver {
+ char name[MAXSSAVER];
+ int num;
+ long time;
+};
+
+typedef struct keymap keymap_t;
+typedef struct fkeytab fkeytab_t;
+typedef struct fkeyarg fkeyarg_t;
+typedef struct vid_info vid_info_t;
+typedef struct vt_mode vtmode_t;
+typedef struct {char scrmap[256];} scrmap_t;
+typedef struct {char fnt8x8[8*256];} fnt8_t;
+typedef struct {char fnt8x14[14*256];} fnt14_t;
+typedef struct {char fnt8x16[16*256];} fnt16_t;
+typedef struct ssaver ssaver_t;
+
+/* defines for "special" keys (spcl bit set in keymap) */
+#define NOP 0x00 /* nothing (dead key) */
+#define LSH 0x02 /* left shift key */
+#define RSH 0x03 /* right shift key */
+#define CLK 0x04 /* caps lock key */
+#define NLK 0x05 /* num lock key */
+#define SLK 0x06 /* scroll lock key */
+#define LALT 0x07 /* left alt key */
+#define LCTR 0x09 /* left control key */
+#define NEXT 0x0a /* switch to next screen */
+#define F_SCR 0x0b /* switch to first screen */
+#define L_SCR 0x1a /* switch to last screen */
+#define F_FN 0x1b /* first function key */
+#define L_FN 0x7a /* last function key */
+#define RCTR 0x7b /* right control key */
+#define RALT 0x7c /* right alt (altgr) key */
+#define ALK 0x7d /* alt lock key */
+#define ASH 0x7e /* alt shift key */
+#define META 0x7f /* meta key */
+#define RBT 0x80 /* boot machine */
+#define DBG 0x81 /* call debugger */
+
+#define F(x) ((x)+F_FN-1)
+#define S(x) ((x)+F_SCR-1)
+#define NOKEY 0x100 /* no key pressed marker */
+#define FKEY 0x200 /* funtion key marker */
+#define MKEY 0x400 /* meta key marker (prepend ESC)*/
+
+#define KB_DATA 0x60 /* kbd data port */
+#define KB_STAT 0x64 /* kbd status port */
+#define KB_BUF_FULL 0x01 /* kbd has char pending */
+#define KB_READY 0x02 /* kbd ready for command */
+#define KB_WRITE 0x60 /* kbd write command */
+#define KB_SETLEDS 0xed /* kbd set leds */
+#define KB_SETRAD 0xf3 /* kbd set repeat&delay command */
+#define KB_ACK 0xfa /* kbd acknowledge answer */
+#define KB_RESET_CPU 0xfe /* kbd reset main cpu command */
+#define KB_RESET 0xff /* kbd reset */
+
+#endif
diff --git a/sys/alpha/include/pc/display.h b/sys/alpha/include/pc/display.h
new file mode 100644
index 0000000..9e64a3f
--- /dev/null
+++ b/sys/alpha/include/pc/display.h
@@ -0,0 +1,45 @@
+/*
+ * IBM PC display definitions
+ *
+ * $Id$
+ */
+
+/* Color attributes for foreground text */
+
+#define FG_BLACK 0
+#define FG_BLUE 1
+#define FG_GREEN 2
+#define FG_CYAN 3
+#define FG_RED 4
+#define FG_MAGENTA 5
+#define FG_BROWN 6
+#define FG_LIGHTGREY 7
+#define FG_DARKGREY 8
+#define FG_LIGHTBLUE 9
+#define FG_LIGHTGREEN 10
+#define FG_LIGHTCYAN 11
+#define FG_LIGHTRED 12
+#define FG_LIGHTMAGENTA 13
+#define FG_YELLOW 14
+#define FG_WHITE 15
+#define FG_BLINK 0x80
+
+/* Color attributes for text background */
+
+#define BG_BLACK 0x00
+#define BG_BLUE 0x10
+#define BG_GREEN 0x20
+#define BG_CYAN 0x30
+#define BG_RED 0x40
+#define BG_MAGENTA 0x50
+#define BG_BROWN 0x60
+#define BG_LIGHTGREY 0x70
+
+/* Monochrome attributes for foreground text */
+
+#define FG_UNDERLINE 0x01
+#define FG_INTENSE 0x08
+
+/* Monochrome attributes for text background */
+
+#define BG_INTENSE 0x10
diff --git a/sys/alpha/include/pc/msdos.h b/sys/alpha/include/pc/msdos.h
new file mode 100644
index 0000000..ea221c7
--- /dev/null
+++ b/sys/alpha/include/pc/msdos.h
@@ -0,0 +1,65 @@
+/*
+ * msdos common header file
+ * [obtained from mtools -wfj]
+ * how to decipher DOS disk structures in coexisting with DOS
+ *
+ * $Id$
+ */
+
+#define MSECTOR_SIZE 512 /* MSDOS sector size in bytes */
+#define MDIR_SIZE 32 /* MSDOS directory size in bytes */
+#define MAX_CLUSTER 8192 /* largest cluster size */
+#define MAX_PATH 128 /* largest MSDOS path length */
+#define MAX_DIR_SECS 64 /* largest directory (in sectors) */
+
+#define NEW 1
+#define OLD 0
+
+struct directory {
+ unsigned char name[8]; /* file name */
+ unsigned char ext[3]; /* file extension */
+ unsigned char attr; /* attribute byte */
+ unsigned char reserved[10]; /* ?? */
+ unsigned char time[2]; /* time stamp */
+ unsigned char date[2]; /* date stamp */
+ unsigned char start[2]; /* starting cluster number */
+ unsigned char size[4]; /* size of the file */
+};
+
+struct bootsector {
+ unsigned char jump[3]; /* Jump to boot code */
+ unsigned char banner[8]; /* OEM name & version */
+ unsigned char secsiz[2]; /* Bytes per sector hopefully 512 */
+ unsigned char clsiz; /* Cluster size in sectors */
+ unsigned char nrsvsect[2]; /* Number of reserved (boot) sectors */
+ unsigned char nfat; /* Number of FAT tables hopefully 2 */
+ unsigned char dirents[2]; /* Number of directory slots */
+ unsigned char psect[2]; /* Total sectors on disk */
+ unsigned char descr; /* Media descriptor=first byte of FAT */
+ unsigned char fatlen[2]; /* Sectors in FAT */
+ unsigned char nsect[2]; /* Sectors/track */
+ unsigned char nheads[2]; /* Heads */
+ unsigned char nhs[4]; /* number of hidden sectors */
+ unsigned char bigsect[4]; /* big total sectors */
+ unsigned char junk[476]; /* who cares? */
+};
+
+/* DOS partition table -- located in boot block */
+
+#define DOSBBSECTOR 0 /* DOS boot block relative sector number */
+#define DOSPARTOFF 446
+#define NDOSPART 4
+
+struct dos_partition {
+ unsigned char dp_flag; /* bootstrap flags */
+ unsigned char dp_shd; /* starting head */
+ unsigned char dp_ssect; /* starting sector */
+ unsigned char dp_scyl; /* starting cylinder */
+ unsigned char dp_typ; /* partition type */
+#define DOSPTYP_386BSD 0xa5 /* 386BSD partition type */
+ unsigned char dp_ehd; /* end head */
+ unsigned char dp_esect; /* end sector */
+ unsigned char dp_ecyl; /* end cylinder */
+ unsigned long dp_start; /* absolute starting sector number */
+ unsigned long dp_size; /* partition size in sectors */
+} dos_partitions[NDOSPART];
diff --git a/sys/amd64/Makefile b/sys/amd64/Makefile
new file mode 100644
index 0000000..4ad5a34
--- /dev/null
+++ b/sys/amd64/Makefile
@@ -0,0 +1,25 @@
+# from: @(#)Makefile 7.3 (Berkeley) 6/9/91
+# $Id$
+
+# Makefile for i386 tags file
+
+all:
+ @echo "make tags or links only"
+
+TI386= ../i386/tags
+SI386= ../i386/i386/*.[ch] ../i386/include/*.h ../i386/isa/*.[ch]
+AI386= ../i386/i386/*.s
+
+# Directories in which to place i386 tags links
+DI386= eisa isa mca include
+
+tags:
+ -ctags -dtf ${TI386} ${COMM} ${SI386}
+ egrep "^ENTRY(.*)|^ALTENTRY(.*)" ${AI386} | \
+ sed "s;\([^:]*\):\([^(]*\)(\([^, )]*\)\(.*\);\3 \1 /^\2(\3\4$$/;" \
+ >> ${TI386}
+ sort -o ${TI386} ${TI386}
+
+links:
+ -for i in ${DI386}; do \
+ cd $$i && rm -f tags; ln -s ../tags tags; done
diff --git a/sys/amd64/amd64/autoconf.c b/sys/amd64/amd64/autoconf.c
new file mode 100644
index 0000000..3575d1c
--- /dev/null
+++ b/sys/amd64/amd64/autoconf.c
@@ -0,0 +1,209 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)autoconf.c 7.1 (Berkeley) 5/9/91
+ * $Id: autoconf.c,v 1.10 1994/03/21 14:53:08 ache Exp $
+ */
+
+/*
+ * Setup the system to run on the current machine.
+ *
+ * Configure() is called at boot time and initializes the vba
+ * device tables and the memory controller monitoring. Available
+ * devices are determined (from possibilities mentioned in ioconf.c),
+ * and the drivers are initialized.
+ */
+#include "param.h"
+#include "systm.h"
+#include "buf.h"
+#include "dkstat.h"
+#include "conf.h"
+#include "dmap.h"
+#include "reboot.h"
+#include "kernel.h"
+
+#include "machine/pte.h"
+
+static void swapconf(void);
+static void setroot(void);
+
+/*
+ * The following several variables are related to
+ * the configuration process, and are used in initializing
+ * the machine.
+ */
+int dkn; /* number of iostat dk numbers assigned so far */
+extern int cold; /* cold start flag initialized in locore.s */
+
+/*
+ * Determine i/o configuration for a machine.
+ */
+void
+configure()
+{
+
+#include "isa.h"
+#if NISA > 0
+ isa_configure();
+#endif
+
+#if GENERICxxx && !defined(DISKLESS)
+ if ((boothowto & RB_ASKNAME) == 0)
+ setroot();
+ setconf();
+#else
+#ifndef DISKLESS
+ setroot();
+#endif
+#endif
+ /*
+ * Configure swap area and related system
+ * parameter based on device(s) used.
+ */
+ swapconf();
+ cold = 0;
+}
+
+/*
+ * Configure swap space and related parameters.
+ */
+static void
+swapconf()
+{
+ register struct swdevt *swp;
+ register int nblks;
+ extern int Maxmem;
+
+ for (swp = swdevt; swp->sw_dev > 0; swp++)
+ {
+ unsigned d = major(swp->sw_dev);
+
+ if (d > nblkdev) break;
+ if (bdevsw[d].d_psize) {
+ nblks = (*bdevsw[d].d_psize)(swp->sw_dev);
+ if (nblks > 0 &&
+ (swp->sw_nblks == 0 || swp->sw_nblks > nblks))
+ swp->sw_nblks = nblks;
+ else
+ swp->sw_nblks = 0;
+ }
+ swp->sw_nblks = ctod(dtoc(swp->sw_nblks));
+ }
+ if (dumplo == 0 && bdevsw[major(dumpdev)].d_psize)
+ dumplo = (*bdevsw[major(dumpdev)].d_psize)(dumpdev) -
+ Maxmem*NBPG/512;
+ if (dumplo < 0)
+ dumplo = 0;
+}
+
+#define DOSWAP /* change swdevt and dumpdev */
+u_long bootdev = 0; /* should be dev_t, but not until 32 bits */
+
+static char devname[][2] = {
+ 'w','d', /* 0 = wd */
+ 's','w', /* 1 = sw */
+#define FDMAJOR 2
+ 'f','d', /* 2 = fd */
+ 'w','t', /* 3 = wt */
+ 's','d', /* 4 = sd -- new SCSI system */
+};
+
+#define PARTITIONMASK 0x7
+#define PARTITIONSHIFT 3
+#define FDUNITSHIFT 6
+
+/*
+ * Attempt to find the device from which we were booted.
+ * If we can do so, and not instructed not to do so,
+ * change rootdev to correspond to the load device.
+ */
+static void
+setroot()
+{
+ int majdev, mindev, unit, part, adaptor;
+ dev_t temp = 0, orootdev;
+ struct swdevt *swp;
+
+/*printf("howto %x bootdev %x ", boothowto, bootdev);*/
+ if (boothowto & RB_DFLTROOT ||
+ (bootdev & B_MAGICMASK) != (u_long)B_DEVMAGIC)
+ return;
+ majdev = (bootdev >> B_TYPESHIFT) & B_TYPEMASK;
+ if (majdev > sizeof(devname) / sizeof(devname[0]))
+ return;
+ adaptor = (bootdev >> B_ADAPTORSHIFT) & B_ADAPTORMASK;
+ unit = (bootdev >> B_UNITSHIFT) & B_UNITMASK;
+ if (majdev == FDMAJOR) {
+ part = 3; /* raw */
+ mindev = unit << FDUNITSHIFT;
+ }
+ else {
+ part = (bootdev >> B_PARTITIONSHIFT) & B_PARTITIONMASK;
+ mindev = (unit << PARTITIONSHIFT) + part;
+ }
+ orootdev = rootdev;
+ rootdev = makedev(majdev, mindev);
+ /*
+ * If the original rootdev is the same as the one
+ * just calculated, don't need to adjust the swap configuration.
+ */
+ if (rootdev == orootdev)
+ return;
+ printf("changing root device to %c%c%d%c\n",
+ devname[majdev][0], devname[majdev][1],
+ mindev >> (majdev == FDMAJOR ? FDUNITSHIFT : PARTITIONSHIFT),
+ part + 'a');
+#ifdef DOSWAP
+ mindev &= ~PARTITIONMASK;
+ for (swp = swdevt; swp->sw_dev; swp++) {
+ if (majdev == major(swp->sw_dev) &&
+ mindev == (minor(swp->sw_dev) & ~PARTITIONMASK)) {
+
+ temp = swdevt[0].sw_dev;
+ swdevt[0].sw_dev = swp->sw_dev;
+ swp->sw_dev = temp;
+ break;
+ }
+ }
+ if (swp->sw_dev == 0)
+ return;
+ /*
+ * If dumpdev was the same as the old primary swap
+ * device, move it to the new primary swap device.
+ */
+ if (temp == dumpdev)
+ dumpdev = swdevt[0].sw_dev;
+#endif
+}
diff --git a/sys/amd64/amd64/cpu_switch.S b/sys/amd64/amd64/cpu_switch.S
new file mode 100644
index 0000000..aa8b5ba
--- /dev/null
+++ b/sys/amd64/amd64/cpu_switch.S
@@ -0,0 +1,458 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: swtch.s,v 1.5 1994/04/02 07:00:30 davidg Exp $
+ */
+
+#include "npx.h" /* for NNPX */
+#include "assym.s" /* for preprocessor defines */
+#include "errno.h" /* for error codes */
+
+#include "machine/asmacros.h" /* for miscellaneous assembly macros */
+#define LOCORE /* XXX inhibit C declarations */
+#include "machine/spl.h" /* for SWI_AST_MASK ... */
+
+
+/*****************************************************************************/
+/* Scheduling */
+/*****************************************************************************/
+
+/*
+ * The following primitives manipulate the run queues.
+ * _whichqs tells which of the 32 queues _qs
+ * have processes in them. setrunqueue puts processes into queues, Remrq
+ * removes them from queues. The running process is on no queue,
+ * other processes are on a queue related to p->p_priority, divided by 4
+ * actually to shrink the 0-127 range of priorities into the 32 available
+ * queues.
+ */
+ .data
+ .globl _curpcb, _whichqs
+_curpcb: .long 0 /* pointer to curproc's PCB area */
+_whichqs: .long 0 /* which run queues have data */
+
+ .globl _qs,_cnt,_panic
+ .comm _noproc,4
+ .comm _runrun,4
+
+ .globl _want_resched
+_want_resched: .long 0 /* we need to re-run the scheduler */
+
+ .text
+/*
+ * setrunqueue(p)
+ *
+ * Call should be made at spl6(), and p->p_stat should be SRUN
+ */
+ENTRY(setrunqueue)
+ movl 4(%esp),%eax
+ cmpl $0,P_RLINK(%eax) /* should not be on q already */
+ je set1
+ pushl $set2
+ call _panic
+set1:
+ movzbl P_PRI(%eax),%edx
+ shrl $2,%edx
+ btsl %edx,_whichqs /* set q full bit */
+ shll $3,%edx
+ addl $_qs,%edx /* locate q hdr */
+ movl %edx,P_LINK(%eax) /* link process on tail of q */
+ movl P_RLINK(%edx),%ecx
+ movl %ecx,P_RLINK(%eax)
+ movl %eax,P_RLINK(%edx)
+ movl %eax,P_LINK(%ecx)
+ ret
+
+set2: .asciz "setrunqueue"
+
+/*
+ * Remrq(p)
+ *
+ * Call should be made at spl6().
+ */
+ENTRY(remrq)
+ movl 4(%esp),%eax
+ movzbl P_PRI(%eax),%edx
+ shrl $2,%edx
+ btrl %edx,_whichqs /* clear full bit, panic if clear already */
+ jb rem1
+ pushl $rem3
+ call _panic
+rem1:
+ pushl %edx
+ movl P_LINK(%eax),%ecx /* unlink process */
+ movl P_RLINK(%eax),%edx
+ movl %edx,P_RLINK(%ecx)
+ movl P_RLINK(%eax),%ecx
+ movl P_LINK(%eax),%edx
+ movl %edx,P_LINK(%ecx)
+ popl %edx
+ movl $_qs,%ecx
+ shll $3,%edx
+ addl %edx,%ecx
+ cmpl P_LINK(%ecx),%ecx /* q still has something? */
+ je rem2
+ shrl $3,%edx /* yes, set bit as still full */
+ btsl %edx,_whichqs
+rem2:
+ movl $0,P_RLINK(%eax) /* zap reverse link to indicate off list */
+ ret
+
+rem3: .asciz "remrq"
+sw0: .asciz "cpu_switch"
+
+/*
+ * When no processes are on the runq, cpu_switch() branches to _idle
+ * to wait for something to come ready.
+ */
+ ALIGN_TEXT
+_idle:
+ MCOUNT
+ movl _IdlePTD,%ecx
+ movl %ecx,%cr3
+ movl $tmpstk-4,%esp
+ sti
+
+ /*
+ * XXX callers of cpu_switch() do a bogus splclock(). Locking should
+ * be left to cpu_switch().
+ */
+ movl $SWI_AST_MASK,_cpl
+ testl $~SWI_AST_MASK,_ipending
+ je idle_loop
+ call _splz
+
+ ALIGN_TEXT
+idle_loop:
+ cli
+ cmpl $0,_whichqs
+ jne sw1a
+ sti
+ hlt /* wait for interrupt */
+ jmp idle_loop
+
+badsw:
+ pushl $sw0
+ call _panic
+ /*NOTREACHED*/
+
+/*
+ * cpu_switch()
+ */
+ENTRY(cpu_switch)
+ incl _cnt+V_SWTCH
+
+ /* switch to new process. first, save context as needed */
+
+ movl _curproc,%ecx
+
+ /* if no process to save, don't bother */
+ testl %ecx,%ecx
+ je sw1
+
+ movl P_ADDR(%ecx),%ecx
+
+ movl (%esp),%eax /* Hardware registers */
+ movl %eax,PCB_EIP(%ecx)
+ movl %ebx,PCB_EBX(%ecx)
+ movl %esp,PCB_ESP(%ecx)
+ movl %ebp,PCB_EBP(%ecx)
+ movl %esi,PCB_ESI(%ecx)
+ movl %edi,PCB_EDI(%ecx)
+
+#if NNPX > 0
+ /* have we used fp, and need a save? */
+ mov _curproc,%eax
+ cmp %eax,_npxproc
+ jne 1f
+ pushl %ecx /* h/w bugs make saving complicated */
+ leal PCB_SAVEFPU(%ecx),%eax
+ pushl %eax
+ call _npxsave /* do it in a big C function */
+ popl %eax
+ popl %ecx
+1:
+#endif /* NNPX > 0 */
+
+ movl _CMAP2,%eax /* save temporary map PTE */
+ movl %eax,PCB_CMAP2(%ecx) /* in our context */
+ movl $0,_curproc /* out of process */
+
+# movw _cpl,%ax
+# movw %ax,PCB_IML(%ecx) /* save ipl */
+
+ /* save is done, now choose a new process or idle */
+sw1:
+ cli
+sw1a:
+ movl _whichqs,%edi
+2:
+ /* XXX - bsf is sloow */
+ bsfl %edi,%eax /* find a full q */
+ je _idle /* if none, idle */
+
+ /* XX update whichqs? */
+ btrl %eax,%edi /* clear q full status */
+ jnb 2b /* if it was clear, look for another */
+ movl %eax,%ebx /* save which one we are using */
+
+ shll $3,%eax
+ addl $_qs,%eax /* select q */
+ movl %eax,%esi
+
+#ifdef DIAGNOSTIC
+ cmpl P_LINK(%eax),%eax /* linked to self? (e.g. not on list) */
+ je badsw /* not possible */
+#endif
+
+ movl P_LINK(%eax),%ecx /* unlink from front of process q */
+ movl P_LINK(%ecx),%edx
+ movl %edx,P_LINK(%eax)
+ movl P_RLINK(%ecx),%eax
+ movl %eax,P_RLINK(%edx)
+
+ cmpl P_LINK(%ecx),%esi /* q empty */
+ je 3f
+ btsl %ebx,%edi /* nope, set to indicate full */
+3:
+ movl %edi,_whichqs /* update q status */
+
+ movl $0,%eax
+ movl %eax,_want_resched
+
+#ifdef DIAGNOSTIC
+ cmpl %eax,P_WCHAN(%ecx)
+ jne badsw
+ cmpb $SRUN,P_STAT(%ecx)
+ jne badsw
+#endif
+
+ movl %eax,P_RLINK(%ecx) /* isolate process to run */
+ movl P_ADDR(%ecx),%edx
+ movl PCB_CR3(%edx),%ebx
+
+ /* switch address space */
+ movl %ebx,%cr3
+
+ /* restore context */
+ movl PCB_EBX(%edx),%ebx
+ movl PCB_ESP(%edx),%esp
+ movl PCB_EBP(%edx),%ebp
+ movl PCB_ESI(%edx),%esi
+ movl PCB_EDI(%edx),%edi
+ movl PCB_EIP(%edx),%eax
+ movl %eax,(%esp)
+
+ movl PCB_CMAP2(%edx),%eax /* get temporary map */
+ movl %eax,_CMAP2 /* reload temporary map PTE */
+
+ movl %ecx,_curproc /* into next process */
+ movl %edx,_curpcb
+
+#ifdef USER_LDT
+ cmpl $0, PCB_USERLDT(%edx)
+ jnz 1f
+ movl __default_ldt,%eax
+ cmpl _currentldt,%eax
+ je 2f
+ lldt __default_ldt
+ movl %eax,_currentldt
+ jmp 2f
+1: pushl %edx
+ call _set_user_ldt
+ popl %edx
+2:
+#endif
+
+ pushl %edx /* save p to return */
+/*
+ * XXX - 0.0 forgot to save it - is that why this was commented out in 0.1?
+ * I think restoring the cpl is unnecessary, but we must turn off the cli
+ * now that spl*() don't do it as a side affect.
+ */
+ pushl PCB_IML(%edx)
+ sti
+#if 0
+ call _splx
+#endif
+ addl $4,%esp
+/*
+ * XXX - 0.0 gets here via swtch_to_inactive(). I think 0.1 gets here in the
+ * same way. Better return a value.
+ */
+ popl %eax /* return(p); */
+ ret
+
+ENTRY(mvesp)
+ movl %esp,%eax
+ ret
+/*
+ * struct proc *swtch_to_inactive(struct proc *p);
+ *
+ * At exit of a process, move off the address space of the
+ * process and onto a "safe" one. Then, on a temporary stack
+ * return and run code that disposes of the old state.
+ * Since this code requires a parameter from the "old" stack,
+ * pass it back as a return value.
+ */
+ENTRY(swtch_to_inactive)
+ popl %edx /* old pc */
+ popl %eax /* arg, our return value */
+ movl _IdlePTD,%ecx
+ movl %ecx,%cr3 /* good bye address space */
+ #write buffer?
+ movl $tmpstk-4,%esp /* temporary stack, compensated for call */
+ MEXITCOUNT
+ jmp %edx /* return, execute remainder of cleanup */
+
+/*
+ * savectx(pcb, altreturn)
+ * Update pcb, saving current processor state and arranging
+ * for alternate return ala longjmp in cpu_switch if altreturn is true.
+ */
+ENTRY(savectx)
+ movl 4(%esp),%ecx
+ movw _cpl,%ax
+ movw %ax,PCB_IML(%ecx)
+ movl (%esp),%eax
+ movl %eax,PCB_EIP(%ecx)
+ movl %ebx,PCB_EBX(%ecx)
+ movl %esp,PCB_ESP(%ecx)
+ movl %ebp,PCB_EBP(%ecx)
+ movl %esi,PCB_ESI(%ecx)
+ movl %edi,PCB_EDI(%ecx)
+
+#if NNPX > 0
+ /*
+ * If npxproc == NULL, then the npx h/w state is irrelevant and the
+ * state had better already be in the pcb. This is true for forks
+ * but not for dumps (the old book-keeping with FP flags in the pcb
+ * always lost for dumps because the dump pcb has 0 flags).
+ *
+ * If npxproc != NULL, then we have to save the npx h/w state to
+ * npxproc's pcb and copy it to the requested pcb, or save to the
+ * requested pcb and reload. Copying is easier because we would
+ * have to handle h/w bugs for reloading. We used to lose the
+ * parent's npx state for forks by forgetting to reload.
+ */
+ mov _npxproc,%eax
+ testl %eax,%eax
+ je 1f
+
+ pushl %ecx
+ movl P_ADDR(%eax),%eax
+ leal PCB_SAVEFPU(%eax),%eax
+ pushl %eax
+ pushl %eax
+ call _npxsave
+ popl %eax
+ popl %eax
+ popl %ecx
+
+ pushl %ecx
+ pushl $108+8*2 /* XXX h/w state size + padding */
+ leal PCB_SAVEFPU(%ecx),%ecx
+ pushl %ecx
+ pushl %eax
+ call _bcopy
+ addl $12,%esp
+ popl %ecx
+1:
+#endif /* NNPX > 0 */
+
+ movl _CMAP2,%edx /* save temporary map PTE */
+ movl %edx,PCB_CMAP2(%ecx) /* in our context */
+
+ cmpl $0,8(%esp)
+ je 1f
+ movl %esp,%edx /* relocate current sp relative to pcb */
+ subl $_kstack,%edx /* (sp is relative to kstack): */
+ addl %edx,%ecx /* pcb += sp - kstack; */
+ movl %eax,(%ecx) /* write return pc at (relocated) sp@ */
+
+/* this mess deals with replicating register state gcc hides */
+ movl 12(%esp),%eax
+ movl %eax,12(%ecx)
+ movl 16(%esp),%eax
+ movl %eax,16(%ecx)
+ movl 20(%esp),%eax
+ movl %eax,20(%ecx)
+ movl 24(%esp),%eax
+ movl %eax,24(%ecx)
+1:
+ xorl %eax,%eax /* return 0 */
+ ret
+
+/*
+ * addupc(int pc, struct uprof *up, int ticks):
+ * update profiling information for the user process.
+ */
+ENTRY(addupc)
+ pushl %ebp
+ movl %esp,%ebp
+ movl 12(%ebp),%edx /* up */
+ movl 8(%ebp),%eax /* pc */
+
+ subl PR_OFF(%edx),%eax /* pc -= up->pr_off */
+ jb L1 /* if (pc was < off) return */
+
+ shrl $1,%eax /* praddr = pc >> 1 */
+ imull PR_SCALE(%edx),%eax /* praddr *= up->pr_scale */
+ shrl $15,%eax /* praddr = praddr << 15 */
+ andl $-2,%eax /* praddr &= ~1 */
+
+ cmpl PR_SIZE(%edx),%eax /* if (praddr > up->pr_size) return */
+ ja L1
+
+/* addl %eax,%eax /* praddr -> word offset */
+ addl PR_BASE(%edx),%eax /* praddr += up-> pr_base */
+ movl 16(%ebp),%ecx /* ticks */
+
+ movl _curpcb,%edx
+ movl $proffault,PCB_ONFAULT(%edx)
+ addl %ecx,(%eax) /* storage location += ticks */
+ movl $0,PCB_ONFAULT(%edx)
+L1:
+ leave
+ ret
+
+ ALIGN_TEXT
+proffault:
+ /* if we get a fault, then kill profiling all together */
+ movl $0,PCB_ONFAULT(%edx) /* squish the fault handler */
+ movl 12(%ebp),%ecx
+ movl $0,PR_SCALE(%ecx) /* up->pr_scale = 0 */
+ leave
+ ret
diff --git a/sys/amd64/amd64/db_disasm.c b/sys/amd64/amd64/db_disasm.c
new file mode 100644
index 0000000..98e251b
--- /dev/null
+++ b/sys/amd64/amd64/db_disasm.c
@@ -0,0 +1,1375 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_disasm.c,v 1.4 1993/11/25 01:30:51 wollman Exp $
+ */
+
+/*
+ * Instruction disassembler.
+ */
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+
+#include <ddb/db_access.h>
+#include <ddb/db_sym.h>
+
+/*
+ * Size attributes
+ */
+#define BYTE 0
+#define WORD 1
+#define LONG 2
+#define QUAD 3
+#define SNGL 4
+#define DBLR 5
+#define EXTR 6
+#define SDEP 7
+#define NONE 8
+
+/*
+ * Addressing modes
+ */
+#define E 1 /* general effective address */
+#define Eind 2 /* indirect address (jump, call) */
+#define Ew 3 /* address, word size */
+#define Eb 4 /* address, byte size */
+#define R 5 /* register, in 'reg' field */
+#define Rw 6 /* word register, in 'reg' field */
+#define Ri 7 /* register in instruction */
+#define S 8 /* segment reg, in 'reg' field */
+#define Si 9 /* segment reg, in instruction */
+#define A 10 /* accumulator */
+#define BX 11 /* (bx) */
+#define CL 12 /* cl, for shifts */
+#define DX 13 /* dx, for IO */
+#define SI 14 /* si */
+#define DI 15 /* di */
+#define CR 16 /* control register */
+#define DR 17 /* debug register */
+#define TR 18 /* test register */
+#define I 19 /* immediate, unsigned */
+#define Is 20 /* immediate, signed */
+#define Ib 21 /* byte immediate, unsigned */
+#define Ibs 22 /* byte immediate, signed */
+#define Iw 23 /* word immediate, unsigned */
+#define Il 24 /* long immediate */
+#define O 25 /* direct address */
+#define Db 26 /* byte displacement from EIP */
+#define Dl 27 /* long displacement from EIP */
+#define o1 28 /* constant 1 */
+#define o3 29 /* constant 3 */
+#define OS 30 /* immediate offset/segment */
+#define ST 31 /* FP stack top */
+#define STI 32 /* FP stack */
+#define X 33 /* extended FP op */
+#define XA 34 /* for 'fstcw %ax' */
+
+struct inst {
+ char * i_name; /* name */
+ short i_has_modrm; /* has regmodrm byte */
+ short i_size; /* operand size */
+ int i_mode; /* addressing modes */
+ char * i_extra; /* pointer to extra opcode table */
+};
+
+#define op1(x) (x)
+#define op2(x,y) ((x)|((y)<<8))
+#define op3(x,y,z) ((x)|((y)<<8)|((z)<<16))
+
+struct finst {
+ char * f_name; /* name for memory instruction */
+ int f_size; /* size for memory instruction */
+ int f_rrmode; /* mode for rr instruction */
+ char * f_rrname; /* name for rr instruction
+ (or pointer to table) */
+};
+
+char * db_Grp6[] = {
+ "sldt",
+ "str",
+ "lldt",
+ "ltr",
+ "verr",
+ "verw",
+ "",
+ ""
+};
+
+char * db_Grp7[] = {
+ "sgdt",
+ "sidt",
+ "lgdt",
+ "lidt",
+ "smsw",
+ "",
+ "lmsw",
+ "invlpg"
+};
+
+char * db_Grp8[] = {
+ "",
+ "",
+ "",
+ "",
+ "bt",
+ "bts",
+ "btr",
+ "btc"
+};
+
+struct inst db_inst_0f0x[] = {
+/*00*/ { "", TRUE, NONE, op1(Ew), (char *)db_Grp6 },
+/*01*/ { "", TRUE, NONE, op1(Ew), (char *)db_Grp7 },
+/*02*/ { "lar", TRUE, LONG, op2(E,R), 0 },
+/*03*/ { "lsl", TRUE, LONG, op2(E,R), 0 },
+/*04*/ { "", FALSE, NONE, 0, 0 },
+/*05*/ { "", FALSE, NONE, 0, 0 },
+/*06*/ { "clts", FALSE, NONE, 0, 0 },
+/*07*/ { "", FALSE, NONE, 0, 0 },
+
+/*08*/ { "invd", FALSE, NONE, 0, 0 },
+/*09*/ { "wbinvd",FALSE, NONE, 0, 0 },
+/*0a*/ { "", FALSE, NONE, 0, 0 },
+/*0b*/ { "", FALSE, NONE, 0, 0 },
+/*0c*/ { "", FALSE, NONE, 0, 0 },
+/*0d*/ { "", FALSE, NONE, 0, 0 },
+/*0e*/ { "", FALSE, NONE, 0, 0 },
+/*0f*/ { "", FALSE, NONE, 0, 0 },
+};
+
+struct inst db_inst_0f2x[] = {
+/*20*/ { "mov", TRUE, LONG, op2(CR,E), 0 }, /* use E for reg */
+/*21*/ { "mov", TRUE, LONG, op2(DR,E), 0 }, /* since mod == 11 */
+/*22*/ { "mov", TRUE, LONG, op2(E,CR), 0 },
+/*23*/ { "mov", TRUE, LONG, op2(E,DR), 0 },
+/*24*/ { "mov", TRUE, LONG, op2(TR,E), 0 },
+/*25*/ { "", FALSE, NONE, 0, 0 },
+/*26*/ { "mov", TRUE, LONG, op2(E,TR), 0 },
+/*27*/ { "", FALSE, NONE, 0, 0 },
+
+/*28*/ { "", FALSE, NONE, 0, 0 },
+/*29*/ { "", FALSE, NONE, 0, 0 },
+/*2a*/ { "", FALSE, NONE, 0, 0 },
+/*2b*/ { "", FALSE, NONE, 0, 0 },
+/*2c*/ { "", FALSE, NONE, 0, 0 },
+/*2d*/ { "", FALSE, NONE, 0, 0 },
+/*2e*/ { "", FALSE, NONE, 0, 0 },
+/*2f*/ { "", FALSE, NONE, 0, 0 },
+};
+
+struct inst db_inst_0f8x[] = {
+/*80*/ { "jo", FALSE, NONE, op1(Dl), 0 },
+/*81*/ { "jno", FALSE, NONE, op1(Dl), 0 },
+/*82*/ { "jb", FALSE, NONE, op1(Dl), 0 },
+/*83*/ { "jnb", FALSE, NONE, op1(Dl), 0 },
+/*84*/ { "jz", FALSE, NONE, op1(Dl), 0 },
+/*85*/ { "jnz", FALSE, NONE, op1(Dl), 0 },
+/*86*/ { "jbe", FALSE, NONE, op1(Dl), 0 },
+/*87*/ { "jnbe", FALSE, NONE, op1(Dl), 0 },
+
+/*88*/ { "js", FALSE, NONE, op1(Dl), 0 },
+/*89*/ { "jns", FALSE, NONE, op1(Dl), 0 },
+/*8a*/ { "jp", FALSE, NONE, op1(Dl), 0 },
+/*8b*/ { "jnp", FALSE, NONE, op1(Dl), 0 },
+/*8c*/ { "jl", FALSE, NONE, op1(Dl), 0 },
+/*8d*/ { "jnl", FALSE, NONE, op1(Dl), 0 },
+/*8e*/ { "jle", FALSE, NONE, op1(Dl), 0 },
+/*8f*/ { "jnle", FALSE, NONE, op1(Dl), 0 },
+};
+
+struct inst db_inst_0f9x[] = {
+/*90*/ { "seto", TRUE, NONE, op1(Eb), 0 },
+/*91*/ { "setno", TRUE, NONE, op1(Eb), 0 },
+/*92*/ { "setb", TRUE, NONE, op1(Eb), 0 },
+/*93*/ { "setnb", TRUE, NONE, op1(Eb), 0 },
+/*94*/ { "setz", TRUE, NONE, op1(Eb), 0 },
+/*95*/ { "setnz", TRUE, NONE, op1(Eb), 0 },
+/*96*/ { "setbe", TRUE, NONE, op1(Eb), 0 },
+/*97*/ { "setnbe",TRUE, NONE, op1(Eb), 0 },
+
+/*98*/ { "sets", TRUE, NONE, op1(Eb), 0 },
+/*99*/ { "setns", TRUE, NONE, op1(Eb), 0 },
+/*9a*/ { "setp", TRUE, NONE, op1(Eb), 0 },
+/*9b*/ { "setnp", TRUE, NONE, op1(Eb), 0 },
+/*9c*/ { "setl", TRUE, NONE, op1(Eb), 0 },
+/*9d*/ { "setnl", TRUE, NONE, op1(Eb), 0 },
+/*9e*/ { "setle", TRUE, NONE, op1(Eb), 0 },
+/*9f*/ { "setnle",TRUE, NONE, op1(Eb), 0 },
+};
+
+struct inst db_inst_0fax[] = {
+/*a0*/ { "push", FALSE, NONE, op1(Si), 0 },
+/*a1*/ { "pop", FALSE, NONE, op1(Si), 0 },
+/*a2*/ { "", FALSE, NONE, 0, 0 },
+/*a3*/ { "bt", TRUE, LONG, op2(E,R), 0 },
+/*a4*/ { "shld", TRUE, LONG, op3(Ib,E,R), 0 },
+/*a5*/ { "shld", TRUE, LONG, op3(CL,E,R), 0 },
+/*a6*/ { "", FALSE, NONE, 0, 0 },
+/*a7*/ { "", FALSE, NONE, 0, 0 },
+
+/*a8*/ { "push", FALSE, NONE, op1(Si), 0 },
+/*a9*/ { "pop", FALSE, NONE, op1(Si), 0 },
+/*aa*/ { "", FALSE, NONE, 0, 0 },
+/*ab*/ { "bts", TRUE, LONG, op2(E,R), 0 },
+/*ac*/ { "shrd", TRUE, LONG, op3(Ib,E,R), 0 },
+/*ad*/ { "shrd", TRUE, LONG, op3(CL,E,R), 0 },
+/*a6*/ { "", FALSE, NONE, 0, 0 },
+/*a7*/ { "imul", TRUE, LONG, op2(E,R), 0 },
+};
+
+struct inst db_inst_0fbx[] = {
+/*b0*/ { "", FALSE, NONE, 0, 0 },
+/*b1*/ { "", FALSE, NONE, 0, 0 },
+/*b2*/ { "lss", TRUE, LONG, op2(E, R), 0 },
+/*b3*/ { "bts", TRUE, LONG, op2(R, E), 0 },
+/*b4*/ { "lfs", TRUE, LONG, op2(E, R), 0 },
+/*b5*/ { "lgs", TRUE, LONG, op2(E, R), 0 },
+/*b6*/ { "movzb", TRUE, LONG, op2(E, R), 0 },
+/*b7*/ { "movzw", TRUE, LONG, op2(E, R), 0 },
+
+/*b8*/ { "", FALSE, NONE, 0, 0 },
+/*b9*/ { "", FALSE, NONE, 0, 0 },
+/*ba*/ { "", TRUE, LONG, op2(Is, E), (char *)db_Grp8 },
+/*bb*/ { "btc", TRUE, LONG, op2(R, E), 0 },
+/*bc*/ { "bsf", TRUE, LONG, op2(E, R), 0 },
+/*bd*/ { "bsr", TRUE, LONG, op2(E, R), 0 },
+/*be*/ { "movsb", TRUE, LONG, op2(E, R), 0 },
+/*bf*/ { "movsw", TRUE, LONG, op2(E, R), 0 },
+};
+
+struct inst db_inst_0fcx[] = {
+/*c0*/ { "xadd", TRUE, BYTE, op2(R, E), 0 },
+/*c1*/ { "xadd", TRUE, LONG, op2(R, E), 0 },
+/*c2*/ { "", FALSE, NONE, 0, 0 },
+/*c3*/ { "", FALSE, NONE, 0, 0 },
+/*c4*/ { "", FALSE, NONE, 0, 0 },
+/*c5*/ { "", FALSE, NONE, 0, 0 },
+/*c6*/ { "", FALSE, NONE, 0, 0 },
+/*c7*/ { "", FALSE, NONE, 0, 0 },
+/*c8*/ { "bswap", FALSE, LONG, op1(Ri), 0 },
+/*c9*/ { "bswap", FALSE, LONG, op1(Ri), 0 },
+/*ca*/ { "bswap", FALSE, LONG, op1(Ri), 0 },
+/*cb*/ { "bswap", FALSE, LONG, op1(Ri), 0 },
+/*cc*/ { "bswap", FALSE, LONG, op1(Ri), 0 },
+/*cd*/ { "bswap", FALSE, LONG, op1(Ri), 0 },
+/*ce*/ { "bswap", FALSE, LONG, op1(Ri), 0 },
+/*cf*/ { "bswap", FALSE, LONG, op1(Ri), 0 },
+};
+
+struct inst db_inst_0fdx[] = {
+/*c0*/ { "cmpxchg",TRUE, BYTE, op2(R, E), 0 },
+/*c1*/ { "cmpxchg",TRUE, LONG, op2(R, E), 0 },
+/*c2*/ { "", FALSE, NONE, 0, 0 },
+/*c3*/ { "", FALSE, NONE, 0, 0 },
+/*c4*/ { "", FALSE, NONE, 0, 0 },
+/*c5*/ { "", FALSE, NONE, 0, 0 },
+/*c6*/ { "", FALSE, NONE, 0, 0 },
+/*c7*/ { "", FALSE, NONE, 0, 0 },
+/*c8*/ { "", FALSE, NONE, 0, 0 },
+/*c9*/ { "", FALSE, NONE, 0, 0 },
+/*ca*/ { "", FALSE, NONE, 0, 0 },
+/*cb*/ { "", FALSE, NONE, 0, 0 },
+/*cc*/ { "", FALSE, NONE, 0, 0 },
+/*cd*/ { "", FALSE, NONE, 0, 0 },
+/*ce*/ { "", FALSE, NONE, 0, 0 },
+/*cf*/ { "", FALSE, NONE, 0, 0 },
+};
+
+struct inst *db_inst_0f[] = {
+ db_inst_0f0x,
+ 0,
+ db_inst_0f2x,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ db_inst_0f8x,
+ db_inst_0f9x,
+ db_inst_0fax,
+ db_inst_0fbx,
+ db_inst_0fcx,
+ db_inst_0fdx,
+ 0,
+ 0
+};
+
+char * db_Esc92[] = {
+ "fnop", "", "", "", "", "", "", ""
+};
+char * db_Esc93[] = {
+ "", "", "", "", "", "", "", ""
+};
+char * db_Esc94[] = {
+ "fchs", "fabs", "", "", "ftst", "fxam", "", ""
+};
+char * db_Esc95[] = {
+ "fld1", "fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz",""
+};
+char * db_Esc96[] = {
+ "f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp",
+ "fincstp"
+};
+char * db_Esc97[] = {
+ "fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"
+};
+
+char * db_Esca4[] = {
+ "", "fucompp","", "", "", "", "", ""
+};
+
+char * db_Escb4[] = {
+ "", "", "fnclex","fninit","", "", "", ""
+};
+
+char * db_Esce3[] = {
+ "", "fcompp","", "", "", "", "", ""
+};
+
+char * db_Escf4[] = {
+ "fnstsw","", "", "", "", "", "", ""
+};
+
+struct finst db_Esc8[] = {
+/*0*/ { "fadd", SNGL, op2(STI,ST), 0 },
+/*1*/ { "fmul", SNGL, op2(STI,ST), 0 },
+/*2*/ { "fcom", SNGL, op2(STI,ST), 0 },
+/*3*/ { "fcomp", SNGL, op2(STI,ST), 0 },
+/*4*/ { "fsub", SNGL, op2(STI,ST), 0 },
+/*5*/ { "fsubr", SNGL, op2(STI,ST), 0 },
+/*6*/ { "fdiv", SNGL, op2(STI,ST), 0 },
+/*7*/ { "fdivr", SNGL, op2(STI,ST), 0 },
+};
+
+struct finst db_Esc9[] = {
+/*0*/ { "fld", SNGL, op1(STI), 0 },
+/*1*/ { "", NONE, op1(STI), "fxch" },
+/*2*/ { "fst", SNGL, op1(X), (char *)db_Esc92 },
+/*3*/ { "fstp", SNGL, op1(X), (char *)db_Esc93 },
+/*4*/ { "fldenv", NONE, op1(X), (char *)db_Esc94 },
+/*5*/ { "fldcw", NONE, op1(X), (char *)db_Esc95 },
+/*6*/ { "fnstenv",NONE, op1(X), (char *)db_Esc96 },
+/*7*/ { "fnstcw", NONE, op1(X), (char *)db_Esc97 },
+};
+
+struct finst db_Esca[] = {
+/*0*/ { "fiadd", WORD, 0, 0 },
+/*1*/ { "fimul", WORD, 0, 0 },
+/*2*/ { "ficom", WORD, 0, 0 },
+/*3*/ { "ficomp", WORD, 0, 0 },
+/*4*/ { "fisub", WORD, op1(X), (char *)db_Esca4 },
+/*5*/ { "fisubr", WORD, 0, 0 },
+/*6*/ { "fidiv", WORD, 0, 0 },
+/*7*/ { "fidivr", WORD, 0, 0 }
+};
+
+struct finst db_Escb[] = {
+/*0*/ { "fild", WORD, 0, 0 },
+/*1*/ { "", NONE, 0, 0 },
+/*2*/ { "fist", WORD, 0, 0 },
+/*3*/ { "fistp", WORD, 0, 0 },
+/*4*/ { "", WORD, op1(X), (char *)db_Escb4 },
+/*5*/ { "fld", EXTR, 0, 0 },
+/*6*/ { "", WORD, 0, 0 },
+/*7*/ { "fstp", EXTR, 0, 0 },
+};
+
+struct finst db_Escc[] = {
+/*0*/ { "fadd", DBLR, op2(ST,STI), 0 },
+/*1*/ { "fmul", DBLR, op2(ST,STI), 0 },
+/*2*/ { "fcom", DBLR, op2(ST,STI), 0 },
+/*3*/ { "fcomp", DBLR, op2(ST,STI), 0 },
+/*4*/ { "fsub", DBLR, op2(ST,STI), "fsubr" },
+/*5*/ { "fsubr", DBLR, op2(ST,STI), "fsub" },
+/*6*/ { "fdiv", DBLR, op2(ST,STI), "fdivr" },
+/*7*/ { "fdivr", DBLR, op2(ST,STI), "fdiv" },
+};
+
+struct finst db_Escd[] = {
+/*0*/ { "fld", DBLR, op1(STI), "ffree" },
+/*1*/ { "", NONE, 0, 0 },
+/*2*/ { "fst", DBLR, op1(STI), 0 },
+/*3*/ { "fstp", DBLR, op1(STI), 0 },
+/*4*/ { "frstor", NONE, op1(STI), "fucom" },
+/*5*/ { "", NONE, op1(STI), "fucomp" },
+/*6*/ { "fnsave", NONE, 0, 0 },
+/*7*/ { "fnstsw", NONE, 0, 0 },
+};
+
+struct finst db_Esce[] = {
+/*0*/ { "fiadd", LONG, op2(ST,STI), "faddp" },
+/*1*/ { "fimul", LONG, op2(ST,STI), "fmulp" },
+/*2*/ { "ficom", LONG, 0, 0 },
+/*3*/ { "ficomp", LONG, op1(X), (char *)db_Esce3 },
+/*4*/ { "fisub", LONG, op2(ST,STI), "fsubrp" },
+/*5*/ { "fisubr", LONG, op2(ST,STI), "fsubp" },
+/*6*/ { "fidiv", LONG, op2(ST,STI), "fdivrp" },
+/*7*/ { "fidivr", LONG, op2(ST,STI), "fdivp" },
+};
+
+struct finst db_Escf[] = {
+/*0*/ { "fild", LONG, 0, 0 },
+/*1*/ { "", LONG, 0, 0 },
+/*2*/ { "fist", LONG, 0, 0 },
+/*3*/ { "fistp", LONG, 0, 0 },
+/*4*/ { "fbld", NONE, op1(XA), (char *)db_Escf4 },
+/*5*/ { "fld", QUAD, 0, 0 },
+/*6*/ { "fbstp", NONE, 0, 0 },
+/*7*/ { "fstp", QUAD, 0, 0 },
+};
+
+struct finst *db_Esc_inst[] = {
+ db_Esc8, db_Esc9, db_Esca, db_Escb,
+ db_Escc, db_Escd, db_Esce, db_Escf
+};
+
+char * db_Grp1[] = {
+ "add",
+ "or",
+ "adc",
+ "sbb",
+ "and",
+ "sub",
+ "xor",
+ "cmp"
+};
+
+char * db_Grp2[] = {
+ "rol",
+ "ror",
+ "rcl",
+ "rcr",
+ "shl",
+ "shr",
+ "shl",
+ "sar"
+};
+
+struct inst db_Grp3[] = {
+ { "test", TRUE, NONE, op2(I,E), 0 },
+ { "test", TRUE, NONE, op2(I,E), 0 },
+ { "not", TRUE, NONE, op1(E), 0 },
+ { "neg", TRUE, NONE, op1(E), 0 },
+ { "mul", TRUE, NONE, op2(E,A), 0 },
+ { "imul", TRUE, NONE, op2(E,A), 0 },
+ { "div", TRUE, NONE, op2(E,A), 0 },
+ { "idiv", TRUE, NONE, op2(E,A), 0 },
+};
+
+struct inst db_Grp4[] = {
+ { "inc", TRUE, BYTE, op1(E), 0 },
+ { "dec", TRUE, BYTE, op1(E), 0 },
+ { "", TRUE, NONE, 0, 0 },
+ { "", TRUE, NONE, 0, 0 },
+ { "", TRUE, NONE, 0, 0 },
+ { "", TRUE, NONE, 0, 0 },
+ { "", TRUE, NONE, 0, 0 },
+ { "", TRUE, NONE, 0, 0 }
+};
+
+struct inst db_Grp5[] = {
+ { "inc", TRUE, LONG, op1(E), 0 },
+ { "dec", TRUE, LONG, op1(E), 0 },
+ { "call", TRUE, NONE, op1(Eind),0 },
+ { "lcall", TRUE, NONE, op1(Eind),0 },
+ { "jmp", TRUE, NONE, op1(Eind),0 },
+ { "ljmp", TRUE, NONE, op1(Eind),0 },
+ { "push", TRUE, LONG, op1(E), 0 },
+ { "", TRUE, NONE, 0, 0 }
+};
+
+struct inst db_inst_table[256] = {
+/*00*/ { "add", TRUE, BYTE, op2(R, E), 0 },
+/*01*/ { "add", TRUE, LONG, op2(R, E), 0 },
+/*02*/ { "add", TRUE, BYTE, op2(E, R), 0 },
+/*03*/ { "add", TRUE, LONG, op2(E, R), 0 },
+/*04*/ { "add", FALSE, BYTE, op2(Is, A), 0 },
+/*05*/ { "add", FALSE, LONG, op2(Is, A), 0 },
+/*06*/ { "push", FALSE, NONE, op1(Si), 0 },
+/*07*/ { "pop", FALSE, NONE, op1(Si), 0 },
+
+/*08*/ { "or", TRUE, BYTE, op2(R, E), 0 },
+/*09*/ { "or", TRUE, LONG, op2(R, E), 0 },
+/*0a*/ { "or", TRUE, BYTE, op2(E, R), 0 },
+/*0b*/ { "or", TRUE, LONG, op2(E, R), 0 },
+/*0c*/ { "or", FALSE, BYTE, op2(I, A), 0 },
+/*0d*/ { "or", FALSE, LONG, op2(I, A), 0 },
+/*0e*/ { "push", FALSE, NONE, op1(Si), 0 },
+/*0f*/ { "", FALSE, NONE, 0, 0 },
+
+/*10*/ { "adc", TRUE, BYTE, op2(R, E), 0 },
+/*11*/ { "adc", TRUE, LONG, op2(R, E), 0 },
+/*12*/ { "adc", TRUE, BYTE, op2(E, R), 0 },
+/*13*/ { "adc", TRUE, LONG, op2(E, R), 0 },
+/*14*/ { "adc", FALSE, BYTE, op2(Is, A), 0 },
+/*15*/ { "adc", FALSE, LONG, op2(Is, A), 0 },
+/*16*/ { "push", FALSE, NONE, op1(Si), 0 },
+/*17*/ { "pop", FALSE, NONE, op1(Si), 0 },
+
+/*18*/ { "sbb", TRUE, BYTE, op2(R, E), 0 },
+/*19*/ { "sbb", TRUE, LONG, op2(R, E), 0 },
+/*1a*/ { "sbb", TRUE, BYTE, op2(E, R), 0 },
+/*1b*/ { "sbb", TRUE, LONG, op2(E, R), 0 },
+/*1c*/ { "sbb", FALSE, BYTE, op2(Is, A), 0 },
+/*1d*/ { "sbb", FALSE, LONG, op2(Is, A), 0 },
+/*1e*/ { "push", FALSE, NONE, op1(Si), 0 },
+/*1f*/ { "pop", FALSE, NONE, op1(Si), 0 },
+
+/*20*/ { "and", TRUE, BYTE, op2(R, E), 0 },
+/*21*/ { "and", TRUE, LONG, op2(R, E), 0 },
+/*22*/ { "and", TRUE, BYTE, op2(E, R), 0 },
+/*23*/ { "and", TRUE, LONG, op2(E, R), 0 },
+/*24*/ { "and", FALSE, BYTE, op2(I, A), 0 },
+/*25*/ { "and", FALSE, LONG, op2(I, A), 0 },
+/*26*/ { "", FALSE, NONE, 0, 0 },
+/*27*/ { "aaa", FALSE, NONE, 0, 0 },
+
+/*28*/ { "sub", TRUE, BYTE, op2(R, E), 0 },
+/*29*/ { "sub", TRUE, LONG, op2(R, E), 0 },
+/*2a*/ { "sub", TRUE, BYTE, op2(E, R), 0 },
+/*2b*/ { "sub", TRUE, LONG, op2(E, R), 0 },
+/*2c*/ { "sub", FALSE, BYTE, op2(Is, A), 0 },
+/*2d*/ { "sub", FALSE, LONG, op2(Is, A), 0 },
+/*2e*/ { "", FALSE, NONE, 0, 0 },
+/*2f*/ { "das", FALSE, NONE, 0, 0 },
+
+/*30*/ { "xor", TRUE, BYTE, op2(R, E), 0 },
+/*31*/ { "xor", TRUE, LONG, op2(R, E), 0 },
+/*32*/ { "xor", TRUE, BYTE, op2(E, R), 0 },
+/*33*/ { "xor", TRUE, LONG, op2(E, R), 0 },
+/*34*/ { "xor", FALSE, BYTE, op2(I, A), 0 },
+/*35*/ { "xor", FALSE, LONG, op2(I, A), 0 },
+/*36*/ { "", FALSE, NONE, 0, 0 },
+/*37*/ { "daa", FALSE, NONE, 0, 0 },
+
+/*38*/ { "cmp", TRUE, BYTE, op2(R, E), 0 },
+/*39*/ { "cmp", TRUE, LONG, op2(R, E), 0 },
+/*3a*/ { "cmp", TRUE, BYTE, op2(E, R), 0 },
+/*3b*/ { "cmp", TRUE, LONG, op2(E, R), 0 },
+/*3c*/ { "cmp", FALSE, BYTE, op2(Is, A), 0 },
+/*3d*/ { "cmp", FALSE, LONG, op2(Is, A), 0 },
+/*3e*/ { "", FALSE, NONE, 0, 0 },
+/*3f*/ { "aas", FALSE, NONE, 0, 0 },
+
+/*40*/ { "inc", FALSE, LONG, op1(Ri), 0 },
+/*41*/ { "inc", FALSE, LONG, op1(Ri), 0 },
+/*42*/ { "inc", FALSE, LONG, op1(Ri), 0 },
+/*43*/ { "inc", FALSE, LONG, op1(Ri), 0 },
+/*44*/ { "inc", FALSE, LONG, op1(Ri), 0 },
+/*45*/ { "inc", FALSE, LONG, op1(Ri), 0 },
+/*46*/ { "inc", FALSE, LONG, op1(Ri), 0 },
+/*47*/ { "inc", FALSE, LONG, op1(Ri), 0 },
+
+/*48*/ { "dec", FALSE, LONG, op1(Ri), 0 },
+/*49*/ { "dec", FALSE, LONG, op1(Ri), 0 },
+/*4a*/ { "dec", FALSE, LONG, op1(Ri), 0 },
+/*4b*/ { "dec", FALSE, LONG, op1(Ri), 0 },
+/*4c*/ { "dec", FALSE, LONG, op1(Ri), 0 },
+/*4d*/ { "dec", FALSE, LONG, op1(Ri), 0 },
+/*4e*/ { "dec", FALSE, LONG, op1(Ri), 0 },
+/*4f*/ { "dec", FALSE, LONG, op1(Ri), 0 },
+
+/*50*/ { "push", FALSE, LONG, op1(Ri), 0 },
+/*51*/ { "push", FALSE, LONG, op1(Ri), 0 },
+/*52*/ { "push", FALSE, LONG, op1(Ri), 0 },
+/*53*/ { "push", FALSE, LONG, op1(Ri), 0 },
+/*54*/ { "push", FALSE, LONG, op1(Ri), 0 },
+/*55*/ { "push", FALSE, LONG, op1(Ri), 0 },
+/*56*/ { "push", FALSE, LONG, op1(Ri), 0 },
+/*57*/ { "push", FALSE, LONG, op1(Ri), 0 },
+
+/*58*/ { "pop", FALSE, LONG, op1(Ri), 0 },
+/*59*/ { "pop", FALSE, LONG, op1(Ri), 0 },
+/*5a*/ { "pop", FALSE, LONG, op1(Ri), 0 },
+/*5b*/ { "pop", FALSE, LONG, op1(Ri), 0 },
+/*5c*/ { "pop", FALSE, LONG, op1(Ri), 0 },
+/*5d*/ { "pop", FALSE, LONG, op1(Ri), 0 },
+/*5e*/ { "pop", FALSE, LONG, op1(Ri), 0 },
+/*5f*/ { "pop", FALSE, LONG, op1(Ri), 0 },
+
+/*60*/ { "pusha", FALSE, LONG, 0, 0 },
+/*61*/ { "popa", FALSE, LONG, 0, 0 },
+/*62*/ { "bound", TRUE, LONG, op2(E, R), 0 },
+/*63*/ { "arpl", TRUE, NONE, op2(Ew,Rw), 0 },
+
+/*64*/ { "", FALSE, NONE, 0, 0 },
+/*65*/ { "", FALSE, NONE, 0, 0 },
+/*66*/ { "", FALSE, NONE, 0, 0 },
+/*67*/ { "", FALSE, NONE, 0, 0 },
+
+/*68*/ { "push", FALSE, LONG, op1(I), 0 },
+/*69*/ { "imul", TRUE, LONG, op3(I,E,R), 0 },
+/*6a*/ { "push", FALSE, LONG, op1(Ib), 0 },
+/*6b*/ { "imul", TRUE, LONG, op3(Ibs,E,R),0 },
+/*6c*/ { "ins", FALSE, BYTE, op2(DX, DI), 0 },
+/*6d*/ { "ins", FALSE, LONG, op2(DX, DI), 0 },
+/*6e*/ { "outs", FALSE, BYTE, op2(SI, DX), 0 },
+/*6f*/ { "outs", FALSE, LONG, op2(SI, DX), 0 },
+
+/*70*/ { "jo", FALSE, NONE, op1(Db), 0 },
+/*71*/ { "jno", FALSE, NONE, op1(Db), 0 },
+/*72*/ { "jb", FALSE, NONE, op1(Db), 0 },
+/*73*/ { "jnb", FALSE, NONE, op1(Db), 0 },
+/*74*/ { "jz", FALSE, NONE, op1(Db), 0 },
+/*75*/ { "jnz", FALSE, NONE, op1(Db), 0 },
+/*76*/ { "jbe", FALSE, NONE, op1(Db), 0 },
+/*77*/ { "jnbe", FALSE, NONE, op1(Db), 0 },
+
+/*78*/ { "js", FALSE, NONE, op1(Db), 0 },
+/*79*/ { "jns", FALSE, NONE, op1(Db), 0 },
+/*7a*/ { "jp", FALSE, NONE, op1(Db), 0 },
+/*7b*/ { "jnp", FALSE, NONE, op1(Db), 0 },
+/*7c*/ { "jl", FALSE, NONE, op1(Db), 0 },
+/*7d*/ { "jnl", FALSE, NONE, op1(Db), 0 },
+/*7e*/ { "jle", FALSE, NONE, op1(Db), 0 },
+/*7f*/ { "jnle", FALSE, NONE, op1(Db), 0 },
+
+/*80*/ { "", TRUE, BYTE, op2(I, E), (char *)db_Grp1 },
+/*81*/ { "", TRUE, LONG, op2(I, E), (char *)db_Grp1 },
+/*82*/ { "", TRUE, BYTE, op2(Is,E), (char *)db_Grp1 },
+/*83*/ { "", TRUE, LONG, op2(Ibs,E), (char *)db_Grp1 },
+/*84*/ { "test", TRUE, BYTE, op2(R, E), 0 },
+/*85*/ { "test", TRUE, LONG, op2(R, E), 0 },
+/*86*/ { "xchg", TRUE, BYTE, op2(R, E), 0 },
+/*87*/ { "xchg", TRUE, LONG, op2(R, E), 0 },
+
+/*88*/ { "mov", TRUE, BYTE, op2(R, E), 0 },
+/*89*/ { "mov", TRUE, LONG, op2(R, E), 0 },
+/*8a*/ { "mov", TRUE, BYTE, op2(E, R), 0 },
+/*8b*/ { "mov", TRUE, LONG, op2(E, R), 0 },
+/*8c*/ { "mov", TRUE, NONE, op2(S, Ew), 0 },
+/*8d*/ { "lea", TRUE, LONG, op2(E, R), 0 },
+/*8e*/ { "mov", TRUE, NONE, op2(Ew, S), 0 },
+/*8f*/ { "pop", TRUE, LONG, op1(E), 0 },
+
+/*90*/ { "nop", FALSE, NONE, 0, 0 },
+/*91*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 },
+/*92*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 },
+/*93*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 },
+/*94*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 },
+/*95*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 },
+/*96*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 },
+/*97*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 },
+
+/*98*/ { "cbw", FALSE, SDEP, 0, "cwde" }, /* cbw/cwde */
+/*99*/ { "cwd", FALSE, SDEP, 0, "cdq" }, /* cwd/cdq */
+/*9a*/ { "lcall", FALSE, NONE, op1(OS), 0 },
+/*9b*/ { "wait", FALSE, NONE, 0, 0 },
+/*9c*/ { "pushf", FALSE, LONG, 0, 0 },
+/*9d*/ { "popf", FALSE, LONG, 0, 0 },
+/*9e*/ { "sahf", FALSE, NONE, 0, 0 },
+/*9f*/ { "lahf", FALSE, NONE, 0, 0 },
+
+/*a0*/ { "mov", FALSE, BYTE, op2(O, A), 0 },
+/*a1*/ { "mov", FALSE, LONG, op2(O, A), 0 },
+/*a2*/ { "mov", FALSE, BYTE, op2(A, O), 0 },
+/*a3*/ { "mov", FALSE, LONG, op2(A, O), 0 },
+/*a4*/ { "movs", FALSE, BYTE, op2(SI,DI), 0 },
+/*a5*/ { "movs", FALSE, LONG, op2(SI,DI), 0 },
+/*a6*/ { "cmps", FALSE, BYTE, op2(SI,DI), 0 },
+/*a7*/ { "cmps", FALSE, LONG, op2(SI,DI), 0 },
+
+/*a8*/ { "test", FALSE, BYTE, op2(I, A), 0 },
+/*a9*/ { "test", FALSE, LONG, op2(I, A), 0 },
+/*aa*/ { "stos", FALSE, BYTE, op1(DI), 0 },
+/*ab*/ { "stos", FALSE, LONG, op1(DI), 0 },
+/*ac*/ { "lods", FALSE, BYTE, op1(SI), 0 },
+/*ad*/ { "lods", FALSE, LONG, op1(SI), 0 },
+/*ae*/ { "scas", FALSE, BYTE, op1(SI), 0 },
+/*af*/ { "scas", FALSE, LONG, op1(SI), 0 },
+
+/*b0*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 },
+/*b1*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 },
+/*b2*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 },
+/*b3*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 },
+/*b4*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 },
+/*b5*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 },
+/*b6*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 },
+/*b7*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 },
+
+/*b8*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
+/*b9*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
+/*ba*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
+/*bb*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
+/*bc*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
+/*bd*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
+/*be*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
+/*bf*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
+
+/*c0*/ { "", TRUE, BYTE, op2(Ib, E), (char *)db_Grp2 },
+/*c1*/ { "", TRUE, LONG, op2(Ib, E), (char *)db_Grp2 },
+/*c2*/ { "ret", FALSE, NONE, op1(Iw), 0 },
+/*c3*/ { "ret", FALSE, NONE, 0, 0 },
+/*c4*/ { "les", TRUE, LONG, op2(E, R), 0 },
+/*c5*/ { "lds", TRUE, LONG, op2(E, R), 0 },
+/*c6*/ { "mov", TRUE, BYTE, op2(I, E), 0 },
+/*c7*/ { "mov", TRUE, LONG, op2(I, E), 0 },
+
+/*c8*/ { "enter", FALSE, NONE, op2(Ib, Iw), 0 },
+/*c9*/ { "leave", FALSE, NONE, 0, 0 },
+/*ca*/ { "lret", FALSE, NONE, op1(Iw), 0 },
+/*cb*/ { "lret", FALSE, NONE, 0, 0 },
+/*cc*/ { "int", FALSE, NONE, op1(o3), 0 },
+/*cd*/ { "int", FALSE, NONE, op1(Ib), 0 },
+/*ce*/ { "into", FALSE, NONE, 0, 0 },
+/*cf*/ { "iret", FALSE, NONE, 0, 0 },
+
+/*d0*/ { "", TRUE, BYTE, op2(o1, E), (char *)db_Grp2 },
+/*d1*/ { "", TRUE, LONG, op2(o1, E), (char *)db_Grp2 },
+/*d2*/ { "", TRUE, BYTE, op2(CL, E), (char *)db_Grp2 },
+/*d3*/ { "", TRUE, LONG, op2(CL, E), (char *)db_Grp2 },
+/*d4*/ { "aam", TRUE, NONE, 0, 0 },
+/*d5*/ { "aad", TRUE, NONE, 0, 0 },
+/*d6*/ { "", FALSE, NONE, 0, 0 },
+/*d7*/ { "xlat", FALSE, BYTE, op1(BX), 0 },
+
+/*d8*/ { "", TRUE, NONE, 0, (char *)db_Esc8 },
+/*d9*/ { "", TRUE, NONE, 0, (char *)db_Esc9 },
+/*da*/ { "", TRUE, NONE, 0, (char *)db_Esca },
+/*db*/ { "", TRUE, NONE, 0, (char *)db_Escb },
+/*dc*/ { "", TRUE, NONE, 0, (char *)db_Escc },
+/*dd*/ { "", TRUE, NONE, 0, (char *)db_Escd },
+/*de*/ { "", TRUE, NONE, 0, (char *)db_Esce },
+/*df*/ { "", TRUE, NONE, 0, (char *)db_Escf },
+
+/*e0*/ { "loopne",FALSE, NONE, op1(Db), 0 },
+/*e1*/ { "loope", FALSE, NONE, op1(Db), 0 },
+/*e2*/ { "loop", FALSE, NONE, op1(Db), 0 },
+/*e3*/ { "jcxz", FALSE, SDEP, op1(Db), "jecxz" },
+/*e4*/ { "in", FALSE, BYTE, op2(Ib, A), 0 },
+/*e5*/ { "in", FALSE, LONG, op2(Ib, A) , 0 },
+/*e6*/ { "out", FALSE, BYTE, op2(A, Ib), 0 },
+/*e7*/ { "out", FALSE, LONG, op2(A, Ib) , 0 },
+
+/*e8*/ { "call", FALSE, NONE, op1(Dl), 0 },
+/*e9*/ { "jmp", FALSE, NONE, op1(Dl), 0 },
+/*ea*/ { "ljmp", FALSE, NONE, op1(OS), 0 },
+/*eb*/ { "jmp", FALSE, NONE, op1(Db), 0 },
+/*ec*/ { "in", FALSE, BYTE, op2(DX, A), 0 },
+/*ed*/ { "in", FALSE, LONG, op2(DX, A) , 0 },
+/*ee*/ { "out", FALSE, BYTE, op2(A, DX), 0 },
+/*ef*/ { "out", FALSE, LONG, op2(A, DX) , 0 },
+
+/*f0*/ { "", FALSE, NONE, 0, 0 },
+/*f1*/ { "", FALSE, NONE, 0, 0 },
+/*f2*/ { "", FALSE, NONE, 0, 0 },
+/*f3*/ { "", FALSE, NONE, 0, 0 },
+/*f4*/ { "hlt", FALSE, NONE, 0, 0 },
+/*f5*/ { "cmc", FALSE, NONE, 0, 0 },
+/*f6*/ { "", TRUE, BYTE, 0, (char *)db_Grp3 },
+/*f7*/ { "", TRUE, LONG, 0, (char *)db_Grp3 },
+
+/*f8*/ { "clc", FALSE, NONE, 0, 0 },
+/*f9*/ { "stc", FALSE, NONE, 0, 0 },
+/*fa*/ { "cli", FALSE, NONE, 0, 0 },
+/*fb*/ { "sti", FALSE, NONE, 0, 0 },
+/*fc*/ { "cld", FALSE, NONE, 0, 0 },
+/*fd*/ { "std", FALSE, NONE, 0, 0 },
+/*fe*/ { "", TRUE, NONE, 0, (char *)db_Grp4 },
+/*ff*/ { "", TRUE, NONE, 0, (char *)db_Grp5 },
+};
+
+struct inst db_bad_inst =
+ { "???", FALSE, NONE, 0, 0 }
+;
+
+#define f_mod(byte) ((byte)>>6)
+#define f_reg(byte) (((byte)>>3)&0x7)
+#define f_rm(byte) ((byte)&0x7)
+
+#define sib_ss(byte) ((byte)>>6)
+#define sib_index(byte) (((byte)>>3)&0x7)
+#define sib_base(byte) ((byte)&0x7)
+
+struct i_addr {
+ int is_reg; /* if reg, reg number is in 'disp' */
+ int disp;
+ char * base;
+ char * index;
+ int ss;
+};
+
+char * db_index_reg_16[8] = {
+ "%bx,%si",
+ "%bx,%di",
+ "%bp,%si",
+ "%bp,%di",
+ "%si",
+ "%di",
+ "%bp",
+ "%bx"
+};
+
+char * db_reg[3][8] = {
+ "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh",
+ "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di",
+ "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi"
+};
+
+char * db_seg_reg[8] = {
+ "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "", ""
+};
+
+/*
+ * lengths for size attributes
+ */
+int db_lengths[] = {
+ 1, /* BYTE */
+ 2, /* WORD */
+ 4, /* LONG */
+ 8, /* QUAD */
+ 4, /* SNGL */
+ 8, /* DBLR */
+ 10, /* EXTR */
+};
+
+#define get_value_inc(result, loc, size, is_signed) \
+ result = db_get_value((loc), (size), (is_signed)); \
+ (loc) += (size);
+
+/*
+ * Read address at location and return updated location.
+ */
+db_addr_t
+db_read_address(loc, short_addr, regmodrm, addrp)
+ db_addr_t loc;
+ int short_addr;
+ int regmodrm;
+ struct i_addr *addrp; /* out */
+{
+ int mod, rm, sib, index, ss, disp;
+
+ mod = f_mod(regmodrm);
+ rm = f_rm(regmodrm);
+
+ if (mod == 3) {
+ addrp->is_reg = TRUE;
+ addrp->disp = rm;
+ return (loc);
+ }
+ addrp->is_reg = FALSE;
+ addrp->index = 0;
+
+ if (short_addr) {
+ addrp->index = 0;
+ addrp->ss = 0;
+ switch (mod) {
+ case 0:
+ if (rm == 6) {
+ get_value_inc(disp, loc, 2, TRUE);
+ addrp->disp = disp;
+ addrp->base = 0;
+ }
+ else {
+ addrp->disp = 0;
+ addrp->base = db_index_reg_16[rm];
+ }
+ break;
+ case 1:
+ get_value_inc(disp, loc, 1, TRUE);
+ addrp->disp = disp;
+ addrp->base = db_index_reg_16[rm];
+ break;
+ case 2:
+ get_value_inc(disp, loc, 2, TRUE);
+ addrp->disp = disp;
+ addrp->base = db_index_reg_16[rm];
+ break;
+ }
+ }
+ else {
+ if (mod != 3 && rm == 4) {
+ get_value_inc(sib, loc, 1, FALSE);
+ rm = sib_base(sib);
+ index = sib_index(sib);
+ if (index != 4)
+ addrp->index = db_reg[LONG][index];
+ addrp->ss = sib_ss(sib);
+ }
+
+ switch (mod) {
+ case 0:
+ if (rm == 5) {
+ get_value_inc(addrp->disp, loc, 4, FALSE);
+ addrp->base = 0;
+ }
+ else {
+ addrp->disp = 0;
+ addrp->base = db_reg[LONG][rm];
+ }
+ break;
+
+ case 1:
+ get_value_inc(disp, loc, 1, TRUE);
+ addrp->disp = disp;
+ addrp->base = db_reg[LONG][rm];
+ break;
+
+ case 2:
+ get_value_inc(disp, loc, 4, FALSE);
+ addrp->disp = disp;
+ addrp->base = db_reg[LONG][rm];
+ break;
+ }
+ }
+ return (loc);
+}
+
+void
+db_print_address(seg, size, addrp)
+ char * seg;
+ int size;
+ struct i_addr *addrp;
+{
+ if (addrp->is_reg) {
+ db_printf("%s", db_reg[size][addrp->disp]);
+ return;
+ }
+
+ if (seg) {
+ db_printf("%s:", seg);
+ }
+
+ db_printsym((db_addr_t)addrp->disp, DB_STGY_ANY);
+ if (addrp->base != 0 || addrp->index != 0) {
+ db_printf("(");
+ if (addrp->base)
+ db_printf("%s", addrp->base);
+ if (addrp->index)
+ db_printf(",%s,%d", addrp->index, 1<<addrp->ss);
+ db_printf(")");
+ }
+}
+
+/*
+ * Disassemble floating-point ("escape") instruction
+ * and return updated location.
+ */
+db_addr_t
+db_disasm_esc(loc, inst, short_addr, size, seg)
+ db_addr_t loc;
+ int inst;
+ int short_addr;
+ int size;
+ char * seg;
+{
+ int regmodrm;
+ struct finst *fp;
+ int mod;
+ struct i_addr address;
+ char * name;
+
+ get_value_inc(regmodrm, loc, 1, FALSE);
+ fp = &db_Esc_inst[inst - 0xd8][f_reg(regmodrm)];
+ mod = f_mod(regmodrm);
+ if (mod != 3) {
+ /*
+ * Normal address modes.
+ */
+ loc = db_read_address(loc, short_addr, regmodrm, &address);
+ db_printf(fp->f_name);
+ switch(fp->f_size) {
+ case SNGL:
+ db_printf("s");
+ break;
+ case DBLR:
+ db_printf("l");
+ break;
+ case EXTR:
+ db_printf("t");
+ break;
+ case WORD:
+ db_printf("s");
+ break;
+ case LONG:
+ db_printf("l");
+ break;
+ case QUAD:
+ db_printf("q");
+ break;
+ default:
+ break;
+ }
+ db_printf("\t");
+ db_print_address(seg, BYTE, &address);
+ }
+ else {
+ /*
+ * 'reg-reg' - special formats
+ */
+ switch (fp->f_rrmode) {
+ case op2(ST,STI):
+ name = (fp->f_rrname) ? fp->f_rrname : fp->f_name;
+ db_printf("%s\t%%st,%%st(%d)",name,f_rm(regmodrm));
+ break;
+ case op2(STI,ST):
+ name = (fp->f_rrname) ? fp->f_rrname : fp->f_name;
+ db_printf("%s\t%%st(%d),%%st",name, f_rm(regmodrm));
+ break;
+ case op1(STI):
+ name = (fp->f_rrname) ? fp->f_rrname : fp->f_name;
+ db_printf("%s\t%%st(%d)",name, f_rm(regmodrm));
+ break;
+ case op1(X):
+ db_printf("%s", ((char **)fp->f_rrname)[f_rm(regmodrm)]);
+ break;
+ case op1(XA):
+ db_printf("%s\t%%ax",
+ ((char **)fp->f_rrname)[f_rm(regmodrm)]);
+ break;
+ default:
+ db_printf("<bad instruction>");
+ break;
+ }
+ }
+
+ return (loc);
+}
+
+/*
+ * Disassemble instruction at 'loc'. 'altfmt' specifies an
+ * (optional) alternate format. Return address of start of
+ * next instruction.
+ */
+db_addr_t
+db_disasm(loc, altfmt)
+ db_addr_t loc;
+ boolean_t altfmt;
+{
+ int inst;
+ int size;
+ int short_addr;
+ char * seg;
+ struct inst * ip;
+ char * i_name;
+ int i_size;
+ int i_mode;
+ int regmodrm = 0;
+ boolean_t first;
+ int displ;
+ int prefix;
+ int imm;
+ int imm2;
+ int len;
+ struct i_addr address;
+
+ get_value_inc(inst, loc, 1, FALSE);
+ short_addr = FALSE;
+ size = LONG;
+ seg = 0;
+
+ /*
+ * Get prefixes
+ */
+ prefix = TRUE;
+ do {
+ switch (inst) {
+ case 0x66: /* data16 */
+ size = WORD;
+ break;
+ case 0x67:
+ short_addr = TRUE;
+ break;
+ case 0x26:
+ seg = "%es";
+ break;
+ case 0x36:
+ seg = "%ss";
+ break;
+ case 0x2e:
+ seg = "%cs";
+ break;
+ case 0x3e:
+ seg = "%ds";
+ break;
+ case 0x64:
+ seg = "%fs";
+ break;
+ case 0x65:
+ seg = "%gs";
+ break;
+ case 0xf0:
+ db_printf("lock ");
+ break;
+ case 0xf2:
+ db_printf("repne ");
+ break;
+ case 0xf3:
+ db_printf("repe "); /* XXX repe VS rep */
+ break;
+ default:
+ prefix = FALSE;
+ break;
+ }
+ if (prefix) {
+ get_value_inc(inst, loc, 1, FALSE);
+ }
+ } while (prefix);
+
+ if (inst >= 0xd8 && inst <= 0xdf) {
+ loc = db_disasm_esc(loc, inst, short_addr, size, seg);
+ db_printf("\n");
+ return (loc);
+ }
+
+ if (inst == 0x0f) {
+ get_value_inc(inst, loc, 1, FALSE);
+ ip = db_inst_0f[inst>>4];
+ if (ip == 0) {
+ ip = &db_bad_inst;
+ }
+ else {
+ ip = &ip[inst&0xf];
+ }
+ }
+ else
+ ip = &db_inst_table[inst];
+
+ if (ip->i_has_modrm) {
+ get_value_inc(regmodrm, loc, 1, FALSE);
+ loc = db_read_address(loc, short_addr, regmodrm, &address);
+ }
+
+ i_name = ip->i_name;
+ i_size = ip->i_size;
+ i_mode = ip->i_mode;
+
+ if (ip->i_extra == (char *)db_Grp1 ||
+ ip->i_extra == (char *)db_Grp2 ||
+ ip->i_extra == (char *)db_Grp6 ||
+ ip->i_extra == (char *)db_Grp7 ||
+ ip->i_extra == (char *)db_Grp8) {
+ i_name = ((char **)ip->i_extra)[f_reg(regmodrm)];
+ }
+ else if (ip->i_extra == (char *)db_Grp3) {
+ ip = (struct inst *)ip->i_extra;
+ ip = &ip[f_reg(regmodrm)];
+ i_name = ip->i_name;
+ i_mode = ip->i_mode;
+ }
+ else if (ip->i_extra == (char *)db_Grp4 ||
+ ip->i_extra == (char *)db_Grp5) {
+ ip = (struct inst *)ip->i_extra;
+ ip = &ip[f_reg(regmodrm)];
+ i_name = ip->i_name;
+ i_mode = ip->i_mode;
+ i_size = ip->i_size;
+ }
+
+ if (i_size == SDEP) {
+ if (size == WORD)
+ db_printf(i_name);
+ else
+ db_printf(ip->i_extra);
+ }
+ else {
+ db_printf(i_name);
+ if (i_size != NONE) {
+ if (i_size == BYTE) {
+ db_printf("b");
+ size = BYTE;
+ }
+ else if (i_size == WORD) {
+ db_printf("w");
+ size = WORD;
+ }
+ else if (size == WORD)
+ db_printf("w");
+ else
+ db_printf("l");
+ }
+ }
+ db_printf("\t");
+ for (first = TRUE;
+ i_mode != 0;
+ i_mode >>= 8, first = FALSE)
+ {
+ if (!first)
+ db_printf(",");
+
+ switch (i_mode & 0xFF) {
+
+ case E:
+ db_print_address(seg, size, &address);
+ break;
+
+ case Eind:
+ db_printf("*");
+ db_print_address(seg, size, &address);
+ break;
+
+ case Ew:
+ db_print_address(seg, WORD, &address);
+ break;
+
+ case Eb:
+ db_print_address(seg, BYTE, &address);
+ break;
+
+ case R:
+ db_printf("%s", db_reg[size][f_reg(regmodrm)]);
+ break;
+
+ case Rw:
+ db_printf("%s", db_reg[WORD][f_reg(regmodrm)]);
+ break;
+
+ case Ri:
+ db_printf("%s", db_reg[size][f_rm(inst)]);
+ break;
+
+ case S:
+ db_printf("%s", db_seg_reg[f_reg(regmodrm)]);
+ break;
+
+ case Si:
+ db_printf("%s", db_seg_reg[f_reg(inst)]);
+ break;
+
+ case A:
+ db_printf("%s", db_reg[size][0]); /* acc */
+ break;
+
+ case BX:
+ if (seg)
+ db_printf("%s:", seg);
+ db_printf("(%s)", short_addr ? "%bx" : "%ebx");
+ break;
+
+ case CL:
+ db_printf("%%cl");
+ break;
+
+ case DX:
+ db_printf("%%dx");
+ break;
+
+ case SI:
+ if (seg)
+ db_printf("%s:", seg);
+ db_printf("(%s)", short_addr ? "%si" : "%esi");
+ break;
+
+ case DI:
+ db_printf("%%es:(%s)", short_addr ? "%di" : "%edi");
+ break;
+
+ case CR:
+ db_printf("%%cr%d", f_reg(regmodrm));
+ break;
+
+ case DR:
+ db_printf("%%dr%d", f_reg(regmodrm));
+ break;
+
+ case TR:
+ db_printf("%%tr%d", f_reg(regmodrm));
+ break;
+
+ case I:
+ len = db_lengths[size];
+ get_value_inc(imm, loc, len, FALSE);/* unsigned */
+ db_printf("$%#n", imm);
+ break;
+
+ case Is:
+ len = db_lengths[size];
+ get_value_inc(imm, loc, len, TRUE); /* signed */
+ db_printf("$%#r", imm);
+ break;
+
+ case Ib:
+ get_value_inc(imm, loc, 1, FALSE); /* unsigned */
+ db_printf("$%#n", imm);
+ break;
+
+ case Ibs:
+ get_value_inc(imm, loc, 1, TRUE); /* signed */
+ db_printf("$%#r", imm);
+ break;
+
+ case Iw:
+ get_value_inc(imm, loc, 2, FALSE); /* unsigned */
+ db_printf("$%#n", imm);
+ break;
+
+ case Il:
+ get_value_inc(imm, loc, 4, FALSE);
+ db_printf("$%#n", imm);
+ break;
+
+ case O:
+ if (short_addr) {
+ get_value_inc(displ, loc, 2, TRUE);
+ }
+ else {
+ get_value_inc(displ, loc, 4, TRUE);
+ }
+ if (seg)
+ db_printf("%s:%#r",seg, displ);
+ else
+ db_printsym((db_addr_t)displ, DB_STGY_ANY);
+ break;
+
+ case Db:
+ get_value_inc(displ, loc, 1, TRUE);
+ db_printsym((db_addr_t)(displ + loc), DB_STGY_XTRN);
+ break;
+
+ case Dl:
+ get_value_inc(displ, loc, 4, TRUE);
+ db_printsym((db_addr_t)(displ + loc), DB_STGY_XTRN);
+ break;
+
+ case o1:
+ db_printf("$1");
+ break;
+
+ case o3:
+ db_printf("$3");
+ break;
+
+ case OS:
+ get_value_inc(imm, loc, 4, FALSE); /* offset */
+ get_value_inc(imm2, loc, 2, FALSE); /* segment */
+ db_printf("$%#n,%#n", imm2, imm);
+ break;
+ }
+ }
+
+ if (altfmt == 0) {
+ if (inst == 0xe9 || inst == 0xeb) {
+ /*
+ * GAS pads to longword boundary after unconditional jumps.
+ */
+ loc = (loc + (4-1)) & ~(4-1);
+ }
+ }
+ db_printf("\n");
+ return (loc);
+}
+
diff --git a/sys/amd64/amd64/db_interface.c b/sys/amd64/amd64/db_interface.c
new file mode 100644
index 0000000..e79a2ae
--- /dev/null
+++ b/sys/amd64/amd64/db_interface.c
@@ -0,0 +1,240 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_interface.c,v 1.5 1993/12/19 00:50:00 wollman Exp $
+ */
+
+/*
+ * Interface to new debugger.
+ */
+#include "param.h"
+#include "systm.h"
+#include "kernel.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+
+#include <sys/reboot.h>
+/* #include <vm/vm_statistics.h> */
+#include <vm/pmap.h>
+
+#include <setjmp.h>
+
+int db_active = 0;
+
+db_regs_t ddb_regs;
+
+/*
+ * Received keyboard interrupt sequence.
+ */
+void
+kdb_kbd_trap(regs)
+ struct i386_saved_state *regs;
+{
+ if (db_active == 0 && (boothowto & RB_KDB)) {
+ printf("\n\nkernel: keyboard interrupt\n");
+ kdb_trap(-1, 0, regs);
+ }
+}
+
+/*
+ * kdb_trap - field a TRACE or BPT trap
+ */
+
+static jmp_buf *db_nofault = 0;
+
+int
+kdb_trap(type, code, regs)
+ int type, code;
+ register struct i386_saved_state *regs;
+{
+#if 0
+ if ((boothowto&RB_KDB) == 0)
+ return(0);
+#endif
+
+ switch (type) {
+ case T_BPTFLT /* T_INT3 */: /* breakpoint */
+ case T_KDBTRAP /* T_WATCHPOINT */: /* watchpoint */
+ case T_PRIVINFLT /* T_DEBUG */: /* single_step */
+
+ case -1: /* keyboard interrupt */
+ break;
+
+ default:
+ kdbprinttrap(type, code);
+
+ if (db_nofault) {
+ jmp_buf *no_fault = db_nofault;
+ db_nofault = 0;
+ longjmp(*no_fault, 1);
+ }
+ }
+
+ /* Should switch to kdb`s own stack here. */
+
+ ddb_regs = *regs;
+
+ if ((regs->tf_cs & 0x3) == 0) {
+ /*
+ * Kernel mode - esp and ss not saved
+ */
+ ddb_regs.tf_esp = (int)&regs->tf_esp; /* kernel stack pointer */
+#if 0
+ ddb_regs.ss = KERNEL_DS;
+#endif
+ asm(" movw %%ss,%%ax; movl %%eax,%0 "
+ : "=g" (ddb_regs.tf_ss)
+ :
+ : "ax");
+ }
+
+ db_active++;
+ cnpollc(TRUE);
+ db_trap(type, code);
+ cnpollc(FALSE);
+ db_active--;
+
+ regs->tf_eip = ddb_regs.tf_eip;
+ regs->tf_eflags = ddb_regs.tf_eflags;
+ regs->tf_eax = ddb_regs.tf_eax;
+ regs->tf_ecx = ddb_regs.tf_ecx;
+ regs->tf_edx = ddb_regs.tf_edx;
+ regs->tf_ebx = ddb_regs.tf_ebx;
+ if (regs->tf_cs & 0x3) {
+ /*
+ * user mode - saved esp and ss valid
+ */
+ regs->tf_esp = ddb_regs.tf_esp; /* user stack pointer */
+ regs->tf_ss = ddb_regs.tf_ss & 0xffff; /* user stack segment */
+ }
+ regs->tf_ebp = ddb_regs.tf_ebp;
+ regs->tf_esi = ddb_regs.tf_esi;
+ regs->tf_edi = ddb_regs.tf_edi;
+ regs->tf_es = ddb_regs.tf_es & 0xffff;
+ regs->tf_cs = ddb_regs.tf_cs & 0xffff;
+ regs->tf_ds = ddb_regs.tf_ds & 0xffff;
+#if 0
+ regs->tf_fs = ddb_regs.tf_fs & 0xffff;
+ regs->tf_gs = ddb_regs.tf_gs & 0xffff;
+#endif
+
+ return (1);
+}
+
+/*
+ * Print trap reason.
+ */
+void
+kdbprinttrap(type, code)
+ int type, code;
+{
+ printf("kernel: ");
+ printf("type %d", type);
+ printf(" trap, code=%x\n", code);
+}
+
+/*
+ * Read bytes from kernel address space for debugger.
+ */
+
+extern jmp_buf db_jmpbuf;
+
+void
+db_read_bytes(addr, size, data)
+ vm_offset_t addr;
+ register int size;
+ register char *data;
+{
+ register char *src;
+
+ db_nofault = &db_jmpbuf;
+
+ src = (char *)addr;
+ while (--size >= 0)
+ *data++ = *src++;
+
+ db_nofault = 0;
+}
+
+/*
+ * Write bytes to kernel address space for debugger.
+ */
+void
+db_write_bytes(addr, size, data)
+ vm_offset_t addr;
+ register int size;
+ register char *data;
+{
+ register char *dst;
+
+ register pt_entry_t *ptep0 = 0;
+ pt_entry_t oldmap0 = { 0 };
+ vm_offset_t addr1;
+ register pt_entry_t *ptep1 = 0;
+ pt_entry_t oldmap1 = { 0 };
+ extern char etext;
+
+ db_nofault = &db_jmpbuf;
+
+ if (addr >= VM_MIN_KERNEL_ADDRESS &&
+ addr <= (vm_offset_t)&etext)
+ {
+ ptep0 = pmap_pte(kernel_pmap, addr);
+ oldmap0 = *ptep0;
+ *(int *)ptep0 |= /* INTEL_PTE_WRITE */ PG_RW;
+
+ addr1 = i386_trunc_page(addr + size - 1);
+ if (i386_trunc_page(addr) != addr1) {
+ /* data crosses a page boundary */
+
+ ptep1 = pmap_pte(kernel_pmap, addr1);
+ oldmap1 = *ptep1;
+ *(int *)ptep1 |= /* INTEL_PTE_WRITE */ PG_RW;
+ }
+ tlbflush();
+ }
+
+ dst = (char *)addr;
+
+ while (--size >= 0)
+ *dst++ = *data++;
+
+ db_nofault = 0;
+
+ if (ptep0) {
+ *ptep0 = oldmap0;
+ if (ptep1) {
+ *ptep1 = oldmap1;
+ }
+ tlbflush();
+ }
+}
+
+void
+Debugger (msg)
+ const char *msg;
+{
+ asm ("int $3");
+}
diff --git a/sys/amd64/amd64/db_trace.c b/sys/amd64/amd64/db_trace.c
new file mode 100644
index 0000000..d536d94
--- /dev/null
+++ b/sys/amd64/amd64/db_trace.c
@@ -0,0 +1,340 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_trace.c,v 1.4 1994/01/03 07:55:19 davidg Exp $
+ */
+
+#include "param.h"
+
+#include <vm/vm_param.h>
+#include <vm/lock.h>
+#include <vm/vm_prot.h>
+#include <vm/pmap.h>
+#include <machine/pmap.h>
+#include "systm.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+
+#include <ddb/db_access.h>
+#include <ddb/db_sym.h>
+#include <ddb/db_variables.h>
+
+/*
+ * Machine register set.
+ */
+struct db_variable db_regs[] = {
+ "cs", (int *)&ddb_regs.tf_cs, FCN_NULL,
+ "ds", (int *)&ddb_regs.tf_ds, FCN_NULL,
+ "es", (int *)&ddb_regs.tf_es, FCN_NULL,
+#if 0
+ "fs", (int *)&ddb_regs.tf_fs, FCN_NULL,
+ "gs", (int *)&ddb_regs.tf_gs, FCN_NULL,
+#endif
+ "ss", (int *)&ddb_regs.tf_ss, FCN_NULL,
+ "eax", (int *)&ddb_regs.tf_eax, FCN_NULL,
+ "ecx", (int *)&ddb_regs.tf_ecx, FCN_NULL,
+ "edx", (int *)&ddb_regs.tf_edx, FCN_NULL,
+ "ebx", (int *)&ddb_regs.tf_ebx, FCN_NULL,
+ "esp", (int *)&ddb_regs.tf_esp,FCN_NULL,
+ "ebp", (int *)&ddb_regs.tf_ebp, FCN_NULL,
+ "esi", (int *)&ddb_regs.tf_esi, FCN_NULL,
+ "edi", (int *)&ddb_regs.tf_edi, FCN_NULL,
+ "eip", (int *)&ddb_regs.tf_eip, FCN_NULL,
+ "efl", (int *)&ddb_regs.tf_eflags, FCN_NULL,
+};
+struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
+
+/*
+ * Stack trace.
+ */
+#define INKERNEL(va) (((vm_offset_t)(va)) >= VM_MIN_KERNEL_ADDRESS)
+
+struct i386_frame {
+ struct i386_frame *f_frame;
+ int f_retaddr;
+ int f_arg0;
+};
+
+#define TRAP 1
+#define INTERRUPT 2
+#define SYSCALL 3
+
+db_addr_t db_trap_symbol_value = 0;
+db_addr_t db_syscall_symbol_value = 0;
+db_addr_t db_kdintr_symbol_value = 0;
+boolean_t db_trace_symbols_found = FALSE;
+
+void
+db_find_trace_symbols()
+{
+ db_expr_t value;
+ if (db_value_of_name("_trap", &value))
+ db_trap_symbol_value = (db_addr_t) value;
+ if (db_value_of_name("_kdintr", &value))
+ db_kdintr_symbol_value = (db_addr_t) value;
+ if (db_value_of_name("_syscall", &value))
+ db_syscall_symbol_value = (db_addr_t) value;
+ db_trace_symbols_found = TRUE;
+}
+
+/*
+ * Figure out how many arguments were passed into the frame at "fp".
+ */
+int
+db_numargs(fp)
+ struct i386_frame *fp;
+{
+ int *argp;
+ int inst;
+ int args;
+ extern char etext[];
+
+ argp = (int *)db_get_value((int)&fp->f_retaddr, 4, FALSE);
+ if (argp < (int *)VM_MIN_KERNEL_ADDRESS || argp > (int *)etext)
+ args = 5;
+ else {
+ inst = db_get_value((int)argp, 4, FALSE);
+ if ((inst & 0xff) == 0x59) /* popl %ecx */
+ args = 1;
+ else if ((inst & 0xffff) == 0xc483) /* addl %n, %esp */
+ args = ((inst >> 16) & 0xff) / 4;
+ else
+ args = 5;
+ }
+ return (args);
+}
+
+/*
+ * Figure out the next frame up in the call stack.
+ * For trap(), we print the address of the faulting instruction and
+ * proceed with the calling frame. We return the ip that faulted.
+ * If the trap was caused by jumping through a bogus pointer, then
+ * the next line in the backtrace will list some random function as
+ * being called. It should get the argument list correct, though.
+ * It might be possible to dig out from the next frame up the name
+ * of the function that faulted, but that could get hairy.
+ */
+void
+db_nextframe(fp, ip, argp, is_trap)
+ struct i386_frame **fp; /* in/out */
+ db_addr_t *ip; /* out */
+ int *argp; /* in */
+ int is_trap; /* in */
+{
+ struct i386_saved_state *saved_regs;
+
+ switch (is_trap) {
+ case 0:
+ *ip = (db_addr_t)
+ db_get_value((int) &(*fp)->f_retaddr, 4, FALSE);
+ *fp = (struct i386_frame *)
+ db_get_value((int) &(*fp)->f_frame, 4, FALSE);
+ break;
+ case TRAP:
+ default:
+ /*
+ * We know that trap() has 1 argument and we know that
+ * it is an (int *).
+ */
+#if 0
+ saved_regs = (struct i386_saved_state *)
+ db_get_value((int)argp, 4, FALSE);
+#endif
+ saved_regs = (struct i386_saved_state *)argp;
+ db_printf("--- trap (number %d) ---\n",
+ saved_regs->tf_trapno & 0xffff);
+ db_printsym(saved_regs->tf_eip, DB_STGY_XTRN);
+ db_printf(":\n");
+ *fp = (struct i386_frame *)saved_regs->tf_ebp;
+ *ip = (db_addr_t)saved_regs->tf_eip;
+ break;
+
+ case SYSCALL: {
+ struct trapframe *saved_regs = (struct trapframe *)argp;
+
+ db_printf("--- syscall (number %d) ---\n", saved_regs->tf_eax);
+ db_printsym(saved_regs->tf_eip, DB_STGY_XTRN);
+ db_printf(":\n");
+ *fp = (struct i386_frame *)saved_regs->tf_ebp;
+ *ip = (db_addr_t)saved_regs->tf_eip;
+ }
+ break;
+ }
+}
+
+void
+db_stack_trace_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ boolean_t have_addr;
+ db_expr_t count;
+ char *modif;
+{
+ struct i386_frame *frame, *lastframe;
+ int *argp;
+ db_addr_t callpc;
+ int is_trap;
+ boolean_t kernel_only = TRUE;
+ boolean_t trace_thread = FALSE;
+
+#if 0
+ if (!db_trace_symbols_found)
+ db_find_trace_symbols();
+#endif
+
+ {
+ register char *cp = modif;
+ register char c;
+
+ while ((c = *cp++) != 0) {
+ if (c == 't')
+ trace_thread = TRUE;
+ if (c == 'u')
+ kernel_only = FALSE;
+ }
+ }
+
+ if (count == -1)
+ count = 65535;
+
+ if (!have_addr) {
+ frame = (struct i386_frame *)ddb_regs.tf_ebp;
+ callpc = (db_addr_t)ddb_regs.tf_eip;
+ }
+ else if (trace_thread) {
+ printf ("db_trace.c: can't trace thread\n");
+ }
+ else {
+ frame = (struct i386_frame *)addr;
+ callpc = (db_addr_t)db_get_value((int)&frame->f_retaddr, 4, FALSE);
+ }
+
+ lastframe = 0;
+ while (count-- && frame != 0) {
+ int narg;
+ char * name;
+ db_expr_t offset;
+ db_sym_t sym;
+#define MAXNARG 16
+ char *argnames[MAXNARG], **argnp = NULL;
+
+ sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
+ db_symbol_values(sym, &name, NULL);
+
+ if (lastframe == 0 && sym == NULL) {
+ /* Symbol not found, peek at code */
+ int instr = db_get_value(callpc, 4, FALSE);
+
+ offset = 1;
+ if ((instr & 0x00ffffff) == 0x00e58955 ||
+ /* enter: pushl %ebp, movl %esp, %ebp */
+ (instr & 0x0000ffff) == 0x0000e589
+ /* enter+1: movl %esp, %ebp */ ) {
+ offset = 0;
+ }
+ }
+#define STRCMP(s1,s2) ((s1) && (s2) && strcmp((s1), (s2)) == 0)
+ if (INKERNEL((int)frame) && STRCMP(name, "_trap")) {
+ narg = 1;
+ is_trap = TRAP;
+ }
+ else
+ if (INKERNEL((int)frame) && STRCMP(name, "_kdintr")) {
+ is_trap = INTERRUPT;
+ narg = 0;
+ }
+ else
+ if (INKERNEL((int)frame) && STRCMP(name, "_syscall")) {
+ is_trap = SYSCALL;
+ narg = 0;
+ }
+#undef STRCMP
+ else {
+ is_trap = 0;
+ narg = MAXNARG;
+ if (db_sym_numargs(sym, &narg, argnames)) {
+ argnp = argnames;
+ } else {
+ narg = db_numargs(frame);
+ }
+ }
+
+ db_printf("%s(", name);
+
+ if (lastframe == 0 && offset == 0 && !have_addr) {
+ /*
+ * We have a breakpoint before the frame is set up
+ * Use %esp instead
+ */
+ argp = &((struct i386_frame *)(ddb_regs.tf_esp-4))->f_arg0;
+ } else
+ argp = &frame->f_arg0;
+
+ while (narg) {
+ if (argnp)
+ db_printf("%s=", *argnp++);
+ db_printf("%x", db_get_value((int)argp, 4, FALSE));
+ argp++;
+ if (--narg != 0)
+ db_printf(",");
+ }
+ db_printf(") at ");
+ db_printsym(callpc, DB_STGY_PROC);
+ db_printf("\n");
+
+ if (lastframe == 0 && offset == 0 && !have_addr) {
+ /* Frame really belongs to next callpc */
+ lastframe = (struct i386_frame *)(ddb_regs.tf_esp-4);
+ callpc = (db_addr_t)db_get_value((int)&lastframe->f_retaddr, 4, FALSE);
+ continue;
+ }
+
+ lastframe = frame;
+ db_nextframe(&frame, &callpc, &frame->f_arg0, is_trap);
+
+ if (frame == 0) {
+ /* end of chain */
+ break;
+ }
+ if (INKERNEL((int)frame)) {
+ /* staying in kernel */
+ if (frame <= lastframe) {
+ db_printf("Bad frame pointer: 0x%x\n", frame);
+ break;
+ }
+ }
+ else if (INKERNEL((int)lastframe)) {
+ /* switch from user to kernel */
+ if (kernel_only)
+ break; /* kernel stack only */
+ }
+ else {
+ /* in user */
+ if (frame <= lastframe) {
+ db_printf("Bad user frame pointer: 0x%x\n", frame);
+ break;
+ }
+ }
+ }
+}
diff --git a/sys/amd64/amd64/exception.S b/sys/amd64/amd64/exception.S
new file mode 100644
index 0000000..30bc164
--- /dev/null
+++ b/sys/amd64/amd64/exception.S
@@ -0,0 +1,275 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: exception.s,v 1.2 1994/01/03 07:55:20 davidg Exp $
+ */
+
+#include "npx.h" /* NNPX */
+
+#include "assym.s" /* system defines */
+
+#include "errno.h" /* error return codes */
+
+#include "machine/spl.h" /* SWI_AST_MASK ... */
+
+#include "machine/psl.h" /* PSL_I */
+
+#include "machine/trap.h" /* trap codes */
+#include "syscall.h" /* syscall numbers */
+
+#include "machine/asmacros.h" /* miscellaneous macros */
+
+#define KDSEL 0x10 /* kernel data selector */
+#define SEL_RPL_MASK 0x0003
+#define TRAPF_CS_OFF (13 * 4)
+
+ .text
+
+/*****************************************************************************/
+/* Trap handling */
+/*****************************************************************************/
+/*
+ * Trap and fault vector routines
+ */
+#define IDTVEC(name) ALIGN_TEXT ; .globl _X/**/name ; _X/**/name:
+#define TRAP(a) pushl $(a) ; jmp _alltraps
+
+/*
+ * XXX - debugger traps are now interrupt gates so at least bdb doesn't lose
+ * control. The sti's give the standard losing behaviour for ddb and kgdb.
+ */
+#ifdef BDE_DEBUGGER
+#define BDBTRAP(name) \
+ ss ; \
+ cmpb $0,_bdb_exists ; \
+ je 1f ; \
+ testb $SEL_RPL_MASK,4(%esp) ; \
+ jne 1f ; \
+ ss ; \
+ .globl bdb_/**/name/**/_ljmp ; \
+bdb_/**/name/**/_ljmp: ; \
+ ljmp $0,$0 ; \
+1:
+#else
+#define BDBTRAP(name)
+#endif
+
+#ifdef KGDB
+# define BPTTRAP(a) testl $PSL_I,4+8(%esp) ; je 1f ; sti ; 1: ; \
+ pushl $(a) ; jmp _bpttraps
+#else
+# define BPTTRAP(a) testl $PSL_I,4+8(%esp) ; je 1f ; sti ; 1: ; TRAP(a)
+#endif
+
+MCOUNT_LABEL(user)
+MCOUNT_LABEL(btrap)
+
+IDTVEC(div)
+ pushl $0; TRAP(T_DIVIDE)
+IDTVEC(dbg)
+ BDBTRAP(dbg)
+ pushl $0; BPTTRAP(T_TRCTRAP)
+IDTVEC(nmi)
+ pushl $0; TRAP(T_NMI)
+IDTVEC(bpt)
+ BDBTRAP(bpt)
+ pushl $0; BPTTRAP(T_BPTFLT)
+IDTVEC(ofl)
+ pushl $0; TRAP(T_OFLOW)
+IDTVEC(bnd)
+ pushl $0; TRAP(T_BOUND)
+IDTVEC(ill)
+ pushl $0; TRAP(T_PRIVINFLT)
+IDTVEC(dna)
+ pushl $0; TRAP(T_DNA)
+IDTVEC(dble)
+ TRAP(T_DOUBLEFLT)
+IDTVEC(fpusegm)
+ pushl $0; TRAP(T_FPOPFLT)
+IDTVEC(tss)
+ TRAP(T_TSSFLT)
+IDTVEC(missing)
+ TRAP(T_SEGNPFLT)
+IDTVEC(stk)
+ TRAP(T_STKFLT)
+IDTVEC(prot)
+ TRAP(T_PROTFLT)
+IDTVEC(page)
+ TRAP(T_PAGEFLT)
+IDTVEC(rsvd)
+ pushl $0; TRAP(T_RESERVED)
+IDTVEC(fpu)
+#if NNPX > 0
+ /*
+ * Handle like an interrupt so that we can call npxintr to clear the
+ * error. It would be better to handle npx interrupts as traps but
+ * this is difficult for nested interrupts.
+ */
+ pushl $0 /* dumby error code */
+ pushl $0 /* dumby trap type */
+ pushal
+ pushl %ds
+ pushl %es /* now the stack frame is a trap frame */
+ movl $KDSEL,%eax
+ movl %ax,%ds
+ movl %ax,%es
+ FAKE_MCOUNT(12*4(%esp))
+ movl _cpl,%eax
+ pushl %eax
+ pushl $0 /* dummy unit to finish building intr frame */
+ incl _cnt+V_TRAP
+ orl $SWI_AST_MASK,%eax
+ movl %eax,_cpl
+ call _npxintr
+ MEXITCOUNT
+ jmp _doreti
+#else /* NNPX > 0 */
+ pushl $0; TRAP(T_ARITHTRAP)
+#endif /* NNPX > 0 */
+ /* 17 - 31 reserved for future exp */
+IDTVEC(rsvd0)
+ pushl $0; TRAP(17)
+IDTVEC(rsvd1)
+ pushl $0; TRAP(18)
+IDTVEC(rsvd2)
+ pushl $0; TRAP(19)
+IDTVEC(rsvd3)
+ pushl $0; TRAP(20)
+IDTVEC(rsvd4)
+ pushl $0; TRAP(21)
+IDTVEC(rsvd5)
+ pushl $0; TRAP(22)
+IDTVEC(rsvd6)
+ pushl $0; TRAP(23)
+IDTVEC(rsvd7)
+ pushl $0; TRAP(24)
+IDTVEC(rsvd8)
+ pushl $0; TRAP(25)
+IDTVEC(rsvd9)
+ pushl $0; TRAP(26)
+IDTVEC(rsvd10)
+ pushl $0; TRAP(27)
+IDTVEC(rsvd11)
+ pushl $0; TRAP(28)
+IDTVEC(rsvd12)
+ pushl $0; TRAP(29)
+IDTVEC(rsvd13)
+ pushl $0; TRAP(30)
+IDTVEC(rsvd14)
+ pushl $0; TRAP(31)
+
+ SUPERALIGN_TEXT
+_alltraps:
+ pushal
+ pushl %ds
+ pushl %es
+ movl $KDSEL,%eax
+ movl %ax,%ds
+ movl %ax,%es
+ FAKE_MCOUNT(12*4(%esp))
+calltrap:
+ FAKE_MCOUNT(_btrap) /* init "from" _btrap -> calltrap */
+ incl _cnt+V_TRAP
+ orl $SWI_AST_MASK,_cpl
+ call _trap
+ /*
+ * There was no place to save the cpl so we have to recover it
+ * indirectly. For traps from user mode it was 0, and for traps
+ * from kernel mode Oring SWI_AST_MASK into it didn't change it.
+ */
+ subl %eax,%eax
+ testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp)
+ jne 1f
+ movl _cpl,%eax
+1:
+ /*
+ * Return via _doreti to handle ASTs. Have to change trap frame
+ * to interrupt frame.
+ */
+ pushl %eax
+ subl $4,%esp
+ MEXITCOUNT
+ jmp _doreti
+
+#ifdef KGDB
+/*
+ * This code checks for a kgdb trap, then falls through
+ * to the regular trap code.
+ */
+ SUPERALIGN_TEXT
+_bpttraps:
+ pushal
+ pushl %ds
+ pushl %es
+ movl $KDSEL,%eax
+ movl %ax,%ds
+ movl %ax,%es
+ FAKE_MCOUNT(12*4(%esp))
+ testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp) /* non-kernel mode? */
+ jne calltrap /* yes */
+ call _kgdb_trap_glue
+ MEXITCOUNT
+ jmp calltrap
+#endif
+
+/*
+ * Call gate entry for syscall
+ */
+ SUPERALIGN_TEXT
+IDTVEC(syscall)
+ pushfl /* Room for tf_err */
+ pushfl /* Room for tf_trapno */
+ pushal
+ pushl %ds
+ pushl %es
+ movl $KDSEL,%eax /* switch to kernel segments */
+ movl %ax,%ds
+ movl %ax,%es
+ movl TF_ERR(%esp),%eax /* copy eflags from tf_err to fs_eflags */
+ movl %eax,TF_EFLAGS(%esp)
+ FAKE_MCOUNT(12*4(%esp))
+ incl _cnt+V_SYSCALL
+ movl $SWI_AST_MASK,_cpl
+ call _syscall
+ /*
+ * Return via _doreti to handle ASTs.
+ */
+ pushl $0 /* cpl to restore */
+ subl $4,%esp
+ MEXITCOUNT
+ jmp _doreti
+
+/*
+ * include generated interrupt vectors and ISA intr code
+ */
+#include "i386/isa/vector.s"
+#include "i386/isa/icu.s"
diff --git a/sys/amd64/amd64/exception.s b/sys/amd64/amd64/exception.s
new file mode 100644
index 0000000..30bc164
--- /dev/null
+++ b/sys/amd64/amd64/exception.s
@@ -0,0 +1,275 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: exception.s,v 1.2 1994/01/03 07:55:20 davidg Exp $
+ */
+
+#include "npx.h" /* NNPX */
+
+#include "assym.s" /* system defines */
+
+#include "errno.h" /* error return codes */
+
+#include "machine/spl.h" /* SWI_AST_MASK ... */
+
+#include "machine/psl.h" /* PSL_I */
+
+#include "machine/trap.h" /* trap codes */
+#include "syscall.h" /* syscall numbers */
+
+#include "machine/asmacros.h" /* miscellaneous macros */
+
+#define KDSEL 0x10 /* kernel data selector */
+#define SEL_RPL_MASK 0x0003
+#define TRAPF_CS_OFF (13 * 4)
+
+ .text
+
+/*****************************************************************************/
+/* Trap handling */
+/*****************************************************************************/
+/*
+ * Trap and fault vector routines
+ */
+#define IDTVEC(name) ALIGN_TEXT ; .globl _X/**/name ; _X/**/name:
+#define TRAP(a) pushl $(a) ; jmp _alltraps
+
+/*
+ * XXX - debugger traps are now interrupt gates so at least bdb doesn't lose
+ * control. The sti's give the standard losing behaviour for ddb and kgdb.
+ */
+#ifdef BDE_DEBUGGER
+#define BDBTRAP(name) \
+ ss ; \
+ cmpb $0,_bdb_exists ; \
+ je 1f ; \
+ testb $SEL_RPL_MASK,4(%esp) ; \
+ jne 1f ; \
+ ss ; \
+ .globl bdb_/**/name/**/_ljmp ; \
+bdb_/**/name/**/_ljmp: ; \
+ ljmp $0,$0 ; \
+1:
+#else
+#define BDBTRAP(name)
+#endif
+
+#ifdef KGDB
+# define BPTTRAP(a) testl $PSL_I,4+8(%esp) ; je 1f ; sti ; 1: ; \
+ pushl $(a) ; jmp _bpttraps
+#else
+# define BPTTRAP(a) testl $PSL_I,4+8(%esp) ; je 1f ; sti ; 1: ; TRAP(a)
+#endif
+
+MCOUNT_LABEL(user)
+MCOUNT_LABEL(btrap)
+
+IDTVEC(div)
+ pushl $0; TRAP(T_DIVIDE)
+IDTVEC(dbg)
+ BDBTRAP(dbg)
+ pushl $0; BPTTRAP(T_TRCTRAP)
+IDTVEC(nmi)
+ pushl $0; TRAP(T_NMI)
+IDTVEC(bpt)
+ BDBTRAP(bpt)
+ pushl $0; BPTTRAP(T_BPTFLT)
+IDTVEC(ofl)
+ pushl $0; TRAP(T_OFLOW)
+IDTVEC(bnd)
+ pushl $0; TRAP(T_BOUND)
+IDTVEC(ill)
+ pushl $0; TRAP(T_PRIVINFLT)
+IDTVEC(dna)
+ pushl $0; TRAP(T_DNA)
+IDTVEC(dble)
+ TRAP(T_DOUBLEFLT)
+IDTVEC(fpusegm)
+ pushl $0; TRAP(T_FPOPFLT)
+IDTVEC(tss)
+ TRAP(T_TSSFLT)
+IDTVEC(missing)
+ TRAP(T_SEGNPFLT)
+IDTVEC(stk)
+ TRAP(T_STKFLT)
+IDTVEC(prot)
+ TRAP(T_PROTFLT)
+IDTVEC(page)
+ TRAP(T_PAGEFLT)
+IDTVEC(rsvd)
+ pushl $0; TRAP(T_RESERVED)
+IDTVEC(fpu)
+#if NNPX > 0
+ /*
+ * Handle like an interrupt so that we can call npxintr to clear the
+ * error. It would be better to handle npx interrupts as traps but
+ * this is difficult for nested interrupts.
+ */
+ pushl $0 /* dumby error code */
+ pushl $0 /* dumby trap type */
+ pushal
+ pushl %ds
+ pushl %es /* now the stack frame is a trap frame */
+ movl $KDSEL,%eax
+ movl %ax,%ds
+ movl %ax,%es
+ FAKE_MCOUNT(12*4(%esp))
+ movl _cpl,%eax
+ pushl %eax
+ pushl $0 /* dummy unit to finish building intr frame */
+ incl _cnt+V_TRAP
+ orl $SWI_AST_MASK,%eax
+ movl %eax,_cpl
+ call _npxintr
+ MEXITCOUNT
+ jmp _doreti
+#else /* NNPX > 0 */
+ pushl $0; TRAP(T_ARITHTRAP)
+#endif /* NNPX > 0 */
+ /* 17 - 31 reserved for future exp */
+IDTVEC(rsvd0)
+ pushl $0; TRAP(17)
+IDTVEC(rsvd1)
+ pushl $0; TRAP(18)
+IDTVEC(rsvd2)
+ pushl $0; TRAP(19)
+IDTVEC(rsvd3)
+ pushl $0; TRAP(20)
+IDTVEC(rsvd4)
+ pushl $0; TRAP(21)
+IDTVEC(rsvd5)
+ pushl $0; TRAP(22)
+IDTVEC(rsvd6)
+ pushl $0; TRAP(23)
+IDTVEC(rsvd7)
+ pushl $0; TRAP(24)
+IDTVEC(rsvd8)
+ pushl $0; TRAP(25)
+IDTVEC(rsvd9)
+ pushl $0; TRAP(26)
+IDTVEC(rsvd10)
+ pushl $0; TRAP(27)
+IDTVEC(rsvd11)
+ pushl $0; TRAP(28)
+IDTVEC(rsvd12)
+ pushl $0; TRAP(29)
+IDTVEC(rsvd13)
+ pushl $0; TRAP(30)
+IDTVEC(rsvd14)
+ pushl $0; TRAP(31)
+
+ SUPERALIGN_TEXT
+_alltraps:
+ pushal
+ pushl %ds
+ pushl %es
+ movl $KDSEL,%eax
+ movl %ax,%ds
+ movl %ax,%es
+ FAKE_MCOUNT(12*4(%esp))
+calltrap:
+ FAKE_MCOUNT(_btrap) /* init "from" _btrap -> calltrap */
+ incl _cnt+V_TRAP
+ orl $SWI_AST_MASK,_cpl
+ call _trap
+ /*
+ * There was no place to save the cpl so we have to recover it
+ * indirectly. For traps from user mode it was 0, and for traps
+ * from kernel mode Oring SWI_AST_MASK into it didn't change it.
+ */
+ subl %eax,%eax
+ testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp)
+ jne 1f
+ movl _cpl,%eax
+1:
+ /*
+ * Return via _doreti to handle ASTs. Have to change trap frame
+ * to interrupt frame.
+ */
+ pushl %eax
+ subl $4,%esp
+ MEXITCOUNT
+ jmp _doreti
+
+#ifdef KGDB
+/*
+ * This code checks for a kgdb trap, then falls through
+ * to the regular trap code.
+ */
+ SUPERALIGN_TEXT
+_bpttraps:
+ pushal
+ pushl %ds
+ pushl %es
+ movl $KDSEL,%eax
+ movl %ax,%ds
+ movl %ax,%es
+ FAKE_MCOUNT(12*4(%esp))
+ testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp) /* non-kernel mode? */
+ jne calltrap /* yes */
+ call _kgdb_trap_glue
+ MEXITCOUNT
+ jmp calltrap
+#endif
+
+/*
+ * Call gate entry for syscall
+ */
+ SUPERALIGN_TEXT
+IDTVEC(syscall)
+ pushfl /* Room for tf_err */
+ pushfl /* Room for tf_trapno */
+ pushal
+ pushl %ds
+ pushl %es
+ movl $KDSEL,%eax /* switch to kernel segments */
+ movl %ax,%ds
+ movl %ax,%es
+ movl TF_ERR(%esp),%eax /* copy eflags from tf_err to fs_eflags */
+ movl %eax,TF_EFLAGS(%esp)
+ FAKE_MCOUNT(12*4(%esp))
+ incl _cnt+V_SYSCALL
+ movl $SWI_AST_MASK,_cpl
+ call _syscall
+ /*
+ * Return via _doreti to handle ASTs.
+ */
+ pushl $0 /* cpl to restore */
+ subl $4,%esp
+ MEXITCOUNT
+ jmp _doreti
+
+/*
+ * include generated interrupt vectors and ISA intr code
+ */
+#include "i386/isa/vector.s"
+#include "i386/isa/icu.s"
diff --git a/sys/amd64/amd64/fpu.c b/sys/amd64/amd64/fpu.c
new file mode 100644
index 0000000..a3ce1e2
--- /dev/null
+++ b/sys/amd64/amd64/fpu.c
@@ -0,0 +1,554 @@
+/*-
+ * Copyright (c) 1990 William Jolitz.
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)npx.c 7.2 (Berkeley) 5/12/91
+ * $Id: npx.c,v 1.6 1994/01/03 07:55:43 davidg Exp $
+ */
+
+#include "npx.h"
+#if NNPX > 0
+
+#include "param.h"
+#include "systm.h"
+#include "conf.h"
+#include "file.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+#include "machine/trap.h"
+#include "ioctl.h"
+#include "machine/specialreg.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/isa.h"
+
+/*
+ * 387 and 287 Numeric Coprocessor Extension (NPX) Driver.
+ */
+
+#ifdef __GNUC__
+
+#define disable_intr() __asm("cli")
+#define enable_intr() __asm("sti")
+#define fldcw(addr) __asm("fldcw %0" : : "m" (*addr))
+#define fnclex() __asm("fnclex")
+#define fninit() __asm("fninit")
+#define fnsave(addr) __asm("fnsave %0" : "=m" (*addr) : "0" (*addr))
+#define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr) : "0" (*addr))
+#define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr) : "0" (*addr))
+#define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fwait")
+#define frstor(addr) __asm("frstor %0" : : "m" (*addr))
+#define fwait() __asm("fwait")
+#define read_eflags() ({u_long ef; \
+ __asm("pushf; popl %0" : "=a" (ef)); \
+ ef; })
+#define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \
+ : : "n" (CR0_TS) : "ax")
+#define stop_emulating() __asm("clts")
+#define write_eflags(ef) __asm("pushl %0; popf" : : "a" ((u_long) ef))
+
+#else /* not __GNUC__ */
+
+void disable_intr __P((void));
+void enable_intr __P((void));
+void fldcw __P((caddr_t addr));
+void fnclex __P((void));
+void fninit __P((void));
+void fnsave __P((caddr_t addr));
+void fnstcw __P((caddr_t addr));
+void fnstsw __P((caddr_t addr));
+void fp_divide_by_0 __P((void));
+void frstor __P((caddr_t addr));
+void fwait __P((void));
+u_long read_eflags __P((void));
+void start_emulating __P((void));
+void stop_emulating __P((void));
+void write_eflags __P((u_long ef));
+
+#endif /* __GNUC__ */
+
+typedef u_char bool_t;
+
+extern struct gate_descriptor idt[];
+
+int npxdna __P((void));
+void npxexit __P((struct proc *p));
+void npxinit __P((u_int control));
+void npxintr __P((struct intrframe frame));
+void npxsave __P((struct save87 *addr));
+static int npxattach __P((struct isa_device *dvp));
+static int npxprobe __P((struct isa_device *dvp));
+static int npxprobe1 __P((struct isa_device *dvp));
+
+struct isa_driver npxdriver = {
+ npxprobe, npxattach, "npx",
+};
+
+u_int npx0_imask;
+struct proc *npxproc;
+
+static bool_t npx_ex16;
+static bool_t npx_exists;
+static struct gate_descriptor npx_idt_probeintr;
+static int npx_intrno;
+static volatile u_int npx_intrs_while_probing;
+static bool_t npx_irq13;
+static volatile u_int npx_traps_while_probing;
+
+/*
+ * Special interrupt handlers. Someday intr0-intr15 will be used to count
+ * interrupts. We'll still need a special exception 16 handler. The busy
+ * latch stuff in probintr() can be moved to npxprobe().
+ */
+void probeintr(void);
+asm
+("
+ .text
+_probeintr:
+ ss
+ incl _npx_intrs_while_probing
+ pushl %eax
+ movb $0x20,%al # EOI (asm in strings loses cpp features)
+ outb %al,$0xa0 # IO_ICU2
+ outb %al,$0x20 #IO_ICU1
+ movb $0,%al
+ outb %al,$0xf0 # clear BUSY# latch
+ popl %eax
+ iret
+");
+
+void probetrap(void);
+asm
+("
+ .text
+_probetrap:
+ ss
+ incl _npx_traps_while_probing
+ fnclex
+ iret
+");
+
+/*
+ * Probe routine. Initialize cr0 to give correct behaviour for [f]wait
+ * whether the device exists or not (XXX should be elsewhere). Set flags
+ * to tell npxattach() what to do. Modify device struct if npx doesn't
+ * need to use interrupts. Return 1 if device exists.
+ */
+static int
+npxprobe(dvp)
+ struct isa_device *dvp;
+{
+ int result;
+ u_long save_eflags;
+ u_char save_icu1_mask;
+ u_char save_icu2_mask;
+ struct gate_descriptor save_idt_npxintr;
+ struct gate_descriptor save_idt_npxtrap;
+ /*
+ * This routine is now just a wrapper for npxprobe1(), to install
+ * special npx interrupt and trap handlers, to enable npx interrupts
+ * and to disable other interrupts. Someday isa_configure() will
+ * install suitable handlers and run with interrupts enabled so we
+ * won't need to do so much here.
+ */
+ npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1;
+ save_eflags = read_eflags();
+ disable_intr();
+ save_icu1_mask = inb(IO_ICU1 + 1);
+ save_icu2_mask = inb(IO_ICU2 + 1);
+ save_idt_npxintr = idt[npx_intrno];
+ save_idt_npxtrap = idt[16];
+ outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq));
+ outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8));
+ setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL);
+ setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL);
+ npx_idt_probeintr = idt[npx_intrno];
+ enable_intr();
+ result = npxprobe1(dvp);
+ disable_intr();
+ outb(IO_ICU1 + 1, save_icu1_mask);
+ outb(IO_ICU2 + 1, save_icu2_mask);
+ idt[npx_intrno] = save_idt_npxintr;
+ idt[16] = save_idt_npxtrap;
+ write_eflags(save_eflags);
+ return (result);
+}
+
+static int
+npxprobe1(dvp)
+ struct isa_device *dvp;
+{
+ int control;
+ int status;
+#ifdef lint
+ npxintr();
+#endif
+ /*
+ * Partially reset the coprocessor, if any. Some BIOS's don't reset
+ * it after a warm boot.
+ */
+ outb(0xf1, 0); /* full reset on some systems, NOP on others */
+ outb(0xf0, 0); /* clear BUSY# latch */
+ /*
+ * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT
+ * instructions. We must set the CR0_MP bit and use the CR0_TS
+ * bit to control the trap, because setting the CR0_EM bit does
+ * not cause WAIT instructions to trap. It's important to trap
+ * WAIT instructions - otherwise the "wait" variants of no-wait
+ * control instructions would degenerate to the "no-wait" variants
+ * after FP context switches but work correctly otherwise. It's
+ * particularly important to trap WAITs when there is no NPX -
+ * otherwise the "wait" variants would always degenerate.
+ *
+ * Try setting CR0_NE to get correct error reporting on 486DX's.
+ * Setting it should fail or do nothing on lesser processors.
+ */
+ load_cr0(rcr0() | CR0_MP | CR0_NE);
+ /*
+ * But don't trap while we're probing.
+ */
+ stop_emulating();
+ /*
+ * Finish resetting the coprocessor, if any. If there is an error
+ * pending, then we may get a bogus IRQ13, but probeintr() will handle
+ * it OK. Bogus halts have never been observed, but we enabled
+ * IRQ13 and cleared the BUSY# latch early to handle them anyway.
+ */
+ fninit();
+ DELAY(1000); /* wait for any IRQ13 (fwait might hang) */
+#ifdef DIAGNOSTIC
+ if (npx_intrs_while_probing != 0)
+ printf("fninit caused %u bogus npx interrupt(s)\n",
+ npx_intrs_while_probing);
+ if (npx_traps_while_probing != 0)
+ printf("fninit caused %u bogus npx trap(s)\n",
+ npx_traps_while_probing);
+#endif
+ /*
+ * Check for a status of mostly zero.
+ */
+ status = 0x5a5a;
+ fnstsw(&status);
+ if ((status & 0xb8ff) == 0) {
+ /*
+ * Good, now check for a proper control word.
+ */
+ control = 0x5a5a;
+ fnstcw(&control);
+ if ((control & 0x1f3f) == 0x033f) {
+ npx_exists = 1;
+ /*
+ * We have an npx, now divide by 0 to see if exception
+ * 16 works.
+ */
+ control &= ~(1 << 2); /* enable divide by 0 trap */
+ fldcw(&control);
+ npx_traps_while_probing = npx_intrs_while_probing = 0;
+ fp_divide_by_0();
+ if (npx_traps_while_probing != 0) {
+ /*
+ * Good, exception 16 works.
+ */
+ npx_ex16 = 1;
+ dvp->id_irq = 0; /* zap the interrupt */
+ /*
+ * special return value to flag that we do not
+ * actually use any I/O registers
+ */
+ return (-1);
+ }
+ if (npx_intrs_while_probing != 0) {
+ /*
+ * Bad, we are stuck with IRQ13.
+ */
+ npx_irq13 = 1;
+ npx0_imask = dvp->id_irq; /* npxattach too late */
+ return (IO_NPXSIZE);
+ }
+ /*
+ * Worse, even IRQ13 is broken. Use emulator.
+ */
+ }
+ }
+ /*
+ * Probe failed, but we want to get to npxattach to initialize the
+ * emulator and say that it has been installed. XXX handle devices
+ * that aren't really devices better.
+ */
+ dvp->id_irq = 0;
+ /*
+ * special return value to flag that we do not
+ * actually use any I/O registers
+ */
+ return (-1);
+}
+
+/*
+ * Attach routine - announce which it is, and wire into system
+ */
+int
+npxattach(dvp)
+ struct isa_device *dvp;
+{
+ if (!npx_ex16 && !npx_irq13) {
+ if (npx_exists) {
+ printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit);
+ npx_exists = 0;
+ } else {
+ printf("npx%d: 387 Emulator\n",dvp->id_unit);
+ }
+ }
+ npxinit(__INITIAL_NPXCW__);
+ return (1); /* XXX unused */
+}
+
+/*
+ * Initialize floating point unit.
+ */
+void
+npxinit(control)
+ u_int control;
+{
+ struct save87 dummy;
+
+ if (!npx_exists)
+ return;
+ /*
+ * fninit has the same h/w bugs as fnsave. Use the detoxified
+ * fnsave to throw away any junk in the fpu. fnsave initializes
+ * the fpu and sets npxproc = NULL as important side effects.
+ */
+ npxsave(&dummy);
+ stop_emulating();
+ fldcw(&control);
+ if (curpcb != NULL)
+ fnsave(&curpcb->pcb_savefpu);
+ start_emulating();
+}
+
+/*
+ * Free coprocessor (if we have it).
+ */
+void
+npxexit(p)
+ struct proc *p;
+{
+
+ if (p == npxproc) {
+ start_emulating();
+ npxproc = NULL;
+ }
+}
+
+/*
+ * Record the FPU state and reinitialize it all except for the control word.
+ * Then generate a SIGFPE.
+ *
+ * Reinitializing the state allows naive SIGFPE handlers to longjmp without
+ * doing any fixups.
+ *
+ * XXX there is currently no way to pass the full error state to signal
+ * handlers, and if this is a nested interrupt there is no way to pass even
+ * a status code! So there is no way to have a non-naive SIGFPE handler. At
+ * best a handler could do an fninit followed by an fldcw of a static value.
+ * fnclex would be of little use because it would leave junk on the FPU stack.
+ * Returning from the handler would be even less safe than usual because
+ * IRQ13 exception handling makes exceptions even less precise than usual.
+ */
+void
+npxintr(frame)
+ struct intrframe frame;
+{
+ int code;
+
+ if (npxproc == NULL || !npx_exists) {
+ /* XXX no %p in stand/printf.c. Cast to quiet gcc -Wall. */
+ printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n",
+ (u_long) npxproc, (u_long) curproc, npx_exists);
+ panic("npxintr from nowhere");
+ }
+ if (npxproc != curproc) {
+ printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n",
+ (u_long) npxproc, (u_long) curproc, npx_exists);
+ panic("npxintr from non-current process");
+ }
+ /*
+ * Save state. This does an implied fninit. It had better not halt
+ * the cpu or we'll hang.
+ */
+ outb(0xf0, 0);
+ fnsave(&curpcb->pcb_savefpu);
+ fwait();
+ /*
+ * Restore control word (was clobbered by fnsave).
+ */
+ fldcw(&curpcb->pcb_savefpu.sv_env.en_cw);
+ fwait();
+ /*
+ * Remember the exception status word and tag word. The current
+ * (almost fninit'ed) fpu state is in the fpu and the exception
+ * state just saved will soon be junk. However, the implied fninit
+ * doesn't change the error pointers or register contents, and we
+ * preserved the control word and will copy the status and tag
+ * words, so the complete exception state can be recovered.
+ */
+ curpcb->pcb_savefpu.sv_ex_sw = curpcb->pcb_savefpu.sv_env.en_sw;
+ curpcb->pcb_savefpu.sv_ex_tw = curpcb->pcb_savefpu.sv_env.en_tw;
+
+ /*
+ * Pass exception to process.
+ */
+ if (ISPL(frame.if_cs) == SEL_UPL) {
+ /*
+ * Interrupt is essentially a trap, so we can afford to call
+ * the SIGFPE handler (if any) as soon as the interrupt
+ * returns.
+ *
+ * XXX little or nothing is gained from this, and plenty is
+ * lost - the interrupt frame has to contain the trap frame
+ * (this is otherwise only necessary for the rescheduling trap
+ * in doreti, and the frame for that could easily be set up
+ * just before it is used).
+ */
+ curproc->p_md.md_regs = (int *)&frame.if_es;
+#ifdef notyet
+ /*
+ * Encode the appropriate code for detailed information on
+ * this exception.
+ */
+ code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw);
+#else
+ code = 0; /* XXX */
+#endif
+ trapsignal(curproc, SIGFPE, code);
+ } else {
+ /*
+ * Nested interrupt. These losers occur when:
+ * o an IRQ13 is bogusly generated at a bogus time, e.g.:
+ * o immediately after an fnsave or frstor of an
+ * error state.
+ * o a couple of 386 instructions after
+ * "fstpl _memvar" causes a stack overflow.
+ * These are especially nasty when combined with a
+ * trace trap.
+ * o an IRQ13 occurs at the same time as another higher-
+ * priority interrupt.
+ *
+ * Treat them like a true async interrupt.
+ */
+ psignal(npxproc, SIGFPE);
+ }
+}
+
+/*
+ * Implement device not available (DNA) exception
+ *
+ * It would be better to switch FP context here (only). This would require
+ * saving the state in the proc table instead of in the pcb.
+ */
+int
+npxdna()
+{
+ if (!npx_exists)
+ return (0);
+ if (npxproc != NULL) {
+ printf("npxdna: npxproc = %lx, curproc = %lx\n",
+ (u_long) npxproc, (u_long) curproc);
+ panic("npxdna");
+ }
+ stop_emulating();
+ /*
+ * Record new context early in case frstor causes an IRQ13.
+ */
+ npxproc = curproc;
+ /*
+ * The following frstor may cause an IRQ13 when the state being
+ * restored has a pending error. The error will appear to have been
+ * triggered by the current (npx) user instruction even when that
+ * instruction is a no-wait instruction that should not trigger an
+ * error (e.g., fnclex). On at least one 486 system all of the
+ * no-wait instructions are broken the same as frstor, so our
+ * treatment does not amplify the breakage. On at least one
+ * 386/Cyrix 387 system, fnclex works correctly while frstor and
+ * fnsave are broken, so our treatment breaks fnclex if it is the
+ * first FPU instruction after a context switch.
+ */
+ frstor(&curpcb->pcb_savefpu);
+
+ return (1);
+}
+
+/*
+ * Wrapper for fnsave instruction to handle h/w bugs. If there is an error
+ * pending, then fnsave generates a bogus IRQ13 on some systems. Force
+ * any IRQ13 to be handled immediately, and then ignore it. This routine is
+ * often called at splhigh so it must not use many system services. In
+ * particular, it's much easier to install a special handler than to
+ * guarantee that it's safe to use npxintr() and its supporting code.
+ */
+void
+npxsave(addr)
+ struct save87 *addr;
+{
+ u_char icu1_mask;
+ u_char icu2_mask;
+ u_char old_icu1_mask;
+ u_char old_icu2_mask;
+ struct gate_descriptor save_idt_npxintr;
+
+ disable_intr();
+ old_icu1_mask = inb(IO_ICU1 + 1);
+ old_icu2_mask = inb(IO_ICU2 + 1);
+ save_idt_npxintr = idt[npx_intrno];
+ outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask));
+ outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8));
+ idt[npx_intrno] = npx_idt_probeintr;
+ enable_intr();
+ stop_emulating();
+ fnsave(addr);
+ fwait();
+ start_emulating();
+ npxproc = NULL;
+ disable_intr();
+ icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */
+ icu2_mask = inb(IO_ICU2 + 1);
+ outb(IO_ICU1 + 1,
+ (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask));
+ outb(IO_ICU2 + 1,
+ (icu2_mask & ~(npx0_imask >> 8))
+ | (old_icu2_mask & (npx0_imask >> 8)));
+ idt[npx_intrno] = save_idt_npxintr;
+ enable_intr(); /* back to usual state */
+}
+
+#endif /* NNPX > 0 */
diff --git a/sys/amd64/amd64/genassym.c b/sys/amd64/amd64/genassym.c
new file mode 100644
index 0000000..a75d1f1
--- /dev/null
+++ b/sys/amd64/amd64/genassym.c
@@ -0,0 +1,192 @@
+/*-
+ * Copyright (c) 1982, 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)genassym.c 5.11 (Berkeley) 5/10/91
+ * $Id: genassym.c,v 1.6 1993/11/13 02:24:59 davidg Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/map.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/msgbuf.h>
+#include <machine/cpu.h>
+#include <machine/trap.h>
+#include <machine/psl.h>
+#include <machine/reg.h>
+#include <sys/syscall.h>
+#include <vm/vm.h>
+#include <sys/user.h>
+
+main()
+{
+ struct proc *p = (struct proc *)0;
+ struct vmmeter *vm = (struct vmmeter *)0;
+ struct user *up = (struct user *)0;
+ struct rusage *rup = (struct rusage *)0;
+ struct uprof *uprof = (struct uprof *)0;
+ struct vmspace *vms = (struct vmspace *)0;
+ vm_map_t map = (vm_map_t)0;
+ pmap_t pmap = (pmap_t)0;
+ struct pcb *pcb = (struct pcb *)0;
+ struct trapframe *tf = (struct trapframe *)0;
+ struct sigframe *sigf = (struct sigframe *)0;
+ register unsigned i;
+
+ printf("#define\tI386_CR3PAT %d\n", I386_CR3PAT);
+ printf("#define\tUDOT_SZ %d\n", sizeof(struct user));
+ printf("#define\tP_LINK %d\n", &p->p_forw);
+ printf("#define\tP_RLINK %d\n", &p->p_back);
+ printf("#define\tP_VMSPACE %d\n", &p->p_vmspace);
+ printf("#define\tVM_PMAP %d\n", &vms->vm_pmap);
+ printf("#define\tP_ADDR %d\n", &p->p_addr);
+ printf("#define\tP_PRI %d\n", &p->p_priority);
+ printf("#define\tP_STAT %d\n", &p->p_stat);
+ printf("#define\tP_WCHAN %d\n", &p->p_wchan);
+ printf("#define\tP_FLAG %d\n", &p->p_flag);
+ printf("#define\tP_PID %d\n", &p->p_pid);
+ printf("#define\tSSLEEP %d\n", SSLEEP);
+ printf("#define\tSRUN %d\n", SRUN);
+ printf("#define\tV_SWTCH %d\n", &vm->v_swtch);
+ printf("#define\tV_TRAP %d\n", &vm->v_trap);
+ printf("#define\tV_SYSCALL %d\n", &vm->v_syscall);
+ printf("#define\tV_INTR %d\n", &vm->v_intr);
+ printf("#define\tV_SOFT %d\n", &vm->v_soft);
+/* printf("#define\tV_PDMA %d\n", &vm->v_pdma); */
+ printf("#define\tV_FAULTS %d\n", &vm->v_faults);
+/* printf("#define\tV_PGREC %d\n", &vm->v_pgrec); */
+/* printf("#define\tV_FASTPGREC %d\n", &vm->v_fastpgrec); */
+ printf("#define\tUPAGES %d\n", UPAGES);
+ printf("#define\tHIGHPAGES %d\n", HIGHPAGES);
+ printf("#define\tCLSIZE %d\n", CLSIZE);
+ printf("#define\tNBPG %d\n", NBPG);
+ printf("#define\tNPTEPG %d\n", NPTEPG);
+ printf("#define\tPDESIZE %d\n", PDESIZE);
+ printf("#define\tPTESIZE %d\n", PTESIZE);
+ printf("#define\tNKPDE %d\n", NKPDE);
+ printf("#define\tNKPT %d\n", NKPT);
+ printf("#define\tKPTDI 0x%x\n", KPTDI);
+ printf("#define\tKSTKPTDI 0x%x\n", KSTKPTDI);
+ printf("#define\tKSTKPTEOFF 0x%x\n", KSTKPTEOFF);
+ printf("#define\tPTDPTDI 0x%x\n", PTDPTDI);
+ printf("#define\tAPTDPTDI 0x%x\n", APTDPTDI);
+ printf("#define\tPGSHIFT %d\n", PGSHIFT);
+ printf("#define\tPDRSHIFT %d\n", PDRSHIFT);
+ printf("#define\tSYSPTSIZE %d\n", SYSPTSIZE);
+ printf("#define\tUSRPTSIZE %d\n", USRPTSIZE);
+ printf("#define\tUSRIOSIZE %d\n", USRIOSIZE);
+#ifdef SYSVSHM
+ printf("#define\tSHMMAXPGS %d\n", SHMMAXPGS);
+#endif
+ printf("#define\tUSRSTACK 0x%x\n", USRSTACK);
+ printf("#define\tVM_MAXUSER_ADDRESS 0x%x\n", VM_MAXUSER_ADDRESS);
+ printf("#define\tKERNBASE 0x%x\n", KERNBASE);
+ printf("#define\tMSGBUFPTECNT %d\n", btoc(sizeof (struct msgbuf)));
+ printf("#define\tNMBCLUSTERS %d\n", NMBCLUSTERS);
+ printf("#define\tMCLBYTES %d\n", MCLBYTES);
+ printf("#define\tPCB_LINK %d\n", &pcb->pcb_tss.tss_link);
+ printf("#define\tPCB_ESP0 %d\n", &pcb->pcb_tss.tss_esp0);
+ printf("#define\tPCB_SS0 %d\n", &pcb->pcb_tss.tss_ss0);
+ printf("#define\tPCB_ESP1 %d\n", &pcb->pcb_tss.tss_esp1);
+ printf("#define\tPCB_SS1 %d\n", &pcb->pcb_tss.tss_ss1);
+ printf("#define\tPCB_ESP2 %d\n", &pcb->pcb_tss.tss_esp2);
+ printf("#define\tPCB_SS2 %d\n", &pcb->pcb_tss.tss_ss2);
+ printf("#define\tPCB_CR3 %d\n", &pcb->pcb_tss.tss_cr3);
+ printf("#define\tPCB_EIP %d\n", &pcb->pcb_tss.tss_eip);
+ printf("#define\tPCB_EFLAGS %d\n", &pcb->pcb_tss.tss_eflags);
+ printf("#define\tPCB_EAX %d\n", &pcb->pcb_tss.tss_eax);
+ printf("#define\tPCB_ECX %d\n", &pcb->pcb_tss.tss_ecx);
+ printf("#define\tPCB_EDX %d\n", &pcb->pcb_tss.tss_edx);
+ printf("#define\tPCB_EBX %d\n", &pcb->pcb_tss.tss_ebx);
+ printf("#define\tPCB_ESP %d\n", &pcb->pcb_tss.tss_esp);
+ printf("#define\tPCB_EBP %d\n", &pcb->pcb_tss.tss_ebp);
+ printf("#define\tPCB_ESI %d\n", &pcb->pcb_tss.tss_esi);
+ printf("#define\tPCB_EDI %d\n", &pcb->pcb_tss.tss_edi);
+ printf("#define\tPCB_ES %d\n", &pcb->pcb_tss.tss_es);
+ printf("#define\tPCB_CS %d\n", &pcb->pcb_tss.tss_cs);
+ printf("#define\tPCB_SS %d\n", &pcb->pcb_tss.tss_ss);
+ printf("#define\tPCB_DS %d\n", &pcb->pcb_tss.tss_ds);
+ printf("#define\tPCB_FS %d\n", &pcb->pcb_tss.tss_fs);
+ printf("#define\tPCB_GS %d\n", &pcb->pcb_tss.tss_gs);
+ printf("#define\tPCB_LDT %d\n", &pcb->pcb_tss.tss_ldt);
+ printf("#define\tPCB_USERLDT %d\n", &pcb->pcb_ldt);
+ printf("#define\tPCB_IOOPT %d\n", &pcb->pcb_tss.tss_ioopt);
+ printf("#define\tU_PROF %d\n", &up->u_stats.p_prof);
+ printf("#define\tU_PROFSCALE %d\n", &up->u_stats.p_prof.pr_scale);
+ printf("#define\tPR_BASE %d\n", &uprof->pr_base);
+ printf("#define\tPR_SIZE %d\n", &uprof->pr_size);
+ printf("#define\tPR_OFF %d\n", &uprof->pr_off);
+ printf("#define\tPR_SCALE %d\n", &uprof->pr_scale);
+ printf("#define\tRU_MINFLT %d\n", &rup->ru_minflt);
+ printf("#define\tPCB_FLAGS %d\n", &pcb->pcb_flags);
+ printf("#define\tPCB_SAVEFPU %d\n", &pcb->pcb_savefpu);
+ printf("#define\tFP_USESEMC %d\n", FP_USESEMC);
+ printf("#define\tPCB_SAVEEMC %d\n", &pcb->pcb_saveemc);
+ printf("#define\tPCB_CMAP2 %d\n", &pcb->pcb_cmap2);
+ printf("#define\tPCB_IML %d\n", &pcb->pcb_iml);
+ printf("#define\tPCB_ONFAULT %d\n", &pcb->pcb_onfault);
+
+ printf("#define\tTF_ES %d\n", &tf->tf_es);
+ printf("#define\tTF_DS %d\n", &tf->tf_ds);
+ printf("#define\tTF_EDI %d\n", &tf->tf_edi);
+ printf("#define\tTF_ESI %d\n", &tf->tf_esi);
+ printf("#define\tTF_EBP %d\n", &tf->tf_ebp);
+ printf("#define\tTF_ISP %d\n", &tf->tf_isp);
+ printf("#define\tTF_EBX %d\n", &tf->tf_ebx);
+ printf("#define\tTF_EDX %d\n", &tf->tf_edx);
+ printf("#define\tTF_ECX %d\n", &tf->tf_ecx);
+ printf("#define\tTF_EAX %d\n", &tf->tf_eax);
+ printf("#define\tTF_TRAPNO %d\n", &tf->tf_trapno);
+ printf("#define\tTF_ERR %d\n", &tf->tf_err);
+ printf("#define\tTF_EIP %d\n", &tf->tf_eip);
+ printf("#define\tTF_CS %d\n", &tf->tf_cs);
+ printf("#define\tTF_EFLAGS %d\n", &tf->tf_eflags);
+ printf("#define\tTF_ESP %d\n", &tf->tf_esp);
+ printf("#define\tTF_SS %d\n", &tf->tf_ss);
+
+ printf("#define\tSIGF_SIGNUM %d\n", &sigf->sf_signum);
+ printf("#define\tSIGF_CODE %d\n", &sigf->sf_code);
+ printf("#define\tSIGF_SCP %d\n", &sigf->sf_scp);
+ printf("#define\tSIGF_HANDLER %d\n", &sigf->sf_handler);
+ printf("#define\tSIGF_SC %d\n", &sigf->sf_sc);
+
+ printf("#define\tB_READ %d\n", B_READ);
+ printf("#define\tENOENT %d\n", ENOENT);
+ printf("#define\tEFAULT %d\n", EFAULT);
+ printf("#define\tENAMETOOLONG %d\n", ENAMETOOLONG);
+ exit(0);
+}
+
diff --git a/sys/amd64/amd64/locore.S b/sys/amd64/amd64/locore.S
new file mode 100644
index 0000000..538c690
--- /dev/null
+++ b/sys/amd64/amd64/locore.S
@@ -0,0 +1,518 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)locore.s 7.3 (Berkeley) 5/13/91
+ * $Id: locore.s,v 1.15 1994/02/01 04:08:54 davidg Exp $
+ */
+
+/*
+ * locore.s: FreeBSD machine support for the Intel 386
+ * originally from: locore.s, by William F. Jolitz
+ *
+ * Substantially rewritten by David Greenman, Rod Grimes,
+ * Bruce Evans, Wolfgang Solfrank, and many others.
+ */
+
+#include "npx.h" /* for NNPX */
+#include "assym.s" /* system definitions */
+#include "machine/psl.h" /* processor status longword defs */
+#include "machine/pte.h" /* page table entry definitions */
+#include "errno.h" /* error return codes */
+#include "machine/specialreg.h" /* x86 special registers */
+#include "machine/cputypes.h" /* x86 cpu type definitions */
+#include "syscall.h" /* system call numbers */
+#include "machine/asmacros.h" /* miscellaneous asm macros */
+
+/*
+ * XXX
+ *
+ * Note: This version greatly munged to avoid various assembler errors
+ * that may be fixed in newer versions of gas. Perhaps newer versions
+ * will have more pleasant appearance.
+ */
+
+/*
+ * PTmap is recursive pagemap at top of virtual address space.
+ * Within PTmap, the page directory can be found (third indirection).
+ */
+ .globl _PTmap,_PTD,_PTDpde,_Sysmap
+ .set _PTmap,PTDPTDI << PDRSHIFT
+ .set _PTD,_PTmap + (PTDPTDI * NBPG)
+ .set _PTDpde,_PTD + (PTDPTDI * PDESIZE)
+
+/* Sysmap is the base address of the kernel page tables */
+ .set _Sysmap,_PTmap + (KPTDI * NBPG)
+
+/*
+ * APTmap, APTD is the alternate recursive pagemap.
+ * It's used when modifying another process's page tables.
+ */
+ .globl _APTmap,_APTD,_APTDpde
+ .set _APTmap,APTDPTDI << PDRSHIFT
+ .set _APTD,_APTmap + (APTDPTDI * NBPG)
+ .set _APTDpde,_PTD + (APTDPTDI * PDESIZE)
+
+/*
+ * Access to each processes kernel stack is via a region of
+ * per-process address space (at the beginning), immediatly above
+ * the user process stack.
+ */
+ .set _kstack,USRSTACK
+ .globl _kstack
+
+/*
+ * Globals
+ */
+ .data
+ .globl _esym
+_esym: .long 0 /* ptr to end of syms */
+
+ .globl _boothowto,_bootdev,_curpcb
+
+ .globl _cpu,_cold,_atdevbase
+_cpu: .long 0 /* are we 386, 386sx, or 486 */
+_cold: .long 1 /* cold till we are not */
+_atdevbase: .long 0 /* location of start of iomem in virtual */
+_atdevphys: .long 0 /* location of device mapping ptes (phys) */
+
+ .globl _KERNend
+_KERNend: .long 0 /* phys addr end of kernel (just after bss) */
+
+ .globl _IdlePTD,_KPTphys
+_IdlePTD: .long 0 /* phys addr of kernel PTD */
+_KPTphys: .long 0 /* phys addr of kernel page tables */
+
+ .globl _cyloffset
+_cyloffset: .long 0 /* cylinder offset from boot blocks */
+
+ .globl _proc0paddr
+_proc0paddr: .long 0 /* address of proc 0 address space */
+
+#ifdef BDE_DEBUGGER
+ .globl _bdb_exists /* flag to indicate BDE debugger is available */
+_bdb_exists: .long 0
+#endif
+
+ .globl tmpstk
+ .space 0x1000
+tmpstk:
+
+
+/*
+ * System Initialization
+ */
+ .text
+
+/*
+ * btext: beginning of text section.
+ * Also the entry point (jumped to directly from the boot blocks).
+ */
+NON_GPROF_ENTRY(btext)
+ movw $0x1234,0x472 /* warm boot */
+ jmp 1f
+ .org 0x500 /* space for BIOS variables */
+
+ /*
+ * pass parameters on stack (howto, bootdev, unit, cyloffset, esym)
+ * note: (%esp) is return address of boot
+ * ( if we want to hold onto /boot, it's physical %esp up to _end)
+ */
+
+ 1: movl 4(%esp),%eax
+ movl %eax,_boothowto-KERNBASE
+ movl 8(%esp),%eax
+ movl %eax,_bootdev-KERNBASE
+ movl 12(%esp),%eax
+ movl %eax,_cyloffset-KERNBASE
+ movl 16(%esp),%eax
+ addl $KERNBASE,%eax
+ movl %eax,_esym-KERNBASE
+#ifdef DISKLESS /* Copy diskless structure */
+ movl _nfs_diskless_size-KERNBASE,%ecx
+ movl 20(%esp),%esi
+ movl $(_nfs_diskless-KERNBASE),%edi
+ cld
+ rep
+ movsb
+#endif
+
+ /* find out our CPU type. */
+ pushfl
+ popl %eax
+ movl %eax,%ecx
+ xorl $0x40000,%eax
+ pushl %eax
+ popfl
+ pushfl
+ popl %eax
+ xorl %ecx,%eax
+ shrl $18,%eax
+ andl $1,%eax
+ push %ecx
+ popfl
+
+ cmpl $0,%eax
+ jne 1f
+ movl $CPU_386,_cpu-KERNBASE
+ jmp 2f
+1: movl $CPU_486,_cpu-KERNBASE
+2:
+
+ /*
+ * Finished with old stack; load new %esp now instead of later so
+ * we can trace this code without having to worry about the trace
+ * trap clobbering the memory test or the zeroing of the bss+bootstrap
+ * page tables.
+ *
+ * XXX - wdboot clears the bss after testing that this is safe.
+ * This is too wasteful - memory below 640K is scarce. The boot
+ * program should check:
+ * text+data <= &stack_variable - more_space_for_stack
+ * text+data+bss+pad+space_for_page_tables <= end_of_memory
+ * Oops, the gdt is in the carcass of the boot program so clearing
+ * the rest of memory is still not possible.
+ */
+ movl $tmpstk-KERNBASE,%esp /* bootstrap stack end location */
+
+/*
+ * Virtual address space of kernel:
+ *
+ * text | data | bss | [syms] | page dir | proc0 kernel stack | usr stk map | Sysmap
+ * pages: 1 UPAGES (2) 1 NKPT (7)
+ */
+
+/* find end of kernel image */
+ movl $_end-KERNBASE,%ecx
+ addl $NBPG-1,%ecx /* page align up */
+ andl $~(NBPG-1),%ecx
+ movl %ecx,%esi /* esi = start of free memory */
+ movl %ecx,_KERNend-KERNBASE /* save end of kernel */
+
+/* clear bss */
+ movl $_edata-KERNBASE,%edi
+ subl %edi,%ecx /* get amount to clear */
+ xorl %eax,%eax /* specify zero fill */
+ cld
+ rep
+ stosb
+
+/*
+ * The value in esi is both the end of the kernel bss and a pointer to
+ * the kernel page directory, and is used by the rest of locore to build
+ * the tables.
+ * esi + 1(page dir) + 2(UPAGES) + 1(p0stack) + NKPT(number of kernel
+ * page table pages) is then passed on the stack to init386(first) as
+ * the value first. esi should ALWAYS be page aligned!!
+ */
+ movl %esi,%ecx /* Get current first availiable address */
+
+/* clear pagetables, page directory, stack, etc... */
+ movl %esi,%edi /* base (page directory) */
+ movl $((1+UPAGES+1+NKPT)*NBPG),%ecx /* amount to clear */
+ xorl %eax,%eax /* specify zero fill */
+ cld
+ rep
+ stosb
+
+/* physical address of Idle proc/kernel page directory */
+ movl %esi,_IdlePTD-KERNBASE
+
+/*
+ * fillkpt
+ * eax = (page frame address | control | status) == pte
+ * ebx = address of page table
+ * ecx = how many pages to map
+ */
+#define fillkpt \
+1: movl %eax,(%ebx) ; \
+ addl $NBPG,%eax ; /* increment physical address */ \
+ addl $4,%ebx ; /* next pte */ \
+ loop 1b ;
+
+/*
+ * Map Kernel
+ *
+ * First step - build page tables
+ */
+#if defined (KGDB) || defined (BDE_DEBUGGER)
+ movl _KERNend-KERNBASE,%ecx /* this much memory, */
+ shrl $PGSHIFT,%ecx /* for this many PTEs */
+#ifdef BDE_DEBUGGER
+ cmpl $0xa0,%ecx /* XXX - cover debugger pages */
+ jae 1f
+ movl $0xa0,%ecx
+1:
+#endif /* BDE_DEBUGGER */
+ movl $PG_V|PG_KW|PG_NC_PWT,%eax /* kernel R/W, valid, cache write-through */
+ lea ((1+UPAGES+1)*NBPG)(%esi),%ebx /* phys addr of kernel PT base */
+ movl %ebx,_KPTphys-KERNBASE /* save in global */
+ fillkpt
+
+#else /* !KGDB && !BDE_DEBUGGER */
+ /* write protect kernel text (doesn't do a thing for 386's - only 486's) */
+ movl $_etext-KERNBASE,%ecx /* get size of text */
+ shrl $PGSHIFT,%ecx /* for this many PTEs */
+ movl $PG_V|PG_KR,%eax /* specify read only */
+ lea ((1+UPAGES+1)*NBPG)(%esi),%ebx /* phys addr of kernel PT base */
+ movl %ebx,_KPTphys-KERNBASE /* save in global */
+ fillkpt
+
+ /* data and bss are r/w */
+ andl $PG_FRAME,%eax /* strip to just addr of bss */
+ movl _KERNend-KERNBASE,%ecx /* calculate size */
+ subl %eax,%ecx
+ shrl $PGSHIFT,%ecx
+ orl $PG_V|PG_KW,%eax /* valid, kernel read/write */
+ fillkpt
+#endif /* KGDB || BDE_DEBUGGER */
+
+/* now initialize the page dir, upages, p0stack PT, and page tables */
+
+ movl $(1+UPAGES+1+NKPT),%ecx /* number of PTEs */
+ movl %esi,%eax /* phys address of PTD */
+ andl $PG_FRAME,%eax /* convert to PFN, should be a NOP */
+ orl $PG_V|PG_KW|PG_NC_PWT,%eax /* valid, kernel read/write, cache write-though */
+ movl %esi,%ebx /* calculate pte offset to ptd */
+ shrl $PGSHIFT-2,%ebx
+ addl %esi,%ebx /* address of page directory */
+ addl $((1+UPAGES+1)*NBPG),%ebx /* offset to kernel page tables */
+ fillkpt
+
+/* map I/O memory map */
+
+ movl _KPTphys-KERNBASE,%ebx /* base of kernel page tables */
+ lea (0xa0 * PTESIZE)(%ebx),%ebx /* hardwire ISA hole at KERNBASE + 0xa0000 */
+ movl $0x100-0xa0,%ecx /* for this many pte s, */
+ movl $(0xa0000|PG_V|PG_KW|PG_N),%eax /* valid, kernel read/write, non-cacheable */
+ movl %ebx,_atdevphys-KERNBASE /* save phys addr of ptes */
+ fillkpt
+
+ /* map proc 0's kernel stack into user page table page */
+
+ movl $UPAGES,%ecx /* for this many pte s, */
+ lea (1*NBPG)(%esi),%eax /* physical address in proc 0 */
+ lea (KERNBASE)(%eax),%edx /* change into virtual addr */
+ movl %edx,_proc0paddr-KERNBASE /* save VA for proc 0 init */
+ orl $PG_V|PG_KW,%eax /* valid, kernel read/write */
+ lea ((1+UPAGES)*NBPG)(%esi),%ebx /* addr of stack page table in proc 0 */
+ addl $(KSTKPTEOFF * PTESIZE),%ebx /* offset to kernel stack PTE */
+ fillkpt
+
+/*
+ * Initialize kernel page table directory
+ */
+ /* install a pde for temporary double map of bottom of VA */
+ movl _KPTphys-KERNBASE,%eax
+ orl $PG_V|PG_KW,%eax /* valid, kernel read/write */
+ movl %eax,(%esi) /* which is where temp maps! */
+
+ /* initialize kernel pde's */
+ movl $(NKPT),%ecx /* for this many PDEs */
+ lea (KPTDI*PDESIZE)(%esi),%ebx /* offset of pde for kernel */
+ fillkpt
+
+ /* install a pde recursively mapping page directory as a page table! */
+ movl %esi,%eax /* phys address of ptd in proc 0 */
+ orl $PG_V|PG_KW,%eax /* pde entry is valid */
+ movl %eax,PTDPTDI*PDESIZE(%esi) /* which is where PTmap maps! */
+
+ /* install a pde to map kernel stack for proc 0 */
+ lea ((1+UPAGES)*NBPG)(%esi),%eax /* physical address of pt in proc 0 */
+ orl $PG_V|PG_KW,%eax /* pde entry is valid */
+ movl %eax,KSTKPTDI*PDESIZE(%esi) /* which is where kernel stack maps! */
+
+#ifdef BDE_DEBUGGER
+ /* copy and convert stuff from old gdt and idt for debugger */
+
+ cmpl $0x0375c339,0x96104 /* XXX - debugger signature */
+ jne 1f
+ movb $1,_bdb_exists-KERNBASE
+1:
+ pushal
+ subl $2*6,%esp
+
+ sgdt (%esp)
+ movl 2(%esp),%esi /* base address of current gdt */
+ movl $_gdt-KERNBASE,%edi
+ movl %edi,2(%esp)
+ movl $8*18/4,%ecx
+ cld
+ rep /* copy gdt */
+ movsl
+ movl $_gdt-KERNBASE,-8+2(%edi) /* adjust gdt self-ptr */
+ movb $0x92,-8+5(%edi)
+
+ sidt 6(%esp)
+ movl 6+2(%esp),%esi /* base address of current idt */
+ movl 8+4(%esi),%eax /* convert dbg descriptor to ... */
+ movw 8(%esi),%ax
+ movl %eax,bdb_dbg_ljmp+1-KERNBASE /* ... immediate offset ... */
+ movl 8+2(%esi),%eax
+ movw %ax,bdb_dbg_ljmp+5-KERNBASE /* ... and selector for ljmp */
+ movl 24+4(%esi),%eax /* same for bpt descriptor */
+ movw 24(%esi),%ax
+ movl %eax,bdb_bpt_ljmp+1-KERNBASE
+ movl 24+2(%esi),%eax
+ movw %ax,bdb_bpt_ljmp+5-KERNBASE
+
+ movl $_idt-KERNBASE,%edi
+ movl %edi,6+2(%esp)
+ movl $8*4/4,%ecx
+ cld
+ rep /* copy idt */
+ movsl
+
+ lgdt (%esp)
+ lidt 6(%esp)
+
+ addl $2*6,%esp
+ popal
+#endif /* BDE_DEBUGGER */
+
+ /* load base of page directory and enable mapping */
+ movl %esi,%eax /* phys address of ptd in proc 0 */
+ orl $I386_CR3PAT,%eax
+ movl %eax,%cr3 /* load ptd addr into mmu */
+ movl %cr0,%eax /* get control word */
+ orl $CR0_PE|CR0_PG,%eax /* enable paging */
+ movl %eax,%cr0 /* and let's page NOW! */
+
+ pushl $begin /* jump to high mem */
+ ret
+
+begin: /* now running relocated at KERNBASE where the system is linked to run */
+
+ .globl _Crtat /* XXX - locore should not know about */
+ movl _Crtat,%eax /* variables of device drivers (pccons)! */
+ subl $(KERNBASE+0xA0000),%eax
+ movl _atdevphys,%edx /* get pte PA */
+ subl _KPTphys,%edx /* remove base of ptes, now have phys offset */
+ shll $PGSHIFT-2,%edx /* corresponding to virt offset */
+ addl $KERNBASE,%edx /* add virtual base */
+ movl %edx,_atdevbase
+ addl %eax,%edx
+ movl %edx,_Crtat
+
+ /* set up bootstrap stack - 48 bytes */
+ movl $_kstack+UPAGES*NBPG-4*12,%esp /* bootstrap stack end location */
+ xorl %eax,%eax /* mark end of frames */
+ movl %eax,%ebp
+ movl _proc0paddr,%eax
+ movl %esi,PCB_CR3(%eax)
+
+#ifdef BDE_DEBUGGER
+ /* relocate debugger gdt entries */
+
+ movl $_gdt+8*9,%eax /* adjust slots 9-17 */
+ movl $9,%ecx
+reloc_gdt:
+ movb $KERNBASE>>24,7(%eax) /* top byte of base addresses, was 0, */
+ addl $8,%eax /* now KERNBASE>>24 */
+ loop reloc_gdt
+
+ cmpl $0,_bdb_exists
+ je 1f
+ int $3
+1:
+#endif /* BDE_DEBUGGER */
+
+ /*
+ * Skip over the page tables and the kernel stack
+ */
+ lea ((1+UPAGES+1+NKPT)*NBPG)(%esi),%esi
+
+ pushl %esi /* value of first for init386(first) */
+ call _init386 /* wire 386 chip for unix operation */
+ popl %esi
+
+#if 0
+ movl $0,_PTD
+#endif
+
+ .globl __ucodesel,__udatasel
+
+ pushl $0 /* unused */
+ pushl __udatasel /* ss */
+ pushl $0 /* esp - filled in by execve() */
+ pushl $0x3200 /* eflags (ring 3, int enab) */
+ pushl __ucodesel /* cs */
+ pushl $0 /* eip - filled in by execve() */
+ subl $(12*4),%esp /* space for rest of registers */
+
+ pushl %esp /* call main with frame pointer */
+ call _main /* autoconfiguration, mountroot etc */
+
+ addl $(13*4),%esp /* back to a frame we can return with */
+
+ /*
+ * now we've run main() and determined what cpu-type we are, we can
+ * enable WP mode on i486 cpus and above.
+ */
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ je 1f
+ movl %cr0,%eax /* get control word */
+ orl $CR0_WP,%eax /* enable write protect for all modes */
+ movl %eax,%cr0 /* and do it */
+#endif
+ /*
+ * on return from main(), we are process 1
+ * set up address space and stack so that we can 'return' to user mode
+ */
+1:
+ movl __ucodesel,%eax
+ movl __udatasel,%ecx
+
+ movl %cx,%ds
+ movl %cx,%es
+ movl %ax,%fs /* double map cs to fs */
+ movl %cx,%gs /* and ds to gs */
+ iret /* goto user! */
+
+#define LCALL(x,y) .byte 0x9a ; .long y ; .word x
+
+NON_GPROF_ENTRY(sigcode)
+ call SIGF_HANDLER(%esp)
+ lea SIGF_SC(%esp),%eax /* scp (the call may have clobbered the */
+ /* copy at 8(%esp)) */
+ pushl %eax
+ pushl %eax /* junk to fake return address */
+ movl $103,%eax /* XXX sigreturn() */
+ LCALL(0x7,0) /* enter kernel with args on stack */
+ hlt /* never gets here */
+
+ .globl _szsigcode
+_szsigcode:
+ .long _szsigcode-_sigcode
diff --git a/sys/amd64/amd64/locore.s b/sys/amd64/amd64/locore.s
new file mode 100644
index 0000000..538c690
--- /dev/null
+++ b/sys/amd64/amd64/locore.s
@@ -0,0 +1,518 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)locore.s 7.3 (Berkeley) 5/13/91
+ * $Id: locore.s,v 1.15 1994/02/01 04:08:54 davidg Exp $
+ */
+
+/*
+ * locore.s: FreeBSD machine support for the Intel 386
+ * originally from: locore.s, by William F. Jolitz
+ *
+ * Substantially rewritten by David Greenman, Rod Grimes,
+ * Bruce Evans, Wolfgang Solfrank, and many others.
+ */
+
+#include "npx.h" /* for NNPX */
+#include "assym.s" /* system definitions */
+#include "machine/psl.h" /* processor status longword defs */
+#include "machine/pte.h" /* page table entry definitions */
+#include "errno.h" /* error return codes */
+#include "machine/specialreg.h" /* x86 special registers */
+#include "machine/cputypes.h" /* x86 cpu type definitions */
+#include "syscall.h" /* system call numbers */
+#include "machine/asmacros.h" /* miscellaneous asm macros */
+
+/*
+ * XXX
+ *
+ * Note: This version greatly munged to avoid various assembler errors
+ * that may be fixed in newer versions of gas. Perhaps newer versions
+ * will have more pleasant appearance.
+ */
+
+/*
+ * PTmap is recursive pagemap at top of virtual address space.
+ * Within PTmap, the page directory can be found (third indirection).
+ */
+ .globl _PTmap,_PTD,_PTDpde,_Sysmap
+ .set _PTmap,PTDPTDI << PDRSHIFT
+ .set _PTD,_PTmap + (PTDPTDI * NBPG)
+ .set _PTDpde,_PTD + (PTDPTDI * PDESIZE)
+
+/* Sysmap is the base address of the kernel page tables */
+ .set _Sysmap,_PTmap + (KPTDI * NBPG)
+
+/*
+ * APTmap, APTD is the alternate recursive pagemap.
+ * It's used when modifying another process's page tables.
+ */
+ .globl _APTmap,_APTD,_APTDpde
+ .set _APTmap,APTDPTDI << PDRSHIFT
+ .set _APTD,_APTmap + (APTDPTDI * NBPG)
+ .set _APTDpde,_PTD + (APTDPTDI * PDESIZE)
+
+/*
+ * Access to each processes kernel stack is via a region of
+ * per-process address space (at the beginning), immediatly above
+ * the user process stack.
+ */
+ .set _kstack,USRSTACK
+ .globl _kstack
+
+/*
+ * Globals
+ */
+ .data
+ .globl _esym
+_esym: .long 0 /* ptr to end of syms */
+
+ .globl _boothowto,_bootdev,_curpcb
+
+ .globl _cpu,_cold,_atdevbase
+_cpu: .long 0 /* are we 386, 386sx, or 486 */
+_cold: .long 1 /* cold till we are not */
+_atdevbase: .long 0 /* location of start of iomem in virtual */
+_atdevphys: .long 0 /* location of device mapping ptes (phys) */
+
+ .globl _KERNend
+_KERNend: .long 0 /* phys addr end of kernel (just after bss) */
+
+ .globl _IdlePTD,_KPTphys
+_IdlePTD: .long 0 /* phys addr of kernel PTD */
+_KPTphys: .long 0 /* phys addr of kernel page tables */
+
+ .globl _cyloffset
+_cyloffset: .long 0 /* cylinder offset from boot blocks */
+
+ .globl _proc0paddr
+_proc0paddr: .long 0 /* address of proc 0 address space */
+
+#ifdef BDE_DEBUGGER
+ .globl _bdb_exists /* flag to indicate BDE debugger is available */
+_bdb_exists: .long 0
+#endif
+
+ .globl tmpstk
+ .space 0x1000
+tmpstk:
+
+
+/*
+ * System Initialization
+ */
+ .text
+
+/*
+ * btext: beginning of text section.
+ * Also the entry point (jumped to directly from the boot blocks).
+ */
+NON_GPROF_ENTRY(btext)
+ movw $0x1234,0x472 /* warm boot */
+ jmp 1f
+ .org 0x500 /* space for BIOS variables */
+
+ /*
+ * pass parameters on stack (howto, bootdev, unit, cyloffset, esym)
+ * note: (%esp) is return address of boot
+ * ( if we want to hold onto /boot, it's physical %esp up to _end)
+ */
+
+ 1: movl 4(%esp),%eax
+ movl %eax,_boothowto-KERNBASE
+ movl 8(%esp),%eax
+ movl %eax,_bootdev-KERNBASE
+ movl 12(%esp),%eax
+ movl %eax,_cyloffset-KERNBASE
+ movl 16(%esp),%eax
+ addl $KERNBASE,%eax
+ movl %eax,_esym-KERNBASE
+#ifdef DISKLESS /* Copy diskless structure */
+ movl _nfs_diskless_size-KERNBASE,%ecx
+ movl 20(%esp),%esi
+ movl $(_nfs_diskless-KERNBASE),%edi
+ cld
+ rep
+ movsb
+#endif
+
+ /* find out our CPU type. */
+ pushfl
+ popl %eax
+ movl %eax,%ecx
+ xorl $0x40000,%eax
+ pushl %eax
+ popfl
+ pushfl
+ popl %eax
+ xorl %ecx,%eax
+ shrl $18,%eax
+ andl $1,%eax
+ push %ecx
+ popfl
+
+ cmpl $0,%eax
+ jne 1f
+ movl $CPU_386,_cpu-KERNBASE
+ jmp 2f
+1: movl $CPU_486,_cpu-KERNBASE
+2:
+
+ /*
+ * Finished with old stack; load new %esp now instead of later so
+ * we can trace this code without having to worry about the trace
+ * trap clobbering the memory test or the zeroing of the bss+bootstrap
+ * page tables.
+ *
+ * XXX - wdboot clears the bss after testing that this is safe.
+ * This is too wasteful - memory below 640K is scarce. The boot
+ * program should check:
+ * text+data <= &stack_variable - more_space_for_stack
+ * text+data+bss+pad+space_for_page_tables <= end_of_memory
+ * Oops, the gdt is in the carcass of the boot program so clearing
+ * the rest of memory is still not possible.
+ */
+ movl $tmpstk-KERNBASE,%esp /* bootstrap stack end location */
+
+/*
+ * Virtual address space of kernel:
+ *
+ * text | data | bss | [syms] | page dir | proc0 kernel stack | usr stk map | Sysmap
+ * pages: 1 UPAGES (2) 1 NKPT (7)
+ */
+
+/* find end of kernel image */
+ movl $_end-KERNBASE,%ecx
+ addl $NBPG-1,%ecx /* page align up */
+ andl $~(NBPG-1),%ecx
+ movl %ecx,%esi /* esi = start of free memory */
+ movl %ecx,_KERNend-KERNBASE /* save end of kernel */
+
+/* clear bss */
+ movl $_edata-KERNBASE,%edi
+ subl %edi,%ecx /* get amount to clear */
+ xorl %eax,%eax /* specify zero fill */
+ cld
+ rep
+ stosb
+
+/*
+ * The value in esi is both the end of the kernel bss and a pointer to
+ * the kernel page directory, and is used by the rest of locore to build
+ * the tables.
+ * esi + 1(page dir) + 2(UPAGES) + 1(p0stack) + NKPT(number of kernel
+ * page table pages) is then passed on the stack to init386(first) as
+ * the value first. esi should ALWAYS be page aligned!!
+ */
+ movl %esi,%ecx /* Get current first availiable address */
+
+/* clear pagetables, page directory, stack, etc... */
+ movl %esi,%edi /* base (page directory) */
+ movl $((1+UPAGES+1+NKPT)*NBPG),%ecx /* amount to clear */
+ xorl %eax,%eax /* specify zero fill */
+ cld
+ rep
+ stosb
+
+/* physical address of Idle proc/kernel page directory */
+ movl %esi,_IdlePTD-KERNBASE
+
+/*
+ * fillkpt
+ * eax = (page frame address | control | status) == pte
+ * ebx = address of page table
+ * ecx = how many pages to map
+ */
+#define fillkpt \
+1: movl %eax,(%ebx) ; \
+ addl $NBPG,%eax ; /* increment physical address */ \
+ addl $4,%ebx ; /* next pte */ \
+ loop 1b ;
+
+/*
+ * Map Kernel
+ *
+ * First step - build page tables
+ */
+#if defined (KGDB) || defined (BDE_DEBUGGER)
+ movl _KERNend-KERNBASE,%ecx /* this much memory, */
+ shrl $PGSHIFT,%ecx /* for this many PTEs */
+#ifdef BDE_DEBUGGER
+ cmpl $0xa0,%ecx /* XXX - cover debugger pages */
+ jae 1f
+ movl $0xa0,%ecx
+1:
+#endif /* BDE_DEBUGGER */
+ movl $PG_V|PG_KW|PG_NC_PWT,%eax /* kernel R/W, valid, cache write-through */
+ lea ((1+UPAGES+1)*NBPG)(%esi),%ebx /* phys addr of kernel PT base */
+ movl %ebx,_KPTphys-KERNBASE /* save in global */
+ fillkpt
+
+#else /* !KGDB && !BDE_DEBUGGER */
+ /* write protect kernel text (doesn't do a thing for 386's - only 486's) */
+ movl $_etext-KERNBASE,%ecx /* get size of text */
+ shrl $PGSHIFT,%ecx /* for this many PTEs */
+ movl $PG_V|PG_KR,%eax /* specify read only */
+ lea ((1+UPAGES+1)*NBPG)(%esi),%ebx /* phys addr of kernel PT base */
+ movl %ebx,_KPTphys-KERNBASE /* save in global */
+ fillkpt
+
+ /* data and bss are r/w */
+ andl $PG_FRAME,%eax /* strip to just addr of bss */
+ movl _KERNend-KERNBASE,%ecx /* calculate size */
+ subl %eax,%ecx
+ shrl $PGSHIFT,%ecx
+ orl $PG_V|PG_KW,%eax /* valid, kernel read/write */
+ fillkpt
+#endif /* KGDB || BDE_DEBUGGER */
+
+/* now initialize the page dir, upages, p0stack PT, and page tables */
+
+ movl $(1+UPAGES+1+NKPT),%ecx /* number of PTEs */
+ movl %esi,%eax /* phys address of PTD */
+ andl $PG_FRAME,%eax /* convert to PFN, should be a NOP */
+ orl $PG_V|PG_KW|PG_NC_PWT,%eax /* valid, kernel read/write, cache write-though */
+ movl %esi,%ebx /* calculate pte offset to ptd */
+ shrl $PGSHIFT-2,%ebx
+ addl %esi,%ebx /* address of page directory */
+ addl $((1+UPAGES+1)*NBPG),%ebx /* offset to kernel page tables */
+ fillkpt
+
+/* map I/O memory map */
+
+ movl _KPTphys-KERNBASE,%ebx /* base of kernel page tables */
+ lea (0xa0 * PTESIZE)(%ebx),%ebx /* hardwire ISA hole at KERNBASE + 0xa0000 */
+ movl $0x100-0xa0,%ecx /* for this many pte s, */
+ movl $(0xa0000|PG_V|PG_KW|PG_N),%eax /* valid, kernel read/write, non-cacheable */
+ movl %ebx,_atdevphys-KERNBASE /* save phys addr of ptes */
+ fillkpt
+
+ /* map proc 0's kernel stack into user page table page */
+
+ movl $UPAGES,%ecx /* for this many pte s, */
+ lea (1*NBPG)(%esi),%eax /* physical address in proc 0 */
+ lea (KERNBASE)(%eax),%edx /* change into virtual addr */
+ movl %edx,_proc0paddr-KERNBASE /* save VA for proc 0 init */
+ orl $PG_V|PG_KW,%eax /* valid, kernel read/write */
+ lea ((1+UPAGES)*NBPG)(%esi),%ebx /* addr of stack page table in proc 0 */
+ addl $(KSTKPTEOFF * PTESIZE),%ebx /* offset to kernel stack PTE */
+ fillkpt
+
+/*
+ * Initialize kernel page table directory
+ */
+ /* install a pde for temporary double map of bottom of VA */
+ movl _KPTphys-KERNBASE,%eax
+ orl $PG_V|PG_KW,%eax /* valid, kernel read/write */
+ movl %eax,(%esi) /* which is where temp maps! */
+
+ /* initialize kernel pde's */
+ movl $(NKPT),%ecx /* for this many PDEs */
+ lea (KPTDI*PDESIZE)(%esi),%ebx /* offset of pde for kernel */
+ fillkpt
+
+ /* install a pde recursively mapping page directory as a page table! */
+ movl %esi,%eax /* phys address of ptd in proc 0 */
+ orl $PG_V|PG_KW,%eax /* pde entry is valid */
+ movl %eax,PTDPTDI*PDESIZE(%esi) /* which is where PTmap maps! */
+
+ /* install a pde to map kernel stack for proc 0 */
+ lea ((1+UPAGES)*NBPG)(%esi),%eax /* physical address of pt in proc 0 */
+ orl $PG_V|PG_KW,%eax /* pde entry is valid */
+ movl %eax,KSTKPTDI*PDESIZE(%esi) /* which is where kernel stack maps! */
+
+#ifdef BDE_DEBUGGER
+ /* copy and convert stuff from old gdt and idt for debugger */
+
+ cmpl $0x0375c339,0x96104 /* XXX - debugger signature */
+ jne 1f
+ movb $1,_bdb_exists-KERNBASE
+1:
+ pushal
+ subl $2*6,%esp
+
+ sgdt (%esp)
+ movl 2(%esp),%esi /* base address of current gdt */
+ movl $_gdt-KERNBASE,%edi
+ movl %edi,2(%esp)
+ movl $8*18/4,%ecx
+ cld
+ rep /* copy gdt */
+ movsl
+ movl $_gdt-KERNBASE,-8+2(%edi) /* adjust gdt self-ptr */
+ movb $0x92,-8+5(%edi)
+
+ sidt 6(%esp)
+ movl 6+2(%esp),%esi /* base address of current idt */
+ movl 8+4(%esi),%eax /* convert dbg descriptor to ... */
+ movw 8(%esi),%ax
+ movl %eax,bdb_dbg_ljmp+1-KERNBASE /* ... immediate offset ... */
+ movl 8+2(%esi),%eax
+ movw %ax,bdb_dbg_ljmp+5-KERNBASE /* ... and selector for ljmp */
+ movl 24+4(%esi),%eax /* same for bpt descriptor */
+ movw 24(%esi),%ax
+ movl %eax,bdb_bpt_ljmp+1-KERNBASE
+ movl 24+2(%esi),%eax
+ movw %ax,bdb_bpt_ljmp+5-KERNBASE
+
+ movl $_idt-KERNBASE,%edi
+ movl %edi,6+2(%esp)
+ movl $8*4/4,%ecx
+ cld
+ rep /* copy idt */
+ movsl
+
+ lgdt (%esp)
+ lidt 6(%esp)
+
+ addl $2*6,%esp
+ popal
+#endif /* BDE_DEBUGGER */
+
+ /* load base of page directory and enable mapping */
+ movl %esi,%eax /* phys address of ptd in proc 0 */
+ orl $I386_CR3PAT,%eax
+ movl %eax,%cr3 /* load ptd addr into mmu */
+ movl %cr0,%eax /* get control word */
+ orl $CR0_PE|CR0_PG,%eax /* enable paging */
+ movl %eax,%cr0 /* and let's page NOW! */
+
+ pushl $begin /* jump to high mem */
+ ret
+
+begin: /* now running relocated at KERNBASE where the system is linked to run */
+
+ .globl _Crtat /* XXX - locore should not know about */
+ movl _Crtat,%eax /* variables of device drivers (pccons)! */
+ subl $(KERNBASE+0xA0000),%eax
+ movl _atdevphys,%edx /* get pte PA */
+ subl _KPTphys,%edx /* remove base of ptes, now have phys offset */
+ shll $PGSHIFT-2,%edx /* corresponding to virt offset */
+ addl $KERNBASE,%edx /* add virtual base */
+ movl %edx,_atdevbase
+ addl %eax,%edx
+ movl %edx,_Crtat
+
+ /* set up bootstrap stack - 48 bytes */
+ movl $_kstack+UPAGES*NBPG-4*12,%esp /* bootstrap stack end location */
+ xorl %eax,%eax /* mark end of frames */
+ movl %eax,%ebp
+ movl _proc0paddr,%eax
+ movl %esi,PCB_CR3(%eax)
+
+#ifdef BDE_DEBUGGER
+ /* relocate debugger gdt entries */
+
+ movl $_gdt+8*9,%eax /* adjust slots 9-17 */
+ movl $9,%ecx
+reloc_gdt:
+ movb $KERNBASE>>24,7(%eax) /* top byte of base addresses, was 0, */
+ addl $8,%eax /* now KERNBASE>>24 */
+ loop reloc_gdt
+
+ cmpl $0,_bdb_exists
+ je 1f
+ int $3
+1:
+#endif /* BDE_DEBUGGER */
+
+ /*
+ * Skip over the page tables and the kernel stack
+ */
+ lea ((1+UPAGES+1+NKPT)*NBPG)(%esi),%esi
+
+ pushl %esi /* value of first for init386(first) */
+ call _init386 /* wire 386 chip for unix operation */
+ popl %esi
+
+#if 0
+ movl $0,_PTD
+#endif
+
+ .globl __ucodesel,__udatasel
+
+ pushl $0 /* unused */
+ pushl __udatasel /* ss */
+ pushl $0 /* esp - filled in by execve() */
+ pushl $0x3200 /* eflags (ring 3, int enab) */
+ pushl __ucodesel /* cs */
+ pushl $0 /* eip - filled in by execve() */
+ subl $(12*4),%esp /* space for rest of registers */
+
+ pushl %esp /* call main with frame pointer */
+ call _main /* autoconfiguration, mountroot etc */
+
+ addl $(13*4),%esp /* back to a frame we can return with */
+
+ /*
+ * now we've run main() and determined what cpu-type we are, we can
+ * enable WP mode on i486 cpus and above.
+ */
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ je 1f
+ movl %cr0,%eax /* get control word */
+ orl $CR0_WP,%eax /* enable write protect for all modes */
+ movl %eax,%cr0 /* and do it */
+#endif
+ /*
+ * on return from main(), we are process 1
+ * set up address space and stack so that we can 'return' to user mode
+ */
+1:
+ movl __ucodesel,%eax
+ movl __udatasel,%ecx
+
+ movl %cx,%ds
+ movl %cx,%es
+ movl %ax,%fs /* double map cs to fs */
+ movl %cx,%gs /* and ds to gs */
+ iret /* goto user! */
+
+#define LCALL(x,y) .byte 0x9a ; .long y ; .word x
+
+NON_GPROF_ENTRY(sigcode)
+ call SIGF_HANDLER(%esp)
+ lea SIGF_SC(%esp),%eax /* scp (the call may have clobbered the */
+ /* copy at 8(%esp)) */
+ pushl %eax
+ pushl %eax /* junk to fake return address */
+ movl $103,%eax /* XXX sigreturn() */
+ LCALL(0x7,0) /* enter kernel with args on stack */
+ hlt /* never gets here */
+
+ .globl _szsigcode
+_szsigcode:
+ .long _szsigcode-_sigcode
diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c
new file mode 100644
index 0000000..70f65bf
--- /dev/null
+++ b/sys/amd64/amd64/machdep.c
@@ -0,0 +1,1556 @@
+/*-
+ * Copyright (c) 1992 Terrence R. Lambert.
+ * Copyright (c) 1982, 1987, 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91
+ * $Id: machdep.c,v 1.41 1994/03/30 02:31:11 davidg Exp $
+ */
+
+#include "npx.h"
+#include "isa.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/signalvar.h>
+#include <sys/kernel.h>
+#include <sys/map.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/buf.h>
+#include <sys/reboot.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/msgbuf.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/sysctl.h>
+
+#ifdef SYSVSHM
+#include "sys/shm.h"
+#endif
+
+#ifdef SYSVMSG
+#include "msg.h"
+#endif
+
+#ifdef SYSVSEM
+#include "sem.h"
+#endif
+
+#include "vm/vm.h"
+#include "vm/vm_kern.h"
+#include "vm/vm_page.h"
+
+#include "sys/exec.h"
+#include "sys/vnode.h"
+
+extern vm_offset_t avail_start, avail_end;
+
+#include "machine/cpu.h"
+#include "machine/reg.h"
+#include "machine/psl.h"
+#include "machine/specialreg.h"
+#include "machine/sysarch.h"
+#include "machine/cons.h"
+
+#include "i386/isa/isa.h"
+#include "i386/isa/rtc.h"
+
+static void identifycpu(void);
+static void initcpu(void);
+static int test_page(int *, int);
+
+extern int grow(struct proc *,u_int);
+const char machine[] = "PC-Class";
+const char *cpu_model;
+
+#ifndef PANIC_REBOOT_WAIT_TIME
+#define PANIC_REBOOT_WAIT_TIME 15 /* default to 15 seconds */
+#endif
+
+/*
+ * Declare these as initialized data so we can patch them.
+ */
+int nswbuf = 0;
+#ifdef NBUF
+int nbuf = NBUF;
+#else
+int nbuf = 0;
+#endif
+#ifdef BUFPAGES
+int bufpages = BUFPAGES;
+#else
+int bufpages = 0;
+#endif
+#ifdef BOUNCEPAGES
+int bouncepages = BOUNCEPAGES;
+#else
+int bouncepages = 0;
+#endif
+int msgbufmapped = 0; /* set when safe to use msgbuf */
+extern int freebufspace;
+extern char *bouncememory;
+
+int _udatasel, _ucodesel;
+
+/*
+ * Machine-dependent startup code
+ */
+int boothowto = 0, Maxmem = 0, badpages = 0, physmem = 0;
+long dumplo;
+extern int bootdev;
+int biosmem;
+
+vm_offset_t phys_avail[6];
+
+extern cyloffset;
+
+int cpu_class;
+
+void dumpsys __P((void));
+vm_offset_t buffer_sva, buffer_eva;
+vm_offset_t clean_sva, clean_eva;
+vm_offset_t pager_sva, pager_eva;
+int maxbkva, pager_map_size;
+
+#define offsetof(type, member) ((size_t)(&((type *)0)->member))
+
+void
+cpu_startup()
+{
+ register int unixsize;
+ register unsigned i;
+ register struct pte *pte;
+ int mapaddr, j;
+ register caddr_t v;
+ int maxbufs, base, residual;
+ extern long Usrptsize;
+ vm_offset_t minaddr, maxaddr;
+ vm_size_t size = 0;
+ int firstaddr;
+
+ /*
+ * Initialize error message buffer (at end of core).
+ */
+
+ /* avail_end was pre-decremented in init_386() to compensate */
+ for (i = 0; i < btoc(sizeof (struct msgbuf)); i++)
+ pmap_enter(pmap_kernel(), (vm_offset_t)msgbufp,
+ avail_end + i * NBPG,
+ VM_PROT_ALL, TRUE);
+ msgbufmapped = 1;
+
+ /*
+ * Good {morning,afternoon,evening,night}.
+ */
+ printf(version);
+ identifycpu();
+ printf("real memory = %d (%d pages)\n", ptoa(physmem), physmem);
+ if (badpages)
+ printf("bad memory = %d (%d pages)\n", ptoa(badpages), badpages);
+
+ /*
+ * Allocate space for system data structures.
+ * The first available kernel virtual address is in "v".
+ * As pages of kernel virtual memory are allocated, "v" is incremented.
+ * As pages of memory are allocated and cleared,
+ * "firstaddr" is incremented.
+ * An index into the kernel page table corresponding to the
+ * virtual memory address maintained in "v" is kept in "mapaddr".
+ */
+
+ /*
+ * Make two passes. The first pass calculates how much memory is
+ * needed and allocates it. The second pass assigns virtual
+ * addresses to the various data structures.
+ */
+ firstaddr = 0;
+again:
+ v = (caddr_t)firstaddr;
+
+#define valloc(name, type, num) \
+ (name) = (type *)v; v = (caddr_t)((name)+(num))
+#define valloclim(name, type, num, lim) \
+ (name) = (type *)v; v = (caddr_t)((lim) = ((name)+(num)))
+ valloc(callout, struct callout, ncallout);
+#ifdef SYSVSHM
+ valloc(shmsegs, struct shmid_ds, shminfo.shmmni);
+#endif
+#ifdef SYSVSEM
+ valloc(sema, struct semid_ds, seminfo.semmni);
+ valloc(sem, struct sem, seminfo.semmns);
+ /* This is pretty disgusting! */
+ valloc(semu, int, (seminfo.semmnu * seminfo.semusz) / sizeof(int));
+#endif
+#ifdef SYSVMSG
+ valloc(msgpool, char, msginfo.msgmax);
+ valloc(msgmaps, struct msgmap, msginfo.msgseg);
+ valloc(msghdrs, struct msg, msginfo.msgtql);
+ valloc(msqids, struct msqid_ds, msginfo.msgmni);
+#endif
+ /*
+ * Determine how many buffers to allocate.
+ * Use 20% of memory of memory beyond the first 2MB
+ * Insure a minimum of 16 fs buffers.
+ * We allocate 1/2 as many swap buffer headers as file i/o buffers.
+ */
+ if (bufpages == 0)
+ bufpages = ((physmem << PGSHIFT) - 2048*1024) / NBPG / 5;
+ if (bufpages < 64)
+ bufpages = 64;
+
+ /*
+ * We must still limit the maximum number of buffers to be no
+ * more than 2/5's of the size of the kernal malloc region, this
+ * will only take effect for machines with lots of memory
+ */
+ bufpages = min(bufpages, (VM_KMEM_SIZE / NBPG) * 2 / 5);
+ if (nbuf == 0) {
+ nbuf = bufpages / 2;
+ if (nbuf < 32)
+ nbuf = 32;
+ }
+ freebufspace = bufpages * NBPG;
+ if (nswbuf == 0) {
+ nswbuf = (nbuf / 2) &~ 1; /* force even */
+ if (nswbuf > 256)
+ nswbuf = 256; /* sanity */
+ }
+ valloc(swbuf, struct buf, nswbuf);
+ valloc(buf, struct buf, nbuf);
+
+#ifndef NOBOUNCE
+ /*
+ * If there is more than 16MB of memory, allocate some bounce buffers
+ */
+ if (Maxmem > 4096) {
+ if (bouncepages == 0)
+ bouncepages = 96; /* largest physio size + extra */
+ v = (caddr_t)((vm_offset_t)((vm_offset_t)v + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1));
+ valloc(bouncememory, char, bouncepages * PAGE_SIZE);
+ }
+#endif
+
+ /*
+ * End of first pass, size has been calculated so allocate memory
+ */
+ if (firstaddr == 0) {
+ size = (vm_size_t)(v - firstaddr);
+ firstaddr = (int)kmem_alloc(kernel_map, round_page(size));
+ if (firstaddr == 0)
+ panic("startup: no room for tables");
+ goto again;
+ }
+
+ /*
+ * End of second pass, addresses have been assigned
+ */
+ if ((vm_size_t)(v - firstaddr) != size)
+ panic("startup: table size inconsistency");
+
+ clean_map = kmem_suballoc(kernel_map, &clean_sva, &clean_eva,
+ (nbuf*MAXBSIZE) + VM_PHYS_SIZE + maxbkva + pager_map_size, TRUE);
+
+ io_map = kmem_suballoc(clean_map, &minaddr, &maxaddr, maxbkva, FALSE);
+ pager_map = kmem_suballoc(clean_map, &pager_sva, &pager_eva,
+ pager_map_size, TRUE);
+
+ buffer_map = kmem_suballoc(clean_map, &buffer_sva, &buffer_eva,
+ (nbuf * MAXBSIZE), TRUE);
+ /*
+ * Allocate a submap for physio
+ */
+ phys_map = kmem_suballoc(clean_map, &minaddr, &maxaddr,
+ VM_PHYS_SIZE, TRUE);
+
+ /*
+ * Finally, allocate mbuf pool. Since mclrefcnt is an off-size
+ * we use the more space efficient malloc in place of kmem_alloc.
+ */
+ mclrefcnt = (char *)malloc(NMBCLUSTERS+CLBYTES/MCLBYTES,
+ M_MBUF, M_NOWAIT);
+ bzero(mclrefcnt, NMBCLUSTERS+CLBYTES/MCLBYTES);
+ mb_map = kmem_suballoc(kmem_map, (vm_offset_t *)&mbutl, &maxaddr,
+ VM_MBUF_SIZE, FALSE);
+ /*
+ * Initialize callouts
+ */
+ callfree = callout;
+ for (i = 1; i < ncallout; i++)
+ callout[i-1].c_next = &callout[i];
+
+ printf("avail memory = %d (%d pages)\n", ptoa(cnt.v_free_count), cnt.v_free_count);
+ printf("using %d buffers containing %d bytes of memory\n",
+ nbuf, bufpages * CLBYTES);
+
+#ifndef NOBOUNCE
+ /*
+ * init bounce buffers
+ */
+ vm_bounce_init();
+#endif
+
+ /*
+ * Set up CPU-specific registers, cache, etc.
+ */
+ initcpu();
+
+ /*
+ * Set up buffers, so they can be used to read disk labels.
+ */
+ bufinit();
+
+ /*
+ * Configure the system.
+ */
+ configure();
+}
+
+
+struct cpu_nameclass i386_cpus[] = {
+ { "Intel 80286", CPUCLASS_286 }, /* CPU_286 */
+ { "i386SX", CPUCLASS_386 }, /* CPU_386SX */
+ { "i386DX", CPUCLASS_386 }, /* CPU_386 */
+ { "i486SX", CPUCLASS_486 }, /* CPU_486SX */
+ { "i486DX", CPUCLASS_486 }, /* CPU_486 */
+ { "i586", CPUCLASS_586 }, /* CPU_586 */
+};
+
+static void
+identifycpu()
+{
+ printf("CPU: ");
+ if (cpu >= 0 && cpu < (sizeof i386_cpus/sizeof(struct cpu_nameclass))) {
+ printf("%s", i386_cpus[cpu].cpu_name);
+ cpu_class = i386_cpus[cpu].cpu_class;
+ cpu_model = i386_cpus[cpu].cpu_name;
+ } else {
+ printf("unknown cpu type %d\n", cpu);
+ panic("startup: bad cpu id");
+ }
+ printf(" (");
+ switch(cpu_class) {
+ case CPUCLASS_286:
+ printf("286");
+ break;
+ case CPUCLASS_386:
+ printf("386");
+ break;
+ case CPUCLASS_486:
+ printf("486");
+ break;
+ case CPUCLASS_586:
+ printf("586");
+ break;
+ default:
+ printf("unknown"); /* will panic below... */
+ }
+ printf("-class CPU)");
+ printf("\n"); /* cpu speed would be nice, but how? */
+
+ /*
+ * Now that we have told the user what they have,
+ * let them know if that machine type isn't configured.
+ */
+ switch (cpu_class) {
+ case CPUCLASS_286: /* a 286 should not make it this far, anyway */
+#if !defined(I386_CPU) && !defined(I486_CPU) && !defined(I586_CPU)
+#error This kernel is not configured for one of the supported CPUs
+#endif
+#if !defined(I386_CPU)
+ case CPUCLASS_386:
+#endif
+#if !defined(I486_CPU)
+ case CPUCLASS_486:
+#endif
+#if !defined(I586_CPU)
+ case CPUCLASS_586:
+#endif
+ panic("CPU class not configured");
+ default:
+ break;
+ }
+}
+
+#ifdef PGINPROF
+/*
+ * Return the difference (in microseconds)
+ * between the current time and a previous
+ * time as represented by the arguments.
+ * If there is a pending clock interrupt
+ * which has not been serviced due to high
+ * ipl, return error code.
+ */
+/*ARGSUSED*/
+vmtime(otime, olbolt, oicr)
+ register int otime, olbolt, oicr;
+{
+
+ return (((time.tv_sec-otime)*60 + lbolt-olbolt)*16667);
+}
+#endif
+
+extern int kstack[];
+
+/*
+ * Send an interrupt to process.
+ *
+ * Stack is set up to allow sigcode stored
+ * in u. to call routine, followed by kcall
+ * to sigreturn routine below. After sigreturn
+ * resets the signal mask, the stack, and the
+ * frame pointer, it returns to the user
+ * specified pc, psl.
+ */
+void
+sendsig(catcher, sig, mask, code)
+ sig_t catcher;
+ int sig, mask;
+ unsigned code;
+{
+ register struct proc *p = curproc;
+ register int *regs;
+ register struct sigframe *fp;
+ struct sigacts *psp = p->p_sigacts;
+ int oonstack, frmtrap;
+
+ regs = p->p_md.md_regs;
+ oonstack = psp->ps_sigstk.ss_flags & SA_ONSTACK;
+ /*
+ * Allocate and validate space for the signal handler
+ * context. Note that if the stack is in P0 space, the
+ * call to grow() is a nop, and the useracc() check
+ * will fail if the process has not already allocated
+ * the space with a `brk'.
+ */
+ if ((psp->ps_flags & SAS_ALTSTACK) &&
+ (psp->ps_sigstk.ss_flags & SA_ONSTACK) == 0 &&
+ (psp->ps_sigonstack & sigmask(sig))) {
+ fp = (struct sigframe *)(psp->ps_sigstk.ss_base +
+ psp->ps_sigstk.ss_size - sizeof(struct sigframe));
+ psp->ps_sigstk.ss_flags |= SA_ONSTACK;
+ } else {
+ fp = (struct sigframe *)(regs[tESP]
+ - sizeof(struct sigframe));
+ }
+
+ /*
+ * grow() will return FALSE if the fp will not fit inside the stack
+ * and the stack can not be grown. useracc will return FALSE
+ * if access is denied.
+ */
+ if ((grow(p, (int)fp) == FALSE) ||
+ (useracc((caddr_t)fp, sizeof (struct sigframe), B_WRITE) == FALSE)) {
+ /*
+ * Process has trashed its stack; give it an illegal
+ * instruction to halt it in its tracks.
+ */
+ SIGACTION(p, SIGILL) = SIG_DFL;
+ sig = sigmask(SIGILL);
+ p->p_sigignore &= ~sig;
+ p->p_sigcatch &= ~sig;
+ p->p_sigmask &= ~sig;
+ psignal(p, SIGILL);
+ return;
+ }
+
+ /*
+ * Build the argument list for the signal handler.
+ */
+ fp->sf_signum = sig;
+ fp->sf_code = code;
+ fp->sf_scp = &fp->sf_sc;
+ fp->sf_addr = (char *) regs[tERR];
+ fp->sf_handler = catcher;
+
+ /* save scratch registers */
+ fp->sf_sc.sc_eax = regs[tEAX];
+ fp->sf_sc.sc_ebx = regs[tEBX];
+ fp->sf_sc.sc_ecx = regs[tECX];
+ fp->sf_sc.sc_edx = regs[tEDX];
+ fp->sf_sc.sc_esi = regs[tESI];
+ fp->sf_sc.sc_edi = regs[tEDI];
+ fp->sf_sc.sc_cs = regs[tCS];
+ fp->sf_sc.sc_ds = regs[tDS];
+ fp->sf_sc.sc_ss = regs[tSS];
+ fp->sf_sc.sc_es = regs[tES];
+ fp->sf_sc.sc_isp = regs[tISP];
+
+ /*
+ * Build the signal context to be used by sigreturn.
+ */
+ fp->sf_sc.sc_onstack = oonstack;
+ fp->sf_sc.sc_mask = mask;
+ fp->sf_sc.sc_sp = regs[tESP];
+ fp->sf_sc.sc_fp = regs[tEBP];
+ fp->sf_sc.sc_pc = regs[tEIP];
+ fp->sf_sc.sc_ps = regs[tEFLAGS];
+ regs[tESP] = (int)fp;
+ regs[tEIP] = (int)((struct pcb *)kstack)->pcb_sigc;
+ regs[tEFLAGS] &= ~PSL_VM;
+ regs[tCS] = _ucodesel;
+ regs[tDS] = _udatasel;
+ regs[tES] = _udatasel;
+ regs[tSS] = _udatasel;
+}
+
+/*
+ * System call to cleanup state after a signal
+ * has been taken. Reset signal mask and
+ * stack state from context left by sendsig (above).
+ * Return to previous pc and psl as specified by
+ * context left by sendsig. Check carefully to
+ * make sure that the user has not modified the
+ * psl to gain improper privileges or to cause
+ * a machine fault.
+ */
+struct sigreturn_args {
+ struct sigcontext *sigcntxp;
+};
+
+int
+sigreturn(p, uap, retval)
+ struct proc *p;
+ struct sigreturn_args *uap;
+ int *retval;
+{
+ register struct sigcontext *scp;
+ register struct sigframe *fp;
+ register int *regs = p->p_md.md_regs;
+ int eflags;
+
+ /*
+ * (XXX old comment) regs[tESP] points to the return address.
+ * The user scp pointer is above that.
+ * The return address is faked in the signal trampoline code
+ * for consistency.
+ */
+ scp = uap->sigcntxp;
+ fp = (struct sigframe *)
+ ((caddr_t)scp - offsetof(struct sigframe, sf_sc));
+
+ if (useracc((caddr_t)fp, sizeof (*fp), 0) == 0)
+ return(EINVAL);
+
+ eflags = scp->sc_ps;
+ if ((eflags & PSL_USERCLR) != 0 ||
+ (eflags & PSL_USERSET) != PSL_USERSET ||
+ (eflags & PSL_IOPL) < (regs[tEFLAGS] & PSL_IOPL)) {
+#ifdef DEBUG
+ printf("sigreturn: eflags=0x%x\n", eflags);
+#endif
+ return(EINVAL);
+ }
+
+ /*
+ * Sanity check the user's selectors and error if they
+ * are suspect.
+ */
+#define max_ldt_sel(pcb) \
+ ((pcb)->pcb_ldt ? (pcb)->pcb_ldt_len : (sizeof(ldt) / sizeof(ldt[0])))
+
+#define valid_ldt_sel(sel) \
+ (ISLDT(sel) && ISPL(sel) == SEL_UPL && \
+ IDXSEL(sel) < max_ldt_sel(&p->p_addr->u_pcb))
+
+#define null_sel(sel) \
+ (!ISLDT(sel) && IDXSEL(sel) == 0)
+
+ if ((scp->sc_cs&0xffff != _ucodesel && !valid_ldt_sel(scp->sc_cs)) ||
+ (scp->sc_ss&0xffff != _udatasel && !valid_ldt_sel(scp->sc_ss)) ||
+ (scp->sc_ds&0xffff != _udatasel && !valid_ldt_sel(scp->sc_ds) &&
+ !null_sel(scp->sc_ds)) ||
+ (scp->sc_es&0xffff != _udatasel && !valid_ldt_sel(scp->sc_es) &&
+ !null_sel(scp->sc_es))) {
+#ifdef DEBUG
+ printf("sigreturn: cs=0x%x ss=0x%x ds=0x%x es=0x%x\n",
+ scp->sc_cs, scp->sc_ss, scp->sc_ds, scp->sc_es);
+#endif
+ trapsignal(p, SIGBUS, T_PROTFLT);
+ return(EINVAL);
+ }
+
+#undef max_ldt_sel
+#undef valid_ldt_sel
+#undef null_sel
+
+ /* restore scratch registers */
+ regs[tEAX] = scp->sc_eax;
+ regs[tEBX] = scp->sc_ebx;
+ regs[tECX] = scp->sc_ecx;
+ regs[tEDX] = scp->sc_edx;
+ regs[tESI] = scp->sc_esi;
+ regs[tEDI] = scp->sc_edi;
+ regs[tCS] = scp->sc_cs;
+ regs[tDS] = scp->sc_ds;
+ regs[tES] = scp->sc_es;
+ regs[tSS] = scp->sc_ss;
+ regs[tISP] = scp->sc_isp;
+
+ if (useracc((caddr_t)scp, sizeof (*scp), 0) == 0)
+ return(EINVAL);
+
+ if (scp->sc_onstack & 01)
+ p->p_sigacts->ps_sigstk.ss_flags |= SA_ONSTACK;
+ else
+ p->p_sigacts->ps_sigstk.ss_flags &= ~SA_ONSTACK;
+ p->p_sigmask = scp->sc_mask &~
+ (sigmask(SIGKILL)|sigmask(SIGCONT)|sigmask(SIGSTOP));
+ regs[tEBP] = scp->sc_fp;
+ regs[tESP] = scp->sc_sp;
+ regs[tEIP] = scp->sc_pc;
+ regs[tEFLAGS] = eflags;
+ return(EJUSTRETURN);
+}
+
+/*
+ * a simple function to make the system panic (and dump a vmcore)
+ * in a predictable fashion
+ */
+void diediedie()
+{
+ panic("because you said to!");
+}
+
+int waittime = -1;
+struct pcb dumppcb;
+
+void
+boot(arghowto)
+ int arghowto;
+{
+ register long dummy; /* r12 is reserved */
+ register int howto; /* r11 == how to boot */
+ register int devtype; /* r10 == major of root dev */
+ extern int cold;
+ int nomsg = 1;
+
+ if (cold) {
+ printf("hit reset please");
+ for(;;);
+ }
+ howto = arghowto;
+ if ((howto&RB_NOSYNC) == 0 && waittime < 0) {
+ register struct buf *bp;
+ int iter, nbusy;
+
+ waittime = 0;
+ (void) splnet();
+ printf("syncing disks... ");
+ /*
+ * Release inodes held by texts before update.
+ */
+ if (panicstr == 0)
+ vnode_pager_umount(NULL);
+ sync(curproc, NULL, NULL);
+ /*
+ * Unmount filesystems
+ */
+#if 0
+ if (panicstr == 0)
+ vfs_unmountall();
+#endif
+
+ for (iter = 0; iter < 20; iter++) {
+ nbusy = 0;
+ for (bp = &buf[nbuf]; --bp >= buf; )
+ if ((bp->b_flags & (B_BUSY|B_INVAL)) == B_BUSY)
+ nbusy++;
+ if (nbusy == 0)
+ break;
+ if (nomsg) {
+ printf("updating disks before rebooting... ");
+ nomsg = 0;
+ }
+ printf("%d ", nbusy);
+ DELAY(40000 * iter);
+ }
+ if (nbusy)
+ printf("giving up\n");
+ else
+ printf("done\n");
+ DELAY(10000); /* wait for printf to finish */
+ }
+ splhigh();
+ devtype = major(rootdev);
+ if (howto&RB_HALT) {
+ printf("\n");
+ printf("The operating system has halted.\n");
+ printf("Please press any key to reboot.\n\n");
+ cngetc();
+ } else {
+ if (howto & RB_DUMP) {
+ savectx(&dumppcb, 0);
+ dumppcb.pcb_ptd = rcr3();
+ dumpsys();
+
+ if (PANIC_REBOOT_WAIT_TIME != 0) {
+ if (PANIC_REBOOT_WAIT_TIME != -1) {
+ int loop;
+ printf("Automatic reboot in %d seconds - press a key on the console to abort\n",
+ PANIC_REBOOT_WAIT_TIME);
+ for (loop = PANIC_REBOOT_WAIT_TIME; loop > 0; --loop) {
+ DELAY(1000 * 1000); /* one second */
+ if (sgetc(1)) /* Did user type a key? */
+ break;
+ }
+ if (!loop)
+ goto die;
+ }
+ } else { /* zero time specified - reboot NOW */
+ goto die;
+ }
+ printf("--> Press a key on the console to reboot <--\n");
+ cngetc();
+ }
+ }
+#ifdef lint
+ dummy = 0; dummy = dummy;
+ printf("howto %d, devtype %d\n", arghowto, devtype);
+#endif
+die:
+ printf("Rebooting...\n");
+ DELAY(1000000); /* wait 1 sec for printf's to complete and be read */
+ cpu_reset();
+ for(;;) ;
+ /* NOTREACHED */
+}
+
+unsigned long dumpmag = 0x8fca0101UL; /* magic number for savecore */
+int dumpsize = 0; /* also for savecore */
+/*
+ * Doadump comes here after turning off memory management and
+ * getting on the dump stack, either when called above, or by
+ * the auto-restart code.
+ */
+void
+dumpsys()
+{
+
+ if (dumpdev == NODEV)
+ return;
+ if ((minor(dumpdev)&07) != 1)
+ return;
+ dumpsize = Maxmem;
+ printf("\ndumping to dev %x, offset %d\n", dumpdev, dumplo);
+ printf("dump ");
+ switch ((*bdevsw[major(dumpdev)].d_dump)(dumpdev)) {
+
+ case ENXIO:
+ printf("device bad\n");
+ break;
+
+ case EFAULT:
+ printf("device not ready\n");
+ break;
+
+ case EINVAL:
+ printf("area improper\n");
+ break;
+
+ case EIO:
+ printf("i/o error\n");
+ break;
+
+ case EINTR:
+ printf("aborted from console\n");
+ break;
+
+ default:
+ printf("succeeded\n");
+ break;
+ }
+}
+
+#ifdef HZ
+/*
+ * If HZ is defined we use this code, otherwise the code in
+ * /sys/i386/i386/microtime.s is used. The othercode only works
+ * for HZ=100.
+ */
+microtime(tvp)
+ register struct timeval *tvp;
+{
+ int s = splhigh();
+
+ *tvp = time;
+ tvp->tv_usec += tick;
+ while (tvp->tv_usec > 1000000) {
+ tvp->tv_sec++;
+ tvp->tv_usec -= 1000000;
+ }
+ splx(s);
+}
+#endif /* HZ */
+
+static void
+initcpu()
+{
+}
+
+/*
+ * Clear registers on exec
+ */
+void
+setregs(p, entry, stack)
+ struct proc *p;
+ u_long entry;
+ u_long stack;
+{
+ p->p_md.md_regs[tEBP] = 0; /* bottom of the fp chain */
+ p->p_md.md_regs[tEIP] = entry;
+ p->p_md.md_regs[tESP] = stack;
+ p->p_md.md_regs[tSS] = _udatasel;
+ p->p_md.md_regs[tDS] = _udatasel;
+ p->p_md.md_regs[tES] = _udatasel;
+ p->p_md.md_regs[tCS] = _ucodesel;
+
+ p->p_addr->u_pcb.pcb_flags = 0; /* no fp at all */
+ load_cr0(rcr0() | CR0_TS); /* start emulating */
+#if NNPX > 0
+ npxinit(__INITIAL_NPXCW__);
+#endif /* NNPX > 0 */
+}
+
+/*
+ * machine dependent system variables.
+ */
+int
+cpu_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+ struct proc *p;
+{
+
+ /* all sysctl names at this level are terminal */
+ if (namelen != 1)
+ return (ENOTDIR); /* overloaded */
+
+ switch (name[0]) {
+ case CPU_CONSDEV:
+ return (sysctl_rdstruct(oldp, oldlenp, newp, &cn_tty->t_dev,
+ sizeof cn_tty->t_dev));
+ default:
+ return (EOPNOTSUPP);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Initialize 386 and configure to run kernel
+ */
+
+/*
+ * Initialize segments & interrupt table
+ */
+
+union descriptor gdt[NGDT];
+union descriptor ldt[NLDT]; /* local descriptor table */
+struct gate_descriptor idt[NIDT]; /* interrupt descriptor table */
+
+int _default_ldt, currentldt;
+
+struct i386tss tss, panic_tss;
+
+extern struct user *proc0paddr;
+
+/* software prototypes -- in more palatable form */
+struct soft_segment_descriptor gdt_segs[] = {
+ /* Null Descriptor */
+{ 0x0, /* segment base address */
+ 0x0, /* length */
+ 0, /* segment type */
+ 0, /* segment descriptor priority level */
+ 0, /* segment descriptor present */
+ 0, 0,
+ 0, /* default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Code Descriptor for kernel */
+{ 0x0, /* segment base address */
+ 0xfffff, /* length - all address space */
+ SDT_MEMERA, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 1, /* default 32 vs 16 bit size */
+ 1 /* limit granularity (byte/page units)*/ },
+ /* Data Descriptor for kernel */
+{ 0x0, /* segment base address */
+ 0xfffff, /* length - all address space */
+ SDT_MEMRWA, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 1, /* default 32 vs 16 bit size */
+ 1 /* limit granularity (byte/page units)*/ },
+ /* LDT Descriptor */
+{ (int) ldt, /* segment base address */
+ sizeof(ldt)-1, /* length - all address space */
+ SDT_SYSLDT, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 0, /* unused - default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Null Descriptor - Placeholder */
+{ 0x0, /* segment base address */
+ 0x0, /* length - all address space */
+ 0, /* segment type */
+ 0, /* segment descriptor priority level */
+ 0, /* segment descriptor present */
+ 0, 0,
+ 0, /* default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Panic Tss Descriptor */
+{ (int) &panic_tss, /* segment base address */
+ sizeof(tss)-1, /* length - all address space */
+ SDT_SYS386TSS, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 0, /* unused - default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Proc 0 Tss Descriptor */
+{ (int) kstack, /* segment base address */
+ sizeof(tss)-1, /* length - all address space */
+ SDT_SYS386TSS, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 0, /* unused - default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* User LDT Descriptor per process */
+{ (int) ldt, /* segment base address */
+ (512 * sizeof(union descriptor)-1), /* length */
+ SDT_SYSLDT, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 0, /* unused - default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+};
+
+struct soft_segment_descriptor ldt_segs[] = {
+ /* Null Descriptor - overwritten by call gate */
+{ 0x0, /* segment base address */
+ 0x0, /* length - all address space */
+ 0, /* segment type */
+ 0, /* segment descriptor priority level */
+ 0, /* segment descriptor present */
+ 0, 0,
+ 0, /* default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Null Descriptor - overwritten by call gate */
+{ 0x0, /* segment base address */
+ 0x0, /* length - all address space */
+ 0, /* segment type */
+ 0, /* segment descriptor priority level */
+ 0, /* segment descriptor present */
+ 0, 0,
+ 0, /* default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Null Descriptor - overwritten by call gate */
+{ 0x0, /* segment base address */
+ 0x0, /* length - all address space */
+ 0, /* segment type */
+ 0, /* segment descriptor priority level */
+ 0, /* segment descriptor present */
+ 0, 0,
+ 0, /* default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Code Descriptor for user */
+{ 0x0, /* segment base address */
+ 0xfffff, /* length - all address space */
+ SDT_MEMERA, /* segment type */
+ SEL_UPL, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 1, /* default 32 vs 16 bit size */
+ 1 /* limit granularity (byte/page units)*/ },
+ /* Data Descriptor for user */
+{ 0x0, /* segment base address */
+ 0xfffff, /* length - all address space */
+ SDT_MEMRWA, /* segment type */
+ SEL_UPL, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 1, /* default 32 vs 16 bit size */
+ 1 /* limit granularity (byte/page units)*/ } };
+
+void
+setidt(idx, func, typ, dpl)
+ int idx;
+ void (*func)();
+ int typ;
+ int dpl;
+{
+ struct gate_descriptor *ip = idt + idx;
+
+ ip->gd_looffset = (int)func;
+ ip->gd_selector = 8;
+ ip->gd_stkcpy = 0;
+ ip->gd_xx = 0;
+ ip->gd_type = typ;
+ ip->gd_dpl = dpl;
+ ip->gd_p = 1;
+ ip->gd_hioffset = ((int)func)>>16 ;
+}
+
+#define IDTVEC(name) __CONCAT(X,name)
+typedef void idtvec_t();
+
+extern idtvec_t
+ IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl),
+ IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(dble), IDTVEC(fpusegm),
+ IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot),
+ IDTVEC(page), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(rsvd0),
+ IDTVEC(rsvd1), IDTVEC(rsvd2), IDTVEC(rsvd3), IDTVEC(rsvd4),
+ IDTVEC(rsvd5), IDTVEC(rsvd6), IDTVEC(rsvd7), IDTVEC(rsvd8),
+ IDTVEC(rsvd9), IDTVEC(rsvd10), IDTVEC(rsvd11), IDTVEC(rsvd12),
+ IDTVEC(rsvd13), IDTVEC(rsvd14), IDTVEC(syscall);
+
+int _gsel_tss;
+
+void
+init386(first)
+ int first;
+{
+ extern ssdtosd(), lgdt(), lidt(), lldt(), etext;
+ int x, *pi;
+ unsigned biosbasemem, biosextmem;
+ struct gate_descriptor *gdp;
+ extern int sigcode,szsigcode;
+ /* table descriptors - used to load tables by microp */
+ struct region_descriptor r_gdt, r_idt;
+ int pagesinbase, pagesinext;
+ int target_page;
+
+ proc0.p_addr = proc0paddr;
+
+ /*
+ * Initialize the console before we print anything out.
+ */
+
+ cninit ();
+
+ /*
+ * make gdt memory segments, the code segment goes up to end of the
+ * page with etext in it, the data segment goes to the end of
+ * the address space
+ */
+ gdt_segs[GCODE_SEL].ssd_limit = i386_btop(i386_round_page(&etext)) - 1;
+ gdt_segs[GDATA_SEL].ssd_limit = i386_btop(0) - 1;
+ for (x=0; x < NGDT; x++) ssdtosd(gdt_segs+x, gdt+x);
+
+ /* make ldt memory segments */
+ /*
+ * The data segment limit must not cover the user area because we
+ * don't want the user area to be writable in copyout() etc. (page
+ * level protection is lost in kernel mode on 386's). Also, we
+ * don't want the user area to be writable directly (page level
+ * protection of the user area is not available on 486's with
+ * CR0_WP set, because there is no user-read/kernel-write mode).
+ *
+ * XXX - VM_MAXUSER_ADDRESS is an end address, not a max. And it
+ * should be spelled ...MAX_USER...
+ */
+#define VM_END_USER_RW_ADDRESS VM_MAXUSER_ADDRESS
+ /*
+ * The code segment limit has to cover the user area until we move
+ * the signal trampoline out of the user area. This is safe because
+ * the code segment cannot be written to directly.
+ */
+#define VM_END_USER_R_ADDRESS (VM_END_USER_RW_ADDRESS + UPAGES * NBPG)
+ ldt_segs[LUCODE_SEL].ssd_limit = i386_btop(VM_END_USER_R_ADDRESS) - 1;
+ ldt_segs[LUDATA_SEL].ssd_limit = i386_btop(VM_END_USER_RW_ADDRESS) - 1;
+ /* Note. eventually want private ldts per process */
+ for (x=0; x < 5; x++) ssdtosd(ldt_segs+x, ldt+x);
+
+ /* exceptions */
+ setidt(0, &IDTVEC(div), SDT_SYS386TGT, SEL_KPL);
+ setidt(1, &IDTVEC(dbg), SDT_SYS386TGT, SEL_KPL);
+ setidt(2, &IDTVEC(nmi), SDT_SYS386TGT, SEL_KPL);
+ setidt(3, &IDTVEC(bpt), SDT_SYS386TGT, SEL_UPL);
+ setidt(4, &IDTVEC(ofl), SDT_SYS386TGT, SEL_UPL);
+ setidt(5, &IDTVEC(bnd), SDT_SYS386TGT, SEL_KPL);
+ setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL);
+ setidt(7, &IDTVEC(dna), SDT_SYS386TGT, SEL_KPL);
+ setidt(8, &IDTVEC(dble), SDT_SYS386TGT, SEL_KPL);
+ setidt(9, &IDTVEC(fpusegm), SDT_SYS386TGT, SEL_KPL);
+ setidt(10, &IDTVEC(tss), SDT_SYS386TGT, SEL_KPL);
+ setidt(11, &IDTVEC(missing), SDT_SYS386TGT, SEL_KPL);
+ setidt(12, &IDTVEC(stk), SDT_SYS386TGT, SEL_KPL);
+ setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL);
+ setidt(14, &IDTVEC(page), SDT_SYS386TGT, SEL_KPL);
+ setidt(15, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL);
+ setidt(16, &IDTVEC(fpu), SDT_SYS386TGT, SEL_KPL);
+ setidt(17, &IDTVEC(rsvd0), SDT_SYS386TGT, SEL_KPL);
+ setidt(18, &IDTVEC(rsvd1), SDT_SYS386TGT, SEL_KPL);
+ setidt(19, &IDTVEC(rsvd2), SDT_SYS386TGT, SEL_KPL);
+ setidt(20, &IDTVEC(rsvd3), SDT_SYS386TGT, SEL_KPL);
+ setidt(21, &IDTVEC(rsvd4), SDT_SYS386TGT, SEL_KPL);
+ setidt(22, &IDTVEC(rsvd5), SDT_SYS386TGT, SEL_KPL);
+ setidt(23, &IDTVEC(rsvd6), SDT_SYS386TGT, SEL_KPL);
+ setidt(24, &IDTVEC(rsvd7), SDT_SYS386TGT, SEL_KPL);
+ setidt(25, &IDTVEC(rsvd8), SDT_SYS386TGT, SEL_KPL);
+ setidt(26, &IDTVEC(rsvd9), SDT_SYS386TGT, SEL_KPL);
+ setidt(27, &IDTVEC(rsvd10), SDT_SYS386TGT, SEL_KPL);
+ setidt(28, &IDTVEC(rsvd11), SDT_SYS386TGT, SEL_KPL);
+ setidt(29, &IDTVEC(rsvd12), SDT_SYS386TGT, SEL_KPL);
+ setidt(30, &IDTVEC(rsvd13), SDT_SYS386TGT, SEL_KPL);
+ setidt(31, &IDTVEC(rsvd14), SDT_SYS386TGT, SEL_KPL);
+
+#include "isa.h"
+#if NISA >0
+ isa_defaultirq();
+#endif
+
+ r_gdt.rd_limit = sizeof(gdt) - 1;
+ r_gdt.rd_base = (int) gdt;
+ lgdt(&r_gdt);
+
+ r_idt.rd_limit = sizeof(idt) - 1;
+ r_idt.rd_base = (int) idt;
+ lidt(&r_idt);
+
+ _default_ldt = GSEL(GLDT_SEL, SEL_KPL);
+ lldt(_default_ldt);
+ currentldt = _default_ldt;
+
+#include "ddb.h"
+#if NDDB > 0
+ kdb_init();
+ if (boothowto & RB_KDB)
+ Debugger("Boot flags requested debugger");
+#endif
+
+ /* Use BIOS values stored in RTC CMOS RAM, since probing
+ * breaks certain 386 AT relics.
+ */
+ biosbasemem = rtcin(RTC_BASELO)+ (rtcin(RTC_BASEHI)<<8);
+ biosextmem = rtcin(RTC_EXTLO)+ (rtcin(RTC_EXTHI)<<8);
+
+ /*
+ * If BIOS tells us that it has more than 640k in the basemem,
+ * don't believe it - set it to 640k.
+ */
+ if (biosbasemem > 640)
+ biosbasemem = 640;
+
+ /*
+ * Some 386 machines might give us a bogus number for extended
+ * mem. If this happens, stop now.
+ */
+#ifndef LARGEMEM
+ if (biosextmem > 65536) {
+ panic("extended memory beyond limit of 64MB");
+ /* NOTREACHED */
+ }
+#endif
+
+ pagesinbase = biosbasemem * 1024 / NBPG;
+ pagesinext = biosextmem * 1024 / NBPG;
+
+ /*
+ * Special hack for chipsets that still remap the 384k hole when
+ * there's 16MB of memory - this really confuses people that
+ * are trying to use bus mastering ISA controllers with the
+ * "16MB limit"; they only have 16MB, but the remapping puts
+ * them beyond the limit.
+ * XXX - this should be removed when bounce buffers are
+ * implemented.
+ */
+ /*
+ * If extended memory is between 15-16MB (16-17MB phys address range),
+ * chop it to 15MB.
+ */
+ if ((pagesinext > 3840) && (pagesinext < 4096))
+ pagesinext = 3840;
+
+ /*
+ * Maxmem isn't the "maximum memory", it's the highest page of
+ * of the physical address space. It should be "Maxphyspage".
+ */
+ Maxmem = pagesinext + 0x100000/PAGE_SIZE;
+
+#ifdef MAXMEM
+ if (MAXMEM/4 < Maxmem)
+ Maxmem = MAXMEM/4;
+#endif
+ /*
+ * Calculate number of physical pages, but account for Maxmem
+ * limitation above.
+ */
+ physmem = pagesinbase +
+ (min(pagesinext + 0x100000/PAGE_SIZE, Maxmem) - 0x100000/PAGE_SIZE);
+
+ /* call pmap initialization to make new kernel address space */
+ pmap_bootstrap (first, 0);
+
+ /*
+ * Do simple memory test over range of extended memory that BIOS
+ * indicates exists. Adjust Maxmem to the highest page of
+ * good memory.
+ */
+ printf("Testing memory (%dMB)...", ptoa(Maxmem)/1024/1024);
+
+ for (target_page = Maxmem - 1; target_page >= atop(first); target_page--) {
+ extern struct pte *CMAP1;
+ extern caddr_t CADDR1;
+
+ /*
+ * map page into kernel: valid, read/write, non-cacheable
+ */
+ *(int *)CMAP1 = PG_V | PG_KW | PG_N | ptoa(target_page);
+ tlbflush();
+
+ /*
+ * Test for alternating 1's and 0's
+ */
+ filli(0xaaaaaaaa, CADDR1, PAGE_SIZE/sizeof(int));
+ if (test_page((int *)CADDR1, 0xaaaaaaaa)) {
+ Maxmem = target_page;
+ badpages++;
+ continue;
+ }
+ /*
+ * Test for alternating 0's and 1's
+ */
+ filli(0x55555555, CADDR1, PAGE_SIZE/sizeof(int));
+ if (test_page((int *)CADDR1, 0x55555555)) {
+ Maxmem = target_page;
+ badpages++;
+ continue;
+ }
+ /*
+ * Test for all 1's
+ */
+ filli(0xffffffff, CADDR1, PAGE_SIZE/sizeof(int));
+ if (test_page((int *)CADDR1, 0xffffffff)) {
+ Maxmem = target_page;
+ badpages++;
+ continue;
+ }
+ /*
+ * Test zeroing of page
+ */
+ bzero(CADDR1, PAGE_SIZE);
+ if (test_page((int *)CADDR1, 0)) {
+ /*
+ * test of page failed
+ */
+ Maxmem = target_page;
+ badpages++;
+ continue;
+ }
+ }
+ printf("done.\n");
+
+ avail_end = (Maxmem << PAGE_SHIFT)
+ - i386_round_page(sizeof(struct msgbuf));
+
+ /*
+ * Initialize pointers to the two chunks of memory; for use
+ * later in vm_page_startup.
+ */
+ /* avail_start is initialized in pmap_bootstrap */
+ x = 0;
+ if (pagesinbase > 1) {
+ phys_avail[x++] = NBPG; /* skip first page of memory */
+ phys_avail[x++] = pagesinbase * NBPG; /* memory up to the ISA hole */
+ }
+ phys_avail[x++] = avail_start; /* memory up to the end */
+ phys_avail[x++] = avail_end;
+ phys_avail[x++] = 0; /* no more chunks */
+ phys_avail[x++] = 0;
+
+ /* now running on new page tables, configured,and u/iom is accessible */
+
+ /* make a initial tss so microp can get interrupt stack on syscall! */
+ proc0.p_addr->u_pcb.pcb_tss.tss_esp0 = (int) kstack + UPAGES*NBPG;
+ proc0.p_addr->u_pcb.pcb_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL) ;
+ _gsel_tss = GSEL(GPROC0_SEL, SEL_KPL);
+
+ ((struct i386tss *)gdt_segs[GPROC0_SEL].ssd_base)->tss_ioopt =
+ (sizeof(tss))<<16;
+
+ ltr(_gsel_tss);
+
+ /* make a call gate to reenter kernel with */
+ gdp = &ldt[LSYS5CALLS_SEL].gd;
+
+ x = (int) &IDTVEC(syscall);
+ gdp->gd_looffset = x++;
+ gdp->gd_selector = GSEL(GCODE_SEL,SEL_KPL);
+ gdp->gd_stkcpy = 1;
+ gdp->gd_type = SDT_SYS386CGT;
+ gdp->gd_dpl = SEL_UPL;
+ gdp->gd_p = 1;
+ gdp->gd_hioffset = ((int) &IDTVEC(syscall)) >>16;
+
+ /* transfer to user mode */
+
+ _ucodesel = LSEL(LUCODE_SEL, SEL_UPL);
+ _udatasel = LSEL(LUDATA_SEL, SEL_UPL);
+
+ /* setup proc 0's pcb */
+ bcopy(&sigcode, proc0.p_addr->u_pcb.pcb_sigc, szsigcode);
+ proc0.p_addr->u_pcb.pcb_flags = 0;
+ proc0.p_addr->u_pcb.pcb_ptd = IdlePTD;
+}
+
+int
+test_page(address, pattern)
+ int *address;
+ int pattern;
+{
+ int *x;
+
+ for (x = address; x < (int *)((char *)address + PAGE_SIZE); x++) {
+ if (*x != pattern)
+ return (1);
+ }
+ return(0);
+}
+
+/*
+ * insert an element into a queue
+ */
+#undef insque
+void /* XXX replace with inline FIXME! */
+_insque(element, head)
+ register struct prochd *element, *head;
+{
+ element->ph_link = head->ph_link;
+ head->ph_link = (struct proc *)element;
+ element->ph_rlink = (struct proc *)head;
+ ((struct prochd *)(element->ph_link))->ph_rlink=(struct proc *)element;
+}
+
+/*
+ * remove an element from a queue
+ */
+#undef remque
+void /* XXX replace with inline FIXME! */
+_remque(element)
+ register struct prochd *element;
+{
+ ((struct prochd *)(element->ph_link))->ph_rlink = element->ph_rlink;
+ ((struct prochd *)(element->ph_rlink))->ph_link = element->ph_link;
+ element->ph_rlink = (struct proc *)0;
+}
+
+/*
+ * The registers are in the frame; the frame is in the user area of
+ * the process in question; when the process is active, the registers
+ * are in "the kernel stack"; when it's not, they're still there, but
+ * things get flipped around. So, since p->p_md.md_regs is the whole address
+ * of the register set, take its offset from the kernel stack, and
+ * index into the user block. Don't you just *love* virtual memory?
+ * (I'm starting to think seymour is right...)
+ */
+
+int
+ptrace_set_pc (struct proc *p, unsigned int addr) {
+ void *regs = (char*)p->p_addr +
+ ((char*) p->p_md.md_regs - (char*) kstack);
+
+ ((struct trapframe *)regs)->tf_eip = addr;
+ return 0;
+}
+
+int
+ptrace_single_step (struct proc *p) {
+ void *regs = (char*)p->p_addr +
+ ((char*) p->p_md.md_regs - (char*) kstack);
+
+ ((struct trapframe *)regs)->tf_eflags |= PSL_T;
+ return 0;
+}
+
+/*
+ * Copy the registers to user-space.
+ */
+
+int
+ptrace_getregs (struct proc *p, unsigned int *addr) {
+ int error;
+ struct reg regs = {0};
+
+ if (error = fill_regs (p, &regs))
+ return error;
+
+ return copyout (&regs, addr, sizeof (regs));
+}
+
+int
+ptrace_setregs (struct proc *p, unsigned int *addr) {
+ int error;
+ struct reg regs = {0};
+
+ if (error = copyin (addr, &regs, sizeof(regs)))
+ return error;
+
+ return set_regs (p, &regs);
+}
+
+int
+fill_regs(struct proc *p, struct reg *regs) {
+ int error;
+ struct trapframe *tp;
+ void *ptr = (char*)p->p_addr +
+ ((char*) p->p_md.md_regs - (char*) kstack);
+
+ tp = ptr;
+ regs->r_es = tp->tf_es;
+ regs->r_ds = tp->tf_ds;
+ regs->r_edi = tp->tf_edi;
+ regs->r_esi = tp->tf_esi;
+ regs->r_ebp = tp->tf_ebp;
+ regs->r_ebx = tp->tf_ebx;
+ regs->r_edx = tp->tf_edx;
+ regs->r_ecx = tp->tf_ecx;
+ regs->r_eax = tp->tf_eax;
+ regs->r_eip = tp->tf_eip;
+ regs->r_cs = tp->tf_cs;
+ regs->r_eflags = tp->tf_eflags;
+ regs->r_esp = tp->tf_esp;
+ regs->r_ss = tp->tf_ss;
+ return 0;
+}
+
+int
+set_regs (struct proc *p, struct reg *regs) {
+ int error;
+ struct trapframe *tp;
+ void *ptr = (char*)p->p_addr +
+ ((char*) p->p_md.md_regs - (char*) kstack);
+
+ tp = ptr;
+ tp->tf_es = regs->r_es;
+ tp->tf_ds = regs->r_ds;
+ tp->tf_edi = regs->r_edi;
+ tp->tf_esi = regs->r_esi;
+ tp->tf_ebp = regs->r_ebp;
+ tp->tf_ebx = regs->r_ebx;
+ tp->tf_edx = regs->r_edx;
+ tp->tf_ecx = regs->r_ecx;
+ tp->tf_eax = regs->r_eax;
+ tp->tf_eip = regs->r_eip;
+ tp->tf_cs = regs->r_cs;
+ tp->tf_eflags = regs->r_eflags;
+ tp->tf_esp = regs->r_esp;
+ tp->tf_ss = regs->r_ss;
+ return 0;
+}
+
+#include "ddb.h"
+#if NDDB <= 0
+void
+Debugger(const char *msg)
+{
+ printf("Debugger(\"%s\") called.\n", msg);
+}
+#endif /* no DDB */
+
+#include <sys/disklabel.h>
+#define b_cylin b_resid
+#define dkpart(dev) (minor(dev) & 7)
+/*
+ * Determine the size of the transfer, and make sure it is
+ * within the boundaries of the partition. Adjust transfer
+ * if needed, and signal errors or early completion.
+ */
+int
+bounds_check_with_label(struct buf *bp, struct disklabel *lp, int wlabel)
+{
+ struct partition *p = lp->d_partitions + dkpart(bp->b_dev);
+ int labelsect = lp->d_partitions[0].p_offset;
+ int maxsz = p->p_size,
+ sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT;
+
+ /* overwriting disk label ? */
+ /* XXX should also protect bootstrap in first 8K */
+ if (bp->b_blkno + p->p_offset <= LABELSECTOR + labelsect &&
+#if LABELSECTOR != 0
+ bp->b_blkno + p->p_offset + sz > LABELSECTOR + labelsect &&
+#endif
+ (bp->b_flags & B_READ) == 0 && wlabel == 0) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+
+#if defined(DOSBBSECTOR) && defined(notyet)
+ /* overwriting master boot record? */
+ if (bp->b_blkno + p->p_offset <= DOSBBSECTOR &&
+ (bp->b_flags & B_READ) == 0 && wlabel == 0) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+#endif
+
+ /* beyond partition? */
+ if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) {
+ /* if exactly at end of disk, return an EOF */
+ if (bp->b_blkno == maxsz) {
+ bp->b_resid = bp->b_bcount;
+ return(0);
+ }
+ /* or truncate if part of it fits */
+ sz = maxsz - bp->b_blkno;
+ if (sz <= 0) {
+ bp->b_error = EINVAL;
+ goto bad;
+ }
+ bp->b_bcount = sz << DEV_BSHIFT;
+ }
+
+ /* calculate cylinder for disksort to order transfers with */
+ bp->b_pblkno = bp->b_blkno + p->p_offset;
+ bp->b_cylin = bp->b_pblkno / lp->d_secpercyl;
+ return(1);
+
+bad:
+ bp->b_flags |= B_ERROR;
+ return(-1);
+}
+
diff --git a/sys/amd64/amd64/mem.c b/sys/amd64/amd64/mem.c
new file mode 100644
index 0000000..1b8f187
--- /dev/null
+++ b/sys/amd64/amd64/mem.c
@@ -0,0 +1,259 @@
+/*-
+ * Copyright (c) 1988 University of Utah.
+ * Copyright (c) 1982, 1986, 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department, and code derived from software contributed to
+ * Berkeley by William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: Utah $Hdr: mem.c 1.13 89/10/08$
+ * from: @(#)mem.c 7.2 (Berkeley) 5/9/91
+ * $Id: mem.c,v 1.6 1993/12/19 00:50:06 wollman Exp $
+ */
+
+/*
+ * Memory special file
+ */
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/buf.h>
+#include <sys/systm.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <machine/cpu.h>
+#include <machine/psl.h>
+
+#include <vm/vm_param.h>
+#include <vm/lock.h>
+#include <vm/vm_prot.h>
+#include <vm/pmap.h>
+
+extern char *ptvmmap; /* poor name! */
+/*ARGSUSED*/
+int
+mmclose(dev, uio, flags)
+ dev_t dev;
+ struct uio *uio;
+ int flags;
+{
+ struct trapframe *fp;
+
+ switch (minor(dev)) {
+ case 14:
+ fp = (struct trapframe *)curproc->p_md.md_regs;
+ fp->tf_eflags &= ~PSL_IOPL;
+ break;
+ default:
+ break;
+ }
+ return(0);
+}
+/*ARGSUSED*/
+int
+mmopen(dev, uio, flags)
+ dev_t dev;
+ struct uio *uio;
+ int flags;
+{
+ struct trapframe *fp;
+
+ switch (minor(dev)) {
+ case 14:
+ fp = (struct trapframe *)curproc->p_md.md_regs;
+ fp->tf_eflags |= PSL_IOPL;
+ break;
+ default:
+ break;
+ }
+ return(0);
+}
+/*ARGSUSED*/
+int
+mmrw(dev, uio, flags)
+ dev_t dev;
+ struct uio *uio;
+ int flags;
+{
+ register int o;
+ register u_int c, v;
+ register struct iovec *iov;
+ int error = 0;
+ caddr_t zbuf = NULL;
+
+ while (uio->uio_resid > 0 && error == 0) {
+ iov = uio->uio_iov;
+ if (iov->iov_len == 0) {
+ uio->uio_iov++;
+ uio->uio_iovcnt--;
+ if (uio->uio_iovcnt < 0)
+ panic("mmrw");
+ continue;
+ }
+ switch (minor(dev)) {
+
+/* minor device 0 is physical memory */
+ case 0:
+ v = uio->uio_offset;
+ pmap_enter(kernel_pmap, (vm_offset_t)ptvmmap, v,
+ uio->uio_rw == UIO_READ ? VM_PROT_READ : VM_PROT_WRITE,
+ TRUE);
+ o = (int)uio->uio_offset & PGOFSET;
+ c = (u_int)(NBPG - ((int)iov->iov_base & PGOFSET));
+ c = min(c, (u_int)(NBPG - o));
+ c = min(c, (u_int)iov->iov_len);
+ error = uiomove((caddr_t)&ptvmmap[o], (int)c, uio);
+ pmap_remove(kernel_pmap, (vm_offset_t)ptvmmap,
+ (vm_offset_t)&ptvmmap[NBPG]);
+ continue;
+
+/* minor device 1 is kernel memory */
+ case 1:
+ c = iov->iov_len;
+ if (!kernacc((caddr_t)(int)uio->uio_offset, c,
+ uio->uio_rw == UIO_READ ? B_READ : B_WRITE))
+ return(EFAULT);
+ error = uiomove((caddr_t)(int)uio->uio_offset, (int)c, uio);
+ continue;
+
+/* minor device 2 is EOF/RATHOLE */
+ case 2:
+ if (uio->uio_rw == UIO_READ)
+ return (0);
+ c = iov->iov_len;
+ break;
+
+/* minor device 12 (/dev/zero) is source of nulls on read, rathole on write */
+ case 12:
+ if (uio->uio_rw == UIO_WRITE) {
+ c = iov->iov_len;
+ break;
+ }
+ if (zbuf == NULL) {
+ zbuf = (caddr_t)
+ malloc(CLBYTES, M_TEMP, M_WAITOK);
+ bzero(zbuf, CLBYTES);
+ }
+ c = min(iov->iov_len, CLBYTES);
+ error = uiomove(zbuf, (int)c, uio);
+ continue;
+
+#ifdef notyet
+/* 386 I/O address space (/dev/ioport[bwl]) is a read/write access to seperate
+ i/o device address bus, different than memory bus. Semantics here are
+ very different than ordinary read/write, as if iov_len is a multiple
+ an implied string move from a single port will be done. Note that lseek
+ must be used to set the port number reliably. */
+ case 14:
+ if (iov->iov_len == 1) {
+ u_char tmp;
+ tmp = inb(uio->uio_offset);
+ error = uiomove (&tmp, iov->iov_len, uio);
+ } else {
+ if (!useracc((caddr_t)iov->iov_base,
+ iov->iov_len, uio->uio_rw))
+ return (EFAULT);
+ insb(uio->uio_offset, iov->iov_base,
+ iov->iov_len);
+ }
+ break;
+ case 15:
+ if (iov->iov_len == sizeof (short)) {
+ u_short tmp;
+ tmp = inw(uio->uio_offset);
+ error = uiomove (&tmp, iov->iov_len, uio);
+ } else {
+ if (!useracc((caddr_t)iov->iov_base,
+ iov->iov_len, uio->uio_rw))
+ return (EFAULT);
+ insw(uio->uio_offset, iov->iov_base,
+ iov->iov_len/ sizeof (short));
+ }
+ break;
+ case 16:
+ if (iov->iov_len == sizeof (long)) {
+ u_long tmp;
+ tmp = inl(uio->uio_offset);
+ error = uiomove (&tmp, iov->iov_len, uio);
+ } else {
+ if (!useracc((caddr_t)iov->iov_base,
+ iov->iov_len, uio->uio_rw))
+ return (EFAULT);
+ insl(uio->uio_offset, iov->iov_base,
+ iov->iov_len/ sizeof (long));
+ }
+ break;
+#endif
+
+ default:
+ return (ENXIO);
+ }
+ if (error)
+ break;
+ iov->iov_base += c;
+ iov->iov_len -= c;
+ uio->uio_offset += c;
+ uio->uio_resid -= c;
+ }
+ if (zbuf)
+ free(zbuf, M_TEMP);
+ return (error);
+}
+
+
+
+
+/*******************************************************\
+* allow user processes to MMAP some memory sections *
+* instead of going through read/write *
+\*******************************************************/
+int memmmap(dev_t dev, int offset, int nprot)
+{
+ switch (minor(dev))
+ {
+
+/* minor device 0 is physical memory */
+ case 0:
+ return i386_btop(offset);
+
+/* minor device 1 is kernel memory */
+ case 1:
+ return i386_btop(vtophys(offset));
+
+ default:
+ return -1;
+ }
+}
+
diff --git a/sys/amd64/amd64/pmap.c b/sys/amd64/amd64/pmap.c
new file mode 100644
index 0000000..88db9dd
--- /dev/null
+++ b/sys/amd64/amd64/pmap.c
@@ -0,0 +1,1944 @@
+/*
+ * Copyright (c) 1991 Regents of the University of California.
+ * All rights reserved.
+ * Copyright (c) 1994 John S. Dyson
+ * All rights reserved.
+ * Copyright (c) 1994 David Greenman
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department and William Jolitz of UUNET Technologies Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)pmap.c 7.7 (Berkeley) 5/12/91
+ * $Id: pmap.c,v 1.24 1994/04/20 07:06:14 davidg Exp $
+ */
+
+/*
+ * Derived from hp300 version by Mike Hibler, this version by William
+ * Jolitz uses a recursive map [a pde points to the page directory] to
+ * map the page tables using the pagetables themselves. This is done to
+ * reduce the impact on kernel virtual memory for lots of sparse address
+ * space, and to reduce the cost of memory to each process.
+ *
+ * Derived from: hp300/@(#)pmap.c 7.1 (Berkeley) 12/5/90
+ */
+/*
+ * Major modifications by John S. Dyson primarily to support
+ * pageable page tables, eliminating pmap_attributes,
+ * discontiguous memory pages, and using more efficient string
+ * instructions. Jan 13, 1994. Further modifications on Mar 2, 1994,
+ * general clean-up and efficiency mods.
+ */
+
+/*
+ * Manages physical address maps.
+ *
+ * In addition to hardware address maps, this
+ * module is called upon to provide software-use-only
+ * maps which may or may not be stored in the same
+ * form as hardware maps. These pseudo-maps are
+ * used to store intermediate results from copy
+ * operations to and from address spaces.
+ *
+ * Since the information managed by this module is
+ * also stored by the logical address mapping module,
+ * this module may throw away valid virtual-to-physical
+ * mappings at almost any time. However, invalidations
+ * of virtual-to-physical mappings must be done as
+ * requested.
+ *
+ * In order to cope with hardware architectures which
+ * make virtual-to-physical map invalidates expensive,
+ * this module may delay invalidate or reduced protection
+ * operations until such time as they are actually
+ * necessary. This module is given full information as
+ * to which processors are currently using which maps,
+ * and to when physical maps must be made correct.
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/user.h>
+
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+
+#include <i386/include/cpufunc.h>
+#include <i386/include/cputypes.h>
+
+#include <i386/isa/isa.h>
+
+/*
+ * Allocate various and sundry SYSMAPs used in the days of old VM
+ * and not yet converted. XXX.
+ */
+#define BSDVM_COMPAT 1
+
+/*
+ * Get PDEs and PTEs for user/kernel address space
+ */
+#define pmap_pde(m, v) (&((m)->pm_pdir[((vm_offset_t)(v) >> PD_SHIFT)&1023]))
+#define pdir_pde(m, v) (m[((vm_offset_t)(v) >> PD_SHIFT)&1023])
+
+#define pmap_pte_pa(pte) (*(int *)(pte) & PG_FRAME)
+
+#define pmap_pde_v(pte) ((*(int *)pte & PG_V) != 0)
+#define pmap_pte_w(pte) ((*(int *)pte & PG_W) != 0)
+#define pmap_pte_m(pte) ((*(int *)pte & PG_M) != 0)
+#define pmap_pte_u(pte) ((*(int *)pte & PG_U) != 0)
+#define pmap_pte_v(pte) ((*(int *)pte & PG_V) != 0)
+
+#define pmap_pte_set_w(pte, v) ((v)?(*(int *)pte |= PG_W):(*(int *)pte &= ~PG_W))
+#define pmap_pte_set_prot(pte, v) ((*(int *)pte &= ~PG_PROT), (*(int *)pte |= (v)))
+
+/*
+ * Given a map and a machine independent protection code,
+ * convert to a vax protection code.
+ */
+#define pte_prot(m, p) (protection_codes[p])
+int protection_codes[8];
+
+struct pmap kernel_pmap_store;
+pmap_t kernel_pmap;
+
+vm_offset_t phys_avail[6]; /* 2 entries + 1 null */
+vm_offset_t avail_start; /* PA of first available physical page */
+vm_offset_t avail_end; /* PA of last available physical page */
+vm_size_t mem_size; /* memory size in bytes */
+vm_offset_t virtual_avail; /* VA of first avail page (after kernel bss)*/
+vm_offset_t virtual_end; /* VA of last avail page (end of kernel AS) */
+int i386pagesperpage; /* PAGE_SIZE / I386_PAGE_SIZE */
+boolean_t pmap_initialized = FALSE; /* Has pmap_init completed? */
+vm_offset_t vm_first_phys, vm_last_phys;
+
+static inline boolean_t pmap_testbit();
+static inline void pmap_changebit();
+static inline int pmap_is_managed();
+static inline void *vm_get_pmap();
+static inline void vm_put_pmap();
+inline void pmap_use_pt();
+inline void pmap_unuse_pt();
+inline pt_entry_t * pmap_pte();
+static inline pv_entry_t get_pv_entry();
+void pmap_alloc_pv_entry();
+void pmap_clear_modify();
+void i386_protection_init();
+extern vm_offset_t clean_sva, clean_eva;
+extern int cpu_class;
+
+#if BSDVM_COMPAT
+#include "msgbuf.h"
+
+/*
+ * All those kernel PT submaps that BSD is so fond of
+ */
+pt_entry_t *CMAP1, *CMAP2, *ptmmap;
+caddr_t CADDR1, CADDR2, ptvmmap;
+pt_entry_t *msgbufmap;
+struct msgbuf *msgbufp;
+#endif
+
+void init_pv_entries(int) ;
+
+/*
+ * Routine: pmap_pte
+ * Function:
+ * Extract the page table entry associated
+ * with the given map/virtual_address pair.
+ * [ what about induced faults -wfj]
+ */
+
+inline pt_entry_t *
+pmap_pte(pmap, va)
+ pmap_t pmap;
+ vm_offset_t va;
+{
+
+ if (pmap && *pmap_pde(pmap, va)) {
+ vm_offset_t frame = (int) pmap->pm_pdir[PTDPTDI] & PG_FRAME;
+ /* are we current address space or kernel? */
+ if ( (pmap == kernel_pmap) || (frame == ((int) PTDpde & PG_FRAME)))
+ return ((pt_entry_t *) vtopte(va));
+ /* otherwise, we are alternate address space */
+ else {
+ if ( frame != ((int) APTDpde & PG_FRAME) ) {
+ APTDpde = pmap->pm_pdir[PTDPTDI];
+ tlbflush();
+ }
+ return((pt_entry_t *) avtopte(va));
+ }
+ }
+ return(0);
+}
+
+/*
+ * Routine: pmap_extract
+ * Function:
+ * Extract the physical page address associated
+ * with the given map/virtual_address pair.
+ */
+
+vm_offset_t
+pmap_extract(pmap, va)
+ register pmap_t pmap;
+ vm_offset_t va;
+{
+ pd_entry_t save;
+ vm_offset_t pa;
+ int s;
+
+ if (pmap && *pmap_pde(pmap, va)) {
+ vm_offset_t frame = (int) pmap->pm_pdir[PTDPTDI] & PG_FRAME;
+ /* are we current address space or kernel? */
+ if ( (pmap == kernel_pmap)
+ || (frame == ((int) PTDpde & PG_FRAME)) ) {
+ pa = *(int *) vtopte(va);
+ /* otherwise, we are alternate address space */
+ } else {
+ if ( frame != ((int) APTDpde & PG_FRAME)) {
+ APTDpde = pmap->pm_pdir[PTDPTDI];
+ tlbflush();
+ }
+ pa = *(int *) avtopte(va);
+ }
+ pa = (pa & PG_FRAME) | (va & ~PG_FRAME);
+ return pa;
+ }
+ return 0;
+
+}
+
+/*
+ * determine if a page is managed (memory vs. device)
+ */
+static inline int
+pmap_is_managed(pa)
+ vm_offset_t pa;
+{
+ int i;
+
+ if (!pmap_initialized)
+ return 0;
+
+ for (i = 0; phys_avail[i + 1]; i += 2) {
+ if (pa >= phys_avail[i] && pa < phys_avail[i + 1])
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * find the vm_page_t of a pte (only) given va of pte and pmap
+ */
+inline vm_page_t
+pmap_pte_vm_page(pmap, pt)
+ pmap_t pmap;
+ vm_offset_t pt;
+{
+ pt = i386_trunc_page( pt);
+ pt = (pt - UPT_MIN_ADDRESS) / NBPG;
+ pt = ((vm_offset_t) pmap->pm_pdir[pt]) & PG_FRAME;
+ return PHYS_TO_VM_PAGE(pt);
+}
+
+/*
+ * Wire a page table page
+ */
+inline void
+pmap_use_pt(pmap, va)
+ pmap_t pmap;
+ vm_offset_t va;
+{
+ vm_offset_t pt;
+
+ if (va >= VM_MAX_ADDRESS || !pmap_initialized)
+ return;
+
+ pt = (vm_offset_t) vtopte(va);
+ vm_page_hold( pmap_pte_vm_page(pmap, pt));
+}
+
+/*
+ * Unwire a page table page
+ */
+inline void
+pmap_unuse_pt(pmap, va)
+ pmap_t pmap;
+ vm_offset_t va;
+{
+ vm_offset_t pt;
+
+ if (va >= VM_MAX_ADDRESS || !pmap_initialized)
+ return;
+
+ pt = (vm_offset_t) vtopte(va);
+ vm_page_unhold( pmap_pte_vm_page(pmap, pt));
+}
+
+/* [ macro again?, should I force kstack into user map here? -wfj ] */
+void
+pmap_activate(pmap, pcbp)
+ register pmap_t pmap;
+ struct pcb *pcbp;
+{
+ PMAP_ACTIVATE(pmap, pcbp);
+}
+
+/*
+ * Bootstrap the system enough to run with virtual memory.
+ * Map the kernel's code and data, and allocate the system page table.
+ *
+ * On the I386 this is called after mapping has already been enabled
+ * and just syncs the pmap module with what has already been done.
+ * [We can't call it easily with mapping off since the kernel is not
+ * mapped with PA == VA, hence we would have to relocate every address
+ * from the linked base (virtual) address "KERNBASE" to the actual
+ * (physical) address starting relative to 0]
+ */
+
+#define DMAPAGES 8
+void
+pmap_bootstrap(firstaddr, loadaddr)
+ vm_offset_t firstaddr;
+ vm_offset_t loadaddr;
+{
+#if BSDVM_COMPAT
+ vm_offset_t va;
+ pt_entry_t *pte;
+#endif
+ extern int IdlePTD;
+
+ avail_start = firstaddr + DMAPAGES*NBPG;
+
+ virtual_avail = (vm_offset_t) KERNBASE + avail_start;
+ virtual_end = VM_MAX_KERNEL_ADDRESS;
+ i386pagesperpage = PAGE_SIZE / NBPG;
+
+ /*
+ * Initialize protection array.
+ */
+ i386_protection_init();
+
+ /*
+ * The kernel's pmap is statically allocated so we don't
+ * have to use pmap_create, which is unlikely to work
+ * correctly at this part of the boot sequence.
+ */
+ kernel_pmap = &kernel_pmap_store;
+
+ kernel_pmap->pm_pdir = (pd_entry_t *)(KERNBASE + IdlePTD);
+
+ simple_lock_init(&kernel_pmap->pm_lock);
+ kernel_pmap->pm_count = 1;
+
+#if BSDVM_COMPAT
+ /*
+ * Allocate all the submaps we need
+ */
+#define SYSMAP(c, p, v, n) \
+ v = (c)va; va += ((n)*NBPG); p = pte; pte += (n);
+
+ va = virtual_avail;
+ pte = pmap_pte(kernel_pmap, va);
+
+ SYSMAP(caddr_t ,CMAP1 ,CADDR1 ,1 )
+ SYSMAP(caddr_t ,CMAP2 ,CADDR2 ,1 )
+ SYSMAP(caddr_t ,ptmmap ,ptvmmap ,1 )
+ SYSMAP(struct msgbuf * ,msgbufmap ,msgbufp ,1 )
+ virtual_avail = va;
+#endif
+ /*
+ * reserve special hunk of memory for use by bus dma as a bounce
+ * buffer (contiguous virtual *and* physical memory). for now,
+ * assume vm does not use memory beneath hole, and we know that
+ * the bootstrap uses top 32k of base memory. -wfj
+ */
+ {
+ extern vm_offset_t isaphysmem;
+ isaphysmem = va;
+
+ virtual_avail = pmap_map(va, firstaddr,
+ firstaddr + DMAPAGES*NBPG, VM_PROT_ALL);
+ }
+
+ *(int *)PTD = 0;
+ tlbflush();
+
+}
+
+/*
+ * Initialize the pmap module.
+ * Called by vm_init, to initialize any structures that the pmap
+ * system needs to map virtual memory.
+ * pmap_init has been enhanced to support in a fairly consistant
+ * way, discontiguous physical memory.
+ */
+void
+pmap_init(phys_start, phys_end)
+ vm_offset_t phys_start, phys_end;
+{
+ vm_offset_t addr, addr2;
+ vm_size_t npg, s;
+ int rv;
+ int i;
+ extern int KPTphys;
+ extern int IdlePTD;
+
+ /*
+ * Now that kernel map has been allocated, we can mark as
+ * unavailable regions which we have mapped in locore.
+ */
+ addr = atdevbase;
+ (void) vm_map_find(kernel_map, NULL, (vm_offset_t) 0,
+ &addr, (0x100000-0xa0000), FALSE);
+
+ addr = (vm_offset_t) KERNBASE + IdlePTD;
+ vm_object_reference(kernel_object);
+ (void) vm_map_find(kernel_map, kernel_object, addr,
+ &addr, (4 + NKPT) * NBPG, FALSE);
+
+
+ /*
+ * calculate the number of pv_entries needed
+ */
+ vm_first_phys = phys_avail[0];
+ for (i = 0; phys_avail[i + 1]; i += 2) ;
+ npg = (phys_avail[(i - 2) + 1] - vm_first_phys) / NBPG;
+
+ /*
+ * Allocate memory for random pmap data structures. Includes the
+ * pv_head_table.
+ */
+ s = (vm_size_t) (sizeof(struct pv_entry) * npg);
+ s = i386_round_page(s);
+ addr = (vm_offset_t) kmem_alloc(kernel_map, s);
+ pv_table = (pv_entry_t) addr;
+
+ /*
+ * init the pv free list
+ */
+ init_pv_entries(npg);
+ /*
+ * Now it is safe to enable pv_table recording.
+ */
+ pmap_initialized = TRUE;
+}
+
+/*
+ * Used to map a range of physical addresses into kernel
+ * virtual address space.
+ *
+ * For now, VM is already on, we only need to map the
+ * specified memory.
+ */
+vm_offset_t
+pmap_map(virt, start, end, prot)
+ vm_offset_t virt;
+ vm_offset_t start;
+ vm_offset_t end;
+ int prot;
+{
+ while (start < end) {
+ pmap_enter(kernel_pmap, virt, start, prot, FALSE);
+ virt += PAGE_SIZE;
+ start += PAGE_SIZE;
+ }
+ return(virt);
+}
+
+/*
+ * Create and return a physical map.
+ *
+ * If the size specified for the map
+ * is zero, the map is an actual physical
+ * map, and may be referenced by the
+ * hardware.
+ *
+ * If the size specified is non-zero,
+ * the map will be used in software only, and
+ * is bounded by that size.
+ *
+ * [ just allocate a ptd and mark it uninitialize -- should we track
+ * with a table which process has which ptd? -wfj ]
+ */
+
+pmap_t
+pmap_create(size)
+ vm_size_t size;
+{
+ register pmap_t pmap;
+
+ /*
+ * Software use map does not need a pmap
+ */
+ if (size)
+ return(NULL);
+
+ pmap = (pmap_t) malloc(sizeof *pmap, M_VMPMAP, M_WAITOK);
+ bzero(pmap, sizeof(*pmap));
+ pmap_pinit(pmap);
+ return (pmap);
+}
+
+
+struct pmaplist {
+ struct pmaplist *next;
+};
+
+static inline void *
+vm_get_pmap()
+{
+ struct pmaplist *rtval;
+
+ rtval = (struct pmaplist *)kmem_alloc(kernel_map, ctob(1));
+ bzero(rtval, ctob(1));
+ return rtval;
+}
+
+static inline void
+vm_put_pmap(up)
+ struct pmaplist *up;
+{
+ kmem_free(kernel_map, (vm_offset_t)up, ctob(1));
+}
+
+/*
+ * Initialize a preallocated and zeroed pmap structure,
+ * such as one in a vmspace structure.
+ */
+void
+pmap_pinit(pmap)
+ register struct pmap *pmap;
+{
+ /*
+ * No need to allocate page table space yet but we do need a
+ * valid page directory table.
+ */
+ pmap->pm_pdir = (pd_entry_t *) vm_get_pmap();
+
+ /* wire in kernel global address entries */
+ bcopy(PTD+KPTDI, pmap->pm_pdir+KPTDI, NKPT*PTESIZE);
+
+ /* install self-referential address mapping entry */
+ *(int *)(pmap->pm_pdir+PTDPTDI) =
+ ((int)pmap_kextract((vm_offset_t)pmap->pm_pdir)) | PG_V | PG_KW;
+
+ pmap->pm_count = 1;
+ simple_lock_init(&pmap->pm_lock);
+}
+
+/*
+ * Retire the given physical map from service.
+ * Should only be called if the map contains
+ * no valid mappings.
+ */
+void
+pmap_destroy(pmap)
+ register pmap_t pmap;
+{
+ int count;
+
+ if (pmap == NULL)
+ return;
+
+ simple_lock(&pmap->pm_lock);
+ count = --pmap->pm_count;
+ simple_unlock(&pmap->pm_lock);
+ if (count == 0) {
+ pmap_release(pmap);
+ free((caddr_t)pmap, M_VMPMAP);
+ }
+}
+
+/*
+ * Release any resources held by the given physical map.
+ * Called when a pmap initialized by pmap_pinit is being released.
+ * Should only be called if the map contains no valid mappings.
+ */
+void
+pmap_release(pmap)
+ register struct pmap *pmap;
+{
+ vm_put_pmap((struct pmaplist *) pmap->pm_pdir);
+}
+
+/*
+ * Add a reference to the specified pmap.
+ */
+void
+pmap_reference(pmap)
+ pmap_t pmap;
+{
+ if (pmap != NULL) {
+ simple_lock(&pmap->pm_lock);
+ pmap->pm_count++;
+ simple_unlock(&pmap->pm_lock);
+ }
+}
+
+#define PV_FREELIST_MIN ((NBPG / sizeof (struct pv_entry)) / 2)
+
+/*
+ * Data for the pv entry allocation mechanism
+ */
+int pv_freelistcnt;
+pv_entry_t pv_freelist;
+vm_offset_t pvva;
+int npvvapg;
+
+/*
+ * free the pv_entry back to the free list
+ */
+inline static void
+free_pv_entry(pv)
+ pv_entry_t pv;
+{
+ if (!pv) return;
+ ++pv_freelistcnt;
+ pv->pv_next = pv_freelist;
+ pv_freelist = pv;
+}
+
+/*
+ * get a new pv_entry, allocating a block from the system
+ * when needed.
+ * the memory allocation is performed bypassing the malloc code
+ * because of the possibility of allocations at interrupt time.
+ */
+static inline pv_entry_t
+get_pv_entry()
+{
+ pv_entry_t tmp;
+
+ /*
+ * get more pv_entry pages if needed
+ */
+ while (pv_freelistcnt < PV_FREELIST_MIN || pv_freelist == 0) {
+ pmap_alloc_pv_entry();
+ }
+
+ /*
+ * get a pv_entry off of the free list
+ */
+ --pv_freelistcnt;
+ tmp = pv_freelist;
+ pv_freelist = tmp->pv_next;
+ tmp->pv_pmap = 0;
+ tmp->pv_va = 0;
+ tmp->pv_next = 0;
+ return tmp;
+}
+
+/*
+ * this *strange* allocation routine *statistically* eliminates the
+ * *possibility* of a malloc failure (*FATAL*) for a pv_entry_t data structure.
+ * also -- this code is MUCH MUCH faster than the malloc equiv...
+ */
+void
+pmap_alloc_pv_entry()
+{
+ /*
+ * do we have any pre-allocated map-pages left?
+ */
+ if (npvvapg) {
+ vm_page_t m;
+ /*
+ * we do this to keep recursion away
+ */
+ pv_freelistcnt += PV_FREELIST_MIN;
+ /*
+ * allocate a physical page out of the vm system
+ */
+ if (m = vm_page_alloc(kernel_object, pvva-vm_map_min(kernel_map))) {
+ int newentries;
+ int i;
+ pv_entry_t entry;
+ newentries = (NBPG/sizeof (struct pv_entry));
+ /*
+ * wire the page
+ */
+ vm_page_wire(m);
+ m->flags &= ~PG_BUSY;
+ /*
+ * let the kernel see it
+ */
+ pmap_enter(vm_map_pmap(kernel_map), pvva,
+ VM_PAGE_TO_PHYS(m), VM_PROT_DEFAULT,1);
+
+ entry = (pv_entry_t) pvva;
+ /*
+ * update the allocation pointers
+ */
+ pvva += NBPG;
+ --npvvapg;
+
+ /*
+ * free the entries into the free list
+ */
+ for (i = 0; i < newentries; i++) {
+ free_pv_entry(entry);
+ entry++;
+ }
+ }
+ pv_freelistcnt -= PV_FREELIST_MIN;
+ }
+ if (!pv_freelist)
+ panic("get_pv_entry: cannot get a pv_entry_t");
+}
+
+
+
+/*
+ * init the pv_entry allocation system
+ */
+#define PVSPERPAGE 64
+void
+init_pv_entries(npg)
+ int npg;
+{
+ /*
+ * allocate enough kvm space for PVSPERPAGE entries per page (lots)
+ * kvm space is fairly cheap, be generous!!! (the system can panic
+ * if this is too small.)
+ */
+ npvvapg = ((npg*PVSPERPAGE) * sizeof(struct pv_entry) + NBPG - 1)/NBPG;
+ pvva = kmem_alloc_pageable(kernel_map, npvvapg * NBPG);
+ /*
+ * get the first batch of entries
+ */
+ free_pv_entry(get_pv_entry());
+}
+
+static pt_entry_t *
+get_pt_entry(pmap)
+ pmap_t pmap;
+{
+ pt_entry_t *ptp;
+ vm_offset_t frame = (int) pmap->pm_pdir[PTDPTDI] & PG_FRAME;
+ /* are we current address space or kernel? */
+ if (pmap == kernel_pmap || frame == ((int) PTDpde & PG_FRAME)) {
+ ptp=PTmap;
+ /* otherwise, we are alternate address space */
+ } else {
+ if ( frame != ((int) APTDpde & PG_FRAME)) {
+ APTDpde = pmap->pm_pdir[PTDPTDI];
+ tlbflush();
+ }
+ ptp=APTmap;
+ }
+ return ptp;
+}
+
+/*
+ * If it is the first entry on the list, it is actually
+ * in the header and we must copy the following entry up
+ * to the header. Otherwise we must search the list for
+ * the entry. In either case we free the now unused entry.
+ */
+void
+pmap_remove_entry(pmap, pv, va)
+ struct pmap *pmap;
+ pv_entry_t pv;
+ vm_offset_t va;
+{
+ pv_entry_t npv;
+ int wired;
+ int s;
+ s = splimp();
+ if (pmap == pv->pv_pmap && va == pv->pv_va) {
+ npv = pv->pv_next;
+ if (npv) {
+ *pv = *npv;
+ free_pv_entry(npv);
+ } else {
+ pv->pv_pmap = NULL;
+ }
+ } else {
+ for (npv = pv->pv_next; npv; npv = npv->pv_next) {
+ if (pmap == npv->pv_pmap && va == npv->pv_va) {
+ break;
+ }
+ pv = npv;
+ }
+ if (npv) {
+ pv->pv_next = npv->pv_next;
+ free_pv_entry(npv);
+ }
+ }
+ splx(s);
+}
+
+/*
+ * Remove the given range of addresses from the specified map.
+ *
+ * It is assumed that the start and end are properly
+ * rounded to the page size.
+ */
+void
+pmap_remove(pmap, sva, eva)
+ struct pmap *pmap;
+ register vm_offset_t sva;
+ register vm_offset_t eva;
+{
+ register pt_entry_t *ptp,*ptq;
+ vm_offset_t pa;
+ register pv_entry_t pv;
+ vm_offset_t va;
+ vm_page_t m;
+ pt_entry_t oldpte;
+
+ if (pmap == NULL)
+ return;
+
+ ptp = get_pt_entry(pmap);
+
+/*
+ * special handling of removing one page. a very
+ * common operation and easy to short circuit some
+ * code.
+ */
+ if( (sva + NBPG) == eva) {
+
+ if( *pmap_pde( pmap, sva) == 0)
+ return;
+
+ ptq = ptp + i386_btop(sva);
+
+ if( !*ptq)
+ return;
+ /*
+ * Update statistics
+ */
+ if (pmap_pte_w(ptq))
+ pmap->pm_stats.wired_count--;
+ pmap->pm_stats.resident_count--;
+
+ pa = pmap_pte_pa(ptq);
+ oldpte = *ptq;
+ *ptq = 0;
+
+ if (pmap_is_managed(pa)) {
+ if ((((int) oldpte & PG_M) && (sva < USRSTACK || sva > UPT_MAX_ADDRESS))
+ || (sva >= USRSTACK && sva < USRSTACK+(UPAGES*NBPG))) {
+ if (sva < clean_sva || sva >= clean_eva) {
+ m = PHYS_TO_VM_PAGE(pa);
+ m->flags &= ~PG_CLEAN;
+ }
+ }
+
+ pv = pa_to_pvh(pa);
+ pmap_remove_entry(pmap, pv, sva);
+ pmap_unuse_pt(pmap, sva);
+ }
+ tlbflush();
+ return;
+ }
+
+ sva = i386_btop(sva);
+ eva = i386_btop(eva);
+
+ while (sva < eva) {
+ /*
+ * Weed out invalid mappings.
+ * Note: we assume that the page directory table is
+ * always allocated, and in kernel virtual.
+ */
+
+ if ( *pmap_pde(pmap, i386_ptob(sva)) == 0 ) {
+ /* We can race ahead here, straight to next pde.. */
+ nextpde:
+ sva = ((sva + NPTEPG) & ~(NPTEPG - 1));
+ continue;
+ }
+
+ ptq = ptp + sva;
+
+ /*
+ * search for page table entries, use string operations
+ * that are much faster than
+ * explicitly scanning when page tables are not fully
+ * populated.
+ */
+ if ( *ptq == 0) {
+ vm_offset_t pdnxt = ((sva + NPTEPG) & ~(NPTEPG - 1));
+ vm_offset_t nscan = pdnxt - sva;
+ int found = 0;
+
+ if ((nscan + sva) > eva)
+ nscan = eva - sva;
+
+ asm("xorl %%eax,%%eax;cld;repe;scasl;jz 1f;incl %%eax;1:;"
+ :"=D"(ptq),"=a"(found)
+ :"c"(nscan),"0"(ptq)
+ :"cx");
+
+ if( !found) {
+ sva = pdnxt;
+ continue;
+ }
+ ptq -= 1;
+
+ sva = ptq - ptp;
+ }
+
+ /*
+ * Update statistics
+ */
+ oldpte = *ptq;
+ if (((int)oldpte) & PG_W)
+ pmap->pm_stats.wired_count--;
+ pmap->pm_stats.resident_count--;
+
+ /*
+ * Invalidate the PTEs.
+ * XXX: should cluster them up and invalidate as many
+ * as possible at once.
+ */
+ *ptq = 0;
+
+ va = i386_ptob(sva);
+
+ /*
+ * Remove from the PV table (raise IPL since we
+ * may be called at interrupt time).
+ */
+ pa = ((int)oldpte) & PG_FRAME;
+ if (!pmap_is_managed(pa)) {
+ ++sva;
+ continue;
+ }
+
+ if ((((int) oldpte & PG_M) && (va < USRSTACK || va > UPT_MAX_ADDRESS))
+ || (va >= USRSTACK && va < USRSTACK+(UPAGES*NBPG))) {
+ if (va < clean_sva || va >= clean_eva ) {
+ m = PHYS_TO_VM_PAGE(pa);
+ m->flags &= ~PG_CLEAN;
+ }
+ }
+
+ pv = pa_to_pvh(pa);
+ pmap_remove_entry(pmap, pv, va);
+ pmap_unuse_pt(pmap, va);
+ ++sva;
+ }
+ tlbflush();
+}
+
+/*
+ * Routine: pmap_remove_all
+ * Function:
+ * Removes this physical page from
+ * all physical maps in which it resides.
+ * Reflects back modify bits to the pager.
+ *
+ * Notes:
+ * Original versions of this routine were very
+ * inefficient because they iteratively called
+ * pmap_remove (slow...)
+ */
+void
+pmap_remove_all(pa)
+ vm_offset_t pa;
+{
+ register pv_entry_t pv, npv;
+ register pt_entry_t *pte, *ptp;
+ vm_offset_t va;
+ struct pmap *pmap;
+ struct map *map;
+ vm_page_t m;
+ int s;
+
+ /*
+ * Not one of ours
+ */
+ if (!pmap_is_managed(pa))
+ return;
+
+ pa = i386_trunc_page(pa);
+ pv = pa_to_pvh(pa);
+ m = PHYS_TO_VM_PAGE(pa);
+
+ s = splimp();
+ while (pv->pv_pmap != NULL) {
+ pmap = pv->pv_pmap;
+ ptp = get_pt_entry(pmap);
+ va = i386_btop(pv->pv_va);
+ pte = ptp + va;
+ if (pmap_pte_w(pte))
+ pmap->pm_stats.wired_count--;
+ if ( *pte)
+ pmap->pm_stats.resident_count--;
+
+ /*
+ * update the vm_page_t clean bit
+ */
+ if ( (m->flags & PG_CLEAN) &&
+ ((((int) *pte) & PG_M) && (pv->pv_va < USRSTACK || pv->pv_va > UPT_MAX_ADDRESS))
+ || (pv->pv_va >= USRSTACK && pv->pv_va < USRSTACK+(UPAGES*NBPG))) {
+ if (pv->pv_va < clean_sva || pv->pv_va >= clean_eva) {
+ m->flags &= ~PG_CLEAN;
+ }
+ }
+
+ *pte = 0;
+ pmap_unuse_pt(pmap, pv->pv_va);
+
+ npv = pv->pv_next;
+ if (npv) {
+ *pv = *npv;
+ free_pv_entry(npv);
+ } else {
+ pv->pv_pmap = NULL;
+ }
+ }
+ splx(s);
+ tlbflush();
+}
+
+
+/*
+ * Set the physical protection on the
+ * specified range of this map as requested.
+ */
+void
+pmap_protect(pmap, sva, eva, prot)
+ register pmap_t pmap;
+ vm_offset_t sva, eva;
+ vm_prot_t prot;
+{
+ register pt_entry_t *pte;
+ register vm_offset_t va;
+ int i386prot;
+ register pt_entry_t *ptp;
+ int evap = i386_btop(eva);
+ int s;
+
+ if (pmap == NULL)
+ return;
+
+ if ((prot & VM_PROT_READ) == VM_PROT_NONE) {
+ pmap_remove(pmap, sva, eva);
+ return;
+ }
+ if (prot & VM_PROT_WRITE)
+ return;
+
+ ptp = get_pt_entry(pmap);
+
+ va = sva;
+ while (va < eva) {
+ int found=0;
+ int svap;
+ vm_offset_t nscan;
+ /*
+ * Page table page is not allocated.
+ * Skip it, we don't want to force allocation
+ * of unnecessary PTE pages just to set the protection.
+ */
+ if (! *pmap_pde(pmap, va)) {
+ /* XXX: avoid address wrap around */
+nextpde:
+ if (va >= i386_trunc_pdr((vm_offset_t)-1))
+ break;
+ va = i386_round_pdr(va + PAGE_SIZE);
+ continue;
+ }
+
+ pte = ptp + i386_btop(va);
+
+ if( *pte == 0) {
+ /*
+ * scan for a non-empty pte
+ */
+ svap = pte - ptp;
+ nscan = ((svap + NPTEPG) & ~(NPTEPG - 1)) - svap;
+
+ if (nscan + svap > evap)
+ nscan = evap - svap;
+
+ found = 0;
+ if (nscan)
+ asm("xorl %%eax,%%eax;cld;repe;scasl;jz 1f;incl %%eax;1:;"
+ :"=D"(pte),"=a"(found)
+ :"c"(nscan),"0"(pte):"cx");
+
+ if( !found)
+ goto nextpde;
+
+ pte -= 1;
+ svap = pte - ptp;
+
+ va = i386_ptob(svap);
+ }
+
+ i386prot = pte_prot(pmap, prot);
+ if (va < UPT_MAX_ADDRESS) {
+ i386prot |= PG_u;
+ if( va >= UPT_MIN_ADDRESS)
+ i386prot |= PG_RW;
+ }
+ pmap_pte_set_prot(pte, i386prot);
+ va += PAGE_SIZE;
+ }
+ tlbflush();
+}
+
+/*
+ * Insert the given physical page (p) at
+ * the specified virtual address (v) in the
+ * target physical map with the protection requested.
+ *
+ * If specified, the page will be wired down, meaning
+ * that the related pte can not be reclaimed.
+ *
+ * NB: This is the only routine which MAY NOT lazy-evaluate
+ * or lose information. That is, this routine must actually
+ * insert this page into the given map NOW.
+ */
+void
+pmap_enter(pmap, va, pa, prot, wired)
+ register pmap_t pmap;
+ vm_offset_t va;
+ register vm_offset_t pa;
+ vm_prot_t prot;
+ boolean_t wired;
+{
+ register pt_entry_t *pte;
+ register pt_entry_t npte;
+ vm_offset_t opa;
+ int cacheable=1;
+
+ if (pmap == NULL)
+ return;
+
+ va = i386_trunc_page(va);
+ pa = i386_trunc_page(pa);
+ if (va > VM_MAX_KERNEL_ADDRESS)panic("pmap_enter: toobig");
+
+ /*
+ * Page Directory table entry not valid, we need a new PT page
+ */
+ if ( *pmap_pde(pmap, va) == 0) {
+ pg("ptdi %x, va %x", pmap->pm_pdir[PTDPTDI], va);
+ }
+
+ pte = pmap_pte(pmap, va);
+ opa = pmap_pte_pa(pte);
+
+ /*
+ * Mapping has not changed, must be protection or wiring change.
+ */
+ if (opa == pa) {
+ /*
+ * Wiring change, just update stats.
+ * We don't worry about wiring PT pages as they remain
+ * resident as long as there are valid mappings in them.
+ * Hence, if a user page is wired, the PT page will be also.
+ */
+ if (wired && !pmap_pte_w(pte) || !wired && pmap_pte_w(pte)) {
+ if (wired)
+ pmap->pm_stats.wired_count++;
+ else
+ pmap->pm_stats.wired_count--;
+ }
+ goto validate;
+ }
+
+ /*
+ * Mapping has changed, invalidate old range and fall through to
+ * handle validating new mapping.
+ */
+ if (opa) {
+ pmap_remove(pmap, va, va + PAGE_SIZE);
+ }
+
+ /*
+ * Enter on the PV list if part of our managed memory
+ * Note that we raise IPL while manipulating pv_table
+ * since pmap_enter can be called at interrupt time.
+ */
+ if (pmap_is_managed(pa)) {
+ register pv_entry_t pv, npv;
+ int s;
+
+ pv = pa_to_pvh(pa);
+ s = splimp();
+ /*
+ * No entries yet, use header as the first entry
+ */
+ if (pv->pv_pmap == NULL) {
+ pv->pv_va = va;
+ pv->pv_pmap = pmap;
+ pv->pv_next = NULL;
+ }
+ /*
+ * There is at least one other VA mapping this page.
+ * Place this entry after the header.
+ */
+ else {
+ npv = get_pv_entry();
+ npv->pv_va = va;
+ npv->pv_pmap = pmap;
+ npv->pv_next = pv->pv_next;
+ pv->pv_next = npv;
+ }
+ splx(s);
+ cacheable = 1;
+ } else {
+ cacheable = 0;
+ }
+
+ pmap_use_pt(pmap, va);
+
+ /*
+ * Increment counters
+ */
+ pmap->pm_stats.resident_count++;
+ if (wired)
+ pmap->pm_stats.wired_count++;
+
+validate:
+ /*
+ * Now validate mapping with desired protection/wiring.
+ */
+ npte = (pt_entry_t) ( (int) (pa | pte_prot(pmap, prot) | PG_V));
+ /*
+ * for correctness:
+ */
+ if( !cacheable)
+ (int) npte |= PG_N;
+
+ /*
+ * When forking (copy-on-write, etc):
+ * A process will turn off write permissions for any of its writable
+ * pages. If the data (object) is only referred to by one process, the
+ * processes map is modified directly as opposed to using the
+ * object manipulation routine. When using pmap_protect, the
+ * modified bits are not kept in the vm_page_t data structure.
+ * Therefore, when using pmap_enter in vm_fault to bring back
+ * writability of a page, there has been no memory of the
+ * modified or referenced bits except at the pte level.
+ * this clause supports the carryover of the modified and
+ * used (referenced) bits.
+ */
+ if (pa == opa)
+ (int) npte |= (int) *pte & (PG_M|PG_U);
+
+
+ if (wired)
+ (int) npte |= PG_W;
+ if (va < UPT_MIN_ADDRESS)
+ (int) npte |= PG_u;
+ else if (va < UPT_MAX_ADDRESS)
+ (int) npte |= PG_u | PG_RW | PG_NC_PWT;
+
+/*
+ printf("mapping: pa: %x, to va: %x, with pte: %x\n", pa, va, npte);
+*/
+
+ if( *pte != npte) {
+ *pte = npte;
+ tlbflush();
+ }
+}
+
+/*
+ * add a wired page to the kva
+ */
+void
+pmap_kenter(va, pa)
+ vm_offset_t va;
+ register vm_offset_t pa;
+{
+ register pt_entry_t *pte;
+ register pv_entry_t pv, npv;
+ vm_offset_t opa;
+ int s;
+
+ /*
+ * Enter on the PV list if part of our managed memory
+ * Note that we raise IPL while manipulating pv_table
+ * since pmap_enter can be called at interrupt time.
+ */
+
+ pte = vtopte(va);
+
+ opa = pmap_pte_pa(pte);
+ /*
+ * Mapping has not changed, must be protection or wiring change.
+ */
+ if (opa == pa) {
+ /*
+ * Wiring change, just update stats.
+ * We don't worry about wiring PT pages as they remain
+ * resident as long as there are valid mappings in them.
+ * Hence, if a user page is wired, the PT page will be also.
+ */
+ if (!pmap_pte_w(pte)) {
+ kernel_pmap->pm_stats.wired_count++;
+ }
+ goto validate;
+ }
+
+ if (opa) {
+ pmap_remove(kernel_pmap, va, va + PAGE_SIZE);
+ }
+
+ pv = pa_to_pvh(pa);
+ s = splimp();
+ /*
+ * No entries yet, use header as the first entry
+ */
+ if (pv->pv_pmap == NULL) {
+ pv->pv_va = va;
+ pv->pv_pmap = kernel_pmap;
+ pv->pv_next = NULL;
+ }
+ /*
+ * There is at least one other VA mapping this page.
+ * Place this entry after the header.
+ */
+ else {
+ npv = get_pv_entry();
+ npv->pv_va = va;
+ npv->pv_pmap = kernel_pmap;
+ npv->pv_next = pv->pv_next;
+ pv->pv_next = npv;
+ }
+ splx(s);
+
+ /*
+ * Increment counters
+ */
+ kernel_pmap->pm_stats.resident_count++;
+
+validate:
+
+ /*
+ * Now validate mapping with desired protection/wiring.
+ */
+ *pte = (pt_entry_t) ( (int) (pa | PG_RW | PG_V | PG_W));
+}
+
+/*
+ * this code makes some *MAJOR* assumptions:
+ * 1. Current pmap & pmap exists.
+ * 2. Not wired.
+ * 3. Read access.
+ * 4. No page table pages.
+ * 5. Tlbflush is deferred to calling procedure.
+ * 6. Page IS managed.
+ * but is *MUCH* faster than pmap_enter...
+ */
+
+static inline void
+pmap_enter_quick(pmap, va, pa)
+ register pmap_t pmap;
+ vm_offset_t va;
+ register vm_offset_t pa;
+{
+ register pt_entry_t *pte;
+ register pv_entry_t pv, npv;
+ int s;
+
+ /*
+ * Enter on the PV list if part of our managed memory
+ * Note that we raise IPL while manipulating pv_table
+ * since pmap_enter can be called at interrupt time.
+ */
+
+ pte = vtopte(va);
+ if (pmap_pte_pa(pte)) {
+ pmap_remove(pmap, va, va + PAGE_SIZE);
+ }
+
+ pv = pa_to_pvh(pa);
+ s = splimp();
+ /*
+ * No entries yet, use header as the first entry
+ */
+ if (pv->pv_pmap == NULL) {
+ pv->pv_va = va;
+ pv->pv_pmap = pmap;
+ pv->pv_next = NULL;
+ }
+ /*
+ * There is at least one other VA mapping this page.
+ * Place this entry after the header.
+ */
+ else {
+ npv = get_pv_entry();
+ npv->pv_va = va;
+ npv->pv_pmap = pmap;
+ npv->pv_next = pv->pv_next;
+ pv->pv_next = npv;
+ }
+ splx(s);
+
+ pmap_use_pt(pmap, va);
+
+ /*
+ * Increment counters
+ */
+ pmap->pm_stats.resident_count++;
+
+validate:
+
+ /*
+ * Now validate mapping with desired protection/wiring.
+ */
+ *pte = (pt_entry_t) ( (int) (pa | PG_V | PG_u));
+}
+
+/*
+ * pmap_object_init_pt preloads the ptes for a given object
+ * into the specified pmap. This eliminates the blast of soft
+ * faults on process startup and immediately after an mmap.
+ */
+void
+pmap_object_init_pt(pmap, addr, object, offset, size)
+ pmap_t pmap;
+ vm_offset_t addr;
+ vm_object_t object;
+ vm_offset_t offset;
+ vm_offset_t size;
+{
+
+ vm_offset_t tmpoff;
+ vm_page_t p;
+ int s;
+ vm_offset_t v, lastv=0;
+ pt_entry_t pte;
+ extern vm_map_t kernel_map;
+ vm_offset_t objbytes;
+
+ if (!pmap)
+ return;
+
+ /*
+ * if we are processing a major portion of the object, then
+ * scan the entire thing.
+ */
+ if( size > object->size / 2) {
+ objbytes = size;
+ p = object->memq.tqh_first;
+ while ((p != NULL) && (objbytes != 0)) {
+ tmpoff = p->offset;
+ if( tmpoff < offset) {
+ p = p->listq.tqe_next;
+ continue;
+ }
+ tmpoff -= offset;
+ if( tmpoff >= size) {
+ p = p->listq.tqe_next;
+ continue;
+ }
+
+ if ((p->flags & (PG_BUSY|PG_FICTITIOUS)) == 0 ) {
+ vm_page_hold(p);
+ v = i386_trunc_page(((vm_offset_t)vtopte( addr+tmpoff)));
+ /* a fault might occur here */
+ *(volatile char *)v += 0;
+ vm_page_unhold(p);
+ pmap_enter_quick(pmap, addr+tmpoff, VM_PAGE_TO_PHYS(p));
+ }
+ p = p->listq.tqe_next;
+ objbytes -= NBPG;
+ }
+ } else {
+ /*
+ * else lookup the pages one-by-one.
+ */
+ for(tmpoff = 0; tmpoff < size; tmpoff += NBPG) {
+ if( p = vm_page_lookup(object, tmpoff + offset)) {
+ if( (p->flags & (PG_BUSY|PG_FICTITIOUS)) == 0) {
+ vm_page_hold(p);
+ v = i386_trunc_page(((vm_offset_t)vtopte( addr+tmpoff)));
+ /* a fault might occur here */
+ *(volatile char *)v += 0;
+ vm_page_unhold(p);
+ pmap_enter_quick(pmap, addr+tmpoff, VM_PAGE_TO_PHYS(p));
+ }
+ }
+ }
+ }
+
+ tlbflush();
+}
+
+/*
+ * Routine: pmap_change_wiring
+ * Function: Change the wiring attribute for a map/virtual-address
+ * pair.
+ * In/out conditions:
+ * The mapping must already exist in the pmap.
+ */
+void
+pmap_change_wiring(pmap, va, wired)
+ register pmap_t pmap;
+ vm_offset_t va;
+ boolean_t wired;
+{
+ register pt_entry_t *pte;
+
+ if (pmap == NULL)
+ return;
+
+ pte = pmap_pte(pmap, va);
+ if (wired && !pmap_pte_w(pte) || !wired && pmap_pte_w(pte)) {
+ if (wired)
+ pmap->pm_stats.wired_count++;
+ else
+ pmap->pm_stats.wired_count--;
+ }
+ /*
+ * Wiring is not a hardware characteristic so there is no need
+ * to invalidate TLB.
+ */
+ pmap_pte_set_w(pte, wired);
+ /*
+ * When unwiring, set the modified bit in the pte -- could have
+ * been changed by the kernel
+ */
+ if (!wired)
+ (int) *pte |= PG_M;
+}
+
+
+
+/*
+ * Copy the range specified by src_addr/len
+ * from the source map to the range dst_addr/len
+ * in the destination map.
+ *
+ * This routine is only advisory and need not do anything.
+ */
+void
+pmap_copy(dst_pmap, src_pmap, dst_addr, len, src_addr)
+ pmap_t dst_pmap, src_pmap;
+ vm_offset_t dst_addr;
+ vm_size_t len;
+ vm_offset_t src_addr;
+{
+}
+/*
+ * Require that all active physical maps contain no
+ * incorrect entries NOW. [This update includes
+ * forcing updates of any address map caching.]
+ *
+ * Generally used to insure that a thread about
+ * to run will see a semantically correct world.
+ */
+void
+pmap_update()
+{
+ tlbflush();
+}
+
+/*
+ * Routine: pmap_kernel
+ * Function:
+ * Returns the physical map handle for the kernel.
+ */
+pmap_t
+pmap_kernel()
+{
+ return (kernel_pmap);
+}
+
+/*
+ * pmap_zero_page zeros the specified (machine independent)
+ * page by mapping the page into virtual memory and using
+ * bzero to clear its contents, one machine dependent page
+ * at a time.
+ */
+void
+pmap_zero_page(phys)
+ vm_offset_t phys;
+{
+ *(int *)CMAP2 = PG_V | PG_KW | i386_trunc_page(phys);
+ tlbflush();
+ bzero(CADDR2,NBPG);
+}
+
+/*
+ * pmap_copy_page copies the specified (machine independent)
+ * page by mapping the page into virtual memory and using
+ * bcopy to copy the page, one machine dependent page at a
+ * time.
+ */
+void
+pmap_copy_page(src, dst)
+ vm_offset_t src;
+ vm_offset_t dst;
+{
+ *(int *)CMAP1 = PG_V | PG_KW | i386_trunc_page(src);
+ *(int *)CMAP2 = PG_V | PG_KW | i386_trunc_page(dst);
+ tlbflush();
+
+#if __GNUC__ > 1
+ memcpy(CADDR2, CADDR1, NBPG);
+#else
+ bcopy(CADDR1, CADDR2, NBPG);
+#endif
+}
+
+
+/*
+ * Routine: pmap_pageable
+ * Function:
+ * Make the specified pages (by pmap, offset)
+ * pageable (or not) as requested.
+ *
+ * A page which is not pageable may not take
+ * a fault; therefore, its page table entry
+ * must remain valid for the duration.
+ *
+ * This routine is merely advisory; pmap_enter
+ * will specify that these pages are to be wired
+ * down (or not) as appropriate.
+ */
+void
+pmap_pageable(pmap, sva, eva, pageable)
+ pmap_t pmap;
+ vm_offset_t sva, eva;
+ boolean_t pageable;
+{
+}
+
+/*
+ * this routine returns true if a physical page resides
+ * in the given pmap.
+ */
+boolean_t
+pmap_page_exists(pmap, pa)
+ pmap_t pmap;
+ vm_offset_t pa;
+{
+ register pv_entry_t pv;
+ int s;
+
+ if (!pmap_is_managed(pa))
+ return FALSE;
+
+ pv = pa_to_pvh(pa);
+ s = splimp();
+
+ /*
+ * Not found, check current mappings returning
+ * immediately if found.
+ */
+ if (pv->pv_pmap != NULL) {
+ for (; pv; pv = pv->pv_next) {
+ if (pv->pv_pmap == pmap) {
+ splx(s);
+ return TRUE;
+ }
+ }
+ }
+ splx(s);
+ return(FALSE);
+}
+
+/*
+ * pmap_testbit tests bits in pte's
+ * note that the testbit/changebit routines are inline,
+ * and a lot of things compile-time evaluate.
+ */
+static inline boolean_t
+pmap_testbit(pa, bit)
+ register vm_offset_t pa;
+ int bit;
+{
+ register pv_entry_t pv;
+ pt_entry_t *pte;
+ int s;
+
+ if (!pmap_is_managed(pa))
+ return FALSE;
+
+ pv = pa_to_pvh(pa);
+ s = splimp();
+
+ /*
+ * Not found, check current mappings returning
+ * immediately if found.
+ */
+ if (pv->pv_pmap != NULL) {
+ for (; pv; pv = pv->pv_next) {
+ /*
+ * if the bit being tested is the modified bit,
+ * then mark UPAGES as always modified, and
+ * ptes as never modified.
+ */
+ if (bit & PG_U ) {
+ if ((pv->pv_va >= clean_sva) && (pv->pv_va < clean_eva)) {
+ continue;
+ }
+ }
+ if (bit & PG_M ) {
+ if (pv->pv_va >= USRSTACK) {
+ if (pv->pv_va >= clean_sva && pv->pv_va < clean_eva) {
+ continue;
+ }
+ if (pv->pv_va < USRSTACK+(UPAGES*NBPG)) {
+ splx(s);
+ return TRUE;
+ }
+ else if (pv->pv_va < UPT_MAX_ADDRESS) {
+ splx(s);
+ return FALSE;
+ }
+ }
+ }
+ pte = pmap_pte(pv->pv_pmap, pv->pv_va);
+ if ((int) *pte & bit) {
+ splx(s);
+ return TRUE;
+ }
+ }
+ }
+ splx(s);
+ return(FALSE);
+}
+
+/*
+ * this routine is used to modify bits in ptes
+ */
+static inline void
+pmap_changebit(pa, bit, setem)
+ vm_offset_t pa;
+ int bit;
+ boolean_t setem;
+{
+ register pv_entry_t pv;
+ register pt_entry_t *pte, npte;
+ vm_offset_t va;
+ int s;
+
+ if (!pmap_is_managed(pa))
+ return;
+
+ pv = pa_to_pvh(pa);
+ s = splimp();
+
+ /*
+ * Loop over all current mappings setting/clearing as appropos
+ * If setting RO do we need to clear the VAC?
+ */
+ if (pv->pv_pmap != NULL) {
+ for (; pv; pv = pv->pv_next) {
+ va = pv->pv_va;
+
+ /*
+ * don't write protect pager mappings
+ */
+ if (!setem && (bit == PG_RW)) {
+ if (va >= clean_sva && va < clean_eva)
+ continue;
+ }
+
+ pte = pmap_pte(pv->pv_pmap, va);
+ if (setem)
+ (int) npte = (int) *pte | bit;
+ else
+ (int) npte = (int) *pte & ~bit;
+ *pte = npte;
+ }
+ }
+ splx(s);
+ tlbflush();
+}
+
+/*
+ * pmap_page_protect:
+ *
+ * Lower the permission for all mappings to a given page.
+ */
+void
+pmap_page_protect(phys, prot)
+ vm_offset_t phys;
+ vm_prot_t prot;
+{
+ if ((prot & VM_PROT_WRITE) == 0) {
+ if (prot & (VM_PROT_READ | VM_PROT_EXECUTE))
+ pmap_changebit(phys, PG_RW, FALSE);
+ else
+ pmap_remove_all(phys);
+ }
+}
+
+/*
+ * Clear the modify bits on the specified physical page.
+ */
+void
+pmap_clear_modify(pa)
+ vm_offset_t pa;
+{
+ pmap_changebit(pa, PG_M, FALSE);
+}
+
+/*
+ * pmap_clear_reference:
+ *
+ * Clear the reference bit on the specified physical page.
+ */
+void
+pmap_clear_reference(pa)
+ vm_offset_t pa;
+{
+ pmap_changebit(pa, PG_U, FALSE);
+}
+
+/*
+ * pmap_is_referenced:
+ *
+ * Return whether or not the specified physical page is referenced
+ * by any physical maps.
+ */
+
+boolean_t
+pmap_is_referenced(pa)
+ vm_offset_t pa;
+{
+ return(pmap_testbit(pa, PG_U));
+}
+
+/*
+ * pmap_is_modified:
+ *
+ * Return whether or not the specified physical page is modified
+ * by any physical maps.
+ */
+
+boolean_t
+pmap_is_modified(pa)
+ vm_offset_t pa;
+{
+ return(pmap_testbit(pa, PG_M));
+}
+
+/*
+ * Routine: pmap_copy_on_write
+ * Function:
+ * Remove write privileges from all
+ * physical maps for this physical page.
+ */
+void
+pmap_copy_on_write(pa)
+ vm_offset_t pa;
+{
+ pmap_changebit(pa, PG_RW, FALSE);
+}
+
+
+vm_offset_t
+pmap_phys_address(ppn)
+ int ppn;
+{
+ return(i386_ptob(ppn));
+}
+
+/*
+ * Miscellaneous support routines follow
+ */
+/*
+ * This really just builds a table for page write enable
+ * translation.
+ */
+
+void
+i386_protection_init()
+{
+ register int *kp, prot;
+
+ kp = protection_codes;
+ for (prot = 0; prot < 8; prot++) {
+ switch (prot) {
+ case VM_PROT_NONE | VM_PROT_NONE | VM_PROT_NONE:
+ case VM_PROT_READ | VM_PROT_NONE | VM_PROT_NONE:
+ case VM_PROT_READ | VM_PROT_NONE | VM_PROT_EXECUTE:
+ case VM_PROT_NONE | VM_PROT_NONE | VM_PROT_EXECUTE:
+ *kp++ = 0;
+ break;
+ case VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_NONE:
+ case VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_EXECUTE:
+ case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_NONE:
+ case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE:
+ *kp++ = PG_RW;
+ break;
+ }
+ }
+}
+
+#ifdef DEBUG
+void
+pmap_pvdump(pa)
+ vm_offset_t pa;
+{
+ register pv_entry_t pv;
+
+ printf("pa %x", pa);
+ for (pv = pa_to_pvh(pa); pv; pv = pv->pv_next) {
+ printf(" -> pmap %x, va %x, flags %x",
+ pv->pv_pmap, pv->pv_va, pv->pv_flags);
+ pads(pv->pv_pmap);
+ }
+ printf(" ");
+}
+
+/* print address space of pmap*/
+void
+pads(pm)
+ pmap_t pm;
+{
+ unsigned va, i, j;
+ pt_entry_t *ptep;
+
+ if (pm == kernel_pmap) return;
+ for (i = 0; i < 1024; i++)
+ if (pm->pm_pdir[i])
+ for (j = 0; j < 1024 ; j++) {
+ va = (i<<PD_SHIFT)+(j<<PG_SHIFT);
+ if (pm == kernel_pmap && va < KERNBASE)
+ continue;
+ if (pm != kernel_pmap && va > UPT_MAX_ADDRESS)
+ continue;
+ ptep = pmap_pte(pm, va);
+ if (pmap_pte_v(ptep))
+ printf("%x:%x ", va, *(int *)ptep);
+ } ;
+
+}
+#endif
diff --git a/sys/amd64/amd64/support.S b/sys/amd64/amd64/support.S
new file mode 100644
index 0000000..c4e37df
--- /dev/null
+++ b/sys/amd64/amd64/support.S
@@ -0,0 +1,1221 @@
+/*-
+ * Copyright (c) 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: support.s,v 1.10 1994/06/06 14:23:49 davidg Exp $
+ */
+
+#include "assym.s" /* system definitions */
+#include "errno.h" /* error return codes */
+#include "machine/asmacros.h" /* miscellaneous asm macros */
+#include "machine/cputypes.h" /* types of CPUs */
+
+#define KDSEL 0x10 /* kernel data selector */
+#define IDXSHIFT 10
+
+/*
+ * Support routines for GCC, general C-callable functions
+ */
+ENTRY(__udivsi3)
+ movl 4(%esp),%eax
+ xorl %edx,%edx
+ divl 8(%esp)
+ ret
+
+ENTRY(__divsi3)
+ movl 4(%esp),%eax
+ cltd
+ idivl 8(%esp)
+ ret
+
+ /*
+ * I/O bus instructions via C
+ */
+ENTRY(inb) /* val = inb(port) */
+ movl 4(%esp),%edx
+ subl %eax,%eax
+ inb %dx,%al
+ NOP
+ ret
+
+ENTRY(inw) /* val = inw(port) */
+ movl 4(%esp),%edx
+ subl %eax,%eax
+ inw %dx,%ax
+ NOP
+ ret
+
+ENTRY(insb) /* insb(port, addr, cnt) */
+ pushl %edi
+ movl 8(%esp),%edx
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ insb
+ NOP
+ movl %edi,%eax
+ popl %edi
+ ret
+
+ENTRY(insw) /* insw(port, addr, cnt) */
+ pushl %edi
+ movl 8(%esp),%edx
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ insw
+ NOP
+ movl %edi,%eax
+ popl %edi
+ ret
+
+ENTRY(insl) /* insl(port, addr, cnt) */
+ pushl %edi
+ movl 8(%esp),%edx
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ insl
+ NOP
+ movl %edi,%eax
+ popl %edi
+ ret
+
+ENTRY(rtcin) /* rtcin(val) */
+ movl 4(%esp),%eax
+ outb %al,$0x70
+ NOP
+ xorl %eax,%eax
+ inb $0x71,%al
+ NOP
+ ret
+
+ENTRY(outb) /* outb(port, val) */
+ movl 4(%esp),%edx
+ movl 8(%esp),%eax
+ outb %al,%dx
+ NOP
+ ret
+
+ENTRY(outw) /* outw(port, val) */
+ movl 4(%esp),%edx
+ movl 8(%esp),%eax
+ outw %ax,%dx
+ NOP
+ ret
+
+ENTRY(outsb) /* outsb(port, addr, cnt) */
+ pushl %esi
+ movl 8(%esp),%edx
+ movl 12(%esp),%esi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ outsb
+ NOP
+ movl %esi,%eax
+ popl %esi
+ ret
+
+ENTRY(outsw) /* outsw(port, addr, cnt) */
+ pushl %esi
+ movl 8(%esp),%edx
+ movl 12(%esp),%esi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ outsw
+ NOP
+ movl %esi,%eax
+ popl %esi
+ ret
+
+ENTRY(outsl) /* outsl(port, addr, cnt) */
+ pushl %esi
+ movl 8(%esp),%edx
+ movl 12(%esp),%esi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ outsl
+ NOP
+ movl %esi,%eax
+ popl %esi
+ ret
+
+/*
+ * bcopy family
+ */
+
+/*
+ * void bzero(void *base, u_int cnt)
+ * Special code for I486 because stosl uses lots
+ * of clocks. Makes little or no difference on DX2 type
+ * machines, but stosl is about 1/2 as fast as
+ * memory moves on a standard DX !!!!!
+ */
+ALTENTRY(blkclr)
+ENTRY(bzero)
+#if defined(I486_CPU)
+ cmpl $CPUCLASS_486,_cpu_class
+ jz 1f
+#endif
+
+ pushl %edi
+ movl 8(%esp),%edi
+ movl 12(%esp),%ecx
+ xorl %eax,%eax
+ shrl $2,%ecx
+ cld
+ rep
+ stosl
+ movl 12(%esp),%ecx
+ andl $3,%ecx
+ rep
+ stosb
+ popl %edi
+ ret
+
+#if defined(I486_CPU)
+ SUPERALIGN_TEXT
+1:
+ movl 4(%esp),%edx
+ movl 8(%esp),%ecx
+ xorl %eax,%eax
+/
+/ do 64 byte chunks first
+/
+/ XXX this is probably over-unrolled at least for DX2's
+/
+2:
+ cmpl $64,%ecx
+ jb 3f
+ movl %eax,(%edx)
+ movl %eax,4(%edx)
+ movl %eax,8(%edx)
+ movl %eax,12(%edx)
+ movl %eax,16(%edx)
+ movl %eax,20(%edx)
+ movl %eax,24(%edx)
+ movl %eax,28(%edx)
+ movl %eax,32(%edx)
+ movl %eax,36(%edx)
+ movl %eax,40(%edx)
+ movl %eax,44(%edx)
+ movl %eax,48(%edx)
+ movl %eax,52(%edx)
+ movl %eax,56(%edx)
+ movl %eax,60(%edx)
+ addl $64,%edx
+ subl $64,%ecx
+ jnz 2b
+ ret
+
+/
+/ do 16 byte chunks
+/
+ SUPERALIGN_TEXT
+3:
+ cmpl $16,%ecx
+ jb 4f
+ movl %eax,(%edx)
+ movl %eax,4(%edx)
+ movl %eax,8(%edx)
+ movl %eax,12(%edx)
+ addl $16,%edx
+ subl $16,%ecx
+ jnz 3b
+ ret
+
+/
+/ do 4 byte chunks
+/
+ SUPERALIGN_TEXT
+4:
+ cmpl $4,%ecx
+ jb 5f
+ movl %eax,(%edx)
+ addl $4,%edx
+ subl $4,%ecx
+ jnz 4b
+ ret
+
+/
+/ do 1 byte chunks
+/ a jump table seems to be faster than a loop or more range reductions
+/
+/ XXX need a const section for non-text
+/
+ SUPERALIGN_TEXT
+jtab:
+ .long do0
+ .long do1
+ .long do2
+ .long do3
+
+ SUPERALIGN_TEXT
+5:
+ jmp jtab(,%ecx,4)
+
+ SUPERALIGN_TEXT
+do3:
+ movw %ax,(%edx)
+ movb %al,2(%edx)
+ ret
+
+ SUPERALIGN_TEXT
+do2:
+ movw %ax,(%edx)
+ ret
+
+ SUPERALIGN_TEXT
+do1:
+ movb %al,(%edx)
+
+ SUPERALIGN_TEXT
+do0:
+ ret
+#endif /* I486_CPU */
+
+/* fillw(pat, base, cnt) */
+ENTRY(fillw)
+ pushl %edi
+ movl 8(%esp),%eax
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ stosw
+ popl %edi
+ ret
+
+/* filli(pat, base, cnt) */
+ENTRY(filli)
+ pushl %edi
+ movl 8(%esp),%eax
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ stosl
+ popl %edi
+ ret
+
+ENTRY(bcopyb)
+bcopyb:
+ pushl %esi
+ pushl %edi
+ movl 12(%esp),%esi
+ movl 16(%esp),%edi
+ movl 20(%esp),%ecx
+ cmpl %esi,%edi /* potentially overlapping? */
+ jnb 1f
+ cld /* nope, copy forwards */
+ rep
+ movsb
+ popl %edi
+ popl %esi
+ ret
+
+ ALIGN_TEXT
+1:
+ addl %ecx,%edi /* copy backwards. */
+ addl %ecx,%esi
+ std
+ decl %edi
+ decl %esi
+ rep
+ movsb
+ popl %edi
+ popl %esi
+ cld
+ ret
+
+ENTRY(bcopyw)
+bcopyw:
+ pushl %esi
+ pushl %edi
+ movl 12(%esp),%esi
+ movl 16(%esp),%edi
+ movl 20(%esp),%ecx
+ cmpl %esi,%edi /* potentially overlapping? */
+ jnb 1f
+ shrl $1,%ecx /* copy by 16-bit words */
+ cld /* nope, copy forwards */
+ rep
+ movsw
+ adc %ecx,%ecx /* any bytes left? */
+ rep
+ movsb
+ popl %edi
+ popl %esi
+ ret
+
+ ALIGN_TEXT
+1:
+ addl %ecx,%edi /* copy backwards */
+ addl %ecx,%esi
+ andl $1,%ecx /* any fractional bytes? */
+ decl %edi
+ decl %esi
+ std
+ rep
+ movsb
+ movl 20(%esp),%ecx /* copy remainder by 16-bit words */
+ shrl $1,%ecx
+ decl %esi
+ decl %edi
+ rep
+ movsw
+ popl %edi
+ popl %esi
+ cld
+ ret
+
+ENTRY(bcopyx)
+ movl 16(%esp),%eax
+ cmpl $2,%eax
+ je bcopyw /* not _bcopyw, to avoid multiple mcounts */
+ cmpl $4,%eax
+ je bcopy /* XXX the shared ret's break mexitcount */
+ jmp bcopyb
+
+/*
+ * (ov)bcopy(src, dst, cnt)
+ * ws@tools.de (Wolfgang Solfrank, TooLs GmbH) +49-228-985800
+ */
+ALTENTRY(ovbcopy)
+ENTRY(bcopy)
+bcopy:
+ pushl %esi
+ pushl %edi
+ movl 12(%esp),%esi
+ movl 16(%esp),%edi
+ movl 20(%esp),%ecx
+ cmpl %esi,%edi /* potentially overlapping? */
+ jnb 1f
+ shrl $2,%ecx /* copy by 32-bit words */
+ cld /* nope, copy forwards */
+ rep
+ movsl
+ movl 20(%esp),%ecx
+ andl $3,%ecx /* any bytes left? */
+ rep
+ movsb
+ popl %edi
+ popl %esi
+ ret
+
+ ALIGN_TEXT
+1:
+ addl %ecx,%edi /* copy backwards */
+ addl %ecx,%esi
+ andl $3,%ecx /* any fractional bytes? */
+ decl %edi
+ decl %esi
+ std
+ rep
+ movsb
+ movl 20(%esp),%ecx /* copy remainder by 32-bit words */
+ shrl $2,%ecx
+ subl $3,%esi
+ subl $3,%edi
+ rep
+ movsl
+ popl %edi
+ popl %esi
+ cld
+ ret
+
+ALTENTRY(ntohl)
+ENTRY(htonl)
+ movl 4(%esp),%eax
+#ifdef i486
+/* XXX */
+/* Since Gas 1.38 does not grok bswap this has been coded as the
+ * equivalent bytes. This can be changed back to bswap when we
+ * upgrade to a newer version of Gas
+ */
+ /* bswap %eax */
+ .byte 0x0f
+ .byte 0xc8
+#else
+ xchgb %al,%ah
+ roll $16,%eax
+ xchgb %al,%ah
+#endif
+ ret
+
+ALTENTRY(ntohs)
+ENTRY(htons)
+ movzwl 4(%esp),%eax
+ xchgb %al,%ah
+ ret
+
+/*****************************************************************************/
+/* copyout and fubyte family */
+/*****************************************************************************/
+/*
+ * Access user memory from inside the kernel. These routines and possibly
+ * the math- and DOS emulators should be the only places that do this.
+ *
+ * We have to access the memory with user's permissions, so use a segment
+ * selector with RPL 3. For writes to user space we have to additionally
+ * check the PTE for write permission, because the 386 does not check
+ * write permissions when we are executing with EPL 0. The 486 does check
+ * this if the WP bit is set in CR0, so we can use a simpler version here.
+ *
+ * These routines set curpcb->onfault for the time they execute. When a
+ * protection violation occurs inside the functions, the trap handler
+ * returns to *curpcb->onfault instead of the function.
+ */
+
+
+ENTRY(copyout) /* copyout(from_kernel, to_user, len) */
+ movl _curpcb,%eax
+ movl $copyout_fault,PCB_ONFAULT(%eax)
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+ movl 16(%esp),%esi
+ movl 20(%esp),%edi
+ movl 24(%esp),%ebx
+ orl %ebx,%ebx /* anything to do? */
+ jz done_copyout
+
+ /*
+ * Check explicitly for non-user addresses. If 486 write protection
+ * is being used, this check is essential because we are in kernel
+ * mode so the h/w does not provide any protection against writing
+ * kernel addresses.
+ *
+ * Otherwise, it saves having to load and restore %es to get the
+ * usual segment-based protection (the destination segment for movs
+ * is always %es). The other explicit checks for user-writablility
+ * are not quite sufficient. They fail for the user area because
+ * we mapped the user area read/write to avoid having an #ifdef in
+ * vm_machdep.c. They fail for user PTEs and/or PTDs! (107
+ * addresses including 0xff800000 and 0xfc000000). I'm not sure if
+ * this can be fixed. Marking the PTEs supervisor mode and the
+ * PDE's user mode would almost work, but there may be a problem
+ * with the self-referential PDE.
+ */
+ movl %edi,%eax
+ addl %ebx,%eax
+ jc copyout_fault
+/*
+ * XXX STOP USING VM_MAXUSER_ADDRESS.
+ * It is an end address, not a max, so every time it is used correctly it
+ * looks like there is an off by one error, and of course it caused an off
+ * by one error in several places.
+ */
+ cmpl $VM_MAXUSER_ADDRESS,%eax
+ ja copyout_fault
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 3f
+#endif
+/*
+ * We have to check each PTE for user write permission.
+ * The checking may cause a page fault, so it is important to set
+ * up everything for return via copyout_fault before here.
+ */
+ /* compute number of pages */
+ movl %edi,%ecx
+ andl $NBPG-1,%ecx
+ addl %ebx,%ecx
+ decl %ecx
+ shrl $IDXSHIFT+2,%ecx
+ incl %ecx
+
+ /* compute PTE offset for start address */
+ movl %edi,%edx
+ shrl $IDXSHIFT,%edx
+ andb $0xfc,%dl
+
+1: /* check PTE for each page */
+ movb _PTmap(%edx),%al
+ andb $0x07,%al /* Pages must be VALID + USERACC + WRITABLE */
+ cmpb $0x07,%al
+ je 2f
+
+ /* simulate a trap */
+ pushl %edx
+ pushl %ecx
+ shll $IDXSHIFT,%edx
+ pushl %edx
+ call _trapwrite /* trapwrite(addr) */
+ popl %edx
+ popl %ecx
+ popl %edx
+
+ orl %eax,%eax /* if not ok, return EFAULT */
+ jnz copyout_fault
+
+2:
+ addl $4,%edx
+ decl %ecx
+ jnz 1b /* check next page */
+#endif /* I386_CPU */
+
+ /* bcopy(%esi, %edi, %ebx) */
+3:
+ movl %ebx,%ecx
+ shrl $2,%ecx
+ cld
+ rep
+ movsl
+ movb %bl,%cl
+ andb $3,%cl
+ rep
+ movsb
+
+done_copyout:
+ popl %ebx
+ popl %edi
+ popl %esi
+ xorl %eax,%eax
+ movl _curpcb,%edx
+ movl %eax,PCB_ONFAULT(%edx)
+ ret
+
+ ALIGN_TEXT
+copyout_fault:
+ popl %ebx
+ popl %edi
+ popl %esi
+ movl _curpcb,%edx
+ movl $0,PCB_ONFAULT(%edx)
+ movl $EFAULT,%eax
+ ret
+
+/* copyin(from_user, to_kernel, len) */
+ENTRY(copyin)
+ movl _curpcb,%eax
+ movl $copyin_fault,PCB_ONFAULT(%eax)
+ pushl %esi
+ pushl %edi
+ movl 12(%esp),%esi /* caddr_t from */
+ movl 16(%esp),%edi /* caddr_t to */
+ movl 20(%esp),%ecx /* size_t len */
+
+ /*
+ * make sure address is valid
+ */
+ movl %esi,%edx
+ addl %ecx,%edx
+ jc copyin_fault
+ cmpl $VM_MAXUSER_ADDRESS,%edx
+ ja copyin_fault
+
+ movb %cl,%al
+ shrl $2,%ecx /* copy longword-wise */
+ cld
+ rep
+ movsl
+ movb %al,%cl
+ andb $3,%cl /* copy remaining bytes */
+ rep
+ movsb
+
+ popl %edi
+ popl %esi
+ xorl %eax,%eax
+ movl _curpcb,%edx
+ movl %eax,PCB_ONFAULT(%edx)
+ ret
+
+ ALIGN_TEXT
+copyin_fault:
+ popl %edi
+ popl %esi
+ movl _curpcb,%edx
+ movl $0,PCB_ONFAULT(%edx)
+ movl $EFAULT,%eax
+ ret
+
+/*
+ * fu{byte,sword,word} : fetch a byte (sword, word) from user memory
+ */
+ALTENTRY(fuiword)
+ENTRY(fuword)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx /* from */
+
+ cmpl $VM_MAXUSER_ADDRESS-4,%edx /* verify address is valid */
+ ja fusufault
+
+ movl (%edx),%eax
+ movl $0,PCB_ONFAULT(%ecx)
+ ret
+
+/*
+ * These two routines are called from the profiling code, potentially
+ * at interrupt time. If they fail, that's okay, good things will
+ * happen later. Fail all the time for now - until the trap code is
+ * able to deal with this.
+ */
+ALTENTRY(suswintr)
+ENTRY(fuswintr)
+ movl $-1,%eax
+ ret
+
+ENTRY(fusword)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+ cmpl $VM_MAXUSER_ADDRESS-2,%edx
+ ja fusufault
+
+ movzwl (%edx),%eax
+ movl $0,PCB_ONFAULT(%ecx)
+ ret
+
+ALTENTRY(fuibyte)
+ENTRY(fubyte)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+ cmpl $VM_MAXUSER_ADDRESS-1,%edx
+ ja fusufault
+
+ movzbl (%edx),%eax
+ movl $0,PCB_ONFAULT(%ecx)
+ ret
+
+ ALIGN_TEXT
+fusufault:
+ movl _curpcb,%ecx
+ xorl %eax,%eax
+ movl %eax,PCB_ONFAULT(%ecx)
+ decl %eax
+ ret
+
+/*
+ * su{byte,sword,word}: write a byte (word, longword) to user memory
+ */
+ALTENTRY(suiword)
+ENTRY(suword)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 2f /* we only have to set the right segment selector */
+#endif /* I486_CPU || I586_CPU */
+
+ /* XXX - page boundary crossing is still not handled */
+ movl %edx,%eax
+ shrl $IDXSHIFT,%edx
+ andb $0xfc,%dl
+ movb _PTmap(%edx),%dl
+ andb $0x7,%dl /* must be VALID + USERACC + WRITE */
+ cmpb $0x7,%dl
+ je 1f
+
+ /* simulate a trap */
+ pushl %eax
+ call _trapwrite
+ popl %edx /* remove junk parameter from stack */
+ movl _curpcb,%ecx /* restore trashed register */
+ orl %eax,%eax
+ jnz fusufault
+1:
+ movl 4(%esp),%edx
+#endif
+
+2:
+ cmpl $VM_MAXUSER_ADDRESS-4,%edx /* verify address validity */
+ ja fusufault
+
+ movl 8(%esp),%eax
+ movl %eax,(%edx)
+ xorl %eax,%eax
+ movl %eax,PCB_ONFAULT(%ecx)
+ ret
+
+ENTRY(susword)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 2f
+#endif /* I486_CPU || I586_CPU */
+
+ /* XXX - page boundary crossing is still not handled */
+ movl %edx,%eax
+ shrl $IDXSHIFT,%edx
+ andb $0xfc,%dl
+ movb _PTmap(%edx),%dl
+ andb $0x7,%dl /* must be VALID + USERACC + WRITE */
+ cmpb $0x7,%dl
+ je 1f
+
+ /* simulate a trap */
+ pushl %eax
+ call _trapwrite
+ popl %edx /* remove junk parameter from stack */
+ movl _curpcb,%ecx /* restore trashed register */
+ orl %eax,%eax
+ jnz fusufault
+1:
+ movl 4(%esp),%edx
+#endif
+
+2:
+ cmpl $VM_MAXUSER_ADDRESS-2,%edx /* verify address validity */
+ ja fusufault
+
+ movw 8(%esp),%ax
+ movw %ax,(%edx)
+ xorl %eax,%eax
+ movl %eax,PCB_ONFAULT(%ecx)
+ ret
+
+ALTENTRY(suibyte)
+ENTRY(subyte)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 2f
+#endif /* I486_CPU || I586_CPU */
+
+ movl %edx,%eax
+ shrl $IDXSHIFT,%edx
+ andb $0xfc,%dl
+ movb _PTmap(%edx),%dl
+ andb $0x7,%dl /* must be VALID + USERACC + WRITE */
+ cmpb $0x7,%dl
+ je 1f
+
+ /* simulate a trap */
+ pushl %eax
+ call _trapwrite
+ popl %edx /* remove junk parameter from stack */
+ movl _curpcb,%ecx /* restore trashed register */
+ orl %eax,%eax
+ jnz fusufault
+1:
+ movl 4(%esp),%edx
+#endif
+
+2:
+ cmpl $VM_MAXUSER_ADDRESS-1,%edx /* verify address validity */
+ ja fusufault
+
+ movb 8(%esp),%al
+ movb %al,(%edx)
+ xorl %eax,%eax
+ movl %eax,PCB_ONFAULT(%ecx)
+ ret
+
+/*
+ * copyoutstr(from, to, maxlen, int *lencopied)
+ * copy a string from from to to, stop when a 0 character is reached.
+ * return ENAMETOOLONG if string is longer than maxlen, and
+ * EFAULT on protection violations. If lencopied is non-zero,
+ * return the actual length in *lencopied.
+ */
+ENTRY(copyoutstr)
+ pushl %esi
+ pushl %edi
+ movl _curpcb,%ecx
+ movl $cpystrflt,PCB_ONFAULT(%ecx) /* XXX rename copyoutstr_fault */
+
+ movl 12(%esp),%esi /* %esi = from */
+ movl 16(%esp),%edi /* %edi = to */
+ movl 20(%esp),%edx /* %edx = maxlen */
+ cld
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 5f
+#endif /* I486_CPU || I586_CPU */
+
+1:
+ /*
+ * It suffices to check that the first byte is in user space, because
+ * we look at a page at a time and the end address is on a page
+ * boundary.
+ */
+ cmpl $VM_MAXUSER_ADDRESS-1,%edi
+ ja cpystrflt
+
+ movl %edi,%eax
+ shrl $IDXSHIFT,%eax
+ andb $0xfc,%al
+ movb _PTmap(%eax),%al
+ andb $7,%al
+ cmpb $7,%al
+ je 2f
+
+ /* simulate trap */
+ pushl %edx
+ pushl %edi
+ call _trapwrite
+ cld
+ popl %edi
+ popl %edx
+ orl %eax,%eax
+ jnz cpystrflt
+
+2: /* copy up to end of this page */
+ movl %edi,%eax
+ andl $NBPG-1,%eax
+ movl $NBPG,%ecx
+ subl %eax,%ecx /* ecx = NBPG - (src % NBPG) */
+ cmpl %ecx,%edx
+ jae 3f
+ movl %edx,%ecx /* ecx = min(ecx, edx) */
+3:
+ orl %ecx,%ecx
+ jz 4f
+ decl %ecx
+ decl %edx
+ lodsb
+ stosb
+ orb %al,%al
+ jnz 3b
+
+ /* Success -- 0 byte reached */
+ decl %edx
+ xorl %eax,%eax
+ jmp 6f
+
+4: /* next page */
+ orl %edx,%edx
+ jnz 1b
+
+ /* edx is zero -- return ENAMETOOLONG */
+ movl $ENAMETOOLONG,%eax
+ jmp cpystrflt_x
+#endif /* I386_CPU */
+
+#if defined(I486_CPU) || defined(I586_CPU)
+5:
+ incl %edx
+1:
+ decl %edx
+ jz 2f
+ /*
+ * XXX - would be faster to rewrite this function to use
+ * strlen() and copyout().
+ */
+ cmpl $VM_MAXUSER_ADDRESS-1,%edi
+ ja cpystrflt
+
+ lodsb
+ stosb
+ orb %al,%al
+ jnz 1b
+
+ /* Success -- 0 byte reached */
+ decl %edx
+ xorl %eax,%eax
+ jmp cpystrflt_x
+2:
+ /* edx is zero -- return ENAMETOOLONG */
+ movl $ENAMETOOLONG,%eax
+ jmp cpystrflt_x
+
+#endif /* I486_CPU || I586_CPU */
+
+/*
+ * This was split from copyinstr_fault mainly because pushing gs changes the
+ * stack offsets. It's better to have it separate for mcounting too.
+ */
+cpystrflt:
+ movl $EFAULT,%eax
+cpystrflt_x:
+ /* set *lencopied and return %eax */
+ movl _curpcb,%ecx
+ movl $0,PCB_ONFAULT(%ecx)
+ movl 20(%esp),%ecx
+ subl %edx,%ecx
+ movl 24(%esp),%edx
+ orl %edx,%edx
+ jz 1f
+ movl %ecx,(%edx)
+1:
+ popl %edi
+ popl %esi
+ ret
+
+
+/*
+ * copyinstr(from, to, maxlen, int *lencopied)
+ * copy a string from from to to, stop when a 0 character is reached.
+ * return ENAMETOOLONG if string is longer than maxlen, and
+ * EFAULT on protection violations. If lencopied is non-zero,
+ * return the actual length in *lencopied.
+ */
+ENTRY(copyinstr)
+ pushl %esi
+ pushl %edi
+ movl _curpcb,%ecx
+ movl $copyinstr_fault,PCB_ONFAULT(%ecx)
+
+ movl 12(%esp),%esi /* %esi = from */
+ movl 16(%esp),%edi /* %edi = to */
+ movl 20(%esp),%edx /* %edx = maxlen */
+ /*
+ * XXX should avoid touching gs. Either copy the string in and
+ * check the bounds later or get its length and check the bounds
+ * and then use copyin().
+ */
+ pushl %gs
+ movl __udatasel,%eax
+ movl %ax,%gs
+ incl %edx
+ cld
+1:
+ decl %edx
+ jz 2f
+ gs
+ lodsb
+ stosb
+ orb %al,%al
+ jnz 1b
+
+ /* Success -- 0 byte reached */
+ decl %edx
+ xorl %eax,%eax
+ jmp 3f
+2:
+ /* edx is zero -- return ENAMETOOLONG */
+ movl $ENAMETOOLONG,%eax
+ jmp 3f
+
+ ALIGN_TEXT
+copyinstr_fault:
+ movl $EFAULT,%eax
+3:
+ /* set *lencopied and return %eax */
+ movl _curpcb,%ecx
+ movl $0,PCB_ONFAULT(%ecx)
+ movl 24(%esp),%ecx
+ subl %edx,%ecx
+ movl 28(%esp),%edx
+ orl %edx,%edx
+ jz 4f
+ movl %ecx,(%edx)
+4:
+ popl %gs
+ popl %edi
+ popl %esi
+ ret
+
+
+/*
+ * copystr(from, to, maxlen, int *lencopied)
+ */
+ENTRY(copystr)
+ pushl %esi
+ pushl %edi
+
+ movl 12(%esp),%esi /* %esi = from */
+ movl 16(%esp),%edi /* %edi = to */
+ movl 20(%esp),%edx /* %edx = maxlen */
+ incl %edx
+ cld
+1:
+ decl %edx
+ jz 4f
+ lodsb
+ stosb
+ orb %al,%al
+ jnz 1b
+
+ /* Success -- 0 byte reached */
+ decl %edx
+ xorl %eax,%eax
+ jmp 6f
+4:
+ /* edx is zero -- return ENAMETOOLONG */
+ movl $ENAMETOOLONG,%eax
+
+6:
+ /* set *lencopied and return %eax */
+ movl 20(%esp),%ecx
+ subl %edx,%ecx
+ movl 24(%esp),%edx
+ orl %edx,%edx
+ jz 7f
+ movl %ecx,(%edx)
+7:
+ popl %edi
+ popl %esi
+ ret
+
+/*
+ * Handling of special 386 registers and descriptor tables etc
+ */
+/* void lgdt(struct region_descriptor *rdp); */
+ENTRY(lgdt)
+ /* reload the descriptor table */
+ movl 4(%esp),%eax
+ lgdt (%eax)
+
+ /* flush the prefetch q */
+ jmp 1f
+ nop
+1:
+ /* reload "stale" selectors */
+ movl $KDSEL,%eax
+ movl %ax,%ds
+ movl %ax,%es
+ movl %ax,%ss
+
+ /* reload code selector by turning return into intersegmental return */
+ movl (%esp),%eax
+ pushl %eax
+# movl $KCSEL,4(%esp)
+ movl $8,4(%esp)
+ lret
+
+/*
+ * void lidt(struct region_descriptor *rdp);
+ */
+ENTRY(lidt)
+ movl 4(%esp),%eax
+ lidt (%eax)
+ ret
+
+/*
+ * void lldt(u_short sel)
+ */
+ENTRY(lldt)
+ lldt 4(%esp)
+ ret
+
+/*
+ * void ltr(u_short sel)
+ */
+ENTRY(ltr)
+ ltr 4(%esp)
+ ret
+
+/* ssdtosd(*ssdp,*sdp) */
+ENTRY(ssdtosd)
+ pushl %ebx
+ movl 8(%esp),%ecx
+ movl 8(%ecx),%ebx
+ shll $16,%ebx
+ movl (%ecx),%edx
+ roll $16,%edx
+ movb %dh,%bl
+ movb %dl,%bh
+ rorl $8,%ebx
+ movl 4(%ecx),%eax
+ movw %ax,%dx
+ andl $0xf0000,%eax
+ orl %eax,%ebx
+ movl 12(%esp),%ecx
+ movl %edx,(%ecx)
+ movl %ebx,4(%ecx)
+ popl %ebx
+ ret
+
+/* load_cr0(cr0) */
+ENTRY(load_cr0)
+ movl 4(%esp),%eax
+ movl %eax,%cr0
+ ret
+
+/* rcr0() */
+ENTRY(rcr0)
+ movl %cr0,%eax
+ ret
+
+/* rcr3() */
+ENTRY(rcr3)
+ movl %cr3,%eax
+ ret
+
+/* void load_cr3(caddr_t cr3) */
+ENTRY(load_cr3)
+ movl 4(%esp),%eax
+ orl $I386_CR3PAT,%eax
+ movl %eax,%cr3
+ ret
+
+
+/*****************************************************************************/
+/* setjump, longjump */
+/*****************************************************************************/
+
+ENTRY(setjmp)
+ movl 4(%esp),%eax
+ movl %ebx,(%eax) /* save ebx */
+ movl %esp,4(%eax) /* save esp */
+ movl %ebp,8(%eax) /* save ebp */
+ movl %esi,12(%eax) /* save esi */
+ movl %edi,16(%eax) /* save edi */
+ movl (%esp),%edx /* get rta */
+ movl %edx,20(%eax) /* save eip */
+ xorl %eax,%eax /* return(0); */
+ ret
+
+ENTRY(longjmp)
+ movl 4(%esp),%eax
+ movl (%eax),%ebx /* restore ebx */
+ movl 4(%eax),%esp /* restore esp */
+ movl 8(%eax),%ebp /* restore ebp */
+ movl 12(%eax),%esi /* restore esi */
+ movl 16(%eax),%edi /* restore edi */
+ movl 20(%eax),%edx /* get rta */
+ movl %edx,(%esp) /* put in return frame */
+ xorl %eax,%eax /* return(1); */
+ incl %eax
+ ret
diff --git a/sys/amd64/amd64/support.s b/sys/amd64/amd64/support.s
new file mode 100644
index 0000000..c4e37df
--- /dev/null
+++ b/sys/amd64/amd64/support.s
@@ -0,0 +1,1221 @@
+/*-
+ * Copyright (c) 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: support.s,v 1.10 1994/06/06 14:23:49 davidg Exp $
+ */
+
+#include "assym.s" /* system definitions */
+#include "errno.h" /* error return codes */
+#include "machine/asmacros.h" /* miscellaneous asm macros */
+#include "machine/cputypes.h" /* types of CPUs */
+
+#define KDSEL 0x10 /* kernel data selector */
+#define IDXSHIFT 10
+
+/*
+ * Support routines for GCC, general C-callable functions
+ */
+ENTRY(__udivsi3)
+ movl 4(%esp),%eax
+ xorl %edx,%edx
+ divl 8(%esp)
+ ret
+
+ENTRY(__divsi3)
+ movl 4(%esp),%eax
+ cltd
+ idivl 8(%esp)
+ ret
+
+ /*
+ * I/O bus instructions via C
+ */
+ENTRY(inb) /* val = inb(port) */
+ movl 4(%esp),%edx
+ subl %eax,%eax
+ inb %dx,%al
+ NOP
+ ret
+
+ENTRY(inw) /* val = inw(port) */
+ movl 4(%esp),%edx
+ subl %eax,%eax
+ inw %dx,%ax
+ NOP
+ ret
+
+ENTRY(insb) /* insb(port, addr, cnt) */
+ pushl %edi
+ movl 8(%esp),%edx
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ insb
+ NOP
+ movl %edi,%eax
+ popl %edi
+ ret
+
+ENTRY(insw) /* insw(port, addr, cnt) */
+ pushl %edi
+ movl 8(%esp),%edx
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ insw
+ NOP
+ movl %edi,%eax
+ popl %edi
+ ret
+
+ENTRY(insl) /* insl(port, addr, cnt) */
+ pushl %edi
+ movl 8(%esp),%edx
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ insl
+ NOP
+ movl %edi,%eax
+ popl %edi
+ ret
+
+ENTRY(rtcin) /* rtcin(val) */
+ movl 4(%esp),%eax
+ outb %al,$0x70
+ NOP
+ xorl %eax,%eax
+ inb $0x71,%al
+ NOP
+ ret
+
+ENTRY(outb) /* outb(port, val) */
+ movl 4(%esp),%edx
+ movl 8(%esp),%eax
+ outb %al,%dx
+ NOP
+ ret
+
+ENTRY(outw) /* outw(port, val) */
+ movl 4(%esp),%edx
+ movl 8(%esp),%eax
+ outw %ax,%dx
+ NOP
+ ret
+
+ENTRY(outsb) /* outsb(port, addr, cnt) */
+ pushl %esi
+ movl 8(%esp),%edx
+ movl 12(%esp),%esi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ outsb
+ NOP
+ movl %esi,%eax
+ popl %esi
+ ret
+
+ENTRY(outsw) /* outsw(port, addr, cnt) */
+ pushl %esi
+ movl 8(%esp),%edx
+ movl 12(%esp),%esi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ outsw
+ NOP
+ movl %esi,%eax
+ popl %esi
+ ret
+
+ENTRY(outsl) /* outsl(port, addr, cnt) */
+ pushl %esi
+ movl 8(%esp),%edx
+ movl 12(%esp),%esi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ outsl
+ NOP
+ movl %esi,%eax
+ popl %esi
+ ret
+
+/*
+ * bcopy family
+ */
+
+/*
+ * void bzero(void *base, u_int cnt)
+ * Special code for I486 because stosl uses lots
+ * of clocks. Makes little or no difference on DX2 type
+ * machines, but stosl is about 1/2 as fast as
+ * memory moves on a standard DX !!!!!
+ */
+ALTENTRY(blkclr)
+ENTRY(bzero)
+#if defined(I486_CPU)
+ cmpl $CPUCLASS_486,_cpu_class
+ jz 1f
+#endif
+
+ pushl %edi
+ movl 8(%esp),%edi
+ movl 12(%esp),%ecx
+ xorl %eax,%eax
+ shrl $2,%ecx
+ cld
+ rep
+ stosl
+ movl 12(%esp),%ecx
+ andl $3,%ecx
+ rep
+ stosb
+ popl %edi
+ ret
+
+#if defined(I486_CPU)
+ SUPERALIGN_TEXT
+1:
+ movl 4(%esp),%edx
+ movl 8(%esp),%ecx
+ xorl %eax,%eax
+/
+/ do 64 byte chunks first
+/
+/ XXX this is probably over-unrolled at least for DX2's
+/
+2:
+ cmpl $64,%ecx
+ jb 3f
+ movl %eax,(%edx)
+ movl %eax,4(%edx)
+ movl %eax,8(%edx)
+ movl %eax,12(%edx)
+ movl %eax,16(%edx)
+ movl %eax,20(%edx)
+ movl %eax,24(%edx)
+ movl %eax,28(%edx)
+ movl %eax,32(%edx)
+ movl %eax,36(%edx)
+ movl %eax,40(%edx)
+ movl %eax,44(%edx)
+ movl %eax,48(%edx)
+ movl %eax,52(%edx)
+ movl %eax,56(%edx)
+ movl %eax,60(%edx)
+ addl $64,%edx
+ subl $64,%ecx
+ jnz 2b
+ ret
+
+/
+/ do 16 byte chunks
+/
+ SUPERALIGN_TEXT
+3:
+ cmpl $16,%ecx
+ jb 4f
+ movl %eax,(%edx)
+ movl %eax,4(%edx)
+ movl %eax,8(%edx)
+ movl %eax,12(%edx)
+ addl $16,%edx
+ subl $16,%ecx
+ jnz 3b
+ ret
+
+/
+/ do 4 byte chunks
+/
+ SUPERALIGN_TEXT
+4:
+ cmpl $4,%ecx
+ jb 5f
+ movl %eax,(%edx)
+ addl $4,%edx
+ subl $4,%ecx
+ jnz 4b
+ ret
+
+/
+/ do 1 byte chunks
+/ a jump table seems to be faster than a loop or more range reductions
+/
+/ XXX need a const section for non-text
+/
+ SUPERALIGN_TEXT
+jtab:
+ .long do0
+ .long do1
+ .long do2
+ .long do3
+
+ SUPERALIGN_TEXT
+5:
+ jmp jtab(,%ecx,4)
+
+ SUPERALIGN_TEXT
+do3:
+ movw %ax,(%edx)
+ movb %al,2(%edx)
+ ret
+
+ SUPERALIGN_TEXT
+do2:
+ movw %ax,(%edx)
+ ret
+
+ SUPERALIGN_TEXT
+do1:
+ movb %al,(%edx)
+
+ SUPERALIGN_TEXT
+do0:
+ ret
+#endif /* I486_CPU */
+
+/* fillw(pat, base, cnt) */
+ENTRY(fillw)
+ pushl %edi
+ movl 8(%esp),%eax
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ stosw
+ popl %edi
+ ret
+
+/* filli(pat, base, cnt) */
+ENTRY(filli)
+ pushl %edi
+ movl 8(%esp),%eax
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ stosl
+ popl %edi
+ ret
+
+ENTRY(bcopyb)
+bcopyb:
+ pushl %esi
+ pushl %edi
+ movl 12(%esp),%esi
+ movl 16(%esp),%edi
+ movl 20(%esp),%ecx
+ cmpl %esi,%edi /* potentially overlapping? */
+ jnb 1f
+ cld /* nope, copy forwards */
+ rep
+ movsb
+ popl %edi
+ popl %esi
+ ret
+
+ ALIGN_TEXT
+1:
+ addl %ecx,%edi /* copy backwards. */
+ addl %ecx,%esi
+ std
+ decl %edi
+ decl %esi
+ rep
+ movsb
+ popl %edi
+ popl %esi
+ cld
+ ret
+
+ENTRY(bcopyw)
+bcopyw:
+ pushl %esi
+ pushl %edi
+ movl 12(%esp),%esi
+ movl 16(%esp),%edi
+ movl 20(%esp),%ecx
+ cmpl %esi,%edi /* potentially overlapping? */
+ jnb 1f
+ shrl $1,%ecx /* copy by 16-bit words */
+ cld /* nope, copy forwards */
+ rep
+ movsw
+ adc %ecx,%ecx /* any bytes left? */
+ rep
+ movsb
+ popl %edi
+ popl %esi
+ ret
+
+ ALIGN_TEXT
+1:
+ addl %ecx,%edi /* copy backwards */
+ addl %ecx,%esi
+ andl $1,%ecx /* any fractional bytes? */
+ decl %edi
+ decl %esi
+ std
+ rep
+ movsb
+ movl 20(%esp),%ecx /* copy remainder by 16-bit words */
+ shrl $1,%ecx
+ decl %esi
+ decl %edi
+ rep
+ movsw
+ popl %edi
+ popl %esi
+ cld
+ ret
+
+ENTRY(bcopyx)
+ movl 16(%esp),%eax
+ cmpl $2,%eax
+ je bcopyw /* not _bcopyw, to avoid multiple mcounts */
+ cmpl $4,%eax
+ je bcopy /* XXX the shared ret's break mexitcount */
+ jmp bcopyb
+
+/*
+ * (ov)bcopy(src, dst, cnt)
+ * ws@tools.de (Wolfgang Solfrank, TooLs GmbH) +49-228-985800
+ */
+ALTENTRY(ovbcopy)
+ENTRY(bcopy)
+bcopy:
+ pushl %esi
+ pushl %edi
+ movl 12(%esp),%esi
+ movl 16(%esp),%edi
+ movl 20(%esp),%ecx
+ cmpl %esi,%edi /* potentially overlapping? */
+ jnb 1f
+ shrl $2,%ecx /* copy by 32-bit words */
+ cld /* nope, copy forwards */
+ rep
+ movsl
+ movl 20(%esp),%ecx
+ andl $3,%ecx /* any bytes left? */
+ rep
+ movsb
+ popl %edi
+ popl %esi
+ ret
+
+ ALIGN_TEXT
+1:
+ addl %ecx,%edi /* copy backwards */
+ addl %ecx,%esi
+ andl $3,%ecx /* any fractional bytes? */
+ decl %edi
+ decl %esi
+ std
+ rep
+ movsb
+ movl 20(%esp),%ecx /* copy remainder by 32-bit words */
+ shrl $2,%ecx
+ subl $3,%esi
+ subl $3,%edi
+ rep
+ movsl
+ popl %edi
+ popl %esi
+ cld
+ ret
+
+ALTENTRY(ntohl)
+ENTRY(htonl)
+ movl 4(%esp),%eax
+#ifdef i486
+/* XXX */
+/* Since Gas 1.38 does not grok bswap this has been coded as the
+ * equivalent bytes. This can be changed back to bswap when we
+ * upgrade to a newer version of Gas
+ */
+ /* bswap %eax */
+ .byte 0x0f
+ .byte 0xc8
+#else
+ xchgb %al,%ah
+ roll $16,%eax
+ xchgb %al,%ah
+#endif
+ ret
+
+ALTENTRY(ntohs)
+ENTRY(htons)
+ movzwl 4(%esp),%eax
+ xchgb %al,%ah
+ ret
+
+/*****************************************************************************/
+/* copyout and fubyte family */
+/*****************************************************************************/
+/*
+ * Access user memory from inside the kernel. These routines and possibly
+ * the math- and DOS emulators should be the only places that do this.
+ *
+ * We have to access the memory with user's permissions, so use a segment
+ * selector with RPL 3. For writes to user space we have to additionally
+ * check the PTE for write permission, because the 386 does not check
+ * write permissions when we are executing with EPL 0. The 486 does check
+ * this if the WP bit is set in CR0, so we can use a simpler version here.
+ *
+ * These routines set curpcb->onfault for the time they execute. When a
+ * protection violation occurs inside the functions, the trap handler
+ * returns to *curpcb->onfault instead of the function.
+ */
+
+
+ENTRY(copyout) /* copyout(from_kernel, to_user, len) */
+ movl _curpcb,%eax
+ movl $copyout_fault,PCB_ONFAULT(%eax)
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+ movl 16(%esp),%esi
+ movl 20(%esp),%edi
+ movl 24(%esp),%ebx
+ orl %ebx,%ebx /* anything to do? */
+ jz done_copyout
+
+ /*
+ * Check explicitly for non-user addresses. If 486 write protection
+ * is being used, this check is essential because we are in kernel
+ * mode so the h/w does not provide any protection against writing
+ * kernel addresses.
+ *
+ * Otherwise, it saves having to load and restore %es to get the
+ * usual segment-based protection (the destination segment for movs
+ * is always %es). The other explicit checks for user-writablility
+ * are not quite sufficient. They fail for the user area because
+ * we mapped the user area read/write to avoid having an #ifdef in
+ * vm_machdep.c. They fail for user PTEs and/or PTDs! (107
+ * addresses including 0xff800000 and 0xfc000000). I'm not sure if
+ * this can be fixed. Marking the PTEs supervisor mode and the
+ * PDE's user mode would almost work, but there may be a problem
+ * with the self-referential PDE.
+ */
+ movl %edi,%eax
+ addl %ebx,%eax
+ jc copyout_fault
+/*
+ * XXX STOP USING VM_MAXUSER_ADDRESS.
+ * It is an end address, not a max, so every time it is used correctly it
+ * looks like there is an off by one error, and of course it caused an off
+ * by one error in several places.
+ */
+ cmpl $VM_MAXUSER_ADDRESS,%eax
+ ja copyout_fault
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 3f
+#endif
+/*
+ * We have to check each PTE for user write permission.
+ * The checking may cause a page fault, so it is important to set
+ * up everything for return via copyout_fault before here.
+ */
+ /* compute number of pages */
+ movl %edi,%ecx
+ andl $NBPG-1,%ecx
+ addl %ebx,%ecx
+ decl %ecx
+ shrl $IDXSHIFT+2,%ecx
+ incl %ecx
+
+ /* compute PTE offset for start address */
+ movl %edi,%edx
+ shrl $IDXSHIFT,%edx
+ andb $0xfc,%dl
+
+1: /* check PTE for each page */
+ movb _PTmap(%edx),%al
+ andb $0x07,%al /* Pages must be VALID + USERACC + WRITABLE */
+ cmpb $0x07,%al
+ je 2f
+
+ /* simulate a trap */
+ pushl %edx
+ pushl %ecx
+ shll $IDXSHIFT,%edx
+ pushl %edx
+ call _trapwrite /* trapwrite(addr) */
+ popl %edx
+ popl %ecx
+ popl %edx
+
+ orl %eax,%eax /* if not ok, return EFAULT */
+ jnz copyout_fault
+
+2:
+ addl $4,%edx
+ decl %ecx
+ jnz 1b /* check next page */
+#endif /* I386_CPU */
+
+ /* bcopy(%esi, %edi, %ebx) */
+3:
+ movl %ebx,%ecx
+ shrl $2,%ecx
+ cld
+ rep
+ movsl
+ movb %bl,%cl
+ andb $3,%cl
+ rep
+ movsb
+
+done_copyout:
+ popl %ebx
+ popl %edi
+ popl %esi
+ xorl %eax,%eax
+ movl _curpcb,%edx
+ movl %eax,PCB_ONFAULT(%edx)
+ ret
+
+ ALIGN_TEXT
+copyout_fault:
+ popl %ebx
+ popl %edi
+ popl %esi
+ movl _curpcb,%edx
+ movl $0,PCB_ONFAULT(%edx)
+ movl $EFAULT,%eax
+ ret
+
+/* copyin(from_user, to_kernel, len) */
+ENTRY(copyin)
+ movl _curpcb,%eax
+ movl $copyin_fault,PCB_ONFAULT(%eax)
+ pushl %esi
+ pushl %edi
+ movl 12(%esp),%esi /* caddr_t from */
+ movl 16(%esp),%edi /* caddr_t to */
+ movl 20(%esp),%ecx /* size_t len */
+
+ /*
+ * make sure address is valid
+ */
+ movl %esi,%edx
+ addl %ecx,%edx
+ jc copyin_fault
+ cmpl $VM_MAXUSER_ADDRESS,%edx
+ ja copyin_fault
+
+ movb %cl,%al
+ shrl $2,%ecx /* copy longword-wise */
+ cld
+ rep
+ movsl
+ movb %al,%cl
+ andb $3,%cl /* copy remaining bytes */
+ rep
+ movsb
+
+ popl %edi
+ popl %esi
+ xorl %eax,%eax
+ movl _curpcb,%edx
+ movl %eax,PCB_ONFAULT(%edx)
+ ret
+
+ ALIGN_TEXT
+copyin_fault:
+ popl %edi
+ popl %esi
+ movl _curpcb,%edx
+ movl $0,PCB_ONFAULT(%edx)
+ movl $EFAULT,%eax
+ ret
+
+/*
+ * fu{byte,sword,word} : fetch a byte (sword, word) from user memory
+ */
+ALTENTRY(fuiword)
+ENTRY(fuword)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx /* from */
+
+ cmpl $VM_MAXUSER_ADDRESS-4,%edx /* verify address is valid */
+ ja fusufault
+
+ movl (%edx),%eax
+ movl $0,PCB_ONFAULT(%ecx)
+ ret
+
+/*
+ * These two routines are called from the profiling code, potentially
+ * at interrupt time. If they fail, that's okay, good things will
+ * happen later. Fail all the time for now - until the trap code is
+ * able to deal with this.
+ */
+ALTENTRY(suswintr)
+ENTRY(fuswintr)
+ movl $-1,%eax
+ ret
+
+ENTRY(fusword)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+ cmpl $VM_MAXUSER_ADDRESS-2,%edx
+ ja fusufault
+
+ movzwl (%edx),%eax
+ movl $0,PCB_ONFAULT(%ecx)
+ ret
+
+ALTENTRY(fuibyte)
+ENTRY(fubyte)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+ cmpl $VM_MAXUSER_ADDRESS-1,%edx
+ ja fusufault
+
+ movzbl (%edx),%eax
+ movl $0,PCB_ONFAULT(%ecx)
+ ret
+
+ ALIGN_TEXT
+fusufault:
+ movl _curpcb,%ecx
+ xorl %eax,%eax
+ movl %eax,PCB_ONFAULT(%ecx)
+ decl %eax
+ ret
+
+/*
+ * su{byte,sword,word}: write a byte (word, longword) to user memory
+ */
+ALTENTRY(suiword)
+ENTRY(suword)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 2f /* we only have to set the right segment selector */
+#endif /* I486_CPU || I586_CPU */
+
+ /* XXX - page boundary crossing is still not handled */
+ movl %edx,%eax
+ shrl $IDXSHIFT,%edx
+ andb $0xfc,%dl
+ movb _PTmap(%edx),%dl
+ andb $0x7,%dl /* must be VALID + USERACC + WRITE */
+ cmpb $0x7,%dl
+ je 1f
+
+ /* simulate a trap */
+ pushl %eax
+ call _trapwrite
+ popl %edx /* remove junk parameter from stack */
+ movl _curpcb,%ecx /* restore trashed register */
+ orl %eax,%eax
+ jnz fusufault
+1:
+ movl 4(%esp),%edx
+#endif
+
+2:
+ cmpl $VM_MAXUSER_ADDRESS-4,%edx /* verify address validity */
+ ja fusufault
+
+ movl 8(%esp),%eax
+ movl %eax,(%edx)
+ xorl %eax,%eax
+ movl %eax,PCB_ONFAULT(%ecx)
+ ret
+
+ENTRY(susword)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 2f
+#endif /* I486_CPU || I586_CPU */
+
+ /* XXX - page boundary crossing is still not handled */
+ movl %edx,%eax
+ shrl $IDXSHIFT,%edx
+ andb $0xfc,%dl
+ movb _PTmap(%edx),%dl
+ andb $0x7,%dl /* must be VALID + USERACC + WRITE */
+ cmpb $0x7,%dl
+ je 1f
+
+ /* simulate a trap */
+ pushl %eax
+ call _trapwrite
+ popl %edx /* remove junk parameter from stack */
+ movl _curpcb,%ecx /* restore trashed register */
+ orl %eax,%eax
+ jnz fusufault
+1:
+ movl 4(%esp),%edx
+#endif
+
+2:
+ cmpl $VM_MAXUSER_ADDRESS-2,%edx /* verify address validity */
+ ja fusufault
+
+ movw 8(%esp),%ax
+ movw %ax,(%edx)
+ xorl %eax,%eax
+ movl %eax,PCB_ONFAULT(%ecx)
+ ret
+
+ALTENTRY(suibyte)
+ENTRY(subyte)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 2f
+#endif /* I486_CPU || I586_CPU */
+
+ movl %edx,%eax
+ shrl $IDXSHIFT,%edx
+ andb $0xfc,%dl
+ movb _PTmap(%edx),%dl
+ andb $0x7,%dl /* must be VALID + USERACC + WRITE */
+ cmpb $0x7,%dl
+ je 1f
+
+ /* simulate a trap */
+ pushl %eax
+ call _trapwrite
+ popl %edx /* remove junk parameter from stack */
+ movl _curpcb,%ecx /* restore trashed register */
+ orl %eax,%eax
+ jnz fusufault
+1:
+ movl 4(%esp),%edx
+#endif
+
+2:
+ cmpl $VM_MAXUSER_ADDRESS-1,%edx /* verify address validity */
+ ja fusufault
+
+ movb 8(%esp),%al
+ movb %al,(%edx)
+ xorl %eax,%eax
+ movl %eax,PCB_ONFAULT(%ecx)
+ ret
+
+/*
+ * copyoutstr(from, to, maxlen, int *lencopied)
+ * copy a string from from to to, stop when a 0 character is reached.
+ * return ENAMETOOLONG if string is longer than maxlen, and
+ * EFAULT on protection violations. If lencopied is non-zero,
+ * return the actual length in *lencopied.
+ */
+ENTRY(copyoutstr)
+ pushl %esi
+ pushl %edi
+ movl _curpcb,%ecx
+ movl $cpystrflt,PCB_ONFAULT(%ecx) /* XXX rename copyoutstr_fault */
+
+ movl 12(%esp),%esi /* %esi = from */
+ movl 16(%esp),%edi /* %edi = to */
+ movl 20(%esp),%edx /* %edx = maxlen */
+ cld
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 5f
+#endif /* I486_CPU || I586_CPU */
+
+1:
+ /*
+ * It suffices to check that the first byte is in user space, because
+ * we look at a page at a time and the end address is on a page
+ * boundary.
+ */
+ cmpl $VM_MAXUSER_ADDRESS-1,%edi
+ ja cpystrflt
+
+ movl %edi,%eax
+ shrl $IDXSHIFT,%eax
+ andb $0xfc,%al
+ movb _PTmap(%eax),%al
+ andb $7,%al
+ cmpb $7,%al
+ je 2f
+
+ /* simulate trap */
+ pushl %edx
+ pushl %edi
+ call _trapwrite
+ cld
+ popl %edi
+ popl %edx
+ orl %eax,%eax
+ jnz cpystrflt
+
+2: /* copy up to end of this page */
+ movl %edi,%eax
+ andl $NBPG-1,%eax
+ movl $NBPG,%ecx
+ subl %eax,%ecx /* ecx = NBPG - (src % NBPG) */
+ cmpl %ecx,%edx
+ jae 3f
+ movl %edx,%ecx /* ecx = min(ecx, edx) */
+3:
+ orl %ecx,%ecx
+ jz 4f
+ decl %ecx
+ decl %edx
+ lodsb
+ stosb
+ orb %al,%al
+ jnz 3b
+
+ /* Success -- 0 byte reached */
+ decl %edx
+ xorl %eax,%eax
+ jmp 6f
+
+4: /* next page */
+ orl %edx,%edx
+ jnz 1b
+
+ /* edx is zero -- return ENAMETOOLONG */
+ movl $ENAMETOOLONG,%eax
+ jmp cpystrflt_x
+#endif /* I386_CPU */
+
+#if defined(I486_CPU) || defined(I586_CPU)
+5:
+ incl %edx
+1:
+ decl %edx
+ jz 2f
+ /*
+ * XXX - would be faster to rewrite this function to use
+ * strlen() and copyout().
+ */
+ cmpl $VM_MAXUSER_ADDRESS-1,%edi
+ ja cpystrflt
+
+ lodsb
+ stosb
+ orb %al,%al
+ jnz 1b
+
+ /* Success -- 0 byte reached */
+ decl %edx
+ xorl %eax,%eax
+ jmp cpystrflt_x
+2:
+ /* edx is zero -- return ENAMETOOLONG */
+ movl $ENAMETOOLONG,%eax
+ jmp cpystrflt_x
+
+#endif /* I486_CPU || I586_CPU */
+
+/*
+ * This was split from copyinstr_fault mainly because pushing gs changes the
+ * stack offsets. It's better to have it separate for mcounting too.
+ */
+cpystrflt:
+ movl $EFAULT,%eax
+cpystrflt_x:
+ /* set *lencopied and return %eax */
+ movl _curpcb,%ecx
+ movl $0,PCB_ONFAULT(%ecx)
+ movl 20(%esp),%ecx
+ subl %edx,%ecx
+ movl 24(%esp),%edx
+ orl %edx,%edx
+ jz 1f
+ movl %ecx,(%edx)
+1:
+ popl %edi
+ popl %esi
+ ret
+
+
+/*
+ * copyinstr(from, to, maxlen, int *lencopied)
+ * copy a string from from to to, stop when a 0 character is reached.
+ * return ENAMETOOLONG if string is longer than maxlen, and
+ * EFAULT on protection violations. If lencopied is non-zero,
+ * return the actual length in *lencopied.
+ */
+ENTRY(copyinstr)
+ pushl %esi
+ pushl %edi
+ movl _curpcb,%ecx
+ movl $copyinstr_fault,PCB_ONFAULT(%ecx)
+
+ movl 12(%esp),%esi /* %esi = from */
+ movl 16(%esp),%edi /* %edi = to */
+ movl 20(%esp),%edx /* %edx = maxlen */
+ /*
+ * XXX should avoid touching gs. Either copy the string in and
+ * check the bounds later or get its length and check the bounds
+ * and then use copyin().
+ */
+ pushl %gs
+ movl __udatasel,%eax
+ movl %ax,%gs
+ incl %edx
+ cld
+1:
+ decl %edx
+ jz 2f
+ gs
+ lodsb
+ stosb
+ orb %al,%al
+ jnz 1b
+
+ /* Success -- 0 byte reached */
+ decl %edx
+ xorl %eax,%eax
+ jmp 3f
+2:
+ /* edx is zero -- return ENAMETOOLONG */
+ movl $ENAMETOOLONG,%eax
+ jmp 3f
+
+ ALIGN_TEXT
+copyinstr_fault:
+ movl $EFAULT,%eax
+3:
+ /* set *lencopied and return %eax */
+ movl _curpcb,%ecx
+ movl $0,PCB_ONFAULT(%ecx)
+ movl 24(%esp),%ecx
+ subl %edx,%ecx
+ movl 28(%esp),%edx
+ orl %edx,%edx
+ jz 4f
+ movl %ecx,(%edx)
+4:
+ popl %gs
+ popl %edi
+ popl %esi
+ ret
+
+
+/*
+ * copystr(from, to, maxlen, int *lencopied)
+ */
+ENTRY(copystr)
+ pushl %esi
+ pushl %edi
+
+ movl 12(%esp),%esi /* %esi = from */
+ movl 16(%esp),%edi /* %edi = to */
+ movl 20(%esp),%edx /* %edx = maxlen */
+ incl %edx
+ cld
+1:
+ decl %edx
+ jz 4f
+ lodsb
+ stosb
+ orb %al,%al
+ jnz 1b
+
+ /* Success -- 0 byte reached */
+ decl %edx
+ xorl %eax,%eax
+ jmp 6f
+4:
+ /* edx is zero -- return ENAMETOOLONG */
+ movl $ENAMETOOLONG,%eax
+
+6:
+ /* set *lencopied and return %eax */
+ movl 20(%esp),%ecx
+ subl %edx,%ecx
+ movl 24(%esp),%edx
+ orl %edx,%edx
+ jz 7f
+ movl %ecx,(%edx)
+7:
+ popl %edi
+ popl %esi
+ ret
+
+/*
+ * Handling of special 386 registers and descriptor tables etc
+ */
+/* void lgdt(struct region_descriptor *rdp); */
+ENTRY(lgdt)
+ /* reload the descriptor table */
+ movl 4(%esp),%eax
+ lgdt (%eax)
+
+ /* flush the prefetch q */
+ jmp 1f
+ nop
+1:
+ /* reload "stale" selectors */
+ movl $KDSEL,%eax
+ movl %ax,%ds
+ movl %ax,%es
+ movl %ax,%ss
+
+ /* reload code selector by turning return into intersegmental return */
+ movl (%esp),%eax
+ pushl %eax
+# movl $KCSEL,4(%esp)
+ movl $8,4(%esp)
+ lret
+
+/*
+ * void lidt(struct region_descriptor *rdp);
+ */
+ENTRY(lidt)
+ movl 4(%esp),%eax
+ lidt (%eax)
+ ret
+
+/*
+ * void lldt(u_short sel)
+ */
+ENTRY(lldt)
+ lldt 4(%esp)
+ ret
+
+/*
+ * void ltr(u_short sel)
+ */
+ENTRY(ltr)
+ ltr 4(%esp)
+ ret
+
+/* ssdtosd(*ssdp,*sdp) */
+ENTRY(ssdtosd)
+ pushl %ebx
+ movl 8(%esp),%ecx
+ movl 8(%ecx),%ebx
+ shll $16,%ebx
+ movl (%ecx),%edx
+ roll $16,%edx
+ movb %dh,%bl
+ movb %dl,%bh
+ rorl $8,%ebx
+ movl 4(%ecx),%eax
+ movw %ax,%dx
+ andl $0xf0000,%eax
+ orl %eax,%ebx
+ movl 12(%esp),%ecx
+ movl %edx,(%ecx)
+ movl %ebx,4(%ecx)
+ popl %ebx
+ ret
+
+/* load_cr0(cr0) */
+ENTRY(load_cr0)
+ movl 4(%esp),%eax
+ movl %eax,%cr0
+ ret
+
+/* rcr0() */
+ENTRY(rcr0)
+ movl %cr0,%eax
+ ret
+
+/* rcr3() */
+ENTRY(rcr3)
+ movl %cr3,%eax
+ ret
+
+/* void load_cr3(caddr_t cr3) */
+ENTRY(load_cr3)
+ movl 4(%esp),%eax
+ orl $I386_CR3PAT,%eax
+ movl %eax,%cr3
+ ret
+
+
+/*****************************************************************************/
+/* setjump, longjump */
+/*****************************************************************************/
+
+ENTRY(setjmp)
+ movl 4(%esp),%eax
+ movl %ebx,(%eax) /* save ebx */
+ movl %esp,4(%eax) /* save esp */
+ movl %ebp,8(%eax) /* save ebp */
+ movl %esi,12(%eax) /* save esi */
+ movl %edi,16(%eax) /* save edi */
+ movl (%esp),%edx /* get rta */
+ movl %edx,20(%eax) /* save eip */
+ xorl %eax,%eax /* return(0); */
+ ret
+
+ENTRY(longjmp)
+ movl 4(%esp),%eax
+ movl (%eax),%ebx /* restore ebx */
+ movl 4(%eax),%esp /* restore esp */
+ movl 8(%eax),%ebp /* restore ebp */
+ movl 12(%eax),%esi /* restore esi */
+ movl 16(%eax),%edi /* restore edi */
+ movl 20(%eax),%edx /* get rta */
+ movl %edx,(%esp) /* put in return frame */
+ xorl %eax,%eax /* return(1); */
+ incl %eax
+ ret
diff --git a/sys/amd64/amd64/swtch.s b/sys/amd64/amd64/swtch.s
new file mode 100644
index 0000000..aa8b5ba
--- /dev/null
+++ b/sys/amd64/amd64/swtch.s
@@ -0,0 +1,458 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: swtch.s,v 1.5 1994/04/02 07:00:30 davidg Exp $
+ */
+
+#include "npx.h" /* for NNPX */
+#include "assym.s" /* for preprocessor defines */
+#include "errno.h" /* for error codes */
+
+#include "machine/asmacros.h" /* for miscellaneous assembly macros */
+#define LOCORE /* XXX inhibit C declarations */
+#include "machine/spl.h" /* for SWI_AST_MASK ... */
+
+
+/*****************************************************************************/
+/* Scheduling */
+/*****************************************************************************/
+
+/*
+ * The following primitives manipulate the run queues.
+ * _whichqs tells which of the 32 queues _qs
+ * have processes in them. setrunqueue puts processes into queues, Remrq
+ * removes them from queues. The running process is on no queue,
+ * other processes are on a queue related to p->p_priority, divided by 4
+ * actually to shrink the 0-127 range of priorities into the 32 available
+ * queues.
+ */
+ .data
+ .globl _curpcb, _whichqs
+_curpcb: .long 0 /* pointer to curproc's PCB area */
+_whichqs: .long 0 /* which run queues have data */
+
+ .globl _qs,_cnt,_panic
+ .comm _noproc,4
+ .comm _runrun,4
+
+ .globl _want_resched
+_want_resched: .long 0 /* we need to re-run the scheduler */
+
+ .text
+/*
+ * setrunqueue(p)
+ *
+ * Call should be made at spl6(), and p->p_stat should be SRUN
+ */
+ENTRY(setrunqueue)
+ movl 4(%esp),%eax
+ cmpl $0,P_RLINK(%eax) /* should not be on q already */
+ je set1
+ pushl $set2
+ call _panic
+set1:
+ movzbl P_PRI(%eax),%edx
+ shrl $2,%edx
+ btsl %edx,_whichqs /* set q full bit */
+ shll $3,%edx
+ addl $_qs,%edx /* locate q hdr */
+ movl %edx,P_LINK(%eax) /* link process on tail of q */
+ movl P_RLINK(%edx),%ecx
+ movl %ecx,P_RLINK(%eax)
+ movl %eax,P_RLINK(%edx)
+ movl %eax,P_LINK(%ecx)
+ ret
+
+set2: .asciz "setrunqueue"
+
+/*
+ * Remrq(p)
+ *
+ * Call should be made at spl6().
+ */
+ENTRY(remrq)
+ movl 4(%esp),%eax
+ movzbl P_PRI(%eax),%edx
+ shrl $2,%edx
+ btrl %edx,_whichqs /* clear full bit, panic if clear already */
+ jb rem1
+ pushl $rem3
+ call _panic
+rem1:
+ pushl %edx
+ movl P_LINK(%eax),%ecx /* unlink process */
+ movl P_RLINK(%eax),%edx
+ movl %edx,P_RLINK(%ecx)
+ movl P_RLINK(%eax),%ecx
+ movl P_LINK(%eax),%edx
+ movl %edx,P_LINK(%ecx)
+ popl %edx
+ movl $_qs,%ecx
+ shll $3,%edx
+ addl %edx,%ecx
+ cmpl P_LINK(%ecx),%ecx /* q still has something? */
+ je rem2
+ shrl $3,%edx /* yes, set bit as still full */
+ btsl %edx,_whichqs
+rem2:
+ movl $0,P_RLINK(%eax) /* zap reverse link to indicate off list */
+ ret
+
+rem3: .asciz "remrq"
+sw0: .asciz "cpu_switch"
+
+/*
+ * When no processes are on the runq, cpu_switch() branches to _idle
+ * to wait for something to come ready.
+ */
+ ALIGN_TEXT
+_idle:
+ MCOUNT
+ movl _IdlePTD,%ecx
+ movl %ecx,%cr3
+ movl $tmpstk-4,%esp
+ sti
+
+ /*
+ * XXX callers of cpu_switch() do a bogus splclock(). Locking should
+ * be left to cpu_switch().
+ */
+ movl $SWI_AST_MASK,_cpl
+ testl $~SWI_AST_MASK,_ipending
+ je idle_loop
+ call _splz
+
+ ALIGN_TEXT
+idle_loop:
+ cli
+ cmpl $0,_whichqs
+ jne sw1a
+ sti
+ hlt /* wait for interrupt */
+ jmp idle_loop
+
+badsw:
+ pushl $sw0
+ call _panic
+ /*NOTREACHED*/
+
+/*
+ * cpu_switch()
+ */
+ENTRY(cpu_switch)
+ incl _cnt+V_SWTCH
+
+ /* switch to new process. first, save context as needed */
+
+ movl _curproc,%ecx
+
+ /* if no process to save, don't bother */
+ testl %ecx,%ecx
+ je sw1
+
+ movl P_ADDR(%ecx),%ecx
+
+ movl (%esp),%eax /* Hardware registers */
+ movl %eax,PCB_EIP(%ecx)
+ movl %ebx,PCB_EBX(%ecx)
+ movl %esp,PCB_ESP(%ecx)
+ movl %ebp,PCB_EBP(%ecx)
+ movl %esi,PCB_ESI(%ecx)
+ movl %edi,PCB_EDI(%ecx)
+
+#if NNPX > 0
+ /* have we used fp, and need a save? */
+ mov _curproc,%eax
+ cmp %eax,_npxproc
+ jne 1f
+ pushl %ecx /* h/w bugs make saving complicated */
+ leal PCB_SAVEFPU(%ecx),%eax
+ pushl %eax
+ call _npxsave /* do it in a big C function */
+ popl %eax
+ popl %ecx
+1:
+#endif /* NNPX > 0 */
+
+ movl _CMAP2,%eax /* save temporary map PTE */
+ movl %eax,PCB_CMAP2(%ecx) /* in our context */
+ movl $0,_curproc /* out of process */
+
+# movw _cpl,%ax
+# movw %ax,PCB_IML(%ecx) /* save ipl */
+
+ /* save is done, now choose a new process or idle */
+sw1:
+ cli
+sw1a:
+ movl _whichqs,%edi
+2:
+ /* XXX - bsf is sloow */
+ bsfl %edi,%eax /* find a full q */
+ je _idle /* if none, idle */
+
+ /* XX update whichqs? */
+ btrl %eax,%edi /* clear q full status */
+ jnb 2b /* if it was clear, look for another */
+ movl %eax,%ebx /* save which one we are using */
+
+ shll $3,%eax
+ addl $_qs,%eax /* select q */
+ movl %eax,%esi
+
+#ifdef DIAGNOSTIC
+ cmpl P_LINK(%eax),%eax /* linked to self? (e.g. not on list) */
+ je badsw /* not possible */
+#endif
+
+ movl P_LINK(%eax),%ecx /* unlink from front of process q */
+ movl P_LINK(%ecx),%edx
+ movl %edx,P_LINK(%eax)
+ movl P_RLINK(%ecx),%eax
+ movl %eax,P_RLINK(%edx)
+
+ cmpl P_LINK(%ecx),%esi /* q empty */
+ je 3f
+ btsl %ebx,%edi /* nope, set to indicate full */
+3:
+ movl %edi,_whichqs /* update q status */
+
+ movl $0,%eax
+ movl %eax,_want_resched
+
+#ifdef DIAGNOSTIC
+ cmpl %eax,P_WCHAN(%ecx)
+ jne badsw
+ cmpb $SRUN,P_STAT(%ecx)
+ jne badsw
+#endif
+
+ movl %eax,P_RLINK(%ecx) /* isolate process to run */
+ movl P_ADDR(%ecx),%edx
+ movl PCB_CR3(%edx),%ebx
+
+ /* switch address space */
+ movl %ebx,%cr3
+
+ /* restore context */
+ movl PCB_EBX(%edx),%ebx
+ movl PCB_ESP(%edx),%esp
+ movl PCB_EBP(%edx),%ebp
+ movl PCB_ESI(%edx),%esi
+ movl PCB_EDI(%edx),%edi
+ movl PCB_EIP(%edx),%eax
+ movl %eax,(%esp)
+
+ movl PCB_CMAP2(%edx),%eax /* get temporary map */
+ movl %eax,_CMAP2 /* reload temporary map PTE */
+
+ movl %ecx,_curproc /* into next process */
+ movl %edx,_curpcb
+
+#ifdef USER_LDT
+ cmpl $0, PCB_USERLDT(%edx)
+ jnz 1f
+ movl __default_ldt,%eax
+ cmpl _currentldt,%eax
+ je 2f
+ lldt __default_ldt
+ movl %eax,_currentldt
+ jmp 2f
+1: pushl %edx
+ call _set_user_ldt
+ popl %edx
+2:
+#endif
+
+ pushl %edx /* save p to return */
+/*
+ * XXX - 0.0 forgot to save it - is that why this was commented out in 0.1?
+ * I think restoring the cpl is unnecessary, but we must turn off the cli
+ * now that spl*() don't do it as a side affect.
+ */
+ pushl PCB_IML(%edx)
+ sti
+#if 0
+ call _splx
+#endif
+ addl $4,%esp
+/*
+ * XXX - 0.0 gets here via swtch_to_inactive(). I think 0.1 gets here in the
+ * same way. Better return a value.
+ */
+ popl %eax /* return(p); */
+ ret
+
+ENTRY(mvesp)
+ movl %esp,%eax
+ ret
+/*
+ * struct proc *swtch_to_inactive(struct proc *p);
+ *
+ * At exit of a process, move off the address space of the
+ * process and onto a "safe" one. Then, on a temporary stack
+ * return and run code that disposes of the old state.
+ * Since this code requires a parameter from the "old" stack,
+ * pass it back as a return value.
+ */
+ENTRY(swtch_to_inactive)
+ popl %edx /* old pc */
+ popl %eax /* arg, our return value */
+ movl _IdlePTD,%ecx
+ movl %ecx,%cr3 /* good bye address space */
+ #write buffer?
+ movl $tmpstk-4,%esp /* temporary stack, compensated for call */
+ MEXITCOUNT
+ jmp %edx /* return, execute remainder of cleanup */
+
+/*
+ * savectx(pcb, altreturn)
+ * Update pcb, saving current processor state and arranging
+ * for alternate return ala longjmp in cpu_switch if altreturn is true.
+ */
+ENTRY(savectx)
+ movl 4(%esp),%ecx
+ movw _cpl,%ax
+ movw %ax,PCB_IML(%ecx)
+ movl (%esp),%eax
+ movl %eax,PCB_EIP(%ecx)
+ movl %ebx,PCB_EBX(%ecx)
+ movl %esp,PCB_ESP(%ecx)
+ movl %ebp,PCB_EBP(%ecx)
+ movl %esi,PCB_ESI(%ecx)
+ movl %edi,PCB_EDI(%ecx)
+
+#if NNPX > 0
+ /*
+ * If npxproc == NULL, then the npx h/w state is irrelevant and the
+ * state had better already be in the pcb. This is true for forks
+ * but not for dumps (the old book-keeping with FP flags in the pcb
+ * always lost for dumps because the dump pcb has 0 flags).
+ *
+ * If npxproc != NULL, then we have to save the npx h/w state to
+ * npxproc's pcb and copy it to the requested pcb, or save to the
+ * requested pcb and reload. Copying is easier because we would
+ * have to handle h/w bugs for reloading. We used to lose the
+ * parent's npx state for forks by forgetting to reload.
+ */
+ mov _npxproc,%eax
+ testl %eax,%eax
+ je 1f
+
+ pushl %ecx
+ movl P_ADDR(%eax),%eax
+ leal PCB_SAVEFPU(%eax),%eax
+ pushl %eax
+ pushl %eax
+ call _npxsave
+ popl %eax
+ popl %eax
+ popl %ecx
+
+ pushl %ecx
+ pushl $108+8*2 /* XXX h/w state size + padding */
+ leal PCB_SAVEFPU(%ecx),%ecx
+ pushl %ecx
+ pushl %eax
+ call _bcopy
+ addl $12,%esp
+ popl %ecx
+1:
+#endif /* NNPX > 0 */
+
+ movl _CMAP2,%edx /* save temporary map PTE */
+ movl %edx,PCB_CMAP2(%ecx) /* in our context */
+
+ cmpl $0,8(%esp)
+ je 1f
+ movl %esp,%edx /* relocate current sp relative to pcb */
+ subl $_kstack,%edx /* (sp is relative to kstack): */
+ addl %edx,%ecx /* pcb += sp - kstack; */
+ movl %eax,(%ecx) /* write return pc at (relocated) sp@ */
+
+/* this mess deals with replicating register state gcc hides */
+ movl 12(%esp),%eax
+ movl %eax,12(%ecx)
+ movl 16(%esp),%eax
+ movl %eax,16(%ecx)
+ movl 20(%esp),%eax
+ movl %eax,20(%ecx)
+ movl 24(%esp),%eax
+ movl %eax,24(%ecx)
+1:
+ xorl %eax,%eax /* return 0 */
+ ret
+
+/*
+ * addupc(int pc, struct uprof *up, int ticks):
+ * update profiling information for the user process.
+ */
+ENTRY(addupc)
+ pushl %ebp
+ movl %esp,%ebp
+ movl 12(%ebp),%edx /* up */
+ movl 8(%ebp),%eax /* pc */
+
+ subl PR_OFF(%edx),%eax /* pc -= up->pr_off */
+ jb L1 /* if (pc was < off) return */
+
+ shrl $1,%eax /* praddr = pc >> 1 */
+ imull PR_SCALE(%edx),%eax /* praddr *= up->pr_scale */
+ shrl $15,%eax /* praddr = praddr << 15 */
+ andl $-2,%eax /* praddr &= ~1 */
+
+ cmpl PR_SIZE(%edx),%eax /* if (praddr > up->pr_size) return */
+ ja L1
+
+/* addl %eax,%eax /* praddr -> word offset */
+ addl PR_BASE(%edx),%eax /* praddr += up-> pr_base */
+ movl 16(%ebp),%ecx /* ticks */
+
+ movl _curpcb,%edx
+ movl $proffault,PCB_ONFAULT(%edx)
+ addl %ecx,(%eax) /* storage location += ticks */
+ movl $0,PCB_ONFAULT(%edx)
+L1:
+ leave
+ ret
+
+ ALIGN_TEXT
+proffault:
+ /* if we get a fault, then kill profiling all together */
+ movl $0,PCB_ONFAULT(%edx) /* squish the fault handler */
+ movl 12(%ebp),%ecx
+ movl $0,PR_SCALE(%ecx) /* up->pr_scale = 0 */
+ leave
+ ret
diff --git a/sys/amd64/amd64/sys_machdep.c b/sys/amd64/amd64/sys_machdep.c
new file mode 100644
index 0000000..92758ad
--- /dev/null
+++ b/sys/amd64/amd64/sys_machdep.c
@@ -0,0 +1,328 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)sys_machdep.c 5.5 (Berkeley) 1/19/91
+ * $Id: sys_machdep.c,v 1.3 1993/10/16 14:15:10 rgrimes Exp $
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "ioctl.h"
+#include "file.h"
+#include "time.h"
+#include "proc.h"
+#include "uio.h"
+#include "kernel.h"
+#include "mtio.h"
+#include "buf.h"
+#include "trace.h"
+
+#ifdef USER_LDT
+#include "user.h"
+#include "machine/cpu.h"
+#include "machine/sysarch.h"
+#include "vm/vm_kern.h" /* for kernel_map */
+#endif
+
+#ifdef TRACE
+int nvualarm;
+
+struct vtrace_args {
+ int request;
+ int value;
+};
+
+vtrace(p, uap, retval)
+ struct proc *p;
+ register struct vtrace_args *uap;
+ int *retval;
+{
+ int vdoualarm();
+
+ switch (uap->request) {
+
+ case VTR_DISABLE: /* disable a trace point */
+ case VTR_ENABLE: /* enable a trace point */
+ if (uap->value < 0 || uap->value >= TR_NFLAGS)
+ return (EINVAL);
+ *retval = traceflags[uap->value];
+ traceflags[uap->value] = uap->request;
+ break;
+
+ case VTR_VALUE: /* return a trace point setting */
+ if (uap->value < 0 || uap->value >= TR_NFLAGS)
+ return (EINVAL);
+ *retval = traceflags[uap->value];
+ break;
+
+ case VTR_UALARM: /* set a real-time ualarm, less than 1 min */
+ if (uap->value <= 0 || uap->value > 60 * hz || nvualarm > 5)
+ return (EINVAL);
+ nvualarm++;
+ timeout(vdoualarm, (caddr_t)p->p_pid, uap->value);
+ break;
+
+ case VTR_STAMP:
+ trace(TR_STAMP, uap->value, p->p_pid);
+ break;
+ }
+ return (0);
+}
+
+vdoualarm(arg)
+ int arg;
+{
+ register struct proc *p;
+
+ p = pfind(arg);
+ if (p)
+ psignal(p, 16);
+ nvualarm--;
+}
+#endif
+
+#ifdef USER_LDT
+void
+set_user_ldt(struct pcb *pcb)
+{
+ gdt_segs[GUSERLDT_SEL].ssd_base = (unsigned)pcb->pcb_ldt;
+ gdt_segs[GUSERLDT_SEL].ssd_limit = (pcb->pcb_ldt_len * sizeof(union descriptor)) - 1;
+ ssdtosd(gdt_segs+GUSERLDT_SEL, gdt+GUSERLDT_SEL);
+ lldt(GSEL(GUSERLDT_SEL, SEL_KPL));
+ currentldt = GSEL(GUSERLDT_SEL, SEL_KPL);
+}
+
+struct i386_get_ldt_args {
+ int start;
+ union descriptor *desc;
+ int num;
+};
+
+int
+i386_get_ldt(p, args, retval)
+ struct proc *p;
+ char *args;
+ int *retval;
+{
+ int error = 0;
+ struct pcb *pcb = &p->p_addr->u_pcb;
+ int nldt, num;
+ union descriptor *lp;
+ int s;
+ struct i386_get_ldt_args ua, *uap;
+
+ if ((error = copyin(args, &ua, sizeof(struct i386_get_ldt_args))) < 0)
+ return(error);
+
+ uap = &ua;
+#ifdef DEBUG
+ printf("i386_get_ldt: start=%d num=%d descs=%x\n", uap->start, uap->num, uap->desc);
+#endif
+
+ if (uap->start < 0 || uap->num < 0)
+ return(EINVAL);
+
+ s = splhigh();
+
+ if (pcb->pcb_ldt) {
+ nldt = pcb->pcb_ldt_len;
+ num = min(uap->num, nldt);
+ lp = &((union descriptor *)(pcb->pcb_ldt))[uap->start];
+ } else {
+ nldt = sizeof(ldt)/sizeof(ldt[0]);
+ num = min(uap->num, nldt);
+ lp = &ldt[uap->start];
+ }
+ if (uap->start > nldt) {
+ splx(s);
+ return(EINVAL);
+ }
+
+ error = copyout(lp, uap->desc, num * sizeof(union descriptor));
+ if (!error)
+ *retval = num;
+
+ splx(s);
+ return(error);
+}
+
+struct i386_set_ldt_args {
+ int start;
+ union descriptor *desc;
+ int num;
+};
+
+int
+i386_set_ldt(p, args, retval)
+ struct proc *p;
+ char *args;
+ int *retval;
+{
+ int error = 0, i, n;
+ struct pcb *pcb = &p->p_addr->u_pcb;
+ union descriptor *lp;
+ int s;
+ struct i386_set_ldt_args ua, *uap;
+
+ if ((error = copyin(args, &ua, sizeof(struct i386_set_ldt_args))) < 0)
+ return(error);
+
+ uap = &ua;
+
+#ifdef DEBUG
+ printf("i386_set_ldt: start=%d num=%d descs=%x\n", uap->start, uap->num, uap->desc);
+#endif
+
+ if (uap->start < 0 || uap->num < 0)
+ return(EINVAL);
+
+ /* XXX Should be 8192 ! */
+ if (uap->start > 512 ||
+ (uap->start + uap->num) > 512)
+ return(EINVAL);
+
+ /* allocate user ldt */
+ if (!pcb->pcb_ldt) {
+ union descriptor *new_ldt =
+ (union descriptor *)kmem_alloc(kernel_map, 512*sizeof(union descriptor));
+ bzero(new_ldt, 512*sizeof(union descriptor));
+ bcopy(ldt, new_ldt, sizeof(ldt));
+ pcb->pcb_ldt = (caddr_t)new_ldt;
+ pcb->pcb_ldt_len = 512; /* XXX need to grow */
+#ifdef DEBUG
+ printf("i386_set_ldt(%d): new_ldt=%x\n", p->p_pid, new_ldt);
+#endif
+ }
+
+ /* Check descriptors for access violations */
+ for (i = 0, n = uap->start; i < uap->num; i++, n++) {
+ union descriptor desc, *dp;
+ dp = &uap->desc[i];
+ error = copyin(dp, &desc, sizeof(union descriptor));
+ if (error)
+ return(error);
+
+ /* Only user (ring-3) descriptors */
+ if (desc.sd.sd_dpl != SEL_UPL)
+ return(EACCES);
+
+ /* Must be "present" */
+ if (desc.sd.sd_p == 0)
+ return(EACCES);
+
+ switch (desc.sd.sd_type) {
+ case SDT_SYSNULL:
+ case SDT_SYS286CGT:
+ case SDT_SYS386CGT:
+ break;
+ case SDT_MEMRO:
+ case SDT_MEMROA:
+ case SDT_MEMRW:
+ case SDT_MEMRWA:
+ case SDT_MEMROD:
+ case SDT_MEMRODA:
+ case SDT_MEME:
+ case SDT_MEMEA:
+ case SDT_MEMER:
+ case SDT_MEMERA:
+ case SDT_MEMEC:
+ case SDT_MEMEAC:
+ case SDT_MEMERC:
+ case SDT_MEMERAC: {
+#if 0
+ unsigned long base = (desc.sd.sd_hibase << 24)&0xFF000000;
+ base |= (desc.sd.sd_lobase&0x00FFFFFF);
+ if (base >= KERNBASE)
+ return(EACCES);
+#endif
+ break;
+ }
+ default:
+ return(EACCES);
+ /*NOTREACHED*/
+ }
+ }
+
+ s = splhigh();
+
+ /* Fill in range */
+ for (i = 0, n = uap->start; i < uap->num && !error; i++, n++) {
+ union descriptor desc, *dp;
+ dp = &uap->desc[i];
+ lp = &((union descriptor *)(pcb->pcb_ldt))[n];
+#ifdef DEBUG
+ printf("i386_set_ldt(%d): ldtp=%x\n", p->p_pid, lp);
+#endif
+ error = copyin(dp, lp, sizeof(union descriptor));
+ }
+ if (!error) {
+ *retval = uap->start;
+/* need_resched(); */
+ }
+
+ splx(s);
+ return(error);
+}
+#endif /* USER_LDT */
+
+struct sysarch_args {
+ int op;
+ char *parms;
+};
+
+int
+sysarch(p, uap, retval)
+ struct proc *p;
+ register struct sysarch_args *uap;
+ int *retval;
+{
+ int error = 0;
+
+ switch(uap->op) {
+#ifdef USER_LDT
+ case I386_GET_LDT:
+ error = i386_get_ldt(p, uap->parms, retval);
+ break;
+
+ case I386_SET_LDT:
+ error = i386_set_ldt(p, uap->parms, retval);
+ break;
+#endif
+ default:
+ error = EINVAL;
+ break;
+ }
+ return(error);
+}
diff --git a/sys/amd64/amd64/trap.c b/sys/amd64/amd64/trap.c
new file mode 100644
index 0000000..7084791
--- /dev/null
+++ b/sys/amd64/amd64/trap.c
@@ -0,0 +1,692 @@
+/*-
+ * Copyright (C) 1994, David Greenman
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the University of Utah, and William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)trap.c 7.4 (Berkeley) 5/13/91
+ * $Id: trap.c,v 1.26 1994/06/11 05:13:33 davidg Exp $
+ */
+
+/*
+ * 386 Trap and System call handling
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/acct.h>
+#include <sys/kernel.h>
+#include <sys/syscall.h>
+#ifdef KTRACE
+#include <sys/ktrace.h>
+#endif
+
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+#include <vm/vm_map.h>
+#include <vm/vm_page.h>
+
+#include <machine/cpu.h>
+#include <machine/psl.h>
+#include <machine/reg.h>
+#include <machine/eflags.h>
+
+#include <machine/trap.h>
+
+#include "isa.h"
+#include "npx.h"
+#include "ddb.h"
+
+int trap_pfault __P((struct trapframe *, int));
+void trap_fatal __P((struct trapframe *));
+
+extern int grow(struct proc *,u_int);
+
+struct sysent sysent[];
+int nsysent;
+
+#define MAX_TRAP_MSG 27
+char *trap_msg[] = {
+ "reserved addressing fault", /* 0 T_RESADFLT */
+ "privileged instruction fault", /* 1 T_PRIVINFLT */
+ "reserved operand fault", /* 2 T_RESOPFLT */
+ "breakpoint instruction fault", /* 3 T_BPTFLT */
+ "", /* 4 unused */
+ "system call trap", /* 5 T_SYSCALL */
+ "arithmetic trap", /* 6 T_ARITHTRAP */
+ "system forced exception", /* 7 T_ASTFLT */
+ "segmentation (limit) fault", /* 8 T_SEGFLT */
+ "general protection fault", /* 9 T_PROTFLT */
+ "trace trap", /* 10 T_TRCTRAP */
+ "", /* 11 unused */
+ "page fault", /* 12 T_PAGEFLT */
+ "page table fault", /* 13 T_TABLEFLT */
+ "alignment fault", /* 14 T_ALIGNFLT */
+ "kernel stack pointer not valid", /* 15 T_KSPNOTVAL */
+ "bus error", /* 16 T_BUSERR */
+ "kernel debugger fault", /* 17 T_KDBTRAP */
+ "integer divide fault", /* 18 T_DIVIDE */
+ "non-maskable interrupt trap", /* 19 T_NMI */
+ "overflow trap", /* 20 T_OFLOW */
+ "FPU bounds check fault", /* 21 T_BOUND */
+ "FPU device not available", /* 22 T_DNA */
+ "double fault", /* 23 T_DOUBLEFLT */
+ "FPU operand fetch fault", /* 24 T_FPOPFLT */
+ "invalid TSS fault", /* 25 T_TSSFLT */
+ "segment not present fault", /* 26 T_SEGNPFLT */
+ "stack fault", /* 27 T_STKFLT */
+};
+
+static inline void
+userret(p, frame, oticks)
+ struct proc *p;
+ struct trapframe *frame;
+ u_quad_t oticks;
+{
+ int sig, s;
+
+ while (sig = CURSIG(p))
+ postsig(sig);
+ p->p_priority = p->p_usrpri;
+ if (want_resched) {
+ /*
+ * Since we are curproc, clock will normally just change
+ * our priority without moving us from one queue to another
+ * (since the running process is not on a queue.)
+ * If that happened after we setrunqueue ourselves but before we
+ * mi_switch()'ed, we might not be on the queue indicated by
+ * our priority.
+ */
+ s = splclock();
+ setrunqueue(p);
+ p->p_stats->p_ru.ru_nivcsw++;
+ mi_switch();
+ splx(s);
+ while (sig = CURSIG(p))
+ postsig(sig);
+ }
+ if (p->p_stats->p_prof.pr_scale) {
+ u_quad_t ticks = p->p_sticks - oticks;
+
+ if (ticks) {
+#ifdef PROFTIMER
+ extern int profscale;
+ addupc(frame->tf_eip, &p->p_stats->p_prof,
+ ticks * profscale);
+#else
+ addupc(frame->tf_eip, &p->p_stats->p_prof, ticks);
+#endif
+ }
+ }
+ curpriority = p->p_priority;
+}
+
+/*
+ * trap(frame):
+ * Exception, fault, and trap interface to the FreeBSD kernel.
+ * This common code is called from assembly language IDT gate entry
+ * routines that prepare a suitable stack frame, and restore this
+ * frame after the exception has been processed.
+ */
+
+/*ARGSUSED*/
+void
+trap(frame)
+ struct trapframe frame;
+{
+ struct proc *p = curproc;
+ u_quad_t sticks = 0;
+ int i = 0, ucode = 0, type, code, eva, fault_type;
+
+ frame.tf_eflags &= ~PSL_NT; /* clear nested trap XXX */
+ type = frame.tf_trapno;
+ code = frame.tf_err;
+
+ if (ISPL(frame.tf_cs) == SEL_UPL) {
+ /* user trap */
+
+ sticks = p->p_sticks;
+ p->p_md.md_regs = (int *)&frame;
+
+ switch (type) {
+ case T_RESADFLT: /* reserved addressing fault */
+ case T_PRIVINFLT: /* privileged instruction fault */
+ case T_RESOPFLT: /* reserved operand fault */
+ ucode = type;
+ i = SIGILL;
+ break;
+
+ case T_BPTFLT: /* bpt instruction fault */
+ case T_TRCTRAP: /* trace trap */
+ frame.tf_eflags &= ~PSL_T;
+ i = SIGTRAP;
+ break;
+
+ case T_ARITHTRAP: /* arithmetic trap */
+ ucode = code;
+ i = SIGFPE;
+ break;
+
+ case T_ASTFLT: /* Allow process switch */
+ astoff();
+ cnt.v_soft++;
+ if ((p->p_flag & P_OWEUPC) && p->p_stats->p_prof.pr_scale) {
+ addupc(frame.tf_eip, &p->p_stats->p_prof, 1);
+ p->p_flag &= ~P_OWEUPC;
+ }
+ goto out;
+
+ case T_PROTFLT: /* general protection fault */
+ case T_SEGNPFLT: /* segment not present fault */
+ case T_STKFLT: /* stack fault */
+ ucode = code + BUS_SEGM_FAULT ;
+ i = SIGBUS;
+ break;
+
+ case T_PAGEFLT: /* page fault */
+ i = trap_pfault(&frame, TRUE);
+ if (i == 0)
+ goto out;
+
+ ucode = T_PAGEFLT;
+ break;
+
+ case T_DIVIDE: /* integer divide fault */
+ ucode = FPE_INTDIV_TRAP;
+ i = SIGFPE;
+ break;
+
+#if NISA > 0
+ case T_NMI:
+#if NDDB > 0
+ /* NMI can be hooked up to a pushbutton for debugging */
+ printf ("NMI ... going to debugger\n");
+ if (kdb_trap (type, 0, &frame))
+ return;
+#endif
+ /* machine/parity/power fail/"kitchen sink" faults */
+ if (isa_nmi(code) == 0) return;
+ /* FALL THROUGH */
+#endif
+
+ case T_OFLOW: /* integer overflow fault */
+ ucode = FPE_INTOVF_TRAP;
+ i = SIGFPE;
+ break;
+
+ case T_BOUND: /* bounds check fault */
+ ucode = FPE_SUBRNG_TRAP;
+ i = SIGFPE;
+ break;
+
+ case T_DNA:
+#if NNPX > 0
+ /* if a transparent fault (due to context switch "late") */
+ if (npxdna())
+ return;
+#endif /* NNPX > 0 */
+
+#if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE)
+ i = math_emulate(&frame);
+ if (i == 0) return;
+#else /* MATH_EMULATE || GPL_MATH_EMULATE */
+ panic("trap: math emulation necessary!");
+#endif /* MATH_EMULATE || GPL_MATH_EMULATE */
+ ucode = FPE_FPU_NP_TRAP;
+ break;
+
+ case T_FPOPFLT: /* FPU operand fetch fault */
+ ucode = T_FPOPFLT;
+ i = SIGILL;
+ break;
+
+ default:
+ trap_fatal(&frame);
+ }
+ } else {
+ /* kernel trap */
+
+ switch (type) {
+ case T_PAGEFLT: /* page fault */
+ (void) trap_pfault(&frame, FALSE);
+ return;
+
+ case T_PROTFLT: /* general protection fault */
+ case T_SEGNPFLT: /* segment not present fault */
+ if (curpcb && curpcb->pcb_onfault) {
+ frame.tf_eip = (int)curpcb->pcb_onfault;
+ return;
+ }
+ break;
+
+#if NDDB > 0
+ case T_BPTFLT:
+ case T_TRCTRAP:
+ if (kdb_trap (type, 0, &frame))
+ return;
+ break;
+#else
+ case T_TRCTRAP: /* trace trap -- someone single stepping lcall's */
+ /* Q: how do we turn it on again? */
+ frame.tf_eflags &= ~PSL_T;
+ return;
+#endif
+
+#if NISA > 0
+ case T_NMI:
+#if NDDB > 0
+ /* NMI can be hooked up to a pushbutton for debugging */
+ printf ("NMI ... going to debugger\n");
+ if (kdb_trap (type, 0, &frame))
+ return;
+#endif
+ /* machine/parity/power fail/"kitchen sink" faults */
+ if (isa_nmi(code) == 0) return;
+ /* FALL THROUGH */
+#endif
+ }
+
+ trap_fatal(&frame);
+ }
+
+ trapsignal(p, i, ucode);
+
+#ifdef DIAGNOSTIC
+ eva = rcr2();
+ if (type <= MAX_TRAP_MSG) {
+ uprintf("fatal process exception: %s",
+ trap_msg[type]);
+ if ((type == T_PAGEFLT) || (type == T_PROTFLT))
+ uprintf(", fault VA = 0x%x", eva);
+ uprintf("\n");
+ }
+#endif
+
+out:
+ userret(p, &frame, sticks);
+}
+
+int
+trap_pfault(frame, usermode)
+ struct trapframe *frame;
+ int usermode;
+{
+ vm_offset_t va;
+ struct vmspace *vm;
+ vm_map_t map = 0;
+ int rv = 0, oldflags;
+ vm_prot_t ftype;
+ extern vm_map_t kernel_map;
+ int eva;
+ struct proc *p = curproc;
+
+ eva = rcr2();
+ va = trunc_page((vm_offset_t)eva);
+
+ /*
+ * Don't allow user-mode faults in kernel address space
+ */
+ if (usermode && (va >= KERNBASE)) {
+ goto nogo;
+ }
+
+ if ((p == 0) || (va >= KERNBASE)) {
+ vm = 0;
+ map = kernel_map;
+ } else {
+ vm = p->p_vmspace;
+ map = &vm->vm_map;
+ }
+
+ if (frame->tf_err & PGEX_W)
+ ftype = VM_PROT_READ | VM_PROT_WRITE;
+ else
+ ftype = VM_PROT_READ;
+
+ if (map != kernel_map) {
+ vm_offset_t pa;
+ vm_offset_t v = (vm_offset_t) vtopte(va);
+ vm_page_t ptepg;
+
+ /*
+ * Keep swapout from messing with us during this
+ * critical time.
+ */
+ ++p->p_lock;
+
+ /*
+ * Grow the stack if necessary
+ */
+ if ((caddr_t)va > vm->vm_maxsaddr
+ && (caddr_t)va < (caddr_t)USRSTACK) {
+ if (!grow(p, va)) {
+ rv = KERN_FAILURE;
+ --p->p_lock;
+ goto nogo;
+ }
+ }
+
+ /*
+ * Check if page table is mapped, if not,
+ * fault it first
+ */
+
+ /* Fault the pte only if needed: */
+ *(volatile char *)v += 0;
+
+ ptepg = (vm_page_t) pmap_pte_vm_page(vm_map_pmap(map), v);
+ if( ptepg->hold_count == 0)
+ ptepg->act_count += 3;
+ vm_page_hold(ptepg);
+
+ /* Fault in the user page: */
+ rv = vm_fault(map, va, ftype, FALSE);
+
+ vm_page_unhold(ptepg);
+
+ /*
+ * page table pages don't need to be kept if they
+ * are not held
+ */
+ if( ptepg->hold_count == 0 && ptepg->wire_count == 0) {
+ pmap_page_protect( VM_PAGE_TO_PHYS(ptepg),
+ VM_PROT_NONE);
+ vm_page_free(ptepg);
+ }
+
+ --p->p_lock;
+ } else {
+ /*
+ * Since we know that kernel virtual address addresses
+ * always have pte pages mapped, we just have to fault
+ * the page.
+ */
+ rv = vm_fault(map, va, ftype, FALSE);
+ }
+
+ if (rv == KERN_SUCCESS)
+ return (0);
+nogo:
+ if (!usermode) {
+ if (curpcb->pcb_onfault) {
+ frame->tf_eip = (int)curpcb->pcb_onfault;
+ return (0);
+ }
+ trap_fatal(frame);
+ }
+
+ /* kludge to pass faulting virtual address to sendsig */
+ frame->tf_err = eva;
+
+ return((rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV);
+}
+
+void
+trap_fatal(frame)
+ struct trapframe *frame;
+{
+ int code, type, eva;
+
+ code = frame->tf_err;
+ type = frame->tf_trapno;
+ eva = rcr2();
+
+ if (type <= MAX_TRAP_MSG)
+ printf("\n\nFatal trap %d: %s while in %s mode\n",
+ type, trap_msg[type],
+ ISPL(frame->tf_cs) == SEL_UPL ? "user" : "kernel");
+ if (type == T_PAGEFLT) {
+ printf("fault virtual address = 0x%x\n", eva);
+ printf("fault code = %s %s, %s\n",
+ code & PGEX_U ? "user" : "supervisor",
+ code & PGEX_W ? "write" : "read",
+ code & PGEX_P ? "protection violation" : "page not present");
+ }
+ printf("instruction pointer = 0x%x\n", frame->tf_eip);
+ printf("processor eflags = ");
+ if (frame->tf_eflags & EFL_TF)
+ printf("trace/trap, ");
+ if (frame->tf_eflags & EFL_IF)
+ printf("interrupt enabled, ");
+ if (frame->tf_eflags & EFL_NT)
+ printf("nested task, ");
+ if (frame->tf_eflags & EFL_RF)
+ printf("resume, ");
+ if (frame->tf_eflags & EFL_VM)
+ printf("vm86, ");
+ printf("IOPL = %d\n", (frame->tf_eflags & EFL_IOPL) >> 12);
+ printf("current process = ");
+ if (curproc) {
+ printf("%d (%s)\n",
+ curproc->p_pid, curproc->p_comm ?
+ curproc->p_comm : "");
+ } else {
+ printf("Idle\n");
+ }
+ printf("interrupt mask = ");
+ if ((cpl & net_imask) == net_imask)
+ printf("net ");
+ if ((cpl & tty_imask) == tty_imask)
+ printf("tty ");
+ if ((cpl & bio_imask) == bio_imask)
+ printf("bio ");
+ if (cpl == 0)
+ printf("none");
+ printf("\n");
+
+#ifdef KDB
+ if (kdb_trap(&psl))
+ return;
+#endif
+#if NDDB > 0
+ if (kdb_trap (type, 0, frame))
+ return;
+#endif
+ if (type <= MAX_TRAP_MSG)
+ panic(trap_msg[type]);
+ else
+ panic("unknown/reserved trap");
+}
+
+/*
+ * Compensate for 386 brain damage (missing URKR).
+ * This is a little simpler than the pagefault handler in trap() because
+ * it the page tables have already been faulted in and high addresses
+ * are thrown out early for other reasons.
+ */
+int trapwrite(addr)
+ unsigned addr;
+{
+ struct proc *p;
+ vm_offset_t va, v;
+ struct vmspace *vm;
+ int oldflags;
+ int rv;
+
+ va = trunc_page((vm_offset_t)addr);
+ /*
+ * XXX - MAX is END. Changed > to >= for temp. fix.
+ */
+ if (va >= VM_MAXUSER_ADDRESS)
+ return (1);
+
+ p = curproc;
+ vm = p->p_vmspace;
+
+ ++p->p_lock;
+
+ if ((caddr_t)va >= vm->vm_maxsaddr
+ && (caddr_t)va < (caddr_t)USRSTACK) {
+ if (!grow(p, va)) {
+ --p->p_lock;
+ return (1);
+ }
+ }
+
+ v = trunc_page(vtopte(va));
+
+ /*
+ * wire the pte page
+ */
+ if (va < USRSTACK) {
+ vm_map_pageable(&vm->vm_map, v, round_page(v+1), FALSE);
+ }
+
+ /*
+ * fault the data page
+ */
+ rv = vm_fault(&vm->vm_map, va, VM_PROT_READ|VM_PROT_WRITE, FALSE);
+
+ /*
+ * unwire the pte page
+ */
+ if (va < USRSTACK) {
+ vm_map_pageable(&vm->vm_map, v, round_page(v+1), TRUE);
+ }
+
+ --p->p_lock;
+
+ if (rv != KERN_SUCCESS)
+ return 1;
+
+ return (0);
+}
+
+/*
+ * syscall(frame):
+ * System call request from POSIX system call gate interface to kernel.
+ * Like trap(), argument is call by reference.
+ */
+/*ARGSUSED*/
+void
+syscall(frame)
+ struct trapframe frame;
+{
+ caddr_t params;
+ int i;
+ struct sysent *callp;
+ struct proc *p = curproc;
+ u_quad_t sticks;
+ int error, opc;
+ int args[8], rval[2];
+ u_int code;
+
+ sticks = p->p_sticks;
+ if (ISPL(frame.tf_cs) != SEL_UPL)
+ panic("syscall");
+
+ code = frame.tf_eax;
+ p->p_md.md_regs = (int *)&frame;
+ params = (caddr_t)frame.tf_esp + sizeof (int) ;
+
+ /*
+ * Reconstruct pc, assuming lcall $X,y is 7 bytes, as it is always.
+ */
+ opc = frame.tf_eip - 7;
+ /*
+ * Need to check if this is a 32 bit or 64 bit syscall.
+ */
+ if (code == SYS_syscall) {
+ /*
+ * Code is first argument, followed by actual args.
+ */
+ code = fuword(params);
+ params += sizeof (int);
+ } else if (code == SYS___syscall) {
+ /*
+ * Like syscall, but code is a quad, so as to maintain
+ * quad alignment for the rest of the arguments.
+ */
+ code = fuword(params + _QUAD_LOWWORD * sizeof(int));
+ params += sizeof(quad_t);
+ }
+
+ if (code >= nsysent)
+ callp = &sysent[0];
+ else
+ callp = &sysent[code];
+
+ if ((i = callp->sy_narg * sizeof (int)) &&
+ (error = copyin(params, (caddr_t)args, (u_int)i))) {
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_SYSCALL))
+ ktrsyscall(p->p_tracep, code, callp->sy_narg, args);
+#endif
+ goto bad;
+ }
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_SYSCALL))
+ ktrsyscall(p->p_tracep, code, callp->sy_narg, args);
+#endif
+ rval[0] = 0;
+ rval[1] = frame.tf_edx;
+
+ error = (*callp->sy_call)(p, args, rval);
+
+ switch (error) {
+
+ case 0:
+ /*
+ * Reinitialize proc pointer `p' as it may be different
+ * if this is a child returning from fork syscall.
+ */
+ p = curproc;
+ frame.tf_eax = rval[0];
+ frame.tf_edx = rval[1];
+ frame.tf_eflags &= ~PSL_C; /* carry bit */
+ break;
+
+ case ERESTART:
+ frame.tf_eip = opc;
+ break;
+
+ case EJUSTRETURN:
+ break;
+
+ default:
+ bad:
+ frame.tf_eax = error;
+ frame.tf_eflags |= PSL_C; /* carry bit */
+ break;
+ }
+
+ userret(p, &frame, sticks);
+
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_SYSRET))
+ ktrsysret(p->p_tracep, code, error, rval[0]);
+#endif
+}
diff --git a/sys/amd64/amd64/tsc.c b/sys/amd64/amd64/tsc.c
new file mode 100644
index 0000000..e40079a
--- /dev/null
+++ b/sys/amd64/amd64/tsc.c
@@ -0,0 +1,442 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz and Don Ahn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)clock.c 7.2 (Berkeley) 5/12/91
+ * $Id: clock.c,v 1.6 1994/02/06 22:48:13 davidg Exp $
+ */
+
+/*
+ * Primitive clock interrupt routines.
+ */
+#include "param.h"
+#include "systm.h"
+#include "time.h"
+#include "kernel.h"
+#include "machine/segments.h"
+#include "machine/frame.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/rtc.h"
+#include "i386/isa/timerreg.h"
+#include <machine/cpu.h>
+
+/* X-tals being what they are, it's nice to be able to fudge this one... */
+/* Note, the name changed here from XTALSPEED to TIMER_FREQ rgrimes 4/26/93 */
+#ifndef TIMER_FREQ
+#define TIMER_FREQ 1193182 /* XXX - should be in isa.h */
+#endif
+#define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x))
+
+void hardclock();
+static int beeping;
+int timer0_divisor = TIMER_DIV(100); /* XXX should be hz */
+u_int timer0_prescale;
+static char timer0_state = 0, timer2_state = 0;
+static char timer0_reprogram = 0;
+static void (*timer_func)() = hardclock;
+static void (*new_function)();
+static u_int new_rate;
+static u_int hardclock_divisor;
+
+
+void
+clkintr(frame)
+ struct clockframe frame;
+{
+ hardclock(&frame);
+}
+
+#if 0
+void
+timerintr(struct clockframe frame)
+{
+ timer_func(&frame);
+ switch (timer0_state) {
+ case 0:
+ break;
+ case 1:
+ if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) {
+ hardclock(&frame);
+ timer0_prescale = 0;
+ }
+ break;
+ case 2:
+ disable_intr();
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+ outb(TIMER_CNTR0, TIMER_DIV(new_rate)%256);
+ outb(TIMER_CNTR0, TIMER_DIV(new_rate)/256);
+ enable_intr();
+ timer0_divisor = TIMER_DIV(new_rate);
+ timer0_prescale = 0;
+ timer_func = new_function;
+ timer0_state = 1;
+ break;
+ case 3:
+ if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) {
+ hardclock(&frame);
+ disable_intr();
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+ outb(TIMER_CNTR0, TIMER_DIV(hz)%256);
+ outb(TIMER_CNTR0, TIMER_DIV(hz)/256);
+ enable_intr();
+ timer0_divisor = TIMER_DIV(hz);
+ timer0_prescale = 0;
+ timer_func = hardclock;;
+ timer0_state = 0;
+ }
+ break;
+ }
+}
+
+#endif
+
+int
+acquire_timer0(int rate, void (*function)() )
+{
+ if (timer0_state || !function)
+ return -1;
+
+ new_function = function;
+ new_rate = rate;
+ timer0_state = 2;
+ return 0;
+}
+
+
+int
+acquire_timer2(int mode)
+{
+ if (timer2_state)
+ return -1;
+ timer2_state = 1;
+ outb(TIMER_MODE, TIMER_SEL2 | (mode &0x3f));
+ return 0;
+}
+
+
+int
+release_timer0()
+{
+ if (!timer0_state)
+ return -1;
+ timer0_state = 3;
+ return 0;
+}
+
+
+int
+release_timer2()
+{
+ if (!timer2_state)
+ return -1;
+ timer2_state = 0;
+ outb(TIMER_MODE, TIMER_SEL2|TIMER_SQWAVE|TIMER_16BIT);
+ return 0;
+}
+
+
+static int
+getit()
+{
+ int high, low;
+
+ disable_intr();
+ /* select timer0 and latch counter value */
+ outb(TIMER_MODE, TIMER_SEL0);
+ low = inb(TIMER_CNTR0);
+ high = inb(TIMER_CNTR0);
+ enable_intr();
+ return ((high << 8) | low);
+}
+
+
+/*
+ * Wait "n" microseconds.
+ * Relies on timer 1 counting down from (TIMER_FREQ / hz)
+ * Note: timer had better have been programmed before this is first used!
+ */
+void
+DELAY(int n)
+{
+ int counter_limit, prev_tick, tick, ticks_left, sec, usec;
+
+#ifdef DELAYDEBUG
+ int getit_calls = 1;
+ int n1;
+ static int state = 0;
+
+ if (state == 0) {
+ state = 1;
+ for (n1 = 1; n1 <= 10000000; n1 *= 10)
+ DELAY(n1);
+ state = 2;
+ }
+ if (state == 1)
+ printf("DELAY(%d)...", n);
+#endif
+ /*
+ * Read the counter first, so that the rest of the setup overhead is
+ * counted. Guess the initial overhead is 20 usec (on most systems it
+ * takes about 1.5 usec for each of the i/o's in getit(). The loop
+ * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The
+ * multiplications and divisions to scale the count take a while).
+ */
+ prev_tick = getit(0, 0);
+ n -= 20;
+ /*
+ * Calculate (n * (TIMER_FREQ / 1e6)) without using floating point
+ * and without any avoidable overflows.
+ */
+ sec = n / 1000000;
+ usec = n - sec * 1000000;
+ ticks_left = sec * TIMER_FREQ
+ + usec * (TIMER_FREQ / 1000000)
+ + usec * ((TIMER_FREQ % 1000000) / 1000) / 1000
+ + usec * (TIMER_FREQ % 1000) / 1000000;
+
+ while (ticks_left > 0) {
+ tick = getit(0, 0);
+#ifdef DELAYDEBUG
+ ++getit_calls;
+#endif
+ if (tick > prev_tick)
+ ticks_left -= prev_tick - (tick - timer0_divisor);
+ else
+ ticks_left -= prev_tick - tick;
+ prev_tick = tick;
+ }
+#ifdef DELAYDEBUG
+ if (state == 1)
+ printf(" %d calls to getit() at %d usec each\n",
+ getit_calls, (n + 5) / getit_calls);
+#endif
+}
+
+
+static void
+sysbeepstop()
+{
+ outb(IO_PPI, inb(IO_PPI)&0xFC); /* disable counter2 output to speaker */
+ release_timer2();
+ beeping = 0;
+}
+
+
+int
+sysbeep(int pitch, int period)
+{
+
+ if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT))
+ return -1;
+ disable_intr();
+ outb(TIMER_CNTR2, pitch);
+ outb(TIMER_CNTR2, (pitch>>8));
+ enable_intr();
+ if (!beeping) {
+ outb(IO_PPI, inb(IO_PPI) | 3); /* enable counter2 output to speaker */
+ beeping = period;
+ timeout(sysbeepstop, 0, period);
+ }
+ return 0;
+}
+
+
+void
+startrtclock()
+{
+ int s;
+
+ /* initialize 8253 clock */
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+
+ /* Correct rounding will buy us a better precision in timekeeping */
+ outb (IO_TIMER1, TIMER_DIV(hz)%256);
+ outb (IO_TIMER1, TIMER_DIV(hz)/256);
+ timer0_divisor = hardclock_divisor = TIMER_DIV(hz);
+
+ /* initialize brain-dead battery powered clock */
+ outb (IO_RTC, RTC_STATUSA);
+ outb (IO_RTC+1, 0x26);
+ outb (IO_RTC, RTC_STATUSB);
+ outb (IO_RTC+1, 2);
+
+ outb (IO_RTC, RTC_DIAG);
+ if (s = inb (IO_RTC+1))
+ printf("RTC BIOS diagnostic error %b\n", s, RTCDG_BITS);
+}
+
+
+/* convert 2 digit BCD number */
+int
+bcd(int i)
+{
+ return ((i/16)*10 + (i%16));
+}
+
+
+/* convert years to seconds (from 1970) */
+unsigned long
+ytos(int y)
+{
+ int i;
+ unsigned long ret;
+
+ ret = 0;
+ for(i = 1970; i < y; i++) {
+ if (i % 4) ret += 365*24*60*60;
+ else ret += 366*24*60*60;
+ }
+ return ret;
+}
+
+
+/* convert months to seconds */
+unsigned long
+mtos(int m, int leap)
+{
+ int i;
+ unsigned long ret;
+
+ ret = 0;
+ for(i=1; i<m; i++) {
+ switch(i){
+ case 1: case 3: case 5: case 7: case 8: case 10: case 12:
+ ret += 31*24*60*60; break;
+ case 4: case 6: case 9: case 11:
+ ret += 30*24*60*60; break;
+ case 2:
+ if (leap) ret += 29*24*60*60;
+ else ret += 28*24*60*60;
+ }
+ }
+ return ret;
+}
+
+
+/*
+ * Initialize the time of day register, based on the time base which is, e.g.
+ * from a filesystem.
+ */
+void
+inittodr(time_t base)
+{
+ unsigned long sec;
+ int leap, day_week, t, yd;
+ int sa,s;
+
+ /* do we have a realtime clock present? (otherwise we loop below) */
+ sa = rtcin(RTC_STATUSA);
+ if (sa == 0xff || sa == 0) return;
+
+ /* ready for a read? */
+ while ((sa&RTCSA_TUP) == RTCSA_TUP)
+ sa = rtcin(RTC_STATUSA);
+
+ sec = bcd(rtcin(RTC_YEAR)) + 1900;
+ if (sec < 1970)
+ sec += 100;
+
+ leap = !(sec % 4); sec = ytos(sec); /* year */
+ yd = mtos(bcd(rtcin(RTC_MONTH)),leap); sec+=yd; /* month */
+ t = (bcd(rtcin(RTC_DAY))-1) * 24*60*60; sec+=t; yd+=t; /* date */
+ day_week = rtcin(RTC_WDAY); /* day */
+ sec += bcd(rtcin(RTC_HRS)) * 60*60; /* hour */
+ sec += bcd(rtcin(RTC_MIN)) * 60; /* minutes */
+ sec += bcd(rtcin(RTC_SEC)); /* seconds */
+ sec += tz.tz_minuteswest * 60;
+ time.tv_sec = sec;
+}
+
+
+#ifdef garbage
+/*
+ * Initialze the time of day register, based on the time base which is, e.g.
+ * from a filesystem.
+ */
+test_inittodr(time_t base)
+{
+
+ outb(IO_RTC,9); /* year */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,8); /* month */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,7); /* day */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,4); /* hour */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,2); /* minutes */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,0); /* seconds */
+ printf("%d\n",bcd(inb(IO_RTC+1)));
+
+ time.tv_sec = base;
+}
+#endif
+
+/*
+ * Wire clock interrupt in.
+ */
+#define V(s) __CONCAT(V, s)
+extern void V(clk)();
+
+
+void
+enablertclock()
+{
+ setidt(ICU_OFFSET+0, &V(clk), SDT_SYS386IGT, SEL_KPL);
+ INTREN(IRQ0);
+}
+
+
+/*
+ * Delay for some number of milliseconds.
+ */
+void
+spinwait(int millisecs)
+{
+ DELAY(1000 * millisecs);
+}
+
+void
+cpu_initclocks()
+{
+ startrtclock();
+ enablertclock();
+}
+
+void
+setstatclockrate(int newhz)
+{
+}
diff --git a/sys/amd64/amd64/vm_machdep.c b/sys/amd64/amd64/vm_machdep.c
new file mode 100644
index 0000000..a7c4e59
--- /dev/null
+++ b/sys/amd64/amd64/vm_machdep.c
@@ -0,0 +1,1246 @@
+/*-
+ * Copyright (c) 1982, 1986 The Regents of the University of California.
+ * Copyright (c) 1989, 1990 William Jolitz
+ * Copyright (c) 1994 John Dyson
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department, and William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)vm_machdep.c 7.3 (Berkeley) 5/13/91
+ * Utah $Hdr: vm_machdep.c 1.16.1.1 89/06/23$
+ * $Id: vm_machdep.c,v 1.20 1994/04/20 07:06:20 davidg Exp $
+ */
+
+#include "npx.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/user.h>
+
+#include <machine/cpu.h>
+
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+
+#define b_cylin b_resid
+
+caddr_t bouncememory;
+vm_offset_t bouncepa, bouncepaend;
+int bouncepages, bpwait;
+vm_map_t io_map;
+int bmwait, bmfreeing;
+
+#define BITS_IN_UNSIGNED (8*sizeof(unsigned))
+int bounceallocarraysize;
+unsigned *bounceallocarray;
+int bouncefree;
+
+#define SIXTEENMEG (4096*4096)
+#define MAXBKVA 512
+int maxbkva=MAXBKVA*NBPG;
+
+/* special list that can be used at interrupt time for eventual kva free */
+struct kvasfree {
+ vm_offset_t addr;
+ vm_offset_t size;
+} kvaf[MAXBKVA];
+
+int kvasfreecnt;
+
+vm_offset_t vm_bounce_kva();
+/*
+ * get bounce buffer pages (count physically contiguous)
+ * (only 1 inplemented now)
+ */
+vm_offset_t
+vm_bounce_page_find(count)
+ int count;
+{
+ int bit;
+ int s,i;
+
+ if (count != 1)
+ panic("vm_bounce_page_find -- no support for > 1 page yet!!!");
+
+ s = splbio();
+retry:
+ for (i = 0; i < bounceallocarraysize; i++) {
+ if (bounceallocarray[i] != 0xffffffff) {
+ if (bit = ffs(~bounceallocarray[i])) {
+ bounceallocarray[i] |= 1 << (bit - 1) ;
+ bouncefree -= count;
+ splx(s);
+ return bouncepa + (i * BITS_IN_UNSIGNED + (bit - 1)) * NBPG;
+ }
+ }
+ }
+ bpwait = 1;
+ tsleep((caddr_t) &bounceallocarray, PRIBIO, "bncwai", 0);
+ goto retry;
+}
+
+void
+vm_bounce_kva_free(addr, size, now)
+ vm_offset_t addr;
+ vm_offset_t size;
+ int now;
+{
+ int s = splbio();
+ kvaf[kvasfreecnt].addr = addr;
+ kvaf[kvasfreecnt++].size = size;
+ if( now) {
+ /*
+ * this will do wakeups
+ */
+ vm_bounce_kva(0,0);
+ } else {
+ if (bmwait) {
+ /*
+ * if anyone is waiting on the bounce-map, then wakeup
+ */
+ wakeup((caddr_t) io_map);
+ bmwait = 0;
+ }
+ }
+ splx(s);
+}
+
+/*
+ * free count bounce buffer pages
+ */
+void
+vm_bounce_page_free(pa, count)
+ vm_offset_t pa;
+ int count;
+{
+ int allocindex;
+ int index;
+ int bit;
+
+ if (count != 1)
+ panic("vm_bounce_page_free -- no support for > 1 page yet!!!\n");
+
+ index = (pa - bouncepa) / NBPG;
+
+ if ((index < 0) || (index >= bouncepages))
+ panic("vm_bounce_page_free -- bad index\n");
+
+ allocindex = index / BITS_IN_UNSIGNED;
+ bit = index % BITS_IN_UNSIGNED;
+
+ bounceallocarray[allocindex] &= ~(1 << bit);
+
+ bouncefree += count;
+ if (bpwait) {
+ bpwait = 0;
+ wakeup((caddr_t) &bounceallocarray);
+ }
+}
+
+/*
+ * allocate count bounce buffer kva pages
+ */
+vm_offset_t
+vm_bounce_kva(count, waitok)
+ int count;
+ int waitok;
+{
+ int tofree;
+ int i;
+ int startfree;
+ vm_offset_t kva = 0;
+ int s = splbio();
+ int size = count;
+ startfree = 0;
+more:
+ if (!bmfreeing && (tofree = kvasfreecnt)) {
+ bmfreeing = 1;
+ for (i = startfree; i < kvasfreecnt; i++) {
+ /*
+ * if we have a kva of the right size, no sense
+ * in freeing/reallocating...
+ * might affect fragmentation short term, but
+ * as long as the amount of io_map is
+ * significantly more than the maximum transfer
+ * size, I don't think that it is a problem.
+ */
+ pmap_remove(kernel_pmap,
+ kvaf[i].addr, kvaf[i].addr + kvaf[i].size);
+ if( size && !kva && kvaf[i].size == size) {
+ kva = kvaf[i].addr;
+ } else {
+ kmem_free_wakeup(io_map, kvaf[i].addr,
+ kvaf[i].size);
+ }
+ }
+ if (kvasfreecnt != tofree) {
+ startfree = i;
+ bmfreeing = 0;
+ goto more;
+ }
+ kvasfreecnt = 0;
+ bmfreeing = 0;
+ }
+
+ if( size == 0) {
+ splx(s);
+ return NULL;
+ }
+
+ if (!kva && !(kva = kmem_alloc_pageable(io_map, size))) {
+ if( !waitok) {
+ splx(s);
+ return NULL;
+ }
+ bmwait = 1;
+ tsleep((caddr_t) io_map, PRIBIO, "bmwait", 0);
+ goto more;
+ }
+ splx(s);
+
+ return kva;
+}
+
+/*
+ * same as vm_bounce_kva -- but really allocate
+ */
+vm_offset_t
+vm_bounce_kva_alloc(count)
+int count;
+{
+ int i;
+ vm_offset_t kva;
+ vm_offset_t pa;
+ if( bouncepages == 0) {
+ kva = (vm_offset_t) malloc(count*NBPG, M_TEMP, M_WAITOK);
+ return kva;
+ }
+ kva = vm_bounce_kva(count, 1);
+ for(i=0;i<count;i++) {
+ pa = vm_bounce_page_find(1);
+ pmap_kenter(kva + i * NBPG, pa);
+ }
+ pmap_update();
+ return kva;
+}
+
+/*
+ * same as vm_bounce_kva_free -- but really free
+ */
+void
+vm_bounce_kva_alloc_free(kva, count)
+ vm_offset_t kva;
+ int count;
+{
+ int i;
+ vm_offset_t pa;
+ if( bouncepages == 0) {
+ free((caddr_t) kva, M_TEMP);
+ return;
+ }
+ for(i = 0; i < count; i++) {
+ pa = pmap_kextract(kva + i * NBPG);
+ vm_bounce_page_free(pa, 1);
+ }
+ vm_bounce_kva_free(kva, count);
+}
+
+/*
+ * do the things necessary to the struct buf to implement
+ * bounce buffers... inserted before the disk sort
+ */
+void
+vm_bounce_alloc(bp)
+ struct buf *bp;
+{
+ int countvmpg;
+ vm_offset_t vastart, vaend;
+ vm_offset_t vapstart, vapend;
+ vm_offset_t va, kva;
+ vm_offset_t pa;
+ int dobounceflag = 0;
+ int bounceindex;
+ int i;
+ int s;
+
+ if (bouncepages == 0)
+ return;
+
+ if (bp->b_bufsize < bp->b_bcount) {
+ printf("vm_bounce_alloc: b_bufsize(%d) < b_bcount(%d) !!!!\n",
+ bp->b_bufsize, bp->b_bcount);
+ bp->b_bufsize = bp->b_bcount;
+ }
+
+ vastart = (vm_offset_t) bp->b_data;
+ vaend = (vm_offset_t) bp->b_data + bp->b_bufsize;
+
+ vapstart = i386_trunc_page(vastart);
+ vapend = i386_round_page(vaend);
+ countvmpg = (vapend - vapstart) / NBPG;
+
+/*
+ * if any page is above 16MB, then go into bounce-buffer mode
+ */
+ va = vapstart;
+ for (i = 0; i < countvmpg; i++) {
+ pa = pmap_kextract(va);
+ if (pa >= SIXTEENMEG)
+ ++dobounceflag;
+ va += NBPG;
+ }
+ if (dobounceflag == 0)
+ return;
+
+ if (bouncepages < dobounceflag)
+ panic("Not enough bounce buffers!!!");
+
+/*
+ * allocate a replacement kva for b_addr
+ */
+ kva = vm_bounce_kva(countvmpg*NBPG, 1);
+ va = vapstart;
+ for (i = 0; i < countvmpg; i++) {
+ pa = pmap_kextract(va);
+ if (pa >= SIXTEENMEG) {
+ /*
+ * allocate a replacement page
+ */
+ vm_offset_t bpa = vm_bounce_page_find(1);
+ pmap_kenter(kva + (NBPG * i), bpa);
+ /*
+ * if we are writing, the copy the data into the page
+ */
+ if ((bp->b_flags & B_READ) == 0) {
+ pmap_update();
+ bcopy((caddr_t) va, (caddr_t) kva + (NBPG * i), NBPG);
+ }
+ } else {
+ /*
+ * use original page
+ */
+ pmap_kenter(kva + (NBPG * i), pa);
+ }
+ va += NBPG;
+ }
+ pmap_update();
+
+/*
+ * flag the buffer as being bounced
+ */
+ bp->b_flags |= B_BOUNCE;
+/*
+ * save the original buffer kva
+ */
+ bp->b_savekva = bp->b_data;
+/*
+ * put our new kva into the buffer (offset by original offset)
+ */
+ bp->b_data = (caddr_t) (((vm_offset_t) kva) |
+ ((vm_offset_t) bp->b_savekva & (NBPG - 1)));
+ return;
+}
+
+/*
+ * hook into biodone to free bounce buffer
+ */
+void
+vm_bounce_free(bp)
+ struct buf *bp;
+{
+ int i;
+ vm_offset_t origkva, bouncekva;
+ vm_offset_t vastart, vaend;
+ vm_offset_t vapstart, vapend;
+ int countbounce = 0;
+ vm_offset_t firstbouncepa = 0;
+ int firstbounceindex;
+ int countvmpg;
+ vm_offset_t bcount;
+ int s;
+
+/*
+ * if this isn't a bounced buffer, then just return
+ */
+ if ((bp->b_flags & B_BOUNCE) == 0)
+ return;
+
+ origkva = (vm_offset_t) bp->b_savekva;
+ bouncekva = (vm_offset_t) bp->b_data;
+
+ vastart = bouncekva;
+ vaend = bouncekva + bp->b_bufsize;
+ bcount = bp->b_bufsize;
+
+ vapstart = i386_trunc_page(vastart);
+ vapend = i386_round_page(vaend);
+
+ countvmpg = (vapend - vapstart) / NBPG;
+
+/*
+ * check every page in the kva space for b_addr
+ */
+ for (i = 0; i < countvmpg; i++) {
+ vm_offset_t mybouncepa;
+ vm_offset_t copycount;
+
+ copycount = i386_round_page(bouncekva + 1) - bouncekva;
+ mybouncepa = pmap_kextract(i386_trunc_page(bouncekva));
+
+/*
+ * if this is a bounced pa, then process as one
+ */
+ if ((mybouncepa >= bouncepa) && (mybouncepa < bouncepaend)) {
+ if (copycount > bcount)
+ copycount = bcount;
+/*
+ * if this is a read, then copy from bounce buffer into original buffer
+ */
+ if (bp->b_flags & B_READ)
+ bcopy((caddr_t) bouncekva, (caddr_t) origkva, copycount);
+/*
+ * free the bounce allocation
+ */
+ vm_bounce_page_free(i386_trunc_page(mybouncepa), 1);
+ }
+
+ origkva += copycount;
+ bouncekva += copycount;
+ bcount -= copycount;
+ }
+
+/*
+ * add the old kva into the "to free" list
+ */
+ bouncekva = i386_trunc_page((vm_offset_t) bp->b_data);
+ vm_bounce_kva_free( bouncekva, countvmpg*NBPG, 0);
+ bp->b_data = bp->b_savekva;
+ bp->b_savekva = 0;
+ bp->b_flags &= ~B_BOUNCE;
+
+ return;
+}
+
+/*
+ * init the bounce buffer system
+ */
+void
+vm_bounce_init()
+{
+ vm_offset_t minaddr, maxaddr;
+
+ kvasfreecnt = 0;
+
+ if (bouncepages == 0)
+ return;
+
+ bounceallocarraysize = (bouncepages + BITS_IN_UNSIGNED - 1) / BITS_IN_UNSIGNED;
+ bounceallocarray = malloc(bounceallocarraysize * sizeof(unsigned), M_TEMP, M_NOWAIT);
+
+ if (!bounceallocarray)
+ panic("Cannot allocate bounce resource array\n");
+
+ bzero(bounceallocarray, bounceallocarraysize * sizeof(long));
+
+
+ bouncepa = pmap_kextract((vm_offset_t) bouncememory);
+ bouncepaend = bouncepa + bouncepages * NBPG;
+ bouncefree = bouncepages;
+}
+
+
+#ifdef BROKEN_IN_44
+static void
+cldiskvamerge( kvanew, orig1, orig1cnt, orig2, orig2cnt)
+ vm_offset_t kvanew;
+ vm_offset_t orig1, orig1cnt;
+ vm_offset_t orig2, orig2cnt;
+{
+ int i;
+ vm_offset_t pa;
+/*
+ * enter the transfer physical addresses into the new kva
+ */
+ for(i=0;i<orig1cnt;i++) {
+ vm_offset_t pa;
+ pa = pmap_kextract((caddr_t) orig1 + i * PAGE_SIZE);
+ pmap_kenter(kvanew + i * PAGE_SIZE, pa);
+ }
+
+ for(i=0;i<orig2cnt;i++) {
+ vm_offset_t pa;
+ pa = pmap_kextract((caddr_t) orig2 + i * PAGE_SIZE);
+ pmap_kenter(kvanew + (i + orig1cnt) * PAGE_SIZE, pa);
+ }
+ pmap_update();
+}
+
+void
+cldisksort(struct buf *dp, struct buf *bp, vm_offset_t maxio)
+{
+ register struct buf *ap, *newbp;
+ int i, trycount=0;
+ vm_offset_t orig1pages, orig2pages;
+ vm_offset_t orig1begin, orig2begin;
+ vm_offset_t kvanew, kvaorig;
+
+ if( bp->b_bcount < MAXCLSTATS*PAGE_SIZE)
+ ++rqstats[bp->b_bcount/PAGE_SIZE];
+ /*
+ * If nothing on the activity queue, then
+ * we become the only thing.
+ */
+ ap = dp->b_actf;
+ if(ap == NULL) {
+ dp->b_actf = bp;
+ dp->b_actl = bp;
+ bp->av_forw = NULL;
+ return;
+ }
+
+ /*
+ * If we lie after the first (currently active)
+ * request, then we must locate the second request list
+ * and add ourselves to it.
+ */
+
+ if (bp->b_pblkno < ap->b_pblkno) {
+ while (ap->av_forw) {
+ /*
+ * Check for an ``inversion'' in the
+ * normally ascending block numbers,
+ * indicating the start of the second request list.
+ */
+ if (ap->av_forw->b_pblkno < ap->b_pblkno) {
+ /*
+ * Search the second request list
+ * for the first request at a larger
+ * block number. We go before that;
+ * if there is no such request, we go at end.
+ */
+ do {
+ if (bp->b_pblkno < ap->av_forw->b_pblkno)
+ goto insert;
+ ap = ap->av_forw;
+ } while (ap->av_forw);
+ goto insert; /* after last */
+ }
+ ap = ap->av_forw;
+ }
+ /*
+ * No inversions... we will go after the last, and
+ * be the first request in the second request list.
+ */
+ goto insert;
+ }
+ /*
+ * Request is at/after the current request...
+ * sort in the first request list.
+ */
+ while (ap->av_forw) {
+ /*
+ * We want to go after the current request
+ * if there is an inversion after it (i.e. it is
+ * the end of the first request list), or if
+ * the next request is a larger block than our request.
+ */
+ if (ap->av_forw->b_pblkno < ap->b_pblkno ||
+ bp->b_pblkno < ap->av_forw->b_pblkno )
+ goto insert;
+ ap = ap->av_forw;
+ }
+
+insert:
+
+ /*
+ * read clustering with new read-ahead disk drives hurts mostly, so
+ * we don't bother...
+ */
+ if( bp->b_flags & B_READ)
+ goto nocluster;
+ /*
+ * we currently only cluster I/O transfers that are at page-aligned
+ * kvas and transfers that are multiples of page lengths.
+ */
+ if ((bp->b_flags & B_BAD) == 0 &&
+ ((bp->b_bcount & PAGE_MASK) == 0) &&
+ (((vm_offset_t) bp->b_un.b_addr & PAGE_MASK) == 0)) {
+ if( maxio > MAXCLSTATS*PAGE_SIZE)
+ maxio = MAXCLSTATS*PAGE_SIZE;
+ /*
+ * merge with previous?
+ * conditions:
+ * 1) We reside physically immediately after the previous block.
+ * 2) The previous block is not first on the device queue because
+ * such a block might be active.
+ * 3) The mode of the two I/Os is identical.
+ * 4) The previous kva is page aligned and the previous transfer
+ * is a multiple of a page in length.
+ * 5) And the total I/O size would be below the maximum.
+ */
+ if( (ap->b_pblkno + (ap->b_bcount / DEV_BSIZE) == bp->b_pblkno) &&
+ (dp->b_actf != ap) &&
+ ((ap->b_flags & ~B_CLUSTER) == bp->b_flags) &&
+ ((ap->b_flags & B_BAD) == 0) &&
+ ((ap->b_bcount & PAGE_MASK) == 0) &&
+ (((vm_offset_t) ap->b_un.b_addr & PAGE_MASK) == 0) &&
+ (ap->b_bcount + bp->b_bcount < maxio)) {
+
+ orig1begin = (vm_offset_t) ap->b_un.b_addr;
+ orig1pages = ap->b_bcount / PAGE_SIZE;
+
+ orig2begin = (vm_offset_t) bp->b_un.b_addr;
+ orig2pages = bp->b_bcount / PAGE_SIZE;
+ /*
+ * see if we can allocate a kva, if we cannot, the don't
+ * cluster.
+ */
+ kvanew = vm_bounce_kva( PAGE_SIZE * (orig1pages + orig2pages), 0);
+ if( !kvanew) {
+ goto nocluster;
+ }
+
+
+ if( (ap->b_flags & B_CLUSTER) == 0) {
+
+ /*
+ * get a physical buf pointer
+ */
+ newbp = (struct buf *)trypbuf();
+ if( !newbp) {
+ vm_bounce_kva_free( kvanew, PAGE_SIZE * (orig1pages + orig2pages), 1);
+ goto nocluster;
+ }
+
+ cldiskvamerge( kvanew, orig1begin, orig1pages, orig2begin, orig2pages);
+
+ /*
+ * build the new bp to be handed off to the device
+ */
+
+ --clstats[ap->b_bcount/PAGE_SIZE];
+ *newbp = *ap;
+ newbp->b_flags |= B_CLUSTER;
+ newbp->b_un.b_addr = (caddr_t) kvanew;
+ newbp->b_bcount += bp->b_bcount;
+ newbp->b_bufsize = newbp->b_bcount;
+ newbp->b_clusterf = ap;
+ newbp->b_clusterl = bp;
+ ++clstats[newbp->b_bcount/PAGE_SIZE];
+
+ /*
+ * enter the new bp onto the device queue
+ */
+ if( ap->av_forw)
+ ap->av_forw->av_back = newbp;
+ else
+ dp->b_actl = newbp;
+
+ if( dp->b_actf != ap )
+ ap->av_back->av_forw = newbp;
+ else
+ dp->b_actf = newbp;
+
+ /*
+ * enter the previous bps onto the cluster queue
+ */
+ ap->av_forw = bp;
+ bp->av_back = ap;
+
+ ap->av_back = NULL;
+ bp->av_forw = NULL;
+
+ } else {
+ vm_offset_t addr;
+
+ cldiskvamerge( kvanew, orig1begin, orig1pages, orig2begin, orig2pages);
+ /*
+ * free the old kva
+ */
+ vm_bounce_kva_free( orig1begin, ap->b_bufsize, 0);
+ --clstats[ap->b_bcount/PAGE_SIZE];
+
+ ap->b_un.b_addr = (caddr_t) kvanew;
+
+ ap->b_clusterl->av_forw = bp;
+ bp->av_forw = NULL;
+ bp->av_back = ap->b_clusterl;
+ ap->b_clusterl = bp;
+
+ ap->b_bcount += bp->b_bcount;
+ ap->b_bufsize = ap->b_bcount;
+ ++clstats[ap->b_bcount/PAGE_SIZE];
+ }
+ return;
+ /*
+ * merge with next?
+ * conditions:
+ * 1) We reside physically before the next block.
+ * 3) The mode of the two I/Os is identical.
+ * 4) The next kva is page aligned and the next transfer
+ * is a multiple of a page in length.
+ * 5) And the total I/O size would be below the maximum.
+ */
+ } else if( ap->av_forw &&
+ (bp->b_pblkno + (bp->b_bcount / DEV_BSIZE) == ap->av_forw->b_pblkno) &&
+ (bp->b_flags == (ap->av_forw->b_flags & ~B_CLUSTER)) &&
+ ((ap->av_forw->b_flags & B_BAD) == 0) &&
+ ((ap->av_forw->b_bcount & PAGE_MASK) == 0) &&
+ (((vm_offset_t) ap->av_forw->b_un.b_addr & PAGE_MASK) == 0) &&
+ (ap->av_forw->b_bcount + bp->b_bcount < maxio)) {
+
+ orig1begin = (vm_offset_t) bp->b_un.b_addr;
+ orig1pages = bp->b_bcount / PAGE_SIZE;
+
+ orig2begin = (vm_offset_t) ap->av_forw->b_un.b_addr;
+ orig2pages = ap->av_forw->b_bcount / PAGE_SIZE;
+
+ /*
+ * see if we can allocate a kva, if we cannot, the don't
+ * cluster.
+ */
+ kvanew = vm_bounce_kva( PAGE_SIZE * (orig1pages + orig2pages), 0);
+ if( !kvanew) {
+ goto nocluster;
+ }
+
+ /*
+ * if next isn't a cluster we need to create one
+ */
+ if( (ap->av_forw->b_flags & B_CLUSTER) == 0) {
+
+ /*
+ * get a physical buf pointer
+ */
+ newbp = (struct buf *)trypbuf();
+ if( !newbp) {
+ vm_bounce_kva_free( kvanew, PAGE_SIZE * (orig1pages + orig2pages), 1);
+ goto nocluster;
+ }
+
+ cldiskvamerge( kvanew, orig1begin, orig1pages, orig2begin, orig2pages);
+ ap = ap->av_forw;
+ --clstats[ap->b_bcount/PAGE_SIZE];
+ *newbp = *ap;
+ newbp->b_flags |= B_CLUSTER;
+ newbp->b_un.b_addr = (caddr_t) kvanew;
+ newbp->b_blkno = bp->b_blkno;
+ newbp->b_pblkno = bp->b_pblkno;
+ newbp->b_bcount += bp->b_bcount;
+ newbp->b_bufsize = newbp->b_bcount;
+ newbp->b_clusterf = bp;
+ newbp->b_clusterl = ap;
+ ++clstats[newbp->b_bcount/PAGE_SIZE];
+
+ if( ap->av_forw)
+ ap->av_forw->av_back = newbp;
+ else
+ dp->b_actl = newbp;
+
+ if( dp->b_actf != ap )
+ ap->av_back->av_forw = newbp;
+ else
+ dp->b_actf = newbp;
+
+ bp->av_forw = ap;
+ ap->av_back = bp;
+
+ bp->av_back = NULL;
+ ap->av_forw = NULL;
+ } else {
+ vm_offset_t addr;
+
+ cldiskvamerge( kvanew, orig1begin, orig1pages, orig2begin, orig2pages);
+ ap = ap->av_forw;
+ vm_bounce_kva_free( orig2begin, ap->b_bufsize, 0);
+
+ ap->b_un.b_addr = (caddr_t) kvanew;
+ bp->av_forw = ap->b_clusterf;
+ ap->b_clusterf->av_back = bp;
+ ap->b_clusterf = bp;
+ bp->av_back = NULL;
+ --clstats[ap->b_bcount/PAGE_SIZE];
+
+ ap->b_blkno = bp->b_blkno;
+ ap->b_pblkno = bp->b_pblkno;
+ ap->b_bcount += bp->b_bcount;
+ ap->b_bufsize = ap->b_bcount;
+ ++clstats[ap->b_bcount/PAGE_SIZE];
+
+ }
+ return;
+ }
+ }
+ /*
+ * don't merge
+ */
+nocluster:
+ ++clstats[bp->b_bcount/PAGE_SIZE];
+ bp->av_forw = ap->av_forw;
+ if( bp->av_forw)
+ bp->av_forw->av_back = bp;
+ else
+ dp->b_actl = bp;
+
+ ap->av_forw = bp;
+ bp->av_back = ap;
+}
+#endif
+
+/*
+ * quick version of vm_fault
+ */
+
+void
+vm_fault_quick( v, prot)
+ vm_offset_t v;
+ int prot;
+{
+ if( (cpu_class == CPUCLASS_386) &&
+ (prot & VM_PROT_WRITE))
+ vm_fault(&curproc->p_vmspace->vm_map, v,
+ VM_PROT_READ|VM_PROT_WRITE, FALSE);
+ else if( prot & VM_PROT_WRITE)
+ *(volatile char *)v += 0;
+ else
+ *(volatile char *)v;
+}
+
+
+/*
+ * Finish a fork operation, with process p2 nearly set up.
+ * Copy and update the kernel stack and pcb, making the child
+ * ready to run, and marking it so that it can return differently
+ * than the parent. Returns 1 in the child process, 0 in the parent.
+ * We currently double-map the user area so that the stack is at the same
+ * address in each process; in the future we will probably relocate
+ * the frame pointers on the stack after copying.
+ */
+int
+cpu_fork(p1, p2)
+ register struct proc *p1, *p2;
+{
+ register struct user *up = p2->p_addr;
+ int foo, offset, addr, i;
+ extern char kstack[];
+ extern int mvesp();
+
+ /*
+ * Copy pcb and stack from proc p1 to p2.
+ * We do this as cheaply as possible, copying only the active
+ * part of the stack. The stack and pcb need to agree;
+ * this is tricky, as the final pcb is constructed by savectx,
+ * but its frame isn't yet on the stack when the stack is copied.
+ * swtch compensates for this when the child eventually runs.
+ * This should be done differently, with a single call
+ * that copies and updates the pcb+stack,
+ * replacing the bcopy and savectx.
+ */
+ p2->p_addr->u_pcb = p1->p_addr->u_pcb;
+ offset = mvesp() - (int)kstack;
+ bcopy((caddr_t)kstack + offset, (caddr_t)p2->p_addr + offset,
+ (unsigned) ctob(UPAGES) - offset);
+ p2->p_md.md_regs = p1->p_md.md_regs;
+
+ /*
+ * Wire top of address space of child to it's kstack.
+ * First, fault in a page of pte's to map it.
+ */
+#if 0
+ addr = trunc_page((u_int)vtopte(kstack));
+ vm_map_pageable(&p2->p_vmspace->vm_map, addr, addr+NBPG, FALSE);
+ for (i=0; i < UPAGES; i++)
+ pmap_enter(&p2->p_vmspace->vm_pmap, kstack+i*NBPG,
+ pmap_extract(kernel_pmap, ((int)p2->p_addr)+i*NBPG),
+ /*
+ * The user area has to be mapped writable because
+ * it contains the kernel stack (when CR0_WP is on
+ * on a 486 there is no user-read/kernel-write
+ * mode). It is protected from user mode access
+ * by the segment limits.
+ */
+ VM_PROT_READ|VM_PROT_WRITE, TRUE);
+#endif
+ pmap_activate(&p2->p_vmspace->vm_pmap, &up->u_pcb);
+
+ /*
+ *
+ * Arrange for a non-local goto when the new process
+ * is started, to resume here, returning nonzero from setjmp.
+ */
+ if (savectx(up, 1)) {
+ /*
+ * Return 1 in child.
+ */
+ return (1);
+ }
+ return (0);
+}
+
+#ifdef notyet
+/*
+ * cpu_exit is called as the last action during exit.
+ *
+ * We change to an inactive address space and a "safe" stack,
+ * passing thru an argument to the new stack. Now, safely isolated
+ * from the resources we're shedding, we release the address space
+ * and any remaining machine-dependent resources, including the
+ * memory for the user structure and kernel stack.
+ *
+ * Next, we assign a dummy context to be written over by swtch,
+ * calling it to send this process off to oblivion.
+ * [The nullpcb allows us to minimize cost in mi_switch() by not having
+ * a special case].
+ */
+struct proc *swtch_to_inactive();
+volatile void
+cpu_exit(p)
+ register struct proc *p;
+{
+ static struct pcb nullpcb; /* pcb to overwrite on last swtch */
+
+#if NNPX > 0
+ npxexit(p);
+#endif /* NNPX */
+
+ /* move to inactive space and stack, passing arg accross */
+ p = swtch_to_inactive(p);
+
+ /* drop per-process resources */
+ vmspace_free(p->p_vmspace);
+ kmem_free(kernel_map, (vm_offset_t)p->p_addr, ctob(UPAGES));
+
+ p->p_addr = (struct user *) &nullpcb;
+ mi_switch();
+ /* NOTREACHED */
+}
+#else
+void
+cpu_exit(p)
+ register struct proc *p;
+{
+
+#if NNPX > 0
+ npxexit(p);
+#endif /* NNPX */
+ curproc = p;
+ mi_switch();
+ /*
+ * This is to shutup the compiler, and if swtch() failed I suppose
+ * this would be a good thing. This keeps gcc happy because panic
+ * is a volatile void function as well.
+ */
+ panic("cpu_exit");
+}
+
+void
+cpu_wait(p) struct proc *p; {
+/* extern vm_map_t upages_map; */
+ extern char kstack[];
+
+ /* drop per-process resources */
+ pmap_remove(vm_map_pmap(kernel_map), (vm_offset_t) p->p_addr,
+ ((vm_offset_t) p->p_addr) + ctob(UPAGES));
+ kmem_free(kernel_map, (vm_offset_t)p->p_addr, ctob(UPAGES));
+ vmspace_free(p->p_vmspace);
+}
+#endif
+
+/*
+ * Dump the machine specific header information at the start of a core dump.
+ */
+int
+cpu_coredump(p, vp, cred)
+ struct proc *p;
+ struct vnode *vp;
+ struct ucred *cred;
+{
+
+ return (vn_rdwr(UIO_WRITE, vp, (caddr_t) p->p_addr, ctob(UPAGES),
+ (off_t)0, UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, cred, (int *)NULL,
+ p));
+}
+
+/*
+ * Set a red zone in the kernel stack after the u. area.
+ */
+void
+setredzone(pte, vaddr)
+ u_short *pte;
+ caddr_t vaddr;
+{
+/* eventually do this by setting up an expand-down stack segment
+ for ss0: selector, allowing stack access down to top of u.
+ this means though that protection violations need to be handled
+ thru a double fault exception that must do an integral task
+ switch to a known good context, within which a dump can be
+ taken. a sensible scheme might be to save the initial context
+ used by sched (that has physical memory mapped 1:1 at bottom)
+ and take the dump while still in mapped mode */
+}
+
+/*
+ * Move pages from one kernel virtual address to another.
+ * Both addresses are assumed to reside in the Sysmap,
+ * and size must be a multiple of CLSIZE.
+ */
+
+/*
+ * Move pages from one kernel virtual address to another.
+ * Both addresses are assumed to reside in the Sysmap,
+ * and size must be a multiple of CLSIZE.
+ */
+
+void
+pagemove(from, to, size)
+ register caddr_t from, to;
+ int size;
+{
+ register vm_offset_t pa;
+
+ if (size & CLOFSET)
+ panic("pagemove");
+ while (size > 0) {
+ pa = pmap_kextract((vm_offset_t)from);
+ if (pa == 0)
+ panic("pagemove 2");
+ if (pmap_kextract((vm_offset_t)to) != 0)
+ panic("pagemove 3");
+ pmap_remove(kernel_pmap,
+ (vm_offset_t)from, (vm_offset_t)from + PAGE_SIZE);
+ pmap_kenter( (vm_offset_t)to, pa);
+ from += PAGE_SIZE;
+ to += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ pmap_update();
+}
+
+/*
+ * Convert kernel VA to physical address
+ */
+u_long
+kvtop(void *addr)
+{
+ vm_offset_t va;
+
+ va = pmap_kextract((vm_offset_t)addr);
+ if (va == 0)
+ panic("kvtop: zero page frame");
+ return((int)va);
+}
+
+extern vm_map_t phys_map;
+
+/*
+ * Map an IO request into kernel virtual address space.
+ *
+ * All requests are (re)mapped into kernel VA space.
+ * Notice that we use b_bufsize for the size of the buffer
+ * to be mapped. b_bcount might be modified by the driver.
+ */
+void
+vmapbuf(bp)
+ register struct buf *bp;
+{
+ register int npf;
+ register caddr_t addr;
+ int off;
+ vm_offset_t kva;
+ vm_offset_t pa, lastv, v;
+
+ if ((bp->b_flags & B_PHYS) == 0)
+ panic("vmapbuf");
+
+ lastv = 0;
+ for (addr = (caddr_t)trunc_page(bp->b_data);
+ addr < bp->b_data + bp->b_bufsize;
+ addr += PAGE_SIZE) {
+
+/*
+ * make sure that the pde is valid and held
+ */
+ v = trunc_page(((vm_offset_t)vtopte(addr)));
+ if (v != lastv) {
+ vm_fault_quick(v, VM_PROT_READ);
+ pa = pmap_extract(&curproc->p_vmspace->vm_pmap, v);
+ vm_page_hold(PHYS_TO_VM_PAGE(pa));
+ lastv = v;
+ }
+
+/*
+ * do the vm_fault if needed, do the copy-on-write thing when
+ * reading stuff off device into memory.
+ */
+ vm_fault_quick(addr,
+ (bp->b_flags&B_READ)?(VM_PROT_READ|VM_PROT_WRITE):VM_PROT_READ);
+ pa = pmap_extract(&curproc->p_vmspace->vm_pmap, (vm_offset_t) addr);
+/*
+ * hold the data page
+ */
+ vm_page_hold(PHYS_TO_VM_PAGE(pa));
+ }
+
+ addr = bp->b_saveaddr = bp->b_un.b_addr;
+ off = (int)addr & PGOFSET;
+ npf = btoc(round_page(bp->b_bufsize + off));
+ kva = kmem_alloc_wait(phys_map, ctob(npf));
+ bp->b_un.b_addr = (caddr_t) (kva + off);
+ while (npf--) {
+ pa = pmap_extract(&curproc->p_vmspace->vm_pmap, (vm_offset_t)addr);
+ if (pa == 0)
+ panic("vmapbuf: null page frame");
+ pmap_kenter(kva, trunc_page(pa));
+ addr += PAGE_SIZE;
+ kva += PAGE_SIZE;
+ }
+ pmap_update();
+}
+
+/*
+ * Free the io map PTEs associated with this IO operation.
+ * We also invalidate the TLB entries and restore the original b_addr.
+ */
+void
+vunmapbuf(bp)
+ register struct buf *bp;
+{
+ register int npf;
+ register caddr_t addr = bp->b_un.b_addr;
+ vm_offset_t kva,va,v,lastv,pa;
+
+ if ((bp->b_flags & B_PHYS) == 0)
+ panic("vunmapbuf");
+ npf = btoc(round_page(bp->b_bufsize + ((int)addr & PGOFSET)));
+ kva = (vm_offset_t)((int)addr & ~PGOFSET);
+ kmem_free_wakeup(phys_map, kva, ctob(npf));
+ bp->b_un.b_addr = bp->b_saveaddr;
+ bp->b_saveaddr = NULL;
+
+
+/*
+ * unhold the pde, and data pages
+ */
+ lastv = 0;
+ for (addr = (caddr_t)trunc_page(bp->b_data);
+ addr < bp->b_data + bp->b_bufsize;
+ addr += NBPG) {
+
+ /*
+ * release the data page
+ */
+ pa = pmap_extract(&curproc->p_vmspace->vm_pmap, (vm_offset_t) addr);
+ vm_page_unhold(PHYS_TO_VM_PAGE(pa));
+
+ /*
+ * and unhold the page table
+ */
+ v = trunc_page(((vm_offset_t)vtopte(addr)));
+ if (v != lastv) {
+ pa = pmap_extract(&curproc->p_vmspace->vm_pmap, v);
+ vm_page_unhold(PHYS_TO_VM_PAGE(pa));
+ lastv = v;
+ }
+ }
+}
+
+/*
+ * Force reset the processor by invalidating the entire address space!
+ */
+void
+cpu_reset() {
+
+ /* force a shutdown by unmapping entire address space ! */
+ bzero((caddr_t) PTD, NBPG);
+
+ /* "good night, sweet prince .... <THUNK!>" */
+ tlbflush();
+ /* NOTREACHED */
+ while(1);
+}
+
+/*
+ * Grow the user stack to allow for 'sp'. This version grows the stack in
+ * chunks of SGROWSIZ.
+ */
+int
+grow(p, sp)
+ struct proc *p;
+ u_int sp;
+{
+ unsigned int nss;
+ caddr_t v;
+ struct vmspace *vm = p->p_vmspace;
+
+ if ((caddr_t)sp <= vm->vm_maxsaddr || (unsigned)sp >= (unsigned)USRSTACK)
+ return (1);
+
+ nss = roundup(USRSTACK - (unsigned)sp, PAGE_SIZE);
+
+ if (nss > p->p_rlimit[RLIMIT_STACK].rlim_cur)
+ return (0);
+
+ if (vm->vm_ssize && roundup(vm->vm_ssize << PAGE_SHIFT,
+ SGROWSIZ) < nss) {
+ int grow_amount;
+ /*
+ * If necessary, grow the VM that the stack occupies
+ * to allow for the rlimit. This allows us to not have
+ * to allocate all of the VM up-front in execve (which
+ * is expensive).
+ * Grow the VM by the amount requested rounded up to
+ * the nearest SGROWSIZ to provide for some hysteresis.
+ */
+ grow_amount = roundup((nss - (vm->vm_ssize << PAGE_SHIFT)), SGROWSIZ);
+ v = (char *)USRSTACK - roundup(vm->vm_ssize << PAGE_SHIFT,
+ SGROWSIZ) - grow_amount;
+ /*
+ * If there isn't enough room to extend by SGROWSIZ, then
+ * just extend to the maximum size
+ */
+ if (v < vm->vm_maxsaddr) {
+ v = vm->vm_maxsaddr;
+ grow_amount = MAXSSIZ - (vm->vm_ssize << PAGE_SHIFT);
+ }
+ if (vm_allocate(&vm->vm_map, (vm_offset_t *)&v,
+ grow_amount, FALSE) != KERN_SUCCESS) {
+ return (0);
+ }
+ vm->vm_ssize += grow_amount >> PAGE_SHIFT;
+ }
+
+ return (1);
+}
diff --git a/sys/amd64/include/asmacros.h b/sys/amd64/include/asmacros.h
new file mode 100644
index 0000000..4af0b97
--- /dev/null
+++ b/sys/amd64/include/asmacros.h
@@ -0,0 +1,49 @@
+#define ALIGN_DATA .align 2 /* 4 byte alignment, zero filled */
+#define ALIGN_TEXT .align 2,0x90 /* 4-byte alignment, nop filled */
+#define SUPERALIGN_TEXT .align 4,0x90 /* 16-byte alignment (better for 486), nop filled */
+
+#define GEN_ENTRY(name) ALIGN_TEXT; .globl name; name:
+#define NON_GPROF_ENTRY(name) GEN_ENTRY(_/**/name)
+
+/* These three are place holders for future changes to the profiling code */
+#define MCOUNT_LABEL(name)
+#define MEXITCOUNT
+#define FAKE_MCOUNT(caller)
+
+#ifdef GPROF
+/*
+ * ALTENTRY() must be before a corresponding ENTRY() so that it can jump
+ * over the mcounting.
+ */
+#define ALTENTRY(name) GEN_ENTRY(_/**/name); MCOUNT; jmp 2f
+#define ENTRY(name) GEN_ENTRY(_/**/name); MCOUNT; 2:
+/*
+ * The call to mcount supports the usual (bad) conventions. We allocate
+ * some data and pass a pointer to it although the FreeBSD doesn't use
+ * the data. We set up a frame before calling mcount because that is
+ * the standard convention although it makes work for both mcount and
+ * callers.
+ */
+#define MCOUNT .data; ALIGN_DATA; 1:; .long 0; .text; \
+ pushl %ebp; movl %esp,%ebp; \
+ movl $1b,%eax; call mcount; popl %ebp
+#else
+/*
+ * ALTENTRY() has to align because it is before a corresponding ENTRY().
+ * ENTRY() has to align to because there may be no ALTENTRY() before it.
+ * If there is a previous ALTENTRY() then the alignment code is empty.
+ */
+#define ALTENTRY(name) GEN_ENTRY(_/**/name)
+#define ENTRY(name) GEN_ENTRY(_/**/name)
+#define MCOUNT
+
+#endif
+
+#ifdef DUMMY_NOPS /* this will break some older machines */
+#define FASTER_NOP
+#define NOP
+#else
+#define FASTER_NOP pushl %eax ; inb $0x84,%al ; popl %eax
+#define NOP pushl %eax ; inb $0x84,%al ; inb $0x84,%al ; popl %eax
+#endif
+
diff --git a/sys/amd64/include/cpu.h b/sys/amd64/include/cpu.h
new file mode 100644
index 0000000..2216d71
--- /dev/null
+++ b/sys/amd64/include/cpu.h
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)cpu.h 5.4 (Berkeley) 5/9/91
+ * $Id: cpu.h,v 1.4 1993/11/07 17:42:46 wollman Exp $
+ */
+
+#ifndef _MACHINE_CPU_H_
+#define _MACHINE_CPU_H_ 1
+
+/*
+ * Definitions unique to i386 cpu support.
+ */
+#include "machine/frame.h"
+#include "machine/segments.h"
+#include <machine/spl.h>
+
+/*
+ * definitions of cpu-dependent requirements
+ * referenced in generic code
+ */
+#undef COPY_SIGCODE /* don't copy sigcode above user stack in exec */
+
+#define cpu_exec(p) /* nothing */
+#define cpu_swapin(p) /* nothing */
+#define cpu_setstack(p, ap) (p)->p_md.md_regs = ap
+#define cpu_set_init_frame(p, fp) (p)->p_md.md_regs = fp
+
+#define CLKF_USERMODE(framep) (ISPL((framep)->cf_cs) == SEL_UPL)
+#define CLKF_INTR(framep) (0)
+#define CLKF_BASEPRI(framep) (((framep)->cf_ppl & ~SWI_AST_MASK) == 0)
+#define CLKF_PC(framep) ((framep)->cf_eip)
+
+#define resettodr() /* no todr to set */
+
+/*
+ * Preempt the current process if in interrupt from user mode,
+ * or after the current trap/syscall if in system mode.
+ */
+#define need_resched() { want_resched = 1; aston(); }
+
+/*
+ * Give a profiling tick to the current process from the softclock
+ * interrupt. On tahoe, request an ast to send us through trap(),
+ * marking the proc as needing a profiling tick.
+ */
+#define need_proftick(p) { (p)->p_flag |= P_OWEUPC; aston(); }
+
+/*
+ * Notify the current process (p) that it has a signal pending,
+ * process as soon as possible.
+ */
+#define signotify(p) aston()
+
+#define aston() setsoftast()
+#define astoff()
+
+/*
+ * pull in #defines for kinds of processors
+ */
+#include "machine/cputypes.h"
+
+struct cpu_nameclass {
+ char *cpu_name;
+ int cpu_class;
+};
+
+/*
+ * CTL_MACHDEP definitions.
+ */
+#define CPU_CONSDEV 1 /* dev_t: console terminal device */
+#define CPU_MAXID 2 /* number of valid machdep ids */
+
+#define CTL_MACHDEP_NAMES { \
+ { 0, 0 }, \
+ { "console_device", CTLTYPE_STRUCT }, \
+}
+
+#ifdef KERNEL
+extern int want_resched; /* resched was called */
+
+extern int cpu;
+extern int cpu_class;
+extern struct cpu_nameclass i386_cpus[];
+#endif
+#endif /* _MACHINE_CPU_H_ */
diff --git a/sys/amd64/include/cpufunc.h b/sys/amd64/include/cpufunc.h
new file mode 100644
index 0000000..df871cb
--- /dev/null
+++ b/sys/amd64/include/cpufunc.h
@@ -0,0 +1,108 @@
+/*
+ * Functions to provide access to special i386 instructions.
+ * XXX - bezillions more are defined in locore.s but are not declared anywhere.
+ *
+ * $Id: cpufunc.h,v 1.9 1994/01/31 23:48:23 davidg Exp $
+ */
+
+#ifndef _MACHINE_CPUFUNC_H_
+#define _MACHINE_CPUFUNC_H_ 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include "machine/spl.h"
+
+#ifdef __GNUC__
+
+static inline int bdb(void)
+{
+ extern int bdb_exists;
+
+ if (!bdb_exists)
+ return (0);
+ __asm("int $3");
+ return (1);
+}
+
+static inline void
+disable_intr(void)
+{
+ __asm __volatile("cli");
+}
+
+static inline void
+enable_intr(void)
+{
+ __asm __volatile("sti");
+}
+
+/*
+ * This roundabout method of returning a u_char helps stop gcc-1.40 from
+ * generating unnecessary movzbl's.
+ */
+#define inb(port) ((u_char) u_int_inb(port))
+
+static inline u_int
+u_int_inb(u_int port)
+{
+ u_char data;
+ /*
+ * We use %%dx and not %1 here because i/o is done at %dx and not at
+ * %edx, while gcc-2.2.2 generates inferior code (movw instead of movl)
+ * if we tell it to load (u_short) port.
+ */
+ __asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port));
+ return data;
+}
+
+static inline void
+outb(u_int port, u_char data)
+{
+ register u_char al asm("ax");
+
+ al = data; /* help gcc-1.40's register allocator */
+ __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port));
+}
+
+static inline void
+tlbflush()
+{
+ __asm __volatile("movl %%cr3, %%eax; movl %%eax, %%cr3" : : : "ax");
+}
+
+static inline u_long
+rcr2()
+{
+ u_long data;
+ __asm __volatile("movl %%cr2,%%eax" : "=a" (data));
+ return data;
+}
+
+#else /* not __GNUC__ */
+extern void insque __P((void *, void *));
+extern void remque __P((void *));
+
+int bdb __P((void));
+void disable_intr __P((void));
+void enable_intr __P((void));
+u_char inb __P((u_int port));
+void outb __P((u_int port, u_int data)); /* XXX - incompat */
+
+#endif /* __GNUC__ */
+
+void load_cr0 __P((u_int cr0));
+u_int rcr0 __P((void));
+void load_cr3(u_long);
+u_long rcr3(void);
+
+void setidt __P((int, void (*)(), int, int));
+extern u_long kvtop(void *);
+extern void outw(int /*u_short*/, int /*u_short*/); /* XXX inline!*/
+extern void outsb(int /*u_short*/, void *, size_t);
+extern void outsw(int /*u_short*/, void *, size_t);
+extern void insw(int /*u_short*/, void *, size_t);
+extern void fillw(int /*u_short*/, void *, size_t);
+extern void filli(int, void *, size_t);
+
+#endif /* _MACHINE_CPUFUNC_H_ */
diff --git a/sys/amd64/include/cputypes.h b/sys/amd64/include/cputypes.h
new file mode 100644
index 0000000..c85fe19
--- /dev/null
+++ b/sys/amd64/include/cputypes.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1993 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: cputypes.h,v 1.1 1993/10/08 13:40:54 rgrimes Exp $
+ */
+
+#ifndef _MACHINE_CPUTYPES_H_
+#define _MACHINE_CPUTYPES_H_ 1
+
+/*
+ * Classes of Processor
+ */
+
+#define CPUCLASS_286 0
+#define CPUCLASS_386 1
+#define CPUCLASS_486 2
+#define CPUCLASS_586 3
+
+/*
+ * Kinds of Processor
+ */
+
+#define CPU_286 0 /* Intel 80286 */
+#define CPU_386SX 1 /* Intel 80386SX */
+#define CPU_386 2 /* Intel 80386DX */
+#define CPU_486SX 3 /* Intel 80486SX */
+#define CPU_486 4 /* Intel 80486DX */
+#define CPU_586 5 /* Intel P.....m (I hate lawyers; it's TM) */
+
+#endif /* _MACHINE_CPUTYPES_H_ */
diff --git a/sys/amd64/include/db_machdep.h b/sys/amd64/include/db_machdep.h
new file mode 100644
index 0000000..a3f4064
--- /dev/null
+++ b/sys/amd64/include/db_machdep.h
@@ -0,0 +1,120 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * $Id: db_machdep.h,v 1.2 1993/10/16 14:39:10 rgrimes Exp $
+ */
+
+#ifndef _I386_DB_MACHDEP_H_
+#define _I386_DB_MACHDEP_H_
+
+/*
+ * Machine-dependent defines for new kernel debugger.
+ */
+
+
+/* #include <mach/i386/vm_types.h> */
+/* #include <mach/i386/vm_param.h> */
+#include <vm/vm_prot.h>
+#include <vm/vm_param.h>
+#include <vm/vm_inherit.h>
+#include <vm/lock.h>
+/* #include <i386/thread.h> */ /* for thread_status */
+#include <machine/frame.h> /* for struct trapframe */
+/* #include <i386/eflags.h> */
+#include <machine/eflags.h> /* from Mach... */
+/* #include <i386/trap.h> */
+#include <machine/trap.h>
+
+#define i386_saved_state trapframe
+/* end of mangling */
+
+typedef vm_offset_t db_addr_t; /* address - unsigned */
+typedef int db_expr_t; /* expression - signed */
+
+typedef struct i386_saved_state db_regs_t;
+extern db_regs_t ddb_regs; /* register state */
+#define DDB_REGS (&ddb_regs)
+
+#define PC_REGS(regs) ((db_addr_t)(regs)->tf_eip)
+
+#define BKPT_INST 0xcc /* breakpoint instruction */
+#define BKPT_SIZE (1) /* size of breakpoint inst */
+#define BKPT_SET(inst) (BKPT_INST)
+
+#define FIXUP_PC_AFTER_BREAK ddb_regs.tf_eip -= 1;
+
+#define db_clear_single_step(regs) ((regs)->tf_eflags &= ~EFL_TF)
+#define db_set_single_step(regs) ((regs)->tf_eflags |= EFL_TF)
+
+/* #define IS_BREAKPOINT_TRAP(type, code) ((type) == T_INT3) */
+/* #define IS_WATCHPOINT_TRAP(type, code) ((type) == T_WATCHPOINT) */
+/* using the 386bsd values, rather than the Mach ones: */
+#define IS_BREAKPOINT_TRAP(type, code) ((type) == T_BPTFLT)
+#define IS_WATCHPOINT_TRAP(type, code) ((type) == T_KDBTRAP)
+
+#define I_CALL 0xe8
+#define I_CALLI 0xff
+#define I_RET 0xc3
+#define I_IRET 0xcf
+
+#define inst_trap_return(ins) (((ins)&0xff) == I_IRET)
+#define inst_return(ins) (((ins)&0xff) == I_RET)
+#define inst_call(ins) (((ins)&0xff) == I_CALL || \
+ (((ins)&0xff) == I_CALLI && \
+ ((ins)&0x3800) == 0x1000))
+#define inst_load(ins) 0
+#define inst_store(ins) 0
+
+/* access capability and access macros */
+
+#define DB_ACCESS_LEVEL 2 /* access any space */
+#define DB_CHECK_ACCESS(addr,size,task) \
+ db_check_access(addr,size,task)
+#define DB_PHYS_EQ(task1,addr1,task2,addr2) \
+ db_phys_eq(task1,addr1,task2,addr2)
+#define DB_VALID_KERN_ADDR(addr) \
+ ((addr) >= VM_MIN_KERNEL_ADDRESS && \
+ (addr) < VM_MAX_KERNEL_ADDRESS)
+#define DB_VALID_ADDRESS(addr,user) \
+ ((!(user) && DB_VALID_KERN_ADDR(addr)) || \
+ ((user) && (addr) < VM_MIN_KERNEL_ADDRESS))
+
+boolean_t db_check_access(/* vm_offset_t, int, task_t */);
+boolean_t db_phys_eq(/* task_t, vm_offset_t, task_t, vm_offset_t */);
+
+/* macros for printing OS server dependent task name */
+
+#define DB_TASK_NAME(task) db_task_name(task)
+#define DB_TASK_NAME_TITLE "COMMAND "
+#define DB_TASK_NAME_LEN 23
+#define DB_NULL_TASK_NAME "? "
+
+void db_task_name(/* task_t */);
+
+/* macro for checking if a thread has used floating-point */
+
+#define db_thread_fp_used(thread) ((thread)->pcb->ims.ifps != 0)
+
+#endif /* _I386_DB_MACHDEP_H_ */
diff --git a/sys/amd64/include/exec.h b/sys/amd64/include/exec.h
new file mode 100644
index 0000000..f63ec49
--- /dev/null
+++ b/sys/amd64/include/exec.h
@@ -0,0 +1,128 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)exec.h 8.1 (Berkeley) 6/11/93
+ */
+
+#ifndef _EXEC_H_
+#define _EXEC_H_
+
+#define __LDPGSZ 4096
+
+/* Valid magic number check. */
+#define N_BADMAG(ex) \
+ (N_GETMAGIC(ex) != OMAGIC && N_GETMAGIC(ex) != NMAGIC && \
+ N_GETMAGIC(ex) != ZMAGIC && N_GETMAGIC(ex) != QMAGIC && \
+ N_GETMAGIC_NET(ex) != OMAGIC && N_GETMAGIC_NET(ex) != NMAGIC && \
+ N_GETMAGIC_NET(ex) != ZMAGIC && N_GETMAGIC_NET(ex) != QMAGIC)
+
+#define N_ALIGN(ex,x) \
+ (N_GETMAGIC(ex) == ZMAGIC || N_GETMAGIC(ex) == QMAGIC || \
+ N_GETMAGIC_NET(ex) == ZMAGIC || N_GETMAGIC_NET(ex) == QMAGIC ? \
+ ((x) + __LDPGSZ - 1) & ~(__LDPGSZ - 1) : (x))
+
+/* Address of the bottom of the text segment. */
+#define N_TXTADDR(ex) \
+ ((N_GETMAGIC(ex) == OMAGIC || N_GETMAGIC(ex) == NMAGIC || \
+ N_GETMAGIC(ex) == ZMAGIC) ? 0 : __LDPGSZ)
+
+/* Address of the bottom of the data segment. */
+#define N_DATADDR(ex) \
+ N_ALIGN(ex, N_TXTADDR(ex) + (ex).a_text)
+
+#define N_GETMAGIC(ex) \
+ ( (ex).a_midmag & 0xffff )
+#define N_GETMID(ex) \
+ ( (N_GETMAGIC_NET(ex) == ZMAGIC) ? N_GETMID_NET(ex) : \
+ ((ex).a_midmag >> 16) & 0x03ff )
+#define N_GETFLAG(ex) \
+ ( (N_GETMAGIC_NET(ex) == ZMAGIC) ? N_GETFLAG_NET(ex) : \
+ ((ex).a_midmag >> 26) & 0x3f )
+#define N_SETMAGIC(ex,mag,mid,flag) \
+ ( (ex).a_midmag = (((flag) & 0x3f) <<26) | (((mid) & 0x03ff) << 16) | \
+ ((mag) & 0xffff) )
+
+#define N_GETMAGIC_NET(ex) \
+ (ntohl((ex).a_midmag) & 0xffff)
+#define N_GETMID_NET(ex) \
+ ((ntohl((ex).a_midmag) >> 16) & 0x03ff)
+#define N_GETFLAG_NET(ex) \
+ ((ntohl((ex).a_midmag) >> 26) & 0x3f)
+#define N_SETMAGIC_NET(ex,mag,mid,flag) \
+ ( (ex).a_midmag = htonl( (((flag)&0x3f)<<26) | (((mid)&0x03ff)<<16) | \
+ (((mag)&0xffff)) ) )
+
+/* Text segment offset. */
+#define N_TXTOFF(ex) \
+ (N_GETMAGIC(ex) == ZMAGIC ? __LDPGSZ : (N_GETMAGIC(ex) == QMAGIC || \
+ N_GETMAGIC_NET(ex) == ZMAGIC) ? 0 : sizeof(struct exec))
+
+/* Data segment offset. */
+#define N_DATOFF(ex) \
+ N_ALIGN(ex, N_TXTOFF(ex) + (ex).a_text)
+
+/* Relocation table offset. */
+#define N_RELOFF(ex) \
+ N_ALIGN(ex, N_DATOFF(ex) + (ex).a_data)
+
+/* Symbol table offset. */
+#define N_SYMOFF(ex) \
+ (N_RELOFF(ex) + (ex).a_trsize + (ex).a_drsize)
+
+/* String table offset. */
+#define N_STROFF(ex) (N_SYMOFF(ex) + (ex).a_syms)
+
+/*
+ * Header prepended to each a.out file.
+ * only manipulate the a_midmag field via the
+ * N_SETMAGIC/N_GET{MAGIC,MID,FLAG} macros in a.out.h
+ */
+
+struct exec {
+unsigned long a_midmag; /* htonl(flags<<26 | mid<<16 | magic) */
+unsigned long a_text; /* text segment size */
+unsigned long a_data; /* initialized data size */
+unsigned long a_bss; /* uninitialized data size */
+unsigned long a_syms; /* symbol table size */
+unsigned long a_entry; /* entry point */
+unsigned long a_trsize; /* text relocation size */
+unsigned long a_drsize; /* data relocation size */
+};
+#define a_magic a_midmag /* XXX Hack to work with current kern_execve.c */
+
+/* a_magic */
+#define OMAGIC 0407 /* old impure format */
+#define NMAGIC 0410 /* read-only text */
+#define ZMAGIC 0413 /* demand load format */
+#define QMAGIC 0314 /* "compact" demand load format */
+
+#endif /* !_EXEC_H_ */
diff --git a/sys/amd64/include/float.h b/sys/amd64/include/float.h
new file mode 100644
index 0000000..fb5967e
--- /dev/null
+++ b/sys/amd64/include/float.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)float.h 7.1 (Berkeley) 5/8/90
+ * $Id: float.h,v 1.4 1993/10/16 14:39:16 rgrimes Exp $
+ */
+
+#ifndef _MACHINE_FLOAT_H_
+#define _MACHINE_FLOAT_H_ 1
+
+#define FLT_RADIX 2 /* b */
+#define FLT_ROUNDS 1 /* FP addition rounds to nearest */
+
+#define FLT_MANT_DIG 24 /* p */
+#define FLT_EPSILON 1.19209290E-07F /* b**(1-p) */
+#define FLT_DIG 6 /* floor((p-1)*log10(b))+(b == 10) */
+#define FLT_MIN_EXP (-125) /* emin */
+#define FLT_MIN 1.17549435E-38F /* b**(emin-1) */
+#define FLT_MIN_10_EXP (-37) /* ceil(log10(b**(emin-1))) */
+#define FLT_MAX_EXP 128 /* emax */
+#define FLT_MAX 3.40282347E+38F /* (1-b**(-p))*b**emax */
+#define FLT_MAX_10_EXP 38 /* floor(log10((1-b**(-p))*b**emax)) */
+
+#define DBL_MANT_DIG 53
+#define DBL_EPSILON 2.2204460492503131E-16
+#define DBL_DIG 15
+#define DBL_MIN_EXP (-1021)
+#define DBL_MIN 2.2250738585072014E-308
+#define DBL_MIN_10_EXP (-307)
+#define DBL_MAX_EXP 1024
+#define DBL_MAX 1.7976931348623157E+308
+#define DBL_MAX_10_EXP 308
+
+#define LDBL_MANT_DIG DBL_MANT_DIG
+#define LDBL_EPSILON DBL_EPSILON
+#define LDBL_DIG DBL_DIG
+#define LDBL_MIN_EXP DBL_MIN_EXP
+#define LDBL_MIN DBL_MIN
+#define LDBL_MIN_10_EXP DBL_MIN_10_EXP
+#define LDBL_MAX_EXP DBL_MAX_EXP
+#define LDBL_MAX DBL_MAX
+#define LDBL_MAX_10_EXP DBL_MAX_10_EXP
+#endif /* _MACHINE_FLOAT_H_ */
diff --git a/sys/amd64/include/floatingpoint.h b/sys/amd64/include/floatingpoint.h
new file mode 100644
index 0000000..ed47cf6
--- /dev/null
+++ b/sys/amd64/include/floatingpoint.h
@@ -0,0 +1,109 @@
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#) floatingpoint.h 1.0 (Berkeley) 9/23/93
+ * $Id: floatingpoint.h,v 1.3 1993/10/16 14:39:18 rgrimes Exp $
+ */
+
+/*
+ * IEEE floating point structure and function definitions
+ */
+
+#ifndef _FLOATINGPOINT_H_
+#define _FLOATINGPOINT_H_
+
+#include <sys/cdefs.h>
+#include <sys/ieeefp.h>
+
+#ifdef __GNUC__
+
+#ifdef __i386__
+
+#define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr) : "0" (*addr))
+#define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr) : "0" (*addr))
+#define fnstenv(addr) __asm("fnstenv %0" : "=m" (*addr) : "0" (*addr))
+#define fldenv(addr) __asm("fldenv %0" : : "m" (*addr))
+
+
+/*
+ * return the contents of a FP register
+ */
+static __inline__ int
+__fpgetreg(int _reg)
+{
+ unsigned short _mem;
+
+ switch(_reg) {
+ default:
+ fnstcw(&_mem);
+ break;
+ case FP_STKY_REG:
+ fnstsw(&_mem);
+ break;
+ }
+ return _mem;
+}
+
+/*
+ * set a FP mode; return previous mode
+ */
+static __inline__ int
+__fpsetreg(int _m, int _reg, int _fld, int _off)
+{
+ unsigned _env[7];
+ unsigned _p;
+
+ fnstenv(_env);
+ _p = (_env[_reg] & _fld) >> _off;
+ _env[_reg] = (_env[_reg] & ~_fld) | (_m << _off & _fld);
+ fldenv(_env);
+ return _p;
+}
+
+#endif /* __i386__ */
+
+#endif /* __GNUC__ */
+
+/*
+ * SysV/386 FP control interface
+ */
+#define fpgetround() ((__fpgetreg(FP_RND_REG) & FP_RND_FLD) >> FP_RND_OFF)
+#define fpsetround(m) __fpsetreg((m), FP_RND_REG, FP_RND_FLD, FP_RND_OFF)
+#define fpgetprec() ((__fpgetreg(FP_PRC_REG) & FP_PRC_FLD) >> FP_PRC_OFF)
+#define fpsetprec(m) __fpsetreg((m), FP_PRC_REG, FP_PRC_FLD, FP_PRC_OFF)
+#define fpgetmask() ((~__fpgetreg(FP_MSKS_REG) & FP_MSKS_FLD) >> FP_MSKS_OFF)
+#define fpsetmask(m) __fpsetreg(~(m), FP_MSKS_REG, FP_MSKS_FLD, FP_MSKS_OFF)
+#define fpgetsticky() ((__fpgetreg(FP_STKY_REG) & FP_STKY_FLD) >> FP_STKY_OFF)
+#define fpresetsticky(m) __fpsetreg(0, FP_STKY_REG, (m), FP_STKY_OFF)
+#define fpsetsticky(m) fpresetsticky(m)
+
+#endif /* !_FLOATINGPOINT_H_ */
diff --git a/sys/amd64/include/fpu.h b/sys/amd64/include/fpu.h
new file mode 100644
index 0000000..87cd6f9
--- /dev/null
+++ b/sys/amd64/include/fpu.h
@@ -0,0 +1,141 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)npx.h 5.3 (Berkeley) 1/18/91
+ * $Id: npx.h,v 1.2 1993/10/16 14:39:22 rgrimes Exp $
+ */
+
+/*
+ * 287/387 NPX Coprocessor Data Structures and Constants
+ * W. Jolitz 1/90
+ */
+
+#ifndef ___NPX87___
+#define ___NPX87___
+
+/* Environment information of floating point unit */
+struct env87 {
+ long en_cw; /* control word (16bits) */
+ long en_sw; /* status word (16bits) */
+ long en_tw; /* tag word (16bits) */
+ long en_fip; /* floating point instruction pointer */
+ u_short en_fcs; /* floating code segment selector */
+ u_short en_opcode; /* opcode last executed (11 bits ) */
+ long en_foo; /* floating operand offset */
+ long en_fos; /* floating operand segment selector */
+};
+
+/* Contents of each floating point accumulator */
+struct fpacc87 {
+#ifdef dontdef /* too unportable */
+ u_long fp_mantlo; /* mantissa low (31:0) */
+ u_long fp_manthi; /* mantissa high (63:32) */
+ int fp_exp:15; /* exponent */
+ int fp_sgn:1; /* mantissa sign */
+#else
+ u_char fp_bytes[10];
+#endif
+};
+
+/* Floating point context */
+struct save87 {
+ struct env87 sv_env; /* floating point control/status */
+ struct fpacc87 sv_ac[8]; /* accumulator contents, 0-7 */
+ u_long sv_ex_sw; /* status word for last exception (was pad) */
+ u_long sv_ex_tw; /* tag word for last exception (was pad) */
+#ifdef GPL_MATH_EMULATE
+ u_char sv_pad[60];
+#else
+ u_char sv_pad[8 * 2 - 2 * 4]; /* bogus historical padding */
+#endif /* GPL_MATH_EMULATE */
+};
+
+/* Cyrix EMC memory - mapped coprocessor context switch information */
+struct emcsts {
+ long em_msw; /* memory mapped status register when swtched */
+ long em_tar; /* memory mapped temp A register when swtched */
+ long em_dl; /* memory mapped D low register when swtched */
+};
+
+/* Intel prefers long real (53 bit) precision */
+#define __iBCS_NPXCW__ 0x262
+/* wfj prefers temporary real (64 bit) precision */
+#define __386BSD_NPXCW__ 0x362
+/*
+ * bde prefers 53 bit precision and all exceptions masked.
+ *
+ * The standard control word from finit is 0x37F, giving:
+ *
+ * round to nearest
+ * 64-bit precision
+ * all exceptions masked.
+ *
+ * Now I want:
+ *
+ * affine mode for 287's (if they work at all) (1 in bitfield 1<<12)
+ * 53-bit precision (2 in bitfield 3<<8)
+ * overflow exception unmasked (0 in bitfield 1<<3)
+ * zero divide exception unmasked (0 in bitfield 1<<2)
+ * invalid-operand exception unmasked (0 in bitfield 1<<0).
+ *
+ * 64-bit precision often gives bad results with high level languages
+ * because it makes the results of calculations depend on whether
+ * intermediate values are stored in memory or in FPU registers.
+ *
+ * The "Intel" and wfj control words have:
+ *
+ * underflow exception unmasked (0 in bitfield 1<<4)
+ *
+ * but that causes an unexpected exception in the test program 'paranoia'
+ * and makes denormals useless (DBL_MIN / 2 underflows). It doesn't make
+ * a lot of sense to trap underflow without trapping denormals.
+ *
+ * Later I will want the IEEE default of all exceptions masked. See the
+ * 0.0 math manpage for why this is better. The 0.1 math manpage is empty.
+ */
+#define __BDE_NPXCW__ 0x1272
+#define __BETTER_BDE_NPXCW__ 0x127f
+
+#ifdef __BROKEN_NPXCW__
+#ifdef __386BSD__
+#define __INITIAL_NPXCW__ __386BSD_NPXCW__
+#else
+#define __INITIAL_NPXCW__ __iBCS_NPXCW__
+#endif
+#else
+#define __INITIAL_NPXCW__ __BDE_NPXCW__
+#endif
+
+#endif ___NPX87___
diff --git a/sys/amd64/include/frame.h b/sys/amd64/include/frame.h
new file mode 100644
index 0000000..db2993e
--- /dev/null
+++ b/sys/amd64/include/frame.h
@@ -0,0 +1,140 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)frame.h 5.2 (Berkeley) 1/18/91
+ * $Id: frame.h,v 1.7 1994/01/03 07:55:32 davidg Exp $
+ */
+
+#ifndef _MACHINE_FRAME_H_
+#define _MACHINE_FRAME_H_ 1
+
+#include <sys/signal.h>
+
+/*
+ * System stack frames.
+ */
+
+/*
+ * Exception/Trap Stack Frame
+ */
+
+struct trapframe {
+ int tf_es;
+ int tf_ds;
+ int tf_edi;
+ int tf_esi;
+ int tf_ebp;
+ int tf_isp;
+ int tf_ebx;
+ int tf_edx;
+ int tf_ecx;
+ int tf_eax;
+ int tf_trapno;
+ /* below portion defined in 386 hardware */
+ int tf_err;
+ int tf_eip;
+ int tf_cs;
+ int tf_eflags;
+ /* below only when transitting rings (e.g. user to kernel) */
+ int tf_esp;
+ int tf_ss;
+};
+
+extern int kdb_trap(int, int, struct trapframe *);
+
+/* Interrupt stack frame */
+
+struct intrframe {
+ int if_vec;
+ int if_ppl;
+ int if_es;
+ int if_ds;
+ int if_edi;
+ int if_esi;
+ int if_ebp;
+ int :32;
+ int if_ebx;
+ int if_edx;
+ int if_ecx;
+ int if_eax;
+ int :32; /* for compat with trap frame - trapno */
+ int :32; /* for compat with trap frame - err */
+ /* below portion defined in 386 hardware */
+ int if_eip;
+ int if_cs;
+ int if_eflags;
+ /* below only when transitting rings (e.g. user to kernel) */
+ int if_esp;
+ int if_ss;
+};
+
+/* frame of clock (same as interrupt frame) */
+
+struct clockframe {
+ int cf_vec;
+ int cf_ppl;
+ int cf_es;
+ int cf_ds;
+ int cf_edi;
+ int cf_esi;
+ int cf_ebp;
+ int :32;
+ int cf_ebx;
+ int cf_edx;
+ int cf_ecx;
+ int cf_eax;
+ int :32; /* for compat with trap frame - trapno */
+ int :32; /* for compat with trap frame - err */
+ /* below portion defined in 386 hardware */
+ int cf_eip;
+ int cf_cs;
+ int cf_eflags;
+ /* below only when transitting rings (e.g. user to kernel) */
+ int cf_esp;
+ int cf_ss;
+};
+
+/*
+ * Signal frame
+ */
+struct sigframe {
+ int sf_signum;
+ int sf_code;
+ struct sigcontext *sf_scp;
+ char *sf_addr;
+ sig_t sf_handler;
+ struct sigcontext sf_sc;
+};
+#endif /* _MACHINE_FRAME_H_ */
diff --git a/sys/amd64/include/npx.h b/sys/amd64/include/npx.h
new file mode 100644
index 0000000..87cd6f9
--- /dev/null
+++ b/sys/amd64/include/npx.h
@@ -0,0 +1,141 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)npx.h 5.3 (Berkeley) 1/18/91
+ * $Id: npx.h,v 1.2 1993/10/16 14:39:22 rgrimes Exp $
+ */
+
+/*
+ * 287/387 NPX Coprocessor Data Structures and Constants
+ * W. Jolitz 1/90
+ */
+
+#ifndef ___NPX87___
+#define ___NPX87___
+
+/* Environment information of floating point unit */
+struct env87 {
+ long en_cw; /* control word (16bits) */
+ long en_sw; /* status word (16bits) */
+ long en_tw; /* tag word (16bits) */
+ long en_fip; /* floating point instruction pointer */
+ u_short en_fcs; /* floating code segment selector */
+ u_short en_opcode; /* opcode last executed (11 bits ) */
+ long en_foo; /* floating operand offset */
+ long en_fos; /* floating operand segment selector */
+};
+
+/* Contents of each floating point accumulator */
+struct fpacc87 {
+#ifdef dontdef /* too unportable */
+ u_long fp_mantlo; /* mantissa low (31:0) */
+ u_long fp_manthi; /* mantissa high (63:32) */
+ int fp_exp:15; /* exponent */
+ int fp_sgn:1; /* mantissa sign */
+#else
+ u_char fp_bytes[10];
+#endif
+};
+
+/* Floating point context */
+struct save87 {
+ struct env87 sv_env; /* floating point control/status */
+ struct fpacc87 sv_ac[8]; /* accumulator contents, 0-7 */
+ u_long sv_ex_sw; /* status word for last exception (was pad) */
+ u_long sv_ex_tw; /* tag word for last exception (was pad) */
+#ifdef GPL_MATH_EMULATE
+ u_char sv_pad[60];
+#else
+ u_char sv_pad[8 * 2 - 2 * 4]; /* bogus historical padding */
+#endif /* GPL_MATH_EMULATE */
+};
+
+/* Cyrix EMC memory - mapped coprocessor context switch information */
+struct emcsts {
+ long em_msw; /* memory mapped status register when swtched */
+ long em_tar; /* memory mapped temp A register when swtched */
+ long em_dl; /* memory mapped D low register when swtched */
+};
+
+/* Intel prefers long real (53 bit) precision */
+#define __iBCS_NPXCW__ 0x262
+/* wfj prefers temporary real (64 bit) precision */
+#define __386BSD_NPXCW__ 0x362
+/*
+ * bde prefers 53 bit precision and all exceptions masked.
+ *
+ * The standard control word from finit is 0x37F, giving:
+ *
+ * round to nearest
+ * 64-bit precision
+ * all exceptions masked.
+ *
+ * Now I want:
+ *
+ * affine mode for 287's (if they work at all) (1 in bitfield 1<<12)
+ * 53-bit precision (2 in bitfield 3<<8)
+ * overflow exception unmasked (0 in bitfield 1<<3)
+ * zero divide exception unmasked (0 in bitfield 1<<2)
+ * invalid-operand exception unmasked (0 in bitfield 1<<0).
+ *
+ * 64-bit precision often gives bad results with high level languages
+ * because it makes the results of calculations depend on whether
+ * intermediate values are stored in memory or in FPU registers.
+ *
+ * The "Intel" and wfj control words have:
+ *
+ * underflow exception unmasked (0 in bitfield 1<<4)
+ *
+ * but that causes an unexpected exception in the test program 'paranoia'
+ * and makes denormals useless (DBL_MIN / 2 underflows). It doesn't make
+ * a lot of sense to trap underflow without trapping denormals.
+ *
+ * Later I will want the IEEE default of all exceptions masked. See the
+ * 0.0 math manpage for why this is better. The 0.1 math manpage is empty.
+ */
+#define __BDE_NPXCW__ 0x1272
+#define __BETTER_BDE_NPXCW__ 0x127f
+
+#ifdef __BROKEN_NPXCW__
+#ifdef __386BSD__
+#define __INITIAL_NPXCW__ __386BSD_NPXCW__
+#else
+#define __INITIAL_NPXCW__ __iBCS_NPXCW__
+#endif
+#else
+#define __INITIAL_NPXCW__ __BDE_NPXCW__
+#endif
+
+#endif ___NPX87___
diff --git a/sys/amd64/include/pc/display.h b/sys/amd64/include/pc/display.h
new file mode 100644
index 0000000..9e64a3f
--- /dev/null
+++ b/sys/amd64/include/pc/display.h
@@ -0,0 +1,45 @@
+/*
+ * IBM PC display definitions
+ *
+ * $Id$
+ */
+
+/* Color attributes for foreground text */
+
+#define FG_BLACK 0
+#define FG_BLUE 1
+#define FG_GREEN 2
+#define FG_CYAN 3
+#define FG_RED 4
+#define FG_MAGENTA 5
+#define FG_BROWN 6
+#define FG_LIGHTGREY 7
+#define FG_DARKGREY 8
+#define FG_LIGHTBLUE 9
+#define FG_LIGHTGREEN 10
+#define FG_LIGHTCYAN 11
+#define FG_LIGHTRED 12
+#define FG_LIGHTMAGENTA 13
+#define FG_YELLOW 14
+#define FG_WHITE 15
+#define FG_BLINK 0x80
+
+/* Color attributes for text background */
+
+#define BG_BLACK 0x00
+#define BG_BLUE 0x10
+#define BG_GREEN 0x20
+#define BG_CYAN 0x30
+#define BG_RED 0x40
+#define BG_MAGENTA 0x50
+#define BG_BROWN 0x60
+#define BG_LIGHTGREY 0x70
+
+/* Monochrome attributes for foreground text */
+
+#define FG_UNDERLINE 0x01
+#define FG_INTENSE 0x08
+
+/* Monochrome attributes for text background */
+
+#define BG_INTENSE 0x10
diff --git a/sys/amd64/include/pcb.h b/sys/amd64/include/pcb.h
new file mode 100644
index 0000000..990e5f9
--- /dev/null
+++ b/sys/amd64/include/pcb.h
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)pcb.h 5.10 (Berkeley) 5/12/91
+ * $Id: pcb.h,v 1.3 1993/11/07 17:42:59 wollman Exp $
+ */
+
+#ifndef _I386_PCB_H_
+#define _I386_PCB_H_
+
+/*
+ * Intel 386 process control block
+ */
+#include "machine/tss.h"
+#include "machine/npx.h"
+
+struct pcb {
+ struct i386tss pcb_tss;
+#define pcb_ksp pcb_tss.tss_esp0
+#define pcb_ptd pcb_tss.tss_cr3
+#define pcb_cr3 pcb_ptd
+#define pcb_pc pcb_tss.tss_eip
+#define pcb_psl pcb_tss.tss_eflags
+#define pcb_usp pcb_tss.tss_esp
+#define pcb_fp pcb_tss.tss_ebp
+#ifdef notyet
+ u_char pcb_iomap[NPORT/sizeof(u_char)]; /* i/o port bitmap */
+#endif
+ caddr_t pcb_ldt; /* per process (user) LDT */
+ int pcb_ldt_len; /* number of LDT entries */
+ struct save87 pcb_savefpu; /* floating point state for 287/387 */
+ struct emcsts pcb_saveemc; /* Cyrix EMC state */
+/*
+ * Software pcb (extension)
+ */
+ int pcb_flags;
+#ifdef notused
+#define FP_WASUSED 0x01 /* process has used fltng pnt hardware */
+#define FP_NEEDSSAVE 0x02 /* ... that needs save on next context switch */
+#define FP_NEEDSRESTORE 0x04 /* ... that needs restore on next DNA fault */
+#endif
+#define FP_USESEMC 0x08 /* process uses EMC memory-mapped mode */
+#define FP_SOFTFP 0x20 /* process using software fltng pnt emulator */
+ short pcb_iml; /* interrupt mask level */
+ caddr_t pcb_onfault; /* copyin/out fault recovery */
+ long pcb_sigc[8]; /* XXX signal code trampoline */
+ int pcb_cmap2; /* XXX temporary PTE - will prefault instead */
+};
+
+/*
+ * The pcb is augmented with machine-dependent additional data for
+ * core dumps. For the i386: ???
+ */
+struct md_coredump {
+};
+
+#ifdef KERNEL
+extern struct pcb *curpcb; /* our current running pcb */
+#endif
+
+#endif /* _I386_PCB_H_ */
diff --git a/sys/amd64/include/pmap.h b/sys/amd64/include/pmap.h
new file mode 100644
index 0000000..7ddcebd
--- /dev/null
+++ b/sys/amd64/include/pmap.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 1991 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department and William Jolitz of UUNET Technologies Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Derived from hp300 version by Mike Hibler, this version by William
+ * Jolitz uses a recursive map [a pde points to the page directory] to
+ * map the page tables using the pagetables themselves. This is done to
+ * reduce the impact on kernel virtual memory for lots of sparse address
+ * space, and to reduce the cost of memory to each process.
+ *
+ * from: hp300: @(#)pmap.h 7.2 (Berkeley) 12/16/90
+ * from: @(#)pmap.h 7.4 (Berkeley) 5/12/91
+ * $Id: pmap.h,v 1.12 1994/03/24 23:12:48 davidg Exp $
+ */
+
+#ifndef _PMAP_MACHINE_
+#define _PMAP_MACHINE_ 1
+
+#include <machine/pte.h>
+
+typedef unsigned int *pd_entry_t;
+typedef unsigned int *pt_entry_t;
+
+/*
+ * NKPDE controls the virtual space of the kernel, what ever is left, minus
+ * the alternate page table area is given to the user (NUPDE)
+ */
+/*
+ * NKPDE controls the virtual space of the kernel, what ever is left is
+ * given to the user (NUPDE)
+ */
+#ifndef NKPT
+#define NKPT 24 /* actual number of kernel pte's */
+#endif
+#ifndef NKPDE
+#define NKPDE 63 /* addressable number of kpte's */
+#endif
+
+#define NUPDE (NPTEPG-NKPDE) /* number of user pde's */
+
+/*
+ * The *PTDI values control the layout of virtual memory
+ *
+ * XXX This works for now, but I am not real happy with it, I'll fix it
+ * right after I fix locore.s and the magic 28K hole
+ */
+#define APTDPTDI (NPTEPG-1) /* alt ptd entry that points to APTD */
+#define KPTDI (APTDPTDI-NKPDE)/* start of kernel virtual pde's */
+#define PTDPTDI (KPTDI-1) /* ptd entry that points to ptd! */
+#define KSTKPTDI (PTDPTDI-1) /* ptd entry for u./kernel&user stack */
+#define KSTKPTEOFF (NBPG/sizeof(pd_entry_t)-UPAGES) /* pte entry for kernel stack */
+
+#define PDESIZE sizeof(pd_entry_t) /* for assembly files */
+#define PTESIZE sizeof(pt_entry_t) /* for assembly files */
+
+/*
+ * Address of current and alternate address space page table maps
+ * and directories.
+ */
+#ifdef KERNEL
+extern pt_entry_t PTmap[], APTmap[], Upte;
+extern pd_entry_t PTD[], APTD[], PTDpde, APTDpde, Upde;
+
+extern int IdlePTD; /* physical address of "Idle" state directory */
+#endif
+
+/*
+ * virtual address to page table entry and
+ * to physical address. Likewise for alternate address space.
+ * Note: these work recursively, thus vtopte of a pte will give
+ * the corresponding pde that in turn maps it.
+ */
+#define vtopte(va) (PTmap + i386_btop(va))
+#define kvtopte(va) vtopte(va)
+#define ptetov(pt) (i386_ptob(pt - PTmap))
+#define vtophys(va) (((int) (*vtopte(va))&PG_FRAME) | ((int)(va) & PGOFSET))
+#define ispt(va) ((va) >= UPT_MIN_ADDRESS && (va) <= KPT_MAX_ADDRESS)
+
+#define avtopte(va) (APTmap + i386_btop(va))
+#define ptetoav(pt) (i386_ptob(pt - APTmap))
+#define avtophys(va) (((int) (*avtopte(va))&PG_FRAME) | ((int)(va) & PGOFSET))
+
+#ifdef KERNEL
+/*
+ * Routine: pmap_kextract
+ * Function:
+ * Extract the physical page address associated
+ * kernel virtual address.
+ */
+static inline vm_offset_t
+pmap_kextract(va)
+ vm_offset_t va;
+{
+ vm_offset_t pa = *(int *)vtopte(va);
+ pa = (pa & PG_FRAME) | (va & ~PG_FRAME);
+ return pa;
+}
+#endif
+
+/*
+ * macros to generate page directory/table indicies
+ */
+
+#define pdei(va) (((va)&PD_MASK)>>PD_SHIFT)
+#define ptei(va) (((va)&PT_MASK)>>PG_SHIFT)
+
+/*
+ * Pmap stuff
+ */
+
+struct pmap {
+ pd_entry_t *pm_pdir; /* KVA of page directory */
+ boolean_t pm_pdchanged; /* pdir changed */
+ short pm_dref; /* page directory ref count */
+ short pm_count; /* pmap reference count */
+ simple_lock_data_t pm_lock; /* lock on pmap */
+ struct pmap_statistics pm_stats; /* pmap statistics */
+ long pm_ptpages; /* more stats: PT pages */
+};
+
+typedef struct pmap *pmap_t;
+
+#ifdef KERNEL
+extern pmap_t kernel_pmap;
+#endif
+
+/*
+ * Macros for speed
+ */
+#define PMAP_ACTIVATE(pmapp, pcbp) \
+ if ((pmapp) != NULL /*&& (pmapp)->pm_pdchanged */) { \
+ (pcbp)->pcb_cr3 = \
+ pmap_extract(kernel_pmap, (vm_offset_t)(pmapp)->pm_pdir); \
+ if ((pmapp) == &curproc->p_vmspace->vm_pmap) \
+ load_cr3((pcbp)->pcb_cr3); \
+ (pmapp)->pm_pdchanged = FALSE; \
+ }
+
+#define PMAP_DEACTIVATE(pmapp, pcbp)
+
+/*
+ * For each vm_page_t, there is a list of all currently valid virtual
+ * mappings of that page. An entry is a pv_entry_t, the list is pv_table.
+ */
+typedef struct pv_entry {
+ struct pv_entry *pv_next; /* next pv_entry */
+ pmap_t pv_pmap; /* pmap where mapping lies */
+ vm_offset_t pv_va; /* virtual address for mapping */
+} *pv_entry_t;
+
+#define PV_ENTRY_NULL ((pv_entry_t) 0)
+
+#define PV_CI 0x01 /* all entries must be cache inhibited */
+#define PV_PTPAGE 0x02 /* entry maps a page table page */
+
+#ifdef KERNEL
+
+pv_entry_t pv_table; /* array of entries, one per page */
+
+#define pa_index(pa) atop(pa - vm_first_phys)
+#define pa_to_pvh(pa) (&pv_table[pa_index(pa)])
+
+#define pmap_resident_count(pmap) ((pmap)->pm_stats.resident_count)
+
+extern pmap_t pmap_create(vm_size_t);
+extern void pmap_pinit(struct pmap *);
+extern void pmap_destroy(pmap_t);
+extern void pmap_release(struct pmap *);
+extern void pmap_reference(pmap_t);
+extern void pmap_remove(struct pmap *, vm_offset_t, vm_offset_t);
+extern void pmap_protect(struct pmap *, vm_offset_t, vm_offset_t, vm_prot_t);
+extern void pmap_enter(pmap_t, vm_offset_t, vm_offset_t, vm_prot_t, boolean_t);
+extern void pmap_change_wiring(pmap_t, vm_offset_t, boolean_t);
+extern inline pt_entry_t *pmap_pte(pmap_t, vm_offset_t);
+extern vm_offset_t pmap_extract(pmap_t, vm_offset_t);
+extern void pmap_copy(pmap_t, pmap_t, vm_offset_t, vm_size_t, vm_offset_t);
+extern void pmap_collect(pmap_t);
+struct pcb; extern void pmap_activate(pmap_t, struct pcb *);
+extern pmap_t pmap_kernel(void);
+extern void pmap_pageable(pmap_t, vm_offset_t, vm_offset_t, boolean_t);
+
+
+#endif /* KERNEL */
+
+#endif /* _PMAP_MACHINE_ */
diff --git a/sys/amd64/include/proc.h b/sys/amd64/include/proc.h
new file mode 100644
index 0000000..92de3af
--- /dev/null
+++ b/sys/amd64/include/proc.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1991 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)proc.h 7.1 (Berkeley) 5/15/91
+ * $Id: proc.h,v 1.2 1993/10/16 14:39:24 rgrimes Exp $
+ */
+
+#ifndef _MACHINE_PROC_H_
+#define _MACHINE_PROC_H_ 1
+
+/*
+ * Machine-dependent part of the proc structure for hp300.
+ */
+struct mdproc {
+ int md_flags; /* machine-dependent flags */
+ int *md_regs; /* registers on current frame */
+};
+
+/* md_flags */
+#define MDP_AST 0x0001 /* async trap pending */
+#endif /* _MACHINE_PROC_H_ */
diff --git a/sys/amd64/include/profile.h b/sys/amd64/include/profile.h
new file mode 100644
index 0000000..f30efff
--- /dev/null
+++ b/sys/amd64/include/profile.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)profile.h 8.1 (Berkeley) 6/11/93
+ */
+
+#define _MCOUNT_DECL static inline void _mcount
+
+#define MCOUNT \
+extern void mcount() asm("mcount"); void mcount() { \
+ int selfpc, frompcindex; \
+ /* \
+ * find the return address for mcount, \
+ * and the return address for mcount's caller. \
+ * \
+ * selfpc = pc pushed by mcount call \
+ */ \
+ asm("movl 4(%%ebp),%0" : "=r" (selfpc)); \
+ /* \
+ * frompcindex = pc pushed by jsr into self. \
+ * In GCC the caller's stack frame has already been built so we \
+ * have to chase a6 to find caller's raddr. \
+ */ \
+ asm("movl (%%ebp),%0" : "=r" (frompcindex)); \
+ frompcindex = ((int *)frompcindex)[1]; \
+ _mcount(frompcindex, selfpc); \
+}
diff --git a/sys/amd64/include/psl.h b/sys/amd64/include/psl.h
new file mode 100644
index 0000000..997fb23
--- /dev/null
+++ b/sys/amd64/include/psl.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)psl.h 5.2 (Berkeley) 1/18/91
+ * $Id: psl.h,v 1.3 1993/11/07 17:43:04 wollman Exp $
+ */
+
+#ifndef _MACHINE_PSL_H_
+#define _MACHINE_PSL_H_ 1
+
+/*
+ * 386 processor status longword.
+ */
+#define PSL_C 0x00000001 /* carry bit */
+#define PSL_PF 0x00000004 /* parity bit */
+#define PSL_AF 0x00000010 /* bcd carry bit */
+#define PSL_Z 0x00000040 /* zero bit */
+#define PSL_N 0x00000080 /* negative bit */
+#define PSL_T 0x00000100 /* trace enable bit */
+#define PSL_I 0x00000200 /* interrupt enable bit */
+#define PSL_D 0x00000400 /* string instruction direction bit */
+#define PSL_V 0x00000800 /* overflow bit */
+#define PSL_IOPL 0x00003000 /* i/o priviledge level enable */
+#define PSL_NT 0x00004000 /* nested task bit */
+#define PSL_RF 0x00010000 /* restart flag bit */
+#define PSL_VM 0x00020000 /* virtual 8086 mode bit */
+
+#define PSL_MBZ 0xffc08028 /* must be zero bits */
+#define PSL_MBO 0x00000002 /* must be one bits */
+
+#define PSL_USERSET (PSL_MBO | PSL_I)
+#define PSL_USERCLR (PSL_MBZ | PSL_NT)
+#endif /* _MACHINE_PSL_H_ */
diff --git a/sys/amd64/include/ptrace.h b/sys/amd64/include/ptrace.h
new file mode 100644
index 0000000..bfcc55f
--- /dev/null
+++ b/sys/amd64/include/ptrace.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ptrace.h 8.1 (Berkeley) 6/11/93
+ */
+
+/*
+ * Machine dependent trace commands.
+ *
+ * None for the i386 at this time.
+ */
diff --git a/sys/amd64/include/reg.h b/sys/amd64/include/reg.h
new file mode 100644
index 0000000..2a1f061
--- /dev/null
+++ b/sys/amd64/include/reg.h
@@ -0,0 +1,106 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)reg.h 5.5 (Berkeley) 1/18/91
+ * $Id: reg.h,v 1.6 1994/01/03 07:55:34 davidg Exp $
+ */
+
+#ifndef _MACHINE_REG_H_
+#define _MACHINE_REG_H_ 1
+
+/*
+ * Location of the users' stored
+ * registers within appropriate frame of 'trap' and 'syscall', relative to
+ * base of stack frame.
+ * Normal usage is u.u_ar0[XX] in kernel.
+ */
+
+/* When referenced during a trap/exception, registers are at these offsets */
+
+#define tES (0)
+#define tDS (1)
+#define tEDI (2)
+#define tESI (3)
+#define tEBP (4)
+#define tISP (5)
+#define tEBX (6)
+#define tEDX (7)
+#define tECX (8)
+#define tEAX (9)
+
+#define tERR (11)
+
+#define tEIP (12)
+#define tCS (13)
+#define tEFLAGS (14)
+#define tESP (15)
+#define tSS (16)
+
+/*
+ * Registers accessible to ptrace(2) syscall for debugger
+ * The machine-dependent code for PT_{SET,GET}REGS needs to
+ * use whichver order, defined above, is correct, so that it
+ * is all invisible to the user.
+ */
+struct reg {
+ unsigned int r_es;
+ unsigned int r_ds;
+ unsigned int r_edi;
+ unsigned int r_esi;
+ unsigned int r_ebp;
+ unsigned int r_isp;
+ unsigned int r_ebx;
+ unsigned int r_edx;
+ unsigned int r_ecx;
+ unsigned int r_eax;
+ unsigned int r_trapno;
+ unsigned int r_err;
+ unsigned int r_eip;
+ unsigned int r_cs;
+ unsigned int r_eflags;
+ unsigned int r_esp;
+ unsigned int r_ss;
+};
+
+/*
+ * Register set accessible via /proc/$pid/fpreg
+ */
+struct fpreg {
+#if 0
+ int fpr_xxx; /* not implemented */
+#endif
+};
+
+#endif /* _MACHINE_REG_H_ */
diff --git a/sys/amd64/include/reloc.h b/sys/amd64/include/reloc.h
new file mode 100644
index 0000000..386241d
--- /dev/null
+++ b/sys/amd64/include/reloc.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)reloc.h 8.1 (Berkeley) 6/10/93
+ */
+
+/* Relocation format. */
+struct relocation_info {
+ int r_address; /* offset in text or data segment */
+ unsigned int r_symbolnum : 24, /* ordinal number of add symbol */
+ r_pcrel : 1, /* 1 if value should be pc-relative */
+ r_length : 2, /* log base 2 of value's width */
+ r_extern : 1, /* 1 if need to add symbol to value */
+ : 4; /* reserved */
+};
diff --git a/sys/amd64/include/segments.h b/sys/amd64/include/segments.h
new file mode 100644
index 0000000..023a0cf
--- /dev/null
+++ b/sys/amd64/include/segments.h
@@ -0,0 +1,235 @@
+/*-
+ * Copyright (c) 1989, 1990 William F. Jolitz
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)segments.h 7.1 (Berkeley) 5/9/91
+ * $Id: segments.h,v 1.3 1993/11/07 17:43:08 wollman Exp $
+ */
+
+#ifndef _MACHINE_SEGMENTS_H_
+#define _MACHINE_SEGMENTS_H_ 1
+
+/*
+ * 386 Segmentation Data Structures and definitions
+ * William F. Jolitz (william@ernie.berkeley.edu) 6/20/1989
+ */
+
+/*
+ * Selectors
+ */
+
+#define ISPL(s) ((s)&3) /* what is the priority level of a selector */
+#define SEL_KPL 0 /* kernel priority level */
+#define SEL_UPL 3 /* user priority level */
+#define ISLDT(s) ((s)&SEL_LDT) /* is it local or global */
+#define SEL_LDT 4 /* local descriptor table */
+#define IDXSEL(s) (((s)>>3) & 0x1fff) /* index of selector */
+#define LSEL(s,r) (((s)<<3) | SEL_LDT | r) /* a local selector */
+#define GSEL(s,r) (((s)<<3) | r) /* a global selector */
+
+/*
+ * Memory and System segment descriptors
+ */
+struct segment_descriptor {
+ unsigned sd_lolimit:16 ; /* segment extent (lsb) */
+ unsigned sd_lobase:24 __attribute__ ((packed));
+ /* segment base address (lsb) */
+ unsigned sd_type:5 ; /* segment type */
+ unsigned sd_dpl:2 ; /* segment descriptor priority level */
+ unsigned sd_p:1 ; /* segment descriptor present */
+ unsigned sd_hilimit:4 ; /* segment extent (msb) */
+ unsigned sd_xx:2 ; /* unused */
+ unsigned sd_def32:1 ; /* default 32 vs 16 bit size */
+ unsigned sd_gran:1 ; /* limit granularity (byte/page units)*/
+ unsigned sd_hibase:8 ; /* segment base address (msb) */
+} ;
+
+/*
+ * Gate descriptors (e.g. indirect descriptors)
+ */
+struct gate_descriptor {
+ unsigned gd_looffset:16 ; /* gate offset (lsb) */
+ unsigned gd_selector:16 ; /* gate segment selector */
+ unsigned gd_stkcpy:5 ; /* number of stack wds to cpy */
+ unsigned gd_xx:3 ; /* unused */
+ unsigned gd_type:5 ; /* segment type */
+ unsigned gd_dpl:2 ; /* segment descriptor priority level */
+ unsigned gd_p:1 ; /* segment descriptor present */
+ unsigned gd_hioffset:16 ; /* gate offset (msb) */
+} ;
+
+/*
+ * Generic descriptor
+ */
+union descriptor {
+ struct segment_descriptor sd;
+ struct gate_descriptor gd;
+};
+
+ /* system segments and gate types */
+#define SDT_SYSNULL 0 /* system null */
+#define SDT_SYS286TSS 1 /* system 286 TSS available */
+#define SDT_SYSLDT 2 /* system local descriptor table */
+#define SDT_SYS286BSY 3 /* system 286 TSS busy */
+#define SDT_SYS286CGT 4 /* system 286 call gate */
+#define SDT_SYSTASKGT 5 /* system task gate */
+#define SDT_SYS286IGT 6 /* system 286 interrupt gate */
+#define SDT_SYS286TGT 7 /* system 286 trap gate */
+#define SDT_SYSNULL2 8 /* system null again */
+#define SDT_SYS386TSS 9 /* system 386 TSS available */
+#define SDT_SYSNULL3 10 /* system null again */
+#define SDT_SYS386BSY 11 /* system 386 TSS busy */
+#define SDT_SYS386CGT 12 /* system 386 call gate */
+#define SDT_SYSNULL4 13 /* system null again */
+#define SDT_SYS386IGT 14 /* system 386 interrupt gate */
+#define SDT_SYS386TGT 15 /* system 386 trap gate */
+
+ /* memory segment types */
+#define SDT_MEMRO 16 /* memory read only */
+#define SDT_MEMROA 17 /* memory read only accessed */
+#define SDT_MEMRW 18 /* memory read write */
+#define SDT_MEMRWA 19 /* memory read write accessed */
+#define SDT_MEMROD 20 /* memory read only expand dwn limit */
+#define SDT_MEMRODA 21 /* memory read only expand dwn limit accessed */
+#define SDT_MEMRWD 22 /* memory read write expand dwn limit */
+#define SDT_MEMRWDA 23 /* memory read write expand dwn limit acessed */
+#define SDT_MEME 24 /* memory execute only */
+#define SDT_MEMEA 25 /* memory execute only accessed */
+#define SDT_MEMER 26 /* memory execute read */
+#define SDT_MEMERA 27 /* memory execute read accessed */
+#define SDT_MEMEC 28 /* memory execute only conforming */
+#define SDT_MEMEAC 29 /* memory execute only accessed conforming */
+#define SDT_MEMERC 30 /* memory execute read conforming */
+#define SDT_MEMERAC 31 /* memory execute read accessed conforming */
+
+/* is memory segment descriptor pointer ? */
+#define ISMEMSDP(s) ((s->d_type) >= SDT_MEMRO && (s->d_type) <= SDT_MEMERAC)
+
+/* is 286 gate descriptor pointer ? */
+#define IS286GDP(s) (((s->d_type) >= SDT_SYS286CGT \
+ && (s->d_type) < SDT_SYS286TGT))
+
+/* is 386 gate descriptor pointer ? */
+#define IS386GDP(s) (((s->d_type) >= SDT_SYS386CGT \
+ && (s->d_type) < SDT_SYS386TGT))
+
+/* is gate descriptor pointer ? */
+#define ISGDP(s) (IS286GDP(s) || IS386GDP(s))
+
+/* is segment descriptor pointer ? */
+#define ISSDP(s) (ISMEMSDP(s) || !ISGDP(s))
+
+/* is system segment descriptor pointer ? */
+#define ISSYSSDP(s) (!ISMEMSDP(s) && !ISGDP(s))
+
+/*
+ * Software definitions are in this convenient format,
+ * which are translated into inconvenient segment descriptors
+ * when needed to be used by the 386 hardware
+ */
+
+struct soft_segment_descriptor {
+ unsigned ssd_base ; /* segment base address */
+ unsigned ssd_limit ; /* segment extent */
+ unsigned ssd_type:5 ; /* segment type */
+ unsigned ssd_dpl:2 ; /* segment descriptor priority level */
+ unsigned ssd_p:1 ; /* segment descriptor present */
+ unsigned ssd_xx:4 ; /* unused */
+ unsigned ssd_xx1:2 ; /* unused */
+ unsigned ssd_def32:1 ; /* default 32 vs 16 bit size */
+ unsigned ssd_gran:1 ; /* limit granularity (byte/page units)*/
+};
+
+extern ssdtosd() ; /* to decode a ssd */
+extern sdtossd() ; /* to encode a sd */
+
+/*
+ * region descriptors, used to load gdt/idt tables before segments yet exist.
+ */
+struct region_descriptor {
+ unsigned rd_limit:16; /* segment extent */
+ unsigned rd_base:32 __attribute__ ((packed)); /* base address */
+};
+
+/*
+ * Segment Protection Exception code bits
+ */
+
+#define SEGEX_EXT 0x01 /* recursive or externally induced */
+#define SEGEX_IDT 0x02 /* interrupt descriptor table */
+#define SEGEX_TI 0x04 /* local descriptor table */
+ /* other bits are affected descriptor index */
+#define SEGEX_IDX(s) ((s)>>3)&0x1fff)
+
+/*
+ * Size of IDT table
+ */
+
+#define NIDT 256
+#define NRSVIDT 32 /* reserved entries for cpu exceptions */
+
+/*
+ * Entries in the Global Descriptor Table (GDT)
+ */
+#define GNULL_SEL 0 /* Null Descriptor */
+#define GCODE_SEL 1 /* Kernel Code Descriptor */
+#define GDATA_SEL 2 /* Kernel Data Descriptor */
+#define GLDT_SEL 3 /* LDT - eventually one per process */
+#define GTGATE_SEL 4 /* Process task switch gate */
+#define GPANIC_SEL 5 /* Task state to consider panic from */
+#define GPROC0_SEL 6 /* Task state process slot zero and up */
+#define GUSERLDT_SEL 7 /* User LDT */
+#define NGDT GUSERLDT_SEL+1
+
+/*
+ * Entries in the Local Descriptor Table (LDT)
+ */
+#define LSYS5CALLS_SEL 0 /* forced by intel BCS */
+#define LSYS5SIGR_SEL 1
+#define L43BSDCALLS_SEL 2 /* notyet */
+#define LUCODE_SEL 3
+#define LUDATA_SEL 4
+/* seperate stack, es,fs,gs sels ? */
+/* #define LPOSIXCALLS_SEL 5*/ /* notyet */
+#define NLDT LUDATA_SEL+1
+
+#ifdef KERNEL
+extern int currentldt;
+extern union descriptor gdt[NGDT];
+extern union descriptor ldt[NLDT];
+extern struct soft_segment_descriptor gdt_segs[];
+#endif
+
+#endif /* _MACHINE_SEGMENTS_H_ */
diff --git a/sys/amd64/include/signal.h b/sys/amd64/include/signal.h
new file mode 100644
index 0000000..16cbef2
--- /dev/null
+++ b/sys/amd64/include/signal.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)signal.h 8.1 (Berkeley) 6/11/93
+ */
+
+/*
+ * Machine-dependent signal definitions
+ */
+
+typedef int sig_atomic_t;
+
+#ifndef _POSIX_SOURCE
+#include <machine/trap.h> /* codes for SIGILL, SIGFPE */
+#endif
+
+/*
+ * Information pushed on stack when a signal is delivered.
+ * This is used by the kernel to restore state following
+ * execution of the signal handler. It is also made available
+ * to the handler to allow it to restore state properly if
+ * a non-standard exit is performed.
+ */
+struct sigcontext {
+ int sc_onstack; /* sigstack state to restore */
+ int sc_mask; /* signal mask to restore */
+ int sc_esp; /* machine state */
+ int sc_ebp;
+ int sc_isp;
+ int sc_eip;
+ int sc_efl;
+ int sc_es;
+ int sc_ds;
+ int sc_cs;
+ int sc_ss;
+ int sc_edi;
+ int sc_esi;
+ int sc_ebx;
+ int sc_edx;
+ int sc_ecx;
+ int sc_eax;
+# define sc_sp sc_esp
+# define sc_fp sc_ebp
+# define sc_pc sc_eip
+# define sc_ps sc_efl
+};
diff --git a/sys/amd64/include/specialreg.h b/sys/amd64/include/specialreg.h
new file mode 100644
index 0000000..935b1ed
--- /dev/null
+++ b/sys/amd64/include/specialreg.h
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)specialreg.h 7.1 (Berkeley) 5/9/91
+ * $Id: specialreg.h,v 1.2 1993/10/16 14:39:32 rgrimes Exp $
+ */
+
+#ifndef _MACHINE_SPECIALREG_H_
+#define _MACHINE_SPECIALREG_H_ 1
+
+/*
+ * Bits in 386 special registers:
+ */
+
+#define CR0_PE 0x00000001 /* Protected mode Enable */
+#define CR0_MP 0x00000002 /* "Math" Present (NPX or NPX emulator) */
+#ifdef notused
+#define CR0_EM 0x00000004 /* EMulate non-NPX coproc. (trap ESC only) */
+#endif
+#define CR0_TS 0x00000008 /* Task Switched (if MP, trap ESC and WAIT) */
+#ifdef notused
+#define CR0_ET 0x00000010 /* Extension Type (387 (if set) vs 287) */
+#endif
+#define CR0_PG 0x80000000 /* PaGing enable */
+
+/*
+ * Bits in 486 special registers:
+ */
+
+#define CR0_NE 0x00000020 /* Numeric Error enable (EX16 vs IRQ13) */
+#define CR0_WP 0x00010000 /* Write Protect (honor ~PG_W in all modes) */
+#ifdef notyet
+#define CR0_AM 0x00040000 /* Alignment Mask (set to enable AC flag) */
+#endif
+#endif /* _MACHINE_SPECIALREG_H_ */
diff --git a/sys/amd64/include/sysarch.h b/sys/amd64/include/sysarch.h
new file mode 100644
index 0000000..2649ba5
--- /dev/null
+++ b/sys/amd64/include/sysarch.h
@@ -0,0 +1,24 @@
+/*
+ * Architecture specific syscalls (i386)
+ *
+ * $Id: sysarch.h,v 1.2 1993/10/16 14:39:35 rgrimes Exp $
+ */
+#ifndef _MACHINE_SYSARCH_H_
+#define _MACHINE_SYSARCH_H_ 1
+
+#include <sys/cdefs.h>
+
+#define I386_GET_LDT 0
+#define I386_SET_LDT 1
+
+#ifdef KERNEL
+/* nothing here yet... */
+#else /* not KERNEL */
+__BEGIN_DECLS
+
+int i386_get_ldt __P((int, union descriptor *, int));
+int i386_set_ldt __P((int, union descriptor *, int));
+
+__END_DECLS
+#endif /* not KERNEL */
+#endif /* _MACHINE_SYSARCH_H_ */
diff --git a/sys/amd64/include/trap.h b/sys/amd64/include/trap.h
new file mode 100644
index 0000000..aa832ff
--- /dev/null
+++ b/sys/amd64/include/trap.h
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)trap.h 5.4 (Berkeley) 5/9/91
+ * $Id: trap.h,v 1.2 1993/10/16 14:39:37 rgrimes Exp $
+ */
+
+#ifndef _MACHINE_TRAP_H_
+#define _MACHINE_TRAP_H_ 1
+
+/*
+ * Trap type values
+ * also known in trap.c for name strings
+ */
+
+#define T_RESADFLT 0 /* reserved addressing */
+#define T_PRIVINFLT 1 /* privileged instruction */
+#define T_RESOPFLT 2 /* reserved operand */
+#define T_BPTFLT 3 /* breakpoint instruction */
+#define T_SYSCALL 5 /* system call (kcall) */
+#define T_ARITHTRAP 6 /* arithmetic trap */
+#define T_ASTFLT 7 /* system forced exception */
+#define T_SEGFLT 8 /* segmentation (limit) fault */
+#define T_PROTFLT 9 /* protection fault */
+#define T_TRCTRAP 10 /* trace trap */
+#define T_PAGEFLT 12 /* page fault */
+#define T_TABLEFLT 13 /* page table fault */
+#define T_ALIGNFLT 14 /* alignment fault */
+#define T_KSPNOTVAL 15 /* kernel stack pointer not valid */
+#define T_BUSERR 16 /* bus error */
+#define T_KDBTRAP 17 /* kernel debugger trap */
+
+#define T_DIVIDE 18 /* integer divide fault */
+#define T_NMI 19 /* non-maskable trap */
+#define T_OFLOW 20 /* overflow trap */
+#define T_BOUND 21 /* bound instruction fault */
+#define T_DNA 22 /* device not available fault */
+#define T_DOUBLEFLT 23 /* double fault */
+#define T_FPOPFLT 24 /* fp coprocessor operand fetch fault */
+#define T_TSSFLT 25 /* invalid tss fault */
+#define T_SEGNPFLT 26 /* segment not present fault */
+#define T_STKFLT 27 /* stack fault */
+#define T_RESERVED 28 /* reserved fault base */
+
+/* definitions for <sys/signal.h> */
+#define ILL_RESAD_FAULT T_RESADFLT
+#define ILL_PRIVIN_FAULT T_PRIVINFLT
+#define ILL_RESOP_FAULT T_RESOPFLT
+#define ILL_ALIGN_FAULT T_ALIGNFLT
+#define ILL_FPOP_FAULT T_FPOPFLT /* coprocessor operand fault */
+
+/* codes for SIGFPE/ARITHTRAP */
+#define FPE_INTOVF_TRAP 0x1 /* integer overflow */
+#define FPE_INTDIV_TRAP 0x2 /* integer divide by zero */
+#define FPE_FLTDIV_TRAP 0x3 /* floating/decimal divide by zero */
+#define FPE_FLTOVF_TRAP 0x4 /* floating overflow */
+#define FPE_FLTUND_TRAP 0x5 /* floating underflow */
+#define FPE_FPU_NP_TRAP 0x6 /* floating point unit not present */
+#define FPE_SUBRNG_TRAP 0x7 /* subrange out of bounds */
+
+/* codes for SIGBUS */
+#define BUS_PAGE_FAULT T_PAGEFLT /* page fault protection base */
+#define BUS_SEGNP_FAULT T_SEGNPFLT /* segment not present */
+#define BUS_STK_FAULT T_STKFLT /* stack segment */
+#define BUS_SEGM_FAULT T_RESERVED /* segment protection base */
+
+/* Trap's coming from user mode */
+#define T_USER 0x100
+#endif /* _MACHINE_TRAP_H_ */
diff --git a/sys/amd64/include/tss.h b/sys/amd64/include/tss.h
new file mode 100644
index 0000000..1fada6e
--- /dev/null
+++ b/sys/amd64/include/tss.h
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)tss.h 5.4 (Berkeley) 1/18/91
+ * $Id: tss.h,v 1.3 1993/11/07 17:43:16 wollman Exp $
+ */
+
+#ifndef _MACHINE_TSS_H_
+#define _MACHINE_TSS_H_ 1
+
+/*
+ * Intel 386 Context Data Type
+ */
+
+struct i386tss {
+ int tss_link; /* actually 16 bits: top 16 bits must be zero */
+ int tss_esp0; /* kernel stack pointer priviledge level 0 */
+#define tss_ksp tss_esp0
+ int tss_ss0; /* actually 16 bits: top 16 bits must be zero */
+ int tss_esp1; /* kernel stack pointer priviledge level 1 */
+ int tss_ss1; /* actually 16 bits: top 16 bits must be zero */
+ int tss_esp2; /* kernel stack pointer priviledge level 2 */
+ int tss_ss2; /* actually 16 bits: top 16 bits must be zero */
+ int tss_cr3; /* page table directory */
+#define tss_ptd tss_cr3
+ int tss_eip; /* program counter */
+#define tss_pc tss_eip
+ int tss_eflags; /* program status longword */
+#define tss_psl tss_eflags
+ int tss_eax;
+ int tss_ecx;
+ int tss_edx;
+ int tss_ebx;
+ int tss_esp; /* user stack pointer */
+#define tss_usp tss_esp
+ int tss_ebp; /* user frame pointer */
+#define tss_fp tss_ebp
+ int tss_esi;
+ int tss_edi;
+ int tss_es; /* actually 16 bits: top 16 bits must be zero */
+ int tss_cs; /* actually 16 bits: top 16 bits must be zero */
+ int tss_ss; /* actually 16 bits: top 16 bits must be zero */
+ int tss_ds; /* actually 16 bits: top 16 bits must be zero */
+ int tss_fs; /* actually 16 bits: top 16 bits must be zero */
+ int tss_gs; /* actually 16 bits: top 16 bits must be zero */
+ int tss_ldt; /* actually 16 bits: top 16 bits must be zero */
+ int tss_ioopt; /* options & io offset bitmap: currently zero */
+ /* XXX unimplemented .. i/o permission bitmap */
+};
+#endif /* _MACHINE_TSS_H_ */
diff --git a/sys/amd64/include/varargs.h b/sys/amd64/include/varargs.h
new file mode 100644
index 0000000..1a913ec
--- /dev/null
+++ b/sys/amd64/include/varargs.h
@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)varargs.h 8.2 (Berkeley) 3/22/94
+ */
+
+#ifndef _VARARGS_H_
+#define _VARARGS_H_
+
+typedef char *va_list;
+
+#define va_dcl int va_alist;
+
+#define va_start(ap) \
+ ap = (char *)&va_alist
+
+#ifdef KERNEL
+#define va_arg(ap, type) \
+ ((type *)(ap += sizeof(type)))[-1]
+#else
+#define va_arg(ap, type) \
+ ((type *)(ap += sizeof(type) < sizeof(int) ? \
+ (abort(), 0) : sizeof(type)))[-1]
+#endif
+
+#define va_end(ap)
+
+#endif /* !_VARARGS_H_ */
diff --git a/sys/amd64/include/vmparam.h b/sys/amd64/include/vmparam.h
new file mode 100644
index 0000000..05218ad
--- /dev/null
+++ b/sys/amd64/include/vmparam.h
@@ -0,0 +1,263 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ * Copyright (c) 1994 John S. Dyson
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)vmparam.h 5.9 (Berkeley) 5/12/91
+ * $Id: vmparam.h,v 1.11 1994/01/14 16:24:00 davidg Exp $
+ */
+
+
+#ifndef _MACHINE_VMPARAM_H_
+#define _MACHINE_VMPARAM_H_ 1
+
+/*
+ * Machine dependent constants for 386.
+ */
+
+/*
+ * Virtual address space arrangement. On 386, both user and kernel
+ * share the address space, not unlike the vax.
+ * USRTEXT is the start of the user text/data space, while USRSTACK
+ * is the top (end) of the user stack. Immediately above the user stack
+ * resides the user structure, which is UPAGES long and contains the
+ * kernel stack.
+ *
+ * Immediately after the user structure is the page table map, and then
+ * kernal address space.
+ */
+#define USRTEXT 0UL
+/* #define USRSTACK 0xFDBFE000UL */
+#define BTOPUSRSTACK (0xFDC00-(UPAGES)) /* btop(USRSTACK) */
+#define LOWPAGES 0UL
+#define HIGHPAGES UPAGES
+
+/*
+ * Virtual memory related constants, all in bytes
+ */
+#define MAXTSIZ (16UL*1024*1024) /* max text size */
+#ifndef DFLDSIZ
+#define DFLDSIZ (64UL*1024*1024) /* initial data size limit */
+#endif
+#ifndef MAXDSIZ
+#define MAXDSIZ (128UL*1024*1024) /* max data size */
+#endif
+#ifndef DFLSSIZ
+#define DFLSSIZ (8UL*1024*1024) /* initial stack size limit */
+#endif
+#ifndef MAXSSIZ
+#define MAXSSIZ (64UL*1024*1024) /* max stack size */
+#endif
+#ifndef SGROWSIZ
+#define SGROWSIZ (128UL*1024) /* amount to grow stack */
+#endif
+
+/*
+ * Default sizes of swap allocation chunks (see dmap.h).
+ * The actual values may be changed in vminit() based on MAXDSIZ.
+ * With MAXDSIZ of 16Mb and NDMAP of 38, dmmax will be 1024.
+ */
+#define DMMIN 32 /* smallest swap allocation */
+#define DMMAX 4096 /* largest potential swap allocation */
+#define DMTEXT 1024 /* swap allocation for text */
+
+/*
+ * Sizes of the system and user portions of the system page table.
+ */
+#define SYSPTSIZE (2*NPTEPG)
+#define USRPTSIZE (2*NPTEPG)
+
+/*
+ * Size of the Shared Memory Pages page table.
+ */
+#ifndef SHMMAXPGS
+#define SHMMAXPGS 512 /* XXX until we have more kmap space */
+#endif
+
+/*
+ * Size of User Raw I/O map
+ */
+#define USRIOSIZE 1024
+
+/*
+ * The size of the clock loop.
+ */
+#define LOOPPAGES (maxfree - firstfree)
+
+/*
+ * The time for a process to be blocked before being very swappable.
+ * This is a number of seconds which the system takes as being a non-trivial
+ * amount of real time. You probably shouldn't change this;
+ * it is used in subtle ways (fractions and multiples of it are, that is, like
+ * half of a ``long time'', almost a long time, etc.)
+ * It is related to human patience and other factors which don't really
+ * change over time.
+ */
+#define MAXSLP 20
+
+/*
+ * A swapped in process is given a small amount of core without being bothered
+ * by the page replacement algorithm. Basically this says that if you are
+ * swapped in you deserve some resources. We protect the last SAFERSS
+ * pages against paging and will just swap you out rather than paging you.
+ * Note that each process has at least UPAGES+CLSIZE pages which are not
+ * paged anyways (this is currently 8+2=10 pages or 5k bytes), so this
+ * number just means a swapped in process is given around 25k bytes.
+ * Just for fun: current memory prices are 4600$ a megabyte on VAX (4/22/81),
+ * so we loan each swapped in process memory worth 100$, or just admit
+ * that we don't consider it worthwhile and swap it out to disk which costs
+ * $30/mb or about $0.75.
+ * { wfj 6/16/89: Retail AT memory expansion $800/megabyte, loan of $17
+ * on disk costing $7/mb or $0.18 (in memory still 100:1 in cost!) }
+ */
+#define SAFERSS 8 /* nominal ``small'' resident set size
+ protected against replacement */
+
+/*
+ * DISKRPM is used to estimate the number of paging i/o operations
+ * which one can expect from a single disk controller.
+ */
+#define DISKRPM 60
+
+/*
+ * Klustering constants. Klustering is the gathering
+ * of pages together for pagein/pageout, while clustering
+ * is the treatment of hardware page size as though it were
+ * larger than it really is.
+ *
+ * KLMAX gives maximum cluster size in CLSIZE page (cluster-page)
+ * units. Note that KLMAX*CLSIZE must be <= DMMIN in dmap.h.
+ */
+
+#define KLMAX (4/CLSIZE)
+#define KLSEQL (2/CLSIZE) /* in klust if vadvise(VA_SEQL) */
+#define KLIN (4/CLSIZE) /* default data/stack in klust */
+#define KLTXT (4/CLSIZE) /* default text in klust */
+#define KLOUT (4/CLSIZE)
+
+/*
+ * KLSDIST is the advance or retard of the fifo reclaim for sequential
+ * processes data space.
+ */
+#define KLSDIST 3 /* klusters advance/retard for seq. fifo */
+
+/*
+ * There are two clock hands, initially separated by HANDSPREAD bytes
+ * (but at most all of user memory). The amount of time to reclaim
+ * a page once the pageout process examines it increases with this
+ * distance and decreases as the scan rate rises.
+ */
+#define HANDSPREAD (2 * 1024 * 1024)
+
+/*
+ * The number of times per second to recompute the desired paging rate
+ * and poke the pagedaemon.
+ */
+#define RATETOSCHEDPAGING 4
+
+/*
+ * Believed threshold (in megabytes) for which interleaved
+ * swapping area is desirable.
+ */
+#define LOTSOFMEM 2
+
+#define mapin(pte, v, pfnum, prot) \
+ {(*(int *)(pte) = ((pfnum)<<PGSHIFT) | (prot)) ; }
+
+/*
+ * Mach derived constants
+ */
+
+/* user/kernel map constants */
+#define KERNBASE (0-(NKPDE+1)*(NBPG*NPTEPG))
+#define KERNSIZE (NKPDE*NBPG*NPTEPG)
+
+#define VM_MIN_ADDRESS ((vm_offset_t)0)
+#define VM_MAXUSER_ADDRESS ((vm_offset_t)KERNBASE - (NBPG*(NPTEPG+UPAGES)))
+#define USRSTACK VM_MAXUSER_ADDRESS
+#define UPT_MIN_ADDRESS ((vm_offset_t)KERNBASE - (NBPG*NPTEPG))
+#define UPT_MAX_ADDRESS ((vm_offset_t)KERNBASE - (NBPG*(NKPDE+2)))
+#define VM_MAX_ADDRESS UPT_MAX_ADDRESS
+#define VM_MIN_KERNEL_ADDRESS ((vm_offset_t)KERNBASE - (NBPG*(NKPDE+2)))
+#define UPDT VM_MIN_KERNEL_ADDRESS
+#define KPT_MIN_ADDRESS ((vm_offset_t)(KERNBASE) - (NBPG*(NKPDE+1)))
+#define KPT_MAX_ADDRESS ((vm_offset_t)(KERNBASE) - NBPG)
+#define VM_MAX_KERNEL_ADDRESS ((vm_offset_t)ALT_MIN_ADDRESS - NBPG)
+#define ALT_MIN_ADDRESS ((vm_offset_t)((APTDPTDI) << 22))
+#define HIGHPAGES UPAGES
+
+
+/* virtual sizes (bytes) for various kernel submaps */
+#define VM_MBUF_SIZE (NMBCLUSTERS*MCLBYTES)
+#define VM_KMEM_SIZE (16 * 1024 * 1024)
+#define VM_PHYS_SIZE (USRIOSIZE*CLBYTES)
+
+/* pcb base */
+#define pcbb(p) ((u_int)(p)->p_addr)
+
+/*
+ * Flush MMU TLB
+ */
+
+#ifndef I386_CR3PAT
+#define I386_CR3PAT 0x0
+#endif
+
+#ifdef notyet
+#define _cr3() ({u_long rtn; \
+ asm (" movl %%cr3,%%eax; movl %%eax,%0 " \
+ : "=g" (rtn) \
+ : \
+ : "ax"); \
+ rtn; \
+})
+
+#define load_cr3(s) ({ u_long val; \
+ val = (s) | I386_CR3PAT; \
+ asm ("movl %0,%%eax; movl %%eax,%%cr3" \
+ : \
+ : "g" (val) \
+ : "ax"); \
+})
+
+#define tlbflush() ({ u_long val; \
+ val = u.u_pcb.pcb_ptd | I386_CR3PAT; \
+ asm ("movl %0,%%eax; movl %%eax,%%cr3" \
+ : \
+ : "g" (val) \
+ : "ax"); \
+})
+#endif
+#endif /* _MACHINE_VMPARAM_H_ */
diff --git a/sys/amd64/isa/clock.c b/sys/amd64/isa/clock.c
new file mode 100644
index 0000000..e40079a
--- /dev/null
+++ b/sys/amd64/isa/clock.c
@@ -0,0 +1,442 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz and Don Ahn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)clock.c 7.2 (Berkeley) 5/12/91
+ * $Id: clock.c,v 1.6 1994/02/06 22:48:13 davidg Exp $
+ */
+
+/*
+ * Primitive clock interrupt routines.
+ */
+#include "param.h"
+#include "systm.h"
+#include "time.h"
+#include "kernel.h"
+#include "machine/segments.h"
+#include "machine/frame.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/rtc.h"
+#include "i386/isa/timerreg.h"
+#include <machine/cpu.h>
+
+/* X-tals being what they are, it's nice to be able to fudge this one... */
+/* Note, the name changed here from XTALSPEED to TIMER_FREQ rgrimes 4/26/93 */
+#ifndef TIMER_FREQ
+#define TIMER_FREQ 1193182 /* XXX - should be in isa.h */
+#endif
+#define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x))
+
+void hardclock();
+static int beeping;
+int timer0_divisor = TIMER_DIV(100); /* XXX should be hz */
+u_int timer0_prescale;
+static char timer0_state = 0, timer2_state = 0;
+static char timer0_reprogram = 0;
+static void (*timer_func)() = hardclock;
+static void (*new_function)();
+static u_int new_rate;
+static u_int hardclock_divisor;
+
+
+void
+clkintr(frame)
+ struct clockframe frame;
+{
+ hardclock(&frame);
+}
+
+#if 0
+void
+timerintr(struct clockframe frame)
+{
+ timer_func(&frame);
+ switch (timer0_state) {
+ case 0:
+ break;
+ case 1:
+ if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) {
+ hardclock(&frame);
+ timer0_prescale = 0;
+ }
+ break;
+ case 2:
+ disable_intr();
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+ outb(TIMER_CNTR0, TIMER_DIV(new_rate)%256);
+ outb(TIMER_CNTR0, TIMER_DIV(new_rate)/256);
+ enable_intr();
+ timer0_divisor = TIMER_DIV(new_rate);
+ timer0_prescale = 0;
+ timer_func = new_function;
+ timer0_state = 1;
+ break;
+ case 3:
+ if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) {
+ hardclock(&frame);
+ disable_intr();
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+ outb(TIMER_CNTR0, TIMER_DIV(hz)%256);
+ outb(TIMER_CNTR0, TIMER_DIV(hz)/256);
+ enable_intr();
+ timer0_divisor = TIMER_DIV(hz);
+ timer0_prescale = 0;
+ timer_func = hardclock;;
+ timer0_state = 0;
+ }
+ break;
+ }
+}
+
+#endif
+
+int
+acquire_timer0(int rate, void (*function)() )
+{
+ if (timer0_state || !function)
+ return -1;
+
+ new_function = function;
+ new_rate = rate;
+ timer0_state = 2;
+ return 0;
+}
+
+
+int
+acquire_timer2(int mode)
+{
+ if (timer2_state)
+ return -1;
+ timer2_state = 1;
+ outb(TIMER_MODE, TIMER_SEL2 | (mode &0x3f));
+ return 0;
+}
+
+
+int
+release_timer0()
+{
+ if (!timer0_state)
+ return -1;
+ timer0_state = 3;
+ return 0;
+}
+
+
+int
+release_timer2()
+{
+ if (!timer2_state)
+ return -1;
+ timer2_state = 0;
+ outb(TIMER_MODE, TIMER_SEL2|TIMER_SQWAVE|TIMER_16BIT);
+ return 0;
+}
+
+
+static int
+getit()
+{
+ int high, low;
+
+ disable_intr();
+ /* select timer0 and latch counter value */
+ outb(TIMER_MODE, TIMER_SEL0);
+ low = inb(TIMER_CNTR0);
+ high = inb(TIMER_CNTR0);
+ enable_intr();
+ return ((high << 8) | low);
+}
+
+
+/*
+ * Wait "n" microseconds.
+ * Relies on timer 1 counting down from (TIMER_FREQ / hz)
+ * Note: timer had better have been programmed before this is first used!
+ */
+void
+DELAY(int n)
+{
+ int counter_limit, prev_tick, tick, ticks_left, sec, usec;
+
+#ifdef DELAYDEBUG
+ int getit_calls = 1;
+ int n1;
+ static int state = 0;
+
+ if (state == 0) {
+ state = 1;
+ for (n1 = 1; n1 <= 10000000; n1 *= 10)
+ DELAY(n1);
+ state = 2;
+ }
+ if (state == 1)
+ printf("DELAY(%d)...", n);
+#endif
+ /*
+ * Read the counter first, so that the rest of the setup overhead is
+ * counted. Guess the initial overhead is 20 usec (on most systems it
+ * takes about 1.5 usec for each of the i/o's in getit(). The loop
+ * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The
+ * multiplications and divisions to scale the count take a while).
+ */
+ prev_tick = getit(0, 0);
+ n -= 20;
+ /*
+ * Calculate (n * (TIMER_FREQ / 1e6)) without using floating point
+ * and without any avoidable overflows.
+ */
+ sec = n / 1000000;
+ usec = n - sec * 1000000;
+ ticks_left = sec * TIMER_FREQ
+ + usec * (TIMER_FREQ / 1000000)
+ + usec * ((TIMER_FREQ % 1000000) / 1000) / 1000
+ + usec * (TIMER_FREQ % 1000) / 1000000;
+
+ while (ticks_left > 0) {
+ tick = getit(0, 0);
+#ifdef DELAYDEBUG
+ ++getit_calls;
+#endif
+ if (tick > prev_tick)
+ ticks_left -= prev_tick - (tick - timer0_divisor);
+ else
+ ticks_left -= prev_tick - tick;
+ prev_tick = tick;
+ }
+#ifdef DELAYDEBUG
+ if (state == 1)
+ printf(" %d calls to getit() at %d usec each\n",
+ getit_calls, (n + 5) / getit_calls);
+#endif
+}
+
+
+static void
+sysbeepstop()
+{
+ outb(IO_PPI, inb(IO_PPI)&0xFC); /* disable counter2 output to speaker */
+ release_timer2();
+ beeping = 0;
+}
+
+
+int
+sysbeep(int pitch, int period)
+{
+
+ if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT))
+ return -1;
+ disable_intr();
+ outb(TIMER_CNTR2, pitch);
+ outb(TIMER_CNTR2, (pitch>>8));
+ enable_intr();
+ if (!beeping) {
+ outb(IO_PPI, inb(IO_PPI) | 3); /* enable counter2 output to speaker */
+ beeping = period;
+ timeout(sysbeepstop, 0, period);
+ }
+ return 0;
+}
+
+
+void
+startrtclock()
+{
+ int s;
+
+ /* initialize 8253 clock */
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+
+ /* Correct rounding will buy us a better precision in timekeeping */
+ outb (IO_TIMER1, TIMER_DIV(hz)%256);
+ outb (IO_TIMER1, TIMER_DIV(hz)/256);
+ timer0_divisor = hardclock_divisor = TIMER_DIV(hz);
+
+ /* initialize brain-dead battery powered clock */
+ outb (IO_RTC, RTC_STATUSA);
+ outb (IO_RTC+1, 0x26);
+ outb (IO_RTC, RTC_STATUSB);
+ outb (IO_RTC+1, 2);
+
+ outb (IO_RTC, RTC_DIAG);
+ if (s = inb (IO_RTC+1))
+ printf("RTC BIOS diagnostic error %b\n", s, RTCDG_BITS);
+}
+
+
+/* convert 2 digit BCD number */
+int
+bcd(int i)
+{
+ return ((i/16)*10 + (i%16));
+}
+
+
+/* convert years to seconds (from 1970) */
+unsigned long
+ytos(int y)
+{
+ int i;
+ unsigned long ret;
+
+ ret = 0;
+ for(i = 1970; i < y; i++) {
+ if (i % 4) ret += 365*24*60*60;
+ else ret += 366*24*60*60;
+ }
+ return ret;
+}
+
+
+/* convert months to seconds */
+unsigned long
+mtos(int m, int leap)
+{
+ int i;
+ unsigned long ret;
+
+ ret = 0;
+ for(i=1; i<m; i++) {
+ switch(i){
+ case 1: case 3: case 5: case 7: case 8: case 10: case 12:
+ ret += 31*24*60*60; break;
+ case 4: case 6: case 9: case 11:
+ ret += 30*24*60*60; break;
+ case 2:
+ if (leap) ret += 29*24*60*60;
+ else ret += 28*24*60*60;
+ }
+ }
+ return ret;
+}
+
+
+/*
+ * Initialize the time of day register, based on the time base which is, e.g.
+ * from a filesystem.
+ */
+void
+inittodr(time_t base)
+{
+ unsigned long sec;
+ int leap, day_week, t, yd;
+ int sa,s;
+
+ /* do we have a realtime clock present? (otherwise we loop below) */
+ sa = rtcin(RTC_STATUSA);
+ if (sa == 0xff || sa == 0) return;
+
+ /* ready for a read? */
+ while ((sa&RTCSA_TUP) == RTCSA_TUP)
+ sa = rtcin(RTC_STATUSA);
+
+ sec = bcd(rtcin(RTC_YEAR)) + 1900;
+ if (sec < 1970)
+ sec += 100;
+
+ leap = !(sec % 4); sec = ytos(sec); /* year */
+ yd = mtos(bcd(rtcin(RTC_MONTH)),leap); sec+=yd; /* month */
+ t = (bcd(rtcin(RTC_DAY))-1) * 24*60*60; sec+=t; yd+=t; /* date */
+ day_week = rtcin(RTC_WDAY); /* day */
+ sec += bcd(rtcin(RTC_HRS)) * 60*60; /* hour */
+ sec += bcd(rtcin(RTC_MIN)) * 60; /* minutes */
+ sec += bcd(rtcin(RTC_SEC)); /* seconds */
+ sec += tz.tz_minuteswest * 60;
+ time.tv_sec = sec;
+}
+
+
+#ifdef garbage
+/*
+ * Initialze the time of day register, based on the time base which is, e.g.
+ * from a filesystem.
+ */
+test_inittodr(time_t base)
+{
+
+ outb(IO_RTC,9); /* year */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,8); /* month */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,7); /* day */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,4); /* hour */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,2); /* minutes */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,0); /* seconds */
+ printf("%d\n",bcd(inb(IO_RTC+1)));
+
+ time.tv_sec = base;
+}
+#endif
+
+/*
+ * Wire clock interrupt in.
+ */
+#define V(s) __CONCAT(V, s)
+extern void V(clk)();
+
+
+void
+enablertclock()
+{
+ setidt(ICU_OFFSET+0, &V(clk), SDT_SYS386IGT, SEL_KPL);
+ INTREN(IRQ0);
+}
+
+
+/*
+ * Delay for some number of milliseconds.
+ */
+void
+spinwait(int millisecs)
+{
+ DELAY(1000 * millisecs);
+}
+
+void
+cpu_initclocks()
+{
+ startrtclock();
+ enablertclock();
+}
+
+void
+setstatclockrate(int newhz)
+{
+}
diff --git a/sys/amd64/isa/icu.h b/sys/amd64/isa/icu.h
new file mode 100644
index 0000000..13216b0
--- /dev/null
+++ b/sys/amd64/isa/icu.h
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)icu.h 5.6 (Berkeley) 5/9/91
+ * $Id: icu.h,v 1.2 1993/10/16 13:45:51 rgrimes Exp $
+ */
+
+/*
+ * AT/386 Interrupt Control constants
+ * W. Jolitz 8/89
+ */
+
+#ifndef __ICU__
+#define __ICU__
+
+#ifndef LOCORE
+
+/*
+ * Interrupt "level" mechanism variables, masks, and macros
+ */
+extern unsigned imen; /* interrupt mask enable */
+
+#define INTREN(s) (imen &= ~(s), SET_ICUS())
+#define INTRDIS(s) (imen |= (s), SET_ICUS())
+#define INTRMASK(msk,s) (msk |= (s))
+#if 0
+#define SET_ICUS() (outb(IO_ICU1 + 1, imen), outb(IU_ICU2 + 1, imen >> 8))
+#else
+/*
+ * XXX - IO_ICU* are defined in isa.h, not icu.h, and nothing much bothers to
+ * include isa.h, while too many things include icu.h.
+ */
+#define SET_ICUS() (outb(0x21, imen), outb(0xa1, imen >> 8))
+#endif
+
+#endif
+
+/*
+ * Interrupt enable bits - in normal order of priority (which we change)
+ */
+#define IRQ0 0x0001 /* highest priority - timer */
+#define IRQ1 0x0002
+#define IRQ_SLAVE 0x0004
+#define IRQ8 0x0100
+#define IRQ9 0x0200
+#define IRQ2 IRQ9
+#define IRQ10 0x0400
+#define IRQ11 0x0800
+#define IRQ12 0x1000
+#define IRQ13 0x2000
+#define IRQ14 0x4000
+#define IRQ15 0x8000
+#define IRQ3 0x0008 /* this is highest after rotation */
+#define IRQ4 0x0010
+#define IRQ5 0x0020
+#define IRQ6 0x0040
+#define IRQ7 0x0080 /* lowest - parallel printer */
+
+/*
+ * Interrupt Control offset into Interrupt descriptor table (IDT)
+ */
+#define ICU_OFFSET 32 /* 0-31 are processor exceptions */
+#define ICU_LEN 16 /* 32-47 are ISA interrupts */
+
+#endif __ICU__
diff --git a/sys/amd64/isa/isa.c b/sys/amd64/isa/isa.c
new file mode 100644
index 0000000..32e59e7
--- /dev/null
+++ b/sys/amd64/isa/isa.c
@@ -0,0 +1,671 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)isa.c 7.2 (Berkeley) 5/13/91
+ * $Id: isa.c,v 1.16 1994/04/02 20:43:25 ache Exp $
+ */
+
+/*
+ * code to manage AT bus
+ *
+ * 92/08/18 Frank P. MacLachlan (fpm@crash.cts.com):
+ * Fixed uninitialized variable problem and added code to deal
+ * with DMA page boundaries in isa_dmarangecheck(). Fixed word
+ * mode DMA count compution and reorganized DMA setup code in
+ * isa_dmastart()
+ */
+
+#include "param.h"
+#include "systm.h" /* isn't it a joy */
+#include "kernel.h" /* to have three of these */
+#include "conf.h"
+#include "file.h"
+#include "buf.h"
+#include "uio.h"
+#include "syslog.h"
+#include "malloc.h"
+#include "rlist.h"
+#include "machine/segments.h"
+#include "vm/vm.h"
+#include <machine/spl.h>
+#include "i386/isa/isa_device.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/ic/i8237.h"
+#include "i386/isa/ic/i8042.h"
+
+/*
+** Register definitions for DMA controller 1 (channels 0..3):
+*/
+#define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */
+#define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */
+#define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */
+#define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */
+
+/*
+** Register definitions for DMA controller 2 (channels 4..7):
+*/
+#define DMA2_CHN(c) (IO_DMA2 + 2*(2*(c))) /* addr reg for channel c */
+#define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */
+#define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */
+#define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */
+
+void config_isadev __P((struct isa_device *, u_int *));
+
+/*
+ * print a conflict message
+ */
+void
+conflict(dvp, tmpdvp, item, reason, format)
+ struct isa_device *dvp, *tmpdvp;
+ int item;
+ char *reason;
+ char *format;
+{
+ printf("%s%d not probed due to %s conflict with %s%d at ",
+ dvp->id_driver->name, dvp->id_unit, reason,
+ tmpdvp->id_driver->name, tmpdvp->id_unit);
+ printf(format, item);
+ printf("\n");
+}
+
+/*
+ * Check to see if things are alread in use, like IRQ's, I/O addresses
+ * and Memory addresses.
+ */
+int
+haveseen(dvp, tmpdvp)
+ struct isa_device *dvp, *tmpdvp;
+{
+ int status = 0;
+
+ /*
+ * Only check against devices that have already been found
+ */
+ if (tmpdvp->id_alive) {
+ /*
+ * Check for I/O address conflict. We can only check the
+ * starting address of the device against the range of the
+ * device that has already been probed since we do not
+ * know how many I/O addresses this device uses.
+ */
+ if (tmpdvp->id_alive != -1) {
+ if ((dvp->id_iobase >= tmpdvp->id_iobase) &&
+ (dvp->id_iobase <=
+ (tmpdvp->id_iobase + tmpdvp->id_alive - 1))) {
+ conflict(dvp, tmpdvp, dvp->id_iobase,
+ "I/O address", "0x%x");
+ status = 1;
+ }
+ }
+ /*
+ * Check for Memory address conflict. We can check for
+ * range overlap, but it will not catch all cases since the
+ * driver may adjust the msize paramater during probe, for
+ * now we just check that the starting address does not
+ * fall within any allocated region.
+ * XXX could add a second check after the probe for overlap,
+ * since at that time we would know the full range.
+ * XXX KERNBASE is a hack, we should have vaddr in the table!
+ */
+ if(tmpdvp->id_maddr) {
+ if((KERNBASE + dvp->id_maddr >= tmpdvp->id_maddr) &&
+ (KERNBASE + dvp->id_maddr <=
+ (tmpdvp->id_maddr + tmpdvp->id_msize - 1))) {
+ conflict(dvp, tmpdvp, dvp->id_maddr, "maddr",
+ "0x%x");
+ status = 1;
+ }
+ }
+#ifndef COM_MULTIPORT
+ /*
+ * Check for IRQ conflicts.
+ */
+ if(tmpdvp->id_irq) {
+ if (tmpdvp->id_irq == dvp->id_irq) {
+ conflict(dvp, tmpdvp, ffs(dvp->id_irq) - 1,
+ "irq", "%d");
+ status = 1;
+ }
+ }
+#endif
+ /*
+ * Check for DRQ conflicts.
+ */
+ if(tmpdvp->id_drq != -1) {
+ if (tmpdvp->id_drq == dvp->id_drq) {
+ conflict(dvp, tmpdvp, dvp->id_drq,
+ "drq", "%d");
+ status = 1;
+ }
+ }
+ }
+ return (status);
+}
+
+/*
+ * Search through all the isa_devtab_* tables looking for anything that
+ * conflicts with the current device.
+ */
+int
+haveseen_isadev(dvp)
+ struct isa_device *dvp;
+{
+ struct isa_device *tmpdvp;
+ int status = 0;
+
+ for (tmpdvp = isa_devtab_tty; tmpdvp->id_driver; tmpdvp++) {
+ status |= haveseen(dvp, tmpdvp);
+ }
+ for (tmpdvp = isa_devtab_bio; tmpdvp->id_driver; tmpdvp++) {
+ status |= haveseen(dvp, tmpdvp);
+ }
+ for (tmpdvp = isa_devtab_net; tmpdvp->id_driver; tmpdvp++) {
+ status |= haveseen(dvp, tmpdvp);
+ }
+ for (tmpdvp = isa_devtab_null; tmpdvp->id_driver; tmpdvp++) {
+ status |= haveseen(dvp, tmpdvp);
+ }
+ return(status);
+}
+
+/*
+ * Configure all ISA devices
+ */
+void
+isa_configure() {
+ struct isa_device *dvp;
+
+ enable_intr();
+ splhigh();
+ INTREN(IRQ_SLAVE);
+ printf("Probing for devices on the ISA bus:\n");
+ for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) {
+ if (!haveseen_isadev(dvp))
+ config_isadev(dvp,&tty_imask);
+ }
+ for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) {
+ if (!haveseen_isadev(dvp))
+ config_isadev(dvp,&bio_imask);
+ }
+ for (dvp = isa_devtab_net; dvp->id_driver; dvp++) {
+ if (!haveseen_isadev(dvp))
+ config_isadev(dvp,&net_imask);
+ }
+ for (dvp = isa_devtab_null; dvp->id_driver; dvp++) {
+ if (!haveseen_isadev(dvp))
+ config_isadev(dvp,(u_int *) NULL);
+ }
+ bio_imask |= SWI_CLOCK_MASK;
+ net_imask |= SWI_NET_MASK;
+ tty_imask |= SWI_TTY_MASK;
+
+/*
+ * XXX we should really add the tty device to net_imask when the line is
+ * switched to SLIPDISC, and then remove it when it is switched away from
+ * SLIPDISC. No need to block out ALL ttys during a splimp when only one
+ * of them is running slip.
+ *
+ * XXX actually, blocking all ttys during a splimp doesn't matter so much
+ * with sio because the serial interrupt layer doesn't use tty_imask. Only
+ * non-serial ttys suffer. It's more stupid that ALL 'net's are blocked
+ * during spltty.
+ */
+#include "sl.h"
+#if NSL > 0
+ net_imask |= tty_imask;
+ tty_imask = net_imask;
+#endif
+ /* bio_imask |= tty_imask ; can some tty devices use buffers? */
+#ifdef DIAGNOSTIC
+ printf("bio_imask %x tty_imask %x net_imask %x\n",
+ bio_imask, tty_imask, net_imask);
+#endif
+ splnone();
+}
+
+/*
+ * Configure an ISA device.
+ */
+void
+config_isadev(isdp, mp)
+ struct isa_device *isdp;
+ u_int *mp;
+{
+ struct isa_driver *dp = isdp->id_driver;
+
+ if (isdp->id_maddr) {
+ extern u_int atdevbase;
+
+ isdp->id_maddr -= 0xa0000; /* XXX should be a define */
+ isdp->id_maddr += atdevbase;
+ }
+ isdp->id_alive = (*dp->probe)(isdp);
+ if (isdp->id_alive) {
+ /*
+ * Only print the I/O address range if id_alive != -1
+ * Right now this is a temporary fix just for the new
+ * NPX code so that if it finds a 486 that can use trap
+ * 16 it will not report I/O addresses.
+ * Rod Grimes 04/26/94
+ */
+ printf("%s%d", dp->name, isdp->id_unit);
+ if (isdp->id_alive != -1) {
+ printf(" at 0x%x", isdp->id_iobase);
+ if ((isdp->id_iobase + isdp->id_alive - 1) !=
+ isdp->id_iobase) {
+ printf("-0x%x",
+ isdp->id_iobase +
+ isdp->id_alive - 1);
+ }
+ }
+ if(isdp->id_irq)
+ printf(" irq %d", ffs(isdp->id_irq) - 1);
+ if (isdp->id_drq != -1)
+ printf(" drq %d", isdp->id_drq);
+ if (isdp->id_maddr)
+ printf(" maddr 0x%x", kvtop(isdp->id_maddr));
+ if (isdp->id_msize)
+ printf(" msize %d", isdp->id_msize);
+ if (isdp->id_flags)
+ printf(" flags 0x%x", isdp->id_flags);
+ if (isdp->id_iobase) {
+ if (isdp->id_iobase < 0x100) {
+ printf(" on motherboard\n");
+ } else {
+ if (isdp->id_iobase >= 0x1000) {
+ printf (" on eisa\n");
+ } else {
+ printf (" on isa\n");
+ }
+ }
+ }
+
+ (*dp->attach)(isdp);
+
+ if(isdp->id_irq) {
+ int intrno;
+
+ intrno = ffs(isdp->id_irq)-1;
+ setidt(ICU_OFFSET+intrno, isdp->id_intr,
+ SDT_SYS386IGT, SEL_KPL);
+ if(mp) {
+ INTRMASK(*mp,isdp->id_irq);
+ }
+ INTREN(isdp->id_irq);
+ }
+ } else {
+ printf("%s%d not found", dp->name, isdp->id_unit);
+ if (isdp->id_iobase) {
+ printf(" at 0x%x", isdp->id_iobase);
+ }
+ printf("\n");
+ }
+}
+
+#define IDTVEC(name) __CONCAT(X,name)
+/* default interrupt vector table entries */
+typedef void inthand_t();
+typedef void (*inthand_func_t)();
+extern inthand_t
+ IDTVEC(intr0), IDTVEC(intr1), IDTVEC(intr2), IDTVEC(intr3),
+ IDTVEC(intr4), IDTVEC(intr5), IDTVEC(intr6), IDTVEC(intr7),
+ IDTVEC(intr8), IDTVEC(intr9), IDTVEC(intr10), IDTVEC(intr11),
+ IDTVEC(intr12), IDTVEC(intr13), IDTVEC(intr14), IDTVEC(intr15);
+
+static inthand_func_t defvec[ICU_LEN] = {
+ &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3),
+ &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7),
+ &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11),
+ &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) };
+
+/*
+ * Fill in default interrupt table (in case of spuruious interrupt
+ * during configuration of kernel, setup interrupt control unit
+ */
+void
+isa_defaultirq()
+{
+ int i;
+
+ /* icu vectors */
+ for (i = 0; i < ICU_LEN; i++)
+ setidt(ICU_OFFSET + i, defvec[i], SDT_SYS386IGT, SEL_KPL);
+
+ /* initialize 8259's */
+ outb(IO_ICU1, 0x11); /* reset; program device, four bytes */
+ outb(IO_ICU1+1, NRSVIDT); /* starting at this vector index */
+ outb(IO_ICU1+1, 1<<2); /* slave on line 2 */
+#ifdef AUTO_EOI_1
+ outb(IO_ICU1+1, 2 | 1); /* auto EOI, 8086 mode */
+#else
+ outb(IO_ICU1+1, 1); /* 8086 mode */
+#endif
+ outb(IO_ICU1+1, 0xff); /* leave interrupts masked */
+ outb(IO_ICU1, 0x0a); /* default to IRR on read */
+ outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */
+
+ outb(IO_ICU2, 0x11); /* reset; program device, four bytes */
+ outb(IO_ICU2+1, NRSVIDT+8); /* staring at this vector index */
+ outb(IO_ICU2+1,2); /* my slave id is 2 */
+#ifdef AUTO_EOI_2
+ outb(IO_ICU2+1, 2 | 1); /* auto EOI, 8086 mode */
+#else
+ outb(IO_ICU2+1,1); /* 8086 mode */
+#endif
+ outb(IO_ICU2+1, 0xff); /* leave interrupts masked */
+ outb(IO_ICU2, 0x0a); /* default to IRR on read */
+}
+
+/* region of physical memory known to be contiguous */
+vm_offset_t isaphysmem;
+static caddr_t dma_bounce[8]; /* XXX */
+static char bounced[8]; /* XXX */
+#define MAXDMASZ 512 /* XXX */
+
+/* high byte of address is stored in this port for i-th dma channel */
+static short dmapageport[8] =
+ { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
+
+/*
+ * isa_dmacascade(): program 8237 DMA controller channel to accept
+ * external dma control by a board.
+ */
+void isa_dmacascade(unsigned chan)
+{
+ if (chan > 7)
+ panic("isa_dmacascade: impossible request");
+
+ /* set dma channel mode, and set dma channel mode */
+ if ((chan & 4) == 0) {
+ outb(DMA1_MODE, DMA37MD_CASCADE | chan);
+ outb(DMA1_SMSK, chan);
+ } else {
+ outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3));
+ outb(DMA2_SMSK, chan & 3);
+ }
+}
+
+/*
+ * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment
+ * problems by using a bounce buffer.
+ */
+void isa_dmastart(int flags, caddr_t addr, unsigned nbytes, unsigned chan)
+{ vm_offset_t phys;
+ int waport;
+ caddr_t newaddr;
+
+ if ( chan > 7
+ || (chan < 4 && nbytes > (1<<16))
+ || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1)))
+ panic("isa_dmastart: impossible request");
+
+ if (isa_dmarangecheck(addr, nbytes, chan)) {
+ if (dma_bounce[chan] == 0)
+ dma_bounce[chan] =
+ /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/
+ (caddr_t) isaphysmem + NBPG*chan;
+ bounced[chan] = 1;
+ newaddr = dma_bounce[chan];
+ *(int *) newaddr = 0; /* XXX */
+
+ /* copy bounce buffer on write */
+ if (!(flags & B_READ))
+ bcopy(addr, newaddr, nbytes);
+ addr = newaddr;
+ }
+
+ /* translate to physical */
+ phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr);
+
+ if ((chan & 4) == 0) {
+ /*
+ * Program one of DMA channels 0..3. These are
+ * byte mode channels.
+ */
+ /* set dma channel mode, and reset address ff */
+ if (flags & B_READ)
+ outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan);
+ else
+ outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan);
+ outb(DMA1_FFC, 0);
+
+ /* send start address */
+ waport = DMA1_CHN(chan);
+ outb(waport, phys);
+ outb(waport, phys>>8);
+ outb(dmapageport[chan], phys>>16);
+
+ /* send count */
+ outb(waport + 1, --nbytes);
+ outb(waport + 1, nbytes>>8);
+
+ /* unmask channel */
+ outb(DMA1_SMSK, chan);
+ } else {
+ /*
+ * Program one of DMA channels 4..7. These are
+ * word mode channels.
+ */
+ /* set dma channel mode, and reset address ff */
+ if (flags & B_READ)
+ outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3));
+ else
+ outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3));
+ outb(DMA2_FFC, 0);
+
+ /* send start address */
+ waport = DMA2_CHN(chan - 4);
+ outb(waport, phys>>1);
+ outb(waport, phys>>9);
+ outb(dmapageport[chan], phys>>16);
+
+ /* send count */
+ nbytes >>= 1;
+ outb(waport + 2, --nbytes);
+ outb(waport + 2, nbytes>>8);
+
+ /* unmask channel */
+ outb(DMA2_SMSK, chan & 3);
+ }
+}
+
+void isa_dmadone(int flags, caddr_t addr, int nbytes, int chan)
+{
+
+ /* copy bounce buffer on read */
+ /*if ((flags & (B_PHYS|B_READ)) == (B_PHYS|B_READ))*/
+ if (bounced[chan]) {
+ bcopy(dma_bounce[chan], addr, nbytes);
+ bounced[chan] = 0;
+ }
+}
+
+/*
+ * Check for problems with the address range of a DMA transfer
+ * (non-contiguous physical pages, outside of bus address space,
+ * crossing DMA page boundaries).
+ * Return true if special handling needed.
+ */
+
+int
+isa_dmarangecheck(caddr_t va, unsigned length, unsigned chan) {
+ vm_offset_t phys, priorpage = 0, endva;
+ u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1);
+
+ endva = (vm_offset_t)round_page(va + length);
+ for (; va < (caddr_t) endva ; va += NBPG) {
+ phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va));
+#define ISARAM_END RAM_END
+ if (phys == 0)
+ panic("isa_dmacheck: no physical page present");
+ if (phys >= ISARAM_END)
+ return (1);
+ if (priorpage) {
+ if (priorpage + NBPG != phys)
+ return (1);
+ /* check if crossing a DMA page boundary */
+ if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk)
+ return (1);
+ }
+ priorpage = phys;
+ }
+ return (0);
+}
+
+/* head of queue waiting for physmem to become available */
+struct buf isa_physmemq;
+
+/* blocked waiting for resource to become free for exclusive use */
+static isaphysmemflag;
+/* if waited for and call requested when free (B_CALL) */
+static void (*isaphysmemunblock)(); /* needs to be a list */
+
+/*
+ * Allocate contiguous physical memory for transfer, returning
+ * a *virtual* address to region. May block waiting for resource.
+ * (assumed to be called at splbio())
+ */
+caddr_t
+isa_allocphysmem(caddr_t va, unsigned length, void (*func)()) {
+
+ isaphysmemunblock = func;
+ while (isaphysmemflag & B_BUSY) {
+ isaphysmemflag |= B_WANTED;
+ tsleep((caddr_t)&isaphysmemflag, PRIBIO, "isaphys", 0);
+ }
+ isaphysmemflag |= B_BUSY;
+
+ return((caddr_t)isaphysmem);
+}
+
+/*
+ * Free contiguous physical memory used for transfer.
+ * (assumed to be called at splbio())
+ */
+void
+isa_freephysmem(caddr_t va, unsigned length) {
+
+ isaphysmemflag &= ~B_BUSY;
+ if (isaphysmemflag & B_WANTED) {
+ isaphysmemflag &= B_WANTED;
+ wakeup((caddr_t)&isaphysmemflag);
+ if (isaphysmemunblock)
+ (*isaphysmemunblock)();
+ }
+}
+
+/*
+ * Handle a NMI, possibly a machine check.
+ * return true to panic system, false to ignore.
+ */
+int
+isa_nmi(cd)
+ int cd;
+{
+
+ log(LOG_CRIT, "\nNMI port 61 %x, port 70 %x\n", inb(0x61), inb(0x70));
+ return(0);
+}
+
+/*
+ * Caught a stray interrupt, notify
+ */
+void
+isa_strayintr(d)
+ int d;
+{
+
+ /* DON'T BOTHER FOR NOW! */
+ /* for some reason, we get bursts of intr #7, even if not enabled! */
+ /*
+ * Well the reason you got bursts of intr #7 is because someone
+ * raised an interrupt line and dropped it before the 8259 could
+ * prioritize it. This is documented in the intel data book. This
+ * means you have BAD hardware! I have changed this so that only
+ * the first 5 get logged, then it quits logging them, and puts
+ * out a special message. rgrimes 3/25/1993
+ */
+ extern u_long intrcnt_stray;
+
+ intrcnt_stray++;
+ if (intrcnt_stray <= 5)
+ log(LOG_ERR,"ISA strayintr %x\n", d);
+ if (intrcnt_stray == 5)
+ log(LOG_CRIT,"Too many ISA strayintr not logging any more\n");
+}
+
+/*
+ * find an ISA device in a given isa_devtab_* table, given
+ * the table to search, the expected id_driver entry, and the unit number.
+ *
+ * this function is defined in isa_device.h, and this location is debatable;
+ * i put it there because it's useless w/o, and directly operates on
+ * the other stuff in that file.
+ *
+ */
+
+struct isa_device *find_isadev(table, driverp, unit)
+ struct isa_device *table;
+ struct isa_driver *driverp;
+ int unit;
+{
+ if (driverp == NULL) /* sanity check */
+ return NULL;
+
+ while ((table->id_driver != driverp) || (table->id_unit != unit)) {
+ if (table->id_driver == 0)
+ return NULL;
+
+ table++;
+ }
+
+ return table;
+}
+
+/*
+ * Return nonzero if a (masked) irq is pending for a given device.
+ */
+int
+isa_irq_pending(dvp)
+ struct isa_device *dvp;
+{
+ unsigned id_irq;
+
+ id_irq = (unsigned short) dvp->id_irq; /* XXX silly type in struct */
+ if (id_irq & 0xff)
+ return (inb(IO_ICU1) & id_irq);
+ return (inb(IO_ICU2) & (id_irq >> 8));
+}
diff --git a/sys/amd64/isa/isa.h b/sys/amd64/isa/isa.h
new file mode 100644
index 0000000..e2a26e7
--- /dev/null
+++ b/sys/amd64/isa/isa.h
@@ -0,0 +1,181 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)isa.h 5.7 (Berkeley) 5/9/91
+ * $Id: isa.h,v 1.4 1994/01/05 15:03:28 rgrimes Exp $
+ */
+
+#ifndef _I386_ISA_ISA_H_
+#define _I386_ISA_ISA_H_ 1
+
+/*
+ * ISA Bus conventions
+ */
+
+#ifndef LOCORE
+#include <sys/cdefs.h>
+
+extern unsigned int atdevbase; /* offset in virtual memory of ISA io mem */
+unsigned char rtcin __P((int));
+#endif
+
+
+/*
+ * Input / Output Port Assignments
+ */
+
+#ifndef IO_BEGIN
+#define IO_ISABEGIN 0x000 /* 0x000 - Beginning of I/O Registers */
+
+ /* CPU Board */
+#define IO_DMA1 0x000 /* 8237A DMA Controller #1 */
+#define IO_ICU1 0x020 /* 8259A Interrupt Controller #1 */
+#define IO_TIMER1 0x040 /* 8253 Timer #1 */
+#define IO_TIMER2 0x048 /* 8253 Timer #2 */
+#define IO_KBD 0x060 /* 8042 Keyboard */
+#define IO_PPI 0x061 /* Programmabel Peripheral Interface */
+#define IO_RTC 0x070 /* RTC */
+#define IO_NMI IO_RTC /* NMI Control */
+#define IO_DMAPG 0x080 /* DMA Page Registers */
+#define IO_ICU2 0x0A0 /* 8259A Interrupt Controller #2 */
+#define IO_DMA2 0x0C0 /* 8237A DMA Controller #2 */
+#define IO_NPX 0x0F0 /* Numeric Coprocessor */
+
+ /* Cards */
+ /* 0x100 - 0x16F Open */
+
+#define IO_WD2 0x170 /* Secondary Fixed Disk Controller */
+
+ /* 0x178 - 0x1EF Open */
+
+#define IO_WD1 0x1f0 /* Primary Fixed Disk Controller */
+#define IO_GAME 0x200 /* Game Controller */
+
+ /* 0x208 - 0x277 Open */
+
+#define IO_LPT2 0x278 /* Parallel Port #2 */
+
+ /* 0x280 - 0x2E7 Open */
+
+#define IO_COM4 0x2e8 /* COM4 i/o address */
+
+ /* 0x2F0 - 0x2F7 Open */
+
+#define IO_COM2 0x2f8 /* COM2 i/o address */
+ /* 0x300 - 0x32F Open */
+
+#define IO_BT0 0x330 /* bustek 742a default addr. */
+#define IO_AHA0 0x330 /* adaptec 1542 default addr. */
+#define IO_UHA0 0x330 /* ultrastore 14f default addr. */
+#define IO_BT1 0x334 /* bustek 742a default addr. */
+#define IO_AHA1 0x334 /* adaptec 1542 default addr. */
+ /* 0x338 - 0x36F Open */
+
+#define IO_FD2 0x370 /* secondary base i/o address */
+#define IO_LPT1 0x378 /* Parallel Port #1 */
+
+ /* 0x380 - 0x3AF Open */
+
+#define IO_MDA 0x3B0 /* Monochome Adapter */
+#define IO_LPT3 0x3BC /* Monochome Adapter Printer Port */
+#define IO_VGA 0x3C0 /* E/VGA Ports */
+#define IO_CGA 0x3D0 /* CGA Ports */
+
+ /* 0x3E0 - 0x3E7 Open */
+
+#define IO_COM3 0x3e8 /* COM3 i/o address */
+#define IO_FD1 0x3f0 /* primary base i/o address */
+#define IO_COM1 0x3f8 /* COM1 i/o address */
+
+#define IO_ISAEND 0x3FF /* - 0x3FF End of I/O Registers */
+#endif IO_ISABEGIN
+
+/*
+ * Input / Output Port Sizes - these are from several sources, and tend
+ * to be the larger of what was found, ie COM ports can be 4, but some
+ * boards do not fully decode the address, thus 8 ports are used.
+ */
+
+#ifndef IO_ISASIZES
+#define IO_ISASIZES
+
+#define IO_COMSIZE 8 /* 8250, 16X50 com controllers (4?) */
+#define IO_CGASIZE 16 /* CGA controllers */
+#define IO_DMASIZE 16 /* 8237 DMA controllers */
+#define IO_DPGSIZE 32 /* 74LS612 DMA page reisters */
+#define IO_FDCSIZE 8 /* Nec765 floppy controllers */
+#define IO_WDCSIZE 8 /* WD compatible disk controllers */
+#define IO_GAMSIZE 16 /* AT compatible game controllers */
+#define IO_ICUSIZE 16 /* 8259A interrupt controllers */
+#define IO_KBDSIZE 16 /* 8042 Keyboard controllers */
+#define IO_LPTSIZE 8 /* LPT controllers, some use only 4 */
+#define IO_MDASIZE 16 /* Monochrome display controllers */
+#define IO_RTCSIZE 16 /* CMOS real time clock, NMI control */
+#define IO_TMRSIZE 16 /* 8253 programmable timers */
+#define IO_NPXSIZE 16 /* 80387/80487 NPX registers */
+#define IO_VGASIZE 16 /* VGA controllers */
+
+#endif /* IO_ISASIZES */
+
+/*
+ * Input / Output Memory Physical Addresses
+ */
+
+#ifndef IOM_BEGIN
+#define IOM_BEGIN 0x0a0000 /* Start of I/O Memory "hole" */
+#define IOM_END 0x100000 /* End of I/O Memory "hole" */
+#define IOM_SIZE (IOM_END - IOM_BEGIN)
+#endif IOM_BEGIN
+
+/*
+ * RAM Physical Address Space (ignoring the above mentioned "hole")
+ */
+
+#ifndef RAM_BEGIN
+#define RAM_BEGIN 0x0000000 /* Start of RAM Memory */
+#define RAM_END 0x1000000 /* End of RAM Memory */
+#define RAM_SIZE (RAM_END - RAM_BEGIN)
+#endif RAM_BEGIN
+
+/*
+ * Oddball Physical Memory Addresses
+ */
+#ifndef COMPAQ_RAMRELOC
+#define COMPAQ_RAMRELOC 0x80c00000 /* Compaq RAM relocation/diag */
+#define COMPAQ_RAMSETUP 0x80c00002 /* Compaq RAM setup */
+#define WEITEK_FPU 0xC0000000 /* WTL 2167 */
+#define CYRIX_EMC 0xC0000000 /* Cyrix EMC */
+#endif COMPAQ_RAMRELOC
+#endif /* _I386_ISA_ISA_H_ */
diff --git a/sys/amd64/isa/npx.c b/sys/amd64/isa/npx.c
new file mode 100644
index 0000000..a3ce1e2
--- /dev/null
+++ b/sys/amd64/isa/npx.c
@@ -0,0 +1,554 @@
+/*-
+ * Copyright (c) 1990 William Jolitz.
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)npx.c 7.2 (Berkeley) 5/12/91
+ * $Id: npx.c,v 1.6 1994/01/03 07:55:43 davidg Exp $
+ */
+
+#include "npx.h"
+#if NNPX > 0
+
+#include "param.h"
+#include "systm.h"
+#include "conf.h"
+#include "file.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+#include "machine/trap.h"
+#include "ioctl.h"
+#include "machine/specialreg.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/isa.h"
+
+/*
+ * 387 and 287 Numeric Coprocessor Extension (NPX) Driver.
+ */
+
+#ifdef __GNUC__
+
+#define disable_intr() __asm("cli")
+#define enable_intr() __asm("sti")
+#define fldcw(addr) __asm("fldcw %0" : : "m" (*addr))
+#define fnclex() __asm("fnclex")
+#define fninit() __asm("fninit")
+#define fnsave(addr) __asm("fnsave %0" : "=m" (*addr) : "0" (*addr))
+#define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr) : "0" (*addr))
+#define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr) : "0" (*addr))
+#define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fwait")
+#define frstor(addr) __asm("frstor %0" : : "m" (*addr))
+#define fwait() __asm("fwait")
+#define read_eflags() ({u_long ef; \
+ __asm("pushf; popl %0" : "=a" (ef)); \
+ ef; })
+#define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \
+ : : "n" (CR0_TS) : "ax")
+#define stop_emulating() __asm("clts")
+#define write_eflags(ef) __asm("pushl %0; popf" : : "a" ((u_long) ef))
+
+#else /* not __GNUC__ */
+
+void disable_intr __P((void));
+void enable_intr __P((void));
+void fldcw __P((caddr_t addr));
+void fnclex __P((void));
+void fninit __P((void));
+void fnsave __P((caddr_t addr));
+void fnstcw __P((caddr_t addr));
+void fnstsw __P((caddr_t addr));
+void fp_divide_by_0 __P((void));
+void frstor __P((caddr_t addr));
+void fwait __P((void));
+u_long read_eflags __P((void));
+void start_emulating __P((void));
+void stop_emulating __P((void));
+void write_eflags __P((u_long ef));
+
+#endif /* __GNUC__ */
+
+typedef u_char bool_t;
+
+extern struct gate_descriptor idt[];
+
+int npxdna __P((void));
+void npxexit __P((struct proc *p));
+void npxinit __P((u_int control));
+void npxintr __P((struct intrframe frame));
+void npxsave __P((struct save87 *addr));
+static int npxattach __P((struct isa_device *dvp));
+static int npxprobe __P((struct isa_device *dvp));
+static int npxprobe1 __P((struct isa_device *dvp));
+
+struct isa_driver npxdriver = {
+ npxprobe, npxattach, "npx",
+};
+
+u_int npx0_imask;
+struct proc *npxproc;
+
+static bool_t npx_ex16;
+static bool_t npx_exists;
+static struct gate_descriptor npx_idt_probeintr;
+static int npx_intrno;
+static volatile u_int npx_intrs_while_probing;
+static bool_t npx_irq13;
+static volatile u_int npx_traps_while_probing;
+
+/*
+ * Special interrupt handlers. Someday intr0-intr15 will be used to count
+ * interrupts. We'll still need a special exception 16 handler. The busy
+ * latch stuff in probintr() can be moved to npxprobe().
+ */
+void probeintr(void);
+asm
+("
+ .text
+_probeintr:
+ ss
+ incl _npx_intrs_while_probing
+ pushl %eax
+ movb $0x20,%al # EOI (asm in strings loses cpp features)
+ outb %al,$0xa0 # IO_ICU2
+ outb %al,$0x20 #IO_ICU1
+ movb $0,%al
+ outb %al,$0xf0 # clear BUSY# latch
+ popl %eax
+ iret
+");
+
+void probetrap(void);
+asm
+("
+ .text
+_probetrap:
+ ss
+ incl _npx_traps_while_probing
+ fnclex
+ iret
+");
+
+/*
+ * Probe routine. Initialize cr0 to give correct behaviour for [f]wait
+ * whether the device exists or not (XXX should be elsewhere). Set flags
+ * to tell npxattach() what to do. Modify device struct if npx doesn't
+ * need to use interrupts. Return 1 if device exists.
+ */
+static int
+npxprobe(dvp)
+ struct isa_device *dvp;
+{
+ int result;
+ u_long save_eflags;
+ u_char save_icu1_mask;
+ u_char save_icu2_mask;
+ struct gate_descriptor save_idt_npxintr;
+ struct gate_descriptor save_idt_npxtrap;
+ /*
+ * This routine is now just a wrapper for npxprobe1(), to install
+ * special npx interrupt and trap handlers, to enable npx interrupts
+ * and to disable other interrupts. Someday isa_configure() will
+ * install suitable handlers and run with interrupts enabled so we
+ * won't need to do so much here.
+ */
+ npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1;
+ save_eflags = read_eflags();
+ disable_intr();
+ save_icu1_mask = inb(IO_ICU1 + 1);
+ save_icu2_mask = inb(IO_ICU2 + 1);
+ save_idt_npxintr = idt[npx_intrno];
+ save_idt_npxtrap = idt[16];
+ outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq));
+ outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8));
+ setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL);
+ setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL);
+ npx_idt_probeintr = idt[npx_intrno];
+ enable_intr();
+ result = npxprobe1(dvp);
+ disable_intr();
+ outb(IO_ICU1 + 1, save_icu1_mask);
+ outb(IO_ICU2 + 1, save_icu2_mask);
+ idt[npx_intrno] = save_idt_npxintr;
+ idt[16] = save_idt_npxtrap;
+ write_eflags(save_eflags);
+ return (result);
+}
+
+static int
+npxprobe1(dvp)
+ struct isa_device *dvp;
+{
+ int control;
+ int status;
+#ifdef lint
+ npxintr();
+#endif
+ /*
+ * Partially reset the coprocessor, if any. Some BIOS's don't reset
+ * it after a warm boot.
+ */
+ outb(0xf1, 0); /* full reset on some systems, NOP on others */
+ outb(0xf0, 0); /* clear BUSY# latch */
+ /*
+ * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT
+ * instructions. We must set the CR0_MP bit and use the CR0_TS
+ * bit to control the trap, because setting the CR0_EM bit does
+ * not cause WAIT instructions to trap. It's important to trap
+ * WAIT instructions - otherwise the "wait" variants of no-wait
+ * control instructions would degenerate to the "no-wait" variants
+ * after FP context switches but work correctly otherwise. It's
+ * particularly important to trap WAITs when there is no NPX -
+ * otherwise the "wait" variants would always degenerate.
+ *
+ * Try setting CR0_NE to get correct error reporting on 486DX's.
+ * Setting it should fail or do nothing on lesser processors.
+ */
+ load_cr0(rcr0() | CR0_MP | CR0_NE);
+ /*
+ * But don't trap while we're probing.
+ */
+ stop_emulating();
+ /*
+ * Finish resetting the coprocessor, if any. If there is an error
+ * pending, then we may get a bogus IRQ13, but probeintr() will handle
+ * it OK. Bogus halts have never been observed, but we enabled
+ * IRQ13 and cleared the BUSY# latch early to handle them anyway.
+ */
+ fninit();
+ DELAY(1000); /* wait for any IRQ13 (fwait might hang) */
+#ifdef DIAGNOSTIC
+ if (npx_intrs_while_probing != 0)
+ printf("fninit caused %u bogus npx interrupt(s)\n",
+ npx_intrs_while_probing);
+ if (npx_traps_while_probing != 0)
+ printf("fninit caused %u bogus npx trap(s)\n",
+ npx_traps_while_probing);
+#endif
+ /*
+ * Check for a status of mostly zero.
+ */
+ status = 0x5a5a;
+ fnstsw(&status);
+ if ((status & 0xb8ff) == 0) {
+ /*
+ * Good, now check for a proper control word.
+ */
+ control = 0x5a5a;
+ fnstcw(&control);
+ if ((control & 0x1f3f) == 0x033f) {
+ npx_exists = 1;
+ /*
+ * We have an npx, now divide by 0 to see if exception
+ * 16 works.
+ */
+ control &= ~(1 << 2); /* enable divide by 0 trap */
+ fldcw(&control);
+ npx_traps_while_probing = npx_intrs_while_probing = 0;
+ fp_divide_by_0();
+ if (npx_traps_while_probing != 0) {
+ /*
+ * Good, exception 16 works.
+ */
+ npx_ex16 = 1;
+ dvp->id_irq = 0; /* zap the interrupt */
+ /*
+ * special return value to flag that we do not
+ * actually use any I/O registers
+ */
+ return (-1);
+ }
+ if (npx_intrs_while_probing != 0) {
+ /*
+ * Bad, we are stuck with IRQ13.
+ */
+ npx_irq13 = 1;
+ npx0_imask = dvp->id_irq; /* npxattach too late */
+ return (IO_NPXSIZE);
+ }
+ /*
+ * Worse, even IRQ13 is broken. Use emulator.
+ */
+ }
+ }
+ /*
+ * Probe failed, but we want to get to npxattach to initialize the
+ * emulator and say that it has been installed. XXX handle devices
+ * that aren't really devices better.
+ */
+ dvp->id_irq = 0;
+ /*
+ * special return value to flag that we do not
+ * actually use any I/O registers
+ */
+ return (-1);
+}
+
+/*
+ * Attach routine - announce which it is, and wire into system
+ */
+int
+npxattach(dvp)
+ struct isa_device *dvp;
+{
+ if (!npx_ex16 && !npx_irq13) {
+ if (npx_exists) {
+ printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit);
+ npx_exists = 0;
+ } else {
+ printf("npx%d: 387 Emulator\n",dvp->id_unit);
+ }
+ }
+ npxinit(__INITIAL_NPXCW__);
+ return (1); /* XXX unused */
+}
+
+/*
+ * Initialize floating point unit.
+ */
+void
+npxinit(control)
+ u_int control;
+{
+ struct save87 dummy;
+
+ if (!npx_exists)
+ return;
+ /*
+ * fninit has the same h/w bugs as fnsave. Use the detoxified
+ * fnsave to throw away any junk in the fpu. fnsave initializes
+ * the fpu and sets npxproc = NULL as important side effects.
+ */
+ npxsave(&dummy);
+ stop_emulating();
+ fldcw(&control);
+ if (curpcb != NULL)
+ fnsave(&curpcb->pcb_savefpu);
+ start_emulating();
+}
+
+/*
+ * Free coprocessor (if we have it).
+ */
+void
+npxexit(p)
+ struct proc *p;
+{
+
+ if (p == npxproc) {
+ start_emulating();
+ npxproc = NULL;
+ }
+}
+
+/*
+ * Record the FPU state and reinitialize it all except for the control word.
+ * Then generate a SIGFPE.
+ *
+ * Reinitializing the state allows naive SIGFPE handlers to longjmp without
+ * doing any fixups.
+ *
+ * XXX there is currently no way to pass the full error state to signal
+ * handlers, and if this is a nested interrupt there is no way to pass even
+ * a status code! So there is no way to have a non-naive SIGFPE handler. At
+ * best a handler could do an fninit followed by an fldcw of a static value.
+ * fnclex would be of little use because it would leave junk on the FPU stack.
+ * Returning from the handler would be even less safe than usual because
+ * IRQ13 exception handling makes exceptions even less precise than usual.
+ */
+void
+npxintr(frame)
+ struct intrframe frame;
+{
+ int code;
+
+ if (npxproc == NULL || !npx_exists) {
+ /* XXX no %p in stand/printf.c. Cast to quiet gcc -Wall. */
+ printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n",
+ (u_long) npxproc, (u_long) curproc, npx_exists);
+ panic("npxintr from nowhere");
+ }
+ if (npxproc != curproc) {
+ printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n",
+ (u_long) npxproc, (u_long) curproc, npx_exists);
+ panic("npxintr from non-current process");
+ }
+ /*
+ * Save state. This does an implied fninit. It had better not halt
+ * the cpu or we'll hang.
+ */
+ outb(0xf0, 0);
+ fnsave(&curpcb->pcb_savefpu);
+ fwait();
+ /*
+ * Restore control word (was clobbered by fnsave).
+ */
+ fldcw(&curpcb->pcb_savefpu.sv_env.en_cw);
+ fwait();
+ /*
+ * Remember the exception status word and tag word. The current
+ * (almost fninit'ed) fpu state is in the fpu and the exception
+ * state just saved will soon be junk. However, the implied fninit
+ * doesn't change the error pointers or register contents, and we
+ * preserved the control word and will copy the status and tag
+ * words, so the complete exception state can be recovered.
+ */
+ curpcb->pcb_savefpu.sv_ex_sw = curpcb->pcb_savefpu.sv_env.en_sw;
+ curpcb->pcb_savefpu.sv_ex_tw = curpcb->pcb_savefpu.sv_env.en_tw;
+
+ /*
+ * Pass exception to process.
+ */
+ if (ISPL(frame.if_cs) == SEL_UPL) {
+ /*
+ * Interrupt is essentially a trap, so we can afford to call
+ * the SIGFPE handler (if any) as soon as the interrupt
+ * returns.
+ *
+ * XXX little or nothing is gained from this, and plenty is
+ * lost - the interrupt frame has to contain the trap frame
+ * (this is otherwise only necessary for the rescheduling trap
+ * in doreti, and the frame for that could easily be set up
+ * just before it is used).
+ */
+ curproc->p_md.md_regs = (int *)&frame.if_es;
+#ifdef notyet
+ /*
+ * Encode the appropriate code for detailed information on
+ * this exception.
+ */
+ code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw);
+#else
+ code = 0; /* XXX */
+#endif
+ trapsignal(curproc, SIGFPE, code);
+ } else {
+ /*
+ * Nested interrupt. These losers occur when:
+ * o an IRQ13 is bogusly generated at a bogus time, e.g.:
+ * o immediately after an fnsave or frstor of an
+ * error state.
+ * o a couple of 386 instructions after
+ * "fstpl _memvar" causes a stack overflow.
+ * These are especially nasty when combined with a
+ * trace trap.
+ * o an IRQ13 occurs at the same time as another higher-
+ * priority interrupt.
+ *
+ * Treat them like a true async interrupt.
+ */
+ psignal(npxproc, SIGFPE);
+ }
+}
+
+/*
+ * Implement device not available (DNA) exception
+ *
+ * It would be better to switch FP context here (only). This would require
+ * saving the state in the proc table instead of in the pcb.
+ */
+int
+npxdna()
+{
+ if (!npx_exists)
+ return (0);
+ if (npxproc != NULL) {
+ printf("npxdna: npxproc = %lx, curproc = %lx\n",
+ (u_long) npxproc, (u_long) curproc);
+ panic("npxdna");
+ }
+ stop_emulating();
+ /*
+ * Record new context early in case frstor causes an IRQ13.
+ */
+ npxproc = curproc;
+ /*
+ * The following frstor may cause an IRQ13 when the state being
+ * restored has a pending error. The error will appear to have been
+ * triggered by the current (npx) user instruction even when that
+ * instruction is a no-wait instruction that should not trigger an
+ * error (e.g., fnclex). On at least one 486 system all of the
+ * no-wait instructions are broken the same as frstor, so our
+ * treatment does not amplify the breakage. On at least one
+ * 386/Cyrix 387 system, fnclex works correctly while frstor and
+ * fnsave are broken, so our treatment breaks fnclex if it is the
+ * first FPU instruction after a context switch.
+ */
+ frstor(&curpcb->pcb_savefpu);
+
+ return (1);
+}
+
+/*
+ * Wrapper for fnsave instruction to handle h/w bugs. If there is an error
+ * pending, then fnsave generates a bogus IRQ13 on some systems. Force
+ * any IRQ13 to be handled immediately, and then ignore it. This routine is
+ * often called at splhigh so it must not use many system services. In
+ * particular, it's much easier to install a special handler than to
+ * guarantee that it's safe to use npxintr() and its supporting code.
+ */
+void
+npxsave(addr)
+ struct save87 *addr;
+{
+ u_char icu1_mask;
+ u_char icu2_mask;
+ u_char old_icu1_mask;
+ u_char old_icu2_mask;
+ struct gate_descriptor save_idt_npxintr;
+
+ disable_intr();
+ old_icu1_mask = inb(IO_ICU1 + 1);
+ old_icu2_mask = inb(IO_ICU2 + 1);
+ save_idt_npxintr = idt[npx_intrno];
+ outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask));
+ outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8));
+ idt[npx_intrno] = npx_idt_probeintr;
+ enable_intr();
+ stop_emulating();
+ fnsave(addr);
+ fwait();
+ start_emulating();
+ npxproc = NULL;
+ disable_intr();
+ icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */
+ icu2_mask = inb(IO_ICU2 + 1);
+ outb(IO_ICU1 + 1,
+ (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask));
+ outb(IO_ICU2 + 1,
+ (icu2_mask & ~(npx0_imask >> 8))
+ | (old_icu2_mask & (npx0_imask >> 8)));
+ idt[npx_intrno] = save_idt_npxintr;
+ enable_intr(); /* back to usual state */
+}
+
+#endif /* NNPX > 0 */
diff --git a/sys/amd64/isa/timerreg.h b/sys/amd64/isa/timerreg.h
new file mode 100644
index 0000000..5742f66
--- /dev/null
+++ b/sys/amd64/isa/timerreg.h
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: Header: timerreg.h,v 1.2 93/02/28 15:08:58 mccanne Exp
+ * $Id$
+ */
+
+/*
+ *
+ * Register definitions for the Intel 8253 Programmable Interval Timer.
+ *
+ * This chip has three independent 16-bit down counters that can be
+ * read on the fly. There are three mode registers and three countdown
+ * registers. The countdown registers are addressed directly, via the
+ * first three I/O ports. The three mode registers are accessed via
+ * the fourth I/O port, with two bits in the mode byte indicating the
+ * register. (Why are hardware interfaces always so braindead?).
+ *
+ * To write a value into the countdown register, the mode register
+ * is first programmed with a command indicating the which byte of
+ * the two byte register is to be modified. The three possibilities
+ * are load msb (TMR_MR_MSB), load lsb (TMR_MR_LSB), or load lsb then
+ * msb (TMR_MR_BOTH).
+ *
+ * To read the current value ("on the fly") from the countdown register,
+ * you write a "latch" command into the mode register, then read the stable
+ * value from the corresponding I/O port. For example, you write
+ * TMR_MR_LATCH into the corresponding mode register. Presumably,
+ * after doing this, a write operation to the I/O port would result
+ * in undefined behavior (but hopefully not fry the chip).
+ * Reading in this manner has no side effects.
+ *
+ * The outputs of the three timers are connected as follows:
+ *
+ * timer 0 -> irq 0
+ * timer 1 -> dma chan 0 (for dram refresh)
+ * timer 2 -> speaker (via keyboard controller)
+ *
+ * Timer 0 is used to call hardclock.
+ * Timer 2 is used to generate console beeps.
+ */
+
+/*
+ * Macros for specifying values to be written into a mode register.
+ */
+#define TIMER_CNTR0 (IO_TIMER1 + 0) /* timer 0 counter port */
+#define TIMER_CNTR1 (IO_TIMER1 + 1) /* timer 1 counter port */
+#define TIMER_CNTR2 (IO_TIMER1 + 2) /* timer 2 counter port */
+#define TIMER_MODE (IO_TIMER1 + 3) /* timer mode port */
+#define TIMER_SEL0 0x00 /* select counter 0 */
+#define TIMER_SEL1 0x40 /* select counter 1 */
+#define TIMER_SEL2 0x80 /* select counter 2 */
+#define TIMER_INTTC 0x00 /* mode 0, intr on terminal cnt */
+#define TIMER_ONESHOT 0x02 /* mode 1, one shot */
+#define TIMER_RATEGEN 0x04 /* mode 2, rate generator */
+#define TIMER_SQWAVE 0x06 /* mode 3, square wave */
+#define TIMER_SWSTROBE 0x08 /* mode 4, s/w triggered strobe */
+#define TIMER_HWSTROBE 0x0a /* mode 5, h/w triggered strobe */
+#define TIMER_LATCH 0x00 /* latch counter for reading */
+#define TIMER_LSB 0x10 /* r/w counter LSB */
+#define TIMER_MSB 0x20 /* r/w counter MSB */
+#define TIMER_16BIT 0x30 /* r/w counter 16 bits, LSB first */
+#define TIMER_BCD 0x01 /* count in BCD */
+
diff --git a/sys/amd64/isa/vector.S b/sys/amd64/isa/vector.S
new file mode 100644
index 0000000..7135ae7
--- /dev/null
+++ b/sys/amd64/isa/vector.S
@@ -0,0 +1,360 @@
+/*
+ * from: vector.s, 386BSD 0.1 unknown origin
+ * $Id: vector.s,v 1.6 1994/01/10 23:15:09 ache Exp $
+ */
+
+#include "i386/isa/icu.h"
+#include "i386/isa/isa.h"
+#include "vector.h"
+
+#define ICU_EOI 0x20 /* XXX - define elsewhere */
+
+#define IRQ_BIT(irq_num) (1 << ((irq_num) % 8))
+#define IRQ_BYTE(irq_num) ((irq_num) / 8)
+
+#ifdef AUTO_EOI_1
+#define ENABLE_ICU1 /* use auto-EOI to reduce i/o */
+#else
+#define ENABLE_ICU1 \
+ movb $ICU_EOI,%al ; /* as soon as possible send EOI ... */ \
+ FASTER_NOP ; /* ... ASAP ... */ \
+ outb %al,$IO_ICU1 /* ... to clear in service bit */
+#endif
+
+#ifdef AUTO_EOI_2
+/*
+ * The data sheet says no auto-EOI on slave, but it sometimes works.
+ */
+#define ENABLE_ICU1_AND_2 ENABLE_ICU1
+#else
+#define ENABLE_ICU1_AND_2 \
+ movb $ICU_EOI,%al ; /* as above */ \
+ FASTER_NOP ; \
+ outb %al,$IO_ICU2 ; /* but do second icu first */ \
+ FASTER_NOP ; \
+ outb %al,$IO_ICU1 /* then first icu */
+#endif
+
+#ifdef FAST_INTR_HANDLER_USES_ES
+#define ACTUALLY_PUSHED 1
+#define MAYBE_MOVW_AX_ES movl %ax,%es
+#define MAYBE_POPL_ES popl %es
+#define MAYBE_PUSHL_ES pushl %es
+#else
+/*
+ * We can usually skip loading %es for fastintr handlers. %es should
+ * only be used for string instructions, and fastintr handlers shouldn't
+ * do anything slow enough to justify using a string instruction.
+ */
+#define ACTUALLY_PUSHED 0
+#define MAYBE_MOVW_AX_ES
+#define MAYBE_POPL_ES
+#define MAYBE_PUSHL_ES
+#endif
+
+/*
+ * Macros for interrupt interrupt entry, call to handler, and exit.
+ *
+ * XXX - the interrupt frame is set up to look like a trap frame. This is
+ * usually a waste of time. The only interrupt handlers that want a frame
+ * are the clock handler (it wants a clock frame), the npx handler (it's
+ * easier to do right all in assembler). The interrupt return routine
+ * needs a trap frame for rare AST's (it could easily convert the frame).
+ * The direct costs of setting up a trap frame are two pushl's (error
+ * code and trap number), an addl to get rid of these, and pushing and
+ * popping the call-saved regs %esi, %edi and %ebp twice, The indirect
+ * costs are making the driver interface nonuniform so unpending of
+ * interrupts is more complicated and slower (call_driver(unit) would
+ * be easier than ensuring an interrupt frame for all handlers. Finally,
+ * there are some struct copies in the npx handler and maybe in the clock
+ * handler that could be avoided by working more with pointers to frames
+ * instead of frames.
+ *
+ * XXX - should we do a cld on every system entry to avoid the requirement
+ * for scattered cld's?
+ *
+ * Coding notes for *.s:
+ *
+ * If possible, avoid operations that involve an operand size override.
+ * Word-sized operations might be smaller, but the operand size override
+ * makes them slower on on 486's and no faster on 386's unless perhaps
+ * the instruction pipeline is depleted. E.g.,
+ *
+ * Use movl to seg regs instead of the equivalent but more descriptive
+ * movw - gas generates an irelevant (slower) operand size override.
+ *
+ * Use movl to ordinary regs in preference to movw and especially
+ * in preference to movz[bw]l. Use unsigned (long) variables with the
+ * top bits clear instead of unsigned short variables to provide more
+ * opportunities for movl.
+ *
+ * If possible, use byte-sized operations. They are smaller and no slower.
+ *
+ * Use (%reg) instead of 0(%reg) - gas generates larger code for the latter.
+ *
+ * If the interrupt frame is made more flexible, INTR can push %eax first
+ * and decide the ipending case with less overhead, e.g., by avoiding
+ * loading segregs.
+ */
+
+#define FAST_INTR(unit, irq_num, id_num, handler, enable_icus) \
+ pushl %eax ; /* save only call-used registers */ \
+ pushl %ecx ; \
+ pushl %edx ; \
+ pushl %ds ; \
+ MAYBE_PUSHL_ES ; \
+ movl $KDSEL,%eax ; \
+ movl %ax,%ds ; \
+ MAYBE_MOVW_AX_ES ; \
+ FAKE_MCOUNT((4+ACTUALLY_PUSHED)*4(%esp)) ; \
+ pushl $unit ; \
+ call handler ; /* do the work ASAP */ \
+ enable_icus ; /* (re)enable ASAP (helps edge trigger?) */ \
+ addl $4,%esp ; \
+ incl _cnt+V_INTR ; /* book-keeping can wait */ \
+ incl _intrcnt_actv + (id_num) * 4 ; \
+ movl _cpl,%eax ; /* are we unmasking pending HWIs or SWIs? */ \
+ notl %eax ; \
+ andl _ipending,%eax ; \
+ jne 1f ; /* yes, handle them */ \
+ MEXITCOUNT ; \
+ MAYBE_POPL_ES ; \
+ popl %ds ; \
+ popl %edx ; \
+ popl %ecx ; \
+ popl %eax ; \
+ iret ; \
+; \
+ ALIGN_TEXT ; \
+1: ; \
+ movl _cpl,%eax ; \
+ movl $HWI_MASK|SWI_MASK,_cpl ; /* limit nesting ... */ \
+ sti ; /* ... to do this as early as possible */ \
+ MAYBE_POPL_ES ; /* discard most of thin frame ... */ \
+ popl %ecx ; /* ... original %ds ... */ \
+ popl %edx ; \
+ xchgl %eax,(1+ACTUALLY_PUSHED)*4(%esp) ; /* orig %eax; save cpl */ \
+ pushal ; /* build fat frame (grrr) ... */ \
+ pushl %ecx ; /* ... actually %ds ... */ \
+ pushl %es ; \
+ movl $KDSEL,%eax ; \
+ movl %ax,%es ; \
+ movl (2+8+0)*4(%esp),%ecx ; /* ... %ecx from thin frame ... */ \
+ movl %ecx,(2+6)*4(%esp) ; /* ... to fat frame ... */ \
+ movl (2+8+1)*4(%esp),%eax ; /* ... cpl from thin frame */ \
+ pushl %eax ; \
+ subl $4,%esp ; /* junk for unit number */ \
+ MEXITCOUNT ; \
+ jmp _doreti
+
+#define INTR(unit, irq_num, id_num, mask, handler, icu, enable_icus, reg, stray) \
+ pushl $0 ; /* dumby error code */ \
+ pushl $0 ; /* dumby trap type */ \
+ pushal ; \
+ pushl %ds ; /* save our data and extra segments ... */ \
+ pushl %es ; \
+ movl $KDSEL,%eax ; /* ... and reload with kernel's own ... */ \
+ movl %ax,%ds ; /* ... early for obsolete reasons */ \
+ movl %ax,%es ; \
+ movb _imen + IRQ_BYTE(irq_num),%al ; \
+ orb $IRQ_BIT(irq_num),%al ; \
+ movb %al,_imen + IRQ_BYTE(irq_num) ; \
+ FASTER_NOP ; \
+ outb %al,$icu+1 ; \
+ enable_icus ; \
+ incl _cnt+V_INTR ; /* tally interrupts */ \
+ movl _cpl,%eax ; \
+ testb $IRQ_BIT(irq_num),%reg ; \
+ jne 2f ; \
+1: ; \
+ FAKE_MCOUNT(12*4(%esp)) ; /* XXX late to avoid double count */ \
+ incl _intrcnt_actv + (id_num) * 4 ; \
+ movl _cpl,%eax ; \
+ pushl %eax ; \
+ pushl $unit ; \
+ orl mask,%eax ; \
+ movl %eax,_cpl ; \
+ sti ; \
+ call handler ; \
+ movb _imen + IRQ_BYTE(irq_num),%al ; \
+ andb $~IRQ_BIT(irq_num),%al ; \
+ movb %al,_imen + IRQ_BYTE(irq_num) ; \
+ FASTER_NOP ; \
+ outb %al,$icu+1 ; \
+ MEXITCOUNT ; \
+ /* We could usually avoid the following jmp by inlining some of */ \
+ /* _doreti, but it's probably better to use less cache. */ \
+ jmp _doreti ; \
+; \
+ ALIGN_TEXT ; \
+2: ; \
+ /* XXX skip mcounting here to avoid double count */ \
+ movl $1b,%eax ; /* register resume address */ \
+ /* XXX - someday do it at attach time */ \
+ movl %eax,ihandlers + (irq_num) * 4 ; \
+ orb $IRQ_BIT(irq_num),_ipending + IRQ_BYTE(irq_num) ; \
+ popl %es ; \
+ popl %ds ; \
+ popal ; \
+ addl $4+4,%esp ; \
+ iret
+
+/*
+ * vector.h has defined a macro 'BUILD_VECTORS' containing a big list of info
+ * about vectors, including a submacro 'BUILD_VECTOR' that operates on the
+ * info about each vector. We redefine 'BUILD_VECTOR' to expand the info
+ * in different ways. Here we expand it to a list of interrupt handlers.
+ * This order is of course unimportant. Elsewhere we expand it to inline
+ * linear search code for which the order is a little more important and
+ * concatenating the code with no holes is very important.
+ *
+ * XXX - now there is BUILD_FAST_VECTOR as well as BUILD_VECTOR.
+ *
+ * The info consists of the following items for each vector:
+ *
+ * name (identifier): name of the vector; used to build labels
+ * unit (expression): unit number to call the device driver with
+ * irq_num (number): number of the IRQ to handled (0-15)
+ * id_num (number): uniq numeric id for handler (assigned by config)
+ * mask (blank-ident): priority mask used
+ * handler (blank-ident): interrupt handler to call
+ * icu_num (number): (1 + irq_num / 8) converted for label building
+ * icu_enables (number): 1 for icu_num == 1, 1_AND_2 for icu_num == 2
+ * reg (blank-ident): al for icu_num == 1, ah for icu_num == 2
+ *
+ * 'irq_num' is converted in several ways at config time to get around
+ * limitations in cpp. The macros have blanks after commas iff they would
+ * not mess up identifiers and numbers.
+ */
+
+#undef BUILD_FAST_VECTOR
+#define BUILD_FAST_VECTOR(name, unit, irq_num, id_num, mask, handler, \
+ icu_num, icu_enables, reg) \
+ .globl handler ; \
+ .text ; \
+ .globl _V/**/name ; \
+ SUPERALIGN_TEXT ; \
+_V/**/name: ; \
+ FAST_INTR(unit, irq_num,id_num, handler, ENABLE_ICU/**/icu_enables)
+
+#undef BUILD_VECTOR
+#define BUILD_VECTOR(name, unit, irq_num, id_num, mask, handler, \
+ icu_num, icu_enables, reg) \
+ .globl handler ; \
+ .text ; \
+ .globl _V/**/name ; \
+ SUPERALIGN_TEXT ; \
+_V/**/name: ; \
+ INTR(unit,irq_num, id_num, mask, handler, IO_ICU/**/icu_num, \
+ ENABLE_ICU/**/icu_enables, reg,)
+
+MCOUNT_LABEL(bintr)
+ BUILD_VECTORS
+
+ /* hardware interrupt catcher (IDT 32 - 47) */
+ .globl _isa_strayintr
+
+#define STRAYINTR(irq_num, icu_num, icu_enables, reg) \
+IDTVEC(intr/**/irq_num) ; \
+ INTR(irq_num,irq_num,irq_num, _high_imask, _isa_strayintr, \
+ IO_ICU/**/icu_num, ENABLE_ICU/**/icu_enables, reg,stray)
+
+/*
+ * XXX - the mask (1 << 2) == IRQ_SLAVE will be generated for IRQ 2, instead
+ * of the mask IRQ2 (defined as IRQ9 == (1 << 9)). But IRQ 2 "can't happen".
+ * In fact, all stray interrupts "can't happen" except for bugs. The
+ * "stray" IRQ 7 is documented behaviour of the 8259. It happens when there
+ * is a glitch on any of its interrupt inputs. Does it really interrupt when
+ * IRQ 7 is masked?
+ *
+ * XXX - unpend doesn't work for these, it sends them to the real handler.
+ *
+ * XXX - the race bug during initialization may be because I changed the
+ * order of switching from the stray to the real interrupt handler to before
+ * enabling interrupts. The old order looked unsafe but maybe it is OK with
+ * the stray interrupt handler installed. But these handlers only reduce
+ * the window of vulnerability - it is still open at the end of
+ * isa_configure().
+ *
+ * XXX - many comments are stale.
+ */
+
+ STRAYINTR(0,1,1, al)
+ STRAYINTR(1,1,1, al)
+ STRAYINTR(2,1,1, al)
+ STRAYINTR(3,1,1, al)
+ STRAYINTR(4,1,1, al)
+ STRAYINTR(5,1,1, al)
+ STRAYINTR(6,1,1, al)
+ STRAYINTR(7,1,1, al)
+ STRAYINTR(8,2,1_AND_2, ah)
+ STRAYINTR(9,2,1_AND_2, ah)
+ STRAYINTR(10,2,1_AND_2, ah)
+ STRAYINTR(11,2,1_AND_2, ah)
+ STRAYINTR(12,2,1_AND_2, ah)
+ STRAYINTR(13,2,1_AND_2, ah)
+ STRAYINTR(14,2,1_AND_2, ah)
+ STRAYINTR(15,2,1_AND_2, ah)
+#if 0
+ INTRSTRAY(255, _highmask, 255) ; call _isa_strayintr ; INTREXIT2
+#endif
+MCOUNT_LABEL(eintr)
+
+/*
+ * These are the interrupt counters, I moved them here from icu.s so that
+ * they are with the name table. rgrimes
+ *
+ * There are now lots of counters, this has been redone to work with
+ * Bruce Evans intr-0.1 code, which I modified some more to make it all
+ * work with vmstat.
+ */
+ .data
+ihandlers: /* addresses of interrupt handlers */
+ .space NHWI*4 /* actually resumption addresses for HWI's */
+ .long swi_tty, swi_net, 0, 0, 0, 0, 0, 0
+ .long 0, 0, 0, 0, 0, 0, swi_clock, swi_ast
+imasks: /* masks for interrupt handlers */
+ .space NHWI*4 /* padding; HWI masks are elsewhere */
+ .long SWI_TTY_MASK, SWI_NET_MASK, 0, 0, 0, 0, 0, 0
+ .long 0, 0, 0, 0, 0, 0, SWI_CLOCK_MASK, SWI_AST_MASK
+
+ .globl _intrcnt
+_intrcnt: /* used by vmstat to calc size of table */
+ .globl _intrcnt_bad7
+_intrcnt_bad7: .space 4 /* glitches on irq 7 */
+ .globl _intrcnt_bad15
+_intrcnt_bad15: .space 4 /* glitches on irq 15 */
+ .globl _intrcnt_stray
+_intrcnt_stray: .space 4 /* total count of stray interrupts */
+ .globl _intrcnt_actv
+_intrcnt_actv: .space NR_REAL_INT_HANDLERS * 4 /* active interrupts */
+ .globl _eintrcnt
+_eintrcnt: /* used by vmstat to calc size of table */
+
+/*
+ * Build the interrupt name table for vmstat
+ */
+
+#undef BUILD_FAST_VECTOR
+#define BUILD_FAST_VECTOR BUILD_VECTOR
+
+#undef BUILD_VECTOR
+#define BUILD_VECTOR(name, unit, irq_num, id_num, mask, handler, \
+ icu_num, icu_enables, reg) \
+ .ascii "name irq" ; \
+ .asciz "irq_num"
+/*
+ * XXX - use the __STRING and __CONCAT macros from <sys/cdefs.h> to stringize
+ * and concatenate names above and elsewhere. Note that __CONCAT doesn't
+ * work when nested.
+ */
+
+ .text
+ .globl _intrnames, _eintrnames
+_intrnames:
+ BUILD_VECTOR(bad,,7,,,,,,)
+ BUILD_VECTOR(bad,,15,,,,,,)
+ BUILD_VECTOR(stray,,,,,,,,)
+ BUILD_VECTORS
+
+_eintrnames:
diff --git a/sys/amd64/isa/vector.s b/sys/amd64/isa/vector.s
new file mode 100644
index 0000000..7135ae7
--- /dev/null
+++ b/sys/amd64/isa/vector.s
@@ -0,0 +1,360 @@
+/*
+ * from: vector.s, 386BSD 0.1 unknown origin
+ * $Id: vector.s,v 1.6 1994/01/10 23:15:09 ache Exp $
+ */
+
+#include "i386/isa/icu.h"
+#include "i386/isa/isa.h"
+#include "vector.h"
+
+#define ICU_EOI 0x20 /* XXX - define elsewhere */
+
+#define IRQ_BIT(irq_num) (1 << ((irq_num) % 8))
+#define IRQ_BYTE(irq_num) ((irq_num) / 8)
+
+#ifdef AUTO_EOI_1
+#define ENABLE_ICU1 /* use auto-EOI to reduce i/o */
+#else
+#define ENABLE_ICU1 \
+ movb $ICU_EOI,%al ; /* as soon as possible send EOI ... */ \
+ FASTER_NOP ; /* ... ASAP ... */ \
+ outb %al,$IO_ICU1 /* ... to clear in service bit */
+#endif
+
+#ifdef AUTO_EOI_2
+/*
+ * The data sheet says no auto-EOI on slave, but it sometimes works.
+ */
+#define ENABLE_ICU1_AND_2 ENABLE_ICU1
+#else
+#define ENABLE_ICU1_AND_2 \
+ movb $ICU_EOI,%al ; /* as above */ \
+ FASTER_NOP ; \
+ outb %al,$IO_ICU2 ; /* but do second icu first */ \
+ FASTER_NOP ; \
+ outb %al,$IO_ICU1 /* then first icu */
+#endif
+
+#ifdef FAST_INTR_HANDLER_USES_ES
+#define ACTUALLY_PUSHED 1
+#define MAYBE_MOVW_AX_ES movl %ax,%es
+#define MAYBE_POPL_ES popl %es
+#define MAYBE_PUSHL_ES pushl %es
+#else
+/*
+ * We can usually skip loading %es for fastintr handlers. %es should
+ * only be used for string instructions, and fastintr handlers shouldn't
+ * do anything slow enough to justify using a string instruction.
+ */
+#define ACTUALLY_PUSHED 0
+#define MAYBE_MOVW_AX_ES
+#define MAYBE_POPL_ES
+#define MAYBE_PUSHL_ES
+#endif
+
+/*
+ * Macros for interrupt interrupt entry, call to handler, and exit.
+ *
+ * XXX - the interrupt frame is set up to look like a trap frame. This is
+ * usually a waste of time. The only interrupt handlers that want a frame
+ * are the clock handler (it wants a clock frame), the npx handler (it's
+ * easier to do right all in assembler). The interrupt return routine
+ * needs a trap frame for rare AST's (it could easily convert the frame).
+ * The direct costs of setting up a trap frame are two pushl's (error
+ * code and trap number), an addl to get rid of these, and pushing and
+ * popping the call-saved regs %esi, %edi and %ebp twice, The indirect
+ * costs are making the driver interface nonuniform so unpending of
+ * interrupts is more complicated and slower (call_driver(unit) would
+ * be easier than ensuring an interrupt frame for all handlers. Finally,
+ * there are some struct copies in the npx handler and maybe in the clock
+ * handler that could be avoided by working more with pointers to frames
+ * instead of frames.
+ *
+ * XXX - should we do a cld on every system entry to avoid the requirement
+ * for scattered cld's?
+ *
+ * Coding notes for *.s:
+ *
+ * If possible, avoid operations that involve an operand size override.
+ * Word-sized operations might be smaller, but the operand size override
+ * makes them slower on on 486's and no faster on 386's unless perhaps
+ * the instruction pipeline is depleted. E.g.,
+ *
+ * Use movl to seg regs instead of the equivalent but more descriptive
+ * movw - gas generates an irelevant (slower) operand size override.
+ *
+ * Use movl to ordinary regs in preference to movw and especially
+ * in preference to movz[bw]l. Use unsigned (long) variables with the
+ * top bits clear instead of unsigned short variables to provide more
+ * opportunities for movl.
+ *
+ * If possible, use byte-sized operations. They are smaller and no slower.
+ *
+ * Use (%reg) instead of 0(%reg) - gas generates larger code for the latter.
+ *
+ * If the interrupt frame is made more flexible, INTR can push %eax first
+ * and decide the ipending case with less overhead, e.g., by avoiding
+ * loading segregs.
+ */
+
+#define FAST_INTR(unit, irq_num, id_num, handler, enable_icus) \
+ pushl %eax ; /* save only call-used registers */ \
+ pushl %ecx ; \
+ pushl %edx ; \
+ pushl %ds ; \
+ MAYBE_PUSHL_ES ; \
+ movl $KDSEL,%eax ; \
+ movl %ax,%ds ; \
+ MAYBE_MOVW_AX_ES ; \
+ FAKE_MCOUNT((4+ACTUALLY_PUSHED)*4(%esp)) ; \
+ pushl $unit ; \
+ call handler ; /* do the work ASAP */ \
+ enable_icus ; /* (re)enable ASAP (helps edge trigger?) */ \
+ addl $4,%esp ; \
+ incl _cnt+V_INTR ; /* book-keeping can wait */ \
+ incl _intrcnt_actv + (id_num) * 4 ; \
+ movl _cpl,%eax ; /* are we unmasking pending HWIs or SWIs? */ \
+ notl %eax ; \
+ andl _ipending,%eax ; \
+ jne 1f ; /* yes, handle them */ \
+ MEXITCOUNT ; \
+ MAYBE_POPL_ES ; \
+ popl %ds ; \
+ popl %edx ; \
+ popl %ecx ; \
+ popl %eax ; \
+ iret ; \
+; \
+ ALIGN_TEXT ; \
+1: ; \
+ movl _cpl,%eax ; \
+ movl $HWI_MASK|SWI_MASK,_cpl ; /* limit nesting ... */ \
+ sti ; /* ... to do this as early as possible */ \
+ MAYBE_POPL_ES ; /* discard most of thin frame ... */ \
+ popl %ecx ; /* ... original %ds ... */ \
+ popl %edx ; \
+ xchgl %eax,(1+ACTUALLY_PUSHED)*4(%esp) ; /* orig %eax; save cpl */ \
+ pushal ; /* build fat frame (grrr) ... */ \
+ pushl %ecx ; /* ... actually %ds ... */ \
+ pushl %es ; \
+ movl $KDSEL,%eax ; \
+ movl %ax,%es ; \
+ movl (2+8+0)*4(%esp),%ecx ; /* ... %ecx from thin frame ... */ \
+ movl %ecx,(2+6)*4(%esp) ; /* ... to fat frame ... */ \
+ movl (2+8+1)*4(%esp),%eax ; /* ... cpl from thin frame */ \
+ pushl %eax ; \
+ subl $4,%esp ; /* junk for unit number */ \
+ MEXITCOUNT ; \
+ jmp _doreti
+
+#define INTR(unit, irq_num, id_num, mask, handler, icu, enable_icus, reg, stray) \
+ pushl $0 ; /* dumby error code */ \
+ pushl $0 ; /* dumby trap type */ \
+ pushal ; \
+ pushl %ds ; /* save our data and extra segments ... */ \
+ pushl %es ; \
+ movl $KDSEL,%eax ; /* ... and reload with kernel's own ... */ \
+ movl %ax,%ds ; /* ... early for obsolete reasons */ \
+ movl %ax,%es ; \
+ movb _imen + IRQ_BYTE(irq_num),%al ; \
+ orb $IRQ_BIT(irq_num),%al ; \
+ movb %al,_imen + IRQ_BYTE(irq_num) ; \
+ FASTER_NOP ; \
+ outb %al,$icu+1 ; \
+ enable_icus ; \
+ incl _cnt+V_INTR ; /* tally interrupts */ \
+ movl _cpl,%eax ; \
+ testb $IRQ_BIT(irq_num),%reg ; \
+ jne 2f ; \
+1: ; \
+ FAKE_MCOUNT(12*4(%esp)) ; /* XXX late to avoid double count */ \
+ incl _intrcnt_actv + (id_num) * 4 ; \
+ movl _cpl,%eax ; \
+ pushl %eax ; \
+ pushl $unit ; \
+ orl mask,%eax ; \
+ movl %eax,_cpl ; \
+ sti ; \
+ call handler ; \
+ movb _imen + IRQ_BYTE(irq_num),%al ; \
+ andb $~IRQ_BIT(irq_num),%al ; \
+ movb %al,_imen + IRQ_BYTE(irq_num) ; \
+ FASTER_NOP ; \
+ outb %al,$icu+1 ; \
+ MEXITCOUNT ; \
+ /* We could usually avoid the following jmp by inlining some of */ \
+ /* _doreti, but it's probably better to use less cache. */ \
+ jmp _doreti ; \
+; \
+ ALIGN_TEXT ; \
+2: ; \
+ /* XXX skip mcounting here to avoid double count */ \
+ movl $1b,%eax ; /* register resume address */ \
+ /* XXX - someday do it at attach time */ \
+ movl %eax,ihandlers + (irq_num) * 4 ; \
+ orb $IRQ_BIT(irq_num),_ipending + IRQ_BYTE(irq_num) ; \
+ popl %es ; \
+ popl %ds ; \
+ popal ; \
+ addl $4+4,%esp ; \
+ iret
+
+/*
+ * vector.h has defined a macro 'BUILD_VECTORS' containing a big list of info
+ * about vectors, including a submacro 'BUILD_VECTOR' that operates on the
+ * info about each vector. We redefine 'BUILD_VECTOR' to expand the info
+ * in different ways. Here we expand it to a list of interrupt handlers.
+ * This order is of course unimportant. Elsewhere we expand it to inline
+ * linear search code for which the order is a little more important and
+ * concatenating the code with no holes is very important.
+ *
+ * XXX - now there is BUILD_FAST_VECTOR as well as BUILD_VECTOR.
+ *
+ * The info consists of the following items for each vector:
+ *
+ * name (identifier): name of the vector; used to build labels
+ * unit (expression): unit number to call the device driver with
+ * irq_num (number): number of the IRQ to handled (0-15)
+ * id_num (number): uniq numeric id for handler (assigned by config)
+ * mask (blank-ident): priority mask used
+ * handler (blank-ident): interrupt handler to call
+ * icu_num (number): (1 + irq_num / 8) converted for label building
+ * icu_enables (number): 1 for icu_num == 1, 1_AND_2 for icu_num == 2
+ * reg (blank-ident): al for icu_num == 1, ah for icu_num == 2
+ *
+ * 'irq_num' is converted in several ways at config time to get around
+ * limitations in cpp. The macros have blanks after commas iff they would
+ * not mess up identifiers and numbers.
+ */
+
+#undef BUILD_FAST_VECTOR
+#define BUILD_FAST_VECTOR(name, unit, irq_num, id_num, mask, handler, \
+ icu_num, icu_enables, reg) \
+ .globl handler ; \
+ .text ; \
+ .globl _V/**/name ; \
+ SUPERALIGN_TEXT ; \
+_V/**/name: ; \
+ FAST_INTR(unit, irq_num,id_num, handler, ENABLE_ICU/**/icu_enables)
+
+#undef BUILD_VECTOR
+#define BUILD_VECTOR(name, unit, irq_num, id_num, mask, handler, \
+ icu_num, icu_enables, reg) \
+ .globl handler ; \
+ .text ; \
+ .globl _V/**/name ; \
+ SUPERALIGN_TEXT ; \
+_V/**/name: ; \
+ INTR(unit,irq_num, id_num, mask, handler, IO_ICU/**/icu_num, \
+ ENABLE_ICU/**/icu_enables, reg,)
+
+MCOUNT_LABEL(bintr)
+ BUILD_VECTORS
+
+ /* hardware interrupt catcher (IDT 32 - 47) */
+ .globl _isa_strayintr
+
+#define STRAYINTR(irq_num, icu_num, icu_enables, reg) \
+IDTVEC(intr/**/irq_num) ; \
+ INTR(irq_num,irq_num,irq_num, _high_imask, _isa_strayintr, \
+ IO_ICU/**/icu_num, ENABLE_ICU/**/icu_enables, reg,stray)
+
+/*
+ * XXX - the mask (1 << 2) == IRQ_SLAVE will be generated for IRQ 2, instead
+ * of the mask IRQ2 (defined as IRQ9 == (1 << 9)). But IRQ 2 "can't happen".
+ * In fact, all stray interrupts "can't happen" except for bugs. The
+ * "stray" IRQ 7 is documented behaviour of the 8259. It happens when there
+ * is a glitch on any of its interrupt inputs. Does it really interrupt when
+ * IRQ 7 is masked?
+ *
+ * XXX - unpend doesn't work for these, it sends them to the real handler.
+ *
+ * XXX - the race bug during initialization may be because I changed the
+ * order of switching from the stray to the real interrupt handler to before
+ * enabling interrupts. The old order looked unsafe but maybe it is OK with
+ * the stray interrupt handler installed. But these handlers only reduce
+ * the window of vulnerability - it is still open at the end of
+ * isa_configure().
+ *
+ * XXX - many comments are stale.
+ */
+
+ STRAYINTR(0,1,1, al)
+ STRAYINTR(1,1,1, al)
+ STRAYINTR(2,1,1, al)
+ STRAYINTR(3,1,1, al)
+ STRAYINTR(4,1,1, al)
+ STRAYINTR(5,1,1, al)
+ STRAYINTR(6,1,1, al)
+ STRAYINTR(7,1,1, al)
+ STRAYINTR(8,2,1_AND_2, ah)
+ STRAYINTR(9,2,1_AND_2, ah)
+ STRAYINTR(10,2,1_AND_2, ah)
+ STRAYINTR(11,2,1_AND_2, ah)
+ STRAYINTR(12,2,1_AND_2, ah)
+ STRAYINTR(13,2,1_AND_2, ah)
+ STRAYINTR(14,2,1_AND_2, ah)
+ STRAYINTR(15,2,1_AND_2, ah)
+#if 0
+ INTRSTRAY(255, _highmask, 255) ; call _isa_strayintr ; INTREXIT2
+#endif
+MCOUNT_LABEL(eintr)
+
+/*
+ * These are the interrupt counters, I moved them here from icu.s so that
+ * they are with the name table. rgrimes
+ *
+ * There are now lots of counters, this has been redone to work with
+ * Bruce Evans intr-0.1 code, which I modified some more to make it all
+ * work with vmstat.
+ */
+ .data
+ihandlers: /* addresses of interrupt handlers */
+ .space NHWI*4 /* actually resumption addresses for HWI's */
+ .long swi_tty, swi_net, 0, 0, 0, 0, 0, 0
+ .long 0, 0, 0, 0, 0, 0, swi_clock, swi_ast
+imasks: /* masks for interrupt handlers */
+ .space NHWI*4 /* padding; HWI masks are elsewhere */
+ .long SWI_TTY_MASK, SWI_NET_MASK, 0, 0, 0, 0, 0, 0
+ .long 0, 0, 0, 0, 0, 0, SWI_CLOCK_MASK, SWI_AST_MASK
+
+ .globl _intrcnt
+_intrcnt: /* used by vmstat to calc size of table */
+ .globl _intrcnt_bad7
+_intrcnt_bad7: .space 4 /* glitches on irq 7 */
+ .globl _intrcnt_bad15
+_intrcnt_bad15: .space 4 /* glitches on irq 15 */
+ .globl _intrcnt_stray
+_intrcnt_stray: .space 4 /* total count of stray interrupts */
+ .globl _intrcnt_actv
+_intrcnt_actv: .space NR_REAL_INT_HANDLERS * 4 /* active interrupts */
+ .globl _eintrcnt
+_eintrcnt: /* used by vmstat to calc size of table */
+
+/*
+ * Build the interrupt name table for vmstat
+ */
+
+#undef BUILD_FAST_VECTOR
+#define BUILD_FAST_VECTOR BUILD_VECTOR
+
+#undef BUILD_VECTOR
+#define BUILD_VECTOR(name, unit, irq_num, id_num, mask, handler, \
+ icu_num, icu_enables, reg) \
+ .ascii "name irq" ; \
+ .asciz "irq_num"
+/*
+ * XXX - use the __STRING and __CONCAT macros from <sys/cdefs.h> to stringize
+ * and concatenate names above and elsewhere. Note that __CONCAT doesn't
+ * work when nested.
+ */
+
+ .text
+ .globl _intrnames, _eintrnames
+_intrnames:
+ BUILD_VECTOR(bad,,7,,,,,,)
+ BUILD_VECTOR(bad,,15,,,,,,)
+ BUILD_VECTOR(stray,,,,,,,,)
+ BUILD_VECTORS
+
+_eintrnames:
diff --git a/sys/conf/Makefile.i386 b/sys/conf/Makefile.i386
new file mode 100644
index 0000000..20d1ee9
--- /dev/null
+++ b/sys/conf/Makefile.i386
@@ -0,0 +1,190 @@
+# Copyright 1990 W. Jolitz
+# from: @(#)Makefile.i386 7.1 5/10/91
+# $Id: Makefile.i386,v 1.23 1994/03/21 20:48:47 ats Exp $
+#
+# Makefile for FreeBSD
+#
+# This makefile is constructed from a machine description:
+# config machineid
+# Most changes should be made in the machine description
+# /sys/i386/conf/``machineid''
+# after which you should do
+# config machineid
+# Generic makefile changes should be made in
+# /sys/i386/conf/Makefile.i386
+# after which config should be rerun for all machines.
+#
+# N.B.: NO DEPENDENCIES ON FOLLOWING FLAGS ARE INVISIBLE TO MAKEFILE
+# IF YOU CHANGE THE DEFINITION OF ANY OF THESE RECOMPILE EVERYTHING
+#
+# -DTRACE compile in kernel tracing hooks
+# -DQUOTA compile in file system quotas
+#
+TOUCH= touch -f -c
+LD= /usr/bin/ld
+CC= cc
+CPP= cpp
+STRIP= strip
+DBSYM= /usr/sbin/dbsym
+
+S= ../..
+I386= ../../i386
+
+CWARNFLAGS=-W -Wreturn-type -Wcomment
+#
+# The following flags are next up for working on:
+# -Wredundant-decls -Wnested-externs
+#
+# When working on removing warnings from code, the `-Werror' flag should be
+# of material assistance.
+#
+COPTFLAGS=-O
+INCLUDES= -I. -I$S -I$S/sys
+COPTS= ${INCLUDES} ${IDENT} -DKERNEL -Di386 -DNPX
+ASFLAGS=
+CFLAGS= ${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS}
+LOAD_ADDRESS?= F0100000
+
+NORMAL_C= ${CC} -c ${CFLAGS} ${PROF} $<
+NORMAL_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $<
+NORMAL_S= ${CPP} -I. -DLOCORE ${COPTS} $< | ${AS} ${ASFLAGS} -o $*.o
+DRIVER_C= ${CC} -c ${CFLAGS} ${PROF} $<
+DRIVER_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $<
+SYSTEM_OBJS=locore.o exception.o swtch.o support.o ${OBJS} param.o \
+ ioconf.o conf.o machdep.o vnode_if.o
+SYSTEM_DEP=Makefile symbols.sort ${SYSTEM_OBJS} libkern.a
+SYSTEM_LD_HEAD= @echo loading $@; rm -f $@
+SYSTEM_LD= @${LD} -Bstatic -Z -T ${LOAD_ADDRESS} -o $@ -X ${SYSTEM_OBJS} vers.o libkern.a
+SYSTEM_LD_TAIL= @echo rearranging symbols; symorder symbols.sort $@; \
+ ${DBSYM} -fT ${LOAD_ADDRESS} $@; ${STRIP} -x $@; size $@; chmod 755 $@
+
+# (XXX) ok, this is weird. but we've got a working ed, and a broken ex, and
+# the script is identical for either... -- cgd
+#
+GPROF.EX= /usr/src/lib/csu.i386/gprof.ex
+PROFILE_C= ${CC} -S -c ${CFLAGS} $< ; \
+ ed - $*.s < ${GPROF.EX} ; \
+ ${AS} -o $@ $*.s ; \
+ rm -f $*.s
+
+%OBJS
+
+%CFILES
+
+%LOAD
+
+libkern.a:
+ -@if [ X${PROF} = X ]; \
+ then ln -s $S/libkern/obj/libkern.a libkern.a; \
+ else ln -s $S/libkern/obj/libkern_p.a libkern.a; \
+ fi; \
+ echo ln -s $S/libkern/obj/libkern.a libkern.a
+
+clean:
+ rm -f eddep *kernel tags *.o locore.i [a-uw-z]*.s \
+ errs linterrs makelinks genassym ,assym.s stamp-assym
+
+#lint: /tmp param.c
+# @lint -hbxn -I. -DGENERIC -Dvolatile= ${COPTS} ${PARAM} \
+# ${I386}/i386/Locore.c ${CFILES} ioconf.c param.c | \
+# grep -v 'struct/union .* never defined' | \
+# grep -v 'possible pointer alignment problem'
+
+symbols.sort: ${I386}/i386/symbols.raw
+ grep -v '^#' ${I386}/i386/symbols.raw \
+ | sed 's/^ //' | sort -u > symbols.sort
+
+locore.o: assym.s ${I386}/i386/locore.s machine/trap.h machine/psl.h \
+ machine/pte.h ${I386}/isa/vector.s ${I386}/isa/icu.s \
+ $S/sys/errno.h machine/specialreg.h \
+ ${I386}/isa/icu.h ${I386}/isa/isa.h vector.h $S/net/netisr.h \
+ machine/asmacros.h
+ ${CPP} -I. -DLOCORE ${COPTS} ${I386}/i386/locore.s | \
+ ${AS} ${ASFLAGS} -o locore.o
+
+exception.o: assym.s ${I386}/i386/exception.s machine/trap.h \
+ ${I386}/isa/vector.s ${I386}/isa/icu.s \
+ $S/sys/errno.h ${I386}/isa/icu.h ${I386}/isa/isa.h vector.h \
+ $S/net/netisr.h machine/asmacros.h
+ ${CPP} -I. -DLOCORE ${COPTS} ${I386}/i386/exception.s | \
+ ${AS} ${ASFLAGS} -o exception.o
+
+swtch.o: assym.s ${I386}/i386/swtch.s \
+ $S/sys/errno.h machine/asmacros.h
+ ${CPP} -I. ${COPTS} ${I386}/i386/swtch.s | \
+ ${AS} ${ASFLAGS} -o swtch.o
+
+support.o: assym.s ${I386}/i386/support.s \
+ $S/sys/errno.h machine/asmacros.h
+ ${CPP} -I. ${COPTS} ${I386}/i386/support.s | \
+ ${AS} ${ASFLAGS} -o support.o
+
+machdep.o: ${I386}/i386/machdep.c Makefile
+ ${CC} -c ${CFLAGS} -DLOAD_ADDRESS=0x${LOAD_ADDRESS} ${PROF} $<
+
+# the following is necessary because autoconf.o depends on #if GENERIC
+autoconf.o: Makefile
+
+# depend on network configuration
+af.o uipc_proto.o locore.o: Makefile
+
+# depends on KDB (cons.o also depends on GENERIC)
+trap.o cons.o: Makefile
+
+assym.s: genassym
+ ./genassym >,assym.s
+ if cmp -s assym.s ,assym.s; then \
+ rm -f ,assym.s; \
+ else \
+ rm -f assym.s; \
+ mv ,assym.s assym.s; \
+ fi
+
+# Some of the defines that genassym outputs may well depend on the
+# value of kernel options.
+genassym: Makefile
+ ${CC} ${INCLUDES} -DKERNEL ${IDENT} ${PARAM} \
+ ${I386}/i386/genassym.c -static -o genassym
+
+depend: assym.s param.c vnode_if.h
+ sh /usr/bin/mkdep -DLOAD_ADDRESS=0x${LOAD_ADDRESS} ${COPTS} ${CFILES} ioconf.c param.c ${I386}/i386/conf.c
+ sh /usr/bin/mkdep -a -p ${INCLUDES} ${IDENT} ${PARAM} ${I386}/i386/genassym.c
+
+links:
+ egrep '#if' ${CFILES} | sed -f $S/conf/defines | \
+ sed -e 's/:.*//' -e 's/\.c/.o/' | sort -u > dontlink
+ echo ${CFILES} | tr -s ' ' '\12' | sed 's/\.c/.o/' | \
+ sort -u | comm -23 - dontlink | \
+ sed 's,../.*/\(.*.o\),rm -f \1;ln -s ../GENERIC/\1 \1,' > makelinks
+ sh makelinks && rm -f dontlink
+
+tags:
+ @echo "see $S/kern/Makefile for tags"
+
+ioconf.o: ioconf.c $S/sys/param.h machine/pte.h $S/sys/buf.h \
+ ${I386}/isa/isa_device.h ${I386}/isa/isa.h ${I386}/isa/icu.h
+ ${CC} -c ${CFLAGS} ioconf.c
+
+conf.o: ${I386}/i386/conf.c $S/sys/conf.h
+ ${CC} -c ${CFLAGS} ${I386}/i386/conf.c
+
+param.c: $S/conf/param.c
+ -rm -f param.c
+ cp $S/conf/param.c .
+
+param.o: param.c Makefile
+ ${CC} -c ${CFLAGS} ${PARAM} param.c
+
+vers.o: ${SYSTEM_DEP} ${SYSTEM_SWAP_DEP}
+ sh $S/conf/newvers.sh ${KERN_IDENT} ${IDENT}
+ ${CC} ${CFLAGS} -c vers.c
+
+vnode_if.c: $S/kern/vnode_if.sh $S/kern/vnode_if.src
+ sh $S/kern/vnode_if.sh $S/kern/vnode_if.src
+vnode_if.h: $S/kern/vnode_if.sh $S/kern/vnode_if.src
+ sh $S/kern/vnode_if.sh $S/kern/vnode_if.src
+
+%RULES
+
+# DO NOT DELETE THIS LINE -- make depend uses it
+
diff --git a/sys/conf/Makefile.powerpc b/sys/conf/Makefile.powerpc
new file mode 100644
index 0000000..20d1ee9
--- /dev/null
+++ b/sys/conf/Makefile.powerpc
@@ -0,0 +1,190 @@
+# Copyright 1990 W. Jolitz
+# from: @(#)Makefile.i386 7.1 5/10/91
+# $Id: Makefile.i386,v 1.23 1994/03/21 20:48:47 ats Exp $
+#
+# Makefile for FreeBSD
+#
+# This makefile is constructed from a machine description:
+# config machineid
+# Most changes should be made in the machine description
+# /sys/i386/conf/``machineid''
+# after which you should do
+# config machineid
+# Generic makefile changes should be made in
+# /sys/i386/conf/Makefile.i386
+# after which config should be rerun for all machines.
+#
+# N.B.: NO DEPENDENCIES ON FOLLOWING FLAGS ARE INVISIBLE TO MAKEFILE
+# IF YOU CHANGE THE DEFINITION OF ANY OF THESE RECOMPILE EVERYTHING
+#
+# -DTRACE compile in kernel tracing hooks
+# -DQUOTA compile in file system quotas
+#
+TOUCH= touch -f -c
+LD= /usr/bin/ld
+CC= cc
+CPP= cpp
+STRIP= strip
+DBSYM= /usr/sbin/dbsym
+
+S= ../..
+I386= ../../i386
+
+CWARNFLAGS=-W -Wreturn-type -Wcomment
+#
+# The following flags are next up for working on:
+# -Wredundant-decls -Wnested-externs
+#
+# When working on removing warnings from code, the `-Werror' flag should be
+# of material assistance.
+#
+COPTFLAGS=-O
+INCLUDES= -I. -I$S -I$S/sys
+COPTS= ${INCLUDES} ${IDENT} -DKERNEL -Di386 -DNPX
+ASFLAGS=
+CFLAGS= ${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS}
+LOAD_ADDRESS?= F0100000
+
+NORMAL_C= ${CC} -c ${CFLAGS} ${PROF} $<
+NORMAL_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $<
+NORMAL_S= ${CPP} -I. -DLOCORE ${COPTS} $< | ${AS} ${ASFLAGS} -o $*.o
+DRIVER_C= ${CC} -c ${CFLAGS} ${PROF} $<
+DRIVER_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $<
+SYSTEM_OBJS=locore.o exception.o swtch.o support.o ${OBJS} param.o \
+ ioconf.o conf.o machdep.o vnode_if.o
+SYSTEM_DEP=Makefile symbols.sort ${SYSTEM_OBJS} libkern.a
+SYSTEM_LD_HEAD= @echo loading $@; rm -f $@
+SYSTEM_LD= @${LD} -Bstatic -Z -T ${LOAD_ADDRESS} -o $@ -X ${SYSTEM_OBJS} vers.o libkern.a
+SYSTEM_LD_TAIL= @echo rearranging symbols; symorder symbols.sort $@; \
+ ${DBSYM} -fT ${LOAD_ADDRESS} $@; ${STRIP} -x $@; size $@; chmod 755 $@
+
+# (XXX) ok, this is weird. but we've got a working ed, and a broken ex, and
+# the script is identical for either... -- cgd
+#
+GPROF.EX= /usr/src/lib/csu.i386/gprof.ex
+PROFILE_C= ${CC} -S -c ${CFLAGS} $< ; \
+ ed - $*.s < ${GPROF.EX} ; \
+ ${AS} -o $@ $*.s ; \
+ rm -f $*.s
+
+%OBJS
+
+%CFILES
+
+%LOAD
+
+libkern.a:
+ -@if [ X${PROF} = X ]; \
+ then ln -s $S/libkern/obj/libkern.a libkern.a; \
+ else ln -s $S/libkern/obj/libkern_p.a libkern.a; \
+ fi; \
+ echo ln -s $S/libkern/obj/libkern.a libkern.a
+
+clean:
+ rm -f eddep *kernel tags *.o locore.i [a-uw-z]*.s \
+ errs linterrs makelinks genassym ,assym.s stamp-assym
+
+#lint: /tmp param.c
+# @lint -hbxn -I. -DGENERIC -Dvolatile= ${COPTS} ${PARAM} \
+# ${I386}/i386/Locore.c ${CFILES} ioconf.c param.c | \
+# grep -v 'struct/union .* never defined' | \
+# grep -v 'possible pointer alignment problem'
+
+symbols.sort: ${I386}/i386/symbols.raw
+ grep -v '^#' ${I386}/i386/symbols.raw \
+ | sed 's/^ //' | sort -u > symbols.sort
+
+locore.o: assym.s ${I386}/i386/locore.s machine/trap.h machine/psl.h \
+ machine/pte.h ${I386}/isa/vector.s ${I386}/isa/icu.s \
+ $S/sys/errno.h machine/specialreg.h \
+ ${I386}/isa/icu.h ${I386}/isa/isa.h vector.h $S/net/netisr.h \
+ machine/asmacros.h
+ ${CPP} -I. -DLOCORE ${COPTS} ${I386}/i386/locore.s | \
+ ${AS} ${ASFLAGS} -o locore.o
+
+exception.o: assym.s ${I386}/i386/exception.s machine/trap.h \
+ ${I386}/isa/vector.s ${I386}/isa/icu.s \
+ $S/sys/errno.h ${I386}/isa/icu.h ${I386}/isa/isa.h vector.h \
+ $S/net/netisr.h machine/asmacros.h
+ ${CPP} -I. -DLOCORE ${COPTS} ${I386}/i386/exception.s | \
+ ${AS} ${ASFLAGS} -o exception.o
+
+swtch.o: assym.s ${I386}/i386/swtch.s \
+ $S/sys/errno.h machine/asmacros.h
+ ${CPP} -I. ${COPTS} ${I386}/i386/swtch.s | \
+ ${AS} ${ASFLAGS} -o swtch.o
+
+support.o: assym.s ${I386}/i386/support.s \
+ $S/sys/errno.h machine/asmacros.h
+ ${CPP} -I. ${COPTS} ${I386}/i386/support.s | \
+ ${AS} ${ASFLAGS} -o support.o
+
+machdep.o: ${I386}/i386/machdep.c Makefile
+ ${CC} -c ${CFLAGS} -DLOAD_ADDRESS=0x${LOAD_ADDRESS} ${PROF} $<
+
+# the following is necessary because autoconf.o depends on #if GENERIC
+autoconf.o: Makefile
+
+# depend on network configuration
+af.o uipc_proto.o locore.o: Makefile
+
+# depends on KDB (cons.o also depends on GENERIC)
+trap.o cons.o: Makefile
+
+assym.s: genassym
+ ./genassym >,assym.s
+ if cmp -s assym.s ,assym.s; then \
+ rm -f ,assym.s; \
+ else \
+ rm -f assym.s; \
+ mv ,assym.s assym.s; \
+ fi
+
+# Some of the defines that genassym outputs may well depend on the
+# value of kernel options.
+genassym: Makefile
+ ${CC} ${INCLUDES} -DKERNEL ${IDENT} ${PARAM} \
+ ${I386}/i386/genassym.c -static -o genassym
+
+depend: assym.s param.c vnode_if.h
+ sh /usr/bin/mkdep -DLOAD_ADDRESS=0x${LOAD_ADDRESS} ${COPTS} ${CFILES} ioconf.c param.c ${I386}/i386/conf.c
+ sh /usr/bin/mkdep -a -p ${INCLUDES} ${IDENT} ${PARAM} ${I386}/i386/genassym.c
+
+links:
+ egrep '#if' ${CFILES} | sed -f $S/conf/defines | \
+ sed -e 's/:.*//' -e 's/\.c/.o/' | sort -u > dontlink
+ echo ${CFILES} | tr -s ' ' '\12' | sed 's/\.c/.o/' | \
+ sort -u | comm -23 - dontlink | \
+ sed 's,../.*/\(.*.o\),rm -f \1;ln -s ../GENERIC/\1 \1,' > makelinks
+ sh makelinks && rm -f dontlink
+
+tags:
+ @echo "see $S/kern/Makefile for tags"
+
+ioconf.o: ioconf.c $S/sys/param.h machine/pte.h $S/sys/buf.h \
+ ${I386}/isa/isa_device.h ${I386}/isa/isa.h ${I386}/isa/icu.h
+ ${CC} -c ${CFLAGS} ioconf.c
+
+conf.o: ${I386}/i386/conf.c $S/sys/conf.h
+ ${CC} -c ${CFLAGS} ${I386}/i386/conf.c
+
+param.c: $S/conf/param.c
+ -rm -f param.c
+ cp $S/conf/param.c .
+
+param.o: param.c Makefile
+ ${CC} -c ${CFLAGS} ${PARAM} param.c
+
+vers.o: ${SYSTEM_DEP} ${SYSTEM_SWAP_DEP}
+ sh $S/conf/newvers.sh ${KERN_IDENT} ${IDENT}
+ ${CC} ${CFLAGS} -c vers.c
+
+vnode_if.c: $S/kern/vnode_if.sh $S/kern/vnode_if.src
+ sh $S/kern/vnode_if.sh $S/kern/vnode_if.src
+vnode_if.h: $S/kern/vnode_if.sh $S/kern/vnode_if.src
+ sh $S/kern/vnode_if.sh $S/kern/vnode_if.src
+
+%RULES
+
+# DO NOT DELETE THIS LINE -- make depend uses it
+
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
new file mode 100644
index 0000000..90c79b8
--- /dev/null
+++ b/sys/conf/NOTES
@@ -0,0 +1,197 @@
+#
+# LINT -- config file for checking all the sources, tries to pull in
+# as much of the source tree as it can.
+#
+# This kernel is NOT MEANT to be runnable!
+#
+# $Id: LINT,v 1.70 1994/05/17 23:20:32 jkh Exp $
+#
+
+machine "i386"
+cpu "I386_CPU"
+cpu "I486_CPU"
+ident LINT
+timezone 8 dst
+maxusers 10
+maxfdescs 2048 #Max file descriptors per process
+options MATH_EMULATE #Support for x87 emulation
+
+# Do not use in binary distributions
+#options GPL_MATH_EMULATE #Support for x87 emualtion via
+ #new math emulator
+
+config "386bsd" root on wd0 swap on wd0 and sd0 dumps on wd0
+
+#
+# options that appear as inline #ifdef's
+#
+options "COM_BIDIR" #Bidirectional support in sys/isa/sio.c
+options "COM_MULTIPORT" #Multiport support in sys/isa/sio.c
+options "FIFO_TRIGGER=FIFO_TRIGGER_1" #Use this fifo value in sio.c
+
+options "COMPAT_43" #compatible with BSD 4.3
+options "SYMTAB_SPACE=113498" #This kernel needs LOTS of symtable
+options GATEWAY #internetwork gateway
+options KTRACE #kernel tracing
+
+options "NCONS=8" #number of syscons virtual consoles
+options "FAT_CURSOR" #block cursor in syscons or pccons
+options "STAR_SAVER" #syscons "stars" screen saver
+options "FADE_SAVER" #syscons "fade" screen saver
+options "SNAKE_SAVER" #syscons "snake" screen saver
+options "BLANK_SAVER" #syscons "blank" screen saver
+
+#options ALLOW_CONFLICT_IOADDR #no IO addr conflict checks (PS/2 mice)
+#options ALLOW_CONFLICT_IRQ #no IRQ conflict checks (mport serial)
+
+options "TCP_COMPAT_42" #tcp/ip compatible with 4.2
+ # ^^^ NOT RECOMMENDED FOR NORMAL USE
+options UCONSOLE #x console support
+options XSERVER #xserver
+options DECBIT #here because clnp.h wanted it here
+ #support for CLNP ``congestion
+ #experienced'' bit in ISO-TP
+options TROLL #CLNP network error simulator
+options ICMPPRINTFS #ICMP packet dump by printf()
+options NSERRPRINTFS #ditto for XNS Error protocol
+ #^^above three NOT RECOMMENTED
+options FASTLINKS #support for fast symbolic links
+options MACHVMCOMPAT #support for Mach-style vm calls
+options IPBROADCASTECHO=1 #send reply to broadcast pings
+options IPMASKAGENT=1 #send reply to icmp mask requests
+options TPCONS #support X.25 network-layer service
+options USER_LDT #allow user-level control of i386 ldt
+
+# See /sys/i386/doc/sound.doc for information about EXCLUDE options for
+# the sound drivers.
+
+# Multicast support.
+options MULTICAST # Multicast code
+options MROUTING # Multicast routing
+
+#
+# options that are in sys/conf/files
+#
+pseudo-device bpfilter 4 #berkeley packet filter
+options CCITT
+device cd0 #Only need one of these, the code dynamically grows
+device ch0
+pseudo-device ddb
+pseudo-device devpager
+options EON
+pseudo-device ether
+options FIFO
+#pseudo-device imp
+options INET #Internet communications protocols
+options ISO
+options ISOFS #ISO 9660 File System
+pseudo-device loop
+options MFS #Memory File System
+options NFS #Network File System
+options NS #Xerox NS communications protocols
+options NSIP #XNS over IP
+options PCFS #PC (MSDOS) File System
+pseudo-device ppp 2
+pseudo-device pty 4
+options QUOTA #enable disk quotas
+options RMP #HP remote maint protocol
+controller scbus0
+device sd0
+device sd1
+device sd2
+device sd3
+pseudo-device sl 2
+device st0
+device st1
+pseudo-device swappager
+options SYSVSHM
+options "SHMMAXPGS=64" # 256Kb of sharable memory
+options SYSVSEM
+options SYSVMSG
+#pseudo-device tb #tablet line discipline.
+options TPIP # ISO TP class 4 over IP
+#pseudo-device tun
+device uk0 #unknown scsi devices
+pseudo-device vnodepager
+
+#
+# options that are in sys/i386/conf/files.i386
+#
+#This is needed here so the isa? below will work
+controller isa0
+
+# driver for the Adaptec 154x SCSI cards.
+controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr
+# driver for the Adaptec 174x SCSI cards.
+controller ahb0 at isa? bio irq 11 vector ahbintr
+# driver for the Bustek 742.
+controller bt0 at isa? port "IO_BT0" bio irq 12 vector btintr
+# driver for the Seagate ST01/ST02 card, not yet finished.
+#controller sg0 at isa? bio irq 5 iomem 0xc8000 iosiz 0x2000 vector sgintr
+controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk fd0 at fdc0 drive 0
+disk fd1 at fdc0 drive 1
+tape ft0 at fdc0 drive 2
+
+# driver for the Western Digital and SMCC WD80xx cards, for the Novell
+# NE1000/2000 card and the 3COM 3C503 card.
+device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr
+# driver for the AT&T Starlan card.
+device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr
+#driver for the Isolan AT 4114-0 and the Isolink 4110 ethernet card.
+device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr
+#device ix0 at isa? port 0x320 net irq 10 iomem 0xd0000 iosiz 32768 vector ixintr
+# driver for the Etherlink III ( 3C509 ) card, beta version.
+device ep0 at isa? port 0x300 net irq 10 vector epintr
+#driver for the 3c501
+device el0 at isa? port 0x300 net irq 9 vector elintr
+
+#special cased above:
+#controller isa0
+# interruptless parallel printer port driver
+device lpa0 at isa? port "IO_LPT1" tty
+device lpa1 at isa? port "IO_LPT2" tty
+# interrupt driven parallel printer port driver
+device lpt0 at isa? port "IO_LPT3" tty irq 7 vector lptintr
+# Driver for Mitsumi CD-ROM players
+device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr
+# Driver for Logitech and ATI inport bus mice
+device mse0 at isa? port 0x23c tty irq 5 vector mseintr
+device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
+device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint
+#only one of pc0 or sc0 allowed
+#device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
+device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
+device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr
+device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr
+device sio3 at isa? port "IO_COM4" tty irq 9 vector siointr
+#PS/2 mouse driver (must follow pc0 or sc0 if enabled). Also enable
+#ALLOW_CONFLICT_IOADDR option (see above) if you want to use this.
+#device psm0 at isa? port "IO_KBD" tty irq 12 vector psmintr
+
+pseudo-device speaker
+#tw device-driver
+controller uha0 at isa? port "IO_UHA0" bio irq 14 drq 5 vector uhaintr
+controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr
+disk wd0 at wdc0 drive 0
+disk wd1 at wdc0 drive 1
+controller wdc1 at isa? port "IO_WD2" bio irq 15 vector wdintr
+disk wd2 at wdc1 drive 0
+disk wd3 at wdc1 drive 1
+device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr
+
+# Various sound card drivers.
+# See /sys/doc/sound.doc for more information.
+device snd5 at isa? port 0x330 irq 6 vector mpuintr
+device snd4 at isa? port 0x220 irq 15 drq 6 vector gusintr
+device snd3 at isa? port 0x388 irq 10 drq 6 vector pasintr
+device snd2 at isa? port 0x220 irq 7 drq 1 vector sbintr
+device snd6 at isa? port 0x220 irq 7 drq 5 vector sbintr
+device snd7 at isa? port 0x300
+device snd1 at isa? port 0x388
+
+# The digital speaker driver (/dev/pcaudio).
+device pca0 at isa? tty
+
+# options that have not been resolved yet
+pseudo-device log
diff --git a/sys/conf/defines b/sys/conf/defines
new file mode 100644
index 0000000..ea70463
--- /dev/null
+++ b/sys/conf/defines
@@ -0,0 +1,15 @@
+/:#if.*[ \t]*KPROF/d
+/:#if.*[ \t]*PGINPROF/d
+/:#if.*[ \t]*UNFAST/d
+/:#if.*[ \t]*INSECURE/d
+/:#if.*[ \t]*TRACE/d
+/:#if.*[ \t]*DISKMON/d
+/:#if.*[ \t]*INTRLVE/d
+/:#if.*[ \t]*lint/d
+/:#if.*[ \t]*notdef/d
+/:#if.*[ \t]*unneeded/d
+/:#if.*[ \t]*vax/d
+/:#if.*[ \t]*TCPTRUEOOB/d
+/:#if.*[ \t]*irele/d
+/:#if.*[ \t]*ilock/d
+/:#if.*[ \t]*notyet/d
diff --git a/sys/conf/files b/sys/conf/files
new file mode 100644
index 0000000..c62ea0f
--- /dev/null
+++ b/sys/conf/files
@@ -0,0 +1,302 @@
+ddb/db_access.c optional ddb
+ddb/db_aout.c optional ddb
+ddb/db_break.c optional ddb
+ddb/db_command.c optional ddb
+ddb/db_examine.c optional ddb
+ddb/db_expr.c optional ddb
+ddb/db_input.c optional ddb
+ddb/db_lex.c optional ddb
+ddb/db_output.c optional ddb
+ddb/db_print.c optional ddb
+ddb/db_run.c optional ddb
+ddb/db_sym.c optional ddb
+ddb/db_trap.c optional ddb
+ddb/db_variables.c optional ddb
+ddb/db_watch.c optional ddb
+ddb/db_write_cmd.c optional ddb
+isofs/cd9660/cd9660_bmap.c optional cd9660
+isofs/cd9660/cd9660_lookup.c optional cd9660
+isofs/cd9660/cd9660_node.c optional cd9660
+isofs/cd9660/cd9660_rrip.c optional cd9660
+isofs/cd9660/cd9660_util.c optional cd9660
+isofs/cd9660/cd9660_vfsops.c optional cd9660
+isofs/cd9660/cd9660_vnops.c optional cd9660
+kdb/kdb_access.c optional kadb
+kdb/kdb_command.c optional kadb
+kdb/kdb_ctype.c optional kadb
+kdb/kdb_expr.c optional kadb
+kdb/kdb_format.c optional kadb
+kdb/kdb_input.c optional kadb
+kdb/kdb_message.c optional kadb
+kdb/kdb_output.c optional kadb
+kdb/kdb_pcs.c optional kadb
+kdb/kdb_print.c optional kadb
+kdb/kdb_runpcs.c optional kadb
+kdb/kdb_sym.c optional kadb
+kdb/kdb_trap.c optional kadb
+kern/imgact_aout.c standard
+kern/imgact_shell.c standard
+kern/init_main.c standard
+kern/init_sysent.c standard
+kern/kern_acct.c standard
+kern/kern_clock.c standard
+kern/kern_descrip.c standard
+kern/kern_exec.c standard
+kern/kern_exit.c standard
+kern/kern_fork.c standard
+kern/kern_ktrace.c standard
+kern/kern_malloc.c standard
+kern/kern_physio.c standard
+kern/kern_proc.c standard
+kern/kern_prot.c standard
+kern/kern_resource.c standard
+kern/kern_sig.c standard
+kern/kern_subr.c standard
+kern/kern_synch.c standard
+kern/kern_sysctl.c standard
+kern/kern_time.c standard
+kern/kern_xxx.c standard
+kern/subr_log.c standard
+kern/subr_prf.c standard
+kern/subr_prof.c standard
+kern/subr_rlist.c standard
+kern/subr_rmap.c standard
+kern/subr_xxx.c standard
+kern/sys_generic.c standard
+kern/sys_process.c standard
+kern/sys_socket.c standard
+kern/sysv_shm.c optional sysvshm
+kern/tty.c standard
+kern/tty_compat.c standard
+kern/tty_conf.c standard
+kern/tty_pty.c optional pty
+kern/tty_subr.c standard
+kern/tty_tb.c optional tb
+kern/tty_tty.c standard
+kern/uipc_domain.c standard
+kern/uipc_mbuf.c standard
+kern/uipc_proto.c standard
+kern/uipc_socket.c standard
+kern/uipc_socket2.c standard
+kern/uipc_syscalls.c standard
+kern/uipc_usrreq.c standard
+kern/vfs_bio.c standard
+kern/vfs_cache.c standard
+kern/vfs_cluster.c standard
+kern/vfs_conf.c standard
+kern/vfs_init.c standard
+kern/vfs_lookup.c standard
+kern/vfs_subr.c standard
+kern/vfs_syscalls.c standard
+kern/vfs_vnops.c standard
+miscfs/deadfs/dead_vnops.c standard
+miscfs/fdesc/fdesc_vfsops.c optional fdesc
+miscfs/fdesc/fdesc_vnops.c optional fdesc
+miscfs/fifofs/fifo_vnops.c optional fifo
+miscfs/kernfs/kernfs_vfsops.c optional kernfs
+miscfs/kernfs/kernfs_vnops.c optional kernfs
+miscfs/nullfs/null_subr.c optional nullfs
+miscfs/nullfs/null_vfsops.c optional nullfs
+miscfs/nullfs/null_vnops.c optional nullfs
+miscfs/portal/portal_vfsops.c optional portal
+miscfs/portal/portal_vnops.c optional portal
+miscfs/procfs/procfs_subr.c optional procfs
+miscfs/procfs/procfs_vnops.c optional procfs
+miscfs/procfs/procfs_vfsops.c optional procfs
+miscfs/procfs/procfs_note.c optional procfs
+miscfs/procfs/procfs_mem.c optional procfs
+miscfs/procfs/procfs_ctl.c optional procfs
+miscfs/procfs/procfs_status.c optional procfs
+miscfs/procfs/procfs_regs.c optional procfs
+miscfs/procfs/procfs_fpregs.c optional procfs
+miscfs/specfs/spec_vnops.c standard
+miscfs/umapfs/umap_subr.c optional umapfs
+miscfs/umapfs/umap_vfsops.c optional umapfs
+miscfs/umapfs/umap_vnops.c optional umapfs
+miscfs/union/union_subr.c optional union
+miscfs/union/union_vfsops.c optional union
+miscfs/union/union_vnops.c optional union
+net/bpf.c optional bpfilter
+net/bpf_filter.c optional bpfilter
+net/if.c standard
+net/if_ethersubr.c optional ether
+net/if_loop.c optional loop
+net/if_sl.c optional sl
+net/radix.c standard
+net/raw_cb.c standard
+net/raw_usrreq.c standard
+net/route.c standard
+net/rtsock.c standard
+net/slcompress.c optional sl
+netccitt/ccitt_proto.c optional ccitt
+netccitt/llc_input.c optional llc
+netccitt/llc_output.c optional llc
+netccitt/llc_subr.c optional llc
+netccitt/llc_timer.c optional llc
+netccitt/pk_llcsubr.c optional llc
+netccitt/pk_llcsubr.c optional hdlc
+netccitt/hd_debug.c optional hdlc
+netccitt/hd_input.c optional hdlc
+netccitt/hd_output.c optional hdlc
+netccitt/hd_subr.c optional hdlc
+netccitt/hd_timer.c optional hdlc
+netccitt/if_x25subr.c optional ccitt
+netccitt/pk_acct.c optional ccitt
+netccitt/pk_debug.c optional ccitt
+netccitt/pk_input.c optional ccitt
+netccitt/pk_output.c optional ccitt
+netccitt/pk_subr.c optional ccitt
+netccitt/pk_timer.c optional ccitt
+netccitt/pk_usrreq.c optional ccitt
+netimp/if_imp.c optional imp
+netimp/if_imphost.c optional imp
+netimp/raw_imp.c optional imp
+netinet/if_ether.c optional ether
+netinet/igmp.c optional inet
+netinet/in.c optional inet
+netinet/in_pcb.c optional inet
+netinet/in_proto.c optional inet
+netinet/ip_icmp.c optional inet
+netinet/ip_input.c optional inet
+netinet/ip_mroute.c optional inet mrouting
+netinet/ip_output.c optional inet
+netinet/raw_ip.c optional inet
+netinet/tcp_debug.c optional inet
+netinet/tcp_input.c optional inet
+netinet/tcp_output.c optional inet
+netinet/tcp_subr.c optional inet
+netinet/tcp_timer.c optional inet
+netinet/tcp_usrreq.c optional inet
+netinet/udp_usrreq.c optional inet
+netiso/clnp_debug.c optional iso
+netiso/clnp_er.c optional iso
+netiso/clnp_frag.c optional iso
+netiso/clnp_input.c optional iso
+netiso/clnp_options.c optional iso
+netiso/clnp_output.c optional iso
+netiso/clnp_raw.c optional iso
+netiso/clnp_subr.c optional iso
+netiso/clnp_timer.c optional iso
+netiso/cltp_usrreq.c optional iso
+netiso/esis.c optional iso
+netiso/idrp_usrreq.c optional iso
+netiso/if_eon.c optional eon
+netiso/iso.c optional iso
+netiso/iso_chksum.c optional iso
+netiso/iso_pcb.c optional iso
+netiso/iso_proto.c optional iso
+netiso/iso_snpac.c optional iso
+netiso/tp_astring.c optional iso
+netiso/tp_astring.c optional tpip
+netiso/tp_cons.c optional iso
+netiso/tp_driver.c optional iso
+netiso/tp_driver.c optional tpip
+netiso/tp_emit.c optional iso
+netiso/tp_emit.c optional tpip
+netiso/tp_inet.c optional iso
+netiso/tp_inet.c optional tpip
+netiso/tp_input.c optional iso
+netiso/tp_input.c optional tpip
+netiso/tp_iso.c optional iso
+netiso/tp_meas.c optional iso
+netiso/tp_meas.c optional tpip
+netiso/tp_output.c optional iso
+netiso/tp_output.c optional tpip
+netiso/tp_pcb.c optional iso
+netiso/tp_pcb.c optional tpip
+netiso/tp_subr.c optional iso
+netiso/tp_subr.c optional tpip
+netiso/tp_subr2.c optional iso
+netiso/tp_subr2.c optional tpip
+netiso/tp_timer.c optional iso
+netiso/tp_timer.c optional tpip
+netiso/tp_trace.c optional iso
+netiso/tp_trace.c optional tpip
+netiso/tp_usrreq.c optional iso
+netiso/tp_usrreq.c optional tpip
+netiso/tuba_subr.c optional iso tuba
+netiso/tuba_table.c optional iso tuba
+netiso/tuba_usrreq.c optional iso tuba
+netns/idp_usrreq.c optional ns
+netns/ns.c optional ns
+netns/ns_error.c optional ns
+netns/ns_input.c optional ns
+netns/ns_ip.c optional ns
+netns/ns_output.c optional ns
+netns/ns_pcb.c optional ns
+netns/ns_proto.c optional ns
+netns/spp_debug.c optional ns
+netns/spp_usrreq.c optional ns
+nfs/nfs_bio.c optional nfs
+nfs/nfs_node.c optional nfs
+nfs/nfs_nqlease.c optional nfs
+nfs/nfs_serv.c optional nfs
+nfs/nfs_socket.c optional nfs
+nfs/nfs_srvcache.c optional nfs
+nfs/nfs_subs.c optional nfs
+nfs/nfs_syscalls.c optional nfs
+nfs/nfs_vfsops.c optional nfs
+nfs/nfs_vnops.c optional nfs
+scsi/cd.c optional cd
+scsi/ch.c optional ch
+scsi/scsiconf.c optional scbus
+scsi/scsi_base.c optional scbus
+scsi/scsi_ioctl.c optional scbus
+scsi/sd.c optional sd
+scsi/st.c optional st
+scsi/su.c optional su
+scsi/uk.c optional uk
+ufs/ffs/ffs_alloc.c optional ffs
+ufs/ffs/ffs_alloc.c optional mfs
+ufs/ffs/ffs_balloc.c optional ffs
+ufs/ffs/ffs_balloc.c optional mfs
+ufs/ffs/ffs_inode.c optional ffs
+ufs/ffs/ffs_inode.c optional mfs
+ufs/ffs/ffs_subr.c optional ffs
+ufs/ffs/ffs_subr.c optional mfs
+ufs/ffs/ffs_tables.c optional ffs
+ufs/ffs/ffs_tables.c optional mfs
+ufs/ffs/ffs_vfsops.c optional ffs
+ufs/ffs/ffs_vfsops.c optional mfs
+ufs/ffs/ffs_vnops.c optional ffs
+ufs/ffs/ffs_vnops.c optional mfs
+ufs/lfs/lfs_alloc.c optional lfs
+ufs/lfs/lfs_bio.c optional lfs
+ufs/lfs/lfs_balloc.c optional lfs
+ufs/lfs/lfs_cksum.c optional lfs
+ufs/lfs/lfs_debug.c optional lfs
+ufs/lfs/lfs_inode.c optional lfs
+ufs/lfs/lfs_segment.c optional lfs
+ufs/lfs/lfs_subr.c optional lfs
+ufs/lfs/lfs_syscalls.c optional lfs
+ufs/lfs/lfs_vfsops.c optional lfs
+ufs/lfs/lfs_vnops.c optional lfs
+ufs/mfs/mfs_vfsops.c optional mfs
+ufs/mfs/mfs_vnops.c optional mfs
+ufs/ufs/ufs_bmap.c standard
+ufs/ufs/ufs_disksubr.c standard
+ufs/ufs/ufs_ihash.c standard
+ufs/ufs/ufs_inode.c standard
+ufs/ufs/ufs_lockf.c standard
+ufs/ufs/ufs_lookup.c standard
+ufs/ufs/ufs_quota.c standard
+ufs/ufs/ufs_vfsops.c standard
+ufs/ufs/ufs_vnops.c standard
+vm/device_pager.c standard
+vm/kern_lock.c standard
+vm/swap_pager.c standard
+vm/vm_fault.c standard
+vm/vm_glue.c standard
+vm/vm_init.c standard
+vm/vm_kern.c standard
+vm/vm_map.c standard
+vm/vm_meter.c standard
+vm/vm_mmap.c standard
+vm/vm_object.c standard
+vm/vm_page.c standard
+vm/vm_pageout.c standard
+vm/vm_pager.c standard
+vm/vm_swap.c standard
+vm/vm_unix.c standard
+vm/vm_user.c standard
+vm/vnode_pager.c standard
diff --git a/sys/conf/files.i386 b/sys/conf/files.i386
new file mode 100644
index 0000000..f70e799
--- /dev/null
+++ b/sys/conf/files.i386
@@ -0,0 +1,117 @@
+# This file tells config what files go into building a kernel,
+# files marked standard are always included.
+#
+# $Id: files.i386,v 1.32 1994/04/29 21:49:02 gclarkii Exp $
+#
+i386/i386/autoconf.c standard device-driver
+i386/i386/cons.c standard
+i386/i386/db_disasm.c optional ddb
+i386/i386/db_interface.c optional ddb
+i386/i386/db_trace.c optional ddb
+i386/i386/in_cksum.c optional inet
+i386/i386/math_emulate.c optional math_emulate
+i386/i386/mem.c standard
+i386/i386/microtime.s standard
+i386/i386/ns_cksum.c optional ns
+i386/i386/pmap.c standard
+i386/i386/procfs_machdep.c optional procfs
+i386/i386/sys_machdep.c standard
+i386/i386/trap.c standard
+i386/i386/vm_machdep.c standard
+i386/isa/aha1542.c optional aha device-driver
+i386/isa/aha1742.c optional ahb device-driver
+i386/isa/bt742a.c optional bt device-driver
+i386/isa/clock.c standard
+i386/isa/com.c optional com device-driver
+i386/isa/fd.c optional fd device-driver
+i386/isa/ft.c optional ft device-driver
+i386/isa/if_ed.c optional ed device-driver
+i386/isa/if_el.c optional el device-driver
+i386/isa/if_ep.c optional ep device-driver
+i386/isa/if_ie.c optional ie device-driver
+i386/isa/if_is.c optional is device-driver
+i386/isa/if_ix.c optional ix device-driver
+i386/isa/isa.c optional isa device-driver
+i386/isa/lpa.c optional lpa device-driver
+i386/isa/lpt.c optional lpt device-driver
+i386/isa/mcd.c optional mcd device-driver
+i386/isa/mse.c optional mse device-driver
+i386/isa/npx.c optional npx device-driver
+i386/isa/syscons.c optional sc device-driver
+i386/isa/pccons.c optional pc device-driver
+i386/isa/pcaudio.c optional pca device-driver
+i386/isa/psm.c optional psm device-driver
+i386/isa/sb.c optional sb device-driver
+i386/isa/scd.c optional scd device-driver
+i386/isa/sg.c optional sg device-driver
+i386/isa/sio.c optional sio device-driver
+i386/isa/sound/adlib_card.c optional snd device-driver
+i386/isa/sound/audio.c optional snd device-driver
+i386/isa/sound/dev_table.c optional snd device-driver
+i386/isa/sound/dmabuf.c optional snd device-driver
+i386/isa/sound/gus_card.c optional snd device-driver
+i386/isa/sound/gus_midi.c optional snd device-driver
+i386/isa/sound/gus_vol.c optional snd device-driver
+i386/isa/sound/gus_wave.c optional snd device-driver
+i386/isa/sound/ics2101.c optional snd device-driver
+i386/isa/sound/midi.c optional snd device-driver
+i386/isa/sound/midibuf.c optional snd device-driver
+i386/isa/sound/mpu401.c optional snd device-driver
+i386/isa/sound/opl3.c optional snd device-driver
+i386/isa/sound/pas2_card.c optional snd device-driver
+i386/isa/sound/pas2_midi.c optional snd device-driver
+i386/isa/sound/pas2_mixer.c optional snd device-driver
+i386/isa/sound/pas2_pcm.c optional snd device-driver
+i386/isa/sound/patmgr.c optional snd device-driver
+i386/isa/sound/pro_midi.c optional snd device-driver
+i386/isa/sound/sb16_dsp.c optional snd device-driver
+i386/isa/sound/sb16_midi.c optional snd device-driver
+i386/isa/sound/sb_card.c optional snd device-driver
+i386/isa/sound/sb_dsp.c optional snd device-driver
+i386/isa/sound/sb_midi.c optional snd device-driver
+i386/isa/sound/sb_mixer.c optional snd device-driver
+i386/isa/sound/sequencer.c optional snd device-driver
+i386/isa/sound/sound_switch.c optional snd device-driver
+i386/isa/sound/soundcard.c optional snd device-driver
+i386/isa/spkr.c optional speaker
+i386/isa/tw.c optional tw device-driver
+i386/isa/ultra14f.c optional uha device-driver
+i386/isa/wd.c optional wd device-driver
+i386/isa/wt.c optional wt device-driver
+i386/isa/pcvt/pcvt_drv.c optional vt device-driver
+i386/isa/pcvt/pcvt_sup.c optional vt device-driver
+i386/isa/pcvt/pcvt_out.c optional vt device-driver
+i386/isa/pcvt/pcvt_kbd.c optional vt device-driver
+i386/isa/pcvt/pcvt_vtf.c optional vt device-driver
+i386/isa/pcvt/pcvt_ext.c optional vt device-driver
+gnu/fpemul/div_small.s optional gpl_math_emulate
+gnu/fpemul/errors.c optional gpl_math_emulate
+gnu/fpemul/fpu_arith.c optional gpl_math_emulate
+gnu/fpemul/fpu_aux.c optional gpl_math_emulate
+gnu/fpemul/fpu_entry.c optional gpl_math_emulate
+gnu/fpemul/fpu_etc.c optional gpl_math_emulate
+gnu/fpemul/fpu_trig.c optional gpl_math_emulate
+gnu/fpemul/get_address.c optional gpl_math_emulate
+gnu/fpemul/load_store.c optional gpl_math_emulate
+gnu/fpemul/poly_2xm1.c optional gpl_math_emulate
+gnu/fpemul/poly_atan.c optional gpl_math_emulate
+gnu/fpemul/poly_div.s optional gpl_math_emulate
+gnu/fpemul/poly_l2.c optional gpl_math_emulate
+gnu/fpemul/poly_mul64.s optional gpl_math_emulate
+gnu/fpemul/poly_sin.c optional gpl_math_emulate
+gnu/fpemul/poly_tan.c optional gpl_math_emulate
+gnu/fpemul/polynomial.s optional gpl_math_emulate
+gnu/fpemul/reg_add_sub.c optional gpl_math_emulate
+gnu/fpemul/reg_compare.c optional gpl_math_emulate
+gnu/fpemul/reg_constant.c optional gpl_math_emulate
+gnu/fpemul/reg_div.s optional gpl_math_emulate
+gnu/fpemul/reg_ld_str.c optional gpl_math_emulate
+gnu/fpemul/reg_mul.c optional gpl_math_emulate
+gnu/fpemul/reg_norm.s optional gpl_math_emulate
+gnu/fpemul/reg_round.s optional gpl_math_emulate
+gnu/fpemul/reg_u_add.s optional gpl_math_emulate
+gnu/fpemul/reg_u_div.s optional gpl_math_emulate
+gnu/fpemul/reg_u_mul.s optional gpl_math_emulate
+gnu/fpemul/reg_u_sub.s optional gpl_math_emulate
+gnu/fpemul/wm_shrx.s optional gpl_math_emulate
+gnu/fpemul/wm_sqrt.s optional gpl_math_emulate
diff --git a/sys/conf/files.newconf b/sys/conf/files.newconf
new file mode 100644
index 0000000..7b0907a
--- /dev/null
+++ b/sys/conf/files.newconf
@@ -0,0 +1,274 @@
+# @(#)files.newconf 8.9 (Berkeley) 3/31/94
+
+# generic attributes
+define disk
+define tape
+define ifnet
+define tty
+
+# net device attributes - we have generic code for ether.
+# we should have imp but right now it is a pseudo-device.
+define ether
+# define imp
+pseudo-device imp
+
+# scsi driver and associated stuff
+define scsi { target = -1 }
+device tg at scsi { drive = -1 }
+file dev/scsi/scsi_subr.c scsi
+
+device sd at tg: disk
+file dev/scsi/sd.c sd needs-flag
+
+# device st at tg: tape -- not yet
+
+# legitimate pseudo-devices
+pseudo-device bpfilter
+pseudo-device cd: disk
+pseudo-device loop
+pseudo-device pty: tty
+pseudo-device sl
+pseudo-device vn: disk
+
+# kernel sources
+file isofs/cd9660/isofs_bmap.c isofs
+file isofs/cd9660/isofs_lookup.c isofs
+file isofs/cd9660/isofs_node.c isofs
+file isofs/cd9660/isofs_rrip.c isofs
+file isofs/cd9660/isofs_util.c isofs
+file isofs/cd9660/isofs_vfsops.c isofs
+file isofs/cd9660/isofs_vnops.c isofs
+file kern/init_main.c
+file kern/init_sysent.c
+file kern/kern_acct.c
+file kern/kern_clock.c
+file kern/kern_descrip.c
+file kern/kern_exec.c
+file kern/kern_exit.c
+file kern/kern_fork.c
+file kern/kern_ktrace.c ktrace
+file kern/kern_malloc.c
+file kern/kern_physio.c
+file kern/kern_proc.c
+file kern/kern_prot.c
+file kern/kern_resource.c
+file kern/kern_sig.c
+file kern/kern_subr.c
+file kern/kern_synch.c
+file kern/kern_sysctl.c
+file kern/kern_time.c
+file kern/kern_xxx.c
+file kern/subr_autoconf.c
+file kern/subr_log.c
+file kern/subr_prf.c
+file kern/subr_prof.c
+file kern/subr_rmap.c
+file kern/subr_xxx.c
+file kern/sys_generic.c
+file kern/sys_process.c
+file kern/sys_socket.c
+file kern/sysv_shm.c sysvshm
+file kern/tty.c
+file kern/tty_compat.c
+file kern/tty_conf.c
+file kern/tty_pty.c pty needs-count
+file kern/tty_subr.c
+file kern/tty_tb.c tb needs-flag
+file kern/tty_tty.c
+file kern/uipc_domain.c
+file kern/uipc_mbuf.c
+file kern/uipc_proto.c
+file kern/uipc_socket.c
+file kern/uipc_socket2.c
+file kern/uipc_syscalls.c
+file kern/uipc_usrreq.c
+file kern/vfs_bio.c
+file kern/vfs_cache.c
+file kern/vfs_cluster.c
+file kern/vfs_conf.c
+file kern/vfs_init.c
+file kern/vfs_lookup.c
+file kern/vfs_subr.c
+file kern/vfs_syscalls.c
+file kern/vfs_vnops.c
+file miscfs/deadfs/dead_vnops.c
+file miscfs/fdesc/fdesc_vfsops.c fdesc
+file miscfs/fdesc/fdesc_vnops.c fdesc
+file miscfs/fifofs/fifo_vnops.c fifo
+file miscfs/kernfs/kernfs_vfsops.c kernfs
+file miscfs/kernfs/kernfs_vnops.c kernfs
+file miscfs/nullfs/null_subr.c nullfs
+file miscfs/nullfs/null_vfsops.c nullfs
+file miscfs/nullfs/null_vnops.c nullfs
+file miscfs/portal/portal_vfsops.c portal
+file miscfs/portal/portal_vnops.c portal
+file miscfs/procfs/procfs_subr.c procfs
+file miscfs/procfs/procfs_vnops.c procfs
+file miscfs/procfs/procfs_vfsops.c procfs
+file miscfs/procfs/procfs_note.c procfs
+file miscfs/procfs/procfs_mem.c procfs
+file miscfs/procfs/procfs_ctl.c procfs
+file miscfs/procfs/procfs_status.c procfs
+file miscfs/procfs/procfs_regs.c procfs
+file miscfs/procfs/procfs_fpregs.c procfs
+file miscfs/specfs/spec_vnops.c
+file miscfs/umapfs/umap_subr.c umapfs
+file miscfs/umapfs/umap_vfsops.c umapfs
+file miscfs/umapfs/umap_vnops.c umapfs
+file miscfs/union/union_subr.c union
+file miscfs/union/union_vfsops.c union
+file miscfs/union/union_vnops.c union
+file net/bpf.c bpfilter needs-count
+file net/bpf_filter.c bpfilter needs-count
+file net/if.c
+file net/if_ethersubr.c ether needs-flag
+file net/if_loop.c loop needs-count
+file net/if_sl.c sl needs-count
+file net/radix.c
+file net/raw_cb.c
+file net/raw_usrreq.c
+file net/route.c
+file net/rtsock.c
+file net/slcompress.c sl
+file netccitt/ccitt_proto.c ccitt
+file netccitt/llc_input.c llc
+file netccitt/llc_output.c llc
+file netccitt/llc_subr.c llc
+file netccitt/llc_timer.c llc
+file netccitt/hd_debug.c hdlc
+file netccitt/hd_input.c hdlc
+file netccitt/hd_output.c hdlc
+file netccitt/hd_subr.c hdlc
+file netccitt/hd_timer.c hdlc
+file netccitt/if_x25subr.c ccitt
+file netccitt/pk_acct.c ccitt
+file netccitt/pk_debug.c ccitt
+file netccitt/pk_input.c ccitt
+file netccitt/pk_llcsubr.c llc hdlc
+file netccitt/pk_output.c ccitt
+file netccitt/pk_subr.c ccitt
+file netccitt/pk_timer.c ccitt
+file netccitt/pk_usrreq.c ccitt
+file netimp/if_imp.c imp needs-count
+file netimp/if_imphost.c imp needs-count
+file netimp/raw_imp.c imp
+file netinet/if_ether.c ether
+file netinet/igmp.c inet
+file netinet/in.c inet
+file netinet/in_pcb.c inet
+file netinet/in_proto.c inet
+file netinet/ip_icmp.c inet
+file netinet/ip_input.c inet
+file netinet/ip_mroute.c inet
+file netinet/ip_output.c inet
+file netinet/raw_ip.c inet
+file netinet/tcp_debug.c inet
+file netinet/tcp_input.c inet
+file netinet/tcp_output.c inet
+file netinet/tcp_subr.c inet
+file netinet/tcp_timer.c inet
+file netinet/tcp_usrreq.c inet
+file netinet/udp_usrreq.c inet
+file netiso/clnp_debug.c iso
+file netiso/clnp_er.c iso
+file netiso/clnp_frag.c iso
+file netiso/clnp_input.c iso
+file netiso/clnp_options.c iso
+file netiso/clnp_output.c iso
+file netiso/clnp_raw.c iso
+file netiso/clnp_subr.c iso
+file netiso/clnp_timer.c iso
+file netiso/cltp_usrreq.c iso
+file netiso/esis.c iso
+file netiso/if_eon.c eon
+file netiso/idrp_usrreq.c iso
+file netiso/iso.c iso
+file netiso/iso_chksum.c iso
+file netiso/iso_pcb.c iso
+file netiso/iso_proto.c iso
+file netiso/iso_snpac.c iso
+file netiso/tp_astring.c iso tpip
+file netiso/tp_cons.c iso
+file netiso/tp_driver.c iso tpip
+file netiso/tp_emit.c iso tpip
+file netiso/tp_inet.c iso tpip
+file netiso/tp_input.c iso tpip
+file netiso/tp_iso.c iso
+file netiso/tp_meas.c iso tpip
+file netiso/tp_output.c iso tpip
+file netiso/tp_pcb.c iso tpip
+file netiso/tp_subr.c iso tpip
+file netiso/tp_subr2.c iso tpip
+file netiso/tp_timer.c iso tpip
+file netiso/tp_trace.c iso tpip
+file netiso/tp_usrreq.c iso tpip
+file netiso/tuba_subr.c iso tuba
+file netiso/tuba_table.c iso tuba
+file netiso/tuba_usrreq.c iso tuba
+file netns/idp_usrreq.c ns
+file netns/ns.c ns
+file netns/ns_error.c ns
+file netns/ns_input.c ns
+file netns/ns_ip.c ns
+file netns/ns_output.c ns
+file netns/ns_pcb.c ns
+file netns/ns_proto.c ns
+file netns/spp_debug.c ns
+file netns/spp_usrreq.c ns
+file nfs/nfs_bio.c nfs
+file nfs/nfs_node.c nfs
+file nfs/nfs_nqlease.c nfs
+file nfs/nfs_serv.c nfs
+file nfs/nfs_socket.c nfs
+file nfs/nfs_srvcache.c nfs
+file nfs/nfs_subs.c nfs
+file nfs/nfs_syscalls.c nfs
+file nfs/nfs_vfsops.c nfs
+file nfs/nfs_vnops.c nfs
+file ufs/ffs/ffs_alloc.c ffs mfs
+file ufs/ffs/ffs_balloc.c ffs mfs
+file ufs/ffs/ffs_inode.c ffs mfs
+file ufs/ffs/ffs_subr.c ffs mfs
+file ufs/ffs/ffs_tables.c ffs mfs
+file ufs/ffs/ffs_vfsops.c ffs mfs
+file ufs/ffs/ffs_vnops.c ffs mfs
+file ufs/lfs/lfs_alloc.c lfs
+file ufs/lfs/lfs_bio.c lfs
+file ufs/lfs/lfs_balloc.c lfs
+file ufs/lfs/lfs_cksum.c lfs
+file ufs/lfs/lfs_debug.c lfs
+file ufs/lfs/lfs_inode.c lfs
+file ufs/lfs/lfs_segment.c lfs
+file ufs/lfs/lfs_subr.c lfs
+file ufs/lfs/lfs_syscalls.c lfs
+file ufs/lfs/lfs_vfsops.c lfs
+file ufs/lfs/lfs_vnops.c lfs
+file ufs/mfs/mfs_vfsops.c mfs
+file ufs/mfs/mfs_vnops.c mfs
+file ufs/ufs/ufs_bmap.c ffs lfs mfs
+file ufs/ufs/ufs_disksubr.c ffs lfs mfs
+file ufs/ufs/ufs_ihash.c ffs lfs mfs
+file ufs/ufs/ufs_inode.c ffs lfs mfs
+file ufs/ufs/ufs_lockf.c ffs lfs mfs
+file ufs/ufs/ufs_lookup.c ffs lfs mfs
+file ufs/ufs/ufs_quota.c ffs lfs mfs
+file ufs/ufs/ufs_vfsops.c ffs lfs mfs
+file ufs/ufs/ufs_vnops.c ffs lfs mfs
+file vm/device_pager.c devpager
+file vm/kern_lock.c
+file vm/swap_pager.c swappager
+file vm/vm_fault.c
+file vm/vm_glue.c
+file vm/vm_init.c
+file vm/vm_kern.c
+file vm/vm_map.c
+file vm/vm_meter.c
+file vm/vm_mmap.c
+file vm/vm_object.c
+file vm/vm_page.c
+file vm/vm_pageout.c
+file vm/vm_pager.c
+file vm/vm_swap.c
+file vm/vm_unix.c
+file vm/vm_user.c
+file vm/vnode_pager.c vnodepager
diff --git a/sys/conf/newvers.sh b/sys/conf/newvers.sh
new file mode 100644
index 0000000..03fdc25
--- /dev/null
+++ b/sys/conf/newvers.sh
@@ -0,0 +1,48 @@
+#!/bin/sh -
+#
+# Copyright (c) 1984, 1986, 1990, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)newvers.sh 8.1 (Berkeley) 4/20/94
+
+if [ ! -r version ]
+then
+ echo 0 > version
+fi
+
+touch version
+v=`cat version` u=${USER-root} d=`pwd` h=`hostname` t=`date`
+echo "char ostype[] = \"FreeBSD\";" > vers.c
+echo "char osrelease[] = \"2.0.0 (Development)\";" >> vers.c
+echo "char sccs[4] = { '@', '(', '#', ')' };" >>vers.c
+echo "char version[] = \"FreeBSD 2.0.0 (Development) #${v}: ${t}\\n ${u}@${h}:${d}\\n\";" >>vers.c
+
+echo `expr ${v} + 1` > version
diff --git a/sys/conf/nfsswapvmunix.c b/sys/conf/nfsswapvmunix.c
new file mode 100644
index 0000000..f9812eb
--- /dev/null
+++ b/sys/conf/nfsswapvmunix.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsswapvmunix.c 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Sample NFS swapvmunix configuration file.
+ * This should be filled in by the bootstrap program.
+ * See /sys/nfs/nfsdiskless.h for details of the fields.
+ */
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/socket.h>
+#include <sys/mount.h>
+
+#include <net/if.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfsdiskless.h>
+
+extern int nfs_mountroot();
+int (*mountroot)() = nfs_mountroot;
+
+dev_t rootdev = NODEV;
+dev_t argdev = NODEV;
+dev_t dumpdev = NODEV;
+
+struct swdevt swdevt[] = {
+ { NODEV, 0, 5000 }, /* happy:/u/swap.dopey */
+ { 0, 0, 0 }
+};
+struct nfs_diskless nfs_diskless = {
+ { { 'q', 'e', '0', '\0' },
+ { 0x10, 0x2, { 0x0, 0x0, 0x83, 0x68, 0x30, 0x2, } },
+ { 0x10, 0x2, { 0x0, 0x0, 0x83, 0x68, 0x30, 0xff, } },
+ { 0x10, 0x0, { 0x0, 0x0, 0xff, 0xff, 0xff, 0x0, } },
+ },
+ { 0x10, 0x2, { 0x0, 0x0, 0x83, 0x68, 0x30, 0x12, } },
+ {
+ (struct sockaddr *)0, SOCK_DGRAM, 0, (nfsv2fh_t *)0,
+ 0, 8192, 8192, 10, 100, (char *)0,
+ },
+ {
+ 0xf,
+ 0x9,
+ 0x0,
+ 0x0,
+ 0x1,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0xc,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x6,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x27,
+ 0x18,
+ 0x79,
+ 0x27,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ },
+ { 0x10, 0x2, { 0x8, 0x1, 0x83, 0x68, 0x30, 0x5, } },
+ "happy",
+ {
+ (struct sockaddr *)0, SOCK_DGRAM, 0, (nfsv2fh_t *)0,
+ 0, 8192, 8192, 10, 100, (char *)0,
+ },
+ {
+ 0x0,
+ 0x9,
+ 0x0,
+ 0x0,
+ 0x1,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0xc,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x2,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0xd0,
+ 0x48,
+ 0x42,
+ 0x25,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ },
+ { 0x10, 0x2, { 0x8, 0x1, 0x83, 0x68, 0x30, 0x5, } },
+ "happy",
+};
diff --git a/sys/conf/param.c b/sys/conf/param.c
new file mode 100644
index 0000000..c871594
--- /dev/null
+++ b/sys/conf/param.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 1980, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)param.c 8.2 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/callout.h>
+#include <sys/clist.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+
+#include <ufs/ufs/quota.h>
+
+#ifdef SYSVSHM
+#include <machine/vmparam.h>
+#include <sys/shm.h>
+#endif
+
+/*
+ * System parameter formulae.
+ *
+ * This file is copied into each directory where we compile
+ * the kernel; it should be modified there to suit local taste
+ * if necessary.
+ *
+ * Compiled with -DHZ=xx -DTIMEZONE=x -DDST=x -DMAXUSERS=xx
+ */
+
+#ifndef HZ
+#define HZ 100
+#endif
+int hz = HZ;
+int tick = 1000000 / HZ;
+int tickadj = 30000 / (60 * HZ); /* can adjust 30ms in 60s */
+struct timezone tz = { TIMEZONE, DST };
+#define NPROC (20 + 16 * MAXUSERS)
+int maxproc = NPROC;
+#define NTEXT NPROC
+int vm_cache_max = NTEXT/2 + 16;
+#define NVNODE (NPROC + NTEXT + 100)
+int desiredvnodes = NVNODE;
+int maxfiles = 3 * (NPROC + MAXUSERS) + 80;
+int ncallout = 16 + NPROC;
+int nclist = 60 + 12 * MAXUSERS;
+int nmbclusters = NMBCLUSTERS;
+int fscale = FSCALE; /* kernel uses `FSCALE', user uses `fscale' */
+
+/*
+ * Values in support of System V compatible shared memory. XXX
+ */
+#ifdef SYSVSHM
+#define SHMMAX (SHMMAXPGS*NBPG)
+#define SHMMIN 1
+#define SHMMNI 32 /* <= SHMMMNI in shm.h */
+#define SHMSEG 8
+#define SHMALL (SHMMAXPGS/CLSIZE)
+
+struct shminfo shminfo = {
+ SHMMAX,
+ SHMMIN,
+ SHMMNI,
+ SHMSEG,
+ SHMALL
+};
+#endif
+
+/*
+ * These are initialized at bootstrap time
+ * to values dependent on memory size
+ */
+int nbuf, nswbuf;
+
+/*
+ * These have to be allocated somewhere; allocating
+ * them here forces loader errors if this file is omitted
+ * (if they've been externed everywhere else; hah!).
+ */
+struct callout *callout;
+struct cblock *cfree;
+struct buf *buf, *swbuf;
+char *buffers;
+
+/*
+ * Proc/pgrp hashing.
+ * Here so that hash table sizes can depend on MAXUSERS/NPROC.
+ * Hash size must be a power of two.
+ * NOW omission of this file will cause loader errors!
+ */
+
+#if NPROC > 1024
+#define PIDHSZ 512
+#else
+#if NPROC > 512
+#define PIDHSZ 256
+#else
+#if NPROC > 256
+#define PIDHSZ 128
+#else
+#define PIDHSZ 64
+#endif
+#endif
+#endif
+
+struct proc *pidhash[PIDHSZ];
+struct pgrp *pgrphash[PIDHSZ];
+int pidhashmask = PIDHSZ - 1;
diff --git a/sys/conf/systags.sh b/sys/conf/systags.sh
new file mode 100644
index 0000000..90714d7
--- /dev/null
+++ b/sys/conf/systags.sh
@@ -0,0 +1,72 @@
+#! /bin/sh
+#
+# Copyright (c) 1992, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)systags.sh 8.1 (Berkeley) 6/10/93
+#
+# systags.sh - construct a system tags file using dependence relations
+# in a .depend file
+#
+# First written May 16, 1992 by Van Jacobson, Lawrence Berkeley Laboratory.
+#
+# from: $Header: systags.sh,v 1.7 92/07/12 08:18:21 torek Exp $
+
+rm -f tags tags.tmp tags.cfiles tags.sfiles tags.hfiles
+MACHINE=`machine`
+sed -e "s,\./machine/,../../$MACHINE/include/,g" \
+ -e 's,[a-z][^/ ]*/\.\./,,g' .depend | awk '{
+ for (i = 1; i <= NF; ++i) {
+ t = substr($i, length($i) - 1)
+ if (t == ".c")
+ cfiles[$i] = 1;
+ else if (t == ".h")
+ hfiles[$i] = 1;
+ else if (t == ".s")
+ sfiles[$i] = 1;
+ }
+ };
+ END {
+ for (i in cfiles)
+ print i > "tags.cfiles";
+ for (i in sfiles)
+ print i > "tags.sfiles";
+ for (i in hfiles)
+ print i > "tags.hfiles";
+ }'
+
+ctags -t -d -w `cat tags.cfiles tags.hfiles tags.sfiles`
+egrep -o "^ENTRY\(.*\)|^ALTENTRY\(.*\)" `cat tags.sfiles` | \
+ sed "s;\([^:]*\):\([^(]*\)(\([^, )]*\)\(.*\);\3 \1 /^\2(\3\4$/;" >> tags
+
+mv tags tags.tmp
+sort -u tags.tmp > tags
+rm tags.tmp tags.cfiles tags.sfiles tags.hfiles
diff --git a/sys/ddb/db_access.c b/sys/ddb/db_access.c
new file mode 100644
index 0000000..9c8a00b
--- /dev/null
+++ b/sys/ddb/db_access.c
@@ -0,0 +1,103 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_access.c,v 1.3 1993/11/25 01:30:01 wollman Exp $
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+
+/*
+ * Access unaligned data items on aligned (longword)
+ * boundaries.
+ */
+
+extern void db_read_bytes(); /* machine-dependent */
+extern void db_write_bytes(); /* machine-dependent */
+
+unsigned db_extend[] = { /* table for sign-extending */
+ 0,
+ 0xFFFFFF80U,
+ 0xFFFF8000U,
+ 0xFF800000U
+};
+
+db_expr_t
+db_get_value(addr, size, is_signed)
+ db_addr_t addr;
+ register int size;
+ boolean_t is_signed;
+{
+ char data[sizeof(int)];
+ register db_expr_t value;
+ register int i;
+
+ db_read_bytes(addr, size, data);
+
+ value = 0;
+#if BYTE_MSF
+ for (i = 0; i < size; i++)
+#else /* BYTE_LSF */
+ for (i = size - 1; i >= 0; i--)
+#endif
+ {
+ value = (value << 8) + (data[i] & 0xFF);
+ }
+
+ if (size < 4) {
+ if (is_signed && (value & db_extend[size]) != 0)
+ value |= db_extend[size];
+ }
+ return (value);
+}
+
+void
+db_put_value(addr, size, value)
+ db_addr_t addr;
+ register int size;
+ register db_expr_t value;
+{
+ char data[sizeof(int)];
+ register int i;
+
+#if BYTE_MSF
+ for (i = size - 1; i >= 0; i--)
+#else /* BYTE_LSF */
+ for (i = 0; i < size; i++)
+#endif
+ {
+ data[i] = value & 0xFF;
+ value >>= 8;
+ }
+
+ db_write_bytes(addr, size, data);
+}
+
diff --git a/sys/ddb/db_access.h b/sys/ddb/db_access.h
new file mode 100644
index 0000000..a62edba
--- /dev/null
+++ b/sys/ddb/db_access.h
@@ -0,0 +1,47 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_access.h,v 1.2 1993/10/16 16:47:05 rgrimes Exp $
+ */
+
+#ifndef _DDB_DB_ACCESS_H_
+#define _DDB_DB_ACCESS_H_ 1
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+/*
+ * Data access functions for debugger.
+ */
+#include <machine/db_machdep.h> /* expression types */
+
+extern db_expr_t db_get_value(/* db_addr_t addr,
+ int size,
+ boolean_t is_signed */);
+extern void db_put_value(/* db_addr_t addr,
+ int size,
+ db_expr_t value */);
+#endif /* _DDB_DB_ACCESS_H_ */
diff --git a/sys/ddb/db_aout.c b/sys/ddb/db_aout.c
new file mode 100644
index 0000000..e256f21
--- /dev/null
+++ b/sys/ddb/db_aout.c
@@ -0,0 +1,407 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_aout.c,v 1.5 1994/01/03 07:54:08 davidg Exp $
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+/*
+ * Symbol table routines for a.out format files.
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+#include <ddb/db_sym.h>
+
+#ifndef DB_NO_AOUT
+
+#define _AOUT_INCLUDE_
+#include "nlist.h"
+#include "stab.h"
+
+/*
+ * An a.out symbol table as loaded into the kernel debugger:
+ *
+ * symtab -> size of symbol entries, in bytes
+ * sp -> first symbol entry
+ * ...
+ * ep -> last symbol entry + 1
+ * strtab == start of string table
+ * size of string table in bytes,
+ * including this word
+ * -> strings
+ */
+
+/*
+ * Find pointers to the start and end of the symbol entries,
+ * given a pointer to the start of the symbol table.
+ */
+#define db_get_aout_symtab(symtab, sp, ep) \
+ (sp = (struct nlist *)((symtab) + 1), \
+ ep = (struct nlist *)((char *)sp + *(symtab)))
+
+#ifndef SYMTAB_SPACE
+#define SYMTAB_SPACE 73000
+#endif /*SYMTAB_SPACE*/
+
+int db_symtabsize = SYMTAB_SPACE;
+char db_symtab[SYMTAB_SPACE] = { 1 };
+
+void
+X_db_sym_init(symtab, esymtab, name)
+ int * symtab; /* pointer to start of symbol table */
+ char * esymtab; /* pointer to end of string table,
+ for checking - rounded up to integer
+ boundary */
+ char * name;
+{
+ register struct nlist *sym_start, *sym_end;
+ register struct nlist *sp;
+ register char * strtab;
+ register int strlen;
+
+ if (*symtab < 4) {
+ printf ("DDB: no symbols\n");
+ return;
+ }
+
+ db_get_aout_symtab(symtab, sym_start, sym_end);
+
+ strtab = (char *)sym_end;
+ strlen = *(int *)strtab;
+
+#if 0
+ if (strtab + ((strlen + sizeof(int) - 1) & ~(sizeof(int)-1))
+ != esymtab)
+ {
+ db_printf("[ %s symbol table not valid ]\n", name);
+ return;
+ }
+
+ db_printf("[ preserving %#x bytes of %s symbol table ]\n",
+ esymtab - (char *)symtab, name);
+#endif
+
+ for (sp = sym_start; sp < sym_end; sp++) {
+ register int strx;
+ strx = sp->n_un.n_strx;
+ if (strx != 0) {
+ if (strx > strlen) {
+ db_printf("Bad string table index (%#x)\n", strx);
+ sp->n_un.n_name = 0;
+ continue;
+ }
+ sp->n_un.n_name = strtab + strx;
+ }
+ }
+
+ db_add_symbol_table(sym_start, sym_end, name, (char *)symtab);
+}
+
+db_sym_t
+X_db_lookup(stab, symstr)
+ db_symtab_t *stab;
+ char * symstr;
+{
+ register struct nlist *sp, *ep;
+
+ sp = (struct nlist *)stab->start;
+ ep = (struct nlist *)stab->end;
+
+ for (; sp < ep; sp++) {
+ if (sp->n_un.n_name == 0)
+ continue;
+ if ((sp->n_type & N_STAB) == 0 &&
+ sp->n_un.n_name != 0 &&
+ db_eqname(sp->n_un.n_name, symstr, '_'))
+ {
+ return ((db_sym_t)sp);
+ }
+ }
+ return ((db_sym_t)0);
+}
+
+db_sym_t
+X_db_search_symbol(symtab, off, strategy, diffp)
+ db_symtab_t * symtab;
+ register
+ db_addr_t off;
+ db_strategy_t strategy;
+ db_expr_t *diffp; /* in/out */
+{
+ register unsigned int diff = *diffp;
+ register struct nlist *symp = 0;
+ register struct nlist *sp, *ep;
+
+ sp = (struct nlist *)symtab->start;
+ ep = (struct nlist *)symtab->end;
+
+ for (; sp < ep; sp++) {
+ if (sp->n_un.n_name == 0)
+ continue;
+ if ((sp->n_type & N_STAB) != 0 || (sp->n_type & N_TYPE) == N_FN)
+ continue;
+ if (off >= sp->n_value) {
+ if (off - sp->n_value < diff) {
+ diff = off - sp->n_value;
+ symp = sp;
+ if (diff == 0 &&
+ (strategy == DB_STGY_PROC &&
+ sp->n_type == (N_TEXT|N_EXT) ||
+ strategy == DB_STGY_ANY &&
+ (sp->n_type & N_EXT)))
+ break;
+ }
+ else if (off - sp->n_value == diff) {
+ if (symp == 0)
+ symp = sp;
+ else if ((symp->n_type & N_EXT) == 0 &&
+ (sp->n_type & N_EXT) != 0)
+ symp = sp; /* pick the external symbol */
+ }
+ }
+ }
+ if (symp == 0) {
+ *diffp = off;
+ }
+ else {
+ *diffp = diff;
+ }
+ return ((db_sym_t)symp);
+}
+
+/*
+ * Return the name and value for a symbol.
+ */
+void
+X_db_symbol_values(sym, namep, valuep)
+ db_sym_t sym;
+ char **namep;
+ db_expr_t *valuep;
+{
+ register struct nlist *sp;
+
+ sp = (struct nlist *)sym;
+ if (namep)
+ *namep = sp->n_un.n_name;
+ if (valuep)
+ *valuep = sp->n_value;
+}
+
+
+boolean_t
+X_db_line_at_pc(symtab, cursym, filename, linenum, off)
+ db_symtab_t * symtab;
+ db_sym_t cursym;
+ char **filename;
+ int *linenum;
+ db_expr_t off;
+{
+ register struct nlist *sp, *ep;
+ register struct nlist *sym = (struct nlist *)cursym;
+ unsigned long sodiff = -1UL, lndiff = -1UL, ln = 0;
+ char *fname = NULL;
+
+ sp = (struct nlist *)symtab->start;
+ ep = (struct nlist *)symtab->end;
+
+/* XXX - gcc specific */
+#define NEWSRC(str) ((str) != NULL && \
+ (str)[0] == 'g' && strcmp((str), "gcc_compiled.") == 0)
+
+ for (; sp < ep; sp++) {
+
+ /*
+ * Prevent bogus linenumbers in case module not compiled
+ * with debugging options
+ */
+#if 0
+ if (sp->n_value <= off && (off - sp->n_value) <= sodiff &&
+ NEWSRC(sp->n_un.n_name)) {
+#endif
+ if ((sp->n_type & N_TYPE) == N_FN || NEWSRC(sp->n_un.n_name)) {
+ sodiff = lndiff = -1UL;
+ ln = 0;
+ fname = NULL;
+ }
+
+ if (sp->n_type == N_SO) {
+ if (sp->n_value <= off && (off - sp->n_value) < sodiff) {
+ sodiff = off - sp->n_value;
+ fname = sp->n_un.n_name;
+ }
+ continue;
+ }
+
+ if (sp->n_type != N_SLINE)
+ continue;
+
+ if (sp->n_value > off)
+ break;
+
+ if (off - sp->n_value < lndiff) {
+ lndiff = off - sp->n_value;
+ ln = sp->n_desc;
+ }
+ }
+
+ if (fname != NULL && ln != 0) {
+ *filename = fname;
+ *linenum = ln;
+ return TRUE;
+ }
+
+ return (FALSE);
+}
+
+boolean_t
+X_db_sym_numargs(symtab, cursym, nargp, argnamep)
+ db_symtab_t * symtab;
+ db_sym_t cursym;
+ int *nargp;
+ char **argnamep;
+{
+ register struct nlist *sp, *ep;
+ u_long addr;
+ int maxnarg = *nargp, nargs = 0;
+
+ if (cursym == NULL)
+ return FALSE;
+
+ addr = ((struct nlist *)cursym)->n_value;
+ sp = (struct nlist *)symtab->start;
+ ep = (struct nlist *)symtab->end;
+
+ for (; sp < ep; sp++) {
+ if (sp->n_type == N_FUN && sp->n_value == addr) {
+ while (++sp < ep && sp->n_type == N_PSYM) {
+ if (nargs >= maxnarg)
+ break;
+ nargs++;
+ *argnamep++ = sp->n_un.n_name?sp->n_un.n_name:"???";
+ {
+ /* XXX - remove trailers */
+ char *cp = *(argnamep-1);
+ while (*cp != '\0' && *cp != ':') cp++;
+ if (*cp == ':') *cp = '\0';
+ }
+ }
+ *nargp = nargs;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * Initialization routine for a.out files.
+ */
+void
+kdb_init(void)
+{
+#if 0
+ extern char *esym;
+ extern int end;
+
+ if (esym > (char *)&end) {
+ X_db_sym_init((int *)&end, esym, "386bsd");
+ }
+#endif
+
+ X_db_sym_init (db_symtab, 0, "386bsd");
+}
+
+#if 0
+/*
+ * Read symbol table from file.
+ * (should be somewhere else)
+ */
+#include <boot_ufs/file_io.h>
+#include <vm/vm_kern.h>
+
+read_symtab_from_file(fp, symtab_name)
+ struct file *fp;
+ char * symtab_name;
+{
+ vm_size_t resid;
+ kern_return_t result;
+ vm_offset_t symoff;
+ vm_size_t symsize;
+ vm_offset_t stroff;
+ vm_size_t strsize;
+ vm_size_t table_size;
+ vm_offset_t symtab;
+
+ if (!get_symtab(fp, &symoff, &symsize)) {
+ boot_printf("[ error %d reading %s file header ]\n",
+ result, symtab_name);
+ return;
+ }
+
+ stroff = symoff + symsize;
+ result = read_file(fp, (vm_offset_t)stroff,
+ (vm_offset_t)&strsize, sizeof(strsize), &resid);
+ if (result || resid) {
+ boot_printf("[ no valid symbol table present for %s ]\n",
+ symtab_name);
+ return;
+ }
+
+ table_size = sizeof(int) + symsize + strsize;
+ table_size = (table_size + sizeof(int)-1) & ~(sizeof(int)-1);
+
+ symtab = kmem_alloc_wired(kernel_map, table_size);
+
+ *(int *)symtab = symsize;
+
+ result = read_file(fp, symoff,
+ symtab + sizeof(int), symsize, &resid);
+ if (result || resid) {
+ boot_printf("[ error %d reading %s symbol table ]\n",
+ result, symtab_name);
+ return;
+ }
+
+ result = read_file(fp, stroff,
+ symtab + sizeof(int) + symsize, strsize, &resid);
+ if (result || resid) {
+ boot_printf("[ error %d reading %s string table ]\n",
+ result, symtab_name);
+ return;
+ }
+
+ X_db_sym_init((int *)symtab,
+ (char *)(symtab + table_size),
+ symtab_name);
+
+}
+#endif
+
+#endif /* DB_NO_AOUT */
diff --git a/sys/ddb/db_break.c b/sys/ddb/db_break.c
new file mode 100644
index 0000000..41761e8
--- /dev/null
+++ b/sys/ddb/db_break.c
@@ -0,0 +1,353 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_break.c,v 1.2 1993/10/16 16:47:07 rgrimes Exp $
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+/*
+ * Breakpoints.
+ */
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+
+#include <ddb/db_lex.h>
+#include <ddb/db_break.h>
+#include <ddb/db_access.h>
+#include <ddb/db_sym.h>
+#include <ddb/db_break.h>
+
+extern boolean_t db_map_equal();
+extern boolean_t db_map_current();
+extern vm_map_t db_map_addr();
+
+#define NBREAKPOINTS 100
+struct db_breakpoint db_break_table[NBREAKPOINTS];
+db_breakpoint_t db_next_free_breakpoint = &db_break_table[0];
+db_breakpoint_t db_free_breakpoints = 0;
+db_breakpoint_t db_breakpoint_list = 0;
+
+db_breakpoint_t
+db_breakpoint_alloc()
+{
+ register db_breakpoint_t bkpt;
+
+ if ((bkpt = db_free_breakpoints) != 0) {
+ db_free_breakpoints = bkpt->link;
+ return (bkpt);
+ }
+ if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) {
+ db_printf("All breakpoints used.\n");
+ return (0);
+ }
+ bkpt = db_next_free_breakpoint;
+ db_next_free_breakpoint++;
+
+ return (bkpt);
+}
+
+void
+db_breakpoint_free(bkpt)
+ register db_breakpoint_t bkpt;
+{
+ bkpt->link = db_free_breakpoints;
+ db_free_breakpoints = bkpt;
+}
+
+void
+db_set_breakpoint(map, addr, count)
+ vm_map_t map;
+ db_addr_t addr;
+ int count;
+{
+ register db_breakpoint_t bkpt;
+
+ if (db_find_breakpoint(map, addr)) {
+ db_printf("Already set.\n");
+ return;
+ }
+
+ bkpt = db_breakpoint_alloc();
+ if (bkpt == 0) {
+ db_printf("Too many breakpoints.\n");
+ return;
+ }
+
+ bkpt->map = map;
+ bkpt->address = addr;
+ bkpt->flags = 0;
+ bkpt->init_count = count;
+ bkpt->count = count;
+
+ bkpt->link = db_breakpoint_list;
+ db_breakpoint_list = bkpt;
+}
+
+void
+db_delete_breakpoint(map, addr)
+ vm_map_t map;
+ db_addr_t addr;
+{
+ register db_breakpoint_t bkpt;
+ register db_breakpoint_t *prev;
+
+ for (prev = &db_breakpoint_list;
+ (bkpt = *prev) != 0;
+ prev = &bkpt->link) {
+ if (db_map_equal(bkpt->map, map) &&
+ (bkpt->address == addr)) {
+ *prev = bkpt->link;
+ break;
+ }
+ }
+ if (bkpt == 0) {
+ db_printf("Not set.\n");
+ return;
+ }
+
+ db_breakpoint_free(bkpt);
+}
+
+db_breakpoint_t
+db_find_breakpoint(map, addr)
+ vm_map_t map;
+ db_addr_t addr;
+{
+ register db_breakpoint_t bkpt;
+
+ for (bkpt = db_breakpoint_list;
+ bkpt != 0;
+ bkpt = bkpt->link)
+ {
+ if (db_map_equal(bkpt->map, map) &&
+ (bkpt->address == addr))
+ return (bkpt);
+ }
+ return (0);
+}
+
+db_breakpoint_t
+db_find_breakpoint_here(addr)
+ db_addr_t addr;
+{
+ return db_find_breakpoint(db_map_addr(addr), addr);
+}
+
+boolean_t db_breakpoints_inserted = TRUE;
+
+void
+db_set_breakpoints()
+{
+ register db_breakpoint_t bkpt;
+
+ if (!db_breakpoints_inserted) {
+
+ for (bkpt = db_breakpoint_list;
+ bkpt != 0;
+ bkpt = bkpt->link)
+ if (db_map_current(bkpt->map)) {
+ bkpt->bkpt_inst = db_get_value(bkpt->address,
+ BKPT_SIZE,
+ FALSE);
+ db_put_value(bkpt->address,
+ BKPT_SIZE,
+ BKPT_SET(bkpt->bkpt_inst));
+ }
+ db_breakpoints_inserted = TRUE;
+ }
+}
+
+void
+db_clear_breakpoints()
+{
+ register db_breakpoint_t bkpt;
+
+ if (db_breakpoints_inserted) {
+
+ for (bkpt = db_breakpoint_list;
+ bkpt != 0;
+ bkpt = bkpt->link)
+ if (db_map_current(bkpt->map)) {
+ db_put_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst);
+ }
+ db_breakpoints_inserted = FALSE;
+ }
+}
+
+/*
+ * Set a temporary breakpoint.
+ * The instruction is changed immediately,
+ * so the breakpoint does not have to be on the breakpoint list.
+ */
+db_breakpoint_t
+db_set_temp_breakpoint(addr)
+ db_addr_t addr;
+{
+ register db_breakpoint_t bkpt;
+
+ bkpt = db_breakpoint_alloc();
+ if (bkpt == 0) {
+ db_printf("Too many breakpoints.\n");
+ return 0;
+ }
+
+ bkpt->map = NULL;
+ bkpt->address = addr;
+ bkpt->flags = BKPT_TEMP;
+ bkpt->init_count = 1;
+ bkpt->count = 1;
+
+ bkpt->bkpt_inst = db_get_value(bkpt->address, BKPT_SIZE, FALSE);
+ db_put_value(bkpt->address, BKPT_SIZE, BKPT_SET(bkpt->bkpt_inst));
+ return bkpt;
+}
+
+void
+db_delete_temp_breakpoint(bkpt)
+ db_breakpoint_t bkpt;
+{
+ db_put_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst);
+ db_breakpoint_free(bkpt);
+}
+
+/*
+ * List breakpoints.
+ */
+void
+db_list_breakpoints()
+{
+ register db_breakpoint_t bkpt;
+
+ if (db_breakpoint_list == 0) {
+ db_printf("No breakpoints set\n");
+ return;
+ }
+
+ db_printf(" Map Count Address\n");
+ for (bkpt = db_breakpoint_list;
+ bkpt != 0;
+ bkpt = bkpt->link)
+ {
+ db_printf("%s%8x %5d ",
+ db_map_current(bkpt->map) ? "*" : " ",
+ bkpt->map, bkpt->init_count);
+ db_printsym(bkpt->address, DB_STGY_PROC);
+ db_printf("\n");
+ }
+}
+
+/* Delete breakpoint */
+/*ARGSUSED*/
+void
+db_delete_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ int have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr);
+}
+
+/* Set breakpoint with skip count */
+/*ARGSUSED*/
+void
+db_breakpoint_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ int have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ if (count == -1)
+ count = 1;
+
+ db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count);
+}
+
+/* list breakpoints */
+void
+db_listbreak_cmd(db_expr_t dummy1, int dummy2, db_expr_t dummy3, char *dummy4)
+{
+ db_list_breakpoints();
+}
+
+#include <vm/vm_kern.h>
+
+/*
+ * We want ddb to be usable before most of the kernel has been
+ * initialized. In particular, current_thread() or kernel_map
+ * (or both) may be null.
+ */
+
+boolean_t
+db_map_equal(map1, map2)
+ vm_map_t map1, map2;
+{
+ return ((map1 == map2) ||
+ ((map1 == NULL) && (map2 == kernel_map)) ||
+ ((map1 == kernel_map) && (map2 == NULL)));
+}
+
+boolean_t
+db_map_current(map)
+ vm_map_t map;
+{
+#if 0
+ thread_t thread;
+
+ return ((map == NULL) ||
+ (map == kernel_map) ||
+ (((thread = current_thread()) != NULL) &&
+ (map == thread->task->map)));
+#else
+ return (1);
+#endif
+}
+
+vm_map_t
+db_map_addr(addr)
+ vm_offset_t addr;
+{
+#if 0
+ thread_t thread;
+
+ /*
+ * We want to return kernel_map for all
+ * non-user addresses, even when debugging
+ * kernel tasks with their own maps.
+ */
+
+ if ((VM_MIN_ADDRESS <= addr) &&
+ (addr < VM_MAX_ADDRESS) &&
+ ((thread = current_thread()) != NULL))
+ return thread->task->map;
+ else
+#endif
+ return kernel_map;
+}
diff --git a/sys/ddb/db_break.h b/sys/ddb/db_break.h
new file mode 100644
index 0000000..dc66dca
--- /dev/null
+++ b/sys/ddb/db_break.h
@@ -0,0 +1,64 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id$
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+#ifndef _DDB_DB_BREAK_H_
+#define _DDB_DB_BREAK_H_
+
+#include <vm/vm_map.h>
+#include <machine/db_machdep.h>
+
+/*
+ * Breakpoint.
+ */
+
+struct db_breakpoint {
+ vm_map_t map; /* in this map */
+ db_addr_t address; /* set here */
+ int init_count; /* number of times to skip bkpt */
+ int count; /* current count */
+ int flags; /* flags: */
+#define BKPT_SINGLE_STEP 0x2 /* to simulate single step */
+#define BKPT_TEMP 0x4 /* temporary */
+ int bkpt_inst; /* saved instruction at bkpt */
+ struct db_breakpoint *link; /* link in in-use or free chain */
+};
+typedef struct db_breakpoint *db_breakpoint_t;
+
+extern db_breakpoint_t db_find_breakpoint();
+extern db_breakpoint_t db_find_breakpoint_here();
+extern void db_set_breakpoints();
+extern void db_clear_breakpoints();
+
+extern db_breakpoint_t db_set_temp_breakpoint(/* db_addr_t addr */);
+extern void db_delete_temp_breakpoint(/* db_breakpoint_t bkpt */);
+
+#endif _DDB_DB_BREAK_H_
diff --git a/sys/ddb/db_command.c b/sys/ddb/db_command.c
new file mode 100644
index 0000000..45fd5c5
--- /dev/null
+++ b/sys/ddb/db_command.c
@@ -0,0 +1,485 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_command.c,v 1.3 1993/11/25 01:30:04 wollman Exp $
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+/*
+ * Command dispatcher.
+ */
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+
+#include <ddb/db_lex.h>
+#include <ddb/db_output.h>
+
+#include <setjmp.h>
+
+/*
+ * Exported global variables
+ */
+boolean_t db_cmd_loop_done;
+jmp_buf db_jmpbuf;
+db_addr_t db_dot;
+db_addr_t db_last_addr;
+db_addr_t db_prev;
+db_addr_t db_next;
+
+/*
+ * if 'ed' style: 'dot' is set at start of last item printed,
+ * and '+' points to next line.
+ * Otherwise: 'dot' points to next item, '..' points to last.
+ */
+boolean_t db_ed_style = TRUE;
+
+
+/*
+ * Utility routine - discard tokens through end-of-line.
+ */
+void
+db_skip_to_eol()
+{
+ int t;
+ do {
+ t = db_read_token();
+ } while (t != tEOL);
+}
+
+/*
+ * Command table
+ */
+struct command {
+ char * name; /* command name */
+ void (*fcn)(); /* function to call */
+ int flag; /* extra info: */
+#define CS_OWN 0x1 /* non-standard syntax */
+#define CS_MORE 0x2 /* standard syntax, but may have other
+ words at end */
+#define CS_SET_DOT 0x100 /* set dot after command */
+ struct command *more; /* another level of command */
+};
+
+/*
+ * Results of command search.
+ */
+#define CMD_UNIQUE 0
+#define CMD_FOUND 1
+#define CMD_NONE 2
+#define CMD_AMBIGUOUS 3
+#define CMD_HELP 4
+
+/*
+ * Search for command prefix.
+ */
+int
+db_cmd_search(name, table, cmdp)
+ char * name;
+ struct command *table;
+ struct command **cmdp; /* out */
+{
+ struct command *cmd;
+ int result = CMD_NONE;
+
+ for (cmd = table; cmd->name != 0; cmd++) {
+ register char *lp;
+ register char *rp;
+ register int c;
+
+ lp = name;
+ rp = cmd->name;
+ while ((c = *lp) == *rp) {
+ if (c == 0) {
+ /* complete match */
+ *cmdp = cmd;
+ return (CMD_UNIQUE);
+ }
+ lp++;
+ rp++;
+ }
+ if (c == 0) {
+ /* end of name, not end of command -
+ partial match */
+ if (result == CMD_FOUND) {
+ result = CMD_AMBIGUOUS;
+ /* but keep looking for a full match -
+ this lets us match single letters */
+ }
+ else {
+ *cmdp = cmd;
+ result = CMD_FOUND;
+ }
+ }
+ }
+ if (result == CMD_NONE) {
+ /* check for 'help' */
+ if (name[0] == 'h' && name[1] == 'e'
+ && name[2] == 'l' && name[3] == 'p')
+ result = CMD_HELP;
+ }
+ return (result);
+}
+
+void
+db_cmd_list(table)
+ struct command *table;
+{
+ register struct command *cmd;
+
+ for (cmd = table; cmd->name != 0; cmd++) {
+ db_printf("%-12s", cmd->name);
+ db_end_line();
+ }
+}
+
+void
+db_command(last_cmdp, cmd_table)
+ struct command **last_cmdp; /* IN_OUT */
+ struct command *cmd_table;
+{
+ struct command *cmd;
+ int t;
+ char modif[TOK_STRING_SIZE];
+ db_expr_t addr, count;
+ boolean_t have_addr = FALSE;
+ int result;
+
+ t = db_read_token();
+ if (t == tEOL) {
+ /* empty line repeats last command, at 'next' */
+ cmd = *last_cmdp;
+ addr = (db_expr_t)db_next;
+ have_addr = FALSE;
+ count = 1;
+ modif[0] = '\0';
+ }
+ else if (t == tEXCL) {
+ void db_fncall();
+ db_fncall();
+ return;
+ }
+ else if (t != tIDENT) {
+ db_printf("?\n");
+ db_flush_lex();
+ return;
+ }
+ else {
+ /*
+ * Search for command
+ */
+ while (cmd_table) {
+ result = db_cmd_search(db_tok_string,
+ cmd_table,
+ &cmd);
+ switch (result) {
+ case CMD_NONE:
+ db_printf("No such command\n");
+ db_flush_lex();
+ return;
+ case CMD_AMBIGUOUS:
+ db_printf("Ambiguous\n");
+ db_flush_lex();
+ return;
+ case CMD_HELP:
+ db_cmd_list(cmd_table);
+ db_flush_lex();
+ return;
+ default:
+ break;
+ }
+ if ((cmd_table = cmd->more) != 0) {
+ t = db_read_token();
+ if (t != tIDENT) {
+ db_cmd_list(cmd_table);
+ db_flush_lex();
+ return;
+ }
+ }
+ }
+
+ if ((cmd->flag & CS_OWN) == 0) {
+ /*
+ * Standard syntax:
+ * command [/modifier] [addr] [,count]
+ */
+ t = db_read_token();
+ if (t == tSLASH) {
+ t = db_read_token();
+ if (t != tIDENT) {
+ db_printf("Bad modifier\n");
+ db_flush_lex();
+ return;
+ }
+ db_strcpy(modif, db_tok_string);
+ }
+ else {
+ db_unread_token(t);
+ modif[0] = '\0';
+ }
+
+ if (db_expression(&addr)) {
+ db_dot = (db_addr_t) addr;
+ db_last_addr = db_dot;
+ have_addr = TRUE;
+ }
+ else {
+ addr = (db_expr_t) db_dot;
+ have_addr = FALSE;
+ }
+ t = db_read_token();
+ if (t == tCOMMA) {
+ if (!db_expression(&count)) {
+ db_printf("Count missing\n");
+ db_flush_lex();
+ return;
+ }
+ }
+ else {
+ db_unread_token(t);
+ count = -1;
+ }
+ if ((cmd->flag & CS_MORE) == 0) {
+ db_skip_to_eol();
+ }
+ }
+ }
+ *last_cmdp = cmd;
+ if (cmd != 0) {
+ /*
+ * Execute the command.
+ */
+ (*cmd->fcn)(addr, have_addr, count, modif);
+
+ if (cmd->flag & CS_SET_DOT) {
+ /*
+ * If command changes dot, set dot to
+ * previous address displayed (if 'ed' style).
+ */
+ if (db_ed_style) {
+ db_dot = db_prev;
+ }
+ else {
+ db_dot = db_next;
+ }
+ }
+ else {
+ /*
+ * If command does not change dot,
+ * set 'next' location to be the same.
+ */
+ db_next = db_dot;
+ }
+ }
+}
+
+/*
+ * 'show' commands
+ */
+extern void db_listbreak_cmd();
+extern void db_listwatch_cmd();
+extern void db_show_regs(), db_show_one_thread(), db_show_all_threads();
+extern void vm_map_print(), vm_object_print(), vm_page_print();
+/* extern void db_ps(); */
+extern void ipc_port_print();
+void db_show_help();
+
+struct command db_show_all_cmds[] = {
+#if 0
+ { "threads", db_show_all_threads, 0, 0 },
+ { "procs", db_ps, 0, 0 },
+#endif
+ { (char *)0 }
+};
+
+struct command db_show_cmds[] = {
+ { "all", 0, 0, db_show_all_cmds },
+ { "registers", db_show_regs, 0, 0 },
+ { "breaks", db_listbreak_cmd, 0, 0 },
+ { "watches", db_listwatch_cmd, 0, 0 },
+#if 0
+ { "thread", db_show_one_thread, 0, 0 },
+#endif
+ { "map", vm_map_print, 0, 0 },
+ { "object", vm_object_print, 0, 0 },
+#if 0
+ { "page", vm_page_print, 0, 0 },
+#endif
+#if 0
+ { "port", ipc_port_print, 0, 0 },
+#endif
+ { (char *)0, }
+};
+
+extern void db_print_cmd(), db_examine_cmd(), db_set_cmd();
+extern void db_search_cmd();
+extern void db_write_cmd();
+extern void db_delete_cmd(), db_breakpoint_cmd();
+extern void db_deletewatch_cmd(), db_watchpoint_cmd();
+extern void db_single_step_cmd(), db_trace_until_call_cmd(),
+ db_trace_until_matching_cmd(), db_continue_cmd();
+extern void db_stack_trace_cmd();
+void db_help_cmd();
+void db_fncall();
+
+struct command db_command_table[] = {
+ { "print", db_print_cmd, 0, 0 },
+ { "examine", db_examine_cmd, CS_SET_DOT, 0 },
+ { "x", db_examine_cmd, CS_SET_DOT, 0 },
+ { "search", db_search_cmd, CS_OWN|CS_SET_DOT, 0 },
+ { "set", db_set_cmd, CS_OWN, 0 },
+ { "write", db_write_cmd, CS_MORE|CS_SET_DOT, 0 },
+ { "w", db_write_cmd, CS_MORE|CS_SET_DOT, 0 },
+ { "delete", db_delete_cmd, 0, 0 },
+ { "d", db_delete_cmd, 0, 0 },
+ { "break", db_breakpoint_cmd, 0, 0 },
+ { "dwatch", db_deletewatch_cmd, 0, 0 },
+ { "watch", db_watchpoint_cmd, CS_MORE,0 },
+ { "step", db_single_step_cmd, 0, 0 },
+ { "s", db_single_step_cmd, 0, 0 },
+ { "continue", db_continue_cmd, 0, 0 },
+ { "c", db_continue_cmd, 0, 0 },
+ { "until", db_trace_until_call_cmd,0, 0 },
+ { "next", db_trace_until_matching_cmd,0, 0 },
+ { "match", db_trace_until_matching_cmd,0, 0 },
+ { "trace", db_stack_trace_cmd, 0, 0 },
+ { "call", db_fncall, CS_OWN, 0 },
+ { "show", 0, 0, db_show_cmds },
+#if 0
+ { "ps", db_ps, 0, 0 },
+#endif
+ { (char *)0, }
+};
+
+struct command *db_last_command = 0;
+
+void
+db_help_cmd()
+{
+ struct command *cmd = db_command_table;
+
+ while (cmd->name != 0) {
+ db_printf("%-12s", cmd->name);
+ db_end_line();
+ cmd++;
+ }
+}
+
+void
+db_command_loop()
+{
+ /*
+ * Initialize 'prev' and 'next' to dot.
+ */
+ db_prev = db_dot;
+ db_next = db_dot;
+
+ db_cmd_loop_done = 0;
+ while (!db_cmd_loop_done) {
+
+ (void) setjmp(db_jmpbuf);
+ if (db_print_position() != 0)
+ db_printf("\n");
+
+ db_printf("db> ");
+ (void) db_read_line();
+
+ db_command(&db_last_command, db_command_table);
+ }
+}
+
+void
+db_error(s)
+ char *s;
+{
+ if (s)
+ db_printf(s);
+ db_flush_lex();
+ longjmp(db_jmpbuf, 1);
+}
+
+
+/*
+ * Call random function:
+ * !expr(arg,arg,arg)
+ */
+void
+db_fncall()
+{
+ db_expr_t fn_addr;
+#define MAXARGS 11
+ db_expr_t args[MAXARGS];
+ int nargs = 0;
+ db_expr_t retval;
+ db_expr_t (*func)();
+ int t;
+
+ if (!db_expression(&fn_addr)) {
+ db_printf("Bad function\n");
+ db_flush_lex();
+ return;
+ }
+ func = (db_expr_t (*) ()) fn_addr;
+
+ t = db_read_token();
+ if (t == tLPAREN) {
+ if (db_expression(&args[0])) {
+ nargs++;
+ while ((t = db_read_token()) == tCOMMA) {
+ if (nargs == MAXARGS) {
+ db_printf("Too many arguments\n");
+ db_flush_lex();
+ return;
+ }
+ if (!db_expression(&args[nargs])) {
+ db_printf("Argument missing\n");
+ db_flush_lex();
+ return;
+ }
+ nargs++;
+ }
+ db_unread_token(t);
+ }
+ if (db_read_token() != tRPAREN) {
+ db_printf("?\n");
+ db_flush_lex();
+ return;
+ }
+ }
+ db_skip_to_eol();
+
+ while (nargs < MAXARGS) {
+ args[nargs++] = 0;
+ }
+
+ retval = (*func)(args[0], args[1], args[2], args[3], args[4],
+ args[5], args[6], args[7], args[8], args[9] );
+ db_printf("%#n\n", retval);
+}
diff --git a/sys/ddb/db_command.h b/sys/ddb/db_command.h
new file mode 100644
index 0000000..a883e63
--- /dev/null
+++ b/sys/ddb/db_command.h
@@ -0,0 +1,57 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_command.h,v 1.2 1993/10/16 16:47:12 rgrimes Exp $
+ */
+
+#ifndef _DDB_DB_COMMAND_H_
+#define _DDB_DB_COMMAND_H_ 1
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+/*
+ * Command loop declarations.
+ */
+
+#include "param.h"
+#include "proc.h"
+#include <machine/db_machdep.h>
+
+extern void db_command_loop();
+extern void db_skip_to_eol();
+
+extern void db_error(/* char * */); /* report error */
+
+extern db_addr_t db_dot; /* current location */
+extern db_addr_t db_last_addr; /* last explicit address typed */
+extern db_addr_t db_prev; /* last address examined
+ or written */
+extern db_addr_t db_next; /* next address to be examined
+ or written */
+
+
+#endif /* _DDB_DB_COMMAND_H_ */
diff --git a/sys/ddb/db_examine.c b/sys/ddb/db_examine.c
new file mode 100644
index 0000000..f4f7ba1
--- /dev/null
+++ b/sys/ddb/db_examine.c
@@ -0,0 +1,340 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_examine.c,v 1.3 1993/11/25 01:30:05 wollman Exp $
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+
+#include "ddb/ddb.h"
+
+#include "ddb/db_lex.h"
+#include "ddb/db_output.h"
+#include "ddb/db_command.h"
+#include "ddb/db_sym.h"
+#include "ddb/db_access.h"
+
+char db_examine_format[TOK_STRING_SIZE] = "x";
+
+extern db_addr_t db_disasm(/* db_addr_t, boolean_t */);
+ /* instruction disassembler */
+
+static void db_examine(db_addr_t, char *, int);
+static void db_search(db_addr_t, int, db_expr_t, db_expr_t, u_int);
+
+/*
+ * Examine (print) data.
+ */
+/*ARGSUSED*/
+void
+db_examine_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ int have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ if (modif[0] != '\0')
+ db_strcpy(db_examine_format, modif);
+
+ if (count == -1)
+ count = 1;
+
+ db_examine((db_addr_t) addr, db_examine_format, count);
+}
+
+static void
+db_examine(addr, fmt, count)
+ register
+ db_addr_t addr;
+ char * fmt; /* format string */
+ int count; /* repeat count */
+{
+ int c;
+ db_expr_t value;
+ int size;
+ int width;
+ char * fp;
+
+ while (--count >= 0) {
+ fp = fmt;
+ size = 4;
+ width = 16;
+ while ((c = *fp++) != 0) {
+ switch (c) {
+ case 'b':
+ size = 1;
+ width = 4;
+ break;
+ case 'h':
+ size = 2;
+ width = 8;
+ break;
+ case 'l':
+ size = 4;
+ width = 16;
+ break;
+ case 'a': /* address */
+ /* always forces a new line */
+ if (db_print_position() != 0)
+ db_printf("\n");
+ db_prev = addr;
+ db_printsym(addr, DB_STGY_ANY);
+ db_printf(":\t");
+ break;
+ default:
+ if (db_print_position() == 0) {
+ /* If we hit a new symbol, print it */
+ char * name;
+ db_expr_t off;
+
+ db_find_sym_and_offset(addr, &name, &off);
+ if (off == 0)
+ db_printf("%s:\t", name);
+ else
+ db_printf("\t\t");
+
+ db_prev = addr;
+ }
+
+ switch (c) {
+ case 'r': /* signed, current radix */
+ value = db_get_value(addr, size, TRUE);
+ addr += size;
+ db_printf("%-*r", width, value);
+ break;
+ case 'x': /* unsigned hex */
+ value = db_get_value(addr, size, FALSE);
+ addr += size;
+ db_printf("%-*x", width, value);
+ break;
+ case 'z': /* signed hex */
+ value = db_get_value(addr, size, TRUE);
+ addr += size;
+ db_printf("%-*z", width, value);
+ break;
+ case 'd': /* signed decimal */
+ value = db_get_value(addr, size, TRUE);
+ addr += size;
+ db_printf("%-*d", width, value);
+ break;
+ case 'u': /* unsigned decimal */
+ value = db_get_value(addr, size, FALSE);
+ addr += size;
+ db_printf("%-*u", width, value);
+ break;
+ case 'o': /* unsigned octal */
+ value = db_get_value(addr, size, FALSE);
+ addr += size;
+ db_printf("%-*o", width, value);
+ break;
+ case 'c': /* character */
+ value = db_get_value(addr, 1, FALSE);
+ addr += 1;
+ if (value >= ' ' && value <= '~')
+ db_printf("%c", value);
+ else
+ db_printf("\\%03o", value);
+ break;
+ case 's': /* null-terminated string */
+ for (;;) {
+ value = db_get_value(addr, 1, FALSE);
+ addr += 1;
+ if (value == 0)
+ break;
+ if (value >= ' ' && value <= '~')
+ db_printf("%c", value);
+ else
+ db_printf("\\%03o", value);
+ }
+ break;
+ case 'i': /* instruction */
+ addr = db_disasm(addr, FALSE);
+ break;
+ case 'I': /* instruction, alternate form */
+ addr = db_disasm(addr, TRUE);
+ break;
+ default:
+ break;
+ }
+ if (db_print_position() != 0)
+ db_end_line();
+ break;
+ }
+ }
+ }
+ db_next = addr;
+}
+
+/*
+ * Print value.
+ */
+char db_print_format = 'x';
+
+/*ARGSUSED*/
+void
+db_print_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ int have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ db_expr_t value;
+
+ if (modif[0] != '\0')
+ db_print_format = modif[0];
+
+ switch (db_print_format) {
+ case 'a':
+ db_printsym((db_addr_t)addr, DB_STGY_ANY);
+ break;
+ case 'r':
+ db_printf("%11r", addr);
+ break;
+ case 'x':
+ db_printf("%8x", addr);
+ break;
+ case 'z':
+ db_printf("%8z", addr);
+ break;
+ case 'd':
+ db_printf("%11d", addr);
+ break;
+ case 'u':
+ db_printf("%11u", addr);
+ break;
+ case 'o':
+ db_printf("%16o", addr);
+ break;
+ case 'c':
+ value = addr & 0xFF;
+ if (value >= ' ' && value <= '~')
+ db_printf("%c", value);
+ else
+ db_printf("\\%03o", value);
+ break;
+ }
+ db_printf("\n");
+}
+
+void
+db_print_loc_and_inst(loc)
+ db_addr_t loc;
+{
+ db_printsym(loc, DB_STGY_PROC);
+ db_printf(":\t");
+ (void) db_disasm(loc, TRUE);
+}
+
+/*
+ * Search for a value in memory.
+ * Syntax: search [/bhl] addr value [mask] [,count]
+ */
+void
+db_search_cmd(db_expr_t dummy1, int dummy2, db_expr_t dummy3, char *dummy4)
+{
+ int t;
+ db_addr_t addr;
+ int size;
+ db_expr_t value;
+ db_expr_t mask;
+ unsigned int count;
+
+ t = db_read_token();
+ if (t == tSLASH) {
+ t = db_read_token();
+ if (t != tIDENT) {
+ bad_modifier:
+ db_printf("Bad modifier\n");
+ db_flush_lex();
+ return;
+ }
+
+ if (!strcmp(db_tok_string, "b"))
+ size = 1;
+ else if (!strcmp(db_tok_string, "h"))
+ size = 2;
+ else if (!strcmp(db_tok_string, "l"))
+ size = 4;
+ else
+ goto bad_modifier;
+ } else {
+ db_unread_token(t);
+ size = 4;
+ }
+
+ if (!db_expression((db_expr_t *)&addr)) {
+ db_printf("Address missing\n");
+ db_flush_lex();
+ return;
+ }
+
+ if (!db_expression(&value)) {
+ db_printf("Value missing\n");
+ db_flush_lex();
+ return;
+ }
+
+ if (!db_expression(&mask))
+ mask = 0xffffffffUL;
+
+ t = db_read_token();
+ if (t == tCOMMA) {
+ if (!db_expression(&count)) {
+ db_printf("Count missing\n");
+ db_flush_lex();
+ return;
+ }
+ } else {
+ db_unread_token(t);
+ count = -1; /* effectively forever */
+ }
+ db_skip_to_eol();
+
+ db_search(addr, size, value, mask, count);
+}
+
+static void
+db_search(addr, size, value, mask, count)
+ register
+ db_addr_t addr;
+ int size;
+ db_expr_t value;
+ db_expr_t mask;
+ unsigned int count;
+{
+ while (count-- != 0) {
+ db_prev = addr;
+ if ((db_get_value(addr, size, FALSE) & mask) == value)
+ break;
+ addr += size;
+ }
+ db_next = addr;
+}
diff --git a/sys/ddb/db_expr.c b/sys/ddb/db_expr.c
new file mode 100644
index 0000000..3d23949
--- /dev/null
+++ b/sys/ddb/db_expr.c
@@ -0,0 +1,226 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_expr.c,v 1.2 1993/10/16 16:47:14 rgrimes Exp $
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+#include <ddb/db_lex.h>
+#include <ddb/db_access.h>
+#include <ddb/db_command.h>
+
+boolean_t
+db_term(valuep)
+ db_expr_t *valuep;
+{
+ int t;
+
+ t = db_read_token();
+ if (t == tIDENT) {
+ if (!db_value_of_name(db_tok_string, valuep)) {
+ db_error("Symbol not found\n");
+ /*NOTREACHED*/
+ }
+ return (TRUE);
+ }
+ if (t == tNUMBER) {
+ *valuep = (db_expr_t)db_tok_number;
+ return (TRUE);
+ }
+ if (t == tDOT) {
+ *valuep = (db_expr_t)db_dot;
+ return (TRUE);
+ }
+ if (t == tDOTDOT) {
+ *valuep = (db_expr_t)db_prev;
+ return (TRUE);
+ }
+ if (t == tPLUS) {
+ *valuep = (db_expr_t) db_next;
+ return (TRUE);
+ }
+ if (t == tDITTO) {
+ *valuep = (db_expr_t)db_last_addr;
+ return (TRUE);
+ }
+ if (t == tDOLLAR) {
+ if (!db_get_variable(valuep))
+ return (FALSE);
+ return (TRUE);
+ }
+ if (t == tLPAREN) {
+ if (!db_expression(valuep)) {
+ db_error("Syntax error\n");
+ /*NOTREACHED*/
+ }
+ t = db_read_token();
+ if (t != tRPAREN) {
+ db_error("Syntax error\n");
+ /*NOTREACHED*/
+ }
+ return (TRUE);
+ }
+ db_unread_token(t);
+ return (FALSE);
+}
+
+boolean_t
+db_unary(valuep)
+ db_expr_t *valuep;
+{
+ int t;
+
+ t = db_read_token();
+ if (t == tMINUS) {
+ if (!db_unary(valuep)) {
+ db_error("Syntax error\n");
+ /*NOTREACHED*/
+ }
+ *valuep = -*valuep;
+ return (TRUE);
+ }
+ if (t == tSTAR) {
+ /* indirection */
+ if (!db_unary(valuep)) {
+ db_error("Syntax error\n");
+ /*NOTREACHED*/
+ }
+ *valuep = db_get_value((db_addr_t)*valuep, sizeof(int), FALSE);
+ return (TRUE);
+ }
+ db_unread_token(t);
+ return (db_term(valuep));
+}
+
+boolean_t
+db_mult_expr(valuep)
+ db_expr_t *valuep;
+{
+ db_expr_t lhs, rhs;
+ int t;
+
+ if (!db_unary(&lhs))
+ return (FALSE);
+
+ t = db_read_token();
+ while (t == tSTAR || t == tSLASH || t == tPCT || t == tHASH) {
+ if (!db_term(&rhs)) {
+ db_error("Syntax error\n");
+ /*NOTREACHED*/
+ }
+ if (t == tSTAR)
+ lhs *= rhs;
+ else {
+ if (rhs == 0) {
+ db_error("Divide by 0\n");
+ /*NOTREACHED*/
+ }
+ if (t == tSLASH)
+ lhs /= rhs;
+ else if (t == tPCT)
+ lhs %= rhs;
+ else
+ lhs = ((lhs+rhs-1)/rhs)*rhs;
+ }
+ t = db_read_token();
+ }
+ db_unread_token(t);
+ *valuep = lhs;
+ return (TRUE);
+}
+
+boolean_t
+db_add_expr(valuep)
+ db_expr_t *valuep;
+{
+ db_expr_t lhs, rhs;
+ int t;
+
+ if (!db_mult_expr(&lhs))
+ return (FALSE);
+
+ t = db_read_token();
+ while (t == tPLUS || t == tMINUS) {
+ if (!db_mult_expr(&rhs)) {
+ db_error("Syntax error\n");
+ /*NOTREACHED*/
+ }
+ if (t == tPLUS)
+ lhs += rhs;
+ else
+ lhs -= rhs;
+ t = db_read_token();
+ }
+ db_unread_token(t);
+ *valuep = lhs;
+ return (TRUE);
+}
+
+boolean_t
+db_shift_expr(valuep)
+ db_expr_t *valuep;
+{
+ db_expr_t lhs, rhs;
+ int t;
+
+ if (!db_add_expr(&lhs))
+ return (FALSE);
+
+ t = db_read_token();
+ while (t == tSHIFT_L || t == tSHIFT_R) {
+ if (!db_add_expr(&rhs)) {
+ db_error("Syntax error\n");
+ /*NOTREACHED*/
+ }
+ if (rhs < 0) {
+ db_error("Negative shift amount\n");
+ /*NOTREACHED*/
+ }
+ if (t == tSHIFT_L)
+ lhs <<= rhs;
+ else {
+ /* Shift right is unsigned */
+ lhs = (unsigned) lhs >> rhs;
+ }
+ t = db_read_token();
+ }
+ db_unread_token(t);
+ *valuep = lhs;
+ return (TRUE);
+}
+
+int
+db_expression(valuep)
+ db_expr_t *valuep;
+{
+ return (db_shift_expr(valuep));
+}
diff --git a/sys/ddb/db_input.c b/sys/ddb/db_input.c
new file mode 100644
index 0000000..94bab94
--- /dev/null
+++ b/sys/ddb/db_input.c
@@ -0,0 +1,256 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_input.c,v 1.3 1993/11/25 01:30:06 wollman Exp $
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+#include "ddb/db_output.h"
+#include "machine/cons.h"
+
+/*
+ * Character input and editing.
+ */
+
+/*
+ * We don't track output position while editing input,
+ * since input always ends with a new-line. We just
+ * reset the line position at the end.
+ */
+char * db_lbuf_start; /* start of input line buffer */
+char * db_lbuf_end; /* end of input line buffer */
+char * db_lc; /* current character */
+char * db_le; /* one past last character */
+
+#define CTRL(c) ((c) & 0x1f)
+#define isspace(c) ((c) == ' ' || (c) == '\t')
+#define BLANK ' '
+#define BACKUP '\b'
+
+void
+db_putstring(s, count)
+ char *s;
+ int count;
+{
+ while (--count >= 0)
+ cnputc(*s++);
+}
+
+void
+db_putnchars(c, count)
+ int c;
+ int count;
+{
+ while (--count >= 0)
+ cnputc(c);
+}
+
+/*
+ * Delete N characters, forward or backward
+ */
+#define DEL_FWD 0
+#define DEL_BWD 1
+void
+db_delete(n, bwd)
+ int n;
+ int bwd;
+{
+ register char *p;
+
+ if (bwd) {
+ db_lc -= n;
+ db_putnchars(BACKUP, n);
+ }
+ for (p = db_lc; p < db_le-n; p++) {
+ *p = *(p+n);
+ cnputc(*p);
+ }
+ db_putnchars(BLANK, n);
+ db_putnchars(BACKUP, db_le - db_lc);
+ db_le -= n;
+}
+
+/* returns TRUE at end-of-line */
+int
+db_inputchar(c)
+ int c;
+{
+ switch (c) {
+ case CTRL('b'):
+ /* back up one character */
+ if (db_lc > db_lbuf_start) {
+ cnputc(BACKUP);
+ db_lc--;
+ }
+ break;
+ case CTRL('f'):
+ /* forward one character */
+ if (db_lc < db_le) {
+ cnputc(*db_lc);
+ db_lc++;
+ }
+ break;
+ case CTRL('a'):
+ /* beginning of line */
+ while (db_lc > db_lbuf_start) {
+ cnputc(BACKUP);
+ db_lc--;
+ }
+ break;
+ case CTRL('e'):
+ /* end of line */
+ while (db_lc < db_le) {
+ cnputc(*db_lc);
+ db_lc++;
+ }
+ break;
+ case CTRL('h'):
+ case 0177:
+ /* erase previous character */
+ if (db_lc > db_lbuf_start)
+ db_delete(1, DEL_BWD);
+ break;
+ case CTRL('d'):
+ /* erase next character */
+ if (db_lc < db_le)
+ db_delete(1, DEL_FWD);
+ break;
+ case CTRL('k'):
+ /* delete to end of line */
+ if (db_lc < db_le)
+ db_delete(db_le - db_lc, DEL_FWD);
+ break;
+ case CTRL('t'):
+ /* twiddle last 2 characters */
+ if (db_lc >= db_lbuf_start + 2) {
+ c = db_lc[-2];
+ db_lc[-2] = db_lc[-1];
+ db_lc[-1] = c;
+ cnputc(BACKUP);
+ cnputc(BACKUP);
+ cnputc(db_lc[-2]);
+ cnputc(db_lc[-1]);
+ }
+ break;
+ case CTRL('r'):
+ db_putstring("^R\n", 3);
+ if (db_le > db_lbuf_start) {
+ db_putstring(db_lbuf_start, db_le - db_lbuf_start);
+ db_putnchars(BACKUP, db_le - db_lc);
+ }
+ break;
+ case '\n':
+ case '\r':
+ *db_le++ = c;
+ return (1);
+ default:
+ if (db_le == db_lbuf_end) {
+ cnputc('\007');
+ }
+ else if (c >= ' ' && c <= '~') {
+ register char *p;
+
+ for (p = db_le; p > db_lc; p--)
+ *p = *(p-1);
+ *db_lc++ = c;
+ db_le++;
+ cnputc(c);
+ db_putstring(db_lc, db_le - db_lc);
+ db_putnchars(BACKUP, db_le - db_lc);
+ }
+ break;
+ }
+ return (0);
+}
+
+int
+db_readline(lstart, lsize)
+ char * lstart;
+ int lsize;
+{
+ db_force_whitespace(); /* synch output position */
+
+ db_lbuf_start = lstart;
+ db_lbuf_end = lstart + lsize;
+ db_lc = lstart;
+ db_le = lstart;
+
+ while (!db_inputchar(cngetc()))
+ continue;
+
+ db_putchar('\n'); /* synch output position */
+
+ *db_le = 0;
+ return (db_le - db_lbuf_start);
+}
+
+void
+db_check_interrupt()
+{
+ register int c;
+
+ c = cnmaygetc();
+ switch (c) {
+ case -1: /* no character */
+ return;
+
+ case CTRL('c'):
+ db_error((char *)0);
+ /*NOTREACHED*/
+
+ case CTRL('s'):
+ do {
+ c = cnmaygetc();
+ if (c == CTRL('c'))
+ db_error((char *)0);
+ } while (c != CTRL('q'));
+ break;
+
+ default:
+ /* drop on floor */
+ break;
+ }
+}
+
+int
+cnmaygetc (void)
+{
+ return (-1);
+}
+
+/* called from kdb_trap in db_interface.c */
+void
+cnpollc (flag)
+ int flag;
+{
+}
diff --git a/sys/ddb/db_lex.c b/sys/ddb/db_lex.c
new file mode 100644
index 0000000..655a1b5
--- /dev/null
+++ b/sys/ddb/db_lex.c
@@ -0,0 +1,279 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_lex.c,v 1.2 1993/10/16 16:47:17 rgrimes Exp $
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+/*
+ * Lexical analyzer.
+ */
+#include "param.h"
+#include "systm.h"
+#include "ddb/ddb.h"
+#include <ddb/db_lex.h>
+
+char db_line[120];
+char * db_lp, *db_endlp;
+
+int
+db_read_line()
+{
+ int i;
+
+ i = db_readline(db_line, sizeof(db_line));
+ if (i == 0)
+ return (0); /* EOI */
+ db_lp = db_line;
+ db_endlp = db_lp + i;
+ return (i);
+}
+
+void
+db_flush_line()
+{
+ db_lp = db_line;
+ db_endlp = db_line;
+}
+
+int db_look_char = 0;
+
+int
+db_read_char()
+{
+ int c;
+
+ if (db_look_char != 0) {
+ c = db_look_char;
+ db_look_char = 0;
+ }
+ else if (db_lp >= db_endlp)
+ c = -1;
+ else
+ c = *db_lp++;
+ return (c);
+}
+
+void
+db_unread_char(c)
+ int c;
+{
+ db_look_char = c;
+}
+
+int db_look_token = 0;
+
+void
+db_unread_token(t)
+ int t;
+{
+ db_look_token = t;
+}
+
+int
+db_read_token()
+{
+ int t;
+
+ if (db_look_token) {
+ t = db_look_token;
+ db_look_token = 0;
+ }
+ else
+ t = db_lex();
+ return (t);
+}
+
+int db_tok_number;
+char db_tok_string[TOK_STRING_SIZE];
+
+int db_radix = 16;
+
+void
+db_flush_lex()
+{
+ db_flush_line();
+ db_look_char = 0;
+ db_look_token = 0;
+}
+
+int
+db_lex()
+{
+ int c;
+
+ c = db_read_char();
+ while (c <= ' ' || c > '~') {
+ if (c == '\n' || c == -1)
+ return (tEOL);
+ c = db_read_char();
+ }
+
+ if (c >= '0' && c <= '9') {
+ /* number */
+ int r, digit = 0;
+
+ if (c > '0')
+ r = db_radix;
+ else {
+ c = db_read_char();
+ if (c == 'O' || c == 'o')
+ r = 8;
+ else if (c == 'T' || c == 't')
+ r = 10;
+ else if (c == 'X' || c == 'x')
+ r = 16;
+ else {
+ r = db_radix;
+ db_unread_char(c);
+ }
+ c = db_read_char();
+ }
+ db_tok_number = 0;
+ for (;;) {
+ if (c >= '0' && c <= ((r == 8) ? '7' : '9'))
+ digit = c - '0';
+ else if (r == 16 && ((c >= 'A' && c <= 'F') ||
+ (c >= 'a' && c <= 'f'))) {
+ if (c >= 'a')
+ digit = c - 'a' + 10;
+ else if (c >= 'A')
+ digit = c - 'A' + 10;
+ }
+ else
+ break;
+ db_tok_number = db_tok_number * r + digit;
+ c = db_read_char();
+ }
+ if ((c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ (c == '_'))
+ {
+ db_error("Bad character in number\n");
+ db_flush_lex();
+ return (tEOF);
+ }
+ db_unread_char(c);
+ return (tNUMBER);
+ }
+ if ((c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ c == '_' || c == '\\')
+ {
+ /* string */
+ char *cp;
+
+ cp = db_tok_string;
+ if (c == '\\') {
+ c = db_read_char();
+ if (c == '\n' || c == -1)
+ db_error("Bad escape\n");
+ }
+ *cp++ = c;
+ while (1) {
+ c = db_read_char();
+ if ((c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= '0' && c <= '9') ||
+ c == '_' || c == '\\' || c == ':')
+ {
+ if (c == '\\') {
+ c = db_read_char();
+ if (c == '\n' || c == -1)
+ db_error("Bad escape\n");
+ }
+ *cp++ = c;
+ if (cp == db_tok_string+sizeof(db_tok_string)) {
+ db_error("String too long\n");
+ db_flush_lex();
+ return (tEOF);
+ }
+ continue;
+ }
+ else {
+ *cp = '\0';
+ break;
+ }
+ }
+ db_unread_char(c);
+ return (tIDENT);
+ }
+
+ switch (c) {
+ case '+':
+ return (tPLUS);
+ case '-':
+ return (tMINUS);
+ case '.':
+ c = db_read_char();
+ if (c == '.')
+ return (tDOTDOT);
+ db_unread_char(c);
+ return (tDOT);
+ case '*':
+ return (tSTAR);
+ case '/':
+ return (tSLASH);
+ case '=':
+ return (tEQ);
+ case '%':
+ return (tPCT);
+ case '#':
+ return (tHASH);
+ case '(':
+ return (tLPAREN);
+ case ')':
+ return (tRPAREN);
+ case ',':
+ return (tCOMMA);
+ case '"':
+ return (tDITTO);
+ case '$':
+ return (tDOLLAR);
+ case '!':
+ return (tEXCL);
+ case '<':
+ c = db_read_char();
+ if (c == '<')
+ return (tSHIFT_L);
+ db_unread_char(c);
+ break;
+ case '>':
+ c = db_read_char();
+ if (c == '>')
+ return (tSHIFT_R);
+ db_unread_char(c);
+ break;
+ case -1:
+ return (tEOF);
+ }
+ db_printf("Bad character\n");
+ db_flush_lex();
+ return (tEOF);
+}
diff --git a/sys/ddb/db_lex.h b/sys/ddb/db_lex.h
new file mode 100644
index 0000000..e2f224e
--- /dev/null
+++ b/sys/ddb/db_lex.h
@@ -0,0 +1,77 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_lex.h,v 1.2 1993/10/16 16:47:19 rgrimes Exp $
+ */
+
+#ifndef _DDB_DB_LEX_H_
+#define _DDB_DB_LEX_H_ 1
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+/*
+ * Lexical analyzer.
+ */
+extern int db_read_line();
+extern void db_flush_line();
+extern int db_read_char();
+extern void db_unread_char(/* char c */);
+extern int db_read_token();
+extern void db_unread_token(/* int t */);
+extern void db_flush_lex();
+
+extern int db_tok_number;
+#define TOK_STRING_SIZE 120
+extern char db_tok_string[TOK_STRING_SIZE];
+extern int db_radix;
+
+#define tEOF (-1)
+#define tEOL 1
+#define tNUMBER 2
+#define tIDENT 3
+#define tPLUS 4
+#define tMINUS 5
+#define tDOT 6
+#define tSTAR 7
+#define tSLASH 8
+#define tEQ 9
+#define tLPAREN 10
+#define tRPAREN 11
+#define tPCT 12
+#define tHASH 13
+#define tCOMMA 14
+#define tDITTO 15
+#define tDOLLAR 16
+#define tEXCL 17
+#define tSHIFT_L 18
+#define tSHIFT_R 19
+#define tDOTDOT 20
+
+
+
+
+#endif /* _DDB_DB_LEX_H_ */
diff --git a/sys/ddb/db_output.c b/sys/ddb/db_output.c
new file mode 100644
index 0000000..fc1bb14
--- /dev/null
+++ b/sys/ddb/db_output.c
@@ -0,0 +1,370 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_output.c,v 1.5 1993/11/25 01:30:08 wollman Exp $
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+/*
+ * Printf and character output for debugger.
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "machine/stdarg.h"
+#include "ddb/ddb.h"
+#include "machine/cons.h"
+
+/*
+ * Character output - tracks position in line.
+ * To do this correctly, we should know how wide
+ * the output device is - then we could zero
+ * the line position when the output device wraps
+ * around to the start of the next line.
+ *
+ * Instead, we count the number of spaces printed
+ * since the last printing character so that we
+ * don't print trailing spaces. This avoids most
+ * of the wraparounds.
+ */
+int db_output_position = 0; /* output column */
+int db_last_non_space = 0; /* last non-space character */
+int db_tab_stop_width = 8; /* how wide are tab stops? */
+#define NEXT_TAB(i) \
+ ((((i) + db_tab_stop_width) / db_tab_stop_width) * db_tab_stop_width)
+int db_max_width = 80; /* output line width */
+
+
+static void db_printf_guts(const char *, va_list);
+
+/*
+ * Force pending whitespace.
+ */
+void
+db_force_whitespace()
+{
+ register int last_print, next_tab;
+
+ last_print = db_last_non_space;
+ while (last_print < db_output_position) {
+ next_tab = NEXT_TAB(last_print);
+ if (next_tab <= db_output_position) {
+ while (last_print < next_tab) { /* DON'T send a tab!!! */
+ cnputc(' ');
+ last_print++;
+ }
+ }
+ else {
+ cnputc(' ');
+ last_print++;
+ }
+ }
+ db_last_non_space = db_output_position;
+}
+
+/*
+ * Output character. Buffer whitespace.
+ */
+void
+db_putchar(c)
+ int c; /* character to output */
+{
+ if (c > ' ' && c <= '~') {
+ /*
+ * Printing character.
+ * If we have spaces to print, print them first.
+ * Use tabs if possible.
+ */
+ db_force_whitespace();
+ cnputc(c);
+ db_output_position++;
+ db_last_non_space = db_output_position;
+ }
+ else if (c == '\n') {
+ /* Return */
+ cnputc(c);
+ db_output_position = 0;
+ db_last_non_space = 0;
+ db_check_interrupt();
+ }
+ else if (c == '\t') {
+ /* assume tabs every 8 positions */
+ db_output_position = NEXT_TAB(db_output_position);
+ }
+ else if (c == ' ') {
+ /* space */
+ db_output_position++;
+ }
+ else if (c == '\007') {
+ /* bell */
+ cnputc(c);
+ }
+ /* other characters are assumed non-printing */
+}
+
+/*
+ * Return output position
+ */
+int
+db_print_position()
+{
+ return (db_output_position);
+}
+
+/*
+ * Printing
+ */
+void
+db_printf(const char *fmt, ...)
+{
+ va_list listp;
+ va_start(listp, fmt);
+ db_printf_guts (fmt, listp);
+ va_end(listp);
+}
+
+/* alternate name */
+
+/*VARARGS1*/
+void
+kdbprintf(char *fmt, ...)
+{
+ va_list listp;
+ va_start(listp, fmt);
+ db_printf_guts (fmt, listp);
+ va_end(listp);
+}
+
+/*
+ * End line if too long.
+ */
+void
+db_end_line()
+{
+ if (db_output_position >= db_max_width)
+ db_printf("\n");
+}
+
+/*
+ * Put a number (base <= 16) in a buffer in reverse order; return an
+ * optional length and a pointer to the NULL terminated (preceded?)
+ * buffer.
+ */
+static char *
+db_ksprintn(ul, base, lenp)
+ register u_long ul;
+ register int base, *lenp;
+{ /* A long in base 8, plus NULL. */
+ static char buf[sizeof(long) * NBBY / 3 + 2];
+ register char *p;
+
+ p = buf;
+ do {
+ *++p = "0123456789abcdef"[ul % base];
+ } while (ul /= base);
+ if (lenp)
+ *lenp = p - buf;
+ return (p);
+}
+
+static void
+db_printf_guts(fmt, ap)
+ register const char *fmt;
+ va_list ap;
+{
+ register char *p;
+ register int ch, n;
+ u_long ul;
+ int base, lflag, tmp, width;
+ char padc;
+ int ladjust;
+ int sharpflag;
+ int neg;
+
+ for (;;) {
+ padc = ' ';
+ width = 0;
+ while ((ch = *(u_char *)fmt++) != '%') {
+ if (ch == '\0')
+ return;
+ db_putchar(ch);
+ }
+ lflag = 0;
+ ladjust = 0;
+ sharpflag = 0;
+ neg = 0;
+reswitch: switch (ch = *(u_char *)fmt++) {
+ case '0':
+ padc = '0';
+ goto reswitch;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ for (width = 0;; ++fmt) {
+ width = width * 10 + ch - '0';
+ ch = *fmt;
+ if (ch < '0' || ch > '9')
+ break;
+ }
+ goto reswitch;
+ case 'l':
+ lflag = 1;
+ goto reswitch;
+ case '-':
+ ladjust = 1;
+ goto reswitch;
+ case '#':
+ sharpflag = 1;
+ goto reswitch;
+ case 'b':
+ ul = va_arg(ap, int);
+ p = va_arg(ap, char *);
+ for (p = db_ksprintn(ul, *p++, NULL); ch = *p--;)
+ db_putchar(ch);
+
+ if (!ul)
+ break;
+
+ for (tmp = 0; n = *p++;) {
+ if (ul & (1 << (n - 1))) {
+ db_putchar(tmp ? ',' : '<');
+ for (; (n = *p) > ' '; ++p)
+ db_putchar(n);
+ tmp = 1;
+ } else
+ for (; *p > ' '; ++p);
+ }
+ if (tmp)
+ db_putchar('>');
+ break;
+ case '*':
+ width = va_arg (ap, int);
+ if (width < 0) {
+ ladjust = !ladjust;
+ width = -width;
+ }
+ goto reswitch;
+ case 'c':
+ db_putchar(va_arg(ap, int));
+ break;
+ case 's':
+ p = va_arg(ap, char *);
+ width -= strlen (p);
+ if (!ladjust && width > 0)
+ while (width--)
+ db_putchar (padc);
+ while (ch = *p++)
+ db_putchar(ch);
+ if (ladjust && width > 0)
+ while (width--)
+ db_putchar (padc);
+ break;
+ case 'r':
+ ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
+ if ((long)ul < 0) {
+ neg = 1;
+ ul = -(long)ul;
+ }
+ base = db_radix;
+ if (base < 8 || base > 16)
+ base = 10;
+ goto number;
+ case 'n':
+ ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
+ base = db_radix;
+ if (base < 8 || base > 16)
+ base = 10;
+ goto number;
+ case 'd':
+ ul = lflag ? va_arg(ap, long) : va_arg(ap, int);
+ if ((long)ul < 0) {
+ neg = 1;
+ ul = -(long)ul;
+ }
+ base = 10;
+ goto number;
+ case 'o':
+ ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
+ base = 8;
+ goto number;
+ case 'u':
+ ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
+ base = 10;
+ goto number;
+ case 'z':
+ ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
+ if ((long)ul < 0) {
+ neg = 1;
+ ul = -(long)ul;
+ }
+ base = 16;
+ goto number;
+ case 'x':
+ ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
+ base = 16;
+number: p = (char *)db_ksprintn(ul, base, &tmp);
+ if (sharpflag && ul != 0) {
+ if (base == 8)
+ tmp++;
+ else if (base == 16)
+ tmp += 2;
+ }
+ if (neg)
+ tmp++;
+
+ if (!ladjust && width && (width -= tmp) > 0)
+ while (width--)
+ db_putchar(padc);
+ if (neg)
+ db_putchar ('-');
+ if (sharpflag && ul != 0) {
+ if (base == 8) {
+ db_putchar ('0');
+ } else if (base == 16) {
+ db_putchar ('0');
+ db_putchar ('x');
+ }
+ }
+ if (ladjust && width && (width -= tmp) > 0)
+ while (width--)
+ db_putchar(padc);
+
+ while (ch = *p--)
+ db_putchar(ch);
+ break;
+ default:
+ db_putchar('%');
+ if (lflag)
+ db_putchar('l');
+ /* FALLTHROUGH */
+ case '%':
+ db_putchar(ch);
+ }
+ }
+}
+
diff --git a/sys/ddb/db_output.h b/sys/ddb/db_output.h
new file mode 100644
index 0000000..3969a34
--- /dev/null
+++ b/sys/ddb/db_output.h
@@ -0,0 +1,44 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_output.h,v 1.3 1993/11/07 17:39:24 wollman Exp $
+ */
+
+#ifndef _DDB_DB_OUTPUT_H_
+#define _DDB_DB_OUTPUT_H_ 1
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 8/90
+ */
+
+/*
+ * Printing routines for kernel debugger.
+ */
+
+extern void db_force_whitespace();
+extern int db_print_position();
+extern void db_end_line();
+#endif /* _DDB_DB_OUTPUT_H_ */
diff --git a/sys/ddb/db_print.c b/sys/ddb/db_print.c
new file mode 100644
index 0000000..7ab2099
--- /dev/null
+++ b/sys/ddb/db_print.c
@@ -0,0 +1,70 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_print.c,v 1.2 1993/10/16 16:47:22 rgrimes Exp $
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+/*
+ * Miscellaneous printing.
+ */
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+
+#include "ddb/ddb.h"
+
+#include <ddb/db_lex.h>
+#include <ddb/db_variables.h>
+#include <ddb/db_sym.h>
+
+extern unsigned int db_maxoff;
+
+void
+db_show_regs(db_expr_t dummy1, int dummy2, db_expr_t dummy3, char *dummy4)
+{
+ int (*func)();
+ register struct db_variable *regp;
+ db_expr_t value, offset;
+ char * name;
+
+ for (regp = db_regs; regp < db_eregs; regp++) {
+ db_read_variable(regp, &value);
+ db_printf("%-12s%#10n", regp->name, value);
+ db_find_xtrn_sym_and_offset((db_addr_t)value, &name, &offset);
+ if (name != 0 && offset <= db_maxoff && offset != value) {
+ db_printf("\t%s", name);
+ if (offset != 0)
+ db_printf("+%#r", offset);
+ }
+ db_printf("\n");
+ }
+ db_print_loc_and_inst(PC_REGS(DDB_REGS));
+}
+
diff --git a/sys/ddb/db_run.c b/sys/ddb/db_run.c
new file mode 100644
index 0000000..1ba43c1
--- /dev/null
+++ b/sys/ddb/db_run.c
@@ -0,0 +1,391 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_run.c,v 1.2 1993/10/16 16:47:24 rgrimes Exp $
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+/*
+ * Commands to run process.
+ */
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+
+#include <ddb/db_lex.h>
+#include <ddb/db_break.h>
+#include <ddb/db_access.h>
+
+int db_run_mode;
+#define STEP_NONE 0
+#define STEP_ONCE 1
+#define STEP_RETURN 2
+#define STEP_CALLT 3
+#define STEP_CONTINUE 4
+#define STEP_INVISIBLE 5
+#define STEP_COUNT 6
+
+boolean_t db_sstep_print;
+int db_loop_count;
+int db_call_depth;
+
+int db_inst_count;
+int db_load_count;
+int db_store_count;
+
+#ifndef db_set_single_step
+void db_set_single_step(/* db_regs_t *regs */); /* forward */
+#endif
+#ifndef db_clear_single_step
+void db_clear_single_step(/* db_regs_t *regs */);
+#endif
+
+boolean_t
+db_stop_at_pc(is_breakpoint)
+ boolean_t *is_breakpoint;
+{
+ register db_addr_t pc;
+ register db_breakpoint_t bkpt;
+
+ db_clear_single_step(DDB_REGS);
+ db_clear_breakpoints();
+ db_clear_watchpoints();
+ pc = PC_REGS(DDB_REGS);
+
+#ifdef FIXUP_PC_AFTER_BREAK
+ if (*is_breakpoint) {
+ /*
+ * Breakpoint trap. Fix up the PC if the
+ * machine requires it.
+ */
+ FIXUP_PC_AFTER_BREAK
+ pc = PC_REGS(DDB_REGS);
+ }
+#endif
+
+ /*
+ * Now check for a breakpoint at this address.
+ */
+ bkpt = db_find_breakpoint_here(pc);
+ if (bkpt) {
+ if (--bkpt->count == 0) {
+ bkpt->count = bkpt->init_count;
+ *is_breakpoint = TRUE;
+ return (TRUE); /* stop here */
+ }
+ } else if (*is_breakpoint) {
+ ddb_regs.tf_eip += 1;
+ }
+
+ *is_breakpoint = FALSE;
+
+ if (db_run_mode == STEP_INVISIBLE) {
+ db_run_mode = STEP_CONTINUE;
+ return (FALSE); /* continue */
+ }
+ if (db_run_mode == STEP_COUNT) {
+ return (FALSE); /* continue */
+ }
+ if (db_run_mode == STEP_ONCE) {
+ if (--db_loop_count > 0) {
+ if (db_sstep_print) {
+ db_printf("\t\t");
+ db_print_loc_and_inst(pc);
+ db_printf("\n");
+ }
+ return (FALSE); /* continue */
+ }
+ }
+ if (db_run_mode == STEP_RETURN) {
+ db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
+
+ /* continue until matching return */
+
+ if (!inst_trap_return(ins) &&
+ (!inst_return(ins) || --db_call_depth != 0)) {
+ if (db_sstep_print) {
+ if (inst_call(ins) || inst_return(ins)) {
+ register int i;
+
+ db_printf("[after %6d] ", db_inst_count);
+ for (i = db_call_depth; --i > 0; )
+ db_printf(" ");
+ db_print_loc_and_inst(pc);
+ db_printf("\n");
+ }
+ }
+ if (inst_call(ins))
+ db_call_depth++;
+ return (FALSE); /* continue */
+ }
+ }
+ if (db_run_mode == STEP_CALLT) {
+ db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
+
+ /* continue until call or return */
+
+ if (!inst_call(ins) &&
+ !inst_return(ins) &&
+ !inst_trap_return(ins)) {
+ return (FALSE); /* continue */
+ }
+ }
+ db_run_mode = STEP_NONE;
+ return (TRUE);
+}
+
+void
+db_restart_at_pc(watchpt)
+ boolean_t watchpt;
+{
+ register db_addr_t pc = PC_REGS(DDB_REGS);
+
+ if ((db_run_mode == STEP_COUNT) ||
+ (db_run_mode == STEP_RETURN) ||
+ (db_run_mode == STEP_CALLT)) {
+ db_expr_t ins;
+
+ /*
+ * We are about to execute this instruction,
+ * so count it now.
+ */
+
+ ins = db_get_value(pc, sizeof(int), FALSE);
+ db_inst_count++;
+ db_load_count += inst_load(ins);
+ db_store_count += inst_store(ins);
+#ifdef SOFTWARE_SSTEP
+ /* XXX works on mips, but... */
+ if (inst_branch(ins) || inst_call(ins)) {
+ ins = db_get_value(next_instr_address(pc,1),
+ sizeof(int), FALSE);
+ db_inst_count++;
+ db_load_count += inst_load(ins);
+ db_store_count += inst_store(ins);
+ }
+#endif SOFTWARE_SSTEP
+ }
+
+ if (db_run_mode == STEP_CONTINUE) {
+ if (watchpt || db_find_breakpoint_here(pc)) {
+ /*
+ * Step over breakpoint/watchpoint.
+ */
+ db_run_mode = STEP_INVISIBLE;
+ db_set_single_step(DDB_REGS);
+ } else {
+ db_set_breakpoints();
+ db_set_watchpoints();
+ }
+ } else {
+ db_set_single_step(DDB_REGS);
+ }
+}
+
+void
+db_single_step(regs)
+ db_regs_t *regs;
+{
+ if (db_run_mode == STEP_CONTINUE) {
+ db_run_mode = STEP_INVISIBLE;
+ db_set_single_step(regs);
+ }
+}
+
+#ifdef SOFTWARE_SSTEP
+/*
+ * Software implementation of single-stepping.
+ * If your machine does not have a trace mode
+ * similar to the vax or sun ones you can use
+ * this implementation, done for the mips.
+ * Just define the above conditional and provide
+ * the functions/macros defined below.
+ *
+ * extern boolean_t
+ * inst_branch(), returns true if the instruction might branch
+ * extern unsigned
+ * branch_taken(), return the address the instruction might
+ * branch to
+ * db_getreg_val(); return the value of a user register,
+ * as indicated in the hardware instruction
+ * encoding, e.g. 8 for r8
+ *
+ * next_instr_address(pc,bd) returns the address of the first
+ * instruction following the one at "pc",
+ * which is either in the taken path of
+ * the branch (bd==1) or not. This is
+ * for machines (mips) with branch delays.
+ *
+ * A single-step may involve at most 2 breakpoints -
+ * one for branch-not-taken and one for branch taken.
+ * If one of these addresses does not already have a breakpoint,
+ * we allocate a breakpoint and save it here.
+ * These breakpoints are deleted on return.
+ */
+db_breakpoint_t db_not_taken_bkpt = 0;
+db_breakpoint_t db_taken_bkpt = 0;
+
+void
+db_set_single_step(regs)
+ register db_regs_t *regs;
+{
+ db_addr_t pc = PC_REGS(regs);
+ register unsigned inst, brpc;
+
+ /*
+ * User was stopped at pc, e.g. the instruction
+ * at pc was not executed.
+ */
+ inst = db_get_value(pc, sizeof(int), FALSE);
+ if (inst_branch(inst) || inst_call(inst)) {
+ extern unsigned getreg_val();
+
+ brpc = branch_taken(inst, pc, getreg_val, regs);
+ if (brpc != pc) { /* self-branches are hopeless */
+ db_taken_bkpt = db_set_temp_breakpoint(brpc);
+ }
+ pc = next_instr_address(pc,1);
+ }
+ pc = next_instr_address(pc,0);
+ db_not_taken_bkpt = db_set_temp_breakpoint(pc);
+}
+
+void
+db_clear_single_step(regs)
+ db_regs_t *regs;
+{
+ register db_breakpoint_t bkpt;
+
+ if (db_taken_bkpt != 0) {
+ db_delete_temp_breakpoint(db_taken_bkpt);
+ db_taken_bkpt = 0;
+ }
+ if (db_not_taken_bkpt != 0) {
+ db_delete_temp_breakpoint(db_not_taken_bkpt);
+ db_not_taken_bkpt = 0;
+ }
+}
+
+#endif SOFTWARE_SSTEP
+
+extern int db_cmd_loop_done;
+
+/* single-step */
+/*ARGSUSED*/
+void
+db_single_step_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ int have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ boolean_t print = FALSE;
+
+ if (count == -1)
+ count = 1;
+
+ if (modif[0] == 'p')
+ print = TRUE;
+
+ db_run_mode = STEP_ONCE;
+ db_loop_count = count;
+ db_sstep_print = print;
+ db_inst_count = 0;
+ db_load_count = 0;
+ db_store_count = 0;
+
+ db_cmd_loop_done = 1;
+}
+
+/* trace and print until call/return */
+/*ARGSUSED*/
+void
+db_trace_until_call_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ int have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ boolean_t print = FALSE;
+
+ if (modif[0] == 'p')
+ print = TRUE;
+
+ db_run_mode = STEP_CALLT;
+ db_sstep_print = print;
+ db_inst_count = 0;
+ db_load_count = 0;
+ db_store_count = 0;
+
+ db_cmd_loop_done = 1;
+}
+
+/*ARGSUSED*/
+void
+db_trace_until_matching_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ int have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ boolean_t print = FALSE;
+
+ if (modif[0] == 'p')
+ print = TRUE;
+
+ db_run_mode = STEP_RETURN;
+ db_call_depth = 1;
+ db_sstep_print = print;
+ db_inst_count = 0;
+ db_load_count = 0;
+ db_store_count = 0;
+
+ db_cmd_loop_done = 1;
+}
+
+/* continue */
+/*ARGSUSED*/
+void
+db_continue_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ int have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ if (modif[0] == 'c')
+ db_run_mode = STEP_COUNT;
+ else
+ db_run_mode = STEP_CONTINUE;
+ db_inst_count = 0;
+ db_load_count = 0;
+ db_store_count = 0;
+
+ db_cmd_loop_done = 1;
+}
diff --git a/sys/ddb/db_sym.c b/sys/ddb/db_sym.c
new file mode 100644
index 0000000..af22a97
--- /dev/null
+++ b/sys/ddb/db_sym.c
@@ -0,0 +1,333 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_sym.c,v 1.3 1993/11/25 01:30:12 wollman Exp $
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+#include <ddb/db_sym.h>
+
+/*
+ * We import from the symbol-table dependent routines:
+ */
+extern db_sym_t X_db_lookup();
+extern db_sym_t X_db_search_symbol();
+extern boolean_t X_db_line_at_pc();
+extern void X_db_symbol_values();
+
+/*
+ * Multiple symbol tables
+ */
+#ifndef MAXNOSYMTABS
+#define MAXNOSYMTABS 3 /* mach, ux, emulator */
+#endif
+
+db_symtab_t db_symtabs[MAXNOSYMTABS] = {{0,},};
+int db_nsymtab = 0;
+
+db_symtab_t *db_last_symtab;
+
+db_sym_t db_lookup(); /* forward */
+
+/*
+ * Add symbol table, with given name, to list of symbol tables.
+ */
+void
+db_add_symbol_table(start, end, name, ref)
+ char *start;
+ char *end;
+ char *name;
+ char *ref;
+{
+ if (db_nsymtab >= MAXNOSYMTABS) {
+ printf ("No slots left for %s symbol table", name);
+ panic ("db_sym.c: db_add_symbol_table");
+ }
+
+ db_symtabs[db_nsymtab].start = start;
+ db_symtabs[db_nsymtab].end = end;
+ db_symtabs[db_nsymtab].name = name;
+ db_symtabs[db_nsymtab].private = ref;
+ db_nsymtab++;
+}
+
+/*
+ * db_qualify("vm_map", "ux") returns "unix:vm_map".
+ *
+ * Note: return value points to static data whose content is
+ * overwritten by each call... but in practice this seems okay.
+ */
+static char *
+db_qualify(sym, symtabname)
+ db_sym_t sym;
+ register char *symtabname;
+{
+ char *symname;
+ static char tmp[256];
+ register char *s;
+
+ db_symbol_values(sym, &symname, 0);
+ s = tmp;
+ while (*s++ = *symtabname++) {
+ }
+ s[-1] = ':';
+ while (*s++ = *symname++) {
+ }
+ return tmp;
+}
+
+
+boolean_t
+db_eqname(src, dst, c)
+ char *src;
+ char *dst;
+ char c;
+{
+ if (!strcmp(src, dst))
+ return (TRUE);
+ if (src[0] == c)
+ return (!strcmp(src+1,dst));
+ return (FALSE);
+}
+
+boolean_t
+db_value_of_name(name, valuep)
+ char *name;
+ db_expr_t *valuep;
+{
+ db_sym_t sym;
+
+ sym = db_lookup(name);
+ if (sym == DB_SYM_NULL)
+ return (FALSE);
+ db_symbol_values(sym, &name, valuep);
+ return (TRUE);
+}
+
+
+/*
+ * Lookup a symbol.
+ * If the symbol has a qualifier (e.g., ux:vm_map),
+ * then only the specified symbol table will be searched;
+ * otherwise, all symbol tables will be searched.
+ */
+db_sym_t
+db_lookup(symstr)
+ char *symstr;
+{
+ db_sym_t sp;
+ register int i;
+ int symtab_start = 0;
+ int symtab_end = db_nsymtab;
+ register char *cp;
+
+ /*
+ * Look for, remove, and remember any symbol table specifier.
+ */
+ for (cp = symstr; *cp; cp++) {
+ if (*cp == ':') {
+ *cp = '\0';
+ for (i = 0; i < db_nsymtab; i++) {
+ if (! strcmp(symstr, db_symtabs[i].name)) {
+ symtab_start = i;
+ symtab_end = i + 1;
+ break;
+ }
+ }
+ *cp = ':';
+ if (i == db_nsymtab) {
+ db_error("invalid symbol table name");
+ }
+ symstr = cp+1;
+ }
+ }
+
+ /*
+ * Look in the specified set of symbol tables.
+ * Return on first match.
+ */
+ for (i = symtab_start; i < symtab_end; i++) {
+ if (sp = X_db_lookup(&db_symtabs[i], symstr)) {
+ db_last_symtab = &db_symtabs[i];
+ return sp;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Does this symbol name appear in more than one symbol table?
+ * Used by db_symbol_values to decide whether to qualify a symbol.
+ */
+boolean_t db_qualify_ambiguous_names = FALSE;
+
+boolean_t
+db_symbol_is_ambiguous(sym)
+ db_sym_t sym;
+{
+ char *sym_name;
+ register int i;
+ register
+ boolean_t found_once = FALSE;
+
+ if (!db_qualify_ambiguous_names)
+ return FALSE;
+
+ db_symbol_values(sym, &sym_name, 0);
+ for (i = 0; i < db_nsymtab; i++) {
+ if (X_db_lookup(&db_symtabs[i], sym_name)) {
+ if (found_once)
+ return TRUE;
+ found_once = TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * Find the closest symbol to val, and return its name
+ * and the difference between val and the symbol found.
+ */
+db_sym_t
+db_search_symbol( val, strategy, offp)
+ register db_addr_t val;
+ db_strategy_t strategy;
+ db_expr_t *offp;
+{
+ register
+ unsigned int diff;
+ unsigned int newdiff;
+ register int i;
+ db_sym_t ret = DB_SYM_NULL, sym;
+
+ newdiff = diff = ~0;
+ db_last_symtab = 0;
+ for (i = 0; i < db_nsymtab; i++) {
+ sym = X_db_search_symbol(&db_symtabs[i], val, strategy, &newdiff);
+ if (newdiff < diff) {
+ db_last_symtab = &db_symtabs[i];
+ diff = newdiff;
+ ret = sym;
+ }
+ }
+ *offp = diff;
+ return ret;
+}
+
+/*
+ * Return name and value of a symbol
+ */
+void
+db_symbol_values(sym, namep, valuep)
+ db_sym_t sym;
+ char **namep;
+ db_expr_t *valuep;
+{
+ db_expr_t value;
+
+ if (sym == DB_SYM_NULL) {
+ *namep = 0;
+ return;
+ }
+
+ X_db_symbol_values(sym, namep, &value);
+
+ if (db_symbol_is_ambiguous(sym))
+ *namep = db_qualify(sym, db_last_symtab->name);
+ if (valuep)
+ *valuep = value;
+}
+
+
+/*
+ * Print a the closest symbol to value
+ *
+ * After matching the symbol according to the given strategy
+ * we print it in the name+offset format, provided the symbol's
+ * value is close enough (eg smaller than db_maxoff).
+ * We also attempt to print [filename:linenum] when applicable
+ * (eg for procedure names).
+ *
+ * If we could not find a reasonable name+offset representation,
+ * then we just print the value in hex. Small values might get
+ * bogus symbol associations, e.g. 3 might get some absolute
+ * value like _INCLUDE_VERSION or something, therefore we do
+ * not accept symbols whose value is zero (and use plain hex).
+ */
+
+unsigned int db_maxoff = 0x10000000;
+
+void
+db_printsym(off, strategy)
+ db_expr_t off;
+ db_strategy_t strategy;
+{
+ db_expr_t d;
+ char *filename;
+ char *name;
+ db_expr_t value;
+ int linenum;
+ db_sym_t cursym;
+
+ cursym = db_search_symbol(off, strategy, &d);
+ db_symbol_values(cursym, &name, &value);
+ if (name == 0 || d >= db_maxoff || value == 0) {
+ db_printf("%#n", off);
+ return;
+ }
+ db_printf("%s", name);
+ if (d)
+ db_printf("+%#r", d);
+ if (strategy == DB_STGY_PROC) {
+ if (db_line_at_pc(cursym, &filename, &linenum, off))
+ db_printf(" [%s:%d]", filename, linenum);
+ }
+}
+
+boolean_t
+db_line_at_pc( sym, filename, linenum, pc)
+ int sym;
+ int filename;
+ int linenum;
+ int pc;
+{
+ return X_db_line_at_pc( db_last_symtab, sym, filename, linenum, pc);
+}
+
+int
+db_sym_numargs(sym, nargp, argnames)
+ db_sym_t sym;
+ int *nargp;
+ char **argnames;
+{
+ return X_db_sym_numargs(db_last_symtab, sym, nargp, argnames);
+}
diff --git a/sys/ddb/db_sym.h b/sys/ddb/db_sym.h
new file mode 100644
index 0000000..a256b79
--- /dev/null
+++ b/sys/ddb/db_sym.h
@@ -0,0 +1,102 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_sym.h,v 1.2 1993/10/16 16:47:27 rgrimes Exp $
+ */
+
+#ifndef _DDB_DB_SYM_H_
+#define _DDB_DB_SYM_H_ 1
+
+/*
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 8/90
+ */
+
+/*
+ * This module can handle multiple symbol tables
+ */
+typedef struct {
+ char *name; /* symtab name */
+ char *start; /* symtab location */
+ char *end;
+ char *private; /* optional machdep pointer */
+} db_symtab_t;
+
+extern db_symtab_t *db_last_symtab; /* where last symbol was found */
+
+/*
+ * Symbol representation is specific to the symtab style:
+ * BSD compilers use dbx' nlist, other compilers might use
+ * a different one
+ */
+typedef char * db_sym_t; /* opaque handle on symbols */
+#define DB_SYM_NULL ((db_sym_t)0)
+
+/*
+ * Non-stripped symbol tables will have duplicates, for instance
+ * the same string could match a parameter name, a local var, a
+ * global var, etc.
+ * We are most concern with the following matches.
+ */
+typedef int db_strategy_t; /* search strategy */
+
+#define DB_STGY_ANY 0 /* anything goes */
+#define DB_STGY_XTRN 1 /* only external symbols */
+#define DB_STGY_PROC 2 /* only procedures */
+
+extern boolean_t db_qualify_ambiguous_names;
+ /* if TRUE, check across symbol tables
+ * for multiple occurrences of a name.
+ * Might slow down quite a bit */
+
+/*
+ * Functions exported by the symtable module
+ */
+extern void db_add_symbol_table();
+ /* extend the list of symbol tables */
+
+extern int db_value_of_name(/* char*, db_expr_t* */);
+ /* find symbol value given name */
+
+extern db_sym_t db_search_symbol(/* db_expr_t, db_strategy_t, int* */);
+ /* find symbol given value */
+
+extern void db_symbol_values(/* db_sym_t, char**, db_expr_t* */);
+ /* return name and value of symbol */
+
+#define db_find_sym_and_offset(val,namep,offp) \
+ db_symbol_values(db_search_symbol(val,DB_STGY_ANY,offp),namep,0)
+ /* find name&value given approx val */
+
+#define db_find_xtrn_sym_and_offset(val,namep,offp) \
+ db_symbol_values(db_search_symbol(val,DB_STGY_XTRN,offp),namep,0)
+ /* ditto, but no locals */
+
+extern int db_eqname(/* char*, char*, char */);
+ /* strcmp, modulo leading char */
+
+extern void db_printsym(/* db_expr_t, db_strategy_t */);
+ /* print closest symbol to a value */
+#endif /* _DDB_DB_SYM_H_ */
diff --git a/sys/ddb/db_trap.c b/sys/ddb/db_trap.c
new file mode 100644
index 0000000..abfa770
--- /dev/null
+++ b/sys/ddb/db_trap.c
@@ -0,0 +1,79 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_trap.c,v 1.2 1993/10/16 16:47:28 rgrimes Exp $
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+/*
+ * Trap entry point to kernel debugger.
+ */
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+#include <ddb/db_command.h>
+#include <ddb/db_break.h>
+
+extern void db_restart_at_pc();
+extern boolean_t db_stop_at_pc();
+
+extern int db_inst_count;
+extern int db_load_count;
+extern int db_store_count;
+
+void
+db_trap(type, code)
+ int type, code;
+{
+ boolean_t bkpt;
+ boolean_t watchpt;
+
+ bkpt = IS_BREAKPOINT_TRAP(type, code);
+ watchpt = IS_WATCHPOINT_TRAP(type, code);
+
+ if (db_stop_at_pc(&bkpt)) {
+ if (db_inst_count) {
+ db_printf("After %d instructions (%d loads, %d stores),\n",
+ db_inst_count, db_load_count, db_store_count);
+ }
+ if (bkpt)
+ db_printf("Breakpoint at\t");
+ else if (watchpt)
+ db_printf("Watchpoint at\t");
+ else
+ db_printf("Stopped at\t");
+ db_dot = PC_REGS(DDB_REGS);
+ db_print_loc_and_inst(db_dot);
+
+ db_command_loop();
+ }
+
+ db_restart_at_pc(watchpt);
+}
diff --git a/sys/ddb/db_variables.c b/sys/ddb/db_variables.c
new file mode 100644
index 0000000..96ef620
--- /dev/null
+++ b/sys/ddb/db_variables.c
@@ -0,0 +1,165 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_variables.c,v 1.2 1993/10/16 16:47:29 rgrimes Exp $
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+
+#include <ddb/db_lex.h>
+#include <ddb/db_variables.h>
+
+void db_read_variable(struct db_variable *, db_expr_t *);
+static void db_write_variable(struct db_variable *, db_expr_t *);
+
+struct db_variable db_vars[] = {
+ { "radix", &db_radix, FCN_NULL },
+ { "maxoff", (int *)&db_maxoff, FCN_NULL },
+ { "maxwidth", &db_max_width, FCN_NULL },
+ { "tabstops", &db_tab_stop_width, FCN_NULL },
+};
+struct db_variable *db_evars = db_vars + sizeof(db_vars)/sizeof(db_vars[0]);
+
+int
+db_find_variable(varp)
+ struct db_variable **varp;
+{
+ int t;
+ struct db_variable *vp;
+
+ t = db_read_token();
+ if (t == tIDENT) {
+ for (vp = db_vars; vp < db_evars; vp++) {
+ if (!strcmp(db_tok_string, vp->name)) {
+ *varp = vp;
+ return (1);
+ }
+ }
+ for (vp = db_regs; vp < db_eregs; vp++) {
+ if (!strcmp(db_tok_string, vp->name)) {
+ *varp = vp;
+ return (1);
+ }
+ }
+ }
+ db_error("Unknown variable\n");
+ return (0);
+}
+
+int
+db_get_variable(valuep)
+ db_expr_t *valuep;
+{
+ struct db_variable *vp;
+
+ if (!db_find_variable(&vp))
+ return (0);
+
+ db_read_variable(vp, valuep);
+
+ return (1);
+}
+
+int
+db_set_variable(value)
+ db_expr_t value;
+{
+ struct db_variable *vp;
+
+ if (!db_find_variable(&vp))
+ return (0);
+
+ db_write_variable(vp, &value);
+
+ return (1);
+}
+
+
+void
+db_read_variable(vp, valuep)
+ struct db_variable *vp;
+ db_expr_t *valuep;
+{
+ int (*func)() = vp->fcn;
+
+ if (func == FCN_NULL)
+ *valuep = *(vp->valuep);
+ else
+ (*func)(vp, valuep, DB_VAR_GET);
+}
+
+static void
+db_write_variable(vp, valuep)
+ struct db_variable *vp;
+ db_expr_t *valuep;
+{
+ int (*func)() = vp->fcn;
+
+ if (func == FCN_NULL)
+ *(vp->valuep) = *valuep;
+ else
+ (*func)(vp, valuep, DB_VAR_SET);
+}
+
+void
+db_set_cmd(db_expr_t dummy1, int dummy2, db_expr_t dummy3, char *dummy4)
+{
+ db_expr_t value;
+ int (*func)();
+ struct db_variable *vp;
+ int t;
+
+ t = db_read_token();
+ if (t != tDOLLAR) {
+ db_error("Unknown variable\n");
+ return;
+ }
+ if (!db_find_variable(&vp)) {
+ db_error("Unknown variable\n");
+ return;
+ }
+
+ t = db_read_token();
+ if (t != tEQ)
+ db_unread_token(t);
+
+ if (!db_expression(&value)) {
+ db_error("No value\n");
+ return;
+ }
+ if (db_read_token() != tEOL) {
+ db_error("?\n");
+ }
+
+ db_write_variable(vp, &value);
+}
diff --git a/sys/ddb/db_variables.h b/sys/ddb/db_variables.h
new file mode 100644
index 0000000..35e5a00
--- /dev/null
+++ b/sys/ddb/db_variables.h
@@ -0,0 +1,57 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_variables.h,v 1.2 1993/10/16 16:47:31 rgrimes Exp $
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+#ifndef _DB_VARIABLES_H_
+#define _DB_VARIABLES_H_
+
+/*
+ * Debugger variables.
+ */
+struct db_variable {
+ char *name; /* Name of variable */
+ int *valuep; /* value of variable */
+ /* function to call when reading/writing */
+ int (*fcn)(/* db_variable *vp, db_expr_t *valuep, int op */);
+#define DB_VAR_GET 0
+#define DB_VAR_SET 1
+};
+#define FCN_NULL ((int (*)())0)
+
+extern struct db_variable db_vars[]; /* debugger variables */
+extern struct db_variable *db_evars;
+extern struct db_variable db_regs[]; /* machine registers */
+extern struct db_variable *db_eregs;
+
+extern void db_read_variable(struct db_variable *, db_expr_t *);
+
+#endif /* _DB_VARIABLES_H_ */
diff --git a/sys/ddb/db_watch.c b/sys/ddb/db_watch.c
new file mode 100644
index 0000000..0322a18
--- /dev/null
+++ b/sys/ddb/db_watch.c
@@ -0,0 +1,270 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_watch.c,v 1.2 1993/10/16 16:47:32 rgrimes Exp $
+ */
+
+/*
+ * Author: Richard P. Draves, Carnegie Mellon University
+ * Date: 10/90
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+
+#include <vm/vm_map.h>
+#include <ddb/db_lex.h>
+#include <ddb/db_watch.h>
+#include <ddb/db_access.h>
+#include <ddb/db_sym.h>
+
+/*
+ * Watchpoints.
+ */
+
+extern boolean_t db_map_equal();
+extern boolean_t db_map_current();
+extern vm_map_t db_map_addr();
+
+boolean_t db_watchpoints_inserted = TRUE;
+
+#define NWATCHPOINTS 100
+struct db_watchpoint db_watch_table[NWATCHPOINTS];
+db_watchpoint_t db_next_free_watchpoint = &db_watch_table[0];
+db_watchpoint_t db_free_watchpoints = 0;
+db_watchpoint_t db_watchpoint_list = 0;
+
+db_watchpoint_t
+db_watchpoint_alloc()
+{
+ register db_watchpoint_t watch;
+
+ if ((watch = db_free_watchpoints) != 0) {
+ db_free_watchpoints = watch->link;
+ return (watch);
+ }
+ if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) {
+ db_printf("All watchpoints used.\n");
+ return (0);
+ }
+ watch = db_next_free_watchpoint;
+ db_next_free_watchpoint++;
+
+ return (watch);
+}
+
+void
+db_watchpoint_free(watch)
+ register db_watchpoint_t watch;
+{
+ watch->link = db_free_watchpoints;
+ db_free_watchpoints = watch;
+}
+
+void
+db_set_watchpoint(map, addr, size)
+ vm_map_t map;
+ db_addr_t addr;
+ vm_size_t size;
+{
+ register db_watchpoint_t watch;
+
+ if (map == NULL) {
+ db_printf("No map.\n");
+ return;
+ }
+
+ /*
+ * Should we do anything fancy with overlapping regions?
+ */
+
+ for (watch = db_watchpoint_list;
+ watch != 0;
+ watch = watch->link)
+ if (db_map_equal(watch->map, map) &&
+ (watch->loaddr == addr) &&
+ (watch->hiaddr == addr+size)) {
+ db_printf("Already set.\n");
+ return;
+ }
+
+ watch = db_watchpoint_alloc();
+ if (watch == 0) {
+ db_printf("Too many watchpoints.\n");
+ return;
+ }
+
+ watch->map = map;
+ watch->loaddr = addr;
+ watch->hiaddr = addr+size;
+
+ watch->link = db_watchpoint_list;
+ db_watchpoint_list = watch;
+
+ db_watchpoints_inserted = FALSE;
+}
+
+void
+db_delete_watchpoint(map, addr)
+ vm_map_t map;
+ db_addr_t addr;
+{
+ register db_watchpoint_t watch;
+ register db_watchpoint_t *prev;
+
+ for (prev = &db_watchpoint_list;
+ (watch = *prev) != 0;
+ prev = &watch->link)
+ if (db_map_equal(watch->map, map) &&
+ (watch->loaddr <= addr) &&
+ (addr < watch->hiaddr)) {
+ *prev = watch->link;
+ db_watchpoint_free(watch);
+ return;
+ }
+
+ db_printf("Not set.\n");
+}
+
+void
+db_list_watchpoints()
+{
+ register db_watchpoint_t watch;
+
+ if (db_watchpoint_list == 0) {
+ db_printf("No watchpoints set\n");
+ return;
+ }
+
+ db_printf(" Map Address Size\n");
+ for (watch = db_watchpoint_list;
+ watch != 0;
+ watch = watch->link)
+ db_printf("%s%8x %8x %x\n",
+ db_map_current(watch->map) ? "*" : " ",
+ watch->map, watch->loaddr,
+ watch->hiaddr - watch->loaddr);
+}
+
+/* Delete watchpoint */
+/*ARGSUSED*/
+void
+db_deletewatch_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ int have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ db_delete_watchpoint(db_map_addr(addr), addr);
+}
+
+/* Set watchpoint */
+/*ARGSUSED*/
+void
+db_watchpoint_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ int have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ vm_size_t size;
+ db_expr_t value;
+
+ if (db_expression(&value))
+ size = (vm_size_t) value;
+ else
+ size = 4;
+ db_skip_to_eol();
+
+ db_set_watchpoint(db_map_addr(addr), addr, size);
+}
+
+/* list watchpoints */
+void
+db_listwatch_cmd(db_expr_t dummy1, int dummy2, db_expr_t dummy3, char *dummmy4)
+{
+ db_list_watchpoints();
+}
+
+void
+db_set_watchpoints()
+{
+ register db_watchpoint_t watch;
+
+ if (!db_watchpoints_inserted) {
+ for (watch = db_watchpoint_list;
+ watch != 0;
+ watch = watch->link)
+ pmap_protect(watch->map->pmap,
+ trunc_page(watch->loaddr),
+ round_page(watch->hiaddr),
+ VM_PROT_READ);
+
+ db_watchpoints_inserted = TRUE;
+ }
+}
+
+void
+db_clear_watchpoints()
+{
+ db_watchpoints_inserted = FALSE;
+}
+
+boolean_t
+db_find_watchpoint(map, addr, regs)
+ vm_map_t map;
+ db_addr_t addr;
+ db_regs_t *regs;
+{
+ register db_watchpoint_t watch;
+ db_watchpoint_t found = 0;
+
+ for (watch = db_watchpoint_list;
+ watch != 0;
+ watch = watch->link)
+ if (db_map_equal(watch->map, map)) {
+ if ((watch->loaddr <= addr) &&
+ (addr < watch->hiaddr))
+ return (TRUE);
+ else if ((trunc_page(watch->loaddr) <= addr) &&
+ (addr < round_page(watch->hiaddr)))
+ found = watch;
+ }
+
+ /*
+ * We didn't hit exactly on a watchpoint, but we are
+ * in a protected region. We want to single-step
+ * and then re-protect.
+ */
+
+ if (found) {
+ db_watchpoints_inserted = FALSE;
+ db_single_step(regs);
+ }
+
+ return (FALSE);
+}
diff --git a/sys/ddb/db_watch.h b/sys/ddb/db_watch.h
new file mode 100644
index 0000000..f8932de
--- /dev/null
+++ b/sys/ddb/db_watch.h
@@ -0,0 +1,60 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id$
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 10/90
+ */
+
+#ifndef _DDB_DB_WATCH_
+#define _DDB_DB_WATCH_
+
+#include <vm/vm_map.h>
+#include <machine/db_machdep.h>
+
+/*
+ * Watchpoint.
+ */
+
+typedef struct db_watchpoint {
+ vm_map_t map; /* in this map */
+ db_addr_t loaddr; /* from this address */
+ db_addr_t hiaddr; /* to this address */
+ struct db_watchpoint *link; /* link in in-use or free chain */
+} *db_watchpoint_t;
+
+extern boolean_t db_find_watchpoint(/* vm_map_t map, db_addr_t addr,
+ db_regs_t *regs */);
+extern void db_set_watchpoints();
+extern void db_clear_watchpoints();
+
+extern void db_set_watchpoint(/* vm_map_t map, db_addr_t addr, vm_size_t size */);
+extern void db_delete_watchpoint(/* vm_map_t map, db_addr_t addr */);
+extern void db_list_watchpoints();
+
+#endif _DDB_DB_WATCH_
diff --git a/sys/ddb/db_write_cmd.c b/sys/ddb/db_write_cmd.c
new file mode 100644
index 0000000..6de58a6
--- /dev/null
+++ b/sys/ddb/db_write_cmd.c
@@ -0,0 +1,99 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_write_cmd.c,v 1.2 1993/10/16 16:47:35 rgrimes Exp $
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+
+#include <ddb/db_lex.h>
+#include <ddb/db_access.h>
+#include <ddb/db_command.h>
+#include <ddb/db_sym.h>
+
+/*
+ * Write to file.
+ */
+/*ARGSUSED*/
+void
+db_write_cmd(address, have_addr, count, modif)
+ db_expr_t address;
+ boolean_t have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ register
+ db_addr_t addr;
+ register
+ db_expr_t old_value;
+ db_expr_t new_value;
+ register int size;
+ boolean_t wrote_one = FALSE;
+
+ addr = (db_addr_t) address;
+
+ switch (modif[0]) {
+ case 'b':
+ size = 1;
+ break;
+ case 'h':
+ size = 2;
+ break;
+ case 'l':
+ case '\0':
+ size = 4;
+ break;
+ default:
+ db_error("Unknown size\n");
+ return;
+ }
+
+ while (db_expression(&new_value)) {
+ old_value = db_get_value(addr, size, FALSE);
+ db_printsym(addr, DB_STGY_ANY);
+ db_printf("\t\t%#8n\t=\t%#8n\n", old_value, new_value);
+ db_put_value(addr, size, new_value);
+ addr += size;
+
+ wrote_one = TRUE;
+ }
+
+ if (!wrote_one)
+ db_error("Nothing written.\n");
+
+ db_next = addr;
+ db_prev = addr - size;
+
+ db_skip_to_eol();
+}
+
diff --git a/sys/ddb/ddb.h b/sys/ddb/ddb.h
new file mode 100644
index 0000000..877436c
--- /dev/null
+++ b/sys/ddb/ddb.h
@@ -0,0 +1,110 @@
+/*-
+ * Copyright (c) 1993, Garrett A. Wollman.
+ * Copyright (c) 1993, University of Vermont and State Agricultural College.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+/*
+ * Necessary declarations for the `ddb' kernel debugger.
+ */
+
+#ifndef __h_ddb_ddb
+#define __h_ddb_ddb 1
+
+#include <machine/db_machdep.h> /* type definitions */
+#include <vm/vm.h>
+
+/*
+ * Global variables...
+ */
+extern char *esym;
+extern unsigned int db_maxoff;
+extern int db_inst_count;
+extern int db_load_count;
+extern int db_store_count;
+extern int db_radix;
+extern int db_max_width;
+extern int db_tab_stop_width;
+
+/*
+ * Functions...
+ */
+extern void
+db_read_bytes(vm_offset_t addr, register int size, register char *data);
+ /* machine-dependent */
+
+extern void
+db_write_bytes(vm_offset_t addr, register int size, register char *data);
+ /* machine-dependent */
+
+struct vm_map; /* forward declaration */
+
+extern boolean_t db_map_equal(struct vm_map *, struct vm_map *);
+extern boolean_t db_map_current(struct vm_map *);
+extern struct vm_map *db_map_addr(vm_offset_t);
+
+#define db_strcpy strcpy
+extern int db_expression (db_expr_t *valuep);
+
+typedef void db_cmd_fcn(db_expr_t, int, db_expr_t, char *);
+
+extern db_cmd_fcn db_listbreak_cmd, db_listwatch_cmd, db_show_regs;
+extern db_cmd_fcn db_print_cmd, db_examine_cmd, db_set_cmd, db_search_cmd;
+extern db_cmd_fcn db_write_cmd, db_delete_cmd, db_breakpoint_cmd;
+extern db_cmd_fcn db_deletewatch_cmd, db_watchpoint_cmd;
+extern db_cmd_fcn db_single_step_cmd, db_trace_until_call_cmd;
+extern db_cmd_fcn db_trace_until_matching_cmd, db_continue_cmd;
+extern db_cmd_fcn db_stack_trace_cmd;
+
+extern db_addr_t db_disasm(db_addr_t loc, boolean_t altfmt);
+ /* instruction disassembler */
+
+extern int db_value_of_name (char *name, db_expr_t *valuep);
+extern int db_get_variable (db_expr_t *valuep);
+extern void db_putchar (int c);
+extern void db_error (char *s);
+extern int db_readline (char *lstart, int lsize);
+extern void db_printf (const char *fmt, ...);
+extern void db_check_interrupt(void);
+extern void db_print_loc_and_inst (db_addr_t loc);
+
+extern void db_clear_watchpoints (void);
+extern void db_set_watchpoints (void);
+
+extern void db_restart_at_pc(boolean_t watchpt);
+extern boolean_t db_stop_at_pc(boolean_t *is_breakpoint);
+
+extern void db_skip_to_eol (void);
+extern void db_single_step (db_regs_t *regs);
+
+extern void db_trap (int type, int code);
+
+extern void kdbprinttrap(int, int);
+
+#endif /* __h_ddb_ddb */
diff --git a/sys/dev/ed/if_ed.c b/sys/dev/ed/if_ed.c
new file mode 100644
index 0000000..26e3ebd
--- /dev/null
+++ b/sys/dev/ed/if_ed.c
@@ -0,0 +1,2487 @@
+/*
+ * Device driver for National Semiconductor DS8390/WD83C690 based ethernet
+ * adapters. By David Greenman, 29-April-1993
+ *
+ * Copyright (C) 1993, David Greenman. This software may be used, modified,
+ * copied, distributed, and sold, in both source and binary form provided
+ * that the above copyright and these terms are retained. Under no
+ * circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ *
+ * Currently supports the Western Digital/SMC 8003 and 8013 series,
+ * the SMC Elite Ultra (8216), the 3Com 3c503, the NE1000 and NE2000,
+ * and a variety of similar clones.
+ *
+ */
+
+/*
+ * $Id: if_ed.c,v 1.36 1994/04/10 20:06:26 davidg Exp $
+ */
+
+#include "ed.h"
+#if NED > 0
+/* bpfilter included here in case it is needed in future net includes */
+#include "bpfilter.h"
+
+#include "param.h"
+#include "systm.h"
+#include "errno.h"
+#include "ioctl.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "syslog.h"
+
+#include "net/if.h"
+#include "net/if_dl.h"
+#include "net/if_types.h"
+
+#ifdef INET
+#include "netinet/in.h"
+#include "netinet/in_systm.h"
+#include "netinet/in_var.h"
+#include "netinet/ip.h"
+#include "netinet/if_ether.h"
+#endif
+
+#ifdef NS
+#include "netns/ns.h"
+#include "netns/ns_if.h"
+#endif
+
+#if NBPFILTER > 0
+#include "net/bpf.h"
+#include "net/bpfdesc.h"
+#endif
+
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/if_edreg.h"
+
+#include "i386/include/pio.h"
+
+/* For backwards compatibility */
+#ifndef IFF_ALTPHYS
+#define IFF_ALTPHYS IFF_LINK0
+#endif
+
+/*
+ * ed_softc: per line info and status
+ */
+struct ed_softc {
+ struct arpcom arpcom; /* ethernet common */
+
+ char *type_str; /* pointer to type string */
+ u_char vendor; /* interface vendor */
+ u_char type; /* interface type code */
+
+ u_short asic_addr; /* ASIC I/O bus address */
+ u_short nic_addr; /* NIC (DS8390) I/O bus address */
+
+/*
+ * The following 'proto' variable is part of a work-around for 8013EBT asics
+ * being write-only. It's sort of a prototype/shadow of the real thing.
+ */
+ u_char wd_laar_proto;
+ u_char isa16bit; /* width of access to card 0=8 or 1=16 */
+ int is790; /* set by the probe code if the card is 790 based */
+
+ caddr_t bpf; /* BPF "magic cookie" */
+ caddr_t mem_start; /* NIC memory start address */
+ caddr_t mem_end; /* NIC memory end address */
+ u_long mem_size; /* total NIC memory size */
+ caddr_t mem_ring; /* start of RX ring-buffer (in NIC mem) */
+
+ u_char mem_shared; /* NIC memory is shared with host */
+ u_char xmit_busy; /* transmitter is busy */
+ u_char txb_cnt; /* number of transmit buffers */
+ u_char txb_inuse; /* number of TX buffers currently in-use*/
+
+ u_char txb_new; /* pointer to where new buffer will be added */
+ u_char txb_next_tx; /* pointer to next buffer ready to xmit */
+ u_short txb_len[8]; /* buffered xmit buffer lengths */
+ u_char tx_page_start; /* first page of TX buffer area */
+ u_char rec_page_start; /* first page of RX ring-buffer */
+ u_char rec_page_stop; /* last page of RX ring-buffer */
+ u_char next_packet; /* pointer to next unread RX packet */
+} ed_softc[NED];
+
+int ed_attach(struct isa_device *);
+void ed_init(int);
+void edintr(int);
+int ed_ioctl(struct ifnet *, int, caddr_t);
+int ed_probe(struct isa_device *);
+void ed_start(struct ifnet *);
+void ed_reset(int);
+void ed_watchdog(int);
+
+static void ed_get_packet(struct ed_softc *, char *, int /*u_short*/);
+static void ed_stop(int);
+
+static inline void ed_rint();
+static inline void ed_xmit();
+static inline char *ed_ring_copy();
+
+void ed_pio_readmem(), ed_pio_writemem();
+u_short ed_pio_write_mbufs();
+
+extern int ether_output();
+
+struct trailer_header {
+ u_short ether_type;
+ u_short ether_residual;
+};
+
+struct isa_driver eddriver = {
+ ed_probe,
+ ed_attach,
+ "ed"
+};
+/*
+ * Interrupt conversion table for WD/SMC ASIC
+ * (IRQ* are defined in icu.h)
+ */
+static unsigned short ed_intr_mask[] = {
+ IRQ9,
+ IRQ3,
+ IRQ5,
+ IRQ7,
+ IRQ10,
+ IRQ11,
+ IRQ15,
+ IRQ4
+};
+
+/*
+ * Interrupt conversion table for 585/790 Combo
+ */
+static unsigned short ed_790_intr_mask[] = {
+ 0,
+ IRQ9,
+ IRQ3,
+ IRQ5,
+ IRQ7,
+ IRQ10,
+ IRQ11,
+ IRQ15
+};
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1518
+#define ETHER_ADDR_LEN 6
+#define ETHER_HDR_SIZE 14
+
+/*
+ * Determine if the device is present
+ *
+ * on entry:
+ * a pointer to an isa_device struct
+ * on exit:
+ * NULL if device not found
+ * or # of i/o addresses used (if found)
+ */
+int
+ed_probe(isa_dev)
+ struct isa_device *isa_dev;
+{
+ struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
+ int nports;
+
+ if (nports = ed_probe_WD80x3(isa_dev))
+ return (nports);
+
+ if (nports = ed_probe_3Com(isa_dev))
+ return (nports);
+
+ if (nports = ed_probe_Novell(isa_dev))
+ return (nports);
+
+ return(0);
+}
+
+/*
+ * Generic probe routine for testing for the existance of a DS8390.
+ * Must be called after the NIC has just been reset. This routine
+ * works by looking at certain register values that are gauranteed
+ * to be initialized a certain way after power-up or reset. Seems
+ * not to currently work on the 83C690.
+ *
+ * Specifically:
+ *
+ * Register reset bits set bits
+ * Command Register (CR) TXP, STA RD2, STP
+ * Interrupt Status (ISR) RST
+ * Interrupt Mask (IMR) All bits
+ * Data Control (DCR) LAS
+ * Transmit Config. (TCR) LB1, LB0
+ *
+ * We only look at the CR and ISR registers, however, because looking at
+ * the others would require changing register pages (which would be
+ * intrusive if this isn't an 8390).
+ *
+ * Return 1 if 8390 was found, 0 if not.
+ */
+
+int
+ed_probe_generic8390(sc)
+ struct ed_softc *sc;
+{
+ if ((inb(sc->nic_addr + ED_P0_CR) &
+ (ED_CR_RD2|ED_CR_TXP|ED_CR_STA|ED_CR_STP)) !=
+ (ED_CR_RD2|ED_CR_STP))
+ return (0);
+ if ((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) != ED_ISR_RST)
+ return (0);
+
+ return(1);
+}
+
+/*
+ * Probe and vendor-specific initialization routine for SMC/WD80x3 boards
+ */
+int
+ed_probe_WD80x3(isa_dev)
+ struct isa_device *isa_dev;
+{
+ struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
+ int i;
+ u_int memsize;
+ u_char iptr, isa16bit, sum;
+
+ sc->asic_addr = isa_dev->id_iobase;
+ sc->nic_addr = sc->asic_addr + ED_WD_NIC_OFFSET;
+ sc->is790 = 0;
+
+#ifdef TOSH_ETHER
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_POW);
+ DELAY(10000);
+#endif
+ /*
+ * Attempt to do a checksum over the station address PROM.
+ * If it fails, it's probably not a SMC/WD board. There
+ * is a problem with this, though: some clone WD boards
+ * don't pass the checksum test. Danpex boards for one.
+ */
+ for (sum = 0, i = 0; i < 8; ++i)
+ sum += inb(sc->asic_addr + ED_WD_PROM + i);
+
+ if (sum != ED_WD_ROM_CHECKSUM_TOTAL) {
+ /*
+ * Checksum is invalid. This often happens with cheap
+ * WD8003E clones. In this case, the checksum byte
+ * (the eighth byte) seems to always be zero.
+ */
+ if (inb(sc->asic_addr + ED_WD_CARD_ID) != ED_TYPE_WD8003E ||
+ inb(sc->asic_addr + ED_WD_PROM + 7) != 0)
+ return(0);
+ }
+
+ /* reset card to force it into a known state. */
+#ifdef TOSH_ETHER
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST | ED_WD_MSR_POW);
+#else
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST);
+#endif
+ DELAY(100);
+ outb(sc->asic_addr + ED_WD_MSR, inb(sc->asic_addr + ED_WD_MSR) & ~ED_WD_MSR_RST);
+ /* wait in the case this card is reading it's EEROM */
+ DELAY(5000);
+
+ sc->vendor = ED_VENDOR_WD_SMC;
+ sc->type = inb(sc->asic_addr + ED_WD_CARD_ID);
+
+ /*
+ * Set initial values for width/size.
+ */
+ memsize = 8192;
+ isa16bit = 0;
+ switch (sc->type) {
+ case ED_TYPE_WD8003S:
+ sc->type_str = "WD8003S";
+ break;
+ case ED_TYPE_WD8003E:
+ sc->type_str = "WD8003E";
+ break;
+ case ED_TYPE_WD8003EB:
+ sc->type_str = "WD8003EB";
+ break;
+ case ED_TYPE_WD8003W:
+ sc->type_str = "WD8003W";
+ break;
+ case ED_TYPE_WD8013EBT:
+ sc->type_str = "WD8013EBT";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_WD8013W:
+ sc->type_str = "WD8013W";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_WD8013EP: /* also WD8003EP */
+ if (inb(sc->asic_addr + ED_WD_ICR)
+ & ED_WD_ICR_16BIT) {
+ isa16bit = 1;
+ memsize = 16384;
+ sc->type_str = "WD8013EP";
+ } else {
+ sc->type_str = "WD8003EP";
+ }
+ break;
+ case ED_TYPE_WD8013WC:
+ sc->type_str = "WD8013WC";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_WD8013EBP:
+ sc->type_str = "WD8013EBP";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_WD8013EPC:
+ sc->type_str = "WD8013EPC";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_SMC8216C:
+ sc->type_str = "SMC8216/SMC8216C";
+ memsize = 16384;
+ isa16bit = 1;
+ sc->is790 = 1;
+ break;
+ case ED_TYPE_SMC8216T:
+ sc->type_str = "SMC8216T";
+ memsize = 16384;
+ isa16bit = 1;
+ sc->is790 = 1;
+ break;
+#ifdef TOSH_ETHER
+ case ED_TYPE_TOSHIBA1:
+ sc->type_str = "Toshiba1";
+ memsize = 32768;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_TOSHIBA4:
+ sc->type_str = "Toshiba4";
+ memsize = 32768;
+ isa16bit = 1;
+ break;
+#endif
+ default:
+ sc->type_str = "";
+ break;
+ }
+ /*
+ * Make some adjustments to initial values depending on what is
+ * found in the ICR.
+ */
+ if (isa16bit && (sc->type != ED_TYPE_WD8013EBT)
+#ifdef TOSH_ETHER
+ && (sc->type != ED_TYPE_TOSHIBA1) && (sc->type != ED_TYPE_TOSHIBA4)
+#endif
+ && ((inb(sc->asic_addr + ED_WD_ICR) & ED_WD_ICR_16BIT) == 0)) {
+ isa16bit = 0;
+ memsize = 8192;
+ }
+
+#if ED_DEBUG
+ printf("type = %x type_str=%s isa16bit=%d memsize=%d id_msize=%d\n",
+ sc->type,sc->type_str,isa16bit,memsize,isa_dev->id_msize);
+ for (i=0; i<8; i++)
+ printf("%x -> %x\n", i, inb(sc->asic_addr + i));
+#endif
+ /*
+ * Allow the user to override the autoconfiguration
+ */
+ if (isa_dev->id_msize)
+ memsize = isa_dev->id_msize;
+ /*
+ * (note that if the user specifies both of the following flags
+ * that '8bit' mode intentionally has precedence)
+ */
+ if (isa_dev->id_flags & ED_FLAGS_FORCE_16BIT_MODE)
+ isa16bit = 1;
+ if (isa_dev->id_flags & ED_FLAGS_FORCE_8BIT_MODE)
+ isa16bit = 0;
+
+ /*
+ * Check 83C584 interrupt configuration register if this board has one
+ * XXX - we could also check the IO address register. But why
+ * bother...if we get past this, it *has* to be correct.
+ */
+ if ((sc->type & ED_WD_SOFTCONFIG) && (!sc->is790)) {
+ /*
+ * Assemble together the encoded interrupt number.
+ */
+ iptr = (inb(isa_dev->id_iobase + ED_WD_ICR) & ED_WD_ICR_IR2) |
+ ((inb(isa_dev->id_iobase + ED_WD_IRR) &
+ (ED_WD_IRR_IR0 | ED_WD_IRR_IR1)) >> 5);
+ /*
+ * Translate it using translation table, and check for correctness.
+ */
+ if (ed_intr_mask[iptr] != isa_dev->id_irq) {
+ printf("ed%d: kernel configured irq %d doesn't match board configured irq %d\n",
+ isa_dev->id_unit, ffs(isa_dev->id_irq) - 1,
+ ffs(ed_intr_mask[iptr]) - 1);
+ return(0);
+ }
+ /*
+ * Enable the interrupt.
+ */
+ outb(isa_dev->id_iobase + ED_WD_IRR,
+ inb(isa_dev->id_iobase + ED_WD_IRR) | ED_WD_IRR_IEN);
+ }
+ if (sc->is790) {
+ outb(isa_dev->id_iobase + ED_WD790_HWR,
+ inb(isa_dev->id_iobase + ED_WD790_HWR) | ED_WD790_HWR_SWH);
+ iptr = (((inb(isa_dev->id_iobase + ED_WD790_GCR) & ED_WD790_GCR_IR2) >> 4) |
+ (inb(isa_dev->id_iobase + ED_WD790_GCR) &
+ (ED_WD790_GCR_IR1|ED_WD790_GCR_IR0)) >> 2);
+ outb(isa_dev->id_iobase + ED_WD790_HWR,
+ inb(isa_dev->id_iobase + ED_WD790_HWR) & ~ED_WD790_HWR_SWH);
+
+ if (ed_790_intr_mask[iptr] != isa_dev->id_irq) {
+ printf("ed%d: kernel configured irq %d doesn't match board configured irq %d %d\n",
+ isa_dev->id_unit, ffs(isa_dev->id_irq) - 1,
+ ffs(ed_790_intr_mask[iptr]) - 1, iptr);
+ return 0;
+ }
+ /*
+ * Enable interrupts.
+ */
+ outb(isa_dev->id_iobase + ED_WD790_ICR,
+ inb(isa_dev->id_iobase + ED_WD790_ICR) | ED_WD790_ICR_EIL);
+ }
+
+ sc->isa16bit = isa16bit;
+
+#ifdef notyet /* XXX - I'm not sure if PIO mode is even possible on WD/SMC boards */
+ /*
+ * The following allows the WD/SMC boards to be used in Programmed I/O
+ * mode - without mapping the NIC memory shared. ...Not the prefered
+ * way, but it might be the only way.
+ */
+ if (isa_dev->id_flags & ED_FLAGS_FORCE_PIO) {
+ sc->mem_shared = 0;
+ isa_dev->id_maddr = 0;
+ } else {
+ sc->mem_shared = 1;
+ }
+#else
+ sc->mem_shared = 1;
+#endif
+ isa_dev->id_msize = memsize;
+
+ sc->mem_start = (caddr_t)isa_dev->id_maddr;
+
+ /*
+ * allocate one xmit buffer if < 16k, two buffers otherwise
+ */
+ if ((memsize < 16384) || (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)) {
+ sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE);
+ sc->txb_cnt = 1;
+ sc->rec_page_start = ED_TXBUF_SIZE;
+ } else {
+ sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE * 2);
+ sc->txb_cnt = 2;
+ sc->rec_page_start = ED_TXBUF_SIZE * 2;
+ }
+ sc->mem_size = memsize;
+ sc->mem_end = sc->mem_start + memsize;
+ sc->rec_page_stop = memsize / ED_PAGE_SIZE;
+ sc->tx_page_start = ED_WD_PAGE_OFFSET;
+
+ /*
+ * Get station address from on-board ROM
+ */
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ sc->arpcom.ac_enaddr[i] = inb(sc->asic_addr + ED_WD_PROM + i);
+
+ if (sc->mem_shared) {
+ /*
+ * Set address and enable interface shared memory.
+ */
+ if(!sc->is790) {
+#ifdef TOSH_ETHER
+ outb(sc->asic_addr + ED_WD_MSR + 1, ((kvtop(sc->mem_start) >> 8) & 0xe0) | 4);
+ outb(sc->asic_addr + ED_WD_MSR + 2, ((kvtop(sc->mem_start) >> 16) & 0x0f));
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB | ED_WD_MSR_POW);
+
+#else
+ outb(sc->asic_addr + ED_WD_MSR, ((kvtop(sc->mem_start) >> 13) &
+ ED_WD_MSR_ADDR) | ED_WD_MSR_MENB);
+#endif
+ } else {
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB);
+ outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) | 0x80));
+ outb(sc->asic_addr + 0x0b, ((kvtop(sc->mem_start) >> 13) & 0x0f) |
+ ((kvtop(sc->mem_start) >> 11) & 0x40) |
+ (inb(sc->asic_addr + 0x0b) & 0xb0));
+ outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) & ~0x80));
+ }
+
+ /*
+ * Set upper address bits and 8/16 bit access to shared memory
+ */
+ if (isa16bit) {
+ if (sc->is790) {
+ sc->wd_laar_proto = inb(sc->asic_addr + ED_WD_LAAR);
+ outb(sc->asic_addr + ED_WD_LAAR, ED_WD_LAAR_M16EN);
+ (void) inb(0x84);
+ } else {
+ outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto =
+ ED_WD_LAAR_L16EN | ED_WD_LAAR_M16EN |
+ ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI)));
+ }
+ } else {
+ if ((sc->type & ED_WD_SOFTCONFIG) ||
+#ifdef TOSH_ETHER
+ (sc->type == ED_TYPE_TOSHIBA1) || (sc->type == ED_TYPE_TOSHIBA4) ||
+#endif
+ (sc->type == ED_TYPE_WD8013EBT) && (!sc->is790)) {
+ outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto =
+ ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI)));
+ }
+ }
+
+ /*
+ * Now zero memory and verify that it is clear
+ */
+ bzero(sc->mem_start, memsize);
+
+ for (i = 0; i < memsize; ++i)
+ if (sc->mem_start[i]) {
+ printf("ed%d: failed to clear shared memory at %x - check configuration\n",
+ isa_dev->id_unit, kvtop(sc->mem_start + i));
+
+ /*
+ * Disable 16 bit access to shared memory
+ */
+ if (isa16bit) {
+ outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &=
+ ~ED_WD_LAAR_M16EN));
+ (void) inb(0x84);
+ }
+
+ return(0);
+ }
+
+ /*
+ * Disable 16bit access to shared memory - we leave it disabled so
+ * that 1) machines reboot properly when the board is set
+ * 16 bit mode and there are conflicting 8bit devices/ROMS
+ * in the same 128k address space as this boards shared
+ * memory. and 2) so that other 8 bit devices with shared
+ * memory can be used in this 128k region, too.
+ */
+ if (isa16bit) {
+ outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &=
+ ~ED_WD_LAAR_M16EN));
+ (void) inb(0x84);
+ }
+ }
+
+ return (ED_WD_IO_PORTS);
+}
+
+/*
+ * Probe and vendor-specific initialization routine for 3Com 3c503 boards
+ */
+int
+ed_probe_3Com(isa_dev)
+ struct isa_device *isa_dev;
+{
+ struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
+ int i;
+ u_int memsize;
+ u_char isa16bit, sum;
+
+ sc->asic_addr = isa_dev->id_iobase + ED_3COM_ASIC_OFFSET;
+ sc->nic_addr = isa_dev->id_iobase + ED_3COM_NIC_OFFSET;
+
+ /*
+ * Verify that the kernel configured I/O address matches the board
+ * configured address
+ */
+ switch (inb(sc->asic_addr + ED_3COM_BCFR)) {
+ case ED_3COM_BCFR_300:
+ if (isa_dev->id_iobase != 0x300)
+ return(0);
+ break;
+ case ED_3COM_BCFR_310:
+ if (isa_dev->id_iobase != 0x310)
+ return(0);
+ break;
+ case ED_3COM_BCFR_330:
+ if (isa_dev->id_iobase != 0x330)
+ return(0);
+ break;
+ case ED_3COM_BCFR_350:
+ if (isa_dev->id_iobase != 0x350)
+ return(0);
+ break;
+ case ED_3COM_BCFR_250:
+ if (isa_dev->id_iobase != 0x250)
+ return(0);
+ break;
+ case ED_3COM_BCFR_280:
+ if (isa_dev->id_iobase != 0x280)
+ return(0);
+ break;
+ case ED_3COM_BCFR_2A0:
+ if (isa_dev->id_iobase != 0x2a0)
+ return(0);
+ break;
+ case ED_3COM_BCFR_2E0:
+ if (isa_dev->id_iobase != 0x2e0)
+ return(0);
+ break;
+ default:
+ return(0);
+ }
+
+ /*
+ * Verify that the kernel shared memory address matches the
+ * board configured address.
+ */
+ switch (inb(sc->asic_addr + ED_3COM_PCFR)) {
+ case ED_3COM_PCFR_DC000:
+ if (kvtop(isa_dev->id_maddr) != 0xdc000)
+ return(0);
+ break;
+ case ED_3COM_PCFR_D8000:
+ if (kvtop(isa_dev->id_maddr) != 0xd8000)
+ return(0);
+ break;
+ case ED_3COM_PCFR_CC000:
+ if (kvtop(isa_dev->id_maddr) != 0xcc000)
+ return(0);
+ break;
+ case ED_3COM_PCFR_C8000:
+ if (kvtop(isa_dev->id_maddr) != 0xc8000)
+ return(0);
+ break;
+ default:
+ return(0);
+ }
+
+
+ /*
+ * Reset NIC and ASIC. Enable on-board transceiver throughout reset
+ * sequence because it'll lock up if the cable isn't connected
+ * if we don't.
+ */
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_RST | ED_3COM_CR_XSEL);
+
+ /*
+ * Wait for a while, then un-reset it
+ */
+ DELAY(50);
+ /*
+ * The 3Com ASIC defaults to rather strange settings for the CR after
+ * a reset - it's important to set it again after the following
+ * outb (this is done when we map the PROM below).
+ */
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL);
+
+ /*
+ * Wait a bit for the NIC to recover from the reset
+ */
+ DELAY(5000);
+
+ sc->vendor = ED_VENDOR_3COM;
+ sc->type_str = "3c503";
+
+ sc->mem_shared = 1;
+
+ /*
+ * Hmmm...a 16bit 3Com board has 16k of memory, but only an 8k
+ * window to it.
+ */
+ memsize = 8192;
+
+ /*
+ * Get station address from on-board ROM
+ */
+ /*
+ * First, map ethernet address PROM over the top of where the NIC
+ * registers normally appear.
+ */
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_EALO | ED_3COM_CR_XSEL);
+
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ sc->arpcom.ac_enaddr[i] = inb(sc->nic_addr + i);
+
+ /*
+ * Unmap PROM - select NIC registers. The proper setting of the
+ * tranceiver is set in ed_init so that the attach code
+ * is given a chance to set the default based on a compile-time
+ * config option
+ */
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL);
+
+ /*
+ * Determine if this is an 8bit or 16bit board
+ */
+
+ /*
+ * select page 0 registers
+ */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP);
+
+ /*
+ * Attempt to clear WTS bit. If it doesn't clear, then this is a
+ * 16bit board.
+ */
+ outb(sc->nic_addr + ED_P0_DCR, 0);
+
+ /*
+ * select page 2 registers
+ */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_2|ED_CR_RD2|ED_CR_STP);
+
+ /*
+ * The 3c503 forces the WTS bit to a one if this is a 16bit board
+ */
+ if (inb(sc->nic_addr + ED_P2_DCR) & ED_DCR_WTS)
+ isa16bit = 1;
+ else
+ isa16bit = 0;
+
+ /*
+ * select page 0 registers
+ */
+ outb(sc->nic_addr + ED_P2_CR, ED_CR_RD2|ED_CR_STP);
+
+ sc->mem_start = (caddr_t)isa_dev->id_maddr;
+ sc->mem_size = memsize;
+ sc->mem_end = sc->mem_start + memsize;
+
+ /*
+ * We have an entire 8k window to put the transmit buffers on the
+ * 16bit boards. But since the 16bit 3c503's shared memory
+ * is only fast enough to overlap the loading of one full-size
+ * packet, trying to load more than 2 buffers can actually
+ * leave the transmitter idle during the load. So 2 seems
+ * the best value. (Although a mix of variable-sized packets
+ * might change this assumption. Nonetheless, we optimize for
+ * linear transfers of same-size packets.)
+ */
+ if (isa16bit) {
+ if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)
+ sc->txb_cnt = 1;
+ else
+ sc->txb_cnt = 2;
+
+ sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_16BIT;
+ sc->rec_page_start = ED_3COM_RX_PAGE_OFFSET_16BIT;
+ sc->rec_page_stop = memsize / ED_PAGE_SIZE +
+ ED_3COM_RX_PAGE_OFFSET_16BIT;
+ sc->mem_ring = sc->mem_start;
+ } else {
+ sc->txb_cnt = 1;
+ sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_8BIT;
+ sc->rec_page_start = ED_TXBUF_SIZE + ED_3COM_TX_PAGE_OFFSET_8BIT;
+ sc->rec_page_stop = memsize / ED_PAGE_SIZE +
+ ED_3COM_TX_PAGE_OFFSET_8BIT;
+ sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE);
+ }
+
+ sc->isa16bit = isa16bit;
+
+ /*
+ * Initialize GA page start/stop registers. Probably only needed
+ * if doing DMA, but what the hell.
+ */
+ outb(sc->asic_addr + ED_3COM_PSTR, sc->rec_page_start);
+ outb(sc->asic_addr + ED_3COM_PSPR, sc->rec_page_stop);
+
+ /*
+ * Set IRQ. 3c503 only allows a choice of irq 2-5.
+ */
+ switch (isa_dev->id_irq) {
+ case IRQ2:
+ outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ2);
+ break;
+ case IRQ3:
+ outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ3);
+ break;
+ case IRQ4:
+ outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ4);
+ break;
+ case IRQ5:
+ outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ5);
+ break;
+ default:
+ printf("ed%d: Invalid irq configuration (%d) must be 2-5 for 3c503\n",
+ isa_dev->id_unit, ffs(isa_dev->id_irq) - 1);
+ return(0);
+ }
+
+ /*
+ * Initialize GA configuration register. Set bank and enable shared mem.
+ */
+ outb(sc->asic_addr + ED_3COM_GACFR, ED_3COM_GACFR_RSEL |
+ ED_3COM_GACFR_MBS0);
+
+ /*
+ * Initialize "Vector Pointer" registers. These gawd-awful things
+ * are compared to 20 bits of the address on ISA, and if they
+ * match, the shared memory is disabled. We set them to
+ * 0xffff0...allegedly the reset vector.
+ */
+ outb(sc->asic_addr + ED_3COM_VPTR2, 0xff);
+ outb(sc->asic_addr + ED_3COM_VPTR1, 0xff);
+ outb(sc->asic_addr + ED_3COM_VPTR0, 0x00);
+
+ /*
+ * Zero memory and verify that it is clear
+ */
+ bzero(sc->mem_start, memsize);
+
+ for (i = 0; i < memsize; ++i)
+ if (sc->mem_start[i]) {
+ printf("ed%d: failed to clear shared memory at %x - check configuration\n",
+ isa_dev->id_unit, kvtop(sc->mem_start + i));
+ return(0);
+ }
+
+ isa_dev->id_msize = memsize;
+ return(ED_3COM_IO_PORTS);
+}
+
+/*
+ * Probe and vendor-specific initialization routine for NE1000/2000 boards
+ */
+int
+ed_probe_Novell(isa_dev)
+ struct isa_device *isa_dev;
+{
+ struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
+ u_int memsize, n;
+ u_char romdata[16], isa16bit = 0, tmp;
+ static char test_pattern[32] = "THIS is A memory TEST pattern";
+ char test_buffer[32];
+
+ sc->asic_addr = isa_dev->id_iobase + ED_NOVELL_ASIC_OFFSET;
+ sc->nic_addr = isa_dev->id_iobase + ED_NOVELL_NIC_OFFSET;
+
+ /* XXX - do Novell-specific probe here */
+
+ /* Reset the board */
+ tmp = inb(sc->asic_addr + ED_NOVELL_RESET);
+
+ /*
+ * I don't know if this is necessary; probably cruft leftover from
+ * Clarkson packet driver code. Doesn't do a thing on the boards
+ * I've tested. -DG [note that a outb(0x84, 0) seems to work
+ * here, and is non-invasive...but some boards don't seem to reset
+ * and I don't have complete documentation on what the 'right'
+ * thing to do is...so we do the invasive thing for now. Yuck.]
+ */
+ outb(sc->asic_addr + ED_NOVELL_RESET, tmp);
+ DELAY(5000);
+
+ /*
+ * This is needed because some NE clones apparently don't reset the
+ * NIC properly (or the NIC chip doesn't reset fully on power-up)
+ * XXX - this makes the probe invasive! ...Done against my better
+ * judgement. -DLG
+ */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP);
+
+ DELAY(5000);
+
+ /* Make sure that we really have an 8390 based board */
+ if (!ed_probe_generic8390(sc))
+ return(0);
+
+ sc->vendor = ED_VENDOR_NOVELL;
+ sc->mem_shared = 0;
+ isa_dev->id_maddr = 0;
+
+ /*
+ * Test the ability to read and write to the NIC memory. This has
+ * the side affect of determining if this is an NE1000 or an NE2000.
+ */
+
+ /*
+ * This prevents packets from being stored in the NIC memory when
+ * the readmem routine turns on the start bit in the CR.
+ */
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON);
+
+ /* Temporarily initialize DCR for byte operations */
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_LS);
+
+ outb(sc->nic_addr + ED_P0_PSTART, 8192 / ED_PAGE_SIZE);
+ outb(sc->nic_addr + ED_P0_PSTOP, 16384 / ED_PAGE_SIZE);
+
+ sc->isa16bit = 0;
+
+ /*
+ * Write a test pattern in byte mode. If this fails, then there
+ * probably isn't any memory at 8k - which likely means
+ * that the board is an NE2000.
+ */
+ ed_pio_writemem(sc, test_pattern, 8192, sizeof(test_pattern));
+ ed_pio_readmem(sc, 8192, test_buffer, sizeof(test_pattern));
+
+ if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) {
+ /* not an NE1000 - try NE2000 */
+
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_WTS|ED_DCR_FT1|ED_DCR_LS);
+ outb(sc->nic_addr + ED_P0_PSTART, 16384 / ED_PAGE_SIZE);
+ outb(sc->nic_addr + ED_P0_PSTOP, 32768 / ED_PAGE_SIZE);
+
+ sc->isa16bit = 1;
+ /*
+ * Write a test pattern in word mode. If this also fails, then
+ * we don't know what this board is.
+ */
+ ed_pio_writemem(sc, test_pattern, 16384, sizeof(test_pattern));
+ ed_pio_readmem(sc, 16384, test_buffer, sizeof(test_pattern));
+
+ if (bcmp(test_pattern, test_buffer, sizeof(test_pattern)))
+ return(0); /* not an NE2000 either */
+
+ sc->type = ED_TYPE_NE2000;
+ sc->type_str = "NE2000";
+ } else {
+ sc->type = ED_TYPE_NE1000;
+ sc->type_str = "NE1000";
+ }
+
+ /* 8k of memory plus an additional 8k if 16bit */
+ memsize = 8192 + sc->isa16bit * 8192;
+
+#if 0 /* probably not useful - NE boards only come two ways */
+ /* allow kernel config file overrides */
+ if (isa_dev->id_msize)
+ memsize = isa_dev->id_msize;
+#endif
+
+ sc->mem_size = memsize;
+
+ /* NIC memory doesn't start at zero on an NE board */
+ /* The start address is tied to the bus width */
+ sc->mem_start = (char *) 8192 + sc->isa16bit * 8192;
+ sc->mem_end = sc->mem_start + memsize;
+ sc->tx_page_start = memsize / ED_PAGE_SIZE;
+
+ /*
+ * Use one xmit buffer if < 16k, two buffers otherwise (if not told
+ * otherwise).
+ */
+ if ((memsize < 16384) || (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING))
+ sc->txb_cnt = 1;
+ else
+ sc->txb_cnt = 2;
+
+ sc->rec_page_start = sc->tx_page_start + sc->txb_cnt * ED_TXBUF_SIZE;
+ sc->rec_page_stop = sc->tx_page_start + memsize / ED_PAGE_SIZE;
+
+ sc->mem_ring = sc->mem_start + sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE;
+
+ ed_pio_readmem(sc, 0, romdata, 16);
+ for (n = 0; n < ETHER_ADDR_LEN; n++)
+ sc->arpcom.ac_enaddr[n] = romdata[n*(sc->isa16bit+1)];
+
+ /* clear any pending interrupts that might have occurred above */
+ outb(sc->nic_addr + ED_P0_ISR, 0xff);
+
+ return(ED_NOVELL_IO_PORTS);
+}
+
+/*
+ * Install interface into kernel networking data structures
+ */
+int
+ed_attach(isa_dev)
+ struct isa_device *isa_dev;
+{
+ struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+
+ /*
+ * Set interface to stopped condition (reset)
+ */
+ ed_stop(isa_dev->id_unit);
+
+ /*
+ * Initialize ifnet structure
+ */
+ ifp->if_unit = isa_dev->id_unit;
+ ifp->if_name = "ed" ;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_init = ed_init;
+ ifp->if_output = ether_output;
+ ifp->if_start = ed_start;
+ ifp->if_ioctl = ed_ioctl;
+ ifp->if_reset = ed_reset;
+ ifp->if_watchdog = ed_watchdog;
+
+ /*
+ * Set default state for ALTPHYS flag (used to disable the tranceiver
+ * for AUI operation), based on compile-time config option.
+ */
+ if (isa_dev->id_flags & ED_FLAGS_DISABLE_TRANCEIVER)
+ ifp->if_flags =
+ (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_ALTPHYS);
+ else
+ ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS);
+
+ /*
+ * Attach the interface
+ */
+ if_attach(ifp);
+
+ /*
+ * Search down the ifa address list looking for the AF_LINK type entry
+ */
+ ifa = ifp->if_addrlist;
+ while ((ifa != 0) && (ifa->ifa_addr != 0) &&
+ (ifa->ifa_addr->sa_family != AF_LINK))
+ ifa = ifa->ifa_next;
+ /*
+ * If we find an AF_LINK type entry we fill in the hardware address.
+ * This is useful for netstat(1) to keep track of which interface
+ * is which.
+ */
+ if ((ifa != 0) && (ifa->ifa_addr != 0)) {
+ /*
+ * Fill in the link-level address for this interface
+ */
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ sdl->sdl_type = IFT_ETHER;
+ sdl->sdl_alen = ETHER_ADDR_LEN;
+ sdl->sdl_slen = 0;
+ bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN);
+ }
+
+ /*
+ * Print additional info when attached
+ */
+ printf("ed%d: address %s, ", isa_dev->id_unit,
+ ether_sprintf(sc->arpcom.ac_enaddr));
+
+ if (sc->type_str && (*sc->type_str != 0))
+ printf("type %s ", sc->type_str);
+ else
+ printf("type unknown (0x%x) ", sc->type);
+
+ printf("%s ",sc->isa16bit ? "(16 bit)" : "(8 bit)");
+
+ printf("%s\n", ((sc->vendor == ED_VENDOR_3COM) &&
+ (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : "");
+
+ /*
+ * If BPF is in the kernel, call the attach for it
+ */
+#if NBPFILTER > 0
+ bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+ return 1;
+}
+
+/*
+ * Reset interface.
+ */
+void
+ed_reset(unit)
+ int unit;
+{
+ int s;
+
+ s = splimp();
+
+ /*
+ * Stop interface and re-initialize.
+ */
+ ed_stop(unit);
+ ed_init(unit);
+
+ (void) splx(s);
+}
+
+/*
+ * Take interface offline.
+ */
+void
+ed_stop(unit)
+ int unit;
+{
+ struct ed_softc *sc = &ed_softc[unit];
+ int n = 5000;
+
+ /*
+ * Stop everything on the interface, and select page 0 registers.
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_STP);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP);
+ }
+ /*
+ * Wait for interface to enter stopped state, but limit # of checks
+ * to 'n' (about 5ms). It shouldn't even take 5us on modern
+ * DS8390's, but just in case it's an old one.
+ */
+ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) == 0) && --n);
+
+}
+
+/*
+ * Device timeout/watchdog routine. Entered if the device neglects to
+ * generate an interrupt after a transmit has been started on it.
+ */
+void
+ed_watchdog(unit)
+ int unit;
+{
+ struct ed_softc *sc = &ed_softc[unit];
+
+ log(LOG_ERR, "ed%d: device timeout\n", unit);
+ ++sc->arpcom.ac_if.if_oerrors;
+
+ ed_reset(unit);
+}
+
+/*
+ * Initialize device.
+ */
+void
+ed_init(unit)
+ int unit;
+{
+ struct ed_softc *sc = &ed_softc[unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ int i, s;
+ u_char command;
+
+
+ /* address not known */
+ if (ifp->if_addrlist == (struct ifaddr *)0) return;
+
+ /*
+ * Initialize the NIC in the exact order outlined in the NS manual.
+ * This init procedure is "mandatory"...don't change what or when
+ * things happen.
+ */
+ s = splimp();
+
+ /* reset transmitter flags */
+ sc->xmit_busy = 0;
+ sc->arpcom.ac_if.if_timer = 0;
+
+ sc->txb_inuse = 0;
+ sc->txb_new = 0;
+ sc->txb_next_tx = 0;
+
+ /* This variable is used below - don't move this assignment */
+ sc->next_packet = sc->rec_page_start + 1;
+
+ /*
+ * Set interface for page 0, Remote DMA complete, Stopped
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_STP);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP);
+ }
+ if (sc->isa16bit) {
+ /*
+ * Set FIFO threshold to 8, No auto-init Remote DMA,
+ * byte order=80x86, word-wide DMA xfers,
+ */
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_WTS|ED_DCR_LS);
+ } else {
+ /*
+ * Same as above, but byte-wide DMA xfers
+ */
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_LS);
+ }
+
+ /*
+ * Clear Remote Byte Count Registers
+ */
+ outb(sc->nic_addr + ED_P0_RBCR0, 0);
+ outb(sc->nic_addr + ED_P0_RBCR1, 0);
+
+ /*
+ * Enable reception of broadcast packets
+ */
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB);
+
+ /*
+ * Place NIC in internal loopback mode
+ */
+ outb(sc->nic_addr + ED_P0_TCR, ED_TCR_LB0);
+
+ /*
+ * Initialize transmit/receive (ring-buffer) Page Start
+ */
+ outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start);
+ outb(sc->nic_addr + ED_P0_PSTART, sc->rec_page_start);
+ /* Set lower bits of byte addressable framing to 0 */
+ if (sc->is790)
+ outb(sc->nic_addr + 0x09, 0);
+
+ /*
+ * Initialize Receiver (ring-buffer) Page Stop and Boundry
+ */
+ outb(sc->nic_addr + ED_P0_PSTOP, sc->rec_page_stop);
+ outb(sc->nic_addr + ED_P0_BNRY, sc->rec_page_start);
+
+ /*
+ * Clear all interrupts. A '1' in each bit position clears the
+ * corresponding flag.
+ */
+ outb(sc->nic_addr + ED_P0_ISR, 0xff);
+
+ /*
+ * Enable the following interrupts: receive/transmit complete,
+ * receive/transmit error, and Receiver OverWrite.
+ *
+ * Counter overflow and Remote DMA complete are *not* enabled.
+ */
+ outb(sc->nic_addr + ED_P0_IMR,
+ ED_IMR_PRXE|ED_IMR_PTXE|ED_IMR_RXEE|ED_IMR_TXEE|ED_IMR_OVWE);
+
+ /*
+ * Program Command Register for page 1
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STP);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STP);
+ }
+ /*
+ * Copy out our station address
+ */
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ outb(sc->nic_addr + ED_P1_PAR0 + i, sc->arpcom.ac_enaddr[i]);
+
+#if NBPFILTER > 0
+ /*
+ * Initialize multicast address hashing registers to accept
+ * all multicasts (only used when in promiscuous mode)
+ */
+ for (i = 0; i < 8; ++i)
+ outb(sc->nic_addr + ED_P1_MAR0 + i, 0xff);
+#endif
+
+ /*
+ * Set Current Page pointer to next_packet (initialized above)
+ */
+ outb(sc->nic_addr + ED_P1_CURR, sc->next_packet);
+
+ /*
+ * Set Command Register for page 0, Remote DMA complete,
+ * and interface Start.
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P1_CR, ED_CR_STA);
+ } else {
+ outb(sc->nic_addr + ED_P1_CR, ED_CR_RD2|ED_CR_STA);
+ }
+ /*
+ * Take interface out of loopback
+ */
+ outb(sc->nic_addr + ED_P0_TCR, 0);
+
+ /*
+ * If this is a 3Com board, the tranceiver must be software enabled
+ * (there is no settable hardware default).
+ */
+ if (sc->vendor == ED_VENDOR_3COM) {
+ if (ifp->if_flags & IFF_ALTPHYS) {
+ outb(sc->asic_addr + ED_3COM_CR, 0);
+ } else {
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL);
+ }
+ }
+
+ /*
+ * Set 'running' flag, and clear output active flag.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ /*
+ * ...and attempt to start output
+ */
+ ed_start(ifp);
+
+ (void) splx(s);
+}
+
+/*
+ * This routine actually starts the transmission on the interface
+ */
+static inline void ed_xmit(ifp)
+ struct ifnet *ifp;
+{
+ struct ed_softc *sc = &ed_softc[ifp->if_unit];
+ unsigned short len;
+
+ len = sc->txb_len[sc->txb_next_tx];
+
+ /*
+ * Set NIC for page 0 register access
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_STA);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+ }
+ /*
+ * Set TX buffer start page
+ */
+ outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start +
+ sc->txb_next_tx * ED_TXBUF_SIZE);
+
+ /*
+ * Set TX length
+ */
+ outb(sc->nic_addr + ED_P0_TBCR0, len);
+ outb(sc->nic_addr + ED_P0_TBCR1, len >> 8);
+
+ /*
+ * Set page 0, Remote DMA complete, Transmit Packet, and *Start*
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_TXP | ED_CR_STA);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_TXP|ED_CR_STA);
+ }
+ sc->xmit_busy = 1;
+
+ /*
+ * Point to next transmit buffer slot and wrap if necessary.
+ */
+ sc->txb_next_tx++;
+ if (sc->txb_next_tx == sc->txb_cnt)
+ sc->txb_next_tx = 0;
+
+ /*
+ * Set a timer just in case we never hear from the board again
+ */
+ ifp->if_timer = 2;
+}
+
+/*
+ * Start output on interface.
+ * We make two assumptions here:
+ * 1) that the current priority is set to splimp _before_ this code
+ * is called *and* is returned to the appropriate priority after
+ * return
+ * 2) that the IFF_OACTIVE flag is checked before this code is called
+ * (i.e. that the output part of the interface is idle)
+ */
+void
+ed_start(ifp)
+ struct ifnet *ifp;
+{
+ struct ed_softc *sc = &ed_softc[ifp->if_unit];
+ struct mbuf *m0, *m;
+ caddr_t buffer;
+ int len;
+
+outloop:
+ /*
+ * First, see if there are buffered packets and an idle
+ * transmitter - should never happen at this point.
+ */
+ if (sc->txb_inuse && (sc->xmit_busy == 0)) {
+ printf("ed: packets buffers, but transmitter idle\n");
+ ed_xmit(ifp);
+ }
+
+ /*
+ * See if there is room to put another packet in the buffer.
+ */
+ if (sc->txb_inuse == sc->txb_cnt) {
+ /*
+ * No room. Indicate this to the outside world
+ * and exit.
+ */
+ ifp->if_flags |= IFF_OACTIVE;
+ return;
+ }
+
+ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m);
+ if (m == 0) {
+ /*
+ * We are using the !OACTIVE flag to indicate to the outside
+ * world that we can accept an additional packet rather than
+ * that the transmitter is _actually_ active. Indeed, the
+ * transmitter may be active, but if we haven't filled all
+ * the buffers with data then we still want to accept more.
+ */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ return;
+ }
+
+ /*
+ * Copy the mbuf chain into the transmit buffer
+ */
+
+ m0 = m;
+
+ /* txb_new points to next open buffer slot */
+ buffer = sc->mem_start + (sc->txb_new * ED_TXBUF_SIZE * ED_PAGE_SIZE);
+
+ if (sc->mem_shared) {
+ /*
+ * Special case setup for 16 bit boards...
+ */
+ if (sc->isa16bit) {
+ switch (sc->vendor) {
+ /*
+ * For 16bit 3Com boards (which have 16k of memory),
+ * we have the xmit buffers in a different page
+ * of memory ('page 0') - so change pages.
+ */
+ case ED_VENDOR_3COM:
+ outb(sc->asic_addr + ED_3COM_GACFR,
+ ED_3COM_GACFR_RSEL);
+ break;
+ /*
+ * Enable 16bit access to shared memory on WD/SMC boards
+ * Don't update wd_laar_proto because we want to restore the
+ * previous state (because an arp reply in the input code
+ * may cause a call-back to ed_start)
+ * XXX - the call-back to 'start' is a bug, IMHO.
+ */
+ case ED_VENDOR_WD_SMC: {
+ outb(sc->asic_addr + ED_WD_LAAR,
+ (sc->wd_laar_proto | ED_WD_LAAR_M16EN));
+ (void) inb(0x84);
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB);
+ (void) inb(0x84);
+ }
+ (void) inb(0x84);
+ break;
+ }
+ }
+ }
+
+ for (len = 0; m != 0; m = m->m_next) {
+ bcopy(mtod(m, caddr_t), buffer, m->m_len);
+ buffer += m->m_len;
+ len += m->m_len;
+ }
+
+ /*
+ * Restore previous shared memory access
+ */
+ if (sc->isa16bit) {
+ switch (sc->vendor) {
+ case ED_VENDOR_3COM:
+ outb(sc->asic_addr + ED_3COM_GACFR,
+ ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0);
+ break;
+ case ED_VENDOR_WD_SMC: {
+ outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto);
+ (void) inb(0x84);
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR, 0x00);
+ (void) inb(0x84);
+ }
+ break;
+ }
+ }
+ }
+ } else {
+ len = ed_pio_write_mbufs(sc, m, buffer);
+ }
+
+ sc->txb_len[sc->txb_new] = max(len, ETHER_MIN_LEN);
+
+ sc->txb_inuse++;
+
+ /*
+ * Point to next buffer slot and wrap if necessary.
+ */
+ sc->txb_new++;
+ if (sc->txb_new == sc->txb_cnt)
+ sc->txb_new = 0;
+
+ if (sc->xmit_busy == 0)
+ ed_xmit(ifp);
+ /*
+ * If there is BPF support in the configuration, tap off here.
+ * The following has support for converting trailer packets
+ * back to normal.
+ * XXX - support for trailer packets in BPF should be moved into
+ * the bpf code proper to avoid code duplication in all of
+ * the drivers.
+ */
+#if NBPFILTER > 0
+ if (sc->bpf) {
+ u_short etype;
+ int off, datasize, resid;
+ struct ether_header *eh;
+ struct trailer_header trailer_header;
+ char ether_packet[ETHER_MAX_LEN];
+ char *ep;
+
+ ep = ether_packet;
+
+ /*
+ * We handle trailers below:
+ * Copy ether header first, then residual data,
+ * then data. Put all this in a temporary buffer
+ * 'ether_packet' and send off to bpf. Since the
+ * system has generated this packet, we assume
+ * that all of the offsets in the packet are
+ * correct; if they're not, the system will almost
+ * certainly crash in m_copydata.
+ * We make no assumptions about how the data is
+ * arranged in the mbuf chain (i.e. how much
+ * data is in each mbuf, if mbuf clusters are
+ * used, etc.), which is why we use m_copydata
+ * to get the ether header rather than assume
+ * that this is located in the first mbuf.
+ */
+ /* copy ether header */
+ m_copydata(m0, 0, sizeof(struct ether_header), ep);
+ eh = (struct ether_header *) ep;
+ ep += sizeof(struct ether_header);
+ etype = ntohs(eh->ether_type);
+ if (etype >= ETHERTYPE_TRAIL &&
+ etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
+ datasize = ((etype - ETHERTYPE_TRAIL) << 9);
+ off = datasize + sizeof(struct ether_header);
+
+ /* copy trailer_header into a data structure */
+ m_copydata(m0, off, sizeof(struct trailer_header),
+ (caddr_t)&trailer_header.ether_type);
+
+ /* copy residual data */
+ m_copydata(m0, off+sizeof(struct trailer_header),
+ resid = ntohs(trailer_header.ether_residual) -
+ sizeof(struct trailer_header), ep);
+ ep += resid;
+
+ /* copy data */
+ m_copydata(m0, sizeof(struct ether_header),
+ datasize, ep);
+ ep += datasize;
+
+ /* restore original ether packet type */
+ eh->ether_type = trailer_header.ether_type;
+
+ bpf_tap(sc->bpf, ether_packet, ep - ether_packet);
+ } else
+ bpf_mtap(sc->bpf, m0);
+ }
+#endif
+
+ m_freem(m0);
+
+ /*
+ * Loop back to the top to possibly buffer more packets
+ */
+ goto outloop;
+}
+
+/*
+ * Ethernet interface receiver interrupt.
+ */
+static inline void
+ed_rint(unit)
+ int unit;
+{
+ register struct ed_softc *sc = &ed_softc[unit];
+ u_char boundry, current;
+ u_short len;
+ struct ed_ring packet_hdr;
+ char *packet_ptr;
+
+ /*
+ * Set NIC to page 1 registers to get 'current' pointer
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STA);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STA);
+ }
+ /*
+ * 'sc->next_packet' is the logical beginning of the ring-buffer - i.e.
+ * it points to where new data has been buffered. The 'CURR'
+ * (current) register points to the logical end of the ring-buffer
+ * - i.e. it points to where additional new data will be added.
+ * We loop here until the logical beginning equals the logical
+ * end (or in other words, until the ring-buffer is empty).
+ */
+ while (sc->next_packet != inb(sc->nic_addr + ED_P1_CURR)) {
+
+ /* get pointer to this buffer's header structure */
+ packet_ptr = sc->mem_ring +
+ (sc->next_packet - sc->rec_page_start) * ED_PAGE_SIZE;
+
+ /*
+ * The byte count includes the FCS - Frame Check Sequence (a
+ * 32 bit CRC).
+ */
+ if (sc->mem_shared)
+ packet_hdr = *(struct ed_ring *)packet_ptr;
+ else
+ ed_pio_readmem(sc, packet_ptr, (char *) &packet_hdr,
+ sizeof(packet_hdr));
+ len = packet_hdr.count;
+ if ((len >= ETHER_MIN_LEN) && (len <= ETHER_MAX_LEN)) {
+ /*
+ * Go get packet. len - 4 removes CRC from length.
+ */
+ ed_get_packet(sc, packet_ptr + 4, len - 4);
+ ++sc->arpcom.ac_if.if_ipackets;
+ } else {
+ /*
+ * Really BAD...probably indicates that the ring pointers
+ * are corrupted. Also seen on early rev chips under
+ * high load - the byte order of the length gets switched.
+ */
+ log(LOG_ERR,
+ "ed%d: NIC memory corrupt - invalid packet length %d\n",
+ unit, len);
+ ++sc->arpcom.ac_if.if_ierrors;
+ ed_reset(unit);
+ return;
+ }
+
+ /*
+ * Update next packet pointer
+ */
+ sc->next_packet = packet_hdr.next_packet;
+
+ /*
+ * Update NIC boundry pointer - being careful to keep it
+ * one buffer behind. (as recommended by NS databook)
+ */
+ boundry = sc->next_packet - 1;
+ if (boundry < sc->rec_page_start)
+ boundry = sc->rec_page_stop - 1;
+
+ /*
+ * Set NIC to page 0 registers to update boundry register
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_STA);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+ }
+ outb(sc->nic_addr + ED_P0_BNRY, boundry);
+
+ /*
+ * Set NIC to page 1 registers before looping to top (prepare to
+ * get 'CURR' current pointer)
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STA);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STA);
+ }
+ }
+}
+
+/*
+ * Ethernet interface interrupt processor
+ */
+void
+edintr(unit)
+ int unit;
+{
+ struct ed_softc *sc = &ed_softc[unit];
+ u_char isr;
+
+ /*
+ * Set NIC to page 0 registers
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_STA);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+ }
+ /*
+ * loop until there are no more new interrupts
+ */
+ while (isr = inb(sc->nic_addr + ED_P0_ISR)) {
+
+ /*
+ * reset all the bits that we are 'acknowledging'
+ * by writing a '1' to each bit position that was set
+ * (writing a '1' *clears* the bit)
+ */
+ outb(sc->nic_addr + ED_P0_ISR, isr);
+
+ /*
+ * Handle transmitter interrupts. Handle these first
+ * because the receiver will reset the board under
+ * some conditions.
+ */
+ if (isr & (ED_ISR_PTX|ED_ISR_TXE)) {
+ u_char collisions = inb(sc->nic_addr + ED_P0_NCR) & 0x0f;
+
+ /*
+ * Check for transmit error. If a TX completed with an
+ * error, we end up throwing the packet away. Really
+ * the only error that is possible is excessive
+ * collisions, and in this case it is best to allow the
+ * automatic mechanisms of TCP to backoff the flow. Of
+ * course, with UDP we're screwed, but this is expected
+ * when a network is heavily loaded.
+ */
+ (void) inb(sc->nic_addr + ED_P0_TSR);
+ if (isr & ED_ISR_TXE) {
+
+ /*
+ * Excessive collisions (16)
+ */
+ if ((inb(sc->nic_addr + ED_P0_TSR) & ED_TSR_ABT)
+ && (collisions == 0)) {
+ /*
+ * When collisions total 16, the
+ * P0_NCR will indicate 0, and the
+ * TSR_ABT is set.
+ */
+ collisions = 16;
+ }
+
+ /*
+ * update output errors counter
+ */
+ ++sc->arpcom.ac_if.if_oerrors;
+ } else {
+ /*
+ * Update total number of successfully
+ * transmitted packets.
+ */
+ ++sc->arpcom.ac_if.if_opackets;
+ }
+
+ /*
+ * reset tx busy and output active flags
+ */
+ sc->xmit_busy = 0;
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+
+ /*
+ * clear watchdog timer
+ */
+ sc->arpcom.ac_if.if_timer = 0;
+
+ /*
+ * Add in total number of collisions on last
+ * transmission.
+ */
+ sc->arpcom.ac_if.if_collisions += collisions;
+
+ /*
+ * Decrement buffer in-use count if not zero (can only
+ * be zero if a transmitter interrupt occured while
+ * not actually transmitting).
+ * If data is ready to transmit, start it transmitting,
+ * otherwise defer until after handling receiver
+ */
+ if (sc->txb_inuse && --sc->txb_inuse)
+ ed_xmit(&sc->arpcom.ac_if);
+ }
+
+ /*
+ * Handle receiver interrupts
+ */
+ if (isr & (ED_ISR_PRX|ED_ISR_RXE|ED_ISR_OVW)) {
+ /*
+ * Overwrite warning. In order to make sure that a lockup
+ * of the local DMA hasn't occurred, we reset and
+ * re-init the NIC. The NSC manual suggests only a
+ * partial reset/re-init is necessary - but some
+ * chips seem to want more. The DMA lockup has been
+ * seen only with early rev chips - Methinks this
+ * bug was fixed in later revs. -DG
+ */
+ if (isr & ED_ISR_OVW) {
+ ++sc->arpcom.ac_if.if_ierrors;
+#ifdef DIAGNOSTIC
+ log(LOG_WARNING,
+ "ed%d: warning - receiver ring buffer overrun\n",
+ unit);
+#endif
+ /*
+ * Stop/reset/re-init NIC
+ */
+ ed_reset(unit);
+ } else {
+
+ /*
+ * Receiver Error. One or more of: CRC error, frame
+ * alignment error FIFO overrun, or missed packet.
+ */
+ if (isr & ED_ISR_RXE) {
+ ++sc->arpcom.ac_if.if_ierrors;
+#ifdef ED_DEBUG
+ printf("ed%d: receive error %x\n", unit,
+ inb(sc->nic_addr + ED_P0_RSR));
+#endif
+ }
+
+ /*
+ * Go get the packet(s)
+ * XXX - Doing this on an error is dubious
+ * because there shouldn't be any data to
+ * get (we've configured the interface to
+ * not accept packets with errors).
+ */
+
+ /*
+ * Enable 16bit access to shared memory first
+ * on WD/SMC boards.
+ */
+ if (sc->isa16bit &&
+ (sc->vendor == ED_VENDOR_WD_SMC)) {
+
+ outb(sc->asic_addr + ED_WD_LAAR,
+ (sc->wd_laar_proto |=
+ ED_WD_LAAR_M16EN));
+ (void) inb(0x84);
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR,
+ ED_WD_MSR_MENB);
+ (void) inb(0x84);
+ }
+ }
+
+ ed_rint (unit);
+
+ /* disable 16bit access */
+ if (sc->isa16bit &&
+ (sc->vendor == ED_VENDOR_WD_SMC)) {
+
+ outb(sc->asic_addr + ED_WD_LAAR,
+ (sc->wd_laar_proto &=
+ ~ED_WD_LAAR_M16EN));
+ (void) inb(0x84);
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR, 0x00);
+ (void) inb(0x84);
+ }
+ }
+ }
+ }
+
+ /*
+ * If it looks like the transmitter can take more data,
+ * attempt to start output on the interface.
+ * This is done after handling the receiver to
+ * give the receiver priority.
+ */
+ if ((sc->arpcom.ac_if.if_flags & IFF_OACTIVE) == 0)
+ ed_start(&sc->arpcom.ac_if);
+
+ /*
+ * return NIC CR to standard state: page 0, remote DMA complete,
+ * start (toggling the TXP bit off, even if was just set
+ * in the transmit routine, is *okay* - it is 'edge'
+ * triggered from low to high)
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_STA);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+ }
+ /*
+ * If the Network Talley Counters overflow, read them to
+ * reset them. It appears that old 8390's won't
+ * clear the ISR flag otherwise - resulting in an
+ * infinite loop.
+ */
+ if (isr & ED_ISR_CNT) {
+ (void) inb(sc->nic_addr + ED_P0_CNTR0);
+ (void) inb(sc->nic_addr + ED_P0_CNTR1);
+ (void) inb(sc->nic_addr + ED_P0_CNTR2);
+ }
+ }
+}
+
+/*
+ * Process an ioctl request. This code needs some work - it looks
+ * pretty ugly.
+ */
+int
+ed_ioctl(ifp, command, data)
+ register struct ifnet *ifp;
+ int command;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *)data;
+ struct ed_softc *sc = &ed_softc[ifp->if_unit];
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s, error = 0;
+
+ s = splimp();
+
+ switch (command) {
+
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ ed_init(ifp->if_unit); /* before arpwhohas */
+ /*
+ * See if another station has *our* IP address.
+ * i.e.: There is an address conflict! If a
+ * conflict exists, a message is sent to the
+ * console.
+ */
+ ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
+ break;
+#endif
+#ifdef NS
+ /*
+ * XXX - This code is probably wrong
+ */
+ case AF_NS:
+ {
+ register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+ if (ns_nullhost(*ina))
+ ina->x_host =
+ *(union ns_host *)(sc->arpcom.ac_enaddr);
+ else {
+ /*
+ *
+ */
+ bcopy((caddr_t)ina->x_host.c_host,
+ (caddr_t)sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr));
+ }
+ /*
+ * Set new address
+ */
+ ed_init(ifp->if_unit);
+ break;
+ }
+#endif
+ default:
+ ed_init(ifp->if_unit);
+ break;
+ }
+ break;
+
+ case SIOCGIFADDR:
+ {
+ struct sockaddr *sa;
+ sa = (struct sockaddr *)&ifr->ifr_data;
+ bcopy((caddr_t)sc->arpcom.ac_enaddr,
+ (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
+ }
+ break;
+
+ case SIOCSIFFLAGS:
+ /*
+ * If interface is marked down and it is running, then stop it
+ */
+ if (((ifp->if_flags & IFF_UP) == 0) &&
+ (ifp->if_flags & IFF_RUNNING)) {
+ ed_stop(ifp->if_unit);
+ ifp->if_flags &= ~IFF_RUNNING;
+ } else {
+ /*
+ * If interface is marked up and it is stopped, then start it
+ */
+ if ((ifp->if_flags & IFF_UP) &&
+ ((ifp->if_flags & IFF_RUNNING) == 0))
+ ed_init(ifp->if_unit);
+ }
+#if NBPFILTER > 0
+ if (ifp->if_flags & IFF_PROMISC) {
+ /*
+ * Set promiscuous mode on interface.
+ * XXX - for multicasts to work, we would need to
+ * write 1's in all bits of multicast
+ * hashing array. For now we assume that
+ * this was done in ed_init().
+ */
+ outb(sc->nic_addr + ED_P0_RCR,
+ ED_RCR_PRO|ED_RCR_AM|ED_RCR_AB);
+ } else {
+ /*
+ * XXX - for multicasts to work, we would need to
+ * rewrite the multicast hashing array with the
+ * proper hash (would have been destroyed above).
+ */
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB);
+ }
+#endif
+ /*
+ * An unfortunate hack to provide the (required) software control
+ * of the tranceiver for 3Com boards. The ALTPHYS flag disables
+ * the tranceiver if set.
+ */
+ if (sc->vendor == ED_VENDOR_3COM) {
+ if (ifp->if_flags & IFF_ALTPHYS) {
+ outb(sc->asic_addr + ED_3COM_CR, 0);
+ } else {
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL);
+ }
+ }
+
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ (void) splx(s);
+ return (error);
+}
+
+/*
+ * Macro to calculate a new address within shared memory when given an offset
+ * from an address, taking into account ring-wrap.
+ */
+#define ringoffset(sc, start, off, type) \
+ ((type)( ((caddr_t)(start)+(off) >= (sc)->mem_end) ? \
+ (((caddr_t)(start)+(off))) - (sc)->mem_end \
+ + (sc)->mem_ring: \
+ ((caddr_t)(start)+(off)) ))
+
+/*
+ * Retreive packet from shared memory and send to the next level up via
+ * ether_input(). If there is a BPF listener, give a copy to BPF, too.
+ */
+static void
+ed_get_packet(sc, buf, len)
+ struct ed_softc *sc;
+ char *buf;
+ u_short len;
+{
+ struct ether_header *eh;
+ struct mbuf *m, *head = 0, *ed_ring_to_mbuf();
+ u_short off;
+ int resid;
+ u_short etype;
+ struct trailer_header trailer_header;
+
+ /* Allocate a header mbuf */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ goto bad;
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ m->m_pkthdr.len = len;
+ m->m_len = 0;
+ head = m;
+
+ /* The following sillines is to make NFS happy */
+#define EROUND ((sizeof(struct ether_header) + 3) & ~3)
+#define EOFF (EROUND - sizeof(struct ether_header))
+
+ /*
+ * The following assumes there is room for
+ * the ether header in the header mbuf
+ */
+ head->m_data += EOFF;
+ eh = mtod(head, struct ether_header *);
+
+ if (sc->mem_shared)
+ bcopy(buf, mtod(head, caddr_t), sizeof(struct ether_header));
+ else
+ ed_pio_readmem(sc, buf, mtod(head, caddr_t),
+ sizeof(struct ether_header));
+ buf += sizeof(struct ether_header);
+ head->m_len += sizeof(struct ether_header);
+ len -= sizeof(struct ether_header);
+
+ etype = ntohs((u_short)eh->ether_type);
+
+ /*
+ * Deal with trailer protocol:
+ * If trailer protocol, calculate the datasize as 'off',
+ * which is also the offset to the trailer header.
+ * Set resid to the amount of packet data following the
+ * trailer header.
+ * Finally, copy residual data into mbuf chain.
+ */
+ if (etype >= ETHERTYPE_TRAIL &&
+ etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
+
+ off = (etype - ETHERTYPE_TRAIL) << 9;
+ if ((off + sizeof(struct trailer_header)) > len)
+ goto bad; /* insanity */
+
+ /*
+ * If we have shared memory, we can get info directly from the
+ * stored packet, otherwise we must get a local copy
+ * of the trailer header using PIO.
+ */
+ if (sc->mem_shared) {
+ eh->ether_type = *ringoffset(sc, buf, off, u_short *);
+ resid = ntohs(*ringoffset(sc, buf, off+2, u_short *));
+ } else {
+ struct trailer_header trailer_header;
+ ed_pio_readmem(sc,
+ ringoffset(sc, buf, off, caddr_t),
+ (char *) &trailer_header,
+ sizeof(trailer_header));
+ eh->ether_type = trailer_header.ether_type;
+ resid = trailer_header.ether_residual;
+ }
+
+ if ((off + resid) > len) goto bad; /* insanity */
+
+ resid -= sizeof(struct trailer_header);
+ if (resid < 0) goto bad; /* insanity */
+
+ m = ed_ring_to_mbuf(sc, ringoffset(sc, buf, off+4, char *),
+ head, resid);
+ if (m == 0) goto bad;
+
+ len = off;
+ head->m_pkthdr.len -= 4; /* subtract trailer header */
+ }
+
+ /*
+ * Pull packet off interface. Or if this was a trailer packet,
+ * the data portion is appended.
+ */
+ m = ed_ring_to_mbuf(sc, buf, m, len);
+ if (m == 0) goto bad;
+
+#if NBPFILTER > 0
+ /*
+ * Check if there's a BPF listener on this interface.
+ * If so, hand off the raw packet to bpf.
+ */
+ if (sc->bpf) {
+ bpf_mtap(sc->bpf, head);
+
+ /*
+ * Note that the interface cannot be in promiscuous mode if
+ * there are no BPF listeners. And if we are in promiscuous
+ * mode, we have to check if this packet is really ours.
+ *
+ * XXX This test does not support multicasts.
+ */
+ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) &&
+ bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
+ sizeof(eh->ether_dhost)) != 0 &&
+ bcmp(eh->ether_dhost, etherbroadcastaddr,
+ sizeof(eh->ether_dhost)) != 0) {
+
+ m_freem(head);
+ return;
+ }
+ }
+#endif
+
+ /*
+ * Fix up data start offset in mbuf to point past ether header
+ */
+ m_adj(head, sizeof(struct ether_header));
+
+ /*
+ * silly ether_input routine needs 'type' in host byte order
+ */
+ eh->ether_type = ntohs(eh->ether_type);
+
+ ether_input(&sc->arpcom.ac_if, eh, head);
+ return;
+
+bad: if (head)
+ m_freem(head);
+ return;
+}
+
+/*
+ * Supporting routines
+ */
+
+/*
+ * Given a NIC memory source address and a host memory destination
+ * address, copy 'amount' from NIC to host using Programmed I/O.
+ * The 'amount' is rounded up to a word - okay as long as mbufs
+ * are word sized.
+ * This routine is currently Novell-specific.
+ */
+void
+ed_pio_readmem(sc,src,dst,amount)
+ struct ed_softc *sc;
+ unsigned short src;
+ unsigned char *dst;
+ unsigned short amount;
+{
+ unsigned short tmp_amount;
+
+ /* select page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+
+ /* round up to a word */
+ tmp_amount = amount;
+ if (amount & 1) ++amount;
+
+ /* set up DMA byte count */
+ outb(sc->nic_addr + ED_P0_RBCR0, amount);
+ outb(sc->nic_addr + ED_P0_RBCR1, amount>>8);
+
+ /* set up source address in NIC mem */
+ outb(sc->nic_addr + ED_P0_RSAR0, src);
+ outb(sc->nic_addr + ED_P0_RSAR1, src>>8);
+
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD0 | ED_CR_STA);
+
+ if (sc->isa16bit) {
+ insw(sc->asic_addr + ED_NOVELL_DATA, dst, amount/2);
+ } else
+ insb(sc->asic_addr + ED_NOVELL_DATA, dst, amount);
+
+}
+
+/*
+ * Stripped down routine for writing a linear buffer to NIC memory.
+ * Only used in the probe routine to test the memory. 'len' must
+ * be even.
+ */
+void
+ed_pio_writemem(sc,src,dst,len)
+ struct ed_softc *sc;
+ char *src;
+ unsigned short dst;
+ unsigned short len;
+{
+ int maxwait=100; /* about 120us */
+
+ /* select page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+
+ /* reset remote DMA complete flag */
+ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC);
+
+ /* set up DMA byte count */
+ outb(sc->nic_addr + ED_P0_RBCR0, len);
+ outb(sc->nic_addr + ED_P0_RBCR1, len>>8);
+
+ /* set up destination address in NIC mem */
+ outb(sc->nic_addr + ED_P0_RSAR0, dst);
+ outb(sc->nic_addr + ED_P0_RSAR1, dst>>8);
+
+ /* set remote DMA write */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA);
+
+ if (sc->isa16bit)
+ outsw(sc->asic_addr + ED_NOVELL_DATA, src, len/2);
+ else
+ outsb(sc->asic_addr + ED_NOVELL_DATA, src, len);
+ /*
+ * Wait for remote DMA complete. This is necessary because on the
+ * transmit side, data is handled internally by the NIC in bursts
+ * and we can't start another remote DMA until this one completes.
+ * Not waiting causes really bad things to happen - like the NIC
+ * irrecoverably jamming the ISA bus.
+ */
+ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait);
+}
+
+/*
+ * Write an mbuf chain to the destination NIC memory address using
+ * programmed I/O.
+ */
+u_short
+ed_pio_write_mbufs(sc,m,dst)
+ struct ed_softc *sc;
+ struct mbuf *m;
+ unsigned short dst;
+{
+ unsigned short len, mb_offset;
+ struct mbuf *mp;
+ unsigned char residual[2];
+ int maxwait=100; /* about 120us */
+
+ /* First, count up the total number of bytes to copy */
+ for (len = 0, mp = m; mp; mp = mp->m_next)
+ len += mp->m_len;
+
+ /* select page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+
+ /* reset remote DMA complete flag */
+ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC);
+
+ /* set up DMA byte count */
+ outb(sc->nic_addr + ED_P0_RBCR0, len);
+ outb(sc->nic_addr + ED_P0_RBCR1, len>>8);
+
+ /* set up destination address in NIC mem */
+ outb(sc->nic_addr + ED_P0_RSAR0, dst);
+ outb(sc->nic_addr + ED_P0_RSAR1, dst>>8);
+
+ /* set remote DMA write */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA);
+
+ mb_offset = 0;
+ /*
+ * Transfer the mbuf chain to the NIC memory.
+ * The following code isn't too pretty. The problem is that we can only
+ * transfer words to the board, and if an mbuf has an odd number
+ * of bytes in it, this is a problem. It's not a simple matter of
+ * just removing a byte from the next mbuf (adjusting data++ and
+ * len--) because this will hose-over the mbuf chain which might
+ * be needed later for BPF. Instead, we maintain an offset
+ * (mb_offset) which let's us skip over the first byte in the
+ * following mbuf.
+ */
+ while (m) {
+ if (m->m_len - mb_offset) {
+ if (sc->isa16bit) {
+ if ((m->m_len - mb_offset) > 1)
+ outsw(sc->asic_addr + ED_NOVELL_DATA,
+ mtod(m, caddr_t) + mb_offset,
+ (m->m_len - mb_offset) / 2);
+
+ /*
+ * if odd number of bytes, get the odd byte from
+ * the next mbuf with data
+ */
+ if ((m->m_len - mb_offset) & 1) {
+ /* first the last byte in current mbuf */
+ residual[0] = *(mtod(m, caddr_t) +
+ m->m_len - 1);
+
+ /* advance past any empty mbufs */
+ while (m->m_next && (m->m_next->m_len == 0))
+ m = m->m_next;
+
+ if (m->m_next) {
+ /* remove first byte in next mbuf */
+ residual[1] = *(mtod(m->m_next, caddr_t));
+ mb_offset = 1;
+ }
+
+ outw(sc->asic_addr + ED_NOVELL_DATA,
+ *((unsigned short *) residual));
+ } else
+ mb_offset = 0;
+ } else
+ outsb(sc->asic_addr + ED_NOVELL_DATA, m->m_data, m->m_len);
+
+ }
+ m = m->m_next;
+ }
+
+ /*
+ * Wait for remote DMA complete. This is necessary because on the
+ * transmit side, data is handled internally by the NIC in bursts
+ * and we can't start another remote DMA until this one completes.
+ * Not waiting causes really bad things to happen - like the NIC
+ * irrecoverably jamming the ISA bus.
+ */
+ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait);
+
+ if (!maxwait) {
+ log(LOG_WARNING, "ed%d: remote transmit DMA failed to complete\n",
+ sc->arpcom.ac_if.if_unit);
+ ed_reset(sc->arpcom.ac_if.if_unit);
+ }
+
+ return(len);
+}
+
+/*
+ * Given a source and destination address, copy 'amount' of a packet from
+ * the ring buffer into a linear destination buffer. Takes into account
+ * ring-wrap.
+ */
+static inline char *
+ed_ring_copy(sc,src,dst,amount)
+ struct ed_softc *sc;
+ char *src;
+ char *dst;
+ u_short amount;
+{
+ u_short tmp_amount;
+
+ /* does copy wrap to lower addr in ring buffer? */
+ if (src + amount > sc->mem_end) {
+ tmp_amount = sc->mem_end - src;
+
+ /* copy amount up to end of NIC memory */
+ if (sc->mem_shared)
+ bcopy(src,dst,tmp_amount);
+ else
+ ed_pio_readmem(sc,src,dst,tmp_amount);
+
+ amount -= tmp_amount;
+ src = sc->mem_ring;
+ dst += tmp_amount;
+ }
+
+ if (sc->mem_shared)
+ bcopy(src, dst, amount);
+ else
+ ed_pio_readmem(sc, src, dst, amount);
+
+ return(src + amount);
+}
+
+/*
+ * Copy data from receive buffer to end of mbuf chain
+ * allocate additional mbufs as needed. return pointer
+ * to last mbuf in chain.
+ * sc = ed info (softc)
+ * src = pointer in ed ring buffer
+ * dst = pointer to last mbuf in mbuf chain to copy to
+ * amount = amount of data to copy
+ */
+struct mbuf *
+ed_ring_to_mbuf(sc,src,dst,total_len)
+ struct ed_softc *sc;
+ char *src;
+ struct mbuf *dst;
+ u_short total_len;
+{
+ register struct mbuf *m = dst;
+
+ while (total_len) {
+ register u_short amount = min(total_len, M_TRAILINGSPACE(m));
+
+ if (amount == 0) { /* no more data in this mbuf, alloc another */
+ /*
+ * If there is enough data for an mbuf cluster, attempt
+ * to allocate one of those, otherwise, a regular
+ * mbuf will do.
+ * Note that a regular mbuf is always required, even if
+ * we get a cluster - getting a cluster does not
+ * allocate any mbufs, and one is needed to assign
+ * the cluster to. The mbuf that has a cluster
+ * extension can not be used to contain data - only
+ * the cluster can contain data.
+ */
+ dst = m;
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ return (0);
+
+ if (total_len >= MINCLSIZE)
+ MCLGET(m, M_DONTWAIT);
+
+ m->m_len = 0;
+ dst->m_next = m;
+ amount = min(total_len, M_TRAILINGSPACE(m));
+ }
+
+ src = ed_ring_copy(sc, src, mtod(m, caddr_t) + m->m_len, amount);
+
+ m->m_len += amount;
+ total_len -= amount;
+
+ }
+ return (m);
+}
+#endif
diff --git a/sys/dev/ed/if_edreg.h b/sys/dev/ed/if_edreg.h
new file mode 100644
index 0000000..f75e261
--- /dev/null
+++ b/sys/dev/ed/if_edreg.h
@@ -0,0 +1,962 @@
+/*
+ * National Semiconductor DS8390 NIC register definitions
+ *
+ * $Id: if_edreg.h,v 1.13 1994/02/02 14:05:58 davidg Exp $
+ *
+ * Modification history
+ *
+ * Revision 2.2 1993/11/29 16:33:39 davidg
+ * From Thomas Sandford <t.d.g.sandford@comp.brad.ac.uk>
+ * Add support for the 8013W board type
+ *
+ * Revision 2.1 1993/11/22 10:52:33 davidg
+ * patch to add support for SMC8216 (Elite-Ultra) boards
+ * from Glen H. Lowe
+ *
+ * Revision 2.0 93/09/29 00:37:15 davidg
+ * changed double buffering flag to multi buffering
+ * made changes/additions for 3c503 multi-buffering
+ * ...companion to Rev. 2.0 of 'ed' driver.
+ *
+ * Revision 1.1 93/06/23 03:01:07 davidg
+ * Initial revision
+ *
+ */
+
+/*
+ * Page 0 register offsets
+ */
+#define ED_P0_CR 0x00 /* Command Register */
+
+#define ED_P0_CLDA0 0x01 /* Current Local DMA Addr low (read) */
+#define ED_P0_PSTART 0x01 /* Page Start register (write) */
+
+#define ED_P0_CLDA1 0x02 /* Current Local DMA Addr high (read) */
+#define ED_P0_PSTOP 0x02 /* Page Stop register (write) */
+
+#define ED_P0_BNRY 0x03 /* Boundary Pointer */
+
+#define ED_P0_TSR 0x04 /* Transmit Status Register (read) */
+#define ED_P0_TPSR 0x04 /* Transmit Page Start (write) */
+
+#define ED_P0_NCR 0x05 /* Number of Collisions Reg (read) */
+#define ED_P0_TBCR0 0x05 /* Transmit Byte count, low (write) */
+
+#define ED_P0_FIFO 0x06 /* FIFO register (read) */
+#define ED_P0_TBCR1 0x06 /* Transmit Byte count, high (write) */
+
+#define ED_P0_ISR 0x07 /* Interrupt Status Register */
+
+#define ED_P0_CRDA0 0x08 /* Current Remote DMA Addr low (read) */
+#define ED_P0_RSAR0 0x08 /* Remote Start Address low (write) */
+
+#define ED_P0_CRDA1 0x09 /* Current Remote DMA Addr high (read) */
+#define ED_P0_RSAR1 0x09 /* Remote Start Address high (write) */
+
+#define ED_P0_RBCR0 0x0a /* Remote Byte Count low (write) */
+
+#define ED_P0_RBCR1 0x0b /* Remote Byte Count high (write) */
+
+#define ED_P0_RSR 0x0c /* Receive Status (read) */
+#define ED_P0_RCR 0x0c /* Receive Configuration Reg (write) */
+
+#define ED_P0_CNTR0 0x0d /* frame alignment error counter (read) */
+#define ED_P0_TCR 0x0d /* Transmit Configuration Reg (write) */
+
+#define ED_P0_CNTR1 0x0e /* CRC error counter (read) */
+#define ED_P0_DCR 0x0e /* Data Configuration Reg (write) */
+
+#define ED_P0_CNTR2 0x0f /* missed packet counter (read) */
+#define ED_P0_IMR 0x0f /* Interrupt Mask Register (write) */
+
+/*
+ * Page 1 register offsets
+ */
+#define ED_P1_CR 0x00 /* Command Register */
+#define ED_P1_PAR0 0x01 /* Physical Address Register 0 */
+#define ED_P1_PAR1 0x02 /* Physical Address Register 1 */
+#define ED_P1_PAR2 0x03 /* Physical Address Register 2 */
+#define ED_P1_PAR3 0x04 /* Physical Address Register 3 */
+#define ED_P1_PAR4 0x05 /* Physical Address Register 4 */
+#define ED_P1_PAR5 0x06 /* Physical Address Register 5 */
+#define ED_P1_CURR 0x07 /* Current RX ring-buffer page */
+#define ED_P1_MAR0 0x08 /* Multicast Address Register 0 */
+#define ED_P1_MAR1 0x09 /* Multicast Address Register 1 */
+#define ED_P1_MAR2 0x0a /* Multicast Address Register 2 */
+#define ED_P1_MAR3 0x0b /* Multicast Address Register 3 */
+#define ED_P1_MAR4 0x0c /* Multicast Address Register 4 */
+#define ED_P1_MAR5 0x0d /* Multicast Address Register 5 */
+#define ED_P1_MAR6 0x0e /* Multicast Address Register 6 */
+#define ED_P1_MAR7 0x0f /* Multicast Address Register 7 */
+
+/*
+ * Page 2 register offsets
+ */
+#define ED_P2_CR 0x00 /* Command Register */
+#define ED_P2_PSTART 0x01 /* Page Start (read) */
+#define ED_P2_CLDA0 0x01 /* Current Local DMA Addr 0 (write) */
+#define ED_P2_PSTOP 0x02 /* Page Stop (read) */
+#define ED_P2_CLDA1 0x02 /* Current Local DMA Addr 1 (write) */
+#define ED_P2_RNPP 0x03 /* Remote Next Packet Pointer */
+#define ED_P2_TPSR 0x04 /* Transmit Page Start (read) */
+#define ED_P2_LNPP 0x05 /* Local Next Packet Pointer */
+#define ED_P2_ACU 0x06 /* Address Counter Upper */
+#define ED_P2_ACL 0x07 /* Address Counter Lower */
+#define ED_P2_RCR 0x0c /* Receive Configuration Register (read) */
+#define ED_P2_TCR 0x0d /* Transmit Configuration Register (read) */
+#define ED_P2_DCR 0x0e /* Data Configuration Register (read) */
+#define ED_P2_IMR 0x0f /* Interrupt Mask Register (read) */
+
+/*
+ * Command Register (CR) definitions
+ */
+
+/*
+ * STP: SToP. Software reset command. Takes the controller offline. No
+ * packets will be received or transmitted. Any reception or
+ * transmission in progress will continue to completion before
+ * entering reset state. To exit this state, the STP bit must
+ * reset and the STA bit must be set. The software reset has
+ * executed only when indicated by the RST bit in the ISR being
+ * set.
+ */
+#define ED_CR_STP 0x01
+
+/*
+ * STA: STArt. This bit is used to activate the NIC after either power-up,
+ * or when the NIC has been put in reset mode by software command
+ * or error.
+ */
+#define ED_CR_STA 0x02
+
+/*
+ * TXP: Transmit Packet. This bit must be set to indicate transmission of
+ * a packet. TXP is internally reset either after the transmission is
+ * completed or aborted. This bit should be set only after the Transmit
+ * Byte Count and Transmit Page Start register have been programmed.
+ */
+#define ED_CR_TXP 0x04
+
+/*
+ * RD0, RD1, RD2: Remote DMA Command. These three bits control the operation
+ * of the remote DMA channel. RD2 can be set to abort any remote DMA
+ * command in progress. The Remote Byte Count registers should be cleared
+ * when a remote DMA has been aborted. The Remote Start Addresses are not
+ * restored to the starting address if the remote DMA is aborted.
+ *
+ * RD2 RD1 RD0 function
+ * 0 0 0 not allowed
+ * 0 0 1 remote read
+ * 0 1 0 remote write
+ * 0 1 1 send packet
+ * 1 X X abort
+ */
+#define ED_CR_RD0 0x08
+#define ED_CR_RD1 0x10
+#define ED_CR_RD2 0x20
+
+/*
+ * PS0, PS1: Page Select. The two bits select which register set or 'page' to
+ * access.
+ *
+ * PS1 PS0 page
+ * 0 0 0
+ * 0 1 1
+ * 1 0 2
+ * 1 1 reserved
+ */
+#define ED_CR_PS0 0x40
+#define ED_CR_PS1 0x80
+/* bit encoded aliases */
+#define ED_CR_PAGE_0 0x00 /* (for consistency) */
+#define ED_CR_PAGE_1 0x40
+#define ED_CR_PAGE_2 0x80
+
+/*
+ * Interrupt Status Register (ISR) definitions
+ */
+
+/*
+ * PRX: Packet Received. Indicates packet received with no errors.
+ */
+#define ED_ISR_PRX 0x01
+
+/*
+ * PTX: Packet Transmitted. Indicates packet transmitted with no errors.
+ */
+#define ED_ISR_PTX 0x02
+
+/*
+ * RXE: Receive Error. Indicates that a packet was received with one or more
+ * the following errors: CRC error, frame alignment error, FIFO overrun,
+ * missed packet.
+ */
+#define ED_ISR_RXE 0x04
+
+/*
+ * TXE: Transmission Error. Indicates that an attempt to transmit a packet
+ * resulted in one or more of the following errors: excessive
+ * collisions, FIFO underrun.
+ */
+#define ED_ISR_TXE 0x08
+
+/*
+ * OVW: OverWrite. Indicates a receive ring-buffer overrun. Incoming network
+ * would exceed (has exceeded?) the boundry pointer, resulting in data
+ * that was previously received and not yet read from the buffer to be
+ * overwritten.
+ */
+#define ED_ISR_OVW 0x10
+
+/*
+ * CNT: Counter Overflow. Set when the MSB of one or more of the Network Talley
+ * Counters has been set.
+ */
+#define ED_ISR_CNT 0x20
+
+/*
+ * RDC: Remote Data Complete. Indicates that a Remote DMA operation has completed.
+ */
+#define ED_ISR_RDC 0x40
+
+/*
+ * RST: Reset status. Set when the NIC enters the reset state and cleared when a
+ * Start Command is issued to the CR. This bit is also set when a receive
+ * ring-buffer overrun (OverWrite) occurs and is cleared when one or more
+ * packets have been removed from the ring. This is a read-only bit.
+ */
+#define ED_ISR_RST 0x80
+
+/*
+ * Interrupt Mask Register (IMR) definitions
+ */
+
+/*
+ * PRXE: Packet Received interrupt Enable. If set, a received packet will cause
+ * an interrupt.
+ */
+#define ED_IMR_PRXE 0x01
+
+/*
+ * PTXE: Packet Transmit interrupt Enable. If set, an interrupt is generated when
+ * a packet transmission completes.
+ */
+#define ED_IMR_PTXE 0x02
+
+/*
+ * RXEE: Receive Error interrupt Enable. If set, an interrupt will occur whenever a
+ * packet is received with an error.
+ */
+#define ED_IMR_RXEE 0x04
+
+/*
+ * TXEE: Transmit Error interrupt Enable. If set, an interrupt will occur whenever
+ * a transmission results in an error.
+ */
+#define ED_IMR_TXEE 0x08
+
+/*
+ * OVWE: OverWrite error interrupt Enable. If set, an interrupt is generated whenever
+ * the receive ring-buffer is overrun. i.e. when the boundry pointer is exceeded.
+ */
+#define ED_IMR_OVWE 0x10
+
+/*
+ * CNTE: Counter overflow interrupt Enable. If set, an interrupt is generated whenever
+ * the MSB of one or more of the Network Statistics counters has been set.
+ */
+#define ED_IMR_CNTE 0x20
+
+/*
+ * RDCE: Remote DMA Complete interrupt Enable. If set, an interrupt is generated
+ * when a remote DMA transfer has completed.
+ */
+#define ED_IMR_RDCE 0x40
+
+/*
+ * bit 7 is unused/reserved
+ */
+
+/*
+ * Data Configuration Register (DCR) definitions
+ */
+
+/*
+ * WTS: Word Transfer Select. WTS establishes byte or word transfers for
+ * both remote and local DMA transfers
+ */
+#define ED_DCR_WTS 0x01
+
+/*
+ * BOS: Byte Order Select. BOS sets the byte order for the host.
+ * Should be 0 for 80x86, and 1 for 68000 series processors
+ */
+#define ED_DCR_BOS 0x02
+
+/*
+ * LAS: Long Address Select. When LAS is 1, the contents of the remote
+ * DMA registers RSAR0 and RSAR1 are used to provide A16-A31
+ */
+#define ED_DCR_LAS 0x04
+
+/*
+ * LS: Loopback Select. When 0, loopback mode is selected. Bits D1 and D2
+ * of the TCR must also be programmed for loopback operation.
+ * When 1, normal operation is selected.
+ */
+#define ED_DCR_LS 0x08
+
+/*
+ * AR: Auto-initialize Remote. When 0, data must be removed from ring-buffer
+ * under program control. When 1, remote DMA is automatically initiated
+ * and the boundry pointer is automatically updated
+ */
+#define ED_DCR_AR 0x10
+
+/*
+ * FT0, FT1: Fifo Threshold select.
+ * FT1 FT0 Word-width Byte-width
+ * 0 0 1 word 2 bytes
+ * 0 1 2 words 4 bytes
+ * 1 0 4 words 8 bytes
+ * 1 1 8 words 12 bytes
+ *
+ * During transmission, the FIFO threshold indicates the number of bytes
+ * or words that the FIFO has filled from the local DMA before BREQ is
+ * asserted. The transmission threshold is 16 bytes minus the receiver
+ * threshold.
+ */
+#define ED_DCR_FT0 0x20
+#define ED_DCR_FT1 0x40
+
+/*
+ * bit 7 (0x80) is unused/reserved
+ */
+
+/*
+ * Transmit Configuration Register (TCR) definitions
+ */
+
+/*
+ * CRC: Inhibit CRC. If 0, CRC will be appended by the transmitter, if 0, CRC
+ * is not appended by the transmitter.
+ */
+#define ED_TCR_CRC 0x01
+
+/*
+ * LB0, LB1: Loopback control. These two bits set the type of loopback that is
+ * to be performed.
+ *
+ * LB1 LB0 mode
+ * 0 0 0 - normal operation (DCR_LS = 0)
+ * 0 1 1 - internal loopback (DCR_LS = 0)
+ * 1 0 2 - external loopback (DCR_LS = 1)
+ * 1 1 3 - external loopback (DCR_LS = 0)
+ */
+#define ED_TCR_LB0 0x02
+#define ED_TCR_LB1 0x04
+
+/*
+ * ATD: Auto Transmit Disable. Clear for normal operation. When set, allows
+ * another station to disable the NIC's transmitter by transmitting to
+ * a multicast address hashing to bit 62. Reception of a multicast address
+ * hashing to bit 63 enables the transmitter.
+ */
+#define ED_TCR_ATD 0x08
+
+/*
+ * OFST: Collision Offset enable. This bit when set modifies the backoff
+ * algorithm to allow prioritization of nodes.
+ */
+#define ED_TCR_OFST 0x10
+
+/*
+ * bits 5, 6, and 7 are unused/reserved
+ */
+
+/*
+ * Transmit Status Register (TSR) definitions
+ */
+
+/*
+ * PTX: Packet Transmitted. Indicates successful transmission of packet.
+ */
+#define ED_TSR_PTX 0x01
+
+/*
+ * bit 1 (0x02) is unused/reserved
+ */
+
+/*
+ * COL: Transmit Collided. Indicates that the transmission collided at least
+ * once with another station on the network.
+ */
+#define ED_TSR_COL 0x04
+
+/*
+ * ABT: Transmit aborted. Indicates that the transmission was aborted due to
+ * excessive collisions.
+ */
+#define ED_TSR_ABT 0x08
+
+/*
+ * CRS: Carrier Sense Lost. Indicates that carrier was lost during the
+ * transmission of the packet. (Transmission is not aborted because
+ * of a loss of carrier)
+ */
+#define ED_TSR_CRS 0x10
+
+/*
+ * FU: FIFO Underrun. Indicates that the NIC wasn't able to access bus/
+ * transmission memory before the FIFO emptied. Transmission of the
+ * packet was aborted.
+ */
+#define ED_TSR_FU 0x20
+
+/*
+ * CDH: CD Heartbeat. Indicates that the collision detection circuitry
+ * isn't working correctly during a collision heartbeat test.
+ */
+#define ED_TSR_CDH 0x40
+
+/*
+ * OWC: Out of Window Collision: Indicates that a collision occurred after
+ * a slot time (51.2us). The transmission is rescheduled just as in
+ * normal collisions.
+ */
+#define ED_TSR_OWC 0x80
+
+/*
+ * Receiver Configuration Register (RCR) definitions
+ */
+
+/*
+ * SEP: Save Errored Packets. If 0, error packets are discarded. If set to 1,
+ * packets with CRC and frame errors are not discarded.
+ */
+#define ED_RCR_SEP 0x01
+
+/*
+ * AR: Accept Runt packet. If 0, packet with less than 64 byte are discarded.
+ * If set to 1, packets with less than 64 byte are not discarded.
+ */
+#define ED_RCR_AR 0x02
+
+/*
+ * AB: Accept Broadcast. If set, packets sent to the broadcast address will be
+ * accepted.
+ */
+#define ED_RCR_AB 0x04
+
+/*
+ * AM: Accept Multicast. If set, packets sent to a multicast address are checked
+ * for a match in the hashing array. If clear, multicast packets are ignored.
+ */
+#define ED_RCR_AM 0x08
+
+/*
+ * PRO: Promiscuous Physical. If set, all packets with a physical addresses are
+ * accepted. If clear, a physical destination address must match this
+ * station's address. Note: for full promiscuous mode, RCR_AB and RCR_AM
+ * must also be set. In addition, the multicast hashing array must be set
+ * to all 1's so that all multicast addresses are accepted.
+ */
+#define ED_RCR_PRO 0x10
+
+/*
+ * MON: Monitor Mode. If set, packets will be checked for good CRC and framing,
+ * but are not stored in the ring-buffer. If clear, packets are stored (normal
+ * operation).
+ */
+#define ED_RCR_MON 0x20
+
+/*
+ * bits 6 and 7 are unused/reserved.
+ */
+
+/*
+ * Receiver Status Register (RSR) definitions
+ */
+
+/*
+ * PRX: Packet Received without error.
+ */
+#define ED_RSR_PRX 0x01
+
+/*
+ * CRC: CRC error. Indicates that a packet has a CRC error. Also set for frame
+ * alignment errors.
+ */
+#define ED_RSR_CRC 0x02
+
+/*
+ * FAE: Frame Alignment Error. Indicates that the incoming packet did not end on
+ * a byte boundry and the CRC did not match at the last byte boundry.
+ */
+#define ED_RSR_FAE 0x04
+
+/*
+ * FO: FIFO Overrun. Indicates that the FIFO was not serviced (during local DMA)
+ * causing it to overrun. Reception of the packet is aborted.
+ */
+#define ED_RSR_FO 0x08
+
+/*
+ * MPA: Missed Packet. Indicates that the received packet couldn't be stored in
+ * the ring-buffer because of insufficient buffer space (exceeding the
+ * boundry pointer), or because the transfer to the ring-buffer was inhibited
+ * by RCR_MON - monitor mode.
+ */
+#define ED_RSR_MPA 0x10
+
+/*
+ * PHY: Physical address. If 0, the packet received was sent to a physical address.
+ * If 1, the packet was accepted because of a multicast/broadcast address
+ * match.
+ */
+#define ED_RSR_PHY 0x20
+
+/*
+ * DIS: Receiver Disabled. Set to indicate that the receiver has enetered monitor
+ * mode. Cleared when the receiver exits monitor mode.
+ */
+#define ED_RSR_DIS 0x40
+
+/*
+ * DFR: Deferring. Set to indicate a 'jabber' condition. The CRS and COL inputs
+ * are active, and the transceiver has set the CD line as a result of the
+ * jabber.
+ */
+#define ED_RSR_DFR 0x80
+
+/*
+ * receive ring discriptor
+ *
+ * The National Semiconductor DS8390 Network interface controller uses
+ * the following receive ring headers. The way this works is that the
+ * memory on the interface card is chopped up into 256 bytes blocks.
+ * A contiguous portion of those blocks are marked for receive packets
+ * by setting start and end block #'s in the NIC. For each packet that
+ * is put into the receive ring, one of these headers (4 bytes each) is
+ * tacked onto the front.
+ */
+struct ed_ring {
+ struct edr_status { /* received packet status */
+ u_char rs_prx:1, /* packet received intack */
+ rs_crc:1, /* crc error */
+ rs_fae:1, /* frame alignment error */
+ rs_fo:1, /* fifo overrun */
+ rs_mpa:1, /* packet received intack */
+ rs_phy:1, /* packet received intack */
+ rs_dis:1, /* packet received intack */
+ rs_dfr:1; /* packet received intack */
+ } ed_rcv_status; /* received packet status */
+ u_char next_packet; /* pointer to next packet */
+ u_short count; /* bytes in packet (length + 4) */
+};
+
+/*
+ * Common constants
+ */
+#define ED_PAGE_SIZE 256 /* Size of RAM pages in bytes */
+#define ED_TXBUF_SIZE 6 /* Size of TX buffer in pages */
+
+/*
+ * Vendor types
+ */
+#define ED_VENDOR_WD_SMC 0x00 /* Western Digital/SMC */
+#define ED_VENDOR_3COM 0x01 /* 3Com */
+#define ED_VENDOR_NOVELL 0x02 /* Novell */
+
+/*
+ * Compile-time config flags
+ */
+/*
+ * this sets the default for enabling/disablng the tranceiver
+ */
+#define ED_FLAGS_DISABLE_TRANCEIVER 0x0001
+
+/*
+ * This forces the board to be used in 8/16bit mode even if it
+ * autoconfigs differently
+ */
+#define ED_FLAGS_FORCE_8BIT_MODE 0x0002
+#define ED_FLAGS_FORCE_16BIT_MODE 0x0004
+
+/*
+ * This disables the use of double transmit buffers.
+ */
+#define ED_FLAGS_NO_MULTI_BUFFERING 0x0008
+
+/*
+ * This forces all operations with the NIC memory to use Programmed
+ * I/O (i.e. not via shared memory)
+ */
+#define ED_FLAGS_FORCE_PIO 0x0010
+
+/*
+ * Definitions for Western digital/SMC WD80x3 series ASIC
+ */
+/*
+ * Memory Select Register (MSR)
+ */
+#define ED_WD_MSR 0
+
+/* next three definitions for Toshiba */
+#define ED_WD_MSR_POW 0x02 /* 0 = power save, 1 = normal (R/W) */
+#define ED_WD_MSR_BSY 0x04 /* gate array busy (R) */
+#define ED_WD_MSR_LEN 0x20 /* data bus width, 0 = 16 bits,
+ 1 = 8 bits (R/W) */
+#define ED_WD_MSR_ADDR 0x3f /* Memory decode bits 18-13 */
+#define ED_WD_MSR_MENB 0x40 /* Memory enable */
+#define ED_WD_MSR_RST 0x80 /* Reset board */
+
+/*
+ * Interface Configuration Register (ICR)
+ */
+#define ED_WD_ICR 1
+
+#define ED_WD_ICR_16BIT 0x01 /* 16-bit interface */
+#define ED_WD_ICR_OAR 0x02 /* select register. 0=BIO 1=EAR */
+#define ED_WD_ICR_IR2 0x04 /* high order bit of encoded IRQ */
+#define ED_WD_ICR_MSZ 0x08 /* memory size (0=8k 1=32k) */
+#define ED_WD_ICR_RLA 0x10 /* recall LAN address */
+#define ED_WD_ICR_RX7 0x20 /* recall all but i/o and LAN address */
+#define ED_WD_ICR_RIO 0x40 /* recall i/o address */
+#define ED_WD_ICR_STO 0x80 /* store to non-volatile memory */
+#ifdef TOSH_ETHER
+#define ED_WD_ICR_MEM 0xe0 /* shared mem address A15-A13 (R/W) */
+#define ED_WD_ICR_MSZ1 0x0f /* memory size, 0x08 = 64K, 0x04 = 32K,
+ 0x02 = 16K, 0x01 = 8K */
+ /* 64K can only be used if mem address
+ above 1Mb */
+ /* IAR holds address A23-A16 (R/W) */
+#endif
+
+/*
+ * IO Address Register (IAR)
+ */
+#define ED_WD_IAR 2
+
+/*
+ * EEROM Address Register
+ */
+#define ED_WD_EAR 3
+
+/*
+ * Interrupt Request Register (IRR)
+ */
+#define ED_WD_IRR 4
+
+#define ED_WD_IRR_0WS 0x01 /* use 0 wait-states on 8 bit bus */
+#define ED_WD_IRR_OUT1 0x02 /* WD83C584 pin 1 output */
+#define ED_WD_IRR_OUT2 0x04 /* WD83C584 pin 2 output */
+#define ED_WD_IRR_OUT3 0x08 /* WD83C584 pin 3 output */
+#define ED_WD_IRR_FLASH 0x10 /* Flash RAM is in the ROM socket */
+
+/*
+ * The three bits of the encoded IRQ are decoded as follows:
+ *
+ * IR2 IR1 IR0 IRQ
+ * 0 0 0 2/9
+ * 0 0 1 3
+ * 0 1 0 5
+ * 0 1 1 7
+ * 1 0 0 10
+ * 1 0 1 11
+ * 1 1 0 15
+ * 1 1 1 4
+ */
+#define ED_WD_IRR_IR0 0x20 /* bit 0 of encoded IRQ */
+#define ED_WD_IRR_IR1 0x40 /* bit 1 of encoded IRQ */
+#define ED_WD_IRR_IEN 0x80 /* Interrupt enable */
+
+/*
+ * LA Address Register (LAAR)
+ */
+#define ED_WD_LAAR 5
+
+#define ED_WD_LAAR_ADDRHI 0x1f /* bits 23-19 of RAM address */
+#define ED_WD_LAAR_0WS16 0x20 /* enable 0 wait-states on 16 bit bus */
+#define ED_WD_LAAR_L16EN 0x40 /* enable 16-bit operation */
+#define ED_WD_LAAR_M16EN 0x80 /* enable 16-bit memory access */
+
+/* i/o base offset to station address/card-ID PROM */
+#define ED_WD_PROM 8
+
+/*
+ * 83C790 specific registers
+ */
+/*
+ * Hardware Support Register (HWR) ('790)
+ */
+#define ED_WD790_HWR 4
+
+#define WD_WD790_HWR_NUKE 0x10 /* hardware reset */
+#define ED_WD790_HWR_LPRM 0x40 /* LAN PROM select */
+#define ED_WD790_HWR_SWH 0x80 /* switch register set */
+
+/*
+ * ICR790 Interrupt Control Register for the 83C790
+ */
+#define ED_WD790_ICR 6
+
+#define ED_WD790_ICR_EIL 0x01 /* enable interrupts */
+
+/*
+ * General Control Register (GCR)
+ * Enabled with SWH bit=1 in HWR register
+ */
+#define ED_WD790_GCR 0x0d
+
+#define ED_WD790_GCR_IR0 0x04 /* bit 0 of encoded IRQ */
+#define ED_WD790_GCR_IR1 0x08 /* bit 1 of encoded IRQ */
+#define ED_WD790_GCR_ZWSEN 0x20 /* zero wait state enable */
+#define ED_WD790_GCR_IR2 0x40 /* bit 2 of encoded IRQ */
+/*
+ * The three bits of the encoded IRQ are decoded as follows:
+ *
+ * IR2 IR1 IR0 IRQ
+ * 0 0 0 none
+ * 0 0 1 9
+ * 0 1 0 3
+ * 0 1 1 5
+ * 1 0 0 7
+ * 1 0 1 10
+ * 1 1 0 11
+ * 1 1 1 15
+ */
+
+/* i/o base offset to CARD ID */
+#define ED_WD_CARD_ID ED_WD_PROM+6
+
+/* Board type codes in card ID */
+#define ED_TYPE_WD8003S 0x02
+#define ED_TYPE_WD8003E 0x03
+#define ED_TYPE_WD8013EBT 0x05
+#define ED_TYPE_TOSHIBA1 0x11 /* named PCETA1 */
+#define ED_TYPE_TOSHIBA2 0x12 /* named PCETA2 */
+#define ED_TYPE_TOSHIBA3 0x13 /* named PCETB */
+#define ED_TYPE_TOSHIBA4 0x14 /* named PCETC */
+#define ED_TYPE_WD8003W 0x24
+#define ED_TYPE_WD8003EB 0x25
+#define ED_TYPE_WD8013W 0x26
+#define ED_TYPE_WD8013EP 0x27
+#define ED_TYPE_WD8013WC 0x28
+#define ED_TYPE_WD8013EPC 0x29
+#define ED_TYPE_SMC8216T 0x2a
+#define ED_TYPE_SMC8216C 0x2b
+#define ED_TYPE_WD8013EBP 0x2c
+
+/* Bit definitions in card ID */
+#define ED_WD_REV_MASK 0x1f /* Revision mask */
+#define ED_WD_SOFTCONFIG 0x20 /* Soft config */
+#define ED_WD_LARGERAM 0x40 /* Large RAM */
+#define ED_MICROCHANEL 0x80 /* Microchannel bus (vs. isa) */
+
+/*
+ * Checksum total. All 8 bytes in station address PROM will add up to this
+ */
+#ifdef TOSH_ETHER
+#define ED_WD_ROM_CHECKSUM_TOTAL 0xA5
+#else
+#define ED_WD_ROM_CHECKSUM_TOTAL 0xFF
+#endif
+
+#define ED_WD_NIC_OFFSET 0x10 /* I/O base offset to NIC */
+#define ED_WD_ASIC_OFFSET 0 /* I/O base offset to ASIC */
+#define ED_WD_IO_PORTS 32 /* # of i/o addresses used */
+
+#define ED_WD_PAGE_OFFSET 0 /* page offset for NIC access to mem */
+
+/*
+ * Definitions for 3Com 3c503
+ */
+#define ED_3COM_NIC_OFFSET 0
+#define ED_3COM_ASIC_OFFSET 0x400 /* offset to nic i/o regs */
+
+/*
+ * XXX - The I/O address range is fragmented in the 3c503; this is the
+ * number of regs at iobase.
+ */
+#define ED_3COM_IO_PORTS 16 /* # of i/o addresses used */
+
+/* tx memory starts in second bank on 8bit cards */
+#define ED_3COM_TX_PAGE_OFFSET_8BIT 0x20
+
+/* tx memory starts in first bank on 16bit cards */
+#define ED_3COM_TX_PAGE_OFFSET_16BIT 0x0
+
+/* ...and rx memory starts in second bank */
+#define ED_3COM_RX_PAGE_OFFSET_16BIT 0x20
+
+
+/*
+ * Page Start Register. Must match PSTART in NIC
+ */
+#define ED_3COM_PSTR 0
+
+/*
+ * Page Stop Register. Must match PSTOP in NIC
+ */
+#define ED_3COM_PSPR 1
+
+/*
+ * Drq Timer Register. Determines number of bytes to be transfered during
+ * a DMA burst.
+ */
+#define ED_3COM_DQTR 2
+
+/*
+ * Base Configuration Register. Read-only register which contains the
+ * board-configured I/O base address of the adapter. Bit encoded.
+ */
+#define ED_3COM_BCFR 3
+
+#define ED_3COM_BCFR_2E0 0x01
+#define ED_3COM_BCFR_2A0 0x02
+#define ED_3COM_BCFR_280 0x04
+#define ED_3COM_BCFR_250 0x08
+#define ED_3COM_BCFR_350 0x10
+#define ED_3COM_BCFR_330 0x20
+#define ED_3COM_BCFR_310 0x40
+#define ED_3COM_BCFR_300 0x80
+
+/*
+ * EPROM Configuration Register. Read-only register which contains the
+ * board-configured memory base address. Bit encoded.
+ */
+#define ED_3COM_PCFR 4
+
+#define ED_3COM_PCFR_C8000 0x10
+#define ED_3COM_PCFR_CC000 0x20
+#define ED_3COM_PCFR_D8000 0x40
+#define ED_3COM_PCFR_DC000 0x80
+
+/*
+ * GA Configuration Register. Gate-Array Configuration Register.
+ */
+#define ED_3COM_GACFR 5
+
+/*
+ * mbs2 mbs1 mbs0 start address
+ * 0 0 0 0x0000
+ * 0 0 1 0x2000
+ * 0 1 0 0x4000
+ * 0 1 1 0x6000
+ *
+ * Note that with adapters with only 8K, the setting for 0x2000 must
+ * always be used.
+ */
+#define ED_3COM_GACFR_MBS0 0x01
+#define ED_3COM_GACFR_MBS1 0x02
+#define ED_3COM_GACFR_MBS2 0x04
+
+#define ED_3COM_GACFR_RSEL 0x08 /* enable shared memory */
+#define ED_3COM_GACFR_TEST 0x10 /* for GA testing */
+#define ED_3COM_GACFR_OWS 0x20 /* select 0WS access to GA */
+#define ED_3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */
+#define ED_3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */
+
+/*
+ * Control Register. Miscellaneous control functions.
+ */
+#define ED_3COM_CR 6
+
+#define ED_3COM_CR_RST 0x01 /* Reset GA and NIC */
+#define ED_3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */
+#define ED_3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */
+#define ED_3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */
+#define ED_3COM_CR_SHARE 0x10 /* select interrupt sharing option */
+#define ED_3COM_CR_DBSEL 0x20 /* Double buffer select */
+#define ED_3COM_CR_DDIR 0x40 /* DMA direction select */
+#define ED_3COM_CR_START 0x80 /* Start DMA controller */
+
+/*
+ * Status Register. Miscellaneous status information.
+ */
+#define ED_3COM_STREG 7
+
+#define ED_3COM_STREG_REV 0x07 /* GA revision */
+#define ED_3COM_STREG_DIP 0x08 /* DMA in progress */
+#define ED_3COM_STREG_DTC 0x10 /* DMA terminal count */
+#define ED_3COM_STREG_OFLW 0x20 /* Overflow */
+#define ED_3COM_STREG_UFLW 0x40 /* Underflow */
+#define ED_3COM_STREG_DPRDY 0x80 /* Data port ready */
+
+/*
+ * Interrupt/DMA Configuration Register
+ */
+#define ED_3COM_IDCFR 8
+
+#define ED_3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */
+#define ED_3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */
+#define ED_3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */
+#define ED_3COM_IDCFR_UNUSED 0x08 /* not used */
+#define ED_3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */
+#define ED_3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */
+#define ED_3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */
+#define ED_3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */
+
+/*
+ * DMA Address Register MSB
+ */
+#define ED_3COM_DAMSB 9
+
+/*
+ * DMA Address Register LSB
+ */
+#define ED_3COM_DALSB 0x0a
+
+/*
+ * Vector Pointer Register 2
+ */
+#define ED_3COM_VPTR2 0x0b
+
+/*
+ * Vector Pointer Register 1
+ */
+#define ED_3COM_VPTR1 0x0c
+
+/*
+ * Vector Pointer Register 0
+ */
+#define ED_3COM_VPTR0 0x0d
+
+/*
+ * Register File Access MSB
+ */
+#define ED_3COM_RFMSB 0x0e
+
+/*
+ * Register File Access LSB
+ */
+#define ED_3COM_RFLSB 0x0f
+
+/*
+ * Definitions for Novell NE1000/2000 boards
+ */
+
+/*
+ * Board type codes
+ */
+#define ED_TYPE_NE1000 0x01
+#define ED_TYPE_NE2000 0x02
+
+/*
+ * Register offsets/total
+ */
+#define ED_NOVELL_NIC_OFFSET 0x00
+#define ED_NOVELL_ASIC_OFFSET 0x10
+#define ED_NOVELL_IO_PORTS 32
+
+/*
+ * Remote DMA data register; for reading or writing to the NIC mem
+ * via programmed I/O (offset from ASIC base)
+ */
+#define ED_NOVELL_DATA 0x00
+
+/*
+ * Reset register; reading from this register causes a board reset
+ */
+#define ED_NOVELL_RESET 0x0f
diff --git a/sys/dev/ep/if_ep.c b/sys/dev/ep/if_ep.c
new file mode 100644
index 0000000..b47f829
--- /dev/null
+++ b/sys/dev/ep/if_ep.c
@@ -0,0 +1,993 @@
+/*
+ * Copyright (c) 1993 Herb Peyerl <hpeyerl@novatel.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * From: if_ep.c,v 1.9 1994/01/25 10:46:29 deraadt Exp $
+ * $Id: if_ep.c,v 1.8 1994/03/15 01:58:22 wollman Exp $
+ */
+
+#include "ep.h"
+#if NEP > 0
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#if defined(__FreeBSD__)
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#endif
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#if defined(__NetBSD__)
+#include <sys/select.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#include <machine/pio.h>
+
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/icu.h>
+#include <i386/isa/if_epreg.h>
+
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1518
+#define ETHER_ADDR_LEN 6
+
+/*
+ * Ethernet software status per interface.
+ */
+struct ep_softc {
+ struct arpcom arpcom; /* Ethernet common part */
+ short ep_io_addr; /* i/o bus address */
+ char ep_connectors; /* Connectors on this card. */
+#define MAX_MBS 8 /* # of mbufs we keep around */
+ struct mbuf *mb[MAX_MBS]; /* spare mbuf storage. */
+ int next_mb; /* Which mbuf to use next. */
+ int last_mb; /* Last mbuf. */
+ int tx_start_thresh; /* Current TX_start_thresh. */
+ caddr_t bpf; /* BPF "magic cookie" */
+ char bus32bit; /* 32bit access possible */
+} ep_softc[NEP];
+
+static int epprobe __P((struct isa_device *));
+static int epattach __P((struct isa_device *));
+static int epioctl __P((struct ifnet * ifp, int, caddr_t));
+
+void epinit __P((int));
+void epintr __P((int));
+void epmbuffill __P((caddr_t));
+void epmbufempty __P((struct ep_softc *));
+void epread __P((struct ep_softc *));
+void epreset __P((int));
+void epstart __P((struct ifnet *));
+void epstop __P((int));
+void epwatchdog __P((int));
+
+struct isa_driver epdriver = {
+ epprobe,
+ epattach,
+ "ep"
+};
+
+static int send_ID_sequence __P((u_short));
+static u_short get_eeprom_data __P((int, int));
+static int is_eeprom_busy __P((struct isa_device *));
+
+/*
+ * Rudimentary support for multiple cards is here but is not
+ * currently handled. In the future we will have to add code
+ * for tagging the cards for later activation. We wanna do something
+ * about the id_port. We're limited due to current config procedure.
+ * Magnum config holds promise of a fix but we'll have to wait a bit.
+ */
+int
+epprobe(is)
+ struct isa_device *is;
+{
+ struct ep_softc *sc = &ep_softc[is->id_unit];
+ u_short k;
+ int id_port = 0x100; /* XXX */
+
+ outw(BASE + EP_COMMAND, GLOBAL_RESET);
+ DELAY(1000);
+ outb(id_port, 0xc0); /* Global reset to id_port. */
+ DELAY(1000);
+ send_ID_sequence(id_port);
+ DELAY(1000);
+
+ /*
+ * MFG_ID should have 0x6d50.
+ * PROD_ID should be 0x9[0-f]50
+ */
+ k = get_eeprom_data(id_port, EEPROM_MFG_ID);
+ if (k != MFG_ID)
+ return (0);
+ k = get_eeprom_data(id_port, EEPROM_PROD_ID);
+ if ((k & 0xf0ff) != (PROD_ID & 0xf0ff))
+ return (0);
+
+ k = get_eeprom_data(id_port, EEPROM_ADDR_CFG); /* get addr cfg */
+ k = (k & 0x1f) * 0x10 + 0x200; /* decode base addr. */
+ if (k != (u_short)is->id_iobase)
+ return (0);
+
+ k = get_eeprom_data(id_port, EEPROM_RESOURCE_CFG);
+ k >>= 12;
+ if (is->id_irq != (1 << ((k == 2) ? 9 : k)))
+ return (0);
+
+ outb(id_port, ACTIVATE_ADAPTER_TO_CONFIG);
+
+ return (0x10); /* 16 bytes of I/O space used. */
+}
+
+static int
+epattach(is)
+ struct isa_device *is;
+{
+ struct ep_softc *sc = &ep_softc[is->id_unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ u_short i;
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+
+ sc->ep_io_addr = is->id_iobase;
+
+ printf("ep%d: ", is->id_unit);
+
+ sc->ep_connectors = 0;
+ i = inw(is->id_iobase + EP_W0_CONFIG_CTRL);
+ if (i & IS_AUI) {
+ printf("aui");
+ sc->ep_connectors |= AUI;
+ }
+ if (i & IS_BNC) {
+ if (sc->ep_connectors)
+ printf("/");
+ printf("bnc");
+ sc->ep_connectors |= BNC;
+ }
+ if (i & IS_UTP) {
+ if (sc->ep_connectors)
+ printf("/");
+ printf("utp");
+ sc->ep_connectors |= UTP;
+ }
+ if (!sc->ep_connectors)
+ printf("no connectors!");
+
+ /*
+ * Read the station address from the eeprom
+ */
+ for (i = 0; i < 3; i++) {
+ u_short *p;
+ GO_WINDOW(0);
+ if (is_eeprom_busy(is))
+ return(0);
+ outw(BASE + EP_W0_EEPROM_COMMAND, READ_EEPROM | i);
+ if (is_eeprom_busy(is))
+ return(0);
+ p =(u_short *)&sc->arpcom.ac_enaddr[i*2];
+ *p = htons(inw(BASE + EP_W0_EEPROM_DATA));
+ GO_WINDOW(2);
+ outw(BASE + EP_W2_ADDR_0 + (i * 2), ntohs(*p));
+ }
+ printf(" address %s\n", ether_sprintf(sc->arpcom.ac_enaddr));
+
+ ifp->if_unit = is->id_unit;
+ ifp->if_name = "ep";
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
+ ifp->if_init = epinit;
+ ifp->if_output = ether_output;
+ ifp->if_start = epstart;
+ ifp->if_ioctl = epioctl;
+ ifp->if_watchdog = epwatchdog;
+
+ if_attach(ifp);
+
+ /*
+ * Fill the hardware address into ifa_addr if we find an
+ * AF_LINK entry. We need to do this so bpf's can get the hardware
+ * addr of this card. netstat likes this too!
+ */
+ ifa = ifp->if_addrlist;
+ while ((ifa != 0) && (ifa->ifa_addr != 0) &&
+ (ifa->ifa_addr->sa_family != AF_LINK))
+ ifa = ifa->ifa_next;
+
+ if ((ifa != 0) && (ifa->ifa_addr != 0)) {
+ sdl = (struct sockaddr_dl *) ifa->ifa_addr;
+ sdl->sdl_type = IFT_ETHER;
+ sdl->sdl_alen = ETHER_ADDR_LEN;
+ sdl->sdl_slen = 0;
+ bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN);
+ }
+#if NBPFILTER > 0
+ bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+ return 1;
+}
+
+
+/*
+ * The order in here seems important. Otherwise we may not receive
+ * interrupts. ?!
+ */
+void
+epinit(unit)
+ int unit;
+{
+ register struct ep_softc *sc = &ep_softc[unit];
+ register struct ifnet *ifp = &sc->arpcom.ac_if;
+ int s, i;
+
+ if (ifp->if_addrlist == (struct ifaddr *) 0)
+ return;
+
+ s = splimp();
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ ;
+
+ GO_WINDOW(0);
+
+ /* Disable the card */
+ outw(BASE + EP_W0_CONFIG_CTRL, 0);
+
+ /* Enable the card */
+ outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ);
+
+ GO_WINDOW(2);
+
+ /* Reload the ether_addr. */
+ for (i = 0; i < 6; i++)
+ outb(BASE + EP_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]);
+
+ outw(BASE + EP_COMMAND, RX_RESET);
+ outw(BASE + EP_COMMAND, TX_RESET);
+
+ /* Window 1 is operating window */
+ GO_WINDOW(1);
+ for (i = 0; i < 31; i++)
+ inb(BASE + EP_W1_TX_STATUS);
+
+ /* get rid of stray intr's */
+ outw(BASE + EP_COMMAND, ACK_INTR | 0xff);
+
+ outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
+ S_TX_COMPLETE | S_TX_AVAIL);
+ outw(BASE + EP_COMMAND, SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
+ S_TX_COMPLETE | S_TX_AVAIL);
+
+ outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL |
+ FIL_GROUP | FIL_BRDCST);
+
+ /*
+ * you can `ifconfig (link0|-link0) ep0' to get the following
+ * behaviour:
+ * -link0 disable AUI/UTP. enable BNC.
+ * link0 disable BNC. enable AUI. if the card has a UTP
+ * connector, that is enabled too. not sure, but it
+ * seems you have to be careful to not plug things
+ * into both AUI & UTP.
+ */
+#if defined(__NetBSD__)
+ if (!(ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & BNC)) {
+#else
+ if (!(ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & BNC)) {
+#endif
+ outw(BASE + EP_COMMAND, START_TRANSCEIVER);
+ DELAY(1000);
+ }
+#if defined(__NetBSD__)
+ if ((ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & UTP)) {
+#else
+ if ((ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & UTP)) {
+#endif
+ GO_WINDOW(4);
+ outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP);
+ GO_WINDOW(1);
+ }
+
+ outw(BASE + EP_COMMAND, RX_ENABLE);
+ outw(BASE + EP_COMMAND, TX_ENABLE);
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE; /* just in case */
+ sc->tx_start_thresh = 20; /* probably a good starting point. */
+ /*
+ * Store up a bunch of mbuf's for use later. (MAX_MBS). First we
+ * free up any that we had in case we're being called from intr or
+ * somewhere else.
+ */
+ sc->last_mb = 0;
+ sc->next_mb = 0;
+ epmbuffill((caddr_t)sc, 0);
+
+ epstart(ifp);
+
+ splx(s);
+}
+
+static const char padmap[] = {0, 3, 2, 1};
+
+void
+epstart(ifp)
+ struct ifnet *ifp;
+{
+ register struct ep_softc *sc = &ep_softc[ifp->if_unit];
+ struct mbuf *m, *top;
+ int s, len, pad;
+
+ s = splimp();
+ if (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) {
+ splx(s);
+ return;
+ }
+
+startagain:
+ /* Sneak a peek at the next packet */
+ m = sc->arpcom.ac_if.if_snd.ifq_head;
+ if (m == 0) {
+ splx(s);
+ return;
+ }
+#if 0
+ len = m->m_pkthdr.len;
+#else
+ for (len = 0, top = m; m; m = m->m_next)
+ len += m->m_len;
+#endif
+
+ pad = padmap[len & 3];
+
+ /*
+ * The 3c509 automatically pads short packets to minimum ethernet
+ * length, but we drop packets that are too large. Perhaps we should
+ * truncate them instead?
+ */
+ if (len + pad > ETHER_MAX_LEN) {
+ /* packet is obviously too large: toss it */
+ ++sc->arpcom.ac_if.if_oerrors;
+ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m);
+ m_freem(m);
+ goto readcheck;
+ }
+
+ if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) {
+ /* no room in FIFO */
+ outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4));
+ sc->arpcom.ac_if.if_flags |= IFF_OACTIVE;
+ splx(s);
+ return;
+ }
+ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m);
+ if (m == 0) { /* not really needed */
+ splx(s);
+ return;
+ }
+ outw(BASE + EP_COMMAND, SET_TX_START_THRESH |
+ (len / 4 + sc->tx_start_thresh));
+
+ outw(BASE + EP_W1_TX_PIO_WR_1, len);
+ outw(BASE + EP_W1_TX_PIO_WR_1, 0xffff); /* Second dword meaningless */
+
+ for (top = m; m != 0; m = m->m_next) {
+ if (sc->bus32bit) {
+ outsl(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t),
+ m->m_len/4);
+ if (m->m_len & 3)
+ outsb(BASE + EP_W1_TX_PIO_WR_1,
+ mtod(m, caddr_t) + m->m_len/4,
+ m->m_len & 3);
+ } else {
+ outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len/2);
+ if (m->m_len & 1)
+ outb(BASE + EP_W1_TX_PIO_WR_1,
+ *(mtod(m, caddr_t) + m->m_len - 1));
+ }
+ }
+ while (pad--)
+ outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */
+
+#if NBPFILTER > 0
+ if (sc->bpf) {
+ u_short etype;
+ int off, datasize, resid;
+ struct ether_header *eh;
+ struct trailer_header {
+ u_short ether_type;
+ u_short ether_residual;
+ } trailer_header;
+ char ether_packet[ETHER_MAX_LEN];
+ char *ep;
+
+ ep = ether_packet;
+
+ /*
+ * We handle trailers below:
+ * Copy ether header first, then residual data,
+ * then data. Put all this in a temporary buffer
+ * 'ether_packet' and send off to bpf. Since the
+ * system has generated this packet, we assume
+ * that all of the offsets in the packet are
+ * correct; if they're not, the system will almost
+ * certainly crash in m_copydata.
+ * We make no assumptions about how the data is
+ * arranged in the mbuf chain (i.e. how much
+ * data is in each mbuf, if mbuf clusters are
+ * used, etc.), which is why we use m_copydata
+ * to get the ether header rather than assume
+ * that this is located in the first mbuf.
+ */
+ /* copy ether header */
+ m_copydata(top, 0, sizeof(struct ether_header), ep);
+ eh = (struct ether_header *) ep;
+ ep += sizeof(struct ether_header);
+ eh->ether_type = etype = ntohs(eh->ether_type);
+ if (etype >= ETHERTYPE_TRAIL &&
+ etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) {
+ datasize = ((etype - ETHERTYPE_TRAIL) << 9);
+ off = datasize + sizeof(struct ether_header);
+
+ /* copy trailer_header into a data structure */
+ m_copydata(top, off, sizeof(struct trailer_header),
+ (caddr_t)&trailer_header.ether_type);
+
+ /* copy residual data */
+ resid = trailer_header.ether_residual -
+ sizeof(struct trailer_header);
+ resid = ntohs(resid);
+ m_copydata(top, off + sizeof(struct trailer_header),
+ resid, ep);
+ ep += resid;
+
+ /* copy data */
+ m_copydata(top, sizeof(struct ether_header),
+ datasize, ep);
+ ep += datasize;
+
+ /* restore original ether packet type */
+ eh->ether_type = trailer_header.ether_type;
+
+ bpf_tap(sc->bpf, ether_packet, ep - ether_packet);
+ } else
+ bpf_mtap(sc->bpf, top);
+ }
+#endif
+
+ m_freem(top);
+ ++sc->arpcom.ac_if.if_opackets;
+
+ /*
+ * Is another packet coming in? We don't want to overflow the
+ * tiny RX fifo.
+ */
+readcheck:
+ if (inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK) {
+ splx(s);
+ return;
+ }
+ goto startagain;
+}
+
+void
+epintr(unit)
+ int unit;
+{
+ int status, i;
+ register struct ep_softc *sc = &ep_softc[unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+
+ status = 0;
+checkintr:
+ status = inw(BASE + EP_STATUS) &
+ (S_TX_COMPLETE | S_TX_AVAIL | S_RX_COMPLETE | S_CARD_FAILURE);
+ if (status == 0) {
+ /* No interrupts. */
+ outw(BASE + EP_COMMAND, C_INTR_LATCH);
+ return;
+ }
+ /* important that we do this first. */
+ outw(BASE + EP_COMMAND, ACK_INTR | status);
+
+ if (status & S_TX_AVAIL) {
+ status &= ~S_TX_AVAIL;
+ inw(BASE + EP_W1_FREE_TX);
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+ epstart(&sc->arpcom.ac_if);
+ }
+ if (status & S_RX_COMPLETE) {
+ status &= ~S_RX_COMPLETE;
+ epread(sc);
+ }
+ if (status & S_CARD_FAILURE) {
+ printf("ep%d: reset (status: %x)\n", unit, status);
+ outw(BASE + EP_COMMAND, C_INTR_LATCH);
+ epinit(unit);
+ return;
+ }
+ if (status & S_TX_COMPLETE) {
+ status &= ~S_TX_COMPLETE;
+ /*
+ * We need to read TX_STATUS until we get a 0 status in
+ * order to turn off the interrupt flag.
+ */
+ while ((i = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE) {
+ outw(BASE + EP_W1_TX_STATUS, 0x0);
+ if (i & (TXS_MAX_COLLISION | TXS_JABBER | TXS_UNDERRUN)) {
+ if (i & TXS_MAX_COLLISION)
+ ++sc->arpcom.ac_if.if_collisions;
+ if (i & (TXS_JABBER | TXS_UNDERRUN)) {
+ outw(BASE + EP_COMMAND, TX_RESET);
+ if (i & TXS_UNDERRUN) {
+ if (sc->tx_start_thresh < ETHER_MAX_LEN) {
+ sc->tx_start_thresh += 20;
+ outw(BASE + EP_COMMAND,
+ SET_TX_START_THRESH |
+ sc->tx_start_thresh);
+ }
+ }
+ }
+ outw(BASE + EP_COMMAND, TX_ENABLE);
+ ++sc->arpcom.ac_if.if_oerrors;
+ }
+ }
+ epstart(ifp);
+ }
+ goto checkintr;
+}
+
+void
+epread(sc)
+ register struct ep_softc *sc;
+{
+ struct ether_header *eh;
+ struct mbuf *mcur, *m, *m0, *top;
+ int totlen, lenthisone;
+ int save_totlen;
+ u_short etype;
+ int off, resid;
+ int count, spinwait;
+ int i;
+
+ totlen = inw(BASE + EP_W1_RX_STATUS);
+ off = 0;
+ top = 0;
+
+ if (totlen & ERR_RX) {
+ ++sc->arpcom.ac_if.if_ierrors;
+ goto out;
+ }
+ save_totlen = totlen &= RX_BYTES_MASK; /* Lower 11 bits = RX bytes. */
+
+ m = sc->mb[sc->next_mb];
+ sc->mb[sc->next_mb] = 0;
+
+ if (m == 0) {
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ goto out;
+ } else {
+ /* Convert one of our saved mbuf's */
+ sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
+ m->m_data = m->m_pktdat;
+ m->m_flags = M_PKTHDR;
+ }
+
+ top = m0 = m; /* We assign top so we can "goto out" */
+#define EROUND ((sizeof(struct ether_header) + 3) & ~3)
+#define EOFF (EROUND - sizeof(struct ether_header))
+ m0->m_data += EOFF;
+ /* Read what should be the header. */
+ insw(BASE + EP_W1_RX_PIO_RD_1,
+ mtod(m0, caddr_t), sizeof(struct ether_header) / 2);
+ m->m_len = sizeof(struct ether_header);
+ totlen -= sizeof(struct ether_header);
+ /*
+ * mostly deal with trailer here. (untested)
+ * We do this in a couple of parts. First we check for a trailer, if
+ * we have one we convert the mbuf back to a regular mbuf and set the offset and
+ * subtract sizeof(struct ether_header) from the pktlen.
+ * After we've read the packet off the interface (all except for the trailer
+ * header, we then get a header mbuf, read the trailer into it, and fix up
+ * the mbuf pointer chain.
+ */
+ eh = mtod(m, struct ether_header *);
+ eh->ether_type = etype = ntohs((u_short) eh->ether_type);
+ if (etype >= ETHERTYPE_TRAIL &&
+ etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) {
+ m->m_data = m->m_dat; /* Convert back to regular mbuf. */
+ m->m_flags = 0; /* This sucks but non-trailers are the norm */
+ off = (etype - ETHERTYPE_TRAIL) * 512;
+ if (off >= ETHERMTU) {
+ m_freem(m);
+ return; /* sanity */
+ }
+ totlen -= sizeof(struct ether_header); /* We don't read the trailer */
+ m->m_data += 2 * sizeof(u_short); /* Get rid of type & len */
+ }
+ while (totlen > 0) {
+ lenthisone = min(totlen, M_TRAILINGSPACE(m));
+ if (lenthisone == 0) { /* no room in this one */
+ mcur = m;
+ m = sc->mb[sc->next_mb];
+ sc->mb[sc->next_mb] = 0;
+ if (!m) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ goto out;
+ } else {
+ timeout(epmbuffill, (caddr_t)sc, 0);
+ sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
+ }
+ if (totlen >= MINCLSIZE)
+ MCLGET(m, M_DONTWAIT);
+ m->m_len = 0;
+ mcur->m_next = m;
+ lenthisone = min(totlen, M_TRAILINGSPACE(m));
+ }
+ if (sc->bus32bit) {
+ insl(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len,
+ lenthisone / 4);
+ m->m_len += (lenthisone & ~3);
+ if (lenthisone & 3)
+ insb(BASE + EP_W1_RX_PIO_RD_1,
+ mtod(m, caddr_t) + m->m_len,
+ lenthisone & 3);
+ m->m_len += (lenthisone & 3);
+ } else {
+ insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len,
+ lenthisone / 2);
+ m->m_len += lenthisone;
+ if (lenthisone & 1)
+ *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1);
+ }
+ totlen -= lenthisone;
+ }
+ if (off) {
+ top = sc->mb[sc->next_mb];
+ sc->mb[sc->next_mb] = 0;
+ if (top == 0) {
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (top == 0) {
+ top = m0;
+ goto out;
+ }
+ } else {
+ /* Convert one of our saved mbuf's */
+ sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
+ top->m_data = top->m_pktdat;
+ top->m_flags = M_PKTHDR;
+ }
+ insw(BASE + EP_W1_RX_PIO_RD_1, mtod(top, caddr_t),
+ sizeof(struct ether_header));
+ top->m_next = m0;
+ top->m_len = sizeof(struct ether_header);
+ /* XXX Accomodate for type and len from beginning of trailer */
+ top->m_pkthdr.len = save_totlen - (2 * sizeof(u_short));
+ } else {
+ top = m0;
+ top->m_pkthdr.len = save_totlen;
+ }
+
+ top->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ ;
+ ++sc->arpcom.ac_if.if_ipackets;
+#if NBPFILTER > 0
+ if (sc->bpf) {
+ bpf_mtap(sc->bpf, top);
+
+ /*
+ * Note that the interface cannot be in promiscuous mode if
+ * there are no BPF listeners. And if we are in promiscuous
+ * mode, we have to check if this packet is really ours.
+ */
+ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) &&
+ (eh->ether_dhost[0] & 1) == 0 &&
+ bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
+ sizeof(eh->ether_dhost)) != 0 &&
+ bcmp(eh->ether_dhost, etherbroadcastaddr,
+ sizeof(eh->ether_dhost)) != 0) {
+ m_freem(top);
+ return;
+ }
+ }
+#endif
+ m_adj(top, sizeof(struct ether_header));
+ ether_input(&sc->arpcom.ac_if, eh, top);
+ return;
+
+out: outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ ;
+ if (top)
+ m_freem(top);
+
+}
+
+
+/*
+ * Look familiar?
+ */
+static int
+epioctl(ifp, cmd, data)
+ register struct ifnet *ifp;
+ int cmd;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *) data;
+ struct ep_softc *sc = &ep_softc[ifp->if_unit];
+ struct ifreq *ifr = (struct ifreq *) data;
+ int s, error = 0;
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ epinit(ifp->if_unit); /* before arpwhohas */
+ ((struct arpcom *) ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *) ifp, &IA_SIN(ifa)->sin_addr);
+ break;
+#endif
+#ifdef NS
+ case AF_NS:
+ {
+ register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+ if (ns_nullhost(*ina))
+ ina->x_host =
+ *(union ns_host *)(sc->arpcom.ac_enaddr);
+ else {
+ ifp->if_flags &= ~IFF_RUNNING;
+ bcopy((caddr_t) ina->x_host.c_host,
+ (caddr_t)sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr));
+ }
+ epinit(ifp->if_unit);
+ break;
+ }
+#endif
+ default:
+ epinit(ifp->if_unit);
+ break;
+ }
+ break;
+ case SIOCSIFFLAGS:
+ if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) {
+ ifp->if_flags &= ~IFF_RUNNING;
+ epstop(ifp->if_unit);
+ epmbufempty(sc);
+ break;
+ }
+ if (ifp->if_flags & IFF_UP && (ifp->if_flags & IFF_RUNNING) == 0)
+ epinit(ifp->if_unit);
+ break;
+#ifdef notdef
+ case SIOCGHWADDR:
+ bcopy((caddr_t) sc->sc_addr, (caddr_t) &ifr->ifr_data,
+ sizeof(sc->sc_addr));
+ break;
+#endif
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+
+void
+epreset(unit)
+ int unit;
+{
+ int s = splimp();
+
+ epstop(unit);
+ epinit(unit);
+ splx(s);
+}
+
+void
+epwatchdog(unit)
+ int unit;
+{
+ struct ep_softc *sc = &ep_softc[unit];
+
+ log(LOG_ERR, "ep%d: watchdog\n", unit);
+ ++sc->arpcom.ac_if.if_oerrors;
+
+ epreset(unit);
+}
+
+void
+epstop(unit)
+ int unit;
+{
+ struct ep_softc *sc = &ep_softc[unit];
+
+ outw(BASE + EP_COMMAND, RX_DISABLE);
+ outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ ;
+ outw(BASE + EP_COMMAND, TX_DISABLE);
+ outw(BASE + EP_COMMAND, STOP_TRANSCEIVER);
+ outw(BASE + EP_COMMAND, RX_RESET);
+ outw(BASE + EP_COMMAND, TX_RESET);
+ outw(BASE + EP_COMMAND, C_INTR_LATCH);
+ outw(BASE + EP_COMMAND, SET_RD_0_MASK);
+ outw(BASE + EP_COMMAND, SET_INTR_MASK);
+ outw(BASE + EP_COMMAND, SET_RX_FILTER);
+}
+
+
+/*
+ * This is adapted straight from the book. There's probably a better way.
+ */
+static int
+send_ID_sequence(port)
+ u_short port;
+{
+ char cx, al;
+
+ cx = 0x0ff;
+ al = 0x0ff;
+
+ outb(port, 0x0);
+ DELAY(1000);
+ outb(port, 0x0);
+ DELAY(1000);
+
+loop1: cx--;
+ outb(port, al);
+ if (!(al & 0x80)) {
+ al = al << 1;
+ goto loop1;
+ }
+ al = al << 1;
+ al ^= 0xcf;
+ if (cx)
+ goto loop1;
+
+ return(1);
+}
+
+
+/*
+ * We get eeprom data from the id_port given an offset into the
+ * eeprom. Basically; after the ID_sequence is sent to all of
+ * the cards; they enter the ID_CMD state where they will accept
+ * command requests. 0x80-0xbf loads the eeprom data. We then
+ * read the port 16 times and with every read; the cards check
+ * for contention (ie: if one card writes a 0 bit and another
+ * writes a 1 bit then the host sees a 0. At the end of the cycle;
+ * each card compares the data on the bus; if there is a difference
+ * then that card goes into ID_WAIT state again). In the meantime;
+ * one bit of data is returned in the AX register which is conveniently
+ * returned to us by inb(). Hence; we read 16 times getting one
+ * bit of data with each read.
+ */
+static u_short
+get_eeprom_data(id_port, offset)
+ int id_port;
+ int offset;
+{
+ int i, data = 0;
+ outb(id_port, 0x80 + offset);
+ DELAY(1000);
+ for (i = 0; i < 16; i++)
+ data = (data << 1) | (inw(id_port) & 1);
+ return (data);
+}
+
+static int
+is_eeprom_busy(is)
+ struct isa_device *is;
+{
+ int i = 0, j;
+ register struct ep_softc *sc = &ep_softc[is->id_unit];
+
+ while (i++ < 100) {
+ j = inw(BASE + EP_W0_EEPROM_COMMAND);
+ if (j & EEPROM_BUSY)
+ DELAY(100);
+ else
+ break;
+ }
+ if (i >= 100) {
+ printf("\nep%d: eeprom failed to come ready.\n", is->id_unit);
+ return (1);
+ }
+ if (j & EEPROM_TST_MODE) {
+ printf("\nep%d: 3c509 in test mode. Erase pencil mark!\n", is->id_unit);
+ return (1);
+ }
+ return (0);
+}
+
+void
+epmbuffill(sp)
+ caddr_t sp;
+{
+ struct ep_softc *sc = (struct ep_softc *)sp;
+ int s, i;
+
+ s = splimp();
+ i = sc->last_mb;
+ do {
+ if(sc->mb[i] == NULL)
+ MGET(sc->mb[i], M_DONTWAIT, MT_DATA);
+ if(sc->mb[i] == NULL)
+ break;
+ i = (i + 1) % MAX_MBS;
+ } while (i != sc->next_mb);
+ sc->last_mb = i;
+ splx(s);
+}
+
+static void
+epmbufempty(sc)
+ struct ep_softc *sc;
+{
+ int s, i;
+
+ s = splimp();
+ for (i = 0; i<MAX_MBS; i++) {
+ if (sc->mb[i]) {
+ m_freem(sc->mb[i]);
+ sc->mb[i] = NULL;
+ }
+ }
+ sc->last_mb = sc->next_mb = 0;
+ untimeout(epmbuffill, sc);
+ splx(s);
+}
+
+#endif /* NEP > 0 */
diff --git a/sys/dev/ep/if_epreg.h b/sys/dev/ep/if_epreg.h
new file mode 100644
index 0000000..f0b4cd9
--- /dev/null
+++ b/sys/dev/ep/if_epreg.h
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: if_epreg.h,v 1.1 1993/12/14 04:26:47 hpeyerl Exp $
+ */
+/**************************************************************************
+ * *
+ * These define the EEPROM data structure. They are used in the probe
+ * function to verify the existance of the adapter after having sent
+ * the ID_Sequence.
+ *
+ * There are others but only the ones we use are defined here.
+ *
+ **************************************************************************/
+
+#define EEPROM_NODE_ADDR_0 0x0 /* Word */
+#define EEPROM_NODE_ADDR_1 0x1 /* Word */
+#define EEPROM_NODE_ADDR_2 0x2 /* Word */
+#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */
+#define EEPROM_MFG_ID 0x7 /* 0x6d50 */
+#define EEPROM_ADDR_CFG 0x8 /* Base addr */
+#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */
+
+/**************************************************************************
+ * *
+ * These are the registers for the 3Com 3c509 and their bit patterns when *
+ * applicable. They have been taken out the the "EtherLink III Parallel *
+ * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual *
+ * from 3com. *
+ * *
+ **************************************************************************/
+
+#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a command reg. */
+#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status reg. */
+#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window reg. */
+/*
+ * Window 0 registers. Setup.
+ */
+ /* Write */
+#define EP_W0_EEPROM_DATA 0x0c
+#define EP_W0_EEPROM_COMMAND 0x0a
+#define EP_W0_RESOURCE_CFG 0x08
+#define EP_W0_ADDRESS_CFG 0x06
+#define EP_W0_CONFIG_CTRL 0x04
+ /* Read */
+#define EP_W0_PRODUCT_ID 0x02
+#define EP_W0_MFG_ID 0x00
+
+/*
+ * Window 1 registers. Operating Set.
+ */
+ /* Write */
+#define EP_W1_TX_PIO_WR_2 0x02
+#define EP_W1_TX_PIO_WR_1 0x00
+ /* Read */
+#define EP_W1_FREE_TX 0x0c
+#define EP_W1_TX_STATUS 0x0b /* byte */
+#define EP_W1_TIMER 0x0a /* byte */
+#define EP_W1_RX_STATUS 0x08
+#define EP_W1_RX_PIO_RD_2 0x02
+#define EP_W1_RX_PIO_RD_1 0x00
+
+/*
+ * Window 2 registers. Station Address Setup/Read
+ */
+ /* Read/Write */
+#define EP_W2_ADDR_5 0x05
+#define EP_W2_ADDR_4 0x04
+#define EP_W2_ADDR_3 0x03
+#define EP_W2_ADDR_2 0x02
+#define EP_W2_ADDR_1 0x01
+#define EP_W2_ADDR_0 0x00
+
+/*
+ * Window 3 registers. FIFO Management.
+ */
+ /* Read */
+#define EP_W3_FREE_TX 0x0c
+#define EP_W3_FREE_RX 0x0a
+
+/*
+ * Window 4 registers. Diagnostics.
+ */
+ /* Read/Write */
+#define EP_W4_MEDIA_TYPE 0x0a
+#define EP_W4_CTRLR_STATUS 0x08
+#define EP_W4_NET_DIAG 0x06
+#define EP_W4_FIFO_DIAG 0x04
+#define EP_W4_HOST_DIAG 0x02
+#define EP_W4_TX_DIAG 0x00
+
+/*
+ * Window 5 Registers. Results and Internal status.
+ */
+ /* Read */
+#define EP_W5_READ_0_MASK 0x0c
+#define EP_W5_INTR_MASK 0x0a
+#define EP_W5_RX_FILTER 0x08
+#define EP_W5_RX_EARLY_THRESH 0x06
+#define EP_W5_TX_AVAIL_THRESH 0x02
+#define EP_W5_TX_START_THRESH 0x00
+
+/*
+ * Window 6 registers. Statistics.
+ */
+ /* Read/Write */
+#define TX_TOTAL_OK 0x0c
+#define RX_TOTAL_OK 0x0a
+#define TX_DEFERRALS 0x08
+#define RX_FRAMES_OK 0x07
+#define TX_FRAMES_OK 0x06
+#define RX_OVERRUNS 0x05
+#define TX_COLLISIONS 0x04
+#define TX_AFTER_1_COLLISION 0x03
+#define TX_AFTER_X_COLLISIONS 0x02
+#define TX_NO_SQE 0x01
+#define TX_CD_LOST 0x00
+
+/****************************************
+ *
+ * Register definitions.
+ *
+ ****************************************/
+
+/*
+ * Command register. All windows.
+ *
+ * 16 bit register.
+ * 15-11: 5-bit code for command to be executed.
+ * 10-0: 11-bit arg if any. For commands with no args;
+ * this can be set to anything.
+ */
+#define GLOBAL_RESET (u_short) 0x0000 /* Wait at least 1ms after issuing */
+#define WINDOW_SELECT (u_short) (0x1<<11)
+#define START_TRANSCEIVER (u_short) (0x2<<11) /* Read ADDR_CFG reg to determine
+ whether this is needed. If so;
+ wait 800 uSec before using trans-
+ ceiver. */
+#define RX_DISABLE (u_short) (0x3<<11) /* state disabled on power-up */
+#define RX_ENABLE (u_short) (0x4<<11)
+#define RX_RESET (u_short) (0x5<<11)
+#define RX_DISCARD_TOP_PACK (u_short) (0x8<<11)
+#define TX_ENABLE (u_short) (0x9<<11)
+#define TX_DISABLE (u_short) (0xa<<11)
+#define TX_RESET (u_short) (0xb<<11)
+#define REQ_INTR (u_short) (0xc<<11)
+ /*
+ * The following C_* acknowledge the various interrupts.
+ * Some of them don't do anything. See the manual.
+ */
+#define ACK_INTR (u_short) (0x6800)
+# define C_INTR_LATCH (u_short) (ACK_INTR|0x1)
+# define C_CARD_FAILURE (u_short) (ACK_INTR|0x2)
+# define C_TX_COMPLETE (u_short) (ACK_INTR|0x4)
+# define C_TX_AVAIL (u_short) (ACK_INTR|0x8)
+# define C_RX_COMPLETE (u_short) (ACK_INTR|0x10)
+# define C_RX_EARLY (u_short) (ACK_INTR|0x20)
+# define C_INT_RQD (u_short) (ACK_INTR|0x40)
+# define C_UPD_STATS (u_short) (ACK_INTR|0x80)
+#define SET_INTR_MASK (u_short) (0xe<<11)
+#define SET_RD_0_MASK (u_short) (0xf<<11)
+#define SET_RX_FILTER (u_short) (0x10<<11)
+# define FIL_INDIVIDUAL (u_short) (0x1)
+# define FIL_GROUP (u_short) (0x2)
+# define FIL_BRDCST (u_short) (0x4)
+# define FIL_ALL (u_short) (0x8)
+#define SET_RX_EARLY_THRESH (u_short) (0x11<<11)
+#define SET_TX_AVAIL_THRESH (u_short) (0x12<<11)
+#define SET_TX_START_THRESH (u_short) (0x13<<11)
+#define STATS_ENABLE (u_short) (0x15<<11)
+#define STATS_DISABLE (u_short) (0x16<<11)
+#define STOP_TRANSCEIVER (u_short) (0x17<<11)
+
+/*
+ * Status register. All windows.
+ *
+ * 15-13: Window number(0-7).
+ * 12: Command_in_progress.
+ * 11: reserved.
+ * 10: reserved.
+ * 9: reserved.
+ * 8: reserved.
+ * 7: Update Statistics.
+ * 6: Interrupt Requested.
+ * 5: RX Early.
+ * 4: RX Complete.
+ * 3: TX Available.
+ * 2: TX Complete.
+ * 1: Adapter Failure.
+ * 0: Interrupt Latch.
+ */
+#define S_INTR_LATCH (u_short) (0x1)
+#define S_CARD_FAILURE (u_short) (0x2)
+#define S_TX_COMPLETE (u_short) (0x4)
+#define S_TX_AVAIL (u_short) (0x8)
+#define S_RX_COMPLETE (u_short) (0x10)
+#define S_RX_EARLY (u_short) (0x20)
+#define S_INT_RQD (u_short) (0x40)
+#define S_UPD_STATS (u_short) (0x80)
+#define S_COMMAND_IN_PROGRESS (u_short) (0x1000)
+
+/*
+ * FIFO Registers. RX Status.
+ *
+ * 15: Incomplete or FIFO empty.
+ * 14: 1: Error in RX Packet 0: Incomplete or no error.
+ * 13-11: Type of error.
+ * 1000 = Overrun.
+ * 1011 = Run Packet Error.
+ * 1100 = Alignment Error.
+ * 1101 = CRC Error.
+ * 1001 = Oversize Packet Error (>1514 bytes)
+ * 0010 = Dribble Bits.
+ * (all other error codes, no errors.)
+ *
+ * 10-0: RX Bytes (0-1514)
+ */
+#define ERR_INCOMPLETE (u_short) (0x8000)
+#define ERR_RX (u_short) (0x4000)
+#define ERR_RX_PACKET (u_short) (0x2000)
+#define ERR_OVERRUN (u_short) (0x1000)
+#define ERR_RUNT (u_short) (0x1300)
+#define ERR_ALIGNMENT (u_short) (0x1400)
+#define ERR_CRC (u_short) (0x1500)
+#define ERR_OVERSIZE (u_short) (0x1100)
+#define ERR_DRIBBLE (u_short) (0x200)
+
+/*
+ * TX Status
+ *
+ * Reports the transmit status of a completed transmission. Writing this
+ * register pops the transmit completion stack.
+ *
+ * Window 1/Port 0x0b.
+ *
+ * 7: Complete
+ * 6: Interrupt on successful transmission requested.
+ * 5: Jabber Error (TP Only, TX Reset required. )
+ * 4: Underrun (TX Reset required. )
+ * 3: Maximum Collisions.
+ * 2: TX Status Overflow.
+ * 1-0: Undefined.
+ *
+ */
+#define TXS_COMPLETE 0x80
+#define TXS_INTR_REQ 0x40
+#define TXS_JABBER 0x20
+#define TXS_UNDERRUN 0x10
+#define TXS_MAX_COLLISION 0x8
+#define TXS_STATUS_OVERFLOW 0x4
+
+/*
+ * Misc defines for various things.
+ */
+#define TAG_ADAPTER_0 0xd0
+#define ACTIVATE_ADAPTER_TO_CONFIG 0xff
+#define ENABLE_DRQ_IRQ 0x0001
+#define MFG_ID 0x6d50
+#define PROD_ID 0x9150
+#define BASE sc->ep_io_addr
+#define GO_WINDOW(x) outw(BASE+EP_COMMAND, WINDOW_SELECT|x)
+#define AUI 0x1
+#define BNC 0x2
+#define UTP 0x4
+#define IS_AUI (1<<13)
+#define IS_BNC (1<<12)
+#define IS_UTP (1<<9)
+#define EEPROM_BUSY (1<<15)
+#define EEPROM_TST_MODE (1<<14)
+#define READ_EEPROM (1<<7)
+#define ETHER_ADDR_LEN 6
+#define ETHER_MAX 1536
+#define ENABLE_UTP 0xc0
+#define DISABLE_UTP 0x0
+#define RX_BYTES_MASK (u_short) (0x07ff)
diff --git a/sys/dev/fdc/fdc.c b/sys/dev/fdc/fdc.c
new file mode 100644
index 0000000..259d451
--- /dev/null
+++ b/sys/dev/fdc/fdc.c
@@ -0,0 +1,1255 @@
+/*#define DEBUG 1*/
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Don Ahn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)fd.c 7.4 (Berkeley) 5/25/91
+ * $Id: fd.c,v 1.24 1994/03/08 16:25:29 nate Exp $
+ *
+ */
+
+#include "ft.h"
+#if NFT < 1
+#undef NFDC
+#endif
+#include "fd.h"
+
+#if NFDC > 0
+
+#include <sys/param.h>
+#include <sys/dkbad.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <machine/ioctl_fd.h>
+#include <sys/disklabel.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/syslog.h>
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/fdreg.h"
+#include "i386/isa/fdc.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/rtc.h"
+
+#if NFT > 0
+extern int ftopen(), ftintr(), ftattach(), ftclose(), ftioctl();
+#endif
+
+#define b_cylin b_resid
+#define FDBLK 512
+
+/* misuse a flag to identify format operation */
+#define B_FORMAT B_XXX
+
+#define NUMTYPES 14
+#define NUMDENS (NUMTYPES - 6)
+
+/* This defines (-1) must match index for fd_types */
+#define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */
+#define NO_TYPE 0 /* must match NO_TYPE in ft.c */
+#define FD_1720 1
+#define FD_1480 2
+#define FD_1440 3
+#define FD_1200 4
+#define FD_820 5
+#define FD_800 6
+#define FD_720 7
+#define FD_360 8
+
+#define FD_1480in5_25 9
+#define FD_1440in5_25 10
+#define FD_820in5_25 11
+#define FD_800in5_25 12
+#define FD_720in5_25 13
+#define FD_360in5_25 14
+
+
+struct fd_type fd_types[NUMTYPES] =
+{
+{ 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
+{ 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
+{ 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
+{ 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */
+{ 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */
+{ 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */
+{ 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */
+{ 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */
+
+{ 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
+{ 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
+{ 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */
+{ 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */
+{ 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */
+{ 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */
+};
+
+#define DRVS_PER_CTLR 2 /* 2 floppies */
+/***********************************************************************\
+* Per controller structure. *
+\***********************************************************************/
+struct fdc_data fdc_data[NFDC];
+
+/***********************************************************************\
+* Per drive structure. *
+* N per controller (DRVS_PER_CTLR) *
+\***********************************************************************/
+struct fd_data {
+ struct fdc_data *fdc; /* pointer to controller structure */
+ int fdsu; /* this units number on this controller */
+ int type; /* Drive type (HD, DD */
+ struct fd_type *ft; /* pointer to the type descriptor */
+ int flags;
+#define FD_OPEN 0x01 /* it's open */
+#define FD_ACTIVE 0x02 /* it's active */
+#define FD_MOTOR 0x04 /* motor should be on */
+#define FD_MOTOR_WAIT 0x08 /* motor coming up */
+ int skip;
+ int hddrv;
+ int track; /* where we think the head is */
+} fd_data[NFD];
+
+/***********************************************************************\
+* Throughout this file the following conventions will be used: *
+* fd is a pointer to the fd_data struct for the drive in question *
+* fdc is a pointer to the fdc_data struct for the controller *
+* fdu is the floppy drive unit number *
+* fdcu is the floppy controller unit number *
+* fdsu is the floppy drive unit number on that controller. (sub-unit) *
+\***********************************************************************/
+
+#define id_physid id_scsiid /* this biotab field doubles as a field */
+ /* for the physical unit number on the controller */
+
+static int retrier(fdcu_t);
+
+#define DEVIDLE 0
+#define FINDWORK 1
+#define DOSEEK 2
+#define SEEKCOMPLETE 3
+#define IOCOMPLETE 4
+#define RECALCOMPLETE 5
+#define STARTRECAL 6
+#define RESETCTLR 7
+#define SEEKWAIT 8
+#define RECALWAIT 9
+#define MOTORWAIT 10
+#define IOTIMEDOUT 11
+
+#ifdef DEBUG
+char *fdstates[] =
+{
+"DEVIDLE",
+"FINDWORK",
+"DOSEEK",
+"SEEKCOMPLETE",
+"IOCOMPLETE",
+"RECALCOMPLETE",
+"STARTRECAL",
+"RESETCTLR",
+"SEEKWAIT",
+"RECALWAIT",
+"MOTORWAIT",
+"IOTIMEDOUT"
+};
+
+
+int fd_debug = 1;
+#define TRACE0(arg) if(fd_debug) printf(arg)
+#define TRACE1(arg1,arg2) if(fd_debug) printf(arg1,arg2)
+#else /* DEBUG */
+#define TRACE0(arg)
+#define TRACE1(arg1,arg2)
+#endif /* DEBUG */
+
+static void fdstart(fdcu_t);
+void fdintr(fdcu_t);
+static void fd_turnoff(caddr_t);
+
+/****************************************************************************/
+/* autoconfiguration stuff */
+/****************************************************************************/
+static int fdprobe(struct isa_device *);
+static int fdattach(struct isa_device *);
+
+struct isa_driver fdcdriver = {
+ fdprobe, fdattach, "fdc",
+};
+
+/*
+ * probe for existance of controller
+ */
+int
+fdprobe(dev)
+ struct isa_device *dev;
+{
+ fdcu_t fdcu = dev->id_unit;
+ if(fdc_data[fdcu].flags & FDC_ATTACHED)
+ {
+ printf("fdc: same unit (%d) used multiple times\n",fdcu);
+ return 0;
+ }
+
+ fdc_data[fdcu].baseport = dev->id_iobase;
+
+ /* First - lets reset the floppy controller */
+
+ outb(dev->id_iobase+fdout,0);
+ DELAY(100);
+ outb(dev->id_iobase+fdout,FDO_FRST);
+
+ /* see if it can handle a command */
+ if (out_fdc(fdcu,NE7CMD_SPECIFY) < 0)
+ {
+ return(0);
+ }
+ out_fdc(fdcu,0xDF);
+ out_fdc(fdcu,2);
+ return (IO_FDCSIZE);
+}
+
+/*
+ * wire controller into system, look for floppy units
+ */
+int
+fdattach(dev)
+ struct isa_device *dev;
+{
+ unsigned fdt,st0, cyl;
+ int hdr;
+ fdu_t fdu;
+ fdcu_t fdcu = dev->id_unit;
+ fdc_p fdc = fdc_data + fdcu;
+ fd_p fd;
+ int fdsu;
+ struct isa_device *fdup;
+
+ fdc->fdcu = fdcu;
+ fdc->flags |= FDC_ATTACHED;
+ fdc->dmachan = dev->id_drq;
+ fdc->state = DEVIDLE;
+ hdr = 0;
+ printf("fdc%d:", fdcu);
+
+ /* check for each floppy drive */
+ for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) {
+ if (fdup->id_iobase != dev->id_iobase)
+ continue;
+ fdu = fdup->id_unit;
+ fd = &fd_data[fdu];
+ if (fdu >= (NFD+NFT))
+ continue;
+ fdsu = fdup->id_physid;
+ /* look up what bios thinks we have */
+ switch (fdu) {
+ case 0: fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
+ break;
+ case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
+ break;
+ default: fdt = RTCFDT_NONE;
+ break;
+ }
+ /* is there a unit? */
+ if ((fdt == RTCFDT_NONE)
+#if NFT > 0
+ || (fdsu >= DRVS_PER_CTLR)) {
+#else
+ ) {
+ fd->type = NO_TYPE;
+#endif
+#if NFT > 0
+ /* If BIOS says no floppy, or > 2nd device */
+ /* Probe for and attach a floppy tape. */
+ if (ftattach(dev, fdup))
+ continue;
+ if (fdsu < DRVS_PER_CTLR)
+ fd->type = NO_TYPE;
+#endif
+ continue;
+ }
+
+#ifdef notyet
+ /* select it */
+ fd_turnon1(fdu);
+ spinwait(1000); /* 1 sec */
+ out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */
+ out_fdc(fdcu,fdsu);
+ spinwait(1000); /* 1 sec */
+
+ /* anything responding */
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ if (st0 & 0xd0)
+ continue;
+
+#endif
+ fd->track = -2;
+ fd->fdc = fdc;
+ fd->fdsu = fdsu;
+ printf(" [%d: fd%d: ", fdsu, fdu);
+
+ switch (fdt) {
+ case RTCFDT_12M:
+ printf("1.2MB 5.25in]");
+ fd->type = FD_1200;
+ break;
+ case RTCFDT_144M:
+ printf("1.44MB 3.5in]");
+ fd->type = FD_1440;
+ break;
+ case RTCFDT_360K:
+ printf("360KB 5.25in]");
+ fd->type = FD_360;
+ break;
+ case RTCFDT_720K:
+ printf("720KB 3.5in]");
+ fd->type = FD_720;
+ break;
+ default:
+ printf("unknown]");
+ fd->type = NO_TYPE;
+ break;
+ }
+
+ fd_turnoff((caddr_t)fdu);
+ hdr = 1;
+ }
+ printf("\n");
+
+ /* Set transfer to 500kbps */
+ outb(fdc->baseport+fdctl,0); /*XXX*/
+ return 1;
+}
+
+int
+fdsize(dev)
+ dev_t dev;
+{
+ return(0);
+}
+
+/****************************************************************************/
+/* fdstrategy */
+/****************************************************************************/
+void fdstrategy(struct buf *bp)
+{
+ register struct buf *dp,*dp0,*dp1;
+ long nblocks,blknum;
+ int s;
+ fdcu_t fdcu;
+ fdu_t fdu;
+ fdc_p fdc;
+ fd_p fd;
+
+ fdu = FDUNIT(minor(bp->b_dev));
+ fd = &fd_data[fdu];
+ fdc = fd->fdc;
+ fdcu = fdc->fdcu;
+
+#if NFT > 0
+ /* check for controller already busy with tape */
+ if (fdc->flags & FDC_TAPE_BUSY) {
+ bp->b_error = EBUSY;
+ bp->b_flags |= B_ERROR;
+ return;
+ }
+#endif
+ if ((fdu >= NFD) || (bp->b_blkno < 0)) {
+ printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n",
+ fdu, bp->b_blkno, bp->b_bcount);
+ pg("fd:error in fdstrategy");
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ goto bad;
+ }
+ /*
+ * Set up block calculations.
+ */
+ blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK;
+ nblocks = fd->ft->size;
+ if (blknum + (bp->b_bcount / FDBLK) > nblocks) {
+ if (blknum == nblocks) {
+ bp->b_resid = bp->b_bcount;
+ } else {
+ bp->b_error = ENOSPC;
+ bp->b_flags |= B_ERROR;
+ }
+ goto bad;
+ }
+ bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads);
+ bp->b_pblkno = bp->b_blkno;
+ dp = &(fdc->head);
+ s = splbio();
+ disksort(dp, bp);
+ untimeout((timeout_func_t)fd_turnoff, (caddr_t)fdu); /* a good idea */
+ fdstart(fdcu);
+ splx(s);
+ return;
+
+bad:
+ biodone(bp);
+ return;
+}
+
+/****************************************************************************/
+/* motor control stuff */
+/* remember to not deselect the drive we're working on */
+/****************************************************************************/
+void
+set_motor(fdcu, fdu, reset)
+ fdcu_t fdcu;
+ fdu_t fdu;
+ int reset;
+{
+ int m0,m1;
+ int selunit;
+ fd_p fd;
+ if(fd = fdc_data[fdcu].fd)/* yes an assign! */
+ {
+ selunit = fd->fdsu;
+ }
+ else
+ {
+ selunit = 0;
+ }
+ m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR;
+ m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR;
+ outb(fdc_data[fdcu].baseport+fdout,
+ selunit
+ | (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
+ | (m0 ? FDO_MOEN0 : 0)
+ | (m1 ? FDO_MOEN1 : 0));
+ TRACE1("[0x%x->fdout]",(
+ selunit
+ | (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
+ | (m0 ? FDO_MOEN0 : 0)
+ | (m1 ? FDO_MOEN1 : 0)));
+}
+
+static void
+fd_turnoff(caddr_t arg1)
+{
+ fdu_t fdu = (fdu_t)arg1;
+ int s;
+
+ fd_p fd = fd_data + fdu;
+ s = splbio();
+ fd->flags &= ~FD_MOTOR;
+ set_motor(fd->fdc->fdcu,fd->fdsu,0);
+ splx(s);
+}
+
+void
+fd_motor_on(caddr_t arg1)
+{
+ fdu_t fdu = (fdu_t)arg1;
+ int s;
+
+ fd_p fd = fd_data + fdu;
+ s = splbio();
+ fd->flags &= ~FD_MOTOR_WAIT;
+ if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
+ {
+ fdintr(fd->fdc->fdcu);
+ }
+ splx(s);
+}
+
+static void fd_turnon1(fdu_t);
+
+void
+fd_turnon(fdu)
+ fdu_t fdu;
+{
+ fd_p fd = fd_data + fdu;
+ if(!(fd->flags & FD_MOTOR))
+ {
+ fd_turnon1(fdu);
+ fd->flags |= FD_MOTOR_WAIT;
+ timeout((timeout_func_t)fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */
+ }
+}
+
+static void
+fd_turnon1(fdu_t fdu)
+{
+ fd_p fd = fd_data + fdu;
+ fd->flags |= FD_MOTOR;
+ set_motor(fd->fdc->fdcu,fd->fdsu,0);
+}
+
+/****************************************************************************/
+/* fdc in/out */
+/****************************************************************************/
+int
+in_fdc(fdcu)
+ fdcu_t fdcu;
+{
+ int baseport = fdc_data[fdcu].baseport;
+ int i, j = 100000;
+ while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM))
+ != (NE7_DIO|NE7_RQM) && j-- > 0)
+ if (i == NE7_RQM) return -1;
+ if (j <= 0)
+ return(-1);
+#ifdef DEBUG
+ i = inb(baseport+fddata);
+ TRACE1("[fddata->0x%x]",(unsigned char)i);
+ return(i);
+#else
+ return inb(baseport+fddata);
+#endif
+}
+
+int
+out_fdc(fdcu, x)
+ fdcu_t fdcu;
+ int x;
+{
+ int baseport = fdc_data[fdcu].baseport;
+ int i;
+
+ /* Check that the direction bit is set */
+ i = 100000;
+ while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0);
+ if (i <= 0) return (-1); /* Floppy timed out */
+
+ /* Check that the floppy controller is ready for a command */
+ i = 100000;
+ while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0);
+ if (i <= 0) return (-1); /* Floppy timed out */
+
+ /* Send the command and return */
+ outb(baseport+fddata,x);
+ TRACE1("[0x%x->fddata]",x);
+ return (0);
+}
+
+/****************************************************************************/
+/* fdopen/fdclose */
+/****************************************************************************/
+int
+Fdopen(dev, flags)
+ dev_t dev;
+ int flags;
+{
+ fdu_t fdu = FDUNIT(minor(dev));
+ int type = FDTYPE(minor(dev));
+ fdc_p fdc;
+
+#if NFT > 0
+ /* check for a tape open */
+ if (type & F_TAPE_TYPE)
+ return(ftopen(dev, flags));
+#endif
+ /* check bounds */
+ if (fdu >= NFD)
+ return(ENXIO);
+ fdc = fd_data[fdu].fdc;
+ if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE))
+ return(ENXIO);
+ if (type > NUMDENS)
+ return(ENXIO);
+ if (type == 0)
+ type = fd_data[fdu].type;
+ else {
+ if (type != fd_data[fdu].type) {
+ switch (fd_data[fdu].type) {
+ case FD_360:
+ return(ENXIO);
+ case FD_720:
+ if ( type != FD_820
+ && type != FD_800
+ )
+ return(ENXIO);
+ break;
+ case FD_1200:
+ switch (type) {
+ case FD_1480:
+ type = FD_1480in5_25;
+ break;
+ case FD_1440:
+ type = FD_1440in5_25;
+ break;
+ case FD_820:
+ type = FD_820in5_25;
+ break;
+ case FD_800:
+ type = FD_800in5_25;
+ break;
+ case FD_720:
+ type = FD_720in5_25;
+ break;
+ case FD_360:
+ type = FD_360in5_25;
+ break;
+ default:
+ return(ENXIO);
+ }
+ break;
+ case FD_1440:
+ if ( type != FD_1720
+ && type != FD_1480
+ && type != FD_1200
+ && type != FD_820
+ && type != FD_800
+ && type != FD_720
+ )
+ return(ENXIO);
+ break;
+ }
+ }
+ }
+ fd_data[fdu].ft = fd_types + type - 1;
+ fd_data[fdu].flags |= FD_OPEN;
+
+ return 0;
+}
+
+int
+fdclose(dev, flags)
+ dev_t dev;
+ int flags;
+{
+ fdu_t fdu = FDUNIT(minor(dev));
+ int type = FDTYPE(minor(dev));
+
+#if NFT > 0
+ if (type & F_TAPE_TYPE)
+ return ftclose(0);
+#endif
+ fd_data[fdu].flags &= ~FD_OPEN;
+ return(0);
+}
+
+
+/***************************************************************\
+* fdstart *
+* We have just queued something.. if the controller is not busy *
+* then simulate the case where it has just finished a command *
+* So that it (the interrupt routine) looks on the queue for more*
+* work to do and picks up what we just added. *
+* If the controller is already busy, we need do nothing, as it *
+* will pick up our work when the present work completes *
+\***************************************************************/
+static void
+fdstart(fdcu)
+ fdcu_t fdcu;
+{
+ register struct buf *dp,*bp;
+ int s;
+ fdu_t fdu;
+
+ s = splbio();
+ if(fdc_data[fdcu].state == DEVIDLE)
+ {
+ fdintr(fdcu);
+ }
+ splx(s);
+}
+
+static void
+fd_timeout(caddr_t arg1)
+{
+ fdcu_t fdcu = (fdcu_t)arg1;
+ fdu_t fdu = fdc_data[fdcu].fdu;
+ int st0, st3, cyl;
+ struct buf *dp,*bp;
+ int s;
+
+ dp = &fdc_data[fdcu].head;
+ s = splbio();
+ bp = dp->b_actf;
+
+ out_fdc(fdcu,NE7CMD_SENSED);
+ out_fdc(fdcu,fd_data[fdu].hddrv);
+ st3 = in_fdc(fdcu);
+
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n",
+ fdu,
+ st0,
+ NE7_ST0BITS,
+ cyl,
+ st3,
+ NE7_ST3BITS);
+
+ if (bp)
+ {
+ retrier(fdcu);
+ fdc_data[fdcu].status[0] = 0xc0;
+ fdc_data[fdcu].state = IOTIMEDOUT;
+ if( fdc_data[fdcu].retry < 6)
+ fdc_data[fdcu].retry = 6;
+ }
+ else
+ {
+ fdc_data[fdcu].fd = (fd_p) 0;
+ fdc_data[fdcu].fdu = -1;
+ fdc_data[fdcu].state = DEVIDLE;
+ }
+ fdintr(fdcu);
+ splx(s);
+}
+
+/* just ensure it has the right spl */
+static void
+fd_pseudointr(caddr_t arg1, int arg2)
+{
+ fdcu_t fdcu = (fdcu_t)arg1;
+ int s;
+ s = splbio();
+ fdintr(fdcu);
+ splx(s);
+}
+
+/***********************************************************************\
+* fdintr *
+* keep calling the state machine until it returns a 0 *
+* ALWAYS called at SPLBIO *
+\***********************************************************************/
+void
+fdintr(fdcu_t fdcu)
+{
+ fdc_p fdc = fdc_data + fdcu;
+#if NFT > 0
+ fdu_t fdu = fdc->fdu;
+
+ if (fdc->flags & FDC_TAPE_BUSY)
+ (ftintr(fdu));
+ else
+#endif
+ while(fdstate(fdcu, fdc))
+ ;
+}
+
+/***********************************************************************\
+* The controller state machine. *
+* if it returns a non zero value, it should be called again immediatly *
+\***********************************************************************/
+int
+fdstate(fdcu, fdc)
+ fdcu_t fdcu;
+ fdc_p fdc;
+{
+ int read, format, head, trac, sec = 0, i = 0, s, sectrac, cyl, st0;
+ unsigned long blknum;
+ fdu_t fdu = fdc->fdu;
+ fd_p fd;
+ register struct buf *dp,*bp;
+ struct fd_formb *finfo = NULL;
+
+ dp = &(fdc->head);
+ bp = dp->b_actf;
+ if(!bp)
+ {
+ /***********************************************\
+ * nothing left for this controller to do *
+ * Force into the IDLE state, *
+ \***********************************************/
+ fdc->state = DEVIDLE;
+ if(fdc->fd)
+ {
+ printf("unexpected valid fd pointer (fdu = %d)\n"
+ ,fdc->fdu);
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ }
+ TRACE1("[fdc%d IDLE]",fdcu);
+ return(0);
+ }
+ fdu = FDUNIT(minor(bp->b_dev));
+ fd = fd_data + fdu;
+ if (fdc->fd && (fd != fdc->fd))
+ {
+ printf("confused fd pointers\n");
+ }
+ read = bp->b_flags & B_READ;
+ format = bp->b_flags & B_FORMAT;
+ if(format)
+ finfo = (struct fd_formb *)bp->b_un.b_addr;
+ TRACE1("fd%d",fdu);
+ TRACE1("[%s]",fdstates[fdc->state]);
+ TRACE1("(0x%x)",fd->flags);
+ untimeout((timeout_func_t)fd_turnoff, (caddr_t)fdu);
+ timeout((timeout_func_t)fd_turnoff, (caddr_t)fdu, 4 * hz);
+ switch (fdc->state)
+ {
+ case DEVIDLE:
+ case FINDWORK: /* we have found new work */
+ fdc->retry = 0;
+ fd->skip = 0;
+ fdc->fd = fd;
+ fdc->fdu = fdu;
+ outb(fdc->baseport+fdctl, fd->ft->trans);
+ /*******************************************************\
+ * If the next drive has a motor startup pending, then *
+ * it will start up in it's own good time *
+ \*******************************************************/
+ if(fd->flags & FD_MOTOR_WAIT)
+ {
+ fdc->state = MOTORWAIT;
+ return(0); /* come back later */
+ }
+ /*******************************************************\
+ * Maybe if it's not starting, it SHOULD be starting *
+ \*******************************************************/
+ if (!(fd->flags & FD_MOTOR))
+ {
+ fdc->state = MOTORWAIT;
+ fd_turnon(fdu);
+ return(0);
+ }
+ else /* at least make sure we are selected */
+ {
+ set_motor(fdcu,fd->fdsu,0);
+ }
+ fdc->state = DOSEEK;
+ break;
+ case DOSEEK:
+ if (bp->b_cylin == fd->track)
+ {
+ fdc->state = SEEKCOMPLETE;
+ break;
+ }
+ out_fdc(fdcu,NE7CMD_SEEK); /* Seek function */
+ out_fdc(fdcu,fd->fdsu); /* Drive number */
+ out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac);
+ fd->track = -2;
+ fdc->state = SEEKWAIT;
+ timeout((timeout_func_t)fd_timeout, (caddr_t)fdcu, 2 * hz);
+ return(0); /* will return later */
+ case SEEKWAIT:
+ untimeout((timeout_func_t)fd_timeout, (caddr_t)fdcu);
+ /* allow heads to settle */
+ timeout((timeout_func_t)fd_pseudointr, (caddr_t)fdcu, hz / 50);
+ fdc->state = SEEKCOMPLETE;
+ return(0); /* will return later */
+ break;
+
+ case SEEKCOMPLETE : /* SEEK DONE, START DMA */
+ /* Make sure seek really happened*/
+ if(fd->track == -2)
+ {
+ int descyl = bp->b_cylin * fd->ft->steptrac;
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ i = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ if (cyl != descyl)
+ {
+ printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
+ fdu, descyl, cyl, i, NE7_ST0BITS);
+ return(retrier(fdcu));
+ }
+ }
+
+ fd->track = bp->b_cylin;
+ if(format)
+ fd->skip = (char *)&(finfo->fd_formb_cylno(0))
+ - (char *)finfo;
+ isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip,
+ format ? bp->b_bcount : FDBLK, fdc->dmachan);
+ blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
+ + fd->skip/FDBLK;
+ sectrac = fd->ft->sectrac;
+ sec = blknum % (sectrac * fd->ft->heads);
+ head = sec / sectrac;
+ sec = sec % sectrac + 1;
+/*XXX*/ fd->hddrv = ((head&1)<<2)+fdu;
+
+ if(format)
+ {
+ /* formatting */
+ out_fdc(fdcu,/* NE7CMD_FORMAT */ 0x4d);
+ out_fdc(fdcu,head << 2 | fdu);
+ out_fdc(fdcu,finfo->fd_formb_secshift);
+ out_fdc(fdcu,finfo->fd_formb_nsecs);
+ out_fdc(fdcu,finfo->fd_formb_gaplen);
+ out_fdc(fdcu,finfo->fd_formb_fillbyte);
+ }
+ else
+ {
+ if (read)
+ {
+ out_fdc(fdcu,NE7CMD_READ); /* READ */
+ }
+ else
+ {
+ out_fdc(fdcu,NE7CMD_WRITE); /* WRITE */
+ }
+ out_fdc(fdcu,head << 2 | fdu); /* head & unit */
+ out_fdc(fdcu,fd->track); /* track */
+ out_fdc(fdcu,head);
+ out_fdc(fdcu,sec); /* sector XXX +1? */
+ out_fdc(fdcu,fd->ft->secsize); /* sector size */
+ out_fdc(fdcu,sectrac); /* sectors/track */
+ out_fdc(fdcu,fd->ft->gap); /* gap size */
+ out_fdc(fdcu,fd->ft->datalen); /* data length */
+ }
+ fdc->state = IOCOMPLETE;
+ timeout((timeout_func_t)fd_timeout, (caddr_t)fdcu, 2 * hz);
+ return(0); /* will return later */
+ case IOCOMPLETE: /* IO DONE, post-analyze */
+ untimeout((timeout_func_t)fd_timeout, (caddr_t)fdcu);
+ for(i=0;i<7;i++)
+ {
+ fdc->status[i] = in_fdc(fdcu);
+ }
+ case IOTIMEDOUT: /*XXX*/
+ isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip,
+ format ? bp->b_bcount : FDBLK, fdc->dmachan);
+ if (fdc->status[0]&0xF8)
+ {
+ if (fdc->status[1] & 0x10) {
+ /*
+ * Operation not completed in reasonable time.
+ * Just restart it, don't increment retry count.
+ * (vak)
+ */
+ fdc->state = SEEKCOMPLETE;
+ return (1);
+ }
+ return(retrier(fdcu));
+ }
+ /* All OK */
+ fd->skip += FDBLK;
+ if (!format && fd->skip < bp->b_bcount)
+ {
+ /* set up next transfer */
+ blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
+ + fd->skip/FDBLK;
+ bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads));
+ fdc->state = DOSEEK;
+ }
+ else
+ {
+ /* ALL DONE */
+ fd->skip = 0;
+ bp->b_resid = 0;
+ dp->b_actf = bp->b_actf;
+ biodone(bp);
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ fdc->state = FINDWORK;
+ }
+ return(1);
+ case RESETCTLR:
+ /* Try a reset, keep motor on */
+ set_motor(fdcu,fd->fdsu,1);
+ DELAY(100);
+ set_motor(fdcu,fd->fdsu,0);
+ outb(fdc->baseport+fdctl,fd->ft->trans);
+ TRACE1("[0x%x->fdctl]",fd->ft->trans);
+ fdc->retry++;
+ fdc->state = STARTRECAL;
+ break;
+ case STARTRECAL:
+ out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */
+ out_fdc(fdcu,0xDF);
+ out_fdc(fdcu,2);
+ out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */
+ out_fdc(fdcu,fdu);
+ fdc->state = RECALWAIT;
+ return(0); /* will return later */
+ case RECALWAIT:
+ /* allow heads to settle */
+ timeout((timeout_func_t)fd_pseudointr, (caddr_t)fdcu, hz / 30);
+ fdc->state = RECALCOMPLETE;
+ return(0); /* will return later */
+ case RECALCOMPLETE:
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ if (cyl != 0)
+ {
+ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu,
+ st0, NE7_ST0BITS, cyl);
+ return(retrier(fdcu));
+ }
+ fd->track = 0;
+ /* Seek (probably) necessary */
+ fdc->state = DOSEEK;
+ return(1); /* will return immediatly */
+ case MOTORWAIT:
+ if(fd->flags & FD_MOTOR_WAIT)
+ {
+ return(0); /* time's not up yet */
+ }
+ fdc->state = DOSEEK;
+ return(1); /* will return immediatly */
+ default:
+ printf("Unexpected FD int->");
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ printf("ST0 = %lx, PCN = %lx\n",i,sec);
+ out_fdc(fdcu,0x4A);
+ out_fdc(fdcu,fd->fdsu);
+ for(i=0;i<7;i++) {
+ fdc->status[i] = in_fdc(fdcu);
+ }
+ printf("intr status :%lx %lx %lx %lx %lx %lx %lx ",
+ fdc->status[0],
+ fdc->status[1],
+ fdc->status[2],
+ fdc->status[3],
+ fdc->status[4],
+ fdc->status[5],
+ fdc->status[6] );
+ return(0);
+ }
+ return(1); /* Come back immediatly to new state */
+}
+
+static int
+retrier(fdcu)
+ fdcu_t fdcu;
+{
+ fdc_p fdc = fdc_data + fdcu;
+ register struct buf *dp,*bp;
+
+ dp = &(fdc->head);
+ bp = dp->b_actf;
+
+ switch(fdc->retry)
+ {
+ case 0: case 1: case 2:
+ fdc->state = SEEKCOMPLETE;
+ break;
+ case 3: case 4: case 5:
+ fdc->state = STARTRECAL;
+ break;
+ case 6:
+ fdc->state = RESETCTLR;
+ break;
+ case 7:
+ break;
+ default:
+ {
+ dev_t sav_b_dev = bp->b_dev;
+ /* Trick diskerr */
+ bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|3);
+ diskerr(bp, "fd", "hard error", LOG_PRINTF,
+ fdc->fd->skip, (struct disklabel *)NULL);
+ bp->b_dev = sav_b_dev;
+ printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS);
+ printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS);
+ printf(" ST2 %b ", fdc->status[2], NE7_ST2BITS);
+ printf("cyl %d hd %d sec %d)\n",
+ fdc->status[3], fdc->status[4], fdc->status[5]);
+ }
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ bp->b_resid = bp->b_bcount - fdc->fd->skip;
+ dp->b_actf = bp->b_actf;
+ fdc->fd->skip = 0;
+ biodone(bp);
+ fdc->state = FINDWORK;
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ /* XXX abort current command, if any. */
+ return(1);
+ }
+ fdc->retry++;
+ return(1);
+}
+
+static int
+fdformat(dev, finfo, p)
+ dev_t dev;
+ struct fd_formb *finfo;
+ struct proc *p;
+{
+ fdu_t fdu;
+ fd_p fd;
+
+ struct buf *bp;
+ int rv = 0, s;
+
+ fdu = FDUNIT(minor(dev));
+ fd = &fd_data[fdu];
+
+ /* set up a buffer header for fdstrategy() */
+ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
+ if(bp == 0)
+ return ENOBUFS;
+ bzero((void *)bp, sizeof(struct buf));
+ bp->b_flags = B_BUSY | B_PHYS | B_FORMAT;
+ bp->b_proc = p;
+ bp->b_dev = dev;
+
+ /*
+ * calculate a fake blkno, so fdstrategy() would initiate a
+ * seek to the requested cylinder
+ */
+ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
+ + finfo->head * fd->ft->sectrac) * FDBLK / DEV_BSIZE;
+
+ bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
+ bp->b_un.b_addr = (caddr_t)finfo;
+
+ /* now do the format */
+ fdstrategy(bp);
+
+ /* ...and wait for it to complete */
+ s = splbio();
+ while(!(bp->b_flags & B_DONE))
+ {
+ rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
+ if(rv == EWOULDBLOCK)
+ break;
+ }
+ splx(s);
+
+ if(rv == EWOULDBLOCK)
+ {
+ /* timed out */
+ biodone(bp);
+ rv = EIO;
+ }
+ free(bp, M_TEMP);
+ return rv;
+}
+
+/*
+ * fdioctl() from jc@irbs.UUCP (John Capo)
+ * i386/i386/conf.c needs to have fdioctl() declared and remove the line that
+ * defines fdioctl to be enxio.
+ *
+ * TODO: Reformat.
+ * Think about allocating buffer off stack.
+ * Don't pass uncast 0's and NULL's to read/write/setdisklabel().
+ * Watch out for NetBSD's different *disklabel() interface.
+ *
+ * Added functionality for floppy formatting
+ * joerg_wunsch@uriah.sax.de (Joerg Wunsch)
+ */
+
+int
+fdioctl (dev, cmd, addr, flag, p)
+ dev_t dev;
+ int cmd;
+ caddr_t addr;
+ int flag;
+ struct proc *p;
+{
+ struct fd_type *fdt;
+ struct disklabel *dl;
+ char buffer[DEV_BSIZE];
+ int error;
+
+#if NFT > 0
+ int type = FDTYPE(minor(dev));
+
+ /* check for a tape ioctl */
+ if (type & F_TAPE_TYPE)
+ return ftioctl(dev, cmd, addr, flag, p);
+#endif
+
+ error = 0;
+
+ switch (cmd)
+ {
+ case DIOCGDINFO:
+ bzero(buffer, sizeof (buffer));
+ dl = (struct disklabel *)buffer;
+ dl->d_secsize = FDBLK;
+ fdt = fd_data[FDUNIT(minor(dev))].ft;
+ dl->d_secpercyl = fdt->size / fdt->tracks;
+ dl->d_type = DTYPE_FLOPPY;
+
+ if (readdisklabel(dev, fdstrategy, dl, NULL, 0, 0) == NULL)
+ error = 0;
+ else
+ error = EINVAL;
+
+ *(struct disklabel *)addr = *dl;
+ break;
+
+ case DIOCSDINFO:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ break;
+
+ case DIOCWLABEL:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ break;
+
+ case DIOCWDINFO:
+ if ((flag & FWRITE) == 0)
+ {
+ error = EBADF;
+ break;
+ }
+
+ dl = (struct disklabel *)addr;
+
+ if (error = setdisklabel ((struct disklabel *)buffer,
+ dl, 0, NULL))
+ break;
+
+ error = writedisklabel(dev, fdstrategy,
+ (struct disklabel *)buffer, NULL);
+ break;
+
+ case FD_FORM:
+ if((flag & FWRITE) == 0)
+ error = EBADF; /* must be opened for writing */
+ else if(((struct fd_formb *)addr)->format_version !=
+ FD_FORMAT_VERSION)
+ error = EINVAL; /* wrong version of formatting prog */
+ else
+ error = fdformat(dev, (struct fd_formb *)addr, p);
+ break;
+
+ case FD_GTYPE: /* get drive type */
+ *(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft;
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ return (error);
+}
+
+#endif
diff --git a/sys/dev/fdc/fdcreg.h b/sys/dev/fdc/fdcreg.h
new file mode 100644
index 0000000..5deb02c
--- /dev/null
+++ b/sys/dev/fdc/fdcreg.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)fdreg.h 7.1 (Berkeley) 5/9/91
+ * $Id: fdreg.h,v 1.3 1994/02/07 04:27:10 alm Exp $
+ */
+
+/*
+ * AT floppy controller registers and bitfields
+ */
+
+/* uses NEC765 controller */
+#include "../i386/isa/ic/nec765.h"
+
+/* registers */
+#define fdout 2 /* Digital Output Register (W) */
+#define FDO_FDSEL 0x03 /* floppy device select */
+#define FDO_FRST 0x04 /* floppy controller reset */
+#define FDO_FDMAEN 0x08 /* enable floppy DMA and Interrupt */
+#define FDO_MOEN0 0x10 /* motor enable drive 0 */
+#define FDO_MOEN1 0x20 /* motor enable drive 1 */
+#define FDO_MOEN2 0x30 /* motor enable drive 2 */
+#define FDO_MOEN3 0x40 /* motor enable drive 3 */
+
+#define fdsts 4 /* NEC 765 Main Status Register (R) */
+#define fddata 5 /* NEC 765 Data Register (R/W) */
+
+#define fdctl 7 /* Control Register (W) */
+#define FDC_500KBPS 0x00 /* 500KBPS MFM drive transfer rate */
+#define FDC_300KBPS 0x01 /* 300KBPS MFM drive transfer rate */
+#define FDC_250KBPS 0x02 /* 250KBPS MFM drive transfer rate */
+#define FDC_125KBPS 0x03 /* 125KBPS FM drive transfer rate */
+
+#define fdin 7 /* Digital Input Register (R) */
+#define FDI_DCHG 0x80 /* diskette has been changed */
+
diff --git a/sys/dev/ic/i8237.h b/sys/dev/ic/i8237.h
new file mode 100644
index 0000000..2199e73
--- /dev/null
+++ b/sys/dev/ic/i8237.h
@@ -0,0 +1,11 @@
+/*
+ * Intel 8237 DMA Controller
+ *
+ * $Id$
+ */
+
+#define DMA37MD_SINGLE 0x40 /* single pass mode */
+#define DMA37MD_CASCADE 0xc0 /* cascade mode */
+#define DMA37MD_WRITE 0x04 /* read the device, write memory operation */
+#define DMA37MD_READ 0x08 /* write the device, read memory operation */
+
diff --git a/sys/dev/ic/i82586.h b/sys/dev/ic/i82586.h
new file mode 100644
index 0000000..577313d
--- /dev/null
+++ b/sys/dev/ic/i82586.h
@@ -0,0 +1,325 @@
+/*-
+ * Copyright (c) 1992, University of Vermont and State Agricultural College.
+ * Copyright (c) 1992, Garrett A. Wollman.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * Vermont and State Agricultural College and Garrett A. Wollman.
+ * 4. Neither the name of the University nor the name of the author
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+/*
+ * Intel 82586 Ethernet chip
+ * Register, bit, and structure definitions.
+ *
+ * Written by GAW with reference to the Clarkson Packet Driver code for this
+ * chip written by Russ Nelson and others.
+ */
+
+struct ie_en_addr {
+ u_char data[6];
+};
+
+/*
+ * This is the master configuration block. It tells the hardware where all
+ * the rest of the stuff is.
+ */
+struct ie_sys_conf_ptr {
+ u_short mbz; /* must be zero */
+ u_char ie_bus_use; /* true if 8-bit only */
+ u_char mbz2[5]; /* must be zero */
+ caddr_t ie_iscp_ptr; /* 24-bit physaddr of ISCP */
+};
+
+/*
+ * Note that this is wired in hardware; the SCP is always located here, no
+ * matter what.
+ */
+#define IE_SCP_ADDR 0xfffff4
+
+/*
+ * The tells the hardware where all the rest of the stuff is, too.
+ * FIXME: some of these should be re-commented after we figure out their
+ * REAL function.
+ */
+struct ie_int_sys_conf_ptr {
+ u_char ie_busy; /* zeroed after init */
+ u_char mbz;
+ u_short ie_scb_offset; /* 16-bit physaddr of next struct */
+ caddr_t ie_base; /* 24-bit physaddr for all 16-bit vars */
+};
+
+/*
+ * This FINALLY tells the hardware what to do and where to put it.
+ */
+struct ie_sys_ctl_block {
+ u_short ie_status; /* status word */
+ u_short ie_command; /* command word */
+ u_short ie_command_list; /* 16-pointer to command block list */
+ u_short ie_recv_list; /* 16-pointer to receive frame list */
+ u_short ie_err_crc; /* CRC errors */
+ u_short ie_err_align; /* Alignment errors */
+ u_short ie_err_resource; /* Resource errors */
+ u_short ie_err_overrun; /* Overrun errors */
+};
+
+/* Command values */
+#define IE_RU_COMMAND 0x0070 /* mask for RU command */
+#define IE_RU_NOP 0 /* for completeness */
+#define IE_RU_START 0x0010 /* start receive unit command */
+#define IE_RU_ENABLE 0x0020 /* enable receiver command */
+#define IE_RU_DISABLE 0x0030 /* disable receiver command */
+#define IE_RU_ABORT 0x0040 /* abort current receive operation */
+
+#define IE_CU_COMMAND 0x0700 /* mask for CU command */
+#define IE_CU_NOP 0 /* included for completeness */
+#define IE_CU_START 0x0100 /* do-command command */
+#define IE_CU_RESUME 0x0200 /* resume a suspended cmd list */
+#define IE_CU_STOP 0x0300 /* SUSPEND was already taken */
+#define IE_CU_ABORT 0x0400 /* abort current command */
+
+#define IE_ACK_COMMAND 0xf000 /* mask for ACK command */
+#define IE_ACK_CX 0x8000 /* ack IE_ST_DONE */
+#define IE_ACK_FR 0x4000 /* ack IE_ST_RECV */
+#define IE_ACK_CNA 0x2000 /* ack IE_ST_ALLDONE */
+#define IE_ACK_RNR 0x1000 /* ack IE_ST_RNR */
+
+#define IE_ACTION_COMMAND(x) (((x) & IE_CU_COMMAND) == IE_CU_START)
+ /* is this command an action command? */
+
+/* Status values */
+#define IE_ST_WHENCE 0xf000 /* mask for cause of interrupt */
+#define IE_ST_DONE 0x8000 /* command with I bit completed */
+#define IE_ST_RECV 0x4000 /* frame received */
+#define IE_ST_ALLDONE 0x2000 /* all commands completed */
+#define IE_ST_RNR 0x1000 /* receive not ready */
+
+#define IE_CU_STATUS 0x700 /* mask for command unit status */
+#define IE_CU_ACTIVE 0x200 /* command unit is active */
+#define IE_CU_SUSPEND 0x100 /* command unit is suspended */
+
+#define IE_RU_STATUS 0x70 /* mask for receiver unit status */
+#define IE_RU_SUSPEND 0x10 /* receiver is suspended */
+#define IE_RU_NOSPACE 0x20 /* receiver has no resources */
+#define IE_RU_READY 0x40 /* reveiver is ready */
+
+/*
+ * This is filled in partially by the chip, partially by us.
+ */
+struct ie_recv_frame_desc {
+ u_short ie_fd_status; /* status for this frame */
+ u_short ie_fd_last; /* end of frame list flag */
+ u_short ie_fd_next; /* 16-pointer to next RFD */
+ u_short ie_fd_buf_desc; /* 16-pointer to list of buffer desc's */
+ struct ie_en_addr dest; /* destination ether */
+ struct ie_en_addr src; /* source ether */
+ u_short ie_length; /* 802 length/Ether type */
+ u_short mbz; /* must be zero */
+};
+
+#define IE_FD_LAST 0x8000 /* last rfd in list */
+#define IE_FD_SUSP 0x4000 /* suspend RU after receipt */
+
+#define IE_FD_COMPLETE 0x8000 /* frame is complete */
+#define IE_FD_BUSY 0x4000 /* frame is busy */
+#define IE_FD_OK 0x2000 /* frame is bad */
+#define IE_FD_RNR 0x0200 /* receiver out of resources here */
+
+/*
+ * linked list of buffers...
+ */
+struct ie_recv_buf_desc {
+ u_short ie_rbd_actual; /* status for this buffer */
+ u_short ie_rbd_next; /* 16-pointer to next RBD */
+ caddr_t ie_rbd_buffer; /* 24-pointer to buffer for this RBD */
+ u_short ie_rbd_length; /* length of the buffer */
+ u_short mbz; /* must be zero */
+};
+
+#define IE_RBD_LAST 0x8000 /* last buffer */
+#define IE_RBD_USED 0x4000 /* this buffer has data */
+/*
+ * All commands share this in common.
+ */
+struct ie_cmd_common {
+ u_short ie_cmd_status; /* status of this command */
+ u_short ie_cmd_cmd; /* command word */
+ u_short ie_cmd_link; /* link to next command */
+};
+
+#define IE_STAT_COMPL 0x8000 /* command is completed */
+#define IE_STAT_BUSY 0x4000 /* command is running now */
+#define IE_STAT_OK 0x2000 /* command completed successfully */
+
+#define IE_CMD_NOP 0x0000 /* NOP */
+#define IE_CMD_IASETUP 0x0001 /* initial address setup */
+#define IE_CMD_CONFIG 0x0002 /* configure command */
+#define IE_CMD_MCAST 0x0003 /* multicast setup command */
+#define IE_CMD_XMIT 0x0004 /* transmit command */
+#define IE_CMD_TDR 0x0005 /* time-domain reflectometer command */
+#define IE_CMD_DUMP 0x0006 /* dump command */
+#define IE_CMD_DIAGNOSE 0x0007 /* diagnostics command */
+
+#define IE_CMD_LAST 0x8000 /* this is the last command in the list */
+#define IE_CMD_SUSPEND 0x4000 /* suspend CU after this command */
+#define IE_CMD_INTR 0x2000 /* post an interrupt after completion */
+
+/*
+ * This is the command to transmit a frame.
+ */
+struct ie_xmit_cmd {
+ struct ie_cmd_common com; /* common part */
+#define ie_xmit_status com.ie_cmd_status
+
+ u_short ie_xmit_desc; /* 16-pointer to buffer descriptor */
+ struct ie_en_addr ie_xmit_addr; /* destination address */
+
+ u_short ie_xmit_length; /* 802.3 length/Ether type field */
+};
+
+#define IE_XS_MAXCOLL 0x000f /* number of collisions during transmit */
+#define IE_XS_EXCMAX 0x0020 /* exceeded maximum number of collisions */
+#define IE_XS_SQE 0x0040 /* SQE positive */
+#define IE_XS_DEFERRED 0x0080 /* transmission deferred */
+#define IE_XS_UNDERRUN 0x0100 /* DMA underrun */
+#define IE_XS_LOSTCTS 0x0200 /* Lost CTS */
+#define IE_XS_NOCARRIER 0x0400 /* No Carrier */
+#define IE_XS_LATECOLL 0x0800 /* Late collision */
+
+/*
+ * This is a buffer descriptor for a frame to be transmitted.
+ */
+
+struct ie_xmit_buf {
+ u_short ie_xmit_flags; /* see below */
+ u_short ie_xmit_next; /* 16-pointer to next desc. */
+ caddr_t ie_xmit_buf; /* 24-pointer to the actual buffer */
+};
+
+#define IE_XMIT_LAST 0x8000 /* this TBD is the last one */
+/* The rest of the `flags' word is actually the length. */
+
+/*
+ * Multicast setup command.
+ */
+
+#define MAXMCAST 50 /* must fit in transmit buffer */
+
+struct ie_mcast_cmd {
+ struct ie_cmd_common com; /* common part */
+#define ie_mcast_status com.ie_cmd_status
+
+ u_short ie_mcast_bytes; /* size (in bytes) of multicast addresses */
+ struct ie_en_addr ie_mcast_addrs[MAXMCAST + 1]; /* space for them */
+};
+
+/*
+ * Time Domain Reflectometer command.
+ */
+
+struct ie_tdr_cmd {
+ struct ie_cmd_common com; /* common part */
+#define ie_tdr_status com.ie_cmd_status
+
+ u_short ie_tdr_time; /* error bits and time */
+};
+
+#define IE_TDR_SUCCESS 0x8000 /* TDR succeeded without error */
+#define IE_TDR_XCVR 0x4000 /* detected a transceiver problem */
+#define IE_TDR_OPEN 0x2000 /* detected an open */
+#define IE_TDR_SHORT 0x1000 /* TDR detected a short */
+#define IE_TDR_TIME 0x07ff /* mask for reflection time */
+
+/*
+ * Initial Address Setup command
+ */
+struct ie_iasetup_cmd {
+ struct ie_cmd_common com;
+#define ie_iasetup_status com.ie_cmd_status
+
+ struct ie_en_addr ie_address;
+};
+
+/*
+ * Configuration command
+ */
+struct ie_config_cmd {
+ struct ie_cmd_common com; /* common part */
+#define ie_config_status com.ie_cmd_status
+
+ u_char ie_config_count; /* byte count (0x0c) */
+ u_char ie_fifo; /* fifo (8) */
+ u_char ie_save_bad; /* save bad frames (0x40) */
+ u_char ie_addr_len; /* address length (0x2e) (AL-LOC == 1) */
+ u_char ie_priority; /* priority and backoff (0x0) */
+ u_char ie_ifs; /* inter-frame spacing (0x60) */
+ u_char ie_slot_low; /* slot time, LSB (0x0) */
+ u_char ie_slot_high; /* slot time, MSN, and retries (0xf2) */
+ u_char ie_promisc; /* 1 if promiscuous, else 0 */
+ u_char ie_crs_cdt; /* CSMA/CD parameters (0x0) */
+ u_char ie_min_len; /* min frame length (0x40) */
+ u_char ie_junk; /* stuff for 82596 (0xff) */
+};
+
+/*
+ * Here are a few useful functions. We could have done these as macros,
+ * but since we have the inline facility, it makes sense to use that
+ * instead.
+ */
+inline void
+ie_setup_config(volatile struct ie_config_cmd *cmd,
+ int promiscuous, int manchester) {
+ cmd->ie_config_count = 0x0c;
+ cmd->ie_fifo = 8;
+ cmd->ie_save_bad = 0x40;
+ cmd->ie_addr_len = 0x2e;
+ cmd->ie_priority = 0;
+ cmd->ie_ifs = 0x60;
+ cmd->ie_slot_low = 0;
+ cmd->ie_slot_high = 0xf2;
+ cmd->ie_promisc = !!promiscuous | manchester << 2;
+ cmd->ie_crs_cdt = 0;
+ cmd->ie_min_len = 64;
+ cmd->ie_junk = 0xff;
+}
+
+inline caddr_t
+Align(caddr_t ptr) {
+ unsigned long l = (unsigned long)ptr;
+ l = (l + 3) & ~3L;
+ return (caddr_t)l;
+}
+
+inline void
+ie_ack(volatile struct ie_sys_ctl_block *scb,
+ u_int mask, int unit,
+ void (*ca)(int)) {
+ scb->ie_command = scb->ie_status & mask;
+ (*ca)(unit);
+}
diff --git a/sys/dev/ic/nec765.h b/sys/dev/ic/nec765.h
new file mode 100644
index 0000000..1895db7
--- /dev/null
+++ b/sys/dev/ic/nec765.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)nec765.h 7.1 (Berkeley) 5/9/91
+ * $Id$
+ */
+
+/*
+ * Nec 765 floppy disc controller definitions
+ */
+
+/* Main status register */
+#define NE7_DAB 0x01 /* Diskette drive A is seeking, thus busy */
+#define NE7_DBB 0x02 /* Diskette drive B is seeking, thus busy */
+#define NE7_CB 0x10 /* Diskette Controller Busy */
+#define NE7_NDM 0x20 /* Diskette Controller in Non Dma Mode */
+#define NE7_DIO 0x40 /* Diskette Controller Data register I/O */
+#define NE7_RQM 0x80 /* Diskette Controller ReQuest for Master */
+
+/* Status register ST0 */
+#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005drv_chck\004drive_rdy\003top_head"
+
+/* Status register ST1 */
+#define NE7_ST1BITS "\020\010end_of_cyl\006bad_crc\005data_overrun\003sec_not_fnd\002write_protect\001no_am"
+
+/* Status register ST2 */
+#define NE7_ST2BITS "\020\007ctrl_mrk\006bad_crc\005wrong_cyl\004scn_eq\003scn_not_fnd\002bad_cyl\001no_dam"
+
+/* Status register ST3 */
+#define NE7_ST3BITS "\020\010fault\007write_protect\006drdy\005tk0\004two_side\003side_sel\002"
+
+/* Commands */
+#define NE7CMD_SPECIFY 3 /* specify drive parameters - requires unit
+ parameters byte */
+#define NE7CMD_SENSED 4 /* sense drive - requires unit select byte */
+#define NE7CMD_WRITE 0xc5 /* write - requires eight additional bytes */
+#define NE7CMD_READ 0xe6 /* read - requires eight additional bytes */
+#define NE7CMD_FORMAT 0x4c /* format - requires five additional bytes */
+#define NE7CMD_RECAL 7 /* recalibrate drive - requires
+ unit select byte */
+#define NE7CMD_SENSEI 8 /* sense controller interrupt status */
+#define NE7CMD_SEEK 15 /* seek drive - requires unit select byte
+ and new cyl byte */
diff --git a/sys/dev/ic/ns16550.h b/sys/dev/ic/ns16550.h
new file mode 100644
index 0000000..ff59757
--- /dev/null
+++ b/sys/dev/ic/ns16550.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)ns16550.h 7.1 (Berkeley) 5/9/91
+ * $Id$
+ */
+
+/*
+ * NS16550 UART registers
+ */
+
+#define com_data 0 /* data register (R/W) */
+#define com_dlbl 0 /* divisor latch low (W) */
+#define com_dlbh 1 /* divisor latch high (W) */
+#define com_ier 1 /* interrupt enable (W) */
+#define com_iir 2 /* interrupt identification (R) */
+#define com_fifo 2 /* FIFO control (W) */
+#define com_lctl 3 /* line control register (R/W) */
+#define com_cfcr 3 /* line control register (R/W) */
+#define com_mcr 4 /* modem control register (R/W) */
+#define com_lsr 5 /* line status register (R/W) */
+#define com_msr 6 /* modem status register (R/W) */
diff --git a/sys/dev/ie/if_ie.c b/sys/dev/ie/if_ie.c
new file mode 100644
index 0000000..fbb1d5e
--- /dev/null
+++ b/sys/dev/ie/if_ie.c
@@ -0,0 +1,1801 @@
+/*-
+ * Copyright (c) 1992, 1993, University of Vermont and State
+ * Agricultural College.
+ * Copyright (c) 1992, 1993, Garrett A. Wollman.
+ *
+ * Portions:
+ * Copyright (c) 1990, 1991, William F. Jolitz
+ * Copyright (c) 1990, The Regents of the University of California
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * Vermont and State Agricultural College and Garrett A. Wollman,
+ * by William F. Jolitz, by the University of California,
+ * Berkeley, by Larwence Berkeley Laboratory, and its contributors.
+ * 4. Neither the names of the Universities nor the names of the authors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR AUTHORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: if_ie.c,v 1.2 1993/11/25 01:31:36 wollman Exp $
+ */
+
+/*
+ * Intel 82586 Ethernet chip
+ * Register, bit, and structure definitions.
+ *
+ * Written by GAW with reference to the Clarkson Packet Driver code for this
+ * chip written by Russ Nelson and others.
+ *
+ * BPF support code stolen directly from hpdev/if_le.c, supplied with
+ * tcpdump.
+ */
+
+/*
+ * The i82586 is a very versatile chip, found in many implementations.
+ * Programming this chip is mostly the same, but certain details differ
+ * from card to card. This driver is written so that different cards
+ * can be automatically detected at run-time. Currently, only the
+ * AT&T EN100/StarLAN 10 series are supported.
+ */
+
+/*
+Mode of operation:
+
+We run the 82586 in a standard Ethernet mode. We keep NFRAMES received
+frame descriptors around for the receiver to use, and NBUFFS associated
+receive buffer descriptors, both in a circular list. Whenever a frame is
+received, we rotate both lists as necessary. (The 586 treats both lists
+as a simple queue.) We also keep a transmit command around so that packets
+can be sent off quickly.
+
+We configure the adapter in AL-LOC = 1 mode, which means that the
+Ethernet/802.3 MAC header is placed at the beginning of the receive buffer
+rather than being split off into various fields in the RFD. This also
+means that we must include this header in the transmit buffer as well.
+
+By convention, all transmit commands, and only transmit commands, shall
+have the I (IE_CMD_INTR) bit set in the command. This way, when an
+interrupt arrives at ieintr(), it is immediately possible to tell
+what precisely caused it. ANY OTHER command-sending routines should
+run at splimp(), and should post an acknowledgement to every interrupt
+they generate.
+
+The 82586 has a 24-bit address space internally, and the adaptor's
+memory is located at the top of this region. However, the value we are
+given in configuration is normally the *bottom* of the adaptor RAM. So,
+we must go through a few gyrations to come up with a kernel virtual address
+which represents the actual beginning of the 586 address space. First,
+we autosize the RAM by running through several possible sizes and trying
+to initialize the adapter under the assumption that the selected size
+is correct. Then, knowing the correct RAM size, we set up our pointers
+in ie_softc[unit]. `iomem' represents the computed base of the 586
+address space. `iomembot' represents the actual configured base
+of adapter RAM. Finally, `iosize' represents the calculated size
+of 586 RAM. Then, when laying out commands, we use the interval
+[iomembot, iomembot + iosize); to make 24-pointers, we subtract
+iomem, and to make 16-pointers, we subtract iomem and and with 0xffff.
+
+*/
+
+#include "ie.h"
+#if NIE > 0
+
+#include "param.h"
+#include "systm.h"
+#include "mbuf.h"
+#include "protosw.h"
+#include "socket.h"
+#include "ioctl.h"
+#include "errno.h"
+#include "syslog.h"
+
+#include "net/if.h"
+#include "net/if_types.h"
+#include "net/if_dl.h"
+#include "net/route.h"
+
+#include "bpfilter.h"
+
+#ifdef INET
+#include "netinet/in.h"
+#include "netinet/in_systm.h"
+#include "netinet/in_var.h"
+#include "netinet/ip.h"
+#include "netinet/if_ether.h"
+#endif
+
+#ifdef NS
+#include "netns/ns.h"
+#include "netns/ns_if.h"
+#endif
+
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/ic/i82586.h"
+#include "i386/isa/if_iereg.h"
+#include "i386/isa/icu.h"
+
+#include "vm/vm.h"
+
+#if NBPFILTER > 0
+#include "net/bpf.h"
+#include "net/bpfdesc.h"
+#endif
+
+#if (NBPFILTER > 0) || defined(MULTICAST)
+#define FILTER
+static struct mbuf *last_not_for_us;
+#endif
+
+#ifdef DEBUG
+#define IED_RINT 1
+#define IED_TINT 2
+#define IED_RNR 4
+#define IED_CNA 8
+#define IED_READFRAME 16
+int ie_debug = IED_RNR;
+#endif
+
+#ifndef ETHERMINLEN
+#define ETHERMINLEN 60
+#endif
+
+#define IE_BUF_LEN 1512 /* length of transmit buffer */
+
+/* Forward declaration */
+struct ie_softc;
+
+static int ieprobe(struct isa_device *dvp);
+static int ieattach(struct isa_device *dvp);
+static void ieinit(int unit);
+static int ieioctl(struct ifnet *ifp, int command, caddr_t data);
+static void iestart(struct ifnet *ifp);
+static void sl_reset_586(int unit);
+static void sl_chan_attn(int unit);
+static void iereset(int unit);
+static void ie_readframe(int unit, struct ie_softc *ie, int bufno);
+static void ie_drop_packet_buffer(int unit, struct ie_softc *ie);
+static void sl_read_ether(int unit, unsigned char addr[6]);
+static void find_ie_mem_size(int unit);
+static int command_and_wait(int unit, int command, void volatile *pcmd, int);
+static int ierint(int unit, struct ie_softc *ie);
+static int ietint(int unit, struct ie_softc *ie);
+static int iernr(int unit, struct ie_softc *ie);
+static void start_receiver(int unit);
+static int ieget(int, struct ie_softc *, struct mbuf **,
+ struct ether_header *, int *);
+static caddr_t setup_rfa(caddr_t ptr, struct ie_softc *ie);
+static int mc_setup(int, caddr_t, volatile struct ie_sys_ctl_block *);
+#ifdef MULTICAST
+static void ie_mc_reset(int unit);
+#endif
+
+#ifdef DEBUG
+void print_rbd(volatile struct ie_recv_buf_desc *rbd);
+
+int in_ierint = 0;
+int in_ietint = 0;
+#endif
+
+/*
+ * This tells the autoconf code how to set us up.
+ */
+struct isa_driver iedriver = {
+ ieprobe, ieattach, "ie",
+};
+
+enum ie_hardware {
+ IE_STARLAN10,
+ IE_EN100,
+ IE_SLFIBER,
+ IE_UNKNOWN
+};
+
+const char *ie_hardware_names[] = {
+ "StarLAN 10",
+ "EN100",
+ "StarLAN Fiber",
+ "Unknown"
+};
+
+/*
+sizeof(iscp) == 1+1+2+4 == 8
+sizeof(scb) == 2+2+2+2+2+2+2+2 == 16
+NFRAMES * sizeof(rfd) == NFRAMES*(2+2+2+2+6+6+2+2) == NFRAMES*24 == 384
+sizeof(xmit_cmd) == 2+2+2+2+6+2 == 18
+sizeof(transmit buffer) == 1512
+sizeof(transmit buffer desc) == 8
+-----
+1946
+
+NBUFFS * sizeof(rbd) == NBUFFS*(2+2+4+2+2) == NBUFFS*12
+NBUFFS * IE_RBUF_SIZE == NBUFFS*256
+
+NBUFFS should be (16384 - 1946) / (256 + 12) == 14438 / 268 == 53
+
+With NBUFFS == 48, this leaves us 1574 bytes for another command or
+more buffers. Another transmit command would be 18+8+1512 == 1538
+---just barely fits!
+
+Obviously all these would have to be reduced for smaller memory sizes.
+With a larger memory, it would be possible to roughly double the number of
+both transmit and receive buffers.
+*/
+
+#define NFRAMES 16 /* number of frames to allow for receive */
+#define NBUFFS 48 /* number of buffers to allocate */
+#define IE_RBUF_SIZE 256 /* size of each buffer, MUST BE POWER OF TWO */
+
+/*
+ * Ethernet status, per interface.
+ */
+struct ie_softc {
+ struct arpcom arpcom;
+ void (*ie_reset_586)(int);
+ void (*ie_chan_attn)(int);
+ enum ie_hardware hard_type;
+ int hard_vers;
+
+ u_short port;
+ caddr_t iomem;
+ caddr_t iomembot;
+ unsigned iosize;
+
+ int want_mcsetup;
+ int promisc;
+ volatile struct ie_int_sys_conf_ptr *iscp;
+ volatile struct ie_sys_ctl_block *scb;
+ volatile struct ie_recv_frame_desc *rframes[NFRAMES];
+ volatile struct ie_recv_buf_desc *rbuffs[NBUFFS];
+ volatile char *cbuffs[NBUFFS];
+ int rfhead, rftail, rbhead, rbtail;
+
+ volatile struct ie_xmit_cmd *xmit_cmds[2];
+ volatile struct ie_xmit_buf *xmit_buffs[2];
+ int xmit_count;
+ u_char *xmit_cbuffs[2];
+
+ struct ie_en_addr mcast_addrs[MAXMCAST + 1];
+ int mcast_count;
+
+#if NBPFILTER > 0
+ caddr_t ie_bpf;
+#endif
+
+} ie_softc[NIE];
+
+#define MK_24(base, ptr) ((caddr_t)((u_long)ptr - (u_long)base))
+#define MK_16(base, ptr) ((u_short)(u_long)MK_24(base, ptr))
+
+#define PORT ie_softc[unit].port
+#define MEM ie_softc[unit].iomem
+
+
+int ieprobe(dvp)
+ struct isa_device *dvp;
+{
+ int unit = dvp->id_unit;
+ u_char c;
+
+ ie_softc[unit].port = dvp->id_iobase;
+ ie_softc[unit].iomembot = dvp->id_maddr;
+ ie_softc[unit].iomem = 0;
+
+ c = inb(PORT + IEATT_REVISION);
+ switch(SL_BOARD(c)) {
+ case SL10_BOARD:
+ ie_softc[unit].hard_type = IE_STARLAN10;
+ ie_softc[unit].ie_reset_586 = sl_reset_586;
+ ie_softc[unit].ie_chan_attn = sl_chan_attn;
+ break;
+ case EN100_BOARD:
+ ie_softc[unit].hard_type = IE_EN100;
+ ie_softc[unit].ie_reset_586 = sl_reset_586;
+ ie_softc[unit].ie_chan_attn = sl_chan_attn;
+ break;
+ case SLFIBER_BOARD:
+ ie_softc[unit].hard_type = IE_SLFIBER;
+ ie_softc[unit].ie_reset_586 = sl_reset_586;
+ ie_softc[unit].ie_chan_attn = sl_chan_attn;
+ break;
+
+ /*
+ * Anything else is not recognized or cannot be used.
+ */
+ default:
+ return 0;
+ }
+
+ ie_softc[unit].hard_vers = SL_REV(c);
+
+ /*
+ * Divine memory size on-board the card. Ususally 16k.
+ */
+ find_ie_mem_size(unit);
+
+ if(!ie_softc[unit].iosize) {
+ return 0;
+ }
+
+ dvp->id_msize = ie_softc[unit].iosize;
+
+ switch(ie_softc[unit].hard_type) {
+ case IE_EN100:
+ case IE_STARLAN10:
+ case IE_SLFIBER:
+ sl_read_ether(unit, ie_softc[unit].arpcom.ac_enaddr);
+ break;
+
+ default:
+ printf("ie%d: unknown AT&T board type code %d\n", unit,
+ ie_softc[unit].hard_type);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Taken almost exactly from Bill's if_is.c, then modified beyond recognition.
+ */
+int
+ieattach(dvp)
+ struct isa_device *dvp;
+{
+ int unit = dvp->id_unit;
+ struct ie_softc *ie = &ie_softc[unit];
+ struct ifnet *ifp = &ie->arpcom.ac_if;
+
+ ifp->if_unit = unit;
+ ifp->if_name = iedriver.name;
+ ifp->if_mtu = ETHERMTU;
+ printf("<%s R%d> ethernet address %s",
+ ie_hardware_names[ie_softc[unit].hard_type],
+ ie_softc[unit].hard_vers + 1,
+ ether_sprintf(ie->arpcom.ac_enaddr));
+
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
+#ifdef MULTICAST
+ ifp->if_flags |= IFF_MULTICAST;
+#endif /* MULTICAST */
+
+ ifp->if_init = ieinit;
+ ifp->if_output = ether_output;
+ ifp->if_start = iestart;
+ ifp->if_ioctl = ieioctl;
+ ifp->if_reset = iereset;
+ ifp->if_type = IFT_ETHER;
+ ifp->if_addrlen = 6;
+ ifp->if_hdrlen = 14;
+
+#if NBPFILTER > 0
+ printf("\n");
+ bpfattach(&ie_softc[unit].ie_bpf, ifp, DLT_EN10MB,
+ sizeof(struct ether_header));
+#endif
+
+ if_attach(ifp);
+ {
+ struct ifaddr *ifa = ifp->if_addrlist;
+ struct sockaddr_dl *sdl;
+ while(ifa && ifa->ifa_addr && ifa->ifa_addr->sa_family != AF_LINK)
+ ifa = ifa->ifa_next;
+
+ if(!ifa || !ifa->ifa_addr) return 1;
+
+ /* Provide our ether address to the higher layers */
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ sdl->sdl_type = IFT_ETHER;
+ sdl->sdl_alen = 6;
+ sdl->sdl_slen = 0;
+ bcopy(ie->arpcom.ac_enaddr, LLADDR(sdl), 6);
+ return 1;
+ }
+}
+
+/*
+ * What to do upon receipt of an interrupt.
+ */
+int ieintr(unit)
+ int unit;
+{
+ register struct ie_softc *ie = &ie_softc[unit];
+ register u_short status;
+
+ status = ie->scb->ie_status;
+
+loop:
+ if(status & (IE_ST_RECV | IE_ST_RNR)) {
+#ifdef DEBUG
+ in_ierint++;
+ if(ie_debug & IED_RINT)
+ printf("ie%d: rint\n", unit);
+#endif
+ ierint(unit, ie);
+#ifdef DEBUG
+ in_ierint--;
+#endif
+ }
+
+ if(status & IE_ST_DONE) {
+#ifdef DEBUG
+ in_ietint++;
+ if(ie_debug & IED_TINT)
+ printf("ie%d: tint\n", unit);
+#endif
+ ietint(unit, ie);
+#ifdef DEBUG
+ in_ietint--;
+#endif
+ }
+
+ if(status & IE_ST_RNR) {
+#ifdef DEBUG
+ if(ie_debug & IED_RNR)
+ printf("ie%d: rnr\n", unit);
+#endif
+ iernr(unit, ie);
+ }
+
+#ifdef DEBUG
+ if((status & IE_ST_ALLDONE)
+ && (ie_debug & IED_CNA))
+ printf("ie%d: cna\n", unit);
+#endif
+
+ /* Don't ack interrupts which we didn't receive */
+ ie_ack(ie->scb, IE_ST_WHENCE & status, unit, ie->ie_chan_attn);
+
+ if((status = ie->scb->ie_status) & IE_ST_WHENCE)
+ goto loop;
+
+ return unit;
+}
+
+/*
+ * Process a received-frame interrupt.
+ */
+static int ierint(unit, ie)
+ int unit;
+ struct ie_softc *ie;
+{
+ int i, status;
+ static int timesthru = 1024;
+
+ i = ie->rfhead;
+ while(1) {
+ status = ie->rframes[i]->ie_fd_status;
+
+ if((status & IE_FD_COMPLETE) && (status & IE_FD_OK)) {
+ ie->arpcom.ac_if.if_ipackets++;
+ if(!--timesthru) {
+ ie->arpcom.ac_if.if_ierrors += ie->scb->ie_err_crc + ie->scb->ie_err_align +
+ ie->scb->ie_err_resource + ie->scb->ie_err_overrun;
+ ie->scb->ie_err_crc = 0;
+ ie->scb->ie_err_align = 0;
+ ie->scb->ie_err_resource = 0;
+ ie->scb->ie_err_overrun = 0;
+ timesthru = 1024;
+ }
+ ie_readframe(unit, ie, i);
+ } else {
+ if(status & IE_FD_RNR) {
+ if(!(ie->scb->ie_status & IE_RU_READY)) {
+ ie->rframes[0]->ie_fd_next = MK_16(MEM, ie->rbuffs[0]);
+ ie->scb->ie_recv_list = MK_16(MEM, ie->rframes[0]);
+ command_and_wait(unit, IE_RU_START, 0, 0);
+ }
+ }
+ break;
+ }
+ i = (i + 1) % NFRAMES;
+ }
+ return 0;
+}
+
+/*
+ * Process a command-complete interrupt. These are only generated by
+ * the transmission of frames. This routine is deceptively simple, since
+ * most of the real work is done by iestart().
+ */
+static int ietint(unit, ie)
+ int unit;
+ struct ie_softc *ie;
+{
+ int status;
+ int i;
+
+ ie->arpcom.ac_if.if_timer = 0;
+ ie->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+
+ for(i = 0; i < ie->xmit_count; i++) {
+ status = ie->xmit_cmds[i]->ie_xmit_status;
+
+ if(status & IE_XS_LATECOLL) {
+ printf("ie%d: late collision\n", unit);
+ ie->arpcom.ac_if.if_collisions++;
+ ie->arpcom.ac_if.if_oerrors++;
+ } else if(status & IE_XS_NOCARRIER) {
+ printf("ie%d: no carrier\n", unit);
+ ie->arpcom.ac_if.if_oerrors++;
+ } else if(status & IE_XS_LOSTCTS) {
+ printf("ie%d: lost CTS\n", unit);
+ ie->arpcom.ac_if.if_oerrors++;
+ } else if(status & IE_XS_UNDERRUN) {
+ printf("ie%d: DMA underrun\n", unit);
+ ie->arpcom.ac_if.if_oerrors++;
+ } else if(status & IE_XS_EXCMAX) {
+ printf("ie%d: too many collisions\n", unit);
+ ie->arpcom.ac_if.if_collisions += 16;
+ ie->arpcom.ac_if.if_oerrors++;
+ } else {
+ ie->arpcom.ac_if.if_opackets++;
+ ie->arpcom.ac_if.if_collisions += status & IE_XS_MAXCOLL;
+ }
+ }
+ ie->xmit_count = 0;
+
+ /*
+ * If multicast addresses were added or deleted while we were transmitting,
+ * ie_mc_reset() set the want_mcsetup flag indicating that we should do it.
+ */
+ if(ie->want_mcsetup) {
+ mc_setup(unit, (caddr_t)ie->xmit_cbuffs[0], ie->scb);
+ ie->want_mcsetup = 0;
+ }
+
+ /* Wish I knew why this seems to be necessary... */
+ ie->xmit_cmds[0]->ie_xmit_status |= IE_STAT_COMPL;
+
+ iestart(&ie->arpcom.ac_if);
+ return 0; /* shouldn't be necessary */
+}
+
+/*
+ * Process a receiver-not-ready interrupt. I believe that we get these
+ * when there aren't enough buffers to go around. For now (FIXME), we
+ * just restart the receiver, and hope everything's ok.
+ */
+static int iernr(unit, ie)
+ int unit;
+ struct ie_softc *ie;
+{
+#ifdef doesnt_work
+ setup_rfa((caddr_t)ie->rframes[0], ie);
+
+ ie->scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]);
+ command_and_wait(unit, IE_RU_START, 0, 0);
+#else
+ /* This doesn't work either, but it doesn't hang either. */
+ command_and_wait(unit, IE_RU_DISABLE, 0, 0); /* just in case */
+ setup_rfa((caddr_t)ie->rframes[0], ie); /* ignore cast-qual */
+
+ ie->scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]);
+ command_and_wait(unit, IE_RU_START, 0, 0); /* was ENABLE */
+
+#endif
+ ie_ack(ie->scb, IE_ST_WHENCE, unit, ie->ie_chan_attn);
+
+ ie->arpcom.ac_if.if_ierrors++;
+ return 0;
+}
+
+#ifdef FILTER
+/*
+ * Compare two Ether/802 addresses for equality, inlined and
+ * unrolled for speed. I'd love to have an inline assembler
+ * version of this...
+ */
+static inline int ether_equal(u_char *one, u_char *two) {
+ if(one[0] != two[0]) return 0;
+ if(one[1] != two[1]) return 0;
+ if(one[2] != two[2]) return 0;
+ if(one[3] != two[3]) return 0;
+ if(one[4] != two[4]) return 0;
+ if(one[5] != two[5]) return 0;
+ return 1;
+}
+
+/*
+ * Check for a valid address. to_bpf is filled in with one of the following:
+ * 0 -> BPF doesn't get this packet
+ * 1 -> BPF does get this packet
+ * 2 -> BPF does get this packet, but we don't
+ * Return value is true if the packet is for us, and false otherwise.
+ *
+ * This routine is a mess, but it's also critical that it be as fast
+ * as possible. It could be made cleaner if we can assume that the
+ * only client which will fiddle with IFF_PROMISC is BPF. This is
+ * probably a good assumption, but we do not make it here. (Yet.)
+ */
+static inline int check_eh(struct ie_softc *ie,
+ struct ether_header *eh,
+ int *to_bpf) {
+ int i;
+
+ switch(ie->promisc) {
+ case IFF_ALLMULTI:
+ /*
+ * Receiving all multicasts, but no unicasts except those destined for us.
+ */
+#if NBPFILTER > 0
+ *to_bpf = (ie->ie_bpf != 0); /* BPF gets this packet if anybody cares */
+#endif
+ if(eh->ether_dhost[0] & 1) {
+ return 1;
+ }
+ if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1;
+ return 0;
+
+ case IFF_PROMISC:
+ /*
+ * Receiving all packets. These need to be passed on to BPF.
+ */
+#if NBPFILTER > 0
+ *to_bpf = (ie->ie_bpf != 0);
+#endif
+ /* If for us, accept and hand up to BPF */
+ if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1;
+
+#if NBPFILTER > 0
+ if(*to_bpf) *to_bpf = 2; /* we don't need to see it */
+#endif
+
+#ifdef MULTICAST
+ /*
+ * Not a multicast, so BPF wants to see it but we don't.
+ */
+ if(!(eh->ether_dhost[0] & 1)) return 1;
+
+ /*
+ * If it's one of our multicast groups, accept it and pass it
+ * up.
+ */
+ for(i = 0; i < ie->mcast_count; i++) {
+ if(ether_equal(eh->ether_dhost, (u_char *)&ie->mcast_addrs[i])) {
+#if NBPFILTER > 0
+ if(*to_bpf) *to_bpf = 1;
+#endif
+ return 1;
+ }
+ }
+#endif /* MULTICAST */
+ return 1;
+
+ case IFF_ALLMULTI | IFF_PROMISC:
+ /*
+ * Acting as a multicast router, and BPF running at the same time.
+ * Whew! (Hope this is a fast machine...)
+ */
+#if NBPFILTER > 0
+ *to_bpf = (ie->ie_bpf != 0);
+#endif
+ /* We want to see multicasts. */
+ if(eh->ether_dhost[0] & 1) return 1;
+
+ /* We want to see our own packets */
+ if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1;
+
+ /* Anything else goes to BPF but nothing else. */
+#if NBPFILTER > 0
+ if(*to_bpf) *to_bpf = 2;
+#endif
+ return 1;
+
+ default:
+ /*
+ * Only accept unicast packets destined for us, or multicasts
+ * for groups that we belong to. For now, we assume that the
+ * '586 will only return packets that we asked it for. This
+ * isn't strictly true (it uses hashing for the multicast filter),
+ * but it will do in this case, and we want to get out of here
+ * as quickly as possible.
+ */
+#if NBPFILTER > 0
+ *to_bpf = (ie->ie_bpf != 0);
+#endif
+ return 1;
+ }
+ return 0;
+}
+#endif /* FILTER */
+
+/*
+ * We want to isolate the bits that have meaning... This assumes that
+ * IE_RBUF_SIZE is an even power of two. If somehow the act_len exceeds
+ * the size of the buffer, then we are screwed anyway.
+ */
+static inline int ie_buflen(struct ie_softc *ie, int head) {
+ return (ie->rbuffs[head]->ie_rbd_actual
+ & (IE_RBUF_SIZE | (IE_RBUF_SIZE - 1)));
+}
+
+static inline int ie_packet_len(int unit, struct ie_softc *ie) {
+ int i;
+ int head = ie->rbhead;
+ int acc = 0;
+
+ do {
+ if(!(ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_USED)) {
+#ifdef DEBUG
+ print_rbd(ie->rbuffs[ie->rbhead]);
+#endif
+ log(LOG_ERR, "ie%d: receive descriptors out of sync at %d\n",
+ unit, ie->rbhead);
+ iereset(unit);
+ return -1;
+ }
+
+ i = ie->rbuffs[head]->ie_rbd_actual & IE_RBD_LAST;
+
+ acc += ie_buflen(ie, head);
+ head = (head + 1) % NBUFFS;
+ } while(!i);
+
+ return acc;
+}
+
+/*
+ * Read data off the interface, and turn it into an mbuf chain.
+ *
+ * This code is DRAMATICALLY different from the previous version; this
+ * version tries to allocate the entire mbuf chain up front, given the
+ * length of the data available. This enables us to allocate mbuf
+ * clusters in many situations where before we would have had a long
+ * chain of partially-full mbufs. This should help to speed up the
+ * operation considerably. (Provided that it works, of course.)
+ */
+static inline int ieget(unit, ie, mp, ehp, to_bpf)
+ int unit;
+ struct ie_softc *ie;
+ struct mbuf **mp;
+ struct ether_header *ehp;
+ int *to_bpf;
+{
+ struct mbuf *m, *top, **mymp;
+ int i;
+ int offset;
+ int totlen, resid;
+ int thismboff;
+ int head;
+
+ totlen = ie_packet_len(unit, ie);
+ if(totlen <= 0) return -1;
+
+ i = ie->rbhead;
+
+ /*
+ * Snarf the Ethernet header.
+ */
+ bcopy((caddr_t)ie->cbuffs[i], (caddr_t)ehp, sizeof *ehp);
+ /* ignore cast-qual warning here */
+
+ /*
+ * As quickly as possible, check if this packet is for us.
+ * If not, don't waste a single cycle copying the rest of the
+ * packet in.
+ * This is only a consideration when FILTER is defined; i.e., when
+ * we are either running BPF or doing multicasting.
+ */
+#ifdef FILTER
+ if(!check_eh(ie, ehp, to_bpf)) {
+ ie_drop_packet_buffer(unit, ie);
+ ie->arpcom.ac_if.if_ierrors--; /* just this case, it's not an error */
+ return -1;
+ }
+#endif
+ totlen -= (offset = sizeof *ehp);
+
+ MGETHDR(*mp, M_DONTWAIT, MT_DATA);
+ if(!*mp) {
+ ie_drop_packet_buffer(unit, ie);
+ return -1;
+ }
+
+ m = *mp;
+ m->m_pkthdr.rcvif = &ie->arpcom.ac_if;
+ m->m_len = MHLEN;
+ resid = m->m_pkthdr.len = totlen;
+ top = 0;
+ mymp = &top;
+
+ /*
+ * This loop goes through and allocates mbufs for all the data we will
+ * be copying in. It does not actually do the copying yet.
+ */
+ do { /* while(resid > 0) */
+ /*
+ * Try to allocate an mbuf to hold the data that we have. If we
+ * already allocated one, just get another one and stick it on the
+ * end (eventually). If we don't already have one, try to allocate
+ * an mbuf cluster big enough to hold the whole packet, if we think it's
+ * reasonable, or a single mbuf which may or may not be big enough.
+ * Got that?
+ */
+ if(top) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if(!m) {
+ m_freem(top);
+ ie_drop_packet_buffer(unit, ie);
+ return -1;
+ }
+ m->m_len = MLEN;
+ }
+
+ if(resid >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if(m->m_flags & M_EXT)
+ m->m_len = min(resid, MCLBYTES);
+ } else {
+ if(resid < m->m_len) {
+ if(!top && resid + max_linkhdr <= m->m_len)
+ m->m_data += max_linkhdr;
+ m->m_len = resid;
+ }
+ }
+ resid -= m->m_len;
+ *mymp = m;
+ mymp = &m->m_next;
+ } while(resid > 0);
+
+ resid = totlen;
+ m = top;
+ thismboff = 0;
+ head = ie->rbhead;
+
+ /*
+ * Now we take the mbuf chain (hopefully only one mbuf most of the
+ * time) and stuff the data into it. There are no possible failures
+ * at or after this point.
+ */
+ while(resid > 0) { /* while there's stuff left */
+ int thislen = ie_buflen(ie, head) - offset;
+
+ /*
+ * If too much data for the current mbuf, then fill the current one
+ * up, go to the next one, and try again.
+ */
+ if(thislen > m->m_len - thismboff) {
+ int newlen = m->m_len - thismboff;
+ bcopy((caddr_t)(ie->cbuffs[head] + offset),
+ mtod(m, caddr_t) + thismboff, (unsigned)newlen);
+ /* ignore cast-qual warning */
+ m = m->m_next;
+ thismboff = 0; /* new mbuf, so no offset */
+ offset += newlen; /* we are now this far into the packet */
+ resid -= newlen; /* so there is this much left to get */
+ continue;
+ }
+
+ /*
+ * If there is more than enough space in the mbuf to hold the
+ * contents of this buffer, copy everything in, advance pointers,
+ * and so on.
+ */
+ if(thislen < m->m_len - thismboff) {
+ bcopy((caddr_t)(ie->cbuffs[head] + offset), /* ignore warning */
+ mtod(m, caddr_t) + thismboff, (unsigned)thislen);
+ thismboff += thislen; /* we are this far into the mbuf */
+ resid -= thislen; /* and this much is left */
+ goto nextbuf;
+ }
+
+ /*
+ * Otherwise, there is exactly enough space to put this buffer's
+ * contents into the current mbuf. Do the combination of the above
+ * actions.
+ */
+ bcopy((caddr_t)(ie->cbuffs[head] + offset), /* ignore warning */
+ mtod(m, caddr_t) + thismboff, (unsigned)thislen);
+ m = m->m_next;
+ thismboff = 0; /* new mbuf, start at the beginning */
+ resid -= thislen; /* and we are this far through */
+
+ /*
+ * Advance all the pointers. We can get here from either of the
+ * last two cases, but never the first.
+ */
+nextbuf:
+ offset = 0;
+ ie->rbuffs[head]->ie_rbd_actual = 0;
+ ie->rbuffs[head]->ie_rbd_length |= IE_RBD_LAST;
+ ie->rbhead = head = (head + 1) % NBUFFS;
+ ie->rbuffs[ie->rbtail]->ie_rbd_length &= ~IE_RBD_LAST;
+ ie->rbtail = (ie->rbtail + 1) % NBUFFS;
+ }
+
+ /*
+ * Unless something changed strangely while we were doing the copy,
+ * we have now copied everything in from the shared memory.
+ * This means that we are done.
+ */
+ return 0;
+}
+
+/*
+ * Read frame NUM from unit UNIT (pre-cached as IE).
+ *
+ * This routine reads the RFD at NUM, and copies in the buffers from
+ * the list of RBD, then rotates the RBD and RFD lists so that the receiver
+ * doesn't start complaining. Trailers are DROPPED---there's no point
+ * in wasting time on confusing code to deal with them. Hopefully,
+ * this machine will never ARP for trailers anyway.
+ */
+static void ie_readframe(unit, ie, num)
+ int unit;
+ struct ie_softc *ie;
+ int num; /* frame number to read */
+{
+ struct ie_recv_frame_desc rfd;
+ struct mbuf *m = 0;
+ struct ether_header eh;
+#if NBPFILTER > 0
+ int bpf_gets_it = 0;
+#endif
+
+ bcopy((caddr_t)(ie->rframes[num]), &rfd, sizeof(struct ie_recv_frame_desc));
+
+ /* Immediately advance the RFD list, since we we have copied ours now. */
+ ie->rframes[num]->ie_fd_status = 0;
+ ie->rframes[num]->ie_fd_last |= IE_FD_LAST;
+ ie->rframes[ie->rftail]->ie_fd_last &= ~IE_FD_LAST;
+ ie->rftail = (ie->rftail + 1) % NFRAMES;
+ ie->rfhead = (ie->rfhead + 1) % NFRAMES;
+
+ if(rfd.ie_fd_status & IE_FD_OK) {
+ if(
+#if NBPFILTER > 0
+ ieget(unit, ie, &m, &eh, &bpf_gets_it)
+#else
+ ieget(unit, ie, &m, &eh, (int *)0)
+#endif
+ ) {
+ ie->arpcom.ac_if.if_ierrors++; /* this counts as an error */
+ return;
+ }
+ }
+
+#ifdef DEBUG
+ if(ie_debug & IED_READFRAME) {
+ printf("ie%d: frame from ether %s type %x\n", unit,
+ ether_sprintf(eh.ether_shost), (unsigned)eh.ether_type);
+ }
+ if(ntohs(eh.ether_type) > ETHERTYPE_TRAIL
+ && ntohs(eh.ether_type) < (ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER))
+ printf("received trailer!\n");
+#endif
+
+ if(!m) return;
+
+#ifdef FILTER
+ if(last_not_for_us) {
+ m_freem(last_not_for_us);
+ last_not_for_us = 0;
+ }
+
+#if NBPFILTER > 0
+ /*
+ * Check for a BPF filter; if so, hand it up.
+ * Note that we have to stick an extra mbuf up front, because
+ * bpf_mtap expects to have the ether header at the front.
+ * It doesn't matter that this results in an ill-formatted mbuf chain,
+ * since BPF just looks at the data. (It doesn't try to free the mbuf,
+ * tho' it will make a copy for tcpdump.)
+ */
+ if(bpf_gets_it) {
+ struct mbuf m0;
+ m0.m_len = sizeof eh;
+ m0.m_data = (caddr_t)&eh;
+ m0.m_next = m;
+
+ /* Pass it up */
+ bpf_mtap(ie->ie_bpf, &m0);
+ }
+ /*
+ * A signal passed up from the filtering code indicating that the
+ * packet is intended for BPF but not for the protocol machinery.
+ * We can save a few cycles by not handing it off to them.
+ */
+ if(bpf_gets_it == 2) {
+ last_not_for_us = m;
+ return;
+ }
+#endif /* NBPFILTER > 0 */
+ /*
+ * In here there used to be code to check destination addresses upon
+ * receipt of a packet. We have deleted that code, and replaced it
+ * with code to check the address much earlier in the cycle, before
+ * copying the data in; this saves us valuable cycles when operating
+ * as a multicast router or when using BPF.
+ */
+#endif /* FILTER */
+
+ eh.ether_type = ntohs(eh.ether_type);
+
+ /*
+ * Finally pass this packet up to higher layers.
+ */
+ ether_input(&ie->arpcom.ac_if, &eh, m);
+}
+
+static void ie_drop_packet_buffer(int unit, struct ie_softc *ie) {
+ int i;
+
+ do {
+ /*
+ * This means we are somehow out of sync. So, we reset the
+ * adapter.
+ */
+ if(!(ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_USED)) {
+#ifdef DEBUG
+ print_rbd(ie->rbuffs[ie->rbhead]);
+#endif
+ log(LOG_ERR, "ie%d: receive descriptors out of sync at %d\n",
+ unit, ie->rbhead);
+ iereset(unit);
+ return;
+ }
+
+ i = ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_LAST;
+
+ ie->rbuffs[ie->rbhead]->ie_rbd_length |= IE_RBD_LAST;
+ ie->rbuffs[ie->rbhead]->ie_rbd_actual = 0;
+ ie->rbhead = (ie->rbhead + 1) % NBUFFS;
+ ie->rbuffs[ie->rbtail]->ie_rbd_length &= ~IE_RBD_LAST;
+ ie->rbtail = (ie->rbtail + 1) % NBUFFS;
+ } while(!i);
+}
+
+
+/*
+ * Start transmission on an interface.
+ */
+static void
+iestart(ifp)
+ struct ifnet *ifp;
+{
+ struct ie_softc *ie = &ie_softc[ifp->if_unit];
+ struct mbuf *m0, *m;
+ unsigned char *buffer;
+ u_short len;
+ /* This is not really volatile, in this routine, but it makes gcc happy. */
+ volatile u_short *bptr = &ie->scb->ie_command_list;
+
+ if(!(ifp->if_flags & IFF_RUNNING))
+ return;
+ if(ifp->if_flags & IFF_OACTIVE)
+ return;
+
+ do {
+ IF_DEQUEUE(&ie->arpcom.ac_if.if_snd, m);
+ if(!m)
+ break;
+
+ buffer = ie->xmit_cbuffs[ie->xmit_count];
+ len = 0;
+
+ for(m0 = m; m && len < IE_BUF_LEN; m = m->m_next) {
+ bcopy(mtod(m, caddr_t), buffer, m->m_len);
+ buffer += m->m_len;
+ len += m->m_len;
+ }
+
+ m_freem(m0);
+ len = max(len, ETHERMINLEN);
+
+#if NBPFILTER > 0
+ /*
+ * See if bpf is listening on this interface, let it see the packet
+ * before we commit it to the wire.
+ */
+ if(ie->ie_bpf)
+ bpf_tap(ie->ie_bpf, ie->xmit_cbuffs[ie->xmit_count], len);
+#endif
+
+ ie->xmit_buffs[ie->xmit_count]->ie_xmit_flags = IE_XMIT_LAST | len;
+ ie->xmit_buffs[ie->xmit_count]->ie_xmit_next = 0xffff;
+ ie->xmit_buffs[ie->xmit_count]->ie_xmit_buf =
+ MK_24(ie->iomem, ie->xmit_cbuffs[ie->xmit_count]);
+
+ ie->xmit_cmds[ie->xmit_count]->com.ie_cmd_cmd = IE_CMD_XMIT;
+ ie->xmit_cmds[ie->xmit_count]->ie_xmit_status = 0;
+ ie->xmit_cmds[ie->xmit_count]->ie_xmit_desc =
+ MK_16(ie->iomem, ie->xmit_buffs[ie->xmit_count]);
+
+ *bptr = MK_16(ie->iomem, ie->xmit_cmds[ie->xmit_count]);
+ bptr = &ie->xmit_cmds[ie->xmit_count]->com.ie_cmd_link;
+ ie->xmit_count++;
+ } while(ie->xmit_count < 2);
+
+ /*
+ * If we queued up anything for transmission, send it.
+ */
+ if(ie->xmit_count) {
+ ie->xmit_cmds[ie->xmit_count - 1]->com.ie_cmd_cmd |=
+ IE_CMD_LAST | IE_CMD_INTR;
+
+ /*
+ * By passing the command pointer as a null, we tell
+ * command_and_wait() to pretend that this isn't an action
+ * command. I wish I understood what was happening here.
+ */
+ command_and_wait(ifp->if_unit, IE_CU_START, 0, 0);
+ ifp->if_flags |= IFF_OACTIVE;
+ }
+
+ return;
+}
+
+/*
+ * Check to see if there's an 82586 out there.
+ */
+int check_ie_present(unit, where, size)
+ int unit;
+ caddr_t where;
+ unsigned size;
+{
+ volatile struct ie_sys_conf_ptr *scp;
+ volatile struct ie_int_sys_conf_ptr *iscp;
+ volatile struct ie_sys_ctl_block *scb;
+ u_long realbase;
+ int s;
+
+ s = splimp();
+
+ realbase = (u_long)where + size - (1 << 24);
+
+ scp = (volatile struct ie_sys_conf_ptr *)(realbase + IE_SCP_ADDR);
+ bzero((char *)scp, sizeof *scp); /* ignore cast-qual */
+
+ /*
+ * First we put the ISCP at the bottom of memory; this tests to make
+ * sure that our idea of the size of memory is the same as the controller's.
+ * This is NOT where the ISCP will be in normal operation.
+ */
+ iscp = (volatile struct ie_int_sys_conf_ptr *)where;
+ bzero((char *)iscp, sizeof *iscp); /* ignore cast-qual */
+
+ scb = (volatile struct ie_sys_ctl_block *)where;
+ bzero((char *)scb, sizeof *scb); /* ignore cast-qual */
+
+ scp->ie_bus_use = 0; /* 16-bit */
+ scp->ie_iscp_ptr = (caddr_t)((volatile caddr_t)iscp - /* ignore cast-qual */
+ (volatile caddr_t)realbase);
+
+ iscp->ie_busy = 1;
+ iscp->ie_scb_offset = MK_16(realbase, scb) + 256;
+
+ (*ie_softc[unit].ie_reset_586)(unit);
+ (*ie_softc[unit].ie_chan_attn)(unit);
+
+ DELAY(100); /* wait a while... */
+
+ if(iscp->ie_busy) {
+ splx(s);
+ return 0;
+ }
+
+ /*
+ * Now relocate the ISCP to its real home, and reset the controller
+ * again.
+ */
+ iscp = (void *)Align((caddr_t)(realbase + IE_SCP_ADDR -
+ sizeof(struct ie_int_sys_conf_ptr)));
+ bzero((char *)iscp, sizeof *iscp); /* ignore cast-qual */
+
+ scp->ie_iscp_ptr = (caddr_t)((caddr_t)iscp - (caddr_t)realbase);
+ /* ignore cast-qual */
+
+ iscp->ie_busy = 1;
+ iscp->ie_scb_offset = MK_16(realbase, scb);
+
+ (*ie_softc[unit].ie_reset_586)(unit);
+ (*ie_softc[unit].ie_chan_attn)(unit);
+
+ DELAY(100);
+
+ if(iscp->ie_busy) {
+ splx(s);
+ return 0;
+ }
+
+ ie_softc[unit].iosize = size;
+ ie_softc[unit].iomem = (caddr_t)realbase;
+
+ ie_softc[unit].iscp = iscp;
+ ie_softc[unit].scb = scb;
+
+ /*
+ * Acknowledge any interrupts we may have caused...
+ */
+ ie_ack(scb, IE_ST_WHENCE, unit, ie_softc[unit].ie_chan_attn);
+ splx(s);
+
+ return 1;
+}
+
+/*
+ * Divine the memory size of ie board UNIT.
+ * Better hope there's nothing important hiding just below the ie card...
+ */
+static void find_ie_mem_size(unit)
+ int unit;
+{
+ unsigned size;
+
+ ie_softc[unit].iosize = 0;
+
+ for(size = 65536; size >= 16384; size -= 16384) {
+ if(check_ie_present(unit, ie_softc[unit].iomembot, size)) {
+ return;
+ }
+ }
+
+ return;
+}
+
+void sl_reset_586(unit)
+ int unit;
+{
+ outb(PORT + IEATT_RESET, 0);
+}
+
+void sl_chan_attn(unit)
+ int unit;
+{
+ outb(PORT + IEATT_ATTN, 0);
+}
+
+void sl_read_ether(unit, addr)
+ int unit;
+ unsigned char addr[6];
+{
+ int i;
+
+ for(i = 0; i < 6; i++)
+ addr[i] = inb(PORT + i);
+}
+
+
+static void
+iereset(unit)
+ int unit;
+{
+ int s = splimp();
+
+ if(unit >= NIE) {
+ splx(s);
+ return;
+ }
+
+ printf("ie%d: reset\n", unit);
+ ie_softc[unit].arpcom.ac_if.if_flags &= ~IFF_UP;
+ ieioctl(&ie_softc[unit].arpcom.ac_if, SIOCSIFFLAGS, 0);
+
+ /*
+ * Stop i82586 dead in its tracks.
+ */
+ if(command_and_wait(unit, IE_RU_ABORT | IE_CU_ABORT, 0, 0))
+ printf("ie%d: abort commands timed out\n", unit);
+
+ if(command_and_wait(unit, IE_RU_DISABLE | IE_CU_STOP, 0, 0))
+ printf("ie%d: disable commands timed out\n", unit);
+
+#ifdef notdef
+ if(!check_ie_present(unit, ie_softc[unit].iomembot, ie_softc[unit].iosize))
+ panic("ie disappeared!\n");
+#endif
+
+ ie_softc[unit].arpcom.ac_if.if_flags |= IFF_UP;
+ ieioctl(&ie_softc[unit].arpcom.ac_if, SIOCSIFFLAGS, 0);
+
+ splx(s);
+ return;
+}
+
+/*
+ * This is called if we time out.
+ */
+static void
+chan_attn_timeout(rock)
+ caddr_t rock;
+{
+ *(int *)rock = 1;
+}
+
+/*
+ * Send a command to the controller and wait for it to either
+ * complete or be accepted, depending on the command. If the
+ * command pointer is null, then pretend that the command is
+ * not an action command. If the command pointer is not null,
+ * and the command is an action command, wait for
+ * ((volatile struct ie_cmd_common *)pcmd)->ie_cmd_status & MASK
+ * to become true.
+ */
+static int command_and_wait(unit, cmd, pcmd, mask)
+ int unit;
+ int cmd;
+ volatile void *pcmd;
+ int mask;
+{
+ volatile struct ie_cmd_common *cc = pcmd;
+ volatile int timedout = 0;
+ extern int hz;
+
+ ie_softc[unit].scb->ie_command = (u_short)cmd;
+
+ if(IE_ACTION_COMMAND(cmd) && pcmd) {
+ (*ie_softc[unit].ie_chan_attn)(unit);
+
+ /*
+ * According to the packet driver, the minimum timeout should be
+ * .369 seconds, which we round up to .37.
+ */
+ timeout(chan_attn_timeout, (caddr_t)&timedout, 37 * hz / 100);
+ /* ignore cast-qual */
+
+ /*
+ * Now spin-lock waiting for status. This is not a very nice
+ * thing to do, but I haven't figured out how, or indeed if, we
+ * can put the process waiting for action to sleep. (We may
+ * be getting called through some other timeout running in the
+ * kernel.)
+ */
+ while(1) {
+ if((cc->ie_cmd_status & mask) || timedout)
+ break;
+ }
+
+ untimeout(chan_attn_timeout, (caddr_t)&timedout);
+ /* ignore cast-qual */
+
+ return timedout;
+ } else {
+
+ /*
+ * Otherwise, just wait for the command to be accepted.
+ */
+ (*ie_softc[unit].ie_chan_attn)(unit);
+
+ while(ie_softc[unit].scb->ie_command)
+ ; /* spin lock */
+
+ return 0;
+ }
+}
+
+/*
+ * Run the time-domain reflectometer...
+ */
+static void run_tdr(unit, cmd)
+ int unit;
+ struct ie_tdr_cmd *cmd;
+{
+ int result;
+
+ cmd->com.ie_cmd_status = 0;
+ cmd->com.ie_cmd_cmd = IE_CMD_TDR | IE_CMD_LAST;
+ cmd->com.ie_cmd_link = 0xffff;
+ cmd->ie_tdr_time = 0;
+
+ ie_softc[unit].scb->ie_command_list = MK_16(MEM, cmd);
+ cmd->ie_tdr_time = 0;
+
+ if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL))
+ result = 0x2000;
+ else
+ result = cmd->ie_tdr_time;
+
+ ie_ack(ie_softc[unit].scb, IE_ST_WHENCE, unit,
+ ie_softc[unit].ie_chan_attn);
+
+ if(result & IE_TDR_SUCCESS)
+ return;
+
+ if(result & IE_TDR_XCVR) {
+ printf("ie%d: transceiver problem\n", unit);
+ } else if(result & IE_TDR_OPEN) {
+ printf("ie%d: TDR detected an open %d clocks away\n", unit,
+ result & IE_TDR_TIME);
+ } else if(result & IE_TDR_SHORT) {
+ printf("ie%d: TDR detected a short %d clocks away\n", unit,
+ result & IE_TDR_TIME);
+ } else {
+ printf("ie%d: TDR returned unknown status %x\n", result);
+ }
+}
+
+static void start_receiver(unit)
+ int unit;
+{
+ int s = splimp();
+
+ ie_softc[unit].scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]);
+ command_and_wait(unit, IE_RU_START, 0, 0);
+
+ ie_ack(ie_softc[unit].scb, IE_ST_WHENCE, unit, ie_softc[unit].ie_chan_attn);
+
+ splx(s);
+}
+
+/*
+ * Here is a helper routine for iernr() and ieinit(). This sets up
+ * the RFA.
+ */
+static caddr_t setup_rfa(caddr_t ptr, struct ie_softc *ie) {
+ volatile struct ie_recv_frame_desc *rfd = (void *)ptr;
+ volatile struct ie_recv_buf_desc *rbd;
+ int i;
+ int unit = ie - &ie_softc[0];
+
+ /* First lay them out */
+ for(i = 0; i < NFRAMES; i++) {
+ ie->rframes[i] = rfd;
+ bzero((char *)rfd, sizeof *rfd); /* ignore cast-qual */
+ rfd++;
+ }
+
+ ptr = (caddr_t)Align((caddr_t)rfd); /* ignore cast-qual */
+
+ /* Now link them together */
+ for(i = 0; i < NFRAMES; i++) {
+ ie->rframes[i]->ie_fd_next =
+ MK_16(MEM, ie->rframes[(i + 1) % NFRAMES]);
+ }
+
+ /* Finally, set the EOL bit on the last one. */
+ ie->rframes[NFRAMES - 1]->ie_fd_last |= IE_FD_LAST;
+
+ /*
+ * Now lay out some buffers for the incoming frames. Note that
+ * we set aside a bit of slop in each buffer, to make sure that
+ * we have enough space to hold a single frame in every buffer.
+ */
+ rbd = (void *)ptr;
+
+ for(i = 0; i < NBUFFS; i++) {
+ ie->rbuffs[i] = rbd;
+ bzero((char *)rbd, sizeof *rbd); /* ignore cast-qual */
+ ptr = (caddr_t)Align(ptr + sizeof *rbd);
+ rbd->ie_rbd_length = IE_RBUF_SIZE;
+ rbd->ie_rbd_buffer = MK_24(MEM, ptr);
+ ie->cbuffs[i] = (void *)ptr;
+ ptr += IE_RBUF_SIZE;
+ rbd = (void *)ptr;
+ }
+
+ /* Now link them together */
+ for(i = 0; i < NBUFFS; i++) {
+ ie->rbuffs[i]->ie_rbd_next = MK_16(MEM, ie->rbuffs[(i + 1) % NBUFFS]);
+ }
+
+ /* Tag EOF on the last one */
+ ie->rbuffs[NBUFFS - 1]->ie_rbd_length |= IE_RBD_LAST;
+
+ /* We use the head and tail pointers on receive to keep track of
+ * the order in which RFDs and RBDs are used. */
+ ie->rfhead = 0;
+ ie->rftail = NFRAMES - 1;
+ ie->rbhead = 0;
+ ie->rbtail = NBUFFS - 1;
+
+ ie->scb->ie_recv_list = MK_16(MEM, ie->rframes[0]);
+ ie->rframes[0]->ie_fd_buf_desc = MK_16(MEM, ie->rbuffs[0]);
+
+ ptr = Align(ptr);
+ return ptr;
+}
+
+/*
+ * Run the multicast setup command.
+ * Call at splimp().
+ */
+static int mc_setup(int unit, caddr_t ptr,
+ volatile struct ie_sys_ctl_block *scb) {
+ struct ie_softc *ie = &ie_softc[unit];
+ volatile struct ie_mcast_cmd *cmd = (void *)ptr;
+
+ cmd->com.ie_cmd_status = 0;
+ cmd->com.ie_cmd_cmd = IE_CMD_MCAST | IE_CMD_LAST;
+ cmd->com.ie_cmd_link = 0xffff;
+
+ /* ignore cast-qual */
+ bcopy((caddr_t)ie->mcast_addrs, (caddr_t)cmd->ie_mcast_addrs,
+ ie->mcast_count * sizeof *ie->mcast_addrs);
+
+ cmd->ie_mcast_bytes = ie->mcast_count * 6; /* grrr... */
+
+ scb->ie_command_list = MK_16(MEM, cmd);
+ if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL)
+ || !(cmd->com.ie_cmd_status & IE_STAT_OK)) {
+ printf("ie%d: multicast address setup command failed\n", unit);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * This routine takes the environment generated by check_ie_present()
+ * and adds to it all the other structures we need to operate the adapter.
+ * This includes executing the CONFIGURE, IA-SETUP, and MC-SETUP commands,
+ * starting the receiver unit, and clearing interrupts.
+ *
+ * THIS ROUTINE MUST BE CALLED AT splimp() OR HIGHER.
+ */
+static void
+ieinit(unit)
+ int unit;
+{
+ struct ie_softc *ie = &ie_softc[unit];
+ volatile struct ie_sys_ctl_block *scb = ie->scb;
+ caddr_t ptr;
+
+ ptr = (caddr_t)Align((caddr_t)scb + sizeof *scb); /* ignore cast-qual */
+
+ /*
+ * Send the configure command first.
+ */
+ {
+ volatile struct ie_config_cmd *cmd = (void *)ptr;
+
+ ie_setup_config(cmd, ie->promisc, ie->hard_type == IE_STARLAN10);
+ cmd->com.ie_cmd_status = 0;
+ cmd->com.ie_cmd_cmd = IE_CMD_CONFIG | IE_CMD_LAST;
+ cmd->com.ie_cmd_link = 0xffff;
+
+ scb->ie_command_list = MK_16(MEM, cmd);
+
+ if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL)
+ || !(cmd->com.ie_cmd_status & IE_STAT_OK)) {
+ printf("ie%d: configure command failed\n", unit);
+ return;
+ }
+ }
+ /*
+ * Now send the Individual Address Setup command.
+ */
+ {
+ volatile struct ie_iasetup_cmd *cmd = (void *)ptr;
+
+ cmd->com.ie_cmd_status = 0;
+ cmd->com.ie_cmd_cmd = IE_CMD_IASETUP | IE_CMD_LAST;
+ cmd->com.ie_cmd_link = 0xffff;
+
+ bcopy((char *)ie_softc[unit].arpcom.ac_enaddr, (char *)&cmd->ie_address,
+ sizeof cmd->ie_address); /* ignore cast-qual */
+
+ scb->ie_command_list = MK_16(MEM, cmd);
+ if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL)
+ || !(cmd->com.ie_cmd_status & IE_STAT_OK)) {
+ printf("ie%d: individual address setup command failed\n", unit);
+ return;
+ }
+ }
+
+ /*
+ * Now run the time-domain reflectometer.
+ */
+ run_tdr(unit, (void *)ptr);
+
+ /*
+ * Acknowledge any interrupts we have generated thus far.
+ */
+ ie_ack(ie->scb, IE_ST_WHENCE, unit, ie->ie_chan_attn);
+
+ /*
+ * Set up the RFA.
+ */
+ ptr = setup_rfa(ptr, ie);
+
+ /*
+ * Finally, the transmit command and buffer are the last little bit of work.
+ */
+ ie->xmit_cmds[0] = (void *)ptr;
+ ptr += sizeof *ie->xmit_cmds[0];
+ ptr = Align(ptr);
+ ie->xmit_buffs[0] = (void *)ptr;
+ ptr += sizeof *ie->xmit_buffs[0];
+ ptr = Align(ptr);
+
+ /* Second transmit command */
+ ie->xmit_cmds[1] = (void *)ptr;
+ ptr += sizeof *ie->xmit_cmds[1];
+ ptr = Align(ptr);
+ ie->xmit_buffs[1] = (void *)ptr;
+ ptr += sizeof *ie->xmit_buffs[1];
+ ptr = Align(ptr);
+
+ /* Both transmit buffers */
+ ie->xmit_cbuffs[0] = (void *)ptr;
+ ptr += IE_BUF_LEN;
+ ptr = Align(ptr);
+ ie->xmit_cbuffs[1] = (void *)ptr;
+
+ bzero((caddr_t)ie->xmit_cmds[0], sizeof *ie->xmit_cmds[0]); /* ignore */
+ bzero((caddr_t)ie->xmit_buffs[0], sizeof *ie->xmit_buffs[0]); /* cast-qual */
+ bzero((caddr_t)ie->xmit_cmds[1], sizeof *ie->xmit_cmds[0]); /* warnings */
+ bzero((caddr_t)ie->xmit_buffs[1], sizeof *ie->xmit_buffs[0]); /* here */
+
+ /*
+ * This must be coordinated with iestart() and ietint().
+ */
+ ie->xmit_cmds[0]->ie_xmit_status = IE_STAT_COMPL;
+
+ ie->arpcom.ac_if.if_flags |= IFF_RUNNING; /* tell higher levels that we are here */
+ start_receiver(unit);
+ return;
+}
+
+static void ie_stop(unit)
+ int unit;
+{
+ command_and_wait(unit, IE_RU_DISABLE, 0, 0);
+}
+
+static int
+ieioctl(ifp, command, data)
+ struct ifnet *ifp;
+ int command;
+ caddr_t data;
+{
+ struct ifaddr *ifa = (struct ifaddr *)data;
+ struct ie_softc *ie = &ie_softc[ifp->if_unit];
+ int s, error = 0;
+
+ s = splimp();
+
+ switch(command) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+
+ switch(ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ ieinit(ifp->if_unit);
+ ((struct arpcom *)ifp)->ac_ipaddr =
+ IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
+ break;
+#endif /* INET */
+
+#ifdef NS
+ /* This magic copied from if_is.c; I don't use XNS, so I have no
+ * way of telling if this actually works or not.
+ */
+ case AF_NS:
+ {
+ struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+ if(ns_nullhost(*ina)) {
+ ina->x_host = *(union ns_host *)(ie->arpcom.ac_enaddr);
+ } else {
+ ifp->if_flags &= ~IFF_RUNNING;
+ bcopy((caddr_t)ina->x_host.c_host,
+ (caddr_t)ie->arpcom.ac_enaddr,
+ sizeof ie->arpcom.ac_enaddr);
+ }
+
+ ieinit(ifp->if_unit);
+ }
+ break;
+#endif /* NS */
+
+ default:
+ ieinit(ifp->if_unit);
+ break;
+ }
+ break;
+
+ case SIOCSIFFLAGS:
+ /*
+ * Note that this device doesn't have an "all multicast" mode, so we
+ * must turn on promiscuous mode and do the filtering manually.
+ */
+ if((ifp->if_flags & IFF_UP) == 0 &&
+ (ifp->if_flags & IFF_RUNNING)) {
+ ifp->if_flags &= ~IFF_RUNNING;
+ ie_stop(ifp->if_unit);
+ } else if((ifp->if_flags & IFF_UP) &&
+ (ifp->if_flags & IFF_RUNNING) == 0) {
+ ie_softc[ifp->if_unit].promisc =
+ ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI);
+ ieinit(ifp->if_unit);
+ } else if(ie_softc[ifp->if_unit].promisc ^
+ (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI))) {
+ ie_softc[ifp->if_unit].promisc =
+ ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI);
+ ieinit(ifp->if_unit);
+ }
+ break;
+
+#ifdef MULTICAST
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ /*
+ * Update multicast listeners
+ */
+ error = ((command == SIOCADDMULTI)
+ ? ether_addmulti((struct ifreq *)data, &ie->arpcom)
+ : ether_delmulti((struct ifreq *)data, &ie->arpcom));
+
+ if(error == ENETRESET) {
+ /* reset multicast filtering */
+ ie_mc_reset(ifp->if_unit);
+ error = 0;
+ }
+ break;
+#endif /* MULTICAST */
+
+ default:
+ error = EINVAL;
+ }
+
+ splx(s);
+ return error;
+}
+
+#ifdef MULTICAST
+static void ie_mc_reset(int unit) {
+ struct ie_softc *ie = &ie_softc[unit];
+ struct ether_multi *enm;
+ struct ether_multistep step;
+
+ /*
+ * Step through the list of addresses.
+ */
+ ie->mcast_count = 0;
+ ETHER_FIRST_MULTI(step, &ie->arpcom, enm);
+ while(enm) {
+ if(ie->mcast_count >= MAXMCAST
+ || bcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) {
+ ie->arpcom.ac_if.if_flags |= IFF_ALLMULTI;
+ ieioctl(&ie->arpcom.ac_if, SIOCSIFFLAGS, (void *)0);
+ goto setflag;
+ }
+
+ bcopy(enm->enm_addrlo, &(ie->mcast_addrs[ie->mcast_count]), 6);
+ ie->mcast_count++;
+ ETHER_NEXT_MULTI(step, enm);
+ }
+
+setflag:
+ ie->want_mcsetup = 1;
+}
+
+#endif
+
+#ifdef DEBUG
+void print_rbd(volatile struct ie_recv_buf_desc *rbd) {
+ printf("RBD at %08lx:\n"
+ "actual %04x, next %04x, buffer %08x\n"
+ "length %04x, mbz %04x\n",
+ (unsigned long)rbd,
+ rbd->ie_rbd_actual, rbd->ie_rbd_next, rbd->ie_rbd_buffer,
+ rbd->ie_rbd_length, rbd->mbz);
+}
+#endif /* DEBUG */
+#endif /* NIE > 0 */
+
diff --git a/sys/dev/ie/if_iereg.h b/sys/dev/ie/if_iereg.h
new file mode 100644
index 0000000..3588b84
--- /dev/null
+++ b/sys/dev/ie/if_iereg.h
@@ -0,0 +1,24 @@
+/*
+ * $Id$
+ * definitions for AT&T StarLAN 10 etc...
+ */
+
+#define IEATT_RESET 0 /* any write here resets the 586 */
+#define IEATT_ATTN 1 /* any write here sends a Chan attn */
+#define IEATT_REVISION 6 /* read here to figure out this board */
+#define IEATT_ATTRIB 7 /* more information about this board */
+
+#define SL_BOARD(x) ((x) & 0x0f)
+#define SL_REV(x) ((x) >> 4)
+
+#define SL1_BOARD 0
+#define SL10_BOARD 1
+#define EN100_BOARD 2
+#define SLFIBER_BOARD 3
+
+#define SL_ATTR_WIDTH 0x04 /* bus width: clear -> 8-bit */
+#define SL_ATTR_SPEED 0x08 /* medium speed: clear -> 10 Mbps */
+#define SL_ATTR_CODING 0x10 /* encoding: clear -> Manchester */
+#define SL_ATTR_HBW 0x20 /* host bus width: clear -> 16-bit */
+#define SL_ATTR_TYPE 0x40 /* medium type: clear -> Ethernet */
+#define SL_ATTR_BOOTROM 0x80 /* set -> boot ROM present */
diff --git a/sys/dev/kbd/kbdtables.h b/sys/dev/kbd/kbdtables.h
new file mode 100644
index 0000000..a923c45
--- /dev/null
+++ b/sys/dev/kbd/kbdtables.h
@@ -0,0 +1,859 @@
+/*
+ * Copyright (C) 1992, 1993, 1994 Søren Schmidt
+ *
+ * This program is free software; you may redistribute it and/or
+ * modify it, provided that it retain the above copyright notice
+ * and the following disclaimer.
+ *
+ * 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.
+ *
+ * Søren Schmidt Email: sos@login.dkuug.dk
+ * Tritonvej 36 UUCP: ...uunet!dkuug!login!sos
+ * DK9210 Aalborg SO Phone: +45 9814 8076
+ *
+ * @(#)kbdtables.h 1.3 940123
+ * $Id: kbdtables.h,v 1.11 1994/02/01 09:27:43 ache Exp $
+ */
+
+#define SET8 0x80 /* eight bit for emacs SET8-key */
+
+#ifdef DKKEYMAP
+keymap_t key_map = { 0x69, /* DK iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '"', 0x00, 0x00, '@', '"', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', '#', NOP, NOP, 0x9E, '#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', 0xA4, NOP, NOP, '$', 0xA4, NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '&', NOP, NOP, '6', '&', NOP, NOP, 0x33, 0x00,
+/* sc=08 */ '7', '/', NOP, NOP, '{', '/', NOP, NOP, 0x33, 0x00,
+/* sc=09 */ '8', '(', 0x1B, 0x1B, '[', '(', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ')', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=0b */ '0', '=', NOP, NOP, '}', '=', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '+', '?', NOP, NOP, '+', '?', NOP, NOP, 0x33, 0x00,
+/* sc=0d */ '\'', '`', NOP, NOP, '|', '`', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x00, 0x00,
+/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x33, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ 0xE5, 0xC5, NOP, NOP, 0x86, 0x8F, NOP, NOP, 0x33, 0x01,
+/* sc=1b */ '"', '^', 0x1E, 0x1E, '~', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ 0xE6, 0xC6, NOP, NOP, 0x91, 0x92, NOP, NOP, 0x33, 0x01,
+/* sc=28 */ 0xF8, 0xD8, NOP, NOP, 0x9B, 0x9D, NOP, NOP, 0x33, 0x01,
+/* sc=29 */ 0xBD, 0xA7, NOP, NOP, 0xBD, 0xA7, NOP, NOP, 0x33, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\'', '*', NOP, NOP, '\'', '*', NOP, NOP, 0x33, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00,
+/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', '-', '-', '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ NOP, '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02,
+/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ '<', '>', 0x1C, 0x1C, '\\', '>', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x00, 0x02,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x00,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+#endif
+
+#ifdef UKKEYMAP
+keymap_t key_map = { 0x69, /* uk iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, '`', '`', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', 0xA3, NOP, NOP, '#', '#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '^', 0x1E, 0x1E, '^', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=08 */ '7', '&', NOP, NOP, '[', '[', 0x1B, 0x1B, 0x30, 0x00,
+/* sc=09 */ '8', '*', NOP, NOP, '8', '*', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '9', '(', NOP, NOP, ']', ']', 0x1D, 0x1D, 0x30, 0x00,
+/* sc=0b */ '0', ')', NOP, NOP, '{', '{', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '-', '_', 0x1F, 0x1F, '|', '|', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=0d */ '=', '+', NOP, NOP, '}', '}', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=1b */ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ ';', ':', NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x00,
+/* sc=28 */ '\'', '@', 0x00, 0x00, '\'', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=29 */ '\\', '|', 0x1C, 0x1C, '\\', '\\', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '#', '~', NOP, NOP, '~', '~', NOP, NOP, 0x33, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', '<', NOP, NOP, ',', '<', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', '>', NOP, NOP, '.', '>', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '/', '?', NOP, NOP, '/', '?', NOP, NOP, 0x33, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00,
+/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02,
+/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+#endif
+
+#ifdef GRKEYMAP
+keymap_t key_map = { 0x69, /* german iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, '`', '`', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', 0xA7, NOP, NOP, '#', '#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '&', 0x1E, 0x1E, '^', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=08 */ '7', '/', 0x1B, 0x1B, '[', '[', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=09 */ '8', '(', NOP, NOP, '8', '(', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ']', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=0b */ '0', '=', NOP, NOP, '{', '{', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ 0xDF, '?', NOP, NOP, '|', '|', NOP, NOP, 0x33, 0x00,
+/* sc=0d */ 0x92, 0x93, NOP, NOP, '\'', '`', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ 0xFC, 0xDC, 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x01,
+/* sc=1b */ '+', '*', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ 0xF6, 0xD6, NOP, NOP, 0xF6, 0xD6, NOP, NOP, 0x33, 0x01,
+/* sc=28 */ 0xE4, 0xC4, NOP, NOP, 0xE4, 0xC4, NOP, NOP, 0x33, 0x01,
+/* sc=29 */ '<', '>', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '#', '^', 0x1E, 0x1E, '`', '~', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=2c */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00,
+/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02,
+/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+#endif
+
+#ifdef SWKEYMAP
+keymap_t key_map = { 0x69, /* swedish iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=03 */ '2', '"', NOP, NOP, '@', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=04 */ '3', '#', NOP, NOP, 0xA3, NOP, NOP, NOP, 0x37, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, 0xA4, NOP, NOP, NOP, 0x37, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=07 */ '6', '&', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=08 */ '7', '/', NOP, NOP, '{', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=09 */ '8', '(', NOP, NOP, '[', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=0a */ '9', ')', NOP, NOP, ']', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=0b */ '0', '=', NOP, NOP, '}', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=0c */ '+', '?', NOP, NOP, '\\', NOP, 0x1C, NOP, 0x35, 0x00,
+/* sc=0d */ 0x180, '`', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ 0xE5, 0xC5, NOP, NOP, '}', ']', NOP, NOP, 0x33, 0x01,
+/* sc=1b */ 0xA8, '^', NOP, NOP, '~', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ 0xF6, 0xD6, NOP, NOP, '|', '\\', NOP, NOP, 0x33, 0x01,
+/* sc=28 */ 0xE4, 0xC4, NOP, NOP, '{', '[', NOP, NOP, 0x33, 0x01,
+/* sc=29 */ 0xA7, 0xBD, NOP, NOP, '\\', '|', NOP, NOP, 0x33, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\'', '*', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', ';', NOP, NOP, NOP, '<', NOP, NOP, 0x3B, 0x00,
+/* sc=34 */ '.', ':', NOP, NOP, NOP, '>', NOP, NOP, 0x3B, 0x00,
+/* sc=35 */ '-', '_', 0x1F, NOP, '/', '?', NOP, NOP, 0x13, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00,
+/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02,
+/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ '<', '>', NOP, NOP, '|', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+#endif
+
+#ifdef RUKEYMAP
+keymap_t key_map = { 0xe9, /* keys number */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * -------------------------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, NOP, NOP, SET8|0x1B, SET8|0x1B, DBG, NOP, 0x33, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, SET8|'1', SET8|'!', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '@', 0x00, 0x00, SET8|'2', SET8|'@', SET8|0x00, SET8|0x00, 0x00, 0x00,
+/* sc=04 */ '3', '#', NOP, NOP, SET8|'3', SET8|'#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, SET8|'4', SET8|'$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, SET8|'5', SET8|'%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '^', 0x1E, 0x1E, SET8|'6', SET8|'^', SET8|0x1E, SET8|0x1E, 0x00, 0x00,
+/* sc=08 */ '7', '&', NOP, NOP, SET8|'7', SET8|'&', NOP, NOP, 0x33, 0x00,
+/* sc=09 */ '8', '*', NOP, NOP, SET8|'8', SET8|'*', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '9', '(', NOP, NOP, SET8|'9', SET8|'(', NOP, NOP, 0x33, 0x00,
+/* sc=0b */ '0', ')', NOP, NOP, SET8|'0', SET8|')', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '-', '_', 0x1F, 0x1F, SET8|'-', SET8|'_', SET8|0x1F, SET8|0x1F, 0x00, 0x00,
+/* sc=0d */ '=', '+', NOP, NOP, SET8|'=', SET8|'+', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, SET8|0x08, SET8|0x08, SET8|0x7F, SET8|0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, F(16), NOP, NOP, SET8|0x09, F(16), NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, SET8|'q', SET8|'Q', SET8|0x11, SET8|0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, SET8|'w', SET8|'W', SET8|0x17, SET8|0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, SET8|'e', SET8|'E', SET8|0x05, SET8|0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, SET8|'r', SET8|'R', SET8|0x12, SET8|0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, SET8|'t', SET8|'T', SET8|0x14, SET8|0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, SET8|'y', SET8|'Y', SET8|0x19, SET8|0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, SET8|'u', SET8|'U', SET8|0x15, SET8|0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, SET8|'i', SET8|'I', SET8|0x09, SET8|0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, SET8|'o', SET8|'O', SET8|0x0F, SET8|0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, SET8|'p', SET8|'P', SET8|0x10, SET8|0x10, 0x00, 0x01,
+/* sc=1a */ '[', '{', 0x1B, 0x1B, SET8|'[', SET8|'{', SET8|0x1B, SET8|0x1B, 0x00, 0x00,
+/* sc=1b */ ']', '}', 0x1D, 0x1D, SET8|']', SET8|'}', SET8|0x1D, SET8|0x1D, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, SET8|'a', SET8|'A', SET8|0x01, SET8|0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, SET8|'s', SET8|'S', SET8|0x13, SET8|0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, SET8|'d', SET8|'D', SET8|0x04, SET8|0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, SET8|'f', SET8|'F', SET8|0x06, SET8|0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, SET8|'g', SET8|'G', SET8|0x07, SET8|0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, SET8|'h', SET8|'H', SET8|0x08, SET8|0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, SET8|'j', SET8|'J', SET8|0x0A, SET8|0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, SET8|'k', SET8|'K', SET8|0x0B, SET8|0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, SET8|'l', SET8|'L', SET8|0x0C, SET8|0x0C, 0x00, 0x01,
+/* sc=27 */ ';', ':', NOP, NOP, SET8|';', SET8|':', NOP, NOP, 0x33, 0x00,
+/* sc=28 */ '\'', '"', NOP, NOP, SET8|'\'', SET8|'"', NOP, NOP, 0x33, 0x00,
+/* sc=29 */ '`', '~', NOP, NOP, SET8|'`', SET8|'~', NOP, NOP, 0x33, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\\', '|', 0x1C, 0x1C, SET8|'\\', SET8|'|', SET8|0x1C, SET8|0x1C, 0x00, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, SET8|'z', SET8|'Z', SET8|0x1A, SET8|0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, SET8|'x', SET8|'X', SET8|0x18, SET8|0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, SET8|'c', SET8|'C', SET8|0x03, SET8|0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, SET8|'v', SET8|'V', SET8|0x16, SET8|0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, SET8|'b', SET8|'B', SET8|0x02, SET8|0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, SET8|'n', SET8|'N', SET8|0x0E, SET8|0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, SET8|'m', SET8|'M', SET8|0x0D, SET8|0x0D, 0x00, 0x01,
+/* sc=33 */ ',', '<', NOP, NOP, SET8|',', SET8|'<', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', '>', NOP, NOP, SET8|'.', SET8|'>', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '/', '?', NOP, NOP, SET8|'/', SET8|'?', NOP, NOP, 0x33, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, SET8|'*', SET8|'*', SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', SET8|' ', SET8|' ', SET8|' ', SET8|' ', 0x00, 0x00,
+/* sc=3a */ ALK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00,
+/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', SET8|'7', SET8|'7', SET8|'7', SET8|'7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', SET8|'8', SET8|'8', SET8|'8', SET8|'8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', SET8|'9', SET8|'9', SET8|'9', SET8|'9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', '-', '-', SET8|'-', SET8|'-', SET8|'-', SET8|'-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', SET8|'4', SET8|'4', SET8|'4', SET8|'4', 0x80, 0x02,
+/* sc=4c */ F(48), '5', '5', '5', SET8|'5', SET8|'5', SET8|'5', SET8|'5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', '6', '6', SET8|'6', SET8|'6', SET8|'6', SET8|'6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', SET8|'+', SET8|'+', SET8|'+', SET8|'+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', SET8|'1', SET8|'1', SET8|'1', SET8|'1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', SET8|'2', SET8|'2', SET8|'2', SET8|'2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', SET8|'3', SET8|'3', SET8|'3', SET8|'3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', SET8|'0', SET8|'0', SET8|'0', SET8|'0', 0x80, 0x02,
+/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0x82, 0x02,
+/* sc=54 */ ALK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', NOP, NOP, SET8|'/', SET8|'/', NOP, NOP, 0x33, 0x00,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0xC2, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=69 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6b */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6c */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6d */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6e */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6f */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=70 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=71 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=72 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=73 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=74 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=75 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=76 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=77 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=78 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=79 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7b */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7c */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7d */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7e */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7f */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* extended (ALTGR LOCK keys) */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, NOP, NOP, SET8|0x1B, SET8|0x1B, DBG, NOP, 0x33, 0x00,
+/* sc=02 */ '!', '1', NOP, NOP, SET8|'1', SET8|'!', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '"', '2', 0x00, 0x00, SET8|'2', SET8|'@', SET8|0x00, SET8|0x00, 0x00, 0x00,
+/* sc=04 */ '\'', '3', NOP, NOP, SET8|'3', SET8|'#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ ';', '4', NOP, NOP, SET8|'4', SET8|'$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ ':', '5', NOP, NOP, SET8|'5', SET8|'%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ ',', '6', 0x1E, 0x1E, SET8|'6', SET8|'^', SET8|0x1E, SET8|0x1E, 0x00, 0x00,
+/* sc=08 */ '.', '7', NOP, NOP, SET8|'7', SET8|'&', NOP, NOP, 0x33, 0x00,
+/* sc=09 */ '*', '8', NOP, NOP, SET8|'8', SET8|'*', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '(', '9', NOP, NOP, SET8|'9', SET8|'(', NOP, NOP, 0x33, 0x00,
+/* sc=0b */ ')', '0', NOP, NOP, SET8|'0', SET8|')', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '-', '_', 0x1F, 0x1F, SET8|'-', SET8|'_', SET8|0x1F, SET8|0x1F, 0x00, 0x00,
+/* sc=0d */ '=', '+', NOP, NOP, SET8|'=', SET8|'+', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, SET8|0x08, SET8|0x08, SET8|0x7F, SET8|0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, F(16), NOP, NOP, SET8|0x09, F(16), NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 0xca, 0xea, 0x11, 0x11, SET8|'q', SET8|'Q', SET8|0x11, SET8|0x11, 0x00, 0x01,
+/* sc=11 */ 0xc3, 0xe3, 0x17, 0x17, SET8|'w', SET8|'W', SET8|0x17, SET8|0x17, 0x00, 0x01,
+/* sc=12 */ 0xd5, 0xf5, 0x05, 0x05, SET8|'e', SET8|'E', SET8|0x05, SET8|0x05, 0x00, 0x01,
+/* sc=13 */ 0xcb, 0xeb, 0x12, 0x12, SET8|'r', SET8|'R', SET8|0x12, SET8|0x12, 0x00, 0x01,
+/* sc=14 */ 0xc5, 0xe5, 0x14, 0x14, SET8|'t', SET8|'T', SET8|0x14, SET8|0x14, 0x00, 0x01,
+/* sc=15 */ 0xce, 0xee, 0x19, 0x19, SET8|'y', SET8|'Y', SET8|0x19, SET8|0x19, 0x00, 0x01,
+/* sc=16 */ 0xc7, 0xe7, 0x15, 0x15, SET8|'u', SET8|'U', SET8|0x15, SET8|0x15, 0x00, 0x01,
+/* sc=17 */ 0xdb, 0xfb, 0x09, 0x09, SET8|'i', SET8|'I', SET8|0x09, SET8|0x09, 0x00, 0x01,
+/* sc=18 */ 0xdd, 0xfd, 0x0F, 0x0F, SET8|'o', SET8|'O', SET8|0x0F, SET8|0x0F, 0x00, 0x01,
+/* sc=19 */ 0xda, 0xfa, 0x10, 0x10, SET8|'p', SET8|'P', SET8|0x10, SET8|0x10, 0x00, 0x01,
+/* sc=1a */ 0xc8, 0xe8, 0x1B, 0x1B, SET8|'[', SET8|'{', SET8|0x1B, SET8|0x1B, 0x00, 0x01,
+/* sc=1b */ 0xdf, 0xff, 0x1D, 0x1D, SET8|']', SET8|'}', SET8|0x1D, SET8|0x1D, 0x00, 0x01,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 0xc6, 0xe6, 0x01, 0x01, SET8|'a', SET8|'A', SET8|0x01, SET8|0x01, 0x00, 0x01,
+/* sc=1f */ 0xd9, 0xf9, 0x13, 0x13, SET8|'s', SET8|'S', SET8|0x13, SET8|0x13, 0x00, 0x01,
+/* sc=20 */ 0xd7, 0xf7, 0x04, 0x04, SET8|'d', SET8|'D', SET8|0x04, SET8|0x04, 0x00, 0x01,
+/* sc=21 */ 0xc1, 0xe1, 0x06, 0x06, SET8|'f', SET8|'F', SET8|0x06, SET8|0x06, 0x00, 0x01,
+/* sc=22 */ 0xd0, 0xf0, 0x07, 0x07, SET8|'g', SET8|'G', SET8|0x07, SET8|0x07, 0x00, 0x01,
+/* sc=23 */ 0xd2, 0xf2, 0x08, 0x08, SET8|'h', SET8|'H', SET8|0x08, SET8|0x08, 0x00, 0x01,
+/* sc=24 */ 0xcf, 0xef, 0x0A, 0x0A, SET8|'j', SET8|'J', SET8|0x0A, SET8|0x0A, 0x00, 0x01,
+/* sc=25 */ 0xcc, 0xec, 0x0B, 0x0B, SET8|'k', SET8|'K', SET8|0x0B, SET8|0x0B, 0x00, 0x01,
+/* sc=26 */ 0xc4, 0xe4, 0x0C, 0x0C, SET8|'l', SET8|'L', SET8|0x0C, SET8|0x0C, 0x00, 0x01,
+/* sc=27 */ 0xd6, 0xf6, NOP, NOP, SET8|';', SET8|':', NOP, NOP, 0x33, 0x01,
+/* sc=28 */ 0xdc, 0xfc, NOP, NOP, SET8|'\'', SET8|'"', NOP, NOP, 0x33, 0x01,
+/* sc=29 */ 0xa3, 0xb3, NOP, NOP, SET8|'`', SET8|'~', NOP, NOP, 0x33, 0x01,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\\', '|', 0x1C, 0x1C, SET8|'\\', SET8|'|', SET8|0x1C, SET8|0x1C, 0x00, 0x00,
+/* sc=2c */ 0xd1, 0xf1, 0x1A, 0x1A, SET8|'z', SET8|'Z', SET8|0x1A, SET8|0x1A, 0x00, 0x01,
+/* sc=2d */ 0xde, 0xfe, 0x18, 0x18, SET8|'x', SET8|'X', SET8|0x18, SET8|0x18, 0x00, 0x01,
+/* sc=2e */ 0xd3, 0xf3, 0x03, 0x03, SET8|'c', SET8|'C', SET8|0x03, SET8|0x03, 0x00, 0x01,
+/* sc=2f */ 0xcd, 0xed, 0x16, 0x16, SET8|'v', SET8|'V', SET8|0x16, SET8|0x16, 0x00, 0x01,
+/* sc=30 */ 0xc9, 0xe9, 0x02, 0x02, SET8|'b', SET8|'B', SET8|0x02, SET8|0x02, 0x00, 0x01,
+/* sc=31 */ 0xd4, 0xf4, 0x0E, 0x0E, SET8|'n', SET8|'N', SET8|0x0E, SET8|0x0E, 0x00, 0x01,
+/* sc=32 */ 0xd8, 0xf8, 0x0D, 0x0D, SET8|'m', SET8|'M', SET8|0x0D, SET8|0x0D, 0x00, 0x01,
+/* sc=33 */ 0xc2, 0xe2, NOP, NOP, SET8|',', SET8|'<', NOP, NOP, 0x33, 0x01,
+/* sc=34 */ 0xc0, 0xe0, NOP, NOP, SET8|'.', SET8|'>', NOP, NOP, 0x33, 0x01,
+/* sc=35 */ '/', '?', NOP, NOP, SET8|'/', SET8|'?', NOP, NOP, 0x33, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, SET8|'*', SET8|'*', SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', SET8|' ', SET8|' ', SET8|' ', SET8|' ', 0x00, 0x00,
+/* sc=3a */ ALK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00,
+/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', SET8|'7', SET8|'7', SET8|'7', SET8|'7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', SET8|'8', SET8|'8', SET8|'8', SET8|'8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', SET8|'9', SET8|'9', SET8|'9', SET8|'9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', '-', '-', SET8|'-', SET8|'-', SET8|'-', SET8|'-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', SET8|'4', SET8|'4', SET8|'4', SET8|'4', 0x80, 0x02,
+/* sc=4c */ F(48), '5', '5', '5', SET8|'5', SET8|'5', SET8|'5', SET8|'5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', '6', '6', SET8|'6', SET8|'6', SET8|'6', SET8|'6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', SET8|'+', SET8|'+', SET8|'+', SET8|'+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', SET8|'1', SET8|'1', SET8|'1', SET8|'1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', SET8|'2', SET8|'2', SET8|'2', SET8|'2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', SET8|'3', SET8|'3', SET8|'3', SET8|'3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', SET8|'0', SET8|'0', SET8|'0', SET8|'0', 0x80, 0x02,
+/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0x82, 0x02,
+/* sc=54 */ ALK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', NOP, NOP, SET8|'/', SET8|'/', NOP, NOP, 0x33, 0x00,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0xC2, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+
+#endif
+
+#if !defined(DKKEYMAP) && !defined(UKKEYMAP) && !defined(GRKEYMAP) && !defined(SWKEYMAP) && !defined(RUKEYMAP)
+keymap_t key_map = { 0x69, /* US iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '@', 0x00, 0x00, '2', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', '#', NOP, NOP, '3', '#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '^', 0x1E, 0x1E, '6', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=08 */ '7', '&', NOP, NOP, '7', '&', NOP, NOP, 0x33, 0x00,
+/* sc=09 */ '8', '*', NOP, NOP, '8', '*', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '9', '(', NOP, NOP, '9', '(', NOP, NOP, 0x33, 0x00,
+/* sc=0b */ '0', ')', NOP, NOP, '0', ')', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=0d */ '=', '+', NOP, NOP, '=', '+', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x33, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=1b */ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ ';', ':', NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x00,
+/* sc=28 */ '\'', '"', NOP, NOP, '\'', '"', NOP, NOP, 0x33, 0x00,
+/* sc=29 */ '`', '~', NOP, NOP, '`', '~', NOP, NOP, 0x33, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', '<', NOP, NOP, ',', '<', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', '>', NOP, NOP, '.', '>', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '/', '?', NOP, NOP, '/', '?', NOP, NOP, 0x33, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00,
+/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', '-', '-', '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ NOP, '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02,
+/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x00, 0x00,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x00,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+
+#endif
+
+fkeytab_t fkey_tab[60] = {
+/* 00-03 */ {"\033[M", 3}, {"\033[N", 3}, {"\033[O", 3}, {"\033[P", 3},
+/* 04-07 */ {"\033[Q", 3}, {"\033[R", 3}, {"\033[S", 3}, {"\033[T", 3},
+/* 08-0B */ {"\033[U", 3}, {"\033[V", 3}, {"\033[W", 3}, {"\033[X", 3},
+/* 0C-0F */ {"\033[W", 3}, {"\033[X", 3}, {"\033[Y", 3}, {"\033[Z", 3},
+/* 10-13 */ {"\033[a", 3}, {"\033[b", 3}, {"\033[c", 3}, {"\033[d", 3},
+/* 14-17 */ {"\033[e", 3}, {"\033[f", 3}, {"\033[g", 3}, {"\033[h", 3},
+/* 18-1B */ {"\033[g", 3}, {"\033[h", 3}, {"\033[i", 3}, {"\033[j", 3},
+/* 1C-1F */ {"\033[k", 3}, {"\033[l", 3}, {"\033[m", 3}, {"\033[n", 3},
+/* 20-23 */ {"\033[o", 3}, {"\033[p", 3}, {"\033[q", 3}, {"\033[r", 3},
+/* 24-27 */ {"\033[g", 3}, {"\033[h", 3}, {"\033[i", 3}, {"\033[j", 3},
+/* 28-2B */ {"\033[k", 3}, {"\033[l", 3}, {"\033[m", 3}, {"\033[n", 3},
+/* 2C-2F */ {"\033[o", 3}, {"\033[p", 3}, {"\033[q", 3}, {"\033[r", 3},
+/* 30-33 */ {"\033[H", 3}, {"\033[A", 3}, {"\033[I", 3}, {"-" , 1},
+/* 34-37 */ {"\033[D", 3}, {"\177" , 1}, {"\033[C", 3}, {"+" , 1},
+/* 38-3B */ {"\033[F", 3}, {"\033[B", 3}, {"\033[G", 3}, {"\033[L", 3}
+};
diff --git a/sys/dev/mcd/mcd.c b/sys/dev/mcd/mcd.c
new file mode 100644
index 0000000..683b0e1
--- /dev/null
+++ b/sys/dev/mcd/mcd.c
@@ -0,0 +1,1335 @@
+/*
+ * Copyright 1993 by Holger Veit (data part)
+ * Copyright 1993 by Brian Moore (audio part)
+ * Changes Copyright 1993 by Gary Clark II
+ *
+ * Rewrote probe routine to work on newer Mitsumi drives.
+ * Additional changes (C) 1994 by Jordan K. Hubbard
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This software was developed by Holger Veit and Brian Moore
+ * for use with "386BSD" and similar operating systems.
+ * "Similar operating systems" includes mainly non-profit oriented
+ * systems for research and education, including but not restricted to
+ * "NetBSD", "FreeBSD", "Mach" (by CMU).
+ * 4. Neither the name of the developer(s) nor the name "386BSD"
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER(S) BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: mcd.c,v 1.15 1994/04/20 07:06:41 davidg Exp $
+ */
+static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";
+
+#include "mcd.h"
+#if NMCD > 0
+#include "types.h"
+#include "param.h"
+#include "systm.h"
+#include "conf.h"
+#include "file.h"
+#include "buf.h"
+#include "stat.h"
+#include "uio.h"
+#include "ioctl.h"
+#include "cdio.h"
+#include "errno.h"
+#include "dkbad.h"
+#include "disklabel.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "mcdreg.h"
+
+/* user definable options */
+/*#define MCD_TO_WARNING_ON*/ /* define to get timeout messages */
+/*#define MCDMINI*/ /* define for a mini configuration for boot kernel */
+
+
+#ifdef MCDMINI
+#define MCD_TRACE(fmt,a,b,c,d)
+#ifdef MCD_TO_WARNING_ON
+#undef MCD_TO_WARNING_ON
+#endif
+#else
+#define MCD_TRACE(fmt,a,b,c,d) {if (mcd_data[unit].debug) {printf("mcd%d st=%02x: ",unit,mcd_data[unit].status); printf(fmt,a,b,c,d);}}
+#endif
+
+#define mcd_part(dev) ((minor(dev)) & 7)
+#define mcd_unit(dev) (((minor(dev)) & 0x38) >> 3)
+#define mcd_phys(dev) (((minor(dev)) & 0x40) >> 6)
+#define RAW_PART 0
+
+/* flags */
+#define MCDOPEN 0x0001 /* device opened */
+#define MCDVALID 0x0002 /* parameters loaded */
+#define MCDINIT 0x0004 /* device is init'd */
+#define MCDWAIT 0x0008 /* waiting for something */
+#define MCDLABEL 0x0010 /* label is read */
+#define MCDPROBING 0x0020 /* probing */
+#define MCDREADRAW 0x0040 /* read raw mode (2352 bytes) */
+#define MCDVOLINFO 0x0080 /* already read volinfo */
+#define MCDTOC 0x0100 /* already read toc */
+#define MCDMBXBSY 0x0200 /* local mbx is busy */
+
+/* status */
+#define MCDAUDIOBSY MCD_ST_AUDIOBSY /* playing audio */
+#define MCDDSKCHNG MCD_ST_DSKCHNG /* sensed change of disk */
+#define MCDDSKIN MCD_ST_DSKIN /* sensed disk in drive */
+#define MCDDOOROPEN MCD_ST_DOOROPEN /* sensed door open */
+
+/* These are apparently the different states a mitsumi can get up to */
+#define MCDCDABSENT 0x0030
+#define MCDCDPRESENT 0x0020
+#define MCDSCLOSED 0x0080
+#define MCDSOPEN 0x00a0
+
+/* toc */
+#define MCD_MAXTOCS 104 /* from the Linux driver */
+#define MCD_LASTPLUS1 170 /* special toc entry */
+
+struct mcd_mbx {
+ short unit;
+ short port;
+ short retry;
+ short nblk;
+ int sz;
+ u_long skip;
+ struct buf *bp;
+ int p_offset;
+ short count;
+};
+
+struct mcd_data {
+ short config;
+ short flags;
+ short status;
+ int blksize;
+ u_long disksize;
+ int iobase;
+ struct disklabel dlabel;
+ int partflags[MAXPARTITIONS];
+ int openflags;
+ struct mcd_volinfo volinfo;
+#ifndef MCDMINI
+ struct mcd_qchninfo toc[MCD_MAXTOCS];
+ short audio_status;
+ struct mcd_read2 lastpb;
+#endif
+ short debug;
+ struct buf head; /* head of buf queue */
+ struct mcd_mbx mbx;
+} mcd_data[NMCD];
+
+/* reader state machine */
+#define MCD_S_BEGIN 0
+#define MCD_S_BEGIN1 1
+#define MCD_S_WAITSTAT 2
+#define MCD_S_WAITMODE 3
+#define MCD_S_WAITREAD 4
+
+/* prototypes */
+int mcdopen(dev_t dev);
+int mcdclose(dev_t dev);
+void mcdstrategy(struct buf *bp);
+int mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags);
+int mcdsize(dev_t dev);
+static void mcd_done(struct mcd_mbx *mbx);
+static void mcd_start(int unit);
+static int mcd_getdisklabel(int unit);
+static void mcd_configure(struct mcd_data *cd);
+static int mcd_get(int unit, char *buf, int nmax);
+static void mcd_setflags(int unit,struct mcd_data *cd);
+static int mcd_getstat(int unit,int sflg);
+static int mcd_send(int unit, int cmd,int nretrys);
+static int bcd2bin(bcd_t b);
+static bcd_t bin2bcd(int b);
+static void hsg2msf(int hsg, bcd_t *msf);
+static int msf2hsg(bcd_t *msf);
+static int mcd_volinfo(int unit);
+static int mcd_waitrdy(int port,int dly);
+static void mcd_doread(int state, struct mcd_mbx *mbxin);
+#ifndef MCDMINI
+static int mcd_setmode(int unit, int mode);
+static int mcd_getqchan(int unit, struct mcd_qchninfo *q);
+static int mcd_subchan(int unit, struct ioc_read_subchannel *sc);
+static int mcd_toc_header(int unit, struct ioc_toc_header *th);
+static int mcd_read_toc(int unit);
+static int mcd_toc_entry(int unit, struct ioc_read_toc_entry *te);
+static int mcd_stop(int unit);
+static int mcd_playtracks(int unit, struct ioc_play_track *pt);
+static int mcd_play(int unit, struct mcd_read2 *pb);
+static int mcd_pause(int unit);
+static int mcd_resume(int unit);
+#endif
+
+extern int hz;
+extern int mcd_probe(struct isa_device *dev);
+extern int mcd_attach(struct isa_device *dev);
+struct isa_driver mcddriver = { mcd_probe, mcd_attach, "mcd" };
+
+#define mcd_put(port,byte) outb(port,byte)
+
+#define MCD_RETRYS 5
+#define MCD_RDRETRYS 8
+
+#define MCDBLK 2048 /* for cooked mode */
+#define MCDRBLK 2352 /* for raw mode */
+
+/* several delays */
+#define RDELAY_WAITSTAT 300
+#define RDELAY_WAITMODE 300
+#define RDELAY_WAITREAD 800
+
+#define DELAY_STATUS 10000l /* 10000 * 1us */
+#define DELAY_GETREPLY 200000l /* 200000 * 2us */
+#define DELAY_SEEKREAD 20000l /* 20000 * 1us */
+#define mcd_delay DELAY
+
+int mcd_attach(struct isa_device *dev)
+{
+ struct mcd_data *cd = mcd_data + dev->id_unit;
+ int i;
+
+ cd->iobase = dev->id_iobase;
+ cd->flags |= MCDINIT;
+ cd->openflags = 0;
+ for (i=0; i<MAXPARTITIONS; i++) cd->partflags[i] = 0;
+
+#ifdef NOTYET
+ /* wire controller for interrupts and dma */
+ mcd_configure(cd);
+#endif
+
+ return 1;
+}
+
+int mcdopen(dev_t dev)
+{
+ int unit,part,phys;
+ struct mcd_data *cd;
+
+ unit = mcd_unit(dev);
+ if (unit >= NMCD)
+ return ENXIO;
+
+ cd = mcd_data + unit;
+ part = mcd_part(dev);
+ phys = mcd_phys(dev);
+
+ /* not initialized*/
+ if (!(cd->flags & MCDINIT))
+ return ENXIO;
+
+ /* invalidated in the meantime? mark all open part's invalid */
+ if (!(cd->flags & MCDVALID) && cd->openflags)
+ return ENXIO;
+
+ if (mcd_getstat(unit,1) < 0)
+ return ENXIO;
+
+ /* XXX get a default disklabel */
+ mcd_getdisklabel(unit);
+
+ if (mcdsize(dev) < 0) {
+ printf("mcd%d: failed to get disk size\n",unit);
+ return ENXIO;
+ } else
+ cd->flags |= MCDVALID;
+
+MCD_TRACE("open: partition=%d, disksize = %d, blksize=%d\n",
+ part,cd->disksize,cd->blksize,0);
+
+ if (part == RAW_PART ||
+ (part < cd->dlabel.d_npartitions &&
+ cd->dlabel.d_partitions[part].p_fstype != FS_UNUSED)) {
+ cd->partflags[part] |= MCDOPEN;
+ cd->openflags |= (1<<part);
+ if (part == RAW_PART && phys != 0)
+ cd->partflags[part] |= MCDREADRAW;
+ return 0;
+ }
+
+ return ENXIO;
+}
+
+int mcdclose(dev_t dev)
+{
+ int unit,part,phys;
+ struct mcd_data *cd;
+
+ unit = mcd_unit(dev);
+ if (unit >= NMCD)
+ return ENXIO;
+
+ cd = mcd_data + unit;
+ part = mcd_part(dev);
+ phys = mcd_phys(dev);
+
+ if (!(cd->flags & MCDINIT))
+ return ENXIO;
+
+ mcd_getstat(unit,1); /* get status */
+
+ /* close channel */
+ cd->partflags[part] &= ~(MCDOPEN|MCDREADRAW);
+ cd->openflags &= ~(1<<part);
+ MCD_TRACE("close: partition=%d\n",part,0,0,0);
+
+ return 0;
+}
+
+void
+mcdstrategy(struct buf *bp)
+{
+ struct mcd_data *cd;
+ struct buf *qp;
+ int s;
+
+ int unit = mcd_unit(bp->b_dev);
+
+ cd = mcd_data + unit;
+
+ /* test validity */
+/*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n",
+ bp,unit,bp->b_blkno,bp->b_bcount);*/
+ if (unit >= NMCD || bp->b_blkno < 0) {
+ printf("mcdstrategy: unit = %d, blkno = %d, bcount = %d\n",
+ unit, bp->b_blkno, bp->b_bcount);
+ pg("mcd: mcdstratregy failure");
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ goto bad;
+ }
+
+ /* if device invalidated (e.g. media change, door open), error */
+ if (!(cd->flags & MCDVALID)) {
+MCD_TRACE("strategy: drive not valid\n",0,0,0,0);
+ bp->b_error = EIO;
+ goto bad;
+ }
+
+ /* read only */
+ if (!(bp->b_flags & B_READ)) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+
+ /* no data to read */
+ if (bp->b_bcount == 0)
+ goto done;
+
+ /* for non raw access, check partition limits */
+ if (mcd_part(bp->b_dev) != RAW_PART) {
+ if (!(cd->flags & MCDLABEL)) {
+ bp->b_error = EIO;
+ goto bad;
+ }
+ /* adjust transfer if necessary */
+ if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) {
+ goto done;
+ }
+ } else {
+ bp->b_pblkno = bp->b_blkno;
+ bp->b_resid = 0;
+ }
+
+ /* queue it */
+ qp = &cd->head;
+ s = splbio();
+ disksort(qp,bp);
+ splx(s);
+
+ /* now check whether we can perform processing */
+ mcd_start(unit);
+ return;
+
+bad:
+ bp->b_flags |= B_ERROR;
+done:
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+ return;
+}
+
+static void mcd_start(int unit)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ struct buf *bp, *qp = &cd->head;
+ struct partition *p;
+ int part;
+ register s = splbio();
+
+ if (cd->flags & MCDMBXBSY)
+ return;
+
+ if ((bp = qp->b_actf) != 0) {
+ /* block found to process, dequeue */
+ /*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/
+ qp->b_actf = bp->b_actf;
+ splx(s);
+ } else {
+ /* nothing to do */
+ splx(s);
+ return;
+ }
+
+ /* changed media? */
+ if (!(cd->flags & MCDVALID)) {
+ MCD_TRACE("mcd_start: drive not valid\n",0,0,0,0);
+ return;
+ }
+
+ p = cd->dlabel.d_partitions + mcd_part(bp->b_dev);
+
+ cd->flags |= MCDMBXBSY;
+ cd->mbx.unit = unit;
+ cd->mbx.port = cd->iobase;
+ cd->mbx.retry = MCD_RETRYS;
+ cd->mbx.bp = bp;
+ cd->mbx.p_offset = p->p_offset;
+
+ /* calling the read routine */
+ mcd_doread(MCD_S_BEGIN,&(cd->mbx));
+ /* triggers mcd_start, when successful finished */
+ return;
+}
+
+int mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags)
+{
+ struct mcd_data *cd;
+ int unit,part;
+
+ unit = mcd_unit(dev);
+ part = mcd_part(dev);
+ cd = mcd_data + unit;
+
+#ifdef MCDMINI
+ return ENOTTY;
+#else
+ if (!(cd->flags & MCDVALID))
+ return EIO;
+MCD_TRACE("ioctl called 0x%x\n",cmd,0,0,0);
+
+ switch (cmd) {
+ case DIOCSBAD:
+ return EINVAL;
+ case DIOCGDINFO:
+ case DIOCGPART:
+ case DIOCWDINFO:
+ case DIOCSDINFO:
+ case DIOCWLABEL:
+ return ENOTTY;
+ case CDIOCPLAYTRACKS:
+ return mcd_playtracks(unit, (struct ioc_play_track *) addr);
+ case CDIOCPLAYBLOCKS:
+ return mcd_play(unit, (struct mcd_read2 *) addr);
+ case CDIOCREADSUBCHANNEL:
+ return mcd_subchan(unit, (struct ioc_read_subchannel *) addr);
+ case CDIOREADTOCHEADER:
+ return mcd_toc_header(unit, (struct ioc_toc_header *) addr);
+ case CDIOREADTOCENTRYS:
+ return mcd_toc_entry(unit, (struct ioc_read_toc_entry *) addr);
+ case CDIOCSETPATCH:
+ case CDIOCGETVOL:
+ case CDIOCSETVOL:
+ case CDIOCSETMONO:
+ case CDIOCSETSTERIO:
+ case CDIOCSETMUTE:
+ case CDIOCSETLEFT:
+ case CDIOCSETRIGHT:
+ return EINVAL;
+ case CDIOCRESUME:
+ return mcd_resume(unit);
+ case CDIOCPAUSE:
+ return mcd_pause(unit);
+ case CDIOCSTART:
+ return EINVAL;
+ case CDIOCSTOP:
+ return mcd_stop(unit);
+ case CDIOCEJECT:
+ return EINVAL;
+ case CDIOCSETDEBUG:
+ cd->debug = 1;
+ return 0;
+ case CDIOCCLRDEBUG:
+ cd->debug = 0;
+ return 0;
+ case CDIOCRESET:
+ return EINVAL;
+ default:
+ return ENOTTY;
+ }
+ /*NOTREACHED*/
+#endif /*!MCDMINI*/
+}
+
+/* this could have been taken from scsi/cd.c, but it is not clear
+ * whether the scsi cd driver is linked in
+ */
+static int mcd_getdisklabel(int unit)
+{
+ struct mcd_data *cd = mcd_data + unit;
+
+ if (cd->flags & MCDLABEL)
+ return -1;
+
+ bzero(&cd->dlabel,sizeof(struct disklabel));
+ strncpy(cd->dlabel.d_typename,"Mitsumi CD ROM ",16);
+ strncpy(cd->dlabel.d_packname,"unknown ",16);
+ cd->dlabel.d_secsize = cd->blksize;
+ cd->dlabel.d_nsectors = 100;
+ cd->dlabel.d_ntracks = 1;
+ cd->dlabel.d_ncylinders = (cd->disksize/100)+1;
+ cd->dlabel.d_secpercyl = 100;
+ cd->dlabel.d_secperunit = cd->disksize;
+ cd->dlabel.d_rpm = 300;
+ cd->dlabel.d_interleave = 1;
+ cd->dlabel.d_flags = D_REMOVABLE;
+ cd->dlabel.d_npartitions= 1;
+ cd->dlabel.d_partitions[0].p_offset = 0;
+ cd->dlabel.d_partitions[0].p_size = cd->disksize;
+ cd->dlabel.d_partitions[0].p_fstype = 9;
+ cd->dlabel.d_magic = DISKMAGIC;
+ cd->dlabel.d_magic2 = DISKMAGIC;
+ cd->dlabel.d_checksum = dkcksum(&cd->dlabel);
+
+ cd->flags |= MCDLABEL;
+ return 0;
+}
+
+int mcdsize(dev_t dev)
+{
+ int size;
+ int unit = mcd_unit(dev);
+ struct mcd_data *cd = mcd_data + unit;
+
+ if (mcd_volinfo(unit) >= 0) {
+ cd->blksize = MCDBLK;
+ size = msf2hsg(cd->volinfo.vol_msf);
+ cd->disksize = size * (MCDBLK/DEV_BSIZE);
+ return 0;
+ }
+ return -1;
+}
+
+/***************************************************************
+ * lower level of driver starts here
+ **************************************************************/
+
+#ifdef NOTDEF
+static char
+irqs[] = {
+ 0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00,
+ 0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00
+};
+
+static char
+drqs[] = {
+ 0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07,
+};
+#endif
+
+static void
+mcd_configure(struct mcd_data *cd)
+{
+ outb(cd->iobase+mcd_config,cd->config);
+}
+
+/* Wait for non-busy - return 0 on timeout */
+static int
+twiddle_thumbs(int port, int unit, int count, char *whine)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (!(inb(port+MCD_FLAGS) & MCD_ST_BUSY)) {
+ return 1;
+ }
+ }
+#ifdef MCD_TO_WARNING_ON
+ printf("mcd%d: timeout %s\n", unit, whine);
+#endif
+ return 0;
+}
+
+/* check to see if a Mitsumi CD-ROM is attached to the ISA bus */
+
+int
+mcd_probe(struct isa_device *dev)
+{
+ int port = dev->id_iobase;
+ int unit = dev->id_unit;
+ int i, j;
+ int status;
+ unsigned char stbytes[3];
+
+ mcd_data[unit].flags = MCDPROBING;
+
+#ifdef NOTDEF
+ /* get irq/drq configuration word */
+ mcd_data[unit].config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/
+#else
+ mcd_data[unit].config = 0;
+#endif
+
+ /* send a reset */
+ outb(port+MCD_FLAGS, M_RESET);
+
+ /*
+ * delay awhile by getting any pending garbage (old data) and
+ * throwing it away.
+ */
+ for (i = 1000000; i != 0; i--) {
+ inb(port+MCD_FLAGS);
+ }
+
+ /* Get status */
+ outb(port+MCD_DATA, MCD_CMDGETSTAT);
+ if (!twiddle_thumbs(port, unit, 1000000, "getting status")) {
+ return 0; /* Timeout */
+ }
+ status = inb(port+MCD_DATA);
+ if (status != MCDCDABSENT && status != MCDCDPRESENT &&
+ status != MCDSOPEN && status != MCDSCLOSED)
+ return 0; /* Not actually a Mitsumi drive here */
+ /* Get version information */
+ outb(port+MCD_DATA, MCD_CMDCONTINFO);
+ for (j = 0; j < 3; j++) {
+ if (!twiddle_thumbs(port, unit, 3000, "getting version info")) {
+ return 0;
+ }
+ stbytes[j] = (inb(port+MCD_DATA) & 0xFF);
+ }
+ printf("mcd%d: version information is %x %c %x\n", unit,
+ stbytes[0], stbytes[1], stbytes[2]);
+ if (stbytes[1] >= 4) {
+ outb(port+MCD_CTRL, M_PICKLE);
+ printf("mcd%d: Adjusted for newer drive model\n", unit);
+ }
+ return 4;
+}
+
+
+static int
+mcd_waitrdy(int port,int dly)
+{
+ int i;
+
+ /* wait until xfer port senses data ready */
+ for (i=0; i<dly; i++) {
+ if ((inb(port+mcd_xfer) & MCD_ST_BUSY)==0)
+ return 0;
+ mcd_delay(1);
+ }
+ return -1;
+}
+
+static int
+mcd_getreply(int unit,int dly)
+{
+ int i;
+ struct mcd_data *cd = mcd_data + unit;
+ int port = cd->iobase;
+
+ /* wait data to become ready */
+ if (mcd_waitrdy(port,dly)<0) {
+#ifdef MCD_TO_WARNING_ON
+ printf("mcd%d: timeout getreply\n",unit);
+#endif
+ return -1;
+ }
+
+ /* get the data */
+ return inb(port+mcd_status) & 0xFF;
+}
+
+static int
+mcd_getstat(int unit,int sflg)
+{
+ int i;
+ struct mcd_data *cd = mcd_data + unit;
+ int port = cd->iobase;
+
+ /* get the status */
+ if (sflg)
+ outb(port+mcd_command, MCD_CMDGETSTAT);
+ i = mcd_getreply(unit,DELAY_GETREPLY);
+ if (i<0) return -1;
+
+ cd->status = i;
+
+ mcd_setflags(unit,cd);
+ return cd->status;
+}
+
+static void
+mcd_setflags(int unit, struct mcd_data *cd)
+{
+ /* check flags */
+ if (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) {
+ MCD_TRACE("getstat: sensed DSKCHNG or DOOROPEN\n",0,0,0,0);
+ cd->flags &= ~MCDVALID;
+ }
+
+#ifndef MCDMINI
+ if (cd->status & MCDAUDIOBSY)
+ cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
+ else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS)
+ cd->audio_status = CD_AS_PLAY_COMPLETED;
+#endif
+}
+
+static int
+mcd_get(int unit, char *buf, int nmax)
+{
+ int port = mcd_data[unit].iobase;
+ int i,k;
+
+ for (i=0; i<nmax; i++) {
+ /* wait for data */
+ if ((k = mcd_getreply(unit,DELAY_GETREPLY)) < 0) {
+#ifdef MCD_TO_WARNING_ON
+ printf("mcd%d: timeout mcd_get\n",unit);
+#endif
+ return -1;
+ }
+ buf[i] = k;
+ }
+ return i;
+}
+
+static int
+mcd_send(int unit, int cmd,int nretrys)
+{
+ int i,k;
+ int port = mcd_data[unit].iobase;
+
+/*MCD_TRACE("mcd_send: command = 0x%x\n",cmd,0,0,0);*/
+ for (i=0; i<nretrys; i++) {
+ outb(port+mcd_command, cmd);
+ if ((k=mcd_getstat(unit,0)) != -1) {
+ break;
+ }
+ }
+ if (i == nretrys) {
+ printf("mcd%d: mcd_send retry cnt exceeded\n",unit);
+ return -1;
+ }
+/*MCD_TRACE("mcd_send: status = 0x%x\n",k,0,0,0);*/
+ return 0;
+}
+
+static int
+bcd2bin(bcd_t b)
+{
+ return (b >> 4) * 10 + (b & 15);
+}
+
+static bcd_t
+bin2bcd(int b)
+{
+ return ((b / 10) << 4) | (b % 10);
+}
+
+static void
+hsg2msf(int hsg, bcd_t *msf)
+{
+ hsg += 150;
+ M_msf(msf) = bin2bcd(hsg / 4500);
+ hsg %= 4500;
+ S_msf(msf) = bin2bcd(hsg / 75);
+ F_msf(msf) = bin2bcd(hsg % 75);
+}
+
+static int
+msf2hsg(bcd_t *msf)
+{
+ return (bcd2bin(M_msf(msf)) * 60 +
+ bcd2bin(S_msf(msf))) * 75 +
+ bcd2bin(F_msf(msf)) - 150;
+}
+
+static int
+mcd_volinfo(int unit)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ int i;
+
+/*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/
+
+ /* Get the status, in case the disc has been changed */
+ if (mcd_getstat(unit, 1) < 0) return EIO;
+
+ /* Just return if we already have it */
+ if (cd->flags & MCDVOLINFO) return 0;
+
+ /* send volume info command */
+ if (mcd_send(unit,MCD_CMDGETVOLINFO,MCD_RETRYS) < 0)
+ return -1;
+
+ /* get data */
+ if (mcd_get(unit,(char*) &cd->volinfo,sizeof(struct mcd_volinfo)) < 0) {
+ printf("mcd%d: mcd_volinfo: error read data\n",unit);
+ return -1;
+ }
+
+ if (cd->volinfo.trk_low != 0 || cd->volinfo.trk_high != 0) {
+ cd->flags |= MCDVOLINFO; /* volinfo is OK */
+ return 0;
+ }
+
+ return -1;
+}
+
+void
+mcdintr(unit)
+ int unit;
+{
+ int port = mcd_data[unit].iobase;
+ u_int i;
+
+ MCD_TRACE("stray interrupt xfer=0x%x\n",inb(port+mcd_xfer),0,0,0);
+
+ /* just read out status and ignore the rest */
+ if ((inb(port+mcd_xfer)&0xFF) != 0xFF) {
+ i = inb(port+mcd_status);
+ }
+}
+
+/* state machine to process read requests
+ * initialize with MCD_S_BEGIN: calculate sizes, and read status
+ * MCD_S_WAITSTAT: wait for status reply, set mode
+ * MCD_S_WAITMODE: waits for status reply from set mode, set read command
+ * MCD_S_WAITREAD: wait for read ready, read data
+ */
+static struct mcd_mbx *mbxsave;
+
+static void
+mcd_doread(int state, struct mcd_mbx *mbxin)
+{
+ struct mcd_mbx *mbx = (state!=MCD_S_BEGIN) ? mbxsave : mbxin;
+ int unit = mbx->unit;
+ int port = mbx->port;
+ struct buf *bp = mbx->bp;
+ struct mcd_data *cd = mcd_data + unit;
+
+ int rm,i,k;
+ struct mcd_read2 rbuf;
+ int blknum;
+ caddr_t addr;
+
+loop:
+ switch (state) {
+ case MCD_S_BEGIN:
+ mbx = mbxsave = mbxin;
+
+ case MCD_S_BEGIN1:
+ /* get status */
+ outb(port+mcd_command, MCD_CMDGETSTAT);
+ mbx->count = RDELAY_WAITSTAT;
+ timeout((timeout_func_t)mcd_doread,
+ (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
+ return;
+ case MCD_S_WAITSTAT:
+ untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITSTAT);
+ if (mbx->count-- >= 0) {
+ if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
+ timeout((timeout_func_t)mcd_doread,
+ (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
+ return;
+ }
+ mcd_setflags(unit,cd);
+ MCD_TRACE("got WAITSTAT delay=%d\n",
+ RDELAY_WAITSTAT-mbx->count,0,0,0);
+ /* reject, if audio active */
+ if (cd->status & MCDAUDIOBSY) {
+ printf("mcd%d: audio is active\n",unit);
+ goto readerr;
+ }
+
+ /* to check for raw/cooked mode */
+ if (cd->flags & MCDREADRAW) {
+ rm = MCD_MD_RAW;
+ mbx->sz = MCDRBLK;
+ } else {
+ rm = MCD_MD_COOKED;
+ mbx->sz = cd->blksize;
+ }
+
+ mbx->count = RDELAY_WAITMODE;
+
+ mcd_put(port+mcd_command, MCD_CMDSETMODE);
+ mcd_put(port+mcd_command, rm);
+ timeout((timeout_func_t)mcd_doread,
+ (caddr_t)MCD_S_WAITMODE,hz/100); /* XXX */
+ return;
+ } else {
+#ifdef MCD_TO_WARNING_ON
+ printf("mcd%d: timeout getstatus\n",unit);
+#endif
+ goto readerr;
+ }
+
+ case MCD_S_WAITMODE:
+ untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE);
+ if (mbx->count-- < 0) {
+#ifdef MCD_TO_WARNING_ON
+ printf("mcd%d: timeout set mode\n",unit);
+#endif
+ goto readerr;
+ }
+ if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
+ timeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE,hz/100);
+ return;
+ }
+ mcd_setflags(unit,cd);
+ MCD_TRACE("got WAITMODE delay=%d\n",
+ RDELAY_WAITMODE-mbx->count,0,0,0);
+ /* for first block */
+ mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz;
+ mbx->skip = 0;
+
+nextblock:
+ blknum = (bp->b_blkno / (mbx->sz/DEV_BSIZE))
+ + mbx->p_offset + mbx->skip/mbx->sz;
+
+ MCD_TRACE("mcd_doread: read blknum=%d for bp=0x%x\n",
+ blknum,bp,0,0);
+
+ /* build parameter block */
+ hsg2msf(blknum,rbuf.start_msf);
+
+ /* send the read command */
+ mcd_put(port+mcd_command,MCD_CMDREAD2);
+ mcd_put(port+mcd_command,rbuf.start_msf[0]);
+ mcd_put(port+mcd_command,rbuf.start_msf[1]);
+ mcd_put(port+mcd_command,rbuf.start_msf[2]);
+ mcd_put(port+mcd_command,0);
+ mcd_put(port+mcd_command,0);
+ mcd_put(port+mcd_command,1);
+ mbx->count = RDELAY_WAITREAD;
+ timeout((timeout_func_t)mcd_doread,
+ (caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
+ return;
+ case MCD_S_WAITREAD:
+ untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITREAD);
+ if (mbx->count-- > 0) {
+ k = inb(port+mcd_xfer);
+ if ((k & 2)==0) {
+ MCD_TRACE("got data delay=%d\n",
+ RDELAY_WAITREAD-mbx->count,0,0,0);
+ /* data is ready */
+ addr = bp->b_un.b_addr + mbx->skip;
+ outb(port+mcd_ctl2,0x04); /* XXX */
+ for (i=0; i<mbx->sz; i++)
+ *addr++ = inb(port+mcd_rdata);
+ outb(port+mcd_ctl2,0x0c); /* XXX */
+
+ if (--mbx->nblk > 0) {
+ mbx->skip += mbx->sz;
+ goto nextblock;
+ }
+
+ /* return buffer */
+ bp->b_resid = 0;
+ biodone(bp);
+
+ cd->flags &= ~MCDMBXBSY;
+ mcd_start(mbx->unit);
+ return;
+ }
+ if ((k & 4)==0)
+ mcd_getstat(unit,0);
+ timeout((timeout_func_t)mcd_doread,
+ (caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
+ return;
+ } else {
+#ifdef MCD_TO_WARNING_ON
+ printf("mcd%d: timeout read data\n",unit);
+#endif
+ goto readerr;
+ }
+ }
+
+readerr:
+ if (mbx->retry-- > 0) {
+#ifdef MCD_TO_WARNING_ON
+ printf("mcd%d: retrying\n",unit);
+#endif
+ state = MCD_S_BEGIN1;
+ goto loop;
+ }
+
+ /* invalidate the buffer */
+ bp->b_flags |= B_ERROR;
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+ mcd_start(mbx->unit);
+ return;
+
+#ifdef NOTDEF
+ printf("mcd%d: unit timeout, resetting\n",mbx->unit);
+ outb(mbx->port+mcd_reset,MCD_CMDRESET);
+ DELAY(300000);
+ (void)mcd_getstat(mbx->unit,1);
+ (void)mcd_getstat(mbx->unit,1);
+ /*cd->status &= ~MCDDSKCHNG; */
+ cd->debug = 1; /* preventive set debug mode */
+
+#endif
+
+}
+
+#ifndef MCDMINI
+static int
+mcd_setmode(int unit, int mode)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ int port = cd->iobase;
+ int retry;
+
+ printf("mcd%d: setting mode to %d\n", unit, mode);
+ for(retry=0; retry<MCD_RETRYS; retry++)
+ {
+ outb(port+mcd_command, MCD_CMDSETMODE);
+ outb(port+mcd_command, mode);
+ if (mcd_getstat(unit, 0) != -1) return 0;
+ }
+
+ return -1;
+}
+
+static int
+mcd_toc_header(int unit, struct ioc_toc_header *th)
+{
+ struct mcd_data *cd = mcd_data + unit;
+
+ if (mcd_volinfo(unit) < 0) {
+ return ENXIO;
+ }
+
+ th->len = msf2hsg(cd->volinfo.vol_msf);
+ th->starting_track = bcd2bin(cd->volinfo.trk_low);
+ th->ending_track = bcd2bin(cd->volinfo.trk_high);
+
+ return 0;
+}
+
+static int
+mcd_read_toc(int unit)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ struct ioc_toc_header th;
+ struct mcd_qchninfo q;
+ int rc, trk, idx, retry;
+
+ /* Only read TOC if needed */
+ if (cd->flags & MCDTOC) {
+ return 0;
+ }
+
+ printf("mcd%d: reading toc header\n", unit);
+ if (mcd_toc_header(unit, &th) != 0) {
+ return ENXIO;
+ }
+
+ printf("mcd%d: stopping play\n", unit);
+ if ((rc=mcd_stop(unit)) != 0) {
+ return rc;
+ }
+
+ /* try setting the mode twice */
+ if (mcd_setmode(unit, MCD_MD_TOC) != 0) {
+ return EIO;
+ }
+ if (mcd_setmode(unit, MCD_MD_TOC) != 0) {
+ return EIO;
+ }
+
+ printf("mcd%d: get_toc reading qchannel info\n",unit);
+ for(trk=th.starting_track; trk<=th.ending_track; trk++)
+ cd->toc[trk].idx_no = 0;
+ trk = th.ending_track - th.starting_track + 1;
+ for(retry=0; retry<300 && trk>0; retry++)
+ {
+ if (mcd_getqchan(unit, &q) < 0) break;
+ idx = bcd2bin(q.idx_no);
+ if (idx>0 && idx < MCD_MAXTOCS && q.trk_no==0) {
+ if (cd->toc[idx].idx_no == 0) {
+ cd->toc[idx] = q;
+ trk--;
+ }
+ }
+ }
+
+ if (mcd_setmode(unit, MCD_MD_COOKED) != 0) {
+ return EIO;
+ }
+
+ if (trk != 0) {
+ return ENXIO;
+ }
+
+ /* add a fake last+1 */
+ idx = th.ending_track + 1;
+ cd->toc[idx].ctrl_adr = cd->toc[idx-1].ctrl_adr;
+ cd->toc[idx].trk_no = 0;
+ cd->toc[idx].idx_no = 0xAA;
+ cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0];
+ cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1];
+ cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2];
+
+ cd->flags |= MCDTOC;
+
+ return 0;
+}
+
+static int
+mcd_toc_entry(int unit, struct ioc_read_toc_entry *te)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ struct ret_toc {
+ struct ioc_toc_header th;
+ struct cd_toc_entry rt;
+ } ret_toc;
+ struct ioc_toc_header th;
+ int rc, i;
+
+ /* Make sure we have a valid toc */
+ if ((rc=mcd_read_toc(unit)) != 0) {
+ return rc;
+ }
+
+ /* find the toc to copy*/
+ i = te->starting_track;
+ if (i == MCD_LASTPLUS1) {
+ i = bcd2bin(cd->volinfo.trk_high) + 1;
+ }
+
+ /* verify starting track */
+ if (i < bcd2bin(cd->volinfo.trk_low) ||
+ i > bcd2bin(cd->volinfo.trk_high)+1) {
+ return EINVAL;
+ }
+
+ /* do we have room */
+ if (te->data_len < sizeof(struct ioc_toc_header) +
+ sizeof(struct cd_toc_entry)) {
+ return EINVAL;
+ }
+
+ /* Copy the toc header */
+ if (mcd_toc_header(unit, &th) < 0) {
+ return EIO;
+ }
+ ret_toc.th = th;
+
+ /* copy the toc data */
+ ret_toc.rt.control = cd->toc[i].ctrl_adr;
+ ret_toc.rt.addr_type = te->address_format;
+ ret_toc.rt.track = i;
+ if (te->address_format == CD_MSF_FORMAT) {
+ ret_toc.rt.addr.addr[1] = cd->toc[i].hd_pos_msf[0];
+ ret_toc.rt.addr.addr[2] = cd->toc[i].hd_pos_msf[1];
+ ret_toc.rt.addr.addr[3] = cd->toc[i].hd_pos_msf[2];
+ }
+
+ /* copy the data back */
+ copyout(&ret_toc, te->data, sizeof(struct cd_toc_entry)
+ + sizeof(struct ioc_toc_header));
+
+ return 0;
+}
+
+static int
+mcd_stop(int unit)
+{
+ struct mcd_data *cd = mcd_data + unit;
+
+ if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0) {
+ return ENXIO;
+ }
+ cd->audio_status = CD_AS_PLAY_COMPLETED;
+ return 0;
+}
+
+static int
+mcd_getqchan(int unit, struct mcd_qchninfo *q)
+{
+ struct mcd_data *cd = mcd_data + unit;
+
+ if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0) {
+ return -1;
+ }
+ if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0) {
+ return -1;
+ }
+ if (cd->debug) {
+ printf("mcd%d: qchannel ctl=%d, t=%d, i=%d, ttm=%d:%d.%d dtm=%d:%d.%d\n",
+ unit,
+ q->ctrl_adr, q->trk_no, q->idx_no,
+ q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2],
+ q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2]);
+ }
+ return 0;
+}
+
+static int
+mcd_subchan(int unit, struct ioc_read_subchannel *sc)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ struct mcd_qchninfo q;
+ struct cd_sub_channel_info data;
+
+ printf("mcd%d: subchan af=%d, df=%d\n", unit,
+ sc->address_format,
+ sc->data_format);
+ if (sc->address_format != CD_MSF_FORMAT) {
+ return EIO;
+ }
+ if (sc->data_format != CD_CURRENT_POSITION) {
+ return EIO;
+ }
+ if (mcd_getqchan(unit, &q) < 0) {
+ return EIO;
+ }
+
+ data.header.audio_status = cd->audio_status;
+ data.what.position.data_format = CD_MSF_FORMAT;
+ data.what.position.track_number = bcd2bin(q.trk_no);
+
+ if (copyout(&data, sc->data, sizeof(struct cd_sub_channel_info))!=0) {
+ return EFAULT;
+ }
+ return 0;
+}
+
+static int
+mcd_playtracks(int unit, struct ioc_play_track *pt)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ struct mcd_read2 pb;
+ int a = pt->start_track;
+ int z = pt->end_track;
+ int rc;
+
+ if ((rc = mcd_read_toc(unit)) != 0) {
+ return rc;
+ }
+ printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit,
+ a, pt->start_index, z, pt->end_index);
+
+ if (a < cd->volinfo.trk_low || a > cd->volinfo.trk_high || a > z ||
+ z < cd->volinfo.trk_low || z > cd->volinfo.trk_high) {
+ return EINVAL;
+ }
+
+ pb.start_msf[0] = cd->toc[a].hd_pos_msf[0];
+ pb.start_msf[1] = cd->toc[a].hd_pos_msf[1];
+ pb.start_msf[2] = cd->toc[a].hd_pos_msf[2];
+ pb.end_msf[0] = cd->toc[z+1].hd_pos_msf[0];
+ pb.end_msf[1] = cd->toc[z+1].hd_pos_msf[1];
+ pb.end_msf[2] = cd->toc[z+1].hd_pos_msf[2];
+
+ return mcd_play(unit, &pb);
+}
+
+static int
+mcd_play(int unit, struct mcd_read2 *pb)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ int port = cd->iobase;
+ int retry, st;
+
+ cd->lastpb = *pb;
+ for(retry=0; retry<MCD_RETRYS; retry++) {
+ outb(port+mcd_command, MCD_CMDREAD2);
+ outb(port+mcd_command, pb->start_msf[0]);
+ outb(port+mcd_command, pb->start_msf[1]);
+ outb(port+mcd_command, pb->start_msf[2]);
+ outb(port+mcd_command, pb->end_msf[0]);
+ outb(port+mcd_command, pb->end_msf[1]);
+ outb(port+mcd_command, pb->end_msf[2]);
+ if ((st=mcd_getstat(unit, 0)) != -1) {
+ break;
+ }
+ }
+
+ if (cd->debug) {
+ printf("mcd%d: mcd_play retry=%d, status=%d\n", unit, retry, st);
+ }
+ if (st == -1) {
+ return ENXIO;
+ }
+ cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
+ return 0;
+}
+
+static int
+mcd_pause(int unit)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ struct mcd_qchninfo q;
+ int rc;
+
+ /* Verify current status */
+ if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) {
+ printf("mcd%d: pause attempted when not playing\n", unit);
+ return EINVAL;
+ }
+
+ /* Get the current position */
+ if (mcd_getqchan(unit, &q) < 0) {
+ return EIO;
+ }
+
+ /* Copy it into lastpb */
+ cd->lastpb.start_msf[0] = q.hd_pos_msf[0];
+ cd->lastpb.start_msf[1] = q.hd_pos_msf[1];
+ cd->lastpb.start_msf[2] = q.hd_pos_msf[2];
+
+ /* Stop playing */
+ if ((rc=mcd_stop(unit)) != 0) {
+ return rc;
+ }
+
+ /* Set the proper status and exit */
+ cd->audio_status = CD_AS_PLAY_PAUSED;
+ return 0;
+}
+
+static int
+mcd_resume(int unit)
+{
+ struct mcd_data *cd = mcd_data + unit;
+
+ if (cd->audio_status != CD_AS_PLAY_PAUSED) {
+ return EINVAL;
+ }
+ return mcd_play(unit, &cd->lastpb);
+}
+#endif /*!MCDMINI*/
+
+#endif /* NMCD > 0 */
diff --git a/sys/dev/mcd/mcdreg.h b/sys/dev/mcd/mcdreg.h
new file mode 100644
index 0000000..0ce5de7
--- /dev/null
+++ b/sys/dev/mcd/mcdreg.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright 1993 by Holger Veit (data part)
+ * Copyright 1993 by Brian Moore (audio part)
+ * Changes Copyright 1993 by Gary Clark II
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This software was developed by Holger Veit and Brian Moore
+ * for use with "386BSD" and similar operating systems.
+ * "Similar operating systems" includes mainly non-profit oriented
+ * systems for research and education, including but not restricted to
+ * "NetBSD", "FreeBSD", "Mach" (by CMU).
+ * 4. Neither the name of the developer(s) nor the name "386BSD"
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER(S) BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file contains definitions for some cdrom control commands
+ * and status codes. This info was "inherited" from the DOS MTMCDE.SYS
+ * driver, and is thus not complete (and may even be wrong). Some day
+ * the manufacturer or anyone else might provide better documentation,
+ * so this file (and the driver) will then have a better quality.
+ *
+ * $Id: mcdreg.h,v 1.2 1994/01/16 23:34:17 jkh Exp $
+ */
+
+#ifndef MCD_H
+#define MCD_H
+
+#ifdef __GNUC__
+#if __GNUC__ >= 2
+#pragma pack(1)
+#endif
+#endif
+
+typedef unsigned char bcd_t;
+#define M_msf(msf) msf[0]
+#define S_msf(msf) msf[1]
+#define F_msf(msf) msf[2]
+
+/* io lines used */
+#define MCD_IO_BASE 0x300
+
+#define mcd_command 0
+#define mcd_status 0
+#define mcd_rdata 0
+
+#define mcd_reset 1
+#define mcd_xfer 1
+#define mcd_ctl2 2 /* XXX Is this right? */
+#define mcd_config 3
+
+#define MCD_MASK_DMA 0x07 /* bits 2-0 = DMA channel */
+#define MCD_MASK_IRQ 0x70 /* bits 6-4 = INT number */
+ /* 001 = int 2,9 */
+ /* 010 = int 3 */
+ /* 011 = int 5 */
+ /* 100 = int 10 */
+ /* 101 = int 11 */
+/* flags */
+#define STATUS_AVAIL 0xB
+#define DATA_AVAIL 0xF
+
+/* New Flags */
+#define M_STATUS_AVAIL 0xFB
+#define M_DATA_AVAIL 0xFD
+
+/* New Commands */
+#define M_RESET 0x00
+#define M_PICKLE 0x04
+
+/* ports */
+#define MCD_DATA 0
+#define MCD_FLAGS 1
+#define MCD_CTRL 2
+#define CHANNEL 3 /* XXX ??? */
+
+/* Status bits */
+#define MCD_ST_DOOROPEN 0x80
+#define MCD_ST_DSKIN 0x40
+#define MCD_ST_DSKCHNG 0x20
+#define MCD_ST_BUSY 0x04
+#define MCD_ST_AUDIOBSY 0x02
+
+/* commands known by the controller */
+#define MCD_CMDRESET 0x00
+#define MCD_CMDGETVOLINFO 0x10 /* gets mcd_volinfo */
+#define MCD_CMDGETQCHN 0x20 /* gets mcd_qchninfo */
+#define MCD_CMDGETSTAT 0x40 /* gets a byte of status */
+#define MCD_CMDSETMODE 0x50 /* set transmission mode, needs byte */
+#define MCD_MD_RAW 0x60
+#define MCD_MD_COOKED 0x01
+#define MCD_MD_TOC 0x05
+#define MCD_CMDSTOPAUDIO 0x70
+#define MCD_CMDGETVOLUME 0x8E /* gets mcd_volume */
+#define MCD_CMDSETVOLUME 0xAE /* sets mcd_volume */
+#define MCD_CMDREAD1 0xB0 /* read n sectors */
+#define MCD_CMDREAD2 0xC0 /* read from-to */
+#define MCD_CMDCONTINFO 0xDC /* Get controller info */
+#define MCD_CMDEJECTDISK 0xF6
+#define MCD_CMDCLOSETRAY 0xF8
+#define MCD_CMDLOCKDRV 0xFE /* needs byte */
+#define MCD_LK_UNLOCK 0x00
+#define MCD_LK_LOCK 0x01
+#define MCD_LK_TEST 0x02
+
+struct mcd_volinfo {
+ bcd_t trk_low;
+ bcd_t trk_high;
+ bcd_t vol_msf[3];
+ bcd_t trk1_msf[3];
+};
+
+struct mcd_qchninfo {
+ u_char ctrl_adr;
+ u_char trk_no;
+ u_char idx_no;
+ bcd_t trk_size_msf[3];
+ u_char :8;
+ bcd_t hd_pos_msf[3];
+};
+
+struct mcd_volume {
+ u_char v0l;
+ u_char v0rs;
+ u_char v0r;
+ u_char v0ls;
+};
+
+struct mcd_read1 {
+ bcd_t start_msf[3];
+ u_char nsec[3];
+};
+
+struct mcd_read2 {
+ bcd_t start_msf[3];
+ bcd_t end_msf[3];
+};
+#endif /* MCD_H */
diff --git a/sys/dev/mse/mse.c b/sys/dev/mse/mse.c
new file mode 100644
index 0000000..466e936
--- /dev/null
+++ b/sys/dev/mse/mse.c
@@ -0,0 +1,499 @@
+/*
+ * Copyright 1992 by the University of Guelph
+ *
+ * Permission to use, copy and modify this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation.
+ * University of Guelph makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+/*
+ * Driver for the Logitech and ATI Inport Bus mice for use with 386bsd and
+ * the X386 port, courtesy of
+ * Rick Macklem, rick@snowhite.cis.uoguelph.ca
+ * Caveats: The driver currently uses spltty(), but doesn't use any
+ * generic tty code. It could use splmse() (that only masks off the
+ * bus mouse interrupt, but that would require hacking in i386/isa/icu.s.
+ * (This may be worth the effort, since the Logitech generates 30/60
+ * interrupts/sec continuously while it is open.)
+ * NB: The ATI has NOT been tested yet!
+ */
+
+/*
+ * Modification history:
+ *
+ * Oct 19, 1992 -- E. Stark (stark@cs.sunysb.edu)
+ * fixes to make it work with Microsoft InPort busmouse
+ *
+ * Jan, 1993 -- E. Stark (stark@cs.sunysb.edu)
+ * added patches for new "select" interface
+ *
+ * May 4, 1993 -- E. Stark (stark@cs.sunysb.edu)
+ * changed position of some spl()'s in mseread
+ *
+ * October 8, 1993 -- E. Stark (stark@cs.sunysb.edu)
+ * limit maximum negative x/y value to -127 to work around XFree problem
+ * that causes spurious button pushes.
+ */
+
+#include "mse.h"
+#if NMSE > 0
+#include "param.h"
+#include "proc.h"
+#include "user.h"
+#include "buf.h"
+#include "systm.h"
+#include "kernel.h"
+#include "ioctl.h"
+#include "tty.h"
+#include "uio.h"
+
+#include "i386/isa/isa_device.h"
+#include "i386/isa/icu.h"
+
+static int mseprobe(struct isa_device *);
+static int mseattach(struct isa_device *);
+void mseintr(int);
+
+struct isa_driver msedriver = {
+ mseprobe, mseattach, "mse"
+};
+
+/*
+ * Software control structure for mouse. The sc_enablemouse(),
+ * sc_disablemouse() and sc_getmouse() routines must be called spl'd().
+ */
+#define PROTOBYTES 5
+struct mse_softc {
+ int sc_flags;
+ int sc_mousetype;
+ struct selinfo sc_selp;
+ u_int sc_port;
+ void (*sc_enablemouse)();
+ void (*sc_disablemouse)();
+ void (*sc_getmouse)();
+ int sc_deltax;
+ int sc_deltay;
+ int sc_obuttons;
+ int sc_buttons;
+ int sc_bytesread;
+ u_char sc_bytes[PROTOBYTES];
+} mse_sc[NMSE];
+
+/* Flags */
+#define MSESC_OPEN 0x1
+#define MSESC_WANT 0x2
+
+/* and Mouse Types */
+#define MSE_LOGITECH 0x1
+#define MSE_ATIINPORT 0x2
+
+#define MSE_PORTA 0
+#define MSE_PORTB 1
+#define MSE_PORTC 2
+#define MSE_PORTD 3
+
+#define MSE_UNIT(dev) (minor(dev) >> 1)
+#define MSE_NBLOCKIO(dev) (minor(dev) & 0x1)
+
+/*
+ * Logitech bus mouse definitions
+ */
+#define MSE_SETUP 0x91 /* What does this mean? */
+#define MSE_HOLD 0x80
+#define MSE_RXLOW 0x00
+#define MSE_RXHIGH 0x20
+#define MSE_RYLOW 0x40
+#define MSE_RYHIGH 0x60
+#define MSE_DISINTR 0x10
+#define MSE_INTREN 0x00
+
+static int mse_probelogi();
+static void mse_enablelogi(), mse_disablelogi(), mse_getlogi();
+
+/*
+ * ATI Inport mouse definitions
+ */
+#define MSE_INPORT_RESET 0x80
+#define MSE_INPORT_STATUS 0x00
+#define MSE_INPORT_DX 0x01
+#define MSE_INPORT_DY 0x02
+#define MSE_INPORT_MODE 0x07
+#define MSE_INPORT_HOLD 0x20
+#define MSE_INPORT_INTREN 0x09
+
+static int mse_probeati();
+static void mse_enableati(), mse_disableati(), mse_getati();
+
+#define MSEPRI (PZERO + 3)
+
+/*
+ * Table of mouse types.
+ * Keep the Logitech last, since I haven't figured out how to probe it
+ * properly yet. (Someday I'll have the documentation.)
+ */
+struct mse_types {
+ int m_type; /* Type of bus mouse */
+ int (*m_probe)(); /* Probe routine to test for it */
+ void (*m_enable)(); /* Start routine */
+ void (*m_disable)(); /* Disable interrupts routine */
+ void (*m_get)(); /* and get mouse status */
+} mse_types[] = {
+ { MSE_ATIINPORT, mse_probeati, mse_enableati, mse_disableati, mse_getati },
+ { MSE_LOGITECH, mse_probelogi, mse_enablelogi, mse_disablelogi, mse_getlogi },
+ { 0, },
+};
+
+int
+mseprobe(idp)
+ register struct isa_device *idp;
+{
+ register struct mse_softc *sc = &mse_sc[idp->id_unit];
+ register int i;
+
+ /*
+ * Check for each mouse type in the table.
+ */
+ i = 0;
+ while (mse_types[i].m_type) {
+ if ((*mse_types[i].m_probe)(idp)) {
+ sc->sc_mousetype = mse_types[i].m_type;
+ sc->sc_enablemouse = mse_types[i].m_enable;
+ sc->sc_disablemouse = mse_types[i].m_disable;
+ sc->sc_getmouse = mse_types[i].m_get;
+ return (1);
+ }
+ i++;
+ }
+ return (0);
+}
+
+int
+mseattach(idp)
+ struct isa_device *idp;
+{
+ struct mse_softc *sc = &mse_sc[idp->id_unit];
+
+ sc->sc_port = idp->id_iobase;
+ return (1);
+}
+
+/*
+ * Exclusive open the mouse, initialize it and enable interrupts.
+ */
+int
+mseopen(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ register struct mse_softc *sc;
+ int s;
+
+ if (MSE_UNIT(dev) >= NMSE)
+ return (ENXIO);
+ sc = &mse_sc[MSE_UNIT(dev)];
+ if (sc->sc_flags & MSESC_OPEN)
+ return (EBUSY);
+ sc->sc_flags |= MSESC_OPEN;
+ sc->sc_obuttons = sc->sc_buttons = 0x7;
+ sc->sc_deltax = sc->sc_deltay = 0;
+ sc->sc_bytesread = PROTOBYTES;
+
+ /*
+ * Initialize mouse interface and enable interrupts.
+ */
+ s = spltty();
+ (*sc->sc_enablemouse)(sc->sc_port);
+ splx(s);
+ return (0);
+}
+
+/*
+ * mseclose: just turn off mouse innterrupts.
+ */
+int
+mseclose(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
+ int s;
+
+ s = spltty();
+ (*sc->sc_disablemouse)(sc->sc_port);
+ sc->sc_flags &= ~MSESC_OPEN;
+ splx(s);
+ return(0);
+}
+
+/*
+ * mseread: return mouse info using the MSC serial protocol, but without
+ * using bytes 4 and 5.
+ * (Yes this is cheesy, but it makes the X386 server happy, so...)
+ */
+int
+mseread(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
+ int xfer, s, error;
+
+ /*
+ * If there are no protocol bytes to be read, set up a new protocol
+ * packet.
+ */
+ s = spltty(); /* XXX Should be its own spl, but where is imlXX() */
+ if (sc->sc_bytesread >= PROTOBYTES) {
+ while (sc->sc_deltax == 0 && sc->sc_deltay == 0 &&
+ (sc->sc_obuttons ^ sc->sc_buttons) == 0) {
+ if (MSE_NBLOCKIO(dev)) {
+ splx(s);
+ return (0);
+ }
+ sc->sc_flags |= MSESC_WANT;
+ if (error = tsleep((caddr_t)sc, MSEPRI | PCATCH,
+ "mseread", 0)) {
+ splx(s);
+ return (error);
+ }
+ }
+
+ /*
+ * Generate protocol bytes.
+ * For some reason X386 expects 5 bytes but never uses
+ * the fourth or fifth?
+ */
+ sc->sc_bytes[0] = 0x80 | (sc->sc_buttons & ~0xf8);
+ if (sc->sc_deltax > 127)
+ sc->sc_deltax = 127;
+ if (sc->sc_deltax < -127)
+ sc->sc_deltax = -127;
+ sc->sc_deltay = -sc->sc_deltay; /* Otherwise mousey goes wrong way */
+ if (sc->sc_deltay > 127)
+ sc->sc_deltay = 127;
+ if (sc->sc_deltay < -127)
+ sc->sc_deltay = -127;
+ sc->sc_bytes[1] = sc->sc_deltax;
+ sc->sc_bytes[2] = sc->sc_deltay;
+ sc->sc_bytes[3] = sc->sc_bytes[4] = 0;
+ sc->sc_obuttons = sc->sc_buttons;
+ sc->sc_deltax = sc->sc_deltay = 0;
+ sc->sc_bytesread = 0;
+ }
+ splx(s);
+ xfer = min(uio->uio_resid, PROTOBYTES - sc->sc_bytesread);
+ if (error = uiomove(&sc->sc_bytes[sc->sc_bytesread], xfer, uio))
+ return (error);
+ sc->sc_bytesread += xfer;
+ return(0);
+}
+
+/*
+ * mseselect: check for mouse input to be processed.
+ */
+int
+mseselect(dev, rw, p)
+ dev_t dev;
+ int rw;
+ struct proc *p;
+{
+ register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
+ int s;
+
+ s = spltty();
+ if (sc->sc_bytesread != PROTOBYTES || sc->sc_deltax != 0 ||
+ sc->sc_deltay != 0 || (sc->sc_obuttons ^ sc->sc_buttons) != 0) {
+ splx(s);
+ return (1);
+ }
+
+ /*
+ * Since this is an exclusive open device, any previous proc.
+ * pointer is trash now, so we can just assign it.
+ */
+ selrecord(p, &sc->sc_selp);
+ splx(s);
+ return (0);
+}
+
+/*
+ * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative.
+ */
+void
+mseintr(unit)
+ int unit;
+{
+ register struct mse_softc *sc = &mse_sc[unit];
+ pid_t p;
+
+#ifdef DEBUG
+ static int mse_intrcnt = 0;
+ if((mse_intrcnt++ % 10000) == 0)
+ printf("mseintr\n");
+#endif /* DEBUG */
+ if ((sc->sc_flags & MSESC_OPEN) == 0)
+ return;
+
+ (*sc->sc_getmouse)(sc->sc_port, &sc->sc_deltax, &sc->sc_deltay, &sc->sc_buttons);
+
+ /*
+ * If mouse state has changed, wake up anyone wanting to know.
+ */
+ if (sc->sc_deltax != 0 || sc->sc_deltay != 0 ||
+ (sc->sc_obuttons ^ sc->sc_buttons) != 0) {
+ if (sc->sc_flags & MSESC_WANT) {
+ sc->sc_flags &= ~MSESC_WANT;
+ wakeup((caddr_t)sc);
+ }
+ selwakeup(&sc->sc_selp);
+ }
+}
+
+/*
+ * Routines for the Logitech mouse.
+ */
+/*
+ * Test for a Logitech bus mouse and return 1 if it is.
+ * (until I know how to use the signature port properly, just disable
+ * interrupts and return 1)
+ */
+static int
+mse_probelogi(idp)
+ register struct isa_device *idp;
+{
+
+ outb(idp->id_iobase + MSE_PORTB, 0x55);
+ if (inb(idp->id_iobase + MSE_PORTB) == 0x55) {
+ outb(idp->id_iobase + MSE_PORTB, 0xaa);
+ if (inb(idp->id_iobase + MSE_PORTB) == 0xaa)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Initialize Logitech mouse and enable interrupts.
+ */
+static void
+mse_enablelogi(port)
+ register u_int port;
+{
+ int dx, dy, but;
+
+ outb(port + MSE_PORTD, MSE_SETUP);
+ mse_getlogi(port, &dx, &dy, &but);
+}
+
+/*
+ * Disable interrupts for Logitech mouse.
+ */
+static void
+mse_disablelogi(port)
+ register u_int port;
+{
+
+ outb(port + MSE_PORTC, MSE_DISINTR);
+}
+
+/*
+ * Get the current dx, dy and button up/down state.
+ */
+static void
+mse_getlogi(port, dx, dy, but)
+ register u_int port;
+ int *dx;
+ int *dy;
+ int *but;
+{
+ register char x, y;
+
+ outb(port + MSE_PORTC, MSE_HOLD | MSE_RXLOW);
+ x = inb(port + MSE_PORTA);
+ *but = (x >> 5) & 0x7;
+ x &= 0xf;
+ outb(port + MSE_PORTC, MSE_HOLD | MSE_RXHIGH);
+ x |= (inb(port + MSE_PORTA) << 4);
+ outb(port + MSE_PORTC, MSE_HOLD | MSE_RYLOW);
+ y = (inb(port + MSE_PORTA) & 0xf);
+ outb(port + MSE_PORTC, MSE_HOLD | MSE_RYHIGH);
+ y |= (inb(port + MSE_PORTA) << 4);
+ *dx += x;
+ *dy += y;
+ outb(port + MSE_PORTC, MSE_INTREN);
+}
+
+/*
+ * Routines for the ATI Inport bus mouse.
+ */
+/*
+ * Test for a ATI Inport bus mouse and return 1 if it is.
+ * (do not enable interrupts)
+ */
+static int
+mse_probeati(idp)
+ register struct isa_device *idp;
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ if (inb(idp->id_iobase + MSE_PORTC) == 0xde)
+ return (1);
+ return (0);
+}
+
+/*
+ * Initialize ATI Inport mouse and enable interrupts.
+ */
+static void
+mse_enableati(port)
+ register u_int port;
+{
+
+ outb(port + MSE_PORTA, MSE_INPORT_RESET);
+ outb(port + MSE_PORTA, MSE_INPORT_MODE);
+ outb(port + MSE_PORTB, MSE_INPORT_INTREN);
+}
+
+/*
+ * Disable interrupts for ATI Inport mouse.
+ */
+static void
+mse_disableati(port)
+ register u_int port;
+{
+
+ outb(port + MSE_PORTA, MSE_INPORT_MODE);
+ outb(port + MSE_PORTB, 0);
+}
+
+/*
+ * Get current dx, dy and up/down button state.
+ */
+static void
+mse_getati(port, dx, dy, but)
+ register u_int port;
+ int *dx;
+ int *dy;
+ int *but;
+{
+ register char byte;
+
+ outb(port + MSE_PORTA, MSE_INPORT_MODE);
+ outb(port + MSE_PORTB, MSE_INPORT_HOLD);
+ outb(port + MSE_PORTA, MSE_INPORT_STATUS);
+ *but = ~(inb(port + MSE_PORTB) & 0x7);
+ outb(port + MSE_PORTA, MSE_INPORT_DX);
+ byte = inb(port + MSE_PORTB);
+ *dx += byte;
+ outb(port + MSE_PORTA, MSE_INPORT_DY);
+ byte = inb(port + MSE_PORTB);
+ *dy += byte;
+ outb(port + MSE_PORTA, MSE_INPORT_MODE);
+ outb(port + MSE_PORTB, MSE_INPORT_INTREN);
+}
+#endif /* NMSE */
diff --git a/sys/dev/ppbus/lptio.h b/sys/dev/ppbus/lptio.h
new file mode 100644
index 0000000..87af5bc
--- /dev/null
+++ b/sys/dev/ppbus/lptio.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 1994 Geoffrey M. Rehmet
+ *
+ * This program is free software; you may redistribute it and/or
+ * modify it, provided that it retain the above copyright notice
+ * and the following disclaimer.
+ *
+ * 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.
+ *
+ * Geoff Rehmet, Rhodes University, South Africa <csgr@cs.ru.ac.za>
+ *
+ */
+
+#ifndef _LPT_PRINTER_H_
+#define _LPT_PRINTER_H_
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#define LPT_IRQ _IOW('p', 1, long) /* set interrupt status */
+
+#endif
diff --git a/sys/dev/sio/sio.c b/sys/dev/sio/sio.c
new file mode 100644
index 0000000..dd4ee55
--- /dev/null
+++ b/sys/dev/sio/sio.c
@@ -0,0 +1,1920 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)com.c 7.5 (Berkeley) 5/16/91
+ * $Id: sio.c,v 1.44 1994/04/03 12:25:57 ache Exp $
+ */
+
+#include "sio.h"
+#if NSIO > 0
+#define DONT_MALLOC_TTYS
+/*
+ * Serial driver, based on 386BSD-0.1 com driver.
+ * Mostly rewritten to use pseudo-DMA.
+ * Works for National Semiconductor NS8250-NS16550AF UARTs.
+ * COM driver, based on HP dca driver.
+ */
+#include "param.h"
+#include "systm.h"
+#include "ioctl.h"
+#include "tty.h"
+#include "proc.h"
+#include "user.h"
+#include "conf.h"
+#include "file.h"
+#include "uio.h"
+#include "kernel.h"
+#include "syslog.h"
+
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/sioreg.h"
+#include "i386/isa/ic/ns16550.h"
+
+#define FAKE_DCD(unit) ((unit) == comconsole)
+#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */
+#define RBSZ 1024
+#define RB_I_HIGH_WATER (RBSZ - 2 * RS_IBUFSIZE)
+#define RB_I_LOW_WATER ((RBSZ - 2 * RS_IBUFSIZE) * 7 / 8)
+#define RS_IBUFSIZE 256
+#define RS_OBUFSIZE 256
+#define TTY_BI TTY_FE /* XXX */
+#define TTY_OE TTY_PE /* XXX */
+
+#ifdef COM_BIDIR
+#define CALLOUT(x) (minor(x) & COM_CALLOUT_MASK)
+#define COM_CALLOUT_MASK 0x80
+#define COM_MINOR_MAGIC_MASK 0x80
+#else /* COM_BIDIR */
+#define COM_MINOR_MAGIC_MASK 0
+#endif /* COM_BIDIR */
+
+#define UNIT(x) (minor(x) & ~COM_MINOR_MAGIC_MASK)
+
+#ifdef COM_MULTIPORT
+/* checks in flags for multiport and which is multiport "master chip"
+ * for a given card
+ */
+#define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01)
+#define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff)
+#define COM_NOMASTER(dev) ((dev)->id_flags & 0x04)
+#endif /* COM_MULTIPORT */
+
+#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02)
+
+#ifndef FIFO_TRIGGER
+/*
+ * This driver is fast enough to work with any value and for high values
+ * to be only slightly more efficient. Low values may be better because
+ * they give lower latency.
+ * TODO: always use low values for low speeds. Mouse movements are jerky
+ * if more than one packet arrives at once. The low speeds used for
+ * serial mice help avoid this, but not if (large) fifos are enabled.
+ */
+#define FIFO_TRIGGER FIFO_TRIGGER_14
+#endif
+
+#define com_scr 7 /* scratch register for 16450-16550 (R/W) */
+
+#ifndef setsofttty
+#define OLD_INTERRUPT_HANDLING /* XXX FreeBSD-1.1 and earlier */
+#define setsofttty() (ipending |= 1 << 4) /* XXX requires owning IRQ4 */
+extern u_int ipending; /* XXX */
+void softsio1 __P((void));
+#endif
+
+/*
+ * Input buffer watermarks.
+ * The external device is asked to stop sending when the buffer exactly reaches
+ * high water, or when the high level requests it.
+ * The high level is notified immediately (rather than at a later clock tick)
+ * when this watermark is reached.
+ * The buffer size is chosen so the watermark should almost never be reached.
+ * The low watermark is invisibly 0 since the buffer is always emptied all at
+ * once.
+ */
+#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)
+
+/*
+ * com state bits.
+ * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
+ * than the other bits so that they can be tested as a group without masking
+ * off the low bits.
+ *
+ * The following com and tty flags correspond closely:
+ * TS_BUSY = CS_BUSY (maintained by comstart() and comflush())
+ * CS_TTGO = ~TS_TTSTOP (maintained by comstart() and siostop())
+ * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam())
+ * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam())
+ * TS_FLUSH is not used.
+ * Bug: I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON.
+ */
+#define CS_BUSY 0x80 /* output in progress */
+#define CS_TTGO 0x40 /* output not stopped by XOFF */
+#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */
+#define CS_CHECKMSR 1 /* check of MSR scheduled */
+#define CS_CTS_OFLOW 2 /* use CTS output flow control */
+#define CS_ODONE 4 /* output completed */
+#define CS_RTS_IFLOW 8 /* use RTS input flow control */
+
+static char *error_desc[] = {
+#define CE_OVERRUN 0
+ "silo overflow",
+#define CE_INTERRUPT_BUF_OVERFLOW 1
+ "interrupt-level buffer overflow",
+#define CE_TTY_BUF_OVERFLOW 2
+ "tty-level buffer overflow",
+};
+
+#define CE_NTYPES 3
+#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum])
+
+/* types. XXX - should be elsewhere */
+typedef u_int Port_t; /* hardware port */
+typedef u_char bool_t; /* boolean */
+
+/* com device structure */
+struct com_s {
+ u_char state; /* miscellaneous flag bits */
+ u_char cfcr_image; /* copy of value written to CFCR */
+ bool_t hasfifo; /* nonzero for 16550 UARTs */
+ u_char mcr_image; /* copy of value written to MCR */
+#ifdef COM_BIDIR
+ bool_t bidir; /* is this unit bidirectional? */
+ bool_t active; /* is the port active _at all_? */
+ bool_t active_in; /* is the incoming port in use? */
+ bool_t active_out; /* is the outgoing port in use? */
+#endif /* COM_BIDIR */
+#ifdef COM_MULTIPORT
+ bool_t multiport; /* is this unit part of a multiport device? */
+#endif /* COM_MULTIPORT */
+ int dtr_wait; /* time to hold DTR down on close (* 1/HZ) */
+ u_int tx_fifo_size;
+
+ /*
+ * The high level of the driver never reads status registers directly
+ * because there would be too many side effects to handle conveniently.
+ * Instead, it reads copies of the registers stored here by the
+ * interrupt handler.
+ */
+ u_char last_modem_status; /* last MSR read by intr handler */
+ u_char prev_modem_status; /* last MSR handled by high level */
+
+ u_char *ibuf; /* start of input buffer */
+ u_char *ibufend; /* end of input buffer */
+ u_char *ihighwater; /* threshold in input buffer */
+ u_char *iptr; /* next free spot in input buffer */
+
+ u_char *obufend; /* end of output buffer */
+ int ocount; /* original count for current output */
+ u_char *optr; /* next char to output */
+
+ Port_t data_port; /* i/o ports */
+ Port_t int_id_port;
+ Port_t iobase;
+ Port_t modem_ctl_port;
+ Port_t line_status_port;
+ Port_t modem_status_port;
+
+ struct tty *tp; /* cross reference */
+
+#ifdef TIOCTIMESTAMP
+ bool_t do_timestamp;
+ struct timeval timestamp;
+#endif
+
+ u_long bytes_in; /* statistics */
+ u_long bytes_out;
+ u_int delta_error_counts[CE_NTYPES];
+ u_int error_counts[CE_NTYPES];
+
+ /*
+ * Ping-pong input buffers. The extra factor of 2 in the sizes is
+ * to allow for an error byte for each input byte.
+ */
+#define CE_INPUT_OFFSET RS_IBUFSIZE
+ u_char ibuf1[2 * RS_IBUFSIZE];
+ u_char ibuf2[2 * RS_IBUFSIZE];
+ u_char obuf[RS_OBUFSIZE];
+};
+
+/*
+ * The public functions in the com module ought to be declared in a com-driver
+ * system header.
+ */
+
+/* Interrupt handling entry points. */
+void siointr __P((int unit));
+void siopoll __P((void));
+
+/* Device switch entry points. */
+int sioopen __P((dev_t dev, int oflags, int devtype,
+ struct proc *p));
+int sioclose __P((dev_t dev, int fflag, int devtype,
+ struct proc *p));
+int sioread __P((dev_t dev, struct uio *uio, int ioflag));
+int siowrite __P((dev_t dev, struct uio *uio, int ioflag));
+int sioioctl __P((dev_t dev, int cmd, caddr_t data,
+ int fflag, struct proc *p));
+void siostop __P((struct tty *tp, int rw));
+#define sioreset noreset
+int sioselect __P((dev_t dev, int rw, struct proc *p));
+#define siommap nommap
+#define siostrategy nostrategy
+
+/* Console device entry points. */
+int siocngetc __P((dev_t dev));
+struct consdev;
+void siocninit __P((struct consdev *cp));
+void siocnprobe __P((struct consdev *cp));
+void siocnputc __P((dev_t dev, int c));
+
+static int sioattach __P((struct isa_device *dev));
+static void comflush __P((struct com_s *com));
+static void comhardclose __P((struct com_s *com));
+static void siointr1 __P((struct com_s *com));
+static void commctl __P((struct com_s *com, int bits, int how));
+static int comparam __P((struct tty *tp, struct termios *t));
+static int sioprobe __P((struct isa_device *dev));
+static void comstart __P((struct tty *tp));
+static void comwakeup __P((caddr_t chan, int ticks));
+static int tiocm_xxx2mcr __P((int tiocm_xxx));
+
+/* table and macro for fast conversion from a unit number to its com struct */
+static struct com_s *p_com_addr[NSIO];
+#define com_addr(unit) (p_com_addr[unit])
+
+static struct com_s com_structs[NSIO];
+
+#ifdef TIOCTIMESTAMP
+static struct timeval intr_timestamp;
+#endif
+
+struct isa_driver siodriver = {
+ sioprobe, sioattach, "sio"
+};
+
+#ifdef COMCONSOLE
+static int comconsole = COMCONSOLE;
+#else
+static int comconsole = -1;
+#endif
+static speed_t comdefaultrate = TTYDEF_SPEED;
+static u_int com_events; /* input chars + weighted output completions */
+static int commajor;
+#define TB_OUT(tp) (&(tp)->t_outq)
+#define TB_RAW(tp) (&(tp)->t_rawq)
+struct tty sio_tty[NSIO];
+extern struct tty *constty;
+extern int tk_nin; /* XXX */
+extern int tk_rawcc; /* XXX */
+
+#ifdef KGDB
+#include "machine/remote-sl.h"
+
+extern int kgdb_dev;
+extern int kgdb_rate;
+extern int kgdb_debug_init;
+#endif
+
+static struct speedtab comspeedtab[] = {
+ 0, 0,
+ 50, COMBRD(50),
+ 75, COMBRD(75),
+ 110, COMBRD(110),
+ 134, COMBRD(134),
+ 150, COMBRD(150),
+ 200, COMBRD(200),
+ 300, COMBRD(300),
+ 600, COMBRD(600),
+ 1200, COMBRD(1200),
+ 1800, COMBRD(1800),
+ 2400, COMBRD(2400),
+ 4800, COMBRD(4800),
+ 9600, COMBRD(9600),
+ 19200, COMBRD(19200),
+ 38400, COMBRD(38400),
+ 57600, COMBRD(57600),
+ 115200, COMBRD(115200),
+ -1, -1
+};
+
+/* XXX - configure this list */
+static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, };
+
+static int
+sioprobe(dev)
+ struct isa_device *dev;
+{
+ static bool_t already_init;
+ Port_t *com_ptr;
+ Port_t iobase;
+ int result;
+
+ if (!already_init) {
+ /*
+ * Turn off MCR_IENABLE for all likely serial ports. An unused
+ * port with its MCR_IENABLE gate open will inhibit interrupts
+ * from any used port that shares the interrupt vector.
+ */
+ for (com_ptr = likely_com_ports;
+ com_ptr < &likely_com_ports[sizeof likely_com_ports
+ / sizeof likely_com_ports[0]];
+ ++com_ptr)
+ outb(*com_ptr + com_mcr, 0);
+ already_init = TRUE;
+ }
+ iobase = dev->id_iobase;
+ result = IO_COMSIZE;
+
+ /*
+ * We don't want to get actual interrupts, just masked ones.
+ * Interrupts from this line should already be masked in the ICU,
+ * but mask them in the processor as well in case there are some
+ * (misconfigured) shared interrupts.
+ */
+ disable_intr();
+
+ /*
+ * Initialize the speed so that any junk in the THR or output fifo will
+ * be transmitted in a known time. (There may be lots of junk after a
+ * soft reboot, and output interrupts don't work right after a master
+ * reset, at least for 16550s. (The speed is undefined after MR, but
+ * MR empties the THR and the TSR so it's not clear why this matters)).
+ * Enable output interrupts (only) and check the following:
+ * o the CFCR, IER and MCR in UART hold the values written to them
+ * (the values happen to be all distinct - this is good for
+ * avoiding false positive tests from bus echoes).
+ * o an output interrupt is generated and its vector is correct.
+ * o the interrupt goes away when the IIR in the UART is read.
+ */
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ outb(iobase + com_dlbl, COMBRD(9600) & 0xff);
+ outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8);
+ outb(iobase + com_cfcr, CFCR_8BITS); /* ensure IER is addressed */
+ outb(iobase + com_mcr, MCR_IENABLE); /* open gate early */
+ outb(iobase + com_ier, 0); /* ensure edge on next intr */
+ outb(iobase + com_ier, IER_ETXRDY); /* generate interrupt */
+ DELAY((16 + 1) * 9600 / 10); /* enough to drain 16 bytes */
+ if ( inb(iobase + com_cfcr) != CFCR_8BITS
+ || inb(iobase + com_ier) != IER_ETXRDY
+ || inb(iobase + com_mcr) != MCR_IENABLE
+#ifndef COM_MULTIPORT /* XXX - need to do more to enable interrupts */
+ || !isa_irq_pending(dev)
+#endif
+ || (inb(iobase + com_iir) & IIR_IMASK) != IIR_TXRDY
+ || isa_irq_pending(dev)
+ || (inb(iobase + com_iir) & IIR_IMASK) != IIR_NOPEND)
+ result = 0;
+
+ /*
+ * Turn off all device interrupts and check that they go off properly.
+ * Leave MCR_IENABLE set. It gates the OUT2 output of the UART to
+ * the ICU input. Closing the gate would give a floating ICU input
+ * (unless there is another device driving at) and spurious interrupts.
+ * (On the system that this was first tested on, the input floats high
+ * and gives a (masked) interrupt as soon as the gate is closed.)
+ */
+ outb(iobase + com_ier, 0);
+ outb(iobase + com_mcr, MCR_IENABLE); /* dummy to avoid bus echo */
+ if ( inb(iobase + com_ier) != 0
+ || isa_irq_pending(dev)
+ || (inb(iobase + com_iir) & IIR_IMASK) != IIR_NOPEND)
+ result = 0;
+ if (result == 0)
+ outb(iobase + com_mcr, 0);
+
+ enable_intr();
+ return (result);
+}
+
+static int
+sioattach(isdp)
+ struct isa_device *isdp;
+{
+ struct com_s *com;
+ static bool_t comwakeup_started = FALSE;
+ Port_t iobase;
+ int s;
+ int unit;
+
+ iobase = isdp->id_iobase;
+ unit = isdp->id_unit;
+ s = spltty();
+
+ /*
+ * sioprobe() has initialized the device registers as follows:
+ * o cfcr = CFCR_8BITS.
+ * It is most important that CFCR_DLAB is off, so that the
+ * data port is not hidden when we enable interrupts.
+ * o ier = 0.
+ * Interrupts are only enabled when the line is open.
+ * o mcr = MCR_IENABLE.
+ * Keeping MCR_DTR and MCR_RTS off might stop the external
+ * device from sending before we are ready.
+ */
+
+ com = &com_structs[unit]; /* XXX malloc it */
+ com->cfcr_image = CFCR_8BITS;
+ com->mcr_image = MCR_IENABLE;
+ com->dtr_wait = 3 * hz;
+ com->tx_fifo_size = 1;
+ com->iptr = com->ibuf = com->ibuf1;
+ com->ibufend = com->ibuf1 + RS_IBUFSIZE;
+ com->ihighwater = com->ibuf1 + RS_IHIGHWATER;
+ com->iobase = iobase;
+ com->data_port = iobase + com_data;
+ com->int_id_port = iobase + com_iir;
+ com->modem_ctl_port = iobase + com_mcr;
+ com->line_status_port = iobase + com_lsr;
+ com->modem_status_port = iobase + com_msr;
+#ifdef DONT_MALLOC_TTYS
+ com->tp = &sio_tty[unit];
+#endif
+
+ /* attempt to determine UART type */
+ printf("sio%d: type", unit);
+#ifdef COM_MULTIPORT
+ if (!COM_ISMULTIPORT(isdp))
+#endif
+ {
+ u_char scr;
+ u_char scr1;
+ u_char scr2;
+
+ scr = inb(iobase + com_scr);
+ outb(iobase + com_scr, 0xa5);
+ scr1 = inb(iobase + com_scr);
+ outb(iobase + com_scr, 0x5a);
+ scr2 = inb(iobase + com_scr);
+ outb(iobase + com_scr, scr);
+ if (scr1 != 0xa5 || scr2 != 0x5a) {
+ printf(" 8250");
+ goto determined_type;
+ }
+ }
+ outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER_14);
+ DELAY(100);
+ switch (inb(com->int_id_port) & IIR_FIFO_MASK) {
+ case FIFO_TRIGGER_1:
+ printf(" 16450");
+ break;
+ case FIFO_TRIGGER_4:
+ printf(" 16450?");
+ break;
+ case FIFO_TRIGGER_8:
+ printf(" 16550?");
+ break;
+ case FIFO_TRIGGER_14:
+ printf(" 16550A");
+ if (COM_NOFIFO(isdp))
+ printf(" fifo disabled");
+ else {
+ com->hasfifo = TRUE;
+ com->tx_fifo_size = 16;
+ }
+ break;
+ }
+ outb(iobase + com_fifo, 0);
+determined_type: ;
+
+#ifdef COM_MULTIPORT
+ if (COM_ISMULTIPORT(isdp)) {
+ com->multiport = TRUE;
+ printf(" (multiport)");
+
+ /* Note: some cards have no master port (e.g., BocaBoards) */
+ if (!COM_NOMASTER(isdp)) {
+ struct isa_device *masterdev;
+
+ /* set the master's common-interrupt-enable reg.,
+ * as appropriate. YYY See your manual
+ */
+ /* enable only common interrupt for port */
+ outb(com->modem_ctl_port, com->mcr_image = 0);
+
+ masterdev = find_isadev(isa_devtab_tty, &siodriver,
+ COM_MPMASTER(isdp));
+ outb(masterdev->id_iobase + com_scr, 0x80);
+ }
+
+ } else
+ com->multiport = FALSE;
+#endif /* COM_MULTIPORT */
+ printf("\n");
+
+#ifdef KGDB
+ if (kgdb_dev == makedev(commajor, unit)) {
+ if (comconsole == unit)
+ kgdb_dev = -1; /* can't debug over console port */
+ else {
+ int divisor;
+
+ /*
+ * XXX now unfinished and broken. Need to do
+ * something more like a full open(). There's no
+ * suitable interrupt handler so don't enable device
+ * interrupts. Watch out for null tp's.
+ */
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ divisor = ttspeedtab(kgdb_rate, comspeedtab);
+ outb(iobase + com_dlbl, divisor & 0xFF);
+ outb(iobase + com_dlbh, (u_int) divisor >> 8);
+ outb(iobase + com_cfcr, CFCR_8BITS);
+ outb(com->modem_status_port,
+ com->mcr_image |= MCR_DTR | MCR_RTS);
+
+ if (kgdb_debug_init) {
+ /*
+ * Print prefix of device name,
+ * let kgdb_connect print the rest.
+ */
+ printf("sio%d: ", unit);
+ kgdb_connect(1);
+ } else
+ printf("sio%d: kgdb enabled\n", unit);
+ }
+ }
+#endif
+
+ com_addr(unit) = com;
+ splx(s);
+ if (!comwakeup_started) {
+ comwakeup((caddr_t) NULL, 0);
+ comwakeup_started = TRUE;
+ }
+ return (1);
+}
+
+/* ARGSUSED */
+int
+sioopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+#ifdef COM_BIDIR
+ bool_t callout;
+#endif /* COM_BIDIR */
+ struct com_s *com;
+ int error = 0;
+ bool_t got_status = FALSE;
+ Port_t iobase;
+ int s;
+ struct tty *tp;
+ int unit;
+
+ unit = UNIT(dev);
+ if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL)
+ return (ENXIO);
+#ifdef COM_BIDIR
+ /* if it's a callout device, and bidir not possible on that dev, die */
+ callout = CALLOUT(dev);
+ if (callout && !(com->bidir))
+ return (ENXIO);
+#endif /* COM_BIDIR */
+
+#ifdef DONT_MALLOC_TTYS
+ tp = com->tp;
+#else
+ sio_tty[unit] = ttymalloc(sio_tty[unit]);
+ tp = com->tp = sio_tty[unit];
+#endif
+ s = spltty();
+
+#ifdef COM_BIDIR
+
+bidir_open_top:
+ got_status = FALSE;
+ /* if it's bidirectional, we've gotta deal with it... */
+ if (com->bidir) {
+ if (callout) {
+ if (com->active_in) {
+ /* it's busy. die */
+ splx(s);
+ return (EBUSY);
+ } else {
+ /* it's ours. lock it down, and set it up */
+ com->active_out = TRUE;
+ }
+ } else {
+ if (com->active_out) {
+ /* it's busy, outgoing. wait, if possible */
+ if (flag & O_NONBLOCK) {
+ /* can't wait; bail */
+ splx(s);
+ return (EBUSY);
+ } else {
+ /* wait for it... */
+ error = tsleep((caddr_t)&com->active_out,
+ TTIPRI|PCATCH,
+ "siooth",
+ 0);
+ /* if there was an error, take off. */
+ if (error != 0) {
+ splx(s);
+ return (error);
+ }
+ /* else take it from the top */
+ goto bidir_open_top;
+ }
+ }
+ disable_intr();
+ com->prev_modem_status =
+ com->last_modem_status = inb(com->modem_status_port);
+ enable_intr();
+ got_status = TRUE;
+ if (com->prev_modem_status & MSR_DCD
+ || FAKE_DCD(unit)) {
+ /* there's a carrier on the line; we win */
+ com->active_in = TRUE;
+ } else {
+ /* there is no carrier on the line */
+ if (flag & O_NONBLOCK) {
+ /* can't wait; let it open */
+ com->active_in = TRUE;
+ } else {
+ /* put DTR & RTS up */
+ /* XXX - bring up RTS earlier? */
+ commctl(com, MCR_DTR | MCR_RTS, DMSET);
+ outb(com->iobase + com_ier, IER_EMSC);
+
+ /* wait for it... */
+ error = tsleep((caddr_t)&com->active_in,
+ TTIPRI|PCATCH,
+ "siodcd",
+ 0);
+
+ /* if not active, turn intrs and DTR off */
+ if (!com->active) {
+ outb(com->iobase + com_ier, 0);
+ commctl(com, MCR_DTR, DMBIC);
+ }
+
+ /* if there was an error, take off. */
+ if (error != 0) {
+ splx(s);
+ return (error);
+ }
+ /* else take it from the top */
+ goto bidir_open_top;
+ }
+ }
+ }
+ }
+
+ com->active = TRUE;
+#endif /* COM_BIDIR */
+
+ tp->t_oproc = comstart;
+ tp->t_param = comparam;
+ tp->t_dev = dev;
+ if (!(tp->t_state & TS_ISOPEN)) {
+ tp->t_state |= TS_WOPEN;
+ ttychars(tp);
+ if (tp->t_ispeed == 0) {
+ /*
+ * We don't use all the flags from <sys/ttydefaults.h>
+ * since those are only relevant for logins. It's
+ * important to have echo off initially so that the
+ * line doesn't start blathering before the echo flag
+ * can be turned off.
+ */
+ tp->t_iflag = 0;
+ tp->t_oflag = 0;
+ tp->t_cflag = CREAD | CS8;
+#ifdef COM_BIDIR
+ if (com->bidir && !callout)
+ tp->t_cflag |= HUPCL;
+#endif
+ tp->t_lflag = 0;
+ tp->t_ispeed = tp->t_ospeed = comdefaultrate;
+ if (unit == comconsole) {
+ tp->t_iflag = TTYDEF_IFLAG;
+ tp->t_oflag = TTYDEF_OFLAG;
+ tp->t_cflag = TTYDEF_CFLAG;
+ tp->t_lflag = TTYDEF_LFLAG;
+ }
+ }
+
+ /*
+ * XXX the full state after a first open() needs to be
+ * programmable and separate for callin and callout.
+ */
+#ifdef COM_BIDIR
+ if (com->bidir) {
+ if (callout)
+ tp->t_cflag |= CLOCAL;
+ else
+ tp->t_cflag &= ~CLOCAL;
+ }
+#endif
+
+ commctl(com, MCR_DTR | MCR_RTS, DMSET);
+ error = comparam(tp, &tp->t_termios);
+ if (error != 0)
+ goto out;
+ ttsetwater(tp);
+ iobase = com->iobase;
+ if (com->hasfifo) {
+ /* (re)enable and drain FIFO */
+ outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER
+ | FIFO_RCV_RST | FIFO_XMT_RST);
+ DELAY(100);
+ }
+ disable_intr();
+ (void) inb(com->line_status_port);
+ (void) inb(com->data_port);
+ if (!got_status)
+ com->prev_modem_status =
+ com->last_modem_status = inb(com->modem_status_port);
+ outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS
+ | IER_EMSC);
+ enable_intr();
+ if (com->prev_modem_status & MSR_DCD || FAKE_DCD(unit))
+ tp->t_state |= TS_CARR_ON;
+ } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
+ splx(s);
+ return (EBUSY);
+ }
+ while (!(flag & O_NONBLOCK) && !(tp->t_cflag & CLOCAL)
+#ifdef COM_BIDIR
+ /* We went through a lot of trouble to open it,
+ * but it's certain we have a carrier now, so
+ * don't spend any time on it now.
+ */
+ && !(com->bidir)
+#endif /* COM_BIDIR */
+ && !(tp->t_state & TS_CARR_ON)) {
+ tp->t_state |= TS_WOPEN;
+ error = ttysleep(tp, (caddr_t)TB_RAW(tp), TTIPRI | PCATCH,
+ ttopen, 0);
+ if (error != 0)
+ break;
+ }
+out:
+ if (error == 0)
+ error = (*linesw[tp->t_line].l_open)(dev, tp);
+ splx(s);
+
+#ifdef COM_BIDIR
+ /* wakeup sleepers */
+ wakeup((caddr_t) &com->active_in);
+#endif /* COM_BIDIR */
+
+ /*
+ * XXX - the next step was once not done, so interrupts, DTR and RTS
+ * remained hot if the process was killed while it was sleeping
+ * waiting for carrier. Now there is the opposite problem. If several
+ * processes are sleeping waiting for carrier on the same line and one
+ * is killed, interrupts are turned off so the other processes will
+ * never see the carrier rise.
+ */
+ if (error != 0 && !(tp->t_state & TS_ISOPEN))
+ comhardclose(com);
+ tp->t_state &= ~TS_WOPEN;
+
+ return (error);
+}
+
+/*ARGSUSED*/
+int
+sioclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+ struct com_s *com;
+ int s;
+ struct tty *tp;
+
+ com = com_addr(UNIT(dev));
+ tp = com->tp;
+ s = spltty();
+ (*linesw[tp->t_line].l_close)(tp, flag);
+ siostop(tp, FREAD | FWRITE);
+ comhardclose(com);
+ ttyclose(tp);
+ splx(s);
+ return (0);
+}
+
+static void
+comhardclose(com)
+ struct com_s *com;
+{
+ Port_t iobase;
+ int s;
+ struct tty *tp;
+ int unit;
+
+ unit = com - &com_structs[0];
+ iobase = com->iobase;
+ s = spltty();
+#ifdef TIOCTIMESTAMP
+ com->do_timestamp = 0;
+#endif
+ outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
+#ifdef KGDB
+ /* do not disable interrupts or hang up if debugging */
+ if (kgdb_dev != makedev(commajor, unit))
+#endif
+ {
+ outb(iobase + com_ier, 0);
+ tp = com->tp;
+ if (tp->t_cflag & HUPCL || tp->t_state & TS_WOPEN
+#ifdef COM_BIDIR
+ /*
+ * XXX we will miss any carrier drop between here and the
+ * next open. Perhaps we should watch DCD even when the
+ * port is closed; it is not sufficient to check it at
+ * the next open because it might go up and down while
+ * we're not watching. And we shouldn't look at DCD if
+ * CLOCAL is set (here or for the dialin device ...).
+ * When the termios state is reinitialized for initial
+ * opens, the correct CLOCAL bit will be
+ * ((the bit now) & (the initial bit)).
+ */
+ || com->active_in
+ && !(com->prev_modem_status & MSR_DCD) && !FAKE_DCD(unit)
+#endif
+ || !(tp->t_state & TS_ISOPEN)) {
+ commctl(com, MCR_RTS, DMSET);
+ if (com->dtr_wait != 0)
+ /*
+ * Uninterruptible sleep since we want to
+ * wait a fixed time.
+ * XXX - delay in open() (if necessary),
+ * not here (always).
+ */
+ tsleep((caddr_t)&com->dtr_wait, TTIPRI,
+ "sioclose", com->dtr_wait);
+ }
+ }
+
+#ifdef COM_BIDIR
+ com->active = com->active_in = com->active_out = FALSE;
+
+ /* wakeup sleepers who are waiting for out to finish */
+ wakeup((caddr_t) &com->active_out);
+#endif /* COM_BIDIR */
+
+ splx(s);
+}
+
+int
+sioread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ struct tty *tp = com_addr(UNIT(dev))->tp;
+
+ return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
+}
+
+int
+siowrite(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ int unit = UNIT(dev);
+ struct tty *tp = com_addr(unit)->tp;
+
+ /*
+ * (XXX) We disallow virtual consoles if the physical console is
+ * a serial port. This is in case there is a display attached that
+ * is not the console. In that situation we don't need/want the X
+ * server taking over the console.
+ */
+ if (constty && unit == comconsole)
+ constty = NULL;
+ return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
+}
+
+#ifdef TIOCTIMESTAMP
+/* Interrupt routine for timekeeping purposes */
+void
+siointrts(unit)
+ int unit;
+{
+ microtime(&intr_timestamp);
+ siointr(unit);
+}
+#endif
+
+void
+siointr(unit)
+ int unit;
+{
+#ifndef COM_MULTIPORT
+ siointr1(com_addr(unit));
+#else /* COM_MULTIPORT */
+ bool_t possibly_more_intrs;
+ struct com_s *com;
+
+ /*
+ * Loop until there is no activity on any port. This is necessary
+ * to get an interrupt edge more than to avoid another interrupt.
+ * If the IRQ signal is just an OR of the IRQ signals from several
+ * devices, then the edge from one may be lost because another is
+ * on.
+ */
+ do {
+ possibly_more_intrs = FALSE;
+ for (unit = 0; unit < NSIO; ++unit) {
+ com = com_addr(unit);
+ if (com != NULL
+ && (inb(com->int_id_port) & IIR_IMASK)
+ != IIR_NOPEND) {
+ siointr1(com);
+ possibly_more_intrs = TRUE;
+ }
+ }
+ } while (possibly_more_intrs);
+#endif /* COM_MULTIPORT */
+}
+
+static void
+siointr1(com)
+ struct com_s *com;
+{
+ u_char line_status;
+ u_char modem_status;
+ u_char *ioptr;
+ u_char recv_data;
+
+#ifdef TIOCTIMESTAMP
+ if (com->do_timestamp)
+ /* XXX a little bloat here... */
+ com->timestamp = intr_timestamp;
+#endif
+ while (TRUE) {
+ line_status = inb(com->line_status_port);
+
+ /* input event? (check first to help avoid overruns) */
+ while (line_status & LSR_RCV_MASK) {
+ /* break/unnattached error bits or real input? */
+ if (!(line_status & LSR_RXRDY))
+ recv_data = 0;
+ else
+ recv_data = inb(com->data_port);
+ ++com->bytes_in;
+ /* XXX reduce SLIP input latency */
+#define FRAME_END 0xc0
+ if (recv_data == FRAME_END)
+ setsofttty();
+#ifdef KGDB
+ /* trap into kgdb? (XXX - needs testing and optim) */
+ if (recv_data == FRAME_END
+ && !(com->tp->t_state & TS_ISOPEN)
+ && kgdb_dev == makedev(commajor, unit)) {
+ kgdb_connect(0);
+ continue;
+ }
+#endif /* KGDB */
+ ioptr = com->iptr;
+ if (ioptr >= com->ibufend)
+ CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW);
+ else {
+ ++com_events;
+#if 0 /* for testing input latency vs efficiency */
+if (com->iptr - com->ibuf == 8)
+ setsofttty();
+#endif
+ ioptr[0] = recv_data;
+ ioptr[CE_INPUT_OFFSET] = line_status;
+ com->iptr = ++ioptr;
+ if (ioptr == com->ihighwater
+ && com->state & CS_RTS_IFLOW)
+ outb(com->modem_ctl_port,
+ com->mcr_image &= ~MCR_RTS);
+ /* XXX - move this out of isr */
+ if (line_status & LSR_OE)
+ CE_RECORD(com, CE_OVERRUN);
+ }
+
+ /*
+ * "& 0x7F" is to avoid the gcc-1.40 generating a slow
+ * jump from the top of the loop to here
+ */
+ line_status = inb(com->line_status_port) & 0x7F;
+ }
+
+ /* modem status change? (always check before doing output) */
+ modem_status = inb(com->modem_status_port);
+ if (modem_status != com->last_modem_status) {
+ /*
+ * Schedule high level to handle DCD changes. Note
+ * that we don't use the delta bits anywhere. Some
+ * UARTs mess them up, and it's easy to remember the
+ * previous bits and calculate the delta.
+ */
+ com->last_modem_status = modem_status;
+ if (!(com->state & CS_CHECKMSR)) {
+ com_events += LOTS_OF_EVENTS;
+ com->state |= CS_CHECKMSR;
+ setsofttty();
+ }
+
+ /* handle CTS change immediately for crisp flow ctl */
+ if (com->state & CS_CTS_OFLOW) {
+ if (modem_status & MSR_CTS)
+ com->state |= CS_ODEVREADY;
+ else
+ com->state &= ~CS_ODEVREADY;
+ }
+ }
+
+ /* output queued and everything ready? */
+ if (line_status & LSR_TXRDY
+ && com->state >= (CS_ODEVREADY | CS_BUSY | CS_TTGO)) {
+ ioptr = com->optr;
+ if (com->tx_fifo_size > 1) {
+ u_int ocount;
+
+ ocount = com->obufend - ioptr;
+ if (ocount > com->tx_fifo_size)
+ ocount = com->tx_fifo_size;
+ com->bytes_out += ocount;
+ do
+ outb(com->data_port, *ioptr++);
+ while (--ocount != 0);
+ } else {
+ outb(com->data_port, *ioptr++);
+ ++com->bytes_out;
+ }
+ com->optr = ioptr;
+ if (ioptr >= com->obufend) {
+ /* output just completed */
+ com_events += LOTS_OF_EVENTS;
+ com->state ^= (CS_ODONE | CS_BUSY);
+ setsofttty(); /* handle at high level ASAP */
+ }
+ }
+
+ /* finished? */
+#ifndef COM_MULTIPORT
+ if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND)
+#endif /* COM_MULTIPORT */
+ return;
+ }
+}
+
+static int
+tiocm_xxx2mcr(tiocm_xxx)
+ int tiocm_xxx;
+{
+ int mcr;
+
+ mcr = 0;
+ if (tiocm_xxx & TIOCM_DTR)
+ mcr |= MCR_DTR;
+ if (tiocm_xxx & TIOCM_RTS)
+ mcr |= MCR_RTS;
+ return (mcr);
+}
+
+int
+sioioctl(dev, cmd, data, flag, p)
+ dev_t dev;
+ int cmd;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+{
+ struct com_s *com;
+ int error;
+ Port_t iobase;
+ int mcr;
+ int msr;
+ int s;
+ int tiocm_xxx;
+ struct tty *tp;
+
+ com = com_addr(UNIT(dev));
+ tp = com->tp;
+ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
+ if (error >= 0)
+ return (error);
+ error = ttioctl(tp, cmd, data, flag);
+
+#ifdef COM_BIDIR
+ /* XXX: plug security hole while sticky bits not yet implemented */
+ if (com->bidir && com->active_in && p->p_ucred->cr_uid != 0)
+ tp->t_cflag &= ~CLOCAL;
+#endif
+
+ if (error >= 0)
+ return (error);
+
+ iobase = com->iobase;
+ s = spltty();
+ switch (cmd) {
+ case TIOCSBRK:
+ outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK);
+ break;
+ case TIOCCBRK:
+ outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
+ break;
+ case TIOCSDTR:
+ commctl(com, MCR_DTR, DMBIS);
+ break;
+ case TIOCCDTR:
+ commctl(com, MCR_DTR, DMBIC);
+ break;
+ case TIOCMSET:
+ commctl(com, tiocm_xxx2mcr(*(int *)data), DMSET);
+ break;
+ case TIOCMBIS:
+ commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIS);
+ break;
+ case TIOCMBIC:
+ commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIC);
+ break;
+ case TIOCMGET:
+ tiocm_xxx = TIOCM_LE; /* XXX - always enabled while open */
+ mcr = com->mcr_image;
+ if (mcr & MCR_DTR)
+ tiocm_xxx |= TIOCM_DTR;
+ if (mcr & MCR_RTS)
+ tiocm_xxx |= TIOCM_RTS;
+ msr = com->prev_modem_status;
+ if (msr & MSR_CTS)
+ tiocm_xxx |= TIOCM_CTS;
+ if (msr & MSR_DCD)
+ tiocm_xxx |= TIOCM_CD;
+ if (msr & MSR_DSR)
+ tiocm_xxx |= TIOCM_DSR;
+ /*
+ * XXX - MSR_RI is naturally volatile, and we make MSR_TERI
+ * more volatile by reading the modem status a lot. Perhaps
+ * we should latch both bits until the status is read here.
+ */
+ if (msr & (MSR_RI | MSR_TERI))
+ tiocm_xxx |= TIOCM_RI;
+ *(int *)data = tiocm_xxx;
+ break;
+#ifdef COM_BIDIR
+ case TIOCMSBIDIR:
+ /* must be root to set bidir. capability */
+ error = suser(p->p_ucred, &p->p_acflag);
+ if (error != 0) {
+ splx(s);
+ return(EPERM);
+ }
+
+ /* if it's the console, can't do it (XXX why?) */
+ if (UNIT(dev) == comconsole) {
+ splx(s);
+ return(ENOTTY);
+ }
+
+#if 0
+ /* XXX - can't do the next, for obvious reasons...
+ * but there are problems to be looked at...
+ */
+ /* if the port is active, don't do it */
+ if (com->active) {
+ splx(s);
+ return(EBUSY);
+ }
+#endif
+
+ com->bidir = *(int *)data;
+ break;
+ case TIOCMGBIDIR:
+ *(int *)data = com->bidir;
+ break;
+#endif /* COM_BIDIR */
+#if 0
+ case TIOCMSDTRWAIT:
+ /* must be root since the wait applies to following logins */
+ error = suser(p->p_ucred, &p->p_acflag);
+ if (error != 0) {
+ splx(s);
+ return(EPERM);
+ }
+
+ /* if it's the console, can't do it (XXX why?) */
+ if (UNIT(dev) == comconsole) {
+ splx(s);
+ return(ENOTTY);
+ }
+ com->dtr_wait = *(int *)data;
+ break;
+ case TIOCMGDTRWAIT:
+ *(int *)data = com->dtr_wait;
+ break;
+#endif
+#ifdef TIOCTIMESTAMP
+ case TIOCTIMESTAMP:
+ com->do_timestamp = TRUE;
+ *(struct timeval *)data = com->timestamp;
+ break;
+#endif
+ default:
+ splx(s);
+ return (ENOTTY);
+ }
+ splx(s);
+ return (0);
+}
+
+/* cancel pending output */
+static void
+comflush(com)
+ struct com_s *com;
+{
+ struct clist *rbp;
+
+ disable_intr();
+ if (com->state & CS_ODONE)
+ com_events -= LOTS_OF_EVENTS;
+ com->state &= ~(CS_ODONE | CS_BUSY);
+ enable_intr();
+ while( getc( TB_OUT(com->tp)) != -1);
+ com->ocount = 0;
+ com->tp->t_state &= ~TS_BUSY;
+}
+
+void
+siopoll()
+{
+#ifdef OLD_INTERRUPT_HANDLING
+ static bool_t awake = FALSE;
+ int s;
+#endif
+ int unit;
+
+ if (com_events == 0)
+ return;
+
+#ifdef OLD_INTERRUPT_HANDLING
+ disable_intr();
+ if (awake) {
+ enable_intr();
+ return;
+ }
+ awake = TRUE;
+ enable_intr();
+ s = spltty();
+#endif
+
+repeat:
+ for (unit = 0; unit < NSIO; ++unit) {
+ u_char *buf;
+ struct com_s *com;
+ u_char *ibuf;
+ int incc;
+ struct tty *tp;
+
+ com = com_addr(unit);
+ if (com == NULL)
+ continue;
+ tp = com->tp;
+#ifdef DONT_MALLOC_TTYS
+ if (tp == NULL)
+ continue;
+#endif
+
+ /* switch the role of the low-level input buffers */
+ if (com->iptr == (ibuf = com->ibuf)) {
+ buf = NULL; /* not used, but compiler can't tell */
+ incc = 0;
+ } else {
+ buf = ibuf;
+ disable_intr();
+ incc = com->iptr - buf;
+ com_events -= incc;
+ if (ibuf == com->ibuf1)
+ ibuf = com->ibuf2;
+ else
+ ibuf = com->ibuf1;
+ com->ibufend = ibuf + RS_IBUFSIZE;
+ com->ihighwater = ibuf + RS_IHIGHWATER;
+ com->iptr = ibuf;
+
+ /*
+ * There is now room for another low-level buffer full
+ * of input, so enable RTS if it is now disabled and
+ * there is room in the high-level buffer.
+ */
+ /*
+ * XXX this used not to look at CS_RTS_IFLOW. The
+ * change is to allow full control of MCR_RTS via
+ * ioctls after turning CS_RTS_IFLOW off. Check
+ * for races. We shouldn't allow the ioctls while
+ * CS_RTS_IFLOW is on.
+ */
+ if ((com->state & CS_RTS_IFLOW)
+ && !(com->mcr_image & MCR_RTS) /*
+ && !(tp->t_state & TS_RTS_IFLOW) */)
+ outb(com->modem_ctl_port,
+ com->mcr_image |= MCR_RTS);
+ enable_intr();
+ com->ibuf = ibuf;
+ }
+
+ if (com->state & CS_CHECKMSR) {
+ u_char delta_modem_status;
+
+ disable_intr();
+ delta_modem_status = com->last_modem_status
+ ^ com->prev_modem_status;
+ com->prev_modem_status = com->last_modem_status;
+ com_events -= LOTS_OF_EVENTS;
+ com->state &= ~CS_CHECKMSR;
+ enable_intr();
+ if (delta_modem_status & MSR_DCD && !FAKE_DCD(unit)) {
+ if (com->prev_modem_status & MSR_DCD) {
+ (*linesw[tp->t_line].l_modem)(tp, 1);
+#ifdef COM_BIDIR
+ wakeup((caddr_t) &com->active_in);
+#endif /* COM_BIDIR */
+ } else
+ (*linesw[tp->t_line].l_modem)(tp, 0);
+ }
+ }
+
+ /* XXX */
+ if (TRUE) {
+ u_int delta;
+ int errnum;
+ u_long total;
+
+ for (errnum = 0; errnum < CE_NTYPES; ++errnum) {
+ disable_intr();
+ delta = com->delta_error_counts[errnum];
+ com->delta_error_counts[errnum] = 0;
+ enable_intr();
+ if (delta != 0) {
+ total =
+ com->error_counts[errnum] += delta;
+ log(LOG_WARNING,
+ "sio%d: %u more %s%s (total %lu)\n",
+ unit, delta, error_desc[errnum],
+ delta == 1 ? "" : "s", total);
+ }
+ }
+ }
+ if (com->state & CS_ODONE) {
+ comflush(com);
+ /* XXX - why isn't the table used for t_line == 0? */
+ if (tp->t_line != 0)
+ (*linesw[tp->t_line].l_start)(tp);
+ else
+ comstart(tp);
+ }
+ if (incc <= 0 || !(tp->t_state & TS_ISOPEN))
+ continue;
+ if (com->state & CS_RTS_IFLOW
+ && TB_RAW(tp)->c_cc + incc >= RB_I_HIGH_WATER /*
+ && !(tp->t_state & TS_RTS_IFLOW) */
+ /*
+ * XXX - need RTS flow control for all line disciplines.
+ * Only have it in standard one now.
+ */
+ && linesw[tp->t_line].l_rint == ttyinput) {
+/* tp->t_state |= TS_RTS_IFLOW; */
+ ttstart(tp);
+ }
+#if 0
+ /*
+ * Avoid the grotesquely inefficient lineswitch routine
+ * (ttyinput) in "raw" mode. It usually takes about 450
+ * instructions (that's without canonical processing or echo!).
+ * slinput is reasonably fast (usually 40 instructions plus
+ * call overhead).
+ */
+ if (!(tp->t_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP
+ | IXOFF | IXON))
+ && !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG
+ | PENDIN))
+ && !(tp->t_state & (TS_CNTTB | TS_LNCH))
+ && linesw[tp->t_line].l_rint == ttyinput) {
+ tk_nin += incc;
+ tk_rawcc += incc;
+ tp->t_rawcc += incc;
+ com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
+ += incc - rb_write(TB_RAW(tp), (char *) buf,
+ incc);
+ ttwakeup(tp);
+ if (tp->t_state & TS_TTSTOP
+ && (tp->t_iflag & IXANY
+ || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
+ tp->t_state &= ~TS_TTSTOP;
+ tp->t_lflag &= ~FLUSHO;
+ ttstart(tp);
+ }
+ } else {
+#endif
+ do {
+ u_char line_status;
+ int recv_data;
+
+ line_status = (u_char) buf[CE_INPUT_OFFSET];
+ recv_data = (u_char) *buf++;
+ if (line_status
+ & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) {
+ if (line_status & LSR_BI)
+ recv_data |= TTY_BI;
+ if (line_status & LSR_FE)
+ recv_data |= TTY_FE;
+ if (line_status & LSR_OE)
+ recv_data |= TTY_OE;
+ if (line_status & LSR_PE)
+ recv_data |= TTY_PE;
+ }
+ (*linesw[tp->t_line].l_rint)(recv_data, tp);
+ } while (--incc > 0);
+#if 0
+ }
+#endif
+ if (com_events == 0)
+ break;
+ }
+ if (com_events >= LOTS_OF_EVENTS)
+ goto repeat;
+
+#ifdef OLD_INTERRUPT_HANDLING
+ splx(s);
+ awake = FALSE;
+#endif
+}
+
+static int
+comparam(tp, t)
+ struct tty *tp;
+ struct termios *t;
+{
+ u_int cfcr;
+ int cflag;
+ struct com_s *com;
+ int divisor;
+ int error;
+ Port_t iobase;
+ int s;
+ int unit;
+
+ /* check requested parameters */
+ divisor = ttspeedtab(t->c_ospeed, comspeedtab);
+ if (t->c_ispeed == 0)
+ t->c_ispeed = t->c_ospeed;
+ if (divisor < 0 || t->c_ispeed != t->c_ospeed)
+ return (EINVAL);
+
+ /* parameters are OK, convert them to the com struct and the device */
+ unit = UNIT(tp->t_dev);
+ com = com_addr(unit);
+ iobase = com->iobase;
+ s = spltty();
+ if (divisor == 0)
+ commctl(com, MCR_DTR, DMBIC); /* hang up line */
+ else
+ commctl(com, MCR_DTR, DMBIS);
+ cflag = t->c_cflag;
+ switch (cflag & CSIZE) {
+ case CS5:
+ cfcr = CFCR_5BITS;
+ break;
+ case CS6:
+ cfcr = CFCR_6BITS;
+ break;
+ case CS7:
+ cfcr = CFCR_7BITS;
+ break;
+ default:
+ cfcr = CFCR_8BITS;
+ break;
+ }
+ if (cflag & PARENB) {
+ cfcr |= CFCR_PENAB;
+ if (!(cflag & PARODD))
+ cfcr |= CFCR_PEVEN;
+ }
+ if (cflag & CSTOPB)
+ cfcr |= CFCR_STOPB;
+
+ /*
+ * Some UARTs lock up if the divisor latch registers are selected
+ * while the UART is doing output (they refuse to transmit anything
+ * more until given a hard reset). Fix this by stopping filling
+ * the device buffers and waiting for them to drain. Reading the
+ * line status port outside of siointr1() might lose some receiver
+ * error bits, but that is acceptable here.
+ */
+ disable_intr();
+retry:
+ com->state &= ~CS_TTGO;
+ enable_intr();
+ while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY)) {
+ error = ttysleep(tp, (caddr_t)TB_RAW(tp), TTIPRI | PCATCH,
+ "sioparam", 1);
+ if (error != 0 && error != EAGAIN) {
+ if (!(tp->t_state & TS_TTSTOP)) {
+ disable_intr();
+ com->state |= CS_TTGO;
+ enable_intr();
+ }
+ splx(s);
+ return (error);
+ }
+ }
+
+ disable_intr(); /* very important while com_data is hidden */
+
+ /*
+ * XXX - clearing CS_TTGO is not sufficient to stop further output,
+ * because siopoll() calls comstart() which usually sets it again
+ * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be
+ * sufficient, for similar reasons.
+ */
+ if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY))
+ goto retry;
+
+ if (divisor != 0) {
+ outb(iobase + com_cfcr, cfcr | CFCR_DLAB);
+ outb(iobase + com_dlbl, divisor & 0xFF);
+ outb(iobase + com_dlbh, (u_int) divisor >> 8);
+ }
+ outb(iobase + com_cfcr, com->cfcr_image = cfcr);
+ if (!(tp->t_state & TS_TTSTOP))
+ com->state |= CS_TTGO;
+ if (cflag & CRTS_IFLOW)
+ com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */
+ else
+ com->state &= ~CS_RTS_IFLOW;
+
+ /*
+ * Set up state to handle output flow control.
+ * XXX - worth handling MDMBUF (DCD) flow control at the lowest level?
+ * Now has 16+ msec latency, while CTS flow has 50- usec latency.
+ */
+ com->state &= ~CS_CTS_OFLOW;
+ com->state |= CS_ODEVREADY;
+ if (cflag & CCTS_OFLOW) {
+ com->state |= CS_CTS_OFLOW;
+ if (!(com->last_modem_status & MSR_CTS))
+ com->state &= ~CS_ODEVREADY;
+ }
+
+ /*
+ * Recover from fiddling with CS_TTGO. We used to call siointr1()
+ * unconditionally, but that defeated the careful discarding of
+ * stale input in sioopen().
+ *
+ * XXX sioopen() is not careful waiting for carrier for the callout
+ * case.
+ */
+ if (com->state >= (CS_BUSY | CS_TTGO))
+ siointr1(com);
+
+ enable_intr();
+ splx(s);
+ return (0);
+}
+
+static void
+comstart(tp)
+ struct tty *tp;
+{
+ struct com_s *com;
+ int s;
+ int unit;
+
+ unit = UNIT(tp->t_dev);
+ com = com_addr(unit);
+ s = spltty();
+ disable_intr();
+ if (tp->t_state & TS_TTSTOP)
+ com->state &= ~CS_TTGO;
+ else
+ com->state |= CS_TTGO;
+#if 0
+ if (tp->t_state & TS_RTS_IFLOW) {
+ if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW)
+ outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS);
+ } else {
+#endif
+ /*
+ * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it
+ * appropriately in comparam() if RTS-flow is being changed.
+ * Check for races.
+ */
+ if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater)
+ outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
+#if 0
+ }
+#endif
+ enable_intr();
+ if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))
+ goto out;
+ if (TB_OUT(tp)->c_cc <= tp->t_lowat) {
+ if (tp->t_state & TS_ASLEEP) {
+ tp->t_state &= ~TS_ASLEEP;
+ wakeup((caddr_t)TB_OUT(tp));
+ }
+ selwakeup(&tp->t_wsel);
+ }
+ if (com->ocount != 0) {
+ disable_intr();
+ siointr1(com);
+ enable_intr();
+ } else if (TB_OUT(tp)->c_cc != 0) {
+ tp->t_state |= TS_BUSY;
+ disable_intr();
+ com->ocount = q_to_b(TB_OUT(tp), com->obuf, sizeof com->obuf);
+ com->optr = com->obuf;
+ com->obufend = com->obuf + com->ocount;
+ com->state |= CS_BUSY;
+ siointr1(com); /* fake interrupt to start output */
+ enable_intr();
+ }
+out:
+ splx(s);
+}
+
+void
+siostop(tp, rw)
+ struct tty *tp;
+ int rw;
+{
+ struct com_s *com;
+
+ com = com_addr(UNIT(tp->t_dev));
+ if (rw & FWRITE)
+ comflush(com);
+ disable_intr();
+ if (rw & FREAD) {
+ com_events -= (com->iptr - com->ibuf);
+ com->iptr = com->ibuf;
+ }
+ if (tp->t_state & TS_TTSTOP)
+ com->state &= ~CS_TTGO;
+ else
+ com->state |= CS_TTGO;
+ enable_intr();
+}
+
+int
+sioselect(dev, rw, p)
+ dev_t dev;
+ int rw;
+ struct proc *p;
+{
+ return (ttselect(dev & ~COM_MINOR_MAGIC_MASK, rw, p));
+}
+
+static void
+commctl(com, bits, how)
+ struct com_s *com;
+ int bits;
+ int how;
+{
+ disable_intr();
+ switch (how) {
+ case DMSET:
+ outb(com->modem_ctl_port,
+ com->mcr_image = bits | (com->mcr_image & MCR_IENABLE));
+ break;
+ case DMBIS:
+ outb(com->modem_ctl_port, com->mcr_image |= bits);
+ break;
+ case DMBIC:
+ outb(com->modem_ctl_port, com->mcr_image &= ~bits);
+ break;
+ }
+ enable_intr();
+}
+
+static void
+comwakeup(chan, ticks)
+ caddr_t chan;
+ int ticks;
+{
+ int unit;
+
+ timeout((timeout_func_t)comwakeup, (caddr_t) NULL, hz / 100);
+
+ if (com_events != 0) {
+#ifndef OLD_INTERRUPT_HANDLING
+ int s = spltty();
+#endif
+ siopoll();
+#ifndef OLD_INTERRUPT_HANDLING
+ splx(s);
+#endif
+ }
+
+ /* recover from lost output interrupts */
+ for (unit = 0; unit < NSIO; ++unit) {
+ struct com_s *com;
+
+ com = com_addr(unit);
+ if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) {
+ disable_intr();
+ siointr1(com);
+ enable_intr();
+ }
+ }
+}
+
+#ifdef OLD_INTERRUPT_HANDLING
+void
+softsio1()
+{
+ siopoll();
+}
+#endif
+
+/*
+ * Following are all routines needed for SIO to act as console
+ */
+#include "i386/i386/cons.h"
+
+struct siocnstate {
+ u_char dlbl;
+ u_char dlbh;
+ u_char ier;
+ u_char cfcr;
+ u_char mcr;
+};
+
+static Port_t siocniobase;
+
+static void
+siocntxwait()
+{
+ int timo;
+
+ /*
+ * Wait for any pending transmission to finish. Required to avoid
+ * the UART lockup bug when the speed is changed, and for normal
+ * transmits.
+ */
+ timo = 100000;
+ while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY) && --timo != 0)
+ ;
+}
+
+static void
+siocnopen(sp)
+ struct siocnstate *sp;
+{
+ int divisor;
+ Port_t iobase;
+
+ /*
+ * Save all the device control registers except the fifo register
+ * and set our default ones (cs8 -parenb speed=comdefaultrate).
+ * We can't save the fifo register since it is read-only.
+ */
+ iobase = siocniobase;
+ sp->ier = inb(iobase + com_ier);
+ outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */
+ siocntxwait();
+ sp->cfcr = inb(iobase + com_cfcr);
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ sp->dlbl = inb(iobase + com_dlbl);
+ sp->dlbh = inb(iobase + com_dlbh);
+ divisor = ttspeedtab(comdefaultrate, comspeedtab);
+ outb(iobase + com_dlbl, divisor & 0xFF);
+ outb(iobase + com_dlbh, (u_int) divisor >> 8);
+ outb(iobase + com_cfcr, CFCR_8BITS);
+ sp->mcr = inb(iobase + com_mcr);
+ outb(iobase + com_mcr, MCR_DTR | MCR_RTS);
+}
+
+static void
+siocnclose(sp)
+ struct siocnstate *sp;
+{
+ Port_t iobase;
+
+ /*
+ * Restore the device control registers.
+ */
+ siocntxwait();
+ iobase = siocniobase;
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ outb(iobase + com_dlbl, sp->dlbl);
+ outb(iobase + com_dlbh, sp->dlbh);
+ outb(iobase + com_cfcr, sp->cfcr);
+ /*
+ * XXX damp osicllations of MCR_DTR or MCR_RTS by not restoring them.
+ */
+ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS);
+ outb(iobase + com_ier, sp->ier);
+}
+
+void
+siocnprobe(cp)
+ struct consdev *cp;
+{
+ int unit;
+
+ /* locate the major number */
+ /* XXX - should be elsewhere since KGDB uses it */
+ for (commajor = 0; commajor < nchrdev; commajor++)
+ if (cdevsw[commajor].d_open == sioopen)
+ break;
+
+ /* XXX: ick */
+ unit = UNIT(CONUNIT);
+ siocniobase = CONADDR;
+
+ /* make sure hardware exists? XXX */
+
+ /* initialize required fields */
+ cp->cn_dev = makedev(commajor, unit);
+#ifdef COMCONSOLE
+ cp->cn_pri = CN_REMOTE; /* Force a serial port console */
+#else
+ cp->cn_pri = CN_NORMAL;
+#endif
+}
+
+void
+siocninit(cp)
+ struct consdev *cp;
+{
+ /*
+ * XXX can delete more comconsole stuff now that i/o routines are
+ * fairly reentrant.
+ */
+ comconsole = UNIT(cp->cn_dev);
+}
+
+int
+siocngetc(dev)
+ dev_t dev;
+{
+ int c;
+ Port_t iobase;
+ int s;
+ struct siocnstate sp;
+
+ iobase = siocniobase;
+ s = spltty();
+ siocnopen(&sp);
+ while (!(inb(iobase + com_lsr) & LSR_RXRDY))
+ ;
+ c = inb(iobase + com_data);
+ siocnclose(&sp);
+ splx(s);
+ return (c);
+}
+
+void
+siocnputc(dev, c)
+ dev_t dev;
+ int c;
+{
+ int s;
+ struct siocnstate sp;
+
+ s = spltty();
+ siocnopen(&sp);
+ siocntxwait();
+ outb(siocniobase + com_data, c);
+ siocnclose(&sp);
+ splx(s);
+}
+
+#endif /* NSIO > 0 */
diff --git a/sys/dev/sio/sioreg.h b/sys/dev/sio/sioreg.h
new file mode 100644
index 0000000..4b0f1b6
--- /dev/null
+++ b/sys/dev/sio/sioreg.h
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)comreg.h 7.2 (Berkeley) 5/9/91
+ * $Id$
+ */
+
+
+/* 16 bit baud rate divisor (lower byte in dca_data, upper in dca_ier) */
+#define COMBRD(x) (1843200 / (16*(x)))
+
+/* interrupt enable register */
+#define IER_ERXRDY 0x1
+#define IER_ETXRDY 0x2
+#define IER_ERLS 0x4
+#define IER_EMSC 0x8
+
+/* interrupt identification register */
+#define IIR_IMASK 0xf
+#define IIR_RXTOUT 0xc
+#define IIR_RLS 0x6
+#define IIR_RXRDY 0x4
+#define IIR_TXRDY 0x2
+#define IIR_NOPEND 0x1
+#define IIR_MLSC 0x0
+#define IIR_FIFO_MASK 0xc0 /* set if FIFOs are enabled */
+
+/* fifo control register */
+#define FIFO_ENABLE 0x01
+#define FIFO_RCV_RST 0x02
+#define FIFO_XMT_RST 0x04
+#define FIFO_DMA_MODE 0x08
+#define FIFO_TRIGGER_1 0x00
+#define FIFO_TRIGGER_4 0x40
+#define FIFO_TRIGGER_8 0x80
+#define FIFO_TRIGGER_14 0xc0
+
+/* character format control register */
+#define CFCR_DLAB 0x80
+#define CFCR_SBREAK 0x40
+#define CFCR_PZERO 0x30
+#define CFCR_PONE 0x20
+#define CFCR_PEVEN 0x10
+#define CFCR_PODD 0x00
+#define CFCR_PENAB 0x08
+#define CFCR_STOPB 0x04
+#define CFCR_8BITS 0x03
+#define CFCR_7BITS 0x02
+#define CFCR_6BITS 0x01
+#define CFCR_5BITS 0x00
+
+/* modem control register */
+#define MCR_LOOPBACK 0x10
+#define MCR_IENABLE 0x08
+#define MCR_DRS 0x04
+#define MCR_RTS 0x02
+#define MCR_DTR 0x01
+
+/* line status register */
+#define LSR_RCV_FIFO 0x80
+#define LSR_TSRE 0x40
+#define LSR_TXRDY 0x20
+#define LSR_BI 0x10
+#define LSR_FE 0x08
+#define LSR_PE 0x04
+#define LSR_OE 0x02
+#define LSR_RXRDY 0x01
+#define LSR_RCV_MASK 0x1f
+
+/* modem status register */
+#define MSR_DCD 0x80
+#define MSR_RI 0x40
+#define MSR_DSR 0x20
+#define MSR_CTS 0x10
+#define MSR_DDCD 0x08
+#define MSR_TERI 0x04
+#define MSR_DDSR 0x02
+#define MSR_DCTS 0x01
+
+/*
+ * WARNING: Serial console is assumed to be at COM1 address
+ * and CONUNIT must be 0.
+ */
+#define CONADDR (0x3f8)
+#define CONUNIT (0)
diff --git a/sys/dev/speaker/speaker.h b/sys/dev/speaker/speaker.h
new file mode 100644
index 0000000..af80a28
--- /dev/null
+++ b/sys/dev/speaker/speaker.h
@@ -0,0 +1,30 @@
+/*
+ * speaker.h -- interface definitions for speaker ioctl()
+ *
+ * v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993
+ * modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su>
+ */
+
+#ifndef _SPEAKER_H_
+#define _SPEAKER_H_
+
+#include <sys/ioctl.h>
+
+#define SPKRTONE _IOW('S', 1, tone_t) /* emit tone */
+#define SPKRTUNE _IO('S', 2) /* emit tone sequence*/
+
+typedef struct
+{
+ int frequency; /* in hertz */
+ int duration; /* in 1/100ths of a second */
+}
+tone_t;
+
+/*
+ * Strings written to the speaker device are interpreted as tunes and played;
+ * see the spkr(4) man page for details.
+ */
+
+#endif /* _SPEAKER_H_ */
+
+/* speaker.h ends here */
diff --git a/sys/dev/speaker/spkr.c b/sys/dev/speaker/spkr.c
new file mode 100644
index 0000000..d273f31
--- /dev/null
+++ b/sys/dev/speaker/spkr.c
@@ -0,0 +1,541 @@
+/*
+ * spkr.c -- device driver for console speaker
+ *
+ * v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993
+ * modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su>
+ *
+ * $Id: spkr.c,v 1.7 1994/01/25 23:04:27 ache Exp $
+ */
+
+#include "speaker.h"
+
+#if NSPEAKER > 0
+
+#include "param.h"
+#include "systm.h"
+#include "kernel.h"
+#include "errno.h"
+#include "buf.h"
+#include "uio.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/timerreg.h"
+#include "machine/speaker.h"
+
+/**************** MACHINE DEPENDENT PART STARTS HERE *************************
+ *
+ * This section defines a function tone() which causes a tone of given
+ * frequency and duration from the 80x86's console speaker.
+ * Another function endtone() is defined to force sound off, and there is
+ * also a rest() entry point to do pauses.
+ *
+ * Audible sound is generated using the Programmable Interval Timer (PIT) and
+ * Programmable Peripheral Interface (PPI) attached to the 80x86's speaker. The
+ * PPI controls whether sound is passed through at all; the PIT's channel 2 is
+ * used to generate clicks (a square wave) of whatever frequency is desired.
+ */
+
+/*
+ * PIT and PPI port addresses and control values
+ *
+ * Most of the magic is hidden in the TIMER_PREP value, which selects PIT
+ * channel 2, frequency LSB first, square-wave mode and binary encoding.
+ * The encoding is as follows:
+ *
+ * +----------+----------+---------------+-----+
+ * | 1 0 | 1 1 | 0 1 1 | 0 |
+ * | SC1 SC0 | RW1 RW0 | M2 M1 M0 | BCD |
+ * +----------+----------+---------------+-----+
+ * Counter Write Mode 3 Binary
+ * Channel 2 LSB first, (Square Wave) Encoding
+ * MSB second
+ */
+#define PPI_SPKR 0x03 /* turn these PPI bits on to pass sound */
+#define PIT_MODE 0xB6 /* set timer mode for sound generation */
+
+/*
+ * Magic numbers for timer control.
+ */
+#define TIMER_CLK 1193180L /* corresponds to 18.2 MHz tick rate */
+
+#define SPKRPRI PSOCK
+static char endtone, endrest;
+
+static void tone(thz, ticks)
+/* emit tone of frequency thz for given number of ticks */
+unsigned int thz, ticks;
+{
+ unsigned int divisor = TIMER_CLK / thz;
+ int sps;
+
+#ifdef DEBUG
+ (void) printf("tone: thz=%d ticks=%d\n", thz, ticks);
+#endif /* DEBUG */
+
+ /* set timer to generate clicks at given frequency in Hertz */
+ sps = spltty();
+
+ if (acquire_timer2(PIT_MODE)) {
+ /* enter list of waiting procs ??? */
+ return;
+ }
+ outb(TIMER_CNTR2, (divisor & 0xff)); /* send lo byte */
+ outb(TIMER_CNTR2, (divisor >> 8)); /* send hi byte */
+ splx(sps);
+
+ /* turn the speaker on */
+ outb(IO_PPI, inb(IO_PPI) | PPI_SPKR);
+
+ /*
+ * Set timeout to endtone function, then give up the timeslice.
+ * This is so other processes can execute while the tone is being
+ * emitted.
+ */
+ (void) tsleep((caddr_t)&endtone, SPKRPRI | PCATCH, "spkrtn", ticks);
+ outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR);
+ release_timer2();
+}
+
+static void rest(ticks)
+/* rest for given number of ticks */
+int ticks;
+{
+ /*
+ * Set timeout to endrest function, then give up the timeslice.
+ * This is so other processes can execute while the rest is being
+ * waited out.
+ */
+#ifdef DEBUG
+ (void) printf("rest: %d\n", ticks);
+#endif /* DEBUG */
+ (void) tsleep((caddr_t)&endrest, SPKRPRI | PCATCH, "spkrrs", ticks);
+}
+
+/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
+ *
+ * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
+ * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave-
+ * tracking facility are added.
+ * Requires tone(), rest(), and endtone(). String play is not interruptible
+ * except possibly at physical block boundaries.
+ */
+
+typedef int bool;
+#define TRUE 1
+#define FALSE 0
+
+#define toupper(c) ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z')))
+#define isdigit(c) (((c) >= '0') && ((c) <= '9'))
+#define dtoi(c) ((c) - '0')
+
+static int octave; /* currently selected octave */
+static int whole; /* whole-note time at current tempo, in ticks */
+static int value; /* whole divisor for note time, quarter note = 1 */
+static int fill; /* controls spacing of notes */
+static bool octtrack; /* octave-tracking on? */
+static bool octprefix; /* override current octave-tracking state? */
+
+/*
+ * Magic number avoidance...
+ */
+#define SECS_PER_MIN 60 /* seconds per minute */
+#define WHOLE_NOTE 4 /* quarter notes per whole note */
+#define MIN_VALUE 64 /* the most we can divide a note by */
+#define DFLT_VALUE 4 /* default value (quarter-note) */
+#define FILLTIME 8 /* for articulation, break note in parts */
+#define STACCATO 6 /* 6/8 = 3/4 of note is filled */
+#define NORMAL 7 /* 7/8ths of note interval is filled */
+#define LEGATO 8 /* all of note interval is filled */
+#define DFLT_OCTAVE 4 /* default octave */
+#define MIN_TEMPO 32 /* minimum tempo */
+#define DFLT_TEMPO 120 /* default tempo */
+#define MAX_TEMPO 255 /* max tempo */
+#define NUM_MULT 3 /* numerator of dot multiplier */
+#define DENOM_MULT 2 /* denominator of dot multiplier */
+
+/* letter to half-tone: A B C D E F G */
+static int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
+
+/*
+ * This is the American Standard A440 Equal-Tempered scale with frequencies
+ * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
+ * our octave 0 is standard octave 2.
+ */
+#define OCTAVE_NOTES 12 /* semitones per octave */
+static int pitchtab[] =
+{
+/* C C# D D# E F F# G G# A A# B*/
+/* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123,
+/* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
+/* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
+/* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
+/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
+/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
+/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
+};
+
+static void playinit()
+{
+ octave = DFLT_OCTAVE;
+ whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
+ fill = NORMAL;
+ value = DFLT_VALUE;
+ octtrack = FALSE;
+ octprefix = TRUE; /* act as though there was an initial O(n) */
+}
+
+static void playtone(pitch, value, sustain)
+/* play tone of proper duration for current rhythm signature */
+int pitch, value, sustain;
+{
+ register int sound, silence, snum = 1, sdenom = 1;
+
+ /* this weirdness avoids floating-point arithmetic */
+ for (; sustain; sustain--)
+ {
+ /* See the BUGS section in the man page for discussion */
+ snum *= NUM_MULT;
+ sdenom *= DENOM_MULT;
+ }
+
+ if (pitch == -1)
+ rest(whole * snum / (value * sdenom));
+ else
+ {
+ sound = (whole * snum) / (value * sdenom)
+ - (whole * (FILLTIME - fill)) / (value * FILLTIME);
+ silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
+
+#ifdef DEBUG
+ (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
+ pitch, sound, silence);
+#endif /* DEBUG */
+
+ tone(pitchtab[pitch], sound);
+ if (fill != LEGATO)
+ rest(silence);
+ }
+}
+
+static int abs(n)
+int n;
+{
+ if (n < 0)
+ return(-n);
+ else
+ return(n);
+}
+
+static void playstring(cp, slen)
+/* interpret and play an item from a notation string */
+char *cp;
+size_t slen;
+{
+ int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
+
+#define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \
+ {v = v * 10 + (*++cp - '0'); slen--;}
+ for (; slen--; cp++)
+ {
+ int sustain, timeval, tempo;
+ register char c = toupper(*cp);
+
+#ifdef DEBUG
+ (void) printf("playstring: %c (%x)\n", c, c);
+#endif /* DEBUG */
+
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+
+ /* compute pitch */
+ pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
+
+ /* this may be followed by an accidental sign */
+ if (cp[1] == '#' || cp[1] == '+')
+ {
+ ++pitch;
+ ++cp;
+ slen--;
+ }
+ else if (cp[1] == '-')
+ {
+ --pitch;
+ ++cp;
+ slen--;
+ }
+
+ /*
+ * If octave-tracking mode is on, and there has been no octave-
+ * setting prefix, find the version of the current letter note
+ * closest to the last regardless of octave.
+ */
+ if (octtrack && !octprefix)
+ {
+ if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
+ {
+ ++octave;
+ pitch += OCTAVE_NOTES;
+ }
+
+ if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
+ {
+ --octave;
+ pitch -= OCTAVE_NOTES;
+ }
+ }
+ octprefix = FALSE;
+ lastpitch = pitch;
+
+ /* ...which may in turn be followed by an override time value */
+ GETNUM(cp, timeval);
+ if (timeval <= 0 || timeval > MIN_VALUE)
+ timeval = value;
+
+ /* ...and/or sustain dots */
+ for (sustain = 0; cp[1] == '.'; cp++)
+ {
+ slen--;
+ sustain++;
+ }
+
+ /* ...and/or a slur mark */
+ oldfill = fill;
+ if (cp[1] == '_')
+ {
+ fill = LEGATO;
+ ++cp;
+ slen--;
+ }
+
+ /* time to emit the actual tone */
+ playtone(pitch, timeval, sustain);
+
+ fill = oldfill;
+ break;
+
+ case 'O':
+ if (cp[1] == 'N' || cp[1] == 'n')
+ {
+ octprefix = octtrack = FALSE;
+ ++cp;
+ slen--;
+ }
+ else if (cp[1] == 'L' || cp[1] == 'l')
+ {
+ octtrack = TRUE;
+ ++cp;
+ slen--;
+ }
+ else
+ {
+ GETNUM(cp, octave);
+ if (octave >= sizeof(pitchtab) / OCTAVE_NOTES)
+ octave = DFLT_OCTAVE;
+ octprefix = TRUE;
+ }
+ break;
+
+ case '>':
+ if (octave < sizeof(pitchtab) / OCTAVE_NOTES - 1)
+ octave++;
+ octprefix = TRUE;
+ break;
+
+ case '<':
+ if (octave > 0)
+ octave--;
+ octprefix = TRUE;
+ break;
+
+ case 'N':
+ GETNUM(cp, pitch);
+ for (sustain = 0; cp[1] == '.'; cp++)
+ {
+ slen--;
+ sustain++;
+ }
+ oldfill = fill;
+ if (cp[1] == '_')
+ {
+ fill = LEGATO;
+ ++cp;
+ slen--;
+ }
+ playtone(pitch - 1, value, sustain);
+ fill = oldfill;
+ break;
+
+ case 'L':
+ GETNUM(cp, value);
+ if (value <= 0 || value > MIN_VALUE)
+ value = DFLT_VALUE;
+ break;
+
+ case 'P':
+ case '~':
+ /* this may be followed by an override time value */
+ GETNUM(cp, timeval);
+ if (timeval <= 0 || timeval > MIN_VALUE)
+ timeval = value;
+ for (sustain = 0; cp[1] == '.'; cp++)
+ {
+ slen--;
+ sustain++;
+ }
+ playtone(-1, timeval, sustain);
+ break;
+
+ case 'T':
+ GETNUM(cp, tempo);
+ if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
+ tempo = DFLT_TEMPO;
+ whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
+ break;
+
+ case 'M':
+ if (cp[1] == 'N' || cp[1] == 'n')
+ {
+ fill = NORMAL;
+ ++cp;
+ slen--;
+ }
+ else if (cp[1] == 'L' || cp[1] == 'l')
+ {
+ fill = LEGATO;
+ ++cp;
+ slen--;
+ }
+ else if (cp[1] == 'S' || cp[1] == 's')
+ {
+ fill = STACCATO;
+ ++cp;
+ slen--;
+ }
+ break;
+ }
+ }
+}
+
+/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
+ *
+ * This section implements driver hooks to run playstring() and the tone(),
+ * endtone(), and rest() functions defined above.
+ */
+
+static int spkr_active = FALSE; /* exclusion flag */
+static struct buf *spkr_inbuf; /* incoming buf */
+
+int spkropen(dev)
+dev_t dev;
+{
+#ifdef DEBUG
+ (void) printf("spkropen: entering with dev = %x\n", dev);
+#endif /* DEBUG */
+
+ if (minor(dev) != 0)
+ return(ENXIO);
+ else if (spkr_active)
+ return(EBUSY);
+ else
+ {
+#ifdef DEBUG
+ (void) printf("spkropen: about to perform play initialization\n");
+#endif /* DEBUG */
+ playinit();
+ spkr_inbuf = geteblk(DEV_BSIZE);
+ spkr_active = TRUE;
+ return(0);
+ }
+}
+
+int spkrwrite(dev, uio)
+dev_t dev;
+struct uio *uio;
+{
+#ifdef DEBUG
+ printf("spkrwrite: entering with dev = %x, count = %d\n",
+ dev, uio->uio_resid);
+#endif /* DEBUG */
+
+ if (minor(dev) != 0)
+ return(ENXIO);
+ else if (uio->uio_resid > DEV_BSIZE) /* prevent system crashes */
+ return(E2BIG);
+ else
+ {
+ unsigned n;
+ char *cp;
+ int error;
+
+ n = uio->uio_resid;
+ cp = spkr_inbuf->b_un.b_addr;
+ if (!(error = uiomove(cp, n, uio)))
+ playstring(cp, n);
+ return(error);
+ }
+}
+
+int spkrclose(dev)
+dev_t dev;
+{
+#ifdef DEBUG
+ (void) printf("spkrclose: entering with dev = %x\n", dev);
+#endif /* DEBUG */
+
+ if (minor(dev) != 0)
+ return(ENXIO);
+ else
+ {
+ wakeup((caddr_t)&endtone);
+ wakeup((caddr_t)&endrest);
+ brelse(spkr_inbuf);
+ spkr_active = FALSE;
+ return(0);
+ }
+}
+
+int spkrioctl(dev, cmd, cmdarg)
+dev_t dev;
+int cmd;
+caddr_t cmdarg;
+{
+#ifdef DEBUG
+ (void) printf("spkrioctl: entering with dev = %x, cmd = %x\n");
+#endif /* DEBUG */
+
+ if (minor(dev) != 0)
+ return(ENXIO);
+ else if (cmd == SPKRTONE)
+ {
+ tone_t *tp = (tone_t *)cmdarg;
+
+ if (tp->frequency == 0)
+ rest(tp->duration);
+ else
+ tone(tp->frequency, tp->duration);
+ return 0;
+ }
+ else if (cmd == SPKRTUNE)
+ {
+ tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg);
+ tone_t ttp;
+ int error;
+
+ for (; ; tp++) {
+ error = copyin(tp, &ttp, sizeof(tone_t));
+ if (error)
+ return(error);
+ if (ttp.duration == 0)
+ break;
+ if (ttp.frequency == 0)
+ rest(ttp.duration);
+ else
+ tone(ttp.frequency, ttp.duration);
+ }
+ return(0);
+ }
+ return(EINVAL);
+}
+
+#endif /* NSPEAKER > 0 */
+/* spkr.c ends here */
diff --git a/sys/dev/syscons/syscons.c b/sys/dev/syscons/syscons.c
new file mode 100644
index 0000000..a2a931c
--- /dev/null
+++ b/sys/dev/syscons/syscons.c
@@ -0,0 +1,2660 @@
+/*-
+ * Copyright (c) 1992-1994 Søren Schmidt
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz and Don Ahn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from:@(#)syscons.c 1.3 940129
+ * $Id: syscons.c,v 1.46 1994/05/25 08:59:56 rgrimes Exp $
+ *
+ */
+
+#if !defined(__FreeBSD__)
+#define FAT_CURSOR
+#endif
+
+#include "param.h"
+#include <sys/systm.h>
+#include "conf.h"
+#include "ioctl.h"
+#include "proc.h"
+#include "user.h"
+#include "tty.h"
+#include "uio.h"
+#include "callout.h"
+#include "kernel.h"
+#include "syslog.h"
+#include "errno.h"
+#include "malloc.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/timerreg.h"
+#include "i386/i386/cons.h"
+#include "machine/console.h"
+#include "machine/psl.h"
+#include "machine/frame.h"
+#include "machine/pc/display.h"
+#include "iso8859.font"
+#include "kbdtables.h"
+#include "sc.h"
+
+#if NSC > 0
+
+#if !defined(NCONS)
+#define NCONS 12
+#endif
+
+/* status flags */
+#define LOCK_KEY_MASK 0x0000F
+#define LED_MASK 0x00007
+#define UNKNOWN_MODE 0x00010
+#define KBD_RAW_MODE 0x00020
+#define SWITCH_WAIT_REL 0x00040
+#define SWITCH_WAIT_ACQ 0x00080
+
+/* video hardware memory addresses */
+#define VIDEOMEM 0x000A0000
+
+/* misc defines */
+#define MAX_ESC_PAR 3
+#define TEXT80x25 1
+#define TEXT80x50 2
+#define COL 80
+#define ROW 25
+#define BELL_DURATION 5
+#define BELL_PITCH 800
+#define TIMER_FREQ 1193182 /* should be in isa.h */
+#define PCBURST 256
+
+/* defines related to hardware addresses */
+#define MONO_BASE 0x3B4 /* crt controller base mono */
+#define COLOR_BASE 0x3D4 /* crt controller base color */
+#define ATC IO_VGA+0x00 /* attribute controller */
+#define TSIDX IO_VGA+0x04 /* timing sequencer idx */
+#define TSREG IO_VGA+0x05 /* timing sequencer data */
+#define PIXMASK IO_VGA+0x06 /* pixel write mask */
+#define PALRADR IO_VGA+0x07 /* palette read address */
+#define PALWADR IO_VGA+0x08 /* palette write address */
+#define PALDATA IO_VGA+0x09 /* palette data register */
+#define GDCIDX IO_VGA+0x0E /* graph data controller idx */
+#define GDCREG IO_VGA+0x0F /* graph data controller data */
+
+/* special characters */
+#define cntlc 0x03
+#define cntld 0x04
+#define bs 0x08
+#define lf 0x0a
+#define cr 0x0d
+#define del 0x7f
+
+typedef struct term_stat {
+ int esc; /* processing escape sequence */
+ int num_param; /* # of parameters to ESC */
+ int last_param; /* last parameter # */
+ int param[MAX_ESC_PAR]; /* contains ESC parameters */
+ int cur_attr; /* current attributes */
+ int std_attr; /* normal attributes */
+ int rev_attr; /* reverse attributes */
+} term_stat;
+
+typedef struct scr_stat {
+ u_short *crt_base; /* address of screen memory */
+ u_short *scr_buf; /* buffer when off screen */
+ u_short *crtat; /* cursor address */
+ int xpos; /* current X position */
+ int ypos; /* current Y position */
+ int xsize; /* X size */
+ int ysize; /* Y size */
+ term_stat term; /* terminal emulation stuff */
+ char cursor_start; /* cursor start line # */
+ char cursor_end; /* cursor end line # */
+ u_char border; /* border color */
+ u_short bell_duration;
+ u_short bell_pitch;
+ u_short status; /* status (bitfield) */
+ u_short mode; /* mode */
+ pid_t pid; /* pid of controlling proc */
+ struct proc *proc; /* proc* of controlling proc */
+ struct vt_mode smode; /* switch mode */
+} scr_stat;
+
+typedef struct default_attr {
+ int std_attr; /* normal attributes */
+ int rev_attr; /* reverse attributes */
+} default_attr;
+
+static default_attr user_default = {
+ (FG_LIGHTGREY | BG_BLACK) << 8,
+ (FG_BLACK | BG_LIGHTGREY) << 8
+};
+
+static default_attr kernel_default = {
+ (FG_WHITE | BG_BLACK) << 8,
+ (FG_BLACK | BG_LIGHTGREY) << 8
+};
+
+#define CONSOLE_BUFFER_SIZE 1024
+int console_buffer_count;
+char console_buffer[CONSOLE_BUFFER_SIZE];
+
+static scr_stat console[NCONS];
+static scr_stat *cur_console = &console[0];
+static scr_stat *new_scp, *old_scp;
+static term_stat kernel_console;
+static default_attr *current_default;
+static int switch_in_progress = 0;
+static u_short *crtat = 0;
+static u_int crtc_addr = MONO_BASE;
+static char crtc_vga = 0;
+static u_char shfts = 0, ctls = 0, alts = 0, agrs = 0, metas = 0;
+static u_char nlkcnt = 0, clkcnt = 0, slkcnt = 0, alkcnt = 0;
+static char palette[3*256];
+static const u_int n_fkey_tab = sizeof(fkey_tab) / sizeof(*fkey_tab);
+static int cur_cursor_pos = -1;
+static char in_putc = 0;
+static char polling = 0;
+static int delayed_next_scr;
+static char saved_console = -1; /* saved console number */
+static long scrn_blank_time = 0; /* screen saver timout value */
+static int scrn_blanked = 0; /* screen saver active flag */
+static int scrn_saver = 0; /* screen saver routine */
+static long scrn_time_stamp;
+static u_char scr_map[256];
+extern int hz;
+extern struct timeval time;
+
+/* function prototypes */
+int pcprobe(struct isa_device *dev);
+int pcattach(struct isa_device *dev);
+int pcopen(dev_t dev, int flag, int mode, struct proc *p);
+int pcclose(dev_t dev, int flag, int mode, struct proc *p);
+int pcread(dev_t dev, struct uio *uio, int flag);
+int pcwrite(dev_t dev, struct uio *uio, int flag);
+int pcparam(struct tty *tp, struct termios *t);
+int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p);
+void pcxint(dev_t dev);
+void pcstart(struct tty *tp);
+void pccnprobe(struct consdev *cp);
+void pccninit(struct consdev *cp);
+void pccnputc(dev_t dev, char c);
+int pccngetc(dev_t dev);
+void scintr(int unit);
+int pcmmap(dev_t dev, int offset, int nprot);
+u_int sgetc(int noblock);
+int getchar(void);
+static void scinit(void);
+static void scput(u_char c);
+static u_int scgetc(int noblock);
+static struct tty *get_tty_ptr(dev_t dev);
+static scr_stat *get_scr_stat(dev_t dev);
+static int get_scr_num();
+static void cursor_shape(int start, int end);
+static void get_cursor_shape(int *start, int *end);
+static void cursor_pos(int force);
+static void clear_screen(scr_stat *scp);
+static int switch_scr(u_int next_scr);
+static void exchange_scr(void);
+static void move_crsr(scr_stat *scp, int x, int y);
+static void move_up(u_short *s, u_short *d, u_int len);
+static void move_down(u_short *s, u_short *d, u_int len);
+static void scan_esc(scr_stat *scp, u_char c);
+static void ansi_put(scr_stat *scp, u_char c);
+static u_char *get_fstr(u_int c, u_int *len);
+static void update_leds(int which);
+static void kbd_wait(void);
+static void kbd_cmd(u_char command);
+static void kbd_cmd2(u_char command, u_char arg);
+static int kbd_reply(void);
+static void set_mode(scr_stat *scp);
+static void set_border(int color);
+static void load_font(int segment, int size, char* font);
+static void save_palette(void);
+static void load_palette(void);
+static void change_winsize(struct tty *tp, int x, int y);
+
+
+/* available screen savers */
+
+static void none_saver(int test);
+static void blank_saver(int test);
+static void fade_saver(int test);
+static void star_saver(int test);
+static void snake_saver(int test);
+
+static const struct {
+ char *name;
+ void (*routine)();
+} screen_savers[] = {
+ { "none", none_saver }, /* 0 */
+ { "blank", blank_saver }, /* 1 */
+ { "fade", fade_saver }, /* 2 */
+ { "star", star_saver }, /* 3 */
+ { "snake", snake_saver }, /* 4 */
+};
+#define SCRN_SAVER(arg) (*screen_savers[scrn_saver].routine)(arg)
+#define NUM_SCRN_SAVERS (sizeof(screen_savers) / sizeof(screen_savers[0]))
+
+/* OS specific stuff */
+
+#if defined(NetBSD)
+#define VIRTUAL_TTY(x) pc_tty[x] ? (pc_tty[x]) : (pc_tty[x] = ttymalloc())
+#define CONSOLE_TTY pc_tty[NCONS] ? (pc_tty[NCONS]) : (pc_tty[NCONS] = ttymalloc())
+#define frametype struct trapframe
+#define eflags tf_eflags
+extern u_short *Crtat;
+struct tty *pc_tty[NCONS+1];
+int ttrstrt();
+#endif
+
+#if defined(__FreeBSD__)
+#if 0
+#define VIRTUAL_TTY(x) (pccons[x] = ttymalloc(pccons[x]))
+#define CONSOLE_TTY (pccons[NCONS] = ttymalloc(pccons[NCONS]))
+struct tty *pccons[NCONS+1];
+#else
+#define VIRTUAL_TTY(x) &pccons[x]
+#define CONSOLE_TTY &pccons[NCONS]
+struct tty pccons[NCONS+1];
+#endif
+#define timeout_t timeout_func_t
+#define frametype struct trapframe
+#define eflags tf_eflags
+#define MONO_BUF (KERNBASE+0xB0000)
+#define CGA_BUF (KERNBASE+0xB8000)
+#endif
+
+#if defined(__386BSD__) && !defined(__FreeBSD__)
+#define VIRTUAL_TTY(x) &pccons[x]
+#define CONSOLE_TTY &pccons[NCONS]
+#define frametype struct syscframe
+#define eflags sf_eflags
+#define timeout_t caddr_t
+#define MONO_BUF (0xFE0B0000)
+#define CGA_BUF (0xFE0B8000)
+struct tty pccons[NCONS+1];
+#endif
+
+#if defined(__386BSD__) || defined(__FreeBSD__)
+u_short *Crtat = (u_short *)MONO_BUF;
+void consinit(void) {scinit();}
+#include "ddb.h"
+#if NDDB > 0
+#define DDB 1
+#endif
+#endif
+
+struct isa_driver scdriver = {
+ pcprobe, pcattach, "sc",
+};
+
+
+int pcprobe(struct isa_device *dev)
+{
+ /* Enable interrupts and keyboard controller */
+ kbd_wait();
+ outb(KB_STAT, KB_WRITE);
+ kbd_cmd(0x4D);
+
+ /* Start keyboard stuff RESET */
+ for (;;) {
+ kbd_cmd(KB_RESET);
+ if (kbd_reply() == KB_ACK && /* command accepted */
+ kbd_reply() == 0xaa) /* self test passed */
+ break;
+ printf("Keyboard reset failed\n");
+ }
+ return (IO_KBDSIZE);
+}
+
+
+int pcattach(struct isa_device *dev)
+{
+ scr_stat *scp;
+ int start = -1, end = -1, i;
+
+ printf("sc%d: ", dev->id_unit);
+ if (crtc_vga)
+ if (crtc_addr == MONO_BASE)
+ printf("VGA mono");
+ else
+ printf("VGA color");
+ else
+ if (crtc_addr == MONO_BASE)
+ printf("MDA/hercules");
+ else
+ printf("CGA/EGA");
+
+ if (NCONS > 1)
+ printf(" <%d virtual consoles>\n", NCONS);
+ else
+ printf("\n");
+#if defined(FAT_CURSOR)
+ start = 0;
+ end = 18;
+ if (crtc_vga) {
+#else
+ if (crtc_vga) {
+ get_cursor_shape(&start, &end);
+#endif
+ save_palette();
+ load_font(0, 16, font_8x16);
+ load_font(1, 8, font_8x8);
+ load_font(2, 14, font_8x14);
+ }
+ current_default = &user_default;
+ for (i = 0; i < NCONS; i++) {
+ scp = &console[i];
+ scp->scr_buf = (u_short *)malloc(COL * ROW * 2, M_DEVBUF, M_NOWAIT);
+ scp->mode = TEXT80x25;
+ scp->term.esc = 0;
+ scp->term.std_attr = current_default->std_attr;
+ scp->term.rev_attr = current_default->rev_attr;
+ scp->term.cur_attr = scp->term.std_attr;
+ scp->border = BG_BLACK;
+ scp->cursor_start = start;
+ scp->cursor_end = end;
+ scp->xsize = COL;
+ scp->ysize = ROW;
+ scp->bell_pitch = BELL_PITCH;
+ scp->bell_duration = BELL_DURATION;
+ scp->status = 0;
+ scp->pid = 0;
+ scp->proc = NULL;
+ scp->smode.mode = VT_AUTO;
+ if (i > 0) {
+ scp->crt_base = scp->crtat = scp->scr_buf;
+ fillw(scp->term.cur_attr|scr_map[0x20], scp->scr_buf, COL*ROW);
+ }
+ }
+ /* get cursor going */
+#if defined(FAT_CURSOR)
+ cursor_shape(console[0].cursor_start,
+ console[0].cursor_end);
+#endif
+ cursor_pos(1);
+ return 0;
+}
+
+
+static struct tty *get_tty_ptr(dev_t dev)
+{
+ int unit = minor(dev);
+
+ if (unit > NCONS)
+ return(NULL);
+ if (unit == NCONS)
+ return(CONSOLE_TTY);
+ return(VIRTUAL_TTY(unit));
+}
+
+
+static scr_stat *get_scr_stat(dev_t dev)
+{
+ int unit = minor(dev);
+
+ if (unit > NCONS)
+ return(NULL);
+ if (unit == NCONS)
+ return(&console[0]);
+ return(&console[unit]);
+}
+
+
+static int get_scr_num()
+{
+ int i = 0;
+
+ while ((i < NCONS) && (cur_console != &console[i])) i++;
+ return i < NCONS ? i : 0;
+}
+
+int pcopen(dev_t dev, int flag, int mode, struct proc *p)
+{
+ struct tty *tp = get_tty_ptr(dev);
+
+ if (!tp)
+ return(ENXIO);
+
+ tp->t_oproc = pcstart;
+ tp->t_param = pcparam;
+ tp->t_dev = dev;
+ if (!(tp->t_state & TS_ISOPEN)) {
+ tp->t_state |= TS_WOPEN;
+ ttychars(tp);
+ tp->t_iflag = TTYDEF_IFLAG;
+ tp->t_oflag = TTYDEF_OFLAG;
+ tp->t_cflag = TTYDEF_CFLAG;
+ tp->t_lflag = TTYDEF_LFLAG;
+ tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
+ pcparam(tp, &tp->t_termios);
+ ttsetwater(tp);
+ } else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0)
+ return(EBUSY);
+ tp->t_state |= TS_CARR_ON;
+ tp->t_cflag |= CLOCAL;
+ return((*linesw[tp->t_line].l_open)(dev, tp));
+}
+
+
+int pcclose(dev_t dev, int flag, int mode, struct proc *p)
+{
+ struct tty *tp = get_tty_ptr(dev);
+ struct scr_stat *scp;
+
+ if (!tp)
+ return(ENXIO);
+ if (minor(dev) < NCONS) {
+ scp = get_scr_stat(tp->t_dev);
+ if (scp->status & SWITCH_WAIT_ACQ)
+ wakeup((caddr_t)&scp->smode);
+ scp->pid = 0;
+ scp->proc = NULL;
+ scp->smode.mode = VT_AUTO;
+ }
+ (*linesw[tp->t_line].l_close)(tp, flag);
+ ttyclose(tp);
+ return(0);
+}
+
+
+int pcread(dev_t dev, struct uio *uio, int flag)
+{
+ struct tty *tp = get_tty_ptr(dev);
+
+ if (!tp)
+ return(ENXIO);
+ return((*linesw[tp->t_line].l_read)(tp, uio, flag));
+}
+
+
+int pcwrite(dev_t dev, struct uio *uio, int flag)
+{
+ struct tty *tp = get_tty_ptr(dev);
+
+ if (!tp)
+ return(ENXIO);
+ return((*linesw[tp->t_line].l_write)(tp, uio, flag));
+}
+
+
+/*
+ * Got a console interrupt, keyboard action !
+ * Catch the character, and see who it goes to.
+ */
+void scintr(int unit)
+{
+ static struct tty *cur_tty;
+ int c, len;
+ u_char *cp;
+
+ /* make screensaver happy */
+ scrn_time_stamp = time.tv_sec;
+ if (scrn_blanked)
+ SCRN_SAVER(0);
+
+ c = scgetc(1);
+
+ cur_tty = VIRTUAL_TTY(get_scr_num());
+ if (!(cur_tty->t_state & TS_ISOPEN))
+ cur_tty = CONSOLE_TTY;
+
+ if (!(cur_tty->t_state & TS_ISOPEN) || polling)
+ return;
+
+ switch (c & 0xff00) {
+ case 0x0000: /* normal key */
+ (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty);
+ break;
+ case NOKEY: /* nothing there */
+ break;
+ case FKEY: /* function key, return string */
+ if (cp = get_fstr((u_int)c, (u_int *)&len)) {
+ while (len-- > 0)
+ (*linesw[cur_tty->t_line].l_rint)
+ (*cp++ & 0xFF, cur_tty);
+ }
+ break;
+ case MKEY: /* meta is active, prepend ESC */
+ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty);
+ (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty);
+ break;
+ }
+}
+
+
+/*
+ * Set line parameters
+ */
+int pcparam(struct tty *tp, struct termios *t)
+{
+ int cflag = t->c_cflag;
+
+ /* and copy to tty */
+ tp->t_ispeed = t->c_ispeed;
+ tp->t_ospeed = t->c_ospeed;
+ tp->t_cflag = cflag;
+ return 0;
+}
+
+
+int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
+{
+ int i, error;
+ struct tty *tp;
+ frametype *fp;
+ scr_stat *scp;
+
+ tp = get_tty_ptr(dev);
+ if (!tp)
+ return ENXIO;
+ scp = get_scr_stat(tp->t_dev);
+
+ switch (cmd) { /* process console hardware related ioctl's */
+
+ case CONS_BLANKTIME: /* set screen saver timeout (0 = no saver) */
+ scrn_blank_time = *(int*)data;
+ return 0;
+ case CONS_SSAVER: /* set screen saver */
+ {
+ register ssaver_t *sav = (ssaver_t *)data;
+ if (sav->num < 0 || sav->num >= NUM_SCRN_SAVERS)
+ return EIO;
+ SCRN_SAVER(0);
+ scrn_saver = sav->num;
+ scrn_blank_time = sav->time;
+ return 0;
+ }
+ case CONS_GSAVER: /* get screen saver info */
+ {
+ register ssaver_t *sav = (ssaver_t *)data;
+ if (sav->num < 0)
+ sav->num = scrn_saver;
+ else if (sav->num >= NUM_SCRN_SAVERS)
+ return EIO;
+ sav->time = scrn_blank_time;
+ strcpy(sav->name, screen_savers[sav->num].name);
+ return 0;
+ }
+ case CONS_80x25TEXT: /* set 80x25 text mode */
+ if (!crtc_vga)
+ return ENXIO;
+ scp->mode = TEXT80x25;
+ scp->ysize = 25;
+ free(scp->scr_buf, M_DEVBUF);
+ scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*2,
+ M_DEVBUF, M_NOWAIT);
+ if (scp != cur_console)
+ scp->crt_base = scp->scr_buf;
+ set_mode(scp);
+ clear_screen(scp);
+ change_winsize(tp, scp->xsize, scp->ysize);
+ return 0;
+
+ case CONS_80x50TEXT: /* set 80x50 text mode */
+ if (!crtc_vga)
+ return ENXIO;
+ scp->mode = TEXT80x50;
+ scp->ysize = 50;
+ free(scp->scr_buf, M_DEVBUF);
+ scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*2,
+ M_DEVBUF, M_NOWAIT);
+ if (scp != cur_console)
+ scp->crt_base = scp->scr_buf;
+ set_mode(scp);
+ clear_screen(scp);
+ change_winsize(tp, scp->xsize, scp->ysize);
+ return 0;
+
+ case CONS_GETVERS: /* get version number */
+ *(int*)data = 0x103; /* version 1.3 */
+ return 0;
+
+ case CONS_GETINFO: /* get current (virtual) console info */
+ {
+ vid_info_t *ptr = (vid_info_t*)data;
+ if (ptr->size == sizeof(struct vid_info)) {
+ ptr->m_num = get_scr_num();
+ ptr->mv_col = scp->xpos;
+ ptr->mv_row = scp->ypos;
+ ptr->mv_csz = scp->xsize;
+ ptr->mv_rsz = scp->ysize;
+ ptr->mv_norm.fore = (scp->term.std_attr & 0x0f00)>>8;
+ ptr->mv_norm.back = (scp->term.std_attr & 0xf000)>>12;
+ ptr->mv_rev.fore = (scp->term.rev_attr & 0x0f00)>>8;
+ ptr->mv_rev.back = (scp->term.rev_attr & 0xf000)>>12;
+ ptr->mv_grfc.fore = 0; /* not supported */
+ ptr->mv_grfc.back = 0; /* not supported */
+ ptr->mv_ovscan = scp->border;
+ ptr->mk_keylock = scp->status & LOCK_KEY_MASK;
+ return 0;
+ }
+ return EINVAL;
+ }
+
+ case VT_SETMODE: /* set screen switcher mode */
+ bcopy(data, &scp->smode, sizeof(struct vt_mode));
+ if (scp->smode.mode == VT_PROCESS) {
+ scp->proc = p;
+ scp->pid = scp->proc->p_pid;
+ }
+ return 0;
+
+ case VT_GETMODE: /* get screen switcher mode */
+ bcopy(&scp->smode, data, sizeof(struct vt_mode));
+ return 0;
+
+ case VT_RELDISP: /* screen switcher ioctl */
+ switch(*data) {
+ case VT_FALSE: /* user refuses to release screen, abort */
+ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) {
+ old_scp->status &= ~SWITCH_WAIT_REL;
+ switch_in_progress = 0;
+ return 0;
+ }
+ return EINVAL;
+
+ case VT_TRUE: /* user has released screen, go on */
+ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) {
+ scp->status &= ~SWITCH_WAIT_REL;
+ exchange_scr();
+ if (new_scp->smode.mode == VT_PROCESS) {
+ new_scp->status |= SWITCH_WAIT_ACQ;
+ psignal(new_scp->proc,
+ new_scp->smode.acqsig);
+ }
+ else
+ switch_in_progress = 0;
+ return 0;
+ }
+ return EINVAL;
+
+ case VT_ACKACQ: /* acquire acknowledged, switch completed */
+ if (scp == new_scp && (scp->status & SWITCH_WAIT_ACQ)) {
+ scp->status &= ~SWITCH_WAIT_ACQ;
+ switch_in_progress = 0;
+ return 0;
+ }
+ return EINVAL;
+
+ default:
+ return EINVAL;
+ }
+ /* NOT REACHED */
+
+ case VT_OPENQRY: /* return free virtual console */
+ for (i = 0; i < NCONS; i++) {
+ tp = VIRTUAL_TTY(i);
+ if (!(tp->t_state & TS_ISOPEN)) {
+ *data = i + 1;
+ return 0;
+ }
+ }
+ return EINVAL;
+
+ case VT_ACTIVATE: /* switch to screen *data */
+ return switch_scr((*data) - 1);
+
+ case VT_WAITACTIVE: /* wait for switch to occur */
+ if (*data > NCONS)
+ return EINVAL;
+ if (minor(dev) == (*data) - 1)
+ return 0;
+ if (*data == 0) {
+ if (scp == cur_console)
+ return 0;
+ while ((error=tsleep((caddr_t)&scp->smode,
+ PZERO|PCATCH, "waitvt", 0)) == ERESTART) ;
+ }
+ else
+ while ((error=tsleep(
+ (caddr_t)&console[*(data-1)].smode,
+ PZERO|PCATCH, "waitvt", 0)) == ERESTART) ;
+ return error;
+
+ case VT_GETACTIVE:
+ *data = get_scr_num()+1;
+ return 0;
+
+ case KDENABIO: /* allow io operations */
+ fp = (frametype *)p->p_md.md_regs;
+ fp->eflags |= PSL_IOPL;
+ return 0;
+
+ case KDDISABIO: /* disallow io operations (default) */
+ fp = (frametype *)p->p_md.md_regs;
+ fp->eflags &= ~PSL_IOPL;
+ return 0;
+
+ case KDSETMODE: /* set current mode of this (virtual) console */
+ switch (*data) {
+ case KD_TEXT: /* switch to TEXT (known) mode */
+ /* restore fonts & palette ! */
+ if (crtc_vga) {
+ load_font(0, 16, font_8x16);
+ load_font(1, 8, font_8x8);
+ load_font(2, 14, font_8x14);
+ load_palette();
+ }
+ /* FALL THROUGH */
+
+ case KD_TEXT1: /* switch to TEXT (known) mode */
+ /* no restore fonts & palette */
+ scp->status &= ~UNKNOWN_MODE;
+ set_mode(scp);
+ clear_screen(scp);
+ return 0;
+
+ case KD_GRAPHICS:/* switch to GRAPHICS (unknown) mode */
+ scp->status |= UNKNOWN_MODE;
+ return 0;
+ default:
+ return EINVAL;
+ }
+ /* NOT REACHED */
+
+ case KDGETMODE: /* get current mode of this (virtual) console */
+ *data = (scp->status & UNKNOWN_MODE) ? KD_GRAPHICS : KD_TEXT;
+ return 0;
+
+ case KDSBORDER: /* set border color of this (virtual) console */
+ if (!crtc_vga)
+ return ENXIO;
+ scp->border = *data;
+ if (scp == cur_console)
+ set_border(scp->border);
+ return 0;
+
+ case KDSKBSTATE: /* set keyboard state (locks) */
+ if (*data >= 0 && *data <= LOCK_KEY_MASK) {
+ scp->status &= ~LOCK_KEY_MASK;
+ scp->status |= *data;
+ if (scp == cur_console)
+ update_leds(scp->status);
+ return 0;
+ }
+ return EINVAL;
+
+ case KDGKBSTATE: /* get keyboard state (locks) */
+ *data = scp->status & LOCK_KEY_MASK;
+ return 0;
+
+ case KDSETRAD: /* set keyboard repeat & delay rates */
+ if (*data & 0x80)
+ return EINVAL;
+ kbd_cmd2(KB_SETRAD, *data);
+ return 0;
+
+ case KDSKBMODE: /* set keyboard mode */
+ switch (*data) {
+ case K_RAW: /* switch to RAW scancode mode */
+ scp->status |= KBD_RAW_MODE;
+ return 0;
+
+ case K_XLATE: /* switch to XLT ascii mode */
+ if (scp == cur_console && scp->status == KBD_RAW_MODE)
+ shfts = ctls = alts = agrs = metas = 0;
+ scp->status &= ~KBD_RAW_MODE;
+ return 0;
+ default:
+ return EINVAL;
+ }
+ /* NOT REACHED */
+
+ case KDGKBMODE: /* get keyboard mode */
+ *data = (scp->status & KBD_RAW_MODE) ? K_RAW : K_XLATE;
+ return 0;
+
+ case KDMKTONE: /* sound the bell */
+ if (scp == cur_console)
+ sysbeep(scp->bell_pitch, scp->bell_duration);
+ return 0;
+
+ case KIOCSOUND: /* make tone (*data) hz */
+ if (scp == cur_console) {
+ if (*(int*)data) {
+ int pitch = TIMER_FREQ/(*(int*)data);
+ /* set command for counter 2, 2 byte write */
+ if (acquire_timer2(TIMER_16BIT|TIMER_SQWAVE)) {
+ return EBUSY;
+ }
+ /* set pitch */
+ outb(TIMER_CNTR2, pitch);
+ outb(TIMER_CNTR2, (pitch>>8));
+ /* enable counter 2 output to speaker */
+ outb(IO_PPI, inb(IO_PPI) | 3);
+ }
+ else {
+ /* disable counter 2 output to speaker */
+ outb(IO_PPI, inb(IO_PPI) & 0xFC);
+ release_timer2();
+ }
+ }
+ return 0;
+
+ case KDGKBTYPE: /* get keyboard type */
+ *data = 0; /* type not known (yet) */
+ return 0;
+
+ case KDSETLED: /* set keyboard LED status */
+ if (*data >= 0 && *data <= LED_MASK) {
+ scp->status &= ~LED_MASK;
+ scp->status |= *data;
+ if (scp == cur_console)
+ update_leds(scp->status);
+ return 0;
+ }
+ return EINVAL;
+
+ case KDGETLED: /* get keyboard LED status */
+ *data = scp->status & LED_MASK;
+ return 0;
+
+ case GETFKEY: /* get functionkey string */
+ if (*(u_short*)data < n_fkey_tab) {
+ fkeyarg_t *ptr = (fkeyarg_t*)data;
+ bcopy(&fkey_tab[ptr->keynum].str,
+ ptr->keydef,
+ fkey_tab[ptr->keynum].len);
+ ptr->flen = fkey_tab[ptr->keynum].len;
+ return 0;
+ }
+ else
+ return EINVAL;
+
+ case SETFKEY: /* set functionkey string */
+ if (*(u_short*)data < n_fkey_tab) {
+ fkeyarg_t *ptr = (fkeyarg_t*)data;
+ bcopy(ptr->keydef,
+ &fkey_tab[ptr->keynum].str,
+ min(ptr->flen, MAXFK));
+ fkey_tab[ptr->keynum].len = min(ptr->flen, MAXFK);
+ return 0;
+ }
+ else
+ return EINVAL;
+
+ case GIO_SCRNMAP: /* get output translation table */
+ bcopy(&scr_map, data, sizeof(scr_map));
+ return 0;
+
+ case PIO_SCRNMAP: /* set output translation table */
+ bcopy(data, &scr_map, sizeof(scr_map));
+ return 0;
+
+ case GIO_KEYMAP: /* get keyboard translation table */
+ bcopy(&key_map, data, sizeof(key_map));
+ return 0;
+
+ case PIO_KEYMAP: /* set keyboard translation table */
+ bcopy(data, &key_map, sizeof(key_map));
+ return 0;
+
+ case PIO_FONT8x8: /* set 8x8 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(data, &font_8x8, sizeof(font_8x8));
+ load_font(1, 8, font_8x8);
+ return 0;
+
+ case GIO_FONT8x8: /* get 8x8 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(&font_8x8, data, sizeof(font_8x8));
+ return 0;
+
+ case PIO_FONT8x14: /* set 8x14 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(data, &font_8x14, sizeof(font_8x14));
+ load_font(2, 14, font_8x14);
+ return 0;
+
+ case GIO_FONT8x14: /* get 8x14 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(&font_8x14, data, sizeof(font_8x14));
+ return 0;
+
+ case PIO_FONT8x16: /* set 8x16 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(data, &font_8x16, sizeof(font_8x16));
+ load_font(0, 16, font_8x16);
+ return 0;
+
+ case GIO_FONT8x16: /* get 8x16 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(&font_8x16, data, sizeof(font_8x16));
+ return 0;
+
+ case CONSOLE_X_MODE_ON: /* just to be compatible */
+ if (saved_console < 0) {
+ saved_console = get_scr_num();
+ switch_scr(minor(dev));
+ fp = (frametype *)p->p_md.md_regs;
+ fp->eflags |= PSL_IOPL;
+ scp->status |= UNKNOWN_MODE;
+ scp->status |= KBD_RAW_MODE;
+ return 0;
+ }
+ return EAGAIN;
+
+ case CONSOLE_X_MODE_OFF:/* just to be compatible */
+ fp = (frametype *)p->p_md.md_regs;
+ fp->eflags &= ~PSL_IOPL;
+ if (crtc_vga) {
+ load_font(0, 16, font_8x16);
+ load_font(1, 8, font_8x8);
+ load_font(2, 14, font_8x14);
+ load_palette();
+ }
+ scp->status &= ~UNKNOWN_MODE;
+ set_mode(scp);
+ clear_screen(scp);
+ scp->status &= ~KBD_RAW_MODE;
+ switch_scr(saved_console);
+ saved_console = -1;
+ return 0;
+
+ case CONSOLE_X_BELL: /* more compatibility */
+ /*
+ * if set, data is a pointer to a length 2 array of
+ * integers. data[0] is the pitch in Hz and data[1]
+ * is the duration in msec.
+ */
+ if (data)
+ sysbeep(TIMER_FREQ/((int*)data)[0],
+ ((int*)data)[1]*hz/3000);
+ else
+ sysbeep(scp->bell_pitch, scp->bell_duration);
+ return 0;
+
+ default:
+ break;
+ }
+
+ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
+ if (error >= 0)
+ return(error);
+ error = ttioctl(tp, cmd, data, flag);
+ if (error >= 0)
+ return(error);
+ return(ENOTTY);
+}
+
+
+void pcxint(dev_t dev)
+{
+ struct tty *tp = get_tty_ptr(dev);
+
+ if (!tp)
+ return;
+ tp->t_state &= ~TS_BUSY;
+ if (tp->t_line)
+ (*linesw[tp->t_line].l_start)(tp);
+ else
+ pcstart(tp);
+}
+
+
+void pcstart(struct tty *tp)
+{
+#if defined(NetBSD) || defined(__FreeBSD__)
+ struct clist *rbp;
+ int i, s, len;
+ u_char buf[PCBURST];
+ scr_stat *scp = get_scr_stat(tp->t_dev);
+
+ if (scp->status & SLKED)
+ return;
+ s = spltty(); /* Isn't start always called at spltty? */
+ if (!(tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))) {
+ tp->t_state |= TS_BUSY;
+ splx(s);
+ rbp = &tp->t_outq;
+ while (rbp->c_cc) {
+ len = q_to_b(rbp, buf, PCBURST);
+ for (i=0; i<len; i++)
+ if (buf[i]) ansi_put(scp, buf[i]);
+ }
+ s = spltty();
+ tp->t_state &= ~TS_BUSY;
+#if 0
+ if (rbp->c_cc) {
+ tp->t_state |= TS_TIMEOUT;
+ timeout((timeout_t)ttrstrt, (caddr_t)tp, 1);
+ }
+#endif
+ if (rbp->c_cc <= tp->t_lowat) {
+ if (tp->t_state & TS_ASLEEP) {
+ tp->t_state &= ~TS_ASLEEP;
+ wakeup((caddr_t)rbp);
+ }
+ selwakeup(&tp->t_wsel);
+ }
+ }
+ splx(s);
+
+#else /* __386BSD__ */
+
+ int c, s, len, i;
+ scr_stat *scp = get_scr_stat(tp->t_dev);
+ u_char buf[PCBURST];
+
+ if (scp->status & SLKED)
+ return;
+ s = spltty();
+ if (!(tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))) {
+ for (;;) {
+ if (RB_LEN(tp->t_out) <= tp->t_lowat) {
+ if (tp->t_state & TS_ASLEEP) {
+ tp->t_state &= ~TS_ASLEEP;
+ wakeup((caddr_t)tp->t_out);
+ }
+ selwakeup(&tp->t_wsel);
+ }
+ if (RB_LEN(tp->t_out) == 0)
+ break;
+ if (scp->status & SLKED)
+ break;
+ len = 0;
+ while( len < PCBURST) {
+ buf[len++] = getc(tp->t_out);
+ if( RB_LEN(tp->t_out) == 0)
+ break;
+ }
+ tp->t_state |= TS_BUSY;
+ splx(s);
+ for(i=0;i<len;i++)
+ ansi_put(scp, buf[i]);
+ s = spltty();
+ tp->t_state &= ~TS_BUSY;
+ }
+ tp->t_state |= TS_BUSY;
+ if( in_putc == 0) {
+ int i;
+ for(i=0;i<console_buffer_count;i++) {
+ scput(console_buffer[i]);
+ }
+ console_buffer_count = 0;
+ }
+ tp->t_state &= ~TS_BUSY;
+ }
+ splx(s);
+#endif
+}
+
+
+void pccnprobe(struct consdev *cp)
+{
+ int maj;
+
+ /* locate the major number */
+ for (maj = 0; maj < nchrdev; maj++)
+ if ((void*)cdevsw[maj].d_open == (void*)pcopen)
+ break;
+
+ /* initialize required fields */
+ cp->cn_dev = makedev(maj, NCONS);
+ cp->cn_pri = CN_INTERNAL;
+#if defined(__386BSD__) && !defined(__FreeBSD__)
+ cp->cn_tp = CONSOLE_TTY;
+#endif
+}
+
+
+void pccninit(struct consdev *cp)
+{
+ scinit();
+}
+
+
+void pccnputc(dev_t dev, char c)
+{
+ if (c == '\n')
+ scput('\r');
+ scput(c);
+ if (cur_console == &console[0]) {
+ int pos = cur_console->crtat - cur_console->crt_base;
+ if (pos != cur_cursor_pos) {
+ cur_cursor_pos = pos;
+ outb(crtc_addr,14);
+ outb(crtc_addr+1,pos >> 8);
+ outb(crtc_addr,15);
+ outb(crtc_addr+1,pos&0xff);
+ }
+ }
+}
+
+
+int pccngetc(dev_t dev)
+{
+ int s = spltty(); /* block scintr while we poll */
+ int c = scgetc(0);
+ splx(s);
+ if (c == '\r') c = '\n';
+ return(c);
+}
+
+static void none_saver(int test)
+{
+}
+
+static void fade_saver(int test)
+{
+ static int count = 0;
+ int i;
+
+ if (test) {
+ scrn_blanked = 1;
+ if (count < 64) {
+ outb(PIXMASK, 0xFF); /* no pixelmask */
+ outb(PALWADR, 0x00);
+ outb(PALDATA, 0);
+ outb(PALDATA, 0);
+ outb(PALDATA, 0);
+ for (i = 3; i < 768; i++) {
+ if (palette[i] - count > 15)
+ outb(PALDATA, palette[i]-count);
+ else
+ outb(PALDATA, 15);
+ }
+ inb(crtc_addr+6); /* reset flip/flop */
+ outb(ATC, 0x20); /* enable palette */
+ count++;
+ }
+ }
+ else {
+ count = scrn_blanked = 0;
+ load_palette();
+ }
+}
+
+static void blank_saver(int test)
+{
+ u_char val;
+ if (test) {
+ scrn_blanked = 1;
+ outb(TSIDX, 0x01); val = inb(TSREG);
+ outb(TSIDX, 0x01); outb(TSREG, val | 0x20);
+ }
+ else {
+ scrn_blanked = 0;
+ outb(TSIDX, 0x01); val = inb(TSREG);
+ outb(TSIDX, 0x01); outb(TSREG, val & 0xDF);
+ }
+}
+
+static u_long rand_next = 1;
+
+static int rand()
+{
+ return ((rand_next = rand_next * 1103515245 + 12345) & 0x7FFFFFFF);
+}
+
+/*
+ * Alternate saver that got its inspiration from a well known utility
+ * package for an unfamous OS.
+ */
+
+#define NUM_STARS 50
+
+static void star_saver(int test)
+{
+ scr_stat *scp = cur_console;
+ int cell, i;
+ char pattern[] = {"...........++++*** "};
+ char colors[] = {FG_DARKGREY, FG_LIGHTGREY,
+ FG_WHITE, FG_LIGHTCYAN};
+ static u_short stars[NUM_STARS][2];
+
+ if (test) {
+ if (!scrn_blanked) {
+ bcopy(Crtat, scp->scr_buf,
+ scp->xsize * scp->ysize * 2);
+ fillw((FG_LIGHTGREY|BG_BLACK)<<8 | scr_map[0x20], Crtat,
+ scp->xsize * scp->ysize);
+ set_border(0);
+ i = scp->ysize * scp->xsize + 5;
+ outb(crtc_addr, 14);
+ outb(crtc_addr+1, i >> 8);
+ outb(crtc_addr, 15);
+ outb(crtc_addr+1, i & 0xff);
+ scrn_blanked = 1;
+ for(i=0; i<NUM_STARS; i++) {
+ stars[i][0] =
+ rand() % (scp->xsize*scp->ysize);
+ stars[i][1] = 0;
+ }
+ }
+ cell = rand() % NUM_STARS;
+ *((u_short*)(Crtat + stars[cell][0])) =
+ scr_map[pattern[stars[cell][1]]] |
+ colors[rand()%sizeof(colors)] << 8;
+ if ((stars[cell][1]+=(rand()%4)) >= sizeof(pattern)-1) {
+ stars[cell][0] = rand() % (scp->xsize*scp->ysize);
+ stars[cell][1] = 0;
+ }
+ }
+ else {
+ if (scrn_blanked) {
+ bcopy(scp->scr_buf, Crtat, scp->xsize*scp->ysize*2);
+ cur_cursor_pos = -1;
+ set_border(scp->border);
+ scrn_blanked = 0;
+ }
+ }
+}
+
+
+static void snake_saver(int test)
+{
+ const char saves[] = {"FreeBSD"};
+ static u_char *savs[sizeof(saves)-1];
+ static int dirx, diry;
+ int f;
+ scr_stat *scp = cur_console;
+
+ if (test) {
+ if (!scrn_blanked) {
+ bcopy(Crtat, scp->scr_buf,
+ scp->xsize * scp->ysize * 2);
+ fillw((FG_LIGHTGREY|BG_BLACK)<<8 | scr_map[0x20],
+ Crtat, scp->xsize * scp->ysize);
+ set_border(0);
+ dirx = (scp->xpos ? 1 : -1);
+ diry = (scp->ypos ?
+ scp->xsize : -scp->xsize);
+ for (f=0; f< sizeof(saves)-1; f++)
+ savs[f] = (u_char *)Crtat + 2 *
+ (scp->xpos+scp->ypos*scp->xsize);
+ *(savs[0]) = scr_map[*saves];
+ f = scp->ysize * scp->xsize + 5;
+ outb(crtc_addr, 14);
+ outb(crtc_addr+1, f >> 8);
+ outb(crtc_addr, 15);
+ outb(crtc_addr+1, f & 0xff);
+ scrn_blanked = 1;
+ }
+ if (scrn_blanked++ < 4)
+ return;
+ scrn_blanked = 1;
+ *(savs[sizeof(saves)-2]) = scr_map[0x20];
+ for (f=sizeof(saves)-2; f > 0; f--)
+ savs[f] = savs[f-1];
+ f = (savs[0] - (u_char *)Crtat) / 2;
+ if ((f % scp->xsize) == 0 ||
+ (f % scp->xsize) == scp->xsize - 1 ||
+ (rand() % 50) == 0)
+ dirx = -dirx;
+ if ((f / scp->xsize) == 0 ||
+ (f / scp->xsize) == scp->ysize - 1 ||
+ (rand() % 20) == 0)
+ diry = -diry;
+ savs[0] += 2*dirx + 2*diry;
+ for (f=sizeof(saves)-2; f>=0; f--)
+ *(savs[f]) = scr_map[saves[f]];
+ }
+ else {
+ if (scrn_blanked) {
+ bcopy(scp->scr_buf, Crtat,
+ scp->xsize * scp->ysize * 2);
+ cur_cursor_pos = -1;
+ set_border(scp->border);
+ scrn_blanked = 0;
+ }
+ }
+}
+
+static void cursor_shape(int start, int end)
+{
+ outb(crtc_addr, 10);
+ outb(crtc_addr+1, start & 0xFF);
+ outb(crtc_addr, 11);
+ outb(crtc_addr+1, end & 0xFF);
+}
+
+
+#if !defined(FAT_CURSOR)
+static void get_cursor_shape(int *start, int *end)
+{
+ outb(crtc_addr, 10);
+ *start = inb(crtc_addr+1) & 0x1F;
+ outb(crtc_addr, 11);
+ *end = inb(crtc_addr+1) & 0x1F;
+}
+#endif
+
+
+static void cursor_pos(int force)
+{
+ int pos;
+
+ if (cur_console->status & UNKNOWN_MODE)
+ return;
+ if (scrn_blank_time && (time.tv_sec > scrn_time_stamp+scrn_blank_time))
+ SCRN_SAVER(1);
+ pos = cur_console->crtat - cur_console->crt_base;
+ if (force || (!scrn_blanked && pos != cur_cursor_pos)) {
+ cur_cursor_pos = pos;
+ outb(crtc_addr, 14);
+ outb(crtc_addr+1, pos>>8);
+ outb(crtc_addr, 15);
+ outb(crtc_addr+1, pos&0xff);
+ }
+ timeout((timeout_t)cursor_pos, 0, hz/20);
+}
+
+
+static void clear_screen(scr_stat *scp)
+{
+ move_crsr(scp, 0, 0);
+ fillw(scp->term.cur_attr | scr_map[0x20], scp->crt_base,
+ scp->xsize * scp->ysize);
+}
+
+
+static int switch_scr(u_int next_scr)
+{
+ if (in_putc) { /* delay switch if in putc */
+ delayed_next_scr = next_scr+1;
+ return 0;
+ }
+ if (switch_in_progress &&
+ (cur_console->proc != pfind(cur_console->pid)))
+ switch_in_progress = 0;
+
+ if (next_scr >= NCONS || switch_in_progress) {
+ sysbeep(BELL_PITCH, BELL_DURATION);
+ return EINVAL;
+ }
+
+ /* is the wanted virtual console open ? */
+ if (next_scr) {
+ struct tty *tp = VIRTUAL_TTY(next_scr);
+ if (!(tp->t_state & TS_ISOPEN)) {
+ sysbeep(BELL_PITCH, BELL_DURATION);
+ return EINVAL;
+ }
+ }
+
+ switch_in_progress = 1;
+ old_scp = cur_console;
+ new_scp = &console[next_scr];
+ wakeup((caddr_t)&new_scp->smode);
+ if (new_scp == old_scp) {
+ switch_in_progress = 0;
+ return 0;
+ }
+
+ /* has controlling process died? */
+ if (old_scp->proc && (old_scp->proc != pfind(old_scp->pid)))
+ old_scp->smode.mode = VT_AUTO;
+ if (new_scp->proc && (new_scp->proc != pfind(new_scp->pid)))
+ new_scp->smode.mode = VT_AUTO;
+
+ /* check the modes and switch approbiatly */
+ if (old_scp->smode.mode == VT_PROCESS) {
+ old_scp->status |= SWITCH_WAIT_REL;
+ psignal(old_scp->proc, old_scp->smode.relsig);
+ }
+ else {
+ exchange_scr();
+ if (new_scp->smode.mode == VT_PROCESS) {
+ new_scp->status |= SWITCH_WAIT_ACQ;
+ psignal(new_scp->proc, new_scp->smode.acqsig);
+ }
+ else
+ switch_in_progress = 0;
+ }
+ return 0;
+}
+
+
+static void exchange_scr(void)
+{
+ struct tty *tp;
+
+ bcopy(Crtat, old_scp->scr_buf, old_scp->xsize * old_scp->ysize * 2);
+ old_scp->crt_base = old_scp->scr_buf;
+ move_crsr(old_scp, old_scp->xpos, old_scp->ypos);
+ cur_console = new_scp;
+ set_mode(new_scp);
+ new_scp->crt_base = Crtat;
+ move_crsr(new_scp, new_scp->xpos, new_scp->ypos);
+ bcopy(new_scp->scr_buf, Crtat, new_scp->xsize * new_scp->ysize * 2);
+ update_leds(new_scp->status);
+ if ((old_scp->status & UNKNOWN_MODE) && crtc_vga) {
+ load_font(0, 16, font_8x16);
+ load_font(1, 8, font_8x8);
+ load_font(2, 14, font_8x14);
+ load_palette();
+ }
+ if (old_scp->status & KBD_RAW_MODE || new_scp->status & KBD_RAW_MODE)
+ shfts = ctls = alts = agrs = metas = 0;
+ delayed_next_scr = 0;
+}
+
+
+static void move_crsr(scr_stat *scp, int x, int y)
+{
+ if (x < 0 || y < 0 || x >= scp->xsize || y >= scp->ysize)
+ return;
+ scp->xpos = x;
+ scp->ypos = y;
+ scp->crtat = scp->crt_base + scp->ypos * scp->xsize + scp->xpos;
+}
+
+static void move_up(u_short *s, u_short *d, u_int len)
+{
+ s += len;
+ d += len;
+ while (len-- > 0)
+ *--d = *--s;
+}
+
+static void move_down(u_short *s, u_short *d, u_int len)
+{
+ while (len-- > 0)
+ *d++ = *s++;
+}
+
+static void scan_esc(scr_stat *scp, u_char c)
+{
+ static u_char ansi_col[16] =
+ {0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15};
+ int i, n;
+ u_short *src, *dst, count;
+
+ if (scp->term.esc == 1) {
+ switch (c) {
+
+ case '[': /* Start ESC [ sequence */
+ scp->term.esc = 2;
+ scp->term.last_param = -1;
+ for (i = scp->term.num_param; i < MAX_ESC_PAR; i++)
+ scp->term.param[i] = 1;
+ scp->term.num_param = 0;
+ return;
+
+ case 'M': /* Move cursor up 1 line, scroll if at top */
+ if (scp->ypos > 0)
+ move_crsr(scp, scp->xpos, scp->ypos - 1);
+ else {
+ move_up(scp->crt_base,
+ scp->crt_base + scp->xsize,
+ (scp->ysize - 1) * scp->xsize);
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base, scp->xsize);
+ }
+ break;
+#if notyet
+ case 'Q':
+ scp->term.esc = 4;
+ break;
+#endif
+ case 'c': /* Clear screen & home */
+ clear_screen(scp);
+ break;
+ }
+ }
+ else if (scp->term.esc == 2) {
+ if (c >= '0' && c <= '9') {
+ if (scp->term.num_param < MAX_ESC_PAR) {
+ if (scp->term.last_param != scp->term.num_param) {
+ scp->term.last_param = scp->term.num_param;
+ scp->term.param[scp->term.num_param] = 0;
+ }
+ else
+ scp->term.param[scp->term.num_param] *= 10;
+ scp->term.param[scp->term.num_param] += c - '0';
+ return;
+ }
+ }
+ scp->term.num_param = scp->term.last_param + 1;
+ switch (c) {
+
+ case ';':
+ if (scp->term.num_param < MAX_ESC_PAR)
+ return;
+ break;
+
+ case '=':
+ scp->term.esc = 3;
+ scp->term.last_param = -1;
+ for (i = scp->term.num_param; i < MAX_ESC_PAR; i++)
+ scp->term.param[i] = 1;
+ scp->term.num_param = 0;
+ return;
+
+ case 'A': /* up n rows */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos, scp->ypos - n);
+ break;
+
+ case 'B': /* down n rows */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos, scp->ypos + n);
+ break;
+
+ case 'C': /* right n columns */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos + n, scp->ypos);
+ break;
+
+ case 'D': /* left n columns */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos - n, scp->ypos);
+ break;
+
+ case 'E': /* cursor to start of line n lines down */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, 0, scp->ypos + n);
+ break;
+
+ case 'F': /* cursor to start of line n lines up */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, 0, scp->ypos - n);
+ break;
+
+ case 'f': /* System V consoles .. */
+ case 'H': /* Cursor move */
+ if (scp->term.num_param == 0)
+ move_crsr(scp, 0, 0);
+ else if (scp->term.num_param == 2)
+ move_crsr(scp, scp->term.param[1] - 1,
+ scp->term.param[0] - 1);
+ break;
+
+ case 'J': /* Clear all or part of display */
+ if (scp->term.num_param == 0)
+ n = 0;
+ else
+ n = scp->term.param[0];
+ switch (n) {
+ case 0: /* clear form cursor to end of display */
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crtat, scp->crt_base +
+ scp->xsize * scp->ysize -
+ scp->crtat);
+ break;
+ case 1: /* clear from beginning of display to cursor */
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base,
+ scp->crtat - scp->crt_base);
+ break;
+ case 2: /* clear entire display */
+ clear_screen(scp);
+ break;
+ }
+ break;
+
+ case 'K': /* Clear all or part of line */
+ if (scp->term.num_param == 0)
+ n = 0;
+ else
+ n = scp->term.param[0];
+ switch (n) {
+ case 0: /* clear form cursor to end of line */
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crtat, scp->xsize - scp->xpos);
+ break;
+ case 1: /* clear from beginning of line to cursor */
+ fillw(scp->term.cur_attr|scr_map[0x20],
+ scp->crtat - (scp->xsize - scp->xpos),
+ (scp->xsize - scp->xpos) + 1);
+ break;
+ case 2: /* clear entire line */
+ fillw(scp->term.cur_attr|scr_map[0x20],
+ scp->crtat - (scp->xsize - scp->xpos),
+ scp->xsize);
+ break;
+ }
+ break;
+
+ case 'L': /* Insert n lines */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->ysize - scp->ypos)
+ n = scp->ysize - scp->ypos;
+ src = scp->crt_base + scp->ypos * scp->xsize;
+ dst = src + n * scp->xsize;
+ count = scp->ysize - (scp->ypos + n);
+ move_up(src, dst, count * scp->xsize);
+ fillw(scp->term.cur_attr | scr_map[0x20], src,
+ n * scp->xsize);
+ break;
+
+ case 'M': /* Delete n lines */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->ysize - scp->ypos)
+ n = scp->ysize - scp->ypos;
+ dst = scp->crt_base + scp->ypos * scp->xsize;
+ src = dst + n * scp->xsize;
+ count = scp->ysize - (scp->ypos + n);
+ move_down(src, dst, count * scp->xsize);
+ src = dst + count * scp->xsize;
+ fillw(scp->term.cur_attr | scr_map[0x20], src,
+ n * scp->xsize);
+ break;
+
+ case 'P': /* Delete n chars */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->xsize - scp->xpos)
+ n = scp->xsize - scp->xpos;
+ dst = scp->crtat;
+ src = dst + n;
+ count = scp->xsize - (scp->xpos + n);
+ move_down(src, dst, count);
+ src = dst + count;
+ fillw(scp->term.cur_attr | scr_map[0x20], src, n);
+ break;
+
+ case '@': /* Insert n chars */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->xsize - scp->xpos)
+ n = scp->xsize - scp->xpos;
+ src = scp->crtat;
+ dst = src + n;
+ count = scp->xsize - (scp->xpos + n);
+ move_up(src, dst, count);
+ fillw(scp->term.cur_attr | scr_map[0x20], src, n);
+ break;
+
+ case 'S': /* scroll up n lines */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->ypos)
+ n = scp->ypos;
+ bcopy(scp->crt_base + (scp->xsize * n),
+ scp->crt_base,
+ scp->xsize * (scp->ysize - n) *
+ sizeof(u_short));
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base + scp->xsize *
+ (scp->ysize - 1),
+ scp->xsize);
+ break;
+
+ case 'T': /* scroll down n lines */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->ysize - scp->ypos)
+ n = scp->ysize - scp->ypos;
+ bcopy(scp->crt_base,
+ scp->crt_base + (scp->xsize * n),
+ scp->xsize * (scp->ysize - n) *
+ sizeof(u_short));
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base, scp->xsize);
+ break;
+
+ case 'X': /* delete n characters in line */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->xsize - scp->xpos)
+ n = scp->xsize - scp->xpos;
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base + scp->xpos +
+ ((scp->xsize*scp->ypos) * sizeof(u_short)), n);
+ break;
+
+ case 'Z': /* move n tabs backwards */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if ((i = scp->xpos & 0xf8) == scp->xpos)
+ i -= 8*n;
+ else
+ i -= 8*(n-1);
+ if (i < 0)
+ i = 0;
+ move_crsr(scp, i, scp->ypos);
+ break;
+
+ case '`': /* move cursor to column n */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, n, scp->ypos);
+ break;
+
+ case 'a': /* move cursor n columns to the right */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos + n, scp->ypos);
+ break;
+
+ case 'd': /* move cursor to row n */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos, n);
+ break;
+
+ case 'e': /* move cursor n rows down */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos, scp->ypos + n);
+ break;
+
+ case 'm': /* change attribute */
+ if (scp->term.num_param == 0)
+ n = 0;
+ else
+ n = scp->term.param[0];
+ switch (n) {
+ case 0: /* back to normal */
+ scp->term.cur_attr = scp->term.std_attr;
+ break;
+ case 1: /* highlight (bold) */
+ scp->term.cur_attr &= 0xFF00;
+ scp->term.cur_attr |= 0x0800;
+ break;
+ case 4: /* highlight (underline) */
+ scp->term.cur_attr &= 0x0F00;
+ scp->term.cur_attr |= 0x0800;
+ break;
+ case 5: /* blink */
+ scp->term.cur_attr &= 0xFF00;
+ scp->term.cur_attr |= 0x8000;
+ break;
+ case 7: /* reverse video */
+ scp->term.cur_attr = scp->term.rev_attr;
+ break;
+ case 30: case 31: case 32: case 33: /* set fg color */
+ case 34: case 35: case 36: case 37:
+ scp->term.cur_attr = (scp->term.cur_attr & 0xF0FF)
+ | (ansi_col[(n - 30) & 7] << 8);
+ break;
+ case 40: case 41: case 42: case 43: /* set bg color */
+ case 44: case 45: case 46: case 47:
+ scp->term.cur_attr = (scp->term.cur_attr & 0x0FFF)
+ | (ansi_col[(n - 40) & 7] << 12);
+ break;
+ }
+ break;
+
+ case 'x':
+ if (scp->term.num_param == 0)
+ n = 0;
+ else
+ n = scp->term.param[0];
+ switch (n) {
+ case 0: /* reset attributes */
+ scp->term.cur_attr = scp->term.std_attr =
+ current_default->std_attr;
+ scp->term.rev_attr = current_default->rev_attr;
+ break;
+ case 1: /* set ansi background */
+ scp->term.cur_attr = scp->term.std_attr =
+ (scp->term.std_attr & 0x0F00) |
+ (ansi_col[(scp->term.param[1])&0x0F]<<12);
+ break;
+ case 2: /* set ansi foreground */
+ scp->term.cur_attr = scp->term.std_attr =
+ (scp->term.std_attr & 0xF000) |
+ (ansi_col[(scp->term.param[1])&0x0F]<<8);
+ break;
+ case 3: /* set ansi attribute directly */
+ scp->term.cur_attr = scp->term.std_attr =
+ (scp->term.param[1]&0xFF)<<8;
+ break;
+ case 5: /* set ansi reverse video background */
+ scp->term.rev_attr =
+ (scp->term.rev_attr & 0x0F00) |
+ (ansi_col[(scp->term.param[1])&0x0F]<<12);
+ break;
+ case 6: /* set ansi reverse video foreground */
+ scp->term.rev_attr =
+ (scp->term.rev_attr & 0xF000) |
+ (ansi_col[(scp->term.param[1])&0x0F]<<8);
+ break;
+ case 7: /* set ansi reverse video directly */
+ scp->term.rev_attr = (scp->term.param[1]&0xFF)<<8;
+ break;
+ }
+ break;
+
+ case 'z': /* switch to (virtual) console n */
+ if (scp->term.num_param == 1)
+ switch_scr(scp->term.param[0]);
+ break;
+ }
+ }
+ else if (scp->term.esc == 3) {
+ if (c >= '0' && c <= '9') {
+ if (scp->term.num_param < MAX_ESC_PAR) {
+ if (scp->term.last_param != scp->term.num_param) {
+ scp->term.last_param = scp->term.num_param;
+ scp->term.param[scp->term.num_param] = 0;
+ }
+ else
+ scp->term.param[scp->term.num_param] *= 10;
+ scp->term.param[scp->term.num_param] += c - '0';
+ return;
+ }
+ }
+ scp->term.num_param = scp->term.last_param + 1;
+ switch (c) {
+
+ case ';':
+ if (scp->term.num_param < MAX_ESC_PAR)
+ return;
+ break;
+
+ case 'A': /* set display border color */
+ if (scp->term.num_param == 1)
+ scp->border=scp->term.param[0] & 0xff;
+ if (scp == cur_console)
+ set_border(scp->border);
+ break;
+
+ case 'B': /* set bell pitch and duration */
+ if (scp->term.num_param == 2) {
+ scp->bell_pitch = scp->term.param[0];
+ scp->bell_duration = scp->term.param[1]*10;
+ }
+ break;
+
+ case 'C': /* set cursor shape (start & end line) */
+ if (scp->term.num_param == 2) {
+ scp->cursor_start = scp->term.param[0] & 0x1F;
+ scp->cursor_end = scp->term.param[1] & 0x1F;
+ if (scp == cur_console)
+ cursor_shape(scp->cursor_start,
+ scp->cursor_end);
+ }
+ break;
+
+ case 'F': /* set ansi foreground */
+ if (scp->term.num_param == 1)
+ scp->term.cur_attr = scp->term.std_attr =
+ (scp->term.std_attr & 0xF000)
+ | ((scp->term.param[0] & 0x0F) << 8);
+ break;
+
+ case 'G': /* set ansi background */
+ if (scp->term.num_param == 1)
+ scp->term.cur_attr = scp->term.std_attr =
+ (scp->term.std_attr & 0x0F00)
+ | ((scp->term.param[0] & 0x0F) << 12);
+ break;
+
+ case 'H': /* set ansi reverse video foreground */
+ if (scp->term.num_param == 1)
+ scp->term.rev_attr =
+ (scp->term.rev_attr & 0xF000)
+ | ((scp->term.param[0] & 0x0F) << 8);
+ break;
+
+ case 'I': /* set ansi reverse video background */
+ if (scp->term.num_param == 1)
+ scp->term.rev_attr =
+ (scp->term.rev_attr & 0x0F00)
+ | ((scp->term.param[0] & 0x0F) << 12);
+ break;
+ }
+ }
+ scp->term.esc = 0;
+}
+
+
+static void ansi_put(scr_stat *scp, u_char c)
+{
+ if (scp->status & UNKNOWN_MODE)
+ return;
+
+ /* make screensaver happy */
+ if (scp == cur_console) {
+ scrn_time_stamp = time.tv_sec;
+ if (scrn_blanked)
+ SCRN_SAVER(0);
+ }
+ in_putc++;
+ if (scp->term.esc)
+ scan_esc(scp, c);
+ else switch(c) {
+ case 0x1B: /* start escape sequence */
+ scp->term.esc = 1;
+ scp->term.num_param = 0;
+ break;
+ case 0x07:
+ if (scp == cur_console)
+ sysbeep(scp->bell_pitch, scp->bell_duration);
+ break;
+ case '\t': /* non-destructive tab */
+ scp->crtat += (8 - scp->xpos % 8);
+ scp->xpos += (8 - scp->xpos % 8);
+ break;
+ case '\b': /* non-destructive backspace */
+ if (scp->crtat > scp->crt_base) {
+ scp->crtat--;
+ if (scp->xpos > 0)
+ scp->xpos--;
+ else {
+ scp->xpos += scp->xsize - 1;
+ scp->ypos--;
+ }
+ }
+ break;
+ case '\r': /* return to pos 0 */
+ move_crsr(scp, 0, scp->ypos);
+ break;
+ case '\n': /* newline, same pos */
+ scp->crtat += scp->xsize;
+ scp->ypos++;
+ break;
+ case '\f': /* form feed, clears screen */
+ clear_screen(scp);
+ break;
+ default:
+ /* Print only printables */
+ *scp->crtat = (scp->term.cur_attr | scr_map[c]);
+ scp->crtat++;
+ if (++scp->xpos >= scp->xsize) {
+ scp->xpos = 0;
+ scp->ypos++;
+ }
+ break;
+ }
+ if (scp->crtat >= scp->crt_base + scp->ysize * scp->xsize) {
+ bcopy(scp->crt_base + scp->xsize, scp->crt_base,
+ scp->xsize * (scp->ysize - 1) * sizeof(u_short));
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base + scp->xsize * (scp->ysize - 1),
+ scp->xsize);
+ scp->crtat -= scp->xsize;
+ scp->ypos--;
+ }
+ in_putc--;
+ if (delayed_next_scr)
+ switch_scr(delayed_next_scr - 1);
+}
+
+static void scinit(void)
+{
+ u_short volatile *cp = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short), was;
+ unsigned cursorat;
+ int i;
+
+ /*
+ * catch that once in a blue moon occurence when scinit is called
+ * TWICE, adding the CGA_BUF offset again -> poooff
+ */
+ if (crtat != 0)
+ return;
+ /*
+ * Crtat initialized to point to MONO buffer, if not present change
+ * to CGA_BUF offset. ONLY ADD the difference since locore.s adds
+ * in the remapped offset at the "right" time
+ */
+ was = *cp;
+ *cp = (u_short) 0xA55A;
+ if (*cp != 0xA55A) {
+ crtc_addr = MONO_BASE;
+ } else {
+ *cp = was;
+ crtc_addr = COLOR_BASE;
+ Crtat = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short);
+ }
+
+ /* Extract cursor location */
+ outb(crtc_addr,14);
+ cursorat = inb(crtc_addr+1)<<8 ;
+ outb(crtc_addr,15);
+ cursorat |= inb(crtc_addr+1);
+ crtat = Crtat + cursorat;
+
+ /* is this a VGA or higher ? */
+ outb(crtc_addr, 7);
+ if (inb(crtc_addr) == 7)
+ crtc_vga = 1;
+
+ current_default = &user_default;
+ console[0].crtat = crtat;
+ console[0].crt_base = Crtat;
+ console[0].term.esc = 0;
+ console[0].term.std_attr = current_default->std_attr;
+ console[0].term.rev_attr = current_default->rev_attr;
+ console[0].term.cur_attr = current_default->std_attr;
+ console[0].xpos = cursorat % COL;
+ console[0].ypos = cursorat / COL;
+ console[0].border = BG_BLACK;;
+ console[0].xsize = COL;
+ console[0].ysize = ROW;
+ console[0].status = 0;
+ console[0].pid = 0;
+ console[0].proc = NULL;
+ console[0].smode.mode = VT_AUTO;
+ console[0].bell_pitch = BELL_PITCH;
+ console[0].bell_duration = BELL_DURATION;
+ kernel_console.esc = 0;
+ kernel_console.std_attr = kernel_default.std_attr;
+ kernel_console.rev_attr = kernel_default.rev_attr;
+ kernel_console.cur_attr = kernel_default.std_attr;
+ /* initialize mapscrn array to a one to one map */
+ for (i=0; i<sizeof(scr_map); i++)
+ scr_map[i] = i;
+ clear_screen(&console[0]);
+}
+
+
+static void scput(u_char c)
+{
+ scr_stat *scp = &console[0];
+ term_stat save;
+
+ if (crtat == 0)
+ scinit();
+ if( in_putc == 0) {
+ ++in_putc;
+ save = scp->term;
+ scp->term = kernel_console;
+ current_default = &kernel_default;
+ ansi_put(scp, c);
+ kernel_console = scp->term;
+ current_default = &user_default;
+ scp->term = save;
+ --in_putc;
+ } else {
+ if( console_buffer_count < CONSOLE_BUFFER_SIZE)
+ console_buffer[console_buffer_count++] = c;
+ }
+}
+
+
+static u_char *get_fstr(u_int c, u_int *len)
+{
+ u_int i;
+
+ if (!(c & FKEY))
+ return(NULL);
+ i = (c & 0xFF) - F_FN;
+ if (i > n_fkey_tab)
+ return(NULL);
+ *len = fkey_tab[i].len;
+ return(fkey_tab[i].str);
+}
+
+
+static void update_leds(int which)
+{
+ static u_char xlate_leds[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+
+ /* replace CAPS led with ALTGR led for ALTGR keyboards */
+ if (key_map.n_keys > ALTGR_OFFSET) {
+ if (which & ALKED)
+ which |= CLKED;
+ else
+ which &= ~CLKED;
+ }
+ kbd_cmd2(KB_SETLEDS, xlate_leds[which & LED_MASK]);
+}
+
+
+/*
+ * scgetc(noblock) : get a character from the keyboard.
+ * If noblock = 0 wait until a key is gotten. Otherwise return NOKEY.
+ */
+u_int scgetc(int noblock)
+{
+ u_char val, code, release;
+ u_int state, action;
+ struct key_t *key;
+ static u_char esc_flag = 0, compose = 0;
+ static u_int chr = 0;
+
+next_code:
+ kbd_wait();
+ /* First see if there is something in the keyboard port */
+ if (inb(KB_STAT) & KB_BUF_FULL)
+ val = inb(KB_DATA);
+ else if (noblock)
+ return(NOKEY);
+ else
+ goto next_code;
+
+ if (cur_console->status & KBD_RAW_MODE)
+ return val;
+
+ code = val & 0x7F;
+ release = val & 0x80;
+
+ switch (esc_flag) {
+ case 0x00: /* normal scancode */
+ switch(code) {
+ case 0x38: /* left alt (compose key) */
+ if (release && compose) {
+ compose = 0;
+ if (chr > 255) {
+ sysbeep(BELL_PITCH, BELL_DURATION);
+ chr = 0;
+ }
+ }
+ else {
+ if (!compose) {
+ compose = 1;
+ chr = 0;
+ }
+ }
+ break;
+ case 0x60:
+ case 0x61:
+ esc_flag = code;
+ goto next_code;
+ }
+ break;
+ case 0x60: /* 0xE0 prefix */
+ esc_flag = 0;
+ switch (code) {
+ case 0x1c: /* right enter key */
+ code = 0x59;
+ break;
+ case 0x1d: /* right ctrl key */
+ code = 0x5a;
+ break;
+ case 0x35: /* keypad divide key */
+ code = 0x5b;
+ break;
+ case 0x37: /* print scrn key */
+ code = 0x5c;
+ break;
+ case 0x38: /* right alt key (alt gr) */
+ code = 0x5d;
+ break;
+ case 0x47: /* grey home key */
+ code = 0x5e;
+ break;
+ case 0x48: /* grey up arrow key */
+ code = 0x5f;
+ break;
+ case 0x49: /* grey page up key */
+ code = 0x60;
+ break;
+ case 0x4b: /* grey left arrow key */
+ code = 0x61;
+ break;
+ case 0x4d: /* grey right arrow key */
+ code = 0x62;
+ break;
+ case 0x4f: /* grey end key */
+ code = 0x63;
+ break;
+ case 0x50: /* grey down arrow key */
+ code = 0x64;
+ break;
+ case 0x51: /* grey page down key */
+ code = 0x65;
+ break;
+ case 0x52: /* grey insert key */
+ code = 0x66;
+ break;
+ case 0x53: /* grey delete key */
+ code = 0x67;
+ break;
+ default: /* ignore everything else */
+ goto next_code;
+ }
+ break;
+ case 0x61: /* 0xE1 prefix */
+ esc_flag = 0;
+ if (code == 0x1D)
+ esc_flag = 0x1D;
+ goto next_code;
+ /* NOT REACHED */
+ case 0x1D: /* pause / break */
+ esc_flag = 0;
+ if (code != 0x45)
+ goto next_code;
+ code = 0x68;
+ break;
+ }
+
+ if (compose) {
+ switch (code) {
+ case 0x47:
+ case 0x48: /* keypad 7,8,9 */
+ case 0x49:
+ if (!release)
+ chr = (code - 0x40) + chr*10;
+ goto next_code;
+ case 0x4b:
+ case 0x4c: /* keypad 4,5,6 */
+ case 0x4d:
+ if (!release)
+ chr = (code - 0x47) + chr*10;
+ goto next_code;
+ case 0x4f:
+ case 0x50: /* keypad 1,2,3 */
+ case 0x51:
+ if (!release)
+ chr = (code - 0x4e) + chr*10;
+ goto next_code;
+ case 0x52: /* keypad 0 */
+ if (!release)
+ chr *= 10;
+ goto next_code;
+ case 0x38: /* left alt key */
+ break;
+ default:
+ if (chr) {
+ compose = chr = 0;
+ sysbeep(BELL_PITCH, BELL_DURATION);
+ goto next_code;
+ }
+ break;
+ }
+ }
+
+ state = (shfts ? 1 : 0 ) | (2 * (ctls ? 1 : 0)) | (4 * (alts ? 1 : 0));
+ if ((!agrs && (cur_console->status & ALKED))
+ || (agrs && !(cur_console->status & ALKED)))
+ code += ALTGR_OFFSET;
+ key = &key_map.key[code];
+ if ( ((key->flgs & FLAG_LOCK_C) && (cur_console->status & CLKED))
+ || ((key->flgs & FLAG_LOCK_N) && (cur_console->status & NLKED)) )
+ state ^= 1;
+
+ /* Check for make/break */
+ action = key->map[state];
+ if (release) { /* key released */
+ if (key->spcl & 0x80) {
+ switch (action) {
+ case LSH:
+ shfts &= ~1;
+ break;
+ case RSH:
+ shfts &= ~2;
+ break;
+ case LCTR:
+ ctls &= ~1;
+ break;
+ case RCTR:
+ ctls &= ~2;
+ break;
+ case LALT:
+ alts &= ~1;
+ break;
+ case RALT:
+ alts &= ~2;
+ break;
+ case NLK:
+ nlkcnt = 0;
+ break;
+ case CLK:
+ clkcnt = 0;
+ break;
+ case SLK:
+ slkcnt = 0;
+ break;
+ case ASH:
+ agrs = 0;
+ break;
+ case ALK:
+ alkcnt = 0;
+ break;
+ case META:
+ metas = 0;
+ break;
+ }
+ }
+ if (chr && !compose) {
+ action = chr;
+ chr = 0;
+ return(action);
+ }
+ } else {
+ /* key pressed */
+ if (key->spcl & (0x80>>state)) {
+ switch (action) {
+ /* LOCKING KEYS */
+ case NLK:
+ if (!nlkcnt) {
+ nlkcnt++;
+ if (cur_console->status & NLKED)
+ cur_console->status &= ~NLKED;
+ else
+ cur_console->status |= NLKED;
+ update_leds(cur_console->status);
+ }
+ break;
+ case CLK:
+ if (!clkcnt) {
+ clkcnt++;
+ if (cur_console->status & CLKED)
+ cur_console->status &= ~CLKED;
+ else
+ cur_console->status |= CLKED;
+ update_leds(cur_console->status);
+ }
+ break;
+ case SLK:
+ if (!slkcnt) {
+ slkcnt++;
+ if (cur_console->status & SLKED) {
+ cur_console->status &= ~SLKED;
+ pcstart(VIRTUAL_TTY(get_scr_num()));
+ }
+ else
+ cur_console->status |= SLKED;
+ update_leds(cur_console->status);
+ }
+ break;
+ case ALK:
+ if (!alkcnt) {
+ alkcnt++;
+ if (cur_console->status & ALKED)
+ cur_console->status &= ~ALKED;
+ else
+ cur_console->status |= ALKED;
+ update_leds(cur_console->status);
+ }
+ break;
+
+ /* NON-LOCKING KEYS */
+ case NOP:
+ break;
+ case RBT:
+#if defined(__FreeBSD__)
+ shutdown_nice();
+#else
+ cpu_reset();
+#endif
+ break;
+ case DBG:
+#if DDB > 0 /* try to switch to console 0 */
+ if (cur_console->smode.mode == VT_AUTO &&
+ console[0].smode.mode == VT_AUTO)
+ switch_scr(0);
+ Debugger("manual escape to debugger");
+ return(NOKEY);
+#else
+ printf("No debugger in kernel\n");
+#endif
+ break;
+ case LSH:
+ shfts |= 1;
+ break;
+ case RSH:
+ shfts |= 2;
+ break;
+ case LCTR:
+ ctls |= 1;
+ break;
+ case RCTR:
+ ctls |= 2;
+ break;
+ case LALT:
+ alts |= 1;
+ break;
+ case RALT:
+ alts |= 2;
+ break;
+ case ASH:
+ agrs = 1;
+ break;
+ case META:
+ metas = 1;
+ break;
+ case NEXT:
+ switch_scr((get_scr_num()+1)%NCONS);
+ break;
+ default:
+ if (action >= F_SCR && action <= L_SCR) {
+ switch_scr(action - F_SCR);
+ break;
+ }
+ if (action >= F_FN && action <= L_FN)
+ action |= FKEY;
+ return(action);
+ }
+ }
+ else {
+ if (metas)
+ action |= MKEY;
+ return(action);
+ }
+ }
+ goto next_code;
+}
+
+
+int getchar(void)
+{
+ u_char thechar;
+ int s;
+
+ polling = 1;
+ s = splhigh();
+ scput('>');
+ thechar = (u_char) scgetc(0);
+ polling = 0;
+ splx(s);
+ switch (thechar) {
+ default:
+ if (thechar >= scr_map[0x20])
+ scput(thechar);
+ return(thechar);
+ case cr:
+ case lf:
+ scput(cr); scput(lf);
+ return(lf);
+ case bs:
+ case del:
+ scput(bs); scput(scr_map[0x20]); scput(bs);
+ return(thechar);
+ case cntld:
+ scput('^'); scput('D'); scput('\r'); scput('\n');
+ return(0);
+ }
+}
+
+
+u_int sgetc(int noblock)
+{
+ return (scgetc(noblock) & 0xff);
+}
+
+int pcmmap(dev_t dev, int offset, int nprot)
+{
+ if (offset > 0x20000)
+ return EINVAL;
+ return i386_btop((VIDEOMEM + offset));
+}
+
+
+static void kbd_wait(void)
+{
+ int i;
+
+ for (i=0; i<1000; i++) { /* up to 10 msec */
+ if ((inb(KB_STAT) & KB_READY) == 0)
+ break;
+ DELAY (10);
+ }
+}
+
+
+static void kbd_cmd(u_char command)
+{
+ kbd_wait();
+ outb(KB_DATA, command);
+}
+
+
+static void kbd_cmd2(u_char command, u_char arg)
+{
+ int r, s = spltty();
+ do {
+ kbd_cmd(command);
+ r = kbd_reply();
+ if (r == KB_ACK) {
+ kbd_cmd(arg & 0x7f);
+ r = kbd_reply();
+ }
+ } while (r != KB_ACK);
+ splx(s);
+}
+
+
+static int kbd_reply()
+{
+ int i;
+
+ kbd_wait();
+ for (i=0; i<60000; i++) { /* at least 300 msec, 600 msec enough */
+ if (inb(KB_STAT) & KB_BUF_FULL)
+ return ((u_char) inb(KB_DATA));
+ DELAY (10);
+ }
+ return(-1);
+}
+
+
+static void set_mode(scr_stat *scp)
+{
+ u_char byte;
+ int s;
+
+ if (scp != cur_console)
+ return;
+
+ /* (re)activate cursor */
+ untimeout((timeout_t)cursor_pos, 0);
+ cursor_pos(1);
+
+ /* change cursor type if set */
+ if (scp->cursor_start != -1 && scp->cursor_end != -1)
+ cursor_shape(scp->cursor_start, scp->cursor_end);
+
+ /* mode change only on VGA's */
+ if (!crtc_vga)
+ return;
+
+ /* setup video hardware for the given mode */
+ s = splhigh();
+ switch(scp->mode) {
+ case TEXT80x25:
+ outb(crtc_addr, 9); byte = inb(crtc_addr+1);
+ outb(crtc_addr, 9); outb(crtc_addr+1, byte | 0x0F);
+ outb(TSIDX, 0x03); outb(TSREG, 0x00); /* select font 0 */
+ break;
+ case TEXT80x50:
+ outb(crtc_addr, 9); byte = inb(crtc_addr+1);
+ outb(crtc_addr, 9); outb(crtc_addr+1, (byte & 0xF0) | 0x07);
+ outb(TSIDX, 0x03); outb(TSREG, 0x05); /* select font 1 */
+ break;
+ default:
+ break;
+ }
+ splx(s);
+
+ /* set border color for this (virtual) console */
+ set_border(scp->border);
+ return;
+}
+
+
+static void set_border(int color)
+{
+ inb(crtc_addr+6); /* reset flip-flop */
+ outb(ATC, 0x11); outb(ATC, color);
+ inb(crtc_addr+6); /* reset flip-flop */
+ outb(ATC, 0x20); /* enable Palette */
+}
+
+static void load_font(int segment, int size, char* font)
+{
+ int ch, line, s;
+ u_char val;
+
+ outb(TSIDX, 0x01); val = inb(TSREG); /* blank screen */
+ outb(TSIDX, 0x01); outb(TSREG, val | 0x20);
+
+ /* setup vga for loading fonts (graphics plane mode) */
+ s = splhigh();
+ inb(crtc_addr+6); /* reset flip/flop */
+ outb(ATC, 0x30); outb(ATC, 0x01);
+ outb(TSIDX, 0x02); outb(TSREG, 0x04);
+ outb(TSIDX, 0x04); outb(TSREG, 0x06);
+ outb(GDCIDX, 0x04); outb(GDCREG, 0x02);
+ outb(GDCIDX, 0x05); outb(GDCREG, 0x00);
+ outb(GDCIDX, 0x06); outb(GDCREG, 0x05); /* addr = a0000, 64kb */
+ splx(s);
+ for (ch=0; ch < 256; ch++)
+ for (line=0; line < size; line++)
+ *((char *)atdevbase+(segment*0x4000)+(ch*32)+line) =
+ font[(ch*size)+line];
+ /* setup vga for text mode again */
+ s = splhigh();
+ inb(crtc_addr+6); /* reset flip/flop */
+ outb(ATC, 0x30); outb(ATC, 0x0C);
+ outb(TSIDX, 0x02); outb(TSREG, 0x03);
+ outb(TSIDX, 0x04); outb(TSREG, 0x02);
+ outb(GDCIDX, 0x04); outb(GDCREG, 0x00);
+ outb(GDCIDX, 0x05); outb(GDCREG, 0x10);
+ if (crtc_addr == MONO_BASE) {
+ outb(GDCIDX, 0x06); outb(GDCREG, 0x0A); /* addr = b0000, 32kb */
+ }
+ else {
+ outb(GDCIDX, 0x06); outb(GDCREG, 0x0E); /* addr = b8000, 32kb */
+ }
+ splx(s);
+ outb(TSIDX, 0x01); val = inb(TSREG); /* unblank screen */
+ outb(TSIDX, 0x01); outb(TSREG, val & 0xDF);
+}
+
+
+static void load_palette(void)
+{
+ int i;
+
+ outb(PIXMASK, 0xFF); /* no pixelmask */
+ outb(PALWADR, 0x00);
+ for (i=0x00; i<0x300; i++)
+ outb(PALDATA, palette[i]);
+ inb(crtc_addr+6); /* reset flip/flop */
+ outb(ATC, 0x20); /* enable palette */
+}
+
+static void save_palette(void)
+{
+ int i;
+
+ outb(PALRADR, 0x00);
+ for (i=0x00; i<0x300; i++)
+ palette[i] = inb(PALDATA);
+ inb(crtc_addr+6); /* reset flip/flop */
+}
+
+
+static void change_winsize(struct tty *tp, int x, int y)
+{
+ if (tp->t_winsize.ws_col != x || tp->t_winsize.ws_row != y) {
+ tp->t_winsize.ws_col = x;
+ tp->t_winsize.ws_row = y;
+ pgsignal(tp->t_pgrp, SIGWINCH, 1);
+ }
+}
+
+#endif /* NSC */
diff --git a/sys/fs/cd9660/TODO b/sys/fs/cd9660/TODO
new file mode 100644
index 0000000..555d26a
--- /dev/null
+++ b/sys/fs/cd9660/TODO
@@ -0,0 +1,77 @@
+# $Id: TODO,v 1.4 1993/09/07 15:40:51 ws Exp $
+
+ 1) should understand "older", original High Sierra ("CDROM001") type
+
+ Not yet. ( I don't have this technical information, yet. )
+
+ 2) should understand Rock Ridge
+
+ Yes, we have follows function.
+
+ o Symbolic Link
+ o Real Name(long name)
+ o File Attribute
+ o Time stamp
+ o uid, gid
+ o Devices
+ o Relocated directories
+
+ Except follows:
+
+ o POSIX device number mapping
+
+ There is some preliminary stuff in there that (ab-)uses the mknod
+ system call, but this needs a writable filesystem
+
+ 3) should be called cdfs, as there are other ISO file system soon possible
+
+ Not yet. Probably we should make another file system when the ECMA draft
+ is valid and do it. For doing Rock Ridge Support, I can use almost same
+ code. So I just use the same file system interface...
+
+ 4) should have file handles implemented for use with NFS, etc
+
+ Yes. we have already this one, and I based it for this release.
+
+ 5) should have name translation enabled by mount flag
+
+ Yes. we can disable the Rock Ridge Extension by follows option;
+
+ "mount -t isofs -o -norrip /dev/cd0d /cdrom"
+
+ 6) should run as a user process, and not take up kernel space (cdroms
+ are slow)
+
+ Not yet.
+
+ 7) ECMA support.
+
+ Not yet. we need not only a technical spec but also ECMA format
+ cd-rom itself!
+
+ 8) Character set change by SVD ( multi SVD support )
+
+ Not yet. We should also hack the other part of system as 8 bit
+ clean. As far as I know, if you export the cdrom by NFS, the client
+ can access the 8 bit clean (ie. Solaris Japanese with EUC code )
+
+ 9) Access checks in isofs_access
+
+ Not yet.
+
+ 10) Support for generation numbers
+
+ Yes. Default is to list only the last file (the one with the highest
+ generation number). If you mount with -gen, all files are shown with
+ their generation numbers. In both cases you can specify the generation
+ number on opening files (if you happen to know it) or leave it off,
+ when it will again find the last file.
+
+ 11) Support for extended attributes
+
+ Yes. Since this requires an extra block buffer for the attributes
+ this must be enabled on mounting with the option -extattr.
+
+----------
+Last update July 19, '93 by Atsushi Murai. (amurai@spec.co.jp)
+Last update August 19, '93 by Wolfgang Solfrank. (ws@tools.de)
diff --git a/sys/fs/cd9660/TODO.hibler b/sys/fs/cd9660/TODO.hibler
new file mode 100644
index 0000000..3501aa2
--- /dev/null
+++ b/sys/fs/cd9660/TODO.hibler
@@ -0,0 +1,22 @@
+1. Investiate making ISOFS another UFS shared filesystem (ala FFS/MFS/LFS).
+ Since it was modelled after the inode code, we might be able to merge
+ them back. It looks like a seperate (but very similar) lookup routine
+ will be needed due to the associated file stuff.
+
+2. Make filesystem exportable. This comes for free if stacked with UFS.
+ Otherwise, the ufs_export routines need to be elevated to vfs_* routines.
+ [ DONE - hibler ]
+
+3. If it can't be merged with UFS, at least get them in sync. For example,
+ it could use the same style hashing routines as in ufs/ufs_ihash.c
+
+4. It would be nice to be able to use the vfs_cluster code.
+ Unfortunately, if the logical block size is smaller than the page size,
+ it won't work. Also, if throughtput is relatively constant for any
+ block size (as it is for the HP drive--150kbs) then clustering may not
+ buy much (or may even hurt when vfs_cluster comes up with a large sync
+ cluster).
+
+5. Seems like there should be a "notrans" or some such mount option to show
+ filenames as they really are without lower-casing, stripping of version
+ numbers, etc. Does this make sense?
diff --git a/sys/fs/cd9660/cd9660_bmap.c b/sys/fs/cd9660/cd9660_bmap.c
new file mode 100644
index 0000000..911eedf
--- /dev/null
+++ b/sys/fs/cd9660/cd9660_bmap.c
@@ -0,0 +1,102 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cd9660_bmap.c 8.3 (Berkeley) 1/23/94
+ */
+
+#include <sys/param.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+
+#include <isofs/cd9660/iso.h>
+#include <isofs/cd9660/cd9660_node.h>
+
+/*
+ * Bmap converts a the logical block number of a file to its physical block
+ * number on the disk. The conversion is done by using the logical block
+ * number to index into the data block (extent) for the file.
+ */
+int
+cd9660_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap;
+{
+ struct iso_node *ip = VTOI(ap->a_vp);
+ daddr_t lblkno = ap->a_bn;
+ long bsize;
+
+ /*
+ * Check for underlying vnode requests and ensure that logical
+ * to physical mapping is requested.
+ */
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = ip->i_devvp;
+ if (ap->a_bnp == NULL)
+ return (0);
+
+ /*
+ * Compute the requested block number
+ */
+ bsize = ip->i_mnt->logical_block_size;
+ *ap->a_bnp = (ip->iso_start + lblkno) * btodb(bsize);
+
+ /*
+ * Determine maximum number of readahead blocks following the
+ * requested block.
+ */
+ if (ap->a_runp) {
+ int nblk;
+
+ nblk = (ip->i_size - (lblkno + 1) * bsize) / bsize;
+ if (nblk <= 0)
+ *ap->a_runp = 0;
+ else if (nblk >= MAXBSIZE/bsize)
+ *ap->a_runp = MAXBSIZE/bsize - 1;
+ else
+ *ap->a_runp = nblk;
+ }
+
+ return 0;
+}
diff --git a/sys/fs/cd9660/cd9660_lookup.c b/sys/fs/cd9660/cd9660_lookup.c
new file mode 100644
index 0000000..36daffd
--- /dev/null
+++ b/sys/fs/cd9660/cd9660_lookup.c
@@ -0,0 +1,467 @@
+/*-
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)ufs_lookup.c 7.33 (Berkeley) 5/19/91
+ *
+ * @(#)cd9660_lookup.c 8.2 (Berkeley) 1/23/94
+ */
+
+#include <sys/param.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+
+#include <isofs/cd9660/iso.h>
+#include <isofs/cd9660/cd9660_node.h>
+#include <isofs/cd9660/iso_rrip.h>
+#include <isofs/cd9660/cd9660_rrip.h>
+
+struct nchstats iso_nchstats;
+
+/*
+ * Convert a component of a pathname into a pointer to a locked inode.
+ * This is a very central and rather complicated routine.
+ * If the file system is not maintained in a strict tree hierarchy,
+ * this can result in a deadlock situation (see comments in code below).
+ *
+ * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
+ * whether the name is to be looked up, created, renamed, or deleted.
+ * When CREATE, RENAME, or DELETE is specified, information usable in
+ * creating, renaming, or deleting a directory entry may be calculated.
+ * If flag has LOCKPARENT or'ed into it and the target of the pathname
+ * exists, lookup returns both the target and its parent directory locked.
+ * When creating or renaming and LOCKPARENT is specified, the target may
+ * not be ".". When deleting and LOCKPARENT is specified, the target may
+ * be "."., but the caller must check to ensure it does an vrele and iput
+ * instead of two iputs.
+ *
+ * Overall outline of ufs_lookup:
+ *
+ * check accessibility of directory
+ * look for name in cache, if found, then if at end of path
+ * and deleting or creating, drop it, else return name
+ * search for name in directory, to found or notfound
+ * notfound:
+ * if creating, return locked directory, leaving info on available slots
+ * else return error
+ * found:
+ * if at end of path and deleting, return information to allow delete
+ * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
+ * inode and return info to allow rewrite
+ * if not at end, add name to cache; if at end and neither creating
+ * nor deleting, add name to cache
+ *
+ * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode unlocked.
+ */
+int
+cd9660_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ register struct vnode *vdp; /* vnode for directory being searched */
+ register struct iso_node *dp; /* inode for directory being searched */
+ register struct iso_mnt *imp; /* file system that directory is in */
+ struct buf *bp; /* a buffer of directory entries */
+ struct iso_directory_record *ep = 0;/* the current directory entry */
+ int entryoffsetinblock; /* offset of ep in bp's buffer */
+ int saveoffset = 0; /* offset of last directory entry in dir */
+ int numdirpasses; /* strategy for directory search */
+ doff_t endsearch; /* offset to end directory search */
+ struct iso_node *pdp; /* saved dp during symlink work */
+ struct iso_node *tdp; /* returned by iget */
+ int lockparent; /* 1 => lockparent flag is set */
+ int wantparent; /* 1 => wantparent or lockparent flag */
+ int error;
+ ino_t ino = 0;
+ int reclen;
+ u_short namelen;
+ char altname[NAME_MAX];
+ int res;
+ int assoc, len;
+ char *name;
+ struct vnode **vpp = ap->a_vpp;
+ struct componentname *cnp = ap->a_cnp;
+ struct ucred *cred = cnp->cn_cred;
+ int flags = cnp->cn_flags;
+ int nameiop = cnp->cn_nameiop;
+
+ bp = NULL;
+ *vpp = NULL;
+ vdp = ap->a_dvp;
+ dp = VTOI(vdp);
+ imp = dp->i_mnt;
+ lockparent = flags & LOCKPARENT;
+ wantparent = flags & (LOCKPARENT|WANTPARENT);
+
+ /*
+ * Check accessiblity of directory.
+ */
+ if (vdp->v_type != VDIR)
+ return (ENOTDIR);
+ if (error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc))
+ return (error);
+
+ /*
+ * We now have a segment name to search for, and a directory to search.
+ *
+ * Before tediously performing a linear scan of the directory,
+ * check the name cache to see if the directory/name pair
+ * we are looking for is known already.
+ */
+ if (error = cache_lookup(vdp, vpp, cnp)) {
+ int vpid; /* capability number of vnode */
+
+ if (error == ENOENT)
+ return (error);
+#ifdef PARANOID
+ if ((vdp->v_flag & VROOT) && (flags & ISDOTDOT))
+ panic("ufs_lookup: .. through root");
+#endif
+ /*
+ * Get the next vnode in the path.
+ * See comment below starting `Step through' for
+ * an explaination of the locking protocol.
+ */
+ pdp = dp;
+ dp = VTOI(*vpp);
+ vdp = *vpp;
+ vpid = vdp->v_id;
+ if (pdp == dp) {
+ VREF(vdp);
+ error = 0;
+ } else if (flags & ISDOTDOT) {
+ ISO_IUNLOCK(pdp);
+ error = vget(vdp, 1);
+ if (!error && lockparent && (flags & ISLASTCN))
+ ISO_ILOCK(pdp);
+ } else {
+ error = vget(vdp, 1);
+ if (!lockparent || error || !(flags & ISLASTCN))
+ ISO_IUNLOCK(pdp);
+ }
+ /*
+ * Check that the capability number did not change
+ * while we were waiting for the lock.
+ */
+ if (!error) {
+ if (vpid == vdp->v_id)
+ return (0);
+ iso_iput(dp);
+ if (lockparent && pdp != dp && (flags & ISLASTCN))
+ ISO_IUNLOCK(pdp);
+ }
+ ISO_ILOCK(pdp);
+ dp = pdp;
+ vdp = ITOV(dp);
+ *vpp = NULL;
+ }
+
+ len = cnp->cn_namelen;
+ name = cnp->cn_nameptr;
+ /*
+ * A leading `=' means, we are looking for an associated file
+ */
+ if (assoc = (imp->iso_ftype != ISO_FTYPE_RRIP && *name == ASSOCCHAR)) {
+ len--;
+ name++;
+ }
+
+ /*
+ * If there is cached information on a previous search of
+ * this directory, pick up where we last left off.
+ * We cache only lookups as these are the most common
+ * and have the greatest payoff. Caching CREATE has little
+ * benefit as it usually must search the entire directory
+ * to determine that the entry does not exist. Caching the
+ * location of the last DELETE or RENAME has not reduced
+ * profiling time and hence has been removed in the interest
+ * of simplicity.
+ */
+ if (nameiop != LOOKUP || dp->i_diroff == 0 ||
+ dp->i_diroff > dp->i_size) {
+ entryoffsetinblock = 0;
+ dp->i_offset = 0;
+ numdirpasses = 1;
+ } else {
+ dp->i_offset = dp->i_diroff;
+ entryoffsetinblock = iso_blkoff(imp, dp->i_offset);
+ if (entryoffsetinblock != 0) {
+ if (error = iso_blkatoff(dp, dp->i_offset, &bp))
+ return (error);
+ }
+ numdirpasses = 2;
+ iso_nchstats.ncs_2passes++;
+ }
+ endsearch = roundup(dp->i_size, imp->logical_block_size);
+
+searchloop:
+ while (dp->i_offset < endsearch) {
+ /*
+ * If offset is on a block boundary,
+ * read the next directory block.
+ * Release previous if it exists.
+ */
+ if (iso_blkoff(imp, dp->i_offset) == 0) {
+ if (bp != NULL)
+ brelse(bp);
+ if (error = iso_blkatoff(dp, dp->i_offset, &bp))
+ return (error);
+ entryoffsetinblock = 0;
+ }
+ /*
+ * Get pointer to next entry.
+ */
+ ep = (struct iso_directory_record *)
+ (bp->b_un.b_addr + entryoffsetinblock);
+
+ reclen = isonum_711 (ep->length);
+ if (reclen == 0) {
+ /* skip to next block, if any */
+ dp->i_offset =
+ roundup(dp->i_offset, imp->logical_block_size);
+ continue;
+ }
+
+ if (reclen < ISO_DIRECTORY_RECORD_SIZE)
+ /* illegal entry, stop */
+ break;
+
+ if (entryoffsetinblock + reclen > imp->logical_block_size)
+ /* entries are not allowed to cross boundaries */
+ break;
+
+ /*
+ * Check for a name match.
+ */
+ namelen = isonum_711(ep->name_len);
+
+ if (reclen < ISO_DIRECTORY_RECORD_SIZE + namelen)
+ /* illegal entry, stop */
+ break;
+
+ switch (imp->iso_ftype) {
+ default:
+ if ((!(isonum_711(ep->flags)&4)) == !assoc) {
+ if ((len == 1
+ && *name == '.')
+ || (flags & ISDOTDOT)) {
+ if (namelen == 1
+ && ep->name[0] == ((flags & ISDOTDOT) ? 1 : 0)) {
+ /*
+ * Save directory entry's inode number and
+ * reclen in ndp->ni_ufs area, and release
+ * directory buffer.
+ */
+ isodirino(&dp->i_ino,ep,imp);
+ goto found;
+ }
+ if (namelen != 1
+ || ep->name[0] != 0)
+ goto notfound;
+ } else if (!(res = isofncmp(name,len,
+ ep->name,namelen))) {
+ if (isonum_711(ep->flags)&2)
+ isodirino(&ino,ep,imp);
+ else
+ ino = dbtob(bp->b_blkno)
+ + entryoffsetinblock;
+ saveoffset = dp->i_offset;
+ } else if (ino)
+ goto foundino;
+#ifdef NOSORTBUG /* On some CDs directory entries are not sorted correctly */
+ else if (res < 0)
+ goto notfound;
+ else if (res > 0 && numdirpasses == 2)
+ numdirpasses++;
+#endif
+ }
+ break;
+ case ISO_FTYPE_RRIP:
+ if (isonum_711(ep->flags)&2)
+ isodirino(&ino,ep,imp);
+ else
+ ino = dbtob(bp->b_blkno) + entryoffsetinblock;
+ dp->i_ino = ino;
+ cd9660_rrip_getname(ep,altname,&namelen,&dp->i_ino,imp);
+ if (namelen == cnp->cn_namelen
+ && !bcmp(name,altname,namelen))
+ goto found;
+ ino = 0;
+ break;
+ }
+ dp->i_offset += reclen;
+ entryoffsetinblock += reclen;
+ }
+ if (ino) {
+foundino:
+ dp->i_ino = ino;
+ if (saveoffset != dp->i_offset) {
+ if (iso_lblkno(imp,dp->i_offset)
+ != iso_lblkno(imp,saveoffset)) {
+ if (bp != NULL)
+ brelse(bp);
+ if (error = iso_blkatoff(dp, saveoffset, &bp))
+ return (error);
+ }
+ ep = (struct iso_directory_record *)(bp->b_un.b_addr
+ + iso_blkoff(imp,saveoffset));
+ dp->i_offset = saveoffset;
+ }
+ goto found;
+ }
+notfound:
+ /*
+ * If we started in the middle of the directory and failed
+ * to find our target, we must check the beginning as well.
+ */
+ if (numdirpasses == 2) {
+ numdirpasses--;
+ dp->i_offset = 0;
+ endsearch = dp->i_diroff;
+ goto searchloop;
+ }
+ if (bp != NULL)
+ brelse(bp);
+ /*
+ * Insert name into cache (as non-existent) if appropriate.
+ */
+ if (cnp->cn_flags & MAKEENTRY)
+ cache_enter(vdp, *vpp, cnp);
+ if (nameiop == CREATE || nameiop == RENAME)
+ return (EJUSTRETURN);
+ return (ENOENT);
+
+found:
+ if (numdirpasses == 2)
+ iso_nchstats.ncs_pass2++;
+ if (bp != NULL)
+ brelse(bp);
+
+ /*
+ * Found component in pathname.
+ * If the final component of path name, save information
+ * in the cache as to where the entry was found.
+ */
+ if ((flags & ISLASTCN) && nameiop == LOOKUP)
+ dp->i_diroff = dp->i_offset;
+
+ /*
+ * Step through the translation in the name. We do not `iput' the
+ * directory because we may need it again if a symbolic link
+ * is relative to the current directory. Instead we save it
+ * unlocked as "pdp". We must get the target inode before unlocking
+ * the directory to insure that the inode will not be removed
+ * before we get it. We prevent deadlock by always fetching
+ * inodes from the root, moving down the directory tree. Thus
+ * when following backward pointers ".." we must unlock the
+ * parent directory before getting the requested directory.
+ * There is a potential race condition here if both the current
+ * and parent directories are removed before the `iget' for the
+ * inode associated with ".." returns. We hope that this occurs
+ * infrequently since we cannot avoid this race condition without
+ * implementing a sophisticated deadlock detection algorithm.
+ * Note also that this simple deadlock detection scheme will not
+ * work if the file system has any hard links other than ".."
+ * that point backwards in the directory structure.
+ */
+ pdp = dp;
+ /*
+ * If ino is different from dp->i_ino,
+ * it's a relocated directory.
+ */
+ if (flags & ISDOTDOT) {
+ ISO_IUNLOCK(pdp); /* race to get the inode */
+ if (error = iso_iget(dp,dp->i_ino,
+ dp->i_ino != ino,
+ &tdp,ep)) {
+ ISO_ILOCK(pdp);
+ return (error);
+ }
+ if (lockparent && (flags & ISLASTCN))
+ ISO_ILOCK(pdp);
+ *vpp = ITOV(tdp);
+ } else if (dp->i_number == dp->i_ino) {
+ VREF(vdp); /* we want ourself, ie "." */
+ *vpp = vdp;
+ } else {
+ if (error = iso_iget(dp,dp->i_ino,dp->i_ino!=ino,&tdp,ep))
+ return (error);
+ if (!lockparent || !(flags & ISLASTCN))
+ ISO_IUNLOCK(pdp);
+ *vpp = ITOV(tdp);
+ }
+
+ /*
+ * Insert name into cache if appropriate.
+ */
+ if (cnp->cn_flags & MAKEENTRY)
+ cache_enter(vdp, *vpp, cnp);
+ return (0);
+}
+
+/*
+ * Return buffer with contents of block "offset"
+ * from the beginning of directory "ip". If "res"
+ * is non-zero, fill it in with a pointer to the
+ * remaining space in the directory.
+ */
+int
+iso_blkatoff(ip, offset, bpp)
+ struct iso_node *ip;
+ doff_t offset;
+ struct buf **bpp;
+{
+ register struct iso_mnt *imp = ip->i_mnt;
+ daddr_t lbn = iso_lblkno(imp,offset);
+ int bsize = iso_blksize(imp,ip,lbn);
+ struct buf *bp;
+ int error;
+
+ if (error = bread(ITOV(ip),lbn,bsize,NOCRED,&bp)) {
+ brelse(bp);
+ *bpp = 0;
+ return (error);
+ }
+ *bpp = bp;
+
+ return (0);
+}
diff --git a/sys/fs/cd9660/cd9660_node.c b/sys/fs/cd9660/cd9660_node.c
new file mode 100644
index 0000000..f9641ff
--- /dev/null
+++ b/sys/fs/cd9660/cd9660_node.c
@@ -0,0 +1,658 @@
+/*-
+ * Copyright (c) 1982, 1986, 1989, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cd9660_node.c 8.2 (Berkeley) 1/23/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/stat.h>
+
+#include <isofs/cd9660/iso.h>
+#include <isofs/cd9660/cd9660_node.h>
+#include <isofs/cd9660/iso_rrip.h>
+
+#define INOHSZ 512
+#if ((INOHSZ&(INOHSZ-1)) == 0)
+#define INOHASH(dev,ino) (((dev)+((ino)>>12))&(INOHSZ-1))
+#else
+#define INOHASH(dev,ino) (((unsigned)((dev)+((ino)>>12)))%INOHSZ)
+#endif
+
+union iso_ihead {
+ union iso_ihead *ih_head[2];
+ struct iso_node *ih_chain[2];
+} iso_ihead[INOHSZ];
+
+#ifdef ISODEVMAP
+#define DNOHSZ 64
+#if ((DNOHSZ&(DNOHSZ-1)) == 0)
+#define DNOHASH(dev,ino) (((dev)+((ino)>>12))&(DNOHSZ-1))
+#else
+#define DNOHASH(dev,ino) (((unsigned)((dev)+((ino)>>12)))%DNOHSZ)
+#endif
+
+union iso_dhead {
+ union iso_dhead *dh_head[2];
+ struct iso_dnode *dh_chain[2];
+} iso_dhead[DNOHSZ];
+#endif
+
+int prtactive; /* 1 => print out reclaim of active vnodes */
+
+/*
+ * Initialize hash links for inodes and dnodes.
+ */
+int
+cd9660_init()
+{
+ register int i;
+ register union iso_ihead *ih = iso_ihead;
+#ifdef ISODEVMAP
+ register union iso_dhead *dh = iso_dhead;
+#endif
+
+ for (i = INOHSZ; --i >= 0; ih++) {
+ ih->ih_head[0] = ih;
+ ih->ih_head[1] = ih;
+ }
+#ifdef ISODEVMAP
+ for (i = DNOHSZ; --i >= 0; dh++) {
+ dh->dh_head[0] = dh;
+ dh->dh_head[1] = dh;
+ }
+#endif
+ return (0);
+}
+
+#ifdef ISODEVMAP
+/*
+ * Enter a new node into the device hash list
+ */
+struct iso_dnode *
+iso_dmap(dev,ino,create)
+ dev_t dev;
+ ino_t ino;
+ int create;
+{
+ struct iso_dnode *dp;
+ union iso_dhead *dh;
+
+ dh = &iso_dhead[DNOHASH(dev, ino)];
+ for (dp = dh->dh_chain[0];
+ dp != (struct iso_dnode *)dh;
+ dp = dp->d_forw)
+ if (ino == dp->i_number && dev == dp->i_dev)
+ return dp;
+
+ if (!create)
+ return (struct iso_dnode *)0;
+
+ MALLOC(dp,struct iso_dnode *,sizeof(struct iso_dnode),M_CACHE,M_WAITOK);
+ dp->i_dev = dev;
+ dp->i_number = ino;
+ insque(dp,dh);
+
+ return dp;
+}
+
+void
+iso_dunmap(dev)
+ dev_t dev;
+{
+ struct iso_dnode *dp, *dq;
+ union iso_dhead *dh;
+
+ for (dh = iso_dhead; dh < iso_dhead + DNOHSZ; dh++) {
+ for (dp = dh->dh_chain[0];
+ dp != (struct iso_dnode *)dh;
+ dp = dq) {
+ dq = dp->d_forw;
+ if (dev == dp->i_dev) {
+ remque(dp);
+ FREE(dp,M_CACHE);
+ }
+ }
+ }
+}
+#endif
+
+/*
+ * Look up a ISOFS dinode number to find its incore vnode.
+ * If it is not in core, read it in from the specified device.
+ * If it is in core, wait for the lock bit to clear, then
+ * return the inode locked. Detection and handling of mount
+ * points must be done by the calling routine.
+ */
+int
+iso_iget(xp, ino, relocated, ipp, isodir)
+ struct iso_node *xp;
+ ino_t ino;
+ int relocated;
+ struct iso_node **ipp;
+ struct iso_directory_record *isodir;
+{
+ dev_t dev = xp->i_dev;
+ struct mount *mntp = ITOV(xp)->v_mount;
+ register struct iso_node *ip, *iq;
+ register struct vnode *vp;
+ register struct iso_dnode *dp;
+ struct vnode *nvp;
+ struct buf *bp = NULL, *bp2 = NULL;
+ union iso_ihead *ih;
+ union iso_dhead *dh;
+ int i, error, result;
+ struct iso_mnt *imp;
+ ino_t defino;
+
+ ih = &iso_ihead[INOHASH(dev, ino)];
+loop:
+ for (ip = ih->ih_chain[0];
+ ip != (struct iso_node *)ih;
+ ip = ip->i_forw) {
+ if (ino != ip->i_number || dev != ip->i_dev)
+ continue;
+ if ((ip->i_flag&ILOCKED) != 0) {
+ ip->i_flag |= IWANT;
+ sleep((caddr_t)ip, PINOD);
+ goto loop;
+ }
+ if (vget(ITOV(ip), 1))
+ goto loop;
+ *ipp = ip;
+ return 0;
+ }
+ /*
+ * Allocate a new vnode/iso_node.
+ */
+ if (error = getnewvnode(VT_ISOFS, mntp, cd9660_vnodeop_p, &nvp)) {
+ *ipp = 0;
+ return error;
+ }
+ MALLOC(ip, struct iso_node *, sizeof(struct iso_node),
+ M_ISOFSNODE, M_WAITOK);
+ bzero((caddr_t)ip, sizeof(struct iso_node));
+ nvp->v_data = ip;
+ ip->i_vnode = nvp;
+ ip->i_flag = 0;
+ ip->i_devvp = 0;
+ ip->i_diroff = 0;
+ ip->i_lockf = 0;
+
+ /*
+ * Put it onto its hash chain and lock it so that other requests for
+ * this inode will block if they arrive while we are sleeping waiting
+ * for old data structures to be purged or for the contents of the
+ * disk portion of this inode to be read.
+ */
+ ip->i_dev = dev;
+ ip->i_number = ino;
+ insque(ip, ih);
+ ISO_ILOCK(ip);
+
+ imp = VFSTOISOFS (mntp);
+ ip->i_mnt = imp;
+ ip->i_devvp = imp->im_devvp;
+ VREF(ip->i_devvp);
+
+ if (relocated) {
+ /*
+ * On relocated directories we must
+ * read the `.' entry out of a dir.
+ */
+ ip->iso_start = ino >> imp->im_bshift;
+ if (error = iso_blkatoff(ip,0,&bp)) {
+ vrele(ip->i_devvp);
+ remque(ip);
+ ip->i_forw = ip;
+ ip->i_back = ip;
+ iso_iput(ip);
+ *ipp = 0;
+ return error;
+ }
+ isodir = (struct iso_directory_record *)bp->b_un.b_addr;
+ }
+
+ ip->iso_extent = isonum_733(isodir->extent);
+ ip->i_size = isonum_733(isodir->size);
+ ip->iso_start = isonum_711(isodir->ext_attr_length) + ip->iso_extent;
+
+ vp = ITOV(ip);
+
+ /*
+ * Setup time stamp, attribute
+ */
+ vp->v_type = VNON;
+ switch (imp->iso_ftype) {
+ default: /* ISO_FTYPE_9660 */
+ if ((imp->im_flags&ISOFSMNT_EXTATT)
+ && isonum_711(isodir->ext_attr_length))
+ iso_blkatoff(ip,-isonum_711(isodir->ext_attr_length),
+ &bp2);
+ cd9660_defattr(isodir,ip,bp2 );
+ cd9660_deftstamp(isodir,ip,bp2 );
+ break;
+ case ISO_FTYPE_RRIP:
+ result = cd9660_rrip_analyze(isodir,ip,imp);
+ break;
+ }
+ if (bp2)
+ brelse(bp2);
+ if (bp)
+ brelse(bp);
+
+ /*
+ * Initialize the associated vnode
+ */
+ vp->v_type = IFTOVT(ip->inode.iso_mode);
+
+ if ( vp->v_type == VFIFO ) {
+#ifdef FIFO
+ extern int (**cd9660_fifoop_p)();
+ vp->v_op = cd9660_fifoop_p;
+#else
+ iso_iput(ip);
+ *ipp = 0;
+ return EOPNOTSUPP;
+#endif /* FIFO */
+ } else if ( vp->v_type == VCHR || vp->v_type == VBLK ) {
+ extern int (**cd9660_specop_p)();
+
+ /*
+ * if device, look at device number table for translation
+ */
+#ifdef ISODEVMAP
+ if (dp = iso_dmap(dev,ino,0))
+ ip->inode.iso_rdev = dp->d_dev;
+#endif
+ vp->v_op = cd9660_specop_p;
+ if (nvp = checkalias(vp, ip->inode.iso_rdev, mntp)) {
+ /*
+ * Reinitialize aliased inode.
+ */
+ vp = nvp;
+ iq = VTOI(vp);
+ iq->i_vnode = vp;
+ iq->i_flag = 0;
+ ISO_ILOCK(iq);
+ iq->i_dev = dev;
+ iq->i_number = ino;
+ iq->i_mnt = ip->i_mnt;
+ bcopy(&ip->iso_extent,&iq->iso_extent,
+ (char *)(ip + 1) - (char *)&ip->iso_extent);
+ insque(iq, ih);
+ /*
+ * Discard unneeded vnode
+ * (This introduces the need of INACTIVE modification)
+ */
+ ip->inode.iso_mode = 0;
+ iso_iput(ip);
+ ip = iq;
+ }
+ }
+
+ if (ip->iso_extent == imp->root_extent)
+ vp->v_flag |= VROOT;
+
+ *ipp = ip;
+ return 0;
+}
+
+/*
+ * Unlock and decrement the reference count of an inode structure.
+ */
+int
+iso_iput(ip)
+ register struct iso_node *ip;
+{
+
+ if ((ip->i_flag & ILOCKED) == 0)
+ panic("iso_iput");
+ ISO_IUNLOCK(ip);
+ vrele(ITOV(ip));
+ return (0);
+}
+
+/*
+ * Last reference to an inode, write the inode out and if necessary,
+ * truncate and deallocate the file.
+ */
+int
+cd9660_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ register struct iso_node *ip = VTOI(vp);
+ int mode, error = 0;
+
+ if (prtactive && vp->v_usecount != 0)
+ vprint("cd9660_inactive: pushing active", vp);
+
+ ip->i_flag = 0;
+ /*
+ * If we are done with the inode, reclaim it
+ * so that it can be reused immediately.
+ */
+ if (vp->v_usecount == 0 && ip->inode.iso_mode == 0)
+ vgone(vp);
+ return error;
+}
+
+/*
+ * Reclaim an inode so that it can be used for other purposes.
+ */
+int
+cd9660_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct iso_node *ip = VTOI(vp);
+ int i;
+
+ if (prtactive && vp->v_usecount != 0)
+ vprint("cd9660_reclaim: pushing active", vp);
+ /*
+ * Remove the inode from its hash chain.
+ */
+ remque(ip);
+ ip->i_forw = ip;
+ ip->i_back = ip;
+ /*
+ * Purge old data structures associated with the inode.
+ */
+ cache_purge(vp);
+ if (ip->i_devvp) {
+ vrele(ip->i_devvp);
+ ip->i_devvp = 0;
+ }
+ FREE(vp->v_data, M_ISOFSNODE);
+ vp->v_data = NULL;
+ return 0;
+}
+
+/*
+ * Lock an inode. If its already locked, set the WANT bit and sleep.
+ */
+int
+iso_ilock(ip)
+ register struct iso_node *ip;
+{
+
+ while (ip->i_flag & ILOCKED) {
+ ip->i_flag |= IWANT;
+ if (ip->i_spare0 == curproc->p_pid)
+ panic("locking against myself");
+ ip->i_spare1 = curproc->p_pid;
+ (void) sleep((caddr_t)ip, PINOD);
+ }
+ ip->i_spare1 = 0;
+ ip->i_spare0 = curproc->p_pid;
+ ip->i_flag |= ILOCKED;
+ return (0);
+}
+
+/*
+ * Unlock an inode. If WANT bit is on, wakeup.
+ */
+int
+iso_iunlock(ip)
+ register struct iso_node *ip;
+{
+
+ if ((ip->i_flag & ILOCKED) == 0)
+ vprint("iso_iunlock: unlocked inode", ITOV(ip));
+ ip->i_spare0 = 0;
+ ip->i_flag &= ~ILOCKED;
+ if (ip->i_flag&IWANT) {
+ ip->i_flag &= ~IWANT;
+ wakeup((caddr_t)ip);
+ }
+ return (0);
+}
+
+/*
+ * File attributes
+ */
+void
+cd9660_defattr(isodir,inop,bp)
+ struct iso_directory_record *isodir;
+ struct iso_node *inop;
+ struct buf *bp;
+{
+ struct buf *bp2 = NULL;
+ struct iso_mnt *imp;
+ struct iso_extended_attributes *ap = NULL;
+ int off;
+
+ if (isonum_711(isodir->flags)&2) {
+ inop->inode.iso_mode = S_IFDIR;
+ /*
+ * If we return 2, fts() will assume there are no subdirectories
+ * (just links for the path and .), so instead we return 1.
+ */
+ inop->inode.iso_links = 1;
+ } else {
+ inop->inode.iso_mode = S_IFREG;
+ inop->inode.iso_links = 1;
+ }
+ if (!bp
+ && ((imp = inop->i_mnt)->im_flags&ISOFSMNT_EXTATT)
+ && (off = isonum_711(isodir->ext_attr_length))) {
+ iso_blkatoff(inop,-off * imp->logical_block_size,&bp2);
+ bp = bp2;
+ }
+ if (bp) {
+ ap = (struct iso_extended_attributes *)bp->b_un.b_addr;
+
+ if (isonum_711(ap->version) == 1) {
+ if (!(ap->perm[0]&0x40))
+ inop->inode.iso_mode |= VEXEC >> 6;
+ if (!(ap->perm[0]&0x10))
+ inop->inode.iso_mode |= VREAD >> 6;
+ if (!(ap->perm[0]&4))
+ inop->inode.iso_mode |= VEXEC >> 3;
+ if (!(ap->perm[0]&1))
+ inop->inode.iso_mode |= VREAD >> 3;
+ if (!(ap->perm[1]&0x40))
+ inop->inode.iso_mode |= VEXEC;
+ if (!(ap->perm[1]&0x10))
+ inop->inode.iso_mode |= VREAD;
+ inop->inode.iso_uid = isonum_723(ap->owner); /* what about 0? */
+ inop->inode.iso_gid = isonum_723(ap->group); /* what about 0? */
+ } else
+ ap = NULL;
+ }
+ if (!ap) {
+ inop->inode.iso_mode |= VREAD|VEXEC|(VREAD|VEXEC)>>3|(VREAD|VEXEC)>>6;
+ inop->inode.iso_uid = (uid_t)0;
+ inop->inode.iso_gid = (gid_t)0;
+ }
+ if (bp2)
+ brelse(bp2);
+}
+
+/*
+ * Time stamps
+ */
+void
+cd9660_deftstamp(isodir,inop,bp)
+ struct iso_directory_record *isodir;
+ struct iso_node *inop;
+ struct buf *bp;
+{
+ struct buf *bp2 = NULL;
+ struct iso_mnt *imp;
+ struct iso_extended_attributes *ap = NULL;
+ int off;
+
+ if (!bp
+ && ((imp = inop->i_mnt)->im_flags&ISOFSMNT_EXTATT)
+ && (off = isonum_711(isodir->ext_attr_length))) {
+ iso_blkatoff(inop,-off * imp->logical_block_size,&bp2);
+ bp = bp2;
+ }
+ if (bp) {
+ ap = (struct iso_extended_attributes *)bp->b_un.b_addr;
+
+ if (isonum_711(ap->version) == 1) {
+ if (!cd9660_tstamp_conv17(ap->ftime,&inop->inode.iso_atime))
+ cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_atime);
+ if (!cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_ctime))
+ inop->inode.iso_ctime = inop->inode.iso_atime;
+ if (!cd9660_tstamp_conv17(ap->mtime,&inop->inode.iso_mtime))
+ inop->inode.iso_mtime = inop->inode.iso_ctime;
+ } else
+ ap = NULL;
+ }
+ if (!ap) {
+ cd9660_tstamp_conv7(isodir->date,&inop->inode.iso_ctime);
+ inop->inode.iso_atime = inop->inode.iso_ctime;
+ inop->inode.iso_mtime = inop->inode.iso_ctime;
+ }
+ if (bp2)
+ brelse(bp2);
+}
+
+int
+cd9660_tstamp_conv7(pi,pu)
+char *pi;
+struct timeval *pu;
+{
+ int i;
+ int crtime, days;
+ int y, m, d, hour, minute, second, tz;
+
+ y = pi[0] + 1900;
+ m = pi[1];
+ d = pi[2];
+ hour = pi[3];
+ minute = pi[4];
+ second = pi[5];
+ tz = pi[6];
+
+ if (y < 1970) {
+ pu->tv_sec = 0;
+ pu->tv_usec = 0;
+ return 0;
+ } else {
+#ifdef ORIGINAL
+ /* computes day number relative to Sept. 19th,1989 */
+ /* don't even *THINK* about changing formula. It works! */
+ days = 367*(y-1980)-7*(y+(m+9)/12)/4-3*((y+(m-9)/7)/100+1)/4+275*m/9+d-100;
+#else
+ /*
+ * Changed :-) to make it relative to Jan. 1st, 1970
+ * and to disambiguate negative division
+ */
+ days = 367*(y-1960)-7*(y+(m+9)/12)/4-3*((y+(m+9)/12-1)/100+1)/4+275*m/9+d-239;
+#endif
+ crtime = ((((days * 24) + hour) * 60 + minute) * 60) + second;
+
+ /* timezone offset is unreliable on some disks */
+ if (-48 <= tz && tz <= 52)
+ crtime += tz * 15 * 60;
+ }
+ pu->tv_sec = crtime;
+ pu->tv_usec = 0;
+ return 1;
+}
+
+static unsigned
+cd9660_chars2ui(begin,len)
+ unsigned char *begin;
+ int len;
+{
+ unsigned rc;
+
+ for (rc = 0; --len >= 0;) {
+ rc *= 10;
+ rc += *begin++ - '0';
+ }
+ return rc;
+}
+
+int
+cd9660_tstamp_conv17(pi,pu)
+ unsigned char *pi;
+ struct timeval *pu;
+{
+ unsigned char buf[7];
+
+ /* year:"0001"-"9999" -> -1900 */
+ buf[0] = cd9660_chars2ui(pi,4) - 1900;
+
+ /* month: " 1"-"12" -> 1 - 12 */
+ buf[1] = cd9660_chars2ui(pi + 4,2);
+
+ /* day: " 1"-"31" -> 1 - 31 */
+ buf[2] = cd9660_chars2ui(pi + 6,2);
+
+ /* hour: " 0"-"23" -> 0 - 23 */
+ buf[3] = cd9660_chars2ui(pi + 8,2);
+
+ /* minute:" 0"-"59" -> 0 - 59 */
+ buf[4] = cd9660_chars2ui(pi + 10,2);
+
+ /* second:" 0"-"59" -> 0 - 59 */
+ buf[5] = cd9660_chars2ui(pi + 12,2);
+
+ /* difference of GMT */
+ buf[6] = pi[16];
+
+ return cd9660_tstamp_conv7(buf,pu);
+}
+
+void
+isodirino(inump,isodir,imp)
+ ino_t *inump;
+ struct iso_directory_record *isodir;
+ struct iso_mnt *imp;
+{
+ *inump = (isonum_733(isodir->extent) + isonum_711(isodir->ext_attr_length))
+ * imp->logical_block_size;
+}
diff --git a/sys/fs/cd9660/cd9660_node.h b/sys/fs/cd9660/cd9660_node.h
new file mode 100644
index 0000000..45de67f
--- /dev/null
+++ b/sys/fs/cd9660/cd9660_node.h
@@ -0,0 +1,143 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cd9660_node.h 8.2 (Berkeley) 1/23/94
+ */
+
+/*
+ * Theoretically, directories can be more than 2Gb in length,
+ * however, in practice this seems unlikely. So, we define
+ * the type doff_t as a long to keep down the cost of doing
+ * lookup on a 32-bit machine. If you are porting to a 64-bit
+ * architecture, you should make doff_t the same as off_t.
+ */
+#define doff_t long
+
+typedef struct {
+ struct timespec iso_atime; /* time of last access */
+ struct timespec iso_mtime; /* time of last modification */
+ struct timespec iso_ctime; /* time file changed */
+ u_short iso_mode; /* files access mode and type */
+ uid_t iso_uid; /* owner user id */
+ gid_t iso_gid; /* owner group id */
+ short iso_links; /* links of file */
+ dev_t iso_rdev; /* Major/Minor number for special */
+} ISO_RRIP_INODE;
+
+#ifdef ISODEVMAP
+/*
+ * FOr device# (major,minor) translation table
+ */
+struct iso_dnode {
+ struct iso_dnode *d_chain[2]; /* hash chain, MUST be first */
+ dev_t i_dev; /* device where dnode resides */
+ ino_t i_number; /* the identity of the inode */
+ dev_t d_dev; /* device # for translation */
+};
+#define d_forw d_chain[0]
+#define d_back d_chain[1]
+#endif
+
+struct iso_node {
+ struct iso_node *i_chain[2]; /* hash chain, MUST be first */
+ struct vnode *i_vnode; /* vnode associated with this inode */
+ struct vnode *i_devvp; /* vnode for block I/O */
+ u_long i_flag; /* see below */
+ dev_t i_dev; /* device where inode resides */
+ ino_t i_number; /* the identity of the inode */
+ /* we use the actual starting block of the file */
+ struct iso_mnt *i_mnt; /* filesystem associated with this inode */
+ struct lockf *i_lockf; /* head of byte-level lock list */
+ doff_t i_endoff; /* end of useful stuff in directory */
+ doff_t i_diroff; /* offset in dir, where we found last entry */
+ doff_t i_offset; /* offset of free space in directory */
+ ino_t i_ino; /* inode number of found directory */
+ long i_spare0;
+ long i_spare1;
+
+ long iso_extent; /* extent of file */
+ long i_size;
+ long iso_start; /* actual start of data of file (may be different */
+ /* from iso_extent, if file has extended attributes) */
+ ISO_RRIP_INODE inode;
+};
+
+#define i_forw i_chain[0]
+#define i_back i_chain[1]
+
+/* flags */
+#define ILOCKED 0x0001 /* inode is locked */
+#define IWANT 0x0002 /* some process waiting on lock */
+#define IACC 0x0020 /* inode access time to be updated */
+
+#define VTOI(vp) ((struct iso_node *)(vp)->v_data)
+#define ITOV(ip) ((ip)->i_vnode)
+
+#define ISO_ILOCK(ip) iso_ilock(ip)
+#define ISO_IUNLOCK(ip) iso_iunlock(ip)
+
+/*
+ * Prototypes for ISOFS vnode operations
+ */
+int cd9660_lookup __P((struct vop_lookup_args *));
+int cd9660_open __P((struct vop_open_args *));
+int cd9660_close __P((struct vop_close_args *));
+int cd9660_access __P((struct vop_access_args *));
+int cd9660_getattr __P((struct vop_getattr_args *));
+int cd9660_read __P((struct vop_read_args *));
+int cd9660_ioctl __P((struct vop_ioctl_args *));
+int cd9660_select __P((struct vop_select_args *));
+int cd9660_mmap __P((struct vop_mmap_args *));
+int cd9660_seek __P((struct vop_seek_args *));
+int cd9660_readdir __P((struct vop_readdir_args *));
+int cd9660_abortop __P((struct vop_abortop_args *));
+int cd9660_inactive __P((struct vop_inactive_args *));
+int cd9660_reclaim __P((struct vop_reclaim_args *));
+int cd9660_bmap __P((struct vop_bmap_args *));
+int cd9660_lock __P((struct vop_lock_args *));
+int cd9660_unlock __P((struct vop_unlock_args *));
+int cd9660_strategy __P((struct vop_strategy_args *));
+int cd9660_print __P((struct vop_print_args *));
+int cd9660_islocked __P((struct vop_islocked_args *));
+void cd9660_defattr __P((struct iso_directory_record *,
+ struct iso_node *, struct buf *));
+void cd9660_deftstamp __P((struct iso_directory_record *,
+ struct iso_node *, struct buf *));
+#ifdef ISODEVMAP
+struct iso_dnode *iso_dmap __P((dev_t, ino_t, int));
+void iso_dunmap __P((dev_t));
+#endif
diff --git a/sys/fs/cd9660/cd9660_rrip.c b/sys/fs/cd9660/cd9660_rrip.c
new file mode 100644
index 0000000..0923fa0
--- /dev/null
+++ b/sys/fs/cd9660/cd9660_rrip.c
@@ -0,0 +1,685 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cd9660_rrip.c 8.2 (Berkeley) 1/23/94
+ */
+
+#include <sys/param.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <sys/time.h>
+
+#include <isofs/cd9660/iso.h>
+#include <isofs/cd9660/cd9660_node.h>
+#include <isofs/cd9660/cd9660_rrip.h>
+#include <isofs/cd9660/iso_rrip.h>
+
+/*
+ * POSIX file attribute
+ */
+static int
+cd9660_rrip_attr(p,ana)
+ ISO_RRIP_ATTR *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ ana->inop->inode.iso_mode = isonum_731(p->mode_l);
+ ana->inop->inode.iso_uid = (uid_t)isonum_731(p->uid_l);
+ ana->inop->inode.iso_gid = (gid_t)isonum_731(p->gid_l);
+ ana->inop->inode.iso_links = isonum_731(p->links_l);
+ ana->fields &= ~ISO_SUSP_ATTR;
+ return ISO_SUSP_ATTR;
+}
+
+static void
+cd9660_rrip_defattr(isodir,ana)
+ struct iso_directory_record *isodir;
+ ISO_RRIP_ANALYZE *ana;
+{
+ /* But this is a required field! */
+ printf("RRIP without PX field?\n");
+ cd9660_defattr(isodir,ana->inop,NULL);
+}
+
+/*
+ * Symbolic Links
+ */
+static int
+cd9660_rrip_slink(p,ana)
+ ISO_RRIP_SLINK *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ register ISO_RRIP_SLINK_COMPONENT *pcomp;
+ register ISO_RRIP_SLINK_COMPONENT *pcompe;
+ int len, wlen, cont;
+ char *outbuf, *inbuf;
+
+ pcomp = (ISO_RRIP_SLINK_COMPONENT *)p->component;
+ pcompe = (ISO_RRIP_SLINK_COMPONENT *)((char *)p + isonum_711(p->h.length));
+ len = *ana->outlen;
+ outbuf = ana->outbuf;
+ cont = ana->cont;
+
+ /*
+ * Gathering a Symbolic name from each component with path
+ */
+ for (;
+ pcomp < pcompe;
+ pcomp = (ISO_RRIP_SLINK_COMPONENT *)((char *)pcomp + ISO_RRIP_SLSIZ
+ + isonum_711(pcomp->clen))) {
+
+ if (!cont) {
+ if (len < ana->maxlen) {
+ len++;
+ *outbuf++ = '/';
+ }
+ }
+ cont = 0;
+
+ inbuf = "..";
+ wlen = 0;
+
+ switch (*pcomp->cflag) {
+
+ case ISO_SUSP_CFLAG_CURRENT:
+ /* Inserting Current */
+ wlen = 1;
+ break;
+
+ case ISO_SUSP_CFLAG_PARENT:
+ /* Inserting Parent */
+ wlen = 2;
+ break;
+
+ case ISO_SUSP_CFLAG_ROOT:
+ /* Inserting slash for ROOT */
+ /* start over from beginning(?) */
+ outbuf -= len;
+ len = 0;
+ break;
+
+ case ISO_SUSP_CFLAG_VOLROOT:
+ /* Inserting a mount point i.e. "/cdrom" */
+ /* same as above */
+ outbuf -= len;
+ len = 0;
+ inbuf = ana->imp->im_mountp->mnt_stat.f_mntonname;
+ wlen = strlen(inbuf);
+ break;
+
+ case ISO_SUSP_CFLAG_HOST:
+ /* Inserting hostname i.e. "kurt.tools.de" */
+ inbuf = hostname;
+ wlen = hostnamelen;
+ break;
+
+ case ISO_SUSP_CFLAG_CONTINUE:
+ cont = 1;
+ /* fall thru */
+ case 0:
+ /* Inserting component */
+ wlen = isonum_711(pcomp->clen);
+ inbuf = pcomp->name;
+ break;
+ default:
+ printf("RRIP with incorrect flags?");
+ wlen = ana->maxlen + 1;
+ break;
+ }
+
+ if (len + wlen > ana->maxlen) {
+ /* indicate error to caller */
+ ana->cont = 1;
+ ana->fields = 0;
+ ana->outbuf -= *ana->outlen;
+ *ana->outlen = 0;
+ return 0;
+ }
+
+ bcopy(inbuf,outbuf,wlen);
+ outbuf += wlen;
+ len += wlen;
+
+ }
+ ana->outbuf = outbuf;
+ *ana->outlen = len;
+ ana->cont = cont;
+
+ if (!isonum_711(p->flags)) {
+ ana->fields &= ~ISO_SUSP_SLINK;
+ return ISO_SUSP_SLINK;
+ }
+ return 0;
+}
+
+/*
+ * Alternate name
+ */
+static int
+cd9660_rrip_altname(p,ana)
+ ISO_RRIP_ALTNAME *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ char *inbuf;
+ int wlen;
+ int cont;
+
+ inbuf = "..";
+ wlen = 0;
+ cont = 0;
+
+ switch (*p->flags) {
+ case ISO_SUSP_CFLAG_CURRENT:
+ /* Inserting Current */
+ wlen = 1;
+ break;
+
+ case ISO_SUSP_CFLAG_PARENT:
+ /* Inserting Parent */
+ wlen = 2;
+ break;
+
+ case ISO_SUSP_CFLAG_HOST:
+ /* Inserting hostname i.e. "kurt.tools.de" */
+ inbuf = hostname;
+ wlen = hostnamelen;
+ break;
+
+ case ISO_SUSP_CFLAG_CONTINUE:
+ cont = 1;
+ /* fall thru */
+ case 0:
+ /* Inserting component */
+ wlen = isonum_711(p->h.length) - 5;
+ inbuf = (char *)p + 5;
+ break;
+
+ default:
+ printf("RRIP with incorrect NM flags?\n");
+ wlen = ana->maxlen + 1;
+ break;
+ }
+
+ if ((*ana->outlen += wlen) > ana->maxlen) {
+ /* treat as no name field */
+ ana->fields &= ~ISO_SUSP_ALTNAME;
+ ana->outbuf -= *ana->outlen - wlen;
+ *ana->outlen = 0;
+ return 0;
+ }
+
+ bcopy(inbuf,ana->outbuf,wlen);
+ ana->outbuf += wlen;
+
+ if (!cont) {
+ ana->fields &= ~ISO_SUSP_ALTNAME;
+ return ISO_SUSP_ALTNAME;
+ }
+ return 0;
+}
+
+static void
+cd9660_rrip_defname(isodir,ana)
+ struct iso_directory_record *isodir;
+ ISO_RRIP_ANALYZE *ana;
+{
+ strcpy(ana->outbuf,"..");
+ switch (*isodir->name) {
+ default:
+ isofntrans(isodir->name,isonum_711(isodir->name_len),
+ ana->outbuf,ana->outlen,
+ 1,isonum_711(isodir->flags)&4);
+ break;
+ case 0:
+ *ana->outlen = 1;
+ break;
+ case 1:
+ *ana->outlen = 2;
+ break;
+ }
+}
+
+/*
+ * Parent or Child Link
+ */
+static int
+cd9660_rrip_pclink(p,ana)
+ ISO_RRIP_CLINK *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ *ana->inump = isonum_733(p->dir_loc) << ana->imp->im_bshift;
+ ana->fields &= ~(ISO_SUSP_CLINK|ISO_SUSP_PLINK);
+ return *p->h.type == 'C' ? ISO_SUSP_CLINK : ISO_SUSP_PLINK;
+}
+
+/*
+ * Relocated directory
+ */
+static int
+cd9660_rrip_reldir(p,ana)
+ ISO_RRIP_RELDIR *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ /* special hack to make caller aware of RE field */
+ *ana->outlen = 0;
+ ana->fields = 0;
+ return ISO_SUSP_RELDIR|ISO_SUSP_ALTNAME|ISO_SUSP_CLINK|ISO_SUSP_PLINK;
+}
+
+static int
+cd9660_rrip_tstamp(p,ana)
+ ISO_RRIP_TSTAMP *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ unsigned char *ptime;
+
+ ptime = p->time;
+
+ /* Check a format of time stamp (7bytes/17bytes) */
+ if (!(*p->flags&ISO_SUSP_TSTAMP_FORM17)) {
+ if (*p->flags&ISO_SUSP_TSTAMP_CREAT)
+ ptime += 7;
+
+ if (*p->flags&ISO_SUSP_TSTAMP_MODIFY) {
+ cd9660_tstamp_conv7(ptime,&ana->inop->inode.iso_mtime);
+ ptime += 7;
+ } else
+ bzero(&ana->inop->inode.iso_mtime,sizeof(struct timeval));
+
+ if (*p->flags&ISO_SUSP_TSTAMP_ACCESS) {
+ cd9660_tstamp_conv7(ptime,&ana->inop->inode.iso_atime);
+ ptime += 7;
+ } else
+ ana->inop->inode.iso_atime = ana->inop->inode.iso_mtime;
+
+ if (*p->flags&ISO_SUSP_TSTAMP_ATTR)
+ cd9660_tstamp_conv7(ptime,&ana->inop->inode.iso_ctime);
+ else
+ ana->inop->inode.iso_ctime = ana->inop->inode.iso_mtime;
+
+ } else {
+ if (*p->flags&ISO_SUSP_TSTAMP_CREAT)
+ ptime += 17;
+
+ if (*p->flags&ISO_SUSP_TSTAMP_MODIFY) {
+ cd9660_tstamp_conv17(ptime,&ana->inop->inode.iso_mtime);
+ ptime += 17;
+ } else
+ bzero(&ana->inop->inode.iso_mtime,sizeof(struct timeval));
+
+ if (*p->flags&ISO_SUSP_TSTAMP_ACCESS) {
+ cd9660_tstamp_conv17(ptime,&ana->inop->inode.iso_atime);
+ ptime += 17;
+ } else
+ ana->inop->inode.iso_atime = ana->inop->inode.iso_mtime;
+
+ if (*p->flags&ISO_SUSP_TSTAMP_ATTR)
+ cd9660_tstamp_conv17(ptime,&ana->inop->inode.iso_ctime);
+ else
+ ana->inop->inode.iso_ctime = ana->inop->inode.iso_mtime;
+
+ }
+ ana->fields &= ~ISO_SUSP_TSTAMP;
+ return ISO_SUSP_TSTAMP;
+}
+
+static void
+cd9660_rrip_deftstamp(isodir,ana)
+ struct iso_directory_record *isodir;
+ ISO_RRIP_ANALYZE *ana;
+{
+ cd9660_deftstamp(isodir,ana->inop,NULL);
+}
+
+/*
+ * POSIX device modes
+ */
+static int
+cd9660_rrip_device(p,ana)
+ ISO_RRIP_DEVICE *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ unsigned high, low;
+
+ high = isonum_733(p->dev_t_high_l);
+ low = isonum_733(p->dev_t_low_l);
+
+ if ( high == 0 ) {
+ ana->inop->inode.iso_rdev = makedev( major(low), minor(low) );
+ } else {
+ ana->inop->inode.iso_rdev = makedev( high, minor(low) );
+ }
+ ana->fields &= ~ISO_SUSP_DEVICE;
+ return ISO_SUSP_DEVICE;
+}
+
+/*
+ * Flag indicating
+ */
+static int
+cd9660_rrip_idflag(p,ana)
+ ISO_RRIP_IDFLAG *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ ana->fields &= isonum_711(p->flags)|~0xff; /* don't touch high bits */
+ /* special handling of RE field */
+ if (ana->fields&ISO_SUSP_RELDIR)
+ return cd9660_rrip_reldir(p,ana);
+
+ return ISO_SUSP_IDFLAG;
+}
+
+/*
+ * Continuation pointer
+ */
+static int
+cd9660_rrip_cont(p,ana)
+ ISO_RRIP_CONT *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ ana->iso_ce_blk = isonum_733(p->location);
+ ana->iso_ce_off = isonum_733(p->offset);
+ ana->iso_ce_len = isonum_733(p->length);
+ return ISO_SUSP_CONT;
+}
+
+/*
+ * System Use end
+ */
+static int
+cd9660_rrip_stop(p,ana)
+ ISO_SUSP_HEADER *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ /* stop analyzing */
+ ana->fields = 0;
+ return ISO_SUSP_STOP;
+}
+
+/*
+ * Extension reference
+ */
+static int
+cd9660_rrip_extref(p,ana)
+ ISO_RRIP_EXTREF *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ if (isonum_711(p->len_id) != 10
+ || bcmp((char *)p + 8,"RRIP_1991A",10)
+ || isonum_711(p->version) != 1)
+ return 0;
+ ana->fields &= ~ISO_SUSP_EXTREF;
+ return ISO_SUSP_EXTREF;
+}
+
+typedef struct {
+ char type[2];
+ int (*func)();
+ void (*func2)();
+ int result;
+} RRIP_TABLE;
+
+static int
+cd9660_rrip_loop(isodir,ana,table)
+ struct iso_directory_record *isodir;
+ ISO_RRIP_ANALYZE *ana;
+ RRIP_TABLE *table;
+{
+ register RRIP_TABLE *ptable;
+ register ISO_SUSP_HEADER *phead;
+ register ISO_SUSP_HEADER *pend;
+ struct buf *bp = NULL;
+ int i;
+ char *pwhead;
+ int result;
+
+ /*
+ * Note: If name length is odd,
+ * it will be padding 1 byte after the name
+ */
+ pwhead = isodir->name + isonum_711(isodir->name_len);
+ if (!(isonum_711(isodir->name_len)&1))
+ pwhead++;
+
+ /* If it's not the '.' entry of the root dir obey SP field */
+ if (*isodir->name != 0
+ || isonum_733(isodir->extent) != ana->imp->root_extent)
+ pwhead += ana->imp->rr_skip;
+ else
+ pwhead += ana->imp->rr_skip0;
+
+ phead = (ISO_SUSP_HEADER *)pwhead;
+ pend = (ISO_SUSP_HEADER *)((char *)isodir + isonum_711(isodir->length));
+
+ result = 0;
+ while (1) {
+ ana->iso_ce_len = 0;
+ /*
+ * Note: "pend" should be more than one SUSP header
+ */
+ while (pend >= phead + 1) {
+ if (isonum_711(phead->version) == 1) {
+ for (ptable = table; ptable->func; ptable++) {
+ if (*phead->type == *ptable->type
+ && phead->type[1] == ptable->type[1]) {
+ result |= ptable->func(phead,ana);
+ break;
+ }
+ }
+ if (!ana->fields)
+ break;
+ }
+ /*
+ * move to next SUSP
+ * Hopefully this works with newer versions, too
+ */
+ phead = (ISO_SUSP_HEADER *)((char *)phead + isonum_711(phead->length));
+ }
+
+ if ( ana->fields && ana->iso_ce_len ) {
+ if (ana->iso_ce_blk >= ana->imp->volume_space_size
+ || ana->iso_ce_off + ana->iso_ce_len > ana->imp->logical_block_size
+ || bread(ana->imp->im_devvp,
+ ana->iso_ce_blk * ana->imp->logical_block_size / DEV_BSIZE,
+ ana->imp->logical_block_size,NOCRED,&bp))
+ /* what to do now? */
+ break;
+ phead = (ISO_SUSP_HEADER *)(bp->b_un.b_addr + ana->iso_ce_off);
+ pend = (ISO_SUSP_HEADER *) ((char *)phead + ana->iso_ce_len);
+ } else
+ break;
+ }
+ if (bp)
+ brelse(bp);
+ /*
+ * If we don't find the Basic SUSP stuffs, just set default value
+ * ( attribute/time stamp )
+ */
+ for (ptable = table; ptable->func2; ptable++)
+ if (!(ptable->result&result))
+ ptable->func2(isodir,ana);
+
+ return result;
+}
+
+static RRIP_TABLE rrip_table_analyze[] = {
+ { "PX", cd9660_rrip_attr, cd9660_rrip_defattr, ISO_SUSP_ATTR },
+ { "TF", cd9660_rrip_tstamp, cd9660_rrip_deftstamp, ISO_SUSP_TSTAMP },
+ { "PN", cd9660_rrip_device, 0, ISO_SUSP_DEVICE },
+ { "RR", cd9660_rrip_idflag, 0, ISO_SUSP_IDFLAG },
+ { "CE", cd9660_rrip_cont, 0, ISO_SUSP_CONT },
+ { "ST", cd9660_rrip_stop, 0, ISO_SUSP_STOP },
+ { "", 0, 0, 0 }
+};
+
+int
+cd9660_rrip_analyze(isodir,inop,imp)
+ struct iso_directory_record *isodir;
+ struct iso_node *inop;
+ struct iso_mnt *imp;
+{
+ ISO_RRIP_ANALYZE analyze;
+
+ analyze.inop = inop;
+ analyze.imp = imp;
+ analyze.fields = ISO_SUSP_ATTR|ISO_SUSP_TSTAMP|ISO_SUSP_DEVICE;
+
+ return cd9660_rrip_loop(isodir,&analyze,rrip_table_analyze);
+}
+
+/*
+ * Get Alternate Name from 'AL' record
+ * If either no AL record or 0 length,
+ * it will be return the translated ISO9660 name,
+ */
+static RRIP_TABLE rrip_table_getname[] = {
+ { "NM", cd9660_rrip_altname, cd9660_rrip_defname, ISO_SUSP_ALTNAME },
+ { "CL", cd9660_rrip_pclink, 0, ISO_SUSP_CLINK|ISO_SUSP_PLINK },
+ { "PL", cd9660_rrip_pclink, 0, ISO_SUSP_CLINK|ISO_SUSP_PLINK },
+ { "RE", cd9660_rrip_reldir, 0, ISO_SUSP_RELDIR },
+ { "RR", cd9660_rrip_idflag, 0, ISO_SUSP_IDFLAG },
+ { "CE", cd9660_rrip_cont, 0, ISO_SUSP_CONT },
+ { "ST", cd9660_rrip_stop, 0, ISO_SUSP_STOP },
+ { "", 0, 0, 0 }
+};
+
+int
+cd9660_rrip_getname(isodir,outbuf,outlen,inump,imp)
+ struct iso_directory_record *isodir;
+ char *outbuf;
+ u_short *outlen;
+ ino_t *inump;
+ struct iso_mnt *imp;
+{
+ ISO_RRIP_ANALYZE analyze;
+ RRIP_TABLE *tab;
+
+ analyze.outbuf = outbuf;
+ analyze.outlen = outlen;
+ analyze.maxlen = NAME_MAX;
+ analyze.inump = inump;
+ analyze.imp = imp;
+ analyze.fields = ISO_SUSP_ALTNAME|ISO_SUSP_RELDIR|ISO_SUSP_CLINK|ISO_SUSP_PLINK;
+ *outlen = 0;
+
+ tab = rrip_table_getname;
+ if (*isodir->name == 0
+ || *isodir->name == 1) {
+ cd9660_rrip_defname(isodir,&analyze);
+
+ analyze.fields &= ~ISO_SUSP_ALTNAME;
+ tab++;
+ }
+
+ return cd9660_rrip_loop(isodir,&analyze,tab);
+}
+
+/*
+ * Get Symbolic Name from 'SL' record
+ *
+ * Note: isodir should contains SL record!
+ */
+static RRIP_TABLE rrip_table_getsymname[] = {
+ { "SL", cd9660_rrip_slink, 0, ISO_SUSP_SLINK },
+ { "RR", cd9660_rrip_idflag, 0, ISO_SUSP_IDFLAG },
+ { "CE", cd9660_rrip_cont, 0, ISO_SUSP_CONT },
+ { "ST", cd9660_rrip_stop, 0, ISO_SUSP_STOP },
+ { "", 0, 0, 0 }
+};
+
+int
+cd9660_rrip_getsymname(isodir,outbuf,outlen,imp)
+ struct iso_directory_record *isodir;
+ char *outbuf;
+ u_short *outlen;
+ struct iso_mnt *imp;
+{
+ ISO_RRIP_ANALYZE analyze;
+
+ analyze.outbuf = outbuf;
+ analyze.outlen = outlen;
+ *outlen = 0;
+ analyze.maxlen = MAXPATHLEN;
+ analyze.cont = 1; /* don't start with a slash */
+ analyze.imp = imp;
+ analyze.fields = ISO_SUSP_SLINK;
+
+ return (cd9660_rrip_loop(isodir,&analyze,rrip_table_getsymname)&ISO_SUSP_SLINK);
+}
+
+static RRIP_TABLE rrip_table_extref[] = {
+ { "ER", cd9660_rrip_extref, 0, ISO_SUSP_EXTREF },
+ { "CE", cd9660_rrip_cont, 0, ISO_SUSP_CONT },
+ { "ST", cd9660_rrip_stop, 0, ISO_SUSP_STOP },
+ { "", 0, 0, 0 }
+};
+
+/*
+ * Check for Rock Ridge Extension and return offset of its fields.
+ * Note: We require the ER field.
+ */
+int
+cd9660_rrip_offset(isodir,imp)
+ struct iso_directory_record *isodir;
+ struct iso_mnt *imp;
+{
+ ISO_RRIP_OFFSET *p;
+ ISO_RRIP_ANALYZE analyze;
+
+ imp->rr_skip0 = 0;
+ p = (ISO_RRIP_OFFSET *)(isodir->name + 1);
+ if (bcmp(p,"SP\7\1\276\357",6)) {
+ /* Maybe, it's a CDROM XA disc? */
+ imp->rr_skip0 = 15;
+ p = (ISO_RRIP_OFFSET *)((char *)p + 15);
+ if (bcmp(p,"SP\7\1\276\357",6))
+ return -1;
+ }
+
+ analyze.imp = imp;
+ analyze.fields = ISO_SUSP_EXTREF;
+ if (!(cd9660_rrip_loop(isodir,&analyze,rrip_table_extref)&ISO_SUSP_EXTREF))
+ return -1;
+
+ return isonum_711(p->skip);
+}
diff --git a/sys/fs/cd9660/cd9660_rrip.h b/sys/fs/cd9660/cd9660_rrip.h
new file mode 100644
index 0000000..b401728
--- /dev/null
+++ b/sys/fs/cd9660/cd9660_rrip.h
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cd9660_rrip.h 8.1 (Berkeley) 1/21/94
+ */
+
+typedef struct {
+ char type [ISODCL ( 0, 1)];
+ unsigned char length [ISODCL ( 2, 2)]; /* 711 */
+ unsigned char version [ISODCL ( 3, 3)];
+} ISO_SUSP_HEADER;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char mode_l [ISODCL ( 4, 7)]; /* 731 */
+ char mode_m [ISODCL ( 8, 11)]; /* 732 */
+ char links_l [ISODCL ( 12, 15)]; /* 731 */
+ char links_m [ISODCL ( 16, 19)]; /* 732 */
+ char uid_l [ISODCL ( 20, 23)]; /* 731 */
+ char uid_m [ISODCL ( 24, 27)]; /* 732 */
+ char gid_l [ISODCL ( 28, 31)]; /* 731 */
+ char gid_m [ISODCL ( 32, 35)]; /* 732 */
+} ISO_RRIP_ATTR;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char dev_t_high_l [ISODCL ( 4, 7)]; /* 731 */
+ char dev_t_high_m [ISODCL ( 8, 11)]; /* 732 */
+ char dev_t_low_l [ISODCL ( 12, 15)]; /* 731 */
+ char dev_t_low_m [ISODCL ( 16, 19)]; /* 732 */
+} ISO_RRIP_DEVICE;
+
+#define ISO_SUSP_CFLAG_CONTINUE 0x01
+#define ISO_SUSP_CFLAG_CURRENT 0x02
+#define ISO_SUSP_CFLAG_PARENT 0x04
+#define ISO_SUSP_CFLAG_ROOT 0x08
+#define ISO_SUSP_CFLAG_VOLROOT 0x10
+#define ISO_SUSP_CFLAG_HOST 0x20
+
+typedef struct {
+ u_char cflag [ISODCL ( 1, 1)];
+ u_char clen [ISODCL ( 2, 2)];
+ u_char name [0];
+} ISO_RRIP_SLINK_COMPONENT;
+#define ISO_RRIP_SLSIZ 2
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char flags [ISODCL ( 4, 4)];
+ u_char component [ISODCL ( 5, 5)];
+} ISO_RRIP_SLINK;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char flags [ISODCL ( 4, 4)];
+} ISO_RRIP_ALTNAME;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char dir_loc [ISODCL ( 4, 11)]; /* 733 */
+} ISO_RRIP_CLINK;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char dir_loc [ISODCL ( 4, 11)]; /* 733 */
+} ISO_RRIP_PLINK;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+} ISO_RRIP_RELDIR;
+
+#define ISO_SUSP_TSTAMP_FORM17 0x80
+#define ISO_SUSP_TSTAMP_FORM7 0x00
+#define ISO_SUSP_TSTAMP_CREAT 0x01
+#define ISO_SUSP_TSTAMP_MODIFY 0x02
+#define ISO_SUSP_TSTAMP_ACCESS 0x04
+#define ISO_SUSP_TSTAMP_ATTR 0x08
+#define ISO_SUSP_TSTAMP_BACKUP 0x10
+#define ISO_SUSP_TSTAMP_EXPIRE 0x20
+#define ISO_SUSP_TSTAMP_EFFECT 0x40
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ unsigned char flags [ISODCL ( 4, 4)];
+ unsigned char time [ISODCL ( 5, 5)];
+} ISO_RRIP_TSTAMP;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ unsigned char flags [ISODCL ( 4, 4)];
+} ISO_RRIP_IDFLAG;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char len_id [ISODCL ( 4, 4)];
+ char len_des [ISODCL ( 5, 5)];
+ char len_src [ISODCL ( 6, 6)];
+ char version [ISODCL ( 7, 7)];
+} ISO_RRIP_EXTREF;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char check [ISODCL ( 4, 5)];
+ char skip [ISODCL ( 6, 6)];
+} ISO_RRIP_OFFSET;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char location [ISODCL ( 4, 11)];
+ char offset [ISODCL ( 12, 19)];
+ char length [ISODCL ( 20, 27)];
+} ISO_RRIP_CONT;
diff --git a/sys/fs/cd9660/cd9660_util.c b/sys/fs/cd9660/cd9660_util.c
new file mode 100644
index 0000000..39c5fe4
--- /dev/null
+++ b/sys/fs/cd9660/cd9660_util.c
@@ -0,0 +1,236 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cd9660_util.c 8.1 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/resourcevar.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <miscfs/specfs/specdev.h> /* XXX */
+#include <miscfs/fifofs/fifo.h> /* XXX */
+#include <sys/malloc.h>
+#include <sys/dir.h>
+
+#include <isofs/cd9660/iso.h>
+
+#ifdef __notanymore__
+int
+isonum_711 (p)
+unsigned char *p;
+{
+ return (*p);
+}
+
+int
+isonum_712 (p)
+signed char *p;
+{
+ return (*p);
+}
+
+int
+isonum_721 (p)
+unsigned char *p;
+{
+ /* little endian short */
+#if BYTE_ORDER != LITTLE_ENDIAN
+ printf ("isonum_721 called on non little-endian machine!\n");
+#endif
+
+ return *(short *)p;
+}
+
+int
+isonum_722 (p)
+unsigned char *p;
+{
+ /* big endian short */
+#if BYTE_ORDER != BIG_ENDIAN
+ printf ("isonum_722 called on non big-endian machine!\n");
+#endif
+
+ return *(short *)p;
+}
+
+int
+isonum_723 (p)
+unsigned char *p;
+{
+#if BYTE_ORDER == BIG_ENDIAN
+ return isonum_722 (p + 2);
+#elif BYTE_ORDER == LITTLE_ENDIAN
+ return isonum_721 (p);
+#else
+ printf ("isonum_723 unsupported byte order!\n");
+ return 0;
+#endif
+}
+
+int
+isonum_731 (p)
+unsigned char *p;
+{
+ /* little endian long */
+#if BYTE_ORDER != LITTLE_ENDIAN
+ printf ("isonum_731 called on non little-endian machine!\n");
+#endif
+
+ return *(long *)p;
+}
+
+int
+isonum_732 (p)
+unsigned char *p;
+{
+ /* big endian long */
+#if BYTE_ORDER != BIG_ENDIAN
+ printf ("isonum_732 called on non big-endian machine!\n");
+#endif
+
+ return *(long *)p;
+}
+
+int
+isonum_733 (p)
+unsigned char *p;
+{
+#if BYTE_ORDER == BIG_ENDIAN
+ return isonum_732 (p + 4);
+#elif BYTE_ORDER == LITTLE_ENDIAN
+ return isonum_731 (p);
+#else
+ printf ("isonum_733 unsupported byte order!\n");
+ return 0;
+#endif
+}
+#endif /* __notanymore__ */
+
+/*
+ * translate and compare a filename
+ * Note: Version number plus ';' may be omitted.
+ */
+int
+isofncmp(unsigned char *fn,int fnlen,unsigned char *isofn,int isolen)
+{
+ int i, j;
+ unsigned char c;
+
+ while (--fnlen >= 0) {
+ if (--isolen < 0)
+ return *fn;
+ if ((c = *isofn++) == ';') {
+ switch (*fn++) {
+ default:
+ return *--fn;
+ case 0:
+ return 0;
+ case ';':
+ break;
+ }
+ for (i = 0; --fnlen >= 0; i = i * 10 + *fn++ - '0') {
+ if (*fn < '0' || *fn > '9') {
+ return -1;
+ }
+ }
+ for (j = 0; --isolen >= 0; j = j * 10 + *isofn++ - '0');
+ return i - j;
+ }
+ if (c != *fn) {
+ if (c >= 'A' && c <= 'Z') {
+ if (c + ('a' - 'A') != *fn) {
+ if (*fn >= 'a' && *fn <= 'z')
+ return *fn - ('a' - 'A') - c;
+ else
+ return *fn - c;
+ }
+ } else
+ return *fn - c;
+ }
+ fn++;
+ }
+ if (isolen > 0) {
+ switch (*isofn) {
+ default:
+ return -1;
+ case '.':
+ if (isofn[1] != ';')
+ return -1;
+ case ';':
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/*
+ * translate a filename
+ */
+void
+isofntrans(unsigned char *infn,int infnlen,
+ unsigned char *outfn,unsigned short *outfnlen,
+ int original,int assoc)
+{
+ int fnidx = 0;
+
+ if (assoc) {
+ *outfn++ = ASSOCCHAR;
+ fnidx++;
+ }
+ for (; fnidx < infnlen; fnidx++) {
+ char c = *infn++;
+
+ if (!original && c >= 'A' && c <= 'Z')
+ *outfn++ = c + ('a' - 'A');
+ else if (!original && c == '.' && *infn == ';')
+ break;
+ else if (!original && c == ';')
+ break;
+ else
+ *outfn++ = c;
+ }
+ *outfnlen = fnidx;
+}
diff --git a/sys/fs/cd9660/cd9660_vfsops.c b/sys/fs/cd9660/cd9660_vfsops.c
new file mode 100644
index 0000000..bc48367
--- /dev/null
+++ b/sys/fs/cd9660/cd9660_vfsops.c
@@ -0,0 +1,688 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cd9660_vfsops.c 8.3 (Berkeley) 1/31/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/specfs/specdev.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/dkbad.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+
+#include <isofs/cd9660/iso.h>
+#include <isofs/cd9660/cd9660_node.h>
+
+extern int enodev ();
+
+struct vfsops cd9660_vfsops = {
+ cd9660_mount,
+ cd9660_start,
+ cd9660_unmount,
+ cd9660_root,
+ cd9660_quotactl,
+ cd9660_statfs,
+ cd9660_sync,
+ cd9660_vget,
+ cd9660_fhtovp,
+ cd9660_vptofh,
+ cd9660_init,
+};
+
+/*
+ * Called by vfs_mountroot when iso is going to be mounted as root.
+ *
+ * Name is updated by mount(8) after booting.
+ */
+#define ROOTNAME "root_device"
+
+static iso_mountfs();
+
+int
+cd9660_mountroot()
+{
+ register struct mount *mp;
+ extern struct vnode *rootvp;
+ struct proc *p = curproc; /* XXX */
+ struct iso_mnt *imp;
+ register struct fs *fs;
+ u_int size;
+ int error;
+ struct iso_args args;
+
+ /*
+ * Get vnodes for swapdev and rootdev.
+ */
+ if (bdevvp(swapdev, &swapdev_vp) || bdevvp(rootdev, &rootvp))
+ panic("cd9660_mountroot: can't setup bdevvp's");
+
+ mp = malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK);
+ bzero((char *)mp, (u_long)sizeof(struct mount));
+ mp->mnt_op = &cd9660_vfsops;
+ mp->mnt_flag = MNT_RDONLY;
+ args.flags = ISOFSMNT_ROOT;
+ if (error = iso_mountfs(rootvp, mp, p, &args)) {
+ free(mp, M_MOUNT);
+ return (error);
+ }
+ if (error = vfs_lock(mp)) {
+ (void)cd9660_unmount(mp, 0, p);
+ free(mp, M_MOUNT);
+ return (error);
+ }
+ TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
+ mp->mnt_flag |= MNT_ROOTFS;
+ mp->mnt_vnodecovered = NULLVP;
+ imp = VFSTOISOFS(mp);
+ bzero(imp->im_fsmnt, sizeof(imp->im_fsmnt));
+ imp->im_fsmnt[0] = '/';
+ bcopy((caddr_t)imp->im_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname,
+ MNAMELEN);
+ (void) copystr(ROOTNAME, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ (void) cd9660_statfs(mp, &mp->mnt_stat, p);
+ vfs_unlock(mp);
+ return (0);
+}
+
+/*
+ * Flag to allow forcible unmounting.
+ */
+int iso_doforce = 1;
+
+/*
+ * VFS Operations.
+ *
+ * mount system call
+ */
+int
+cd9660_mount(mp, path, data, ndp, p)
+ register struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ struct vnode *devvp;
+ struct iso_args args;
+ u_int size;
+ int error;
+ struct iso_mnt *imp = 0;
+
+ if (error = copyin(data, (caddr_t)&args, sizeof (struct iso_args)))
+ return (error);
+
+ if ((mp->mnt_flag & MNT_RDONLY) == 0)
+ return (EROFS);
+
+ /*
+ * If updating, check whether changing from read-only to
+ * read/write; if there is no device name, that's all we do.
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ imp = VFSTOISOFS(mp);
+ if (args.fspec == 0)
+ return (vfs_export(mp, &imp->im_export, &args.export));
+ }
+ /*
+ * Not an update, or updating the name: look up the name
+ * and verify that it refers to a sensible block device.
+ */
+ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
+ if (error = namei(ndp))
+ return (error);
+ devvp = ndp->ni_vp;
+
+ if (devvp->v_type != VBLK) {
+ vrele(devvp);
+ return ENOTBLK;
+ }
+ if (major(devvp->v_rdev) >= nblkdev) {
+ vrele(devvp);
+ return ENXIO;
+ }
+ if ((mp->mnt_flag & MNT_UPDATE) == 0)
+ error = iso_mountfs(devvp, mp, p, &args);
+ else {
+ if (devvp != imp->im_devvp)
+ error = EINVAL; /* needs translation */
+ else
+ vrele(devvp);
+ }
+ if (error) {
+ vrele(devvp);
+ return error;
+ }
+ imp = VFSTOISOFS(mp);
+ (void) copyinstr(path, imp->im_fsmnt, sizeof(imp->im_fsmnt)-1, &size);
+ bzero(imp->im_fsmnt + size, sizeof(imp->im_fsmnt) - size);
+ bcopy((caddr_t)imp->im_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname,
+ MNAMELEN);
+ (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ (void) cd9660_statfs(mp, &mp->mnt_stat, p);
+ return 0;
+}
+
+/*
+ * Common code for mount and mountroot
+ */
+static int
+iso_mountfs(devvp, mp, p, argp)
+ register struct vnode *devvp;
+ struct mount *mp;
+ struct proc *p;
+ struct iso_args *argp;
+{
+ register struct iso_mnt *isomp = (struct iso_mnt *)0;
+ struct buf *bp = NULL;
+ dev_t dev = devvp->v_rdev;
+ caddr_t base, space;
+ int havepart = 0, blks;
+ int error = EINVAL, i, size;
+ int needclose = 0;
+ int ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
+ extern struct vnode *rootvp;
+ int j;
+ int iso_bsize;
+ int iso_blknum;
+ struct iso_volume_descriptor *vdp;
+ struct iso_primary_descriptor *pri;
+ struct iso_directory_record *rootp;
+ int logical_block_size;
+
+ if (!ronly)
+ return EROFS;
+
+ /*
+ * Disallow multiple mounts of the same device.
+ * Disallow mounting of a device that is currently in use
+ * (except for root, which might share swap device for miniroot).
+ * Flush out any old buffers remaining from a previous use.
+ */
+ if (error = vfs_mountedon(devvp))
+ return error;
+ if (vcount(devvp) > 1 && devvp != rootvp)
+ return EBUSY;
+ if (error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0))
+ return (error);
+
+ if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p))
+ return error;
+ needclose = 1;
+
+ /* This is the "logical sector size". The standard says this
+ * should be 2048 or the physical sector size on the device,
+ * whichever is greater. For now, we'll just use a constant.
+ */
+ iso_bsize = ISO_DEFAULT_BLOCK_SIZE;
+
+ for (iso_blknum = 16; iso_blknum < 100; iso_blknum++) {
+ if (error = bread (devvp, btodb(iso_blknum * iso_bsize),
+ iso_bsize, NOCRED, &bp))
+ goto out;
+
+ vdp = (struct iso_volume_descriptor *)bp->b_un.b_addr;
+ if (bcmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) != 0) {
+ error = EINVAL;
+ goto out;
+ }
+
+ if (isonum_711 (vdp->type) == ISO_VD_END) {
+ error = EINVAL;
+ goto out;
+ }
+
+ if (isonum_711 (vdp->type) == ISO_VD_PRIMARY)
+ break;
+ brelse(bp);
+ }
+
+ if (isonum_711 (vdp->type) != ISO_VD_PRIMARY) {
+ error = EINVAL;
+ goto out;
+ }
+
+ pri = (struct iso_primary_descriptor *)vdp;
+
+ logical_block_size = isonum_723 (pri->logical_block_size);
+
+ if (logical_block_size < DEV_BSIZE || logical_block_size > MAXBSIZE
+ || (logical_block_size & (logical_block_size - 1)) != 0) {
+ error = EINVAL;
+ goto out;
+ }
+
+ rootp = (struct iso_directory_record *)pri->root_directory_record;
+
+ isomp = malloc(sizeof *isomp, M_ISOFSMNT, M_WAITOK);
+ bzero((caddr_t)isomp, sizeof *isomp);
+ isomp->logical_block_size = logical_block_size;
+ isomp->volume_space_size = isonum_733 (pri->volume_space_size);
+ bcopy (rootp, isomp->root, sizeof isomp->root);
+ isomp->root_extent = isonum_733 (rootp->extent);
+ isomp->root_size = isonum_733 (rootp->size);
+
+ isomp->im_bmask = logical_block_size - 1;
+ isomp->im_bshift = 0;
+ while ((1 << isomp->im_bshift) < isomp->logical_block_size)
+ isomp->im_bshift++;
+
+ bp->b_flags |= B_AGE;
+ brelse(bp);
+ bp = NULL;
+
+ mp->mnt_data = (qaddr_t)isomp;
+ mp->mnt_stat.f_fsid.val[0] = (long)dev;
+ mp->mnt_stat.f_fsid.val[1] = MOUNT_CD9660;
+ mp->mnt_maxsymlinklen = 0;
+ mp->mnt_flag |= MNT_LOCAL;
+ isomp->im_mountp = mp;
+ isomp->im_dev = dev;
+ isomp->im_devvp = devvp;
+
+ devvp->v_specflags |= SI_MOUNTEDON;
+
+ /* Check the Rock Ridge Extention support */
+ if (!(argp->flags & ISOFSMNT_NORRIP)) {
+ if (error = bread (isomp->im_devvp,
+ (isomp->root_extent + isonum_711(rootp->ext_attr_length))
+ * isomp->logical_block_size / DEV_BSIZE,
+ isomp->logical_block_size,NOCRED,&bp))
+ goto out;
+
+ rootp = (struct iso_directory_record *)bp->b_un.b_addr;
+
+ if ((isomp->rr_skip = cd9660_rrip_offset(rootp,isomp)) < 0) {
+ argp->flags |= ISOFSMNT_NORRIP;
+ } else {
+ argp->flags &= ~ISOFSMNT_GENS;
+ }
+
+ /*
+ * The contents are valid,
+ * but they will get reread as part of another vnode, so...
+ */
+ bp->b_flags |= B_AGE;
+ brelse(bp);
+ bp = NULL;
+ }
+ isomp->im_flags = argp->flags&(ISOFSMNT_NORRIP|ISOFSMNT_GENS|ISOFSMNT_EXTATT);
+ switch (isomp->im_flags&(ISOFSMNT_NORRIP|ISOFSMNT_GENS)) {
+ default:
+ isomp->iso_ftype = ISO_FTYPE_DEFAULT;
+ break;
+ case ISOFSMNT_GENS|ISOFSMNT_NORRIP:
+ isomp->iso_ftype = ISO_FTYPE_9660;
+ break;
+ case 0:
+ isomp->iso_ftype = ISO_FTYPE_RRIP;
+ break;
+ }
+
+ return 0;
+out:
+ if (bp)
+ brelse(bp);
+ if (needclose)
+ (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p);
+ if (isomp) {
+ free((caddr_t)isomp, M_ISOFSMNT);
+ mp->mnt_data = (qaddr_t)0;
+ }
+ return error;
+}
+
+/*
+ * Make a filesystem operational.
+ * Nothing to do at the moment.
+ */
+/* ARGSUSED */
+int
+cd9660_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ return 0;
+}
+
+/*
+ * unmount system call
+ */
+int
+cd9660_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ register struct iso_mnt *isomp;
+ int i, error, ronly, flags = 0;
+
+ if (mntflags & MNT_FORCE) {
+ if (!iso_doforce || (mp->mnt_flag & MNT_ROOTFS))
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+#if 0
+ mntflushbuf(mp, 0);
+ if (mntinvalbuf(mp))
+ return EBUSY;
+#endif
+ if (error = vflush(mp, NULLVP, flags))
+ return (error);
+
+ isomp = VFSTOISOFS(mp);
+
+#ifdef ISODEVMAP
+ if (isomp->iso_ftype == ISO_FTYPE_RRIP)
+ iso_dunmap(isomp->im_dev);
+#endif
+
+ isomp->im_devvp->v_specflags &= ~SI_MOUNTEDON;
+ error = VOP_CLOSE(isomp->im_devvp, FREAD, NOCRED, p);
+ vrele(isomp->im_devvp);
+ free((caddr_t)isomp, M_ISOFSMNT);
+ mp->mnt_data = (qaddr_t)0;
+ mp->mnt_flag &= ~MNT_LOCAL;
+ return (error);
+}
+
+/*
+ * Return root of a filesystem
+ */
+int
+cd9660_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ register struct iso_node *ip;
+ struct iso_node tip, *nip;
+ struct vnode tvp;
+ int error;
+ struct iso_mnt *imp = VFSTOISOFS (mp);
+ struct iso_directory_record *dp;
+
+ tvp.v_mount = mp;
+ tvp.v_data = &tip;
+ ip = VTOI(&tvp);
+ ip->i_vnode = &tvp;
+ ip->i_dev = imp->im_dev;
+ ip->i_diroff = 0;
+ dp = (struct iso_directory_record *)imp->root;
+ isodirino(&ip->i_number,dp,imp);
+
+ /*
+ * With RRIP we must use the `.' entry of the root directory.
+ * Simply tell iget, that it's a relocated directory.
+ */
+ error = iso_iget(ip,ip->i_number,
+ imp->iso_ftype == ISO_FTYPE_RRIP,
+ &nip,dp);
+ if (error)
+ return error;
+ *vpp = ITOV(nip);
+ return 0;
+}
+
+/*
+ * Do operations associated with quotas, not supported
+ */
+/* ARGSUSED */
+int
+cd9660_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * Get file system statistics.
+ */
+int
+cd9660_statfs(mp, sbp, p)
+ struct mount *mp;
+ register struct statfs *sbp;
+ struct proc *p;
+{
+ register struct iso_mnt *isomp;
+ register struct fs *fs;
+
+ isomp = VFSTOISOFS(mp);
+
+ sbp->f_type = MOUNT_CD9660;
+ sbp->f_bsize = isomp->logical_block_size;
+ sbp->f_iosize = sbp->f_bsize; /* XXX */
+ sbp->f_blocks = isomp->volume_space_size;
+ sbp->f_bfree = 0; /* total free blocks */
+ sbp->f_bavail = 0; /* blocks free for non superuser */
+ sbp->f_files = 0; /* total files */
+ sbp->f_ffree = 0; /* free file nodes */
+ if (sbp != &mp->mnt_stat) {
+ bcopy((caddr_t)mp->mnt_stat.f_mntonname,
+ (caddr_t)&sbp->f_mntonname[0], MNAMELEN);
+ bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
+ (caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
+ }
+ /* Use the first spare for flags: */
+ sbp->f_spare[0] = isomp->im_flags;
+ return 0;
+}
+
+/* ARGSUSED */
+int
+cd9660_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+ return (0);
+}
+
+/*
+ * Flat namespace lookup.
+ * Currently unsupported.
+ */
+/* ARGSUSED */
+int
+cd9660_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * File handle to vnode
+ *
+ * Have to be really careful about stale file handles:
+ * - check that the inode number is in range
+ * - call iget() to get the locked inode
+ * - check for an unallocated inode (i_mode == 0)
+ * - check that the generation number matches
+ */
+
+struct ifid {
+ ushort ifid_len;
+ ushort ifid_pad;
+ int ifid_ino;
+ long ifid_start;
+};
+
+/* ARGSUSED */
+int
+cd9660_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
+ register struct mount *mp;
+ struct fid *fhp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred **credanonp;
+{
+ struct vnode tvp;
+ int error;
+ int lbn, off;
+ struct ifid *ifhp;
+ struct iso_mnt *imp;
+ struct buf *bp;
+ struct iso_directory_record *dirp;
+ struct iso_node tip, *ip, *nip;
+ struct netcred *np;
+
+ imp = VFSTOISOFS (mp);
+ ifhp = (struct ifid *)fhp;
+
+#ifdef ISOFS_DBG
+ printf("fhtovp: ino %d, start %ld\n",
+ ifhp->ifid_ino, ifhp->ifid_start);
+#endif
+
+ np = vfs_export_lookup(mp, &imp->im_export, nam);
+ if (np == NULL)
+ return (EACCES);
+
+ lbn = iso_lblkno(imp, ifhp->ifid_ino);
+ if (lbn >= imp->volume_space_size) {
+ printf("fhtovp: lbn exceed volume space %d\n", lbn);
+ return (ESTALE);
+ }
+
+ off = iso_blkoff(imp, ifhp->ifid_ino);
+ if (off + ISO_DIRECTORY_RECORD_SIZE > imp->logical_block_size) {
+ printf("fhtovp: crosses block boundary %d\n",
+ off + ISO_DIRECTORY_RECORD_SIZE);
+ return (ESTALE);
+ }
+
+ error = bread(imp->im_devvp, btodb(lbn * imp->logical_block_size),
+ imp->logical_block_size, NOCRED, &bp);
+ if (error) {
+ printf("fhtovp: bread error %d\n",error);
+ brelse(bp);
+ return (error);
+ }
+
+ dirp = (struct iso_directory_record *)(bp->b_un.b_addr + off);
+ if (off + isonum_711(dirp->length) > imp->logical_block_size) {
+ brelse(bp);
+ printf("fhtovp: directory crosses block boundary %d[off=%d/len=%d]\n",
+ off+isonum_711(dirp->length), off,
+ isonum_711(dirp->length));
+ return (ESTALE);
+ }
+
+ if (isonum_733(dirp->extent) + isonum_711(dirp->ext_attr_length) !=
+ ifhp->ifid_start) {
+ brelse(bp);
+ printf("fhtovp: file start miss %d vs %d\n",
+ isonum_733(dirp->extent)+isonum_711(dirp->ext_attr_length),
+ ifhp->ifid_start);
+ return (ESTALE);
+ }
+ brelse(bp);
+
+ ip = &tip;
+ tvp.v_mount = mp;
+ tvp.v_data = ip;
+ ip->i_vnode = &tvp;
+ ip->i_dev = imp->im_dev;
+ if (error = iso_iget(ip, ifhp->ifid_ino, 0, &nip, dirp)) {
+ *vpp = NULLVP;
+ printf("fhtovp: failed to get inode\n");
+ return (error);
+ }
+ ip = nip;
+ /*
+ * XXX need generation number?
+ */
+ if (ip->inode.iso_mode == 0) {
+ iso_iput(ip);
+ *vpp = NULLVP;
+ printf("fhtovp: inode mode == 0\n");
+ return (ESTALE);
+ }
+ *vpp = ITOV(ip);
+ *exflagsp = np->netc_exflags;
+ *credanonp = &np->netc_anon;
+ return 0;
+}
+
+/*
+ * Vnode pointer to File handle
+ */
+/* ARGSUSED */
+int
+cd9660_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+ register struct iso_node *ip = VTOI(vp);
+ register struct ifid *ifhp;
+ register struct iso_mnt *mp = ip->i_mnt;
+
+ ifhp = (struct ifid *)fhp;
+ ifhp->ifid_len = sizeof(struct ifid);
+
+ ifhp->ifid_ino = ip->i_number;
+ ifhp->ifid_start = ip->iso_start;
+
+#ifdef ISOFS_DBG
+ printf("vptofh: ino %d, start %ld\n",
+ ifhp->ifid_ino,ifhp->ifid_start);
+#endif
+ return 0;
+}
diff --git a/sys/fs/cd9660/cd9660_vnops.c b/sys/fs/cd9660/cd9660_vnops.c
new file mode 100644
index 0000000..7a2964b
--- /dev/null
+++ b/sys/fs/cd9660/cd9660_vnops.c
@@ -0,0 +1,1041 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cd9660_vnops.c 8.3 (Berkeley) 1/23/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/resourcevar.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <miscfs/specfs/specdev.h>
+#include <miscfs/fifofs/fifo.h>
+#include <sys/malloc.h>
+#include <sys/dir.h>
+
+#include <isofs/cd9660/iso.h>
+#include <isofs/cd9660/cd9660_node.h>
+#include <isofs/cd9660/iso_rrip.h>
+
+#if 0
+/*
+ * Mknod vnode call
+ * Actually remap the device number
+ */
+cd9660_mknod(ndp, vap, cred, p)
+ struct nameidata *ndp;
+ struct ucred *cred;
+ struct vattr *vap;
+ struct proc *p;
+{
+#ifndef ISODEVMAP
+ free(ndp->ni_pnbuf, M_NAMEI);
+ vput(ndp->ni_dvp);
+ vput(ndp->ni_vp);
+ return EINVAL;
+#else
+ register struct vnode *vp;
+ struct iso_node *ip;
+ struct iso_dnode *dp;
+ int error;
+
+ vp = ndp->ni_vp;
+ ip = VTOI(vp);
+
+ if (ip->i_mnt->iso_ftype != ISO_FTYPE_RRIP
+ || vap->va_type != vp->v_type
+ || (vap->va_type != VCHR && vap->va_type != VBLK)) {
+ free(ndp->ni_pnbuf, M_NAMEI);
+ vput(ndp->ni_dvp);
+ vput(ndp->ni_vp);
+ return EINVAL;
+ }
+
+ dp = iso_dmap(ip->i_dev,ip->i_number,1);
+ if (ip->inode.iso_rdev == vap->va_rdev || vap->va_rdev == VNOVAL) {
+ /* same as the unmapped one, delete the mapping */
+ remque(dp);
+ FREE(dp,M_CACHE);
+ } else
+ /* enter new mapping */
+ dp->d_dev = vap->va_rdev;
+
+ /*
+ * Remove inode so that it will be reloaded by iget and
+ * checked to see if it is an alias of an existing entry
+ * in the inode cache.
+ */
+ vput(vp);
+ vp->v_type = VNON;
+ vgone(vp);
+ return (0);
+#endif
+}
+#endif
+
+/*
+ * Open called.
+ *
+ * Nothing to do.
+ */
+/* ARGSUSED */
+int
+cd9660_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ return (0);
+}
+
+/*
+ * Close called
+ *
+ * Update the times on the inode on writeable file systems.
+ */
+/* ARGSUSED */
+int
+cd9660_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ return (0);
+}
+
+/*
+ * Check mode permission on inode pointer. Mode is READ, WRITE or EXEC.
+ * The mode is shifted to select the owner/group/other fields. The
+ * super user is granted all permissions.
+ */
+/* ARGSUSED */
+int
+cd9660_access(ap)
+ struct vop_access_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ return (0);
+}
+
+int
+cd9660_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+
+{
+ struct vnode *vp = ap->a_vp;
+ register struct vattr *vap = ap->a_vap;
+ register struct iso_node *ip = VTOI(vp);
+ int i;
+
+ vap->va_fsid = ip->i_dev;
+ vap->va_fileid = ip->i_number;
+
+ vap->va_mode = ip->inode.iso_mode;
+ vap->va_nlink = ip->inode.iso_links;
+ vap->va_uid = ip->inode.iso_uid;
+ vap->va_gid = ip->inode.iso_gid;
+ vap->va_atime = ip->inode.iso_atime;
+ vap->va_mtime = ip->inode.iso_mtime;
+ vap->va_ctime = ip->inode.iso_ctime;
+ vap->va_rdev = ip->inode.iso_rdev;
+
+ vap->va_size = (u_quad_t) ip->i_size;
+ vap->va_flags = 0;
+ vap->va_gen = 1;
+ vap->va_blocksize = ip->i_mnt->logical_block_size;
+ vap->va_bytes = (u_quad_t) ip->i_size;
+ vap->va_type = vp->v_type;
+ return (0);
+}
+
+#if ISO_DEFAULT_BLOCK_SIZE >= NBPG
+#ifdef DEBUG
+extern int doclusterread;
+#else
+#define doclusterread 1
+#endif
+#else
+/* XXX until cluster routines can handle block sizes less than one page */
+#define doclusterread 0
+#endif
+
+/*
+ * Vnode op for reading.
+ */
+int
+cd9660_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ register struct uio *uio = ap->a_uio;
+ register struct iso_node *ip = VTOI(vp);
+ register struct iso_mnt *imp;
+ struct buf *bp;
+ daddr_t lbn, bn, rablock;
+ off_t diff;
+ int rasize, error = 0;
+ long size, n, on;
+
+ if (uio->uio_resid == 0)
+ return (0);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+ ip->i_flag |= IACC;
+ imp = ip->i_mnt;
+ do {
+ lbn = iso_lblkno(imp, uio->uio_offset);
+ on = iso_blkoff(imp, uio->uio_offset);
+ n = min((unsigned)(imp->logical_block_size - on),
+ uio->uio_resid);
+ diff = (off_t)ip->i_size - uio->uio_offset;
+ if (diff <= 0)
+ return (0);
+ if (diff < n)
+ n = diff;
+ size = iso_blksize(imp, ip, lbn);
+ rablock = lbn + 1;
+ if (doclusterread) {
+ if (iso_lblktosize(imp, rablock) <= ip->i_size)
+ error = cluster_read(vp, (off_t)ip->i_size,
+ lbn, size, NOCRED, &bp);
+ else
+ error = bread(vp, lbn, size, NOCRED, &bp);
+ } else {
+ if (vp->v_lastr + 1 == lbn &&
+ iso_lblktosize(imp, rablock) < ip->i_size) {
+ rasize = iso_blksize(imp, ip, rablock);
+ error = breadn(vp, lbn, size, &rablock,
+ &rasize, 1, NOCRED, &bp);
+ } else
+ error = bread(vp, lbn, size, NOCRED, &bp);
+ }
+ vp->v_lastr = lbn;
+ n = min(n, size - bp->b_resid);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+
+ error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
+ if (n + on == imp->logical_block_size ||
+ uio->uio_offset == (off_t)ip->i_size)
+ bp->b_flags |= B_AGE;
+ brelse(bp);
+ } while (error == 0 && uio->uio_resid > 0 && n != 0);
+ return (error);
+}
+
+/* ARGSUSED */
+int
+cd9660_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ printf("You did ioctl for isofs !!\n");
+ return (ENOTTY);
+}
+
+/* ARGSUSED */
+int
+cd9660_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ /*
+ * We should really check to see if I/O is possible.
+ */
+ return (1);
+}
+
+/*
+ * Mmap a file
+ *
+ * NB Currently unsupported.
+ */
+/* ARGSUSED */
+int
+cd9660_mmap(ap)
+ struct vop_mmap_args /* {
+ struct vnode *a_vp;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (EINVAL);
+}
+
+/*
+ * Seek on a file
+ *
+ * Nothing to do, so just return.
+ */
+/* ARGSUSED */
+int
+cd9660_seek(ap)
+ struct vop_seek_args /* {
+ struct vnode *a_vp;
+ off_t a_oldoff;
+ off_t a_newoff;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * Structure for reading directories
+ */
+struct isoreaddir {
+ struct dirent saveent;
+ struct dirent assocent;
+ struct dirent current;
+ off_t saveoff;
+ off_t assocoff;
+ off_t curroff;
+ struct uio *uio;
+ off_t uio_off;
+ u_int *cookiep;
+ int ncookies;
+ int eof;
+};
+
+static int
+iso_uiodir(idp,dp,off)
+ struct isoreaddir *idp;
+ struct dirent *dp;
+ off_t off;
+{
+ int error;
+
+ dp->d_name[dp->d_namlen] = 0;
+ dp->d_reclen = DIRSIZ(dp);
+
+ if (idp->uio->uio_resid < dp->d_reclen) {
+ idp->eof = 0;
+ return -1;
+ }
+
+ if (idp->cookiep) {
+ if (idp->ncookies <= 0) {
+ idp->eof = 0;
+ return -1;
+ }
+
+ *idp->cookiep++ = off;
+ --idp->ncookies;
+ }
+
+ if (error = uiomove(dp,dp->d_reclen,idp->uio))
+ return error;
+ idp->uio_off = off;
+ return 0;
+}
+
+static int
+iso_shipdir(idp)
+ struct isoreaddir *idp;
+{
+ struct dirent *dp;
+ int cl, sl, assoc;
+ int error;
+ char *cname, *sname;
+
+ cl = idp->current.d_namlen;
+ cname = idp->current.d_name;
+ if (assoc = cl > 1 && *cname == ASSOCCHAR) {
+ cl--;
+ cname++;
+ }
+
+ dp = &idp->saveent;
+ sname = dp->d_name;
+ if (!(sl = dp->d_namlen)) {
+ dp = &idp->assocent;
+ sname = dp->d_name + 1;
+ sl = dp->d_namlen - 1;
+ }
+ if (sl > 0) {
+ if (sl != cl
+ || bcmp(sname,cname,sl)) {
+ if (idp->assocent.d_namlen) {
+ if (error = iso_uiodir(idp,&idp->assocent,idp->assocoff))
+ return error;
+ idp->assocent.d_namlen = 0;
+ }
+ if (idp->saveent.d_namlen) {
+ if (error = iso_uiodir(idp,&idp->saveent,idp->saveoff))
+ return error;
+ idp->saveent.d_namlen = 0;
+ }
+ }
+ }
+ idp->current.d_reclen = DIRSIZ(&idp->current);
+ if (assoc) {
+ idp->assocoff = idp->curroff;
+ bcopy(&idp->current,&idp->assocent,idp->current.d_reclen);
+ } else {
+ idp->saveoff = idp->curroff;
+ bcopy(&idp->current,&idp->saveent,idp->current.d_reclen);
+ }
+ return 0;
+}
+
+/*
+ * Vnode op for readdir
+ * XXX make sure everything still works now that eofflagp and cookiep
+ * are no longer args.
+ */
+int
+cd9660_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct uio *uio = ap->a_uio;
+ struct isoreaddir *idp;
+ int entryoffsetinblock;
+ int error = 0;
+ int endsearch;
+ struct iso_directory_record *ep;
+ u_short elen;
+ int reclen;
+ struct iso_mnt *imp;
+ struct iso_node *ip;
+ struct buf *bp = NULL;
+
+ ip = VTOI(ap->a_vp);
+ imp = ip->i_mnt;
+
+ MALLOC(idp,struct isoreaddir *,sizeof(*idp),M_TEMP,M_WAITOK);
+ idp->saveent.d_namlen = 0;
+ idp->assocent.d_namlen = 0;
+ idp->uio = uio;
+#if 0
+ idp->cookiep = cookies;
+ idp->ncookies = ncookies;
+ idp->eof = 1;
+#else
+ idp->cookiep = 0;
+#endif
+ idp->curroff = uio->uio_offset;
+
+ entryoffsetinblock = iso_blkoff(imp, idp->curroff);
+ if (entryoffsetinblock != 0) {
+ if (error = iso_blkatoff(ip, idp->curroff, &bp)) {
+ FREE(idp,M_TEMP);
+ return (error);
+ }
+ }
+
+ endsearch = ip->i_size;
+
+ while (idp->curroff < endsearch) {
+ /*
+ * If offset is on a block boundary,
+ * read the next directory block.
+ * Release previous if it exists.
+ */
+
+ if (iso_blkoff(imp, idp->curroff) == 0) {
+ if (bp != NULL)
+ brelse(bp);
+ if (error = iso_blkatoff(ip, idp->curroff, &bp))
+ break;
+ entryoffsetinblock = 0;
+ }
+ /*
+ * Get pointer to next entry.
+ */
+
+ ep = (struct iso_directory_record *)
+ (bp->b_un.b_addr + entryoffsetinblock);
+
+ reclen = isonum_711 (ep->length);
+ if (reclen == 0) {
+ /* skip to next block, if any */
+ idp->curroff = roundup (idp->curroff,
+ imp->logical_block_size);
+ continue;
+ }
+
+ if (reclen < ISO_DIRECTORY_RECORD_SIZE) {
+ error = EINVAL;
+ /* illegal entry, stop */
+ break;
+ }
+
+ if (entryoffsetinblock + reclen > imp->logical_block_size) {
+ error = EINVAL;
+ /* illegal directory, so stop looking */
+ break;
+ }
+
+ idp->current.d_namlen = isonum_711 (ep->name_len);
+ if (isonum_711(ep->flags)&2)
+ isodirino(&idp->current.d_fileno,ep,imp);
+ else
+ idp->current.d_fileno = dbtob(bp->b_blkno) +
+ idp->curroff;
+
+ if (reclen < ISO_DIRECTORY_RECORD_SIZE + idp->current.d_namlen) {
+ error = EINVAL;
+ /* illegal entry, stop */
+ break;
+ }
+
+ idp->curroff += reclen;
+ /*
+ *
+ */
+ switch (imp->iso_ftype) {
+ case ISO_FTYPE_RRIP:
+ cd9660_rrip_getname(ep,idp->current.d_name,
+ (u_short *)&idp->current.d_namlen,
+ &idp->current.d_fileno,imp);
+ if (idp->current.d_namlen)
+ error = iso_uiodir(idp,&idp->current,idp->curroff);
+ break;
+ default: /* ISO_FTYPE_DEFAULT || ISO_FTYPE_9660 */
+ strcpy(idp->current.d_name,"..");
+ switch (ep->name[0]) {
+ case 0:
+ idp->current.d_namlen = 1;
+ error = iso_uiodir(idp,&idp->current,idp->curroff);
+ break;
+ case 1:
+ idp->current.d_namlen = 2;
+ error = iso_uiodir(idp,&idp->current,idp->curroff);
+ break;
+ default:
+ isofntrans(ep->name,idp->current.d_namlen,
+ idp->current.d_name, &elen,
+ imp->iso_ftype == ISO_FTYPE_9660,
+ isonum_711(ep->flags)&4);
+ idp->current.d_namlen = (u_char)elen;
+ if (imp->iso_ftype == ISO_FTYPE_DEFAULT)
+ error = iso_shipdir(idp);
+ else
+ error = iso_uiodir(idp,&idp->current,idp->curroff);
+ break;
+ }
+ }
+ if (error)
+ break;
+
+ entryoffsetinblock += reclen;
+ }
+
+ if (!error && imp->iso_ftype == ISO_FTYPE_DEFAULT) {
+ idp->current.d_namlen = 0;
+ error = iso_shipdir(idp);
+ }
+ if (error < 0)
+ error = 0;
+
+ if (bp)
+ brelse (bp);
+
+ uio->uio_offset = idp->uio_off;
+#if 0
+ *eofflagp = idp->eof;
+#endif
+
+ FREE(idp,M_TEMP);
+
+ return (error);
+}
+
+/*
+ * Return target name of a symbolic link
+ * Shouldn't we get the parent vnode and read the data from there?
+ * This could eventually result in deadlocks in cd9660_lookup.
+ * But otherwise the block read here is in the block buffer two times.
+ */
+typedef struct iso_directory_record ISODIR;
+typedef struct iso_node ISONODE;
+typedef struct iso_mnt ISOMNT;
+int
+cd9660_readlink(ap)
+ struct vop_readlink_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ ISONODE *ip;
+ ISODIR *dirp;
+ ISOMNT *imp;
+ struct buf *bp;
+ u_short symlen;
+ int error;
+ char *symname;
+ ino_t ino;
+
+ ip = VTOI(ap->a_vp);
+ imp = ip->i_mnt;
+
+ if (imp->iso_ftype != ISO_FTYPE_RRIP)
+ return EINVAL;
+
+ /*
+ * Get parents directory record block that this inode included.
+ */
+ error = bread(imp->im_devvp,
+ (daddr_t)(ip->i_number / DEV_BSIZE),
+ imp->logical_block_size,
+ NOCRED,
+ &bp);
+ if (error) {
+ brelse(bp);
+ return EINVAL;
+ }
+
+ /*
+ * Setup the directory pointer for this inode
+ */
+ dirp = (ISODIR *)(bp->b_un.b_addr + (ip->i_number & imp->im_bmask));
+#ifdef DEBUG
+ printf("lbn=%d,off=%d,bsize=%d,DEV_BSIZE=%d, dirp= %08x, b_addr=%08x, offset=%08x(%08x)\n",
+ (daddr_t)(ip->i_number >> imp->im_bshift),
+ ip->i_number & imp->im_bmask,
+ imp->logical_block_size,
+ DEV_BSIZE,
+ dirp,
+ bp->b_un.b_addr,
+ ip->i_number,
+ ip->i_number & imp->im_bmask );
+#endif
+
+ /*
+ * Just make sure, we have a right one....
+ * 1: Check not cross boundary on block
+ */
+ if ((ip->i_number & imp->im_bmask) + isonum_711(dirp->length)
+ > imp->logical_block_size) {
+ brelse(bp);
+ return EINVAL;
+ }
+
+ /*
+ * Now get a buffer
+ * Abuse a namei buffer for now.
+ */
+ MALLOC(symname,char *,MAXPATHLEN,M_NAMEI,M_WAITOK);
+
+ /*
+ * Ok, we just gathering a symbolic name in SL record.
+ */
+ if (cd9660_rrip_getsymname(dirp,symname,&symlen,imp) == 0) {
+ FREE(symname,M_NAMEI);
+ brelse(bp);
+ return EINVAL;
+ }
+ /*
+ * Don't forget before you leave from home ;-)
+ */
+ brelse(bp);
+
+ /*
+ * return with the symbolic name to caller's.
+ */
+ error = uiomove(symname,symlen,ap->a_uio);
+
+ FREE(symname,M_NAMEI);
+
+ return error;
+}
+
+/*
+ * Ufs abort op, called after namei() when a CREATE/DELETE isn't actually
+ * done. If a buffer has been saved in anticipation of a CREATE, delete it.
+ */
+int
+cd9660_abortop(ap)
+ struct vop_abortop_args /* {
+ struct vnode *a_dvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
+ FREE(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ return 0;
+}
+
+/*
+ * Lock an inode.
+ */
+int
+cd9660_lock(ap)
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct iso_node *ip = VTOI(ap->a_vp);
+
+ ISO_ILOCK(ip);
+ return 0;
+}
+
+/*
+ * Unlock an inode.
+ */
+int
+cd9660_unlock(ap)
+ struct vop_unlock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct iso_node *ip = VTOI(ap->a_vp);
+
+ if (!(ip->i_flag & ILOCKED))
+ panic("cd9660_unlock NOT LOCKED");
+ ISO_IUNLOCK(ip);
+ return 0;
+}
+
+/*
+ * Check for a locked inode.
+ */
+int
+cd9660_islocked(ap)
+ struct vop_islocked_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ if (VTOI(ap->a_vp)->i_flag & ILOCKED)
+ return 1;
+ return 0;
+}
+
+/*
+ * Calculate the logical to physical mapping if not done already,
+ * then call the device strategy routine.
+ */
+int
+cd9660_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ register struct buf *bp = ap->a_bp;
+ register struct vnode *vp = bp->b_vp;
+ register struct iso_node *ip;
+ int error;
+
+ ip = VTOI(vp);
+ if (vp->v_type == VBLK || vp->v_type == VCHR)
+ panic("cd9660_strategy: spec");
+ if (bp->b_blkno == bp->b_lblkno) {
+ if (error =
+ VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL)) {
+ bp->b_error = error;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return (error);
+ }
+ if ((long)bp->b_blkno == -1)
+ clrbuf(bp);
+ }
+ if ((long)bp->b_blkno == -1) {
+ biodone(bp);
+ return (0);
+ }
+ vp = ip->i_devvp;
+ bp->b_dev = vp->v_rdev;
+ VOCALL (vp->v_op, VOFFSET(vop_strategy), ap);
+ return (0);
+}
+
+/*
+ * Print out the contents of an inode.
+ */
+int
+cd9660_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ printf("tag VT_ISOFS, isofs vnode\n");
+ return 0;
+}
+
+/*
+ * Unsupported operation
+ */
+int
+cd9660_enotsupp()
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * Global vfs data structures for isofs
+ */
+#define cd9660_create \
+ ((int (*) __P((struct vop_create_args *)))cd9660_enotsupp)
+#define cd9660_mknod ((int (*) __P((struct vop_mknod_args *)))cd9660_enotsupp)
+#define cd9660_setattr \
+ ((int (*) __P((struct vop_setattr_args *)))cd9660_enotsupp)
+#define cd9660_write ((int (*) __P((struct vop_write_args *)))cd9660_enotsupp)
+#define cd9660_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
+#define cd9660_remove \
+ ((int (*) __P((struct vop_remove_args *)))cd9660_enotsupp)
+#define cd9660_link ((int (*) __P((struct vop_link_args *)))cd9660_enotsupp)
+#define cd9660_rename \
+ ((int (*) __P((struct vop_rename_args *)))cd9660_enotsupp)
+#define cd9660_mkdir ((int (*) __P((struct vop_mkdir_args *)))cd9660_enotsupp)
+#define cd9660_rmdir ((int (*) __P((struct vop_rmdir_args *)))cd9660_enotsupp)
+#define cd9660_symlink \
+ ((int (*) __P((struct vop_symlink_args *)))cd9660_enotsupp)
+#define cd9660_pathconf \
+ ((int (*) __P((struct vop_pathconf_args *)))cd9660_enotsupp)
+#define cd9660_advlock \
+ ((int (*) __P((struct vop_advlock_args *)))cd9660_enotsupp)
+#define cd9660_blkatoff \
+ ((int (*) __P((struct vop_blkatoff_args *)))cd9660_enotsupp)
+#define cd9660_valloc ((int(*) __P(( \
+ struct vnode *pvp, \
+ int mode, \
+ struct ucred *cred, \
+ struct vnode **vpp))) cd9660_enotsupp)
+#define cd9660_vfree ((int (*) __P((struct vop_vfree_args *)))cd9660_enotsupp)
+#define cd9660_truncate \
+ ((int (*) __P((struct vop_truncate_args *)))cd9660_enotsupp)
+#define cd9660_update \
+ ((int (*) __P((struct vop_update_args *)))cd9660_enotsupp)
+#define cd9660_bwrite \
+ ((int (*) __P((struct vop_bwrite_args *)))cd9660_enotsupp)
+
+/*
+ * Global vfs data structures for nfs
+ */
+int (**cd9660_vnodeop_p)();
+struct vnodeopv_entry_desc cd9660_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, cd9660_lookup }, /* lookup */
+ { &vop_create_desc, cd9660_create }, /* create */
+ { &vop_mknod_desc, cd9660_mknod }, /* mknod */
+ { &vop_open_desc, cd9660_open }, /* open */
+ { &vop_close_desc, cd9660_close }, /* close */
+ { &vop_access_desc, cd9660_access }, /* access */
+ { &vop_getattr_desc, cd9660_getattr }, /* getattr */
+ { &vop_setattr_desc, cd9660_setattr }, /* setattr */
+ { &vop_read_desc, cd9660_read }, /* read */
+ { &vop_write_desc, cd9660_write }, /* write */
+ { &vop_ioctl_desc, cd9660_ioctl }, /* ioctl */
+ { &vop_select_desc, cd9660_select }, /* select */
+ { &vop_mmap_desc, cd9660_mmap }, /* mmap */
+ { &vop_fsync_desc, cd9660_fsync }, /* fsync */
+ { &vop_seek_desc, cd9660_seek }, /* seek */
+ { &vop_remove_desc, cd9660_remove }, /* remove */
+ { &vop_link_desc, cd9660_link }, /* link */
+ { &vop_rename_desc, cd9660_rename }, /* rename */
+ { &vop_mkdir_desc, cd9660_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, cd9660_rmdir }, /* rmdir */
+ { &vop_symlink_desc, cd9660_symlink }, /* symlink */
+ { &vop_readdir_desc, cd9660_readdir }, /* readdir */
+ { &vop_readlink_desc, cd9660_readlink },/* readlink */
+ { &vop_abortop_desc, cd9660_abortop }, /* abortop */
+ { &vop_inactive_desc, cd9660_inactive },/* inactive */
+ { &vop_reclaim_desc, cd9660_reclaim }, /* reclaim */
+ { &vop_lock_desc, cd9660_lock }, /* lock */
+ { &vop_unlock_desc, cd9660_unlock }, /* unlock */
+ { &vop_bmap_desc, cd9660_bmap }, /* bmap */
+ { &vop_strategy_desc, cd9660_strategy },/* strategy */
+ { &vop_print_desc, cd9660_print }, /* print */
+ { &vop_islocked_desc, cd9660_islocked },/* islocked */
+ { &vop_pathconf_desc, cd9660_pathconf },/* pathconf */
+ { &vop_advlock_desc, cd9660_advlock }, /* advlock */
+ { &vop_blkatoff_desc, cd9660_blkatoff },/* blkatoff */
+ { &vop_valloc_desc, cd9660_valloc }, /* valloc */
+ { &vop_vfree_desc, cd9660_vfree }, /* vfree */
+ { &vop_truncate_desc, cd9660_truncate },/* truncate */
+ { &vop_update_desc, cd9660_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc cd9660_vnodeop_opv_desc =
+ { &cd9660_vnodeop_p, cd9660_vnodeop_entries };
+
+/*
+ * Special device vnode ops
+ */
+int (**cd9660_specop_p)();
+struct vnodeopv_entry_desc cd9660_specop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, spec_lookup }, /* lookup */
+ { &vop_create_desc, cd9660_create }, /* create */
+ { &vop_mknod_desc, cd9660_mknod }, /* mknod */
+ { &vop_open_desc, spec_open }, /* open */
+ { &vop_close_desc, spec_close }, /* close */
+ { &vop_access_desc, cd9660_access }, /* access */
+ { &vop_getattr_desc, cd9660_getattr }, /* getattr */
+ { &vop_setattr_desc, cd9660_setattr }, /* setattr */
+ { &vop_read_desc, spec_read }, /* read */
+ { &vop_write_desc, spec_write }, /* write */
+ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */
+ { &vop_select_desc, spec_select }, /* select */
+ { &vop_mmap_desc, spec_mmap }, /* mmap */
+ { &vop_fsync_desc, spec_fsync }, /* fsync */
+ { &vop_seek_desc, spec_seek }, /* seek */
+ { &vop_remove_desc, cd9660_remove }, /* remove */
+ { &vop_link_desc, cd9660_link }, /* link */
+ { &vop_rename_desc, cd9660_rename }, /* rename */
+ { &vop_mkdir_desc, cd9660_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, cd9660_rmdir }, /* rmdir */
+ { &vop_symlink_desc, cd9660_symlink }, /* symlink */
+ { &vop_readdir_desc, spec_readdir }, /* readdir */
+ { &vop_readlink_desc, spec_readlink }, /* readlink */
+ { &vop_abortop_desc, spec_abortop }, /* abortop */
+ { &vop_inactive_desc, cd9660_inactive },/* inactive */
+ { &vop_reclaim_desc, cd9660_reclaim }, /* reclaim */
+ { &vop_lock_desc, cd9660_lock }, /* lock */
+ { &vop_unlock_desc, cd9660_unlock }, /* unlock */
+ { &vop_bmap_desc, spec_bmap }, /* bmap */
+ /* XXX strategy: panics, should be notsupp instead? */
+ { &vop_strategy_desc, cd9660_strategy },/* strategy */
+ { &vop_print_desc, cd9660_print }, /* print */
+ { &vop_islocked_desc, cd9660_islocked },/* islocked */
+ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */
+ { &vop_advlock_desc, spec_advlock }, /* advlock */
+ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, spec_valloc }, /* valloc */
+ { &vop_vfree_desc, spec_vfree }, /* vfree */
+ { &vop_truncate_desc, spec_truncate }, /* truncate */
+ { &vop_update_desc, cd9660_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc cd9660_specop_opv_desc =
+ { &cd9660_specop_p, cd9660_specop_entries };
+
+#ifdef FIFO
+int (**cd9660_fifoop_p)();
+struct vnodeopv_entry_desc cd9660_fifoop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, fifo_lookup }, /* lookup */
+ { &vop_create_desc, cd9660_create }, /* create */
+ { &vop_mknod_desc, cd9660_mknod }, /* mknod */
+ { &vop_open_desc, fifo_open }, /* open */
+ { &vop_close_desc, fifo_close }, /* close */
+ { &vop_access_desc, cd9660_access }, /* access */
+ { &vop_getattr_desc, cd9660_getattr }, /* getattr */
+ { &vop_setattr_desc, cd9660_setattr }, /* setattr */
+ { &vop_read_desc, fifo_read }, /* read */
+ { &vop_write_desc, fifo_write }, /* write */
+ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */
+ { &vop_select_desc, fifo_select }, /* select */
+ { &vop_mmap_desc, fifo_mmap }, /* mmap */
+ { &vop_fsync_desc, fifo_fsync }, /* fsync */
+ { &vop_seek_desc, fifo_seek }, /* seek */
+ { &vop_remove_desc, cd9660_remove }, /* remove */
+ { &vop_link_desc, cd9660_link }, /* link */
+ { &vop_rename_desc, cd9660_rename }, /* rename */
+ { &vop_mkdir_desc, cd9660_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, cd9660_rmdir }, /* rmdir */
+ { &vop_symlink_desc, cd9660_symlink }, /* symlink */
+ { &vop_readdir_desc, fifo_readdir }, /* readdir */
+ { &vop_readlink_desc, fifo_readlink }, /* readlink */
+ { &vop_abortop_desc, fifo_abortop }, /* abortop */
+ { &vop_inactive_desc, cd9660_inactive },/* inactive */
+ { &vop_reclaim_desc, cd9660_reclaim }, /* reclaim */
+ { &vop_lock_desc, cd9660_lock }, /* lock */
+ { &vop_unlock_desc, cd9660_unlock }, /* unlock */
+ { &vop_bmap_desc, fifo_bmap }, /* bmap */
+ { &vop_strategy_desc, fifo_badop }, /* strategy */
+ { &vop_print_desc, cd9660_print }, /* print */
+ { &vop_islocked_desc, cd9660_islocked },/* islocked */
+ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */
+ { &vop_advlock_desc, fifo_advlock }, /* advlock */
+ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, fifo_valloc }, /* valloc */
+ { &vop_vfree_desc, fifo_vfree }, /* vfree */
+ { &vop_truncate_desc, fifo_truncate }, /* truncate */
+ { &vop_update_desc, cd9660_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc cd9660_fifoop_opv_desc =
+ { &cd9660_fifoop_p, cd9660_fifoop_entries };
+#endif /* FIFO */
diff --git a/sys/fs/cd9660/iso.h b/sys/fs/cd9660/iso.h
new file mode 100644
index 0000000..e356706
--- /dev/null
+++ b/sys/fs/cd9660/iso.h
@@ -0,0 +1,256 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)iso.h 8.2 (Berkeley) 1/23/94
+ */
+
+#define ISODCL(from, to) (to - from + 1)
+
+struct iso_volume_descriptor {
+ char type[ISODCL(1,1)]; /* 711 */
+ char id[ISODCL(2,6)];
+ char version[ISODCL(7,7)];
+ char data[ISODCL(8,2048)];
+};
+
+/* volume descriptor types */
+#define ISO_VD_PRIMARY 1
+#define ISO_VD_END 255
+
+#define ISO_STANDARD_ID "CD001"
+#define ISO_ECMA_ID "CDW01"
+
+struct iso_primary_descriptor {
+ char type [ISODCL ( 1, 1)]; /* 711 */
+ char id [ISODCL ( 2, 6)];
+ char version [ISODCL ( 7, 7)]; /* 711 */
+ char unused1 [ISODCL ( 8, 8)];
+ char system_id [ISODCL ( 9, 40)]; /* achars */
+ char volume_id [ISODCL ( 41, 72)]; /* dchars */
+ char unused2 [ISODCL ( 73, 80)];
+ char volume_space_size [ISODCL ( 81, 88)]; /* 733 */
+ char unused3 [ISODCL ( 89, 120)];
+ char volume_set_size [ISODCL (121, 124)]; /* 723 */
+ char volume_sequence_number [ISODCL (125, 128)]; /* 723 */
+ char logical_block_size [ISODCL (129, 132)]; /* 723 */
+ char path_table_size [ISODCL (133, 140)]; /* 733 */
+ char type_l_path_table [ISODCL (141, 144)]; /* 731 */
+ char opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */
+ char type_m_path_table [ISODCL (149, 152)]; /* 732 */
+ char opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */
+ char root_directory_record [ISODCL (157, 190)]; /* 9.1 */
+ char volume_set_id [ISODCL (191, 318)]; /* dchars */
+ char publisher_id [ISODCL (319, 446)]; /* achars */
+ char preparer_id [ISODCL (447, 574)]; /* achars */
+ char application_id [ISODCL (575, 702)]; /* achars */
+ char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */
+ char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */
+ char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */
+ char creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */
+ char modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */
+ char expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */
+ char effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */
+ char file_structure_version [ISODCL (882, 882)]; /* 711 */
+ char unused4 [ISODCL (883, 883)];
+ char application_data [ISODCL (884, 1395)];
+ char unused5 [ISODCL (1396, 2048)];
+};
+#define ISO_DEFAULT_BLOCK_SIZE 2048
+
+struct iso_directory_record {
+ char length [ISODCL (1, 1)]; /* 711 */
+ char ext_attr_length [ISODCL (2, 2)]; /* 711 */
+ unsigned char extent [ISODCL (3, 10)]; /* 733 */
+ unsigned char size [ISODCL (11, 18)]; /* 733 */
+ char date [ISODCL (19, 25)]; /* 7 by 711 */
+ char flags [ISODCL (26, 26)];
+ char file_unit_size [ISODCL (27, 27)]; /* 711 */
+ char interleave [ISODCL (28, 28)]; /* 711 */
+ char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
+ char name_len [ISODCL (33, 33)]; /* 711 */
+ char name [0];
+};
+/* can't take sizeof(iso_directory_record), because of possible alignment
+ of the last entry (34 instead of 33) */
+#define ISO_DIRECTORY_RECORD_SIZE 33
+
+struct iso_extended_attributes {
+ unsigned char owner [ISODCL (1, 4)]; /* 723 */
+ unsigned char group [ISODCL (5, 8)]; /* 723 */
+ unsigned char perm [ISODCL (9, 10)]; /* 9.5.3 */
+ char ctime [ISODCL (11, 27)]; /* 8.4.26.1 */
+ char mtime [ISODCL (28, 44)]; /* 8.4.26.1 */
+ char xtime [ISODCL (45, 61)]; /* 8.4.26.1 */
+ char ftime [ISODCL (62, 78)]; /* 8.4.26.1 */
+ char recfmt [ISODCL (79, 79)]; /* 711 */
+ char recattr [ISODCL (80, 80)]; /* 711 */
+ unsigned char reclen [ISODCL (81, 84)]; /* 723 */
+ char system_id [ISODCL (85, 116)]; /* achars */
+ char system_use [ISODCL (117, 180)];
+ char version [ISODCL (181, 181)]; /* 711 */
+ char len_esc [ISODCL (182, 182)]; /* 711 */
+ char reserved [ISODCL (183, 246)];
+ unsigned char len_au [ISODCL (247, 250)]; /* 723 */
+};
+
+/* CD-ROM Format type */
+enum ISO_FTYPE { ISO_FTYPE_DEFAULT, ISO_FTYPE_9660, ISO_FTYPE_RRIP, ISO_FTYPE_ECMA };
+
+#ifndef ISOFSMNT_ROOT
+#define ISOFSMNT_ROOT 0
+#endif
+
+struct iso_mnt {
+ int im_flags;
+
+ struct mount *im_mountp;
+ dev_t im_dev;
+ struct vnode *im_devvp;
+
+ int logical_block_size;
+ int im_bshift;
+ int im_bmask;
+
+ int volume_space_size;
+ char im_fsmnt[50];
+ struct netexport im_export;
+
+ char root[ISODCL (157, 190)];
+ int root_extent;
+ int root_size;
+ enum ISO_FTYPE iso_ftype;
+
+ int rr_skip;
+ int rr_skip0;
+};
+
+#define VFSTOISOFS(mp) ((struct iso_mnt *)((mp)->mnt_data))
+
+#define iso_blkoff(imp, loc) ((loc) & (imp)->im_bmask)
+#define iso_lblkno(imp, loc) ((loc) >> (imp)->im_bshift)
+#define iso_blksize(imp, ip, lbn) ((imp)->logical_block_size)
+#define iso_lblktosize(imp, blk) ((blk) << (imp)->im_bshift)
+
+int cd9660_mount __P((struct mount *,
+ char *, caddr_t, struct nameidata *, struct proc *));
+int cd9660_start __P((struct mount *, int, struct proc *));
+int cd9660_unmount __P((struct mount *, int, struct proc *));
+int cd9660_root __P((struct mount *, struct vnode **));
+int cd9660_quotactl __P((struct mount *, int, uid_t, caddr_t, struct proc *));
+int cd9660_statfs __P((struct mount *, struct statfs *, struct proc *));
+int cd9660_sync __P((struct mount *, int, struct ucred *, struct proc *));
+int cd9660_vget __P((struct mount *, ino_t, struct vnode **));
+int cd9660_fhtovp __P((struct mount *, struct fid *, struct mbuf *,
+ struct vnode **, int *, struct ucred **));
+int cd9660_vptofh __P((struct vnode *, struct fid *));
+int cd9660_init __P(());
+
+struct iso_node;
+int iso_blkatoff __P((struct iso_node *ip, long offset, struct buf **bpp));
+int iso_iget __P((struct iso_node *xp, ino_t ino, int relocated,
+ struct iso_node **ipp, struct iso_directory_record *isodir));
+int iso_iput __P((struct iso_node *ip));
+int iso_ilock __P((struct iso_node *ip));
+int iso_iunlock __P((struct iso_node *ip));
+int cd9660_mountroot __P((void));
+
+extern int (**cd9660_vnodeop_p)();
+
+extern inline int
+isonum_711(p)
+ unsigned char *p;
+{
+ return *p;
+}
+
+extern inline int
+isonum_712(p)
+ char *p;
+{
+ return *p;
+}
+
+extern inline int
+isonum_721(p)
+ unsigned char *p;
+{
+ return *p|((char)p[1] << 8);
+}
+
+extern inline int
+isonum_722(p)
+ unsigned char *p;
+{
+ return ((char)*p << 8)|p[1];
+}
+
+extern inline int
+isonum_723(p)
+ unsigned char *p;
+{
+ return isonum_721(p);
+}
+
+extern inline int
+isonum_731(p)
+ unsigned char *p;
+{
+ return *p|(p[1] << 8)|(p[2] << 16)|(p[3] << 24);
+}
+
+extern inline int
+isonum_732(p)
+ unsigned char *p;
+{
+ return (*p << 24)|(p[1] << 16)|(p[2] << 8)|p[3];
+}
+
+extern inline int
+isonum_733(p)
+ unsigned char *p;
+{
+ return isonum_731(p);
+}
+
+int isofncmp __P((unsigned char *, int, unsigned char *, int));
+void isofntrans __P((unsigned char *, int, unsigned char *, unsigned short *,
+ int, int));
+
+/*
+ * Associated files have a leading '='.
+ */
+#define ASSOCCHAR '='
diff --git a/sys/fs/cd9660/iso_rrip.h b/sys/fs/cd9660/iso_rrip.h
new file mode 100644
index 0000000..78e4a77
--- /dev/null
+++ b/sys/fs/cd9660/iso_rrip.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)iso_rrip.h 8.2 (Berkeley) 1/23/94
+ */
+
+
+/*
+ * Analyze function flag (similar to RR field bits)
+ */
+#define ISO_SUSP_ATTR 0x0001
+#define ISO_SUSP_DEVICE 0x0002
+#define ISO_SUSP_SLINK 0x0004
+#define ISO_SUSP_ALTNAME 0x0008
+#define ISO_SUSP_CLINK 0x0010
+#define ISO_SUSP_PLINK 0x0020
+#define ISO_SUSP_RELDIR 0x0040
+#define ISO_SUSP_TSTAMP 0x0080
+#define ISO_SUSP_IDFLAG 0x0100
+#define ISO_SUSP_EXTREF 0x0200
+#define ISO_SUSP_CONT 0x0400
+#define ISO_SUSP_OFFSET 0x0800
+#define ISO_SUSP_STOP 0x1000
+#define ISO_SUSP_UNKNOWN 0x8000
+
+typedef struct {
+ struct iso_node *inop;
+ int fields; /* interesting fields in this analysis */
+ daddr_t iso_ce_blk; /* block of continuation area */
+ off_t iso_ce_off; /* offset of continuation area */
+ int iso_ce_len; /* length of continuation area */
+ struct iso_mnt *imp; /* mount structure */
+ ino_t *inump; /* inode number pointer */
+ char *outbuf; /* name/symbolic link output area */
+ u_short *outlen; /* length of above */
+ u_short maxlen; /* maximum length of above */
+ int cont; /* continuation of above */
+} ISO_RRIP_ANALYZE;
+
+int cd9660_rrip_analyze __P((struct iso_directory_record *isodir,
+ struct iso_node *inop, struct iso_mnt *imp));
+int cd9660_rrip_getname __P((struct iso_directory_record *isodir,
+ char *outbuf, u_short *outlen,
+ ino_t *inump, struct iso_mnt *imp));
+int cd9660_rrip_getsymname __P((struct iso_directory_record *isodir,
+ char *outbuf, u_short *outlen,
+ struct iso_mnt *imp));
+int cd9660_rrip_offset __P((struct iso_directory_record *isodir,
+ struct iso_mnt *imp));
diff --git a/sys/fs/deadfs/dead_vnops.c b/sys/fs/deadfs/dead_vnops.c
new file mode 100644
index 0000000..cac8775
--- /dev/null
+++ b/sys/fs/deadfs/dead_vnops.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dead_vnops.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vnode.h>
+#include <sys/errno.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+
+/*
+ * Prototypes for dead operations on vnodes.
+ */
+int dead_badop(),
+ dead_ebadf();
+int dead_lookup __P((struct vop_lookup_args *));
+#define dead_create ((int (*) __P((struct vop_create_args *)))dead_badop)
+#define dead_mknod ((int (*) __P((struct vop_mknod_args *)))dead_badop)
+int dead_open __P((struct vop_open_args *));
+#define dead_close ((int (*) __P((struct vop_close_args *)))nullop)
+#define dead_access ((int (*) __P((struct vop_access_args *)))dead_ebadf)
+#define dead_getattr ((int (*) __P((struct vop_getattr_args *)))dead_ebadf)
+#define dead_setattr ((int (*) __P((struct vop_setattr_args *)))dead_ebadf)
+int dead_read __P((struct vop_read_args *));
+int dead_write __P((struct vop_write_args *));
+int dead_ioctl __P((struct vop_ioctl_args *));
+int dead_select __P((struct vop_select_args *));
+#define dead_mmap ((int (*) __P((struct vop_mmap_args *)))dead_badop)
+#define dead_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
+#define dead_seek ((int (*) __P((struct vop_seek_args *)))nullop)
+#define dead_remove ((int (*) __P((struct vop_remove_args *)))dead_badop)
+#define dead_link ((int (*) __P((struct vop_link_args *)))dead_badop)
+#define dead_rename ((int (*) __P((struct vop_rename_args *)))dead_badop)
+#define dead_mkdir ((int (*) __P((struct vop_mkdir_args *)))dead_badop)
+#define dead_rmdir ((int (*) __P((struct vop_rmdir_args *)))dead_badop)
+#define dead_symlink ((int (*) __P((struct vop_symlink_args *)))dead_badop)
+#define dead_readdir ((int (*) __P((struct vop_readdir_args *)))dead_ebadf)
+#define dead_readlink ((int (*) __P((struct vop_readlink_args *)))dead_ebadf)
+#define dead_abortop ((int (*) __P((struct vop_abortop_args *)))dead_badop)
+#define dead_inactive ((int (*) __P((struct vop_inactive_args *)))nullop)
+#define dead_reclaim ((int (*) __P((struct vop_reclaim_args *)))nullop)
+int dead_lock __P((struct vop_lock_args *));
+#define dead_unlock ((int (*) __P((struct vop_unlock_args *)))nullop)
+int dead_bmap __P((struct vop_bmap_args *));
+int dead_strategy __P((struct vop_strategy_args *));
+int dead_print __P((struct vop_print_args *));
+#define dead_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+#define dead_pathconf ((int (*) __P((struct vop_pathconf_args *)))dead_ebadf)
+#define dead_advlock ((int (*) __P((struct vop_advlock_args *)))dead_ebadf)
+#define dead_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))dead_badop)
+#define dead_valloc ((int (*) __P((struct vop_valloc_args *)))dead_badop)
+#define dead_vfree ((int (*) __P((struct vop_vfree_args *)))dead_badop)
+#define dead_truncate ((int (*) __P((struct vop_truncate_args *)))nullop)
+#define dead_update ((int (*) __P((struct vop_update_args *)))nullop)
+#define dead_bwrite ((int (*) __P((struct vop_bwrite_args *)))nullop)
+
+int (**dead_vnodeop_p)();
+struct vnodeopv_entry_desc dead_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, dead_lookup }, /* lookup */
+ { &vop_create_desc, dead_create }, /* create */
+ { &vop_mknod_desc, dead_mknod }, /* mknod */
+ { &vop_open_desc, dead_open }, /* open */
+ { &vop_close_desc, dead_close }, /* close */
+ { &vop_access_desc, dead_access }, /* access */
+ { &vop_getattr_desc, dead_getattr }, /* getattr */
+ { &vop_setattr_desc, dead_setattr }, /* setattr */
+ { &vop_read_desc, dead_read }, /* read */
+ { &vop_write_desc, dead_write }, /* write */
+ { &vop_ioctl_desc, dead_ioctl }, /* ioctl */
+ { &vop_select_desc, dead_select }, /* select */
+ { &vop_mmap_desc, dead_mmap }, /* mmap */
+ { &vop_fsync_desc, dead_fsync }, /* fsync */
+ { &vop_seek_desc, dead_seek }, /* seek */
+ { &vop_remove_desc, dead_remove }, /* remove */
+ { &vop_link_desc, dead_link }, /* link */
+ { &vop_rename_desc, dead_rename }, /* rename */
+ { &vop_mkdir_desc, dead_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, dead_rmdir }, /* rmdir */
+ { &vop_symlink_desc, dead_symlink }, /* symlink */
+ { &vop_readdir_desc, dead_readdir }, /* readdir */
+ { &vop_readlink_desc, dead_readlink }, /* readlink */
+ { &vop_abortop_desc, dead_abortop }, /* abortop */
+ { &vop_inactive_desc, dead_inactive }, /* inactive */
+ { &vop_reclaim_desc, dead_reclaim }, /* reclaim */
+ { &vop_lock_desc, dead_lock }, /* lock */
+ { &vop_unlock_desc, dead_unlock }, /* unlock */
+ { &vop_bmap_desc, dead_bmap }, /* bmap */
+ { &vop_strategy_desc, dead_strategy }, /* strategy */
+ { &vop_print_desc, dead_print }, /* print */
+ { &vop_islocked_desc, dead_islocked }, /* islocked */
+ { &vop_pathconf_desc, dead_pathconf }, /* pathconf */
+ { &vop_advlock_desc, dead_advlock }, /* advlock */
+ { &vop_blkatoff_desc, dead_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, dead_valloc }, /* valloc */
+ { &vop_vfree_desc, dead_vfree }, /* vfree */
+ { &vop_truncate_desc, dead_truncate }, /* truncate */
+ { &vop_update_desc, dead_update }, /* update */
+ { &vop_bwrite_desc, dead_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc dead_vnodeop_opv_desc =
+ { &dead_vnodeop_p, dead_vnodeop_entries };
+
+/*
+ * Trivial lookup routine that always fails.
+ */
+/* ARGSUSED */
+int
+dead_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode * a_dvp;
+ struct vnode ** a_vpp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+
+ *ap->a_vpp = NULL;
+ return (ENOTDIR);
+}
+
+/*
+ * Open always fails as if device did not exist.
+ */
+/* ARGSUSED */
+int
+dead_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (ENXIO);
+}
+
+/*
+ * Vnode op for read
+ */
+/* ARGSUSED */
+int
+dead_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+
+ if (chkvnlock(ap->a_vp))
+ panic("dead_read: lock");
+ /*
+ * Return EOF for character devices, EIO for others
+ */
+ if (ap->a_vp->v_type != VCHR)
+ return (EIO);
+ return (0);
+}
+
+/*
+ * Vnode op for write
+ */
+/* ARGSUSED */
+int
+dead_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+
+ if (chkvnlock(ap->a_vp))
+ panic("dead_write: lock");
+ return (EIO);
+}
+
+/*
+ * Device ioctl operation.
+ */
+/* ARGSUSED */
+int
+dead_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ if (!chkvnlock(ap->a_vp))
+ return (EBADF);
+ return (VCALL(ap->a_vp, VOFFSET(vop_ioctl), ap));
+}
+
+/* ARGSUSED */
+int
+dead_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ /*
+ * Let the user find out that the descriptor is gone.
+ */
+ return (1);
+}
+
+/*
+ * Just call the device strategy routine
+ */
+int
+dead_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+
+ if (ap->a_bp->b_vp == NULL || !chkvnlock(ap->a_bp->b_vp)) {
+ ap->a_bp->b_flags |= B_ERROR;
+ biodone(ap->a_bp);
+ return (EIO);
+ }
+ return (VOP_STRATEGY(ap->a_bp));
+}
+
+/*
+ * Wait until the vnode has finished changing state.
+ */
+int
+dead_lock(ap)
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ if (!chkvnlock(ap->a_vp))
+ return (0);
+ return (VCALL(ap->a_vp, VOFFSET(vop_lock), ap));
+}
+
+/*
+ * Wait until the vnode has finished changing state.
+ */
+int
+dead_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap;
+{
+
+ if (!chkvnlock(ap->a_vp))
+ return (EIO);
+ return (VOP_BMAP(ap->a_vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp));
+}
+
+/*
+ * Print out the contents of a dead vnode.
+ */
+/* ARGSUSED */
+int
+dead_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_NON, dead vnode\n");
+ return (0);
+}
+
+/*
+ * Empty vnode failed operation
+ */
+int
+dead_ebadf()
+{
+
+ return (EBADF);
+}
+
+/*
+ * Empty vnode bad operation
+ */
+int
+dead_badop()
+{
+
+ panic("dead_badop called");
+ /* NOTREACHED */
+}
+
+/*
+ * Empty vnode null operation
+ */
+int
+dead_nullop()
+{
+
+ return (0);
+}
+
+/*
+ * We have to wait during times when the vnode is
+ * in a state of change.
+ */
+int
+chkvnlock(vp)
+ register struct vnode *vp;
+{
+ int locked = 0;
+
+ while (vp->v_flag & VXLOCK) {
+ vp->v_flag |= VXWANT;
+ sleep((caddr_t)vp, PINOD);
+ locked = 1;
+ }
+ return (locked);
+}
diff --git a/sys/fs/fdescfs/fdesc.h b/sys/fs/fdescfs/fdesc.h
new file mode 100644
index 0000000..4c682e7
--- /dev/null
+++ b/sys/fs/fdescfs/fdesc.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fdesc.h 8.5 (Berkeley) 1/21/94
+ *
+ * $Id: fdesc.h,v 1.8 1993/04/06 15:28:33 jsp Exp $
+ */
+
+#ifdef KERNEL
+struct fdescmount {
+ struct vnode *f_root; /* Root node */
+};
+
+#define FD_ROOT 2
+#define FD_DEVFD 3
+#define FD_STDIN 4
+#define FD_STDOUT 5
+#define FD_STDERR 6
+#define FD_CTTY 7
+#define FD_DESC 8
+#define FD_MAX 12
+
+typedef enum {
+ Froot,
+ Fdevfd,
+ Fdesc,
+ Flink,
+ Fctty
+} fdntype;
+
+struct fdescnode {
+ struct fdescnode *fd_forw; /* Hash chain */
+ struct fdescnode *fd_back;
+ struct vnode *fd_vnode; /* Back ptr to vnode */
+ fdntype fd_type; /* Type of this node */
+ unsigned fd_fd; /* Fd to be dup'ed */
+ char *fd_link; /* Link to fd/n */
+ int fd_ix; /* filesystem index */
+};
+
+#define VFSTOFDESC(mp) ((struct fdescmount *)((mp)->mnt_data))
+#define VTOFDESC(vp) ((struct fdescnode *)(vp)->v_data)
+
+extern dev_t devctty;
+extern int fdesc_init __P((void));
+extern int fdesc_root __P((struct mount *, struct vnode **));
+extern int fdesc_allocvp __P((fdntype, int, struct mount *, struct vnode **));
+extern int (**fdesc_vnodeop_p)();
+extern struct vfsops fdesc_vfsops;
+#endif /* KERNEL */
diff --git a/sys/fs/fdescfs/fdesc_vfsops.c b/sys/fs/fdescfs/fdesc_vfsops.c
new file mode 100644
index 0000000..80c543d
--- /dev/null
+++ b/sys/fs/fdescfs/fdesc_vfsops.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fdesc_vfsops.c 8.4 (Berkeley) 1/21/94
+ *
+ * $Id: fdesc_vfsops.c,v 1.9 1993/04/06 15:28:33 jsp Exp $
+ */
+
+/*
+ * /dev/fd Filesystem
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/resourcevar.h>
+#include <sys/filedesc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <miscfs/fdesc/fdesc.h>
+
+/*
+ * Mount the per-process file descriptors (/dev/fd)
+ */
+int
+fdesc_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ int error = 0;
+ u_int size;
+ struct fdescmount *fmp;
+ struct vnode *rvp;
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE)
+ return (EOPNOTSUPP);
+
+ error = fdesc_allocvp(Froot, FD_ROOT, mp, &rvp);
+ if (error)
+ return (error);
+
+ MALLOC(fmp, struct fdescmount *, sizeof(struct fdescmount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+ rvp->v_type = VDIR;
+ rvp->v_flag |= VROOT;
+ fmp->f_root = rvp;
+ /* XXX -- don't mark as local to work around fts() problems */
+ /*mp->mnt_flag |= MNT_LOCAL;*/
+ mp->mnt_data = (qaddr_t) fmp;
+ getnewfsid(mp, MOUNT_FDESC);
+
+ (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+ bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
+ bcopy("fdesc", mp->mnt_stat.f_mntfromname, sizeof("fdesc"));
+ return (0);
+}
+
+int
+fdesc_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ return (0);
+}
+
+int
+fdesc_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ int error;
+ int flags = 0;
+ extern int doforce;
+ struct vnode *rootvp = VFSTOFDESC(mp)->f_root;
+
+ if (mntflags & MNT_FORCE) {
+ /* fdesc can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ /*
+ * Clear out buffer cache. I don't think we
+ * ever get anything cached at this level at the
+ * moment, but who knows...
+ */
+ if (rootvp->v_usecount > 1)
+ return (EBUSY);
+ if (error = vflush(mp, rootvp, flags))
+ return (error);
+
+ /*
+ * Release reference on underlying root vnode
+ */
+ vrele(rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(rootvp);
+ /*
+ * Finally, throw away the fdescmount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ mp->mnt_data = 0;
+
+ return (0);
+}
+
+int
+fdesc_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct vnode *vp;
+
+ /*
+ * Return locked reference to root.
+ */
+ vp = VFSTOFDESC(mp)->f_root;
+ VREF(vp);
+ VOP_LOCK(vp);
+ *vpp = vp;
+ return (0);
+}
+
+int
+fdesc_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+fdesc_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ struct filedesc *fdp;
+ int lim;
+ int i;
+ int last;
+ int freefd;
+
+ /*
+ * Compute number of free file descriptors.
+ * [ Strange results will ensue if the open file
+ * limit is ever reduced below the current number
+ * of open files... ]
+ */
+ lim = p->p_rlimit[RLIMIT_NOFILE].rlim_cur;
+ fdp = p->p_fd;
+ last = min(fdp->fd_nfiles, lim);
+ freefd = 0;
+ for (i = fdp->fd_freefile; i < last; i++)
+ if (fdp->fd_ofiles[i] == NULL)
+ freefd++;
+
+ /*
+ * Adjust for the fact that the fdesc array may not
+ * have been fully allocated yet.
+ */
+ if (fdp->fd_nfiles < lim)
+ freefd += (lim - fdp->fd_nfiles);
+
+ sbp->f_type = MOUNT_FDESC;
+ sbp->f_flags = 0;
+ sbp->f_bsize = DEV_BSIZE;
+ sbp->f_iosize = DEV_BSIZE;
+ sbp->f_blocks = 2; /* 1K to keep df happy */
+ sbp->f_bfree = 0;
+ sbp->f_bavail = 0;
+ sbp->f_files = lim + 1; /* Allow for "." */
+ sbp->f_ffree = freefd; /* See comments above */
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+ return (0);
+}
+
+int
+fdesc_sync(mp, waitfor)
+ struct mount *mp;
+ int waitfor;
+{
+
+ return (0);
+}
+
+/*
+ * Fdesc flat namespace lookup.
+ * Currently unsupported.
+ */
+int
+fdesc_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+fdesc_fhtovp(mp, fhp, setgen, vpp)
+ struct mount *mp;
+ struct fid *fhp;
+ int setgen;
+ struct vnode **vpp;
+{
+ return (EOPNOTSUPP);
+}
+
+int
+fdesc_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+struct vfsops fdesc_vfsops = {
+ fdesc_mount,
+ fdesc_start,
+ fdesc_unmount,
+ fdesc_root,
+ fdesc_quotactl,
+ fdesc_statfs,
+ fdesc_sync,
+ fdesc_vget,
+ fdesc_fhtovp,
+ fdesc_vptofh,
+ fdesc_init,
+};
diff --git a/sys/fs/fdescfs/fdesc_vnops.c b/sys/fs/fdescfs/fdesc_vnops.c
new file mode 100644
index 0000000..83e6657
--- /dev/null
+++ b/sys/fs/fdescfs/fdesc_vnops.c
@@ -0,0 +1,977 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fdesc_vnops.c 8.9 (Berkeley) 1/21/94
+ *
+ * $Id: fdesc_vnops.c,v 1.12 1993/04/06 16:17:17 jsp Exp $
+ */
+
+/*
+ * /dev/fd Filesystem
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/kernel.h> /* boottime */
+#include <sys/resourcevar.h>
+#include <sys/filedesc.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+#include <sys/dirent.h>
+#include <miscfs/fdesc/fdesc.h>
+
+#define cttyvp(p) ((p)->p_flag & P_CONTROLT ? (p)->p_session->s_ttyvp : NULL)
+
+#define FDL_WANT 0x01
+#define FDL_LOCKED 0x02
+static int fdcache_lock;
+
+dev_t devctty;
+
+#if (FD_STDIN != FD_STDOUT-1) || (FD_STDOUT != FD_STDERR-1)
+FD_STDIN, FD_STDOUT, FD_STDERR must be a sequence n, n+1, n+2
+#endif
+
+#define NFDCACHE 3
+#define FD_NHASH(ix) ((ix) & NFDCACHE)
+
+/*
+ * Cache head
+ */
+struct fdcache {
+ struct fdescnode *fc_forw;
+ struct fdescnode *fc_back;
+};
+
+static struct fdcache fdcache[NFDCACHE];
+
+/*
+ * Initialise cache headers
+ */
+int
+fdesc_init()
+{
+ struct fdcache *fc;
+
+ devctty = makedev(nchrdev, 0);
+
+ for (fc = fdcache; fc < fdcache + NFDCACHE; fc++)
+ fc->fc_forw = fc->fc_back = (struct fdescnode *) fc;
+ return (0);
+}
+
+/*
+ * Compute hash list for given target vnode
+ */
+static struct fdcache *
+fdesc_hash(ix)
+ int ix;
+{
+
+ return (&fdcache[FD_NHASH(ix)]);
+}
+
+int
+fdesc_allocvp(ftype, ix, mp, vpp)
+ fdntype ftype;
+ int ix;
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct fdcache *fc;
+ struct fdescnode *fd;
+ int error = 0;
+
+loop:
+ fc = fdesc_hash(ix);
+ for (fd = fc->fc_forw; fd != (struct fdescnode *) fc; fd = fd->fd_forw) {
+ if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) {
+ if (vget(fd->fd_vnode, 0))
+ goto loop;
+ *vpp = fd->fd_vnode;
+ return (error);
+ }
+ }
+
+ /*
+ * otherwise lock the array while we call getnewvnode
+ * since that can block.
+ */
+ if (fdcache_lock & FDL_LOCKED) {
+ fdcache_lock |= FDL_WANT;
+ sleep((caddr_t) &fdcache_lock, PINOD);
+ goto loop;
+ }
+ fdcache_lock |= FDL_LOCKED;
+
+ error = getnewvnode(VT_FDESC, mp, fdesc_vnodeop_p, vpp);
+ if (error)
+ goto out;
+ MALLOC(fd, void *, sizeof(struct fdescnode), M_TEMP, M_WAITOK);
+ (*vpp)->v_data = fd;
+ fd->fd_vnode = *vpp;
+ fd->fd_type = ftype;
+ fd->fd_fd = -1;
+ fd->fd_link = 0;
+ fd->fd_ix = ix;
+ fc = fdesc_hash(ix);
+ insque(fd, fc);
+
+out:;
+ fdcache_lock &= ~FDL_LOCKED;
+
+ if (fdcache_lock & FDL_WANT) {
+ fdcache_lock &= ~FDL_WANT;
+ wakeup((caddr_t) &fdcache_lock);
+ }
+
+ return (error);
+}
+
+/*
+ * vp is the current namei directory
+ * ndp is the name to locate in that directory...
+ */
+int
+fdesc_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode * a_dvp;
+ struct vnode ** a_vpp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+ struct vnode **vpp = ap->a_vpp;
+ struct vnode *dvp = ap->a_dvp;
+ char *pname;
+ struct proc *p;
+ int nfiles;
+ unsigned fd = 0;
+ int error;
+ struct vnode *fvp;
+ char *ln;
+
+ pname = ap->a_cnp->cn_nameptr;
+ if (ap->a_cnp->cn_namelen == 1 && *pname == '.') {
+ *vpp = dvp;
+ VREF(dvp);
+ VOP_LOCK(dvp);
+ return (0);
+ }
+
+ p = ap->a_cnp->cn_proc;
+ nfiles = p->p_fd->fd_nfiles;
+
+ switch (VTOFDESC(dvp)->fd_type) {
+ default:
+ case Flink:
+ case Fdesc:
+ case Fctty:
+ error = ENOTDIR;
+ goto bad;
+
+ case Froot:
+ if (ap->a_cnp->cn_namelen == 2 && bcmp(pname, "fd", 2) == 0) {
+ error = fdesc_allocvp(Fdevfd, FD_DEVFD, dvp->v_mount, &fvp);
+ if (error)
+ goto bad;
+ *vpp = fvp;
+ fvp->v_type = VDIR;
+ VOP_LOCK(fvp);
+ return (0);
+ }
+
+ if (ap->a_cnp->cn_namelen == 3 && bcmp(pname, "tty", 3) == 0) {
+ struct vnode *ttyvp = cttyvp(p);
+ if (ttyvp == NULL) {
+ error = ENXIO;
+ goto bad;
+ }
+ error = fdesc_allocvp(Fctty, FD_CTTY, dvp->v_mount, &fvp);
+ if (error)
+ goto bad;
+ *vpp = fvp;
+ fvp->v_type = VFIFO;
+ VOP_LOCK(fvp);
+ return (0);
+ }
+
+ ln = 0;
+ switch (ap->a_cnp->cn_namelen) {
+ case 5:
+ if (bcmp(pname, "stdin", 5) == 0) {
+ ln = "fd/0";
+ fd = FD_STDIN;
+ }
+ break;
+ case 6:
+ if (bcmp(pname, "stdout", 6) == 0) {
+ ln = "fd/1";
+ fd = FD_STDOUT;
+ } else
+ if (bcmp(pname, "stderr", 6) == 0) {
+ ln = "fd/2";
+ fd = FD_STDERR;
+ }
+ break;
+ }
+
+ if (ln) {
+ error = fdesc_allocvp(Flink, fd, dvp->v_mount, &fvp);
+ if (error)
+ goto bad;
+ VTOFDESC(fvp)->fd_link = ln;
+ *vpp = fvp;
+ fvp->v_type = VLNK;
+ VOP_LOCK(fvp);
+ return (0);
+ } else {
+ error = ENOENT;
+ goto bad;
+ }
+
+ /* FALL THROUGH */
+
+ case Fdevfd:
+ if (ap->a_cnp->cn_namelen == 2 && bcmp(pname, "..", 2) == 0) {
+ error = fdesc_root(dvp->v_mount, vpp);
+ return (error);
+ }
+
+ fd = 0;
+ while (*pname >= '0' && *pname <= '9') {
+ fd = 10 * fd + *pname++ - '0';
+ if (fd >= nfiles)
+ break;
+ }
+
+ if (*pname != '\0') {
+ error = ENOENT;
+ goto bad;
+ }
+
+ if (fd >= nfiles || p->p_fd->fd_ofiles[fd] == NULL) {
+ error = EBADF;
+ goto bad;
+ }
+
+ error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp);
+ if (error)
+ goto bad;
+ VTOFDESC(fvp)->fd_fd = fd;
+ *vpp = fvp;
+ return (0);
+ }
+
+bad:;
+ *vpp = NULL;
+ return (error);
+}
+
+int
+fdesc_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ int error = 0;
+
+ switch (VTOFDESC(vp)->fd_type) {
+ case Fdesc:
+ /*
+ * XXX Kludge: set p->p_dupfd to contain the value of the
+ * the file descriptor being sought for duplication. The error
+ * return ensures that the vnode for this device will be
+ * released by vn_open. Open will detect this special error and
+ * take the actions in dupfdopen. Other callers of vn_open or
+ * VOP_OPEN will simply report the error.
+ */
+ ap->a_p->p_dupfd = VTOFDESC(vp)->fd_fd; /* XXX */
+ error = ENODEV;
+ break;
+
+ case Fctty:
+ error = cttyopen(devctty, ap->a_mode, 0, ap->a_p);
+ break;
+ }
+
+ return (error);
+}
+
+static int
+fdesc_attr(fd, vap, cred, p)
+ int fd;
+ struct vattr *vap;
+ struct ucred *cred;
+ struct proc *p;
+{
+ struct filedesc *fdp = p->p_fd;
+ struct file *fp;
+ struct stat stb;
+ int error;
+
+ if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL)
+ return (EBADF);
+
+ switch (fp->f_type) {
+ case DTYPE_VNODE:
+ error = VOP_GETATTR((struct vnode *) fp->f_data, vap, cred, p);
+ if (error == 0 && vap->va_type == VDIR) {
+ /*
+ * don't allow directories to show up because
+ * that causes loops in the namespace.
+ */
+ vap->va_type = VFIFO;
+ }
+ break;
+
+ case DTYPE_SOCKET:
+ error = soo_stat((struct socket *)fp->f_data, &stb);
+ if (error == 0) {
+ vattr_null(vap);
+ vap->va_type = VSOCK;
+ vap->va_mode = stb.st_mode;
+ vap->va_nlink = stb.st_nlink;
+ vap->va_uid = stb.st_uid;
+ vap->va_gid = stb.st_gid;
+ vap->va_fsid = stb.st_dev;
+ vap->va_fileid = stb.st_ino;
+ vap->va_size = stb.st_size;
+ vap->va_blocksize = stb.st_blksize;
+ vap->va_atime = stb.st_atimespec;
+ vap->va_mtime = stb.st_mtimespec;
+ vap->va_ctime = stb.st_ctimespec;
+ vap->va_gen = stb.st_gen;
+ vap->va_flags = stb.st_flags;
+ vap->va_rdev = stb.st_rdev;
+ vap->va_bytes = stb.st_blocks * stb.st_blksize;
+ }
+ break;
+
+ default:
+ panic("fdesc attr");
+ break;
+ }
+
+ return (error);
+}
+
+int
+fdesc_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct vattr *vap = ap->a_vap;
+ unsigned fd;
+ int error = 0;
+
+ switch (VTOFDESC(vp)->fd_type) {
+ case Froot:
+ case Fdevfd:
+ case Flink:
+ case Fctty:
+ bzero((caddr_t) vap, sizeof(*vap));
+ vattr_null(vap);
+ vap->va_fileid = VTOFDESC(vp)->fd_ix;
+
+ switch (VTOFDESC(vp)->fd_type) {
+ case Flink:
+ vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
+ vap->va_type = VLNK;
+ vap->va_nlink = 1;
+ vap->va_size = strlen(VTOFDESC(vp)->fd_link);
+ break;
+
+ case Fctty:
+ vap->va_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+ vap->va_type = VFIFO;
+ vap->va_nlink = 1;
+ vap->va_size = 0;
+ break;
+
+ default:
+ vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
+ vap->va_type = VDIR;
+ vap->va_nlink = 2;
+ vap->va_size = DEV_BSIZE;
+ break;
+ }
+ vap->va_uid = 0;
+ vap->va_gid = 0;
+ vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+ vap->va_blocksize = DEV_BSIZE;
+ vap->va_atime.ts_sec = boottime.tv_sec;
+ vap->va_atime.ts_nsec = 0;
+ vap->va_mtime = vap->va_atime;
+ vap->va_ctime = vap->va_mtime;
+ vap->va_gen = 0;
+ vap->va_flags = 0;
+ vap->va_rdev = 0;
+ vap->va_bytes = 0;
+ break;
+
+ case Fdesc:
+ fd = VTOFDESC(vp)->fd_fd;
+ error = fdesc_attr(fd, vap, ap->a_cred, ap->a_p);
+ break;
+
+ default:
+ panic("fdesc_getattr");
+ break;
+ }
+
+ if (error == 0)
+ vp->v_type = vap->va_type;
+
+ return (error);
+}
+
+int
+fdesc_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct filedesc *fdp = ap->a_p->p_fd;
+ struct file *fp;
+ unsigned fd;
+ int error;
+
+ /*
+ * Can't mess with the root vnode
+ */
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fdesc:
+ break;
+
+ case Fctty:
+ return (0);
+
+ default:
+ return (EACCES);
+ }
+
+ fd = VTOFDESC(ap->a_vp)->fd_fd;
+ if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) {
+ return (EBADF);
+ }
+
+ /*
+ * Can setattr the underlying vnode, but not sockets!
+ */
+ switch (fp->f_type) {
+ case DTYPE_VNODE:
+ error = VOP_SETATTR((struct vnode *) fp->f_data, ap->a_vap, ap->a_cred, ap->a_p);
+ break;
+
+ case DTYPE_SOCKET:
+ error = 0;
+ break;
+
+ default:
+ panic("fdesc setattr");
+ break;
+ }
+
+ return (error);
+}
+
+#define UIO_MX 16
+
+static struct dirtmp {
+ u_long d_fileno;
+ u_short d_reclen;
+ u_short d_namlen;
+ char d_name[8];
+} rootent[] = {
+ { FD_DEVFD, UIO_MX, 2, "fd" },
+ { FD_STDIN, UIO_MX, 5, "stdin" },
+ { FD_STDOUT, UIO_MX, 6, "stdout" },
+ { FD_STDERR, UIO_MX, 6, "stderr" },
+ { FD_CTTY, UIO_MX, 3, "tty" },
+ { 0 }
+};
+
+int
+fdesc_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct uio *uio = ap->a_uio;
+ struct filedesc *fdp;
+ int i;
+ int error;
+
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fctty:
+ return (0);
+
+ case Fdesc:
+ return (ENOTDIR);
+
+ default:
+ break;
+ }
+
+ fdp = uio->uio_procp->p_fd;
+
+ if (VTOFDESC(ap->a_vp)->fd_type == Froot) {
+ struct dirent d;
+ struct dirent *dp = &d;
+ struct dirtmp *dt;
+
+ i = uio->uio_offset / UIO_MX;
+ error = 0;
+
+ while (uio->uio_resid > 0) {
+ dt = &rootent[i];
+ if (dt->d_fileno == 0) {
+ /**eofflagp = 1;*/
+ break;
+ }
+ i++;
+
+ switch (dt->d_fileno) {
+ case FD_CTTY:
+ if (cttyvp(uio->uio_procp) == NULL)
+ continue;
+ break;
+
+ case FD_STDIN:
+ case FD_STDOUT:
+ case FD_STDERR:
+ if ((dt->d_fileno-FD_STDIN) >= fdp->fd_nfiles)
+ continue;
+ if (fdp->fd_ofiles[dt->d_fileno-FD_STDIN] == NULL)
+ continue;
+ break;
+ }
+ bzero((caddr_t) dp, UIO_MX);
+ dp->d_fileno = dt->d_fileno;
+ dp->d_namlen = dt->d_namlen;
+ dp->d_type = DT_UNKNOWN;
+ dp->d_reclen = dt->d_reclen;
+ bcopy(dt->d_name, dp->d_name, dp->d_namlen+1);
+ error = uiomove((caddr_t) dp, UIO_MX, uio);
+ if (error)
+ break;
+ }
+ uio->uio_offset = i * UIO_MX;
+ return (error);
+ }
+
+ i = uio->uio_offset / UIO_MX;
+ error = 0;
+ while (uio->uio_resid > 0) {
+ if (i >= fdp->fd_nfiles)
+ break;
+
+ if (fdp->fd_ofiles[i] != NULL) {
+ struct dirent d;
+ struct dirent *dp = &d;
+
+ bzero((caddr_t) dp, UIO_MX);
+
+ dp->d_namlen = sprintf(dp->d_name, "%d", i);
+ dp->d_reclen = UIO_MX;
+ dp->d_type = DT_UNKNOWN;
+ dp->d_fileno = i + FD_STDIN;
+ /*
+ * And ship to userland
+ */
+ error = uiomove((caddr_t) dp, UIO_MX, uio);
+ if (error)
+ break;
+ }
+ i++;
+ }
+
+ uio->uio_offset = i * UIO_MX;
+ return (error);
+}
+
+int
+fdesc_readlink(ap)
+ struct vop_readlink_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ int error;
+
+ if (vp->v_type != VLNK)
+ return (EPERM);
+
+ if (VTOFDESC(vp)->fd_type == Flink) {
+ char *ln = VTOFDESC(vp)->fd_link;
+ error = uiomove(ln, strlen(ln), ap->a_uio);
+ } else {
+ error = EOPNOTSUPP;
+ }
+
+ return (error);
+}
+
+int
+fdesc_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error = EOPNOTSUPP;
+
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fctty:
+ error = cttyread(devctty, ap->a_uio, ap->a_ioflag);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
+
+int
+fdesc_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error = EOPNOTSUPP;
+
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fctty:
+ error = cttywrite(devctty, ap->a_uio, ap->a_ioflag);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
+
+int
+fdesc_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error = EOPNOTSUPP;
+
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fctty:
+ error = cttyioctl(devctty, ap->a_command, ap->a_data,
+ ap->a_fflag, ap->a_p);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
+
+int
+fdesc_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error = EOPNOTSUPP;
+
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fctty:
+ error = cttyselect(devctty, ap->a_fflags, ap->a_p);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
+
+int
+fdesc_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+
+ /*
+ * Clear out the v_type field to avoid
+ * nasty things happening in vgone().
+ */
+ vp->v_type = VNON;
+ return (0);
+}
+
+int
+fdesc_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+
+ remque(VTOFDESC(vp));
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = 0;
+
+ return (0);
+}
+
+/*
+ * Return POSIX pathconf information applicable to special devices.
+ */
+int
+fdesc_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_MAX_CANON:
+ *ap->a_retval = MAX_CANON;
+ return (0);
+ case _PC_MAX_INPUT:
+ *ap->a_retval = MAX_INPUT;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ case _PC_VDISABLE:
+ *ap->a_retval = _POSIX_VDISABLE;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Print out the contents of a /dev/fd vnode.
+ */
+/* ARGSUSED */
+int
+fdesc_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_NON, fdesc vnode\n");
+ return (0);
+}
+
+/*void*/
+int
+fdesc_vfree(ap)
+ struct vop_vfree_args /* {
+ struct vnode *a_pvp;
+ ino_t a_ino;
+ int a_mode;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * /dev/fd vnode unsupported operation
+ */
+int
+fdesc_enotsupp()
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * /dev/fd "should never get here" operation
+ */
+int
+fdesc_badop()
+{
+
+ panic("fdesc: bad op");
+ /* NOTREACHED */
+}
+
+/*
+ * /dev/fd vnode null operation
+ */
+int
+fdesc_nullop()
+{
+
+ return (0);
+}
+
+#define fdesc_create ((int (*) __P((struct vop_create_args *)))fdesc_enotsupp)
+#define fdesc_mknod ((int (*) __P((struct vop_mknod_args *)))fdesc_enotsupp)
+#define fdesc_close ((int (*) __P((struct vop_close_args *)))nullop)
+#define fdesc_access ((int (*) __P((struct vop_access_args *)))nullop)
+#define fdesc_mmap ((int (*) __P((struct vop_mmap_args *)))fdesc_enotsupp)
+#define fdesc_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
+#define fdesc_seek ((int (*) __P((struct vop_seek_args *)))nullop)
+#define fdesc_remove ((int (*) __P((struct vop_remove_args *)))fdesc_enotsupp)
+#define fdesc_link ((int (*) __P((struct vop_link_args *)))fdesc_enotsupp)
+#define fdesc_rename ((int (*) __P((struct vop_rename_args *)))fdesc_enotsupp)
+#define fdesc_mkdir ((int (*) __P((struct vop_mkdir_args *)))fdesc_enotsupp)
+#define fdesc_rmdir ((int (*) __P((struct vop_rmdir_args *)))fdesc_enotsupp)
+#define fdesc_symlink ((int (*) __P((struct vop_symlink_args *)))fdesc_enotsupp)
+#define fdesc_abortop ((int (*) __P((struct vop_abortop_args *)))nullop)
+#define fdesc_lock ((int (*) __P((struct vop_lock_args *)))nullop)
+#define fdesc_unlock ((int (*) __P((struct vop_unlock_args *)))nullop)
+#define fdesc_bmap ((int (*) __P((struct vop_bmap_args *)))fdesc_badop)
+#define fdesc_strategy ((int (*) __P((struct vop_strategy_args *)))fdesc_badop)
+#define fdesc_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+#define fdesc_advlock ((int (*) __P((struct vop_advlock_args *)))fdesc_enotsupp)
+#define fdesc_blkatoff \
+ ((int (*) __P((struct vop_blkatoff_args *)))fdesc_enotsupp)
+#define fdesc_vget ((int (*) __P((struct vop_vget_args *)))fdesc_enotsupp)
+#define fdesc_valloc ((int(*) __P(( \
+ struct vnode *pvp, \
+ int mode, \
+ struct ucred *cred, \
+ struct vnode **vpp))) fdesc_enotsupp)
+#define fdesc_truncate \
+ ((int (*) __P((struct vop_truncate_args *)))fdesc_enotsupp)
+#define fdesc_update ((int (*) __P((struct vop_update_args *)))fdesc_enotsupp)
+#define fdesc_bwrite ((int (*) __P((struct vop_bwrite_args *)))fdesc_enotsupp)
+
+int (**fdesc_vnodeop_p)();
+struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, fdesc_lookup }, /* lookup */
+ { &vop_create_desc, fdesc_create }, /* create */
+ { &vop_mknod_desc, fdesc_mknod }, /* mknod */
+ { &vop_open_desc, fdesc_open }, /* open */
+ { &vop_close_desc, fdesc_close }, /* close */
+ { &vop_access_desc, fdesc_access }, /* access */
+ { &vop_getattr_desc, fdesc_getattr }, /* getattr */
+ { &vop_setattr_desc, fdesc_setattr }, /* setattr */
+ { &vop_read_desc, fdesc_read }, /* read */
+ { &vop_write_desc, fdesc_write }, /* write */
+ { &vop_ioctl_desc, fdesc_ioctl }, /* ioctl */
+ { &vop_select_desc, fdesc_select }, /* select */
+ { &vop_mmap_desc, fdesc_mmap }, /* mmap */
+ { &vop_fsync_desc, fdesc_fsync }, /* fsync */
+ { &vop_seek_desc, fdesc_seek }, /* seek */
+ { &vop_remove_desc, fdesc_remove }, /* remove */
+ { &vop_link_desc, fdesc_link }, /* link */
+ { &vop_rename_desc, fdesc_rename }, /* rename */
+ { &vop_mkdir_desc, fdesc_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, fdesc_rmdir }, /* rmdir */
+ { &vop_symlink_desc, fdesc_symlink }, /* symlink */
+ { &vop_readdir_desc, fdesc_readdir }, /* readdir */
+ { &vop_readlink_desc, fdesc_readlink }, /* readlink */
+ { &vop_abortop_desc, fdesc_abortop }, /* abortop */
+ { &vop_inactive_desc, fdesc_inactive }, /* inactive */
+ { &vop_reclaim_desc, fdesc_reclaim }, /* reclaim */
+ { &vop_lock_desc, fdesc_lock }, /* lock */
+ { &vop_unlock_desc, fdesc_unlock }, /* unlock */
+ { &vop_bmap_desc, fdesc_bmap }, /* bmap */
+ { &vop_strategy_desc, fdesc_strategy }, /* strategy */
+ { &vop_print_desc, fdesc_print }, /* print */
+ { &vop_islocked_desc, fdesc_islocked }, /* islocked */
+ { &vop_pathconf_desc, fdesc_pathconf }, /* pathconf */
+ { &vop_advlock_desc, fdesc_advlock }, /* advlock */
+ { &vop_blkatoff_desc, fdesc_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, fdesc_valloc }, /* valloc */
+ { &vop_vfree_desc, fdesc_vfree }, /* vfree */
+ { &vop_truncate_desc, fdesc_truncate }, /* truncate */
+ { &vop_update_desc, fdesc_update }, /* update */
+ { &vop_bwrite_desc, fdesc_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc fdesc_vnodeop_opv_desc =
+ { &fdesc_vnodeop_p, fdesc_vnodeop_entries };
diff --git a/sys/fs/fifofs/fifo.h b/sys/fs/fifofs/fifo.h
new file mode 100644
index 0000000..e89186d
--- /dev/null
+++ b/sys/fs/fifofs/fifo.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fifo.h 8.2 (Berkeley) 2/2/94
+ */
+
+#ifdef FIFO
+/*
+ * Prototypes for fifo operations on vnodes.
+ */
+int fifo_badop(),
+ fifo_ebadf();
+
+int fifo_lookup __P((struct vop_lookup_args *));
+#define fifo_create ((int (*) __P((struct vop_create_args *)))fifo_badop)
+#define fifo_mknod ((int (*) __P((struct vop_mknod_args *)))fifo_badop)
+int fifo_open __P((struct vop_open_args *));
+int fifo_close __P((struct vop_close_args *));
+#define fifo_access ((int (*) __P((struct vop_access_args *)))fifo_ebadf)
+#define fifo_getattr ((int (*) __P((struct vop_getattr_args *)))fifo_ebadf)
+#define fifo_setattr ((int (*) __P((struct vop_setattr_args *)))fifo_ebadf)
+int fifo_read __P((struct vop_read_args *));
+int fifo_write __P((struct vop_write_args *));
+int fifo_ioctl __P((struct vop_ioctl_args *));
+int fifo_select __P((struct vop_select_args *));
+#define fifo_mmap ((int (*) __P((struct vop_mmap_args *)))fifo_badop)
+#define fifo_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
+#define fifo_seek ((int (*) __P((struct vop_seek_args *)))fifo_badop)
+#define fifo_remove ((int (*) __P((struct vop_remove_args *)))fifo_badop)
+#define fifo_link ((int (*) __P((struct vop_link_args *)))fifo_badop)
+#define fifo_rename ((int (*) __P((struct vop_rename_args *)))fifo_badop)
+#define fifo_mkdir ((int (*) __P((struct vop_mkdir_args *)))fifo_badop)
+#define fifo_rmdir ((int (*) __P((struct vop_rmdir_args *)))fifo_badop)
+#define fifo_symlink ((int (*) __P((struct vop_symlink_args *)))fifo_badop)
+#define fifo_readdir ((int (*) __P((struct vop_readdir_args *)))fifo_badop)
+#define fifo_readlink ((int (*) __P((struct vop_readlink_args *)))fifo_badop)
+#define fifo_abortop ((int (*) __P((struct vop_abortop_args *)))fifo_badop)
+#define fifo_inactive ((int (*) __P((struct vop_inactive_args *)))nullop)
+#define fifo_reclaim ((int (*) __P((struct vop_reclaim_args *)))nullop)
+int fifo_lock __P((struct vop_lock_args *));
+int fifo_unlock __P((struct vop_unlock_args *));
+int fifo_bmap __P((struct vop_bmap_args *));
+#define fifo_strategy ((int (*) __P((struct vop_strategy_args *)))fifo_badop)
+int fifo_print __P((struct vop_print_args *));
+#define fifo_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+int fifo_pathconf __P((struct vop_pathconf_args *));
+int fifo_advlock __P((struct vop_advlock_args *));
+#define fifo_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))fifo_badop)
+#define fifo_valloc ((int (*) __P((struct vop_valloc_args *)))fifo_badop)
+#define fifo_reallocblks \
+ ((int (*) __P((struct vop_reallocblks_args *)))fifo_badop)
+#define fifo_vfree ((int (*) __P((struct vop_vfree_args *)))fifo_badop)
+#define fifo_truncate ((int (*) __P((struct vop_truncate_args *)))nullop)
+#define fifo_update ((int (*) __P((struct vop_update_args *)))nullop)
+#define fifo_bwrite ((int (*) __P((struct vop_bwrite_args *)))nullop)
+#endif /* FIFO */
diff --git a/sys/fs/fifofs/fifo_vnops.c b/sys/fs/fifofs/fifo_vnops.c
new file mode 100644
index 0000000..a1ba3f4
--- /dev/null
+++ b/sys/fs/fifofs/fifo_vnops.c
@@ -0,0 +1,512 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fifo_vnops.c 8.2 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/time.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/stat.h>
+#include <sys/systm.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <miscfs/fifofs/fifo.h>
+
+/*
+ * This structure is associated with the FIFO vnode and stores
+ * the state associated with the FIFO.
+ */
+struct fifoinfo {
+ struct socket *fi_readsock;
+ struct socket *fi_writesock;
+ long fi_readers;
+ long fi_writers;
+};
+
+int (**fifo_vnodeop_p)();
+struct vnodeopv_entry_desc fifo_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, fifo_lookup }, /* lookup */
+ { &vop_create_desc, fifo_create }, /* create */
+ { &vop_mknod_desc, fifo_mknod }, /* mknod */
+ { &vop_open_desc, fifo_open }, /* open */
+ { &vop_close_desc, fifo_close }, /* close */
+ { &vop_access_desc, fifo_access }, /* access */
+ { &vop_getattr_desc, fifo_getattr }, /* getattr */
+ { &vop_setattr_desc, fifo_setattr }, /* setattr */
+ { &vop_read_desc, fifo_read }, /* read */
+ { &vop_write_desc, fifo_write }, /* write */
+ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */
+ { &vop_select_desc, fifo_select }, /* select */
+ { &vop_mmap_desc, fifo_mmap }, /* mmap */
+ { &vop_fsync_desc, fifo_fsync }, /* fsync */
+ { &vop_seek_desc, fifo_seek }, /* seek */
+ { &vop_remove_desc, fifo_remove }, /* remove */
+ { &vop_link_desc, fifo_link }, /* link */
+ { &vop_rename_desc, fifo_rename }, /* rename */
+ { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */
+ { &vop_symlink_desc, fifo_symlink }, /* symlink */
+ { &vop_readdir_desc, fifo_readdir }, /* readdir */
+ { &vop_readlink_desc, fifo_readlink }, /* readlink */
+ { &vop_abortop_desc, fifo_abortop }, /* abortop */
+ { &vop_inactive_desc, fifo_inactive }, /* inactive */
+ { &vop_reclaim_desc, fifo_reclaim }, /* reclaim */
+ { &vop_lock_desc, fifo_lock }, /* lock */
+ { &vop_unlock_desc, fifo_unlock }, /* unlock */
+ { &vop_bmap_desc, fifo_bmap }, /* bmap */
+ { &vop_strategy_desc, fifo_strategy }, /* strategy */
+ { &vop_print_desc, fifo_print }, /* print */
+ { &vop_islocked_desc, fifo_islocked }, /* islocked */
+ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */
+ { &vop_advlock_desc, fifo_advlock }, /* advlock */
+ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, fifo_valloc }, /* valloc */
+ { &vop_vfree_desc, fifo_vfree }, /* vfree */
+ { &vop_truncate_desc, fifo_truncate }, /* truncate */
+ { &vop_update_desc, fifo_update }, /* update */
+ { &vop_bwrite_desc, fifo_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc fifo_vnodeop_opv_desc =
+ { &fifo_vnodeop_p, fifo_vnodeop_entries };
+
+/*
+ * Trivial lookup routine that always fails.
+ */
+/* ARGSUSED */
+int
+fifo_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode * a_dvp;
+ struct vnode ** a_vpp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+
+ *ap->a_vpp = NULL;
+ return (ENOTDIR);
+}
+
+/*
+ * Open called to set up a new instance of a fifo or
+ * to find an active instance of a fifo.
+ */
+/* ARGSUSED */
+int
+fifo_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct fifoinfo *fip;
+ struct socket *rso, *wso;
+ int error;
+ static char openstr[] = "fifo";
+
+ if ((ap->a_mode & (FREAD|FWRITE)) == (FREAD|FWRITE))
+ return (EINVAL);
+ if ((fip = vp->v_fifoinfo) == NULL) {
+ MALLOC(fip, struct fifoinfo *, sizeof(*fip), M_VNODE, M_WAITOK);
+ vp->v_fifoinfo = fip;
+ if (error = socreate(AF_UNIX, &rso, SOCK_STREAM, 0)) {
+ free(fip, M_VNODE);
+ vp->v_fifoinfo = NULL;
+ return (error);
+ }
+ fip->fi_readsock = rso;
+ if (error = socreate(AF_UNIX, &wso, SOCK_STREAM, 0)) {
+ (void)soclose(rso);
+ free(fip, M_VNODE);
+ vp->v_fifoinfo = NULL;
+ return (error);
+ }
+ fip->fi_writesock = wso;
+ if (error = unp_connect2(wso, rso)) {
+ (void)soclose(wso);
+ (void)soclose(rso);
+ free(fip, M_VNODE);
+ vp->v_fifoinfo = NULL;
+ return (error);
+ }
+ fip->fi_readers = fip->fi_writers = 0;
+ wso->so_state |= SS_CANTRCVMORE;
+ rso->so_state |= SS_CANTSENDMORE;
+ }
+ error = 0;
+ if (ap->a_mode & FREAD) {
+ fip->fi_readers++;
+ if (fip->fi_readers == 1) {
+ fip->fi_writesock->so_state &= ~SS_CANTSENDMORE;
+ if (fip->fi_writers > 0)
+ wakeup((caddr_t)&fip->fi_writers);
+ }
+ if (ap->a_mode & O_NONBLOCK)
+ return (0);
+ while (fip->fi_writers == 0) {
+ VOP_UNLOCK(vp);
+ error = tsleep((caddr_t)&fip->fi_readers,
+ PCATCH | PSOCK, openstr, 0);
+ VOP_LOCK(vp);
+ if (error)
+ break;
+ }
+ } else {
+ fip->fi_writers++;
+ if (fip->fi_readers == 0 && (ap->a_mode & O_NONBLOCK)) {
+ error = ENXIO;
+ } else {
+ if (fip->fi_writers == 1) {
+ fip->fi_readsock->so_state &= ~SS_CANTRCVMORE;
+ if (fip->fi_readers > 0)
+ wakeup((caddr_t)&fip->fi_readers);
+ }
+ while (fip->fi_readers == 0) {
+ VOP_UNLOCK(vp);
+ error = tsleep((caddr_t)&fip->fi_writers,
+ PCATCH | PSOCK, openstr, 0);
+ VOP_LOCK(vp);
+ if (error)
+ break;
+ }
+ }
+ }
+ if (error)
+ VOP_CLOSE(vp, ap->a_mode, ap->a_cred, ap->a_p);
+ return (error);
+}
+
+/*
+ * Vnode op for read
+ */
+/* ARGSUSED */
+int
+fifo_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct uio *uio = ap->a_uio;
+ register struct socket *rso = ap->a_vp->v_fifoinfo->fi_readsock;
+ int error, startresid;
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_READ)
+ panic("fifo_read mode");
+#endif
+ if (uio->uio_resid == 0)
+ return (0);
+ if (ap->a_ioflag & IO_NDELAY)
+ rso->so_state |= SS_NBIO;
+ startresid = uio->uio_resid;
+ VOP_UNLOCK(ap->a_vp);
+ error = soreceive(rso, (struct mbuf **)0, uio, (int *)0,
+ (struct mbuf **)0, (struct mbuf **)0);
+ VOP_LOCK(ap->a_vp);
+ /*
+ * Clear EOF indication after first such return.
+ */
+ if (uio->uio_resid == startresid)
+ rso->so_state &= ~SS_CANTRCVMORE;
+ if (ap->a_ioflag & IO_NDELAY)
+ rso->so_state &= ~SS_NBIO;
+ return (error);
+}
+
+/*
+ * Vnode op for write
+ */
+/* ARGSUSED */
+int
+fifo_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct socket *wso = ap->a_vp->v_fifoinfo->fi_writesock;
+ int error;
+
+#ifdef DIAGNOSTIC
+ if (ap->a_uio->uio_rw != UIO_WRITE)
+ panic("fifo_write mode");
+#endif
+ if (ap->a_ioflag & IO_NDELAY)
+ wso->so_state |= SS_NBIO;
+ VOP_UNLOCK(ap->a_vp);
+ error = sosend(wso, (struct mbuf *)0, ap->a_uio, 0, (struct mbuf *)0, 0);
+ VOP_LOCK(ap->a_vp);
+ if (ap->a_ioflag & IO_NDELAY)
+ wso->so_state &= ~SS_NBIO;
+ return (error);
+}
+
+/*
+ * Device ioctl operation.
+ */
+/* ARGSUSED */
+int
+fifo_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct file filetmp;
+
+ if (ap->a_command == FIONBIO)
+ return (0);
+ if (ap->a_fflag & FREAD)
+ filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock;
+ else
+ filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock;
+ return (soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p));
+}
+
+/* ARGSUSED */
+int
+fifo_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct file filetmp;
+
+ if (ap->a_fflags & FREAD)
+ filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock;
+ else
+ filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock;
+ return (soo_select(&filetmp, ap->a_which, ap->a_p));
+}
+
+/*
+ * This is a noop, simply returning what one has been given.
+ */
+int
+fifo_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ } */ *ap;
+{
+
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = ap->a_vp;
+ if (ap->a_bnp != NULL)
+ *ap->a_bnp = ap->a_bn;
+ return (0);
+}
+
+/*
+ * At the moment we do not do any locking.
+ */
+/* ARGSUSED */
+int
+fifo_lock(ap)
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/* ARGSUSED */
+int
+fifo_unlock(ap)
+ struct vop_unlock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * Device close routine
+ */
+/* ARGSUSED */
+int
+fifo_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct fifoinfo *fip = vp->v_fifoinfo;
+ int error1, error2;
+
+ if (ap->a_fflag & FWRITE) {
+ fip->fi_writers--;
+ if (fip->fi_writers == 0)
+ socantrcvmore(fip->fi_readsock);
+ } else {
+ fip->fi_readers--;
+ if (fip->fi_readers == 0)
+ socantsendmore(fip->fi_writesock);
+ }
+ if (vp->v_usecount > 1)
+ return (0);
+ error1 = soclose(fip->fi_readsock);
+ error2 = soclose(fip->fi_writesock);
+ FREE(fip, M_VNODE);
+ vp->v_fifoinfo = NULL;
+ if (error1)
+ return (error1);
+ return (error2);
+}
+
+/*
+ * Print out the contents of a fifo vnode.
+ */
+int
+fifo_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_NON");
+ fifo_printinfo(ap->a_vp);
+ printf("\n");
+ return (0);
+}
+
+/*
+ * Print out internal contents of a fifo vnode.
+ */
+int
+fifo_printinfo(vp)
+ struct vnode *vp;
+{
+ register struct fifoinfo *fip = vp->v_fifoinfo;
+
+ printf(", fifo with %d readers and %d writers",
+ fip->fi_readers, fip->fi_writers);
+ return (0);
+}
+
+/*
+ * Return POSIX pathconf information applicable to fifo's.
+ */
+int
+fifo_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Fifo failed operation
+ */
+int
+fifo_ebadf()
+{
+
+ return (EBADF);
+}
+
+/*
+ * Fifo advisory byte-level locks.
+ */
+/* ARGSUSED */
+int
+fifo_advlock(ap)
+ struct vop_advlock_args /* {
+ struct vnode *a_vp;
+ caddr_t a_id;
+ int a_op;
+ struct flock *a_fl;
+ int a_flags;
+ } */ *ap;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * Fifo bad operation
+ */
+int
+fifo_badop()
+{
+
+ panic("fifo_badop called");
+ /* NOTREACHED */
+}
diff --git a/sys/fs/nullfs/null.h b/sys/fs/nullfs/null.h
new file mode 100644
index 0000000..14286ff
--- /dev/null
+++ b/sys/fs/nullfs/null.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)null.h 8.2 (Berkeley) 1/21/94
+ *
+ * $Id: lofs.h,v 1.8 1992/05/30 10:05:43 jsp Exp jsp $
+ */
+
+struct null_args {
+ char *target; /* Target of loopback */
+};
+
+struct null_mount {
+ struct mount *nullm_vfs;
+ struct vnode *nullm_rootvp; /* Reference to root null_node */
+};
+
+#ifdef KERNEL
+/*
+ * A cache of vnode references
+ */
+struct null_node {
+ struct null_node *null_forw; /* Hash chain */
+ struct null_node *null_back;
+ struct vnode *null_lowervp; /* VREFed once */
+ struct vnode *null_vnode; /* Back pointer */
+};
+
+extern int null_node_create __P((struct mount *mp, struct vnode *target, struct vnode **vpp));
+
+#define MOUNTTONULLMOUNT(mp) ((struct null_mount *)((mp)->mnt_data))
+#define VTONULL(vp) ((struct null_node *)(vp)->v_data)
+#define NULLTOV(xp) ((xp)->null_vnode)
+#ifdef NULLFS_DIAGNOSTIC
+extern struct vnode *null_checkvp __P((struct vnode *vp, char *fil, int lno));
+#define NULLVPTOLOWERVP(vp) null_checkvp((vp), __FILE__, __LINE__)
+#else
+#define NULLVPTOLOWERVP(vp) (VTONULL(vp)->null_lowervp)
+#endif
+
+extern int (**null_vnodeop_p)();
+extern struct vfsops null_vfsops;
+#endif /* KERNEL */
diff --git a/sys/fs/nullfs/null_subr.c b/sys/fs/nullfs/null_subr.c
new file mode 100644
index 0000000..5541fb3
--- /dev/null
+++ b/sys/fs/nullfs/null_subr.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)null_subr.c 8.4 (Berkeley) 1/21/94
+ *
+ * $Id: lofs_subr.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <miscfs/nullfs/null.h>
+
+#define LOG2_SIZEVNODE 7 /* log2(sizeof struct vnode) */
+#define NNULLNODECACHE 16
+#define NULL_NHASH(vp) ((((u_long)vp)>>LOG2_SIZEVNODE) & (NNULLNODECACHE-1))
+
+/*
+ * Null layer cache:
+ * Each cache entry holds a reference to the lower vnode
+ * along with a pointer to the alias vnode. When an
+ * entry is added the lower vnode is VREF'd. When the
+ * alias is removed the lower vnode is vrele'd.
+ */
+
+/*
+ * Cache head
+ */
+struct null_node_cache {
+ struct null_node *ac_forw;
+ struct null_node *ac_back;
+};
+
+static struct null_node_cache null_node_cache[NNULLNODECACHE];
+
+/*
+ * Initialise cache headers
+ */
+int
+nullfs_init()
+{
+ struct null_node_cache *ac;
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_init\n"); /* printed during system boot */
+#endif
+
+ for (ac = null_node_cache; ac < null_node_cache + NNULLNODECACHE; ac++)
+ ac->ac_forw = ac->ac_back = (struct null_node *) ac;
+ return (0);
+}
+
+/*
+ * Compute hash list for given lower vnode
+ */
+static struct null_node_cache *
+null_node_hash(lowervp)
+struct vnode *lowervp;
+{
+
+ return (&null_node_cache[NULL_NHASH(lowervp)]);
+}
+
+/*
+ * Return a VREF'ed alias for lower vnode if already exists, else 0.
+ */
+static struct vnode *
+null_node_find(mp, lowervp)
+ struct mount *mp;
+ struct vnode *lowervp;
+{
+ struct null_node_cache *hd;
+ struct null_node *a;
+ struct vnode *vp;
+
+ /*
+ * Find hash base, and then search the (two-way) linked
+ * list looking for a null_node structure which is referencing
+ * the lower vnode. If found, the increment the null_node
+ * reference count (but NOT the lower vnode's VREF counter).
+ */
+ hd = null_node_hash(lowervp);
+loop:
+ for (a = hd->ac_forw; a != (struct null_node *) hd; a = a->null_forw) {
+ if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) {
+ vp = NULLTOV(a);
+ /*
+ * We need vget for the VXLOCK
+ * stuff, but we don't want to lock
+ * the lower node.
+ */
+ if (vget(vp, 0)) {
+ printf ("null_node_find: vget failed.\n");
+ goto loop;
+ };
+ return (vp);
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Make a new null_node node.
+ * Vp is the alias vnode, lofsvp is the lower vnode.
+ * Maintain a reference to (lowervp).
+ */
+static int
+null_node_alloc(mp, lowervp, vpp)
+ struct mount *mp;
+ struct vnode *lowervp;
+ struct vnode **vpp;
+{
+ struct null_node_cache *hd;
+ struct null_node *xp;
+ struct vnode *othervp, *vp;
+ int error;
+
+ if (error = getnewvnode(VT_NULL, mp, null_vnodeop_p, vpp))
+ return (error);
+ vp = *vpp;
+
+ MALLOC(xp, struct null_node *, sizeof(struct null_node), M_TEMP, M_WAITOK);
+ vp->v_type = lowervp->v_type;
+ xp->null_vnode = vp;
+ vp->v_data = xp;
+ xp->null_lowervp = lowervp;
+ /*
+ * Before we insert our new node onto the hash chains,
+ * check to see if someone else has beaten us to it.
+ * (We could have slept in MALLOC.)
+ */
+ if (othervp = null_node_find(lowervp)) {
+ FREE(xp, M_TEMP);
+ vp->v_type = VBAD; /* node is discarded */
+ vp->v_usecount = 0; /* XXX */
+ *vpp = othervp;
+ return 0;
+ };
+ VREF(lowervp); /* Extra VREF will be vrele'd in null_node_create */
+ hd = null_node_hash(lowervp);
+ insque(xp, hd);
+ return 0;
+}
+
+
+/*
+ * Try to find an existing null_node vnode refering
+ * to it, otherwise make a new null_node vnode which
+ * contains a reference to the lower vnode.
+ */
+int
+null_node_create(mp, lowervp, newvpp)
+ struct mount *mp;
+ struct vnode *lowervp;
+ struct vnode **newvpp;
+{
+ struct vnode *aliasvp;
+
+ if (aliasvp = null_node_find(mp, lowervp)) {
+ /*
+ * null_node_find has taken another reference
+ * to the alias vnode.
+ */
+#ifdef NULLFS_DIAGNOSTIC
+ vprint("null_node_create: exists", NULLTOV(ap));
+#endif
+ /* VREF(aliasvp); --- done in null_node_find */
+ } else {
+ int error;
+
+ /*
+ * Get new vnode.
+ */
+#ifdef NULLFS_DIAGNOSTIC
+ printf("null_node_create: create new alias vnode\n");
+#endif
+
+ /*
+ * Make new vnode reference the null_node.
+ */
+ if (error = null_node_alloc(mp, lowervp, &aliasvp))
+ return error;
+
+ /*
+ * aliasvp is already VREF'd by getnewvnode()
+ */
+ }
+
+ vrele(lowervp);
+
+#ifdef DIAGNOSTIC
+ if (lowervp->v_usecount < 1) {
+ /* Should never happen... */
+ vprint ("null_node_create: alias ");
+ vprint ("null_node_create: lower ");
+ printf ("null_node_create: lower has 0 usecount.\n");
+ panic ("null_node_create: lower has 0 usecount.");
+ };
+#endif
+
+#ifdef NULLFS_DIAGNOSTIC
+ vprint("null_node_create: alias", aliasvp);
+ vprint("null_node_create: lower", lowervp);
+#endif
+
+ *newvpp = aliasvp;
+ return (0);
+}
+#ifdef NULLFS_DIAGNOSTIC
+struct vnode *
+null_checkvp(vp, fil, lno)
+ struct vnode *vp;
+ char *fil;
+ int lno;
+{
+ struct null_node *a = VTONULL(vp);
+#ifdef notyet
+ /*
+ * Can't do this check because vop_reclaim runs
+ * with a funny vop vector.
+ */
+ if (vp->v_op != null_vnodeop_p) {
+ printf ("null_checkvp: on non-null-node\n");
+ while (null_checkvp_barrier) /*WAIT*/ ;
+ panic("null_checkvp");
+ };
+#endif
+ if (a->null_lowervp == NULL) {
+ /* Should never happen */
+ int i; u_long *p;
+ printf("vp = %x, ZERO ptr\n", vp);
+ for (p = (u_long *) a, i = 0; i < 8; i++)
+ printf(" %x", p[i]);
+ printf("\n");
+ /* wait for debugger */
+ while (null_checkvp_barrier) /*WAIT*/ ;
+ panic("null_checkvp");
+ }
+ if (a->null_lowervp->v_usecount < 1) {
+ int i; u_long *p;
+ printf("vp = %x, unref'ed lowervp\n", vp);
+ for (p = (u_long *) a, i = 0; i < 8; i++)
+ printf(" %x", p[i]);
+ printf("\n");
+ /* wait for debugger */
+ while (null_checkvp_barrier) /*WAIT*/ ;
+ panic ("null with unref'ed lowervp");
+ };
+#ifdef notyet
+ printf("null %x/%d -> %x/%d [%s, %d]\n",
+ NULLTOV(a), NULLTOV(a)->v_usecount,
+ a->null_lowervp, a->null_lowervp->v_usecount,
+ fil, lno);
+#endif
+ return a->null_lowervp;
+}
+#endif
diff --git a/sys/fs/nullfs/null_vfsops.c b/sys/fs/nullfs/null_vfsops.c
new file mode 100644
index 0000000..b0d2df7
--- /dev/null
+++ b/sys/fs/nullfs/null_vfsops.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)null_vfsops.c 8.2 (Berkeley) 1/21/94
+ *
+ * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92
+ * $Id: lofs_vfsops.c,v 1.9 1992/05/30 10:26:24 jsp Exp jsp $
+ */
+
+/*
+ * Null Layer
+ * (See null_vnops.c for a description of what this does.)
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <miscfs/nullfs/null.h>
+
+/*
+ * Mount null layer
+ */
+int
+nullfs_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ int error = 0;
+ struct null_args args;
+ struct vnode *lowerrootvp, *vp;
+ struct vnode *nullm_rootvp;
+ struct null_mount *xmp;
+ u_int size;
+
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_mount(mp = %x)\n", mp);
+#endif
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ return (EOPNOTSUPP);
+ /* return VFS_MOUNT(MOUNTTONULLMOUNT(mp)->nullm_vfs, path, data, ndp, p);*/
+ }
+
+ /*
+ * Get argument
+ */
+ if (error = copyin(data, (caddr_t)&args, sizeof(struct null_args)))
+ return (error);
+
+ /*
+ * Find lower node
+ */
+ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF,
+ UIO_USERSPACE, args.target, p);
+ if (error = namei(ndp))
+ return (error);
+
+ /*
+ * Sanity check on lower vnode
+ */
+ lowerrootvp = ndp->ni_vp;
+
+ vrele(ndp->ni_dvp);
+ ndp->ni_dvp = NULL;
+
+ xmp = (struct null_mount *) malloc(sizeof(struct null_mount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+
+ /*
+ * Save reference to underlying FS
+ */
+ xmp->nullm_vfs = lowerrootvp->v_mount;
+
+ /*
+ * Save reference. Each mount also holds
+ * a reference on the root vnode.
+ */
+ error = null_node_create(mp, lowerrootvp, &vp);
+ /*
+ * Unlock the node (either the lower or the alias)
+ */
+ VOP_UNLOCK(vp);
+ /*
+ * Make sure the node alias worked
+ */
+ if (error) {
+ vrele(lowerrootvp);
+ free(xmp, M_UFSMNT); /* XXX */
+ return (error);
+ }
+
+ /*
+ * Keep a held reference to the root vnode.
+ * It is vrele'd in nullfs_unmount.
+ */
+ nullm_rootvp = vp;
+ nullm_rootvp->v_flag |= VROOT;
+ xmp->nullm_rootvp = nullm_rootvp;
+ if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL)
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_data = (qaddr_t) xmp;
+ getnewfsid(mp, MOUNT_LOFS);
+
+ (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+ (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_mount: lower %s, alias at %s\n",
+ mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
+#endif
+ return (0);
+}
+
+/*
+ * VFS start. Nothing needed here - the start routine
+ * on the underlying filesystem will have been called
+ * when that filesystem was mounted.
+ */
+int
+nullfs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ return (0);
+ /* return VFS_START(MOUNTTONULLMOUNT(mp)->nullm_vfs, flags, p); */
+}
+
+/*
+ * Free reference to null layer
+ */
+int
+nullfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ struct vnode *nullm_rootvp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
+ int error;
+ int flags = 0;
+ extern int doforce;
+
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_unmount(mp = %x)\n", mp);
+#endif
+
+ if (mntflags & MNT_FORCE) {
+ /* lofs can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ /*
+ * Clear out buffer cache. I don't think we
+ * ever get anything cached at this level at the
+ * moment, but who knows...
+ */
+#if 0
+ mntflushbuf(mp, 0);
+ if (mntinvalbuf(mp, 1))
+ return (EBUSY);
+#endif
+ if (nullm_rootvp->v_usecount > 1)
+ return (EBUSY);
+ if (error = vflush(mp, nullm_rootvp, flags))
+ return (error);
+
+#ifdef NULLFS_DIAGNOSTIC
+ vprint("alias root of lower", nullm_rootvp);
+#endif
+ /*
+ * Release reference on underlying root vnode
+ */
+ vrele(nullm_rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(nullm_rootvp);
+ /*
+ * Finally, throw away the null_mount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ mp->mnt_data = 0;
+ return 0;
+}
+
+int
+nullfs_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct vnode *vp;
+
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_root(mp = %x, vp = %x->%x)\n", mp,
+ MOUNTTONULLMOUNT(mp)->nullm_rootvp,
+ NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)
+ );
+#endif
+
+ /*
+ * Return locked reference to root.
+ */
+ vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
+ VREF(vp);
+ VOP_LOCK(vp);
+ *vpp = vp;
+ return 0;
+}
+
+int
+nullfs_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+ return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg, p);
+}
+
+int
+nullfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ int error;
+ struct statfs mstat;
+
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_statfs(mp = %x, vp = %x->%x)\n", mp,
+ MOUNTTONULLMOUNT(mp)->nullm_rootvp,
+ NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)
+ );
+#endif
+
+ bzero(&mstat, sizeof(mstat));
+
+ error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat, p);
+ if (error)
+ return (error);
+
+ /* now copy across the "interesting" information and fake the rest */
+ sbp->f_type = mstat.f_type;
+ sbp->f_flags = mstat.f_flags;
+ sbp->f_bsize = mstat.f_bsize;
+ sbp->f_iosize = mstat.f_iosize;
+ sbp->f_blocks = mstat.f_blocks;
+ sbp->f_bfree = mstat.f_bfree;
+ sbp->f_bavail = mstat.f_bavail;
+ sbp->f_files = mstat.f_files;
+ sbp->f_ffree = mstat.f_ffree;
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+ return (0);
+}
+
+int
+nullfs_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+ /*
+ * XXX - Assumes no data cached at null layer.
+ */
+ return (0);
+}
+
+int
+nullfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, vpp);
+}
+
+int
+nullfs_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
+ struct mount *mp;
+ struct fid *fidp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred**credanonp;
+{
+
+ return VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, nam, vpp, exflagsp,credanonp);
+}
+
+int
+nullfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+ return VFS_VPTOFH(NULLVPTOLOWERVP(vp), fhp);
+}
+
+int nullfs_init __P((void));
+
+struct vfsops null_vfsops = {
+ nullfs_mount,
+ nullfs_start,
+ nullfs_unmount,
+ nullfs_root,
+ nullfs_quotactl,
+ nullfs_statfs,
+ nullfs_sync,
+ nullfs_vget,
+ nullfs_fhtovp,
+ nullfs_vptofh,
+ nullfs_init,
+};
diff --git a/sys/fs/nullfs/null_vnops.c b/sys/fs/nullfs/null_vnops.c
new file mode 100644
index 0000000..115ff6f
--- /dev/null
+++ b/sys/fs/nullfs/null_vnops.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * John Heidemann of the UCLA Ficus project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)null_vnops.c 8.1 (Berkeley) 6/10/93
+ *
+ * Ancestors:
+ * @(#)lofs_vnops.c 1.2 (Berkeley) 6/18/92
+ * $Id: lofs_vnops.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
+ * ...and...
+ * @(#)null_vnodeops.c 1.20 92/07/07 UCLA Ficus project
+ */
+
+/*
+ * Null Layer
+ *
+ * (See mount_null(8) for more information.)
+ *
+ * The null layer duplicates a portion of the file system
+ * name space under a new name. In this respect, it is
+ * similar to the loopback file system. It differs from
+ * the loopback fs in two respects: it is implemented using
+ * a stackable layers techniques, and it's "null-node"s stack above
+ * all lower-layer vnodes, not just over directory vnodes.
+ *
+ * The null layer has two purposes. First, it serves as a demonstration
+ * of layering by proving a layer which does nothing. (It actually
+ * does everything the loopback file system does, which is slightly
+ * more than nothing.) Second, the null layer can serve as a prototype
+ * layer. Since it provides all necessary layer framework,
+ * new file system layers can be created very easily be starting
+ * with a null layer.
+ *
+ * The remainder of this man page examines the null layer as a basis
+ * for constructing new layers.
+ *
+ *
+ * INSTANTIATING NEW NULL LAYERS
+ *
+ * New null layers are created with mount_null(8).
+ * Mount_null(8) takes two arguments, the pathname
+ * of the lower vfs (target-pn) and the pathname where the null
+ * layer will appear in the namespace (alias-pn). After
+ * the null layer is put into place, the contents
+ * of target-pn subtree will be aliased under alias-pn.
+ *
+ *
+ * OPERATION OF A NULL LAYER
+ *
+ * The null layer is the minimum file system layer,
+ * simply bypassing all possible operations to the lower layer
+ * for processing there. The majority of its activity centers
+ * on the bypass routine, though which nearly all vnode operations
+ * pass.
+ *
+ * The bypass routine accepts arbitrary vnode operations for
+ * handling by the lower layer. It begins by examing vnode
+ * operation arguments and replacing any null-nodes by their
+ * lower-layer equivlants. It then invokes the operation
+ * on the lower layer. Finally, it replaces the null-nodes
+ * in the arguments and, if a vnode is return by the operation,
+ * stacks a null-node on top of the returned vnode.
+ *
+ * Although bypass handles most operations,
+ * vop_getattr, _inactive, _reclaim, and _print are not bypassed.
+ * Vop_getattr must change the fsid being returned.
+ * Vop_inactive and vop_reclaim are not bypassed so that
+ * they can handle freeing null-layer specific data.
+ * Vop_print is not bypassed to avoid excessive debugging
+ * information.
+ *
+ *
+ * INSTANTIATING VNODE STACKS
+ *
+ * Mounting associates the null layer with a lower layer,
+ * effect stacking two VFSes. Vnode stacks are instead
+ * created on demand as files are accessed.
+ *
+ * The initial mount creates a single vnode stack for the
+ * root of the new null layer. All other vnode stacks
+ * are created as a result of vnode operations on
+ * this or other null vnode stacks.
+ *
+ * New vnode stacks come into existance as a result of
+ * an operation which returns a vnode.
+ * The bypass routine stacks a null-node above the new
+ * vnode before returning it to the caller.
+ *
+ * For example, imagine mounting a null layer with
+ * "mount_null /usr/include /dev/layer/null".
+ * Changing directory to /dev/layer/null will assign
+ * the root null-node (which was created when the null layer was mounted).
+ * Now consider opening "sys". A vop_lookup would be
+ * done on the root null-node. This operation would bypass through
+ * to the lower layer which would return a vnode representing
+ * the UFS "sys". Null_bypass then builds a null-node
+ * aliasing the UFS "sys" and returns this to the caller.
+ * Later operations on the null-node "sys" will repeat this
+ * process when constructing other vnode stacks.
+ *
+ *
+ * CREATING OTHER FILE SYSTEM LAYERS
+ *
+ * One of the easiest ways to construct new file system layers is to make
+ * a copy of the null layer, rename all files and variables, and
+ * then begin modifing the copy. Sed can be used to easily rename
+ * all variables.
+ *
+ * The umap layer is an example of a layer descended from the
+ * null layer.
+ *
+ *
+ * INVOKING OPERATIONS ON LOWER LAYERS
+ *
+ * There are two techniques to invoke operations on a lower layer
+ * when the operation cannot be completely bypassed. Each method
+ * is appropriate in different situations. In both cases,
+ * it is the responsibility of the aliasing layer to make
+ * the operation arguments "correct" for the lower layer
+ * by mapping an vnode arguments to the lower layer.
+ *
+ * The first approach is to call the aliasing layer's bypass routine.
+ * This method is most suitable when you wish to invoke the operation
+ * currently being hanldled on the lower layer. It has the advantage
+ * that the bypass routine already must do argument mapping.
+ * An example of this is null_getattrs in the null layer.
+ *
+ * A second approach is to directly invoked vnode operations on
+ * the lower layer with the VOP_OPERATIONNAME interface.
+ * The advantage of this method is that it is easy to invoke
+ * arbitrary operations on the lower layer. The disadvantage
+ * is that vnodes arguments must be manualy mapped.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <miscfs/nullfs/null.h>
+
+
+int null_bug_bypass = 0; /* for debugging: enables bypass printf'ing */
+
+/*
+ * This is the 10-Apr-92 bypass routine.
+ * This version has been optimized for speed, throwing away some
+ * safety checks. It should still always work, but it's not as
+ * robust to programmer errors.
+ * Define SAFETY to include some error checking code.
+ *
+ * In general, we map all vnodes going down and unmap them on the way back.
+ * As an exception to this, vnodes can be marked "unmapped" by setting
+ * the Nth bit in operation's vdesc_flags.
+ *
+ * Also, some BSD vnode operations have the side effect of vrele'ing
+ * their arguments. With stacking, the reference counts are held
+ * by the upper node, not the lower one, so we must handle these
+ * side-effects here. This is not of concern in Sun-derived systems
+ * since there are no such side-effects.
+ *
+ * This makes the following assumptions:
+ * - only one returned vpp
+ * - no INOUT vpp's (Sun's vop_open has one of these)
+ * - the vnode operation vector of the first vnode should be used
+ * to determine what implementation of the op should be invoked
+ * - all mapped vnodes are of our vnode-type (NEEDSWORK:
+ * problems on rmdir'ing mount points and renaming?)
+ */
+int
+null_bypass(ap)
+ struct vop_generic_args /* {
+ struct vnodeop_desc *a_desc;
+ <other random data follows, presumably>
+ } */ *ap;
+{
+ extern int (**null_vnodeop_p)(); /* not extern, really "forward" */
+ register struct vnode **this_vp_p;
+ int error;
+ struct vnode *old_vps[VDESC_MAX_VPS];
+ struct vnode **vps_p[VDESC_MAX_VPS];
+ struct vnode ***vppp;
+ struct vnodeop_desc *descp = ap->a_desc;
+ int reles, i;
+
+ if (null_bug_bypass)
+ printf ("null_bypass: %s\n", descp->vdesc_name);
+
+#ifdef SAFETY
+ /*
+ * We require at least one vp.
+ */
+ if (descp->vdesc_vp_offsets == NULL ||
+ descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET)
+ panic ("null_bypass: no vp's in map.\n");
+#endif
+
+ /*
+ * Map the vnodes going in.
+ * Later, we'll invoke the operation based on
+ * the first mapped vnode's operation vector.
+ */
+ reles = descp->vdesc_flags;
+ for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
+ if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
+ break; /* bail out at end of list */
+ vps_p[i] = this_vp_p =
+ VOPARG_OFFSETTO(struct vnode**,descp->vdesc_vp_offsets[i],ap);
+ /*
+ * We're not guaranteed that any but the first vnode
+ * are of our type. Check for and don't map any
+ * that aren't. (We must always map first vp or vclean fails.)
+ */
+ if (i && (*this_vp_p)->v_op != null_vnodeop_p) {
+ old_vps[i] = NULL;
+ } else {
+ old_vps[i] = *this_vp_p;
+ *(vps_p[i]) = NULLVPTOLOWERVP(*this_vp_p);
+ /*
+ * XXX - Several operations have the side effect
+ * of vrele'ing their vp's. We must account for
+ * that. (This should go away in the future.)
+ */
+ if (reles & 1)
+ VREF(*this_vp_p);
+ }
+
+ }
+
+ /*
+ * Call the operation on the lower layer
+ * with the modified argument structure.
+ */
+ error = VCALL(*(vps_p[0]), descp->vdesc_offset, ap);
+
+ /*
+ * Maintain the illusion of call-by-value
+ * by restoring vnodes in the argument structure
+ * to their original value.
+ */
+ reles = descp->vdesc_flags;
+ for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
+ if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
+ break; /* bail out at end of list */
+ if (old_vps[i]) {
+ *(vps_p[i]) = old_vps[i];
+ if (reles & 1)
+ vrele(*(vps_p[i]));
+ }
+ }
+
+ /*
+ * Map the possible out-going vpp
+ * (Assumes that the lower layer always returns
+ * a VREF'ed vpp unless it gets an error.)
+ */
+ if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET &&
+ !(descp->vdesc_flags & VDESC_NOMAP_VPP) &&
+ !error) {
+ /*
+ * XXX - even though some ops have vpp returned vp's,
+ * several ops actually vrele this before returning.
+ * We must avoid these ops.
+ * (This should go away when these ops are regularized.)
+ */
+ if (descp->vdesc_flags & VDESC_VPP_WILLRELE)
+ goto out;
+ vppp = VOPARG_OFFSETTO(struct vnode***,
+ descp->vdesc_vpp_offset,ap);
+ error = null_node_create(old_vps[0]->v_mount, **vppp, *vppp);
+ }
+
+ out:
+ return (error);
+}
+
+
+/*
+ * We handle getattr only to change the fsid.
+ */
+int
+null_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error;
+ if (error = null_bypass(ap))
+ return (error);
+ /* Requires that arguments be restored. */
+ ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
+ return (0);
+}
+
+
+int
+null_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ /*
+ * Do nothing (and _don't_ bypass).
+ * Wait to vrele lowervp until reclaim,
+ * so that until then our null_node is in the
+ * cache and reusable.
+ *
+ * NEEDSWORK: Someday, consider inactive'ing
+ * the lowervp and then trying to reactivate it
+ * with capabilities (v_id)
+ * like they do in the name lookup cache code.
+ * That's too much work for now.
+ */
+ return (0);
+}
+
+int
+null_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct null_node *xp = VTONULL(vp);
+ struct vnode *lowervp = xp->null_lowervp;
+
+ /*
+ * Note: in vop_reclaim, vp->v_op == dead_vnodeop_p,
+ * so we can't call VOPs on ourself.
+ */
+ /* After this assignment, this node will not be re-used. */
+ xp->null_lowervp = NULL;
+ remque(xp);
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = NULL;
+ vrele (lowervp);
+ return (0);
+}
+
+
+int
+null_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ printf ("\ttag VT_NULLFS, vp=%x, lowervp=%x\n", vp, NULLVPTOLOWERVP(vp));
+ return (0);
+}
+
+
+/*
+ * XXX - vop_strategy must be hand coded because it has no
+ * vnode in its arguments.
+ * This goes away with a merged VM/buffer cache.
+ */
+int
+null_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ int error;
+ struct vnode *savedvp;
+
+ savedvp = bp->b_vp;
+ bp->b_vp = NULLVPTOLOWERVP(bp->b_vp);
+
+ error = VOP_STRATEGY(bp);
+
+ bp->b_vp = savedvp;
+
+ return (error);
+}
+
+
+/*
+ * XXX - like vop_strategy, vop_bwrite must be hand coded because it has no
+ * vnode in its arguments.
+ * This goes away with a merged VM/buffer cache.
+ */
+int
+null_bwrite(ap)
+ struct vop_bwrite_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ int error;
+ struct vnode *savedvp;
+
+ savedvp = bp->b_vp;
+ bp->b_vp = NULLVPTOLOWERVP(bp->b_vp);
+
+ error = VOP_BWRITE(bp);
+
+ bp->b_vp = savedvp;
+
+ return (error);
+}
+
+/*
+ * Global vfs data structures
+ */
+int (**null_vnodeop_p)();
+struct vnodeopv_entry_desc null_vnodeop_entries[] = {
+ { &vop_default_desc, null_bypass },
+
+ { &vop_getattr_desc, null_getattr },
+ { &vop_inactive_desc, null_inactive },
+ { &vop_reclaim_desc, null_reclaim },
+ { &vop_print_desc, null_print },
+
+ { &vop_strategy_desc, null_strategy },
+ { &vop_bwrite_desc, null_bwrite },
+
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc null_vnodeop_opv_desc =
+ { &null_vnodeop_p, null_vnodeop_entries };
diff --git a/sys/fs/portalfs/portal.h b/sys/fs/portalfs/portal.h
new file mode 100644
index 0000000..38d7ee0
--- /dev/null
+++ b/sys/fs/portalfs/portal.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)portal.h 8.4 (Berkeley) 1/21/94
+ *
+ * $Id: portal.h,v 1.3 1992/05/30 10:05:24 jsp Exp jsp $
+ */
+
+struct portal_args {
+ char *pa_config; /* Config file */
+ int pa_socket; /* Socket to server */
+};
+
+struct portal_cred {
+ int pcr_flag; /* File open mode */
+ uid_t pcr_uid; /* From ucred */
+ short pcr_ngroups; /* From ucred */
+ gid_t pcr_groups[NGROUPS]; /* From ucred */
+};
+
+#ifdef KERNEL
+struct portalmount {
+ struct vnode *pm_root; /* Root node */
+ struct file *pm_server; /* Held reference to server socket */
+};
+
+struct portalnode {
+ int pt_size; /* Length of Arg */
+ char *pt_arg; /* Arg to send to server */
+ int pt_fileid; /* cookie */
+};
+
+#define VFSTOPORTAL(mp) ((struct portalmount *)((mp)->mnt_data))
+#define VTOPORTAL(vp) ((struct portalnode *)(vp)->v_data)
+
+#define PORTAL_ROOTFILEID 2
+
+extern int (**portal_vnodeop_p)();
+extern struct vfsops portal_vfsops;
+#endif /* KERNEL */
diff --git a/sys/fs/portalfs/portal_vfsops.c b/sys/fs/portalfs/portal_vfsops.c
new file mode 100644
index 0000000..39e8563
--- /dev/null
+++ b/sys/fs/portalfs/portal_vfsops.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)portal_vfsops.c 8.6 (Berkeley) 1/21/94
+ *
+ * $Id: portal_vfsops.c,v 1.5 1992/05/30 10:25:27 jsp Exp jsp $
+ */
+
+/*
+ * Portal Filesystem
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/filedesc.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/un.h>
+#include <miscfs/portal/portal.h>
+
+int
+portal_init()
+{
+
+ return (0);
+}
+
+/*
+ * Mount the per-process file descriptors (/dev/fd)
+ */
+int
+portal_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ struct file *fp;
+ struct portal_args args;
+ struct portalmount *fmp;
+ struct socket *so;
+ struct vnode *rvp;
+ u_int size;
+ int error;
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE)
+ return (EOPNOTSUPP);
+
+ if (error = copyin(data, (caddr_t) &args, sizeof(struct portal_args)))
+ return (error);
+
+ if (error = getsock(p->p_fd, args.pa_socket, &fp))
+ return (error);
+ so = (struct socket *) fp->f_data;
+ if (so->so_proto->pr_domain->dom_family != AF_UNIX)
+ return (ESOCKTNOSUPPORT);
+
+ error = getnewvnode(VT_PORTAL, mp, portal_vnodeop_p, &rvp); /* XXX */
+ if (error)
+ return (error);
+ MALLOC(rvp->v_data, void *, sizeof(struct portalnode),
+ M_TEMP, M_WAITOK);
+
+ fmp = (struct portalmount *) malloc(sizeof(struct portalmount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+ rvp->v_type = VDIR;
+ rvp->v_flag |= VROOT;
+ VTOPORTAL(rvp)->pt_arg = 0;
+ VTOPORTAL(rvp)->pt_size = 0;
+ VTOPORTAL(rvp)->pt_fileid = PORTAL_ROOTFILEID;
+ fmp->pm_root = rvp;
+ fmp->pm_server = fp; fp->f_count++;
+
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_data = (qaddr_t) fmp;
+ getnewfsid(mp, MOUNT_PORTAL);
+
+ (void)copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+ (void)copyinstr(args.pa_config,
+ mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+
+#ifdef notdef
+ bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
+ bcopy("portal", mp->mnt_stat.f_mntfromname, sizeof("portal"));
+#endif
+
+ return (0);
+}
+
+int
+portal_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+
+ return (0);
+}
+
+int
+portal_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ extern int doforce;
+ struct vnode *rootvp = VFSTOPORTAL(mp)->pm_root;
+ int error, flags = 0;
+
+
+ if (mntflags & MNT_FORCE) {
+ /* portal can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ /*
+ * Clear out buffer cache. I don't think we
+ * ever get anything cached at this level at the
+ * moment, but who knows...
+ */
+#ifdef notyet
+ mntflushbuf(mp, 0);
+ if (mntinvalbuf(mp, 1))
+ return (EBUSY);
+#endif
+ if (rootvp->v_usecount > 1)
+ return (EBUSY);
+ if (error = vflush(mp, rootvp, flags))
+ return (error);
+
+ /*
+ * Release reference on underlying root vnode
+ */
+ vrele(rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(rootvp);
+ /*
+ * Shutdown the socket. This will cause the select in the
+ * daemon to wake up, and then the accept will get ECONNABORTED
+ * which it interprets as a request to go and bury itself.
+ */
+ soshutdown((struct socket *) VFSTOPORTAL(mp)->pm_server->f_data, 2);
+ /*
+ * Discard reference to underlying file. Must call closef because
+ * this may be the last reference.
+ */
+ closef(VFSTOPORTAL(mp)->pm_server, (struct proc *) 0);
+ /*
+ * Finally, throw away the portalmount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ mp->mnt_data = 0;
+ return (0);
+}
+
+int
+portal_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct vnode *vp;
+
+
+ /*
+ * Return locked reference to root.
+ */
+ vp = VFSTOPORTAL(mp)->pm_root;
+ VREF(vp);
+ VOP_LOCK(vp);
+ *vpp = vp;
+ return (0);
+}
+
+int
+portal_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+portal_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+
+ sbp->f_type = MOUNT_PORTAL;
+ sbp->f_flags = 0;
+ sbp->f_bsize = DEV_BSIZE;
+ sbp->f_iosize = DEV_BSIZE;
+ sbp->f_blocks = 2; /* 1K to keep df happy */
+ sbp->f_bfree = 0;
+ sbp->f_bavail = 0;
+ sbp->f_files = 1; /* Allow for "." */
+ sbp->f_ffree = 0; /* See comments above */
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+ return (0);
+}
+
+int
+portal_sync(mp, waitfor)
+ struct mount *mp;
+ int waitfor;
+{
+
+ return (0);
+}
+
+int
+portal_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+portal_fhtovp(mp, fhp, vpp)
+ struct mount *mp;
+ struct fid *fhp;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+portal_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+struct vfsops portal_vfsops = {
+ portal_mount,
+ portal_start,
+ portal_unmount,
+ portal_root,
+ portal_quotactl,
+ portal_statfs,
+ portal_sync,
+ portal_vget,
+ portal_fhtovp,
+ portal_vptofh,
+ portal_init,
+};
diff --git a/sys/fs/portalfs/portal_vnops.c b/sys/fs/portalfs/portal_vnops.c
new file mode 100644
index 0000000..c19e815
--- /dev/null
+++ b/sys/fs/portalfs/portal_vnops.c
@@ -0,0 +1,708 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)portal_vnops.c 8.8 (Berkeley) 1/21/94
+ *
+ * $Id: portal_vnops.c,v 1.4 1992/05/30 10:05:24 jsp Exp jsp $
+ */
+
+/*
+ * Portal Filesystem
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/filedesc.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/malloc.h>
+#include <sys/namei.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/un.h>
+#include <sys/unpcb.h>
+#include <miscfs/portal/portal.h>
+
+static int portal_fileid = PORTAL_ROOTFILEID+1;
+
+static void
+portal_closefd(p, fd)
+ struct proc *p;
+ int fd;
+{
+ int error;
+ struct {
+ int fd;
+ } ua;
+ int rc;
+
+ ua.fd = fd;
+ error = close(p, &ua, &rc);
+ /*
+ * We should never get an error, and there isn't anything
+ * we could do if we got one, so just print a message.
+ */
+ if (error)
+ printf("portal_closefd: error = %d\n", error);
+}
+
+/*
+ * vp is the current namei directory
+ * cnp is the name to locate in that directory...
+ */
+int
+portal_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode * a_dvp;
+ struct vnode ** a_vpp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+ char *pname = ap->a_cnp->cn_nameptr;
+ struct portalnode *pt;
+ int error;
+ struct vnode *fvp = 0;
+ char *path;
+ int size;
+
+ if (ap->a_cnp->cn_namelen == 1 && *pname == '.') {
+ *ap->a_vpp = ap->a_dvp;
+ VREF(ap->a_dvp);
+ /*VOP_LOCK(ap->a_dvp);*/
+ return (0);
+ }
+
+
+ error = getnewvnode(VT_PORTAL, ap->a_dvp->v_mount, portal_vnodeop_p, &fvp);
+ if (error)
+ goto bad;
+ fvp->v_type = VREG;
+ MALLOC(fvp->v_data, void *, sizeof(struct portalnode),
+ M_TEMP, M_WAITOK);
+
+ pt = VTOPORTAL(fvp);
+ /*
+ * Save all of the remaining pathname and
+ * advance the namei next pointer to the end
+ * of the string.
+ */
+ for (size = 0, path = pname; *path; path++)
+ size++;
+ ap->a_cnp->cn_consume = size - ap->a_cnp->cn_namelen;
+
+ pt->pt_arg = malloc(size+1, M_TEMP, M_WAITOK);
+ pt->pt_size = size+1;
+ bcopy(pname, pt->pt_arg, pt->pt_size);
+ pt->pt_fileid = portal_fileid++;
+
+ *ap->a_vpp = fvp;
+ /*VOP_LOCK(fvp);*/
+ return (0);
+
+bad:;
+ if (fvp) {
+ vrele(fvp);
+ }
+ *ap->a_vpp = NULL;
+ return (error);
+}
+
+static int
+portal_connect(so, so2)
+ struct socket *so;
+ struct socket *so2;
+{
+ /* from unp_connect, bypassing the namei stuff... */
+ struct socket *so3;
+ struct unpcb *unp2;
+ struct unpcb *unp3;
+
+ if (so2 == 0)
+ return (ECONNREFUSED);
+
+ if (so->so_type != so2->so_type)
+ return (EPROTOTYPE);
+
+ if ((so2->so_options & SO_ACCEPTCONN) == 0)
+ return (ECONNREFUSED);
+
+ if ((so3 = sonewconn(so2, 0)) == 0)
+ return (ECONNREFUSED);
+
+ unp2 = sotounpcb(so2);
+ unp3 = sotounpcb(so3);
+ if (unp2->unp_addr)
+ unp3->unp_addr = m_copy(unp2->unp_addr, 0, (int)M_COPYALL);
+
+ so2 = so3;
+
+
+ return (unp_connect2(so, so2));
+}
+
+int
+portal_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct socket *so = 0;
+ struct portalnode *pt;
+ struct proc *p = ap->a_p;
+ struct vnode *vp = ap->a_vp;
+ int s;
+ struct uio auio;
+ struct iovec aiov[2];
+ int res;
+ struct mbuf *cm = 0;
+ struct cmsghdr *cmsg;
+ int newfds;
+ int *ip;
+ int fd;
+ int error;
+ int len;
+ struct portalmount *fmp;
+ struct file *fp;
+ struct portal_cred pcred;
+
+ /*
+ * Nothing to do when opening the root node.
+ */
+ if (vp->v_flag & VROOT)
+ return (0);
+
+ /*
+ * Can't be opened unless the caller is set up
+ * to deal with the side effects. Check for this
+ * by testing whether the p_dupfd has been set.
+ */
+ if (p->p_dupfd >= 0)
+ return (ENODEV);
+
+ pt = VTOPORTAL(vp);
+ fmp = VFSTOPORTAL(vp->v_mount);
+
+ /*
+ * Create a new socket.
+ */
+ error = socreate(AF_UNIX, &so, SOCK_STREAM, 0);
+ if (error)
+ goto bad;
+
+ /*
+ * Reserve some buffer space
+ */
+ res = pt->pt_size + sizeof(pcred) + 512; /* XXX */
+ error = soreserve(so, res, res);
+ if (error)
+ goto bad;
+
+ /*
+ * Kick off connection
+ */
+ error = portal_connect(so, (struct socket *)fmp->pm_server->f_data);
+ if (error)
+ goto bad;
+
+ /*
+ * Wait for connection to complete
+ */
+ /*
+ * XXX: Since the mount point is holding a reference on the
+ * underlying server socket, it is not easy to find out whether
+ * the server process is still running. To handle this problem
+ * we loop waiting for the new socket to be connected (something
+ * which will only happen if the server is still running) or for
+ * the reference count on the server socket to drop to 1, which
+ * will happen if the server dies. Sleep for 5 second intervals
+ * and keep polling the reference count. XXX.
+ */
+ s = splnet();
+ while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
+ if (fmp->pm_server->f_count == 1) {
+ error = ECONNREFUSED;
+ splx(s);
+ goto bad;
+ }
+ (void) tsleep((caddr_t) &so->so_timeo, PSOCK, "portalcon", 5 * hz);
+ }
+ splx(s);
+
+ if (so->so_error) {
+ error = so->so_error;
+ goto bad;
+ }
+
+ /*
+ * Set miscellaneous flags
+ */
+ so->so_rcv.sb_timeo = 0;
+ so->so_snd.sb_timeo = 0;
+ so->so_rcv.sb_flags |= SB_NOINTR;
+ so->so_snd.sb_flags |= SB_NOINTR;
+
+
+ pcred.pcr_flag = ap->a_mode;
+ pcred.pcr_uid = ap->a_cred->cr_uid;
+ pcred.pcr_ngroups = ap->a_cred->cr_ngroups;
+ bcopy(ap->a_cred->cr_groups, pcred.pcr_groups, NGROUPS * sizeof(gid_t));
+ aiov[0].iov_base = (caddr_t) &pcred;
+ aiov[0].iov_len = sizeof(pcred);
+ aiov[1].iov_base = pt->pt_arg;
+ aiov[1].iov_len = pt->pt_size;
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = 2;
+ auio.uio_rw = UIO_WRITE;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_procp = p;
+ auio.uio_offset = 0;
+ auio.uio_resid = aiov[0].iov_len + aiov[1].iov_len;
+
+ error = sosend(so, (struct mbuf *) 0, &auio,
+ (struct mbuf *) 0, (struct mbuf *) 0, 0);
+ if (error)
+ goto bad;
+
+ len = auio.uio_resid = sizeof(int);
+ do {
+ struct mbuf *m = 0;
+ int flags = MSG_WAITALL;
+ error = soreceive(so, (struct mbuf **) 0, &auio,
+ &m, &cm, &flags);
+ if (error)
+ goto bad;
+
+ /*
+ * Grab an error code from the mbuf.
+ */
+ if (m) {
+ m = m_pullup(m, sizeof(int)); /* Needed? */
+ if (m) {
+ error = *(mtod(m, int *));
+ m_freem(m);
+ } else {
+ error = EINVAL;
+ }
+ } else {
+ if (cm == 0) {
+ error = ECONNRESET; /* XXX */
+#ifdef notdef
+ break;
+#endif
+ }
+ }
+ } while (cm == 0 && auio.uio_resid == len && !error);
+
+ if (cm == 0)
+ goto bad;
+
+ if (auio.uio_resid) {
+ error = 0;
+#ifdef notdef
+ error = EMSGSIZE;
+ goto bad;
+#endif
+ }
+
+ /*
+ * XXX: Break apart the control message, and retrieve the
+ * received file descriptor. Note that more than one descriptor
+ * may have been received, or that the rights chain may have more
+ * than a single mbuf in it. What to do?
+ */
+ cmsg = mtod(cm, struct cmsghdr *);
+ newfds = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof (int);
+ if (newfds == 0) {
+ error = ECONNREFUSED;
+ goto bad;
+ }
+ /*
+ * At this point the rights message consists of a control message
+ * header, followed by a data region containing a vector of
+ * integer file descriptors. The fds were allocated by the action
+ * of receiving the control message.
+ */
+ ip = (int *) (cmsg + 1);
+ fd = *ip++;
+ if (newfds > 1) {
+ /*
+ * Close extra fds.
+ */
+ int i;
+ printf("portal_open: %d extra fds\n", newfds - 1);
+ for (i = 1; i < newfds; i++) {
+ portal_closefd(p, *ip);
+ ip++;
+ }
+ }
+
+ /*
+ * Check that the mode the file is being opened for is a subset
+ * of the mode of the existing descriptor.
+ */
+ fp = p->p_fd->fd_ofiles[fd];
+ if (((ap->a_mode & (FREAD|FWRITE)) | fp->f_flag) != fp->f_flag) {
+ portal_closefd(p, fd);
+ error = EACCES;
+ goto bad;
+ }
+
+ /*
+ * Save the dup fd in the proc structure then return the
+ * special error code (ENXIO) which causes magic things to
+ * happen in vn_open. The whole concept is, well, hmmm.
+ */
+ p->p_dupfd = fd;
+ error = ENXIO;
+
+bad:;
+ /*
+ * And discard the control message.
+ */
+ if (cm) {
+ m_freem(cm);
+ }
+
+ if (so) {
+ soshutdown(so, 2);
+ soclose(so);
+ }
+ return (error);
+}
+
+int
+portal_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct vattr *vap = ap->a_vap;
+
+ bzero(vap, sizeof(*vap));
+ vattr_null(vap);
+ vap->va_uid = 0;
+ vap->va_gid = 0;
+ vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+ vap->va_size = DEV_BSIZE;
+ vap->va_blocksize = DEV_BSIZE;
+ microtime(&vap->va_atime);
+ vap->va_mtime = vap->va_atime;
+ vap->va_ctime = vap->va_ctime;
+ vap->va_gen = 0;
+ vap->va_flags = 0;
+ vap->va_rdev = 0;
+ /* vap->va_qbytes = 0; */
+ vap->va_bytes = 0;
+ /* vap->va_qsize = 0; */
+ if (vp->v_flag & VROOT) {
+ vap->va_type = VDIR;
+ vap->va_mode = S_IRUSR|S_IWUSR|S_IXUSR|
+ S_IRGRP|S_IWGRP|S_IXGRP|
+ S_IROTH|S_IWOTH|S_IXOTH;
+ vap->va_nlink = 2;
+ vap->va_fileid = 2;
+ } else {
+ vap->va_type = VREG;
+ vap->va_mode = S_IRUSR|S_IWUSR|
+ S_IRGRP|S_IWGRP|
+ S_IROTH|S_IWOTH;
+ vap->va_nlink = 1;
+ vap->va_fileid = VTOPORTAL(vp)->pt_fileid;
+ }
+ return (0);
+}
+
+int
+portal_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ /*
+ * Can't mess with the root vnode
+ */
+ if (ap->a_vp->v_flag & VROOT)
+ return (EACCES);
+
+ return (0);
+}
+
+/*
+ * Fake readdir, just return empty directory.
+ * It is hard to deal with '.' and '..' so don't bother.
+ */
+int
+portal_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+int
+portal_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+int
+portal_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct portalnode *pt = VTOPORTAL(ap->a_vp);
+
+ if (pt->pt_arg) {
+ free((caddr_t) pt->pt_arg, M_TEMP);
+ pt->pt_arg = 0;
+ }
+ FREE(ap->a_vp->v_data, M_TEMP);
+ ap->a_vp->v_data = 0;
+
+ return (0);
+}
+
+/*
+ * Return POSIX pathconf information applicable to special devices.
+ */
+int
+portal_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_MAX_CANON:
+ *ap->a_retval = MAX_CANON;
+ return (0);
+ case _PC_MAX_INPUT:
+ *ap->a_retval = MAX_INPUT;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ case _PC_VDISABLE:
+ *ap->a_retval = _POSIX_VDISABLE;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Print out the contents of a Portal vnode.
+ */
+/* ARGSUSED */
+int
+portal_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_PORTAL, portal vnode\n");
+ return (0);
+}
+
+/*void*/
+int
+portal_vfree(ap)
+ struct vop_vfree_args /* {
+ struct vnode *a_pvp;
+ ino_t a_ino;
+ int a_mode;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+
+/*
+ * Portal vnode unsupported operation
+ */
+int
+portal_enotsupp()
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * Portal "should never get here" operation
+ */
+int
+portal_badop()
+{
+
+ panic("portal: bad op");
+ /* NOTREACHED */
+}
+
+/*
+ * Portal vnode null operation
+ */
+int
+portal_nullop()
+{
+
+ return (0);
+}
+
+#define portal_create ((int (*) __P((struct vop_create_args *)))portal_enotsupp)
+#define portal_mknod ((int (*) __P((struct vop_mknod_args *)))portal_enotsupp)
+#define portal_close ((int (*) __P((struct vop_close_args *)))nullop)
+#define portal_access ((int (*) __P((struct vop_access_args *)))nullop)
+#define portal_read ((int (*) __P((struct vop_read_args *)))portal_enotsupp)
+#define portal_write ((int (*) __P((struct vop_write_args *)))portal_enotsupp)
+#define portal_ioctl ((int (*) __P((struct vop_ioctl_args *)))portal_enotsupp)
+#define portal_select ((int (*) __P((struct vop_select_args *)))portal_enotsupp)
+#define portal_mmap ((int (*) __P((struct vop_mmap_args *)))portal_enotsupp)
+#define portal_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
+#define portal_seek ((int (*) __P((struct vop_seek_args *)))nullop)
+#define portal_remove ((int (*) __P((struct vop_remove_args *)))portal_enotsupp)
+#define portal_link ((int (*) __P((struct vop_link_args *)))portal_enotsupp)
+#define portal_rename ((int (*) __P((struct vop_rename_args *)))portal_enotsupp)
+#define portal_mkdir ((int (*) __P((struct vop_mkdir_args *)))portal_enotsupp)
+#define portal_rmdir ((int (*) __P((struct vop_rmdir_args *)))portal_enotsupp)
+#define portal_symlink \
+ ((int (*) __P((struct vop_symlink_args *)))portal_enotsupp)
+#define portal_readlink \
+ ((int (*) __P((struct vop_readlink_args *)))portal_enotsupp)
+#define portal_abortop ((int (*) __P((struct vop_abortop_args *)))nullop)
+#define portal_lock ((int (*) __P((struct vop_lock_args *)))nullop)
+#define portal_unlock ((int (*) __P((struct vop_unlock_args *)))nullop)
+#define portal_bmap ((int (*) __P((struct vop_bmap_args *)))portal_badop)
+#define portal_strategy \
+ ((int (*) __P((struct vop_strategy_args *)))portal_badop)
+#define portal_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+#define portal_advlock \
+ ((int (*) __P((struct vop_advlock_args *)))portal_enotsupp)
+#define portal_blkatoff \
+ ((int (*) __P((struct vop_blkatoff_args *)))portal_enotsupp)
+#define portal_valloc ((int(*) __P(( \
+ struct vnode *pvp, \
+ int mode, \
+ struct ucred *cred, \
+ struct vnode **vpp))) portal_enotsupp)
+#define portal_truncate \
+ ((int (*) __P((struct vop_truncate_args *)))portal_enotsupp)
+#define portal_update ((int (*) __P((struct vop_update_args *)))portal_enotsupp)
+#define portal_bwrite ((int (*) __P((struct vop_bwrite_args *)))portal_enotsupp)
+
+int (**portal_vnodeop_p)();
+struct vnodeopv_entry_desc portal_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, portal_lookup }, /* lookup */
+ { &vop_create_desc, portal_create }, /* create */
+ { &vop_mknod_desc, portal_mknod }, /* mknod */
+ { &vop_open_desc, portal_open }, /* open */
+ { &vop_close_desc, portal_close }, /* close */
+ { &vop_access_desc, portal_access }, /* access */
+ { &vop_getattr_desc, portal_getattr }, /* getattr */
+ { &vop_setattr_desc, portal_setattr }, /* setattr */
+ { &vop_read_desc, portal_read }, /* read */
+ { &vop_write_desc, portal_write }, /* write */
+ { &vop_ioctl_desc, portal_ioctl }, /* ioctl */
+ { &vop_select_desc, portal_select }, /* select */
+ { &vop_mmap_desc, portal_mmap }, /* mmap */
+ { &vop_fsync_desc, portal_fsync }, /* fsync */
+ { &vop_seek_desc, portal_seek }, /* seek */
+ { &vop_remove_desc, portal_remove }, /* remove */
+ { &vop_link_desc, portal_link }, /* link */
+ { &vop_rename_desc, portal_rename }, /* rename */
+ { &vop_mkdir_desc, portal_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, portal_rmdir }, /* rmdir */
+ { &vop_symlink_desc, portal_symlink }, /* symlink */
+ { &vop_readdir_desc, portal_readdir }, /* readdir */
+ { &vop_readlink_desc, portal_readlink }, /* readlink */
+ { &vop_abortop_desc, portal_abortop }, /* abortop */
+ { &vop_inactive_desc, portal_inactive }, /* inactive */
+ { &vop_reclaim_desc, portal_reclaim }, /* reclaim */
+ { &vop_lock_desc, portal_lock }, /* lock */
+ { &vop_unlock_desc, portal_unlock }, /* unlock */
+ { &vop_bmap_desc, portal_bmap }, /* bmap */
+ { &vop_strategy_desc, portal_strategy }, /* strategy */
+ { &vop_print_desc, portal_print }, /* print */
+ { &vop_islocked_desc, portal_islocked }, /* islocked */
+ { &vop_pathconf_desc, portal_pathconf }, /* pathconf */
+ { &vop_advlock_desc, portal_advlock }, /* advlock */
+ { &vop_blkatoff_desc, portal_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, portal_valloc }, /* valloc */
+ { &vop_vfree_desc, portal_vfree }, /* vfree */
+ { &vop_truncate_desc, portal_truncate }, /* truncate */
+ { &vop_update_desc, portal_update }, /* update */
+ { &vop_bwrite_desc, portal_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc portal_vnodeop_opv_desc =
+ { &portal_vnodeop_p, portal_vnodeop_entries };
diff --git a/sys/fs/procfs/README b/sys/fs/procfs/README
new file mode 100644
index 0000000..38811b3
--- /dev/null
+++ b/sys/fs/procfs/README
@@ -0,0 +1,113 @@
+saute procfs lyonnais
+
+procfs supports two levels of directory. the filesystem root
+directory contains a representation of the system process table.
+this consists of an entry for each active and zombie process, and
+an additional entry "curproc" which always represents the process
+making the lookup request.
+
+each of the sub-directories contains several files. these files
+are used to control and interrogate processes. the files implemented
+are:
+
+ file - xxx. the exec'ed file.
+
+ status - r/o. returns process status.
+
+ ctl - w/o. sends a control message to the process.
+ for example:
+ echo hup > /proc/curproc/note
+ will send a SIGHUP to the shell.
+ whereas
+ echo attach > /proc/1293/ctl
+ would set up process 1293 for debugging.
+ see below for more details.
+
+ mem - r/w. virtual memory image of the process.
+ parts of the address space are readable
+ only if they exist in the target process.
+ a more reasonable alternative might be
+ to return zero pages instead of an error.
+ comments?
+
+ note - w/o. writing a string here sends the
+ equivalent note to the process.
+ [ not implemented. ]
+
+ notepg - w/o. the same as note, but sends to all
+ members of the process group.
+ [ not implemented. ]
+
+ regs - r/w. process register set. this can be read
+ or written any time even if the process
+ is not stopped. since the bsd kernel
+ is single-processor, this implementation
+ will get the "right" register values.
+ a multi-proc kernel would need to do some
+ synchronisation.
+
+this then looks like:
+
+% ls -li /proc
+total 0
+ 9 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 0
+ 17 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 1
+ 89 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 10
+ 25 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 2
+2065 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 257
+2481 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 309
+ 265 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 32
+3129 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 390
+3209 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 400
+3217 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 401
+3273 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 408
+ 393 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 48
+ 409 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 50
+ 465 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 57
+ 481 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 59
+ 537 dr-xr-xr-x 2 root kmem 0 Sep 21 15:06 66
+ 545 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 67
+ 657 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 81
+ 665 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 82
+ 673 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 83
+ 681 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 84
+3273 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 curproc
+% ls -li /proc/curproc
+total 408
+3341 --w------- 1 jsp staff 0 Sep 21 15:06 ctl
+1554 -r-xr-xr-x 1 bin bin 90112 Mar 29 04:52 file
+3339 -rw------- 1 jsp staff 118784 Sep 21 15:06 mem
+3343 --w------- 1 jsp staff 0 Sep 21 15:06 note
+3344 --w------- 1 jsp staff 0 Sep 21 15:06 notepg
+3340 -rw------- 1 jsp staff 0 Sep 21 15:06 regs
+3342 -r--r--r-- 1 jsp staff 0 Sep 21 15:06 status
+% df /proc/curproc /proc/curproc/file
+Filesystem 512-blocks Used Avail Capacity Mounted on
+proc 2 2 0 100% /proc
+/dev/wd0a 16186 13548 1018 93% /
+% cat /proc/curproc/status
+cat 446 439 400 81 12,0 ctty 748620684 270000 0 0 0 20000 nochan 11 20 20 20 0 21 117
+
+
+
+the basic sequence of commands written to "ctl" would be
+
+ attach - this stops the target process and
+ arranges for the sending process
+ to become the debug control process
+ wait - wait for the target process to come to
+ a steady state ready for debugging.
+ step - single step, with no signal delivery.
+ run - continue running, with no signal delivery,
+ until next trap or breakpoint.
+ <signame> - deliver signal <signame> and continue running.
+ detach - continue execution of the target process
+ and remove it from control by the debug process
+
+in a normal debugging environment, where the target is fork/exec'd by
+the debugger, the debugger should fork and the child should stop itself
+(with a self-inflicted SIGSTOP). the parent should do a "wait" then an
+"attach". as before, the child will hit a breakpoint on the first
+instruction in any newly exec'd image.
+
+$Id: README,v 3.1 1993/12/15 09:40:17 jsp Exp $
diff --git a/sys/fs/procfs/procfs.h b/sys/fs/procfs/procfs.h
new file mode 100644
index 0000000..f7b8fa3
--- /dev/null
+++ b/sys/fs/procfs/procfs.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs.h 8.6 (Berkeley) 2/3/94
+ *
+ * From:
+ * $Id: procfs.h,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+/*
+ * The different types of node in a procfs filesystem
+ */
+typedef enum {
+ Proot, /* the filesystem root */
+ Pproc, /* a process-specific sub-directory */
+ Pfile, /* the executable file */
+ Pmem, /* the process's memory image */
+ Pregs, /* the process's register set */
+ Pfpregs, /* the process's FP register set */
+ Pctl, /* process control */
+ Pstatus, /* process status */
+ Pnote, /* process notifier */
+ Pnotepg /* process group notifier */
+} pfstype;
+
+/*
+ * control data for the proc file system.
+ */
+struct pfsnode {
+ struct pfsnode *pfs_next; /* next on list */
+ struct vnode *pfs_vnode; /* vnode associated with this pfsnode */
+ pfstype pfs_type; /* type of procfs node */
+ pid_t pfs_pid; /* associated process */
+ u_short pfs_mode; /* mode bits for stat() */
+ u_long pfs_flags; /* open flags */
+ u_long pfs_fileno; /* unique file id */
+};
+
+#define PROCFS_NOTELEN 64 /* max length of a note (/proc/$pid/note) */
+#define PROCFS_CTLLEN 8 /* max length of a ctl msg (/proc/$pid/ctl */
+
+/*
+ * Kernel stuff follows
+ */
+#ifdef KERNEL
+#define CNEQ(cnp, s, len) \
+ ((cnp)->cn_namelen == (len) && \
+ (bcmp((s), (cnp)->cn_nameptr, (len)) == 0))
+
+/*
+ * Format of a directory entry in /proc, ...
+ * This must map onto struct dirent (see <dirent.h>)
+ */
+#define PROCFS_NAMELEN 8
+struct pfsdent {
+ u_long d_fileno;
+ u_short d_reclen;
+ u_char d_type;
+ u_char d_namlen;
+ char d_name[PROCFS_NAMELEN];
+};
+#define UIO_MX sizeof(struct pfsdent)
+#define PROCFS_FILENO(pid, type) \
+ (((type) == Proot) ? \
+ 2 : \
+ ((((pid)+1) << 3) + ((int) (type))))
+
+/*
+ * Convert between pfsnode vnode
+ */
+#define VTOPFS(vp) ((struct pfsnode *)(vp)->v_data)
+#define PFSTOV(pfs) ((pfs)->pfs_vnode)
+
+typedef struct vfs_namemap vfs_namemap_t;
+struct vfs_namemap {
+ const char *nm_name;
+ int nm_val;
+};
+
+extern int vfs_getuserstr __P((struct uio *, char *, int *));
+extern vfs_namemap_t *vfs_findname __P((vfs_namemap_t *, char *, int));
+
+/* <machine/reg.h> */
+struct reg;
+struct fpreg;
+
+#define PFIND(pid) ((pid) ? pfind(pid) : &proc0)
+extern int procfs_freevp __P((struct vnode *));
+extern int procfs_allocvp __P((struct mount *, struct vnode **, long, pfstype));
+extern struct vnode *procfs_findtextvp __P((struct proc *));
+extern int procfs_sstep __P((struct proc *));
+extern void procfs_fix_sstep __P((struct proc *));
+extern int procfs_read_regs __P((struct proc *, struct reg *));
+extern int procfs_write_regs __P((struct proc *, struct reg *));
+extern int procfs_read_fpregs __P((struct proc *, struct fpreg *));
+extern int procfs_write_fpregs __P((struct proc *, struct fpreg *));
+extern int procfs_donote __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+extern int procfs_doregs __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+extern int procfs_dofpregs __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+extern int procfs_domem __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+extern int procfs_doctl __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+extern int procfs_dostatus __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+
+#define PROCFS_LOCKED 0x01
+#define PROCFS_WANT 0x02
+
+extern int (**procfs_vnodeop_p)();
+extern struct vfsops procfs_vfsops;
+
+/*
+ * Prototypes for procfs vnode ops
+ */
+int procfs_badop(); /* varargs */
+int procfs_rw __P((struct vop_read_args *));
+int procfs_lookup __P((struct vop_lookup_args *));
+#define procfs_create ((int (*) __P((struct vop_create_args *))) procfs_badop)
+#define procfs_mknod ((int (*) __P((struct vop_mknod_args *))) procfs_badop)
+int procfs_open __P((struct vop_open_args *));
+int procfs_close __P((struct vop_close_args *));
+int procfs_access __P((struct vop_access_args *));
+int procfs_getattr __P((struct vop_getattr_args *));
+int procfs_setattr __P((struct vop_setattr_args *));
+#define procfs_read procfs_rw
+#define procfs_write procfs_rw
+int procfs_ioctl __P((struct vop_ioctl_args *));
+#define procfs_select ((int (*) __P((struct vop_select_args *))) procfs_badop)
+#define procfs_mmap ((int (*) __P((struct vop_mmap_args *))) procfs_badop)
+#define procfs_fsync ((int (*) __P((struct vop_fsync_args *))) procfs_badop)
+#define procfs_seek ((int (*) __P((struct vop_seek_args *))) procfs_badop)
+#define procfs_remove ((int (*) __P((struct vop_remove_args *))) procfs_badop)
+#define procfs_link ((int (*) __P((struct vop_link_args *))) procfs_badop)
+#define procfs_rename ((int (*) __P((struct vop_rename_args *))) procfs_badop)
+#define procfs_mkdir ((int (*) __P((struct vop_mkdir_args *))) procfs_badop)
+#define procfs_rmdir ((int (*) __P((struct vop_rmdir_args *))) procfs_badop)
+#define procfs_symlink ((int (*) __P((struct vop_symlink_args *))) procfs_badop)
+int procfs_readdir __P((struct vop_readdir_args *));
+#define procfs_readlink ((int (*) __P((struct vop_readlink_args *))) procfs_badop)
+int procfs_abortop __P((struct vop_abortop_args *));
+int procfs_inactive __P((struct vop_inactive_args *));
+int procfs_reclaim __P((struct vop_reclaim_args *));
+#define procfs_lock ((int (*) __P((struct vop_lock_args *))) nullop)
+#define procfs_unlock ((int (*) __P((struct vop_unlock_args *))) nullop)
+int procfs_bmap __P((struct vop_bmap_args *));
+#define procfs_strategy ((int (*) __P((struct vop_strategy_args *))) procfs_badop)
+int procfs_print __P((struct vop_print_args *));
+#define procfs_islocked ((int (*) __P((struct vop_islocked_args *))) nullop)
+#define procfs_advlock ((int (*) __P((struct vop_advlock_args *))) procfs_badop)
+#define procfs_blkatoff ((int (*) __P((struct vop_blkatoff_args *))) procfs_badop)
+#define procfs_valloc ((int (*) __P((struct vop_valloc_args *))) procfs_badop)
+#define procfs_vfree ((int (*) __P((struct vop_vfree_args *))) nullop)
+#define procfs_truncate ((int (*) __P((struct vop_truncate_args *))) procfs_badop)
+#define procfs_update ((int (*) __P((struct vop_update_args *))) nullop)
+#endif /* KERNEL */
diff --git a/sys/fs/procfs/procfs_ctl.c b/sys/fs/procfs/procfs_ctl.c
new file mode 100644
index 0000000..a42a03c
--- /dev/null
+++ b/sys/fs/procfs/procfs_ctl.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_ctl.c 8.3 (Berkeley) 1/21/94
+ *
+ * From:
+ * $Id: procfs_ctl.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/resource.h>
+#include <sys/resourcevar.h>
+#include <miscfs/procfs/procfs.h>
+
+/*
+ * True iff process (p) is in trace wait state
+ * relative to process (curp)
+ */
+#define TRACE_WAIT_P(curp, p) \
+ ((p)->p_stat == SSTOP && \
+ (p)->p_pptr == (curp) && \
+ ((p)->p_flag & P_TRACED))
+
+#ifdef notdef
+#define FIX_SSTEP(p) { \
+ procfs_fix_sstep(p); \
+ } \
+}
+#else
+#define FIX_SSTEP(p)
+#endif
+
+#define PROCFS_CTL_ATTACH 1
+#define PROCFS_CTL_DETACH 2
+#define PROCFS_CTL_STEP 3
+#define PROCFS_CTL_RUN 4
+#define PROCFS_CTL_WAIT 5
+
+static vfs_namemap_t ctlnames[] = {
+ /* special /proc commands */
+ { "attach", PROCFS_CTL_ATTACH },
+ { "detach", PROCFS_CTL_DETACH },
+ { "step", PROCFS_CTL_STEP },
+ { "run", PROCFS_CTL_RUN },
+ { "wait", PROCFS_CTL_WAIT },
+ { 0 },
+};
+
+static vfs_namemap_t signames[] = {
+ /* regular signal names */
+ { "hup", SIGHUP }, { "int", SIGINT },
+ { "quit", SIGQUIT }, { "ill", SIGILL },
+ { "trap", SIGTRAP }, { "abrt", SIGABRT },
+ { "iot", SIGIOT }, { "emt", SIGEMT },
+ { "fpe", SIGFPE }, { "kill", SIGKILL },
+ { "bus", SIGBUS }, { "segv", SIGSEGV },
+ { "sys", SIGSYS }, { "pipe", SIGPIPE },
+ { "alrm", SIGALRM }, { "term", SIGTERM },
+ { "urg", SIGURG }, { "stop", SIGSTOP },
+ { "tstp", SIGTSTP }, { "cont", SIGCONT },
+ { "chld", SIGCHLD }, { "ttin", SIGTTIN },
+ { "ttou", SIGTTOU }, { "io", SIGIO },
+ { "xcpu", SIGXCPU }, { "xfsz", SIGXFSZ },
+ { "vtalrm", SIGVTALRM }, { "prof", SIGPROF },
+ { "winch", SIGWINCH }, { "info", SIGINFO },
+ { "usr1", SIGUSR1 }, { "usr2", SIGUSR2 },
+ { 0 },
+};
+
+static int
+procfs_control(curp, p, op)
+ struct proc *curp;
+ struct proc *p;
+ int op;
+{
+ int error;
+
+ /*
+ * Attach - attaches the target process for debugging
+ * by the calling process.
+ */
+ if (op == PROCFS_CTL_ATTACH) {
+ /* check whether already being traced */
+ if (p->p_flag & P_TRACED)
+ return (EBUSY);
+
+ /* can't trace yourself! */
+ if (p->p_pid == curp->p_pid)
+ return (EINVAL);
+
+ /*
+ * Go ahead and set the trace flag.
+ * Save the old parent (it's reset in
+ * _DETACH, and also in kern_exit.c:wait4()
+ * Reparent the process so that the tracing
+ * proc gets to see all the action.
+ * Stop the target.
+ */
+ p->p_flag |= P_TRACED;
+ p->p_xstat = 0; /* XXX ? */
+ if (p->p_pptr != curp) {
+ p->p_oppid = p->p_pptr->p_pid;
+ proc_reparent(p, curp);
+ }
+ psignal(p, SIGSTOP);
+ return (0);
+ }
+
+ /*
+ * Target process must be stopped, owned by (curp) and
+ * be set up for tracing (P_TRACED flag set).
+ * Allow DETACH to take place at any time for sanity.
+ * Allow WAIT any time, of course.
+ */
+ switch (op) {
+ case PROCFS_CTL_DETACH:
+ case PROCFS_CTL_WAIT:
+ break;
+
+ default:
+ if (!TRACE_WAIT_P(curp, p))
+ return (EBUSY);
+ }
+
+ /*
+ * do single-step fixup if needed
+ */
+ FIX_SSTEP(p);
+
+ /*
+ * Don't deliver any signal by default.
+ * To continue with a signal, just send
+ * the signal name to the ctl file
+ */
+ p->p_xstat = 0;
+
+ switch (op) {
+ /*
+ * Detach. Cleans up the target process, reparent it if possible
+ * and set it running once more.
+ */
+ case PROCFS_CTL_DETACH:
+ /* if not being traced, then this is a painless no-op */
+ if ((p->p_flag & P_TRACED) == 0)
+ return (0);
+
+ /* not being traced any more */
+ p->p_flag &= ~P_TRACED;
+
+ /* give process back to original parent */
+ if (p->p_oppid != p->p_pptr->p_pid) {
+ struct proc *pp;
+
+ pp = pfind(p->p_oppid);
+ if (pp)
+ proc_reparent(p, pp);
+ }
+
+ p->p_oppid = 0;
+ p->p_flag &= ~P_WAITED; /* XXX ? */
+ wakeup((caddr_t) curp); /* XXX for CTL_WAIT below ? */
+
+ break;
+
+ /*
+ * Step. Let the target process execute a single instruction.
+ */
+ case PROCFS_CTL_STEP:
+ procfs_sstep(p);
+ break;
+
+ /*
+ * Run. Let the target process continue running until a breakpoint
+ * or some other trap.
+ */
+ case PROCFS_CTL_RUN:
+ break;
+
+ /*
+ * Wait for the target process to stop.
+ * If the target is not being traced then just wait
+ * to enter
+ */
+ case PROCFS_CTL_WAIT:
+ error = 0;
+ if (p->p_flag & P_TRACED) {
+ while (error == 0 &&
+ (p->p_stat != SSTOP) &&
+ (p->p_flag & P_TRACED) &&
+ (p->p_pptr == curp)) {
+ error = tsleep((caddr_t) p,
+ PWAIT|PCATCH, "procfsx", 0);
+ }
+ if (error == 0 && !TRACE_WAIT_P(curp, p))
+ error = EBUSY;
+ } else {
+ while (error == 0 && p->p_stat != SSTOP) {
+ error = tsleep((caddr_t) p,
+ PWAIT|PCATCH, "procfs", 0);
+ }
+ }
+ return (error);
+
+ default:
+ panic("procfs_control");
+ }
+
+ if (p->p_stat == SSTOP)
+ setrunnable(p);
+ return (0);
+}
+
+int
+procfs_doctl(curp, p, pfs, uio)
+ struct proc *curp;
+ struct pfsnode *pfs;
+ struct uio *uio;
+ struct proc *p;
+{
+ int xlen;
+ int error;
+ char msg[PROCFS_CTLLEN+1];
+ vfs_namemap_t *nm;
+
+ if (uio->uio_rw != UIO_WRITE)
+ return (EOPNOTSUPP);
+
+ xlen = PROCFS_CTLLEN;
+ error = vfs_getuserstr(uio, msg, &xlen);
+ if (error)
+ return (error);
+
+ /*
+ * Map signal names into signal generation
+ * or debug control. Unknown commands and/or signals
+ * return EOPNOTSUPP.
+ *
+ * Sending a signal while the process is being debugged
+ * also has the side effect of letting the target continue
+ * to run. There is no way to single-step a signal delivery.
+ */
+ error = EOPNOTSUPP;
+
+ nm = vfs_findname(ctlnames, msg, xlen);
+ if (nm) {
+ error = procfs_control(curp, p, nm->nm_val);
+ } else {
+ nm = vfs_findname(signames, msg, xlen);
+ if (nm) {
+ if (TRACE_WAIT_P(curp, p)) {
+ p->p_xstat = nm->nm_val;
+ FIX_SSTEP(p);
+ setrunnable(p);
+ } else {
+ psignal(p, nm->nm_val);
+ }
+ error = 0;
+ }
+ }
+
+ return (error);
+}
diff --git a/sys/fs/procfs/procfs_fpregs.c b/sys/fs/procfs/procfs_fpregs.c
new file mode 100644
index 0000000..6d850a6
--- /dev/null
+++ b/sys/fs/procfs/procfs_fpregs.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_fpregs.c 8.1 (Berkeley) 1/27/94
+ *
+ * From:
+ * $Id: procfs_regs.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <machine/reg.h>
+#include <miscfs/procfs/procfs.h>
+
+int
+procfs_dofpregs(curp, p, pfs, uio)
+ struct proc *curp;
+ struct proc *p;
+ struct pfsnode *pfs;
+ struct uio *uio;
+{
+ int error;
+ struct fpreg r;
+ char *kv;
+ int kl;
+
+ kl = sizeof(r);
+ kv = (char *) &r;
+
+ kv += uio->uio_offset;
+ kl -= uio->uio_offset;
+ if (kl > uio->uio_resid)
+ kl = uio->uio_resid;
+
+ if (kl < 0)
+ error = EINVAL;
+ else
+ error = procfs_read_fpregs(p, &r);
+ if (error == 0)
+ error = uiomove(kv, kl, uio);
+ if (error == 0 && uio->uio_rw == UIO_WRITE) {
+ if (p->p_stat != SSTOP)
+ error = EBUSY;
+ else
+ error = procfs_write_fpregs(p, &r);
+ }
+
+ uio->uio_offset = 0;
+ return (error);
+}
diff --git a/sys/fs/procfs/procfs_mem.c b/sys/fs/procfs/procfs_mem.c
new file mode 100644
index 0000000..039983d
--- /dev/null
+++ b/sys/fs/procfs/procfs_mem.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993 Sean Eric Fagan
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry and Sean Eric Fagan.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_mem.c 8.4 (Berkeley) 1/21/94
+ *
+ * From:
+ * $Id: procfs_mem.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+/*
+ * This is a lightly hacked and merged version
+ * of sef's pread/pwrite functions
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <miscfs/procfs/procfs.h>
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+
+static int
+procfs_rwmem(p, uio)
+ struct proc *p;
+ struct uio *uio;
+{
+ int error;
+ int writing;
+
+ writing = uio->uio_rw == UIO_WRITE;
+
+ /*
+ * Only map in one page at a time. We don't have to, but it
+ * makes things easier. This way is trivial - right?
+ */
+ do {
+ vm_map_t map, tmap;
+ vm_object_t object;
+ vm_offset_t kva;
+ vm_offset_t uva;
+ int page_offset; /* offset into page */
+ vm_offset_t pageno; /* page number */
+ vm_map_entry_t out_entry;
+ vm_prot_t out_prot;
+ vm_page_t m;
+ boolean_t wired, single_use;
+ vm_offset_t off;
+ u_int len;
+ int fix_prot;
+
+ uva = (vm_offset_t) uio->uio_offset;
+ if (uva > VM_MAXUSER_ADDRESS) {
+ error = 0;
+ break;
+ }
+
+ /*
+ * Get the page number of this segment.
+ */
+ pageno = trunc_page(uva);
+ page_offset = uva - pageno;
+
+ /*
+ * How many bytes to copy
+ */
+ len = min(PAGE_SIZE - page_offset, uio->uio_resid);
+
+ /*
+ * The map we want...
+ */
+ map = &p->p_vmspace->vm_map;
+
+ /*
+ * Check the permissions for the area we're interested
+ * in.
+ */
+ fix_prot = 0;
+ if (writing)
+ fix_prot = !vm_map_check_protection(map, pageno,
+ pageno + PAGE_SIZE, VM_PROT_WRITE);
+
+ if (fix_prot) {
+ /*
+ * If the page is not writable, we make it so.
+ * XXX It is possible that a page may *not* be
+ * read/executable, if a process changes that!
+ * We will assume, for now, that a page is either
+ * VM_PROT_ALL, or VM_PROT_READ|VM_PROT_EXECUTE.
+ */
+ error = vm_map_protect(map, pageno,
+ pageno + PAGE_SIZE, VM_PROT_ALL, 0);
+ if (error)
+ break;
+ }
+
+ /*
+ * Now we need to get the page. out_entry, out_prot, wired,
+ * and single_use aren't used. One would think the vm code
+ * would be a *bit* nicer... We use tmap because
+ * vm_map_lookup() can change the map argument.
+ */
+ tmap = map;
+ error = vm_map_lookup(&tmap, pageno,
+ writing ? VM_PROT_WRITE : VM_PROT_READ,
+ &out_entry, &object, &off, &out_prot,
+ &wired, &single_use);
+ /*
+ * We're done with tmap now.
+ */
+ if (!error)
+ vm_map_lookup_done(tmap, out_entry);
+
+ /*
+ * Fault the page in...
+ */
+ if (!error && writing && object->shadow) {
+ m = vm_page_lookup(object, off);
+ if (m == 0 || (m->flags & PG_COPYONWRITE))
+ error = vm_fault(map, pageno,
+ VM_PROT_WRITE, FALSE);
+ }
+
+ /* Find space in kernel_map for the page we're interested in */
+ if (!error)
+ error = vm_map_find(kernel_map, object, off, &kva,
+ PAGE_SIZE, 1);
+
+ if (!error) {
+ /*
+ * Neither vm_map_lookup() nor vm_map_find() appear
+ * to add a reference count to the object, so we do
+ * that here and now.
+ */
+ vm_object_reference(object);
+
+ /*
+ * Mark the page we just found as pageable.
+ */
+ error = vm_map_pageable(kernel_map, kva,
+ kva + PAGE_SIZE, 0);
+
+ /*
+ * Now do the i/o move.
+ */
+ if (!error)
+ error = uiomove(kva + page_offset, len, uio);
+
+ vm_map_remove(kernel_map, kva, kva + PAGE_SIZE);
+ }
+ if (fix_prot)
+ vm_map_protect(map, pageno, pageno + PAGE_SIZE,
+ VM_PROT_READ|VM_PROT_EXECUTE, 0);
+ } while (error == 0 && uio->uio_resid > 0);
+
+ return (error);
+}
+
+/*
+ * Copy data in and out of the target process.
+ * We do this by mapping the process's page into
+ * the kernel and then doing a uiomove direct
+ * from the kernel address space.
+ */
+int
+procfs_domem(curp, p, pfs, uio)
+ struct proc *curp;
+ struct proc *p;
+ struct pfsnode *pfs;
+ struct uio *uio;
+{
+ int error;
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ error = procfs_rwmem(p, uio);
+
+ return (error);
+}
+
+/*
+ * Given process (p), find the vnode from which
+ * it's text segment is being executed.
+ *
+ * It would be nice to grab this information from
+ * the VM system, however, there is no sure-fire
+ * way of doing that. Instead, fork(), exec() and
+ * wait() all maintain the p_textvp field in the
+ * process proc structure which contains a held
+ * reference to the exec'ed vnode.
+ */
+struct vnode *
+procfs_findtextvp(p)
+ struct proc *p;
+{
+ return (p->p_textvp);
+}
+
+
+#ifdef probably_never
+/*
+ * Given process (p), find the vnode from which
+ * it's text segment is being mapped.
+ *
+ * (This is here, rather than in procfs_subr in order
+ * to keep all the VM related code in one place.)
+ */
+struct vnode *
+procfs_findtextvp(p)
+ struct proc *p;
+{
+ int error;
+ vm_object_t object;
+ vm_offset_t pageno; /* page number */
+
+ /* find a vnode pager for the user address space */
+
+ for (pageno = VM_MIN_ADDRESS;
+ pageno < VM_MAXUSER_ADDRESS;
+ pageno += PAGE_SIZE) {
+ vm_map_t map;
+ vm_map_entry_t out_entry;
+ vm_prot_t out_prot;
+ boolean_t wired, single_use;
+ vm_offset_t off;
+
+ map = &p->p_vmspace->vm_map;
+ error = vm_map_lookup(&map, pageno,
+ VM_PROT_READ,
+ &out_entry, &object, &off, &out_prot,
+ &wired, &single_use);
+
+ if (!error) {
+ vm_pager_t pager;
+
+ printf("procfs: found vm object\n");
+ vm_map_lookup_done(map, out_entry);
+ printf("procfs: vm object = %x\n", object);
+
+ /*
+ * At this point, assuming no errors, object
+ * is the VM object mapping UVA (pageno).
+ * Ensure it has a vnode pager, then grab
+ * the vnode from that pager's handle.
+ */
+
+ pager = object->pager;
+ printf("procfs: pager = %x\n", pager);
+ if (pager)
+ printf("procfs: found pager, type = %d\n", pager->pg_type);
+ if (pager && pager->pg_type == PG_VNODE) {
+ struct vnode *vp;
+
+ vp = (struct vnode *) pager->pg_handle;
+ printf("procfs: vp = 0x%x\n", vp);
+ return (vp);
+ }
+ }
+ }
+
+ printf("procfs: text object not found\n");
+ return (0);
+}
+#endif /* probably_never */
diff --git a/sys/fs/procfs/procfs_note.c b/sys/fs/procfs/procfs_note.c
new file mode 100644
index 0000000..bf2f160
--- /dev/null
+++ b/sys/fs/procfs/procfs_note.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_note.c 8.2 (Berkeley) 1/21/94
+ *
+ * From:
+ * $Id: procfs_note.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/signal.h>
+#include <miscfs/procfs/procfs.h>
+
+int
+procfs_donote(curp, p, pfs, uio)
+ struct proc *curp;
+ struct proc *p;
+ struct pfsnode *pfs;
+ struct uio *uio;
+{
+ int xlen;
+ int error;
+ char note[PROCFS_NOTELEN+1];
+
+ if (uio->uio_rw != UIO_WRITE)
+ return (EINVAL);
+
+ xlen = PROCFS_NOTELEN;
+ error = vfs_getuserstr(uio, note, &xlen);
+ if (error)
+ return (error);
+
+ /* send to process's notify function */
+ return (EOPNOTSUPP);
+}
diff --git a/sys/fs/procfs/procfs_regs.c b/sys/fs/procfs/procfs_regs.c
new file mode 100644
index 0000000..fa95fef
--- /dev/null
+++ b/sys/fs/procfs/procfs_regs.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_regs.c 8.3 (Berkeley) 1/27/94
+ *
+ * From:
+ * $Id: procfs_regs.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <machine/reg.h>
+#include <miscfs/procfs/procfs.h>
+
+int
+procfs_doregs(curp, p, pfs, uio)
+ struct proc *curp;
+ struct proc *p;
+ struct pfsnode *pfs;
+ struct uio *uio;
+{
+ int error;
+ struct reg r;
+ char *kv;
+ int kl;
+
+ kl = sizeof(r);
+ kv = (char *) &r;
+
+ kv += uio->uio_offset;
+ kl -= uio->uio_offset;
+ if (kl > uio->uio_resid)
+ kl = uio->uio_resid;
+
+ if (kl < 0)
+ error = EINVAL;
+ else
+ error = procfs_read_regs(p, &r);
+ if (error == 0)
+ error = uiomove(kv, kl, uio);
+ if (error == 0 && uio->uio_rw == UIO_WRITE) {
+ if (p->p_stat != SSTOP)
+ error = EBUSY;
+ else
+ error = procfs_write_regs(p, &r);
+ }
+
+ uio->uio_offset = 0;
+ return (error);
+}
diff --git a/sys/fs/procfs/procfs_status.c b/sys/fs/procfs/procfs_status.c
new file mode 100644
index 0000000..d88aaab
--- /dev/null
+++ b/sys/fs/procfs/procfs_status.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_status.c 8.3 (Berkeley) 2/17/94
+ *
+ * From:
+ * $Id: procfs_status.c,v 3.1 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/resource.h>
+#include <sys/resourcevar.h>
+#include <miscfs/procfs/procfs.h>
+
+int
+procfs_dostatus(curp, p, pfs, uio)
+ struct proc *curp;
+ struct proc *p;
+ struct pfsnode *pfs;
+ struct uio *uio;
+{
+ struct session *sess;
+ struct tty *tp;
+ struct ucred *cr;
+ char *ps;
+ char *sep;
+ int pid, ppid, pgid, sid;
+ int i;
+ int xlen;
+ int error;
+ char psbuf[256]; /* XXX - conservative */
+
+ if (uio->uio_rw != UIO_READ)
+ return (EOPNOTSUPP);
+
+ pid = p->p_pid;
+ ppid = p->p_pptr ? p->p_pptr->p_pid : 0,
+ pgid = p->p_pgrp->pg_id;
+ sess = p->p_pgrp->pg_session;
+ sid = sess->s_leader ? sess->s_leader->p_pid : 0;
+
+/* comm pid ppid pgid sid maj,min ctty,sldr start ut st wmsg uid groups ... */
+
+ ps = psbuf;
+ bcopy(p->p_comm, ps, MAXCOMLEN);
+ ps[MAXCOMLEN] = '\0';
+ ps += strlen(ps);
+ ps += sprintf(ps, " %d %d %d %d ", pid, ppid, pgid, sid);
+
+ if ((p->p_flag&P_CONTROLT) && (tp = sess->s_ttyp))
+ ps += sprintf(ps, "%d,%d ", major(tp->t_dev), minor(tp->t_dev));
+ else
+ ps += sprintf(ps, "%d,%d ", -1, -1);
+
+ sep = "";
+ if (sess->s_ttyvp) {
+ ps += sprintf(ps, "%sctty", sep);
+ sep = ",";
+ }
+ if (SESS_LEADER(p)) {
+ ps += sprintf(ps, "%ssldr", sep);
+ sep = ",";
+ }
+ if (*sep != ',')
+ ps += sprintf(ps, "noflags");
+
+ if (p->p_flag & P_INMEM)
+ ps += sprintf(ps, " %d,%d",
+ p->p_stats->p_start.tv_sec,
+ p->p_stats->p_start.tv_usec);
+ else
+ ps += sprintf(ps, " -1,-1");
+
+ {
+ struct timeval ut, st;
+
+ calcru(p, &ut, &st, (void *) 0);
+ ps += sprintf(ps, " %d,%d %d,%d",
+ ut.tv_sec,
+ ut.tv_usec,
+ st.tv_sec,
+ st.tv_usec);
+ }
+
+ ps += sprintf(ps, " %s",
+ (p->p_wchan && p->p_wmesg) ? p->p_wmesg : "nochan");
+
+ cr = p->p_ucred;
+
+ ps += sprintf(ps, " %d", cr->cr_uid, cr->cr_gid);
+ for (i = 0; i < cr->cr_ngroups; i++)
+ ps += sprintf(ps, ",%d", cr->cr_groups[i]);
+ ps += sprintf(ps, "\n");
+
+ xlen = ps - psbuf;
+ xlen -= uio->uio_offset;
+ ps = psbuf + uio->uio_offset;
+ xlen = min(xlen, uio->uio_resid);
+ if (xlen <= 0)
+ error = 0;
+ else
+ error = uiomove(ps, xlen, uio);
+
+ return (error);
+}
diff --git a/sys/fs/procfs/procfs_subr.c b/sys/fs/procfs/procfs_subr.c
new file mode 100644
index 0000000..b371af1
--- /dev/null
+++ b/sys/fs/procfs/procfs_subr.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_subr.c 8.4 (Berkeley) 1/27/94
+ *
+ * From:
+ * $Id: procfs_subr.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <miscfs/procfs/procfs.h>
+
+static struct pfsnode *pfshead;
+static int pfsvplock;
+
+/*
+ * allocate a pfsnode/vnode pair. the vnode is
+ * referenced, but not locked.
+ *
+ * the pid, pfs_type, and mount point uniquely
+ * identify a pfsnode. the mount point is needed
+ * because someone might mount this filesystem
+ * twice.
+ *
+ * all pfsnodes are maintained on a singly-linked
+ * list. new nodes are only allocated when they cannot
+ * be found on this list. entries on the list are
+ * removed when the vfs reclaim entry is called.
+ *
+ * a single lock is kept for the entire list. this is
+ * needed because the getnewvnode() function can block
+ * waiting for a vnode to become free, in which case there
+ * may be more than one process trying to get the same
+ * vnode. this lock is only taken if we are going to
+ * call getnewvnode, since the kernel itself is single-threaded.
+ *
+ * if an entry is found on the list, then call vget() to
+ * take a reference. this is done because there may be
+ * zero references to it and so it needs to removed from
+ * the vnode free list.
+ */
+int
+procfs_allocvp(mp, vpp, pid, pfs_type)
+ struct mount *mp;
+ struct vnode **vpp;
+ long pid;
+ pfstype pfs_type;
+{
+ int error;
+ struct pfsnode *pfs;
+ struct pfsnode **pp;
+
+loop:
+ for (pfs = pfshead; pfs != 0; pfs = pfs->pfs_next) {
+ if (pfs->pfs_pid == pid &&
+ pfs->pfs_type == pfs_type &&
+ PFSTOV(pfs)->v_mount == mp) {
+ if (vget(pfs->pfs_vnode, 0))
+ goto loop;
+ *vpp = pfs->pfs_vnode;
+ return (0);
+ }
+ }
+
+ /*
+ * otherwise lock the vp list while we call getnewvnode
+ * since that can block.
+ */
+ if (pfsvplock & PROCFS_LOCKED) {
+ pfsvplock |= PROCFS_WANT;
+ sleep((caddr_t) &pfsvplock, PINOD);
+ goto loop;
+ }
+ pfsvplock |= PROCFS_LOCKED;
+
+ error = getnewvnode(VT_PROCFS, mp, procfs_vnodeop_p, vpp);
+ if (error)
+ goto out;
+
+ MALLOC((*vpp)->v_data, void *, sizeof(struct pfsnode),
+ M_TEMP, M_WAITOK);
+
+ pfs = VTOPFS(*vpp);
+ pfs->pfs_next = 0;
+ pfs->pfs_pid = (pid_t) pid;
+ pfs->pfs_type = pfs_type;
+ pfs->pfs_vnode = *vpp;
+ pfs->pfs_flags = 0;
+ pfs->pfs_fileno = PROCFS_FILENO(pid, pfs_type);
+
+ switch (pfs_type) {
+ case Proot: /* /proc = dr-xr-xr-x */
+ pfs->pfs_mode = (VREAD|VEXEC) |
+ (VREAD|VEXEC) >> 3 |
+ (VREAD|VEXEC) >> 6;
+ break;
+
+ case Pproc:
+ pfs->pfs_mode = (VREAD|VEXEC) |
+ (VREAD|VEXEC) >> 3 |
+ (VREAD|VEXEC) >> 6;
+ break;
+
+ case Pfile:
+ pfs->pfs_mode = (VREAD|VWRITE);
+ break;
+
+ case Pmem:
+ pfs->pfs_mode = (VREAD|VWRITE);
+ break;
+
+ case Pregs:
+ pfs->pfs_mode = (VREAD|VWRITE);
+ break;
+
+ case Pfpregs:
+ pfs->pfs_mode = (VREAD|VWRITE);
+ break;
+
+ case Pctl:
+ pfs->pfs_mode = (VWRITE);
+ break;
+
+ case Pstatus:
+ pfs->pfs_mode = (VREAD) |
+ (VREAD >> 3) |
+ (VREAD >> 6);
+ break;
+
+ case Pnote:
+ pfs->pfs_mode = (VWRITE);
+ break;
+
+ case Pnotepg:
+ pfs->pfs_mode = (VWRITE);
+ break;
+
+ default:
+ panic("procfs_allocvp");
+ }
+
+ /* add to procfs vnode list */
+ for (pp = &pfshead; *pp; pp = &(*pp)->pfs_next)
+ continue;
+ *pp = pfs;
+
+out:
+ pfsvplock &= ~PROCFS_LOCKED;
+
+ if (pfsvplock & PROCFS_WANT) {
+ pfsvplock &= ~PROCFS_WANT;
+ wakeup((caddr_t) &pfsvplock);
+ }
+
+ return (error);
+}
+
+int
+procfs_freevp(vp)
+ struct vnode *vp;
+{
+ struct pfsnode **pfspp;
+ struct pfsnode *pfs = VTOPFS(vp);
+
+ for (pfspp = &pfshead; *pfspp != 0; pfspp = &(*pfspp)->pfs_next) {
+ if (*pfspp == pfs) {
+ *pfspp = pfs->pfs_next;
+ break;
+ }
+ }
+
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = 0;
+ return (0);
+}
+
+int
+procfs_rw(ap)
+ struct vop_read_args *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct uio *uio = ap->a_uio;
+ struct proc *curp = uio->uio_procp;
+ struct pfsnode *pfs = VTOPFS(vp);
+ struct proc *p;
+
+ p = PFIND(pfs->pfs_pid);
+ if (p == 0)
+ return (EINVAL);
+
+ switch (pfs->pfs_type) {
+ case Pnote:
+ case Pnotepg:
+ return (procfs_donote(curp, p, pfs, uio));
+
+ case Pregs:
+ return (procfs_doregs(curp, p, pfs, uio));
+
+ case Pfpregs:
+ return (procfs_dofpregs(curp, p, pfs, uio));
+
+ case Pctl:
+ return (procfs_doctl(curp, p, pfs, uio));
+
+ case Pstatus:
+ return (procfs_dostatus(curp, p, pfs, uio));
+
+ case Pmem:
+ return (procfs_domem(curp, p, pfs, uio));
+
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
+/*
+ * Get a string from userland into (buf). Strip a trailing
+ * nl character (to allow easy access from the shell).
+ * The buffer should be *buflenp + 1 chars long. vfs_getuserstr
+ * will automatically add a nul char at the end.
+ *
+ * Returns 0 on success or the following errors
+ *
+ * EINVAL: file offset is non-zero.
+ * EMSGSIZE: message is longer than kernel buffer
+ * EFAULT: user i/o buffer is not addressable
+ */
+int
+vfs_getuserstr(uio, buf, buflenp)
+ struct uio *uio;
+ char *buf;
+ int *buflenp;
+{
+ int xlen;
+ int error;
+
+ if (uio->uio_offset != 0)
+ return (EINVAL);
+
+ xlen = *buflenp;
+
+ /* must be able to read the whole string in one go */
+ if (xlen < uio->uio_resid)
+ return (EMSGSIZE);
+ xlen = uio->uio_resid;
+
+ error = uiomove(buf, xlen, uio);
+ if (error)
+ return (error);
+
+ /* allow multiple writes without seeks */
+ uio->uio_offset = 0;
+
+ /* cleanup string and remove trailing newline */
+ buf[xlen] = '\0';
+ xlen = strlen(buf);
+ if (xlen > 0 && buf[xlen-1] == '\n')
+ buf[--xlen] = '\0';
+ *buflenp = xlen;
+
+ return (0);
+}
+
+vfs_namemap_t *
+vfs_findname(nm, buf, buflen)
+ vfs_namemap_t *nm;
+ char *buf;
+ int buflen;
+{
+ for (; nm->nm_name; nm++)
+ if (bcmp(buf, (char *) nm->nm_name, buflen+1) == 0)
+ return (nm);
+
+ return (0);
+}
diff --git a/sys/fs/procfs/procfs_vfsops.c b/sys/fs/procfs/procfs_vfsops.c
new file mode 100644
index 0000000..67dc31f
--- /dev/null
+++ b/sys/fs/procfs/procfs_vfsops.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_vfsops.c 8.4 (Berkeley) 1/21/94
+ *
+ * From:
+ * $Id: procfs_vfsops.c,v 3.1 1993/12/15 09:40:17 jsp Exp $
+ */
+
+/*
+ * procfs VFS interface
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/syslog.h>
+#include <sys/mount.h>
+#include <sys/signalvar.h>
+#include <sys/vnode.h>
+#include <miscfs/procfs/procfs.h>
+#include <vm/vm.h> /* for PAGE_SIZE */
+
+/*
+ * VFS Operations.
+ *
+ * mount system call
+ */
+/* ARGSUSED */
+int
+procfs_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ u_int size;
+
+ if (UIO_MX & (UIO_MX-1)) {
+ log(LOG_ERR, "procfs: invalid directory entry size");
+ return (EINVAL);
+ }
+
+ if (mp->mnt_flag & MNT_UPDATE)
+ return (EOPNOTSUPP);
+
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_data = 0;
+ getnewfsid(mp, MOUNT_PROCFS);
+
+ (void) copyinstr(path, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+
+ size = sizeof("procfs") - 1;
+ bcopy("procfs", mp->mnt_stat.f_mntfromname, size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+
+ return (0);
+}
+
+/*
+ * unmount system call
+ */
+int
+procfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ int error;
+ extern int doforce;
+ int flags = 0;
+
+ if (mntflags & MNT_FORCE) {
+ /* procfs can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ if (error = vflush(mp, 0, flags))
+ return (error);
+
+ return (0);
+}
+
+int
+procfs_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct pfsnode *pfs;
+ struct vnode *vp;
+ int error;
+
+ error = procfs_allocvp(mp, &vp, (pid_t) 0, Proot);
+ if (error)
+ return (error);
+
+ vp->v_type = VDIR;
+ vp->v_flag = VROOT;
+ pfs = VTOPFS(vp);
+
+ *vpp = vp;
+ return (0);
+}
+
+/*
+ */
+/* ARGSUSED */
+int
+procfs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+
+ return (0);
+}
+
+/*
+ * Get file system statistics.
+ */
+int
+procfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ sbp->f_type = MOUNT_PROCFS;
+ sbp->f_bsize = PAGE_SIZE;
+ sbp->f_iosize = PAGE_SIZE;
+ sbp->f_blocks = 1; /* avoid divide by zero in some df's */
+ sbp->f_bfree = 0;
+ sbp->f_bavail = 0;
+ sbp->f_files = maxproc; /* approx */
+ sbp->f_ffree = maxproc - nprocs; /* approx */
+
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+
+ return (0);
+}
+
+
+int
+procfs_quotactl(mp, cmds, uid, arg, p)
+ struct mount *mp;
+ int cmds;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+procfs_sync(mp, waitfor)
+ struct mount *mp;
+ int waitfor;
+{
+
+ return (0);
+}
+
+int
+procfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+procfs_fhtovp(mp, fhp, vpp)
+ struct mount *mp;
+ struct fid *fhp;
+ struct vnode **vpp;
+{
+
+ return (EINVAL);
+}
+
+int
+procfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return EINVAL;
+}
+
+int
+procfs_init()
+{
+
+ return (0);
+}
+
+struct vfsops procfs_vfsops = {
+ procfs_mount,
+ procfs_start,
+ procfs_unmount,
+ procfs_root,
+ procfs_quotactl,
+ procfs_statfs,
+ procfs_sync,
+ procfs_vget,
+ procfs_fhtovp,
+ procfs_vptofh,
+ procfs_init,
+};
diff --git a/sys/fs/procfs/procfs_vnops.c b/sys/fs/procfs/procfs_vnops.c
new file mode 100644
index 0000000..b6c6e6f
--- /dev/null
+++ b/sys/fs/procfs/procfs_vnops.c
@@ -0,0 +1,830 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_vnops.c 8.6 (Berkeley) 2/7/94
+ *
+ * From:
+ * $Id: procfs_vnops.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+/*
+ * procfs vnode interface
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/resourcevar.h>
+#include <miscfs/procfs/procfs.h>
+#include <vm/vm.h> /* for PAGE_SIZE */
+
+/*
+ * Vnode Operations.
+ *
+ */
+
+/*
+ * This is a list of the valid names in the
+ * process-specific sub-directories. It is
+ * used in procfs_lookup and procfs_readdir
+ */
+static struct pfsnames {
+ u_short d_namlen;
+ char d_name[PROCFS_NAMELEN];
+ pfstype d_pfstype;
+} procent[] = {
+#define N(s) sizeof(s)-1, s
+ /* namlen, nam, type */
+ { N("file"), Pfile },
+ { N("mem"), Pmem },
+ { N("regs"), Pregs },
+ { N("fpregs"), Pfpregs },
+ { N("ctl"), Pctl },
+ { N("status"), Pstatus },
+ { N("note"), Pnote },
+ { N("notepg"), Pnotepg },
+#undef N
+};
+#define Nprocent (sizeof(procent)/sizeof(procent[0]))
+
+static pid_t atopid __P((const char *, u_int));
+
+/*
+ * set things up for doing i/o on
+ * the pfsnode (vp). (vp) is locked
+ * on entry, and should be left locked
+ * on exit.
+ *
+ * for procfs we don't need to do anything
+ * in particular for i/o. all that is done
+ * is to support exclusive open on process
+ * memory images.
+ */
+int
+procfs_open(ap)
+ struct vop_open_args *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+
+ switch (pfs->pfs_type) {
+ case Pmem:
+ if (PFIND(pfs->pfs_pid) == 0)
+ return (ENOENT); /* was ESRCH, jsp */
+
+ if ((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL) ||
+ (pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE))
+ return (EBUSY);
+
+
+ if (ap->a_mode & FWRITE)
+ pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL);
+
+ return (0);
+
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * close the pfsnode (vp) after doing i/o.
+ * (vp) is not locked on entry or exit.
+ *
+ * nothing to do for procfs other than undo
+ * any exclusive open flag (see _open above).
+ */
+int
+procfs_close(ap)
+ struct vop_close_args *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+
+ switch (pfs->pfs_type) {
+ case Pmem:
+ if ((ap->a_fflag & FWRITE) && (pfs->pfs_flags & O_EXCL))
+ pfs->pfs_flags &= ~(FWRITE|O_EXCL);
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * do an ioctl operation on pfsnode (vp).
+ * (vp) is not locked on entry or exit.
+ */
+int
+procfs_ioctl(ap)
+ struct vop_ioctl_args *ap;
+{
+
+ return (ENOTTY);
+}
+
+/*
+ * do block mapping for pfsnode (vp).
+ * since we don't use the buffer cache
+ * for procfs this function should never
+ * be called. in any case, it's not clear
+ * what part of the kernel ever makes use
+ * of this function. for sanity, this is the
+ * usual no-op bmap, although returning
+ * (EIO) would be a reasonable alternative.
+ */
+int
+procfs_bmap(ap)
+ struct vop_bmap_args *ap;
+{
+
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = ap->a_vp;
+ if (ap->a_bnp != NULL)
+ *ap->a_bnp = ap->a_bn;
+ return (0);
+}
+
+/*
+ * _inactive is called when the pfsnode
+ * is vrele'd and the reference count goes
+ * to zero. (vp) will be on the vnode free
+ * list, so to get it back vget() must be
+ * used.
+ *
+ * for procfs, check if the process is still
+ * alive and if it isn't then just throw away
+ * the vnode by calling vgone(). this may
+ * be overkill and a waste of time since the
+ * chances are that the process will still be
+ * there and PFIND is not free.
+ *
+ * (vp) is not locked on entry or exit.
+ */
+int
+procfs_inactive(ap)
+ struct vop_inactive_args *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+
+ if (PFIND(pfs->pfs_pid) == 0)
+ vgone(ap->a_vp);
+
+ return (0);
+}
+
+/*
+ * _reclaim is called when getnewvnode()
+ * wants to make use of an entry on the vnode
+ * free list. at this time the filesystem needs
+ * to free any private data and remove the node
+ * from any private lists.
+ */
+int
+procfs_reclaim(ap)
+ struct vop_reclaim_args *ap;
+{
+ int error;
+
+ error = procfs_freevp(ap->a_vp);
+ return (error);
+}
+
+/*
+ * Return POSIX pathconf information applicable to special devices.
+ */
+int
+procfs_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_MAX_CANON:
+ *ap->a_retval = MAX_CANON;
+ return (0);
+ case _PC_MAX_INPUT:
+ *ap->a_retval = MAX_INPUT;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ case _PC_VDISABLE:
+ *ap->a_retval = _POSIX_VDISABLE;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * _print is used for debugging.
+ * just print a readable description
+ * of (vp).
+ */
+int
+procfs_print(ap)
+ struct vop_print_args *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+
+ printf("tag VT_PROCFS, pid %d, mode %x, flags %x\n",
+ pfs->pfs_pid,
+ pfs->pfs_mode, pfs->pfs_flags);
+ return (0);
+}
+
+/*
+ * _abortop is called when operations such as
+ * rename and create fail. this entry is responsible
+ * for undoing any side-effects caused by the lookup.
+ * this will always include freeing the pathname buffer.
+ */
+int
+procfs_abortop(ap)
+ struct vop_abortop_args *ap;
+{
+
+ if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
+ FREE(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ return (0);
+}
+
+/*
+ * generic entry point for unsupported operations
+ */
+int
+procfs_badop()
+{
+
+ return (EIO);
+}
+
+/*
+ * Invent attributes for pfsnode (vp) and store
+ * them in (vap).
+ * Directories lengths are returned as zero since
+ * any real length would require the genuine size
+ * to be computed, and nothing cares anyway.
+ *
+ * this is relatively minimal for procfs.
+ */
+int
+procfs_getattr(ap)
+ struct vop_getattr_args *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+ struct vattr *vap = ap->a_vap;
+ struct proc *procp;
+ int error;
+
+ /* first check the process still exists */
+ switch (pfs->pfs_type) {
+ case Proot:
+ procp = 0;
+ break;
+
+ default:
+ procp = PFIND(pfs->pfs_pid);
+ if (procp == 0)
+ return (ENOENT);
+ }
+
+ error = 0;
+
+ /* start by zeroing out the attributes */
+ VATTR_NULL(vap);
+
+ /* next do all the common fields */
+ vap->va_type = ap->a_vp->v_type;
+ vap->va_mode = pfs->pfs_mode;
+ vap->va_fileid = pfs->pfs_fileno;
+ vap->va_flags = 0;
+ vap->va_blocksize = PAGE_SIZE;
+ vap->va_bytes = vap->va_size = 0;
+
+ /*
+ * If the process has exercised some setuid or setgid
+ * privilege, then rip away read/write permission so
+ * that only root can gain access.
+ */
+ switch (pfs->pfs_type) {
+ case Pregs:
+ case Pfpregs:
+ case Pmem:
+ if (procp->p_flag & P_SUGID)
+ vap->va_mode &= ~((VREAD|VWRITE)|
+ ((VREAD|VWRITE)>>3)|
+ ((VREAD|VWRITE)>>6));
+ break;
+ }
+
+ /*
+ * Make all times be current TOD.
+ * It would be possible to get the process start
+ * time from the p_stat structure, but there's
+ * no "file creation" time stamp anyway, and the
+ * p_stat structure is not addressible if u. gets
+ * swapped out for that process.
+ */
+ microtime(&vap->va_ctime);
+ vap->va_atime = vap->va_mtime = vap->va_ctime;
+
+ /*
+ * now do the object specific fields
+ *
+ * The size could be set from struct reg, but it's hardly
+ * worth the trouble, and it puts some (potentially) machine
+ * dependent data into this machine-independent code. If it
+ * becomes important then this function should break out into
+ * a per-file stat function in the corresponding .c file.
+ */
+
+ switch (pfs->pfs_type) {
+ case Proot:
+ vap->va_nlink = 2;
+ vap->va_uid = 0;
+ vap->va_gid = 0;
+ break;
+
+ case Pproc:
+ vap->va_nlink = 2;
+ vap->va_uid = procp->p_ucred->cr_uid;
+ vap->va_gid = procp->p_ucred->cr_gid;
+ break;
+
+ case Pfile:
+ error = EOPNOTSUPP;
+ break;
+
+ case Pmem:
+ vap->va_nlink = 1;
+ vap->va_bytes = vap->va_size =
+ ctob(procp->p_vmspace->vm_tsize +
+ procp->p_vmspace->vm_dsize +
+ procp->p_vmspace->vm_ssize);
+ vap->va_uid = procp->p_ucred->cr_uid;
+ vap->va_gid = procp->p_ucred->cr_gid;
+ break;
+
+ case Pregs:
+ case Pfpregs:
+ case Pctl:
+ case Pstatus:
+ case Pnote:
+ case Pnotepg:
+ vap->va_nlink = 1;
+ vap->va_uid = procp->p_ucred->cr_uid;
+ vap->va_gid = procp->p_ucred->cr_gid;
+ break;
+
+ default:
+ panic("procfs_getattr");
+ }
+
+ return (error);
+}
+
+int
+procfs_setattr(ap)
+ struct vop_setattr_args *ap;
+{
+ /*
+ * just fake out attribute setting
+ * it's not good to generate an error
+ * return, otherwise things like creat()
+ * will fail when they try to set the
+ * file length to 0. worse, this means
+ * that echo $note > /proc/$pid/note will fail.
+ */
+
+ return (0);
+}
+
+/*
+ * implement access checking.
+ *
+ * something very similar to this code is duplicated
+ * throughout the 4bsd kernel and should be moved
+ * into kern/vfs_subr.c sometime.
+ *
+ * actually, the check for super-user is slightly
+ * broken since it will allow read access to write-only
+ * objects. this doesn't cause any particular trouble
+ * but does mean that the i/o entry points need to check
+ * that the operation really does make sense.
+ */
+int
+procfs_access(ap)
+ struct vop_access_args *ap;
+{
+ struct vattr *vap;
+ struct vattr vattr;
+ int error;
+
+ /*
+ * If you're the super-user,
+ * you always get access.
+ */
+ if (ap->a_cred->cr_uid == (uid_t) 0)
+ return (0);
+ vap = &vattr;
+ if (error = VOP_GETATTR(ap->a_vp, vap, ap->a_cred, ap->a_p))
+ return (error);
+
+ /*
+ * Access check is based on only one of owner, group, public.
+ * If not owner, then check group. If not a member of the
+ * group, then check public access.
+ */
+ if (ap->a_cred->cr_uid != vap->va_uid) {
+ gid_t *gp;
+ int i;
+
+ (ap->a_mode) >>= 3;
+ gp = ap->a_cred->cr_groups;
+ for (i = 0; i < ap->a_cred->cr_ngroups; i++, gp++)
+ if (vap->va_gid == *gp)
+ goto found;
+ ap->a_mode >>= 3;
+found:
+ ;
+ }
+
+ if ((vap->va_mode & ap->a_mode) == ap->a_mode)
+ return (0);
+
+ return (EACCES);
+}
+
+/*
+ * lookup. this is incredibly complicated in the
+ * general case, however for most pseudo-filesystems
+ * very little needs to be done.
+ *
+ * unless you want to get a migraine, just make sure your
+ * filesystem doesn't do any locking of its own. otherwise
+ * read and inwardly digest ufs_lookup().
+ */
+int
+procfs_lookup(ap)
+ struct vop_lookup_args *ap;
+{
+ struct componentname *cnp = ap->a_cnp;
+ struct vnode **vpp = ap->a_vpp;
+ struct vnode *dvp = ap->a_dvp;
+ char *pname = cnp->cn_nameptr;
+ int error = 0;
+ pid_t pid;
+ struct vnode *nvp;
+ struct pfsnode *pfs;
+ struct proc *procp;
+ pfstype pfs_type;
+ int i;
+
+ if (cnp->cn_namelen == 1 && *pname == '.') {
+ *vpp = dvp;
+ VREF(dvp);
+ /*VOP_LOCK(dvp);*/
+ return (0);
+ }
+
+ *vpp = NULL;
+
+ pfs = VTOPFS(dvp);
+ switch (pfs->pfs_type) {
+ case Proot:
+ if (cnp->cn_flags & ISDOTDOT)
+ return (EIO);
+
+ if (CNEQ(cnp, "curproc", 7))
+ pid = cnp->cn_proc->p_pid;
+ else
+ pid = atopid(pname, cnp->cn_namelen);
+ if (pid == NO_PID)
+ return (ENOENT);
+
+ procp = PFIND(pid);
+ if (procp == 0)
+ return (ENOENT);
+
+ error = procfs_allocvp(dvp->v_mount, &nvp, pid, Pproc);
+ if (error)
+ return (error);
+
+ nvp->v_type = VDIR;
+ pfs = VTOPFS(nvp);
+
+ *vpp = nvp;
+ return (0);
+
+ case Pproc:
+ if (cnp->cn_flags & ISDOTDOT) {
+ error = procfs_root(dvp->v_mount, vpp);
+ return (error);
+ }
+
+ procp = PFIND(pfs->pfs_pid);
+ if (procp == 0)
+ return (ENOENT);
+
+ for (i = 0; i < Nprocent; i++) {
+ struct pfsnames *dp = &procent[i];
+
+ if (cnp->cn_namelen == dp->d_namlen &&
+ bcmp(pname, dp->d_name, dp->d_namlen) == 0) {
+ pfs_type = dp->d_pfstype;
+ goto found;
+ }
+ }
+ return (ENOENT);
+
+ found:
+ if (pfs_type == Pfile) {
+ nvp = procfs_findtextvp(procp);
+ if (nvp) {
+ VREF(nvp);
+ VOP_LOCK(nvp);
+ } else {
+ error = ENXIO;
+ }
+ } else {
+ error = procfs_allocvp(dvp->v_mount, &nvp,
+ pfs->pfs_pid, pfs_type);
+ if (error)
+ return (error);
+
+ nvp->v_type = VREG;
+ pfs = VTOPFS(nvp);
+ }
+ *vpp = nvp;
+ return (error);
+
+ default:
+ return (ENOTDIR);
+ }
+}
+
+/*
+ * readdir returns directory entries from pfsnode (vp).
+ *
+ * the strategy here with procfs is to generate a single
+ * directory entry at a time (struct pfsdent) and then
+ * copy that out to userland using uiomove. a more efficent
+ * though more complex implementation, would try to minimize
+ * the number of calls to uiomove(). for procfs, this is
+ * hardly worth the added code complexity.
+ *
+ * this should just be done through read()
+ */
+int
+procfs_readdir(ap)
+ struct vop_readdir_args *ap;
+{
+ struct uio *uio = ap->a_uio;
+ struct pfsdent d;
+ struct pfsdent *dp = &d;
+ struct pfsnode *pfs;
+ int error;
+ int count;
+ int i;
+
+ pfs = VTOPFS(ap->a_vp);
+
+ if (uio->uio_resid < UIO_MX)
+ return (EINVAL);
+ if (uio->uio_offset & (UIO_MX-1))
+ return (EINVAL);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+
+ error = 0;
+ count = 0;
+ i = uio->uio_offset / UIO_MX;
+
+ switch (pfs->pfs_type) {
+ /*
+ * this is for the process-specific sub-directories.
+ * all that is needed to is copy out all the entries
+ * from the procent[] table (top of this file).
+ */
+ case Pproc: {
+ while (uio->uio_resid >= UIO_MX) {
+ struct pfsnames *dt;
+
+ if (i >= Nprocent)
+ break;
+
+ dt = &procent[i];
+
+ dp->d_reclen = UIO_MX;
+ dp->d_fileno = PROCFS_FILENO(pfs->pfs_pid, dt->d_pfstype);
+ dp->d_type = DT_REG;
+ dp->d_namlen = dt->d_namlen;
+ bcopy(dt->d_name, dp->d_name, sizeof(dt->d_name)-1);
+ error = uiomove((caddr_t) dp, UIO_MX, uio);
+ if (error)
+ break;
+ count += UIO_MX;
+ i++;
+ }
+
+ break;
+
+ }
+
+ /*
+ * this is for the root of the procfs filesystem
+ * what is needed is a special entry for "curproc"
+ * followed by an entry for each process on allproc
+#ifdef PROCFS_ZOMBIE
+ * and zombproc.
+#endif
+ */
+
+ case Proot: {
+ int pcnt;
+#ifdef PROCFS_ZOMBIE
+ int doingzomb = 0;
+#endif
+ volatile struct proc *p;
+
+ p = allproc;
+
+#define PROCFS_XFILES 1 /* number of other entries, like "curproc" */
+ pcnt = PROCFS_XFILES;
+
+ while (p && uio->uio_resid >= UIO_MX) {
+ bzero((char *) dp, UIO_MX);
+ dp->d_type = DT_DIR;
+ dp->d_reclen = UIO_MX;
+
+ switch (i) {
+ case 0:
+ /* ship out entry for "curproc" */
+ dp->d_fileno = PROCFS_FILENO(PID_MAX+1, Pproc);
+ dp->d_namlen = sprintf(dp->d_name, "curproc");
+ break;
+
+ default:
+ if (pcnt >= i) {
+ dp->d_fileno = PROCFS_FILENO(p->p_pid, Pproc);
+ dp->d_namlen = sprintf(dp->d_name, "%ld", (long) p->p_pid);
+ }
+
+ p = p->p_next;
+
+#ifdef PROCFS_ZOMBIE
+ if (p == 0 && doingzomb == 0) {
+ doingzomb = 1;
+ p = zombproc;
+ }
+#endif
+
+ if (pcnt++ < i)
+ continue;
+
+ break;
+ }
+ error = uiomove((caddr_t) dp, UIO_MX, uio);
+ if (error)
+ break;
+ count += UIO_MX;
+ i++;
+ }
+
+ break;
+
+ }
+
+ default:
+ error = ENOTDIR;
+ break;
+ }
+
+ uio->uio_offset = i * UIO_MX;
+
+ return (error);
+}
+
+/*
+ * convert decimal ascii to pid_t
+ */
+static pid_t
+atopid(b, len)
+ const char *b;
+ u_int len;
+{
+ pid_t p = 0;
+
+ while (len--) {
+ char c = *b++;
+ if (c < '0' || c > '9')
+ return (NO_PID);
+ p = 10 * p + (c - '0');
+ if (p > PID_MAX)
+ return (NO_PID);
+ }
+
+ return (p);
+}
+
+/*
+ * procfs vnode operations.
+ */
+int (**procfs_vnodeop_p)();
+struct vnodeopv_entry_desc procfs_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, procfs_lookup }, /* lookup */
+ { &vop_create_desc, procfs_create }, /* create */
+ { &vop_mknod_desc, procfs_mknod }, /* mknod */
+ { &vop_open_desc, procfs_open }, /* open */
+ { &vop_close_desc, procfs_close }, /* close */
+ { &vop_access_desc, procfs_access }, /* access */
+ { &vop_getattr_desc, procfs_getattr }, /* getattr */
+ { &vop_setattr_desc, procfs_setattr }, /* setattr */
+ { &vop_read_desc, procfs_read }, /* read */
+ { &vop_write_desc, procfs_write }, /* write */
+ { &vop_ioctl_desc, procfs_ioctl }, /* ioctl */
+ { &vop_select_desc, procfs_select }, /* select */
+ { &vop_mmap_desc, procfs_mmap }, /* mmap */
+ { &vop_fsync_desc, procfs_fsync }, /* fsync */
+ { &vop_seek_desc, procfs_seek }, /* seek */
+ { &vop_remove_desc, procfs_remove }, /* remove */
+ { &vop_link_desc, procfs_link }, /* link */
+ { &vop_rename_desc, procfs_rename }, /* rename */
+ { &vop_mkdir_desc, procfs_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, procfs_rmdir }, /* rmdir */
+ { &vop_symlink_desc, procfs_symlink }, /* symlink */
+ { &vop_readdir_desc, procfs_readdir }, /* readdir */
+ { &vop_readlink_desc, procfs_readlink }, /* readlink */
+ { &vop_abortop_desc, procfs_abortop }, /* abortop */
+ { &vop_inactive_desc, procfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, procfs_reclaim }, /* reclaim */
+ { &vop_lock_desc, procfs_lock }, /* lock */
+ { &vop_unlock_desc, procfs_unlock }, /* unlock */
+ { &vop_bmap_desc, procfs_bmap }, /* bmap */
+ { &vop_strategy_desc, procfs_strategy }, /* strategy */
+ { &vop_print_desc, procfs_print }, /* print */
+ { &vop_islocked_desc, procfs_islocked }, /* islocked */
+ { &vop_pathconf_desc, procfs_pathconf }, /* pathconf */
+ { &vop_advlock_desc, procfs_advlock }, /* advlock */
+ { &vop_blkatoff_desc, procfs_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, procfs_valloc }, /* valloc */
+ { &vop_vfree_desc, procfs_vfree }, /* vfree */
+ { &vop_truncate_desc, procfs_truncate }, /* truncate */
+ { &vop_update_desc, procfs_update }, /* update */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc procfs_vnodeop_opv_desc =
+ { &procfs_vnodeop_p, procfs_vnodeop_entries };
diff --git a/sys/fs/specfs/spec_vnops.c b/sys/fs/specfs/spec_vnops.c
new file mode 100644
index 0000000..55b5dd8
--- /dev/null
+++ b/sys/fs/specfs/spec_vnops.c
@@ -0,0 +1,705 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)spec_vnops.c 8.6 (Berkeley) 4/9/94
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/buf.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/disklabel.h>
+#include <miscfs/specfs/specdev.h>
+
+/* symbolic sleep message strings for devices */
+char devopn[] = "devopn";
+char devio[] = "devio";
+char devwait[] = "devwait";
+char devin[] = "devin";
+char devout[] = "devout";
+char devioc[] = "devioc";
+char devcls[] = "devcls";
+
+int (**spec_vnodeop_p)();
+struct vnodeopv_entry_desc spec_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, spec_lookup }, /* lookup */
+ { &vop_create_desc, spec_create }, /* create */
+ { &vop_mknod_desc, spec_mknod }, /* mknod */
+ { &vop_open_desc, spec_open }, /* open */
+ { &vop_close_desc, spec_close }, /* close */
+ { &vop_access_desc, spec_access }, /* access */
+ { &vop_getattr_desc, spec_getattr }, /* getattr */
+ { &vop_setattr_desc, spec_setattr }, /* setattr */
+ { &vop_read_desc, spec_read }, /* read */
+ { &vop_write_desc, spec_write }, /* write */
+ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */
+ { &vop_select_desc, spec_select }, /* select */
+ { &vop_mmap_desc, spec_mmap }, /* mmap */
+ { &vop_fsync_desc, spec_fsync }, /* fsync */
+ { &vop_seek_desc, spec_seek }, /* seek */
+ { &vop_remove_desc, spec_remove }, /* remove */
+ { &vop_link_desc, spec_link }, /* link */
+ { &vop_rename_desc, spec_rename }, /* rename */
+ { &vop_mkdir_desc, spec_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, spec_rmdir }, /* rmdir */
+ { &vop_symlink_desc, spec_symlink }, /* symlink */
+ { &vop_readdir_desc, spec_readdir }, /* readdir */
+ { &vop_readlink_desc, spec_readlink }, /* readlink */
+ { &vop_abortop_desc, spec_abortop }, /* abortop */
+ { &vop_inactive_desc, spec_inactive }, /* inactive */
+ { &vop_reclaim_desc, spec_reclaim }, /* reclaim */
+ { &vop_lock_desc, spec_lock }, /* lock */
+ { &vop_unlock_desc, spec_unlock }, /* unlock */
+ { &vop_bmap_desc, spec_bmap }, /* bmap */
+ { &vop_strategy_desc, spec_strategy }, /* strategy */
+ { &vop_print_desc, spec_print }, /* print */
+ { &vop_islocked_desc, spec_islocked }, /* islocked */
+ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */
+ { &vop_advlock_desc, spec_advlock }, /* advlock */
+ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, spec_valloc }, /* valloc */
+ { &vop_vfree_desc, spec_vfree }, /* vfree */
+ { &vop_truncate_desc, spec_truncate }, /* truncate */
+ { &vop_update_desc, spec_update }, /* update */
+ { &vop_bwrite_desc, spec_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc spec_vnodeop_opv_desc =
+ { &spec_vnodeop_p, spec_vnodeop_entries };
+
+/*
+ * Trivial lookup routine that always fails.
+ */
+int
+spec_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+
+ *ap->a_vpp = NULL;
+ return (ENOTDIR);
+}
+
+/*
+ * Open a special file.
+ */
+/* ARGSUSED */
+int
+spec_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *bvp, *vp = ap->a_vp;
+ dev_t bdev, dev = (dev_t)vp->v_rdev;
+ register int maj = major(dev);
+ int error;
+
+ /*
+ * Don't allow open if fs is mounted -nodev.
+ */
+ if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV))
+ return (ENXIO);
+
+ switch (vp->v_type) {
+
+ case VCHR:
+ if ((u_int)maj >= nchrdev)
+ return (ENXIO);
+ if (ap->a_cred != FSCRED && (ap->a_mode & FWRITE)) {
+ /*
+ * When running in very secure mode, do not allow
+ * opens for writing of any disk character devices.
+ */
+ if (securelevel >= 2 && isdisk(dev, VCHR))
+ return (EPERM);
+ /*
+ * When running in secure mode, do not allow opens
+ * for writing of /dev/mem, /dev/kmem, or character
+ * devices whose corresponding block devices are
+ * currently mounted.
+ */
+ if (securelevel >= 1) {
+ if ((bdev = chrtoblk(dev)) != NODEV &&
+ vfinddev(bdev, VBLK, &bvp) &&
+ bvp->v_usecount > 0 &&
+ (error = vfs_mountedon(bvp)))
+ return (error);
+ if (iskmemdev(dev))
+ return (EPERM);
+ }
+ }
+ VOP_UNLOCK(vp);
+ error = (*cdevsw[maj].d_open)(dev, ap->a_mode, S_IFCHR, ap->a_p);
+ VOP_LOCK(vp);
+ return (error);
+
+ case VBLK:
+ if ((u_int)maj >= nblkdev)
+ return (ENXIO);
+ /*
+ * When running in very secure mode, do not allow
+ * opens for writing of any disk block devices.
+ */
+ if (securelevel >= 2 && ap->a_cred != FSCRED &&
+ (ap->a_mode & FWRITE) && isdisk(dev, VBLK))
+ return (EPERM);
+ /*
+ * Do not allow opens of block devices that are
+ * currently mounted.
+ */
+ if (error = vfs_mountedon(vp))
+ return (error);
+ return ((*bdevsw[maj].d_open)(dev, ap->a_mode, S_IFBLK, ap->a_p));
+ }
+ return (0);
+}
+
+/*
+ * Vnode op for read
+ */
+/* ARGSUSED */
+int
+spec_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct uio *uio = ap->a_uio;
+ struct proc *p = uio->uio_procp;
+ struct buf *bp;
+ daddr_t bn, nextbn;
+ long bsize, bscale;
+ struct partinfo dpart;
+ int n, on, majordev, (*ioctl)();
+ int error = 0;
+ dev_t dev;
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_READ)
+ panic("spec_read mode");
+ if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
+ panic("spec_read proc");
+#endif
+ if (uio->uio_resid == 0)
+ return (0);
+
+ switch (vp->v_type) {
+
+ case VCHR:
+ VOP_UNLOCK(vp);
+ error = (*cdevsw[major(vp->v_rdev)].d_read)
+ (vp->v_rdev, uio, ap->a_ioflag);
+ VOP_LOCK(vp);
+ return (error);
+
+ case VBLK:
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+ bsize = BLKDEV_IOSIZE;
+ dev = vp->v_rdev;
+ if ((majordev = major(dev)) < nblkdev &&
+ (ioctl = bdevsw[majordev].d_ioctl) != NULL &&
+ (*ioctl)(dev, DIOCGPART, (caddr_t)&dpart, FREAD, p) == 0 &&
+ dpart.part->p_fstype == FS_BSDFFS &&
+ dpart.part->p_frag != 0 && dpart.part->p_fsize != 0)
+ bsize = dpart.part->p_frag * dpart.part->p_fsize;
+ bscale = bsize / DEV_BSIZE;
+ do {
+ bn = (uio->uio_offset / DEV_BSIZE) &~ (bscale - 1);
+ on = uio->uio_offset % bsize;
+ n = min((unsigned)(bsize - on), uio->uio_resid);
+ if (vp->v_lastr + bscale == bn) {
+ nextbn = bn + bscale;
+ error = breadn(vp, bn, (int)bsize, &nextbn,
+ (int *)&bsize, 1, NOCRED, &bp);
+ } else
+ error = bread(vp, bn, (int)bsize, NOCRED, &bp);
+ vp->v_lastr = bn;
+ n = min(n, bsize - bp->b_resid);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ error = uiomove((char *)bp->b_data + on, n, uio);
+ if (n + on == bsize)
+ bp->b_flags |= B_AGE;
+ brelse(bp);
+ } while (error == 0 && uio->uio_resid > 0 && n != 0);
+ return (error);
+
+ default:
+ panic("spec_read type");
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Vnode op for write
+ */
+/* ARGSUSED */
+int
+spec_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct uio *uio = ap->a_uio;
+ struct proc *p = uio->uio_procp;
+ struct buf *bp;
+ daddr_t bn;
+ int bsize, blkmask;
+ struct partinfo dpart;
+ register int n, on;
+ int error = 0;
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_WRITE)
+ panic("spec_write mode");
+ if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
+ panic("spec_write proc");
+#endif
+
+ switch (vp->v_type) {
+
+ case VCHR:
+ VOP_UNLOCK(vp);
+ error = (*cdevsw[major(vp->v_rdev)].d_write)
+ (vp->v_rdev, uio, ap->a_ioflag);
+ VOP_LOCK(vp);
+ return (error);
+
+ case VBLK:
+ if (uio->uio_resid == 0)
+ return (0);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+ bsize = BLKDEV_IOSIZE;
+ if ((*bdevsw[major(vp->v_rdev)].d_ioctl)(vp->v_rdev, DIOCGPART,
+ (caddr_t)&dpart, FREAD, p) == 0) {
+ if (dpart.part->p_fstype == FS_BSDFFS &&
+ dpart.part->p_frag != 0 && dpart.part->p_fsize != 0)
+ bsize = dpart.part->p_frag *
+ dpart.part->p_fsize;
+ }
+ blkmask = (bsize / DEV_BSIZE) - 1;
+ do {
+ bn = (uio->uio_offset / DEV_BSIZE) &~ blkmask;
+ on = uio->uio_offset % bsize;
+ n = min((unsigned)(bsize - on), uio->uio_resid);
+ if (n == bsize)
+ bp = getblk(vp, bn, bsize, 0, 0);
+ else
+ error = bread(vp, bn, bsize, NOCRED, &bp);
+ n = min(n, bsize - bp->b_resid);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ error = uiomove((char *)bp->b_data + on, n, uio);
+ if (n + on == bsize) {
+ bp->b_flags |= B_AGE;
+ bawrite(bp);
+ } else
+ bdwrite(bp);
+ } while (error == 0 && uio->uio_resid > 0 && n != 0);
+ return (error);
+
+ default:
+ panic("spec_write type");
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Device ioctl operation.
+ */
+/* ARGSUSED */
+int
+spec_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ dev_t dev = ap->a_vp->v_rdev;
+
+ switch (ap->a_vp->v_type) {
+
+ case VCHR:
+ return ((*cdevsw[major(dev)].d_ioctl)(dev, ap->a_command, ap->a_data,
+ ap->a_fflag, ap->a_p));
+
+ case VBLK:
+ if (ap->a_command == 0 && (int)ap->a_data == B_TAPE)
+ if (bdevsw[major(dev)].d_flags & B_TAPE)
+ return (0);
+ else
+ return (1);
+ return ((*bdevsw[major(dev)].d_ioctl)(dev, ap->a_command, ap->a_data,
+ ap->a_fflag, ap->a_p));
+
+ default:
+ panic("spec_ioctl");
+ /* NOTREACHED */
+ }
+}
+
+/* ARGSUSED */
+int
+spec_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register dev_t dev;
+
+ switch (ap->a_vp->v_type) {
+
+ default:
+ return (1); /* XXX */
+
+ case VCHR:
+ dev = ap->a_vp->v_rdev;
+ return (*cdevsw[major(dev)].d_select)(dev, ap->a_which, ap->a_p);
+ }
+}
+/*
+ * Synch buffers associated with a block device
+ */
+/* ARGSUSED */
+int
+spec_fsync(ap)
+ struct vop_fsync_args /* {
+ struct vnode *a_vp;
+ struct ucred *a_cred;
+ int a_waitfor;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct buf *bp;
+ struct buf *nbp;
+ int s;
+
+ if (vp->v_type == VCHR)
+ return (0);
+ /*
+ * Flush all dirty buffers associated with a block device.
+ */
+loop:
+ s = splbio();
+ for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
+ nbp = bp->b_vnbufs.le_next;
+ if ((bp->b_flags & B_BUSY))
+ continue;
+ if ((bp->b_flags & B_DELWRI) == 0)
+ panic("spec_fsync: not dirty");
+ bremfree(bp);
+ bp->b_flags |= B_BUSY;
+ splx(s);
+ bawrite(bp);
+ goto loop;
+ }
+ if (ap->a_waitfor == MNT_WAIT) {
+ while (vp->v_numoutput) {
+ vp->v_flag |= VBWAIT;
+ sleep((caddr_t)&vp->v_numoutput, PRIBIO + 1);
+ }
+#ifdef DIAGNOSTIC
+ if (vp->v_dirtyblkhd.lh_first) {
+ vprint("spec_fsync: dirty", vp);
+ goto loop;
+ }
+#endif
+ }
+ splx(s);
+ return (0);
+}
+
+/*
+ * Just call the device strategy routine
+ */
+int
+spec_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+
+ (*bdevsw[major(ap->a_bp->b_dev)].d_strategy)(ap->a_bp);
+ return (0);
+}
+
+/*
+ * This is a noop, simply returning what one has been given.
+ */
+int
+spec_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ } */ *ap;
+{
+
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = ap->a_vp;
+ if (ap->a_bnp != NULL)
+ *ap->a_bnp = ap->a_bn;
+ return (0);
+}
+
+/*
+ * At the moment we do not do any locking.
+ */
+/* ARGSUSED */
+int
+spec_lock(ap)
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/* ARGSUSED */
+int
+spec_unlock(ap)
+ struct vop_unlock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * Device close routine
+ */
+/* ARGSUSED */
+int
+spec_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ dev_t dev = vp->v_rdev;
+ int (*devclose) __P((dev_t, int, int, struct proc *));
+ int mode, error;
+
+ switch (vp->v_type) {
+
+ case VCHR:
+ /*
+ * Hack: a tty device that is a controlling terminal
+ * has a reference from the session structure.
+ * We cannot easily tell that a character device is
+ * a controlling terminal, unless it is the closing
+ * process' controlling terminal. In that case,
+ * if the reference count is 2 (this last descriptor
+ * plus the session), release the reference from the session.
+ */
+ if (vcount(vp) == 2 && ap->a_p &&
+ vp == ap->a_p->p_session->s_ttyvp) {
+ vrele(vp);
+ ap->a_p->p_session->s_ttyvp = NULL;
+ }
+ /*
+ * If the vnode is locked, then we are in the midst
+ * of forcably closing the device, otherwise we only
+ * close on last reference.
+ */
+ if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0)
+ return (0);
+ devclose = cdevsw[major(dev)].d_close;
+ mode = S_IFCHR;
+ break;
+
+ case VBLK:
+ /*
+ * On last close of a block device (that isn't mounted)
+ * we must invalidate any in core blocks, so that
+ * we can, for instance, change floppy disks.
+ */
+ if (error = vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 0, 0))
+ return (error);
+ /*
+ * We do not want to really close the device if it
+ * is still in use unless we are trying to close it
+ * forcibly. Since every use (buffer, vnode, swap, cmap)
+ * holds a reference to the vnode, and because we mark
+ * any other vnodes that alias this device, when the
+ * sum of the reference counts on all the aliased
+ * vnodes descends to one, we are on last close.
+ */
+ if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0)
+ return (0);
+ devclose = bdevsw[major(dev)].d_close;
+ mode = S_IFBLK;
+ break;
+
+ default:
+ panic("spec_close: not special");
+ }
+
+ return ((*devclose)(dev, ap->a_fflag, mode, ap->a_p));
+}
+
+/*
+ * Print out the contents of a special device vnode.
+ */
+int
+spec_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_NON, dev %d, %d\n", major(ap->a_vp->v_rdev),
+ minor(ap->a_vp->v_rdev));
+ return (0);
+}
+
+/*
+ * Return POSIX pathconf information applicable to special devices.
+ */
+int
+spec_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_MAX_CANON:
+ *ap->a_retval = MAX_CANON;
+ return (0);
+ case _PC_MAX_INPUT:
+ *ap->a_retval = MAX_INPUT;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ case _PC_VDISABLE:
+ *ap->a_retval = _POSIX_VDISABLE;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Special device advisory byte-level locks.
+ */
+/* ARGSUSED */
+int
+spec_advlock(ap)
+ struct vop_advlock_args /* {
+ struct vnode *a_vp;
+ caddr_t a_id;
+ int a_op;
+ struct flock *a_fl;
+ int a_flags;
+ } */ *ap;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * Special device failed operation
+ */
+int
+spec_ebadf()
+{
+
+ return (EBADF);
+}
+
+/*
+ * Special device bad operation
+ */
+int
+spec_badop()
+{
+
+ panic("spec_badop called");
+ /* NOTREACHED */
+}
diff --git a/sys/fs/umapfs/umap.h b/sys/fs/umapfs/umap.h
new file mode 100644
index 0000000..9f4d1e7
--- /dev/null
+++ b/sys/fs/umapfs/umap.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * the UCLA Ficus project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)umap.h 8.3 (Berkeley) 1/21/94
+ *
+ * @(#)null_vnops.c 1.5 (Berkeley) 7/10/92
+ */
+
+#define MAPFILEENTRIES 64
+#define GMAPFILEENTRIES 16
+#define NOBODY 32767
+#define NULLGROUP 65534
+
+struct umap_args {
+ char *target; /* Target of loopback */
+ int nentries; /* # of entries in user map array */
+ int gnentries; /* # of entries in group map array */
+ u_long (*mapdata)[2]; /* pointer to array of user mappings */
+ u_long (*gmapdata)[2]; /* pointer to array of group mappings */
+};
+
+struct umap_mount {
+ struct mount *umapm_vfs;
+ struct vnode *umapm_rootvp; /* Reference to root umap_node */
+ int info_nentries; /* number of uid mappings */
+ int info_gnentries; /* number of gid mappings */
+ u_long info_mapdata[MAPFILEENTRIES][2]; /* mapping data for
+ user mapping in ficus */
+ u_long info_gmapdata[GMAPFILEENTRIES][2]; /*mapping data for
+ group mapping in ficus */
+};
+
+#ifdef KERNEL
+/*
+ * A cache of vnode references
+ */
+struct umap_node {
+ struct umap_node *umap_forw; /* Hash chain */
+ struct umap_node *umap_back;
+ struct vnode *umap_lowervp; /* Aliased vnode - VREFed once */
+ struct vnode *umap_vnode; /* Back pointer to vnode/umap_node */
+};
+
+extern int umap_node_create __P((struct mount *mp, struct vnode *target, struct vnode **vpp));
+extern u_long umap_reverse_findid __P((u_long id, u_long map[][2], int nentries));
+extern void umap_mapids __P((struct mount *v_mount, struct ucred *credp));
+
+#define MOUNTTOUMAPMOUNT(mp) ((struct umap_mount *)((mp)->mnt_data))
+#define VTOUMAP(vp) ((struct umap_node *)(vp)->v_data)
+#define UMAPTOV(xp) ((xp)->umap_vnode)
+#ifdef UMAPFS_DIAGNOSTIC
+extern struct vnode *umap_checkvp __P((struct vnode *vp, char *fil, int lno));
+#define UMAPVPTOLOWERVP(vp) umap_checkvp((vp), __FILE__, __LINE__)
+#else
+#define UMAPVPTOLOWERVP(vp) (VTOUMAP(vp)->umap_lowervp)
+#endif
+
+extern int (**umap_vnodeop_p)();
+extern struct vfsops umap_vfsops;
+#endif /* KERNEL */
diff --git a/sys/fs/umapfs/umap_subr.c b/sys/fs/umapfs/umap_subr.c
new file mode 100644
index 0000000..b640891
--- /dev/null
+++ b/sys/fs/umapfs/umap_subr.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)umap_subr.c 8.6 (Berkeley) 1/26/94
+ *
+ * $Id: lofs_subr.c, v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <miscfs/umapfs/umap.h>
+
+#define LOG2_SIZEVNODE 7 /* log2(sizeof struct vnode) */
+#define NUMAPNODECACHE 16
+#define UMAP_NHASH(vp) ((((u_long) vp)>>LOG2_SIZEVNODE) & (NUMAPNODECACHE-1))
+
+/*
+ * Null layer cache:
+ * Each cache entry holds a reference to the target vnode
+ * along with a pointer to the alias vnode. When an
+ * entry is added the target vnode is VREF'd. When the
+ * alias is removed the target vnode is vrele'd.
+ */
+
+/*
+ * Cache head
+ */
+struct umap_node_cache {
+ struct umap_node *ac_forw;
+ struct umap_node *ac_back;
+};
+
+static struct umap_node_cache umap_node_cache[NUMAPNODECACHE];
+
+/*
+ * Initialise cache headers
+ */
+int
+umapfs_init()
+{
+ struct umap_node_cache *ac;
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_init\n"); /* printed during system boot */
+#endif
+
+ for (ac = umap_node_cache; ac < umap_node_cache + NUMAPNODECACHE; ac++)
+ ac->ac_forw = ac->ac_back = (struct umap_node *) ac;
+ return (0);
+}
+
+/*
+ * Compute hash list for given target vnode
+ */
+static struct umap_node_cache *
+umap_node_hash(targetvp)
+ struct vnode *targetvp;
+{
+
+ return (&umap_node_cache[UMAP_NHASH(targetvp)]);
+}
+
+/*
+ * umap_findid is called by various routines in umap_vnodeops.c to
+ * find a user or group id in a map.
+ */
+static u_long
+umap_findid(id, map, nentries)
+ u_long id;
+ u_long map[][2];
+ int nentries;
+{
+ int i;
+
+ /* Find uid entry in map */
+ i = 0;
+ while ((i<nentries) && ((map[i][0]) != id))
+ i++;
+
+ if (i < nentries)
+ return (map[i][1]);
+ else
+ return (-1);
+
+}
+
+/*
+ * umap_reverse_findid is called by umap_getattr() in umap_vnodeops.c to
+ * find a user or group id in a map, in reverse.
+ */
+u_long
+umap_reverse_findid(id, map, nentries)
+ u_long id;
+ u_long map[][2];
+ int nentries;
+{
+ int i;
+
+ /* Find uid entry in map */
+ i = 0;
+ while ((i<nentries) && ((map[i][1]) != id))
+ i++;
+
+ if (i < nentries)
+ return (map[i][0]);
+ else
+ return (-1);
+
+}
+
+/*
+ * Return alias for target vnode if already exists, else 0.
+ */
+static struct vnode *
+umap_node_find(mp, targetvp)
+ struct mount *mp;
+ struct vnode *targetvp;
+{
+ struct umap_node_cache *hd;
+ struct umap_node *a;
+ struct vnode *vp;
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umap_node_find(mp = %x, target = %x)\n", mp, targetvp);
+#endif
+
+ /*
+ * Find hash base, and then search the (two-way) linked
+ * list looking for a umap_node structure which is referencing
+ * the target vnode. If found, the increment the umap_node
+ * reference count (but NOT the target vnode's VREF counter).
+ */
+ hd = umap_node_hash(targetvp);
+
+ loop:
+ for (a = hd->ac_forw; a != (struct umap_node *) hd; a = a->umap_forw) {
+ if (a->umap_lowervp == targetvp &&
+ a->umap_vnode->v_mount == mp) {
+ vp = UMAPTOV(a);
+ /*
+ * We need vget for the VXLOCK
+ * stuff, but we don't want to lock
+ * the lower node.
+ */
+ if (vget(vp, 0)) {
+#ifdef UMAPFS_DIAGNOSTIC
+ printf ("umap_node_find: vget failed.\n");
+#endif
+ goto loop;
+ }
+ return (vp);
+ }
+ }
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umap_node_find(%x, %x): NOT found\n", mp, targetvp);
+#endif
+
+ return (0);
+}
+
+/*
+ * Make a new umap_node node.
+ * Vp is the alias vnode, lofsvp is the target vnode.
+ * Maintain a reference to (targetvp).
+ */
+static int
+umap_node_alloc(mp, lowervp, vpp)
+ struct mount *mp;
+ struct vnode *lowervp;
+ struct vnode **vpp;
+{
+ struct umap_node_cache *hd;
+ struct umap_node *xp;
+ struct vnode *othervp, *vp;
+ int error;
+
+ if (error = getnewvnode(VT_UMAP, mp, umap_vnodeop_p, vpp))
+ return (error);
+ vp = *vpp;
+
+ MALLOC(xp, struct umap_node *, sizeof(struct umap_node),
+ M_TEMP, M_WAITOK);
+ vp->v_type = lowervp->v_type;
+ xp->umap_vnode = vp;
+ vp->v_data = xp;
+ xp->umap_lowervp = lowervp;
+ /*
+ * Before we insert our new node onto the hash chains,
+ * check to see if someone else has beaten us to it.
+ * (We could have slept in MALLOC.)
+ */
+ if (othervp = umap_node_find(lowervp)) {
+ FREE(xp, M_TEMP);
+ vp->v_type = VBAD; /* node is discarded */
+ vp->v_usecount = 0; /* XXX */
+ *vpp = othervp;
+ return (0);
+ }
+ VREF(lowervp); /* Extra VREF will be vrele'd in umap_node_create */
+ hd = umap_node_hash(lowervp);
+ insque(xp, hd);
+ return (0);
+}
+
+
+/*
+ * Try to find an existing umap_node vnode refering
+ * to it, otherwise make a new umap_node vnode which
+ * contains a reference to the target vnode.
+ */
+int
+umap_node_create(mp, targetvp, newvpp)
+ struct mount *mp;
+ struct vnode *targetvp;
+ struct vnode **newvpp;
+{
+ struct vnode *aliasvp;
+
+ if (aliasvp = umap_node_find(mp, targetvp)) {
+ /*
+ * Take another reference to the alias vnode
+ */
+#ifdef UMAPFS_DIAGNOSTIC
+ vprint("umap_node_create: exists", ap->umap_vnode);
+#endif
+ /* VREF(aliasvp); */
+ } else {
+ int error;
+
+ /*
+ * Get new vnode.
+ */
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umap_node_create: create new alias vnode\n");
+#endif
+ /*
+ * Make new vnode reference the umap_node.
+ */
+ if (error = umap_node_alloc(mp, targetvp, &aliasvp))
+ return (error);
+
+ /*
+ * aliasvp is already VREF'd by getnewvnode()
+ */
+ }
+
+ vrele(targetvp);
+
+#ifdef UMAPFS_DIAGNOSTIC
+ vprint("umap_node_create: alias", aliasvp);
+ vprint("umap_node_create: target", targetvp);
+#endif
+
+ *newvpp = aliasvp;
+ return (0);
+}
+
+#ifdef UMAPFS_DIAGNOSTIC
+int umap_checkvp_barrier = 1;
+struct vnode *
+umap_checkvp(vp, fil, lno)
+ struct vnode *vp;
+ char *fil;
+ int lno;
+{
+ struct umap_node *a = VTOUMAP(vp);
+#if 0
+ /*
+ * Can't do this check because vop_reclaim runs
+ * with funny vop vector.
+ */
+ if (vp->v_op != umap_vnodeop_p) {
+ printf ("umap_checkvp: on non-umap-node\n");
+ while (umap_checkvp_barrier) /*WAIT*/ ;
+ panic("umap_checkvp");
+ }
+#endif
+ if (a->umap_lowervp == NULL) {
+ /* Should never happen */
+ int i; u_long *p;
+ printf("vp = %x, ZERO ptr\n", vp);
+ for (p = (u_long *) a, i = 0; i < 8; i++)
+ printf(" %x", p[i]);
+ printf("\n");
+ /* wait for debugger */
+ while (umap_checkvp_barrier) /*WAIT*/ ;
+ panic("umap_checkvp");
+ }
+ if (a->umap_lowervp->v_usecount < 1) {
+ int i; u_long *p;
+ printf("vp = %x, unref'ed lowervp\n", vp);
+ for (p = (u_long *) a, i = 0; i < 8; i++)
+ printf(" %x", p[i]);
+ printf("\n");
+ /* wait for debugger */
+ while (umap_checkvp_barrier) /*WAIT*/ ;
+ panic ("umap with unref'ed lowervp");
+ }
+#if 0
+ printf("umap %x/%d -> %x/%d [%s, %d]\n",
+ a->umap_vnode, a->umap_vnode->v_usecount,
+ a->umap_lowervp, a->umap_lowervp->v_usecount,
+ fil, lno);
+#endif
+ return (a->umap_lowervp);
+}
+#endif
+
+/* umap_mapids maps all of the ids in a credential, both user and group. */
+
+void
+umap_mapids(v_mount, credp)
+ struct mount *v_mount;
+ struct ucred *credp;
+{
+ int i, unentries, gnentries;
+ u_long *groupmap, *usermap;
+ uid_t uid;
+ gid_t gid;
+
+ unentries = MOUNTTOUMAPMOUNT(v_mount)->info_nentries;
+ usermap = &(MOUNTTOUMAPMOUNT(v_mount)->info_mapdata[0][0]);
+ gnentries = MOUNTTOUMAPMOUNT(v_mount)->info_gnentries;
+ groupmap = &(MOUNTTOUMAPMOUNT(v_mount)->info_gmapdata[0][0]);
+
+ /* Find uid entry in map */
+
+ uid = (uid_t) umap_findid(credp->cr_uid, usermap, unentries);
+
+ if (uid != -1)
+ credp->cr_uid = uid;
+ else
+ credp->cr_uid = (uid_t) NOBODY;
+
+#ifdef notdef
+ /* cr_gid is the same as cr_groups[0] in 4BSD */
+
+ /* Find gid entry in map */
+
+ gid = (gid_t) umap_findid(credp->cr_gid, groupmap, gnentries);
+
+ if (gid != -1)
+ credp->cr_gid = gid;
+ else
+ credp->cr_gid = NULLGROUP;
+#endif
+
+ /* Now we must map each of the set of groups in the cr_groups
+ structure. */
+
+ i = 0;
+ while (credp->cr_groups[i] != 0) {
+ gid = (gid_t) umap_findid(credp->cr_groups[i],
+ groupmap, gnentries);
+
+ if (gid != -1)
+ credp->cr_groups[i++] = gid;
+ else
+ credp->cr_groups[i++] = NULLGROUP;
+ }
+}
diff --git a/sys/fs/umapfs/umap_vfsops.c b/sys/fs/umapfs/umap_vfsops.c
new file mode 100644
index 0000000..2480a85
--- /dev/null
+++ b/sys/fs/umapfs/umap_vfsops.c
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * the UCLA Ficus project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)umap_vfsops.c 8.3 (Berkeley) 1/21/94
+ *
+ * @(#)null_vfsops.c 1.5 (Berkeley) 7/10/92
+ */
+
+/*
+ * Umap Layer
+ * (See mount_umap(8) for a description of this layer.)
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <miscfs/umapfs/umap.h>
+
+/*
+ * Mount umap layer
+ */
+int
+umapfs_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ struct umap_args args;
+ struct vnode *lowerrootvp, *vp;
+ struct vnode *umapm_rootvp;
+ struct umap_mount *amp;
+ u_int size;
+ int error;
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_mount(mp = %x)\n", mp);
+#endif
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ return (EOPNOTSUPP);
+ /* return (VFS_MOUNT(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, path, data, ndp, p));*/
+ }
+
+ /*
+ * Get argument
+ */
+ if (error = copyin(data, (caddr_t)&args, sizeof(struct umap_args)))
+ return (error);
+
+ /*
+ * Find lower node
+ */
+ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF,
+ UIO_USERSPACE, args.target, p);
+ if (error = namei(ndp))
+ return (error);
+
+ /*
+ * Sanity check on lower vnode
+ */
+ lowerrootvp = ndp->ni_vp;
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("vp = %x, check for VDIR...\n", lowerrootvp);
+#endif
+ vrele(ndp->ni_dvp);
+ ndp->ni_dvp = 0;
+
+ if (lowerrootvp->v_type != VDIR) {
+ vput(lowerrootvp);
+ return (EINVAL);
+ }
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("mp = %x\n", mp);
+#endif
+
+ amp = (struct umap_mount *) malloc(sizeof(struct umap_mount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+
+ /*
+ * Save reference to underlying FS
+ */
+ amp->umapm_vfs = lowerrootvp->v_mount;
+
+ /*
+ * Now copy in the number of entries and maps for umap mapping.
+ */
+ amp->info_nentries = args.nentries;
+ amp->info_gnentries = args.gnentries;
+ error = copyin(args.mapdata, (caddr_t)amp->info_mapdata,
+ 2*sizeof(u_long)*args.nentries);
+ if (error)
+ return (error);
+
+#ifdef UMAP_DIAGNOSTIC
+ printf("umap_mount:nentries %d\n",args.nentries);
+ for (i = 0; i < args.nentries; i++)
+ printf(" %d maps to %d\n", amp->info_mapdata[i][0],
+ amp->info_mapdata[i][1]);
+#endif
+
+ error = copyin(args.gmapdata, (caddr_t)amp->info_gmapdata,
+ 2*sizeof(u_long)*args.nentries);
+ if (error)
+ return (error);
+
+#ifdef UMAP_DIAGNOSTIC
+ printf("umap_mount:gnentries %d\n",args.gnentries);
+ for (i = 0; i < args.gnentries; i++)
+ printf(" group %d maps to %d\n",
+ amp->info_gmapdata[i][0],
+ amp->info_gmapdata[i][1]);
+#endif
+
+
+ /*
+ * Save reference. Each mount also holds
+ * a reference on the root vnode.
+ */
+ error = umap_node_create(mp, lowerrootvp, &vp);
+ /*
+ * Unlock the node (either the lower or the alias)
+ */
+ VOP_UNLOCK(vp);
+ /*
+ * Make sure the node alias worked
+ */
+ if (error) {
+ vrele(lowerrootvp);
+ free(amp, M_UFSMNT); /* XXX */
+ return (error);
+ }
+
+ /*
+ * Keep a held reference to the root vnode.
+ * It is vrele'd in umapfs_unmount.
+ */
+ umapm_rootvp = vp;
+ umapm_rootvp->v_flag |= VROOT;
+ amp->umapm_rootvp = umapm_rootvp;
+ if (UMAPVPTOLOWERVP(umapm_rootvp)->v_mount->mnt_flag & MNT_LOCAL)
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_data = (qaddr_t) amp;
+ getnewfsid(mp, MOUNT_LOFS);
+
+ (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+ (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_mount: lower %s, alias at %s\n",
+ mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
+#endif
+ return (0);
+}
+
+/*
+ * VFS start. Nothing needed here - the start routine
+ * on the underlying filesystem will have been called
+ * when that filesystem was mounted.
+ */
+int
+umapfs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ return (0);
+ /* return (VFS_START(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, flags, p)); */
+}
+
+/*
+ * Free reference to umap layer
+ */
+int
+umapfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ struct vnode *umapm_rootvp = MOUNTTOUMAPMOUNT(mp)->umapm_rootvp;
+ int error;
+ int flags = 0;
+ extern int doforce;
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_unmount(mp = %x)\n", mp);
+#endif
+
+ if (mntflags & MNT_FORCE) {
+ /* lofs can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ /*
+ * Clear out buffer cache. I don't think we
+ * ever get anything cached at this level at the
+ * moment, but who knows...
+ */
+#ifdef notyet
+ mntflushbuf(mp, 0);
+ if (mntinvalbuf(mp, 1))
+ return (EBUSY);
+#endif
+ if (umapm_rootvp->v_usecount > 1)
+ return (EBUSY);
+ if (error = vflush(mp, umapm_rootvp, flags))
+ return (error);
+
+#ifdef UMAPFS_DIAGNOSTIC
+ vprint("alias root of lower", umapm_rootvp);
+#endif
+ /*
+ * Release reference on underlying root vnode
+ */
+ vrele(umapm_rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(umapm_rootvp);
+ /*
+ * Finally, throw away the umap_mount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ mp->mnt_data = 0;
+ return (0);
+}
+
+int
+umapfs_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct vnode *vp;
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_root(mp = %x, vp = %x->%x)\n", mp,
+ MOUNTTOUMAPMOUNT(mp)->umapm_rootvp,
+ UMAPVPTOLOWERVP(MOUNTTOUMAPMOUNT(mp)->umapm_rootvp)
+ );
+#endif
+
+ /*
+ * Return locked reference to root.
+ */
+ vp = MOUNTTOUMAPMOUNT(mp)->umapm_rootvp;
+ VREF(vp);
+ VOP_LOCK(vp);
+ *vpp = vp;
+ return (0);
+}
+
+int
+umapfs_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+ return (VFS_QUOTACTL(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, cmd, uid, arg, p));
+}
+
+int
+umapfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ int error;
+ struct statfs mstat;
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_statfs(mp = %x, vp = %x->%x)\n", mp,
+ MOUNTTOUMAPMOUNT(mp)->umapm_rootvp,
+ UMAPVPTOLOWERVP(MOUNTTOUMAPMOUNT(mp)->umapm_rootvp)
+ );
+#endif
+
+ bzero(&mstat, sizeof(mstat));
+
+ error = VFS_STATFS(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, &mstat, p);
+ if (error)
+ return (error);
+
+ /* now copy across the "interesting" information and fake the rest */
+ sbp->f_type = mstat.f_type;
+ sbp->f_flags = mstat.f_flags;
+ sbp->f_bsize = mstat.f_bsize;
+ sbp->f_iosize = mstat.f_iosize;
+ sbp->f_blocks = mstat.f_blocks;
+ sbp->f_bfree = mstat.f_bfree;
+ sbp->f_bavail = mstat.f_bavail;
+ sbp->f_files = mstat.f_files;
+ sbp->f_ffree = mstat.f_ffree;
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+ return (0);
+}
+
+int
+umapfs_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+ /*
+ * XXX - Assumes no data cached at umap layer.
+ */
+ return (0);
+}
+
+int
+umapfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (VFS_VGET(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, ino, vpp));
+}
+
+int
+umapfs_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
+ struct mount *mp;
+ struct fid *fidp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred**credanonp;
+{
+
+ return (VFS_FHTOVP(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, fidp, nam, vpp, exflagsp,credanonp));
+}
+
+int
+umapfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+ return (VFS_VPTOFH(UMAPVPTOLOWERVP(vp), fhp));
+}
+
+int umapfs_init __P((void));
+
+struct vfsops umap_vfsops = {
+ umapfs_mount,
+ umapfs_start,
+ umapfs_unmount,
+ umapfs_root,
+ umapfs_quotactl,
+ umapfs_statfs,
+ umapfs_sync,
+ umapfs_vget,
+ umapfs_fhtovp,
+ umapfs_vptofh,
+ umapfs_init,
+};
diff --git a/sys/fs/umapfs/umap_vnops.c b/sys/fs/umapfs/umap_vnops.c
new file mode 100644
index 0000000..0c1955f
--- /dev/null
+++ b/sys/fs/umapfs/umap_vnops.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * the UCLA Ficus project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)umap_vnops.c 8.3 (Berkeley) 1/5/94
+ */
+
+/*
+ * Umap Layer
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <miscfs/umapfs/umap.h>
+
+
+int umap_bug_bypass = 0; /* for debugging: enables bypass printf'ing */
+
+/*
+ * This is the 10-Apr-92 bypass routine.
+ * See null_vnops.c:null_bypass for more details.
+ */
+int
+umap_bypass(ap)
+ struct vop_generic_args /* {
+ struct vnodeop_desc *a_desc;
+ <other random data follows, presumably>
+ } */ *ap;
+{
+ extern int (**umap_vnodeop_p)(); /* not extern, really "forward" */
+ struct ucred **credpp = 0, *credp = 0;
+ struct ucred *savecredp = 0, *savecompcredp = 0;
+ struct ucred *compcredp = 0;
+ struct vnode **this_vp_p;
+ int error;
+ struct vnode *old_vps[VDESC_MAX_VPS];
+ struct vnode *vp1 = 0;
+ struct vnode **vps_p[VDESC_MAX_VPS];
+ struct vnode ***vppp;
+ struct vnodeop_desc *descp = ap->a_desc;
+ int reles, i;
+ struct componentname **compnamepp = 0;
+
+ if (umap_bug_bypass)
+ printf ("umap_bypass: %s\n", descp->vdesc_name);
+
+#ifdef SAFETY
+ /*
+ * We require at least one vp.
+ */
+ if (descp->vdesc_vp_offsets == NULL ||
+ descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET)
+ panic ("umap_bypass: no vp's in map.\n");
+#endif
+
+ /*
+ * Map the vnodes going in.
+ * Later, we'll invoke the operation based on
+ * the first mapped vnode's operation vector.
+ */
+ reles = descp->vdesc_flags;
+ for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
+ if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
+ break; /* bail out at end of list */
+ vps_p[i] = this_vp_p =
+ VOPARG_OFFSETTO(struct vnode**, descp->vdesc_vp_offsets[i], ap);
+
+ if (i == 0) {
+ vp1 = *vps_p[0];
+ }
+
+ /*
+ * We're not guaranteed that any but the first vnode
+ * are of our type. Check for and don't map any
+ * that aren't. (Must map first vp or vclean fails.)
+ */
+
+ if (i && (*this_vp_p)->v_op != umap_vnodeop_p) {
+ old_vps[i] = NULL;
+ } else {
+ old_vps[i] = *this_vp_p;
+ *(vps_p[i]) = UMAPVPTOLOWERVP(*this_vp_p);
+ if (reles & 1)
+ VREF(*this_vp_p);
+ }
+
+ }
+
+ /*
+ * Fix the credentials. (That's the purpose of this layer.)
+ */
+
+ if (descp->vdesc_cred_offset != VDESC_NO_OFFSET) {
+
+ credpp = VOPARG_OFFSETTO(struct ucred**,
+ descp->vdesc_cred_offset, ap);
+
+ /* Save old values */
+
+ savecredp = (*credpp);
+ (*credpp) = crdup(savecredp);
+ credp = *credpp;
+
+ if (umap_bug_bypass && credp->cr_uid != 0)
+ printf("umap_bypass: user was %d, group %d\n",
+ credp->cr_uid, credp->cr_gid);
+
+ /* Map all ids in the credential structure. */
+
+ umap_mapids(vp1->v_mount, credp);
+
+ if (umap_bug_bypass && credp->cr_uid != 0)
+ printf("umap_bypass: user now %d, group %d\n",
+ credp->cr_uid, credp->cr_gid);
+ }
+
+ /* BSD often keeps a credential in the componentname structure
+ * for speed. If there is one, it better get mapped, too.
+ */
+
+ if (descp->vdesc_componentname_offset != VDESC_NO_OFFSET) {
+
+ compnamepp = VOPARG_OFFSETTO(struct componentname**,
+ descp->vdesc_componentname_offset, ap);
+
+ compcredp = (*compnamepp)->cn_cred;
+ savecompcredp = compcredp;
+ compcredp = (*compnamepp)->cn_cred = crdup(savecompcredp);
+
+ if (umap_bug_bypass && compcredp->cr_uid != 0)
+ printf("umap_bypass: component credit user was %d, group %d\n",
+ compcredp->cr_uid, compcredp->cr_gid);
+
+ /* Map all ids in the credential structure. */
+
+ umap_mapids(vp1->v_mount, compcredp);
+
+ if (umap_bug_bypass && compcredp->cr_uid != 0)
+ printf("umap_bypass: component credit user now %d, group %d\n",
+ compcredp->cr_uid, compcredp->cr_gid);
+ }
+
+ /*
+ * Call the operation on the lower layer
+ * with the modified argument structure.
+ */
+ error = VCALL(*(vps_p[0]), descp->vdesc_offset, ap);
+
+ /*
+ * Maintain the illusion of call-by-value
+ * by restoring vnodes in the argument structure
+ * to their original value.
+ */
+ reles = descp->vdesc_flags;
+ for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
+ if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
+ break; /* bail out at end of list */
+ if (old_vps[i]) {
+ *(vps_p[i]) = old_vps[i];
+ if (reles & 1)
+ vrele(*(vps_p[i]));
+ };
+ };
+
+ /*
+ * Map the possible out-going vpp
+ * (Assumes that the lower layer always returns
+ * a VREF'ed vpp unless it gets an error.)
+ */
+ if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET &&
+ !(descp->vdesc_flags & VDESC_NOMAP_VPP) &&
+ !error) {
+ if (descp->vdesc_flags & VDESC_VPP_WILLRELE)
+ goto out;
+ vppp = VOPARG_OFFSETTO(struct vnode***,
+ descp->vdesc_vpp_offset, ap);
+ error = umap_node_create(old_vps[0]->v_mount, **vppp, *vppp);
+ };
+
+ out:
+ /*
+ * Free duplicate cred structure and restore old one.
+ */
+ if (descp->vdesc_cred_offset != VDESC_NO_OFFSET) {
+ if (umap_bug_bypass && credp && credp->cr_uid != 0)
+ printf("umap_bypass: returning-user was %d\n",
+ credp->cr_uid);
+
+ crfree(credp);
+ (*credpp) = savecredp;
+ if (umap_bug_bypass && credpp && (*credpp)->cr_uid != 0)
+ printf("umap_bypass: returning-user now %d\n\n",
+ (*credpp)->cr_uid);
+ }
+
+ if (descp->vdesc_componentname_offset != VDESC_NO_OFFSET) {
+ if (umap_bug_bypass && compcredp && compcredp->cr_uid != 0)
+ printf("umap_bypass: returning-component-user was %d\n",
+ compcredp->cr_uid);
+
+ crfree(compcredp);
+ (*compnamepp)->cn_cred = savecompcredp;
+ if (umap_bug_bypass && credpp && (*credpp)->cr_uid != 0)
+ printf("umap_bypass: returning-component-user now %d\n",
+ compcredp->cr_uid);
+ }
+
+ return (error);
+}
+
+
+/*
+ * We handle getattr to change the fsid.
+ */
+int
+umap_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ short uid, gid;
+ int error, tmpid, nentries, gnentries;
+ u_long (*mapdata)[2], (*gmapdata)[2];
+ struct vnode **vp1p;
+ struct vnodeop_desc *descp = ap->a_desc;
+
+ if (error = umap_bypass(ap))
+ return (error);
+ /* Requires that arguments be restored. */
+ ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
+
+ /*
+ * Umap needs to map the uid and gid returned by a stat
+ * into the proper values for this site. This involves
+ * finding the returned uid in the mapping information,
+ * translating it into the uid on the other end,
+ * and filling in the proper field in the vattr
+ * structure pointed to by ap->a_vap. The group
+ * is easier, since currently all groups will be
+ * translate to the NULLGROUP.
+ */
+
+ /* Find entry in map */
+
+ uid = ap->a_vap->va_uid;
+ gid = ap->a_vap->va_gid;
+ if (umap_bug_bypass)
+ printf("umap_getattr: mapped uid = %d, mapped gid = %d\n", uid,
+ gid);
+
+ vp1p = VOPARG_OFFSETTO(struct vnode**, descp->vdesc_vp_offsets[0], ap);
+ nentries = MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_nentries;
+ mapdata = (MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_mapdata);
+ gnentries = MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_gnentries;
+ gmapdata = (MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_gmapdata);
+
+ /* Reverse map the uid for the vnode. Since it's a reverse
+ map, we can't use umap_mapids() to do it. */
+
+ tmpid = umap_reverse_findid(uid, mapdata, nentries);
+
+ if (tmpid != -1) {
+
+ ap->a_vap->va_uid = (uid_t) tmpid;
+ if (umap_bug_bypass)
+ printf("umap_getattr: original uid = %d\n", uid);
+ } else
+ ap->a_vap->va_uid = (uid_t) NOBODY;
+
+ /* Reverse map the gid for the vnode. */
+
+ tmpid = umap_reverse_findid(gid, gmapdata, gnentries);
+
+ if (tmpid != -1) {
+
+ ap->a_vap->va_gid = (gid_t) tmpid;
+ if (umap_bug_bypass)
+ printf("umap_getattr: original gid = %d\n", gid);
+ } else
+ ap->a_vap->va_gid = (gid_t) NULLGROUP;
+
+ return (0);
+}
+
+int
+umap_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ /*
+ * Do nothing (and _don't_ bypass).
+ * Wait to vrele lowervp until reclaim,
+ * so that until then our umap_node is in the
+ * cache and reusable.
+ *
+ */
+ return (0);
+}
+
+int
+umap_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct umap_node *xp = VTOUMAP(vp);
+ struct vnode *lowervp = xp->umap_lowervp;
+
+ /* After this assignment, this node will not be re-used. */
+ xp->umap_lowervp = NULL;
+ remque(xp);
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = NULL;
+ vrele(lowervp);
+ return (0);
+}
+
+int
+umap_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ int error;
+ struct vnode *savedvp;
+
+ savedvp = bp->b_vp;
+ bp->b_vp = UMAPVPTOLOWERVP(bp->b_vp);
+
+ error = VOP_STRATEGY(ap->a_bp);
+
+ bp->b_vp = savedvp;
+
+ return (error);
+}
+
+int
+umap_bwrite(ap)
+ struct vop_bwrite_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ int error;
+ struct vnode *savedvp;
+
+ savedvp = bp->b_vp;
+ bp->b_vp = UMAPVPTOLOWERVP(bp->b_vp);
+
+ error = VOP_BWRITE(ap->a_bp);
+
+ bp->b_vp = savedvp;
+
+ return (error);
+}
+
+
+int
+umap_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ printf("\ttag VT_UMAPFS, vp=%x, lowervp=%x\n", vp, UMAPVPTOLOWERVP(vp));
+ return (0);
+}
+
+int
+umap_rename(ap)
+ struct vop_rename_args /* {
+ struct vnode *a_fdvp;
+ struct vnode *a_fvp;
+ struct componentname *a_fcnp;
+ struct vnode *a_tdvp;
+ struct vnode *a_tvp;
+ struct componentname *a_tcnp;
+ } */ *ap;
+{
+ int error;
+ struct componentname *compnamep;
+ struct ucred *compcredp, *savecompcredp;
+ struct vnode *vp;
+
+ /*
+ * Rename is irregular, having two componentname structures.
+ * We need to map the cre in the second structure,
+ * and then bypass takes care of the rest.
+ */
+
+ vp = ap->a_fdvp;
+ compnamep = ap->a_tcnp;
+ compcredp = compnamep->cn_cred;
+
+ savecompcredp = compcredp;
+ compcredp = compnamep->cn_cred = crdup(savecompcredp);
+
+ if (umap_bug_bypass && compcredp->cr_uid != 0)
+ printf("umap_rename: rename component credit user was %d, group %d\n",
+ compcredp->cr_uid, compcredp->cr_gid);
+
+ /* Map all ids in the credential structure. */
+
+ umap_mapids(vp->v_mount, compcredp);
+
+ if (umap_bug_bypass && compcredp->cr_uid != 0)
+ printf("umap_rename: rename component credit user now %d, group %d\n",
+ compcredp->cr_uid, compcredp->cr_gid);
+
+ error = umap_bypass(ap);
+
+ /* Restore the additional mapped componentname cred structure. */
+
+ crfree(compcredp);
+ compnamep->cn_cred = savecompcredp;
+
+ return error;
+}
+
+/*
+ * Global vfs data structures
+ */
+/*
+ * XXX - strategy, bwrite are hand coded currently. They should
+ * go away with a merged buffer/block cache.
+ *
+ */
+int (**umap_vnodeop_p)();
+struct vnodeopv_entry_desc umap_vnodeop_entries[] = {
+ { &vop_default_desc, umap_bypass },
+
+ { &vop_getattr_desc, umap_getattr },
+ { &vop_inactive_desc, umap_inactive },
+ { &vop_reclaim_desc, umap_reclaim },
+ { &vop_print_desc, umap_print },
+ { &vop_rename_desc, umap_rename },
+
+ { &vop_strategy_desc, umap_strategy },
+ { &vop_bwrite_desc, umap_bwrite },
+
+ { (struct vnodeop_desc*) NULL, (int(*)()) NULL }
+};
+struct vnodeopv_desc umap_vnodeop_opv_desc =
+ { &umap_vnodeop_p, umap_vnodeop_entries };
diff --git a/sys/fs/unionfs/union.h b/sys/fs/unionfs/union.h
new file mode 100644
index 0000000..463218a
--- /dev/null
+++ b/sys/fs/unionfs/union.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1994 The Regents of the University of California.
+ * Copyright (c) 1994 Jan-Simon Pendry.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)union.h 8.2 (Berkeley) 2/17/94
+ */
+
+struct union_args {
+ char *target; /* Target of loopback */
+ int mntflags; /* Options on the mount */
+};
+
+#define UNMNT_ABOVE 0x0001 /* Target appears below mount point */
+#define UNMNT_BELOW 0x0002 /* Target appears below mount point */
+#define UNMNT_REPLACE 0x0003 /* Target replaces mount point */
+#define UNMNT_OPMASK 0x0003
+
+struct union_mount {
+ struct vnode *um_uppervp;
+ struct vnode *um_lowervp;
+ struct ucred *um_cred; /* Credentials of user calling mount */
+ int um_cmode; /* cmask from mount process */
+ int um_op; /* Operation mode */
+};
+
+#ifdef KERNEL
+
+/*
+ * DEFDIRMODE is the mode bits used to create a shadow directory.
+ */
+#define VRWXMODE (VREAD|VWRITE|VEXEC)
+#define VRWMODE (VREAD|VWRITE)
+#define UN_DIRMODE ((VRWXMODE)|(VRWXMODE>>3)|(VRWXMODE>>6))
+#define UN_FILEMODE ((VRWMODE)|(VRWMODE>>3)|(VRWMODE>>6))
+
+/*
+ * A cache of vnode references
+ */
+struct union_node {
+ LIST_ENTRY(union_node) un_cache; /* Hash chain */
+ struct vnode *un_vnode; /* Back pointer */
+ struct vnode *un_uppervp; /* overlaying object */
+ struct vnode *un_lowervp; /* underlying object */
+ struct vnode *un_dirvp; /* Parent dir of uppervp */
+ char *un_path; /* saved component name */
+ int un_hash; /* saved un_path hash value */
+ int un_openl; /* # of opens on lowervp */
+ int un_flags;
+#ifdef DIAGNOSTIC
+ pid_t un_pid;
+#endif
+};
+
+#define UN_WANT 0x01
+#define UN_LOCKED 0x02
+#define UN_ULOCK 0x04 /* Upper node is locked */
+#define UN_KLOCK 0x08 /* Keep upper node locked on vput */
+
+extern int union_allocvp __P((struct vnode **, struct mount *,
+ struct vnode *, struct vnode *,
+ struct componentname *, struct vnode *,
+ struct vnode *));
+extern int union_copyfile __P((struct proc *, struct ucred *,
+ struct vnode *, struct vnode *));
+extern int union_mkshadow __P((struct union_mount *, struct vnode *,
+ struct componentname *, struct vnode **));
+extern int union_vn_create __P((struct vnode **, struct union_node *,
+ struct proc *));
+extern int union_cn_close __P((struct vnode *, int, struct ucred *,
+ struct proc *));
+extern void union_removed_upper __P((struct union_node *un));
+extern struct vnode *union_lowervp __P((struct vnode *));
+extern void union_newlower __P((struct union_node *, struct vnode *));
+extern void union_newupper __P((struct union_node *, struct vnode *));
+
+#define MOUNTTOUNIONMOUNT(mp) ((struct union_mount *)((mp)->mnt_data))
+#define VTOUNION(vp) ((struct union_node *)(vp)->v_data)
+#define UNIONTOV(un) ((un)->un_vnode)
+#define LOWERVP(vp) (VTOUNION(vp)->un_lowervp)
+#define UPPERVP(vp) (VTOUNION(vp)->un_uppervp)
+#define OTHERVP(vp) (UPPERVP(vp) ? UPPERVP(vp) : LOWERVP(vp))
+
+extern int (**union_vnodeop_p)();
+extern struct vfsops union_vfsops;
+#endif /* KERNEL */
diff --git a/sys/fs/unionfs/union_subr.c b/sys/fs/unionfs/union_subr.c
new file mode 100644
index 0000000..ea4f804
--- /dev/null
+++ b/sys/fs/unionfs/union_subr.c
@@ -0,0 +1,743 @@
+/*
+ * Copyright (c) 1994 Jan-Simon Pendry
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)union_subr.c 8.4 (Berkeley) 2/17/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/queue.h>
+#include <miscfs/union/union.h>
+
+#include <sys/proc.h>
+
+/* must be power of two, otherwise change UNION_HASH() */
+#define NHASH 32
+
+/* unsigned int ... */
+#define UNION_HASH(u, l) \
+ (((((unsigned long) (u)) + ((unsigned long) l)) >> 8) & (NHASH-1))
+
+static LIST_HEAD(unhead, union_node) unhead[NHASH];
+static int unvplock[NHASH];
+
+int
+union_init()
+{
+ int i;
+
+ for (i = 0; i < NHASH; i++)
+ LIST_INIT(&unhead[i]);
+ bzero((caddr_t) unvplock, sizeof(unvplock));
+ return (0);
+}
+
+static int
+union_list_lock(ix)
+ int ix;
+{
+
+ if (unvplock[ix] & UN_LOCKED) {
+ unvplock[ix] |= UN_WANT;
+ sleep((caddr_t) &unvplock[ix], PINOD);
+ return (1);
+ }
+
+ unvplock[ix] |= UN_LOCKED;
+
+ return (0);
+}
+
+static void
+union_list_unlock(ix)
+ int ix;
+{
+
+ unvplock[ix] &= ~UN_LOCKED;
+
+ if (unvplock[ix] & UN_WANT) {
+ unvplock[ix] &= ~UN_WANT;
+ wakeup((caddr_t) &unvplock[ix]);
+ }
+}
+
+void
+union_updatevp(un, uppervp, lowervp)
+ struct union_node *un;
+ struct vnode *uppervp;
+ struct vnode *lowervp;
+{
+ int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp);
+ int nhash = UNION_HASH(uppervp, lowervp);
+
+ if (ohash != nhash) {
+ /*
+ * Ensure locking is ordered from lower to higher
+ * to avoid deadlocks.
+ */
+ if (nhash < ohash) {
+ int t = ohash;
+ ohash = nhash;
+ nhash = t;
+ }
+
+ while (union_list_lock(ohash))
+ continue;
+
+ while (union_list_lock(nhash))
+ continue;
+
+ LIST_REMOVE(un, un_cache);
+ union_list_unlock(ohash);
+ } else {
+ while (union_list_lock(nhash))
+ continue;
+ }
+
+ if (un->un_lowervp != lowervp) {
+ if (un->un_lowervp) {
+ vrele(un->un_lowervp);
+ if (un->un_path) {
+ free(un->un_path, M_TEMP);
+ un->un_path = 0;
+ }
+ if (un->un_dirvp) {
+ vrele(un->un_dirvp);
+ un->un_dirvp = NULLVP;
+ }
+ }
+ un->un_lowervp = lowervp;
+ }
+
+ if (un->un_uppervp != uppervp) {
+ if (un->un_uppervp)
+ vrele(un->un_uppervp);
+
+ un->un_uppervp = uppervp;
+ }
+
+ if (ohash != nhash)
+ LIST_INSERT_HEAD(&unhead[nhash], un, un_cache);
+
+ union_list_unlock(nhash);
+}
+
+void
+union_newlower(un, lowervp)
+ struct union_node *un;
+ struct vnode *lowervp;
+{
+
+ union_updatevp(un, un->un_uppervp, lowervp);
+}
+
+void
+union_newupper(un, uppervp)
+ struct union_node *un;
+ struct vnode *uppervp;
+{
+
+ union_updatevp(un, uppervp, un->un_lowervp);
+}
+
+/*
+ * allocate a union_node/vnode pair. the vnode is
+ * referenced and locked. the new vnode is returned
+ * via (vpp). (mp) is the mountpoint of the union filesystem,
+ * (dvp) is the parent directory where the upper layer object
+ * should exist (but doesn't) and (cnp) is the componentname
+ * information which is partially copied to allow the upper
+ * layer object to be created at a later time. (uppervp)
+ * and (lowervp) reference the upper and lower layer objects
+ * being mapped. either, but not both, can be nil.
+ * if supplied, (uppervp) is locked.
+ * the reference is either maintained in the new union_node
+ * object which is allocated, or they are vrele'd.
+ *
+ * all union_nodes are maintained on a singly-linked
+ * list. new nodes are only allocated when they cannot
+ * be found on this list. entries on the list are
+ * removed when the vfs reclaim entry is called.
+ *
+ * a single lock is kept for the entire list. this is
+ * needed because the getnewvnode() function can block
+ * waiting for a vnode to become free, in which case there
+ * may be more than one process trying to get the same
+ * vnode. this lock is only taken if we are going to
+ * call getnewvnode, since the kernel itself is single-threaded.
+ *
+ * if an entry is found on the list, then call vget() to
+ * take a reference. this is done because there may be
+ * zero references to it and so it needs to removed from
+ * the vnode free list.
+ */
+int
+union_allocvp(vpp, mp, undvp, dvp, cnp, uppervp, lowervp)
+ struct vnode **vpp;
+ struct mount *mp;
+ struct vnode *undvp;
+ struct vnode *dvp; /* may be null */
+ struct componentname *cnp; /* may be null */
+ struct vnode *uppervp; /* may be null */
+ struct vnode *lowervp; /* may be null */
+{
+ int error;
+ struct union_node *un = 0;
+ struct union_node **pp;
+ struct vnode *xlowervp = NULLVP;
+ int hash = 0;
+ int try;
+
+ if (uppervp == NULLVP && lowervp == NULLVP)
+ panic("union: unidentifiable allocation");
+
+ if (uppervp && lowervp && (uppervp->v_type != lowervp->v_type)) {
+ xlowervp = lowervp;
+ lowervp = NULLVP;
+ }
+
+loop:
+ for (try = 0; try < 3; try++) {
+ switch (try) {
+ case 0:
+ if (lowervp == NULLVP)
+ continue;
+ hash = UNION_HASH(uppervp, lowervp);
+ break;
+
+ case 1:
+ if (uppervp == NULLVP)
+ continue;
+ hash = UNION_HASH(uppervp, NULLVP);
+ break;
+
+ case 2:
+ if (lowervp == NULLVP)
+ continue;
+ hash = UNION_HASH(NULLVP, lowervp);
+ break;
+ }
+
+ while (union_list_lock(hash))
+ continue;
+
+ for (un = unhead[hash].lh_first; un != 0;
+ un = un->un_cache.le_next) {
+ if ((un->un_lowervp == lowervp ||
+ un->un_lowervp == NULLVP) &&
+ (un->un_uppervp == uppervp ||
+ un->un_uppervp == NULLVP) &&
+ (UNIONTOV(un)->v_mount == mp)) {
+ if (vget(UNIONTOV(un), 0)) {
+ union_list_unlock(hash);
+ goto loop;
+ }
+ break;
+ }
+ }
+
+ union_list_unlock(hash);
+
+ if (un)
+ break;
+ }
+
+ if (un) {
+ /*
+ * Obtain a lock on the union_node.
+ * uppervp is locked, though un->un_uppervp
+ * may not be. this doesn't break the locking
+ * hierarchy since in the case that un->un_uppervp
+ * is not yet locked it will be vrele'd and replaced
+ * with uppervp.
+ */
+
+ if ((dvp != NULLVP) && (uppervp == dvp)) {
+ /*
+ * Access ``.'', so (un) will already
+ * be locked. Since this process has
+ * the lock on (uppervp) no other
+ * process can hold the lock on (un).
+ */
+#ifdef DIAGNOSTIC
+ if ((un->un_flags & UN_LOCKED) == 0)
+ panic("union: . not locked");
+ else if (curproc && un->un_pid != curproc->p_pid &&
+ un->un_pid > -1 && curproc->p_pid > -1)
+ panic("union: allocvp not lock owner");
+#endif
+ } else {
+ if (un->un_flags & UN_LOCKED) {
+ vrele(UNIONTOV(un));
+ un->un_flags |= UN_WANT;
+ sleep((caddr_t) &un->un_flags, PINOD);
+ goto loop;
+ }
+ un->un_flags |= UN_LOCKED;
+
+#ifdef DIAGNOSTIC
+ if (curproc)
+ un->un_pid = curproc->p_pid;
+ else
+ un->un_pid = -1;
+#endif
+ }
+
+ /*
+ * At this point, the union_node is locked,
+ * un->un_uppervp may not be locked, and uppervp
+ * is locked or nil.
+ */
+
+ /*
+ * Save information about the upper layer.
+ */
+ if (uppervp != un->un_uppervp) {
+ union_newupper(un, uppervp);
+ } else if (uppervp) {
+ vrele(uppervp);
+ }
+
+ if (un->un_uppervp) {
+ un->un_flags |= UN_ULOCK;
+ un->un_flags &= ~UN_KLOCK;
+ }
+
+ /*
+ * Save information about the lower layer.
+ * This needs to keep track of pathname
+ * and directory information which union_vn_create
+ * might need.
+ */
+ if (lowervp != un->un_lowervp) {
+ union_newlower(un, lowervp);
+ if (cnp && (lowervp != NULLVP) &&
+ (lowervp->v_type == VREG)) {
+ un->un_hash = cnp->cn_hash;
+ un->un_path = malloc(cnp->cn_namelen+1,
+ M_TEMP, M_WAITOK);
+ bcopy(cnp->cn_nameptr, un->un_path,
+ cnp->cn_namelen);
+ un->un_path[cnp->cn_namelen] = '\0';
+ VREF(dvp);
+ un->un_dirvp = dvp;
+ }
+ } else if (lowervp) {
+ vrele(lowervp);
+ }
+ *vpp = UNIONTOV(un);
+ return (0);
+ }
+
+ /*
+ * otherwise lock the vp list while we call getnewvnode
+ * since that can block.
+ */
+ hash = UNION_HASH(uppervp, lowervp);
+
+ if (union_list_lock(hash))
+ goto loop;
+
+ error = getnewvnode(VT_UNION, mp, union_vnodeop_p, vpp);
+ if (error) {
+ if (uppervp) {
+ if (dvp == uppervp)
+ vrele(uppervp);
+ else
+ vput(uppervp);
+ }
+ if (lowervp)
+ vrele(lowervp);
+
+ goto out;
+ }
+
+ MALLOC((*vpp)->v_data, void *, sizeof(struct union_node),
+ M_TEMP, M_WAITOK);
+
+ if (uppervp)
+ (*vpp)->v_type = uppervp->v_type;
+ else
+ (*vpp)->v_type = lowervp->v_type;
+ un = VTOUNION(*vpp);
+ un->un_vnode = *vpp;
+ un->un_uppervp = uppervp;
+ un->un_lowervp = lowervp;
+ un->un_openl = 0;
+ un->un_flags = UN_LOCKED;
+ if (un->un_uppervp)
+ un->un_flags |= UN_ULOCK;
+#ifdef DIAGNOSTIC
+ if (curproc)
+ un->un_pid = curproc->p_pid;
+ else
+ un->un_pid = -1;
+#endif
+ if (cnp && (lowervp != NULLVP) && (lowervp->v_type == VREG)) {
+ un->un_hash = cnp->cn_hash;
+ un->un_path = malloc(cnp->cn_namelen+1, M_TEMP, M_WAITOK);
+ bcopy(cnp->cn_nameptr, un->un_path, cnp->cn_namelen);
+ un->un_path[cnp->cn_namelen] = '\0';
+ VREF(dvp);
+ un->un_dirvp = dvp;
+ } else {
+ un->un_hash = 0;
+ un->un_path = 0;
+ un->un_dirvp = 0;
+ }
+
+ LIST_INSERT_HEAD(&unhead[hash], un, un_cache);
+
+ if (xlowervp)
+ vrele(xlowervp);
+
+out:
+ union_list_unlock(hash);
+
+ return (error);
+}
+
+int
+union_freevp(vp)
+ struct vnode *vp;
+{
+ struct union_node *un = VTOUNION(vp);
+
+ LIST_REMOVE(un, un_cache);
+
+ if (un->un_uppervp)
+ vrele(un->un_uppervp);
+ if (un->un_lowervp)
+ vrele(un->un_lowervp);
+ if (un->un_dirvp)
+ vrele(un->un_dirvp);
+ if (un->un_path)
+ free(un->un_path, M_TEMP);
+
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = 0;
+
+ return (0);
+}
+
+/*
+ * copyfile. copy the vnode (fvp) to the vnode (tvp)
+ * using a sequence of reads and writes. both (fvp)
+ * and (tvp) are locked on entry and exit.
+ */
+int
+union_copyfile(p, cred, fvp, tvp)
+ struct proc *p;
+ struct ucred *cred;
+ struct vnode *fvp;
+ struct vnode *tvp;
+{
+ char *buf;
+ struct uio uio;
+ struct iovec iov;
+ int error = 0;
+
+ /*
+ * strategy:
+ * allocate a buffer of size MAXBSIZE.
+ * loop doing reads and writes, keeping track
+ * of the current uio offset.
+ * give up at the first sign of trouble.
+ */
+
+ uio.uio_procp = p;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_offset = 0;
+
+ VOP_UNLOCK(fvp); /* XXX */
+ LEASE_CHECK(fvp, p, cred, LEASE_READ);
+ VOP_LOCK(fvp); /* XXX */
+ VOP_UNLOCK(tvp); /* XXX */
+ LEASE_CHECK(tvp, p, cred, LEASE_WRITE);
+ VOP_LOCK(tvp); /* XXX */
+
+ buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
+
+ /* ugly loop follows... */
+ do {
+ off_t offset = uio.uio_offset;
+
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ iov.iov_base = buf;
+ iov.iov_len = MAXBSIZE;
+ uio.uio_resid = iov.iov_len;
+ uio.uio_rw = UIO_READ;
+ error = VOP_READ(fvp, &uio, 0, cred);
+
+ if (error == 0) {
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ iov.iov_base = buf;
+ iov.iov_len = MAXBSIZE - uio.uio_resid;
+ uio.uio_offset = offset;
+ uio.uio_rw = UIO_WRITE;
+ uio.uio_resid = iov.iov_len;
+
+ if (uio.uio_resid == 0)
+ break;
+
+ do {
+ error = VOP_WRITE(tvp, &uio, 0, cred);
+ } while ((uio.uio_resid > 0) && (error == 0));
+ }
+
+ } while (error == 0);
+
+ free(buf, M_TEMP);
+ return (error);
+}
+
+/*
+ * Create a shadow directory in the upper layer.
+ * The new vnode is returned locked.
+ *
+ * (um) points to the union mount structure for access to the
+ * the mounting process's credentials.
+ * (dvp) is the directory in which to create the shadow directory.
+ * it is unlocked on entry and exit.
+ * (cnp) is the componentname to be created.
+ * (vpp) is the returned newly created shadow directory, which
+ * is returned locked.
+ */
+int
+union_mkshadow(um, dvp, cnp, vpp)
+ struct union_mount *um;
+ struct vnode *dvp;
+ struct componentname *cnp;
+ struct vnode **vpp;
+{
+ int error;
+ struct vattr va;
+ struct proc *p = cnp->cn_proc;
+ struct componentname cn;
+
+ /*
+ * policy: when creating the shadow directory in the
+ * upper layer, create it owned by the user who did
+ * the mount, group from parent directory, and mode
+ * 777 modified by umask (ie mostly identical to the
+ * mkdir syscall). (jsp, kb)
+ */
+
+ /*
+ * A new componentname structure must be faked up because
+ * there is no way to know where the upper level cnp came
+ * from or what it is being used for. This must duplicate
+ * some of the work done by NDINIT, some of the work done
+ * by namei, some of the work done by lookup and some of
+ * the work done by VOP_LOOKUP when given a CREATE flag.
+ * Conclusion: Horrible.
+ *
+ * The pathname buffer will be FREEed by VOP_MKDIR.
+ */
+ cn.cn_pnbuf = malloc(cnp->cn_namelen+1, M_NAMEI, M_WAITOK);
+ bcopy(cnp->cn_nameptr, cn.cn_pnbuf, cnp->cn_namelen);
+ cn.cn_pnbuf[cnp->cn_namelen] = '\0';
+
+ cn.cn_nameiop = CREATE;
+ cn.cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN);
+ cn.cn_proc = cnp->cn_proc;
+ if (um->um_op == UNMNT_ABOVE)
+ cn.cn_cred = cnp->cn_cred;
+ else
+ cn.cn_cred = um->um_cred;
+ cn.cn_nameptr = cn.cn_pnbuf;
+ cn.cn_namelen = cnp->cn_namelen;
+ cn.cn_hash = cnp->cn_hash;
+ cn.cn_consume = cnp->cn_consume;
+
+ VREF(dvp);
+ if (error = relookup(dvp, vpp, &cn))
+ return (error);
+ vrele(dvp);
+
+ if (*vpp) {
+ VOP_ABORTOP(dvp, &cn);
+ VOP_UNLOCK(dvp);
+ vrele(*vpp);
+ *vpp = NULLVP;
+ return (EEXIST);
+ }
+
+ VATTR_NULL(&va);
+ va.va_type = VDIR;
+ va.va_mode = um->um_cmode;
+
+ /* LEASE_CHECK: dvp is locked */
+ LEASE_CHECK(dvp, p, p->p_ucred, LEASE_WRITE);
+
+ error = VOP_MKDIR(dvp, vpp, &cn, &va);
+ return (error);
+}
+
+/*
+ * union_vn_create: creates and opens a new shadow file
+ * on the upper union layer. this function is similar
+ * in spirit to calling vn_open but it avoids calling namei().
+ * the problem with calling namei is that a) it locks too many
+ * things, and b) it doesn't start at the "right" directory,
+ * whereas relookup is told where to start.
+ */
+int
+union_vn_create(vpp, un, p)
+ struct vnode **vpp;
+ struct union_node *un;
+ struct proc *p;
+{
+ struct vnode *vp;
+ struct ucred *cred = p->p_ucred;
+ struct vattr vat;
+ struct vattr *vap = &vat;
+ int fmode = FFLAGS(O_WRONLY|O_CREAT|O_TRUNC|O_EXCL);
+ int error;
+ int cmode = UN_FILEMODE & ~p->p_fd->fd_cmask;
+ char *cp;
+ struct componentname cn;
+
+ *vpp = NULLVP;
+
+ /*
+ * Build a new componentname structure (for the same
+ * reasons outlines in union_mkshadow).
+ * The difference here is that the file is owned by
+ * the current user, rather than by the person who
+ * did the mount, since the current user needs to be
+ * able to write the file (that's why it is being
+ * copied in the first place).
+ */
+ cn.cn_namelen = strlen(un->un_path);
+ cn.cn_pnbuf = (caddr_t) malloc(cn.cn_namelen, M_NAMEI, M_WAITOK);
+ bcopy(un->un_path, cn.cn_pnbuf, cn.cn_namelen+1);
+ cn.cn_nameiop = CREATE;
+ cn.cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN);
+ cn.cn_proc = p;
+ cn.cn_cred = p->p_ucred;
+ cn.cn_nameptr = cn.cn_pnbuf;
+ cn.cn_hash = un->un_hash;
+ cn.cn_consume = 0;
+
+ VREF(un->un_dirvp);
+ if (error = relookup(un->un_dirvp, &vp, &cn))
+ return (error);
+ vrele(un->un_dirvp);
+
+ if (vp) {
+ VOP_ABORTOP(un->un_dirvp, &cn);
+ if (un->un_dirvp == vp)
+ vrele(un->un_dirvp);
+ else
+ vput(un->un_dirvp);
+ vrele(vp);
+ return (EEXIST);
+ }
+
+ /*
+ * Good - there was no race to create the file
+ * so go ahead and create it. The permissions
+ * on the file will be 0666 modified by the
+ * current user's umask. Access to the file, while
+ * it is unioned, will require access to the top *and*
+ * bottom files. Access when not unioned will simply
+ * require access to the top-level file.
+ * TODO: confirm choice of access permissions.
+ */
+ VATTR_NULL(vap);
+ vap->va_type = VREG;
+ vap->va_mode = cmode;
+ LEASE_CHECK(un->un_dirvp, p, cred, LEASE_WRITE);
+ if (error = VOP_CREATE(un->un_dirvp, &vp, &cn, vap))
+ return (error);
+
+ if (error = VOP_OPEN(vp, fmode, cred, p)) {
+ vput(vp);
+ return (error);
+ }
+
+ vp->v_writecount++;
+ *vpp = vp;
+ return (0);
+}
+
+int
+union_vn_close(vp, fmode, cred, p)
+ struct vnode *vp;
+ int fmode;
+ struct ucred *cred;
+ struct proc *p;
+{
+ if (fmode & FWRITE)
+ --vp->v_writecount;
+ return (VOP_CLOSE(vp, fmode));
+}
+
+void
+union_removed_upper(un)
+ struct union_node *un;
+{
+ if (un->un_flags & UN_ULOCK) {
+ un->un_flags &= ~UN_ULOCK;
+ VOP_UNLOCK(un->un_uppervp);
+ }
+
+ union_newupper(un, NULLVP);
+}
+
+struct vnode *
+union_lowervp(vp)
+ struct vnode *vp;
+{
+ struct union_node *un = VTOUNION(vp);
+
+ if (un->un_lowervp && (vp->v_type == un->un_lowervp->v_type)) {
+ if (vget(un->un_lowervp, 0))
+ return (NULLVP);
+ }
+
+ return (un->un_lowervp);
+}
diff --git a/sys/fs/unionfs/union_vfsops.c b/sys/fs/unionfs/union_vfsops.c
new file mode 100644
index 0000000..42931d7
--- /dev/null
+++ b/sys/fs/unionfs/union_vfsops.c
@@ -0,0 +1,550 @@
+/*
+ * Copyright (c) 1994 The Regents of the University of California.
+ * Copyright (c) 1994 Jan-Simon Pendry.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)union_vfsops.c 8.7 (Berkeley) 3/5/94
+ */
+
+/*
+ * Union Layer
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/filedesc.h>
+#include <sys/queue.h>
+#include <miscfs/union/union.h>
+
+/*
+ * Mount union filesystem
+ */
+int
+union_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ int error = 0;
+ struct union_args args;
+ struct vnode *lowerrootvp = NULLVP;
+ struct vnode *upperrootvp = NULLVP;
+ struct union_mount *um;
+ struct ucred *cred = 0;
+ struct ucred *scred;
+ struct vattr va;
+ char *cp = 0;
+ int len;
+ u_int size;
+
+#ifdef UNION_DIAGNOSTIC
+ printf("union_mount(mp = %x)\n", mp);
+#endif
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ /*
+ * Need to provide.
+ * 1. a way to convert between rdonly and rdwr mounts.
+ * 2. support for nfs exports.
+ */
+ error = EOPNOTSUPP;
+ goto bad;
+ }
+
+ /*
+ * Take a copy of the process's credentials. This isn't
+ * quite right since the euid will always be zero and we
+ * want to get the "real" users credentials. So fix up
+ * the uid field after taking the copy.
+ */
+ cred = crdup(p->p_ucred);
+ cred->cr_uid = p->p_cred->p_ruid;
+
+ /*
+ * Ensure the *real* user has write permission on the
+ * mounted-on directory. This allows the mount_union
+ * command to be made setuid root so allowing anyone
+ * to do union mounts onto any directory on which they
+ * have write permission and which they also own.
+ */
+ error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p);
+ if (error)
+ goto bad;
+ if ((va.va_uid != cred->cr_uid) &&
+ (cred->cr_uid != 0)) {
+ error = EACCES;
+ goto bad;
+ }
+ error = VOP_ACCESS(mp->mnt_vnodecovered, VWRITE, cred, p);
+ if (error)
+ goto bad;
+
+ /*
+ * Get argument
+ */
+ if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args)))
+ goto bad;
+
+ lowerrootvp = mp->mnt_vnodecovered;
+ VREF(lowerrootvp);
+
+ /*
+ * Find upper node. Use the real process credentials,
+ * not the effective ones since this will have come
+ * through a setuid process (mount_union). All this
+ * messing around with permissions is entirely bogus
+ * and should be removed by allowing any user straight
+ * past the mount system call.
+ */
+ scred = p->p_ucred;
+ p->p_ucred = cred;
+ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
+ UIO_USERSPACE, args.target, p);
+ p->p_ucred = scred;
+
+ if (error = namei(ndp))
+ goto bad;
+
+ upperrootvp = ndp->ni_vp;
+ vrele(ndp->ni_dvp);
+ ndp->ni_dvp = NULL;
+
+ if (upperrootvp->v_type != VDIR) {
+ error = EINVAL;
+ goto bad;
+ }
+
+ um = (struct union_mount *) malloc(sizeof(struct union_mount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+
+ /*
+ * Keep a held reference to the target vnodes.
+ * They are vrele'd in union_unmount.
+ *
+ * Depending on the _BELOW flag, the filesystems are
+ * viewed in a different order. In effect, this is the
+ * same as providing a mount under option to the mount syscall.
+ */
+
+ um->um_op = args.mntflags & UNMNT_OPMASK;
+ switch (um->um_op) {
+ case UNMNT_ABOVE:
+ um->um_lowervp = lowerrootvp;
+ um->um_uppervp = upperrootvp;
+ break;
+
+ case UNMNT_BELOW:
+ um->um_lowervp = upperrootvp;
+ um->um_uppervp = lowerrootvp;
+ break;
+
+ case UNMNT_REPLACE:
+ vrele(lowerrootvp);
+ lowerrootvp = NULLVP;
+ um->um_uppervp = upperrootvp;
+ um->um_lowervp = lowerrootvp;
+ break;
+
+ default:
+ error = EINVAL;
+ goto bad;
+ }
+
+ um->um_cred = cred;
+ um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
+
+ /*
+ * Depending on what you think the MNT_LOCAL flag might mean,
+ * you may want the && to be || on the conditional below.
+ * At the moment it has been defined that the filesystem is
+ * only local if it is all local, ie the MNT_LOCAL flag implies
+ * that the entire namespace is local. If you think the MNT_LOCAL
+ * flag implies that some of the files might be stored locally
+ * then you will want to change the conditional.
+ */
+ if (um->um_op == UNMNT_ABOVE) {
+ if (((um->um_lowervp == NULLVP) ||
+ (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
+ (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
+ mp->mnt_flag |= MNT_LOCAL;
+ }
+
+ /*
+ * Copy in the upper layer's RDONLY flag. This is for the benefit
+ * of lookup() which explicitly checks the flag, rather than asking
+ * the filesystem for it's own opinion. This means, that an update
+ * mount of the underlying filesystem to go from rdonly to rdwr
+ * will leave the unioned view as read-only.
+ */
+ mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
+
+ /*
+ * This is a user mount. Privilege check for unmount
+ * will be done in union_unmount.
+ */
+ mp->mnt_flag |= MNT_USER;
+
+ mp->mnt_data = (qaddr_t) um;
+ getnewfsid(mp, MOUNT_UNION);
+
+ (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+
+ switch (um->um_op) {
+ case UNMNT_ABOVE:
+ cp = "<above>";
+ break;
+ case UNMNT_BELOW:
+ cp = "<below>";
+ break;
+ case UNMNT_REPLACE:
+ cp = "";
+ break;
+ }
+ len = strlen(cp);
+ bcopy(cp, mp->mnt_stat.f_mntfromname, len);
+
+ cp = mp->mnt_stat.f_mntfromname + len;
+ len = MNAMELEN - len;
+
+ (void) copyinstr(args.target, cp, len - 1, &size);
+ bzero(cp + size, len - size);
+
+#ifdef UNION_DIAGNOSTIC
+ printf("union_mount: from %s, on %s\n",
+ mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
+#endif
+ return (0);
+
+bad:
+ if (cred)
+ crfree(cred);
+ if (upperrootvp)
+ vrele(upperrootvp);
+ if (lowerrootvp)
+ vrele(lowerrootvp);
+ return (error);
+}
+
+/*
+ * VFS start. Nothing needed here - the start routine
+ * on the underlying filesystem(s) will have been called
+ * when that filesystem was mounted.
+ */
+int
+union_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+
+ return (0);
+}
+
+/*
+ * Free reference to union layer
+ */
+int
+union_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
+ struct vnode *um_rootvp;
+ int error;
+ int flags = 0;
+ extern int doforce;
+
+#ifdef UNION_DIAGNOSTIC
+ printf("union_unmount(mp = %x)\n", mp);
+#endif
+
+ /* only the mounter, or superuser can unmount */
+ if ((p->p_cred->p_ruid != um->um_cred->cr_uid) &&
+ (error = suser(p->p_ucred, &p->p_acflag)))
+ return (error);
+
+ if (mntflags & MNT_FORCE) {
+ /* union can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ if (error = union_root(mp, &um_rootvp))
+ return (error);
+ if (um_rootvp->v_usecount > 1) {
+ vput(um_rootvp);
+ return (EBUSY);
+ }
+ if (error = vflush(mp, um_rootvp, flags)) {
+ vput(um_rootvp);
+ return (error);
+ }
+
+#ifdef UNION_DIAGNOSTIC
+ vprint("alias root of lower", um_rootvp);
+#endif
+ /*
+ * Discard references to upper and lower target vnodes.
+ */
+ if (um->um_lowervp)
+ vrele(um->um_lowervp);
+ vrele(um->um_uppervp);
+ crfree(um->um_cred);
+ /*
+ * Release reference on underlying root vnode
+ */
+ vput(um_rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(um_rootvp);
+ /*
+ * Finally, throw away the union_mount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ mp->mnt_data = 0;
+ return (0);
+}
+
+int
+union_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
+ int error;
+ int loselock;
+
+#ifdef UNION_DIAGNOSTIC
+ printf("union_root(mp = %x, lvp = %x, uvp = %x)\n", mp,
+ um->um_lowervp,
+ um->um_uppervp);
+#endif
+
+ /*
+ * Return locked reference to root.
+ */
+ VREF(um->um_uppervp);
+ if ((um->um_op == UNMNT_BELOW) &&
+ VOP_ISLOCKED(um->um_uppervp)) {
+ loselock = 1;
+ } else {
+ VOP_LOCK(um->um_uppervp);
+ loselock = 0;
+ }
+ if (um->um_lowervp)
+ VREF(um->um_lowervp);
+ error = union_allocvp(vpp, mp,
+ (struct vnode *) 0,
+ (struct vnode *) 0,
+ (struct componentname *) 0,
+ um->um_uppervp,
+ um->um_lowervp);
+
+ if (error) {
+ if (!loselock)
+ VOP_UNLOCK(um->um_uppervp);
+ vrele(um->um_uppervp);
+ if (um->um_lowervp)
+ vrele(um->um_lowervp);
+ } else {
+ (*vpp)->v_flag |= VROOT;
+ if (loselock)
+ VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
+ }
+
+ return (error);
+}
+
+int
+union_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+union_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ int error;
+ struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
+ struct statfs mstat;
+ int lbsize;
+
+#ifdef UNION_DIAGNOSTIC
+ printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp,
+ um->um_lowervp,
+ um->um_uppervp);
+#endif
+
+ bzero(&mstat, sizeof(mstat));
+
+ if (um->um_lowervp) {
+ error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
+ if (error)
+ return (error);
+ }
+
+ /* now copy across the "interesting" information and fake the rest */
+#if 0
+ sbp->f_type = mstat.f_type;
+ sbp->f_flags = mstat.f_flags;
+ sbp->f_bsize = mstat.f_bsize;
+ sbp->f_iosize = mstat.f_iosize;
+#endif
+ lbsize = mstat.f_bsize;
+ sbp->f_blocks = mstat.f_blocks;
+ sbp->f_bfree = mstat.f_bfree;
+ sbp->f_bavail = mstat.f_bavail;
+ sbp->f_files = mstat.f_files;
+ sbp->f_ffree = mstat.f_ffree;
+
+ error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p);
+ if (error)
+ return (error);
+
+ sbp->f_type = MOUNT_UNION;
+ sbp->f_flags = mstat.f_flags;
+ sbp->f_bsize = mstat.f_bsize;
+ sbp->f_iosize = mstat.f_iosize;
+
+ /*
+ * if the lower and upper blocksizes differ, then frig the
+ * block counts so that the sizes reported by df make some
+ * kind of sense. none of this makes sense though.
+ */
+
+ if (mstat.f_bsize != lbsize) {
+ sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize;
+ sbp->f_bfree = sbp->f_bfree * lbsize / mstat.f_bsize;
+ sbp->f_bavail = sbp->f_bavail * lbsize / mstat.f_bsize;
+ }
+ sbp->f_blocks += mstat.f_blocks;
+ sbp->f_bfree += mstat.f_bfree;
+ sbp->f_bavail += mstat.f_bavail;
+ sbp->f_files += mstat.f_files;
+ sbp->f_ffree += mstat.f_ffree;
+
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+ return (0);
+}
+
+int
+union_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+
+ /*
+ * XXX - Assumes no data cached at union layer.
+ */
+ return (0);
+}
+
+int
+union_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+union_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
+ struct mount *mp;
+ struct fid *fidp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred **credanonp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+union_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int union_init __P((void));
+
+struct vfsops union_vfsops = {
+ union_mount,
+ union_start,
+ union_unmount,
+ union_root,
+ union_quotactl,
+ union_statfs,
+ union_sync,
+ union_vget,
+ union_fhtovp,
+ union_vptofh,
+ union_init,
+};
diff --git a/sys/fs/unionfs/union_vnops.c b/sys/fs/unionfs/union_vnops.c
new file mode 100644
index 0000000..30f2233
--- /dev/null
+++ b/sys/fs/unionfs/union_vnops.c
@@ -0,0 +1,1495 @@
+/*
+ * Copyright (c) 1992, 1993, 1994 The Regents of the University of California.
+ * Copyright (c) 1992, 1993, 1994 Jan-Simon Pendry.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)union_vnops.c 8.6 (Berkeley) 2/17/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/queue.h>
+#include <miscfs/union/union.h>
+
+#define FIXUP(un) { \
+ if (((un)->un_flags & UN_ULOCK) == 0) { \
+ union_fixup(un); \
+ } \
+}
+
+static void
+union_fixup(un)
+ struct union_node *un;
+{
+
+ VOP_LOCK(un->un_uppervp);
+ un->un_flags |= UN_ULOCK;
+}
+
+static int
+union_lookup1(udvp, dvp, vpp, cnp)
+ struct vnode *udvp;
+ struct vnode *dvp;
+ struct vnode **vpp;
+ struct componentname *cnp;
+{
+ int error;
+ struct vnode *tdvp;
+ struct mount *mp;
+
+ /*
+ * If stepping up the directory tree, check for going
+ * back across the mount point, in which case do what
+ * lookup would do by stepping back down the mount
+ * hierarchy.
+ */
+ if (cnp->cn_flags & ISDOTDOT) {
+ for (;;) {
+ /*
+ * Don't do the NOCROSSMOUNT check
+ * at this level. By definition,
+ * union fs deals with namespaces, not
+ * filesystems.
+ */
+ if ((dvp->v_flag & VROOT) == 0)
+ break;
+
+ tdvp = dvp;
+ dvp = dvp->v_mount->mnt_vnodecovered;
+ vput(tdvp);
+ VREF(dvp);
+ VOP_LOCK(dvp);
+ }
+ }
+
+ error = VOP_LOOKUP(dvp, &tdvp, cnp);
+ if (error)
+ return (error);
+
+ /*
+ * The parent directory will have been unlocked, unless lookup
+ * found the last component. In which case, re-lock the node
+ * here to allow it to be unlocked again (phew) in union_lookup.
+ */
+ if (dvp != tdvp && !(cnp->cn_flags & ISLASTCN))
+ VOP_LOCK(dvp);
+
+ dvp = tdvp;
+
+ /*
+ * Lastly check if the current node is a mount point in
+ * which case walk up the mount hierarchy making sure not to
+ * bump into the root of the mount tree (ie. dvp != udvp).
+ */
+ while (dvp != udvp && (dvp->v_type == VDIR) &&
+ (mp = dvp->v_mountedhere)) {
+
+ if (mp->mnt_flag & MNT_MLOCK) {
+ mp->mnt_flag |= MNT_MWAIT;
+ sleep((caddr_t) mp, PVFS);
+ continue;
+ }
+
+ if (error = VFS_ROOT(mp, &tdvp)) {
+ vput(dvp);
+ return (error);
+ }
+
+ vput(dvp);
+ dvp = tdvp;
+ }
+
+ *vpp = dvp;
+ return (0);
+}
+
+int
+union_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error;
+ int uerror, lerror;
+ struct vnode *uppervp, *lowervp;
+ struct vnode *upperdvp, *lowerdvp;
+ struct vnode *dvp = ap->a_dvp;
+ struct union_node *dun = VTOUNION(dvp);
+ struct componentname *cnp = ap->a_cnp;
+ int lockparent = cnp->cn_flags & LOCKPARENT;
+ int rdonly = cnp->cn_flags & RDONLY;
+ struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount);
+ struct ucred *saved_cred = 0;
+
+ cnp->cn_flags |= LOCKPARENT;
+
+ upperdvp = dun->un_uppervp;
+ lowerdvp = dun->un_lowervp;
+ uppervp = NULLVP;
+ lowervp = NULLVP;
+
+ /*
+ * do the lookup in the upper level.
+ * if that level comsumes additional pathnames,
+ * then assume that something special is going
+ * on and just return that vnode.
+ */
+ if (upperdvp) {
+ FIXUP(dun);
+ uerror = union_lookup1(um->um_uppervp, upperdvp,
+ &uppervp, cnp);
+ /*if (uppervp == upperdvp)
+ dun->un_flags |= UN_KLOCK;*/
+
+ if (cnp->cn_consume != 0) {
+ *ap->a_vpp = uppervp;
+ if (!lockparent)
+ cnp->cn_flags &= ~LOCKPARENT;
+ return (uerror);
+ }
+ } else {
+ uerror = ENOENT;
+ }
+
+ /*
+ * in a similar way to the upper layer, do the lookup
+ * in the lower layer. this time, if there is some
+ * component magic going on, then vput whatever we got
+ * back from the upper layer and return the lower vnode
+ * instead.
+ */
+ if (lowerdvp) {
+ int nameiop;
+
+ VOP_LOCK(lowerdvp);
+
+ /*
+ * Only do a LOOKUP on the bottom node, since
+ * we won't be making changes to it anyway.
+ */
+ nameiop = cnp->cn_nameiop;
+ cnp->cn_nameiop = LOOKUP;
+ if (um->um_op == UNMNT_BELOW) {
+ saved_cred = cnp->cn_cred;
+ cnp->cn_cred = um->um_cred;
+ }
+ lerror = union_lookup1(um->um_lowervp, lowerdvp,
+ &lowervp, cnp);
+ if (um->um_op == UNMNT_BELOW)
+ cnp->cn_cred = saved_cred;
+ cnp->cn_nameiop = nameiop;
+
+ if (lowervp != lowerdvp)
+ VOP_UNLOCK(lowerdvp);
+
+ if (cnp->cn_consume != 0) {
+ if (uppervp) {
+ if (uppervp == upperdvp)
+ vrele(uppervp);
+ else
+ vput(uppervp);
+ uppervp = NULLVP;
+ }
+ *ap->a_vpp = lowervp;
+ if (!lockparent)
+ cnp->cn_flags &= ~LOCKPARENT;
+ return (lerror);
+ }
+ } else {
+ lerror = ENOENT;
+ }
+
+ if (!lockparent)
+ cnp->cn_flags &= ~LOCKPARENT;
+
+ /*
+ * at this point, we have uerror and lerror indicating
+ * possible errors with the lookups in the upper and lower
+ * layers. additionally, uppervp and lowervp are (locked)
+ * references to existing vnodes in the upper and lower layers.
+ *
+ * there are now three cases to consider.
+ * 1. if both layers returned an error, then return whatever
+ * error the upper layer generated.
+ *
+ * 2. if the top layer failed and the bottom layer succeeded
+ * then two subcases occur.
+ * a. the bottom vnode is not a directory, in which
+ * case just return a new union vnode referencing
+ * an empty top layer and the existing bottom layer.
+ * b. the bottom vnode is a directory, in which case
+ * create a new directory in the top-level and
+ * continue as in case 3.
+ *
+ * 3. if the top layer succeeded then return a new union
+ * vnode referencing whatever the new top layer and
+ * whatever the bottom layer returned.
+ */
+
+ *ap->a_vpp = NULLVP;
+
+ /* case 1. */
+ if ((uerror != 0) && (lerror != 0)) {
+ return (uerror);
+ }
+
+ /* case 2. */
+ if (uerror != 0 /* && (lerror == 0) */ ) {
+ if (lowervp->v_type == VDIR) { /* case 2b. */
+ dun->un_flags &= ~UN_ULOCK;
+ VOP_UNLOCK(upperdvp);
+ uerror = union_mkshadow(um, upperdvp, cnp, &uppervp);
+ VOP_LOCK(upperdvp);
+ dun->un_flags |= UN_ULOCK;
+
+ if (uerror) {
+ if (lowervp) {
+ vput(lowervp);
+ lowervp = NULLVP;
+ }
+ return (uerror);
+ }
+ }
+ }
+
+ if (lowervp)
+ VOP_UNLOCK(lowervp);
+
+ error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
+ uppervp, lowervp);
+
+ if (error) {
+ if (uppervp)
+ vput(uppervp);
+ if (lowervp)
+ vrele(lowervp);
+ } else {
+ if (*ap->a_vpp != dvp)
+ if (!lockparent || !(cnp->cn_flags & ISLASTCN))
+ VOP_UNLOCK(dvp);
+ }
+
+ return (error);
+}
+
+int
+union_create(ap)
+ struct vop_create_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_dvp);
+ struct vnode *dvp = un->un_uppervp;
+
+ if (dvp) {
+ int error;
+ struct vnode *vp;
+
+ FIXUP(un);
+
+ VREF(dvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ error = VOP_CREATE(dvp, &vp, ap->a_cnp, ap->a_vap);
+ if (error)
+ return (error);
+
+ error = union_allocvp(
+ ap->a_vpp,
+ ap->a_dvp->v_mount,
+ ap->a_dvp,
+ NULLVP,
+ ap->a_cnp,
+ vp,
+ NULLVP);
+ if (error)
+ vput(vp);
+ return (error);
+ }
+
+ vput(ap->a_dvp);
+ return (EROFS);
+}
+
+int
+union_mknod(ap)
+ struct vop_mknod_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_dvp);
+ struct vnode *dvp = un->un_uppervp;
+
+ if (dvp) {
+ int error;
+ struct vnode *vp;
+
+ FIXUP(un);
+
+ VREF(dvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ error = VOP_MKNOD(dvp, &vp, ap->a_cnp, ap->a_vap);
+ if (error)
+ return (error);
+
+ if (vp) {
+ error = union_allocvp(
+ ap->a_vpp,
+ ap->a_dvp->v_mount,
+ ap->a_dvp,
+ NULLVP,
+ ap->a_cnp,
+ vp,
+ NULLVP);
+ if (error)
+ vput(vp);
+ }
+ return (error);
+ }
+
+ vput(ap->a_dvp);
+ return (EROFS);
+}
+
+int
+union_open(ap)
+ struct vop_open_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *tvp;
+ int mode = ap->a_mode;
+ struct ucred *cred = ap->a_cred;
+ struct proc *p = ap->a_p;
+ int error;
+
+ /*
+ * If there is an existing upper vp then simply open that.
+ */
+ tvp = un->un_uppervp;
+ if (tvp == NULLVP) {
+ /*
+ * If the lower vnode is being opened for writing, then
+ * copy the file contents to the upper vnode and open that,
+ * otherwise can simply open the lower vnode.
+ */
+ tvp = un->un_lowervp;
+ if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) {
+ struct vnode *vp;
+ int i;
+
+ /*
+ * Open the named file in the upper layer. Note that
+ * the file may have come into existence *since* the
+ * lookup was done, since the upper layer may really
+ * be a loopback mount of some other filesystem...
+ * so open the file with exclusive create and barf if
+ * it already exists.
+ * XXX - perhaps should re-lookup the node (once more
+ * with feeling) and simply open that. Who knows.
+ */
+ error = union_vn_create(&vp, un, p);
+ if (error)
+ return (error);
+
+ /* at this point, uppervp is locked */
+ union_newupper(un, vp);
+ un->un_flags |= UN_ULOCK;
+
+ /*
+ * Now, if the file is being opened with truncation,
+ * then the (new) upper vnode is ready to fly,
+ * otherwise the data from the lower vnode must be
+ * copied to the upper layer first. This only works
+ * for regular files (check is made above).
+ */
+ if ((mode & O_TRUNC) == 0) {
+ /*
+ * XXX - should not ignore errors
+ * from VOP_CLOSE
+ */
+ VOP_LOCK(tvp);
+ error = VOP_OPEN(tvp, FREAD, cred, p);
+ if (error == 0) {
+ error = union_copyfile(p, cred,
+ tvp, un->un_uppervp);
+ VOP_UNLOCK(tvp);
+ (void) VOP_CLOSE(tvp, FREAD);
+ } else {
+ VOP_UNLOCK(tvp);
+ }
+
+#ifdef UNION_DIAGNOSTIC
+ if (!error)
+ uprintf("union: copied up %s\n",
+ un->un_path);
+#endif
+ }
+
+ un->un_flags &= ~UN_ULOCK;
+ VOP_UNLOCK(un->un_uppervp);
+ union_vn_close(un->un_uppervp, FWRITE, cred, p);
+ VOP_LOCK(un->un_uppervp);
+ un->un_flags |= UN_ULOCK;
+
+ /*
+ * Subsequent IOs will go to the top layer, so
+ * call close on the lower vnode and open on the
+ * upper vnode to ensure that the filesystem keeps
+ * its references counts right. This doesn't do
+ * the right thing with (cred) and (FREAD) though.
+ * Ignoring error returns is not righ, either.
+ */
+ for (i = 0; i < un->un_openl; i++) {
+ (void) VOP_CLOSE(tvp, FREAD);
+ (void) VOP_OPEN(un->un_uppervp, FREAD, cred, p);
+ }
+ un->un_openl = 0;
+
+ if (error == 0)
+ error = VOP_OPEN(un->un_uppervp, mode, cred, p);
+ return (error);
+ }
+
+ /*
+ * Just open the lower vnode
+ */
+ un->un_openl++;
+ VOP_LOCK(tvp);
+ error = VOP_OPEN(tvp, mode, cred, p);
+ VOP_UNLOCK(tvp);
+
+ return (error);
+ }
+
+ FIXUP(un);
+
+ error = VOP_OPEN(tvp, mode, cred, p);
+
+ return (error);
+}
+
+int
+union_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *vp;
+
+ if (un->un_uppervp) {
+ vp = un->un_uppervp;
+ } else {
+#ifdef UNION_DIAGNOSTIC
+ if (un->un_openl <= 0)
+ panic("union: un_openl cnt");
+#endif
+ --un->un_openl;
+ vp = un->un_lowervp;
+ }
+
+ return (VOP_CLOSE(vp, ap->a_fflag, ap->a_cred, ap->a_p));
+}
+
+/*
+ * Check access permission on the union vnode.
+ * The access check being enforced is to check
+ * against both the underlying vnode, and any
+ * copied vnode. This ensures that no additional
+ * file permissions are given away simply because
+ * the user caused an implicit file copy.
+ */
+int
+union_access(ap)
+ struct vop_access_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_vp);
+ int error = EACCES;
+ struct vnode *vp;
+
+ if (vp = un->un_uppervp) {
+ FIXUP(un);
+ return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p));
+ }
+
+ if (vp = un->un_lowervp) {
+ VOP_LOCK(vp);
+ error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p);
+ if (error == 0) {
+ struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount);
+
+ if (um->um_op == UNMNT_BELOW)
+ error = VOP_ACCESS(vp, ap->a_mode,
+ um->um_cred, ap->a_p);
+ }
+ VOP_UNLOCK(vp);
+ if (error)
+ return (error);
+ }
+
+ return (error);
+}
+
+/*
+ * We handle getattr only to change the fsid.
+ */
+int
+union_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error;
+ struct union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *vp = un->un_uppervp;
+ struct vattr *vap;
+ struct vattr va;
+
+
+ /*
+ * Some programs walk the filesystem hierarchy by counting
+ * links to directories to avoid stat'ing all the time.
+ * This means the link count on directories needs to be "correct".
+ * The only way to do that is to call getattr on both layers
+ * and fix up the link count. The link count will not necessarily
+ * be accurate but will be large enough to defeat the tree walkers.
+ */
+
+ vap = ap->a_vap;
+
+ vp = un->un_uppervp;
+ if (vp != NULLVP) {
+ FIXUP(un);
+ error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
+ if (error)
+ return (error);
+ }
+
+ if (vp == NULLVP) {
+ vp = un->un_lowervp;
+ } else if (vp->v_type == VDIR) {
+ vp = un->un_lowervp;
+ vap = &va;
+ } else {
+ vp = NULLVP;
+ }
+
+ if (vp != NULLVP) {
+ VOP_LOCK(vp);
+ error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
+ VOP_UNLOCK(vp);
+ if (error)
+ return (error);
+ }
+
+ if ((vap != ap->a_vap) && (vap->va_type == VDIR))
+ ap->a_vap->va_nlink += vap->va_nlink;
+
+ vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
+ return (0);
+}
+
+int
+union_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_vp);
+ int error;
+
+ /*
+ * Handle case of truncating lower object to zero size,
+ * by creating a zero length upper object. This is to
+ * handle the case of open with O_TRUNC and O_CREAT.
+ */
+ if ((un->un_uppervp == NULLVP) &&
+ /* assert(un->un_lowervp != NULLVP) */
+ (un->un_lowervp->v_type == VREG) &&
+ (ap->a_vap->va_size == 0)) {
+ struct vnode *vp;
+
+ error = union_vn_create(&vp, un, ap->a_p);
+ if (error)
+ return (error);
+
+ /* at this point, uppervp is locked */
+ union_newupper(un, vp);
+
+ VOP_UNLOCK(vp);
+ union_vn_close(un->un_uppervp, FWRITE, ap->a_cred, ap->a_p);
+ VOP_LOCK(vp);
+ un->un_flags |= UN_ULOCK;
+ }
+
+ /*
+ * Try to set attributes in upper layer,
+ * otherwise return read-only filesystem error.
+ */
+ if (un->un_uppervp != NULLVP) {
+ FIXUP(un);
+ error = VOP_SETATTR(un->un_uppervp, ap->a_vap,
+ ap->a_cred, ap->a_p);
+ } else {
+ error = EROFS;
+ }
+
+ return (error);
+}
+
+int
+union_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
+ if (dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_write(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
+ if (dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (VOP_IOCTL(OTHERVP(ap->a_vp), ap->a_command, ap->a_data,
+ ap->a_fflag, ap->a_cred, ap->a_p));
+}
+
+int
+union_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (VOP_SELECT(OTHERVP(ap->a_vp), ap->a_which, ap->a_fflags,
+ ap->a_cred, ap->a_p));
+}
+
+int
+union_mmap(ap)
+ struct vop_mmap_args /* {
+ struct vnode *a_vp;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (VOP_MMAP(OTHERVP(ap->a_vp), ap->a_fflags,
+ ap->a_cred, ap->a_p));
+}
+
+int
+union_fsync(ap)
+ struct vop_fsync_args /* {
+ struct vnode *a_vp;
+ struct ucred *a_cred;
+ int a_waitfor;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error = 0;
+ struct vnode *targetvp = OTHERVP(ap->a_vp);
+
+ if (targetvp) {
+ int dolock = (targetvp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(targetvp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ error = VOP_FSYNC(targetvp, ap->a_cred,
+ ap->a_waitfor, ap->a_p);
+ if (dolock)
+ VOP_UNLOCK(targetvp);
+ }
+
+ return (error);
+}
+
+int
+union_seek(ap)
+ struct vop_seek_args /* {
+ struct vnode *a_vp;
+ off_t a_oldoff;
+ off_t a_newoff;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+
+ return (VOP_SEEK(OTHERVP(ap->a_vp), ap->a_oldoff, ap->a_newoff, ap->a_cred));
+}
+
+int
+union_remove(ap)
+ struct vop_remove_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error;
+ struct union_node *dun = VTOUNION(ap->a_dvp);
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+ if (dun->un_uppervp && un->un_uppervp) {
+ struct vnode *dvp = dun->un_uppervp;
+ struct vnode *vp = un->un_uppervp;
+
+ FIXUP(dun);
+ VREF(dvp);
+ dun->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ FIXUP(un);
+ VREF(vp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_vp);
+
+ error = VOP_REMOVE(dvp, vp, ap->a_cnp);
+ if (!error)
+ union_removed_upper(un);
+
+ /*
+ * XXX: should create a whiteout here
+ */
+ } else {
+ /*
+ * XXX: should create a whiteout here
+ */
+ vput(ap->a_dvp);
+ vput(ap->a_vp);
+ error = EROFS;
+ }
+
+ return (error);
+}
+
+int
+union_link(ap)
+ struct vop_link_args /* {
+ struct vnode *a_vp;
+ struct vnode *a_tdvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error;
+ struct union_node *dun = VTOUNION(ap->a_vp);
+ struct union_node *un = VTOUNION(ap->a_tdvp);
+
+ if (dun->un_uppervp && un->un_uppervp) {
+ struct vnode *dvp = dun->un_uppervp;
+ struct vnode *vp = un->un_uppervp;
+
+ FIXUP(dun);
+ VREF(dvp);
+ dun->un_flags |= UN_KLOCK;
+ vput(ap->a_vp);
+ FIXUP(un);
+ VREF(vp);
+ vrele(ap->a_tdvp);
+
+ error = VOP_LINK(dvp, vp, ap->a_cnp);
+ } else {
+ /*
+ * XXX: need to copy to upper layer
+ * and do the link there.
+ */
+ vput(ap->a_vp);
+ vrele(ap->a_tdvp);
+ error = EROFS;
+ }
+
+ return (error);
+}
+
+int
+union_rename(ap)
+ struct vop_rename_args /* {
+ struct vnode *a_fdvp;
+ struct vnode *a_fvp;
+ struct componentname *a_fcnp;
+ struct vnode *a_tdvp;
+ struct vnode *a_tvp;
+ struct componentname *a_tcnp;
+ } */ *ap;
+{
+ int error;
+
+ struct vnode *fdvp = ap->a_fdvp;
+ struct vnode *fvp = ap->a_fvp;
+ struct vnode *tdvp = ap->a_tdvp;
+ struct vnode *tvp = ap->a_tvp;
+
+ if (fdvp->v_op == union_vnodeop_p) { /* always true */
+ struct union_node *un = VTOUNION(fdvp);
+ if (un->un_uppervp == NULLVP) {
+ error = EROFS;
+ goto bad;
+ }
+
+ FIXUP(un);
+ fdvp = un->un_uppervp;
+ VREF(fdvp);
+ vrele(ap->a_fdvp);
+ }
+
+ if (fvp->v_op == union_vnodeop_p) { /* always true */
+ struct union_node *un = VTOUNION(fvp);
+ if (un->un_uppervp == NULLVP) {
+ error = EROFS;
+ goto bad;
+ }
+
+ FIXUP(un);
+ fvp = un->un_uppervp;
+ VREF(fvp);
+ vrele(ap->a_fvp);
+ }
+
+ if (tdvp->v_op == union_vnodeop_p) {
+ struct union_node *un = VTOUNION(tdvp);
+ if (un->un_uppervp == NULLVP) {
+ error = EROFS;
+ goto bad;
+ }
+
+ tdvp = un->un_uppervp;
+ VREF(tdvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_tdvp);
+ }
+
+ if (tvp && tvp->v_op == union_vnodeop_p) {
+ struct union_node *un = VTOUNION(tvp);
+ if (un->un_uppervp == NULLVP) {
+ error = EROFS;
+ goto bad;
+ }
+
+ tvp = un->un_uppervp;
+ VREF(tvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_tvp);
+ }
+
+ return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp));
+
+bad:
+ vrele(fdvp);
+ vrele(fvp);
+ vput(tdvp);
+ if (tvp)
+ vput(tvp);
+
+ return (error);
+}
+
+int
+union_mkdir(ap)
+ struct vop_mkdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_dvp);
+ struct vnode *dvp = un->un_uppervp;
+
+ if (dvp) {
+ int error;
+ struct vnode *vp;
+
+ FIXUP(un);
+ VREF(dvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ error = VOP_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap);
+ if (error)
+ return (error);
+
+ error = union_allocvp(
+ ap->a_vpp,
+ ap->a_dvp->v_mount,
+ ap->a_dvp,
+ NULLVP,
+ ap->a_cnp,
+ vp,
+ NULLVP);
+ if (error)
+ vput(vp);
+ return (error);
+ }
+
+ vput(ap->a_dvp);
+ return (EROFS);
+}
+
+int
+union_rmdir(ap)
+ struct vop_rmdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error;
+ struct union_node *dun = VTOUNION(ap->a_dvp);
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+ if (dun->un_uppervp && un->un_uppervp) {
+ struct vnode *dvp = dun->un_uppervp;
+ struct vnode *vp = un->un_uppervp;
+
+ FIXUP(dun);
+ VREF(dvp);
+ dun->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ FIXUP(un);
+ VREF(vp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_vp);
+
+ error = VOP_RMDIR(dvp, vp, ap->a_cnp);
+ if (!error)
+ union_removed_upper(un);
+
+ /*
+ * XXX: should create a whiteout here
+ */
+ } else {
+ /*
+ * XXX: should create a whiteout here
+ */
+ vput(ap->a_dvp);
+ vput(ap->a_vp);
+ error = EROFS;
+ }
+
+ return (error);
+}
+
+int
+union_symlink(ap)
+ struct vop_symlink_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ char *a_target;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_dvp);
+ struct vnode *dvp = un->un_uppervp;
+
+ if (dvp) {
+ int error;
+ struct vnode *vp;
+ struct mount *mp = ap->a_dvp->v_mount;
+
+ FIXUP(un);
+ VREF(dvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ error = VOP_SYMLINK(dvp, &vp, ap->a_cnp,
+ ap->a_vap, ap->a_target);
+ *ap->a_vpp = NULLVP;
+ return (error);
+ }
+
+ vput(ap->a_dvp);
+ return (EROFS);
+}
+
+/*
+ * union_readdir works in concert with getdirentries and
+ * readdir(3) to provide a list of entries in the unioned
+ * directories. getdirentries is responsible for walking
+ * down the union stack. readdir(3) is responsible for
+ * eliminating duplicate names from the returned data stream.
+ */
+int
+union_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error = 0;
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+ if (un->un_uppervp) {
+ FIXUP(un);
+ error = VOP_READDIR(un->un_uppervp, ap->a_uio, ap->a_cred);
+ }
+
+ return (error);
+}
+
+int
+union_readlink(ap)
+ struct vop_readlink_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
+ if (dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_abortop(ap)
+ struct vop_abortop_args /* {
+ struct vnode *a_dvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_dvp);
+ struct union_node *un = VTOUNION(ap->a_dvp);
+ int islocked = un->un_flags & UN_LOCKED;
+ int dolock = (vp == LOWERVP(ap->a_dvp));
+
+ if (islocked) {
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_dvp));
+ }
+ error = VOP_ABORTOP(vp, ap->a_cnp);
+ if (islocked && dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ /*
+ * Do nothing (and _don't_ bypass).
+ * Wait to vrele lowervp until reclaim,
+ * so that until then our union_node is in the
+ * cache and reusable.
+ *
+ * NEEDSWORK: Someday, consider inactive'ing
+ * the lowervp and then trying to reactivate it
+ * with capabilities (v_id)
+ * like they do in the name lookup cache code.
+ * That's too much work for now.
+ */
+
+#ifdef UNION_DIAGNOSTIC
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+ if (un->un_flags & UN_LOCKED)
+ panic("union: inactivating locked node");
+#endif
+
+ return (0);
+}
+
+int
+union_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ union_freevp(ap->a_vp);
+
+ return (0);
+}
+
+int
+union_lock(ap)
+ struct vop_lock_args *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct union_node *un;
+
+start:
+ while (vp->v_flag & VXLOCK) {
+ vp->v_flag |= VXWANT;
+ sleep((caddr_t)vp, PINOD);
+ }
+
+ un = VTOUNION(vp);
+
+ if (un->un_uppervp) {
+ if ((un->un_flags & UN_ULOCK) == 0) {
+ un->un_flags |= UN_ULOCK;
+ VOP_LOCK(un->un_uppervp);
+ }
+#ifdef DIAGNOSTIC
+ if (un->un_flags & UN_KLOCK)
+ panic("union: dangling upper lock");
+#endif
+ }
+
+ if (un->un_flags & UN_LOCKED) {
+#ifdef DIAGNOSTIC
+ if (curproc && un->un_pid == curproc->p_pid &&
+ un->un_pid > -1 && curproc->p_pid > -1)
+ panic("union: locking against myself");
+#endif
+ un->un_flags |= UN_WANT;
+ sleep((caddr_t) &un->un_flags, PINOD);
+ goto start;
+ }
+
+#ifdef DIAGNOSTIC
+ if (curproc)
+ un->un_pid = curproc->p_pid;
+ else
+ un->un_pid = -1;
+#endif
+
+ un->un_flags |= UN_LOCKED;
+ return (0);
+}
+
+int
+union_unlock(ap)
+ struct vop_lock_args *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+#ifdef DIAGNOSTIC
+ if ((un->un_flags & UN_LOCKED) == 0)
+ panic("union: unlock unlocked node");
+ if (curproc && un->un_pid != curproc->p_pid &&
+ curproc->p_pid > -1 && un->un_pid > -1)
+ panic("union: unlocking other process's union node");
+#endif
+
+ un->un_flags &= ~UN_LOCKED;
+
+ if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK)
+ VOP_UNLOCK(un->un_uppervp);
+
+ un->un_flags &= ~(UN_ULOCK|UN_KLOCK);
+
+ if (un->un_flags & UN_WANT) {
+ un->un_flags &= ~UN_WANT;
+ wakeup((caddr_t) &un->un_flags);
+ }
+
+#ifdef DIAGNOSTIC
+ un->un_pid = 0;
+#endif
+
+ return (0);
+}
+
+int
+union_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp);
+ if (dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+
+ printf("\ttag VT_UNION, vp=%x, uppervp=%x, lowervp=%x\n",
+ vp, UPPERVP(vp), LOWERVP(vp));
+ return (0);
+}
+
+int
+union_islocked(ap)
+ struct vop_islocked_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0);
+}
+
+int
+union_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval);
+ if (dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_advlock(ap)
+ struct vop_advlock_args /* {
+ struct vnode *a_vp;
+ caddr_t a_id;
+ int a_op;
+ struct flock *a_fl;
+ int a_flags;
+ } */ *ap;
+{
+
+ return (VOP_ADVLOCK(OTHERVP(ap->a_vp), ap->a_id, ap->a_op,
+ ap->a_fl, ap->a_flags));
+}
+
+
+/*
+ * XXX - vop_strategy must be hand coded because it has no
+ * vnode in its arguments.
+ * This goes away with a merged VM/buffer cache.
+ */
+int
+union_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ int error;
+ struct vnode *savedvp;
+
+ savedvp = bp->b_vp;
+ bp->b_vp = OTHERVP(bp->b_vp);
+
+#ifdef DIAGNOSTIC
+ if (bp->b_vp == NULLVP)
+ panic("union_strategy: nil vp");
+ if (((bp->b_flags & B_READ) == 0) &&
+ (bp->b_vp == LOWERVP(savedvp)))
+ panic("union_strategy: writing to lowervp");
+#endif
+
+ error = VOP_STRATEGY(bp);
+ bp->b_vp = savedvp;
+
+ return (error);
+}
+
+/*
+ * Global vfs data structures
+ */
+int (**union_vnodeop_p)();
+struct vnodeopv_entry_desc union_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, union_lookup }, /* lookup */
+ { &vop_create_desc, union_create }, /* create */
+ { &vop_mknod_desc, union_mknod }, /* mknod */
+ { &vop_open_desc, union_open }, /* open */
+ { &vop_close_desc, union_close }, /* close */
+ { &vop_access_desc, union_access }, /* access */
+ { &vop_getattr_desc, union_getattr }, /* getattr */
+ { &vop_setattr_desc, union_setattr }, /* setattr */
+ { &vop_read_desc, union_read }, /* read */
+ { &vop_write_desc, union_write }, /* write */
+ { &vop_ioctl_desc, union_ioctl }, /* ioctl */
+ { &vop_select_desc, union_select }, /* select */
+ { &vop_mmap_desc, union_mmap }, /* mmap */
+ { &vop_fsync_desc, union_fsync }, /* fsync */
+ { &vop_seek_desc, union_seek }, /* seek */
+ { &vop_remove_desc, union_remove }, /* remove */
+ { &vop_link_desc, union_link }, /* link */
+ { &vop_rename_desc, union_rename }, /* rename */
+ { &vop_mkdir_desc, union_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, union_rmdir }, /* rmdir */
+ { &vop_symlink_desc, union_symlink }, /* symlink */
+ { &vop_readdir_desc, union_readdir }, /* readdir */
+ { &vop_readlink_desc, union_readlink }, /* readlink */
+ { &vop_abortop_desc, union_abortop }, /* abortop */
+ { &vop_inactive_desc, union_inactive }, /* inactive */
+ { &vop_reclaim_desc, union_reclaim }, /* reclaim */
+ { &vop_lock_desc, union_lock }, /* lock */
+ { &vop_unlock_desc, union_unlock }, /* unlock */
+ { &vop_bmap_desc, union_bmap }, /* bmap */
+ { &vop_strategy_desc, union_strategy }, /* strategy */
+ { &vop_print_desc, union_print }, /* print */
+ { &vop_islocked_desc, union_islocked }, /* islocked */
+ { &vop_pathconf_desc, union_pathconf }, /* pathconf */
+ { &vop_advlock_desc, union_advlock }, /* advlock */
+#ifdef notdef
+ { &vop_blkatoff_desc, union_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, union_valloc }, /* valloc */
+ { &vop_vfree_desc, union_vfree }, /* vfree */
+ { &vop_truncate_desc, union_truncate }, /* truncate */
+ { &vop_update_desc, union_update }, /* update */
+ { &vop_bwrite_desc, union_bwrite }, /* bwrite */
+#endif
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc union_vnodeop_opv_desc =
+ { &union_vnodeop_p, union_vnodeop_entries };
diff --git a/sys/gnu/ext2fs/ext2_bmap.c b/sys/gnu/ext2fs/ext2_bmap.c
new file mode 100644
index 0000000..a424d31
--- /dev/null
+++ b/sys/gnu/ext2fs/ext2_bmap.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_bmap.c 8.6 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/resourcevar.h>
+#include <sys/trace.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+/*
+ * Bmap converts a the logical block number of a file to its physical block
+ * number on the disk. The conversion is done by using the logical block
+ * number to index into the array of block pointers described by the dinode.
+ */
+int
+ufs_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap;
+{
+ /*
+ * Check for underlying vnode requests and ensure that logical
+ * to physical mapping is requested.
+ */
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = VTOI(ap->a_vp)->i_devvp;
+ if (ap->a_bnp == NULL)
+ return (0);
+
+ return (ufs_bmaparray(ap->a_vp, ap->a_bn, ap->a_bnp, NULL, NULL,
+ ap->a_runp));
+}
+
+/*
+ * Indirect blocks are now on the vnode for the file. They are given negative
+ * logical block numbers. Indirect blocks are addressed by the negative
+ * address of the first data block to which they point. Double indirect blocks
+ * are addressed by one less than the address of the first indirect block to
+ * which they point. Triple indirect blocks are addressed by one less than
+ * the address of the first double indirect block to which they point.
+ *
+ * ufs_bmaparray does the bmap conversion, and if requested returns the
+ * array of logical blocks which must be traversed to get to a block.
+ * Each entry contains the offset into that block that gets you to the
+ * next block and the disk address of the block (if it is assigned).
+ */
+
+int
+ufs_bmaparray(vp, bn, bnp, ap, nump, runp)
+ struct vnode *vp;
+ register daddr_t bn;
+ daddr_t *bnp;
+ struct indir *ap;
+ int *nump;
+ int *runp;
+{
+ register struct inode *ip;
+ struct buf *bp;
+ struct ufsmount *ump;
+ struct mount *mp;
+ struct vnode *devvp;
+ struct indir a[NIADDR], *xap;
+ daddr_t daddr;
+ long metalbn;
+ int error, maxrun = 0, num;
+
+ ip = VTOI(vp);
+ mp = vp->v_mount;
+ ump = VFSTOUFS(mp);
+#ifdef DIAGNOSTIC
+ if (ap != NULL && nump == NULL || ap == NULL && nump != NULL)
+ panic("ufs_bmaparray: invalid arguments");
+#endif
+
+ if (runp) {
+ /*
+ * XXX
+ * If MAXBSIZE is the largest transfer the disks can handle,
+ * we probably want maxrun to be 1 block less so that we
+ * don't create a block larger than the device can handle.
+ */
+ *runp = 0;
+ maxrun = MAXBSIZE / mp->mnt_stat.f_iosize - 1;
+ }
+
+ xap = ap == NULL ? a : ap;
+ if (!nump)
+ nump = &num;
+ if (error = ufs_getlbns(vp, bn, xap, nump))
+ return (error);
+
+ num = *nump;
+ if (num == 0) {
+ *bnp = blkptrtodb(ump, ip->i_db[bn]);
+ if (*bnp == 0)
+ *bnp = -1;
+ else if (runp)
+ for (++bn; bn < NDADDR && *runp < maxrun &&
+ is_sequential(ump, ip->i_db[bn - 1], ip->i_db[bn]);
+ ++bn, ++*runp);
+ return (0);
+ }
+
+
+ /* Get disk address out of indirect block array */
+ daddr = ip->i_ib[xap->in_off];
+
+ devvp = VFSTOUFS(vp->v_mount)->um_devvp;
+ for (bp = NULL, ++xap; --num; ++xap) {
+ /*
+ * Exit the loop if there is no disk address assigned yet and
+ * the indirect block isn't in the cache, or if we were
+ * looking for an indirect block and we've found it.
+ */
+
+ metalbn = xap->in_lbn;
+ if (daddr == 0 && !incore(vp, metalbn) || metalbn == bn)
+ break;
+ /*
+ * If we get here, we've either got the block in the cache
+ * or we have a disk address for it, go fetch it.
+ */
+ if (bp)
+ brelse(bp);
+
+ xap->in_exists = 1;
+ bp = getblk(vp, metalbn, mp->mnt_stat.f_iosize, 0, 0);
+ if (bp->b_flags & (B_DONE | B_DELWRI)) {
+ trace(TR_BREADHIT, pack(vp, size), metalbn);
+ }
+#ifdef DIAGNOSTIC
+ else if (!daddr)
+ panic("ufs_bmaparry: indirect block not in cache");
+#endif
+ else {
+ trace(TR_BREADMISS, pack(vp, size), metalbn);
+ bp->b_blkno = blkptrtodb(ump, daddr);
+ bp->b_flags |= B_READ;
+ VOP_STRATEGY(bp);
+ curproc->p_stats->p_ru.ru_inblock++; /* XXX */
+ if (error = biowait(bp)) {
+ brelse(bp);
+ return (error);
+ }
+ }
+
+ daddr = ((daddr_t *)bp->b_data)[xap->in_off];
+ if (num == 1 && daddr && runp)
+ for (bn = xap->in_off + 1;
+ bn < MNINDIR(ump) && *runp < maxrun &&
+ is_sequential(ump, ((daddr_t *)bp->b_data)[bn - 1],
+ ((daddr_t *)bp->b_data)[bn]);
+ ++bn, ++*runp);
+ }
+ if (bp)
+ brelse(bp);
+
+ daddr = blkptrtodb(ump, daddr);
+ *bnp = daddr == 0 ? -1 : daddr;
+ return (0);
+}
+
+/*
+ * Create an array of logical block number/offset pairs which represent the
+ * path of indirect blocks required to access a data block. The first "pair"
+ * contains the logical block number of the appropriate single, double or
+ * triple indirect block and the offset into the inode indirect block array.
+ * Note, the logical block number of the inode single/double/triple indirect
+ * block appears twice in the array, once with the offset into the i_ib and
+ * once with the offset into the page itself.
+ */
+int
+ufs_getlbns(vp, bn, ap, nump)
+ struct vnode *vp;
+ register daddr_t bn;
+ struct indir *ap;
+ int *nump;
+{
+ long metalbn, realbn;
+ struct ufsmount *ump;
+ int blockcnt, i, numlevels, off;
+
+ ump = VFSTOUFS(vp->v_mount);
+ if (nump)
+ *nump = 0;
+ numlevels = 0;
+ realbn = bn;
+ if ((long)bn < 0)
+ bn = -(long)bn;
+
+ /* The first NDADDR blocks are direct blocks. */
+ if (bn < NDADDR)
+ return (0);
+
+ /*
+ * Determine the number of levels of indirection. After this loop
+ * is done, blockcnt indicates the number of data blocks possible
+ * at the given level of indirection, and NIADDR - i is the number
+ * of levels of indirection needed to locate the requested block.
+ */
+ for (blockcnt = 1, i = NIADDR, bn -= NDADDR;; i--, bn -= blockcnt) {
+ if (i == 0)
+ return (EFBIG);
+ blockcnt *= MNINDIR(ump);
+ if (bn < blockcnt)
+ break;
+ }
+
+ /* Calculate the address of the first meta-block. */
+ if (realbn >= 0)
+ metalbn = -(realbn - bn + NIADDR - i);
+ else
+ metalbn = -(-realbn - bn + NIADDR - i);
+
+ /*
+ * At each iteration, off is the offset into the bap array which is
+ * an array of disk addresses at the current level of indirection.
+ * The logical block number and the offset in that block are stored
+ * into the argument array.
+ */
+ ap->in_lbn = metalbn;
+ ap->in_off = off = NIADDR - i;
+ ap->in_exists = 0;
+ ap++;
+ for (++numlevels; i <= NIADDR; i++) {
+ /* If searching for a meta-data block, quit when found. */
+ if (metalbn == realbn)
+ break;
+
+ blockcnt /= MNINDIR(ump);
+ off = (bn / blockcnt) % MNINDIR(ump);
+
+ ++numlevels;
+ ap->in_lbn = metalbn;
+ ap->in_off = off;
+ ap->in_exists = 0;
+ ++ap;
+
+ metalbn -= -1 + off * blockcnt;
+ }
+ if (nump)
+ *nump = numlevels;
+ return (0);
+}
diff --git a/sys/gnu/ext2fs/ext2_ihash.c b/sys/gnu/ext2fs/ext2_ihash.c
new file mode 100644
index 0000000..4a37c90
--- /dev/null
+++ b/sys/gnu/ext2fs/ext2_ihash.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_ihash.c 8.4 (Berkeley) 12/30/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufs_extern.h>
+
+/*
+ * Structures associated with inode cacheing.
+ */
+struct inode **ihashtbl;
+u_long ihash; /* size of hash table - 1 */
+#define INOHASH(device, inum) (((device) + (inum)) & ihash)
+
+/*
+ * Initialize inode hash table.
+ */
+void
+ufs_ihashinit()
+{
+
+ ihashtbl = hashinit(desiredvnodes, M_UFSMNT, &ihash);
+}
+
+/*
+ * Use the device/inum pair to find the incore inode, and return a pointer
+ * to it. If it is in core, return it, even if it is locked.
+ */
+struct vnode *
+ufs_ihashlookup(device, inum)
+ dev_t device;
+ ino_t inum;
+{
+ register struct inode *ip;
+
+ for (ip = ihashtbl[INOHASH(device, inum)];; ip = ip->i_next) {
+ if (ip == NULL)
+ return (NULL);
+ if (inum == ip->i_number && device == ip->i_dev)
+ return (ITOV(ip));
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Use the device/inum pair to find the incore inode, and return a pointer
+ * to it. If it is in core, but locked, wait for it.
+ */
+struct vnode *
+ufs_ihashget(device, inum)
+ dev_t device;
+ ino_t inum;
+{
+ register struct inode *ip;
+ struct vnode *vp;
+
+ for (;;)
+ for (ip = ihashtbl[INOHASH(device, inum)];; ip = ip->i_next) {
+ if (ip == NULL)
+ return (NULL);
+ if (inum == ip->i_number && device == ip->i_dev) {
+ if (ip->i_flag & IN_LOCKED) {
+ ip->i_flag |= IN_WANTED;
+ sleep(ip, PINOD);
+ break;
+ }
+ vp = ITOV(ip);
+ if (!vget(vp, 1))
+ return (vp);
+ break;
+ }
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Insert the inode into the hash table, and return it locked.
+ */
+void
+ufs_ihashins(ip)
+ struct inode *ip;
+{
+ struct inode **ipp, *iq;
+
+ ipp = &ihashtbl[INOHASH(ip->i_dev, ip->i_number)];
+ if (iq = *ipp)
+ iq->i_prev = &ip->i_next;
+ ip->i_next = iq;
+ ip->i_prev = ipp;
+ *ipp = ip;
+ if (ip->i_flag & IN_LOCKED)
+ panic("ufs_ihashins: already locked");
+ if (curproc)
+ ip->i_lockholder = curproc->p_pid;
+ else
+ ip->i_lockholder = -1;
+ ip->i_flag |= IN_LOCKED;
+}
+
+/*
+ * Remove the inode from the hash table.
+ */
+void
+ufs_ihashrem(ip)
+ register struct inode *ip;
+{
+ register struct inode *iq;
+
+ if (iq = ip->i_next)
+ iq->i_prev = ip->i_prev;
+ *ip->i_prev = iq;
+#ifdef DIAGNOSTIC
+ ip->i_next = NULL;
+ ip->i_prev = NULL;
+#endif
+}
diff --git a/sys/gnu/ext2fs/ext2_mount.h b/sys/gnu/ext2fs/ext2_mount.h
new file mode 100644
index 0000000..237871f
--- /dev/null
+++ b/sys/gnu/ext2fs/ext2_mount.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufsmount.h 8.2 (Berkeley) 1/12/94
+ */
+
+struct buf;
+struct inode;
+struct nameidata;
+struct timeval;
+struct ucred;
+struct uio;
+struct vnode;
+struct netexport;
+
+/* This structure describes the UFS specific mount structure data. */
+struct ufsmount {
+ struct mount *um_mountp; /* filesystem vfs structure */
+ dev_t um_dev; /* device mounted */
+ struct vnode *um_devvp; /* block device mounted vnode */
+ union { /* pointer to superblock */
+ struct lfs *lfs; /* LFS */
+ struct fs *fs; /* FFS */
+ } ufsmount_u;
+#define um_fs ufsmount_u.fs
+#define um_lfs ufsmount_u.lfs
+ struct vnode *um_quotas[MAXQUOTAS]; /* pointer to quota files */
+ struct ucred *um_cred[MAXQUOTAS]; /* quota file access cred */
+ u_long um_nindir; /* indirect ptrs per block */
+ u_long um_bptrtodb; /* indir ptr to disk block */
+ u_long um_seqinc; /* inc between seq blocks */
+ time_t um_btime[MAXQUOTAS]; /* block quota time limit */
+ time_t um_itime[MAXQUOTAS]; /* inode quota time limit */
+ char um_qflags[MAXQUOTAS]; /* quota specific flags */
+ struct netexport um_export; /* export information */
+};
+/*
+ * Flags describing the state of quotas.
+ */
+#define QTF_OPENING 0x01 /* Q_QUOTAON in progress */
+#define QTF_CLOSING 0x02 /* Q_QUOTAOFF in progress */
+
+/* Convert mount ptr to ufsmount ptr. */
+#define VFSTOUFS(mp) ((struct ufsmount *)((mp)->mnt_data))
+
+/*
+ * Macros to access file system parameters in the ufsmount structure.
+ * Used by ufs_bmap.
+ */
+#define blkptrtodb(ump, b) ((b) << (ump)->um_bptrtodb)
+#define is_sequential(ump, a, b) ((b) == (a) + ump->um_seqinc)
+#define MNINDIR(ump) ((ump)->um_nindir)
+
+
diff --git a/sys/gnu/ext2fs/inode.h b/sys/gnu/ext2fs/inode.h
new file mode 100644
index 0000000..df15596
--- /dev/null
+++ b/sys/gnu/ext2fs/inode.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 1982, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)inode.h 8.4 (Berkeley) 1/21/94
+ */
+
+#include <ufs/ufs/dinode.h>
+
+/*
+ * Theoretically, directories can be more than 2Gb in length, however, in
+ * practice this seems unlikely. So, we define the type doff_t as a long
+ * to keep down the cost of doing lookup on a 32-bit machine. If you are
+ * porting to a 64-bit architecture, you should make doff_t the same as off_t.
+ */
+#define doff_t long
+
+/*
+ * The inode is used to describe each active (or recently active)
+ * file in the UFS filesystem. It is composed of two types of
+ * information. The first part is the information that is needed
+ * only while the file is active (such as the identity of the file
+ * and linkage to speed its lookup). The second part is the
+ * permannent meta-data associated with the file which is read
+ * in from the permanent dinode from long term storage when the
+ * file becomes active, and is put back when the file is no longer
+ * being used.
+ */
+struct inode {
+ struct inode *i_next; /* Hash chain forward. */
+ struct inode **i_prev; /* Hash chain back. */
+ struct vnode *i_vnode; /* Vnode associated with this inode. */
+ struct vnode *i_devvp; /* Vnode for block I/O. */
+ u_long i_flag; /* I* flags. */
+ dev_t i_dev; /* Device associated with the inode. */
+ ino_t i_number; /* The identity of the inode. */
+ union { /* Associated filesystem. */
+ struct fs *fs; /* FFS */
+ struct lfs *lfs; /* LFS */
+ } inode_u;
+#define i_fs inode_u.fs
+#define i_lfs inode_u.lfs
+ struct dquot *i_dquot[MAXQUOTAS]; /* Dquot structures. */
+ u_quad_t i_modrev; /* Revision level for lease. */
+ struct lockf *i_lockf; /* Head of byte-level lock list. */
+ pid_t i_lockholder; /* DEBUG: holder of inode lock. */
+ pid_t i_lockwaiter; /* DEBUG: latest blocked for inode lock. */
+ /*
+ * Side effects; used during directory lookup.
+ */
+ long i_count; /* Size of free slot in directory. */
+ doff_t i_endoff; /* End of useful stuff in directory. */
+ doff_t i_diroff; /* Offset in dir, where we found last entry. */
+ doff_t i_offset; /* Offset of free space in directory. */
+ ino_t i_ino; /* Inode number of found directory. */
+ u_long i_reclen; /* Size of found directory entry. */
+ long i_spare[11]; /* Spares to round up to 128 bytes. */
+ /*
+ * The on-disk dinode itself.
+ */
+ struct dinode i_din; /* 128 bytes of the on-disk dinode. */
+};
+
+#define i_atime i_din.di_atime
+#define i_blocks i_din.di_blocks
+#define i_ctime i_din.di_ctime
+#define i_db i_din.di_db
+#define i_flags i_din.di_flags
+#define i_gen i_din.di_gen
+#define i_gid i_din.di_gid
+#define i_ib i_din.di_ib
+#define i_mode i_din.di_mode
+#define i_mtime i_din.di_mtime
+#define i_nlink i_din.di_nlink
+#define i_rdev i_din.di_rdev
+#define i_shortlink i_din.di_shortlink
+#define i_size i_din.di_size
+#define i_uid i_din.di_uid
+
+/* These flags are kept in i_flag. */
+#define IN_ACCESS 0x0001 /* Access time update request. */
+#define IN_CHANGE 0x0002 /* Inode change time update request. */
+#define IN_EXLOCK 0x0004 /* File has exclusive lock. */
+#define IN_LOCKED 0x0008 /* Inode lock. */
+#define IN_LWAIT 0x0010 /* Process waiting on file lock. */
+#define IN_MODIFIED 0x0020 /* Inode has been modified. */
+#define IN_RENAME 0x0040 /* Inode is being renamed. */
+#define IN_SHLOCK 0x0080 /* File has shared lock. */
+#define IN_UPDATE 0x0100 /* Modification time update request. */
+#define IN_WANTED 0x0200 /* Inode is wanted by a process. */
+
+#ifdef KERNEL
+/*
+ * Structure used to pass around logical block paths generated by
+ * ufs_getlbns and used by truncate and bmap code.
+ */
+struct indir {
+ daddr_t in_lbn; /* Logical block number. */
+ int in_off; /* Offset in buffer. */
+ int in_exists; /* Flag if the block exists. */
+};
+
+/* Convert between inode pointers and vnode pointers. */
+#define VTOI(vp) ((struct inode *)(vp)->v_data)
+#define ITOV(ip) ((ip)->i_vnode)
+
+#define ITIMES(ip, t1, t2) { \
+ if ((ip)->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) { \
+ (ip)->i_flag |= IN_MODIFIED; \
+ if ((ip)->i_flag & IN_ACCESS) \
+ (ip)->i_atime.ts_sec = (t1)->tv_sec; \
+ if ((ip)->i_flag & IN_UPDATE) { \
+ (ip)->i_mtime.ts_sec = (t2)->tv_sec; \
+ (ip)->i_modrev++; \
+ } \
+ if ((ip)->i_flag & IN_CHANGE) \
+ (ip)->i_ctime.ts_sec = time.tv_sec; \
+ (ip)->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE); \
+ } \
+}
+
+/* This overlays the fid structure (see mount.h). */
+struct ufid {
+ u_short ufid_len; /* Length of structure. */
+ u_short ufid_pad; /* Force long alignment. */
+ ino_t ufid_ino; /* File number (ino). */
+ long ufid_gen; /* Generation number. */
+};
+#endif /* KERNEL */
diff --git a/sys/gnu/fs/ext2fs/ext2_bmap.c b/sys/gnu/fs/ext2fs/ext2_bmap.c
new file mode 100644
index 0000000..a424d31
--- /dev/null
+++ b/sys/gnu/fs/ext2fs/ext2_bmap.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_bmap.c 8.6 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/resourcevar.h>
+#include <sys/trace.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+/*
+ * Bmap converts a the logical block number of a file to its physical block
+ * number on the disk. The conversion is done by using the logical block
+ * number to index into the array of block pointers described by the dinode.
+ */
+int
+ufs_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap;
+{
+ /*
+ * Check for underlying vnode requests and ensure that logical
+ * to physical mapping is requested.
+ */
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = VTOI(ap->a_vp)->i_devvp;
+ if (ap->a_bnp == NULL)
+ return (0);
+
+ return (ufs_bmaparray(ap->a_vp, ap->a_bn, ap->a_bnp, NULL, NULL,
+ ap->a_runp));
+}
+
+/*
+ * Indirect blocks are now on the vnode for the file. They are given negative
+ * logical block numbers. Indirect blocks are addressed by the negative
+ * address of the first data block to which they point. Double indirect blocks
+ * are addressed by one less than the address of the first indirect block to
+ * which they point. Triple indirect blocks are addressed by one less than
+ * the address of the first double indirect block to which they point.
+ *
+ * ufs_bmaparray does the bmap conversion, and if requested returns the
+ * array of logical blocks which must be traversed to get to a block.
+ * Each entry contains the offset into that block that gets you to the
+ * next block and the disk address of the block (if it is assigned).
+ */
+
+int
+ufs_bmaparray(vp, bn, bnp, ap, nump, runp)
+ struct vnode *vp;
+ register daddr_t bn;
+ daddr_t *bnp;
+ struct indir *ap;
+ int *nump;
+ int *runp;
+{
+ register struct inode *ip;
+ struct buf *bp;
+ struct ufsmount *ump;
+ struct mount *mp;
+ struct vnode *devvp;
+ struct indir a[NIADDR], *xap;
+ daddr_t daddr;
+ long metalbn;
+ int error, maxrun = 0, num;
+
+ ip = VTOI(vp);
+ mp = vp->v_mount;
+ ump = VFSTOUFS(mp);
+#ifdef DIAGNOSTIC
+ if (ap != NULL && nump == NULL || ap == NULL && nump != NULL)
+ panic("ufs_bmaparray: invalid arguments");
+#endif
+
+ if (runp) {
+ /*
+ * XXX
+ * If MAXBSIZE is the largest transfer the disks can handle,
+ * we probably want maxrun to be 1 block less so that we
+ * don't create a block larger than the device can handle.
+ */
+ *runp = 0;
+ maxrun = MAXBSIZE / mp->mnt_stat.f_iosize - 1;
+ }
+
+ xap = ap == NULL ? a : ap;
+ if (!nump)
+ nump = &num;
+ if (error = ufs_getlbns(vp, bn, xap, nump))
+ return (error);
+
+ num = *nump;
+ if (num == 0) {
+ *bnp = blkptrtodb(ump, ip->i_db[bn]);
+ if (*bnp == 0)
+ *bnp = -1;
+ else if (runp)
+ for (++bn; bn < NDADDR && *runp < maxrun &&
+ is_sequential(ump, ip->i_db[bn - 1], ip->i_db[bn]);
+ ++bn, ++*runp);
+ return (0);
+ }
+
+
+ /* Get disk address out of indirect block array */
+ daddr = ip->i_ib[xap->in_off];
+
+ devvp = VFSTOUFS(vp->v_mount)->um_devvp;
+ for (bp = NULL, ++xap; --num; ++xap) {
+ /*
+ * Exit the loop if there is no disk address assigned yet and
+ * the indirect block isn't in the cache, or if we were
+ * looking for an indirect block and we've found it.
+ */
+
+ metalbn = xap->in_lbn;
+ if (daddr == 0 && !incore(vp, metalbn) || metalbn == bn)
+ break;
+ /*
+ * If we get here, we've either got the block in the cache
+ * or we have a disk address for it, go fetch it.
+ */
+ if (bp)
+ brelse(bp);
+
+ xap->in_exists = 1;
+ bp = getblk(vp, metalbn, mp->mnt_stat.f_iosize, 0, 0);
+ if (bp->b_flags & (B_DONE | B_DELWRI)) {
+ trace(TR_BREADHIT, pack(vp, size), metalbn);
+ }
+#ifdef DIAGNOSTIC
+ else if (!daddr)
+ panic("ufs_bmaparry: indirect block not in cache");
+#endif
+ else {
+ trace(TR_BREADMISS, pack(vp, size), metalbn);
+ bp->b_blkno = blkptrtodb(ump, daddr);
+ bp->b_flags |= B_READ;
+ VOP_STRATEGY(bp);
+ curproc->p_stats->p_ru.ru_inblock++; /* XXX */
+ if (error = biowait(bp)) {
+ brelse(bp);
+ return (error);
+ }
+ }
+
+ daddr = ((daddr_t *)bp->b_data)[xap->in_off];
+ if (num == 1 && daddr && runp)
+ for (bn = xap->in_off + 1;
+ bn < MNINDIR(ump) && *runp < maxrun &&
+ is_sequential(ump, ((daddr_t *)bp->b_data)[bn - 1],
+ ((daddr_t *)bp->b_data)[bn]);
+ ++bn, ++*runp);
+ }
+ if (bp)
+ brelse(bp);
+
+ daddr = blkptrtodb(ump, daddr);
+ *bnp = daddr == 0 ? -1 : daddr;
+ return (0);
+}
+
+/*
+ * Create an array of logical block number/offset pairs which represent the
+ * path of indirect blocks required to access a data block. The first "pair"
+ * contains the logical block number of the appropriate single, double or
+ * triple indirect block and the offset into the inode indirect block array.
+ * Note, the logical block number of the inode single/double/triple indirect
+ * block appears twice in the array, once with the offset into the i_ib and
+ * once with the offset into the page itself.
+ */
+int
+ufs_getlbns(vp, bn, ap, nump)
+ struct vnode *vp;
+ register daddr_t bn;
+ struct indir *ap;
+ int *nump;
+{
+ long metalbn, realbn;
+ struct ufsmount *ump;
+ int blockcnt, i, numlevels, off;
+
+ ump = VFSTOUFS(vp->v_mount);
+ if (nump)
+ *nump = 0;
+ numlevels = 0;
+ realbn = bn;
+ if ((long)bn < 0)
+ bn = -(long)bn;
+
+ /* The first NDADDR blocks are direct blocks. */
+ if (bn < NDADDR)
+ return (0);
+
+ /*
+ * Determine the number of levels of indirection. After this loop
+ * is done, blockcnt indicates the number of data blocks possible
+ * at the given level of indirection, and NIADDR - i is the number
+ * of levels of indirection needed to locate the requested block.
+ */
+ for (blockcnt = 1, i = NIADDR, bn -= NDADDR;; i--, bn -= blockcnt) {
+ if (i == 0)
+ return (EFBIG);
+ blockcnt *= MNINDIR(ump);
+ if (bn < blockcnt)
+ break;
+ }
+
+ /* Calculate the address of the first meta-block. */
+ if (realbn >= 0)
+ metalbn = -(realbn - bn + NIADDR - i);
+ else
+ metalbn = -(-realbn - bn + NIADDR - i);
+
+ /*
+ * At each iteration, off is the offset into the bap array which is
+ * an array of disk addresses at the current level of indirection.
+ * The logical block number and the offset in that block are stored
+ * into the argument array.
+ */
+ ap->in_lbn = metalbn;
+ ap->in_off = off = NIADDR - i;
+ ap->in_exists = 0;
+ ap++;
+ for (++numlevels; i <= NIADDR; i++) {
+ /* If searching for a meta-data block, quit when found. */
+ if (metalbn == realbn)
+ break;
+
+ blockcnt /= MNINDIR(ump);
+ off = (bn / blockcnt) % MNINDIR(ump);
+
+ ++numlevels;
+ ap->in_lbn = metalbn;
+ ap->in_off = off;
+ ap->in_exists = 0;
+ ++ap;
+
+ metalbn -= -1 + off * blockcnt;
+ }
+ if (nump)
+ *nump = numlevels;
+ return (0);
+}
diff --git a/sys/gnu/fs/ext2fs/ext2_mount.h b/sys/gnu/fs/ext2fs/ext2_mount.h
new file mode 100644
index 0000000..237871f
--- /dev/null
+++ b/sys/gnu/fs/ext2fs/ext2_mount.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufsmount.h 8.2 (Berkeley) 1/12/94
+ */
+
+struct buf;
+struct inode;
+struct nameidata;
+struct timeval;
+struct ucred;
+struct uio;
+struct vnode;
+struct netexport;
+
+/* This structure describes the UFS specific mount structure data. */
+struct ufsmount {
+ struct mount *um_mountp; /* filesystem vfs structure */
+ dev_t um_dev; /* device mounted */
+ struct vnode *um_devvp; /* block device mounted vnode */
+ union { /* pointer to superblock */
+ struct lfs *lfs; /* LFS */
+ struct fs *fs; /* FFS */
+ } ufsmount_u;
+#define um_fs ufsmount_u.fs
+#define um_lfs ufsmount_u.lfs
+ struct vnode *um_quotas[MAXQUOTAS]; /* pointer to quota files */
+ struct ucred *um_cred[MAXQUOTAS]; /* quota file access cred */
+ u_long um_nindir; /* indirect ptrs per block */
+ u_long um_bptrtodb; /* indir ptr to disk block */
+ u_long um_seqinc; /* inc between seq blocks */
+ time_t um_btime[MAXQUOTAS]; /* block quota time limit */
+ time_t um_itime[MAXQUOTAS]; /* inode quota time limit */
+ char um_qflags[MAXQUOTAS]; /* quota specific flags */
+ struct netexport um_export; /* export information */
+};
+/*
+ * Flags describing the state of quotas.
+ */
+#define QTF_OPENING 0x01 /* Q_QUOTAON in progress */
+#define QTF_CLOSING 0x02 /* Q_QUOTAOFF in progress */
+
+/* Convert mount ptr to ufsmount ptr. */
+#define VFSTOUFS(mp) ((struct ufsmount *)((mp)->mnt_data))
+
+/*
+ * Macros to access file system parameters in the ufsmount structure.
+ * Used by ufs_bmap.
+ */
+#define blkptrtodb(ump, b) ((b) << (ump)->um_bptrtodb)
+#define is_sequential(ump, a, b) ((b) == (a) + ump->um_seqinc)
+#define MNINDIR(ump) ((ump)->um_nindir)
+
+
diff --git a/sys/gnu/fs/ext2fs/inode.h b/sys/gnu/fs/ext2fs/inode.h
new file mode 100644
index 0000000..df15596
--- /dev/null
+++ b/sys/gnu/fs/ext2fs/inode.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 1982, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)inode.h 8.4 (Berkeley) 1/21/94
+ */
+
+#include <ufs/ufs/dinode.h>
+
+/*
+ * Theoretically, directories can be more than 2Gb in length, however, in
+ * practice this seems unlikely. So, we define the type doff_t as a long
+ * to keep down the cost of doing lookup on a 32-bit machine. If you are
+ * porting to a 64-bit architecture, you should make doff_t the same as off_t.
+ */
+#define doff_t long
+
+/*
+ * The inode is used to describe each active (or recently active)
+ * file in the UFS filesystem. It is composed of two types of
+ * information. The first part is the information that is needed
+ * only while the file is active (such as the identity of the file
+ * and linkage to speed its lookup). The second part is the
+ * permannent meta-data associated with the file which is read
+ * in from the permanent dinode from long term storage when the
+ * file becomes active, and is put back when the file is no longer
+ * being used.
+ */
+struct inode {
+ struct inode *i_next; /* Hash chain forward. */
+ struct inode **i_prev; /* Hash chain back. */
+ struct vnode *i_vnode; /* Vnode associated with this inode. */
+ struct vnode *i_devvp; /* Vnode for block I/O. */
+ u_long i_flag; /* I* flags. */
+ dev_t i_dev; /* Device associated with the inode. */
+ ino_t i_number; /* The identity of the inode. */
+ union { /* Associated filesystem. */
+ struct fs *fs; /* FFS */
+ struct lfs *lfs; /* LFS */
+ } inode_u;
+#define i_fs inode_u.fs
+#define i_lfs inode_u.lfs
+ struct dquot *i_dquot[MAXQUOTAS]; /* Dquot structures. */
+ u_quad_t i_modrev; /* Revision level for lease. */
+ struct lockf *i_lockf; /* Head of byte-level lock list. */
+ pid_t i_lockholder; /* DEBUG: holder of inode lock. */
+ pid_t i_lockwaiter; /* DEBUG: latest blocked for inode lock. */
+ /*
+ * Side effects; used during directory lookup.
+ */
+ long i_count; /* Size of free slot in directory. */
+ doff_t i_endoff; /* End of useful stuff in directory. */
+ doff_t i_diroff; /* Offset in dir, where we found last entry. */
+ doff_t i_offset; /* Offset of free space in directory. */
+ ino_t i_ino; /* Inode number of found directory. */
+ u_long i_reclen; /* Size of found directory entry. */
+ long i_spare[11]; /* Spares to round up to 128 bytes. */
+ /*
+ * The on-disk dinode itself.
+ */
+ struct dinode i_din; /* 128 bytes of the on-disk dinode. */
+};
+
+#define i_atime i_din.di_atime
+#define i_blocks i_din.di_blocks
+#define i_ctime i_din.di_ctime
+#define i_db i_din.di_db
+#define i_flags i_din.di_flags
+#define i_gen i_din.di_gen
+#define i_gid i_din.di_gid
+#define i_ib i_din.di_ib
+#define i_mode i_din.di_mode
+#define i_mtime i_din.di_mtime
+#define i_nlink i_din.di_nlink
+#define i_rdev i_din.di_rdev
+#define i_shortlink i_din.di_shortlink
+#define i_size i_din.di_size
+#define i_uid i_din.di_uid
+
+/* These flags are kept in i_flag. */
+#define IN_ACCESS 0x0001 /* Access time update request. */
+#define IN_CHANGE 0x0002 /* Inode change time update request. */
+#define IN_EXLOCK 0x0004 /* File has exclusive lock. */
+#define IN_LOCKED 0x0008 /* Inode lock. */
+#define IN_LWAIT 0x0010 /* Process waiting on file lock. */
+#define IN_MODIFIED 0x0020 /* Inode has been modified. */
+#define IN_RENAME 0x0040 /* Inode is being renamed. */
+#define IN_SHLOCK 0x0080 /* File has shared lock. */
+#define IN_UPDATE 0x0100 /* Modification time update request. */
+#define IN_WANTED 0x0200 /* Inode is wanted by a process. */
+
+#ifdef KERNEL
+/*
+ * Structure used to pass around logical block paths generated by
+ * ufs_getlbns and used by truncate and bmap code.
+ */
+struct indir {
+ daddr_t in_lbn; /* Logical block number. */
+ int in_off; /* Offset in buffer. */
+ int in_exists; /* Flag if the block exists. */
+};
+
+/* Convert between inode pointers and vnode pointers. */
+#define VTOI(vp) ((struct inode *)(vp)->v_data)
+#define ITOV(ip) ((ip)->i_vnode)
+
+#define ITIMES(ip, t1, t2) { \
+ if ((ip)->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) { \
+ (ip)->i_flag |= IN_MODIFIED; \
+ if ((ip)->i_flag & IN_ACCESS) \
+ (ip)->i_atime.ts_sec = (t1)->tv_sec; \
+ if ((ip)->i_flag & IN_UPDATE) { \
+ (ip)->i_mtime.ts_sec = (t2)->tv_sec; \
+ (ip)->i_modrev++; \
+ } \
+ if ((ip)->i_flag & IN_CHANGE) \
+ (ip)->i_ctime.ts_sec = time.tv_sec; \
+ (ip)->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE); \
+ } \
+}
+
+/* This overlays the fid structure (see mount.h). */
+struct ufid {
+ u_short ufid_len; /* Length of structure. */
+ u_short ufid_pad; /* Force long alignment. */
+ ino_t ufid_ino; /* File number (ino). */
+ long ufid_gen; /* Generation number. */
+};
+#endif /* KERNEL */
diff --git a/sys/gnu/i386/fpemul/Changelog b/sys/gnu/i386/fpemul/Changelog
new file mode 100644
index 0000000..a2fbccd1
--- /dev/null
+++ b/sys/gnu/i386/fpemul/Changelog
@@ -0,0 +1,36 @@
+This file contains the changes made to W. Metzenthem's 387 FPU
+emulator to make it work under NetBSD.
+
+a, Changes to make it compile:
+
+ 1 - Changed the #include's to get the appropriate .h files.
+ 2 - Renamed .S to .s, to satisfy the kernel Makefile.
+ 3 - Changed the C++ style // comments to /* */
+ 4 - Changed the FPU_ORIG_EIP macro. A letter from bde included
+ in the package suggested using tf_isp for using instead
+ of the linux __orig_eip. This later turned out to interfere
+ with the user stack, so i created a separate variable, stored
+ in the i387_union.
+ 5 - Changed the get_fs_.. put_fs_.. fns to fubyte,fuword,subyte,
+ suword.
+ 6 - Removed the verify_area fns. I don't really know what they do,
+ i suppose they verify access to memory. The sufu routines
+ should do this.
+
+b, Changes to make it work:
+
+ 1 - Made math_emulate() to return 0 when successful, so trap() won't
+ try to generate a signal.
+ 2 - Changed the size of the save87 struct in /sys/arch/i387/include/
+ npx.h to accomodate the i387_union.
+
+d, Other changes:
+
+ 1 - Removed obsolate and/or linux specific stuff.
+ 2 - Changed the RE_ENTRANT_CHECK_[ON|OFF] macro to
+ REENTRANT_CHECK([ON|OFF]) so indent can grok it.
+ 3 - Re-indented to Berkeley style.
+ 4 - Limited max no of lookaheads. LOOKAHEAD_LIMIT in fpu_entry.c
+
+
+ Szabolcs Szigeti (pink@fsz.bme.hu)
diff --git a/sys/gnu/i386/fpemul/README b/sys/gnu/i386/fpemul/README
new file mode 100644
index 0000000..0cef6c4
--- /dev/null
+++ b/sys/gnu/i386/fpemul/README
@@ -0,0 +1,277 @@
+/*
+ * wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ */
+
+wm-FPU-emu is an FPU emulator for Linux. It is derived from wm-emu387
+which is my 80387 emulator for djgpp (gcc under msdos); wm-emu387 was
+in turn based upon emu387 which was written by DJ Delorie for djgpp.
+The interface to the Linux kernel is based upon the original Linux
+math emulator by Linus Torvalds.
+
+My target FPU for wm-FPU-emu is that described in the Intel486
+Programmer's Reference Manual (1992 edition). Numerous facets of the
+functioning of the FPU are not well covered in the Reference Manual;
+in the absence of clear details I have made guesses about the most
+reasonable behaviour. Recently, this situation has improved because
+I now have some access to the results produced by a real 80486 FPU.
+
+wm-FPU-emu does not implement all of the behaviour of the 80486 FPU.
+See "Limitations" later in this file for a partial list of some
+differences. I believe that the missing features are never used by
+normal C or FORTRAN programs.
+
+
+Please report bugs, etc to me at:
+ apm233m@vaxc.cc.monash.edu.au
+
+
+--Bill Metzenthen
+ May 1993
+
+
+----------------------- Internals of wm-FPU-emu -----------------------
+
+Numeric algorithms:
+(1) Add, subtract, and multiply. Nothing remarkable in these.
+(2) Divide has been tuned to get reasonable performance. The algorithm
+ is not the obvious one which most people seem to use, but is designed
+ to take advantage of the characteristics of the 80386. I expect that
+ it has been invented many times before I discovered it, but I have not
+ seen it. It is based upon one of those ideas which one carries around
+ for years without ever bothering to check it out.
+(3) The sqrt function has been tuned to get good performance. It is based
+ upon Newton's classic method. Performance was improved by capitalizing
+ upon the properties of Newton's method, and the code is once again
+ structured taking account of the 80386 characteristics.
+(4) The trig, log, and exp functions are based in each case upon quasi-
+ "optimal" polynomial approximations. My definition of "optimal" was
+ based upon getting good accuracy with reasonable speed.
+
+The code of the emulator is complicated slightly by the need to
+account for a limited form of re-entrancy. Normally, the emulator will
+emulate each FPU instruction to completion without interruption.
+However, it may happen that when the emulator is accessing the user
+memory space, swapping may be needed. In this case the emulator may be
+temporarily suspended while disk i/o takes place. During this time
+another process may use the emulator, thereby changing some static
+variables (eg FPU_st0_ptr, etc). The code which accesses user memory
+is confined to five files:
+ fpu_entry.c
+ reg_ld_str.c
+ load_store.c
+ get_address.c
+ errors.c
+
+----------------------- Limitations of wm-FPU-emu -----------------------
+
+There are a number of differences between the current wm-FPU-emu
+(version beta 1.4) and the 80486 FPU (apart from bugs). Some of the
+more important differences are listed below:
+
+All internal computations are performed at 64 bit or higher precision
+and rounded etc as required by the PC bits of the FPU control word.
+Under the crt0 version for Linux current at March 1993, the FPU PC
+bits specify 53 bits precision.
+
+The precision flag (PE of the FPU status word) and the Roundup flag
+(C1 of the status word) are now partially implemented. Does anyone
+write code which uses these features?
+
+The functions which load/store the FPU state are partially implemented,
+but the implementation should be sufficient for handling FPU errors etc
+in 32 bit protected mode.
+
+The implementation of the exception mechanism is flawed for unmasked
+interrupts.
+
+Detection of certain conditions, such as denormal operands, is not yet
+complete.
+
+----------------------- Performance of wm-FPU-emu -----------------------
+
+Speed.
+-----
+
+The speed of floating point computation with the emulator will depend
+upon instruction mix. Relative performance is best for the instructions
+which require most computation. The simple instructions are adversely
+affected by the fpu instruction trap overhead.
+
+
+Timing: Some simple timing tests have been made on the emulator functions.
+The times include load/store instructions. All times are in microseconds
+measured on a 33MHz 386 with 64k cache. The Turbo C tests were under
+ms-dos, the next two columns are for emulators running with the djgpp
+ms-dos extender. The final column is for wm-FPU-emu in Linux 0.97,
+using libm4.0 (hard).
+
+function Turbo C djgpp 1.06 WM-emu387 wm-FPU-emu
+
+ + 60.5 154.8 76.5 139.4
+ - 61.1-65.5 157.3-160.8 76.2-79.5 142.9-144.7
+ * 71.0 190.8 79.6 146.6
+ / 61.2-75.0 261.4-266.9 75.3-91.6 142.2-158.1
+
+ sin() 310.8 4692.0 319.0 398.5
+ cos() 284.4 4855.2 308.0 388.7
+ tan() 495.0 8807.1 394.9 504.7
+ atan() 328.9 4866.4 601.1 419.5-491.9
+
+ sqrt() 128.7 crashed 145.2 227.0
+ log() 413.1-419.1 5103.4-5354.21 254.7-282.2 409.4-437.1
+ exp() 479.1 6619.2 469.1 850.8
+
+
+The performance under Linux is improved by the use of look-ahead code.
+The following results show the improvement which is obtained under
+Linux due to the look-ahead code. Also given are the times for the
+original Linux emulator with the 4.1 'soft' lib.
+
+ [ Linus' note: I changed look-ahead to be the default under linux, as
+ there was no reason not to use it after I had edited it to be
+ disabled during tracing ]
+
+ wm-FPU-emu w original w
+ look-ahead 'soft' lib
+ + 106.4 190.2
+ - 108.6-111.6 192.4-216.2
+ * 113.4 193.1
+ / 108.8-124.4 700.1-706.2
+
+ sin() 390.5 2642.0
+ cos() 381.5 2767.4
+ tan() 496.5 3153.3
+ atan() 367.2-435.5 2439.4-3396.8
+
+ sqrt() 195.1 4732.5
+ log() 358.0-387.5 3359.2-3390.3
+ exp() 619.3 4046.4
+
+
+These figures are now somewhat out-of-date. The emulator has become
+progressively slower for most functions as more of the 80486 features
+have been implemented.
+
+
+----------------------- Accuracy of wm-FPU-emu -----------------------
+
+
+Accuracy: The following table gives the accuracy of the sqrt(), trig
+and log functions. Each function was tested at about 400 points. Ideal
+results would be 64 bits. The reduced accuracy of cos() and tan() for
+arguments greater than pi/4 can be thought of as being due to the
+precision of the argument x; e.g. an argument of pi/2-(1e-10) which is
+accurate to 64 bits can result in a relative accuracy in cos() of about
+64 + log2(cos(x)) = 31 bits. Results for the Turbo C emulator are given
+in the last column.
+
+
+Function Tested x range Worst result (bits) Turbo C
+
+sqrt(x) 1 .. 2 64.1 63.2
+atan(x) 1e-10 .. 200 62.6 62.8
+cos(x) 0 .. pi/2-(1e-10) 63.2 (x <= pi/4) 62.4
+ 35.2 (x = pi/2-(1e-10)) 31.9
+sin(x) 1e-10 .. pi/2 63.0 62.8
+tan(x) 1e-10 .. pi/2-(1e-10) 62.4 (x <= pi/4) 62.1
+ 35.2 (x = pi/2-(1e-10)) 31.9
+exp(x) 0 .. 1 63.1 62.9
+log(x) 1+1e-6 .. 2 62.4 62.1
+
+
+As of version 1.3 of the emulator, the accuracy of the basic
+arithmetic has been improved (by a small fraction of a bit). Care has
+been taken to ensure full accuracy of the rounding of the basic
+arithmetic functions (+,-,*,/,and fsqrt), and they all now produce
+results which are exact to the 64th bit (unless there are any bugs
+left). To ensure this, it was necessary to effectively get information
+of up to about 128 bits precision. The emulator now passes the
+"paranoia" tests (compiled with gcc 2.3.3) for 'float' variables (24
+bit precision numbers) when precision control is set to 24, 53 or 64
+bits, and for 'double' variables (53 bit precision numbers) when
+precision control is set to 53 bits (a properly performing FPU cannot
+pass the 'paranoia' tests for 'double' variables when precision
+control is set to 64 bits).
+
+------------------------- Contributors -------------------------------
+
+A number of people have contributed to the development of the
+emulator, often by just reporting bugs, sometimes with a suggested
+fix, and a few kind people have provided me with access in one way or
+another to an 80486 machine. Contributors include (to those people who
+I have forgotten, please excuse me):
+
+Linus Torvalds
+Tommy.Thorn@daimi.aau.dk
+Andrew.Tridgell@anu.edu.au
+Nick Holloway alfie@dcs.warwick.ac.uk
+Hermano Moura moura@dcs.gla.ac.uk
+Jon Jagger J.Jagger@scp.ac.uk
+Lennart Benschop
+Brian Gallew geek+@CMU.EDU
+Thomas Staniszewski ts3v+@andrew.cmu.edu
+Martin Howell mph@plasma.apana.org.au
+M Saggaf alsaggaf@athena.mit.edu
+Peter Barker PETER@socpsy.sci.fau.edu
+tom@vlsivie.tuwien.ac.at
+Dan Russel russed@rpi.edu
+Daniel Carosone danielce@ee.mu.oz.au
+cae@jpmorgan.com
+Hamish Coleman t933093@minyos.xx.rmit.oz.au
+
+...and numerous others who responded to my request for help with
+a real 80486.
+
diff --git a/sys/gnu/i386/fpemul/bde_trapinfo.mail b/sys/gnu/i386/fpemul/bde_trapinfo.mail
new file mode 100644
index 0000000..2749e04
--- /dev/null
+++ b/sys/gnu/i386/fpemul/bde_trapinfo.mail
@@ -0,0 +1,35 @@
+From bde@kralizec.zeta.org.au Sun Jun 27 01:18:32 1993
+Received: from ultima.socs.uts.EDU.AU by bsd.coe.montana.edu (5.67/KAOS-1)
+ id AA11952; Sun, 27 Jun 93 01:18:32 -0600
+Received: by ultima.socs.uts.EDU.AU (5.65+/SMI-3.3)
+ id AA03033; Sun, 27 Jun 93 17:10:22 +1000
+Received: by kralizec.zeta.org.au (4.0/SMI-4.0)
+ id AA15074; Sat, 26 Jun 93 02:32:58 EST
+Date: Sat, 26 Jun 93 02:32:58 EST
+From: bde@kralizec.zeta.org.au (Bruce Evans)
+Message-Id: <9306251632.AA15074@kralizec.zeta.org.au>
+To: nate@bsd.coe.montana.edu
+Subject: Re: Trapframe information
+Status: OR
+
+tf_isp original esp (probably spare - popal ignores it)
+tf_trapno s/w trap no (may be spare - trap.c has already looked at it)
+tf_err h/w error code (probably spare - gets discarded before iret)
+
+___fs not stored in 386BSD pcb. Constant anyway unless user has
+ screwed with it (?).
+___gs ditto
+___orig_eip in linux, this is on the stack just before the call to the
+ emulator. The reason that it's not a local variable is to
+ avoid passing around pointers to it - current->frame (or
+ whatever) points to everything in the stack frame. The
+ macros hide a lot of slow memory references
+ current->frame->var.
+
+>(And I need to see if I can map orig_eip to one of the three that I'm unsure of
+>in the BSD sources)
+
+tf_isp is the least evil.
+
+Bruce
+
diff --git a/sys/gnu/i386/fpemul/control_w.h b/sys/gnu/i386/fpemul/control_w.h
new file mode 100644
index 0000000..d7ede07
--- /dev/null
+++ b/sys/gnu/i386/fpemul/control_w.h
@@ -0,0 +1,95 @@
+/*
+ * control_w.h
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: control_w.h,v 1.2 1994/04/29 21:02:30 gclarkii Exp $
+ *
+ */
+
+#ifndef _CONTROLW_H_
+#define _CONTROLW_H_
+
+#ifdef LOCORE
+#define _Const_(x) $/**/x
+#else
+#define _Const_(x) x
+#endif
+
+#define CW_RC _Const_(0x0C00) /* rounding control */
+#define CW_PC _Const_(0x0300) /* precision control */
+
+#define CW_Precision Const_(0x0020) /* loss of precision mask */
+#define CW_Underflow Const_(0x0010) /* underflow mask */
+#define CW_Overflow Const_(0x0008) /* overflow mask */
+#define CW_ZeroDiv Const_(0x0004) /* divide by zero mask */
+#define CW_Denormal Const_(0x0002) /* denormalized operand mask */
+#define CW_Invalid Const_(0x0001) /* invalid operation mask */
+
+#define CW_Exceptions _Const_(0x003f) /* all masks */
+
+#define RC_RND _Const_(0x0000)
+#define RC_DOWN _Const_(0x0400)
+#define RC_UP _Const_(0x0800)
+#define RC_CHOP _Const_(0x0C00)
+
+/* p 15-5: Precision control bits affect only the following:
+ ADD, SUB(R), MUL, DIV(R), and SQRT */
+#define PR_24_BITS _Const_(0x000)
+#define PR_53_BITS _Const_(0x200)
+#define PR_64_BITS _Const_(0x300)
+/* FULL_PRECISION simulates all exceptions masked */
+#define FULL_PRECISION (PR_64_BITS | RC_RND | 0x3f)
+
+#endif /* _CONTROLW_H_ */
diff --git a/sys/gnu/i386/fpemul/div_small.s b/sys/gnu/i386/fpemul/div_small.s
new file mode 100644
index 0000000..d25313b
--- /dev/null
+++ b/sys/gnu/i386/fpemul/div_small.s
@@ -0,0 +1,101 @@
+ .file "div_small.S"
+/*
+ * div_small.S
+ *
+ * Divide a 64 bit integer by a 32 bit integer & return remainder.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: div_small.s,v 1.2 1994/04/29 21:07:11 gclarkii Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | unsigned long div_small(unsigned long long *x, unsigned long y) |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_asm.h"
+
+.text
+ .align 2,144
+
+.globl _div_small
+
+_div_small:
+ pushl %ebp
+ movl %esp,%ebp
+
+ pushl %esi
+
+ movl PARAM1,%esi /* pointer to num */
+ movl PARAM2,%ecx /* The denominator */
+
+ movl 4(%esi),%eax /* Get the current num msw */
+ xorl %edx,%edx
+ divl %ecx
+
+ movl %eax,4(%esi)
+
+ movl (%esi),%eax /* Get the num lsw */
+ divl %ecx
+
+ movl %eax,(%esi)
+
+ movl %edx,%eax /* Return the remainder in eax */
+
+ popl %esi
+
+ leave
+ ret
+
diff --git a/sys/gnu/i386/fpemul/errors.c b/sys/gnu/i386/fpemul/errors.c
new file mode 100644
index 0000000..f9f6eb1
--- /dev/null
+++ b/sys/gnu/i386/fpemul/errors.c
@@ -0,0 +1,612 @@
+/*
+ * errors.c
+ *
+ * The error handling functions for wm-FPU-emu
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: errors.c,v 1.2 1994/04/29 21:07:12 gclarkii Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | Note: |
+ | The file contains code which accesses user memory. |
+ | Emulator static data may change when user memory is accessed, due to |
+ | other processes using the emulator while swapping is in progress. |
+ +---------------------------------------------------------------------------*/
+
+
+
+
+
+#include "param.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+#include "status_w.h"
+#include "control_w.h"
+#include "reg_constant.h"
+#include "version.h"
+
+/* */
+#undef PRINT_MESSAGES
+/* */
+
+
+void
+Un_impl(void)
+{
+ unsigned char byte1, FPU_modrm;
+
+ REENTRANT_CHECK(OFF);
+ byte1 = fubyte((unsigned char *) FPU_ORIG_EIP);
+ FPU_modrm = fubyte(1 + (unsigned char *) FPU_ORIG_EIP);
+
+ printf("Unimplemented FPU Opcode at eip=%p : %02x ",
+ FPU_ORIG_EIP, byte1);
+
+ if (FPU_modrm >= 0300)
+ printf("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
+ else
+ printf("/%d\n", (FPU_modrm >> 3) & 7);
+ REENTRANT_CHECK(ON);
+
+ EXCEPTION(EX_Invalid);
+
+}
+
+
+
+
+void
+emu_printall()
+{
+ int i;
+ static char *tag_desc[] = {"Valid", "Zero", "ERROR", "ERROR",
+ "DeNorm", "Inf", "NaN", "Empty"};
+ unsigned char byte1, FPU_modrm;
+
+ REENTRANT_CHECK(OFF);
+ byte1 = fubyte((unsigned char *) FPU_ORIG_EIP);
+ FPU_modrm = fubyte(1 + (unsigned char *) FPU_ORIG_EIP);
+
+#ifdef DEBUGGING
+ if (status_word & SW_Backward)
+ printf("SW: backward compatibility\n");
+ if (status_word & SW_C3)
+ printf("SW: condition bit 3\n");
+ if (status_word & SW_C2)
+ printf("SW: condition bit 2\n");
+ if (status_word & SW_C1)
+ printf("SW: condition bit 1\n");
+ if (status_word & SW_C0)
+ printf("SW: condition bit 0\n");
+ if (status_word & SW_Summary)
+ printf("SW: exception summary\n");
+ if (status_word & SW_Stack_Fault)
+ printf("SW: stack fault\n");
+ if (status_word & SW_Precision)
+ printf("SW: loss of precision\n");
+ if (status_word & SW_Underflow)
+ printf("SW: underflow\n");
+ if (status_word & SW_Overflow)
+ printf("SW: overflow\n");
+ if (status_word & SW_Zero_Div)
+ printf("SW: divide by zero\n");
+ if (status_word & SW_Denorm_Op)
+ printf("SW: denormalized operand\n");
+ if (status_word & SW_Invalid)
+ printf("SW: invalid operation\n");
+#endif /* DEBUGGING */
+
+ status_word = status_word & ~SW_Top;
+ status_word |= (top & 7) << SW_Top_Shift;
+
+ printf("At %p: %02x ", FPU_ORIG_EIP, byte1);
+ if (FPU_modrm >= 0300)
+ printf("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
+ else
+ printf("/%d, mod=%d rm=%d\n",
+ (FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7);
+
+ printf(" SW: b=%d st=%d es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n",
+ status_word & 0x8000 ? 1 : 0, /* busy */
+ (status_word & 0x3800) >> 11, /* stack top pointer */
+ status_word & 0x80 ? 1 : 0, /* Error summary status */
+ status_word & 0x40 ? 1 : 0, /* Stack flag */
+ status_word & SW_C3 ? 1 : 0, status_word & SW_C2 ? 1 : 0, /* cc */
+ status_word & SW_C1 ? 1 : 0, status_word & SW_C0 ? 1 : 0, /* cc */
+ status_word & SW_Precision ? 1 : 0, status_word & SW_Underflow ? 1 : 0,
+ status_word & SW_Overflow ? 1 : 0, status_word & SW_Zero_Div ? 1 : 0,
+ status_word & SW_Denorm_Op ? 1 : 0, status_word & SW_Invalid ? 1 : 0);
+
+ printf(" CW: ic=%d rc=%d%d pc=%d%d iem=%d ef=%d%d%d%d%d%d\n",
+ control_word & 0x1000 ? 1 : 0,
+ (control_word & 0x800) >> 11, (control_word & 0x400) >> 10,
+ (control_word & 0x200) >> 9, (control_word & 0x100) >> 8,
+ control_word & 0x80 ? 1 : 0,
+ control_word & SW_Precision ? 1 : 0, control_word & SW_Underflow ? 1 : 0,
+ control_word & SW_Overflow ? 1 : 0, control_word & SW_Zero_Div ? 1 : 0,
+ control_word & SW_Denorm_Op ? 1 : 0, control_word & SW_Invalid ? 1 : 0);
+
+ for (i = 0; i < 8; i++) {
+ FPU_REG *r = &st(i);
+ switch (r->tag) {
+ case TW_Empty:
+ continue;
+ break;
+ case TW_Zero:
+ printf("st(%d) %c .0000 0000 0000 0000 ",
+ i, r->sign ? '-' : '+');
+ break;
+ case TW_Valid:
+ case TW_NaN:
+ case TW_Denormal:
+ case TW_Infinity:
+ printf("st(%d) %c .%04x %04x %04x %04x e%+-6d ", i,
+ r->sign ? '-' : '+',
+ (long) (r->sigh >> 16),
+ (long) (r->sigh & 0xFFFF),
+ (long) (r->sigl >> 16),
+ (long) (r->sigl & 0xFFFF),
+ r->exp - EXP_BIAS + 1);
+ break;
+ default:
+ printf("Whoops! Error in errors.c ");
+ break;
+ }
+ printf("%s\n", tag_desc[(int) (unsigned) r->tag]);
+ }
+
+ printf("[data] %c .%04x %04x %04x %04x e%+-6d ",
+ FPU_loaded_data.sign ? '-' : '+',
+ (long) (FPU_loaded_data.sigh >> 16),
+ (long) (FPU_loaded_data.sigh & 0xFFFF),
+ (long) (FPU_loaded_data.sigl >> 16),
+ (long) (FPU_loaded_data.sigl & 0xFFFF),
+ FPU_loaded_data.exp - EXP_BIAS + 1);
+ printf("%s\n", tag_desc[(int) (unsigned) FPU_loaded_data.tag]);
+ REENTRANT_CHECK(ON);
+
+}
+
+static struct {
+ int type;
+ char *name;
+} exception_names[] = {
+ {
+ EX_StackOver, "stack overflow"
+ },
+ {
+ EX_StackUnder, "stack underflow"
+ },
+ {
+ EX_Precision, "loss of precision"
+ },
+ {
+ EX_Underflow, "underflow"
+ },
+ {
+ EX_Overflow, "overflow"
+ },
+ {
+ EX_ZeroDiv, "divide by zero"
+ },
+ {
+ EX_Denormal, "denormalized operand"
+ },
+ {
+ EX_Invalid, "invalid operation"
+ },
+ {
+ EX_INTERNAL, "INTERNAL BUG in " FPU_VERSION
+ },
+ {
+ 0, NULL
+ }
+};
+/*
+ EX_INTERNAL is always given with a code which indicates where the
+ error was detected.
+
+ Internal error types:
+ 0x14 in e14.c
+ 0x1nn in a *.c file:
+ 0x101 in reg_add_sub.c
+ 0x102 in reg_mul.c
+ 0x103 in poly_sin.c
+ 0x104 in poly_tan.c
+ 0x105 in reg_mul.c
+ 0x106 in reg_mov.c
+ 0x107 in fpu_trig.c
+ 0x108 in reg_compare.c
+ 0x109 in reg_compare.c
+ 0x110 in reg_add_sub.c
+ 0x111 in interface.c
+ 0x112 in fpu_trig.c
+ 0x113 in reg_add_sub.c
+ 0x114 in reg_ld_str.c
+ 0x115 in fpu_trig.c
+ 0x116 in fpu_trig.c
+ 0x117 in fpu_trig.c
+ 0x118 in fpu_trig.c
+ 0x119 in fpu_trig.c
+ 0x120 in poly_atan.c
+ 0x121 in reg_compare.c
+ 0x122 in reg_compare.c
+ 0x123 in reg_compare.c
+ 0x2nn in an *.s file:
+ 0x201 in reg_u_add.S
+ 0x202 in reg_u_div.S
+ 0x203 in reg_u_div.S
+ 0x204 in reg_u_div.S
+ 0x205 in reg_u_mul.S
+ 0x206 in reg_u_sub.S
+ 0x207 in wm_sqrt.S
+ 0x208 in reg_div.S
+ 0x209 in reg_u_sub.S
+ 0x210 in reg_u_sub.S
+ 0x211 in reg_u_sub.S
+ 0x212 in reg_u_sub.S
+ 0x213 in wm_sqrt.S
+ 0x214 in wm_sqrt.S
+ 0x215 in wm_sqrt.S
+ 0x216 in reg_round.S
+ 0x217 in reg_round.S
+ 0x218 in reg_round.S
+ */
+
+void
+exception(int n)
+{
+ int i, int_type;
+
+ int_type = 0; /* Needed only to stop compiler warnings */
+ if (n & EX_INTERNAL) {
+ int_type = n - EX_INTERNAL;
+ n = EX_INTERNAL;
+ /* Set lots of exception bits! */
+ status_word |= (SW_Exc_Mask | SW_Summary | FPU_BUSY);
+ } else {
+ /* Extract only the bits which we use to set the status word */
+ n &= (SW_Exc_Mask);
+ /* Set the corresponding exception bit */
+ status_word |= n;
+ if (status_word & ~control_word & CW_Exceptions)
+ status_word |= SW_Summary;
+ if (n & (SW_Stack_Fault | EX_Precision)) {
+ if (!(n & SW_C1))
+ /* This bit distinguishes over- from underflow
+ * for a stack fault, and roundup from
+ * round-down for precision loss. */
+ status_word &= ~SW_C1;
+ }
+ }
+
+ REENTRANT_CHECK(OFF);
+ if ((~control_word & n & CW_Exceptions) || (n == EX_INTERNAL)) {
+#ifdef PRINT_MESSAGES
+ /* My message from the sponsor */
+ printf(FPU_VERSION " " __DATE__ " (C) W. Metzenthen.\n");
+#endif /* PRINT_MESSAGES */
+
+ /* Get a name string for error reporting */
+ for (i = 0; exception_names[i].type; i++)
+ if ((exception_names[i].type & n) == exception_names[i].type)
+ break;
+
+ if (exception_names[i].type) {
+#ifdef PRINT_MESSAGES
+ printf("FP Exception: %s!\n", exception_names[i].name);
+#endif /* PRINT_MESSAGES */
+ } else
+ printf("FP emulator: Unknown Exception: 0x%04x!\n", n);
+
+ if (n == EX_INTERNAL) {
+ printf("FP emulator: Internal error type 0x%04x\n", int_type);
+ emu_printall();
+ }
+#ifdef PRINT_MESSAGES
+ else
+ emu_printall();
+#endif /* PRINT_MESSAGES */
+
+ /* The 80486 generates an interrupt on the next non-control
+ * FPU instruction. So we need some means of flagging it. We
+ * use the ES (Error Summary) bit for this, assuming that this
+ * is the way a real FPU does it (until I can check it out),
+ * if not, then some method such as the following kludge might
+ * be needed. */
+/* regs[0].tag |= TW_FPU_Interrupt; */
+ }
+ REENTRANT_CHECK(ON);
+
+#ifdef __DEBUG__
+ math_abort(SIGFPE);
+#endif /* __DEBUG__ */
+
+}
+
+
+/* Real operation attempted on two operands, one a NaN */
+void
+real_2op_NaN(FPU_REG * a, FPU_REG * b, FPU_REG * dest)
+{
+ FPU_REG *x;
+ int signalling;
+
+ x = a;
+ if (a->tag == TW_NaN) {
+ if (b->tag == TW_NaN) {
+ signalling = !(a->sigh & b->sigh & 0x40000000);
+ /* find the "larger" */
+ if (*(long long *) &(a->sigl) < *(long long *) &(b->sigl))
+ x = b;
+ } else {
+ /* return the quiet version of the NaN in a */
+ signalling = !(a->sigh & 0x40000000);
+ }
+ } else
+#ifdef PARANOID
+ if (b->tag == TW_NaN)
+#endif /* PARANOID */
+ {
+ signalling = !(b->sigh & 0x40000000);
+ x = b;
+ }
+#ifdef PARANOID
+ else {
+ signalling = 0;
+ EXCEPTION(EX_INTERNAL | 0x113);
+ x = &CONST_QNaN;
+ }
+#endif /* PARANOID */
+
+ if (!signalling) {
+ if (!(x->sigh & 0x80000000)) /* pseudo-NaN ? */
+ x = &CONST_QNaN;
+ reg_move(x, dest);
+ return;
+ }
+ if (control_word & CW_Invalid) {
+ /* The masked response */
+ if (!(x->sigh & 0x80000000)) /* pseudo-NaN ? */
+ x = &CONST_QNaN;
+ reg_move(x, dest);
+ /* ensure a Quiet NaN */
+ dest->sigh |= 0x40000000;
+ }
+ EXCEPTION(EX_Invalid);
+
+ return;
+}
+/* Invalid arith operation on Valid registers */
+void
+arith_invalid(FPU_REG * dest)
+{
+
+ if (control_word & CW_Invalid) {
+ /* The masked response */
+ reg_move(&CONST_QNaN, dest);
+ }
+ EXCEPTION(EX_Invalid);
+
+ return;
+
+}
+
+
+/* Divide a finite number by zero */
+void
+divide_by_zero(int sign, FPU_REG * dest)
+{
+
+ if (control_word & CW_ZeroDiv) {
+ /* The masked response */
+ reg_move(&CONST_INF, dest);
+ dest->sign = (unsigned char) sign;
+ }
+ EXCEPTION(EX_ZeroDiv);
+
+ return;
+
+}
+
+
+/* This may be called often, so keep it lean */
+void
+set_precision_flag_up(void)
+{
+ if (control_word & CW_Precision)
+ status_word |= (SW_Precision | SW_C1); /* The masked response */
+ else
+ exception(EX_Precision | SW_C1);
+
+}
+
+
+/* This may be called often, so keep it lean */
+void
+set_precision_flag_down(void)
+{
+ if (control_word & CW_Precision) { /* The masked response */
+ status_word &= ~SW_C1;
+ status_word |= SW_Precision;
+ } else
+ exception(EX_Precision);
+}
+
+
+int
+denormal_operand(void)
+{
+ if (control_word & CW_Denormal) { /* The masked response */
+ status_word |= SW_Denorm_Op;
+ return 0;
+ } else {
+ exception(EX_Denormal);
+ return 1;
+ }
+}
+
+
+void
+arith_overflow(FPU_REG * dest)
+{
+
+ if (control_word & CW_Overflow) {
+ char sign;
+ /* The masked response */
+/* **** The response here depends upon the rounding mode */
+ sign = dest->sign;
+ reg_move(&CONST_INF, dest);
+ dest->sign = sign;
+ } else {
+ /* Subtract the magic number from the exponent */
+ dest->exp -= (3 * (1 << 13));
+ }
+
+ /* By definition, precision is lost. It appears that the roundup bit
+ * (C1) is also set by convention. */
+ EXCEPTION(EX_Overflow | EX_Precision | SW_C1);
+
+ return;
+
+}
+
+
+void
+arith_underflow(FPU_REG * dest)
+{
+
+ if (control_word & CW_Underflow) {
+ /* The masked response */
+ if (dest->exp <= EXP_UNDER - 63)
+ reg_move(&CONST_Z, dest);
+ } else {
+ /* Add the magic number to the exponent */
+ dest->exp += (3 * (1 << 13));
+ }
+
+ EXCEPTION(EX_Underflow);
+
+ return;
+}
+
+
+void
+stack_overflow(void)
+{
+
+ if (control_word & CW_Invalid) {
+ /* The masked response */
+ top--;
+ reg_move(&CONST_QNaN, FPU_st0_ptr = &st(0));
+ }
+ EXCEPTION(EX_StackOver);
+
+ return;
+
+}
+
+
+void
+stack_underflow(void)
+{
+
+ if (control_word & CW_Invalid) {
+ /* The masked response */
+ reg_move(&CONST_QNaN, FPU_st0_ptr);
+ }
+ EXCEPTION(EX_StackUnder);
+
+ return;
+
+}
+
+
+void
+stack_underflow_i(int i)
+{
+
+ if (control_word & CW_Invalid) {
+ /* The masked response */
+ reg_move(&CONST_QNaN, &(st(i)));
+ }
+ EXCEPTION(EX_StackUnder);
+
+ return;
+
+}
+
+
+void
+stack_underflow_pop(int i)
+{
+
+ if (control_word & CW_Invalid) {
+ /* The masked response */
+ reg_move(&CONST_QNaN, &(st(i)));
+ pop();
+ }
+ EXCEPTION(EX_StackUnder);
+
+ return;
+
+}
diff --git a/sys/gnu/i386/fpemul/exception.h b/sys/gnu/i386/fpemul/exception.h
new file mode 100644
index 0000000..11f9d02
--- /dev/null
+++ b/sys/gnu/i386/fpemul/exception.h
@@ -0,0 +1,102 @@
+/*
+ * exception.h
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: exception.h,v 1.2 1994/04/29 21:07:13 gclarkii Exp $
+ *
+ *
+ */
+
+#ifndef _EXCEPTION_H_
+#define _EXCEPTION_H_
+
+
+#ifdef LOCORE
+#define Const_(x) $/**/x
+#else
+#define Const_(x) x
+#endif
+
+#ifndef SW_C1
+#include "fpu_emu.h"
+#endif /* SW_C1 */
+
+#define FPU_BUSY Const_(0x8000) /* FPU busy bit (8087 compatibility) */
+#define EX_ErrorSummary Const_(0x0080) /* Error summary status */
+/* Special exceptions: */
+#define EX_INTERNAL Const_(0x8000) /* Internal error in wm-FPU-emu */
+#define EX_StackOver Const_(0x0041|SW_C1) /* stack overflow */
+#define EX_StackUnder Const_(0x0041) /* stack underflow */
+/* Exception flags: */
+#define EX_Precision Const_(0x0020) /* loss of precision */
+#define EX_Underflow Const_(0x0010) /* underflow */
+#define EX_Overflow Const_(0x0008) /* overflow */
+#define EX_ZeroDiv Const_(0x0004) /* divide by zero */
+#define EX_Denormal Const_(0x0002) /* denormalized operand */
+#define EX_Invalid Const_(0x0001) /* invalid operation */
+
+
+#ifndef LOCORE
+
+#ifdef DEBUG
+#define EXCEPTION(x) { printf("exception in %s at line %d\n", \
+ __FILE__, __LINE__); exception(x); }
+#else
+#define EXCEPTION(x) exception(x)
+#endif
+
+#endif /* LOCORE */
+
+#endif /* _EXCEPTION_H_ */
diff --git a/sys/gnu/i386/fpemul/fpu_arith.c b/sys/gnu/i386/fpemul/fpu_arith.c
new file mode 100644
index 0000000..a1da514
--- /dev/null
+++ b/sys/gnu/i386/fpemul/fpu_arith.c
@@ -0,0 +1,235 @@
+/*
+ * fpu_arith.c
+ *
+ * Code to implement the FPU register/register arithmetic instructions
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: fpu_arith.c,v 1.2 1994/04/29 21:07:14 gclarkii Exp $
+ *
+ */
+
+
+
+
+#include "param.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "control_w.h"
+
+
+void
+fadd__()
+{
+ /* fadd st,st(i) */
+ reg_add(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
+}
+
+
+void
+fmul__()
+{
+ /* fmul st,st(i) */
+ reg_mul(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
+}
+
+
+
+void
+fsub__()
+{
+ /* fsub st,st(i) */
+ reg_sub(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
+}
+
+
+void
+fsubr_()
+{
+ /* fsubr st,st(i) */
+ reg_sub(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word);
+}
+
+
+void
+fdiv__()
+{
+ /* fdiv st,st(i) */
+ reg_div(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
+}
+
+
+void
+fdivr_()
+{
+ /* fdivr st,st(i) */
+ reg_div(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word);
+}
+
+
+
+void
+fadd_i()
+{
+ /* fadd st(i),st */
+ reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
+}
+
+
+void
+fmul_i()
+{
+ /* fmul st(i),st */
+ reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
+}
+
+
+void
+fsubri()
+{
+ /* fsubr st(i),st */
+ /* This is the sense of the 80486 manual reg_sub(&st(FPU_rm),
+ * FPU_st0_ptr, &st(FPU_rm), control_word); */
+ reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
+}
+
+
+void
+fsub_i()
+{
+ /* fsub st(i),st */
+ /* This is the sense of the 80486 manual reg_sub(FPU_st0_ptr,
+ * &st(FPU_rm), &st(FPU_rm), control_word); */
+ reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
+}
+
+
+void
+fdivri()
+{
+ /* fdivr st(i),st */
+ reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
+}
+
+
+void
+fdiv_i()
+{
+ /* fdiv st(i),st */
+ reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
+}
+
+
+
+void
+faddp_()
+{
+ /* faddp st(i),st */
+ reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
+ pop();
+}
+
+
+void
+fmulp_()
+{
+ /* fmulp st(i),st */
+ reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
+ pop();
+}
+
+
+
+void
+fsubrp()
+{
+ /* fsubrp st(i),st */
+ /* This is the sense of the 80486 manual reg_sub(&st(FPU_rm),
+ * FPU_st0_ptr, &st(FPU_rm), control_word); */
+ reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
+ pop();
+}
+
+
+void
+fsubp_()
+{
+ /* fsubp st(i),st */
+ /* This is the sense of the 80486 manual reg_sub(FPU_st0_ptr,
+ * &st(FPU_rm), &st(FPU_rm), control_word); */
+ reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
+ pop();
+}
+
+
+void
+fdivrp()
+{
+ /* fdivrp st(i),st */
+ reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
+ pop();
+}
+
+
+void
+fdivp_()
+{
+ /* fdivp st(i),st */
+ reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
+ pop();
+}
diff --git a/sys/gnu/i386/fpemul/fpu_asm.h b/sys/gnu/i386/fpemul/fpu_asm.h
new file mode 100644
index 0000000..e618c04
--- /dev/null
+++ b/sys/gnu/i386/fpemul/fpu_asm.h
@@ -0,0 +1,82 @@
+/*
+ * fpu_asm.h
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: fpu_asm.h,v 1.2 1994/04/29 21:07:14 gclarkii Exp $
+ *
+ */
+
+#ifndef _FPU_ASM_H_
+#define _FPU_ASM_H_
+
+#include "fpu_emu.h"
+
+#define EXCEPTION _exception
+
+
+#define PARAM1 8(%ebp)
+#define PARAM2 12(%ebp)
+#define PARAM3 16(%ebp)
+#define PARAM4 20(%ebp)
+
+#define SIGL_OFFSET 8
+#define SIGN(x) (x)
+#define TAG(x) 1(x)
+#define EXP(x) 4(x)
+#define SIG(x) SIGL_OFFSET/**/(x)
+#define SIGL(x) SIGL_OFFSET/**/(x)
+#define SIGH(x) 12(x)
+
+#endif /* _FPU_ASM_H_ */
diff --git a/sys/gnu/i386/fpemul/fpu_aux.c b/sys/gnu/i386/fpemul/fpu_aux.c
new file mode 100644
index 0000000..629e45a
--- /dev/null
+++ b/sys/gnu/i386/fpemul/fpu_aux.c
@@ -0,0 +1,233 @@
+/*
+ * fpu_aux.c
+ *
+ * Code to implement some of the FPU auxiliary instructions.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: fpu_aux.c,v 1.2 1994/04/29 21:16:19 gclarkii Exp $
+ *
+ */
+
+
+#include "param.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+#include "status_w.h"
+
+
+
+void
+fclex(void)
+{
+ status_word &= ~(SW_Backward | SW_Summary | SW_Stack_Fault | SW_Precision |
+ SW_Underflow | SW_Overflow | SW_Zero_Div | SW_Denorm_Op |
+ SW_Invalid);
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+}
+/* Needs to be externally visible */
+void
+finit()
+{
+ int r;
+ control_word = 0x037f;
+ status_word = 0;
+ top = 0; /* We don't keep top in the status word
+ * internally. */
+ for (r = 0; r < 8; r++) {
+ regs[r].tag = TW_Empty;
+ }
+ FPU_entry_eip = ip_offset = 0;
+}
+
+static FUNC finit_table[] = {
+ Un_impl, Un_impl, fclex, finit, Un_impl, Un_impl, Un_impl, Un_impl
+};
+
+void
+finit_()
+{
+ (finit_table[FPU_rm]) ();
+}
+
+
+static void
+fstsw_ax(void)
+{
+
+ status_word &= ~SW_Top;
+ status_word |= (top & 7) << SW_Top_Shift;
+
+ *(short *) &FPU_EAX = status_word;
+
+}
+
+static FUNC fstsw_table[] = {
+ fstsw_ax, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl
+};
+
+void
+fstsw_()
+{
+ (fstsw_table[FPU_rm]) ();
+}
+
+
+
+static void
+fnop(void)
+{
+}
+
+FUNC fp_nop_table[] = {
+ fnop, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl
+};
+
+void
+fp_nop()
+{
+ (fp_nop_table[FPU_rm]) ();
+}
+
+
+void
+fld_i_()
+{
+ FPU_REG *st_new_ptr;
+
+ if (STACK_OVERFLOW) {
+ stack_overflow();
+ return;
+ }
+ /* fld st(i) */
+ if (NOT_EMPTY(FPU_rm)) {
+ reg_move(&st(FPU_rm), st_new_ptr);
+ push();
+ } else {
+ if (control_word & EX_Invalid) {
+ /* The masked response */
+ push();
+ stack_underflow();
+ } else
+ EXCEPTION(EX_StackUnder);
+ }
+
+}
+
+
+void
+fxch_i()
+{
+ /* fxch st(i) */
+ FPU_REG t;
+ register FPU_REG *sti_ptr = &st(FPU_rm);
+
+ if (FPU_st0_tag == TW_Empty) {
+ if (sti_ptr->tag == TW_Empty) {
+ stack_underflow();
+ stack_underflow_i(FPU_rm);
+ return;
+ }
+ reg_move(sti_ptr, FPU_st0_ptr);
+ stack_underflow_i(FPU_rm);
+ return;
+ }
+ if (sti_ptr->tag == TW_Empty) {
+ reg_move(FPU_st0_ptr, sti_ptr);
+ stack_underflow();
+ return;
+ }
+ reg_move(FPU_st0_ptr, &t);
+ reg_move(sti_ptr, FPU_st0_ptr);
+ reg_move(&t, sti_ptr);
+}
+
+
+void
+ffree_()
+{
+ /* ffree st(i) */
+ st(FPU_rm).tag = TW_Empty;
+}
+
+
+void
+ffreep()
+{
+ /* ffree st(i) + pop - unofficial code */
+ st(FPU_rm).tag = TW_Empty;
+ pop();
+}
+
+
+void
+fst_i_()
+{
+ /* fst st(i) */
+ reg_move(FPU_st0_ptr, &st(FPU_rm));
+}
+
+
+void
+fstp_i()
+{
+ /* fstp st(i) */
+ reg_move(FPU_st0_ptr, &st(FPU_rm));
+ pop();
+}
diff --git a/sys/gnu/i386/fpemul/fpu_emu.h b/sys/gnu/i386/fpemul/fpu_emu.h
new file mode 100644
index 0000000..b62a9cf
--- /dev/null
+++ b/sys/gnu/i386/fpemul/fpu_emu.h
@@ -0,0 +1,188 @@
+/*
+ * fpu_emu.h
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: fpu_emu.h,v 1.2 1994/04/29 21:16:20 gclarkii Exp $
+ *
+ */
+
+
+#ifndef _FPU_EMU_H_
+#define _FPU_EMU_H_
+
+/*
+ * Define DENORM_OPERAND to make the emulator detect denormals
+ * and use the denormal flag of the status word. Note: this only
+ * affects the flag and corresponding interrupt, the emulator
+ * will always generate denormals and operate upon them as required.
+ */
+#define DENORM_OPERAND
+
+/*
+ * Define PECULIAR_486 to get a closer approximation to 80486 behaviour,
+ * rather than behaviour which appears to be cleaner.
+ * This is a matter of opinion: for all I know, the 80486 may simply
+ * be complying with the IEEE spec. Maybe one day I'll get to see the
+ * spec...
+ */
+#define PECULIAR_486
+
+#ifdef LOCORE
+#include "fpu_asm.h"
+#define Const(x) $/**/x
+#else
+#define Const(x) x
+#endif
+
+#define EXP_BIAS Const(0)
+#define EXP_OVER Const(0x4000) /* smallest invalid large exponent */
+#define EXP_UNDER Const(-0x3fff) /* largest invalid small exponent */
+
+#define SIGN_POS Const(0)
+#define SIGN_NEG Const(1)
+
+/* Keep the order TW_Valid, TW_Zero, TW_Denormal */
+#define TW_Valid Const(0)/* valid */
+#define TW_Zero Const(1)/* zero */
+/* The following fold to 2 (Special) in the Tag Word */
+#define TW_Denormal Const(4)/* De-normal */
+#define TW_Infinity Const(5)/* + or - infinity */
+#define TW_NaN Const(6)/* Not a Number */
+
+#define TW_Empty Const(7)/* empty */
+
+ /* #define TW_FPU_Interrupt Const(0x80) *//* Signals an interrupt */
+
+
+#ifndef LOCORE
+
+#include "types.h"
+#include "math_emu.h"
+
+#ifdef PARANOID
+extern char emulating;
+#define REENTRANT_CHECK(state) emulating = (state)
+#define ON 1
+#define OFF 0
+#else
+#define REENTRANT_CHECK(state)
+#endif /* PARANOID */
+
+typedef void (*FUNC) (void);
+typedef struct fpu_reg FPU_REG;
+
+#define st(x) ( regs[((top+x) &7 )] )
+
+#define STACK_OVERFLOW (st_new_ptr = &st(-1), st_new_ptr->tag != TW_Empty)
+#define NOT_EMPTY(i) (st(i).tag != TW_Empty)
+#define NOT_EMPTY_0 (FPU_st0_tag ^ TW_Empty)
+
+extern unsigned char FPU_rm;
+
+extern char FPU_st0_tag;
+extern FPU_REG *FPU_st0_ptr;
+
+extern void *FPU_data_address;
+
+extern FPU_REG FPU_loaded_data;
+
+#define pop() { FPU_st0_ptr->tag = TW_Empty; top++; }
+
+/* push() does not affect the tags */
+#define push() { top--; FPU_st0_ptr = st_new_ptr; }
+
+
+#define reg_move(x, y) { \
+ *(short *)&((y)->sign) = *(short *)&((x)->sign); \
+ *(long *)&((y)->exp) = *(long *)&((x)->exp); \
+ *(long long *)&((y)->sigl) = *(long long *)&((x)->sigl); }
+
+
+/*----- Prototypes for functions written in assembler -----*/
+/* extern void reg_move(FPU_REG *a, FPU_REG *b); */
+
+extern void mul64(long long *a, long long *b, long long *result);
+extern void poly_div2(long long *x);
+extern void poly_div4(long long *x);
+extern void poly_div16(long long *x);
+extern void
+polynomial(unsigned accum[], unsigned x[],
+ unsigned short terms[][4], int n);
+ extern void normalize(FPU_REG * x);
+ extern void normalize_nuo(FPU_REG * x);
+ extern void reg_div(FPU_REG * arg1, FPU_REG * arg2, FPU_REG * answ,
+ unsigned int control_w);
+ extern void reg_u_sub(FPU_REG * arg1, FPU_REG * arg2, FPU_REG * answ,
+ unsigned int control_w);
+ extern void reg_u_mul(FPU_REG * arg1, FPU_REG * arg2, FPU_REG * answ,
+ unsigned int control_w);
+ extern void reg_u_div(FPU_REG * arg1, FPU_REG * arg2, FPU_REG * answ,
+ unsigned int control_w);
+ extern void reg_u_add(FPU_REG * arg1, FPU_REG * arg2, FPU_REG * answ,
+ unsigned int control_w);
+ extern void wm_sqrt(FPU_REG * n, unsigned int control_w);
+ extern unsigned shrx(void *l, unsigned x);
+ extern unsigned shrxs(void *v, unsigned x);
+ extern unsigned long div_small(unsigned long long *x, unsigned long y);
+ extern void round_reg(FPU_REG * arg, unsigned int extent,
+ unsigned int control_w);
+
+#ifndef MAKING_PROTO
+#include "fpu_proto.h"
+#endif
+
+#endif /* LOCORE */
+
+#endif /* _FPU_EMU_H_ */
diff --git a/sys/gnu/i386/fpemul/fpu_entry.c b/sys/gnu/i386/fpemul/fpu_entry.c
new file mode 100644
index 0000000..367340a
--- /dev/null
+++ b/sys/gnu/i386/fpemul/fpu_entry.c
@@ -0,0 +1,483 @@
+/*
+ * fpu_entry.c
+ *
+ * The entry function for wm-FPU-emu
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ * $Id: fpu_entry.c,v 1.3 1994/06/10 07:44:22 rich Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | Note: |
+ | The file contains code which accesses user memory. |
+ | Emulator static data may change when user memory is accessed, due to |
+ | other processes using the emulator while swapping is in progress. |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | math_emulate() is the sole entry point for wm-FPU-emu |
+ +---------------------------------------------------------------------------*/
+
+
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+#include "control_w.h"
+#include "status_w.h"
+
+
+#define __BAD__ Un_impl /* Not implemented */
+
+#define FPU_LOOKAHEAD 1 /* For performance boost */
+
+#if FPU_LOOKAHEAD != 0 /* I think thet we have to limit the */
+#define LOOKAHEAD_LIMIT 7 /* Max number of lookahead instructions*/
+#endif /* Or else a prog consisting of a million */
+ /* fnops will spend all its time in kernel*/
+
+#ifndef NO_UNDOC_CODE /* Un-documented FPU op-codes supported by
+ * default. */
+
+/* WARNING: These codes are not documented by Intel in their 80486 manual
+ and may not work on FPU clones or later Intel FPUs. */
+
+/* Changes to support the un-doc codes provided by Linus Torvalds. */
+
+#define _d9_d8_ fstp_i /* unofficial code (19) */
+#define _dc_d0_ fcom_st /* unofficial code (14) */
+#define _dc_d8_ fcompst /* unofficial code (1c) */
+#define _dd_c8_ fxch_i /* unofficial code (0d) */
+#define _de_d0_ fcompst /* unofficial code (16) */
+#define _df_c0_ ffreep /* unofficial code (07) ffree + pop */
+#define _df_c8_ fxch_i /* unofficial code (0f) */
+#define _df_d0_ fstp_i /* unofficial code (17) */
+#define _df_d8_ fstp_i /* unofficial code (1f) */
+
+static FUNC st_instr_table[64] = {
+ fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_,
+ fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_,
+ fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_,
+ fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_,
+ fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
+ fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
+ fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
+ fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
+};
+#else /* Support only documented FPU op-codes */
+
+static FUNC st_instr_table[64] = {
+ fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__,
+ fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__,
+ fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__,
+ fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__,
+ fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
+ fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
+ fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
+ fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
+};
+#endif /* NO_UNDOC_CODE */
+
+
+#define _NONE_ 0 /* Take no special action */
+#define _REG0_ 1 /* Need to check for not empty st(0) */
+#define _REGI_ 2 /* Need to check for not empty st(0) and
+ * st(rm) */
+#define _REGi_ 0 /* Uses st(rm) */
+#define _PUSH_ 3 /* Need to check for space to push onto stack */
+#define _null_ 4 /* Function illegal or not implemented */
+#define _REGIi 5 /* Uses st(0) and st(rm), result to st(rm) */
+#define _REGIp 6 /* Uses st(0) and st(rm), result to st(rm)
+ * then pop */
+#define _REGIc 0 /* Compare st(0) and st(rm) */
+#define _REGIn 0 /* Uses st(0) and st(rm), but handle checks
+ * later */
+
+#ifndef NO_UNDOC_CODE
+
+/* Un-documented FPU op-codes supported by default. (see above) */
+
+static unsigned char type_table[64] = {
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
+ _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
+ _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
+ _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
+ _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
+ _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
+};
+#else /* Support only documented FPU op-codes */
+
+static unsigned char type_table[64] = {
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_,
+ _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
+ _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
+ _REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_,
+ _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
+ _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
+};
+#endif /* NO_UNDOC_CODE */
+
+/* Be careful when using any of these global variables...
+ they might change if swapping is triggered */
+unsigned char FPU_rm;
+char FPU_st0_tag;
+FPU_REG *FPU_st0_ptr;
+
+#ifdef PARANOID
+char emulating = 0;
+#endif /* PARANOID */
+
+#define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x))
+#define math_abort(signo) \
+ FPU_EIP = FPU_ORIG_EIP;REENTRANT_CHECK(OFF);return(signo);
+
+int
+math_emulate(struct trapframe * tframe)
+{
+
+ unsigned char FPU_modrm;
+ unsigned short code;
+#ifdef LOOKAHEAD_LIMIT
+ int lookahead_limit = LOOKAHEAD_LIMIT;
+#endif
+#ifdef PARANOID
+ if (emulating) {
+ printf("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
+ }
+ REENTRANT_CHECK(ON);
+#endif /* PARANOID */
+
+ if ((((struct pcb *) curproc->p_addr)->pcb_flags & FP_SOFTFP) == 0) {
+ finit();
+ control_word = __INITIAL_NPXCW__;
+ ((struct pcb *) curproc->p_addr)->pcb_flags |= FP_SOFTFP;
+ }
+ FPU_info = tframe;
+ FPU_ORIG_EIP = FPU_EIP; /* --pink-- */
+
+ if (FPU_CS != 0x001f) {
+ printf("math_emulate: %x : %x\n", FPU_CS, FPU_EIP);
+ panic("FPU emulation in kernel");
+ }
+#ifdef notyet
+ /* We cannot handle emulation in v86-mode */
+ if (FPU_EFLAGS & 0x00020000) {
+ FPU_ORIG_EIP = FPU_EIP;
+ math_abort(FPU_info, SIGILL);
+ }
+#endif
+
+ FPU_lookahead = FPU_LOOKAHEAD;
+ if (curproc->p_flag & STRC)
+ FPU_lookahead = 0;
+
+do_another_FPU_instruction:
+
+ REENTRANT_CHECK(OFF);
+ code = fuword((u_int *) FPU_EIP);
+ REENTRANT_CHECK(ON);
+ if ((code & 0xff) == 0x9b) { /* fwait */
+ if (status_word & SW_Summary)
+ goto do_the_FPU_interrupt;
+ else {
+ FPU_EIP++;
+ goto FPU_instruction_done;
+ }
+ }
+ if (status_word & SW_Summary) {
+ /* Ignore the error for now if the current instruction is a
+ * no-wait control instruction */
+ /* The 80486 manual contradicts itself on this topic, so I use
+ * the following list of such instructions until I can check
+ * on a real 80486: fninit, fnstenv, fnsave, fnstsw, fnstenv,
+ * fnclex. */
+ if (!((((code & 0xf803) == 0xe003) || /* fnclex, fninit,
+ * fnstsw */
+ (((code & 0x3003) == 0x3001) && /* fnsave, fnstcw,
+ * fnstenv, fnstsw */
+ ((code & 0xc000) != 0xc000))))) {
+ /* This is a guess about what a real FPU might do to
+ * this bit: */
+/* status_word &= ~SW_Summary; ****/
+
+ /* We need to simulate the action of the kernel to FPU
+ * interrupts here. Currently, the "real FPU" part of
+ * the kernel (0.99.10) clears the exception flags,
+ * sets the registers to empty, and passes information
+ * back to the interrupted process via the cs selector
+ * and operand selector, so we do the same. */
+ do_the_FPU_interrupt:
+ cs_selector &= 0xffff0000;
+ cs_selector |= (status_word & ~SW_Top) | ((top & 7) << SW_Top_Shift);
+ operand_selector = tag_word();
+ status_word = 0;
+ top = 0;
+ {
+ int r;
+ for (r = 0; r < 8; r++) {
+ regs[r].tag = TW_Empty;
+ }
+ }
+ REENTRANT_CHECK(OFF);
+ math_abort(SIGFPE);
+ }
+ }
+ FPU_entry_eip = FPU_ORIG_EIP = FPU_EIP;
+
+ if ((code & 0xff) == 0x66) { /* size prefix */
+ FPU_EIP++;
+ REENTRANT_CHECK(OFF);
+ code = fuword((u_int *) FPU_EIP);
+ REENTRANT_CHECK(ON);
+ }
+ FPU_EIP += 2;
+
+ FPU_modrm = code >> 8;
+ FPU_rm = FPU_modrm & 7;
+
+ if (FPU_modrm < 0300) {
+ /* All of these instructions use the mod/rm byte to get a data
+ * address */
+ get_address(FPU_modrm);
+ if (!(code & 1)) {
+ unsigned short status1 = status_word;
+ FPU_st0_ptr = &st(0);
+ FPU_st0_tag = FPU_st0_ptr->tag;
+
+ /* Stack underflow has priority */
+ if (NOT_EMPTY_0) {
+ switch ((code >> 1) & 3) {
+ case 0:
+ reg_load_single();
+ break;
+ case 1:
+ reg_load_int32();
+ break;
+ case 2:
+ reg_load_double();
+ break;
+ case 3:
+ reg_load_int16();
+ break;
+ }
+
+ /* No more access to user memory, it is safe
+ * to use static data now */
+ FPU_st0_ptr = &st(0);
+ FPU_st0_tag = FPU_st0_ptr->tag;
+
+ /* NaN operands have the next priority. */
+ /* We have to delay looking at st(0) until
+ * after loading the data, because that data
+ * might contain an SNaN */
+ if ((FPU_st0_tag == TW_NaN) ||
+ (FPU_loaded_data.tag == TW_NaN)) {
+ /* Restore the status word; we might
+ * have loaded a denormal. */
+ status_word = status1;
+ if ((FPU_modrm & 0x30) == 0x10) {
+ /* fcom or fcomp */
+ EXCEPTION(EX_Invalid);
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ if (FPU_modrm & 0x08)
+ pop(); /* fcomp, so we pop. */
+ } else
+ real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr);
+ goto reg_mem_instr_done;
+ }
+ switch ((FPU_modrm >> 3) & 7) {
+ case 0: /* fadd */
+ reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word);
+ break;
+ case 1: /* fmul */
+ reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word);
+ break;
+ case 2: /* fcom */
+ compare_st_data();
+ break;
+ case 3: /* fcomp */
+ compare_st_data();
+ pop();
+ break;
+ case 4: /* fsub */
+ reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word);
+ break;
+ case 5: /* fsubr */
+ reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, control_word);
+ break;
+ case 6: /* fdiv */
+ reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word);
+ break;
+ case 7: /* fdivr */
+ if (FPU_st0_tag == TW_Zero)
+ status_word = status1; /* Undo any denorm tag,
+ * zero-divide has
+ * priority. */
+ reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, control_word);
+ break;
+ }
+ } else {
+ if ((FPU_modrm & 0x30) == 0x10) {
+ /* The instruction is fcom or fcomp */
+ EXCEPTION(EX_StackUnder);
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ if (FPU_modrm & 0x08)
+ pop(); /* fcomp, Empty or not,
+ * we pop. */
+ } else
+ stack_underflow();
+ }
+ } else {
+ load_store_instr(((FPU_modrm & 0x38) | (code & 6)) >> 1);
+ }
+
+reg_mem_instr_done:
+
+ data_operand_offset = (unsigned long) FPU_data_address;
+ } else {
+ /* None of these instructions access user memory */
+ unsigned char instr_index = (FPU_modrm & 0x38) | (code & 7);
+
+ FPU_st0_ptr = &st(0);
+ FPU_st0_tag = FPU_st0_ptr->tag;
+ switch (type_table[(int) instr_index]) {
+ case _NONE_: /* also _REGIc: _REGIn */
+ break;
+ case _REG0_:
+ if (!NOT_EMPTY_0) {
+ stack_underflow();
+ goto FPU_instruction_done;
+ }
+ break;
+ case _REGIi:
+ if (!NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm)) {
+ stack_underflow_i(FPU_rm);
+ goto FPU_instruction_done;
+ }
+ break;
+ case _REGIp:
+ if (!NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm)) {
+ stack_underflow_i(FPU_rm);
+ pop();
+ goto FPU_instruction_done;
+ }
+ break;
+ case _REGI_:
+ if (!NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm)) {
+ stack_underflow();
+ goto FPU_instruction_done;
+ }
+ break;
+ case _PUSH_: /* Only used by the fld st(i) instruction */
+ break;
+ case _null_:
+ Un_impl();
+ goto FPU_instruction_done;
+ default:
+ EXCEPTION(EX_INTERNAL | 0x111);
+ goto FPU_instruction_done;
+ }
+ (*st_instr_table[(int) instr_index]) ();
+ }
+
+FPU_instruction_done:
+
+ ip_offset = FPU_entry_eip;
+ bswapw(code);
+ *(1 + (unsigned short *) &cs_selector) = code & 0x7ff;
+
+#ifdef DEBUG
+ REENTRANT_CHECK(OFF);
+ emu_printall();
+ REENTRANT_CHECK(ON);
+#endif /* DEBUG */
+#ifdef LOOKAHEAD_LIMIT
+if (--lookahead_limit)
+#endif
+ if (FPU_lookahead) {
+ unsigned char next;
+
+ /* (This test should generate no machine code) */
+ while (1) {
+ REENTRANT_CHECK(OFF);
+ next = fubyte((u_char *) FPU_EIP);
+ REENTRANT_CHECK(ON);
+ if (((next & 0xf8) == 0xd8) || (next == 0x9b)) { /* fwait */
+ goto do_another_FPU_instruction;
+ } else
+ if (next == 0x66) { /* size prefix */
+ REENTRANT_CHECK(OFF);
+ next = fubyte((u_char *) (FPU_EIP + 1));
+ REENTRANT_CHECK(ON);
+ if ((next & 0xf8) == 0xd8) {
+ FPU_EIP++;
+ goto do_another_FPU_instruction;
+ }
+ }
+ break;
+ }
+ }
+ REENTRANT_CHECK(OFF);
+ return (0); /* --pink-- */
+}
diff --git a/sys/gnu/i386/fpemul/fpu_etc.c b/sys/gnu/i386/fpemul/fpu_etc.c
new file mode 100644
index 0000000..936e39a
--- /dev/null
+++ b/sys/gnu/i386/fpemul/fpu_etc.c
@@ -0,0 +1,175 @@
+/*
+ * fpu_etc.c
+ *
+ * Implement a few FPU instructions.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: fpu_etc.c,v 1.2 1994/04/29 21:16:22 gclarkii Exp $
+ *
+ */
+
+#include "param.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+#include "status_w.h"
+#include "reg_constant.h"
+
+
+static void
+fchs(void)
+{
+ if (NOT_EMPTY_0) {
+ FPU_st0_ptr->sign ^= SIGN_POS ^ SIGN_NEG;
+ status_word &= ~SW_C1;
+ } else
+ stack_underflow();
+}
+
+static void
+fabs(void)
+{
+ if (FPU_st0_tag ^ TW_Empty) {
+ FPU_st0_ptr->sign = SIGN_POS;
+ status_word &= ~SW_C1;
+ } else
+ stack_underflow();
+}
+
+
+static void
+ftst_(void)
+{
+ switch (FPU_st0_tag) {
+ case TW_Zero:
+ setcc(SW_C3);
+ break;
+ case TW_Valid:
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ if (FPU_st0_ptr->sign == SIGN_POS)
+ setcc(0);
+ else
+ setcc(SW_C0);
+ break;
+ case TW_NaN:
+ setcc(SW_C0 | SW_C2 | SW_C3); /* Operand is not comparable */
+ EXCEPTION(EX_Invalid);
+ break;
+ case TW_Infinity:
+ if (FPU_st0_ptr->sign == SIGN_POS)
+ setcc(0);
+ else
+ setcc(SW_C0);
+ EXCEPTION(EX_Invalid);
+ break;
+ case TW_Empty:
+ setcc(SW_C0 | SW_C2 | SW_C3);
+ EXCEPTION(EX_StackUnder);
+ break;
+ default:
+ setcc(SW_C0 | SW_C2 | SW_C3); /* Operand is not comparable */
+ EXCEPTION(EX_INTERNAL | 0x14);
+ break;
+ }
+}
+
+static void
+fxam(void)
+{
+ int c = 0;
+ switch (FPU_st0_tag) {
+ case TW_Empty:
+ c = SW_C3 | SW_C0;
+ break;
+ case TW_Zero:
+ c = SW_C3;
+ break;
+ case TW_Valid:
+ /* This will need to be changed if TW_Denormal is ever used. */
+ if (FPU_st0_ptr->exp <= EXP_UNDER)
+ c = SW_C2 | SW_C3; /* Denormal */
+ else
+ c = SW_C3;
+ break;
+ case TW_NaN:
+ c = SW_C0;
+ break;
+ case TW_Infinity:
+ c = SW_C2 | SW_C0;
+ break;
+ }
+ if (FPU_st0_ptr->sign == SIGN_NEG)
+ c |= SW_C1;
+ setcc(c);
+}
+
+static FUNC fp_etc_table[] = {
+ fchs, fabs, Un_impl, Un_impl, ftst_, fxam, Un_impl, Un_impl
+};
+
+void
+fp_etc()
+{
+ (fp_etc_table[FPU_rm]) ();
+}
diff --git a/sys/gnu/i386/fpemul/fpu_proto.h b/sys/gnu/i386/fpemul/fpu_proto.h
new file mode 100644
index 0000000..255aa5d
--- /dev/null
+++ b/sys/gnu/i386/fpemul/fpu_proto.h
@@ -0,0 +1,115 @@
+/*
+ *
+ * $Id:$
+ *
+ */
+
+
+/* errors.c */
+extern void Un_impl(void);
+extern void emu_printall(void);
+extern void exception(int n);
+extern void real_2op_NaN(FPU_REG * a, FPU_REG * b, FPU_REG * dest);
+extern void arith_invalid(FPU_REG * dest);
+extern void divide_by_zero(int sign, FPU_REG * dest);
+extern void set_precision_flag_up(void);
+extern void set_precision_flag_down(void);
+extern int denormal_operand(void);
+extern void arith_overflow(FPU_REG * dest);
+extern void arith_underflow(FPU_REG * dest);
+extern void stack_overflow(void);
+extern void stack_underflow(void);
+extern void stack_underflow_i(int i);
+extern void stack_underflow_pop(int i);
+/* fpu_arith.c */
+extern void fadd__(void);
+extern void fmul__(void);
+extern void fsub__(void);
+extern void fsubr_(void);
+extern void fdiv__(void);
+extern void fdivr_(void);
+extern void fadd_i(void);
+extern void fmul_i(void);
+extern void fsubri(void);
+extern void fsub_i(void);
+extern void fdivri(void);
+extern void fdiv_i(void);
+extern void faddp_(void);
+extern void fmulp_(void);
+extern void fsubrp(void);
+extern void fsubp_(void);
+extern void fdivrp(void);
+extern void fdivp_(void);
+/* fpu_aux.c */
+extern void fclex(void);
+extern void finit(void);
+extern void finit_(void);
+extern void fstsw_(void);
+extern void fp_nop(void);
+extern void fld_i_(void);
+extern void fxch_i(void);
+extern void ffree_(void);
+extern void ffreep(void);
+extern void fst_i_(void);
+extern void fstp_i(void);
+/* fpu_entry.c */
+extern int math_emulate(struct trapframe * info);
+/* fpu_etc.c */
+extern void fp_etc(void);
+/* fpu_trig.c */
+extern void convert_l2reg(long *arg, FPU_REG * dest);
+extern void trig_a(void);
+extern void trig_b(void);
+/* get_address.c */
+extern void get_address(unsigned char FPU_modrm);
+/* load_store.c */
+extern void load_store_instr(char type);
+/* poly_2xm1.c */
+extern int poly_2xm1(FPU_REG * arg, FPU_REG * result);
+/* poly_atan.c */
+extern void poly_atan(FPU_REG * arg);
+extern void poly_add_1(FPU_REG * src);
+/* poly_l2.c */
+extern void poly_l2(FPU_REG * arg, FPU_REG * result);
+extern int poly_l2p1(FPU_REG * arg, FPU_REG * result);
+/* poly_sin.c */
+extern void poly_sine(FPU_REG * arg, FPU_REG * result);
+/* poly_tan.c */
+extern void poly_tan(FPU_REG * arg, FPU_REG * y_reg);
+/* reg_add_sub.c */
+extern void reg_add(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w);
+extern void reg_sub(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w);
+/* reg_compare.c */
+extern int compare(FPU_REG * b);
+extern int compare_st_data(void);
+extern void fcom_st(void);
+extern void fcompst(void);
+extern void fcompp(void);
+extern void fucom_(void);
+extern void fucomp(void);
+extern void fucompp(void);
+/* reg_constant.c */
+extern void fconst(void);
+/* reg_ld_str.c */
+extern void reg_load_extended(void);
+extern void reg_load_double(void);
+extern void reg_load_single(void);
+extern void reg_load_int64(void);
+extern void reg_load_int32(void);
+extern void reg_load_int16(void);
+extern void reg_load_bcd(void);
+extern int reg_store_extended(void);
+extern int reg_store_double(void);
+extern int reg_store_single(void);
+extern int reg_store_int64(void);
+extern int reg_store_int32(void);
+extern int reg_store_int16(void);
+extern int reg_store_bcd(void);
+extern int round_to_int(FPU_REG * r);
+extern char *fldenv(void);
+extern void frstor(void);
+extern unsigned short tag_word(void);
+extern char *fstenv(void);
+extern void fsave(void);
+/* reg_mul.c */
+extern void reg_mul(FPU_REG * a, FPU_REG * b, FPU_REG * dest, unsigned int control_w);
diff --git a/sys/gnu/i386/fpemul/fpu_system.h b/sys/gnu/i386/fpemul/fpu_system.h
new file mode 100644
index 0000000..8bcc14c
--- /dev/null
+++ b/sys/gnu/i386/fpemul/fpu_system.h
@@ -0,0 +1,97 @@
+/*
+ * fpu_system.h
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: fpu_system.h,v 1.2 1994/04/29 21:16:24 gclarkii Exp $
+ *
+ */
+
+
+#ifndef _FPU_SYSTEM_H
+#define _FPU_SYSTEM_H
+
+/* system dependent definitions */
+
+/*
+#include <linux/sched.h>
+#include <linux/kernel.h>
+*/
+
+#define I387 (*(union i387_union *)&(((struct pcb *)curproc->p_addr)->pcb_savefpu))
+#define FPU_info (I387.soft.frame)
+
+#define FPU_CS (*(unsigned short *) &(FPU_info->tf_cs))
+#define FPU_DS (*(unsigned short *) &(FPU_info->tf_ds))
+#define FPU_EAX (FPU_info->tf_eax)
+#define FPU_EFLAGS (FPU_info->tf_eflags)
+#define FPU_EIP (FPU_info->tf_eip)
+/*#define FPU_ORIG_EIP (FPU_info->___orig_eip) */
+/*#define FPU_ORIG_EIP (FPU_info->tf_isp)*/
+#define FPU_ORIG_EIP (I387.soft.orig_eip)
+
+#define FPU_lookahead (I387.soft.lookahead)
+#define FPU_entry_eip (I387.soft.entry_eip)
+
+#define status_word (I387.soft.swd)
+#define control_word (I387.soft.cwd)
+#define regs (I387.soft.regs)
+#define top (I387.soft.top)
+
+#define ip_offset (I387.soft.fip)
+#define cs_selector (I387.soft.fcs)
+#define data_operand_offset (I387.soft.foo)
+#define operand_selector (I387.soft.fos)
+
+#endif
diff --git a/sys/gnu/i386/fpemul/fpu_trig.c b/sys/gnu/i386/fpemul/fpu_trig.c
new file mode 100644
index 0000000..ca32f91
--- /dev/null
+++ b/sys/gnu/i386/fpemul/fpu_trig.c
@@ -0,0 +1,1367 @@
+/*
+ * fpu_trig.c
+ *
+ * Implementation of the FPU "transcendental" functions.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: fpu_trig.c,v 1.3 1994/04/29 21:16:25 gclarkii Exp $
+ *
+ */
+
+
+#include "param.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+#include "status_w.h"
+#include "reg_constant.h"
+#include "control_w.h"
+
+static int
+trig_arg(FPU_REG * X)
+{
+ FPU_REG tmp, quot;
+ int rv;
+ long long q;
+ int old_cw = control_word;
+
+ control_word &= ~CW_RC;
+ control_word |= RC_CHOP;
+
+ reg_move(X, &quot);
+ reg_div(&quot, &CONST_PI2, &quot, FULL_PRECISION);
+
+ reg_move(&quot, &tmp);
+ round_to_int(&tmp);
+ if (tmp.sigh & 0x80000000)
+ return -1; /* |Arg| is >= 2^63 */
+ tmp.exp = EXP_BIAS + 63;
+ q = *(long long *) &(tmp.sigl);
+ normalize(&tmp);
+
+ reg_sub(&quot, &tmp, X, FULL_PRECISION);
+ rv = q & 7;
+
+ control_word = old_cw;
+ return rv;;
+}
+
+
+/* Convert a long to register */
+void
+convert_l2reg(long *arg, FPU_REG * dest)
+{
+ long num = *arg;
+
+ if (num == 0) {
+ reg_move(&CONST_Z, dest);
+ return;
+ }
+ if (num > 0)
+ dest->sign = SIGN_POS;
+ else {
+ num = -num;
+ dest->sign = SIGN_NEG;
+ }
+
+ dest->sigh = num;
+ dest->sigl = 0;
+ dest->exp = EXP_BIAS + 31;
+ dest->tag = TW_Valid;
+ normalize(dest);
+}
+
+
+static void
+single_arg_error(void)
+{
+ switch (FPU_st0_tag) {
+ case TW_NaN:
+ if (!(FPU_st0_ptr->sigh & 0x40000000)) { /* Signaling ? */
+ EXCEPTION(EX_Invalid);
+ /* Convert to a QNaN */
+ FPU_st0_ptr->sigh |= 0x40000000;
+ }
+ break; /* return with a NaN in st(0) */
+ case TW_Empty:
+ stack_underflow(); /* Puts a QNaN in st(0) */
+ break;
+#ifdef PARANOID
+ default:
+ EXCEPTION(EX_INTERNAL | 0x0112);
+#endif /* PARANOID */
+ }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+static void
+f2xm1(void)
+{
+ switch (FPU_st0_tag) {
+ case TW_Valid:
+ {
+ FPU_REG rv, tmp;
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ if (FPU_st0_ptr->sign == SIGN_POS) {
+ /* poly_2xm1(x) requires 0 < x < 1. */
+ if (poly_2xm1(FPU_st0_ptr, &rv))
+ return; /* error */
+ reg_mul(&rv, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION);
+ } else {
+/* **** Should change poly_2xm1() to at least handle numbers near 0 */
+ /* poly_2xm1(x) doesn't handle negative
+ * numbers. */
+ /* So we compute (poly_2xm1(x+1)-1)/2, for -1
+ * < x < 0 */
+ reg_add(FPU_st0_ptr, &CONST_1, &tmp, FULL_PRECISION);
+ poly_2xm1(&tmp, &rv);
+ reg_mul(&rv, &tmp, &tmp, FULL_PRECISION);
+ reg_sub(&tmp, &CONST_1, FPU_st0_ptr, FULL_PRECISION);
+ FPU_st0_ptr->exp--;
+ if (FPU_st0_ptr->exp <= EXP_UNDER)
+ arith_underflow(FPU_st0_ptr);
+ }
+ return;
+ }
+ case TW_Zero:
+ return;
+ case TW_Infinity:
+ if (FPU_st0_ptr->sign == SIGN_NEG) {
+ /* -infinity gives -1 (p16-10) */
+ reg_move(&CONST_1, FPU_st0_ptr);
+ FPU_st0_ptr->sign = SIGN_NEG;
+ }
+ return;
+ default:
+ single_arg_error();
+ }
+}
+
+static void
+fptan(void)
+{
+ FPU_REG *st_new_ptr;
+ int q;
+ char arg_sign = FPU_st0_ptr->sign;
+
+ if (STACK_OVERFLOW) {
+ stack_overflow();
+ return;
+ }
+ switch (FPU_st0_tag) {
+ case TW_Valid:
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ FPU_st0_ptr->sign = SIGN_POS;
+ if ((q = trig_arg(FPU_st0_ptr)) != -1) {
+ if (q & 1)
+ reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION);
+
+ poly_tan(FPU_st0_ptr, FPU_st0_ptr);
+
+ FPU_st0_ptr->sign = (q & 1) ^ arg_sign;
+
+ if (FPU_st0_ptr->exp <= EXP_UNDER)
+ arith_underflow(FPU_st0_ptr);
+
+ push();
+ reg_move(&CONST_1, FPU_st0_ptr);
+ setcc(0);
+ } else {
+ /* Operand is out of range */
+ setcc(SW_C2);
+ FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
+ return;
+ }
+ break;
+ case TW_Infinity:
+ /* Operand is out of range */
+ setcc(SW_C2);
+ FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
+ return;
+ case TW_Zero:
+ push();
+ reg_move(&CONST_1, FPU_st0_ptr);
+ setcc(0);
+ break;
+ default:
+ single_arg_error();
+ break;
+ }
+}
+
+
+static void
+fxtract(void)
+{
+ FPU_REG *st_new_ptr;
+ register FPU_REG *st1_ptr = FPU_st0_ptr; /* anticipate */
+
+ if (STACK_OVERFLOW) {
+ stack_overflow();
+ return;
+ }
+ if (!(FPU_st0_tag ^ TW_Valid)) {
+ long e;
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ push();
+ reg_move(st1_ptr, FPU_st0_ptr);
+ FPU_st0_ptr->exp = EXP_BIAS;
+ e = st1_ptr->exp - EXP_BIAS;
+ convert_l2reg(&e, st1_ptr);
+ return;
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ char sign = FPU_st0_ptr->sign;
+ divide_by_zero(SIGN_NEG, FPU_st0_ptr);
+ push();
+ reg_move(&CONST_Z, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+ return;
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ char sign = FPU_st0_ptr->sign;
+ FPU_st0_ptr->sign = SIGN_POS;
+ push();
+ reg_move(&CONST_INF, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+ return;
+ } else
+ if (FPU_st0_tag == TW_NaN) {
+ if (!(FPU_st0_ptr->sigh & 0x40000000)) { /* Signaling ? */
+ EXCEPTION(EX_Invalid);
+ /* Convert to a QNaN */
+ FPU_st0_ptr->sigh |= 0x40000000;
+ }
+ push();
+ reg_move(st1_ptr, FPU_st0_ptr);
+ return;
+ } else
+ if (FPU_st0_tag == TW_Empty) {
+ /* Is this the correct
+ * behaviour? */
+ if (control_word & EX_Invalid) {
+ stack_underflow();
+ push();
+ stack_underflow();
+ } else
+ EXCEPTION(EX_StackUnder);
+ }
+#ifdef PARANOID
+ else
+ EXCEPTION(EX_INTERNAL | 0x119);
+#endif /* PARANOID */
+}
+
+
+static void
+fdecstp(void)
+{
+ top--; /* FPU_st0_ptr will be fixed in math_emulate()
+ * before the next instr */
+}
+
+static void
+fincstp(void)
+{
+ top++; /* FPU_st0_ptr will be fixed in math_emulate()
+ * before the next instr */
+}
+
+
+static void
+fsqrt_(void)
+{
+ if (!(FPU_st0_tag ^ TW_Valid)) {
+ int expon;
+
+ if (FPU_st0_ptr->sign == SIGN_NEG) {
+ arith_invalid(FPU_st0_ptr); /* sqrt(negative) is
+ * invalid */
+ return;
+ }
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ expon = FPU_st0_ptr->exp - EXP_BIAS;
+ FPU_st0_ptr->exp = EXP_BIAS + (expon & 1); /* make st(0) in [1.0
+ * .. 4.0) */
+
+ wm_sqrt(FPU_st0_ptr, control_word); /* Do the computation */
+
+ FPU_st0_ptr->exp += expon >> 1;
+ FPU_st0_ptr->sign = SIGN_POS;
+ } else
+ if (FPU_st0_tag == TW_Zero)
+ return;
+ else
+ if (FPU_st0_tag == TW_Infinity) {
+ if (FPU_st0_ptr->sign == SIGN_NEG)
+ arith_invalid(FPU_st0_ptr); /* sqrt(-Infinity) is
+ * invalid */
+ return;
+ } else {
+ single_arg_error();
+ return;
+ }
+
+}
+
+
+static void
+frndint_(void)
+{
+ if (!(FPU_st0_tag ^ TW_Valid)) {
+ if (FPU_st0_ptr->exp > EXP_BIAS + 63)
+ return;
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ round_to_int(FPU_st0_ptr); /* Fortunately, this can't
+ * overflow to 2^64 */
+ FPU_st0_ptr->exp = EXP_BIAS + 63;
+ normalize(FPU_st0_ptr);
+ return;
+ } else
+ if ((FPU_st0_tag == TW_Zero) || (FPU_st0_tag == TW_Infinity))
+ return;
+ else
+ single_arg_error();
+}
+
+
+static void
+fsin(void)
+{
+ char arg_sign = FPU_st0_ptr->sign;
+
+ if (FPU_st0_tag == TW_Valid) {
+ int q;
+ FPU_st0_ptr->sign = SIGN_POS;
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ if ((q = trig_arg(FPU_st0_ptr)) != -1) {
+ FPU_REG rv;
+
+ if (q & 1)
+ reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION);
+
+ poly_sine(FPU_st0_ptr, &rv);
+
+ setcc(0);
+ if (q & 2)
+ rv.sign ^= SIGN_POS ^ SIGN_NEG;
+ rv.sign ^= arg_sign;
+ reg_move(&rv, FPU_st0_ptr);
+
+ if (FPU_st0_ptr->exp <= EXP_UNDER)
+ arith_underflow(FPU_st0_ptr);
+
+ set_precision_flag_up(); /* We do not really know
+ * if up or down */
+
+ return;
+ } else {
+ /* Operand is out of range */
+ setcc(SW_C2);
+ FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
+ return;
+ }
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ setcc(0);
+ return;
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ /* Operand is out of range */
+ setcc(SW_C2);
+ FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
+ return;
+ } else
+ single_arg_error();
+}
+
+
+static int
+f_cos(FPU_REG * arg)
+{
+ char arg_sign = arg->sign;
+
+ if (arg->tag == TW_Valid) {
+ int q;
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return 1;
+#endif /* DENORM_OPERAND */
+
+ arg->sign = SIGN_POS;
+ if ((q = trig_arg(arg)) != -1) {
+ FPU_REG rv;
+
+ if (!(q & 1))
+ reg_sub(&CONST_1, arg, arg, FULL_PRECISION);
+
+ poly_sine(arg, &rv);
+
+ setcc(0);
+ if ((q + 1) & 2)
+ rv.sign ^= SIGN_POS ^ SIGN_NEG;
+ reg_move(&rv, arg);
+
+ set_precision_flag_up(); /* We do not really know
+ * if up or down */
+
+ return 0;
+ } else {
+ /* Operand is out of range */
+ setcc(SW_C2);
+ arg->sign = arg_sign; /* restore st(0) */
+ return 1;
+ }
+ } else
+ if (arg->tag == TW_Zero) {
+ reg_move(&CONST_1, arg);
+ setcc(0);
+ return 0;
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ /* Operand is out of range */
+ setcc(SW_C2);
+ arg->sign = arg_sign; /* restore st(0) */
+ return 1;
+ } else {
+ single_arg_error(); /* requires arg ==
+ * &st(0) */
+ return 1;
+ }
+}
+
+
+static void
+fcos(void)
+{
+ f_cos(FPU_st0_ptr);
+}
+
+
+static void
+fsincos(void)
+{
+ FPU_REG *st_new_ptr;
+ FPU_REG arg;
+
+ if (STACK_OVERFLOW) {
+ stack_overflow();
+ return;
+ }
+ reg_move(FPU_st0_ptr, &arg);
+ if (!f_cos(&arg)) {
+ fsin();
+ push();
+ reg_move(&arg, FPU_st0_ptr);
+ }
+}
+
+
+/*---------------------------------------------------------------------------*/
+/* The following all require two arguments: st(0) and st(1) */
+
+/* remainder of st(0) / st(1) */
+/* Assumes that st(0) and st(1) are both TW_Valid */
+static void
+fprem_kernel(int round)
+{
+ FPU_REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+
+ if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) {
+ FPU_REG tmp;
+ int old_cw = control_word;
+ int expdif = FPU_st0_ptr->exp - (st1_ptr)->exp;
+
+#ifdef DENORM_OPERAND
+ if (((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ control_word &= ~CW_RC;
+ control_word |= round;
+
+ if (expdif < 64) {
+ /* This should be the most common case */
+ long long q;
+ int c = 0;
+
+ reg_div(FPU_st0_ptr, st1_ptr, &tmp, FULL_PRECISION);
+
+ round_to_int(&tmp); /* Fortunately, this can't
+ * overflow to 2^64 */
+ tmp.exp = EXP_BIAS + 63;
+ q = *(long long *) &(tmp.sigl);
+ normalize(&tmp);
+
+ reg_mul(st1_ptr, &tmp, &tmp, FULL_PRECISION);
+ reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr, FULL_PRECISION);
+
+ if (q & 4)
+ c |= SW_C3;
+ if (q & 2)
+ c |= SW_C1;
+ if (q & 1)
+ c |= SW_C0;
+
+ setcc(c);
+ } else {
+ /* There is a large exponent difference ( >= 64 ) */
+ int N_exp;
+
+ reg_div(FPU_st0_ptr, st1_ptr, &tmp, FULL_PRECISION);
+ /* N is 'a number between 32 and 63' (p26-113) */
+ N_exp = (tmp.exp & 31) + 32;
+ tmp.exp = EXP_BIAS + N_exp;
+
+ round_to_int(&tmp); /* Fortunately, this can't
+ * overflow to 2^64 */
+ tmp.exp = EXP_BIAS + 63;
+ normalize(&tmp);
+
+ tmp.exp = EXP_BIAS + expdif - N_exp;
+
+ reg_mul(st1_ptr, &tmp, &tmp, FULL_PRECISION);
+ reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr, FULL_PRECISION);
+
+ setcc(SW_C2);
+ }
+ control_word = old_cw;
+
+ if (FPU_st0_ptr->exp <= EXP_UNDER)
+ arith_underflow(FPU_st0_ptr);
+ return;
+ } else
+ if ((FPU_st0_tag == TW_Empty) | (st1_tag == TW_Empty)) {
+ stack_underflow();
+ return;
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ if (st1_tag == TW_Valid) {
+
+#ifdef DENORM_OPERAND
+ if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ setcc(0);
+ return;
+ } else
+ if (st1_tag == TW_Zero) {
+ arith_invalid(FPU_st0_ptr);
+ return;
+ }
+ /* fprem(?,0) always invalid */
+ else
+ if (st1_tag == TW_Infinity) {
+ setcc(0);
+ return;
+ }
+ } else
+ if (FPU_st0_tag == TW_Valid) {
+ if (st1_tag == TW_Zero) {
+ arith_invalid(FPU_st0_ptr); /* fprem(Valid,Zero) is
+ * invalid */
+ return;
+ } else
+ if (st1_tag != TW_NaN) {
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ if (st1_tag == TW_Infinity) {
+ /* fprem(Valid,
+ * Infinity)
+ * is o.k. */
+ setcc(0);
+ return;
+ }
+ }
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ if (st1_tag != TW_NaN) {
+ arith_invalid(FPU_st0_ptr); /* fprem(Infinity,?) is
+ * invalid */
+ return;
+ }
+ }
+ /* One of the registers must contain a NaN is we got here. */
+
+#ifdef PARANOID
+ if ((FPU_st0_tag != TW_NaN) && (st1_tag != TW_NaN))
+ EXCEPTION(EX_INTERNAL | 0x118);
+#endif /* PARANOID */
+
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr);
+
+}
+
+
+/* ST(1) <- ST(1) * log ST; pop ST */
+static void
+fyl2x(void)
+{
+ FPU_REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+
+ if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) {
+ if (FPU_st0_ptr->sign == SIGN_POS) {
+ int saved_control, saved_status;
+
+#ifdef DENORM_OPERAND
+ if (((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ /* We use the general purpose arithmetic, so we need
+ * to save these. */
+ saved_status = status_word;
+ saved_control = control_word;
+ control_word = FULL_PRECISION;
+
+ poly_l2(FPU_st0_ptr, FPU_st0_ptr);
+
+ /* Enough of the basic arithmetic is done now */
+ control_word = saved_control;
+ status_word = saved_status;
+
+ /* Let the multiply set the flags */
+ reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION);
+
+ pop();
+ FPU_st0_ptr = &st(0);
+ } else {
+ /* negative */
+ pop();
+ FPU_st0_ptr = &st(0);
+ arith_invalid(FPU_st0_ptr); /* st(0) cannot be
+ * negative */
+ return;
+ }
+ } else
+ if ((FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty)) {
+ stack_underflow_pop(1);
+ return;
+ } else
+ if ((FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN)) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ } else
+ if ((FPU_st0_tag <= TW_Zero) && (st1_tag <= TW_Zero)) {
+ /* one of the args is zero, the other
+ * valid, or both zero */
+ if (FPU_st0_tag == TW_Zero) {
+ pop();
+ FPU_st0_ptr = &st(0);
+ if (FPU_st0_ptr->tag == TW_Zero)
+ arith_invalid(FPU_st0_ptr); /* Both args zero is
+ * invalid */
+#ifdef PECULIAR_486
+ /* This case is not
+ * specifically covered in the
+ * manual, but divide-by-zero
+ * would seem to be the best
+ * response. However, a real
+ * 80486 does it this way... */
+ else
+ if (FPU_st0_ptr->tag == TW_Infinity) {
+ reg_move(&CONST_INF, FPU_st0_ptr);
+ return;
+ }
+#endif /* PECULIAR_486 */
+ else
+ divide_by_zero(st1_ptr->sign ^ SIGN_NEG ^ SIGN_POS, FPU_st0_ptr);
+ return;
+ } else {
+ /* st(1) contains zero, st(0)
+ * valid <> 0 */
+ /* Zero is the valid answer */
+ char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+ if (FPU_st0_ptr->sign == SIGN_NEG) {
+ pop();
+ FPU_st0_ptr = &st(0);
+ arith_invalid(FPU_st0_ptr); /* log(negative) */
+ return;
+ }
+ if (FPU_st0_ptr->exp < EXP_BIAS)
+ sign ^= SIGN_NEG ^ SIGN_POS;
+ pop();
+ FPU_st0_ptr = &st(0);
+ reg_move(&CONST_Z, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+ return;
+ }
+ }
+ /* One or both arg must be an infinity */
+ else
+ if (FPU_st0_tag == TW_Infinity) {
+ if ((FPU_st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero)) {
+ pop();
+ FPU_st0_ptr = &st(0);
+ arith_invalid(FPU_st0_ptr); /* log(-infinity) or
+ * 0*log(infinity) */
+ return;
+ } else {
+ char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ pop();
+ FPU_st0_ptr = &st(0);
+ reg_move(&CONST_INF, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+ return;
+ }
+ }
+ /* st(1) must be infinity here */
+ else
+ if ((FPU_st0_tag == TW_Valid) && (FPU_st0_ptr->sign == SIGN_POS)) {
+ if (FPU_st0_ptr->exp >= EXP_BIAS) {
+ if ((FPU_st0_ptr->exp == EXP_BIAS) &&
+ (FPU_st0_ptr->sigh == 0x80000000) &&
+ (FPU_st0_ptr->sigl == 0)) {
+ /* st(0
+ * )
+ * hold
+ * s
+ * 1.0 */
+ pop();
+ FPU_st0_ptr = &st(0);
+ arith_invalid(FPU_st0_ptr); /* infinity*log(1) */
+ return;
+ }
+ /* st(0) is
+ * positive
+ * and > 1.0 */
+ pop();
+ } else {
+ /* st(0) is
+ * positive
+ * and < 1.0 */
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ st1_ptr->sign ^= SIGN_NEG;
+ pop();
+ }
+ return;
+ } else {
+ /* st(0) must be zero
+ * or negative */
+ if (FPU_st0_ptr->tag == TW_Zero) {
+ pop();
+ FPU_st0_ptr = st1_ptr;
+ st1_ptr->sign ^= SIGN_NEG ^ SIGN_POS;
+ /* This should
+ * be invalid,
+ * but a real
+ * 80486 is
+ * happy with
+ * it. */
+#ifndef PECULIAR_486
+ divide_by_zero(st1_ptr->sign, FPU_st0_ptr);
+#endif /* PECULIAR_486 */
+ } else {
+ pop();
+ FPU_st0_ptr = st1_ptr;
+ arith_invalid(FPU_st0_ptr); /* log(negative) */
+ }
+ return;
+ }
+}
+
+
+static void
+fpatan(void)
+{
+ FPU_REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+
+ if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) {
+ int saved_control, saved_status;
+ FPU_REG sum;
+ int quadrant = st1_ptr->sign | ((FPU_st0_ptr->sign) << 1);
+
+#ifdef DENORM_OPERAND
+ if (((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ /* We use the general purpose arithmetic so we need to save
+ * these. */
+ saved_status = status_word;
+ saved_control = control_word;
+ control_word = FULL_PRECISION;
+
+ st1_ptr->sign = FPU_st0_ptr->sign = SIGN_POS;
+ if (compare(st1_ptr) == COMP_A_lt_B) {
+ quadrant |= 4;
+ reg_div(FPU_st0_ptr, st1_ptr, &sum, FULL_PRECISION);
+ } else
+ reg_div(st1_ptr, FPU_st0_ptr, &sum, FULL_PRECISION);
+
+ poly_atan(&sum);
+
+ if (quadrant & 4) {
+ reg_sub(&CONST_PI2, &sum, &sum, FULL_PRECISION);
+ }
+ if (quadrant & 2) {
+ reg_sub(&CONST_PI, &sum, &sum, FULL_PRECISION);
+ }
+ if (quadrant & 1)
+ sum.sign ^= SIGN_POS ^ SIGN_NEG;
+
+ /* All of the basic arithmetic is done now */
+ control_word = saved_control;
+ status_word = saved_status;
+
+ reg_move(&sum, st1_ptr);
+ } else
+ if ((FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty)) {
+ stack_underflow_pop(1);
+ return;
+ } else
+ if ((FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN)) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ } else
+ if ((FPU_st0_tag == TW_Infinity) || (st1_tag == TW_Infinity)) {
+ char sign = st1_ptr->sign;
+ if (FPU_st0_tag == TW_Infinity) {
+ if (st1_tag == TW_Infinity) {
+ if (FPU_st0_ptr->sign == SIGN_POS) {
+ reg_move(&CONST_PI4, st1_ptr);
+ } else
+ reg_add(&CONST_PI4, &CONST_PI2, st1_ptr, FULL_PRECISION);
+ } else {
+
+#ifdef DENORM_OPERAND
+ if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ if (FPU_st0_ptr->sign == SIGN_POS) {
+ reg_move(&CONST_Z, st1_ptr);
+ pop();
+ return;
+ } else
+ reg_move(&CONST_PI, st1_ptr);
+ }
+ } else {
+ /* st(1) is infinity, st(0)
+ * not infinity */
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ reg_move(&CONST_PI2, st1_ptr);
+ }
+ st1_ptr->sign = sign;
+ } else
+ if (st1_tag == TW_Zero) {
+ /* st(0) must be valid or zero */
+ char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ if (FPU_st0_ptr->sign == SIGN_POS) {
+ reg_move(&CONST_Z, st1_ptr);
+ pop();
+ return;
+ } else
+ reg_move(&CONST_PI, st1_ptr);
+ st1_ptr->sign = sign;
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ /* st(1) must be
+ * TW_Valid here */
+ char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ reg_move(&CONST_PI2, st1_ptr);
+ st1_ptr->sign = sign;
+ }
+#ifdef PARANOID
+ else
+ EXCEPTION(EX_INTERNAL | 0x220);
+#endif /* PARANOID */
+
+ pop();
+ set_precision_flag_up();/* We do not really know if up or down */
+}
+
+
+static void
+fprem(void)
+{
+ fprem_kernel(RC_CHOP);
+}
+
+
+static void
+fprem1(void)
+{
+ fprem_kernel(RC_RND);
+}
+
+
+static void
+fyl2xp1(void)
+{
+ FPU_REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+
+ if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) {
+ int saved_control, saved_status;
+
+#ifdef DENORM_OPERAND
+ if (((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ /* We use the general purpose arithmetic so we need to save
+ * these. */
+ saved_status = status_word;
+ saved_control = control_word;
+ control_word = FULL_PRECISION;
+
+ if (poly_l2p1(FPU_st0_ptr, FPU_st0_ptr)) {
+ arith_invalid(st1_ptr); /* poly_l2p1() returned
+ * invalid */
+ pop();
+ return;
+ }
+ /* Enough of the basic arithmetic is done now */
+ control_word = saved_control;
+ status_word = saved_status;
+
+ /* Let the multiply set the flags */
+ reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION);
+
+ pop();
+ } else
+ if ((FPU_st0_tag == TW_Empty) | (st1_tag == TW_Empty)) {
+ stack_underflow_pop(1);
+ return;
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ if (st1_tag <= TW_Zero) {
+
+#ifdef DENORM_OPERAND
+ if ((st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) &&
+ (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ st1_ptr->sign ^= FPU_st0_ptr->sign;
+ reg_move(FPU_st0_ptr, st1_ptr);
+ } else
+ if (st1_tag == TW_Infinity) {
+ arith_invalid(st1_ptr); /* Infinity*log(1) */
+ pop();
+ return;
+ } else
+ if (st1_tag == TW_NaN) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ }
+#ifdef PARANOID
+ else {
+ EXCEPTION(EX_INTERNAL | 0x116);
+ return;
+ }
+#endif /* PARANOID */
+ pop();
+ return;
+ } else
+ if (FPU_st0_tag == TW_Valid) {
+ if (st1_tag == TW_Zero) {
+ if (FPU_st0_ptr->sign == SIGN_NEG) {
+ if (FPU_st0_ptr->exp >= EXP_BIAS) {
+ /* st(0) holds
+ * <= -1.0 */
+ arith_invalid(st1_ptr); /* infinity*log(1) */
+ pop();
+ return;
+ }
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+ st1_ptr->sign ^= SIGN_POS ^ SIGN_NEG;
+ pop();
+ return;
+ }
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+ pop();
+ return;
+ }
+ if (st1_tag == TW_Infinity) {
+ if (FPU_st0_ptr->sign == SIGN_NEG) {
+ if ((FPU_st0_ptr->exp >= EXP_BIAS) &&
+ !((FPU_st0_ptr->sigh == 0x80000000) &&
+ (FPU_st0_ptr->sigl == 0))) {
+ /* st(0) holds
+ * < -1.0 */
+ arith_invalid(st1_ptr);
+ pop();
+ return;
+ }
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+ st1_ptr->sign ^= SIGN_POS ^ SIGN_NEG;
+ pop();
+ return;
+ }
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+ pop();
+ return;
+ }
+ if (st1_tag == TW_NaN) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ }
+ } else
+ if (FPU_st0_tag == TW_NaN) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ if (st1_tag == TW_NaN) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ } else
+ if ((FPU_st0_ptr->sign == SIGN_NEG) ||
+ (st1_tag == TW_Zero)) {
+ arith_invalid(st1_ptr); /* log(infinity) */
+ pop();
+ return;
+ }
+ /* st(1) must be valid
+ * here. */
+
+#ifdef DENORM_OPERAND
+ if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ /* The Manual says
+ * that log(Infinity)
+ * is invalid, but a
+ * real 80486 sensibly
+ * says that it is
+ * o.k. */
+ {
+ char sign = st1_ptr->sign;
+ reg_move(&CONST_INF, st1_ptr);
+ st1_ptr->sign = sign;
+ }
+ pop();
+ return;
+ }
+#ifdef PARANOID
+ else {
+ EXCEPTION(EX_INTERNAL | 0x117);
+ }
+#endif /* PARANOID */
+}
+
+
+static void
+emu_fscale(void)
+{
+ FPU_REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+ int old_cw = control_word;
+
+ if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) {
+ long scale;
+ FPU_REG tmp;
+
+#ifdef DENORM_OPERAND
+ if (((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ if (st1_ptr->exp > EXP_BIAS + 30) {
+ /* 2^31 is far too large, would require 2^(2^30) or
+ * 2^(-2^30) */
+ char sign;
+
+ if (st1_ptr->sign == SIGN_POS) {
+ EXCEPTION(EX_Overflow);
+ sign = FPU_st0_ptr->sign;
+ reg_move(&CONST_INF, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+ } else {
+ EXCEPTION(EX_Underflow);
+ sign = FPU_st0_ptr->sign;
+ reg_move(&CONST_Z, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+ }
+ return;
+ }
+ control_word &= ~CW_RC;
+ control_word |= RC_CHOP;
+ reg_move(st1_ptr, &tmp);
+ round_to_int(&tmp); /* This can never overflow here */
+ control_word = old_cw;
+ scale = st1_ptr->sign ? -tmp.sigl : tmp.sigl;
+ scale += FPU_st0_ptr->exp;
+ FPU_st0_ptr->exp = scale;
+
+ /* Use round_reg() to properly detect under/overflow etc */
+ round_reg(FPU_st0_ptr, 0, control_word);
+
+ return;
+ } else
+ if (FPU_st0_tag == TW_Valid) {
+ if (st1_tag == TW_Zero) {
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ return;
+ }
+ if (st1_tag == TW_Infinity) {
+ char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ if (sign == SIGN_POS) {
+ reg_move(&CONST_INF, FPU_st0_ptr);
+ } else
+ reg_move(&CONST_Z, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+ return;
+ }
+ if (st1_tag == TW_NaN) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr);
+ return;
+ }
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ if (st1_tag == TW_Valid) {
+
+#ifdef DENORM_OPERAND
+ if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ return;
+ } else
+ if (st1_tag == TW_Zero) {
+ return;
+ } else
+ if (st1_tag == TW_Infinity) {
+ if (st1_ptr->sign == SIGN_NEG)
+ return;
+ else {
+ arith_invalid(FPU_st0_ptr); /* Zero scaled by
+ * +Infinity */
+ return;
+ }
+ } else
+ if (st1_tag == TW_NaN) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr);
+ return;
+ }
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ if (st1_tag == TW_Valid) {
+
+#ifdef DENORM_OPERAND
+ if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ return;
+ }
+ if (((st1_tag == TW_Infinity) && (st1_ptr->sign == SIGN_POS))
+ || (st1_tag == TW_Zero))
+ return;
+ else
+ if (st1_tag == TW_Infinity) {
+ arith_invalid(FPU_st0_ptr); /* Infinity scaled by
+ * -Infinity */
+ return;
+ } else
+ if (st1_tag == TW_NaN) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr);
+ return;
+ }
+ } else
+ if (FPU_st0_tag == TW_NaN) {
+ if (st1_tag != TW_Empty) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr);
+ return;
+ }
+ }
+#ifdef PARANOID
+ if (!((FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty))) {
+ EXCEPTION(EX_INTERNAL | 0x115);
+ return;
+ }
+#endif
+
+ /* At least one of st(0), st(1) must be empty */
+ stack_underflow();
+
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+static FUNC trig_table_a[] = {
+ f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp
+};
+
+void
+trig_a(void)
+{
+ (trig_table_a[FPU_rm]) ();
+}
+
+
+static FUNC trig_table_b[] =
+{
+ fprem, fyl2xp1, fsqrt_, fsincos, frndint_, emu_fscale, fsin, fcos
+};
+
+void
+trig_b(void)
+{
+ (trig_table_b[FPU_rm]) ();
+}
diff --git a/sys/gnu/i386/fpemul/get_address.c b/sys/gnu/i386/fpemul/get_address.c
new file mode 100644
index 0000000..5839e57
--- /dev/null
+++ b/sys/gnu/i386/fpemul/get_address.c
@@ -0,0 +1,203 @@
+/*
+ * get_address.c
+ *
+ * Get the effective address from an FPU instruction.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: get_address.c,v 1.2 1994/04/29 21:16:26 gclarkii Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | Note: |
+ | The file contains code which accesses user memory. |
+ | Emulator static data may change when user memory is accessed, due to |
+ | other processes using the emulator while swapping is in progress. |
+ +---------------------------------------------------------------------------*/
+
+#include "param.h"
+#include "proc.h"
+#include "systm.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+#include "machine/reg.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+
+static int reg_offset[] = {
+tEAX, tECX, tEDX, tEBX, tESP, tEBP, tESI, tEDI};
+#define REG_(x) (*(((int*)FPU_info) + reg_offset[(x)]))
+
+void *FPU_data_address;
+
+
+/* Decode the SIB byte. This function assumes mod != 0 */
+static void *
+sib(int mod)
+{
+ unsigned char ss, index, base;
+ long offset;
+
+ REENTRANT_CHECK(OFF);
+ base = fubyte((char *) FPU_EIP); /* The SIB byte */
+ REENTRANT_CHECK(ON);
+ FPU_EIP++;
+ ss = base >> 6;
+ index = (base >> 3) & 7;
+ base &= 7;
+
+ if ((mod == 0) && (base == 5))
+ offset = 0; /* No base register */
+ else
+ offset = REG_(base);
+
+ if (index == 4) {
+ /* No index register */
+ /* A non-zero ss is illegal */
+ if (ss)
+ EXCEPTION(EX_Invalid);
+ } else {
+ offset += (REG_(index)) << ss;
+ }
+
+ if (mod == 1) {
+ /* 8 bit signed displacement */
+ REENTRANT_CHECK(OFF);
+ offset += (signed char) fubyte((char *) FPU_EIP);
+ REENTRANT_CHECK(ON);
+ FPU_EIP++;
+ } else
+ if (mod == 2 || base == 5) { /* The second condition also
+ * has mod==0 */
+ /* 32 bit displacment */
+ REENTRANT_CHECK(OFF);
+ offset += (signed) fuword((unsigned long *) FPU_EIP);
+ REENTRANT_CHECK(ON);
+ FPU_EIP += 4;
+ }
+ return (void *) offset;
+}
+
+
+/*
+ MOD R/M byte: MOD == 3 has a special use for the FPU
+ SIB byte used iff R/M = 100b
+
+ 7 6 5 4 3 2 1 0
+ ..... ......... .........
+ MOD OPCODE(2) R/M
+
+
+ SIB byte
+
+ 7 6 5 4 3 2 1 0
+ ..... ......... .........
+ SS INDEX BASE
+
+*/
+
+void
+get_address(unsigned char FPU_modrm)
+{
+ unsigned char mod;
+ long *cpu_reg_ptr;
+ int offset = 0; /* Initialized just to stop compiler warnings. */
+
+ mod = (FPU_modrm >> 6) & 3;
+
+ if (FPU_rm == 4 && mod != 3) {
+ FPU_data_address = sib(mod);
+ return;
+ }
+ cpu_reg_ptr = (long *) &REG_(FPU_rm);
+ switch (mod) {
+ case 0:
+ if (FPU_rm == 5) {
+ /* Special case: disp32 */
+ REENTRANT_CHECK(OFF);
+ offset = fuword((unsigned long *) FPU_EIP);
+ REENTRANT_CHECK(ON);
+ FPU_EIP += 4;
+ FPU_data_address = (void *) offset;
+ return;
+ } else {
+ FPU_data_address = (void *) *cpu_reg_ptr; /* Just return the
+ * contents of the cpu
+ * register */
+ return;
+ }
+ case 1:
+ /* 8 bit signed displacement */
+ REENTRANT_CHECK(OFF);
+ offset = (signed char) fubyte((char *) FPU_EIP);
+ REENTRANT_CHECK(ON);
+ FPU_EIP++;
+ break;
+ case 2:
+ /* 32 bit displacement */
+ REENTRANT_CHECK(OFF);
+ offset = (signed) fuword((unsigned long *) FPU_EIP);
+ REENTRANT_CHECK(ON);
+ FPU_EIP += 4;
+ break;
+ case 3:
+ /* Not legal for the FPU */
+ EXCEPTION(EX_Invalid);
+ }
+
+ FPU_data_address = offset + (char *) *cpu_reg_ptr;
+}
diff --git a/sys/gnu/i386/fpemul/load_store.c b/sys/gnu/i386/fpemul/load_store.c
new file mode 100644
index 0000000..0192291
--- /dev/null
+++ b/sys/gnu/i386/fpemul/load_store.c
@@ -0,0 +1,269 @@
+/*
+ * load_store.c
+ *
+ * This file contains most of the code to interpret the FPU instructions
+ * which load and store from user memory.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: load_store.c,v 1.2 1994/04/29 21:16:27 gclarkii Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | Note: |
+ | The file contains code which accesses user memory. |
+ | Emulator static data may change when user memory is accessed, due to |
+ | other processes using the emulator while swapping is in progress. |
+ +---------------------------------------------------------------------------*/
+
+#include "param.h"
+#include "proc.h"
+#include "systm.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+#include "status_w.h"
+
+
+#define _NONE_ 0 /* FPU_st0_ptr etc not needed */
+#define _REG0_ 1 /* Will be storing st(0) */
+#define _PUSH_ 3 /* Need to check for space to push onto stack */
+#define _null_ 4 /* Function illegal or not implemented */
+
+#define pop_0() { pop_ptr->tag = TW_Empty; top++; }
+
+
+static unsigned char type_table[32] = {
+ _PUSH_, _PUSH_, _PUSH_, _PUSH_,
+ _null_, _null_, _null_, _null_,
+ _REG0_, _REG0_, _REG0_, _REG0_,
+ _REG0_, _REG0_, _REG0_, _REG0_,
+ _NONE_, _null_, _NONE_, _PUSH_,
+ _NONE_, _PUSH_, _null_, _PUSH_,
+ _NONE_, _null_, _NONE_, _REG0_,
+ _NONE_, _REG0_, _NONE_, _REG0_
+};
+
+void
+load_store_instr(char type)
+{
+ FPU_REG *pop_ptr; /* We need a version of FPU_st0_ptr which
+ * won't change. */
+
+ pop_ptr = NULL; /* Initialized just to stop compiler warnings. */
+
+
+ switch (type_table[(int) (unsigned) type]) {
+ case _NONE_:
+ break;
+ case _REG0_:
+ pop_ptr = &st(0); /* Some of these instructions pop
+ * after storing */
+
+ FPU_st0_ptr = pop_ptr; /* Set the global variables. */
+ FPU_st0_tag = FPU_st0_ptr->tag;
+ break;
+ case _PUSH_:
+ {
+ pop_ptr = &st(-1);
+ if (pop_ptr->tag != TW_Empty) {
+ stack_overflow();
+ return;
+ }
+ top--;
+ }
+ break;
+ case _null_:
+ return Un_impl();
+#ifdef PARANOID
+ default:
+ return EXCEPTION(EX_INTERNAL);
+#endif /* PARANOID */
+ }
+
+ switch (type) {
+ case 000: /* fld m32real */
+ reg_load_single();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
+ reg_move(&FPU_loaded_data, pop_ptr);
+ break;
+ case 001: /* fild m32int */
+ reg_load_int32();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
+ reg_move(&FPU_loaded_data, pop_ptr);
+ break;
+ case 002: /* fld m64real */
+ reg_load_double();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
+ reg_move(&FPU_loaded_data, pop_ptr);
+ break;
+ case 003: /* fild m16int */
+ reg_load_int16();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
+ reg_move(&FPU_loaded_data, pop_ptr);
+ break;
+ case 010: /* fst m32real */
+ reg_store_single();
+ break;
+ case 011: /* fist m32int */
+ reg_store_int32();
+ break;
+ case 012: /* fst m64real */
+ reg_store_double();
+ break;
+ case 013: /* fist m16int */
+ reg_store_int16();
+ break;
+ case 014: /* fstp m32real */
+ if (reg_store_single())
+ pop_0();/* pop only if the number was actually stored
+ * (see the 80486 manual p16-28) */
+ break;
+ case 015: /* fistp m32int */
+ if (reg_store_int32())
+ pop_0();/* pop only if the number was actually stored
+ * (see the 80486 manual p16-28) */
+ break;
+ case 016: /* fstp m64real */
+ if (reg_store_double())
+ pop_0();/* pop only if the number was actually stored
+ * (see the 80486 manual p16-28) */
+ break;
+ case 017: /* fistp m16int */
+ if (reg_store_int16())
+ pop_0();/* pop only if the number was actually stored
+ * (see the 80486 manual p16-28) */
+ break;
+ case 020: /* fldenv m14/28byte */
+ fldenv();
+ break;
+ case 022: /* frstor m94/108byte */
+ frstor();
+ break;
+ case 023: /* fbld m80dec */
+ reg_load_bcd();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
+ reg_move(&FPU_loaded_data, pop_ptr);
+ break;
+ case 024: /* fldcw */
+ REENTRANT_CHECK(OFF);
+ control_word = fuword((unsigned short *) FPU_data_address);
+ REENTRANT_CHECK(ON);
+#ifdef NO_UNDERFLOW_TRAP
+ if (!(control_word & EX_Underflow)) {
+ control_word |= EX_Underflow;
+ }
+#endif
+ FPU_data_address = (void *) data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+ break;
+ case 025: /* fld m80real */
+ reg_load_extended();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
+ reg_move(&FPU_loaded_data, pop_ptr);
+ break;
+ case 027: /* fild m64int */
+ reg_load_int64();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
+ reg_move(&FPU_loaded_data, pop_ptr);
+ break;
+ case 030: /* fstenv m14/28byte */
+ fstenv();
+ FPU_data_address = (void *) data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+ break;
+ case 032: /* fsave */
+ fsave();
+ FPU_data_address = (void *) data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+ break;
+ case 033: /* fbstp m80dec */
+ if (reg_store_bcd())
+ pop_0();/* pop only if the number was actually stored
+ * (see the 80486 manual p16-28) */
+ break;
+ case 034: /* fstcw m16int */
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, FPU_data_address, 2);*/
+ suword( (short *) FPU_data_address,control_word);
+ REENTRANT_CHECK(ON);
+ FPU_data_address = (void *) data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+ break;
+ case 035: /* fstp m80real */
+ if (reg_store_extended())
+ pop_0();/* pop only if the number was actually stored
+ * (see the 80486 manual p16-28) */
+ break;
+ case 036: /* fstsw m2byte */
+ status_word &= ~SW_Top;
+ status_word |= (top & 7) << SW_Top_Shift;
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, FPU_data_address, 2);*/
+ suword( (short *) FPU_data_address,status_word);
+ REENTRANT_CHECK(ON);
+ FPU_data_address = (void *) data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+ break;
+ case 037: /* fistp m64int */
+ if (reg_store_int64())
+ pop_0();/* pop only if the number was actually stored
+ * (see the 80486 manual p16-28) */
+ break;
+ }
+}
diff --git a/sys/gnu/i386/fpemul/math_emu.h b/sys/gnu/i386/fpemul/math_emu.h
new file mode 100644
index 0000000..4dc90b8
--- /dev/null
+++ b/sys/gnu/i386/fpemul/math_emu.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * $Id:$
+ *
+ */
+
+#ifndef _MATH_EMU_H
+#define _MATH_EMU_H
+
+struct fpu_reg {
+ char sign;
+ char tag;
+ long exp;
+ u_long sigl;
+ u_long sigh;
+};
+
+union i387_union {
+ struct i387_hard_struct {
+ long cwd;
+ long swd;
+ long twd;
+ long fip;
+ long fcs;
+ long foo;
+ long fos;
+ long st_space[20]; /* 8*10 bytes for each FP-reg = 80
+ * bytes */
+ } hard;
+ struct i387_soft_struct {
+ long cwd;
+ long swd;
+ long twd;
+ long fip;
+ long fcs;
+ long foo;
+ long fos;
+ long top;
+ struct fpu_reg regs[8]; /* 8*16 bytes for each FP-reg = 128
+ * bytes */
+ unsigned char lookahead;
+ struct trapframe *frame;
+ unsigned long entry_eip;
+ int orig_eip;
+ } soft;
+};
+#endif
diff --git a/sys/gnu/i386/fpemul/poly_2xm1.c b/sys/gnu/i386/fpemul/poly_2xm1.c
new file mode 100644
index 0000000..b7e81f2
--- /dev/null
+++ b/sys/gnu/i386/fpemul/poly_2xm1.c
@@ -0,0 +1,141 @@
+/*
+ * poly_2xm1.c
+ *
+ * Function to compute 2^x-1 by a polynomial approximation.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: poly_2xm1.c,v 1.2 1994/04/29 21:23:25 gclarkii Exp $
+ *
+ */
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+
+
+
+#define HIPOWER 13
+static unsigned short lterms[HIPOWER][4] =
+{
+ {0x79b5, 0xd1cf, 0x17f7, 0xb172},
+ {0x1b56, 0x058b, 0x7bff, 0x3d7f},
+ {0x8bb0, 0x8250, 0x846b, 0x0e35},
+ {0xbc65, 0xf747, 0x556d, 0x0276},
+ {0x17cb, 0x9e39, 0x61ff, 0x0057},
+ {0xe018, 0x9776, 0x1848, 0x000a},
+ {0x66f2, 0xff30, 0xffe5, 0x0000},
+ {0x682f, 0xffb6, 0x162b, 0x0000},
+ {0xb7ca, 0x2956, 0x01b5, 0x0000},
+ {0xcd3e, 0x4817, 0x001e, 0x0000},
+ {0xb7e2, 0xecbe, 0x0001, 0x0000},
+ {0x0ed5, 0x1a27, 0x0000, 0x0000},
+ {0x101d, 0x0222, 0x0000, 0x0000},
+};
+
+
+/*--- poly_2xm1() -----------------------------------------------------------+
+ | |
+ +---------------------------------------------------------------------------*/
+int
+poly_2xm1(FPU_REG * arg, FPU_REG * result)
+{
+ short exponent;
+ long long Xll;
+ FPU_REG accum;
+
+
+ exponent = arg->exp - EXP_BIAS;
+
+ if (arg->tag == TW_Zero) {
+ /* Return 0.0 */
+ reg_move(&CONST_Z, result);
+ return 0;
+ }
+ if (exponent >= 0) { /* Can't hack a number >= 1.0 */
+ arith_invalid(result); /* Number too large */
+ return 1;
+ }
+ if (arg->sign != SIGN_POS) { /* Can't hack a number < 0.0 */
+ arith_invalid(result); /* Number negative */
+ return 1;
+ }
+ if (exponent < -64) {
+ reg_move(&CONST_LN2, result);
+ return 0;
+ }
+ *(unsigned *) &Xll = arg->sigl;
+ *(((unsigned *) &Xll) + 1) = arg->sigh;
+ if (exponent < -1) {
+ /* shift the argument right by the required places */
+ if (shrx(&Xll, -1 - exponent) >= (unsigned)0x80000000)
+ Xll++; /* round up */
+ }
+ *(short *) &(accum.sign) = 0; /* will be a valid positive nr with
+ * expon = 0 */
+ accum.exp = 0;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((unsigned *) &accum.sigl, (unsigned *) &Xll, lterms, HIPOWER - 1);
+
+ /* Convert to 64 bit signed-compatible */
+ accum.exp += EXP_BIAS - 1;
+
+ reg_move(&accum, result);
+
+ normalize(result);
+
+ return 0;
+
+}
diff --git a/sys/gnu/i386/fpemul/poly_atan.c b/sys/gnu/i386/fpemul/poly_atan.c
new file mode 100644
index 0000000..179049f
--- /dev/null
+++ b/sys/gnu/i386/fpemul/poly_atan.c
@@ -0,0 +1,252 @@
+/*
+ * p_atan.c
+ *
+ * Compute the tan of a FPU_REG, using a polynomial approximation.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: poly_atan.c,v 1.3 1994/04/29 21:23:26 gclarkii Exp $
+ *
+ */
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+
+
+#define HIPOWERon 6 /* odd poly, negative terms */
+static unsigned oddnegterms[HIPOWERon][2] =
+{
+ {0x00000000, 0x00000000}, /* for + 1.0 */
+ {0x763b6f3d, 0x1adc4428},
+ {0x20f0630b, 0x0502909d},
+ {0x4e825578, 0x0198ce38},
+ {0x22b7cb87, 0x008da6e3},
+ {0x9b30ca03, 0x00239c79}
+};
+#define HIPOWERop 6 /* odd poly, positive terms */
+static unsigned oddplterms[HIPOWERop][2] =
+{
+ {0xa6f67cb8, 0x94d910bd},
+ {0xa02ffab4, 0x0a43cb45},
+ {0x04265e6b, 0x02bf5655},
+ {0x0a728914, 0x00f280f7},
+ {0x6d640e01, 0x004d6556},
+ {0xf1dd2dbf, 0x000a530a}
+};
+
+
+static unsigned denomterm[2] =
+{0xfc4bd208, 0xea2e6612};
+
+
+
+/*--- poly_atan() -----------------------------------------------------------+
+ | |
+ +---------------------------------------------------------------------------*/
+void
+poly_atan(FPU_REG * arg)
+{
+ char recursions = 0;
+ short exponent;
+ FPU_REG odd_poly, even_poly, pos_poly, neg_poly;
+ FPU_REG argSq;
+ long long arg_signif, argSqSq;
+
+
+#ifdef PARANOID
+ if (arg->sign != 0) { /* Can't hack a number < 0.0 */
+ arith_invalid(arg);
+ return;
+ } /* Need a positive number */
+#endif /* PARANOID */
+
+ exponent = arg->exp - EXP_BIAS;
+
+ if (arg->tag == TW_Zero) {
+ /* Return 0.0 */
+ reg_move(&CONST_Z, arg);
+ return;
+ }
+ if (exponent >= -2) {
+ /* argument is in the range [0.25 .. 1.0] */
+ if (exponent >= 0) {
+#ifdef PARANOID
+ if ((exponent == 0) &&
+ (arg->sigl == 0) && (arg->sigh == 0x80000000))
+#endif /* PARANOID */
+ {
+ reg_move(&CONST_PI4, arg);
+ return;
+ }
+#ifdef PARANOID
+ EXCEPTION(EX_INTERNAL | 0x104); /* There must be a logic
+ * error */
+#endif /* PARANOID */
+ }
+ /* If the argument is greater than sqrt(2)-1 (=0.414213562...) */
+ /* convert the argument by an identity for atan */
+ if ((exponent >= -1) || (arg->sigh > 0xd413ccd0)) {
+ FPU_REG numerator, denom;
+
+ recursions++;
+
+ arg_signif = *(long long *) &(arg->sigl);
+ if (exponent < -1) {
+ if (shrx(&arg_signif, -1 - exponent) >= (unsigned)0x80000000)
+ arg_signif++; /* round up */
+ }
+ *(long long *) &(numerator.sigl) = -arg_signif;
+ numerator.exp = EXP_BIAS - 1;
+ normalize(&numerator); /* 1 - arg */
+
+ arg_signif = *(long long *) &(arg->sigl);
+ if (shrx(&arg_signif, -exponent) >= (unsigned)0x80000000)
+ arg_signif++; /* round up */
+ *(long long *) &(denom.sigl) = arg_signif;
+ denom.sigh |= 0x80000000; /* 1 + arg */
+
+ arg->exp = numerator.exp;
+ reg_u_div(&numerator, &denom, arg, FULL_PRECISION);
+
+ exponent = arg->exp - EXP_BIAS;
+ }
+ }
+ *(long long *) &arg_signif = *(long long *) &(arg->sigl);
+
+#ifdef PARANOID
+ /* This must always be true */
+ if (exponent >= -1) {
+ EXCEPTION(EX_INTERNAL | 0x120); /* There must be a logic error */
+ }
+#endif /* PARANOID */
+
+ /* shift the argument right by the required places */
+ if (shrx(&arg_signif, -1 - exponent) >= (unsigned)0x80000000)
+ arg_signif++; /* round up */
+
+ /* Now have arg_signif with binary point at the left .1xxxxxxxx */
+ mul64(&arg_signif, &arg_signif, (long long *) (&argSq.sigl));
+ mul64((long long *) (&argSq.sigl), (long long *) (&argSq.sigl), &argSqSq);
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(pos_poly.sign) = 0;
+ pos_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &pos_poly.sigl, (unsigned *) &argSqSq,
+ (unsigned short (*)[4]) oddplterms, HIPOWERop - 1);
+ mul64((long long *) (&argSq.sigl), (long long *) (&pos_poly.sigl),
+ (long long *) (&pos_poly.sigl));
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(neg_poly.sign) = 0;
+ neg_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &neg_poly.sigl, (unsigned *) &argSqSq,
+ (unsigned short (*)[4]) oddnegterms, HIPOWERon - 1);
+
+ /* Subtract the mantissas */
+ *((long long *) (&pos_poly.sigl)) -= *((long long *) (&neg_poly.sigl));
+
+ reg_move(&pos_poly, &odd_poly);
+ poly_add_1(&odd_poly);
+
+ /* The complete odd polynomial */
+ reg_u_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION);
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(even_poly.sign) = 0;
+
+ mul64((long long *) (&argSq.sigl),
+ (long long *) (&denomterm), (long long *) (&even_poly.sigl));
+
+ poly_add_1(&even_poly);
+
+ reg_div(&odd_poly, &even_poly, arg, FULL_PRECISION);
+
+ if (recursions)
+ reg_sub(&CONST_PI4, arg, arg, FULL_PRECISION);
+}
+
+
+/* The argument to this function must be polynomial() compatible,
+ i.e. have an exponent (not checked) of EXP_BIAS-1 but need not
+ be normalized.
+ This function adds 1.0 to the (assumed positive) argument. */
+void
+poly_add_1(FPU_REG * src)
+{
+/* Rounding in a consistent direction produces better results
+ for the use of this function in poly_atan. Simple truncation
+ is used here instead of round-to-nearest. */
+
+#ifdef OBSOLETE
+ char round = (src->sigl & 3) == 3;
+#endif /* OBSOLETE */
+
+ shrx(&src->sigl, 1);
+
+#ifdef OBSOLETE
+ if (round)
+ (*(long long *) &src->sigl)++; /* Round to even */
+#endif /* OBSOLETE */
+
+ src->sigh |= 0x80000000;
+
+ src->exp = EXP_BIAS;
+
+}
diff --git a/sys/gnu/i386/fpemul/poly_div.s b/sys/gnu/i386/fpemul/poly_div.s
new file mode 100644
index 0000000..3ac5bf0
--- /dev/null
+++ b/sys/gnu/i386/fpemul/poly_div.s
@@ -0,0 +1,144 @@
+ .file "poly_div.S"
+/*
+ * poly_div.S
+ *
+ * A set of functions to divide 64 bit integers by fixed numbers.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: poly_div.s,v 1.2 1994/04/29 21:23:27 gclarkii Exp $
+ *
+ */
+
+#include "fpu_asm.h"
+
+.text
+
+/*---------------------------------------------------------------------------*/
+ .align 2,144
+.globl _poly_div2
+_poly_div2:
+ pushl %ebp
+ movl %esp,%ebp
+
+ movl PARAM1,%ecx
+ movw (%ecx),%ax
+
+ shrl $1,4(%ecx)
+ rcrl $1,(%ecx)
+
+ testw $1,%ax
+ je poly_div2_exit
+
+ addl $1,(%ecx)
+ adcl $0,4(%ecx)
+poly_div2_exit:
+
+ leave
+ ret
+/*---------------------------------------------------------------------------*/
+ .align 2,144
+.globl _poly_div4
+_poly_div4:
+ pushl %ebp
+ movl %esp,%ebp
+
+ movl PARAM1,%ecx
+ movw (%ecx),%ax
+
+ movl 4(%ecx),%edx
+ shll $30,%edx
+
+ shrl $2,4(%ecx)
+ shrl $2,(%ecx)
+
+ orl %edx,(%ecx)
+
+ testw $2,%ax
+ je poly_div4_exit
+
+ addl $1,(%ecx)
+ adcl $0,4(%ecx)
+poly_div4_exit:
+
+ leave
+ ret
+/*---------------------------------------------------------------------------*/
+ .align 2,144
+.globl _poly_div16
+_poly_div16:
+ pushl %ebp
+ movl %esp,%ebp
+
+ movl PARAM1,%ecx
+ movw (%ecx),%ax
+
+ movl 4(%ecx),%edx
+ shll $28,%edx
+
+ shrl $4,4(%ecx)
+ shrl $4,(%ecx)
+
+ orl %edx,(%ecx)
+
+ testw $8,%ax
+ je poly_div16_exit
+
+ addl $1,(%ecx)
+ adcl $0,4(%ecx)
+poly_div16_exit:
+
+ leave
+ ret
+/*---------------------------------------------------------------------------*/
diff --git a/sys/gnu/i386/fpemul/poly_l2.c b/sys/gnu/i386/fpemul/poly_l2.c
new file mode 100644
index 0000000..0607c16
--- /dev/null
+++ b/sys/gnu/i386/fpemul/poly_l2.c
@@ -0,0 +1,318 @@
+/*
+ * poly_l2.c
+ *
+ * Compute the base 2 log of a FPU_REG, using a polynomial approximation.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: poly_l2.c,v 1.4 1994/04/30 16:47:08 gclarkii Exp $
+ *
+ */
+
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+
+
+
+#define HIPOWER 9
+static unsigned short lterms[HIPOWER][4] =
+{
+ /* Ideal computation with these coeffs gives about 64.6 bit rel
+ * accuracy. */
+ {0xe177, 0xb82f, 0x7652, 0x7154},
+ {0xee0f, 0xe80f, 0x2770, 0x7b1c},
+ {0x0fc0, 0xbe87, 0xb143, 0x49dd},
+ {0x78b9, 0xdadd, 0xec54, 0x34c2},
+ {0x003a, 0x5de9, 0x628b, 0x2909},
+ {0x5588, 0xed16, 0x4abf, 0x2193},
+ {0xb461, 0x85f7, 0x347a, 0x1c6a},
+ {0x0975, 0x87b3, 0xd5bf, 0x1876},
+ {0xe85c, 0xcec9, 0x84e7, 0x187d}
+};
+
+
+
+
+/*--- poly_l2() -------------------------------------------------------------+
+ | Base 2 logarithm by a polynomial approximation. |
+ +---------------------------------------------------------------------------*/
+void
+poly_l2(FPU_REG * arg, FPU_REG * result)
+{
+ short exponent;
+ char zero; /* flag for an Xx == 0 */
+ unsigned short bits, shift;
+ long long Xsq;
+ FPU_REG accum, denom, num, Xx;
+
+
+ exponent = arg->exp - EXP_BIAS;
+
+ accum.tag = TW_Valid; /* set the tags to Valid */
+
+ if (arg->sigh > (unsigned) 0xb504f334) {
+ /* This is good enough for the computation of the polynomial
+ * sum, but actually results in a loss of precision for the
+ * computation of Xx. This will matter only if exponent
+ * becomes zero. */
+ exponent++;
+ accum.sign = 1; /* sign to negative */
+ num.exp = EXP_BIAS; /* needed to prevent errors in div
+ * routine */
+ reg_u_div(&CONST_1, arg, &num, FULL_PRECISION);
+ } else {
+ accum.sign = 0; /* set the sign to positive */
+ num.sigl = arg->sigl; /* copy the mantissa */
+ num.sigh = arg->sigh;
+ }
+
+
+ /* shift num left, lose the ms bit */
+ num.sigh <<= 1;
+ if (num.sigl & 0x80000000)
+ num.sigh |= 1;
+ num.sigl <<= 1;
+
+ denom.sigl = num.sigl;
+ denom.sigh = num.sigh;
+ poly_div4((long long *) &(denom.sigl));
+ denom.sigh += 0x80000000; /* set the msb */
+ Xx.exp = EXP_BIAS; /* needed to prevent errors in div routine */
+ reg_u_div(&num, &denom, &Xx, FULL_PRECISION);
+
+ zero = !(Xx.sigh | Xx.sigl);
+
+ mul64((long long *) &Xx.sigl, (long long *) &Xx.sigl, &Xsq);
+ poly_div16(&Xsq);
+
+ accum.exp = -1; /* exponent of accum */
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((unsigned *) &accum.sigl, (unsigned *) &Xsq, lterms, HIPOWER - 1);
+
+ if (!exponent) {
+ /* If the exponent is zero, then we would lose precision by
+ * sticking to fixed point computation here */
+ /* We need to re-compute Xx because of loss of precision. */
+ FPU_REG lXx;
+ char sign;
+
+ sign = accum.sign;
+ accum.sign = 0;
+
+ /* make accum compatible and normalize */
+ accum.exp = EXP_BIAS + accum.exp;
+ normalize(&accum);
+
+ if (zero) {
+ reg_move(&CONST_Z, result);
+ } else {
+ /* we need to re-compute lXx to better accuracy */
+ num.tag = TW_Valid; /* set the tags to Vaild */
+ num.sign = 0; /* set the sign to positive */
+ num.exp = EXP_BIAS - 1;
+ if (sign) {
+ /* The argument is of the form 1-x */
+ /* Use 1-1/(1-x) = x/(1-x) */
+ *((long long *) &num.sigl) = -*((long long *) &(arg->sigl));
+ normalize(&num);
+ reg_div(&num, arg, &num, FULL_PRECISION);
+ } else {
+ normalize(&num);
+ }
+
+ denom.tag = TW_Valid; /* set the tags to Valid */
+ denom.sign = SIGN_POS; /* set the sign to positive */
+ denom.exp = EXP_BIAS;
+
+ reg_div(&num, &denom, &lXx, FULL_PRECISION);
+
+ reg_u_mul(&lXx, &accum, &accum, FULL_PRECISION);
+
+ reg_u_add(&lXx, &accum, result, FULL_PRECISION);
+
+ normalize(result);
+ }
+
+ result->sign = sign;
+ return;
+ }
+ mul64((long long *) &accum.sigl,
+ (long long *) &Xx.sigl, (long long *) &accum.sigl);
+
+ *((long long *) (&accum.sigl)) += *((long long *) (&Xx.sigl));
+
+ if (Xx.sigh > accum.sigh) {
+ /* There was an overflow */
+
+ poly_div2((long long *) &accum.sigl);
+ accum.sigh |= 0x80000000;
+ accum.exp++;
+ }
+ /* When we add the exponent to the accum result later, we will require
+ * that their signs are the same. Here we ensure that this is so. */
+ if (exponent && ((exponent < 0) ^ (accum.sign))) {
+ /* signs are different */
+
+ accum.sign = !accum.sign;
+
+ /* An exceptional case is when accum is zero */
+ if (accum.sigl | accum.sigh) {
+ /* find 1-accum */
+ /* Shift to get exponent == 0 */
+ if (accum.exp < 0) {
+ poly_div2((long long *) &accum.sigl);
+ accum.exp++;
+ }
+ /* Just negate, but throw away the sign */
+ *((long long *) &(accum.sigl)) = -*((long long *) &(accum.sigl));
+ if (exponent < 0)
+ exponent++;
+ else
+ exponent--;
+ }
+ }
+ shift = exponent >= 0 ? exponent : -exponent;
+ bits = 0;
+ if (shift) {
+ if (accum.exp) {
+ accum.exp++;
+ poly_div2((long long *) &accum.sigl);
+ }
+ while (shift) {
+ poly_div2((long long *) &accum.sigl);
+ if (shift & 1)
+ accum.sigh |= 0x80000000;
+ shift >>= 1;
+ bits++;
+ }
+ }
+ /* Convert to 64 bit signed-compatible */
+ accum.exp += bits + EXP_BIAS - 1;
+
+ reg_move(&accum, result);
+ normalize(result);
+
+ return;
+}
+
+
+/*--- poly_l2p1() -----------------------------------------------------------+
+ | Base 2 logarithm by a polynomial approximation. |
+ | log2(x+1) |
+ +---------------------------------------------------------------------------*/
+int
+poly_l2p1(FPU_REG * arg, FPU_REG * result)
+{
+ char sign = 0;
+ long long Xsq;
+ FPU_REG arg_pl1, denom, accum, local_arg, poly_arg;
+
+
+ sign = arg->sign;
+
+ reg_add(arg, &CONST_1, &arg_pl1, FULL_PRECISION);
+
+ if ((arg_pl1.sign) | (arg_pl1.tag)) { /* We need a valid positive
+ * number! */
+ return 1;
+ }
+ reg_add(&CONST_1, &arg_pl1, &denom, FULL_PRECISION);
+ reg_div(arg, &denom, &local_arg, FULL_PRECISION);
+ local_arg.sign = 0; /* Make the sign positive */
+
+ /* Now we need to check that |local_arg| is less than 3-2*sqrt(2) =
+ * 0.17157.. = .0xafb0ccc0 * 2^-2 */
+
+ if (local_arg.exp >= EXP_BIAS - 3) {
+ if ((local_arg.exp > EXP_BIAS - 3) ||
+ (local_arg.sigh > (unsigned) 0xafb0ccc0)) {
+ /* The argument is large */
+ poly_l2(&arg_pl1, result);
+ return 0;
+ }
+ }
+ /* Make a copy of local_arg */
+ reg_move(&local_arg, &poly_arg);
+
+ /* Get poly_arg bits aligned as required */
+ shrx((unsigned *) &(poly_arg.sigl), -(poly_arg.exp - EXP_BIAS + 3));
+
+ mul64((long long *) &(poly_arg.sigl), (long long *) &(poly_arg.sigl), &Xsq);
+ poly_div16(&Xsq);
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &accum.sigl, (unsigned *) &Xsq, lterms, HIPOWER - 1);
+
+ accum.tag = TW_Valid; /* set the tags to Valid */
+ accum.sign = SIGN_POS; /* and make accum positive */
+
+ /* make accum compatible and normalize */
+ accum.exp = EXP_BIAS - 1;
+ normalize(&accum);
+
+ reg_u_mul(&local_arg, &accum, &accum, FULL_PRECISION);
+
+ reg_u_add(&local_arg, &accum, result, FULL_PRECISION);
+
+ /* Multiply the result by 2 */
+ result->exp++;
+
+ result->sign = sign;
+
+ return 0;
+}
diff --git a/sys/gnu/i386/fpemul/poly_mul64.s b/sys/gnu/i386/fpemul/poly_mul64.s
new file mode 100644
index 0000000..c0f5d0f
--- /dev/null
+++ b/sys/gnu/i386/fpemul/poly_mul64.s
@@ -0,0 +1,124 @@
+/*
+ * poly_mul64.S
+ *
+ * Multiply two 64 bit integers.
+ *
+ * Call from C as:
+ * void mul64(long long *a, long long *b, long long *result)
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: poly_mul64.s,v 1.2 1994/04/29 21:23:29 gclarkii Exp $
+ *
+ */
+
+
+#include "fpu_asm.h"
+
+.text
+ .align 2,144
+.globl _mul64
+_mul64:
+ pushl %ebp
+ movl %esp,%ebp
+ subl $16,%esp
+ pushl %esi
+ pushl %ebx
+
+ movl PARAM1,%esi
+ movl PARAM2,%ecx
+ movl PARAM3,%ebx
+
+ xor %eax,%eax
+ movl %eax,-4(%ebp)
+ movl %eax,-8(%ebp)
+
+ movl (%esi),%eax
+ mull (%ecx)
+ movl %eax,-16(%ebp) /* Not used */
+ movl %edx,-12(%ebp)
+
+ movl (%esi),%eax
+ mull 4(%ecx)
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl 4(%esi),%eax
+ mull (%ecx)
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl 4(%esi),%eax
+ mull 4(%ecx)
+ addl %eax,-8(%ebp)
+ adcl %edx,-4(%ebp)
+
+ testb $128,-9(%ebp)
+ je L_no_round
+
+ addl $1,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+L_no_round:
+ movl -8(%ebp),%esi
+ movl %esi,(%ebx)
+ movl -4(%ebp),%esi
+ movl %esi,4(%ebx)
+
+ popl %ebx
+ popl %esi
+ leave
+ ret
diff --git a/sys/gnu/i386/fpemul/poly_sin.c b/sys/gnu/i386/fpemul/poly_sin.c
new file mode 100644
index 0000000..fd3cbd2
--- /dev/null
+++ b/sys/gnu/i386/fpemul/poly_sin.c
@@ -0,0 +1,192 @@
+/*
+ * poly_sin.c
+ *
+ * Computation of an approximation of the sin function by a polynomial
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: poly_sin.c,v 1.3 1994/04/29 21:23:30 gclarkii Exp $
+ *
+ */
+
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+
+
+#define HIPOWER 5
+static unsigned short lterms[HIPOWER][4] =
+{
+ {0x846a, 0x42d1, 0xb544, 0x921f},
+ {0xe110, 0x75aa, 0xbc67, 0x1466},
+ {0x503d, 0xa43f, 0x83c1, 0x000a},
+ {0x8f9d, 0x7a19, 0x00f4, 0x0000},
+ {0xda03, 0x06aa, 0x0000, 0x0000},
+};
+
+static unsigned short negterms[HIPOWER][4] =
+{
+ {0x95ed, 0x2df2, 0xe731, 0xa55d},
+ {0xd159, 0xe62b, 0xd2cc, 0x0132},
+ {0x6342, 0xe9fb, 0x3c60, 0x0000},
+ {0x6256, 0xdf5a, 0x0002, 0x0000},
+ {0xf279, 0x000b, 0x0000, 0x0000},
+};
+
+
+/*--- poly_sine() -----------------------------------------------------------+
+ | |
+ +---------------------------------------------------------------------------*/
+void
+poly_sine(FPU_REG * arg, FPU_REG * result)
+{
+ short exponent;
+ FPU_REG Xx, Xx2, Xx4, accum, negaccum;
+
+
+ exponent = arg->exp - EXP_BIAS;
+
+ if (arg->tag == TW_Zero) {
+ /* Return 0.0 */
+ reg_move(&CONST_Z, result);
+ return;
+ }
+#ifdef PARANOID
+ if (arg->sign != 0) { /* Can't hack a number < 0.0 */
+ EXCEPTION(EX_Invalid);
+ reg_move(&CONST_QNaN, result);
+ return;
+ }
+ if (exponent >= 0) { /* Can't hack a number > 1.0 */
+ if ((exponent == 0) && (arg->sigl == 0) && (arg->sigh == 0x80000000)) {
+ reg_move(&CONST_1, result);
+ return;
+ }
+ EXCEPTION(EX_Invalid);
+ reg_move(&CONST_QNaN, result);
+ return;
+ }
+#endif /* PARANOID */
+
+ Xx.sigl = arg->sigl;
+ Xx.sigh = arg->sigh;
+ if (exponent < -1) {
+ /* shift the argument right by the required places */
+ if (shrx(&(Xx.sigl), -1 - exponent) >= (unsigned)0x80000000)
+ (*((long long *) (&(Xx.sigl))))++; /* round up */
+ }
+ mul64((long long *) &(Xx.sigl), (long long *) &(Xx.sigl),
+ (long long *) &(Xx2.sigl));
+ mul64((long long *) &(Xx2.sigl), (long long *) &(Xx2.sigl),
+ (long long *) &(Xx4.sigl));
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(accum.sign) = 0;
+ accum.exp = 0;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &(accum.sigl), &(Xx4.sigl), lterms, HIPOWER - 1);
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(negaccum.sign) = 0;
+ negaccum.exp = 0;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &(negaccum.sigl), &(Xx4.sigl), negterms, HIPOWER - 1);
+ mul64((long long *) &(Xx2.sigl), (long long *) &(negaccum.sigl),
+ (long long *) &(negaccum.sigl));
+
+ /* Subtract the mantissas */
+ *((long long *) (&(accum.sigl))) -= *((long long *) (&(negaccum.sigl)));
+
+ /* Convert to 64 bit signed-compatible */
+ accum.exp = EXP_BIAS - 1 + accum.exp;
+
+ *(short *) &(result->sign) = *(short *) &(accum.sign);
+ result->exp = accum.exp;
+ result->sigl = accum.sigl;
+ result->sigh = accum.sigh;
+
+ normalize(result);
+
+ reg_mul(result, arg, result, FULL_PRECISION);
+ reg_u_add(result, arg, result, FULL_PRECISION);
+
+ /* A small overflow may be possible... but an illegal result. */
+ if (result->exp >= EXP_BIAS) {
+ if ((result->exp > EXP_BIAS) /* Larger or equal 2.0 */
+ ||(result->sigl > 1) /* Larger than 1.0+msb */
+ ||(result->sigh != 0x80000000) /* Much > 1.0 */
+ ) {
+#ifdef DEBUGGING
+ RE_ENTRANT_CHECK_OFF
+ printk("\nEXP=%d, MS=%08x, LS=%08x\n", result->exp,
+ result->sigh, result->sigl);
+ RE_ENTRANT_CHECK_ON
+#endif /* DEBUGGING */
+ EXCEPTION(EX_INTERNAL | 0x103);
+ }
+#ifdef DEBUGGING
+ RE_ENTRANT_CHECK_OFF
+ printk("\n***CORRECTING ILLEGAL RESULT*** in poly_sin() computation\n");
+ printk("EXP=%d, MS=%08x, LS=%08x\n", result->exp,
+ result->sigh, result->sigl);
+ RE_ENTRANT_CHECK_ON
+#endif /* DEBUGGING */
+
+ result->sigl = 0; /* Truncate the result to 1.00 */
+ }
+}
diff --git a/sys/gnu/i386/fpemul/poly_tan.c b/sys/gnu/i386/fpemul/poly_tan.c
new file mode 100644
index 0000000..7f70388
--- /dev/null
+++ b/sys/gnu/i386/fpemul/poly_tan.c
@@ -0,0 +1,229 @@
+/*
+ * poly_tan.c
+ *
+ * Compute the tan of a FPU_REG, using a polynomial approximation.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: poly_tan.c,v 1.3 1994/04/29 21:23:31 gclarkii Exp $
+ *
+ */
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+
+
+#define HIPOWERop 3 /* odd poly, positive terms */
+static unsigned short oddplterms[HIPOWERop][4] =
+{
+ {0x846a, 0x42d1, 0xb544, 0x921f},
+ {0x6fb2, 0x0215, 0x95c0, 0x099c},
+ {0xfce6, 0x0cc8, 0x1c9a, 0x0000}
+};
+#define HIPOWERon 2 /* odd poly, negative terms */
+static unsigned short oddnegterms[HIPOWERon][4] =
+{
+ {0x6906, 0xe205, 0x25c8, 0x8838},
+ {0x1dd7, 0x3fe3, 0x944e, 0x002c}
+};
+#define HIPOWERep 2 /* even poly, positive terms */
+static unsigned short evenplterms[HIPOWERep][4] =
+{
+ {0xdb8f, 0x3761, 0x1432, 0x2acf},
+ {0x16eb, 0x13c1, 0x3099, 0x0003}
+};
+#define HIPOWERen 2 /* even poly, negative terms */
+static unsigned short evennegterms[HIPOWERen][4] =
+{
+ {0x3a7c, 0xe4c5, 0x7f87, 0x2945},
+ {0x572b, 0x664c, 0xc543, 0x018c}
+};
+
+
+/*--- poly_tan() ------------------------------------------------------------+
+ | |
+ +---------------------------------------------------------------------------*/
+void
+poly_tan(FPU_REG * arg, FPU_REG * y_reg)
+{
+ char invert = 0;
+ short exponent;
+ FPU_REG odd_poly, even_poly, pos_poly, neg_poly;
+ FPU_REG argSq;
+ long long arg_signif, argSqSq;
+
+
+ exponent = arg->exp - EXP_BIAS;
+
+ if (arg->tag == TW_Zero) {
+ /* Return 0.0 */
+ reg_move(&CONST_Z, y_reg);
+ return;
+ }
+ if (exponent >= -1) {
+ /* argument is in the range [0.5 .. 1.0] */
+ if (exponent >= 0) {
+#ifdef PARANOID
+ if ((exponent == 0) &&
+ (arg->sigl == 0) && (arg->sigh == 0x80000000))
+#endif /* PARANOID */
+ {
+ arith_overflow(y_reg);
+ return;
+ }
+#ifdef PARANOID
+ EXCEPTION(EX_INTERNAL | 0x104); /* There must be a logic
+ * error */
+ return;
+#endif /* PARANOID */
+ }
+ /* The argument is in the range [0.5 .. 1.0) */
+ /* Convert the argument to a number in the range (0.0 .. 0.5] */
+ *((long long *) (&arg->sigl)) = -*((long long *) (&arg->sigl));
+ normalize(arg); /* Needed later */
+ exponent = arg->exp - EXP_BIAS;
+ invert = 1;
+ }
+#ifdef PARANOID
+ if (arg->sign != 0) { /* Can't hack a number < 0.0 */
+ arith_invalid(y_reg);
+ return;
+ } /* Need a positive number */
+#endif /* PARANOID */
+
+ *(long long *) &arg_signif = *(long long *) &(arg->sigl);
+ if (exponent < -1) {
+ /* shift the argument right by the required places */
+ if (shrx(&arg_signif, -1 - exponent) >= (unsigned)0x80000000)
+ arg_signif++; /* round up */
+ }
+ mul64(&arg_signif, &arg_signif, (long long *) (&argSq.sigl));
+ mul64((long long *) (&argSq.sigl), (long long *) (&argSq.sigl), &argSqSq);
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(pos_poly.sign) = 0;
+ pos_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &pos_poly.sigl, (unsigned *) &argSqSq, oddplterms, HIPOWERop - 1);
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(neg_poly.sign) = 0;
+ neg_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &neg_poly.sigl, (unsigned *) &argSqSq, oddnegterms, HIPOWERon - 1);
+ mul64((long long *) (&argSq.sigl), (long long *) (&neg_poly.sigl),
+ (long long *) (&neg_poly.sigl));
+
+ /* Subtract the mantissas */
+ *((long long *) (&pos_poly.sigl)) -= *((long long *) (&neg_poly.sigl));
+
+ /* Convert to 64 bit signed-compatible */
+ pos_poly.exp -= 1;
+
+ reg_move(&pos_poly, &odd_poly);
+ normalize(&odd_poly);
+
+ reg_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION);
+ reg_u_add(&odd_poly, arg, &odd_poly, FULL_PRECISION); /* This is just the odd
+ * polynomial */
+
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(pos_poly.sign) = 0;
+ pos_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &pos_poly.sigl, (unsigned *) &argSqSq, evenplterms, HIPOWERep - 1);
+ mul64((long long *) (&argSq.sigl),
+ (long long *) (&pos_poly.sigl), (long long *) (&pos_poly.sigl));
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(neg_poly.sign) = 0;
+ neg_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &neg_poly.sigl, (unsigned *) &argSqSq, evennegterms, HIPOWERen - 1);
+
+ /* Subtract the mantissas */
+ *((long long *) (&neg_poly.sigl)) -= *((long long *) (&pos_poly.sigl));
+ /* and multiply by argSq */
+
+ /* Convert argSq to a valid reg number */
+ *(short *) &(argSq.sign) = 0;
+ argSq.exp = EXP_BIAS - 1;
+ normalize(&argSq);
+
+ /* Convert to 64 bit signed-compatible */
+ neg_poly.exp -= 1;
+
+ reg_move(&neg_poly, &even_poly);
+ normalize(&even_poly);
+
+ reg_mul(&even_poly, &argSq, &even_poly, FULL_PRECISION);
+ reg_add(&even_poly, &argSq, &even_poly, FULL_PRECISION);
+ reg_sub(&CONST_1, &even_poly, &even_poly, FULL_PRECISION); /* This is just the even
+ * polynomial */
+
+ /* Now ready to copy the results */
+ if (invert) {
+ reg_div(&even_poly, &odd_poly, y_reg, FULL_PRECISION);
+ } else {
+ reg_div(&odd_poly, &even_poly, y_reg, FULL_PRECISION);
+ }
+
+}
diff --git a/sys/gnu/i386/fpemul/polynomial.s b/sys/gnu/i386/fpemul/polynomial.s
new file mode 100644
index 0000000..f54c729
--- /dev/null
+++ b/sys/gnu/i386/fpemul/polynomial.s
@@ -0,0 +1,192 @@
+/*
+ * polynomial.S
+ *
+ * Fixed point arithmetic polynomial evaluation.
+ *
+ * Call from C as:
+ * void polynomial(unsigned accum[], unsigned x[], unsigned terms[][2],
+ * int n)
+ *
+ * Computes:
+ * terms[0] + (terms[1] + (terms[2] + ... + (terms[n-1]*x)*x)*x)*x) ... )*x
+ * The result is returned in accum.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: polynomial.s,v 1.2 1994/04/29 21:23:31 gclarkii Exp $
+ *
+ */
+
+ .file "fpolynom.s"
+
+#include "fpu_asm.h"
+
+
+/* #define EXTRA_PRECISE*/
+
+#define TERM_SIZE $8
+
+
+.text
+ .align 2,144
+.globl _polynomial
+_polynomial:
+ pushl %ebp
+ movl %esp,%ebp
+ subl $32,%esp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi /* accum */
+ movl PARAM2,%edi /* x */
+ movl PARAM3,%ebx /* terms */
+ movl PARAM4,%ecx /* n */
+
+ movl TERM_SIZE,%eax
+ mull %ecx
+ movl %eax,%ecx
+
+ movl 4(%ebx,%ecx,1),%edx /* terms[n] */
+ movl %edx,-20(%ebp)
+ movl (%ebx,%ecx,1),%edx /* terms[n] */
+ movl %edx,-24(%ebp)
+ xor %eax,%eax
+ movl %eax,-28(%ebp)
+
+ subl TERM_SIZE,%ecx
+ js L_accum_done
+
+L_accum_loop:
+ xor %eax,%eax
+ movl %eax,-4(%ebp)
+ movl %eax,-8(%ebp)
+
+#ifdef EXTRA_PRECISE
+ movl -28(%ebp),%eax
+ mull 4(%edi) /* x ms long */
+ movl %edx,-12(%ebp)
+#endif EXTRA_PRECISE
+
+ movl -24(%ebp),%eax
+ mull (%edi) /* x ls long */
+/* movl %eax,-16(%ebp) */ /* Not needed */
+ addl %edx,-12(%ebp)
+ adcl $0,-8(%ebp)
+
+ movl -24(%ebp),%eax
+ mull 4(%edi) /* x ms long */
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl -20(%ebp),%eax
+ mull (%edi)
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl -20(%ebp),%eax
+ mull 4(%edi)
+ addl %eax,-8(%ebp)
+ adcl %edx,-4(%ebp)
+
+/* Now add the next term */
+ movl (%ebx,%ecx,1),%eax
+ addl %eax,-8(%ebp)
+ movl 4(%ebx,%ecx,1),%eax
+ adcl %eax,-4(%ebp)
+
+/* And put into the second register */
+ movl -4(%ebp),%eax
+ movl %eax,-20(%ebp)
+ movl -8(%ebp),%eax
+ movl %eax,-24(%ebp)
+
+#ifdef EXTRA_PRECISE
+ movl -12(%ebp),%eax
+ movl %eax,-28(%ebp)
+#else
+ testb $128,-25(%ebp)
+ je L_no_poly_round
+
+ addl $1,-24(%ebp)
+ adcl $0,-20(%ebp)
+L_no_poly_round:
+#endif EXTRA_PRECISE
+
+ subl TERM_SIZE,%ecx
+ jns L_accum_loop
+
+L_accum_done:
+#ifdef EXTRA_PRECISE
+/* And round the result */
+ testb $128,-25(%ebp)
+ je L_poly_done
+
+ addl $1,-24(%ebp)
+ adcl $0,-20(%ebp)
+#endif EXTRA_PRECISE
+
+L_poly_done:
+ movl -24(%ebp),%eax
+ movl %eax,(%esi)
+ movl -20(%ebp),%eax
+ movl %eax,4(%esi)
+
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
diff --git a/sys/gnu/i386/fpemul/reg_add_sub.c b/sys/gnu/i386/fpemul/reg_add_sub.c
new file mode 100644
index 0000000..a122452
--- /dev/null
+++ b/sys/gnu/i386/fpemul/reg_add_sub.c
@@ -0,0 +1,303 @@
+/*
+ * reg_add_sub.c
+ *
+ * Functions to add or subtract two registers and put the result in a third.
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_add_sub.c,v 1.2 1994/04/29 21:30:15 gclarkii Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | For each function, the destination may be any FPU_REG, including one of |
+ | the source FPU_REGs. |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+#include "fpu_system.h"
+
+
+void
+reg_add(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w)
+{
+ int diff;
+
+ if (!(a->tag | b->tag)) {
+ /* Both registers are valid */
+ if (!(a->sign ^ b->sign)) {
+ /* signs are the same */
+ reg_u_add(a, b, dest, control_w);
+ dest->sign = a->sign;
+ return;
+ }
+ /* The signs are different, so do a subtraction */
+ diff = a->exp - b->exp;
+ if (!diff) {
+ diff = a->sigh - b->sigh; /* Works only if ms bits
+ * are identical */
+ if (!diff) {
+ diff = a->sigl > b->sigl;
+ if (!diff)
+ diff = -(a->sigl < b->sigl);
+ }
+ }
+ if (diff > 0) {
+ reg_u_sub(a, b, dest, control_w);
+ dest->sign = a->sign;
+ } else
+ if (diff == 0) {
+ reg_move(&CONST_Z, dest);
+ /* sign depends upon rounding mode */
+ dest->sign = ((control_w & CW_RC) != RC_DOWN)
+ ? SIGN_POS : SIGN_NEG;
+ } else {
+ reg_u_sub(b, a, dest, control_w);
+ dest->sign = b->sign;
+ }
+ return;
+ } else {
+ if ((a->tag == TW_NaN) || (b->tag == TW_NaN)) {
+ real_2op_NaN(a, b, dest);
+ return;
+ } else
+ if (a->tag == TW_Zero) {
+ if (b->tag == TW_Zero) {
+ char different_signs = a->sign ^ b->sign;
+ /* Both are zero, result will be zero. */
+ reg_move(a, dest);
+ if (different_signs) {
+ /* Signs are different. */
+ /* Sign of answer depends upon
+ * rounding mode. */
+ dest->sign = ((control_w & CW_RC) != RC_DOWN)
+ ? SIGN_POS : SIGN_NEG;
+ }
+ } else {
+#ifdef DENORM_OPERAND
+ if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(b, dest);
+ }
+ return;
+ } else
+ if (b->tag == TW_Zero) {
+#ifdef DENORM_OPERAND
+ if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(a, dest);
+ return;
+ } else
+ if (a->tag == TW_Infinity) {
+ if (b->tag != TW_Infinity) {
+#ifdef DENORM_OPERAND
+ if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(a, dest);
+ return;
+ }
+ if (a->sign == b->sign) {
+ /* They are both + or
+ * - infinity */
+ reg_move(a, dest);
+ return;
+ }
+ arith_invalid(dest); /* Infinity-Infinity is
+ * undefined. */
+ return;
+ } else
+ if (b->tag == TW_Infinity) {
+#ifdef DENORM_OPERAND
+ if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(b, dest);
+ return;
+ }
+ }
+#ifdef PARANOID
+ EXCEPTION(EX_INTERNAL | 0x101);
+#endif
+}
+
+
+/* Subtract b from a. (a-b) -> dest */
+void
+reg_sub(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w)
+{
+ int diff;
+
+ if (!(a->tag | b->tag)) {
+ /* Both registers are valid */
+ diff = a->exp - b->exp;
+ if (!diff) {
+ diff = a->sigh - b->sigh; /* Works only if ms bits
+ * are identical */
+ if (!diff) {
+ diff = a->sigl > b->sigl;
+ if (!diff)
+ diff = -(a->sigl < b->sigl);
+ }
+ }
+ switch (a->sign * 2 + b->sign) {
+ case 0: /* P - P */
+ case 3: /* N - N */
+ if (diff > 0) {
+ reg_u_sub(a, b, dest, control_w);
+ dest->sign = a->sign;
+ } else
+ if (diff == 0) {
+#ifdef DENORM_OPERAND
+ if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(&CONST_Z, dest);
+ /* sign depends upon rounding mode */
+ dest->sign = ((control_w & CW_RC) != RC_DOWN)
+ ? SIGN_POS : SIGN_NEG;
+ } else {
+ reg_u_sub(b, a, dest, control_w);
+ dest->sign = a->sign ^ SIGN_POS ^ SIGN_NEG;
+ }
+ return;
+ case 1: /* P - N */
+ reg_u_add(a, b, dest, control_w);
+ dest->sign = SIGN_POS;
+ return;
+ case 2: /* N - P */
+ reg_u_add(a, b, dest, control_w);
+ dest->sign = SIGN_NEG;
+ return;
+ }
+ } else {
+ if ((a->tag == TW_NaN) || (b->tag == TW_NaN)) {
+ real_2op_NaN(a, b, dest);
+ return;
+ } else
+ if (b->tag == TW_Zero) {
+ if (a->tag == TW_Zero) {
+ char same_signs = !(a->sign ^ b->sign);
+ /* Both are zero, result will be zero. */
+ reg_move(a, dest); /* Answer for different
+ * signs. */
+ if (same_signs) {
+ /* Sign depends upon rounding
+ * mode */
+ dest->sign = ((control_w & CW_RC) != RC_DOWN)
+ ? SIGN_POS : SIGN_NEG;
+ }
+ } else {
+#ifdef DENORM_OPERAND
+ if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(a, dest);
+ }
+ return;
+ } else
+ if (a->tag == TW_Zero) {
+#ifdef DENORM_OPERAND
+ if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(b, dest);
+ dest->sign ^= SIGN_POS ^ SIGN_NEG;
+ return;
+ } else
+ if (a->tag == TW_Infinity) {
+ if (b->tag != TW_Infinity) {
+#ifdef DENORM_OPERAND
+ if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(a, dest);
+ return;
+ }
+ /* Both args are Infinity */
+ if (a->sign == b->sign) {
+ arith_invalid(dest); /* Infinity-Infinity is
+ * undefined. */
+ return;
+ }
+ reg_move(a, dest);
+ return;
+ } else
+ if (b->tag == TW_Infinity) {
+#ifdef DENORM_OPERAND
+ if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(b, dest);
+ dest->sign ^= SIGN_POS ^ SIGN_NEG;
+ return;
+ }
+ }
+#ifdef PARANOID
+ EXCEPTION(EX_INTERNAL | 0x110);
+#endif
+}
diff --git a/sys/gnu/i386/fpemul/reg_compare.c b/sys/gnu/i386/fpemul/reg_compare.c
new file mode 100644
index 0000000..a4f8ae7
--- /dev/null
+++ b/sys/gnu/i386/fpemul/reg_compare.c
@@ -0,0 +1,384 @@
+/*
+ * reg_compare.c
+ *
+ * Compare two floating point registers
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_compare.c,v 1.2 1994/04/29 21:30:16 gclarkii Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | compare() is the core FPU_REG comparison function |
+ +---------------------------------------------------------------------------*/
+#include "param.h"
+#include "proc.h"
+#include "systm.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+#include "control_w.h"
+#include "status_w.h"
+
+
+int
+compare(FPU_REG * b)
+{
+ int diff;
+
+ if (FPU_st0_ptr->tag | b->tag) {
+ if (FPU_st0_ptr->tag == TW_Zero) {
+ if (b->tag == TW_Zero)
+ return COMP_A_eq_B;
+ if (b->tag == TW_Valid) {
+#ifdef DENORM_OPERAND
+ if ((b->exp <= EXP_UNDER) && (denormal_operand()))
+ return COMP_Denormal;
+#endif /* DENORM_OPERAND */
+ return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B;
+ }
+ } else
+ if (b->tag == TW_Zero) {
+ if (FPU_st0_ptr->tag == TW_Valid) {
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return COMP_Denormal;
+#endif /* DENORM_OPERAND */
+ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
+ }
+ }
+ if (FPU_st0_ptr->tag == TW_Infinity) {
+ if ((b->tag == TW_Valid) || (b->tag == TW_Zero)) {
+#ifdef DENORM_OPERAND
+ if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)
+ && (denormal_operand()))
+ return COMP_Denormal;
+#endif /* DENORM_OPERAND */
+ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
+ } else
+ if (b->tag == TW_Infinity) {
+ /* The 80486 book says that infinities
+ * can be equal! */
+ return (FPU_st0_ptr->sign == b->sign) ? COMP_A_eq_B :
+ ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
+ }
+ /* Fall through to the NaN code */
+ } else
+ if (b->tag == TW_Infinity) {
+ if ((FPU_st0_ptr->tag == TW_Valid) || (FPU_st0_ptr->tag == TW_Zero)) {
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->tag == TW_Valid)
+ && (FPU_st0_ptr->exp <= EXP_UNDER)
+ && (denormal_operand()))
+ return COMP_Denormal;
+#endif /* DENORM_OPERAND */
+ return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B;
+ }
+ /* Fall through to the NaN code */
+ }
+ /* The only possibility now should be that one of the
+ * arguments is a NaN */
+ if ((FPU_st0_ptr->tag == TW_NaN) || (b->tag == TW_NaN)) {
+ if (((FPU_st0_ptr->tag == TW_NaN) && !(FPU_st0_ptr->sigh & 0x40000000))
+ || ((b->tag == TW_NaN) && !(b->sigh & 0x40000000)))
+ /* At least one arg is a signaling NaN */
+ return COMP_No_Comp | COMP_SNaN | COMP_NaN;
+ else
+ /* Neither is a signaling NaN */
+ return COMP_No_Comp | COMP_NaN;
+ }
+ EXCEPTION(EX_Invalid);
+ }
+#ifdef PARANOID
+ if (!(FPU_st0_ptr->sigh & 0x80000000))
+ EXCEPTION(EX_Invalid);
+ if (!(b->sigh & 0x80000000))
+ EXCEPTION(EX_Invalid);
+#endif /* PARANOID */
+
+#ifdef DENORM_OPERAND
+ if (((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (b->exp <= EXP_UNDER)) && (denormal_operand()))
+ return COMP_Denormal;
+#endif /* DENORM_OPERAND */
+
+ if (FPU_st0_ptr->sign != b->sign)
+ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
+
+ diff = FPU_st0_ptr->exp - b->exp;
+ if (diff == 0) {
+ diff = FPU_st0_ptr->sigh - b->sigh; /* Works only if ms bits
+ * are identical */
+ if (diff == 0) {
+ diff = FPU_st0_ptr->sigl > b->sigl;
+ if (diff == 0)
+ diff = -(FPU_st0_ptr->sigl < b->sigl);
+ }
+ }
+ if (diff > 0)
+ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
+ if (diff < 0)
+ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B;
+ return COMP_A_eq_B;
+
+}
+
+
+/* This function requires that st(0) is not empty */
+int
+compare_st_data(void)
+{
+ int f, c;
+
+ c = compare(&FPU_loaded_data);
+
+ if (c & (COMP_NaN | COMP_Denormal)) {
+ if (c & COMP_NaN) {
+ EXCEPTION(EX_Invalid);
+ f = SW_C3 | SW_C2 | SW_C0;
+ } else {
+ /* One of the operands is a de-normal */
+ return 0;
+ }
+ } else
+ switch (c) {
+ case COMP_A_lt_B:
+ f = SW_C0;
+ break;
+ case COMP_A_eq_B:
+ f = SW_C3;
+ break;
+ case COMP_A_gt_B:
+ f = 0;
+ break;
+ case COMP_No_Comp:
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#ifdef PARANOID
+ default:
+ EXCEPTION(EX_INTERNAL | 0x121);
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#endif /* PARANOID */
+ }
+ setcc(f);
+ return 1;
+}
+
+
+static int
+compare_st_st(int nr)
+{
+ int f, c;
+
+ if (!NOT_EMPTY_0 || !NOT_EMPTY(nr)) {
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ /* Stack fault */
+ EXCEPTION(EX_StackUnder);
+ return control_word & CW_Invalid;
+ }
+ c = compare(&st(nr));
+ if (c & (COMP_NaN | COMP_Denormal)) {
+ if (c & COMP_NaN) {
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ EXCEPTION(EX_Invalid);
+ return control_word & CW_Invalid;
+ } else {
+ /* One of the operands is a de-normal */
+ return control_word & CW_Denormal;
+ }
+ } else
+ switch (c) {
+ case COMP_A_lt_B:
+ f = SW_C0;
+ break;
+ case COMP_A_eq_B:
+ f = SW_C3;
+ break;
+ case COMP_A_gt_B:
+ f = 0;
+ break;
+ case COMP_No_Comp:
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#ifdef PARANOID
+ default:
+ EXCEPTION(EX_INTERNAL | 0x122);
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#endif /* PARANOID */
+ }
+ setcc(f);
+ return 1;
+}
+
+
+static int
+compare_u_st_st(int nr)
+{
+ int f, c;
+
+ if (!NOT_EMPTY_0 || !NOT_EMPTY(nr)) {
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ /* Stack fault */
+ EXCEPTION(EX_StackUnder);
+ return control_word & CW_Invalid;
+ }
+ c = compare(&st(nr));
+ if (c & (COMP_NaN | COMP_Denormal)) {
+ if (c & COMP_NaN) {
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ if (c & COMP_SNaN) { /* This is the only difference
+ * between un-ordered and
+ * ordinary comparisons */
+ EXCEPTION(EX_Invalid);
+ return control_word & CW_Invalid;
+ }
+ return 1;
+ } else {
+ /* One of the operands is a de-normal */
+ return control_word & CW_Denormal;
+ }
+ } else
+ switch (c) {
+ case COMP_A_lt_B:
+ f = SW_C0;
+ break;
+ case COMP_A_eq_B:
+ f = SW_C3;
+ break;
+ case COMP_A_gt_B:
+ f = 0;
+ break;
+ case COMP_No_Comp:
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#ifdef PARANOID
+ default:
+ EXCEPTION(EX_INTERNAL | 0x123);
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#endif /* PARANOID */
+ }
+ setcc(f);
+ return 1;
+}
+/*---------------------------------------------------------------------------*/
+
+void
+fcom_st()
+{
+ /* fcom st(i) */
+ compare_st_st(FPU_rm);
+}
+
+
+void
+fcompst()
+{
+ /* fcomp st(i) */
+ if (compare_st_st(FPU_rm))
+ pop();
+}
+
+
+void
+fcompp()
+{
+ /* fcompp */
+ if (FPU_rm != 1)
+ return Un_impl();
+ if (compare_st_st(1)) {
+ pop();
+ FPU_st0_ptr = &st(0);
+ pop();
+ }
+}
+
+
+void
+fucom_()
+{
+ /* fucom st(i) */
+ compare_u_st_st(FPU_rm);
+
+}
+
+
+void
+fucomp()
+{
+ /* fucomp st(i) */
+ if (compare_u_st_st(FPU_rm))
+ pop();
+}
+
+
+void
+fucompp()
+{
+ /* fucompp */
+ if (FPU_rm == 1) {
+ if (compare_u_st_st(1)) {
+ pop();
+ FPU_st0_ptr = &st(0);
+ pop();
+ }
+ } else
+ Un_impl();
+}
diff --git a/sys/gnu/i386/fpemul/reg_constant.c b/sys/gnu/i386/fpemul/reg_constant.c
new file mode 100644
index 0000000..f334273
--- /dev/null
+++ b/sys/gnu/i386/fpemul/reg_constant.c
@@ -0,0 +1,175 @@
+/*
+ * reg_constant.c
+ *
+ * All of the constant FPU_REGs
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $id:$
+ *
+ */
+
+
+
+#include "param.h"
+#include "proc.h"
+#include "systm.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "status_w.h"
+#include "reg_constant.h"
+
+
+FPU_REG CONST_1 = {SIGN_POS, TW_Valid, EXP_BIAS,
+0x00000000, 0x80000000};
+FPU_REG CONST_2 = {SIGN_POS, TW_Valid, EXP_BIAS + 1,
+0x00000000, 0x80000000};
+FPU_REG CONST_HALF = {SIGN_POS, TW_Valid, EXP_BIAS - 1,
+0x00000000, 0x80000000};
+FPU_REG CONST_L2T = {SIGN_POS, TW_Valid, EXP_BIAS + 1,
+0xcd1b8afe, 0xd49a784b};
+FPU_REG CONST_L2E = {SIGN_POS, TW_Valid, EXP_BIAS,
+0x5c17f0bc, 0xb8aa3b29};
+FPU_REG CONST_PI = {SIGN_POS, TW_Valid, EXP_BIAS + 1,
+0x2168c235, 0xc90fdaa2};
+FPU_REG CONST_PI2 = {SIGN_POS, TW_Valid, EXP_BIAS,
+0x2168c235, 0xc90fdaa2};
+FPU_REG CONST_PI4 = {SIGN_POS, TW_Valid, EXP_BIAS - 1,
+0x2168c235, 0xc90fdaa2};
+FPU_REG CONST_LG2 = {SIGN_POS, TW_Valid, EXP_BIAS - 2,
+0xfbcff799, 0x9a209a84};
+FPU_REG CONST_LN2 = {SIGN_POS, TW_Valid, EXP_BIAS - 1,
+0xd1cf79ac, 0xb17217f7};
+/* Only the sign (and tag) is used in internal zeroes */
+FPU_REG CONST_Z = {SIGN_POS, TW_Zero, 0, 0x0, 0x0};
+/* Only the sign and significand (and tag) are used in internal NaNs */
+/* The 80486 never generates one of these
+FPU_REG CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 };
+ */
+/* This is the real indefinite QNaN */
+FPU_REG CONST_QNaN = {SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000};
+/* Only the sign (and tag) is used in internal infinities */
+FPU_REG CONST_INF = {SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000};
+
+
+
+static void
+fld_const(FPU_REG * c)
+{
+ FPU_REG *st_new_ptr;
+
+ if (STACK_OVERFLOW) {
+ stack_overflow();
+ return;
+ }
+ push();
+ reg_move(c, FPU_st0_ptr);
+ status_word &= ~SW_C1;
+}
+
+
+static void
+fld1(void)
+{
+ fld_const(&CONST_1);
+}
+
+static void
+fldl2t(void)
+{
+ fld_const(&CONST_L2T);
+}
+
+static void
+fldl2e(void)
+{
+ fld_const(&CONST_L2E);
+}
+
+static void
+fldpi(void)
+{
+ fld_const(&CONST_PI);
+}
+
+static void
+fldlg2(void)
+{
+ fld_const(&CONST_LG2);
+}
+
+static void
+fldln2(void)
+{
+ fld_const(&CONST_LN2);
+}
+
+static void
+fldz(void)
+{
+ fld_const(&CONST_Z);
+}
+
+static FUNC constants_table[] = {
+ fld1, fldl2t, fldl2e, fldpi, fldlg2, fldln2, fldz, Un_impl
+};
+
+void
+fconst(void)
+{
+ (constants_table[FPU_rm]) ();
+}
diff --git a/sys/gnu/i386/fpemul/reg_constant.h b/sys/gnu/i386/fpemul/reg_constant.h
new file mode 100644
index 0000000..6e4ffa8
--- /dev/null
+++ b/sys/gnu/i386/fpemul/reg_constant.h
@@ -0,0 +1,82 @@
+/*
+ * reg_constant.h
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_constant.h,v 1.2 1994/04/29 21:30:18 gclarkii Exp $
+ *
+ */
+
+#ifndef _REG_CONSTANT_H_
+#define _REG_CONSTANT_H_
+
+#include "fpu_emu.h"
+
+extern FPU_REG CONST_1;
+extern FPU_REG CONST_2;
+extern FPU_REG CONST_HALF;
+extern FPU_REG CONST_L2T;
+extern FPU_REG CONST_L2E;
+extern FPU_REG CONST_PI;
+extern FPU_REG CONST_PI2;
+extern FPU_REG CONST_PI4;
+extern FPU_REG CONST_LG2;
+extern FPU_REG CONST_LN2;
+extern FPU_REG CONST_Z;
+extern FPU_REG CONST_PINF;
+extern FPU_REG CONST_INF;
+extern FPU_REG CONST_MINF;
+extern FPU_REG CONST_QNaN;
+
+#endif /* _REG_CONSTANT_H_ */
diff --git a/sys/gnu/i386/fpemul/reg_div.s b/sys/gnu/i386/fpemul/reg_div.s
new file mode 100644
index 0000000..5de5a46
--- /dev/null
+++ b/sys/gnu/i386/fpemul/reg_div.s
@@ -0,0 +1,295 @@
+ .file "reg_div.S"
+/*
+ * reg_div.S
+ *
+ * Divide one FPU_REG by another and put the result in a destination FPU_REG.
+ *
+ * Call from C as:
+ * void reg_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest,
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_div.s,v 1.2 1994/04/29 21:30:19 gclarkii Exp $
+ *
+ */
+
+#include "exception.h"
+#include "fpu_asm.h"
+#include "control_w.h"
+
+.text
+ .align 2
+
+.globl _reg_div
+_reg_div:
+ pushl %ebp
+ movl %esp,%ebp
+
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi
+ movl PARAM2,%ebx
+ movl PARAM3,%edi
+
+ movb TAG(%esi),%al
+ orb TAG(%ebx),%al
+
+ jne L_div_special /* Not (both numbers TW_Valid) */
+
+#ifdef DENORM_OPERAND
+/* Check for denormals */
+ cmpl EXP_UNDER,EXP(%esi)
+ jg xL_arg1_not_denormal
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xL_arg1_not_denormal:
+ cmpl EXP_UNDER,EXP(%ebx)
+ jg xL_arg2_not_denormal
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xL_arg2_not_denormal:
+#endif DENORM_OPERAND
+
+/* Both arguments are TW_Valid */
+ movb TW_Valid,TAG(%edi)
+
+ movb SIGN(%esi),%cl
+ cmpb %cl,SIGN(%ebx)
+ setne (%edi) /* Set the sign, requires SIGN_NEG=1, SIGN_POS=0 */
+
+ movl EXP(%esi),%edx
+ movl EXP(%ebx),%eax
+ subl %eax,%edx
+ addl EXP_BIAS,%edx
+ movl %edx,EXP(%edi)
+
+ jmp _divide_kernel
+
+
+/*-----------------------------------------------------------------------*/
+L_div_special:
+ cmpb TW_NaN,TAG(%esi) /* A NaN with anything to give NaN */
+ je L_arg1_NaN
+
+ cmpb TW_NaN,TAG(%ebx) /* A NaN with anything to give NaN */
+ jne L_no_NaN_arg
+
+/* Operations on NaNs */
+L_arg1_NaN:
+L_arg2_NaN:
+ pushl %edi /* Destination */
+ pushl %ebx
+ pushl %esi
+ call _real_2op_NaN
+ jmp LDiv_exit
+
+/* Invalid operations */
+L_zero_zero:
+L_inf_inf:
+ pushl %edi /* Destination */
+ call _arith_invalid /* 0/0 or Infinity/Infinity */
+ jmp LDiv_exit
+
+L_no_NaN_arg:
+ cmpb TW_Infinity,TAG(%esi)
+ jne L_arg1_not_inf
+
+ cmpb TW_Infinity,TAG(%ebx)
+ je L_inf_inf /* invalid operation */
+
+ cmpb TW_Valid,TAG(%ebx)
+ je L_inf_valid
+
+#ifdef PARANOID
+ /* arg2 must be zero or valid */
+ cmpb TW_Zero,TAG(%ebx)
+ ja L_unknown_tags
+#endif PARANOID
+
+ /* Note that p16-9 says that infinity/0 returns infinity */
+ jmp L_copy_arg1 /* Answer is Inf */
+
+L_inf_valid:
+#ifdef DENORM_OPERAND
+ cmpl EXP_UNDER,EXP(%ebx)
+ jg L_copy_arg1 /* Answer is Inf */
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+#endif DENORM_OPERAND
+
+ jmp L_copy_arg1 /* Answer is Inf */
+
+L_arg1_not_inf:
+ cmpb TW_Zero,TAG(%ebx) /* Priority to div-by-zero error */
+ jne L_arg2_not_zero
+
+ cmpb TW_Zero,TAG(%esi)
+ je L_zero_zero /* invalid operation */
+
+#ifdef PARANOID
+ /* arg1 must be valid */
+ cmpb TW_Valid,TAG(%esi)
+ ja L_unknown_tags
+#endif PARANOID
+
+/* Division by zero error */
+ pushl %edi /* destination */
+ movb SIGN(%esi),%al
+ xorb SIGN(%ebx),%al
+ pushl %eax /* lower 8 bits have the sign */
+ call _divide_by_zero
+ jmp LDiv_exit
+
+L_arg2_not_zero:
+ cmpb TW_Infinity,TAG(%ebx)
+ jne L_arg2_not_inf
+
+#ifdef DENORM_OPERAND
+ cmpb TW_Valid,TAG(%esi)
+ jne L_return_zero
+
+ cmpl EXP_UNDER,EXP(%esi)
+ jg L_return_zero /* Answer is zero */
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+#endif DENORM_OPERAND
+
+ jmp L_return_zero /* Answer is zero */
+
+L_arg2_not_inf:
+
+#ifdef PARANOID
+ cmpb TW_Zero,TAG(%esi)
+ jne L_unknown_tags
+#endif PARANOID
+
+ /* arg1 is zero, arg2 is not Infinity or a NaN */
+
+#ifdef DENORM_OPERAND
+ cmpl EXP_UNDER,EXP(%ebx)
+ jg L_copy_arg1 /* Answer is zero */
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+#endif DENORM_OPERAND
+
+L_copy_arg1:
+ movb TAG(%esi),%ax
+ movb %ax,TAG(%edi)
+ movl EXP(%esi),%eax
+ movl %eax,EXP(%edi)
+ movl SIGL(%esi),%eax
+ movl %eax,SIGL(%edi)
+ movl SIGH(%esi),%eax
+ movl %eax,SIGH(%edi)
+
+ movb SIGN(%esi),%cl
+ cmpb %cl,SIGN(%ebx)
+ jne LDiv_negative_result
+
+ movb SIGN_POS,SIGN(%edi)
+ jmp LDiv_exit
+
+LDiv_set_result_sign:
+ movb SIGN(%esi),%cl
+ cmpb %cl,SIGN(%edi)
+ jne LDiv_negative_result
+
+ movb SIGN_POS,SIGN(%ebx)
+ jmp LDiv_exit
+
+LDiv_negative_result:
+ movb SIGN_NEG,SIGN(%edi)
+
+LDiv_exit:
+ leal -12(%ebp),%esp
+
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
+
+
+L_return_zero:
+ movb TW_Zero,TAG(%edi)
+ jmp LDiv_set_result_sign
+
+#ifdef PARANOID
+L_unknown_tags:
+ push EX_INTERNAL | 0x208
+ call EXCEPTION
+
+ /* Generate a NaN for unknown tags */
+ movl _CONST_QNaN,%eax
+ movl %eax,(%edi)
+ movl _CONST_QNaN+4,%eax
+ movl %eax,SIGL(%edi)
+ movl _CONST_QNaN+8,%eax
+ movl %eax,SIGH(%edi)
+ jmp LDiv_exit
+#endif PARANOID
diff --git a/sys/gnu/i386/fpemul/reg_ld_str.c b/sys/gnu/i386/fpemul/reg_ld_str.c
new file mode 100644
index 0000000..4edaa9d
--- /dev/null
+++ b/sys/gnu/i386/fpemul/reg_ld_str.c
@@ -0,0 +1,1387 @@
+/*
+ * reg_ld_str.c
+ *
+ * All of the functions which transfer data between user memory and FPU_REGs.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_ld_str.c,v 1.2 1994/04/29 21:30:20 gclarkii Exp $
+ *
+ */
+
+
+/*---------------------------------------------------------------------------+
+ | Note: |
+ | The file contains code which accesses user memory. |
+ | Emulator static data may change when user memory is accessed, due to |
+ | other processes using the emulator while swapping is in progress. |
+ +---------------------------------------------------------------------------*/
+#include "param.h"
+#include "proc.h"
+#include "systm.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+#include "reg_constant.h"
+#include "control_w.h"
+#include "status_w.h"
+
+
+#define EXTENDED_Emax 0x3fff /* largest valid exponent */
+#define EXTENDED_Ebias 0x3fff
+#define EXTENDED_Emin (-0x3ffe) /* smallest valid exponent */
+
+#define DOUBLE_Emax 1023 /* largest valid exponent */
+#define DOUBLE_Ebias 1023
+#define DOUBLE_Emin (-1022) /* smallest valid exponent */
+
+#define SINGLE_Emax 127 /* largest valid exponent */
+#define SINGLE_Ebias 127
+#define SINGLE_Emin (-126) /* smallest valid exponent */
+
+#define LOST_UP (EX_Precision | SW_C1)
+#define LOST_DOWN EX_Precision
+
+FPU_REG FPU_loaded_data;
+
+
+/* Get a long double from user memory */
+void
+reg_load_extended(void)
+{
+ long double *s = (long double *) FPU_data_address;
+ unsigned long sigl, sigh, exp;
+
+ REENTRANT_CHECK(OFF);
+ /* Use temporary variables here because FPU_loaded data is static and
+ * hence re-entrancy problems can arise */
+ sigl = fuword((unsigned long *) s);
+ sigh = fuword(1 + (unsigned long *) s);
+ exp = fuword(4 + (unsigned short *) s);
+ REENTRANT_CHECK(ON);
+
+ FPU_loaded_data.sigl = sigl;
+ FPU_loaded_data.sigh = sigh;
+ FPU_loaded_data.exp = exp;
+
+ if (FPU_loaded_data.exp & 0x8000)
+ FPU_loaded_data.sign = SIGN_NEG;
+ else
+ FPU_loaded_data.sign = SIGN_POS;
+ if ((FPU_loaded_data.exp &= 0x7fff) == 0) {
+ if (!(FPU_loaded_data.sigl | FPU_loaded_data.sigh)) {
+ FPU_loaded_data.tag = TW_Zero;
+ return;
+ }
+ /* The number is a de-normal or pseudodenormal. */
+ /* The 80486 doesn't regard pseudodenormals as denormals here. */
+ if (!(FPU_loaded_data.sigh & 0x80000000))
+ EXCEPTION(EX_Denormal);
+ FPU_loaded_data.exp++;
+
+ /* The default behaviour will now take care of it. */
+ } else
+ if (FPU_loaded_data.exp == 0x7fff) {
+ FPU_loaded_data.exp = EXTENDED_Emax;
+ if ((FPU_loaded_data.sigh == 0x80000000)
+ && (FPU_loaded_data.sigl == 0)) {
+ FPU_loaded_data.tag = TW_Infinity;
+ return;
+ } else
+ if (!(FPU_loaded_data.sigh & 0x80000000)) {
+ /* Unsupported NaN data type */
+ EXCEPTION(EX_Invalid);
+ FPU_loaded_data.tag = TW_NaN;
+ return;
+ }
+ FPU_loaded_data.tag = TW_NaN;
+ return;
+ }
+ FPU_loaded_data.exp = (FPU_loaded_data.exp & 0x7fff) - EXTENDED_Ebias
+ + EXP_BIAS;
+ FPU_loaded_data.tag = TW_Valid;
+
+ if (!(sigh & 0x80000000)) {
+ /* Unsupported data type */
+ EXCEPTION(EX_Invalid);
+ normalize_nuo(&FPU_loaded_data);
+ }
+}
+
+
+/* Get a double from user memory */
+void
+reg_load_double(void)
+{
+ double *dfloat = (double *) FPU_data_address;
+ int exp;
+ unsigned m64, l64;
+
+ REENTRANT_CHECK(OFF);
+ m64 = fuword(1 + (unsigned long *) dfloat);
+ l64 = fuword((unsigned long *) dfloat);
+ REENTRANT_CHECK(ON);
+
+ if (m64 & 0x80000000)
+ FPU_loaded_data.sign = SIGN_NEG;
+ else
+ FPU_loaded_data.sign = SIGN_POS;
+ exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias;
+ m64 &= 0xfffff;
+ if (exp > DOUBLE_Emax) {
+ /* Infinity or NaN */
+ if ((m64 == 0) && (l64 == 0)) {
+ /* +- infinity */
+ FPU_loaded_data.exp = EXTENDED_Emax;
+ FPU_loaded_data.tag = TW_Infinity;
+ return;
+ } else {
+ /* Must be a signaling or quiet NaN */
+ FPU_loaded_data.exp = EXTENDED_Emax;
+ FPU_loaded_data.tag = TW_NaN;
+ FPU_loaded_data.sigh = (m64 << 11) | 0x80000000;
+ FPU_loaded_data.sigh |= l64 >> 21;
+ FPU_loaded_data.sigl = l64 << 11;
+ return;
+ }
+ } else
+ if (exp < DOUBLE_Emin) {
+ /* Zero or de-normal */
+ if ((m64 == 0) && (l64 == 0)) {
+ /* Zero */
+ int c = FPU_loaded_data.sign;
+ reg_move(&CONST_Z, &FPU_loaded_data);
+ FPU_loaded_data.sign = c;
+ return;
+ } else {
+ /* De-normal */
+ EXCEPTION(EX_Denormal);
+ FPU_loaded_data.exp = DOUBLE_Emin + EXP_BIAS;
+ FPU_loaded_data.tag = TW_Valid;
+ FPU_loaded_data.sigh = m64 << 11;
+ FPU_loaded_data.sigh |= l64 >> 21;
+ FPU_loaded_data.sigl = l64 << 11;
+ normalize_nuo(&FPU_loaded_data);
+ return;
+ }
+ } else {
+ FPU_loaded_data.exp = exp + EXP_BIAS;
+ FPU_loaded_data.tag = TW_Valid;
+ FPU_loaded_data.sigh = (m64 << 11) | 0x80000000;
+ FPU_loaded_data.sigh |= l64 >> 21;
+ FPU_loaded_data.sigl = l64 << 11;
+
+ return;
+ }
+}
+
+
+/* Get a float from user memory */
+void
+reg_load_single(void)
+{
+ float *single = (float *) FPU_data_address;
+ unsigned m32;
+ int exp;
+
+ REENTRANT_CHECK(OFF);
+ m32 = fuword((unsigned long *) single);
+ REENTRANT_CHECK(ON);
+
+ if (m32 & 0x80000000)
+ FPU_loaded_data.sign = SIGN_NEG;
+ else
+ FPU_loaded_data.sign = SIGN_POS;
+ if (!(m32 & 0x7fffffff)) {
+ /* Zero */
+ int c = FPU_loaded_data.sign;
+ reg_move(&CONST_Z, &FPU_loaded_data);
+ FPU_loaded_data.sign = c;
+ return;
+ }
+ exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias;
+ m32 = (m32 & 0x7fffff) << 8;
+ if (exp < SINGLE_Emin) {
+ /* De-normals */
+ EXCEPTION(EX_Denormal);
+ FPU_loaded_data.exp = SINGLE_Emin + EXP_BIAS;
+ FPU_loaded_data.tag = TW_Valid;
+ FPU_loaded_data.sigh = m32;
+ FPU_loaded_data.sigl = 0;
+ normalize_nuo(&FPU_loaded_data);
+ return;
+ } else
+ if (exp > SINGLE_Emax) {
+ /* Infinity or NaN */
+ if (m32 == 0) {
+ /* +- infinity */
+ FPU_loaded_data.exp = EXTENDED_Emax;
+ FPU_loaded_data.tag = TW_Infinity;
+ return;
+ } else {
+ /* Must be a signaling or quiet NaN */
+ FPU_loaded_data.exp = EXTENDED_Emax;
+ FPU_loaded_data.tag = TW_NaN;
+ FPU_loaded_data.sigh = m32 | 0x80000000;
+ FPU_loaded_data.sigl = 0;
+ return;
+ }
+ } else {
+ FPU_loaded_data.exp = exp + EXP_BIAS;
+ FPU_loaded_data.sigh = m32 | 0x80000000;
+ FPU_loaded_data.sigl = 0;
+ FPU_loaded_data.tag = TW_Valid;
+ }
+}
+
+
+/* Get a long long from user memory */
+void
+reg_load_int64(void)
+{
+ long long *_s = (long long *) FPU_data_address;
+ int e;
+ long long s;
+
+ REENTRANT_CHECK(OFF);
+ ((unsigned long *) &s)[0] = fuword((unsigned long *) _s);
+ ((unsigned long *) &s)[1] = fuword(1 + (unsigned long *) _s);
+ REENTRANT_CHECK(ON);
+
+ if (s == 0) {
+ reg_move(&CONST_Z, &FPU_loaded_data);
+ return;
+ }
+ if (s > 0)
+ FPU_loaded_data.sign = SIGN_POS;
+ else {
+ s = -s;
+ FPU_loaded_data.sign = SIGN_NEG;
+ }
+
+ e = EXP_BIAS + 63;
+ *((long long *) &FPU_loaded_data.sigl) = s;
+ FPU_loaded_data.exp = e;
+ FPU_loaded_data.tag = TW_Valid;
+ normalize_nuo(&FPU_loaded_data);
+}
+
+
+/* Get a long from user memory */
+void
+reg_load_int32(void)
+{
+ long *_s = (long *) FPU_data_address;
+ long s;
+ int e;
+
+ REENTRANT_CHECK(OFF);
+ s = (long) fuword((unsigned long *) _s);
+ REENTRANT_CHECK(ON);
+
+ if (s == 0) {
+ reg_move(&CONST_Z, &FPU_loaded_data);
+ return;
+ }
+ if (s > 0)
+ FPU_loaded_data.sign = SIGN_POS;
+ else {
+ s = -s;
+ FPU_loaded_data.sign = SIGN_NEG;
+ }
+
+ e = EXP_BIAS + 31;
+ FPU_loaded_data.sigh = s;
+ FPU_loaded_data.sigl = 0;
+ FPU_loaded_data.exp = e;
+ FPU_loaded_data.tag = TW_Valid;
+ normalize_nuo(&FPU_loaded_data);
+}
+
+
+/* Get a short from user memory */
+void
+reg_load_int16(void)
+{
+ short *_s = (short *) FPU_data_address;
+ int s, e;
+
+ REENTRANT_CHECK(OFF);
+ /* Cast as short to get the sign extended. */
+ s = (short) fuword((unsigned short *) _s);
+ REENTRANT_CHECK(ON);
+
+ if (s == 0) {
+ reg_move(&CONST_Z, &FPU_loaded_data);
+ return;
+ }
+ if (s > 0)
+ FPU_loaded_data.sign = SIGN_POS;
+ else {
+ s = -s;
+ FPU_loaded_data.sign = SIGN_NEG;
+ }
+
+ e = EXP_BIAS + 15;
+ FPU_loaded_data.sigh = s << 16;
+
+ FPU_loaded_data.sigl = 0;
+ FPU_loaded_data.exp = e;
+ FPU_loaded_data.tag = TW_Valid;
+ normalize_nuo(&FPU_loaded_data);
+}
+
+
+/* Get a packed bcd array from user memory */
+void
+reg_load_bcd(void)
+{
+ char *s = (char *) FPU_data_address;
+ int pos;
+ unsigned char bcd;
+ long long l = 0;
+
+ for (pos = 8; pos >= 0; pos--) {
+ l *= 10;
+ REENTRANT_CHECK(OFF);
+ bcd = (unsigned char) fubyte((unsigned char *) s + pos);
+ REENTRANT_CHECK(ON);
+ l += bcd >> 4;
+ l *= 10;
+ l += bcd & 0x0f;
+ }
+
+ /* Finish all access to user memory before putting stuff into the
+ * static FPU_loaded_data */
+ REENTRANT_CHECK(OFF);
+ FPU_loaded_data.sign =
+ ((unsigned char) fubyte((unsigned char *) s + 9)) & 0x80 ?
+ SIGN_NEG : SIGN_POS;
+ REENTRANT_CHECK(ON);
+
+ if (l == 0) {
+ char sign = FPU_loaded_data.sign;
+ reg_move(&CONST_Z, &FPU_loaded_data);
+ FPU_loaded_data.sign = sign;
+ } else {
+ *((long long *) &FPU_loaded_data.sigl) = l;
+ FPU_loaded_data.exp = EXP_BIAS + 63;
+ FPU_loaded_data.tag = TW_Valid;
+ normalize_nuo(&FPU_loaded_data);
+ }
+}
+/*===========================================================================*/
+
+/* Put a long double into user memory */
+int
+reg_store_extended(void)
+{
+ long double *d = (long double *) FPU_data_address;
+ long e = FPU_st0_ptr->exp - EXP_BIAS + EXTENDED_Ebias;
+ unsigned short sign = FPU_st0_ptr->sign * 0x8000;
+ unsigned long ls, ms;
+
+
+ if (FPU_st0_tag == TW_Valid) {
+ if (e >= 0x7fff) {
+ EXCEPTION(EX_Overflow); /* Overflow */
+ /* This is a special case: see sec 16.2.5.1 of the
+ * 80486 book */
+ if (control_word & EX_Overflow) {
+ /* Overflow to infinity */
+ ls = 0;
+ ms = 0x80000000;
+ e = 0x7fff;
+ } else
+ return 0;
+ } else
+ if (e <= 0) {
+ if (e > -63) {
+ /* Correctly format the de-normal */
+ int precision_loss;
+ FPU_REG tmp;
+
+ EXCEPTION(EX_Denormal);
+ reg_move(FPU_st0_ptr, &tmp);
+ tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 62 */
+ if ((precision_loss = round_to_int(&tmp))) {
+ EXCEPTION(EX_Underflow | precision_loss);
+ /* This is a special case: see
+ * sec 16.2.5.1 of the 80486
+ * book */
+ if (!(control_word & EX_Underflow))
+ return 0;
+ }
+ e = 0;
+ ls = tmp.sigl;
+ ms = tmp.sigh;
+ } else {
+ /* ****** ??? This should not be
+ * possible */
+ EXCEPTION(EX_Underflow); /* Underflow */
+ /* This is a special case: see sec
+ * 16.2.5.1 of the 80486 book */
+ if (control_word & EX_Underflow) {
+ /* Underflow to zero */
+ ls = 0;
+ ms = 0;
+ e = FPU_st0_ptr->sign == SIGN_POS ? 0x7fff : 0xffff;
+ } else
+ return 0;
+ }
+ } else {
+ ls = FPU_st0_ptr->sigl;
+ ms = FPU_st0_ptr->sigh;
+ }
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ ls = ms = 0;
+ e = 0;
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ ls = 0;
+ ms = 0x80000000;
+ e = 0x7fff;
+ } else
+ if (FPU_st0_tag == TW_NaN) {
+ ls = FPU_st0_ptr->sigl;
+ ms = FPU_st0_ptr->sigh;
+ e = 0x7fff;
+ } else
+ if (FPU_st0_tag == TW_Empty) {
+ /* Empty register (stack
+ * underflow) */
+ EXCEPTION(EX_StackUnder);
+ if (control_word & EX_Invalid) {
+ /* The masked response */
+ /* Put out the QNaN
+ * indefinite */
+ ls = 0;
+ ms = 0xc0000000;
+ e = 0xffff;
+ } else
+ return 0;
+ } else {
+ /* We don't use TW_Denormal
+ * yet ... perhaps never! */
+ EXCEPTION(EX_Invalid);
+ /* Store a NaN */
+ e = 0x7fff;
+ ls = 1;
+ ms = 0x80000000;
+ }
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, d, 10); */
+ suword((unsigned long *) d, ls);
+ suword(1 + (unsigned long *) d, ms);
+ suword(4 + (short *) d, (unsigned short) e | sign);
+ REENTRANT_CHECK(ON);
+
+ return 1;
+
+}
+
+
+/* Put a double into user memory */
+int
+reg_store_double(void)
+{
+ double *dfloat = (double *) FPU_data_address;
+ unsigned long l[2];
+ if (FPU_st0_tag == TW_Valid) {
+ int exp;
+ FPU_REG tmp;
+
+ reg_move(FPU_st0_ptr, &tmp);
+ exp = tmp.exp - EXP_BIAS;
+
+ if (exp < DOUBLE_Emin) { /* It may be a denormal */
+ /* Make a de-normal */
+ int precision_loss;
+
+ if (exp <= -EXTENDED_Ebias)
+ EXCEPTION(EX_Denormal);
+
+ tmp.exp += -DOUBLE_Emin + 52; /* largest exp to be 51 */
+
+ if ((precision_loss = round_to_int(&tmp))) {
+#ifdef PECULIAR_486
+ /* Did it round to a non-denormal ? */
+ /* This behaviour might be regarded as
+ * peculiar, it appears that the 80486 rounds
+ * to the dest precision, then converts to
+ * decide underflow. */
+ if ((tmp.sigh == 0x00100000) && (tmp.sigl == 0) &&
+ (FPU_st0_ptr->sigl & 0x000007ff))
+ EXCEPTION(precision_loss);
+ else
+#endif /* PECULIAR_486 */
+ {
+ EXCEPTION(EX_Underflow | precision_loss);
+ /* This is a special case: see sec
+ * 16.2.5.1 of the 80486 book */
+ if (!(control_word & EX_Underflow))
+ return 0;
+ }
+ }
+ l[0] = tmp.sigl;
+ l[1] = tmp.sigh;
+ } else {
+ if (tmp.sigl & 0x000007ff) {
+ unsigned long increment = 0; /* avoid gcc warnings */
+
+ switch (control_word & CW_RC) {
+ case RC_RND:
+ /* Rounding can get a little messy.. */
+ increment = ((tmp.sigl & 0x7ff) > 0x400) | /* nearest */
+ ((tmp.sigl & 0xc00) == 0xc00); /* odd -> even */
+ break;
+ case RC_DOWN: /* towards -infinity */
+ increment = (tmp.sign == SIGN_POS) ? 0 : tmp.sigl & 0x7ff;
+ break;
+ case RC_UP: /* towards +infinity */
+ increment = (tmp.sign == SIGN_POS) ? tmp.sigl & 0x7ff : 0;
+ break;
+ case RC_CHOP:
+ increment = 0;
+ break;
+ }
+
+ /* Truncate the mantissa */
+ tmp.sigl &= 0xfffff800;
+
+ if (increment) {
+ set_precision_flag_up();
+
+ if (tmp.sigl >= 0xfffff800) {
+ /* the sigl part overflows */
+ if (tmp.sigh == 0xffffffff) {
+ /* The sigh part
+ * overflows */
+ tmp.sigh = 0x80000000;
+ exp++;
+ if (exp >= EXP_OVER)
+ goto overflow;
+ } else {
+ tmp.sigh++;
+ }
+ tmp.sigl = 0x00000000;
+ } else {
+ /* We only need to increment
+ * sigl */
+ tmp.sigl += 0x00000800;
+ }
+ } else
+ set_precision_flag_down();
+ }
+ l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
+ l[1] = ((tmp.sigh >> 11) & 0xfffff);
+
+ if (exp > DOUBLE_Emax) {
+ overflow:
+ EXCEPTION(EX_Overflow);
+ /* This is a special case: see sec 16.2.5.1 of
+ * the 80486 book */
+ if (control_word & EX_Overflow) {
+ /* Overflow to infinity */
+ l[0] = 0x00000000; /* Set to */
+ l[1] = 0x7ff00000; /* + INF */
+ } else
+ return 0;
+ } else {
+ /* Add the exponent */
+ l[1] |= (((exp + DOUBLE_Ebias) & 0x7ff) << 20);
+ }
+ }
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ /* Number is zero */
+ l[0] = 0;
+ l[1] = 0;
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ l[0] = 0;
+ l[1] = 0x7ff00000;
+ } else
+ if (FPU_st0_tag == TW_NaN) {
+ /* See if we can get a valid NaN from
+ * the FPU_REG */
+ l[0] = (FPU_st0_ptr->sigl >> 11) | (FPU_st0_ptr->sigh << 21);
+ l[1] = ((FPU_st0_ptr->sigh >> 11) & 0xfffff);
+ if (!(l[0] | l[1])) {
+ /* This case does not seem to
+ * be handled by the 80486
+ * specs */
+ EXCEPTION(EX_Invalid);
+ /* Make the quiet NaN "real
+ * indefinite" */
+ goto put_indefinite;
+ }
+ l[1] |= 0x7ff00000;
+ } else
+ if (FPU_st0_tag == TW_Empty) {
+ /* Empty register (stack
+ * underflow) */
+ EXCEPTION(EX_StackUnder);
+ if (control_word & EX_Invalid) {
+ /* The masked response */
+ /* Put out the QNaN
+ * indefinite */
+ put_indefinite:
+ REENTRANT_CHECK(OFF);
+ /* verify_area(VERIFY_W
+ * RITE, (void *)
+ * dfloat, 8); */
+ suword((unsigned long *) dfloat, 0);
+ suword(1 + (unsigned long *) dfloat, 0xfff80000);
+ REENTRANT_CHECK(ON);
+ return 1;
+ } else
+ return 0;
+ }
+#if 0 /* TW_Denormal is not used yet, and probably
+ * won't be */
+ else
+ if (FPU_st0_tag == TW_Denormal) {
+ /* Extended real ->
+ * double real will
+ * always underflow */
+ l[0] = l[1] = 0;
+ EXCEPTION(EX_Underflow);
+ }
+#endif
+ if (FPU_st0_ptr->sign)
+ l[1] |= 0x80000000;
+
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, (void *) dfloat, 8);*/
+ suword((u_long *) dfloat, l[0]);
+ suword((u_long *) dfloat + 1, l[1]);
+/*
+ suword(l[0], (unsigned long *) dfloat);
+ suword(l[1], 1 + (unsigned long *) dfloat);*/
+ REENTRANT_CHECK(ON);
+
+ return 1;
+}
+
+
+/* Put a float into user memory */
+int
+reg_store_single(void)
+{
+ float *single = (float *) FPU_data_address;
+ long templ;
+
+ if (FPU_st0_tag == TW_Valid) {
+ int exp;
+ FPU_REG tmp;
+
+ reg_move(FPU_st0_ptr, &tmp);
+ exp = tmp.exp - EXP_BIAS;
+
+ if (exp < SINGLE_Emin) {
+ /* Make a de-normal */
+ int precision_loss;
+
+ if (exp <= -EXTENDED_Ebias)
+ EXCEPTION(EX_Denormal);
+
+ tmp.exp += -SINGLE_Emin + 23; /* largest exp to be 22 */
+
+ if ((precision_loss = round_to_int(&tmp))) {
+#ifdef PECULIAR_486
+ /* Did it round to a non-denormal ? */
+ /* This behaviour might be regarded as
+ * peculiar, it appears that the 80486 rounds
+ * to the dest precision, then converts to
+ * decide underflow. */
+ if ((tmp.sigl == 0x00800000) &&
+ ((FPU_st0_ptr->sigh & 0x000000ff) || FPU_st0_ptr->sigl))
+ EXCEPTION(precision_loss);
+ else
+#endif /* PECULIAR_486 */
+ {
+ EXCEPTION(EX_Underflow | precision_loss);
+ /* This is a special case: see sec
+ * 16.2.5.1 of the 80486 book */
+ if (!(control_word & EX_Underflow))
+ return 0;
+ }
+ }
+ templ = tmp.sigl;
+ } else {
+ if (tmp.sigl | (tmp.sigh & 0x000000ff)) {
+ unsigned long increment = 0; /* avoid gcc warnings */
+ unsigned long sigh = tmp.sigh;
+ unsigned long sigl = tmp.sigl;
+
+ switch (control_word & CW_RC) {
+ case RC_RND:
+ increment = ((sigh & 0xff) > 0x80) /* more than half */
+ ||(((sigh & 0xff) == 0x80) && sigl) /* more than half */
+ ||((sigh & 0x180) == 0x180); /* round to even */
+ break;
+ case RC_DOWN: /* towards -infinity */
+ increment = (tmp.sign == SIGN_POS)
+ ? 0 : (sigl | (sigh & 0xff));
+ break;
+ case RC_UP: /* towards +infinity */
+ increment = (tmp.sign == SIGN_POS)
+ ? (sigl | (sigh & 0xff)) : 0;
+ break;
+ case RC_CHOP:
+ increment = 0;
+ break;
+ }
+
+ /* Truncate part of the mantissa */
+ tmp.sigl = 0;
+
+ if (increment) {
+ set_precision_flag_up();
+
+ if (sigh >= 0xffffff00) {
+ /* The sigh part overflows */
+ tmp.sigh = 0x80000000;
+ exp++;
+ if (exp >= EXP_OVER)
+ goto overflow;
+ } else {
+ tmp.sigh &= 0xffffff00;
+ tmp.sigh += 0x100;
+ }
+ } else {
+ set_precision_flag_down();
+ tmp.sigh &= 0xffffff00; /* Finish the truncation */
+ }
+ }
+ templ = (tmp.sigh >> 8) & 0x007fffff;
+
+ if (exp > SINGLE_Emax) {
+ overflow:
+ EXCEPTION(EX_Overflow);
+ /* This is a special case: see sec 16.2.5.1 of
+ * the 80486 book */
+ if (control_word & EX_Overflow) {
+ /* Overflow to infinity */
+ templ = 0x7f800000;
+ } else
+ return 0;
+ } else
+ templ |= ((exp + SINGLE_Ebias) & 0xff) << 23;
+ }
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ templ = 0;
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ templ = 0x7f800000;
+ } else
+ if (FPU_st0_tag == TW_NaN) {
+ /* See if we can get a valid NaN from
+ * the FPU_REG */
+ templ = FPU_st0_ptr->sigh >> 8;
+ if (!(templ & 0x3fffff)) {
+ /* This case does not seem to
+ * be handled by the 80486
+ * specs */
+ EXCEPTION(EX_Invalid);
+ /* Make the quiet NaN "real
+ * indefinite" */
+ goto put_indefinite;
+ }
+ templ |= 0x7f800000;
+ } else
+ if (FPU_st0_tag == TW_Empty) {
+ /* Empty register (stack
+ * underflow) */
+ EXCEPTION(EX_StackUnder);
+ if (control_word & EX_Invalid) {
+ /* The masked response */
+ /* Put out the QNaN
+ * indefinite */
+ put_indefinite:
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, (void *) single, 4); */
+ suword((unsigned long *) single, 0xffc00000);
+ REENTRANT_CHECK(ON);
+ return 1;
+ } else
+ return 0;
+ }
+#if 0 /* TW_Denormal is not used yet, and probably
+ * won't be */
+ else
+ if (FPU_st0_tag == TW_Denormal) {
+ /* Extended real ->
+ * real will always
+ * underflow */
+ templ = 0;
+ EXCEPTION(EX_Underflow);
+ }
+#endif
+#ifdef PARANOID
+ else {
+ EXCEPTION(EX_INTERNAL | 0x106);
+ return 0;
+ }
+#endif
+ if (FPU_st0_ptr->sign)
+ templ |= 0x80000000;
+
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, (void *) single, 4); */
+ suword((unsigned long *) single, templ);
+ REENTRANT_CHECK(ON);
+
+ return 1;
+}
+
+
+/* Put a long long into user memory */
+int
+reg_store_int64(void)
+{
+ long long *d = (long long *) FPU_data_address;
+ FPU_REG t;
+ long long tll;
+
+ if (FPU_st0_tag == TW_Empty) {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if (control_word & EX_Invalid) {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ goto put_indefinite;
+ } else
+ return 0;
+ }
+ reg_move(FPU_st0_ptr, &t);
+ round_to_int(&t);
+ ((long *) &tll)[0] = t.sigl;
+ ((long *) &tll)[1] = t.sigh;
+ if ((t.sigh & 0x80000000) &&
+ !((t.sigh == 0x80000000) && (t.sigl == 0) && (t.sign == SIGN_NEG))) {
+ EXCEPTION(EX_Invalid);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if (control_word & EX_Invalid) {
+ /* Produce "indefinite" */
+ put_indefinite:
+ ((long *) &tll)[1] = 0x80000000;
+ ((long *) &tll)[0] = 0;
+ } else
+ return 0;
+ } else
+ if (t.sign)
+ tll = -tll;
+
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, (void *) d, 8); */
+ suword((unsigned long *) d, ((long *) &tll)[0]);
+ suword(1 + (unsigned long *) d, ((long *) &tll)[1]);
+ REENTRANT_CHECK(ON);
+
+ return 1;
+}
+
+
+/* Put a long into user memory */
+int
+reg_store_int32(void)
+{
+ long *d = (long *) FPU_data_address;
+ FPU_REG t;
+
+ if (FPU_st0_tag == TW_Empty) {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if (control_word & EX_Invalid) {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, d, 4);*/
+ suword((unsigned long *) d, 0x80000000);
+ REENTRANT_CHECK(ON);
+ return 1;
+ } else
+ return 0;
+ }
+ reg_move(FPU_st0_ptr, &t);
+ round_to_int(&t);
+ if (t.sigh ||
+ ((t.sigl & 0x80000000) &&
+ !((t.sigl == 0x80000000) && (t.sign == SIGN_NEG)))) {
+ EXCEPTION(EX_Invalid);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if (control_word & EX_Invalid) {
+ /* Produce "indefinite" */
+ t.sigl = 0x80000000;
+ } else
+ return 0;
+ } else
+ if (t.sign)
+ t.sigl = -(long) t.sigl;
+
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, d, 4); */
+ suword((unsigned long *) d, t.sigl);
+ REENTRANT_CHECK(ON);
+
+ return 1;
+}
+
+
+/* Put a short into user memory */
+int
+reg_store_int16(void)
+{
+ short *d = (short *) FPU_data_address;
+ FPU_REG t;
+ short ts;
+
+ if (FPU_st0_tag == TW_Empty) {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if (control_word & EX_Invalid) {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, d, 2);*/
+ suword((unsigned short *) d, 0x8000);
+ REENTRANT_CHECK(ON);
+ return 1;
+ } else
+ return 0;
+ }
+ reg_move(FPU_st0_ptr, &t);
+ round_to_int(&t);
+ if (t.sigh ||
+ ((t.sigl & 0xffff8000) &&
+ !((t.sigl == 0x8000) && (t.sign == SIGN_NEG)))) {
+ EXCEPTION(EX_Invalid);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if (control_word & EX_Invalid) {
+ /* Produce "indefinite" */
+ ts = 0x8000;
+ } else
+ return 0;
+ } else
+ if (t.sign)
+ t.sigl = -t.sigl;
+
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, d, 2); */
+ suword((short *) d, (short) t.sigl);
+ REENTRANT_CHECK(ON);
+
+ return 1;
+}
+
+
+/* Put a packed bcd array into user memory */
+int
+reg_store_bcd(void)
+{
+ char *d = (char *) FPU_data_address;
+ FPU_REG t;
+ long long ll;
+ unsigned char b;
+ int i;
+ unsigned char sign = (FPU_st0_ptr->sign == SIGN_NEG) ? 0x80 : 0;
+
+ if (FPU_st0_tag == TW_Empty) {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if (control_word & EX_Invalid) {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ goto put_indefinite;
+ } else
+ return 0;
+ }
+ reg_move(FPU_st0_ptr, &t);
+ round_to_int(&t);
+ ll = *(long long *) (&t.sigl);
+
+ /* Check for overflow, by comparing with 999999999999999999 decimal. */
+ if ((t.sigh > 0x0de0b6b3) ||
+ ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff))) {
+ EXCEPTION(EX_Invalid);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if (control_word & EX_Invalid) {
+ put_indefinite:
+ /* Produce "indefinite" */
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, d, 10);*/
+ subyte((unsigned char *) d + 7, 0xff);
+ subyte((unsigned char *) d + 8, 0xff);
+ subyte((unsigned char *) d + 9, 0xff);
+ REENTRANT_CHECK(ON);
+ return 1;
+ } else
+ return 0;
+ }
+/* verify_area(VERIFY_WRITE, d, 10);*/
+ for (i = 0; i < 9; i++) {
+ b = div_small(&ll, 10);
+ b |= (div_small(&ll, 10)) << 4;
+ REENTRANT_CHECK(OFF);
+ subyte((unsigned char *) d + i, b);
+ REENTRANT_CHECK(ON);
+ }
+ REENTRANT_CHECK(OFF);
+ subyte((unsigned char *) d + 9, sign);
+ REENTRANT_CHECK(ON);
+
+ return 1;
+}
+/*===========================================================================*/
+
+/* r gets mangled such that sig is int, sign:
+ it is NOT normalized */
+/* The return value (in eax) is zero if the result is exact,
+ if bits are changed due to rounding, truncation, etc, then
+ a non-zero value is returned */
+/* Overflow is signalled by a non-zero return value (in eax).
+ In the case of overflow, the returned significand always has the
+ the largest possible value */
+/* The value returned in eax is never actually needed :-) */
+int
+round_to_int(FPU_REG * r)
+{
+ char very_big;
+ unsigned eax;
+
+ if (r->tag == TW_Zero) {
+ /* Make sure that zero is returned */
+ *(long long *) &r->sigl = 0;
+ return 0; /* o.k. */
+ }
+ if (r->exp > EXP_BIAS + 63) {
+ r->sigl = r->sigh = ~0; /* The largest representable number */
+ return 1; /* overflow */
+ }
+ eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp);
+ very_big = !(~(r->sigh) | ~(r->sigl)); /* test for 0xfff...fff */
+#define half_or_more (eax & 0x80000000)
+#define frac_part (eax)
+#define more_than_half ((eax & 0x80000001) == 0x80000001)
+ switch (control_word & CW_RC) {
+ case RC_RND:
+ if (more_than_half /* nearest */
+ || (half_or_more && (r->sigl & 1))) { /* odd -> even */
+ if (very_big)
+ return 1; /* overflow */
+ (*(long long *) (&r->sigl))++;
+ return LOST_UP;
+ }
+ break;
+ case RC_DOWN:
+ if (frac_part && r->sign) {
+ if (very_big)
+ return 1; /* overflow */
+ (*(long long *) (&r->sigl))++;
+ return LOST_UP;
+ }
+ break;
+ case RC_UP:
+ if (frac_part && !r->sign) {
+ if (very_big)
+ return 1; /* overflow */
+ (*(long long *) (&r->sigl))++;
+ return LOST_UP;
+ }
+ break;
+ case RC_CHOP:
+ break;
+ }
+
+ return eax ? LOST_DOWN : 0;
+
+}
+/*===========================================================================*/
+
+char *
+fldenv(void)
+{
+ char *s = (char *) FPU_data_address;
+ unsigned short tag_word = 0;
+ unsigned char tag;
+ int i;
+
+ REENTRANT_CHECK(OFF);
+ control_word = fuword((unsigned short *) s);
+ status_word = fuword((unsigned short *) (s + 4));
+ tag_word = fuword((unsigned short *) (s + 8));
+ ip_offset = fuword((unsigned long *) (s + 0x0c));
+ cs_selector = fuword((unsigned long *) (s + 0x10));
+ data_operand_offset = fuword((unsigned long *) (s + 0x14));
+ operand_selector = fuword((unsigned long *) (s + 0x18));
+ REENTRANT_CHECK(ON);
+
+ top = (status_word >> SW_Top_Shift) & 7;
+
+ for (i = 0; i < 8; i++) {
+ tag = tag_word & 3;
+ tag_word >>= 2;
+
+ switch (tag) {
+ case 0:
+ regs[i].tag = TW_Valid;
+ break;
+ case 1:
+ regs[i].tag = TW_Zero;
+ break;
+ case 2:
+ regs[i].tag = TW_NaN;
+ break;
+ case 3:
+ regs[i].tag = TW_Empty;
+ break;
+ }
+ }
+
+ FPU_data_address = (void *) data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+
+ return s + 0x1c;
+}
+
+
+void
+frstor(void)
+{
+ int i, stnr;
+ unsigned char tag;
+ unsigned short saved_status, saved_control;
+ char *s = (char *) fldenv();
+
+ saved_status = status_word;
+ saved_control = control_word;
+ control_word = 0x037f; /* Mask all interrupts while we load. */
+ for (i = 0; i < 8; i++) {
+ /* load each register */
+ FPU_data_address = (void *) (s + i * 10);
+ reg_load_extended();
+ stnr = (i + top) & 7;
+ tag = regs[stnr].tag; /* derived from the loaded tag word */
+ reg_move(&FPU_loaded_data, &regs[stnr]);
+ if (tag == TW_NaN) {
+ /* The current data is a special, i.e. NaN,
+ * unsupported, infinity, or denormal */
+ unsigned char t = regs[stnr].tag; /* derived from the new
+ * data */
+ if ( /* (t == TW_Valid) || *** */ (t == TW_Zero))
+ regs[stnr].tag = TW_NaN;
+ } else
+ regs[stnr].tag = tag;
+ }
+ control_word = saved_control;
+ status_word = saved_status;
+
+ FPU_data_address = (void *) data_operand_offset; /* We want no net effect */
+}
+
+
+unsigned short
+tag_word(void)
+{
+ unsigned short word = 0;
+ unsigned char tag;
+ int i;
+
+ for (i = 7; i >= 0; i--) {
+ switch (tag = regs[i].tag) {
+#if 0 /* TW_Denormal is not used yet, and probably
+ * won't be */
+ case TW_Denormal:
+#endif
+ case TW_Valid:
+ if (regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias))
+ tag = 2;
+ break;
+ case TW_Infinity:
+ case TW_NaN:
+ tag = 2;
+ break;
+ case TW_Empty:
+ tag = 3;
+ break;
+ /* TW_Valid and TW_Zero already have the correct value */
+ }
+ word <<= 2;
+ word |= tag;
+ }
+ return word;
+}
+
+
+char *
+fstenv(void)
+{
+ char *d = (char *) FPU_data_address;
+
+/* verify_area(VERIFY_WRITE, d, 28);*/
+
+#if 0 /****/
+ *(unsigned short *) &cs_selector = fpu_cs;
+ *(unsigned short *) &operand_selector = fpu_os;
+#endif /****/
+
+ REENTRANT_CHECK(OFF);
+ suword((unsigned short *) d, control_word);
+ suword((unsigned short *) (d + 4), (status_word & ~SW_Top) | ((top & 7) << SW_Top_Shift));
+ suword((unsigned short *) (d + 8), tag_word());
+ suword((unsigned long *) (d + 0x0c), ip_offset);
+ suword((unsigned long *) (d + 0x10), cs_selector);
+ suword((unsigned long *) (d + 0x14), data_operand_offset);
+ suword((unsigned long *) (d + 0x18), operand_selector);
+ REENTRANT_CHECK(ON);
+
+ return d + 0x1c;
+}
+
+
+void
+fsave(void)
+{
+ char *d;
+ FPU_REG tmp, *rp;
+ int i;
+ short e;
+
+ d = fstenv();
+/* verify_area(VERIFY_WRITE, d, 80);*/
+ for (i = 0; i < 8; i++) {
+ /* Store each register in the order: st(0), st(1), ... */
+ rp = &regs[(top + i) & 7];
+
+ e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
+
+ if (rp->tag == TW_Valid) {
+ if (e >= 0x7fff) {
+ /* Overflow to infinity */
+ REENTRANT_CHECK(OFF);
+ suword((unsigned long *) (d + i * 10), 0);
+ suword((unsigned long *) (d + i * 10 + 4), 0);
+ REENTRANT_CHECK(ON);
+ e = 0x7fff;
+ } else
+ if (e <= 0) {
+ if (e > -63) {
+ /* Make a de-normal */
+ reg_move(rp, &tmp);
+ tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 62 */
+ round_to_int(&tmp);
+ REENTRANT_CHECK(OFF);
+ suword((unsigned long *) (d + i * 10), tmp.sigl);
+ suword((unsigned long *) (d + i * 10 + 4), tmp.sigh);
+ REENTRANT_CHECK(ON);
+ } else {
+ /* Underflow to zero */
+ REENTRANT_CHECK(OFF);
+ suword((unsigned long *) (d + i * 10), 0);
+ suword((unsigned long *) (d + i * 10 + 4), 0);
+ REENTRANT_CHECK(ON);
+ }
+ e = 0;
+ } else {
+ REENTRANT_CHECK(OFF);
+ suword((unsigned long *) (d + i * 10), rp->sigl);
+ suword((unsigned long *) (d + i * 10 + 4), rp->sigh);
+ REENTRANT_CHECK(ON);
+ }
+ } else
+ if (rp->tag == TW_Zero) {
+ REENTRANT_CHECK(OFF);
+ suword((unsigned long *) (d + i * 10), 0);
+ suword((unsigned long *) (d + i * 10 + 4), 0);
+ REENTRANT_CHECK(ON);
+ e = 0;
+ } else
+ if (rp->tag == TW_Infinity) {
+ REENTRANT_CHECK(OFF);
+ suword((unsigned long *) (d + i * 10), 0);
+ suword((unsigned long *) (d + i * 10 + 4), 0x80000000);
+ REENTRANT_CHECK(ON);
+ e = 0x7fff;
+ } else
+ if (rp->tag == TW_NaN) {
+ REENTRANT_CHECK(OFF);
+ suword((unsigned long *) (d + i * 10), rp->sigl);
+ suword((unsigned long *) (d + i * 10 + 4), rp->sigh);
+ REENTRANT_CHECK(ON);
+ e = 0x7fff;
+ } else
+ if (rp->tag == TW_Empty) {
+ /* just copy the reg */
+ REENTRANT_CHECK(OFF);
+ suword((unsigned long *) (d + i * 10), rp->sigl);
+ suword((unsigned long *) (d + i * 10 + 4), rp->sigh);
+ REENTRANT_CHECK(ON);
+ }
+ e |= rp->sign == SIGN_POS ? 0 : 0x8000;
+ REENTRANT_CHECK(OFF);
+ suword((unsigned short *) (d + i * 10 + 8), e);
+ REENTRANT_CHECK(ON);
+ }
+
+ finit();
+
+}
+/*===========================================================================*/
diff --git a/sys/gnu/i386/fpemul/reg_mul.c b/sys/gnu/i386/fpemul/reg_mul.c
new file mode 100644
index 0000000..b51e9fc
--- /dev/null
+++ b/sys/gnu/i386/fpemul/reg_mul.c
@@ -0,0 +1,162 @@
+/*
+ * reg_mul.c
+ *
+ * Multiply one FPU_REG by another, put the result in a destination FPU_REG.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_mul.c,v 1.2 1994/04/29 21:30:21 gclarkii Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | The destination may be any FPU_REG, including one of the source FPU_REGs. |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "fpu_system.h"
+
+
+/* This routine must be called with non-empty source registers */
+void
+reg_mul(FPU_REG * a, FPU_REG * b, FPU_REG * dest, unsigned int control_w)
+{
+ char sign = (a->sign ^ b->sign);
+
+ if (!(a->tag | b->tag)) {
+ /* This should be the most common case */
+ reg_u_mul(a, b, dest, control_w);
+ dest->sign = sign;
+ return;
+ } else
+ if ((a->tag <= TW_Zero) && (b->tag <= TW_Zero)) {
+#ifdef DENORM_OPERAND
+ if (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ||
+ ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER))) {
+ if (denormal_operand())
+ return;
+ }
+#endif /* DENORM_OPERAND */
+ /* Must have either both arguments == zero, or one
+ * valid and the other zero. The result is therefore
+ * zero. */
+ reg_move(&CONST_Z, dest);
+#ifdef PECULIAR_486
+ /* The 80486 book says that the answer is +0, but a
+ * real 80486 appears to behave this way... */
+ dest->sign = sign;
+#endif /* PECULIAR_486 */
+ return;
+ }
+#if 0 /* TW_Denormal is not used yet... perhaps
+ * never will be. */
+ else
+ if ((a->tag <= TW_Denormal) && (b->tag <= TW_Denormal)) {
+ /* One or both arguments are de-normalized */
+ /* Internal de-normalized numbers are not
+ * supported yet */
+ EXCEPTION(EX_INTERNAL | 0x105);
+ reg_move(&CONST_Z, dest);
+ }
+#endif
+ else {
+ /* Must have infinities, NaNs, etc */
+ if ((a->tag == TW_NaN) || (b->tag == TW_NaN)) {
+ real_2op_NaN(a, b, dest);
+ return;
+ } else
+ if (a->tag == TW_Infinity) {
+ if (b->tag == TW_Zero) {
+ arith_invalid(dest);
+ return;
+ }
+ /* Zero*Infinity is invalid */
+ else {
+#ifdef DENORM_OPERAND
+ if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(a, dest);
+ dest->sign = sign;
+ }
+ return;
+ } else
+ if (b->tag == TW_Infinity) {
+ if (a->tag == TW_Zero) {
+ arith_invalid(dest);
+ return;
+ }
+ /* Zero*Infinity is
+ * invalid */
+ else {
+#ifdef DENORM_OPERAND
+ if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(b, dest);
+ dest->sign = sign;
+ }
+ return;
+ }
+#ifdef PARANOID
+ else {
+ EXCEPTION(EX_INTERNAL | 0x102);
+ }
+#endif /* PARANOID */
+ }
+}
diff --git a/sys/gnu/i386/fpemul/reg_norm.s b/sys/gnu/i386/fpemul/reg_norm.s
new file mode 100644
index 0000000..6f9dd7c
--- /dev/null
+++ b/sys/gnu/i386/fpemul/reg_norm.s
@@ -0,0 +1,182 @@
+/*
+ * reg_norm.s
+ *
+ * Normalize the value in a FPU_REG.
+ *
+ * Call from C as:
+ * void normalize(FPU_REG *n)
+ *
+ * void normalize_nuo(FPU_REG *n)
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_norm.s,v 1.2 1994/04/29 21:30:22 gclarkii Exp $
+ *
+ */
+
+
+#include "fpu_asm.h"
+
+
+.text
+
+ .align 2,144
+.globl _normalize
+
+_normalize:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %ebx
+
+ movl PARAM1,%ebx
+
+ movl SIGH(%ebx),%edx
+ movl SIGL(%ebx),%eax
+
+ orl %edx,%edx /* ms bits */
+ js L_done /* Already normalized */
+ jnz L_shift_1 /* Shift left 1 - 31 bits */
+
+ orl %eax,%eax
+ jz L_zero /* The contents are zero */
+
+/* L_shift_32: */
+ movl %eax,%edx
+ xorl %eax,%eax
+ subl $32,EXP(%ebx) /* This can cause an underflow */
+
+/* We need to shift left by 1 - 31 bits */
+L_shift_1:
+ bsrl %edx,%ecx /* get the required shift in %ecx */
+ subl $31,%ecx
+ negl %ecx
+ shld %cl,%eax,%edx
+ shl %cl,%eax
+ subl %ecx,EXP(%ebx) /* This can cause an underflow */
+
+ movl %edx,SIGH(%ebx)
+ movl %eax,SIGL(%ebx)
+
+L_done:
+ cmpl EXP_OVER,EXP(%ebx)
+ jge L_overflow
+
+ cmpl EXP_UNDER,EXP(%ebx)
+ jle L_underflow
+
+L_exit:
+ popl %ebx
+ leave
+ ret
+
+
+L_zero:
+ movl EXP_UNDER,EXP(%ebx)
+ movb TW_Zero,TAG(%ebx)
+ jmp L_exit
+
+L_underflow:
+ push %ebx
+ call _arith_underflow
+ pop %ebx
+ jmp L_exit
+
+L_overflow:
+ push %ebx
+ call _arith_overflow
+ pop %ebx
+ jmp L_exit
+
+
+
+/* Normalise without reporting underflow or overflow */
+ .align 2,144
+.globl _normalize_nuo
+
+_normalize_nuo:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %ebx
+
+ movl PARAM1,%ebx
+
+ movl SIGH(%ebx),%edx
+ movl SIGL(%ebx),%eax
+
+ orl %edx,%edx /* ms bits */
+ js L_exit /* Already normalized */
+ jnz L_nuo_shift_1 /* Shift left 1 - 31 bits */
+
+ orl %eax,%eax
+ jz L_zero /* The contents are zero */
+
+/* L_nuo_shift_32: */
+ movl %eax,%edx
+ xorl %eax,%eax
+ subl $32,EXP(%ebx) /* This can cause an underflow */
+
+/* We need to shift left by 1 - 31 bits */
+L_nuo_shift_1:
+ bsrl %edx,%ecx /* get the required shift in %ecx */
+ subl $31,%ecx
+ negl %ecx
+ shld %cl,%eax,%edx
+ shl %cl,%eax
+ subl %ecx,EXP(%ebx) /* This can cause an underflow */
+
+ movl %edx,SIGH(%ebx)
+ movl %eax,SIGL(%ebx)
+ jmp L_exit
+
+
diff --git a/sys/gnu/i386/fpemul/reg_round.s b/sys/gnu/i386/fpemul/reg_round.s
new file mode 100644
index 0000000..99a2a8a
--- /dev/null
+++ b/sys/gnu/i386/fpemul/reg_round.s
@@ -0,0 +1,653 @@
+ .file "reg_round.S"
+/*
+ * reg_round.S
+ *
+ * Rounding/truncation/etc for FPU basic arithmetic functions.
+ *
+ * This code has four possible entry points.
+ * The following must be entered by a jmp intruction:
+ * FPU_round, FPU_round_sqrt, and FPU_Arith_exit.
+ *
+ * The _round_reg entry point is intended to be used by C code.
+ * From C, call as:
+ * void round_reg(FPU_REG *arg, unsigned int extent, unsigned int control_w)
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_round.s,v 1.2 1994/04/29 21:30:23 gclarkii Exp $
+ *
+ */
+
+
+/*---------------------------------------------------------------------------+
+ | Four entry points. |
+ | |
+ | Needed by both the FPU_round and FPU_round_sqrt entry points: |
+ | %eax:%ebx 64 bit significand |
+ | %edx 32 bit extension of the significand |
+ | %edi pointer to an FPU_REG for the result to be stored |
+ | stack calling function must have set up a C stack frame and |
+ | pushed %esi, %edi, and %ebx |
+ | |
+ | Needed just for the FPU_round_sqrt entry point: |
+ | %cx A control word in the same format as the FPU control word. |
+ | Otherwise, PARAM4 must give such a value. |
+ | |
+ | |
+ | The significand and its extension are assumed to be exact in the |
+ | following sense: |
+ | If the significand by itself is the exact result then the significand |
+ | extension (%edx) must contain 0, otherwise the significand extension |
+ | must be non-zero. |
+ | If the significand extension is non-zero then the significand is |
+ | smaller than the magnitude of the correct exact result by an amount |
+ | greater than zero and less than one ls bit of the significand. |
+ | The significand extension is only required to have three possible |
+ | non-zero values: |
+ | less than 0x80000000 <=> the significand is less than 1/2 an ls |
+ | bit smaller than the magnitude of the |
+ | true exact result. |
+ | exactly 0x80000000 <=> the significand is exactly 1/2 an ls bit |
+ | smaller than the magnitude of the true |
+ | exact result. |
+ | greater than 0x80000000 <=> the significand is more than 1/2 an ls |
+ | bit smaller than the magnitude of the |
+ | true exact result. |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | The code in this module has become quite complex, but it should handle |
+ | all of the FPU flags which are set at this stage of the basic arithmetic |
+ | computations. |
+ | There are a few rare cases where the results are not set identically to |
+ | a real FPU. These require a bit more thought because at this stage the |
+ | results of the code here appear to be more consistent... |
+ | This may be changed in a future version. |
+ +---------------------------------------------------------------------------*/
+
+
+#include "fpu_asm.h"
+#include "exception.h"
+#include "control_w.h"
+
+#define LOST_DOWN $1
+#define LOST_UP $2
+#define DENORMAL $1
+#define UNMASKED_UNDERFLOW $2
+
+.data
+ .align 2,0
+FPU_bits_lost:
+ .byte 0
+FPU_denormal:
+ .byte 0
+
+.text
+ .align 2,144
+.globl FPU_round
+.globl FPU_round_sqrt
+.globl FPU_Arith_exit
+.globl _round_reg
+
+/* Entry point when called from C */
+_round_reg:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%edi
+ movl SIGH(%edi),%eax
+ movl SIGL(%edi),%ebx
+ movl PARAM2,%edx
+ movl PARAM3,%ecx
+ jmp FPU_round_sqrt
+
+FPU_round: /* Normal entry point */
+ movl PARAM4,%ecx
+
+FPU_round_sqrt: /* Entry point from wm_sqrt.S */
+
+#ifdef PARANOID
+/* Cannot use this here yet */
+/* orl %eax,%eax */
+/* jns L_entry_bugged */
+#endif PARANOID
+
+ cmpl EXP_UNDER,EXP(%edi)
+ jle xMake_denorm /* The number is a de-normal*/
+
+ movb $0,FPU_denormal /* 0 -> not a de-normal*/
+
+xDenorm_done:
+ movb $0,FPU_bits_lost /*No bits yet lost in rounding*/
+
+ movl %ecx,%esi
+ andl CW_PC,%ecx
+ cmpl PR_64_BITS,%ecx
+ je LRound_To_64
+
+ cmpl PR_53_BITS,%ecx
+ je LRound_To_53
+
+ cmpl PR_24_BITS,%ecx
+ je LRound_To_24
+
+#ifdef PARANOID
+ jmp L_bugged /* There is no bug, just a bad control word */
+#endif PARANOID
+
+
+/* Round etc to 24 bit precision */
+LRound_To_24:
+ movl %esi,%ecx
+ andl CW_RC,%ecx
+ cmpl RC_RND,%ecx
+ je LRound_nearest_24
+
+ cmpl RC_CHOP,%ecx
+ je LCheck_truncate_24
+
+ cmpl RC_UP,%ecx /* Towards +infinity */
+ je LUp_24
+
+ cmpl RC_DOWN,%ecx /* Towards -infinity */
+ je LDown_24
+
+#ifdef PARANOID
+ jmp L_bugged
+#endif PARANOID
+
+LUp_24:
+ cmpb SIGN_POS,SIGN(%edi)
+ jne LCheck_truncate_24 /* If negative then up==truncate */
+
+ jmp LCheck_24_round_up
+
+LDown_24:
+ cmpb SIGN_POS,SIGN(%edi)
+ je LCheck_truncate_24 /* If positive then down==truncate */
+
+LCheck_24_round_up:
+ movl %eax,%ecx
+ andl $0x000000ff,%ecx
+ orl %ebx,%ecx
+ orl %edx,%ecx
+ jnz LDo_24_round_up
+ jmp LRe_normalise
+
+LRound_nearest_24:
+ /* Do rounding of the 24th bit if needed (nearest or even) */
+ movl %eax,%ecx
+ andl $0x000000ff,%ecx
+ cmpl $0x00000080,%ecx
+ jc LCheck_truncate_24 /*less than half, no increment needed*/
+
+ jne LGreater_Half_24 /* greater than half, increment needed*/
+
+ /* Possibly half, we need to check the ls bits */
+ orl %ebx,%ebx
+ jnz LGreater_Half_24 /* greater than half, increment needed*/
+
+ orl %edx,%edx
+ jnz LGreater_Half_24 /* greater than half, increment needed*/
+
+ /* Exactly half, increment only if 24th bit is 1 (round to even)*/
+ testl $0x00000100,%eax
+ jz LDo_truncate_24
+
+LGreater_Half_24: /*Rounding: increment at the 24th bit*/
+LDo_24_round_up:
+ andl $0xffffff00,%eax /*Truncate to 24 bits*/
+ xorl %ebx,%ebx
+ movb LOST_UP,FPU_bits_lost
+ addl $0x00000100,%eax
+ jmp LCheck_Round_Overflow
+
+LCheck_truncate_24:
+ movl %eax,%ecx
+ andl $0x000000ff,%ecx
+ orl %ebx,%ecx
+ orl %edx,%ecx
+ jz LRe_normalise /* No truncation needed*/
+
+LDo_truncate_24:
+ andl $0xffffff00,%eax /* Truncate to 24 bits*/
+ xorl %ebx,%ebx
+ movb LOST_DOWN,FPU_bits_lost
+ jmp LRe_normalise
+
+
+/* Round etc to 53 bit precision */
+LRound_To_53:
+ movl %esi,%ecx
+ andl CW_RC,%ecx
+ cmpl RC_RND,%ecx
+ je LRound_nearest_53
+
+ cmpl RC_CHOP,%ecx
+ je LCheck_truncate_53
+
+ cmpl RC_UP,%ecx /* Towards +infinity*/
+ je LUp_53
+
+ cmpl RC_DOWN,%ecx /* Towards -infinity*/
+ je LDown_53
+
+#ifdef PARANOID
+ jmp L_bugged
+#endif PARANOID
+
+LUp_53:
+ cmpb SIGN_POS,SIGN(%edi)
+ jne LCheck_truncate_53 /* If negative then up==truncate*/
+
+ jmp LCheck_53_round_up
+
+LDown_53:
+ cmpb SIGN_POS,SIGN(%edi)
+ je LCheck_truncate_53 /* If positive then down==truncate*/
+
+LCheck_53_round_up:
+ movl %ebx,%ecx
+ andl $0x000007ff,%ecx
+ orl %edx,%ecx
+ jnz LDo_53_round_up
+ jmp LRe_normalise
+
+LRound_nearest_53:
+ /*Do rounding of the 53rd bit if needed (nearest or even)*/
+ movl %ebx,%ecx
+ andl $0x000007ff,%ecx
+ cmpl $0x00000400,%ecx
+ jc LCheck_truncate_53 /* less than half, no increment needed*/
+
+ jnz LGreater_Half_53 /* greater than half, increment needed*/
+
+ /*Possibly half, we need to check the ls bits*/
+ orl %edx,%edx
+ jnz LGreater_Half_53 /* greater than half, increment needed*/
+
+ /* Exactly half, increment only if 53rd bit is 1 (round to even)*/
+ testl $0x00000800,%ebx
+ jz LTruncate_53
+
+LGreater_Half_53: /*Rounding: increment at the 53rd bit*/
+LDo_53_round_up:
+ movb LOST_UP,FPU_bits_lost
+ andl $0xfffff800,%ebx /* Truncate to 53 bits*/
+ addl $0x00000800,%ebx
+ adcl $0,%eax
+ jmp LCheck_Round_Overflow
+
+LCheck_truncate_53:
+ movl %ebx,%ecx
+ andl $0x000007ff,%ecx
+ orl %edx,%ecx
+ jz LRe_normalise
+
+LTruncate_53:
+ movb LOST_DOWN,FPU_bits_lost
+ andl $0xfffff800,%ebx /* Truncate to 53 bits*/
+ jmp LRe_normalise
+
+
+/* Round etc to 64 bit precision*/
+LRound_To_64:
+ movl %esi,%ecx
+ andl CW_RC,%ecx
+ cmpl RC_RND,%ecx
+ je LRound_nearest_64
+
+ cmpl RC_CHOP,%ecx
+ je LCheck_truncate_64
+
+ cmpl RC_UP,%ecx /* Towards +infinity*/
+ je LUp_64
+
+ cmpl RC_DOWN,%ecx /* Towards -infinity*/
+ je LDown_64
+
+#ifdef PARANOID
+ jmp L_bugged
+#endif PARANOID
+
+LUp_64:
+ cmpb SIGN_POS,SIGN(%edi)
+ jne LCheck_truncate_64 /* If negative then up==truncate*/
+
+ orl %edx,%edx
+ jnz LDo_64_round_up
+ jmp LRe_normalise
+
+LDown_64:
+ cmpb SIGN_POS,SIGN(%edi)
+ je LCheck_truncate_64 /*If positive then down==truncate*/
+
+ orl %edx,%edx
+ jnz LDo_64_round_up
+ jmp LRe_normalise
+
+LRound_nearest_64:
+ cmpl $0x80000000,%edx
+ jc LCheck_truncate_64
+
+ jne LDo_64_round_up
+
+ /* Now test for round-to-even */
+ testb $1,%ebx
+ jz LCheck_truncate_64
+
+LDo_64_round_up:
+ movb LOST_UP,FPU_bits_lost
+ addl $1,%ebx
+ adcl $0,%eax
+
+LCheck_Round_Overflow:
+ jnc LRe_normalise /* Rounding done, no overflow */
+
+ /* Overflow, adjust the result (to 1.0) */
+ rcrl $1,%eax
+ rcrl $1,%ebx
+ incl EXP(%edi)
+ jmp LRe_normalise
+
+LCheck_truncate_64:
+ orl %edx,%edx
+ jz LRe_normalise
+
+LTruncate_64:
+ movb LOST_DOWN,FPU_bits_lost
+
+LRe_normalise:
+ testb $0xff,FPU_denormal
+ jnz xNormalise_result
+
+xL_Normalised:
+ cmpb LOST_UP,FPU_bits_lost
+ je xL_precision_lost_up
+
+ cmpb LOST_DOWN,FPU_bits_lost
+ je xL_precision_lost_down
+
+xL_no_precision_loss:
+ cmpl EXP_OVER,EXP(%edi)
+ jge L_overflow
+
+ /* store the result */
+ movb TW_Valid,TAG(%edi)
+
+xL_Store_significand:
+ movl %eax,SIGH(%edi)
+ movl %ebx,SIGL(%edi)
+
+FPU_Arith_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
+
+
+/* Set the FPU status flags to represent precision loss due to*/
+/* round-up.*/
+xL_precision_lost_up:
+ push %eax
+ call _set_precision_flag_up
+ popl %eax
+ jmp xL_no_precision_loss
+
+/* Set the FPU status flags to represent precision loss due to*/
+/* truncation.*/
+xL_precision_lost_down:
+ push %eax
+ call _set_precision_flag_down
+ popl %eax
+ jmp xL_no_precision_loss
+
+
+/* The number is a denormal (which might get rounded up to a normal)
+// Shift the number right the required number of bits, which will
+// have to be undone later...*/
+xMake_denorm:
+ /* The action to be taken depends upon whether the underflow
+ // exception is masked*/
+ testb CW_Underflow,%cl /* Underflow mask.*/
+ jz xUnmasked_underflow /* Do not make a denormal.*/
+
+ movb DENORMAL,FPU_denormal
+
+ pushl %ecx /* Save*/
+ movl EXP(%edi),%ecx
+ subl EXP_UNDER+1,%ecx
+ negl %ecx
+
+ cmpl $64,%ecx /* shrd only works for 0..31 bits */
+ jnc xDenorm_shift_more_than_63
+
+ cmpl $32,%ecx /* shrd only works for 0..31 bits */
+ jnc xDenorm_shift_more_than_32
+
+/* We got here without jumps by assuming that the most common requirement
+// is for a small de-normalising shift.
+// Shift by [1..31] bits */
+ addl %ecx,EXP(%edi)
+ orl %edx,%edx /* extension*/
+ setne %ch
+ xorl %edx,%edx
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ orb %ch,%dl
+ popl %ecx
+ jmp xDenorm_done
+
+/* Shift by [32..63] bits*/
+xDenorm_shift_more_than_32:
+ addl %ecx,EXP(%edi)
+ subb $32,%cl
+ orl %edx,%edx
+ setne %ch
+ orb %ch,%bl
+ xorl %edx,%edx
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ orl %edx,%edx /*test these 32 bits*/
+ setne %cl
+ orb %ch,%bl
+ orb %cl,%bl
+ movl %ebx,%edx
+ movl %eax,%ebx
+ xorl %eax,%eax
+ popl %ecx
+ jmp xDenorm_done
+
+/* Shift by [64..) bits*/
+xDenorm_shift_more_than_63:
+ cmpl $64,%ecx
+ jne xDenorm_shift_more_than_64
+
+/* Exactly 64 bit shift*/
+ addl %ecx,EXP(%edi)
+ xorl %ecx,%ecx
+ orl %edx,%edx
+ setne %cl
+ orl %ebx,%ebx
+ setne %ch
+ orb %ch,%cl
+ orb %cl,%al
+ movl %eax,%edx
+ xorl %eax,%eax
+ xorl %ebx,%ebx
+ popl %ecx
+ jmp xDenorm_done
+
+xDenorm_shift_more_than_64:
+ movl EXP_UNDER+1,EXP(%edi)
+/* This is easy, %eax must be non-zero, so..*/
+ movl $1,%edx
+ xorl %eax,%eax
+ xorl %ebx,%ebx
+ popl %ecx
+ jmp xDenorm_done
+
+
+xUnmasked_underflow:
+ /* Increase the exponent by the magic number*/
+ addl $(3*(1<<13)),EXP(%edi)
+ movb UNMASKED_UNDERFLOW,FPU_denormal
+ jmp xDenorm_done
+
+
+/* Undo the de-normalisation.*/
+xNormalise_result:
+ cmpb UNMASKED_UNDERFLOW,FPU_denormal
+ je xSignal_underflow
+
+/* The number must be a denormal if we got here.*/
+#ifdef PARANOID
+ /* But check it... just in case.*/
+ cmpl EXP_UNDER+1,EXP(%edi)
+ jne L_norm_bugged
+#endif PARANOID
+
+ orl %eax,%eax /* ms bits*/
+ jnz LNormalise_shift_up_to_31 /* Shift left 0 - 31 bits*/
+
+ orl %ebx,%ebx
+ jz L_underflow_to_zero /* The contents are zero*/
+
+/* Shift left 32 - 63 bits*/
+ movl %ebx,%eax
+ xorl %ebx,%ebx
+ subl $32,EXP(%edi)
+
+LNormalise_shift_up_to_31:
+ bsrl %eax,%ecx /* get the required shift in %ecx */
+ subl $31,%ecx
+ negl %ecx
+ shld %cl,%ebx,%eax
+ shl %cl,%ebx
+ subl %ecx,EXP(%edi)
+
+LNormalise_shift_done:
+ testb $0xff,FPU_bits_lost /* bits lost == underflow*/
+ jz xL_Normalised
+
+ /* There must be a masked underflow*/
+ push %eax
+ pushl EX_Underflow
+ call _exception
+ popl %eax
+ popl %eax
+ jmp xL_Normalised
+
+
+/* The operations resulted in a number too small to represent.
+// Masked response.*/
+L_underflow_to_zero:
+ push %eax
+ call _set_precision_flag_down
+ popl %eax
+
+ push %eax
+ pushl EX_Underflow
+ call _exception
+ popl %eax
+ popl %eax
+
+ movb TW_Zero,TAG(%edi)
+ jmp xL_Store_significand
+
+
+/* The operations resulted in a number too large to represent.*/
+L_overflow:
+ push %edi
+ call _arith_overflow
+ pop %edi
+ jmp FPU_Arith_exit
+
+
+xSignal_underflow:
+ push %eax
+ pushl EX_Underflow
+ call EXCEPTION
+ popl %eax
+ popl %eax
+ jmp xL_Normalised
+
+
+#ifdef PARANOID
+/* If we ever get here then we have problems! */
+L_bugged:
+ pushl EX_INTERNAL|0x201
+ call EXCEPTION
+ popl %ebx
+ jmp FPU_Arith_exit
+
+L_norm_bugged:
+ pushl EX_INTERNAL|0x216
+ call EXCEPTION
+ popl %ebx
+ jmp FPU_Arith_exit
+
+L_entry_bugged:
+ pushl EX_INTERNAL|0x217
+ call EXCEPTION
+ popl %ebx
+ jmp FPU_Arith_exit
+#endif PARANOID
diff --git a/sys/gnu/i386/fpemul/reg_u_add.s b/sys/gnu/i386/fpemul/reg_u_add.s
new file mode 100644
index 0000000..86c8475
--- /dev/null
+++ b/sys/gnu/i386/fpemul/reg_u_add.s
@@ -0,0 +1,244 @@
+ .file "reg_u_add.S"
+/*
+ * reg_u_add.S
+ *
+ * Add two valid (TW_Valid) FPU_REG numbers, of the same sign, and put the
+ * result in a destination FPU_REG.
+ *
+ * Call from C as:
+ * void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
+ * int control_w)
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_u_add.s,v 1.2 1994/04/29 21:34:23 gclarkii Exp $
+ *
+ */
+
+
+/*
+ | Kernel addition routine reg_u_add(reg *arg1, reg *arg2, reg *answ).
+ | Takes two valid reg f.p. numbers (TW_Valid), which are
+ | treated as unsigned numbers,
+ | and returns their sum as a TW_Valid or TW_S f.p. number.
+ | The returned number is normalized.
+ | Basic checks are performed if PARANOID is defined.
+ */
+
+#include "exception.h"
+#include "fpu_asm.h"
+#include "control_w.h"
+
+.text
+ .align 2,144
+.globl _reg_u_add
+_reg_u_add:
+ pushl %ebp
+ movl %esp,%ebp
+/* subl $16,%esp*/
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi /* source 1 */
+ movl PARAM2,%edi /* source 2 */
+
+#ifdef DENORM_OPERAND
+ cmpl EXP_UNDER,EXP(%esi)
+ jg xOp1_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp1_not_denorm:
+ cmpl EXP_UNDER,EXP(%edi)
+ jg xOp2_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp2_not_denorm:
+#endif DENORM_OPERAND
+
+/* xorl %ecx,%ecx*/
+ movl EXP(%esi),%ecx
+ subl EXP(%edi),%ecx /* exp1 - exp2 */
+/* jnc L_arg1_larger*/
+ jge L_arg1_larger
+
+ /* num1 is smaller */
+ movl SIGL(%esi),%ebx
+ movl SIGH(%esi),%eax
+
+ movl %edi,%esi
+ negw %cx
+ jmp L_accum_loaded
+
+L_arg1_larger:
+ /* num1 has larger or equal exponent */
+ movl SIGL(%edi),%ebx
+ movl SIGH(%edi),%eax
+
+L_accum_loaded:
+ movl PARAM3,%edi /* destination */
+ movb SIGN(%esi),%dl
+ movb %dl,SIGN(%edi) /* Copy the sign from the first arg */
+
+
+ movl EXP(%esi),%edx
+ movl %edx,EXP(%edi) /* Copy exponent to destination */
+
+ xorl %edx,%edx /* clear the extension */
+
+#ifdef PARANOID
+ testl $0x80000000,%eax
+ je L_bugged
+
+ testl $0x80000000,SIGH(%esi)
+ je L_bugged
+#endif PARANOID
+
+/* The number to be shifted is in %eax:%ebx:%edx*/
+ cmpw $32,%cx /* shrd only works for 0..31 bits */
+ jnc L_more_than_31
+
+/* less than 32 bits */
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ jmp L_shift_done
+
+L_more_than_31:
+ cmpw $64,%cx
+ jnc L_more_than_63
+
+ subb $32,%cl
+ jz L_exactly_32
+
+ shrd %cl,%eax,%edx
+ shr %cl,%eax
+ orl %ebx,%ebx
+ jz L_more_31_no_low /* none of the lowest bits is set*/
+
+ orl $1,%edx /* record the fact in the extension*/
+
+L_more_31_no_low:
+ movl %eax,%ebx
+ xorl %eax,%eax
+ jmp L_shift_done
+
+L_exactly_32:
+ movl %ebx,%edx
+ movl %eax,%ebx
+ xorl %eax,%eax
+ jmp L_shift_done
+
+L_more_than_63:
+ cmpw $65,%cx
+ jnc L_more_than_64
+
+ movl %eax,%edx
+ orl %ebx,%ebx
+ jz L_more_63_no_low
+
+ orl $1,%edx
+ jmp L_more_63_no_low
+
+L_more_than_64:
+ movl $1,%edx /* The shifted nr always at least one '1'*/
+
+L_more_63_no_low:
+ xorl %ebx,%ebx
+ xorl %eax,%eax
+
+L_shift_done:
+ /* Now do the addition */
+ addl SIGL(%esi),%ebx
+ adcl SIGH(%esi),%eax
+ jnc L_round_the_result
+
+ /* Overflow, adjust the result */
+ rcrl $1,%eax
+ rcrl $1,%ebx
+ rcrl $1,%edx
+ jnc L_no_bit_lost
+
+ orl $1,%edx
+
+L_no_bit_lost:
+ incl EXP(%edi)
+
+L_round_the_result:
+ jmp FPU_round /* Round the result*/
+
+
+
+#ifdef PARANOID
+/* If we ever get here then we have problems! */
+L_bugged:
+ pushl EX_INTERNAL|0x201
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+#endif PARANOID
+
+
+L_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
diff --git a/sys/gnu/i386/fpemul/reg_u_div.s b/sys/gnu/i386/fpemul/reg_u_div.s
new file mode 100644
index 0000000..4952923
--- /dev/null
+++ b/sys/gnu/i386/fpemul/reg_u_div.s
@@ -0,0 +1,506 @@
+ .file "reg_u_div.S"
+/*
+ * reg_u_div.S
+ *
+ * Core division routines
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_u_div.s,v 1.2 1994/04/29 21:34:24 gclarkii Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | Kernel for the division routines. |
+ | |
+ | void reg_u_div(FPU_REG *a, FPU_REG *a, |
+ | FPU_REG *dest, unsigned int control_word) |
+ | |
+ | Does not compute the destination exponent, but does adjust it. |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "fpu_asm.h"
+#include "control_w.h"
+
+
+/* #define dSIGL(x) (x) */
+/* #define dSIGH(x) 4(x) */
+
+
+.data
+/*
+ Local storage:
+ Result: accum_3:accum_2:accum_1:accum_0
+ Overflow flag: ovfl_flag
+ */
+ .align 2,0
+accum_3:
+ .long 0
+accum_2:
+ .long 0
+accum_1:
+ .long 0
+accum_0:
+ .long 0
+result_1:
+ .long 0
+result_2:
+ .long 0
+ovfl_flag:
+ .byte 0
+
+
+.text
+ .align 2,144
+
+.globl _reg_u_div
+
+.globl _divide_kernel
+
+_reg_u_div:
+ pushl %ebp
+ movl %esp,%ebp
+
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi /* pointer to num */
+ movl PARAM2,%ebx /* pointer to denom */
+ movl PARAM3,%edi /* pointer to answer */
+
+#ifdef DENORM_OPERAND
+ movl EXP(%esi),%eax
+ cmpl EXP_UNDER,%eax
+ jg xOp1_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp1_not_denorm:
+ movl EXP(%ebx),%eax
+ cmpl EXP_UNDER,%eax
+ jg xOp2_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp2_not_denorm:
+#endif DENORM_OPERAND
+
+_divide_kernel:
+#ifdef PARANOID
+/* testl $0x80000000, SIGH(%esi) *//* Dividend */
+/* je L_bugged */
+ testl $0x80000000, SIGH(%ebx) /* Divisor*/
+ je L_bugged
+#endif PARANOID
+
+/* Check if the divisor can be treated as having just 32 bits */
+ cmpl $0,SIGL(%ebx)
+ jnz L_Full_Division /* Can't do a quick divide */
+
+/* We should be able to zip through the division here */
+ movl SIGH(%ebx),%ecx /* The divisor */
+ movl SIGH(%esi),%edx /* Dividend */
+ movl SIGL(%esi),%eax /* Dividend */
+
+ cmpl %ecx,%edx
+ setaeb ovfl_flag /* Keep a record */
+ jb L_no_adjust
+
+ subl %ecx,%edx /* Prevent the overflow */
+
+L_no_adjust:
+ /* Divide the 64 bit number by the 32 bit denominator */
+ divl %ecx
+ movl %eax,result_2
+
+ /* Work on the remainder of the first division */
+ xorl %eax,%eax
+ divl %ecx
+ movl %eax,result_1
+
+ /* Work on the remainder of the 64 bit division */
+ xorl %eax,%eax
+ divl %ecx
+
+ testb $255,ovfl_flag /* was the num > denom ? */
+ je L_no_overflow
+
+ /* Do the shifting here */
+ /* increase the exponent */
+ incl EXP(%edi)
+
+ /* shift the mantissa right one bit */
+ stc /* To set the ms bit */
+ rcrl result_2
+ rcrl result_1
+ rcrl %eax
+
+L_no_overflow:
+ jmp LRound_precision /* Do the rounding as required*/
+
+
+/*---------------------------------------------------------------------------+
+ | Divide: Return arg1/arg2 to arg3. |
+ | |
+ | This routine does not use the exponents of arg1 and arg2, but does |
+ | adjust the exponent of arg3. |
+ | |
+ | The maximum returned value is (ignoring exponents) |
+ | .ffffffff ffffffff |
+ | ------------------ = 1.ffffffff fffffffe |
+ | .80000000 00000000 |
+ | and the minimum is |
+ | .80000000 00000000 |
+ | ------------------ = .80000000 00000001 (rounded) |
+ | .ffffffff ffffffff |
+ | |
+ +---------------------------------------------------------------------------*/
+
+
+L_Full_Division:
+ /* Save extended dividend in local register*/
+ movl SIGL(%esi),%eax
+ movl %eax,accum_2
+ movl SIGH(%esi),%eax
+ movl %eax,accum_3
+ xorl %eax,%eax
+ movl %eax,accum_1 /* zero the extension */
+ movl %eax,accum_0 /* zero the extension */
+
+ movl SIGL(%esi),%eax /* Get the current num */
+ movl SIGH(%esi),%edx
+
+/*----------------------------------------------------------------------*/
+/* Initialization done */
+/* Do the first 32 bits */
+
+ movb $0,ovfl_flag
+ cmpl SIGH(%ebx),%edx /* Test for imminent overflow */
+ jb LLess_than_1
+ ja LGreater_than_1
+
+ cmpl SIGL(%ebx),%eax
+ jb LLess_than_1
+
+LGreater_than_1:
+/* The dividend is greater or equal, would cause overflow */
+ setaeb ovfl_flag /* Keep a record */
+
+ subl SIGL(%ebx),%eax
+ sbbl SIGH(%ebx),%edx /* Prevent the overflow */
+ movl %eax,accum_2
+ movl %edx,accum_3
+
+LLess_than_1:
+/* At this point, we have a dividend < divisor, with a record of
+ adjustment in ovfl_flag */
+
+ /* We will divide by a number which is too large */
+ movl SIGH(%ebx),%ecx
+ addl $1,%ecx
+ jnc LFirst_div_not_1
+
+ /* here we need to divide by 100000000h,
+ i.e., no division at all.. */
+ mov %edx,%eax
+ jmp LFirst_div_done
+
+LFirst_div_not_1:
+ divl %ecx /* Divide the numerator by the augmented
+ denom ms dw */
+
+LFirst_div_done:
+ movl %eax,result_2 /* Put the result in the answer */
+
+ mull SIGH(%ebx) /* mul by the ms dw of the denom */
+
+ subl %eax,accum_2 /* Subtract from the num local reg */
+ sbbl %edx,accum_3
+
+ movl result_2,%eax /* Get the result back */
+ mull SIGL(%ebx) /* now mul the ls dw of the denom */
+
+ subl %eax,accum_1 /* Subtract from the num local reg */
+ sbbl %edx,accum_2
+ sbbl $0,accum_3
+ je LDo_2nd_32_bits /* Must check for non-zero result here */
+
+#ifdef PARANOID
+ jb L_bugged_1
+#endif PARANOID
+
+ /* need to subtract another once of the denom */
+ incl result_2 /* Correct the answer */
+
+ movl SIGL(%ebx),%eax
+ movl SIGH(%ebx),%edx
+ subl %eax,accum_1 /* Subtract from the num local reg */
+ sbbl %edx,accum_2
+
+#ifdef PARANOID
+ sbbl $0,accum_3
+ jne L_bugged_1 /* Must check for non-zero result here */
+#endif PARANOID
+
+/*----------------------------------------------------------------------*/
+/* Half of the main problem is done, there is just a reduced numerator
+ to handle now */
+/* Work with the second 32 bits, accum_0 not used from now on */
+LDo_2nd_32_bits:
+ movl accum_2,%edx /* get the reduced num */
+ movl accum_1,%eax
+
+ /* need to check for possible subsequent overflow */
+ cmpl SIGH(%ebx),%edx
+ jb LDo_2nd_div
+ ja LPrevent_2nd_overflow
+
+ cmpl SIGL(%ebx),%eax
+ jb LDo_2nd_div
+
+LPrevent_2nd_overflow:
+/* The numerator is greater or equal, would cause overflow */
+ /* prevent overflow */
+ subl SIGL(%ebx),%eax
+ sbbl SIGH(%ebx),%edx
+ movl %edx,accum_2
+ movl %eax,accum_1
+
+ incl result_2 /* Reflect the subtraction in the answer */
+
+#ifdef PARANOID
+ je L_bugged_2 /* Can't bump the result to 1.0 */
+#endif PARANOID
+
+LDo_2nd_div:
+ cmpl $0,%ecx /* augmented denom msw*/
+ jnz LSecond_div_not_1
+
+ /* %ecx == 0, we are dividing by 1.0 */
+ mov %edx,%eax
+ jmp LSecond_div_done
+
+LSecond_div_not_1:
+ divl %ecx /* Divide the numerator by the denom ms dw */
+
+LSecond_div_done:
+ movl %eax,result_1 /* Put the result in the answer */
+
+ mull SIGH(%ebx) /* mul by the ms dw of the denom */
+
+ subl %eax,accum_1 /* Subtract from the num local reg */
+ sbbl %edx,accum_2
+
+#ifdef PARANOID
+ jc L_bugged_2
+#endif PARANOID
+
+ movl result_1,%eax /* Get the result back */
+ mull SIGL(%ebx) /* now mul the ls dw of the denom */
+
+ subl %eax,accum_0 /* Subtract from the num local reg */
+ sbbl %edx,accum_1 /* Subtract from the num local reg */
+ sbbl $0,accum_2
+
+#ifdef PARANOID
+ jc L_bugged_2
+#endif PARANOID
+
+ jz LDo_3rd_32_bits
+
+#ifdef PARANOID
+ cmpl $1,accum_2
+ jne L_bugged_2
+#endif PARANOID
+
+ /* need to subtract another once of the denom */
+ movl SIGL(%ebx),%eax
+ movl SIGH(%ebx),%edx
+ subl %eax,accum_0 /* Subtract from the num local reg */
+ sbbl %edx,accum_1
+ sbbl $0,accum_2
+
+#ifdef PARANOID
+ jc L_bugged_2
+ jne L_bugged_2
+#endif PARANOID
+
+ addl $1,result_1 /* Correct the answer */
+ adcl $0,result_2
+
+#ifdef PARANOID
+ jc L_bugged_2 /* Must check for non-zero result here */
+#endif PARANOID
+
+/*----------------------------------------------------------------------*/
+/* The division is essentially finished here, we just need to perform
+ tidying operations. */
+/* deal with the 3rd 32 bits */
+LDo_3rd_32_bits:
+ movl accum_1,%edx /* get the reduced num */
+ movl accum_0,%eax
+
+ /* need to check for possible subsequent overflow */
+ cmpl SIGH(%ebx),%edx /* denom*/
+ jb LRound_prep
+ ja LPrevent_3rd_overflow
+
+ cmpl SIGL(%ebx),%eax /* denom */
+ jb LRound_prep
+
+LPrevent_3rd_overflow:
+ /* prevent overflow */
+ subl SIGL(%ebx),%eax
+ sbbl SIGH(%ebx),%edx
+ movl %edx,accum_1
+ movl %eax,accum_0
+
+ addl $1,result_1 /* Reflect the subtraction in the answer */
+ adcl $0,result_2
+ jne LRound_prep
+ jnc LRound_prep
+
+ /* This is a tricky spot, there is an overflow of the answer */
+ movb $255,ovfl_flag /* Overflow -> 1.000 */
+
+LRound_prep:
+/* Prepare for rounding.
+// To test for rounding, we just need to compare 2*accum with the
+// denom. */
+ movl accum_0,%ecx
+ movl accum_1,%edx
+ movl %ecx,%eax
+ orl %edx,%eax
+ jz LRound_ovfl /* The accumulator contains zero.*/
+
+ /* Multiply by 2 */
+ clc
+ rcll $1,%ecx
+ rcll $1,%edx
+ jc LRound_large /* No need to compare, denom smaller */
+
+ subl SIGL(%ebx),%ecx
+ sbbl SIGH(%ebx),%edx
+ jnc LRound_not_small
+
+ movl $0x70000000,%eax /* Denom was larger */
+ jmp LRound_ovfl
+
+LRound_not_small:
+ jnz LRound_large
+
+ movl $0x80000000,%eax /* Remainder was exactly 1/2 denom */
+ jmp LRound_ovfl
+
+LRound_large:
+ movl $0xff000000,%eax /* Denom was smaller */
+
+LRound_ovfl:
+/* We are now ready to deal with rounding, but first we must get
+ the bits properly aligned */
+ testb $255,ovfl_flag /* was the num > denom ? */
+ je LRound_precision
+
+ incl EXP(%edi)
+
+ /* shift the mantissa right one bit */
+ stc /* Will set the ms bit */
+ rcrl result_2
+ rcrl result_1
+ rcrl %eax
+
+/* Round the result as required */
+LRound_precision:
+ decl EXP(%edi) /* binary point between 1st & 2nd bits */
+
+ movl %eax,%edx
+ movl result_1,%ebx
+ movl result_2,%eax
+ jmp FPU_round
+
+
+#ifdef PARANOID
+/* The logic is wrong if we got here */
+L_bugged:
+ pushl EX_INTERNAL|0x202
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_1:
+ pushl EX_INTERNAL|0x203
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_2:
+ pushl EX_INTERNAL|0x204
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+
+ leave
+ ret
+#endif PARANOID
diff --git a/sys/gnu/i386/fpemul/reg_u_mul.s b/sys/gnu/i386/fpemul/reg_u_mul.s
new file mode 100644
index 0000000..798f204
--- /dev/null
+++ b/sys/gnu/i386/fpemul/reg_u_mul.s
@@ -0,0 +1,199 @@
+ .file "reg_u_mul.S"
+/*
+ * reg_u_mul.S
+ *
+ * Core multiplication routine
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_u_mul.s,v 1.2 1994/04/29 21:34:25 gclarkii Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | Basic multiplication routine. |
+ | Does not check the resulting exponent for overflow/underflow |
+ | |
+ | reg_u_mul(FPU_REG *a, FPU_REG *b, FPU_REG *c, unsigned int cw); |
+ | |
+ | Internal working is at approx 128 bits. |
+ | Result is rounded to nearest 53 or 64 bits, using "nearest or even". |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "fpu_asm.h"
+#include "control_w.h"
+
+
+.data
+ .align 2,0
+accum_0:
+ .long 0
+accum_1:
+ .long 0
+
+
+.text
+ .align 2,144
+
+.globl _reg_u_mul
+_reg_u_mul:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi
+ movl PARAM2,%edi
+
+#ifdef PARANOID
+ testl $0x80000000,SIGH(%esi)
+ jz L_bugged
+ testl $0x80000000,SIGH(%edi)
+ jz L_bugged
+#endif PARANOID
+
+#ifdef DENORM_OPERAND
+ movl EXP(%esi),%eax
+ cmpl EXP_UNDER,%eax
+ jg xOp1_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp1_not_denorm:
+ movl EXP(%edi),%eax
+ cmpl EXP_UNDER,%eax
+ jg xOp2_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp2_not_denorm:
+#endif DENORM_OPERAND
+
+ xorl %ecx,%ecx
+ xorl %ebx,%ebx
+
+ movl SIGL(%esi),%eax
+ mull SIGL(%edi)
+ movl %eax,accum_0
+ movl %edx,accum_1
+
+ movl SIGL(%esi),%eax
+ mull SIGH(%edi)
+ addl %eax,accum_1
+ adcl %edx,%ebx
+/* adcl $0,%ecx *//* overflow here is not possible */
+
+ movl SIGH(%esi),%eax
+ mull SIGL(%edi)
+ addl %eax,accum_1
+ adcl %edx,%ebx
+ adcl $0,%ecx
+
+ movl SIGH(%esi),%eax
+ mull SIGH(%edi)
+ addl %eax,%ebx
+ adcl %edx,%ecx
+
+ movl EXP(%esi),%eax /* Compute the exponent */
+ addl EXP(%edi),%eax
+ subl EXP_BIAS-1,%eax
+/* Have now finished with the sources */
+ movl PARAM3,%edi /* Point to the destination */
+ movl %eax,EXP(%edi)
+
+/* Now make sure that the result is normalized */
+ testl $0x80000000,%ecx
+ jnz LResult_Normalised
+
+ /* Normalize by shifting left one bit */
+ shll $1,accum_0
+ rcll $1,accum_1
+ rcll $1,%ebx
+ rcll $1,%ecx
+ decl EXP(%edi)
+
+LResult_Normalised:
+ movl accum_0,%eax
+ movl accum_1,%edx
+ orl %eax,%eax
+ jz L_extent_zero
+
+ orl $1,%edx
+
+L_extent_zero:
+ movl %ecx,%eax
+ jmp FPU_round
+
+
+#ifdef PARANOID
+L_bugged:
+ pushl EX_INTERNAL|0x205
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
+#endif PARANOID
+
diff --git a/sys/gnu/i386/fpemul/reg_u_sub.s b/sys/gnu/i386/fpemul/reg_u_sub.s
new file mode 100644
index 0000000..3161e19
--- /dev/null
+++ b/sys/gnu/i386/fpemul/reg_u_sub.s
@@ -0,0 +1,361 @@
+ .file "reg_u_sub.S"
+/*
+ * reg_u_sub.S
+ *
+ * Core floating point subtraction routine.
+ *
+ * Call from C as:
+ * void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
+ * int control_w)
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_u_sub.s,v 1.2 1994/04/29 21:34:26 gclarkii Exp $
+ *
+ */
+
+/*
+ | Kernel subtraction routine reg_u_sub(reg *arg1, reg *arg2, reg *answ).
+ | Takes two valid reg f.p. numbers (TW_Valid), which are
+ | treated as unsigned numbers,
+ | and returns their difference as a TW_Valid or TW_Zero f.p.
+ | number.
+ | The first number (arg1) must be the larger.
+ | The returned number is normalized.
+ | Basic checks are performed if PARANOID is defined.
+ */
+
+#include "exception.h"
+#include "fpu_asm.h"
+#include "control_w.h"
+
+.text
+ .align 2,144
+.globl _reg_u_sub
+_reg_u_sub:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi /* source 1 */
+ movl PARAM2,%edi /* source 2 */
+
+#ifdef DENORM_OPERAND
+ cmpl EXP_UNDER,EXP(%esi)
+ jg xOp1_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp1_not_denorm:
+ cmpl EXP_UNDER,EXP(%edi)
+ jg xOp2_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp2_not_denorm:
+#endif DENORM_OPERAND
+
+/* xorl %ecx,%ecx */
+ movl EXP(%esi),%ecx
+ subl EXP(%edi),%ecx /* exp1 - exp2 */
+
+#ifdef PARANOID
+ /* source 2 is always smaller than source 1 */
+/* jc L_bugged */
+ js L_bugged_1
+
+ testl $0x80000000,SIGH(%edi) /* The args are assumed to be be normalized */
+ je L_bugged_2
+
+ testl $0x80000000,SIGH(%esi)
+ je L_bugged_2
+#endif PARANOID
+
+/*--------------------------------------+
+ | Form a register holding the |
+ | smaller number |
+ +--------------------------------------*/
+ movl SIGH(%edi),%eax /* register ms word */
+ movl SIGL(%edi),%ebx /* register ls word */
+
+ movl PARAM3,%edi /* destination */
+ movl EXP(%esi),%edx
+ movl %edx,EXP(%edi) /* Copy exponent to destination */
+ movb SIGN(%esi),%dl
+ movb %dl,SIGN(%edi) /* Copy the sign from the first arg */
+
+ xorl %edx,%edx /* register extension */
+
+/*--------------------------------------+
+ | Shift the temporary register |
+ | right the required number of |
+ | places. |
+ +--------------------------------------*/
+L_shift_r:
+ cmpl $32,%ecx /* shrd only works for 0..31 bits */
+ jnc L_more_than_31
+
+/* less than 32 bits */
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ jmp L_shift_done
+
+L_more_than_31:
+ cmpl $64,%ecx
+ jnc L_more_than_63
+
+ subb $32,%cl
+ jz L_exactly_32
+
+ shrd %cl,%eax,%edx
+ shr %cl,%eax
+ orl %ebx,%ebx
+ jz L_more_31_no_low /* none of the lowest bits is set */
+
+ orl $1,%edx /* record the fact in the extension */
+
+L_more_31_no_low:
+ movl %eax,%ebx
+ xorl %eax,%eax
+ jmp L_shift_done
+
+L_exactly_32:
+ movl %ebx,%edx
+ movl %eax,%ebx
+ xorl %eax,%eax
+ jmp L_shift_done
+
+L_more_than_63:
+ cmpw $65,%cx
+ jnc L_more_than_64
+
+ /* Shift right by 64 bits */
+ movl %eax,%edx
+ orl %ebx,%ebx
+ jz L_more_63_no_low
+
+ orl $1,%edx
+ jmp L_more_63_no_low
+
+L_more_than_64:
+ jne L_more_than_65
+
+ /* Shift right by 65 bits */
+ /* Carry is clear if we get here */
+ movl %eax,%edx
+ rcrl %edx
+ jnc L_shift_65_nc
+
+ orl $1,%edx
+ jmp L_more_63_no_low
+
+L_shift_65_nc:
+ orl %ebx,%ebx
+ jz L_more_63_no_low
+
+ orl $1,%edx
+ jmp L_more_63_no_low
+
+L_more_than_65:
+ movl $1,%edx /* The shifted nr always at least one '1' */
+
+L_more_63_no_low:
+ xorl %ebx,%ebx
+ xorl %eax,%eax
+
+L_shift_done:
+L_subtr:
+/*------------------------------+
+ | Do the subtraction |
+ +------------------------------*/
+ xorl %ecx,%ecx
+ subl %edx,%ecx
+ movl %ecx,%edx
+ movl SIGL(%esi),%ecx
+ sbbl %ebx,%ecx
+ movl %ecx,%ebx
+ movl SIGH(%esi),%ecx
+ sbbl %eax,%ecx
+ movl %ecx,%eax
+
+#ifdef PARANOID
+ /* We can never get a borrow */
+ jc L_bugged
+#endif PARANOID
+
+/*--------------------------------------+
+ | Normalize the result |
+ +--------------------------------------*/
+ testl $0x80000000,%eax
+ jnz L_round /* no shifting needed */
+
+ orl %eax,%eax
+ jnz L_shift_1 /* shift left 1 - 31 bits */
+
+ orl %ebx,%ebx
+ jnz L_shift_32 /* shift left 32 - 63 bits */
+
+/* A rare case, the only one which is non-zero if we got here
+// is: 1000000 .... 0000
+// -0111111 .... 1111 1
+// --------------------
+// 0000000 .... 0000 1 */
+
+ cmpl $0x80000000,%edx
+ jnz L_must_be_zero
+
+ /* Shift left 64 bits */
+ subl $64,EXP(%edi)
+ movl %edx,%eax
+ jmp L_store
+
+L_must_be_zero:
+#ifdef PARANOID
+ orl %edx,%edx
+ jnz L_bugged_3
+#endif PARANOID
+
+ /* The result is zero */
+ movb TW_Zero,TAG(%edi)
+ movl $0,EXP(%edi) /* exponent */
+ movl $0,SIGL(%edi)
+ movl $0,SIGH(%edi)
+ jmp L_exit /* Does not underflow */
+
+L_shift_32:
+ movl %ebx,%eax
+ movl %edx,%ebx
+ movl $0,%edx
+ subl $32,EXP(%edi) /* Can get underflow here */
+
+/* We need to shift left by 1 - 31 bits */
+L_shift_1:
+ bsrl %eax,%ecx /* get the required shift in %ecx */
+ subl $31,%ecx
+ negl %ecx
+ shld %cl,%ebx,%eax
+ shld %cl,%edx,%ebx
+ shl %cl,%edx
+ subl %ecx,EXP(%edi) /* Can get underflow here */
+
+L_round:
+ jmp FPU_round /* Round the result */
+
+
+#ifdef PARANOID
+L_bugged_1:
+ pushl EX_INTERNAL|0x206
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_2:
+ pushl EX_INTERNAL|0x209
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_3:
+ pushl EX_INTERNAL|0x210
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_4:
+ pushl EX_INTERNAL|0x211
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged:
+ pushl EX_INTERNAL|0x212
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+#endif PARANOID
+
+
+L_store:
+/*------------------------------+
+ | Store the result |
+ +------------------------------*/
+ movl %eax,SIGH(%edi)
+ movl %ebx,SIGL(%edi)
+
+ movb TW_Valid,TAG(%edi) /* Set the tags to TW_Valid */
+
+ cmpl EXP_UNDER,EXP(%edi)
+ jle L_underflow
+
+L_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
+
+
+L_underflow:
+ push %edi
+ call _arith_underflow
+ pop %ebx
+ jmp L_exit
+
diff --git a/sys/gnu/i386/fpemul/status_w.h b/sys/gnu/i386/fpemul/status_w.h
new file mode 100644
index 0000000..75b87b4
--- /dev/null
+++ b/sys/gnu/i386/fpemul/status_w.h
@@ -0,0 +1,106 @@
+/*
+ * status_w.h
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: status_w.h,v 1.2 1994/04/29 21:34:27 gclarkii Exp $
+ *
+ */
+
+
+#ifndef _STATUS_H_
+#define _STATUS_H_
+
+
+#ifdef LOCORE
+#define Const__(x) $/**/x
+#else
+#define Const__(x) x
+#endif
+
+#define SW_Backward Const__(0x8000) /* backward compatibility */
+#define SW_C3 Const__(0x4000) /* condition bit 3 */
+#define SW_Top Const__(0x3800) /* top of stack */
+#define SW_Top_Shift Const__(11) /* shift for top of stack bits */
+#define SW_C2 Const__(0x0400) /* condition bit 2 */
+#define SW_C1 Const__(0x0200) /* condition bit 1 */
+#define SW_C0 Const__(0x0100) /* condition bit 0 */
+#define SW_Summary Const__(0x0080) /* exception summary */
+#define SW_Stack_Fault Const__(0x0040) /* stack fault */
+#define SW_Precision Const__(0x0020) /* loss of precision */
+#define SW_Underflow Const__(0x0010) /* underflow */
+#define SW_Overflow Const__(0x0008) /* overflow */
+#define SW_Zero_Div Const__(0x0004) /* divide by zero */
+#define SW_Denorm_Op Const__(0x0002) /* denormalized operand */
+#define SW_Invalid Const__(0x0001) /* invalid operation */
+
+#define SW_Exc_Mask Const__(0x27f) /* Status word exception bit mask */
+
+#ifndef LOCORE
+
+#define COMP_A_gt_B 1
+#define COMP_A_eq_B 2
+#define COMP_A_lt_B 3
+#define COMP_No_Comp 4
+#define COMP_Denormal 0x20
+#define COMP_NaN 0x40
+#define COMP_SNaN 0x80
+
+#define setcc(cc) ({ \
+ status_word &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \
+ status_word |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); })
+
+#endif /* LOCORE */
+
+#endif /* _STATUS_H_ */
diff --git a/sys/gnu/i386/fpemul/version.h b/sys/gnu/i386/fpemul/version.h
new file mode 100644
index 0000000..9c08aed
--- /dev/null
+++ b/sys/gnu/i386/fpemul/version.h
@@ -0,0 +1,61 @@
+/*
+ * version.h
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: version.h,v 1.2 1994/04/29 21:34:28 gclarkii Exp $
+ *
+ */
+
+#define FPU_VERSION "wm-FPU-emu version BETA 1.4"
diff --git a/sys/gnu/i386/fpemul/wm_shrx.s b/sys/gnu/i386/fpemul/wm_shrx.s
new file mode 100644
index 0000000..9de9464
--- /dev/null
+++ b/sys/gnu/i386/fpemul/wm_shrx.s
@@ -0,0 +1,261 @@
+ .file "wm_shrx.S"
+/*
+ * wm_shrx.S
+ *
+ * 64 bit right shift functions
+ *
+ * Call from C as:
+ * unsigned shrx(void *arg1, unsigned arg2)
+ * and
+ * unsigned shrxs(void *arg1, unsigned arg2)
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: wm_shrx.s,v 1.2 1994/04/29 21:34:29 gclarkii Exp $
+ *
+ */
+
+
+#include "fpu_asm.h"
+
+.text
+ .align 2,144
+
+/*---------------------------------------------------------------------------+
+ | unsigned shrx(void *arg1, unsigned arg2) |
+ | |
+ | Extended shift right function. |
+ | Fastest for small shifts. |
+ | Shifts the 64 bit quantity pointed to by the first arg (arg1) |
+ | right by the number of bits specified by the second arg (arg2). |
+ | Forms a 96 bit quantity from the 64 bit arg and eax: |
+ | [ 64 bit arg ][ eax ] |
+ | shift right ---------> |
+ | The eax register is initialized to 0 before the shifting. |
+ | Results returned in the 64 bit arg and eax. |
+ +---------------------------------------------------------------------------*/
+
+ .globl _shrx
+
+_shrx:
+ push %ebp
+ movl %esp,%ebp
+ pushl %esi
+ movl PARAM2,%ecx
+ movl PARAM1,%esi
+ cmpl $32,%ecx /* shrd only works for 0..31 bits */
+ jnc L_more_than_31
+
+/* less than 32 bits */
+ pushl %ebx
+ movl (%esi),%ebx /* lsl */
+ movl 4(%esi),%edx /* msl */
+ xorl %eax,%eax /* extension */
+ shrd %cl,%ebx,%eax
+ shrd %cl,%edx,%ebx
+ shr %cl,%edx
+ movl %ebx,(%esi)
+ movl %edx,4(%esi)
+ popl %ebx
+ popl %esi
+ leave
+ ret
+
+L_more_than_31:
+ cmpl $64,%ecx
+ jnc L_more_than_63
+
+ subb $32,%cl
+ movl (%esi),%eax /* lsl */
+ movl 4(%esi),%edx /* msl */
+ shrd %cl,%edx,%eax
+ shr %cl,%edx
+ movl %edx,(%esi)
+ movl $0,4(%esi)
+ popl %esi
+ leave
+ ret
+
+L_more_than_63:
+ cmpl $96,%ecx
+ jnc L_more_than_95
+
+ subb $64,%cl
+ movl 4(%esi),%eax /* msl */
+ shr %cl,%eax
+ xorl %edx,%edx
+ movl %edx,(%esi)
+ movl %edx,4(%esi)
+ popl %esi
+ leave
+ ret
+
+L_more_than_95:
+ xorl %eax,%eax
+ movl %eax,(%esi)
+ movl %eax,4(%esi)
+ popl %esi
+ leave
+ ret
+
+
+/*---------------------------------------------------------------------------+
+ | unsigned shrxs(void *arg1, unsigned arg2) |
+ | |
+ | Extended shift right function (optimized for small floating point |
+ | integers). |
+ | Shifts the 64 bit quantity pointed to by the first arg (arg1) |
+ | right by the number of bits specified by the second arg (arg2). |
+ | Forms a 96 bit quantity from the 64 bit arg and eax: |
+ | [ 64 bit arg ][ eax ] |
+ | shift right ---------> |
+ | The eax register is initialized to 0 before the shifting. |
+ | The lower 8 bits of eax are lost and replaced by a flag which is |
+ | set (to 0x01) if any bit, apart from the first one, is set in the |
+ | part which has been shifted out of the arg. |
+ | Results returned in the 64 bit arg and eax. |
+ +---------------------------------------------------------------------------*/
+ .globl _shrxs
+_shrxs:
+ push %ebp
+ movl %esp,%ebp
+ pushl %esi
+ pushl %ebx
+ movl PARAM2,%ecx
+ movl PARAM1,%esi
+ cmpl $64,%ecx /* shrd only works for 0..31 bits */
+ jnc Ls_more_than_63
+
+ cmpl $32,%ecx /* shrd only works for 0..31 bits */
+ jc Ls_less_than_32
+
+/* We got here without jumps by assuming that the most common requirement
+ is for small integers */
+/* Shift by [32..63] bits */
+ subb $32,%cl
+ movl (%esi),%eax /* lsl */
+ movl 4(%esi),%edx /* msl */
+ xorl %ebx,%ebx
+ shrd %cl,%eax,%ebx
+ shrd %cl,%edx,%eax
+ shr %cl,%edx
+ orl %ebx,%ebx /* test these 32 bits */
+ setne %bl
+ test $0x7fffffff,%eax /* and 31 bits here */
+ setne %bh
+ orw %bx,%bx /* Any of the 63 bit set ? */
+ setne %al
+ movl %edx,(%esi)
+ movl $0,4(%esi)
+ popl %ebx
+ popl %esi
+ leave
+ ret
+
+/* Shift by [0..31] bits */
+Ls_less_than_32:
+ movl (%esi),%ebx /* lsl */
+ movl 4(%esi),%edx /* msl */
+ xorl %eax,%eax /* extension */
+ shrd %cl,%ebx,%eax
+ shrd %cl,%edx,%ebx
+ shr %cl,%edx
+ test $0x7fffffff,%eax /* only need to look at eax here */
+ setne %al
+ movl %ebx,(%esi)
+ movl %edx,4(%esi)
+ popl %ebx
+ popl %esi
+ leave
+ ret
+
+/* Shift by [64..95] bits */
+Ls_more_than_63:
+ cmpl $96,%ecx
+ jnc Ls_more_than_95
+
+ subb $64,%cl
+ movl (%esi),%ebx /* lsl */
+ movl 4(%esi),%eax /* msl */
+ xorl %edx,%edx /* extension */
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ orl %ebx,%edx
+ setne %bl
+ test $0x7fffffff,%eax /* only need to look at eax here */
+ setne %bh
+ orw %bx,%bx
+ setne %al
+ xorl %edx,%edx
+ movl %edx,(%esi) /* set to zero */
+ movl %edx,4(%esi) /* set to zero */
+ popl %ebx
+ popl %esi
+ leave
+ ret
+
+Ls_more_than_95:
+/* Shift by [96..inf) bits */
+ xorl %eax,%eax
+ movl (%esi),%ebx
+ orl 4(%esi),%ebx
+ setne %al
+ xorl %ebx,%ebx
+ movl %ebx,(%esi)
+ movl %ebx,4(%esi)
+ popl %ebx
+ popl %esi
+ leave
+ ret
diff --git a/sys/gnu/i386/fpemul/wm_sqrt.s b/sys/gnu/i386/fpemul/wm_sqrt.s
new file mode 100644
index 0000000..b1bc03b
--- /dev/null
+++ b/sys/gnu/i386/fpemul/wm_sqrt.s
@@ -0,0 +1,496 @@
+ .file "wm_sqrt.S"
+/*
+ * wm_sqrt.S
+ *
+ * Fixed point arithmetic square root evaluation.
+ *
+ * Call from C as:
+ * void wm_sqrt(FPU_REG *n, unsigned int control_word)
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: wm_sqrt.s,v 1.2 1994/04/29 21:34:30 gclarkii Exp $
+ *
+ */
+
+
+/*---------------------------------------------------------------------------+
+ | wm_sqrt(FPU_REG *n, unsigned int control_word) |
+ | returns the square root of n in n. |
+ | |
+ | Use Newton's method to compute the square root of a number, which must |
+ | be in the range [1.0 .. 4.0), to 64 bits accuracy. |
+ | Does not check the sign or tag of the argument. |
+ | Sets the exponent, but not the sign or tag of the result. |
+ | |
+ | The guess is kept in %esi:%edi |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "fpu_asm.h"
+
+
+.data
+/*
+ Local storage:
+ */
+ .align 4,0
+accum_3:
+ .long 0 /* ms word */
+accum_2:
+ .long 0
+accum_1:
+ .long 0
+accum_0:
+ .long 0
+
+/* The de-normalised argument:
+// sq_2 sq_1 sq_0
+// b b b b b b b ... b b b b b b .... b b b b 0 0 0 ... 0
+// ^ binary point here */
+fsqrt_arg_2:
+ .long 0 /* ms word */
+fsqrt_arg_1:
+ .long 0
+fsqrt_arg_0:
+ .long 0 /* ls word, at most the ms bit is set */
+
+.text
+ .align 2,144
+
+.globl _wm_sqrt
+
+_wm_sqrt:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi
+
+ movl SIGH(%esi),%eax
+ movl SIGL(%esi),%ecx
+ xorl %edx,%edx
+
+/* We use a rough linear estimate for the first guess.. */
+
+ cmpl EXP_BIAS,EXP(%esi)
+ jnz sqrt_arg_ge_2
+
+ shrl $1,%eax /* arg is in the range [1.0 .. 2.0) */
+ rcrl $1,%ecx
+ rcrl $1,%edx
+
+sqrt_arg_ge_2:
+/* From here on, n is never accessed directly again until it is
+// replaced by the answer. */
+
+ movl %eax,fsqrt_arg_2 /* ms word of n */
+ movl %ecx,fsqrt_arg_1
+ movl %edx,fsqrt_arg_0
+
+/* Make a linear first estimate */
+ shrl $1,%eax
+ addl $0x40000000,%eax
+ movl $0xaaaaaaaa,%ecx
+ mull %ecx
+ shll %edx /* max result was 7fff... */
+ testl $0x80000000,%edx /* but min was 3fff... */
+ jnz sqrt_prelim_no_adjust
+
+ movl $0x80000000,%edx /* round up */
+
+sqrt_prelim_no_adjust:
+ movl %edx,%esi /* Our first guess */
+
+/* We have now computed (approx) (2 + x) / 3, which forms the basis
+ for a few iterations of Newton's method */
+
+ movl fsqrt_arg_2,%ecx /* ms word */
+
+/* From our initial estimate, three iterations are enough to get us
+// to 30 bits or so. This will then allow two iterations at better
+// precision to complete the process.
+
+// Compute (g + n/g)/2 at each iteration (g is the guess). */
+ shrl %ecx /* Doing this first will prevent a divide */
+ /* overflow later. */
+
+ movl %ecx,%edx /* msw of the arg / 2 */
+ divl %esi /* current estimate */
+ shrl %esi /* divide by 2 */
+ addl %eax,%esi /* the new estimate */
+
+ movl %ecx,%edx
+ divl %esi
+ shrl %esi
+ addl %eax,%esi
+
+ movl %ecx,%edx
+ divl %esi
+ shrl %esi
+ addl %eax,%esi
+
+/* Now that an estimate accurate to about 30 bits has been obtained (in %esi),
+// we improve it to 60 bits or so.
+
+// The strategy from now on is to compute new estimates from
+// guess := guess + (n - guess^2) / (2 * guess) */
+
+/* First, find the square of the guess */
+ movl %esi,%eax
+ mull %esi
+/* guess^2 now in %edx:%eax */
+
+ movl fsqrt_arg_1,%ecx
+ subl %ecx,%eax
+ movl fsqrt_arg_2,%ecx /* ms word of normalized n */
+ sbbl %ecx,%edx
+ jnc sqrt_stage_2_positive
+/* subtraction gives a negative result
+// negate the result before division */
+ notl %edx
+ notl %eax
+ addl $1,%eax
+ adcl $0,%edx
+
+ divl %esi
+ movl %eax,%ecx
+
+ movl %edx,%eax
+ divl %esi
+ jmp sqrt_stage_2_finish
+
+sqrt_stage_2_positive:
+ divl %esi
+ movl %eax,%ecx
+
+ movl %edx,%eax
+ divl %esi
+
+ notl %ecx
+ notl %eax
+ addl $1,%eax
+ adcl $0,%ecx
+
+sqrt_stage_2_finish:
+ sarl $1,%ecx /* divide by 2 */
+ rcrl $1,%eax
+
+ /* Form the new estimate in %esi:%edi */
+ movl %eax,%edi
+ addl %ecx,%esi
+
+ jnz sqrt_stage_2_done /* result should be [1..2) */
+
+#ifdef PARANOID
+/* It should be possible to get here only if the arg is ffff....ffff*/
+ cmp $0xffffffff,fsqrt_arg_1
+ jnz sqrt_stage_2_error
+#endif PARANOID
+
+/* The best rounded result.*/
+ xorl %eax,%eax
+ decl %eax
+ movl %eax,%edi
+ movl %eax,%esi
+ movl $0x7fffffff,%eax
+ jmp sqrt_round_result
+
+#ifdef PARANOID
+sqrt_stage_2_error:
+ pushl EX_INTERNAL|0x213
+ call EXCEPTION
+#endif PARANOID
+
+sqrt_stage_2_done:
+
+/* Now the square root has been computed to better than 60 bits */
+
+/* Find the square of the guess*/
+ movl %edi,%eax /* ls word of guess*/
+ mull %edi
+ movl %edx,accum_1
+
+ movl %esi,%eax
+ mull %esi
+ movl %edx,accum_3
+ movl %eax,accum_2
+
+ movl %edi,%eax
+ mull %esi
+ addl %eax,accum_1
+ adcl %edx,accum_2
+ adcl $0,accum_3
+
+/* movl %esi,%eax*/
+/* mull %edi*/
+ addl %eax,accum_1
+ adcl %edx,accum_2
+ adcl $0,accum_3
+
+/* guess^2 now in accum_3:accum_2:accum_1*/
+
+ movl fsqrt_arg_0,%eax /* get normalized n*/
+ subl %eax,accum_1
+ movl fsqrt_arg_1,%eax
+ sbbl %eax,accum_2
+ movl fsqrt_arg_2,%eax /* ms word of normalized n*/
+ sbbl %eax,accum_3
+ jnc sqrt_stage_3_positive
+
+/* subtraction gives a negative result*/
+/* negate the result before division */
+ notl accum_1
+ notl accum_2
+ notl accum_3
+ addl $1,accum_1
+ adcl $0,accum_2
+
+#ifdef PARANOID
+ adcl $0,accum_3 /* This must be zero */
+ jz sqrt_stage_3_no_error
+
+sqrt_stage_3_error:
+ pushl EX_INTERNAL|0x207
+ call EXCEPTION
+
+sqrt_stage_3_no_error:
+#endif PARANOID
+
+ movl accum_2,%edx
+ movl accum_1,%eax
+ divl %esi
+ movl %eax,%ecx
+
+ movl %edx,%eax
+ divl %esi
+
+ sarl $1,%ecx / divide by 2*/
+ rcrl $1,%eax
+
+ /* prepare to round the result*/
+
+ addl %ecx,%edi
+ adcl $0,%esi
+
+ jmp sqrt_stage_3_finished
+
+sqrt_stage_3_positive:
+ movl accum_2,%edx
+ movl accum_1,%eax
+ divl %esi
+ movl %eax,%ecx
+
+ movl %edx,%eax
+ divl %esi
+
+ sarl $1,%ecx /* divide by 2*/
+ rcrl $1,%eax
+
+ /* prepare to round the result*/
+
+ notl %eax /* Negate the correction term*/
+ notl %ecx
+ addl $1,%eax
+ adcl $0,%ecx /* carry here ==> correction == 0*/
+ adcl $0xffffffff,%esi
+
+ addl %ecx,%edi
+ adcl $0,%esi
+
+sqrt_stage_3_finished:
+
+/* The result in %esi:%edi:%esi should be good to about 90 bits here,
+// and the rounding information here does not have sufficient accuracy
+// in a few rare cases. */
+ cmpl $0xffffffe0,%eax
+ ja sqrt_near_exact_x
+
+ cmpl $0x00000020,%eax
+ jb sqrt_near_exact
+
+ cmpl $0x7fffffe0,%eax
+ jb sqrt_round_result
+
+ cmpl $0x80000020,%eax
+ jb sqrt_get_more_precision
+
+sqrt_round_result:
+/* Set up for rounding operations*/
+ movl %eax,%edx
+ movl %esi,%eax
+ movl %edi,%ebx
+ movl PARAM1,%edi
+ movl EXP_BIAS,EXP(%edi) /* Result is in [1.0 .. 2.0)*/
+ movl PARAM2,%ecx
+ jmp FPU_round_sqrt
+
+
+sqrt_near_exact_x:
+/* First, the estimate must be rounded up.*/
+ addl $1,%edi
+ adcl $0,%esi
+
+sqrt_near_exact:
+/* This is an easy case because x^1/2 is monotonic.
+// We need just find the square of our estimate, compare it
+// with the argument, and deduce whether our estimate is
+// above, below, or exact. We use the fact that the estimate
+// is known to be accurate to about 90 bits. */
+ movl %edi,%eax /* ls word of guess*/
+ mull %edi
+ movl %edx,%ebx /* 2nd ls word of square*/
+ movl %eax,%ecx /* ls word of square*/
+
+ movl %edi,%eax
+ mull %esi
+ addl %eax,%ebx
+ addl %eax,%ebx
+
+#ifdef PARANOID
+ cmp $0xffffffb0,%ebx
+ jb sqrt_near_exact_ok
+
+ cmp $0x00000050,%ebx
+ ja sqrt_near_exact_ok
+
+ pushl EX_INTERNAL|0x214
+ call EXCEPTION
+
+sqrt_near_exact_ok:
+#endif PARANOID
+
+ or %ebx,%ebx
+ js sqrt_near_exact_small
+
+ jnz sqrt_near_exact_large
+
+ or %ebx,%edx
+ jnz sqrt_near_exact_large
+
+/* Our estimate is exactly the right answer*/
+ xorl %eax,%eax
+ jmp sqrt_round_result
+
+sqrt_near_exact_small:
+/* Our estimate is too small*/
+ movl $0x000000ff,%eax
+ jmp sqrt_round_result
+
+sqrt_near_exact_large:
+/* Our estimate is too large, we need to decrement it*/
+ subl $1,%edi
+ sbbl $0,%esi
+ movl $0xffffff00,%eax
+ jmp sqrt_round_result
+
+
+sqrt_get_more_precision:
+/* This case is almost the same as the above, except we start*/
+/* with an extra bit of precision in the estimate.*/
+ stc /* The extra bit.*/
+ rcll $1,%edi /* Shift the estimate left one bit*/
+ rcll $1,%esi
+
+ movl %edi,%eax /* ls word of guess*/
+ mull %edi
+ movl %edx,%ebx /* 2nd ls word of square*/
+ movl %eax,%ecx /* ls word of square*/
+
+ movl %edi,%eax
+ mull %esi
+ addl %eax,%ebx
+ addl %eax,%ebx
+
+/* Put our estimate back to its original value*/
+ stc /* The ms bit.*/
+ rcrl $1,%esi /* Shift the estimate left one bit*/
+ rcrl $1,%edi
+
+#ifdef PARANOID
+ cmp $0xffffff60,%ebx
+ jb sqrt_more_prec_ok
+
+ cmp $0x000000a0,%ebx
+ ja sqrt_more_prec_ok
+
+ pushl EX_INTERNAL|0x215
+ call EXCEPTION
+
+sqrt_more_prec_ok:
+#endif PARANOID
+
+ or %ebx,%ebx
+ js sqrt_more_prec_small
+
+ jnz sqrt_more_prec_large
+
+ or %ebx,%ecx
+ jnz sqrt_more_prec_large
+
+/* Our estimate is exactly the right answer*/
+ movl $0x80000000,%eax
+ jmp sqrt_round_result
+
+sqrt_more_prec_small:
+/* Our estimate is too small*/
+ movl $0x800000ff,%eax
+ jmp sqrt_round_result
+
+sqrt_more_prec_large:
+/* Our estimate is too large*/
+ movl $0x7fffff00,%eax
+ jmp sqrt_round_result
diff --git a/sys/i386/Makefile b/sys/i386/Makefile
new file mode 100644
index 0000000..4ad5a34
--- /dev/null
+++ b/sys/i386/Makefile
@@ -0,0 +1,25 @@
+# from: @(#)Makefile 7.3 (Berkeley) 6/9/91
+# $Id$
+
+# Makefile for i386 tags file
+
+all:
+ @echo "make tags or links only"
+
+TI386= ../i386/tags
+SI386= ../i386/i386/*.[ch] ../i386/include/*.h ../i386/isa/*.[ch]
+AI386= ../i386/i386/*.s
+
+# Directories in which to place i386 tags links
+DI386= eisa isa mca include
+
+tags:
+ -ctags -dtf ${TI386} ${COMM} ${SI386}
+ egrep "^ENTRY(.*)|^ALTENTRY(.*)" ${AI386} | \
+ sed "s;\([^:]*\):\([^(]*\)(\([^, )]*\)\(.*\);\3 \1 /^\2(\3\4$$/;" \
+ >> ${TI386}
+ sort -o ${TI386} ${TI386}
+
+links:
+ -for i in ${DI386}; do \
+ cd $$i && rm -f tags; ln -s ../tags tags; done
diff --git a/sys/i386/boot/Makefile b/sys/i386/boot/Makefile
new file mode 100644
index 0000000..568c747
--- /dev/null
+++ b/sys/i386/boot/Makefile
@@ -0,0 +1,106 @@
+#
+# Permission to use, copy, modify and distribute this software and its
+# documentation is hereby granted, provided that both the copyright
+# notice and this permission notice appear in all copies of the
+# software, derivative works or modified versions, and any portions
+# thereof, and that both notices appear in supporting documentation.
+#
+# CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+# CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+# ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+#
+# Carnegie Mellon requests users of this software to return to
+#
+# Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+# School of Computer Science
+# Carnegie Mellon University
+# Pittsburgh PA 15213-3890
+#
+# any improvements or extensions that they make and grant Carnegie Mellon
+# the rights to redistribute these changes.
+#
+# from: Mach, Revision 2.2 92/04/04 11:33:46 rpd
+# $Id: Makefile,v 1.9 1994/06/16 03:53:24 adam Exp $
+#
+
+wd0:
+ dd if=boot of=biosboot count=1
+ dd if=boot of=bootbios skip=1
+ disklabel -r -w wd0 nec5655 newboot biosboot bootbios
+ rm biosboot bootbios
+
+NOPROG= noprog
+NOMAN= noman
+
+# tunable loopcount parameter, waiting for keypress
+BOOTWAIT?= 2400
+
+CFLAGS = -O2 -DDO_BAD144 -DBOOTWAIT=${BOOTWAIT} -I${.CURDIR}
+LIBS= -lc
+INC= -I${.CURDIR}/../..
+
+# start.o should be first
+OBJS = start.o table.o boot2.o boot.o asm.o bios.o io.o disk.o sys.o
+
+.SUFFIXES: .S .c .o
+
+.c.o:
+ $(CC) $(CFLAGS) $(INC) -c $<
+
+.S.o:
+ $(CC) $(CFLAGS) -c $<
+
+boot: $(OBJS)
+ $(LD) -Bstatic -N -T 0 -o boot $(OBJS) $(LIBS)
+ cp boot boot.sym
+ @strip boot
+ @sh ${.CURDIR}/rmaouthdr boot boot.tmp
+ @mv -f boot.tmp boot
+ @ls -l boot
+
+biosboot: boot
+ dd if=boot of=biosboot count=1
+
+bootbios: boot
+ dd if=boot of=bootbios skip=1
+
+${DESTDIR}/usr/mdec/bootsd: bootbios
+ cp bootbios ${DESTDIR}/usr/mdec/bootsd
+
+${DESTDIR}/usr/mdec/sdboot: biosboot
+ cp biosboot ${DESTDIR}/usr/mdec/sdboot
+
+${DESTDIR}/usr/mdec/bootwd: ${DESTDIR}/usr/mdec/bootsd
+ rm -f ${DESTDIR}/usr/mdec/bootwd
+ ln ${DESTDIR}/usr/mdec/bootsd ${DESTDIR}/usr/mdec/bootwd
+
+${DESTDIR}/usr/mdec/wdboot: ${DESTDIR}/usr/mdec/sdboot
+ rm -f ${DESTDIR}/usr/mdec/wdboot
+ ln ${DESTDIR}/usr/mdec/sdboot ${DESTDIR}/usr/mdec/wdboot
+
+${DESTDIR}/usr/mdec/bootfd: ${DESTDIR}/usr/mdec/bootsd
+ rm -f ${DESTDIR}/usr/mdec/bootfd
+ ln ${DESTDIR}/usr/mdec/bootsd ${DESTDIR}/usr/mdec/bootfd
+
+${DESTDIR}/usr/mdec/fdboot: ${DESTDIR}/usr/mdec/sdboot
+ rm -f ${DESTDIR}/usr/mdec/fdboot
+ ln ${DESTDIR}/usr/mdec/sdboot ${DESTDIR}/usr/mdec/fdboot
+
+sd: ${DESTDIR}/usr/mdec/bootsd ${DESTDIR}/usr/mdec/sdboot
+wd: ${DESTDIR}/usr/mdec/bootwd ${DESTDIR}/usr/mdec/wdboot
+fd: ${DESTDIR}/usr/mdec/bootfd ${DESTDIR}/usr/mdec/fdboot
+
+all: biosboot bootbios
+
+fd0:
+ dd if=boot of=biosboot count=1
+ dd if=boot of=bootbios skip=1
+ disklabel -r -w fd0 floppy bootflpy biosboot bootbios
+ rm biosboot bootbios
+
+install: wd sd fd
+
+clean:
+ /bin/rm -f *.o *.d boot bootbios biosboot boot.sym
+
+.include <bsd.prog.mk>
diff --git a/sys/i386/boot/README.386BSD b/sys/i386/boot/README.386BSD
new file mode 100644
index 0000000..cc5cb8f
--- /dev/null
+++ b/sys/i386/boot/README.386BSD
@@ -0,0 +1,151 @@
+This Boot code is different from the original boot code that came with
+386BSD in that it uses the BIOS to load the kernel and to provide all i/o
+services. The advantage ofthis is that the same boot code exactly, can run
+on any device that is supported by the BIOS. (That's most of them)
+This is important for the 'generic scsi' project because it means we can
+write drivers for new scsi adapters without having to develop an new
+set of boot blocks for each.
+
+At this point you should read the first part of README.MACH... come back here
+when you have done that:
+
+In normal operation, when co-existing with other operating systems, the
+following operations occur:
+
+1/ the BIOS loads the first block of the disk (called the Master Boot Record
+or MBR) and if it has the correct magic numbers, jumps into it:
+
+2/ The MBR code, looks at the Partition table that is embedded within it,
+to detirmine which is the partition to boot from. If you are using the os-bs
+bootblocks (highly recommended) then it will give you a menu to choose from.
+
+3/ The MBR will load the first record of the selected partition and
+if it has (the same) magic numbers, jumps into it. In 386bsd this is the
+first stage boot, (or boot1) it is represented in /usr/mdec by
+wdboot, asboot and sdboot. If the disk has been set up without DOS partitioning
+then this block will be at block zero, and will have been loaded directly by
+the BIOS.
+
+4/ Boot1 will look at block0 (which might be itself if there are no DOS
+partitions) and will find the 386bsd partition, and using the information
+regarding the start position of that partition, will load the next 13 sectors
+or so, to around 90000 (640k - 64k). and will jump into it at the appropriate
+entry point. Since boot1 and boot2 were compiled together as one file
+and then split later, boot1 knows the exact position within boot2 of the
+entry point.
+
+Boot 1 also contains a compiled in DOS partition table
+(in case it is at block 0), which contains a 386bsd partition starting
+at 0. This ensures that the same code can work whether or not
+boot1 is at block 0.
+
+5/ Boot2 asks the user for a boot device, partition and filename, and then
+loads the MBR of the selected device. This may or may not be the device
+which was originally used to boot the first MBR. The partition table
+of the new MBR is searched for a 386bsd partition, and if one is found,
+that is then in turn searched for the disklabel. This could all be on the
+second disk at this point, if the user selected it.
+
+6/On finding the disklabel, boot2 can find the correct unix partition
+within the 386bsd partition, and using cutdown filesystem code,
+look for the file to boot (e.g. 386bsd).
+
+7/ Boot2 loads this file starting at the location specified by the a.out header,
+(see later) and leaps into it at the location specified in he header.
+
+if the file does not exist or cannot be loaded, boot2 goes back to step 5.
+
+386bsd is now running and will hopefully start vm etc. and get to multi-user
+mode.
+
+##########################################################################
+During all these steps, all i/o is performed using the BIOS. This has a number
+of side effects:
+
+1/ Since BIOS disk calls are specified in terms of cylinder,head and sector,
+and the BIOS read the disk information from either the CMOS or from some other
+location which is un-available to us, we must use the cyl,head,sec information
+that is given in the MBR, rather than the start address in the MBR, because
+we cannot guarentee that we can corectly calculate C,H,S from the start address.
+
+Therefore, the C,H,S information in the MBR must be as correct for this boot
+to work as it would be for DOS to boot. For example, adaptec BIOS routines
+assume a layout of 64 heads and 32 sectors giving 1MB per ficticious cylinder.
+You must use these figures to calculate the correct values. Luckily, the DOS
+fdisk program will do all this for you if you tell it to give you a DOS
+partition, and you can change it to a 386BSD partition later. If you use
+no DOS partitioning, then the compiled in table in Boot1 will do just fine.
+
+If you want to do it by hand remember that BIOS counts sectors starting at 1.
+(cylinders and heads start at 0 (??))
+
+2/ you cannot overwrite the bottom 4k of ram until you have finished ALL
+bios calls, as BIOS uses this area as scratch memory.
+
+3/ Since BIOS runs in REAL mode, and Boot2 runs in protected mode,
+Boot 2 switches back to real mode just before each BIOS call and then
+back to protected mode on each return. Touch this at your peril.!
+
+#########################################################################
+In answering the prompt from Boot2:
+you can,
+1/ leave it alone.. it will boot the indicated file from the first
+partition of the first drive seen by the BIOS (C:)
+
+2/ enter only "-s" to boot the default to single user mode
+
+3/ enter only a filename (optionally with -s) to boot that kernel,
+
+4/ enter a whole line of the form shown in the prompt. This allows you to
+boot some other partition, possibly on the second drive, as root.
+
+
+##########################################################################
+In the case you have two drives the same type (both scsi or bith IDE/ESDI),
+wd(0,a)xxx
+ will boot xxx from drive 0, a partition.
+wd(1,a)xxx
+ will boot xxx from drive 1, a partition.
+
+similarly for sd.
+
+if you have one wd drive and one scsi drive, then you MUST
+use device 'hd'
+
+otherwise the following will happen:
+
+with wd0 and sd0, you specify sd1 or wd1 to indicate the 2nd drive.
+it boots the kernel correctly, then tells the kernel to use sd1 as root.
+you however may not have an sd1, and problems arise.
+
+hd is special in that the kernel is always told to use unit 0,
+The correct TYPE of device will be specified too, so the kernel
+will be told either sd0 or wd0.
+
+Whether sd or wd is specified to the kernel is read from the disklabel,
+so ensure that all SCSI disks have type SCSI in their disklabel or the
+boot code will assume they are ESDI or IDE. (Remember, because it is
+working through the BIOS it has ho idea what kind of disk it is.
+
+##########################################################################
+Installing:
+The makefile supplied has a target install which will create the
+files wdboot,bootwd ,sdboot and bootsd in /usr/mdec.
+BEWARE these will overwrite the existing wdboot and bootwd. (so back
+them up)
+
+there are also targets wd and sd which wil only do one of them
+
+The commented out targets wd0 and sd0 are examples of how to
+load the new bootblocks, however,make sure you change the
+device type and label to suit your drive if you uncomment them.
+(see 'man disklabel')
+
+If you already have made partitions using the old bootblocks
+these should install painlessly.
+
+Before you do this ensure you have a booting floppy with correct
+disktab and bootblock files on it so that if it doesn't work, you can
+re-disklabel from the floppy.
+
+$Id$
diff --git a/sys/i386/boot/README.MACH b/sys/i386/boot/README.MACH
new file mode 100644
index 0000000..cb62329
--- /dev/null
+++ b/sys/i386/boot/README.MACH
@@ -0,0 +1,210 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:33:55 rpd
+ * $Id$
+ */
+
+********NOTE: This is not all relevant to the 386BSD version**********
+
+ AT386 Protected Mode Bootstrap Loader
+ =====================================
+
+1. Overview of Startup
+ -------------------
+
+ After the system is rebooted, the BIOS bootstrap routine reads Sector
+ 1, Track 0 into memory at location 0000:7C00H. If location 0000:7DFEH
+ (last two bytes of that sector) contains the value AA55H, the BIOS
+ bootstrap routine will transfer control to 0000:7C00H. Otherwise, the
+ boot code in that sector is bad and the boot routine stops.
+
+ For DOS compatibility reason, one extra stage of boot is required if
+ the boot device is a hard disk. The first sector of the hard disk will
+ contain the MOS-DOS boot code and a boot record partition table.
+ When this sector is loaded into 0000:7C00H, it will relocate itself
+ to somewhere else and then load the first sector of the active
+ partition into 0000:7C00H. Both UNIX and DOS use the command "fdisk"
+[ 386bsd does not have an 'fdisk' (yet) ]
+ to install this first sector into the hard disk and to manipulate
+ the hard disk partitions.
+
+
+
+2. The First Stage Bootstrap Loader
+ --------------------------------
+
+ After startup, the first stage boot is loaded at 0000:7C00H. This
+ first stage boot will load itself and the second stage boot into
+ memory at location 0000:1000H. For floppy disks, the first cylinder
+ is reserved as the boot cylinder, and the boot code (first and second)
+ will be loaded from there. Currently, only double sided, high density
+ (15 sectors per track) floppies are supported. For hard disks, the
+ first 29 sectors of the active partition is reserved for boot code
+ which will be loaded by the first stage boot. All the disk types
+ recognized by BIOS are supported by this bootstrap loader.
+[for 386bsd we load the second stage booter to 9000:0]
+
+
+
+3. The Second Stage Bootstrap Loader
+ --------------------------------
+
+ After the boot code is loaded, the control is passed to the second
+ stage bootstrap loader "boot2()". In order to be able to load the
+ big kernel image (bigger than 512K or 640K, depends on the memory
+ configuration), the second stage boot loader will run on the protected
+ mode. This bootstarp loader does not have any stand alone device
+ drivers, all the I/O's are through the BIOS calls. Since the first
+ stage boot code will no longer be used at this moment, the memory
+ location of the first stage boot code (0000:1000H to 0000:1200H) will
+ be used as an internal buffer for BIOS calls. Immediately after this
+ internal buffer is the GDT table for the second stage boot loader.
+ Since this boot loader needs to switch back and forth between protected
+ and real mode in order to use BIOS calls, the limit of the boot code
+ and boot data segments must not be greater than 64K.
+
+ The boot loader loads the kernel image at memory location above 1 MB
+ to skip the memory hole between 521K/640K and 1MB. After the kernel
+ is loaded, the boot loader stores the information in the stack and
+ then passes control to kernel. Currently, the three information passed
+ fromm the boot loader to the kernel are type of the boot device, size
+ of the base memory and size of the extended memory.
+
+[ 386bsd receives: howto, bootdev]
+
+[ 386bsd is loaded where-ever the "MByte" bits of the load address specify,
+so if you link it for FE100000 it will load to 1MB, but if you link
+it for FE000000 it will load ad 0MB]
+
+[for machines with only 512KB normal ram the kernel will need to be linked
+for 1MB and the bootblocks modified to run below 512KB. (8000:0)]
+
+
+4. The UNIX Startup
+ ----------------
+
+ Since the boot loader loads the kernel image at memory location above
+ 1MB, the kernel has to start as protected mode. In addition, the
+ link editor description file (vuifile) has to indicate that
+ the text and data segments start above 1MB. Also, the boot loader
+ passes the infomation to the kernel through the stack.
+
+[MOST of what is mentionned below is NOT relevant to 386bsd]
+
+5. Disk Layout and Bad Block Handling
+ ---------------------------------
+
+ The System V/386 Release 3.2 (AT) disk layout will be used as the disk
+ layout for the MACH System on the AT platform.
+
+ This disk layout is as follows:
+
+ * Reserve the first sector of cylinder 0 for the DOS boot record which
+ contains the master boot code (446 bytes) and the partition table.
+ (Refer to DOS Technical Reference Manual page 9-6 to 9-10).
+
+ * Reserve the first 29 sectors of the UNIX partition for the first
+ and the second stage bootstrap.
+
+ * Reserve the 30th sector of the UNIX partition for the pdinfo and
+ the vtoc tables.
+
+ * Reserve the 31st to the 34th sectors of the UNIX partition for the
+ bad track and the bad block mapping tables.
+
+ * Reserve up to 253 consecutive tracks when required, beginning with
+ the 35th sector of the UNIX partition, for alternate tracks.
+
+ * Reserve up to 253 consecutive blocks, beginning with the first
+ sector after the alternate tracks area, for alternate blocks.
+
+ SEC
+ 1
+ ----------------------------------------------------
+ | X | | CYL 0, TRK 0
+ ---------------- .......... --------------------
+ | .......... |
+ ---------------- .......... --------------------
+ | .......... |
+ ===============================================================
+ ^ | BOOTSTRAP | CYL N, TRK M
+ | ----------------------------------------------------
+ | | |30 |31 |32 |33 |34 |
+ ---------------------------------------------------- ---
+ U | .......... | ^
+ N ---------------- .......... --------------------- |
+ I | .......... | Alternate Tracks
+ X ---------------- .......... --------------------- |
+ | .......... | V
+ P ---------------------------------------------------- ---
+ A | .......... | ^
+ R ---------------- .......... --------------------- |
+ T | .......... | Alternate Blocks
+ I ---------------- .......... -------------------- |
+ T | .......... | V
+ I ---------------------------------------------------- ---
+ O | Unix root partition starts from here |
+ N ---------------- -----------------
+ | |
+ ----------------------------------------------------
+ | |
+ ----------------------------------------------------
+ | |
+ | ---------------------------------------------------
+ | | |
+ | ----------------------------------------------------
+ V | |
+ ===============================================================
+ | ........ |
+ --------------- ........ --------------
+ | ........ |
+ ----------------------------------------------------
+
+
+ The bad block handling mechanism is as follows:
+
+ * Use the alternate track in the alternate tracks area if the
+ track containing the target sector is bad.
+
+ * Use the alternate block in the alternate blocks area if the
+ target sector is bad.
+
+
+
+
+6. How to make:
+ -----------
+
+ Since the kernel image is loaded above 1 MB, the kernel must start
+ as protected mode. This means that this bootstrap loader will work
+ only when the corresponding changes on the kernel startup code are done.
+
+ The make command to generate this bootstrap loader is:
+
+ make -f boot.mk fdboot (floppy boot loader)
+ make -f boot.mk hdboot (wini boot loader)
+[to make 386bsd bootblocks "make sd wd" (warning: they will be installed
+in /dev/mdec.. take backups)]
diff --git a/sys/i386/boot/asm.S b/sys/i386/boot/asm.S
new file mode 100644
index 0000000..b291428
--- /dev/null
+++ b/sys/i386/boot/asm.S
@@ -0,0 +1,260 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:34:13 rpd
+ * $Id$
+ */
+
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+ .file "asm.s"
+
+#include "asm.h"
+
+
+CR0_PE_ON = 0x1
+CR0_PE_OFF = 0xfffffffe
+
+.globl _ouraddr
+ .text
+
+/*
+#
+# real_to_prot()
+# transfer from real mode to protected mode.
+*/
+
+ENTRY(real_to_prot)
+ # guarantee that interrupt is disabled when in prot mode
+ cli
+
+ # load the gdtr
+ addr32
+ data32
+ lgdt EXT(Gdtr)
+
+ # set the PE bit of CR0
+ mov %cr0, %eax
+
+ data32
+ or $CR0_PE_ON, %eax
+ mov %eax, %cr0
+
+ # make intrasegment jump to flush the processor pipeline and
+ # reload CS register
+ data32
+ ljmp $0x18, $xprot
+
+xprot:
+ # we are in USE32 mode now
+ # set up the protected mode segment registers : DS, SS, ES
+ mov $0x20, %eax
+ movw %ax, %ds
+ movw %ax, %ss
+ movw %ax, %es
+
+ # load idtr so we can debug
+ lidt EXT(Idtr_prot)
+
+ ret
+
+/*
+#
+# prot_to_real()
+# transfer from protected mode to real mode
+#
+*/
+
+ENTRY(prot_to_real)
+
+ # set up a dummy stack frame for the second seg change.
+ movl _ouraddr, %eax
+ sarl $4, %eax
+ pushw %ax
+ movw $xreal, %ax # gas botches pushw $xreal - extra bytes 0, 0
+ pushw %ax # decode to add %al, (%eax) (%al usually 0)
+
+ # Change to use16 mode.
+ ljmp $0x28, $x16
+
+x16:
+ # clear the PE bit of CR0
+ mov %cr0, %eax
+ data32
+ and $CR0_PE_OFF, %eax
+ mov %eax, %cr0
+
+ # make intersegment jmp to flush the processor pipeline
+ # using the fake stack frame set up earlier
+ # and reload CS register
+ lret
+
+xreal:
+ # we are in real mode now
+ # set up the real mode segment registers : DS, SS, ES
+ movw %cs, %ax
+ movw %ax, %ds
+ movw %ax, %ss
+ movw %ax, %es
+
+ # load idtr so we can debug
+ addr32
+ data32
+ lidt EXT(Idtr_real)
+
+ data32
+ ret
+
+/*
+#
+# startprog(phyaddr)
+# start the program on protected mode where phyaddr is the entry point
+#
+*/
+
+ENTRY(startprog)
+ push %ebp
+ mov %esp, %ebp
+
+ # get things we need into registers
+ movl 0x8(%ebp), %ecx # entry offset
+ movl 0x0c(%ebp), %eax # &argv
+
+ # make a new stack at 0:0xa0000 (big segs)
+ mov $0x10, %ebx
+ movw %bx, %ss
+ movl $0xa0000, %ebx
+ movl %ebx, %esp
+
+ # push some number of args onto the stack
+ pushl $0 # nominally a cyl offset in the boot.
+ pushl 0x8(%eax) # argv[2] = bootdev
+ pushl 0x4(%eax) # argv[1] = howto
+ pushl $0 # dummy 'return' address
+
+ # push on our entry address
+ mov $0x08, %ebx # segment
+ pushl %ebx
+ pushl %ecx
+
+ # convert over the other data segs
+ mov $0x10, %ebx
+ movw %bx, %ds
+ movw %bx, %es
+
+ # convert the PC (and code seg)
+ lret
+/*
+#
+# pbzero( dst, cnt)
+# where src is a virtual address and dst is a physical address
+*/
+
+ENTRY(pbzero)
+ push %ebp
+ mov %esp, %ebp
+ push %es
+ push %esi
+ push %edi
+ push %ecx
+
+ cld
+
+ # set %es to point at the flat segment
+ mov $0x10, %eax
+ movw %ax, %es
+
+ mov 0x8(%ebp), %edi # destination
+ mov 0xc(%ebp), %ecx # count
+ mov $0x0, %eax # value
+
+ rep
+ stosb
+
+ pop %ecx
+ pop %edi
+ pop %esi
+ pop %es
+ pop %ebp
+
+ ret
+/*
+#
+# pcpy(src, dst, cnt)
+# where src is a virtual address and dst is a physical address
+#
+*/
+
+ENTRY(pcpy)
+ push %ebp
+ mov %esp, %ebp
+ push %es
+ push %esi
+ push %edi
+ push %ecx
+
+ cld
+
+ # set %es to point at the flat segment
+ mov $0x10, %eax
+ movw %ax, %es
+
+ mov 0x8(%ebp), %esi # source
+ mov 0xc(%ebp), %edi # destination
+ mov 0x10(%ebp), %ecx # count
+
+ rep
+ movsb
+
+ pop %ecx
+ pop %edi
+ pop %esi
+ pop %es
+ pop %ebp
+
+ ret
+
diff --git a/sys/i386/boot/asm.h b/sys/i386/boot/asm.h
new file mode 100644
index 0000000..43242bd
--- /dev/null
+++ b/sys/i386/boot/asm.h
@@ -0,0 +1,144 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.7 92/02/29 15:33:41 rpd
+ * $Id$
+ */
+
+#define S_ARG0 4(%esp)
+#define S_ARG1 8(%esp)
+#define S_ARG2 12(%esp)
+#define S_ARG3 16(%esp)
+
+#define FRAME pushl %ebp; movl %esp, %ebp
+#define EMARF leave
+
+#define B_ARG0 8(%ebp)
+#define B_ARG1 12(%ebp)
+#define B_ARG2 16(%ebp)
+#define B_ARG3 20(%ebp)
+
+#ifdef wheeze
+
+#define ALIGN 4
+#define EXT(x) x
+#define LEXT(x) x:
+#define LCL(x) ./**/x
+
+#define LB(x,n) ./**/x
+#define LBb(x,n) ./**/x
+#define LBf(x,n) ./**/x
+
+#define SVC lcall $7,$0
+
+#define String .string
+#define Value .value
+#define Times(a,b) [a\*b]
+#define Divide(a,b) [a\\b]
+
+#define INB inb (%dx)
+#define OUTB outb (%dx)
+#define INL inl (%dx)
+#define OUTL outl (%dx)
+
+#else wheeze
+#define ALIGN
+#define LCL(x) x
+
+#define LB(x,n) n
+#ifdef __STDC__
+#define EXT(x) _ ## x
+#define LEXT(x) _ ## x ## :
+
+#define LBb(x,n) n ## b
+#define LBf(x,n) n ## f
+#else __STDC__
+#define EXT(x) _/**/x
+#define LEXT(x) _/**/x/**/:
+#define LBb(x,n) n/**/b
+#define LBf(x,n) n/**/f
+#endif __STDC__
+#define SVC .byte 0x9a; .long 0; .word 0x7
+
+#define String .ascii
+#define Value .word
+#define Times(a,b) (a*b)
+#define Divide(a,b) (a/b)
+
+#define INB inb %dx, %al
+#define OUTB outb %al, %dx
+#define INL inl %dx, %eax
+#define OUTL outl %eax, %dx
+
+#endif wheeze
+
+#define addr32 .byte 0x67
+#define data32 .byte 0x66
+
+#ifdef GPROF
+#ifdef __STDC__
+
+#define MCOUNT .data; LB(x, 9); .long 0; .text; lea LBb(x, 9),%edx; call mcount
+#define ENTRY(x) .globl EXT(x); .align ALIGN; LEXT(x) ; \
+ pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp;
+#define ENTRY2(x,y) .globl EXT(x); .globl EXT(y); \
+ .align ALIGN; LEXT(x) LEXT(y) ; \
+ pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp;
+#define ASENTRY(x) .globl x; .align ALIGN; x ## : ; \
+ pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp;
+
+#else __STDC__
+
+#define MCOUNT .data; LB(x, 9): .long 0; .text; lea LBb(x, 9),%edx; call mcount
+#define ENTRY(x) .globl EXT(x); .align ALIGN; LEXT(x) ; \
+ pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp;
+#define ENTRY2(x,y) .globl EXT(x); .globl EXT(y); \
+ .align ALIGN; LEXT(x) LEXT(y)
+#define ASENTRY(x) .globl x; .align ALIGN; x: ; \
+ pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp;
+
+#endif __STDC__
+#else GPROF
+#ifdef __STDC__
+
+#define MCOUNT
+#define ENTRY(x) .globl EXT(x); .align ALIGN; LEXT(x)
+#define ENTRY2(x,y) .globl EXT(x); .globl EXT(y); \
+ .align ALIGN; LEXT(x) LEXT(y)
+#define ASENTRY(x) .globl x; .align ALIGN; x ## :
+
+#else __STDC__
+
+#define MCOUNT
+#define ENTRY(x) .globl EXT(x); .align ALIGN; LEXT(x)
+#define ENTRY2(x,y) .globl EXT(x); .globl EXT(y); \
+ .align ALIGN; LEXT(x) LEXT(y)
+#define ASENTRY(x) .globl x; .align ALIGN; x:
+
+#endif __STDC__
+#endif GPROF
+
+#define Entry(x) .globl EXT(x); .align ALIGN; LEXT(x)
+#define DATA(x) .globl EXT(x); .align ALIGN; LEXT(x)
diff --git a/sys/i386/boot/asm.s b/sys/i386/boot/asm.s
new file mode 100644
index 0000000..8802b13
--- /dev/null
+++ b/sys/i386/boot/asm.s
@@ -0,0 +1,270 @@
+/*
+ * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ *
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/*
+ * HISTORY
+ * $Log: asm.s,v $
+ * Revision 2.2 92/04/04 11:34:13 rpd
+ * Fix Intel Copyright as per B. Davies authorization.
+ * [92/04/03 rvb]
+ * From 2.5 boot: pruned inb(), outb(), and pzero().
+ * [92/03/30 rvb]
+ *
+ * Revision 2.2 91/04/02 14:35:10 mbj
+ * Added _sp() => where is the stack at. [kupfer]
+ * Add Intel copyright
+ * [90/02/09 rvb]
+ *
+ */
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+ .file "asm.s"
+
+#include "asm.h"
+
+
+CR0_PE_ON = 0x1
+CR0_PE_OFF = 0xfffffffe
+
+.globl _ouraddr
+ .text
+
+/*
+#
+# real_to_prot()
+# transfer from real mode to protected mode.
+*/
+
+ENTRY(real_to_prot)
+ # guarantee that interrupt is disabled when in prot mode
+ cli
+
+ # load the gdtr
+ addr16
+ data32
+ lgdt EXT(Gdtr)
+
+ # set the PE bit of CR0
+ mov %cr0, %eax
+
+ data32
+ or $CR0_PE_ON, %eax
+ mov %eax, %cr0
+
+ # make intrasegment jump to flush the processor pipeline and
+ # reload CS register
+ data32
+ ljmp $0x18, $xprot
+
+xprot:
+ # we are in USE32 mode now
+ # set up the protective mode segment registers : DS, SS, ES
+ mov $0x20, %eax
+ movw %ax, %ds
+ movw %ax, %ss
+ movw %ax, %es
+
+ ret
+
+/*
+#
+# prot_to_real()
+# transfer from protected mode to real mode
+#
+*/
+
+ENTRY(prot_to_real)
+
+ # set up a dummy stack frame for the second seg change.
+ movl _ouraddr, %eax
+ sarl $4, %eax
+ pushw %ax
+ pushw $xreal
+
+ # Change to use16 mode.
+ ljmp $0x28, $x16
+
+x16:
+ # clear the PE bit of CR0
+ mov %cr0, %eax
+ data32
+ and $CR0_PE_OFF, %eax
+ mov %eax, %cr0
+
+
+ # make intersegment jmp to flush the processor pipeline
+ # using the fake stack frame set up earlier
+ # and reload CS register
+ lret
+
+
+xreal:
+ # we are in real mode now
+ # set up the real mode segment registers : DS, SS, ES
+ movw %cs, %ax
+ movw %ax, %ds
+ movw %ax, %ss
+ movw %ax, %es
+
+ data32
+ ret
+
+/*
+#
+# startprog(phyaddr)
+# start the program on protected mode where phyaddr is the entry point
+#
+*/
+
+ENTRY(startprog)
+ push %ebp
+ mov %esp, %ebp
+
+
+
+ # get things we need into registers
+ movl 0x8(%ebp), %ecx # entry offset
+ movl 0x0c(%ebp), %eax # &argv
+
+ # make a new stack at 0:0xa0000 (big segs)
+ mov $0x10, %ebx
+ movw %bx, %ss
+ movl $0xa0000,%ebx
+ movl %ebx,%esp
+
+
+ # push some number of args onto the stack
+ pushl $0 # nominally a cyl offset in the boot.
+ pushl 0x8(%eax) # argv[2] = bootdev
+ pushl 0x4(%eax) # argv[1] = howto
+ pushl $0 # dummy 'return' address
+
+ # push on our entry address
+ mov $0x08, %ebx # segment
+ pushl %ebx
+ pushl %ecx
+
+ # convert over the other data segs
+ mov $0x10, %ebx
+ movw %bx, %ds
+ movw %bx, %es
+
+ # convert the PC (and code seg)
+ lret
+/*
+#
+# pbzero( dst, cnt)
+# where src is a virtual address and dst is a physical address
+*/
+
+ENTRY(pbzero)
+ push %ebp
+ mov %esp, %ebp
+ push %es
+ push %esi
+ push %edi
+ push %ecx
+
+ cld
+
+ # set %es to point at the flat segment
+ mov $0x10, %eax
+ movw %ax, %es
+
+ mov 0x8(%ebp), %edi # destination
+ mov 0xc(%ebp), %ecx # count
+ mov $0x0,%eax # value
+
+ rep
+ stosb
+
+ pop %ecx
+ pop %edi
+ pop %esi
+ pop %es
+ pop %ebp
+
+ ret
+/*
+#
+# pcpy(src, dst, cnt)
+# where src is a virtual address and dst is a physical address
+#
+*/
+
+ENTRY(pcpy)
+ push %ebp
+ mov %esp, %ebp
+ push %es
+ push %esi
+ push %edi
+ push %ecx
+
+ cld
+
+ # set %es to point at the flat segment
+ mov $0x10, %eax
+ movw %ax, %es
+
+ mov 0x8(%ebp), %esi # source
+ mov 0xc(%ebp), %edi # destination
+ mov 0x10(%ebp), %ecx # count
+
+ rep
+ movsb
+
+ pop %ecx
+ pop %edi
+ pop %esi
+ pop %es
+ pop %ebp
+
+ ret
+
diff --git a/sys/i386/boot/bios.S b/sys/i386/boot/bios.S
new file mode 100644
index 0000000..687ca18
--- /dev/null
+++ b/sys/i386/boot/bios.S
@@ -0,0 +1,329 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:34:26 rpd
+ * $Id$
+ */
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+ .file "bios.s"
+
+#include "asm.h"
+ .text
+
+/*
+# biosread(dev, cyl, head, sec, nsec, offset)
+# Read "nsec" sectors from disk to offset "offset" in boot segment
+# BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
+# Call with %ah = 0x2
+# %al = number of sectors
+# %ch = cylinder
+# %cl = sector
+# %dh = head
+# %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
+# %es:%bx = segment:offset of buffer
+# Return:
+# %al = 0x0 on success; err code on failure
+*/
+
+ENTRY(biosread)
+ push %ebp
+ mov %esp, %ebp
+
+ push %ebx
+ push %ecx
+ push %edx
+ push %es
+
+ movb 0x10(%ebp), %dh
+ movw 0x0c(%ebp), %cx
+ xchgb %ch, %cl # cylinder; the highest 2 bits of cyl is in %cl
+ rorb $2, %cl
+ movb 0x14(%ebp), %al
+ orb %al, %cl
+ incb %cl # sector; sec starts from 1, not 0
+ movb 0x8(%ebp), %dl # device
+ movl 0x1c(%ebp), %ebx # offset
+ # prot_to_real will set %es to BOOTSEG
+
+ call EXT(prot_to_real) # enter real mode
+ movb $0x2, %ah # subfunction
+ addr32
+ movb 0x18(%ebp), %al # number of sectors
+
+ sti
+ int $0x13
+ cli
+
+ mov %eax, %ebx # save return value (actually movw %ax, %bx)
+
+ data32
+ call EXT(real_to_prot) # back to protected mode
+
+ xor %eax, %eax
+ movb %bh, %al # return value in %ax
+
+ pop %es
+ pop %edx
+ pop %ecx
+ pop %ebx
+ pop %ebp
+
+ ret
+
+
+/*
+# putc(ch)
+# BIOS call "INT 10H Function 0Eh" to write character to console
+# Call with %ah = 0x0e
+# %al = character
+# %bh = page
+# %bl = foreground color ( graphics modes)
+*/
+
+
+ENTRY(putc)
+ push %ebp
+ mov %esp, %ebp
+ push %ebx
+ push %ecx
+
+ movb 0x8(%ebp), %cl
+
+ call EXT(prot_to_real)
+
+ data32
+ mov $0x1, %ebx # %bh=0, %bl=1 (blue)
+ movb $0xe, %ah
+ movb %cl, %al
+ sti
+ int $0x10 # display a byte
+ cli
+
+ data32
+ call EXT(real_to_prot)
+
+ pop %ecx
+ pop %ebx
+ pop %ebp
+ ret
+
+
+/*
+# getc()
+# BIOS call "INT 16H Function 00H" to read character from keyboard
+# Call with %ah = 0x0
+# Return: %ah = keyboard scan code
+# %al = ASCII character
+*/
+
+ENTRY(getc)
+ push %ebp
+ mov %esp, %ebp
+ push %ebx # save %ebx
+
+ call EXT(prot_to_real)
+
+ movb $0x0, %ah
+ sti
+ int $0x16
+ cli
+
+ movb %al, %bl # real_to_prot uses %eax
+
+ data32
+ call EXT(real_to_prot)
+
+ xor %eax, %eax
+ movb %bl, %al
+
+ pop %ebx
+ pop %ebp
+ ret
+/*
+# ischar()
+# if there is a character pending, return it; otherwise return 0
+# BIOS call "INT 16H Function 01H" to check whether a character is pending
+# Call with %ah = 0x1
+# Return:
+# If key waiting to be input:
+# %ah = keyboard scan code
+# %al = ASCII character
+# Zero flag = clear
+# else
+# Zero flag = set
+*/
+ENTRY(ischar)
+ push %ebp
+ mov %esp, %ebp
+ push %ebx
+
+ call EXT(prot_to_real) # enter real mode
+
+ xor %ebx, %ebx
+ movb $0x1, %ah
+ sti
+ int $0x16
+ cli
+ data32
+ jz nochar
+ movb %al, %bl
+
+nochar:
+ data32
+ call EXT(real_to_prot)
+
+ xor %eax, %eax
+ movb %bl, %al
+
+ pop %ebx
+ pop %ebp
+ ret
+
+/*
+#
+# get_diskinfo(): return a word that represents the
+# max number of sectors and heads and drives for this device
+#
+*/
+
+ENTRY(get_diskinfo)
+ push %ebp
+ mov %esp, %ebp
+ push %es
+ push %ebx
+ push %ecx
+ push %edx
+
+ movb 0x8(%ebp), %dl # diskinfo(drive #)
+ call EXT(prot_to_real) # enter real mode
+
+ movb $0x8, %ah # ask for disk info
+
+ sti
+ int $0x13
+ cli
+
+ jnc ok
+ /*
+ * Urk. Call failed. It is not supported for floppies by old BIOS's.
+ * Guess it's a 15-sector floppy. Initialize all the registers for
+ * documentation, although we only need head and sector counts.
+ */
+ subb %ah, %ah # %ax = 0
+ movb %al, %al
+ movb %ah, %bh # %bh = 0
+ movb $2, %bl # %bl bits 0-3 = drive type, 2 = 1.2M
+ movb $79, %ch # max track
+ movb $15, %cl # max sector
+ movb $1, %dh # max head
+ movb $1, %dl # # floppy drives installed
+ # es:di = parameter table
+ # carry = 0
+ok:
+
+ data32
+ call EXT(real_to_prot) # back to protected mode
+
+ xor %eax, %eax
+
+ /*form a longword representing all this gunk*/
+ movb %dh, %ah # max head
+ andb $0x3f, %cl # mask of cylinder gunk
+ movb %cl, %al # max sector (and # sectors)
+
+ pop %edx
+ pop %ecx
+ pop %ebx
+ pop %es
+ pop %ebp
+ ret
+
+/*
+#
+# memsize(i) : return the memory size in KB. i == 0 for conventional memory,
+# i == 1 for extended memory
+# BIOS call "INT 12H" to get conventional memory size
+# BIOS call "INT 15H, AH=88H" to get extended memory size
+# Both have the return value in AX.
+#
+*/
+
+ENTRY(memsize)
+ push %ebp
+ mov %esp, %ebp
+ push %ebx
+
+ mov 8(%ebp), %ebx
+
+ call EXT(prot_to_real) # enter real mode
+
+ cmpb $0x1, %bl
+ data32
+ je xext
+
+ sti
+ int $0x12
+ cli
+ data32
+ jmp xdone
+
+xext: movb $0x88, %ah
+ sti
+ int $0x15
+ cli
+
+xdone:
+ mov %eax, %ebx
+
+ data32
+ call EXT(real_to_prot)
+
+ mov %ebx, %eax
+ pop %ebx
+ pop %ebp
+ ret
diff --git a/sys/i386/boot/bios.s b/sys/i386/boot/bios.s
new file mode 100644
index 0000000..d1673a5
--- /dev/null
+++ b/sys/i386/boot/bios.s
@@ -0,0 +1,326 @@
+/*
+ * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ *
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/*
+ * HISTORY
+ * $Log: bios.s,v $
+ * Revision 2.2 92/04/04 11:34:26 rpd
+ * Fix Intel Copyright as per B. Davies authorization.
+ * [92/04/03 rvb]
+ * From 2.5 version
+ * [92/03/30 mg32]
+ *
+ * Revision 2.2 91/04/02 14:35:21 mbj
+ * Add Intel copyright
+ * [90/02/09 rvb]
+ *
+ */
+
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+ .file "bios.s"
+
+#include "asm.h"
+ .text
+
+/*
+# biosread(dev, cyl, head, sec)
+# Read one sector from disk into the internal buffer "intbuf" which
+# is the first 512 bytes of the boot loader.
+# BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
+# Call with %ah = 0x2
+# %al = number of sectors
+# %ch = cylinder
+# %cl = sector
+# %dh = head
+# %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
+# %es:%bx = segment:offset of buffer
+# Return:
+# %al = 0x0 on success; err code on failure
+*/
+
+ENTRY(biosread)
+ push %ebp
+ mov %esp, %ebp
+
+ push %ebx
+ push %ecx
+ push %edx
+ push %es
+
+ movb 0x10(%ebp), %dh
+ movw 0x0c(%ebp), %cx
+ xchgb %ch, %cl # cylinder; the highest 2 bits of cyl is in %cl
+ rorb $2, %cl
+ movb 0x14(%ebp), %al
+ orb %al, %cl
+ incb %cl # sector; sec starts from 1, not 0
+ movb 0x8(%ebp), %dl # device
+ xor %ebx, %ebx # offset -- 0
+ # prot_to_real will set %es to BOOTSEG
+
+ call EXT(prot_to_real) # enter real mode
+ movb $0x2, %ah # subfunction
+ movb $0x1, %al # number of sectors -- one
+
+ sti
+ int $0x13
+ cli
+
+ mov %eax, %ebx # save return value
+
+ data16
+ call EXT(real_to_prot) # back to protected mode
+
+ xor %eax, %eax
+ movb %bh, %al # return value in %ax
+
+ pop %es
+ pop %edx
+ pop %ecx
+ pop %ebx
+ pop %ebp
+
+ ret
+
+
+/*
+# putc(ch)
+# BIOS call "INT 10H Function 0Eh" to write character to console
+# Call with %ah = 0x0e
+# %al = character
+# %bh = page
+# %bl = foreground color ( graphics modes)
+*/
+
+
+ENTRY(putc)
+ push %ebp
+ mov %esp, %ebp
+ push %ebx
+ push %ecx
+
+ movb 0x8(%ebp), %cl
+
+ call EXT(prot_to_real)
+
+ data16
+ mov $0x1, %ebx # %bh=0, %bl=1 (blue)
+ movb $0xe, %ah
+ movb %cl, %al
+ sti
+ int $0x10 # display a byte
+ cli
+
+ data16
+ call EXT(real_to_prot)
+
+ pop %ecx
+ pop %ebx
+ pop %ebp
+ ret
+
+
+/*
+# getc()
+# BIOS call "INT 16H Function 00H" to read character from keyboard
+# Call with %ah = 0x0
+# Return: %ah = keyboard scan code
+# %al = ASCII character
+*/
+
+ENTRY(getc)
+ push %ebp
+ mov %esp, %ebp
+ push %ebx # save %ebx
+
+ call EXT(prot_to_real)
+
+ movb $0x0, %ah
+ sti
+ int $0x16
+ cli
+
+ movb %al, %bl # real_to_prot uses %eax
+
+ data16
+ call EXT(real_to_prot)
+
+ xor %eax, %eax
+ movb %bl, %al
+
+ pop %ebx
+ pop %ebp
+ ret
+/*
+# ischar()
+# if there is a character pending, return it; otherwise return 0
+# BIOS call "INT 16H Function 01H" to check whether a character is pending
+# Call with %ah = 0x1
+# Return:
+# If key waiting to be input:
+# %ah = keyboard scan code
+# %al = ASCII character
+# Zero flag = clear
+# else
+# Zero flag = set
+*/
+ENTRY(ischar)
+ push %ebp
+ mov %esp, %ebp
+ push %ebx
+
+ call EXT(prot_to_real) # enter real mode
+
+ xor %ebx, %ebx
+ movb $0x1, %ah
+ sti
+ int $0x16
+ cli
+ data16
+ jz nochar
+ movb %al, %bl
+
+nochar:
+ data16
+ call EXT(real_to_prot)
+
+ xor %eax, %eax
+ movb %bl, %al
+
+ pop %ebx
+ pop %ebp
+ ret
+
+/*
+#
+# get_diskinfo(): return a word that represents the
+# max number of sectors and heads and drives for this device
+#
+*/
+
+ENTRY(get_diskinfo)
+ push %ebp
+ mov %esp, %ebp
+ push %es
+ push %ebx
+ push %ecx
+ push %edx
+
+ movb 0x8(%ebp), %dl # diskinfo(drive #)
+ call EXT(prot_to_real) # enter real mode
+
+ movb $0x8, %ah # ask for disk info
+
+ sti
+ int $0x13
+ cli
+
+ data16
+ call EXT(real_to_prot) # back to protected mode
+
+ xor %eax, %eax
+
+ /*form a longword representing all this gunk*/
+ movb %dh, %ah # # heads
+ andb $0x3f, %cl # mask of cylinder gunk
+ movb %cl, %al # # sectors
+
+ pop %edx
+ pop %ecx
+ pop %ebx
+ pop %es
+ pop %ebp
+ ret
+
+/*
+#
+# memsize(i) : return the memory size in KB. i == 0 for conventional memory,
+# i == 1 for extended memory
+# BIOS call "INT 12H" to get conventional memory size
+# BIOS call "INT 15H, AH=88H" to get extended memory size
+# Both have the return value in AX.
+#
+*/
+
+ENTRY(memsize)
+ push %ebp
+ mov %esp, %ebp
+ push %ebx
+
+ mov 8(%ebp), %ebx
+
+ call EXT(prot_to_real) # enter real mode
+
+ cmpb $0x1, %bl
+ data16
+ je xext
+
+ sti
+ int $0x12
+ cli
+ data16
+ jmp xdone
+
+xext: movb $0x88, %ah
+ sti
+ int $0x15
+ cli
+
+xdone:
+ mov %eax, %ebx
+
+ data16
+ call EXT(real_to_prot)
+
+ mov %ebx, %eax
+ pop %ebx
+ pop %ebp
+ ret
diff --git a/sys/i386/boot/biosboot/Makefile b/sys/i386/boot/biosboot/Makefile
new file mode 100644
index 0000000..568c747
--- /dev/null
+++ b/sys/i386/boot/biosboot/Makefile
@@ -0,0 +1,106 @@
+#
+# Permission to use, copy, modify and distribute this software and its
+# documentation is hereby granted, provided that both the copyright
+# notice and this permission notice appear in all copies of the
+# software, derivative works or modified versions, and any portions
+# thereof, and that both notices appear in supporting documentation.
+#
+# CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+# CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+# ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+#
+# Carnegie Mellon requests users of this software to return to
+#
+# Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+# School of Computer Science
+# Carnegie Mellon University
+# Pittsburgh PA 15213-3890
+#
+# any improvements or extensions that they make and grant Carnegie Mellon
+# the rights to redistribute these changes.
+#
+# from: Mach, Revision 2.2 92/04/04 11:33:46 rpd
+# $Id: Makefile,v 1.9 1994/06/16 03:53:24 adam Exp $
+#
+
+wd0:
+ dd if=boot of=biosboot count=1
+ dd if=boot of=bootbios skip=1
+ disklabel -r -w wd0 nec5655 newboot biosboot bootbios
+ rm biosboot bootbios
+
+NOPROG= noprog
+NOMAN= noman
+
+# tunable loopcount parameter, waiting for keypress
+BOOTWAIT?= 2400
+
+CFLAGS = -O2 -DDO_BAD144 -DBOOTWAIT=${BOOTWAIT} -I${.CURDIR}
+LIBS= -lc
+INC= -I${.CURDIR}/../..
+
+# start.o should be first
+OBJS = start.o table.o boot2.o boot.o asm.o bios.o io.o disk.o sys.o
+
+.SUFFIXES: .S .c .o
+
+.c.o:
+ $(CC) $(CFLAGS) $(INC) -c $<
+
+.S.o:
+ $(CC) $(CFLAGS) -c $<
+
+boot: $(OBJS)
+ $(LD) -Bstatic -N -T 0 -o boot $(OBJS) $(LIBS)
+ cp boot boot.sym
+ @strip boot
+ @sh ${.CURDIR}/rmaouthdr boot boot.tmp
+ @mv -f boot.tmp boot
+ @ls -l boot
+
+biosboot: boot
+ dd if=boot of=biosboot count=1
+
+bootbios: boot
+ dd if=boot of=bootbios skip=1
+
+${DESTDIR}/usr/mdec/bootsd: bootbios
+ cp bootbios ${DESTDIR}/usr/mdec/bootsd
+
+${DESTDIR}/usr/mdec/sdboot: biosboot
+ cp biosboot ${DESTDIR}/usr/mdec/sdboot
+
+${DESTDIR}/usr/mdec/bootwd: ${DESTDIR}/usr/mdec/bootsd
+ rm -f ${DESTDIR}/usr/mdec/bootwd
+ ln ${DESTDIR}/usr/mdec/bootsd ${DESTDIR}/usr/mdec/bootwd
+
+${DESTDIR}/usr/mdec/wdboot: ${DESTDIR}/usr/mdec/sdboot
+ rm -f ${DESTDIR}/usr/mdec/wdboot
+ ln ${DESTDIR}/usr/mdec/sdboot ${DESTDIR}/usr/mdec/wdboot
+
+${DESTDIR}/usr/mdec/bootfd: ${DESTDIR}/usr/mdec/bootsd
+ rm -f ${DESTDIR}/usr/mdec/bootfd
+ ln ${DESTDIR}/usr/mdec/bootsd ${DESTDIR}/usr/mdec/bootfd
+
+${DESTDIR}/usr/mdec/fdboot: ${DESTDIR}/usr/mdec/sdboot
+ rm -f ${DESTDIR}/usr/mdec/fdboot
+ ln ${DESTDIR}/usr/mdec/sdboot ${DESTDIR}/usr/mdec/fdboot
+
+sd: ${DESTDIR}/usr/mdec/bootsd ${DESTDIR}/usr/mdec/sdboot
+wd: ${DESTDIR}/usr/mdec/bootwd ${DESTDIR}/usr/mdec/wdboot
+fd: ${DESTDIR}/usr/mdec/bootfd ${DESTDIR}/usr/mdec/fdboot
+
+all: biosboot bootbios
+
+fd0:
+ dd if=boot of=biosboot count=1
+ dd if=boot of=bootbios skip=1
+ disklabel -r -w fd0 floppy bootflpy biosboot bootbios
+ rm biosboot bootbios
+
+install: wd sd fd
+
+clean:
+ /bin/rm -f *.o *.d boot bootbios biosboot boot.sym
+
+.include <bsd.prog.mk>
diff --git a/sys/i386/boot/biosboot/README.386BSD b/sys/i386/boot/biosboot/README.386BSD
new file mode 100644
index 0000000..cc5cb8f
--- /dev/null
+++ b/sys/i386/boot/biosboot/README.386BSD
@@ -0,0 +1,151 @@
+This Boot code is different from the original boot code that came with
+386BSD in that it uses the BIOS to load the kernel and to provide all i/o
+services. The advantage ofthis is that the same boot code exactly, can run
+on any device that is supported by the BIOS. (That's most of them)
+This is important for the 'generic scsi' project because it means we can
+write drivers for new scsi adapters without having to develop an new
+set of boot blocks for each.
+
+At this point you should read the first part of README.MACH... come back here
+when you have done that:
+
+In normal operation, when co-existing with other operating systems, the
+following operations occur:
+
+1/ the BIOS loads the first block of the disk (called the Master Boot Record
+or MBR) and if it has the correct magic numbers, jumps into it:
+
+2/ The MBR code, looks at the Partition table that is embedded within it,
+to detirmine which is the partition to boot from. If you are using the os-bs
+bootblocks (highly recommended) then it will give you a menu to choose from.
+
+3/ The MBR will load the first record of the selected partition and
+if it has (the same) magic numbers, jumps into it. In 386bsd this is the
+first stage boot, (or boot1) it is represented in /usr/mdec by
+wdboot, asboot and sdboot. If the disk has been set up without DOS partitioning
+then this block will be at block zero, and will have been loaded directly by
+the BIOS.
+
+4/ Boot1 will look at block0 (which might be itself if there are no DOS
+partitions) and will find the 386bsd partition, and using the information
+regarding the start position of that partition, will load the next 13 sectors
+or so, to around 90000 (640k - 64k). and will jump into it at the appropriate
+entry point. Since boot1 and boot2 were compiled together as one file
+and then split later, boot1 knows the exact position within boot2 of the
+entry point.
+
+Boot 1 also contains a compiled in DOS partition table
+(in case it is at block 0), which contains a 386bsd partition starting
+at 0. This ensures that the same code can work whether or not
+boot1 is at block 0.
+
+5/ Boot2 asks the user for a boot device, partition and filename, and then
+loads the MBR of the selected device. This may or may not be the device
+which was originally used to boot the first MBR. The partition table
+of the new MBR is searched for a 386bsd partition, and if one is found,
+that is then in turn searched for the disklabel. This could all be on the
+second disk at this point, if the user selected it.
+
+6/On finding the disklabel, boot2 can find the correct unix partition
+within the 386bsd partition, and using cutdown filesystem code,
+look for the file to boot (e.g. 386bsd).
+
+7/ Boot2 loads this file starting at the location specified by the a.out header,
+(see later) and leaps into it at the location specified in he header.
+
+if the file does not exist or cannot be loaded, boot2 goes back to step 5.
+
+386bsd is now running and will hopefully start vm etc. and get to multi-user
+mode.
+
+##########################################################################
+During all these steps, all i/o is performed using the BIOS. This has a number
+of side effects:
+
+1/ Since BIOS disk calls are specified in terms of cylinder,head and sector,
+and the BIOS read the disk information from either the CMOS or from some other
+location which is un-available to us, we must use the cyl,head,sec information
+that is given in the MBR, rather than the start address in the MBR, because
+we cannot guarentee that we can corectly calculate C,H,S from the start address.
+
+Therefore, the C,H,S information in the MBR must be as correct for this boot
+to work as it would be for DOS to boot. For example, adaptec BIOS routines
+assume a layout of 64 heads and 32 sectors giving 1MB per ficticious cylinder.
+You must use these figures to calculate the correct values. Luckily, the DOS
+fdisk program will do all this for you if you tell it to give you a DOS
+partition, and you can change it to a 386BSD partition later. If you use
+no DOS partitioning, then the compiled in table in Boot1 will do just fine.
+
+If you want to do it by hand remember that BIOS counts sectors starting at 1.
+(cylinders and heads start at 0 (??))
+
+2/ you cannot overwrite the bottom 4k of ram until you have finished ALL
+bios calls, as BIOS uses this area as scratch memory.
+
+3/ Since BIOS runs in REAL mode, and Boot2 runs in protected mode,
+Boot 2 switches back to real mode just before each BIOS call and then
+back to protected mode on each return. Touch this at your peril.!
+
+#########################################################################
+In answering the prompt from Boot2:
+you can,
+1/ leave it alone.. it will boot the indicated file from the first
+partition of the first drive seen by the BIOS (C:)
+
+2/ enter only "-s" to boot the default to single user mode
+
+3/ enter only a filename (optionally with -s) to boot that kernel,
+
+4/ enter a whole line of the form shown in the prompt. This allows you to
+boot some other partition, possibly on the second drive, as root.
+
+
+##########################################################################
+In the case you have two drives the same type (both scsi or bith IDE/ESDI),
+wd(0,a)xxx
+ will boot xxx from drive 0, a partition.
+wd(1,a)xxx
+ will boot xxx from drive 1, a partition.
+
+similarly for sd.
+
+if you have one wd drive and one scsi drive, then you MUST
+use device 'hd'
+
+otherwise the following will happen:
+
+with wd0 and sd0, you specify sd1 or wd1 to indicate the 2nd drive.
+it boots the kernel correctly, then tells the kernel to use sd1 as root.
+you however may not have an sd1, and problems arise.
+
+hd is special in that the kernel is always told to use unit 0,
+The correct TYPE of device will be specified too, so the kernel
+will be told either sd0 or wd0.
+
+Whether sd or wd is specified to the kernel is read from the disklabel,
+so ensure that all SCSI disks have type SCSI in their disklabel or the
+boot code will assume they are ESDI or IDE. (Remember, because it is
+working through the BIOS it has ho idea what kind of disk it is.
+
+##########################################################################
+Installing:
+The makefile supplied has a target install which will create the
+files wdboot,bootwd ,sdboot and bootsd in /usr/mdec.
+BEWARE these will overwrite the existing wdboot and bootwd. (so back
+them up)
+
+there are also targets wd and sd which wil only do one of them
+
+The commented out targets wd0 and sd0 are examples of how to
+load the new bootblocks, however,make sure you change the
+device type and label to suit your drive if you uncomment them.
+(see 'man disklabel')
+
+If you already have made partitions using the old bootblocks
+these should install painlessly.
+
+Before you do this ensure you have a booting floppy with correct
+disktab and bootblock files on it so that if it doesn't work, you can
+re-disklabel from the floppy.
+
+$Id$
diff --git a/sys/i386/boot/biosboot/README.MACH b/sys/i386/boot/biosboot/README.MACH
new file mode 100644
index 0000000..cb62329
--- /dev/null
+++ b/sys/i386/boot/biosboot/README.MACH
@@ -0,0 +1,210 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:33:55 rpd
+ * $Id$
+ */
+
+********NOTE: This is not all relevant to the 386BSD version**********
+
+ AT386 Protected Mode Bootstrap Loader
+ =====================================
+
+1. Overview of Startup
+ -------------------
+
+ After the system is rebooted, the BIOS bootstrap routine reads Sector
+ 1, Track 0 into memory at location 0000:7C00H. If location 0000:7DFEH
+ (last two bytes of that sector) contains the value AA55H, the BIOS
+ bootstrap routine will transfer control to 0000:7C00H. Otherwise, the
+ boot code in that sector is bad and the boot routine stops.
+
+ For DOS compatibility reason, one extra stage of boot is required if
+ the boot device is a hard disk. The first sector of the hard disk will
+ contain the MOS-DOS boot code and a boot record partition table.
+ When this sector is loaded into 0000:7C00H, it will relocate itself
+ to somewhere else and then load the first sector of the active
+ partition into 0000:7C00H. Both UNIX and DOS use the command "fdisk"
+[ 386bsd does not have an 'fdisk' (yet) ]
+ to install this first sector into the hard disk and to manipulate
+ the hard disk partitions.
+
+
+
+2. The First Stage Bootstrap Loader
+ --------------------------------
+
+ After startup, the first stage boot is loaded at 0000:7C00H. This
+ first stage boot will load itself and the second stage boot into
+ memory at location 0000:1000H. For floppy disks, the first cylinder
+ is reserved as the boot cylinder, and the boot code (first and second)
+ will be loaded from there. Currently, only double sided, high density
+ (15 sectors per track) floppies are supported. For hard disks, the
+ first 29 sectors of the active partition is reserved for boot code
+ which will be loaded by the first stage boot. All the disk types
+ recognized by BIOS are supported by this bootstrap loader.
+[for 386bsd we load the second stage booter to 9000:0]
+
+
+
+3. The Second Stage Bootstrap Loader
+ --------------------------------
+
+ After the boot code is loaded, the control is passed to the second
+ stage bootstrap loader "boot2()". In order to be able to load the
+ big kernel image (bigger than 512K or 640K, depends on the memory
+ configuration), the second stage boot loader will run on the protected
+ mode. This bootstarp loader does not have any stand alone device
+ drivers, all the I/O's are through the BIOS calls. Since the first
+ stage boot code will no longer be used at this moment, the memory
+ location of the first stage boot code (0000:1000H to 0000:1200H) will
+ be used as an internal buffer for BIOS calls. Immediately after this
+ internal buffer is the GDT table for the second stage boot loader.
+ Since this boot loader needs to switch back and forth between protected
+ and real mode in order to use BIOS calls, the limit of the boot code
+ and boot data segments must not be greater than 64K.
+
+ The boot loader loads the kernel image at memory location above 1 MB
+ to skip the memory hole between 521K/640K and 1MB. After the kernel
+ is loaded, the boot loader stores the information in the stack and
+ then passes control to kernel. Currently, the three information passed
+ fromm the boot loader to the kernel are type of the boot device, size
+ of the base memory and size of the extended memory.
+
+[ 386bsd receives: howto, bootdev]
+
+[ 386bsd is loaded where-ever the "MByte" bits of the load address specify,
+so if you link it for FE100000 it will load to 1MB, but if you link
+it for FE000000 it will load ad 0MB]
+
+[for machines with only 512KB normal ram the kernel will need to be linked
+for 1MB and the bootblocks modified to run below 512KB. (8000:0)]
+
+
+4. The UNIX Startup
+ ----------------
+
+ Since the boot loader loads the kernel image at memory location above
+ 1MB, the kernel has to start as protected mode. In addition, the
+ link editor description file (vuifile) has to indicate that
+ the text and data segments start above 1MB. Also, the boot loader
+ passes the infomation to the kernel through the stack.
+
+[MOST of what is mentionned below is NOT relevant to 386bsd]
+
+5. Disk Layout and Bad Block Handling
+ ---------------------------------
+
+ The System V/386 Release 3.2 (AT) disk layout will be used as the disk
+ layout for the MACH System on the AT platform.
+
+ This disk layout is as follows:
+
+ * Reserve the first sector of cylinder 0 for the DOS boot record which
+ contains the master boot code (446 bytes) and the partition table.
+ (Refer to DOS Technical Reference Manual page 9-6 to 9-10).
+
+ * Reserve the first 29 sectors of the UNIX partition for the first
+ and the second stage bootstrap.
+
+ * Reserve the 30th sector of the UNIX partition for the pdinfo and
+ the vtoc tables.
+
+ * Reserve the 31st to the 34th sectors of the UNIX partition for the
+ bad track and the bad block mapping tables.
+
+ * Reserve up to 253 consecutive tracks when required, beginning with
+ the 35th sector of the UNIX partition, for alternate tracks.
+
+ * Reserve up to 253 consecutive blocks, beginning with the first
+ sector after the alternate tracks area, for alternate blocks.
+
+ SEC
+ 1
+ ----------------------------------------------------
+ | X | | CYL 0, TRK 0
+ ---------------- .......... --------------------
+ | .......... |
+ ---------------- .......... --------------------
+ | .......... |
+ ===============================================================
+ ^ | BOOTSTRAP | CYL N, TRK M
+ | ----------------------------------------------------
+ | | |30 |31 |32 |33 |34 |
+ ---------------------------------------------------- ---
+ U | .......... | ^
+ N ---------------- .......... --------------------- |
+ I | .......... | Alternate Tracks
+ X ---------------- .......... --------------------- |
+ | .......... | V
+ P ---------------------------------------------------- ---
+ A | .......... | ^
+ R ---------------- .......... --------------------- |
+ T | .......... | Alternate Blocks
+ I ---------------- .......... -------------------- |
+ T | .......... | V
+ I ---------------------------------------------------- ---
+ O | Unix root partition starts from here |
+ N ---------------- -----------------
+ | |
+ ----------------------------------------------------
+ | |
+ ----------------------------------------------------
+ | |
+ | ---------------------------------------------------
+ | | |
+ | ----------------------------------------------------
+ V | |
+ ===============================================================
+ | ........ |
+ --------------- ........ --------------
+ | ........ |
+ ----------------------------------------------------
+
+
+ The bad block handling mechanism is as follows:
+
+ * Use the alternate track in the alternate tracks area if the
+ track containing the target sector is bad.
+
+ * Use the alternate block in the alternate blocks area if the
+ target sector is bad.
+
+
+
+
+6. How to make:
+ -----------
+
+ Since the kernel image is loaded above 1 MB, the kernel must start
+ as protected mode. This means that this bootstrap loader will work
+ only when the corresponding changes on the kernel startup code are done.
+
+ The make command to generate this bootstrap loader is:
+
+ make -f boot.mk fdboot (floppy boot loader)
+ make -f boot.mk hdboot (wini boot loader)
+[to make 386bsd bootblocks "make sd wd" (warning: they will be installed
+in /dev/mdec.. take backups)]
diff --git a/sys/i386/boot/biosboot/asm.S b/sys/i386/boot/biosboot/asm.S
new file mode 100644
index 0000000..b291428
--- /dev/null
+++ b/sys/i386/boot/biosboot/asm.S
@@ -0,0 +1,260 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:34:13 rpd
+ * $Id$
+ */
+
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+ .file "asm.s"
+
+#include "asm.h"
+
+
+CR0_PE_ON = 0x1
+CR0_PE_OFF = 0xfffffffe
+
+.globl _ouraddr
+ .text
+
+/*
+#
+# real_to_prot()
+# transfer from real mode to protected mode.
+*/
+
+ENTRY(real_to_prot)
+ # guarantee that interrupt is disabled when in prot mode
+ cli
+
+ # load the gdtr
+ addr32
+ data32
+ lgdt EXT(Gdtr)
+
+ # set the PE bit of CR0
+ mov %cr0, %eax
+
+ data32
+ or $CR0_PE_ON, %eax
+ mov %eax, %cr0
+
+ # make intrasegment jump to flush the processor pipeline and
+ # reload CS register
+ data32
+ ljmp $0x18, $xprot
+
+xprot:
+ # we are in USE32 mode now
+ # set up the protected mode segment registers : DS, SS, ES
+ mov $0x20, %eax
+ movw %ax, %ds
+ movw %ax, %ss
+ movw %ax, %es
+
+ # load idtr so we can debug
+ lidt EXT(Idtr_prot)
+
+ ret
+
+/*
+#
+# prot_to_real()
+# transfer from protected mode to real mode
+#
+*/
+
+ENTRY(prot_to_real)
+
+ # set up a dummy stack frame for the second seg change.
+ movl _ouraddr, %eax
+ sarl $4, %eax
+ pushw %ax
+ movw $xreal, %ax # gas botches pushw $xreal - extra bytes 0, 0
+ pushw %ax # decode to add %al, (%eax) (%al usually 0)
+
+ # Change to use16 mode.
+ ljmp $0x28, $x16
+
+x16:
+ # clear the PE bit of CR0
+ mov %cr0, %eax
+ data32
+ and $CR0_PE_OFF, %eax
+ mov %eax, %cr0
+
+ # make intersegment jmp to flush the processor pipeline
+ # using the fake stack frame set up earlier
+ # and reload CS register
+ lret
+
+xreal:
+ # we are in real mode now
+ # set up the real mode segment registers : DS, SS, ES
+ movw %cs, %ax
+ movw %ax, %ds
+ movw %ax, %ss
+ movw %ax, %es
+
+ # load idtr so we can debug
+ addr32
+ data32
+ lidt EXT(Idtr_real)
+
+ data32
+ ret
+
+/*
+#
+# startprog(phyaddr)
+# start the program on protected mode where phyaddr is the entry point
+#
+*/
+
+ENTRY(startprog)
+ push %ebp
+ mov %esp, %ebp
+
+ # get things we need into registers
+ movl 0x8(%ebp), %ecx # entry offset
+ movl 0x0c(%ebp), %eax # &argv
+
+ # make a new stack at 0:0xa0000 (big segs)
+ mov $0x10, %ebx
+ movw %bx, %ss
+ movl $0xa0000, %ebx
+ movl %ebx, %esp
+
+ # push some number of args onto the stack
+ pushl $0 # nominally a cyl offset in the boot.
+ pushl 0x8(%eax) # argv[2] = bootdev
+ pushl 0x4(%eax) # argv[1] = howto
+ pushl $0 # dummy 'return' address
+
+ # push on our entry address
+ mov $0x08, %ebx # segment
+ pushl %ebx
+ pushl %ecx
+
+ # convert over the other data segs
+ mov $0x10, %ebx
+ movw %bx, %ds
+ movw %bx, %es
+
+ # convert the PC (and code seg)
+ lret
+/*
+#
+# pbzero( dst, cnt)
+# where src is a virtual address and dst is a physical address
+*/
+
+ENTRY(pbzero)
+ push %ebp
+ mov %esp, %ebp
+ push %es
+ push %esi
+ push %edi
+ push %ecx
+
+ cld
+
+ # set %es to point at the flat segment
+ mov $0x10, %eax
+ movw %ax, %es
+
+ mov 0x8(%ebp), %edi # destination
+ mov 0xc(%ebp), %ecx # count
+ mov $0x0, %eax # value
+
+ rep
+ stosb
+
+ pop %ecx
+ pop %edi
+ pop %esi
+ pop %es
+ pop %ebp
+
+ ret
+/*
+#
+# pcpy(src, dst, cnt)
+# where src is a virtual address and dst is a physical address
+#
+*/
+
+ENTRY(pcpy)
+ push %ebp
+ mov %esp, %ebp
+ push %es
+ push %esi
+ push %edi
+ push %ecx
+
+ cld
+
+ # set %es to point at the flat segment
+ mov $0x10, %eax
+ movw %ax, %es
+
+ mov 0x8(%ebp), %esi # source
+ mov 0xc(%ebp), %edi # destination
+ mov 0x10(%ebp), %ecx # count
+
+ rep
+ movsb
+
+ pop %ecx
+ pop %edi
+ pop %esi
+ pop %es
+ pop %ebp
+
+ ret
+
diff --git a/sys/i386/boot/biosboot/asm.h b/sys/i386/boot/biosboot/asm.h
new file mode 100644
index 0000000..43242bd
--- /dev/null
+++ b/sys/i386/boot/biosboot/asm.h
@@ -0,0 +1,144 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.7 92/02/29 15:33:41 rpd
+ * $Id$
+ */
+
+#define S_ARG0 4(%esp)
+#define S_ARG1 8(%esp)
+#define S_ARG2 12(%esp)
+#define S_ARG3 16(%esp)
+
+#define FRAME pushl %ebp; movl %esp, %ebp
+#define EMARF leave
+
+#define B_ARG0 8(%ebp)
+#define B_ARG1 12(%ebp)
+#define B_ARG2 16(%ebp)
+#define B_ARG3 20(%ebp)
+
+#ifdef wheeze
+
+#define ALIGN 4
+#define EXT(x) x
+#define LEXT(x) x:
+#define LCL(x) ./**/x
+
+#define LB(x,n) ./**/x
+#define LBb(x,n) ./**/x
+#define LBf(x,n) ./**/x
+
+#define SVC lcall $7,$0
+
+#define String .string
+#define Value .value
+#define Times(a,b) [a\*b]
+#define Divide(a,b) [a\\b]
+
+#define INB inb (%dx)
+#define OUTB outb (%dx)
+#define INL inl (%dx)
+#define OUTL outl (%dx)
+
+#else wheeze
+#define ALIGN
+#define LCL(x) x
+
+#define LB(x,n) n
+#ifdef __STDC__
+#define EXT(x) _ ## x
+#define LEXT(x) _ ## x ## :
+
+#define LBb(x,n) n ## b
+#define LBf(x,n) n ## f
+#else __STDC__
+#define EXT(x) _/**/x
+#define LEXT(x) _/**/x/**/:
+#define LBb(x,n) n/**/b
+#define LBf(x,n) n/**/f
+#endif __STDC__
+#define SVC .byte 0x9a; .long 0; .word 0x7
+
+#define String .ascii
+#define Value .word
+#define Times(a,b) (a*b)
+#define Divide(a,b) (a/b)
+
+#define INB inb %dx, %al
+#define OUTB outb %al, %dx
+#define INL inl %dx, %eax
+#define OUTL outl %eax, %dx
+
+#endif wheeze
+
+#define addr32 .byte 0x67
+#define data32 .byte 0x66
+
+#ifdef GPROF
+#ifdef __STDC__
+
+#define MCOUNT .data; LB(x, 9); .long 0; .text; lea LBb(x, 9),%edx; call mcount
+#define ENTRY(x) .globl EXT(x); .align ALIGN; LEXT(x) ; \
+ pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp;
+#define ENTRY2(x,y) .globl EXT(x); .globl EXT(y); \
+ .align ALIGN; LEXT(x) LEXT(y) ; \
+ pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp;
+#define ASENTRY(x) .globl x; .align ALIGN; x ## : ; \
+ pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp;
+
+#else __STDC__
+
+#define MCOUNT .data; LB(x, 9): .long 0; .text; lea LBb(x, 9),%edx; call mcount
+#define ENTRY(x) .globl EXT(x); .align ALIGN; LEXT(x) ; \
+ pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp;
+#define ENTRY2(x,y) .globl EXT(x); .globl EXT(y); \
+ .align ALIGN; LEXT(x) LEXT(y)
+#define ASENTRY(x) .globl x; .align ALIGN; x: ; \
+ pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp;
+
+#endif __STDC__
+#else GPROF
+#ifdef __STDC__
+
+#define MCOUNT
+#define ENTRY(x) .globl EXT(x); .align ALIGN; LEXT(x)
+#define ENTRY2(x,y) .globl EXT(x); .globl EXT(y); \
+ .align ALIGN; LEXT(x) LEXT(y)
+#define ASENTRY(x) .globl x; .align ALIGN; x ## :
+
+#else __STDC__
+
+#define MCOUNT
+#define ENTRY(x) .globl EXT(x); .align ALIGN; LEXT(x)
+#define ENTRY2(x,y) .globl EXT(x); .globl EXT(y); \
+ .align ALIGN; LEXT(x) LEXT(y)
+#define ASENTRY(x) .globl x; .align ALIGN; x:
+
+#endif __STDC__
+#endif GPROF
+
+#define Entry(x) .globl EXT(x); .align ALIGN; LEXT(x)
+#define DATA(x) .globl EXT(x); .align ALIGN; LEXT(x)
diff --git a/sys/i386/boot/biosboot/bios.S b/sys/i386/boot/biosboot/bios.S
new file mode 100644
index 0000000..687ca18
--- /dev/null
+++ b/sys/i386/boot/biosboot/bios.S
@@ -0,0 +1,329 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:34:26 rpd
+ * $Id$
+ */
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+ .file "bios.s"
+
+#include "asm.h"
+ .text
+
+/*
+# biosread(dev, cyl, head, sec, nsec, offset)
+# Read "nsec" sectors from disk to offset "offset" in boot segment
+# BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
+# Call with %ah = 0x2
+# %al = number of sectors
+# %ch = cylinder
+# %cl = sector
+# %dh = head
+# %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
+# %es:%bx = segment:offset of buffer
+# Return:
+# %al = 0x0 on success; err code on failure
+*/
+
+ENTRY(biosread)
+ push %ebp
+ mov %esp, %ebp
+
+ push %ebx
+ push %ecx
+ push %edx
+ push %es
+
+ movb 0x10(%ebp), %dh
+ movw 0x0c(%ebp), %cx
+ xchgb %ch, %cl # cylinder; the highest 2 bits of cyl is in %cl
+ rorb $2, %cl
+ movb 0x14(%ebp), %al
+ orb %al, %cl
+ incb %cl # sector; sec starts from 1, not 0
+ movb 0x8(%ebp), %dl # device
+ movl 0x1c(%ebp), %ebx # offset
+ # prot_to_real will set %es to BOOTSEG
+
+ call EXT(prot_to_real) # enter real mode
+ movb $0x2, %ah # subfunction
+ addr32
+ movb 0x18(%ebp), %al # number of sectors
+
+ sti
+ int $0x13
+ cli
+
+ mov %eax, %ebx # save return value (actually movw %ax, %bx)
+
+ data32
+ call EXT(real_to_prot) # back to protected mode
+
+ xor %eax, %eax
+ movb %bh, %al # return value in %ax
+
+ pop %es
+ pop %edx
+ pop %ecx
+ pop %ebx
+ pop %ebp
+
+ ret
+
+
+/*
+# putc(ch)
+# BIOS call "INT 10H Function 0Eh" to write character to console
+# Call with %ah = 0x0e
+# %al = character
+# %bh = page
+# %bl = foreground color ( graphics modes)
+*/
+
+
+ENTRY(putc)
+ push %ebp
+ mov %esp, %ebp
+ push %ebx
+ push %ecx
+
+ movb 0x8(%ebp), %cl
+
+ call EXT(prot_to_real)
+
+ data32
+ mov $0x1, %ebx # %bh=0, %bl=1 (blue)
+ movb $0xe, %ah
+ movb %cl, %al
+ sti
+ int $0x10 # display a byte
+ cli
+
+ data32
+ call EXT(real_to_prot)
+
+ pop %ecx
+ pop %ebx
+ pop %ebp
+ ret
+
+
+/*
+# getc()
+# BIOS call "INT 16H Function 00H" to read character from keyboard
+# Call with %ah = 0x0
+# Return: %ah = keyboard scan code
+# %al = ASCII character
+*/
+
+ENTRY(getc)
+ push %ebp
+ mov %esp, %ebp
+ push %ebx # save %ebx
+
+ call EXT(prot_to_real)
+
+ movb $0x0, %ah
+ sti
+ int $0x16
+ cli
+
+ movb %al, %bl # real_to_prot uses %eax
+
+ data32
+ call EXT(real_to_prot)
+
+ xor %eax, %eax
+ movb %bl, %al
+
+ pop %ebx
+ pop %ebp
+ ret
+/*
+# ischar()
+# if there is a character pending, return it; otherwise return 0
+# BIOS call "INT 16H Function 01H" to check whether a character is pending
+# Call with %ah = 0x1
+# Return:
+# If key waiting to be input:
+# %ah = keyboard scan code
+# %al = ASCII character
+# Zero flag = clear
+# else
+# Zero flag = set
+*/
+ENTRY(ischar)
+ push %ebp
+ mov %esp, %ebp
+ push %ebx
+
+ call EXT(prot_to_real) # enter real mode
+
+ xor %ebx, %ebx
+ movb $0x1, %ah
+ sti
+ int $0x16
+ cli
+ data32
+ jz nochar
+ movb %al, %bl
+
+nochar:
+ data32
+ call EXT(real_to_prot)
+
+ xor %eax, %eax
+ movb %bl, %al
+
+ pop %ebx
+ pop %ebp
+ ret
+
+/*
+#
+# get_diskinfo(): return a word that represents the
+# max number of sectors and heads and drives for this device
+#
+*/
+
+ENTRY(get_diskinfo)
+ push %ebp
+ mov %esp, %ebp
+ push %es
+ push %ebx
+ push %ecx
+ push %edx
+
+ movb 0x8(%ebp), %dl # diskinfo(drive #)
+ call EXT(prot_to_real) # enter real mode
+
+ movb $0x8, %ah # ask for disk info
+
+ sti
+ int $0x13
+ cli
+
+ jnc ok
+ /*
+ * Urk. Call failed. It is not supported for floppies by old BIOS's.
+ * Guess it's a 15-sector floppy. Initialize all the registers for
+ * documentation, although we only need head and sector counts.
+ */
+ subb %ah, %ah # %ax = 0
+ movb %al, %al
+ movb %ah, %bh # %bh = 0
+ movb $2, %bl # %bl bits 0-3 = drive type, 2 = 1.2M
+ movb $79, %ch # max track
+ movb $15, %cl # max sector
+ movb $1, %dh # max head
+ movb $1, %dl # # floppy drives installed
+ # es:di = parameter table
+ # carry = 0
+ok:
+
+ data32
+ call EXT(real_to_prot) # back to protected mode
+
+ xor %eax, %eax
+
+ /*form a longword representing all this gunk*/
+ movb %dh, %ah # max head
+ andb $0x3f, %cl # mask of cylinder gunk
+ movb %cl, %al # max sector (and # sectors)
+
+ pop %edx
+ pop %ecx
+ pop %ebx
+ pop %es
+ pop %ebp
+ ret
+
+/*
+#
+# memsize(i) : return the memory size in KB. i == 0 for conventional memory,
+# i == 1 for extended memory
+# BIOS call "INT 12H" to get conventional memory size
+# BIOS call "INT 15H, AH=88H" to get extended memory size
+# Both have the return value in AX.
+#
+*/
+
+ENTRY(memsize)
+ push %ebp
+ mov %esp, %ebp
+ push %ebx
+
+ mov 8(%ebp), %ebx
+
+ call EXT(prot_to_real) # enter real mode
+
+ cmpb $0x1, %bl
+ data32
+ je xext
+
+ sti
+ int $0x12
+ cli
+ data32
+ jmp xdone
+
+xext: movb $0x88, %ah
+ sti
+ int $0x15
+ cli
+
+xdone:
+ mov %eax, %ebx
+
+ data32
+ call EXT(real_to_prot)
+
+ mov %ebx, %eax
+ pop %ebx
+ pop %ebp
+ ret
diff --git a/sys/i386/boot/biosboot/boot.c b/sys/i386/boot/biosboot/boot.c
new file mode 100644
index 0000000..e186bdc
--- /dev/null
+++ b/sys/i386/boot/biosboot/boot.c
@@ -0,0 +1,308 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, [92/04/03 16:51:14 rvb]
+ * $Id: boot.c,v 1.13 1994/06/14 07:31:42 rgrimes Exp $
+ */
+
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <sys/param.h>
+#include "boot.h"
+#include <a.out.h>
+#include <sys/reboot.h>
+
+struct exec head;
+int argv[10], esym;
+char *name;
+char *names[] = {
+ "/386bsd", "/o386bsd", "/386bsd.old"
+};
+#define NUMNAMES (sizeof(names)/sizeof(char *))
+
+extern int end;
+boot(drive)
+int drive;
+{
+ int loadflags, currname = 0;
+ char *t;
+
+ printf("\n>> FreeBSD BOOT @ 0x%x: %d/%d k of memory [%s]\n",
+ ouraddr,
+ argv[7] = memsize(0),
+ argv[8] = memsize(1),
+ "$Revision: 1.14 $");
+ printf("use hd(1,a)/386bsd to boot sd0 when wd0 is also installed\n");
+ gateA20();
+loadstart:
+ /***************************************************************\
+ * As a default set it to the first partition of the first *
+ * floppy or hard drive *
+ \***************************************************************/
+ part = unit = 0;
+ maj = (drive&0x80 ? 0 : 2); /* a good first bet */
+ name = names[currname++];
+
+ loadflags = 0;
+ if (currname == NUMNAMES)
+ currname = 0;
+ getbootdev(&loadflags);
+ if (openrd()) {
+ printf("Can't find %s\n", name);
+ goto loadstart;
+ }
+/* if (inode.i_mode&IEXEC)
+ loadflags |= RB_KDB;
+*/
+ loadprog(loadflags);
+ goto loadstart;
+}
+
+loadprog(howto)
+ int howto;
+{
+ long int startaddr;
+ long int addr; /* physical address.. not directly useable */
+ long int addr0;
+ int i;
+ static int (*x_entry)() = 0;
+ unsigned char tmpbuf[4096]; /* we need to load the first 4k here */
+
+ argv[3] = 0;
+ argv[4] = 0;
+ read(&head, sizeof(head));
+ if ( N_BADMAG(head)) {
+ printf("Invalid format!\n");
+ return;
+ }
+
+ poff = N_TXTOFF(head);
+ /*if(poff==0)
+ poff = 32;*/
+
+ startaddr = (int)head.a_entry;
+ addr = (startaddr & 0x00ffffff); /* some MEG boundary */
+ addr0 = addr;
+ printf("Booting %s(%d,%c)%s @ 0x%x\n"
+ , devs[maj]
+ , unit
+ , 'a'+part
+ , name
+ , addr);
+ if(addr < ouraddr)
+ {
+ if((addr + head.a_text + head.a_data) > ouraddr)
+ {
+ printf("kernel overlaps loader\n");
+ return;
+ }
+ if((addr + head.a_text + head.a_data + head.a_bss) > 0xa0000)
+ {
+ printf("bss exceeds 640k limit\n");
+ return;
+ }
+ }
+ printf("text=0x%x ", head.a_text);
+ /********************************************************/
+ /* LOAD THE TEXT SEGMENT */
+ /* don't clobber the first 4k yet (BIOS NEEDS IT) */
+ /********************************************************/
+ read(tmpbuf,4096);
+ addr += 4096;
+ xread(addr, head.a_text - 4096);
+ addr += head.a_text - 4096;
+
+ /********************************************************/
+ /* Load the Initialised data after the text */
+ /********************************************************/
+ while (addr & CLOFSET)
+ *(char *)addr++ = 0;
+
+ printf("data=0x%x ", head.a_data);
+ xread(addr, head.a_data);
+ addr += head.a_data;
+
+ /********************************************************/
+ /* Skip over the uninitialised data */
+ /* (but clear it) */
+ /********************************************************/
+ printf("bss=0x%x ", head.a_bss);
+ if( (addr < ouraddr) && ((addr + head.a_bss) > ouraddr))
+ {
+ pbzero(addr,ouraddr - (int)addr);
+ }
+ else
+ {
+ pbzero(addr,head.a_bss);
+ }
+ argv[3] = (addr += head.a_bss);
+
+#ifdef LOADSYMS /* not yet, haven't worked this out yet */
+ if (addr > 0x100000)
+ {
+ /********************************************************/
+ /*copy in the symbol header */
+ /********************************************************/
+ pcpy(&head.a_syms, addr, sizeof(head.a_syms));
+ addr += sizeof(head.a_syms);
+
+ /********************************************************/
+ /* READ in the symbol table */
+ /********************************************************/
+ printf("symbols=[+0x%x", head.a_syms);
+ xread(addr, head.a_syms);
+ addr += head.a_syms;
+
+ /********************************************************/
+ /* Followed by the next integer (another header) */
+ /* more debug symbols? */
+ /********************************************************/
+ read(&i, sizeof(int));
+ pcpy(&i, addr, sizeof(int));
+ i -= sizeof(int);
+ addr += sizeof(int);
+
+
+ /********************************************************/
+ /* and that many bytes of (debug symbols?) */
+ /********************************************************/
+ printf("+0x%x] ", i);
+ xread(addr, i);
+ addr += i;
+ }
+#endif LOADSYMS
+ /********************************************************/
+ /* and note the end address of all this */
+ /********************************************************/
+
+ argv[4] = ((addr+sizeof(int)-1))&~(sizeof(int)-1);
+ printf("total=0x%x ",argv[4]);
+
+
+ /*
+ * We now pass the various bootstrap parameters to the loaded
+ * image via the argument list
+ * (THIS IS A BIT OF HISTORY FROM MACH.. LEAVE FOR NOW)
+ * arg1 = boot flags
+ * arg2 = boot device
+ * arg3 = start of symbol table (0 if not loaded)
+ * arg4 = end of symbol table (0 if not loaded)
+ * arg5 = transfer address from image
+ * arg6 = transfer address for next image pointer
+ */
+ switch(maj)
+ {
+ case 2:
+ printf("\n\nInsert file system floppy in drive A or B\n");
+ printf("Press 'A', 'B' or any other key for the default ");
+ printf("%c: ", unit+'A');
+ i = getchar();
+ if (i=='0' || i=='A' || i=='a')
+ unit = 0;
+ if (i=='1' || i=='B' || i=='b')
+ unit = 1;
+ printf("\n");
+ break;
+ case 4:
+ break;
+ }
+ argv[1] = howto;
+ argv[2] = (MAKEBOOTDEV(maj, 0, 0, unit, part)) ;
+ argv[5] = (head.a_entry &= 0xfffffff);
+ argv[6] = (int) &x_entry;
+ argv[0] = 8;
+ /****************************************************************/
+ /* copy that first page and overwrite any BIOS variables */
+ /****************************************************************/
+ printf("entry point=0x%x\n" ,((int)startaddr) & 0xffffff);
+ /* Under no circumstances overwrite precious BIOS variables! */
+ pcpy(tmpbuf, addr0, 0x400);
+ pcpy(tmpbuf + 0x500, addr0 + 0x500, 4096 - 0x500);
+ startprog(((int)startaddr & 0xffffff),argv);
+}
+
+char namebuf[100];
+getbootdev(howto)
+ int *howto;
+{
+ char c, *ptr = namebuf;
+ printf("Boot: [[[%s(%d,%c)]%s][-s][-a][-d]] :- "
+ , devs[maj]
+ , unit
+ , 'a'+part
+ , name);
+ if (gets(namebuf)) {
+ while (c=*ptr) {
+ while (c==' ')
+ c = *++ptr;
+ if (!c)
+ return;
+ if (c=='-')
+ while ((c = *++ptr) && c!=' ')
+ switch (c) {
+ case 'r':
+ *howto |= RB_DFLTROOT; continue;
+ case 'a':
+ *howto |= RB_ASKNAME; continue;
+ case 's':
+ *howto |= RB_SINGLE; continue;
+ case 'd':
+ *howto |= RB_KDB; continue;
+ case 'b':
+ *howto |= RB_HALT; continue;
+ }
+ else {
+ name = ptr;
+ while ((c = *++ptr) && c!=' ');
+ if (c)
+ *ptr++ = 0;
+ }
+ }
+ } else
+ printf("\n");
+}
+
diff --git a/sys/i386/boot/biosboot/boot.h b/sys/i386/boot/biosboot/boot.h
new file mode 100644
index 0000000..f77f882
--- /dev/null
+++ b/sys/i386/boot/biosboot/boot.h
@@ -0,0 +1,40 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:35:03 rpd
+ * $Id$
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <ufs/quota.h>
+#include <ufs/fs.h>
+#include <ufs/inode.h>
+
+extern char *devs[], *name, *iodest;
+extern struct fs *fs;
+extern struct inode inode;
+extern int dosdev, unit, part, maj, boff, poff, bnum, cnt;
+extern long int ouraddr;
diff --git a/sys/i386/boot/biosboot/boot2.S b/sys/i386/boot/biosboot/boot2.S
new file mode 100644
index 0000000..f78ce10
--- /dev/null
+++ b/sys/i386/boot/biosboot/boot2.S
@@ -0,0 +1,177 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:35:26 rpd
+ * $Id: boot2.S,v 1.3 1993/11/13 04:43:25 rgrimes Exp $
+ */
+
+#include "asm.h"
+
+/* Conventional GDT indexes. */
+#define BOOT_CS_INDEX 3
+#define BOOT_CS16_INDEX 5
+#define BOOT_DS_INDEX 4
+#define DB_CS_INDEX 14
+#define DB_CS16_INDEX 15
+#define DB_DS_INDEX 16
+#define GDT_INDEX 17
+
+/* Vector numbers. */
+#define BREAKPOINT_VECTOR 3
+#define DEBUG_VECTOR 1
+
+/*
+ * boot2() -- second stage boot
+ */
+
+.globl EXT(ouraddr)
+
+ENTRY(boot2)
+ data32
+ subl %eax, %eax
+ mov %cs, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ data32
+ shll $4, %eax
+ addr32
+ data32
+ movl %eax, EXT(ouraddr)
+
+ /* fix up GDT entries for bootstrap */
+#define FIXUP(gdt_index) \
+ addr32; \
+ movl %eax, EXT(Gdt)+(8*gdt_index)+2; /* actually movw %ax */ \
+ addr32; \
+ movb %bl, EXT(Gdt)+(8*gdt_index)+4
+
+ data32
+ shld $16, %eax, %ebx
+ FIXUP(BOOT_CS_INDEX)
+ FIXUP(BOOT_CS16_INDEX)
+ FIXUP(BOOT_DS_INDEX)
+
+ /* fix up GDT entry for GDT, and GDT and IDT pointers */
+ data32
+ movl %eax, %ecx
+ data32
+ addl $ EXT(Gdt), %eax
+ data32
+ shld $16, %eax, %ebx
+ FIXUP(GDT_INDEX)
+ addr32
+ data32
+ movl %eax, EXT(Gdtr)+2
+ data32
+ addl $ EXT(Idt), %ecx
+ addr32
+ data32
+ movl %ecx, EXT(Idtr_prot)+2
+
+ /* %es = vector table segment for a while */
+ push %es
+ data32
+ subl %eax, %eax
+ mov %ax, %es
+
+ /* fix up GDT entries for bdb */
+ data32
+ movl $4*DEBUG_VECTOR, %esi
+ addr32
+ movl %es: 2(%esi), %eax /* actually movw to %ax */
+ data32
+ shll $4, %eax
+ data32
+ shld $16, %eax, %ebx
+ FIXUP(DB_CS_INDEX)
+ FIXUP(DB_CS16_INDEX)
+ FIXUP(DB_DS_INDEX)
+
+ /* Fetch entry points of bdb's protected mode trap handlers. These
+ * are stored at 2 before the corresponding entry points for real mode.
+ */
+ data32
+ subl %ebx, %ebx
+ addr32
+ movl %es: (%esi), %ebx /* actually movw to %bx */
+ data32
+ subl %ecx, %ecx
+ addr32
+ movl %es: 4*(BREAKPOINT_VECTOR-DEBUG_VECTOR)(%esi), %ecx
+ /* actually movw to %cx */
+
+ /* %es = bdb segment for a while */
+ data32
+ shrl $4, %eax
+ mov %ax, %es
+
+ /* fix up IDT entries for bdb */
+ data32
+ subl $2, %ebx /* calculate EA to check it */
+ jb 1f /* give up if it would trap */
+ addr32
+ movl %es: (%ebx), %eax /* actually movw to %ax */
+ addr32
+ movl %eax, EXT(Idt)+8*DEBUG_VECTOR /* actually movw %ax */
+1:
+ data32
+ subl $2, %ecx
+ jb 1f
+ addr32
+ movl %es: (%ecx), %eax /* actually movw to %ax */
+ addr32
+ movl %eax, EXT(Idt)+8*BREAKPOINT_VECTOR /* actually movw %ax */
+1:
+
+ /* finished with groping in real mode segments */
+ pop %es
+
+ /* change to protected mode */
+ data32
+ call EXT(real_to_prot)
+
+ /* clear the bss */
+ movl $ EXT(edata), %edi /* no EXT(_edata) - krufty ld */
+ movl $ EXT(end), %ecx /* or EXT(_end) */
+ subl %edi, %ecx
+ subb %al, %al
+ rep
+ stosb
+
+ movzbl %dl, %edx /* discard head (%dh) and random high bits */
+ pushl %edx
+ call EXT(boot)
+oops:
+ hlt
+ jmp oops
+
+ .data
+ .align 2
+#if 0 /* XXX this would give losing "_ouraddr :". Better declared in C */
+EXT(ouraddr):
+#else
+_ouraddr:
+#endif
+ .long 0
diff --git a/sys/i386/boot/biosboot/disk.c b/sys/i386/boot/biosboot/disk.c
new file mode 100644
index 0000000..a63ca5a
--- /dev/null
+++ b/sys/i386/boot/biosboot/disk.c
@@ -0,0 +1,281 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:35:49 rpd
+ * $Id: disk.c,v 1.4 1994/02/22 22:59:40 rgrimes Exp $
+ */
+
+/*
+ * 93/10/08 bde
+ * If there is no 386BSD partition, initialize the label sector with
+ * LABELSECTOR instead of with garbage.
+ *
+ * 93/08/22 bde
+ * Fixed reading of bad sector table. It is at the end of the 'c'
+ * partition, which is not always at the end of the disk.
+ */
+
+#include "boot.h"
+#ifdef DO_BAD144
+#include <sys/dkbad.h>
+#endif DO_BAD144
+#include <sys/disklabel.h>
+
+#define BIOS_DEV_FLOPPY 0x0
+#define BIOS_DEV_WIN 0x80
+
+#define BPS 512
+#define SPT(di) ((di)&0xff)
+#define HEADS(di) ((((di)>>8)&0xff)+1)
+
+char *devs[] = {"wd", "hd", "fd", "wt", "sd", 0};
+
+#ifdef DO_BAD144
+struct dkbad dkb;
+int do_bad144;
+int bsize;
+#endif DO_BAD144
+
+int spt, spc;
+
+char *iodest;
+struct fs *fs;
+struct inode inode;
+int dosdev, unit, part, maj, boff, poff, bnum, cnt;
+
+/*#define EMBEDDED_DISKLABEL 1*/
+extern struct disklabel disklabel;
+/*struct disklabel disklabel;*/
+
+devopen()
+{
+ struct dos_partition *dptr;
+ struct disklabel *dl;
+ int dosdev = inode.i_dev;
+ int i, sector, di;
+
+ di = get_diskinfo(dosdev);
+ spc = (spt = SPT(di)) * HEADS(di);
+ if (dosdev == 2)
+ {
+ boff = 0;
+ part = (spt == 15 ? 3 : 1);
+ }
+ else
+ {
+#ifdef EMBEDDED_DISKLABEL
+ dl = &disklabel;
+#else EMBEDDED_DISKLABEL
+ Bread(dosdev, 0);
+ dptr = (struct dos_partition *)(((char *)0)+DOSPARTOFF);
+ sector = LABELSECTOR;
+ for (i = 0; i < NDOSPART; i++, dptr++)
+ if (dptr->dp_typ == DOSPTYP_386BSD) {
+ sector = dptr->dp_start + LABELSECTOR;
+ break;
+ }
+ Bread(dosdev, sector++);
+ dl=((struct disklabel *)0);
+ disklabel = *dl; /* structure copy (maybe useful later)*/
+#endif EMBEDDED_DISKLABEL
+ if (dl->d_magic != DISKMAGIC) {
+ printf("bad disklabel");
+ return 1;
+ }
+ if( (maj == 4) || (maj == 0) || (maj == 1))
+ {
+ if (dl->d_type == DTYPE_SCSI)
+ {
+ maj = 4; /* use scsi as boot dev */
+ }
+ else
+ {
+ maj = 0; /* must be ESDI/IDE */
+ }
+ }
+ boff = dl->d_partitions[part].p_offset;
+#ifdef DO_BAD144
+ bsize = dl->d_partitions[part].p_size;
+ do_bad144 = 0;
+ if (dl->d_flags & D_BADSECT) {
+ /* this disk uses bad144 */
+ int i;
+ int dkbbnum;
+ struct dkbad *dkbptr;
+
+ /* find the first readable bad sector table */
+ /* some of this code is copied from ufs/ufs_disksubr.c */
+ /* including the bugs :-( */
+ /* read a bad sector table */
+
+#define BAD144_PART 2 /* XXX scattered magic numbers */
+#define BSD_PART 0 /* XXX should be 2 but bad144.c uses 0 */
+ if (dl->d_partitions[BSD_PART].p_offset != 0)
+ dkbbnum = dl->d_partitions[BAD144_PART].p_offset
+ + dl->d_partitions[BAD144_PART].p_size;
+ else
+ dkbbnum = dl->d_secperunit;
+ dkbbnum -= dl->d_nsectors;
+
+ if (dl->d_secsize > DEV_BSIZE)
+ dkbbnum *= dl->d_secsize / DEV_BSIZE;
+ else
+ dkbbnum /= DEV_BSIZE / dl->d_secsize;
+ i = 0;
+ do_bad144 = 0;
+ do {
+ /* XXX: what if the "DOS sector" < 512 bytes ??? */
+ Bread(dosdev, dkbbnum + i);
+ dkbptr = (struct dkbad *) 0;
+/* XXX why is this not in <sys/dkbad.h> ??? */
+#define DKBAD_MAGIC 0x4321
+ if (dkbptr->bt_mbz == 0 &&
+ dkbptr->bt_flag == DKBAD_MAGIC) {
+ dkb = *dkbptr; /* structure copy */
+ do_bad144 = 1;
+ break;
+ }
+ i += 2;
+ } while (i < 10 && i < dl->d_nsectors);
+ if (!do_bad144)
+ printf("Bad bad sector table\n");
+ else
+ printf("Using bad sector table at %d\n", dkbbnum+i);
+ }
+#endif DO_BAD144
+ }
+ return 0;
+}
+
+devread()
+{
+ int offset, sector = bnum;
+ int dosdev = inode.i_dev;
+ for (offset = 0; offset < cnt; offset += BPS)
+ {
+ Bread(dosdev, badsect(dosdev, sector++));
+ bcopy(0, iodest+offset, BPS);
+ }
+}
+
+#define I_ADDR ((void *) 0) /* XXX where all reads go */
+
+/* Read ahead buffer large enough for one track on a 1440K floppy. For
+ * reading from floppies, the bootstrap has to be loaded on a 64K boundary
+ * to ensure that this buffer doesn't cross a 64K DMA boundary.
+ */
+#define RA_SECTORS 18
+static char ra_buf[RA_SECTORS * BPS];
+static int ra_dev;
+static int ra_end;
+static int ra_first;
+
+Bread(dosdev,sector)
+ int dosdev,sector;
+{
+ if (dosdev != ra_dev || sector < ra_first || sector >= ra_end)
+ {
+ int cyl, head, sec, nsec;
+
+ cyl = sector/spc;
+ head = (sector % spc) / spt;
+ sec = sector % spt;
+ nsec = spt - sec;
+ if (nsec > RA_SECTORS)
+ nsec = RA_SECTORS;
+ twiddle();
+ if (biosread(dosdev, cyl, head, sec, nsec, ra_buf) != 0)
+ {
+ nsec = 1;
+ twiddle();
+ while (biosread(dosdev, cyl, head, sec, nsec, ra_buf) != 0) {
+ printf("Error: C:%d H:%d S:%d\n", cyl, head, sec);
+ twiddle();
+ }
+ }
+ ra_dev = dosdev;
+ ra_first = sector;
+ ra_end = sector + nsec;
+ }
+ bcopy(ra_buf + (sector - ra_first) * BPS, I_ADDR, BPS);
+}
+
+badsect(dosdev, sector)
+ int dosdev, sector;
+{
+ int i;
+#ifdef DO_BAD144
+ if (do_bad144) {
+ u_short cyl;
+ u_short head;
+ u_short sec;
+ int newsec;
+ struct disklabel *dl = &disklabel;
+
+ /* XXX */
+ /* from wd.c */
+ /* bt_cyl = cylinder number in sorted order */
+ /* bt_trksec is actually (head << 8) + sec */
+
+ /* only remap sectors in the partition */
+ if (sector < boff || sector >= boff + bsize) {
+ goto no_remap;
+ }
+
+ cyl = sector / dl->d_secpercyl;
+ head = (sector % dl->d_secpercyl) / dl->d_nsectors;
+ sec = sector % dl->d_nsectors;
+ sec = (head<<8) + sec;
+
+ /* now, look in the table for a possible bad sector */
+ for (i=0; i<126; i++) {
+ if (dkb.bt_bad[i].bt_cyl == cyl) {
+ /* found same cylinder */
+ if (dkb.bt_bad[i].bt_trksec == sec) {
+ /* FOUND! */
+ break;
+ }
+ } else if (dkb.bt_bad[i].bt_cyl > cyl) {
+ i = 126;
+ break;
+ }
+ }
+ if (i == 126) {
+ /* didn't find bad sector */
+ goto no_remap;
+ }
+ /* otherwise find replacement sector */
+ if (dl->d_partitions[BSD_PART].p_offset != 0)
+ newsec = dl->d_partitions[BAD144_PART].p_offset
+ + dl->d_partitions[BAD144_PART].p_size;
+ else
+ newsec = dl->d_secperunit;
+ newsec -= dl->d_nsectors + i + 1;
+ return newsec;
+ }
+#endif DO_BAD144
+ no_remap:
+ return sector;
+}
diff --git a/sys/i386/boot/biosboot/io.c b/sys/i386/boot/biosboot/io.c
new file mode 100644
index 0000000..ee441f0
--- /dev/null
+++ b/sys/i386/boot/biosboot/io.c
@@ -0,0 +1,214 @@
+
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:35:57 rpd
+ * $Id: io.c,v 1.5 1994/06/15 19:09:14 jkh Exp $
+ */
+
+#include <i386/include/pio.h>
+
+#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */
+#define K_STATUS 0x64 /* keyboard status */
+#define K_CMD 0x64 /* keybd ctlr command (write-only) */
+
+#define K_OBUF_FUL 0x01 /* output buffer full */
+#define K_IBUF_FUL 0x02 /* input buffer full */
+
+#define KC_CMD_WIN 0xd0 /* read output port */
+#define KC_CMD_WOUT 0xd1 /* write output port */
+#define KB_A20 0xdf /* enable A20,
+ enable output buffer full interrupt
+ enable data line
+ enable clock line */
+
+/*
+ * Gate A20 for high memory
+ */
+unsigned char x_20 = KB_A20;
+gateA20()
+{
+#ifdef IBM_L40
+ outb(0x92, 0x2);
+#else IBM_L40
+ while (inb(K_STATUS) & K_IBUF_FUL);
+ while (inb(K_STATUS) & K_OBUF_FUL)
+ (void)inb(K_RDWR);
+
+ outb(K_CMD, KC_CMD_WOUT);
+ while (inb(K_STATUS) & K_IBUF_FUL);
+ outb(K_RDWR, x_20);
+ while (inb(K_STATUS) & K_IBUF_FUL);
+#endif IBM_L40
+}
+
+/* printf - only handles %d as decimal, %c as char, %s as string */
+
+printf(format,data)
+ char *format;
+ int data;
+{
+ int *dataptr = &data;
+ char c;
+
+ reset_twiddle();
+ while (c = *format++)
+ if (c != '%')
+ putchar(c);
+ else
+ switch (c = *format++) {
+ case 'd': {
+ int num = *dataptr++;
+ char buf[10], *ptr = buf;
+ if (num<0) {
+ num = -num;
+ putchar('-');
+ }
+ do
+ *ptr++ = '0'+num%10;
+ while (num /= 10);
+ do
+ putchar(*--ptr);
+ while (ptr != buf);
+ break;
+ }
+ case 'x': {
+ int num = *dataptr++, dig;
+ char buf[8], *ptr = buf;
+ do
+ *ptr++ = (dig=(num&0xf)) > 9?
+ 'a' + dig - 10 :
+ '0' + dig;
+ while (num >>= 4);
+ do
+ putchar(*--ptr);
+ while (ptr != buf);
+ break;
+ }
+ case 'c': putchar((*dataptr++)&0xff); break;
+ case 's': {
+ char *ptr = (char *)*dataptr++;
+ while (c = *ptr++)
+ putchar(c);
+ break;
+ }
+ }
+}
+
+putchar(c)
+{
+ if (c == '\n')
+ putc('\r');
+ putc(c);
+}
+
+getchar()
+{
+ int c;
+
+ if ((c=getc()) == '\r')
+ c = '\n';
+ if (c == '\b') {
+ putchar('\b');
+ putchar(' ');
+ }
+ putchar(c);
+ return(c);
+}
+
+#if BOOTWAIT
+spinwait(i)
+int i;
+{
+ while (--i >= 0)
+ (void)inb(0x84);
+}
+#endif
+
+gets(buf)
+char *buf;
+{
+ int i;
+ char *ptr=buf;
+
+#if BOOTWAIT
+ for (i = BOOTWAIT; i>0; spinwait(10000),i--)
+#endif
+ if (ischar())
+ for (;;)
+ switch(*ptr = getchar() & 0xff) {
+ case '\n':
+ case '\r':
+ *ptr = '\0';
+ return 1;
+ case '\b':
+ if (ptr > buf) ptr--;
+ continue;
+ default:
+ ptr++;
+ }
+ return 0;
+}
+
+strcmp(s1, s2)
+char *s1, *s2;
+{
+ while (*s1 == *s2) {
+ if (!*s1++)
+ return 0;
+ s2++;
+ }
+ return 1;
+}
+
+bcopy(from, to, len)
+char *from, *to;
+int len;
+{
+ while (len-- > 0)
+ *to++ = *from++;
+}
+
+static int tw_on;
+static int tw_pos;
+static char tw_chars[] = "|/-\\";
+
+reset_twiddle()
+{
+ if (tw_on)
+ putchar('\b');
+ tw_on = 0;
+ tw_pos = 0;
+}
+
+twiddle()
+{
+ if (tw_on)
+ putchar('\b');
+ else
+ tw_on = 1;
+ putchar(tw_chars[tw_pos++]);
+ tw_pos %= (sizeof(tw_chars) - 1);
+}
diff --git a/sys/i386/boot/biosboot/start.S b/sys/i386/boot/biosboot/start.S
new file mode 100644
index 0000000..aa65212
--- /dev/null
+++ b/sys/i386/boot/biosboot/start.S
@@ -0,0 +1,292 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:36:29 rpd
+ * $Id: start.S,v 1.2 1993/10/16 19:11:38 rgrimes Exp $
+ */
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+#include "asm.h"
+
+ .file "start.s"
+
+BOOTSEG = 0x9000 # boot will be loaded here (below 640K)
+BOOTSTACK = 0xe000 # boot stack
+SIGNATURE = 0xaa55
+LOADSZ = 15 # size of unix boot
+PARTSTART = 0x1be # starting address of partition table
+NUMPART = 4 # number of partitions in partition table
+PARTSZ = 16 # each partition table entry is 16 bytes
+BSDPART = 0xA5 # value of boot_ind, means bootable partition
+BOOTABLE = 0x80 # value of boot_ind, means bootable partition
+
+ .text
+
+ENTRY(boot1)
+ # start (aka boot1) is loaded at 0x0:0x7c00 but we want 0x7c0:0
+ # ljmp to the next instruction to adjust %cs
+ data32
+ ljmp $0x7c0, $start
+
+start:
+ # set up %ds
+ mov %cs, %ax
+ mov %ax, %ds
+
+ # set up %ss and %esp
+ data32
+ mov $BOOTSEG, %eax
+ mov %ax, %ss
+ data32
+ mov $BOOTSTACK, %esp
+
+ /*** set up %es, (where we will load boot2 to) ***/
+ mov %ax, %es
+
+#ifdef DEBUG
+ data32
+ mov $one, %esi
+ data32
+ call message
+#endif
+
+ # bootstrap passes us drive number in %dl
+ cmpb $0x80, %dl
+ data32
+ jae hd
+
+fd:
+ mov $0x0, %dl
+# reset the disk system
+#ifdef DEBUG
+ data32
+ mov $two, %esi
+ data32
+ call message
+#endif
+ movb $0x0, %ah
+ int $0x13
+ data32
+ mov $0x0001, %ecx # cyl 0, sector 1
+ movb $0, %dh # head
+#ifdef DEBUG
+ data32
+ mov $three, %esi
+ data32
+ call message
+#endif
+ data32
+ jmp load
+
+hd: /**** load sector 0 into the BOOTSEG ****/
+#ifdef DEBUG
+ data32
+ mov $four, %esi
+ data32
+ call message
+#endif
+ data32
+ mov $0x0201, %eax
+ xor %ebx, %ebx # %bx = 0
+ data32
+ mov $0x0001, %ecx
+#ifdef DEBUG
+ data32
+ mov $five, %esi
+ data32
+ call message
+#endif
+ data32
+ andl $0xff, %edx
+ /*mov $0x0080, %edx*/
+ int $0x13
+ data32
+ jb read_error
+
+ /***# find the first 386BSD partition *****/
+ data32
+ mov $PARTSTART, %ebx
+ data32
+ mov $NUMPART, %ecx
+again:
+ addr32
+ movb %es:4(%ebx), %al
+ cmpb $BSDPART, %al
+ data32
+ je found
+ data32
+ add $PARTSZ, %ebx
+ data32
+ loop again
+ data32
+ mov $enoboot, %esi
+ data32
+ jmp err_stop
+
+
+/*
+# BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
+# Call with %ah = 0x2
+# %al = number of sectors
+# %ch = cylinder
+# %cl = sector
+# %dh = head
+# %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
+# %es:%bx = segment:offset of buffer
+# Return:
+# %al = 0x0 on success; err code on failure
+*/
+
+found:
+ addr32
+ movb %es:1(%ebx), %dh /* head */
+ addr32
+ movl %es:2(%ebx), %ecx /*sect, cyl (+ 2 bytes junk in top word) */
+
+load:
+ movb $0x2, %ah /* function 2 */
+ movb $LOADSZ, %al /* number of blocks */
+ xor %ebx, %ebx /* %bx = 0, put it at 0 in the BOOTSEG */
+ int $0x13
+ data32
+ jb read_error
+
+ # ljmp to the second stage boot loader (boot2).
+ # After ljmp, %cs is BOOTSEG and boot1 (512 bytes) will be used
+ # as an internal buffer "intbuf".
+
+#ifdef DEBUG
+ data32
+ mov $six, %esi
+ data32
+ call message
+#endif
+ data32
+ ljmp $BOOTSEG, $ EXT(boot2)
+
+#
+# read_error
+#
+
+read_error:
+ data32
+ mov $eread, %esi
+err_stop:
+ data32
+ call message
+ data32
+ jmp stop
+
+#
+# message: write the error message in %ds:%esi to console
+#
+
+message:
+/*
+ # Use BIOS "int 10H Function 0Eh" to write character in teletype mode
+ # %ah = 0xe %al = character
+ # %bh = page %bl = foreground color (graphics modes)
+*/
+
+ data32
+ push %eax
+ data32
+ push %ebx
+ data32
+ mov $0x0001, %ebx
+ cld
+
+nextb:
+ lodsb # load a byte into %al
+ cmpb $0x0, %al
+ data32
+ je done
+ movb $0xe, %ah
+ int $0x10 # display a byte
+ data32
+ jmp nextb
+done:
+ data32
+ pop %ebx
+ data32
+ pop %eax
+ data32
+ ret
+
+stop: hlt
+ data32
+ jmp stop # halt doesnt actually halt forever
+
+/* error messages */
+
+#ifdef DEBUG
+one: String "1\r\n\0"
+two: String "2\r\n\0"
+three: String "3\r\n\0"
+four: String "4\r\n\0"
+five: String "5\r\n\0"
+six: String "6\r\n\0"
+seven: String "7\r\n\0"
+#endif DEBUG
+eread: String "Read error\r\n\0"
+enoboot: String "No bootable partition\r\n\0"
+endofcode:
+/* throw in a partition in case we are block0 as well */
+/* flag, head, sec, cyl, typ, ehead, esect, ecyl, start, len */
+ . = EXT(boot1) + PARTSTART
+ .byte 0x0,0,0,0,0,0,0,0
+ .long 0,0
+ .byte 0x0,0,0,0,0,0,0,0
+ .long 0,0
+ .byte 0x0,0,0,0,0,0,0,0
+ .long 0,0
+ .byte BOOTABLE,0,1,0,BSDPART,255,255,255
+ .long 0,50000
+/* the last 2 bytes in the sector 0 contain the signature */
+ . = EXT(boot1) + 0x1fe
+ .value SIGNATURE
+ENTRY(disklabel)
+ . = EXT(boot1) + 0x400
diff --git a/sys/i386/boot/biosboot/sys.c b/sys/i386/boot/biosboot/sys.c
new file mode 100644
index 0000000..4ffe171
--- /dev/null
+++ b/sys/i386/boot/biosboot/sys.c
@@ -0,0 +1,232 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:36:34 rpd
+ * $Id$
+ */
+
+#include "boot.h"
+#include <sys/dir.h>
+#include <sys/reboot.h>
+
+/* #define BUFSIZE 4096 */
+#define BUFSIZE MAXBSIZE
+
+char buf[BUFSIZE], fsbuf[SBSIZE], iobuf[MAXBSIZE];
+
+int xread(addr, size)
+ char * addr;
+ int size;
+{
+ int count = BUFSIZE;
+ while (size > 0) {
+ if (BUFSIZE > size)
+ count = size;
+ read(buf, count);
+ pcpy(buf, addr, count);
+ size -= count;
+ addr += count;
+ }
+}
+
+read(buffer, count)
+ int count;
+ char *buffer;
+{
+ int logno, off, size;
+ int cnt2, bnum2;
+
+ while (count) {
+ off = blkoff(fs, poff);
+ logno = lblkno(fs, poff);
+ cnt2 = size = blksize(fs, &inode, logno);
+ bnum2 = fsbtodb(fs, block_map(logno)) + boff;
+ cnt = cnt2;
+ bnum = bnum2;
+ if ( (!off) && (size <= count))
+ {
+ iodest = buffer;
+ devread();
+ }
+ else
+ {
+ iodest = iobuf;
+ size -= off;
+ if (size > count)
+ size = count;
+ devread();
+ bcopy(iodest+off,buffer,size);
+ }
+ buffer += size;
+ count -= size;
+ poff += size;
+ }
+}
+
+find(path)
+ char *path;
+{
+ char *rest, ch;
+ int block, off, loc, ino = ROOTINO;
+ struct direct *dp;
+loop: iodest = iobuf;
+ cnt = fs->fs_bsize;
+ bnum = fsbtodb(fs,itod(fs,ino)) + boff;
+ devread();
+ bcopy(&((struct dinode *)iodest)[ino % fs->fs_inopb],
+ &inode.i_din,
+ sizeof (struct dinode));
+ if (!*path)
+ return 1;
+ while (*path == '/')
+ path++;
+ if (!inode.i_size || ((inode.i_mode&IFMT) != IFDIR))
+ return 0;
+ for (rest = path; (ch = *rest) && ch != '/'; rest++) ;
+ *rest = 0;
+ loc = 0;
+ do {
+ if (loc >= inode.i_size)
+ return 0;
+ if (!(off = blkoff(fs, loc))) {
+ block = lblkno(fs, loc);
+ cnt = blksize(fs, &inode, block);
+ bnum = fsbtodb(fs, block_map(block)) + boff;
+ iodest = iobuf;
+ devread();
+ }
+ dp = (struct direct *)(iodest + off);
+ loc += dp->d_reclen;
+ } while (!dp->d_ino || strcmp(path, dp->d_name));
+ ino = dp->d_ino;
+ *(path = rest) = ch;
+ goto loop;
+}
+
+char mapbuf[MAXBSIZE];
+int mapblock = 0;
+
+block_map(file_block)
+ int file_block;
+{
+ if (file_block < NDADDR)
+ return(inode.i_db[file_block]);
+ if ((bnum=fsbtodb(fs, inode.i_ib[0])+boff) != mapblock) {
+ iodest = mapbuf;
+ cnt = fs->fs_bsize;
+ devread();
+ mapblock = bnum;
+ }
+ return (((int *)mapbuf)[(file_block - NDADDR) % NINDIR(fs)]);
+}
+
+openrd()
+{
+ char **devp, *cp = name;
+ /*******************************************************\
+ * If bracket given look for preceding device name *
+ \*******************************************************/
+ while (*cp && *cp!='(')
+ cp++;
+ if (!*cp)
+ {
+ cp = name;
+ }
+ else
+ {
+ if (cp++ != name)
+ {
+ for (devp = devs; *devp; devp++)
+ if (name[0] == (*devp)[0] &&
+ name[1] == (*devp)[1])
+ break;
+ if (!*devp)
+ {
+ printf("Unknown device\n");
+ return 1;
+ }
+ maj = devp-devs;
+ }
+ /*******************************************************\
+ * Look inside brackets for unit number, and partition *
+ \*******************************************************/
+ if (*cp >= '0' && *cp <= '9')
+ if ((unit = *cp++ - '0') > 1)
+ {
+ printf("Bad unit\n");
+ return 1;
+ }
+ if (!*cp || (*cp == ',' && !*++cp))
+ return 1;
+ if (*cp >= 'a' && *cp <= 'p')
+ part = *cp++ - 'a';
+ while (*cp && *cp++!=')') ;
+ if (!*cp)
+ return 1;
+ }
+ switch(maj)
+ {
+ case 1:
+ dosdev = unit | 0x80;
+ unit = 0;
+ break;
+ case 0:
+ case 4:
+ dosdev = unit | 0x80;
+ break;
+ case 2:
+ dosdev = unit;
+ break;
+ case 3:
+ printf("Wangtek unsupported\n");
+ return 1;
+ break;
+ }
+ inode.i_dev = dosdev;
+ /***********************************************\
+ * Now we know the disk unit and part, *
+ * Load disk info, (open the device) *
+ \***********************************************/
+ if (devopen())
+ return 1;
+
+ /***********************************************\
+ * Load Filesystem info (mount the device) *
+ \***********************************************/
+ iodest = (char *)(fs = (struct fs *)fsbuf);
+ cnt = SBSIZE;
+ bnum = SBLOCK + boff;
+ devread();
+ /***********************************************\
+ * Find the actual FILE on the mounted device *
+ \***********************************************/
+ if (!find(cp))
+ {
+ return 1;
+ }
+ poff = 0;
+ name = cp;
+ return 0;
+}
diff --git a/sys/i386/boot/biosboot/table.c b/sys/i386/boot/biosboot/table.c
new file mode 100644
index 0000000..c53173d
--- /dev/null
+++ b/sys/i386/boot/biosboot/table.c
@@ -0,0 +1,125 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:36:43 rpd
+ * $Id$
+ */
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/* Segment Descriptor
+ *
+ * 31 24 19 16 7 0
+ * ------------------------------------------------------------
+ * | | |B| |A| | | |1|0|E|W|A| |
+ * | BASE 31..24 |G|/|0|V| LIMIT |P|DPL| TYPE | BASE 23:16 |
+ * | | |D| |L| 19..16| | |1|1|C|R|A| |
+ * ------------------------------------------------------------
+ * | | |
+ * | BASE 15..0 | LIMIT 15..0 |
+ * | | |
+ * ------------------------------------------------------------
+ */
+
+struct seg_desc {
+ unsigned short limit_15_0;
+ unsigned short base_15_0;
+ unsigned char base_23_16;
+ unsigned char p_dpl_type;
+ unsigned char g_b_a_limit;
+ unsigned char base_31_24;
+ };
+
+#define RUN 0 /* not really 0, but filled in at boot time */
+
+struct seg_desc Gdt[] = {
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, /* 0x0 : null */
+ {0xFFFF, 0x0, 0x0, 0x9F, 0xCF, 0x0}, /* 0x08 : kernel code */
+ /* 0x9E? */
+ {0xFFFF, 0x0, 0x0, 0x93, 0xCF, 0x0}, /* 0x10 : kernel data */
+ /* 0x92? */
+ {0xFFFF, RUN, RUN, 0x9E, 0x40, 0x0}, /* 0x18 : boot code */
+ {0xFFFF, RUN, RUN, 0x92, 0x40, 0x0}, /* 0x20 : boot data */
+ {0xFFFF, RUN, RUN, 0x9E, 0x0, 0x0}, /* 0x28 : boot code, 16 bits */
+ /* More for bdb. */
+ {}, /* BIOS_CS_INDEX = 6 : null */
+ {}, /* BIOS_TMP_INDEX = 7 : null */
+ {}, /* TSS_INDEX = 8 : null */
+ {0xFFFF, 0x0, 0x0, 0xB2, 0x40, 0x0}, /* DS_286_INDEX = 9 */
+ {0xFFFF, 0x0, 0x0, 0xB2, 0x40, 0x0}, /* ES_286_INDEX = 10 */
+ {}, /* Unused = 11 : null */
+ {0x7FFF, 0x8000, 0xB, 0xB2, 0x40, 0x0}, /* COLOR_INDEX = 12 */
+ {0x7FFF, 0x0, 0xB, 0xB2, 0x40, 0x0}, /* MONO_INDEX = 13 */
+ {0xFFFF, RUN, RUN, 0x9A, 0x40, 0x0}, /* DB_CS_INDEX = 14 */
+ {0xFFFF, RUN, RUN, 0x9A, 0x0, 0x0}, /* DB_CS16_INDEX = 15 */
+ {0xFFFF, RUN, RUN, 0x92, 0x40, 0x0}, /* DB_DS_INDEX = 16 */
+ {8*18-1, RUN, RUN, 0x92, 0x40, 0x0}, /* GDT_INDEX = 17 */
+};
+
+struct idt_desc {
+ unsigned short entry_15_0;
+ unsigned short selector;
+ unsigned char padding;
+ unsigned char p_dpl_type;
+ unsigned short entry_31_16;
+};
+
+struct idt_desc Idt[] = {
+ {}, /* Null (int 0) */
+ {RUN, 0x70, 0, 0x8E, 0}, /* DEBUG_VECTOR = 1 */
+ {}, /* Null (int 2) */
+ {RUN, 0x70, 0, 0xEE, 0}, /* BREAKPOINT_VECTOR = 3 */
+};
+
+struct pseudo_desc {
+ unsigned short limit;
+ unsigned short base_low;
+ unsigned short base_high;
+ };
+
+struct pseudo_desc Gdtr = { sizeof Gdt - 1, RUN, RUN };
+struct pseudo_desc Idtr_prot = { sizeof Idt - 1, RUN, RUN };
+struct pseudo_desc Idtr_real = { 0x400 - 1, 0x0, 0x0 };
diff --git a/sys/i386/boot/boot.c b/sys/i386/boot/boot.c
new file mode 100644
index 0000000..e186bdc
--- /dev/null
+++ b/sys/i386/boot/boot.c
@@ -0,0 +1,308 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, [92/04/03 16:51:14 rvb]
+ * $Id: boot.c,v 1.13 1994/06/14 07:31:42 rgrimes Exp $
+ */
+
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <sys/param.h>
+#include "boot.h"
+#include <a.out.h>
+#include <sys/reboot.h>
+
+struct exec head;
+int argv[10], esym;
+char *name;
+char *names[] = {
+ "/386bsd", "/o386bsd", "/386bsd.old"
+};
+#define NUMNAMES (sizeof(names)/sizeof(char *))
+
+extern int end;
+boot(drive)
+int drive;
+{
+ int loadflags, currname = 0;
+ char *t;
+
+ printf("\n>> FreeBSD BOOT @ 0x%x: %d/%d k of memory [%s]\n",
+ ouraddr,
+ argv[7] = memsize(0),
+ argv[8] = memsize(1),
+ "$Revision: 1.14 $");
+ printf("use hd(1,a)/386bsd to boot sd0 when wd0 is also installed\n");
+ gateA20();
+loadstart:
+ /***************************************************************\
+ * As a default set it to the first partition of the first *
+ * floppy or hard drive *
+ \***************************************************************/
+ part = unit = 0;
+ maj = (drive&0x80 ? 0 : 2); /* a good first bet */
+ name = names[currname++];
+
+ loadflags = 0;
+ if (currname == NUMNAMES)
+ currname = 0;
+ getbootdev(&loadflags);
+ if (openrd()) {
+ printf("Can't find %s\n", name);
+ goto loadstart;
+ }
+/* if (inode.i_mode&IEXEC)
+ loadflags |= RB_KDB;
+*/
+ loadprog(loadflags);
+ goto loadstart;
+}
+
+loadprog(howto)
+ int howto;
+{
+ long int startaddr;
+ long int addr; /* physical address.. not directly useable */
+ long int addr0;
+ int i;
+ static int (*x_entry)() = 0;
+ unsigned char tmpbuf[4096]; /* we need to load the first 4k here */
+
+ argv[3] = 0;
+ argv[4] = 0;
+ read(&head, sizeof(head));
+ if ( N_BADMAG(head)) {
+ printf("Invalid format!\n");
+ return;
+ }
+
+ poff = N_TXTOFF(head);
+ /*if(poff==0)
+ poff = 32;*/
+
+ startaddr = (int)head.a_entry;
+ addr = (startaddr & 0x00ffffff); /* some MEG boundary */
+ addr0 = addr;
+ printf("Booting %s(%d,%c)%s @ 0x%x\n"
+ , devs[maj]
+ , unit
+ , 'a'+part
+ , name
+ , addr);
+ if(addr < ouraddr)
+ {
+ if((addr + head.a_text + head.a_data) > ouraddr)
+ {
+ printf("kernel overlaps loader\n");
+ return;
+ }
+ if((addr + head.a_text + head.a_data + head.a_bss) > 0xa0000)
+ {
+ printf("bss exceeds 640k limit\n");
+ return;
+ }
+ }
+ printf("text=0x%x ", head.a_text);
+ /********************************************************/
+ /* LOAD THE TEXT SEGMENT */
+ /* don't clobber the first 4k yet (BIOS NEEDS IT) */
+ /********************************************************/
+ read(tmpbuf,4096);
+ addr += 4096;
+ xread(addr, head.a_text - 4096);
+ addr += head.a_text - 4096;
+
+ /********************************************************/
+ /* Load the Initialised data after the text */
+ /********************************************************/
+ while (addr & CLOFSET)
+ *(char *)addr++ = 0;
+
+ printf("data=0x%x ", head.a_data);
+ xread(addr, head.a_data);
+ addr += head.a_data;
+
+ /********************************************************/
+ /* Skip over the uninitialised data */
+ /* (but clear it) */
+ /********************************************************/
+ printf("bss=0x%x ", head.a_bss);
+ if( (addr < ouraddr) && ((addr + head.a_bss) > ouraddr))
+ {
+ pbzero(addr,ouraddr - (int)addr);
+ }
+ else
+ {
+ pbzero(addr,head.a_bss);
+ }
+ argv[3] = (addr += head.a_bss);
+
+#ifdef LOADSYMS /* not yet, haven't worked this out yet */
+ if (addr > 0x100000)
+ {
+ /********************************************************/
+ /*copy in the symbol header */
+ /********************************************************/
+ pcpy(&head.a_syms, addr, sizeof(head.a_syms));
+ addr += sizeof(head.a_syms);
+
+ /********************************************************/
+ /* READ in the symbol table */
+ /********************************************************/
+ printf("symbols=[+0x%x", head.a_syms);
+ xread(addr, head.a_syms);
+ addr += head.a_syms;
+
+ /********************************************************/
+ /* Followed by the next integer (another header) */
+ /* more debug symbols? */
+ /********************************************************/
+ read(&i, sizeof(int));
+ pcpy(&i, addr, sizeof(int));
+ i -= sizeof(int);
+ addr += sizeof(int);
+
+
+ /********************************************************/
+ /* and that many bytes of (debug symbols?) */
+ /********************************************************/
+ printf("+0x%x] ", i);
+ xread(addr, i);
+ addr += i;
+ }
+#endif LOADSYMS
+ /********************************************************/
+ /* and note the end address of all this */
+ /********************************************************/
+
+ argv[4] = ((addr+sizeof(int)-1))&~(sizeof(int)-1);
+ printf("total=0x%x ",argv[4]);
+
+
+ /*
+ * We now pass the various bootstrap parameters to the loaded
+ * image via the argument list
+ * (THIS IS A BIT OF HISTORY FROM MACH.. LEAVE FOR NOW)
+ * arg1 = boot flags
+ * arg2 = boot device
+ * arg3 = start of symbol table (0 if not loaded)
+ * arg4 = end of symbol table (0 if not loaded)
+ * arg5 = transfer address from image
+ * arg6 = transfer address for next image pointer
+ */
+ switch(maj)
+ {
+ case 2:
+ printf("\n\nInsert file system floppy in drive A or B\n");
+ printf("Press 'A', 'B' or any other key for the default ");
+ printf("%c: ", unit+'A');
+ i = getchar();
+ if (i=='0' || i=='A' || i=='a')
+ unit = 0;
+ if (i=='1' || i=='B' || i=='b')
+ unit = 1;
+ printf("\n");
+ break;
+ case 4:
+ break;
+ }
+ argv[1] = howto;
+ argv[2] = (MAKEBOOTDEV(maj, 0, 0, unit, part)) ;
+ argv[5] = (head.a_entry &= 0xfffffff);
+ argv[6] = (int) &x_entry;
+ argv[0] = 8;
+ /****************************************************************/
+ /* copy that first page and overwrite any BIOS variables */
+ /****************************************************************/
+ printf("entry point=0x%x\n" ,((int)startaddr) & 0xffffff);
+ /* Under no circumstances overwrite precious BIOS variables! */
+ pcpy(tmpbuf, addr0, 0x400);
+ pcpy(tmpbuf + 0x500, addr0 + 0x500, 4096 - 0x500);
+ startprog(((int)startaddr & 0xffffff),argv);
+}
+
+char namebuf[100];
+getbootdev(howto)
+ int *howto;
+{
+ char c, *ptr = namebuf;
+ printf("Boot: [[[%s(%d,%c)]%s][-s][-a][-d]] :- "
+ , devs[maj]
+ , unit
+ , 'a'+part
+ , name);
+ if (gets(namebuf)) {
+ while (c=*ptr) {
+ while (c==' ')
+ c = *++ptr;
+ if (!c)
+ return;
+ if (c=='-')
+ while ((c = *++ptr) && c!=' ')
+ switch (c) {
+ case 'r':
+ *howto |= RB_DFLTROOT; continue;
+ case 'a':
+ *howto |= RB_ASKNAME; continue;
+ case 's':
+ *howto |= RB_SINGLE; continue;
+ case 'd':
+ *howto |= RB_KDB; continue;
+ case 'b':
+ *howto |= RB_HALT; continue;
+ }
+ else {
+ name = ptr;
+ while ((c = *++ptr) && c!=' ');
+ if (c)
+ *ptr++ = 0;
+ }
+ }
+ } else
+ printf("\n");
+}
+
diff --git a/sys/i386/boot/boot.h b/sys/i386/boot/boot.h
new file mode 100644
index 0000000..f77f882
--- /dev/null
+++ b/sys/i386/boot/boot.h
@@ -0,0 +1,40 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:35:03 rpd
+ * $Id$
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <ufs/quota.h>
+#include <ufs/fs.h>
+#include <ufs/inode.h>
+
+extern char *devs[], *name, *iodest;
+extern struct fs *fs;
+extern struct inode inode;
+extern int dosdev, unit, part, maj, boff, poff, bnum, cnt;
+extern long int ouraddr;
diff --git a/sys/i386/boot/boot.sed b/sys/i386/boot/boot.sed
new file mode 100644
index 0000000..c6b38ea
--- /dev/null
+++ b/sys/i386/boot/boot.sed
@@ -0,0 +1,3 @@
+/^[ ]*.data/c\
+ .text
+/^[ ]*.ident/d
diff --git a/sys/i386/boot/boot2.S b/sys/i386/boot/boot2.S
new file mode 100644
index 0000000..f78ce10
--- /dev/null
+++ b/sys/i386/boot/boot2.S
@@ -0,0 +1,177 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:35:26 rpd
+ * $Id: boot2.S,v 1.3 1993/11/13 04:43:25 rgrimes Exp $
+ */
+
+#include "asm.h"
+
+/* Conventional GDT indexes. */
+#define BOOT_CS_INDEX 3
+#define BOOT_CS16_INDEX 5
+#define BOOT_DS_INDEX 4
+#define DB_CS_INDEX 14
+#define DB_CS16_INDEX 15
+#define DB_DS_INDEX 16
+#define GDT_INDEX 17
+
+/* Vector numbers. */
+#define BREAKPOINT_VECTOR 3
+#define DEBUG_VECTOR 1
+
+/*
+ * boot2() -- second stage boot
+ */
+
+.globl EXT(ouraddr)
+
+ENTRY(boot2)
+ data32
+ subl %eax, %eax
+ mov %cs, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ data32
+ shll $4, %eax
+ addr32
+ data32
+ movl %eax, EXT(ouraddr)
+
+ /* fix up GDT entries for bootstrap */
+#define FIXUP(gdt_index) \
+ addr32; \
+ movl %eax, EXT(Gdt)+(8*gdt_index)+2; /* actually movw %ax */ \
+ addr32; \
+ movb %bl, EXT(Gdt)+(8*gdt_index)+4
+
+ data32
+ shld $16, %eax, %ebx
+ FIXUP(BOOT_CS_INDEX)
+ FIXUP(BOOT_CS16_INDEX)
+ FIXUP(BOOT_DS_INDEX)
+
+ /* fix up GDT entry for GDT, and GDT and IDT pointers */
+ data32
+ movl %eax, %ecx
+ data32
+ addl $ EXT(Gdt), %eax
+ data32
+ shld $16, %eax, %ebx
+ FIXUP(GDT_INDEX)
+ addr32
+ data32
+ movl %eax, EXT(Gdtr)+2
+ data32
+ addl $ EXT(Idt), %ecx
+ addr32
+ data32
+ movl %ecx, EXT(Idtr_prot)+2
+
+ /* %es = vector table segment for a while */
+ push %es
+ data32
+ subl %eax, %eax
+ mov %ax, %es
+
+ /* fix up GDT entries for bdb */
+ data32
+ movl $4*DEBUG_VECTOR, %esi
+ addr32
+ movl %es: 2(%esi), %eax /* actually movw to %ax */
+ data32
+ shll $4, %eax
+ data32
+ shld $16, %eax, %ebx
+ FIXUP(DB_CS_INDEX)
+ FIXUP(DB_CS16_INDEX)
+ FIXUP(DB_DS_INDEX)
+
+ /* Fetch entry points of bdb's protected mode trap handlers. These
+ * are stored at 2 before the corresponding entry points for real mode.
+ */
+ data32
+ subl %ebx, %ebx
+ addr32
+ movl %es: (%esi), %ebx /* actually movw to %bx */
+ data32
+ subl %ecx, %ecx
+ addr32
+ movl %es: 4*(BREAKPOINT_VECTOR-DEBUG_VECTOR)(%esi), %ecx
+ /* actually movw to %cx */
+
+ /* %es = bdb segment for a while */
+ data32
+ shrl $4, %eax
+ mov %ax, %es
+
+ /* fix up IDT entries for bdb */
+ data32
+ subl $2, %ebx /* calculate EA to check it */
+ jb 1f /* give up if it would trap */
+ addr32
+ movl %es: (%ebx), %eax /* actually movw to %ax */
+ addr32
+ movl %eax, EXT(Idt)+8*DEBUG_VECTOR /* actually movw %ax */
+1:
+ data32
+ subl $2, %ecx
+ jb 1f
+ addr32
+ movl %es: (%ecx), %eax /* actually movw to %ax */
+ addr32
+ movl %eax, EXT(Idt)+8*BREAKPOINT_VECTOR /* actually movw %ax */
+1:
+
+ /* finished with groping in real mode segments */
+ pop %es
+
+ /* change to protected mode */
+ data32
+ call EXT(real_to_prot)
+
+ /* clear the bss */
+ movl $ EXT(edata), %edi /* no EXT(_edata) - krufty ld */
+ movl $ EXT(end), %ecx /* or EXT(_end) */
+ subl %edi, %ecx
+ subb %al, %al
+ rep
+ stosb
+
+ movzbl %dl, %edx /* discard head (%dh) and random high bits */
+ pushl %edx
+ call EXT(boot)
+oops:
+ hlt
+ jmp oops
+
+ .data
+ .align 2
+#if 0 /* XXX this would give losing "_ouraddr :". Better declared in C */
+EXT(ouraddr):
+#else
+_ouraddr:
+#endif
+ .long 0
diff --git a/sys/i386/boot/boot2.s b/sys/i386/boot/boot2.s
new file mode 100644
index 0000000..d319edf
--- /dev/null
+++ b/sys/i386/boot/boot2.s
@@ -0,0 +1,73 @@
+/*
+ * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ *
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/*
+ * HISTORY
+ * $Log: boot2.s,v $
+ * Revision 2.2 92/04/04 11:35:26 rpd
+ * From 2.5
+ * [92/03/30 rvb]
+ *
+ * Revision 2.2 91/04/02 14:39:21 mbj
+ * Put into rcs tree
+ * [90/02/09 rvb]
+ *
+ */
+
+#include "asm.h"
+#define LOADMSG 1
+/*
+ * boot2() -- second stage boot
+ */
+
+.globl _ouraddr
+
+ENTRY(boot2)
+ movl %cs, %ax
+ movl %ax, %ds
+ movl %ax, %es
+ data32
+ sall $4, %eax
+ data32
+ movl %eax, _ouraddr
+ /* save the drive type and ID */
+ data32
+ pushl %edx
+ /* change to protected mode */
+ data32
+ call _real_to_prot
+
+ call _boot
+ ret
+
+ .data
+ .align 2
+_ouraddr:
+ .long 0
+
+
diff --git a/sys/i386/boot/disk.c b/sys/i386/boot/disk.c
new file mode 100644
index 0000000..a63ca5a
--- /dev/null
+++ b/sys/i386/boot/disk.c
@@ -0,0 +1,281 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:35:49 rpd
+ * $Id: disk.c,v 1.4 1994/02/22 22:59:40 rgrimes Exp $
+ */
+
+/*
+ * 93/10/08 bde
+ * If there is no 386BSD partition, initialize the label sector with
+ * LABELSECTOR instead of with garbage.
+ *
+ * 93/08/22 bde
+ * Fixed reading of bad sector table. It is at the end of the 'c'
+ * partition, which is not always at the end of the disk.
+ */
+
+#include "boot.h"
+#ifdef DO_BAD144
+#include <sys/dkbad.h>
+#endif DO_BAD144
+#include <sys/disklabel.h>
+
+#define BIOS_DEV_FLOPPY 0x0
+#define BIOS_DEV_WIN 0x80
+
+#define BPS 512
+#define SPT(di) ((di)&0xff)
+#define HEADS(di) ((((di)>>8)&0xff)+1)
+
+char *devs[] = {"wd", "hd", "fd", "wt", "sd", 0};
+
+#ifdef DO_BAD144
+struct dkbad dkb;
+int do_bad144;
+int bsize;
+#endif DO_BAD144
+
+int spt, spc;
+
+char *iodest;
+struct fs *fs;
+struct inode inode;
+int dosdev, unit, part, maj, boff, poff, bnum, cnt;
+
+/*#define EMBEDDED_DISKLABEL 1*/
+extern struct disklabel disklabel;
+/*struct disklabel disklabel;*/
+
+devopen()
+{
+ struct dos_partition *dptr;
+ struct disklabel *dl;
+ int dosdev = inode.i_dev;
+ int i, sector, di;
+
+ di = get_diskinfo(dosdev);
+ spc = (spt = SPT(di)) * HEADS(di);
+ if (dosdev == 2)
+ {
+ boff = 0;
+ part = (spt == 15 ? 3 : 1);
+ }
+ else
+ {
+#ifdef EMBEDDED_DISKLABEL
+ dl = &disklabel;
+#else EMBEDDED_DISKLABEL
+ Bread(dosdev, 0);
+ dptr = (struct dos_partition *)(((char *)0)+DOSPARTOFF);
+ sector = LABELSECTOR;
+ for (i = 0; i < NDOSPART; i++, dptr++)
+ if (dptr->dp_typ == DOSPTYP_386BSD) {
+ sector = dptr->dp_start + LABELSECTOR;
+ break;
+ }
+ Bread(dosdev, sector++);
+ dl=((struct disklabel *)0);
+ disklabel = *dl; /* structure copy (maybe useful later)*/
+#endif EMBEDDED_DISKLABEL
+ if (dl->d_magic != DISKMAGIC) {
+ printf("bad disklabel");
+ return 1;
+ }
+ if( (maj == 4) || (maj == 0) || (maj == 1))
+ {
+ if (dl->d_type == DTYPE_SCSI)
+ {
+ maj = 4; /* use scsi as boot dev */
+ }
+ else
+ {
+ maj = 0; /* must be ESDI/IDE */
+ }
+ }
+ boff = dl->d_partitions[part].p_offset;
+#ifdef DO_BAD144
+ bsize = dl->d_partitions[part].p_size;
+ do_bad144 = 0;
+ if (dl->d_flags & D_BADSECT) {
+ /* this disk uses bad144 */
+ int i;
+ int dkbbnum;
+ struct dkbad *dkbptr;
+
+ /* find the first readable bad sector table */
+ /* some of this code is copied from ufs/ufs_disksubr.c */
+ /* including the bugs :-( */
+ /* read a bad sector table */
+
+#define BAD144_PART 2 /* XXX scattered magic numbers */
+#define BSD_PART 0 /* XXX should be 2 but bad144.c uses 0 */
+ if (dl->d_partitions[BSD_PART].p_offset != 0)
+ dkbbnum = dl->d_partitions[BAD144_PART].p_offset
+ + dl->d_partitions[BAD144_PART].p_size;
+ else
+ dkbbnum = dl->d_secperunit;
+ dkbbnum -= dl->d_nsectors;
+
+ if (dl->d_secsize > DEV_BSIZE)
+ dkbbnum *= dl->d_secsize / DEV_BSIZE;
+ else
+ dkbbnum /= DEV_BSIZE / dl->d_secsize;
+ i = 0;
+ do_bad144 = 0;
+ do {
+ /* XXX: what if the "DOS sector" < 512 bytes ??? */
+ Bread(dosdev, dkbbnum + i);
+ dkbptr = (struct dkbad *) 0;
+/* XXX why is this not in <sys/dkbad.h> ??? */
+#define DKBAD_MAGIC 0x4321
+ if (dkbptr->bt_mbz == 0 &&
+ dkbptr->bt_flag == DKBAD_MAGIC) {
+ dkb = *dkbptr; /* structure copy */
+ do_bad144 = 1;
+ break;
+ }
+ i += 2;
+ } while (i < 10 && i < dl->d_nsectors);
+ if (!do_bad144)
+ printf("Bad bad sector table\n");
+ else
+ printf("Using bad sector table at %d\n", dkbbnum+i);
+ }
+#endif DO_BAD144
+ }
+ return 0;
+}
+
+devread()
+{
+ int offset, sector = bnum;
+ int dosdev = inode.i_dev;
+ for (offset = 0; offset < cnt; offset += BPS)
+ {
+ Bread(dosdev, badsect(dosdev, sector++));
+ bcopy(0, iodest+offset, BPS);
+ }
+}
+
+#define I_ADDR ((void *) 0) /* XXX where all reads go */
+
+/* Read ahead buffer large enough for one track on a 1440K floppy. For
+ * reading from floppies, the bootstrap has to be loaded on a 64K boundary
+ * to ensure that this buffer doesn't cross a 64K DMA boundary.
+ */
+#define RA_SECTORS 18
+static char ra_buf[RA_SECTORS * BPS];
+static int ra_dev;
+static int ra_end;
+static int ra_first;
+
+Bread(dosdev,sector)
+ int dosdev,sector;
+{
+ if (dosdev != ra_dev || sector < ra_first || sector >= ra_end)
+ {
+ int cyl, head, sec, nsec;
+
+ cyl = sector/spc;
+ head = (sector % spc) / spt;
+ sec = sector % spt;
+ nsec = spt - sec;
+ if (nsec > RA_SECTORS)
+ nsec = RA_SECTORS;
+ twiddle();
+ if (biosread(dosdev, cyl, head, sec, nsec, ra_buf) != 0)
+ {
+ nsec = 1;
+ twiddle();
+ while (biosread(dosdev, cyl, head, sec, nsec, ra_buf) != 0) {
+ printf("Error: C:%d H:%d S:%d\n", cyl, head, sec);
+ twiddle();
+ }
+ }
+ ra_dev = dosdev;
+ ra_first = sector;
+ ra_end = sector + nsec;
+ }
+ bcopy(ra_buf + (sector - ra_first) * BPS, I_ADDR, BPS);
+}
+
+badsect(dosdev, sector)
+ int dosdev, sector;
+{
+ int i;
+#ifdef DO_BAD144
+ if (do_bad144) {
+ u_short cyl;
+ u_short head;
+ u_short sec;
+ int newsec;
+ struct disklabel *dl = &disklabel;
+
+ /* XXX */
+ /* from wd.c */
+ /* bt_cyl = cylinder number in sorted order */
+ /* bt_trksec is actually (head << 8) + sec */
+
+ /* only remap sectors in the partition */
+ if (sector < boff || sector >= boff + bsize) {
+ goto no_remap;
+ }
+
+ cyl = sector / dl->d_secpercyl;
+ head = (sector % dl->d_secpercyl) / dl->d_nsectors;
+ sec = sector % dl->d_nsectors;
+ sec = (head<<8) + sec;
+
+ /* now, look in the table for a possible bad sector */
+ for (i=0; i<126; i++) {
+ if (dkb.bt_bad[i].bt_cyl == cyl) {
+ /* found same cylinder */
+ if (dkb.bt_bad[i].bt_trksec == sec) {
+ /* FOUND! */
+ break;
+ }
+ } else if (dkb.bt_bad[i].bt_cyl > cyl) {
+ i = 126;
+ break;
+ }
+ }
+ if (i == 126) {
+ /* didn't find bad sector */
+ goto no_remap;
+ }
+ /* otherwise find replacement sector */
+ if (dl->d_partitions[BSD_PART].p_offset != 0)
+ newsec = dl->d_partitions[BAD144_PART].p_offset
+ + dl->d_partitions[BAD144_PART].p_size;
+ else
+ newsec = dl->d_secperunit;
+ newsec -= dl->d_nsectors + i + 1;
+ return newsec;
+ }
+#endif DO_BAD144
+ no_remap:
+ return sector;
+}
diff --git a/sys/i386/boot/io.c b/sys/i386/boot/io.c
new file mode 100644
index 0000000..ee441f0
--- /dev/null
+++ b/sys/i386/boot/io.c
@@ -0,0 +1,214 @@
+
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:35:57 rpd
+ * $Id: io.c,v 1.5 1994/06/15 19:09:14 jkh Exp $
+ */
+
+#include <i386/include/pio.h>
+
+#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */
+#define K_STATUS 0x64 /* keyboard status */
+#define K_CMD 0x64 /* keybd ctlr command (write-only) */
+
+#define K_OBUF_FUL 0x01 /* output buffer full */
+#define K_IBUF_FUL 0x02 /* input buffer full */
+
+#define KC_CMD_WIN 0xd0 /* read output port */
+#define KC_CMD_WOUT 0xd1 /* write output port */
+#define KB_A20 0xdf /* enable A20,
+ enable output buffer full interrupt
+ enable data line
+ enable clock line */
+
+/*
+ * Gate A20 for high memory
+ */
+unsigned char x_20 = KB_A20;
+gateA20()
+{
+#ifdef IBM_L40
+ outb(0x92, 0x2);
+#else IBM_L40
+ while (inb(K_STATUS) & K_IBUF_FUL);
+ while (inb(K_STATUS) & K_OBUF_FUL)
+ (void)inb(K_RDWR);
+
+ outb(K_CMD, KC_CMD_WOUT);
+ while (inb(K_STATUS) & K_IBUF_FUL);
+ outb(K_RDWR, x_20);
+ while (inb(K_STATUS) & K_IBUF_FUL);
+#endif IBM_L40
+}
+
+/* printf - only handles %d as decimal, %c as char, %s as string */
+
+printf(format,data)
+ char *format;
+ int data;
+{
+ int *dataptr = &data;
+ char c;
+
+ reset_twiddle();
+ while (c = *format++)
+ if (c != '%')
+ putchar(c);
+ else
+ switch (c = *format++) {
+ case 'd': {
+ int num = *dataptr++;
+ char buf[10], *ptr = buf;
+ if (num<0) {
+ num = -num;
+ putchar('-');
+ }
+ do
+ *ptr++ = '0'+num%10;
+ while (num /= 10);
+ do
+ putchar(*--ptr);
+ while (ptr != buf);
+ break;
+ }
+ case 'x': {
+ int num = *dataptr++, dig;
+ char buf[8], *ptr = buf;
+ do
+ *ptr++ = (dig=(num&0xf)) > 9?
+ 'a' + dig - 10 :
+ '0' + dig;
+ while (num >>= 4);
+ do
+ putchar(*--ptr);
+ while (ptr != buf);
+ break;
+ }
+ case 'c': putchar((*dataptr++)&0xff); break;
+ case 's': {
+ char *ptr = (char *)*dataptr++;
+ while (c = *ptr++)
+ putchar(c);
+ break;
+ }
+ }
+}
+
+putchar(c)
+{
+ if (c == '\n')
+ putc('\r');
+ putc(c);
+}
+
+getchar()
+{
+ int c;
+
+ if ((c=getc()) == '\r')
+ c = '\n';
+ if (c == '\b') {
+ putchar('\b');
+ putchar(' ');
+ }
+ putchar(c);
+ return(c);
+}
+
+#if BOOTWAIT
+spinwait(i)
+int i;
+{
+ while (--i >= 0)
+ (void)inb(0x84);
+}
+#endif
+
+gets(buf)
+char *buf;
+{
+ int i;
+ char *ptr=buf;
+
+#if BOOTWAIT
+ for (i = BOOTWAIT; i>0; spinwait(10000),i--)
+#endif
+ if (ischar())
+ for (;;)
+ switch(*ptr = getchar() & 0xff) {
+ case '\n':
+ case '\r':
+ *ptr = '\0';
+ return 1;
+ case '\b':
+ if (ptr > buf) ptr--;
+ continue;
+ default:
+ ptr++;
+ }
+ return 0;
+}
+
+strcmp(s1, s2)
+char *s1, *s2;
+{
+ while (*s1 == *s2) {
+ if (!*s1++)
+ return 0;
+ s2++;
+ }
+ return 1;
+}
+
+bcopy(from, to, len)
+char *from, *to;
+int len;
+{
+ while (len-- > 0)
+ *to++ = *from++;
+}
+
+static int tw_on;
+static int tw_pos;
+static char tw_chars[] = "|/-\\";
+
+reset_twiddle()
+{
+ if (tw_on)
+ putchar('\b');
+ tw_on = 0;
+ tw_pos = 0;
+}
+
+twiddle()
+{
+ if (tw_on)
+ putchar('\b');
+ else
+ tw_on = 1;
+ putchar(tw_chars[tw_pos++]);
+ tw_pos %= (sizeof(tw_chars) - 1);
+}
diff --git a/sys/i386/boot/rmaouthdr b/sys/i386/boot/rmaouthdr
new file mode 100644
index 0000000..4bb9c84
--- /dev/null
+++ b/sys/i386/boot/rmaouthdr
@@ -0,0 +1,6 @@
+#!/bin/csh -f
+#
+# from: Mach, Revision 2.2 92/04/04 11:36:01 rpd
+# $Id$
+#
+dd if=$1 of=$2 ibs=32 skip=1 obs=1024b
diff --git a/sys/i386/boot/start.S b/sys/i386/boot/start.S
new file mode 100644
index 0000000..aa65212
--- /dev/null
+++ b/sys/i386/boot/start.S
@@ -0,0 +1,292 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:36:29 rpd
+ * $Id: start.S,v 1.2 1993/10/16 19:11:38 rgrimes Exp $
+ */
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+#include "asm.h"
+
+ .file "start.s"
+
+BOOTSEG = 0x9000 # boot will be loaded here (below 640K)
+BOOTSTACK = 0xe000 # boot stack
+SIGNATURE = 0xaa55
+LOADSZ = 15 # size of unix boot
+PARTSTART = 0x1be # starting address of partition table
+NUMPART = 4 # number of partitions in partition table
+PARTSZ = 16 # each partition table entry is 16 bytes
+BSDPART = 0xA5 # value of boot_ind, means bootable partition
+BOOTABLE = 0x80 # value of boot_ind, means bootable partition
+
+ .text
+
+ENTRY(boot1)
+ # start (aka boot1) is loaded at 0x0:0x7c00 but we want 0x7c0:0
+ # ljmp to the next instruction to adjust %cs
+ data32
+ ljmp $0x7c0, $start
+
+start:
+ # set up %ds
+ mov %cs, %ax
+ mov %ax, %ds
+
+ # set up %ss and %esp
+ data32
+ mov $BOOTSEG, %eax
+ mov %ax, %ss
+ data32
+ mov $BOOTSTACK, %esp
+
+ /*** set up %es, (where we will load boot2 to) ***/
+ mov %ax, %es
+
+#ifdef DEBUG
+ data32
+ mov $one, %esi
+ data32
+ call message
+#endif
+
+ # bootstrap passes us drive number in %dl
+ cmpb $0x80, %dl
+ data32
+ jae hd
+
+fd:
+ mov $0x0, %dl
+# reset the disk system
+#ifdef DEBUG
+ data32
+ mov $two, %esi
+ data32
+ call message
+#endif
+ movb $0x0, %ah
+ int $0x13
+ data32
+ mov $0x0001, %ecx # cyl 0, sector 1
+ movb $0, %dh # head
+#ifdef DEBUG
+ data32
+ mov $three, %esi
+ data32
+ call message
+#endif
+ data32
+ jmp load
+
+hd: /**** load sector 0 into the BOOTSEG ****/
+#ifdef DEBUG
+ data32
+ mov $four, %esi
+ data32
+ call message
+#endif
+ data32
+ mov $0x0201, %eax
+ xor %ebx, %ebx # %bx = 0
+ data32
+ mov $0x0001, %ecx
+#ifdef DEBUG
+ data32
+ mov $five, %esi
+ data32
+ call message
+#endif
+ data32
+ andl $0xff, %edx
+ /*mov $0x0080, %edx*/
+ int $0x13
+ data32
+ jb read_error
+
+ /***# find the first 386BSD partition *****/
+ data32
+ mov $PARTSTART, %ebx
+ data32
+ mov $NUMPART, %ecx
+again:
+ addr32
+ movb %es:4(%ebx), %al
+ cmpb $BSDPART, %al
+ data32
+ je found
+ data32
+ add $PARTSZ, %ebx
+ data32
+ loop again
+ data32
+ mov $enoboot, %esi
+ data32
+ jmp err_stop
+
+
+/*
+# BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
+# Call with %ah = 0x2
+# %al = number of sectors
+# %ch = cylinder
+# %cl = sector
+# %dh = head
+# %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
+# %es:%bx = segment:offset of buffer
+# Return:
+# %al = 0x0 on success; err code on failure
+*/
+
+found:
+ addr32
+ movb %es:1(%ebx), %dh /* head */
+ addr32
+ movl %es:2(%ebx), %ecx /*sect, cyl (+ 2 bytes junk in top word) */
+
+load:
+ movb $0x2, %ah /* function 2 */
+ movb $LOADSZ, %al /* number of blocks */
+ xor %ebx, %ebx /* %bx = 0, put it at 0 in the BOOTSEG */
+ int $0x13
+ data32
+ jb read_error
+
+ # ljmp to the second stage boot loader (boot2).
+ # After ljmp, %cs is BOOTSEG and boot1 (512 bytes) will be used
+ # as an internal buffer "intbuf".
+
+#ifdef DEBUG
+ data32
+ mov $six, %esi
+ data32
+ call message
+#endif
+ data32
+ ljmp $BOOTSEG, $ EXT(boot2)
+
+#
+# read_error
+#
+
+read_error:
+ data32
+ mov $eread, %esi
+err_stop:
+ data32
+ call message
+ data32
+ jmp stop
+
+#
+# message: write the error message in %ds:%esi to console
+#
+
+message:
+/*
+ # Use BIOS "int 10H Function 0Eh" to write character in teletype mode
+ # %ah = 0xe %al = character
+ # %bh = page %bl = foreground color (graphics modes)
+*/
+
+ data32
+ push %eax
+ data32
+ push %ebx
+ data32
+ mov $0x0001, %ebx
+ cld
+
+nextb:
+ lodsb # load a byte into %al
+ cmpb $0x0, %al
+ data32
+ je done
+ movb $0xe, %ah
+ int $0x10 # display a byte
+ data32
+ jmp nextb
+done:
+ data32
+ pop %ebx
+ data32
+ pop %eax
+ data32
+ ret
+
+stop: hlt
+ data32
+ jmp stop # halt doesnt actually halt forever
+
+/* error messages */
+
+#ifdef DEBUG
+one: String "1\r\n\0"
+two: String "2\r\n\0"
+three: String "3\r\n\0"
+four: String "4\r\n\0"
+five: String "5\r\n\0"
+six: String "6\r\n\0"
+seven: String "7\r\n\0"
+#endif DEBUG
+eread: String "Read error\r\n\0"
+enoboot: String "No bootable partition\r\n\0"
+endofcode:
+/* throw in a partition in case we are block0 as well */
+/* flag, head, sec, cyl, typ, ehead, esect, ecyl, start, len */
+ . = EXT(boot1) + PARTSTART
+ .byte 0x0,0,0,0,0,0,0,0
+ .long 0,0
+ .byte 0x0,0,0,0,0,0,0,0
+ .long 0,0
+ .byte 0x0,0,0,0,0,0,0,0
+ .long 0,0
+ .byte BOOTABLE,0,1,0,BSDPART,255,255,255
+ .long 0,50000
+/* the last 2 bytes in the sector 0 contain the signature */
+ . = EXT(boot1) + 0x1fe
+ .value SIGNATURE
+ENTRY(disklabel)
+ . = EXT(boot1) + 0x400
diff --git a/sys/i386/boot/start.s b/sys/i386/boot/start.s
new file mode 100644
index 0000000..f6f4bf0
--- /dev/null
+++ b/sys/i386/boot/start.s
@@ -0,0 +1,323 @@
+/*
+ * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ *
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/*
+ * HISTORY
+ * $Log: start.s,v $
+ * Revision 2.2 92/04/04 11:36:29 rpd
+ * Fix Intel Copyright as per B. Davies authorization.
+ * [92/04/03 rvb]
+ * Need to zero dh on hd path; at least for an adaptec card.
+ * [92/01/14 rvb]
+ *
+ * From 2.5 boot:
+ * Flush digit printing.
+ * Fuse floppy and hd boot by using Int 21 to tell
+ * boot type (slightly dubious since Int 21 is DOS
+ * not BIOS)
+ * [92/03/30 mg32]
+ *
+ * Revision 2.2 91/04/02 14:42:04 mbj
+ * Fix the BIG boot bug. We had missed a necessary data
+ * before a xor that was clearing a register used later
+ * as an index register.
+ * [91/03/01 rvb]
+ * Remember floppy type for swapgeneric
+ * Add Intel copyright
+ * [90/02/09 rvb]
+ *
+ */
+
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+#include "asm.h"
+
+ .file "start.s"
+
+BOOTSEG = 0x9000 # boot will be loaded at 640k-64k
+BOOTSTACK = 0xe000 # boot stack
+SIGNATURE = 0xaa55
+LOADSZ = 14 # size of unix boot
+PARTSTART = 0x1be # starting address of partition table
+NUMPART = 4 # number of partitions in partition table
+PARTSZ = 16 # each partition table entry is 16 bytes
+BSDPART = 0xA5 # value of boot_ind, means bootable partition
+BOOTABLE = 0x80 # value of boot_ind, means bootable partition
+
+ .text
+
+ENTRY(boot1)
+
+ # boot1 is loaded at 0x0:0x7c00
+ # ljmp to the next instruction to set up %cs
+ data32
+ ljmp $0x7c0, $start
+
+start:
+ # set up %ds
+ mov %cs, %ax
+ mov %ax, %ds
+
+
+ # set up %ss and %esp
+ data32
+ mov $BOOTSEG, %eax
+ mov %ax, %ss
+ data32
+ mov $BOOTSTACK, %esp
+
+ /*** set up %es, (where we will load boot2 to) ***/
+ mov %ax, %es
+
+#ifdef DEBUG
+ data32
+ mov $one, %esi
+ data32
+ call message
+#endif
+ # get the boot drive id
+ movb $0x33, %ah
+ movb $0x05, %al
+ int $0x21
+
+ cmpb $0x80, %dl
+ data32
+ jge hd
+
+fd:
+# reset the disk system
+#ifdef DEBUG
+ data32
+ mov $two, %esi
+ data32
+ call message
+#endif
+ movb $0x0, %ah
+ int $0x13
+ data32
+ mov $0x0001, %ecx # cyl 0, sector 1
+ data32
+#ifdef DEBUG
+ data32
+ mov $three, %esi
+ data32
+ call message
+#endif
+ jmp load
+
+hd: /**** load sector 0 into the BOOTSEG ****/
+#ifdef DEBUG
+ data32
+ mov $four, %esi
+ data32
+ call message
+#endif
+ data32
+ mov $0x0201, %eax
+ xor %ebx, %ebx # %bx = 0
+ data32
+ mov $0x0001, %ecx
+#ifdef DEBUG
+ data32
+ mov $five, %esi
+ data32
+ call message
+#endif
+ data32
+ andl $0xff, %edx
+ /*mov $0x0080, %edx*/
+ int $0x13
+ data32
+ jb read_error
+
+ /***# find the bootable partition *****/
+ data32
+ mov $PARTSTART, %ebx
+ data32
+ mov $NUMPART, %ecx
+again:
+ addr16
+ movb %es:4(%ebx), %al
+ cmpb $BSDPART, %al
+ data32
+ je found
+ data32
+ add $PARTSZ, %ebx
+ data32
+ loop again
+ data32
+ mov $enoboot, %esi
+ data32
+ jmp err_stop
+
+
+/*
+# BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
+# Call with %ah = 0x2
+# %al = number of sectors
+# %ch = cylinder
+# %cl = sector
+# %dh = head
+# %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
+# %es:%bx = segment:offset of buffer
+# Return:
+# %al = 0x0 on success; err code on failure
+*/
+
+found:
+ addr16
+ movb %es:1(%ebx), %dh /* head */
+ addr16
+ xor %ecx, %ecx
+ addr16
+ movw %es:2(%ebx), %ecx /*sect,cyl (+ 2 bytes junk in top word )*/
+
+load:
+ movb $0x2, %ah /* function 2 */
+ movb $LOADSZ, %al /* number of blocks */
+ xor %ebx, %ebx /* %bx = 0, put it at 0 in the BOOTSEG */
+ int $0x13
+ data32
+ jb read_error
+
+ # ljmp to the second stage boot loader (boot2).
+ # After ljmp, %cs is BOOTSEG and boot1 (512 bytes) will be used
+ # as an internal buffer "intbuf".
+
+#ifdef DEBUG
+ data32
+ mov $six, %esi
+ data32
+ call message
+#endif
+ data32
+ ljmp $BOOTSEG, $EXT(boot2)
+
+#
+# read_error
+#
+
+read_error:
+
+ data32
+ mov $eread, %esi
+err_stop:
+ data32
+ call message
+ data32
+ jmp stop
+
+#
+# message: write the error message in %ds:%esi to console
+#
+
+message:
+ # Use BIOS "int 10H Function 0Eh" to write character in teletype mode
+ # %ah = 0xe %al = character
+ # %bh = page %bl = foreground color (graphics modes)
+
+ data32
+ push %eax
+ data32
+ push %ebx
+ data32
+ mov $0x0001, %ebx
+ cld
+
+nextb:
+ lodsb # load a byte into %al
+ cmpb $0x0, %al
+ data32
+ je done
+ movb $0xe, %ah
+ int $0x10 # display a byte
+ data32
+ jmp nextb
+done:
+ data32
+ pop %ebx
+ data32
+ pop %eax
+ data32
+ ret
+
+stop: hlt
+ data32
+ jmp stop # halt doesnt actually halt forever
+
+/* error messages */
+
+#ifdef DEBUG
+one: String "1\r\n\0"
+two: String "2\r\n\0"
+three: String "3\r\n\0"
+four: String "4\r\n\0"
+five: String "5\r\n\0"
+six: String "6\r\n\0"
+seven: String "7\r\n\0"
+#endif DEBUG
+eread: String "Read error\r\n\0"
+enoboot: String "No bootable partition\r\n\0"
+endofcode:
+/* throw in a partition in case we are block0 as well */
+/* flag,head,sec,cyl,typ,ehead,esect,ecyl,start,len */
+ . = EXT(boot1) + PARTSTART
+ .byte 0x0,0,0,0,0,0,0,0
+ .long 0,0
+ .byte 0x0,0,0,0,0,0,0,0
+ .long 0,0
+ .byte 0x0,0,0,0,0,0,0,0
+ .long 0,0
+ .byte BOOTABLE,0,1,0,BSDPART,255,255,255
+ .long 0,50000
+/* the last 2 bytes in the sector 0 contain the signature */
+ . = EXT(boot1) + 0x1fe
+ .value SIGNATURE
+ENTRY(disklabel)
+ . = EXT(boot1) + 0x400
diff --git a/sys/i386/boot/sys.c b/sys/i386/boot/sys.c
new file mode 100644
index 0000000..4ffe171
--- /dev/null
+++ b/sys/i386/boot/sys.c
@@ -0,0 +1,232 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:36:34 rpd
+ * $Id$
+ */
+
+#include "boot.h"
+#include <sys/dir.h>
+#include <sys/reboot.h>
+
+/* #define BUFSIZE 4096 */
+#define BUFSIZE MAXBSIZE
+
+char buf[BUFSIZE], fsbuf[SBSIZE], iobuf[MAXBSIZE];
+
+int xread(addr, size)
+ char * addr;
+ int size;
+{
+ int count = BUFSIZE;
+ while (size > 0) {
+ if (BUFSIZE > size)
+ count = size;
+ read(buf, count);
+ pcpy(buf, addr, count);
+ size -= count;
+ addr += count;
+ }
+}
+
+read(buffer, count)
+ int count;
+ char *buffer;
+{
+ int logno, off, size;
+ int cnt2, bnum2;
+
+ while (count) {
+ off = blkoff(fs, poff);
+ logno = lblkno(fs, poff);
+ cnt2 = size = blksize(fs, &inode, logno);
+ bnum2 = fsbtodb(fs, block_map(logno)) + boff;
+ cnt = cnt2;
+ bnum = bnum2;
+ if ( (!off) && (size <= count))
+ {
+ iodest = buffer;
+ devread();
+ }
+ else
+ {
+ iodest = iobuf;
+ size -= off;
+ if (size > count)
+ size = count;
+ devread();
+ bcopy(iodest+off,buffer,size);
+ }
+ buffer += size;
+ count -= size;
+ poff += size;
+ }
+}
+
+find(path)
+ char *path;
+{
+ char *rest, ch;
+ int block, off, loc, ino = ROOTINO;
+ struct direct *dp;
+loop: iodest = iobuf;
+ cnt = fs->fs_bsize;
+ bnum = fsbtodb(fs,itod(fs,ino)) + boff;
+ devread();
+ bcopy(&((struct dinode *)iodest)[ino % fs->fs_inopb],
+ &inode.i_din,
+ sizeof (struct dinode));
+ if (!*path)
+ return 1;
+ while (*path == '/')
+ path++;
+ if (!inode.i_size || ((inode.i_mode&IFMT) != IFDIR))
+ return 0;
+ for (rest = path; (ch = *rest) && ch != '/'; rest++) ;
+ *rest = 0;
+ loc = 0;
+ do {
+ if (loc >= inode.i_size)
+ return 0;
+ if (!(off = blkoff(fs, loc))) {
+ block = lblkno(fs, loc);
+ cnt = blksize(fs, &inode, block);
+ bnum = fsbtodb(fs, block_map(block)) + boff;
+ iodest = iobuf;
+ devread();
+ }
+ dp = (struct direct *)(iodest + off);
+ loc += dp->d_reclen;
+ } while (!dp->d_ino || strcmp(path, dp->d_name));
+ ino = dp->d_ino;
+ *(path = rest) = ch;
+ goto loop;
+}
+
+char mapbuf[MAXBSIZE];
+int mapblock = 0;
+
+block_map(file_block)
+ int file_block;
+{
+ if (file_block < NDADDR)
+ return(inode.i_db[file_block]);
+ if ((bnum=fsbtodb(fs, inode.i_ib[0])+boff) != mapblock) {
+ iodest = mapbuf;
+ cnt = fs->fs_bsize;
+ devread();
+ mapblock = bnum;
+ }
+ return (((int *)mapbuf)[(file_block - NDADDR) % NINDIR(fs)]);
+}
+
+openrd()
+{
+ char **devp, *cp = name;
+ /*******************************************************\
+ * If bracket given look for preceding device name *
+ \*******************************************************/
+ while (*cp && *cp!='(')
+ cp++;
+ if (!*cp)
+ {
+ cp = name;
+ }
+ else
+ {
+ if (cp++ != name)
+ {
+ for (devp = devs; *devp; devp++)
+ if (name[0] == (*devp)[0] &&
+ name[1] == (*devp)[1])
+ break;
+ if (!*devp)
+ {
+ printf("Unknown device\n");
+ return 1;
+ }
+ maj = devp-devs;
+ }
+ /*******************************************************\
+ * Look inside brackets for unit number, and partition *
+ \*******************************************************/
+ if (*cp >= '0' && *cp <= '9')
+ if ((unit = *cp++ - '0') > 1)
+ {
+ printf("Bad unit\n");
+ return 1;
+ }
+ if (!*cp || (*cp == ',' && !*++cp))
+ return 1;
+ if (*cp >= 'a' && *cp <= 'p')
+ part = *cp++ - 'a';
+ while (*cp && *cp++!=')') ;
+ if (!*cp)
+ return 1;
+ }
+ switch(maj)
+ {
+ case 1:
+ dosdev = unit | 0x80;
+ unit = 0;
+ break;
+ case 0:
+ case 4:
+ dosdev = unit | 0x80;
+ break;
+ case 2:
+ dosdev = unit;
+ break;
+ case 3:
+ printf("Wangtek unsupported\n");
+ return 1;
+ break;
+ }
+ inode.i_dev = dosdev;
+ /***********************************************\
+ * Now we know the disk unit and part, *
+ * Load disk info, (open the device) *
+ \***********************************************/
+ if (devopen())
+ return 1;
+
+ /***********************************************\
+ * Load Filesystem info (mount the device) *
+ \***********************************************/
+ iodest = (char *)(fs = (struct fs *)fsbuf);
+ cnt = SBSIZE;
+ bnum = SBLOCK + boff;
+ devread();
+ /***********************************************\
+ * Find the actual FILE on the mounted device *
+ \***********************************************/
+ if (!find(cp))
+ {
+ return 1;
+ }
+ poff = 0;
+ name = cp;
+ return 0;
+}
diff --git a/sys/i386/boot/table.c b/sys/i386/boot/table.c
new file mode 100644
index 0000000..c53173d
--- /dev/null
+++ b/sys/i386/boot/table.c
@@ -0,0 +1,125 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:36:43 rpd
+ * $Id$
+ */
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/* Segment Descriptor
+ *
+ * 31 24 19 16 7 0
+ * ------------------------------------------------------------
+ * | | |B| |A| | | |1|0|E|W|A| |
+ * | BASE 31..24 |G|/|0|V| LIMIT |P|DPL| TYPE | BASE 23:16 |
+ * | | |D| |L| 19..16| | |1|1|C|R|A| |
+ * ------------------------------------------------------------
+ * | | |
+ * | BASE 15..0 | LIMIT 15..0 |
+ * | | |
+ * ------------------------------------------------------------
+ */
+
+struct seg_desc {
+ unsigned short limit_15_0;
+ unsigned short base_15_0;
+ unsigned char base_23_16;
+ unsigned char p_dpl_type;
+ unsigned char g_b_a_limit;
+ unsigned char base_31_24;
+ };
+
+#define RUN 0 /* not really 0, but filled in at boot time */
+
+struct seg_desc Gdt[] = {
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, /* 0x0 : null */
+ {0xFFFF, 0x0, 0x0, 0x9F, 0xCF, 0x0}, /* 0x08 : kernel code */
+ /* 0x9E? */
+ {0xFFFF, 0x0, 0x0, 0x93, 0xCF, 0x0}, /* 0x10 : kernel data */
+ /* 0x92? */
+ {0xFFFF, RUN, RUN, 0x9E, 0x40, 0x0}, /* 0x18 : boot code */
+ {0xFFFF, RUN, RUN, 0x92, 0x40, 0x0}, /* 0x20 : boot data */
+ {0xFFFF, RUN, RUN, 0x9E, 0x0, 0x0}, /* 0x28 : boot code, 16 bits */
+ /* More for bdb. */
+ {}, /* BIOS_CS_INDEX = 6 : null */
+ {}, /* BIOS_TMP_INDEX = 7 : null */
+ {}, /* TSS_INDEX = 8 : null */
+ {0xFFFF, 0x0, 0x0, 0xB2, 0x40, 0x0}, /* DS_286_INDEX = 9 */
+ {0xFFFF, 0x0, 0x0, 0xB2, 0x40, 0x0}, /* ES_286_INDEX = 10 */
+ {}, /* Unused = 11 : null */
+ {0x7FFF, 0x8000, 0xB, 0xB2, 0x40, 0x0}, /* COLOR_INDEX = 12 */
+ {0x7FFF, 0x0, 0xB, 0xB2, 0x40, 0x0}, /* MONO_INDEX = 13 */
+ {0xFFFF, RUN, RUN, 0x9A, 0x40, 0x0}, /* DB_CS_INDEX = 14 */
+ {0xFFFF, RUN, RUN, 0x9A, 0x0, 0x0}, /* DB_CS16_INDEX = 15 */
+ {0xFFFF, RUN, RUN, 0x92, 0x40, 0x0}, /* DB_DS_INDEX = 16 */
+ {8*18-1, RUN, RUN, 0x92, 0x40, 0x0}, /* GDT_INDEX = 17 */
+};
+
+struct idt_desc {
+ unsigned short entry_15_0;
+ unsigned short selector;
+ unsigned char padding;
+ unsigned char p_dpl_type;
+ unsigned short entry_31_16;
+};
+
+struct idt_desc Idt[] = {
+ {}, /* Null (int 0) */
+ {RUN, 0x70, 0, 0x8E, 0}, /* DEBUG_VECTOR = 1 */
+ {}, /* Null (int 2) */
+ {RUN, 0x70, 0, 0xEE, 0}, /* BREAKPOINT_VECTOR = 3 */
+};
+
+struct pseudo_desc {
+ unsigned short limit;
+ unsigned short base_low;
+ unsigned short base_high;
+ };
+
+struct pseudo_desc Gdtr = { sizeof Gdt - 1, RUN, RUN };
+struct pseudo_desc Idtr_prot = { sizeof Idt - 1, RUN, RUN };
+struct pseudo_desc Idtr_real = { 0x400 - 1, 0x0, 0x0 };
diff --git a/sys/i386/conf/GENERICAH b/sys/i386/conf/GENERICAH
new file mode 100644
index 0000000..0e98a3d
--- /dev/null
+++ b/sys/i386/conf/GENERICAH
@@ -0,0 +1,88 @@
+#
+# GENERICAH -- Generic machine with WD/AHx family disks
+#
+# $Id: GENERICAH,v 1.32 1994/05/19 10:55:32 jkh Exp $
+#
+
+machine "i386"
+cpu "I386_CPU"
+cpu "I486_CPU"
+ident GENERICAH
+timezone 8 dst
+maxusers 10
+options MATH_EMULATE #Support for x87 emulation
+options INET #InterNETworking
+options FFS #Berkeley Fast File System
+options KERNFS #Kernel File System
+options FDESC #File Descriptor File System
+options MFS #Memory File System
+options "CD9660" #CD ISO9660 File System
+options NFS #Network File System
+options "COMPAT_43" #Compatible with BSD 4.3
+options "TCP_COMPAT_42" #TCP/IP compatible with 4.2
+options XSERVER #Xserver
+options UCONSOLE #X Console support
+options "FAT_CURSOR" #block cursor in syscons or pccons
+#options GATEWAY #Host is a Gateway (forwards packets)
+options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device
+options "NCONS=4" #4 virtual consoles
+options "STAR_SAVER" #syscons "stars" screen saver
+
+config kernel root on wd0 swap on wd0 and wd1 and sd0 and sd1 dumps on wd0
+
+controller isa0
+
+controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk fd0 at fdc0 drive 0
+disk fd1 at fdc0 drive 1
+#tape ft0 at fdc0 drive 2
+
+controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr
+disk wd0 at wdc0 drive 0
+disk wd1 at wdc0 drive 1
+
+controller wdc1 at isa? port "IO_WD2" bio irq 15 vector wdintr
+disk wd2 at wdc1 drive 0
+disk wd3 at wdc1 drive 1
+
+controller ahb0 at isa? bio irq 11 vector ahbintr
+controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr
+controller scbus0
+
+device sd0
+device sd1
+device sd2
+device sd3
+
+device st0
+device st1
+
+device cd0 #Only need one of these, the code dynamically grows
+
+device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr
+device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr
+device mcd1 at isa? port 0x340 bio irq 11 vector mcdintr
+
+device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
+device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
+
+device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
+device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr
+device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr
+device sio3 at isa? port "IO_COM4" tty irq 9 vector siointr
+
+device lpt0 at isa? port? tty irq 7 vector lptintr
+device lpt1 at isa? port? tty
+device lpt2 at isa? port? tty
+
+device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr
+device ed1 at isa? port 0x300 net irq 5 iomem 0xd8000 vector edintr
+device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr
+device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr
+
+pseudo-device loop
+pseudo-device ether
+pseudo-device log
+pseudo-device sl 2
+pseudo-device pty 16
+pseudo-device speaker
diff --git a/sys/i386/conf/GENERICBT b/sys/i386/conf/GENERICBT
new file mode 100644
index 0000000..7df05d4
--- /dev/null
+++ b/sys/i386/conf/GENERICBT
@@ -0,0 +1,88 @@
+#
+# GENERICBT -- Generic machine with WD/BTx family disks
+#
+# $Id: GENERICBT,v 1.32 1994/05/19 10:55:34 jkh Exp $
+#
+
+machine "i386"
+cpu "I386_CPU"
+cpu "I486_CPU"
+ident GENERICBT
+timezone 8 dst
+maxusers 10
+options MATH_EMULATE #Support for x87 emulation
+options INET #InterNETworking
+options FFS #Berkeley Fast File System
+options KERNFS #Kernel File System
+options FDESC #File Descriptor File System
+options MFS #Memory File System
+options "CD9660" #CD ISO9660 File System
+options NFS #Network File System
+options "COMPAT_43" #Compatible with BSD 4.3
+options "TCP_COMPAT_42" #TCP/IP compatible with 4.2
+options XSERVER #Xserver
+options UCONSOLE #X Console support
+options "FAT_CURSOR" #block cursor in syscons or pccons
+#options GATEWAY #Host is a Gateway (forwards packets)
+options "NCONS=4" #4 virtual consoles
+options "STAR_SAVER" #syscons "stars" screen saver
+options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device
+
+config kernel root on wd0 swap on wd0 and wd1 and sd0 and sd1 dumps on wd0
+
+controller isa0
+
+controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk fd0 at fdc0 drive 0
+disk fd1 at fdc0 drive 1
+#tape ft0 at fdc0 drive 2
+
+controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr
+disk wd0 at wdc0 drive 0
+disk wd1 at wdc0 drive 1
+
+controller wdc1 at isa? port "IO_WD2" bio irq 15 vector wdintr
+disk wd2 at wdc1 drive 0
+disk wd3 at wdc1 drive 1
+
+controller bt0 at isa? port "IO_BT0" bio irq 12 vector btintr
+controller uha0 at isa? port "IO_UHA0" bio irq 14 drq 5 vector uhaintr
+controller scbus0
+
+device sd0
+device sd1
+device sd2
+device sd3
+
+device st0
+device st1
+
+device cd0 #Only need one of these, the code dynamically grows
+
+device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr
+device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr
+device mcd1 at isa? port 0x340 bio irq 11 vector mcdintr
+
+device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
+device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
+
+device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
+device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr
+device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr
+device sio3 at isa? port "IO_COM4" tty irq 9 vector siointr
+
+device lpt0 at isa? port? tty irq 7 vector lptintr
+device lpt1 at isa? port? tty
+device lpt2 at isa? port? tty
+
+device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr
+device ed1 at isa? port 0x300 net irq 5 iomem 0xd8000 vector edintr
+device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr
+device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr
+
+pseudo-device loop
+pseudo-device ether
+pseudo-device log
+pseudo-device sl 2
+pseudo-device pty 16
+pseudo-device speaker
diff --git a/sys/i386/conf/LINT b/sys/i386/conf/LINT
new file mode 100644
index 0000000..90c79b8
--- /dev/null
+++ b/sys/i386/conf/LINT
@@ -0,0 +1,197 @@
+#
+# LINT -- config file for checking all the sources, tries to pull in
+# as much of the source tree as it can.
+#
+# This kernel is NOT MEANT to be runnable!
+#
+# $Id: LINT,v 1.70 1994/05/17 23:20:32 jkh Exp $
+#
+
+machine "i386"
+cpu "I386_CPU"
+cpu "I486_CPU"
+ident LINT
+timezone 8 dst
+maxusers 10
+maxfdescs 2048 #Max file descriptors per process
+options MATH_EMULATE #Support for x87 emulation
+
+# Do not use in binary distributions
+#options GPL_MATH_EMULATE #Support for x87 emualtion via
+ #new math emulator
+
+config "386bsd" root on wd0 swap on wd0 and sd0 dumps on wd0
+
+#
+# options that appear as inline #ifdef's
+#
+options "COM_BIDIR" #Bidirectional support in sys/isa/sio.c
+options "COM_MULTIPORT" #Multiport support in sys/isa/sio.c
+options "FIFO_TRIGGER=FIFO_TRIGGER_1" #Use this fifo value in sio.c
+
+options "COMPAT_43" #compatible with BSD 4.3
+options "SYMTAB_SPACE=113498" #This kernel needs LOTS of symtable
+options GATEWAY #internetwork gateway
+options KTRACE #kernel tracing
+
+options "NCONS=8" #number of syscons virtual consoles
+options "FAT_CURSOR" #block cursor in syscons or pccons
+options "STAR_SAVER" #syscons "stars" screen saver
+options "FADE_SAVER" #syscons "fade" screen saver
+options "SNAKE_SAVER" #syscons "snake" screen saver
+options "BLANK_SAVER" #syscons "blank" screen saver
+
+#options ALLOW_CONFLICT_IOADDR #no IO addr conflict checks (PS/2 mice)
+#options ALLOW_CONFLICT_IRQ #no IRQ conflict checks (mport serial)
+
+options "TCP_COMPAT_42" #tcp/ip compatible with 4.2
+ # ^^^ NOT RECOMMENDED FOR NORMAL USE
+options UCONSOLE #x console support
+options XSERVER #xserver
+options DECBIT #here because clnp.h wanted it here
+ #support for CLNP ``congestion
+ #experienced'' bit in ISO-TP
+options TROLL #CLNP network error simulator
+options ICMPPRINTFS #ICMP packet dump by printf()
+options NSERRPRINTFS #ditto for XNS Error protocol
+ #^^above three NOT RECOMMENTED
+options FASTLINKS #support for fast symbolic links
+options MACHVMCOMPAT #support for Mach-style vm calls
+options IPBROADCASTECHO=1 #send reply to broadcast pings
+options IPMASKAGENT=1 #send reply to icmp mask requests
+options TPCONS #support X.25 network-layer service
+options USER_LDT #allow user-level control of i386 ldt
+
+# See /sys/i386/doc/sound.doc for information about EXCLUDE options for
+# the sound drivers.
+
+# Multicast support.
+options MULTICAST # Multicast code
+options MROUTING # Multicast routing
+
+#
+# options that are in sys/conf/files
+#
+pseudo-device bpfilter 4 #berkeley packet filter
+options CCITT
+device cd0 #Only need one of these, the code dynamically grows
+device ch0
+pseudo-device ddb
+pseudo-device devpager
+options EON
+pseudo-device ether
+options FIFO
+#pseudo-device imp
+options INET #Internet communications protocols
+options ISO
+options ISOFS #ISO 9660 File System
+pseudo-device loop
+options MFS #Memory File System
+options NFS #Network File System
+options NS #Xerox NS communications protocols
+options NSIP #XNS over IP
+options PCFS #PC (MSDOS) File System
+pseudo-device ppp 2
+pseudo-device pty 4
+options QUOTA #enable disk quotas
+options RMP #HP remote maint protocol
+controller scbus0
+device sd0
+device sd1
+device sd2
+device sd3
+pseudo-device sl 2
+device st0
+device st1
+pseudo-device swappager
+options SYSVSHM
+options "SHMMAXPGS=64" # 256Kb of sharable memory
+options SYSVSEM
+options SYSVMSG
+#pseudo-device tb #tablet line discipline.
+options TPIP # ISO TP class 4 over IP
+#pseudo-device tun
+device uk0 #unknown scsi devices
+pseudo-device vnodepager
+
+#
+# options that are in sys/i386/conf/files.i386
+#
+#This is needed here so the isa? below will work
+controller isa0
+
+# driver for the Adaptec 154x SCSI cards.
+controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr
+# driver for the Adaptec 174x SCSI cards.
+controller ahb0 at isa? bio irq 11 vector ahbintr
+# driver for the Bustek 742.
+controller bt0 at isa? port "IO_BT0" bio irq 12 vector btintr
+# driver for the Seagate ST01/ST02 card, not yet finished.
+#controller sg0 at isa? bio irq 5 iomem 0xc8000 iosiz 0x2000 vector sgintr
+controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk fd0 at fdc0 drive 0
+disk fd1 at fdc0 drive 1
+tape ft0 at fdc0 drive 2
+
+# driver for the Western Digital and SMCC WD80xx cards, for the Novell
+# NE1000/2000 card and the 3COM 3C503 card.
+device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr
+# driver for the AT&T Starlan card.
+device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr
+#driver for the Isolan AT 4114-0 and the Isolink 4110 ethernet card.
+device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr
+#device ix0 at isa? port 0x320 net irq 10 iomem 0xd0000 iosiz 32768 vector ixintr
+# driver for the Etherlink III ( 3C509 ) card, beta version.
+device ep0 at isa? port 0x300 net irq 10 vector epintr
+#driver for the 3c501
+device el0 at isa? port 0x300 net irq 9 vector elintr
+
+#special cased above:
+#controller isa0
+# interruptless parallel printer port driver
+device lpa0 at isa? port "IO_LPT1" tty
+device lpa1 at isa? port "IO_LPT2" tty
+# interrupt driven parallel printer port driver
+device lpt0 at isa? port "IO_LPT3" tty irq 7 vector lptintr
+# Driver for Mitsumi CD-ROM players
+device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr
+# Driver for Logitech and ATI inport bus mice
+device mse0 at isa? port 0x23c tty irq 5 vector mseintr
+device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
+device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint
+#only one of pc0 or sc0 allowed
+#device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
+device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
+device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr
+device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr
+device sio3 at isa? port "IO_COM4" tty irq 9 vector siointr
+#PS/2 mouse driver (must follow pc0 or sc0 if enabled). Also enable
+#ALLOW_CONFLICT_IOADDR option (see above) if you want to use this.
+#device psm0 at isa? port "IO_KBD" tty irq 12 vector psmintr
+
+pseudo-device speaker
+#tw device-driver
+controller uha0 at isa? port "IO_UHA0" bio irq 14 drq 5 vector uhaintr
+controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr
+disk wd0 at wdc0 drive 0
+disk wd1 at wdc0 drive 1
+controller wdc1 at isa? port "IO_WD2" bio irq 15 vector wdintr
+disk wd2 at wdc1 drive 0
+disk wd3 at wdc1 drive 1
+device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr
+
+# Various sound card drivers.
+# See /sys/doc/sound.doc for more information.
+device snd5 at isa? port 0x330 irq 6 vector mpuintr
+device snd4 at isa? port 0x220 irq 15 drq 6 vector gusintr
+device snd3 at isa? port 0x388 irq 10 drq 6 vector pasintr
+device snd2 at isa? port 0x220 irq 7 drq 1 vector sbintr
+device snd6 at isa? port 0x220 irq 7 drq 5 vector sbintr
+device snd7 at isa? port 0x300
+device snd1 at isa? port 0x388
+
+# The digital speaker driver (/dev/pcaudio).
+device pca0 at isa? tty
+
+# options that have not been resolved yet
+pseudo-device log
diff --git a/sys/i386/conf/Makefile.i386 b/sys/i386/conf/Makefile.i386
new file mode 100644
index 0000000..20d1ee9
--- /dev/null
+++ b/sys/i386/conf/Makefile.i386
@@ -0,0 +1,190 @@
+# Copyright 1990 W. Jolitz
+# from: @(#)Makefile.i386 7.1 5/10/91
+# $Id: Makefile.i386,v 1.23 1994/03/21 20:48:47 ats Exp $
+#
+# Makefile for FreeBSD
+#
+# This makefile is constructed from a machine description:
+# config machineid
+# Most changes should be made in the machine description
+# /sys/i386/conf/``machineid''
+# after which you should do
+# config machineid
+# Generic makefile changes should be made in
+# /sys/i386/conf/Makefile.i386
+# after which config should be rerun for all machines.
+#
+# N.B.: NO DEPENDENCIES ON FOLLOWING FLAGS ARE INVISIBLE TO MAKEFILE
+# IF YOU CHANGE THE DEFINITION OF ANY OF THESE RECOMPILE EVERYTHING
+#
+# -DTRACE compile in kernel tracing hooks
+# -DQUOTA compile in file system quotas
+#
+TOUCH= touch -f -c
+LD= /usr/bin/ld
+CC= cc
+CPP= cpp
+STRIP= strip
+DBSYM= /usr/sbin/dbsym
+
+S= ../..
+I386= ../../i386
+
+CWARNFLAGS=-W -Wreturn-type -Wcomment
+#
+# The following flags are next up for working on:
+# -Wredundant-decls -Wnested-externs
+#
+# When working on removing warnings from code, the `-Werror' flag should be
+# of material assistance.
+#
+COPTFLAGS=-O
+INCLUDES= -I. -I$S -I$S/sys
+COPTS= ${INCLUDES} ${IDENT} -DKERNEL -Di386 -DNPX
+ASFLAGS=
+CFLAGS= ${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS}
+LOAD_ADDRESS?= F0100000
+
+NORMAL_C= ${CC} -c ${CFLAGS} ${PROF} $<
+NORMAL_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $<
+NORMAL_S= ${CPP} -I. -DLOCORE ${COPTS} $< | ${AS} ${ASFLAGS} -o $*.o
+DRIVER_C= ${CC} -c ${CFLAGS} ${PROF} $<
+DRIVER_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $<
+SYSTEM_OBJS=locore.o exception.o swtch.o support.o ${OBJS} param.o \
+ ioconf.o conf.o machdep.o vnode_if.o
+SYSTEM_DEP=Makefile symbols.sort ${SYSTEM_OBJS} libkern.a
+SYSTEM_LD_HEAD= @echo loading $@; rm -f $@
+SYSTEM_LD= @${LD} -Bstatic -Z -T ${LOAD_ADDRESS} -o $@ -X ${SYSTEM_OBJS} vers.o libkern.a
+SYSTEM_LD_TAIL= @echo rearranging symbols; symorder symbols.sort $@; \
+ ${DBSYM} -fT ${LOAD_ADDRESS} $@; ${STRIP} -x $@; size $@; chmod 755 $@
+
+# (XXX) ok, this is weird. but we've got a working ed, and a broken ex, and
+# the script is identical for either... -- cgd
+#
+GPROF.EX= /usr/src/lib/csu.i386/gprof.ex
+PROFILE_C= ${CC} -S -c ${CFLAGS} $< ; \
+ ed - $*.s < ${GPROF.EX} ; \
+ ${AS} -o $@ $*.s ; \
+ rm -f $*.s
+
+%OBJS
+
+%CFILES
+
+%LOAD
+
+libkern.a:
+ -@if [ X${PROF} = X ]; \
+ then ln -s $S/libkern/obj/libkern.a libkern.a; \
+ else ln -s $S/libkern/obj/libkern_p.a libkern.a; \
+ fi; \
+ echo ln -s $S/libkern/obj/libkern.a libkern.a
+
+clean:
+ rm -f eddep *kernel tags *.o locore.i [a-uw-z]*.s \
+ errs linterrs makelinks genassym ,assym.s stamp-assym
+
+#lint: /tmp param.c
+# @lint -hbxn -I. -DGENERIC -Dvolatile= ${COPTS} ${PARAM} \
+# ${I386}/i386/Locore.c ${CFILES} ioconf.c param.c | \
+# grep -v 'struct/union .* never defined' | \
+# grep -v 'possible pointer alignment problem'
+
+symbols.sort: ${I386}/i386/symbols.raw
+ grep -v '^#' ${I386}/i386/symbols.raw \
+ | sed 's/^ //' | sort -u > symbols.sort
+
+locore.o: assym.s ${I386}/i386/locore.s machine/trap.h machine/psl.h \
+ machine/pte.h ${I386}/isa/vector.s ${I386}/isa/icu.s \
+ $S/sys/errno.h machine/specialreg.h \
+ ${I386}/isa/icu.h ${I386}/isa/isa.h vector.h $S/net/netisr.h \
+ machine/asmacros.h
+ ${CPP} -I. -DLOCORE ${COPTS} ${I386}/i386/locore.s | \
+ ${AS} ${ASFLAGS} -o locore.o
+
+exception.o: assym.s ${I386}/i386/exception.s machine/trap.h \
+ ${I386}/isa/vector.s ${I386}/isa/icu.s \
+ $S/sys/errno.h ${I386}/isa/icu.h ${I386}/isa/isa.h vector.h \
+ $S/net/netisr.h machine/asmacros.h
+ ${CPP} -I. -DLOCORE ${COPTS} ${I386}/i386/exception.s | \
+ ${AS} ${ASFLAGS} -o exception.o
+
+swtch.o: assym.s ${I386}/i386/swtch.s \
+ $S/sys/errno.h machine/asmacros.h
+ ${CPP} -I. ${COPTS} ${I386}/i386/swtch.s | \
+ ${AS} ${ASFLAGS} -o swtch.o
+
+support.o: assym.s ${I386}/i386/support.s \
+ $S/sys/errno.h machine/asmacros.h
+ ${CPP} -I. ${COPTS} ${I386}/i386/support.s | \
+ ${AS} ${ASFLAGS} -o support.o
+
+machdep.o: ${I386}/i386/machdep.c Makefile
+ ${CC} -c ${CFLAGS} -DLOAD_ADDRESS=0x${LOAD_ADDRESS} ${PROF} $<
+
+# the following is necessary because autoconf.o depends on #if GENERIC
+autoconf.o: Makefile
+
+# depend on network configuration
+af.o uipc_proto.o locore.o: Makefile
+
+# depends on KDB (cons.o also depends on GENERIC)
+trap.o cons.o: Makefile
+
+assym.s: genassym
+ ./genassym >,assym.s
+ if cmp -s assym.s ,assym.s; then \
+ rm -f ,assym.s; \
+ else \
+ rm -f assym.s; \
+ mv ,assym.s assym.s; \
+ fi
+
+# Some of the defines that genassym outputs may well depend on the
+# value of kernel options.
+genassym: Makefile
+ ${CC} ${INCLUDES} -DKERNEL ${IDENT} ${PARAM} \
+ ${I386}/i386/genassym.c -static -o genassym
+
+depend: assym.s param.c vnode_if.h
+ sh /usr/bin/mkdep -DLOAD_ADDRESS=0x${LOAD_ADDRESS} ${COPTS} ${CFILES} ioconf.c param.c ${I386}/i386/conf.c
+ sh /usr/bin/mkdep -a -p ${INCLUDES} ${IDENT} ${PARAM} ${I386}/i386/genassym.c
+
+links:
+ egrep '#if' ${CFILES} | sed -f $S/conf/defines | \
+ sed -e 's/:.*//' -e 's/\.c/.o/' | sort -u > dontlink
+ echo ${CFILES} | tr -s ' ' '\12' | sed 's/\.c/.o/' | \
+ sort -u | comm -23 - dontlink | \
+ sed 's,../.*/\(.*.o\),rm -f \1;ln -s ../GENERIC/\1 \1,' > makelinks
+ sh makelinks && rm -f dontlink
+
+tags:
+ @echo "see $S/kern/Makefile for tags"
+
+ioconf.o: ioconf.c $S/sys/param.h machine/pte.h $S/sys/buf.h \
+ ${I386}/isa/isa_device.h ${I386}/isa/isa.h ${I386}/isa/icu.h
+ ${CC} -c ${CFLAGS} ioconf.c
+
+conf.o: ${I386}/i386/conf.c $S/sys/conf.h
+ ${CC} -c ${CFLAGS} ${I386}/i386/conf.c
+
+param.c: $S/conf/param.c
+ -rm -f param.c
+ cp $S/conf/param.c .
+
+param.o: param.c Makefile
+ ${CC} -c ${CFLAGS} ${PARAM} param.c
+
+vers.o: ${SYSTEM_DEP} ${SYSTEM_SWAP_DEP}
+ sh $S/conf/newvers.sh ${KERN_IDENT} ${IDENT}
+ ${CC} ${CFLAGS} -c vers.c
+
+vnode_if.c: $S/kern/vnode_if.sh $S/kern/vnode_if.src
+ sh $S/kern/vnode_if.sh $S/kern/vnode_if.src
+vnode_if.h: $S/kern/vnode_if.sh $S/kern/vnode_if.src
+ sh $S/kern/vnode_if.sh $S/kern/vnode_if.src
+
+%RULES
+
+# DO NOT DELETE THIS LINE -- make depend uses it
+
diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES
new file mode 100644
index 0000000..90c79b8
--- /dev/null
+++ b/sys/i386/conf/NOTES
@@ -0,0 +1,197 @@
+#
+# LINT -- config file for checking all the sources, tries to pull in
+# as much of the source tree as it can.
+#
+# This kernel is NOT MEANT to be runnable!
+#
+# $Id: LINT,v 1.70 1994/05/17 23:20:32 jkh Exp $
+#
+
+machine "i386"
+cpu "I386_CPU"
+cpu "I486_CPU"
+ident LINT
+timezone 8 dst
+maxusers 10
+maxfdescs 2048 #Max file descriptors per process
+options MATH_EMULATE #Support for x87 emulation
+
+# Do not use in binary distributions
+#options GPL_MATH_EMULATE #Support for x87 emualtion via
+ #new math emulator
+
+config "386bsd" root on wd0 swap on wd0 and sd0 dumps on wd0
+
+#
+# options that appear as inline #ifdef's
+#
+options "COM_BIDIR" #Bidirectional support in sys/isa/sio.c
+options "COM_MULTIPORT" #Multiport support in sys/isa/sio.c
+options "FIFO_TRIGGER=FIFO_TRIGGER_1" #Use this fifo value in sio.c
+
+options "COMPAT_43" #compatible with BSD 4.3
+options "SYMTAB_SPACE=113498" #This kernel needs LOTS of symtable
+options GATEWAY #internetwork gateway
+options KTRACE #kernel tracing
+
+options "NCONS=8" #number of syscons virtual consoles
+options "FAT_CURSOR" #block cursor in syscons or pccons
+options "STAR_SAVER" #syscons "stars" screen saver
+options "FADE_SAVER" #syscons "fade" screen saver
+options "SNAKE_SAVER" #syscons "snake" screen saver
+options "BLANK_SAVER" #syscons "blank" screen saver
+
+#options ALLOW_CONFLICT_IOADDR #no IO addr conflict checks (PS/2 mice)
+#options ALLOW_CONFLICT_IRQ #no IRQ conflict checks (mport serial)
+
+options "TCP_COMPAT_42" #tcp/ip compatible with 4.2
+ # ^^^ NOT RECOMMENDED FOR NORMAL USE
+options UCONSOLE #x console support
+options XSERVER #xserver
+options DECBIT #here because clnp.h wanted it here
+ #support for CLNP ``congestion
+ #experienced'' bit in ISO-TP
+options TROLL #CLNP network error simulator
+options ICMPPRINTFS #ICMP packet dump by printf()
+options NSERRPRINTFS #ditto for XNS Error protocol
+ #^^above three NOT RECOMMENTED
+options FASTLINKS #support for fast symbolic links
+options MACHVMCOMPAT #support for Mach-style vm calls
+options IPBROADCASTECHO=1 #send reply to broadcast pings
+options IPMASKAGENT=1 #send reply to icmp mask requests
+options TPCONS #support X.25 network-layer service
+options USER_LDT #allow user-level control of i386 ldt
+
+# See /sys/i386/doc/sound.doc for information about EXCLUDE options for
+# the sound drivers.
+
+# Multicast support.
+options MULTICAST # Multicast code
+options MROUTING # Multicast routing
+
+#
+# options that are in sys/conf/files
+#
+pseudo-device bpfilter 4 #berkeley packet filter
+options CCITT
+device cd0 #Only need one of these, the code dynamically grows
+device ch0
+pseudo-device ddb
+pseudo-device devpager
+options EON
+pseudo-device ether
+options FIFO
+#pseudo-device imp
+options INET #Internet communications protocols
+options ISO
+options ISOFS #ISO 9660 File System
+pseudo-device loop
+options MFS #Memory File System
+options NFS #Network File System
+options NS #Xerox NS communications protocols
+options NSIP #XNS over IP
+options PCFS #PC (MSDOS) File System
+pseudo-device ppp 2
+pseudo-device pty 4
+options QUOTA #enable disk quotas
+options RMP #HP remote maint protocol
+controller scbus0
+device sd0
+device sd1
+device sd2
+device sd3
+pseudo-device sl 2
+device st0
+device st1
+pseudo-device swappager
+options SYSVSHM
+options "SHMMAXPGS=64" # 256Kb of sharable memory
+options SYSVSEM
+options SYSVMSG
+#pseudo-device tb #tablet line discipline.
+options TPIP # ISO TP class 4 over IP
+#pseudo-device tun
+device uk0 #unknown scsi devices
+pseudo-device vnodepager
+
+#
+# options that are in sys/i386/conf/files.i386
+#
+#This is needed here so the isa? below will work
+controller isa0
+
+# driver for the Adaptec 154x SCSI cards.
+controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr
+# driver for the Adaptec 174x SCSI cards.
+controller ahb0 at isa? bio irq 11 vector ahbintr
+# driver for the Bustek 742.
+controller bt0 at isa? port "IO_BT0" bio irq 12 vector btintr
+# driver for the Seagate ST01/ST02 card, not yet finished.
+#controller sg0 at isa? bio irq 5 iomem 0xc8000 iosiz 0x2000 vector sgintr
+controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk fd0 at fdc0 drive 0
+disk fd1 at fdc0 drive 1
+tape ft0 at fdc0 drive 2
+
+# driver for the Western Digital and SMCC WD80xx cards, for the Novell
+# NE1000/2000 card and the 3COM 3C503 card.
+device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr
+# driver for the AT&T Starlan card.
+device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr
+#driver for the Isolan AT 4114-0 and the Isolink 4110 ethernet card.
+device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr
+#device ix0 at isa? port 0x320 net irq 10 iomem 0xd0000 iosiz 32768 vector ixintr
+# driver for the Etherlink III ( 3C509 ) card, beta version.
+device ep0 at isa? port 0x300 net irq 10 vector epintr
+#driver for the 3c501
+device el0 at isa? port 0x300 net irq 9 vector elintr
+
+#special cased above:
+#controller isa0
+# interruptless parallel printer port driver
+device lpa0 at isa? port "IO_LPT1" tty
+device lpa1 at isa? port "IO_LPT2" tty
+# interrupt driven parallel printer port driver
+device lpt0 at isa? port "IO_LPT3" tty irq 7 vector lptintr
+# Driver for Mitsumi CD-ROM players
+device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr
+# Driver for Logitech and ATI inport bus mice
+device mse0 at isa? port 0x23c tty irq 5 vector mseintr
+device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
+device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint
+#only one of pc0 or sc0 allowed
+#device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
+device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
+device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr
+device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr
+device sio3 at isa? port "IO_COM4" tty irq 9 vector siointr
+#PS/2 mouse driver (must follow pc0 or sc0 if enabled). Also enable
+#ALLOW_CONFLICT_IOADDR option (see above) if you want to use this.
+#device psm0 at isa? port "IO_KBD" tty irq 12 vector psmintr
+
+pseudo-device speaker
+#tw device-driver
+controller uha0 at isa? port "IO_UHA0" bio irq 14 drq 5 vector uhaintr
+controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr
+disk wd0 at wdc0 drive 0
+disk wd1 at wdc0 drive 1
+controller wdc1 at isa? port "IO_WD2" bio irq 15 vector wdintr
+disk wd2 at wdc1 drive 0
+disk wd3 at wdc1 drive 1
+device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr
+
+# Various sound card drivers.
+# See /sys/doc/sound.doc for more information.
+device snd5 at isa? port 0x330 irq 6 vector mpuintr
+device snd4 at isa? port 0x220 irq 15 drq 6 vector gusintr
+device snd3 at isa? port 0x388 irq 10 drq 6 vector pasintr
+device snd2 at isa? port 0x220 irq 7 drq 1 vector sbintr
+device snd6 at isa? port 0x220 irq 7 drq 5 vector sbintr
+device snd7 at isa? port 0x300
+device snd1 at isa? port 0x388
+
+# The digital speaker driver (/dev/pcaudio).
+device pca0 at isa? tty
+
+# options that have not been resolved yet
+pseudo-device log
diff --git a/sys/i386/conf/SYSCONS b/sys/i386/conf/SYSCONS
new file mode 100644
index 0000000..5c1fb04
--- /dev/null
+++ b/sys/i386/conf/SYSCONS
@@ -0,0 +1,87 @@
+#
+# SYSCONS -- Generic machine with WD/AHx family disks and syscons
+#
+# $Id: SYSCONS,v 1.20 1994/03/01 01:27:03 alm Exp $
+#
+
+machine "i386"
+cpu "I386_CPU"
+cpu "I486_CPU"
+ident SYSCONS
+timezone 8 dst
+maxusers 10
+maxfdescs 2048 #Max file descriptors per process
+options MATH_EMULATE #Support for x87 emulation
+options INET #InterNETworking
+options ISOFS #ISO File System
+options NFS #Network File System
+options PCFS #MSDOS File System
+options "COMPAT_43" #Compatible with BSD 4.3
+options "TCP_COMPAT_42" #TCP/IP compatible with 4.2
+options XSERVER #Xserver
+options UCONSOLE #X Console support
+options "NCONS=8" #8 virtual consoles
+options "FAT_CURSOR" #block cursor in syscons
+options "STAR_SAVER" #syscons "stars" screen saver
+#options GATEWAY #Host is a Gateway (forwards packets)
+
+config "386bsd" root on wd0 swap on wd0 and sd0 dumps on wd0
+
+controller isa0
+
+controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk fd0 at fdc0 drive 0
+disk fd1 at fdc0 drive 1
+#tape ft0 at fdc0 drive 2
+
+controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr
+disk wd0 at wdc0 drive 0
+disk wd1 at wdc0 drive 1
+
+controller wdc1 at isa? port "IO_WD2" bio irq 15 vector wdintr
+disk wd2 at wdc1 drive 0
+disk wd3 at wdc1 drive 1
+
+controller ahb0 at isa? bio irq 11 vector ahbintr
+controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr
+controller scbus0
+
+device sd0
+device sd1
+device sd2
+device sd3
+
+device st0
+device st1
+
+device cd0 #Only need one of these, the code dynamically grows
+
+device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
+device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
+
+device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
+device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr
+device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr
+device sio3 at isa? port "IO_COM4" tty irq 9 vector siointr
+
+device lpt0 at isa? port "IO_LPT3" tty irq 7 vector lptintr
+device lpa0 at isa? port "IO_LPT1" tty
+device lpa1 at isa? port "IO_LPT2" tty
+
+device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr
+device ed1 at isa? port 0x300 net irq 5 iomem 0xd8000 vector edintr
+device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr
+device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr
+
+device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr
+
+pseudo-device loop
+pseudo-device ether
+pseudo-device log
+pseudo-device sl 2
+pseudo-device pty 16
+pseudo-device speaker
+
+pseudo-device swappager
+pseudo-device vnodepager
+pseudo-device devpager
diff --git a/sys/i386/conf/devices.i386 b/sys/i386/conf/devices.i386
new file mode 100644
index 0000000..2b3061a
--- /dev/null
+++ b/sys/i386/conf/devices.i386
@@ -0,0 +1,13 @@
+# This file tells what major numbers the various possible swap devices have.
+#
+# $Id: devices.i386,v 1.5 1994/01/04 20:09:28 nate Exp $
+#
+wd 0
+dk 1
+fd 2
+wt 3
+sd 4
+st 5
+cd 6
+mcd 7
+scd 8
diff --git a/sys/i386/conf/files.i386 b/sys/i386/conf/files.i386
new file mode 100644
index 0000000..f70e799
--- /dev/null
+++ b/sys/i386/conf/files.i386
@@ -0,0 +1,117 @@
+# This file tells config what files go into building a kernel,
+# files marked standard are always included.
+#
+# $Id: files.i386,v 1.32 1994/04/29 21:49:02 gclarkii Exp $
+#
+i386/i386/autoconf.c standard device-driver
+i386/i386/cons.c standard
+i386/i386/db_disasm.c optional ddb
+i386/i386/db_interface.c optional ddb
+i386/i386/db_trace.c optional ddb
+i386/i386/in_cksum.c optional inet
+i386/i386/math_emulate.c optional math_emulate
+i386/i386/mem.c standard
+i386/i386/microtime.s standard
+i386/i386/ns_cksum.c optional ns
+i386/i386/pmap.c standard
+i386/i386/procfs_machdep.c optional procfs
+i386/i386/sys_machdep.c standard
+i386/i386/trap.c standard
+i386/i386/vm_machdep.c standard
+i386/isa/aha1542.c optional aha device-driver
+i386/isa/aha1742.c optional ahb device-driver
+i386/isa/bt742a.c optional bt device-driver
+i386/isa/clock.c standard
+i386/isa/com.c optional com device-driver
+i386/isa/fd.c optional fd device-driver
+i386/isa/ft.c optional ft device-driver
+i386/isa/if_ed.c optional ed device-driver
+i386/isa/if_el.c optional el device-driver
+i386/isa/if_ep.c optional ep device-driver
+i386/isa/if_ie.c optional ie device-driver
+i386/isa/if_is.c optional is device-driver
+i386/isa/if_ix.c optional ix device-driver
+i386/isa/isa.c optional isa device-driver
+i386/isa/lpa.c optional lpa device-driver
+i386/isa/lpt.c optional lpt device-driver
+i386/isa/mcd.c optional mcd device-driver
+i386/isa/mse.c optional mse device-driver
+i386/isa/npx.c optional npx device-driver
+i386/isa/syscons.c optional sc device-driver
+i386/isa/pccons.c optional pc device-driver
+i386/isa/pcaudio.c optional pca device-driver
+i386/isa/psm.c optional psm device-driver
+i386/isa/sb.c optional sb device-driver
+i386/isa/scd.c optional scd device-driver
+i386/isa/sg.c optional sg device-driver
+i386/isa/sio.c optional sio device-driver
+i386/isa/sound/adlib_card.c optional snd device-driver
+i386/isa/sound/audio.c optional snd device-driver
+i386/isa/sound/dev_table.c optional snd device-driver
+i386/isa/sound/dmabuf.c optional snd device-driver
+i386/isa/sound/gus_card.c optional snd device-driver
+i386/isa/sound/gus_midi.c optional snd device-driver
+i386/isa/sound/gus_vol.c optional snd device-driver
+i386/isa/sound/gus_wave.c optional snd device-driver
+i386/isa/sound/ics2101.c optional snd device-driver
+i386/isa/sound/midi.c optional snd device-driver
+i386/isa/sound/midibuf.c optional snd device-driver
+i386/isa/sound/mpu401.c optional snd device-driver
+i386/isa/sound/opl3.c optional snd device-driver
+i386/isa/sound/pas2_card.c optional snd device-driver
+i386/isa/sound/pas2_midi.c optional snd device-driver
+i386/isa/sound/pas2_mixer.c optional snd device-driver
+i386/isa/sound/pas2_pcm.c optional snd device-driver
+i386/isa/sound/patmgr.c optional snd device-driver
+i386/isa/sound/pro_midi.c optional snd device-driver
+i386/isa/sound/sb16_dsp.c optional snd device-driver
+i386/isa/sound/sb16_midi.c optional snd device-driver
+i386/isa/sound/sb_card.c optional snd device-driver
+i386/isa/sound/sb_dsp.c optional snd device-driver
+i386/isa/sound/sb_midi.c optional snd device-driver
+i386/isa/sound/sb_mixer.c optional snd device-driver
+i386/isa/sound/sequencer.c optional snd device-driver
+i386/isa/sound/sound_switch.c optional snd device-driver
+i386/isa/sound/soundcard.c optional snd device-driver
+i386/isa/spkr.c optional speaker
+i386/isa/tw.c optional tw device-driver
+i386/isa/ultra14f.c optional uha device-driver
+i386/isa/wd.c optional wd device-driver
+i386/isa/wt.c optional wt device-driver
+i386/isa/pcvt/pcvt_drv.c optional vt device-driver
+i386/isa/pcvt/pcvt_sup.c optional vt device-driver
+i386/isa/pcvt/pcvt_out.c optional vt device-driver
+i386/isa/pcvt/pcvt_kbd.c optional vt device-driver
+i386/isa/pcvt/pcvt_vtf.c optional vt device-driver
+i386/isa/pcvt/pcvt_ext.c optional vt device-driver
+gnu/fpemul/div_small.s optional gpl_math_emulate
+gnu/fpemul/errors.c optional gpl_math_emulate
+gnu/fpemul/fpu_arith.c optional gpl_math_emulate
+gnu/fpemul/fpu_aux.c optional gpl_math_emulate
+gnu/fpemul/fpu_entry.c optional gpl_math_emulate
+gnu/fpemul/fpu_etc.c optional gpl_math_emulate
+gnu/fpemul/fpu_trig.c optional gpl_math_emulate
+gnu/fpemul/get_address.c optional gpl_math_emulate
+gnu/fpemul/load_store.c optional gpl_math_emulate
+gnu/fpemul/poly_2xm1.c optional gpl_math_emulate
+gnu/fpemul/poly_atan.c optional gpl_math_emulate
+gnu/fpemul/poly_div.s optional gpl_math_emulate
+gnu/fpemul/poly_l2.c optional gpl_math_emulate
+gnu/fpemul/poly_mul64.s optional gpl_math_emulate
+gnu/fpemul/poly_sin.c optional gpl_math_emulate
+gnu/fpemul/poly_tan.c optional gpl_math_emulate
+gnu/fpemul/polynomial.s optional gpl_math_emulate
+gnu/fpemul/reg_add_sub.c optional gpl_math_emulate
+gnu/fpemul/reg_compare.c optional gpl_math_emulate
+gnu/fpemul/reg_constant.c optional gpl_math_emulate
+gnu/fpemul/reg_div.s optional gpl_math_emulate
+gnu/fpemul/reg_ld_str.c optional gpl_math_emulate
+gnu/fpemul/reg_mul.c optional gpl_math_emulate
+gnu/fpemul/reg_norm.s optional gpl_math_emulate
+gnu/fpemul/reg_round.s optional gpl_math_emulate
+gnu/fpemul/reg_u_add.s optional gpl_math_emulate
+gnu/fpemul/reg_u_div.s optional gpl_math_emulate
+gnu/fpemul/reg_u_mul.s optional gpl_math_emulate
+gnu/fpemul/reg_u_sub.s optional gpl_math_emulate
+gnu/fpemul/wm_shrx.s optional gpl_math_emulate
+gnu/fpemul/wm_sqrt.s optional gpl_math_emulate
diff --git a/sys/i386/eisa/aha1742.c b/sys/i386/eisa/aha1742.c
new file mode 100644
index 0000000..27bce5f
--- /dev/null
+++ b/sys/i386/eisa/aha1742.c
@@ -0,0 +1,1244 @@
+/*
+ * Written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * commenced: Sun Sep 27 18:14:01 PDT 1992
+ *
+ * $Id: aha1742.c,v 1.14 1994/01/11 07:24:32 rgrimes Exp $
+ */
+
+#include <sys/types.h>
+
+#ifdef KERNEL /* don't laugh, it compiles as a program too.. look */
+#include <ahb.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <i386/include/pio.h>
+#include <i386/isa/isa_device.h>
+#endif /*KERNEL */
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+/* */
+
+#ifdef KERNEL
+# ifdef NetBSD
+# ifdef DDB
+int Debugger();
+# else /* DDB */
+#define Debugger() panic("should call debugger here (adaptec.c)")
+# endif /* DDB */
+# else
+#include "ddb.h"
+#endif /* netbsd */
+#else /* KERNEL */
+#define NAHB 1
+#endif /* kernel */
+
+#ifndef NetBSD
+typedef timeout_func_t timeout_t;
+#endif
+
+typedef unsigned long int physaddr;
+#include "kernel.h"
+
+#define KVTOPHYS(x) vtophys(x)
+
+#define AHB_ECB_MAX 32 /* store up to 32ECBs at any one time */
+ /* in aha1742 H/W ( Not MAX ? ) */
+#define ECB_HASH_SIZE 32 /* when we have a physical addr. for */
+ /* a ecb and need to find the ecb in */
+ /* space, look it up in the hash table */
+#define ECB_HASH_SHIFT 9 /* only hash on multiples of 512 */
+#define ECB_HASH(x) ((((long int)(x))>>ECB_HASH_SHIFT) % ECB_HASH_SIZE)
+
+#define AHB_NSEG 33 /* number of dma segments supported */
+
+/*
+ * AHA1740 standard EISA Host ID regs (Offset from slot base)
+ */
+#define HID0 0xC80 /* 0,1: msb of ID2, 3-7: ID1 */
+#define HID1 0xC81 /* 0-4: ID3, 4-7: LSB ID2 */
+#define HID2 0xC82 /* product, 0=174[20] 1 = 1744 */
+#define HID3 0xC83 /* firmware revision */
+
+#define CHAR1(B1,B2) (((B1>>2) & 0x1F) | '@')
+#define CHAR2(B1,B2) (((B1<<3) & 0x18) | ((B2>>5) & 0x7)|'@')
+#define CHAR3(B1,B2) ((B2 & 0x1F) | '@')
+
+/* AHA1740 EISA board control registers (Offset from slot base) */
+#define EBCTRL 0xC84
+#define CDEN 0x01
+/*
+ * AHA1740 EISA board mode registers (Offset from slot base)
+ */
+#define PORTADDR 0xCC0
+#define PORTADDR_ENHANCED 0x80
+#define BIOSADDR 0xCC1
+#define INTDEF 0xCC2
+#define SCSIDEF 0xCC3
+#define BUSDEF 0xCC4
+#define RESV0 0xCC5
+#define RESV1 0xCC6
+#define RESV2 0xCC7
+/**** bit definitions for INTDEF ****/
+#define INT9 0x00
+#define INT10 0x01
+#define INT11 0x02
+#define INT12 0x03
+#define INT14 0x05
+#define INT15 0x06
+#define INTHIGH 0x08 /* int high=ACTIVE (else edge) */
+#define INTEN 0x10
+/**** bit definitions for SCSIDEF ****/
+#define HSCSIID 0x0F /* our SCSI ID */
+#define RSTPWR 0x10 /* reset scsi bus on power up or reset */
+/**** bit definitions for BUSDEF ****/
+#define B0uS 0x00 /* give up bus immediatly */
+#define B4uS 0x01 /* delay 4uSec. */
+#define B8uS 0x02
+/*
+ * AHA1740 ENHANCED mode mailbox control regs (Offset from slot base)
+ */
+#define MBOXOUT0 0xCD0
+#define MBOXOUT1 0xCD1
+#define MBOXOUT2 0xCD2
+#define MBOXOUT3 0xCD3
+
+#define ATTN 0xCD4
+#define G2CNTRL 0xCD5
+#define G2INTST 0xCD6
+#define G2STAT 0xCD7
+
+#define MBOXIN0 0xCD8
+#define MBOXIN1 0xCD9
+#define MBOXIN2 0xCDA
+#define MBOXIN3 0xCDB
+
+#define G2STAT2 0xCDC
+
+/*
+ * Bit definitions for the 5 control/status registers
+ */
+#define ATTN_TARGET 0x0F
+#define ATTN_OPCODE 0xF0
+#define OP_IMMED 0x10
+#define AHB_TARG_RESET 0x80
+#define OP_START_ECB 0x40
+#define OP_ABORT_ECB 0x50
+
+#define G2CNTRL_SET_HOST_READY 0x20
+#define G2CNTRL_CLEAR_EISA_INT 0x40
+#define G2CNTRL_HARD_RESET 0x80
+
+#define G2INTST_TARGET 0x0F
+#define G2INTST_INT_STAT 0xF0
+#define AHB_ECB_OK 0x10
+#define AHB_ECB_RECOVERED 0x50
+#define AHB_HW_ERR 0x70
+#define AHB_IMMED_OK 0xA0
+#define AHB_ECB_ERR 0xC0
+#define AHB_ASN 0xD0 /* for target mode */
+#define AHB_IMMED_ERR 0xE0
+
+#define G2STAT_BUSY 0x01
+#define G2STAT_INT_PEND 0x02
+#define G2STAT_MBOX_EMPTY 0x04
+
+#define G2STAT2_HOST_READY 0x01
+
+struct ahb_dma_seg {
+ physaddr addr;
+ long len;
+};
+
+struct ahb_ecb_status {
+ u_short status;
+#define ST_DON 0x0001
+#define ST_DU 0x0002
+#define ST_QF 0x0008
+#define ST_SC 0x0010
+#define ST_DO 0x0020
+#define ST_CH 0x0040
+#define ST_INT 0x0080
+#define ST_ASA 0x0100
+#define ST_SNS 0x0200
+#define ST_INI 0x0800
+#define ST_ME 0x1000
+#define ST_ECA 0x4000
+ u_char ha_status;
+#define HS_OK 0x00
+#define HS_CMD_ABORTED_HOST 0x04
+#define HS_CMD_ABORTED_ADAPTER 0x05
+#define HS_TIMED_OUT 0x11
+#define HS_HARDWARE_ERR 0x20
+#define HS_SCSI_RESET_ADAPTER 0x22
+#define HS_SCSI_RESET_INCOMING 0x23
+ u_char targ_status;
+#define TS_OK 0x00
+#define TS_CHECK_CONDITION 0x02
+#define TS_BUSY 0x08
+ u_long resid_count;
+ u_long resid_addr;
+ u_short addit_status;
+ u_char sense_len;
+ u_char unused[9];
+ u_char cdb[6];
+};
+
+
+struct ecb {
+ u_char opcode;
+#define ECB_SCSI_OP 0x01
+ u_char:4;
+ u_char options:3;
+ u_char:1;
+ short opt1;
+#define ECB_CNE 0x0001
+#define ECB_DI 0x0080
+#define ECB_SES 0x0400
+#define ECB_S_G 0x1000
+#define ECB_DSB 0x4000
+#define ECB_ARS 0x8000
+ short opt2;
+#define ECB_LUN 0x0007
+#define ECB_TAG 0x0008
+#define ECB_TT 0x0030
+#define ECB_ND 0x0040
+#define ECB_DAT 0x0100
+#define ECB_DIR 0x0200
+#define ECB_ST 0x0400
+#define ECB_CHK 0x0800
+#define ECB_REC 0x4000
+#define ECB_NRB 0x8000
+ u_short unused1;
+ physaddr data;
+ u_long datalen;
+ physaddr status;
+ physaddr chain;
+ short unused2;
+ short unused3;
+ physaddr sense;
+ u_char senselen;
+ u_char cdblen;
+ short cksum;
+ u_char cdb[12];
+ /*-----------------end of hardware supported fields----------------*/
+ struct ecb *next; /* in free list */
+ struct scsi_xfer *xs; /* the scsi_xfer for this cmd */
+ int flags;
+#define ECB_FREE 0
+#define ECB_ACTIVE 1
+#define ECB_ABORTED 2
+#define ECB_IMMED 4
+#define ECB_IMMED_FAIL 8
+ struct ahb_dma_seg ahb_dma[AHB_NSEG];
+ struct ahb_ecb_status ecb_status;
+ struct scsi_sense_data ecb_sense;
+ struct ecb *nexthash;
+ physaddr hashkey; /* physaddr of this struct */
+};
+
+struct ahb_data {
+ int flags;
+#define AHB_INIT 0x01;
+ int baseport;
+ struct ecb *ecbhash[ECB_HASH_SIZE];
+ struct ecb *free_ecb;
+ int our_id; /* our scsi id */
+ int vect;
+ struct ecb *immed_ecb; /* an outstanding immediete command */
+ struct scsi_link sc_link;
+ int numecbs;
+} *ahbdata[NAHB];
+
+int ahbprobe();
+int ahbprobe1 __P((struct isa_device *dev));
+int ahb_attach();
+int ahb_init __P((int unit));
+int ahbintr();
+int32 ahb_scsi_cmd();
+void ahb_timeout(caddr_t);
+void ahb_done();
+struct ecb *cheat;
+void ahb_free_ecb();
+void ahbminphys();
+struct ecb *ahb_ecb_phys_kv();
+u_int32 ahb_adapter_info();
+
+#define MAX_SLOTS 8 /* XXX should this be 16?? Need EISA spec */
+static ahb_slot = 0; /* slot last board was found in */
+static ahb_unit = 0;
+int ahb_debug = 0;
+#define AHB_SHOWECBS 0x01
+#define AHB_SHOWINTS 0x02
+#define AHB_SHOWCMDS 0x04
+#define AHB_SHOWMISC 0x08
+#define FAIL 1
+#define SUCCESS 0
+#define PAGESIZ 4096
+
+#ifdef KERNEL
+struct isa_driver ahbdriver =
+{
+ ahbprobe,
+ ahb_attach,
+ "ahb"
+};
+
+struct scsi_adapter ahb_switch =
+{
+ ahb_scsi_cmd,
+ ahbminphys,
+ 0,
+ 0,
+ ahb_adapter_info,
+ "ahb",
+ { 0, 0 }
+};
+
+/* the below structure is so we have a default dev struct for our link struct */
+struct scsi_device ahb_dev =
+{
+ NULL, /* Use default error handler */
+ NULL, /* have a queue, served by this */
+ NULL, /* have no async handler */
+ NULL, /* Use default 'done' routine */
+ "ahb",
+ 0,
+ { 0, 0 }
+};
+
+#endif /*KERNEL */
+
+#ifndef KERNEL
+main()
+{
+ printf("ahb_data size is %d\n", sizeof(struct ahb_data));
+ printf("ecb size is %d\n", sizeof(struct ecb));
+}
+
+#else /*KERNEL */
+
+/*
+ * Function to send a command out through a mailbox
+ */
+void
+ahb_send_mbox(int unit, int opcode, int target, struct ecb *ecb)
+{
+ int port = ahbdata[unit]->baseport;
+ int wait = 300; /* 3ms should be enough */
+ int stport = port + G2STAT;
+ int s = splbio();
+
+ while (--wait) {
+ if ((inb(stport) & (G2STAT_BUSY | G2STAT_MBOX_EMPTY))
+ == (G2STAT_MBOX_EMPTY))
+ break;
+ DELAY(10);
+ }
+ if (wait == 0) {
+ printf("ahb%d: board not responding\n", unit);
+ Debugger("aha1742");
+ }
+ outl(port + MBOXOUT0, KVTOPHYS(ecb)); /* don't know this will work */
+ outb(port + ATTN, opcode | target);
+
+ splx(s);
+}
+
+/*
+ * Function to poll for command completion when in poll mode
+ */
+int
+ahb_poll(int unit, int wait)
+{ /* in msec */
+ struct ahb_data *ahb = ahbdata[unit];
+ int port = ahb->baseport;
+ int stport = port + G2STAT;
+
+ retry:
+ while (--wait) {
+ if (inb(stport) & G2STAT_INT_PEND)
+ break;
+ DELAY(1000);
+ } if (wait == 0) {
+ printf("ahb%d: board not responding\n", unit);
+ return (EIO);
+ }
+ if (cheat != ahb_ecb_phys_kv(ahb, inl(port + MBOXIN0))) {
+ printf("discarding %x ", inl(port + MBOXIN0));
+ outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT);
+ DELAY(50000);
+ goto retry;
+ }
+ /* don't know this will work */
+ ahbintr(unit);
+ return (0);
+}
+
+/*
+ * Function to send an immediate type command to the adapter
+ */
+void
+ahb_send_immed(int unit, int target, u_long cmd)
+{
+ int port = ahbdata[unit]->baseport;
+ int s = splbio();
+ int stport = port + G2STAT;
+ int wait = 100; /* 1 ms enough? */
+
+ while (--wait) {
+ if ((inb(stport) & (G2STAT_BUSY | G2STAT_MBOX_EMPTY))
+ == (G2STAT_MBOX_EMPTY))
+ break;
+ DELAY(10);
+ } if (wait == 0) {
+ printf("ahb%d: board not responding\n", unit);
+ Debugger("aha1742");
+ }
+ outl(port + MBOXOUT0, cmd); /* don't know this will work */
+ outb(port + G2CNTRL, G2CNTRL_SET_HOST_READY);
+ outb(port + ATTN, OP_IMMED | target);
+ splx(s);
+}
+
+/*
+ * Check the slots looking for a board we recognise
+ * If we find one, note it's address (slot) and call
+ * the actual probe routine to check it out.
+ */
+int
+ahbprobe(dev)
+ struct isa_device *dev;
+{
+ int port;
+ u_char byte1, byte2, byte3;
+
+ ahb_slot++;
+ while (ahb_slot <= MAX_SLOTS) {
+ port = 0x1000 * ahb_slot;
+ byte1 = inb(port + HID0);
+ byte2 = inb(port + HID1);
+ byte3 = inb(port + HID2);
+ if (byte1 == 0xff) {
+ ahb_slot++;
+ continue;
+ }
+ if ((CHAR1(byte1, byte2) == 'A')
+ && (CHAR2(byte1, byte2) == 'D')
+ && (CHAR3(byte1, byte2) == 'P')
+ && ((byte3 == 0) || (byte3 == 1))) {
+ dev->id_iobase = port;
+ return ahbprobe1(dev);
+ }
+ ahb_slot++;
+ }
+ return 0;
+}
+
+/*
+ * Check if the device can be found at the port given
+ * and if so, set it up ready for further work
+ * as an argument, takes the isa_device structure from
+ * autoconf.c.
+ */
+int
+ahbprobe1(dev)
+ struct isa_device *dev;
+{
+ /*
+ * find unit and check we have that many defined
+ */
+ int unit = ahb_unit;
+ struct ahb_data *ahb;
+
+ if (unit >= NAHB) {
+ printf("ahb: unit number (%d) too high\n", unit);
+ return 0;
+ }
+ dev->id_unit = unit;
+
+ /*
+ * Allocate a storage area for us
+ */
+ if (ahbdata[unit]) {
+ printf("ahb%d: memory already allocated\n", unit);
+ return 0;
+ }
+ ahb = malloc(sizeof(struct ahb_data), M_TEMP, M_NOWAIT);
+ if (!ahb) {
+ printf("ahb%d: cannot malloc!\n", unit);
+ return 0;
+ }
+ bzero(ahb, sizeof(struct ahb_data));
+ ahbdata[unit] = ahb;
+ ahb->baseport = dev->id_iobase;
+ /*
+ * Try initialise a unit at this location
+ * sets up dma and bus speed, loads ahb->vect
+ */
+ if (ahb_init(unit) != 0) {
+ ahbdata[unit] = NULL;
+ free(ahb, M_TEMP);
+ return (0);
+ }
+ /*
+ * If it's there, put in it's interrupt vectors
+ */
+ dev->id_irq = (1 << ahb->vect);
+ dev->id_drq = -1; /* use EISA dma */
+
+ ahb_unit++;
+ return 0x1000;
+}
+
+/*
+ * Attach all the sub-devices we can find
+ */
+int
+ahb_attach(dev)
+ struct isa_device *dev;
+{
+#ifdef NetBSD
+ int unit = dev->id_masunit;
+#else
+ int unit = dev->id_unit;
+#endif
+ struct ahb_data *ahb = ahbdata[unit];
+
+ /*
+ * fill in the prototype scsi_link.
+ */
+ ahb->sc_link.adapter_unit = unit;
+ ahb->sc_link.adapter_targ = ahb->our_id;
+ ahb->sc_link.adapter = &ahb_switch;
+ ahb->sc_link.device = &ahb_dev;
+
+ /*
+ * ask the adapter what subunits are present
+ */
+ scsi_attachdevs(&(ahb->sc_link));
+
+ return 1;
+}
+
+/*
+ * Return some information to the caller about
+ * the adapter and it's capabilities
+ */
+u_int32
+ahb_adapter_info(unit)
+ int unit;
+{
+ return (2); /* 2 outstanding requests at a time per device */
+}
+
+/*
+ * Catch an interrupt from the adaptor
+ */
+int
+ahbintr(unit)
+ int unit;
+{
+ struct ecb *ecb;
+ unsigned char stat;
+ u_char ahbstat;
+ int target;
+ long int mboxval;
+ struct ahb_data *ahb = ahbdata[unit];
+
+ int port = ahb->baseport;
+
+#ifdef AHBDEBUG
+ printf("ahbintr ");
+#endif /*AHBDEBUG */
+
+ while (inb(port + G2STAT) & G2STAT_INT_PEND) {
+ /*
+ * First get all the information and then
+ * acknowlege the interrupt
+ */
+ ahbstat = inb(port + G2INTST);
+ target = ahbstat & G2INTST_TARGET;
+ stat = ahbstat & G2INTST_INT_STAT;
+ mboxval = inl(port + MBOXIN0); /* don't know this will work */
+ outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT);
+#ifdef AHBDEBUG
+ printf("status = 0x%x ", stat);
+#endif /*AHBDEBUG */
+ /*
+ * Process the completed operation
+ */
+
+ if (stat == AHB_ECB_OK) { /* common case is fast */
+ ecb = ahb_ecb_phys_kv(ahb, mboxval);
+ } else {
+ switch (stat) {
+ case AHB_IMMED_OK:
+ ecb = ahb->immed_ecb;
+ ahb->immed_ecb = 0;
+ break;
+ case AHB_IMMED_ERR:
+ ecb = ahb->immed_ecb;
+ ecb->flags |= ECB_IMMED_FAIL;
+ ahb->immed_ecb = 0;
+ break;
+ case AHB_ASN: /* for target mode */
+ printf("ahb%d: Unexpected ASN interrupt(%x)\n",
+ unit, mboxval);
+ ecb = 0;
+ break;
+ case AHB_HW_ERR:
+ printf("ahb%d: Hardware error interrupt(%x)\n",
+ unit, mboxval);
+ ecb = 0;
+ break;
+ case AHB_ECB_RECOVERED:
+ ecb = ahb_ecb_phys_kv(ahb, mboxval);
+ break;
+ case AHB_ECB_ERR:
+ ecb = ahb_ecb_phys_kv(ahb, mboxval);
+ break;
+ default:
+ printf(" Unknown return from ahb%d(%x)\n", unit, ahbstat);
+ ecb = 0;
+ }
+ } if (ecb) {
+#ifdef AHBDEBUG
+ if (ahb_debug & AHB_SHOWCMDS) {
+ show_scsi_cmd(ecb->xs);
+ }
+ if ((ahb_debug & AHB_SHOWECBS) && ecb)
+ printf("<int ecb(%x)>", ecb);
+#endif /*AHBDEBUG */
+ untimeout((timeout_t)ahb_timeout, (caddr_t)ecb);
+ ahb_done(unit, ecb, ((stat == AHB_ECB_OK) ? SUCCESS : FAIL));
+ }
+ }
+ return 1;
+}
+
+/*
+ * We have a ecb which has been processed by the
+ * adaptor, now we look to see how the operation
+ * went.
+ */
+void
+ahb_done(unit, ecb, state)
+ int unit, state;
+ struct ecb *ecb;
+{
+ struct ahb_ecb_status *stat = &ecb->ecb_status;
+ struct scsi_sense_data *s1, *s2;
+ struct scsi_xfer *xs = ecb->xs;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB2, ("ahb_done\n"));
+ /*
+ * Otherwise, put the results of the operation
+ * into the xfer and call whoever started it
+ */
+ if (ecb->flags & ECB_IMMED) {
+ if (ecb->flags & ECB_IMMED_FAIL) {
+ xs->error = XS_DRIVER_STUFFUP;
+ }
+ goto done;
+ }
+ if ((state == SUCCESS) || (xs->flags & SCSI_ERR_OK)) { /* All went correctly OR errors expected */
+ xs->resid = 0;
+ xs->error = 0;
+ } else {
+
+ s1 = &(ecb->ecb_sense);
+ s2 = &(xs->sense);
+
+ if (stat->ha_status) {
+ switch (stat->ha_status) {
+ case HS_SCSI_RESET_ADAPTER:
+ break;
+ case HS_SCSI_RESET_INCOMING:
+ break;
+ case HS_CMD_ABORTED_HOST: /* No response */
+ case HS_CMD_ABORTED_ADAPTER: /* No response */
+ break;
+ case HS_TIMED_OUT: /* No response */
+#ifdef AHBDEBUG
+ if (ahb_debug & AHB_SHOWMISC) {
+ printf("timeout reported back\n");
+ }
+#endif /*AHBDEBUG */
+ xs->error = XS_TIMEOUT;
+ break;
+ default: /* Other scsi protocol messes */
+ xs->error = XS_DRIVER_STUFFUP;
+#ifdef AHBDEBUG
+ if (ahb_debug & AHB_SHOWMISC) {
+ printf("unexpected ha_status: %x\n",
+ stat->ha_status);
+ }
+#endif /*AHBDEBUG */
+ }
+ } else {
+ switch (stat->targ_status) {
+ case TS_CHECK_CONDITION:
+ /* structure copy!!!!! */
+ *s2 = *s1;
+ xs->error = XS_SENSE;
+ break;
+ case TS_BUSY:
+ xs->error = XS_BUSY;
+ break;
+ default:
+#ifdef AHBDEBUG
+ if (ahb_debug & AHB_SHOWMISC) {
+ printf("unexpected targ_status: %x\n",
+ stat->targ_status);
+ }
+#endif /*AHBDEBUG */
+ xs->error = XS_DRIVER_STUFFUP;
+ }
+ }
+ }
+done: xs->flags |= ITSDONE;
+ ahb_free_ecb(unit, ecb, xs->flags);
+ scsi_done(xs);
+}
+
+/*
+ * A ecb (and hence a mbx-out is put onto the
+ * free list.
+ */
+void
+ahb_free_ecb(unit, ecb, flags)
+ int unit, flags;
+ struct ecb *ecb;
+{
+ unsigned int opri = 0;
+ struct ahb_data *ahb = ahbdata[unit];
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+
+ ecb->next = ahb->free_ecb;
+ ahb->free_ecb = ecb;
+ ecb->flags = ECB_FREE;
+ /*
+ * If there were none, wake abybody waiting for
+ * one to come free, starting with queued entries
+ */
+ if (!ecb->next) {
+ wakeup((caddr_t)&ahb->free_ecb);
+ }
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+}
+
+/*
+ * Get a free ecb
+ * If there are none, see if we can allocate a
+ * new one. If so, put it in the hash table too
+ * otherwise either return an error or sleep
+ */
+struct ecb *
+ahb_get_ecb(unit, flags)
+ int unit, flags;
+{
+ struct ahb_data *ahb = ahbdata[unit];
+ unsigned opri = 0;
+ struct ecb *ecbp;
+ int hashnum;
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+ /*
+ * If we can and have to, sleep waiting for one to come free
+ * but only if we can't allocate a new one.
+ */
+ while (!(ecbp = ahb->free_ecb)) {
+ if (ahb->numecbs < AHB_ECB_MAX) {
+ ecbp = (struct ecb *) malloc(sizeof(struct ecb),
+ M_TEMP,
+ M_NOWAIT);
+ if (ecbp) {
+ bzero(ecbp, sizeof(struct ecb));
+ ahb->numecbs++;
+ ecbp->flags = ECB_ACTIVE;
+ /*
+ * put in the phystokv hash table
+ * Never gets taken out.
+ */
+ ecbp->hashkey = KVTOPHYS(ecbp);
+ hashnum = ECB_HASH(ecbp->hashkey);
+ ecbp->nexthash = ahb->ecbhash[hashnum];
+ ahb->ecbhash[hashnum] = ecbp;
+ } else {
+ printf("ahb%d: Can't malloc ECB\n", unit);
+ } goto gottit;
+ } else {
+ if (!(flags & SCSI_NOSLEEP)) {
+ tsleep((caddr_t)&ahb->free_ecb, PRIBIO,
+ "ahbecb", 0);
+ }
+ }
+ } if (ecbp) {
+ /* Get ECB from from free list */
+ ahb->free_ecb = ecbp->next;
+ ecbp->flags = ECB_ACTIVE;
+ }
+gottit: if (!(flags & SCSI_NOMASK))
+ splx(opri);
+
+ return (ecbp);
+}
+
+/*
+ * given a physical address, find the ecb that
+ * it corresponds to:
+ */
+struct ecb *
+ahb_ecb_phys_kv(ahb, ecb_phys)
+ struct ahb_data *ahb;
+ physaddr ecb_phys;
+{
+ int hashnum = ECB_HASH(ecb_phys);
+ struct ecb *ecbp = ahb->ecbhash[hashnum];
+
+ while (ecbp) {
+ if (ecbp->hashkey == ecb_phys)
+ break;
+ ecbp = ecbp->nexthash;
+ }
+ return ecbp;
+}
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+ahb_init(unit)
+ int unit;
+{
+ struct ahb_data *ahb = ahbdata[unit];
+ int port = ahb->baseport;
+ int intdef;
+ int wait = 1000; /* 1 sec enough? */
+ int i;
+ int stport = port + G2STAT;
+#define NO_NO 1
+#ifdef NO_NO
+ /*
+ * reset board, If it doesn't respond, assume
+ * that it's not there.. good for the probe
+ */
+ outb(port + EBCTRL, CDEN); /* enable full card */
+ outb(port + PORTADDR, PORTADDR_ENHANCED);
+
+ outb(port + G2CNTRL, G2CNTRL_HARD_RESET);
+ DELAY(1000);
+ outb(port + G2CNTRL, 0);
+ DELAY(10000);
+ while (--wait) {
+ if ((inb(stport) & G2STAT_BUSY) == 0)
+ break;
+ DELAY(1000);
+ } if (wait == 0) {
+#ifdef AHBDEBUG
+ if (ahb_debug & AHB_SHOWMISC)
+ printf("ahb_init: No answer from aha1742 board\n");
+#endif /*AHBDEBUG */
+ return (ENXIO);
+ }
+ i = inb(port + MBOXIN0) & 0xff;
+ if (i) {
+ printf("self test failed, val = 0x%x\n", i);
+ return (EIO);
+ }
+#endif
+ while (inb(stport) & G2STAT_INT_PEND) {
+ printf(".");
+ outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT);
+ DELAY(10000);
+ }
+ outb(port + EBCTRL, CDEN); /* enable full card */
+ outb(port + PORTADDR, PORTADDR_ENHANCED);
+ /*
+ * Assume we have a board at this stage
+ * setup dma channel from jumpers and save int
+ * level
+ */
+ printf("ahb%d: reading board settings, ", unit);
+
+ intdef = inb(port + INTDEF);
+ switch (intdef & 0x07) {
+ case INT9:
+ ahb->vect = 9;
+ break;
+ case INT10:
+ ahb->vect = 10;
+ break;
+ case INT11:
+ ahb->vect = 11;
+ break;
+ case INT12:
+ ahb->vect = 12;
+ break;
+ case INT14:
+ ahb->vect = 14;
+ break;
+ case INT15:
+ ahb->vect = 15;
+ break;
+ default:
+ printf("illegal int setting\n");
+ return (EIO);
+ }
+ printf("int=%d\n", ahb->vect);
+
+ outb(port + INTDEF, (intdef | INTEN)); /* make sure we can interrupt */
+
+ /* who are we on the scsi bus? */
+ ahb->our_id = (inb(port + SCSIDEF) & HSCSIID);
+
+ /*
+ * Note that we are going and return (to probe)
+ */
+ ahb->flags |= AHB_INIT;
+ return (0);
+}
+
+#ifndef min
+#define min(x,y) (x < y ? x : y)
+#endif /* min */
+
+void
+ahbminphys(bp)
+ struct buf *bp;
+{
+ if (bp->b_bcount > ((AHB_NSEG - 1) * PAGESIZ)) {
+ bp->b_bcount = ((AHB_NSEG - 1) * PAGESIZ);
+ }
+}
+
+/*
+ * start a scsi operation given the command and
+ * the data address. Also needs the unit, target
+ * and lu
+ */
+int32
+ahb_scsi_cmd(xs)
+ struct scsi_xfer *xs;
+{
+ struct ecb *ecb;
+ struct ahb_dma_seg *sg;
+ int seg; /* scatter gather seg being worked on */
+ int thiskv;
+ physaddr thisphys, nextphys;
+ int unit = xs->sc_link->adapter_unit;
+ int bytes_this_seg, bytes_this_page, datalen, flags;
+ struct ahb_data *ahb = ahbdata[unit];
+ int s;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB2, ("ahb_scsi_cmd\n"));
+ /*
+ * get a ecb (mbox-out) to use. If the transfer
+ * is from a buf (possibly from interrupt time)
+ * then we can't allow it to sleep
+ */
+ flags = xs->flags;
+ if (xs->bp)
+ flags |= (SCSI_NOSLEEP); /* just to be sure */
+ if (flags & ITSDONE) {
+ printf("ahb%d: Already done?", unit);
+ xs->flags &= ~ITSDONE;
+ }
+ if (!(flags & INUSE)) {
+ printf("ahb%d: Not in use?", unit);
+ xs->flags |= INUSE;
+ }
+ if (!(ecb = ahb_get_ecb(unit, flags))) {
+ xs->error = XS_DRIVER_STUFFUP;
+ return (TRY_AGAIN_LATER);
+ }
+ cheat = ecb;
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("start ecb(%x)\n", ecb));
+ ecb->xs = xs;
+ /*
+ * If it's a reset, we need to do an 'immediate'
+ * command, and store it's ecb for later
+ * if there is already an immediate waiting,
+ * then WE must wait
+ */
+ if (flags & SCSI_RESET) {
+ ecb->flags |= ECB_IMMED;
+ if (ahb->immed_ecb) {
+ return (TRY_AGAIN_LATER);
+ }
+ ahb->immed_ecb = ecb;
+ if (!(flags & SCSI_NOMASK)) {
+ s = splbio();
+ ahb_send_immed(unit, xs->sc_link->target, AHB_TARG_RESET);
+ timeout((timeout_t)ahb_timeout, (caddr_t)ecb, (xs->timeout * hz) / 1000);
+ splx(s);
+ return (SUCCESSFULLY_QUEUED);
+ } else {
+ ahb_send_immed(unit, xs->sc_link->target, AHB_TARG_RESET);
+ /*
+ * If we can't use interrupts, poll on completion
+ */
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("wait\n"));
+ if (ahb_poll(unit, xs->timeout)) {
+ ahb_free_ecb(unit, ecb, flags);
+ xs->error = XS_TIMEOUT;
+ return (HAD_ERROR);
+ }
+ return (COMPLETE);
+ }
+ }
+ /*
+ * Put all the arguments for the xfer in the ecb
+ */
+ ecb->opcode = ECB_SCSI_OP;
+ ecb->opt1 = ECB_SES | ECB_DSB | ECB_ARS;
+ if (xs->datalen) {
+ ecb->opt1 |= ECB_S_G;
+ }
+ ecb->opt2 = xs->sc_link->lun | ECB_NRB;
+ ecb->cdblen = xs->cmdlen;
+ ecb->sense = KVTOPHYS(&(ecb->ecb_sense));
+ ecb->senselen = sizeof(ecb->ecb_sense);
+ ecb->status = KVTOPHYS(&(ecb->ecb_status));
+
+ if (xs->datalen) { /* should use S/G only if not zero length */
+ ecb->data = KVTOPHYS(ecb->ahb_dma);
+ sg = ecb->ahb_dma;
+ seg = 0;
+#ifdef TFS
+ if (flags & SCSI_DATA_UIO) {
+ iovp = ((struct uio *) xs->data)->uio_iov;
+ datalen = ((struct uio *) xs->data)->uio_iovcnt;
+ xs->datalen = 0;
+ while ((datalen) && (seg < AHB_NSEG)) {
+ sg->addr = (physaddr) iovp->iov_base;
+ xs->datalen += sg->len = iovp->iov_len;
+ SC_DEBUGN(xs->sc_link, SDEV_DB4,
+ ("(0x%x@0x%x)", iovp->iov_len
+ ,iovp->iov_base));
+ sg++;
+ iovp++;
+ seg++;
+ datalen--;
+ }
+ }
+ else
+#endif /*TFS */
+ {
+ /*
+ * Set up the scatter gather block
+ */
+
+ SC_DEBUG(xs->sc_link, SDEV_DB4,
+ ("%d @0x%x:- ", xs->datalen, xs->data));
+ datalen = xs->datalen;
+ thiskv = (int) xs->data;
+ thisphys = KVTOPHYS(thiskv);
+
+ while ((datalen) && (seg < AHB_NSEG)) {
+ bytes_this_seg = 0;
+
+ /* put in the base address */
+ sg->addr = thisphys;
+
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("0x%x", thisphys));
+
+ /* do it at least once */
+ nextphys = thisphys;
+ while ((datalen) && (thisphys == nextphys)) {
+ /*
+ * This page is contiguous (physically) with
+ * the the last, just extend the length
+ */
+ /* how far to the end of the page */
+ nextphys = (thisphys & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ bytes_this_page = nextphys - thisphys;
+ /**** or the data ****/
+ bytes_this_page = min(bytes_this_page
+ ,datalen);
+ bytes_this_seg += bytes_this_page;
+ datalen -= bytes_this_page;
+
+ /* get more ready for the next page */
+ thiskv = (thiskv & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ if (datalen)
+ thisphys = KVTOPHYS(thiskv);
+ }
+ /*
+ * next page isn't contiguous, finish the seg
+ */
+ SC_DEBUGN(xs->sc_link, SDEV_DB4,
+ ("(0x%x)", bytes_this_seg));
+ sg->len = bytes_this_seg;
+ sg++;
+ seg++;
+ }
+ } /*end of iov/kv decision */
+ ecb->datalen = seg * sizeof(struct ahb_dma_seg);
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("\n"));
+ if (datalen) { /* there's still data, must have run out of segs! */
+ printf("ahb_scsi_cmd%d: more than %d DMA segs\n",
+ unit, AHB_NSEG);
+ xs->error = XS_DRIVER_STUFFUP;
+ ahb_free_ecb(unit, ecb, flags);
+ return (HAD_ERROR);
+ }
+ } else { /* No data xfer, use non S/G values */
+ ecb->data = (physaddr) 0;
+ ecb->datalen = 0;
+ } ecb->chain = (physaddr) 0;
+ /*
+ * Put the scsi command in the ecb and start it
+ */
+ bcopy(xs->cmd, ecb->cdb, xs->cmdlen);
+ /*
+ * Usually return SUCCESSFULLY QUEUED
+ */
+ if (!(flags & SCSI_NOMASK)) {
+ s = splbio();
+ ahb_send_mbox(unit, OP_START_ECB, xs->sc_link->target, ecb);
+ timeout((timeout_t)ahb_timeout, (caddr_t)ecb, (xs->timeout * hz) / 1000);
+ splx(s);
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_sent\n"));
+ return (SUCCESSFULLY_QUEUED);
+ }
+ /*
+ * If we can't use interrupts, poll on completion
+ */
+ ahb_send_mbox(unit, OP_START_ECB, xs->sc_link->target, ecb);
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_wait\n"));
+ do {
+ if (ahb_poll(unit, xs->timeout)) {
+ if (!(xs->flags & SCSI_SILENT))
+ printf("cmd fail\n");
+ ahb_send_mbox(unit, OP_ABORT_ECB, xs->sc_link->target, ecb);
+ if (ahb_poll(unit, 2000)) {
+ printf("abort failed in wait\n");
+ ahb_free_ecb(unit, ecb, flags);
+ }
+ xs->error = XS_DRIVER_STUFFUP;
+ return (HAD_ERROR);
+ }
+ } while (!(xs->flags & ITSDONE)); /* something (?) else finished */
+ if (xs->error) {
+ return (HAD_ERROR);
+ }
+ return (COMPLETE);
+}
+
+void
+ahb_timeout(caddr_t arg1)
+{
+ struct ecb * ecb = (struct ecb *)arg1;
+ int unit;
+ struct ahb_data *ahb;
+ int s = splbio();
+
+ unit = ecb->xs->sc_link->adapter_unit;
+ ahb = ahbdata[unit];
+ printf("ahb%d:%d:%d (%s%d) timed out ", unit
+ ,ecb->xs->sc_link->target
+ ,ecb->xs->sc_link->lun
+ ,ecb->xs->sc_link->device->name
+ ,ecb->xs->sc_link->dev_unit);
+
+#ifdef AHBDEBUG
+ if (ahb_debug & AHB_SHOWECBS)
+ ahb_print_active_ecb(unit);
+#endif /*AHBDEBUG */
+
+ /*
+ * If it's immediate, don't try abort it
+ */
+ if (ecb->flags & ECB_IMMED) {
+ ecb->xs->retries = 0; /* I MEAN IT ! */
+ ecb->flags |= ECB_IMMED_FAIL;
+ ahb_done(unit, ecb, FAIL);
+ splx(s);
+ return;
+ }
+ /*
+ * If it has been through before, then
+ * a previous abort has failed, don't
+ * try abort again
+ */
+ if (ecb->flags == ECB_ABORTED) {
+ /*
+ * abort timed out
+ */
+ printf("AGAIN");
+ ecb->xs->retries = 0; /* I MEAN IT ! */
+ ecb->ecb_status.ha_status = HS_CMD_ABORTED_HOST;
+ ahb_done(unit, ecb, FAIL);
+ } else { /* abort the operation that has timed out */
+ printf("\n");
+ ahb_send_mbox(unit, OP_ABORT_ECB, ecb->xs->sc_link->target, ecb);
+ /* 2 secs for the abort */
+ timeout((timeout_t)ahb_timeout, (caddr_t)ecb, 2 * hz);
+ ecb->flags = ECB_ABORTED;
+ }
+ splx(s);
+}
+
+#ifdef AHBDEBUG
+void
+ahb_print_ecb(ecb)
+ struct ecb *ecb;
+{
+ printf("ecb:%x op:%x cmdlen:%d senlen:%d\n"
+ ,ecb
+ ,ecb->opcode
+ ,ecb->cdblen
+ ,ecb->senselen);
+ printf(" datlen:%d hstat:%x tstat:%x flags:%x\n"
+ ,ecb->datalen
+ ,ecb->ecb_status.ha_status
+ ,ecb->ecb_status.targ_status
+ ,ecb->flags);
+ show_scsi_cmd(ecb->xs);
+}
+
+void
+ahb_print_active_ecb(int unit)
+{
+ struct ahb_data *ahb = ahbdata[unit];
+ struct ecb *ecb;
+ int i = 0;
+
+ while (i < ECB_HASH_SIZE) {
+ ecb = ahb->ecbhash[i];
+ while (ecb) {
+ if (ecb->flags != ECB_FREE) {
+ ahb_print_ecb(ecb);
+ }
+ ecb = ecb->nexthash;
+ } i++;
+ }
+}
+#endif /*AHBDEBUG */
+#endif /*KERNEL */
diff --git a/sys/i386/i386/autoconf.c b/sys/i386/i386/autoconf.c
new file mode 100644
index 0000000..3575d1c
--- /dev/null
+++ b/sys/i386/i386/autoconf.c
@@ -0,0 +1,209 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)autoconf.c 7.1 (Berkeley) 5/9/91
+ * $Id: autoconf.c,v 1.10 1994/03/21 14:53:08 ache Exp $
+ */
+
+/*
+ * Setup the system to run on the current machine.
+ *
+ * Configure() is called at boot time and initializes the vba
+ * device tables and the memory controller monitoring. Available
+ * devices are determined (from possibilities mentioned in ioconf.c),
+ * and the drivers are initialized.
+ */
+#include "param.h"
+#include "systm.h"
+#include "buf.h"
+#include "dkstat.h"
+#include "conf.h"
+#include "dmap.h"
+#include "reboot.h"
+#include "kernel.h"
+
+#include "machine/pte.h"
+
+static void swapconf(void);
+static void setroot(void);
+
+/*
+ * The following several variables are related to
+ * the configuration process, and are used in initializing
+ * the machine.
+ */
+int dkn; /* number of iostat dk numbers assigned so far */
+extern int cold; /* cold start flag initialized in locore.s */
+
+/*
+ * Determine i/o configuration for a machine.
+ */
+void
+configure()
+{
+
+#include "isa.h"
+#if NISA > 0
+ isa_configure();
+#endif
+
+#if GENERICxxx && !defined(DISKLESS)
+ if ((boothowto & RB_ASKNAME) == 0)
+ setroot();
+ setconf();
+#else
+#ifndef DISKLESS
+ setroot();
+#endif
+#endif
+ /*
+ * Configure swap area and related system
+ * parameter based on device(s) used.
+ */
+ swapconf();
+ cold = 0;
+}
+
+/*
+ * Configure swap space and related parameters.
+ */
+static void
+swapconf()
+{
+ register struct swdevt *swp;
+ register int nblks;
+ extern int Maxmem;
+
+ for (swp = swdevt; swp->sw_dev > 0; swp++)
+ {
+ unsigned d = major(swp->sw_dev);
+
+ if (d > nblkdev) break;
+ if (bdevsw[d].d_psize) {
+ nblks = (*bdevsw[d].d_psize)(swp->sw_dev);
+ if (nblks > 0 &&
+ (swp->sw_nblks == 0 || swp->sw_nblks > nblks))
+ swp->sw_nblks = nblks;
+ else
+ swp->sw_nblks = 0;
+ }
+ swp->sw_nblks = ctod(dtoc(swp->sw_nblks));
+ }
+ if (dumplo == 0 && bdevsw[major(dumpdev)].d_psize)
+ dumplo = (*bdevsw[major(dumpdev)].d_psize)(dumpdev) -
+ Maxmem*NBPG/512;
+ if (dumplo < 0)
+ dumplo = 0;
+}
+
+#define DOSWAP /* change swdevt and dumpdev */
+u_long bootdev = 0; /* should be dev_t, but not until 32 bits */
+
+static char devname[][2] = {
+ 'w','d', /* 0 = wd */
+ 's','w', /* 1 = sw */
+#define FDMAJOR 2
+ 'f','d', /* 2 = fd */
+ 'w','t', /* 3 = wt */
+ 's','d', /* 4 = sd -- new SCSI system */
+};
+
+#define PARTITIONMASK 0x7
+#define PARTITIONSHIFT 3
+#define FDUNITSHIFT 6
+
+/*
+ * Attempt to find the device from which we were booted.
+ * If we can do so, and not instructed not to do so,
+ * change rootdev to correspond to the load device.
+ */
+static void
+setroot()
+{
+ int majdev, mindev, unit, part, adaptor;
+ dev_t temp = 0, orootdev;
+ struct swdevt *swp;
+
+/*printf("howto %x bootdev %x ", boothowto, bootdev);*/
+ if (boothowto & RB_DFLTROOT ||
+ (bootdev & B_MAGICMASK) != (u_long)B_DEVMAGIC)
+ return;
+ majdev = (bootdev >> B_TYPESHIFT) & B_TYPEMASK;
+ if (majdev > sizeof(devname) / sizeof(devname[0]))
+ return;
+ adaptor = (bootdev >> B_ADAPTORSHIFT) & B_ADAPTORMASK;
+ unit = (bootdev >> B_UNITSHIFT) & B_UNITMASK;
+ if (majdev == FDMAJOR) {
+ part = 3; /* raw */
+ mindev = unit << FDUNITSHIFT;
+ }
+ else {
+ part = (bootdev >> B_PARTITIONSHIFT) & B_PARTITIONMASK;
+ mindev = (unit << PARTITIONSHIFT) + part;
+ }
+ orootdev = rootdev;
+ rootdev = makedev(majdev, mindev);
+ /*
+ * If the original rootdev is the same as the one
+ * just calculated, don't need to adjust the swap configuration.
+ */
+ if (rootdev == orootdev)
+ return;
+ printf("changing root device to %c%c%d%c\n",
+ devname[majdev][0], devname[majdev][1],
+ mindev >> (majdev == FDMAJOR ? FDUNITSHIFT : PARTITIONSHIFT),
+ part + 'a');
+#ifdef DOSWAP
+ mindev &= ~PARTITIONMASK;
+ for (swp = swdevt; swp->sw_dev; swp++) {
+ if (majdev == major(swp->sw_dev) &&
+ mindev == (minor(swp->sw_dev) & ~PARTITIONMASK)) {
+
+ temp = swdevt[0].sw_dev;
+ swdevt[0].sw_dev = swp->sw_dev;
+ swp->sw_dev = temp;
+ break;
+ }
+ }
+ if (swp->sw_dev == 0)
+ return;
+ /*
+ * If dumpdev was the same as the old primary swap
+ * device, move it to the new primary swap device.
+ */
+ if (temp == dumpdev)
+ dumpdev = swdevt[0].sw_dev;
+#endif
+}
diff --git a/sys/i386/i386/conf.c b/sys/i386/i386/conf.c
new file mode 100644
index 0000000..3360de6
--- /dev/null
+++ b/sys/i386/i386/conf.c
@@ -0,0 +1,756 @@
+/*
+ * Copyright (c) UNIX System Laboratories, Inc. All or some portions
+ * of this file are derived from material licensed to the
+ * University of California by American Telephone and Telegraph Co.
+ * or UNIX System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ */
+/*
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)conf.c 5.8 (Berkeley) 5/12/91
+ * $Id: conf.c,v 1.24 1994/04/21 14:10:31 sos Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/tty.h>
+#include <sys/conf.h>
+
+typedef int d_open_t __P((dev_t, int, int, struct proc *));
+typedef int d_close_t __P((dev_t, int, int, struct proc *));
+typedef int d_strategy_t __P((struct buf *));
+typedef int d_ioctl_t __P((dev_t, int, caddr_t, int, struct proc *));
+typedef int d_dump_t __P(());
+typedef int d_psize_t __P((dev_t));
+
+typedef int d_rdwr_t __P((dev_t, struct uio *, int));
+typedef int d_stop_t __P((struct tty *, int));
+typedef int d_reset_t __P((int));
+typedef int d_select_t __P((dev_t, int, struct proc *));
+typedef int d_mmap_t __P((/* XXX */));
+
+int nullop(), enxio(), enodev();
+d_rdwr_t rawread, rawwrite;
+d_strategy_t swstrategy;
+
+#include "wd.h"
+#if (NWD > 0)
+d_open_t wdopen;
+d_close_t wdclose;
+d_strategy_t wdstrategy;
+d_ioctl_t wdioctl;
+d_dump_t wddump;
+d_psize_t wdsize;
+#else
+#define wdopen (d_open_t *)enxio
+#define wdclose (d_close_t *)enxio
+#define wdstrategy (d_strategy_t *)enxio
+#define wdioctl (d_ioctl_t *)enxio
+#define wddump (d_dump_t *)enxio
+#define wdsize (d_psize_t *)0
+#endif
+
+#include "sd.h"
+#if NSD > 0
+d_open_t sdopen;
+d_close_t sdclose;
+d_strategy_t sdstrategy;
+d_ioctl_t sdioctl;
+d_dump_t sddump;
+d_psize_t sdsize;
+#else
+#define sdopen (d_open_t *)enxio
+#define sdclose (d_close_t *)enxio
+#define sdstrategy (d_strategy_t *)enxio
+#define sdioctl (d_ioctl_t *)enxio
+#define sddump (d_dump_t *)enxio
+#define sdsize (d_psize_t *)0
+#endif
+
+#include "st.h"
+#if NST > 0
+d_open_t stopen;
+d_close_t stclose;
+d_strategy_t ststrategy;
+d_ioctl_t stioctl;
+/*int stdump(),stsize();*/
+#define stdump (d_dump_t *)enxio
+#define stsize (d_psize_t *)0
+#else
+#define stopen (d_open_t *)enxio
+#define stclose (d_close_t *)enxio
+#define ststrategy (d_strategy_t *)enxio
+#define stioctl (d_ioctl_t *)enxio
+#define stdump (d_dump_t *)enxio
+#define stsize (d_psize_t *)0
+#endif
+
+#include "cd.h"
+#if NCD > 0
+d_open_t cdopen;
+d_close_t cdclose;
+d_strategy_t cdstrategy;
+d_ioctl_t cdioctl;
+d_psize_t cdsize;
+#define cddump (d_dump_t *)enxio
+#else
+#define cdopen (d_open_t *)enxio
+#define cdclose (d_close_t *)enxio
+#define cdstrategy (d_strategy_t *)enxio
+#define cdioctl (d_ioctl_t *)enxio
+#define cddump (d_dump_t *)enxio
+#define cdsize (d_psize_t *)0
+#endif
+
+#include "mcd.h"
+#if NMCD > 0
+d_open_t mcdopen;
+d_close_t mcdclose;
+d_strategy_t mcdstrategy;
+d_ioctl_t mcdioctl;
+d_psize_t mcdsize;
+#define mcddump (d_dump_t *)enxio
+#else
+#define mcdopen (d_open_t *)enxio
+#define mcdclose (d_close_t *)enxio
+#define mcdstrategy (d_strategy_t *)enxio
+#define mcdioctl (d_ioctl_t *)enxio
+#define mcddump (d_dump_t *)enxio
+#define mcdsize (d_psize_t *)0
+#endif
+
+#include "ch.h"
+#if NCH > 0
+d_open_t chopen;
+d_close_t chclose;
+d_ioctl_t chioctl;
+#else
+#define chopen (d_open_t *)enxio
+#define chclose (d_close_t *)enxio
+#define chioctl (d_ioctl_t *)enxio
+#endif
+
+#include "wt.h"
+#if NWT > 0
+d_open_t wtopen;
+d_close_t wtclose;
+d_strategy_t wtstrategy;
+d_ioctl_t wtioctl;
+d_dump_t wtdump;
+d_psize_t wtsize;
+#else
+#define wtopen (d_open_t *)enxio
+#define wtclose (d_close_t *)enxio
+#define wtstrategy (d_strategy_t *)enxio
+#define wtioctl (d_ioctl_t *)enxio
+#define wtdump (d_dump_t *)enxio
+#define wtsize (d_psize_t *)0
+#endif
+
+#include "fd.h"
+#if NFD > 0
+d_open_t Fdopen;
+d_close_t fdclose;
+d_strategy_t fdstrategy;
+d_ioctl_t fdioctl;
+#define fddump (d_dump_t *)enxio
+#define fdsize (d_psize_t *)0
+#else
+#define Fdopen (d_open_t *)enxio
+#define fdclose (d_close_t *)enxio
+#define fdstrategy (d_strategy_t *)enxio
+#define fdioctl (d_ioctl_t *)enxio
+#define fddump (d_dump_t *)enxio
+#define fdsize (d_psize_t *)0
+#endif
+
+#define swopen (d_open_t *)enodev
+#define swclose (d_close_t *)enodev
+d_strategy_t swstrategy;
+#define swioctl (d_ioctl_t *)enodev
+#define swdump (d_dump_t *)enodev
+#define swsize (d_psize_t *)enodev
+
+d_rdwr_t swread, swwrite;
+
+struct bdevsw bdevsw[] =
+{
+ { wdopen, wdclose, wdstrategy, wdioctl, /*0*/
+ wddump, wdsize, 0 },
+ { swopen, swclose, swstrategy, swioctl, /*1*/
+ swdump, swsize, 0 },
+ { Fdopen, fdclose, fdstrategy, fdioctl, /*2*/
+ fddump, fdsize, 0 },
+ { wtopen, wtclose, wtstrategy, wtioctl, /*3*/
+ wtdump, wtsize, B_TAPE },
+ { sdopen, sdclose, sdstrategy, sdioctl, /*4*/
+ sddump, sdsize, 0 },
+ { stopen, stclose, ststrategy, stioctl, /*5*/
+ stdump, stsize, 0 },
+ { cdopen, cdclose, cdstrategy, cdioctl, /*6*/
+ cddump, cdsize, 0 },
+ { mcdopen, mcdclose, mcdstrategy, mcdioctl, /*7*/
+ mcddump, mcdsize, 0 },
+ { 0, } /* block major 8 is reserved for local use */
+/*
+ * If you need a bdev major number, please contact the FreeBSD team
+ * by sending mail to "FreeBSD-hackers@freefall.cdrom.com".
+ * If you assign one yourself it may conflict with someone else.
+ */
+};
+int nblkdev = sizeof (bdevsw) / sizeof (bdevsw[0]);
+
+/* console */
+#include "machine/cons.h"
+
+d_open_t cnopen;
+d_close_t cnclose;
+d_rdwr_t cnread, cnwrite;
+d_ioctl_t cnioctl;
+d_select_t cnselect;
+
+/* more console */
+d_open_t pcopen;
+d_close_t pcclose;
+d_rdwr_t pcread, pcwrite;
+d_ioctl_t pcioctl;
+d_mmap_t pcmmap;
+extern struct tty pccons[];
+
+/* controlling TTY */
+d_open_t cttyopen;
+d_rdwr_t cttyread, cttywrite;
+d_ioctl_t cttyioctl;
+d_select_t cttyselect;
+
+/* /dev/mem */
+d_open_t mmopen;
+d_close_t mmclose;
+d_rdwr_t mmrw;
+d_mmap_t memmmap;
+#define mmselect seltrue
+
+#include "pty.h"
+#if NPTY > 0
+d_open_t ptsopen;
+d_close_t ptsclose;
+d_rdwr_t ptsread, ptswrite;
+d_stop_t ptsstop;
+d_open_t ptcopen;
+d_close_t ptcclose;
+d_rdwr_t ptcread, ptcwrite;
+d_select_t ptcselect;
+d_ioctl_t ptyioctl;
+extern struct tty pt_tty[];
+#else
+#define ptsopen (d_open_t *)enxio
+#define ptsclose (d_close_t *)enxio
+#define ptsread (d_rdwr_t *)enxio
+#define ptswrite (d_rdwr_t *)enxio
+#define ptcopen (d_open_t *)enxio
+#define ptcclose (d_close_t *)enxio
+#define ptcread (d_rdwr_t *)enxio
+#define ptcwrite (d_rdwr_t *)enxio
+#define ptyioctl (d_ioctl_t *)enxio
+#define pt_tty NULL
+#define ptcselect (d_select_t *)enxio
+#define ptsstop (d_stop_t *)nullop
+#endif
+
+#include "com.h"
+#if NCOM > 0
+d_open_t comopen;
+d_close_t comclose;
+d_rdwr_t comread;
+d_rdwr_t comwrite;
+d_ioctl_t comioctl;
+d_select_t comselect;
+#define comreset (d_reset_t *)enxio
+extern struct tty com_tty[];
+#else
+#define comopen (d_open_t *)enxio
+#define comclose (d_close_t *)enxio
+#define comread (d_rdwr_t *)enxio
+#define comwrite (d_rdwr_t *)enxio
+#define comioctl (d_ioctl_t *)enxio
+#define comreset (d_reset_t *)enxio
+#define comselect (d_select_t *)enxio
+#define com_tty NULL
+#endif
+
+/* /dev/klog */
+d_open_t logopen;
+d_close_t logclose;
+d_rdwr_t logread;
+d_ioctl_t logioctl;
+d_select_t logselect;
+
+d_select_t ttselect, seltrue;
+
+#include "lpt.h"
+#if NLPT > 0
+d_open_t lptopen;
+d_close_t lptclose;
+d_rdwr_t lptwrite;
+d_ioctl_t lptioctl;
+#else
+#define lptopen (d_open_t *)enxio
+#define lptclose (d_close_t *)enxio
+#define lptwrite (d_rdwr_t *)enxio
+#define lptioctl (d_ioctl_t *)enxio
+#endif
+
+#include "tw.h"
+#if NTW > 0
+d_open_t twopen;
+d_close_t twclose;
+d_rdwr_t twread, twwrite;
+d_select_t twselect;
+#else
+#define twopen (d_open_t *)enxio
+#define twclose (d_close_t *)enxio
+#define twread (d_rdwr_t *)enxio
+#define twwrite (d_rdwr_t *)enxio
+#define twselect (d_select_t *)enxio
+#endif
+
+#include "sb.h" /* Sound Blaster */
+#if NSB > 0
+d_open_t sbopen;
+d_close_t sbclose;
+d_ioctl_t sbioctl;
+d_rdwr_t sbread, sbwrite;
+d_select_t sbselect;
+#else
+#define sbopen (d_open_t *)enxio
+#define sbclose (d_close_t *)enxio
+#define sbioctl (d_ioctl_t *)enxio
+#define sbread (d_rdwr_t *)enxio
+#define sbwrite (d_rdwr_t *)enxio
+#define sbselect seltrue
+#endif
+
+#include "psm.h"
+#if NPSM > 0
+d_open_t psmopen;
+d_close_t psmclose;
+d_rdwr_t psmread;
+d_select_t psmselect;
+d_ioctl_t psmioctl;
+#else
+#define psmopen (d_open_t *)enxio
+#define psmclose (d_close_t *)enxio
+#define psmread (d_rdwr_t *)enxio
+#define psmselect (d_select_t *)enxio
+#define psmioctl (d_ioctl_t *)enxio
+#endif
+
+#include "snd.h" /* General Sound Driver */
+#if NSND > 0
+d_open_t sndopen;
+d_close_t sndclose;
+d_ioctl_t sndioctl;
+d_rdwr_t sndread, sndwrite;
+d_select_t sndselect;
+#else
+#define sndopen (d_open_t *)enxio
+#define sndclose (d_close_t *)enxio
+#define sndioctl (d_ioctl_t *)enxio
+#define sndread (d_rdwr_t *)enxio
+#define sndwrite (d_rdwr_t *)enxio
+#define sndselect seltrue
+#endif
+
+/* /dev/fd/NNN */
+d_open_t fdopen;
+
+#include "bpfilter.h"
+#if NBPFILTER > 0
+d_open_t bpfopen;
+d_close_t bpfclose;
+d_rdwr_t bpfread, bpfwrite;
+d_select_t bpfselect;
+d_ioctl_t bpfioctl;
+#else
+#define bpfopen (d_open_t *)enxio
+#define bpfclose (d_close_t *)enxio
+#define bpfread (d_rdwr_t *)enxio
+#define bpfwrite (d_rdwr_t *)enxio
+#define bpfselect (d_select_t *)enxio
+#define bpfioctl (d_ioctl_t *)enxio
+#endif
+
+#include "lpa.h"
+#if NLPA > 0
+d_open_t lpaopen;
+d_close_t lpaclose;
+d_rdwr_t lpawrite;
+d_ioctl_t lpaioctl;
+#else
+#define lpaopen (d_open_t *)enxio
+#define lpaclose (d_close_t *)enxio
+#define lpawrite (d_rdwr_t *)enxio
+#define lpaioctl (d_ioctl_t *)enxio
+#endif
+
+#include "speaker.h"
+#if NSPEAKER > 0
+d_open_t spkropen;
+d_close_t spkrclose;
+d_rdwr_t spkrwrite;
+d_ioctl_t spkrioctl;
+#else
+#define spkropen (d_open_t *)enxio
+#define spkrclose (d_close_t *)enxio
+#define spkrwrite (d_rdwr_t *)enxio
+#define spkrioctl (d_ioctl_t *)enxio
+#endif
+
+#include "pca.h"
+#if NPCA > 0
+d_open_t pcaopen;
+d_close_t pcaclose;
+d_rdwr_t pcawrite;
+d_ioctl_t pcaioctl;
+#else
+#define pcaopen (d_open_t *)enxio
+#define pcaclose (d_close_t *)enxio
+#define pcawrite (d_rdwr_t *)enxio
+#define pcaioctl (d_ioctl_t *)enxio
+#endif
+
+#include "mse.h"
+#if NMSE > 0
+d_open_t mseopen;
+d_close_t mseclose;
+d_rdwr_t mseread;
+d_select_t mseselect;
+#else
+#define mseopen (d_open_t *)enxio
+#define mseclose (d_close_t *)enxio
+#define mseread (d_rdwr_t *)enxio
+#define mseselect (d_select_t *)enxio
+#endif
+
+#include "sio.h"
+#if NSIO > 0
+d_open_t sioopen;
+d_close_t sioclose;
+d_rdwr_t sioread, siowrite;
+d_ioctl_t sioioctl;
+d_select_t sioselect;
+d_stop_t siostop;
+#define sioreset (d_reset_t *)enxio
+extern struct tty sio_tty[];
+#else
+#define sioopen (d_open_t *)enxio
+#define sioclose (d_close_t *)enxio
+#define sioread (d_rdwr_t *)enxio
+#define siowrite (d_rdwr_t *)enxio
+#define sioioctl (d_ioctl_t *)enxio
+#define siostop (d_stop_t *)enxio
+#define sioreset (d_reset_t *)enxio
+#define sioselect (d_select_t *)enxio
+#define sio_tty NULL
+#endif
+
+#include "su.h"
+#if NSU > 0
+d_open_t suopen;
+d_close_t suclose;
+d_ioctl_t suioctl;
+#else
+#define suopen (d_open_t *)enxio
+#define suclose (d_close_t *)enxio
+#define suioctl (d_ioctl_t *)enxio
+#endif
+
+#include "uk.h"
+#if NUK > 0
+d_open_t ukopen;
+d_close_t ukclose;
+d_ioctl_t ukioctl;
+#else
+#define ukopen (d_open_t *)enxio
+#define ukclose (d_close_t *)enxio
+#define ukioctl (d_ioctl_t *)enxio
+#endif
+
+#define noopen (d_open_t *)enodev
+#define noclose (d_close_t *)enodev
+#define noread (d_rdwr_t *)enodev
+#define nowrite noread
+#define noioc (d_ioctl_t *)enodev
+#define nostop (d_stop_t *)enodev
+#define noreset (d_reset_t *)enodev
+#define noselect (d_select_t *)enodev
+#define nommap (d_mmap_t *)enodev
+#define nostrat (d_strategy_t *)enodev
+
+#define nullopen (d_open_t *)nullop
+#define nullclose (d_close_t *)nullop
+#define nullstop (d_stop_t *)nullop
+#define nullreset (d_reset_t *)nullop
+
+/* open, close, read, write, ioctl, stop, reset, ttys, select, mmap, strat */
+struct cdevsw cdevsw[] =
+{
+ { cnopen, cnclose, cnread, cnwrite, /*0*/
+ cnioctl, nullstop, nullreset, NULL, /* console */
+ cnselect, nommap, NULL },
+ { cttyopen, nullclose, cttyread, cttywrite, /*1*/
+ cttyioctl, nullstop, nullreset, NULL, /* tty */
+ cttyselect, nommap, NULL },
+ { mmopen, mmclose, mmrw, mmrw, /*2*/
+ noioc, nullstop, nullreset, NULL, /* memory */
+ mmselect, memmmap, NULL },
+ { wdopen, wdclose, rawread, rawwrite, /*3*/
+ wdioctl, nostop, nullreset, NULL, /* wd */
+ seltrue, nommap, wdstrategy },
+ { nullopen, nullclose, rawread, rawwrite, /*4*/
+ noioc, nostop, noreset, NULL, /* swap */
+ noselect, nommap, swstrategy },
+ { ptsopen, ptsclose, ptsread, ptswrite, /*5*/
+ ptyioctl, ptsstop, nullreset, pt_tty, /* ttyp */
+ ttselect, nommap, NULL },
+ { ptcopen, ptcclose, ptcread, ptcwrite, /*6*/
+ ptyioctl, nullstop, nullreset, pt_tty, /* ptyp */
+ ptcselect, nommap, NULL },
+ { logopen, logclose, logread, nowrite, /*7*/
+ logioctl, nostop, nullreset, NULL, /* klog */
+ logselect, nommap, NULL },
+ { comopen, comclose, comread, comwrite, /*8*/
+ comioctl, nostop, comreset, com_tty, /* com */
+ comselect, nommap, NULL },
+ { Fdopen, fdclose, rawread, rawwrite, /*9*/
+ fdioctl, nostop, nullreset, NULL, /* Fd (!=fd) */
+ seltrue, nommap, fdstrategy },
+ { wtopen, wtclose, rawread, rawwrite, /*10*/
+ wtioctl, nostop, nullreset, NULL, /* wt */
+ seltrue, nommap, wtstrategy },
+ { noopen, noclose, noread, nowrite, /*11*/
+ noioc, nostop, nullreset, NULL,
+ seltrue, nommap, nostrat },
+ { pcopen, pcclose, pcread, pcwrite, /*12*/
+ pcioctl, nullstop, nullreset, pccons, /* pc */
+ ttselect, pcmmap, NULL },
+ { sdopen, sdclose, rawread, rawwrite, /*13*/
+ sdioctl, nostop, nullreset, NULL, /* sd */
+ seltrue, nommap, sdstrategy },
+ { stopen, stclose, rawread, rawwrite, /*14*/
+ stioctl, nostop, nullreset, NULL, /* st */
+ seltrue, nommap, ststrategy },
+ { cdopen, cdclose, rawread, nowrite, /*15*/
+ cdioctl, nostop, nullreset, NULL, /* cd */
+ seltrue, nommap, cdstrategy },
+ { lptopen, lptclose, noread, lptwrite, /*16*/
+ lptioctl, nullstop, nullreset, NULL, /* lpt */
+ seltrue, nommap, nostrat},
+ { chopen, chclose, noread, nowrite, /*17*/
+ chioctl, nostop, nullreset, NULL, /* ch */
+ noselect, nommap, nostrat },
+ { suopen, suclose, noread, nowrite, /*18*/
+ suioctl, nostop, nullreset, NULL, /* scsi 'generic' */
+ seltrue, nommap, nostrat },
+ { twopen, twclose, twread, twwrite, /*19*/
+ noioc, nullstop, nullreset, NULL, /* tw */
+ twselect, nommap, nostrat },
+ { sbopen, sbclose, sbread, sbwrite, /*20*/
+ sbioctl, nostop, nullreset, NULL, /* soundblaster*/
+ sbselect, nommap, NULL },
+ { psmopen, psmclose, psmread, nowrite, /*21*/
+ psmioctl, nostop, nullreset, NULL, /* psm mice */
+ psmselect, nommap, NULL },
+ { fdopen, noclose, noread, nowrite, /*22*/
+ noioc, nostop, nullreset, NULL, /* fd (!=Fd) */
+ noselect, nommap, nostrat },
+ { bpfopen, bpfclose, bpfread, bpfwrite, /*23*/
+ bpfioctl, nostop, nullreset, NULL, /* bpf */
+ bpfselect, nommap, NULL },
+ { pcaopen, pcaclose, noread, pcawrite, /*24*/
+ pcaioctl, nostop, nullreset, NULL, /* pcaudio */
+ seltrue, nommap, NULL },
+ { lpaopen, lpaclose, noread, lpawrite, /*25*/
+ lpaioctl, nullstop, nullreset, NULL, /* lpa */
+ seltrue, nommap, NULL },
+ { spkropen, spkrclose, noread, spkrwrite, /*26*/
+ spkrioctl, nostop, nullreset, NULL, /* spkr */
+ seltrue, nommap, NULL },
+ { mseopen, mseclose, mseread, nowrite, /*27*/
+ noioc, nostop, nullreset, NULL, /* mse */
+ mseselect, nommap, NULL },
+ { sioopen, sioclose, sioread, siowrite, /*28*/
+ sioioctl, siostop, sioreset, sio_tty, /* sio */
+ sioselect, nommap, NULL },
+ { mcdopen, mcdclose, rawread, nowrite, /*29*/
+ mcdioctl, nostop, nullreset, NULL, /* mitsumi cd */
+ seltrue, nommap, mcdstrategy },
+ { sndopen, sndclose, sndread, sndwrite, /*30*/
+ sndioctl, nostop, nullreset, NULL, /* sound driver */
+ sndselect, nommap, NULL },
+ { ukopen, ukclose, noread, nowrite, /*31*/
+ ukioctl, nostop, nullreset, NULL, /* unknown */
+ seltrue, nommap, NULL }, /* scsi */
+ { 0, } /* character device 32 is reserved for local use */
+/*
+ * If you need a cdev major number, please contact the FreeBSD team
+ * by sending mail to `freebsd-hackers@freefall.cdrom.com'.
+ * If you assign one yourself it may then conflict with someone else.
+ */
+};
+int nchrdev = sizeof (cdevsw) / sizeof (cdevsw[0]);
+
+int mem_no = 2; /* major device number of memory special file */
+
+/*
+ * Swapdev is a fake device implemented
+ * in sw.c used only internally to get to swstrategy.
+ * It cannot be provided to the users, because the
+ * swstrategy routine munches the b_dev and b_blkno entries
+ * before calling the appropriate driver. This would horribly
+ * confuse, e.g. the hashing routines. Instead, /dev/drum is
+ * provided as a character (raw) device.
+ */
+dev_t swapdev = makedev(1, 0);
+
+/*
+ * Routine that identifies /dev/mem and /dev/kmem.
+ *
+ * A minimal stub routine can always return 0.
+ */
+int
+iskmemdev(dev)
+ dev_t dev;
+{
+
+ return (major(dev) == 2 && (minor(dev) == 0 || minor(dev) == 1));
+}
+
+int
+iszerodev(dev)
+ dev_t dev;
+{
+ return (major(dev) == 2 && minor(dev) == 12);
+}
+
+/*
+ * Routine to determine if a device is a disk.
+ *
+ * A minimal stub routine can always return 0.
+ */
+int
+isdisk(dev, type)
+ dev_t dev;
+ int type;
+{
+
+ switch (major(dev)) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ case 7:
+ if (type == VBLK)
+ return (1);
+ return (0);
+ case 3:
+ case 9:
+ case 13:
+ case 15:
+ case 29:
+ if (type == VCHR)
+ return (1);
+ /* fall through */
+ default:
+ return (0);
+ }
+ /* NOTREACHED */
+}
+
+#define MAXDEV 32
+static int chrtoblktbl[MAXDEV] = {
+ /* VCHR */ /* VBLK */
+ /* 0 */ NODEV,
+ /* 1 */ NODEV,
+ /* 2 */ NODEV,
+ /* 3 */ 0,
+ /* 4 */ NODEV,
+ /* 5 */ NODEV,
+ /* 6 */ NODEV,
+ /* 7 */ NODEV,
+ /* 8 */ NODEV,
+ /* 9 */ 2,
+ /* 10 */ 3,
+ /* 11 */ NODEV,
+ /* 12 */ NODEV,
+ /* 13 */ 4,
+ /* 14 */ 5,
+ /* 15 */ 6,
+ /* 16 */ NODEV,
+ /* 17 */ NODEV,
+ /* 18 */ NODEV,
+ /* 19 */ NODEV,
+ /* 20 */ NODEV,
+ /* 21 */ NODEV,
+ /* 22 */ NODEV,
+ /* 23 */ NODEV,
+ /* 25 */ NODEV,
+ /* 26 */ NODEV,
+ /* 27 */ NODEV,
+ /* 28 */ NODEV,
+ /* 29 */ 7,
+ /* 30 */ NODEV,
+ /* 31 */ NODEV,
+};
+/*
+ * Routine to convert from character to block device number.
+ *
+ * A minimal stub routine can always return NODEV.
+ */
+int
+chrtoblk(dev)
+ dev_t dev;
+{
+ int blkmaj;
+
+ if (major(dev) >= MAXDEV || (blkmaj = chrtoblktbl[major(dev)]) == NODEV)
+ return (NODEV);
+ return (makedev(blkmaj, minor(dev)));
+}
diff --git a/sys/i386/i386/cons.c b/sys/i386/i386/cons.c
new file mode 100644
index 0000000..ceb4b39
--- /dev/null
+++ b/sys/i386/i386/cons.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 1988 University of Utah.
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)cons.c 7.2 (Berkeley) 5/9/91
+ * $Id: cons.c,v 1.10 1994/01/23 19:17:17 davidg Exp $
+ */
+
+
+#include "sys/param.h"
+#include <sys/systm.h>
+#include "sys/proc.h"
+#include "sys/user.h"
+#include "sys/buf.h"
+#include "sys/ioctl.h"
+#include "sys/tty.h"
+#include "sys/file.h"
+#include "sys/conf.h"
+#include "sys/vnode.h"
+#include "machine/stdarg.h"
+
+#include "machine/cons.h"
+
+/* XXX - all this could be autoconfig()ed */
+int pccnprobe(), pccninit(), pccngetc(), pccnputc();
+
+#include "sio.h"
+#if NSIO > 0
+int siocnprobe(), siocninit(), siocngetc(), siocnputc();
+#endif
+
+#include "com.h"
+#if NCOM > 0
+int comcnprobe(), comcninit(), comcngetc(), comcnputc();
+#endif
+
+struct consdev constab[] = {
+ { pccnprobe, pccninit, pccngetc, pccnputc },
+#if NSIO > 0
+ { siocnprobe, siocninit, siocngetc, siocnputc },
+#endif
+#if NCOM > 0
+ { comcnprobe, comcninit, comcngetc, comcnputc },
+#endif
+ { 0 },
+};
+/* end XXX */
+
+struct tty *constty = 0; /* virtual console output device */
+struct consdev *cn_tab; /* physical console device info */
+struct tty *cn_tty; /* XXX: console tty struct for tprintf */
+
+void
+cninit()
+{
+ register struct consdev *cp;
+
+ /*
+ * Collect information about all possible consoles
+ * and find the one with highest priority
+ */
+ for (cp = constab; cp->cn_probe; cp++) {
+ (*cp->cn_probe)(cp);
+ if (cp->cn_pri > CN_DEAD &&
+ (cn_tab == NULL || cp->cn_pri > cn_tab->cn_pri))
+ cn_tab = cp;
+ }
+ /*
+ * No console, we can handle it
+ */
+ if ((cp = cn_tab) == NULL)
+ return;
+ /*
+ * Turn on console
+ */
+ cn_tty = cp->cn_tp;
+ (*cp->cn_init)(cp);
+}
+
+int
+cnopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+ struct vnode *vp = 0;
+
+ if (cn_tab == NULL)
+ return (0);
+
+ dev = cn_tab->cn_dev;
+ if (vfinddev(dev, VCHR, &vp) && vcount(vp))
+ return (0);
+
+ return ((*cdevsw[major(dev)].d_open)(dev, flag, mode, p));
+}
+
+int
+cnclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+ struct vnode *vp = 0;
+
+ if (cn_tab == NULL)
+ return (0);
+
+ dev = cn_tab->cn_dev;
+ if (vfinddev(dev, VCHR, &vp) && vcount(vp))
+ return (0);
+
+ return ((*cdevsw[major(dev)].d_close)(dev, flag, mode, p));
+}
+
+int
+cnread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ if (cn_tab == NULL)
+ return (0);
+ dev = cn_tab->cn_dev;
+ return ((*cdevsw[major(dev)].d_read)(dev, uio, flag));
+}
+
+int
+cnwrite(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ if (cn_tab == NULL)
+ return (0);
+ if (constty)
+ dev = constty->t_dev;
+ else
+ dev = cn_tab->cn_dev;
+ return ((*cdevsw[major(dev)].d_write)(dev, uio, flag));
+}
+
+int
+cnioctl(dev, cmd, data, flag, p)
+ dev_t dev;
+ int cmd;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+{
+ int error;
+
+ if (cn_tab == NULL)
+ return (0);
+ /*
+ * Superuser can always use this to wrest control of console
+ * output from the "virtual" console.
+ */
+ if (cmd == TIOCCONS && constty) {
+ error = suser(p->p_ucred, (u_short *) NULL);
+ if (error)
+ return (error);
+ constty = NULL;
+ return (0);
+ }
+ dev = cn_tab->cn_dev;
+ return ((*cdevsw[major(dev)].d_ioctl)(dev, cmd, data, flag, p));
+}
+
+/*ARGSUSED*/
+int
+cnselect(dev, rw, p)
+ dev_t dev;
+ int rw;
+ struct proc *p;
+{
+ if (cn_tab == NULL)
+ return (1);
+ return (ttselect(cn_tab->cn_dev, rw, p));
+}
+
+int
+cngetc()
+{
+ if (cn_tab == NULL)
+ return (0);
+ return ((*cn_tab->cn_getc)(cn_tab->cn_dev));
+}
+
+void
+cnputc(c)
+ register int c;
+{
+ if (cn_tab == NULL)
+ return;
+ if (c) {
+ (*cn_tab->cn_putc)(cn_tab->cn_dev, c);
+ if (c == '\n')
+ (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r');
+ }
+}
+
+int
+pg(const char *p, ...) {
+ va_list args;
+ va_start(args, p);
+ printf("%r\n>", p, args);
+ return(cngetc());
+}
+
+
diff --git a/sys/i386/i386/cons.h b/sys/i386/i386/cons.h
new file mode 100644
index 0000000..2766193
--- /dev/null
+++ b/sys/i386/i386/cons.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1988 University of Utah.
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)cons.h 7.2 (Berkeley) 5/9/91
+ * $Id: cons.h,v 1.3 1993/11/07 17:41:33 wollman Exp $
+ */
+
+#ifndef _MACHINE_CONS_H_
+#define _MACHINE_CONS_H_ 1
+
+struct consdev {
+ int (*cn_probe)(); /* probe hardware and fill in consdev info */
+ int (*cn_init)(); /* turn on as console */
+ int (*cn_getc)(); /* kernel getchar interface */
+ int (*cn_putc)(); /* kernel putchar interface */
+ struct tty *cn_tp; /* tty structure for console device */
+ dev_t cn_dev; /* major/minor of device */
+ short cn_pri; /* pecking order; the higher the better */
+};
+
+/* values for cn_pri - reflect our policy for console selection */
+#define CN_DEAD 0 /* device doesn't exist */
+#define CN_NORMAL 1 /* device exists but is nothing special */
+#define CN_INTERNAL 2 /* "internal" bit-mapped display */
+#define CN_REMOTE 3 /* serial interface with remote bit set */
+
+/* XXX */
+#define CONSMAJOR 0
+
+#ifdef KERNEL
+extern struct consdev constab[];
+extern struct consdev *cn_tab;
+extern struct tty *cn_tty;
+
+struct proc; struct uio;
+
+/* cdevsw[] entries */
+extern int cnopen(dev_t, int, int, struct proc *);
+extern int cnclose(dev_t, int, int, struct proc *);
+extern int cnread(dev_t, struct uio *, int);
+extern int cnwrite(dev_t, struct uio *, int);
+extern int cnioctl(dev_t, int, caddr_t, int, struct proc *);
+extern int cnselect(dev_t, int, struct proc *);
+
+/* other kernel entry points */
+extern void cninit(void);
+extern int cngetc(void);
+extern void cnputc(int /*char*/);
+extern int pg(const char *, ...);
+
+#endif /* KERNEL */
+#endif /* _MACHINE_CONS_H_ */
diff --git a/sys/i386/i386/db_disasm.c b/sys/i386/i386/db_disasm.c
new file mode 100644
index 0000000..98e251b
--- /dev/null
+++ b/sys/i386/i386/db_disasm.c
@@ -0,0 +1,1375 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_disasm.c,v 1.4 1993/11/25 01:30:51 wollman Exp $
+ */
+
+/*
+ * Instruction disassembler.
+ */
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+
+#include <ddb/db_access.h>
+#include <ddb/db_sym.h>
+
+/*
+ * Size attributes
+ */
+#define BYTE 0
+#define WORD 1
+#define LONG 2
+#define QUAD 3
+#define SNGL 4
+#define DBLR 5
+#define EXTR 6
+#define SDEP 7
+#define NONE 8
+
+/*
+ * Addressing modes
+ */
+#define E 1 /* general effective address */
+#define Eind 2 /* indirect address (jump, call) */
+#define Ew 3 /* address, word size */
+#define Eb 4 /* address, byte size */
+#define R 5 /* register, in 'reg' field */
+#define Rw 6 /* word register, in 'reg' field */
+#define Ri 7 /* register in instruction */
+#define S 8 /* segment reg, in 'reg' field */
+#define Si 9 /* segment reg, in instruction */
+#define A 10 /* accumulator */
+#define BX 11 /* (bx) */
+#define CL 12 /* cl, for shifts */
+#define DX 13 /* dx, for IO */
+#define SI 14 /* si */
+#define DI 15 /* di */
+#define CR 16 /* control register */
+#define DR 17 /* debug register */
+#define TR 18 /* test register */
+#define I 19 /* immediate, unsigned */
+#define Is 20 /* immediate, signed */
+#define Ib 21 /* byte immediate, unsigned */
+#define Ibs 22 /* byte immediate, signed */
+#define Iw 23 /* word immediate, unsigned */
+#define Il 24 /* long immediate */
+#define O 25 /* direct address */
+#define Db 26 /* byte displacement from EIP */
+#define Dl 27 /* long displacement from EIP */
+#define o1 28 /* constant 1 */
+#define o3 29 /* constant 3 */
+#define OS 30 /* immediate offset/segment */
+#define ST 31 /* FP stack top */
+#define STI 32 /* FP stack */
+#define X 33 /* extended FP op */
+#define XA 34 /* for 'fstcw %ax' */
+
+struct inst {
+ char * i_name; /* name */
+ short i_has_modrm; /* has regmodrm byte */
+ short i_size; /* operand size */
+ int i_mode; /* addressing modes */
+ char * i_extra; /* pointer to extra opcode table */
+};
+
+#define op1(x) (x)
+#define op2(x,y) ((x)|((y)<<8))
+#define op3(x,y,z) ((x)|((y)<<8)|((z)<<16))
+
+struct finst {
+ char * f_name; /* name for memory instruction */
+ int f_size; /* size for memory instruction */
+ int f_rrmode; /* mode for rr instruction */
+ char * f_rrname; /* name for rr instruction
+ (or pointer to table) */
+};
+
+char * db_Grp6[] = {
+ "sldt",
+ "str",
+ "lldt",
+ "ltr",
+ "verr",
+ "verw",
+ "",
+ ""
+};
+
+char * db_Grp7[] = {
+ "sgdt",
+ "sidt",
+ "lgdt",
+ "lidt",
+ "smsw",
+ "",
+ "lmsw",
+ "invlpg"
+};
+
+char * db_Grp8[] = {
+ "",
+ "",
+ "",
+ "",
+ "bt",
+ "bts",
+ "btr",
+ "btc"
+};
+
+struct inst db_inst_0f0x[] = {
+/*00*/ { "", TRUE, NONE, op1(Ew), (char *)db_Grp6 },
+/*01*/ { "", TRUE, NONE, op1(Ew), (char *)db_Grp7 },
+/*02*/ { "lar", TRUE, LONG, op2(E,R), 0 },
+/*03*/ { "lsl", TRUE, LONG, op2(E,R), 0 },
+/*04*/ { "", FALSE, NONE, 0, 0 },
+/*05*/ { "", FALSE, NONE, 0, 0 },
+/*06*/ { "clts", FALSE, NONE, 0, 0 },
+/*07*/ { "", FALSE, NONE, 0, 0 },
+
+/*08*/ { "invd", FALSE, NONE, 0, 0 },
+/*09*/ { "wbinvd",FALSE, NONE, 0, 0 },
+/*0a*/ { "", FALSE, NONE, 0, 0 },
+/*0b*/ { "", FALSE, NONE, 0, 0 },
+/*0c*/ { "", FALSE, NONE, 0, 0 },
+/*0d*/ { "", FALSE, NONE, 0, 0 },
+/*0e*/ { "", FALSE, NONE, 0, 0 },
+/*0f*/ { "", FALSE, NONE, 0, 0 },
+};
+
+struct inst db_inst_0f2x[] = {
+/*20*/ { "mov", TRUE, LONG, op2(CR,E), 0 }, /* use E for reg */
+/*21*/ { "mov", TRUE, LONG, op2(DR,E), 0 }, /* since mod == 11 */
+/*22*/ { "mov", TRUE, LONG, op2(E,CR), 0 },
+/*23*/ { "mov", TRUE, LONG, op2(E,DR), 0 },
+/*24*/ { "mov", TRUE, LONG, op2(TR,E), 0 },
+/*25*/ { "", FALSE, NONE, 0, 0 },
+/*26*/ { "mov", TRUE, LONG, op2(E,TR), 0 },
+/*27*/ { "", FALSE, NONE, 0, 0 },
+
+/*28*/ { "", FALSE, NONE, 0, 0 },
+/*29*/ { "", FALSE, NONE, 0, 0 },
+/*2a*/ { "", FALSE, NONE, 0, 0 },
+/*2b*/ { "", FALSE, NONE, 0, 0 },
+/*2c*/ { "", FALSE, NONE, 0, 0 },
+/*2d*/ { "", FALSE, NONE, 0, 0 },
+/*2e*/ { "", FALSE, NONE, 0, 0 },
+/*2f*/ { "", FALSE, NONE, 0, 0 },
+};
+
+struct inst db_inst_0f8x[] = {
+/*80*/ { "jo", FALSE, NONE, op1(Dl), 0 },
+/*81*/ { "jno", FALSE, NONE, op1(Dl), 0 },
+/*82*/ { "jb", FALSE, NONE, op1(Dl), 0 },
+/*83*/ { "jnb", FALSE, NONE, op1(Dl), 0 },
+/*84*/ { "jz", FALSE, NONE, op1(Dl), 0 },
+/*85*/ { "jnz", FALSE, NONE, op1(Dl), 0 },
+/*86*/ { "jbe", FALSE, NONE, op1(Dl), 0 },
+/*87*/ { "jnbe", FALSE, NONE, op1(Dl), 0 },
+
+/*88*/ { "js", FALSE, NONE, op1(Dl), 0 },
+/*89*/ { "jns", FALSE, NONE, op1(Dl), 0 },
+/*8a*/ { "jp", FALSE, NONE, op1(Dl), 0 },
+/*8b*/ { "jnp", FALSE, NONE, op1(Dl), 0 },
+/*8c*/ { "jl", FALSE, NONE, op1(Dl), 0 },
+/*8d*/ { "jnl", FALSE, NONE, op1(Dl), 0 },
+/*8e*/ { "jle", FALSE, NONE, op1(Dl), 0 },
+/*8f*/ { "jnle", FALSE, NONE, op1(Dl), 0 },
+};
+
+struct inst db_inst_0f9x[] = {
+/*90*/ { "seto", TRUE, NONE, op1(Eb), 0 },
+/*91*/ { "setno", TRUE, NONE, op1(Eb), 0 },
+/*92*/ { "setb", TRUE, NONE, op1(Eb), 0 },
+/*93*/ { "setnb", TRUE, NONE, op1(Eb), 0 },
+/*94*/ { "setz", TRUE, NONE, op1(Eb), 0 },
+/*95*/ { "setnz", TRUE, NONE, op1(Eb), 0 },
+/*96*/ { "setbe", TRUE, NONE, op1(Eb), 0 },
+/*97*/ { "setnbe",TRUE, NONE, op1(Eb), 0 },
+
+/*98*/ { "sets", TRUE, NONE, op1(Eb), 0 },
+/*99*/ { "setns", TRUE, NONE, op1(Eb), 0 },
+/*9a*/ { "setp", TRUE, NONE, op1(Eb), 0 },
+/*9b*/ { "setnp", TRUE, NONE, op1(Eb), 0 },
+/*9c*/ { "setl", TRUE, NONE, op1(Eb), 0 },
+/*9d*/ { "setnl", TRUE, NONE, op1(Eb), 0 },
+/*9e*/ { "setle", TRUE, NONE, op1(Eb), 0 },
+/*9f*/ { "setnle",TRUE, NONE, op1(Eb), 0 },
+};
+
+struct inst db_inst_0fax[] = {
+/*a0*/ { "push", FALSE, NONE, op1(Si), 0 },
+/*a1*/ { "pop", FALSE, NONE, op1(Si), 0 },
+/*a2*/ { "", FALSE, NONE, 0, 0 },
+/*a3*/ { "bt", TRUE, LONG, op2(E,R), 0 },
+/*a4*/ { "shld", TRUE, LONG, op3(Ib,E,R), 0 },
+/*a5*/ { "shld", TRUE, LONG, op3(CL,E,R), 0 },
+/*a6*/ { "", FALSE, NONE, 0, 0 },
+/*a7*/ { "", FALSE, NONE, 0, 0 },
+
+/*a8*/ { "push", FALSE, NONE, op1(Si), 0 },
+/*a9*/ { "pop", FALSE, NONE, op1(Si), 0 },
+/*aa*/ { "", FALSE, NONE, 0, 0 },
+/*ab*/ { "bts", TRUE, LONG, op2(E,R), 0 },
+/*ac*/ { "shrd", TRUE, LONG, op3(Ib,E,R), 0 },
+/*ad*/ { "shrd", TRUE, LONG, op3(CL,E,R), 0 },
+/*a6*/ { "", FALSE, NONE, 0, 0 },
+/*a7*/ { "imul", TRUE, LONG, op2(E,R), 0 },
+};
+
+struct inst db_inst_0fbx[] = {
+/*b0*/ { "", FALSE, NONE, 0, 0 },
+/*b1*/ { "", FALSE, NONE, 0, 0 },
+/*b2*/ { "lss", TRUE, LONG, op2(E, R), 0 },
+/*b3*/ { "bts", TRUE, LONG, op2(R, E), 0 },
+/*b4*/ { "lfs", TRUE, LONG, op2(E, R), 0 },
+/*b5*/ { "lgs", TRUE, LONG, op2(E, R), 0 },
+/*b6*/ { "movzb", TRUE, LONG, op2(E, R), 0 },
+/*b7*/ { "movzw", TRUE, LONG, op2(E, R), 0 },
+
+/*b8*/ { "", FALSE, NONE, 0, 0 },
+/*b9*/ { "", FALSE, NONE, 0, 0 },
+/*ba*/ { "", TRUE, LONG, op2(Is, E), (char *)db_Grp8 },
+/*bb*/ { "btc", TRUE, LONG, op2(R, E), 0 },
+/*bc*/ { "bsf", TRUE, LONG, op2(E, R), 0 },
+/*bd*/ { "bsr", TRUE, LONG, op2(E, R), 0 },
+/*be*/ { "movsb", TRUE, LONG, op2(E, R), 0 },
+/*bf*/ { "movsw", TRUE, LONG, op2(E, R), 0 },
+};
+
+struct inst db_inst_0fcx[] = {
+/*c0*/ { "xadd", TRUE, BYTE, op2(R, E), 0 },
+/*c1*/ { "xadd", TRUE, LONG, op2(R, E), 0 },
+/*c2*/ { "", FALSE, NONE, 0, 0 },
+/*c3*/ { "", FALSE, NONE, 0, 0 },
+/*c4*/ { "", FALSE, NONE, 0, 0 },
+/*c5*/ { "", FALSE, NONE, 0, 0 },
+/*c6*/ { "", FALSE, NONE, 0, 0 },
+/*c7*/ { "", FALSE, NONE, 0, 0 },
+/*c8*/ { "bswap", FALSE, LONG, op1(Ri), 0 },
+/*c9*/ { "bswap", FALSE, LONG, op1(Ri), 0 },
+/*ca*/ { "bswap", FALSE, LONG, op1(Ri), 0 },
+/*cb*/ { "bswap", FALSE, LONG, op1(Ri), 0 },
+/*cc*/ { "bswap", FALSE, LONG, op1(Ri), 0 },
+/*cd*/ { "bswap", FALSE, LONG, op1(Ri), 0 },
+/*ce*/ { "bswap", FALSE, LONG, op1(Ri), 0 },
+/*cf*/ { "bswap", FALSE, LONG, op1(Ri), 0 },
+};
+
+struct inst db_inst_0fdx[] = {
+/*c0*/ { "cmpxchg",TRUE, BYTE, op2(R, E), 0 },
+/*c1*/ { "cmpxchg",TRUE, LONG, op2(R, E), 0 },
+/*c2*/ { "", FALSE, NONE, 0, 0 },
+/*c3*/ { "", FALSE, NONE, 0, 0 },
+/*c4*/ { "", FALSE, NONE, 0, 0 },
+/*c5*/ { "", FALSE, NONE, 0, 0 },
+/*c6*/ { "", FALSE, NONE, 0, 0 },
+/*c7*/ { "", FALSE, NONE, 0, 0 },
+/*c8*/ { "", FALSE, NONE, 0, 0 },
+/*c9*/ { "", FALSE, NONE, 0, 0 },
+/*ca*/ { "", FALSE, NONE, 0, 0 },
+/*cb*/ { "", FALSE, NONE, 0, 0 },
+/*cc*/ { "", FALSE, NONE, 0, 0 },
+/*cd*/ { "", FALSE, NONE, 0, 0 },
+/*ce*/ { "", FALSE, NONE, 0, 0 },
+/*cf*/ { "", FALSE, NONE, 0, 0 },
+};
+
+struct inst *db_inst_0f[] = {
+ db_inst_0f0x,
+ 0,
+ db_inst_0f2x,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ db_inst_0f8x,
+ db_inst_0f9x,
+ db_inst_0fax,
+ db_inst_0fbx,
+ db_inst_0fcx,
+ db_inst_0fdx,
+ 0,
+ 0
+};
+
+char * db_Esc92[] = {
+ "fnop", "", "", "", "", "", "", ""
+};
+char * db_Esc93[] = {
+ "", "", "", "", "", "", "", ""
+};
+char * db_Esc94[] = {
+ "fchs", "fabs", "", "", "ftst", "fxam", "", ""
+};
+char * db_Esc95[] = {
+ "fld1", "fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz",""
+};
+char * db_Esc96[] = {
+ "f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp",
+ "fincstp"
+};
+char * db_Esc97[] = {
+ "fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"
+};
+
+char * db_Esca4[] = {
+ "", "fucompp","", "", "", "", "", ""
+};
+
+char * db_Escb4[] = {
+ "", "", "fnclex","fninit","", "", "", ""
+};
+
+char * db_Esce3[] = {
+ "", "fcompp","", "", "", "", "", ""
+};
+
+char * db_Escf4[] = {
+ "fnstsw","", "", "", "", "", "", ""
+};
+
+struct finst db_Esc8[] = {
+/*0*/ { "fadd", SNGL, op2(STI,ST), 0 },
+/*1*/ { "fmul", SNGL, op2(STI,ST), 0 },
+/*2*/ { "fcom", SNGL, op2(STI,ST), 0 },
+/*3*/ { "fcomp", SNGL, op2(STI,ST), 0 },
+/*4*/ { "fsub", SNGL, op2(STI,ST), 0 },
+/*5*/ { "fsubr", SNGL, op2(STI,ST), 0 },
+/*6*/ { "fdiv", SNGL, op2(STI,ST), 0 },
+/*7*/ { "fdivr", SNGL, op2(STI,ST), 0 },
+};
+
+struct finst db_Esc9[] = {
+/*0*/ { "fld", SNGL, op1(STI), 0 },
+/*1*/ { "", NONE, op1(STI), "fxch" },
+/*2*/ { "fst", SNGL, op1(X), (char *)db_Esc92 },
+/*3*/ { "fstp", SNGL, op1(X), (char *)db_Esc93 },
+/*4*/ { "fldenv", NONE, op1(X), (char *)db_Esc94 },
+/*5*/ { "fldcw", NONE, op1(X), (char *)db_Esc95 },
+/*6*/ { "fnstenv",NONE, op1(X), (char *)db_Esc96 },
+/*7*/ { "fnstcw", NONE, op1(X), (char *)db_Esc97 },
+};
+
+struct finst db_Esca[] = {
+/*0*/ { "fiadd", WORD, 0, 0 },
+/*1*/ { "fimul", WORD, 0, 0 },
+/*2*/ { "ficom", WORD, 0, 0 },
+/*3*/ { "ficomp", WORD, 0, 0 },
+/*4*/ { "fisub", WORD, op1(X), (char *)db_Esca4 },
+/*5*/ { "fisubr", WORD, 0, 0 },
+/*6*/ { "fidiv", WORD, 0, 0 },
+/*7*/ { "fidivr", WORD, 0, 0 }
+};
+
+struct finst db_Escb[] = {
+/*0*/ { "fild", WORD, 0, 0 },
+/*1*/ { "", NONE, 0, 0 },
+/*2*/ { "fist", WORD, 0, 0 },
+/*3*/ { "fistp", WORD, 0, 0 },
+/*4*/ { "", WORD, op1(X), (char *)db_Escb4 },
+/*5*/ { "fld", EXTR, 0, 0 },
+/*6*/ { "", WORD, 0, 0 },
+/*7*/ { "fstp", EXTR, 0, 0 },
+};
+
+struct finst db_Escc[] = {
+/*0*/ { "fadd", DBLR, op2(ST,STI), 0 },
+/*1*/ { "fmul", DBLR, op2(ST,STI), 0 },
+/*2*/ { "fcom", DBLR, op2(ST,STI), 0 },
+/*3*/ { "fcomp", DBLR, op2(ST,STI), 0 },
+/*4*/ { "fsub", DBLR, op2(ST,STI), "fsubr" },
+/*5*/ { "fsubr", DBLR, op2(ST,STI), "fsub" },
+/*6*/ { "fdiv", DBLR, op2(ST,STI), "fdivr" },
+/*7*/ { "fdivr", DBLR, op2(ST,STI), "fdiv" },
+};
+
+struct finst db_Escd[] = {
+/*0*/ { "fld", DBLR, op1(STI), "ffree" },
+/*1*/ { "", NONE, 0, 0 },
+/*2*/ { "fst", DBLR, op1(STI), 0 },
+/*3*/ { "fstp", DBLR, op1(STI), 0 },
+/*4*/ { "frstor", NONE, op1(STI), "fucom" },
+/*5*/ { "", NONE, op1(STI), "fucomp" },
+/*6*/ { "fnsave", NONE, 0, 0 },
+/*7*/ { "fnstsw", NONE, 0, 0 },
+};
+
+struct finst db_Esce[] = {
+/*0*/ { "fiadd", LONG, op2(ST,STI), "faddp" },
+/*1*/ { "fimul", LONG, op2(ST,STI), "fmulp" },
+/*2*/ { "ficom", LONG, 0, 0 },
+/*3*/ { "ficomp", LONG, op1(X), (char *)db_Esce3 },
+/*4*/ { "fisub", LONG, op2(ST,STI), "fsubrp" },
+/*5*/ { "fisubr", LONG, op2(ST,STI), "fsubp" },
+/*6*/ { "fidiv", LONG, op2(ST,STI), "fdivrp" },
+/*7*/ { "fidivr", LONG, op2(ST,STI), "fdivp" },
+};
+
+struct finst db_Escf[] = {
+/*0*/ { "fild", LONG, 0, 0 },
+/*1*/ { "", LONG, 0, 0 },
+/*2*/ { "fist", LONG, 0, 0 },
+/*3*/ { "fistp", LONG, 0, 0 },
+/*4*/ { "fbld", NONE, op1(XA), (char *)db_Escf4 },
+/*5*/ { "fld", QUAD, 0, 0 },
+/*6*/ { "fbstp", NONE, 0, 0 },
+/*7*/ { "fstp", QUAD, 0, 0 },
+};
+
+struct finst *db_Esc_inst[] = {
+ db_Esc8, db_Esc9, db_Esca, db_Escb,
+ db_Escc, db_Escd, db_Esce, db_Escf
+};
+
+char * db_Grp1[] = {
+ "add",
+ "or",
+ "adc",
+ "sbb",
+ "and",
+ "sub",
+ "xor",
+ "cmp"
+};
+
+char * db_Grp2[] = {
+ "rol",
+ "ror",
+ "rcl",
+ "rcr",
+ "shl",
+ "shr",
+ "shl",
+ "sar"
+};
+
+struct inst db_Grp3[] = {
+ { "test", TRUE, NONE, op2(I,E), 0 },
+ { "test", TRUE, NONE, op2(I,E), 0 },
+ { "not", TRUE, NONE, op1(E), 0 },
+ { "neg", TRUE, NONE, op1(E), 0 },
+ { "mul", TRUE, NONE, op2(E,A), 0 },
+ { "imul", TRUE, NONE, op2(E,A), 0 },
+ { "div", TRUE, NONE, op2(E,A), 0 },
+ { "idiv", TRUE, NONE, op2(E,A), 0 },
+};
+
+struct inst db_Grp4[] = {
+ { "inc", TRUE, BYTE, op1(E), 0 },
+ { "dec", TRUE, BYTE, op1(E), 0 },
+ { "", TRUE, NONE, 0, 0 },
+ { "", TRUE, NONE, 0, 0 },
+ { "", TRUE, NONE, 0, 0 },
+ { "", TRUE, NONE, 0, 0 },
+ { "", TRUE, NONE, 0, 0 },
+ { "", TRUE, NONE, 0, 0 }
+};
+
+struct inst db_Grp5[] = {
+ { "inc", TRUE, LONG, op1(E), 0 },
+ { "dec", TRUE, LONG, op1(E), 0 },
+ { "call", TRUE, NONE, op1(Eind),0 },
+ { "lcall", TRUE, NONE, op1(Eind),0 },
+ { "jmp", TRUE, NONE, op1(Eind),0 },
+ { "ljmp", TRUE, NONE, op1(Eind),0 },
+ { "push", TRUE, LONG, op1(E), 0 },
+ { "", TRUE, NONE, 0, 0 }
+};
+
+struct inst db_inst_table[256] = {
+/*00*/ { "add", TRUE, BYTE, op2(R, E), 0 },
+/*01*/ { "add", TRUE, LONG, op2(R, E), 0 },
+/*02*/ { "add", TRUE, BYTE, op2(E, R), 0 },
+/*03*/ { "add", TRUE, LONG, op2(E, R), 0 },
+/*04*/ { "add", FALSE, BYTE, op2(Is, A), 0 },
+/*05*/ { "add", FALSE, LONG, op2(Is, A), 0 },
+/*06*/ { "push", FALSE, NONE, op1(Si), 0 },
+/*07*/ { "pop", FALSE, NONE, op1(Si), 0 },
+
+/*08*/ { "or", TRUE, BYTE, op2(R, E), 0 },
+/*09*/ { "or", TRUE, LONG, op2(R, E), 0 },
+/*0a*/ { "or", TRUE, BYTE, op2(E, R), 0 },
+/*0b*/ { "or", TRUE, LONG, op2(E, R), 0 },
+/*0c*/ { "or", FALSE, BYTE, op2(I, A), 0 },
+/*0d*/ { "or", FALSE, LONG, op2(I, A), 0 },
+/*0e*/ { "push", FALSE, NONE, op1(Si), 0 },
+/*0f*/ { "", FALSE, NONE, 0, 0 },
+
+/*10*/ { "adc", TRUE, BYTE, op2(R, E), 0 },
+/*11*/ { "adc", TRUE, LONG, op2(R, E), 0 },
+/*12*/ { "adc", TRUE, BYTE, op2(E, R), 0 },
+/*13*/ { "adc", TRUE, LONG, op2(E, R), 0 },
+/*14*/ { "adc", FALSE, BYTE, op2(Is, A), 0 },
+/*15*/ { "adc", FALSE, LONG, op2(Is, A), 0 },
+/*16*/ { "push", FALSE, NONE, op1(Si), 0 },
+/*17*/ { "pop", FALSE, NONE, op1(Si), 0 },
+
+/*18*/ { "sbb", TRUE, BYTE, op2(R, E), 0 },
+/*19*/ { "sbb", TRUE, LONG, op2(R, E), 0 },
+/*1a*/ { "sbb", TRUE, BYTE, op2(E, R), 0 },
+/*1b*/ { "sbb", TRUE, LONG, op2(E, R), 0 },
+/*1c*/ { "sbb", FALSE, BYTE, op2(Is, A), 0 },
+/*1d*/ { "sbb", FALSE, LONG, op2(Is, A), 0 },
+/*1e*/ { "push", FALSE, NONE, op1(Si), 0 },
+/*1f*/ { "pop", FALSE, NONE, op1(Si), 0 },
+
+/*20*/ { "and", TRUE, BYTE, op2(R, E), 0 },
+/*21*/ { "and", TRUE, LONG, op2(R, E), 0 },
+/*22*/ { "and", TRUE, BYTE, op2(E, R), 0 },
+/*23*/ { "and", TRUE, LONG, op2(E, R), 0 },
+/*24*/ { "and", FALSE, BYTE, op2(I, A), 0 },
+/*25*/ { "and", FALSE, LONG, op2(I, A), 0 },
+/*26*/ { "", FALSE, NONE, 0, 0 },
+/*27*/ { "aaa", FALSE, NONE, 0, 0 },
+
+/*28*/ { "sub", TRUE, BYTE, op2(R, E), 0 },
+/*29*/ { "sub", TRUE, LONG, op2(R, E), 0 },
+/*2a*/ { "sub", TRUE, BYTE, op2(E, R), 0 },
+/*2b*/ { "sub", TRUE, LONG, op2(E, R), 0 },
+/*2c*/ { "sub", FALSE, BYTE, op2(Is, A), 0 },
+/*2d*/ { "sub", FALSE, LONG, op2(Is, A), 0 },
+/*2e*/ { "", FALSE, NONE, 0, 0 },
+/*2f*/ { "das", FALSE, NONE, 0, 0 },
+
+/*30*/ { "xor", TRUE, BYTE, op2(R, E), 0 },
+/*31*/ { "xor", TRUE, LONG, op2(R, E), 0 },
+/*32*/ { "xor", TRUE, BYTE, op2(E, R), 0 },
+/*33*/ { "xor", TRUE, LONG, op2(E, R), 0 },
+/*34*/ { "xor", FALSE, BYTE, op2(I, A), 0 },
+/*35*/ { "xor", FALSE, LONG, op2(I, A), 0 },
+/*36*/ { "", FALSE, NONE, 0, 0 },
+/*37*/ { "daa", FALSE, NONE, 0, 0 },
+
+/*38*/ { "cmp", TRUE, BYTE, op2(R, E), 0 },
+/*39*/ { "cmp", TRUE, LONG, op2(R, E), 0 },
+/*3a*/ { "cmp", TRUE, BYTE, op2(E, R), 0 },
+/*3b*/ { "cmp", TRUE, LONG, op2(E, R), 0 },
+/*3c*/ { "cmp", FALSE, BYTE, op2(Is, A), 0 },
+/*3d*/ { "cmp", FALSE, LONG, op2(Is, A), 0 },
+/*3e*/ { "", FALSE, NONE, 0, 0 },
+/*3f*/ { "aas", FALSE, NONE, 0, 0 },
+
+/*40*/ { "inc", FALSE, LONG, op1(Ri), 0 },
+/*41*/ { "inc", FALSE, LONG, op1(Ri), 0 },
+/*42*/ { "inc", FALSE, LONG, op1(Ri), 0 },
+/*43*/ { "inc", FALSE, LONG, op1(Ri), 0 },
+/*44*/ { "inc", FALSE, LONG, op1(Ri), 0 },
+/*45*/ { "inc", FALSE, LONG, op1(Ri), 0 },
+/*46*/ { "inc", FALSE, LONG, op1(Ri), 0 },
+/*47*/ { "inc", FALSE, LONG, op1(Ri), 0 },
+
+/*48*/ { "dec", FALSE, LONG, op1(Ri), 0 },
+/*49*/ { "dec", FALSE, LONG, op1(Ri), 0 },
+/*4a*/ { "dec", FALSE, LONG, op1(Ri), 0 },
+/*4b*/ { "dec", FALSE, LONG, op1(Ri), 0 },
+/*4c*/ { "dec", FALSE, LONG, op1(Ri), 0 },
+/*4d*/ { "dec", FALSE, LONG, op1(Ri), 0 },
+/*4e*/ { "dec", FALSE, LONG, op1(Ri), 0 },
+/*4f*/ { "dec", FALSE, LONG, op1(Ri), 0 },
+
+/*50*/ { "push", FALSE, LONG, op1(Ri), 0 },
+/*51*/ { "push", FALSE, LONG, op1(Ri), 0 },
+/*52*/ { "push", FALSE, LONG, op1(Ri), 0 },
+/*53*/ { "push", FALSE, LONG, op1(Ri), 0 },
+/*54*/ { "push", FALSE, LONG, op1(Ri), 0 },
+/*55*/ { "push", FALSE, LONG, op1(Ri), 0 },
+/*56*/ { "push", FALSE, LONG, op1(Ri), 0 },
+/*57*/ { "push", FALSE, LONG, op1(Ri), 0 },
+
+/*58*/ { "pop", FALSE, LONG, op1(Ri), 0 },
+/*59*/ { "pop", FALSE, LONG, op1(Ri), 0 },
+/*5a*/ { "pop", FALSE, LONG, op1(Ri), 0 },
+/*5b*/ { "pop", FALSE, LONG, op1(Ri), 0 },
+/*5c*/ { "pop", FALSE, LONG, op1(Ri), 0 },
+/*5d*/ { "pop", FALSE, LONG, op1(Ri), 0 },
+/*5e*/ { "pop", FALSE, LONG, op1(Ri), 0 },
+/*5f*/ { "pop", FALSE, LONG, op1(Ri), 0 },
+
+/*60*/ { "pusha", FALSE, LONG, 0, 0 },
+/*61*/ { "popa", FALSE, LONG, 0, 0 },
+/*62*/ { "bound", TRUE, LONG, op2(E, R), 0 },
+/*63*/ { "arpl", TRUE, NONE, op2(Ew,Rw), 0 },
+
+/*64*/ { "", FALSE, NONE, 0, 0 },
+/*65*/ { "", FALSE, NONE, 0, 0 },
+/*66*/ { "", FALSE, NONE, 0, 0 },
+/*67*/ { "", FALSE, NONE, 0, 0 },
+
+/*68*/ { "push", FALSE, LONG, op1(I), 0 },
+/*69*/ { "imul", TRUE, LONG, op3(I,E,R), 0 },
+/*6a*/ { "push", FALSE, LONG, op1(Ib), 0 },
+/*6b*/ { "imul", TRUE, LONG, op3(Ibs,E,R),0 },
+/*6c*/ { "ins", FALSE, BYTE, op2(DX, DI), 0 },
+/*6d*/ { "ins", FALSE, LONG, op2(DX, DI), 0 },
+/*6e*/ { "outs", FALSE, BYTE, op2(SI, DX), 0 },
+/*6f*/ { "outs", FALSE, LONG, op2(SI, DX), 0 },
+
+/*70*/ { "jo", FALSE, NONE, op1(Db), 0 },
+/*71*/ { "jno", FALSE, NONE, op1(Db), 0 },
+/*72*/ { "jb", FALSE, NONE, op1(Db), 0 },
+/*73*/ { "jnb", FALSE, NONE, op1(Db), 0 },
+/*74*/ { "jz", FALSE, NONE, op1(Db), 0 },
+/*75*/ { "jnz", FALSE, NONE, op1(Db), 0 },
+/*76*/ { "jbe", FALSE, NONE, op1(Db), 0 },
+/*77*/ { "jnbe", FALSE, NONE, op1(Db), 0 },
+
+/*78*/ { "js", FALSE, NONE, op1(Db), 0 },
+/*79*/ { "jns", FALSE, NONE, op1(Db), 0 },
+/*7a*/ { "jp", FALSE, NONE, op1(Db), 0 },
+/*7b*/ { "jnp", FALSE, NONE, op1(Db), 0 },
+/*7c*/ { "jl", FALSE, NONE, op1(Db), 0 },
+/*7d*/ { "jnl", FALSE, NONE, op1(Db), 0 },
+/*7e*/ { "jle", FALSE, NONE, op1(Db), 0 },
+/*7f*/ { "jnle", FALSE, NONE, op1(Db), 0 },
+
+/*80*/ { "", TRUE, BYTE, op2(I, E), (char *)db_Grp1 },
+/*81*/ { "", TRUE, LONG, op2(I, E), (char *)db_Grp1 },
+/*82*/ { "", TRUE, BYTE, op2(Is,E), (char *)db_Grp1 },
+/*83*/ { "", TRUE, LONG, op2(Ibs,E), (char *)db_Grp1 },
+/*84*/ { "test", TRUE, BYTE, op2(R, E), 0 },
+/*85*/ { "test", TRUE, LONG, op2(R, E), 0 },
+/*86*/ { "xchg", TRUE, BYTE, op2(R, E), 0 },
+/*87*/ { "xchg", TRUE, LONG, op2(R, E), 0 },
+
+/*88*/ { "mov", TRUE, BYTE, op2(R, E), 0 },
+/*89*/ { "mov", TRUE, LONG, op2(R, E), 0 },
+/*8a*/ { "mov", TRUE, BYTE, op2(E, R), 0 },
+/*8b*/ { "mov", TRUE, LONG, op2(E, R), 0 },
+/*8c*/ { "mov", TRUE, NONE, op2(S, Ew), 0 },
+/*8d*/ { "lea", TRUE, LONG, op2(E, R), 0 },
+/*8e*/ { "mov", TRUE, NONE, op2(Ew, S), 0 },
+/*8f*/ { "pop", TRUE, LONG, op1(E), 0 },
+
+/*90*/ { "nop", FALSE, NONE, 0, 0 },
+/*91*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 },
+/*92*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 },
+/*93*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 },
+/*94*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 },
+/*95*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 },
+/*96*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 },
+/*97*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 },
+
+/*98*/ { "cbw", FALSE, SDEP, 0, "cwde" }, /* cbw/cwde */
+/*99*/ { "cwd", FALSE, SDEP, 0, "cdq" }, /* cwd/cdq */
+/*9a*/ { "lcall", FALSE, NONE, op1(OS), 0 },
+/*9b*/ { "wait", FALSE, NONE, 0, 0 },
+/*9c*/ { "pushf", FALSE, LONG, 0, 0 },
+/*9d*/ { "popf", FALSE, LONG, 0, 0 },
+/*9e*/ { "sahf", FALSE, NONE, 0, 0 },
+/*9f*/ { "lahf", FALSE, NONE, 0, 0 },
+
+/*a0*/ { "mov", FALSE, BYTE, op2(O, A), 0 },
+/*a1*/ { "mov", FALSE, LONG, op2(O, A), 0 },
+/*a2*/ { "mov", FALSE, BYTE, op2(A, O), 0 },
+/*a3*/ { "mov", FALSE, LONG, op2(A, O), 0 },
+/*a4*/ { "movs", FALSE, BYTE, op2(SI,DI), 0 },
+/*a5*/ { "movs", FALSE, LONG, op2(SI,DI), 0 },
+/*a6*/ { "cmps", FALSE, BYTE, op2(SI,DI), 0 },
+/*a7*/ { "cmps", FALSE, LONG, op2(SI,DI), 0 },
+
+/*a8*/ { "test", FALSE, BYTE, op2(I, A), 0 },
+/*a9*/ { "test", FALSE, LONG, op2(I, A), 0 },
+/*aa*/ { "stos", FALSE, BYTE, op1(DI), 0 },
+/*ab*/ { "stos", FALSE, LONG, op1(DI), 0 },
+/*ac*/ { "lods", FALSE, BYTE, op1(SI), 0 },
+/*ad*/ { "lods", FALSE, LONG, op1(SI), 0 },
+/*ae*/ { "scas", FALSE, BYTE, op1(SI), 0 },
+/*af*/ { "scas", FALSE, LONG, op1(SI), 0 },
+
+/*b0*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 },
+/*b1*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 },
+/*b2*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 },
+/*b3*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 },
+/*b4*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 },
+/*b5*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 },
+/*b6*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 },
+/*b7*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 },
+
+/*b8*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
+/*b9*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
+/*ba*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
+/*bb*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
+/*bc*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
+/*bd*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
+/*be*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
+/*bf*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
+
+/*c0*/ { "", TRUE, BYTE, op2(Ib, E), (char *)db_Grp2 },
+/*c1*/ { "", TRUE, LONG, op2(Ib, E), (char *)db_Grp2 },
+/*c2*/ { "ret", FALSE, NONE, op1(Iw), 0 },
+/*c3*/ { "ret", FALSE, NONE, 0, 0 },
+/*c4*/ { "les", TRUE, LONG, op2(E, R), 0 },
+/*c5*/ { "lds", TRUE, LONG, op2(E, R), 0 },
+/*c6*/ { "mov", TRUE, BYTE, op2(I, E), 0 },
+/*c7*/ { "mov", TRUE, LONG, op2(I, E), 0 },
+
+/*c8*/ { "enter", FALSE, NONE, op2(Ib, Iw), 0 },
+/*c9*/ { "leave", FALSE, NONE, 0, 0 },
+/*ca*/ { "lret", FALSE, NONE, op1(Iw), 0 },
+/*cb*/ { "lret", FALSE, NONE, 0, 0 },
+/*cc*/ { "int", FALSE, NONE, op1(o3), 0 },
+/*cd*/ { "int", FALSE, NONE, op1(Ib), 0 },
+/*ce*/ { "into", FALSE, NONE, 0, 0 },
+/*cf*/ { "iret", FALSE, NONE, 0, 0 },
+
+/*d0*/ { "", TRUE, BYTE, op2(o1, E), (char *)db_Grp2 },
+/*d1*/ { "", TRUE, LONG, op2(o1, E), (char *)db_Grp2 },
+/*d2*/ { "", TRUE, BYTE, op2(CL, E), (char *)db_Grp2 },
+/*d3*/ { "", TRUE, LONG, op2(CL, E), (char *)db_Grp2 },
+/*d4*/ { "aam", TRUE, NONE, 0, 0 },
+/*d5*/ { "aad", TRUE, NONE, 0, 0 },
+/*d6*/ { "", FALSE, NONE, 0, 0 },
+/*d7*/ { "xlat", FALSE, BYTE, op1(BX), 0 },
+
+/*d8*/ { "", TRUE, NONE, 0, (char *)db_Esc8 },
+/*d9*/ { "", TRUE, NONE, 0, (char *)db_Esc9 },
+/*da*/ { "", TRUE, NONE, 0, (char *)db_Esca },
+/*db*/ { "", TRUE, NONE, 0, (char *)db_Escb },
+/*dc*/ { "", TRUE, NONE, 0, (char *)db_Escc },
+/*dd*/ { "", TRUE, NONE, 0, (char *)db_Escd },
+/*de*/ { "", TRUE, NONE, 0, (char *)db_Esce },
+/*df*/ { "", TRUE, NONE, 0, (char *)db_Escf },
+
+/*e0*/ { "loopne",FALSE, NONE, op1(Db), 0 },
+/*e1*/ { "loope", FALSE, NONE, op1(Db), 0 },
+/*e2*/ { "loop", FALSE, NONE, op1(Db), 0 },
+/*e3*/ { "jcxz", FALSE, SDEP, op1(Db), "jecxz" },
+/*e4*/ { "in", FALSE, BYTE, op2(Ib, A), 0 },
+/*e5*/ { "in", FALSE, LONG, op2(Ib, A) , 0 },
+/*e6*/ { "out", FALSE, BYTE, op2(A, Ib), 0 },
+/*e7*/ { "out", FALSE, LONG, op2(A, Ib) , 0 },
+
+/*e8*/ { "call", FALSE, NONE, op1(Dl), 0 },
+/*e9*/ { "jmp", FALSE, NONE, op1(Dl), 0 },
+/*ea*/ { "ljmp", FALSE, NONE, op1(OS), 0 },
+/*eb*/ { "jmp", FALSE, NONE, op1(Db), 0 },
+/*ec*/ { "in", FALSE, BYTE, op2(DX, A), 0 },
+/*ed*/ { "in", FALSE, LONG, op2(DX, A) , 0 },
+/*ee*/ { "out", FALSE, BYTE, op2(A, DX), 0 },
+/*ef*/ { "out", FALSE, LONG, op2(A, DX) , 0 },
+
+/*f0*/ { "", FALSE, NONE, 0, 0 },
+/*f1*/ { "", FALSE, NONE, 0, 0 },
+/*f2*/ { "", FALSE, NONE, 0, 0 },
+/*f3*/ { "", FALSE, NONE, 0, 0 },
+/*f4*/ { "hlt", FALSE, NONE, 0, 0 },
+/*f5*/ { "cmc", FALSE, NONE, 0, 0 },
+/*f6*/ { "", TRUE, BYTE, 0, (char *)db_Grp3 },
+/*f7*/ { "", TRUE, LONG, 0, (char *)db_Grp3 },
+
+/*f8*/ { "clc", FALSE, NONE, 0, 0 },
+/*f9*/ { "stc", FALSE, NONE, 0, 0 },
+/*fa*/ { "cli", FALSE, NONE, 0, 0 },
+/*fb*/ { "sti", FALSE, NONE, 0, 0 },
+/*fc*/ { "cld", FALSE, NONE, 0, 0 },
+/*fd*/ { "std", FALSE, NONE, 0, 0 },
+/*fe*/ { "", TRUE, NONE, 0, (char *)db_Grp4 },
+/*ff*/ { "", TRUE, NONE, 0, (char *)db_Grp5 },
+};
+
+struct inst db_bad_inst =
+ { "???", FALSE, NONE, 0, 0 }
+;
+
+#define f_mod(byte) ((byte)>>6)
+#define f_reg(byte) (((byte)>>3)&0x7)
+#define f_rm(byte) ((byte)&0x7)
+
+#define sib_ss(byte) ((byte)>>6)
+#define sib_index(byte) (((byte)>>3)&0x7)
+#define sib_base(byte) ((byte)&0x7)
+
+struct i_addr {
+ int is_reg; /* if reg, reg number is in 'disp' */
+ int disp;
+ char * base;
+ char * index;
+ int ss;
+};
+
+char * db_index_reg_16[8] = {
+ "%bx,%si",
+ "%bx,%di",
+ "%bp,%si",
+ "%bp,%di",
+ "%si",
+ "%di",
+ "%bp",
+ "%bx"
+};
+
+char * db_reg[3][8] = {
+ "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh",
+ "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di",
+ "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi"
+};
+
+char * db_seg_reg[8] = {
+ "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "", ""
+};
+
+/*
+ * lengths for size attributes
+ */
+int db_lengths[] = {
+ 1, /* BYTE */
+ 2, /* WORD */
+ 4, /* LONG */
+ 8, /* QUAD */
+ 4, /* SNGL */
+ 8, /* DBLR */
+ 10, /* EXTR */
+};
+
+#define get_value_inc(result, loc, size, is_signed) \
+ result = db_get_value((loc), (size), (is_signed)); \
+ (loc) += (size);
+
+/*
+ * Read address at location and return updated location.
+ */
+db_addr_t
+db_read_address(loc, short_addr, regmodrm, addrp)
+ db_addr_t loc;
+ int short_addr;
+ int regmodrm;
+ struct i_addr *addrp; /* out */
+{
+ int mod, rm, sib, index, ss, disp;
+
+ mod = f_mod(regmodrm);
+ rm = f_rm(regmodrm);
+
+ if (mod == 3) {
+ addrp->is_reg = TRUE;
+ addrp->disp = rm;
+ return (loc);
+ }
+ addrp->is_reg = FALSE;
+ addrp->index = 0;
+
+ if (short_addr) {
+ addrp->index = 0;
+ addrp->ss = 0;
+ switch (mod) {
+ case 0:
+ if (rm == 6) {
+ get_value_inc(disp, loc, 2, TRUE);
+ addrp->disp = disp;
+ addrp->base = 0;
+ }
+ else {
+ addrp->disp = 0;
+ addrp->base = db_index_reg_16[rm];
+ }
+ break;
+ case 1:
+ get_value_inc(disp, loc, 1, TRUE);
+ addrp->disp = disp;
+ addrp->base = db_index_reg_16[rm];
+ break;
+ case 2:
+ get_value_inc(disp, loc, 2, TRUE);
+ addrp->disp = disp;
+ addrp->base = db_index_reg_16[rm];
+ break;
+ }
+ }
+ else {
+ if (mod != 3 && rm == 4) {
+ get_value_inc(sib, loc, 1, FALSE);
+ rm = sib_base(sib);
+ index = sib_index(sib);
+ if (index != 4)
+ addrp->index = db_reg[LONG][index];
+ addrp->ss = sib_ss(sib);
+ }
+
+ switch (mod) {
+ case 0:
+ if (rm == 5) {
+ get_value_inc(addrp->disp, loc, 4, FALSE);
+ addrp->base = 0;
+ }
+ else {
+ addrp->disp = 0;
+ addrp->base = db_reg[LONG][rm];
+ }
+ break;
+
+ case 1:
+ get_value_inc(disp, loc, 1, TRUE);
+ addrp->disp = disp;
+ addrp->base = db_reg[LONG][rm];
+ break;
+
+ case 2:
+ get_value_inc(disp, loc, 4, FALSE);
+ addrp->disp = disp;
+ addrp->base = db_reg[LONG][rm];
+ break;
+ }
+ }
+ return (loc);
+}
+
+void
+db_print_address(seg, size, addrp)
+ char * seg;
+ int size;
+ struct i_addr *addrp;
+{
+ if (addrp->is_reg) {
+ db_printf("%s", db_reg[size][addrp->disp]);
+ return;
+ }
+
+ if (seg) {
+ db_printf("%s:", seg);
+ }
+
+ db_printsym((db_addr_t)addrp->disp, DB_STGY_ANY);
+ if (addrp->base != 0 || addrp->index != 0) {
+ db_printf("(");
+ if (addrp->base)
+ db_printf("%s", addrp->base);
+ if (addrp->index)
+ db_printf(",%s,%d", addrp->index, 1<<addrp->ss);
+ db_printf(")");
+ }
+}
+
+/*
+ * Disassemble floating-point ("escape") instruction
+ * and return updated location.
+ */
+db_addr_t
+db_disasm_esc(loc, inst, short_addr, size, seg)
+ db_addr_t loc;
+ int inst;
+ int short_addr;
+ int size;
+ char * seg;
+{
+ int regmodrm;
+ struct finst *fp;
+ int mod;
+ struct i_addr address;
+ char * name;
+
+ get_value_inc(regmodrm, loc, 1, FALSE);
+ fp = &db_Esc_inst[inst - 0xd8][f_reg(regmodrm)];
+ mod = f_mod(regmodrm);
+ if (mod != 3) {
+ /*
+ * Normal address modes.
+ */
+ loc = db_read_address(loc, short_addr, regmodrm, &address);
+ db_printf(fp->f_name);
+ switch(fp->f_size) {
+ case SNGL:
+ db_printf("s");
+ break;
+ case DBLR:
+ db_printf("l");
+ break;
+ case EXTR:
+ db_printf("t");
+ break;
+ case WORD:
+ db_printf("s");
+ break;
+ case LONG:
+ db_printf("l");
+ break;
+ case QUAD:
+ db_printf("q");
+ break;
+ default:
+ break;
+ }
+ db_printf("\t");
+ db_print_address(seg, BYTE, &address);
+ }
+ else {
+ /*
+ * 'reg-reg' - special formats
+ */
+ switch (fp->f_rrmode) {
+ case op2(ST,STI):
+ name = (fp->f_rrname) ? fp->f_rrname : fp->f_name;
+ db_printf("%s\t%%st,%%st(%d)",name,f_rm(regmodrm));
+ break;
+ case op2(STI,ST):
+ name = (fp->f_rrname) ? fp->f_rrname : fp->f_name;
+ db_printf("%s\t%%st(%d),%%st",name, f_rm(regmodrm));
+ break;
+ case op1(STI):
+ name = (fp->f_rrname) ? fp->f_rrname : fp->f_name;
+ db_printf("%s\t%%st(%d)",name, f_rm(regmodrm));
+ break;
+ case op1(X):
+ db_printf("%s", ((char **)fp->f_rrname)[f_rm(regmodrm)]);
+ break;
+ case op1(XA):
+ db_printf("%s\t%%ax",
+ ((char **)fp->f_rrname)[f_rm(regmodrm)]);
+ break;
+ default:
+ db_printf("<bad instruction>");
+ break;
+ }
+ }
+
+ return (loc);
+}
+
+/*
+ * Disassemble instruction at 'loc'. 'altfmt' specifies an
+ * (optional) alternate format. Return address of start of
+ * next instruction.
+ */
+db_addr_t
+db_disasm(loc, altfmt)
+ db_addr_t loc;
+ boolean_t altfmt;
+{
+ int inst;
+ int size;
+ int short_addr;
+ char * seg;
+ struct inst * ip;
+ char * i_name;
+ int i_size;
+ int i_mode;
+ int regmodrm = 0;
+ boolean_t first;
+ int displ;
+ int prefix;
+ int imm;
+ int imm2;
+ int len;
+ struct i_addr address;
+
+ get_value_inc(inst, loc, 1, FALSE);
+ short_addr = FALSE;
+ size = LONG;
+ seg = 0;
+
+ /*
+ * Get prefixes
+ */
+ prefix = TRUE;
+ do {
+ switch (inst) {
+ case 0x66: /* data16 */
+ size = WORD;
+ break;
+ case 0x67:
+ short_addr = TRUE;
+ break;
+ case 0x26:
+ seg = "%es";
+ break;
+ case 0x36:
+ seg = "%ss";
+ break;
+ case 0x2e:
+ seg = "%cs";
+ break;
+ case 0x3e:
+ seg = "%ds";
+ break;
+ case 0x64:
+ seg = "%fs";
+ break;
+ case 0x65:
+ seg = "%gs";
+ break;
+ case 0xf0:
+ db_printf("lock ");
+ break;
+ case 0xf2:
+ db_printf("repne ");
+ break;
+ case 0xf3:
+ db_printf("repe "); /* XXX repe VS rep */
+ break;
+ default:
+ prefix = FALSE;
+ break;
+ }
+ if (prefix) {
+ get_value_inc(inst, loc, 1, FALSE);
+ }
+ } while (prefix);
+
+ if (inst >= 0xd8 && inst <= 0xdf) {
+ loc = db_disasm_esc(loc, inst, short_addr, size, seg);
+ db_printf("\n");
+ return (loc);
+ }
+
+ if (inst == 0x0f) {
+ get_value_inc(inst, loc, 1, FALSE);
+ ip = db_inst_0f[inst>>4];
+ if (ip == 0) {
+ ip = &db_bad_inst;
+ }
+ else {
+ ip = &ip[inst&0xf];
+ }
+ }
+ else
+ ip = &db_inst_table[inst];
+
+ if (ip->i_has_modrm) {
+ get_value_inc(regmodrm, loc, 1, FALSE);
+ loc = db_read_address(loc, short_addr, regmodrm, &address);
+ }
+
+ i_name = ip->i_name;
+ i_size = ip->i_size;
+ i_mode = ip->i_mode;
+
+ if (ip->i_extra == (char *)db_Grp1 ||
+ ip->i_extra == (char *)db_Grp2 ||
+ ip->i_extra == (char *)db_Grp6 ||
+ ip->i_extra == (char *)db_Grp7 ||
+ ip->i_extra == (char *)db_Grp8) {
+ i_name = ((char **)ip->i_extra)[f_reg(regmodrm)];
+ }
+ else if (ip->i_extra == (char *)db_Grp3) {
+ ip = (struct inst *)ip->i_extra;
+ ip = &ip[f_reg(regmodrm)];
+ i_name = ip->i_name;
+ i_mode = ip->i_mode;
+ }
+ else if (ip->i_extra == (char *)db_Grp4 ||
+ ip->i_extra == (char *)db_Grp5) {
+ ip = (struct inst *)ip->i_extra;
+ ip = &ip[f_reg(regmodrm)];
+ i_name = ip->i_name;
+ i_mode = ip->i_mode;
+ i_size = ip->i_size;
+ }
+
+ if (i_size == SDEP) {
+ if (size == WORD)
+ db_printf(i_name);
+ else
+ db_printf(ip->i_extra);
+ }
+ else {
+ db_printf(i_name);
+ if (i_size != NONE) {
+ if (i_size == BYTE) {
+ db_printf("b");
+ size = BYTE;
+ }
+ else if (i_size == WORD) {
+ db_printf("w");
+ size = WORD;
+ }
+ else if (size == WORD)
+ db_printf("w");
+ else
+ db_printf("l");
+ }
+ }
+ db_printf("\t");
+ for (first = TRUE;
+ i_mode != 0;
+ i_mode >>= 8, first = FALSE)
+ {
+ if (!first)
+ db_printf(",");
+
+ switch (i_mode & 0xFF) {
+
+ case E:
+ db_print_address(seg, size, &address);
+ break;
+
+ case Eind:
+ db_printf("*");
+ db_print_address(seg, size, &address);
+ break;
+
+ case Ew:
+ db_print_address(seg, WORD, &address);
+ break;
+
+ case Eb:
+ db_print_address(seg, BYTE, &address);
+ break;
+
+ case R:
+ db_printf("%s", db_reg[size][f_reg(regmodrm)]);
+ break;
+
+ case Rw:
+ db_printf("%s", db_reg[WORD][f_reg(regmodrm)]);
+ break;
+
+ case Ri:
+ db_printf("%s", db_reg[size][f_rm(inst)]);
+ break;
+
+ case S:
+ db_printf("%s", db_seg_reg[f_reg(regmodrm)]);
+ break;
+
+ case Si:
+ db_printf("%s", db_seg_reg[f_reg(inst)]);
+ break;
+
+ case A:
+ db_printf("%s", db_reg[size][0]); /* acc */
+ break;
+
+ case BX:
+ if (seg)
+ db_printf("%s:", seg);
+ db_printf("(%s)", short_addr ? "%bx" : "%ebx");
+ break;
+
+ case CL:
+ db_printf("%%cl");
+ break;
+
+ case DX:
+ db_printf("%%dx");
+ break;
+
+ case SI:
+ if (seg)
+ db_printf("%s:", seg);
+ db_printf("(%s)", short_addr ? "%si" : "%esi");
+ break;
+
+ case DI:
+ db_printf("%%es:(%s)", short_addr ? "%di" : "%edi");
+ break;
+
+ case CR:
+ db_printf("%%cr%d", f_reg(regmodrm));
+ break;
+
+ case DR:
+ db_printf("%%dr%d", f_reg(regmodrm));
+ break;
+
+ case TR:
+ db_printf("%%tr%d", f_reg(regmodrm));
+ break;
+
+ case I:
+ len = db_lengths[size];
+ get_value_inc(imm, loc, len, FALSE);/* unsigned */
+ db_printf("$%#n", imm);
+ break;
+
+ case Is:
+ len = db_lengths[size];
+ get_value_inc(imm, loc, len, TRUE); /* signed */
+ db_printf("$%#r", imm);
+ break;
+
+ case Ib:
+ get_value_inc(imm, loc, 1, FALSE); /* unsigned */
+ db_printf("$%#n", imm);
+ break;
+
+ case Ibs:
+ get_value_inc(imm, loc, 1, TRUE); /* signed */
+ db_printf("$%#r", imm);
+ break;
+
+ case Iw:
+ get_value_inc(imm, loc, 2, FALSE); /* unsigned */
+ db_printf("$%#n", imm);
+ break;
+
+ case Il:
+ get_value_inc(imm, loc, 4, FALSE);
+ db_printf("$%#n", imm);
+ break;
+
+ case O:
+ if (short_addr) {
+ get_value_inc(displ, loc, 2, TRUE);
+ }
+ else {
+ get_value_inc(displ, loc, 4, TRUE);
+ }
+ if (seg)
+ db_printf("%s:%#r",seg, displ);
+ else
+ db_printsym((db_addr_t)displ, DB_STGY_ANY);
+ break;
+
+ case Db:
+ get_value_inc(displ, loc, 1, TRUE);
+ db_printsym((db_addr_t)(displ + loc), DB_STGY_XTRN);
+ break;
+
+ case Dl:
+ get_value_inc(displ, loc, 4, TRUE);
+ db_printsym((db_addr_t)(displ + loc), DB_STGY_XTRN);
+ break;
+
+ case o1:
+ db_printf("$1");
+ break;
+
+ case o3:
+ db_printf("$3");
+ break;
+
+ case OS:
+ get_value_inc(imm, loc, 4, FALSE); /* offset */
+ get_value_inc(imm2, loc, 2, FALSE); /* segment */
+ db_printf("$%#n,%#n", imm2, imm);
+ break;
+ }
+ }
+
+ if (altfmt == 0) {
+ if (inst == 0xe9 || inst == 0xeb) {
+ /*
+ * GAS pads to longword boundary after unconditional jumps.
+ */
+ loc = (loc + (4-1)) & ~(4-1);
+ }
+ }
+ db_printf("\n");
+ return (loc);
+}
+
diff --git a/sys/i386/i386/db_interface.c b/sys/i386/i386/db_interface.c
new file mode 100644
index 0000000..e79a2ae
--- /dev/null
+++ b/sys/i386/i386/db_interface.c
@@ -0,0 +1,240 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_interface.c,v 1.5 1993/12/19 00:50:00 wollman Exp $
+ */
+
+/*
+ * Interface to new debugger.
+ */
+#include "param.h"
+#include "systm.h"
+#include "kernel.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+
+#include <sys/reboot.h>
+/* #include <vm/vm_statistics.h> */
+#include <vm/pmap.h>
+
+#include <setjmp.h>
+
+int db_active = 0;
+
+db_regs_t ddb_regs;
+
+/*
+ * Received keyboard interrupt sequence.
+ */
+void
+kdb_kbd_trap(regs)
+ struct i386_saved_state *regs;
+{
+ if (db_active == 0 && (boothowto & RB_KDB)) {
+ printf("\n\nkernel: keyboard interrupt\n");
+ kdb_trap(-1, 0, regs);
+ }
+}
+
+/*
+ * kdb_trap - field a TRACE or BPT trap
+ */
+
+static jmp_buf *db_nofault = 0;
+
+int
+kdb_trap(type, code, regs)
+ int type, code;
+ register struct i386_saved_state *regs;
+{
+#if 0
+ if ((boothowto&RB_KDB) == 0)
+ return(0);
+#endif
+
+ switch (type) {
+ case T_BPTFLT /* T_INT3 */: /* breakpoint */
+ case T_KDBTRAP /* T_WATCHPOINT */: /* watchpoint */
+ case T_PRIVINFLT /* T_DEBUG */: /* single_step */
+
+ case -1: /* keyboard interrupt */
+ break;
+
+ default:
+ kdbprinttrap(type, code);
+
+ if (db_nofault) {
+ jmp_buf *no_fault = db_nofault;
+ db_nofault = 0;
+ longjmp(*no_fault, 1);
+ }
+ }
+
+ /* Should switch to kdb`s own stack here. */
+
+ ddb_regs = *regs;
+
+ if ((regs->tf_cs & 0x3) == 0) {
+ /*
+ * Kernel mode - esp and ss not saved
+ */
+ ddb_regs.tf_esp = (int)&regs->tf_esp; /* kernel stack pointer */
+#if 0
+ ddb_regs.ss = KERNEL_DS;
+#endif
+ asm(" movw %%ss,%%ax; movl %%eax,%0 "
+ : "=g" (ddb_regs.tf_ss)
+ :
+ : "ax");
+ }
+
+ db_active++;
+ cnpollc(TRUE);
+ db_trap(type, code);
+ cnpollc(FALSE);
+ db_active--;
+
+ regs->tf_eip = ddb_regs.tf_eip;
+ regs->tf_eflags = ddb_regs.tf_eflags;
+ regs->tf_eax = ddb_regs.tf_eax;
+ regs->tf_ecx = ddb_regs.tf_ecx;
+ regs->tf_edx = ddb_regs.tf_edx;
+ regs->tf_ebx = ddb_regs.tf_ebx;
+ if (regs->tf_cs & 0x3) {
+ /*
+ * user mode - saved esp and ss valid
+ */
+ regs->tf_esp = ddb_regs.tf_esp; /* user stack pointer */
+ regs->tf_ss = ddb_regs.tf_ss & 0xffff; /* user stack segment */
+ }
+ regs->tf_ebp = ddb_regs.tf_ebp;
+ regs->tf_esi = ddb_regs.tf_esi;
+ regs->tf_edi = ddb_regs.tf_edi;
+ regs->tf_es = ddb_regs.tf_es & 0xffff;
+ regs->tf_cs = ddb_regs.tf_cs & 0xffff;
+ regs->tf_ds = ddb_regs.tf_ds & 0xffff;
+#if 0
+ regs->tf_fs = ddb_regs.tf_fs & 0xffff;
+ regs->tf_gs = ddb_regs.tf_gs & 0xffff;
+#endif
+
+ return (1);
+}
+
+/*
+ * Print trap reason.
+ */
+void
+kdbprinttrap(type, code)
+ int type, code;
+{
+ printf("kernel: ");
+ printf("type %d", type);
+ printf(" trap, code=%x\n", code);
+}
+
+/*
+ * Read bytes from kernel address space for debugger.
+ */
+
+extern jmp_buf db_jmpbuf;
+
+void
+db_read_bytes(addr, size, data)
+ vm_offset_t addr;
+ register int size;
+ register char *data;
+{
+ register char *src;
+
+ db_nofault = &db_jmpbuf;
+
+ src = (char *)addr;
+ while (--size >= 0)
+ *data++ = *src++;
+
+ db_nofault = 0;
+}
+
+/*
+ * Write bytes to kernel address space for debugger.
+ */
+void
+db_write_bytes(addr, size, data)
+ vm_offset_t addr;
+ register int size;
+ register char *data;
+{
+ register char *dst;
+
+ register pt_entry_t *ptep0 = 0;
+ pt_entry_t oldmap0 = { 0 };
+ vm_offset_t addr1;
+ register pt_entry_t *ptep1 = 0;
+ pt_entry_t oldmap1 = { 0 };
+ extern char etext;
+
+ db_nofault = &db_jmpbuf;
+
+ if (addr >= VM_MIN_KERNEL_ADDRESS &&
+ addr <= (vm_offset_t)&etext)
+ {
+ ptep0 = pmap_pte(kernel_pmap, addr);
+ oldmap0 = *ptep0;
+ *(int *)ptep0 |= /* INTEL_PTE_WRITE */ PG_RW;
+
+ addr1 = i386_trunc_page(addr + size - 1);
+ if (i386_trunc_page(addr) != addr1) {
+ /* data crosses a page boundary */
+
+ ptep1 = pmap_pte(kernel_pmap, addr1);
+ oldmap1 = *ptep1;
+ *(int *)ptep1 |= /* INTEL_PTE_WRITE */ PG_RW;
+ }
+ tlbflush();
+ }
+
+ dst = (char *)addr;
+
+ while (--size >= 0)
+ *dst++ = *data++;
+
+ db_nofault = 0;
+
+ if (ptep0) {
+ *ptep0 = oldmap0;
+ if (ptep1) {
+ *ptep1 = oldmap1;
+ }
+ tlbflush();
+ }
+}
+
+void
+Debugger (msg)
+ const char *msg;
+{
+ asm ("int $3");
+}
diff --git a/sys/i386/i386/db_trace.c b/sys/i386/i386/db_trace.c
new file mode 100644
index 0000000..d536d94
--- /dev/null
+++ b/sys/i386/i386/db_trace.c
@@ -0,0 +1,340 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: db_trace.c,v 1.4 1994/01/03 07:55:19 davidg Exp $
+ */
+
+#include "param.h"
+
+#include <vm/vm_param.h>
+#include <vm/lock.h>
+#include <vm/vm_prot.h>
+#include <vm/pmap.h>
+#include <machine/pmap.h>
+#include "systm.h"
+#include "proc.h"
+#include "ddb/ddb.h"
+
+#include <ddb/db_access.h>
+#include <ddb/db_sym.h>
+#include <ddb/db_variables.h>
+
+/*
+ * Machine register set.
+ */
+struct db_variable db_regs[] = {
+ "cs", (int *)&ddb_regs.tf_cs, FCN_NULL,
+ "ds", (int *)&ddb_regs.tf_ds, FCN_NULL,
+ "es", (int *)&ddb_regs.tf_es, FCN_NULL,
+#if 0
+ "fs", (int *)&ddb_regs.tf_fs, FCN_NULL,
+ "gs", (int *)&ddb_regs.tf_gs, FCN_NULL,
+#endif
+ "ss", (int *)&ddb_regs.tf_ss, FCN_NULL,
+ "eax", (int *)&ddb_regs.tf_eax, FCN_NULL,
+ "ecx", (int *)&ddb_regs.tf_ecx, FCN_NULL,
+ "edx", (int *)&ddb_regs.tf_edx, FCN_NULL,
+ "ebx", (int *)&ddb_regs.tf_ebx, FCN_NULL,
+ "esp", (int *)&ddb_regs.tf_esp,FCN_NULL,
+ "ebp", (int *)&ddb_regs.tf_ebp, FCN_NULL,
+ "esi", (int *)&ddb_regs.tf_esi, FCN_NULL,
+ "edi", (int *)&ddb_regs.tf_edi, FCN_NULL,
+ "eip", (int *)&ddb_regs.tf_eip, FCN_NULL,
+ "efl", (int *)&ddb_regs.tf_eflags, FCN_NULL,
+};
+struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
+
+/*
+ * Stack trace.
+ */
+#define INKERNEL(va) (((vm_offset_t)(va)) >= VM_MIN_KERNEL_ADDRESS)
+
+struct i386_frame {
+ struct i386_frame *f_frame;
+ int f_retaddr;
+ int f_arg0;
+};
+
+#define TRAP 1
+#define INTERRUPT 2
+#define SYSCALL 3
+
+db_addr_t db_trap_symbol_value = 0;
+db_addr_t db_syscall_symbol_value = 0;
+db_addr_t db_kdintr_symbol_value = 0;
+boolean_t db_trace_symbols_found = FALSE;
+
+void
+db_find_trace_symbols()
+{
+ db_expr_t value;
+ if (db_value_of_name("_trap", &value))
+ db_trap_symbol_value = (db_addr_t) value;
+ if (db_value_of_name("_kdintr", &value))
+ db_kdintr_symbol_value = (db_addr_t) value;
+ if (db_value_of_name("_syscall", &value))
+ db_syscall_symbol_value = (db_addr_t) value;
+ db_trace_symbols_found = TRUE;
+}
+
+/*
+ * Figure out how many arguments were passed into the frame at "fp".
+ */
+int
+db_numargs(fp)
+ struct i386_frame *fp;
+{
+ int *argp;
+ int inst;
+ int args;
+ extern char etext[];
+
+ argp = (int *)db_get_value((int)&fp->f_retaddr, 4, FALSE);
+ if (argp < (int *)VM_MIN_KERNEL_ADDRESS || argp > (int *)etext)
+ args = 5;
+ else {
+ inst = db_get_value((int)argp, 4, FALSE);
+ if ((inst & 0xff) == 0x59) /* popl %ecx */
+ args = 1;
+ else if ((inst & 0xffff) == 0xc483) /* addl %n, %esp */
+ args = ((inst >> 16) & 0xff) / 4;
+ else
+ args = 5;
+ }
+ return (args);
+}
+
+/*
+ * Figure out the next frame up in the call stack.
+ * For trap(), we print the address of the faulting instruction and
+ * proceed with the calling frame. We return the ip that faulted.
+ * If the trap was caused by jumping through a bogus pointer, then
+ * the next line in the backtrace will list some random function as
+ * being called. It should get the argument list correct, though.
+ * It might be possible to dig out from the next frame up the name
+ * of the function that faulted, but that could get hairy.
+ */
+void
+db_nextframe(fp, ip, argp, is_trap)
+ struct i386_frame **fp; /* in/out */
+ db_addr_t *ip; /* out */
+ int *argp; /* in */
+ int is_trap; /* in */
+{
+ struct i386_saved_state *saved_regs;
+
+ switch (is_trap) {
+ case 0:
+ *ip = (db_addr_t)
+ db_get_value((int) &(*fp)->f_retaddr, 4, FALSE);
+ *fp = (struct i386_frame *)
+ db_get_value((int) &(*fp)->f_frame, 4, FALSE);
+ break;
+ case TRAP:
+ default:
+ /*
+ * We know that trap() has 1 argument and we know that
+ * it is an (int *).
+ */
+#if 0
+ saved_regs = (struct i386_saved_state *)
+ db_get_value((int)argp, 4, FALSE);
+#endif
+ saved_regs = (struct i386_saved_state *)argp;
+ db_printf("--- trap (number %d) ---\n",
+ saved_regs->tf_trapno & 0xffff);
+ db_printsym(saved_regs->tf_eip, DB_STGY_XTRN);
+ db_printf(":\n");
+ *fp = (struct i386_frame *)saved_regs->tf_ebp;
+ *ip = (db_addr_t)saved_regs->tf_eip;
+ break;
+
+ case SYSCALL: {
+ struct trapframe *saved_regs = (struct trapframe *)argp;
+
+ db_printf("--- syscall (number %d) ---\n", saved_regs->tf_eax);
+ db_printsym(saved_regs->tf_eip, DB_STGY_XTRN);
+ db_printf(":\n");
+ *fp = (struct i386_frame *)saved_regs->tf_ebp;
+ *ip = (db_addr_t)saved_regs->tf_eip;
+ }
+ break;
+ }
+}
+
+void
+db_stack_trace_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ boolean_t have_addr;
+ db_expr_t count;
+ char *modif;
+{
+ struct i386_frame *frame, *lastframe;
+ int *argp;
+ db_addr_t callpc;
+ int is_trap;
+ boolean_t kernel_only = TRUE;
+ boolean_t trace_thread = FALSE;
+
+#if 0
+ if (!db_trace_symbols_found)
+ db_find_trace_symbols();
+#endif
+
+ {
+ register char *cp = modif;
+ register char c;
+
+ while ((c = *cp++) != 0) {
+ if (c == 't')
+ trace_thread = TRUE;
+ if (c == 'u')
+ kernel_only = FALSE;
+ }
+ }
+
+ if (count == -1)
+ count = 65535;
+
+ if (!have_addr) {
+ frame = (struct i386_frame *)ddb_regs.tf_ebp;
+ callpc = (db_addr_t)ddb_regs.tf_eip;
+ }
+ else if (trace_thread) {
+ printf ("db_trace.c: can't trace thread\n");
+ }
+ else {
+ frame = (struct i386_frame *)addr;
+ callpc = (db_addr_t)db_get_value((int)&frame->f_retaddr, 4, FALSE);
+ }
+
+ lastframe = 0;
+ while (count-- && frame != 0) {
+ int narg;
+ char * name;
+ db_expr_t offset;
+ db_sym_t sym;
+#define MAXNARG 16
+ char *argnames[MAXNARG], **argnp = NULL;
+
+ sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
+ db_symbol_values(sym, &name, NULL);
+
+ if (lastframe == 0 && sym == NULL) {
+ /* Symbol not found, peek at code */
+ int instr = db_get_value(callpc, 4, FALSE);
+
+ offset = 1;
+ if ((instr & 0x00ffffff) == 0x00e58955 ||
+ /* enter: pushl %ebp, movl %esp, %ebp */
+ (instr & 0x0000ffff) == 0x0000e589
+ /* enter+1: movl %esp, %ebp */ ) {
+ offset = 0;
+ }
+ }
+#define STRCMP(s1,s2) ((s1) && (s2) && strcmp((s1), (s2)) == 0)
+ if (INKERNEL((int)frame) && STRCMP(name, "_trap")) {
+ narg = 1;
+ is_trap = TRAP;
+ }
+ else
+ if (INKERNEL((int)frame) && STRCMP(name, "_kdintr")) {
+ is_trap = INTERRUPT;
+ narg = 0;
+ }
+ else
+ if (INKERNEL((int)frame) && STRCMP(name, "_syscall")) {
+ is_trap = SYSCALL;
+ narg = 0;
+ }
+#undef STRCMP
+ else {
+ is_trap = 0;
+ narg = MAXNARG;
+ if (db_sym_numargs(sym, &narg, argnames)) {
+ argnp = argnames;
+ } else {
+ narg = db_numargs(frame);
+ }
+ }
+
+ db_printf("%s(", name);
+
+ if (lastframe == 0 && offset == 0 && !have_addr) {
+ /*
+ * We have a breakpoint before the frame is set up
+ * Use %esp instead
+ */
+ argp = &((struct i386_frame *)(ddb_regs.tf_esp-4))->f_arg0;
+ } else
+ argp = &frame->f_arg0;
+
+ while (narg) {
+ if (argnp)
+ db_printf("%s=", *argnp++);
+ db_printf("%x", db_get_value((int)argp, 4, FALSE));
+ argp++;
+ if (--narg != 0)
+ db_printf(",");
+ }
+ db_printf(") at ");
+ db_printsym(callpc, DB_STGY_PROC);
+ db_printf("\n");
+
+ if (lastframe == 0 && offset == 0 && !have_addr) {
+ /* Frame really belongs to next callpc */
+ lastframe = (struct i386_frame *)(ddb_regs.tf_esp-4);
+ callpc = (db_addr_t)db_get_value((int)&lastframe->f_retaddr, 4, FALSE);
+ continue;
+ }
+
+ lastframe = frame;
+ db_nextframe(&frame, &callpc, &frame->f_arg0, is_trap);
+
+ if (frame == 0) {
+ /* end of chain */
+ break;
+ }
+ if (INKERNEL((int)frame)) {
+ /* staying in kernel */
+ if (frame <= lastframe) {
+ db_printf("Bad frame pointer: 0x%x\n", frame);
+ break;
+ }
+ }
+ else if (INKERNEL((int)lastframe)) {
+ /* switch from user to kernel */
+ if (kernel_only)
+ break; /* kernel stack only */
+ }
+ else {
+ /* in user */
+ if (frame <= lastframe) {
+ db_printf("Bad user frame pointer: 0x%x\n", frame);
+ break;
+ }
+ }
+ }
+}
diff --git a/sys/i386/i386/dkbad.c b/sys/i386/i386/dkbad.c
new file mode 100644
index 0000000..2ebf05c
--- /dev/null
+++ b/sys/i386/i386/dkbad.c
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)dkbad.c 5.4 (Berkeley) 1/19/91
+ * $Id: dkbad.c,v 1.2 1993/10/16 14:14:59 rgrimes Exp $
+ */
+
+
+#ifndef NOBADSECT
+#include "sys/param.h"
+#include "systm.h"
+#include "sys/buf.h"
+#include "sys/dkbad.h"
+
+/*
+ * Search the bad sector table looking for
+ * the specified sector. Return index if found.
+ * Return -1 if not found.
+ */
+
+isbad(bt, cyl, trk, sec)
+ register struct dkbad *bt;
+{
+ register int i;
+ register long blk, bblk;
+
+ blk = ((long)cyl << 16) + (trk << 8) + sec;
+ for (i = 0; i < 126; i++) {
+ bblk = ((long)bt->bt_bad[i].bt_cyl << 16) + bt->bt_bad[i].bt_trksec;
+ if (blk == bblk)
+ return (i);
+ if (blk < bblk || bblk < 0)
+ break;
+ }
+ return (-1);
+}
+#endif
diff --git a/sys/i386/i386/exception.s b/sys/i386/i386/exception.s
new file mode 100644
index 0000000..30bc164
--- /dev/null
+++ b/sys/i386/i386/exception.s
@@ -0,0 +1,275 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: exception.s,v 1.2 1994/01/03 07:55:20 davidg Exp $
+ */
+
+#include "npx.h" /* NNPX */
+
+#include "assym.s" /* system defines */
+
+#include "errno.h" /* error return codes */
+
+#include "machine/spl.h" /* SWI_AST_MASK ... */
+
+#include "machine/psl.h" /* PSL_I */
+
+#include "machine/trap.h" /* trap codes */
+#include "syscall.h" /* syscall numbers */
+
+#include "machine/asmacros.h" /* miscellaneous macros */
+
+#define KDSEL 0x10 /* kernel data selector */
+#define SEL_RPL_MASK 0x0003
+#define TRAPF_CS_OFF (13 * 4)
+
+ .text
+
+/*****************************************************************************/
+/* Trap handling */
+/*****************************************************************************/
+/*
+ * Trap and fault vector routines
+ */
+#define IDTVEC(name) ALIGN_TEXT ; .globl _X/**/name ; _X/**/name:
+#define TRAP(a) pushl $(a) ; jmp _alltraps
+
+/*
+ * XXX - debugger traps are now interrupt gates so at least bdb doesn't lose
+ * control. The sti's give the standard losing behaviour for ddb and kgdb.
+ */
+#ifdef BDE_DEBUGGER
+#define BDBTRAP(name) \
+ ss ; \
+ cmpb $0,_bdb_exists ; \
+ je 1f ; \
+ testb $SEL_RPL_MASK,4(%esp) ; \
+ jne 1f ; \
+ ss ; \
+ .globl bdb_/**/name/**/_ljmp ; \
+bdb_/**/name/**/_ljmp: ; \
+ ljmp $0,$0 ; \
+1:
+#else
+#define BDBTRAP(name)
+#endif
+
+#ifdef KGDB
+# define BPTTRAP(a) testl $PSL_I,4+8(%esp) ; je 1f ; sti ; 1: ; \
+ pushl $(a) ; jmp _bpttraps
+#else
+# define BPTTRAP(a) testl $PSL_I,4+8(%esp) ; je 1f ; sti ; 1: ; TRAP(a)
+#endif
+
+MCOUNT_LABEL(user)
+MCOUNT_LABEL(btrap)
+
+IDTVEC(div)
+ pushl $0; TRAP(T_DIVIDE)
+IDTVEC(dbg)
+ BDBTRAP(dbg)
+ pushl $0; BPTTRAP(T_TRCTRAP)
+IDTVEC(nmi)
+ pushl $0; TRAP(T_NMI)
+IDTVEC(bpt)
+ BDBTRAP(bpt)
+ pushl $0; BPTTRAP(T_BPTFLT)
+IDTVEC(ofl)
+ pushl $0; TRAP(T_OFLOW)
+IDTVEC(bnd)
+ pushl $0; TRAP(T_BOUND)
+IDTVEC(ill)
+ pushl $0; TRAP(T_PRIVINFLT)
+IDTVEC(dna)
+ pushl $0; TRAP(T_DNA)
+IDTVEC(dble)
+ TRAP(T_DOUBLEFLT)
+IDTVEC(fpusegm)
+ pushl $0; TRAP(T_FPOPFLT)
+IDTVEC(tss)
+ TRAP(T_TSSFLT)
+IDTVEC(missing)
+ TRAP(T_SEGNPFLT)
+IDTVEC(stk)
+ TRAP(T_STKFLT)
+IDTVEC(prot)
+ TRAP(T_PROTFLT)
+IDTVEC(page)
+ TRAP(T_PAGEFLT)
+IDTVEC(rsvd)
+ pushl $0; TRAP(T_RESERVED)
+IDTVEC(fpu)
+#if NNPX > 0
+ /*
+ * Handle like an interrupt so that we can call npxintr to clear the
+ * error. It would be better to handle npx interrupts as traps but
+ * this is difficult for nested interrupts.
+ */
+ pushl $0 /* dumby error code */
+ pushl $0 /* dumby trap type */
+ pushal
+ pushl %ds
+ pushl %es /* now the stack frame is a trap frame */
+ movl $KDSEL,%eax
+ movl %ax,%ds
+ movl %ax,%es
+ FAKE_MCOUNT(12*4(%esp))
+ movl _cpl,%eax
+ pushl %eax
+ pushl $0 /* dummy unit to finish building intr frame */
+ incl _cnt+V_TRAP
+ orl $SWI_AST_MASK,%eax
+ movl %eax,_cpl
+ call _npxintr
+ MEXITCOUNT
+ jmp _doreti
+#else /* NNPX > 0 */
+ pushl $0; TRAP(T_ARITHTRAP)
+#endif /* NNPX > 0 */
+ /* 17 - 31 reserved for future exp */
+IDTVEC(rsvd0)
+ pushl $0; TRAP(17)
+IDTVEC(rsvd1)
+ pushl $0; TRAP(18)
+IDTVEC(rsvd2)
+ pushl $0; TRAP(19)
+IDTVEC(rsvd3)
+ pushl $0; TRAP(20)
+IDTVEC(rsvd4)
+ pushl $0; TRAP(21)
+IDTVEC(rsvd5)
+ pushl $0; TRAP(22)
+IDTVEC(rsvd6)
+ pushl $0; TRAP(23)
+IDTVEC(rsvd7)
+ pushl $0; TRAP(24)
+IDTVEC(rsvd8)
+ pushl $0; TRAP(25)
+IDTVEC(rsvd9)
+ pushl $0; TRAP(26)
+IDTVEC(rsvd10)
+ pushl $0; TRAP(27)
+IDTVEC(rsvd11)
+ pushl $0; TRAP(28)
+IDTVEC(rsvd12)
+ pushl $0; TRAP(29)
+IDTVEC(rsvd13)
+ pushl $0; TRAP(30)
+IDTVEC(rsvd14)
+ pushl $0; TRAP(31)
+
+ SUPERALIGN_TEXT
+_alltraps:
+ pushal
+ pushl %ds
+ pushl %es
+ movl $KDSEL,%eax
+ movl %ax,%ds
+ movl %ax,%es
+ FAKE_MCOUNT(12*4(%esp))
+calltrap:
+ FAKE_MCOUNT(_btrap) /* init "from" _btrap -> calltrap */
+ incl _cnt+V_TRAP
+ orl $SWI_AST_MASK,_cpl
+ call _trap
+ /*
+ * There was no place to save the cpl so we have to recover it
+ * indirectly. For traps from user mode it was 0, and for traps
+ * from kernel mode Oring SWI_AST_MASK into it didn't change it.
+ */
+ subl %eax,%eax
+ testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp)
+ jne 1f
+ movl _cpl,%eax
+1:
+ /*
+ * Return via _doreti to handle ASTs. Have to change trap frame
+ * to interrupt frame.
+ */
+ pushl %eax
+ subl $4,%esp
+ MEXITCOUNT
+ jmp _doreti
+
+#ifdef KGDB
+/*
+ * This code checks for a kgdb trap, then falls through
+ * to the regular trap code.
+ */
+ SUPERALIGN_TEXT
+_bpttraps:
+ pushal
+ pushl %ds
+ pushl %es
+ movl $KDSEL,%eax
+ movl %ax,%ds
+ movl %ax,%es
+ FAKE_MCOUNT(12*4(%esp))
+ testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp) /* non-kernel mode? */
+ jne calltrap /* yes */
+ call _kgdb_trap_glue
+ MEXITCOUNT
+ jmp calltrap
+#endif
+
+/*
+ * Call gate entry for syscall
+ */
+ SUPERALIGN_TEXT
+IDTVEC(syscall)
+ pushfl /* Room for tf_err */
+ pushfl /* Room for tf_trapno */
+ pushal
+ pushl %ds
+ pushl %es
+ movl $KDSEL,%eax /* switch to kernel segments */
+ movl %ax,%ds
+ movl %ax,%es
+ movl TF_ERR(%esp),%eax /* copy eflags from tf_err to fs_eflags */
+ movl %eax,TF_EFLAGS(%esp)
+ FAKE_MCOUNT(12*4(%esp))
+ incl _cnt+V_SYSCALL
+ movl $SWI_AST_MASK,_cpl
+ call _syscall
+ /*
+ * Return via _doreti to handle ASTs.
+ */
+ pushl $0 /* cpl to restore */
+ subl $4,%esp
+ MEXITCOUNT
+ jmp _doreti
+
+/*
+ * include generated interrupt vectors and ISA intr code
+ */
+#include "i386/isa/vector.s"
+#include "i386/isa/icu.s"
diff --git a/sys/i386/i386/genassym.c b/sys/i386/i386/genassym.c
new file mode 100644
index 0000000..a75d1f1
--- /dev/null
+++ b/sys/i386/i386/genassym.c
@@ -0,0 +1,192 @@
+/*-
+ * Copyright (c) 1982, 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)genassym.c 5.11 (Berkeley) 5/10/91
+ * $Id: genassym.c,v 1.6 1993/11/13 02:24:59 davidg Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/map.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/msgbuf.h>
+#include <machine/cpu.h>
+#include <machine/trap.h>
+#include <machine/psl.h>
+#include <machine/reg.h>
+#include <sys/syscall.h>
+#include <vm/vm.h>
+#include <sys/user.h>
+
+main()
+{
+ struct proc *p = (struct proc *)0;
+ struct vmmeter *vm = (struct vmmeter *)0;
+ struct user *up = (struct user *)0;
+ struct rusage *rup = (struct rusage *)0;
+ struct uprof *uprof = (struct uprof *)0;
+ struct vmspace *vms = (struct vmspace *)0;
+ vm_map_t map = (vm_map_t)0;
+ pmap_t pmap = (pmap_t)0;
+ struct pcb *pcb = (struct pcb *)0;
+ struct trapframe *tf = (struct trapframe *)0;
+ struct sigframe *sigf = (struct sigframe *)0;
+ register unsigned i;
+
+ printf("#define\tI386_CR3PAT %d\n", I386_CR3PAT);
+ printf("#define\tUDOT_SZ %d\n", sizeof(struct user));
+ printf("#define\tP_LINK %d\n", &p->p_forw);
+ printf("#define\tP_RLINK %d\n", &p->p_back);
+ printf("#define\tP_VMSPACE %d\n", &p->p_vmspace);
+ printf("#define\tVM_PMAP %d\n", &vms->vm_pmap);
+ printf("#define\tP_ADDR %d\n", &p->p_addr);
+ printf("#define\tP_PRI %d\n", &p->p_priority);
+ printf("#define\tP_STAT %d\n", &p->p_stat);
+ printf("#define\tP_WCHAN %d\n", &p->p_wchan);
+ printf("#define\tP_FLAG %d\n", &p->p_flag);
+ printf("#define\tP_PID %d\n", &p->p_pid);
+ printf("#define\tSSLEEP %d\n", SSLEEP);
+ printf("#define\tSRUN %d\n", SRUN);
+ printf("#define\tV_SWTCH %d\n", &vm->v_swtch);
+ printf("#define\tV_TRAP %d\n", &vm->v_trap);
+ printf("#define\tV_SYSCALL %d\n", &vm->v_syscall);
+ printf("#define\tV_INTR %d\n", &vm->v_intr);
+ printf("#define\tV_SOFT %d\n", &vm->v_soft);
+/* printf("#define\tV_PDMA %d\n", &vm->v_pdma); */
+ printf("#define\tV_FAULTS %d\n", &vm->v_faults);
+/* printf("#define\tV_PGREC %d\n", &vm->v_pgrec); */
+/* printf("#define\tV_FASTPGREC %d\n", &vm->v_fastpgrec); */
+ printf("#define\tUPAGES %d\n", UPAGES);
+ printf("#define\tHIGHPAGES %d\n", HIGHPAGES);
+ printf("#define\tCLSIZE %d\n", CLSIZE);
+ printf("#define\tNBPG %d\n", NBPG);
+ printf("#define\tNPTEPG %d\n", NPTEPG);
+ printf("#define\tPDESIZE %d\n", PDESIZE);
+ printf("#define\tPTESIZE %d\n", PTESIZE);
+ printf("#define\tNKPDE %d\n", NKPDE);
+ printf("#define\tNKPT %d\n", NKPT);
+ printf("#define\tKPTDI 0x%x\n", KPTDI);
+ printf("#define\tKSTKPTDI 0x%x\n", KSTKPTDI);
+ printf("#define\tKSTKPTEOFF 0x%x\n", KSTKPTEOFF);
+ printf("#define\tPTDPTDI 0x%x\n", PTDPTDI);
+ printf("#define\tAPTDPTDI 0x%x\n", APTDPTDI);
+ printf("#define\tPGSHIFT %d\n", PGSHIFT);
+ printf("#define\tPDRSHIFT %d\n", PDRSHIFT);
+ printf("#define\tSYSPTSIZE %d\n", SYSPTSIZE);
+ printf("#define\tUSRPTSIZE %d\n", USRPTSIZE);
+ printf("#define\tUSRIOSIZE %d\n", USRIOSIZE);
+#ifdef SYSVSHM
+ printf("#define\tSHMMAXPGS %d\n", SHMMAXPGS);
+#endif
+ printf("#define\tUSRSTACK 0x%x\n", USRSTACK);
+ printf("#define\tVM_MAXUSER_ADDRESS 0x%x\n", VM_MAXUSER_ADDRESS);
+ printf("#define\tKERNBASE 0x%x\n", KERNBASE);
+ printf("#define\tMSGBUFPTECNT %d\n", btoc(sizeof (struct msgbuf)));
+ printf("#define\tNMBCLUSTERS %d\n", NMBCLUSTERS);
+ printf("#define\tMCLBYTES %d\n", MCLBYTES);
+ printf("#define\tPCB_LINK %d\n", &pcb->pcb_tss.tss_link);
+ printf("#define\tPCB_ESP0 %d\n", &pcb->pcb_tss.tss_esp0);
+ printf("#define\tPCB_SS0 %d\n", &pcb->pcb_tss.tss_ss0);
+ printf("#define\tPCB_ESP1 %d\n", &pcb->pcb_tss.tss_esp1);
+ printf("#define\tPCB_SS1 %d\n", &pcb->pcb_tss.tss_ss1);
+ printf("#define\tPCB_ESP2 %d\n", &pcb->pcb_tss.tss_esp2);
+ printf("#define\tPCB_SS2 %d\n", &pcb->pcb_tss.tss_ss2);
+ printf("#define\tPCB_CR3 %d\n", &pcb->pcb_tss.tss_cr3);
+ printf("#define\tPCB_EIP %d\n", &pcb->pcb_tss.tss_eip);
+ printf("#define\tPCB_EFLAGS %d\n", &pcb->pcb_tss.tss_eflags);
+ printf("#define\tPCB_EAX %d\n", &pcb->pcb_tss.tss_eax);
+ printf("#define\tPCB_ECX %d\n", &pcb->pcb_tss.tss_ecx);
+ printf("#define\tPCB_EDX %d\n", &pcb->pcb_tss.tss_edx);
+ printf("#define\tPCB_EBX %d\n", &pcb->pcb_tss.tss_ebx);
+ printf("#define\tPCB_ESP %d\n", &pcb->pcb_tss.tss_esp);
+ printf("#define\tPCB_EBP %d\n", &pcb->pcb_tss.tss_ebp);
+ printf("#define\tPCB_ESI %d\n", &pcb->pcb_tss.tss_esi);
+ printf("#define\tPCB_EDI %d\n", &pcb->pcb_tss.tss_edi);
+ printf("#define\tPCB_ES %d\n", &pcb->pcb_tss.tss_es);
+ printf("#define\tPCB_CS %d\n", &pcb->pcb_tss.tss_cs);
+ printf("#define\tPCB_SS %d\n", &pcb->pcb_tss.tss_ss);
+ printf("#define\tPCB_DS %d\n", &pcb->pcb_tss.tss_ds);
+ printf("#define\tPCB_FS %d\n", &pcb->pcb_tss.tss_fs);
+ printf("#define\tPCB_GS %d\n", &pcb->pcb_tss.tss_gs);
+ printf("#define\tPCB_LDT %d\n", &pcb->pcb_tss.tss_ldt);
+ printf("#define\tPCB_USERLDT %d\n", &pcb->pcb_ldt);
+ printf("#define\tPCB_IOOPT %d\n", &pcb->pcb_tss.tss_ioopt);
+ printf("#define\tU_PROF %d\n", &up->u_stats.p_prof);
+ printf("#define\tU_PROFSCALE %d\n", &up->u_stats.p_prof.pr_scale);
+ printf("#define\tPR_BASE %d\n", &uprof->pr_base);
+ printf("#define\tPR_SIZE %d\n", &uprof->pr_size);
+ printf("#define\tPR_OFF %d\n", &uprof->pr_off);
+ printf("#define\tPR_SCALE %d\n", &uprof->pr_scale);
+ printf("#define\tRU_MINFLT %d\n", &rup->ru_minflt);
+ printf("#define\tPCB_FLAGS %d\n", &pcb->pcb_flags);
+ printf("#define\tPCB_SAVEFPU %d\n", &pcb->pcb_savefpu);
+ printf("#define\tFP_USESEMC %d\n", FP_USESEMC);
+ printf("#define\tPCB_SAVEEMC %d\n", &pcb->pcb_saveemc);
+ printf("#define\tPCB_CMAP2 %d\n", &pcb->pcb_cmap2);
+ printf("#define\tPCB_IML %d\n", &pcb->pcb_iml);
+ printf("#define\tPCB_ONFAULT %d\n", &pcb->pcb_onfault);
+
+ printf("#define\tTF_ES %d\n", &tf->tf_es);
+ printf("#define\tTF_DS %d\n", &tf->tf_ds);
+ printf("#define\tTF_EDI %d\n", &tf->tf_edi);
+ printf("#define\tTF_ESI %d\n", &tf->tf_esi);
+ printf("#define\tTF_EBP %d\n", &tf->tf_ebp);
+ printf("#define\tTF_ISP %d\n", &tf->tf_isp);
+ printf("#define\tTF_EBX %d\n", &tf->tf_ebx);
+ printf("#define\tTF_EDX %d\n", &tf->tf_edx);
+ printf("#define\tTF_ECX %d\n", &tf->tf_ecx);
+ printf("#define\tTF_EAX %d\n", &tf->tf_eax);
+ printf("#define\tTF_TRAPNO %d\n", &tf->tf_trapno);
+ printf("#define\tTF_ERR %d\n", &tf->tf_err);
+ printf("#define\tTF_EIP %d\n", &tf->tf_eip);
+ printf("#define\tTF_CS %d\n", &tf->tf_cs);
+ printf("#define\tTF_EFLAGS %d\n", &tf->tf_eflags);
+ printf("#define\tTF_ESP %d\n", &tf->tf_esp);
+ printf("#define\tTF_SS %d\n", &tf->tf_ss);
+
+ printf("#define\tSIGF_SIGNUM %d\n", &sigf->sf_signum);
+ printf("#define\tSIGF_CODE %d\n", &sigf->sf_code);
+ printf("#define\tSIGF_SCP %d\n", &sigf->sf_scp);
+ printf("#define\tSIGF_HANDLER %d\n", &sigf->sf_handler);
+ printf("#define\tSIGF_SC %d\n", &sigf->sf_sc);
+
+ printf("#define\tB_READ %d\n", B_READ);
+ printf("#define\tENOENT %d\n", ENOENT);
+ printf("#define\tEFAULT %d\n", EFAULT);
+ printf("#define\tENAMETOOLONG %d\n", ENAMETOOLONG);
+ exit(0);
+}
+
diff --git a/sys/i386/i386/in_cksum.c b/sys/i386/i386/in_cksum.c
new file mode 100644
index 0000000..b11f0ff
--- /dev/null
+++ b/sys/i386/i386/in_cksum.c
@@ -0,0 +1,237 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from tahoe: in_cksum.c 1.2 86/01/05
+ * from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91
+ * $Id: in_cksum.c,v 1.4 1993/12/19 00:50:02 wollman Exp $
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "sys/mbuf.h"
+
+/*
+ * Checksum routine for Internet Protocol family headers.
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ *
+ * This implementation is 386 version.
+ */
+
+#undef ADDCARRY
+#define ADDCARRY(x) if ((x) > 0xffff) (x) -= 0xffff
+#define REDUCE {sum = (sum & 0xffff) + (sum >> 16); ADDCARRY(sum);}
+
+/*
+ * Thanks to gcc we don't have to guess
+ * which registers contain sum & w.
+ */
+#define ADD(n) asm("addl " #n "(%2), %0" : "=r" (sum) : "0" (sum), "r" (w))
+#define ADDC(n) asm("adcl " #n "(%2), %0" : "=r" (sum) : "0" (sum), "r" (w))
+#define LOAD(n) asm volatile("movb " #n "(%1), %0" : "=r" (junk) : "r" (w))
+#define MOP asm("adcl $0, %0" : "=r" (sum) : "0" (sum))
+
+int
+in_cksum(m, len)
+ register struct mbuf *m;
+ register int len;
+{
+ register u_short *w;
+ register unsigned sum = 0;
+ register int mlen = 0;
+ int byte_swapped = 0;
+ union { char c[2]; u_short s; } su;
+
+ for (;m && len; m = m->m_next) {
+ if (m->m_len == 0)
+ continue;
+ w = mtod(m, u_short *);
+ if (mlen == -1) {
+ /*
+ * The first byte of this mbuf is the continuation
+ * of a word spanning between this mbuf and the
+ * last mbuf.
+ */
+
+ /* su.c[0] is already saved when scanning previous
+ * mbuf. sum was REDUCEd when we found mlen == -1
+ */
+ su.c[1] = *(u_char *)w;
+ sum += su.s;
+ w = (u_short *)((char *)w + 1);
+ mlen = m->m_len - 1;
+ len--;
+ } else
+ mlen = m->m_len;
+ if (len < mlen)
+ mlen = len;
+ len -= mlen;
+ /*
+ * Force to long boundary so we do longword aligned
+ * memory operations
+ */
+ if (3 & (int) w) {
+ REDUCE;
+ if ((1 & (int) w) && (mlen > 0)) {
+ sum <<= 8;
+ su.c[0] = *(char *)w;
+ w = (u_short *)((char *)w + 1);
+ mlen--;
+ byte_swapped = 1;
+ }
+ if ((2 & (int) w) && (mlen >= 2)) {
+ sum += *w++;
+ mlen -= 2;
+ }
+ }
+ /*
+ * Advance to a 486 cache line boundary.
+ */
+ if (4 & (int) w && mlen >= 4) {
+ ADD(0);
+ MOP;
+ w += 2;
+ mlen -= 4;
+ }
+ if (8 & (int) w && mlen >= 8) {
+ ADD(0);
+ ADDC(4);
+ MOP;
+ w += 4;
+ mlen -= 8;
+ }
+ /*
+ * Do as much of the checksum as possible 32 bits at at time.
+ * In fact, this loop is unrolled to make overhead from
+ * branches &c small.
+ */
+ mlen -= 1;
+ while ((mlen -= 32) >= 0) {
+ u_char junk;
+ /*
+ * Add with carry 16 words and fold in the last
+ * carry by adding a 0 with carry.
+ *
+ * The early ADD(16) and the LOAD(32) are to load
+ * the next 2 cache lines in advance on 486's. The
+ * 486 has a penalty of 2 clock cycles for loading
+ * a cache line, plus whatever time the external
+ * memory takes to load the first word(s) addressed.
+ * These penalties are unavoidable. Subsequent
+ * accesses to a cache line being loaded (and to
+ * other external memory?) are delayed until the
+ * whole load finishes. These penalties are mostly
+ * avoided by not accessing external memory for
+ * 8 cycles after the ADD(16) and 12 cycles after
+ * the LOAD(32). The loop terminates when mlen
+ * is initially 33 (not 32) to guaranteed that
+ * the LOAD(32) is within bounds.
+ */
+ ADD(16);
+ ADDC(0);
+ ADDC(4);
+ ADDC(8);
+ ADDC(12);
+ LOAD(32);
+ ADDC(20);
+ ADDC(24);
+ ADDC(28);
+ MOP;
+ w += 16;
+ }
+ mlen += 32 + 1;
+ if (mlen >= 32) {
+ ADD(16);
+ ADDC(0);
+ ADDC(4);
+ ADDC(8);
+ ADDC(12);
+ ADDC(20);
+ ADDC(24);
+ ADDC(28);
+ MOP;
+ w += 16;
+ mlen -= 32;
+ }
+ if (mlen >= 16) {
+ ADD(0);
+ ADDC(4);
+ ADDC(8);
+ ADDC(12);
+ MOP;
+ w += 8;
+ mlen -= 16;
+ }
+ if (mlen >= 8) {
+ ADD(0);
+ ADDC(4);
+ MOP;
+ w += 4;
+ mlen -= 8;
+ }
+ if (mlen == 0 && byte_swapped == 0)
+ continue; /* worth 1% maybe ?? */
+ REDUCE;
+ while ((mlen -= 2) >= 0) {
+ sum += *w++;
+ }
+ if (byte_swapped) {
+ sum <<= 8;
+ byte_swapped = 0;
+ if (mlen == -1) {
+ su.c[1] = *(char *)w;
+ sum += su.s;
+ mlen = 0;
+ } else
+ mlen = -1;
+ } else if (mlen == -1)
+ /*
+ * This mbuf has odd number of bytes.
+ * There could be a word split betwen
+ * this mbuf and the next mbuf.
+ * Save the last byte (to prepend to next mbuf).
+ */
+ su.c[0] = *(char *)w;
+ }
+
+ if (len)
+ printf("cksum: out of data\n");
+ if (mlen == -1) {
+ /* The last mbuf has odd # of bytes. Follow the
+ standard (the odd byte is shifted left by 8 bits) */
+ su.c[1] = 0;
+ sum += su.s;
+ }
+ REDUCE;
+ return (~sum & 0xffff);
+}
diff --git a/sys/i386/i386/locore.s b/sys/i386/i386/locore.s
new file mode 100644
index 0000000..538c690
--- /dev/null
+++ b/sys/i386/i386/locore.s
@@ -0,0 +1,518 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)locore.s 7.3 (Berkeley) 5/13/91
+ * $Id: locore.s,v 1.15 1994/02/01 04:08:54 davidg Exp $
+ */
+
+/*
+ * locore.s: FreeBSD machine support for the Intel 386
+ * originally from: locore.s, by William F. Jolitz
+ *
+ * Substantially rewritten by David Greenman, Rod Grimes,
+ * Bruce Evans, Wolfgang Solfrank, and many others.
+ */
+
+#include "npx.h" /* for NNPX */
+#include "assym.s" /* system definitions */
+#include "machine/psl.h" /* processor status longword defs */
+#include "machine/pte.h" /* page table entry definitions */
+#include "errno.h" /* error return codes */
+#include "machine/specialreg.h" /* x86 special registers */
+#include "machine/cputypes.h" /* x86 cpu type definitions */
+#include "syscall.h" /* system call numbers */
+#include "machine/asmacros.h" /* miscellaneous asm macros */
+
+/*
+ * XXX
+ *
+ * Note: This version greatly munged to avoid various assembler errors
+ * that may be fixed in newer versions of gas. Perhaps newer versions
+ * will have more pleasant appearance.
+ */
+
+/*
+ * PTmap is recursive pagemap at top of virtual address space.
+ * Within PTmap, the page directory can be found (third indirection).
+ */
+ .globl _PTmap,_PTD,_PTDpde,_Sysmap
+ .set _PTmap,PTDPTDI << PDRSHIFT
+ .set _PTD,_PTmap + (PTDPTDI * NBPG)
+ .set _PTDpde,_PTD + (PTDPTDI * PDESIZE)
+
+/* Sysmap is the base address of the kernel page tables */
+ .set _Sysmap,_PTmap + (KPTDI * NBPG)
+
+/*
+ * APTmap, APTD is the alternate recursive pagemap.
+ * It's used when modifying another process's page tables.
+ */
+ .globl _APTmap,_APTD,_APTDpde
+ .set _APTmap,APTDPTDI << PDRSHIFT
+ .set _APTD,_APTmap + (APTDPTDI * NBPG)
+ .set _APTDpde,_PTD + (APTDPTDI * PDESIZE)
+
+/*
+ * Access to each processes kernel stack is via a region of
+ * per-process address space (at the beginning), immediatly above
+ * the user process stack.
+ */
+ .set _kstack,USRSTACK
+ .globl _kstack
+
+/*
+ * Globals
+ */
+ .data
+ .globl _esym
+_esym: .long 0 /* ptr to end of syms */
+
+ .globl _boothowto,_bootdev,_curpcb
+
+ .globl _cpu,_cold,_atdevbase
+_cpu: .long 0 /* are we 386, 386sx, or 486 */
+_cold: .long 1 /* cold till we are not */
+_atdevbase: .long 0 /* location of start of iomem in virtual */
+_atdevphys: .long 0 /* location of device mapping ptes (phys) */
+
+ .globl _KERNend
+_KERNend: .long 0 /* phys addr end of kernel (just after bss) */
+
+ .globl _IdlePTD,_KPTphys
+_IdlePTD: .long 0 /* phys addr of kernel PTD */
+_KPTphys: .long 0 /* phys addr of kernel page tables */
+
+ .globl _cyloffset
+_cyloffset: .long 0 /* cylinder offset from boot blocks */
+
+ .globl _proc0paddr
+_proc0paddr: .long 0 /* address of proc 0 address space */
+
+#ifdef BDE_DEBUGGER
+ .globl _bdb_exists /* flag to indicate BDE debugger is available */
+_bdb_exists: .long 0
+#endif
+
+ .globl tmpstk
+ .space 0x1000
+tmpstk:
+
+
+/*
+ * System Initialization
+ */
+ .text
+
+/*
+ * btext: beginning of text section.
+ * Also the entry point (jumped to directly from the boot blocks).
+ */
+NON_GPROF_ENTRY(btext)
+ movw $0x1234,0x472 /* warm boot */
+ jmp 1f
+ .org 0x500 /* space for BIOS variables */
+
+ /*
+ * pass parameters on stack (howto, bootdev, unit, cyloffset, esym)
+ * note: (%esp) is return address of boot
+ * ( if we want to hold onto /boot, it's physical %esp up to _end)
+ */
+
+ 1: movl 4(%esp),%eax
+ movl %eax,_boothowto-KERNBASE
+ movl 8(%esp),%eax
+ movl %eax,_bootdev-KERNBASE
+ movl 12(%esp),%eax
+ movl %eax,_cyloffset-KERNBASE
+ movl 16(%esp),%eax
+ addl $KERNBASE,%eax
+ movl %eax,_esym-KERNBASE
+#ifdef DISKLESS /* Copy diskless structure */
+ movl _nfs_diskless_size-KERNBASE,%ecx
+ movl 20(%esp),%esi
+ movl $(_nfs_diskless-KERNBASE),%edi
+ cld
+ rep
+ movsb
+#endif
+
+ /* find out our CPU type. */
+ pushfl
+ popl %eax
+ movl %eax,%ecx
+ xorl $0x40000,%eax
+ pushl %eax
+ popfl
+ pushfl
+ popl %eax
+ xorl %ecx,%eax
+ shrl $18,%eax
+ andl $1,%eax
+ push %ecx
+ popfl
+
+ cmpl $0,%eax
+ jne 1f
+ movl $CPU_386,_cpu-KERNBASE
+ jmp 2f
+1: movl $CPU_486,_cpu-KERNBASE
+2:
+
+ /*
+ * Finished with old stack; load new %esp now instead of later so
+ * we can trace this code without having to worry about the trace
+ * trap clobbering the memory test or the zeroing of the bss+bootstrap
+ * page tables.
+ *
+ * XXX - wdboot clears the bss after testing that this is safe.
+ * This is too wasteful - memory below 640K is scarce. The boot
+ * program should check:
+ * text+data <= &stack_variable - more_space_for_stack
+ * text+data+bss+pad+space_for_page_tables <= end_of_memory
+ * Oops, the gdt is in the carcass of the boot program so clearing
+ * the rest of memory is still not possible.
+ */
+ movl $tmpstk-KERNBASE,%esp /* bootstrap stack end location */
+
+/*
+ * Virtual address space of kernel:
+ *
+ * text | data | bss | [syms] | page dir | proc0 kernel stack | usr stk map | Sysmap
+ * pages: 1 UPAGES (2) 1 NKPT (7)
+ */
+
+/* find end of kernel image */
+ movl $_end-KERNBASE,%ecx
+ addl $NBPG-1,%ecx /* page align up */
+ andl $~(NBPG-1),%ecx
+ movl %ecx,%esi /* esi = start of free memory */
+ movl %ecx,_KERNend-KERNBASE /* save end of kernel */
+
+/* clear bss */
+ movl $_edata-KERNBASE,%edi
+ subl %edi,%ecx /* get amount to clear */
+ xorl %eax,%eax /* specify zero fill */
+ cld
+ rep
+ stosb
+
+/*
+ * The value in esi is both the end of the kernel bss and a pointer to
+ * the kernel page directory, and is used by the rest of locore to build
+ * the tables.
+ * esi + 1(page dir) + 2(UPAGES) + 1(p0stack) + NKPT(number of kernel
+ * page table pages) is then passed on the stack to init386(first) as
+ * the value first. esi should ALWAYS be page aligned!!
+ */
+ movl %esi,%ecx /* Get current first availiable address */
+
+/* clear pagetables, page directory, stack, etc... */
+ movl %esi,%edi /* base (page directory) */
+ movl $((1+UPAGES+1+NKPT)*NBPG),%ecx /* amount to clear */
+ xorl %eax,%eax /* specify zero fill */
+ cld
+ rep
+ stosb
+
+/* physical address of Idle proc/kernel page directory */
+ movl %esi,_IdlePTD-KERNBASE
+
+/*
+ * fillkpt
+ * eax = (page frame address | control | status) == pte
+ * ebx = address of page table
+ * ecx = how many pages to map
+ */
+#define fillkpt \
+1: movl %eax,(%ebx) ; \
+ addl $NBPG,%eax ; /* increment physical address */ \
+ addl $4,%ebx ; /* next pte */ \
+ loop 1b ;
+
+/*
+ * Map Kernel
+ *
+ * First step - build page tables
+ */
+#if defined (KGDB) || defined (BDE_DEBUGGER)
+ movl _KERNend-KERNBASE,%ecx /* this much memory, */
+ shrl $PGSHIFT,%ecx /* for this many PTEs */
+#ifdef BDE_DEBUGGER
+ cmpl $0xa0,%ecx /* XXX - cover debugger pages */
+ jae 1f
+ movl $0xa0,%ecx
+1:
+#endif /* BDE_DEBUGGER */
+ movl $PG_V|PG_KW|PG_NC_PWT,%eax /* kernel R/W, valid, cache write-through */
+ lea ((1+UPAGES+1)*NBPG)(%esi),%ebx /* phys addr of kernel PT base */
+ movl %ebx,_KPTphys-KERNBASE /* save in global */
+ fillkpt
+
+#else /* !KGDB && !BDE_DEBUGGER */
+ /* write protect kernel text (doesn't do a thing for 386's - only 486's) */
+ movl $_etext-KERNBASE,%ecx /* get size of text */
+ shrl $PGSHIFT,%ecx /* for this many PTEs */
+ movl $PG_V|PG_KR,%eax /* specify read only */
+ lea ((1+UPAGES+1)*NBPG)(%esi),%ebx /* phys addr of kernel PT base */
+ movl %ebx,_KPTphys-KERNBASE /* save in global */
+ fillkpt
+
+ /* data and bss are r/w */
+ andl $PG_FRAME,%eax /* strip to just addr of bss */
+ movl _KERNend-KERNBASE,%ecx /* calculate size */
+ subl %eax,%ecx
+ shrl $PGSHIFT,%ecx
+ orl $PG_V|PG_KW,%eax /* valid, kernel read/write */
+ fillkpt
+#endif /* KGDB || BDE_DEBUGGER */
+
+/* now initialize the page dir, upages, p0stack PT, and page tables */
+
+ movl $(1+UPAGES+1+NKPT),%ecx /* number of PTEs */
+ movl %esi,%eax /* phys address of PTD */
+ andl $PG_FRAME,%eax /* convert to PFN, should be a NOP */
+ orl $PG_V|PG_KW|PG_NC_PWT,%eax /* valid, kernel read/write, cache write-though */
+ movl %esi,%ebx /* calculate pte offset to ptd */
+ shrl $PGSHIFT-2,%ebx
+ addl %esi,%ebx /* address of page directory */
+ addl $((1+UPAGES+1)*NBPG),%ebx /* offset to kernel page tables */
+ fillkpt
+
+/* map I/O memory map */
+
+ movl _KPTphys-KERNBASE,%ebx /* base of kernel page tables */
+ lea (0xa0 * PTESIZE)(%ebx),%ebx /* hardwire ISA hole at KERNBASE + 0xa0000 */
+ movl $0x100-0xa0,%ecx /* for this many pte s, */
+ movl $(0xa0000|PG_V|PG_KW|PG_N),%eax /* valid, kernel read/write, non-cacheable */
+ movl %ebx,_atdevphys-KERNBASE /* save phys addr of ptes */
+ fillkpt
+
+ /* map proc 0's kernel stack into user page table page */
+
+ movl $UPAGES,%ecx /* for this many pte s, */
+ lea (1*NBPG)(%esi),%eax /* physical address in proc 0 */
+ lea (KERNBASE)(%eax),%edx /* change into virtual addr */
+ movl %edx,_proc0paddr-KERNBASE /* save VA for proc 0 init */
+ orl $PG_V|PG_KW,%eax /* valid, kernel read/write */
+ lea ((1+UPAGES)*NBPG)(%esi),%ebx /* addr of stack page table in proc 0 */
+ addl $(KSTKPTEOFF * PTESIZE),%ebx /* offset to kernel stack PTE */
+ fillkpt
+
+/*
+ * Initialize kernel page table directory
+ */
+ /* install a pde for temporary double map of bottom of VA */
+ movl _KPTphys-KERNBASE,%eax
+ orl $PG_V|PG_KW,%eax /* valid, kernel read/write */
+ movl %eax,(%esi) /* which is where temp maps! */
+
+ /* initialize kernel pde's */
+ movl $(NKPT),%ecx /* for this many PDEs */
+ lea (KPTDI*PDESIZE)(%esi),%ebx /* offset of pde for kernel */
+ fillkpt
+
+ /* install a pde recursively mapping page directory as a page table! */
+ movl %esi,%eax /* phys address of ptd in proc 0 */
+ orl $PG_V|PG_KW,%eax /* pde entry is valid */
+ movl %eax,PTDPTDI*PDESIZE(%esi) /* which is where PTmap maps! */
+
+ /* install a pde to map kernel stack for proc 0 */
+ lea ((1+UPAGES)*NBPG)(%esi),%eax /* physical address of pt in proc 0 */
+ orl $PG_V|PG_KW,%eax /* pde entry is valid */
+ movl %eax,KSTKPTDI*PDESIZE(%esi) /* which is where kernel stack maps! */
+
+#ifdef BDE_DEBUGGER
+ /* copy and convert stuff from old gdt and idt for debugger */
+
+ cmpl $0x0375c339,0x96104 /* XXX - debugger signature */
+ jne 1f
+ movb $1,_bdb_exists-KERNBASE
+1:
+ pushal
+ subl $2*6,%esp
+
+ sgdt (%esp)
+ movl 2(%esp),%esi /* base address of current gdt */
+ movl $_gdt-KERNBASE,%edi
+ movl %edi,2(%esp)
+ movl $8*18/4,%ecx
+ cld
+ rep /* copy gdt */
+ movsl
+ movl $_gdt-KERNBASE,-8+2(%edi) /* adjust gdt self-ptr */
+ movb $0x92,-8+5(%edi)
+
+ sidt 6(%esp)
+ movl 6+2(%esp),%esi /* base address of current idt */
+ movl 8+4(%esi),%eax /* convert dbg descriptor to ... */
+ movw 8(%esi),%ax
+ movl %eax,bdb_dbg_ljmp+1-KERNBASE /* ... immediate offset ... */
+ movl 8+2(%esi),%eax
+ movw %ax,bdb_dbg_ljmp+5-KERNBASE /* ... and selector for ljmp */
+ movl 24+4(%esi),%eax /* same for bpt descriptor */
+ movw 24(%esi),%ax
+ movl %eax,bdb_bpt_ljmp+1-KERNBASE
+ movl 24+2(%esi),%eax
+ movw %ax,bdb_bpt_ljmp+5-KERNBASE
+
+ movl $_idt-KERNBASE,%edi
+ movl %edi,6+2(%esp)
+ movl $8*4/4,%ecx
+ cld
+ rep /* copy idt */
+ movsl
+
+ lgdt (%esp)
+ lidt 6(%esp)
+
+ addl $2*6,%esp
+ popal
+#endif /* BDE_DEBUGGER */
+
+ /* load base of page directory and enable mapping */
+ movl %esi,%eax /* phys address of ptd in proc 0 */
+ orl $I386_CR3PAT,%eax
+ movl %eax,%cr3 /* load ptd addr into mmu */
+ movl %cr0,%eax /* get control word */
+ orl $CR0_PE|CR0_PG,%eax /* enable paging */
+ movl %eax,%cr0 /* and let's page NOW! */
+
+ pushl $begin /* jump to high mem */
+ ret
+
+begin: /* now running relocated at KERNBASE where the system is linked to run */
+
+ .globl _Crtat /* XXX - locore should not know about */
+ movl _Crtat,%eax /* variables of device drivers (pccons)! */
+ subl $(KERNBASE+0xA0000),%eax
+ movl _atdevphys,%edx /* get pte PA */
+ subl _KPTphys,%edx /* remove base of ptes, now have phys offset */
+ shll $PGSHIFT-2,%edx /* corresponding to virt offset */
+ addl $KERNBASE,%edx /* add virtual base */
+ movl %edx,_atdevbase
+ addl %eax,%edx
+ movl %edx,_Crtat
+
+ /* set up bootstrap stack - 48 bytes */
+ movl $_kstack+UPAGES*NBPG-4*12,%esp /* bootstrap stack end location */
+ xorl %eax,%eax /* mark end of frames */
+ movl %eax,%ebp
+ movl _proc0paddr,%eax
+ movl %esi,PCB_CR3(%eax)
+
+#ifdef BDE_DEBUGGER
+ /* relocate debugger gdt entries */
+
+ movl $_gdt+8*9,%eax /* adjust slots 9-17 */
+ movl $9,%ecx
+reloc_gdt:
+ movb $KERNBASE>>24,7(%eax) /* top byte of base addresses, was 0, */
+ addl $8,%eax /* now KERNBASE>>24 */
+ loop reloc_gdt
+
+ cmpl $0,_bdb_exists
+ je 1f
+ int $3
+1:
+#endif /* BDE_DEBUGGER */
+
+ /*
+ * Skip over the page tables and the kernel stack
+ */
+ lea ((1+UPAGES+1+NKPT)*NBPG)(%esi),%esi
+
+ pushl %esi /* value of first for init386(first) */
+ call _init386 /* wire 386 chip for unix operation */
+ popl %esi
+
+#if 0
+ movl $0,_PTD
+#endif
+
+ .globl __ucodesel,__udatasel
+
+ pushl $0 /* unused */
+ pushl __udatasel /* ss */
+ pushl $0 /* esp - filled in by execve() */
+ pushl $0x3200 /* eflags (ring 3, int enab) */
+ pushl __ucodesel /* cs */
+ pushl $0 /* eip - filled in by execve() */
+ subl $(12*4),%esp /* space for rest of registers */
+
+ pushl %esp /* call main with frame pointer */
+ call _main /* autoconfiguration, mountroot etc */
+
+ addl $(13*4),%esp /* back to a frame we can return with */
+
+ /*
+ * now we've run main() and determined what cpu-type we are, we can
+ * enable WP mode on i486 cpus and above.
+ */
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ je 1f
+ movl %cr0,%eax /* get control word */
+ orl $CR0_WP,%eax /* enable write protect for all modes */
+ movl %eax,%cr0 /* and do it */
+#endif
+ /*
+ * on return from main(), we are process 1
+ * set up address space and stack so that we can 'return' to user mode
+ */
+1:
+ movl __ucodesel,%eax
+ movl __udatasel,%ecx
+
+ movl %cx,%ds
+ movl %cx,%es
+ movl %ax,%fs /* double map cs to fs */
+ movl %cx,%gs /* and ds to gs */
+ iret /* goto user! */
+
+#define LCALL(x,y) .byte 0x9a ; .long y ; .word x
+
+NON_GPROF_ENTRY(sigcode)
+ call SIGF_HANDLER(%esp)
+ lea SIGF_SC(%esp),%eax /* scp (the call may have clobbered the */
+ /* copy at 8(%esp)) */
+ pushl %eax
+ pushl %eax /* junk to fake return address */
+ movl $103,%eax /* XXX sigreturn() */
+ LCALL(0x7,0) /* enter kernel with args on stack */
+ hlt /* never gets here */
+
+ .globl _szsigcode
+_szsigcode:
+ .long _szsigcode-_sigcode
diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c
new file mode 100644
index 0000000..70f65bf
--- /dev/null
+++ b/sys/i386/i386/machdep.c
@@ -0,0 +1,1556 @@
+/*-
+ * Copyright (c) 1992 Terrence R. Lambert.
+ * Copyright (c) 1982, 1987, 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91
+ * $Id: machdep.c,v 1.41 1994/03/30 02:31:11 davidg Exp $
+ */
+
+#include "npx.h"
+#include "isa.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/signalvar.h>
+#include <sys/kernel.h>
+#include <sys/map.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/buf.h>
+#include <sys/reboot.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/msgbuf.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/sysctl.h>
+
+#ifdef SYSVSHM
+#include "sys/shm.h"
+#endif
+
+#ifdef SYSVMSG
+#include "msg.h"
+#endif
+
+#ifdef SYSVSEM
+#include "sem.h"
+#endif
+
+#include "vm/vm.h"
+#include "vm/vm_kern.h"
+#include "vm/vm_page.h"
+
+#include "sys/exec.h"
+#include "sys/vnode.h"
+
+extern vm_offset_t avail_start, avail_end;
+
+#include "machine/cpu.h"
+#include "machine/reg.h"
+#include "machine/psl.h"
+#include "machine/specialreg.h"
+#include "machine/sysarch.h"
+#include "machine/cons.h"
+
+#include "i386/isa/isa.h"
+#include "i386/isa/rtc.h"
+
+static void identifycpu(void);
+static void initcpu(void);
+static int test_page(int *, int);
+
+extern int grow(struct proc *,u_int);
+const char machine[] = "PC-Class";
+const char *cpu_model;
+
+#ifndef PANIC_REBOOT_WAIT_TIME
+#define PANIC_REBOOT_WAIT_TIME 15 /* default to 15 seconds */
+#endif
+
+/*
+ * Declare these as initialized data so we can patch them.
+ */
+int nswbuf = 0;
+#ifdef NBUF
+int nbuf = NBUF;
+#else
+int nbuf = 0;
+#endif
+#ifdef BUFPAGES
+int bufpages = BUFPAGES;
+#else
+int bufpages = 0;
+#endif
+#ifdef BOUNCEPAGES
+int bouncepages = BOUNCEPAGES;
+#else
+int bouncepages = 0;
+#endif
+int msgbufmapped = 0; /* set when safe to use msgbuf */
+extern int freebufspace;
+extern char *bouncememory;
+
+int _udatasel, _ucodesel;
+
+/*
+ * Machine-dependent startup code
+ */
+int boothowto = 0, Maxmem = 0, badpages = 0, physmem = 0;
+long dumplo;
+extern int bootdev;
+int biosmem;
+
+vm_offset_t phys_avail[6];
+
+extern cyloffset;
+
+int cpu_class;
+
+void dumpsys __P((void));
+vm_offset_t buffer_sva, buffer_eva;
+vm_offset_t clean_sva, clean_eva;
+vm_offset_t pager_sva, pager_eva;
+int maxbkva, pager_map_size;
+
+#define offsetof(type, member) ((size_t)(&((type *)0)->member))
+
+void
+cpu_startup()
+{
+ register int unixsize;
+ register unsigned i;
+ register struct pte *pte;
+ int mapaddr, j;
+ register caddr_t v;
+ int maxbufs, base, residual;
+ extern long Usrptsize;
+ vm_offset_t minaddr, maxaddr;
+ vm_size_t size = 0;
+ int firstaddr;
+
+ /*
+ * Initialize error message buffer (at end of core).
+ */
+
+ /* avail_end was pre-decremented in init_386() to compensate */
+ for (i = 0; i < btoc(sizeof (struct msgbuf)); i++)
+ pmap_enter(pmap_kernel(), (vm_offset_t)msgbufp,
+ avail_end + i * NBPG,
+ VM_PROT_ALL, TRUE);
+ msgbufmapped = 1;
+
+ /*
+ * Good {morning,afternoon,evening,night}.
+ */
+ printf(version);
+ identifycpu();
+ printf("real memory = %d (%d pages)\n", ptoa(physmem), physmem);
+ if (badpages)
+ printf("bad memory = %d (%d pages)\n", ptoa(badpages), badpages);
+
+ /*
+ * Allocate space for system data structures.
+ * The first available kernel virtual address is in "v".
+ * As pages of kernel virtual memory are allocated, "v" is incremented.
+ * As pages of memory are allocated and cleared,
+ * "firstaddr" is incremented.
+ * An index into the kernel page table corresponding to the
+ * virtual memory address maintained in "v" is kept in "mapaddr".
+ */
+
+ /*
+ * Make two passes. The first pass calculates how much memory is
+ * needed and allocates it. The second pass assigns virtual
+ * addresses to the various data structures.
+ */
+ firstaddr = 0;
+again:
+ v = (caddr_t)firstaddr;
+
+#define valloc(name, type, num) \
+ (name) = (type *)v; v = (caddr_t)((name)+(num))
+#define valloclim(name, type, num, lim) \
+ (name) = (type *)v; v = (caddr_t)((lim) = ((name)+(num)))
+ valloc(callout, struct callout, ncallout);
+#ifdef SYSVSHM
+ valloc(shmsegs, struct shmid_ds, shminfo.shmmni);
+#endif
+#ifdef SYSVSEM
+ valloc(sema, struct semid_ds, seminfo.semmni);
+ valloc(sem, struct sem, seminfo.semmns);
+ /* This is pretty disgusting! */
+ valloc(semu, int, (seminfo.semmnu * seminfo.semusz) / sizeof(int));
+#endif
+#ifdef SYSVMSG
+ valloc(msgpool, char, msginfo.msgmax);
+ valloc(msgmaps, struct msgmap, msginfo.msgseg);
+ valloc(msghdrs, struct msg, msginfo.msgtql);
+ valloc(msqids, struct msqid_ds, msginfo.msgmni);
+#endif
+ /*
+ * Determine how many buffers to allocate.
+ * Use 20% of memory of memory beyond the first 2MB
+ * Insure a minimum of 16 fs buffers.
+ * We allocate 1/2 as many swap buffer headers as file i/o buffers.
+ */
+ if (bufpages == 0)
+ bufpages = ((physmem << PGSHIFT) - 2048*1024) / NBPG / 5;
+ if (bufpages < 64)
+ bufpages = 64;
+
+ /*
+ * We must still limit the maximum number of buffers to be no
+ * more than 2/5's of the size of the kernal malloc region, this
+ * will only take effect for machines with lots of memory
+ */
+ bufpages = min(bufpages, (VM_KMEM_SIZE / NBPG) * 2 / 5);
+ if (nbuf == 0) {
+ nbuf = bufpages / 2;
+ if (nbuf < 32)
+ nbuf = 32;
+ }
+ freebufspace = bufpages * NBPG;
+ if (nswbuf == 0) {
+ nswbuf = (nbuf / 2) &~ 1; /* force even */
+ if (nswbuf > 256)
+ nswbuf = 256; /* sanity */
+ }
+ valloc(swbuf, struct buf, nswbuf);
+ valloc(buf, struct buf, nbuf);
+
+#ifndef NOBOUNCE
+ /*
+ * If there is more than 16MB of memory, allocate some bounce buffers
+ */
+ if (Maxmem > 4096) {
+ if (bouncepages == 0)
+ bouncepages = 96; /* largest physio size + extra */
+ v = (caddr_t)((vm_offset_t)((vm_offset_t)v + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1));
+ valloc(bouncememory, char, bouncepages * PAGE_SIZE);
+ }
+#endif
+
+ /*
+ * End of first pass, size has been calculated so allocate memory
+ */
+ if (firstaddr == 0) {
+ size = (vm_size_t)(v - firstaddr);
+ firstaddr = (int)kmem_alloc(kernel_map, round_page(size));
+ if (firstaddr == 0)
+ panic("startup: no room for tables");
+ goto again;
+ }
+
+ /*
+ * End of second pass, addresses have been assigned
+ */
+ if ((vm_size_t)(v - firstaddr) != size)
+ panic("startup: table size inconsistency");
+
+ clean_map = kmem_suballoc(kernel_map, &clean_sva, &clean_eva,
+ (nbuf*MAXBSIZE) + VM_PHYS_SIZE + maxbkva + pager_map_size, TRUE);
+
+ io_map = kmem_suballoc(clean_map, &minaddr, &maxaddr, maxbkva, FALSE);
+ pager_map = kmem_suballoc(clean_map, &pager_sva, &pager_eva,
+ pager_map_size, TRUE);
+
+ buffer_map = kmem_suballoc(clean_map, &buffer_sva, &buffer_eva,
+ (nbuf * MAXBSIZE), TRUE);
+ /*
+ * Allocate a submap for physio
+ */
+ phys_map = kmem_suballoc(clean_map, &minaddr, &maxaddr,
+ VM_PHYS_SIZE, TRUE);
+
+ /*
+ * Finally, allocate mbuf pool. Since mclrefcnt is an off-size
+ * we use the more space efficient malloc in place of kmem_alloc.
+ */
+ mclrefcnt = (char *)malloc(NMBCLUSTERS+CLBYTES/MCLBYTES,
+ M_MBUF, M_NOWAIT);
+ bzero(mclrefcnt, NMBCLUSTERS+CLBYTES/MCLBYTES);
+ mb_map = kmem_suballoc(kmem_map, (vm_offset_t *)&mbutl, &maxaddr,
+ VM_MBUF_SIZE, FALSE);
+ /*
+ * Initialize callouts
+ */
+ callfree = callout;
+ for (i = 1; i < ncallout; i++)
+ callout[i-1].c_next = &callout[i];
+
+ printf("avail memory = %d (%d pages)\n", ptoa(cnt.v_free_count), cnt.v_free_count);
+ printf("using %d buffers containing %d bytes of memory\n",
+ nbuf, bufpages * CLBYTES);
+
+#ifndef NOBOUNCE
+ /*
+ * init bounce buffers
+ */
+ vm_bounce_init();
+#endif
+
+ /*
+ * Set up CPU-specific registers, cache, etc.
+ */
+ initcpu();
+
+ /*
+ * Set up buffers, so they can be used to read disk labels.
+ */
+ bufinit();
+
+ /*
+ * Configure the system.
+ */
+ configure();
+}
+
+
+struct cpu_nameclass i386_cpus[] = {
+ { "Intel 80286", CPUCLASS_286 }, /* CPU_286 */
+ { "i386SX", CPUCLASS_386 }, /* CPU_386SX */
+ { "i386DX", CPUCLASS_386 }, /* CPU_386 */
+ { "i486SX", CPUCLASS_486 }, /* CPU_486SX */
+ { "i486DX", CPUCLASS_486 }, /* CPU_486 */
+ { "i586", CPUCLASS_586 }, /* CPU_586 */
+};
+
+static void
+identifycpu()
+{
+ printf("CPU: ");
+ if (cpu >= 0 && cpu < (sizeof i386_cpus/sizeof(struct cpu_nameclass))) {
+ printf("%s", i386_cpus[cpu].cpu_name);
+ cpu_class = i386_cpus[cpu].cpu_class;
+ cpu_model = i386_cpus[cpu].cpu_name;
+ } else {
+ printf("unknown cpu type %d\n", cpu);
+ panic("startup: bad cpu id");
+ }
+ printf(" (");
+ switch(cpu_class) {
+ case CPUCLASS_286:
+ printf("286");
+ break;
+ case CPUCLASS_386:
+ printf("386");
+ break;
+ case CPUCLASS_486:
+ printf("486");
+ break;
+ case CPUCLASS_586:
+ printf("586");
+ break;
+ default:
+ printf("unknown"); /* will panic below... */
+ }
+ printf("-class CPU)");
+ printf("\n"); /* cpu speed would be nice, but how? */
+
+ /*
+ * Now that we have told the user what they have,
+ * let them know if that machine type isn't configured.
+ */
+ switch (cpu_class) {
+ case CPUCLASS_286: /* a 286 should not make it this far, anyway */
+#if !defined(I386_CPU) && !defined(I486_CPU) && !defined(I586_CPU)
+#error This kernel is not configured for one of the supported CPUs
+#endif
+#if !defined(I386_CPU)
+ case CPUCLASS_386:
+#endif
+#if !defined(I486_CPU)
+ case CPUCLASS_486:
+#endif
+#if !defined(I586_CPU)
+ case CPUCLASS_586:
+#endif
+ panic("CPU class not configured");
+ default:
+ break;
+ }
+}
+
+#ifdef PGINPROF
+/*
+ * Return the difference (in microseconds)
+ * between the current time and a previous
+ * time as represented by the arguments.
+ * If there is a pending clock interrupt
+ * which has not been serviced due to high
+ * ipl, return error code.
+ */
+/*ARGSUSED*/
+vmtime(otime, olbolt, oicr)
+ register int otime, olbolt, oicr;
+{
+
+ return (((time.tv_sec-otime)*60 + lbolt-olbolt)*16667);
+}
+#endif
+
+extern int kstack[];
+
+/*
+ * Send an interrupt to process.
+ *
+ * Stack is set up to allow sigcode stored
+ * in u. to call routine, followed by kcall
+ * to sigreturn routine below. After sigreturn
+ * resets the signal mask, the stack, and the
+ * frame pointer, it returns to the user
+ * specified pc, psl.
+ */
+void
+sendsig(catcher, sig, mask, code)
+ sig_t catcher;
+ int sig, mask;
+ unsigned code;
+{
+ register struct proc *p = curproc;
+ register int *regs;
+ register struct sigframe *fp;
+ struct sigacts *psp = p->p_sigacts;
+ int oonstack, frmtrap;
+
+ regs = p->p_md.md_regs;
+ oonstack = psp->ps_sigstk.ss_flags & SA_ONSTACK;
+ /*
+ * Allocate and validate space for the signal handler
+ * context. Note that if the stack is in P0 space, the
+ * call to grow() is a nop, and the useracc() check
+ * will fail if the process has not already allocated
+ * the space with a `brk'.
+ */
+ if ((psp->ps_flags & SAS_ALTSTACK) &&
+ (psp->ps_sigstk.ss_flags & SA_ONSTACK) == 0 &&
+ (psp->ps_sigonstack & sigmask(sig))) {
+ fp = (struct sigframe *)(psp->ps_sigstk.ss_base +
+ psp->ps_sigstk.ss_size - sizeof(struct sigframe));
+ psp->ps_sigstk.ss_flags |= SA_ONSTACK;
+ } else {
+ fp = (struct sigframe *)(regs[tESP]
+ - sizeof(struct sigframe));
+ }
+
+ /*
+ * grow() will return FALSE if the fp will not fit inside the stack
+ * and the stack can not be grown. useracc will return FALSE
+ * if access is denied.
+ */
+ if ((grow(p, (int)fp) == FALSE) ||
+ (useracc((caddr_t)fp, sizeof (struct sigframe), B_WRITE) == FALSE)) {
+ /*
+ * Process has trashed its stack; give it an illegal
+ * instruction to halt it in its tracks.
+ */
+ SIGACTION(p, SIGILL) = SIG_DFL;
+ sig = sigmask(SIGILL);
+ p->p_sigignore &= ~sig;
+ p->p_sigcatch &= ~sig;
+ p->p_sigmask &= ~sig;
+ psignal(p, SIGILL);
+ return;
+ }
+
+ /*
+ * Build the argument list for the signal handler.
+ */
+ fp->sf_signum = sig;
+ fp->sf_code = code;
+ fp->sf_scp = &fp->sf_sc;
+ fp->sf_addr = (char *) regs[tERR];
+ fp->sf_handler = catcher;
+
+ /* save scratch registers */
+ fp->sf_sc.sc_eax = regs[tEAX];
+ fp->sf_sc.sc_ebx = regs[tEBX];
+ fp->sf_sc.sc_ecx = regs[tECX];
+ fp->sf_sc.sc_edx = regs[tEDX];
+ fp->sf_sc.sc_esi = regs[tESI];
+ fp->sf_sc.sc_edi = regs[tEDI];
+ fp->sf_sc.sc_cs = regs[tCS];
+ fp->sf_sc.sc_ds = regs[tDS];
+ fp->sf_sc.sc_ss = regs[tSS];
+ fp->sf_sc.sc_es = regs[tES];
+ fp->sf_sc.sc_isp = regs[tISP];
+
+ /*
+ * Build the signal context to be used by sigreturn.
+ */
+ fp->sf_sc.sc_onstack = oonstack;
+ fp->sf_sc.sc_mask = mask;
+ fp->sf_sc.sc_sp = regs[tESP];
+ fp->sf_sc.sc_fp = regs[tEBP];
+ fp->sf_sc.sc_pc = regs[tEIP];
+ fp->sf_sc.sc_ps = regs[tEFLAGS];
+ regs[tESP] = (int)fp;
+ regs[tEIP] = (int)((struct pcb *)kstack)->pcb_sigc;
+ regs[tEFLAGS] &= ~PSL_VM;
+ regs[tCS] = _ucodesel;
+ regs[tDS] = _udatasel;
+ regs[tES] = _udatasel;
+ regs[tSS] = _udatasel;
+}
+
+/*
+ * System call to cleanup state after a signal
+ * has been taken. Reset signal mask and
+ * stack state from context left by sendsig (above).
+ * Return to previous pc and psl as specified by
+ * context left by sendsig. Check carefully to
+ * make sure that the user has not modified the
+ * psl to gain improper privileges or to cause
+ * a machine fault.
+ */
+struct sigreturn_args {
+ struct sigcontext *sigcntxp;
+};
+
+int
+sigreturn(p, uap, retval)
+ struct proc *p;
+ struct sigreturn_args *uap;
+ int *retval;
+{
+ register struct sigcontext *scp;
+ register struct sigframe *fp;
+ register int *regs = p->p_md.md_regs;
+ int eflags;
+
+ /*
+ * (XXX old comment) regs[tESP] points to the return address.
+ * The user scp pointer is above that.
+ * The return address is faked in the signal trampoline code
+ * for consistency.
+ */
+ scp = uap->sigcntxp;
+ fp = (struct sigframe *)
+ ((caddr_t)scp - offsetof(struct sigframe, sf_sc));
+
+ if (useracc((caddr_t)fp, sizeof (*fp), 0) == 0)
+ return(EINVAL);
+
+ eflags = scp->sc_ps;
+ if ((eflags & PSL_USERCLR) != 0 ||
+ (eflags & PSL_USERSET) != PSL_USERSET ||
+ (eflags & PSL_IOPL) < (regs[tEFLAGS] & PSL_IOPL)) {
+#ifdef DEBUG
+ printf("sigreturn: eflags=0x%x\n", eflags);
+#endif
+ return(EINVAL);
+ }
+
+ /*
+ * Sanity check the user's selectors and error if they
+ * are suspect.
+ */
+#define max_ldt_sel(pcb) \
+ ((pcb)->pcb_ldt ? (pcb)->pcb_ldt_len : (sizeof(ldt) / sizeof(ldt[0])))
+
+#define valid_ldt_sel(sel) \
+ (ISLDT(sel) && ISPL(sel) == SEL_UPL && \
+ IDXSEL(sel) < max_ldt_sel(&p->p_addr->u_pcb))
+
+#define null_sel(sel) \
+ (!ISLDT(sel) && IDXSEL(sel) == 0)
+
+ if ((scp->sc_cs&0xffff != _ucodesel && !valid_ldt_sel(scp->sc_cs)) ||
+ (scp->sc_ss&0xffff != _udatasel && !valid_ldt_sel(scp->sc_ss)) ||
+ (scp->sc_ds&0xffff != _udatasel && !valid_ldt_sel(scp->sc_ds) &&
+ !null_sel(scp->sc_ds)) ||
+ (scp->sc_es&0xffff != _udatasel && !valid_ldt_sel(scp->sc_es) &&
+ !null_sel(scp->sc_es))) {
+#ifdef DEBUG
+ printf("sigreturn: cs=0x%x ss=0x%x ds=0x%x es=0x%x\n",
+ scp->sc_cs, scp->sc_ss, scp->sc_ds, scp->sc_es);
+#endif
+ trapsignal(p, SIGBUS, T_PROTFLT);
+ return(EINVAL);
+ }
+
+#undef max_ldt_sel
+#undef valid_ldt_sel
+#undef null_sel
+
+ /* restore scratch registers */
+ regs[tEAX] = scp->sc_eax;
+ regs[tEBX] = scp->sc_ebx;
+ regs[tECX] = scp->sc_ecx;
+ regs[tEDX] = scp->sc_edx;
+ regs[tESI] = scp->sc_esi;
+ regs[tEDI] = scp->sc_edi;
+ regs[tCS] = scp->sc_cs;
+ regs[tDS] = scp->sc_ds;
+ regs[tES] = scp->sc_es;
+ regs[tSS] = scp->sc_ss;
+ regs[tISP] = scp->sc_isp;
+
+ if (useracc((caddr_t)scp, sizeof (*scp), 0) == 0)
+ return(EINVAL);
+
+ if (scp->sc_onstack & 01)
+ p->p_sigacts->ps_sigstk.ss_flags |= SA_ONSTACK;
+ else
+ p->p_sigacts->ps_sigstk.ss_flags &= ~SA_ONSTACK;
+ p->p_sigmask = scp->sc_mask &~
+ (sigmask(SIGKILL)|sigmask(SIGCONT)|sigmask(SIGSTOP));
+ regs[tEBP] = scp->sc_fp;
+ regs[tESP] = scp->sc_sp;
+ regs[tEIP] = scp->sc_pc;
+ regs[tEFLAGS] = eflags;
+ return(EJUSTRETURN);
+}
+
+/*
+ * a simple function to make the system panic (and dump a vmcore)
+ * in a predictable fashion
+ */
+void diediedie()
+{
+ panic("because you said to!");
+}
+
+int waittime = -1;
+struct pcb dumppcb;
+
+void
+boot(arghowto)
+ int arghowto;
+{
+ register long dummy; /* r12 is reserved */
+ register int howto; /* r11 == how to boot */
+ register int devtype; /* r10 == major of root dev */
+ extern int cold;
+ int nomsg = 1;
+
+ if (cold) {
+ printf("hit reset please");
+ for(;;);
+ }
+ howto = arghowto;
+ if ((howto&RB_NOSYNC) == 0 && waittime < 0) {
+ register struct buf *bp;
+ int iter, nbusy;
+
+ waittime = 0;
+ (void) splnet();
+ printf("syncing disks... ");
+ /*
+ * Release inodes held by texts before update.
+ */
+ if (panicstr == 0)
+ vnode_pager_umount(NULL);
+ sync(curproc, NULL, NULL);
+ /*
+ * Unmount filesystems
+ */
+#if 0
+ if (panicstr == 0)
+ vfs_unmountall();
+#endif
+
+ for (iter = 0; iter < 20; iter++) {
+ nbusy = 0;
+ for (bp = &buf[nbuf]; --bp >= buf; )
+ if ((bp->b_flags & (B_BUSY|B_INVAL)) == B_BUSY)
+ nbusy++;
+ if (nbusy == 0)
+ break;
+ if (nomsg) {
+ printf("updating disks before rebooting... ");
+ nomsg = 0;
+ }
+ printf("%d ", nbusy);
+ DELAY(40000 * iter);
+ }
+ if (nbusy)
+ printf("giving up\n");
+ else
+ printf("done\n");
+ DELAY(10000); /* wait for printf to finish */
+ }
+ splhigh();
+ devtype = major(rootdev);
+ if (howto&RB_HALT) {
+ printf("\n");
+ printf("The operating system has halted.\n");
+ printf("Please press any key to reboot.\n\n");
+ cngetc();
+ } else {
+ if (howto & RB_DUMP) {
+ savectx(&dumppcb, 0);
+ dumppcb.pcb_ptd = rcr3();
+ dumpsys();
+
+ if (PANIC_REBOOT_WAIT_TIME != 0) {
+ if (PANIC_REBOOT_WAIT_TIME != -1) {
+ int loop;
+ printf("Automatic reboot in %d seconds - press a key on the console to abort\n",
+ PANIC_REBOOT_WAIT_TIME);
+ for (loop = PANIC_REBOOT_WAIT_TIME; loop > 0; --loop) {
+ DELAY(1000 * 1000); /* one second */
+ if (sgetc(1)) /* Did user type a key? */
+ break;
+ }
+ if (!loop)
+ goto die;
+ }
+ } else { /* zero time specified - reboot NOW */
+ goto die;
+ }
+ printf("--> Press a key on the console to reboot <--\n");
+ cngetc();
+ }
+ }
+#ifdef lint
+ dummy = 0; dummy = dummy;
+ printf("howto %d, devtype %d\n", arghowto, devtype);
+#endif
+die:
+ printf("Rebooting...\n");
+ DELAY(1000000); /* wait 1 sec for printf's to complete and be read */
+ cpu_reset();
+ for(;;) ;
+ /* NOTREACHED */
+}
+
+unsigned long dumpmag = 0x8fca0101UL; /* magic number for savecore */
+int dumpsize = 0; /* also for savecore */
+/*
+ * Doadump comes here after turning off memory management and
+ * getting on the dump stack, either when called above, or by
+ * the auto-restart code.
+ */
+void
+dumpsys()
+{
+
+ if (dumpdev == NODEV)
+ return;
+ if ((minor(dumpdev)&07) != 1)
+ return;
+ dumpsize = Maxmem;
+ printf("\ndumping to dev %x, offset %d\n", dumpdev, dumplo);
+ printf("dump ");
+ switch ((*bdevsw[major(dumpdev)].d_dump)(dumpdev)) {
+
+ case ENXIO:
+ printf("device bad\n");
+ break;
+
+ case EFAULT:
+ printf("device not ready\n");
+ break;
+
+ case EINVAL:
+ printf("area improper\n");
+ break;
+
+ case EIO:
+ printf("i/o error\n");
+ break;
+
+ case EINTR:
+ printf("aborted from console\n");
+ break;
+
+ default:
+ printf("succeeded\n");
+ break;
+ }
+}
+
+#ifdef HZ
+/*
+ * If HZ is defined we use this code, otherwise the code in
+ * /sys/i386/i386/microtime.s is used. The othercode only works
+ * for HZ=100.
+ */
+microtime(tvp)
+ register struct timeval *tvp;
+{
+ int s = splhigh();
+
+ *tvp = time;
+ tvp->tv_usec += tick;
+ while (tvp->tv_usec > 1000000) {
+ tvp->tv_sec++;
+ tvp->tv_usec -= 1000000;
+ }
+ splx(s);
+}
+#endif /* HZ */
+
+static void
+initcpu()
+{
+}
+
+/*
+ * Clear registers on exec
+ */
+void
+setregs(p, entry, stack)
+ struct proc *p;
+ u_long entry;
+ u_long stack;
+{
+ p->p_md.md_regs[tEBP] = 0; /* bottom of the fp chain */
+ p->p_md.md_regs[tEIP] = entry;
+ p->p_md.md_regs[tESP] = stack;
+ p->p_md.md_regs[tSS] = _udatasel;
+ p->p_md.md_regs[tDS] = _udatasel;
+ p->p_md.md_regs[tES] = _udatasel;
+ p->p_md.md_regs[tCS] = _ucodesel;
+
+ p->p_addr->u_pcb.pcb_flags = 0; /* no fp at all */
+ load_cr0(rcr0() | CR0_TS); /* start emulating */
+#if NNPX > 0
+ npxinit(__INITIAL_NPXCW__);
+#endif /* NNPX > 0 */
+}
+
+/*
+ * machine dependent system variables.
+ */
+int
+cpu_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+ struct proc *p;
+{
+
+ /* all sysctl names at this level are terminal */
+ if (namelen != 1)
+ return (ENOTDIR); /* overloaded */
+
+ switch (name[0]) {
+ case CPU_CONSDEV:
+ return (sysctl_rdstruct(oldp, oldlenp, newp, &cn_tty->t_dev,
+ sizeof cn_tty->t_dev));
+ default:
+ return (EOPNOTSUPP);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Initialize 386 and configure to run kernel
+ */
+
+/*
+ * Initialize segments & interrupt table
+ */
+
+union descriptor gdt[NGDT];
+union descriptor ldt[NLDT]; /* local descriptor table */
+struct gate_descriptor idt[NIDT]; /* interrupt descriptor table */
+
+int _default_ldt, currentldt;
+
+struct i386tss tss, panic_tss;
+
+extern struct user *proc0paddr;
+
+/* software prototypes -- in more palatable form */
+struct soft_segment_descriptor gdt_segs[] = {
+ /* Null Descriptor */
+{ 0x0, /* segment base address */
+ 0x0, /* length */
+ 0, /* segment type */
+ 0, /* segment descriptor priority level */
+ 0, /* segment descriptor present */
+ 0, 0,
+ 0, /* default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Code Descriptor for kernel */
+{ 0x0, /* segment base address */
+ 0xfffff, /* length - all address space */
+ SDT_MEMERA, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 1, /* default 32 vs 16 bit size */
+ 1 /* limit granularity (byte/page units)*/ },
+ /* Data Descriptor for kernel */
+{ 0x0, /* segment base address */
+ 0xfffff, /* length - all address space */
+ SDT_MEMRWA, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 1, /* default 32 vs 16 bit size */
+ 1 /* limit granularity (byte/page units)*/ },
+ /* LDT Descriptor */
+{ (int) ldt, /* segment base address */
+ sizeof(ldt)-1, /* length - all address space */
+ SDT_SYSLDT, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 0, /* unused - default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Null Descriptor - Placeholder */
+{ 0x0, /* segment base address */
+ 0x0, /* length - all address space */
+ 0, /* segment type */
+ 0, /* segment descriptor priority level */
+ 0, /* segment descriptor present */
+ 0, 0,
+ 0, /* default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Panic Tss Descriptor */
+{ (int) &panic_tss, /* segment base address */
+ sizeof(tss)-1, /* length - all address space */
+ SDT_SYS386TSS, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 0, /* unused - default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Proc 0 Tss Descriptor */
+{ (int) kstack, /* segment base address */
+ sizeof(tss)-1, /* length - all address space */
+ SDT_SYS386TSS, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 0, /* unused - default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* User LDT Descriptor per process */
+{ (int) ldt, /* segment base address */
+ (512 * sizeof(union descriptor)-1), /* length */
+ SDT_SYSLDT, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 0, /* unused - default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+};
+
+struct soft_segment_descriptor ldt_segs[] = {
+ /* Null Descriptor - overwritten by call gate */
+{ 0x0, /* segment base address */
+ 0x0, /* length - all address space */
+ 0, /* segment type */
+ 0, /* segment descriptor priority level */
+ 0, /* segment descriptor present */
+ 0, 0,
+ 0, /* default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Null Descriptor - overwritten by call gate */
+{ 0x0, /* segment base address */
+ 0x0, /* length - all address space */
+ 0, /* segment type */
+ 0, /* segment descriptor priority level */
+ 0, /* segment descriptor present */
+ 0, 0,
+ 0, /* default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Null Descriptor - overwritten by call gate */
+{ 0x0, /* segment base address */
+ 0x0, /* length - all address space */
+ 0, /* segment type */
+ 0, /* segment descriptor priority level */
+ 0, /* segment descriptor present */
+ 0, 0,
+ 0, /* default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Code Descriptor for user */
+{ 0x0, /* segment base address */
+ 0xfffff, /* length - all address space */
+ SDT_MEMERA, /* segment type */
+ SEL_UPL, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 1, /* default 32 vs 16 bit size */
+ 1 /* limit granularity (byte/page units)*/ },
+ /* Data Descriptor for user */
+{ 0x0, /* segment base address */
+ 0xfffff, /* length - all address space */
+ SDT_MEMRWA, /* segment type */
+ SEL_UPL, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 1, /* default 32 vs 16 bit size */
+ 1 /* limit granularity (byte/page units)*/ } };
+
+void
+setidt(idx, func, typ, dpl)
+ int idx;
+ void (*func)();
+ int typ;
+ int dpl;
+{
+ struct gate_descriptor *ip = idt + idx;
+
+ ip->gd_looffset = (int)func;
+ ip->gd_selector = 8;
+ ip->gd_stkcpy = 0;
+ ip->gd_xx = 0;
+ ip->gd_type = typ;
+ ip->gd_dpl = dpl;
+ ip->gd_p = 1;
+ ip->gd_hioffset = ((int)func)>>16 ;
+}
+
+#define IDTVEC(name) __CONCAT(X,name)
+typedef void idtvec_t();
+
+extern idtvec_t
+ IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl),
+ IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(dble), IDTVEC(fpusegm),
+ IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot),
+ IDTVEC(page), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(rsvd0),
+ IDTVEC(rsvd1), IDTVEC(rsvd2), IDTVEC(rsvd3), IDTVEC(rsvd4),
+ IDTVEC(rsvd5), IDTVEC(rsvd6), IDTVEC(rsvd7), IDTVEC(rsvd8),
+ IDTVEC(rsvd9), IDTVEC(rsvd10), IDTVEC(rsvd11), IDTVEC(rsvd12),
+ IDTVEC(rsvd13), IDTVEC(rsvd14), IDTVEC(syscall);
+
+int _gsel_tss;
+
+void
+init386(first)
+ int first;
+{
+ extern ssdtosd(), lgdt(), lidt(), lldt(), etext;
+ int x, *pi;
+ unsigned biosbasemem, biosextmem;
+ struct gate_descriptor *gdp;
+ extern int sigcode,szsigcode;
+ /* table descriptors - used to load tables by microp */
+ struct region_descriptor r_gdt, r_idt;
+ int pagesinbase, pagesinext;
+ int target_page;
+
+ proc0.p_addr = proc0paddr;
+
+ /*
+ * Initialize the console before we print anything out.
+ */
+
+ cninit ();
+
+ /*
+ * make gdt memory segments, the code segment goes up to end of the
+ * page with etext in it, the data segment goes to the end of
+ * the address space
+ */
+ gdt_segs[GCODE_SEL].ssd_limit = i386_btop(i386_round_page(&etext)) - 1;
+ gdt_segs[GDATA_SEL].ssd_limit = i386_btop(0) - 1;
+ for (x=0; x < NGDT; x++) ssdtosd(gdt_segs+x, gdt+x);
+
+ /* make ldt memory segments */
+ /*
+ * The data segment limit must not cover the user area because we
+ * don't want the user area to be writable in copyout() etc. (page
+ * level protection is lost in kernel mode on 386's). Also, we
+ * don't want the user area to be writable directly (page level
+ * protection of the user area is not available on 486's with
+ * CR0_WP set, because there is no user-read/kernel-write mode).
+ *
+ * XXX - VM_MAXUSER_ADDRESS is an end address, not a max. And it
+ * should be spelled ...MAX_USER...
+ */
+#define VM_END_USER_RW_ADDRESS VM_MAXUSER_ADDRESS
+ /*
+ * The code segment limit has to cover the user area until we move
+ * the signal trampoline out of the user area. This is safe because
+ * the code segment cannot be written to directly.
+ */
+#define VM_END_USER_R_ADDRESS (VM_END_USER_RW_ADDRESS + UPAGES * NBPG)
+ ldt_segs[LUCODE_SEL].ssd_limit = i386_btop(VM_END_USER_R_ADDRESS) - 1;
+ ldt_segs[LUDATA_SEL].ssd_limit = i386_btop(VM_END_USER_RW_ADDRESS) - 1;
+ /* Note. eventually want private ldts per process */
+ for (x=0; x < 5; x++) ssdtosd(ldt_segs+x, ldt+x);
+
+ /* exceptions */
+ setidt(0, &IDTVEC(div), SDT_SYS386TGT, SEL_KPL);
+ setidt(1, &IDTVEC(dbg), SDT_SYS386TGT, SEL_KPL);
+ setidt(2, &IDTVEC(nmi), SDT_SYS386TGT, SEL_KPL);
+ setidt(3, &IDTVEC(bpt), SDT_SYS386TGT, SEL_UPL);
+ setidt(4, &IDTVEC(ofl), SDT_SYS386TGT, SEL_UPL);
+ setidt(5, &IDTVEC(bnd), SDT_SYS386TGT, SEL_KPL);
+ setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL);
+ setidt(7, &IDTVEC(dna), SDT_SYS386TGT, SEL_KPL);
+ setidt(8, &IDTVEC(dble), SDT_SYS386TGT, SEL_KPL);
+ setidt(9, &IDTVEC(fpusegm), SDT_SYS386TGT, SEL_KPL);
+ setidt(10, &IDTVEC(tss), SDT_SYS386TGT, SEL_KPL);
+ setidt(11, &IDTVEC(missing), SDT_SYS386TGT, SEL_KPL);
+ setidt(12, &IDTVEC(stk), SDT_SYS386TGT, SEL_KPL);
+ setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL);
+ setidt(14, &IDTVEC(page), SDT_SYS386TGT, SEL_KPL);
+ setidt(15, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL);
+ setidt(16, &IDTVEC(fpu), SDT_SYS386TGT, SEL_KPL);
+ setidt(17, &IDTVEC(rsvd0), SDT_SYS386TGT, SEL_KPL);
+ setidt(18, &IDTVEC(rsvd1), SDT_SYS386TGT, SEL_KPL);
+ setidt(19, &IDTVEC(rsvd2), SDT_SYS386TGT, SEL_KPL);
+ setidt(20, &IDTVEC(rsvd3), SDT_SYS386TGT, SEL_KPL);
+ setidt(21, &IDTVEC(rsvd4), SDT_SYS386TGT, SEL_KPL);
+ setidt(22, &IDTVEC(rsvd5), SDT_SYS386TGT, SEL_KPL);
+ setidt(23, &IDTVEC(rsvd6), SDT_SYS386TGT, SEL_KPL);
+ setidt(24, &IDTVEC(rsvd7), SDT_SYS386TGT, SEL_KPL);
+ setidt(25, &IDTVEC(rsvd8), SDT_SYS386TGT, SEL_KPL);
+ setidt(26, &IDTVEC(rsvd9), SDT_SYS386TGT, SEL_KPL);
+ setidt(27, &IDTVEC(rsvd10), SDT_SYS386TGT, SEL_KPL);
+ setidt(28, &IDTVEC(rsvd11), SDT_SYS386TGT, SEL_KPL);
+ setidt(29, &IDTVEC(rsvd12), SDT_SYS386TGT, SEL_KPL);
+ setidt(30, &IDTVEC(rsvd13), SDT_SYS386TGT, SEL_KPL);
+ setidt(31, &IDTVEC(rsvd14), SDT_SYS386TGT, SEL_KPL);
+
+#include "isa.h"
+#if NISA >0
+ isa_defaultirq();
+#endif
+
+ r_gdt.rd_limit = sizeof(gdt) - 1;
+ r_gdt.rd_base = (int) gdt;
+ lgdt(&r_gdt);
+
+ r_idt.rd_limit = sizeof(idt) - 1;
+ r_idt.rd_base = (int) idt;
+ lidt(&r_idt);
+
+ _default_ldt = GSEL(GLDT_SEL, SEL_KPL);
+ lldt(_default_ldt);
+ currentldt = _default_ldt;
+
+#include "ddb.h"
+#if NDDB > 0
+ kdb_init();
+ if (boothowto & RB_KDB)
+ Debugger("Boot flags requested debugger");
+#endif
+
+ /* Use BIOS values stored in RTC CMOS RAM, since probing
+ * breaks certain 386 AT relics.
+ */
+ biosbasemem = rtcin(RTC_BASELO)+ (rtcin(RTC_BASEHI)<<8);
+ biosextmem = rtcin(RTC_EXTLO)+ (rtcin(RTC_EXTHI)<<8);
+
+ /*
+ * If BIOS tells us that it has more than 640k in the basemem,
+ * don't believe it - set it to 640k.
+ */
+ if (biosbasemem > 640)
+ biosbasemem = 640;
+
+ /*
+ * Some 386 machines might give us a bogus number for extended
+ * mem. If this happens, stop now.
+ */
+#ifndef LARGEMEM
+ if (biosextmem > 65536) {
+ panic("extended memory beyond limit of 64MB");
+ /* NOTREACHED */
+ }
+#endif
+
+ pagesinbase = biosbasemem * 1024 / NBPG;
+ pagesinext = biosextmem * 1024 / NBPG;
+
+ /*
+ * Special hack for chipsets that still remap the 384k hole when
+ * there's 16MB of memory - this really confuses people that
+ * are trying to use bus mastering ISA controllers with the
+ * "16MB limit"; they only have 16MB, but the remapping puts
+ * them beyond the limit.
+ * XXX - this should be removed when bounce buffers are
+ * implemented.
+ */
+ /*
+ * If extended memory is between 15-16MB (16-17MB phys address range),
+ * chop it to 15MB.
+ */
+ if ((pagesinext > 3840) && (pagesinext < 4096))
+ pagesinext = 3840;
+
+ /*
+ * Maxmem isn't the "maximum memory", it's the highest page of
+ * of the physical address space. It should be "Maxphyspage".
+ */
+ Maxmem = pagesinext + 0x100000/PAGE_SIZE;
+
+#ifdef MAXMEM
+ if (MAXMEM/4 < Maxmem)
+ Maxmem = MAXMEM/4;
+#endif
+ /*
+ * Calculate number of physical pages, but account for Maxmem
+ * limitation above.
+ */
+ physmem = pagesinbase +
+ (min(pagesinext + 0x100000/PAGE_SIZE, Maxmem) - 0x100000/PAGE_SIZE);
+
+ /* call pmap initialization to make new kernel address space */
+ pmap_bootstrap (first, 0);
+
+ /*
+ * Do simple memory test over range of extended memory that BIOS
+ * indicates exists. Adjust Maxmem to the highest page of
+ * good memory.
+ */
+ printf("Testing memory (%dMB)...", ptoa(Maxmem)/1024/1024);
+
+ for (target_page = Maxmem - 1; target_page >= atop(first); target_page--) {
+ extern struct pte *CMAP1;
+ extern caddr_t CADDR1;
+
+ /*
+ * map page into kernel: valid, read/write, non-cacheable
+ */
+ *(int *)CMAP1 = PG_V | PG_KW | PG_N | ptoa(target_page);
+ tlbflush();
+
+ /*
+ * Test for alternating 1's and 0's
+ */
+ filli(0xaaaaaaaa, CADDR1, PAGE_SIZE/sizeof(int));
+ if (test_page((int *)CADDR1, 0xaaaaaaaa)) {
+ Maxmem = target_page;
+ badpages++;
+ continue;
+ }
+ /*
+ * Test for alternating 0's and 1's
+ */
+ filli(0x55555555, CADDR1, PAGE_SIZE/sizeof(int));
+ if (test_page((int *)CADDR1, 0x55555555)) {
+ Maxmem = target_page;
+ badpages++;
+ continue;
+ }
+ /*
+ * Test for all 1's
+ */
+ filli(0xffffffff, CADDR1, PAGE_SIZE/sizeof(int));
+ if (test_page((int *)CADDR1, 0xffffffff)) {
+ Maxmem = target_page;
+ badpages++;
+ continue;
+ }
+ /*
+ * Test zeroing of page
+ */
+ bzero(CADDR1, PAGE_SIZE);
+ if (test_page((int *)CADDR1, 0)) {
+ /*
+ * test of page failed
+ */
+ Maxmem = target_page;
+ badpages++;
+ continue;
+ }
+ }
+ printf("done.\n");
+
+ avail_end = (Maxmem << PAGE_SHIFT)
+ - i386_round_page(sizeof(struct msgbuf));
+
+ /*
+ * Initialize pointers to the two chunks of memory; for use
+ * later in vm_page_startup.
+ */
+ /* avail_start is initialized in pmap_bootstrap */
+ x = 0;
+ if (pagesinbase > 1) {
+ phys_avail[x++] = NBPG; /* skip first page of memory */
+ phys_avail[x++] = pagesinbase * NBPG; /* memory up to the ISA hole */
+ }
+ phys_avail[x++] = avail_start; /* memory up to the end */
+ phys_avail[x++] = avail_end;
+ phys_avail[x++] = 0; /* no more chunks */
+ phys_avail[x++] = 0;
+
+ /* now running on new page tables, configured,and u/iom is accessible */
+
+ /* make a initial tss so microp can get interrupt stack on syscall! */
+ proc0.p_addr->u_pcb.pcb_tss.tss_esp0 = (int) kstack + UPAGES*NBPG;
+ proc0.p_addr->u_pcb.pcb_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL) ;
+ _gsel_tss = GSEL(GPROC0_SEL, SEL_KPL);
+
+ ((struct i386tss *)gdt_segs[GPROC0_SEL].ssd_base)->tss_ioopt =
+ (sizeof(tss))<<16;
+
+ ltr(_gsel_tss);
+
+ /* make a call gate to reenter kernel with */
+ gdp = &ldt[LSYS5CALLS_SEL].gd;
+
+ x = (int) &IDTVEC(syscall);
+ gdp->gd_looffset = x++;
+ gdp->gd_selector = GSEL(GCODE_SEL,SEL_KPL);
+ gdp->gd_stkcpy = 1;
+ gdp->gd_type = SDT_SYS386CGT;
+ gdp->gd_dpl = SEL_UPL;
+ gdp->gd_p = 1;
+ gdp->gd_hioffset = ((int) &IDTVEC(syscall)) >>16;
+
+ /* transfer to user mode */
+
+ _ucodesel = LSEL(LUCODE_SEL, SEL_UPL);
+ _udatasel = LSEL(LUDATA_SEL, SEL_UPL);
+
+ /* setup proc 0's pcb */
+ bcopy(&sigcode, proc0.p_addr->u_pcb.pcb_sigc, szsigcode);
+ proc0.p_addr->u_pcb.pcb_flags = 0;
+ proc0.p_addr->u_pcb.pcb_ptd = IdlePTD;
+}
+
+int
+test_page(address, pattern)
+ int *address;
+ int pattern;
+{
+ int *x;
+
+ for (x = address; x < (int *)((char *)address + PAGE_SIZE); x++) {
+ if (*x != pattern)
+ return (1);
+ }
+ return(0);
+}
+
+/*
+ * insert an element into a queue
+ */
+#undef insque
+void /* XXX replace with inline FIXME! */
+_insque(element, head)
+ register struct prochd *element, *head;
+{
+ element->ph_link = head->ph_link;
+ head->ph_link = (struct proc *)element;
+ element->ph_rlink = (struct proc *)head;
+ ((struct prochd *)(element->ph_link))->ph_rlink=(struct proc *)element;
+}
+
+/*
+ * remove an element from a queue
+ */
+#undef remque
+void /* XXX replace with inline FIXME! */
+_remque(element)
+ register struct prochd *element;
+{
+ ((struct prochd *)(element->ph_link))->ph_rlink = element->ph_rlink;
+ ((struct prochd *)(element->ph_rlink))->ph_link = element->ph_link;
+ element->ph_rlink = (struct proc *)0;
+}
+
+/*
+ * The registers are in the frame; the frame is in the user area of
+ * the process in question; when the process is active, the registers
+ * are in "the kernel stack"; when it's not, they're still there, but
+ * things get flipped around. So, since p->p_md.md_regs is the whole address
+ * of the register set, take its offset from the kernel stack, and
+ * index into the user block. Don't you just *love* virtual memory?
+ * (I'm starting to think seymour is right...)
+ */
+
+int
+ptrace_set_pc (struct proc *p, unsigned int addr) {
+ void *regs = (char*)p->p_addr +
+ ((char*) p->p_md.md_regs - (char*) kstack);
+
+ ((struct trapframe *)regs)->tf_eip = addr;
+ return 0;
+}
+
+int
+ptrace_single_step (struct proc *p) {
+ void *regs = (char*)p->p_addr +
+ ((char*) p->p_md.md_regs - (char*) kstack);
+
+ ((struct trapframe *)regs)->tf_eflags |= PSL_T;
+ return 0;
+}
+
+/*
+ * Copy the registers to user-space.
+ */
+
+int
+ptrace_getregs (struct proc *p, unsigned int *addr) {
+ int error;
+ struct reg regs = {0};
+
+ if (error = fill_regs (p, &regs))
+ return error;
+
+ return copyout (&regs, addr, sizeof (regs));
+}
+
+int
+ptrace_setregs (struct proc *p, unsigned int *addr) {
+ int error;
+ struct reg regs = {0};
+
+ if (error = copyin (addr, &regs, sizeof(regs)))
+ return error;
+
+ return set_regs (p, &regs);
+}
+
+int
+fill_regs(struct proc *p, struct reg *regs) {
+ int error;
+ struct trapframe *tp;
+ void *ptr = (char*)p->p_addr +
+ ((char*) p->p_md.md_regs - (char*) kstack);
+
+ tp = ptr;
+ regs->r_es = tp->tf_es;
+ regs->r_ds = tp->tf_ds;
+ regs->r_edi = tp->tf_edi;
+ regs->r_esi = tp->tf_esi;
+ regs->r_ebp = tp->tf_ebp;
+ regs->r_ebx = tp->tf_ebx;
+ regs->r_edx = tp->tf_edx;
+ regs->r_ecx = tp->tf_ecx;
+ regs->r_eax = tp->tf_eax;
+ regs->r_eip = tp->tf_eip;
+ regs->r_cs = tp->tf_cs;
+ regs->r_eflags = tp->tf_eflags;
+ regs->r_esp = tp->tf_esp;
+ regs->r_ss = tp->tf_ss;
+ return 0;
+}
+
+int
+set_regs (struct proc *p, struct reg *regs) {
+ int error;
+ struct trapframe *tp;
+ void *ptr = (char*)p->p_addr +
+ ((char*) p->p_md.md_regs - (char*) kstack);
+
+ tp = ptr;
+ tp->tf_es = regs->r_es;
+ tp->tf_ds = regs->r_ds;
+ tp->tf_edi = regs->r_edi;
+ tp->tf_esi = regs->r_esi;
+ tp->tf_ebp = regs->r_ebp;
+ tp->tf_ebx = regs->r_ebx;
+ tp->tf_edx = regs->r_edx;
+ tp->tf_ecx = regs->r_ecx;
+ tp->tf_eax = regs->r_eax;
+ tp->tf_eip = regs->r_eip;
+ tp->tf_cs = regs->r_cs;
+ tp->tf_eflags = regs->r_eflags;
+ tp->tf_esp = regs->r_esp;
+ tp->tf_ss = regs->r_ss;
+ return 0;
+}
+
+#include "ddb.h"
+#if NDDB <= 0
+void
+Debugger(const char *msg)
+{
+ printf("Debugger(\"%s\") called.\n", msg);
+}
+#endif /* no DDB */
+
+#include <sys/disklabel.h>
+#define b_cylin b_resid
+#define dkpart(dev) (minor(dev) & 7)
+/*
+ * Determine the size of the transfer, and make sure it is
+ * within the boundaries of the partition. Adjust transfer
+ * if needed, and signal errors or early completion.
+ */
+int
+bounds_check_with_label(struct buf *bp, struct disklabel *lp, int wlabel)
+{
+ struct partition *p = lp->d_partitions + dkpart(bp->b_dev);
+ int labelsect = lp->d_partitions[0].p_offset;
+ int maxsz = p->p_size,
+ sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT;
+
+ /* overwriting disk label ? */
+ /* XXX should also protect bootstrap in first 8K */
+ if (bp->b_blkno + p->p_offset <= LABELSECTOR + labelsect &&
+#if LABELSECTOR != 0
+ bp->b_blkno + p->p_offset + sz > LABELSECTOR + labelsect &&
+#endif
+ (bp->b_flags & B_READ) == 0 && wlabel == 0) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+
+#if defined(DOSBBSECTOR) && defined(notyet)
+ /* overwriting master boot record? */
+ if (bp->b_blkno + p->p_offset <= DOSBBSECTOR &&
+ (bp->b_flags & B_READ) == 0 && wlabel == 0) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+#endif
+
+ /* beyond partition? */
+ if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) {
+ /* if exactly at end of disk, return an EOF */
+ if (bp->b_blkno == maxsz) {
+ bp->b_resid = bp->b_bcount;
+ return(0);
+ }
+ /* or truncate if part of it fits */
+ sz = maxsz - bp->b_blkno;
+ if (sz <= 0) {
+ bp->b_error = EINVAL;
+ goto bad;
+ }
+ bp->b_bcount = sz << DEV_BSHIFT;
+ }
+
+ /* calculate cylinder for disksort to order transfers with */
+ bp->b_pblkno = bp->b_blkno + p->p_offset;
+ bp->b_cylin = bp->b_pblkno / lp->d_secpercyl;
+ return(1);
+
+bad:
+ bp->b_flags |= B_ERROR;
+ return(-1);
+}
+
diff --git a/sys/i386/i386/math_emu.h b/sys/i386/i386/math_emu.h
new file mode 100644
index 0000000..453a903
--- /dev/null
+++ b/sys/i386/i386/math_emu.h
@@ -0,0 +1,156 @@
+/*
+ * linux/include/linux/math_emu.h
+ *
+ * (C) 1991 Linus Torvalds
+ *
+ * $Id$
+ */
+#ifndef _LINUX_MATH_EMU_H
+#define _LINUX_MATH_EMU_H
+
+/*#define math_abort(x,y) \
+(((volatile void (*)(struct info *,unsigned int)) __math_abort)((x),(y)))*/
+
+/*
+ * Gcc forces this stupid alignment problem: I want to use only two longs
+ * for the temporary real 64-bit mantissa, but then gcc aligns out the
+ * structure to 12 bytes which breaks things in math_emulate.c. Shit. I
+ * want some kind of "no-alignt" pragma or something.
+ */
+
+typedef struct {
+ long a,b;
+ short exponent;
+} temp_real;
+
+typedef struct {
+ short m0,m1,m2,m3;
+ short exponent;
+} temp_real_unaligned;
+
+#define real_to_real(a,b) \
+((*(long long *) (b) = *(long long *) (a)),((b)->exponent = (a)->exponent))
+
+typedef struct {
+ long a,b;
+} long_real;
+
+typedef long short_real;
+
+typedef struct {
+ long a,b;
+ short sign;
+} temp_int;
+
+struct swd {
+ int ie:1;
+ int de:1;
+ int ze:1;
+ int oe:1;
+ int ue:1;
+ int pe:1;
+ int sf:1;
+ int ir:1;
+ int c0:1;
+ int c1:1;
+ int c2:1;
+ int top:3;
+ int c3:1;
+ int b:1;
+};
+struct i387_struct {
+ long cwd;
+ long swd;
+ long twd;
+ long fip;
+ long fcs;
+ long foo;
+ long fos;
+ long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */
+};
+
+#define I387 (*(struct i387_struct *)&(((struct pcb *)curproc->p_addr)->pcb_savefpu))
+#define SWD (*(struct swd *) &I387.swd)
+#define ROUNDING ((I387.cwd >> 10) & 3)
+#define PRECISION ((I387.cwd >> 8) & 3)
+
+#define BITS24 0
+#define BITS53 2
+#define BITS64 3
+
+#define ROUND_NEAREST 0
+#define ROUND_DOWN 1
+#define ROUND_UP 2
+#define ROUND_0 3
+
+#define CONSTZ (temp_real_unaligned) {0x0000,0x0000,0x0000,0x0000,0x0000}
+#define CONST1 (temp_real_unaligned) {0x0000,0x0000,0x0000,0x8000,0x3FFF}
+#define CONSTPI (temp_real_unaligned) {0xC235,0x2168,0xDAA2,0xC90F,0x4000}
+#define CONSTLN2 (temp_real_unaligned) {0x79AC,0xD1CF,0x17F7,0xB172,0x3FFE}
+#define CONSTLG2 (temp_real_unaligned) {0xF799,0xFBCF,0x9A84,0x9A20,0x3FFD}
+#define CONSTL2E (temp_real_unaligned) {0xF0BC,0x5C17,0x3B29,0xB8AA,0x3FFF}
+#define CONSTL2T (temp_real_unaligned) {0x8AFE,0xCD1B,0x784B,0xD49A,0x4000}
+
+#define set_IE() (I387.swd |= 1)
+#define set_DE() (I387.swd |= 2)
+#define set_ZE() (I387.swd |= 4)
+#define set_OE() (I387.swd |= 8)
+#define set_UE() (I387.swd |= 16)
+#define set_PE() (I387.swd |= 32)
+
+#define set_C0() (I387.swd |= 0x0100)
+#define set_C1() (I387.swd |= 0x0200)
+#define set_C2() (I387.swd |= 0x0400)
+#define set_C3() (I387.swd |= 0x4000)
+
+/* ea.c */
+
+char * ea(struct trapframe *, unsigned short);
+
+/* convert.c */
+
+void frndint(const temp_real * __a, temp_real * __b);
+void Fscale(const temp_real *, const temp_real *, temp_real *);
+void short_to_temp(const short_real * __a, temp_real * __b);
+void long_to_temp(const long_real * __a, temp_real * __b);
+void temp_to_short(const temp_real * __a, short_real * __b);
+void temp_to_long(const temp_real * __a, long_real * __b);
+void real_to_int(const temp_real * __a, temp_int * __b);
+void int_to_real(const temp_int * __a, temp_real * __b);
+
+/* get_put.c */
+
+void get_short_real(temp_real *, struct trapframe *, unsigned short);
+void get_long_real(temp_real *, struct trapframe *, unsigned short);
+void get_temp_real(temp_real *, struct trapframe *, unsigned short);
+void get_short_int(temp_real *, struct trapframe *, unsigned short);
+void get_long_int(temp_real *, struct trapframe *, unsigned short);
+void get_longlong_int(temp_real *, struct trapframe *, unsigned short);
+void get_BCD(temp_real *, struct trapframe *, unsigned short);
+void put_short_real(const temp_real *, struct trapframe *, unsigned short);
+void put_long_real(const temp_real *, struct trapframe *, unsigned short);
+void put_temp_real(const temp_real *, struct trapframe *, unsigned short);
+void put_short_int(const temp_real *, struct trapframe *, unsigned short);
+void put_long_int(const temp_real *, struct trapframe *, unsigned short);
+void put_longlong_int(const temp_real *, struct trapframe *, unsigned short);
+void put_BCD(const temp_real *, struct trapframe *, unsigned short);
+
+/* add.c */
+
+void fadd(const temp_real *, const temp_real *, temp_real *);
+
+/* mul.c */
+
+void fmul(const temp_real *, const temp_real *, temp_real *);
+
+/* div.c */
+
+void fdiv(const temp_real *, const temp_real *, temp_real *);
+
+/* compare.c */
+
+void fcom(const temp_real *, const temp_real *);
+void fucom(const temp_real *, const temp_real *);
+void ftst(const temp_real *);
+
+#endif
diff --git a/sys/i386/i386/math_emulate.c b/sys/i386/i386/math_emulate.c
new file mode 100644
index 0000000..5013123
--- /dev/null
+++ b/sys/i386/i386/math_emulate.c
@@ -0,0 +1,1476 @@
+/*
+ * linux/kernel/math/math_emulate.c
+ *
+ * (C) 1991 Linus Torvalds
+ *
+ * [expediant "port" of linux 8087 emulator to 386BSD, with apologies -wfj]
+ *
+ * from: 386BSD 0.1
+ * $Id: math_emulate.c,v 1.7 1994/01/29 22:07:16 nate Exp $
+ */
+
+/*
+ * Limited emulation 27.12.91 - mostly loads/stores, which gcc wants
+ * even for soft-float, unless you use bruce evans' patches. The patches
+ * are great, but they have to be re-applied for every version, and the
+ * library is different for soft-float and 80387. So emulation is more
+ * practical, even though it's slower.
+ *
+ * 28.12.91 - loads/stores work, even BCD. I'll have to start thinking
+ * about add/sub/mul/div. Urgel. I should find some good source, but I'll
+ * just fake up something.
+ *
+ * 30.12.91 - add/sub/mul/div/com seem to work mostly. I should really
+ * test every possible combination.
+ */
+
+/*
+ * This file is full of ugly macros etc: one problem was that gcc simply
+ * didn't want to make the structures as they should be: it has to try to
+ * align them. Sickening code, but at least I've hidden the ugly things
+ * in this one file: the other files don't need to know about these things.
+ *
+ * The other files also don't care about ST(x) etc - they just get addresses
+ * to 80-bit temporary reals, and do with them as they please. I wanted to
+ * hide most of the 387-specific things here.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <machine/cpu.h>
+#include <machine/psl.h>
+#include <machine/reg.h>
+
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/acct.h>
+#include <sys/kernel.h>
+#include <sys/signal.h>
+
+#define __ALIGNED_TEMP_REAL 1
+#include "math_emu.h"
+
+#define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x))
+#define ST(x) (*__st((x)))
+#define PST(x) ((const temp_real *) __st((x)))
+#define math_abort(tfp, signo) tfp->tf_eip = oldeip; return (signo);
+
+/*
+ * We don't want these inlined - it gets too messy in the machine-code.
+ */
+static void fpop(void);
+static void fpush(void);
+static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b);
+static temp_real_unaligned * __st(int i);
+
+unsigned char get_fs_byte(char *adr) { return(fubyte(adr)); }
+unsigned short get_fs_word(unsigned short *adr) { return(fuword(adr)); }
+unsigned long get_fs_long(unsigned long *adr) { return(fuword(adr)); }
+void put_fs_byte(unsigned char val, char *adr) { (void)subyte(adr,val); }
+void put_fs_word(unsigned short val, short *adr) { (void)susword(adr,val); }
+void put_fs_long(u_long val, unsigned long *adr) { (void)suword(adr,val); }
+
+int
+math_emulate(struct trapframe * info)
+{
+ unsigned short code;
+ temp_real tmp;
+ char * address;
+ u_long oldeip;
+
+ /* ever used fp? */
+ if ((((struct pcb *)curproc->p_addr)->pcb_flags & FP_SOFTFP) == 0) {
+ ((struct pcb *)curproc->p_addr)->pcb_flags |= FP_SOFTFP;
+ I387.cwd = 0x037f;
+ I387.swd = 0x0000;
+ I387.twd = 0x0000;
+ }
+
+ if (I387.cwd & I387.swd & 0x3f)
+ I387.swd |= 0x8000;
+ else
+ I387.swd &= 0x7fff;
+ oldeip = info->tf_eip;
+/* 0x001f means user code space */
+ if ((u_short)info->tf_cs != 0x001F) {
+ printf("math_emulate: %04x:%08x\n\r", (u_short)info->tf_cs,
+ oldeip);
+ panic("?Math emulation needed in kernel?");
+ }
+ code = get_fs_word((unsigned short *) oldeip);
+ bswapw(code);
+ code &= 0x7ff;
+ I387.fip = oldeip;
+ *(unsigned short *) &I387.fcs = (u_short) info->tf_cs;
+ *(1+(unsigned short *) &I387.fcs) = code;
+ info->tf_eip += 2;
+ switch (code) {
+ case 0x1d0: /* fnop */
+ return(0);
+ case 0x1d1: case 0x1d2: case 0x1d3:
+ case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7:
+ math_abort(info,SIGILL);
+ case 0x1e0: /* fchs */
+ ST(0).exponent ^= 0x8000;
+ return(0);
+ case 0x1e1: /* fabs */
+ ST(0).exponent &= 0x7fff;
+ return(0);
+ case 0x1e2: case 0x1e3:
+ math_abort(info,SIGILL);
+ case 0x1e4: /* fxtract */
+ ftst(PST(0)); /* ?????? */
+ return(0);
+ case 0x1e5: /* fxam */
+ printf("fxam not implemented\n\r");
+ math_abort(info,SIGILL);
+ case 0x1e6: case 0x1e7:
+ math_abort(info,SIGILL);
+ case 0x1e8: /* fld1 */
+ fpush();
+ ST(0) = CONST1;
+ return(0);
+ case 0x1e9: /* fld2t */
+ fpush();
+ ST(0) = CONSTL2T;
+ return(0);
+ case 0x1ea: /* fld2e */
+ fpush();
+ ST(0) = CONSTL2E;
+ return(0);
+ case 0x1eb: /* fldpi */
+ fpush();
+ ST(0) = CONSTPI;
+ return(0);
+ case 0x1ec: /* fldlg2 */
+ fpush();
+ ST(0) = CONSTLG2;
+ return(0);
+ case 0x1ed: /* fldln2 */
+ fpush();
+ ST(0) = CONSTLN2;
+ return(0);
+ case 0x1ee: /* fldz */
+ fpush();
+ ST(0) = CONSTZ;
+ return(0);
+ case 0x1ef:
+ math_abort(info,SIGILL);
+ case 0x1f0: case 0x1f1: case 0x1f2: case 0x1f3:
+ case 0x1f4: case 0x1f5: case 0x1f6: case 0x1f7:
+ case 0x1f8: case 0x1f9: case 0x1fa: case 0x1fb:
+ case 0x1fe: case 0x1ff:
+ uprintf(
+ "math_emulate: instruction %04x not implemented\n",
+ code + 0xd800);
+ math_abort(info,SIGILL);
+ case 0x1fc: /* frndint */
+ frndint(PST(0),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ case 0x1fd: /* fscale */
+ /* incomplete and totally inadequate -wfj */
+ Fscale(PST(0), PST(1), &tmp);
+ real_to_real(&tmp,&ST(0));
+ return(0); /* 19 Sep 92*/
+ case 0x2e9: /* ????? */
+/* if this should be a fucomp ST(0),ST(1) , it must be a 0x3e9 ATS */
+ fucom(PST(1),PST(0));
+ fpop(); fpop();
+ return(0);
+ case 0x3d0: case 0x3d1: /* fist ?? */
+ return(0);
+ case 0x3e2: /* fclex */
+ I387.swd &= 0x7f00;
+ return(0);
+ case 0x3e3: /* fninit */
+ I387.cwd = 0x037f;
+ I387.swd = 0x0000;
+ I387.twd = 0x0000;
+ return(0);
+ case 0x3e4:
+ return(0);
+ case 0x6d9: /* fcompp */
+ fcom(PST(1),PST(0));
+ fpop(); fpop();
+ return(0);
+ case 0x7e0: /* fstsw ax */
+ *(short *) &info->tf_eax = I387.swd;
+ return(0);
+ }
+ switch (code >> 3) {
+ case 0x18: /* fadd */
+ fadd(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ case 0x19: /* fmul */
+ fmul(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ case 0x1a: /* fcom */
+ fcom(PST(code & 7),PST(0));
+ return(0);
+ case 0x1b: /* fcomp */
+ fcom(PST(code & 7),PST(0));
+ fpop();
+ return(0);
+ case 0x1c: /* fsubr */
+ real_to_real(&ST(code & 7),&tmp);
+ tmp.exponent ^= 0x8000;
+ fadd(PST(0),&tmp,&tmp);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ case 0x1d: /* fsub */
+ ST(0).exponent ^= 0x8000;
+ fadd(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ case 0x1e: /* fdivr */
+ fdiv(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ case 0x1f: /* fdiv */
+ fdiv(PST(code & 7),PST(0),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ case 0x38: /* fld */
+ fpush();
+ ST(0) = ST((code & 7)+1); /* why plus 1 ????? ATS */
+ return(0);
+ case 0x39: /* fxch */
+ fxchg(&ST(0),&ST(code & 7));
+ return(0);
+ case 0x3b: /* ??? ??? wrong ???? ATS */
+ ST(code & 7) = ST(0);
+ fpop();
+ return(0);
+ case 0x98: /* fadd */
+ fadd(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ return(0);
+ case 0x99: /* fmul */
+ fmul(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ return(0);
+ case 0x9a: /* ???? , my manual don't list a direction bit
+for fcom , ??? ATS */
+ fcom(PST(code & 7),PST(0));
+ return(0);
+ case 0x9b: /* same as above , ATS */
+ fcom(PST(code & 7),PST(0));
+ fpop();
+ return(0);
+ case 0x9c: /* fsubr */
+ ST(code & 7).exponent ^= 0x8000;
+ fadd(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ return(0);
+ case 0x9d: /* fsub */
+ real_to_real(&ST(0),&tmp);
+ tmp.exponent ^= 0x8000;
+ fadd(PST(code & 7),&tmp,&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ return(0);
+ case 0x9e: /* fdivr */
+ fdiv(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ return(0);
+ case 0x9f: /* fdiv */
+ fdiv(PST(code & 7),PST(0),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ return(0);
+ case 0xb8: /* ffree */
+ printf("ffree not implemented\n\r");
+ math_abort(info,SIGILL);
+ case 0xb9: /* fstp ???? where is the pop ? ATS */
+ fxchg(&ST(0),&ST(code & 7));
+ return(0);
+ case 0xba: /* fst */
+ ST(code & 7) = ST(0);
+ return(0);
+ case 0xbb: /* ????? encoding of fstp to mem ? ATS */
+ ST(code & 7) = ST(0);
+ fpop();
+ return(0);
+ case 0xbc: /* fucom */
+ fucom(PST(code & 7),PST(0));
+ return(0);
+ case 0xbd: /* fucomp */
+ fucom(PST(code & 7),PST(0));
+ fpop();
+ return(0);
+ case 0xd8: /* faddp */
+ fadd(PST(code & 7),PST(0),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ fpop();
+ return(0);
+ case 0xd9: /* fmulp */
+ fmul(PST(code & 7),PST(0),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ fpop();
+ return(0);
+ case 0xda: /* ??? encoding of ficom with 16 bit mem ? ATS */
+ fcom(PST(code & 7),PST(0));
+ fpop();
+ return(0);
+ case 0xdc: /* fsubrp */
+ ST(code & 7).exponent ^= 0x8000;
+ fadd(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ fpop();
+ return(0);
+ case 0xdd: /* fsubp */
+ real_to_real(&ST(0),&tmp);
+ tmp.exponent ^= 0x8000;
+ fadd(PST(code & 7),&tmp,&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ fpop();
+ return(0);
+ case 0xde: /* fdivrp */
+ fdiv(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ fpop();
+ return(0);
+ case 0xdf: /* fdivp */
+ fdiv(PST(code & 7),PST(0),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ fpop();
+ return(0);
+ case 0xf8: /* fild 16-bit mem ???? ATS */
+ printf("ffree not implemented\n\r");
+ math_abort(info,SIGILL);
+ fpop();
+ return(0);
+ case 0xf9: /* ????? ATS */
+ fxchg(&ST(0),&ST(code & 7));
+ return(0);
+ case 0xfa: /* fist 16-bit mem ? ATS */
+ case 0xfb: /* fistp 16-bit mem ? ATS */
+ ST(code & 7) = ST(0);
+ fpop();
+ return(0);
+ }
+ switch ((code>>3) & 0xe7) {
+ case 0x22:
+ put_short_real(PST(0),info,code);
+ return(0);
+ case 0x23:
+ put_short_real(PST(0),info,code);
+ fpop();
+ return(0);
+ case 0x24:
+ address = ea(info,code);
+ for (code = 0 ; code < 7 ; code++) {
+ ((long *) & I387)[code] =
+ get_fs_long((unsigned long *) address);
+ address += 4;
+ }
+ return(0);
+ case 0x25:
+ address = ea(info,code);
+ *(unsigned short *) &I387.cwd =
+ get_fs_word((unsigned short *) address);
+ return(0);
+ case 0x26:
+ address = ea(info,code);
+ /*verify_area(address,28);*/
+ for (code = 0 ; code < 7 ; code++) {
+ put_fs_long( ((long *) & I387)[code],
+ (unsigned long *) address);
+ address += 4;
+ }
+ return(0);
+ case 0x27:
+ address = ea(info,code);
+ /*verify_area(address,2);*/
+ put_fs_word(I387.cwd,(short *) address);
+ return(0);
+ case 0x62:
+ put_long_int(PST(0),info,code);
+ return(0);
+ case 0x63:
+ put_long_int(PST(0),info,code);
+ fpop();
+ return(0);
+ case 0x65:
+ fpush();
+ get_temp_real(&tmp,info,code);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ case 0x67:
+ put_temp_real(PST(0),info,code);
+ fpop();
+ return(0);
+ case 0xa2:
+ put_long_real(PST(0),info,code);
+ return(0);
+ case 0xa3:
+ put_long_real(PST(0),info,code);
+ fpop();
+ return(0);
+ case 0xa4:
+ address = ea(info,code);
+ for (code = 0 ; code < 27 ; code++) {
+ ((long *) & I387)[code] =
+ get_fs_long((unsigned long *) address);
+ address += 4;
+ }
+ return(0);
+ case 0xa6:
+ address = ea(info,code);
+ /*verify_area(address,108);*/
+ for (code = 0 ; code < 27 ; code++) {
+ put_fs_long( ((long *) & I387)[code],
+ (unsigned long *) address);
+ address += 4;
+ }
+ I387.cwd = 0x037f;
+ I387.swd = 0x0000;
+ I387.twd = 0x0000;
+ return(0);
+ case 0xa7:
+ address = ea(info,code);
+ /*verify_area(address,2);*/
+ put_fs_word(I387.swd,(short *) address);
+ return(0);
+ case 0xe2:
+ put_short_int(PST(0),info,code);
+ return(0);
+ case 0xe3:
+ put_short_int(PST(0),info,code);
+ fpop();
+ return(0);
+ case 0xe4:
+ fpush();
+ get_BCD(&tmp,info,code);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ case 0xe5:
+ fpush();
+ get_longlong_int(&tmp,info,code);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ case 0xe6:
+ put_BCD(PST(0),info,code);
+ fpop();
+ return(0);
+ case 0xe7:
+ put_longlong_int(PST(0),info,code);
+ fpop();
+ return(0);
+ }
+ switch (code >> 9) {
+ case 0:
+ get_short_real(&tmp,info,code);
+ break;
+ case 1:
+ get_long_int(&tmp,info,code);
+ break;
+ case 2:
+ get_long_real(&tmp,info,code);
+ break;
+ case 4:
+ get_short_int(&tmp,info,code);
+ }
+ switch ((code>>3) & 0x27) {
+ case 0:
+ fadd(&tmp,PST(0),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ case 1:
+ fmul(&tmp,PST(0),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ case 2:
+ fcom(&tmp,PST(0));
+ return(0);
+ case 3:
+ fcom(&tmp,PST(0));
+ fpop();
+ return(0);
+ case 4:
+ tmp.exponent ^= 0x8000;
+ fadd(&tmp,PST(0),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ case 5:
+ ST(0).exponent ^= 0x8000;
+ fadd(&tmp,PST(0),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ case 6:
+ fdiv(PST(0),&tmp,&tmp);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ case 7:
+ fdiv(&tmp,PST(0),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ }
+ if ((code & 0x138) == 0x100) {
+ fpush();
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ }
+ printf("Unknown math-insns: %04x:%08x %04x\n\r",(u_short)info->tf_cs,
+ info->tf_eip,code);
+ math_abort(info,SIGFPE);
+}
+
+static void fpop(void)
+{
+ unsigned long tmp;
+
+ tmp = I387.swd & 0xffffc7ffUL;
+ I387.swd += 0x00000800;
+ I387.swd &= 0x00003800;
+ I387.swd |= tmp;
+}
+
+static void fpush(void)
+{
+ unsigned long tmp;
+
+ tmp = I387.swd & 0xffffc7ffUL;
+ I387.swd += 0x00003800;
+ I387.swd &= 0x00003800;
+ I387.swd |= tmp;
+}
+
+static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b)
+{
+ temp_real_unaligned c;
+
+ c = *a;
+ *a = *b;
+ *b = c;
+}
+
+static temp_real_unaligned * __st(int i)
+{
+ i += I387.swd >> 11;
+ i &= 7;
+ return (temp_real_unaligned *) (i*10 + (char *)(I387.st_space));
+}
+
+/*
+ * linux/kernel/math/ea.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+/*
+ * Calculate the effective address.
+ */
+
+
+static int __regoffset[] = {
+ tEAX, tECX, tEDX, tEBX, tESP, tEBP, tESI, tEDI
+};
+
+#define REG(x) (curproc->p_md.md_regs[__regoffset[(x)]])
+
+static char * sib(struct trapframe * info, int mod)
+{
+ unsigned char ss,index,base;
+ long offset = 0;
+
+ base = get_fs_byte((char *) info->tf_eip);
+ info->tf_eip++;
+ ss = base >> 6;
+ index = (base >> 3) & 7;
+ base &= 7;
+ if (index == 4)
+ offset = 0;
+ else
+ offset = REG(index);
+ offset <<= ss;
+ if (mod || base != 5)
+ offset += REG(base);
+ if (mod == 1) {
+ offset += (signed char) get_fs_byte((char *) info->tf_eip);
+ info->tf_eip++;
+ } else if (mod == 2 || base == 5) {
+ offset += (signed) get_fs_long((unsigned long *) info->tf_eip);
+ info->tf_eip += 4;
+ }
+ I387.foo = offset;
+ I387.fos = 0x17;
+ return (char *) offset;
+}
+
+char * ea(struct trapframe * info, unsigned short code)
+{
+ unsigned char mod,rm;
+ long * tmp;
+ int offset = 0;
+
+ mod = (code >> 6) & 3;
+ rm = code & 7;
+ if (rm == 4 && mod != 3)
+ return sib(info,mod);
+ if (rm == 5 && !mod) {
+ offset = get_fs_long((unsigned long *) info->tf_eip);
+ info->tf_eip += 4;
+ I387.foo = offset;
+ I387.fos = 0x17;
+ return (char *) offset;
+ }
+ tmp = (long *) &REG(rm);
+ switch (mod) {
+ case 0: offset = 0; break;
+ case 1:
+ offset = (signed char) get_fs_byte((char *) info->tf_eip);
+ info->tf_eip++;
+ break;
+ case 2:
+ offset = (signed) get_fs_long((unsigned long *) info->tf_eip);
+ info->tf_eip += 4;
+ break;
+#ifdef notyet
+ case 3:
+ math_abort(info,1<<(SIGILL-1));
+#endif
+ }
+ I387.foo = offset;
+ I387.fos = 0x17;
+ return offset + (char *) *tmp;
+}
+/*
+ * linux/kernel/math/get_put.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+/*
+ * This file handles all accesses to user memory: getting and putting
+ * ints/reals/BCD etc. This is the only part that concerns itself with
+ * other than temporary real format. All other cals are strictly temp_real.
+ */
+
+void get_short_real(temp_real * tmp,
+ struct trapframe * info, unsigned short code)
+{
+ char * addr;
+ short_real sr;
+
+ addr = ea(info,code);
+ sr = get_fs_long((unsigned long *) addr);
+ short_to_temp(&sr,tmp);
+}
+
+void get_long_real(temp_real * tmp,
+ struct trapframe * info, unsigned short code)
+{
+ char * addr;
+ long_real lr;
+
+ addr = ea(info,code);
+ lr.a = get_fs_long((unsigned long *) addr);
+ lr.b = get_fs_long(1 + (unsigned long *) addr);
+ long_to_temp(&lr,tmp);
+}
+
+void get_temp_real(temp_real * tmp,
+ struct trapframe * info, unsigned short code)
+{
+ char * addr;
+
+ addr = ea(info,code);
+ tmp->a = get_fs_long((unsigned long *) addr);
+ tmp->b = get_fs_long(1 + (unsigned long *) addr);
+ tmp->exponent = get_fs_word(4 + (unsigned short *) addr);
+}
+
+void get_short_int(temp_real * tmp,
+ struct trapframe * info, unsigned short code)
+{
+ char * addr;
+ temp_int ti;
+
+ addr = ea(info,code);
+ ti.a = (signed short) get_fs_word((unsigned short *) addr);
+ ti.b = 0;
+ if (ti.sign = (ti.a < 0))
+ ti.a = - ti.a;
+ int_to_real(&ti,tmp);
+}
+
+void get_long_int(temp_real * tmp,
+ struct trapframe * info, unsigned short code)
+{
+ char * addr;
+ temp_int ti;
+
+ addr = ea(info,code);
+ ti.a = get_fs_long((unsigned long *) addr);
+ ti.b = 0;
+ if (ti.sign = (ti.a < 0))
+ ti.a = - ti.a;
+ int_to_real(&ti,tmp);
+}
+
+void get_longlong_int(temp_real * tmp,
+ struct trapframe * info, unsigned short code)
+{
+ char * addr;
+ temp_int ti;
+
+ addr = ea(info,code);
+ ti.a = get_fs_long((unsigned long *) addr);
+ ti.b = get_fs_long(1 + (unsigned long *) addr);
+ if (ti.sign = (ti.b < 0))
+ __asm__("notl %0 ; notl %1\n\t"
+ "addl $1,%0 ; adcl $0,%1"
+ :"=r" (ti.a),"=r" (ti.b)
+ :"0" (ti.a),"1" (ti.b));
+ int_to_real(&ti,tmp);
+}
+
+#define MUL10(low,high) \
+__asm__("addl %0,%0 ; adcl %1,%1\n\t" \
+"movl %0,%%ecx ; movl %1,%%ebx\n\t" \
+"addl %0,%0 ; adcl %1,%1\n\t" \
+"addl %0,%0 ; adcl %1,%1\n\t" \
+"addl %%ecx,%0 ; adcl %%ebx,%1" \
+:"=a" (low),"=d" (high) \
+:"0" (low),"1" (high):"cx","bx")
+
+#define ADD64(val,low,high) \
+__asm__("addl %4,%0 ; adcl $0,%1":"=r" (low),"=r" (high) \
+:"0" (low),"1" (high),"r" ((unsigned long) (val)))
+
+void get_BCD(temp_real * tmp, struct trapframe * info, unsigned short code)
+{
+ int k;
+ char * addr;
+ temp_int i;
+ unsigned char c;
+
+ addr = ea(info,code);
+ addr += 9;
+ i.sign = 0x80 & get_fs_byte(addr--);
+ i.a = i.b = 0;
+ for (k = 0; k < 9; k++) {
+ c = get_fs_byte(addr--);
+ MUL10(i.a, i.b);
+ ADD64((c>>4), i.a, i.b);
+ MUL10(i.a, i.b);
+ ADD64((c&0xf), i.a, i.b);
+ }
+ int_to_real(&i,tmp);
+}
+
+void put_short_real(const temp_real * tmp,
+ struct trapframe * info, unsigned short code)
+{
+ char * addr;
+ short_real sr;
+
+ addr = ea(info,code);
+ /*verify_area(addr,4);*/
+ temp_to_short(tmp,&sr);
+ put_fs_long(sr,(unsigned long *) addr);
+}
+
+void put_long_real(const temp_real * tmp,
+ struct trapframe * info, unsigned short code)
+{
+ char * addr;
+ long_real lr;
+
+ addr = ea(info,code);
+ /*verify_area(addr,8);*/
+ temp_to_long(tmp,&lr);
+ put_fs_long(lr.a, (unsigned long *) addr);
+ put_fs_long(lr.b, 1 + (unsigned long *) addr);
+}
+
+void put_temp_real(const temp_real * tmp,
+ struct trapframe * info, unsigned short code)
+{
+ char * addr;
+
+ addr = ea(info,code);
+ /*verify_area(addr,10);*/
+ put_fs_long(tmp->a, (unsigned long *) addr);
+ put_fs_long(tmp->b, 1 + (unsigned long *) addr);
+ put_fs_word(tmp->exponent, 4 + (short *) addr);
+}
+
+void put_short_int(const temp_real * tmp,
+ struct trapframe * info, unsigned short code)
+{
+ char * addr;
+ temp_int ti;
+
+ addr = ea(info,code);
+ real_to_int(tmp,&ti);
+ /*verify_area(addr,2);*/
+ if (ti.sign)
+ ti.a = -ti.a;
+ put_fs_word(ti.a,(short *) addr);
+}
+
+void put_long_int(const temp_real * tmp,
+ struct trapframe * info, unsigned short code)
+{
+ char * addr;
+ temp_int ti;
+
+ addr = ea(info,code);
+ real_to_int(tmp,&ti);
+ /*verify_area(addr,4);*/
+ if (ti.sign)
+ ti.a = -ti.a;
+ put_fs_long(ti.a,(unsigned long *) addr);
+}
+
+void put_longlong_int(const temp_real * tmp,
+ struct trapframe * info, unsigned short code)
+{
+ char * addr;
+ temp_int ti;
+
+ addr = ea(info,code);
+ real_to_int(tmp,&ti);
+ /*verify_area(addr,8);*/
+ if (ti.sign)
+ __asm__("notl %0 ; notl %1\n\t"
+ "addl $1,%0 ; adcl $0,%1"
+ :"=r" (ti.a),"=r" (ti.b)
+ :"0" (ti.a),"1" (ti.b));
+ put_fs_long(ti.a,(unsigned long *) addr);
+ put_fs_long(ti.b,1 + (unsigned long *) addr);
+}
+
+#define DIV10(low,high,rem) \
+__asm__("divl %6 ; xchgl %1,%2 ; divl %6" \
+ :"=d" (rem),"=a" (low),"=r" (high) \
+ :"0" (0),"1" (high),"2" (low),"c" (10))
+
+void put_BCD(const temp_real * tmp,struct trapframe * info, unsigned short code)
+{
+ int k,rem;
+ char * addr;
+ temp_int i;
+ unsigned char c;
+
+ addr = ea(info,code);
+ /*verify_area(addr,10);*/
+ real_to_int(tmp,&i);
+ if (i.sign)
+ put_fs_byte(0x80, addr+9);
+ else
+ put_fs_byte(0, addr+9);
+ for (k = 0; k < 9; k++) {
+ DIV10(i.a,i.b,rem);
+ c = rem;
+ DIV10(i.a,i.b,rem);
+ c += rem<<4;
+ put_fs_byte(c,addr++);
+ }
+}
+
+/*
+ * linux/kernel/math/mul.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+/*
+ * temporary real multiplication routine.
+ */
+
+
+static void shift(int * c)
+{
+ __asm__("movl (%0),%%eax ; addl %%eax,(%0)\n\t"
+ "movl 4(%0),%%eax ; adcl %%eax,4(%0)\n\t"
+ "movl 8(%0),%%eax ; adcl %%eax,8(%0)\n\t"
+ "movl 12(%0),%%eax ; adcl %%eax,12(%0)"
+ ::"r" ((long) c):"ax");
+}
+
+static void mul64(const temp_real * a, const temp_real * b, int * c)
+{
+ __asm__("movl (%0),%%eax\n\t"
+ "mull (%1)\n\t"
+ "movl %%eax,(%2)\n\t"
+ "movl %%edx,4(%2)\n\t"
+ "movl 4(%0),%%eax\n\t"
+ "mull 4(%1)\n\t"
+ "movl %%eax,8(%2)\n\t"
+ "movl %%edx,12(%2)\n\t"
+ "movl (%0),%%eax\n\t"
+ "mull 4(%1)\n\t"
+ "addl %%eax,4(%2)\n\t"
+ "adcl %%edx,8(%2)\n\t"
+ "adcl $0,12(%2)\n\t"
+ "movl 4(%0),%%eax\n\t"
+ "mull (%1)\n\t"
+ "addl %%eax,4(%2)\n\t"
+ "adcl %%edx,8(%2)\n\t"
+ "adcl $0,12(%2)"
+ ::"S" ((long) a),"c" ((long) b),"D" ((long) c)
+ :"ax","dx");
+}
+
+void fmul(const temp_real * src1, const temp_real * src2, temp_real * result)
+{
+ int i,sign;
+ int tmp[4] = {0,0,0,0};
+
+ sign = (src1->exponent ^ src2->exponent) & 0x8000;
+ i = (src1->exponent & 0x7fff) + (src2->exponent & 0x7fff) - 16383 + 1;
+ if (i<0) {
+ result->exponent = sign;
+ result->a = result->b = 0;
+ return;
+ }
+ if (i>0x7fff) {
+ set_OE();
+ return;
+ }
+ mul64(src1,src2,tmp);
+ if (tmp[0] || tmp[1] || tmp[2] || tmp[3])
+ while (i && tmp[3] >= 0) {
+ i--;
+ shift(tmp);
+ }
+ else
+ i = 0;
+ result->exponent = i | sign;
+ result->a = tmp[2];
+ result->b = tmp[3];
+}
+
+/*
+ * linux/kernel/math/div.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+/*
+ * temporary real division routine.
+ */
+
+static void shift_left(int * c)
+{
+ __asm__ __volatile__("movl (%0),%%eax ; addl %%eax,(%0)\n\t"
+ "movl 4(%0),%%eax ; adcl %%eax,4(%0)\n\t"
+ "movl 8(%0),%%eax ; adcl %%eax,8(%0)\n\t"
+ "movl 12(%0),%%eax ; adcl %%eax,12(%0)"
+ ::"r" ((long) c):"ax");
+}
+
+static void shift_right(int * c)
+{
+ __asm__("shrl $1,12(%0) ; rcrl $1,8(%0) ; rcrl $1,4(%0) ; rcrl $1,(%0)"
+ ::"r" ((long) c));
+}
+
+static int try_sub(int * a, int * b)
+{
+ char ok;
+
+ __asm__ __volatile__("movl (%1),%%eax ; subl %%eax,(%2)\n\t"
+ "movl 4(%1),%%eax ; sbbl %%eax,4(%2)\n\t"
+ "movl 8(%1),%%eax ; sbbl %%eax,8(%2)\n\t"
+ "movl 12(%1),%%eax ; sbbl %%eax,12(%2)\n\t"
+ "setae %%al":"=a" (ok):"c" ((long) a),"d" ((long) b));
+ return ok;
+}
+
+static void div64(int * a, int * b, int * c)
+{
+ int tmp[4];
+ int i;
+ unsigned int mask = 0;
+
+ c += 4;
+ for (i = 0 ; i<64 ; i++) {
+ if (!(mask >>= 1)) {
+ c--;
+ mask = 0x80000000UL;
+ }
+ tmp[0] = a[0]; tmp[1] = a[1];
+ tmp[2] = a[2]; tmp[3] = a[3];
+ if (try_sub(b,tmp)) {
+ *c |= mask;
+ a[0] = tmp[0]; a[1] = tmp[1];
+ a[2] = tmp[2]; a[3] = tmp[3];
+ }
+ shift_right(b);
+ }
+}
+
+void fdiv(const temp_real * src1, const temp_real * src2, temp_real * result)
+{
+ int i,sign;
+ int a[4],b[4],tmp[4] = {0,0,0,0};
+
+ sign = (src1->exponent ^ src2->exponent) & 0x8000;
+ if (!(src2->a || src2->b)) {
+ set_ZE();
+ return;
+ }
+ i = (src1->exponent & 0x7fff) - (src2->exponent & 0x7fff) + 16383;
+ if (i<0) {
+ set_UE();
+ result->exponent = sign;
+ result->a = result->b = 0;
+ return;
+ }
+ a[0] = a[1] = 0;
+ a[2] = src1->a;
+ a[3] = src1->b;
+ b[0] = b[1] = 0;
+ b[2] = src2->a;
+ b[3] = src2->b;
+ while (b[3] >= 0) {
+ i++;
+ shift_left(b);
+ }
+ div64(a,b,tmp);
+ if (tmp[0] || tmp[1] || tmp[2] || tmp[3]) {
+ while (i && tmp[3] >= 0) {
+ i--;
+ shift_left(tmp);
+ }
+ if (tmp[3] >= 0)
+ set_DE();
+ } else
+ i = 0;
+ if (i>0x7fff) {
+ set_OE();
+ return;
+ }
+ if (tmp[0] || tmp[1])
+ set_PE();
+ result->exponent = i | sign;
+ result->a = tmp[2];
+ result->b = tmp[3];
+}
+
+/*
+ * linux/kernel/math/add.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+/*
+ * temporary real addition routine.
+ *
+ * NOTE! These aren't exact: they are only 62 bits wide, and don't do
+ * correct rounding. Fast hack. The reason is that we shift right the
+ * values by two, in order not to have overflow (1 bit), and to be able
+ * to move the sign into the mantissa (1 bit). Much simpler algorithms,
+ * and 62 bits (61 really - no rounding) accuracy is usually enough. The
+ * only time you should notice anything weird is when adding 64-bit
+ * integers together. When using doubles (52 bits accuracy), the
+ * 61-bit accuracy never shows at all.
+ */
+
+#define NEGINT(a) \
+__asm__("notl %0 ; notl %1 ; addl $1,%0 ; adcl $0,%1" \
+ :"=r" (a->a),"=r" (a->b) \
+ :"0" (a->a),"1" (a->b))
+
+static void signify(temp_real * a)
+{
+ a->exponent += 2;
+ __asm__("shrdl $2,%1,%0 ; shrl $2,%1"
+ :"=r" (a->a),"=r" (a->b)
+ :"0" (a->a),"1" (a->b));
+ if (a->exponent < 0)
+ NEGINT(a);
+ a->exponent &= 0x7fff;
+}
+
+static void unsignify(temp_real * a)
+{
+ if (!(a->a || a->b)) {
+ a->exponent = 0;
+ return;
+ }
+ a->exponent &= 0x7fff;
+ if (a->b < 0) {
+ NEGINT(a);
+ a->exponent |= 0x8000;
+ }
+ while (a->b >= 0) {
+ a->exponent--;
+ __asm__("addl %0,%0 ; adcl %1,%1"
+ :"=r" (a->a),"=r" (a->b)
+ :"0" (a->a),"1" (a->b));
+ }
+}
+
+void fadd(const temp_real * src1, const temp_real * src2, temp_real * result)
+{
+ temp_real a,b;
+ int x1,x2,shift;
+
+ x1 = src1->exponent & 0x7fff;
+ x2 = src2->exponent & 0x7fff;
+ if (x1 > x2) {
+ a = *src1;
+ b = *src2;
+ shift = x1-x2;
+ } else {
+ a = *src2;
+ b = *src1;
+ shift = x2-x1;
+ }
+ if (shift >= 64) {
+ *result = a;
+ return;
+ }
+ if (shift >= 32) {
+ b.a = b.b;
+ b.b = 0;
+ shift -= 32;
+ }
+ __asm__("shrdl %4,%1,%0 ; shrl %4,%1"
+ :"=r" (b.a),"=r" (b.b)
+ :"0" (b.a),"1" (b.b),"c" ((char) shift));
+ signify(&a);
+ signify(&b);
+ __asm__("addl %4,%0 ; adcl %5,%1"
+ :"=r" (a.a),"=r" (a.b)
+ :"0" (a.a),"1" (a.b),"g" (b.a),"g" (b.b));
+ unsignify(&a);
+ *result = a;
+}
+
+/*
+ * linux/kernel/math/compare.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+/*
+ * temporary real comparison routines
+ */
+
+
+#define clear_Cx() (I387.swd &= ~0x4500)
+
+static void normalize(temp_real * a)
+{
+ int i = a->exponent & 0x7fff;
+ int sign = a->exponent & 0x8000;
+
+ if (!(a->a || a->b)) {
+ a->exponent = 0;
+ return;
+ }
+ while (i && a->b >= 0) {
+ i--;
+ __asm__("addl %0,%0 ; adcl %1,%1"
+ :"=r" (a->a),"=r" (a->b)
+ :"0" (a->a),"1" (a->b));
+ }
+ a->exponent = i | sign;
+}
+
+void ftst(const temp_real * a)
+{
+ temp_real b;
+
+ clear_Cx();
+ b = *a;
+ normalize(&b);
+ if (b.a || b.b || b.exponent) {
+ if (b.exponent < 0)
+ set_C0();
+ } else
+ set_C3();
+}
+
+void fcom(const temp_real * src1, const temp_real * src2)
+{
+ temp_real a;
+
+ a = *src1;
+ a.exponent ^= 0x8000;
+ fadd(&a,src2,&a);
+ ftst(&a);
+}
+
+void fucom(const temp_real * src1, const temp_real * src2)
+{
+ fcom(src1,src2);
+}
+
+/*
+ * linux/kernel/math/convert.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+
+/*
+ * NOTE!!! There is some "non-obvious" optimisations in the temp_to_long
+ * and temp_to_short conversion routines: don't touch them if you don't
+ * know what's going on. They are the adding of one in the rounding: the
+ * overflow bit is also used for adding one into the exponent. Thus it
+ * looks like the overflow would be incorrectly handled, but due to the
+ * way the IEEE numbers work, things are correct.
+ *
+ * There is no checking for total overflow in the conversions, though (ie
+ * if the temp-real number simply won't fit in a short- or long-real.)
+ */
+
+void short_to_temp(const short_real * a, temp_real * b)
+{
+ if (!(*a & 0x7fffffff)) {
+ b->a = b->b = 0;
+ if (*a)
+ b->exponent = 0x8000;
+ else
+ b->exponent = 0;
+ return;
+ }
+ b->exponent = ((*a>>23) & 0xff)-127+16383;
+ if (*a<0)
+ b->exponent |= 0x8000;
+ b->b = (*a<<8) | 0x80000000UL;
+ b->a = 0;
+}
+
+void long_to_temp(const long_real * a, temp_real * b)
+{
+ if (!a->a && !(a->b & 0x7fffffff)) {
+ b->a = b->b = 0;
+ if (a->b)
+ b->exponent = 0x8000;
+ else
+ b->exponent = 0;
+ return;
+ }
+ b->exponent = ((a->b >> 20) & 0x7ff)-1023+16383;
+ if (a->b<0)
+ b->exponent |= 0x8000;
+ b->b = 0x80000000UL | (a->b<<11) | (((unsigned long)a->a)>>21);
+ b->a = a->a<<11;
+}
+
+void temp_to_short(const temp_real * a, short_real * b)
+{
+ if (!(a->exponent & 0x7fff)) {
+ *b = (a->exponent)?0x80000000UL:0;
+ return;
+ }
+ *b = ((((long) a->exponent)-16383+127) << 23) & 0x7f800000;
+ if (a->exponent < 0)
+ *b |= 0x80000000UL;
+ *b |= (a->b >> 8) & 0x007fffff;
+ switch ((int)ROUNDING) {
+ case ROUND_NEAREST:
+ if ((a->b & 0xff) > 0x80)
+ ++*b;
+ break;
+ case ROUND_DOWN:
+ if ((a->exponent & 0x8000) && (a->b & 0xff))
+ ++*b;
+ break;
+ case ROUND_UP:
+ if (!(a->exponent & 0x8000) && (a->b & 0xff))
+ ++*b;
+ break;
+ }
+}
+
+void temp_to_long(const temp_real * a, long_real * b)
+{
+ if (!(a->exponent & 0x7fff)) {
+ b->a = 0;
+ b->b = (a->exponent)?0x80000000UL:0;
+ return;
+ }
+ b->b = (((0x7fff & (long) a->exponent)-16383+1023) << 20) & 0x7ff00000;
+ if (a->exponent < 0)
+ b->b |= 0x80000000UL;
+ b->b |= (a->b >> 11) & 0x000fffff;
+ b->a = a->b << 21;
+ b->a |= (a->a >> 11) & 0x001fffff;
+ switch ((int)ROUNDING) {
+ case ROUND_NEAREST:
+ if ((a->a & 0x7ff) > 0x400)
+ __asm__("addl $1,%0 ; adcl $0,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b));
+ break;
+ case ROUND_DOWN:
+ if ((a->exponent & 0x8000) && (a->b & 0xff))
+ __asm__("addl $1,%0 ; adcl $0,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b));
+ break;
+ case ROUND_UP:
+ if (!(a->exponent & 0x8000) && (a->b & 0xff))
+ __asm__("addl $1,%0 ; adcl $0,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b));
+ break;
+ }
+}
+
+void frndint(const temp_real * a, temp_real * b)
+{
+ int shift = 16383 + 63 - (a->exponent & 0x7fff);
+ unsigned long underflow;
+
+ if ((shift < 0) || (shift == 16383+63)) {
+ *b = *a;
+ return;
+ }
+ b->a = b->b = underflow = 0;
+ b->exponent = a->exponent;
+ if (shift < 32) {
+ b->b = a->b; b->a = a->a;
+ } else if (shift < 64) {
+ b->a = a->b; underflow = a->a;
+ shift -= 32;
+ b->exponent += 32;
+ } else if (shift < 96) {
+ underflow = a->b;
+ shift -= 64;
+ b->exponent += 64;
+ } else {
+ underflow = 1;
+ shift = 0;
+ }
+ b->exponent += shift;
+ __asm__("shrdl %2,%1,%0"
+ :"=r" (underflow),"=r" (b->a)
+ :"c" ((char) shift),"0" (underflow),"1" (b->a));
+ __asm__("shrdl %2,%1,%0"
+ :"=r" (b->a),"=r" (b->b)
+ :"c" ((char) shift),"0" (b->a),"1" (b->b));
+ __asm__("shrl %1,%0"
+ :"=r" (b->b)
+ :"c" ((char) shift),"0" (b->b));
+ switch ((int)ROUNDING) {
+ case ROUND_NEAREST:
+ __asm__("addl %4,%5 ; adcl $0,%0 ; adcl $0,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b)
+ ,"r" (0x7fffffff + (b->a & 1))
+ ,"m" (*&underflow));
+ break;
+ case ROUND_UP:
+ if ((b->exponent >= 0) && underflow)
+ __asm__("addl $1,%0 ; adcl $0,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b));
+ break;
+ case ROUND_DOWN:
+ if ((b->exponent < 0) && underflow)
+ __asm__("addl $1,%0 ; adcl $0,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b));
+ break;
+ }
+ if (b->a || b->b)
+ while (b->b >= 0) {
+ b->exponent--;
+ __asm__("addl %0,%0 ; adcl %1,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b));
+ }
+ else
+ b->exponent = 0;
+}
+
+void Fscale(const temp_real *a, const temp_real *b, temp_real *c)
+{
+ temp_int ti;
+
+ *c = *a;
+ if(!c->a && !c->b) { /* 19 Sep 92*/
+ c->exponent = 0;
+ return;
+ }
+ real_to_int(b, &ti);
+ if(ti.sign)
+ c->exponent -= ti.a;
+ else
+ c->exponent += ti.a;
+}
+
+void real_to_int(const temp_real * a, temp_int * b)
+{
+ int shift = 16383 + 63 - (a->exponent & 0x7fff);
+ unsigned long underflow;
+
+ b->a = b->b = underflow = 0;
+ b->sign = (a->exponent < 0);
+ if (shift < 0) {
+ set_OE();
+ return;
+ }
+ if (shift < 32) {
+ b->b = a->b; b->a = a->a;
+ } else if (shift < 64) {
+ b->a = a->b; underflow = a->a;
+ shift -= 32;
+ } else if (shift < 96) {
+ underflow = a->b;
+ shift -= 64;
+ } else {
+ underflow = 1;
+ shift = 0;
+ }
+ __asm__("shrdl %2,%1,%0"
+ :"=r" (underflow),"=r" (b->a)
+ :"c" ((char) shift),"0" (underflow),"1" (b->a));
+ __asm__("shrdl %2,%1,%0"
+ :"=r" (b->a),"=r" (b->b)
+ :"c" ((char) shift),"0" (b->a),"1" (b->b));
+ __asm__("shrl %1,%0"
+ :"=r" (b->b)
+ :"c" ((char) shift),"0" (b->b));
+ switch ((int)ROUNDING) {
+ case ROUND_NEAREST:
+ __asm__("addl %4,%5 ; adcl $0,%0 ; adcl $0,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b)
+ ,"r" (0x7fffffff + (b->a & 1))
+ ,"m" (*&underflow));
+ break;
+ case ROUND_UP:
+ if (!b->sign && underflow)
+ __asm__("addl $1,%0 ; adcl $0,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b));
+ break;
+ case ROUND_DOWN:
+ if (b->sign && underflow)
+ __asm__("addl $1,%0 ; adcl $0,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b));
+ break;
+ }
+}
+
+void int_to_real(const temp_int * a, temp_real * b)
+{
+ b->a = a->a;
+ b->b = a->b;
+ if (b->a || b->b)
+ b->exponent = 16383 + 63 + (a->sign? 0x8000:0);
+ else {
+ b->exponent = 0;
+ return;
+ }
+ while (b->b >= 0) {
+ b->exponent--;
+ __asm__("addl %0,%0 ; adcl %1,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b));
+ }
+}
diff --git a/sys/i386/i386/mem.c b/sys/i386/i386/mem.c
new file mode 100644
index 0000000..1b8f187
--- /dev/null
+++ b/sys/i386/i386/mem.c
@@ -0,0 +1,259 @@
+/*-
+ * Copyright (c) 1988 University of Utah.
+ * Copyright (c) 1982, 1986, 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department, and code derived from software contributed to
+ * Berkeley by William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: Utah $Hdr: mem.c 1.13 89/10/08$
+ * from: @(#)mem.c 7.2 (Berkeley) 5/9/91
+ * $Id: mem.c,v 1.6 1993/12/19 00:50:06 wollman Exp $
+ */
+
+/*
+ * Memory special file
+ */
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/buf.h>
+#include <sys/systm.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <machine/cpu.h>
+#include <machine/psl.h>
+
+#include <vm/vm_param.h>
+#include <vm/lock.h>
+#include <vm/vm_prot.h>
+#include <vm/pmap.h>
+
+extern char *ptvmmap; /* poor name! */
+/*ARGSUSED*/
+int
+mmclose(dev, uio, flags)
+ dev_t dev;
+ struct uio *uio;
+ int flags;
+{
+ struct trapframe *fp;
+
+ switch (minor(dev)) {
+ case 14:
+ fp = (struct trapframe *)curproc->p_md.md_regs;
+ fp->tf_eflags &= ~PSL_IOPL;
+ break;
+ default:
+ break;
+ }
+ return(0);
+}
+/*ARGSUSED*/
+int
+mmopen(dev, uio, flags)
+ dev_t dev;
+ struct uio *uio;
+ int flags;
+{
+ struct trapframe *fp;
+
+ switch (minor(dev)) {
+ case 14:
+ fp = (struct trapframe *)curproc->p_md.md_regs;
+ fp->tf_eflags |= PSL_IOPL;
+ break;
+ default:
+ break;
+ }
+ return(0);
+}
+/*ARGSUSED*/
+int
+mmrw(dev, uio, flags)
+ dev_t dev;
+ struct uio *uio;
+ int flags;
+{
+ register int o;
+ register u_int c, v;
+ register struct iovec *iov;
+ int error = 0;
+ caddr_t zbuf = NULL;
+
+ while (uio->uio_resid > 0 && error == 0) {
+ iov = uio->uio_iov;
+ if (iov->iov_len == 0) {
+ uio->uio_iov++;
+ uio->uio_iovcnt--;
+ if (uio->uio_iovcnt < 0)
+ panic("mmrw");
+ continue;
+ }
+ switch (minor(dev)) {
+
+/* minor device 0 is physical memory */
+ case 0:
+ v = uio->uio_offset;
+ pmap_enter(kernel_pmap, (vm_offset_t)ptvmmap, v,
+ uio->uio_rw == UIO_READ ? VM_PROT_READ : VM_PROT_WRITE,
+ TRUE);
+ o = (int)uio->uio_offset & PGOFSET;
+ c = (u_int)(NBPG - ((int)iov->iov_base & PGOFSET));
+ c = min(c, (u_int)(NBPG - o));
+ c = min(c, (u_int)iov->iov_len);
+ error = uiomove((caddr_t)&ptvmmap[o], (int)c, uio);
+ pmap_remove(kernel_pmap, (vm_offset_t)ptvmmap,
+ (vm_offset_t)&ptvmmap[NBPG]);
+ continue;
+
+/* minor device 1 is kernel memory */
+ case 1:
+ c = iov->iov_len;
+ if (!kernacc((caddr_t)(int)uio->uio_offset, c,
+ uio->uio_rw == UIO_READ ? B_READ : B_WRITE))
+ return(EFAULT);
+ error = uiomove((caddr_t)(int)uio->uio_offset, (int)c, uio);
+ continue;
+
+/* minor device 2 is EOF/RATHOLE */
+ case 2:
+ if (uio->uio_rw == UIO_READ)
+ return (0);
+ c = iov->iov_len;
+ break;
+
+/* minor device 12 (/dev/zero) is source of nulls on read, rathole on write */
+ case 12:
+ if (uio->uio_rw == UIO_WRITE) {
+ c = iov->iov_len;
+ break;
+ }
+ if (zbuf == NULL) {
+ zbuf = (caddr_t)
+ malloc(CLBYTES, M_TEMP, M_WAITOK);
+ bzero(zbuf, CLBYTES);
+ }
+ c = min(iov->iov_len, CLBYTES);
+ error = uiomove(zbuf, (int)c, uio);
+ continue;
+
+#ifdef notyet
+/* 386 I/O address space (/dev/ioport[bwl]) is a read/write access to seperate
+ i/o device address bus, different than memory bus. Semantics here are
+ very different than ordinary read/write, as if iov_len is a multiple
+ an implied string move from a single port will be done. Note that lseek
+ must be used to set the port number reliably. */
+ case 14:
+ if (iov->iov_len == 1) {
+ u_char tmp;
+ tmp = inb(uio->uio_offset);
+ error = uiomove (&tmp, iov->iov_len, uio);
+ } else {
+ if (!useracc((caddr_t)iov->iov_base,
+ iov->iov_len, uio->uio_rw))
+ return (EFAULT);
+ insb(uio->uio_offset, iov->iov_base,
+ iov->iov_len);
+ }
+ break;
+ case 15:
+ if (iov->iov_len == sizeof (short)) {
+ u_short tmp;
+ tmp = inw(uio->uio_offset);
+ error = uiomove (&tmp, iov->iov_len, uio);
+ } else {
+ if (!useracc((caddr_t)iov->iov_base,
+ iov->iov_len, uio->uio_rw))
+ return (EFAULT);
+ insw(uio->uio_offset, iov->iov_base,
+ iov->iov_len/ sizeof (short));
+ }
+ break;
+ case 16:
+ if (iov->iov_len == sizeof (long)) {
+ u_long tmp;
+ tmp = inl(uio->uio_offset);
+ error = uiomove (&tmp, iov->iov_len, uio);
+ } else {
+ if (!useracc((caddr_t)iov->iov_base,
+ iov->iov_len, uio->uio_rw))
+ return (EFAULT);
+ insl(uio->uio_offset, iov->iov_base,
+ iov->iov_len/ sizeof (long));
+ }
+ break;
+#endif
+
+ default:
+ return (ENXIO);
+ }
+ if (error)
+ break;
+ iov->iov_base += c;
+ iov->iov_len -= c;
+ uio->uio_offset += c;
+ uio->uio_resid -= c;
+ }
+ if (zbuf)
+ free(zbuf, M_TEMP);
+ return (error);
+}
+
+
+
+
+/*******************************************************\
+* allow user processes to MMAP some memory sections *
+* instead of going through read/write *
+\*******************************************************/
+int memmmap(dev_t dev, int offset, int nprot)
+{
+ switch (minor(dev))
+ {
+
+/* minor device 0 is physical memory */
+ case 0:
+ return i386_btop(offset);
+
+/* minor device 1 is kernel memory */
+ case 1:
+ return i386_btop(vtophys(offset));
+
+ default:
+ return -1;
+ }
+}
+
diff --git a/sys/i386/i386/microtime.s b/sys/i386/i386/microtime.s
new file mode 100644
index 0000000..3c4d86e
--- /dev/null
+++ b/sys/i386/i386/microtime.s
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: Steve McCanne's microtime code
+ * $Id: microtime.s,v 1.3 1994/04/02 07:00:27 davidg Exp $
+ */
+
+#include "machine/asmacros.h"
+#include "../isa/isa.h"
+#include "../isa/timerreg.h"
+
+/*
+ * Use a higher resolution version of microtime if HZ is not
+ * overridden (i.e. it is 100Hz).
+ */
+#ifndef HZ
+ENTRY(microtime)
+ pushl %edi # save registers
+ pushl %esi
+ pushl %ebx
+
+ movl $_time, %ebx # get timeval ptr
+ movl (%ebx), %edi # sec = time.tv_sec
+ movl 4(%ebx), %esi # usec = time.tv_usec
+
+ cli # disable interrupts
+
+ movl $(TIMER_SEL0|TIMER_LATCH), %eax
+ outb %al, $TIMER_MODE # latch timer 0's counter
+
+ xorl %ebx, %ebx # clear ebx
+ inb $TIMER_CNTR0, %al # Read counter value, LSB first
+ movb %al, %bl
+ inb $TIMER_CNTR0, %al
+ movb %al, %bh
+
+ # Now check for counter overflow. This is tricky because the
+ # timer chip doesn't let us atomically read the current counter
+ # value and the output state (i.e., overflow state). We have
+ # to read the ICU interrupt request register (IRR) to see if the
+ # overflow has occured. Because we lack atomicity, we use
+ # the (very accurate) heuristic that we only check for
+ # overflow if the value read is close to the interrupt period.
+ # E.g., if we just checked the IRR, we might read a non-overflowing
+ # value close to 0, experience overflow, then read this overflow
+ # from the IRR, and mistakenly add a correction to the "close
+ # to zero" value.
+ #
+ # We compare the counter value to heuristic constant 11890.
+ # If the counter value is less than this, we assume the counter
+ # didn't overflow between disabling interrupts above and latching
+ # the counter value. For example, we assume that the above 10 or so
+ # instructions take less than 11932 - 11890 = 42 microseconds to
+ # execute.
+ #
+ # Otherwise, the counter might have overflowed. We check for this
+ # condition by reading the interrupt request register out of the ICU.
+ # If it overflowed, we add in one clock period.
+ #
+ # The heuristic is "very accurate" because it works 100% if
+ # we're called from an ipl less than the clock. Otherwise,
+ # it might not work. Currently, only gettimeofday and bpf
+ # call microtime so it's not a problem.
+
+ movl _timer0_prescale, %eax # adjust value if timer is
+ addl _timer0_divisor, %eax # reprogrammed
+ addl $-11932, %eax
+ subl %eax, %ebx
+
+ cmpl $11890, %ebx # do we have a possible overflow condition
+ jle 1f
+
+ inb $IO_ICU1, %al # read IRR in ICU
+ testb $1, %al # is a timer interrupt pending?
+ je 1f
+ addl $-11932, %ebx # yes, subtract one clock period
+1:
+ sti # enable interrupts
+
+ movl $11932, %eax # subtract counter value from 11932 since
+ subl %ebx, %eax # it is a count-down value
+
+ movl %eax, %ebx # this really is a "imull $1000, %eax, %eax"
+ sall $10, %eax # instruction, but this saves us
+ sall $3, %ebx # 33/23 clocks on a 486/386 CPU
+ subl %ebx, %eax #
+ sall $1, %ebx # /sos
+ subl %ebx, %eax #
+
+ movl $0, %edx # zero extend eax into edx for div
+ movl $1193, %ecx
+ idivl %ecx # convert to usecs: mult by 1000/1193
+
+ addl %eax, %esi # add counter usecs to time.tv_usec
+ cmpl $1000000, %esi # carry in timeval?
+ jl 2f
+ subl $1000000, %esi # adjust usec
+ incl %edi # bump sec
+2:
+ movl 16(%esp), %ecx # load timeval pointer arg
+ movl %edi, (%ecx) # tvp->tv_sec = sec
+ movl %esi, 4(%ecx) # tvp->tv_usec = usec
+
+ popl %ebx # restore regs
+ popl %esi
+ popl %edi
+ ret
+#endif
diff --git a/sys/i386/i386/ns_cksum.c b/sys/i386/i386/ns_cksum.c
new file mode 100644
index 0000000..6b56172
--- /dev/null
+++ b/sys/i386/i386/ns_cksum.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 1982, 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)ns_cksum.c 7.7 (Berkeley) 4/29/91
+ * from NetBSD: ns_cksum.c,v 1.2 1993/05/22 07:59:55 cgd Exp
+ * $Id: ns_cksum.c,v 1.1 1993/09/06 12:12:24 rgrimes Exp $
+ */
+
+#include "sys/param.h"
+#include "systm.h"
+#include "sys/mbuf.h"
+
+/*
+ * Checksum routine for Network Systems Protocol Packets (Big-Endian).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ */
+
+#define ADDCARRY(x) { if ((x) > 65535) (x) -= 65535; }
+#define FOLD(x) {l_util.l = (x); (x) = l_util.s[0] + l_util.s[1]; ADDCARRY(x);}
+
+u_short
+ns_cksum(m, len)
+ register struct mbuf *m;
+ register int len;
+{
+ register u_short *w;
+ register int sum = 0;
+ register int mlen = 0;
+ register int sum2;
+
+ union {
+ u_short s[2];
+ long l;
+ } l_util;
+
+ for (;m && len; m = m->m_next) {
+ if (m->m_len == 0)
+ continue;
+ /*
+ * Each trip around loop adds in
+ * word from one mbuf segment.
+ */
+ w = mtod(m, u_short *);
+ if (mlen == -1) {
+ /*
+ * There is a byte left from the last segment;
+ * ones-complement add it into the checksum.
+ */
+#if BYTE_ORDER == BIG_ENDIAN
+ sum += *(u_char *)w;
+#else
+ sum += *(u_char *)w << 8;
+#endif
+ sum += sum;
+ w = (u_short *)(1 + (char *)w);
+ mlen = m->m_len - 1;
+ len--;
+ FOLD(sum);
+ } else
+ mlen = m->m_len;
+ if (len < mlen)
+ mlen = len;
+ len -= mlen;
+ /*
+ * We can do a 16 bit ones complement sum using
+ * 32 bit arithmetic registers for adding,
+ * with carries from the low added
+ * into the high (by normal carry-chaining)
+ * so long as we fold back before 16 carries have occured.
+ */
+ if (1 & (int) w)
+ goto uuuuglyy;
+#ifndef TINY
+/* -DTINY reduces the size from 1250 to 550, but slows it down by 22% */
+ while ((mlen -= 32) >= 0) {
+ sum += w[0]; sum += sum; sum += w[1]; sum += sum;
+ sum += w[2]; sum += sum; sum += w[3]; sum += sum;
+ sum += w[4]; sum += sum; sum += w[5]; sum += sum;
+ sum += w[6]; sum += sum; sum += w[7]; sum += sum;
+ FOLD(sum);
+ sum += w[8]; sum += sum; sum += w[9]; sum += sum;
+ sum += w[10]; sum += sum; sum += w[11]; sum += sum;
+ sum += w[12]; sum += sum; sum += w[13]; sum += sum;
+ sum += w[14]; sum += sum; sum += w[15]; sum += sum;
+ FOLD(sum);
+ w += 16;
+ }
+ mlen += 32;
+#endif
+ while ((mlen -= 8) >= 0) {
+ sum += w[0]; sum += sum; sum += w[1]; sum += sum;
+ sum += w[2]; sum += sum; sum += w[3]; sum += sum;
+ FOLD(sum);
+ w += 4;
+ }
+ mlen += 8;
+ while ((mlen -= 2) >= 0) {
+ sum += *w++; sum += sum;
+ }
+ goto commoncase;
+uuuuglyy:
+#if BYTE_ORDER == BIG_ENDIAN
+#define ww(n) (((u_char *)w)[n + n + 1])
+#define vv(n) (((u_char *)w)[n + n])
+#else
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define vv(n) (((u_char *)w)[n + n + 1])
+#define ww(n) (((u_char *)w)[n + n])
+#endif
+#endif
+ sum2 = 0;
+#ifndef TINY
+ while ((mlen -= 32) >= 0) {
+ sum += ww(0); sum += sum; sum += ww(1); sum += sum;
+ sum += ww(2); sum += sum; sum += ww(3); sum += sum;
+ sum += ww(4); sum += sum; sum += ww(5); sum += sum;
+ sum += ww(6); sum += sum; sum += ww(7); sum += sum;
+ FOLD(sum);
+ sum += ww(8); sum += sum; sum += ww(9); sum += sum;
+ sum += ww(10); sum += sum; sum += ww(11); sum += sum;
+ sum += ww(12); sum += sum; sum += ww(13); sum += sum;
+ sum += ww(14); sum += sum; sum += ww(15); sum += sum;
+ FOLD(sum);
+ sum2 += vv(0); sum2 += sum2; sum2 += vv(1); sum2 += sum2;
+ sum2 += vv(2); sum2 += sum2; sum2 += vv(3); sum2 += sum2;
+ sum2 += vv(4); sum2 += sum2; sum2 += vv(5); sum2 += sum2;
+ sum2 += vv(6); sum2 += sum2; sum2 += vv(7); sum2 += sum2;
+ FOLD(sum2);
+ sum2 += vv(8); sum2 += sum2; sum2 += vv(9); sum2 += sum2;
+ sum2 += vv(10); sum2 += sum2; sum2 += vv(11); sum2 += sum2;
+ sum2 += vv(12); sum2 += sum2; sum2 += vv(13); sum2 += sum2;
+ sum2 += vv(14); sum2 += sum2; sum2 += vv(15); sum2 += sum2;
+ FOLD(sum2);
+ w += 16;
+ }
+ mlen += 32;
+#endif
+ while ((mlen -= 8) >= 0) {
+ sum += ww(0); sum += sum; sum += ww(1); sum += sum;
+ sum += ww(2); sum += sum; sum += ww(3); sum += sum;
+ FOLD(sum);
+ sum2 += vv(0); sum2 += sum2; sum2 += vv(1); sum2 += sum2;
+ sum2 += vv(2); sum2 += sum2; sum2 += vv(3); sum2 += sum2;
+ FOLD(sum2);
+ w += 4;
+ }
+ mlen += 8;
+ while ((mlen -= 2) >= 0) {
+ sum += ww(0); sum += sum;
+ sum2 += vv(0); sum2 += sum2;
+ w++;
+ }
+ sum += (sum2 << 8);
+commoncase:
+ if (mlen == -1) {
+#if BYTE_ORDER == BIG_ENDIAN
+ sum += *(u_char *)w << 8;
+#else
+ sum += *(u_char *)w;
+#endif
+ }
+ FOLD(sum);
+ }
+ if (mlen == -1) {
+ /* We had an odd number of bytes to sum; assume a garbage
+ byte of zero and clean up */
+ sum += sum;
+ FOLD(sum);
+ }
+ /*
+ * sum has already been kept to low sixteen bits.
+ * just examine result and exit.
+ */
+ if(sum==0xffff) sum = 0;
+ return (sum);
+}
diff --git a/sys/i386/i386/pmap.c b/sys/i386/i386/pmap.c
new file mode 100644
index 0000000..88db9dd
--- /dev/null
+++ b/sys/i386/i386/pmap.c
@@ -0,0 +1,1944 @@
+/*
+ * Copyright (c) 1991 Regents of the University of California.
+ * All rights reserved.
+ * Copyright (c) 1994 John S. Dyson
+ * All rights reserved.
+ * Copyright (c) 1994 David Greenman
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department and William Jolitz of UUNET Technologies Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)pmap.c 7.7 (Berkeley) 5/12/91
+ * $Id: pmap.c,v 1.24 1994/04/20 07:06:14 davidg Exp $
+ */
+
+/*
+ * Derived from hp300 version by Mike Hibler, this version by William
+ * Jolitz uses a recursive map [a pde points to the page directory] to
+ * map the page tables using the pagetables themselves. This is done to
+ * reduce the impact on kernel virtual memory for lots of sparse address
+ * space, and to reduce the cost of memory to each process.
+ *
+ * Derived from: hp300/@(#)pmap.c 7.1 (Berkeley) 12/5/90
+ */
+/*
+ * Major modifications by John S. Dyson primarily to support
+ * pageable page tables, eliminating pmap_attributes,
+ * discontiguous memory pages, and using more efficient string
+ * instructions. Jan 13, 1994. Further modifications on Mar 2, 1994,
+ * general clean-up and efficiency mods.
+ */
+
+/*
+ * Manages physical address maps.
+ *
+ * In addition to hardware address maps, this
+ * module is called upon to provide software-use-only
+ * maps which may or may not be stored in the same
+ * form as hardware maps. These pseudo-maps are
+ * used to store intermediate results from copy
+ * operations to and from address spaces.
+ *
+ * Since the information managed by this module is
+ * also stored by the logical address mapping module,
+ * this module may throw away valid virtual-to-physical
+ * mappings at almost any time. However, invalidations
+ * of virtual-to-physical mappings must be done as
+ * requested.
+ *
+ * In order to cope with hardware architectures which
+ * make virtual-to-physical map invalidates expensive,
+ * this module may delay invalidate or reduced protection
+ * operations until such time as they are actually
+ * necessary. This module is given full information as
+ * to which processors are currently using which maps,
+ * and to when physical maps must be made correct.
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/user.h>
+
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+
+#include <i386/include/cpufunc.h>
+#include <i386/include/cputypes.h>
+
+#include <i386/isa/isa.h>
+
+/*
+ * Allocate various and sundry SYSMAPs used in the days of old VM
+ * and not yet converted. XXX.
+ */
+#define BSDVM_COMPAT 1
+
+/*
+ * Get PDEs and PTEs for user/kernel address space
+ */
+#define pmap_pde(m, v) (&((m)->pm_pdir[((vm_offset_t)(v) >> PD_SHIFT)&1023]))
+#define pdir_pde(m, v) (m[((vm_offset_t)(v) >> PD_SHIFT)&1023])
+
+#define pmap_pte_pa(pte) (*(int *)(pte) & PG_FRAME)
+
+#define pmap_pde_v(pte) ((*(int *)pte & PG_V) != 0)
+#define pmap_pte_w(pte) ((*(int *)pte & PG_W) != 0)
+#define pmap_pte_m(pte) ((*(int *)pte & PG_M) != 0)
+#define pmap_pte_u(pte) ((*(int *)pte & PG_U) != 0)
+#define pmap_pte_v(pte) ((*(int *)pte & PG_V) != 0)
+
+#define pmap_pte_set_w(pte, v) ((v)?(*(int *)pte |= PG_W):(*(int *)pte &= ~PG_W))
+#define pmap_pte_set_prot(pte, v) ((*(int *)pte &= ~PG_PROT), (*(int *)pte |= (v)))
+
+/*
+ * Given a map and a machine independent protection code,
+ * convert to a vax protection code.
+ */
+#define pte_prot(m, p) (protection_codes[p])
+int protection_codes[8];
+
+struct pmap kernel_pmap_store;
+pmap_t kernel_pmap;
+
+vm_offset_t phys_avail[6]; /* 2 entries + 1 null */
+vm_offset_t avail_start; /* PA of first available physical page */
+vm_offset_t avail_end; /* PA of last available physical page */
+vm_size_t mem_size; /* memory size in bytes */
+vm_offset_t virtual_avail; /* VA of first avail page (after kernel bss)*/
+vm_offset_t virtual_end; /* VA of last avail page (end of kernel AS) */
+int i386pagesperpage; /* PAGE_SIZE / I386_PAGE_SIZE */
+boolean_t pmap_initialized = FALSE; /* Has pmap_init completed? */
+vm_offset_t vm_first_phys, vm_last_phys;
+
+static inline boolean_t pmap_testbit();
+static inline void pmap_changebit();
+static inline int pmap_is_managed();
+static inline void *vm_get_pmap();
+static inline void vm_put_pmap();
+inline void pmap_use_pt();
+inline void pmap_unuse_pt();
+inline pt_entry_t * pmap_pte();
+static inline pv_entry_t get_pv_entry();
+void pmap_alloc_pv_entry();
+void pmap_clear_modify();
+void i386_protection_init();
+extern vm_offset_t clean_sva, clean_eva;
+extern int cpu_class;
+
+#if BSDVM_COMPAT
+#include "msgbuf.h"
+
+/*
+ * All those kernel PT submaps that BSD is so fond of
+ */
+pt_entry_t *CMAP1, *CMAP2, *ptmmap;
+caddr_t CADDR1, CADDR2, ptvmmap;
+pt_entry_t *msgbufmap;
+struct msgbuf *msgbufp;
+#endif
+
+void init_pv_entries(int) ;
+
+/*
+ * Routine: pmap_pte
+ * Function:
+ * Extract the page table entry associated
+ * with the given map/virtual_address pair.
+ * [ what about induced faults -wfj]
+ */
+
+inline pt_entry_t *
+pmap_pte(pmap, va)
+ pmap_t pmap;
+ vm_offset_t va;
+{
+
+ if (pmap && *pmap_pde(pmap, va)) {
+ vm_offset_t frame = (int) pmap->pm_pdir[PTDPTDI] & PG_FRAME;
+ /* are we current address space or kernel? */
+ if ( (pmap == kernel_pmap) || (frame == ((int) PTDpde & PG_FRAME)))
+ return ((pt_entry_t *) vtopte(va));
+ /* otherwise, we are alternate address space */
+ else {
+ if ( frame != ((int) APTDpde & PG_FRAME) ) {
+ APTDpde = pmap->pm_pdir[PTDPTDI];
+ tlbflush();
+ }
+ return((pt_entry_t *) avtopte(va));
+ }
+ }
+ return(0);
+}
+
+/*
+ * Routine: pmap_extract
+ * Function:
+ * Extract the physical page address associated
+ * with the given map/virtual_address pair.
+ */
+
+vm_offset_t
+pmap_extract(pmap, va)
+ register pmap_t pmap;
+ vm_offset_t va;
+{
+ pd_entry_t save;
+ vm_offset_t pa;
+ int s;
+
+ if (pmap && *pmap_pde(pmap, va)) {
+ vm_offset_t frame = (int) pmap->pm_pdir[PTDPTDI] & PG_FRAME;
+ /* are we current address space or kernel? */
+ if ( (pmap == kernel_pmap)
+ || (frame == ((int) PTDpde & PG_FRAME)) ) {
+ pa = *(int *) vtopte(va);
+ /* otherwise, we are alternate address space */
+ } else {
+ if ( frame != ((int) APTDpde & PG_FRAME)) {
+ APTDpde = pmap->pm_pdir[PTDPTDI];
+ tlbflush();
+ }
+ pa = *(int *) avtopte(va);
+ }
+ pa = (pa & PG_FRAME) | (va & ~PG_FRAME);
+ return pa;
+ }
+ return 0;
+
+}
+
+/*
+ * determine if a page is managed (memory vs. device)
+ */
+static inline int
+pmap_is_managed(pa)
+ vm_offset_t pa;
+{
+ int i;
+
+ if (!pmap_initialized)
+ return 0;
+
+ for (i = 0; phys_avail[i + 1]; i += 2) {
+ if (pa >= phys_avail[i] && pa < phys_avail[i + 1])
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * find the vm_page_t of a pte (only) given va of pte and pmap
+ */
+inline vm_page_t
+pmap_pte_vm_page(pmap, pt)
+ pmap_t pmap;
+ vm_offset_t pt;
+{
+ pt = i386_trunc_page( pt);
+ pt = (pt - UPT_MIN_ADDRESS) / NBPG;
+ pt = ((vm_offset_t) pmap->pm_pdir[pt]) & PG_FRAME;
+ return PHYS_TO_VM_PAGE(pt);
+}
+
+/*
+ * Wire a page table page
+ */
+inline void
+pmap_use_pt(pmap, va)
+ pmap_t pmap;
+ vm_offset_t va;
+{
+ vm_offset_t pt;
+
+ if (va >= VM_MAX_ADDRESS || !pmap_initialized)
+ return;
+
+ pt = (vm_offset_t) vtopte(va);
+ vm_page_hold( pmap_pte_vm_page(pmap, pt));
+}
+
+/*
+ * Unwire a page table page
+ */
+inline void
+pmap_unuse_pt(pmap, va)
+ pmap_t pmap;
+ vm_offset_t va;
+{
+ vm_offset_t pt;
+
+ if (va >= VM_MAX_ADDRESS || !pmap_initialized)
+ return;
+
+ pt = (vm_offset_t) vtopte(va);
+ vm_page_unhold( pmap_pte_vm_page(pmap, pt));
+}
+
+/* [ macro again?, should I force kstack into user map here? -wfj ] */
+void
+pmap_activate(pmap, pcbp)
+ register pmap_t pmap;
+ struct pcb *pcbp;
+{
+ PMAP_ACTIVATE(pmap, pcbp);
+}
+
+/*
+ * Bootstrap the system enough to run with virtual memory.
+ * Map the kernel's code and data, and allocate the system page table.
+ *
+ * On the I386 this is called after mapping has already been enabled
+ * and just syncs the pmap module with what has already been done.
+ * [We can't call it easily with mapping off since the kernel is not
+ * mapped with PA == VA, hence we would have to relocate every address
+ * from the linked base (virtual) address "KERNBASE" to the actual
+ * (physical) address starting relative to 0]
+ */
+
+#define DMAPAGES 8
+void
+pmap_bootstrap(firstaddr, loadaddr)
+ vm_offset_t firstaddr;
+ vm_offset_t loadaddr;
+{
+#if BSDVM_COMPAT
+ vm_offset_t va;
+ pt_entry_t *pte;
+#endif
+ extern int IdlePTD;
+
+ avail_start = firstaddr + DMAPAGES*NBPG;
+
+ virtual_avail = (vm_offset_t) KERNBASE + avail_start;
+ virtual_end = VM_MAX_KERNEL_ADDRESS;
+ i386pagesperpage = PAGE_SIZE / NBPG;
+
+ /*
+ * Initialize protection array.
+ */
+ i386_protection_init();
+
+ /*
+ * The kernel's pmap is statically allocated so we don't
+ * have to use pmap_create, which is unlikely to work
+ * correctly at this part of the boot sequence.
+ */
+ kernel_pmap = &kernel_pmap_store;
+
+ kernel_pmap->pm_pdir = (pd_entry_t *)(KERNBASE + IdlePTD);
+
+ simple_lock_init(&kernel_pmap->pm_lock);
+ kernel_pmap->pm_count = 1;
+
+#if BSDVM_COMPAT
+ /*
+ * Allocate all the submaps we need
+ */
+#define SYSMAP(c, p, v, n) \
+ v = (c)va; va += ((n)*NBPG); p = pte; pte += (n);
+
+ va = virtual_avail;
+ pte = pmap_pte(kernel_pmap, va);
+
+ SYSMAP(caddr_t ,CMAP1 ,CADDR1 ,1 )
+ SYSMAP(caddr_t ,CMAP2 ,CADDR2 ,1 )
+ SYSMAP(caddr_t ,ptmmap ,ptvmmap ,1 )
+ SYSMAP(struct msgbuf * ,msgbufmap ,msgbufp ,1 )
+ virtual_avail = va;
+#endif
+ /*
+ * reserve special hunk of memory for use by bus dma as a bounce
+ * buffer (contiguous virtual *and* physical memory). for now,
+ * assume vm does not use memory beneath hole, and we know that
+ * the bootstrap uses top 32k of base memory. -wfj
+ */
+ {
+ extern vm_offset_t isaphysmem;
+ isaphysmem = va;
+
+ virtual_avail = pmap_map(va, firstaddr,
+ firstaddr + DMAPAGES*NBPG, VM_PROT_ALL);
+ }
+
+ *(int *)PTD = 0;
+ tlbflush();
+
+}
+
+/*
+ * Initialize the pmap module.
+ * Called by vm_init, to initialize any structures that the pmap
+ * system needs to map virtual memory.
+ * pmap_init has been enhanced to support in a fairly consistant
+ * way, discontiguous physical memory.
+ */
+void
+pmap_init(phys_start, phys_end)
+ vm_offset_t phys_start, phys_end;
+{
+ vm_offset_t addr, addr2;
+ vm_size_t npg, s;
+ int rv;
+ int i;
+ extern int KPTphys;
+ extern int IdlePTD;
+
+ /*
+ * Now that kernel map has been allocated, we can mark as
+ * unavailable regions which we have mapped in locore.
+ */
+ addr = atdevbase;
+ (void) vm_map_find(kernel_map, NULL, (vm_offset_t) 0,
+ &addr, (0x100000-0xa0000), FALSE);
+
+ addr = (vm_offset_t) KERNBASE + IdlePTD;
+ vm_object_reference(kernel_object);
+ (void) vm_map_find(kernel_map, kernel_object, addr,
+ &addr, (4 + NKPT) * NBPG, FALSE);
+
+
+ /*
+ * calculate the number of pv_entries needed
+ */
+ vm_first_phys = phys_avail[0];
+ for (i = 0; phys_avail[i + 1]; i += 2) ;
+ npg = (phys_avail[(i - 2) + 1] - vm_first_phys) / NBPG;
+
+ /*
+ * Allocate memory for random pmap data structures. Includes the
+ * pv_head_table.
+ */
+ s = (vm_size_t) (sizeof(struct pv_entry) * npg);
+ s = i386_round_page(s);
+ addr = (vm_offset_t) kmem_alloc(kernel_map, s);
+ pv_table = (pv_entry_t) addr;
+
+ /*
+ * init the pv free list
+ */
+ init_pv_entries(npg);
+ /*
+ * Now it is safe to enable pv_table recording.
+ */
+ pmap_initialized = TRUE;
+}
+
+/*
+ * Used to map a range of physical addresses into kernel
+ * virtual address space.
+ *
+ * For now, VM is already on, we only need to map the
+ * specified memory.
+ */
+vm_offset_t
+pmap_map(virt, start, end, prot)
+ vm_offset_t virt;
+ vm_offset_t start;
+ vm_offset_t end;
+ int prot;
+{
+ while (start < end) {
+ pmap_enter(kernel_pmap, virt, start, prot, FALSE);
+ virt += PAGE_SIZE;
+ start += PAGE_SIZE;
+ }
+ return(virt);
+}
+
+/*
+ * Create and return a physical map.
+ *
+ * If the size specified for the map
+ * is zero, the map is an actual physical
+ * map, and may be referenced by the
+ * hardware.
+ *
+ * If the size specified is non-zero,
+ * the map will be used in software only, and
+ * is bounded by that size.
+ *
+ * [ just allocate a ptd and mark it uninitialize -- should we track
+ * with a table which process has which ptd? -wfj ]
+ */
+
+pmap_t
+pmap_create(size)
+ vm_size_t size;
+{
+ register pmap_t pmap;
+
+ /*
+ * Software use map does not need a pmap
+ */
+ if (size)
+ return(NULL);
+
+ pmap = (pmap_t) malloc(sizeof *pmap, M_VMPMAP, M_WAITOK);
+ bzero(pmap, sizeof(*pmap));
+ pmap_pinit(pmap);
+ return (pmap);
+}
+
+
+struct pmaplist {
+ struct pmaplist *next;
+};
+
+static inline void *
+vm_get_pmap()
+{
+ struct pmaplist *rtval;
+
+ rtval = (struct pmaplist *)kmem_alloc(kernel_map, ctob(1));
+ bzero(rtval, ctob(1));
+ return rtval;
+}
+
+static inline void
+vm_put_pmap(up)
+ struct pmaplist *up;
+{
+ kmem_free(kernel_map, (vm_offset_t)up, ctob(1));
+}
+
+/*
+ * Initialize a preallocated and zeroed pmap structure,
+ * such as one in a vmspace structure.
+ */
+void
+pmap_pinit(pmap)
+ register struct pmap *pmap;
+{
+ /*
+ * No need to allocate page table space yet but we do need a
+ * valid page directory table.
+ */
+ pmap->pm_pdir = (pd_entry_t *) vm_get_pmap();
+
+ /* wire in kernel global address entries */
+ bcopy(PTD+KPTDI, pmap->pm_pdir+KPTDI, NKPT*PTESIZE);
+
+ /* install self-referential address mapping entry */
+ *(int *)(pmap->pm_pdir+PTDPTDI) =
+ ((int)pmap_kextract((vm_offset_t)pmap->pm_pdir)) | PG_V | PG_KW;
+
+ pmap->pm_count = 1;
+ simple_lock_init(&pmap->pm_lock);
+}
+
+/*
+ * Retire the given physical map from service.
+ * Should only be called if the map contains
+ * no valid mappings.
+ */
+void
+pmap_destroy(pmap)
+ register pmap_t pmap;
+{
+ int count;
+
+ if (pmap == NULL)
+ return;
+
+ simple_lock(&pmap->pm_lock);
+ count = --pmap->pm_count;
+ simple_unlock(&pmap->pm_lock);
+ if (count == 0) {
+ pmap_release(pmap);
+ free((caddr_t)pmap, M_VMPMAP);
+ }
+}
+
+/*
+ * Release any resources held by the given physical map.
+ * Called when a pmap initialized by pmap_pinit is being released.
+ * Should only be called if the map contains no valid mappings.
+ */
+void
+pmap_release(pmap)
+ register struct pmap *pmap;
+{
+ vm_put_pmap((struct pmaplist *) pmap->pm_pdir);
+}
+
+/*
+ * Add a reference to the specified pmap.
+ */
+void
+pmap_reference(pmap)
+ pmap_t pmap;
+{
+ if (pmap != NULL) {
+ simple_lock(&pmap->pm_lock);
+ pmap->pm_count++;
+ simple_unlock(&pmap->pm_lock);
+ }
+}
+
+#define PV_FREELIST_MIN ((NBPG / sizeof (struct pv_entry)) / 2)
+
+/*
+ * Data for the pv entry allocation mechanism
+ */
+int pv_freelistcnt;
+pv_entry_t pv_freelist;
+vm_offset_t pvva;
+int npvvapg;
+
+/*
+ * free the pv_entry back to the free list
+ */
+inline static void
+free_pv_entry(pv)
+ pv_entry_t pv;
+{
+ if (!pv) return;
+ ++pv_freelistcnt;
+ pv->pv_next = pv_freelist;
+ pv_freelist = pv;
+}
+
+/*
+ * get a new pv_entry, allocating a block from the system
+ * when needed.
+ * the memory allocation is performed bypassing the malloc code
+ * because of the possibility of allocations at interrupt time.
+ */
+static inline pv_entry_t
+get_pv_entry()
+{
+ pv_entry_t tmp;
+
+ /*
+ * get more pv_entry pages if needed
+ */
+ while (pv_freelistcnt < PV_FREELIST_MIN || pv_freelist == 0) {
+ pmap_alloc_pv_entry();
+ }
+
+ /*
+ * get a pv_entry off of the free list
+ */
+ --pv_freelistcnt;
+ tmp = pv_freelist;
+ pv_freelist = tmp->pv_next;
+ tmp->pv_pmap = 0;
+ tmp->pv_va = 0;
+ tmp->pv_next = 0;
+ return tmp;
+}
+
+/*
+ * this *strange* allocation routine *statistically* eliminates the
+ * *possibility* of a malloc failure (*FATAL*) for a pv_entry_t data structure.
+ * also -- this code is MUCH MUCH faster than the malloc equiv...
+ */
+void
+pmap_alloc_pv_entry()
+{
+ /*
+ * do we have any pre-allocated map-pages left?
+ */
+ if (npvvapg) {
+ vm_page_t m;
+ /*
+ * we do this to keep recursion away
+ */
+ pv_freelistcnt += PV_FREELIST_MIN;
+ /*
+ * allocate a physical page out of the vm system
+ */
+ if (m = vm_page_alloc(kernel_object, pvva-vm_map_min(kernel_map))) {
+ int newentries;
+ int i;
+ pv_entry_t entry;
+ newentries = (NBPG/sizeof (struct pv_entry));
+ /*
+ * wire the page
+ */
+ vm_page_wire(m);
+ m->flags &= ~PG_BUSY;
+ /*
+ * let the kernel see it
+ */
+ pmap_enter(vm_map_pmap(kernel_map), pvva,
+ VM_PAGE_TO_PHYS(m), VM_PROT_DEFAULT,1);
+
+ entry = (pv_entry_t) pvva;
+ /*
+ * update the allocation pointers
+ */
+ pvva += NBPG;
+ --npvvapg;
+
+ /*
+ * free the entries into the free list
+ */
+ for (i = 0; i < newentries; i++) {
+ free_pv_entry(entry);
+ entry++;
+ }
+ }
+ pv_freelistcnt -= PV_FREELIST_MIN;
+ }
+ if (!pv_freelist)
+ panic("get_pv_entry: cannot get a pv_entry_t");
+}
+
+
+
+/*
+ * init the pv_entry allocation system
+ */
+#define PVSPERPAGE 64
+void
+init_pv_entries(npg)
+ int npg;
+{
+ /*
+ * allocate enough kvm space for PVSPERPAGE entries per page (lots)
+ * kvm space is fairly cheap, be generous!!! (the system can panic
+ * if this is too small.)
+ */
+ npvvapg = ((npg*PVSPERPAGE) * sizeof(struct pv_entry) + NBPG - 1)/NBPG;
+ pvva = kmem_alloc_pageable(kernel_map, npvvapg * NBPG);
+ /*
+ * get the first batch of entries
+ */
+ free_pv_entry(get_pv_entry());
+}
+
+static pt_entry_t *
+get_pt_entry(pmap)
+ pmap_t pmap;
+{
+ pt_entry_t *ptp;
+ vm_offset_t frame = (int) pmap->pm_pdir[PTDPTDI] & PG_FRAME;
+ /* are we current address space or kernel? */
+ if (pmap == kernel_pmap || frame == ((int) PTDpde & PG_FRAME)) {
+ ptp=PTmap;
+ /* otherwise, we are alternate address space */
+ } else {
+ if ( frame != ((int) APTDpde & PG_FRAME)) {
+ APTDpde = pmap->pm_pdir[PTDPTDI];
+ tlbflush();
+ }
+ ptp=APTmap;
+ }
+ return ptp;
+}
+
+/*
+ * If it is the first entry on the list, it is actually
+ * in the header and we must copy the following entry up
+ * to the header. Otherwise we must search the list for
+ * the entry. In either case we free the now unused entry.
+ */
+void
+pmap_remove_entry(pmap, pv, va)
+ struct pmap *pmap;
+ pv_entry_t pv;
+ vm_offset_t va;
+{
+ pv_entry_t npv;
+ int wired;
+ int s;
+ s = splimp();
+ if (pmap == pv->pv_pmap && va == pv->pv_va) {
+ npv = pv->pv_next;
+ if (npv) {
+ *pv = *npv;
+ free_pv_entry(npv);
+ } else {
+ pv->pv_pmap = NULL;
+ }
+ } else {
+ for (npv = pv->pv_next; npv; npv = npv->pv_next) {
+ if (pmap == npv->pv_pmap && va == npv->pv_va) {
+ break;
+ }
+ pv = npv;
+ }
+ if (npv) {
+ pv->pv_next = npv->pv_next;
+ free_pv_entry(npv);
+ }
+ }
+ splx(s);
+}
+
+/*
+ * Remove the given range of addresses from the specified map.
+ *
+ * It is assumed that the start and end are properly
+ * rounded to the page size.
+ */
+void
+pmap_remove(pmap, sva, eva)
+ struct pmap *pmap;
+ register vm_offset_t sva;
+ register vm_offset_t eva;
+{
+ register pt_entry_t *ptp,*ptq;
+ vm_offset_t pa;
+ register pv_entry_t pv;
+ vm_offset_t va;
+ vm_page_t m;
+ pt_entry_t oldpte;
+
+ if (pmap == NULL)
+ return;
+
+ ptp = get_pt_entry(pmap);
+
+/*
+ * special handling of removing one page. a very
+ * common operation and easy to short circuit some
+ * code.
+ */
+ if( (sva + NBPG) == eva) {
+
+ if( *pmap_pde( pmap, sva) == 0)
+ return;
+
+ ptq = ptp + i386_btop(sva);
+
+ if( !*ptq)
+ return;
+ /*
+ * Update statistics
+ */
+ if (pmap_pte_w(ptq))
+ pmap->pm_stats.wired_count--;
+ pmap->pm_stats.resident_count--;
+
+ pa = pmap_pte_pa(ptq);
+ oldpte = *ptq;
+ *ptq = 0;
+
+ if (pmap_is_managed(pa)) {
+ if ((((int) oldpte & PG_M) && (sva < USRSTACK || sva > UPT_MAX_ADDRESS))
+ || (sva >= USRSTACK && sva < USRSTACK+(UPAGES*NBPG))) {
+ if (sva < clean_sva || sva >= clean_eva) {
+ m = PHYS_TO_VM_PAGE(pa);
+ m->flags &= ~PG_CLEAN;
+ }
+ }
+
+ pv = pa_to_pvh(pa);
+ pmap_remove_entry(pmap, pv, sva);
+ pmap_unuse_pt(pmap, sva);
+ }
+ tlbflush();
+ return;
+ }
+
+ sva = i386_btop(sva);
+ eva = i386_btop(eva);
+
+ while (sva < eva) {
+ /*
+ * Weed out invalid mappings.
+ * Note: we assume that the page directory table is
+ * always allocated, and in kernel virtual.
+ */
+
+ if ( *pmap_pde(pmap, i386_ptob(sva)) == 0 ) {
+ /* We can race ahead here, straight to next pde.. */
+ nextpde:
+ sva = ((sva + NPTEPG) & ~(NPTEPG - 1));
+ continue;
+ }
+
+ ptq = ptp + sva;
+
+ /*
+ * search for page table entries, use string operations
+ * that are much faster than
+ * explicitly scanning when page tables are not fully
+ * populated.
+ */
+ if ( *ptq == 0) {
+ vm_offset_t pdnxt = ((sva + NPTEPG) & ~(NPTEPG - 1));
+ vm_offset_t nscan = pdnxt - sva;
+ int found = 0;
+
+ if ((nscan + sva) > eva)
+ nscan = eva - sva;
+
+ asm("xorl %%eax,%%eax;cld;repe;scasl;jz 1f;incl %%eax;1:;"
+ :"=D"(ptq),"=a"(found)
+ :"c"(nscan),"0"(ptq)
+ :"cx");
+
+ if( !found) {
+ sva = pdnxt;
+ continue;
+ }
+ ptq -= 1;
+
+ sva = ptq - ptp;
+ }
+
+ /*
+ * Update statistics
+ */
+ oldpte = *ptq;
+ if (((int)oldpte) & PG_W)
+ pmap->pm_stats.wired_count--;
+ pmap->pm_stats.resident_count--;
+
+ /*
+ * Invalidate the PTEs.
+ * XXX: should cluster them up and invalidate as many
+ * as possible at once.
+ */
+ *ptq = 0;
+
+ va = i386_ptob(sva);
+
+ /*
+ * Remove from the PV table (raise IPL since we
+ * may be called at interrupt time).
+ */
+ pa = ((int)oldpte) & PG_FRAME;
+ if (!pmap_is_managed(pa)) {
+ ++sva;
+ continue;
+ }
+
+ if ((((int) oldpte & PG_M) && (va < USRSTACK || va > UPT_MAX_ADDRESS))
+ || (va >= USRSTACK && va < USRSTACK+(UPAGES*NBPG))) {
+ if (va < clean_sva || va >= clean_eva ) {
+ m = PHYS_TO_VM_PAGE(pa);
+ m->flags &= ~PG_CLEAN;
+ }
+ }
+
+ pv = pa_to_pvh(pa);
+ pmap_remove_entry(pmap, pv, va);
+ pmap_unuse_pt(pmap, va);
+ ++sva;
+ }
+ tlbflush();
+}
+
+/*
+ * Routine: pmap_remove_all
+ * Function:
+ * Removes this physical page from
+ * all physical maps in which it resides.
+ * Reflects back modify bits to the pager.
+ *
+ * Notes:
+ * Original versions of this routine were very
+ * inefficient because they iteratively called
+ * pmap_remove (slow...)
+ */
+void
+pmap_remove_all(pa)
+ vm_offset_t pa;
+{
+ register pv_entry_t pv, npv;
+ register pt_entry_t *pte, *ptp;
+ vm_offset_t va;
+ struct pmap *pmap;
+ struct map *map;
+ vm_page_t m;
+ int s;
+
+ /*
+ * Not one of ours
+ */
+ if (!pmap_is_managed(pa))
+ return;
+
+ pa = i386_trunc_page(pa);
+ pv = pa_to_pvh(pa);
+ m = PHYS_TO_VM_PAGE(pa);
+
+ s = splimp();
+ while (pv->pv_pmap != NULL) {
+ pmap = pv->pv_pmap;
+ ptp = get_pt_entry(pmap);
+ va = i386_btop(pv->pv_va);
+ pte = ptp + va;
+ if (pmap_pte_w(pte))
+ pmap->pm_stats.wired_count--;
+ if ( *pte)
+ pmap->pm_stats.resident_count--;
+
+ /*
+ * update the vm_page_t clean bit
+ */
+ if ( (m->flags & PG_CLEAN) &&
+ ((((int) *pte) & PG_M) && (pv->pv_va < USRSTACK || pv->pv_va > UPT_MAX_ADDRESS))
+ || (pv->pv_va >= USRSTACK && pv->pv_va < USRSTACK+(UPAGES*NBPG))) {
+ if (pv->pv_va < clean_sva || pv->pv_va >= clean_eva) {
+ m->flags &= ~PG_CLEAN;
+ }
+ }
+
+ *pte = 0;
+ pmap_unuse_pt(pmap, pv->pv_va);
+
+ npv = pv->pv_next;
+ if (npv) {
+ *pv = *npv;
+ free_pv_entry(npv);
+ } else {
+ pv->pv_pmap = NULL;
+ }
+ }
+ splx(s);
+ tlbflush();
+}
+
+
+/*
+ * Set the physical protection on the
+ * specified range of this map as requested.
+ */
+void
+pmap_protect(pmap, sva, eva, prot)
+ register pmap_t pmap;
+ vm_offset_t sva, eva;
+ vm_prot_t prot;
+{
+ register pt_entry_t *pte;
+ register vm_offset_t va;
+ int i386prot;
+ register pt_entry_t *ptp;
+ int evap = i386_btop(eva);
+ int s;
+
+ if (pmap == NULL)
+ return;
+
+ if ((prot & VM_PROT_READ) == VM_PROT_NONE) {
+ pmap_remove(pmap, sva, eva);
+ return;
+ }
+ if (prot & VM_PROT_WRITE)
+ return;
+
+ ptp = get_pt_entry(pmap);
+
+ va = sva;
+ while (va < eva) {
+ int found=0;
+ int svap;
+ vm_offset_t nscan;
+ /*
+ * Page table page is not allocated.
+ * Skip it, we don't want to force allocation
+ * of unnecessary PTE pages just to set the protection.
+ */
+ if (! *pmap_pde(pmap, va)) {
+ /* XXX: avoid address wrap around */
+nextpde:
+ if (va >= i386_trunc_pdr((vm_offset_t)-1))
+ break;
+ va = i386_round_pdr(va + PAGE_SIZE);
+ continue;
+ }
+
+ pte = ptp + i386_btop(va);
+
+ if( *pte == 0) {
+ /*
+ * scan for a non-empty pte
+ */
+ svap = pte - ptp;
+ nscan = ((svap + NPTEPG) & ~(NPTEPG - 1)) - svap;
+
+ if (nscan + svap > evap)
+ nscan = evap - svap;
+
+ found = 0;
+ if (nscan)
+ asm("xorl %%eax,%%eax;cld;repe;scasl;jz 1f;incl %%eax;1:;"
+ :"=D"(pte),"=a"(found)
+ :"c"(nscan),"0"(pte):"cx");
+
+ if( !found)
+ goto nextpde;
+
+ pte -= 1;
+ svap = pte - ptp;
+
+ va = i386_ptob(svap);
+ }
+
+ i386prot = pte_prot(pmap, prot);
+ if (va < UPT_MAX_ADDRESS) {
+ i386prot |= PG_u;
+ if( va >= UPT_MIN_ADDRESS)
+ i386prot |= PG_RW;
+ }
+ pmap_pte_set_prot(pte, i386prot);
+ va += PAGE_SIZE;
+ }
+ tlbflush();
+}
+
+/*
+ * Insert the given physical page (p) at
+ * the specified virtual address (v) in the
+ * target physical map with the protection requested.
+ *
+ * If specified, the page will be wired down, meaning
+ * that the related pte can not be reclaimed.
+ *
+ * NB: This is the only routine which MAY NOT lazy-evaluate
+ * or lose information. That is, this routine must actually
+ * insert this page into the given map NOW.
+ */
+void
+pmap_enter(pmap, va, pa, prot, wired)
+ register pmap_t pmap;
+ vm_offset_t va;
+ register vm_offset_t pa;
+ vm_prot_t prot;
+ boolean_t wired;
+{
+ register pt_entry_t *pte;
+ register pt_entry_t npte;
+ vm_offset_t opa;
+ int cacheable=1;
+
+ if (pmap == NULL)
+ return;
+
+ va = i386_trunc_page(va);
+ pa = i386_trunc_page(pa);
+ if (va > VM_MAX_KERNEL_ADDRESS)panic("pmap_enter: toobig");
+
+ /*
+ * Page Directory table entry not valid, we need a new PT page
+ */
+ if ( *pmap_pde(pmap, va) == 0) {
+ pg("ptdi %x, va %x", pmap->pm_pdir[PTDPTDI], va);
+ }
+
+ pte = pmap_pte(pmap, va);
+ opa = pmap_pte_pa(pte);
+
+ /*
+ * Mapping has not changed, must be protection or wiring change.
+ */
+ if (opa == pa) {
+ /*
+ * Wiring change, just update stats.
+ * We don't worry about wiring PT pages as they remain
+ * resident as long as there are valid mappings in them.
+ * Hence, if a user page is wired, the PT page will be also.
+ */
+ if (wired && !pmap_pte_w(pte) || !wired && pmap_pte_w(pte)) {
+ if (wired)
+ pmap->pm_stats.wired_count++;
+ else
+ pmap->pm_stats.wired_count--;
+ }
+ goto validate;
+ }
+
+ /*
+ * Mapping has changed, invalidate old range and fall through to
+ * handle validating new mapping.
+ */
+ if (opa) {
+ pmap_remove(pmap, va, va + PAGE_SIZE);
+ }
+
+ /*
+ * Enter on the PV list if part of our managed memory
+ * Note that we raise IPL while manipulating pv_table
+ * since pmap_enter can be called at interrupt time.
+ */
+ if (pmap_is_managed(pa)) {
+ register pv_entry_t pv, npv;
+ int s;
+
+ pv = pa_to_pvh(pa);
+ s = splimp();
+ /*
+ * No entries yet, use header as the first entry
+ */
+ if (pv->pv_pmap == NULL) {
+ pv->pv_va = va;
+ pv->pv_pmap = pmap;
+ pv->pv_next = NULL;
+ }
+ /*
+ * There is at least one other VA mapping this page.
+ * Place this entry after the header.
+ */
+ else {
+ npv = get_pv_entry();
+ npv->pv_va = va;
+ npv->pv_pmap = pmap;
+ npv->pv_next = pv->pv_next;
+ pv->pv_next = npv;
+ }
+ splx(s);
+ cacheable = 1;
+ } else {
+ cacheable = 0;
+ }
+
+ pmap_use_pt(pmap, va);
+
+ /*
+ * Increment counters
+ */
+ pmap->pm_stats.resident_count++;
+ if (wired)
+ pmap->pm_stats.wired_count++;
+
+validate:
+ /*
+ * Now validate mapping with desired protection/wiring.
+ */
+ npte = (pt_entry_t) ( (int) (pa | pte_prot(pmap, prot) | PG_V));
+ /*
+ * for correctness:
+ */
+ if( !cacheable)
+ (int) npte |= PG_N;
+
+ /*
+ * When forking (copy-on-write, etc):
+ * A process will turn off write permissions for any of its writable
+ * pages. If the data (object) is only referred to by one process, the
+ * processes map is modified directly as opposed to using the
+ * object manipulation routine. When using pmap_protect, the
+ * modified bits are not kept in the vm_page_t data structure.
+ * Therefore, when using pmap_enter in vm_fault to bring back
+ * writability of a page, there has been no memory of the
+ * modified or referenced bits except at the pte level.
+ * this clause supports the carryover of the modified and
+ * used (referenced) bits.
+ */
+ if (pa == opa)
+ (int) npte |= (int) *pte & (PG_M|PG_U);
+
+
+ if (wired)
+ (int) npte |= PG_W;
+ if (va < UPT_MIN_ADDRESS)
+ (int) npte |= PG_u;
+ else if (va < UPT_MAX_ADDRESS)
+ (int) npte |= PG_u | PG_RW | PG_NC_PWT;
+
+/*
+ printf("mapping: pa: %x, to va: %x, with pte: %x\n", pa, va, npte);
+*/
+
+ if( *pte != npte) {
+ *pte = npte;
+ tlbflush();
+ }
+}
+
+/*
+ * add a wired page to the kva
+ */
+void
+pmap_kenter(va, pa)
+ vm_offset_t va;
+ register vm_offset_t pa;
+{
+ register pt_entry_t *pte;
+ register pv_entry_t pv, npv;
+ vm_offset_t opa;
+ int s;
+
+ /*
+ * Enter on the PV list if part of our managed memory
+ * Note that we raise IPL while manipulating pv_table
+ * since pmap_enter can be called at interrupt time.
+ */
+
+ pte = vtopte(va);
+
+ opa = pmap_pte_pa(pte);
+ /*
+ * Mapping has not changed, must be protection or wiring change.
+ */
+ if (opa == pa) {
+ /*
+ * Wiring change, just update stats.
+ * We don't worry about wiring PT pages as they remain
+ * resident as long as there are valid mappings in them.
+ * Hence, if a user page is wired, the PT page will be also.
+ */
+ if (!pmap_pte_w(pte)) {
+ kernel_pmap->pm_stats.wired_count++;
+ }
+ goto validate;
+ }
+
+ if (opa) {
+ pmap_remove(kernel_pmap, va, va + PAGE_SIZE);
+ }
+
+ pv = pa_to_pvh(pa);
+ s = splimp();
+ /*
+ * No entries yet, use header as the first entry
+ */
+ if (pv->pv_pmap == NULL) {
+ pv->pv_va = va;
+ pv->pv_pmap = kernel_pmap;
+ pv->pv_next = NULL;
+ }
+ /*
+ * There is at least one other VA mapping this page.
+ * Place this entry after the header.
+ */
+ else {
+ npv = get_pv_entry();
+ npv->pv_va = va;
+ npv->pv_pmap = kernel_pmap;
+ npv->pv_next = pv->pv_next;
+ pv->pv_next = npv;
+ }
+ splx(s);
+
+ /*
+ * Increment counters
+ */
+ kernel_pmap->pm_stats.resident_count++;
+
+validate:
+
+ /*
+ * Now validate mapping with desired protection/wiring.
+ */
+ *pte = (pt_entry_t) ( (int) (pa | PG_RW | PG_V | PG_W));
+}
+
+/*
+ * this code makes some *MAJOR* assumptions:
+ * 1. Current pmap & pmap exists.
+ * 2. Not wired.
+ * 3. Read access.
+ * 4. No page table pages.
+ * 5. Tlbflush is deferred to calling procedure.
+ * 6. Page IS managed.
+ * but is *MUCH* faster than pmap_enter...
+ */
+
+static inline void
+pmap_enter_quick(pmap, va, pa)
+ register pmap_t pmap;
+ vm_offset_t va;
+ register vm_offset_t pa;
+{
+ register pt_entry_t *pte;
+ register pv_entry_t pv, npv;
+ int s;
+
+ /*
+ * Enter on the PV list if part of our managed memory
+ * Note that we raise IPL while manipulating pv_table
+ * since pmap_enter can be called at interrupt time.
+ */
+
+ pte = vtopte(va);
+ if (pmap_pte_pa(pte)) {
+ pmap_remove(pmap, va, va + PAGE_SIZE);
+ }
+
+ pv = pa_to_pvh(pa);
+ s = splimp();
+ /*
+ * No entries yet, use header as the first entry
+ */
+ if (pv->pv_pmap == NULL) {
+ pv->pv_va = va;
+ pv->pv_pmap = pmap;
+ pv->pv_next = NULL;
+ }
+ /*
+ * There is at least one other VA mapping this page.
+ * Place this entry after the header.
+ */
+ else {
+ npv = get_pv_entry();
+ npv->pv_va = va;
+ npv->pv_pmap = pmap;
+ npv->pv_next = pv->pv_next;
+ pv->pv_next = npv;
+ }
+ splx(s);
+
+ pmap_use_pt(pmap, va);
+
+ /*
+ * Increment counters
+ */
+ pmap->pm_stats.resident_count++;
+
+validate:
+
+ /*
+ * Now validate mapping with desired protection/wiring.
+ */
+ *pte = (pt_entry_t) ( (int) (pa | PG_V | PG_u));
+}
+
+/*
+ * pmap_object_init_pt preloads the ptes for a given object
+ * into the specified pmap. This eliminates the blast of soft
+ * faults on process startup and immediately after an mmap.
+ */
+void
+pmap_object_init_pt(pmap, addr, object, offset, size)
+ pmap_t pmap;
+ vm_offset_t addr;
+ vm_object_t object;
+ vm_offset_t offset;
+ vm_offset_t size;
+{
+
+ vm_offset_t tmpoff;
+ vm_page_t p;
+ int s;
+ vm_offset_t v, lastv=0;
+ pt_entry_t pte;
+ extern vm_map_t kernel_map;
+ vm_offset_t objbytes;
+
+ if (!pmap)
+ return;
+
+ /*
+ * if we are processing a major portion of the object, then
+ * scan the entire thing.
+ */
+ if( size > object->size / 2) {
+ objbytes = size;
+ p = object->memq.tqh_first;
+ while ((p != NULL) && (objbytes != 0)) {
+ tmpoff = p->offset;
+ if( tmpoff < offset) {
+ p = p->listq.tqe_next;
+ continue;
+ }
+ tmpoff -= offset;
+ if( tmpoff >= size) {
+ p = p->listq.tqe_next;
+ continue;
+ }
+
+ if ((p->flags & (PG_BUSY|PG_FICTITIOUS)) == 0 ) {
+ vm_page_hold(p);
+ v = i386_trunc_page(((vm_offset_t)vtopte( addr+tmpoff)));
+ /* a fault might occur here */
+ *(volatile char *)v += 0;
+ vm_page_unhold(p);
+ pmap_enter_quick(pmap, addr+tmpoff, VM_PAGE_TO_PHYS(p));
+ }
+ p = p->listq.tqe_next;
+ objbytes -= NBPG;
+ }
+ } else {
+ /*
+ * else lookup the pages one-by-one.
+ */
+ for(tmpoff = 0; tmpoff < size; tmpoff += NBPG) {
+ if( p = vm_page_lookup(object, tmpoff + offset)) {
+ if( (p->flags & (PG_BUSY|PG_FICTITIOUS)) == 0) {
+ vm_page_hold(p);
+ v = i386_trunc_page(((vm_offset_t)vtopte( addr+tmpoff)));
+ /* a fault might occur here */
+ *(volatile char *)v += 0;
+ vm_page_unhold(p);
+ pmap_enter_quick(pmap, addr+tmpoff, VM_PAGE_TO_PHYS(p));
+ }
+ }
+ }
+ }
+
+ tlbflush();
+}
+
+/*
+ * Routine: pmap_change_wiring
+ * Function: Change the wiring attribute for a map/virtual-address
+ * pair.
+ * In/out conditions:
+ * The mapping must already exist in the pmap.
+ */
+void
+pmap_change_wiring(pmap, va, wired)
+ register pmap_t pmap;
+ vm_offset_t va;
+ boolean_t wired;
+{
+ register pt_entry_t *pte;
+
+ if (pmap == NULL)
+ return;
+
+ pte = pmap_pte(pmap, va);
+ if (wired && !pmap_pte_w(pte) || !wired && pmap_pte_w(pte)) {
+ if (wired)
+ pmap->pm_stats.wired_count++;
+ else
+ pmap->pm_stats.wired_count--;
+ }
+ /*
+ * Wiring is not a hardware characteristic so there is no need
+ * to invalidate TLB.
+ */
+ pmap_pte_set_w(pte, wired);
+ /*
+ * When unwiring, set the modified bit in the pte -- could have
+ * been changed by the kernel
+ */
+ if (!wired)
+ (int) *pte |= PG_M;
+}
+
+
+
+/*
+ * Copy the range specified by src_addr/len
+ * from the source map to the range dst_addr/len
+ * in the destination map.
+ *
+ * This routine is only advisory and need not do anything.
+ */
+void
+pmap_copy(dst_pmap, src_pmap, dst_addr, len, src_addr)
+ pmap_t dst_pmap, src_pmap;
+ vm_offset_t dst_addr;
+ vm_size_t len;
+ vm_offset_t src_addr;
+{
+}
+/*
+ * Require that all active physical maps contain no
+ * incorrect entries NOW. [This update includes
+ * forcing updates of any address map caching.]
+ *
+ * Generally used to insure that a thread about
+ * to run will see a semantically correct world.
+ */
+void
+pmap_update()
+{
+ tlbflush();
+}
+
+/*
+ * Routine: pmap_kernel
+ * Function:
+ * Returns the physical map handle for the kernel.
+ */
+pmap_t
+pmap_kernel()
+{
+ return (kernel_pmap);
+}
+
+/*
+ * pmap_zero_page zeros the specified (machine independent)
+ * page by mapping the page into virtual memory and using
+ * bzero to clear its contents, one machine dependent page
+ * at a time.
+ */
+void
+pmap_zero_page(phys)
+ vm_offset_t phys;
+{
+ *(int *)CMAP2 = PG_V | PG_KW | i386_trunc_page(phys);
+ tlbflush();
+ bzero(CADDR2,NBPG);
+}
+
+/*
+ * pmap_copy_page copies the specified (machine independent)
+ * page by mapping the page into virtual memory and using
+ * bcopy to copy the page, one machine dependent page at a
+ * time.
+ */
+void
+pmap_copy_page(src, dst)
+ vm_offset_t src;
+ vm_offset_t dst;
+{
+ *(int *)CMAP1 = PG_V | PG_KW | i386_trunc_page(src);
+ *(int *)CMAP2 = PG_V | PG_KW | i386_trunc_page(dst);
+ tlbflush();
+
+#if __GNUC__ > 1
+ memcpy(CADDR2, CADDR1, NBPG);
+#else
+ bcopy(CADDR1, CADDR2, NBPG);
+#endif
+}
+
+
+/*
+ * Routine: pmap_pageable
+ * Function:
+ * Make the specified pages (by pmap, offset)
+ * pageable (or not) as requested.
+ *
+ * A page which is not pageable may not take
+ * a fault; therefore, its page table entry
+ * must remain valid for the duration.
+ *
+ * This routine is merely advisory; pmap_enter
+ * will specify that these pages are to be wired
+ * down (or not) as appropriate.
+ */
+void
+pmap_pageable(pmap, sva, eva, pageable)
+ pmap_t pmap;
+ vm_offset_t sva, eva;
+ boolean_t pageable;
+{
+}
+
+/*
+ * this routine returns true if a physical page resides
+ * in the given pmap.
+ */
+boolean_t
+pmap_page_exists(pmap, pa)
+ pmap_t pmap;
+ vm_offset_t pa;
+{
+ register pv_entry_t pv;
+ int s;
+
+ if (!pmap_is_managed(pa))
+ return FALSE;
+
+ pv = pa_to_pvh(pa);
+ s = splimp();
+
+ /*
+ * Not found, check current mappings returning
+ * immediately if found.
+ */
+ if (pv->pv_pmap != NULL) {
+ for (; pv; pv = pv->pv_next) {
+ if (pv->pv_pmap == pmap) {
+ splx(s);
+ return TRUE;
+ }
+ }
+ }
+ splx(s);
+ return(FALSE);
+}
+
+/*
+ * pmap_testbit tests bits in pte's
+ * note that the testbit/changebit routines are inline,
+ * and a lot of things compile-time evaluate.
+ */
+static inline boolean_t
+pmap_testbit(pa, bit)
+ register vm_offset_t pa;
+ int bit;
+{
+ register pv_entry_t pv;
+ pt_entry_t *pte;
+ int s;
+
+ if (!pmap_is_managed(pa))
+ return FALSE;
+
+ pv = pa_to_pvh(pa);
+ s = splimp();
+
+ /*
+ * Not found, check current mappings returning
+ * immediately if found.
+ */
+ if (pv->pv_pmap != NULL) {
+ for (; pv; pv = pv->pv_next) {
+ /*
+ * if the bit being tested is the modified bit,
+ * then mark UPAGES as always modified, and
+ * ptes as never modified.
+ */
+ if (bit & PG_U ) {
+ if ((pv->pv_va >= clean_sva) && (pv->pv_va < clean_eva)) {
+ continue;
+ }
+ }
+ if (bit & PG_M ) {
+ if (pv->pv_va >= USRSTACK) {
+ if (pv->pv_va >= clean_sva && pv->pv_va < clean_eva) {
+ continue;
+ }
+ if (pv->pv_va < USRSTACK+(UPAGES*NBPG)) {
+ splx(s);
+ return TRUE;
+ }
+ else if (pv->pv_va < UPT_MAX_ADDRESS) {
+ splx(s);
+ return FALSE;
+ }
+ }
+ }
+ pte = pmap_pte(pv->pv_pmap, pv->pv_va);
+ if ((int) *pte & bit) {
+ splx(s);
+ return TRUE;
+ }
+ }
+ }
+ splx(s);
+ return(FALSE);
+}
+
+/*
+ * this routine is used to modify bits in ptes
+ */
+static inline void
+pmap_changebit(pa, bit, setem)
+ vm_offset_t pa;
+ int bit;
+ boolean_t setem;
+{
+ register pv_entry_t pv;
+ register pt_entry_t *pte, npte;
+ vm_offset_t va;
+ int s;
+
+ if (!pmap_is_managed(pa))
+ return;
+
+ pv = pa_to_pvh(pa);
+ s = splimp();
+
+ /*
+ * Loop over all current mappings setting/clearing as appropos
+ * If setting RO do we need to clear the VAC?
+ */
+ if (pv->pv_pmap != NULL) {
+ for (; pv; pv = pv->pv_next) {
+ va = pv->pv_va;
+
+ /*
+ * don't write protect pager mappings
+ */
+ if (!setem && (bit == PG_RW)) {
+ if (va >= clean_sva && va < clean_eva)
+ continue;
+ }
+
+ pte = pmap_pte(pv->pv_pmap, va);
+ if (setem)
+ (int) npte = (int) *pte | bit;
+ else
+ (int) npte = (int) *pte & ~bit;
+ *pte = npte;
+ }
+ }
+ splx(s);
+ tlbflush();
+}
+
+/*
+ * pmap_page_protect:
+ *
+ * Lower the permission for all mappings to a given page.
+ */
+void
+pmap_page_protect(phys, prot)
+ vm_offset_t phys;
+ vm_prot_t prot;
+{
+ if ((prot & VM_PROT_WRITE) == 0) {
+ if (prot & (VM_PROT_READ | VM_PROT_EXECUTE))
+ pmap_changebit(phys, PG_RW, FALSE);
+ else
+ pmap_remove_all(phys);
+ }
+}
+
+/*
+ * Clear the modify bits on the specified physical page.
+ */
+void
+pmap_clear_modify(pa)
+ vm_offset_t pa;
+{
+ pmap_changebit(pa, PG_M, FALSE);
+}
+
+/*
+ * pmap_clear_reference:
+ *
+ * Clear the reference bit on the specified physical page.
+ */
+void
+pmap_clear_reference(pa)
+ vm_offset_t pa;
+{
+ pmap_changebit(pa, PG_U, FALSE);
+}
+
+/*
+ * pmap_is_referenced:
+ *
+ * Return whether or not the specified physical page is referenced
+ * by any physical maps.
+ */
+
+boolean_t
+pmap_is_referenced(pa)
+ vm_offset_t pa;
+{
+ return(pmap_testbit(pa, PG_U));
+}
+
+/*
+ * pmap_is_modified:
+ *
+ * Return whether or not the specified physical page is modified
+ * by any physical maps.
+ */
+
+boolean_t
+pmap_is_modified(pa)
+ vm_offset_t pa;
+{
+ return(pmap_testbit(pa, PG_M));
+}
+
+/*
+ * Routine: pmap_copy_on_write
+ * Function:
+ * Remove write privileges from all
+ * physical maps for this physical page.
+ */
+void
+pmap_copy_on_write(pa)
+ vm_offset_t pa;
+{
+ pmap_changebit(pa, PG_RW, FALSE);
+}
+
+
+vm_offset_t
+pmap_phys_address(ppn)
+ int ppn;
+{
+ return(i386_ptob(ppn));
+}
+
+/*
+ * Miscellaneous support routines follow
+ */
+/*
+ * This really just builds a table for page write enable
+ * translation.
+ */
+
+void
+i386_protection_init()
+{
+ register int *kp, prot;
+
+ kp = protection_codes;
+ for (prot = 0; prot < 8; prot++) {
+ switch (prot) {
+ case VM_PROT_NONE | VM_PROT_NONE | VM_PROT_NONE:
+ case VM_PROT_READ | VM_PROT_NONE | VM_PROT_NONE:
+ case VM_PROT_READ | VM_PROT_NONE | VM_PROT_EXECUTE:
+ case VM_PROT_NONE | VM_PROT_NONE | VM_PROT_EXECUTE:
+ *kp++ = 0;
+ break;
+ case VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_NONE:
+ case VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_EXECUTE:
+ case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_NONE:
+ case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE:
+ *kp++ = PG_RW;
+ break;
+ }
+ }
+}
+
+#ifdef DEBUG
+void
+pmap_pvdump(pa)
+ vm_offset_t pa;
+{
+ register pv_entry_t pv;
+
+ printf("pa %x", pa);
+ for (pv = pa_to_pvh(pa); pv; pv = pv->pv_next) {
+ printf(" -> pmap %x, va %x, flags %x",
+ pv->pv_pmap, pv->pv_va, pv->pv_flags);
+ pads(pv->pv_pmap);
+ }
+ printf(" ");
+}
+
+/* print address space of pmap*/
+void
+pads(pm)
+ pmap_t pm;
+{
+ unsigned va, i, j;
+ pt_entry_t *ptep;
+
+ if (pm == kernel_pmap) return;
+ for (i = 0; i < 1024; i++)
+ if (pm->pm_pdir[i])
+ for (j = 0; j < 1024 ; j++) {
+ va = (i<<PD_SHIFT)+(j<<PG_SHIFT);
+ if (pm == kernel_pmap && va < KERNBASE)
+ continue;
+ if (pm != kernel_pmap && va > UPT_MAX_ADDRESS)
+ continue;
+ ptep = pmap_pte(pm, va);
+ if (pmap_pte_v(ptep))
+ printf("%x:%x ", va, *(int *)ptep);
+ } ;
+
+}
+#endif
diff --git a/sys/i386/i386/procfs_machdep.c b/sys/i386/i386/procfs_machdep.c
new file mode 100644
index 0000000..d122fc6
--- /dev/null
+++ b/sys/i386/i386/procfs_machdep.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993 Jan-Simon Pendry
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_machdep.c 8.3 (Berkeley) 1/27/94
+ *
+ * From:
+ * $Id: procfs_i386.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+/*
+ * Functions to be implemented here are:
+ *
+ * procfs_read_regs(proc, regs)
+ * Get the current user-visible register set from the process
+ * and copy it into the regs structure (<machine/reg.h>).
+ * The process is stopped at the time read_regs is called.
+ *
+ * procfs_write_regs(proc, regs)
+ * Update the current register set from the passed in regs
+ * structure. Take care to avoid clobbering special CPU
+ * registers or privileged bits in the PSL.
+ * The process is stopped at the time write_regs is called.
+ *
+ * procfs_read_fpregs, procfs_write_fpregs
+ * deal with the floating point register set, otherwise as above.
+ *
+ * procfs_sstep(proc)
+ * Arrange for the process to trap after executing a single instruction.
+ *
+ * procfs_fix_sstep(proc)
+ * Cleanup process state after executing a single-step instruction.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/vnode.h>
+#include <machine/psl.h>
+#include <machine/reg.h>
+#include <machine/frame.h>
+#include <miscfs/procfs/procfs.h>
+
+int
+procfs_read_regs(p, regs)
+ struct proc *p;
+ struct reg *regs;
+{
+ struct trapframe *f;
+
+ if ((p->p_flag & P_INMEM) == 0)
+ return (EIO);
+
+ f = (struct trapframe *) p->p_md.md_regs;
+ bcopy((void *) f, (void *) regs, sizeof(*regs));
+
+ return (0);
+}
+
+/*
+ * Update the process's current register
+ * set. Depending on the architecture this
+ * may have fix-up work to do, especially
+ * if the IAR or PCW are modified.
+ */
+int
+procfs_write_regs(p, regs)
+ struct proc *p;
+ struct reg *regs;
+{
+ struct trapframe *f;
+
+ if ((p->p_flag & P_INMEM) == 0)
+ return (EIO);
+
+ f = (struct trapframe *) p->p_md.md_regs;
+ bcopy((void *) regs, (void *) f, sizeof(*regs));
+
+ return (0);
+}
+
+int
+procfs_read_fpregs(p, fpregs)
+ struct proc *p;
+ struct fpreg *fpregs;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+procfs_write_fpregs(p, fpregs)
+ struct proc *p;
+ struct fpreg *fpregs;
+{
+
+ return (EOPNOTSUPP);
+}
+
+
+int
+procfs_sstep(p)
+ struct proc *p;
+{
+ int error;
+ struct reg r;
+
+ error = procfs_read_regs(p, &r);
+ if (error == 0) {
+ r.r_eflags |= PSL_T;
+ error = procfs_write_regs(p, &r);
+ }
+
+ return (error);
+}
+
+void
+procfs_fix_sstep(p)
+ struct proc *p;
+{
+}
diff --git a/sys/i386/i386/support.s b/sys/i386/i386/support.s
new file mode 100644
index 0000000..c4e37df
--- /dev/null
+++ b/sys/i386/i386/support.s
@@ -0,0 +1,1221 @@
+/*-
+ * Copyright (c) 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: support.s,v 1.10 1994/06/06 14:23:49 davidg Exp $
+ */
+
+#include "assym.s" /* system definitions */
+#include "errno.h" /* error return codes */
+#include "machine/asmacros.h" /* miscellaneous asm macros */
+#include "machine/cputypes.h" /* types of CPUs */
+
+#define KDSEL 0x10 /* kernel data selector */
+#define IDXSHIFT 10
+
+/*
+ * Support routines for GCC, general C-callable functions
+ */
+ENTRY(__udivsi3)
+ movl 4(%esp),%eax
+ xorl %edx,%edx
+ divl 8(%esp)
+ ret
+
+ENTRY(__divsi3)
+ movl 4(%esp),%eax
+ cltd
+ idivl 8(%esp)
+ ret
+
+ /*
+ * I/O bus instructions via C
+ */
+ENTRY(inb) /* val = inb(port) */
+ movl 4(%esp),%edx
+ subl %eax,%eax
+ inb %dx,%al
+ NOP
+ ret
+
+ENTRY(inw) /* val = inw(port) */
+ movl 4(%esp),%edx
+ subl %eax,%eax
+ inw %dx,%ax
+ NOP
+ ret
+
+ENTRY(insb) /* insb(port, addr, cnt) */
+ pushl %edi
+ movl 8(%esp),%edx
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ insb
+ NOP
+ movl %edi,%eax
+ popl %edi
+ ret
+
+ENTRY(insw) /* insw(port, addr, cnt) */
+ pushl %edi
+ movl 8(%esp),%edx
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ insw
+ NOP
+ movl %edi,%eax
+ popl %edi
+ ret
+
+ENTRY(insl) /* insl(port, addr, cnt) */
+ pushl %edi
+ movl 8(%esp),%edx
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ insl
+ NOP
+ movl %edi,%eax
+ popl %edi
+ ret
+
+ENTRY(rtcin) /* rtcin(val) */
+ movl 4(%esp),%eax
+ outb %al,$0x70
+ NOP
+ xorl %eax,%eax
+ inb $0x71,%al
+ NOP
+ ret
+
+ENTRY(outb) /* outb(port, val) */
+ movl 4(%esp),%edx
+ movl 8(%esp),%eax
+ outb %al,%dx
+ NOP
+ ret
+
+ENTRY(outw) /* outw(port, val) */
+ movl 4(%esp),%edx
+ movl 8(%esp),%eax
+ outw %ax,%dx
+ NOP
+ ret
+
+ENTRY(outsb) /* outsb(port, addr, cnt) */
+ pushl %esi
+ movl 8(%esp),%edx
+ movl 12(%esp),%esi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ outsb
+ NOP
+ movl %esi,%eax
+ popl %esi
+ ret
+
+ENTRY(outsw) /* outsw(port, addr, cnt) */
+ pushl %esi
+ movl 8(%esp),%edx
+ movl 12(%esp),%esi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ outsw
+ NOP
+ movl %esi,%eax
+ popl %esi
+ ret
+
+ENTRY(outsl) /* outsl(port, addr, cnt) */
+ pushl %esi
+ movl 8(%esp),%edx
+ movl 12(%esp),%esi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ outsl
+ NOP
+ movl %esi,%eax
+ popl %esi
+ ret
+
+/*
+ * bcopy family
+ */
+
+/*
+ * void bzero(void *base, u_int cnt)
+ * Special code for I486 because stosl uses lots
+ * of clocks. Makes little or no difference on DX2 type
+ * machines, but stosl is about 1/2 as fast as
+ * memory moves on a standard DX !!!!!
+ */
+ALTENTRY(blkclr)
+ENTRY(bzero)
+#if defined(I486_CPU)
+ cmpl $CPUCLASS_486,_cpu_class
+ jz 1f
+#endif
+
+ pushl %edi
+ movl 8(%esp),%edi
+ movl 12(%esp),%ecx
+ xorl %eax,%eax
+ shrl $2,%ecx
+ cld
+ rep
+ stosl
+ movl 12(%esp),%ecx
+ andl $3,%ecx
+ rep
+ stosb
+ popl %edi
+ ret
+
+#if defined(I486_CPU)
+ SUPERALIGN_TEXT
+1:
+ movl 4(%esp),%edx
+ movl 8(%esp),%ecx
+ xorl %eax,%eax
+/
+/ do 64 byte chunks first
+/
+/ XXX this is probably over-unrolled at least for DX2's
+/
+2:
+ cmpl $64,%ecx
+ jb 3f
+ movl %eax,(%edx)
+ movl %eax,4(%edx)
+ movl %eax,8(%edx)
+ movl %eax,12(%edx)
+ movl %eax,16(%edx)
+ movl %eax,20(%edx)
+ movl %eax,24(%edx)
+ movl %eax,28(%edx)
+ movl %eax,32(%edx)
+ movl %eax,36(%edx)
+ movl %eax,40(%edx)
+ movl %eax,44(%edx)
+ movl %eax,48(%edx)
+ movl %eax,52(%edx)
+ movl %eax,56(%edx)
+ movl %eax,60(%edx)
+ addl $64,%edx
+ subl $64,%ecx
+ jnz 2b
+ ret
+
+/
+/ do 16 byte chunks
+/
+ SUPERALIGN_TEXT
+3:
+ cmpl $16,%ecx
+ jb 4f
+ movl %eax,(%edx)
+ movl %eax,4(%edx)
+ movl %eax,8(%edx)
+ movl %eax,12(%edx)
+ addl $16,%edx
+ subl $16,%ecx
+ jnz 3b
+ ret
+
+/
+/ do 4 byte chunks
+/
+ SUPERALIGN_TEXT
+4:
+ cmpl $4,%ecx
+ jb 5f
+ movl %eax,(%edx)
+ addl $4,%edx
+ subl $4,%ecx
+ jnz 4b
+ ret
+
+/
+/ do 1 byte chunks
+/ a jump table seems to be faster than a loop or more range reductions
+/
+/ XXX need a const section for non-text
+/
+ SUPERALIGN_TEXT
+jtab:
+ .long do0
+ .long do1
+ .long do2
+ .long do3
+
+ SUPERALIGN_TEXT
+5:
+ jmp jtab(,%ecx,4)
+
+ SUPERALIGN_TEXT
+do3:
+ movw %ax,(%edx)
+ movb %al,2(%edx)
+ ret
+
+ SUPERALIGN_TEXT
+do2:
+ movw %ax,(%edx)
+ ret
+
+ SUPERALIGN_TEXT
+do1:
+ movb %al,(%edx)
+
+ SUPERALIGN_TEXT
+do0:
+ ret
+#endif /* I486_CPU */
+
+/* fillw(pat, base, cnt) */
+ENTRY(fillw)
+ pushl %edi
+ movl 8(%esp),%eax
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ stosw
+ popl %edi
+ ret
+
+/* filli(pat, base, cnt) */
+ENTRY(filli)
+ pushl %edi
+ movl 8(%esp),%eax
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ stosl
+ popl %edi
+ ret
+
+ENTRY(bcopyb)
+bcopyb:
+ pushl %esi
+ pushl %edi
+ movl 12(%esp),%esi
+ movl 16(%esp),%edi
+ movl 20(%esp),%ecx
+ cmpl %esi,%edi /* potentially overlapping? */
+ jnb 1f
+ cld /* nope, copy forwards */
+ rep
+ movsb
+ popl %edi
+ popl %esi
+ ret
+
+ ALIGN_TEXT
+1:
+ addl %ecx,%edi /* copy backwards. */
+ addl %ecx,%esi
+ std
+ decl %edi
+ decl %esi
+ rep
+ movsb
+ popl %edi
+ popl %esi
+ cld
+ ret
+
+ENTRY(bcopyw)
+bcopyw:
+ pushl %esi
+ pushl %edi
+ movl 12(%esp),%esi
+ movl 16(%esp),%edi
+ movl 20(%esp),%ecx
+ cmpl %esi,%edi /* potentially overlapping? */
+ jnb 1f
+ shrl $1,%ecx /* copy by 16-bit words */
+ cld /* nope, copy forwards */
+ rep
+ movsw
+ adc %ecx,%ecx /* any bytes left? */
+ rep
+ movsb
+ popl %edi
+ popl %esi
+ ret
+
+ ALIGN_TEXT
+1:
+ addl %ecx,%edi /* copy backwards */
+ addl %ecx,%esi
+ andl $1,%ecx /* any fractional bytes? */
+ decl %edi
+ decl %esi
+ std
+ rep
+ movsb
+ movl 20(%esp),%ecx /* copy remainder by 16-bit words */
+ shrl $1,%ecx
+ decl %esi
+ decl %edi
+ rep
+ movsw
+ popl %edi
+ popl %esi
+ cld
+ ret
+
+ENTRY(bcopyx)
+ movl 16(%esp),%eax
+ cmpl $2,%eax
+ je bcopyw /* not _bcopyw, to avoid multiple mcounts */
+ cmpl $4,%eax
+ je bcopy /* XXX the shared ret's break mexitcount */
+ jmp bcopyb
+
+/*
+ * (ov)bcopy(src, dst, cnt)
+ * ws@tools.de (Wolfgang Solfrank, TooLs GmbH) +49-228-985800
+ */
+ALTENTRY(ovbcopy)
+ENTRY(bcopy)
+bcopy:
+ pushl %esi
+ pushl %edi
+ movl 12(%esp),%esi
+ movl 16(%esp),%edi
+ movl 20(%esp),%ecx
+ cmpl %esi,%edi /* potentially overlapping? */
+ jnb 1f
+ shrl $2,%ecx /* copy by 32-bit words */
+ cld /* nope, copy forwards */
+ rep
+ movsl
+ movl 20(%esp),%ecx
+ andl $3,%ecx /* any bytes left? */
+ rep
+ movsb
+ popl %edi
+ popl %esi
+ ret
+
+ ALIGN_TEXT
+1:
+ addl %ecx,%edi /* copy backwards */
+ addl %ecx,%esi
+ andl $3,%ecx /* any fractional bytes? */
+ decl %edi
+ decl %esi
+ std
+ rep
+ movsb
+ movl 20(%esp),%ecx /* copy remainder by 32-bit words */
+ shrl $2,%ecx
+ subl $3,%esi
+ subl $3,%edi
+ rep
+ movsl
+ popl %edi
+ popl %esi
+ cld
+ ret
+
+ALTENTRY(ntohl)
+ENTRY(htonl)
+ movl 4(%esp),%eax
+#ifdef i486
+/* XXX */
+/* Since Gas 1.38 does not grok bswap this has been coded as the
+ * equivalent bytes. This can be changed back to bswap when we
+ * upgrade to a newer version of Gas
+ */
+ /* bswap %eax */
+ .byte 0x0f
+ .byte 0xc8
+#else
+ xchgb %al,%ah
+ roll $16,%eax
+ xchgb %al,%ah
+#endif
+ ret
+
+ALTENTRY(ntohs)
+ENTRY(htons)
+ movzwl 4(%esp),%eax
+ xchgb %al,%ah
+ ret
+
+/*****************************************************************************/
+/* copyout and fubyte family */
+/*****************************************************************************/
+/*
+ * Access user memory from inside the kernel. These routines and possibly
+ * the math- and DOS emulators should be the only places that do this.
+ *
+ * We have to access the memory with user's permissions, so use a segment
+ * selector with RPL 3. For writes to user space we have to additionally
+ * check the PTE for write permission, because the 386 does not check
+ * write permissions when we are executing with EPL 0. The 486 does check
+ * this if the WP bit is set in CR0, so we can use a simpler version here.
+ *
+ * These routines set curpcb->onfault for the time they execute. When a
+ * protection violation occurs inside the functions, the trap handler
+ * returns to *curpcb->onfault instead of the function.
+ */
+
+
+ENTRY(copyout) /* copyout(from_kernel, to_user, len) */
+ movl _curpcb,%eax
+ movl $copyout_fault,PCB_ONFAULT(%eax)
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+ movl 16(%esp),%esi
+ movl 20(%esp),%edi
+ movl 24(%esp),%ebx
+ orl %ebx,%ebx /* anything to do? */
+ jz done_copyout
+
+ /*
+ * Check explicitly for non-user addresses. If 486 write protection
+ * is being used, this check is essential because we are in kernel
+ * mode so the h/w does not provide any protection against writing
+ * kernel addresses.
+ *
+ * Otherwise, it saves having to load and restore %es to get the
+ * usual segment-based protection (the destination segment for movs
+ * is always %es). The other explicit checks for user-writablility
+ * are not quite sufficient. They fail for the user area because
+ * we mapped the user area read/write to avoid having an #ifdef in
+ * vm_machdep.c. They fail for user PTEs and/or PTDs! (107
+ * addresses including 0xff800000 and 0xfc000000). I'm not sure if
+ * this can be fixed. Marking the PTEs supervisor mode and the
+ * PDE's user mode would almost work, but there may be a problem
+ * with the self-referential PDE.
+ */
+ movl %edi,%eax
+ addl %ebx,%eax
+ jc copyout_fault
+/*
+ * XXX STOP USING VM_MAXUSER_ADDRESS.
+ * It is an end address, not a max, so every time it is used correctly it
+ * looks like there is an off by one error, and of course it caused an off
+ * by one error in several places.
+ */
+ cmpl $VM_MAXUSER_ADDRESS,%eax
+ ja copyout_fault
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 3f
+#endif
+/*
+ * We have to check each PTE for user write permission.
+ * The checking may cause a page fault, so it is important to set
+ * up everything for return via copyout_fault before here.
+ */
+ /* compute number of pages */
+ movl %edi,%ecx
+ andl $NBPG-1,%ecx
+ addl %ebx,%ecx
+ decl %ecx
+ shrl $IDXSHIFT+2,%ecx
+ incl %ecx
+
+ /* compute PTE offset for start address */
+ movl %edi,%edx
+ shrl $IDXSHIFT,%edx
+ andb $0xfc,%dl
+
+1: /* check PTE for each page */
+ movb _PTmap(%edx),%al
+ andb $0x07,%al /* Pages must be VALID + USERACC + WRITABLE */
+ cmpb $0x07,%al
+ je 2f
+
+ /* simulate a trap */
+ pushl %edx
+ pushl %ecx
+ shll $IDXSHIFT,%edx
+ pushl %edx
+ call _trapwrite /* trapwrite(addr) */
+ popl %edx
+ popl %ecx
+ popl %edx
+
+ orl %eax,%eax /* if not ok, return EFAULT */
+ jnz copyout_fault
+
+2:
+ addl $4,%edx
+ decl %ecx
+ jnz 1b /* check next page */
+#endif /* I386_CPU */
+
+ /* bcopy(%esi, %edi, %ebx) */
+3:
+ movl %ebx,%ecx
+ shrl $2,%ecx
+ cld
+ rep
+ movsl
+ movb %bl,%cl
+ andb $3,%cl
+ rep
+ movsb
+
+done_copyout:
+ popl %ebx
+ popl %edi
+ popl %esi
+ xorl %eax,%eax
+ movl _curpcb,%edx
+ movl %eax,PCB_ONFAULT(%edx)
+ ret
+
+ ALIGN_TEXT
+copyout_fault:
+ popl %ebx
+ popl %edi
+ popl %esi
+ movl _curpcb,%edx
+ movl $0,PCB_ONFAULT(%edx)
+ movl $EFAULT,%eax
+ ret
+
+/* copyin(from_user, to_kernel, len) */
+ENTRY(copyin)
+ movl _curpcb,%eax
+ movl $copyin_fault,PCB_ONFAULT(%eax)
+ pushl %esi
+ pushl %edi
+ movl 12(%esp),%esi /* caddr_t from */
+ movl 16(%esp),%edi /* caddr_t to */
+ movl 20(%esp),%ecx /* size_t len */
+
+ /*
+ * make sure address is valid
+ */
+ movl %esi,%edx
+ addl %ecx,%edx
+ jc copyin_fault
+ cmpl $VM_MAXUSER_ADDRESS,%edx
+ ja copyin_fault
+
+ movb %cl,%al
+ shrl $2,%ecx /* copy longword-wise */
+ cld
+ rep
+ movsl
+ movb %al,%cl
+ andb $3,%cl /* copy remaining bytes */
+ rep
+ movsb
+
+ popl %edi
+ popl %esi
+ xorl %eax,%eax
+ movl _curpcb,%edx
+ movl %eax,PCB_ONFAULT(%edx)
+ ret
+
+ ALIGN_TEXT
+copyin_fault:
+ popl %edi
+ popl %esi
+ movl _curpcb,%edx
+ movl $0,PCB_ONFAULT(%edx)
+ movl $EFAULT,%eax
+ ret
+
+/*
+ * fu{byte,sword,word} : fetch a byte (sword, word) from user memory
+ */
+ALTENTRY(fuiword)
+ENTRY(fuword)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx /* from */
+
+ cmpl $VM_MAXUSER_ADDRESS-4,%edx /* verify address is valid */
+ ja fusufault
+
+ movl (%edx),%eax
+ movl $0,PCB_ONFAULT(%ecx)
+ ret
+
+/*
+ * These two routines are called from the profiling code, potentially
+ * at interrupt time. If they fail, that's okay, good things will
+ * happen later. Fail all the time for now - until the trap code is
+ * able to deal with this.
+ */
+ALTENTRY(suswintr)
+ENTRY(fuswintr)
+ movl $-1,%eax
+ ret
+
+ENTRY(fusword)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+ cmpl $VM_MAXUSER_ADDRESS-2,%edx
+ ja fusufault
+
+ movzwl (%edx),%eax
+ movl $0,PCB_ONFAULT(%ecx)
+ ret
+
+ALTENTRY(fuibyte)
+ENTRY(fubyte)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+ cmpl $VM_MAXUSER_ADDRESS-1,%edx
+ ja fusufault
+
+ movzbl (%edx),%eax
+ movl $0,PCB_ONFAULT(%ecx)
+ ret
+
+ ALIGN_TEXT
+fusufault:
+ movl _curpcb,%ecx
+ xorl %eax,%eax
+ movl %eax,PCB_ONFAULT(%ecx)
+ decl %eax
+ ret
+
+/*
+ * su{byte,sword,word}: write a byte (word, longword) to user memory
+ */
+ALTENTRY(suiword)
+ENTRY(suword)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 2f /* we only have to set the right segment selector */
+#endif /* I486_CPU || I586_CPU */
+
+ /* XXX - page boundary crossing is still not handled */
+ movl %edx,%eax
+ shrl $IDXSHIFT,%edx
+ andb $0xfc,%dl
+ movb _PTmap(%edx),%dl
+ andb $0x7,%dl /* must be VALID + USERACC + WRITE */
+ cmpb $0x7,%dl
+ je 1f
+
+ /* simulate a trap */
+ pushl %eax
+ call _trapwrite
+ popl %edx /* remove junk parameter from stack */
+ movl _curpcb,%ecx /* restore trashed register */
+ orl %eax,%eax
+ jnz fusufault
+1:
+ movl 4(%esp),%edx
+#endif
+
+2:
+ cmpl $VM_MAXUSER_ADDRESS-4,%edx /* verify address validity */
+ ja fusufault
+
+ movl 8(%esp),%eax
+ movl %eax,(%edx)
+ xorl %eax,%eax
+ movl %eax,PCB_ONFAULT(%ecx)
+ ret
+
+ENTRY(susword)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 2f
+#endif /* I486_CPU || I586_CPU */
+
+ /* XXX - page boundary crossing is still not handled */
+ movl %edx,%eax
+ shrl $IDXSHIFT,%edx
+ andb $0xfc,%dl
+ movb _PTmap(%edx),%dl
+ andb $0x7,%dl /* must be VALID + USERACC + WRITE */
+ cmpb $0x7,%dl
+ je 1f
+
+ /* simulate a trap */
+ pushl %eax
+ call _trapwrite
+ popl %edx /* remove junk parameter from stack */
+ movl _curpcb,%ecx /* restore trashed register */
+ orl %eax,%eax
+ jnz fusufault
+1:
+ movl 4(%esp),%edx
+#endif
+
+2:
+ cmpl $VM_MAXUSER_ADDRESS-2,%edx /* verify address validity */
+ ja fusufault
+
+ movw 8(%esp),%ax
+ movw %ax,(%edx)
+ xorl %eax,%eax
+ movl %eax,PCB_ONFAULT(%ecx)
+ ret
+
+ALTENTRY(suibyte)
+ENTRY(subyte)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 2f
+#endif /* I486_CPU || I586_CPU */
+
+ movl %edx,%eax
+ shrl $IDXSHIFT,%edx
+ andb $0xfc,%dl
+ movb _PTmap(%edx),%dl
+ andb $0x7,%dl /* must be VALID + USERACC + WRITE */
+ cmpb $0x7,%dl
+ je 1f
+
+ /* simulate a trap */
+ pushl %eax
+ call _trapwrite
+ popl %edx /* remove junk parameter from stack */
+ movl _curpcb,%ecx /* restore trashed register */
+ orl %eax,%eax
+ jnz fusufault
+1:
+ movl 4(%esp),%edx
+#endif
+
+2:
+ cmpl $VM_MAXUSER_ADDRESS-1,%edx /* verify address validity */
+ ja fusufault
+
+ movb 8(%esp),%al
+ movb %al,(%edx)
+ xorl %eax,%eax
+ movl %eax,PCB_ONFAULT(%ecx)
+ ret
+
+/*
+ * copyoutstr(from, to, maxlen, int *lencopied)
+ * copy a string from from to to, stop when a 0 character is reached.
+ * return ENAMETOOLONG if string is longer than maxlen, and
+ * EFAULT on protection violations. If lencopied is non-zero,
+ * return the actual length in *lencopied.
+ */
+ENTRY(copyoutstr)
+ pushl %esi
+ pushl %edi
+ movl _curpcb,%ecx
+ movl $cpystrflt,PCB_ONFAULT(%ecx) /* XXX rename copyoutstr_fault */
+
+ movl 12(%esp),%esi /* %esi = from */
+ movl 16(%esp),%edi /* %edi = to */
+ movl 20(%esp),%edx /* %edx = maxlen */
+ cld
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 5f
+#endif /* I486_CPU || I586_CPU */
+
+1:
+ /*
+ * It suffices to check that the first byte is in user space, because
+ * we look at a page at a time and the end address is on a page
+ * boundary.
+ */
+ cmpl $VM_MAXUSER_ADDRESS-1,%edi
+ ja cpystrflt
+
+ movl %edi,%eax
+ shrl $IDXSHIFT,%eax
+ andb $0xfc,%al
+ movb _PTmap(%eax),%al
+ andb $7,%al
+ cmpb $7,%al
+ je 2f
+
+ /* simulate trap */
+ pushl %edx
+ pushl %edi
+ call _trapwrite
+ cld
+ popl %edi
+ popl %edx
+ orl %eax,%eax
+ jnz cpystrflt
+
+2: /* copy up to end of this page */
+ movl %edi,%eax
+ andl $NBPG-1,%eax
+ movl $NBPG,%ecx
+ subl %eax,%ecx /* ecx = NBPG - (src % NBPG) */
+ cmpl %ecx,%edx
+ jae 3f
+ movl %edx,%ecx /* ecx = min(ecx, edx) */
+3:
+ orl %ecx,%ecx
+ jz 4f
+ decl %ecx
+ decl %edx
+ lodsb
+ stosb
+ orb %al,%al
+ jnz 3b
+
+ /* Success -- 0 byte reached */
+ decl %edx
+ xorl %eax,%eax
+ jmp 6f
+
+4: /* next page */
+ orl %edx,%edx
+ jnz 1b
+
+ /* edx is zero -- return ENAMETOOLONG */
+ movl $ENAMETOOLONG,%eax
+ jmp cpystrflt_x
+#endif /* I386_CPU */
+
+#if defined(I486_CPU) || defined(I586_CPU)
+5:
+ incl %edx
+1:
+ decl %edx
+ jz 2f
+ /*
+ * XXX - would be faster to rewrite this function to use
+ * strlen() and copyout().
+ */
+ cmpl $VM_MAXUSER_ADDRESS-1,%edi
+ ja cpystrflt
+
+ lodsb
+ stosb
+ orb %al,%al
+ jnz 1b
+
+ /* Success -- 0 byte reached */
+ decl %edx
+ xorl %eax,%eax
+ jmp cpystrflt_x
+2:
+ /* edx is zero -- return ENAMETOOLONG */
+ movl $ENAMETOOLONG,%eax
+ jmp cpystrflt_x
+
+#endif /* I486_CPU || I586_CPU */
+
+/*
+ * This was split from copyinstr_fault mainly because pushing gs changes the
+ * stack offsets. It's better to have it separate for mcounting too.
+ */
+cpystrflt:
+ movl $EFAULT,%eax
+cpystrflt_x:
+ /* set *lencopied and return %eax */
+ movl _curpcb,%ecx
+ movl $0,PCB_ONFAULT(%ecx)
+ movl 20(%esp),%ecx
+ subl %edx,%ecx
+ movl 24(%esp),%edx
+ orl %edx,%edx
+ jz 1f
+ movl %ecx,(%edx)
+1:
+ popl %edi
+ popl %esi
+ ret
+
+
+/*
+ * copyinstr(from, to, maxlen, int *lencopied)
+ * copy a string from from to to, stop when a 0 character is reached.
+ * return ENAMETOOLONG if string is longer than maxlen, and
+ * EFAULT on protection violations. If lencopied is non-zero,
+ * return the actual length in *lencopied.
+ */
+ENTRY(copyinstr)
+ pushl %esi
+ pushl %edi
+ movl _curpcb,%ecx
+ movl $copyinstr_fault,PCB_ONFAULT(%ecx)
+
+ movl 12(%esp),%esi /* %esi = from */
+ movl 16(%esp),%edi /* %edi = to */
+ movl 20(%esp),%edx /* %edx = maxlen */
+ /*
+ * XXX should avoid touching gs. Either copy the string in and
+ * check the bounds later or get its length and check the bounds
+ * and then use copyin().
+ */
+ pushl %gs
+ movl __udatasel,%eax
+ movl %ax,%gs
+ incl %edx
+ cld
+1:
+ decl %edx
+ jz 2f
+ gs
+ lodsb
+ stosb
+ orb %al,%al
+ jnz 1b
+
+ /* Success -- 0 byte reached */
+ decl %edx
+ xorl %eax,%eax
+ jmp 3f
+2:
+ /* edx is zero -- return ENAMETOOLONG */
+ movl $ENAMETOOLONG,%eax
+ jmp 3f
+
+ ALIGN_TEXT
+copyinstr_fault:
+ movl $EFAULT,%eax
+3:
+ /* set *lencopied and return %eax */
+ movl _curpcb,%ecx
+ movl $0,PCB_ONFAULT(%ecx)
+ movl 24(%esp),%ecx
+ subl %edx,%ecx
+ movl 28(%esp),%edx
+ orl %edx,%edx
+ jz 4f
+ movl %ecx,(%edx)
+4:
+ popl %gs
+ popl %edi
+ popl %esi
+ ret
+
+
+/*
+ * copystr(from, to, maxlen, int *lencopied)
+ */
+ENTRY(copystr)
+ pushl %esi
+ pushl %edi
+
+ movl 12(%esp),%esi /* %esi = from */
+ movl 16(%esp),%edi /* %edi = to */
+ movl 20(%esp),%edx /* %edx = maxlen */
+ incl %edx
+ cld
+1:
+ decl %edx
+ jz 4f
+ lodsb
+ stosb
+ orb %al,%al
+ jnz 1b
+
+ /* Success -- 0 byte reached */
+ decl %edx
+ xorl %eax,%eax
+ jmp 6f
+4:
+ /* edx is zero -- return ENAMETOOLONG */
+ movl $ENAMETOOLONG,%eax
+
+6:
+ /* set *lencopied and return %eax */
+ movl 20(%esp),%ecx
+ subl %edx,%ecx
+ movl 24(%esp),%edx
+ orl %edx,%edx
+ jz 7f
+ movl %ecx,(%edx)
+7:
+ popl %edi
+ popl %esi
+ ret
+
+/*
+ * Handling of special 386 registers and descriptor tables etc
+ */
+/* void lgdt(struct region_descriptor *rdp); */
+ENTRY(lgdt)
+ /* reload the descriptor table */
+ movl 4(%esp),%eax
+ lgdt (%eax)
+
+ /* flush the prefetch q */
+ jmp 1f
+ nop
+1:
+ /* reload "stale" selectors */
+ movl $KDSEL,%eax
+ movl %ax,%ds
+ movl %ax,%es
+ movl %ax,%ss
+
+ /* reload code selector by turning return into intersegmental return */
+ movl (%esp),%eax
+ pushl %eax
+# movl $KCSEL,4(%esp)
+ movl $8,4(%esp)
+ lret
+
+/*
+ * void lidt(struct region_descriptor *rdp);
+ */
+ENTRY(lidt)
+ movl 4(%esp),%eax
+ lidt (%eax)
+ ret
+
+/*
+ * void lldt(u_short sel)
+ */
+ENTRY(lldt)
+ lldt 4(%esp)
+ ret
+
+/*
+ * void ltr(u_short sel)
+ */
+ENTRY(ltr)
+ ltr 4(%esp)
+ ret
+
+/* ssdtosd(*ssdp,*sdp) */
+ENTRY(ssdtosd)
+ pushl %ebx
+ movl 8(%esp),%ecx
+ movl 8(%ecx),%ebx
+ shll $16,%ebx
+ movl (%ecx),%edx
+ roll $16,%edx
+ movb %dh,%bl
+ movb %dl,%bh
+ rorl $8,%ebx
+ movl 4(%ecx),%eax
+ movw %ax,%dx
+ andl $0xf0000,%eax
+ orl %eax,%ebx
+ movl 12(%esp),%ecx
+ movl %edx,(%ecx)
+ movl %ebx,4(%ecx)
+ popl %ebx
+ ret
+
+/* load_cr0(cr0) */
+ENTRY(load_cr0)
+ movl 4(%esp),%eax
+ movl %eax,%cr0
+ ret
+
+/* rcr0() */
+ENTRY(rcr0)
+ movl %cr0,%eax
+ ret
+
+/* rcr3() */
+ENTRY(rcr3)
+ movl %cr3,%eax
+ ret
+
+/* void load_cr3(caddr_t cr3) */
+ENTRY(load_cr3)
+ movl 4(%esp),%eax
+ orl $I386_CR3PAT,%eax
+ movl %eax,%cr3
+ ret
+
+
+/*****************************************************************************/
+/* setjump, longjump */
+/*****************************************************************************/
+
+ENTRY(setjmp)
+ movl 4(%esp),%eax
+ movl %ebx,(%eax) /* save ebx */
+ movl %esp,4(%eax) /* save esp */
+ movl %ebp,8(%eax) /* save ebp */
+ movl %esi,12(%eax) /* save esi */
+ movl %edi,16(%eax) /* save edi */
+ movl (%esp),%edx /* get rta */
+ movl %edx,20(%eax) /* save eip */
+ xorl %eax,%eax /* return(0); */
+ ret
+
+ENTRY(longjmp)
+ movl 4(%esp),%eax
+ movl (%eax),%ebx /* restore ebx */
+ movl 4(%eax),%esp /* restore esp */
+ movl 8(%eax),%ebp /* restore ebp */
+ movl 12(%eax),%esi /* restore esi */
+ movl 16(%eax),%edi /* restore edi */
+ movl 20(%eax),%edx /* get rta */
+ movl %edx,(%esp) /* put in return frame */
+ xorl %eax,%eax /* return(1); */
+ incl %eax
+ ret
diff --git a/sys/i386/i386/swapgeneric.c b/sys/i386/i386/swapgeneric.c
new file mode 100644
index 0000000..b987afb
--- /dev/null
+++ b/sys/i386/i386/swapgeneric.c
@@ -0,0 +1,169 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)swapgeneric.c 5.5 (Berkeley) 5/9/91
+ * $Id: swapgeneric.c,v 1.2 1993/10/16 14:15:09 rgrimes Exp $
+ */
+
+#include "machine/pte.h"
+
+#include "sys/param.h"
+#include "sys/conf.h"
+#include "sys/buf.h"
+#include "sys/vm.h"
+#include "sys/systm.h"
+#include "sys/reboot.h"
+
+/*
+ * Generic configuration; all in one
+ */
+dev_t rootdev = makedev(0,0);
+dev_t dumpdev = makedev(0,1);
+int nswap;
+struct swdevt swdevt[] = {
+ { 1, 0, 0 },
+ { 0, 1, 0 },
+};
+long dumplo;
+int dmmin, dmmax, dmtext;
+
+extern struct driver wddriver;
+extern struct driver wxdriver;
+
+struct genericconf {
+ caddr_t gc_driver;
+ char *gc_name;
+ dev_t gc_root;
+} genericconf[] = {
+ { (caddr_t)&wddriver, "wd", makedev(0, 0), },
+ { (caddr_t)&wxdriver, "wx", makedev(0, 0), },
+ { 0 },
+};
+
+setconf()
+{
+#ifdef notdef
+ register struct genericconf *gc;
+ int unit, swaponroot = 0;
+
+ if (rootdev != NODEV)
+ goto doswap;
+ if (boothowto & RB_ASKNAME) {
+ char name[128];
+retry:
+ printf("root device? ");
+ gets(name);
+ for (gc = genericconf; gc->gc_driver; gc++)
+ if (gc->gc_name[0] == name[0] &&
+ gc->gc_name[1] == name[1])
+ goto gotit;
+ goto bad;
+gotit:
+ if (name[3] == '*') {
+ name[3] = name[4];
+ swaponroot++;
+ }
+ if (name[2] >= '0' && name[2] <= '7' && name[3] == 0) {
+ unit = name[2] - '0';
+ goto found;
+ }
+ printf("bad/missing unit number\n");
+bad:
+ printf("use dk%%d\n");
+ goto retry;
+ }
+ unit = 0;
+ for (gc = genericconf; gc->gc_driver; gc++) {
+ for (ui = vbdinit; ui->ui_driver; ui++) {
+ if (ui->ui_alive == 0)
+ continue;
+ if (ui->ui_unit == 0 && ui->ui_driver ==
+ (struct vba_driver *)gc->gc_driver) {
+ printf("root on %s0\n",
+ ui->ui_driver->ud_dname);
+ goto found;
+ }
+ }
+ }
+ printf("no suitable root\n");
+ asm("halt");
+found:
+ gc->gc_root = makedev(major(gc->gc_root), unit*8);
+ rootdev = gc->gc_root;
+doswap:
+ swdevt[0].sw_dev = argdev = dumpdev =
+ makedev(major(rootdev), minor(rootdev)+1);
+ /* swap size and dumplo set during autoconfigure */
+ if (swaponroot)
+ rootdev = dumpdev;
+#endif
+}
+
+gets(cp)
+ char *cp;
+{
+ register char *lp;
+ register c;
+
+ lp = cp;
+ for (;;) {
+ printf("%c", c = cngetc()&0177);
+ switch (c) {
+ case '\n':
+ case '\r':
+ *lp++ = '\0';
+ return;
+ case '\b':
+ case '\177':
+ if (lp > cp) {
+ printf(" \b");
+ lp--;
+ }
+ continue;
+ case '#':
+ lp--;
+ if (lp < cp)
+ lp = cp;
+ continue;
+ case '@':
+ case 'u'&037:
+ lp = cp;
+ printf("%c", '\n');
+ continue;
+ default:
+ *lp++ = c;
+ }
+ }
+}
diff --git a/sys/i386/i386/swtch.s b/sys/i386/i386/swtch.s
new file mode 100644
index 0000000..aa8b5ba
--- /dev/null
+++ b/sys/i386/i386/swtch.s
@@ -0,0 +1,458 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: swtch.s,v 1.5 1994/04/02 07:00:30 davidg Exp $
+ */
+
+#include "npx.h" /* for NNPX */
+#include "assym.s" /* for preprocessor defines */
+#include "errno.h" /* for error codes */
+
+#include "machine/asmacros.h" /* for miscellaneous assembly macros */
+#define LOCORE /* XXX inhibit C declarations */
+#include "machine/spl.h" /* for SWI_AST_MASK ... */
+
+
+/*****************************************************************************/
+/* Scheduling */
+/*****************************************************************************/
+
+/*
+ * The following primitives manipulate the run queues.
+ * _whichqs tells which of the 32 queues _qs
+ * have processes in them. setrunqueue puts processes into queues, Remrq
+ * removes them from queues. The running process is on no queue,
+ * other processes are on a queue related to p->p_priority, divided by 4
+ * actually to shrink the 0-127 range of priorities into the 32 available
+ * queues.
+ */
+ .data
+ .globl _curpcb, _whichqs
+_curpcb: .long 0 /* pointer to curproc's PCB area */
+_whichqs: .long 0 /* which run queues have data */
+
+ .globl _qs,_cnt,_panic
+ .comm _noproc,4
+ .comm _runrun,4
+
+ .globl _want_resched
+_want_resched: .long 0 /* we need to re-run the scheduler */
+
+ .text
+/*
+ * setrunqueue(p)
+ *
+ * Call should be made at spl6(), and p->p_stat should be SRUN
+ */
+ENTRY(setrunqueue)
+ movl 4(%esp),%eax
+ cmpl $0,P_RLINK(%eax) /* should not be on q already */
+ je set1
+ pushl $set2
+ call _panic
+set1:
+ movzbl P_PRI(%eax),%edx
+ shrl $2,%edx
+ btsl %edx,_whichqs /* set q full bit */
+ shll $3,%edx
+ addl $_qs,%edx /* locate q hdr */
+ movl %edx,P_LINK(%eax) /* link process on tail of q */
+ movl P_RLINK(%edx),%ecx
+ movl %ecx,P_RLINK(%eax)
+ movl %eax,P_RLINK(%edx)
+ movl %eax,P_LINK(%ecx)
+ ret
+
+set2: .asciz "setrunqueue"
+
+/*
+ * Remrq(p)
+ *
+ * Call should be made at spl6().
+ */
+ENTRY(remrq)
+ movl 4(%esp),%eax
+ movzbl P_PRI(%eax),%edx
+ shrl $2,%edx
+ btrl %edx,_whichqs /* clear full bit, panic if clear already */
+ jb rem1
+ pushl $rem3
+ call _panic
+rem1:
+ pushl %edx
+ movl P_LINK(%eax),%ecx /* unlink process */
+ movl P_RLINK(%eax),%edx
+ movl %edx,P_RLINK(%ecx)
+ movl P_RLINK(%eax),%ecx
+ movl P_LINK(%eax),%edx
+ movl %edx,P_LINK(%ecx)
+ popl %edx
+ movl $_qs,%ecx
+ shll $3,%edx
+ addl %edx,%ecx
+ cmpl P_LINK(%ecx),%ecx /* q still has something? */
+ je rem2
+ shrl $3,%edx /* yes, set bit as still full */
+ btsl %edx,_whichqs
+rem2:
+ movl $0,P_RLINK(%eax) /* zap reverse link to indicate off list */
+ ret
+
+rem3: .asciz "remrq"
+sw0: .asciz "cpu_switch"
+
+/*
+ * When no processes are on the runq, cpu_switch() branches to _idle
+ * to wait for something to come ready.
+ */
+ ALIGN_TEXT
+_idle:
+ MCOUNT
+ movl _IdlePTD,%ecx
+ movl %ecx,%cr3
+ movl $tmpstk-4,%esp
+ sti
+
+ /*
+ * XXX callers of cpu_switch() do a bogus splclock(). Locking should
+ * be left to cpu_switch().
+ */
+ movl $SWI_AST_MASK,_cpl
+ testl $~SWI_AST_MASK,_ipending
+ je idle_loop
+ call _splz
+
+ ALIGN_TEXT
+idle_loop:
+ cli
+ cmpl $0,_whichqs
+ jne sw1a
+ sti
+ hlt /* wait for interrupt */
+ jmp idle_loop
+
+badsw:
+ pushl $sw0
+ call _panic
+ /*NOTREACHED*/
+
+/*
+ * cpu_switch()
+ */
+ENTRY(cpu_switch)
+ incl _cnt+V_SWTCH
+
+ /* switch to new process. first, save context as needed */
+
+ movl _curproc,%ecx
+
+ /* if no process to save, don't bother */
+ testl %ecx,%ecx
+ je sw1
+
+ movl P_ADDR(%ecx),%ecx
+
+ movl (%esp),%eax /* Hardware registers */
+ movl %eax,PCB_EIP(%ecx)
+ movl %ebx,PCB_EBX(%ecx)
+ movl %esp,PCB_ESP(%ecx)
+ movl %ebp,PCB_EBP(%ecx)
+ movl %esi,PCB_ESI(%ecx)
+ movl %edi,PCB_EDI(%ecx)
+
+#if NNPX > 0
+ /* have we used fp, and need a save? */
+ mov _curproc,%eax
+ cmp %eax,_npxproc
+ jne 1f
+ pushl %ecx /* h/w bugs make saving complicated */
+ leal PCB_SAVEFPU(%ecx),%eax
+ pushl %eax
+ call _npxsave /* do it in a big C function */
+ popl %eax
+ popl %ecx
+1:
+#endif /* NNPX > 0 */
+
+ movl _CMAP2,%eax /* save temporary map PTE */
+ movl %eax,PCB_CMAP2(%ecx) /* in our context */
+ movl $0,_curproc /* out of process */
+
+# movw _cpl,%ax
+# movw %ax,PCB_IML(%ecx) /* save ipl */
+
+ /* save is done, now choose a new process or idle */
+sw1:
+ cli
+sw1a:
+ movl _whichqs,%edi
+2:
+ /* XXX - bsf is sloow */
+ bsfl %edi,%eax /* find a full q */
+ je _idle /* if none, idle */
+
+ /* XX update whichqs? */
+ btrl %eax,%edi /* clear q full status */
+ jnb 2b /* if it was clear, look for another */
+ movl %eax,%ebx /* save which one we are using */
+
+ shll $3,%eax
+ addl $_qs,%eax /* select q */
+ movl %eax,%esi
+
+#ifdef DIAGNOSTIC
+ cmpl P_LINK(%eax),%eax /* linked to self? (e.g. not on list) */
+ je badsw /* not possible */
+#endif
+
+ movl P_LINK(%eax),%ecx /* unlink from front of process q */
+ movl P_LINK(%ecx),%edx
+ movl %edx,P_LINK(%eax)
+ movl P_RLINK(%ecx),%eax
+ movl %eax,P_RLINK(%edx)
+
+ cmpl P_LINK(%ecx),%esi /* q empty */
+ je 3f
+ btsl %ebx,%edi /* nope, set to indicate full */
+3:
+ movl %edi,_whichqs /* update q status */
+
+ movl $0,%eax
+ movl %eax,_want_resched
+
+#ifdef DIAGNOSTIC
+ cmpl %eax,P_WCHAN(%ecx)
+ jne badsw
+ cmpb $SRUN,P_STAT(%ecx)
+ jne badsw
+#endif
+
+ movl %eax,P_RLINK(%ecx) /* isolate process to run */
+ movl P_ADDR(%ecx),%edx
+ movl PCB_CR3(%edx),%ebx
+
+ /* switch address space */
+ movl %ebx,%cr3
+
+ /* restore context */
+ movl PCB_EBX(%edx),%ebx
+ movl PCB_ESP(%edx),%esp
+ movl PCB_EBP(%edx),%ebp
+ movl PCB_ESI(%edx),%esi
+ movl PCB_EDI(%edx),%edi
+ movl PCB_EIP(%edx),%eax
+ movl %eax,(%esp)
+
+ movl PCB_CMAP2(%edx),%eax /* get temporary map */
+ movl %eax,_CMAP2 /* reload temporary map PTE */
+
+ movl %ecx,_curproc /* into next process */
+ movl %edx,_curpcb
+
+#ifdef USER_LDT
+ cmpl $0, PCB_USERLDT(%edx)
+ jnz 1f
+ movl __default_ldt,%eax
+ cmpl _currentldt,%eax
+ je 2f
+ lldt __default_ldt
+ movl %eax,_currentldt
+ jmp 2f
+1: pushl %edx
+ call _set_user_ldt
+ popl %edx
+2:
+#endif
+
+ pushl %edx /* save p to return */
+/*
+ * XXX - 0.0 forgot to save it - is that why this was commented out in 0.1?
+ * I think restoring the cpl is unnecessary, but we must turn off the cli
+ * now that spl*() don't do it as a side affect.
+ */
+ pushl PCB_IML(%edx)
+ sti
+#if 0
+ call _splx
+#endif
+ addl $4,%esp
+/*
+ * XXX - 0.0 gets here via swtch_to_inactive(). I think 0.1 gets here in the
+ * same way. Better return a value.
+ */
+ popl %eax /* return(p); */
+ ret
+
+ENTRY(mvesp)
+ movl %esp,%eax
+ ret
+/*
+ * struct proc *swtch_to_inactive(struct proc *p);
+ *
+ * At exit of a process, move off the address space of the
+ * process and onto a "safe" one. Then, on a temporary stack
+ * return and run code that disposes of the old state.
+ * Since this code requires a parameter from the "old" stack,
+ * pass it back as a return value.
+ */
+ENTRY(swtch_to_inactive)
+ popl %edx /* old pc */
+ popl %eax /* arg, our return value */
+ movl _IdlePTD,%ecx
+ movl %ecx,%cr3 /* good bye address space */
+ #write buffer?
+ movl $tmpstk-4,%esp /* temporary stack, compensated for call */
+ MEXITCOUNT
+ jmp %edx /* return, execute remainder of cleanup */
+
+/*
+ * savectx(pcb, altreturn)
+ * Update pcb, saving current processor state and arranging
+ * for alternate return ala longjmp in cpu_switch if altreturn is true.
+ */
+ENTRY(savectx)
+ movl 4(%esp),%ecx
+ movw _cpl,%ax
+ movw %ax,PCB_IML(%ecx)
+ movl (%esp),%eax
+ movl %eax,PCB_EIP(%ecx)
+ movl %ebx,PCB_EBX(%ecx)
+ movl %esp,PCB_ESP(%ecx)
+ movl %ebp,PCB_EBP(%ecx)
+ movl %esi,PCB_ESI(%ecx)
+ movl %edi,PCB_EDI(%ecx)
+
+#if NNPX > 0
+ /*
+ * If npxproc == NULL, then the npx h/w state is irrelevant and the
+ * state had better already be in the pcb. This is true for forks
+ * but not for dumps (the old book-keeping with FP flags in the pcb
+ * always lost for dumps because the dump pcb has 0 flags).
+ *
+ * If npxproc != NULL, then we have to save the npx h/w state to
+ * npxproc's pcb and copy it to the requested pcb, or save to the
+ * requested pcb and reload. Copying is easier because we would
+ * have to handle h/w bugs for reloading. We used to lose the
+ * parent's npx state for forks by forgetting to reload.
+ */
+ mov _npxproc,%eax
+ testl %eax,%eax
+ je 1f
+
+ pushl %ecx
+ movl P_ADDR(%eax),%eax
+ leal PCB_SAVEFPU(%eax),%eax
+ pushl %eax
+ pushl %eax
+ call _npxsave
+ popl %eax
+ popl %eax
+ popl %ecx
+
+ pushl %ecx
+ pushl $108+8*2 /* XXX h/w state size + padding */
+ leal PCB_SAVEFPU(%ecx),%ecx
+ pushl %ecx
+ pushl %eax
+ call _bcopy
+ addl $12,%esp
+ popl %ecx
+1:
+#endif /* NNPX > 0 */
+
+ movl _CMAP2,%edx /* save temporary map PTE */
+ movl %edx,PCB_CMAP2(%ecx) /* in our context */
+
+ cmpl $0,8(%esp)
+ je 1f
+ movl %esp,%edx /* relocate current sp relative to pcb */
+ subl $_kstack,%edx /* (sp is relative to kstack): */
+ addl %edx,%ecx /* pcb += sp - kstack; */
+ movl %eax,(%ecx) /* write return pc at (relocated) sp@ */
+
+/* this mess deals with replicating register state gcc hides */
+ movl 12(%esp),%eax
+ movl %eax,12(%ecx)
+ movl 16(%esp),%eax
+ movl %eax,16(%ecx)
+ movl 20(%esp),%eax
+ movl %eax,20(%ecx)
+ movl 24(%esp),%eax
+ movl %eax,24(%ecx)
+1:
+ xorl %eax,%eax /* return 0 */
+ ret
+
+/*
+ * addupc(int pc, struct uprof *up, int ticks):
+ * update profiling information for the user process.
+ */
+ENTRY(addupc)
+ pushl %ebp
+ movl %esp,%ebp
+ movl 12(%ebp),%edx /* up */
+ movl 8(%ebp),%eax /* pc */
+
+ subl PR_OFF(%edx),%eax /* pc -= up->pr_off */
+ jb L1 /* if (pc was < off) return */
+
+ shrl $1,%eax /* praddr = pc >> 1 */
+ imull PR_SCALE(%edx),%eax /* praddr *= up->pr_scale */
+ shrl $15,%eax /* praddr = praddr << 15 */
+ andl $-2,%eax /* praddr &= ~1 */
+
+ cmpl PR_SIZE(%edx),%eax /* if (praddr > up->pr_size) return */
+ ja L1
+
+/* addl %eax,%eax /* praddr -> word offset */
+ addl PR_BASE(%edx),%eax /* praddr += up-> pr_base */
+ movl 16(%ebp),%ecx /* ticks */
+
+ movl _curpcb,%edx
+ movl $proffault,PCB_ONFAULT(%edx)
+ addl %ecx,(%eax) /* storage location += ticks */
+ movl $0,PCB_ONFAULT(%edx)
+L1:
+ leave
+ ret
+
+ ALIGN_TEXT
+proffault:
+ /* if we get a fault, then kill profiling all together */
+ movl $0,PCB_ONFAULT(%edx) /* squish the fault handler */
+ movl 12(%ebp),%ecx
+ movl $0,PR_SCALE(%ecx) /* up->pr_scale = 0 */
+ leave
+ ret
diff --git a/sys/i386/i386/symbols.raw b/sys/i386/i386/symbols.raw
new file mode 100644
index 0000000..084f9b6
--- /dev/null
+++ b/sys/i386/i386/symbols.raw
@@ -0,0 +1,84 @@
+# @(#)symbols.raw 7.6 (Berkeley) 5/8/91
+#
+# $Id$
+#
+
+
+#gdb
+ _IdlePTD
+ _PTD
+ _curpcb
+ _kstack
+ _panicstr
+ _atdevbase
+# _version
+#dmesg
+ _msgbufp
+# _msgbuf
+#iostat
+ _dk_busy
+ _dk_time
+ _dk_xfer
+ _dk_wds
+ _tk_nin
+ _tk_nout
+ _dk_seek
+ _cp_time
+ _dk_wpms
+# _io_info
+#ps
+ _nswap
+ _maxslp
+ _ccpu
+ _fscale
+ _avail_start
+ _avail_end
+#pstat
+# _cons
+ _nswap
+ _swapmap
+#vmstat
+ _cp_time
+# _rate
+ _total
+# _sum
+# _rectime
+# _pgintime
+ _dk_xfer
+ _boottime
+#w
+ _swapdev
+ _nswap
+ _averunnable
+ _boottime
+#netstat
+ _mbstat
+ _ipstat
+ _tcb
+ _tcpstat
+ _udb
+ _udpstat
+ _rawcb
+ _Sysmap
+ _ifnet
+# _rthost
+# _rtnet
+ _icmpstat
+ _filehead
+ _nfiles
+# _rthashsize
+# _radix_node_head
+#routed
+ _ifnet
+#rwho
+ _boottime
+#savecore
+ _dumpdev
+ _dumplo
+ _version
+ _time
+ _dumpsize
+ _panicstr
+ _dumpmag
+#deprecated
+# _avenrun
diff --git a/sys/i386/i386/sys_machdep.c b/sys/i386/i386/sys_machdep.c
new file mode 100644
index 0000000..92758ad
--- /dev/null
+++ b/sys/i386/i386/sys_machdep.c
@@ -0,0 +1,328 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)sys_machdep.c 5.5 (Berkeley) 1/19/91
+ * $Id: sys_machdep.c,v 1.3 1993/10/16 14:15:10 rgrimes Exp $
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "ioctl.h"
+#include "file.h"
+#include "time.h"
+#include "proc.h"
+#include "uio.h"
+#include "kernel.h"
+#include "mtio.h"
+#include "buf.h"
+#include "trace.h"
+
+#ifdef USER_LDT
+#include "user.h"
+#include "machine/cpu.h"
+#include "machine/sysarch.h"
+#include "vm/vm_kern.h" /* for kernel_map */
+#endif
+
+#ifdef TRACE
+int nvualarm;
+
+struct vtrace_args {
+ int request;
+ int value;
+};
+
+vtrace(p, uap, retval)
+ struct proc *p;
+ register struct vtrace_args *uap;
+ int *retval;
+{
+ int vdoualarm();
+
+ switch (uap->request) {
+
+ case VTR_DISABLE: /* disable a trace point */
+ case VTR_ENABLE: /* enable a trace point */
+ if (uap->value < 0 || uap->value >= TR_NFLAGS)
+ return (EINVAL);
+ *retval = traceflags[uap->value];
+ traceflags[uap->value] = uap->request;
+ break;
+
+ case VTR_VALUE: /* return a trace point setting */
+ if (uap->value < 0 || uap->value >= TR_NFLAGS)
+ return (EINVAL);
+ *retval = traceflags[uap->value];
+ break;
+
+ case VTR_UALARM: /* set a real-time ualarm, less than 1 min */
+ if (uap->value <= 0 || uap->value > 60 * hz || nvualarm > 5)
+ return (EINVAL);
+ nvualarm++;
+ timeout(vdoualarm, (caddr_t)p->p_pid, uap->value);
+ break;
+
+ case VTR_STAMP:
+ trace(TR_STAMP, uap->value, p->p_pid);
+ break;
+ }
+ return (0);
+}
+
+vdoualarm(arg)
+ int arg;
+{
+ register struct proc *p;
+
+ p = pfind(arg);
+ if (p)
+ psignal(p, 16);
+ nvualarm--;
+}
+#endif
+
+#ifdef USER_LDT
+void
+set_user_ldt(struct pcb *pcb)
+{
+ gdt_segs[GUSERLDT_SEL].ssd_base = (unsigned)pcb->pcb_ldt;
+ gdt_segs[GUSERLDT_SEL].ssd_limit = (pcb->pcb_ldt_len * sizeof(union descriptor)) - 1;
+ ssdtosd(gdt_segs+GUSERLDT_SEL, gdt+GUSERLDT_SEL);
+ lldt(GSEL(GUSERLDT_SEL, SEL_KPL));
+ currentldt = GSEL(GUSERLDT_SEL, SEL_KPL);
+}
+
+struct i386_get_ldt_args {
+ int start;
+ union descriptor *desc;
+ int num;
+};
+
+int
+i386_get_ldt(p, args, retval)
+ struct proc *p;
+ char *args;
+ int *retval;
+{
+ int error = 0;
+ struct pcb *pcb = &p->p_addr->u_pcb;
+ int nldt, num;
+ union descriptor *lp;
+ int s;
+ struct i386_get_ldt_args ua, *uap;
+
+ if ((error = copyin(args, &ua, sizeof(struct i386_get_ldt_args))) < 0)
+ return(error);
+
+ uap = &ua;
+#ifdef DEBUG
+ printf("i386_get_ldt: start=%d num=%d descs=%x\n", uap->start, uap->num, uap->desc);
+#endif
+
+ if (uap->start < 0 || uap->num < 0)
+ return(EINVAL);
+
+ s = splhigh();
+
+ if (pcb->pcb_ldt) {
+ nldt = pcb->pcb_ldt_len;
+ num = min(uap->num, nldt);
+ lp = &((union descriptor *)(pcb->pcb_ldt))[uap->start];
+ } else {
+ nldt = sizeof(ldt)/sizeof(ldt[0]);
+ num = min(uap->num, nldt);
+ lp = &ldt[uap->start];
+ }
+ if (uap->start > nldt) {
+ splx(s);
+ return(EINVAL);
+ }
+
+ error = copyout(lp, uap->desc, num * sizeof(union descriptor));
+ if (!error)
+ *retval = num;
+
+ splx(s);
+ return(error);
+}
+
+struct i386_set_ldt_args {
+ int start;
+ union descriptor *desc;
+ int num;
+};
+
+int
+i386_set_ldt(p, args, retval)
+ struct proc *p;
+ char *args;
+ int *retval;
+{
+ int error = 0, i, n;
+ struct pcb *pcb = &p->p_addr->u_pcb;
+ union descriptor *lp;
+ int s;
+ struct i386_set_ldt_args ua, *uap;
+
+ if ((error = copyin(args, &ua, sizeof(struct i386_set_ldt_args))) < 0)
+ return(error);
+
+ uap = &ua;
+
+#ifdef DEBUG
+ printf("i386_set_ldt: start=%d num=%d descs=%x\n", uap->start, uap->num, uap->desc);
+#endif
+
+ if (uap->start < 0 || uap->num < 0)
+ return(EINVAL);
+
+ /* XXX Should be 8192 ! */
+ if (uap->start > 512 ||
+ (uap->start + uap->num) > 512)
+ return(EINVAL);
+
+ /* allocate user ldt */
+ if (!pcb->pcb_ldt) {
+ union descriptor *new_ldt =
+ (union descriptor *)kmem_alloc(kernel_map, 512*sizeof(union descriptor));
+ bzero(new_ldt, 512*sizeof(union descriptor));
+ bcopy(ldt, new_ldt, sizeof(ldt));
+ pcb->pcb_ldt = (caddr_t)new_ldt;
+ pcb->pcb_ldt_len = 512; /* XXX need to grow */
+#ifdef DEBUG
+ printf("i386_set_ldt(%d): new_ldt=%x\n", p->p_pid, new_ldt);
+#endif
+ }
+
+ /* Check descriptors for access violations */
+ for (i = 0, n = uap->start; i < uap->num; i++, n++) {
+ union descriptor desc, *dp;
+ dp = &uap->desc[i];
+ error = copyin(dp, &desc, sizeof(union descriptor));
+ if (error)
+ return(error);
+
+ /* Only user (ring-3) descriptors */
+ if (desc.sd.sd_dpl != SEL_UPL)
+ return(EACCES);
+
+ /* Must be "present" */
+ if (desc.sd.sd_p == 0)
+ return(EACCES);
+
+ switch (desc.sd.sd_type) {
+ case SDT_SYSNULL:
+ case SDT_SYS286CGT:
+ case SDT_SYS386CGT:
+ break;
+ case SDT_MEMRO:
+ case SDT_MEMROA:
+ case SDT_MEMRW:
+ case SDT_MEMRWA:
+ case SDT_MEMROD:
+ case SDT_MEMRODA:
+ case SDT_MEME:
+ case SDT_MEMEA:
+ case SDT_MEMER:
+ case SDT_MEMERA:
+ case SDT_MEMEC:
+ case SDT_MEMEAC:
+ case SDT_MEMERC:
+ case SDT_MEMERAC: {
+#if 0
+ unsigned long base = (desc.sd.sd_hibase << 24)&0xFF000000;
+ base |= (desc.sd.sd_lobase&0x00FFFFFF);
+ if (base >= KERNBASE)
+ return(EACCES);
+#endif
+ break;
+ }
+ default:
+ return(EACCES);
+ /*NOTREACHED*/
+ }
+ }
+
+ s = splhigh();
+
+ /* Fill in range */
+ for (i = 0, n = uap->start; i < uap->num && !error; i++, n++) {
+ union descriptor desc, *dp;
+ dp = &uap->desc[i];
+ lp = &((union descriptor *)(pcb->pcb_ldt))[n];
+#ifdef DEBUG
+ printf("i386_set_ldt(%d): ldtp=%x\n", p->p_pid, lp);
+#endif
+ error = copyin(dp, lp, sizeof(union descriptor));
+ }
+ if (!error) {
+ *retval = uap->start;
+/* need_resched(); */
+ }
+
+ splx(s);
+ return(error);
+}
+#endif /* USER_LDT */
+
+struct sysarch_args {
+ int op;
+ char *parms;
+};
+
+int
+sysarch(p, uap, retval)
+ struct proc *p;
+ register struct sysarch_args *uap;
+ int *retval;
+{
+ int error = 0;
+
+ switch(uap->op) {
+#ifdef USER_LDT
+ case I386_GET_LDT:
+ error = i386_get_ldt(p, uap->parms, retval);
+ break;
+
+ case I386_SET_LDT:
+ error = i386_set_ldt(p, uap->parms, retval);
+ break;
+#endif
+ default:
+ error = EINVAL;
+ break;
+ }
+ return(error);
+}
diff --git a/sys/i386/i386/trap.c b/sys/i386/i386/trap.c
new file mode 100644
index 0000000..7084791
--- /dev/null
+++ b/sys/i386/i386/trap.c
@@ -0,0 +1,692 @@
+/*-
+ * Copyright (C) 1994, David Greenman
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the University of Utah, and William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)trap.c 7.4 (Berkeley) 5/13/91
+ * $Id: trap.c,v 1.26 1994/06/11 05:13:33 davidg Exp $
+ */
+
+/*
+ * 386 Trap and System call handling
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/acct.h>
+#include <sys/kernel.h>
+#include <sys/syscall.h>
+#ifdef KTRACE
+#include <sys/ktrace.h>
+#endif
+
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+#include <vm/vm_map.h>
+#include <vm/vm_page.h>
+
+#include <machine/cpu.h>
+#include <machine/psl.h>
+#include <machine/reg.h>
+#include <machine/eflags.h>
+
+#include <machine/trap.h>
+
+#include "isa.h"
+#include "npx.h"
+#include "ddb.h"
+
+int trap_pfault __P((struct trapframe *, int));
+void trap_fatal __P((struct trapframe *));
+
+extern int grow(struct proc *,u_int);
+
+struct sysent sysent[];
+int nsysent;
+
+#define MAX_TRAP_MSG 27
+char *trap_msg[] = {
+ "reserved addressing fault", /* 0 T_RESADFLT */
+ "privileged instruction fault", /* 1 T_PRIVINFLT */
+ "reserved operand fault", /* 2 T_RESOPFLT */
+ "breakpoint instruction fault", /* 3 T_BPTFLT */
+ "", /* 4 unused */
+ "system call trap", /* 5 T_SYSCALL */
+ "arithmetic trap", /* 6 T_ARITHTRAP */
+ "system forced exception", /* 7 T_ASTFLT */
+ "segmentation (limit) fault", /* 8 T_SEGFLT */
+ "general protection fault", /* 9 T_PROTFLT */
+ "trace trap", /* 10 T_TRCTRAP */
+ "", /* 11 unused */
+ "page fault", /* 12 T_PAGEFLT */
+ "page table fault", /* 13 T_TABLEFLT */
+ "alignment fault", /* 14 T_ALIGNFLT */
+ "kernel stack pointer not valid", /* 15 T_KSPNOTVAL */
+ "bus error", /* 16 T_BUSERR */
+ "kernel debugger fault", /* 17 T_KDBTRAP */
+ "integer divide fault", /* 18 T_DIVIDE */
+ "non-maskable interrupt trap", /* 19 T_NMI */
+ "overflow trap", /* 20 T_OFLOW */
+ "FPU bounds check fault", /* 21 T_BOUND */
+ "FPU device not available", /* 22 T_DNA */
+ "double fault", /* 23 T_DOUBLEFLT */
+ "FPU operand fetch fault", /* 24 T_FPOPFLT */
+ "invalid TSS fault", /* 25 T_TSSFLT */
+ "segment not present fault", /* 26 T_SEGNPFLT */
+ "stack fault", /* 27 T_STKFLT */
+};
+
+static inline void
+userret(p, frame, oticks)
+ struct proc *p;
+ struct trapframe *frame;
+ u_quad_t oticks;
+{
+ int sig, s;
+
+ while (sig = CURSIG(p))
+ postsig(sig);
+ p->p_priority = p->p_usrpri;
+ if (want_resched) {
+ /*
+ * Since we are curproc, clock will normally just change
+ * our priority without moving us from one queue to another
+ * (since the running process is not on a queue.)
+ * If that happened after we setrunqueue ourselves but before we
+ * mi_switch()'ed, we might not be on the queue indicated by
+ * our priority.
+ */
+ s = splclock();
+ setrunqueue(p);
+ p->p_stats->p_ru.ru_nivcsw++;
+ mi_switch();
+ splx(s);
+ while (sig = CURSIG(p))
+ postsig(sig);
+ }
+ if (p->p_stats->p_prof.pr_scale) {
+ u_quad_t ticks = p->p_sticks - oticks;
+
+ if (ticks) {
+#ifdef PROFTIMER
+ extern int profscale;
+ addupc(frame->tf_eip, &p->p_stats->p_prof,
+ ticks * profscale);
+#else
+ addupc(frame->tf_eip, &p->p_stats->p_prof, ticks);
+#endif
+ }
+ }
+ curpriority = p->p_priority;
+}
+
+/*
+ * trap(frame):
+ * Exception, fault, and trap interface to the FreeBSD kernel.
+ * This common code is called from assembly language IDT gate entry
+ * routines that prepare a suitable stack frame, and restore this
+ * frame after the exception has been processed.
+ */
+
+/*ARGSUSED*/
+void
+trap(frame)
+ struct trapframe frame;
+{
+ struct proc *p = curproc;
+ u_quad_t sticks = 0;
+ int i = 0, ucode = 0, type, code, eva, fault_type;
+
+ frame.tf_eflags &= ~PSL_NT; /* clear nested trap XXX */
+ type = frame.tf_trapno;
+ code = frame.tf_err;
+
+ if (ISPL(frame.tf_cs) == SEL_UPL) {
+ /* user trap */
+
+ sticks = p->p_sticks;
+ p->p_md.md_regs = (int *)&frame;
+
+ switch (type) {
+ case T_RESADFLT: /* reserved addressing fault */
+ case T_PRIVINFLT: /* privileged instruction fault */
+ case T_RESOPFLT: /* reserved operand fault */
+ ucode = type;
+ i = SIGILL;
+ break;
+
+ case T_BPTFLT: /* bpt instruction fault */
+ case T_TRCTRAP: /* trace trap */
+ frame.tf_eflags &= ~PSL_T;
+ i = SIGTRAP;
+ break;
+
+ case T_ARITHTRAP: /* arithmetic trap */
+ ucode = code;
+ i = SIGFPE;
+ break;
+
+ case T_ASTFLT: /* Allow process switch */
+ astoff();
+ cnt.v_soft++;
+ if ((p->p_flag & P_OWEUPC) && p->p_stats->p_prof.pr_scale) {
+ addupc(frame.tf_eip, &p->p_stats->p_prof, 1);
+ p->p_flag &= ~P_OWEUPC;
+ }
+ goto out;
+
+ case T_PROTFLT: /* general protection fault */
+ case T_SEGNPFLT: /* segment not present fault */
+ case T_STKFLT: /* stack fault */
+ ucode = code + BUS_SEGM_FAULT ;
+ i = SIGBUS;
+ break;
+
+ case T_PAGEFLT: /* page fault */
+ i = trap_pfault(&frame, TRUE);
+ if (i == 0)
+ goto out;
+
+ ucode = T_PAGEFLT;
+ break;
+
+ case T_DIVIDE: /* integer divide fault */
+ ucode = FPE_INTDIV_TRAP;
+ i = SIGFPE;
+ break;
+
+#if NISA > 0
+ case T_NMI:
+#if NDDB > 0
+ /* NMI can be hooked up to a pushbutton for debugging */
+ printf ("NMI ... going to debugger\n");
+ if (kdb_trap (type, 0, &frame))
+ return;
+#endif
+ /* machine/parity/power fail/"kitchen sink" faults */
+ if (isa_nmi(code) == 0) return;
+ /* FALL THROUGH */
+#endif
+
+ case T_OFLOW: /* integer overflow fault */
+ ucode = FPE_INTOVF_TRAP;
+ i = SIGFPE;
+ break;
+
+ case T_BOUND: /* bounds check fault */
+ ucode = FPE_SUBRNG_TRAP;
+ i = SIGFPE;
+ break;
+
+ case T_DNA:
+#if NNPX > 0
+ /* if a transparent fault (due to context switch "late") */
+ if (npxdna())
+ return;
+#endif /* NNPX > 0 */
+
+#if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE)
+ i = math_emulate(&frame);
+ if (i == 0) return;
+#else /* MATH_EMULATE || GPL_MATH_EMULATE */
+ panic("trap: math emulation necessary!");
+#endif /* MATH_EMULATE || GPL_MATH_EMULATE */
+ ucode = FPE_FPU_NP_TRAP;
+ break;
+
+ case T_FPOPFLT: /* FPU operand fetch fault */
+ ucode = T_FPOPFLT;
+ i = SIGILL;
+ break;
+
+ default:
+ trap_fatal(&frame);
+ }
+ } else {
+ /* kernel trap */
+
+ switch (type) {
+ case T_PAGEFLT: /* page fault */
+ (void) trap_pfault(&frame, FALSE);
+ return;
+
+ case T_PROTFLT: /* general protection fault */
+ case T_SEGNPFLT: /* segment not present fault */
+ if (curpcb && curpcb->pcb_onfault) {
+ frame.tf_eip = (int)curpcb->pcb_onfault;
+ return;
+ }
+ break;
+
+#if NDDB > 0
+ case T_BPTFLT:
+ case T_TRCTRAP:
+ if (kdb_trap (type, 0, &frame))
+ return;
+ break;
+#else
+ case T_TRCTRAP: /* trace trap -- someone single stepping lcall's */
+ /* Q: how do we turn it on again? */
+ frame.tf_eflags &= ~PSL_T;
+ return;
+#endif
+
+#if NISA > 0
+ case T_NMI:
+#if NDDB > 0
+ /* NMI can be hooked up to a pushbutton for debugging */
+ printf ("NMI ... going to debugger\n");
+ if (kdb_trap (type, 0, &frame))
+ return;
+#endif
+ /* machine/parity/power fail/"kitchen sink" faults */
+ if (isa_nmi(code) == 0) return;
+ /* FALL THROUGH */
+#endif
+ }
+
+ trap_fatal(&frame);
+ }
+
+ trapsignal(p, i, ucode);
+
+#ifdef DIAGNOSTIC
+ eva = rcr2();
+ if (type <= MAX_TRAP_MSG) {
+ uprintf("fatal process exception: %s",
+ trap_msg[type]);
+ if ((type == T_PAGEFLT) || (type == T_PROTFLT))
+ uprintf(", fault VA = 0x%x", eva);
+ uprintf("\n");
+ }
+#endif
+
+out:
+ userret(p, &frame, sticks);
+}
+
+int
+trap_pfault(frame, usermode)
+ struct trapframe *frame;
+ int usermode;
+{
+ vm_offset_t va;
+ struct vmspace *vm;
+ vm_map_t map = 0;
+ int rv = 0, oldflags;
+ vm_prot_t ftype;
+ extern vm_map_t kernel_map;
+ int eva;
+ struct proc *p = curproc;
+
+ eva = rcr2();
+ va = trunc_page((vm_offset_t)eva);
+
+ /*
+ * Don't allow user-mode faults in kernel address space
+ */
+ if (usermode && (va >= KERNBASE)) {
+ goto nogo;
+ }
+
+ if ((p == 0) || (va >= KERNBASE)) {
+ vm = 0;
+ map = kernel_map;
+ } else {
+ vm = p->p_vmspace;
+ map = &vm->vm_map;
+ }
+
+ if (frame->tf_err & PGEX_W)
+ ftype = VM_PROT_READ | VM_PROT_WRITE;
+ else
+ ftype = VM_PROT_READ;
+
+ if (map != kernel_map) {
+ vm_offset_t pa;
+ vm_offset_t v = (vm_offset_t) vtopte(va);
+ vm_page_t ptepg;
+
+ /*
+ * Keep swapout from messing with us during this
+ * critical time.
+ */
+ ++p->p_lock;
+
+ /*
+ * Grow the stack if necessary
+ */
+ if ((caddr_t)va > vm->vm_maxsaddr
+ && (caddr_t)va < (caddr_t)USRSTACK) {
+ if (!grow(p, va)) {
+ rv = KERN_FAILURE;
+ --p->p_lock;
+ goto nogo;
+ }
+ }
+
+ /*
+ * Check if page table is mapped, if not,
+ * fault it first
+ */
+
+ /* Fault the pte only if needed: */
+ *(volatile char *)v += 0;
+
+ ptepg = (vm_page_t) pmap_pte_vm_page(vm_map_pmap(map), v);
+ if( ptepg->hold_count == 0)
+ ptepg->act_count += 3;
+ vm_page_hold(ptepg);
+
+ /* Fault in the user page: */
+ rv = vm_fault(map, va, ftype, FALSE);
+
+ vm_page_unhold(ptepg);
+
+ /*
+ * page table pages don't need to be kept if they
+ * are not held
+ */
+ if( ptepg->hold_count == 0 && ptepg->wire_count == 0) {
+ pmap_page_protect( VM_PAGE_TO_PHYS(ptepg),
+ VM_PROT_NONE);
+ vm_page_free(ptepg);
+ }
+
+ --p->p_lock;
+ } else {
+ /*
+ * Since we know that kernel virtual address addresses
+ * always have pte pages mapped, we just have to fault
+ * the page.
+ */
+ rv = vm_fault(map, va, ftype, FALSE);
+ }
+
+ if (rv == KERN_SUCCESS)
+ return (0);
+nogo:
+ if (!usermode) {
+ if (curpcb->pcb_onfault) {
+ frame->tf_eip = (int)curpcb->pcb_onfault;
+ return (0);
+ }
+ trap_fatal(frame);
+ }
+
+ /* kludge to pass faulting virtual address to sendsig */
+ frame->tf_err = eva;
+
+ return((rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV);
+}
+
+void
+trap_fatal(frame)
+ struct trapframe *frame;
+{
+ int code, type, eva;
+
+ code = frame->tf_err;
+ type = frame->tf_trapno;
+ eva = rcr2();
+
+ if (type <= MAX_TRAP_MSG)
+ printf("\n\nFatal trap %d: %s while in %s mode\n",
+ type, trap_msg[type],
+ ISPL(frame->tf_cs) == SEL_UPL ? "user" : "kernel");
+ if (type == T_PAGEFLT) {
+ printf("fault virtual address = 0x%x\n", eva);
+ printf("fault code = %s %s, %s\n",
+ code & PGEX_U ? "user" : "supervisor",
+ code & PGEX_W ? "write" : "read",
+ code & PGEX_P ? "protection violation" : "page not present");
+ }
+ printf("instruction pointer = 0x%x\n", frame->tf_eip);
+ printf("processor eflags = ");
+ if (frame->tf_eflags & EFL_TF)
+ printf("trace/trap, ");
+ if (frame->tf_eflags & EFL_IF)
+ printf("interrupt enabled, ");
+ if (frame->tf_eflags & EFL_NT)
+ printf("nested task, ");
+ if (frame->tf_eflags & EFL_RF)
+ printf("resume, ");
+ if (frame->tf_eflags & EFL_VM)
+ printf("vm86, ");
+ printf("IOPL = %d\n", (frame->tf_eflags & EFL_IOPL) >> 12);
+ printf("current process = ");
+ if (curproc) {
+ printf("%d (%s)\n",
+ curproc->p_pid, curproc->p_comm ?
+ curproc->p_comm : "");
+ } else {
+ printf("Idle\n");
+ }
+ printf("interrupt mask = ");
+ if ((cpl & net_imask) == net_imask)
+ printf("net ");
+ if ((cpl & tty_imask) == tty_imask)
+ printf("tty ");
+ if ((cpl & bio_imask) == bio_imask)
+ printf("bio ");
+ if (cpl == 0)
+ printf("none");
+ printf("\n");
+
+#ifdef KDB
+ if (kdb_trap(&psl))
+ return;
+#endif
+#if NDDB > 0
+ if (kdb_trap (type, 0, frame))
+ return;
+#endif
+ if (type <= MAX_TRAP_MSG)
+ panic(trap_msg[type]);
+ else
+ panic("unknown/reserved trap");
+}
+
+/*
+ * Compensate for 386 brain damage (missing URKR).
+ * This is a little simpler than the pagefault handler in trap() because
+ * it the page tables have already been faulted in and high addresses
+ * are thrown out early for other reasons.
+ */
+int trapwrite(addr)
+ unsigned addr;
+{
+ struct proc *p;
+ vm_offset_t va, v;
+ struct vmspace *vm;
+ int oldflags;
+ int rv;
+
+ va = trunc_page((vm_offset_t)addr);
+ /*
+ * XXX - MAX is END. Changed > to >= for temp. fix.
+ */
+ if (va >= VM_MAXUSER_ADDRESS)
+ return (1);
+
+ p = curproc;
+ vm = p->p_vmspace;
+
+ ++p->p_lock;
+
+ if ((caddr_t)va >= vm->vm_maxsaddr
+ && (caddr_t)va < (caddr_t)USRSTACK) {
+ if (!grow(p, va)) {
+ --p->p_lock;
+ return (1);
+ }
+ }
+
+ v = trunc_page(vtopte(va));
+
+ /*
+ * wire the pte page
+ */
+ if (va < USRSTACK) {
+ vm_map_pageable(&vm->vm_map, v, round_page(v+1), FALSE);
+ }
+
+ /*
+ * fault the data page
+ */
+ rv = vm_fault(&vm->vm_map, va, VM_PROT_READ|VM_PROT_WRITE, FALSE);
+
+ /*
+ * unwire the pte page
+ */
+ if (va < USRSTACK) {
+ vm_map_pageable(&vm->vm_map, v, round_page(v+1), TRUE);
+ }
+
+ --p->p_lock;
+
+ if (rv != KERN_SUCCESS)
+ return 1;
+
+ return (0);
+}
+
+/*
+ * syscall(frame):
+ * System call request from POSIX system call gate interface to kernel.
+ * Like trap(), argument is call by reference.
+ */
+/*ARGSUSED*/
+void
+syscall(frame)
+ struct trapframe frame;
+{
+ caddr_t params;
+ int i;
+ struct sysent *callp;
+ struct proc *p = curproc;
+ u_quad_t sticks;
+ int error, opc;
+ int args[8], rval[2];
+ u_int code;
+
+ sticks = p->p_sticks;
+ if (ISPL(frame.tf_cs) != SEL_UPL)
+ panic("syscall");
+
+ code = frame.tf_eax;
+ p->p_md.md_regs = (int *)&frame;
+ params = (caddr_t)frame.tf_esp + sizeof (int) ;
+
+ /*
+ * Reconstruct pc, assuming lcall $X,y is 7 bytes, as it is always.
+ */
+ opc = frame.tf_eip - 7;
+ /*
+ * Need to check if this is a 32 bit or 64 bit syscall.
+ */
+ if (code == SYS_syscall) {
+ /*
+ * Code is first argument, followed by actual args.
+ */
+ code = fuword(params);
+ params += sizeof (int);
+ } else if (code == SYS___syscall) {
+ /*
+ * Like syscall, but code is a quad, so as to maintain
+ * quad alignment for the rest of the arguments.
+ */
+ code = fuword(params + _QUAD_LOWWORD * sizeof(int));
+ params += sizeof(quad_t);
+ }
+
+ if (code >= nsysent)
+ callp = &sysent[0];
+ else
+ callp = &sysent[code];
+
+ if ((i = callp->sy_narg * sizeof (int)) &&
+ (error = copyin(params, (caddr_t)args, (u_int)i))) {
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_SYSCALL))
+ ktrsyscall(p->p_tracep, code, callp->sy_narg, args);
+#endif
+ goto bad;
+ }
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_SYSCALL))
+ ktrsyscall(p->p_tracep, code, callp->sy_narg, args);
+#endif
+ rval[0] = 0;
+ rval[1] = frame.tf_edx;
+
+ error = (*callp->sy_call)(p, args, rval);
+
+ switch (error) {
+
+ case 0:
+ /*
+ * Reinitialize proc pointer `p' as it may be different
+ * if this is a child returning from fork syscall.
+ */
+ p = curproc;
+ frame.tf_eax = rval[0];
+ frame.tf_edx = rval[1];
+ frame.tf_eflags &= ~PSL_C; /* carry bit */
+ break;
+
+ case ERESTART:
+ frame.tf_eip = opc;
+ break;
+
+ case EJUSTRETURN:
+ break;
+
+ default:
+ bad:
+ frame.tf_eax = error;
+ frame.tf_eflags |= PSL_C; /* carry bit */
+ break;
+ }
+
+ userret(p, &frame, sticks);
+
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_SYSRET))
+ ktrsysret(p->p_tracep, code, error, rval[0]);
+#endif
+}
diff --git a/sys/i386/i386/tsc.c b/sys/i386/i386/tsc.c
new file mode 100644
index 0000000..e40079a
--- /dev/null
+++ b/sys/i386/i386/tsc.c
@@ -0,0 +1,442 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz and Don Ahn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)clock.c 7.2 (Berkeley) 5/12/91
+ * $Id: clock.c,v 1.6 1994/02/06 22:48:13 davidg Exp $
+ */
+
+/*
+ * Primitive clock interrupt routines.
+ */
+#include "param.h"
+#include "systm.h"
+#include "time.h"
+#include "kernel.h"
+#include "machine/segments.h"
+#include "machine/frame.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/rtc.h"
+#include "i386/isa/timerreg.h"
+#include <machine/cpu.h>
+
+/* X-tals being what they are, it's nice to be able to fudge this one... */
+/* Note, the name changed here from XTALSPEED to TIMER_FREQ rgrimes 4/26/93 */
+#ifndef TIMER_FREQ
+#define TIMER_FREQ 1193182 /* XXX - should be in isa.h */
+#endif
+#define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x))
+
+void hardclock();
+static int beeping;
+int timer0_divisor = TIMER_DIV(100); /* XXX should be hz */
+u_int timer0_prescale;
+static char timer0_state = 0, timer2_state = 0;
+static char timer0_reprogram = 0;
+static void (*timer_func)() = hardclock;
+static void (*new_function)();
+static u_int new_rate;
+static u_int hardclock_divisor;
+
+
+void
+clkintr(frame)
+ struct clockframe frame;
+{
+ hardclock(&frame);
+}
+
+#if 0
+void
+timerintr(struct clockframe frame)
+{
+ timer_func(&frame);
+ switch (timer0_state) {
+ case 0:
+ break;
+ case 1:
+ if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) {
+ hardclock(&frame);
+ timer0_prescale = 0;
+ }
+ break;
+ case 2:
+ disable_intr();
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+ outb(TIMER_CNTR0, TIMER_DIV(new_rate)%256);
+ outb(TIMER_CNTR0, TIMER_DIV(new_rate)/256);
+ enable_intr();
+ timer0_divisor = TIMER_DIV(new_rate);
+ timer0_prescale = 0;
+ timer_func = new_function;
+ timer0_state = 1;
+ break;
+ case 3:
+ if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) {
+ hardclock(&frame);
+ disable_intr();
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+ outb(TIMER_CNTR0, TIMER_DIV(hz)%256);
+ outb(TIMER_CNTR0, TIMER_DIV(hz)/256);
+ enable_intr();
+ timer0_divisor = TIMER_DIV(hz);
+ timer0_prescale = 0;
+ timer_func = hardclock;;
+ timer0_state = 0;
+ }
+ break;
+ }
+}
+
+#endif
+
+int
+acquire_timer0(int rate, void (*function)() )
+{
+ if (timer0_state || !function)
+ return -1;
+
+ new_function = function;
+ new_rate = rate;
+ timer0_state = 2;
+ return 0;
+}
+
+
+int
+acquire_timer2(int mode)
+{
+ if (timer2_state)
+ return -1;
+ timer2_state = 1;
+ outb(TIMER_MODE, TIMER_SEL2 | (mode &0x3f));
+ return 0;
+}
+
+
+int
+release_timer0()
+{
+ if (!timer0_state)
+ return -1;
+ timer0_state = 3;
+ return 0;
+}
+
+
+int
+release_timer2()
+{
+ if (!timer2_state)
+ return -1;
+ timer2_state = 0;
+ outb(TIMER_MODE, TIMER_SEL2|TIMER_SQWAVE|TIMER_16BIT);
+ return 0;
+}
+
+
+static int
+getit()
+{
+ int high, low;
+
+ disable_intr();
+ /* select timer0 and latch counter value */
+ outb(TIMER_MODE, TIMER_SEL0);
+ low = inb(TIMER_CNTR0);
+ high = inb(TIMER_CNTR0);
+ enable_intr();
+ return ((high << 8) | low);
+}
+
+
+/*
+ * Wait "n" microseconds.
+ * Relies on timer 1 counting down from (TIMER_FREQ / hz)
+ * Note: timer had better have been programmed before this is first used!
+ */
+void
+DELAY(int n)
+{
+ int counter_limit, prev_tick, tick, ticks_left, sec, usec;
+
+#ifdef DELAYDEBUG
+ int getit_calls = 1;
+ int n1;
+ static int state = 0;
+
+ if (state == 0) {
+ state = 1;
+ for (n1 = 1; n1 <= 10000000; n1 *= 10)
+ DELAY(n1);
+ state = 2;
+ }
+ if (state == 1)
+ printf("DELAY(%d)...", n);
+#endif
+ /*
+ * Read the counter first, so that the rest of the setup overhead is
+ * counted. Guess the initial overhead is 20 usec (on most systems it
+ * takes about 1.5 usec for each of the i/o's in getit(). The loop
+ * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The
+ * multiplications and divisions to scale the count take a while).
+ */
+ prev_tick = getit(0, 0);
+ n -= 20;
+ /*
+ * Calculate (n * (TIMER_FREQ / 1e6)) without using floating point
+ * and without any avoidable overflows.
+ */
+ sec = n / 1000000;
+ usec = n - sec * 1000000;
+ ticks_left = sec * TIMER_FREQ
+ + usec * (TIMER_FREQ / 1000000)
+ + usec * ((TIMER_FREQ % 1000000) / 1000) / 1000
+ + usec * (TIMER_FREQ % 1000) / 1000000;
+
+ while (ticks_left > 0) {
+ tick = getit(0, 0);
+#ifdef DELAYDEBUG
+ ++getit_calls;
+#endif
+ if (tick > prev_tick)
+ ticks_left -= prev_tick - (tick - timer0_divisor);
+ else
+ ticks_left -= prev_tick - tick;
+ prev_tick = tick;
+ }
+#ifdef DELAYDEBUG
+ if (state == 1)
+ printf(" %d calls to getit() at %d usec each\n",
+ getit_calls, (n + 5) / getit_calls);
+#endif
+}
+
+
+static void
+sysbeepstop()
+{
+ outb(IO_PPI, inb(IO_PPI)&0xFC); /* disable counter2 output to speaker */
+ release_timer2();
+ beeping = 0;
+}
+
+
+int
+sysbeep(int pitch, int period)
+{
+
+ if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT))
+ return -1;
+ disable_intr();
+ outb(TIMER_CNTR2, pitch);
+ outb(TIMER_CNTR2, (pitch>>8));
+ enable_intr();
+ if (!beeping) {
+ outb(IO_PPI, inb(IO_PPI) | 3); /* enable counter2 output to speaker */
+ beeping = period;
+ timeout(sysbeepstop, 0, period);
+ }
+ return 0;
+}
+
+
+void
+startrtclock()
+{
+ int s;
+
+ /* initialize 8253 clock */
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+
+ /* Correct rounding will buy us a better precision in timekeeping */
+ outb (IO_TIMER1, TIMER_DIV(hz)%256);
+ outb (IO_TIMER1, TIMER_DIV(hz)/256);
+ timer0_divisor = hardclock_divisor = TIMER_DIV(hz);
+
+ /* initialize brain-dead battery powered clock */
+ outb (IO_RTC, RTC_STATUSA);
+ outb (IO_RTC+1, 0x26);
+ outb (IO_RTC, RTC_STATUSB);
+ outb (IO_RTC+1, 2);
+
+ outb (IO_RTC, RTC_DIAG);
+ if (s = inb (IO_RTC+1))
+ printf("RTC BIOS diagnostic error %b\n", s, RTCDG_BITS);
+}
+
+
+/* convert 2 digit BCD number */
+int
+bcd(int i)
+{
+ return ((i/16)*10 + (i%16));
+}
+
+
+/* convert years to seconds (from 1970) */
+unsigned long
+ytos(int y)
+{
+ int i;
+ unsigned long ret;
+
+ ret = 0;
+ for(i = 1970; i < y; i++) {
+ if (i % 4) ret += 365*24*60*60;
+ else ret += 366*24*60*60;
+ }
+ return ret;
+}
+
+
+/* convert months to seconds */
+unsigned long
+mtos(int m, int leap)
+{
+ int i;
+ unsigned long ret;
+
+ ret = 0;
+ for(i=1; i<m; i++) {
+ switch(i){
+ case 1: case 3: case 5: case 7: case 8: case 10: case 12:
+ ret += 31*24*60*60; break;
+ case 4: case 6: case 9: case 11:
+ ret += 30*24*60*60; break;
+ case 2:
+ if (leap) ret += 29*24*60*60;
+ else ret += 28*24*60*60;
+ }
+ }
+ return ret;
+}
+
+
+/*
+ * Initialize the time of day register, based on the time base which is, e.g.
+ * from a filesystem.
+ */
+void
+inittodr(time_t base)
+{
+ unsigned long sec;
+ int leap, day_week, t, yd;
+ int sa,s;
+
+ /* do we have a realtime clock present? (otherwise we loop below) */
+ sa = rtcin(RTC_STATUSA);
+ if (sa == 0xff || sa == 0) return;
+
+ /* ready for a read? */
+ while ((sa&RTCSA_TUP) == RTCSA_TUP)
+ sa = rtcin(RTC_STATUSA);
+
+ sec = bcd(rtcin(RTC_YEAR)) + 1900;
+ if (sec < 1970)
+ sec += 100;
+
+ leap = !(sec % 4); sec = ytos(sec); /* year */
+ yd = mtos(bcd(rtcin(RTC_MONTH)),leap); sec+=yd; /* month */
+ t = (bcd(rtcin(RTC_DAY))-1) * 24*60*60; sec+=t; yd+=t; /* date */
+ day_week = rtcin(RTC_WDAY); /* day */
+ sec += bcd(rtcin(RTC_HRS)) * 60*60; /* hour */
+ sec += bcd(rtcin(RTC_MIN)) * 60; /* minutes */
+ sec += bcd(rtcin(RTC_SEC)); /* seconds */
+ sec += tz.tz_minuteswest * 60;
+ time.tv_sec = sec;
+}
+
+
+#ifdef garbage
+/*
+ * Initialze the time of day register, based on the time base which is, e.g.
+ * from a filesystem.
+ */
+test_inittodr(time_t base)
+{
+
+ outb(IO_RTC,9); /* year */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,8); /* month */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,7); /* day */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,4); /* hour */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,2); /* minutes */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,0); /* seconds */
+ printf("%d\n",bcd(inb(IO_RTC+1)));
+
+ time.tv_sec = base;
+}
+#endif
+
+/*
+ * Wire clock interrupt in.
+ */
+#define V(s) __CONCAT(V, s)
+extern void V(clk)();
+
+
+void
+enablertclock()
+{
+ setidt(ICU_OFFSET+0, &V(clk), SDT_SYS386IGT, SEL_KPL);
+ INTREN(IRQ0);
+}
+
+
+/*
+ * Delay for some number of milliseconds.
+ */
+void
+spinwait(int millisecs)
+{
+ DELAY(1000 * millisecs);
+}
+
+void
+cpu_initclocks()
+{
+ startrtclock();
+ enablertclock();
+}
+
+void
+setstatclockrate(int newhz)
+{
+}
diff --git a/sys/i386/i386/vm_machdep.c b/sys/i386/i386/vm_machdep.c
new file mode 100644
index 0000000..a7c4e59
--- /dev/null
+++ b/sys/i386/i386/vm_machdep.c
@@ -0,0 +1,1246 @@
+/*-
+ * Copyright (c) 1982, 1986 The Regents of the University of California.
+ * Copyright (c) 1989, 1990 William Jolitz
+ * Copyright (c) 1994 John Dyson
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department, and William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)vm_machdep.c 7.3 (Berkeley) 5/13/91
+ * Utah $Hdr: vm_machdep.c 1.16.1.1 89/06/23$
+ * $Id: vm_machdep.c,v 1.20 1994/04/20 07:06:20 davidg Exp $
+ */
+
+#include "npx.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/user.h>
+
+#include <machine/cpu.h>
+
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+
+#define b_cylin b_resid
+
+caddr_t bouncememory;
+vm_offset_t bouncepa, bouncepaend;
+int bouncepages, bpwait;
+vm_map_t io_map;
+int bmwait, bmfreeing;
+
+#define BITS_IN_UNSIGNED (8*sizeof(unsigned))
+int bounceallocarraysize;
+unsigned *bounceallocarray;
+int bouncefree;
+
+#define SIXTEENMEG (4096*4096)
+#define MAXBKVA 512
+int maxbkva=MAXBKVA*NBPG;
+
+/* special list that can be used at interrupt time for eventual kva free */
+struct kvasfree {
+ vm_offset_t addr;
+ vm_offset_t size;
+} kvaf[MAXBKVA];
+
+int kvasfreecnt;
+
+vm_offset_t vm_bounce_kva();
+/*
+ * get bounce buffer pages (count physically contiguous)
+ * (only 1 inplemented now)
+ */
+vm_offset_t
+vm_bounce_page_find(count)
+ int count;
+{
+ int bit;
+ int s,i;
+
+ if (count != 1)
+ panic("vm_bounce_page_find -- no support for > 1 page yet!!!");
+
+ s = splbio();
+retry:
+ for (i = 0; i < bounceallocarraysize; i++) {
+ if (bounceallocarray[i] != 0xffffffff) {
+ if (bit = ffs(~bounceallocarray[i])) {
+ bounceallocarray[i] |= 1 << (bit - 1) ;
+ bouncefree -= count;
+ splx(s);
+ return bouncepa + (i * BITS_IN_UNSIGNED + (bit - 1)) * NBPG;
+ }
+ }
+ }
+ bpwait = 1;
+ tsleep((caddr_t) &bounceallocarray, PRIBIO, "bncwai", 0);
+ goto retry;
+}
+
+void
+vm_bounce_kva_free(addr, size, now)
+ vm_offset_t addr;
+ vm_offset_t size;
+ int now;
+{
+ int s = splbio();
+ kvaf[kvasfreecnt].addr = addr;
+ kvaf[kvasfreecnt++].size = size;
+ if( now) {
+ /*
+ * this will do wakeups
+ */
+ vm_bounce_kva(0,0);
+ } else {
+ if (bmwait) {
+ /*
+ * if anyone is waiting on the bounce-map, then wakeup
+ */
+ wakeup((caddr_t) io_map);
+ bmwait = 0;
+ }
+ }
+ splx(s);
+}
+
+/*
+ * free count bounce buffer pages
+ */
+void
+vm_bounce_page_free(pa, count)
+ vm_offset_t pa;
+ int count;
+{
+ int allocindex;
+ int index;
+ int bit;
+
+ if (count != 1)
+ panic("vm_bounce_page_free -- no support for > 1 page yet!!!\n");
+
+ index = (pa - bouncepa) / NBPG;
+
+ if ((index < 0) || (index >= bouncepages))
+ panic("vm_bounce_page_free -- bad index\n");
+
+ allocindex = index / BITS_IN_UNSIGNED;
+ bit = index % BITS_IN_UNSIGNED;
+
+ bounceallocarray[allocindex] &= ~(1 << bit);
+
+ bouncefree += count;
+ if (bpwait) {
+ bpwait = 0;
+ wakeup((caddr_t) &bounceallocarray);
+ }
+}
+
+/*
+ * allocate count bounce buffer kva pages
+ */
+vm_offset_t
+vm_bounce_kva(count, waitok)
+ int count;
+ int waitok;
+{
+ int tofree;
+ int i;
+ int startfree;
+ vm_offset_t kva = 0;
+ int s = splbio();
+ int size = count;
+ startfree = 0;
+more:
+ if (!bmfreeing && (tofree = kvasfreecnt)) {
+ bmfreeing = 1;
+ for (i = startfree; i < kvasfreecnt; i++) {
+ /*
+ * if we have a kva of the right size, no sense
+ * in freeing/reallocating...
+ * might affect fragmentation short term, but
+ * as long as the amount of io_map is
+ * significantly more than the maximum transfer
+ * size, I don't think that it is a problem.
+ */
+ pmap_remove(kernel_pmap,
+ kvaf[i].addr, kvaf[i].addr + kvaf[i].size);
+ if( size && !kva && kvaf[i].size == size) {
+ kva = kvaf[i].addr;
+ } else {
+ kmem_free_wakeup(io_map, kvaf[i].addr,
+ kvaf[i].size);
+ }
+ }
+ if (kvasfreecnt != tofree) {
+ startfree = i;
+ bmfreeing = 0;
+ goto more;
+ }
+ kvasfreecnt = 0;
+ bmfreeing = 0;
+ }
+
+ if( size == 0) {
+ splx(s);
+ return NULL;
+ }
+
+ if (!kva && !(kva = kmem_alloc_pageable(io_map, size))) {
+ if( !waitok) {
+ splx(s);
+ return NULL;
+ }
+ bmwait = 1;
+ tsleep((caddr_t) io_map, PRIBIO, "bmwait", 0);
+ goto more;
+ }
+ splx(s);
+
+ return kva;
+}
+
+/*
+ * same as vm_bounce_kva -- but really allocate
+ */
+vm_offset_t
+vm_bounce_kva_alloc(count)
+int count;
+{
+ int i;
+ vm_offset_t kva;
+ vm_offset_t pa;
+ if( bouncepages == 0) {
+ kva = (vm_offset_t) malloc(count*NBPG, M_TEMP, M_WAITOK);
+ return kva;
+ }
+ kva = vm_bounce_kva(count, 1);
+ for(i=0;i<count;i++) {
+ pa = vm_bounce_page_find(1);
+ pmap_kenter(kva + i * NBPG, pa);
+ }
+ pmap_update();
+ return kva;
+}
+
+/*
+ * same as vm_bounce_kva_free -- but really free
+ */
+void
+vm_bounce_kva_alloc_free(kva, count)
+ vm_offset_t kva;
+ int count;
+{
+ int i;
+ vm_offset_t pa;
+ if( bouncepages == 0) {
+ free((caddr_t) kva, M_TEMP);
+ return;
+ }
+ for(i = 0; i < count; i++) {
+ pa = pmap_kextract(kva + i * NBPG);
+ vm_bounce_page_free(pa, 1);
+ }
+ vm_bounce_kva_free(kva, count);
+}
+
+/*
+ * do the things necessary to the struct buf to implement
+ * bounce buffers... inserted before the disk sort
+ */
+void
+vm_bounce_alloc(bp)
+ struct buf *bp;
+{
+ int countvmpg;
+ vm_offset_t vastart, vaend;
+ vm_offset_t vapstart, vapend;
+ vm_offset_t va, kva;
+ vm_offset_t pa;
+ int dobounceflag = 0;
+ int bounceindex;
+ int i;
+ int s;
+
+ if (bouncepages == 0)
+ return;
+
+ if (bp->b_bufsize < bp->b_bcount) {
+ printf("vm_bounce_alloc: b_bufsize(%d) < b_bcount(%d) !!!!\n",
+ bp->b_bufsize, bp->b_bcount);
+ bp->b_bufsize = bp->b_bcount;
+ }
+
+ vastart = (vm_offset_t) bp->b_data;
+ vaend = (vm_offset_t) bp->b_data + bp->b_bufsize;
+
+ vapstart = i386_trunc_page(vastart);
+ vapend = i386_round_page(vaend);
+ countvmpg = (vapend - vapstart) / NBPG;
+
+/*
+ * if any page is above 16MB, then go into bounce-buffer mode
+ */
+ va = vapstart;
+ for (i = 0; i < countvmpg; i++) {
+ pa = pmap_kextract(va);
+ if (pa >= SIXTEENMEG)
+ ++dobounceflag;
+ va += NBPG;
+ }
+ if (dobounceflag == 0)
+ return;
+
+ if (bouncepages < dobounceflag)
+ panic("Not enough bounce buffers!!!");
+
+/*
+ * allocate a replacement kva for b_addr
+ */
+ kva = vm_bounce_kva(countvmpg*NBPG, 1);
+ va = vapstart;
+ for (i = 0; i < countvmpg; i++) {
+ pa = pmap_kextract(va);
+ if (pa >= SIXTEENMEG) {
+ /*
+ * allocate a replacement page
+ */
+ vm_offset_t bpa = vm_bounce_page_find(1);
+ pmap_kenter(kva + (NBPG * i), bpa);
+ /*
+ * if we are writing, the copy the data into the page
+ */
+ if ((bp->b_flags & B_READ) == 0) {
+ pmap_update();
+ bcopy((caddr_t) va, (caddr_t) kva + (NBPG * i), NBPG);
+ }
+ } else {
+ /*
+ * use original page
+ */
+ pmap_kenter(kva + (NBPG * i), pa);
+ }
+ va += NBPG;
+ }
+ pmap_update();
+
+/*
+ * flag the buffer as being bounced
+ */
+ bp->b_flags |= B_BOUNCE;
+/*
+ * save the original buffer kva
+ */
+ bp->b_savekva = bp->b_data;
+/*
+ * put our new kva into the buffer (offset by original offset)
+ */
+ bp->b_data = (caddr_t) (((vm_offset_t) kva) |
+ ((vm_offset_t) bp->b_savekva & (NBPG - 1)));
+ return;
+}
+
+/*
+ * hook into biodone to free bounce buffer
+ */
+void
+vm_bounce_free(bp)
+ struct buf *bp;
+{
+ int i;
+ vm_offset_t origkva, bouncekva;
+ vm_offset_t vastart, vaend;
+ vm_offset_t vapstart, vapend;
+ int countbounce = 0;
+ vm_offset_t firstbouncepa = 0;
+ int firstbounceindex;
+ int countvmpg;
+ vm_offset_t bcount;
+ int s;
+
+/*
+ * if this isn't a bounced buffer, then just return
+ */
+ if ((bp->b_flags & B_BOUNCE) == 0)
+ return;
+
+ origkva = (vm_offset_t) bp->b_savekva;
+ bouncekva = (vm_offset_t) bp->b_data;
+
+ vastart = bouncekva;
+ vaend = bouncekva + bp->b_bufsize;
+ bcount = bp->b_bufsize;
+
+ vapstart = i386_trunc_page(vastart);
+ vapend = i386_round_page(vaend);
+
+ countvmpg = (vapend - vapstart) / NBPG;
+
+/*
+ * check every page in the kva space for b_addr
+ */
+ for (i = 0; i < countvmpg; i++) {
+ vm_offset_t mybouncepa;
+ vm_offset_t copycount;
+
+ copycount = i386_round_page(bouncekva + 1) - bouncekva;
+ mybouncepa = pmap_kextract(i386_trunc_page(bouncekva));
+
+/*
+ * if this is a bounced pa, then process as one
+ */
+ if ((mybouncepa >= bouncepa) && (mybouncepa < bouncepaend)) {
+ if (copycount > bcount)
+ copycount = bcount;
+/*
+ * if this is a read, then copy from bounce buffer into original buffer
+ */
+ if (bp->b_flags & B_READ)
+ bcopy((caddr_t) bouncekva, (caddr_t) origkva, copycount);
+/*
+ * free the bounce allocation
+ */
+ vm_bounce_page_free(i386_trunc_page(mybouncepa), 1);
+ }
+
+ origkva += copycount;
+ bouncekva += copycount;
+ bcount -= copycount;
+ }
+
+/*
+ * add the old kva into the "to free" list
+ */
+ bouncekva = i386_trunc_page((vm_offset_t) bp->b_data);
+ vm_bounce_kva_free( bouncekva, countvmpg*NBPG, 0);
+ bp->b_data = bp->b_savekva;
+ bp->b_savekva = 0;
+ bp->b_flags &= ~B_BOUNCE;
+
+ return;
+}
+
+/*
+ * init the bounce buffer system
+ */
+void
+vm_bounce_init()
+{
+ vm_offset_t minaddr, maxaddr;
+
+ kvasfreecnt = 0;
+
+ if (bouncepages == 0)
+ return;
+
+ bounceallocarraysize = (bouncepages + BITS_IN_UNSIGNED - 1) / BITS_IN_UNSIGNED;
+ bounceallocarray = malloc(bounceallocarraysize * sizeof(unsigned), M_TEMP, M_NOWAIT);
+
+ if (!bounceallocarray)
+ panic("Cannot allocate bounce resource array\n");
+
+ bzero(bounceallocarray, bounceallocarraysize * sizeof(long));
+
+
+ bouncepa = pmap_kextract((vm_offset_t) bouncememory);
+ bouncepaend = bouncepa + bouncepages * NBPG;
+ bouncefree = bouncepages;
+}
+
+
+#ifdef BROKEN_IN_44
+static void
+cldiskvamerge( kvanew, orig1, orig1cnt, orig2, orig2cnt)
+ vm_offset_t kvanew;
+ vm_offset_t orig1, orig1cnt;
+ vm_offset_t orig2, orig2cnt;
+{
+ int i;
+ vm_offset_t pa;
+/*
+ * enter the transfer physical addresses into the new kva
+ */
+ for(i=0;i<orig1cnt;i++) {
+ vm_offset_t pa;
+ pa = pmap_kextract((caddr_t) orig1 + i * PAGE_SIZE);
+ pmap_kenter(kvanew + i * PAGE_SIZE, pa);
+ }
+
+ for(i=0;i<orig2cnt;i++) {
+ vm_offset_t pa;
+ pa = pmap_kextract((caddr_t) orig2 + i * PAGE_SIZE);
+ pmap_kenter(kvanew + (i + orig1cnt) * PAGE_SIZE, pa);
+ }
+ pmap_update();
+}
+
+void
+cldisksort(struct buf *dp, struct buf *bp, vm_offset_t maxio)
+{
+ register struct buf *ap, *newbp;
+ int i, trycount=0;
+ vm_offset_t orig1pages, orig2pages;
+ vm_offset_t orig1begin, orig2begin;
+ vm_offset_t kvanew, kvaorig;
+
+ if( bp->b_bcount < MAXCLSTATS*PAGE_SIZE)
+ ++rqstats[bp->b_bcount/PAGE_SIZE];
+ /*
+ * If nothing on the activity queue, then
+ * we become the only thing.
+ */
+ ap = dp->b_actf;
+ if(ap == NULL) {
+ dp->b_actf = bp;
+ dp->b_actl = bp;
+ bp->av_forw = NULL;
+ return;
+ }
+
+ /*
+ * If we lie after the first (currently active)
+ * request, then we must locate the second request list
+ * and add ourselves to it.
+ */
+
+ if (bp->b_pblkno < ap->b_pblkno) {
+ while (ap->av_forw) {
+ /*
+ * Check for an ``inversion'' in the
+ * normally ascending block numbers,
+ * indicating the start of the second request list.
+ */
+ if (ap->av_forw->b_pblkno < ap->b_pblkno) {
+ /*
+ * Search the second request list
+ * for the first request at a larger
+ * block number. We go before that;
+ * if there is no such request, we go at end.
+ */
+ do {
+ if (bp->b_pblkno < ap->av_forw->b_pblkno)
+ goto insert;
+ ap = ap->av_forw;
+ } while (ap->av_forw);
+ goto insert; /* after last */
+ }
+ ap = ap->av_forw;
+ }
+ /*
+ * No inversions... we will go after the last, and
+ * be the first request in the second request list.
+ */
+ goto insert;
+ }
+ /*
+ * Request is at/after the current request...
+ * sort in the first request list.
+ */
+ while (ap->av_forw) {
+ /*
+ * We want to go after the current request
+ * if there is an inversion after it (i.e. it is
+ * the end of the first request list), or if
+ * the next request is a larger block than our request.
+ */
+ if (ap->av_forw->b_pblkno < ap->b_pblkno ||
+ bp->b_pblkno < ap->av_forw->b_pblkno )
+ goto insert;
+ ap = ap->av_forw;
+ }
+
+insert:
+
+ /*
+ * read clustering with new read-ahead disk drives hurts mostly, so
+ * we don't bother...
+ */
+ if( bp->b_flags & B_READ)
+ goto nocluster;
+ /*
+ * we currently only cluster I/O transfers that are at page-aligned
+ * kvas and transfers that are multiples of page lengths.
+ */
+ if ((bp->b_flags & B_BAD) == 0 &&
+ ((bp->b_bcount & PAGE_MASK) == 0) &&
+ (((vm_offset_t) bp->b_un.b_addr & PAGE_MASK) == 0)) {
+ if( maxio > MAXCLSTATS*PAGE_SIZE)
+ maxio = MAXCLSTATS*PAGE_SIZE;
+ /*
+ * merge with previous?
+ * conditions:
+ * 1) We reside physically immediately after the previous block.
+ * 2) The previous block is not first on the device queue because
+ * such a block might be active.
+ * 3) The mode of the two I/Os is identical.
+ * 4) The previous kva is page aligned and the previous transfer
+ * is a multiple of a page in length.
+ * 5) And the total I/O size would be below the maximum.
+ */
+ if( (ap->b_pblkno + (ap->b_bcount / DEV_BSIZE) == bp->b_pblkno) &&
+ (dp->b_actf != ap) &&
+ ((ap->b_flags & ~B_CLUSTER) == bp->b_flags) &&
+ ((ap->b_flags & B_BAD) == 0) &&
+ ((ap->b_bcount & PAGE_MASK) == 0) &&
+ (((vm_offset_t) ap->b_un.b_addr & PAGE_MASK) == 0) &&
+ (ap->b_bcount + bp->b_bcount < maxio)) {
+
+ orig1begin = (vm_offset_t) ap->b_un.b_addr;
+ orig1pages = ap->b_bcount / PAGE_SIZE;
+
+ orig2begin = (vm_offset_t) bp->b_un.b_addr;
+ orig2pages = bp->b_bcount / PAGE_SIZE;
+ /*
+ * see if we can allocate a kva, if we cannot, the don't
+ * cluster.
+ */
+ kvanew = vm_bounce_kva( PAGE_SIZE * (orig1pages + orig2pages), 0);
+ if( !kvanew) {
+ goto nocluster;
+ }
+
+
+ if( (ap->b_flags & B_CLUSTER) == 0) {
+
+ /*
+ * get a physical buf pointer
+ */
+ newbp = (struct buf *)trypbuf();
+ if( !newbp) {
+ vm_bounce_kva_free( kvanew, PAGE_SIZE * (orig1pages + orig2pages), 1);
+ goto nocluster;
+ }
+
+ cldiskvamerge( kvanew, orig1begin, orig1pages, orig2begin, orig2pages);
+
+ /*
+ * build the new bp to be handed off to the device
+ */
+
+ --clstats[ap->b_bcount/PAGE_SIZE];
+ *newbp = *ap;
+ newbp->b_flags |= B_CLUSTER;
+ newbp->b_un.b_addr = (caddr_t) kvanew;
+ newbp->b_bcount += bp->b_bcount;
+ newbp->b_bufsize = newbp->b_bcount;
+ newbp->b_clusterf = ap;
+ newbp->b_clusterl = bp;
+ ++clstats[newbp->b_bcount/PAGE_SIZE];
+
+ /*
+ * enter the new bp onto the device queue
+ */
+ if( ap->av_forw)
+ ap->av_forw->av_back = newbp;
+ else
+ dp->b_actl = newbp;
+
+ if( dp->b_actf != ap )
+ ap->av_back->av_forw = newbp;
+ else
+ dp->b_actf = newbp;
+
+ /*
+ * enter the previous bps onto the cluster queue
+ */
+ ap->av_forw = bp;
+ bp->av_back = ap;
+
+ ap->av_back = NULL;
+ bp->av_forw = NULL;
+
+ } else {
+ vm_offset_t addr;
+
+ cldiskvamerge( kvanew, orig1begin, orig1pages, orig2begin, orig2pages);
+ /*
+ * free the old kva
+ */
+ vm_bounce_kva_free( orig1begin, ap->b_bufsize, 0);
+ --clstats[ap->b_bcount/PAGE_SIZE];
+
+ ap->b_un.b_addr = (caddr_t) kvanew;
+
+ ap->b_clusterl->av_forw = bp;
+ bp->av_forw = NULL;
+ bp->av_back = ap->b_clusterl;
+ ap->b_clusterl = bp;
+
+ ap->b_bcount += bp->b_bcount;
+ ap->b_bufsize = ap->b_bcount;
+ ++clstats[ap->b_bcount/PAGE_SIZE];
+ }
+ return;
+ /*
+ * merge with next?
+ * conditions:
+ * 1) We reside physically before the next block.
+ * 3) The mode of the two I/Os is identical.
+ * 4) The next kva is page aligned and the next transfer
+ * is a multiple of a page in length.
+ * 5) And the total I/O size would be below the maximum.
+ */
+ } else if( ap->av_forw &&
+ (bp->b_pblkno + (bp->b_bcount / DEV_BSIZE) == ap->av_forw->b_pblkno) &&
+ (bp->b_flags == (ap->av_forw->b_flags & ~B_CLUSTER)) &&
+ ((ap->av_forw->b_flags & B_BAD) == 0) &&
+ ((ap->av_forw->b_bcount & PAGE_MASK) == 0) &&
+ (((vm_offset_t) ap->av_forw->b_un.b_addr & PAGE_MASK) == 0) &&
+ (ap->av_forw->b_bcount + bp->b_bcount < maxio)) {
+
+ orig1begin = (vm_offset_t) bp->b_un.b_addr;
+ orig1pages = bp->b_bcount / PAGE_SIZE;
+
+ orig2begin = (vm_offset_t) ap->av_forw->b_un.b_addr;
+ orig2pages = ap->av_forw->b_bcount / PAGE_SIZE;
+
+ /*
+ * see if we can allocate a kva, if we cannot, the don't
+ * cluster.
+ */
+ kvanew = vm_bounce_kva( PAGE_SIZE * (orig1pages + orig2pages), 0);
+ if( !kvanew) {
+ goto nocluster;
+ }
+
+ /*
+ * if next isn't a cluster we need to create one
+ */
+ if( (ap->av_forw->b_flags & B_CLUSTER) == 0) {
+
+ /*
+ * get a physical buf pointer
+ */
+ newbp = (struct buf *)trypbuf();
+ if( !newbp) {
+ vm_bounce_kva_free( kvanew, PAGE_SIZE * (orig1pages + orig2pages), 1);
+ goto nocluster;
+ }
+
+ cldiskvamerge( kvanew, orig1begin, orig1pages, orig2begin, orig2pages);
+ ap = ap->av_forw;
+ --clstats[ap->b_bcount/PAGE_SIZE];
+ *newbp = *ap;
+ newbp->b_flags |= B_CLUSTER;
+ newbp->b_un.b_addr = (caddr_t) kvanew;
+ newbp->b_blkno = bp->b_blkno;
+ newbp->b_pblkno = bp->b_pblkno;
+ newbp->b_bcount += bp->b_bcount;
+ newbp->b_bufsize = newbp->b_bcount;
+ newbp->b_clusterf = bp;
+ newbp->b_clusterl = ap;
+ ++clstats[newbp->b_bcount/PAGE_SIZE];
+
+ if( ap->av_forw)
+ ap->av_forw->av_back = newbp;
+ else
+ dp->b_actl = newbp;
+
+ if( dp->b_actf != ap )
+ ap->av_back->av_forw = newbp;
+ else
+ dp->b_actf = newbp;
+
+ bp->av_forw = ap;
+ ap->av_back = bp;
+
+ bp->av_back = NULL;
+ ap->av_forw = NULL;
+ } else {
+ vm_offset_t addr;
+
+ cldiskvamerge( kvanew, orig1begin, orig1pages, orig2begin, orig2pages);
+ ap = ap->av_forw;
+ vm_bounce_kva_free( orig2begin, ap->b_bufsize, 0);
+
+ ap->b_un.b_addr = (caddr_t) kvanew;
+ bp->av_forw = ap->b_clusterf;
+ ap->b_clusterf->av_back = bp;
+ ap->b_clusterf = bp;
+ bp->av_back = NULL;
+ --clstats[ap->b_bcount/PAGE_SIZE];
+
+ ap->b_blkno = bp->b_blkno;
+ ap->b_pblkno = bp->b_pblkno;
+ ap->b_bcount += bp->b_bcount;
+ ap->b_bufsize = ap->b_bcount;
+ ++clstats[ap->b_bcount/PAGE_SIZE];
+
+ }
+ return;
+ }
+ }
+ /*
+ * don't merge
+ */
+nocluster:
+ ++clstats[bp->b_bcount/PAGE_SIZE];
+ bp->av_forw = ap->av_forw;
+ if( bp->av_forw)
+ bp->av_forw->av_back = bp;
+ else
+ dp->b_actl = bp;
+
+ ap->av_forw = bp;
+ bp->av_back = ap;
+}
+#endif
+
+/*
+ * quick version of vm_fault
+ */
+
+void
+vm_fault_quick( v, prot)
+ vm_offset_t v;
+ int prot;
+{
+ if( (cpu_class == CPUCLASS_386) &&
+ (prot & VM_PROT_WRITE))
+ vm_fault(&curproc->p_vmspace->vm_map, v,
+ VM_PROT_READ|VM_PROT_WRITE, FALSE);
+ else if( prot & VM_PROT_WRITE)
+ *(volatile char *)v += 0;
+ else
+ *(volatile char *)v;
+}
+
+
+/*
+ * Finish a fork operation, with process p2 nearly set up.
+ * Copy and update the kernel stack and pcb, making the child
+ * ready to run, and marking it so that it can return differently
+ * than the parent. Returns 1 in the child process, 0 in the parent.
+ * We currently double-map the user area so that the stack is at the same
+ * address in each process; in the future we will probably relocate
+ * the frame pointers on the stack after copying.
+ */
+int
+cpu_fork(p1, p2)
+ register struct proc *p1, *p2;
+{
+ register struct user *up = p2->p_addr;
+ int foo, offset, addr, i;
+ extern char kstack[];
+ extern int mvesp();
+
+ /*
+ * Copy pcb and stack from proc p1 to p2.
+ * We do this as cheaply as possible, copying only the active
+ * part of the stack. The stack and pcb need to agree;
+ * this is tricky, as the final pcb is constructed by savectx,
+ * but its frame isn't yet on the stack when the stack is copied.
+ * swtch compensates for this when the child eventually runs.
+ * This should be done differently, with a single call
+ * that copies and updates the pcb+stack,
+ * replacing the bcopy and savectx.
+ */
+ p2->p_addr->u_pcb = p1->p_addr->u_pcb;
+ offset = mvesp() - (int)kstack;
+ bcopy((caddr_t)kstack + offset, (caddr_t)p2->p_addr + offset,
+ (unsigned) ctob(UPAGES) - offset);
+ p2->p_md.md_regs = p1->p_md.md_regs;
+
+ /*
+ * Wire top of address space of child to it's kstack.
+ * First, fault in a page of pte's to map it.
+ */
+#if 0
+ addr = trunc_page((u_int)vtopte(kstack));
+ vm_map_pageable(&p2->p_vmspace->vm_map, addr, addr+NBPG, FALSE);
+ for (i=0; i < UPAGES; i++)
+ pmap_enter(&p2->p_vmspace->vm_pmap, kstack+i*NBPG,
+ pmap_extract(kernel_pmap, ((int)p2->p_addr)+i*NBPG),
+ /*
+ * The user area has to be mapped writable because
+ * it contains the kernel stack (when CR0_WP is on
+ * on a 486 there is no user-read/kernel-write
+ * mode). It is protected from user mode access
+ * by the segment limits.
+ */
+ VM_PROT_READ|VM_PROT_WRITE, TRUE);
+#endif
+ pmap_activate(&p2->p_vmspace->vm_pmap, &up->u_pcb);
+
+ /*
+ *
+ * Arrange for a non-local goto when the new process
+ * is started, to resume here, returning nonzero from setjmp.
+ */
+ if (savectx(up, 1)) {
+ /*
+ * Return 1 in child.
+ */
+ return (1);
+ }
+ return (0);
+}
+
+#ifdef notyet
+/*
+ * cpu_exit is called as the last action during exit.
+ *
+ * We change to an inactive address space and a "safe" stack,
+ * passing thru an argument to the new stack. Now, safely isolated
+ * from the resources we're shedding, we release the address space
+ * and any remaining machine-dependent resources, including the
+ * memory for the user structure and kernel stack.
+ *
+ * Next, we assign a dummy context to be written over by swtch,
+ * calling it to send this process off to oblivion.
+ * [The nullpcb allows us to minimize cost in mi_switch() by not having
+ * a special case].
+ */
+struct proc *swtch_to_inactive();
+volatile void
+cpu_exit(p)
+ register struct proc *p;
+{
+ static struct pcb nullpcb; /* pcb to overwrite on last swtch */
+
+#if NNPX > 0
+ npxexit(p);
+#endif /* NNPX */
+
+ /* move to inactive space and stack, passing arg accross */
+ p = swtch_to_inactive(p);
+
+ /* drop per-process resources */
+ vmspace_free(p->p_vmspace);
+ kmem_free(kernel_map, (vm_offset_t)p->p_addr, ctob(UPAGES));
+
+ p->p_addr = (struct user *) &nullpcb;
+ mi_switch();
+ /* NOTREACHED */
+}
+#else
+void
+cpu_exit(p)
+ register struct proc *p;
+{
+
+#if NNPX > 0
+ npxexit(p);
+#endif /* NNPX */
+ curproc = p;
+ mi_switch();
+ /*
+ * This is to shutup the compiler, and if swtch() failed I suppose
+ * this would be a good thing. This keeps gcc happy because panic
+ * is a volatile void function as well.
+ */
+ panic("cpu_exit");
+}
+
+void
+cpu_wait(p) struct proc *p; {
+/* extern vm_map_t upages_map; */
+ extern char kstack[];
+
+ /* drop per-process resources */
+ pmap_remove(vm_map_pmap(kernel_map), (vm_offset_t) p->p_addr,
+ ((vm_offset_t) p->p_addr) + ctob(UPAGES));
+ kmem_free(kernel_map, (vm_offset_t)p->p_addr, ctob(UPAGES));
+ vmspace_free(p->p_vmspace);
+}
+#endif
+
+/*
+ * Dump the machine specific header information at the start of a core dump.
+ */
+int
+cpu_coredump(p, vp, cred)
+ struct proc *p;
+ struct vnode *vp;
+ struct ucred *cred;
+{
+
+ return (vn_rdwr(UIO_WRITE, vp, (caddr_t) p->p_addr, ctob(UPAGES),
+ (off_t)0, UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, cred, (int *)NULL,
+ p));
+}
+
+/*
+ * Set a red zone in the kernel stack after the u. area.
+ */
+void
+setredzone(pte, vaddr)
+ u_short *pte;
+ caddr_t vaddr;
+{
+/* eventually do this by setting up an expand-down stack segment
+ for ss0: selector, allowing stack access down to top of u.
+ this means though that protection violations need to be handled
+ thru a double fault exception that must do an integral task
+ switch to a known good context, within which a dump can be
+ taken. a sensible scheme might be to save the initial context
+ used by sched (that has physical memory mapped 1:1 at bottom)
+ and take the dump while still in mapped mode */
+}
+
+/*
+ * Move pages from one kernel virtual address to another.
+ * Both addresses are assumed to reside in the Sysmap,
+ * and size must be a multiple of CLSIZE.
+ */
+
+/*
+ * Move pages from one kernel virtual address to another.
+ * Both addresses are assumed to reside in the Sysmap,
+ * and size must be a multiple of CLSIZE.
+ */
+
+void
+pagemove(from, to, size)
+ register caddr_t from, to;
+ int size;
+{
+ register vm_offset_t pa;
+
+ if (size & CLOFSET)
+ panic("pagemove");
+ while (size > 0) {
+ pa = pmap_kextract((vm_offset_t)from);
+ if (pa == 0)
+ panic("pagemove 2");
+ if (pmap_kextract((vm_offset_t)to) != 0)
+ panic("pagemove 3");
+ pmap_remove(kernel_pmap,
+ (vm_offset_t)from, (vm_offset_t)from + PAGE_SIZE);
+ pmap_kenter( (vm_offset_t)to, pa);
+ from += PAGE_SIZE;
+ to += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ pmap_update();
+}
+
+/*
+ * Convert kernel VA to physical address
+ */
+u_long
+kvtop(void *addr)
+{
+ vm_offset_t va;
+
+ va = pmap_kextract((vm_offset_t)addr);
+ if (va == 0)
+ panic("kvtop: zero page frame");
+ return((int)va);
+}
+
+extern vm_map_t phys_map;
+
+/*
+ * Map an IO request into kernel virtual address space.
+ *
+ * All requests are (re)mapped into kernel VA space.
+ * Notice that we use b_bufsize for the size of the buffer
+ * to be mapped. b_bcount might be modified by the driver.
+ */
+void
+vmapbuf(bp)
+ register struct buf *bp;
+{
+ register int npf;
+ register caddr_t addr;
+ int off;
+ vm_offset_t kva;
+ vm_offset_t pa, lastv, v;
+
+ if ((bp->b_flags & B_PHYS) == 0)
+ panic("vmapbuf");
+
+ lastv = 0;
+ for (addr = (caddr_t)trunc_page(bp->b_data);
+ addr < bp->b_data + bp->b_bufsize;
+ addr += PAGE_SIZE) {
+
+/*
+ * make sure that the pde is valid and held
+ */
+ v = trunc_page(((vm_offset_t)vtopte(addr)));
+ if (v != lastv) {
+ vm_fault_quick(v, VM_PROT_READ);
+ pa = pmap_extract(&curproc->p_vmspace->vm_pmap, v);
+ vm_page_hold(PHYS_TO_VM_PAGE(pa));
+ lastv = v;
+ }
+
+/*
+ * do the vm_fault if needed, do the copy-on-write thing when
+ * reading stuff off device into memory.
+ */
+ vm_fault_quick(addr,
+ (bp->b_flags&B_READ)?(VM_PROT_READ|VM_PROT_WRITE):VM_PROT_READ);
+ pa = pmap_extract(&curproc->p_vmspace->vm_pmap, (vm_offset_t) addr);
+/*
+ * hold the data page
+ */
+ vm_page_hold(PHYS_TO_VM_PAGE(pa));
+ }
+
+ addr = bp->b_saveaddr = bp->b_un.b_addr;
+ off = (int)addr & PGOFSET;
+ npf = btoc(round_page(bp->b_bufsize + off));
+ kva = kmem_alloc_wait(phys_map, ctob(npf));
+ bp->b_un.b_addr = (caddr_t) (kva + off);
+ while (npf--) {
+ pa = pmap_extract(&curproc->p_vmspace->vm_pmap, (vm_offset_t)addr);
+ if (pa == 0)
+ panic("vmapbuf: null page frame");
+ pmap_kenter(kva, trunc_page(pa));
+ addr += PAGE_SIZE;
+ kva += PAGE_SIZE;
+ }
+ pmap_update();
+}
+
+/*
+ * Free the io map PTEs associated with this IO operation.
+ * We also invalidate the TLB entries and restore the original b_addr.
+ */
+void
+vunmapbuf(bp)
+ register struct buf *bp;
+{
+ register int npf;
+ register caddr_t addr = bp->b_un.b_addr;
+ vm_offset_t kva,va,v,lastv,pa;
+
+ if ((bp->b_flags & B_PHYS) == 0)
+ panic("vunmapbuf");
+ npf = btoc(round_page(bp->b_bufsize + ((int)addr & PGOFSET)));
+ kva = (vm_offset_t)((int)addr & ~PGOFSET);
+ kmem_free_wakeup(phys_map, kva, ctob(npf));
+ bp->b_un.b_addr = bp->b_saveaddr;
+ bp->b_saveaddr = NULL;
+
+
+/*
+ * unhold the pde, and data pages
+ */
+ lastv = 0;
+ for (addr = (caddr_t)trunc_page(bp->b_data);
+ addr < bp->b_data + bp->b_bufsize;
+ addr += NBPG) {
+
+ /*
+ * release the data page
+ */
+ pa = pmap_extract(&curproc->p_vmspace->vm_pmap, (vm_offset_t) addr);
+ vm_page_unhold(PHYS_TO_VM_PAGE(pa));
+
+ /*
+ * and unhold the page table
+ */
+ v = trunc_page(((vm_offset_t)vtopte(addr)));
+ if (v != lastv) {
+ pa = pmap_extract(&curproc->p_vmspace->vm_pmap, v);
+ vm_page_unhold(PHYS_TO_VM_PAGE(pa));
+ lastv = v;
+ }
+ }
+}
+
+/*
+ * Force reset the processor by invalidating the entire address space!
+ */
+void
+cpu_reset() {
+
+ /* force a shutdown by unmapping entire address space ! */
+ bzero((caddr_t) PTD, NBPG);
+
+ /* "good night, sweet prince .... <THUNK!>" */
+ tlbflush();
+ /* NOTREACHED */
+ while(1);
+}
+
+/*
+ * Grow the user stack to allow for 'sp'. This version grows the stack in
+ * chunks of SGROWSIZ.
+ */
+int
+grow(p, sp)
+ struct proc *p;
+ u_int sp;
+{
+ unsigned int nss;
+ caddr_t v;
+ struct vmspace *vm = p->p_vmspace;
+
+ if ((caddr_t)sp <= vm->vm_maxsaddr || (unsigned)sp >= (unsigned)USRSTACK)
+ return (1);
+
+ nss = roundup(USRSTACK - (unsigned)sp, PAGE_SIZE);
+
+ if (nss > p->p_rlimit[RLIMIT_STACK].rlim_cur)
+ return (0);
+
+ if (vm->vm_ssize && roundup(vm->vm_ssize << PAGE_SHIFT,
+ SGROWSIZ) < nss) {
+ int grow_amount;
+ /*
+ * If necessary, grow the VM that the stack occupies
+ * to allow for the rlimit. This allows us to not have
+ * to allocate all of the VM up-front in execve (which
+ * is expensive).
+ * Grow the VM by the amount requested rounded up to
+ * the nearest SGROWSIZ to provide for some hysteresis.
+ */
+ grow_amount = roundup((nss - (vm->vm_ssize << PAGE_SHIFT)), SGROWSIZ);
+ v = (char *)USRSTACK - roundup(vm->vm_ssize << PAGE_SHIFT,
+ SGROWSIZ) - grow_amount;
+ /*
+ * If there isn't enough room to extend by SGROWSIZ, then
+ * just extend to the maximum size
+ */
+ if (v < vm->vm_maxsaddr) {
+ v = vm->vm_maxsaddr;
+ grow_amount = MAXSSIZ - (vm->vm_ssize << PAGE_SHIFT);
+ }
+ if (vm_allocate(&vm->vm_map, (vm_offset_t *)&v,
+ grow_amount, FALSE) != KERN_SUCCESS) {
+ return (0);
+ }
+ vm->vm_ssize += grow_amount >> PAGE_SHIFT;
+ }
+
+ return (1);
+}
diff --git a/sys/i386/include/_limits.h b/sys/i386/include/_limits.h
new file mode 100644
index 0000000..e507313
--- /dev/null
+++ b/sys/i386/include/_limits.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)limits.h 8.3 (Berkeley) 1/4/94
+ */
+
+#ifndef _MACHINE_LIMITS_H_
+#define _MACHINE_LIMITS_H_ 1
+
+#define CHAR_BIT 8 /* number of bits in a char */
+#define MB_LEN_MAX 6 /* Allow 31 bit UTF2 */
+
+
+#define CLK_TCK 128 /* ticks per second */
+
+/*
+ * According to ANSI (section 2.2.4.2), the values below must be usable by
+ * #if preprocessing directives. Additionally, the expression must have the
+ * same type as would an expression that is an object of the corresponding
+ * type converted according to the integral promotions. The subtraction for
+ * INT_MIN and LONG_MIN is so the value is not unsigned; 2147483648 is an
+ * unsigned int for 32-bit two's complement ANSI compilers (section 3.1.3.2).
+ * These numbers work for pcc as well. The UINT_MAX and ULONG_MAX values
+ * are written as hex so that GCC will be quiet about large integer constants.
+ */
+#define SCHAR_MAX 127 /* min value for a signed char */
+#define SCHAR_MIN (-128) /* max value for a signed char */
+
+#define UCHAR_MAX 255 /* max value for an unsigned char */
+#define CHAR_MAX 127 /* max value for a char */
+#define CHAR_MIN (-128) /* min value for a char */
+
+#define USHRT_MAX 65535 /* max value for an unsigned short */
+#define SHRT_MAX 32767 /* max value for a short */
+#define SHRT_MIN (-32768) /* min value for a short */
+
+#define UINT_MAX 0xffffffff /* max value for an unsigned int */
+#define INT_MAX 2147483647 /* max value for an int */
+#define INT_MIN (-2147483647-1) /* min value for an int */
+
+#define ULONG_MAX 0xffffffff /* max value for an unsigned long */
+#define LONG_MAX 2147483647 /* max value for a long */
+#define LONG_MIN (-2147483647-1) /* min value for a long */
+
+#if !defined(_ANSI_SOURCE)
+#define SSIZE_MAX INT_MAX /* max value for a ssize_t */
+
+#if !defined(_POSIX_SOURCE)
+#define SIZE_T_MAX UINT_MAX /* max value for a size_t */
+
+/* GCC requires that quad constants be written as expressions. */
+#define UQUAD_MAX ((u_quad_t)0-1) /* max value for a uquad_t */
+ /* max value for a quad_t */
+#define QUAD_MAX ((quad_t)(UQUAD_MAX >> 1))
+#define QUAD_MIN (-QUAD_MAX-1) /* min value for a quad_t */
+
+#endif /* !_POSIX_SOURCE */
+#endif /* !_ANSI_SOURCE */
+
+#endif /* _MACHINE_LIMITS_H_ */
diff --git a/sys/i386/include/ansi.h b/sys/i386/include/ansi.h
new file mode 100644
index 0000000..0ef060a
--- /dev/null
+++ b/sys/i386/include/ansi.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ansi.h 8.2 (Berkeley) 1/4/94
+ */
+
+#ifndef _ANSI_H_
+#define _ANSI_H_
+
+/*
+ * Types which are fundamental to the implementation and may appear in
+ * more than one standard header are defined here. Standard headers
+ * then use:
+ * #ifdef _BSD_SIZE_T_
+ * typedef _BSD_SIZE_T_ size_t;
+ * #undef _BSD_SIZE_T_
+ * #endif
+ */
+#define _BSD_CLOCK_T_ unsigned long /* clock() */
+#define _BSD_PTRDIFF_T_ int /* ptr1 - ptr2 */
+#define _BSD_SIZE_T_ unsigned int /* sizeof() */
+#define _BSD_SSIZE_T_ int /* byte count or error */
+#define _BSD_TIME_T_ long /* time() */
+#define _BSD_VA_LIST_ char * /* va_list */
+
+/*
+ * Runes (wchar_t) is declared to be an ``int'' instead of the more natural
+ * ``unsigned long'' or ``long''. Two things are happening here. It is not
+ * unsigned so that EOF (-1) can be naturally assigned to it and used. Also,
+ * it looks like 10646 will be a 31 bit standard. This means that if your
+ * ints cannot hold 32 bits, you will be in trouble. The reason an int was
+ * chosen over a long is that the is*() and to*() routines take ints (says
+ * ANSI C), but they use _RUNE_T_ instead of int. By changing it here, you
+ * lose a bit of ANSI conformance, but your programs will still work.
+ *
+ * Note that _WCHAR_T_ and _RUNE_T_ must be of the same type. When wchar_t
+ * and rune_t are typedef'd, _WCHAR_T_ will be undef'd, but _RUNE_T remains
+ * defined for ctype.h.
+ */
+#define _BSD_WCHAR_T_ int /* wchar_t */
+#define _BSD_RUNE_T_ int /* rune_t */
+
+#endif /* _ANSI_H_ */
diff --git a/sys/i386/include/asmacros.h b/sys/i386/include/asmacros.h
new file mode 100644
index 0000000..4af0b97
--- /dev/null
+++ b/sys/i386/include/asmacros.h
@@ -0,0 +1,49 @@
+#define ALIGN_DATA .align 2 /* 4 byte alignment, zero filled */
+#define ALIGN_TEXT .align 2,0x90 /* 4-byte alignment, nop filled */
+#define SUPERALIGN_TEXT .align 4,0x90 /* 16-byte alignment (better for 486), nop filled */
+
+#define GEN_ENTRY(name) ALIGN_TEXT; .globl name; name:
+#define NON_GPROF_ENTRY(name) GEN_ENTRY(_/**/name)
+
+/* These three are place holders for future changes to the profiling code */
+#define MCOUNT_LABEL(name)
+#define MEXITCOUNT
+#define FAKE_MCOUNT(caller)
+
+#ifdef GPROF
+/*
+ * ALTENTRY() must be before a corresponding ENTRY() so that it can jump
+ * over the mcounting.
+ */
+#define ALTENTRY(name) GEN_ENTRY(_/**/name); MCOUNT; jmp 2f
+#define ENTRY(name) GEN_ENTRY(_/**/name); MCOUNT; 2:
+/*
+ * The call to mcount supports the usual (bad) conventions. We allocate
+ * some data and pass a pointer to it although the FreeBSD doesn't use
+ * the data. We set up a frame before calling mcount because that is
+ * the standard convention although it makes work for both mcount and
+ * callers.
+ */
+#define MCOUNT .data; ALIGN_DATA; 1:; .long 0; .text; \
+ pushl %ebp; movl %esp,%ebp; \
+ movl $1b,%eax; call mcount; popl %ebp
+#else
+/*
+ * ALTENTRY() has to align because it is before a corresponding ENTRY().
+ * ENTRY() has to align to because there may be no ALTENTRY() before it.
+ * If there is a previous ALTENTRY() then the alignment code is empty.
+ */
+#define ALTENTRY(name) GEN_ENTRY(_/**/name)
+#define ENTRY(name) GEN_ENTRY(_/**/name)
+#define MCOUNT
+
+#endif
+
+#ifdef DUMMY_NOPS /* this will break some older machines */
+#define FASTER_NOP
+#define NOP
+#else
+#define FASTER_NOP pushl %eax ; inb $0x84,%al ; popl %eax
+#define NOP pushl %eax ; inb $0x84,%al ; inb $0x84,%al ; popl %eax
+#endif
+
diff --git a/sys/i386/include/cons.h b/sys/i386/include/cons.h
new file mode 100644
index 0000000..f66277e
--- /dev/null
+++ b/sys/i386/include/cons.h
@@ -0,0 +1,6 @@
+/*
+ * Console support headers should be in <machine/cons.h> since MI software
+ * needs to access these functions. In the mean time, just include the
+ * header where it sits.
+ */
+#include <i386/i386/cons.h>
diff --git a/sys/i386/include/console.h b/sys/i386/include/console.h
new file mode 100644
index 0000000..e663dea
--- /dev/null
+++ b/sys/i386/include/console.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 1992, 1993, 1994 Søren Schmidt
+ *
+ * This program is free software; you may redistribute it and/or
+ * modify it, provided that it retain the above copyright notice
+ * and the following disclaimer.
+ *
+ * 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.
+ *
+ * Søren Schmidt Email: sos@login.dkuug.dk
+ * Tritonvej 36 UUCP: ...uunet!dkuug!login!sos
+ * DK9210 Aalborg SO Phone: +45 9814 8076
+ *
+ * from:@(#)console.h 1.1 940105
+ * $Id: console.h,v 1.7 1994/02/04 10:35:29 chmr Exp $
+ */
+
+#ifndef _CONSOLE_H_
+#define _CONSOLE_H_
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#define KDGKBMODE _IOR('K', 6, int)
+#define KDSKBMODE _IO('K', 7)
+#define KDMKTONE _IO('K', 8)
+#define KDGETMODE _IOR('K', 9, int)
+#define KDSETMODE _IO('K', 10)
+#define KDSBORDER _IO('K', 13)
+#define KDGKBSTATE _IOR('K', 19, int)
+#define KDSKBSTATE _IO('K', 20)
+#define KDENABIO _IO('K', 60)
+#define KDDISABIO _IO('K', 61)
+#define KIOCSOUND _IO('K', 63)
+#define KDGKBTYPE _IOR('K', 64, int)
+#define KDGETLED _IOR('K', 65, int)
+#define KDSETLED _IO('K', 66)
+#define KDSETRAD _IO('K', 67)
+
+#define GETFKEY _IOWR('k', 0, fkeyarg_t)
+#define SETFKEY _IOWR('k', 1, fkeyarg_t)
+#define GIO_SCRNMAP _IOR('k', 2, scrmap_t)
+#define PIO_SCRNMAP _IOW('k', 3, scrmap_t)
+#define GIO_KEYMAP _IOR('k', 6, keymap_t)
+#define PIO_KEYMAP _IOW('k', 7, keymap_t)
+
+#define CONS_BLANKTIME _IOW('c', 4, long)
+#define CONS_SSAVER _IOW('c', 5, ssaver_t)
+#define CONS_GSAVER _IOWR('c', 6, ssaver_t)
+#define PIO_FONT8x8 _IOW('c', 64, fnt8_t)
+#define GIO_FONT8x8 _IOR('c', 65, fnt8_t)
+#define PIO_FONT8x14 _IOW('c', 66, fnt14_t)
+#define GIO_FONT8x14 _IOR('c', 67, fnt14_t)
+#define PIO_FONT8x16 _IOW('c', 68, fnt16_t)
+#define GIO_FONT8x16 _IOR('c', 69, fnt16_t)
+#define CONS_GETINFO _IOWR('c', 73, vid_info_t)
+#define CONS_GETVERS _IOR('c', 74, long)
+#define CONS_80x25TEXT _IO('c', 102)
+#define CONS_80x50TEXT _IO('c', 103)
+
+#define VT_OPENQRY _IOR('v', 1, int)
+#define VT_SETMODE _IOW('v', 2, vtmode_t)
+#define VT_GETMODE _IOR('v', 3, vtmode_t)
+#define VT_RELDISP _IO('v', 4)
+#define VT_ACTIVATE _IO('v', 5)
+#define VT_WAITACTIVE _IO('v', 6)
+#define VT_GETACTIVE _IOR('v', 7, int)
+
+#define VT_FALSE 0
+#define VT_TRUE 1
+#define VT_ACKACQ 2
+
+#define VT_AUTO 0 /* switching is automatic */
+#define VT_PROCESS 1 /* switching controlled by prog */
+
+/* compatibility to old pccons & X386 */
+#define CONSOLE_X_MODE_ON _IO('t', 121)
+#define CONSOLE_X_MODE_OFF _IO('t', 122)
+#define CONSOLE_X_BELL _IOW('t',123,int[2])
+
+struct vt_mode {
+ char mode;
+ char waitv; /* not implemented yet SOS */
+ short relsig;
+ short acqsig;
+ short frsig; /* not implemented yet SOS */
+};
+
+
+#define KD_MONO 1 /* monochrome adapter */
+#define KD_HERCULES 2 /* hercules adapter */
+#define KD_CGA 3 /* color graphics adapter */
+#define KD_EGA 4 /* enhanced graphics adapter */
+#define KD_VGA 5 /* video graohics adapter */
+
+#define KD_TEXT 0 /* set text mode restore fonts */
+#define KD_TEXT0 0 /* ditto */
+#define KD_TEXT1 2 /* set text mode !restore fonts */
+#define KD_GRAPHICS 1 /* set graphics mode */
+
+#define K_RAW 0 /* keyboard returns scancodes */
+#define K_XLATE 1 /* keyboard returns ascii */
+
+#define KB_84 1 /* 'old' 84 key AT-keyboard */
+#define KB_101 2 /* MF-101 or MF-102 keyboard */
+#define KB_OTHER 3 /* keyboard not known */
+
+#define CLKED 1 /* Caps locked */
+#define NLKED 2 /* Num locked */
+#define SLKED 4 /* Scroll locked */
+#define ALKED 8 /* AltGr locked */
+#define LED_CAP 1 /* Caps lock LED */
+#define LED_NUM 2 /* Num lock LED */
+#define LED_SCR 4 /* Scroll lock LED */
+
+/* possible flag values */
+#define FLAG_LOCK_O 0
+#define FLAG_LOCK_C 1
+#define FLAG_LOCK_N 2
+
+#define NUM_KEYS 256 /* number of keys in table */
+#define NUM_STATES 8 /* states per key */
+#define ALTGR_OFFSET 128 /* offset for altlock keys */
+
+struct keymap {
+ u_short n_keys;
+ struct key_t {
+ u_char map[NUM_STATES];
+ u_char spcl;
+ u_char flgs;
+ } key[NUM_KEYS];
+};
+
+#define MAXFK 16
+
+struct fkeytab {
+ u_char str[MAXFK];
+ u_char len;
+};
+
+struct fkeyarg {
+ u_short keynum;
+ char keydef[MAXFK];
+ char flen;
+};
+
+struct colors {
+ char fore;
+ char back;
+};
+
+struct vid_info {
+ short size;
+ short m_num;
+ u_short mv_row, mv_col;
+ u_short mv_rsz, mv_csz;
+ struct colors mv_norm,
+ mv_rev,
+ mv_grfc;
+ u_char mv_ovscan;
+ u_char mk_keylock;
+};
+
+#define MAXSSAVER 16
+
+struct ssaver {
+ char name[MAXSSAVER];
+ int num;
+ long time;
+};
+
+typedef struct keymap keymap_t;
+typedef struct fkeytab fkeytab_t;
+typedef struct fkeyarg fkeyarg_t;
+typedef struct vid_info vid_info_t;
+typedef struct vt_mode vtmode_t;
+typedef struct {char scrmap[256];} scrmap_t;
+typedef struct {char fnt8x8[8*256];} fnt8_t;
+typedef struct {char fnt8x14[14*256];} fnt14_t;
+typedef struct {char fnt8x16[16*256];} fnt16_t;
+typedef struct ssaver ssaver_t;
+
+/* defines for "special" keys (spcl bit set in keymap) */
+#define NOP 0x00 /* nothing (dead key) */
+#define LSH 0x02 /* left shift key */
+#define RSH 0x03 /* right shift key */
+#define CLK 0x04 /* caps lock key */
+#define NLK 0x05 /* num lock key */
+#define SLK 0x06 /* scroll lock key */
+#define LALT 0x07 /* left alt key */
+#define LCTR 0x09 /* left control key */
+#define NEXT 0x0a /* switch to next screen */
+#define F_SCR 0x0b /* switch to first screen */
+#define L_SCR 0x1a /* switch to last screen */
+#define F_FN 0x1b /* first function key */
+#define L_FN 0x7a /* last function key */
+#define RCTR 0x7b /* right control key */
+#define RALT 0x7c /* right alt (altgr) key */
+#define ALK 0x7d /* alt lock key */
+#define ASH 0x7e /* alt shift key */
+#define META 0x7f /* meta key */
+#define RBT 0x80 /* boot machine */
+#define DBG 0x81 /* call debugger */
+
+#define F(x) ((x)+F_FN-1)
+#define S(x) ((x)+F_SCR-1)
+#define NOKEY 0x100 /* no key pressed marker */
+#define FKEY 0x200 /* funtion key marker */
+#define MKEY 0x400 /* meta key marker (prepend ESC)*/
+
+#define KB_DATA 0x60 /* kbd data port */
+#define KB_STAT 0x64 /* kbd status port */
+#define KB_BUF_FULL 0x01 /* kbd has char pending */
+#define KB_READY 0x02 /* kbd ready for command */
+#define KB_WRITE 0x60 /* kbd write command */
+#define KB_SETLEDS 0xed /* kbd set leds */
+#define KB_SETRAD 0xf3 /* kbd set repeat&delay command */
+#define KB_ACK 0xfa /* kbd acknowledge answer */
+#define KB_RESET_CPU 0xfe /* kbd reset main cpu command */
+#define KB_RESET 0xff /* kbd reset */
+
+#endif
diff --git a/sys/i386/include/cpu.h b/sys/i386/include/cpu.h
new file mode 100644
index 0000000..2216d71
--- /dev/null
+++ b/sys/i386/include/cpu.h
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)cpu.h 5.4 (Berkeley) 5/9/91
+ * $Id: cpu.h,v 1.4 1993/11/07 17:42:46 wollman Exp $
+ */
+
+#ifndef _MACHINE_CPU_H_
+#define _MACHINE_CPU_H_ 1
+
+/*
+ * Definitions unique to i386 cpu support.
+ */
+#include "machine/frame.h"
+#include "machine/segments.h"
+#include <machine/spl.h>
+
+/*
+ * definitions of cpu-dependent requirements
+ * referenced in generic code
+ */
+#undef COPY_SIGCODE /* don't copy sigcode above user stack in exec */
+
+#define cpu_exec(p) /* nothing */
+#define cpu_swapin(p) /* nothing */
+#define cpu_setstack(p, ap) (p)->p_md.md_regs = ap
+#define cpu_set_init_frame(p, fp) (p)->p_md.md_regs = fp
+
+#define CLKF_USERMODE(framep) (ISPL((framep)->cf_cs) == SEL_UPL)
+#define CLKF_INTR(framep) (0)
+#define CLKF_BASEPRI(framep) (((framep)->cf_ppl & ~SWI_AST_MASK) == 0)
+#define CLKF_PC(framep) ((framep)->cf_eip)
+
+#define resettodr() /* no todr to set */
+
+/*
+ * Preempt the current process if in interrupt from user mode,
+ * or after the current trap/syscall if in system mode.
+ */
+#define need_resched() { want_resched = 1; aston(); }
+
+/*
+ * Give a profiling tick to the current process from the softclock
+ * interrupt. On tahoe, request an ast to send us through trap(),
+ * marking the proc as needing a profiling tick.
+ */
+#define need_proftick(p) { (p)->p_flag |= P_OWEUPC; aston(); }
+
+/*
+ * Notify the current process (p) that it has a signal pending,
+ * process as soon as possible.
+ */
+#define signotify(p) aston()
+
+#define aston() setsoftast()
+#define astoff()
+
+/*
+ * pull in #defines for kinds of processors
+ */
+#include "machine/cputypes.h"
+
+struct cpu_nameclass {
+ char *cpu_name;
+ int cpu_class;
+};
+
+/*
+ * CTL_MACHDEP definitions.
+ */
+#define CPU_CONSDEV 1 /* dev_t: console terminal device */
+#define CPU_MAXID 2 /* number of valid machdep ids */
+
+#define CTL_MACHDEP_NAMES { \
+ { 0, 0 }, \
+ { "console_device", CTLTYPE_STRUCT }, \
+}
+
+#ifdef KERNEL
+extern int want_resched; /* resched was called */
+
+extern int cpu;
+extern int cpu_class;
+extern struct cpu_nameclass i386_cpus[];
+#endif
+#endif /* _MACHINE_CPU_H_ */
diff --git a/sys/i386/include/cpufunc.h b/sys/i386/include/cpufunc.h
new file mode 100644
index 0000000..df871cb
--- /dev/null
+++ b/sys/i386/include/cpufunc.h
@@ -0,0 +1,108 @@
+/*
+ * Functions to provide access to special i386 instructions.
+ * XXX - bezillions more are defined in locore.s but are not declared anywhere.
+ *
+ * $Id: cpufunc.h,v 1.9 1994/01/31 23:48:23 davidg Exp $
+ */
+
+#ifndef _MACHINE_CPUFUNC_H_
+#define _MACHINE_CPUFUNC_H_ 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include "machine/spl.h"
+
+#ifdef __GNUC__
+
+static inline int bdb(void)
+{
+ extern int bdb_exists;
+
+ if (!bdb_exists)
+ return (0);
+ __asm("int $3");
+ return (1);
+}
+
+static inline void
+disable_intr(void)
+{
+ __asm __volatile("cli");
+}
+
+static inline void
+enable_intr(void)
+{
+ __asm __volatile("sti");
+}
+
+/*
+ * This roundabout method of returning a u_char helps stop gcc-1.40 from
+ * generating unnecessary movzbl's.
+ */
+#define inb(port) ((u_char) u_int_inb(port))
+
+static inline u_int
+u_int_inb(u_int port)
+{
+ u_char data;
+ /*
+ * We use %%dx and not %1 here because i/o is done at %dx and not at
+ * %edx, while gcc-2.2.2 generates inferior code (movw instead of movl)
+ * if we tell it to load (u_short) port.
+ */
+ __asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port));
+ return data;
+}
+
+static inline void
+outb(u_int port, u_char data)
+{
+ register u_char al asm("ax");
+
+ al = data; /* help gcc-1.40's register allocator */
+ __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port));
+}
+
+static inline void
+tlbflush()
+{
+ __asm __volatile("movl %%cr3, %%eax; movl %%eax, %%cr3" : : : "ax");
+}
+
+static inline u_long
+rcr2()
+{
+ u_long data;
+ __asm __volatile("movl %%cr2,%%eax" : "=a" (data));
+ return data;
+}
+
+#else /* not __GNUC__ */
+extern void insque __P((void *, void *));
+extern void remque __P((void *));
+
+int bdb __P((void));
+void disable_intr __P((void));
+void enable_intr __P((void));
+u_char inb __P((u_int port));
+void outb __P((u_int port, u_int data)); /* XXX - incompat */
+
+#endif /* __GNUC__ */
+
+void load_cr0 __P((u_int cr0));
+u_int rcr0 __P((void));
+void load_cr3(u_long);
+u_long rcr3(void);
+
+void setidt __P((int, void (*)(), int, int));
+extern u_long kvtop(void *);
+extern void outw(int /*u_short*/, int /*u_short*/); /* XXX inline!*/
+extern void outsb(int /*u_short*/, void *, size_t);
+extern void outsw(int /*u_short*/, void *, size_t);
+extern void insw(int /*u_short*/, void *, size_t);
+extern void fillw(int /*u_short*/, void *, size_t);
+extern void filli(int, void *, size_t);
+
+#endif /* _MACHINE_CPUFUNC_H_ */
diff --git a/sys/i386/include/cputypes.h b/sys/i386/include/cputypes.h
new file mode 100644
index 0000000..c85fe19
--- /dev/null
+++ b/sys/i386/include/cputypes.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1993 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: cputypes.h,v 1.1 1993/10/08 13:40:54 rgrimes Exp $
+ */
+
+#ifndef _MACHINE_CPUTYPES_H_
+#define _MACHINE_CPUTYPES_H_ 1
+
+/*
+ * Classes of Processor
+ */
+
+#define CPUCLASS_286 0
+#define CPUCLASS_386 1
+#define CPUCLASS_486 2
+#define CPUCLASS_586 3
+
+/*
+ * Kinds of Processor
+ */
+
+#define CPU_286 0 /* Intel 80286 */
+#define CPU_386SX 1 /* Intel 80386SX */
+#define CPU_386 2 /* Intel 80386DX */
+#define CPU_486SX 3 /* Intel 80486SX */
+#define CPU_486 4 /* Intel 80486DX */
+#define CPU_586 5 /* Intel P.....m (I hate lawyers; it's TM) */
+
+#endif /* _MACHINE_CPUTYPES_H_ */
diff --git a/sys/i386/include/db_machdep.h b/sys/i386/include/db_machdep.h
new file mode 100644
index 0000000..a3f4064
--- /dev/null
+++ b/sys/i386/include/db_machdep.h
@@ -0,0 +1,120 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * $Id: db_machdep.h,v 1.2 1993/10/16 14:39:10 rgrimes Exp $
+ */
+
+#ifndef _I386_DB_MACHDEP_H_
+#define _I386_DB_MACHDEP_H_
+
+/*
+ * Machine-dependent defines for new kernel debugger.
+ */
+
+
+/* #include <mach/i386/vm_types.h> */
+/* #include <mach/i386/vm_param.h> */
+#include <vm/vm_prot.h>
+#include <vm/vm_param.h>
+#include <vm/vm_inherit.h>
+#include <vm/lock.h>
+/* #include <i386/thread.h> */ /* for thread_status */
+#include <machine/frame.h> /* for struct trapframe */
+/* #include <i386/eflags.h> */
+#include <machine/eflags.h> /* from Mach... */
+/* #include <i386/trap.h> */
+#include <machine/trap.h>
+
+#define i386_saved_state trapframe
+/* end of mangling */
+
+typedef vm_offset_t db_addr_t; /* address - unsigned */
+typedef int db_expr_t; /* expression - signed */
+
+typedef struct i386_saved_state db_regs_t;
+extern db_regs_t ddb_regs; /* register state */
+#define DDB_REGS (&ddb_regs)
+
+#define PC_REGS(regs) ((db_addr_t)(regs)->tf_eip)
+
+#define BKPT_INST 0xcc /* breakpoint instruction */
+#define BKPT_SIZE (1) /* size of breakpoint inst */
+#define BKPT_SET(inst) (BKPT_INST)
+
+#define FIXUP_PC_AFTER_BREAK ddb_regs.tf_eip -= 1;
+
+#define db_clear_single_step(regs) ((regs)->tf_eflags &= ~EFL_TF)
+#define db_set_single_step(regs) ((regs)->tf_eflags |= EFL_TF)
+
+/* #define IS_BREAKPOINT_TRAP(type, code) ((type) == T_INT3) */
+/* #define IS_WATCHPOINT_TRAP(type, code) ((type) == T_WATCHPOINT) */
+/* using the 386bsd values, rather than the Mach ones: */
+#define IS_BREAKPOINT_TRAP(type, code) ((type) == T_BPTFLT)
+#define IS_WATCHPOINT_TRAP(type, code) ((type) == T_KDBTRAP)
+
+#define I_CALL 0xe8
+#define I_CALLI 0xff
+#define I_RET 0xc3
+#define I_IRET 0xcf
+
+#define inst_trap_return(ins) (((ins)&0xff) == I_IRET)
+#define inst_return(ins) (((ins)&0xff) == I_RET)
+#define inst_call(ins) (((ins)&0xff) == I_CALL || \
+ (((ins)&0xff) == I_CALLI && \
+ ((ins)&0x3800) == 0x1000))
+#define inst_load(ins) 0
+#define inst_store(ins) 0
+
+/* access capability and access macros */
+
+#define DB_ACCESS_LEVEL 2 /* access any space */
+#define DB_CHECK_ACCESS(addr,size,task) \
+ db_check_access(addr,size,task)
+#define DB_PHYS_EQ(task1,addr1,task2,addr2) \
+ db_phys_eq(task1,addr1,task2,addr2)
+#define DB_VALID_KERN_ADDR(addr) \
+ ((addr) >= VM_MIN_KERNEL_ADDRESS && \
+ (addr) < VM_MAX_KERNEL_ADDRESS)
+#define DB_VALID_ADDRESS(addr,user) \
+ ((!(user) && DB_VALID_KERN_ADDR(addr)) || \
+ ((user) && (addr) < VM_MIN_KERNEL_ADDRESS))
+
+boolean_t db_check_access(/* vm_offset_t, int, task_t */);
+boolean_t db_phys_eq(/* task_t, vm_offset_t, task_t, vm_offset_t */);
+
+/* macros for printing OS server dependent task name */
+
+#define DB_TASK_NAME(task) db_task_name(task)
+#define DB_TASK_NAME_TITLE "COMMAND "
+#define DB_TASK_NAME_LEN 23
+#define DB_NULL_TASK_NAME "? "
+
+void db_task_name(/* task_t */);
+
+/* macro for checking if a thread has used floating-point */
+
+#define db_thread_fp_used(thread) ((thread)->pcb->ims.ifps != 0)
+
+#endif /* _I386_DB_MACHDEP_H_ */
diff --git a/sys/i386/include/dkio.h b/sys/i386/include/dkio.h
new file mode 100644
index 0000000..af781be
--- /dev/null
+++ b/sys/i386/include/dkio.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)dkio.h 5.2 (Berkeley) 1/18/91
+ * $Id: dkio.h,v 1.2 1993/10/16 14:39:12 rgrimes Exp $
+ */
+
+#ifndef _MACHINE_DKIO_H_
+#define _MACHINE_DKIO_H_ 1
+
+/*
+ * Structures and definitions for disk io control commands
+ *
+ * THIS WHOLE AREA NEEDS MORE THOUGHT. FOR NOW JUST IMPLEMENT
+ * ENOUGH TO READ AND WRITE HEADERS ON MASSBUS DISKS. EVENTUALLY
+ * SHOULD BE ABLE TO DETERMINE DRIVE TYPE AND DO OTHER GOOD STUFF.
+ */
+
+/* disk io control commands */
+#define DKIOCHDR _IO(d, 1) /* next I/O will read/write header */
+#endif /* _MACHINE_DKIO_H_ */
diff --git a/sys/i386/include/eflags.h b/sys/i386/include/eflags.h
new file mode 100644
index 0000000..663b317
--- /dev/null
+++ b/sys/i386/include/eflags.h
@@ -0,0 +1,54 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * $Id$
+ */
+
+#ifndef _I386_EFLAGS_H_
+#define _I386_EFLAGS_H_
+
+/*
+ * i386 flags register
+ */
+#define EFL_CF 0x00000001 /* carry */
+#define EFL_PF 0x00000004 /* parity of low 8 bits */
+#define EFL_AF 0x00000010 /* carry out of bit 3 */
+#define EFL_ZF 0x00000040 /* zero */
+#define EFL_SF 0x00000080 /* sign */
+#define EFL_TF 0x00000100 /* trace trap */
+#define EFL_IF 0x00000200 /* interrupt enable */
+#define EFL_DF 0x00000400 /* direction */
+#define EFL_OF 0x00000800 /* overflow */
+#define EFL_IOPL 0x00003000 /* IO privilege level: */
+#define EFL_IOPL_KERNEL 0x00000000 /* kernel */
+#define EFL_IOPL_USER 0x00003000 /* user */
+#define EFL_NT 0x00004000 /* nested task */
+#define EFL_RF 0x00010000 /* resume without tracing */
+#define EFL_VM 0x00020000 /* virtual 8086 mode */
+
+#define EFL_USER_SET (EFL_IF)
+#define EFL_USER_CLEAR (EFL_IOPL|EFL_NT|EFL_RF)
+
+#endif _I386_EFLAGS_H_
diff --git a/sys/i386/include/endian.h b/sys/i386/include/endian.h
new file mode 100644
index 0000000..dc4e98c
--- /dev/null
+++ b/sys/i386/include/endian.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 1987, 1991 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)endian.h 7.8 (Berkeley) 4/3/91
+ * $Id: endian.h,v 1.3 1993/11/07 17:42:52 wollman Exp $
+ */
+
+#ifndef _MACHINE_ENDIAN_H_
+#define _MACHINE_ENDIAN_H_ 1
+
+/*
+ * Define the order of 32-bit words in 64-bit words.
+ */
+#define _QUAD_HIGHWORD 1
+#define _QUAD_LOWWORD 0
+
+/*
+ * Definitions for byte order, according to byte significance from low
+ * address to high.
+ */
+#define LITTLE_ENDIAN 1234 /* LSB first: i386, vax */
+#define BIG_ENDIAN 4321 /* MSB first: 68000, ibm, net */
+#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long */
+
+#define BYTE_ORDER LITTLE_ENDIAN
+
+#ifndef KERNEL
+#include <sys/cdefs.h>
+#endif
+
+#define __word_swap_long(x) \
+({ register u_long X = (x); \
+ asm ("rorl $16, %1" \
+ : "=r" (X) \
+ : "0" (X)); \
+ X; })
+#if __GNUC__ >= 2
+#define __byte_swap_long(x) \
+({ register u_long X = (x); \
+ asm ("xchgb %h1, %b1\n\trorl $16, %1\n\txchgb %h1, %b1" \
+ : "=q" (X) \
+ : "0" (X)); \
+ X; })
+#define __byte_swap_word(x) \
+({ register u_short X = (x); \
+ asm ("xchgb %h1, %b1" \
+ : "=q" (X) \
+ : "0" (X)); \
+ X; })
+#else /* __GNUC__ >= 2 */
+#define __byte_swap_long(x) \
+({ register u_long X = (x); \
+ asm ("rorw $8, %w1\n\trorl $16, %1\n\trorw $8, %w1" \
+ : "=r" (X) \
+ : "0" (X)); \
+ X; })
+#define __byte_swap_word(x) \
+({ register u_short X = (x); \
+ asm ("rorw $8, %w1" \
+ : "=r" (X) \
+ : "0" (X)); \
+ X; })
+#endif /* __GNUC__ >= 2 */
+
+/*
+ * Macros for network/external number representation conversion.
+ */
+#if BYTE_ORDER == BIG_ENDIAN && !defined(lint)
+#define ntohl(x) (x)
+#define ntohs(x) (x)
+#define htonl(x) (x)
+#define htons(x) (x)
+
+#define NTOHL(x) (x)
+#define NTOHS(x) (x)
+#define HTONL(x) (x)
+#define HTONS(x) (x)
+
+#else
+
+#define ntohl __byte_swap_long
+#define ntohs __byte_swap_word
+#define htonl __byte_swap_long
+#define htons __byte_swap_word
+
+#define NTOHL(x) (x) = ntohl((u_long)x)
+#define NTOHS(x) (x) = ntohs((u_short)x)
+#define HTONL(x) (x) = htonl((u_long)x)
+#define HTONS(x) (x) = htons((u_short)x)
+#endif
+#endif /* _MACHINE_ENDIAN_H_ */
diff --git a/sys/i386/include/exec.h b/sys/i386/include/exec.h
new file mode 100644
index 0000000..f63ec49
--- /dev/null
+++ b/sys/i386/include/exec.h
@@ -0,0 +1,128 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)exec.h 8.1 (Berkeley) 6/11/93
+ */
+
+#ifndef _EXEC_H_
+#define _EXEC_H_
+
+#define __LDPGSZ 4096
+
+/* Valid magic number check. */
+#define N_BADMAG(ex) \
+ (N_GETMAGIC(ex) != OMAGIC && N_GETMAGIC(ex) != NMAGIC && \
+ N_GETMAGIC(ex) != ZMAGIC && N_GETMAGIC(ex) != QMAGIC && \
+ N_GETMAGIC_NET(ex) != OMAGIC && N_GETMAGIC_NET(ex) != NMAGIC && \
+ N_GETMAGIC_NET(ex) != ZMAGIC && N_GETMAGIC_NET(ex) != QMAGIC)
+
+#define N_ALIGN(ex,x) \
+ (N_GETMAGIC(ex) == ZMAGIC || N_GETMAGIC(ex) == QMAGIC || \
+ N_GETMAGIC_NET(ex) == ZMAGIC || N_GETMAGIC_NET(ex) == QMAGIC ? \
+ ((x) + __LDPGSZ - 1) & ~(__LDPGSZ - 1) : (x))
+
+/* Address of the bottom of the text segment. */
+#define N_TXTADDR(ex) \
+ ((N_GETMAGIC(ex) == OMAGIC || N_GETMAGIC(ex) == NMAGIC || \
+ N_GETMAGIC(ex) == ZMAGIC) ? 0 : __LDPGSZ)
+
+/* Address of the bottom of the data segment. */
+#define N_DATADDR(ex) \
+ N_ALIGN(ex, N_TXTADDR(ex) + (ex).a_text)
+
+#define N_GETMAGIC(ex) \
+ ( (ex).a_midmag & 0xffff )
+#define N_GETMID(ex) \
+ ( (N_GETMAGIC_NET(ex) == ZMAGIC) ? N_GETMID_NET(ex) : \
+ ((ex).a_midmag >> 16) & 0x03ff )
+#define N_GETFLAG(ex) \
+ ( (N_GETMAGIC_NET(ex) == ZMAGIC) ? N_GETFLAG_NET(ex) : \
+ ((ex).a_midmag >> 26) & 0x3f )
+#define N_SETMAGIC(ex,mag,mid,flag) \
+ ( (ex).a_midmag = (((flag) & 0x3f) <<26) | (((mid) & 0x03ff) << 16) | \
+ ((mag) & 0xffff) )
+
+#define N_GETMAGIC_NET(ex) \
+ (ntohl((ex).a_midmag) & 0xffff)
+#define N_GETMID_NET(ex) \
+ ((ntohl((ex).a_midmag) >> 16) & 0x03ff)
+#define N_GETFLAG_NET(ex) \
+ ((ntohl((ex).a_midmag) >> 26) & 0x3f)
+#define N_SETMAGIC_NET(ex,mag,mid,flag) \
+ ( (ex).a_midmag = htonl( (((flag)&0x3f)<<26) | (((mid)&0x03ff)<<16) | \
+ (((mag)&0xffff)) ) )
+
+/* Text segment offset. */
+#define N_TXTOFF(ex) \
+ (N_GETMAGIC(ex) == ZMAGIC ? __LDPGSZ : (N_GETMAGIC(ex) == QMAGIC || \
+ N_GETMAGIC_NET(ex) == ZMAGIC) ? 0 : sizeof(struct exec))
+
+/* Data segment offset. */
+#define N_DATOFF(ex) \
+ N_ALIGN(ex, N_TXTOFF(ex) + (ex).a_text)
+
+/* Relocation table offset. */
+#define N_RELOFF(ex) \
+ N_ALIGN(ex, N_DATOFF(ex) + (ex).a_data)
+
+/* Symbol table offset. */
+#define N_SYMOFF(ex) \
+ (N_RELOFF(ex) + (ex).a_trsize + (ex).a_drsize)
+
+/* String table offset. */
+#define N_STROFF(ex) (N_SYMOFF(ex) + (ex).a_syms)
+
+/*
+ * Header prepended to each a.out file.
+ * only manipulate the a_midmag field via the
+ * N_SETMAGIC/N_GET{MAGIC,MID,FLAG} macros in a.out.h
+ */
+
+struct exec {
+unsigned long a_midmag; /* htonl(flags<<26 | mid<<16 | magic) */
+unsigned long a_text; /* text segment size */
+unsigned long a_data; /* initialized data size */
+unsigned long a_bss; /* uninitialized data size */
+unsigned long a_syms; /* symbol table size */
+unsigned long a_entry; /* entry point */
+unsigned long a_trsize; /* text relocation size */
+unsigned long a_drsize; /* data relocation size */
+};
+#define a_magic a_midmag /* XXX Hack to work with current kern_execve.c */
+
+/* a_magic */
+#define OMAGIC 0407 /* old impure format */
+#define NMAGIC 0410 /* read-only text */
+#define ZMAGIC 0413 /* demand load format */
+#define QMAGIC 0314 /* "compact" demand load format */
+
+#endif /* !_EXEC_H_ */
diff --git a/sys/i386/include/float.h b/sys/i386/include/float.h
new file mode 100644
index 0000000..fb5967e
--- /dev/null
+++ b/sys/i386/include/float.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)float.h 7.1 (Berkeley) 5/8/90
+ * $Id: float.h,v 1.4 1993/10/16 14:39:16 rgrimes Exp $
+ */
+
+#ifndef _MACHINE_FLOAT_H_
+#define _MACHINE_FLOAT_H_ 1
+
+#define FLT_RADIX 2 /* b */
+#define FLT_ROUNDS 1 /* FP addition rounds to nearest */
+
+#define FLT_MANT_DIG 24 /* p */
+#define FLT_EPSILON 1.19209290E-07F /* b**(1-p) */
+#define FLT_DIG 6 /* floor((p-1)*log10(b))+(b == 10) */
+#define FLT_MIN_EXP (-125) /* emin */
+#define FLT_MIN 1.17549435E-38F /* b**(emin-1) */
+#define FLT_MIN_10_EXP (-37) /* ceil(log10(b**(emin-1))) */
+#define FLT_MAX_EXP 128 /* emax */
+#define FLT_MAX 3.40282347E+38F /* (1-b**(-p))*b**emax */
+#define FLT_MAX_10_EXP 38 /* floor(log10((1-b**(-p))*b**emax)) */
+
+#define DBL_MANT_DIG 53
+#define DBL_EPSILON 2.2204460492503131E-16
+#define DBL_DIG 15
+#define DBL_MIN_EXP (-1021)
+#define DBL_MIN 2.2250738585072014E-308
+#define DBL_MIN_10_EXP (-307)
+#define DBL_MAX_EXP 1024
+#define DBL_MAX 1.7976931348623157E+308
+#define DBL_MAX_10_EXP 308
+
+#define LDBL_MANT_DIG DBL_MANT_DIG
+#define LDBL_EPSILON DBL_EPSILON
+#define LDBL_DIG DBL_DIG
+#define LDBL_MIN_EXP DBL_MIN_EXP
+#define LDBL_MIN DBL_MIN
+#define LDBL_MIN_10_EXP DBL_MIN_10_EXP
+#define LDBL_MAX_EXP DBL_MAX_EXP
+#define LDBL_MAX DBL_MAX
+#define LDBL_MAX_10_EXP DBL_MAX_10_EXP
+#endif /* _MACHINE_FLOAT_H_ */
diff --git a/sys/i386/include/floatingpoint.h b/sys/i386/include/floatingpoint.h
new file mode 100644
index 0000000..ed47cf6
--- /dev/null
+++ b/sys/i386/include/floatingpoint.h
@@ -0,0 +1,109 @@
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#) floatingpoint.h 1.0 (Berkeley) 9/23/93
+ * $Id: floatingpoint.h,v 1.3 1993/10/16 14:39:18 rgrimes Exp $
+ */
+
+/*
+ * IEEE floating point structure and function definitions
+ */
+
+#ifndef _FLOATINGPOINT_H_
+#define _FLOATINGPOINT_H_
+
+#include <sys/cdefs.h>
+#include <sys/ieeefp.h>
+
+#ifdef __GNUC__
+
+#ifdef __i386__
+
+#define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr) : "0" (*addr))
+#define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr) : "0" (*addr))
+#define fnstenv(addr) __asm("fnstenv %0" : "=m" (*addr) : "0" (*addr))
+#define fldenv(addr) __asm("fldenv %0" : : "m" (*addr))
+
+
+/*
+ * return the contents of a FP register
+ */
+static __inline__ int
+__fpgetreg(int _reg)
+{
+ unsigned short _mem;
+
+ switch(_reg) {
+ default:
+ fnstcw(&_mem);
+ break;
+ case FP_STKY_REG:
+ fnstsw(&_mem);
+ break;
+ }
+ return _mem;
+}
+
+/*
+ * set a FP mode; return previous mode
+ */
+static __inline__ int
+__fpsetreg(int _m, int _reg, int _fld, int _off)
+{
+ unsigned _env[7];
+ unsigned _p;
+
+ fnstenv(_env);
+ _p = (_env[_reg] & _fld) >> _off;
+ _env[_reg] = (_env[_reg] & ~_fld) | (_m << _off & _fld);
+ fldenv(_env);
+ return _p;
+}
+
+#endif /* __i386__ */
+
+#endif /* __GNUC__ */
+
+/*
+ * SysV/386 FP control interface
+ */
+#define fpgetround() ((__fpgetreg(FP_RND_REG) & FP_RND_FLD) >> FP_RND_OFF)
+#define fpsetround(m) __fpsetreg((m), FP_RND_REG, FP_RND_FLD, FP_RND_OFF)
+#define fpgetprec() ((__fpgetreg(FP_PRC_REG) & FP_PRC_FLD) >> FP_PRC_OFF)
+#define fpsetprec(m) __fpsetreg((m), FP_PRC_REG, FP_PRC_FLD, FP_PRC_OFF)
+#define fpgetmask() ((~__fpgetreg(FP_MSKS_REG) & FP_MSKS_FLD) >> FP_MSKS_OFF)
+#define fpsetmask(m) __fpsetreg(~(m), FP_MSKS_REG, FP_MSKS_FLD, FP_MSKS_OFF)
+#define fpgetsticky() ((__fpgetreg(FP_STKY_REG) & FP_STKY_FLD) >> FP_STKY_OFF)
+#define fpresetsticky(m) __fpsetreg(0, FP_STKY_REG, (m), FP_STKY_OFF)
+#define fpsetsticky(m) fpresetsticky(m)
+
+#endif /* !_FLOATINGPOINT_H_ */
diff --git a/sys/i386/include/frame.h b/sys/i386/include/frame.h
new file mode 100644
index 0000000..db2993e
--- /dev/null
+++ b/sys/i386/include/frame.h
@@ -0,0 +1,140 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)frame.h 5.2 (Berkeley) 1/18/91
+ * $Id: frame.h,v 1.7 1994/01/03 07:55:32 davidg Exp $
+ */
+
+#ifndef _MACHINE_FRAME_H_
+#define _MACHINE_FRAME_H_ 1
+
+#include <sys/signal.h>
+
+/*
+ * System stack frames.
+ */
+
+/*
+ * Exception/Trap Stack Frame
+ */
+
+struct trapframe {
+ int tf_es;
+ int tf_ds;
+ int tf_edi;
+ int tf_esi;
+ int tf_ebp;
+ int tf_isp;
+ int tf_ebx;
+ int tf_edx;
+ int tf_ecx;
+ int tf_eax;
+ int tf_trapno;
+ /* below portion defined in 386 hardware */
+ int tf_err;
+ int tf_eip;
+ int tf_cs;
+ int tf_eflags;
+ /* below only when transitting rings (e.g. user to kernel) */
+ int tf_esp;
+ int tf_ss;
+};
+
+extern int kdb_trap(int, int, struct trapframe *);
+
+/* Interrupt stack frame */
+
+struct intrframe {
+ int if_vec;
+ int if_ppl;
+ int if_es;
+ int if_ds;
+ int if_edi;
+ int if_esi;
+ int if_ebp;
+ int :32;
+ int if_ebx;
+ int if_edx;
+ int if_ecx;
+ int if_eax;
+ int :32; /* for compat with trap frame - trapno */
+ int :32; /* for compat with trap frame - err */
+ /* below portion defined in 386 hardware */
+ int if_eip;
+ int if_cs;
+ int if_eflags;
+ /* below only when transitting rings (e.g. user to kernel) */
+ int if_esp;
+ int if_ss;
+};
+
+/* frame of clock (same as interrupt frame) */
+
+struct clockframe {
+ int cf_vec;
+ int cf_ppl;
+ int cf_es;
+ int cf_ds;
+ int cf_edi;
+ int cf_esi;
+ int cf_ebp;
+ int :32;
+ int cf_ebx;
+ int cf_edx;
+ int cf_ecx;
+ int cf_eax;
+ int :32; /* for compat with trap frame - trapno */
+ int :32; /* for compat with trap frame - err */
+ /* below portion defined in 386 hardware */
+ int cf_eip;
+ int cf_cs;
+ int cf_eflags;
+ /* below only when transitting rings (e.g. user to kernel) */
+ int cf_esp;
+ int cf_ss;
+};
+
+/*
+ * Signal frame
+ */
+struct sigframe {
+ int sf_signum;
+ int sf_code;
+ struct sigcontext *sf_scp;
+ char *sf_addr;
+ sig_t sf_handler;
+ struct sigcontext sf_sc;
+};
+#endif /* _MACHINE_FRAME_H_ */
diff --git a/sys/i386/include/ioctl_fd.h b/sys/i386/include/ioctl_fd.h
new file mode 100644
index 0000000..2e3ac31
--- /dev/null
+++ b/sys/i386/include/ioctl_fd.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 1992-1993 by Joerg Wunsch, Dresden
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _IOCTL_FD_H
+#define _IOCTL_FD_H
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#define FD_FORMAT_VERSION 110 /* used to validate before formatting */
+#define FD_MAX_NSEC 36 /* highest known number of spt - allow for */
+ /* 2.88 MB drives */
+
+struct fd_formb {
+ int format_version; /* == FD_FORMAT_VERSION */
+ int cyl, head;
+ int transfer_rate; /* fdreg.h: FDC_???KBPS */
+
+ union {
+ struct fd_form_data {
+ /*
+ * DO NOT CHANGE THE LAYOUT OF THIS STRUCTS
+ * it is hardware-dependant since it exactly
+ * matches the byte sequence to write to FDC
+ * during its `format track' operation
+ */
+ u_char secshift; /* 0 -> 128, ...; usually 2 -> 512 */
+ u_char nsecs; /* must be <= FD_MAX_NSEC */
+ u_char gaplen; /* GAP 3 length; usually 84 */
+ u_char fillbyte; /* usually 0xf6 */
+ struct fd_idfield_data {
+ /*
+ * data to write into id fields;
+ * for obscure formats, they mustn't match
+ * the real values (but mostly do)
+ */
+ u_char cylno; /* 0 thru 79 (or 39) */
+ u_char headno; /* 0, or 1 */
+ u_char secno; /* starting at 1! */
+ u_char secsize; /* usually 2 */
+ } idfields[FD_MAX_NSEC]; /* 0 <= idx < nsecs used */
+ } structured;
+ u_char raw[1]; /* to have continuous indexed access */
+ } format_info;
+};
+
+/* make life easier */
+# define fd_formb_secshift format_info.structured.secshift
+# define fd_formb_nsecs format_info.structured.nsecs
+# define fd_formb_gaplen format_info.structured.gaplen
+# define fd_formb_fillbyte format_info.structured.fillbyte
+/* these data must be filled in for(i = 0; i < fd_formb_nsecs; i++) */
+# define fd_formb_cylno(i) format_info.structured.idfields[i].cylno
+# define fd_formb_headno(i) format_info.structured.idfields[i].headno
+# define fd_formb_secno(i) format_info.structured.idfields[i].secno
+# define fd_formb_secsize(i) format_info.structured.idfields[i].secsize
+
+struct fd_type {
+ int sectrac; /* sectors per track */
+ int secsize; /* size code for sectors */
+ int datalen; /* data len when secsize = 0 */
+ int gap; /* gap len between sectors */
+ int tracks; /* total num of tracks */
+ int size; /* size of disk in sectors */
+ int steptrac; /* steps per cylinder */
+ int trans; /* transfer speed code */
+ int heads; /* number of heads */
+ int f_gap; /* format gap len */
+ int f_inter; /* format interleave factor */
+};
+
+#define FD_FORM _IOW('F', 61, struct fd_formb) /* format a track */
+#define FD_GTYPE _IOR('F', 62, struct fd_type) /* get drive type */
+
+#endif /* !def _IOCTL_FD_H */
diff --git a/sys/i386/include/ioctl_pc.h b/sys/i386/include/ioctl_pc.h
new file mode 100644
index 0000000..bc6a255
--- /dev/null
+++ b/sys/i386/include/ioctl_pc.h
@@ -0,0 +1,797 @@
+/* Copyright 1992,1993 by Holger Veit
+ * May be freely used with Bill Jolitz's port of
+ * 386bsd and may be included in a 386bsd collection
+ * as long as binary and source are available and reproduce the above
+ * copyright.
+ *
+ * You may freely modify this code and contribute improvements based
+ * on this code as long as you don't claim to be the original author.
+ * Commercial use of this source requires permittance of the copyright
+ * holder. A general license for 386bsd will override this restriction.
+ *
+ * Use at your own risk. The copyright holder or any person who makes
+ * this code available for the public (administrators of public archives
+ * for instance) are not responsible for any harm to hardware or software
+ * that might happen due to wrong application or program faults.
+ *
+ * Addendum: The XFree86 developers and maintainers are hereby granted the
+ * right to distribute this file together with their source distributions
+ * and patchkits of XFree86 without further explicit permission of the
+ * above copyright holder.
+ * This and another file is a necessary include file for the unified
+ * pccons/codrv implementation of XFree86. This file is needed if
+ * someone wants to compile an Xserver on a system which does not have,
+ * for some reasons, the codrv console driver which comes with this file. The
+ * availability of this file avoids a large number of #ifdef's and
+ * allows to make the xserver code easier runtime-configurable.
+ * To make use of this file, it must be installed in /usr/include/sys.
+ * This file is not the complete console device driver, so it is possible
+ * that properties described in this file do not work without having the
+ * complete driver distribution. This is not a fault of the Xserver that
+ * was built with this file.
+ *
+ *
+ *
+ * From: @(#)$RCSfile: ioctl_pc.h,v
+ * Revision: 1.1.1.1 (Contributed to 386bsd)
+ * Date: 1993/06/12 14:58:11
+ *
+ * Important notice: #defined values are subject to be changed!!!
+ * Don't use the constant, use the name instead!
+ *
+ * codrv1-style uses ioctls 'K': 1-33,255
+ * 'V': 100-109
+ *
+ * -hv- Holger Veit, Holger.Veit@gmd.de
+ * -hm Hellmuth Michaelis, hm@hcshh.hcs.de
+ * -vak- Sergey Vakulenko, vak@kiae.su
+ *
+ * 25-07-92 -hv- First version
+ * 16-08-92 -hm adding vga ioctl for cursor shape
+ * 25-10-92 -hv- X11 + video related ioctls
+ * 01/12/92 -vak- 8x16 font loading, beep ioctl,
+ * LED reassignment ioctl.
+ * 22-04-93 -hv- unified most CODRV1/CODRV2 codes
+ * 24-04-93 -hv- revised parts of keymap structures
+ *
+ * $Id$
+ */
+
+#ifndef _IOCTL_PC_H_
+#define _IOCTL_PC_H_
+
+#ifdef NOTDEF
+#if __GNUC__ >= 2
+#pragma pack(1)
+#endif
+#endif
+
+#ifndef KERNEL
+#include <sys/ioctl.h>
+#ifndef _TYPES_H_
+#include <sys/types.h>
+#endif
+#else
+#include "ioctl.h"
+#endif
+
+
+/***************************************************************************
+ * Basic definitions
+ ***************************************************************************/
+
+/* Use this data type when manipulating characters, don't use 'char' or 'u_char'
+ * some day this will be changed to 'u_short' or 'u_long' size to allow
+ * characters > 255
+ */
+typedef u_char XCHAR;
+
+/***************************************************************************
+ * driver identification
+ ***************************************************************************/
+
+/*
+ * This defines the CONSOLE INFORMATION data structure, used to
+ * describe console capabilities, to distinguish between different
+ * versions. If this ioctl fail, you probably have an old style "pccons"
+ * driver (or an "improved" console driver, whose writer is not interested
+ * in providing compatibility for anything).
+ * In this case, a considerable number of features may not work as expected,
+ * or do not work at all.
+ */
+
+#define MAXINFOSIZE 16
+struct consinfo {
+ u_long info1;
+ u_long __reserved1__;
+ u_long __reserved2__;
+ u_long __reserved3__;
+ XCHAR drv_name[MAXINFOSIZE+1];
+ XCHAR emul_name[MAXINFOSIZE+1];
+ XCHAR __reserved1_name__[MAXINFOSIZE+1];
+ XCHAR __reserved2_name__[MAXINFOSIZE+1];
+};
+
+struct oldconsinfo {
+ u_long info1;
+ u_long __reserved__;
+};
+
+#define CONSGINFO _IOR('K',255,struct consinfo) /* Get console capabilities */
+#define OLDCONSGINFO _IOR('K',255,struct oldconsinfo) /* compatibility */
+#define CONS_ISPC 0x00000001 /* is derived from old PCCONS */
+#define CONS_ISCO 0x00000002 /* is derived from CO driver */
+#define CONS_reserved1 0x00000004 /* reserved for other console drivers */
+#define CONS_reserved2 0x00000008 /* reserved for other console drivers */
+#define CONS_HASKBD 0x00000010 /* has /dev/kbd */
+#define CONS_HASSCAN 0x00000020 /* uses Scan codes */
+#define CONS_HASKEYNUM 0x00000040 /* uses KEYNUMS */
+#define CONS_HASVTY 0x00000080 /* has /dev/vty* */
+#define CONS_HASPC3 0x00000100 /* unused, historical */
+#define CONS_HASVTHP 0x00000200 /* unused, historical */
+#define CONS_reserved3 0x00000400 /* reserved */
+#define CONS_reserved4 0x00000800 /* reserved */
+#define CONS_HASPX386 0x00001000 /* has X386 probing support +new CONSOLE_X_MODE */
+#define CONS_HASOX386 0x00002000 /* has old X386 support CONSOLE_X_MODE_ON/OFF */
+#define CONS_reserved5 0x00004000 /* reserved */
+#define CONS_reserved6 0x00008000 /* reserved */
+#define CONS_HASKCAP 0x00010000 /* has ioctl keycap support */
+#define CONS_HASFNT 0x00020000 /* has ioctl font support */
+#define CONS_reserved7 0x00040000 /* reserved */
+#define CONS_reserved8 0x00080000 /* reserved */
+#define CONS_USE7BIT 0x00100000 /* does not support 8bit characters */
+#define CONS_USEPC8 0x00200000 /* uses PC8 8-bit mapping */
+#define CONS_USELATIN1 0x00400000 /* uses ISO LATIN1 mapping */
+#define CONS_HAS10646 0x00800000 /* has /dev/unicode */
+#define CONS_PCCONS2 0x01000000 /* modified pccons */
+#define CONS_CODRV1 0x02000000 /* old codrv ioctls */
+#define CONS_CODRV2 0x04000000 /* codrv ioctls 0.1.2 */
+#define CONS_reserved9 0x08000000 /* reserved */
+#define CONS_reserved10 0x10000000 /* reserved */
+#define CONS_reserved11 0x20000000 /* reserved */
+#define CONS_reserved12 0x40000000 /* reserved */
+#define CONS_reserved13 0x80000000 /* reserved */
+
+
+/***************************************************************************
+ * IOCTLs for AT Keyboard
+ ***************************************************************************/
+
+/**** initializing the keyboard ****/
+
+/* reset keyboard, run selftests and set default values:
+ * default keymap, no overloaded keys, default typematic rate
+ * KBD_TPD500|KBD_TPM100, repetition on
+ */
+#define KBDCOLDRESET _IO('K', 1) /* reset keyboard and set default
+ * values:
+ * default keymap, no overloaded
+ * keys, default typematic rate
+ * KBD_TPD500|KBD_TPM100
+ */
+/* resets the mode in keyboard controller only */
+#define KBDWARMRESET _IO('K', 23)
+
+
+
+/**** key repetition (typematic) feature ****/
+
+/* get (G) / set (S) key repetition rate and delay
+ * see below for a definition of rate and delay and the necessary
+ * argument
+ */
+#define KBDGTPMAT _IOR('K', 2, int)
+#define KBDSTPMAT _IOW('K', 3, int)
+
+/* Typematic rates:
+ * Rate = 1 / Period, with
+ * Period = (8+ (Val&7)) * 2^((Val>>3)&3) * 0.00417 seconds,
+ * and Val the typematic value below
+ *
+ * The typematic delay is determined by
+ * Delay = (1+((Val>>5)&3)) * 250 msec +/- 20 %
+ *
+ * Source IBM/AT reference manual, 1987
+ *
+ * Note that you have to pass one TPD* and one TPM* value to the KBDSTPMAT
+ * ioctl: they are different flags of the same data word. Also note that
+ * 0x00 is a valid value: KBD_TPD250|KBD_TPM300 which is really fast, instead
+ * of turning off key repetition entirely. You can turn off key repetition
+ * with the ioctls KBDGREPSW/KBDSREPSW.
+*/
+
+#define KBD_TPD250 0x0000 /* 250 ms */
+#define KBD_TPD500 0x0020 /* 500 ms */
+#define KBD_TPD750 0x0040 /* 750 ms */
+#define KBD_TPD1000 0x0060 /* 1000 ms */
+
+#define KBD_TPM300 0x0000 /* 30.0 rate */
+#define KBD_TPM267 0x0001 /* 26.7 rate */
+#define KBD_TPM240 0x0002 /* 24.0 rate */
+#define KBD_TPM218 0x0003 /* 21.8 rate */
+#define KBD_TPM200 0x0004 /* 20.0 rate */
+#define KBD_TPM185 0x0005 /* 18.5 rate */
+#define KBD_TPM171 0x0006 /* 17.1 rate */
+#define KBD_TPM160 0x0007 /* 16.0 rate */
+#define KBD_TPM150 0x0008 /* 15.0 rate */
+#define KBD_TPM133 0x0009 /* 13.3 rate */
+#define KBD_TPM120 0x000a /* 12.0 rate */
+#define KBD_TPM109 0x000b /* 10.9 rate */
+#define KBD_TPM100 0x000c /* 10.0 rate */
+#define KBD_TPM92 0x000d /* 9.2 rate */
+#define KBD_TPM86 0x000e /* 8.6 rate */
+#define KBD_TPM80 0x000f /* 8.0 rate */
+#define KBD_TPM75 0x0010 /* 7.5 rate */
+#define KBD_TPM67 0x0011 /* 6.7 rate */
+#define KBD_TPM60 0x0012 /* 6.0 rate */
+#define KBD_TPM55 0x0013 /* 5.5 rate */
+#define KBD_TPM50 0x0014 /* 5.0 rate */
+#define KBD_TPM46 0x0015 /* 4.6 rate */
+#define KBD_TPM43 0x0016 /* 4.3 rate */
+#define KBD_TPM40 0x0017 /* 4.0 rate */
+#define KBD_TPM37 0x0018 /* 3.7 rate */
+#define KBD_TPM33 0x0019 /* 3.3 rate */
+#define KBD_TPM30 0x001a /* 3.0 rate */
+#define KBD_TPM27 0x001b /* 2.7 rate */
+#define KBD_TPM25 0x001c /* 2.5 rate */
+#define KBD_TPM23 0x001d /* 2.3 rate */
+#define KBD_TPM21 0x001e /* 2.1 rate */
+#define KBD_TPM20 0x001f /* 2.0 rate */
+
+
+/* get (G) / set (S) the key repetition switch */
+#define KBD_REPEATOFF 0
+#define KBD_REPEATON 1
+#define KBDGREPSW _IOR('K', 4, int)
+#define KBDSREPSW _IOW('K', 5, int)
+
+
+
+/**** handling keyboard LEDS and Lock keys ****/
+
+/* get (G) / set (S) the keyboard LEDs,
+ * does not influence the state of the lock keys.
+ * Note: if keyboard serves tty console mode (VTYs have keyboard focus),
+ * the lock keys will still modify the state when used
+ */
+#define KBDGLEDS _IOR('K', 6, int)
+#define KBDSLEDS _IOW('K', 7, int)
+
+/* get (G) / set (S) the SCROLL, NUM, CAPS ALTGRLOCK keys
+ * (note: ALTGRLOCK or SHIFTLOCK are not necessarily accessible
+ * on your keyboard)
+ */
+#define KBD_LOCKSCROLL 0x0001
+#define KBD_LOCKNUM 0x0002
+#define KBD_LOCKCAPS 0x0004
+#define KBD_LOCKALTGR 0x0008
+#define KBD_LOCKSHIFT 0x0010
+#define KBDGLOCK _IOR('K', 8, int)
+#define KBDSLOCK _IOW('K', 9, int)
+
+
+
+/**** making noise ****/
+
+/* get (G) / set (S) the beeper frequency and tone duration
+ * the nr param determines the VTY which parameters are changed
+ * VTY# = 0...n, n < max_vtys
+ * nr = -1: actual vty
+ * nr = -2: Set the system default beep frequency
+ *
+ * in some emulations, you can also set pitch and duration by an ESC code
+ */
+#define KBD_ACTVTY -1
+#define KBD_DEFLT -2
+struct kbd_bell {
+ int pitch;
+ int duration;
+ int nr;
+};
+
+#define KBDGETBEEP _IOWR('K',28, struct kbd_bell)
+#define KBDSETBEEP _IOW('K',29, struct kbd_bell)
+
+/* do a beep of specified frequency and duration
+ * the argument nr is unused
+ * a NULL arg performs a default system beep
+ */
+#define KBDBELL _IOW('K',30, struct kbd_bell)
+
+
+
+/**** I/O access ****/
+
+/* This call allows programs to access I/O ports.
+ * The ioctl is intended to perform several tasks for the XFree86 Xserver,
+ * but currently has other interesting applications. This is why it is
+ * priviledged and can only be executed by root (or with setuid-root).
+ * In future the ioctl might be restricted to allow access to video ports
+ * only.
+ */
+#define X_MODE_ON 1
+#define X_MODE_OFF 0
+#define CONSOLE_X_MODE _IOW('K',22,int)
+
+
+/**** keyboard overloading ****/
+
+/* Codrv allows loading of strings to keys in six layers.
+ * Any string may have a length of up to KBDMAXOVLKEYSIZE XCHARS.
+ * !!! Warning: This ioctl uses the type XCHAR. In future, this may
+ * !!! no longer be a char type, so str*** functions might not work any more
+ * !!! some day.
+ * The available layers are:
+ *
+ * - unshifted
+ * - with shift key
+ * - with ctrl key
+ * - with meta key (usually ALT-left)
+ * - with altgr key (usually ALT-right)
+ * - with shift+altgr key
+ *
+ * There are no combinations: shift-ctrl, ctrl-alt, shift-meta.
+ * The combination ctrl-altleft-somekey is reserved for system purposes.
+ * These keys are usually processed before the above keys. To gain control
+ * over these keys, you must run the keyboard in raw mode (/dev/kbd) and
+ * do ALL the processing yourself. The Xserver for instance does it this way.
+ * The following special keys are currently defined:
+ *
+ * CTRL-ALTLEFT-DELETE: Reboot
+ * CTRL-ALTLEFT-ESCAPE: Call the debugger (if compiled into the kernel)
+ * CTRL-ALTLEFT-KP+: Switch to next resolution (Xserver only)
+ * CTRL-ALTLEFT-KP-: Switch to previous resolution (Xserver only)
+ */
+
+/* values for type field of various kbd_overload ioctls */
+#define KBD_NONE 0 /* no function, key is disabled */
+#define KBD_SHIFT 1 /* keyboard shift */
+#define KBD_META 2 /* (ALT) alternate shift, sets bit8 to ASCII code */
+#define KBD_NUM 3 /* numeric shift cursors vs. numeric */
+#define KBD_CTL 4 /* control shift -- allows ctl function */
+#define KBD_CAPS 5 /* caps shift -- swaps case of letter */
+#define KBD_ASCII 6 /* ascii code for this key */
+#define KBD_SCROLL 7 /* stop output */
+#define KBD_FUNC 8 /* function key */
+#define KBD_KP 9 /* Keypad keys */
+#define KBD_BREAK 10 /* The damned BREAK key, ignored in ioctl */
+#define KBD_ALTGR 11 /* AltGr Translation feature */
+#define KBD_SHFTLOCK 12 /* some people are accustomed to this nonsense */
+#define KBD_ALTGRLOCK 13 /* Useful for 8-bit national kbds (cyrillic) */
+#define KBD_DOALTCAPS 0x0400 /* change by altgr + caps shift */
+#define KBD_DOCAPS 0x0800 /* change by caps shift */
+#define KBD_DIACPFX 0x4000 /* Key carries a diacritical prefix */
+#define KBD_OVERLOAD 0x8000 /* Key is overloaded, ignored in ioctl */
+#define KBD_MASK 0x001f /* mask for type */
+
+#define KBDMAXOVLKEYSIZE 15 /* excl. zero byte */
+struct kbd_ovlkey {
+ u_short keynum;
+ u_short type;
+ XCHAR unshift[KBDMAXOVLKEYSIZE+1];
+ XCHAR shift[KBDMAXOVLKEYSIZE+1];
+ XCHAR ctrl[KBDMAXOVLKEYSIZE+1];
+ XCHAR meta[KBDMAXOVLKEYSIZE+1];
+ XCHAR altgr[KBDMAXOVLKEYSIZE+1];
+ XCHAR shiftaltgr[KBDMAXOVLKEYSIZE+1];
+};
+
+
+/* Get (G) / Set (S) a key assignment. This will influence the current
+ * key value only
+ */
+#define KBDGCKEY _IOWR('K',16, struct kbd_ovlkey)
+#define KBDSCKEY _IOW('K',17, struct kbd_ovlkey)
+
+/* Get (G) the default (old) key assignment. You cannot overwrite the
+ * default setting, so this ioctl is unpaired
+ */
+#define KBDGOKEY _IOWR('K',18, struct kbd_ovlkey)
+
+
+
+/* Remove a key assignment for a key, i.e. restore default setting for key
+ * arg = keynum
+ */
+#define KBDRMKEY _IOW('K', 19, int)
+
+/* Restore the default key setting */
+#define KBDDEFAULT _IO('K',20)
+
+
+
+/* Set behavior of unassigned key layers
+ * Note that there is a hack from further versions which uses
+ * the flags KBD_C0 and KBD_A0 for this. This is still supported, but
+ * is not recommended way to do. It may disappear in future
+ * (what means that it won't :-))
+ */
+#define KBD_CLEARCTRL 2
+#define KBD_CLEARMETA 4
+#define KBD_CLEARALT 1
+#ifdef notyet
+ #define KBD_CLEARNORM 8
+ #define KBD_CLEARSHIFT 16
+ #define KBD_CLEARSHALT 32
+#endif
+#define KBDSCLRLYR _IOW('K',31,int)
+
+/* get (G) / set (S) CAPSLOCK LED behaviour.
+ * Not all of this keys may be accessible at your keyboard
+ * Note: For compatibility, the S ioctl returns the old state in arg
+ */
+#define KBD_CAPSCAPS 0 /* LED follows CAPSLOCK state */
+#define KBD_CAPSSHIFT 1 /* LED follows SHIFTLOCK state */
+#define KBD_CAPSALTGR 2 /* LED follows ALTGRLOCK state */
+#define KBD_CAPSINIT 0x04 /* bit to set to set a default for all VTYs */
+#define KBDGCAPSLED _IOR('K',27,int)
+#define KBDSCAPSLED _IOWR('K',25,int)
+
+/* extended functions: functions that are triggered by a keypress
+ * before key is converted to ASCII
+ *
+ * use function KBD_HOTKEYDELETE to remove a hotkey from a key
+ */
+struct kbd_hotkey {
+ u_short key;
+ u_short modifier;
+ u_short function;
+};
+#define KBDGSPECF _IOWR('K',32,struct kbd_hotkey)
+#define KBDSSPECF _IOW('K',33,struct kbd_hotkey)
+
+/* extended function prefixes (in modifier field)
+ * bit set triggers a special function on the key layer
+ */
+#define KBD_NOEXT 0x00 /* trigger never */
+#define KBD_EXT_N 0x01 /* on normal key (normal layer) */
+#define KBD_EXT_S 0x02 /* on shift key (shift layer) */
+#define KBD_EXT_C 0x04 /* on ctrl key (ctrl layer) */
+#define KBD_EXT_A 0x08 /* on alt key (alt layer) */
+#define KBD_EXT_SK 0x10 /* on syskey (PRINTSCREEN) (Meta Layer) */
+#define KBD_EXT_CA 0x20 /* on ctrl-alt (shift alt layer) */
+
+/* extended functions (in function field) */
+#define KBD_VTY0 0 /* select vty 0 */
+#define KBD_VTY1 1 /* select vty 1 */
+#define KBD_VTY2 2 /* select vty 2 */
+#define KBD_VTY3 3 /* select vty 3 */
+#define KBD_VTY4 4 /* select vty 4 */
+#define KBD_VTY5 5 /* select vty 5 */
+#define KBD_VTY6 6 /* select vty 6 */
+#define KBD_VTY7 7 /* select vty 7 */
+#define KBD_VTY8 8 /* select vty 8 */
+#define KBD_VTY9 9 /* select vty 9 */
+#define KBD_VTY10 10 /* select vty 10 */
+#define KBD_VTY11 11 /* select vty 11 */
+#define KBD_VTYUP 0x80 /* select next vty */
+#define KBD_VTYDOWN 0x81 /* select previous vty */
+#define KBD_RESETKEY 0x82 /* the CTRL-ALT-DEL key (movable) */
+#define KBD_DEBUGKEY 0x83 /* the CTRL-ALT-ESC key (debugger) */
+
+#define KBD_HOTKEYDELETE 0xff /* use to delete a hotkey KBDSSPECF */
+
+
+
+/* These are names used in older versions of keycap/codrv */
+/* do not use the following functions any longer in future */
+#ifdef COMPAT_CO011
+#define KBDRESET KBDCOLDRESET
+#define KBDRESET8042 KBDWARMRESET
+#define KBDFORCEASCII _IOW('K', 24, int) /* no op in codrv-0.1.2 */
+#define KBD_SCROLLLOCK KBD_LOCKSCROLL
+#define KBD_NUMLOCK KBD_LOCKNUM
+#define KBD_CAPSLOCK KBD_LOCKCAPS
+#define KBDASGNLEDS KBDSCAPSLED
+#ifndef KERNEL
+struct kbd_sound {
+ int pitch; /* Frequency in Hz */
+ int duration; /* Time in msec */
+};
+#endif
+#define KBDSETBELL _IOW('K',21, struct kbd_sound) /* do some music */
+#define OLDKBDSETBEEP _IOW('K',26, struct kbd_sound) /* change beep settings */
+
+struct oldkbd_ovlkey {
+ u_short keynum;
+ u_short type;
+ char unshift[KBDMAXOVLKEYSIZE+1];
+ char shift[KBDMAXOVLKEYSIZE+1];
+ char ctrl[KBDMAXOVLKEYSIZE+1];
+ char altgr[KBDMAXOVLKEYSIZE+1];
+};
+#define OLDKBDGCKEY _IOWR('K',16, struct oldkbd_ovlkey) /* get current key values */
+
+
+
+#endif /*COMPAT_CO011*/
+
+/***************************************************************************
+ * IOCTLs for Video Adapter
+ ***************************************************************************/
+
+/* to define the cursor shape for ioctl */
+struct cursorshape {
+ int start; /* topmost scanline, range 0...31 */
+ int end; /* bottom scanline, range 0...31 */
+};
+
+#define VGAGCURSOR _IOR('V',100, struct cursorshape) /* get cursor shape */
+#define VGASCURSOR _IOW('V',101, struct cursorshape) /* set cursor shape */
+
+
+
+/**** information ****/
+
+/* the video information structure for ioctl */
+struct videoinfo {
+ char name[20]; /* ASCIZ name of detected card */
+ short type; /* Adapter type, see below */
+ short subtype; /* Adapter specific subtype */
+ short ram; /* in KBytes */
+ short iobase; /* Address of 6845: 0x3b0 / 0x3d0 */
+};
+
+/* Get information about the videoboard */
+#define VGAGINFO _IOR('V',102, struct videoinfo)
+
+/* recognized Adapter types */
+#define VG_UNKNOWN 0
+#define VG_MONO 1
+#define VG_CGA 2
+#define VG_EGA 3
+#define VG_VGA 4
+#define VG_CHIPS 5
+/* CHIPS & TECHNOLOGIES has subtypes:
+ * 0x10 82c451
+ * 0x11 82c452
+ * 0x20 82c455
+ * 0x30 82c453
+ * 0x50 82c455
+ */
+#define VG_GENOA 6
+/* GENOA has subtypes:
+ * 0x33/0x55 5100-5400, ET3000 based
+ * 0x22 6100
+ * 0x00 6200,6300
+ * 0x11 6400,6600
+ */
+#define VG_PARADISE 7
+/* PARADISE has subtypes:
+ * 01 PVGA1A,WD90C90
+ * 02 WD90C00
+ * 03 WD90C10
+ * 04 WD90C11
+ */
+#define VG_TVGA 8
+/* TVGA has subtypes:
+ * 00-02 8800
+ * 03 8900B
+ * 04 8900C
+ * 13 8900C
+ * 23 9000
+ */
+#define VG_ET3000 9
+#define VG_ET4000 10
+#define VG_VIDEO7 11
+/* VIDEO7 has subtypes:
+ * 0x80-0xfe VEGA VGA
+ * 0x70-0x7e V7VGA FASTWRITE/VRAM
+ * 0x50-0x59 V7VGA version 5
+ * 0x41-0x49 1024i
+ */
+#define VG_ATI 12
+/* ATI has subtypes:
+ * 0x01nn 18800
+ * 0x02nn 18800-1
+ * 0x03nn 28800-2
+ * 0x04nn-05nn
+ * with nn:
+ * 0x01 VGA WONDER
+ * 0x02 EGA WONDER800+
+ * 0x03 VGA BASIC 16+
+ */
+
+
+
+/**** Screen blanking ****/
+
+/* Get (G) / Set (S) screen blanker timeout (seconds),
+ * time=0 disables blanking
+ *
+ * The blanking state is coded in bits 31 and 30 of word returned by get
+ */
+#define VGA_BLANKOFF 0x00000000 /* display is on, no blanking */
+#define VGA_BLANKON 0x40000000 /* display is on, wait for blank */
+#define VGA_BLANKED 0x80000000 /* display is dark */
+#define VGAGBLANK _IOR('V',2,int)
+#define VGASBLANK _IOW('V',3,int)
+
+
+
+/**** Text/Attribute direct access, block move ****/
+
+struct vga_block {
+ short mode;
+ short pagenum;
+ short x0,y0; /* upper left coordinates 0..x-1, 0..y-1 */
+ short x1,y1; /* lower right coordinates >= x0,y0 */
+ u_char *map; /* must be allocated by user process ! */
+};
+
+/* mode word */
+#define VGA_SCREEN 0x01 /* entire screen, ignore x,y */
+#define VGA_WINDOW 0x02 /* use x,y for a rectangular window */
+#define VGA_TEXT 0x10 /* copy text information only */
+#define VGA_ATTR 0x20 /* copy attribute information only */
+#define VGA_BOTH 0x30 /* copy text and attribute */
+#define VGA_ALL 0x31 /* copy complete screen */
+
+/* Get (G) / Set (S) a rectangular block of screen
+ * The virtual screen need not be visible.
+ * The buffer must be provided by the user process and must be large enough
+ * use VGAGVRES to find out how many bytes
+ * pagenum: 0..n, n < max_vty, VTY number
+ * -1, actual VTY
+ */
+#define VGAGBLOCK _IOWR('V',4,struct vga_block)
+#define VGASBLOCK _IOW('V',5,struct vga_block)
+
+
+
+#define VGA_TXTPAGE0 0
+#define VGA_TXTPAGE1 1
+#ifdef notyet
+#define VGA_GFXPAGE 2
+#endif
+#define VGA_PC8CODING 0x80 /* obsolete ! */
+
+/* maximum dimension of pixels
+ * Note: this is the space reserved in the fontchar map, but
+ * does not mean, that this resolution is accepted in the current release
+ * codrv-0.1.2 accepts 8x16 / "9x16" fonts only
+ */
+#define VGA_MAXX 16
+#define VGA_MAXY 16
+
+struct fchar {
+ XCHAR encoding; /* encoding of character */
+ char _f1_,_f2_,_f3_; /* filler */
+ u_char map[VGA_MAXX/8*VGA_MAXY];
+};
+
+struct fmap {
+ short page; /* page to load */
+ short nr; /* nr of characters to load */
+ char x,y; /* x,y pixel width */
+ XCHAR start; /* first character in sequence (get only) */
+ struct fchar *fntmap; /* allocated by user process */
+};
+
+/* get (G) / set (S) font map. Must provide page,nr,start for get */
+#define VGAGFONTMAP _IOWR('V',6,struct fmap)
+#define VGASFONTMAP _IOW('V',7,struct fmap)
+
+
+
+/* do not use the following functions any longer in future */
+#ifdef COMPAT_CO011
+/* miscellaneous functions: */
+#define VGA_DIS1 1 /* disable font 1 */
+#define VGA_GTENC 2 /* get current encoding */
+#define VGA_SBLANK 3 /* set screen blanking timeout (use VGASBLANK!) */
+#define VGA_GBLANK 4 /* get screen blanking timeout (use VGAGBLANK!) */
+
+struct miscfcns {
+ u_char cmd;
+ union {
+ short enc[2];
+ int timeout;
+ } u;
+};
+#define VGAMISCFCNS _IOWR('V',107,struct miscfcns) /* misc functions */
+
+
+/* Font mapping this needs at least an EGA card (else EINVAL) */
+#define VGAFNTLATIN1 0x00
+#define VGAFNTEXTEND1 0x01
+#define VGAFNTEXTEND2 0x02
+#define VGAFNTGREEK 0x03
+#define VGAFNTCYRILLIC 0x04
+#define VGAFNTHEBREW 0x05
+#define VGAFNTARABIAN 0x06
+
+#define VGA_FNTNCHARS 256
+#define VGA_FNTCSIZE 15
+
+struct fontchar {
+ u_char page; /* which font page */
+ u_char idx; /* which char in font page */
+ u_char cmap[VGA_FNTCSIZE]; /* character bitmap */
+};
+
+#define OLDVGAGCHAR _IOWR('V',105,struct fontchar) /* get character of font */
+#define OLDVGASCHAR _IOW('V',106,struct fontchar) /* set character in font */
+
+struct fontmap {
+ u_char page; /* page to load */
+ u_short encoding; /* font encoding */
+ u_char map[VGA_FNTNCHARS*VGA_FNTCSIZE];
+};
+
+#define OLDVGAGFNTMAP _IOWR('V',103,struct fontmap) /* get font */
+#define VGAGFNTMAP OLDVGAGFNTMAP
+#define OLDVGASFNTMAP _IOW('V',104,struct fontmap) /* set font */
+#define VGASFNTMAP OLDVGASFNTMAP
+
+#endif
+
+
+
+
+struct textpage {
+ u_char pagenum; /* note: only page 0 used by vtys */
+#define VGA_TEXTATTR 0
+#define VGA_TEXTDATA 1
+ u_char ad;
+#define VGA_LINES 50 /* only 25 used for now */
+#define VGA_COLUMNS 80
+ u_char map[VGA_LINES*VGA_COLUMNS];
+};
+
+#define VGAGPAGE _IOWR('V',108,struct textpage) /* get a data page */
+#define VGASPAGE _IOW('V',109,struct textpage) /* set a data page */
+
+/**** Signalling access ****/
+
+/* Use "take control" in an application program to signal the kernel
+ * that the program wants to use video memory (such as Xserver)
+ * before the program switches modes
+ *
+ * Use "give control" to return the control to the kernel. The application
+ * should have restored the original state before giving back control.
+ * Close /dev/vga also returns control.
+ *
+ * However, the kernel remains the master in the house, and reserves the right
+ * to grab control back at any time. (It usually doesn't).
+ *
+ */
+#define VGATAKECTRL _IO('V',8)
+#define VGAGIVECTRL _IO('V',9)
+
+/***************************************************************************
+ * Pandora's box, don't even think of using the following ioctl's
+ * (if you happen to find some; codrv_experimental might not be
+ * available at your system)
+ ***************************************************************************/
+
+#ifdef PANDORA
+#include "codrv_experimental.h"
+#endif
+
+
+
+/***************************************************************************
+ * XFree86 pccons support
+ ***************************************************************************/
+
+#ifdef COMPAT_PCCONS
+/* The following calls are special to the old pccons driver and are
+ * not understood or supported by codrv.
+ * This file serves as a central definition base for these calls
+ * in order to avoid defining them in applications that want to
+ * use them.
+ *
+ * One word of warning: There are different purpose tty ioctls
+ * with the same encoding, see <sys/ioctl.h>
+ * TIOCSDTR = _IO('t', 121)
+ * TIOCCBRK = _IO('t', 122)
+ *
+ */
+#define CONSOLE_X_MODE_ON _IO('t',121)
+#define CONSOLE_X_MODE_OFF _IO('t',122)
+#define CONSOLE_X_BELL _IOW('t',123,int[2])
+#endif /* COMPAT_PCCONS */
+
+#endif /* _IOCTL_PC_H_ */
+
diff --git a/sys/i386/include/ipl.h b/sys/i386/include/ipl.h
new file mode 100644
index 0000000..248ca56
--- /dev/null
+++ b/sys/i386/include/ipl.h
@@ -0,0 +1,7 @@
+#ifndef _ISA_IPL_H_
+#define _ISA_IPL_H_
+
+#define NHWI 16 /* number of h/w interrupts */
+#define HWI_MASK 0xffff /* bits corresponding to h/w interrupts */
+
+#endif /* _ISA_IPL_H_ */
diff --git a/sys/i386/include/limits.h b/sys/i386/include/limits.h
new file mode 100644
index 0000000..e507313
--- /dev/null
+++ b/sys/i386/include/limits.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)limits.h 8.3 (Berkeley) 1/4/94
+ */
+
+#ifndef _MACHINE_LIMITS_H_
+#define _MACHINE_LIMITS_H_ 1
+
+#define CHAR_BIT 8 /* number of bits in a char */
+#define MB_LEN_MAX 6 /* Allow 31 bit UTF2 */
+
+
+#define CLK_TCK 128 /* ticks per second */
+
+/*
+ * According to ANSI (section 2.2.4.2), the values below must be usable by
+ * #if preprocessing directives. Additionally, the expression must have the
+ * same type as would an expression that is an object of the corresponding
+ * type converted according to the integral promotions. The subtraction for
+ * INT_MIN and LONG_MIN is so the value is not unsigned; 2147483648 is an
+ * unsigned int for 32-bit two's complement ANSI compilers (section 3.1.3.2).
+ * These numbers work for pcc as well. The UINT_MAX and ULONG_MAX values
+ * are written as hex so that GCC will be quiet about large integer constants.
+ */
+#define SCHAR_MAX 127 /* min value for a signed char */
+#define SCHAR_MIN (-128) /* max value for a signed char */
+
+#define UCHAR_MAX 255 /* max value for an unsigned char */
+#define CHAR_MAX 127 /* max value for a char */
+#define CHAR_MIN (-128) /* min value for a char */
+
+#define USHRT_MAX 65535 /* max value for an unsigned short */
+#define SHRT_MAX 32767 /* max value for a short */
+#define SHRT_MIN (-32768) /* min value for a short */
+
+#define UINT_MAX 0xffffffff /* max value for an unsigned int */
+#define INT_MAX 2147483647 /* max value for an int */
+#define INT_MIN (-2147483647-1) /* min value for an int */
+
+#define ULONG_MAX 0xffffffff /* max value for an unsigned long */
+#define LONG_MAX 2147483647 /* max value for a long */
+#define LONG_MIN (-2147483647-1) /* min value for a long */
+
+#if !defined(_ANSI_SOURCE)
+#define SSIZE_MAX INT_MAX /* max value for a ssize_t */
+
+#if !defined(_POSIX_SOURCE)
+#define SIZE_T_MAX UINT_MAX /* max value for a size_t */
+
+/* GCC requires that quad constants be written as expressions. */
+#define UQUAD_MAX ((u_quad_t)0-1) /* max value for a uquad_t */
+ /* max value for a quad_t */
+#define QUAD_MAX ((quad_t)(UQUAD_MAX >> 1))
+#define QUAD_MIN (-QUAD_MAX-1) /* min value for a quad_t */
+
+#endif /* !_POSIX_SOURCE */
+#endif /* !_ANSI_SOURCE */
+
+#endif /* _MACHINE_LIMITS_H_ */
diff --git a/sys/i386/include/lpt.h b/sys/i386/include/lpt.h
new file mode 100644
index 0000000..87af5bc
--- /dev/null
+++ b/sys/i386/include/lpt.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 1994 Geoffrey M. Rehmet
+ *
+ * This program is free software; you may redistribute it and/or
+ * modify it, provided that it retain the above copyright notice
+ * and the following disclaimer.
+ *
+ * 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.
+ *
+ * Geoff Rehmet, Rhodes University, South Africa <csgr@cs.ru.ac.za>
+ *
+ */
+
+#ifndef _LPT_PRINTER_H_
+#define _LPT_PRINTER_H_
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#define LPT_IRQ _IOW('p', 1, long) /* set interrupt status */
+
+#endif
diff --git a/sys/i386/include/mtpr.h b/sys/i386/include/mtpr.h
new file mode 100644
index 0000000..e8347e6
--- /dev/null
+++ b/sys/i386/include/mtpr.h
@@ -0,0 +1,4 @@
+/*
+ * Unused in 386BSD port
+ * $Id$
+ */
diff --git a/sys/i386/include/npx.h b/sys/i386/include/npx.h
new file mode 100644
index 0000000..87cd6f9
--- /dev/null
+++ b/sys/i386/include/npx.h
@@ -0,0 +1,141 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)npx.h 5.3 (Berkeley) 1/18/91
+ * $Id: npx.h,v 1.2 1993/10/16 14:39:22 rgrimes Exp $
+ */
+
+/*
+ * 287/387 NPX Coprocessor Data Structures and Constants
+ * W. Jolitz 1/90
+ */
+
+#ifndef ___NPX87___
+#define ___NPX87___
+
+/* Environment information of floating point unit */
+struct env87 {
+ long en_cw; /* control word (16bits) */
+ long en_sw; /* status word (16bits) */
+ long en_tw; /* tag word (16bits) */
+ long en_fip; /* floating point instruction pointer */
+ u_short en_fcs; /* floating code segment selector */
+ u_short en_opcode; /* opcode last executed (11 bits ) */
+ long en_foo; /* floating operand offset */
+ long en_fos; /* floating operand segment selector */
+};
+
+/* Contents of each floating point accumulator */
+struct fpacc87 {
+#ifdef dontdef /* too unportable */
+ u_long fp_mantlo; /* mantissa low (31:0) */
+ u_long fp_manthi; /* mantissa high (63:32) */
+ int fp_exp:15; /* exponent */
+ int fp_sgn:1; /* mantissa sign */
+#else
+ u_char fp_bytes[10];
+#endif
+};
+
+/* Floating point context */
+struct save87 {
+ struct env87 sv_env; /* floating point control/status */
+ struct fpacc87 sv_ac[8]; /* accumulator contents, 0-7 */
+ u_long sv_ex_sw; /* status word for last exception (was pad) */
+ u_long sv_ex_tw; /* tag word for last exception (was pad) */
+#ifdef GPL_MATH_EMULATE
+ u_char sv_pad[60];
+#else
+ u_char sv_pad[8 * 2 - 2 * 4]; /* bogus historical padding */
+#endif /* GPL_MATH_EMULATE */
+};
+
+/* Cyrix EMC memory - mapped coprocessor context switch information */
+struct emcsts {
+ long em_msw; /* memory mapped status register when swtched */
+ long em_tar; /* memory mapped temp A register when swtched */
+ long em_dl; /* memory mapped D low register when swtched */
+};
+
+/* Intel prefers long real (53 bit) precision */
+#define __iBCS_NPXCW__ 0x262
+/* wfj prefers temporary real (64 bit) precision */
+#define __386BSD_NPXCW__ 0x362
+/*
+ * bde prefers 53 bit precision and all exceptions masked.
+ *
+ * The standard control word from finit is 0x37F, giving:
+ *
+ * round to nearest
+ * 64-bit precision
+ * all exceptions masked.
+ *
+ * Now I want:
+ *
+ * affine mode for 287's (if they work at all) (1 in bitfield 1<<12)
+ * 53-bit precision (2 in bitfield 3<<8)
+ * overflow exception unmasked (0 in bitfield 1<<3)
+ * zero divide exception unmasked (0 in bitfield 1<<2)
+ * invalid-operand exception unmasked (0 in bitfield 1<<0).
+ *
+ * 64-bit precision often gives bad results with high level languages
+ * because it makes the results of calculations depend on whether
+ * intermediate values are stored in memory or in FPU registers.
+ *
+ * The "Intel" and wfj control words have:
+ *
+ * underflow exception unmasked (0 in bitfield 1<<4)
+ *
+ * but that causes an unexpected exception in the test program 'paranoia'
+ * and makes denormals useless (DBL_MIN / 2 underflows). It doesn't make
+ * a lot of sense to trap underflow without trapping denormals.
+ *
+ * Later I will want the IEEE default of all exceptions masked. See the
+ * 0.0 math manpage for why this is better. The 0.1 math manpage is empty.
+ */
+#define __BDE_NPXCW__ 0x1272
+#define __BETTER_BDE_NPXCW__ 0x127f
+
+#ifdef __BROKEN_NPXCW__
+#ifdef __386BSD__
+#define __INITIAL_NPXCW__ __386BSD_NPXCW__
+#else
+#define __INITIAL_NPXCW__ __iBCS_NPXCW__
+#endif
+#else
+#define __INITIAL_NPXCW__ __BDE_NPXCW__
+#endif
+
+#endif ___NPX87___
diff --git a/sys/i386/include/param.h b/sys/i386/include/param.h
new file mode 100644
index 0000000..ab8ad9c
--- /dev/null
+++ b/sys/i386/include/param.h
@@ -0,0 +1,168 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)param.h 5.8 (Berkeley) 6/28/91
+ * $Id: param.h,v 1.13 1994/01/31 04:18:54 davidg Exp $
+ */
+
+#ifndef _MACHINE_PARAM_H_
+#define _MACHINE_PARAM_H_ 1
+
+/*
+ * Machine dependent constants for Intel 386.
+ */
+
+#define MACHINE "i386"
+#define MID_MACHINE MID_I386
+
+/*
+ * Round p (pointer or byte index) up to a correctly-aligned value
+ * for all data types (int, long, ...). The result is u_int and
+ * must be cast to any desired pointer type.
+ */
+#define ALIGNBYTES (sizeof(int) - 1)
+#define ALIGN(p) (((u_int)(p) + ALIGNBYTES) &~ ALIGNBYTES)
+
+/* XXX PGSHIFT and PG_SHIFT are two names for the same thing */
+#define PGSHIFT 12 /* LOG2(NBPG) */
+#define PAGE_SHIFT 12
+#define NBPG (1 << PAGE_SHIFT) /* bytes/page */
+#define PAGE_SIZE (1 << PAGE_SHIFT)
+#define PAGE_MASK (PAGE_SIZE-1)
+#define PGOFSET (NBPG-1) /* byte offset into page */
+#define NPTEPG (NBPG/(sizeof (pt_entry_t)))
+
+/* XXX PDRSHIFT and PD_SHIFT are two names for the same thing */
+#define PDRSHIFT 22 /* LOG2(NBPDR) */
+#define NBPDR (1 << PDRSHIFT) /* bytes/page dir */
+#define PDROFSET (NBPDR-1) /* byte offset into page dir */
+
+/*
+ * XXX This should really be KPTDPTDI << PDRSHIFT, but since KPTDPTDI is
+ * defined in pmap.h which is included after this we can't do that
+ * (YET!)
+ */
+#define BTOPKERNBASE (KERNBASE >> PGSHIFT)
+
+#define DEV_BSHIFT 9 /* log2(DEV_BSIZE) */
+#define DEV_BSIZE (1 << DEV_BSHIFT)
+
+#define BLKDEV_IOSIZE 2048
+#define MAXPHYS (64 * 1024) /* max raw I/O transfer size */
+
+#define CLSIZELOG2 0
+#define CLSIZE (1 << CLSIZELOG2)
+
+/* NOTE: SSIZE, SINCR and UPAGES must be multiples of CLSIZE */
+#define SSIZE 1 /* initial stack size/NBPG */
+#define SINCR 1 /* increment of stack/NBPG */
+
+#define UPAGES 2 /* pages of u-area */
+
+/*
+ * Constants related to network buffer management.
+ * MCLBYTES must be no larger than CLBYTES (the software page size), and,
+ * on machines that exchange pages of input or output buffers with mbuf
+ * clusters (MAPPED_MBUFS), MCLBYTES must also be an integral multiple
+ * of the hardware page size.
+ */
+#ifndef MSIZE
+#define MSIZE 128 /* size of an mbuf */
+#endif /* MSIZE */
+
+#ifndef MCLSHIFT
+#define MCLSHIFT 12 /* convert bytes to m_buf clusters */
+#endif /* MCLSHIFT */
+#define MCLBYTES (1 << MCLSHIFT) /* size of an m_buf cluster */
+#define MCLOFSET (MCLBYTES - 1) /* offset within an m_buf cluster */
+
+#ifndef NMBCLUSTERS
+#ifdef GATEWAY
+#define NMBCLUSTERS 512 /* map size, max cluster allocation */
+#else
+#define NMBCLUSTERS 256 /* map size, max cluster allocation */
+#endif /* GATEWAY */
+#endif /* NMBCLUSTERS */
+
+/*
+ * Some macros for units conversion
+ */
+/* Core clicks (4096 bytes) to segments and vice versa */
+#define ctos(x) (x)
+#define stoc(x) (x)
+
+/* Core clicks (4096 bytes) to disk blocks */
+#define ctod(x) ((x)<<(PGSHIFT-DEV_BSHIFT))
+#define dtoc(x) ((x)>>(PGSHIFT-DEV_BSHIFT))
+#define dtob(x) ((x)<<DEV_BSHIFT)
+
+/* clicks to bytes */
+#define ctob(x) ((x)<<PGSHIFT)
+
+/* bytes to clicks */
+#define btoc(x) (((unsigned)(x)+(NBPG-1))>>PGSHIFT)
+
+#define btodb(bytes) /* calculates (bytes / DEV_BSIZE) */ \
+ ((unsigned)(bytes) >> DEV_BSHIFT)
+#define dbtob(db) /* calculates (db * DEV_BSIZE) */ \
+ ((unsigned)(db) << DEV_BSHIFT)
+
+/*
+ * Map a ``block device block'' to a file system block.
+ * This should be device dependent, and will be if we
+ * add an entry to cdevsw/bdevsw for that purpose.
+ * For now though just use DEV_BSIZE.
+ */
+#define bdbtofsb(bn) ((bn) / (BLKDEV_IOSIZE/DEV_BSIZE))
+
+/*
+ * Mach derived conversion macros
+ */
+#define trunc_page(x) ((unsigned)(x) & ~(NBPG-1))
+#define round_page(x) ((((unsigned)(x)) + NBPG - 1) & ~(NBPG-1))
+
+#define atop(x) ((unsigned)(x) >> PG_SHIFT)
+#define ptoa(x) ((unsigned)(x) << PG_SHIFT)
+
+#define i386_round_pdr(x) ((((unsigned)(x)) + NBPDR - 1) & ~(NBPDR-1))
+#define i386_trunc_pdr(x) ((unsigned)(x) & ~(NBPDR-1))
+#define i386_round_page(x) ((((unsigned)(x)) + NBPG - 1) & ~(NBPG-1))
+#define i386_trunc_page(x) ((unsigned)(x) & ~(NBPG-1))
+#define i386_btod(x) ((unsigned)(x) >> PDRSHIFT)
+#define i386_dtob(x) ((unsigned)(x) << PDRSHIFT)
+#define i386_btop(x) ((unsigned)(x) >> PGSHIFT)
+#define i386_ptob(x) ((unsigned)(x) << PGSHIFT)
+
+#endif /* _MACHINE_PARAM_H_ */
diff --git a/sys/i386/include/pc/display.h b/sys/i386/include/pc/display.h
new file mode 100644
index 0000000..9e64a3f
--- /dev/null
+++ b/sys/i386/include/pc/display.h
@@ -0,0 +1,45 @@
+/*
+ * IBM PC display definitions
+ *
+ * $Id$
+ */
+
+/* Color attributes for foreground text */
+
+#define FG_BLACK 0
+#define FG_BLUE 1
+#define FG_GREEN 2
+#define FG_CYAN 3
+#define FG_RED 4
+#define FG_MAGENTA 5
+#define FG_BROWN 6
+#define FG_LIGHTGREY 7
+#define FG_DARKGREY 8
+#define FG_LIGHTBLUE 9
+#define FG_LIGHTGREEN 10
+#define FG_LIGHTCYAN 11
+#define FG_LIGHTRED 12
+#define FG_LIGHTMAGENTA 13
+#define FG_YELLOW 14
+#define FG_WHITE 15
+#define FG_BLINK 0x80
+
+/* Color attributes for text background */
+
+#define BG_BLACK 0x00
+#define BG_BLUE 0x10
+#define BG_GREEN 0x20
+#define BG_CYAN 0x30
+#define BG_RED 0x40
+#define BG_MAGENTA 0x50
+#define BG_BROWN 0x60
+#define BG_LIGHTGREY 0x70
+
+/* Monochrome attributes for foreground text */
+
+#define FG_UNDERLINE 0x01
+#define FG_INTENSE 0x08
+
+/* Monochrome attributes for text background */
+
+#define BG_INTENSE 0x10
diff --git a/sys/i386/include/pc/msdos.h b/sys/i386/include/pc/msdos.h
new file mode 100644
index 0000000..ea221c7
--- /dev/null
+++ b/sys/i386/include/pc/msdos.h
@@ -0,0 +1,65 @@
+/*
+ * msdos common header file
+ * [obtained from mtools -wfj]
+ * how to decipher DOS disk structures in coexisting with DOS
+ *
+ * $Id$
+ */
+
+#define MSECTOR_SIZE 512 /* MSDOS sector size in bytes */
+#define MDIR_SIZE 32 /* MSDOS directory size in bytes */
+#define MAX_CLUSTER 8192 /* largest cluster size */
+#define MAX_PATH 128 /* largest MSDOS path length */
+#define MAX_DIR_SECS 64 /* largest directory (in sectors) */
+
+#define NEW 1
+#define OLD 0
+
+struct directory {
+ unsigned char name[8]; /* file name */
+ unsigned char ext[3]; /* file extension */
+ unsigned char attr; /* attribute byte */
+ unsigned char reserved[10]; /* ?? */
+ unsigned char time[2]; /* time stamp */
+ unsigned char date[2]; /* date stamp */
+ unsigned char start[2]; /* starting cluster number */
+ unsigned char size[4]; /* size of the file */
+};
+
+struct bootsector {
+ unsigned char jump[3]; /* Jump to boot code */
+ unsigned char banner[8]; /* OEM name & version */
+ unsigned char secsiz[2]; /* Bytes per sector hopefully 512 */
+ unsigned char clsiz; /* Cluster size in sectors */
+ unsigned char nrsvsect[2]; /* Number of reserved (boot) sectors */
+ unsigned char nfat; /* Number of FAT tables hopefully 2 */
+ unsigned char dirents[2]; /* Number of directory slots */
+ unsigned char psect[2]; /* Total sectors on disk */
+ unsigned char descr; /* Media descriptor=first byte of FAT */
+ unsigned char fatlen[2]; /* Sectors in FAT */
+ unsigned char nsect[2]; /* Sectors/track */
+ unsigned char nheads[2]; /* Heads */
+ unsigned char nhs[4]; /* number of hidden sectors */
+ unsigned char bigsect[4]; /* big total sectors */
+ unsigned char junk[476]; /* who cares? */
+};
+
+/* DOS partition table -- located in boot block */
+
+#define DOSBBSECTOR 0 /* DOS boot block relative sector number */
+#define DOSPARTOFF 446
+#define NDOSPART 4
+
+struct dos_partition {
+ unsigned char dp_flag; /* bootstrap flags */
+ unsigned char dp_shd; /* starting head */
+ unsigned char dp_ssect; /* starting sector */
+ unsigned char dp_scyl; /* starting cylinder */
+ unsigned char dp_typ; /* partition type */
+#define DOSPTYP_386BSD 0xa5 /* 386BSD partition type */
+ unsigned char dp_ehd; /* end head */
+ unsigned char dp_esect; /* end sector */
+ unsigned char dp_ecyl; /* end cylinder */
+ unsigned long dp_start; /* absolute starting sector number */
+ unsigned long dp_size; /* partition size in sectors */
+} dos_partitions[NDOSPART];
diff --git a/sys/i386/include/pcaudioio.h b/sys/i386/include/pcaudioio.h
new file mode 100644
index 0000000..30e5ad6
--- /dev/null
+++ b/sys/i386/include/pcaudioio.h
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1994 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#ifndef _PCAUDIOIO_H_
+#define _PCAUDIOIO_H_
+
+typedef struct audio_prinfo {
+ unsigned sample_rate; /* samples per second */
+ unsigned channels; /* # of channels (interleaved) */
+ unsigned precision; /* sample size in bits */
+ unsigned encoding; /* encoding method used */
+
+ unsigned gain; /* volume level: 0 - 255 */
+ unsigned port; /* input/output device */
+ unsigned _fill1[4];
+
+ unsigned samples; /* samples played */
+ unsigned eof; /* ?!? */
+ unsigned char pause; /* !=0 pause, ==0 continue */
+ unsigned char error; /* !=0 if overflow/underflow */
+ unsigned char waiting; /* !=0 if others wants access */
+ unsigned char _fill2[3];
+
+ unsigned char open; /* is device open */
+ unsigned char active; /* !=0 if sound hardware is active */
+} audio_prinfo_t;
+
+typedef struct audio_info {
+ audio_prinfo_t play;
+ audio_prinfo_t record;
+ unsigned monitor_gain;
+ unsigned _fill[4];
+} audio_info_t;
+
+#define AUDIO_ENCODING_ULAW (1) /* u-law encoding */
+#define AUDIO_ENCODING_ALAW (2) /* A-law encoding */
+#define AUDIO_ENCODING_RAW (3) /* linear encoding */
+
+#define AUDIO_MIN_GAIN (0) /* minimum volume value */
+#define AUDIO_MAX_GAIN (255) /* maximum volume value */
+
+#define AUDIO_INITINFO(i) memset((void*)i, 0xff, sizeof(audio_info_t))
+
+#define AUDIO_GETINFO _IOR('A', 1, audio_info_t)
+#define AUDIO_SETINFO _IOWR('A', 2, audio_info_t)
+#define AUDIO_DRAIN _IO('A', 3)
+#define AUDIO_FLUSH _IO('A', 4)
+
+#endif /*!_PCAUDIOIO_H*/
diff --git a/sys/i386/include/pcb.h b/sys/i386/include/pcb.h
new file mode 100644
index 0000000..990e5f9
--- /dev/null
+++ b/sys/i386/include/pcb.h
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)pcb.h 5.10 (Berkeley) 5/12/91
+ * $Id: pcb.h,v 1.3 1993/11/07 17:42:59 wollman Exp $
+ */
+
+#ifndef _I386_PCB_H_
+#define _I386_PCB_H_
+
+/*
+ * Intel 386 process control block
+ */
+#include "machine/tss.h"
+#include "machine/npx.h"
+
+struct pcb {
+ struct i386tss pcb_tss;
+#define pcb_ksp pcb_tss.tss_esp0
+#define pcb_ptd pcb_tss.tss_cr3
+#define pcb_cr3 pcb_ptd
+#define pcb_pc pcb_tss.tss_eip
+#define pcb_psl pcb_tss.tss_eflags
+#define pcb_usp pcb_tss.tss_esp
+#define pcb_fp pcb_tss.tss_ebp
+#ifdef notyet
+ u_char pcb_iomap[NPORT/sizeof(u_char)]; /* i/o port bitmap */
+#endif
+ caddr_t pcb_ldt; /* per process (user) LDT */
+ int pcb_ldt_len; /* number of LDT entries */
+ struct save87 pcb_savefpu; /* floating point state for 287/387 */
+ struct emcsts pcb_saveemc; /* Cyrix EMC state */
+/*
+ * Software pcb (extension)
+ */
+ int pcb_flags;
+#ifdef notused
+#define FP_WASUSED 0x01 /* process has used fltng pnt hardware */
+#define FP_NEEDSSAVE 0x02 /* ... that needs save on next context switch */
+#define FP_NEEDSRESTORE 0x04 /* ... that needs restore on next DNA fault */
+#endif
+#define FP_USESEMC 0x08 /* process uses EMC memory-mapped mode */
+#define FP_SOFTFP 0x20 /* process using software fltng pnt emulator */
+ short pcb_iml; /* interrupt mask level */
+ caddr_t pcb_onfault; /* copyin/out fault recovery */
+ long pcb_sigc[8]; /* XXX signal code trampoline */
+ int pcb_cmap2; /* XXX temporary PTE - will prefault instead */
+};
+
+/*
+ * The pcb is augmented with machine-dependent additional data for
+ * core dumps. For the i386: ???
+ */
+struct md_coredump {
+};
+
+#ifdef KERNEL
+extern struct pcb *curpcb; /* our current running pcb */
+#endif
+
+#endif /* _I386_PCB_H_ */
diff --git a/sys/i386/include/pio.h b/sys/i386/include/pio.h
new file mode 100644
index 0000000..ef9aba3
--- /dev/null
+++ b/sys/i386/include/pio.h
@@ -0,0 +1,48 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1990 Carnegie-Mellon University
+ * All rights reserved. The CMU software License Agreement specifies
+ * the terms and conditions for use and redistribution.
+ *
+ * from: Mach, unknown, 386BSD patch kit
+ * $Id: pio.h,v 1.2 1993/10/16 14:39:23 rgrimes Exp $
+ */
+
+#ifndef _MACHINE_PIO_H_
+#define _MACHINE_PIO_H_ 1
+
+#define inl(y) \
+({ unsigned long _tmp__; \
+ asm volatile("inl %1, %0" : "=a" (_tmp__) : "d" ((unsigned short)(y))); \
+ _tmp__; })
+
+#define inw(y) \
+({ unsigned short _tmp__; \
+ asm volatile(".byte 0x66; inl %1, %0" : "=a" (_tmp__) : "d" ((unsigned short)(y))); \
+ _tmp__; })
+
+/*
+ * only do this if it has not already be defined.. this is a crock for the
+ * patch kit for right now. Need to clean up all the inx, outx stuff for
+ * 0.1.5 to use 1 common header file, that has Bruces fast mode inb/outb
+ * stuff in it. Rgrimes 5/27/93
+ */
+#ifndef inb
+#define inb(y) \
+({ unsigned char _tmp__; \
+ asm volatile("inb %1, %0" : "=a" (_tmp__) : "d" ((unsigned short)(y))); \
+ _tmp__; })
+#endif
+
+
+#define outl(x, y) \
+{ asm volatile("outl %0, %1" : : "a" (y) , "d" ((unsigned short)(x))); }
+
+
+#define outw(x, y) \
+{asm volatile(".byte 0x66; outl %0, %1" : : "a" ((unsigned short)(y)) , "d" ((unsigned short)(x))); }
+
+
+#define outb(x, y) \
+{ asm volatile("outb %0, %1" : : "a" ((unsigned char)(y)) , "d" ((unsigned short)(x))); }
+#endif /* _MACHINE_PIO_H_ */
diff --git a/sys/i386/include/pmap.h b/sys/i386/include/pmap.h
new file mode 100644
index 0000000..7ddcebd
--- /dev/null
+++ b/sys/i386/include/pmap.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 1991 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department and William Jolitz of UUNET Technologies Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Derived from hp300 version by Mike Hibler, this version by William
+ * Jolitz uses a recursive map [a pde points to the page directory] to
+ * map the page tables using the pagetables themselves. This is done to
+ * reduce the impact on kernel virtual memory for lots of sparse address
+ * space, and to reduce the cost of memory to each process.
+ *
+ * from: hp300: @(#)pmap.h 7.2 (Berkeley) 12/16/90
+ * from: @(#)pmap.h 7.4 (Berkeley) 5/12/91
+ * $Id: pmap.h,v 1.12 1994/03/24 23:12:48 davidg Exp $
+ */
+
+#ifndef _PMAP_MACHINE_
+#define _PMAP_MACHINE_ 1
+
+#include <machine/pte.h>
+
+typedef unsigned int *pd_entry_t;
+typedef unsigned int *pt_entry_t;
+
+/*
+ * NKPDE controls the virtual space of the kernel, what ever is left, minus
+ * the alternate page table area is given to the user (NUPDE)
+ */
+/*
+ * NKPDE controls the virtual space of the kernel, what ever is left is
+ * given to the user (NUPDE)
+ */
+#ifndef NKPT
+#define NKPT 24 /* actual number of kernel pte's */
+#endif
+#ifndef NKPDE
+#define NKPDE 63 /* addressable number of kpte's */
+#endif
+
+#define NUPDE (NPTEPG-NKPDE) /* number of user pde's */
+
+/*
+ * The *PTDI values control the layout of virtual memory
+ *
+ * XXX This works for now, but I am not real happy with it, I'll fix it
+ * right after I fix locore.s and the magic 28K hole
+ */
+#define APTDPTDI (NPTEPG-1) /* alt ptd entry that points to APTD */
+#define KPTDI (APTDPTDI-NKPDE)/* start of kernel virtual pde's */
+#define PTDPTDI (KPTDI-1) /* ptd entry that points to ptd! */
+#define KSTKPTDI (PTDPTDI-1) /* ptd entry for u./kernel&user stack */
+#define KSTKPTEOFF (NBPG/sizeof(pd_entry_t)-UPAGES) /* pte entry for kernel stack */
+
+#define PDESIZE sizeof(pd_entry_t) /* for assembly files */
+#define PTESIZE sizeof(pt_entry_t) /* for assembly files */
+
+/*
+ * Address of current and alternate address space page table maps
+ * and directories.
+ */
+#ifdef KERNEL
+extern pt_entry_t PTmap[], APTmap[], Upte;
+extern pd_entry_t PTD[], APTD[], PTDpde, APTDpde, Upde;
+
+extern int IdlePTD; /* physical address of "Idle" state directory */
+#endif
+
+/*
+ * virtual address to page table entry and
+ * to physical address. Likewise for alternate address space.
+ * Note: these work recursively, thus vtopte of a pte will give
+ * the corresponding pde that in turn maps it.
+ */
+#define vtopte(va) (PTmap + i386_btop(va))
+#define kvtopte(va) vtopte(va)
+#define ptetov(pt) (i386_ptob(pt - PTmap))
+#define vtophys(va) (((int) (*vtopte(va))&PG_FRAME) | ((int)(va) & PGOFSET))
+#define ispt(va) ((va) >= UPT_MIN_ADDRESS && (va) <= KPT_MAX_ADDRESS)
+
+#define avtopte(va) (APTmap + i386_btop(va))
+#define ptetoav(pt) (i386_ptob(pt - APTmap))
+#define avtophys(va) (((int) (*avtopte(va))&PG_FRAME) | ((int)(va) & PGOFSET))
+
+#ifdef KERNEL
+/*
+ * Routine: pmap_kextract
+ * Function:
+ * Extract the physical page address associated
+ * kernel virtual address.
+ */
+static inline vm_offset_t
+pmap_kextract(va)
+ vm_offset_t va;
+{
+ vm_offset_t pa = *(int *)vtopte(va);
+ pa = (pa & PG_FRAME) | (va & ~PG_FRAME);
+ return pa;
+}
+#endif
+
+/*
+ * macros to generate page directory/table indicies
+ */
+
+#define pdei(va) (((va)&PD_MASK)>>PD_SHIFT)
+#define ptei(va) (((va)&PT_MASK)>>PG_SHIFT)
+
+/*
+ * Pmap stuff
+ */
+
+struct pmap {
+ pd_entry_t *pm_pdir; /* KVA of page directory */
+ boolean_t pm_pdchanged; /* pdir changed */
+ short pm_dref; /* page directory ref count */
+ short pm_count; /* pmap reference count */
+ simple_lock_data_t pm_lock; /* lock on pmap */
+ struct pmap_statistics pm_stats; /* pmap statistics */
+ long pm_ptpages; /* more stats: PT pages */
+};
+
+typedef struct pmap *pmap_t;
+
+#ifdef KERNEL
+extern pmap_t kernel_pmap;
+#endif
+
+/*
+ * Macros for speed
+ */
+#define PMAP_ACTIVATE(pmapp, pcbp) \
+ if ((pmapp) != NULL /*&& (pmapp)->pm_pdchanged */) { \
+ (pcbp)->pcb_cr3 = \
+ pmap_extract(kernel_pmap, (vm_offset_t)(pmapp)->pm_pdir); \
+ if ((pmapp) == &curproc->p_vmspace->vm_pmap) \
+ load_cr3((pcbp)->pcb_cr3); \
+ (pmapp)->pm_pdchanged = FALSE; \
+ }
+
+#define PMAP_DEACTIVATE(pmapp, pcbp)
+
+/*
+ * For each vm_page_t, there is a list of all currently valid virtual
+ * mappings of that page. An entry is a pv_entry_t, the list is pv_table.
+ */
+typedef struct pv_entry {
+ struct pv_entry *pv_next; /* next pv_entry */
+ pmap_t pv_pmap; /* pmap where mapping lies */
+ vm_offset_t pv_va; /* virtual address for mapping */
+} *pv_entry_t;
+
+#define PV_ENTRY_NULL ((pv_entry_t) 0)
+
+#define PV_CI 0x01 /* all entries must be cache inhibited */
+#define PV_PTPAGE 0x02 /* entry maps a page table page */
+
+#ifdef KERNEL
+
+pv_entry_t pv_table; /* array of entries, one per page */
+
+#define pa_index(pa) atop(pa - vm_first_phys)
+#define pa_to_pvh(pa) (&pv_table[pa_index(pa)])
+
+#define pmap_resident_count(pmap) ((pmap)->pm_stats.resident_count)
+
+extern pmap_t pmap_create(vm_size_t);
+extern void pmap_pinit(struct pmap *);
+extern void pmap_destroy(pmap_t);
+extern void pmap_release(struct pmap *);
+extern void pmap_reference(pmap_t);
+extern void pmap_remove(struct pmap *, vm_offset_t, vm_offset_t);
+extern void pmap_protect(struct pmap *, vm_offset_t, vm_offset_t, vm_prot_t);
+extern void pmap_enter(pmap_t, vm_offset_t, vm_offset_t, vm_prot_t, boolean_t);
+extern void pmap_change_wiring(pmap_t, vm_offset_t, boolean_t);
+extern inline pt_entry_t *pmap_pte(pmap_t, vm_offset_t);
+extern vm_offset_t pmap_extract(pmap_t, vm_offset_t);
+extern void pmap_copy(pmap_t, pmap_t, vm_offset_t, vm_size_t, vm_offset_t);
+extern void pmap_collect(pmap_t);
+struct pcb; extern void pmap_activate(pmap_t, struct pcb *);
+extern pmap_t pmap_kernel(void);
+extern void pmap_pageable(pmap_t, vm_offset_t, vm_offset_t, boolean_t);
+
+
+#endif /* KERNEL */
+
+#endif /* _PMAP_MACHINE_ */
diff --git a/sys/i386/include/proc.h b/sys/i386/include/proc.h
new file mode 100644
index 0000000..92de3af
--- /dev/null
+++ b/sys/i386/include/proc.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1991 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)proc.h 7.1 (Berkeley) 5/15/91
+ * $Id: proc.h,v 1.2 1993/10/16 14:39:24 rgrimes Exp $
+ */
+
+#ifndef _MACHINE_PROC_H_
+#define _MACHINE_PROC_H_ 1
+
+/*
+ * Machine-dependent part of the proc structure for hp300.
+ */
+struct mdproc {
+ int md_flags; /* machine-dependent flags */
+ int *md_regs; /* registers on current frame */
+};
+
+/* md_flags */
+#define MDP_AST 0x0001 /* async trap pending */
+#endif /* _MACHINE_PROC_H_ */
diff --git a/sys/i386/include/profile.h b/sys/i386/include/profile.h
new file mode 100644
index 0000000..f30efff
--- /dev/null
+++ b/sys/i386/include/profile.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)profile.h 8.1 (Berkeley) 6/11/93
+ */
+
+#define _MCOUNT_DECL static inline void _mcount
+
+#define MCOUNT \
+extern void mcount() asm("mcount"); void mcount() { \
+ int selfpc, frompcindex; \
+ /* \
+ * find the return address for mcount, \
+ * and the return address for mcount's caller. \
+ * \
+ * selfpc = pc pushed by mcount call \
+ */ \
+ asm("movl 4(%%ebp),%0" : "=r" (selfpc)); \
+ /* \
+ * frompcindex = pc pushed by jsr into self. \
+ * In GCC the caller's stack frame has already been built so we \
+ * have to chase a6 to find caller's raddr. \
+ */ \
+ asm("movl (%%ebp),%0" : "=r" (frompcindex)); \
+ frompcindex = ((int *)frompcindex)[1]; \
+ _mcount(frompcindex, selfpc); \
+}
diff --git a/sys/i386/include/psl.h b/sys/i386/include/psl.h
new file mode 100644
index 0000000..997fb23
--- /dev/null
+++ b/sys/i386/include/psl.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)psl.h 5.2 (Berkeley) 1/18/91
+ * $Id: psl.h,v 1.3 1993/11/07 17:43:04 wollman Exp $
+ */
+
+#ifndef _MACHINE_PSL_H_
+#define _MACHINE_PSL_H_ 1
+
+/*
+ * 386 processor status longword.
+ */
+#define PSL_C 0x00000001 /* carry bit */
+#define PSL_PF 0x00000004 /* parity bit */
+#define PSL_AF 0x00000010 /* bcd carry bit */
+#define PSL_Z 0x00000040 /* zero bit */
+#define PSL_N 0x00000080 /* negative bit */
+#define PSL_T 0x00000100 /* trace enable bit */
+#define PSL_I 0x00000200 /* interrupt enable bit */
+#define PSL_D 0x00000400 /* string instruction direction bit */
+#define PSL_V 0x00000800 /* overflow bit */
+#define PSL_IOPL 0x00003000 /* i/o priviledge level enable */
+#define PSL_NT 0x00004000 /* nested task bit */
+#define PSL_RF 0x00010000 /* restart flag bit */
+#define PSL_VM 0x00020000 /* virtual 8086 mode bit */
+
+#define PSL_MBZ 0xffc08028 /* must be zero bits */
+#define PSL_MBO 0x00000002 /* must be one bits */
+
+#define PSL_USERSET (PSL_MBO | PSL_I)
+#define PSL_USERCLR (PSL_MBZ | PSL_NT)
+#endif /* _MACHINE_PSL_H_ */
diff --git a/sys/i386/include/pte.h b/sys/i386/include/pte.h
new file mode 100644
index 0000000..feb74a4
--- /dev/null
+++ b/sys/i386/include/pte.h
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)pte.h 5.5 (Berkeley) 5/9/91
+ * $Id: pte.h,v 1.4 1994/01/31 06:52:41 davidg Exp $
+ */
+
+#ifndef _MACHINE_PTE_H_
+#define _MACHINE_PTE_H_ 1
+
+/*
+ * 386 page table entry and page table directory
+ * W.Jolitz, 8/89
+ *
+ * There are two major kinds of pte's: those which have ever existed (and are
+ * thus either now in core or on the swap device), and those which have
+ * never existed, but which will be filled on demand at first reference.
+ * There is a structure describing each. There is also an ancillary
+ * structure used in page clustering.
+ */
+
+#ifndef LOCORE
+
+struct pde {
+unsigned int
+ pd_v:1, /* valid bit */
+ pd_prot:2, /* access control */
+ pd_ncpwt:1, /* page cache write through */
+ pd_ncpcd:1, /* page cache disable */
+ pd_u:1, /* hardware maintained 'used' bit */
+ pd_m:1, /* not used */
+ pd_mbz2:2, /* reserved, must be zero */
+ :3, /* reserved for software */
+ pd_pfnum:20; /* physical page frame number of pte's*/
+};
+
+struct pte {
+unsigned int
+ pg_v:1, /* valid bit */
+ pg_prot:2, /* access control */
+ pg_ncpwt:1, /* page cache write through */
+ pg_ncpcd:1, /* page cache disable */
+ pg_u:1, /* hardware maintained 'used' bit */
+ pg_m:1, /* hardware maintained modified bit */
+ pg_mbz2:2, /* reserved, must be zero */
+ :3, /* (unused) */
+ pg_pfnum:20; /* physical page frame number */
+};
+#endif
+
+#define PD_MASK 0xffc00000 /* page directory address bits */
+#define PD_SHIFT 22 /* page directory address bits */
+#define PT_MASK 0x003ff000UL /* page table address bits */
+#define PG_SHIFT 12 /* page table address shift */
+
+#define PG_V 0x00000001
+#define PG_RW 0x00000002
+#define PG_u 0x00000004
+#define PG_PROT 0x00000006 /* all protection bits . */
+#define PG_NC_PWT 0x00000008 /* page cache write through */
+#define PG_NC_PCD 0x00000010 /* page cache disable */
+#define PG_N 0x00000018 /* Non-cacheable */
+#define PG_U 0x00000020 /* page was accessed */
+#define PG_M 0x00000040 /* page was modified */
+#define PG_W 0x00000200
+#define PG_FRAME 0xfffff000
+
+#define PG_NOACC 0
+#define PG_KR 0x00000000
+#define PG_KW 0x00000002
+#define PG_URKR 0x00000004
+#define PG_URKW 0x00000004
+#define PG_UW 0x00000006
+
+#define PG_FZERO 0
+#define PG_FTEXT 1
+#define PG_FMAX (PG_FTEXT)
+
+/*
+ * Page Protection Exception bits
+ */
+
+#define PGEX_P 0x01 /* Protection violation vs. not present */
+#define PGEX_W 0x02 /* during a Write cycle */
+#define PGEX_U 0x04 /* access from User mode (UPL) */
+
+/*
+ * Pte related macros
+ */
+#define dirty(pte) ((pte) & PG_M)
+
+#ifndef LOCORE
+#ifdef KERNEL
+/* utilities defined in pmap.c */
+extern struct pte *Sysmap;
+#endif
+#endif
+#endif /* _MACHINE_PTE_H_ */
diff --git a/sys/i386/include/ptrace.h b/sys/i386/include/ptrace.h
new file mode 100644
index 0000000..bfcc55f
--- /dev/null
+++ b/sys/i386/include/ptrace.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ptrace.h 8.1 (Berkeley) 6/11/93
+ */
+
+/*
+ * Machine dependent trace commands.
+ *
+ * None for the i386 at this time.
+ */
diff --git a/sys/i386/include/reg.h b/sys/i386/include/reg.h
new file mode 100644
index 0000000..2a1f061
--- /dev/null
+++ b/sys/i386/include/reg.h
@@ -0,0 +1,106 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)reg.h 5.5 (Berkeley) 1/18/91
+ * $Id: reg.h,v 1.6 1994/01/03 07:55:34 davidg Exp $
+ */
+
+#ifndef _MACHINE_REG_H_
+#define _MACHINE_REG_H_ 1
+
+/*
+ * Location of the users' stored
+ * registers within appropriate frame of 'trap' and 'syscall', relative to
+ * base of stack frame.
+ * Normal usage is u.u_ar0[XX] in kernel.
+ */
+
+/* When referenced during a trap/exception, registers are at these offsets */
+
+#define tES (0)
+#define tDS (1)
+#define tEDI (2)
+#define tESI (3)
+#define tEBP (4)
+#define tISP (5)
+#define tEBX (6)
+#define tEDX (7)
+#define tECX (8)
+#define tEAX (9)
+
+#define tERR (11)
+
+#define tEIP (12)
+#define tCS (13)
+#define tEFLAGS (14)
+#define tESP (15)
+#define tSS (16)
+
+/*
+ * Registers accessible to ptrace(2) syscall for debugger
+ * The machine-dependent code for PT_{SET,GET}REGS needs to
+ * use whichver order, defined above, is correct, so that it
+ * is all invisible to the user.
+ */
+struct reg {
+ unsigned int r_es;
+ unsigned int r_ds;
+ unsigned int r_edi;
+ unsigned int r_esi;
+ unsigned int r_ebp;
+ unsigned int r_isp;
+ unsigned int r_ebx;
+ unsigned int r_edx;
+ unsigned int r_ecx;
+ unsigned int r_eax;
+ unsigned int r_trapno;
+ unsigned int r_err;
+ unsigned int r_eip;
+ unsigned int r_cs;
+ unsigned int r_eflags;
+ unsigned int r_esp;
+ unsigned int r_ss;
+};
+
+/*
+ * Register set accessible via /proc/$pid/fpreg
+ */
+struct fpreg {
+#if 0
+ int fpr_xxx; /* not implemented */
+#endif
+};
+
+#endif /* _MACHINE_REG_H_ */
diff --git a/sys/i386/include/reloc.h b/sys/i386/include/reloc.h
new file mode 100644
index 0000000..386241d
--- /dev/null
+++ b/sys/i386/include/reloc.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)reloc.h 8.1 (Berkeley) 6/10/93
+ */
+
+/* Relocation format. */
+struct relocation_info {
+ int r_address; /* offset in text or data segment */
+ unsigned int r_symbolnum : 24, /* ordinal number of add symbol */
+ r_pcrel : 1, /* 1 if value should be pc-relative */
+ r_length : 2, /* log base 2 of value's width */
+ r_extern : 1, /* 1 if need to add symbol to value */
+ : 4; /* reserved */
+};
diff --git a/sys/i386/include/segments.h b/sys/i386/include/segments.h
new file mode 100644
index 0000000..023a0cf
--- /dev/null
+++ b/sys/i386/include/segments.h
@@ -0,0 +1,235 @@
+/*-
+ * Copyright (c) 1989, 1990 William F. Jolitz
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)segments.h 7.1 (Berkeley) 5/9/91
+ * $Id: segments.h,v 1.3 1993/11/07 17:43:08 wollman Exp $
+ */
+
+#ifndef _MACHINE_SEGMENTS_H_
+#define _MACHINE_SEGMENTS_H_ 1
+
+/*
+ * 386 Segmentation Data Structures and definitions
+ * William F. Jolitz (william@ernie.berkeley.edu) 6/20/1989
+ */
+
+/*
+ * Selectors
+ */
+
+#define ISPL(s) ((s)&3) /* what is the priority level of a selector */
+#define SEL_KPL 0 /* kernel priority level */
+#define SEL_UPL 3 /* user priority level */
+#define ISLDT(s) ((s)&SEL_LDT) /* is it local or global */
+#define SEL_LDT 4 /* local descriptor table */
+#define IDXSEL(s) (((s)>>3) & 0x1fff) /* index of selector */
+#define LSEL(s,r) (((s)<<3) | SEL_LDT | r) /* a local selector */
+#define GSEL(s,r) (((s)<<3) | r) /* a global selector */
+
+/*
+ * Memory and System segment descriptors
+ */
+struct segment_descriptor {
+ unsigned sd_lolimit:16 ; /* segment extent (lsb) */
+ unsigned sd_lobase:24 __attribute__ ((packed));
+ /* segment base address (lsb) */
+ unsigned sd_type:5 ; /* segment type */
+ unsigned sd_dpl:2 ; /* segment descriptor priority level */
+ unsigned sd_p:1 ; /* segment descriptor present */
+ unsigned sd_hilimit:4 ; /* segment extent (msb) */
+ unsigned sd_xx:2 ; /* unused */
+ unsigned sd_def32:1 ; /* default 32 vs 16 bit size */
+ unsigned sd_gran:1 ; /* limit granularity (byte/page units)*/
+ unsigned sd_hibase:8 ; /* segment base address (msb) */
+} ;
+
+/*
+ * Gate descriptors (e.g. indirect descriptors)
+ */
+struct gate_descriptor {
+ unsigned gd_looffset:16 ; /* gate offset (lsb) */
+ unsigned gd_selector:16 ; /* gate segment selector */
+ unsigned gd_stkcpy:5 ; /* number of stack wds to cpy */
+ unsigned gd_xx:3 ; /* unused */
+ unsigned gd_type:5 ; /* segment type */
+ unsigned gd_dpl:2 ; /* segment descriptor priority level */
+ unsigned gd_p:1 ; /* segment descriptor present */
+ unsigned gd_hioffset:16 ; /* gate offset (msb) */
+} ;
+
+/*
+ * Generic descriptor
+ */
+union descriptor {
+ struct segment_descriptor sd;
+ struct gate_descriptor gd;
+};
+
+ /* system segments and gate types */
+#define SDT_SYSNULL 0 /* system null */
+#define SDT_SYS286TSS 1 /* system 286 TSS available */
+#define SDT_SYSLDT 2 /* system local descriptor table */
+#define SDT_SYS286BSY 3 /* system 286 TSS busy */
+#define SDT_SYS286CGT 4 /* system 286 call gate */
+#define SDT_SYSTASKGT 5 /* system task gate */
+#define SDT_SYS286IGT 6 /* system 286 interrupt gate */
+#define SDT_SYS286TGT 7 /* system 286 trap gate */
+#define SDT_SYSNULL2 8 /* system null again */
+#define SDT_SYS386TSS 9 /* system 386 TSS available */
+#define SDT_SYSNULL3 10 /* system null again */
+#define SDT_SYS386BSY 11 /* system 386 TSS busy */
+#define SDT_SYS386CGT 12 /* system 386 call gate */
+#define SDT_SYSNULL4 13 /* system null again */
+#define SDT_SYS386IGT 14 /* system 386 interrupt gate */
+#define SDT_SYS386TGT 15 /* system 386 trap gate */
+
+ /* memory segment types */
+#define SDT_MEMRO 16 /* memory read only */
+#define SDT_MEMROA 17 /* memory read only accessed */
+#define SDT_MEMRW 18 /* memory read write */
+#define SDT_MEMRWA 19 /* memory read write accessed */
+#define SDT_MEMROD 20 /* memory read only expand dwn limit */
+#define SDT_MEMRODA 21 /* memory read only expand dwn limit accessed */
+#define SDT_MEMRWD 22 /* memory read write expand dwn limit */
+#define SDT_MEMRWDA 23 /* memory read write expand dwn limit acessed */
+#define SDT_MEME 24 /* memory execute only */
+#define SDT_MEMEA 25 /* memory execute only accessed */
+#define SDT_MEMER 26 /* memory execute read */
+#define SDT_MEMERA 27 /* memory execute read accessed */
+#define SDT_MEMEC 28 /* memory execute only conforming */
+#define SDT_MEMEAC 29 /* memory execute only accessed conforming */
+#define SDT_MEMERC 30 /* memory execute read conforming */
+#define SDT_MEMERAC 31 /* memory execute read accessed conforming */
+
+/* is memory segment descriptor pointer ? */
+#define ISMEMSDP(s) ((s->d_type) >= SDT_MEMRO && (s->d_type) <= SDT_MEMERAC)
+
+/* is 286 gate descriptor pointer ? */
+#define IS286GDP(s) (((s->d_type) >= SDT_SYS286CGT \
+ && (s->d_type) < SDT_SYS286TGT))
+
+/* is 386 gate descriptor pointer ? */
+#define IS386GDP(s) (((s->d_type) >= SDT_SYS386CGT \
+ && (s->d_type) < SDT_SYS386TGT))
+
+/* is gate descriptor pointer ? */
+#define ISGDP(s) (IS286GDP(s) || IS386GDP(s))
+
+/* is segment descriptor pointer ? */
+#define ISSDP(s) (ISMEMSDP(s) || !ISGDP(s))
+
+/* is system segment descriptor pointer ? */
+#define ISSYSSDP(s) (!ISMEMSDP(s) && !ISGDP(s))
+
+/*
+ * Software definitions are in this convenient format,
+ * which are translated into inconvenient segment descriptors
+ * when needed to be used by the 386 hardware
+ */
+
+struct soft_segment_descriptor {
+ unsigned ssd_base ; /* segment base address */
+ unsigned ssd_limit ; /* segment extent */
+ unsigned ssd_type:5 ; /* segment type */
+ unsigned ssd_dpl:2 ; /* segment descriptor priority level */
+ unsigned ssd_p:1 ; /* segment descriptor present */
+ unsigned ssd_xx:4 ; /* unused */
+ unsigned ssd_xx1:2 ; /* unused */
+ unsigned ssd_def32:1 ; /* default 32 vs 16 bit size */
+ unsigned ssd_gran:1 ; /* limit granularity (byte/page units)*/
+};
+
+extern ssdtosd() ; /* to decode a ssd */
+extern sdtossd() ; /* to encode a sd */
+
+/*
+ * region descriptors, used to load gdt/idt tables before segments yet exist.
+ */
+struct region_descriptor {
+ unsigned rd_limit:16; /* segment extent */
+ unsigned rd_base:32 __attribute__ ((packed)); /* base address */
+};
+
+/*
+ * Segment Protection Exception code bits
+ */
+
+#define SEGEX_EXT 0x01 /* recursive or externally induced */
+#define SEGEX_IDT 0x02 /* interrupt descriptor table */
+#define SEGEX_TI 0x04 /* local descriptor table */
+ /* other bits are affected descriptor index */
+#define SEGEX_IDX(s) ((s)>>3)&0x1fff)
+
+/*
+ * Size of IDT table
+ */
+
+#define NIDT 256
+#define NRSVIDT 32 /* reserved entries for cpu exceptions */
+
+/*
+ * Entries in the Global Descriptor Table (GDT)
+ */
+#define GNULL_SEL 0 /* Null Descriptor */
+#define GCODE_SEL 1 /* Kernel Code Descriptor */
+#define GDATA_SEL 2 /* Kernel Data Descriptor */
+#define GLDT_SEL 3 /* LDT - eventually one per process */
+#define GTGATE_SEL 4 /* Process task switch gate */
+#define GPANIC_SEL 5 /* Task state to consider panic from */
+#define GPROC0_SEL 6 /* Task state process slot zero and up */
+#define GUSERLDT_SEL 7 /* User LDT */
+#define NGDT GUSERLDT_SEL+1
+
+/*
+ * Entries in the Local Descriptor Table (LDT)
+ */
+#define LSYS5CALLS_SEL 0 /* forced by intel BCS */
+#define LSYS5SIGR_SEL 1
+#define L43BSDCALLS_SEL 2 /* notyet */
+#define LUCODE_SEL 3
+#define LUDATA_SEL 4
+/* seperate stack, es,fs,gs sels ? */
+/* #define LPOSIXCALLS_SEL 5*/ /* notyet */
+#define NLDT LUDATA_SEL+1
+
+#ifdef KERNEL
+extern int currentldt;
+extern union descriptor gdt[NGDT];
+extern union descriptor ldt[NLDT];
+extern struct soft_segment_descriptor gdt_segs[];
+#endif
+
+#endif /* _MACHINE_SEGMENTS_H_ */
diff --git a/sys/i386/include/signal.h b/sys/i386/include/signal.h
new file mode 100644
index 0000000..16cbef2
--- /dev/null
+++ b/sys/i386/include/signal.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)signal.h 8.1 (Berkeley) 6/11/93
+ */
+
+/*
+ * Machine-dependent signal definitions
+ */
+
+typedef int sig_atomic_t;
+
+#ifndef _POSIX_SOURCE
+#include <machine/trap.h> /* codes for SIGILL, SIGFPE */
+#endif
+
+/*
+ * Information pushed on stack when a signal is delivered.
+ * This is used by the kernel to restore state following
+ * execution of the signal handler. It is also made available
+ * to the handler to allow it to restore state properly if
+ * a non-standard exit is performed.
+ */
+struct sigcontext {
+ int sc_onstack; /* sigstack state to restore */
+ int sc_mask; /* signal mask to restore */
+ int sc_esp; /* machine state */
+ int sc_ebp;
+ int sc_isp;
+ int sc_eip;
+ int sc_efl;
+ int sc_es;
+ int sc_ds;
+ int sc_cs;
+ int sc_ss;
+ int sc_edi;
+ int sc_esi;
+ int sc_ebx;
+ int sc_edx;
+ int sc_ecx;
+ int sc_eax;
+# define sc_sp sc_esp
+# define sc_fp sc_ebp
+# define sc_pc sc_eip
+# define sc_ps sc_efl
+};
diff --git a/sys/i386/include/soundcard.h b/sys/i386/include/soundcard.h
new file mode 100644
index 0000000..ce28a14
--- /dev/null
+++ b/sys/i386/include/soundcard.h
@@ -0,0 +1,763 @@
+#ifndef _SOUNDCARD_H_
+#define _SOUNDCARD_H_
+/*
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ */
+
+ /*
+ * If you make modifications to this file, please contact me before
+ * distributing the modified version. There is already enough
+ * divercity in the world.
+ *
+ * Regards,
+ * Hannu Savolainen
+ * hannu@voxware.pp.fi, Hannu.Savolainen@helsinki.fi
+ */
+
+#define SOUND_VERSION 205
+#define VOXWARE
+
+#include <sys/ioctl.h>
+
+/*
+ * Supported card ID numbers (Should be somewhere else?)
+ */
+
+#define SNDCARD_ADLIB 1
+#define SNDCARD_SB 2
+#define SNDCARD_PAS 3
+#define SNDCARD_GUS 4
+#define SNDCARD_MPU401 5
+#define SNDCARD_SB16 6
+#define SNDCARD_SB16MIDI 7
+
+/***********************************
+ * IOCTL Commands for /dev/sequencer
+ */
+
+#ifndef _IOWR
+/* @(#)ioctlp.h */
+
+/* Ioctl's have the command encoded in the lower word,
+ * and the size of any in or out parameters in the upper
+ * word. The high 2 bits of the upper word are used
+ * to encode the in/out status of the parameter; for now
+ * we restrict parameters to at most 128 bytes.
+ */
+/* #define IOCTYPE (0xff<<8) */
+#define IOCPARM_MASK 0x7f /* parameters must be < 128 bytes */
+#define IOC_VOID 0x00000000 /* no parameters */
+#define IOC_OUT 0x20000000 /* copy out parameters */
+#define IOC_IN 0x40000000 /* copy in parameters */
+#define IOC_INOUT (IOC_IN|IOC_OUT)
+/* the 0x20000000 is so we can distinguish new ioctl's from old */
+#define _IO(x,y) ((int)(IOC_VOID|(x<<8)|y))
+#define _IOR(x,y,t) ((int)(IOC_OUT|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y))
+#define _IOW(x,y,t) ((int)(IOC_IN|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y))
+/* this should be _IORW, but stdio got there first */
+#define _IOWR(x,y,t) ((int)(IOC_INOUT|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y))
+#endif /* !_IOWR */
+
+#define SNDCTL_SEQ_RESET _IO ('Q', 0)
+#define SNDCTL_SEQ_SYNC _IO ('Q', 1)
+#define SNDCTL_SYNTH_INFO _IOWR('Q', 2, struct synth_info)
+#define SNDCTL_SEQ_CTRLRATE _IOWR('Q', 3, int) /* Set/get timer resolution (HZ) */
+#define SNDCTL_SEQ_GETOUTCOUNT _IOR ('Q', 4, int)
+#define SNDCTL_SEQ_GETINCOUNT _IOR ('Q', 5, int)
+#define SNDCTL_SEQ_PERCMODE _IOW ('Q', 6, int)
+#define SNDCTL_FM_LOAD_INSTR _IOW ('Q', 7, struct sbi_instrument) /* Valid for FM only */
+#define SNDCTL_SEQ_TESTMIDI _IOW ('Q', 8, int)
+#define SNDCTL_SEQ_RESETSAMPLES _IOW ('Q', 9, int)
+#define SNDCTL_SEQ_NRSYNTHS _IOR ('Q',10, int)
+#define SNDCTL_SEQ_NRMIDIS _IOR ('Q',11, int)
+#define SNDCTL_MIDI_INFO _IOWR('Q',12, struct midi_info)
+#define SNDCTL_SEQ_TRESHOLD _IOW ('Q',13, int)
+#define SNDCTL_SYNTH_MEMAVL _IOWR('Q',14, int) /* in=dev#, out=memsize */
+#define SNDCTL_FM_4OP_ENABLE _IOW ('Q',15, int) /* in=dev# */
+#define SNDCTL_PMGR_ACCESS _IOWR('Q',16, struct patmgr_info)
+
+/*
+ * Sample loading mechanism for internal synthesizers (/dev/sequencer)
+ * The following patch_info structure has been designed to support
+ * Gravis UltraSound. It tries to be universal format for uploading
+ * sample based patches but is propably too limited.
+ */
+
+struct patch_info {
+ short key; /* Use GUS_PATCH here */
+#define GUS_PATCH 0x04fd
+#define OBSOLETE_GUS_PATCH 0x02fd
+ short device_no; /* Synthesizer number */
+ short instr_no; /* Midi pgm# */
+
+ unsigned long mode;
+/*
+ * The least significant byte has the same format than the GUS .PAT
+ * files
+ */
+#define WAVE_16_BITS 0x01 /* bit 0 = 8 or 16 bit wave data. */
+#define WAVE_UNSIGNED 0x02 /* bit 1 = Signed - Unsigned data. */
+#define WAVE_LOOPING 0x04 /* bit 2 = looping enabled-1. */
+#define WAVE_BIDIR_LOOP 0x08 /* bit 3 = Set is bidirectional looping. */
+#define WAVE_LOOP_BACK 0x10 /* bit 4 = Set is looping backward. */
+#define WAVE_SUSTAIN_ON 0x20 /* bit 5 = Turn sustaining on. (Env. pts. 3)*/
+#define WAVE_ENVELOPES 0x40 /* bit 6 = Enable envelopes - 1 */
+ /* (use the env_rate/env_offs fields). */
+/* Linux specific bits */
+#define WAVE_VIBRATO 0x00010000 /* The vibrato info is valid */
+#define WAVE_TREMOLO 0x00020000 /* The tremolo info is valid */
+#define WAVE_SCALE 0x00040000 /* The scaling info is valid */
+/* Other bits must be zeroed */
+
+ long len; /* Size of the wave data in bytes */
+ long loop_start, loop_end; /* Byte offsets from the beginning */
+
+/*
+ * The base_freq and base_note fields are used when computing the
+ * playback speed for a note. The base_note defines the tone frequency
+ * which is heard if the sample is played using the base_freq as the
+ * playback speed.
+ *
+ * The low_note and high_note fields define the minimum and maximum note
+ * frequencies for which this sample is valid. It is possible to define
+ * more than one samples for a instrument number at the same time. The
+ * low_note and high_note fields are used to select the most suitable one.
+ *
+ * The fields base_note, high_note and low_note should contain
+ * the note frequency multiplied by 1000. For example value for the
+ * middle A is 440*1000.
+ */
+
+ unsigned int base_freq;
+ unsigned long base_note;
+ unsigned long high_note;
+ unsigned long low_note;
+ int panning; /* -128=left, 127=right */
+ int detuning;
+
+/* New fields introduced in version 1.99.5 */
+
+ /* Envelope. Enabled by mode bit WAVE_ENVELOPES */
+ unsigned char env_rate[ 6 ]; /* GUS HW ramping rate */
+ unsigned char env_offset[ 6 ]; /* 255 == 100% */
+
+ /*
+ * The tremolo, vibrato and scale info are not supported yet.
+ * Enable by setting the mode bits WAVE_TREMOLO, WAVE_VIBRATO or
+ * WAVE_SCALE
+ */
+
+ unsigned char tremolo_sweep;
+ unsigned char tremolo_rate;
+ unsigned char tremolo_depth;
+
+ unsigned char vibrato_sweep;
+ unsigned char vibrato_rate;
+ unsigned char vibrato_depth;
+
+ int scale_frequency;
+ unsigned int scale_factor; /* from 0 to 2048 or 0 to 2 */
+
+ int volume;
+ int spare[4];
+ char data[1]; /* The waveform data starts here */
+ };
+
+
+/*
+ * Patch management interface (/dev/sequencer, /dev/patmgr#)
+ * Don't use these calls if you want to maintain compatibility with
+ * the future versions of the driver.
+ */
+
+#define PS_NO_PATCHES 0 /* No patch support on device */
+#define PS_MGR_NOT_OK 1 /* Plain patch support (no mgr) */
+#define PS_MGR_OK 2 /* Patch manager supported */
+#define PS_MANAGED 3 /* Patch manager running */
+
+#define SNDCTL_PMGR_IFACE _IOWR('P', 1, struct patmgr_info)
+
+/*
+ * The patmgr_info is a fixed size structure which is used for two
+ * different purposes. The intended use is for communication between
+ * the application using /dev/sequencer and the patch manager daemon
+ * associated with a synthesizer device (ioctl(SNDCTL_PMGR_ACCESS)).
+ *
+ * This structure is also used with ioctl(SNDCTL_PGMR_IFACE) which allows
+ * a patch manager daemon to read and write device parameters. This
+ * ioctl available through /dev/sequencer also. Avoid using it since it's
+ * extremely hardware dependent. In addition access trough /dev/sequencer
+ * may confuse the patch manager daemon.
+ */
+
+struct patmgr_info { /* Note! size must be < 4k since kmalloc() is used */
+ unsigned long key; /* Don't worry. Reserved for communication
+ between the patch manager and the driver. */
+#define PM_K_EVENT 1 /* Event from the /dev/sequencer driver */
+#define PM_K_COMMAND 2 /* Request from a application */
+#define PM_K_RESPONSE 3 /* From patmgr to application */
+#define PM_ERROR 4 /* Error returned by the patmgr */
+ int device;
+ int command;
+
+/*
+ * Commands 0x000 to 0xfff reserved for patch manager programs
+ */
+#define PM_GET_DEVTYPE 1 /* Returns type of the patch mgr interface of dev */
+#define PMTYPE_FM2 1 /* 2 OP fm */
+#define PMTYPE_FM4 2 /* Mixed 4 or 2 op FM (OPL-3) */
+#define PMTYPE_WAVE 3 /* Wave table synthesizer (GUS) */
+#define PM_GET_NRPGM 2 /* Returns max # of midi programs in parm1 */
+#define PM_GET_PGMMAP 3 /* Returns map of loaded midi programs in data8 */
+#define PM_GET_PGM_PATCHES 4 /* Return list of patches of a program (parm1) */
+#define PM_GET_PATCH 5 /* Return patch header of patch parm1 */
+#define PM_SET_PATCH 6 /* Set patch header of patch parm1 */
+#define PM_READ_PATCH 7 /* Read patch (wave) data */
+#define PM_WRITE_PATCH 8 /* Write patch (wave) data */
+
+/*
+ * Commands 0x1000 to 0xffff are for communication between the patch manager
+ * and the client
+ */
+#define _PM_LOAD_PATCH 0x100
+
+/*
+ * Commands above 0xffff reserved for device specific use
+ */
+
+ long parm1;
+ long parm2;
+ long parm3;
+
+ union {
+ unsigned char data8[4000];
+ unsigned short data16[2000];
+ unsigned long data32[1000];
+ struct patch_info patch;
+ } data;
+ };
+
+/*
+ * When a patch manager daemon is present, it will be informed by the
+ * driver when something important happens. For example when the
+ * /dev/sequencer is opened or closed. A record with key == PM_K_EVENT is
+ * returned. The command field contains the event type:
+ */
+#define PM_E_OPENED 1 /* /dev/sequencer opened */
+#define PM_E_CLOSED 2 /* /dev/sequencer closed */
+#define PM_E_PATCH_RESET 3 /* SNDCTL_RESETSAMPLES called */
+#define PM_E_PATCH_LOADED 4 /* A patch has been loaded by appl */
+
+/*
+ * /dev/sequencer input events.
+ *
+ * The data written to the /dev/sequencer is a stream of events. Events
+ * are records of 4 or 8 bytes. The first byte defines the size.
+ * Any number of events can be written with a write call. There
+ * is a set of macros for sending these events. Use these macros if you
+ * want to maximize portability of your program.
+ *
+ * Events SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO. Are also input events.
+ * (All input events are currently 4 bytes long. Be prepared to support
+ * 8 byte events also. If you receive any event having first byte >= 0xf0,
+ * it's a 8 byte event.
+ *
+ * The events are documented at the end of this file.
+ *
+ * Normal events (4 bytes)
+ * There is also a 8 byte version of most of the 4 byte events. The
+ * 8 byte one is recommended.
+ */
+#define SEQ_NOTEOFF 0
+#define SEQ_FMNOTEOFF SEQ_NOTEOFF /* Just old name */
+#define SEQ_NOTEON 1
+#define SEQ_FMNOTEON SEQ_NOTEON
+#define SEQ_WAIT 2
+#define SEQ_PGMCHANGE 3
+#define SEQ_FMPGMCHANGE SEQ_PGMCHANGE
+#define SEQ_SYNCTIMER 4
+#define SEQ_MIDIPUTC 5
+#define SEQ_DRUMON 6 /*** OBSOLETE ***/
+#define SEQ_DRUMOFF 7 /*** OBSOLETE ***/
+#define SEQ_ECHO 8 /* For synching programs with output */
+#define SEQ_AFTERTOUCH 9
+#define SEQ_CONTROLLER 10
+#define CTRL_PITCH_BENDER 255
+#define CTRL_PITCH_BENDER_RANGE 254
+#define CTRL_EXPRESSION 253
+#define CTRL_MAIN_VOLUME 252
+#define SEQ_BALANCE 11
+#define SEQ_VOLMODE 12
+
+/*
+ * Volume mode decides how volumes are used
+ */
+
+#define VOL_METHOD_ADAGIO 1
+#define VOL_METHOD_LINEAR 2
+
+/*
+ * Note! SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO are used also as
+ * input events.
+ */
+
+/*
+ * Event codes 0xf0 to 0xfc are reserved for future extensions.
+ */
+
+#define SEQ_FULLSIZE 0xfd /* Long events */
+/*
+ * SEQ_FULLSIZE events are used for loading patches/samples to the
+ * synthesizer devices. These events are passed directly to the driver
+ * of the associated synthesizer device. There is no limit to the size
+ * of the extended events. These events are not queued but executed
+ * immediately when the write() is called (execution can take several
+ * seconds of time).
+ *
+ * When a SEQ_FULLSIZE message is written to the device, it must
+ * be written using exactly one write() call. Other events cannot
+ * be mixed to the same write.
+ *
+ * For FM synths (YM3812/OPL3) use struct sbi_instrument and write it to the
+ * /dev/sequencer. Don't write other data together with the instrument structure
+ * Set the key field of the structure to FM_PATCH. The device field is used to
+ * route the patch to the corresponding device.
+ *
+ * For Gravis UltraSound use struct patch_info. Initialize the key field
+ * to GUS_PATCH.
+ */
+#define SEQ_PRIVATE 0xfe /* Low level HW dependent events (8 bytes) */
+#define SEQ_EXTENDED 0xff /* Extended events (8 bytes) */
+
+/*
+ * Extended events for synthesizers (8 bytes)
+ *
+ * Format:
+ *
+ * b0 = SEQ_EXTENDED
+ * b1 = command
+ * b2 = device
+ * b3-b7 = parameters
+ *
+ * Command b3 b4 b5 b6 b7
+ * ----------------------------------------------------------------------------
+ * SEQ_NOTEON voice note volume 0 0
+ * SEQ_NOTEOFF voice note volume 0 0
+ * SEQ_PGMCHANGE voice pgm 0 0 0
+ * SEQ_DRUMON (voice) drum# volume 0 0
+ * SEQ_DRUMOFF (voice) drum# volume 0 0
+ */
+
+/*
+ * Record for FM patches
+ */
+
+typedef unsigned char sbi_instr_data[32];
+
+struct sbi_instrument {
+ unsigned short key; /* Initialize to FM_PATCH or OPL3_PATCH */
+#define FM_PATCH 0x01fd
+#define OPL3_PATCH 0x03fd
+ short device; /* Synth# (0-4) */
+ int channel; /* Program# to be initialized */
+ sbi_instr_data operators; /* Register settings for operator cells (.SBI format) */
+ };
+
+struct synth_info { /* Read only */
+ char name[30];
+ int device; /* 0-N. INITIALIZE BEFORE CALLING */
+ int synth_type;
+#define SYNTH_TYPE_FM 0
+#define SYNTH_TYPE_SAMPLE 1
+
+ int synth_subtype;
+#define FM_TYPE_ADLIB 0x00
+#define FM_TYPE_OPL3 0x01
+
+#define SAMPLE_TYPE_GUS 0x10
+
+ int perc_mode; /* No longer supported */
+ int nr_voices;
+ int nr_drums; /* Obsolete field */
+ int instr_bank_size;
+ unsigned long capabilities;
+#define SYNTH_CAP_PERCMODE 0x00000001 /* No longer used */
+#define SYNTH_CAP_OPL3 0x00000002 /* Set if OPL3 supported */
+ int dummies[19]; /* Reserve space */
+ };
+
+struct midi_info {
+ char name[30];
+ int device; /* 0-N. INITIALIZE BEFORE CALLING */
+ unsigned long capabilities; /* To be defined later */
+ int dev_type;
+ int dummies[18]; /* Reserve space */
+ };
+
+/********************************************
+ * IOCTL commands for /dev/dsp and /dev/audio
+ */
+
+#define SNDCTL_DSP_RESET _IO ('P', 0)
+#define SNDCTL_DSP_SYNC _IO ('P', 1)
+#define SNDCTL_DSP_SPEED _IOWR('P', 2, int)
+#define SNDCTL_DSP_STEREO _IOWR('P', 3, int)
+#define SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
+#define SNDCTL_DSP_SAMPLESIZE _IOWR('P', 5, int) /* 8, 12 or 16 */
+#define SOUND_PCM_WRITE_CHANNELS _IOWR('P', 6, int)
+#define SOUND_PCM_WRITE_FILTER _IOWR('P', 7, int)
+#define SNDCTL_DSP_POST _IO ('P', 8)
+#define SNDCTL_DSP_SUBDIVIDE _IOWR('P', 9, int)
+
+#define SOUND_PCM_READ_RATE _IOR ('P', 2, int)
+#define SOUND_PCM_READ_CHANNELS _IOR ('P', 6, int)
+#define SOUND_PCM_READ_BITS _IOR ('P', 5, int)
+#define SOUND_PCM_READ_FILTER _IOR ('P', 7, int)
+
+/* Some alias names */
+#define SOUND_PCM_WRITE_BITS SNDCTL_DSP_SAMPLESIZE
+#define SOUND_PCM_WRITE_RATE SNDCTL_DSP_SPEED
+#define SOUND_PCM_POST SNDCTL_DSP_POST
+#define SOUND_PCM_RESET SNDCTL_DSP_RESET
+#define SOUND_PCM_SYNC SNDCTL_DSP_SYNC
+#define SOUND_PCM_SUBDIVIDE SNDCTL_DSP_SUBDIVIDE
+
+/*********************************************
+ * IOCTL commands for /dev/mixer
+ */
+
+/*
+ * Mixer devices
+ *
+ * There can be up to 20 different analog mixer channels. The
+ * SOUND_MIXER_NRDEVICES gives the currently supported maximum.
+ * The SOUND_MIXER_READ_DEVMASK returns a bitmask which tells
+ * the devices supported by the particular mixer.
+ */
+
+#define SOUND_MIXER_NRDEVICES 12
+#define SOUND_MIXER_VOLUME 0
+#define SOUND_MIXER_BASS 1
+#define SOUND_MIXER_TREBLE 2
+#define SOUND_MIXER_SYNTH 3
+#define SOUND_MIXER_PCM 4
+#define SOUND_MIXER_SPEAKER 5
+#define SOUND_MIXER_LINE 6
+#define SOUND_MIXER_MIC 7
+#define SOUND_MIXER_CD 8
+#define SOUND_MIXER_IMIX 9 /* Recording monitor */
+#define SOUND_MIXER_ALTPCM 10
+#define SOUND_MIXER_RECLEV 11 /* Recording level */
+
+/* Some on/off settings (SOUND_SPECIAL_MIN - SOUND_SPECIAL_MAX) */
+/* Not counted to SOUND_MIXER_NRDEVICES, but use the same number space */
+#define SOUND_ONOFF_MIN 28
+#define SOUND_ONOFF_MAX 30
+#define SOUND_MIXER_MUTE 28 /* 0 or 1 */
+#define SOUND_MIXER_ENHANCE 29 /* Enhanced stereo (0, 40, 60 or 80) */
+#define SOUND_MIXER_LOUD 30 /* 0 or 1 */
+
+/* Note! Number 31 cannot be used since the sign bit is reserved */
+
+#define SOUND_DEVICE_LABELS {"Vol ", "Bass ", "Trebl", "Synth", "Pcm ", "Spkr ", "Line ", \
+ "Mic ", "CD ", "Mix ", "Pcm2 ", "rec"}
+
+#define SOUND_DEVICE_NAMES {"vol", "bass", "treble", "synth", "pcm", "speaker", "line", \
+ "mic", "cd", "mix", "pcm2", "rec"}
+
+/* Device bitmask identifiers */
+
+#define SOUND_MIXER_RECSRC 0xff /* Arg contains a bit for each recording source */
+#define SOUND_MIXER_DEVMASK 0xfe /* Arg contains a bit for each supported device */
+#define SOUND_MIXER_RECMASK 0xfd /* Arg contains a bit for each supported recording source */
+#define SOUND_MIXER_CAPS 0xfc
+ #define SOUND_CAP_EXCL_INPUT 0x00000001 /* Only one recording source at a time */
+#define SOUND_MIXER_STEREODEVS 0xfb /* Mixer channels supporting stereo */
+
+/* Device mask bits */
+
+#define SOUND_MASK_VOLUME (1 << SOUND_MIXER_VOLUME)
+#define SOUND_MASK_BASS (1 << SOUND_MIXER_BASS)
+#define SOUND_MASK_TREBLE (1 << SOUND_MIXER_TREBLE)
+#define SOUND_MASK_SYNTH (1 << SOUND_MIXER_SYNTH)
+#define SOUND_MASK_PCM (1 << SOUND_MIXER_PCM)
+#define SOUND_MASK_SPEAKER (1 << SOUND_MIXER_SPEAKER)
+#define SOUND_MASK_LINE (1 << SOUND_MIXER_LINE)
+#define SOUND_MASK_MIC (1 << SOUND_MIXER_MIC)
+#define SOUND_MASK_CD (1 << SOUND_MIXER_CD)
+#define SOUND_MASK_IMIX (1 << SOUND_MIXER_IMIX)
+#define SOUND_MASK_ALTPCM (1 << SOUND_MIXER_ALTPCM)
+#define SOUND_MASK_RECLEV (1 << SOUND_MIXER_RECLEV)
+
+#define SOUND_MASK_MUTE (1 << SOUND_MIXER_MUTE)
+#define SOUND_MASK_ENHANCE (1 << SOUND_MIXER_ENHANCE)
+#define SOUND_MASK_LOUD (1 << SOUND_MIXER_LOUD)
+
+#define MIXER_READ(dev) _IOR('M', dev, int)
+#define SOUND_MIXER_READ_VOLUME MIXER_READ(SOUND_MIXER_VOLUME)
+#define SOUND_MIXER_READ_BASS MIXER_READ(SOUND_MIXER_BASS)
+#define SOUND_MIXER_READ_TREBLE MIXER_READ(SOUND_MIXER_TREBLE)
+#define SOUND_MIXER_READ_SYNTH MIXER_READ(SOUND_MIXER_SYNTH)
+#define SOUND_MIXER_READ_PCM MIXER_READ(SOUND_MIXER_PCM)
+#define SOUND_MIXER_READ_SPEAKER MIXER_READ(SOUND_MIXER_SPEAKER)
+#define SOUND_MIXER_READ_LINE MIXER_READ(SOUND_MIXER_LINE)
+#define SOUND_MIXER_READ_MIC MIXER_READ(SOUND_MIXER_MIC)
+#define SOUND_MIXER_READ_CD MIXER_READ(SOUND_MIXER_CD)
+#define SOUND_MIXER_READ_IMIX MIXER_READ(SOUND_MIXER_IMIX)
+#define SOUND_MIXER_READ_ALTPCM MIXER_READ(SOUND_MIXER_ALTPCM)
+#define SOUND_MIXER_READ_RECLEV MIXER_READ(SOUND_MIXER_RECLEV)
+#define SOUND_MIXER_READ_MUTE MIXER_READ(SOUND_MIXER_MUTE)
+#define SOUND_MIXER_READ_ENHANCE MIXER_READ(SOUND_MIXER_ENHANCE)
+#define SOUND_MIXER_READ_LOUD MIXER_READ(SOUND_MIXER_LOUD)
+
+#define SOUND_MIXER_READ_RECSRC MIXER_READ(SOUND_MIXER_RECSRC)
+#define SOUND_MIXER_READ_DEVMASK MIXER_READ(SOUND_MIXER_DEVMASK)
+#define SOUND_MIXER_READ_RECMASK MIXER_READ(SOUND_MIXER_RECMASK)
+#define SOUND_MIXER_READ_STEREODEVS MIXER_READ(SOUND_MIXER_STEREODEVS)
+#define SOUND_MIXER_READ_CAPS MIXER_READ(SOUND_MIXER_CAPS)
+
+#define MIXER_WRITE(dev) _IOWR('M', dev, int)
+#define SOUND_MIXER_WRITE_VOLUME MIXER_WRITE(SOUND_MIXER_VOLUME)
+#define SOUND_MIXER_WRITE_BASS MIXER_WRITE(SOUND_MIXER_BASS)
+#define SOUND_MIXER_WRITE_TREBLE MIXER_WRITE(SOUND_MIXER_TREBLE)
+#define SOUND_MIXER_WRITE_SYNTH MIXER_WRITE(SOUND_MIXER_SYNTH)
+#define SOUND_MIXER_WRITE_PCM MIXER_WRITE(SOUND_MIXER_PCM)
+#define SOUND_MIXER_WRITE_SPEAKER MIXER_WRITE(SOUND_MIXER_SPEAKER)
+#define SOUND_MIXER_WRITE_LINE MIXER_WRITE(SOUND_MIXER_LINE)
+#define SOUND_MIXER_WRITE_MIC MIXER_WRITE(SOUND_MIXER_MIC)
+#define SOUND_MIXER_WRITE_CD MIXER_WRITE(SOUND_MIXER_CD)
+#define SOUND_MIXER_WRITE_IMIX MIXER_WRITE(SOUND_MIXER_IMIX)
+#define SOUND_MIXER_WRITE_ALTPCM MIXER_WRITE(SOUND_MIXER_ALTPCM)
+#define SOUND_MIXER_WRITE_RECLEV MIXER_WRITE(SOUND_MIXER_RECLEV)
+#define SOUND_MIXER_WRITE_MUTE MIXER_WRITE(SOUND_MIXER_MUTE)
+#define SOUND_MIXER_WRITE_ENHANCE MIXER_WRITE(SOUND_MIXER_ENHANCE)
+#define SOUND_MIXER_WRITE_LOUD MIXER_WRITE(SOUND_MIXER_LOUD)
+
+#define SOUND_MIXER_WRITE_RECSRC MIXER_WRITE(SOUND_MIXER_RECSRC)
+
+/*
+ * The following mixer ioctl calls are compatible with the BSD driver by
+ * Steve Haehnichen <shaehnic@ucsd.edu>
+ *
+ * Since this interface is entirely SB specific, it will be dropped in the
+ * near future.
+ */
+
+typedef unsigned char S_BYTE;
+typedef unsigned char S_FLAG;
+struct stereo_vol
+{
+ S_BYTE l; /* Left volume */
+ S_BYTE r; /* Right volume */
+};
+
+#define MIXER_IOCTL_SET_LEVELS _IOW ('s', 20, struct sb_mixer_levels)
+#define MIXER_IOCTL_SET_PARAMS _IOW ('s', 21, struct sb_mixer_params)
+#define MIXER_IOCTL_READ_LEVELS _IOR ('s', 22, struct sb_mixer_levels)
+#define MIXER_IOCTL_READ_PARAMS _IOR ('s', 23, struct sb_mixer_params)
+#define MIXER_IOCTL_RESET _IO ('s', 24)
+
+/*
+ * Mixer volume levels for MIXER_IOCTL_SET_VOL & MIXER_IOCTL_READ_VOL
+ */
+struct sb_mixer_levels
+{
+ struct stereo_vol master; /* Master volume */
+ struct stereo_vol voc; /* DSP Voice volume */
+ struct stereo_vol fm; /* FM volume */
+ struct stereo_vol line; /* Line-in volume */
+ struct stereo_vol cd; /* CD audio */
+ S_BYTE mic; /* Microphone level */
+};
+
+/*
+ * Mixer parameters for MIXER_IOCTL_SET_PARAMS & MIXER_IOCTL_READ_PARAMS
+ */
+struct sb_mixer_params
+{
+ S_BYTE record_source; /* Recording source (See SRC_xxx below) */
+ S_FLAG hifreq_filter; /* Filter frequency (hi/low) */
+ S_FLAG filter_input; /* ANFI input filter */
+ S_FLAG filter_output; /* DNFI output filter */
+ S_FLAG dsp_stereo; /* 1 if DSP is in Stereo mode */
+};
+
+#define SRC_MIC 1 /* Select Microphone recording source */
+#define SRC_CD 3 /* Select CD recording source */
+#define SRC_LINE 7 /* Use Line-in for recording source */
+
+#if !defined(KERNEL) && !defined(INKERNEL)
+/*
+ * Some convenience macros to simplify programming of the
+ * /dev/sequencer interface
+ *
+ * These macros define the API which should be used when possible.
+ */
+
+void seqbuf_dump(void); /* This function must be provided by programs */
+
+/* Sample seqbuf_dump() implementation:
+ *
+ * SEQ_DEFINEBUF (2048); -- Defines a buffer for 2048 bytes
+ *
+ * int seqfd; -- The file descriptor for /dev/sequencer.
+ *
+ * void
+ * seqbuf_dump ()
+ * {
+ * if (_seqbufptr)
+ * if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ * {
+ * perror ("write /dev/sequencer");
+ * exit (-1);
+ * }
+ * _seqbufptr = 0;
+ * }
+ */
+
+#define SEQ_DEFINEBUF(len) unsigned char _seqbuf[len]; int _seqbuflen = len; int _seqbufptr = 0
+#define SEQ_DECLAREBUF() extern unsigned char _seqbuf[]; extern int _seqbuflen;extern int _seqbufptr
+#define SEQ_PM_DEFINES struct patmgr_info _pm_info
+#define _SEQ_NEEDBUF(len) if ((_seqbufptr+(len)) > _seqbuflen) seqbuf_dump()
+#define _SEQ_ADVBUF(len) _seqbufptr += len
+#define SEQ_DUMPBUF seqbuf_dump
+#define PM_LOAD_PATCH(dev, bank, pgm) (SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \
+ _pm_info.device=dev, _pm_info.data.data8[0]=pgm, \
+ _pm_info.parm1 = bank, _pm_info.parm2 = 1, \
+ ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info))
+#define PM_LOAD_PATCHES(dev, bank, pgm) (SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \
+ _pm_info.device=dev, memcpy(_pm_info.data.data8, pgm, 128), \
+ _pm_info.parm1 = bank, _pm_info.parm2 = 128, \
+ ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info))
+
+#define SEQ_VOLUME_MODE(dev, mode) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_VOLMODE;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (mode);\
+ _seqbuf[_seqbufptr+4] = 0;\
+ _seqbuf[_seqbufptr+5] = 0;\
+ _seqbuf[_seqbufptr+6] = 0;\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_START_NOTE(dev, voice, note, vol) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_NOTEON;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (voice);\
+ _seqbuf[_seqbufptr+4] = (note);\
+ _seqbuf[_seqbufptr+5] = (vol);\
+ _seqbuf[_seqbufptr+6] = 0;\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_STOP_NOTE(dev, voice, note, vol) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_NOTEOFF;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (voice);\
+ _seqbuf[_seqbufptr+4] = (note);\
+ _seqbuf[_seqbufptr+5] = (vol);\
+ _seqbuf[_seqbufptr+6] = 0;\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_CHN_PRESSURE(dev, voice, pressure) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_AFTERTOUCH;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (voice);\
+ _seqbuf[_seqbufptr+4] = (pressure);\
+ _seqbuf[_seqbufptr+5] = 0;\
+ _seqbuf[_seqbufptr+6] = 0;\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_PANNING(dev, voice, pos) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_BALANCE;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (voice);\
+ (char)_seqbuf[_seqbufptr+4] = (pos);\
+ _seqbuf[_seqbufptr+5] = 0;\
+ _seqbuf[_seqbufptr+6] = 0;\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_CONTROL(dev, voice, controller, value) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_CONTROLLER;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (voice);\
+ _seqbuf[_seqbufptr+4] = (controller);\
+ *(short *)&_seqbuf[_seqbufptr+5] = (value);\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_PITCHBEND(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_PITCH_BENDER, value)
+#define SEQ_BENDER_RANGE(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_PITCH_BENDER_RANGE, value)
+#define SEQ_EXPRESSION(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_EXPRESSION, value)
+#define SEQ_MAIN_VOLUME(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_MAIN_VOLUME, value)
+
+#define SEQ_START_TIMER() {_SEQ_NEEDBUF(4);\
+ _seqbuf[_seqbufptr] = SEQ_SYNCTIMER;\
+ _seqbuf[_seqbufptr+1] = 0;\
+ _seqbuf[_seqbufptr+2] = 0;\
+ _seqbuf[_seqbufptr+3] = 0;\
+ _SEQ_ADVBUF(4);}
+#define SEQ_SET_PATCH(dev, voice, patch) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_PGMCHANGE;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (voice);\
+ _seqbuf[_seqbufptr+4] = (patch);\
+ _seqbuf[_seqbufptr+5] = 0;\
+ _seqbuf[_seqbufptr+6] = 0;\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_WAIT_TIME(ticks) {_SEQ_NEEDBUF(4);\
+ *(unsigned long *)&_seqbuf[_seqbufptr] = SEQ_WAIT | ((ticks) << 8);\
+ _SEQ_ADVBUF(4);}
+
+#define SEQ_ECHO_BACK(key) {_SEQ_NEEDBUF(4);\
+ *(unsigned long *)&_seqbuf[_seqbufptr] = SEQ_ECHO | ((key) << 8);\
+ _SEQ_ADVBUF(4);}
+
+#define SEQ_MIDIOUT(device, byte) {_SEQ_NEEDBUF(4);\
+ _seqbuf[_seqbufptr] = SEQ_MIDIPUTC;\
+ _seqbuf[_seqbufptr+1] = (byte);\
+ _seqbuf[_seqbufptr+2] = (device);\
+ _seqbuf[_seqbufptr+3] = 0;\
+ _SEQ_ADVBUF(4);}
+#define SEQ_WRPATCH(patchx, len) {if (_seqbufptr) seqbuf_dump();\
+ if (write(seqfd, (char*)(patchx), len)==-1) \
+ perror("Write patch: /dev/sequencer");}
+
+#endif
+long soundcard_init(long mem_start);
+#endif /* _SOUNDCARD_H_ */
diff --git a/sys/i386/include/speaker.h b/sys/i386/include/speaker.h
new file mode 100644
index 0000000..af80a28
--- /dev/null
+++ b/sys/i386/include/speaker.h
@@ -0,0 +1,30 @@
+/*
+ * speaker.h -- interface definitions for speaker ioctl()
+ *
+ * v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993
+ * modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su>
+ */
+
+#ifndef _SPEAKER_H_
+#define _SPEAKER_H_
+
+#include <sys/ioctl.h>
+
+#define SPKRTONE _IOW('S', 1, tone_t) /* emit tone */
+#define SPKRTUNE _IO('S', 2) /* emit tone sequence*/
+
+typedef struct
+{
+ int frequency; /* in hertz */
+ int duration; /* in 1/100ths of a second */
+}
+tone_t;
+
+/*
+ * Strings written to the speaker device are interpreted as tunes and played;
+ * see the spkr(4) man page for details.
+ */
+
+#endif /* _SPEAKER_H_ */
+
+/* speaker.h ends here */
diff --git a/sys/i386/include/specialreg.h b/sys/i386/include/specialreg.h
new file mode 100644
index 0000000..935b1ed
--- /dev/null
+++ b/sys/i386/include/specialreg.h
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)specialreg.h 7.1 (Berkeley) 5/9/91
+ * $Id: specialreg.h,v 1.2 1993/10/16 14:39:32 rgrimes Exp $
+ */
+
+#ifndef _MACHINE_SPECIALREG_H_
+#define _MACHINE_SPECIALREG_H_ 1
+
+/*
+ * Bits in 386 special registers:
+ */
+
+#define CR0_PE 0x00000001 /* Protected mode Enable */
+#define CR0_MP 0x00000002 /* "Math" Present (NPX or NPX emulator) */
+#ifdef notused
+#define CR0_EM 0x00000004 /* EMulate non-NPX coproc. (trap ESC only) */
+#endif
+#define CR0_TS 0x00000008 /* Task Switched (if MP, trap ESC and WAIT) */
+#ifdef notused
+#define CR0_ET 0x00000010 /* Extension Type (387 (if set) vs 287) */
+#endif
+#define CR0_PG 0x80000000 /* PaGing enable */
+
+/*
+ * Bits in 486 special registers:
+ */
+
+#define CR0_NE 0x00000020 /* Numeric Error enable (EX16 vs IRQ13) */
+#define CR0_WP 0x00010000 /* Write Protect (honor ~PG_W in all modes) */
+#ifdef notyet
+#define CR0_AM 0x00040000 /* Alignment Mask (set to enable AC flag) */
+#endif
+#endif /* _MACHINE_SPECIALREG_H_ */
diff --git a/sys/i386/include/spl.h b/sys/i386/include/spl.h
new file mode 100644
index 0000000..c916df3
--- /dev/null
+++ b/sys/i386/include/spl.h
@@ -0,0 +1,107 @@
+#ifndef _MACHINE_IPL_H_
+#define _MACHINE_IPL_H_
+
+#include <machine/ipl.h> /* XXX "machine" means cpu for i386 */
+
+/*
+ * Software interrupt bit numbers in priority order. The priority only
+ * determines which swi will be dispatched next; a higher priority swi
+ * may be dispatched when a nested h/w interrupt handler returns.
+ */
+#define SWI_TTY (NHWI + 0)
+#define SWI_NET (NHWI + 1)
+#define SWI_CLOCK 30
+#define SWI_AST 31
+
+/*
+ * Corresponding interrupt-pending bits for ipending.
+ */
+#define SWI_TTY_PENDING (1 << SWI_TTY)
+#define SWI_NET_PENDING (1 << SWI_NET)
+#define SWI_CLOCK_PENDING (1 << SWI_CLOCK)
+#define SWI_AST_PENDING (1 << SWI_AST)
+
+/*
+ * Corresponding interrupt-disable masks for cpl. The ordering is now by
+ * inclusion (where each mask is considered as a set of bits). Everything
+ * except SWI_AST_MASK includes SWI_CLOCK_MASK so that softclock() doesn't
+ * run while other swi handlers are running and timeout routines can call
+ * swi handlers. Everything includes SWI_AST_MASK so that AST's are masked
+ * until just before return to user mode.
+ */
+#define SWI_TTY_MASK (SWI_TTY_PENDING | SWI_CLOCK_MASK)
+#define SWI_NET_MASK (SWI_NET_PENDING | SWI_CLOCK_MASK)
+#define SWI_CLOCK_MASK (SWI_CLOCK_PENDING | SWI_AST_MASK)
+#define SWI_AST_MASK SWI_AST_PENDING
+#define SWI_MASK (~HWI_MASK)
+
+#ifndef LOCORE
+
+extern unsigned bio_imask; /* group of interrupts masked with splbio() */
+extern unsigned cpl; /* current priority level mask */
+extern unsigned high_imask; /* group of interrupts masked with splhigh() */
+extern unsigned net_imask; /* group of interrupts masked with splimp() */
+extern volatile unsigned ipending; /* active interrupts masked by cpl */
+extern volatile unsigned netisr;
+extern unsigned tty_imask; /* group of interrupts masked with spltty() */
+
+/*
+ * ipending has to be volatile so that it is read every time it is accessed
+ * in splx() and spl0(), but we don't want it to be read nonatomically when
+ * it is changed. Pretending that ipending is a plain int happens to give
+ * suitable atomic code for "ipending |= constant;".
+ */
+#define setsoftast() (*(unsigned *)&ipending |= SWI_AST_PENDING)
+#define setsoftclock() (*(unsigned *)&ipending |= SWI_CLOCK_PENDING)
+#define setsoftnet() (*(unsigned *)&ipending |= SWI_NET_PENDING)
+#define setsofttty() (*(unsigned *)&ipending |= SWI_TTY_PENDING)
+
+void unpend_V __P((void));
+
+#ifdef __GNUC__
+
+void splz __P((void));
+
+#define GENSPL(name, set_cpl) \
+static __inline int name(void) \
+{ \
+ unsigned x; \
+ \
+ x = cpl; \
+ set_cpl; \
+ return (x); \
+}
+
+GENSPL(splbio, cpl |= bio_imask)
+GENSPL(splclock, cpl = HWI_MASK | SWI_MASK)
+GENSPL(splhigh, cpl = HWI_MASK | SWI_MASK)
+GENSPL(splstatclock, cpl = HWI_MASK | SWI_MASK)
+GENSPL(splimp, cpl |= net_imask)
+GENSPL(splnet, cpl |= SWI_NET_MASK)
+GENSPL(splsoftclock, cpl = SWI_CLOCK_MASK)
+GENSPL(splsofttty, cpl |= SWI_TTY_MASK)
+GENSPL(spltty, cpl |= tty_imask)
+
+#define splnone() spl0()
+
+static __inline void
+spl0(void)
+{
+ cpl = SWI_AST_MASK;
+ if (ipending & ~SWI_AST_MASK)
+ splz();
+}
+
+static __inline void
+splx(int ipl)
+{
+ cpl = ipl;
+ if (ipending & ~ipl)
+ splz();
+}
+
+#endif /* __GNUC__ */
+
+#endif /* LOCORE */
+
+#endif /* _MACHINE_IPL_H_ */
diff --git a/sys/i386/include/stdarg.h b/sys/i386/include/stdarg.h
new file mode 100644
index 0000000..1110b66
--- /dev/null
+++ b/sys/i386/include/stdarg.h
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)stdarg.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef _STDARG_H_
+#define _STDARG_H_
+
+typedef char *va_list;
+
+#define __va_promote(type) \
+ (((sizeof(type) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))
+
+#define va_start(ap, last) \
+ (ap = ((char *)&(last) + __va_promote(last)))
+
+#ifdef KERNEL
+#define va_arg(ap, type) \
+ ((type *)(ap += sizeof(type)))[-1]
+#else
+#define va_arg(ap, type) \
+ ((type *)(ap += sizeof(type) < sizeof(int) ? \
+ (abort(), 0) : sizeof(type)))[-1]
+#endif
+
+#define va_end(ap)
+
+#endif /* !_STDARG_H_ */
diff --git a/sys/i386/include/sysarch.h b/sys/i386/include/sysarch.h
new file mode 100644
index 0000000..2649ba5
--- /dev/null
+++ b/sys/i386/include/sysarch.h
@@ -0,0 +1,24 @@
+/*
+ * Architecture specific syscalls (i386)
+ *
+ * $Id: sysarch.h,v 1.2 1993/10/16 14:39:35 rgrimes Exp $
+ */
+#ifndef _MACHINE_SYSARCH_H_
+#define _MACHINE_SYSARCH_H_ 1
+
+#include <sys/cdefs.h>
+
+#define I386_GET_LDT 0
+#define I386_SET_LDT 1
+
+#ifdef KERNEL
+/* nothing here yet... */
+#else /* not KERNEL */
+__BEGIN_DECLS
+
+int i386_get_ldt __P((int, union descriptor *, int));
+int i386_set_ldt __P((int, union descriptor *, int));
+
+__END_DECLS
+#endif /* not KERNEL */
+#endif /* _MACHINE_SYSARCH_H_ */
diff --git a/sys/i386/include/trap.h b/sys/i386/include/trap.h
new file mode 100644
index 0000000..aa832ff
--- /dev/null
+++ b/sys/i386/include/trap.h
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)trap.h 5.4 (Berkeley) 5/9/91
+ * $Id: trap.h,v 1.2 1993/10/16 14:39:37 rgrimes Exp $
+ */
+
+#ifndef _MACHINE_TRAP_H_
+#define _MACHINE_TRAP_H_ 1
+
+/*
+ * Trap type values
+ * also known in trap.c for name strings
+ */
+
+#define T_RESADFLT 0 /* reserved addressing */
+#define T_PRIVINFLT 1 /* privileged instruction */
+#define T_RESOPFLT 2 /* reserved operand */
+#define T_BPTFLT 3 /* breakpoint instruction */
+#define T_SYSCALL 5 /* system call (kcall) */
+#define T_ARITHTRAP 6 /* arithmetic trap */
+#define T_ASTFLT 7 /* system forced exception */
+#define T_SEGFLT 8 /* segmentation (limit) fault */
+#define T_PROTFLT 9 /* protection fault */
+#define T_TRCTRAP 10 /* trace trap */
+#define T_PAGEFLT 12 /* page fault */
+#define T_TABLEFLT 13 /* page table fault */
+#define T_ALIGNFLT 14 /* alignment fault */
+#define T_KSPNOTVAL 15 /* kernel stack pointer not valid */
+#define T_BUSERR 16 /* bus error */
+#define T_KDBTRAP 17 /* kernel debugger trap */
+
+#define T_DIVIDE 18 /* integer divide fault */
+#define T_NMI 19 /* non-maskable trap */
+#define T_OFLOW 20 /* overflow trap */
+#define T_BOUND 21 /* bound instruction fault */
+#define T_DNA 22 /* device not available fault */
+#define T_DOUBLEFLT 23 /* double fault */
+#define T_FPOPFLT 24 /* fp coprocessor operand fetch fault */
+#define T_TSSFLT 25 /* invalid tss fault */
+#define T_SEGNPFLT 26 /* segment not present fault */
+#define T_STKFLT 27 /* stack fault */
+#define T_RESERVED 28 /* reserved fault base */
+
+/* definitions for <sys/signal.h> */
+#define ILL_RESAD_FAULT T_RESADFLT
+#define ILL_PRIVIN_FAULT T_PRIVINFLT
+#define ILL_RESOP_FAULT T_RESOPFLT
+#define ILL_ALIGN_FAULT T_ALIGNFLT
+#define ILL_FPOP_FAULT T_FPOPFLT /* coprocessor operand fault */
+
+/* codes for SIGFPE/ARITHTRAP */
+#define FPE_INTOVF_TRAP 0x1 /* integer overflow */
+#define FPE_INTDIV_TRAP 0x2 /* integer divide by zero */
+#define FPE_FLTDIV_TRAP 0x3 /* floating/decimal divide by zero */
+#define FPE_FLTOVF_TRAP 0x4 /* floating overflow */
+#define FPE_FLTUND_TRAP 0x5 /* floating underflow */
+#define FPE_FPU_NP_TRAP 0x6 /* floating point unit not present */
+#define FPE_SUBRNG_TRAP 0x7 /* subrange out of bounds */
+
+/* codes for SIGBUS */
+#define BUS_PAGE_FAULT T_PAGEFLT /* page fault protection base */
+#define BUS_SEGNP_FAULT T_SEGNPFLT /* segment not present */
+#define BUS_STK_FAULT T_STKFLT /* stack segment */
+#define BUS_SEGM_FAULT T_RESERVED /* segment protection base */
+
+/* Trap's coming from user mode */
+#define T_USER 0x100
+#endif /* _MACHINE_TRAP_H_ */
diff --git a/sys/i386/include/tss.h b/sys/i386/include/tss.h
new file mode 100644
index 0000000..1fada6e
--- /dev/null
+++ b/sys/i386/include/tss.h
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)tss.h 5.4 (Berkeley) 1/18/91
+ * $Id: tss.h,v 1.3 1993/11/07 17:43:16 wollman Exp $
+ */
+
+#ifndef _MACHINE_TSS_H_
+#define _MACHINE_TSS_H_ 1
+
+/*
+ * Intel 386 Context Data Type
+ */
+
+struct i386tss {
+ int tss_link; /* actually 16 bits: top 16 bits must be zero */
+ int tss_esp0; /* kernel stack pointer priviledge level 0 */
+#define tss_ksp tss_esp0
+ int tss_ss0; /* actually 16 bits: top 16 bits must be zero */
+ int tss_esp1; /* kernel stack pointer priviledge level 1 */
+ int tss_ss1; /* actually 16 bits: top 16 bits must be zero */
+ int tss_esp2; /* kernel stack pointer priviledge level 2 */
+ int tss_ss2; /* actually 16 bits: top 16 bits must be zero */
+ int tss_cr3; /* page table directory */
+#define tss_ptd tss_cr3
+ int tss_eip; /* program counter */
+#define tss_pc tss_eip
+ int tss_eflags; /* program status longword */
+#define tss_psl tss_eflags
+ int tss_eax;
+ int tss_ecx;
+ int tss_edx;
+ int tss_ebx;
+ int tss_esp; /* user stack pointer */
+#define tss_usp tss_esp
+ int tss_ebp; /* user frame pointer */
+#define tss_fp tss_ebp
+ int tss_esi;
+ int tss_edi;
+ int tss_es; /* actually 16 bits: top 16 bits must be zero */
+ int tss_cs; /* actually 16 bits: top 16 bits must be zero */
+ int tss_ss; /* actually 16 bits: top 16 bits must be zero */
+ int tss_ds; /* actually 16 bits: top 16 bits must be zero */
+ int tss_fs; /* actually 16 bits: top 16 bits must be zero */
+ int tss_gs; /* actually 16 bits: top 16 bits must be zero */
+ int tss_ldt; /* actually 16 bits: top 16 bits must be zero */
+ int tss_ioopt; /* options & io offset bitmap: currently zero */
+ /* XXX unimplemented .. i/o permission bitmap */
+};
+#endif /* _MACHINE_TSS_H_ */
diff --git a/sys/i386/include/types.h b/sys/i386/include/types.h
new file mode 100644
index 0000000..dcbb29c
--- /dev/null
+++ b/sys/i386/include/types.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)types.h 8.3 (Berkeley) 1/5/94
+ */
+
+#ifndef _MACHTYPES_H_
+#define _MACHTYPES_H_
+
+#if !defined(_ANSI_SOURCE) && !defined(_POSIX_SOURCE)
+typedef struct _physadr {
+ int r[1];
+} *physadr;
+
+typedef struct label_t {
+ int val[6];
+} label_t;
+#endif
+
+typedef unsigned long vm_offset_t;
+typedef unsigned long vm_size_t;
+
+/*
+ * Basic integral types. Omit the typedef if
+ * not possible for a machine/compiler combination.
+ */
+typedef __signed char int8_t;
+typedef unsigned char u_int8_t;
+typedef short int16_t;
+typedef unsigned short u_int16_t;
+typedef int int32_t;
+typedef unsigned int u_int32_t;
+typedef long long int64_t;
+typedef unsigned long long u_int64_t;
+
+#endif /* _MACHTYPES_H_ */
diff --git a/sys/i386/include/ultrasound.h b/sys/i386/include/ultrasound.h
new file mode 100644
index 0000000..40e2443
--- /dev/null
+++ b/sys/i386/include/ultrasound.h
@@ -0,0 +1,121 @@
+#ifndef _ULTRASOUND_H_
+#define _ULTRASOUND_H_
+/*
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * ultrasound.h - Macros for programming the Gravis Ultrasound
+ * These macros are extremely device dependent
+ * and not portable.
+ */
+
+/*
+ * Private events for Gravis Ultrasound (GUS)
+ *
+ * Format:
+ * byte 0 - SEQ_PRIVATE (0xfe)
+ * byte 1 - Synthesizer device number (0-N)
+ * byte 2 - Command (see below)
+ * byte 3 - Voice number (0-31)
+ * bytes 4 and 5 - parameter P1 (unsigned short)
+ * bytes 6 and 7 - parameter P2 (unsigned short)
+ *
+ * Commands:
+ * Each command affects one voice defined in byte 3.
+ * Unused parameters (P1 and/or P2 *MUST* be initialized to zero).
+ * _GUS_NUMVOICES - Sets max. number of concurrent voices (P1=14-31, default 16)
+ * _GUS_VOICESAMPLE- ************ OBSOLETE *************
+ * _GUS_VOICEON - Starts voice (P1=voice mode)
+ * _GUS_VOICEOFF - Stops voice (no parameters)
+ * _GUS_VOICEFADE - Stops the voice smoothly.
+ * _GUS_VOICEMODE - Alters the voice mode, don't start or stop voice (P1=voice mode)
+ * _GUS_VOICEBALA - Sets voice balence (P1, 0=left, 7=middle and 15=right, default 7)
+ * _GUS_VOICEFREQ - Sets voice (sample) playback frequency (P1=Hz)
+ * _GUS_VOICEVOL - Sets voice volume (P1=volume, 0xfff=max, 0xeff=half, 0x000=off)
+ * _GUS_VOICEVOL2 - Sets voice volume (P1=volume, 0xfff=max, 0xeff=half, 0x000=off)
+ * (Like GUS_VOICEVOL but doesn't change the hw
+ * volume. It just updates volume in the voice table).
+ *
+ * _GUS_RAMPRANGE - Sets limits for volume ramping (P1=low volume, P2=high volume)
+ * _GUS_RAMPRATE - Sets the speed for volume ramping (P1=scale, P2=rate)
+ * _GUS_RAMPMODE - Sets the volume ramping mode (P1=ramping mode)
+ * _GUS_RAMPON - Starts volume ramping (no parameters)
+ * _GUS_RAMPOFF - Stops volume ramping (no parameters)
+ * _GUS_VOLUME_SCALE - Changes the volume calculation constants
+ * for all voices.
+ */
+
+#define _GUS_NUMVOICES 0x00
+#define _GUS_VOICESAMPLE 0x01 /* OBSOLETE */
+#define _GUS_VOICEON 0x02
+#define _GUS_VOICEOFF 0x03
+#define _GUS_VOICEMODE 0x04
+#define _GUS_VOICEBALA 0x05
+#define _GUS_VOICEFREQ 0x06
+#define _GUS_VOICEVOL 0x07
+#define _GUS_RAMPRANGE 0x08
+#define _GUS_RAMPRATE 0x09
+#define _GUS_RAMPMODE 0x0a
+#define _GUS_RAMPON 0x0b
+#define _GUS_RAMPOFF 0x0c
+#define _GUS_VOICEFADE 0x0d
+#define _GUS_VOLUME_SCALE 0x0e
+#define _GUS_VOICEVOL2 0x0f
+#define _GUS_VOICE_POS 0x10
+
+/*
+ * GUS API macros
+ */
+
+#define _GUS_CMD(chn, voice, cmd, p1, p2) \
+ {_SEQ_NEEDBUF(8); _seqbuf[_seqbufptr] = SEQ_PRIVATE;\
+ _seqbuf[_seqbufptr+1] = (chn); _seqbuf[_seqbufptr+2] = cmd;\
+ _seqbuf[_seqbufptr+3] = voice;\
+ *(unsigned short*)&_seqbuf[_seqbufptr+4] = p1;\
+ *(unsigned short*)&_seqbuf[_seqbufptr+6] = p2;\
+ _SEQ_ADVBUF(8);}
+
+#define GUS_NUMVOICES(chn, p1) _GUS_CMD(chn, 0, _GUS_NUMVOICES, (p1), 0)
+#define GUS_VOICESAMPLE(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICESAMPLE, (p1), 0) /* OBSOLETE */
+#define GUS_VOICEON(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICEON, (p1), 0)
+#define GUS_VOICEOFF(chn, voice) _GUS_CMD(chn, voice, _GUS_VOICEOFF, 0, 0)
+#define GUS_VOICEFADE(chn, voice) _GUS_CMD(chn, voice, _GUS_VOICEFADE, 0, 0)
+#define GUS_VOICEMODE(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICEMODE, (p1), 0)
+#define GUS_VOICEBALA(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICEBALA, (p1), 0)
+#define GUS_VOICEFREQ(chn, voice, p) _GUS_CMD(chn, voice, _GUS_VOICEFREQ, \
+ (p) & 0xffff, ((p) >> 16) & 0xffff)
+#define GUS_VOICEVOL(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICEVOL, (p1), 0)
+#define GUS_VOICEVOL2(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICEVOL2, (p1), 0)
+#define GUS_RAMPRANGE(chn, voice, low, high) _GUS_CMD(chn, voice, _GUS_RAMPRANGE, (low), (high))
+#define GUS_RAMPRATE(chn, voice, p1, p2) _GUS_CMD(chn, voice, _GUS_RAMPRATE, (p1), (p2))
+#define GUS_RAMPMODE(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_RAMPMODE, (p1), 0)
+#define GUS_RAMPON(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_RAMPON, (p1), 0)
+#define GUS_RAMPOFF(chn, voice) _GUS_CMD(chn, voice, _GUS_RAMPOFF, 0, 0)
+#define GUS_VOLUME_SCALE(chn, voice, p1, p2) _GUS_CMD(chn, voice, _GUS_VOLUME_SCALE, (p1), (p2))
+#define GUS_VOICE_POS(chn, voice, p) _GUS_CMD(chn, voice, _GUS_VOICE_POS, \
+ (p) & 0xffff, ((p) >> 16) & 0xffff)
+
+#endif
diff --git a/sys/i386/include/varargs.h b/sys/i386/include/varargs.h
new file mode 100644
index 0000000..1a913ec
--- /dev/null
+++ b/sys/i386/include/varargs.h
@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)varargs.h 8.2 (Berkeley) 3/22/94
+ */
+
+#ifndef _VARARGS_H_
+#define _VARARGS_H_
+
+typedef char *va_list;
+
+#define va_dcl int va_alist;
+
+#define va_start(ap) \
+ ap = (char *)&va_alist
+
+#ifdef KERNEL
+#define va_arg(ap, type) \
+ ((type *)(ap += sizeof(type)))[-1]
+#else
+#define va_arg(ap, type) \
+ ((type *)(ap += sizeof(type) < sizeof(int) ? \
+ (abort(), 0) : sizeof(type)))[-1]
+#endif
+
+#define va_end(ap)
+
+#endif /* !_VARARGS_H_ */
diff --git a/sys/i386/include/vmparam.h b/sys/i386/include/vmparam.h
new file mode 100644
index 0000000..05218ad
--- /dev/null
+++ b/sys/i386/include/vmparam.h
@@ -0,0 +1,263 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ * Copyright (c) 1994 John S. Dyson
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)vmparam.h 5.9 (Berkeley) 5/12/91
+ * $Id: vmparam.h,v 1.11 1994/01/14 16:24:00 davidg Exp $
+ */
+
+
+#ifndef _MACHINE_VMPARAM_H_
+#define _MACHINE_VMPARAM_H_ 1
+
+/*
+ * Machine dependent constants for 386.
+ */
+
+/*
+ * Virtual address space arrangement. On 386, both user and kernel
+ * share the address space, not unlike the vax.
+ * USRTEXT is the start of the user text/data space, while USRSTACK
+ * is the top (end) of the user stack. Immediately above the user stack
+ * resides the user structure, which is UPAGES long and contains the
+ * kernel stack.
+ *
+ * Immediately after the user structure is the page table map, and then
+ * kernal address space.
+ */
+#define USRTEXT 0UL
+/* #define USRSTACK 0xFDBFE000UL */
+#define BTOPUSRSTACK (0xFDC00-(UPAGES)) /* btop(USRSTACK) */
+#define LOWPAGES 0UL
+#define HIGHPAGES UPAGES
+
+/*
+ * Virtual memory related constants, all in bytes
+ */
+#define MAXTSIZ (16UL*1024*1024) /* max text size */
+#ifndef DFLDSIZ
+#define DFLDSIZ (64UL*1024*1024) /* initial data size limit */
+#endif
+#ifndef MAXDSIZ
+#define MAXDSIZ (128UL*1024*1024) /* max data size */
+#endif
+#ifndef DFLSSIZ
+#define DFLSSIZ (8UL*1024*1024) /* initial stack size limit */
+#endif
+#ifndef MAXSSIZ
+#define MAXSSIZ (64UL*1024*1024) /* max stack size */
+#endif
+#ifndef SGROWSIZ
+#define SGROWSIZ (128UL*1024) /* amount to grow stack */
+#endif
+
+/*
+ * Default sizes of swap allocation chunks (see dmap.h).
+ * The actual values may be changed in vminit() based on MAXDSIZ.
+ * With MAXDSIZ of 16Mb and NDMAP of 38, dmmax will be 1024.
+ */
+#define DMMIN 32 /* smallest swap allocation */
+#define DMMAX 4096 /* largest potential swap allocation */
+#define DMTEXT 1024 /* swap allocation for text */
+
+/*
+ * Sizes of the system and user portions of the system page table.
+ */
+#define SYSPTSIZE (2*NPTEPG)
+#define USRPTSIZE (2*NPTEPG)
+
+/*
+ * Size of the Shared Memory Pages page table.
+ */
+#ifndef SHMMAXPGS
+#define SHMMAXPGS 512 /* XXX until we have more kmap space */
+#endif
+
+/*
+ * Size of User Raw I/O map
+ */
+#define USRIOSIZE 1024
+
+/*
+ * The size of the clock loop.
+ */
+#define LOOPPAGES (maxfree - firstfree)
+
+/*
+ * The time for a process to be blocked before being very swappable.
+ * This is a number of seconds which the system takes as being a non-trivial
+ * amount of real time. You probably shouldn't change this;
+ * it is used in subtle ways (fractions and multiples of it are, that is, like
+ * half of a ``long time'', almost a long time, etc.)
+ * It is related to human patience and other factors which don't really
+ * change over time.
+ */
+#define MAXSLP 20
+
+/*
+ * A swapped in process is given a small amount of core without being bothered
+ * by the page replacement algorithm. Basically this says that if you are
+ * swapped in you deserve some resources. We protect the last SAFERSS
+ * pages against paging and will just swap you out rather than paging you.
+ * Note that each process has at least UPAGES+CLSIZE pages which are not
+ * paged anyways (this is currently 8+2=10 pages or 5k bytes), so this
+ * number just means a swapped in process is given around 25k bytes.
+ * Just for fun: current memory prices are 4600$ a megabyte on VAX (4/22/81),
+ * so we loan each swapped in process memory worth 100$, or just admit
+ * that we don't consider it worthwhile and swap it out to disk which costs
+ * $30/mb or about $0.75.
+ * { wfj 6/16/89: Retail AT memory expansion $800/megabyte, loan of $17
+ * on disk costing $7/mb or $0.18 (in memory still 100:1 in cost!) }
+ */
+#define SAFERSS 8 /* nominal ``small'' resident set size
+ protected against replacement */
+
+/*
+ * DISKRPM is used to estimate the number of paging i/o operations
+ * which one can expect from a single disk controller.
+ */
+#define DISKRPM 60
+
+/*
+ * Klustering constants. Klustering is the gathering
+ * of pages together for pagein/pageout, while clustering
+ * is the treatment of hardware page size as though it were
+ * larger than it really is.
+ *
+ * KLMAX gives maximum cluster size in CLSIZE page (cluster-page)
+ * units. Note that KLMAX*CLSIZE must be <= DMMIN in dmap.h.
+ */
+
+#define KLMAX (4/CLSIZE)
+#define KLSEQL (2/CLSIZE) /* in klust if vadvise(VA_SEQL) */
+#define KLIN (4/CLSIZE) /* default data/stack in klust */
+#define KLTXT (4/CLSIZE) /* default text in klust */
+#define KLOUT (4/CLSIZE)
+
+/*
+ * KLSDIST is the advance or retard of the fifo reclaim for sequential
+ * processes data space.
+ */
+#define KLSDIST 3 /* klusters advance/retard for seq. fifo */
+
+/*
+ * There are two clock hands, initially separated by HANDSPREAD bytes
+ * (but at most all of user memory). The amount of time to reclaim
+ * a page once the pageout process examines it increases with this
+ * distance and decreases as the scan rate rises.
+ */
+#define HANDSPREAD (2 * 1024 * 1024)
+
+/*
+ * The number of times per second to recompute the desired paging rate
+ * and poke the pagedaemon.
+ */
+#define RATETOSCHEDPAGING 4
+
+/*
+ * Believed threshold (in megabytes) for which interleaved
+ * swapping area is desirable.
+ */
+#define LOTSOFMEM 2
+
+#define mapin(pte, v, pfnum, prot) \
+ {(*(int *)(pte) = ((pfnum)<<PGSHIFT) | (prot)) ; }
+
+/*
+ * Mach derived constants
+ */
+
+/* user/kernel map constants */
+#define KERNBASE (0-(NKPDE+1)*(NBPG*NPTEPG))
+#define KERNSIZE (NKPDE*NBPG*NPTEPG)
+
+#define VM_MIN_ADDRESS ((vm_offset_t)0)
+#define VM_MAXUSER_ADDRESS ((vm_offset_t)KERNBASE - (NBPG*(NPTEPG+UPAGES)))
+#define USRSTACK VM_MAXUSER_ADDRESS
+#define UPT_MIN_ADDRESS ((vm_offset_t)KERNBASE - (NBPG*NPTEPG))
+#define UPT_MAX_ADDRESS ((vm_offset_t)KERNBASE - (NBPG*(NKPDE+2)))
+#define VM_MAX_ADDRESS UPT_MAX_ADDRESS
+#define VM_MIN_KERNEL_ADDRESS ((vm_offset_t)KERNBASE - (NBPG*(NKPDE+2)))
+#define UPDT VM_MIN_KERNEL_ADDRESS
+#define KPT_MIN_ADDRESS ((vm_offset_t)(KERNBASE) - (NBPG*(NKPDE+1)))
+#define KPT_MAX_ADDRESS ((vm_offset_t)(KERNBASE) - NBPG)
+#define VM_MAX_KERNEL_ADDRESS ((vm_offset_t)ALT_MIN_ADDRESS - NBPG)
+#define ALT_MIN_ADDRESS ((vm_offset_t)((APTDPTDI) << 22))
+#define HIGHPAGES UPAGES
+
+
+/* virtual sizes (bytes) for various kernel submaps */
+#define VM_MBUF_SIZE (NMBCLUSTERS*MCLBYTES)
+#define VM_KMEM_SIZE (16 * 1024 * 1024)
+#define VM_PHYS_SIZE (USRIOSIZE*CLBYTES)
+
+/* pcb base */
+#define pcbb(p) ((u_int)(p)->p_addr)
+
+/*
+ * Flush MMU TLB
+ */
+
+#ifndef I386_CR3PAT
+#define I386_CR3PAT 0x0
+#endif
+
+#ifdef notyet
+#define _cr3() ({u_long rtn; \
+ asm (" movl %%cr3,%%eax; movl %%eax,%0 " \
+ : "=g" (rtn) \
+ : \
+ : "ax"); \
+ rtn; \
+})
+
+#define load_cr3(s) ({ u_long val; \
+ val = (s) | I386_CR3PAT; \
+ asm ("movl %0,%%eax; movl %%eax,%%cr3" \
+ : \
+ : "g" (val) \
+ : "ax"); \
+})
+
+#define tlbflush() ({ u_long val; \
+ val = u.u_pcb.pcb_ptd | I386_CR3PAT; \
+ asm ("movl %0,%%eax; movl %%eax,%%cr3" \
+ : \
+ : "g" (val) \
+ : "ax"); \
+})
+#endif
+#endif /* _MACHINE_VMPARAM_H_ */
diff --git a/sys/i386/isa/aha1542.c b/sys/i386/isa/aha1542.c
new file mode 100644
index 0000000..670f212
--- /dev/null
+++ b/sys/i386/isa/aha1542.c
@@ -0,0 +1,1479 @@
+/*
+ * (Mostly) Written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * $Id: aha1542.c,v 1.25 1994/05/03 05:44:53 rgrimes Exp $
+ */
+
+/*
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+
+#include <sys/types.h>
+#ifdef KERNEL /* don't laugh.. look for main() */
+#include <aha.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <i386/isa/isa_device.h>
+#endif /* KERNEL */
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#ifdef KERNEL
+#include "ddb.h"
+#include "kernel.h"
+#else /*KERNEL */
+#define NAHA 1
+#endif /*KERNEL */
+
+#ifndef NetBSD
+typedef timeout_func_t timeout_t;
+#endif
+
+/************************** board definitions *******************************/
+
+/*
+ * I/O Port Interface
+ */
+
+#define AHA_BASE aha->aha_base
+#define AHA_CTRL_STAT_PORT (AHA_BASE + 0x0) /* control & status */
+#define AHA_CMD_DATA_PORT (AHA_BASE + 0x1) /* cmds and datas */
+#define AHA_INTR_PORT (AHA_BASE + 0x2) /* Intr. stat */
+
+/*
+ * AHA_CTRL_STAT bits (write)
+ */
+
+#define AHA_HRST 0x80 /* Hardware reset */
+#define AHA_SRST 0x40 /* Software reset */
+#define AHA_IRST 0x20 /* Interrupt reset */
+#define AHA_SCRST 0x10 /* SCSI bus reset */
+
+/*
+ * AHA_CTRL_STAT bits (read)
+ */
+
+#define AHA_STST 0x80 /* Self test in Progress */
+#define AHA_DIAGF 0x40 /* Diagnostic Failure */
+#define AHA_INIT 0x20 /* Mbx Init required */
+#define AHA_IDLE 0x10 /* Host Adapter Idle */
+#define AHA_CDF 0x08 /* cmd/data out port full */
+#define AHA_DF 0x04 /* Data in port full */
+#define AHA_INVDCMD 0x01 /* Invalid command */
+
+/*
+ * AHA_CMD_DATA bits (write)
+ */
+
+#define AHA_NOP 0x00 /* No operation */
+#define AHA_MBX_INIT 0x01 /* Mbx initialization */
+#define AHA_START_SCSI 0x02 /* start scsi command */
+#define AHA_START_BIOS 0x03 /* start bios command */
+#define AHA_INQUIRE 0x04 /* Adapter Inquiry */
+#define AHA_MBO_INTR_EN 0x05 /* Enable MBO available interrupt */
+#define AHA_SEL_TIMEOUT_SET 0x06 /* set selection time-out */
+#define AHA_BUS_ON_TIME_SET 0x07 /* set bus-on time */
+#define AHA_BUS_OFF_TIME_SET 0x08 /* set bus-off time */
+#define AHA_SPEED_SET 0x09 /* set transfer speed */
+#define AHA_DEV_GET 0x0a /* return installed devices */
+#define AHA_CONF_GET 0x0b /* return configuration data */
+#define AHA_TARGET_EN 0x0c /* enable target mode */
+#define AHA_SETUP_GET 0x0d /* return setup data */
+#define AHA_WRITE_CH2 0x1a /* write channel 2 buffer */
+#define AHA_READ_CH2 0x1b /* read channel 2 buffer */
+#define AHA_WRITE_FIFO 0x1c /* write fifo buffer */
+#define AHA_READ_FIFO 0x1d /* read fifo buffer */
+#define AHA_ECHO 0x1e /* Echo command data */
+#define AHA_EXT_BIOS 0x28 /* return extended bios info */
+#define AHA_MBX_ENABLE 0x29 /* enable mail box interface */
+
+struct aha_cmd_buf {
+ u_char byte[16];
+};
+
+/*
+ * AHA_INTR_PORT bits (read)
+ */
+
+#define AHA_ANY_INTR 0x80 /* Any interrupt */
+#define AHA_SCRD 0x08 /* SCSI reset detected */
+#define AHA_HACC 0x04 /* Command complete */
+#define AHA_MBOA 0x02 /* MBX out empty */
+#define AHA_MBIF 0x01 /* MBX in full */
+
+/*
+ * Mail box defs
+ */
+
+#define AHA_MBX_SIZE 16 /* mail box size */
+
+struct aha_mbx {
+ struct aha_mbx_out {
+ unsigned char cmd;
+ unsigned char ccb_addr[3];
+ } mbo[AHA_MBX_SIZE];
+ struct aha_mbx_in {
+ unsigned char stat;
+ unsigned char ccb_addr[3];
+ } mbi[AHA_MBX_SIZE];
+};
+
+/*
+ * mbo.cmd values
+ */
+
+#define AHA_MBO_FREE 0x0 /* MBO entry is free */
+#define AHA_MBO_START 0x1 /* MBO activate entry */
+#define AHA_MBO_ABORT 0x2 /* MBO abort entry */
+
+/*
+ * mbi.stat values
+ */
+
+#define AHA_MBI_FREE 0x0 /* MBI entry is free */
+#define AHA_MBI_OK 0x1 /* completed without error */
+#define AHA_MBI_ABORT 0x2 /* aborted ccb */
+#define AHA_MBI_UNKNOWN 0x3 /* Tried to abort invalid CCB */
+#define AHA_MBI_ERROR 0x4 /* Completed with error */
+
+/* FOR OLD VERSIONS OF THE !%$@ this may have to be 16 (yuk) */
+#define AHA_NSEG 17 /* Number of scatter gather segments <= 16 */
+ /* allow 64 K i/o (min) */
+
+struct aha_ccb {
+ unsigned char opcode;
+ unsigned char lun:3;
+ unsigned char data_in:1; /* must be 0 */
+ unsigned char data_out:1; /* must be 0 */
+ unsigned char target:3;
+ unsigned char scsi_cmd_length;
+ unsigned char req_sense_length;
+ unsigned char data_length[3];
+ unsigned char data_addr[3];
+ unsigned char link_addr[3];
+ unsigned char link_id;
+ unsigned char host_stat;
+ unsigned char target_stat;
+ unsigned char reserved[2];
+ struct scsi_generic scsi_cmd;
+ struct scsi_sense_data scsi_sense;
+ struct aha_scat_gath {
+ unsigned char seg_len[3];
+ unsigned char seg_addr[3];
+ } scat_gath[AHA_NSEG];
+ struct aha_ccb *next;
+ struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */
+ struct aha_mbx_out *mbx; /* pointer to mail box */
+ int flags;
+#define CCB_FREE 0
+#define CCB_ACTIVE 1
+#define CCB_ABORTED 2
+};
+
+/*
+ * opcode fields
+ */
+
+#define AHA_INITIATOR_CCB 0x00 /* SCSI Initiator CCB */
+#define AHA_TARGET_CCB 0x01 /* SCSI Target CCB */
+#define AHA_INIT_SCAT_GATH_CCB 0x02 /* SCSI Initiator with scatter gather */
+#define AHA_RESET_CCB 0x81 /* SCSI Bus reset */
+
+/*
+ * aha_ccb.host_stat values
+ */
+
+#define AHA_OK 0x00 /* cmd ok */
+#define AHA_LINK_OK 0x0a /* Link cmd ok */
+#define AHA_LINK_IT 0x0b /* Link cmd ok + int */
+#define AHA_SEL_TIMEOUT 0x11 /* Selection time out */
+#define AHA_OVER_UNDER 0x12 /* Data over/under run */
+#define AHA_BUS_FREE 0x13 /* Bus dropped at unexpected time */
+#define AHA_INV_BUS 0x14 /* Invalid bus phase/sequence */
+#define AHA_BAD_MBO 0x15 /* Incorrect MBO cmd */
+#define AHA_BAD_CCB 0x16 /* Incorrect ccb opcode */
+#define AHA_BAD_LINK 0x17 /* Not same values of LUN for links */
+#define AHA_INV_TARGET 0x18 /* Invalid target direction */
+#define AHA_CCB_DUP 0x19 /* Duplicate CCB received */
+#define AHA_INV_CCB 0x1a /* Invalid CCB or segment list */
+#define AHA_ABORTED 42
+
+struct aha_setup {
+ u_char sync_neg:1;
+ u_char parity:1;
+ u_char:6;
+ u_char speed;
+ u_char bus_on;
+ u_char bus_off;
+ u_char num_mbx;
+ u_char mbx[3];
+ struct {
+ u_char offset:4;
+ u_char period:3;
+ u_char valid:1;
+ } sync[8];
+ u_char disc_sts;
+};
+
+struct aha_config {
+ u_char chan;
+ u_char intr;
+ u_char scsi_dev:3;
+ u_char:5;
+};
+
+struct aha_inquire
+{
+ u_char boardid; /* type of board */
+ /* 0x20 = BusLogic 545, but it gets
+ the command wrong, only returns
+ one byte */
+ /* 0x31 = AHA-1540 */
+ /* 0x41 = AHA-1540A/1542A/1542B */
+ /* 0x42 = AHA-1640 */
+ /* 0x43 = AHA-1542C */
+ /* 0x44 = AHA-1542CF */
+ /* 0x45 = AHA-1542CF, BIOS v2.01 */
+ u_char spec_opts; /* special options ID */
+ /* 0x41 = Board is standard model */
+ u_char revision_1; /* firmware revision [0-9A-Z] */
+ u_char revision_2; /* firmware revision [0-9A-Z] */
+};
+
+struct aha_extbios
+{
+ u_char flags; /* Bit 3 == 1 extended bios enabled */
+ u_char mailboxlock; /* mail box lock code to unlock it */
+};
+
+#define INT9 0x01
+#define INT10 0x02
+#define INT11 0x04
+#define INT12 0x08
+#define INT14 0x20
+#define INT15 0x40
+
+#define CHAN0 0x01
+#define CHAN5 0x20
+#define CHAN6 0x40
+#define CHAN7 0x80
+
+/*********************************** end of board definitions***************/
+
+#define PHYSTOKV(x) (((long int)(x)) ^ aha->kv_phys_xor)
+#define KVTOPHYS(x) vtophys(x)
+#define AHA_DMA_PAGES AHA_NSEG
+
+#define PAGESIZ 4096
+#define INVALIDATE_CACHE {asm volatile( ".byte 0x0F ;.byte 0x08" ); }
+
+u_char aha_scratch_buf[256];
+#ifdef AHADEBUG
+int aha_debug = 1;
+#endif /*AHADEBUG */
+
+struct aha_data {
+ short aha_base; /* base port for each board */
+ /*
+ * xor this with a physaddr to get a kv addr and visa versa
+ * for items in THIS STRUCT only.
+ * Used to get the CCD's physical and kv addresses from each
+ * other.
+ */
+ long int kv_phys_xor;
+ struct aha_mbx aha_mbx; /* all the mailboxes */
+ struct aha_ccb *aha_ccb_free; /* the next free ccb */
+ struct aha_ccb aha_ccb[AHA_MBX_SIZE]; /* all the CCBs */
+ int aha_int; /* our irq level */
+ int aha_dma; /* out DMA req channel */
+ int aha_scsi_dev; /* ourscsi bus address */
+ struct scsi_link sc_link; /* prototype for subdevs */
+} *ahadata[NAHA];
+
+struct aha_ccb *aha_get_ccb();
+int ahaprobe();
+void aha_done();
+int ahaattach();
+int ahaintr();
+int32 aha_scsi_cmd();
+void aha_timeout(caddr_t);
+void ahaminphys();
+u_int32 aha_adapter_info();
+
+#ifdef KERNEL
+struct scsi_adapter aha_switch =
+{
+ aha_scsi_cmd,
+ ahaminphys,
+ 0,
+ 0,
+ aha_adapter_info,
+ "aha",
+ 0, 0
+};
+
+/* the below structure is so we have a default dev struct for out link struct */
+struct scsi_device aha_dev =
+{
+ NULL, /* Use default error handler */
+ NULL, /* have a queue, served by this */
+ NULL, /* have no async handler */
+ NULL, /* Use default 'done' routine */
+ "aha",
+ 0,
+ 0, 0
+};
+
+struct isa_driver ahadriver =
+{
+ ahaprobe,
+ ahaattach,
+ "aha"
+};
+
+#endif /* KERNEL */
+
+static int ahaunit = 0;
+
+#define aha_abortmbx(mbx) \
+ (mbx)->cmd = AHA_MBO_ABORT; \
+ outb(AHA_CMD_DATA_PORT, AHA_START_SCSI);
+#define aha_startmbx(mbx) \
+ (mbx)->cmd = AHA_MBO_START; \
+ outb(AHA_CMD_DATA_PORT, AHA_START_SCSI);
+
+#define AHA_RESET_TIMEOUT 2000 /* time to wait for reset (mSec) */
+#ifndef KERNEL
+main()
+{
+ printf("size of aha_data is %d\n", sizeof(struct aha_data));
+ printf("size of aha_ccb is %d\n", sizeof(struct aha_ccb));
+ printf("size of aha_mbx is %d\n", sizeof(struct aha_mbx));
+}
+
+#else /*KERNEL */
+
+/*
+ * aha_cmd(unit,icnt, ocnt,wait, retval, opcode, args)
+ * Activate Adapter command
+ * icnt: number of args (outbound bytes written after opcode)
+ * ocnt: number of expected returned bytes
+ * wait: number of seconds to wait for response
+ * retval: buffer where to place returned bytes
+ * opcode: opcode AHA_NOP, AHA_MBX_INIT, AHA_START_SCSI ...
+ * args: parameters
+ *
+ * Performs an adapter command through the ports. Not to be confused
+ * with a scsi command, which is read in via the dma. One of the adapter
+ * commands tells it to read in a scsi command but that one is done
+ * separately. This is only called during set-up.
+ */
+int
+aha_cmd(unit, icnt, ocnt, wait, retval, opcode, args)
+ int unit;
+ int icnt;
+ int ocnt;
+ int wait;
+ u_char *retval;
+ unsigned opcode;
+ u_char args;
+{
+ struct aha_data *aha = ahadata[unit];
+ unsigned *ic = &opcode;
+ u_char oc;
+ register i;
+ int sts;
+
+ /*
+ * multiply the wait argument by a big constant
+ * zero defaults to 1 sec..
+ * all wait loops are in 50uSec cycles
+ */
+ if (wait)
+ wait *= 20000;
+ else
+ wait = 20000;
+ /*
+ * Wait for the adapter to go idle, unless it's one of
+ * the commands which don't need this
+ */
+ if (opcode != AHA_MBX_INIT && opcode != AHA_START_SCSI) {
+ i = 20000; /*do this for upto about a second */
+ while (--i) {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ if (sts & AHA_IDLE) {
+ break;
+ }
+ DELAY(50);
+ }
+ if (!i) {
+ printf("aha%d: aha_cmd, host not idle(0x%x)\n",
+ unit, sts);
+ return (ENXIO);
+ }
+ }
+ /*
+ * Now that it is idle, if we expect output, preflush the
+ * queue feeding to us.
+ */
+ if (ocnt) {
+ while ((inb(AHA_CTRL_STAT_PORT)) & AHA_DF)
+ inb(AHA_CMD_DATA_PORT);
+ }
+ /*
+ * Output the command and the number of arguments given
+ * for each byte, first check the port is empty.
+ */
+ icnt++;
+ /* include the command */
+ while (icnt--) {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ for (i = wait; i; i--) {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ if (!(sts & AHA_CDF))
+ break;
+ DELAY(50);
+ }
+ if (i == 0) {
+ printf("aha%d: aha_cmd, cmd/data port full\n", unit);
+ outb(AHA_CTRL_STAT_PORT, AHA_SRST);
+ return (ENXIO);
+ }
+ outb(AHA_CMD_DATA_PORT, (u_char) (*ic++));
+ }
+ /*
+ * If we expect input, loop that many times, each time,
+ * looking for the data register to have valid data
+ */
+ while (ocnt--) {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ for (i = wait; i; i--) {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ if (sts & AHA_DF)
+ break;
+ DELAY(50);
+ }
+ if (i == 0) {
+ printf("aha%d: aha_cmd, cmd/data port empty %d\n",
+ unit, ocnt);
+ return (ENXIO);
+ }
+ oc = inb(AHA_CMD_DATA_PORT);
+ if (retval)
+ *retval++ = oc;
+ }
+ /*
+ * Wait for the board to report a finised instruction
+ */
+ i = 20000;
+ while (--i) {
+ sts = inb(AHA_INTR_PORT);
+ if (sts & AHA_HACC) {
+ break;
+ }
+ DELAY(50);
+ }
+ if (i == 0) {
+ printf("aha%d: aha_cmd, host not finished(0x%x)\n", unit, sts);
+ return (ENXIO);
+ }
+ outb(AHA_CTRL_STAT_PORT, AHA_IRST);
+ return 0;
+}
+
+/*
+ * Check if the device can be found at the port given
+ * and if so, set it up ready for further work
+ * as an argument, takes the isa_device structure from
+ * autoconf.c
+ */
+int
+ahaprobe(dev)
+ struct isa_device *dev;
+{
+ int unit = ahaunit;
+ struct aha_data *aha;
+
+ /*
+ * find unit and check we have that many defined
+ */
+ if (unit >= NAHA) {
+ printf("aha%d: unit number too high\n", unit);
+ return 0;
+ }
+ dev->id_unit = unit;
+
+ /*
+ * a quick safety check so we can be sleazy later
+ */
+ if (sizeof(struct aha_data) > PAGESIZ) {
+ printf("aha struct > pagesize\n");
+ return 0;
+ }
+ /*
+ * Allocate a storage area for us
+ */
+ if (ahadata[unit]) {
+ printf("aha%d: memory already allocated\n", unit);
+ return 0;
+ }
+ aha = malloc(sizeof(struct aha_data), M_TEMP, M_NOWAIT);
+ if (!aha) {
+ printf("aha%d: cannot malloc!\n", unit);
+ return 0;
+ }
+ bzero(aha, sizeof(struct aha_data));
+ ahadata[unit] = aha;
+ aha->aha_base = dev->id_iobase;
+ /*
+ * Try initialise a unit at this location
+ * sets up dma and bus speed, loads aha->aha_int
+ */
+ if (aha_init(unit) != 0) {
+ ahadata[unit] = NULL;
+ free(aha, M_TEMP);
+ return 0;
+ }
+ /*
+ * Calculate the xor product of the aha struct's
+ * physical and virtual address. This allows us
+ * to change addresses within the structure
+ * from physical to virtual easily, as long as
+ * the structure is less than 1 page in size.
+ * This is used to recognise CCBs which are in
+ * this struct and which are refered to by the
+ * hardware using physical addresses.
+ * (assumes malloc returns a chunk that doesn't
+ * span pages)
+ * eventually use the hash table in aha1742.c
+ */
+ aha->kv_phys_xor = (long int) aha ^ (KVTOPHYS(aha));
+
+ /*
+ * If it's there, put in it's interrupt vectors
+ */
+ dev->id_irq = (1 << aha->aha_int);
+ dev->id_drq = aha->aha_dma;
+ ahaunit++;
+ return 0x4;
+}
+
+/*
+ * Attach all the sub-devices we can find
+ */
+int
+ahaattach(dev)
+ struct isa_device *dev;
+{
+ int unit = dev->id_unit;
+ struct aha_data *aha = ahadata[unit];
+
+ /*
+ * fill in the prototype scsi_link.
+ */
+ aha->sc_link.adapter_unit = unit;
+ aha->sc_link.adapter_targ = aha->aha_scsi_dev;
+ aha->sc_link.adapter = &aha_switch;
+ aha->sc_link.device = &aha_dev;
+ aha->sc_link.flags = SDEV_BOUNCE;
+
+ /*
+ * ask the adapter what subunits are present
+ */
+ scsi_attachdevs(&(aha->sc_link));
+
+ return 1;
+}
+
+/*
+ * Return some information to the caller about the adapter and its
+ * capabilities.
+ */
+u_int32
+aha_adapter_info(unit)
+ int unit;
+{
+ return (2); /* 2 outstanding requests at a time per device */
+}
+
+/*
+ * Catch an interrupt from the adaptor
+ */
+int
+ahaintr(unit)
+ int unit;
+{
+ struct aha_ccb *ccb;
+ unsigned char stat;
+ register i;
+ struct aha_data *aha = ahadata[unit];
+
+#ifdef AHADEBUG
+ printf("ahaintr ");
+#endif /*AHADEBUG */
+ /*
+ * First acknowlege the interrupt, Then if it's not telling about
+ * a completed operation just return.
+ */
+ stat = inb(AHA_INTR_PORT);
+ outb(AHA_CTRL_STAT_PORT, AHA_IRST);
+ if (!(stat & AHA_MBIF))
+ return 1;
+#ifdef AHADEBUG
+ printf("mbxin ");
+#endif /*AHADEBUG */
+ /*
+ * If it IS then process the competed operation
+ */
+ for (i = 0; i < AHA_MBX_SIZE; i++) {
+ if (aha->aha_mbx.mbi[i].stat != AHA_MBI_FREE) {
+ ccb = (struct aha_ccb *) PHYSTOKV(
+ (_3btol(aha->aha_mbx.mbi[i].ccb_addr)));
+
+ if ((stat = aha->aha_mbx.mbi[i].stat) != AHA_MBI_OK) {
+ switch (stat) {
+ case AHA_MBI_ABORT:
+#ifdef AHADEBUG
+ if (aha_debug)
+ printf("abort");
+#endif /*AHADEBUG */
+ ccb->host_stat = AHA_ABORTED;
+ break;
+
+ case AHA_MBI_UNKNOWN:
+ ccb = (struct aha_ccb *) 0;
+#ifdef AHADEBUG
+ if (aha_debug)
+ printf("unknown ccb for abort ");
+#endif /*AHADEBUG */
+ /* may have missed it */
+ /* no such ccb known for abort */
+
+ case AHA_MBI_ERROR:
+ break;
+
+ default:
+ panic("Impossible mbxi status");
+
+ }
+#ifdef AHADEBUG
+ if (aha_debug && ccb) {
+ u_char *cp;
+ cp = (u_char *) (&(ccb->scsi_cmd));
+ printf("op=%x %x %x %x %x %x\n",
+ cp[0], cp[1], cp[2],
+ cp[3], cp[4], cp[5]);
+ printf("stat %x for mbi[%d]\n"
+ ,aha->aha_mbx.mbi[i].stat, i);
+ printf("addr = 0x%x\n", ccb);
+ }
+#endif /*AHADEBUG */
+ }
+ if (ccb) {
+ untimeout((timeout_t)aha_timeout, (caddr_t)ccb);
+ aha_done(unit, ccb);
+ }
+ aha->aha_mbx.mbi[i].stat = AHA_MBI_FREE;
+ }
+ }
+ return 1;
+}
+
+/*
+ * A ccb (and hence a mbx-out is put onto the
+ * free list.
+ */
+void
+aha_free_ccb(unit, ccb, flags)
+ int unit;
+ struct aha_ccb *ccb;
+ int flags;
+{
+ struct aha_data *aha = ahadata[unit];
+ unsigned int opri = 0;
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+
+ ccb->next = aha->aha_ccb_free;
+ aha->aha_ccb_free = ccb;
+ ccb->flags = CCB_FREE;
+ /*
+ * If there were none, wake anybody waiting for
+ * one to come free, starting with queued entries
+ */
+ if (!ccb->next) {
+ wakeup((caddr_t)&aha->aha_ccb_free);
+ }
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+}
+
+/*
+ * Get a free ccb (and hence mbox-out entry)
+ */
+struct aha_ccb *
+aha_get_ccb(unit, flags)
+ int unit;
+ int flags;
+{
+ struct aha_data *aha = ahadata[unit];
+ unsigned opri = 0;
+ struct aha_ccb *rc;
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+ /*
+ * If we can and have to, sleep waiting for one
+ * to come free
+ */
+ while ((!(rc = aha->aha_ccb_free)) && (!(flags & SCSI_NOSLEEP))) {
+ tsleep((caddr_t)&aha->aha_ccb_free, PRIBIO, "ahaccb", 0);
+ }
+ if (rc) {
+ aha->aha_ccb_free = aha->aha_ccb_free->next;
+ rc->flags = CCB_ACTIVE;
+ }
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+ return (rc);
+}
+
+/*
+ * We have a ccb which has been processed by the
+ * adaptor, now we look to see how the operation
+ * went. Wake up the owner if waiting
+ */
+void
+aha_done(unit, ccb)
+ int unit;
+ struct aha_ccb *ccb;
+{
+ struct aha_data *aha = ahadata[unit];
+ struct scsi_sense_data *s1, *s2;
+ struct scsi_xfer *xs = ccb->xfer;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB2, ("aha_done\n"));
+ /*
+ * Otherwise, put the results of the operation
+ * into the xfer and call whoever started it
+ */
+ if (!(xs->flags & INUSE)) {
+ printf("aha%d: exiting but not in use!\n", unit);
+ Debugger("aha1542");
+ }
+ if (((ccb->host_stat != AHA_OK) || (ccb->target_stat != SCSI_OK))
+ && ((xs->flags & SCSI_ERR_OK) == 0)) {
+ /*
+ * We have an error, that we cannot ignore.
+ */
+ s1 = (struct scsi_sense_data *) (((char *) (&ccb->scsi_cmd))
+ + ccb->scsi_cmd_length);
+ s2 = &(xs->sense);
+
+ if (ccb->host_stat) {
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("host err 0x%x\n",
+ ccb->host_stat));
+ switch (ccb->host_stat) {
+ case AHA_ABORTED:
+ case AHA_SEL_TIMEOUT: /* No response */
+ xs->error = XS_TIMEOUT;
+ break;
+ default: /* Other scsi protocol messes */
+ xs->error = XS_DRIVER_STUFFUP;
+ printf("aha%d:host_stat%x\n",
+ unit, ccb->host_stat);
+ }
+ } else {
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("target err 0x%x\n",
+ ccb->target_stat));
+ switch (ccb->target_stat) {
+ case 0x02:
+ /* structure copy!!!!! */
+ *s2 = *s1;
+ xs->error = XS_SENSE;
+ break;
+ case 0x08:
+ xs->error = XS_BUSY;
+ break;
+ default:
+ printf("aha%d:target_stat%x\n",
+ unit, ccb->target_stat);
+ xs->error = XS_DRIVER_STUFFUP;
+ }
+ }
+ } else {
+ /* All went correctly OR errors expected */
+ xs->resid = 0;
+ }
+ xs->flags |= ITSDONE;
+ aha_free_ccb(unit, ccb, xs->flags);
+ scsi_done(xs);
+}
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+aha_init(unit)
+ int unit;
+{
+ struct aha_data *aha = ahadata[unit];
+ unsigned char ad[3];
+ volatile int i, sts;
+ struct aha_config conf;
+ struct aha_inquire inquire;
+ struct aha_extbios extbios;
+
+ /*
+ * reset board, If it doesn't respond, assume
+ * that it's not there.. good for the probe
+ */
+
+ outb(AHA_CTRL_STAT_PORT, AHA_HRST | AHA_SRST);
+
+ for (i = AHA_RESET_TIMEOUT; i; i--) {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ if (sts == (AHA_IDLE | AHA_INIT)) {
+ break;
+ }
+ DELAY(1000); /* calibrated in msec */
+ }
+#ifdef AHADEBUG
+ printf("aha_init: AHA_RESET_TIMEOUT went to %d\n", i);
+#endif /* AHADEBUG */
+ if (i == 0) {
+#ifdef AHADEBUG
+ if (aha_debug)
+ printf("aha_init: No answer from board\n");
+#endif /*AHADEBUG */
+ return (ENXIO);
+ }
+
+ /*
+ * Assume we have a board at this stage, do an adapter inquire
+ * to find out what type of controller it is. If the AHA_INQUIRE
+ * command fails, blatter about it, nuke the boardid so the 1542C
+ * stuff gets skipped over, and reset the board again.
+ */
+ if(aha_cmd(unit, 0, sizeof(inquire), 1 ,&inquire, AHA_INQUIRE)) {
+ /*
+ * Blah.. not a real adaptec board!!!
+ * Seems that the Buslogic 545S and the DTC3290 both get
+ * this wrong.
+ */
+ printf ("aha%d: not a REAL adaptec board, may cause warnings\n",
+ unit);
+ inquire.boardid = 0;
+ outb(AHA_CTRL_STAT_PORT, AHA_HRST | AHA_SRST);
+ for (i = AHA_RESET_TIMEOUT; i; i--) {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ if (sts == (AHA_IDLE | AHA_INIT)) {
+ break;
+ }
+ DELAY(1000); /* calibrated in msec */
+ }
+#ifdef AHADEBUG
+ printf("aha_init2: AHA_RESET_TIMEOUT went to %d\n", i);
+#endif /* AHADEBUG */
+ if (i == 0) {
+#ifdef AHADEBUG
+ if (aha_debug)
+ printf("aha_init2: No answer from board\n");
+#endif /*AHADEBUG */
+ return (ENXIO);
+ }
+ }
+#ifdef AHADEBUG
+ printf("aha%d: inquire %x, %x, %x, %x\n",
+ unit,
+ inquire.boardid, inquire.spec_opts,
+ inquire.revision_1, inquire.revision_2);
+#endif /* AHADEBUG */
+
+ /*
+ * If we are a 1542C or 1542CF disable the extended bios so that the
+ * mailbox interface is unlocked.
+ * No need to check the extended bios flags as some of the
+ * extensions that cause us problems are not flagged in that byte.
+ */
+ if ((inquire.boardid == 0x43) || (inquire.boardid == 0x44) ||
+ (inquire.boardid == 0x45)) {
+ aha_cmd(unit, 0, sizeof(extbios), 0, &extbios, AHA_EXT_BIOS);
+#ifdef AHADEBUG
+ printf("aha%d: extended bios flags %x\n", unit, extbios.flags);
+#endif /* AHADEBUG */
+ printf("aha%d: 1542C/CF detected, unlocking mailbox\n", unit);
+ aha_cmd(unit, 2, 0, 0, 0, AHA_MBX_ENABLE,
+ 0, extbios.mailboxlock);
+ }
+
+ /*
+ * setup dma channel from jumpers and save int
+ * level
+ */
+ printf("aha%d: reading board settings, ", unit);
+#define PRNT(x) printf(x)
+ DELAY(1000); /* for Bustek 545 */
+ aha_cmd(unit, 0, sizeof(conf), 0, &conf, AHA_CONF_GET);
+ switch (conf.chan) {
+ case CHAN0:
+ outb(0x0b, 0x0c);
+ outb(0x0a, 0x00);
+ aha->aha_dma = 0;
+ PRNT("dma=0 ");
+ break;
+ case CHAN5:
+ outb(0xd6, 0xc1);
+ outb(0xd4, 0x01);
+ aha->aha_dma = 5;
+ PRNT("dma=5 ");
+ break;
+ case CHAN6:
+ outb(0xd6, 0xc2);
+ outb(0xd4, 0x02);
+ aha->aha_dma = 6;
+ PRNT("dma=6 ");
+ break;
+ case CHAN7:
+ outb(0xd6, 0xc3);
+ outb(0xd4, 0x03);
+ aha->aha_dma = 7;
+ PRNT("dma=7 ");
+ break;
+ default:
+ printf("illegal dma jumper setting\n");
+ return (EIO);
+ }
+ switch (conf.intr) {
+ case INT9:
+ aha->aha_int = 9;
+ PRNT("int=9 ");
+ break;
+ case INT10:
+ aha->aha_int = 10;
+ PRNT("int=10 ");
+ break;
+ case INT11:
+ aha->aha_int = 11;
+ PRNT("int=11 ");
+ break;
+ case INT12:
+ aha->aha_int = 12;
+ PRNT("int=12 ");
+ break;
+ case INT14:
+ aha->aha_int = 14;
+ PRNT("int=14 ");
+ break;
+ case INT15:
+ aha->aha_int = 15;
+ PRNT("int=15 ");
+ break;
+ default:
+ printf("illegal int jumper setting\n");
+ return (EIO);
+ }
+
+ /* who are we on the scsi bus? */
+ aha->aha_scsi_dev = conf.scsi_dev;
+
+ /*
+ * Change the bus on/off times to not clash with other dma users.
+ */
+ aha_cmd(unit, 1, 0, 0, 0, AHA_BUS_ON_TIME_SET, 7);
+ aha_cmd(unit, 1, 0, 0, 0, AHA_BUS_OFF_TIME_SET, 4);
+
+#ifdef TUNE_1542
+ /*
+ * Initialize memory transfer speed
+ * Not compiled in by default because it breaks some machines
+ */
+ if (!(aha_set_bus_speed(unit))) {
+ return (EIO);
+ }
+#else
+ printf (" (bus speed defaulted)\n");
+#endif /*TUNE_1542*/
+ /*
+ * Initialize mail box
+ */
+ lto3b(KVTOPHYS(&aha->aha_mbx), ad);
+
+ aha_cmd(unit, 4, 0, 0, 0, AHA_MBX_INIT,
+ AHA_MBX_SIZE,
+ ad[0],
+ ad[1],
+ ad[2]);
+
+ /*
+ * link the ccb's with the mbox-out entries and
+ * into a free-list
+ * this is a kludge but it works
+ */
+ for (i = 0; i < AHA_MBX_SIZE; i++) {
+ aha->aha_ccb[i].next = aha->aha_ccb_free;
+ aha->aha_ccb_free = &aha->aha_ccb[i];
+ aha->aha_ccb_free->flags = CCB_FREE;
+ aha->aha_ccb_free->mbx = &aha->aha_mbx.mbo[i];
+ lto3b(KVTOPHYS(aha->aha_ccb_free), aha->aha_mbx.mbo[i].ccb_addr);
+ }
+ /*
+ * Note that we are going and return (to probe)
+ */
+ return 0;
+}
+
+void
+ahaminphys(bp)
+ struct buf *bp;
+{
+/* aha seems to explode with 17 segs (64k may require 17 segs) */
+/* on old boards so use a max of 16 segs if you have problems here */
+ if (bp->b_bcount > ((AHA_NSEG - 1) * PAGESIZ)) {
+ bp->b_bcount = ((AHA_NSEG - 1) * PAGESIZ);
+ }
+}
+
+/*
+ * start a scsi operation given the command and
+ * the data address. Also needs the unit, target
+ * and lu
+ */
+int32
+aha_scsi_cmd(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_link *sc_link = xs->sc_link;
+ int unit = sc_link->adapter_unit;
+ struct aha_data *aha = ahadata[unit];
+ struct scsi_sense_data *s1, *s2;
+ struct aha_ccb *ccb;
+ struct aha_scat_gath *sg;
+ int seg; /* scatter gather seg being worked on */
+ int i = 0;
+ int rc = 0;
+ int thiskv;
+ int thisphys, nextphys;
+ int bytes_this_seg, bytes_this_page, datalen, flags;
+ struct iovec *iovp;
+ int s;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB2, ("aha_scsi_cmd\n"));
+ /*
+ * get a ccb (mbox-out) to use. If the transfer
+ * is from a buf (possibly from interrupt time)
+ * then we can't allow it to sleep
+ */
+ flags = xs->flags;
+ if (!(ccb = aha_get_ccb(unit, flags))) {
+ xs->error = XS_DRIVER_STUFFUP;
+ return (TRY_AGAIN_LATER);
+ }
+ if (ccb->mbx->cmd != AHA_MBO_FREE)
+ printf("aha%d: MBO not free\n", unit);
+
+ /*
+ * Put all the arguments for the xfer in the ccb
+ */
+ ccb->xfer = xs;
+ if (flags & SCSI_RESET) {
+ ccb->opcode = AHA_RESET_CCB;
+ } else {
+ /* can't use S/G if zero length */
+ ccb->opcode = (xs->datalen ?
+ AHA_INIT_SCAT_GATH_CCB
+ : AHA_INITIATOR_CCB);
+ }
+ ccb->target = sc_link->target;
+ ccb->data_out = 0;
+ ccb->data_in = 0;
+ ccb->lun = sc_link->lun;
+ ccb->scsi_cmd_length = xs->cmdlen;
+ ccb->req_sense_length = sizeof(ccb->scsi_sense);
+
+ if ((xs->datalen) && (!(flags & SCSI_RESET))) {
+ /* can use S/G only if not zero length */
+ lto3b(KVTOPHYS(ccb->scat_gath), ccb->data_addr);
+ sg = ccb->scat_gath;
+ seg = 0;
+#ifdef TFS_ONLY
+ if (flags & SCSI_DATA_UIO) {
+ iovp = ((struct uio *) xs->data)->uio_iov;
+ datalen = ((struct uio *) xs->data)->uio_iovcnt;
+ while ((datalen) && (seg < AHA_NSEG)) {
+ lto3b(iovp->iov_base, sg->seg_addr);
+ lto3b(iovp->iov_len, sg->seg_len);
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("UIO(0x%x@0x%x)"
+ ,iovp->iov_len
+ ,iovp->iov_base));
+ sg++;
+ iovp++;
+ seg++;
+ datalen--;
+ }
+ } else
+#endif /*TFS_ONLY */
+ {
+ /*
+ * Set up the scatter gather block
+ */
+
+ SC_DEBUG(xs->sc_link, SDEV_DB4,
+ ("%d @0x%x:- ", xs->datalen, xs->data));
+ datalen = xs->datalen;
+ thiskv = (int) xs->data;
+ thisphys = KVTOPHYS(thiskv);
+
+ while ((datalen) && (seg < AHA_NSEG)) {
+ bytes_this_seg = 0;
+
+ /* put in the base address */
+ lto3b(thisphys, sg->seg_addr);
+
+ SC_DEBUGN(xs->sc_link, SDEV_DB4,
+ ("0x%x", thisphys));
+
+ /* do it at least once */
+ nextphys = thisphys;
+ while ((datalen) && (thisphys == nextphys)) {
+ /*
+ * This page is contiguous (physically)
+ * with the the last, just extend the
+ * length
+ */
+ /* check it fits on the ISA bus */
+ if (thisphys > 0xFFFFFF)
+ {
+ printf("aha%d: DMA beyond"
+ " end Of ISA\n", unit);
+ xs->error = XS_DRIVER_STUFFUP;
+ aha_free_ccb(unit, ccb, flags);
+ return (HAD_ERROR);
+ }
+ /** how far to the end of the page ***/
+ nextphys = (thisphys & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ bytes_this_page = nextphys - thisphys;
+ /**** or the data ****/
+ bytes_this_page = min(bytes_this_page
+ ,datalen);
+ bytes_this_seg += bytes_this_page;
+ datalen -= bytes_this_page;
+
+ /**** get more ready for the next page ****/
+ thiskv = (thiskv & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ if (datalen)
+ thisphys = KVTOPHYS(thiskv);
+ }
+ /*
+ * next page isn't contiguous, finish the seg
+ */
+ SC_DEBUGN(xs->sc_link, SDEV_DB4,
+ ("(0x%x)", bytes_this_seg));
+ lto3b(bytes_this_seg, sg->seg_len);
+ sg++;
+ seg++;
+ }
+ }
+ lto3b(seg * sizeof(struct aha_scat_gath), ccb->data_length);
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("\n"));
+
+ if (datalen) { /* there's still data, must have run out of segs! */
+ printf("aha%d: aha_scsi_cmd, more than %d DMA segs\n",
+ unit, AHA_NSEG);
+ xs->error = XS_DRIVER_STUFFUP;
+ aha_free_ccb(unit, ccb, flags);
+ return (HAD_ERROR);
+ }
+ } else { /* No data xfer, use non S/G values */
+ lto3b(0, ccb->data_addr);
+ lto3b(0, ccb->data_length);
+ }
+ lto3b(0, ccb->link_addr);
+ /*
+ * Put the scsi command in the ccb and start it
+ */
+ if (!(flags & SCSI_RESET))
+ bcopy(xs->cmd, &ccb->scsi_cmd, ccb->scsi_cmd_length);
+ if (!(flags & SCSI_NOMASK)) {
+ s = splbio(); /* stop instant timeouts */
+ timeout((timeout_t)aha_timeout, (caddr_t)ccb, (xs->timeout * hz) / 1000);
+ aha_startmbx(ccb->mbx);
+ /*
+ * Usually return SUCCESSFULLY QUEUED
+ */
+ splx(s);
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("sent\n"));
+ return (SUCCESSFULLY_QUEUED);
+ }
+ aha_startmbx(ccb->mbx);
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd sent, waiting\n"));
+
+ /*
+ * If we can't use interrupts, poll on completion
+ */
+ return (aha_poll(unit, xs, ccb)); /* only during boot */
+}
+
+/*
+ * Poll a particular unit, looking for a particular xs
+ */
+int
+aha_poll(unit, xs, ccb)
+ int unit;
+ struct scsi_xfer *xs;
+ struct aha_ccb *ccb;
+{
+ struct aha_data *aha = ahadata[unit];
+ int done = 0;
+ int count = xs->timeout;
+ u_char stat;
+
+ /*timeouts are in msec, so we loop in 1000uSec cycles */
+ while (count) {
+ /*
+ * If we had interrupts enabled, would we
+ * have got an interrupt?
+ */
+ stat = inb(AHA_INTR_PORT);
+ if (stat & AHA_ANY_INTR) {
+ ahaintr(unit);
+ }
+ if (xs->flags & ITSDONE) {
+ break;
+ }
+ DELAY(1000); /* only happens in boot so ok */
+ count--;
+ }
+ if (count == 0) {
+ /*
+ * We timed out, so call the timeout handler
+ * manually, accout for the fact that the
+ * clock is not running yet by taking out the
+ * clock queue entry it makes
+ */
+ aha_timeout((caddr_t)ccb);
+
+ /*
+ * because we are polling,
+ * take out the timeout entry aha_timeout made
+ */
+ untimeout((timeout_t)aha_timeout, (caddr_t)ccb);
+ count = 2000;
+ while (count) {
+ /*
+ * Once again, wait for the int bit
+ */
+ stat = inb(AHA_INTR_PORT);
+ if (stat & AHA_ANY_INTR) {
+ ahaintr(unit);
+ }
+ if (xs->flags & ITSDONE) {
+ break;
+ }
+ DELAY(1000); /* only happens in boot so ok */
+ count--;
+ }
+ if (count == 0) {
+ /*
+ * We timed out again.. this is bad
+ * Notice that this time there is no
+ * clock queue entry to remove
+ */
+ aha_timeout((caddr_t)ccb);
+ }
+ }
+ if (xs->error)
+ return (HAD_ERROR);
+ return (COMPLETE);
+
+}
+
+#ifdef TUNE_1542
+/*
+ * Try all the speeds from slowest to fastest.. if it finds a
+ * speed that fails, back off one notch from the last working
+ * speed (unless there is no other notch).
+ * Returns the nSEC value of the time used
+ * or 0 if it could get a working speed (or the NEXT speed
+ * failed)
+ */
+static struct bus_speed
+{
+ char arg;
+ int nsecs;
+}aha_bus_speeds[] =
+{
+ {0x88,100},
+ {0x99,150},
+ {0xaa,200},
+ {0xbb,250},
+ {0xcc,300},
+ {0xdd,350},
+ {0xee,400},
+ {0xff,450}
+};
+
+int
+aha_set_bus_speed(unit)
+ int unit;
+{
+ int speed;
+ int lastworking;
+ int retval,retval2;
+ struct aha_data *aha = ahadata[unit];
+
+ lastworking = -1;
+ speed = 7;
+ while (1) {
+ retval = aha_bus_speed_check(unit,speed);
+ if(retval != 0) {
+ lastworking = speed;
+ }
+ if((retval == 0) || (speed == 0)) {
+ if(lastworking == -1) {
+ printf("No working bus speed for aha154X\n");
+ return 0;
+ }
+ printf("%d nSEC ok, using "
+ ,aha_bus_speeds[lastworking].nsecs);
+ if(lastworking == 7) { /* is slowest already */
+ printf("marginal ");
+ } else {
+ lastworking++;
+ }
+ retval2 = aha_bus_speed_check(unit,lastworking);
+ if(retval2 == 0) {
+ printf("test retry failed.. aborting.\n");
+ return 0;
+ }
+ printf("%d nSEC\n",retval2);
+ return retval2 ;
+
+ }
+ speed--;
+ }
+}
+
+/*
+ * Set the DMA speed to the Nth speed and try an xfer. If it
+ * fails return 0, if it succeeds return the nSec value selected
+ * If there is no such speed return HAD_ERROR.
+ */
+static char aha_test_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz!@";
+
+int
+aha_bus_speed_check(unit, speed)
+ int unit, speed;
+{
+ int numspeeds = sizeof(aha_bus_speeds) / sizeof(struct bus_speed);
+ int loopcount;
+ u_char ad[3];
+ struct aha_data *aha = ahadata[unit];
+
+ /*
+ * Check we have such an entry
+ */
+ if (speed >= numspeeds)
+ return (HAD_ERROR); /* illegal speed */
+
+ /*
+ * Set the dma-speed
+ */
+ aha_cmd(unit, 1, 0, 0, 0, AHA_SPEED_SET, aha_bus_speeds[speed].arg);
+
+ /*
+ * put the test data into the buffer and calculate
+ * it's address. Read it onto the board
+ */
+ lto3b(KVTOPHYS(aha_scratch_buf), ad);
+ for(loopcount = 2000;loopcount;loopcount--)
+ {
+ strcpy(aha_scratch_buf, aha_test_string);
+
+ aha_cmd(unit, 3, 0, 0, 0, AHA_WRITE_FIFO, ad[0], ad[1], ad[2]);
+
+ /*
+ * clear the buffer then copy the contents back from the
+ * board.
+ */
+ bzero(aha_scratch_buf, 54); /* 54 bytes transfered by test */
+
+ aha_cmd(unit, 3, 0, 0, 0, AHA_READ_FIFO, ad[0], ad[1], ad[2]);
+
+ /*
+ * Compare the original data and the final data and
+ * return the correct value depending upon the result
+ */
+ if (strcmp(aha_test_string, aha_scratch_buf))
+ return 0; /* failed test */
+ }
+ /* copy succeded assume speed ok */
+
+ return (aha_bus_speeds[speed].nsecs);
+
+}
+#endif /*TUNE_1542*/
+
+void
+aha_timeout(caddr_t arg1)
+{
+ struct aha_ccb * ccb = (struct aha_ccb *)arg1;
+ int unit;
+ int s = splbio();
+ struct aha_data *aha;
+
+ unit = ccb->xfer->sc_link->adapter_unit;
+ aha = ahadata[unit];
+ sc_print_addr(ccb->xfer->sc_link);
+ printf("timed out ");
+
+ /*
+ * If The ccb's mbx is not free, then
+ * the board has gone south
+ */
+ if (ccb->mbx->cmd != AHA_MBO_FREE) {
+ printf("\nadapter not taking commands.. frozen?!\n");
+ Debugger("aha1542");
+ }
+ /*
+ * If it has been through before, then
+ * a previous abort has failed, don't
+ * try abort again
+ */
+ if (ccb->flags == CCB_ABORTED) {
+ /* abort timed out */
+ printf(" AGAIN\n");
+ ccb->xfer->retries = 0; /* I MEAN IT ! */
+ ccb->host_stat = AHA_ABORTED;
+ aha_done(unit, ccb);
+ } else {
+ /* abort the operation that has timed out */
+ printf("\n");
+ aha_abortmbx(ccb->mbx);
+ /* 4 secs for the abort */
+ timeout((timeout_t)aha_timeout, (caddr_t)ccb, 4 * hz);
+ ccb->flags = CCB_ABORTED;
+ } splx(s);
+}
+#endif /* KERNEL */
diff --git a/sys/i386/isa/aha1742.c b/sys/i386/isa/aha1742.c
new file mode 100644
index 0000000..27bce5f
--- /dev/null
+++ b/sys/i386/isa/aha1742.c
@@ -0,0 +1,1244 @@
+/*
+ * Written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * commenced: Sun Sep 27 18:14:01 PDT 1992
+ *
+ * $Id: aha1742.c,v 1.14 1994/01/11 07:24:32 rgrimes Exp $
+ */
+
+#include <sys/types.h>
+
+#ifdef KERNEL /* don't laugh, it compiles as a program too.. look */
+#include <ahb.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <i386/include/pio.h>
+#include <i386/isa/isa_device.h>
+#endif /*KERNEL */
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+/* */
+
+#ifdef KERNEL
+# ifdef NetBSD
+# ifdef DDB
+int Debugger();
+# else /* DDB */
+#define Debugger() panic("should call debugger here (adaptec.c)")
+# endif /* DDB */
+# else
+#include "ddb.h"
+#endif /* netbsd */
+#else /* KERNEL */
+#define NAHB 1
+#endif /* kernel */
+
+#ifndef NetBSD
+typedef timeout_func_t timeout_t;
+#endif
+
+typedef unsigned long int physaddr;
+#include "kernel.h"
+
+#define KVTOPHYS(x) vtophys(x)
+
+#define AHB_ECB_MAX 32 /* store up to 32ECBs at any one time */
+ /* in aha1742 H/W ( Not MAX ? ) */
+#define ECB_HASH_SIZE 32 /* when we have a physical addr. for */
+ /* a ecb and need to find the ecb in */
+ /* space, look it up in the hash table */
+#define ECB_HASH_SHIFT 9 /* only hash on multiples of 512 */
+#define ECB_HASH(x) ((((long int)(x))>>ECB_HASH_SHIFT) % ECB_HASH_SIZE)
+
+#define AHB_NSEG 33 /* number of dma segments supported */
+
+/*
+ * AHA1740 standard EISA Host ID regs (Offset from slot base)
+ */
+#define HID0 0xC80 /* 0,1: msb of ID2, 3-7: ID1 */
+#define HID1 0xC81 /* 0-4: ID3, 4-7: LSB ID2 */
+#define HID2 0xC82 /* product, 0=174[20] 1 = 1744 */
+#define HID3 0xC83 /* firmware revision */
+
+#define CHAR1(B1,B2) (((B1>>2) & 0x1F) | '@')
+#define CHAR2(B1,B2) (((B1<<3) & 0x18) | ((B2>>5) & 0x7)|'@')
+#define CHAR3(B1,B2) ((B2 & 0x1F) | '@')
+
+/* AHA1740 EISA board control registers (Offset from slot base) */
+#define EBCTRL 0xC84
+#define CDEN 0x01
+/*
+ * AHA1740 EISA board mode registers (Offset from slot base)
+ */
+#define PORTADDR 0xCC0
+#define PORTADDR_ENHANCED 0x80
+#define BIOSADDR 0xCC1
+#define INTDEF 0xCC2
+#define SCSIDEF 0xCC3
+#define BUSDEF 0xCC4
+#define RESV0 0xCC5
+#define RESV1 0xCC6
+#define RESV2 0xCC7
+/**** bit definitions for INTDEF ****/
+#define INT9 0x00
+#define INT10 0x01
+#define INT11 0x02
+#define INT12 0x03
+#define INT14 0x05
+#define INT15 0x06
+#define INTHIGH 0x08 /* int high=ACTIVE (else edge) */
+#define INTEN 0x10
+/**** bit definitions for SCSIDEF ****/
+#define HSCSIID 0x0F /* our SCSI ID */
+#define RSTPWR 0x10 /* reset scsi bus on power up or reset */
+/**** bit definitions for BUSDEF ****/
+#define B0uS 0x00 /* give up bus immediatly */
+#define B4uS 0x01 /* delay 4uSec. */
+#define B8uS 0x02
+/*
+ * AHA1740 ENHANCED mode mailbox control regs (Offset from slot base)
+ */
+#define MBOXOUT0 0xCD0
+#define MBOXOUT1 0xCD1
+#define MBOXOUT2 0xCD2
+#define MBOXOUT3 0xCD3
+
+#define ATTN 0xCD4
+#define G2CNTRL 0xCD5
+#define G2INTST 0xCD6
+#define G2STAT 0xCD7
+
+#define MBOXIN0 0xCD8
+#define MBOXIN1 0xCD9
+#define MBOXIN2 0xCDA
+#define MBOXIN3 0xCDB
+
+#define G2STAT2 0xCDC
+
+/*
+ * Bit definitions for the 5 control/status registers
+ */
+#define ATTN_TARGET 0x0F
+#define ATTN_OPCODE 0xF0
+#define OP_IMMED 0x10
+#define AHB_TARG_RESET 0x80
+#define OP_START_ECB 0x40
+#define OP_ABORT_ECB 0x50
+
+#define G2CNTRL_SET_HOST_READY 0x20
+#define G2CNTRL_CLEAR_EISA_INT 0x40
+#define G2CNTRL_HARD_RESET 0x80
+
+#define G2INTST_TARGET 0x0F
+#define G2INTST_INT_STAT 0xF0
+#define AHB_ECB_OK 0x10
+#define AHB_ECB_RECOVERED 0x50
+#define AHB_HW_ERR 0x70
+#define AHB_IMMED_OK 0xA0
+#define AHB_ECB_ERR 0xC0
+#define AHB_ASN 0xD0 /* for target mode */
+#define AHB_IMMED_ERR 0xE0
+
+#define G2STAT_BUSY 0x01
+#define G2STAT_INT_PEND 0x02
+#define G2STAT_MBOX_EMPTY 0x04
+
+#define G2STAT2_HOST_READY 0x01
+
+struct ahb_dma_seg {
+ physaddr addr;
+ long len;
+};
+
+struct ahb_ecb_status {
+ u_short status;
+#define ST_DON 0x0001
+#define ST_DU 0x0002
+#define ST_QF 0x0008
+#define ST_SC 0x0010
+#define ST_DO 0x0020
+#define ST_CH 0x0040
+#define ST_INT 0x0080
+#define ST_ASA 0x0100
+#define ST_SNS 0x0200
+#define ST_INI 0x0800
+#define ST_ME 0x1000
+#define ST_ECA 0x4000
+ u_char ha_status;
+#define HS_OK 0x00
+#define HS_CMD_ABORTED_HOST 0x04
+#define HS_CMD_ABORTED_ADAPTER 0x05
+#define HS_TIMED_OUT 0x11
+#define HS_HARDWARE_ERR 0x20
+#define HS_SCSI_RESET_ADAPTER 0x22
+#define HS_SCSI_RESET_INCOMING 0x23
+ u_char targ_status;
+#define TS_OK 0x00
+#define TS_CHECK_CONDITION 0x02
+#define TS_BUSY 0x08
+ u_long resid_count;
+ u_long resid_addr;
+ u_short addit_status;
+ u_char sense_len;
+ u_char unused[9];
+ u_char cdb[6];
+};
+
+
+struct ecb {
+ u_char opcode;
+#define ECB_SCSI_OP 0x01
+ u_char:4;
+ u_char options:3;
+ u_char:1;
+ short opt1;
+#define ECB_CNE 0x0001
+#define ECB_DI 0x0080
+#define ECB_SES 0x0400
+#define ECB_S_G 0x1000
+#define ECB_DSB 0x4000
+#define ECB_ARS 0x8000
+ short opt2;
+#define ECB_LUN 0x0007
+#define ECB_TAG 0x0008
+#define ECB_TT 0x0030
+#define ECB_ND 0x0040
+#define ECB_DAT 0x0100
+#define ECB_DIR 0x0200
+#define ECB_ST 0x0400
+#define ECB_CHK 0x0800
+#define ECB_REC 0x4000
+#define ECB_NRB 0x8000
+ u_short unused1;
+ physaddr data;
+ u_long datalen;
+ physaddr status;
+ physaddr chain;
+ short unused2;
+ short unused3;
+ physaddr sense;
+ u_char senselen;
+ u_char cdblen;
+ short cksum;
+ u_char cdb[12];
+ /*-----------------end of hardware supported fields----------------*/
+ struct ecb *next; /* in free list */
+ struct scsi_xfer *xs; /* the scsi_xfer for this cmd */
+ int flags;
+#define ECB_FREE 0
+#define ECB_ACTIVE 1
+#define ECB_ABORTED 2
+#define ECB_IMMED 4
+#define ECB_IMMED_FAIL 8
+ struct ahb_dma_seg ahb_dma[AHB_NSEG];
+ struct ahb_ecb_status ecb_status;
+ struct scsi_sense_data ecb_sense;
+ struct ecb *nexthash;
+ physaddr hashkey; /* physaddr of this struct */
+};
+
+struct ahb_data {
+ int flags;
+#define AHB_INIT 0x01;
+ int baseport;
+ struct ecb *ecbhash[ECB_HASH_SIZE];
+ struct ecb *free_ecb;
+ int our_id; /* our scsi id */
+ int vect;
+ struct ecb *immed_ecb; /* an outstanding immediete command */
+ struct scsi_link sc_link;
+ int numecbs;
+} *ahbdata[NAHB];
+
+int ahbprobe();
+int ahbprobe1 __P((struct isa_device *dev));
+int ahb_attach();
+int ahb_init __P((int unit));
+int ahbintr();
+int32 ahb_scsi_cmd();
+void ahb_timeout(caddr_t);
+void ahb_done();
+struct ecb *cheat;
+void ahb_free_ecb();
+void ahbminphys();
+struct ecb *ahb_ecb_phys_kv();
+u_int32 ahb_adapter_info();
+
+#define MAX_SLOTS 8 /* XXX should this be 16?? Need EISA spec */
+static ahb_slot = 0; /* slot last board was found in */
+static ahb_unit = 0;
+int ahb_debug = 0;
+#define AHB_SHOWECBS 0x01
+#define AHB_SHOWINTS 0x02
+#define AHB_SHOWCMDS 0x04
+#define AHB_SHOWMISC 0x08
+#define FAIL 1
+#define SUCCESS 0
+#define PAGESIZ 4096
+
+#ifdef KERNEL
+struct isa_driver ahbdriver =
+{
+ ahbprobe,
+ ahb_attach,
+ "ahb"
+};
+
+struct scsi_adapter ahb_switch =
+{
+ ahb_scsi_cmd,
+ ahbminphys,
+ 0,
+ 0,
+ ahb_adapter_info,
+ "ahb",
+ { 0, 0 }
+};
+
+/* the below structure is so we have a default dev struct for our link struct */
+struct scsi_device ahb_dev =
+{
+ NULL, /* Use default error handler */
+ NULL, /* have a queue, served by this */
+ NULL, /* have no async handler */
+ NULL, /* Use default 'done' routine */
+ "ahb",
+ 0,
+ { 0, 0 }
+};
+
+#endif /*KERNEL */
+
+#ifndef KERNEL
+main()
+{
+ printf("ahb_data size is %d\n", sizeof(struct ahb_data));
+ printf("ecb size is %d\n", sizeof(struct ecb));
+}
+
+#else /*KERNEL */
+
+/*
+ * Function to send a command out through a mailbox
+ */
+void
+ahb_send_mbox(int unit, int opcode, int target, struct ecb *ecb)
+{
+ int port = ahbdata[unit]->baseport;
+ int wait = 300; /* 3ms should be enough */
+ int stport = port + G2STAT;
+ int s = splbio();
+
+ while (--wait) {
+ if ((inb(stport) & (G2STAT_BUSY | G2STAT_MBOX_EMPTY))
+ == (G2STAT_MBOX_EMPTY))
+ break;
+ DELAY(10);
+ }
+ if (wait == 0) {
+ printf("ahb%d: board not responding\n", unit);
+ Debugger("aha1742");
+ }
+ outl(port + MBOXOUT0, KVTOPHYS(ecb)); /* don't know this will work */
+ outb(port + ATTN, opcode | target);
+
+ splx(s);
+}
+
+/*
+ * Function to poll for command completion when in poll mode
+ */
+int
+ahb_poll(int unit, int wait)
+{ /* in msec */
+ struct ahb_data *ahb = ahbdata[unit];
+ int port = ahb->baseport;
+ int stport = port + G2STAT;
+
+ retry:
+ while (--wait) {
+ if (inb(stport) & G2STAT_INT_PEND)
+ break;
+ DELAY(1000);
+ } if (wait == 0) {
+ printf("ahb%d: board not responding\n", unit);
+ return (EIO);
+ }
+ if (cheat != ahb_ecb_phys_kv(ahb, inl(port + MBOXIN0))) {
+ printf("discarding %x ", inl(port + MBOXIN0));
+ outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT);
+ DELAY(50000);
+ goto retry;
+ }
+ /* don't know this will work */
+ ahbintr(unit);
+ return (0);
+}
+
+/*
+ * Function to send an immediate type command to the adapter
+ */
+void
+ahb_send_immed(int unit, int target, u_long cmd)
+{
+ int port = ahbdata[unit]->baseport;
+ int s = splbio();
+ int stport = port + G2STAT;
+ int wait = 100; /* 1 ms enough? */
+
+ while (--wait) {
+ if ((inb(stport) & (G2STAT_BUSY | G2STAT_MBOX_EMPTY))
+ == (G2STAT_MBOX_EMPTY))
+ break;
+ DELAY(10);
+ } if (wait == 0) {
+ printf("ahb%d: board not responding\n", unit);
+ Debugger("aha1742");
+ }
+ outl(port + MBOXOUT0, cmd); /* don't know this will work */
+ outb(port + G2CNTRL, G2CNTRL_SET_HOST_READY);
+ outb(port + ATTN, OP_IMMED | target);
+ splx(s);
+}
+
+/*
+ * Check the slots looking for a board we recognise
+ * If we find one, note it's address (slot) and call
+ * the actual probe routine to check it out.
+ */
+int
+ahbprobe(dev)
+ struct isa_device *dev;
+{
+ int port;
+ u_char byte1, byte2, byte3;
+
+ ahb_slot++;
+ while (ahb_slot <= MAX_SLOTS) {
+ port = 0x1000 * ahb_slot;
+ byte1 = inb(port + HID0);
+ byte2 = inb(port + HID1);
+ byte3 = inb(port + HID2);
+ if (byte1 == 0xff) {
+ ahb_slot++;
+ continue;
+ }
+ if ((CHAR1(byte1, byte2) == 'A')
+ && (CHAR2(byte1, byte2) == 'D')
+ && (CHAR3(byte1, byte2) == 'P')
+ && ((byte3 == 0) || (byte3 == 1))) {
+ dev->id_iobase = port;
+ return ahbprobe1(dev);
+ }
+ ahb_slot++;
+ }
+ return 0;
+}
+
+/*
+ * Check if the device can be found at the port given
+ * and if so, set it up ready for further work
+ * as an argument, takes the isa_device structure from
+ * autoconf.c.
+ */
+int
+ahbprobe1(dev)
+ struct isa_device *dev;
+{
+ /*
+ * find unit and check we have that many defined
+ */
+ int unit = ahb_unit;
+ struct ahb_data *ahb;
+
+ if (unit >= NAHB) {
+ printf("ahb: unit number (%d) too high\n", unit);
+ return 0;
+ }
+ dev->id_unit = unit;
+
+ /*
+ * Allocate a storage area for us
+ */
+ if (ahbdata[unit]) {
+ printf("ahb%d: memory already allocated\n", unit);
+ return 0;
+ }
+ ahb = malloc(sizeof(struct ahb_data), M_TEMP, M_NOWAIT);
+ if (!ahb) {
+ printf("ahb%d: cannot malloc!\n", unit);
+ return 0;
+ }
+ bzero(ahb, sizeof(struct ahb_data));
+ ahbdata[unit] = ahb;
+ ahb->baseport = dev->id_iobase;
+ /*
+ * Try initialise a unit at this location
+ * sets up dma and bus speed, loads ahb->vect
+ */
+ if (ahb_init(unit) != 0) {
+ ahbdata[unit] = NULL;
+ free(ahb, M_TEMP);
+ return (0);
+ }
+ /*
+ * If it's there, put in it's interrupt vectors
+ */
+ dev->id_irq = (1 << ahb->vect);
+ dev->id_drq = -1; /* use EISA dma */
+
+ ahb_unit++;
+ return 0x1000;
+}
+
+/*
+ * Attach all the sub-devices we can find
+ */
+int
+ahb_attach(dev)
+ struct isa_device *dev;
+{
+#ifdef NetBSD
+ int unit = dev->id_masunit;
+#else
+ int unit = dev->id_unit;
+#endif
+ struct ahb_data *ahb = ahbdata[unit];
+
+ /*
+ * fill in the prototype scsi_link.
+ */
+ ahb->sc_link.adapter_unit = unit;
+ ahb->sc_link.adapter_targ = ahb->our_id;
+ ahb->sc_link.adapter = &ahb_switch;
+ ahb->sc_link.device = &ahb_dev;
+
+ /*
+ * ask the adapter what subunits are present
+ */
+ scsi_attachdevs(&(ahb->sc_link));
+
+ return 1;
+}
+
+/*
+ * Return some information to the caller about
+ * the adapter and it's capabilities
+ */
+u_int32
+ahb_adapter_info(unit)
+ int unit;
+{
+ return (2); /* 2 outstanding requests at a time per device */
+}
+
+/*
+ * Catch an interrupt from the adaptor
+ */
+int
+ahbintr(unit)
+ int unit;
+{
+ struct ecb *ecb;
+ unsigned char stat;
+ u_char ahbstat;
+ int target;
+ long int mboxval;
+ struct ahb_data *ahb = ahbdata[unit];
+
+ int port = ahb->baseport;
+
+#ifdef AHBDEBUG
+ printf("ahbintr ");
+#endif /*AHBDEBUG */
+
+ while (inb(port + G2STAT) & G2STAT_INT_PEND) {
+ /*
+ * First get all the information and then
+ * acknowlege the interrupt
+ */
+ ahbstat = inb(port + G2INTST);
+ target = ahbstat & G2INTST_TARGET;
+ stat = ahbstat & G2INTST_INT_STAT;
+ mboxval = inl(port + MBOXIN0); /* don't know this will work */
+ outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT);
+#ifdef AHBDEBUG
+ printf("status = 0x%x ", stat);
+#endif /*AHBDEBUG */
+ /*
+ * Process the completed operation
+ */
+
+ if (stat == AHB_ECB_OK) { /* common case is fast */
+ ecb = ahb_ecb_phys_kv(ahb, mboxval);
+ } else {
+ switch (stat) {
+ case AHB_IMMED_OK:
+ ecb = ahb->immed_ecb;
+ ahb->immed_ecb = 0;
+ break;
+ case AHB_IMMED_ERR:
+ ecb = ahb->immed_ecb;
+ ecb->flags |= ECB_IMMED_FAIL;
+ ahb->immed_ecb = 0;
+ break;
+ case AHB_ASN: /* for target mode */
+ printf("ahb%d: Unexpected ASN interrupt(%x)\n",
+ unit, mboxval);
+ ecb = 0;
+ break;
+ case AHB_HW_ERR:
+ printf("ahb%d: Hardware error interrupt(%x)\n",
+ unit, mboxval);
+ ecb = 0;
+ break;
+ case AHB_ECB_RECOVERED:
+ ecb = ahb_ecb_phys_kv(ahb, mboxval);
+ break;
+ case AHB_ECB_ERR:
+ ecb = ahb_ecb_phys_kv(ahb, mboxval);
+ break;
+ default:
+ printf(" Unknown return from ahb%d(%x)\n", unit, ahbstat);
+ ecb = 0;
+ }
+ } if (ecb) {
+#ifdef AHBDEBUG
+ if (ahb_debug & AHB_SHOWCMDS) {
+ show_scsi_cmd(ecb->xs);
+ }
+ if ((ahb_debug & AHB_SHOWECBS) && ecb)
+ printf("<int ecb(%x)>", ecb);
+#endif /*AHBDEBUG */
+ untimeout((timeout_t)ahb_timeout, (caddr_t)ecb);
+ ahb_done(unit, ecb, ((stat == AHB_ECB_OK) ? SUCCESS : FAIL));
+ }
+ }
+ return 1;
+}
+
+/*
+ * We have a ecb which has been processed by the
+ * adaptor, now we look to see how the operation
+ * went.
+ */
+void
+ahb_done(unit, ecb, state)
+ int unit, state;
+ struct ecb *ecb;
+{
+ struct ahb_ecb_status *stat = &ecb->ecb_status;
+ struct scsi_sense_data *s1, *s2;
+ struct scsi_xfer *xs = ecb->xs;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB2, ("ahb_done\n"));
+ /*
+ * Otherwise, put the results of the operation
+ * into the xfer and call whoever started it
+ */
+ if (ecb->flags & ECB_IMMED) {
+ if (ecb->flags & ECB_IMMED_FAIL) {
+ xs->error = XS_DRIVER_STUFFUP;
+ }
+ goto done;
+ }
+ if ((state == SUCCESS) || (xs->flags & SCSI_ERR_OK)) { /* All went correctly OR errors expected */
+ xs->resid = 0;
+ xs->error = 0;
+ } else {
+
+ s1 = &(ecb->ecb_sense);
+ s2 = &(xs->sense);
+
+ if (stat->ha_status) {
+ switch (stat->ha_status) {
+ case HS_SCSI_RESET_ADAPTER:
+ break;
+ case HS_SCSI_RESET_INCOMING:
+ break;
+ case HS_CMD_ABORTED_HOST: /* No response */
+ case HS_CMD_ABORTED_ADAPTER: /* No response */
+ break;
+ case HS_TIMED_OUT: /* No response */
+#ifdef AHBDEBUG
+ if (ahb_debug & AHB_SHOWMISC) {
+ printf("timeout reported back\n");
+ }
+#endif /*AHBDEBUG */
+ xs->error = XS_TIMEOUT;
+ break;
+ default: /* Other scsi protocol messes */
+ xs->error = XS_DRIVER_STUFFUP;
+#ifdef AHBDEBUG
+ if (ahb_debug & AHB_SHOWMISC) {
+ printf("unexpected ha_status: %x\n",
+ stat->ha_status);
+ }
+#endif /*AHBDEBUG */
+ }
+ } else {
+ switch (stat->targ_status) {
+ case TS_CHECK_CONDITION:
+ /* structure copy!!!!! */
+ *s2 = *s1;
+ xs->error = XS_SENSE;
+ break;
+ case TS_BUSY:
+ xs->error = XS_BUSY;
+ break;
+ default:
+#ifdef AHBDEBUG
+ if (ahb_debug & AHB_SHOWMISC) {
+ printf("unexpected targ_status: %x\n",
+ stat->targ_status);
+ }
+#endif /*AHBDEBUG */
+ xs->error = XS_DRIVER_STUFFUP;
+ }
+ }
+ }
+done: xs->flags |= ITSDONE;
+ ahb_free_ecb(unit, ecb, xs->flags);
+ scsi_done(xs);
+}
+
+/*
+ * A ecb (and hence a mbx-out is put onto the
+ * free list.
+ */
+void
+ahb_free_ecb(unit, ecb, flags)
+ int unit, flags;
+ struct ecb *ecb;
+{
+ unsigned int opri = 0;
+ struct ahb_data *ahb = ahbdata[unit];
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+
+ ecb->next = ahb->free_ecb;
+ ahb->free_ecb = ecb;
+ ecb->flags = ECB_FREE;
+ /*
+ * If there were none, wake abybody waiting for
+ * one to come free, starting with queued entries
+ */
+ if (!ecb->next) {
+ wakeup((caddr_t)&ahb->free_ecb);
+ }
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+}
+
+/*
+ * Get a free ecb
+ * If there are none, see if we can allocate a
+ * new one. If so, put it in the hash table too
+ * otherwise either return an error or sleep
+ */
+struct ecb *
+ahb_get_ecb(unit, flags)
+ int unit, flags;
+{
+ struct ahb_data *ahb = ahbdata[unit];
+ unsigned opri = 0;
+ struct ecb *ecbp;
+ int hashnum;
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+ /*
+ * If we can and have to, sleep waiting for one to come free
+ * but only if we can't allocate a new one.
+ */
+ while (!(ecbp = ahb->free_ecb)) {
+ if (ahb->numecbs < AHB_ECB_MAX) {
+ ecbp = (struct ecb *) malloc(sizeof(struct ecb),
+ M_TEMP,
+ M_NOWAIT);
+ if (ecbp) {
+ bzero(ecbp, sizeof(struct ecb));
+ ahb->numecbs++;
+ ecbp->flags = ECB_ACTIVE;
+ /*
+ * put in the phystokv hash table
+ * Never gets taken out.
+ */
+ ecbp->hashkey = KVTOPHYS(ecbp);
+ hashnum = ECB_HASH(ecbp->hashkey);
+ ecbp->nexthash = ahb->ecbhash[hashnum];
+ ahb->ecbhash[hashnum] = ecbp;
+ } else {
+ printf("ahb%d: Can't malloc ECB\n", unit);
+ } goto gottit;
+ } else {
+ if (!(flags & SCSI_NOSLEEP)) {
+ tsleep((caddr_t)&ahb->free_ecb, PRIBIO,
+ "ahbecb", 0);
+ }
+ }
+ } if (ecbp) {
+ /* Get ECB from from free list */
+ ahb->free_ecb = ecbp->next;
+ ecbp->flags = ECB_ACTIVE;
+ }
+gottit: if (!(flags & SCSI_NOMASK))
+ splx(opri);
+
+ return (ecbp);
+}
+
+/*
+ * given a physical address, find the ecb that
+ * it corresponds to:
+ */
+struct ecb *
+ahb_ecb_phys_kv(ahb, ecb_phys)
+ struct ahb_data *ahb;
+ physaddr ecb_phys;
+{
+ int hashnum = ECB_HASH(ecb_phys);
+ struct ecb *ecbp = ahb->ecbhash[hashnum];
+
+ while (ecbp) {
+ if (ecbp->hashkey == ecb_phys)
+ break;
+ ecbp = ecbp->nexthash;
+ }
+ return ecbp;
+}
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+ahb_init(unit)
+ int unit;
+{
+ struct ahb_data *ahb = ahbdata[unit];
+ int port = ahb->baseport;
+ int intdef;
+ int wait = 1000; /* 1 sec enough? */
+ int i;
+ int stport = port + G2STAT;
+#define NO_NO 1
+#ifdef NO_NO
+ /*
+ * reset board, If it doesn't respond, assume
+ * that it's not there.. good for the probe
+ */
+ outb(port + EBCTRL, CDEN); /* enable full card */
+ outb(port + PORTADDR, PORTADDR_ENHANCED);
+
+ outb(port + G2CNTRL, G2CNTRL_HARD_RESET);
+ DELAY(1000);
+ outb(port + G2CNTRL, 0);
+ DELAY(10000);
+ while (--wait) {
+ if ((inb(stport) & G2STAT_BUSY) == 0)
+ break;
+ DELAY(1000);
+ } if (wait == 0) {
+#ifdef AHBDEBUG
+ if (ahb_debug & AHB_SHOWMISC)
+ printf("ahb_init: No answer from aha1742 board\n");
+#endif /*AHBDEBUG */
+ return (ENXIO);
+ }
+ i = inb(port + MBOXIN0) & 0xff;
+ if (i) {
+ printf("self test failed, val = 0x%x\n", i);
+ return (EIO);
+ }
+#endif
+ while (inb(stport) & G2STAT_INT_PEND) {
+ printf(".");
+ outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT);
+ DELAY(10000);
+ }
+ outb(port + EBCTRL, CDEN); /* enable full card */
+ outb(port + PORTADDR, PORTADDR_ENHANCED);
+ /*
+ * Assume we have a board at this stage
+ * setup dma channel from jumpers and save int
+ * level
+ */
+ printf("ahb%d: reading board settings, ", unit);
+
+ intdef = inb(port + INTDEF);
+ switch (intdef & 0x07) {
+ case INT9:
+ ahb->vect = 9;
+ break;
+ case INT10:
+ ahb->vect = 10;
+ break;
+ case INT11:
+ ahb->vect = 11;
+ break;
+ case INT12:
+ ahb->vect = 12;
+ break;
+ case INT14:
+ ahb->vect = 14;
+ break;
+ case INT15:
+ ahb->vect = 15;
+ break;
+ default:
+ printf("illegal int setting\n");
+ return (EIO);
+ }
+ printf("int=%d\n", ahb->vect);
+
+ outb(port + INTDEF, (intdef | INTEN)); /* make sure we can interrupt */
+
+ /* who are we on the scsi bus? */
+ ahb->our_id = (inb(port + SCSIDEF) & HSCSIID);
+
+ /*
+ * Note that we are going and return (to probe)
+ */
+ ahb->flags |= AHB_INIT;
+ return (0);
+}
+
+#ifndef min
+#define min(x,y) (x < y ? x : y)
+#endif /* min */
+
+void
+ahbminphys(bp)
+ struct buf *bp;
+{
+ if (bp->b_bcount > ((AHB_NSEG - 1) * PAGESIZ)) {
+ bp->b_bcount = ((AHB_NSEG - 1) * PAGESIZ);
+ }
+}
+
+/*
+ * start a scsi operation given the command and
+ * the data address. Also needs the unit, target
+ * and lu
+ */
+int32
+ahb_scsi_cmd(xs)
+ struct scsi_xfer *xs;
+{
+ struct ecb *ecb;
+ struct ahb_dma_seg *sg;
+ int seg; /* scatter gather seg being worked on */
+ int thiskv;
+ physaddr thisphys, nextphys;
+ int unit = xs->sc_link->adapter_unit;
+ int bytes_this_seg, bytes_this_page, datalen, flags;
+ struct ahb_data *ahb = ahbdata[unit];
+ int s;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB2, ("ahb_scsi_cmd\n"));
+ /*
+ * get a ecb (mbox-out) to use. If the transfer
+ * is from a buf (possibly from interrupt time)
+ * then we can't allow it to sleep
+ */
+ flags = xs->flags;
+ if (xs->bp)
+ flags |= (SCSI_NOSLEEP); /* just to be sure */
+ if (flags & ITSDONE) {
+ printf("ahb%d: Already done?", unit);
+ xs->flags &= ~ITSDONE;
+ }
+ if (!(flags & INUSE)) {
+ printf("ahb%d: Not in use?", unit);
+ xs->flags |= INUSE;
+ }
+ if (!(ecb = ahb_get_ecb(unit, flags))) {
+ xs->error = XS_DRIVER_STUFFUP;
+ return (TRY_AGAIN_LATER);
+ }
+ cheat = ecb;
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("start ecb(%x)\n", ecb));
+ ecb->xs = xs;
+ /*
+ * If it's a reset, we need to do an 'immediate'
+ * command, and store it's ecb for later
+ * if there is already an immediate waiting,
+ * then WE must wait
+ */
+ if (flags & SCSI_RESET) {
+ ecb->flags |= ECB_IMMED;
+ if (ahb->immed_ecb) {
+ return (TRY_AGAIN_LATER);
+ }
+ ahb->immed_ecb = ecb;
+ if (!(flags & SCSI_NOMASK)) {
+ s = splbio();
+ ahb_send_immed(unit, xs->sc_link->target, AHB_TARG_RESET);
+ timeout((timeout_t)ahb_timeout, (caddr_t)ecb, (xs->timeout * hz) / 1000);
+ splx(s);
+ return (SUCCESSFULLY_QUEUED);
+ } else {
+ ahb_send_immed(unit, xs->sc_link->target, AHB_TARG_RESET);
+ /*
+ * If we can't use interrupts, poll on completion
+ */
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("wait\n"));
+ if (ahb_poll(unit, xs->timeout)) {
+ ahb_free_ecb(unit, ecb, flags);
+ xs->error = XS_TIMEOUT;
+ return (HAD_ERROR);
+ }
+ return (COMPLETE);
+ }
+ }
+ /*
+ * Put all the arguments for the xfer in the ecb
+ */
+ ecb->opcode = ECB_SCSI_OP;
+ ecb->opt1 = ECB_SES | ECB_DSB | ECB_ARS;
+ if (xs->datalen) {
+ ecb->opt1 |= ECB_S_G;
+ }
+ ecb->opt2 = xs->sc_link->lun | ECB_NRB;
+ ecb->cdblen = xs->cmdlen;
+ ecb->sense = KVTOPHYS(&(ecb->ecb_sense));
+ ecb->senselen = sizeof(ecb->ecb_sense);
+ ecb->status = KVTOPHYS(&(ecb->ecb_status));
+
+ if (xs->datalen) { /* should use S/G only if not zero length */
+ ecb->data = KVTOPHYS(ecb->ahb_dma);
+ sg = ecb->ahb_dma;
+ seg = 0;
+#ifdef TFS
+ if (flags & SCSI_DATA_UIO) {
+ iovp = ((struct uio *) xs->data)->uio_iov;
+ datalen = ((struct uio *) xs->data)->uio_iovcnt;
+ xs->datalen = 0;
+ while ((datalen) && (seg < AHB_NSEG)) {
+ sg->addr = (physaddr) iovp->iov_base;
+ xs->datalen += sg->len = iovp->iov_len;
+ SC_DEBUGN(xs->sc_link, SDEV_DB4,
+ ("(0x%x@0x%x)", iovp->iov_len
+ ,iovp->iov_base));
+ sg++;
+ iovp++;
+ seg++;
+ datalen--;
+ }
+ }
+ else
+#endif /*TFS */
+ {
+ /*
+ * Set up the scatter gather block
+ */
+
+ SC_DEBUG(xs->sc_link, SDEV_DB4,
+ ("%d @0x%x:- ", xs->datalen, xs->data));
+ datalen = xs->datalen;
+ thiskv = (int) xs->data;
+ thisphys = KVTOPHYS(thiskv);
+
+ while ((datalen) && (seg < AHB_NSEG)) {
+ bytes_this_seg = 0;
+
+ /* put in the base address */
+ sg->addr = thisphys;
+
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("0x%x", thisphys));
+
+ /* do it at least once */
+ nextphys = thisphys;
+ while ((datalen) && (thisphys == nextphys)) {
+ /*
+ * This page is contiguous (physically) with
+ * the the last, just extend the length
+ */
+ /* how far to the end of the page */
+ nextphys = (thisphys & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ bytes_this_page = nextphys - thisphys;
+ /**** or the data ****/
+ bytes_this_page = min(bytes_this_page
+ ,datalen);
+ bytes_this_seg += bytes_this_page;
+ datalen -= bytes_this_page;
+
+ /* get more ready for the next page */
+ thiskv = (thiskv & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ if (datalen)
+ thisphys = KVTOPHYS(thiskv);
+ }
+ /*
+ * next page isn't contiguous, finish the seg
+ */
+ SC_DEBUGN(xs->sc_link, SDEV_DB4,
+ ("(0x%x)", bytes_this_seg));
+ sg->len = bytes_this_seg;
+ sg++;
+ seg++;
+ }
+ } /*end of iov/kv decision */
+ ecb->datalen = seg * sizeof(struct ahb_dma_seg);
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("\n"));
+ if (datalen) { /* there's still data, must have run out of segs! */
+ printf("ahb_scsi_cmd%d: more than %d DMA segs\n",
+ unit, AHB_NSEG);
+ xs->error = XS_DRIVER_STUFFUP;
+ ahb_free_ecb(unit, ecb, flags);
+ return (HAD_ERROR);
+ }
+ } else { /* No data xfer, use non S/G values */
+ ecb->data = (physaddr) 0;
+ ecb->datalen = 0;
+ } ecb->chain = (physaddr) 0;
+ /*
+ * Put the scsi command in the ecb and start it
+ */
+ bcopy(xs->cmd, ecb->cdb, xs->cmdlen);
+ /*
+ * Usually return SUCCESSFULLY QUEUED
+ */
+ if (!(flags & SCSI_NOMASK)) {
+ s = splbio();
+ ahb_send_mbox(unit, OP_START_ECB, xs->sc_link->target, ecb);
+ timeout((timeout_t)ahb_timeout, (caddr_t)ecb, (xs->timeout * hz) / 1000);
+ splx(s);
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_sent\n"));
+ return (SUCCESSFULLY_QUEUED);
+ }
+ /*
+ * If we can't use interrupts, poll on completion
+ */
+ ahb_send_mbox(unit, OP_START_ECB, xs->sc_link->target, ecb);
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_wait\n"));
+ do {
+ if (ahb_poll(unit, xs->timeout)) {
+ if (!(xs->flags & SCSI_SILENT))
+ printf("cmd fail\n");
+ ahb_send_mbox(unit, OP_ABORT_ECB, xs->sc_link->target, ecb);
+ if (ahb_poll(unit, 2000)) {
+ printf("abort failed in wait\n");
+ ahb_free_ecb(unit, ecb, flags);
+ }
+ xs->error = XS_DRIVER_STUFFUP;
+ return (HAD_ERROR);
+ }
+ } while (!(xs->flags & ITSDONE)); /* something (?) else finished */
+ if (xs->error) {
+ return (HAD_ERROR);
+ }
+ return (COMPLETE);
+}
+
+void
+ahb_timeout(caddr_t arg1)
+{
+ struct ecb * ecb = (struct ecb *)arg1;
+ int unit;
+ struct ahb_data *ahb;
+ int s = splbio();
+
+ unit = ecb->xs->sc_link->adapter_unit;
+ ahb = ahbdata[unit];
+ printf("ahb%d:%d:%d (%s%d) timed out ", unit
+ ,ecb->xs->sc_link->target
+ ,ecb->xs->sc_link->lun
+ ,ecb->xs->sc_link->device->name
+ ,ecb->xs->sc_link->dev_unit);
+
+#ifdef AHBDEBUG
+ if (ahb_debug & AHB_SHOWECBS)
+ ahb_print_active_ecb(unit);
+#endif /*AHBDEBUG */
+
+ /*
+ * If it's immediate, don't try abort it
+ */
+ if (ecb->flags & ECB_IMMED) {
+ ecb->xs->retries = 0; /* I MEAN IT ! */
+ ecb->flags |= ECB_IMMED_FAIL;
+ ahb_done(unit, ecb, FAIL);
+ splx(s);
+ return;
+ }
+ /*
+ * If it has been through before, then
+ * a previous abort has failed, don't
+ * try abort again
+ */
+ if (ecb->flags == ECB_ABORTED) {
+ /*
+ * abort timed out
+ */
+ printf("AGAIN");
+ ecb->xs->retries = 0; /* I MEAN IT ! */
+ ecb->ecb_status.ha_status = HS_CMD_ABORTED_HOST;
+ ahb_done(unit, ecb, FAIL);
+ } else { /* abort the operation that has timed out */
+ printf("\n");
+ ahb_send_mbox(unit, OP_ABORT_ECB, ecb->xs->sc_link->target, ecb);
+ /* 2 secs for the abort */
+ timeout((timeout_t)ahb_timeout, (caddr_t)ecb, 2 * hz);
+ ecb->flags = ECB_ABORTED;
+ }
+ splx(s);
+}
+
+#ifdef AHBDEBUG
+void
+ahb_print_ecb(ecb)
+ struct ecb *ecb;
+{
+ printf("ecb:%x op:%x cmdlen:%d senlen:%d\n"
+ ,ecb
+ ,ecb->opcode
+ ,ecb->cdblen
+ ,ecb->senselen);
+ printf(" datlen:%d hstat:%x tstat:%x flags:%x\n"
+ ,ecb->datalen
+ ,ecb->ecb_status.ha_status
+ ,ecb->ecb_status.targ_status
+ ,ecb->flags);
+ show_scsi_cmd(ecb->xs);
+}
+
+void
+ahb_print_active_ecb(int unit)
+{
+ struct ahb_data *ahb = ahbdata[unit];
+ struct ecb *ecb;
+ int i = 0;
+
+ while (i < ECB_HASH_SIZE) {
+ ecb = ahb->ecbhash[i];
+ while (ecb) {
+ if (ecb->flags != ECB_FREE) {
+ ahb_print_ecb(ecb);
+ }
+ ecb = ecb->nexthash;
+ } i++;
+ }
+}
+#endif /*AHBDEBUG */
+#endif /*KERNEL */
diff --git a/sys/i386/isa/bt742a.c b/sys/i386/isa/bt742a.c
new file mode 100644
index 0000000..9a53a7a
--- /dev/null
+++ b/sys/i386/isa/bt742a.c
@@ -0,0 +1,1533 @@
+/*
+ * Written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * $Id: bt742a.c,v 1.14 1994/03/24 02:22:58 davidg Exp $
+ */
+
+/*
+ * bt742a SCSI driver
+ */
+
+#include <sys/types.h>
+
+#ifdef KERNEL /* don't laugh.. it compiles to a program too.. look */
+#include <bt.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#endif /* KERNEL */
+
+#include <i386/isa/isa_device.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#ifdef KERNEL
+#include "ddb.h"
+#include "kernel.h"
+#else /*KERNEL */
+#define NBT 1
+#endif /*KERNEL */
+
+#ifndef NetBSD
+typedef timeout_func_t timeout_t;
+#endif
+
+typedef unsigned long int physaddr;
+
+/*
+ * I/O Port Interface
+ */
+
+#define BT_BASE bt->bt_base
+#define BT_CTRL_STAT_PORT (BT_BASE + 0x0) /* control & status */
+#define BT_CMD_DATA_PORT (BT_BASE + 0x1) /* cmds and datas */
+#define BT_INTR_PORT (BT_BASE + 0x2) /* Intr. stat */
+
+/*
+ * BT_CTRL_STAT bits (write)
+ */
+
+#define BT_HRST 0x80 /* Hardware reset */
+#define BT_SRST 0x40 /* Software reset */
+#define BT_IRST 0x20 /* Interrupt reset */
+#define BT_SCRST 0x10 /* SCSI bus reset */
+
+/*
+ * BT_CTRL_STAT bits (read)
+ */
+
+#define BT_STST 0x80 /* Self test in Progress */
+#define BT_DIAGF 0x40 /* Diagnostic Failure */
+#define BT_INIT 0x20 /* Mbx Init required */
+#define BT_IDLE 0x10 /* Host Adapter Idle */
+#define BT_CDF 0x08 /* cmd/data out port full */
+#define BT_DF 0x04 /* Data in port full */
+#define BT_INVDCMD 0x01 /* Invalid command */
+
+/*
+ * BT_CMD_DATA bits (write)
+ */
+
+#define BT_NOP 0x00 /* No operation */
+#define BT_MBX_INIT 0x01 /* Mbx initialization */
+#define BT_START_SCSI 0x02 /* start scsi command */
+#define BT_START_BIOS 0x03 /* start bios command */
+#define BT_INQUIRE 0x04 /* Adapter Inquiry */
+#define BT_MBO_INTR_EN 0x05 /* Enable MBO available interrupt */
+#define BT_SEL_TIMEOUT_SET 0x06 /* set selection time-out */
+#define BT_BUS_ON_TIME_SET 0x07 /* set bus-on time */
+#define BT_BUS_OFF_TIME_SET 0x08 /* set bus-off time */
+#define BT_SPEED_SET 0x09 /* set transfer speed */
+#define BT_DEV_GET 0x0a /* return installed devices */
+#define BT_CONF_GET 0x0b /* return configuration data */
+#define BT_TARGET_EN 0x0c /* enable target mode */
+#define BT_SETUP_GET 0x0d /* return setup data */
+#define BT_WRITE_CH2 0x1a /* write channel 2 buffer */
+#define BT_READ_CH2 0x1b /* read channel 2 buffer */
+#define BT_WRITE_FIFO 0x1c /* write fifo buffer */
+#define BT_READ_FIFO 0x1d /* read fifo buffer */
+#define BT_ECHO 0x1e /* Echo command data */
+#define BT_MBX_INIT_EXTENDED 0x81 /* Mbx initialization */
+#define BT_INQUIRE_EXTENDED 0x8D /* Adapter Setup Inquiry */
+
+/* Follows command appeared at FirmWare 3.31 */
+#define BT_ROUND_ROBIN 0x8f /* Enable/Disable(default) round robin */
+#define BT_DISABLE 0x00 /* Parameter value for Disable */
+#define BT_ENABLE 0x01 /* Parameter value for Enable */
+
+struct bt_cmd_buf {
+ u_char byte[16];
+};
+
+/*
+ * BT_INTR_PORT bits (read)
+ */
+
+#define BT_ANY_INTR 0x80 /* Any interrupt */
+#define BT_SCRD 0x08 /* SCSI reset detected */
+#define BT_HACC 0x04 /* Command complete */
+#define BT_MBOA 0x02 /* MBX out empty */
+#define BT_MBIF 0x01 /* MBX in full */
+
+/*
+ * Mail box defs etc.
+ * these could be bigger but we need the bt_data to fit on a single page..
+ */
+
+#define BT_MBX_SIZE 16 /* mail box size (MAX 255 MBxs) */
+ /* don't need that many really */
+#define BT_CCB_MAX 32 /* store up to 32CCBs at any one time */
+ /* in bt742a H/W ( Not MAX ? ) */
+#define CCB_HASH_SIZE 32 /* when we have a physical addr. for */
+ /* a ccb and need to find the ccb in */
+ /* space, look it up in the hash table */
+#define CCB_HASH_SHIFT 9 /* only hash on multiples of 512 */
+#define CCB_HASH(x) ((((long int)(x))>>CCB_HASH_SHIFT) % CCB_HASH_SIZE)
+
+#define bt_nextmbx( wmb, mbx, mbio ) \
+ if ( (wmb) == &((mbx)->mbio[BT_MBX_SIZE - 1 ]) ) \
+ (wmb) = &((mbx)->mbio[0]); \
+ else \
+ (wmb)++;
+
+typedef struct bt_mbx_out {
+ physaddr ccb_addr;
+ unsigned char dummy[3];
+ unsigned char cmd;
+} BT_MBO;
+
+typedef struct bt_mbx_in {
+ physaddr ccb_addr;
+ unsigned char btstat;
+ unsigned char sdstat;
+ unsigned char dummy;
+ unsigned char stat;
+} BT_MBI;
+
+struct bt_mbx {
+ BT_MBO mbo[BT_MBX_SIZE];
+ BT_MBI mbi[BT_MBX_SIZE];
+ BT_MBO *tmbo; /* Target Mail Box out */
+ BT_MBI *tmbi; /* Target Mail Box in */
+};
+
+/*
+ * mbo.cmd values
+ */
+
+#define BT_MBO_FREE 0x0 /* MBO entry is free */
+#define BT_MBO_START 0x1 /* MBO activate entry */
+#define BT_MBO_ABORT 0x2 /* MBO abort entry */
+
+/*
+ * mbi.stat values
+ */
+
+#define BT_MBI_FREE 0x0 /* MBI entry is free */
+#define BT_MBI_OK 0x1 /* completed without error */
+#define BT_MBI_ABORT 0x2 /* aborted ccb */
+#define BT_MBI_UNKNOWN 0x3 /* Tried to abort invalid CCB */
+#define BT_MBI_ERROR 0x4 /* Completed with error */
+
+#if defined(BIG_DMA)
+WARNING...THIS WON'T WORK(won't fit on 1 page)
+/* #define BT_NSEG 2048*/ /* Number of scatter gather segments - to much vm */
+#define BT_NSEG 128
+#else
+#define BT_NSEG 33
+#endif /* BIG_DMA */
+
+struct bt_scat_gath {
+ unsigned long seg_len;
+ physaddr seg_addr;
+};
+
+struct bt_ccb {
+ unsigned char opcode;
+ unsigned char:3, data_in:1, data_out:1,:3;
+ unsigned char scsi_cmd_length;
+ unsigned char req_sense_length;
+ /*------------------------------------longword boundary */
+ unsigned long data_length;
+ /*------------------------------------longword boundary */
+ physaddr data_addr;
+ /*------------------------------------longword boundary */
+ unsigned char dummy[2];
+ unsigned char host_stat;
+ unsigned char target_stat;
+ /*------------------------------------longword boundary */
+ unsigned char target;
+ unsigned char lun;
+ unsigned char scsi_cmd[12]; /* 12 bytes (bytes only) */
+ unsigned char dummy2[1];
+ unsigned char link_id;
+ /*------------------------------------4 longword boundary */
+ physaddr link_addr;
+ /*------------------------------------longword boundary */
+ physaddr sense_ptr;
+/*-----end of HW fields-------------------------------longword boundary */
+ struct scsi_sense_data scsi_sense;
+ /*------------------------------------longword boundary */
+ struct bt_scat_gath scat_gath[BT_NSEG];
+ /*------------------------------------longword boundary */
+ struct bt_ccb *next;
+ /*------------------------------------longword boundary */
+ struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */
+ /*------------------------------------longword boundary */
+ struct bt_mbx_out *mbx; /* pointer to mail box */
+ /*------------------------------------longword boundary */
+ int flags;
+#define CCB_FREE 0
+#define CCB_ACTIVE 1
+#define CCB_ABORTED 2
+ /*------------------------------------longword boundary */
+ struct bt_ccb *nexthash; /* if two hash the same */
+ /*------------------------------------longword boundary */
+ physaddr hashkey; /*physaddr of this ccb */
+ /*------------------------------------longword boundary */
+};
+
+/*
+ * opcode fields
+ */
+
+#define BT_INITIATOR_CCB 0x00 /* SCSI Initiator CCB */
+#define BT_TARGET_CCB 0x01 /* SCSI Target CCB */
+#define BT_INIT_SCAT_GATH_CCB 0x02 /* SCSI Initiator with scattter gather */
+#define BT_RESET_CCB 0x81 /* SCSI Bus reset */
+
+/*
+ * bt_ccb.host_stat values
+ */
+
+#define BT_OK 0x00 /* cmd ok */
+#define BT_LINK_OK 0x0a /* Link cmd ok */
+#define BT_LINK_IT 0x0b /* Link cmd ok + int */
+#define BT_SEL_TIMEOUT 0x11 /* Selection time out */
+#define BT_OVER_UNDER 0x12 /* Data over/under run */
+#define BT_BUS_FREE 0x13 /* Bus dropped at unexpected time */
+#define BT_INV_BUS 0x14 /* Invalid bus phase/sequence */
+#define BT_BAD_MBO 0x15 /* Incorrect MBO cmd */
+#define BT_BAD_CCB 0x16 /* Incorrect ccb opcode */
+#define BT_BAD_LINK 0x17 /* Not same values of LUN for links */
+#define BT_INV_TARGET 0x18 /* Invalid target direction */
+#define BT_CCB_DUP 0x19 /* Duplicate CCB received */
+#define BT_INV_CCB 0x1a /* Invalid CCB or segment list */
+#define BT_ABORTED 42 /* pseudo value from driver */
+
+struct bt_boardID {
+ u_char board_type;
+ u_char custom_feture;
+ char firm_revision;
+ u_char firm_version;
+};
+
+struct bt_setup {
+ u_char sync_neg:1;
+ u_char parity:1;
+ u_char :6;
+ u_char speed;
+ u_char bus_on;
+ u_char bus_off;
+ u_char num_mbx;
+ u_char mbx[3]; /*XXX */
+ /* doesn't make sense with 32bit addresses */
+ struct {
+ u_char offset:4;
+ u_char period:3;
+ u_char valid:1;
+ } sync[8];
+ u_char disc_sts;
+};
+
+struct bt_config {
+ u_char chan;
+ u_char intr;
+ u_char scsi_dev:3;
+ u_char :5;
+};
+
+#define INT9 0x01
+#define INT10 0x02
+#define INT11 0x04
+#define INT12 0x08
+#define INT14 0x20
+#define INT15 0x40
+
+#define EISADMA 0x00
+#define CHAN0 0x01
+#define CHAN5 0x20
+#define CHAN6 0x40
+#define CHAN7 0x80
+
+#define KVTOPHYS(x) vtophys(x)
+#define PAGESIZ 4096
+#define INVALIDATE_CACHE {asm volatile( ".byte 0x0F ;.byte 0x08" ); }
+
+u_char bt_scratch_buf[256];
+
+struct bt_data {
+ short bt_base; /* base port for each board */
+ struct bt_mbx bt_mbx; /* all our mailboxes */
+ struct bt_ccb *bt_ccb_free; /* list of free CCBs */
+ struct bt_ccb *ccbhash[CCB_HASH_SIZE]; /* phys to kv hash */
+ int bt_int; /* int. read off board */
+ int bt_dma; /* DMA channel read of board */
+ int bt_scsi_dev; /* adapters scsi id */
+ int numccbs; /* how many we have malloc'd */
+ struct scsi_link sc_link; /* prototype for devs */
+} *btdata[NBT];
+
+/***********debug values *************/
+#define BT_SHOWCCBS 0x01
+#define BT_SHOWINTS 0x02
+#define BT_SHOWCMDS 0x04
+#define BT_SHOWMISC 0x08
+int bt_debug = 0;
+
+#ifdef KERNEL
+int btprobe();
+int btattach();
+int btintr();
+int32 bt_scsi_cmd();
+void bt_timeout(caddr_t);
+void bt_inquire_setup_information();
+void bt_done();
+void btminphys();
+u_int32 bt_adapter_info();
+struct bt_ccb *bt_get_ccb();
+struct bt_ccb *bt_ccb_phys_kv();
+
+static int btunit = 0;
+
+struct isa_driver btdriver =
+{
+ btprobe,
+ btattach,
+ "bt"
+};
+
+struct scsi_adapter bt_switch =
+{
+ bt_scsi_cmd,
+ btminphys,
+ 0,
+ 0,
+ bt_adapter_info,
+ "bt",
+ 0, 0
+};
+
+/* the below structure is so we have a default dev struct for out link struct */
+struct scsi_device bt_dev =
+{
+ NULL, /* Use default error handler */
+ NULL, /* have a queue, served by this */
+ NULL, /* have no async handler */
+ NULL, /* Use default 'done' routine */
+ "bt",
+ 0,
+ 0, 0
+};
+
+#endif /*KERNEL */
+
+#define BT_RESET_TIMEOUT 1000
+#ifndef KERNEL
+main()
+{
+ printf("bt_data is %d bytes\n", sizeof(struct bt_data));
+ printf("bt_ccb is %d bytes\n", sizeof(struct bt_ccb));
+ printf("bt_mbx is %d bytes\n", sizeof(struct bt_mbx));
+}
+
+#else /*KERNEL */
+
+/*
+ * bt_cmd(unit,icnt, ocnt,wait, retval, opcode, args)
+ *
+ * Activate Adapter command
+ * icnt: number of args (outbound bytes written after opcode)
+ * ocnt: number of expected returned bytes
+ * wait: number of seconds to wait for response
+ * retval: buffer where to place returned bytes
+ * opcode: opcode BT_NOP, BT_MBX_INIT, BT_START_SCSI ...
+ * args: parameters
+ *
+ * Performs an adapter command through the ports. Not to be confused with a
+ * scsi command, which is read in via the dma; one of the adapter commands
+ * tells it to read in a scsi command.
+ */
+int
+bt_cmd(unit, icnt, ocnt, wait, retval, opcode, args)
+ int unit;
+ int icnt;
+ int ocnt;
+ int wait;
+ u_char *retval;
+ unsigned opcode;
+ u_char args;
+{
+ struct bt_data *bt = btdata[unit];
+ unsigned *ic = &opcode;
+ u_char oc;
+ register i;
+ int sts;
+
+ /*
+ * multiply the wait argument by a big constant
+ * zero defaults to 1
+ */
+ if (wait)
+ wait *= 100000;
+ else
+ wait = 100000;
+ /*
+ * Wait for the adapter to go idle, unless it's one of
+ * the commands which don't need this
+ */
+ if (opcode != BT_MBX_INIT && opcode != BT_START_SCSI) {
+ i = 100000; /* 1 sec? */
+ while (--i) {
+ sts = inb(BT_CTRL_STAT_PORT);
+ if (sts & BT_IDLE) {
+ break;
+ }
+ DELAY(10);
+ }
+ if (i == 0) {
+ printf("bt%d: bt_cmd, host not idle(0x%x)\n", unit, sts);
+ return (ENXIO);
+ }
+ }
+ /*
+ * Now that it is idle, if we expect output, preflush the
+ * queue feeding to us.
+ */
+ if (ocnt) {
+ while ((inb(BT_CTRL_STAT_PORT)) & BT_DF)
+ inb(BT_CMD_DATA_PORT);
+ }
+ /*
+ * Output the command and the number of arguments given
+ * for each byte, first check the port is empty.
+ */
+ icnt++;
+ /* include the command */
+ while (icnt--) {
+ sts = inb(BT_CTRL_STAT_PORT);
+ for (i = wait; i; i--) {
+ sts = inb(BT_CTRL_STAT_PORT);
+ if (!(sts & BT_CDF))
+ break;
+ DELAY(10);
+ }
+ if (i == 0) {
+ printf("bt%d: bt_cmd, cmd/data port full\n", unit);
+ outb(BT_CTRL_STAT_PORT, BT_SRST);
+ return (ENXIO);
+ }
+ outb(BT_CMD_DATA_PORT, (u_char) (*ic++));
+ }
+ /*
+ * If we expect input, loop that many times, each time,
+ * looking for the data register to have valid data
+ */
+ while (ocnt--) {
+ sts = inb(BT_CTRL_STAT_PORT);
+ for (i = wait; i; i--) {
+ sts = inb(BT_CTRL_STAT_PORT);
+ if (sts & BT_DF)
+ break;
+ DELAY(10);
+ }
+ if (i == 0) {
+ printf("bt%d: bt_cmd, cmd/data port empty %d\n",
+ unit, ocnt);
+ return (ENXIO);
+ }
+ oc = inb(BT_CMD_DATA_PORT);
+ if (retval)
+ *retval++ = oc;
+ }
+ /*
+ * Wait for the board to report a finised instruction
+ */
+ i = 100000; /* 1 sec? */
+ while (--i) {
+ sts = inb(BT_INTR_PORT);
+ if (sts & BT_HACC) {
+ break;
+ }
+ DELAY(10);
+ }
+ if (i == 0) {
+ printf("bt%d: bt_cmd, host not finished(0x%x)\n", unit, sts);
+ return (ENXIO);
+ }
+ outb(BT_CTRL_STAT_PORT, BT_IRST);
+ return (0);
+}
+
+/*
+ * Check if the device can be found at the port given
+ * and if so, set it up ready for further work
+ * as an argument, takes the isa_device structure from
+ * autoconf.c
+ */
+int
+btprobe(dev)
+ struct isa_device *dev;
+{
+ /*
+ * find unit and check we have that many defined
+ */
+ int unit = btunit;
+ struct bt_data *bt;
+
+ if (unit >= NBT) {
+ printf("bt%d: unit number too high\n", unit);
+ return 0;
+ }
+ /*
+ * Allocate a storage area for us
+ */
+ if (btdata[unit]) {
+ printf("bt%d: memory already allocated\n", unit);
+ return 0;
+ }
+ bt = malloc(sizeof(struct bt_data), M_TEMP, M_NOWAIT);
+ if (!bt) {
+ printf("bt%d: cannot malloc!\n", unit);
+ return 0;
+ }
+ bzero(bt, sizeof(struct bt_data));
+ btdata[unit] = bt;
+ bt->bt_base = dev->id_iobase;
+
+ /*
+ * Try initialise a unit at this location
+ * sets up dma and bus speed, loads bt->bt_int
+ */
+ if (bt_init(unit) != 0) {
+ btdata[unit] = NULL;
+ free(bt, M_TEMP);
+ return 0;
+ }
+ /*
+ * If it's there, put in it's interrupt vectors
+ */ dev->id_unit = unit;
+ dev->id_irq = (1 << bt->bt_int);
+ dev->id_drq = bt->bt_dma;
+
+ btunit++;
+ return 1;
+}
+
+/*
+ * Attach all the sub-devices we can find
+ */
+int
+btattach(dev)
+ struct isa_device *dev;
+{
+ int unit = dev->id_unit;
+ struct bt_data *bt = btdata[unit];
+
+ /*
+ * fill in the prototype scsi_link.
+ */
+ bt->sc_link.adapter_unit = unit;
+ bt->sc_link.adapter_targ = bt->bt_scsi_dev;
+ bt->sc_link.adapter = &bt_switch;
+ bt->sc_link.device = &bt_dev;
+ bt->sc_link.flags = SDEV_BOUNCE;
+
+ /*
+ * ask the adapter what subunits are present
+ */
+ scsi_attachdevs(&(bt->sc_link));
+ return 1;
+}
+
+/*
+ * Return some information to the caller about the adapter and its
+ * capabilities.
+ */
+u_int32
+bt_adapter_info(unit)
+ int unit;
+{
+ return (2); /* 2 outstanding requests at a time per device */
+}
+
+/*
+ * Catch an interrupt from the adaptor
+ */
+int
+btintr(unit)
+ int unit;
+{
+ struct bt_data *bt = btdata[unit];
+ BT_MBI *wmbi;
+ struct bt_mbx *wmbx;
+ struct bt_ccb *ccb;
+ unsigned char stat;
+ int i, wait;
+ int found = 0;
+
+#ifdef UTEST
+ printf("btintr ");
+#endif
+ /*
+ * First acknowlege the interrupt, Then if it's
+ * not telling about a completed operation
+ * just return.
+ */
+ stat = inb(BT_INTR_PORT);
+
+ /* Mail Box out empty ? */
+ if (stat & BT_MBOA) {
+ printf("bt%d: Available Free mbo post\n", unit);
+ /* Disable MBO available interrupt */
+ outb(BT_CMD_DATA_PORT, BT_MBO_INTR_EN);
+ wait = 100000; /* 1 sec enough? */
+ for (i = wait; i; i--) {
+ if (!(inb(BT_CTRL_STAT_PORT) & BT_CDF))
+ break;
+ DELAY(10);
+ }
+ if (i == 0) {
+ printf("bt%d: bt_intr, cmd/data port full\n", unit);
+ outb(BT_CTRL_STAT_PORT, BT_SRST);
+ return 1;
+ }
+ outb(BT_CMD_DATA_PORT, 0x00); /* Disable */
+ wakeup((caddr_t)&bt->bt_mbx);
+ outb(BT_CTRL_STAT_PORT, BT_IRST);
+ return 1;
+ }
+ if (!(stat & BT_MBIF)) {
+ outb(BT_CTRL_STAT_PORT, BT_IRST);
+ return 1;
+ }
+ /*
+ * If it IS then process the competed operation
+ */
+ wmbx = &bt->bt_mbx;
+ wmbi = wmbx->tmbi;
+ AGAIN:
+ while (wmbi->stat != BT_MBI_FREE) {
+ ccb = bt_ccb_phys_kv(bt, (wmbi->ccb_addr));
+ if (!ccb) {
+ wmbi->stat = BT_MBI_FREE;
+ printf("bt: BAD CCB ADDR!\n");
+ continue;
+ }
+ found++;
+ if ((stat = wmbi->stat) != BT_MBI_OK) {
+ switch (stat) {
+ case BT_MBI_ABORT:
+#ifdef UTEST
+ if (bt_debug & BT_SHOWMISC)
+ printf("abort ");
+#endif
+ ccb->host_stat = BT_ABORTED;
+ break;
+
+ case BT_MBI_UNKNOWN:
+ ccb = (struct bt_ccb *) 0;
+#ifdef UTEST
+ if (bt_debug & BT_SHOWMISC)
+ printf("unknown ccb for abort");
+#endif
+ break;
+
+ case BT_MBI_ERROR:
+ break;
+
+ default:
+ panic("Impossible mbxi status");
+
+ }
+#ifdef UTEST
+ if ((bt_debug & BT_SHOWCMDS) && ccb) {
+ u_char *cp;
+ cp = ccb->scsi_cmd;
+ printf("op=%x %x %x %x %x %x\n",
+ cp[0], cp[1], cp[2],
+ cp[3], cp[4], cp[5]);
+ printf("stat %x for mbi addr = 0x%08x\n"
+ ,wmbi->stat, wmbi);
+ printf("addr = 0x%x\n", ccb);
+ }
+#endif
+ }
+ wmbi->stat = BT_MBI_FREE;
+ if (ccb) {
+ untimeout((timeout_t)bt_timeout, (caddr_t)ccb);
+ bt_done(unit, ccb);
+ }
+ /* Set the IN mail Box pointer for next */ bt_nextmbx(wmbi, wmbx, mbi);
+ }
+ if (!found) {
+ for (i = 0; i < BT_MBX_SIZE; i++) {
+ if (wmbi->stat != BT_MBI_FREE) {
+ found++;
+ break;
+ }
+ bt_nextmbx(wmbi, wmbx, mbi);
+ }
+ if (!found) {
+ printf("bt%d: mbi at 0x%08x should be found, stat=%02x..resync\n",
+ unit, wmbi, stat);
+ } else {
+ found = 0;
+ goto AGAIN;
+ }
+ }
+ wmbx->tmbi = wmbi;
+ outb(BT_CTRL_STAT_PORT, BT_IRST);
+ return 1;
+}
+
+/*
+ * A ccb is put onto the free list.
+ */
+void
+bt_free_ccb(unit, ccb, flags)
+ int unit;
+ struct bt_ccb *ccb;
+ int flags;
+{
+ struct bt_data *bt = btdata[unit];
+ unsigned int opri = 0;
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+
+ ccb->next = bt->bt_ccb_free;
+ bt->bt_ccb_free = ccb;
+ ccb->flags = CCB_FREE;
+ /*
+ * If there were none, wake anybody waiting for one to come free,
+ * starting with queued entries.
+ */
+ if (!ccb->next) {
+ wakeup((caddr_t)&bt->bt_ccb_free);
+ }
+
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+}
+
+/*
+ * Get a free ccb
+ *
+ * If there are none, see if we can allocate a new one. If so, put it in
+ * the hash table too otherwise either return an error or sleep.
+ */
+struct bt_ccb *
+bt_get_ccb(unit, flags)
+ int unit;
+ int flags;
+{
+ struct bt_data *bt = btdata[unit];
+ unsigned opri = 0;
+ struct bt_ccb *ccbp;
+ struct bt_mbx *wmbx; /* Mail Box pointer specified unit */
+ BT_MBO *wmbo; /* Out Mail Box pointer */
+ int hashnum;
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+ /*
+ * If we can and have to, sleep waiting for one to come free
+ * but only if we can't allocate a new one.
+ */
+ while (!(ccbp = bt->bt_ccb_free)) {
+ if (bt->numccbs < BT_CCB_MAX) {
+ if (ccbp = (struct bt_ccb *) malloc(sizeof(struct bt_ccb),
+ M_TEMP,
+ M_NOWAIT)) {
+ bzero(ccbp, sizeof(struct bt_ccb));
+ bt->numccbs++;
+ ccbp->flags = CCB_ACTIVE;
+ /*
+ * put in the phystokv hash table
+ * Never gets taken out.
+ */
+ ccbp->hashkey = KVTOPHYS(ccbp);
+ hashnum = CCB_HASH(ccbp->hashkey);
+ ccbp->nexthash = bt->ccbhash[hashnum];
+ bt->ccbhash[hashnum] = ccbp;
+ } else {
+ printf("bt%d: Can't malloc CCB\n", unit);
+ }
+ goto gottit;
+ } else {
+ if (!(flags & SCSI_NOSLEEP)) {
+ tsleep((caddr_t)&bt->bt_ccb_free, PRIBIO,
+ "btccb", 0);
+ }
+ }
+ }
+ if (ccbp) {
+ /* Get CCB from from free list */
+ bt->bt_ccb_free = ccbp->next;
+ ccbp->flags = CCB_ACTIVE;
+ }
+ gottit:
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+
+ return (ccbp);
+}
+
+/*
+ * given a physical address, find the ccb that
+ * it corresponds to:
+ */
+struct bt_ccb *
+bt_ccb_phys_kv(bt, ccb_phys)
+ struct bt_data *bt;
+ physaddr ccb_phys;
+{
+ int hashnum = CCB_HASH(ccb_phys);
+ struct bt_ccb *ccbp = bt->ccbhash[hashnum];
+
+ while (ccbp) {
+ if (ccbp->hashkey == ccb_phys)
+ break;
+ ccbp = ccbp->nexthash;
+ }
+ return ccbp;
+}
+
+/*
+ * Get a MBO and then Send it
+ */
+BT_MBO *
+bt_send_mbo(int unit, int flags, int cmd, struct bt_ccb *ccb)
+{
+ struct bt_data *bt = btdata[unit];
+ unsigned opri = 0;
+ BT_MBO *wmbo; /* Mail Box Out pointer */
+ struct bt_mbx *wmbx; /* Mail Box pointer specified unit */
+ int i, wait;
+
+ wmbx = &bt->bt_mbx;
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+
+ /* Get the Target OUT mail Box pointer and move to Next */
+ wmbo = wmbx->tmbo;
+ wmbx->tmbo = (wmbo == &(wmbx->mbo[BT_MBX_SIZE - 1]) ?
+ &(wmbx->mbo[0]) : wmbo + 1);
+
+ /*
+ * Check the outmail box is free or not.
+ * Note: Under the normal operation, it shuld NOT happen to wait.
+ */
+ while (wmbo->cmd != BT_MBO_FREE) {
+ wait = 100000; /* 1 sec enough? */
+ /* Enable MBO available interrupt */
+ outb(BT_CMD_DATA_PORT, BT_MBO_INTR_EN);
+ for (i = wait; i; i--) {
+ if (!(inb(BT_CTRL_STAT_PORT) & BT_CDF))
+ break;
+ DELAY(10);
+ }
+ if (i == 0) {
+ printf("bt%d: bt_send_mbo, cmd/data port full\n", unit);
+ outb(BT_CTRL_STAT_PORT, BT_SRST);
+ return ((BT_MBO *) 0);
+ }
+ outb(BT_CMD_DATA_PORT, 0x01); /* Enable */
+ tsleep((caddr_t)wmbx, PRIBIO, "btsend", 0);
+ /* XXX */ /*can't do this! */
+ /* May be servicing an int */
+ }
+ /* Link CCB to the Mail Box */
+ wmbo->ccb_addr = KVTOPHYS(ccb);
+ ccb->mbx = wmbo;
+ wmbo->cmd = cmd;
+
+ /* Send it! */
+ outb(BT_CMD_DATA_PORT, BT_START_SCSI);
+
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+
+ return (wmbo);
+}
+
+/*
+ * We have a ccb which has been processed by the
+ * adaptor, now we look to see how the operation
+ * went. Wake up the owner if waiting
+ */
+void
+bt_done(unit, ccb)
+ int unit;
+ struct bt_ccb *ccb;
+{
+ struct bt_data *bt = btdata[unit];
+ struct scsi_sense_data *s1, *s2;
+ struct scsi_xfer *xs = ccb->xfer;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB2, ("bt_done\n"));
+ /*
+ * Otherwise, put the results of the operation
+ * into the xfer and call whoever started it
+ */
+ if ((ccb->host_stat != BT_OK || ccb->target_stat != SCSI_OK)
+ && (!(xs->flags & SCSI_ERR_OK))) {
+
+ s1 = &(ccb->scsi_sense);
+ s2 = &(xs->sense);
+
+ if (ccb->host_stat) {
+ switch (ccb->host_stat) {
+ case BT_ABORTED: /* No response */
+ case BT_SEL_TIMEOUT: /* No response */
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("timeout reported back\n"));
+ xs->error = XS_TIMEOUT;
+ break;
+ default: /* Other scsi protocol messes */
+ xs->error = XS_DRIVER_STUFFUP;
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("unexpected host_stat: %x\n",
+ ccb->host_stat));
+ }
+ } else {
+ switch (ccb->target_stat) {
+ case 0x02:
+ *s2 = *s1;
+ xs->error = XS_SENSE;
+ break;
+ case 0x08:
+ xs->error = XS_BUSY;
+ break;
+ default:
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("unexpected target_stat: %x\n",
+ ccb->target_stat));
+ xs->error = XS_DRIVER_STUFFUP;
+ }
+ }
+ } else { /* All went correctly OR errors expected */
+ xs->resid = 0;
+ }
+ xs->flags |= ITSDONE;
+ bt_free_ccb(unit, ccb, xs->flags);
+ scsi_done(xs);
+}
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+bt_init(unit)
+ int unit;
+{
+ struct bt_data *bt = btdata[unit];
+ unsigned char ad[4];
+ volatile int i, sts;
+ struct bt_config conf;
+
+ /*
+ * reset board, If it doesn't respond, assume
+ * that it's not there.. good for the probe
+ */
+
+ outb(BT_CTRL_STAT_PORT, BT_HRST | BT_SRST);
+
+ for (i = BT_RESET_TIMEOUT; i; i--) {
+ sts = inb(BT_CTRL_STAT_PORT);
+ if (sts == (BT_IDLE | BT_INIT))
+ break;
+ DELAY(1000);
+ }
+ if (i == 0) {
+#ifdef UTEST
+ printf("bt_init: No answer from bt742a board\n");
+#endif
+ return (ENXIO);
+ }
+ /*
+ * Assume we have a board at this stage
+ * setup dma channel from jumpers and save int
+ * level
+ */
+ printf("bt%d: reading board settings, ", unit);
+
+ bt_cmd(unit, 0, sizeof(conf), 0, &conf, BT_CONF_GET);
+ switch (conf.chan) {
+ case EISADMA:
+ bt->bt_dma = -1;
+ break;
+ case CHAN0:
+ outb(0x0b, 0x0c);
+ outb(0x0a, 0x00);
+ bt->bt_dma = 0;
+ break;
+ case CHAN5:
+ outb(0xd6, 0xc1);
+ outb(0xd4, 0x01);
+ bt->bt_dma = 5;
+ break;
+ case CHAN6:
+ outb(0xd6, 0xc2);
+ outb(0xd4, 0x02);
+ bt->bt_dma = 6;
+ break;
+ case CHAN7:
+ outb(0xd6, 0xc3);
+ outb(0xd4, 0x03);
+ bt->bt_dma = 7;
+ break;
+ default:
+ printf("illegal dma setting %x\n", conf.chan);
+ return (EIO);
+ }
+ if (bt->bt_dma == -1)
+ printf("eisa dma, ");
+ else
+ printf("dma=%d, ", bt->bt_dma);
+
+ switch (conf.intr) {
+ case INT9:
+ bt->bt_int = 9;
+ break;
+ case INT10:
+ bt->bt_int = 10;
+ break;
+ case INT11:
+ bt->bt_int = 11;
+ break;
+ case INT12:
+ bt->bt_int = 12;
+ break;
+ case INT14:
+ bt->bt_int = 14;
+ break;
+ case INT15:
+ bt->bt_int = 15;
+ break;
+ default:
+ printf("illegal int setting\n");
+ return (EIO);
+ }
+ printf("int=%d\n", bt->bt_int);
+
+ /* who are we on the scsi bus */
+ bt->bt_scsi_dev = conf.scsi_dev;
+ /*
+ * Initialize mail box
+ */
+ *((physaddr *) ad) = KVTOPHYS(&bt->bt_mbx);
+ bt_cmd(unit, 5, 0, 0, 0, BT_MBX_INIT_EXTENDED
+ ,BT_MBX_SIZE
+ ,ad[0]
+ ,ad[1]
+ ,ad[2]
+ ,ad[3]);
+
+ /*
+ * Set Pointer chain null for just in case
+ * Link the ccb's into a free-list W/O mbox
+ * Initialize mail box status to free
+ */
+ if (bt->bt_ccb_free != (struct bt_ccb *) 0) {
+ printf("bt%d: bt_ccb_free is NOT initialized but init here\n",
+ unit);
+ bt->bt_ccb_free = (struct bt_ccb *) 0;
+ }
+ for (i = 0; i < BT_MBX_SIZE; i++) {
+ bt->bt_mbx.mbo[i].cmd = BT_MBO_FREE;
+ bt->bt_mbx.mbi[i].stat = BT_MBI_FREE;
+ }
+ /*
+ * Set up initial mail box for round-robin operation.
+ */
+ bt->bt_mbx.tmbo = &bt->bt_mbx.mbo[0];
+ bt->bt_mbx.tmbi = &bt->bt_mbx.mbi[0];
+ bt_inquire_setup_information(unit);
+
+ /* Enable round-robin scheme - appeared at firmware rev. 3.31 */
+ bt_cmd(unit, 1, 0, 0, 0, BT_ROUND_ROBIN, BT_ENABLE);
+
+ /*
+ * Note that we are going and return (to probe)
+ */
+ return 0;
+}
+
+void
+bt_inquire_setup_information(unit)
+ int unit;
+{
+ struct bt_data *bt = btdata[unit];
+ struct bt_setup setup;
+ struct bt_boardID bID;
+ int i;
+
+ /* Inquire Board ID to Bt742 for firmware version */
+ bt_cmd(unit, 0, sizeof(bID), 0, &bID, BT_INQUIRE);
+ printf("bt%d: version %c.%c, ",
+ unit, bID.firm_revision, bID.firm_version);
+
+ /* Obtain setup information from Bt742. */
+ bt_cmd(unit, 1, sizeof(setup), 0, &setup, BT_SETUP_GET, sizeof(setup));
+
+ if (setup.sync_neg) {
+ printf("sync, ");
+ } else {
+ printf("async, ");
+ }
+ if (setup.parity) {
+ printf("parity, ");
+ } else {
+ printf("no parity, ");
+ }
+ printf("%d mbxs, %d ccbs\n", setup.num_mbx, bt->numccbs);
+
+ for (i = 0; i < 8; i++) {
+ if (!setup.sync[i].offset &&
+ !setup.sync[i].period &&
+ !setup.sync[i].valid)
+ continue;
+
+ printf("bt%d: dev%02d Offset=%d,Transfer period=%d, Synchronous? %s",
+ unit, i,
+ setup.sync[i].offset, setup.sync[i].period,
+ setup.sync[i].valid ? "Yes" : "No");
+ }
+}
+
+#ifndef min
+#define min(x,y) (x < y ? x : y)
+#endif /* min */
+
+void
+btminphys(bp)
+ struct buf *bp;
+{
+ if (bp->b_bcount > ((BT_NSEG - 1) * PAGESIZ)) {
+ bp->b_bcount = ((BT_NSEG - 1) * PAGESIZ);
+ }
+}
+
+/*
+ * start a scsi operation given the command and the data address. Also needs
+ * the unit, target and lu.
+ */
+int32
+bt_scsi_cmd(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_sense_data *s1, *s2;
+ struct bt_ccb *ccb;
+ struct bt_scat_gath *sg;
+ int seg; /* scatter gather seg being worked on */
+ int i = 0;
+ int c = 0;
+ int thiskv;
+ physaddr thisphys, nextphys;
+ int unit = xs->sc_link->adapter_unit;
+ int bytes_this_seg, bytes_this_page, datalen, flags;
+ struct iovec *iovp;
+ struct bt_data *bt = btdata[unit];
+ BT_MBO *mbo;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB2, ("bt_scsi_cmd\n"));
+ /*
+ * get a ccb (mbox-out) to use. If the transfer
+ * is from a buf (possibly from interrupt time)
+ * then we can't allow it to sleep
+ */
+ flags = xs->flags;
+ if (xs->bp)
+ flags |= (SCSI_NOSLEEP); /* just to be sure */
+ if (flags & ITSDONE) {
+ printf("bt%d: Already done?\n", unit);
+ xs->flags &= ~ITSDONE;
+ }
+ if (!(flags & INUSE)) {
+ printf("bt%d: Not in use?\n", unit);
+ xs->flags |= INUSE;
+ }
+ if (!(ccb = bt_get_ccb(unit, flags))) {
+ xs->error = XS_DRIVER_STUFFUP;
+ return (TRY_AGAIN_LATER);
+ }
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("start ccb(%x)\n", ccb));
+ /*
+ * Put all the arguments for the xfer in the ccb
+ */
+ ccb->xfer = xs;
+ if (flags & SCSI_RESET) {
+ ccb->opcode = BT_RESET_CCB;
+ } else {
+ /* can't use S/G if zero length */
+ ccb->opcode = (xs->datalen ?
+ BT_INIT_SCAT_GATH_CCB
+ : BT_INITIATOR_CCB);
+ }
+ ccb->target = xs->sc_link->target;
+ ccb->data_out = 0;
+ ccb->data_in = 0;
+ ccb->lun = xs->sc_link->lun;
+ ccb->scsi_cmd_length = xs->cmdlen;
+ ccb->sense_ptr = KVTOPHYS(&(ccb->scsi_sense));
+ ccb->req_sense_length = sizeof(ccb->scsi_sense);
+
+ if ((xs->datalen) && (!(flags & SCSI_RESET))) { /* can use S/G only if not zero length */
+ ccb->data_addr = KVTOPHYS(ccb->scat_gath);
+ sg = ccb->scat_gath;
+ seg = 0;
+#ifdef TFS
+ if (flags & SCSI_DATA_UIO) {
+ iovp = ((struct uio *) xs->data)->uio_iov;
+ datalen = ((struct uio *) xs->data)->uio_iovcnt;
+ xs->datalen = 0;
+ while ((datalen) && (seg < BT_NSEG)) {
+ sg->seg_addr = (physaddr) iovp->iov_base;
+ xs->datalen += sg->seg_len = iovp->iov_len;
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("(0x%x@0x%x)"
+ ,iovp->iov_len, iovp->iov_base));
+ sg++;
+ iovp++;
+ seg++;
+ datalen--;
+ }
+ } else
+#endif /* TFS */
+ {
+ /*
+ * Set up the scatter gather block
+ */
+
+ SC_DEBUG(xs->sc_link, SDEV_DB4,
+ ("%d @0x%x:- ", xs->datalen, xs->data));
+ datalen = xs->datalen;
+ thiskv = (int) xs->data;
+ thisphys = KVTOPHYS(thiskv);
+
+ while ((datalen) && (seg < BT_NSEG)) {
+ bytes_this_seg = 0;
+
+ /* put in the base address */
+ sg->seg_addr = thisphys;
+
+ SC_DEBUGN(xs->sc_link, SDEV_DB4,
+ ("0x%x", thisphys));
+
+ /* do it at least once */
+ nextphys = thisphys;
+ while ((datalen) && (thisphys == nextphys))
+ /*
+ * This page is contiguous (physically) with
+ * the the last, just extend the length
+ */
+ {
+
+ /* check it fits on the ISA bus */
+ if (thisphys > 0xFFFFFF)
+ {
+ printf("bt%d: DMA beyond"
+ " end Of ISA\n", unit);
+ xs->error = XS_DRIVER_STUFFUP;
+ bt_free_ccb(unit, ccb, flags);
+ return (HAD_ERROR);
+ }
+ /** how far to the end of the page ***/
+ /* how far to the end of the page */
+ nextphys = (thisphys & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ bytes_this_page = nextphys - thisphys;
+ /**** or the data ****/
+ bytes_this_page = min(bytes_this_page
+ ,datalen);
+ bytes_this_seg += bytes_this_page;
+ datalen -= bytes_this_page;
+
+ /* get more ready for the next page */
+ thiskv = (thiskv & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ if (datalen)
+ thisphys = KVTOPHYS(thiskv);
+ }
+ /*
+ * next page isn't contiguous, finish the seg
+ */
+ SC_DEBUGN(xs->sc_link, SDEV_DB4,
+ ("(0x%x)", bytes_this_seg));
+ sg->seg_len = bytes_this_seg;
+ sg++;
+ seg++;
+ }
+ }
+ /* end of iov/kv decision */
+ ccb->data_length = seg * sizeof(struct bt_scat_gath);
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("\n"));
+ if (datalen) {
+ /*
+ * there's still data, must have run out of segs!
+ */
+ printf("bt%d: bt_scsi_cmd, more than %d DMA segs\n",
+ unit, BT_NSEG);
+ xs->error = XS_DRIVER_STUFFUP;
+ bt_free_ccb(unit, ccb, flags);
+ return (HAD_ERROR);
+ }
+ } else { /* No data xfer, use non S/G values */
+ ccb->data_addr = (physaddr) 0;
+ ccb->data_length = 0;
+ }
+ ccb->link_id = 0;
+ ccb->link_addr = (physaddr) 0;
+ /*
+ * Put the scsi command in the ccb and start it
+ */
+ if (!(flags & SCSI_RESET)) {
+ bcopy(xs->cmd, ccb->scsi_cmd, ccb->scsi_cmd_length);
+ }
+ if (bt_send_mbo(unit, flags, BT_MBO_START, ccb) == (BT_MBO *) 0) {
+ xs->error = XS_DRIVER_STUFFUP;
+ bt_free_ccb(unit, ccb, flags);
+ return (TRY_AGAIN_LATER);
+ }
+ /*
+ * Usually return SUCCESSFULLY QUEUED
+ */
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_sent\n"));
+ if (!(flags & SCSI_NOMASK)) {
+ timeout((timeout_t)bt_timeout, (caddr_t)ccb, (xs->timeout * hz) / 1000);
+ return (SUCCESSFULLY_QUEUED);
+ }
+ /*
+ * If we can't use interrupts, poll on completion
+ */
+ return (bt_poll(unit, xs, ccb));
+}
+
+/*
+ * Poll a particular unit, looking for a particular xs
+ */
+int
+bt_poll(unit, xs, ccb)
+ int unit;
+ struct scsi_xfer *xs;
+ struct bt_ccb *ccb;
+{
+ struct bt_data *bt = btdata[unit];
+ int done = 0;
+ int count = xs->timeout;
+ u_char stat;
+
+ /* timeouts are in msec, so we loop in 1000 usec cycles */
+ while (count) {
+ /*
+ * If we had interrupts enabled, would we
+ * have got an interrupt?
+ */
+ stat = inb(BT_INTR_PORT);
+ if (stat & BT_ANY_INTR) {
+ btintr(unit);
+ }
+ if (xs->flags & ITSDONE) {
+ break;
+ }
+ DELAY(1000); /* only happens in boot so ok */
+ count--;
+ }
+ if (count == 0) {
+ /*
+ * We timed out, so call the timeout handler manually,
+ * accounting for the fact that the clock is not running yet
+ * by taking out the clock queue entry it makes.
+ */
+ bt_timeout((caddr_t)ccb);
+
+ /*
+ * because we are polling, take out the timeout entry
+ * bt_timeout made
+ */
+ untimeout((timeout_t)bt_timeout, (caddr_t)ccb);
+ count = 2000;
+ while (count) {
+ /*
+ * Once again, wait for the int bit
+ */
+ stat = inb(BT_INTR_PORT);
+ if (stat & BT_ANY_INTR) {
+ btintr(unit);
+ }
+ if (xs->flags & ITSDONE) {
+ break;
+ }
+ DELAY(1000); /* only happens in boot so ok */
+ count--;
+ }
+ if (count == 0) {
+ /*
+ * We timed out again... This is bad. Notice that
+ * this time there is no clock queue entry to remove.
+ */
+ bt_timeout((caddr_t)ccb);
+ }
+ }
+ if (xs->error)
+ return (HAD_ERROR);
+ return (COMPLETE);
+}
+
+void
+bt_timeout(caddr_t arg1)
+{
+ struct bt_ccb * ccb = (struct bt_ccb *)arg1;
+ int unit;
+ struct bt_data *bt;
+ int s = splbio();
+
+ unit = ccb->xfer->sc_link->adapter_unit;
+ bt = btdata[unit];
+ printf("bt%d:%d:%d (%s%d) timed out ", unit
+ ,ccb->xfer->sc_link->target
+ ,ccb->xfer->sc_link->lun
+ ,ccb->xfer->sc_link->device->name
+ ,ccb->xfer->sc_link->dev_unit);
+
+#ifdef UTEST
+ bt_print_active_ccbs(unit);
+#endif
+
+ /*
+ * If the ccb's mbx is not free, then the board has gone Far East?
+ */
+ if (bt_ccb_phys_kv(bt, ccb->mbx->ccb_addr) == ccb &&
+ ccb->mbx->cmd != BT_MBO_FREE) {
+ printf("bt%d: not taking commands!\n", unit);
+ Debugger("bt742a");
+ }
+ /*
+ * If it has been through before, then
+ * a previous abort has failed, don't
+ * try abort again
+ */
+ if (ccb->flags == CCB_ABORTED) {
+ /*
+ * abort timed out
+ */
+ printf("bt%d: Abort Operation has timed out\n", unit);
+ ccb->xfer->retries = 0; /* I MEAN IT ! */
+ ccb->host_stat = BT_ABORTED;
+ bt_done(unit, ccb);
+ } else { /* abort the operation that has timed out */
+ printf("bt%d: Try to abort\n", unit);
+ bt_send_mbo(unit, ~SCSI_NOMASK,
+ BT_MBO_ABORT, ccb);
+ /* 2 secs for the abort */
+ timeout((timeout_t)bt_timeout, (caddr_t)ccb, 2 * hz);
+ ccb->flags = CCB_ABORTED;
+ }
+ splx(s);
+}
+
+#ifdef UTEST
+void
+bt_print_ccb(ccb)
+ struct bt_ccb *ccb;
+{
+ printf("ccb:%x op:%x cmdlen:%d senlen:%d\n"
+ ,ccb
+ ,ccb->opcode
+ ,ccb->scsi_cmd_length
+ ,ccb->req_sense_length);
+ printf(" datlen:%d hstat:%x tstat:%x flags:%x\n"
+ ,ccb->data_length
+ ,ccb->host_stat
+ ,ccb->target_stat
+ ,ccb->flags);
+}
+
+void
+bt_print_active_ccbs(int unit)
+{
+ struct bt_data *bt = btdata[unit];
+ struct bt_ccb *ccb;
+ int i = 0;
+
+ while (i < CCB_HASH_SIZE) {
+ ccb = bt->ccbhash[i];
+ while (ccb) {
+ if (ccb->flags != CCB_FREE)
+ bt_print_ccb(ccb);
+ ccb = ccb->nexthash;
+ }
+ i++;
+ }
+}
+#endif /*UTEST */
+#endif /*KERNEL */
diff --git a/sys/i386/isa/clock.c b/sys/i386/isa/clock.c
new file mode 100644
index 0000000..e40079a
--- /dev/null
+++ b/sys/i386/isa/clock.c
@@ -0,0 +1,442 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz and Don Ahn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)clock.c 7.2 (Berkeley) 5/12/91
+ * $Id: clock.c,v 1.6 1994/02/06 22:48:13 davidg Exp $
+ */
+
+/*
+ * Primitive clock interrupt routines.
+ */
+#include "param.h"
+#include "systm.h"
+#include "time.h"
+#include "kernel.h"
+#include "machine/segments.h"
+#include "machine/frame.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/rtc.h"
+#include "i386/isa/timerreg.h"
+#include <machine/cpu.h>
+
+/* X-tals being what they are, it's nice to be able to fudge this one... */
+/* Note, the name changed here from XTALSPEED to TIMER_FREQ rgrimes 4/26/93 */
+#ifndef TIMER_FREQ
+#define TIMER_FREQ 1193182 /* XXX - should be in isa.h */
+#endif
+#define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x))
+
+void hardclock();
+static int beeping;
+int timer0_divisor = TIMER_DIV(100); /* XXX should be hz */
+u_int timer0_prescale;
+static char timer0_state = 0, timer2_state = 0;
+static char timer0_reprogram = 0;
+static void (*timer_func)() = hardclock;
+static void (*new_function)();
+static u_int new_rate;
+static u_int hardclock_divisor;
+
+
+void
+clkintr(frame)
+ struct clockframe frame;
+{
+ hardclock(&frame);
+}
+
+#if 0
+void
+timerintr(struct clockframe frame)
+{
+ timer_func(&frame);
+ switch (timer0_state) {
+ case 0:
+ break;
+ case 1:
+ if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) {
+ hardclock(&frame);
+ timer0_prescale = 0;
+ }
+ break;
+ case 2:
+ disable_intr();
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+ outb(TIMER_CNTR0, TIMER_DIV(new_rate)%256);
+ outb(TIMER_CNTR0, TIMER_DIV(new_rate)/256);
+ enable_intr();
+ timer0_divisor = TIMER_DIV(new_rate);
+ timer0_prescale = 0;
+ timer_func = new_function;
+ timer0_state = 1;
+ break;
+ case 3:
+ if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) {
+ hardclock(&frame);
+ disable_intr();
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+ outb(TIMER_CNTR0, TIMER_DIV(hz)%256);
+ outb(TIMER_CNTR0, TIMER_DIV(hz)/256);
+ enable_intr();
+ timer0_divisor = TIMER_DIV(hz);
+ timer0_prescale = 0;
+ timer_func = hardclock;;
+ timer0_state = 0;
+ }
+ break;
+ }
+}
+
+#endif
+
+int
+acquire_timer0(int rate, void (*function)() )
+{
+ if (timer0_state || !function)
+ return -1;
+
+ new_function = function;
+ new_rate = rate;
+ timer0_state = 2;
+ return 0;
+}
+
+
+int
+acquire_timer2(int mode)
+{
+ if (timer2_state)
+ return -1;
+ timer2_state = 1;
+ outb(TIMER_MODE, TIMER_SEL2 | (mode &0x3f));
+ return 0;
+}
+
+
+int
+release_timer0()
+{
+ if (!timer0_state)
+ return -1;
+ timer0_state = 3;
+ return 0;
+}
+
+
+int
+release_timer2()
+{
+ if (!timer2_state)
+ return -1;
+ timer2_state = 0;
+ outb(TIMER_MODE, TIMER_SEL2|TIMER_SQWAVE|TIMER_16BIT);
+ return 0;
+}
+
+
+static int
+getit()
+{
+ int high, low;
+
+ disable_intr();
+ /* select timer0 and latch counter value */
+ outb(TIMER_MODE, TIMER_SEL0);
+ low = inb(TIMER_CNTR0);
+ high = inb(TIMER_CNTR0);
+ enable_intr();
+ return ((high << 8) | low);
+}
+
+
+/*
+ * Wait "n" microseconds.
+ * Relies on timer 1 counting down from (TIMER_FREQ / hz)
+ * Note: timer had better have been programmed before this is first used!
+ */
+void
+DELAY(int n)
+{
+ int counter_limit, prev_tick, tick, ticks_left, sec, usec;
+
+#ifdef DELAYDEBUG
+ int getit_calls = 1;
+ int n1;
+ static int state = 0;
+
+ if (state == 0) {
+ state = 1;
+ for (n1 = 1; n1 <= 10000000; n1 *= 10)
+ DELAY(n1);
+ state = 2;
+ }
+ if (state == 1)
+ printf("DELAY(%d)...", n);
+#endif
+ /*
+ * Read the counter first, so that the rest of the setup overhead is
+ * counted. Guess the initial overhead is 20 usec (on most systems it
+ * takes about 1.5 usec for each of the i/o's in getit(). The loop
+ * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The
+ * multiplications and divisions to scale the count take a while).
+ */
+ prev_tick = getit(0, 0);
+ n -= 20;
+ /*
+ * Calculate (n * (TIMER_FREQ / 1e6)) without using floating point
+ * and without any avoidable overflows.
+ */
+ sec = n / 1000000;
+ usec = n - sec * 1000000;
+ ticks_left = sec * TIMER_FREQ
+ + usec * (TIMER_FREQ / 1000000)
+ + usec * ((TIMER_FREQ % 1000000) / 1000) / 1000
+ + usec * (TIMER_FREQ % 1000) / 1000000;
+
+ while (ticks_left > 0) {
+ tick = getit(0, 0);
+#ifdef DELAYDEBUG
+ ++getit_calls;
+#endif
+ if (tick > prev_tick)
+ ticks_left -= prev_tick - (tick - timer0_divisor);
+ else
+ ticks_left -= prev_tick - tick;
+ prev_tick = tick;
+ }
+#ifdef DELAYDEBUG
+ if (state == 1)
+ printf(" %d calls to getit() at %d usec each\n",
+ getit_calls, (n + 5) / getit_calls);
+#endif
+}
+
+
+static void
+sysbeepstop()
+{
+ outb(IO_PPI, inb(IO_PPI)&0xFC); /* disable counter2 output to speaker */
+ release_timer2();
+ beeping = 0;
+}
+
+
+int
+sysbeep(int pitch, int period)
+{
+
+ if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT))
+ return -1;
+ disable_intr();
+ outb(TIMER_CNTR2, pitch);
+ outb(TIMER_CNTR2, (pitch>>8));
+ enable_intr();
+ if (!beeping) {
+ outb(IO_PPI, inb(IO_PPI) | 3); /* enable counter2 output to speaker */
+ beeping = period;
+ timeout(sysbeepstop, 0, period);
+ }
+ return 0;
+}
+
+
+void
+startrtclock()
+{
+ int s;
+
+ /* initialize 8253 clock */
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+
+ /* Correct rounding will buy us a better precision in timekeeping */
+ outb (IO_TIMER1, TIMER_DIV(hz)%256);
+ outb (IO_TIMER1, TIMER_DIV(hz)/256);
+ timer0_divisor = hardclock_divisor = TIMER_DIV(hz);
+
+ /* initialize brain-dead battery powered clock */
+ outb (IO_RTC, RTC_STATUSA);
+ outb (IO_RTC+1, 0x26);
+ outb (IO_RTC, RTC_STATUSB);
+ outb (IO_RTC+1, 2);
+
+ outb (IO_RTC, RTC_DIAG);
+ if (s = inb (IO_RTC+1))
+ printf("RTC BIOS diagnostic error %b\n", s, RTCDG_BITS);
+}
+
+
+/* convert 2 digit BCD number */
+int
+bcd(int i)
+{
+ return ((i/16)*10 + (i%16));
+}
+
+
+/* convert years to seconds (from 1970) */
+unsigned long
+ytos(int y)
+{
+ int i;
+ unsigned long ret;
+
+ ret = 0;
+ for(i = 1970; i < y; i++) {
+ if (i % 4) ret += 365*24*60*60;
+ else ret += 366*24*60*60;
+ }
+ return ret;
+}
+
+
+/* convert months to seconds */
+unsigned long
+mtos(int m, int leap)
+{
+ int i;
+ unsigned long ret;
+
+ ret = 0;
+ for(i=1; i<m; i++) {
+ switch(i){
+ case 1: case 3: case 5: case 7: case 8: case 10: case 12:
+ ret += 31*24*60*60; break;
+ case 4: case 6: case 9: case 11:
+ ret += 30*24*60*60; break;
+ case 2:
+ if (leap) ret += 29*24*60*60;
+ else ret += 28*24*60*60;
+ }
+ }
+ return ret;
+}
+
+
+/*
+ * Initialize the time of day register, based on the time base which is, e.g.
+ * from a filesystem.
+ */
+void
+inittodr(time_t base)
+{
+ unsigned long sec;
+ int leap, day_week, t, yd;
+ int sa,s;
+
+ /* do we have a realtime clock present? (otherwise we loop below) */
+ sa = rtcin(RTC_STATUSA);
+ if (sa == 0xff || sa == 0) return;
+
+ /* ready for a read? */
+ while ((sa&RTCSA_TUP) == RTCSA_TUP)
+ sa = rtcin(RTC_STATUSA);
+
+ sec = bcd(rtcin(RTC_YEAR)) + 1900;
+ if (sec < 1970)
+ sec += 100;
+
+ leap = !(sec % 4); sec = ytos(sec); /* year */
+ yd = mtos(bcd(rtcin(RTC_MONTH)),leap); sec+=yd; /* month */
+ t = (bcd(rtcin(RTC_DAY))-1) * 24*60*60; sec+=t; yd+=t; /* date */
+ day_week = rtcin(RTC_WDAY); /* day */
+ sec += bcd(rtcin(RTC_HRS)) * 60*60; /* hour */
+ sec += bcd(rtcin(RTC_MIN)) * 60; /* minutes */
+ sec += bcd(rtcin(RTC_SEC)); /* seconds */
+ sec += tz.tz_minuteswest * 60;
+ time.tv_sec = sec;
+}
+
+
+#ifdef garbage
+/*
+ * Initialze the time of day register, based on the time base which is, e.g.
+ * from a filesystem.
+ */
+test_inittodr(time_t base)
+{
+
+ outb(IO_RTC,9); /* year */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,8); /* month */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,7); /* day */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,4); /* hour */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,2); /* minutes */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,0); /* seconds */
+ printf("%d\n",bcd(inb(IO_RTC+1)));
+
+ time.tv_sec = base;
+}
+#endif
+
+/*
+ * Wire clock interrupt in.
+ */
+#define V(s) __CONCAT(V, s)
+extern void V(clk)();
+
+
+void
+enablertclock()
+{
+ setidt(ICU_OFFSET+0, &V(clk), SDT_SYS386IGT, SEL_KPL);
+ INTREN(IRQ0);
+}
+
+
+/*
+ * Delay for some number of milliseconds.
+ */
+void
+spinwait(int millisecs)
+{
+ DELAY(1000 * millisecs);
+}
+
+void
+cpu_initclocks()
+{
+ startrtclock();
+ enablertclock();
+}
+
+void
+setstatclockrate(int newhz)
+{
+}
diff --git a/sys/i386/isa/fd.c b/sys/i386/isa/fd.c
new file mode 100644
index 0000000..259d451
--- /dev/null
+++ b/sys/i386/isa/fd.c
@@ -0,0 +1,1255 @@
+/*#define DEBUG 1*/
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Don Ahn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)fd.c 7.4 (Berkeley) 5/25/91
+ * $Id: fd.c,v 1.24 1994/03/08 16:25:29 nate Exp $
+ *
+ */
+
+#include "ft.h"
+#if NFT < 1
+#undef NFDC
+#endif
+#include "fd.h"
+
+#if NFDC > 0
+
+#include <sys/param.h>
+#include <sys/dkbad.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <machine/ioctl_fd.h>
+#include <sys/disklabel.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/syslog.h>
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/fdreg.h"
+#include "i386/isa/fdc.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/rtc.h"
+
+#if NFT > 0
+extern int ftopen(), ftintr(), ftattach(), ftclose(), ftioctl();
+#endif
+
+#define b_cylin b_resid
+#define FDBLK 512
+
+/* misuse a flag to identify format operation */
+#define B_FORMAT B_XXX
+
+#define NUMTYPES 14
+#define NUMDENS (NUMTYPES - 6)
+
+/* This defines (-1) must match index for fd_types */
+#define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */
+#define NO_TYPE 0 /* must match NO_TYPE in ft.c */
+#define FD_1720 1
+#define FD_1480 2
+#define FD_1440 3
+#define FD_1200 4
+#define FD_820 5
+#define FD_800 6
+#define FD_720 7
+#define FD_360 8
+
+#define FD_1480in5_25 9
+#define FD_1440in5_25 10
+#define FD_820in5_25 11
+#define FD_800in5_25 12
+#define FD_720in5_25 13
+#define FD_360in5_25 14
+
+
+struct fd_type fd_types[NUMTYPES] =
+{
+{ 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
+{ 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
+{ 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
+{ 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */
+{ 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */
+{ 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */
+{ 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */
+{ 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */
+
+{ 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
+{ 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
+{ 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */
+{ 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */
+{ 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */
+{ 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */
+};
+
+#define DRVS_PER_CTLR 2 /* 2 floppies */
+/***********************************************************************\
+* Per controller structure. *
+\***********************************************************************/
+struct fdc_data fdc_data[NFDC];
+
+/***********************************************************************\
+* Per drive structure. *
+* N per controller (DRVS_PER_CTLR) *
+\***********************************************************************/
+struct fd_data {
+ struct fdc_data *fdc; /* pointer to controller structure */
+ int fdsu; /* this units number on this controller */
+ int type; /* Drive type (HD, DD */
+ struct fd_type *ft; /* pointer to the type descriptor */
+ int flags;
+#define FD_OPEN 0x01 /* it's open */
+#define FD_ACTIVE 0x02 /* it's active */
+#define FD_MOTOR 0x04 /* motor should be on */
+#define FD_MOTOR_WAIT 0x08 /* motor coming up */
+ int skip;
+ int hddrv;
+ int track; /* where we think the head is */
+} fd_data[NFD];
+
+/***********************************************************************\
+* Throughout this file the following conventions will be used: *
+* fd is a pointer to the fd_data struct for the drive in question *
+* fdc is a pointer to the fdc_data struct for the controller *
+* fdu is the floppy drive unit number *
+* fdcu is the floppy controller unit number *
+* fdsu is the floppy drive unit number on that controller. (sub-unit) *
+\***********************************************************************/
+
+#define id_physid id_scsiid /* this biotab field doubles as a field */
+ /* for the physical unit number on the controller */
+
+static int retrier(fdcu_t);
+
+#define DEVIDLE 0
+#define FINDWORK 1
+#define DOSEEK 2
+#define SEEKCOMPLETE 3
+#define IOCOMPLETE 4
+#define RECALCOMPLETE 5
+#define STARTRECAL 6
+#define RESETCTLR 7
+#define SEEKWAIT 8
+#define RECALWAIT 9
+#define MOTORWAIT 10
+#define IOTIMEDOUT 11
+
+#ifdef DEBUG
+char *fdstates[] =
+{
+"DEVIDLE",
+"FINDWORK",
+"DOSEEK",
+"SEEKCOMPLETE",
+"IOCOMPLETE",
+"RECALCOMPLETE",
+"STARTRECAL",
+"RESETCTLR",
+"SEEKWAIT",
+"RECALWAIT",
+"MOTORWAIT",
+"IOTIMEDOUT"
+};
+
+
+int fd_debug = 1;
+#define TRACE0(arg) if(fd_debug) printf(arg)
+#define TRACE1(arg1,arg2) if(fd_debug) printf(arg1,arg2)
+#else /* DEBUG */
+#define TRACE0(arg)
+#define TRACE1(arg1,arg2)
+#endif /* DEBUG */
+
+static void fdstart(fdcu_t);
+void fdintr(fdcu_t);
+static void fd_turnoff(caddr_t);
+
+/****************************************************************************/
+/* autoconfiguration stuff */
+/****************************************************************************/
+static int fdprobe(struct isa_device *);
+static int fdattach(struct isa_device *);
+
+struct isa_driver fdcdriver = {
+ fdprobe, fdattach, "fdc",
+};
+
+/*
+ * probe for existance of controller
+ */
+int
+fdprobe(dev)
+ struct isa_device *dev;
+{
+ fdcu_t fdcu = dev->id_unit;
+ if(fdc_data[fdcu].flags & FDC_ATTACHED)
+ {
+ printf("fdc: same unit (%d) used multiple times\n",fdcu);
+ return 0;
+ }
+
+ fdc_data[fdcu].baseport = dev->id_iobase;
+
+ /* First - lets reset the floppy controller */
+
+ outb(dev->id_iobase+fdout,0);
+ DELAY(100);
+ outb(dev->id_iobase+fdout,FDO_FRST);
+
+ /* see if it can handle a command */
+ if (out_fdc(fdcu,NE7CMD_SPECIFY) < 0)
+ {
+ return(0);
+ }
+ out_fdc(fdcu,0xDF);
+ out_fdc(fdcu,2);
+ return (IO_FDCSIZE);
+}
+
+/*
+ * wire controller into system, look for floppy units
+ */
+int
+fdattach(dev)
+ struct isa_device *dev;
+{
+ unsigned fdt,st0, cyl;
+ int hdr;
+ fdu_t fdu;
+ fdcu_t fdcu = dev->id_unit;
+ fdc_p fdc = fdc_data + fdcu;
+ fd_p fd;
+ int fdsu;
+ struct isa_device *fdup;
+
+ fdc->fdcu = fdcu;
+ fdc->flags |= FDC_ATTACHED;
+ fdc->dmachan = dev->id_drq;
+ fdc->state = DEVIDLE;
+ hdr = 0;
+ printf("fdc%d:", fdcu);
+
+ /* check for each floppy drive */
+ for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) {
+ if (fdup->id_iobase != dev->id_iobase)
+ continue;
+ fdu = fdup->id_unit;
+ fd = &fd_data[fdu];
+ if (fdu >= (NFD+NFT))
+ continue;
+ fdsu = fdup->id_physid;
+ /* look up what bios thinks we have */
+ switch (fdu) {
+ case 0: fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
+ break;
+ case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
+ break;
+ default: fdt = RTCFDT_NONE;
+ break;
+ }
+ /* is there a unit? */
+ if ((fdt == RTCFDT_NONE)
+#if NFT > 0
+ || (fdsu >= DRVS_PER_CTLR)) {
+#else
+ ) {
+ fd->type = NO_TYPE;
+#endif
+#if NFT > 0
+ /* If BIOS says no floppy, or > 2nd device */
+ /* Probe for and attach a floppy tape. */
+ if (ftattach(dev, fdup))
+ continue;
+ if (fdsu < DRVS_PER_CTLR)
+ fd->type = NO_TYPE;
+#endif
+ continue;
+ }
+
+#ifdef notyet
+ /* select it */
+ fd_turnon1(fdu);
+ spinwait(1000); /* 1 sec */
+ out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */
+ out_fdc(fdcu,fdsu);
+ spinwait(1000); /* 1 sec */
+
+ /* anything responding */
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ if (st0 & 0xd0)
+ continue;
+
+#endif
+ fd->track = -2;
+ fd->fdc = fdc;
+ fd->fdsu = fdsu;
+ printf(" [%d: fd%d: ", fdsu, fdu);
+
+ switch (fdt) {
+ case RTCFDT_12M:
+ printf("1.2MB 5.25in]");
+ fd->type = FD_1200;
+ break;
+ case RTCFDT_144M:
+ printf("1.44MB 3.5in]");
+ fd->type = FD_1440;
+ break;
+ case RTCFDT_360K:
+ printf("360KB 5.25in]");
+ fd->type = FD_360;
+ break;
+ case RTCFDT_720K:
+ printf("720KB 3.5in]");
+ fd->type = FD_720;
+ break;
+ default:
+ printf("unknown]");
+ fd->type = NO_TYPE;
+ break;
+ }
+
+ fd_turnoff((caddr_t)fdu);
+ hdr = 1;
+ }
+ printf("\n");
+
+ /* Set transfer to 500kbps */
+ outb(fdc->baseport+fdctl,0); /*XXX*/
+ return 1;
+}
+
+int
+fdsize(dev)
+ dev_t dev;
+{
+ return(0);
+}
+
+/****************************************************************************/
+/* fdstrategy */
+/****************************************************************************/
+void fdstrategy(struct buf *bp)
+{
+ register struct buf *dp,*dp0,*dp1;
+ long nblocks,blknum;
+ int s;
+ fdcu_t fdcu;
+ fdu_t fdu;
+ fdc_p fdc;
+ fd_p fd;
+
+ fdu = FDUNIT(minor(bp->b_dev));
+ fd = &fd_data[fdu];
+ fdc = fd->fdc;
+ fdcu = fdc->fdcu;
+
+#if NFT > 0
+ /* check for controller already busy with tape */
+ if (fdc->flags & FDC_TAPE_BUSY) {
+ bp->b_error = EBUSY;
+ bp->b_flags |= B_ERROR;
+ return;
+ }
+#endif
+ if ((fdu >= NFD) || (bp->b_blkno < 0)) {
+ printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n",
+ fdu, bp->b_blkno, bp->b_bcount);
+ pg("fd:error in fdstrategy");
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ goto bad;
+ }
+ /*
+ * Set up block calculations.
+ */
+ blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK;
+ nblocks = fd->ft->size;
+ if (blknum + (bp->b_bcount / FDBLK) > nblocks) {
+ if (blknum == nblocks) {
+ bp->b_resid = bp->b_bcount;
+ } else {
+ bp->b_error = ENOSPC;
+ bp->b_flags |= B_ERROR;
+ }
+ goto bad;
+ }
+ bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads);
+ bp->b_pblkno = bp->b_blkno;
+ dp = &(fdc->head);
+ s = splbio();
+ disksort(dp, bp);
+ untimeout((timeout_func_t)fd_turnoff, (caddr_t)fdu); /* a good idea */
+ fdstart(fdcu);
+ splx(s);
+ return;
+
+bad:
+ biodone(bp);
+ return;
+}
+
+/****************************************************************************/
+/* motor control stuff */
+/* remember to not deselect the drive we're working on */
+/****************************************************************************/
+void
+set_motor(fdcu, fdu, reset)
+ fdcu_t fdcu;
+ fdu_t fdu;
+ int reset;
+{
+ int m0,m1;
+ int selunit;
+ fd_p fd;
+ if(fd = fdc_data[fdcu].fd)/* yes an assign! */
+ {
+ selunit = fd->fdsu;
+ }
+ else
+ {
+ selunit = 0;
+ }
+ m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR;
+ m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR;
+ outb(fdc_data[fdcu].baseport+fdout,
+ selunit
+ | (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
+ | (m0 ? FDO_MOEN0 : 0)
+ | (m1 ? FDO_MOEN1 : 0));
+ TRACE1("[0x%x->fdout]",(
+ selunit
+ | (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
+ | (m0 ? FDO_MOEN0 : 0)
+ | (m1 ? FDO_MOEN1 : 0)));
+}
+
+static void
+fd_turnoff(caddr_t arg1)
+{
+ fdu_t fdu = (fdu_t)arg1;
+ int s;
+
+ fd_p fd = fd_data + fdu;
+ s = splbio();
+ fd->flags &= ~FD_MOTOR;
+ set_motor(fd->fdc->fdcu,fd->fdsu,0);
+ splx(s);
+}
+
+void
+fd_motor_on(caddr_t arg1)
+{
+ fdu_t fdu = (fdu_t)arg1;
+ int s;
+
+ fd_p fd = fd_data + fdu;
+ s = splbio();
+ fd->flags &= ~FD_MOTOR_WAIT;
+ if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
+ {
+ fdintr(fd->fdc->fdcu);
+ }
+ splx(s);
+}
+
+static void fd_turnon1(fdu_t);
+
+void
+fd_turnon(fdu)
+ fdu_t fdu;
+{
+ fd_p fd = fd_data + fdu;
+ if(!(fd->flags & FD_MOTOR))
+ {
+ fd_turnon1(fdu);
+ fd->flags |= FD_MOTOR_WAIT;
+ timeout((timeout_func_t)fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */
+ }
+}
+
+static void
+fd_turnon1(fdu_t fdu)
+{
+ fd_p fd = fd_data + fdu;
+ fd->flags |= FD_MOTOR;
+ set_motor(fd->fdc->fdcu,fd->fdsu,0);
+}
+
+/****************************************************************************/
+/* fdc in/out */
+/****************************************************************************/
+int
+in_fdc(fdcu)
+ fdcu_t fdcu;
+{
+ int baseport = fdc_data[fdcu].baseport;
+ int i, j = 100000;
+ while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM))
+ != (NE7_DIO|NE7_RQM) && j-- > 0)
+ if (i == NE7_RQM) return -1;
+ if (j <= 0)
+ return(-1);
+#ifdef DEBUG
+ i = inb(baseport+fddata);
+ TRACE1("[fddata->0x%x]",(unsigned char)i);
+ return(i);
+#else
+ return inb(baseport+fddata);
+#endif
+}
+
+int
+out_fdc(fdcu, x)
+ fdcu_t fdcu;
+ int x;
+{
+ int baseport = fdc_data[fdcu].baseport;
+ int i;
+
+ /* Check that the direction bit is set */
+ i = 100000;
+ while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0);
+ if (i <= 0) return (-1); /* Floppy timed out */
+
+ /* Check that the floppy controller is ready for a command */
+ i = 100000;
+ while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0);
+ if (i <= 0) return (-1); /* Floppy timed out */
+
+ /* Send the command and return */
+ outb(baseport+fddata,x);
+ TRACE1("[0x%x->fddata]",x);
+ return (0);
+}
+
+/****************************************************************************/
+/* fdopen/fdclose */
+/****************************************************************************/
+int
+Fdopen(dev, flags)
+ dev_t dev;
+ int flags;
+{
+ fdu_t fdu = FDUNIT(minor(dev));
+ int type = FDTYPE(minor(dev));
+ fdc_p fdc;
+
+#if NFT > 0
+ /* check for a tape open */
+ if (type & F_TAPE_TYPE)
+ return(ftopen(dev, flags));
+#endif
+ /* check bounds */
+ if (fdu >= NFD)
+ return(ENXIO);
+ fdc = fd_data[fdu].fdc;
+ if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE))
+ return(ENXIO);
+ if (type > NUMDENS)
+ return(ENXIO);
+ if (type == 0)
+ type = fd_data[fdu].type;
+ else {
+ if (type != fd_data[fdu].type) {
+ switch (fd_data[fdu].type) {
+ case FD_360:
+ return(ENXIO);
+ case FD_720:
+ if ( type != FD_820
+ && type != FD_800
+ )
+ return(ENXIO);
+ break;
+ case FD_1200:
+ switch (type) {
+ case FD_1480:
+ type = FD_1480in5_25;
+ break;
+ case FD_1440:
+ type = FD_1440in5_25;
+ break;
+ case FD_820:
+ type = FD_820in5_25;
+ break;
+ case FD_800:
+ type = FD_800in5_25;
+ break;
+ case FD_720:
+ type = FD_720in5_25;
+ break;
+ case FD_360:
+ type = FD_360in5_25;
+ break;
+ default:
+ return(ENXIO);
+ }
+ break;
+ case FD_1440:
+ if ( type != FD_1720
+ && type != FD_1480
+ && type != FD_1200
+ && type != FD_820
+ && type != FD_800
+ && type != FD_720
+ )
+ return(ENXIO);
+ break;
+ }
+ }
+ }
+ fd_data[fdu].ft = fd_types + type - 1;
+ fd_data[fdu].flags |= FD_OPEN;
+
+ return 0;
+}
+
+int
+fdclose(dev, flags)
+ dev_t dev;
+ int flags;
+{
+ fdu_t fdu = FDUNIT(minor(dev));
+ int type = FDTYPE(minor(dev));
+
+#if NFT > 0
+ if (type & F_TAPE_TYPE)
+ return ftclose(0);
+#endif
+ fd_data[fdu].flags &= ~FD_OPEN;
+ return(0);
+}
+
+
+/***************************************************************\
+* fdstart *
+* We have just queued something.. if the controller is not busy *
+* then simulate the case where it has just finished a command *
+* So that it (the interrupt routine) looks on the queue for more*
+* work to do and picks up what we just added. *
+* If the controller is already busy, we need do nothing, as it *
+* will pick up our work when the present work completes *
+\***************************************************************/
+static void
+fdstart(fdcu)
+ fdcu_t fdcu;
+{
+ register struct buf *dp,*bp;
+ int s;
+ fdu_t fdu;
+
+ s = splbio();
+ if(fdc_data[fdcu].state == DEVIDLE)
+ {
+ fdintr(fdcu);
+ }
+ splx(s);
+}
+
+static void
+fd_timeout(caddr_t arg1)
+{
+ fdcu_t fdcu = (fdcu_t)arg1;
+ fdu_t fdu = fdc_data[fdcu].fdu;
+ int st0, st3, cyl;
+ struct buf *dp,*bp;
+ int s;
+
+ dp = &fdc_data[fdcu].head;
+ s = splbio();
+ bp = dp->b_actf;
+
+ out_fdc(fdcu,NE7CMD_SENSED);
+ out_fdc(fdcu,fd_data[fdu].hddrv);
+ st3 = in_fdc(fdcu);
+
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n",
+ fdu,
+ st0,
+ NE7_ST0BITS,
+ cyl,
+ st3,
+ NE7_ST3BITS);
+
+ if (bp)
+ {
+ retrier(fdcu);
+ fdc_data[fdcu].status[0] = 0xc0;
+ fdc_data[fdcu].state = IOTIMEDOUT;
+ if( fdc_data[fdcu].retry < 6)
+ fdc_data[fdcu].retry = 6;
+ }
+ else
+ {
+ fdc_data[fdcu].fd = (fd_p) 0;
+ fdc_data[fdcu].fdu = -1;
+ fdc_data[fdcu].state = DEVIDLE;
+ }
+ fdintr(fdcu);
+ splx(s);
+}
+
+/* just ensure it has the right spl */
+static void
+fd_pseudointr(caddr_t arg1, int arg2)
+{
+ fdcu_t fdcu = (fdcu_t)arg1;
+ int s;
+ s = splbio();
+ fdintr(fdcu);
+ splx(s);
+}
+
+/***********************************************************************\
+* fdintr *
+* keep calling the state machine until it returns a 0 *
+* ALWAYS called at SPLBIO *
+\***********************************************************************/
+void
+fdintr(fdcu_t fdcu)
+{
+ fdc_p fdc = fdc_data + fdcu;
+#if NFT > 0
+ fdu_t fdu = fdc->fdu;
+
+ if (fdc->flags & FDC_TAPE_BUSY)
+ (ftintr(fdu));
+ else
+#endif
+ while(fdstate(fdcu, fdc))
+ ;
+}
+
+/***********************************************************************\
+* The controller state machine. *
+* if it returns a non zero value, it should be called again immediatly *
+\***********************************************************************/
+int
+fdstate(fdcu, fdc)
+ fdcu_t fdcu;
+ fdc_p fdc;
+{
+ int read, format, head, trac, sec = 0, i = 0, s, sectrac, cyl, st0;
+ unsigned long blknum;
+ fdu_t fdu = fdc->fdu;
+ fd_p fd;
+ register struct buf *dp,*bp;
+ struct fd_formb *finfo = NULL;
+
+ dp = &(fdc->head);
+ bp = dp->b_actf;
+ if(!bp)
+ {
+ /***********************************************\
+ * nothing left for this controller to do *
+ * Force into the IDLE state, *
+ \***********************************************/
+ fdc->state = DEVIDLE;
+ if(fdc->fd)
+ {
+ printf("unexpected valid fd pointer (fdu = %d)\n"
+ ,fdc->fdu);
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ }
+ TRACE1("[fdc%d IDLE]",fdcu);
+ return(0);
+ }
+ fdu = FDUNIT(minor(bp->b_dev));
+ fd = fd_data + fdu;
+ if (fdc->fd && (fd != fdc->fd))
+ {
+ printf("confused fd pointers\n");
+ }
+ read = bp->b_flags & B_READ;
+ format = bp->b_flags & B_FORMAT;
+ if(format)
+ finfo = (struct fd_formb *)bp->b_un.b_addr;
+ TRACE1("fd%d",fdu);
+ TRACE1("[%s]",fdstates[fdc->state]);
+ TRACE1("(0x%x)",fd->flags);
+ untimeout((timeout_func_t)fd_turnoff, (caddr_t)fdu);
+ timeout((timeout_func_t)fd_turnoff, (caddr_t)fdu, 4 * hz);
+ switch (fdc->state)
+ {
+ case DEVIDLE:
+ case FINDWORK: /* we have found new work */
+ fdc->retry = 0;
+ fd->skip = 0;
+ fdc->fd = fd;
+ fdc->fdu = fdu;
+ outb(fdc->baseport+fdctl, fd->ft->trans);
+ /*******************************************************\
+ * If the next drive has a motor startup pending, then *
+ * it will start up in it's own good time *
+ \*******************************************************/
+ if(fd->flags & FD_MOTOR_WAIT)
+ {
+ fdc->state = MOTORWAIT;
+ return(0); /* come back later */
+ }
+ /*******************************************************\
+ * Maybe if it's not starting, it SHOULD be starting *
+ \*******************************************************/
+ if (!(fd->flags & FD_MOTOR))
+ {
+ fdc->state = MOTORWAIT;
+ fd_turnon(fdu);
+ return(0);
+ }
+ else /* at least make sure we are selected */
+ {
+ set_motor(fdcu,fd->fdsu,0);
+ }
+ fdc->state = DOSEEK;
+ break;
+ case DOSEEK:
+ if (bp->b_cylin == fd->track)
+ {
+ fdc->state = SEEKCOMPLETE;
+ break;
+ }
+ out_fdc(fdcu,NE7CMD_SEEK); /* Seek function */
+ out_fdc(fdcu,fd->fdsu); /* Drive number */
+ out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac);
+ fd->track = -2;
+ fdc->state = SEEKWAIT;
+ timeout((timeout_func_t)fd_timeout, (caddr_t)fdcu, 2 * hz);
+ return(0); /* will return later */
+ case SEEKWAIT:
+ untimeout((timeout_func_t)fd_timeout, (caddr_t)fdcu);
+ /* allow heads to settle */
+ timeout((timeout_func_t)fd_pseudointr, (caddr_t)fdcu, hz / 50);
+ fdc->state = SEEKCOMPLETE;
+ return(0); /* will return later */
+ break;
+
+ case SEEKCOMPLETE : /* SEEK DONE, START DMA */
+ /* Make sure seek really happened*/
+ if(fd->track == -2)
+ {
+ int descyl = bp->b_cylin * fd->ft->steptrac;
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ i = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ if (cyl != descyl)
+ {
+ printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
+ fdu, descyl, cyl, i, NE7_ST0BITS);
+ return(retrier(fdcu));
+ }
+ }
+
+ fd->track = bp->b_cylin;
+ if(format)
+ fd->skip = (char *)&(finfo->fd_formb_cylno(0))
+ - (char *)finfo;
+ isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip,
+ format ? bp->b_bcount : FDBLK, fdc->dmachan);
+ blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
+ + fd->skip/FDBLK;
+ sectrac = fd->ft->sectrac;
+ sec = blknum % (sectrac * fd->ft->heads);
+ head = sec / sectrac;
+ sec = sec % sectrac + 1;
+/*XXX*/ fd->hddrv = ((head&1)<<2)+fdu;
+
+ if(format)
+ {
+ /* formatting */
+ out_fdc(fdcu,/* NE7CMD_FORMAT */ 0x4d);
+ out_fdc(fdcu,head << 2 | fdu);
+ out_fdc(fdcu,finfo->fd_formb_secshift);
+ out_fdc(fdcu,finfo->fd_formb_nsecs);
+ out_fdc(fdcu,finfo->fd_formb_gaplen);
+ out_fdc(fdcu,finfo->fd_formb_fillbyte);
+ }
+ else
+ {
+ if (read)
+ {
+ out_fdc(fdcu,NE7CMD_READ); /* READ */
+ }
+ else
+ {
+ out_fdc(fdcu,NE7CMD_WRITE); /* WRITE */
+ }
+ out_fdc(fdcu,head << 2 | fdu); /* head & unit */
+ out_fdc(fdcu,fd->track); /* track */
+ out_fdc(fdcu,head);
+ out_fdc(fdcu,sec); /* sector XXX +1? */
+ out_fdc(fdcu,fd->ft->secsize); /* sector size */
+ out_fdc(fdcu,sectrac); /* sectors/track */
+ out_fdc(fdcu,fd->ft->gap); /* gap size */
+ out_fdc(fdcu,fd->ft->datalen); /* data length */
+ }
+ fdc->state = IOCOMPLETE;
+ timeout((timeout_func_t)fd_timeout, (caddr_t)fdcu, 2 * hz);
+ return(0); /* will return later */
+ case IOCOMPLETE: /* IO DONE, post-analyze */
+ untimeout((timeout_func_t)fd_timeout, (caddr_t)fdcu);
+ for(i=0;i<7;i++)
+ {
+ fdc->status[i] = in_fdc(fdcu);
+ }
+ case IOTIMEDOUT: /*XXX*/
+ isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip,
+ format ? bp->b_bcount : FDBLK, fdc->dmachan);
+ if (fdc->status[0]&0xF8)
+ {
+ if (fdc->status[1] & 0x10) {
+ /*
+ * Operation not completed in reasonable time.
+ * Just restart it, don't increment retry count.
+ * (vak)
+ */
+ fdc->state = SEEKCOMPLETE;
+ return (1);
+ }
+ return(retrier(fdcu));
+ }
+ /* All OK */
+ fd->skip += FDBLK;
+ if (!format && fd->skip < bp->b_bcount)
+ {
+ /* set up next transfer */
+ blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
+ + fd->skip/FDBLK;
+ bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads));
+ fdc->state = DOSEEK;
+ }
+ else
+ {
+ /* ALL DONE */
+ fd->skip = 0;
+ bp->b_resid = 0;
+ dp->b_actf = bp->b_actf;
+ biodone(bp);
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ fdc->state = FINDWORK;
+ }
+ return(1);
+ case RESETCTLR:
+ /* Try a reset, keep motor on */
+ set_motor(fdcu,fd->fdsu,1);
+ DELAY(100);
+ set_motor(fdcu,fd->fdsu,0);
+ outb(fdc->baseport+fdctl,fd->ft->trans);
+ TRACE1("[0x%x->fdctl]",fd->ft->trans);
+ fdc->retry++;
+ fdc->state = STARTRECAL;
+ break;
+ case STARTRECAL:
+ out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */
+ out_fdc(fdcu,0xDF);
+ out_fdc(fdcu,2);
+ out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */
+ out_fdc(fdcu,fdu);
+ fdc->state = RECALWAIT;
+ return(0); /* will return later */
+ case RECALWAIT:
+ /* allow heads to settle */
+ timeout((timeout_func_t)fd_pseudointr, (caddr_t)fdcu, hz / 30);
+ fdc->state = RECALCOMPLETE;
+ return(0); /* will return later */
+ case RECALCOMPLETE:
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ if (cyl != 0)
+ {
+ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu,
+ st0, NE7_ST0BITS, cyl);
+ return(retrier(fdcu));
+ }
+ fd->track = 0;
+ /* Seek (probably) necessary */
+ fdc->state = DOSEEK;
+ return(1); /* will return immediatly */
+ case MOTORWAIT:
+ if(fd->flags & FD_MOTOR_WAIT)
+ {
+ return(0); /* time's not up yet */
+ }
+ fdc->state = DOSEEK;
+ return(1); /* will return immediatly */
+ default:
+ printf("Unexpected FD int->");
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ printf("ST0 = %lx, PCN = %lx\n",i,sec);
+ out_fdc(fdcu,0x4A);
+ out_fdc(fdcu,fd->fdsu);
+ for(i=0;i<7;i++) {
+ fdc->status[i] = in_fdc(fdcu);
+ }
+ printf("intr status :%lx %lx %lx %lx %lx %lx %lx ",
+ fdc->status[0],
+ fdc->status[1],
+ fdc->status[2],
+ fdc->status[3],
+ fdc->status[4],
+ fdc->status[5],
+ fdc->status[6] );
+ return(0);
+ }
+ return(1); /* Come back immediatly to new state */
+}
+
+static int
+retrier(fdcu)
+ fdcu_t fdcu;
+{
+ fdc_p fdc = fdc_data + fdcu;
+ register struct buf *dp,*bp;
+
+ dp = &(fdc->head);
+ bp = dp->b_actf;
+
+ switch(fdc->retry)
+ {
+ case 0: case 1: case 2:
+ fdc->state = SEEKCOMPLETE;
+ break;
+ case 3: case 4: case 5:
+ fdc->state = STARTRECAL;
+ break;
+ case 6:
+ fdc->state = RESETCTLR;
+ break;
+ case 7:
+ break;
+ default:
+ {
+ dev_t sav_b_dev = bp->b_dev;
+ /* Trick diskerr */
+ bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|3);
+ diskerr(bp, "fd", "hard error", LOG_PRINTF,
+ fdc->fd->skip, (struct disklabel *)NULL);
+ bp->b_dev = sav_b_dev;
+ printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS);
+ printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS);
+ printf(" ST2 %b ", fdc->status[2], NE7_ST2BITS);
+ printf("cyl %d hd %d sec %d)\n",
+ fdc->status[3], fdc->status[4], fdc->status[5]);
+ }
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ bp->b_resid = bp->b_bcount - fdc->fd->skip;
+ dp->b_actf = bp->b_actf;
+ fdc->fd->skip = 0;
+ biodone(bp);
+ fdc->state = FINDWORK;
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ /* XXX abort current command, if any. */
+ return(1);
+ }
+ fdc->retry++;
+ return(1);
+}
+
+static int
+fdformat(dev, finfo, p)
+ dev_t dev;
+ struct fd_formb *finfo;
+ struct proc *p;
+{
+ fdu_t fdu;
+ fd_p fd;
+
+ struct buf *bp;
+ int rv = 0, s;
+
+ fdu = FDUNIT(minor(dev));
+ fd = &fd_data[fdu];
+
+ /* set up a buffer header for fdstrategy() */
+ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
+ if(bp == 0)
+ return ENOBUFS;
+ bzero((void *)bp, sizeof(struct buf));
+ bp->b_flags = B_BUSY | B_PHYS | B_FORMAT;
+ bp->b_proc = p;
+ bp->b_dev = dev;
+
+ /*
+ * calculate a fake blkno, so fdstrategy() would initiate a
+ * seek to the requested cylinder
+ */
+ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
+ + finfo->head * fd->ft->sectrac) * FDBLK / DEV_BSIZE;
+
+ bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
+ bp->b_un.b_addr = (caddr_t)finfo;
+
+ /* now do the format */
+ fdstrategy(bp);
+
+ /* ...and wait for it to complete */
+ s = splbio();
+ while(!(bp->b_flags & B_DONE))
+ {
+ rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
+ if(rv == EWOULDBLOCK)
+ break;
+ }
+ splx(s);
+
+ if(rv == EWOULDBLOCK)
+ {
+ /* timed out */
+ biodone(bp);
+ rv = EIO;
+ }
+ free(bp, M_TEMP);
+ return rv;
+}
+
+/*
+ * fdioctl() from jc@irbs.UUCP (John Capo)
+ * i386/i386/conf.c needs to have fdioctl() declared and remove the line that
+ * defines fdioctl to be enxio.
+ *
+ * TODO: Reformat.
+ * Think about allocating buffer off stack.
+ * Don't pass uncast 0's and NULL's to read/write/setdisklabel().
+ * Watch out for NetBSD's different *disklabel() interface.
+ *
+ * Added functionality for floppy formatting
+ * joerg_wunsch@uriah.sax.de (Joerg Wunsch)
+ */
+
+int
+fdioctl (dev, cmd, addr, flag, p)
+ dev_t dev;
+ int cmd;
+ caddr_t addr;
+ int flag;
+ struct proc *p;
+{
+ struct fd_type *fdt;
+ struct disklabel *dl;
+ char buffer[DEV_BSIZE];
+ int error;
+
+#if NFT > 0
+ int type = FDTYPE(minor(dev));
+
+ /* check for a tape ioctl */
+ if (type & F_TAPE_TYPE)
+ return ftioctl(dev, cmd, addr, flag, p);
+#endif
+
+ error = 0;
+
+ switch (cmd)
+ {
+ case DIOCGDINFO:
+ bzero(buffer, sizeof (buffer));
+ dl = (struct disklabel *)buffer;
+ dl->d_secsize = FDBLK;
+ fdt = fd_data[FDUNIT(minor(dev))].ft;
+ dl->d_secpercyl = fdt->size / fdt->tracks;
+ dl->d_type = DTYPE_FLOPPY;
+
+ if (readdisklabel(dev, fdstrategy, dl, NULL, 0, 0) == NULL)
+ error = 0;
+ else
+ error = EINVAL;
+
+ *(struct disklabel *)addr = *dl;
+ break;
+
+ case DIOCSDINFO:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ break;
+
+ case DIOCWLABEL:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ break;
+
+ case DIOCWDINFO:
+ if ((flag & FWRITE) == 0)
+ {
+ error = EBADF;
+ break;
+ }
+
+ dl = (struct disklabel *)addr;
+
+ if (error = setdisklabel ((struct disklabel *)buffer,
+ dl, 0, NULL))
+ break;
+
+ error = writedisklabel(dev, fdstrategy,
+ (struct disklabel *)buffer, NULL);
+ break;
+
+ case FD_FORM:
+ if((flag & FWRITE) == 0)
+ error = EBADF; /* must be opened for writing */
+ else if(((struct fd_formb *)addr)->format_version !=
+ FD_FORMAT_VERSION)
+ error = EINVAL; /* wrong version of formatting prog */
+ else
+ error = fdformat(dev, (struct fd_formb *)addr, p);
+ break;
+
+ case FD_GTYPE: /* get drive type */
+ *(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft;
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ return (error);
+}
+
+#endif
diff --git a/sys/i386/isa/fdc.h b/sys/i386/isa/fdc.h
new file mode 100644
index 0000000..1542f0e
--- /dev/null
+++ b/sys/i386/isa/fdc.h
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)fd.c 7.4 (Berkeley) 5/25/91
+ * $Id:$
+ *
+ */
+
+
+/***********************************************************************\
+* Per controller structure. *
+\***********************************************************************/
+struct fdc_data
+{
+ int fdcu; /* our unit number */
+ int baseport;
+ int dmachan;
+ int flags;
+#define FDC_ATTACHED 0x01
+#define FDC_HASFTAPE 0x02
+#define FDC_TAPE_BUSY 0x04
+ struct fd_data *fd;
+ int fdu; /* the active drive */
+ struct buf head; /* Head of buf chain */
+ struct buf rhead; /* Raw head of buf chain */
+ int state;
+ int retry;
+ int status[7]; /* copy of the registers */
+};
+
+/***********************************************************************\
+* Throughout this file the following conventions will be used: *
+* fd is a pointer to the fd_data struct for the drive in question *
+* fdc is a pointer to the fdc_data struct for the controller *
+* fdu is the floppy drive unit number *
+* fdcu is the floppy controller unit number *
+* fdsu is the floppy drive unit number on that controller. (sub-unit) *
+\***********************************************************************/
+typedef int fdu_t;
+typedef int fdcu_t;
+typedef int fdsu_t;
+typedef struct fd_data *fd_p;
+typedef struct fdc_data *fdc_p;
+
+#define FDUNIT(s) (((s)>>6)&03)
+#define FDTYPE(s) ((s)&077)
diff --git a/sys/i386/isa/fdreg.h b/sys/i386/isa/fdreg.h
new file mode 100644
index 0000000..5deb02c
--- /dev/null
+++ b/sys/i386/isa/fdreg.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)fdreg.h 7.1 (Berkeley) 5/9/91
+ * $Id: fdreg.h,v 1.3 1994/02/07 04:27:10 alm Exp $
+ */
+
+/*
+ * AT floppy controller registers and bitfields
+ */
+
+/* uses NEC765 controller */
+#include "../i386/isa/ic/nec765.h"
+
+/* registers */
+#define fdout 2 /* Digital Output Register (W) */
+#define FDO_FDSEL 0x03 /* floppy device select */
+#define FDO_FRST 0x04 /* floppy controller reset */
+#define FDO_FDMAEN 0x08 /* enable floppy DMA and Interrupt */
+#define FDO_MOEN0 0x10 /* motor enable drive 0 */
+#define FDO_MOEN1 0x20 /* motor enable drive 1 */
+#define FDO_MOEN2 0x30 /* motor enable drive 2 */
+#define FDO_MOEN3 0x40 /* motor enable drive 3 */
+
+#define fdsts 4 /* NEC 765 Main Status Register (R) */
+#define fddata 5 /* NEC 765 Data Register (R/W) */
+
+#define fdctl 7 /* Control Register (W) */
+#define FDC_500KBPS 0x00 /* 500KBPS MFM drive transfer rate */
+#define FDC_300KBPS 0x01 /* 300KBPS MFM drive transfer rate */
+#define FDC_250KBPS 0x02 /* 250KBPS MFM drive transfer rate */
+#define FDC_125KBPS 0x03 /* 125KBPS FM drive transfer rate */
+
+#define fdin 7 /* Digital Input Register (R) */
+#define FDI_DCHG 0x80 /* diskette has been changed */
+
diff --git a/sys/i386/isa/ft.c b/sys/i386/isa/ft.c
new file mode 100644
index 0000000..5f4f62c
--- /dev/null
+++ b/sys/i386/isa/ft.c
@@ -0,0 +1,2129 @@
+/*
+ * Copyright (c) 1993 Steve Gerakines
+ *
+ * This is freely redistributable software. You may do anything you
+ * wish with it, so long as the above notice stays intact.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ft.c - QIC-40/80 floppy tape driver
+ * $Id: ft.c,v 1.4 1994/02/14 22:24:28 nate Exp $
+ *
+ *
+ * 01/26/94 v0.3b - Jim Babb
+ * Got rid of the hard coded device selection. Moved (some of) the
+ * static variables into a structure for support of multiple devices.
+ * ( still has a way to go for 2 controllers - but closer )
+ * Changed the interface with fd.c so we no longer 'steal' it's
+ * driver routine vectors.
+ *
+ * 10/30/93 v0.3
+ * Fixed a couple more bugs. Reading was sometimes looping when an
+ * an error such as address-mark-missing was encountered. Both
+ * reading and writing was having more backup-and-retries than was
+ * necessary. Added support to get hardware info. Updated for use
+ * with FreeBSD.
+ *
+ * 09/15/93 v0.2 pl01
+ * Fixed a bunch of bugs: extra isa_dmadone() in async_write() (shouldn't
+ * matter), fixed double buffering in async_req(), changed tape_end() in
+ * set_fdcmode() to reduce unexpected interrupts, changed end of track
+ * processing in async_req(), protected more of ftreq_rw() with an
+ * splbio(). Changed some of the ftreq_*() functions so that they wait
+ * for inactivity and then go, instead of aborting immediately.
+ *
+ * 08/07/93 v0.2 release
+ * Shifted from ftstrat to ioctl support for I/O. Streaming is now much
+ * more reliable. Added internal support for error correction, QIC-40,
+ * and variable length tapes. Random access of segments greatly
+ * improved. Formatting and verification support is close but still
+ * incomplete.
+ *
+ * 06/03/93 v0.1 Alpha release
+ * Hopefully the last re-write. Many bugs fixed, many remain.
+ */
+
+#include "ft.h"
+#if NFT > 0
+#include "fd.h"
+
+#include <sys/param.h>
+#include <sys/dkbad.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/ftape.h>
+#include <machine/pio.h>
+#include "i386/isa/isa_device.h"
+#include "i386/isa/fdreg.h"
+#include "i386/isa/fdc.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/rtc.h"
+#include "ftreg.h"
+
+/* Enable or disable debugging messages. */
+#define FTDBGALL 0 /* everything */
+/* #define DPRT(a) printf a */
+#define DPRT(a)
+
+/* Constants private to the driver */
+#define FTPRI (PRIBIO) /* sleep priority */
+
+/* The following items are needed from the fd driver. */
+extern int in_fdc(int); /* read fdc registers */
+extern int out_fdc(int, int); /* write fdc registers */
+
+extern int hz; /* system clock rate */
+
+/* Type of tape attached */
+/* use numbers that don't interfere with the possible floppy types */
+#define NO_TYPE 0 /* (same as NO_TYPE in fd.c) */
+ /* F_TAPE_TYPE must match value in fd.c */
+#define F_TAPE_TYPE 0x020 /* bit for ft->types to indicate tape */
+#define FT_MOUNTAIN (F_TAPE_TYPE | 1)
+#define FT_COLORADO (F_TAPE_TYPE | 2)
+
+
+/* Mode FDC is currently in: tape or disk */
+enum { FDC_TAPE_MODE, FDC_DISK_MODE };
+
+/* Command we are awaiting completion of */
+enum { FTCMD_NONE, FTCMD_RESET, FTCMD_RECAL, FTCMD_SEEK, FTCMD_READID };
+
+/* Tape interrupt status of current request */
+enum { FTSTS_NONE, FTSTS_SNOOZE, FTSTS_INTERRUPT, FTSTS_TIMEOUT };
+
+/* Tape I/O status */
+enum {
+ FTIO_READY, /* No I/O activity */
+ FTIO_READING, /* Currently reading blocks */
+ FTIO_RDAHEAD, /* Currently reading ahead */
+ FTIO_WRITING /* Buffers are being written */
+};
+
+/* Current tape mode */
+enum {
+ FTM_PRIMARY, /* Primary mode */
+ FTM_VERIFY, /* Verify mode */
+ FTM_FORMAT, /* Format mode */
+ FTM_DIAG1, /* Diagnostic mode 1 */
+ FTM_DIAG2 /* Diagnostic mode 2 */
+};
+
+/* Tape geometries table */
+QIC_Geom ftgtbl[] = {
+ { 0, 0, "Unformatted", "Unknown", 0, 0, 0, 0, 0 }, /* XXX */
+ { 1, 1, "QIC-40", "205/550", 20, 68, 2176, 128, 21760 },
+ { 1, 2, "QIC-40", "307.5/550", 20, 102, 3264, 128, 32640 },
+ { 1, 3, "QIC-40", "295/900", 0, 0, 0, 0, 0 }, /* ??? */
+ { 1, 4, "QIC-40", "1100/550", 20, 365, 11680, 128, 32512 },
+ { 1, 5, "QIC-40", "1100/900", 0, 0, 0, 0, 0 }, /* ??? */
+ { 2, 1, "QIC-80", "205/550", 28, 100, 3200, 128, 19200 },
+ { 2, 2, "QIC-80", "307.5/550", 28, 150, 4800, 128, 19200 },
+ { 2, 3, "QIC-80", "295/900", 0, 0, 0, 0, 0 }, /* ??? */
+ { 2, 4, "QIC-80", "1100/550", 28, 537, 17184, 128, 32512 },
+ { 2, 5, "QIC-80", "1100/900", 0, 0, 0, 0, 0 }, /* ??? */
+ { 3, 1, "QIC-500", "205/550", 0, 0, 0, 0, 0 }, /* ??? */
+ { 3, 2, "QIC-500", "307.5/550", 0, 0, 0, 0, 0 }, /* ??? */
+ { 3, 3, "QIC-500", "295/900", 0, 0, 0, 0, 0 }, /* ??? */
+ { 3, 4, "QIC-500", "1100/550", 0, 0, 0, 0, 0 }, /* ??? */
+ { 3, 5, "QIC-500", "1100/900", 0, 0, 0, 0, 0 } /* ??? */
+};
+#define NGEOM (sizeof(ftgtbl) / sizeof(QIC_Geom))
+
+QIC_Geom *ftg = NULL; /* Current tape's geometry */
+
+/*
+ * things relating to asynchronous commands
+ */
+static int astk_depth; /* async_cmd stack depth */
+static int awr_state; /* state of async write */
+static int ard_state; /* state of async read */
+static int arq_state; /* state of async request */
+static int async_retries; /* retries, one per invocation */
+static int async_func; /* function to perform */
+static int async_state; /* state current function is at */
+static int async_arg[5]; /* up to 5 arguments for async cmds */
+static int async_ret; /* return value */
+
+/* List of valid async (interrupt driven) tape support functions. */
+enum {
+ ACMD_NONE, /* no command */
+ ACMD_SEEK, /* command seek */
+ ACMD_STATUS, /* report status */
+ ACMD_STATE, /* wait for state bits to be true */
+ ACMD_SEEKSTS, /* perform command and wait for status */
+ ACMD_READID, /* read id */
+ ACMD_RUNBLK /* ready tape for I/O on the given block */
+};
+
+/* Call another asyncronous command from within async_cmd(). */
+#define CALL_ACMD(r,f,a,b,c,d,e) \
+ astk[astk_depth].over_retries = async_retries; \
+ astk[astk_depth].over_func = async_func; \
+ astk[astk_depth].over_state = (r); \
+ for (i = 0; i < 5; i++) \
+ astk[astk_depth].over_arg[i] = async_arg[i]; \
+ async_func = (f); async_state = 0; async_retries = 0; \
+ async_arg[0]=(a); async_arg[1]=(b); async_arg[2]=(c); \
+ async_arg[3]=(d); async_arg[4]=(e); \
+ astk_depth++; \
+ goto restate
+
+/* Perform an asyncronous command from outside async_cmd(). */
+#define ACMD_FUNC(r,f,a,b,c,d,e) over_async = (r); astk_depth = 0; \
+ async_func = (f); async_state = 0; async_retries = 0; \
+ async_arg[0]=(a); async_arg[1]=(b); async_arg[2]=(c); \
+ async_arg[3]=(d); async_arg[4]=(e); \
+ async_cmd(ftu); \
+ return
+
+/* Various wait channels */
+static struct {
+ int buff_avail;
+ int iosts_change;
+ int long_delay;
+ int intr_wait;
+} ftsem;
+
+/***********************************************************************\
+* Per controller structure. *
+\***********************************************************************/
+extern struct fdc_data fdc_data[NFDC];
+
+/***********************************************************************\
+* Per tape drive structure. *
+\***********************************************************************/
+struct ft_data {
+ struct fdc_data *fdc; /* pointer to controller structure */
+ int ftsu; /* this units number on this controller */
+ int type; /* Drive type (Mountain, Colorado) */
+/* QIC_Geom *ftg; */ /* pointer to Current tape's geometry */
+ int flags;
+ int cmd_wait; /* Command we are awaiting completion of */
+ int sts_wait; /* Tape interrupt status of current request */
+ int io_sts; /* Tape I/O status */
+ int mode;
+ int pcn; /* present cylinder number */
+ int attaching; /* true when ft is attaching */
+ unsigned char *xptr; /* pointer to buffer blk to xfer */
+ int xcnt; /* transfer count */
+ int xblk; /* block number to transfer */
+ SegReq *curseg; /* Current segment to do I/O on */
+ SegReq *bufseg; /* Buffered segment to r/w ahead */
+ /* the next 3 should be defines in 'flags' */
+ int active; /* TRUE if transfer is active */
+ int rdonly; /* TRUE if tape is read-only */
+ int newcart; /* TRUE if new cartridge detected */
+ int laststs; /* last reported status code */
+ int lastcfg; /* last reported QIC config */
+ int lasterr; /* last QIC error code */
+ int lastpos; /* last known segment number */
+ int moving; /* TRUE if tape is moving */
+ int rid[7]; /* read_id return values */
+
+} ft_data[NFT];
+
+/***********************************************************************\
+* Throughout this file the following conventions will be used: *
+* ft is a pointer to the ft_data struct for the drive in question *
+* fdc is a pointer to the fdc_data struct for the controller *
+* ftu is the tape drive unit number *
+* fdcu is the floppy controller unit number *
+* ftsu is the tape drive unit number on that controller. (sub-unit) *
+\***********************************************************************/
+
+
+typedef int ftu_t;
+typedef int ftsu_t;
+typedef struct ft_data *ft_p;
+
+#define id_physid id_scsiid /* this biotab field doubles as a field */
+ /* for the physical unit number on the controller */
+
+int ftopen(dev_t, int);
+int ftclose(dev_t, int);
+void ftstrategy(struct buf *);
+int ftioctl(dev_t, int, caddr_t, int, struct proc *);
+int ftdump(dev_t);
+int ftsize(dev_t);
+static void ft_timeout(caddr_t arg1);
+void async_cmd(ftu_t);
+void async_req(ftu_t, int);
+void async_read(ftu_t, int);
+void async_write(ftu_t, int);
+void tape_start(ftu_t);
+void tape_end(ftu_t);
+void tape_inactive(ftu_t);
+
+
+
+
+
+/*
+ * Probe/attach floppy tapes.
+ */
+int ftattach(isadev, fdup)
+ struct isa_device *isadev, *fdup;
+{
+ fdcu_t fdcu = isadev->id_unit; /* fdc active unit */
+ fdc_p fdc = fdc_data + fdcu; /* pointer to controller structure */
+ ftu_t ftu = fdup->id_unit;
+ ft_p ft;
+ ftsu_t ftsu = fdup->id_physid;
+
+ if (ftu >= NFT)
+ return 0;
+ ft = &ft_data[ftu];
+ /* Probe for tape */
+ ft->attaching = 1;
+ ft->type = NO_TYPE;
+ ft->fdc = fdc;
+ ft->ftsu = ftsu;
+
+ tape_start(ftu); /* ready controller for tape */
+ tape_cmd(ftu, QC_COL_ENABLE1);
+ tape_cmd(ftu, QC_COL_ENABLE2);
+ if (tape_status(ftu) >= 0) {
+ ft->type = FT_COLORADO;
+ fdc->flags |= FDC_HASFTAPE;
+ printf(" [%d: ft%d: Colorado tape]",
+ fdup->id_physid, fdup->id_unit );
+ tape_cmd(ftu, QC_COL_DISABLE);
+ goto out;
+ }
+
+ tape_start(ftu); /* ready controller for tape */
+ tape_cmd(ftu, QC_MTN_ENABLE1);
+ tape_cmd(ftu, QC_MTN_ENABLE2);
+ if (tape_status(ftu) >= 0) {
+ ft->type = FT_MOUNTAIN;
+ fdc->flags |= FDC_HASFTAPE;
+ printf(" [%d: ft%d: Mountain tape]",
+ fdup->id_physid, fdup->id_unit );
+ tape_cmd(ftu, QC_MTN_DISABLE);
+ goto out;
+ }
+
+out:
+ tape_end(ftu);
+ ft->attaching = 0;
+ return(ft->type);
+}
+
+
+/*
+ * Perform common commands asynchronously.
+ */
+void async_cmd(ftu_t ftu) {
+ ft_p ft = &ft_data[ftu];
+ fdcu_t fdcu = ft->fdc->fdcu;
+ int cmd, i, st0, st3, pcn;
+ static int bitn, retval, retpos, nbits, newcn;
+ static struct {
+ int over_func;
+ int over_state;
+ int over_retries;
+ int over_arg[5];
+ } astk[15];
+ static int wanttrk, wantblk, wantdir;
+ static int curpos, curtrk, curblk, curdir, curdiff;
+ static int errcnt = 0;
+
+restate:
+#if FTDBGALL
+ DPRT(("async_cmd state: func: %d state: %d\n", async_func, async_state));
+#endif
+ switch(async_func) {
+ case ACMD_SEEK:
+ /*
+ * Arguments:
+ * 0 - command to perform
+ */
+ switch (async_state) {
+ case 0:
+ cmd = async_arg[0];
+#if FTDBGALL
+ DPRT(("===>async_seek cmd = %d\n", cmd));
+#endif
+ newcn = (cmd <= ft->pcn) ? ft->pcn - cmd : ft->pcn + cmd;
+ async_state = 1;
+ i = 0;
+ if (out_fdc(fdcu, NE7CMD_SEEK) < 0) i = 1;
+ if (!i && out_fdc(fdcu, 0x00) < 0) i = 1;
+ if (!i && out_fdc(fdcu, newcn) < 0) i = 1;
+ if (i) {
+ if (++async_retries >= 10) {
+ DPRT(("ft%d: async_cmd command seek failed!!\n", ftu));
+ goto complete;
+ }
+ DPRT(("ft%d: async_cmd command seek retry...\n",ftu));
+ async_state = 0;
+ goto restate;
+ }
+ break;
+ case 1:
+ out_fdc(fdcu, NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ pcn = in_fdc(fdcu);
+ if (st0 < 0 || pcn < 0 || newcn != pcn) {
+ if (++async_retries >= 10) {
+ DPRT(("ft%d: async_cmd seek retries exceeded\n",ftu));
+ goto complete;
+ }
+ DPRT(("ft%d: async_cmd command bad st0=$%02x pcn=$%02x\n",
+ ftu, st0, pcn));
+ async_state = 0;
+ timeout(ft_timeout, (caddr_t)ftu, hz/10);
+ break;
+ }
+ if (st0 & 0x20) { /* seek done */
+ ft->pcn = pcn;
+ }
+#if FTDBGALL
+ else
+ DPRT(("ft%d: async_seek error st0 = $%02x pcn = %d\n",
+ ftu, st0, pcn));
+#endif
+ if (async_arg[1]) goto complete;
+ async_state = 2;
+ timeout(ft_timeout, (caddr_t)ftu, hz/50);
+ break;
+ case 2:
+ goto complete;
+ /* NOTREACHED */
+ }
+ break;
+
+ case ACMD_STATUS:
+ /*
+ * Arguments:
+ * 0 - command to issue report from
+ * 1 - number of bits
+ * modifies: bitn, retval, st3
+ */
+ switch (async_state) {
+ case 0:
+ bitn = 0;
+ retval = 0;
+ cmd = async_arg[0];
+ nbits = async_arg[1];
+ DPRT(("async_status got cmd = %d nbits = %d\n", cmd,nbits));
+ CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0);
+ /* NOTREACHED */
+ case 1:
+ out_fdc(fdcu, NE7CMD_SENSED);
+ out_fdc(fdcu, 0x00);
+ st3 = in_fdc(fdcu);
+ if (st3 < 0) {
+ DPRT(("ft%d: async_status timed out on bit %d r=$%02x\n",
+ ftu,bitn,retval));
+ async_ret = -1;
+ goto complete;
+ }
+ if ((st3 & 0x10) != 0) retval |= (1 << bitn);
+ bitn++;
+ if (bitn >= (nbits+2)) {
+ if ((retval & 1) && (retval & (1 << (nbits+1)))) {
+ async_ret = (retval & ~(1<<(nbits+1))) >> 1;
+ if (async_arg[0] == QC_STATUS && async_arg[2] == 0 &&
+ (async_ret & (QS_ERROR|QS_NEWCART))) {
+ async_state = 2;
+ goto restate;
+ }
+ DPRT(("async status got $%04x ($%04x)\n", async_ret,retval));
+ } else {
+ DPRT(("ft%d: async_status failed: retval=$%04x nbits=%d\n",
+ ftu, retval,nbits));
+ async_ret = -2;
+ }
+ goto complete;
+ }
+ CALL_ACMD(1, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0);
+ /* NOTREACHED */
+ case 2:
+ if (async_ret & QS_NEWCART) ft->newcart = 1;
+ CALL_ACMD(3, ACMD_STATUS, QC_ERRCODE, 16, 1, 0, 0);
+ case 3:
+ ft->lasterr = async_ret;
+ if ((ft->lasterr & QS_NEWCART) == 0 && ft->lasterr) {
+ DPRT(("ft%d: QIC error %d occurred on cmd %d\n",
+ ftu, ft->lasterr & 0xff, ft->lasterr >> 8));
+ }
+ cmd = async_arg[0];
+ nbits = async_arg[1];
+ CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 1, 0, 0);
+ case 4:
+ goto complete;
+ case 5:
+ CALL_ACMD(6, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0);
+ case 6:
+ CALL_ACMD(7, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0);
+ case 7:
+ CALL_ACMD(8, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0);
+ case 8:
+ cmd = async_arg[0];
+ CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0, 0, 0);
+ }
+ break;
+
+ case ACMD_STATE:
+ /*
+ * Arguments:
+ * 0 - status bits to check
+ */
+ switch(async_state) {
+ case 0:
+ CALL_ACMD(1, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0);
+ case 1:
+ if ((async_ret & async_arg[0]) != 0) goto complete;
+ async_state = 0;
+ if (++async_retries == 360) { /* 90 secs. */
+ DPRT(("ft%d: acmd_state exceeded retry count\n", ftu));
+ goto complete;
+ }
+ timeout(ft_timeout, (caddr_t)ftu, hz/4);
+ break;
+ }
+ break;
+
+ case ACMD_SEEKSTS:
+ /*
+ * Arguments:
+ * 0 - command to perform
+ * 1 - status bits to check
+ * 2 - (optional) seconds to wait until completion
+ */
+ switch(async_state) {
+ case 0:
+ cmd = async_arg[0];
+ async_retries = (async_arg[2]) ? (async_arg[2]*4) : 10;
+ CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0, 0, 0);
+ case 1:
+ CALL_ACMD(2, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0);
+ case 2:
+ if ((async_ret & async_arg[1]) != 0) goto complete;
+ if (--async_retries == 0) {
+ DPRT(("ft%d: acmd_seeksts retries exceeded\n", ftu));
+ goto complete;
+ }
+ async_state = 1;
+ timeout(ft_timeout, (caddr_t)ftu, hz/4);
+ break;
+ }
+ break;
+
+ case ACMD_READID:
+ /*
+ * Arguments: (none)
+ */
+ switch(async_state) {
+ case 0:
+ if (!ft->moving) {
+ CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
+ /* NOTREACHED */
+ }
+ async_state = 1;
+ out_fdc(fdcu, 0x4a); /* READ_ID */
+ out_fdc(fdcu, 0);
+ break;
+ case 1:
+ for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu);
+ async_ret = (ft->rid[3]*ftg->g_fdtrk) +
+ (ft->rid[4]*ftg->g_fdside) + ft->rid[5] - 1;
+ DPRT(("readid st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d\n",
+ ft->rid[0], ft->rid[1], ft->rid[2], ft->rid[3],
+ ft->rid[4], ft->rid[5], async_ret));
+ if ((ft->rid[0] & 0xc0) == 0x40) {
+ if (++errcnt >= 10) {
+ DPRT(("ft%d: acmd_readid errcnt exceeded\n", fdcu));
+ async_ret = ft->lastpos;
+ errcnt = 0;
+ goto complete;
+ }
+ if (errcnt > 2) {
+ ft->moving = 0;
+ CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
+ }
+ DPRT(("readid retry...\n"));
+ async_state = 0;
+ goto restate;
+ }
+ if ((async_ret % ftg->g_blktrk) == (ftg->g_blktrk-1)) {
+ DPRT(("acmd_readid detected last block on track\n"));
+ retpos = async_ret;
+ CALL_ACMD(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0);
+ /* NOTREACHED */
+ }
+ ft->lastpos = async_ret;
+ errcnt = 0;
+ goto complete;
+ /* NOTREACHED */
+ case 2:
+ CALL_ACMD(3, ACMD_STATE, QS_READY, 0, 0, 0, 0);
+ case 3:
+ ft->moving = 0;
+ async_ret = retpos+1;
+ goto complete;
+ case 4:
+ CALL_ACMD(5, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0);
+ case 5:
+ ft->moving = 1;
+ async_state = 0;
+ timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
+ break;
+ }
+ break;
+
+ case ACMD_RUNBLK:
+ /*
+ * Arguments:
+ * 0 - block number I/O will be performed on
+ *
+ * modifies: curpos
+ */
+ switch (async_state) {
+ case 0:
+ wanttrk = async_arg[0] / ftg->g_blktrk;
+ wantblk = async_arg[0] % ftg->g_blktrk;
+ wantdir = wanttrk & 1;
+ ft->moving = 0;
+ CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
+ case 1:
+ curtrk = wanttrk;
+ curdir = curtrk & 1;
+ DPRT(("Changing to track %d\n", wanttrk));
+ CALL_ACMD(2, ACMD_SEEK, QC_SEEKTRACK, 0, 0, 0, 0);
+ case 2:
+ cmd = wanttrk+2;
+ CALL_ACMD(3, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0);
+ case 3:
+ CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0);
+ case 4:
+ ft->laststs = async_ret;
+ if (wantblk == 0) {
+ curblk = 0;
+ cmd = (wantdir) ? QC_SEEKEND : QC_SEEKSTART;
+ CALL_ACMD(6, ACMD_SEEKSTS, cmd, QS_READY, 90, 0, 0);
+ }
+ if (ft->laststs & QS_BOT) {
+ DPRT(("Tape is at BOT\n"));
+ curblk = (wantdir) ? 4800 : 0;
+ async_state = 6;
+ goto restate;
+ }
+ if (ft->laststs & QS_EOT) {
+ DPRT(("Tape is at EOT\n"));
+ curblk = (wantdir) ? 0 : 4800;
+ async_state = 6;
+ goto restate;
+ }
+ CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0);
+ case 5:
+ curtrk = (async_ret+1) / ftg->g_blktrk;
+ curblk = (async_ret+1) % ftg->g_blktrk;
+ DPRT(("gotid: curtrk=%d wanttrk=%d curblk=%d wantblk=%d\n",
+ curtrk, wanttrk, curblk, wantblk));
+ if (curtrk != wanttrk) { /* oops! */
+ DPRT(("oops!! wrong track!\n"));
+ CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
+ }
+ async_state = 6;
+ goto restate;
+ case 6:
+ DPRT(("curtrk = %d nextblk = %d\n", curtrk, curblk));
+ if (curblk == wantblk) {
+ ft->lastpos = curblk - 1;
+ async_ret = ft->lastpos;
+ if (ft->moving) goto complete;
+ CALL_ACMD(7, ACMD_STATE, QS_READY, 0, 0, 0, 0);
+ }
+ if (curblk > wantblk) { /* passed it */
+ ft->moving = 0;
+ CALL_ACMD(10, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
+ }
+ if ((wantblk - curblk) <= 96) { /* approaching it */
+ CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0);
+ }
+ /* way up ahead */
+ ft->moving = 0;
+ CALL_ACMD(14, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
+ break;
+ case 7:
+ ft->moving = 1;
+ CALL_ACMD(8, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0);
+ break;
+ case 8:
+ async_state = 9;
+ timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
+ break;
+ case 9:
+ goto complete;
+ case 10:
+ curdiff = ((curblk - wantblk) / QCV_BLKSEG) + 2;
+ if (curdiff >= ftg->g_segtrk) curdiff = ftg->g_segtrk - 1;
+ DPRT(("pos %d past %d, reverse %d\n", curblk, wantblk, curdiff));
+ CALL_ACMD(11, ACMD_SEEK, QC_SEEKREV, 0, 0, 0, 0);
+ case 11:
+ DPRT(("reverse 1 done\n"));
+ CALL_ACMD(12, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0, 0, 0);
+ case 12:
+ DPRT(("reverse 2 done\n"));
+ CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90, 0, 0);
+ case 13:
+ CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0);
+ case 14:
+ curdiff = ((wantblk - curblk) / QCV_BLKSEG) - 2;
+ if (curdiff < 0) curdiff = 0;
+ DPRT(("pos %d before %d, forward %d\n", curblk, wantblk, curdiff));
+ CALL_ACMD(15, ACMD_SEEK, QC_SEEKFWD, 0, 0, 0, 0);
+ case 15:
+ DPRT(("forward 1 done\n"));
+ CALL_ACMD(16, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0, 0, 0);
+ case 16:
+ DPRT(("forward 2 done\n"));
+ CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90, 0, 0);
+ }
+ break;
+ }
+
+ return;
+
+complete:
+ if (astk_depth) {
+ astk_depth--;
+ async_retries = astk[astk_depth].over_retries;
+ async_func = astk[astk_depth].over_func;
+ async_state = astk[astk_depth].over_state;
+ for(i = 0; i < 5; i++)
+ async_arg[i] = astk[astk_depth].over_arg[i];
+ goto restate;
+ }
+ async_func = ACMD_NONE;
+ async_state = 0;
+ switch (ft->io_sts) {
+ case FTIO_READY:
+ async_req(ftu, 2);
+ break;
+ case FTIO_READING:
+ async_read(ftu, 2);
+ break;
+ case FTIO_WRITING:
+ async_write(ftu, 2);
+ break;
+ default:
+ DPRT(("ft%d: bad async_cmd ending I/O state!\n", ftu));
+ break;
+ }
+}
+
+
+/*
+ * Entry point for the async request processor.
+ */
+void async_req(ftu_t ftu, int from)
+{
+ ft_p ft = &ft_data[ftu];
+ SegReq *sp;
+ static int over_async, lastreq, domore;
+ int cmd;
+
+ if (from == 2) arq_state = over_async;
+
+restate:
+ switch (arq_state) {
+ case 0: /* Process segment */
+ ft->io_sts = ft->curseg->reqtype;
+ if (ft->io_sts == FTIO_WRITING)
+ async_write(ftu, from);
+ else
+ async_read(ftu, from);
+ if (ft->io_sts != FTIO_READY) return;
+
+ /* Swap buffered and current segment */
+ lastreq = ft->curseg->reqtype;
+ ft->curseg->reqtype = FTIO_READY;
+ sp = ft->curseg;
+ ft->curseg = ft->bufseg;
+ ft->bufseg = sp;
+
+ wakeup((caddr_t)&ftsem.buff_avail);
+
+ /* Detect end of track */
+ if (((ft->xblk / QCV_BLKSEG) % ftg->g_segtrk) == 0) {
+ domore = (ft->curseg->reqtype != FTIO_READY);
+ ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0);
+ }
+ arq_state = 1;
+ goto restate;
+
+ case 1: /* Next request */
+ if (ft->curseg->reqtype != FTIO_READY) {
+ ft->curseg->reqcrc = 0;
+ arq_state = ard_state = awr_state = 0;
+ ft->xblk = ft->curseg->reqblk;
+ ft->xcnt = 0;
+ ft->xptr = ft->curseg->buff;
+ DPRT(("I/O reqblk = %d\n", ft->curseg->reqblk));
+ goto restate;
+ }
+ if (lastreq == FTIO_READING) {
+ ft->curseg->reqtype = FTIO_RDAHEAD;
+ ft->curseg->reqblk = ft->xblk;
+ ft->curseg->reqcrc = 0;
+ ft->curseg->reqcan = 0;
+ bzero(ft->curseg->buff, QCV_SEGSIZE);
+ arq_state = ard_state = awr_state = 0;
+ ft->xblk = ft->curseg->reqblk;
+ ft->xcnt = 0;
+ ft->xptr = ft->curseg->buff;
+ DPRT(("Processing readahead reqblk = %d\n", ft->curseg->reqblk));
+ goto restate;
+ }
+ if (ft->moving) {
+ DPRT(("No more I/O.. Stopping.\n"));
+ ACMD_FUNC(7, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
+ break;
+ }
+ arq_state = 7;
+ goto restate;
+
+ case 2: /* End of track */
+ ft->moving = 0;
+ ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0);
+ break;
+
+ case 3:
+ DPRT(("async_req seek head to track %d\n", ft->xblk / ftg->g_blktrk));
+ ACMD_FUNC(4, ACMD_SEEK, QC_SEEKTRACK, 0, 0, 0, 0);
+ break;
+
+ case 4:
+ cmd = (ft->xblk / ftg->g_blktrk) + 2;
+ if (domore) {
+ ACMD_FUNC(5, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0);
+ } else {
+ ACMD_FUNC(7, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0);
+ }
+ break;
+
+ case 5:
+ ft->moving = 1;
+ ACMD_FUNC(6, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0);
+ break;
+
+ case 6:
+ arq_state = 1;
+ timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
+ break;
+
+ case 7:
+ ft->moving = 0;
+
+ /* Check one last time to see if a request came in. */
+ if (ft->curseg->reqtype != FTIO_READY) {
+ DPRT(("async_req: Never say no!\n"));
+ arq_state = 1;
+ goto restate;
+ }
+
+ /* Time to rest. */
+ ft->active = 0;
+ wakeup((caddr_t)&ftsem.iosts_change); /* wakeup those who want an i/o chg */
+ break;
+ }
+}
+
+/*
+ * Entry for async read.
+ */
+void async_read(ftu_t ftu, int from)
+{
+ ft_p ft = &ft_data[ftu];
+ fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
+ unsigned long paddr;
+ int i, cmd, newcn, rddta[7];
+ int st0, pcn, where;
+ static int over_async;
+
+ if (from == 2) ard_state = over_async;
+
+restate:
+#if FTDBGALL
+ DPRT(("async_read: state: %d from = %d\n", ard_state, from));
+#endif
+ switch (ard_state) {
+ case 0: /* Start off */
+ /* If tape is not at desired position, stop and locate */
+ if (ft->lastpos != (ft->xblk-1)) {
+ DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n",
+ ftu, ft->lastpos, ft->xblk));
+ ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0, 0, 0);
+ }
+
+ /* Tape is in position but stopped. */
+ if (!ft->moving) {
+ DPRT(("async_read ******STARTING TAPE\n"));
+ ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0);
+ }
+ ard_state = 1;
+ goto restate;
+
+ case 1: /* Start DMA */
+ /* Tape is now moving and in position-- start DMA now! */
+ isa_dmastart(B_READ, ft->xptr, QCV_BLKSIZE, 2);
+ out_fdc(fdcu, 0x66); /* read */
+ out_fdc(fdcu, 0x00); /* unit */
+ out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */
+ out_fdc(fdcu, ft->xblk / ftg->g_fdside); /* head */
+ out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* sector */
+ out_fdc(fdcu, 0x03); /* 1K sectors */
+ out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* count */
+ out_fdc(fdcu, 0x74); /* gap length */
+ out_fdc(fdcu, 0xff); /* transfer size */
+ ard_state = 2;
+ break;
+
+ case 2: /* DMA completed */
+ /* Transfer complete, get status */
+ for (i = 0; i < 7; i++) rddta[i] = in_fdc(fdcu);
+ isa_dmadone(B_READ, ft->xptr, QCV_BLKSIZE, 2);
+
+#if FTDBGALL
+ /* Compute where the controller thinks we are */
+ where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5]-1;
+ DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
+ rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
+ where, ft->xblk));
+#endif
+
+ /* Check for errors */
+ if ((rddta[0] & 0xc0) != 0x00) {
+ if (rddta[1] & 0x04) {
+ /* Probably wrong position */
+ ft->lastpos = ft->xblk;
+ ard_state = 0;
+ goto restate;
+ } else {
+ /* CRC/Address-mark/Data-mark, et. al. */
+ DPRT(("ft%d: CRC error on block %d\n", fdcu, ft->xblk));
+ ft->curseg->reqcrc |= (1 << ft->xcnt);
+ }
+ }
+
+ /* Otherwise, transfer completed okay. */
+ ft->lastpos = ft->xblk;
+ ft->xblk++;
+ ft->xcnt++;
+ ft->xptr += QCV_BLKSIZE;
+ if (ft->xcnt < QCV_BLKSEG && ft->curseg->reqcan == 0) {
+ ard_state = 0;
+ goto restate;
+ }
+ DPRT(("Read done.. Cancel = %d\n", ft->curseg->reqcan));
+ ft->io_sts = FTIO_READY;
+ break;
+
+ case 3:
+ ft->moving = 1;
+ ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0);
+ break;
+
+ case 4:
+ ard_state = 1;
+ timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
+ break;
+
+ default:
+ DPRT(("ft%d: bad async_read state %d!!\n", ftu, ard_state));
+ break;
+ }
+}
+
+
+/*
+ * Entry for async write. If from is 0, this came from the interrupt
+ * routine, if it's 1 then it was a timeout, if it's 2, then an
+ * async_cmd completed.
+ */
+void async_write(ftu_t ftu, int from)
+{
+ ft_p ft = &ft_data[ftu];
+ fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
+ unsigned long paddr;
+ int i, cmd, newcn, rddta[7];
+ int st0, pcn, where;
+ static int over_async;
+ static int retries = 0;
+
+ if (from == 2) awr_state = over_async;
+
+restate:
+#if FTDBGALL
+ DPRT(("async_write: state: %d from = %d\n", awr_state, from));
+#endif
+ switch (awr_state) {
+ case 0: /* Start off */
+ /* If tape is not at desired position, stop and locate */
+ if (ft->lastpos != (ft->xblk-1)) {
+ DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n",
+ ftu, ft->lastpos, ft->xblk));
+ ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0, 0, 0);
+ }
+
+ /* Tape is in position but stopped. */
+ if (!ft->moving) {
+ DPRT(("async_write ******STARTING TAPE\n"));
+ ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0);
+ }
+ awr_state = 1;
+ goto restate;
+
+ case 1: /* Start DMA */
+ /* Tape is now moving and in position-- start DMA now! */
+ isa_dmastart(B_WRITE, ft->xptr, QCV_BLKSIZE, 2);
+ out_fdc(fdcu, 0x45); /* write */
+ out_fdc(fdcu, 0x00); /* unit */
+ out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */
+ out_fdc(fdcu, ft->xblk / ftg->g_fdside); /* head */
+ out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* sector */
+ out_fdc(fdcu, 0x03); /* 1K sectors */
+ out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* count */
+ out_fdc(fdcu, 0x74); /* gap length */
+ out_fdc(fdcu, 0xff); /* transfer size */
+ awr_state = 2;
+ break;
+
+ case 2: /* DMA completed */
+ /* Transfer complete, get status */
+ for (i = 0; i < 7; i++) rddta[i] = in_fdc(fdcu);
+ isa_dmadone(B_WRITE, ft->xptr, QCV_BLKSIZE, 2);
+
+#if FTDBGALL
+ /* Compute where the controller thinks we are */
+ where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5]-1;
+ DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
+ rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
+ where, ft->xblk));
+#endif
+
+ /* Check for errors */
+ if ((rddta[0] & 0xc0) != 0x00) {
+ if (rddta[1] & 0x04) {
+ /* Probably wrong position */
+ ft->lastpos = ft->xblk;
+ awr_state = 0;
+ goto restate;
+ } else if (retries < 5) {
+ /* Something happened -- try again */
+ ft->lastpos = ft->xblk;
+ awr_state = 0;
+ retries++;
+ goto restate;
+ } else {
+ /*
+ * Retries failed. Note the unrecoverable error.
+ * Marking the block as bad is fairly useless.
+ */
+ printf("ft%d: unrecoverable write error on block %d\n",
+ ftu, ft->xblk);
+ ft->curseg->reqcrc |= (1 << ft->xcnt);
+ }
+ }
+
+ /* Otherwise, transfer completed okay. */
+ retries = 0;
+ ft->lastpos = ft->xblk;
+ ft->xblk++;
+ ft->xcnt++;
+ ft->xptr += QCV_BLKSIZE;
+ if (ft->xcnt < QCV_BLKSEG) {
+ awr_state = 0; /* next block */
+ goto restate;
+ }
+#if FTDBGALL
+ DPRT(("Write done.\n"));
+#endif
+ ft->io_sts = FTIO_READY;
+ break;
+
+ case 3:
+ ft->moving = 1;
+ ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0);
+ break;
+
+ case 4:
+ awr_state = 1;
+ timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
+ break;
+
+ default:
+ DPRT(("ft%d: bad async_write state %d!!\n", ftu, awr_state));
+ break;
+ }
+}
+
+
+/*
+ * Interrupt handler for active tape. Bounced off of fdintr().
+ */
+int ftintr(ftu_t ftu)
+{
+ int st0, pcn, i;
+ ft_p ft = &ft_data[ftu];
+ fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
+ st0 = 0;
+ pcn = 0;
+
+ /* I/O segment transfer completed */
+ if (ft->active) {
+ if (async_func != ACMD_NONE) {
+ async_cmd(ftu);
+ return(1);
+ }
+#if FTDBGALL
+ DPRT(("Got request interrupt\n"));
+#endif
+ async_req(ftu, 0);
+ return(1);
+ }
+
+ /* Get interrupt status */
+ if (ft->cmd_wait != FTCMD_READID) {
+ out_fdc(fdcu, NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ pcn = in_fdc(fdcu);
+ }
+
+ if (ft->cmd_wait == FTCMD_NONE || ft->sts_wait != FTSTS_SNOOZE) {
+huh_what:
+ printf("ft%d: unexpected interrupt; st0 = $%02x pcn = %d\n",
+ ftu, st0, pcn);
+ return(1);
+ }
+
+ switch (ft->cmd_wait) {
+ case FTCMD_RESET:
+ ft->sts_wait = FTSTS_INTERRUPT;
+ wakeup((caddr_t)&ftsem.intr_wait);
+ break;
+ case FTCMD_RECAL:
+ case FTCMD_SEEK:
+ if (st0 & 0x20) { /* seek done */
+ ft->sts_wait = FTSTS_INTERRUPT;
+ ft->pcn = pcn;
+ wakeup((caddr_t)&ftsem.intr_wait);
+ }
+#if FTDBGALL
+ else
+ DPRT(("ft%d: seek error st0 = $%02x pcn = %d\n",
+ ftu, st0, pcn));
+#endif
+ break;
+ case FTCMD_READID:
+ for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu);
+ ft->sts_wait = FTSTS_INTERRUPT;
+ wakeup((caddr_t)&ftsem.intr_wait);
+ break;
+
+ default:
+ goto huh_what;
+ }
+
+ return(1);
+}
+
+/*
+ * Interrupt timeout routine.
+ */
+static void ft_timeout(caddr_t arg1)
+{
+ int s;
+ ftu_t ftu = (ftu_t)arg1;
+ ft_p ft = &ft_data[ftu];
+
+ s = splbio();
+ if (ft->active) {
+ if (async_func != ACMD_NONE) {
+ async_cmd(ftu);
+ splx(s);
+ return;
+ }
+ async_req(ftu, 1);
+ } else {
+ ft->sts_wait = FTSTS_TIMEOUT;
+ wakeup((caddr_t)&ftsem.intr_wait);
+ }
+ splx(s);
+}
+
+/*
+ * Wait for a particular interrupt to occur. ftintr() will wake us up
+ * if it sees what we want. Otherwise, time out and return error.
+ * Should always disable ints before trigger is sent and calling here.
+ */
+int ftintr_wait(ftu_t ftu, int cmd, int ticks)
+{
+ int retries, st0, pcn;
+ ft_p ft = &ft_data[ftu];
+ fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
+
+ ft->cmd_wait = cmd;
+ ft->sts_wait = FTSTS_SNOOZE;
+
+ /* At attach time, we can't rely on having interrupts serviced */
+ if (ft->attaching) {
+ switch (cmd) {
+ case FTCMD_RESET:
+ DELAY(100);
+ ft->sts_wait = FTSTS_INTERRUPT;
+ goto intrdone;
+ case FTCMD_RECAL:
+ case FTCMD_SEEK:
+ for (retries = 0; retries < 10000; retries++) {
+ out_fdc(fdcu, NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ pcn = in_fdc(fdcu);
+ if (st0 & 0x20) {
+ ft->sts_wait = FTSTS_INTERRUPT;
+ ft->pcn = pcn;
+ goto intrdone;
+ }
+ DELAY(100);
+ }
+ break;
+ }
+ ft->sts_wait = FTSTS_TIMEOUT;
+ goto intrdone;
+ }
+
+ if (ticks) timeout(ft_timeout, (caddr_t)ftu, ticks);
+ tsleep((caddr_t)&ftsem.intr_wait, FTPRI, "ftwait", 0);
+
+intrdone:
+ if (ft->sts_wait == FTSTS_TIMEOUT) { /* timeout */
+ if (ft->cmd_wait != FTCMD_RESET)
+ DPRT(("ft%d: timeout on command %d\n", ftu, ft->cmd_wait));
+ ft->cmd_wait = FTCMD_NONE;
+ ft->sts_wait = FTSTS_NONE;
+ return(1);
+ }
+
+ /* got interrupt */
+ if (ft->attaching == 0 && ticks) untimeout(ft_timeout, (caddr_t)ftu);
+ ft->cmd_wait = FTCMD_NONE;
+ ft->sts_wait = FTSTS_NONE;
+ return(0);
+}
+
+/*
+ * Recalibrate tape drive. Parameter totape is true, if we should
+ * recalibrate to tape drive settings.
+ */
+int tape_recal(ftu_t ftu, int totape)
+{
+ int s;
+ ft_p ft = &ft_data[ftu];
+ fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
+
+ DPRT(("tape_recal start\n"));
+
+ out_fdc(fdcu, NE7CMD_SPECIFY);
+ out_fdc(fdcu, (totape) ? 0xAD : 0xDF);
+ out_fdc(fdcu, 0x02);
+
+ s = splbio();
+ out_fdc(fdcu, NE7CMD_RECAL);
+ out_fdc(fdcu, 0x00);
+
+ if (ftintr_wait(ftu, FTCMD_RECAL, hz)) {
+ splx(s);
+ DPRT(("ft%d: recalibrate timeout\n", ftu));
+ return(1);
+ }
+ splx(s);
+
+ out_fdc(fdcu, NE7CMD_SPECIFY);
+ out_fdc(fdcu, (totape) ? 0xFD : 0xDF);
+ out_fdc(fdcu, 0x02);
+
+ DPRT(("tape_recal end\n"));
+ return(0);
+}
+
+static void state_timeout(caddr_t arg1, int arg2)
+{
+ ftu_t ftu = (ftu_t)arg1;
+
+ wakeup((caddr_t)&ftsem.long_delay);
+}
+
+/*
+ * Wait for a particular tape status to be met. If all is TRUE, then
+ * all states must be met, otherwise any state can be met.
+ */
+int tape_state(ftu_t ftu, int all, int mask, int seconds)
+{
+ int r, tries, maxtries;
+
+ maxtries = (seconds) ? (4 * seconds) : 1;
+ for (tries = 0; tries < maxtries; tries++) {
+ r = tape_status(ftu);
+ if (r >= 0) {
+ if (all && (r & mask) == mask) return(r);
+ if ((r & mask) != 0) return(r);
+ }
+ if (seconds) {
+ timeout(state_timeout, (caddr_t)ftu, hz/4);
+ tsleep((caddr_t)&ftsem.long_delay, FTPRI, "ftstate", 0);
+ }
+ }
+ DPRT(("ft%d: tape_state failed on mask=$%02x maxtries=%d\n",
+ ftu, mask, maxtries));
+ return(-1);
+}
+
+/*
+ * Send a QIC command to tape drive, wait for completion.
+ */
+int tape_cmd(ftu_t ftu, int cmd)
+{
+ int newcn;
+ int retries = 0;
+ int s;
+ ft_p ft = &ft_data[ftu];
+ fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
+
+ DPRT(("===> tape_cmd: %d\n",cmd));
+ newcn = (cmd <= ft->pcn) ? ft->pcn - cmd : ft->pcn + cmd;
+
+retry:
+
+ /* Perform seek */
+ s = splbio();
+ out_fdc(fdcu, NE7CMD_SEEK);
+ out_fdc(fdcu, 0x00);
+ out_fdc(fdcu, newcn);
+
+ if (ftintr_wait(ftu, FTCMD_SEEK, hz)) {
+ DPRT(("ft%d: tape_cmd seek timeout\n", ftu));
+redo:
+ splx(s);
+ if (++retries < 5) goto retry;
+ DPRT(("ft%d: tape_cmd seek failed!\n", ftu));
+ return(1);
+ }
+ splx(s);
+
+ if (ft->pcn != newcn) {
+ DPRT(("ft%d: bad seek in tape_cmd; pcn = %d newcn = %d\n",
+ ftu, ft->pcn, newcn));
+ goto redo;
+ }
+ DELAY(2500);
+ return(0);
+}
+
+/*
+ * Return status of tape drive
+ */
+int tape_status(ftu_t ftu)
+{
+ int r, err, tries;
+ ft_p ft = &ft_data[ftu];
+
+ for (r = -1, tries = 0; r < 0 && tries < 3; tries++)
+ r = qic_status(ftu, QC_STATUS, 8);
+ if (tries == 3) return(-1);
+ DPRT(("tape_status got $%04x\n",r));
+ ft->laststs = r;
+
+ if (r & (QS_ERROR|QS_NEWCART)) {
+ if (r & QS_NEWCART) ft->newcart = 1;
+ err = qic_status(ftu, QC_ERRCODE, 16);
+ ft->lasterr = err;
+ if ((r & QS_NEWCART) == 0 && err && ft->attaching == 0) {
+ DPRT(("ft%d: QIC error %d occurred on cmd %d\n",
+ ftu, err & 0xff, err >> 8));
+ }
+ r = qic_status(ftu, QC_STATUS, 8);
+ ft->laststs = r;
+ DPRT(("tape_status got error code $%04x new sts = $%02x\n",err,r));
+ }
+ ft->rdonly = (r & QS_RDONLY);
+ return(r);
+}
+
+/*
+ * Transfer control to tape drive.
+ */
+void tape_start(ftu_t ftu)
+{
+ ft_p ft = &ft_data[ftu];
+ fdc_p fdc = ft->fdc;
+ int s;
+
+ DPRT(("tape_start start\n"));
+
+ s = splbio();
+
+ /* reset, dma disable */
+ outb(fdc->baseport+fdout, 0x00);
+ (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
+
+ /* raise reset, enable DMA */
+ outb(fdc->baseport+fdout, FDO_FRST | FDO_FDMAEN);
+ (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
+
+ splx(s);
+
+ tape_recal(ftu, 1);
+
+ /* set transfer speed */
+ outb(fdc->baseport+fdctl, FDC_500KBPS);
+ DELAY(10);
+
+ DPRT(("tape_start end\n"));
+}
+
+/*
+ * Transfer control back to floppy disks.
+ */
+void tape_end(ftu_t ftu)
+{
+ ft_p ft = &ft_data[ftu];
+ fdc_p fdc = ft->fdc;
+ int s;
+
+ DPRT(("tape_end start\n"));
+ tape_recal(ftu, 0);
+
+ s = splbio();
+
+ /* reset, dma disable */
+ outb(fdc->baseport+fdout, 0x00);
+ (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
+
+ /* raise reset, enable DMA */
+ outb(fdc->baseport+fdout, FDO_FRST | FDO_FDMAEN);
+ (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
+
+ splx(s);
+
+ /* set transfer speed */
+ outb(fdc->baseport+fdctl, FDC_500KBPS);
+ DELAY(10);
+ fdc->flags &= ~FDC_TAPE_BUSY;
+
+ DPRT(("tape_end end\n"));
+}
+
+/*
+ * Wait for the driver to go inactive, cancel readahead if necessary.
+ */
+void tape_inactive(ftu_t ftu)
+{
+ ft_p ft = &ft_data[ftu];
+
+ if (ft->curseg->reqtype == FTIO_RDAHEAD) {
+ ft->curseg->reqcan = 1; /* XXX cancel rdahead */
+ while (ft->active)
+ tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftinact", 0);
+ }
+ while (ft->active)
+ tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftinact", 0);
+}
+
+/*
+ * Get the geometry of the tape currently in the drive.
+ */
+int ftgetgeom(ftu_t ftu)
+{
+ int r, i, tries;
+ int cfg, qic80, ext;
+ int sts, fmt, len;
+ ft_p ft = &ft_data[ftu];
+
+ r = tape_status(ftu);
+
+ /* XXX fix me when format mode is finished */
+ if ((r & QS_CART) == 0 || (r & QS_FMTOK) == 0) {
+ DPRT(("ftgetgeom: no cart or not formatted 0x%04x\n",r));
+ ftg = NULL;
+ ft->newcart = 1;
+ return(0);
+ }
+
+ /* Report drive configuration */
+ for (cfg = -1, tries = 0; cfg < 0 && tries < 3; tries++)
+ cfg = qic_status(ftu, QC_CONFIG, 8);
+ if (tries == 3) {
+ DPRT(("ftgetgeom report config failed\n"));
+ ftg = NULL;
+ return(-1);
+ }
+ DPRT(("ftgetgeom report config got $%04x\n", cfg));
+ ft->lastcfg = cfg;
+
+ qic80 = cfg & QCF_QIC80;
+ ext = cfg & QCF_EXTRA;
+
+/*
+ * XXX - This doesn't seem to work on my Colorado Jumbo 250...
+ * if it works on your drive, I'd sure like to hear about it.
+ */
+#if 0
+ /* Report drive status */
+ for (sts = -1, tries = 0; sts < 0 && tries < 3; tries++)
+ sts = qic_status(ftu, QC_TSTATUS, 8);
+ if (tries == 3) {
+ DPRT(("ftgetgeom report tape status failed\n"));
+ ftg = NULL;
+ return(-1);
+ }
+ DPRT(("ftgetgeom report tape status got $%04x\n", sts));
+#else
+ /*
+ * XXX - Forge a fake tape status based upon the returned
+ * configuration, since the above command or code is broken
+ * for my drive and probably other older drives.
+ */
+ sts = 0;
+ sts = (qic80) ? QTS_QIC80 : QTS_QIC40;
+ sts |= (ext) ? QTS_LEN2 : QTS_LEN1;
+#endif
+
+ fmt = sts & QTS_FMMASK;
+ len = (sts & QTS_LNMASK) >> 4;
+
+ if (fmt > QCV_NFMT) {
+ ftg = NULL;
+ printf("ft%d: unsupported tape format\n", ftu);
+ return(-1);
+ }
+ if (len > QCV_NLEN) {
+ ftg = NULL;
+ printf("ft%d: unsupported tape length\n", ftu);
+ return(-1);
+ }
+
+ /* Look up geometry in the table */
+ for (i = 1; i < NGEOM; i++)
+ if (ftgtbl[i].g_fmtno == fmt && ftgtbl[i].g_lenno == len) break;
+ if (i == NGEOM) {
+ printf("ft%d: unknown tape geometry\n", ftu);
+ ftg = NULL;
+ return(-1);
+ }
+ ftg = &ftgtbl[i];
+ if (!ftg->g_trktape) {
+ printf("ft%d: unsupported format %s w/len %s\n",
+ ftu, ftg->g_fmtdesc, ftg->g_lendesc);
+ ftg = NULL;
+ return(-1);
+ }
+ DPRT(("Tape format is %s, length is %s\n", ftg->g_fmtdesc, ftg->g_lendesc));
+ ft->newcart = 0;
+ return(0);
+}
+
+/*
+ * Switch between tape/floppy. This will send the tape enable/disable
+ * codes for this drive's manufacturer.
+ */
+int set_fdcmode(dev_t dev, int newmode)
+{
+ ftu_t ftu = FDUNIT(minor(dev));
+ ft_p ft = &ft_data[ftu];
+ fdc_p fdc = ft->fdc;
+
+ static int havebufs = 0;
+ void *buf;
+ int r, s, i;
+ SegReq *sp;
+
+ if (newmode == FDC_TAPE_MODE) {
+ /* Wake up the tape drive */
+ switch (ft->type) {
+ case NO_TYPE:
+ fdc->flags &= ~FDC_TAPE_BUSY;
+ return(ENXIO);
+ case FT_COLORADO:
+ tape_start(ftu);
+ if (tape_cmd(ftu, QC_COL_ENABLE1)) {
+ tape_end(ftu);
+ return(EIO);
+ }
+ if (tape_cmd(ftu, QC_COL_ENABLE2)) {
+ tape_end(ftu);
+ return(EIO);
+ }
+ break;
+ case FT_MOUNTAIN:
+ tape_start(ftu);
+ if (tape_cmd(ftu, QC_MTN_ENABLE1)) {
+ tape_end(ftu);
+ return(EIO);
+ }
+ if (tape_cmd(ftu, QC_MTN_ENABLE2)) {
+ tape_end(ftu);
+ return(EIO);
+ }
+ break;
+ default:
+ DPRT(("ft%d: bad tape type\n", ftu));
+ return(ENXIO);
+ }
+ if (tape_status(ftu) < 0) {
+ tape_cmd(ftu, (ft->type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE);
+ tape_end(ftu);
+ return(EIO);
+ }
+
+ /* Grab buffers from memory. */
+ if (!havebufs) {
+ ft->curseg = malloc(sizeof(SegReq), M_DEVBUF, M_NOWAIT);
+ if (ft->curseg == NULL) {
+ printf("ft%d: not enough memory for buffers\n", ftu);
+ return(ENOMEM);
+ }
+ ft->bufseg = malloc(sizeof(SegReq), M_DEVBUF, M_NOWAIT);
+ if (ft->bufseg == NULL) {
+ free(ft->curseg, M_DEVBUF);
+ printf("ft%d: not enough memory for buffers\n", ftu);
+ return(ENOMEM);
+ }
+ havebufs = 1;
+ }
+ ft->curseg->reqtype = FTIO_READY;
+ ft->bufseg->reqtype = FTIO_READY;
+ ft->io_sts = FTIO_READY; /* tape drive is ready */
+ ft->active = 0; /* interrupt driver not active */
+ ft->moving = 0; /* tape not moving */
+ ft->rdonly = 0; /* tape read only */
+ ft->newcart = 0; /* a new cart was inserted */
+ ft->lastpos = -1; /* tape is rewound */
+ tape_state(ftu, 0, QS_READY, 60);
+ tape_cmd(ftu, QC_RATE);
+ tape_cmd(ftu, QCF_RT500+2); /* 500K bps */
+ tape_state(ftu, 0, QS_READY, 60);
+ ft->mode = FTM_PRIMARY;
+ tape_cmd(ftu, QC_PRIMARY); /* Make sure we're in primary mode */
+ tape_state(ftu, 0, QS_READY, 60);
+ ftg = NULL; /* No geometry yet */
+ ftgetgeom(ftu); /* Get tape geometry */
+ ftreq_rewind(ftu); /* Make sure tape is rewound */
+ } else {
+ tape_cmd(ftu, (ft->type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE);
+ tape_end(ftu);
+ ft->newcart = 0; /* clear new cartridge */
+ havebufs = 0;
+ free(ft->curseg, M_DEVBUF);
+ free(ft->bufseg, M_DEVBUF);
+ }
+ return(0);
+}
+
+
+/*
+ * Perform a QIC status function.
+ */
+int qic_status(ftu_t ftu, int cmd, int nbits)
+{
+ int st3, val, r, i;
+ ft_p ft = &ft_data[ftu];
+ fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
+
+ if (tape_cmd(ftu, cmd)) {
+ DPRT(("ft%d: QIC status timeout\n", ftu));
+ return(-1);
+ }
+
+ /* Sense drive status */
+ out_fdc(fdcu, NE7CMD_SENSED);
+ out_fdc(fdcu, 0x00);
+ st3 = in_fdc(fdcu);
+
+ if ((st3 & 0x10) == 0) { /* track 0 */
+ DPRT(("qic_status has dead drive... st3 = $%02x\n", st3));
+ return(-1);
+ }
+
+ for (i = r = 0; i <= nbits; i++) {
+ if (tape_cmd(ftu, QC_NEXTBIT)) {
+ DPRT(("ft%d: QIC status bit timed out on %d\n", ftu, i));
+ return(-1);
+ }
+
+ out_fdc(fdcu, NE7CMD_SENSED);
+ out_fdc(fdcu, 0x00);
+ st3 = in_fdc(fdcu);
+ if (st3 < 0) {
+ DPRT(("ft%d: controller timed out on bit %d r=$%02x\n",
+ ftu, i, r));
+ return(-1);
+ }
+
+ r >>= 1;
+ if (i < nbits)
+ r |= ((st3 & 0x10) ? 1 : 0) << nbits;
+ else if ((st3 & 0x10) == 0) {
+ DPRT(("ft%d: qic status stop bit missing at %d, st3=$%02x r=$%04x\n",
+ ftu,i,st3,r));
+ return(-1);
+ }
+ }
+
+ DPRT(("qic_status returned $%02x\n", r));
+ return(r);
+}
+
+/*
+ * Open tape drive for use. Bounced off of Fdopen if tape minor is
+ * detected.
+ */
+int ftopen(dev_t dev, int arg2) {
+ ftu_t ftu = FDUNIT(minor(dev));
+ int type = FDTYPE(minor(dev));
+ fdc_p fdc;
+
+ /* check bounds */
+ if (ftu >= NFT)
+ return(ENXIO);
+ fdc = ft_data[ftu].fdc;
+ /* check for controller already busy with tape */
+ if (fdc->flags & FDC_TAPE_BUSY)
+ return(EBUSY);
+ /* make sure we found a tape when probed */
+ if (!(fdc->flags & FDC_HASFTAPE))
+ return(ENODEV);
+ fdc->fdu = ftu;
+ fdc->flags |= FDC_TAPE_BUSY;
+ return(set_fdcmode(dev, FDC_TAPE_MODE)); /* try to switch to tape */
+}
+
+/*
+ * Close tape and return floppy controller to disk mode.
+ */
+int ftclose(dev_t dev, int flags)
+{
+ int s;
+ SegReq *sp;
+ ftu_t ftu = FDUNIT(minor(dev));
+ ft_p ft = &ft_data[ftu];
+
+ /* Wait for any remaining I/O activity to complete. */
+ if (ft->curseg->reqtype == FTIO_RDAHEAD) ft->curseg->reqcan = 1;
+ while (ft->active)
+ tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftclose", 0);
+
+ ft->mode = FTM_PRIMARY;
+ tape_cmd(ftu, QC_PRIMARY);
+ tape_state(ftu, 0, QS_READY, 60);
+ ftreq_rewind(ftu);
+ return(set_fdcmode(dev, FDC_DISK_MODE)); /* Otherwise, close tape */
+}
+
+/*
+ * Perform strategy on a given buffer (not!). The driver was not
+ * performing very efficiently using the buffering routines. After
+ * support for error correction was added, this routine became
+ * obsolete in favor of doing ioctl's. Ugly, yes.
+ */
+void ftstrategy(struct buf *bp)
+{
+ return;
+}
+
+/* Read or write a segment. */
+int ftreq_rw(ftu_t ftu, int cmd, QIC_Segment *sr, struct proc *p)
+{
+ int r, i, j;
+ SegReq *sp;
+ int s;
+ long blk, bad;
+ unsigned char *cp, *cp2;
+ ft_p ft = &ft_data[ftu];
+
+ if (!ft->active) {
+ r = tape_status(ftu);
+ if ((r & QS_CART) == 0) {
+ return(ENXIO); /* No cartridge */
+ }
+ if ((r & QS_FMTOK) == 0) {
+ return(ENXIO); /* Not formatted */
+ }
+ tape_state(ftu, 0, QS_READY, 90);
+ }
+
+ if (ftg == NULL || ft->newcart) {
+ while (ft->active)
+ tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftrw", 0);
+ tape_state(ftu, 0, QS_READY, 90);
+ if (ftgetgeom(ftu) < 0) {
+ return(ENXIO);
+ }
+ }
+
+ /* Write not allowed on a read-only tape. */
+ if (cmd == QIOWRITE && ft->rdonly) {
+ return(EROFS);
+ }
+ /* Quick check of request and buffer. */
+ if (sr == NULL || sr->sg_data == NULL) {
+ return(EINVAL);
+ }
+ if (sr->sg_trk >= ftg->g_trktape ||
+ sr->sg_seg >= ftg->g_segtrk) {
+ return(EINVAL);
+ }
+ blk = sr->sg_trk * ftg->g_blktrk + sr->sg_seg * QCV_BLKSEG;
+
+ s = splbio();
+ if (cmd == QIOREAD) {
+ if (ft->curseg->reqtype == FTIO_RDAHEAD) {
+ if (blk == ft->curseg->reqblk) {
+ sp = ft->curseg;
+ sp->reqtype = FTIO_READING;
+ sp->reqbad = sr->sg_badmap;
+ goto rdwait;
+ } else
+ ft->curseg->reqcan = 1; /* XXX cancel rdahead */
+ }
+
+ /* Wait until we're ready. */
+ while (ft->active)
+ tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftrw", 0);
+
+ /* Set up a new read request. */
+ sp = ft->curseg;
+ sp->reqcrc = 0;
+ sp->reqbad = sr->sg_badmap;
+ sp->reqblk = blk;
+ sp->reqcan = 0;
+ sp->reqtype = FTIO_READING;
+
+ /* Start the read request off. */
+ DPRT(("Starting read I/O chain\n"));
+ arq_state = ard_state = awr_state = 0;
+ ft->xblk = sp->reqblk;
+ ft->xcnt = 0;
+ ft->xptr = sp->buff;
+ ft->active = 1;
+ timeout(ft_timeout, (caddr_t)ftu, 1);
+
+rdwait:
+ tsleep((caddr_t)&ftsem.buff_avail, FTPRI, "ftrw", 0);
+ bad = sp->reqbad;
+ sr->sg_crcmap = sp->reqcrc & ~bad;
+
+ /* Copy out segment and discard bad mapped blocks. */
+ cp = sp->buff; cp2 = sr->sg_data;
+ for (i = 0; i < QCV_BLKSEG; cp += QCV_BLKSIZE, i++) {
+ if (bad & (1 << i)) continue;
+ copyout(cp, cp2, QCV_BLKSIZE);
+ cp2 += QCV_BLKSIZE;
+ }
+ } else {
+ if (ft->curseg->reqtype == FTIO_RDAHEAD) {
+ ft->curseg->reqcan = 1; /* XXX cancel rdahead */
+ while (ft->active)
+ tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftrw", 0);
+ }
+
+ /* Sleep until a buffer becomes available. */
+ while (ft->bufseg->reqtype != FTIO_READY)
+ tsleep((caddr_t)&ftsem.buff_avail, FTPRI, "ftrwbuf", 0);
+ sp = (ft->curseg->reqtype == FTIO_READY) ? ft->curseg : ft->bufseg;
+
+ /* Copy in segment and expand bad blocks. */
+ bad = sr->sg_badmap;
+ cp = sr->sg_data; cp2 = sp->buff;
+ for (i = 0; i < QCV_BLKSEG; cp2 += QCV_BLKSIZE, i++) {
+ if (bad & (1 << i)) continue;
+ copyin(cp, cp2, QCV_BLKSIZE);
+ cp += QCV_BLKSIZE;
+ }
+
+ sp->reqblk = blk;
+ sp->reqcan = 0;
+ sp->reqtype = FTIO_WRITING;
+
+ if (!ft->active) {
+ DPRT(("Starting write I/O chain\n"));
+ arq_state = ard_state = awr_state = 0;
+ ft->xblk = sp->reqblk;
+ ft->xcnt = 0;
+ ft->xptr = sp->buff;
+ ft->active = 1;
+ timeout(ft_timeout, (caddr_t)ftu, 1);
+ }
+ }
+ splx(s);
+ return(0);
+}
+
+
+/* Rewind to beginning of tape */
+int ftreq_rewind(ftu_t ftu)
+{
+ ft_p ft = &ft_data[ftu];
+
+ tape_inactive(ftu);
+ tape_cmd(ftu, QC_STOP);
+ tape_state(ftu, 0, QS_READY, 90);
+ tape_cmd(ftu, QC_SEEKSTART);
+ tape_state(ftu, 0, QS_READY, 90);
+ tape_cmd(ftu, QC_SEEKTRACK);
+ tape_cmd(ftu, 2);
+ tape_state(ftu, 0, QS_READY, 90);
+ ft->lastpos = -1;
+ ft->moving = 0;
+ return(0);
+}
+
+/* Move to logical beginning or end of track */
+int ftreq_trkpos(ftu_t ftu, int req)
+{
+ int curtrk, r, cmd;
+ ft_p ft = &ft_data[ftu];
+
+ tape_inactive(ftu);
+ tape_cmd(ftu, QC_STOP);
+ tape_state(ftu, 0, QS_READY, 90);
+
+ r = tape_status(ftu);
+ if ((r & QS_CART) == 0) return(ENXIO); /* No cartridge */
+ if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */
+
+ if (ftg == NULL || ft->newcart) {
+ if (ftgetgeom(ftu) < 0) return(ENXIO);
+ }
+
+ curtrk = (ft->lastpos < 0) ? 0 : ft->lastpos / ftg->g_blktrk;
+ if (req == QIOBOT)
+ cmd = (curtrk & 1) ? QC_SEEKEND : QC_SEEKSTART;
+ else
+ cmd = (curtrk & 1) ? QC_SEEKSTART : QC_SEEKEND;
+ tape_cmd(ftu, cmd);
+ tape_state(ftu, 0, QS_READY, 90);
+ return(0);
+}
+
+/* Seek tape head to a particular track. */
+int ftreq_trkset(ftu_t ftu, int *trk)
+{
+ int curtrk, r, cmd;
+ ft_p ft = &ft_data[ftu];
+
+ tape_inactive(ftu);
+ tape_cmd(ftu, QC_STOP);
+ tape_state(ftu, 0, QS_READY, 90);
+
+ r = tape_status(ftu);
+ if ((r & QS_CART) == 0) return(ENXIO); /* No cartridge */
+ if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */
+ if (ftg == NULL || ft->newcart) {
+ if (ftgetgeom(ftu) < 0) return(ENXIO);
+ }
+
+ tape_cmd(ftu, QC_SEEKTRACK);
+ tape_cmd(ftu, *trk + 2);
+ tape_state(ftu, 0, QS_READY, 90);
+ return(0);
+}
+
+/* Start tape moving forward. */
+int ftreq_lfwd(ftu_t ftu)
+{
+ tape_inactive(ftu);
+ tape_cmd(ftu, QC_STOP);
+ tape_state(ftu, 0, QS_READY, 90);
+ tape_cmd(ftu, QC_FORWARD);
+ return(0);
+}
+
+/* Stop the tape */
+int ftreq_stop(ftu_t ftu)
+{
+ tape_inactive(ftu);
+ tape_cmd(ftu, QC_STOP);
+ tape_state(ftu, 0, QS_READY, 90);
+ return(0);
+}
+
+/* Set the particular mode the drive should be in. */
+int ftreq_setmode(ftu_t ftu, int cmd)
+{
+ int r;
+ ft_p ft = &ft_data[ftu];
+
+ tape_inactive(ftu);
+ r = tape_status(ftu);
+
+ switch(cmd) {
+ case QIOPRIMARY:
+ ft->mode = FTM_PRIMARY;
+ tape_cmd(ftu, QC_PRIMARY);
+ break;
+ case QIOFORMAT:
+ if (r & QS_RDONLY) return(ENXIO);
+ if ((r & QS_BOT) == 0) return(ENXIO);
+ tape_cmd(ftu, QC_FORMAT);
+ break;
+ case QIOVERIFY:
+ if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */
+ tape_cmd(ftu, QC_VERIFY);
+ break;
+ }
+ tape_state(ftu, 0, QS_READY, 60);
+ return(0);
+}
+
+/* Return drive status bits */
+int ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p)
+{
+ ft_p ft = &ft_data[ftu];
+
+ if (ft->active)
+ *sts = ft->laststs & ~QS_READY;
+ else
+ *sts = tape_status(ftu);
+ return(0);
+}
+
+/* Return drive configuration bits */
+int ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p)
+{
+ int r, tries;
+ ft_p ft = &ft_data[ftu];
+
+ if (ft->active)
+ r = ft->lastcfg;
+ else {
+ for (r = -1, tries = 0; r < 0 && tries < 3; tries++)
+ r = qic_status(ftu, QC_CONFIG, 8);
+ if (tries == 3) return(ENXIO);
+ }
+ *cfg = r;
+ return(0);
+}
+
+/* Return current tape's geometry. */
+int ftreq_geom(ftu_t ftu, QIC_Geom *g)
+{
+ tape_inactive(ftu);
+ if (ftg == NULL && ftgetgeom(ftu) < 0) return(ENXIO);
+ bcopy(ftg, g, sizeof(QIC_Geom));
+ return(0);
+}
+
+/* Return drive hardware information */
+int ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp)
+{
+ int r, tries;
+ int rom, vend;
+
+ tape_inactive(ftu);
+ bzero(hwp, sizeof(QIC_HWInfo));
+
+ for (rom = -1, tries = 0; rom < 0 && tries < 3; tries++)
+ rom = qic_status(ftu, QC_VERSION, 8);
+ if (rom > 0) {
+ hwp->hw_rombeta = (rom >> 7) & 0x01;
+ hwp->hw_romid = rom & 0x7f;
+ }
+
+ for (vend = -1, tries = 0; vend < 0 && tries < 3; tries++)
+ vend = qic_status(ftu, QC_VENDORID, 16);
+ if (vend > 0) {
+ hwp->hw_make = (vend >> 6) & 0x3ff;
+ hwp->hw_model = vend & 0x3f;
+ }
+
+ return(0);
+}
+
+/*
+ * I/O functions.
+ */
+int ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
+{
+ ftu_t ftu = FDUNIT(minor(dev));
+ ft_p ft = &ft_data[ftu];
+
+ switch(cmd) {
+ case QIOREAD: /* Request reading a segment from tape. */
+ case QIOWRITE: /* Request writing a segment to tape. */
+ return(ftreq_rw(ftu, cmd, (QIC_Segment *)data, p));
+
+ case QIOREWIND: /* Rewind tape. */
+ return(ftreq_rewind(ftu));
+
+ case QIOBOT: /* Seek to logical beginning of track. */
+ case QIOEOT: /* Seek to logical end of track. */
+ return(ftreq_trkpos(ftu, cmd));
+
+ case QIOTRACK: /* Seek tape head to specified track. */
+ return(ftreq_trkset(ftu, (int *)data));
+
+ case QIOSEEKLP: /* Seek load point. */
+ goto badreq;
+
+ case QIOFORWARD: /* Move tape in logical forward direction. */
+ return(ftreq_lfwd(ftu));
+
+ case QIOSTOP: /* Causes tape to stop. */
+ return(ftreq_stop(ftu));
+
+ case QIOPRIMARY: /* Enter primary mode. */
+ case QIOFORMAT: /* Enter format mode. */
+ case QIOVERIFY: /* Enter verify mode. */
+ return(ftreq_setmode(ftu, cmd));
+
+ case QIOWRREF: /* Write reference burst. */
+ goto badreq;
+
+ case QIOSTATUS: /* Get drive status. */
+ return(ftreq_status(ftu, cmd, (int *)data, p));
+
+ case QIOCONFIG: /* Get tape configuration. */
+ return(ftreq_config(ftu, cmd, (int *)data, p));
+
+ case QIOGEOM:
+ return(ftreq_geom(ftu, (QIC_Geom *)data));
+
+ case QIOHWINFO:
+ return(ftreq_hwinfo(ftu, (QIC_HWInfo *)data));
+ }
+badreq:
+ DPRT(("ft%d: unknown ioctl(%d) request\n", ftu, cmd));
+ return(ENXIO);
+}
+
+/* Not implemented */
+int ftdump(dev_t dev)
+{
+ return(EINVAL);
+}
+
+/* Not implemented */
+int ftsize(dev_t dev)
+{
+ return(EINVAL);
+}
+#endif
diff --git a/sys/i386/isa/ftreg.h b/sys/i386/isa/ftreg.h
new file mode 100644
index 0000000..7b4ca6a
--- /dev/null
+++ b/sys/i386/isa/ftreg.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 1993 Steve Gerakines
+ *
+ * This is freely redistributable software. You may do anything you
+ * wish with it, so long as the above notice stays intact.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ftreg.h - QIC-40/80 floppy tape driver header
+ * 10/30/93 v0.3
+ * More things will end up here. QC_VENDORID and QC_VERSION now used.
+ *
+ * 08/07/93 v0.2 release
+ * Things that should've been here in the first place were moved.
+ * Tape geometry and segment request types were added.
+ *
+ * 06/03/93 v0.1 Alpha release
+ * Initial revision. Many more things should be moved here.
+ */
+
+/* QIC-117 command set. */
+#define QC_RESET 1 /* reset */
+#define QC_NEXTBIT 2 /* report next bit */
+#define QC_PAUSE 3 /* pause */
+#define QC_STPAUSE 4 /* step pause */
+#define QC_TIMEOUT 5 /* alt timeout */
+#define QC_STATUS 6 /* report status */
+#define QC_ERRCODE 7 /* report error code */
+#define QC_CONFIG 8 /* report config */
+#define QC_VERSION 9 /* report version */
+#define QC_FORWARD 10 /* logical forward */
+#define QC_SEEKSTART 11 /* seek to track start */
+#define QC_SEEKEND 12 /* seek to track end */
+#define QC_SEEKTRACK 13 /* seek head to track */
+#define QC_SEEKLOAD 14 /* seek load point */
+#define QC_FORMAT 15 /* format mode */
+#define QC_WRITEREF 16 /* write reference */
+#define QC_VERIFY 17 /* verify mode */
+#define QC_STOP 18 /* stop tape */
+#define QC_STEPUP 21 /* step head up */
+#define QC_STEPDOWN 22 /* step head down */
+#define QC_SEEKREV 25 /* seek reverse */
+#define QC_SEEKFWD 26 /* seek forward */
+#define QC_RATE 27 /* select data rate */
+#define QC_DIAG1 28 /* diagnostic mode 1 */
+#define QC_DIAG2 29 /* diagnostic mode 2 */
+#define QC_PRIMARY 30 /* primary mode */
+#define QC_VENDORID 32 /* vendor id */
+#define QC_TSTATUS 33 /* report tape status */
+#define QC_EXTREV 34 /* extended skip reverse */
+#define QC_EXTFWD 35 /* extended skip forward */
+
+/* Colorado enable/disable. */
+#define QC_COL_ENABLE1 46 /* enable */
+#define QC_COL_ENABLE2 2 /* null-op */
+#define QC_COL_DISABLE 47 /* disable */
+
+/* Mountain enable/disable. */
+#define QC_MTN_ENABLE1 23 /* enable 1 */
+#define QC_MTN_ENABLE2 20 /* enable 2 */
+#define QC_MTN_DISABLE 24 /* disable */
+
+/* Segment I/O request. */
+typedef struct segq {
+ unsigned char buff[QCV_SEGSIZE];/* Segment data; first for alignment */
+ int reqtype; /* Request type */
+ long reqcrc; /* CRC Errors found */
+ long reqbad; /* Bad sector map */
+ long reqblk; /* Block request starts at */
+ int reqcan; /* Cancel read-ahead */
+} SegReq;
diff --git a/sys/i386/isa/ic/i8042.h b/sys/i386/isa/ic/i8042.h
new file mode 100644
index 0000000..84ee90f
--- /dev/null
+++ b/sys/i386/isa/ic/i8042.h
@@ -0,0 +1,27 @@
+/*
+ * $Id$
+ */
+
+#define KBSTATP 0x64 /* kbd controller status port (I) */
+#define KBS_DIB 0x01 /* kbd data in buffer */
+#define KBS_IBF 0x02 /* kbd input buffer low */
+#define KBS_WARM 0x04 /* kbd input buffer low */
+#define KBS_OCMD 0x08 /* kbd output buffer has command */
+#define KBS_NOSEC 0x10 /* kbd security lock not engaged */
+#define KBS_TERR 0x20 /* kbd transmission error */
+#define KBS_RERR 0x40 /* kbd receive error */
+#define KBS_PERR 0x80 /* kbd parity error */
+
+#define KBCMDP 0x64 /* kbd controller port (O) */
+#define KBDATAP 0x60 /* kbd data port (I) */
+#define KBOUTP 0x60 /* kbd data port (O) */
+
+#define K_LDCMDBYTE 0x60
+
+#define KC8_TRANS 0x40 /* convert to old scan codes */
+#define KC8_OLDPC 0x20 /* old 9bit codes instead of new 11bit */
+#define KC8_DISABLE 0x10 /* disable keyboard */
+#define KC8_IGNSEC 0x08 /* ignore security lock */
+#define KC8_CPU 0x04 /* exit from protected mode reset */
+#define KC8_IEN 0x01 /* enable interrupt */
+#define CMDBYTE (KC8_TRANS|KC8_IGNSEC|KC8_CPU|KC8_IEN)
diff --git a/sys/i386/isa/ic/i82365.h b/sys/i386/isa/ic/i82365.h
new file mode 100644
index 0000000..ab38125
--- /dev/null
+++ b/sys/i386/isa/ic/i82365.h
@@ -0,0 +1,190 @@
+#ifndef __83265_H__
+#define __83265_H__
+
+/***********************************************************************
+ * 82365.h -- information necessary for direct manipulation of PCMCIA
+ * cards and controllers
+ *
+ * Support is included for Intel 82365SL PCIC controllers and clones
+ * thereof.
+ *
+ * originally by Barry Jaspan; hacked over by Keith Moore
+ *
+ ***********************************************************************/
+
+/*
+ * PCIC Registers
+ * Each register is given a name, and most of the bits are named too.
+ * I should really name them all.
+ *
+ * Finally, since the banks can be addressed with a regular syntax,
+ * some macros are provided for that purpose.
+ */
+
+#define PCIC_BASE 0x03e0 /* base adddress of pcic register set */
+
+/* First, all the registers */
+#define PCIC_ID_REV 0x00 /* Identification and Revision */
+#define PCIC_STATUS 0x01 /* Interface Status */
+#define PCIC_POWER 0x02 /* Power and RESETDRV control */
+#define PCIC_INT_GEN 0x03 /* Interrupt and General Control */
+#define PCIC_STAT_CHG 0x04 /* Card Status Change */
+#define PCIC_STAT_INT 0x05 /* Card Status Change Interrupt Config */
+#define PCIC_ADDRWINE 0x06 /* Address Window Enable */
+#define PCIC_IOCTL 0x07 /* I/O Control */
+#define PCIC_IO0_STL 0x08 /* I/O Address 0 Start Low Byte */
+#define PCIC_IO0_STH 0x09 /* I/O Address 0 Start High Byte */
+#define PCIC_IO0_SPL 0x0a /* I/O Address 0 Stop Low Byte */
+#define PCIC_IO0_SPH 0x0b /* I/O Address 0 Stop High Byte */
+#define PCIC_IO1_STL 0x0c /* I/O Address 1 Start Low Byte */
+#define PCIC_IO1_STH 0x0d /* I/O Address 1 Start High Byte */
+#define PCIC_IO1_SPL 0x0e /* I/O Address 1 Stop Low Byte */
+#define PCIC_IO1_SPH 0x0f /* I/O Address 1 Stop High Byte */
+#define PCIC_SM0_STL 0x10 /* System Memory Address 0 Mapping Start Low Byte */
+#define PCIC_SM0_STH 0x11 /* System Memory Address 0 Mapping Start High Byte */
+#define PCIC_SM0_SPL 0x12 /* System Memory Address 0 Mapping Stop Low Byte */
+#define PCIC_SM0_SPH 0x13 /* System Memory Address 0 Mapping Stop High Byte */
+#define PCIC_CM0_L 0x14 /* Card Memory Offset Address 0 Low Byte */
+#define PCIC_CM0_H 0x15 /* Card Memory Offset Address 0 High Byte */
+#define PCIC_CDGC 0x16 /* Card Detect and General Control */
+#define PCIC_RES17 0x17 /* Reserved */
+#define PCIC_SM1_STL 0x18 /* System Memory Address 1 Mapping Start Low Byte */
+#define PCIC_SM1_STH 0x19 /* System Memory Address 1 Mapping Start High Byte */
+#define PCIC_SM1_SPL 0x1a /* System Memory Address 1 Mapping Stop Low Byte */
+#define PCIC_SM1_SPH 0x1b /* System Memory Address 1 Mapping Stop High Byte */
+#define PCIC_CM1_L 0x1c /* Card Memory Offset Address 1 Low Byte */
+#define PCIC_CM1_H 0x1d /* Card Memory Offset Address 1 High Byte */
+#define PCIC_GLO_CTRL 0x1e /* Global Control Register */
+#define PCIC_RES1F 0x1f /* Reserved */
+#define PCIC_SM2_STL 0x20 /* System Memory Address 2 Mapping Start Low Byte */
+#define PCIC_SM2_STH 0x21 /* System Memory Address 2 Mapping Start High Byte */
+#define PCIC_SM2_SPL 0x22 /* System Memory Address 2 Mapping Stop Low Byte */
+#define PCIC_SM2_SPH 0x23 /* System Memory Address 2 Mapping Stop High Byte */
+#define PCIC_CM2_L 0x24 /* Card Memory Offset Address 2 Low Byte */
+#define PCIC_CM2_H 0x25 /* Card Memory Offset Address 2 High Byte */
+#define PCIC_RES26 0x26 /* Reserved */
+#define PCIC_RES27 0x27 /* Reserved */
+#define PCIC_SM3_STL 0x28 /* System Memory Address 3 Mapping Start Low Byte */
+#define PCIC_SM3_STH 0x29 /* System Memory Address 3 Mapping Start High Byte */
+#define PCIC_SM3_SPL 0x2a /* System Memory Address 3 Mapping Stop Low Byte */
+#define PCIC_SM3_SPH 0x2b /* System Memory Address 3 Mapping Stop High Byte */
+#define PCIC_CM3_L 0x2c /* Card Memory Offset Address 3 Low Byte */
+#define PCIC_CM3_H 0x2d /* Card Memory Offset Address 3 High Byte */
+#define PCIC_RES2E 0x2e /* Reserved */
+#define PCIC_RES2F 0x2f /* Reserved */
+#define PCIC_SM4_STL 0x30 /* System Memory Address 4 Mapping Start Low Byte */
+#define PCIC_SM4_STH 0x31 /* System Memory Address 4 Mapping Start High Byte */
+#define PCIC_SM4_SPL 0x32 /* System Memory Address 4 Mapping Stop Low Byte */
+#define PCIC_SM4_SPH 0x33 /* System Memory Address 4 Mapping Stop High Byte */
+#define PCIC_CM4_L 0x34 /* Card Memory Offset Address 4 Low Byte */
+#define PCIC_CM4_H 0x35 /* Card Memory Offset Address 4 High Byte */
+#define PCIC_RES36 0x36 /* Reserved */
+#define PCIC_RES37 0x37 /* Reserved */
+#define PCIC_RES38 0x38 /* Reserved */
+#define PCIC_RES39 0x39 /* Reserved */
+#define PCIC_RES3A 0x3a /* Reserved */
+#define PCIC_RES3B 0x3b /* Reserved */
+#define PCIC_RES3C 0x3c /* Reserved */
+#define PCIC_RES3D 0x3d /* Reserved */
+#define PCIC_RES3E 0x3e /* Reserved */
+#define PCIC_RES3F 0x3f /* Reserved */
+
+/* Now register bits, ordered by reg # */
+
+/* For Identification and Revision (PCIC_ID_REV) */
+#define PCIC_INTEL0 0x82 /* Intel 82365SL Rev. 0; Both Memory and I/O */
+#define PCIC_INTEL1 0x83 /* Intel 82365SL Rev. 1; Both Memory and I/O */
+#define PCIC_IBM1 0x88 /* IBM PCIC clone; Both Memory and I/O */
+#define PCIC_IBM2 0x89 /* IBM PCIC clone; Both Memory and I/O */
+
+/* For Interface Status register (PCIC_STATUS) */
+#define PCIC_VPPV 0x80 /* Vpp_valid */
+#define PCIC_POW 0x40 /* PC Card power active */
+#define PCIC_READY 0x20 /* Ready/~Busy */
+#define PCIC_MWP 0x10 /* Memory Write Protect */
+#define PCIC_CD 0x0C /* Both card detect bits */
+#define PCIC_BVD 0x03 /* Both Battery Voltage Detect bits */
+
+/* For the Power and RESETDRV register (PCIC_POWER) */
+#define PCIC_OUTENA 0x80 /* Output Enable */
+#define PCIC_DISRST 0x40 /* Disable RESETDRV */
+#define PCIC_APSENA 0x20 /* Auto Pwer Switch Enable */
+#define PCIC_PCPWRE 0x10 /* PC Card Power Enable */
+
+/* For the Interrupt and General Control register (PCIC_INT_GEN) */
+#define PCIC_CARDTYPE 0x20 /* Card Type 0 = memory, 1 = I/O */
+#define PCIC_IOCARD 0x20
+#define PCIC_MEMCARD 0x00
+#define PCIC_CARDRESET 0x40 /* Card reset 0 = Reset, 1 = Normal */
+
+/* For the Card Status Change register (PCIC_STAT_CHG) */
+#define PCIC_CDTCH 0x08 /* Card Detect Change */
+#define PCIC_RDYCH 0x04 /* Ready Change */
+#define PCIC_BATWRN 0x02 /* Battery Warning */
+#define PCIC_BATDED 0x01 /* Battery Dead */
+
+/* For the Address Window Enable Register (PCIC_ADDRWINE) */
+#define PCIC_SM0_EN 0x01 /* Memory Window 0 Enable */
+#define PCIC_SM1_EN 0x02 /* Memory Window 1 Enable */
+#define PCIC_SM2_EN 0x04 /* Memory Window 2 Enable */
+#define PCIC_SM3_EN 0x08 /* Memory Window 3 Enable */
+#define PCIC_SM4_EN 0x10 /* Memory Window 4 Enable */
+#define PCIC_MEMCS16 0x20 /* ~MEMCS16 Decode A23-A12 */
+#define PCIC_IO0_EN 0x40 /* I/O Window 0 Enable */
+#define PCIC_IO1_EN 0x80 /* I/O Window 1 Enable */
+
+/* For the I/O Control Register (PCIC_IOCTL) */
+#define PCIC_IO0_16BIT 0x01 /* I/O to this segment is 16 bit */
+#define PCIC_IO0_CS16 0x02 /* I/O cs16 source is the card */
+#define PCIC_IO0_0WS 0x04 /* zero wait states added on 8 bit cycles */
+#define PCIC_IO0_WS 0x08 /* Wait states added for 16 bit cycles */
+#define PCIC_IO1_16BIT 0x10 /* I/O to this segment is 16 bit */
+#define PCIC_IO1_CS16 0x20 /* I/O cs16 source is the card */
+#define PCIC_IO1_0WS 0x04 /* zero wait states added on 8 bit cycles */
+#define PCIC_IO1_WS 0x80 /* Wait states added for 16 bit cycles */
+
+/* For the various I/O and Memory windows */
+#define PCIC_ADDR_LOW 0
+#define PCIC_ADDR_HIGH 1
+#define PCIC_START 0x00 /* Start of mapping region */
+#define PCIC_END 0x02 /* End of mapping region */
+#define PCIC_MOFF 0x04 /* Card Memory Mapping region offset */
+#define PCIC_IO0 0x08 /* I/O Address 0 */
+#define PCIC_IO1 0x0c /* I/O Address 1 */
+#define PCIC_SM0 0x10 /* System Memory Address 0 Mapping */
+#define PCIC_SM1 0x18 /* System Memory Address 1 Mapping */
+#define PCIC_SM2 0x20 /* System Memory Address 2 Mapping */
+#define PCIC_SM3 0x28 /* System Memory Address 3 Mapping */
+#define PCIC_SM4 0x30 /* System Memory Address 4 Mapping */
+
+/* For System Memory Window start registers
+ (PCIC_SMx|PCIC_START|PCIC_ADDR_HIGH) */
+#define PCIC_ZEROWS 0x40 /* Zero wait states */
+#define PCIC_DATA16 0x80 /* Data width is 16 bits */
+
+/* For System Memory Window stop registers
+ (PCIC_SMx|PCIC_END|PCIC_ADDR_HIGH) */
+#define PCIC_MW0 0x40 /* Wait state bit 0 */
+#define PCIC_MW1 0x80 /* Wait state bit 1 */
+
+/* For System Memory Window offset registers
+ (PCIC_SMx|PCIC_MOFF|PCIC_ADDR_HIGH) */
+#define PCIC_REG 0x40 /* Attribute/Common select (why called Reg?) */
+#define PCIC_WP 0x80 /* Write-protect this window */
+
+/* For Card Detect and General Control register (PCIC_CDGC) */
+#define PCIC_16_DL_INH 0x01 /* 16-bit memory delay inhibit */
+#define PCIC_CNFG_RST_EN 0x02 /* configuration reset enable */
+#define PCIC_GPI_EN 0x04 /* GPI Enable */
+#define PCIC_GPI_TRANS 0x08 /* GPI Transition Control */
+#define PCIC_CDRES_EN 0x10 /* card detect resume enable */
+#define PCIC_SW_CD_INT 0x20 /* s/w card detect interrupt */
+
+/* For Global Control register (PCIC_GLO_CTRL) */
+#define PCIC_PWR_DOWN 0x01 /* power down */
+#define PCIC_LVL_MODE 0x02 /* level mode interrupt enable */
+#define PCIC_WB_CSCINT 0x04 /* explicit write-back csc intr */
+#define PCIC_IRQ14_PULSE 0x08 /* irq 14 pulse mode enable */
+
+/* DON'T ADD ANYTHING AFTER THIS #endif */
+#endif /* __83265_H__ */
diff --git a/sys/i386/isa/ic/i8237.h b/sys/i386/isa/ic/i8237.h
new file mode 100644
index 0000000..2199e73
--- /dev/null
+++ b/sys/i386/isa/ic/i8237.h
@@ -0,0 +1,11 @@
+/*
+ * Intel 8237 DMA Controller
+ *
+ * $Id$
+ */
+
+#define DMA37MD_SINGLE 0x40 /* single pass mode */
+#define DMA37MD_CASCADE 0xc0 /* cascade mode */
+#define DMA37MD_WRITE 0x04 /* read the device, write memory operation */
+#define DMA37MD_READ 0x08 /* write the device, read memory operation */
+
diff --git a/sys/i386/isa/ic/i82586.h b/sys/i386/isa/ic/i82586.h
new file mode 100644
index 0000000..577313d
--- /dev/null
+++ b/sys/i386/isa/ic/i82586.h
@@ -0,0 +1,325 @@
+/*-
+ * Copyright (c) 1992, University of Vermont and State Agricultural College.
+ * Copyright (c) 1992, Garrett A. Wollman.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * Vermont and State Agricultural College and Garrett A. Wollman.
+ * 4. Neither the name of the University nor the name of the author
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+/*
+ * Intel 82586 Ethernet chip
+ * Register, bit, and structure definitions.
+ *
+ * Written by GAW with reference to the Clarkson Packet Driver code for this
+ * chip written by Russ Nelson and others.
+ */
+
+struct ie_en_addr {
+ u_char data[6];
+};
+
+/*
+ * This is the master configuration block. It tells the hardware where all
+ * the rest of the stuff is.
+ */
+struct ie_sys_conf_ptr {
+ u_short mbz; /* must be zero */
+ u_char ie_bus_use; /* true if 8-bit only */
+ u_char mbz2[5]; /* must be zero */
+ caddr_t ie_iscp_ptr; /* 24-bit physaddr of ISCP */
+};
+
+/*
+ * Note that this is wired in hardware; the SCP is always located here, no
+ * matter what.
+ */
+#define IE_SCP_ADDR 0xfffff4
+
+/*
+ * The tells the hardware where all the rest of the stuff is, too.
+ * FIXME: some of these should be re-commented after we figure out their
+ * REAL function.
+ */
+struct ie_int_sys_conf_ptr {
+ u_char ie_busy; /* zeroed after init */
+ u_char mbz;
+ u_short ie_scb_offset; /* 16-bit physaddr of next struct */
+ caddr_t ie_base; /* 24-bit physaddr for all 16-bit vars */
+};
+
+/*
+ * This FINALLY tells the hardware what to do and where to put it.
+ */
+struct ie_sys_ctl_block {
+ u_short ie_status; /* status word */
+ u_short ie_command; /* command word */
+ u_short ie_command_list; /* 16-pointer to command block list */
+ u_short ie_recv_list; /* 16-pointer to receive frame list */
+ u_short ie_err_crc; /* CRC errors */
+ u_short ie_err_align; /* Alignment errors */
+ u_short ie_err_resource; /* Resource errors */
+ u_short ie_err_overrun; /* Overrun errors */
+};
+
+/* Command values */
+#define IE_RU_COMMAND 0x0070 /* mask for RU command */
+#define IE_RU_NOP 0 /* for completeness */
+#define IE_RU_START 0x0010 /* start receive unit command */
+#define IE_RU_ENABLE 0x0020 /* enable receiver command */
+#define IE_RU_DISABLE 0x0030 /* disable receiver command */
+#define IE_RU_ABORT 0x0040 /* abort current receive operation */
+
+#define IE_CU_COMMAND 0x0700 /* mask for CU command */
+#define IE_CU_NOP 0 /* included for completeness */
+#define IE_CU_START 0x0100 /* do-command command */
+#define IE_CU_RESUME 0x0200 /* resume a suspended cmd list */
+#define IE_CU_STOP 0x0300 /* SUSPEND was already taken */
+#define IE_CU_ABORT 0x0400 /* abort current command */
+
+#define IE_ACK_COMMAND 0xf000 /* mask for ACK command */
+#define IE_ACK_CX 0x8000 /* ack IE_ST_DONE */
+#define IE_ACK_FR 0x4000 /* ack IE_ST_RECV */
+#define IE_ACK_CNA 0x2000 /* ack IE_ST_ALLDONE */
+#define IE_ACK_RNR 0x1000 /* ack IE_ST_RNR */
+
+#define IE_ACTION_COMMAND(x) (((x) & IE_CU_COMMAND) == IE_CU_START)
+ /* is this command an action command? */
+
+/* Status values */
+#define IE_ST_WHENCE 0xf000 /* mask for cause of interrupt */
+#define IE_ST_DONE 0x8000 /* command with I bit completed */
+#define IE_ST_RECV 0x4000 /* frame received */
+#define IE_ST_ALLDONE 0x2000 /* all commands completed */
+#define IE_ST_RNR 0x1000 /* receive not ready */
+
+#define IE_CU_STATUS 0x700 /* mask for command unit status */
+#define IE_CU_ACTIVE 0x200 /* command unit is active */
+#define IE_CU_SUSPEND 0x100 /* command unit is suspended */
+
+#define IE_RU_STATUS 0x70 /* mask for receiver unit status */
+#define IE_RU_SUSPEND 0x10 /* receiver is suspended */
+#define IE_RU_NOSPACE 0x20 /* receiver has no resources */
+#define IE_RU_READY 0x40 /* reveiver is ready */
+
+/*
+ * This is filled in partially by the chip, partially by us.
+ */
+struct ie_recv_frame_desc {
+ u_short ie_fd_status; /* status for this frame */
+ u_short ie_fd_last; /* end of frame list flag */
+ u_short ie_fd_next; /* 16-pointer to next RFD */
+ u_short ie_fd_buf_desc; /* 16-pointer to list of buffer desc's */
+ struct ie_en_addr dest; /* destination ether */
+ struct ie_en_addr src; /* source ether */
+ u_short ie_length; /* 802 length/Ether type */
+ u_short mbz; /* must be zero */
+};
+
+#define IE_FD_LAST 0x8000 /* last rfd in list */
+#define IE_FD_SUSP 0x4000 /* suspend RU after receipt */
+
+#define IE_FD_COMPLETE 0x8000 /* frame is complete */
+#define IE_FD_BUSY 0x4000 /* frame is busy */
+#define IE_FD_OK 0x2000 /* frame is bad */
+#define IE_FD_RNR 0x0200 /* receiver out of resources here */
+
+/*
+ * linked list of buffers...
+ */
+struct ie_recv_buf_desc {
+ u_short ie_rbd_actual; /* status for this buffer */
+ u_short ie_rbd_next; /* 16-pointer to next RBD */
+ caddr_t ie_rbd_buffer; /* 24-pointer to buffer for this RBD */
+ u_short ie_rbd_length; /* length of the buffer */
+ u_short mbz; /* must be zero */
+};
+
+#define IE_RBD_LAST 0x8000 /* last buffer */
+#define IE_RBD_USED 0x4000 /* this buffer has data */
+/*
+ * All commands share this in common.
+ */
+struct ie_cmd_common {
+ u_short ie_cmd_status; /* status of this command */
+ u_short ie_cmd_cmd; /* command word */
+ u_short ie_cmd_link; /* link to next command */
+};
+
+#define IE_STAT_COMPL 0x8000 /* command is completed */
+#define IE_STAT_BUSY 0x4000 /* command is running now */
+#define IE_STAT_OK 0x2000 /* command completed successfully */
+
+#define IE_CMD_NOP 0x0000 /* NOP */
+#define IE_CMD_IASETUP 0x0001 /* initial address setup */
+#define IE_CMD_CONFIG 0x0002 /* configure command */
+#define IE_CMD_MCAST 0x0003 /* multicast setup command */
+#define IE_CMD_XMIT 0x0004 /* transmit command */
+#define IE_CMD_TDR 0x0005 /* time-domain reflectometer command */
+#define IE_CMD_DUMP 0x0006 /* dump command */
+#define IE_CMD_DIAGNOSE 0x0007 /* diagnostics command */
+
+#define IE_CMD_LAST 0x8000 /* this is the last command in the list */
+#define IE_CMD_SUSPEND 0x4000 /* suspend CU after this command */
+#define IE_CMD_INTR 0x2000 /* post an interrupt after completion */
+
+/*
+ * This is the command to transmit a frame.
+ */
+struct ie_xmit_cmd {
+ struct ie_cmd_common com; /* common part */
+#define ie_xmit_status com.ie_cmd_status
+
+ u_short ie_xmit_desc; /* 16-pointer to buffer descriptor */
+ struct ie_en_addr ie_xmit_addr; /* destination address */
+
+ u_short ie_xmit_length; /* 802.3 length/Ether type field */
+};
+
+#define IE_XS_MAXCOLL 0x000f /* number of collisions during transmit */
+#define IE_XS_EXCMAX 0x0020 /* exceeded maximum number of collisions */
+#define IE_XS_SQE 0x0040 /* SQE positive */
+#define IE_XS_DEFERRED 0x0080 /* transmission deferred */
+#define IE_XS_UNDERRUN 0x0100 /* DMA underrun */
+#define IE_XS_LOSTCTS 0x0200 /* Lost CTS */
+#define IE_XS_NOCARRIER 0x0400 /* No Carrier */
+#define IE_XS_LATECOLL 0x0800 /* Late collision */
+
+/*
+ * This is a buffer descriptor for a frame to be transmitted.
+ */
+
+struct ie_xmit_buf {
+ u_short ie_xmit_flags; /* see below */
+ u_short ie_xmit_next; /* 16-pointer to next desc. */
+ caddr_t ie_xmit_buf; /* 24-pointer to the actual buffer */
+};
+
+#define IE_XMIT_LAST 0x8000 /* this TBD is the last one */
+/* The rest of the `flags' word is actually the length. */
+
+/*
+ * Multicast setup command.
+ */
+
+#define MAXMCAST 50 /* must fit in transmit buffer */
+
+struct ie_mcast_cmd {
+ struct ie_cmd_common com; /* common part */
+#define ie_mcast_status com.ie_cmd_status
+
+ u_short ie_mcast_bytes; /* size (in bytes) of multicast addresses */
+ struct ie_en_addr ie_mcast_addrs[MAXMCAST + 1]; /* space for them */
+};
+
+/*
+ * Time Domain Reflectometer command.
+ */
+
+struct ie_tdr_cmd {
+ struct ie_cmd_common com; /* common part */
+#define ie_tdr_status com.ie_cmd_status
+
+ u_short ie_tdr_time; /* error bits and time */
+};
+
+#define IE_TDR_SUCCESS 0x8000 /* TDR succeeded without error */
+#define IE_TDR_XCVR 0x4000 /* detected a transceiver problem */
+#define IE_TDR_OPEN 0x2000 /* detected an open */
+#define IE_TDR_SHORT 0x1000 /* TDR detected a short */
+#define IE_TDR_TIME 0x07ff /* mask for reflection time */
+
+/*
+ * Initial Address Setup command
+ */
+struct ie_iasetup_cmd {
+ struct ie_cmd_common com;
+#define ie_iasetup_status com.ie_cmd_status
+
+ struct ie_en_addr ie_address;
+};
+
+/*
+ * Configuration command
+ */
+struct ie_config_cmd {
+ struct ie_cmd_common com; /* common part */
+#define ie_config_status com.ie_cmd_status
+
+ u_char ie_config_count; /* byte count (0x0c) */
+ u_char ie_fifo; /* fifo (8) */
+ u_char ie_save_bad; /* save bad frames (0x40) */
+ u_char ie_addr_len; /* address length (0x2e) (AL-LOC == 1) */
+ u_char ie_priority; /* priority and backoff (0x0) */
+ u_char ie_ifs; /* inter-frame spacing (0x60) */
+ u_char ie_slot_low; /* slot time, LSB (0x0) */
+ u_char ie_slot_high; /* slot time, MSN, and retries (0xf2) */
+ u_char ie_promisc; /* 1 if promiscuous, else 0 */
+ u_char ie_crs_cdt; /* CSMA/CD parameters (0x0) */
+ u_char ie_min_len; /* min frame length (0x40) */
+ u_char ie_junk; /* stuff for 82596 (0xff) */
+};
+
+/*
+ * Here are a few useful functions. We could have done these as macros,
+ * but since we have the inline facility, it makes sense to use that
+ * instead.
+ */
+inline void
+ie_setup_config(volatile struct ie_config_cmd *cmd,
+ int promiscuous, int manchester) {
+ cmd->ie_config_count = 0x0c;
+ cmd->ie_fifo = 8;
+ cmd->ie_save_bad = 0x40;
+ cmd->ie_addr_len = 0x2e;
+ cmd->ie_priority = 0;
+ cmd->ie_ifs = 0x60;
+ cmd->ie_slot_low = 0;
+ cmd->ie_slot_high = 0xf2;
+ cmd->ie_promisc = !!promiscuous | manchester << 2;
+ cmd->ie_crs_cdt = 0;
+ cmd->ie_min_len = 64;
+ cmd->ie_junk = 0xff;
+}
+
+inline caddr_t
+Align(caddr_t ptr) {
+ unsigned long l = (unsigned long)ptr;
+ l = (l + 3) & ~3L;
+ return (caddr_t)l;
+}
+
+inline void
+ie_ack(volatile struct ie_sys_ctl_block *scb,
+ u_int mask, int unit,
+ void (*ca)(int)) {
+ scb->ie_command = scb->ie_status & mask;
+ (*ca)(unit);
+}
diff --git a/sys/i386/isa/ic/nec765.h b/sys/i386/isa/ic/nec765.h
new file mode 100644
index 0000000..1895db7
--- /dev/null
+++ b/sys/i386/isa/ic/nec765.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)nec765.h 7.1 (Berkeley) 5/9/91
+ * $Id$
+ */
+
+/*
+ * Nec 765 floppy disc controller definitions
+ */
+
+/* Main status register */
+#define NE7_DAB 0x01 /* Diskette drive A is seeking, thus busy */
+#define NE7_DBB 0x02 /* Diskette drive B is seeking, thus busy */
+#define NE7_CB 0x10 /* Diskette Controller Busy */
+#define NE7_NDM 0x20 /* Diskette Controller in Non Dma Mode */
+#define NE7_DIO 0x40 /* Diskette Controller Data register I/O */
+#define NE7_RQM 0x80 /* Diskette Controller ReQuest for Master */
+
+/* Status register ST0 */
+#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005drv_chck\004drive_rdy\003top_head"
+
+/* Status register ST1 */
+#define NE7_ST1BITS "\020\010end_of_cyl\006bad_crc\005data_overrun\003sec_not_fnd\002write_protect\001no_am"
+
+/* Status register ST2 */
+#define NE7_ST2BITS "\020\007ctrl_mrk\006bad_crc\005wrong_cyl\004scn_eq\003scn_not_fnd\002bad_cyl\001no_dam"
+
+/* Status register ST3 */
+#define NE7_ST3BITS "\020\010fault\007write_protect\006drdy\005tk0\004two_side\003side_sel\002"
+
+/* Commands */
+#define NE7CMD_SPECIFY 3 /* specify drive parameters - requires unit
+ parameters byte */
+#define NE7CMD_SENSED 4 /* sense drive - requires unit select byte */
+#define NE7CMD_WRITE 0xc5 /* write - requires eight additional bytes */
+#define NE7CMD_READ 0xe6 /* read - requires eight additional bytes */
+#define NE7CMD_FORMAT 0x4c /* format - requires five additional bytes */
+#define NE7CMD_RECAL 7 /* recalibrate drive - requires
+ unit select byte */
+#define NE7CMD_SENSEI 8 /* sense controller interrupt status */
+#define NE7CMD_SEEK 15 /* seek drive - requires unit select byte
+ and new cyl byte */
diff --git a/sys/i386/isa/ic/ns16450.h b/sys/i386/isa/ic/ns16450.h
new file mode 100644
index 0000000..aa6280d
--- /dev/null
+++ b/sys/i386/isa/ic/ns16450.h
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)ns16450.h 7.1 (Berkeley) 5/9/91
+ * $Id$
+ */
+
+/*
+ * NS16450 UART registers
+ */
+
+#define com_data 0 /* data register (R/W) */
+#define com_dlbl 0 /* divisor latch low (W) */
+#define com_dlbh 1 /* divisor latch high (W) */
+#define com_ier 1 /* interrupt enable (W) */
+#define com_iir 2 /* interrupt identification (R) */
+#define com_lctl 3 /* line control register (R/W) */
+#define com_cfcr 3 /* line control register (R/W) */
+#define com_mcr 4 /* modem control register (R/W) */
+#define com_lsr 5 /* line status register (R/W) */
+#define com_msr 6 /* modem status register (R/W) */
diff --git a/sys/i386/isa/ic/ns16550.h b/sys/i386/isa/ic/ns16550.h
new file mode 100644
index 0000000..ff59757
--- /dev/null
+++ b/sys/i386/isa/ic/ns16550.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)ns16550.h 7.1 (Berkeley) 5/9/91
+ * $Id$
+ */
+
+/*
+ * NS16550 UART registers
+ */
+
+#define com_data 0 /* data register (R/W) */
+#define com_dlbl 0 /* divisor latch low (W) */
+#define com_dlbh 1 /* divisor latch high (W) */
+#define com_ier 1 /* interrupt enable (W) */
+#define com_iir 2 /* interrupt identification (R) */
+#define com_fifo 2 /* FIFO control (W) */
+#define com_lctl 3 /* line control register (R/W) */
+#define com_cfcr 3 /* line control register (R/W) */
+#define com_mcr 4 /* modem control register (R/W) */
+#define com_lsr 5 /* line status register (R/W) */
+#define com_msr 6 /* modem status register (R/W) */
diff --git a/sys/i386/isa/icu.h b/sys/i386/isa/icu.h
new file mode 100644
index 0000000..13216b0
--- /dev/null
+++ b/sys/i386/isa/icu.h
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)icu.h 5.6 (Berkeley) 5/9/91
+ * $Id: icu.h,v 1.2 1993/10/16 13:45:51 rgrimes Exp $
+ */
+
+/*
+ * AT/386 Interrupt Control constants
+ * W. Jolitz 8/89
+ */
+
+#ifndef __ICU__
+#define __ICU__
+
+#ifndef LOCORE
+
+/*
+ * Interrupt "level" mechanism variables, masks, and macros
+ */
+extern unsigned imen; /* interrupt mask enable */
+
+#define INTREN(s) (imen &= ~(s), SET_ICUS())
+#define INTRDIS(s) (imen |= (s), SET_ICUS())
+#define INTRMASK(msk,s) (msk |= (s))
+#if 0
+#define SET_ICUS() (outb(IO_ICU1 + 1, imen), outb(IU_ICU2 + 1, imen >> 8))
+#else
+/*
+ * XXX - IO_ICU* are defined in isa.h, not icu.h, and nothing much bothers to
+ * include isa.h, while too many things include icu.h.
+ */
+#define SET_ICUS() (outb(0x21, imen), outb(0xa1, imen >> 8))
+#endif
+
+#endif
+
+/*
+ * Interrupt enable bits - in normal order of priority (which we change)
+ */
+#define IRQ0 0x0001 /* highest priority - timer */
+#define IRQ1 0x0002
+#define IRQ_SLAVE 0x0004
+#define IRQ8 0x0100
+#define IRQ9 0x0200
+#define IRQ2 IRQ9
+#define IRQ10 0x0400
+#define IRQ11 0x0800
+#define IRQ12 0x1000
+#define IRQ13 0x2000
+#define IRQ14 0x4000
+#define IRQ15 0x8000
+#define IRQ3 0x0008 /* this is highest after rotation */
+#define IRQ4 0x0010
+#define IRQ5 0x0020
+#define IRQ6 0x0040
+#define IRQ7 0x0080 /* lowest - parallel printer */
+
+/*
+ * Interrupt Control offset into Interrupt descriptor table (IDT)
+ */
+#define ICU_OFFSET 32 /* 0-31 are processor exceptions */
+#define ICU_LEN 16 /* 32-47 are ISA interrupts */
+
+#endif __ICU__
diff --git a/sys/i386/isa/icu.s b/sys/i386/isa/icu.s
new file mode 100644
index 0000000..c713806
--- /dev/null
+++ b/sys/i386/isa/icu.s
@@ -0,0 +1,322 @@
+/*-
+ * Copyright (c) 1989, 1990 William F. Jolitz.
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)icu.s 7.2 (Berkeley) 5/21/91
+ *
+ * $Id: icu.s,v 1.7 1993/12/20 14:58:21 wollman Exp $
+ */
+
+/*
+ * AT/386
+ * Vector interrupt control section
+ */
+
+/*
+ * XXX this file should be named ipl.s. All spls are now soft and the
+ * only thing related to the hardware icu is that the h/w interrupt
+ * numbers are used without translation in the masks.
+ */
+
+#include "../net/netisr.h"
+
+ .data
+ .globl _cpl
+_cpl: .long HWI_MASK | SWI_MASK /* current priority (all off) */
+ .globl _imen
+_imen: .long HWI_MASK /* interrupt mask enable (all h/w off) */
+_high_imask: .long HWI_MASK | SWI_MASK
+ .globl _tty_imask
+_tty_imask: .long 0
+ .globl _bio_imask
+_bio_imask: .long 0
+ .globl _net_imask
+_net_imask: .long 0
+ .globl _ipending
+_ipending: .long 0
+ .globl _astpending
+_astpending: .long 0 /* tells us an AST needs to be taken */
+ .globl _netisr
+_netisr: .long 0 /* set with bits for which queue to service */
+vec:
+ .long vec0, vec1, vec2, vec3, vec4, vec5, vec6, vec7
+ .long vec8, vec9, vec10, vec11, vec12, vec13, vec14, vec15
+
+ .text
+
+/*
+ * Handle return from interrupts, traps and syscalls.
+ */
+ SUPERALIGN_TEXT
+_doreti:
+ FAKE_MCOUNT(_bintr) /* init "from" _bintr -> _doreti */
+ addl $4,%esp /* discard unit number */
+ popl %eax /* cpl to restore */
+doreti_next:
+ /*
+ * Check for pending HWIs and SWIs atomically with restoring cpl
+ * and exiting. The check has to be atomic with exiting to stop
+ * (ipending & ~cpl) changing from zero to nonzero while we're
+ * looking at it (this wouldn't be fatal but it would increase
+ * interrupt latency). Restoring cpl has to be atomic with exiting
+ * so that the stack cannot pile up (the nesting level of interrupt
+ * handlers is limited by the number of bits in cpl).
+ */
+ movl %eax,%ecx
+ notl %ecx
+ cli
+ andl _ipending,%ecx
+ jne doreti_unpend
+doreti_exit:
+ movl %eax,_cpl
+ MEXITCOUNT
+ popl %es
+ popl %ds
+ popal
+ addl $8,%esp
+ iret
+
+ ALIGN_TEXT
+doreti_unpend:
+ /*
+ * Enabling interrupts is safe because we haven't restored cpl yet.
+ * The locking from the "btrl" test is probably no longer necessary.
+ * We won't miss any new pending interrupts because we will check
+ * for them again.
+ */
+ sti
+ bsfl %ecx,%ecx /* slow, but not worth optimizing */
+ btrl %ecx,_ipending
+ jnc doreti_next /* some intr cleared memory copy */
+ movl ihandlers(,%ecx,4),%edx
+ testl %edx,%edx
+ je doreti_next /* "can't happen" */
+ cmpl $NHWI,%ecx
+ jae doreti_swi
+ cli
+ movl %eax,_cpl
+ MEXITCOUNT
+ jmp %edx
+
+ ALIGN_TEXT
+doreti_swi:
+ pushl %eax
+ /*
+ * The SWI_AST handler has to run at cpl = SWI_AST_MASK and the
+ * SWI_CLOCK handler at cpl = SWI_CLOCK_MASK, so we have to restore
+ * all the h/w bits in cpl now and have to worry about stack growth.
+ * The worst case is currently (30 Jan 1994) 2 SWI handlers nested
+ * in dying interrupt frames and about 12 HWIs nested in active
+ * interrupt frames. There are only 4 different SWIs and the HWI
+ * and SWI masks limit the nesting further.
+ */
+ orl imasks(,%ecx,4),%eax
+ movl %eax,_cpl
+ call %edx
+ popl %eax
+ jmp doreti_next
+
+ ALIGN_TEXT
+swi_ast:
+ addl $8,%esp /* discard raddr & cpl to get trap frame */
+ testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp)
+ je swi_ast_phantom
+ movl $T_ASTFLT,(2+8+0)*4(%esp)
+ call _trap
+ subl %eax,%eax /* recover cpl */
+ jmp doreti_next
+
+ ALIGN_TEXT
+swi_ast_phantom:
+ /*
+ * These happen when there is an interrupt in a trap handler before
+ * ASTs can be masked or in an lcall handler before they can be
+ * masked or after they are unmasked. They could be avoided for
+ * trap entries by using interrupt gates, and for lcall exits by
+ * using by using cli, but they are unavoidable for lcall entries.
+ */
+ cli
+ orl $SWI_AST_PENDING,_ipending
+ jmp doreti_exit /* SWI_AST is highest so we must be done */
+
+/*
+ * Interrupt priority mechanism
+ * -- soft splXX masks with group mechanism (cpl)
+ * -- h/w masks for currently active or unused interrupts (imen)
+ * -- ipending = active interrupts currently masked by cpl
+ */
+
+ENTRY(splz)
+ /*
+ * The caller has restored cpl and checked that (ipending & ~cpl)
+ * is nonzero. We have to repeat the check since if there is an
+ * interrupt while we're looking, _doreti processing for the
+ * interrupt will handle all the unmasked pending interrupts
+ * because we restored early. We're repeating the calculation
+ * of (ipending & ~cpl) anyway so that the caller doesn't have
+ * to pass it, so this only costs one "jne". "bsfl %ecx,%ecx"
+ * is undefined when %ecx is 0 so we can't rely on the secondary
+ * btrl tests.
+ */
+ movl _cpl,%eax
+splz_next:
+ /*
+ * We don't need any locking here. (ipending & ~cpl) cannot grow
+ * while we're looking at it - any interrupt will shrink it to 0.
+ */
+ movl %eax,%ecx
+ notl %ecx
+ andl _ipending,%ecx
+ jne splz_unpend
+ ret
+
+ ALIGN_TEXT
+splz_unpend:
+ bsfl %ecx,%ecx
+ btrl %ecx,_ipending
+ jnc splz_next
+ movl ihandlers(,%ecx,4),%edx
+ testl %edx,%edx
+ je splz_next /* "can't happen" */
+ cmpl $NHWI,%ecx
+ jae splz_swi
+ /*
+ * We would prefer to call the intr handler directly here but that
+ * doesn't work for badly behaved handlers that want the interrupt
+ * frame. Also, there's a problem determining the unit number.
+ * We should change the interface so that the unit number is not
+ * determined at config time.
+ */
+ jmp *vec(,%ecx,4)
+
+ ALIGN_TEXT
+splz_swi:
+ cmpl $SWI_AST,%ecx
+ je splz_next /* "can't happen" */
+ pushl %eax
+ orl imasks(,%ecx,4),%eax
+ movl %eax,_cpl
+ call %edx
+ popl %eax
+ movl %eax,_cpl
+ jmp splz_next
+
+/*
+ * Fake clock IRQ so that it appears to come from our caller and not from
+ * vec0, so that kernel profiling works.
+ * XXX do this more generally (for all vectors; look up the C entry point).
+ * XXX frame bogusness stops us from just jumping to the C entry point.
+ */
+ ALIGN_TEXT
+vec0:
+ popl %eax /* return address */
+ pushfl
+#define KCSEL 8
+ pushl $KCSEL
+ pushl %eax
+ cli
+ MEXITCOUNT
+ jmp _Vclk
+
+#define BUILD_VEC(irq_num) \
+ ALIGN_TEXT ; \
+vec/**/irq_num: ; \
+ int $ICU_OFFSET + (irq_num) ; \
+ ret
+
+ BUILD_VEC(1)
+ BUILD_VEC(2)
+ BUILD_VEC(3)
+ BUILD_VEC(4)
+ BUILD_VEC(5)
+ BUILD_VEC(6)
+ BUILD_VEC(7)
+ BUILD_VEC(8)
+ BUILD_VEC(9)
+ BUILD_VEC(10)
+ BUILD_VEC(11)
+ BUILD_VEC(12)
+ BUILD_VEC(13)
+ BUILD_VEC(14)
+ BUILD_VEC(15)
+
+ ALIGN_TEXT
+swi_clock:
+ MCOUNT
+ subl %eax,%eax
+ cmpl $_splz,(%esp) /* XXX call from splz()? */
+ jae 1f /* yes, usermode = 0 */
+ movl 4+4+TRAPF_CS_OFF(%esp),%eax /* no, check trap frame */
+ andl $SEL_RPL_MASK,%eax
+1:
+ pushl %eax
+ call _softclock
+ addl $4,%esp
+ ret
+
+#define DONET(s, c, event) ; \
+ .globl c ; \
+ btrl $s,_netisr ; \
+ jnc 9f ; \
+ call c ; \
+9:
+
+ ALIGN_TEXT
+swi_net:
+ MCOUNT
+#ifdef INET
+ DONET(NETISR_ARP, _arpintr,netisr_ip)
+ DONET(NETISR_IP, _ipintr,netisr_ip)
+#endif
+#ifdef NS
+ DONET(NETISR_NS, _nsintr,netisr_ns)
+#endif
+#ifdef ISO
+ DONET(NETISR_ISO, _clnlintr,netisr_iso)
+#endif
+#ifdef CCITT
+ DONET(NETISR_CCITT, _ccittintr, 29)
+#endif
+ ret
+
+ ALIGN_TEXT
+swi_tty:
+ MCOUNT
+#include "sio.h"
+#if NSIO > 0
+ jmp _siopoll
+#else
+ ret
+#endif
diff --git a/sys/i386/isa/if_ed.c b/sys/i386/isa/if_ed.c
new file mode 100644
index 0000000..26e3ebd
--- /dev/null
+++ b/sys/i386/isa/if_ed.c
@@ -0,0 +1,2487 @@
+/*
+ * Device driver for National Semiconductor DS8390/WD83C690 based ethernet
+ * adapters. By David Greenman, 29-April-1993
+ *
+ * Copyright (C) 1993, David Greenman. This software may be used, modified,
+ * copied, distributed, and sold, in both source and binary form provided
+ * that the above copyright and these terms are retained. Under no
+ * circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ *
+ * Currently supports the Western Digital/SMC 8003 and 8013 series,
+ * the SMC Elite Ultra (8216), the 3Com 3c503, the NE1000 and NE2000,
+ * and a variety of similar clones.
+ *
+ */
+
+/*
+ * $Id: if_ed.c,v 1.36 1994/04/10 20:06:26 davidg Exp $
+ */
+
+#include "ed.h"
+#if NED > 0
+/* bpfilter included here in case it is needed in future net includes */
+#include "bpfilter.h"
+
+#include "param.h"
+#include "systm.h"
+#include "errno.h"
+#include "ioctl.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "syslog.h"
+
+#include "net/if.h"
+#include "net/if_dl.h"
+#include "net/if_types.h"
+
+#ifdef INET
+#include "netinet/in.h"
+#include "netinet/in_systm.h"
+#include "netinet/in_var.h"
+#include "netinet/ip.h"
+#include "netinet/if_ether.h"
+#endif
+
+#ifdef NS
+#include "netns/ns.h"
+#include "netns/ns_if.h"
+#endif
+
+#if NBPFILTER > 0
+#include "net/bpf.h"
+#include "net/bpfdesc.h"
+#endif
+
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/if_edreg.h"
+
+#include "i386/include/pio.h"
+
+/* For backwards compatibility */
+#ifndef IFF_ALTPHYS
+#define IFF_ALTPHYS IFF_LINK0
+#endif
+
+/*
+ * ed_softc: per line info and status
+ */
+struct ed_softc {
+ struct arpcom arpcom; /* ethernet common */
+
+ char *type_str; /* pointer to type string */
+ u_char vendor; /* interface vendor */
+ u_char type; /* interface type code */
+
+ u_short asic_addr; /* ASIC I/O bus address */
+ u_short nic_addr; /* NIC (DS8390) I/O bus address */
+
+/*
+ * The following 'proto' variable is part of a work-around for 8013EBT asics
+ * being write-only. It's sort of a prototype/shadow of the real thing.
+ */
+ u_char wd_laar_proto;
+ u_char isa16bit; /* width of access to card 0=8 or 1=16 */
+ int is790; /* set by the probe code if the card is 790 based */
+
+ caddr_t bpf; /* BPF "magic cookie" */
+ caddr_t mem_start; /* NIC memory start address */
+ caddr_t mem_end; /* NIC memory end address */
+ u_long mem_size; /* total NIC memory size */
+ caddr_t mem_ring; /* start of RX ring-buffer (in NIC mem) */
+
+ u_char mem_shared; /* NIC memory is shared with host */
+ u_char xmit_busy; /* transmitter is busy */
+ u_char txb_cnt; /* number of transmit buffers */
+ u_char txb_inuse; /* number of TX buffers currently in-use*/
+
+ u_char txb_new; /* pointer to where new buffer will be added */
+ u_char txb_next_tx; /* pointer to next buffer ready to xmit */
+ u_short txb_len[8]; /* buffered xmit buffer lengths */
+ u_char tx_page_start; /* first page of TX buffer area */
+ u_char rec_page_start; /* first page of RX ring-buffer */
+ u_char rec_page_stop; /* last page of RX ring-buffer */
+ u_char next_packet; /* pointer to next unread RX packet */
+} ed_softc[NED];
+
+int ed_attach(struct isa_device *);
+void ed_init(int);
+void edintr(int);
+int ed_ioctl(struct ifnet *, int, caddr_t);
+int ed_probe(struct isa_device *);
+void ed_start(struct ifnet *);
+void ed_reset(int);
+void ed_watchdog(int);
+
+static void ed_get_packet(struct ed_softc *, char *, int /*u_short*/);
+static void ed_stop(int);
+
+static inline void ed_rint();
+static inline void ed_xmit();
+static inline char *ed_ring_copy();
+
+void ed_pio_readmem(), ed_pio_writemem();
+u_short ed_pio_write_mbufs();
+
+extern int ether_output();
+
+struct trailer_header {
+ u_short ether_type;
+ u_short ether_residual;
+};
+
+struct isa_driver eddriver = {
+ ed_probe,
+ ed_attach,
+ "ed"
+};
+/*
+ * Interrupt conversion table for WD/SMC ASIC
+ * (IRQ* are defined in icu.h)
+ */
+static unsigned short ed_intr_mask[] = {
+ IRQ9,
+ IRQ3,
+ IRQ5,
+ IRQ7,
+ IRQ10,
+ IRQ11,
+ IRQ15,
+ IRQ4
+};
+
+/*
+ * Interrupt conversion table for 585/790 Combo
+ */
+static unsigned short ed_790_intr_mask[] = {
+ 0,
+ IRQ9,
+ IRQ3,
+ IRQ5,
+ IRQ7,
+ IRQ10,
+ IRQ11,
+ IRQ15
+};
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1518
+#define ETHER_ADDR_LEN 6
+#define ETHER_HDR_SIZE 14
+
+/*
+ * Determine if the device is present
+ *
+ * on entry:
+ * a pointer to an isa_device struct
+ * on exit:
+ * NULL if device not found
+ * or # of i/o addresses used (if found)
+ */
+int
+ed_probe(isa_dev)
+ struct isa_device *isa_dev;
+{
+ struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
+ int nports;
+
+ if (nports = ed_probe_WD80x3(isa_dev))
+ return (nports);
+
+ if (nports = ed_probe_3Com(isa_dev))
+ return (nports);
+
+ if (nports = ed_probe_Novell(isa_dev))
+ return (nports);
+
+ return(0);
+}
+
+/*
+ * Generic probe routine for testing for the existance of a DS8390.
+ * Must be called after the NIC has just been reset. This routine
+ * works by looking at certain register values that are gauranteed
+ * to be initialized a certain way after power-up or reset. Seems
+ * not to currently work on the 83C690.
+ *
+ * Specifically:
+ *
+ * Register reset bits set bits
+ * Command Register (CR) TXP, STA RD2, STP
+ * Interrupt Status (ISR) RST
+ * Interrupt Mask (IMR) All bits
+ * Data Control (DCR) LAS
+ * Transmit Config. (TCR) LB1, LB0
+ *
+ * We only look at the CR and ISR registers, however, because looking at
+ * the others would require changing register pages (which would be
+ * intrusive if this isn't an 8390).
+ *
+ * Return 1 if 8390 was found, 0 if not.
+ */
+
+int
+ed_probe_generic8390(sc)
+ struct ed_softc *sc;
+{
+ if ((inb(sc->nic_addr + ED_P0_CR) &
+ (ED_CR_RD2|ED_CR_TXP|ED_CR_STA|ED_CR_STP)) !=
+ (ED_CR_RD2|ED_CR_STP))
+ return (0);
+ if ((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) != ED_ISR_RST)
+ return (0);
+
+ return(1);
+}
+
+/*
+ * Probe and vendor-specific initialization routine for SMC/WD80x3 boards
+ */
+int
+ed_probe_WD80x3(isa_dev)
+ struct isa_device *isa_dev;
+{
+ struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
+ int i;
+ u_int memsize;
+ u_char iptr, isa16bit, sum;
+
+ sc->asic_addr = isa_dev->id_iobase;
+ sc->nic_addr = sc->asic_addr + ED_WD_NIC_OFFSET;
+ sc->is790 = 0;
+
+#ifdef TOSH_ETHER
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_POW);
+ DELAY(10000);
+#endif
+ /*
+ * Attempt to do a checksum over the station address PROM.
+ * If it fails, it's probably not a SMC/WD board. There
+ * is a problem with this, though: some clone WD boards
+ * don't pass the checksum test. Danpex boards for one.
+ */
+ for (sum = 0, i = 0; i < 8; ++i)
+ sum += inb(sc->asic_addr + ED_WD_PROM + i);
+
+ if (sum != ED_WD_ROM_CHECKSUM_TOTAL) {
+ /*
+ * Checksum is invalid. This often happens with cheap
+ * WD8003E clones. In this case, the checksum byte
+ * (the eighth byte) seems to always be zero.
+ */
+ if (inb(sc->asic_addr + ED_WD_CARD_ID) != ED_TYPE_WD8003E ||
+ inb(sc->asic_addr + ED_WD_PROM + 7) != 0)
+ return(0);
+ }
+
+ /* reset card to force it into a known state. */
+#ifdef TOSH_ETHER
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST | ED_WD_MSR_POW);
+#else
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST);
+#endif
+ DELAY(100);
+ outb(sc->asic_addr + ED_WD_MSR, inb(sc->asic_addr + ED_WD_MSR) & ~ED_WD_MSR_RST);
+ /* wait in the case this card is reading it's EEROM */
+ DELAY(5000);
+
+ sc->vendor = ED_VENDOR_WD_SMC;
+ sc->type = inb(sc->asic_addr + ED_WD_CARD_ID);
+
+ /*
+ * Set initial values for width/size.
+ */
+ memsize = 8192;
+ isa16bit = 0;
+ switch (sc->type) {
+ case ED_TYPE_WD8003S:
+ sc->type_str = "WD8003S";
+ break;
+ case ED_TYPE_WD8003E:
+ sc->type_str = "WD8003E";
+ break;
+ case ED_TYPE_WD8003EB:
+ sc->type_str = "WD8003EB";
+ break;
+ case ED_TYPE_WD8003W:
+ sc->type_str = "WD8003W";
+ break;
+ case ED_TYPE_WD8013EBT:
+ sc->type_str = "WD8013EBT";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_WD8013W:
+ sc->type_str = "WD8013W";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_WD8013EP: /* also WD8003EP */
+ if (inb(sc->asic_addr + ED_WD_ICR)
+ & ED_WD_ICR_16BIT) {
+ isa16bit = 1;
+ memsize = 16384;
+ sc->type_str = "WD8013EP";
+ } else {
+ sc->type_str = "WD8003EP";
+ }
+ break;
+ case ED_TYPE_WD8013WC:
+ sc->type_str = "WD8013WC";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_WD8013EBP:
+ sc->type_str = "WD8013EBP";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_WD8013EPC:
+ sc->type_str = "WD8013EPC";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_SMC8216C:
+ sc->type_str = "SMC8216/SMC8216C";
+ memsize = 16384;
+ isa16bit = 1;
+ sc->is790 = 1;
+ break;
+ case ED_TYPE_SMC8216T:
+ sc->type_str = "SMC8216T";
+ memsize = 16384;
+ isa16bit = 1;
+ sc->is790 = 1;
+ break;
+#ifdef TOSH_ETHER
+ case ED_TYPE_TOSHIBA1:
+ sc->type_str = "Toshiba1";
+ memsize = 32768;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_TOSHIBA4:
+ sc->type_str = "Toshiba4";
+ memsize = 32768;
+ isa16bit = 1;
+ break;
+#endif
+ default:
+ sc->type_str = "";
+ break;
+ }
+ /*
+ * Make some adjustments to initial values depending on what is
+ * found in the ICR.
+ */
+ if (isa16bit && (sc->type != ED_TYPE_WD8013EBT)
+#ifdef TOSH_ETHER
+ && (sc->type != ED_TYPE_TOSHIBA1) && (sc->type != ED_TYPE_TOSHIBA4)
+#endif
+ && ((inb(sc->asic_addr + ED_WD_ICR) & ED_WD_ICR_16BIT) == 0)) {
+ isa16bit = 0;
+ memsize = 8192;
+ }
+
+#if ED_DEBUG
+ printf("type = %x type_str=%s isa16bit=%d memsize=%d id_msize=%d\n",
+ sc->type,sc->type_str,isa16bit,memsize,isa_dev->id_msize);
+ for (i=0; i<8; i++)
+ printf("%x -> %x\n", i, inb(sc->asic_addr + i));
+#endif
+ /*
+ * Allow the user to override the autoconfiguration
+ */
+ if (isa_dev->id_msize)
+ memsize = isa_dev->id_msize;
+ /*
+ * (note that if the user specifies both of the following flags
+ * that '8bit' mode intentionally has precedence)
+ */
+ if (isa_dev->id_flags & ED_FLAGS_FORCE_16BIT_MODE)
+ isa16bit = 1;
+ if (isa_dev->id_flags & ED_FLAGS_FORCE_8BIT_MODE)
+ isa16bit = 0;
+
+ /*
+ * Check 83C584 interrupt configuration register if this board has one
+ * XXX - we could also check the IO address register. But why
+ * bother...if we get past this, it *has* to be correct.
+ */
+ if ((sc->type & ED_WD_SOFTCONFIG) && (!sc->is790)) {
+ /*
+ * Assemble together the encoded interrupt number.
+ */
+ iptr = (inb(isa_dev->id_iobase + ED_WD_ICR) & ED_WD_ICR_IR2) |
+ ((inb(isa_dev->id_iobase + ED_WD_IRR) &
+ (ED_WD_IRR_IR0 | ED_WD_IRR_IR1)) >> 5);
+ /*
+ * Translate it using translation table, and check for correctness.
+ */
+ if (ed_intr_mask[iptr] != isa_dev->id_irq) {
+ printf("ed%d: kernel configured irq %d doesn't match board configured irq %d\n",
+ isa_dev->id_unit, ffs(isa_dev->id_irq) - 1,
+ ffs(ed_intr_mask[iptr]) - 1);
+ return(0);
+ }
+ /*
+ * Enable the interrupt.
+ */
+ outb(isa_dev->id_iobase + ED_WD_IRR,
+ inb(isa_dev->id_iobase + ED_WD_IRR) | ED_WD_IRR_IEN);
+ }
+ if (sc->is790) {
+ outb(isa_dev->id_iobase + ED_WD790_HWR,
+ inb(isa_dev->id_iobase + ED_WD790_HWR) | ED_WD790_HWR_SWH);
+ iptr = (((inb(isa_dev->id_iobase + ED_WD790_GCR) & ED_WD790_GCR_IR2) >> 4) |
+ (inb(isa_dev->id_iobase + ED_WD790_GCR) &
+ (ED_WD790_GCR_IR1|ED_WD790_GCR_IR0)) >> 2);
+ outb(isa_dev->id_iobase + ED_WD790_HWR,
+ inb(isa_dev->id_iobase + ED_WD790_HWR) & ~ED_WD790_HWR_SWH);
+
+ if (ed_790_intr_mask[iptr] != isa_dev->id_irq) {
+ printf("ed%d: kernel configured irq %d doesn't match board configured irq %d %d\n",
+ isa_dev->id_unit, ffs(isa_dev->id_irq) - 1,
+ ffs(ed_790_intr_mask[iptr]) - 1, iptr);
+ return 0;
+ }
+ /*
+ * Enable interrupts.
+ */
+ outb(isa_dev->id_iobase + ED_WD790_ICR,
+ inb(isa_dev->id_iobase + ED_WD790_ICR) | ED_WD790_ICR_EIL);
+ }
+
+ sc->isa16bit = isa16bit;
+
+#ifdef notyet /* XXX - I'm not sure if PIO mode is even possible on WD/SMC boards */
+ /*
+ * The following allows the WD/SMC boards to be used in Programmed I/O
+ * mode - without mapping the NIC memory shared. ...Not the prefered
+ * way, but it might be the only way.
+ */
+ if (isa_dev->id_flags & ED_FLAGS_FORCE_PIO) {
+ sc->mem_shared = 0;
+ isa_dev->id_maddr = 0;
+ } else {
+ sc->mem_shared = 1;
+ }
+#else
+ sc->mem_shared = 1;
+#endif
+ isa_dev->id_msize = memsize;
+
+ sc->mem_start = (caddr_t)isa_dev->id_maddr;
+
+ /*
+ * allocate one xmit buffer if < 16k, two buffers otherwise
+ */
+ if ((memsize < 16384) || (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)) {
+ sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE);
+ sc->txb_cnt = 1;
+ sc->rec_page_start = ED_TXBUF_SIZE;
+ } else {
+ sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE * 2);
+ sc->txb_cnt = 2;
+ sc->rec_page_start = ED_TXBUF_SIZE * 2;
+ }
+ sc->mem_size = memsize;
+ sc->mem_end = sc->mem_start + memsize;
+ sc->rec_page_stop = memsize / ED_PAGE_SIZE;
+ sc->tx_page_start = ED_WD_PAGE_OFFSET;
+
+ /*
+ * Get station address from on-board ROM
+ */
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ sc->arpcom.ac_enaddr[i] = inb(sc->asic_addr + ED_WD_PROM + i);
+
+ if (sc->mem_shared) {
+ /*
+ * Set address and enable interface shared memory.
+ */
+ if(!sc->is790) {
+#ifdef TOSH_ETHER
+ outb(sc->asic_addr + ED_WD_MSR + 1, ((kvtop(sc->mem_start) >> 8) & 0xe0) | 4);
+ outb(sc->asic_addr + ED_WD_MSR + 2, ((kvtop(sc->mem_start) >> 16) & 0x0f));
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB | ED_WD_MSR_POW);
+
+#else
+ outb(sc->asic_addr + ED_WD_MSR, ((kvtop(sc->mem_start) >> 13) &
+ ED_WD_MSR_ADDR) | ED_WD_MSR_MENB);
+#endif
+ } else {
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB);
+ outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) | 0x80));
+ outb(sc->asic_addr + 0x0b, ((kvtop(sc->mem_start) >> 13) & 0x0f) |
+ ((kvtop(sc->mem_start) >> 11) & 0x40) |
+ (inb(sc->asic_addr + 0x0b) & 0xb0));
+ outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) & ~0x80));
+ }
+
+ /*
+ * Set upper address bits and 8/16 bit access to shared memory
+ */
+ if (isa16bit) {
+ if (sc->is790) {
+ sc->wd_laar_proto = inb(sc->asic_addr + ED_WD_LAAR);
+ outb(sc->asic_addr + ED_WD_LAAR, ED_WD_LAAR_M16EN);
+ (void) inb(0x84);
+ } else {
+ outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto =
+ ED_WD_LAAR_L16EN | ED_WD_LAAR_M16EN |
+ ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI)));
+ }
+ } else {
+ if ((sc->type & ED_WD_SOFTCONFIG) ||
+#ifdef TOSH_ETHER
+ (sc->type == ED_TYPE_TOSHIBA1) || (sc->type == ED_TYPE_TOSHIBA4) ||
+#endif
+ (sc->type == ED_TYPE_WD8013EBT) && (!sc->is790)) {
+ outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto =
+ ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI)));
+ }
+ }
+
+ /*
+ * Now zero memory and verify that it is clear
+ */
+ bzero(sc->mem_start, memsize);
+
+ for (i = 0; i < memsize; ++i)
+ if (sc->mem_start[i]) {
+ printf("ed%d: failed to clear shared memory at %x - check configuration\n",
+ isa_dev->id_unit, kvtop(sc->mem_start + i));
+
+ /*
+ * Disable 16 bit access to shared memory
+ */
+ if (isa16bit) {
+ outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &=
+ ~ED_WD_LAAR_M16EN));
+ (void) inb(0x84);
+ }
+
+ return(0);
+ }
+
+ /*
+ * Disable 16bit access to shared memory - we leave it disabled so
+ * that 1) machines reboot properly when the board is set
+ * 16 bit mode and there are conflicting 8bit devices/ROMS
+ * in the same 128k address space as this boards shared
+ * memory. and 2) so that other 8 bit devices with shared
+ * memory can be used in this 128k region, too.
+ */
+ if (isa16bit) {
+ outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &=
+ ~ED_WD_LAAR_M16EN));
+ (void) inb(0x84);
+ }
+ }
+
+ return (ED_WD_IO_PORTS);
+}
+
+/*
+ * Probe and vendor-specific initialization routine for 3Com 3c503 boards
+ */
+int
+ed_probe_3Com(isa_dev)
+ struct isa_device *isa_dev;
+{
+ struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
+ int i;
+ u_int memsize;
+ u_char isa16bit, sum;
+
+ sc->asic_addr = isa_dev->id_iobase + ED_3COM_ASIC_OFFSET;
+ sc->nic_addr = isa_dev->id_iobase + ED_3COM_NIC_OFFSET;
+
+ /*
+ * Verify that the kernel configured I/O address matches the board
+ * configured address
+ */
+ switch (inb(sc->asic_addr + ED_3COM_BCFR)) {
+ case ED_3COM_BCFR_300:
+ if (isa_dev->id_iobase != 0x300)
+ return(0);
+ break;
+ case ED_3COM_BCFR_310:
+ if (isa_dev->id_iobase != 0x310)
+ return(0);
+ break;
+ case ED_3COM_BCFR_330:
+ if (isa_dev->id_iobase != 0x330)
+ return(0);
+ break;
+ case ED_3COM_BCFR_350:
+ if (isa_dev->id_iobase != 0x350)
+ return(0);
+ break;
+ case ED_3COM_BCFR_250:
+ if (isa_dev->id_iobase != 0x250)
+ return(0);
+ break;
+ case ED_3COM_BCFR_280:
+ if (isa_dev->id_iobase != 0x280)
+ return(0);
+ break;
+ case ED_3COM_BCFR_2A0:
+ if (isa_dev->id_iobase != 0x2a0)
+ return(0);
+ break;
+ case ED_3COM_BCFR_2E0:
+ if (isa_dev->id_iobase != 0x2e0)
+ return(0);
+ break;
+ default:
+ return(0);
+ }
+
+ /*
+ * Verify that the kernel shared memory address matches the
+ * board configured address.
+ */
+ switch (inb(sc->asic_addr + ED_3COM_PCFR)) {
+ case ED_3COM_PCFR_DC000:
+ if (kvtop(isa_dev->id_maddr) != 0xdc000)
+ return(0);
+ break;
+ case ED_3COM_PCFR_D8000:
+ if (kvtop(isa_dev->id_maddr) != 0xd8000)
+ return(0);
+ break;
+ case ED_3COM_PCFR_CC000:
+ if (kvtop(isa_dev->id_maddr) != 0xcc000)
+ return(0);
+ break;
+ case ED_3COM_PCFR_C8000:
+ if (kvtop(isa_dev->id_maddr) != 0xc8000)
+ return(0);
+ break;
+ default:
+ return(0);
+ }
+
+
+ /*
+ * Reset NIC and ASIC. Enable on-board transceiver throughout reset
+ * sequence because it'll lock up if the cable isn't connected
+ * if we don't.
+ */
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_RST | ED_3COM_CR_XSEL);
+
+ /*
+ * Wait for a while, then un-reset it
+ */
+ DELAY(50);
+ /*
+ * The 3Com ASIC defaults to rather strange settings for the CR after
+ * a reset - it's important to set it again after the following
+ * outb (this is done when we map the PROM below).
+ */
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL);
+
+ /*
+ * Wait a bit for the NIC to recover from the reset
+ */
+ DELAY(5000);
+
+ sc->vendor = ED_VENDOR_3COM;
+ sc->type_str = "3c503";
+
+ sc->mem_shared = 1;
+
+ /*
+ * Hmmm...a 16bit 3Com board has 16k of memory, but only an 8k
+ * window to it.
+ */
+ memsize = 8192;
+
+ /*
+ * Get station address from on-board ROM
+ */
+ /*
+ * First, map ethernet address PROM over the top of where the NIC
+ * registers normally appear.
+ */
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_EALO | ED_3COM_CR_XSEL);
+
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ sc->arpcom.ac_enaddr[i] = inb(sc->nic_addr + i);
+
+ /*
+ * Unmap PROM - select NIC registers. The proper setting of the
+ * tranceiver is set in ed_init so that the attach code
+ * is given a chance to set the default based on a compile-time
+ * config option
+ */
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL);
+
+ /*
+ * Determine if this is an 8bit or 16bit board
+ */
+
+ /*
+ * select page 0 registers
+ */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP);
+
+ /*
+ * Attempt to clear WTS bit. If it doesn't clear, then this is a
+ * 16bit board.
+ */
+ outb(sc->nic_addr + ED_P0_DCR, 0);
+
+ /*
+ * select page 2 registers
+ */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_2|ED_CR_RD2|ED_CR_STP);
+
+ /*
+ * The 3c503 forces the WTS bit to a one if this is a 16bit board
+ */
+ if (inb(sc->nic_addr + ED_P2_DCR) & ED_DCR_WTS)
+ isa16bit = 1;
+ else
+ isa16bit = 0;
+
+ /*
+ * select page 0 registers
+ */
+ outb(sc->nic_addr + ED_P2_CR, ED_CR_RD2|ED_CR_STP);
+
+ sc->mem_start = (caddr_t)isa_dev->id_maddr;
+ sc->mem_size = memsize;
+ sc->mem_end = sc->mem_start + memsize;
+
+ /*
+ * We have an entire 8k window to put the transmit buffers on the
+ * 16bit boards. But since the 16bit 3c503's shared memory
+ * is only fast enough to overlap the loading of one full-size
+ * packet, trying to load more than 2 buffers can actually
+ * leave the transmitter idle during the load. So 2 seems
+ * the best value. (Although a mix of variable-sized packets
+ * might change this assumption. Nonetheless, we optimize for
+ * linear transfers of same-size packets.)
+ */
+ if (isa16bit) {
+ if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)
+ sc->txb_cnt = 1;
+ else
+ sc->txb_cnt = 2;
+
+ sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_16BIT;
+ sc->rec_page_start = ED_3COM_RX_PAGE_OFFSET_16BIT;
+ sc->rec_page_stop = memsize / ED_PAGE_SIZE +
+ ED_3COM_RX_PAGE_OFFSET_16BIT;
+ sc->mem_ring = sc->mem_start;
+ } else {
+ sc->txb_cnt = 1;
+ sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_8BIT;
+ sc->rec_page_start = ED_TXBUF_SIZE + ED_3COM_TX_PAGE_OFFSET_8BIT;
+ sc->rec_page_stop = memsize / ED_PAGE_SIZE +
+ ED_3COM_TX_PAGE_OFFSET_8BIT;
+ sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE);
+ }
+
+ sc->isa16bit = isa16bit;
+
+ /*
+ * Initialize GA page start/stop registers. Probably only needed
+ * if doing DMA, but what the hell.
+ */
+ outb(sc->asic_addr + ED_3COM_PSTR, sc->rec_page_start);
+ outb(sc->asic_addr + ED_3COM_PSPR, sc->rec_page_stop);
+
+ /*
+ * Set IRQ. 3c503 only allows a choice of irq 2-5.
+ */
+ switch (isa_dev->id_irq) {
+ case IRQ2:
+ outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ2);
+ break;
+ case IRQ3:
+ outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ3);
+ break;
+ case IRQ4:
+ outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ4);
+ break;
+ case IRQ5:
+ outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ5);
+ break;
+ default:
+ printf("ed%d: Invalid irq configuration (%d) must be 2-5 for 3c503\n",
+ isa_dev->id_unit, ffs(isa_dev->id_irq) - 1);
+ return(0);
+ }
+
+ /*
+ * Initialize GA configuration register. Set bank and enable shared mem.
+ */
+ outb(sc->asic_addr + ED_3COM_GACFR, ED_3COM_GACFR_RSEL |
+ ED_3COM_GACFR_MBS0);
+
+ /*
+ * Initialize "Vector Pointer" registers. These gawd-awful things
+ * are compared to 20 bits of the address on ISA, and if they
+ * match, the shared memory is disabled. We set them to
+ * 0xffff0...allegedly the reset vector.
+ */
+ outb(sc->asic_addr + ED_3COM_VPTR2, 0xff);
+ outb(sc->asic_addr + ED_3COM_VPTR1, 0xff);
+ outb(sc->asic_addr + ED_3COM_VPTR0, 0x00);
+
+ /*
+ * Zero memory and verify that it is clear
+ */
+ bzero(sc->mem_start, memsize);
+
+ for (i = 0; i < memsize; ++i)
+ if (sc->mem_start[i]) {
+ printf("ed%d: failed to clear shared memory at %x - check configuration\n",
+ isa_dev->id_unit, kvtop(sc->mem_start + i));
+ return(0);
+ }
+
+ isa_dev->id_msize = memsize;
+ return(ED_3COM_IO_PORTS);
+}
+
+/*
+ * Probe and vendor-specific initialization routine for NE1000/2000 boards
+ */
+int
+ed_probe_Novell(isa_dev)
+ struct isa_device *isa_dev;
+{
+ struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
+ u_int memsize, n;
+ u_char romdata[16], isa16bit = 0, tmp;
+ static char test_pattern[32] = "THIS is A memory TEST pattern";
+ char test_buffer[32];
+
+ sc->asic_addr = isa_dev->id_iobase + ED_NOVELL_ASIC_OFFSET;
+ sc->nic_addr = isa_dev->id_iobase + ED_NOVELL_NIC_OFFSET;
+
+ /* XXX - do Novell-specific probe here */
+
+ /* Reset the board */
+ tmp = inb(sc->asic_addr + ED_NOVELL_RESET);
+
+ /*
+ * I don't know if this is necessary; probably cruft leftover from
+ * Clarkson packet driver code. Doesn't do a thing on the boards
+ * I've tested. -DG [note that a outb(0x84, 0) seems to work
+ * here, and is non-invasive...but some boards don't seem to reset
+ * and I don't have complete documentation on what the 'right'
+ * thing to do is...so we do the invasive thing for now. Yuck.]
+ */
+ outb(sc->asic_addr + ED_NOVELL_RESET, tmp);
+ DELAY(5000);
+
+ /*
+ * This is needed because some NE clones apparently don't reset the
+ * NIC properly (or the NIC chip doesn't reset fully on power-up)
+ * XXX - this makes the probe invasive! ...Done against my better
+ * judgement. -DLG
+ */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP);
+
+ DELAY(5000);
+
+ /* Make sure that we really have an 8390 based board */
+ if (!ed_probe_generic8390(sc))
+ return(0);
+
+ sc->vendor = ED_VENDOR_NOVELL;
+ sc->mem_shared = 0;
+ isa_dev->id_maddr = 0;
+
+ /*
+ * Test the ability to read and write to the NIC memory. This has
+ * the side affect of determining if this is an NE1000 or an NE2000.
+ */
+
+ /*
+ * This prevents packets from being stored in the NIC memory when
+ * the readmem routine turns on the start bit in the CR.
+ */
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON);
+
+ /* Temporarily initialize DCR for byte operations */
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_LS);
+
+ outb(sc->nic_addr + ED_P0_PSTART, 8192 / ED_PAGE_SIZE);
+ outb(sc->nic_addr + ED_P0_PSTOP, 16384 / ED_PAGE_SIZE);
+
+ sc->isa16bit = 0;
+
+ /*
+ * Write a test pattern in byte mode. If this fails, then there
+ * probably isn't any memory at 8k - which likely means
+ * that the board is an NE2000.
+ */
+ ed_pio_writemem(sc, test_pattern, 8192, sizeof(test_pattern));
+ ed_pio_readmem(sc, 8192, test_buffer, sizeof(test_pattern));
+
+ if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) {
+ /* not an NE1000 - try NE2000 */
+
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_WTS|ED_DCR_FT1|ED_DCR_LS);
+ outb(sc->nic_addr + ED_P0_PSTART, 16384 / ED_PAGE_SIZE);
+ outb(sc->nic_addr + ED_P0_PSTOP, 32768 / ED_PAGE_SIZE);
+
+ sc->isa16bit = 1;
+ /*
+ * Write a test pattern in word mode. If this also fails, then
+ * we don't know what this board is.
+ */
+ ed_pio_writemem(sc, test_pattern, 16384, sizeof(test_pattern));
+ ed_pio_readmem(sc, 16384, test_buffer, sizeof(test_pattern));
+
+ if (bcmp(test_pattern, test_buffer, sizeof(test_pattern)))
+ return(0); /* not an NE2000 either */
+
+ sc->type = ED_TYPE_NE2000;
+ sc->type_str = "NE2000";
+ } else {
+ sc->type = ED_TYPE_NE1000;
+ sc->type_str = "NE1000";
+ }
+
+ /* 8k of memory plus an additional 8k if 16bit */
+ memsize = 8192 + sc->isa16bit * 8192;
+
+#if 0 /* probably not useful - NE boards only come two ways */
+ /* allow kernel config file overrides */
+ if (isa_dev->id_msize)
+ memsize = isa_dev->id_msize;
+#endif
+
+ sc->mem_size = memsize;
+
+ /* NIC memory doesn't start at zero on an NE board */
+ /* The start address is tied to the bus width */
+ sc->mem_start = (char *) 8192 + sc->isa16bit * 8192;
+ sc->mem_end = sc->mem_start + memsize;
+ sc->tx_page_start = memsize / ED_PAGE_SIZE;
+
+ /*
+ * Use one xmit buffer if < 16k, two buffers otherwise (if not told
+ * otherwise).
+ */
+ if ((memsize < 16384) || (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING))
+ sc->txb_cnt = 1;
+ else
+ sc->txb_cnt = 2;
+
+ sc->rec_page_start = sc->tx_page_start + sc->txb_cnt * ED_TXBUF_SIZE;
+ sc->rec_page_stop = sc->tx_page_start + memsize / ED_PAGE_SIZE;
+
+ sc->mem_ring = sc->mem_start + sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE;
+
+ ed_pio_readmem(sc, 0, romdata, 16);
+ for (n = 0; n < ETHER_ADDR_LEN; n++)
+ sc->arpcom.ac_enaddr[n] = romdata[n*(sc->isa16bit+1)];
+
+ /* clear any pending interrupts that might have occurred above */
+ outb(sc->nic_addr + ED_P0_ISR, 0xff);
+
+ return(ED_NOVELL_IO_PORTS);
+}
+
+/*
+ * Install interface into kernel networking data structures
+ */
+int
+ed_attach(isa_dev)
+ struct isa_device *isa_dev;
+{
+ struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+
+ /*
+ * Set interface to stopped condition (reset)
+ */
+ ed_stop(isa_dev->id_unit);
+
+ /*
+ * Initialize ifnet structure
+ */
+ ifp->if_unit = isa_dev->id_unit;
+ ifp->if_name = "ed" ;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_init = ed_init;
+ ifp->if_output = ether_output;
+ ifp->if_start = ed_start;
+ ifp->if_ioctl = ed_ioctl;
+ ifp->if_reset = ed_reset;
+ ifp->if_watchdog = ed_watchdog;
+
+ /*
+ * Set default state for ALTPHYS flag (used to disable the tranceiver
+ * for AUI operation), based on compile-time config option.
+ */
+ if (isa_dev->id_flags & ED_FLAGS_DISABLE_TRANCEIVER)
+ ifp->if_flags =
+ (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_ALTPHYS);
+ else
+ ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS);
+
+ /*
+ * Attach the interface
+ */
+ if_attach(ifp);
+
+ /*
+ * Search down the ifa address list looking for the AF_LINK type entry
+ */
+ ifa = ifp->if_addrlist;
+ while ((ifa != 0) && (ifa->ifa_addr != 0) &&
+ (ifa->ifa_addr->sa_family != AF_LINK))
+ ifa = ifa->ifa_next;
+ /*
+ * If we find an AF_LINK type entry we fill in the hardware address.
+ * This is useful for netstat(1) to keep track of which interface
+ * is which.
+ */
+ if ((ifa != 0) && (ifa->ifa_addr != 0)) {
+ /*
+ * Fill in the link-level address for this interface
+ */
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ sdl->sdl_type = IFT_ETHER;
+ sdl->sdl_alen = ETHER_ADDR_LEN;
+ sdl->sdl_slen = 0;
+ bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN);
+ }
+
+ /*
+ * Print additional info when attached
+ */
+ printf("ed%d: address %s, ", isa_dev->id_unit,
+ ether_sprintf(sc->arpcom.ac_enaddr));
+
+ if (sc->type_str && (*sc->type_str != 0))
+ printf("type %s ", sc->type_str);
+ else
+ printf("type unknown (0x%x) ", sc->type);
+
+ printf("%s ",sc->isa16bit ? "(16 bit)" : "(8 bit)");
+
+ printf("%s\n", ((sc->vendor == ED_VENDOR_3COM) &&
+ (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : "");
+
+ /*
+ * If BPF is in the kernel, call the attach for it
+ */
+#if NBPFILTER > 0
+ bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+ return 1;
+}
+
+/*
+ * Reset interface.
+ */
+void
+ed_reset(unit)
+ int unit;
+{
+ int s;
+
+ s = splimp();
+
+ /*
+ * Stop interface and re-initialize.
+ */
+ ed_stop(unit);
+ ed_init(unit);
+
+ (void) splx(s);
+}
+
+/*
+ * Take interface offline.
+ */
+void
+ed_stop(unit)
+ int unit;
+{
+ struct ed_softc *sc = &ed_softc[unit];
+ int n = 5000;
+
+ /*
+ * Stop everything on the interface, and select page 0 registers.
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_STP);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP);
+ }
+ /*
+ * Wait for interface to enter stopped state, but limit # of checks
+ * to 'n' (about 5ms). It shouldn't even take 5us on modern
+ * DS8390's, but just in case it's an old one.
+ */
+ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) == 0) && --n);
+
+}
+
+/*
+ * Device timeout/watchdog routine. Entered if the device neglects to
+ * generate an interrupt after a transmit has been started on it.
+ */
+void
+ed_watchdog(unit)
+ int unit;
+{
+ struct ed_softc *sc = &ed_softc[unit];
+
+ log(LOG_ERR, "ed%d: device timeout\n", unit);
+ ++sc->arpcom.ac_if.if_oerrors;
+
+ ed_reset(unit);
+}
+
+/*
+ * Initialize device.
+ */
+void
+ed_init(unit)
+ int unit;
+{
+ struct ed_softc *sc = &ed_softc[unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ int i, s;
+ u_char command;
+
+
+ /* address not known */
+ if (ifp->if_addrlist == (struct ifaddr *)0) return;
+
+ /*
+ * Initialize the NIC in the exact order outlined in the NS manual.
+ * This init procedure is "mandatory"...don't change what or when
+ * things happen.
+ */
+ s = splimp();
+
+ /* reset transmitter flags */
+ sc->xmit_busy = 0;
+ sc->arpcom.ac_if.if_timer = 0;
+
+ sc->txb_inuse = 0;
+ sc->txb_new = 0;
+ sc->txb_next_tx = 0;
+
+ /* This variable is used below - don't move this assignment */
+ sc->next_packet = sc->rec_page_start + 1;
+
+ /*
+ * Set interface for page 0, Remote DMA complete, Stopped
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_STP);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP);
+ }
+ if (sc->isa16bit) {
+ /*
+ * Set FIFO threshold to 8, No auto-init Remote DMA,
+ * byte order=80x86, word-wide DMA xfers,
+ */
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_WTS|ED_DCR_LS);
+ } else {
+ /*
+ * Same as above, but byte-wide DMA xfers
+ */
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_LS);
+ }
+
+ /*
+ * Clear Remote Byte Count Registers
+ */
+ outb(sc->nic_addr + ED_P0_RBCR0, 0);
+ outb(sc->nic_addr + ED_P0_RBCR1, 0);
+
+ /*
+ * Enable reception of broadcast packets
+ */
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB);
+
+ /*
+ * Place NIC in internal loopback mode
+ */
+ outb(sc->nic_addr + ED_P0_TCR, ED_TCR_LB0);
+
+ /*
+ * Initialize transmit/receive (ring-buffer) Page Start
+ */
+ outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start);
+ outb(sc->nic_addr + ED_P0_PSTART, sc->rec_page_start);
+ /* Set lower bits of byte addressable framing to 0 */
+ if (sc->is790)
+ outb(sc->nic_addr + 0x09, 0);
+
+ /*
+ * Initialize Receiver (ring-buffer) Page Stop and Boundry
+ */
+ outb(sc->nic_addr + ED_P0_PSTOP, sc->rec_page_stop);
+ outb(sc->nic_addr + ED_P0_BNRY, sc->rec_page_start);
+
+ /*
+ * Clear all interrupts. A '1' in each bit position clears the
+ * corresponding flag.
+ */
+ outb(sc->nic_addr + ED_P0_ISR, 0xff);
+
+ /*
+ * Enable the following interrupts: receive/transmit complete,
+ * receive/transmit error, and Receiver OverWrite.
+ *
+ * Counter overflow and Remote DMA complete are *not* enabled.
+ */
+ outb(sc->nic_addr + ED_P0_IMR,
+ ED_IMR_PRXE|ED_IMR_PTXE|ED_IMR_RXEE|ED_IMR_TXEE|ED_IMR_OVWE);
+
+ /*
+ * Program Command Register for page 1
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STP);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STP);
+ }
+ /*
+ * Copy out our station address
+ */
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ outb(sc->nic_addr + ED_P1_PAR0 + i, sc->arpcom.ac_enaddr[i]);
+
+#if NBPFILTER > 0
+ /*
+ * Initialize multicast address hashing registers to accept
+ * all multicasts (only used when in promiscuous mode)
+ */
+ for (i = 0; i < 8; ++i)
+ outb(sc->nic_addr + ED_P1_MAR0 + i, 0xff);
+#endif
+
+ /*
+ * Set Current Page pointer to next_packet (initialized above)
+ */
+ outb(sc->nic_addr + ED_P1_CURR, sc->next_packet);
+
+ /*
+ * Set Command Register for page 0, Remote DMA complete,
+ * and interface Start.
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P1_CR, ED_CR_STA);
+ } else {
+ outb(sc->nic_addr + ED_P1_CR, ED_CR_RD2|ED_CR_STA);
+ }
+ /*
+ * Take interface out of loopback
+ */
+ outb(sc->nic_addr + ED_P0_TCR, 0);
+
+ /*
+ * If this is a 3Com board, the tranceiver must be software enabled
+ * (there is no settable hardware default).
+ */
+ if (sc->vendor == ED_VENDOR_3COM) {
+ if (ifp->if_flags & IFF_ALTPHYS) {
+ outb(sc->asic_addr + ED_3COM_CR, 0);
+ } else {
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL);
+ }
+ }
+
+ /*
+ * Set 'running' flag, and clear output active flag.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ /*
+ * ...and attempt to start output
+ */
+ ed_start(ifp);
+
+ (void) splx(s);
+}
+
+/*
+ * This routine actually starts the transmission on the interface
+ */
+static inline void ed_xmit(ifp)
+ struct ifnet *ifp;
+{
+ struct ed_softc *sc = &ed_softc[ifp->if_unit];
+ unsigned short len;
+
+ len = sc->txb_len[sc->txb_next_tx];
+
+ /*
+ * Set NIC for page 0 register access
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_STA);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+ }
+ /*
+ * Set TX buffer start page
+ */
+ outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start +
+ sc->txb_next_tx * ED_TXBUF_SIZE);
+
+ /*
+ * Set TX length
+ */
+ outb(sc->nic_addr + ED_P0_TBCR0, len);
+ outb(sc->nic_addr + ED_P0_TBCR1, len >> 8);
+
+ /*
+ * Set page 0, Remote DMA complete, Transmit Packet, and *Start*
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_TXP | ED_CR_STA);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_TXP|ED_CR_STA);
+ }
+ sc->xmit_busy = 1;
+
+ /*
+ * Point to next transmit buffer slot and wrap if necessary.
+ */
+ sc->txb_next_tx++;
+ if (sc->txb_next_tx == sc->txb_cnt)
+ sc->txb_next_tx = 0;
+
+ /*
+ * Set a timer just in case we never hear from the board again
+ */
+ ifp->if_timer = 2;
+}
+
+/*
+ * Start output on interface.
+ * We make two assumptions here:
+ * 1) that the current priority is set to splimp _before_ this code
+ * is called *and* is returned to the appropriate priority after
+ * return
+ * 2) that the IFF_OACTIVE flag is checked before this code is called
+ * (i.e. that the output part of the interface is idle)
+ */
+void
+ed_start(ifp)
+ struct ifnet *ifp;
+{
+ struct ed_softc *sc = &ed_softc[ifp->if_unit];
+ struct mbuf *m0, *m;
+ caddr_t buffer;
+ int len;
+
+outloop:
+ /*
+ * First, see if there are buffered packets and an idle
+ * transmitter - should never happen at this point.
+ */
+ if (sc->txb_inuse && (sc->xmit_busy == 0)) {
+ printf("ed: packets buffers, but transmitter idle\n");
+ ed_xmit(ifp);
+ }
+
+ /*
+ * See if there is room to put another packet in the buffer.
+ */
+ if (sc->txb_inuse == sc->txb_cnt) {
+ /*
+ * No room. Indicate this to the outside world
+ * and exit.
+ */
+ ifp->if_flags |= IFF_OACTIVE;
+ return;
+ }
+
+ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m);
+ if (m == 0) {
+ /*
+ * We are using the !OACTIVE flag to indicate to the outside
+ * world that we can accept an additional packet rather than
+ * that the transmitter is _actually_ active. Indeed, the
+ * transmitter may be active, but if we haven't filled all
+ * the buffers with data then we still want to accept more.
+ */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ return;
+ }
+
+ /*
+ * Copy the mbuf chain into the transmit buffer
+ */
+
+ m0 = m;
+
+ /* txb_new points to next open buffer slot */
+ buffer = sc->mem_start + (sc->txb_new * ED_TXBUF_SIZE * ED_PAGE_SIZE);
+
+ if (sc->mem_shared) {
+ /*
+ * Special case setup for 16 bit boards...
+ */
+ if (sc->isa16bit) {
+ switch (sc->vendor) {
+ /*
+ * For 16bit 3Com boards (which have 16k of memory),
+ * we have the xmit buffers in a different page
+ * of memory ('page 0') - so change pages.
+ */
+ case ED_VENDOR_3COM:
+ outb(sc->asic_addr + ED_3COM_GACFR,
+ ED_3COM_GACFR_RSEL);
+ break;
+ /*
+ * Enable 16bit access to shared memory on WD/SMC boards
+ * Don't update wd_laar_proto because we want to restore the
+ * previous state (because an arp reply in the input code
+ * may cause a call-back to ed_start)
+ * XXX - the call-back to 'start' is a bug, IMHO.
+ */
+ case ED_VENDOR_WD_SMC: {
+ outb(sc->asic_addr + ED_WD_LAAR,
+ (sc->wd_laar_proto | ED_WD_LAAR_M16EN));
+ (void) inb(0x84);
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB);
+ (void) inb(0x84);
+ }
+ (void) inb(0x84);
+ break;
+ }
+ }
+ }
+
+ for (len = 0; m != 0; m = m->m_next) {
+ bcopy(mtod(m, caddr_t), buffer, m->m_len);
+ buffer += m->m_len;
+ len += m->m_len;
+ }
+
+ /*
+ * Restore previous shared memory access
+ */
+ if (sc->isa16bit) {
+ switch (sc->vendor) {
+ case ED_VENDOR_3COM:
+ outb(sc->asic_addr + ED_3COM_GACFR,
+ ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0);
+ break;
+ case ED_VENDOR_WD_SMC: {
+ outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto);
+ (void) inb(0x84);
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR, 0x00);
+ (void) inb(0x84);
+ }
+ break;
+ }
+ }
+ }
+ } else {
+ len = ed_pio_write_mbufs(sc, m, buffer);
+ }
+
+ sc->txb_len[sc->txb_new] = max(len, ETHER_MIN_LEN);
+
+ sc->txb_inuse++;
+
+ /*
+ * Point to next buffer slot and wrap if necessary.
+ */
+ sc->txb_new++;
+ if (sc->txb_new == sc->txb_cnt)
+ sc->txb_new = 0;
+
+ if (sc->xmit_busy == 0)
+ ed_xmit(ifp);
+ /*
+ * If there is BPF support in the configuration, tap off here.
+ * The following has support for converting trailer packets
+ * back to normal.
+ * XXX - support for trailer packets in BPF should be moved into
+ * the bpf code proper to avoid code duplication in all of
+ * the drivers.
+ */
+#if NBPFILTER > 0
+ if (sc->bpf) {
+ u_short etype;
+ int off, datasize, resid;
+ struct ether_header *eh;
+ struct trailer_header trailer_header;
+ char ether_packet[ETHER_MAX_LEN];
+ char *ep;
+
+ ep = ether_packet;
+
+ /*
+ * We handle trailers below:
+ * Copy ether header first, then residual data,
+ * then data. Put all this in a temporary buffer
+ * 'ether_packet' and send off to bpf. Since the
+ * system has generated this packet, we assume
+ * that all of the offsets in the packet are
+ * correct; if they're not, the system will almost
+ * certainly crash in m_copydata.
+ * We make no assumptions about how the data is
+ * arranged in the mbuf chain (i.e. how much
+ * data is in each mbuf, if mbuf clusters are
+ * used, etc.), which is why we use m_copydata
+ * to get the ether header rather than assume
+ * that this is located in the first mbuf.
+ */
+ /* copy ether header */
+ m_copydata(m0, 0, sizeof(struct ether_header), ep);
+ eh = (struct ether_header *) ep;
+ ep += sizeof(struct ether_header);
+ etype = ntohs(eh->ether_type);
+ if (etype >= ETHERTYPE_TRAIL &&
+ etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
+ datasize = ((etype - ETHERTYPE_TRAIL) << 9);
+ off = datasize + sizeof(struct ether_header);
+
+ /* copy trailer_header into a data structure */
+ m_copydata(m0, off, sizeof(struct trailer_header),
+ (caddr_t)&trailer_header.ether_type);
+
+ /* copy residual data */
+ m_copydata(m0, off+sizeof(struct trailer_header),
+ resid = ntohs(trailer_header.ether_residual) -
+ sizeof(struct trailer_header), ep);
+ ep += resid;
+
+ /* copy data */
+ m_copydata(m0, sizeof(struct ether_header),
+ datasize, ep);
+ ep += datasize;
+
+ /* restore original ether packet type */
+ eh->ether_type = trailer_header.ether_type;
+
+ bpf_tap(sc->bpf, ether_packet, ep - ether_packet);
+ } else
+ bpf_mtap(sc->bpf, m0);
+ }
+#endif
+
+ m_freem(m0);
+
+ /*
+ * Loop back to the top to possibly buffer more packets
+ */
+ goto outloop;
+}
+
+/*
+ * Ethernet interface receiver interrupt.
+ */
+static inline void
+ed_rint(unit)
+ int unit;
+{
+ register struct ed_softc *sc = &ed_softc[unit];
+ u_char boundry, current;
+ u_short len;
+ struct ed_ring packet_hdr;
+ char *packet_ptr;
+
+ /*
+ * Set NIC to page 1 registers to get 'current' pointer
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STA);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STA);
+ }
+ /*
+ * 'sc->next_packet' is the logical beginning of the ring-buffer - i.e.
+ * it points to where new data has been buffered. The 'CURR'
+ * (current) register points to the logical end of the ring-buffer
+ * - i.e. it points to where additional new data will be added.
+ * We loop here until the logical beginning equals the logical
+ * end (or in other words, until the ring-buffer is empty).
+ */
+ while (sc->next_packet != inb(sc->nic_addr + ED_P1_CURR)) {
+
+ /* get pointer to this buffer's header structure */
+ packet_ptr = sc->mem_ring +
+ (sc->next_packet - sc->rec_page_start) * ED_PAGE_SIZE;
+
+ /*
+ * The byte count includes the FCS - Frame Check Sequence (a
+ * 32 bit CRC).
+ */
+ if (sc->mem_shared)
+ packet_hdr = *(struct ed_ring *)packet_ptr;
+ else
+ ed_pio_readmem(sc, packet_ptr, (char *) &packet_hdr,
+ sizeof(packet_hdr));
+ len = packet_hdr.count;
+ if ((len >= ETHER_MIN_LEN) && (len <= ETHER_MAX_LEN)) {
+ /*
+ * Go get packet. len - 4 removes CRC from length.
+ */
+ ed_get_packet(sc, packet_ptr + 4, len - 4);
+ ++sc->arpcom.ac_if.if_ipackets;
+ } else {
+ /*
+ * Really BAD...probably indicates that the ring pointers
+ * are corrupted. Also seen on early rev chips under
+ * high load - the byte order of the length gets switched.
+ */
+ log(LOG_ERR,
+ "ed%d: NIC memory corrupt - invalid packet length %d\n",
+ unit, len);
+ ++sc->arpcom.ac_if.if_ierrors;
+ ed_reset(unit);
+ return;
+ }
+
+ /*
+ * Update next packet pointer
+ */
+ sc->next_packet = packet_hdr.next_packet;
+
+ /*
+ * Update NIC boundry pointer - being careful to keep it
+ * one buffer behind. (as recommended by NS databook)
+ */
+ boundry = sc->next_packet - 1;
+ if (boundry < sc->rec_page_start)
+ boundry = sc->rec_page_stop - 1;
+
+ /*
+ * Set NIC to page 0 registers to update boundry register
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_STA);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+ }
+ outb(sc->nic_addr + ED_P0_BNRY, boundry);
+
+ /*
+ * Set NIC to page 1 registers before looping to top (prepare to
+ * get 'CURR' current pointer)
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STA);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STA);
+ }
+ }
+}
+
+/*
+ * Ethernet interface interrupt processor
+ */
+void
+edintr(unit)
+ int unit;
+{
+ struct ed_softc *sc = &ed_softc[unit];
+ u_char isr;
+
+ /*
+ * Set NIC to page 0 registers
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_STA);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+ }
+ /*
+ * loop until there are no more new interrupts
+ */
+ while (isr = inb(sc->nic_addr + ED_P0_ISR)) {
+
+ /*
+ * reset all the bits that we are 'acknowledging'
+ * by writing a '1' to each bit position that was set
+ * (writing a '1' *clears* the bit)
+ */
+ outb(sc->nic_addr + ED_P0_ISR, isr);
+
+ /*
+ * Handle transmitter interrupts. Handle these first
+ * because the receiver will reset the board under
+ * some conditions.
+ */
+ if (isr & (ED_ISR_PTX|ED_ISR_TXE)) {
+ u_char collisions = inb(sc->nic_addr + ED_P0_NCR) & 0x0f;
+
+ /*
+ * Check for transmit error. If a TX completed with an
+ * error, we end up throwing the packet away. Really
+ * the only error that is possible is excessive
+ * collisions, and in this case it is best to allow the
+ * automatic mechanisms of TCP to backoff the flow. Of
+ * course, with UDP we're screwed, but this is expected
+ * when a network is heavily loaded.
+ */
+ (void) inb(sc->nic_addr + ED_P0_TSR);
+ if (isr & ED_ISR_TXE) {
+
+ /*
+ * Excessive collisions (16)
+ */
+ if ((inb(sc->nic_addr + ED_P0_TSR) & ED_TSR_ABT)
+ && (collisions == 0)) {
+ /*
+ * When collisions total 16, the
+ * P0_NCR will indicate 0, and the
+ * TSR_ABT is set.
+ */
+ collisions = 16;
+ }
+
+ /*
+ * update output errors counter
+ */
+ ++sc->arpcom.ac_if.if_oerrors;
+ } else {
+ /*
+ * Update total number of successfully
+ * transmitted packets.
+ */
+ ++sc->arpcom.ac_if.if_opackets;
+ }
+
+ /*
+ * reset tx busy and output active flags
+ */
+ sc->xmit_busy = 0;
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+
+ /*
+ * clear watchdog timer
+ */
+ sc->arpcom.ac_if.if_timer = 0;
+
+ /*
+ * Add in total number of collisions on last
+ * transmission.
+ */
+ sc->arpcom.ac_if.if_collisions += collisions;
+
+ /*
+ * Decrement buffer in-use count if not zero (can only
+ * be zero if a transmitter interrupt occured while
+ * not actually transmitting).
+ * If data is ready to transmit, start it transmitting,
+ * otherwise defer until after handling receiver
+ */
+ if (sc->txb_inuse && --sc->txb_inuse)
+ ed_xmit(&sc->arpcom.ac_if);
+ }
+
+ /*
+ * Handle receiver interrupts
+ */
+ if (isr & (ED_ISR_PRX|ED_ISR_RXE|ED_ISR_OVW)) {
+ /*
+ * Overwrite warning. In order to make sure that a lockup
+ * of the local DMA hasn't occurred, we reset and
+ * re-init the NIC. The NSC manual suggests only a
+ * partial reset/re-init is necessary - but some
+ * chips seem to want more. The DMA lockup has been
+ * seen only with early rev chips - Methinks this
+ * bug was fixed in later revs. -DG
+ */
+ if (isr & ED_ISR_OVW) {
+ ++sc->arpcom.ac_if.if_ierrors;
+#ifdef DIAGNOSTIC
+ log(LOG_WARNING,
+ "ed%d: warning - receiver ring buffer overrun\n",
+ unit);
+#endif
+ /*
+ * Stop/reset/re-init NIC
+ */
+ ed_reset(unit);
+ } else {
+
+ /*
+ * Receiver Error. One or more of: CRC error, frame
+ * alignment error FIFO overrun, or missed packet.
+ */
+ if (isr & ED_ISR_RXE) {
+ ++sc->arpcom.ac_if.if_ierrors;
+#ifdef ED_DEBUG
+ printf("ed%d: receive error %x\n", unit,
+ inb(sc->nic_addr + ED_P0_RSR));
+#endif
+ }
+
+ /*
+ * Go get the packet(s)
+ * XXX - Doing this on an error is dubious
+ * because there shouldn't be any data to
+ * get (we've configured the interface to
+ * not accept packets with errors).
+ */
+
+ /*
+ * Enable 16bit access to shared memory first
+ * on WD/SMC boards.
+ */
+ if (sc->isa16bit &&
+ (sc->vendor == ED_VENDOR_WD_SMC)) {
+
+ outb(sc->asic_addr + ED_WD_LAAR,
+ (sc->wd_laar_proto |=
+ ED_WD_LAAR_M16EN));
+ (void) inb(0x84);
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR,
+ ED_WD_MSR_MENB);
+ (void) inb(0x84);
+ }
+ }
+
+ ed_rint (unit);
+
+ /* disable 16bit access */
+ if (sc->isa16bit &&
+ (sc->vendor == ED_VENDOR_WD_SMC)) {
+
+ outb(sc->asic_addr + ED_WD_LAAR,
+ (sc->wd_laar_proto &=
+ ~ED_WD_LAAR_M16EN));
+ (void) inb(0x84);
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR, 0x00);
+ (void) inb(0x84);
+ }
+ }
+ }
+ }
+
+ /*
+ * If it looks like the transmitter can take more data,
+ * attempt to start output on the interface.
+ * This is done after handling the receiver to
+ * give the receiver priority.
+ */
+ if ((sc->arpcom.ac_if.if_flags & IFF_OACTIVE) == 0)
+ ed_start(&sc->arpcom.ac_if);
+
+ /*
+ * return NIC CR to standard state: page 0, remote DMA complete,
+ * start (toggling the TXP bit off, even if was just set
+ * in the transmit routine, is *okay* - it is 'edge'
+ * triggered from low to high)
+ */
+ if (sc->is790) {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_STA);
+ } else {
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+ }
+ /*
+ * If the Network Talley Counters overflow, read them to
+ * reset them. It appears that old 8390's won't
+ * clear the ISR flag otherwise - resulting in an
+ * infinite loop.
+ */
+ if (isr & ED_ISR_CNT) {
+ (void) inb(sc->nic_addr + ED_P0_CNTR0);
+ (void) inb(sc->nic_addr + ED_P0_CNTR1);
+ (void) inb(sc->nic_addr + ED_P0_CNTR2);
+ }
+ }
+}
+
+/*
+ * Process an ioctl request. This code needs some work - it looks
+ * pretty ugly.
+ */
+int
+ed_ioctl(ifp, command, data)
+ register struct ifnet *ifp;
+ int command;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *)data;
+ struct ed_softc *sc = &ed_softc[ifp->if_unit];
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s, error = 0;
+
+ s = splimp();
+
+ switch (command) {
+
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ ed_init(ifp->if_unit); /* before arpwhohas */
+ /*
+ * See if another station has *our* IP address.
+ * i.e.: There is an address conflict! If a
+ * conflict exists, a message is sent to the
+ * console.
+ */
+ ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
+ break;
+#endif
+#ifdef NS
+ /*
+ * XXX - This code is probably wrong
+ */
+ case AF_NS:
+ {
+ register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+ if (ns_nullhost(*ina))
+ ina->x_host =
+ *(union ns_host *)(sc->arpcom.ac_enaddr);
+ else {
+ /*
+ *
+ */
+ bcopy((caddr_t)ina->x_host.c_host,
+ (caddr_t)sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr));
+ }
+ /*
+ * Set new address
+ */
+ ed_init(ifp->if_unit);
+ break;
+ }
+#endif
+ default:
+ ed_init(ifp->if_unit);
+ break;
+ }
+ break;
+
+ case SIOCGIFADDR:
+ {
+ struct sockaddr *sa;
+ sa = (struct sockaddr *)&ifr->ifr_data;
+ bcopy((caddr_t)sc->arpcom.ac_enaddr,
+ (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
+ }
+ break;
+
+ case SIOCSIFFLAGS:
+ /*
+ * If interface is marked down and it is running, then stop it
+ */
+ if (((ifp->if_flags & IFF_UP) == 0) &&
+ (ifp->if_flags & IFF_RUNNING)) {
+ ed_stop(ifp->if_unit);
+ ifp->if_flags &= ~IFF_RUNNING;
+ } else {
+ /*
+ * If interface is marked up and it is stopped, then start it
+ */
+ if ((ifp->if_flags & IFF_UP) &&
+ ((ifp->if_flags & IFF_RUNNING) == 0))
+ ed_init(ifp->if_unit);
+ }
+#if NBPFILTER > 0
+ if (ifp->if_flags & IFF_PROMISC) {
+ /*
+ * Set promiscuous mode on interface.
+ * XXX - for multicasts to work, we would need to
+ * write 1's in all bits of multicast
+ * hashing array. For now we assume that
+ * this was done in ed_init().
+ */
+ outb(sc->nic_addr + ED_P0_RCR,
+ ED_RCR_PRO|ED_RCR_AM|ED_RCR_AB);
+ } else {
+ /*
+ * XXX - for multicasts to work, we would need to
+ * rewrite the multicast hashing array with the
+ * proper hash (would have been destroyed above).
+ */
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB);
+ }
+#endif
+ /*
+ * An unfortunate hack to provide the (required) software control
+ * of the tranceiver for 3Com boards. The ALTPHYS flag disables
+ * the tranceiver if set.
+ */
+ if (sc->vendor == ED_VENDOR_3COM) {
+ if (ifp->if_flags & IFF_ALTPHYS) {
+ outb(sc->asic_addr + ED_3COM_CR, 0);
+ } else {
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL);
+ }
+ }
+
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ (void) splx(s);
+ return (error);
+}
+
+/*
+ * Macro to calculate a new address within shared memory when given an offset
+ * from an address, taking into account ring-wrap.
+ */
+#define ringoffset(sc, start, off, type) \
+ ((type)( ((caddr_t)(start)+(off) >= (sc)->mem_end) ? \
+ (((caddr_t)(start)+(off))) - (sc)->mem_end \
+ + (sc)->mem_ring: \
+ ((caddr_t)(start)+(off)) ))
+
+/*
+ * Retreive packet from shared memory and send to the next level up via
+ * ether_input(). If there is a BPF listener, give a copy to BPF, too.
+ */
+static void
+ed_get_packet(sc, buf, len)
+ struct ed_softc *sc;
+ char *buf;
+ u_short len;
+{
+ struct ether_header *eh;
+ struct mbuf *m, *head = 0, *ed_ring_to_mbuf();
+ u_short off;
+ int resid;
+ u_short etype;
+ struct trailer_header trailer_header;
+
+ /* Allocate a header mbuf */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ goto bad;
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ m->m_pkthdr.len = len;
+ m->m_len = 0;
+ head = m;
+
+ /* The following sillines is to make NFS happy */
+#define EROUND ((sizeof(struct ether_header) + 3) & ~3)
+#define EOFF (EROUND - sizeof(struct ether_header))
+
+ /*
+ * The following assumes there is room for
+ * the ether header in the header mbuf
+ */
+ head->m_data += EOFF;
+ eh = mtod(head, struct ether_header *);
+
+ if (sc->mem_shared)
+ bcopy(buf, mtod(head, caddr_t), sizeof(struct ether_header));
+ else
+ ed_pio_readmem(sc, buf, mtod(head, caddr_t),
+ sizeof(struct ether_header));
+ buf += sizeof(struct ether_header);
+ head->m_len += sizeof(struct ether_header);
+ len -= sizeof(struct ether_header);
+
+ etype = ntohs((u_short)eh->ether_type);
+
+ /*
+ * Deal with trailer protocol:
+ * If trailer protocol, calculate the datasize as 'off',
+ * which is also the offset to the trailer header.
+ * Set resid to the amount of packet data following the
+ * trailer header.
+ * Finally, copy residual data into mbuf chain.
+ */
+ if (etype >= ETHERTYPE_TRAIL &&
+ etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
+
+ off = (etype - ETHERTYPE_TRAIL) << 9;
+ if ((off + sizeof(struct trailer_header)) > len)
+ goto bad; /* insanity */
+
+ /*
+ * If we have shared memory, we can get info directly from the
+ * stored packet, otherwise we must get a local copy
+ * of the trailer header using PIO.
+ */
+ if (sc->mem_shared) {
+ eh->ether_type = *ringoffset(sc, buf, off, u_short *);
+ resid = ntohs(*ringoffset(sc, buf, off+2, u_short *));
+ } else {
+ struct trailer_header trailer_header;
+ ed_pio_readmem(sc,
+ ringoffset(sc, buf, off, caddr_t),
+ (char *) &trailer_header,
+ sizeof(trailer_header));
+ eh->ether_type = trailer_header.ether_type;
+ resid = trailer_header.ether_residual;
+ }
+
+ if ((off + resid) > len) goto bad; /* insanity */
+
+ resid -= sizeof(struct trailer_header);
+ if (resid < 0) goto bad; /* insanity */
+
+ m = ed_ring_to_mbuf(sc, ringoffset(sc, buf, off+4, char *),
+ head, resid);
+ if (m == 0) goto bad;
+
+ len = off;
+ head->m_pkthdr.len -= 4; /* subtract trailer header */
+ }
+
+ /*
+ * Pull packet off interface. Or if this was a trailer packet,
+ * the data portion is appended.
+ */
+ m = ed_ring_to_mbuf(sc, buf, m, len);
+ if (m == 0) goto bad;
+
+#if NBPFILTER > 0
+ /*
+ * Check if there's a BPF listener on this interface.
+ * If so, hand off the raw packet to bpf.
+ */
+ if (sc->bpf) {
+ bpf_mtap(sc->bpf, head);
+
+ /*
+ * Note that the interface cannot be in promiscuous mode if
+ * there are no BPF listeners. And if we are in promiscuous
+ * mode, we have to check if this packet is really ours.
+ *
+ * XXX This test does not support multicasts.
+ */
+ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) &&
+ bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
+ sizeof(eh->ether_dhost)) != 0 &&
+ bcmp(eh->ether_dhost, etherbroadcastaddr,
+ sizeof(eh->ether_dhost)) != 0) {
+
+ m_freem(head);
+ return;
+ }
+ }
+#endif
+
+ /*
+ * Fix up data start offset in mbuf to point past ether header
+ */
+ m_adj(head, sizeof(struct ether_header));
+
+ /*
+ * silly ether_input routine needs 'type' in host byte order
+ */
+ eh->ether_type = ntohs(eh->ether_type);
+
+ ether_input(&sc->arpcom.ac_if, eh, head);
+ return;
+
+bad: if (head)
+ m_freem(head);
+ return;
+}
+
+/*
+ * Supporting routines
+ */
+
+/*
+ * Given a NIC memory source address and a host memory destination
+ * address, copy 'amount' from NIC to host using Programmed I/O.
+ * The 'amount' is rounded up to a word - okay as long as mbufs
+ * are word sized.
+ * This routine is currently Novell-specific.
+ */
+void
+ed_pio_readmem(sc,src,dst,amount)
+ struct ed_softc *sc;
+ unsigned short src;
+ unsigned char *dst;
+ unsigned short amount;
+{
+ unsigned short tmp_amount;
+
+ /* select page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+
+ /* round up to a word */
+ tmp_amount = amount;
+ if (amount & 1) ++amount;
+
+ /* set up DMA byte count */
+ outb(sc->nic_addr + ED_P0_RBCR0, amount);
+ outb(sc->nic_addr + ED_P0_RBCR1, amount>>8);
+
+ /* set up source address in NIC mem */
+ outb(sc->nic_addr + ED_P0_RSAR0, src);
+ outb(sc->nic_addr + ED_P0_RSAR1, src>>8);
+
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD0 | ED_CR_STA);
+
+ if (sc->isa16bit) {
+ insw(sc->asic_addr + ED_NOVELL_DATA, dst, amount/2);
+ } else
+ insb(sc->asic_addr + ED_NOVELL_DATA, dst, amount);
+
+}
+
+/*
+ * Stripped down routine for writing a linear buffer to NIC memory.
+ * Only used in the probe routine to test the memory. 'len' must
+ * be even.
+ */
+void
+ed_pio_writemem(sc,src,dst,len)
+ struct ed_softc *sc;
+ char *src;
+ unsigned short dst;
+ unsigned short len;
+{
+ int maxwait=100; /* about 120us */
+
+ /* select page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+
+ /* reset remote DMA complete flag */
+ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC);
+
+ /* set up DMA byte count */
+ outb(sc->nic_addr + ED_P0_RBCR0, len);
+ outb(sc->nic_addr + ED_P0_RBCR1, len>>8);
+
+ /* set up destination address in NIC mem */
+ outb(sc->nic_addr + ED_P0_RSAR0, dst);
+ outb(sc->nic_addr + ED_P0_RSAR1, dst>>8);
+
+ /* set remote DMA write */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA);
+
+ if (sc->isa16bit)
+ outsw(sc->asic_addr + ED_NOVELL_DATA, src, len/2);
+ else
+ outsb(sc->asic_addr + ED_NOVELL_DATA, src, len);
+ /*
+ * Wait for remote DMA complete. This is necessary because on the
+ * transmit side, data is handled internally by the NIC in bursts
+ * and we can't start another remote DMA until this one completes.
+ * Not waiting causes really bad things to happen - like the NIC
+ * irrecoverably jamming the ISA bus.
+ */
+ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait);
+}
+
+/*
+ * Write an mbuf chain to the destination NIC memory address using
+ * programmed I/O.
+ */
+u_short
+ed_pio_write_mbufs(sc,m,dst)
+ struct ed_softc *sc;
+ struct mbuf *m;
+ unsigned short dst;
+{
+ unsigned short len, mb_offset;
+ struct mbuf *mp;
+ unsigned char residual[2];
+ int maxwait=100; /* about 120us */
+
+ /* First, count up the total number of bytes to copy */
+ for (len = 0, mp = m; mp; mp = mp->m_next)
+ len += mp->m_len;
+
+ /* select page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+
+ /* reset remote DMA complete flag */
+ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC);
+
+ /* set up DMA byte count */
+ outb(sc->nic_addr + ED_P0_RBCR0, len);
+ outb(sc->nic_addr + ED_P0_RBCR1, len>>8);
+
+ /* set up destination address in NIC mem */
+ outb(sc->nic_addr + ED_P0_RSAR0, dst);
+ outb(sc->nic_addr + ED_P0_RSAR1, dst>>8);
+
+ /* set remote DMA write */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA);
+
+ mb_offset = 0;
+ /*
+ * Transfer the mbuf chain to the NIC memory.
+ * The following code isn't too pretty. The problem is that we can only
+ * transfer words to the board, and if an mbuf has an odd number
+ * of bytes in it, this is a problem. It's not a simple matter of
+ * just removing a byte from the next mbuf (adjusting data++ and
+ * len--) because this will hose-over the mbuf chain which might
+ * be needed later for BPF. Instead, we maintain an offset
+ * (mb_offset) which let's us skip over the first byte in the
+ * following mbuf.
+ */
+ while (m) {
+ if (m->m_len - mb_offset) {
+ if (sc->isa16bit) {
+ if ((m->m_len - mb_offset) > 1)
+ outsw(sc->asic_addr + ED_NOVELL_DATA,
+ mtod(m, caddr_t) + mb_offset,
+ (m->m_len - mb_offset) / 2);
+
+ /*
+ * if odd number of bytes, get the odd byte from
+ * the next mbuf with data
+ */
+ if ((m->m_len - mb_offset) & 1) {
+ /* first the last byte in current mbuf */
+ residual[0] = *(mtod(m, caddr_t) +
+ m->m_len - 1);
+
+ /* advance past any empty mbufs */
+ while (m->m_next && (m->m_next->m_len == 0))
+ m = m->m_next;
+
+ if (m->m_next) {
+ /* remove first byte in next mbuf */
+ residual[1] = *(mtod(m->m_next, caddr_t));
+ mb_offset = 1;
+ }
+
+ outw(sc->asic_addr + ED_NOVELL_DATA,
+ *((unsigned short *) residual));
+ } else
+ mb_offset = 0;
+ } else
+ outsb(sc->asic_addr + ED_NOVELL_DATA, m->m_data, m->m_len);
+
+ }
+ m = m->m_next;
+ }
+
+ /*
+ * Wait for remote DMA complete. This is necessary because on the
+ * transmit side, data is handled internally by the NIC in bursts
+ * and we can't start another remote DMA until this one completes.
+ * Not waiting causes really bad things to happen - like the NIC
+ * irrecoverably jamming the ISA bus.
+ */
+ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait);
+
+ if (!maxwait) {
+ log(LOG_WARNING, "ed%d: remote transmit DMA failed to complete\n",
+ sc->arpcom.ac_if.if_unit);
+ ed_reset(sc->arpcom.ac_if.if_unit);
+ }
+
+ return(len);
+}
+
+/*
+ * Given a source and destination address, copy 'amount' of a packet from
+ * the ring buffer into a linear destination buffer. Takes into account
+ * ring-wrap.
+ */
+static inline char *
+ed_ring_copy(sc,src,dst,amount)
+ struct ed_softc *sc;
+ char *src;
+ char *dst;
+ u_short amount;
+{
+ u_short tmp_amount;
+
+ /* does copy wrap to lower addr in ring buffer? */
+ if (src + amount > sc->mem_end) {
+ tmp_amount = sc->mem_end - src;
+
+ /* copy amount up to end of NIC memory */
+ if (sc->mem_shared)
+ bcopy(src,dst,tmp_amount);
+ else
+ ed_pio_readmem(sc,src,dst,tmp_amount);
+
+ amount -= tmp_amount;
+ src = sc->mem_ring;
+ dst += tmp_amount;
+ }
+
+ if (sc->mem_shared)
+ bcopy(src, dst, amount);
+ else
+ ed_pio_readmem(sc, src, dst, amount);
+
+ return(src + amount);
+}
+
+/*
+ * Copy data from receive buffer to end of mbuf chain
+ * allocate additional mbufs as needed. return pointer
+ * to last mbuf in chain.
+ * sc = ed info (softc)
+ * src = pointer in ed ring buffer
+ * dst = pointer to last mbuf in mbuf chain to copy to
+ * amount = amount of data to copy
+ */
+struct mbuf *
+ed_ring_to_mbuf(sc,src,dst,total_len)
+ struct ed_softc *sc;
+ char *src;
+ struct mbuf *dst;
+ u_short total_len;
+{
+ register struct mbuf *m = dst;
+
+ while (total_len) {
+ register u_short amount = min(total_len, M_TRAILINGSPACE(m));
+
+ if (amount == 0) { /* no more data in this mbuf, alloc another */
+ /*
+ * If there is enough data for an mbuf cluster, attempt
+ * to allocate one of those, otherwise, a regular
+ * mbuf will do.
+ * Note that a regular mbuf is always required, even if
+ * we get a cluster - getting a cluster does not
+ * allocate any mbufs, and one is needed to assign
+ * the cluster to. The mbuf that has a cluster
+ * extension can not be used to contain data - only
+ * the cluster can contain data.
+ */
+ dst = m;
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ return (0);
+
+ if (total_len >= MINCLSIZE)
+ MCLGET(m, M_DONTWAIT);
+
+ m->m_len = 0;
+ dst->m_next = m;
+ amount = min(total_len, M_TRAILINGSPACE(m));
+ }
+
+ src = ed_ring_copy(sc, src, mtod(m, caddr_t) + m->m_len, amount);
+
+ m->m_len += amount;
+ total_len -= amount;
+
+ }
+ return (m);
+}
+#endif
diff --git a/sys/i386/isa/if_edreg.h b/sys/i386/isa/if_edreg.h
new file mode 100644
index 0000000..f75e261
--- /dev/null
+++ b/sys/i386/isa/if_edreg.h
@@ -0,0 +1,962 @@
+/*
+ * National Semiconductor DS8390 NIC register definitions
+ *
+ * $Id: if_edreg.h,v 1.13 1994/02/02 14:05:58 davidg Exp $
+ *
+ * Modification history
+ *
+ * Revision 2.2 1993/11/29 16:33:39 davidg
+ * From Thomas Sandford <t.d.g.sandford@comp.brad.ac.uk>
+ * Add support for the 8013W board type
+ *
+ * Revision 2.1 1993/11/22 10:52:33 davidg
+ * patch to add support for SMC8216 (Elite-Ultra) boards
+ * from Glen H. Lowe
+ *
+ * Revision 2.0 93/09/29 00:37:15 davidg
+ * changed double buffering flag to multi buffering
+ * made changes/additions for 3c503 multi-buffering
+ * ...companion to Rev. 2.0 of 'ed' driver.
+ *
+ * Revision 1.1 93/06/23 03:01:07 davidg
+ * Initial revision
+ *
+ */
+
+/*
+ * Page 0 register offsets
+ */
+#define ED_P0_CR 0x00 /* Command Register */
+
+#define ED_P0_CLDA0 0x01 /* Current Local DMA Addr low (read) */
+#define ED_P0_PSTART 0x01 /* Page Start register (write) */
+
+#define ED_P0_CLDA1 0x02 /* Current Local DMA Addr high (read) */
+#define ED_P0_PSTOP 0x02 /* Page Stop register (write) */
+
+#define ED_P0_BNRY 0x03 /* Boundary Pointer */
+
+#define ED_P0_TSR 0x04 /* Transmit Status Register (read) */
+#define ED_P0_TPSR 0x04 /* Transmit Page Start (write) */
+
+#define ED_P0_NCR 0x05 /* Number of Collisions Reg (read) */
+#define ED_P0_TBCR0 0x05 /* Transmit Byte count, low (write) */
+
+#define ED_P0_FIFO 0x06 /* FIFO register (read) */
+#define ED_P0_TBCR1 0x06 /* Transmit Byte count, high (write) */
+
+#define ED_P0_ISR 0x07 /* Interrupt Status Register */
+
+#define ED_P0_CRDA0 0x08 /* Current Remote DMA Addr low (read) */
+#define ED_P0_RSAR0 0x08 /* Remote Start Address low (write) */
+
+#define ED_P0_CRDA1 0x09 /* Current Remote DMA Addr high (read) */
+#define ED_P0_RSAR1 0x09 /* Remote Start Address high (write) */
+
+#define ED_P0_RBCR0 0x0a /* Remote Byte Count low (write) */
+
+#define ED_P0_RBCR1 0x0b /* Remote Byte Count high (write) */
+
+#define ED_P0_RSR 0x0c /* Receive Status (read) */
+#define ED_P0_RCR 0x0c /* Receive Configuration Reg (write) */
+
+#define ED_P0_CNTR0 0x0d /* frame alignment error counter (read) */
+#define ED_P0_TCR 0x0d /* Transmit Configuration Reg (write) */
+
+#define ED_P0_CNTR1 0x0e /* CRC error counter (read) */
+#define ED_P0_DCR 0x0e /* Data Configuration Reg (write) */
+
+#define ED_P0_CNTR2 0x0f /* missed packet counter (read) */
+#define ED_P0_IMR 0x0f /* Interrupt Mask Register (write) */
+
+/*
+ * Page 1 register offsets
+ */
+#define ED_P1_CR 0x00 /* Command Register */
+#define ED_P1_PAR0 0x01 /* Physical Address Register 0 */
+#define ED_P1_PAR1 0x02 /* Physical Address Register 1 */
+#define ED_P1_PAR2 0x03 /* Physical Address Register 2 */
+#define ED_P1_PAR3 0x04 /* Physical Address Register 3 */
+#define ED_P1_PAR4 0x05 /* Physical Address Register 4 */
+#define ED_P1_PAR5 0x06 /* Physical Address Register 5 */
+#define ED_P1_CURR 0x07 /* Current RX ring-buffer page */
+#define ED_P1_MAR0 0x08 /* Multicast Address Register 0 */
+#define ED_P1_MAR1 0x09 /* Multicast Address Register 1 */
+#define ED_P1_MAR2 0x0a /* Multicast Address Register 2 */
+#define ED_P1_MAR3 0x0b /* Multicast Address Register 3 */
+#define ED_P1_MAR4 0x0c /* Multicast Address Register 4 */
+#define ED_P1_MAR5 0x0d /* Multicast Address Register 5 */
+#define ED_P1_MAR6 0x0e /* Multicast Address Register 6 */
+#define ED_P1_MAR7 0x0f /* Multicast Address Register 7 */
+
+/*
+ * Page 2 register offsets
+ */
+#define ED_P2_CR 0x00 /* Command Register */
+#define ED_P2_PSTART 0x01 /* Page Start (read) */
+#define ED_P2_CLDA0 0x01 /* Current Local DMA Addr 0 (write) */
+#define ED_P2_PSTOP 0x02 /* Page Stop (read) */
+#define ED_P2_CLDA1 0x02 /* Current Local DMA Addr 1 (write) */
+#define ED_P2_RNPP 0x03 /* Remote Next Packet Pointer */
+#define ED_P2_TPSR 0x04 /* Transmit Page Start (read) */
+#define ED_P2_LNPP 0x05 /* Local Next Packet Pointer */
+#define ED_P2_ACU 0x06 /* Address Counter Upper */
+#define ED_P2_ACL 0x07 /* Address Counter Lower */
+#define ED_P2_RCR 0x0c /* Receive Configuration Register (read) */
+#define ED_P2_TCR 0x0d /* Transmit Configuration Register (read) */
+#define ED_P2_DCR 0x0e /* Data Configuration Register (read) */
+#define ED_P2_IMR 0x0f /* Interrupt Mask Register (read) */
+
+/*
+ * Command Register (CR) definitions
+ */
+
+/*
+ * STP: SToP. Software reset command. Takes the controller offline. No
+ * packets will be received or transmitted. Any reception or
+ * transmission in progress will continue to completion before
+ * entering reset state. To exit this state, the STP bit must
+ * reset and the STA bit must be set. The software reset has
+ * executed only when indicated by the RST bit in the ISR being
+ * set.
+ */
+#define ED_CR_STP 0x01
+
+/*
+ * STA: STArt. This bit is used to activate the NIC after either power-up,
+ * or when the NIC has been put in reset mode by software command
+ * or error.
+ */
+#define ED_CR_STA 0x02
+
+/*
+ * TXP: Transmit Packet. This bit must be set to indicate transmission of
+ * a packet. TXP is internally reset either after the transmission is
+ * completed or aborted. This bit should be set only after the Transmit
+ * Byte Count and Transmit Page Start register have been programmed.
+ */
+#define ED_CR_TXP 0x04
+
+/*
+ * RD0, RD1, RD2: Remote DMA Command. These three bits control the operation
+ * of the remote DMA channel. RD2 can be set to abort any remote DMA
+ * command in progress. The Remote Byte Count registers should be cleared
+ * when a remote DMA has been aborted. The Remote Start Addresses are not
+ * restored to the starting address if the remote DMA is aborted.
+ *
+ * RD2 RD1 RD0 function
+ * 0 0 0 not allowed
+ * 0 0 1 remote read
+ * 0 1 0 remote write
+ * 0 1 1 send packet
+ * 1 X X abort
+ */
+#define ED_CR_RD0 0x08
+#define ED_CR_RD1 0x10
+#define ED_CR_RD2 0x20
+
+/*
+ * PS0, PS1: Page Select. The two bits select which register set or 'page' to
+ * access.
+ *
+ * PS1 PS0 page
+ * 0 0 0
+ * 0 1 1
+ * 1 0 2
+ * 1 1 reserved
+ */
+#define ED_CR_PS0 0x40
+#define ED_CR_PS1 0x80
+/* bit encoded aliases */
+#define ED_CR_PAGE_0 0x00 /* (for consistency) */
+#define ED_CR_PAGE_1 0x40
+#define ED_CR_PAGE_2 0x80
+
+/*
+ * Interrupt Status Register (ISR) definitions
+ */
+
+/*
+ * PRX: Packet Received. Indicates packet received with no errors.
+ */
+#define ED_ISR_PRX 0x01
+
+/*
+ * PTX: Packet Transmitted. Indicates packet transmitted with no errors.
+ */
+#define ED_ISR_PTX 0x02
+
+/*
+ * RXE: Receive Error. Indicates that a packet was received with one or more
+ * the following errors: CRC error, frame alignment error, FIFO overrun,
+ * missed packet.
+ */
+#define ED_ISR_RXE 0x04
+
+/*
+ * TXE: Transmission Error. Indicates that an attempt to transmit a packet
+ * resulted in one or more of the following errors: excessive
+ * collisions, FIFO underrun.
+ */
+#define ED_ISR_TXE 0x08
+
+/*
+ * OVW: OverWrite. Indicates a receive ring-buffer overrun. Incoming network
+ * would exceed (has exceeded?) the boundry pointer, resulting in data
+ * that was previously received and not yet read from the buffer to be
+ * overwritten.
+ */
+#define ED_ISR_OVW 0x10
+
+/*
+ * CNT: Counter Overflow. Set when the MSB of one or more of the Network Talley
+ * Counters has been set.
+ */
+#define ED_ISR_CNT 0x20
+
+/*
+ * RDC: Remote Data Complete. Indicates that a Remote DMA operation has completed.
+ */
+#define ED_ISR_RDC 0x40
+
+/*
+ * RST: Reset status. Set when the NIC enters the reset state and cleared when a
+ * Start Command is issued to the CR. This bit is also set when a receive
+ * ring-buffer overrun (OverWrite) occurs and is cleared when one or more
+ * packets have been removed from the ring. This is a read-only bit.
+ */
+#define ED_ISR_RST 0x80
+
+/*
+ * Interrupt Mask Register (IMR) definitions
+ */
+
+/*
+ * PRXE: Packet Received interrupt Enable. If set, a received packet will cause
+ * an interrupt.
+ */
+#define ED_IMR_PRXE 0x01
+
+/*
+ * PTXE: Packet Transmit interrupt Enable. If set, an interrupt is generated when
+ * a packet transmission completes.
+ */
+#define ED_IMR_PTXE 0x02
+
+/*
+ * RXEE: Receive Error interrupt Enable. If set, an interrupt will occur whenever a
+ * packet is received with an error.
+ */
+#define ED_IMR_RXEE 0x04
+
+/*
+ * TXEE: Transmit Error interrupt Enable. If set, an interrupt will occur whenever
+ * a transmission results in an error.
+ */
+#define ED_IMR_TXEE 0x08
+
+/*
+ * OVWE: OverWrite error interrupt Enable. If set, an interrupt is generated whenever
+ * the receive ring-buffer is overrun. i.e. when the boundry pointer is exceeded.
+ */
+#define ED_IMR_OVWE 0x10
+
+/*
+ * CNTE: Counter overflow interrupt Enable. If set, an interrupt is generated whenever
+ * the MSB of one or more of the Network Statistics counters has been set.
+ */
+#define ED_IMR_CNTE 0x20
+
+/*
+ * RDCE: Remote DMA Complete interrupt Enable. If set, an interrupt is generated
+ * when a remote DMA transfer has completed.
+ */
+#define ED_IMR_RDCE 0x40
+
+/*
+ * bit 7 is unused/reserved
+ */
+
+/*
+ * Data Configuration Register (DCR) definitions
+ */
+
+/*
+ * WTS: Word Transfer Select. WTS establishes byte or word transfers for
+ * both remote and local DMA transfers
+ */
+#define ED_DCR_WTS 0x01
+
+/*
+ * BOS: Byte Order Select. BOS sets the byte order for the host.
+ * Should be 0 for 80x86, and 1 for 68000 series processors
+ */
+#define ED_DCR_BOS 0x02
+
+/*
+ * LAS: Long Address Select. When LAS is 1, the contents of the remote
+ * DMA registers RSAR0 and RSAR1 are used to provide A16-A31
+ */
+#define ED_DCR_LAS 0x04
+
+/*
+ * LS: Loopback Select. When 0, loopback mode is selected. Bits D1 and D2
+ * of the TCR must also be programmed for loopback operation.
+ * When 1, normal operation is selected.
+ */
+#define ED_DCR_LS 0x08
+
+/*
+ * AR: Auto-initialize Remote. When 0, data must be removed from ring-buffer
+ * under program control. When 1, remote DMA is automatically initiated
+ * and the boundry pointer is automatically updated
+ */
+#define ED_DCR_AR 0x10
+
+/*
+ * FT0, FT1: Fifo Threshold select.
+ * FT1 FT0 Word-width Byte-width
+ * 0 0 1 word 2 bytes
+ * 0 1 2 words 4 bytes
+ * 1 0 4 words 8 bytes
+ * 1 1 8 words 12 bytes
+ *
+ * During transmission, the FIFO threshold indicates the number of bytes
+ * or words that the FIFO has filled from the local DMA before BREQ is
+ * asserted. The transmission threshold is 16 bytes minus the receiver
+ * threshold.
+ */
+#define ED_DCR_FT0 0x20
+#define ED_DCR_FT1 0x40
+
+/*
+ * bit 7 (0x80) is unused/reserved
+ */
+
+/*
+ * Transmit Configuration Register (TCR) definitions
+ */
+
+/*
+ * CRC: Inhibit CRC. If 0, CRC will be appended by the transmitter, if 0, CRC
+ * is not appended by the transmitter.
+ */
+#define ED_TCR_CRC 0x01
+
+/*
+ * LB0, LB1: Loopback control. These two bits set the type of loopback that is
+ * to be performed.
+ *
+ * LB1 LB0 mode
+ * 0 0 0 - normal operation (DCR_LS = 0)
+ * 0 1 1 - internal loopback (DCR_LS = 0)
+ * 1 0 2 - external loopback (DCR_LS = 1)
+ * 1 1 3 - external loopback (DCR_LS = 0)
+ */
+#define ED_TCR_LB0 0x02
+#define ED_TCR_LB1 0x04
+
+/*
+ * ATD: Auto Transmit Disable. Clear for normal operation. When set, allows
+ * another station to disable the NIC's transmitter by transmitting to
+ * a multicast address hashing to bit 62. Reception of a multicast address
+ * hashing to bit 63 enables the transmitter.
+ */
+#define ED_TCR_ATD 0x08
+
+/*
+ * OFST: Collision Offset enable. This bit when set modifies the backoff
+ * algorithm to allow prioritization of nodes.
+ */
+#define ED_TCR_OFST 0x10
+
+/*
+ * bits 5, 6, and 7 are unused/reserved
+ */
+
+/*
+ * Transmit Status Register (TSR) definitions
+ */
+
+/*
+ * PTX: Packet Transmitted. Indicates successful transmission of packet.
+ */
+#define ED_TSR_PTX 0x01
+
+/*
+ * bit 1 (0x02) is unused/reserved
+ */
+
+/*
+ * COL: Transmit Collided. Indicates that the transmission collided at least
+ * once with another station on the network.
+ */
+#define ED_TSR_COL 0x04
+
+/*
+ * ABT: Transmit aborted. Indicates that the transmission was aborted due to
+ * excessive collisions.
+ */
+#define ED_TSR_ABT 0x08
+
+/*
+ * CRS: Carrier Sense Lost. Indicates that carrier was lost during the
+ * transmission of the packet. (Transmission is not aborted because
+ * of a loss of carrier)
+ */
+#define ED_TSR_CRS 0x10
+
+/*
+ * FU: FIFO Underrun. Indicates that the NIC wasn't able to access bus/
+ * transmission memory before the FIFO emptied. Transmission of the
+ * packet was aborted.
+ */
+#define ED_TSR_FU 0x20
+
+/*
+ * CDH: CD Heartbeat. Indicates that the collision detection circuitry
+ * isn't working correctly during a collision heartbeat test.
+ */
+#define ED_TSR_CDH 0x40
+
+/*
+ * OWC: Out of Window Collision: Indicates that a collision occurred after
+ * a slot time (51.2us). The transmission is rescheduled just as in
+ * normal collisions.
+ */
+#define ED_TSR_OWC 0x80
+
+/*
+ * Receiver Configuration Register (RCR) definitions
+ */
+
+/*
+ * SEP: Save Errored Packets. If 0, error packets are discarded. If set to 1,
+ * packets with CRC and frame errors are not discarded.
+ */
+#define ED_RCR_SEP 0x01
+
+/*
+ * AR: Accept Runt packet. If 0, packet with less than 64 byte are discarded.
+ * If set to 1, packets with less than 64 byte are not discarded.
+ */
+#define ED_RCR_AR 0x02
+
+/*
+ * AB: Accept Broadcast. If set, packets sent to the broadcast address will be
+ * accepted.
+ */
+#define ED_RCR_AB 0x04
+
+/*
+ * AM: Accept Multicast. If set, packets sent to a multicast address are checked
+ * for a match in the hashing array. If clear, multicast packets are ignored.
+ */
+#define ED_RCR_AM 0x08
+
+/*
+ * PRO: Promiscuous Physical. If set, all packets with a physical addresses are
+ * accepted. If clear, a physical destination address must match this
+ * station's address. Note: for full promiscuous mode, RCR_AB and RCR_AM
+ * must also be set. In addition, the multicast hashing array must be set
+ * to all 1's so that all multicast addresses are accepted.
+ */
+#define ED_RCR_PRO 0x10
+
+/*
+ * MON: Monitor Mode. If set, packets will be checked for good CRC and framing,
+ * but are not stored in the ring-buffer. If clear, packets are stored (normal
+ * operation).
+ */
+#define ED_RCR_MON 0x20
+
+/*
+ * bits 6 and 7 are unused/reserved.
+ */
+
+/*
+ * Receiver Status Register (RSR) definitions
+ */
+
+/*
+ * PRX: Packet Received without error.
+ */
+#define ED_RSR_PRX 0x01
+
+/*
+ * CRC: CRC error. Indicates that a packet has a CRC error. Also set for frame
+ * alignment errors.
+ */
+#define ED_RSR_CRC 0x02
+
+/*
+ * FAE: Frame Alignment Error. Indicates that the incoming packet did not end on
+ * a byte boundry and the CRC did not match at the last byte boundry.
+ */
+#define ED_RSR_FAE 0x04
+
+/*
+ * FO: FIFO Overrun. Indicates that the FIFO was not serviced (during local DMA)
+ * causing it to overrun. Reception of the packet is aborted.
+ */
+#define ED_RSR_FO 0x08
+
+/*
+ * MPA: Missed Packet. Indicates that the received packet couldn't be stored in
+ * the ring-buffer because of insufficient buffer space (exceeding the
+ * boundry pointer), or because the transfer to the ring-buffer was inhibited
+ * by RCR_MON - monitor mode.
+ */
+#define ED_RSR_MPA 0x10
+
+/*
+ * PHY: Physical address. If 0, the packet received was sent to a physical address.
+ * If 1, the packet was accepted because of a multicast/broadcast address
+ * match.
+ */
+#define ED_RSR_PHY 0x20
+
+/*
+ * DIS: Receiver Disabled. Set to indicate that the receiver has enetered monitor
+ * mode. Cleared when the receiver exits monitor mode.
+ */
+#define ED_RSR_DIS 0x40
+
+/*
+ * DFR: Deferring. Set to indicate a 'jabber' condition. The CRS and COL inputs
+ * are active, and the transceiver has set the CD line as a result of the
+ * jabber.
+ */
+#define ED_RSR_DFR 0x80
+
+/*
+ * receive ring discriptor
+ *
+ * The National Semiconductor DS8390 Network interface controller uses
+ * the following receive ring headers. The way this works is that the
+ * memory on the interface card is chopped up into 256 bytes blocks.
+ * A contiguous portion of those blocks are marked for receive packets
+ * by setting start and end block #'s in the NIC. For each packet that
+ * is put into the receive ring, one of these headers (4 bytes each) is
+ * tacked onto the front.
+ */
+struct ed_ring {
+ struct edr_status { /* received packet status */
+ u_char rs_prx:1, /* packet received intack */
+ rs_crc:1, /* crc error */
+ rs_fae:1, /* frame alignment error */
+ rs_fo:1, /* fifo overrun */
+ rs_mpa:1, /* packet received intack */
+ rs_phy:1, /* packet received intack */
+ rs_dis:1, /* packet received intack */
+ rs_dfr:1; /* packet received intack */
+ } ed_rcv_status; /* received packet status */
+ u_char next_packet; /* pointer to next packet */
+ u_short count; /* bytes in packet (length + 4) */
+};
+
+/*
+ * Common constants
+ */
+#define ED_PAGE_SIZE 256 /* Size of RAM pages in bytes */
+#define ED_TXBUF_SIZE 6 /* Size of TX buffer in pages */
+
+/*
+ * Vendor types
+ */
+#define ED_VENDOR_WD_SMC 0x00 /* Western Digital/SMC */
+#define ED_VENDOR_3COM 0x01 /* 3Com */
+#define ED_VENDOR_NOVELL 0x02 /* Novell */
+
+/*
+ * Compile-time config flags
+ */
+/*
+ * this sets the default for enabling/disablng the tranceiver
+ */
+#define ED_FLAGS_DISABLE_TRANCEIVER 0x0001
+
+/*
+ * This forces the board to be used in 8/16bit mode even if it
+ * autoconfigs differently
+ */
+#define ED_FLAGS_FORCE_8BIT_MODE 0x0002
+#define ED_FLAGS_FORCE_16BIT_MODE 0x0004
+
+/*
+ * This disables the use of double transmit buffers.
+ */
+#define ED_FLAGS_NO_MULTI_BUFFERING 0x0008
+
+/*
+ * This forces all operations with the NIC memory to use Programmed
+ * I/O (i.e. not via shared memory)
+ */
+#define ED_FLAGS_FORCE_PIO 0x0010
+
+/*
+ * Definitions for Western digital/SMC WD80x3 series ASIC
+ */
+/*
+ * Memory Select Register (MSR)
+ */
+#define ED_WD_MSR 0
+
+/* next three definitions for Toshiba */
+#define ED_WD_MSR_POW 0x02 /* 0 = power save, 1 = normal (R/W) */
+#define ED_WD_MSR_BSY 0x04 /* gate array busy (R) */
+#define ED_WD_MSR_LEN 0x20 /* data bus width, 0 = 16 bits,
+ 1 = 8 bits (R/W) */
+#define ED_WD_MSR_ADDR 0x3f /* Memory decode bits 18-13 */
+#define ED_WD_MSR_MENB 0x40 /* Memory enable */
+#define ED_WD_MSR_RST 0x80 /* Reset board */
+
+/*
+ * Interface Configuration Register (ICR)
+ */
+#define ED_WD_ICR 1
+
+#define ED_WD_ICR_16BIT 0x01 /* 16-bit interface */
+#define ED_WD_ICR_OAR 0x02 /* select register. 0=BIO 1=EAR */
+#define ED_WD_ICR_IR2 0x04 /* high order bit of encoded IRQ */
+#define ED_WD_ICR_MSZ 0x08 /* memory size (0=8k 1=32k) */
+#define ED_WD_ICR_RLA 0x10 /* recall LAN address */
+#define ED_WD_ICR_RX7 0x20 /* recall all but i/o and LAN address */
+#define ED_WD_ICR_RIO 0x40 /* recall i/o address */
+#define ED_WD_ICR_STO 0x80 /* store to non-volatile memory */
+#ifdef TOSH_ETHER
+#define ED_WD_ICR_MEM 0xe0 /* shared mem address A15-A13 (R/W) */
+#define ED_WD_ICR_MSZ1 0x0f /* memory size, 0x08 = 64K, 0x04 = 32K,
+ 0x02 = 16K, 0x01 = 8K */
+ /* 64K can only be used if mem address
+ above 1Mb */
+ /* IAR holds address A23-A16 (R/W) */
+#endif
+
+/*
+ * IO Address Register (IAR)
+ */
+#define ED_WD_IAR 2
+
+/*
+ * EEROM Address Register
+ */
+#define ED_WD_EAR 3
+
+/*
+ * Interrupt Request Register (IRR)
+ */
+#define ED_WD_IRR 4
+
+#define ED_WD_IRR_0WS 0x01 /* use 0 wait-states on 8 bit bus */
+#define ED_WD_IRR_OUT1 0x02 /* WD83C584 pin 1 output */
+#define ED_WD_IRR_OUT2 0x04 /* WD83C584 pin 2 output */
+#define ED_WD_IRR_OUT3 0x08 /* WD83C584 pin 3 output */
+#define ED_WD_IRR_FLASH 0x10 /* Flash RAM is in the ROM socket */
+
+/*
+ * The three bits of the encoded IRQ are decoded as follows:
+ *
+ * IR2 IR1 IR0 IRQ
+ * 0 0 0 2/9
+ * 0 0 1 3
+ * 0 1 0 5
+ * 0 1 1 7
+ * 1 0 0 10
+ * 1 0 1 11
+ * 1 1 0 15
+ * 1 1 1 4
+ */
+#define ED_WD_IRR_IR0 0x20 /* bit 0 of encoded IRQ */
+#define ED_WD_IRR_IR1 0x40 /* bit 1 of encoded IRQ */
+#define ED_WD_IRR_IEN 0x80 /* Interrupt enable */
+
+/*
+ * LA Address Register (LAAR)
+ */
+#define ED_WD_LAAR 5
+
+#define ED_WD_LAAR_ADDRHI 0x1f /* bits 23-19 of RAM address */
+#define ED_WD_LAAR_0WS16 0x20 /* enable 0 wait-states on 16 bit bus */
+#define ED_WD_LAAR_L16EN 0x40 /* enable 16-bit operation */
+#define ED_WD_LAAR_M16EN 0x80 /* enable 16-bit memory access */
+
+/* i/o base offset to station address/card-ID PROM */
+#define ED_WD_PROM 8
+
+/*
+ * 83C790 specific registers
+ */
+/*
+ * Hardware Support Register (HWR) ('790)
+ */
+#define ED_WD790_HWR 4
+
+#define WD_WD790_HWR_NUKE 0x10 /* hardware reset */
+#define ED_WD790_HWR_LPRM 0x40 /* LAN PROM select */
+#define ED_WD790_HWR_SWH 0x80 /* switch register set */
+
+/*
+ * ICR790 Interrupt Control Register for the 83C790
+ */
+#define ED_WD790_ICR 6
+
+#define ED_WD790_ICR_EIL 0x01 /* enable interrupts */
+
+/*
+ * General Control Register (GCR)
+ * Enabled with SWH bit=1 in HWR register
+ */
+#define ED_WD790_GCR 0x0d
+
+#define ED_WD790_GCR_IR0 0x04 /* bit 0 of encoded IRQ */
+#define ED_WD790_GCR_IR1 0x08 /* bit 1 of encoded IRQ */
+#define ED_WD790_GCR_ZWSEN 0x20 /* zero wait state enable */
+#define ED_WD790_GCR_IR2 0x40 /* bit 2 of encoded IRQ */
+/*
+ * The three bits of the encoded IRQ are decoded as follows:
+ *
+ * IR2 IR1 IR0 IRQ
+ * 0 0 0 none
+ * 0 0 1 9
+ * 0 1 0 3
+ * 0 1 1 5
+ * 1 0 0 7
+ * 1 0 1 10
+ * 1 1 0 11
+ * 1 1 1 15
+ */
+
+/* i/o base offset to CARD ID */
+#define ED_WD_CARD_ID ED_WD_PROM+6
+
+/* Board type codes in card ID */
+#define ED_TYPE_WD8003S 0x02
+#define ED_TYPE_WD8003E 0x03
+#define ED_TYPE_WD8013EBT 0x05
+#define ED_TYPE_TOSHIBA1 0x11 /* named PCETA1 */
+#define ED_TYPE_TOSHIBA2 0x12 /* named PCETA2 */
+#define ED_TYPE_TOSHIBA3 0x13 /* named PCETB */
+#define ED_TYPE_TOSHIBA4 0x14 /* named PCETC */
+#define ED_TYPE_WD8003W 0x24
+#define ED_TYPE_WD8003EB 0x25
+#define ED_TYPE_WD8013W 0x26
+#define ED_TYPE_WD8013EP 0x27
+#define ED_TYPE_WD8013WC 0x28
+#define ED_TYPE_WD8013EPC 0x29
+#define ED_TYPE_SMC8216T 0x2a
+#define ED_TYPE_SMC8216C 0x2b
+#define ED_TYPE_WD8013EBP 0x2c
+
+/* Bit definitions in card ID */
+#define ED_WD_REV_MASK 0x1f /* Revision mask */
+#define ED_WD_SOFTCONFIG 0x20 /* Soft config */
+#define ED_WD_LARGERAM 0x40 /* Large RAM */
+#define ED_MICROCHANEL 0x80 /* Microchannel bus (vs. isa) */
+
+/*
+ * Checksum total. All 8 bytes in station address PROM will add up to this
+ */
+#ifdef TOSH_ETHER
+#define ED_WD_ROM_CHECKSUM_TOTAL 0xA5
+#else
+#define ED_WD_ROM_CHECKSUM_TOTAL 0xFF
+#endif
+
+#define ED_WD_NIC_OFFSET 0x10 /* I/O base offset to NIC */
+#define ED_WD_ASIC_OFFSET 0 /* I/O base offset to ASIC */
+#define ED_WD_IO_PORTS 32 /* # of i/o addresses used */
+
+#define ED_WD_PAGE_OFFSET 0 /* page offset for NIC access to mem */
+
+/*
+ * Definitions for 3Com 3c503
+ */
+#define ED_3COM_NIC_OFFSET 0
+#define ED_3COM_ASIC_OFFSET 0x400 /* offset to nic i/o regs */
+
+/*
+ * XXX - The I/O address range is fragmented in the 3c503; this is the
+ * number of regs at iobase.
+ */
+#define ED_3COM_IO_PORTS 16 /* # of i/o addresses used */
+
+/* tx memory starts in second bank on 8bit cards */
+#define ED_3COM_TX_PAGE_OFFSET_8BIT 0x20
+
+/* tx memory starts in first bank on 16bit cards */
+#define ED_3COM_TX_PAGE_OFFSET_16BIT 0x0
+
+/* ...and rx memory starts in second bank */
+#define ED_3COM_RX_PAGE_OFFSET_16BIT 0x20
+
+
+/*
+ * Page Start Register. Must match PSTART in NIC
+ */
+#define ED_3COM_PSTR 0
+
+/*
+ * Page Stop Register. Must match PSTOP in NIC
+ */
+#define ED_3COM_PSPR 1
+
+/*
+ * Drq Timer Register. Determines number of bytes to be transfered during
+ * a DMA burst.
+ */
+#define ED_3COM_DQTR 2
+
+/*
+ * Base Configuration Register. Read-only register which contains the
+ * board-configured I/O base address of the adapter. Bit encoded.
+ */
+#define ED_3COM_BCFR 3
+
+#define ED_3COM_BCFR_2E0 0x01
+#define ED_3COM_BCFR_2A0 0x02
+#define ED_3COM_BCFR_280 0x04
+#define ED_3COM_BCFR_250 0x08
+#define ED_3COM_BCFR_350 0x10
+#define ED_3COM_BCFR_330 0x20
+#define ED_3COM_BCFR_310 0x40
+#define ED_3COM_BCFR_300 0x80
+
+/*
+ * EPROM Configuration Register. Read-only register which contains the
+ * board-configured memory base address. Bit encoded.
+ */
+#define ED_3COM_PCFR 4
+
+#define ED_3COM_PCFR_C8000 0x10
+#define ED_3COM_PCFR_CC000 0x20
+#define ED_3COM_PCFR_D8000 0x40
+#define ED_3COM_PCFR_DC000 0x80
+
+/*
+ * GA Configuration Register. Gate-Array Configuration Register.
+ */
+#define ED_3COM_GACFR 5
+
+/*
+ * mbs2 mbs1 mbs0 start address
+ * 0 0 0 0x0000
+ * 0 0 1 0x2000
+ * 0 1 0 0x4000
+ * 0 1 1 0x6000
+ *
+ * Note that with adapters with only 8K, the setting for 0x2000 must
+ * always be used.
+ */
+#define ED_3COM_GACFR_MBS0 0x01
+#define ED_3COM_GACFR_MBS1 0x02
+#define ED_3COM_GACFR_MBS2 0x04
+
+#define ED_3COM_GACFR_RSEL 0x08 /* enable shared memory */
+#define ED_3COM_GACFR_TEST 0x10 /* for GA testing */
+#define ED_3COM_GACFR_OWS 0x20 /* select 0WS access to GA */
+#define ED_3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */
+#define ED_3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */
+
+/*
+ * Control Register. Miscellaneous control functions.
+ */
+#define ED_3COM_CR 6
+
+#define ED_3COM_CR_RST 0x01 /* Reset GA and NIC */
+#define ED_3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */
+#define ED_3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */
+#define ED_3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */
+#define ED_3COM_CR_SHARE 0x10 /* select interrupt sharing option */
+#define ED_3COM_CR_DBSEL 0x20 /* Double buffer select */
+#define ED_3COM_CR_DDIR 0x40 /* DMA direction select */
+#define ED_3COM_CR_START 0x80 /* Start DMA controller */
+
+/*
+ * Status Register. Miscellaneous status information.
+ */
+#define ED_3COM_STREG 7
+
+#define ED_3COM_STREG_REV 0x07 /* GA revision */
+#define ED_3COM_STREG_DIP 0x08 /* DMA in progress */
+#define ED_3COM_STREG_DTC 0x10 /* DMA terminal count */
+#define ED_3COM_STREG_OFLW 0x20 /* Overflow */
+#define ED_3COM_STREG_UFLW 0x40 /* Underflow */
+#define ED_3COM_STREG_DPRDY 0x80 /* Data port ready */
+
+/*
+ * Interrupt/DMA Configuration Register
+ */
+#define ED_3COM_IDCFR 8
+
+#define ED_3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */
+#define ED_3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */
+#define ED_3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */
+#define ED_3COM_IDCFR_UNUSED 0x08 /* not used */
+#define ED_3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */
+#define ED_3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */
+#define ED_3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */
+#define ED_3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */
+
+/*
+ * DMA Address Register MSB
+ */
+#define ED_3COM_DAMSB 9
+
+/*
+ * DMA Address Register LSB
+ */
+#define ED_3COM_DALSB 0x0a
+
+/*
+ * Vector Pointer Register 2
+ */
+#define ED_3COM_VPTR2 0x0b
+
+/*
+ * Vector Pointer Register 1
+ */
+#define ED_3COM_VPTR1 0x0c
+
+/*
+ * Vector Pointer Register 0
+ */
+#define ED_3COM_VPTR0 0x0d
+
+/*
+ * Register File Access MSB
+ */
+#define ED_3COM_RFMSB 0x0e
+
+/*
+ * Register File Access LSB
+ */
+#define ED_3COM_RFLSB 0x0f
+
+/*
+ * Definitions for Novell NE1000/2000 boards
+ */
+
+/*
+ * Board type codes
+ */
+#define ED_TYPE_NE1000 0x01
+#define ED_TYPE_NE2000 0x02
+
+/*
+ * Register offsets/total
+ */
+#define ED_NOVELL_NIC_OFFSET 0x00
+#define ED_NOVELL_ASIC_OFFSET 0x10
+#define ED_NOVELL_IO_PORTS 32
+
+/*
+ * Remote DMA data register; for reading or writing to the NIC mem
+ * via programmed I/O (offset from ASIC base)
+ */
+#define ED_NOVELL_DATA 0x00
+
+/*
+ * Reset register; reading from this register causes a board reset
+ */
+#define ED_NOVELL_RESET 0x0f
diff --git a/sys/i386/isa/if_el.c b/sys/i386/isa/if_el.c
new file mode 100644
index 0000000..a9033ec
--- /dev/null
+++ b/sys/i386/isa/if_el.c
@@ -0,0 +1,800 @@
+/* Copyright (c) 1994, Matthew E. Kimmel. Permission is hereby granted
+ * to use, copy, modify and distribute this software provided that both
+ * the copyright notice and this permission notice appear in all copies
+ * of the software, derivative works or modified versions, and any
+ * portions thereof.
+ *
+ * Questions, comments, bug reports and fixes to kimmel@cs.umass.edu.
+ */
+/* Except of course for the portions of code lifted from other FreeBSD
+ * drivers (mainly elread, elget and el_ioctl)
+ */
+/* 3COM Etherlink 3C501 device driver for FreeBSD */
+/* Yeah, I know these cards suck, but you can also get them for free
+ * really easily...
+ */
+/* Bugs/possible improvements:
+ * - Does not currently support DMA
+ * - Does not currently support multicasts
+ */
+#include "el.h"
+#if NEL > 0
+#include "bpfilter.h"
+
+#include "param.h"
+#include "systm.h"
+#include "errno.h"
+#include "ioctl.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "syslog.h"
+
+#include "net/if.h"
+#include "net/if_dl.h"
+#include "net/if_types.h"
+
+#ifdef INET
+#include "netinet/in.h"
+#include "netinet/in_systm.h"
+#include "netinet/in_var.h"
+#include "netinet/ip.h"
+#include "netinet/if_ether.h"
+#endif
+
+#ifdef NS
+#include "netns/ns.h"
+#include "netns/ns_if.h"
+#endif
+
+#if NBPFILTER > 0
+#include "net/bpf.h"
+#include "net/bpfdesc.h"
+#endif
+
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/if_elreg.h"
+
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1518
+
+/* For debugging convenience */
+#ifdef EL_DEBUG
+#define dprintf(x) printf x
+#else
+#define dprintf(x)
+#endif
+
+/* el_softc: per line info and status */
+struct el_softc {
+ struct arpcom arpcom; /* Ethernet common */
+ u_short el_base; /* Base I/O addr */
+ caddr_t bpf; /* BPF magic cookie */
+ char el_pktbuf[EL_BUFSIZ]; /* Frame buffer */
+} el_softc[NEL];
+
+/* Prototypes */
+int el_attach(struct isa_device *);
+void el_init(int);
+void elintr(int);
+int el_ioctl(struct ifnet *,int,caddr_t);
+int el_probe(struct isa_device *);
+void el_start(struct ifnet *);
+void el_reset(int,int);
+void el_watchdog(int);
+
+static void el_stop(int);
+static int el_xmit(struct el_softc *,int);
+static inline void elread(struct el_softc *,caddr_t,int);
+static struct mbuf *elget(caddr_t,int,int,struct ifnet *);
+static inline void el_hardreset(int);
+
+/* isa_driver structure for autoconf */
+struct isa_driver eldriver = {
+ el_probe, el_attach, "el"
+};
+
+/* Probe routine. See if the card is there and at the right place. */
+int el_probe(struct isa_device *idev)
+{
+ struct el_softc *sc;
+ u_short base; /* Just for convenience */
+ u_char station_addr[ETHER_ADDR_LEN];
+ int i;
+
+ /* Grab some info for our structure */
+ sc = &el_softc[idev->id_unit];
+ sc->el_base = idev->id_iobase;
+ base = sc->el_base;
+
+ /* First check the base */
+ if((base < 0x280) || (base > 0x3f0)) {
+ printf("el%d: ioaddr must be between 0x280 and 0x3f0\n",
+ idev->id_unit);
+ return(0);
+ }
+
+ /* Now attempt to grab the station address from the PROM
+ * and see if it contains the 3com vendor code.
+ */
+ dprintf(("Probing 3c501 at 0x%x...\n",base));
+
+ /* Reset the board */
+ dprintf(("Resetting board...\n"));
+ outb(base+EL_AC,EL_AC_RESET);
+ DELAY(5);
+ outb(base+EL_AC,0);
+ dprintf(("Reading station address...\n"));
+ /* Now read the address */
+ for(i=0;i<ETHER_ADDR_LEN;i++) {
+ outb(base+EL_GPBL,i);
+ station_addr[i] = inb(base+EL_EAW);
+ }
+ dprintf(("Address is %s\n",ether_sprintf(station_addr)));
+
+ /* If the vendor code is ok, return a 1. We'll assume that
+ * whoever configured this system is right about the IRQ.
+ */
+ if((station_addr[0] != 0x02) || (station_addr[1] != 0x60)
+ || (station_addr[2] != 0x8c)) {
+ dprintf(("Bad vendor code.\n"));
+ return(0);
+ } else {
+ dprintf(("Vendor code ok.\n"));
+ /* Copy the station address into the arpcom structure */
+ bcopy(station_addr,sc->arpcom.ac_enaddr,ETHER_ADDR_LEN);
+ return(1);
+ }
+}
+
+/* Attach the interface to the kernel data structures. By the time
+ * this is called, we know that the card exists at the given I/O address.
+ * We still assume that the IRQ given is correct.
+ */
+int el_attach(struct isa_device *idev)
+{
+ struct el_softc *sc;
+ struct ifnet *ifp;
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+ u_short base;
+ int t;
+
+ dprintf(("Attaching el%d...\n",idev->id_unit));
+
+ /* Get things pointing to the right places. */
+ sc = &el_softc[idev->id_unit];
+ ifp = &sc->arpcom.ac_if;
+ base = sc->el_base;
+
+ /* Now reset the board */
+ dprintf(("Resetting board...\n"));
+ el_hardreset(idev->id_unit);
+
+ /* Initialize ifnet structure */
+ ifp->if_unit = idev->id_unit;
+ ifp->if_name = "el";
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_init = el_init;
+ ifp->if_output = ether_output;
+ ifp->if_start = el_start;
+ ifp->if_ioctl = el_ioctl;
+ ifp->if_reset = el_reset;
+ ifp->if_watchdog = el_watchdog;
+ ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS);
+
+ /* Now we can attach the interface */
+ dprintf(("Attaching interface...\n"));
+ if_attach(ifp);
+
+ /* Put the station address in the ifa address list's AF_LINK
+ * entry, if any.
+ */
+ ifa = ifp->if_addrlist;
+ while ((ifa != NULL) && (ifa->ifa_addr != NULL) &&
+ (ifa->ifa_addr->sa_family != AF_LINK))
+ ifa = ifa->ifa_next;
+ if((ifa != NULL) && (ifa->ifa_addr != NULL)) {
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ sdl->sdl_type = IFT_ETHER;
+ sdl->sdl_alen = ETHER_ADDR_LEN;
+ sdl->sdl_slen = 0;
+ bcopy(sc->arpcom.ac_enaddr,LLADDR(sdl),ETHER_ADDR_LEN);
+ }
+
+ /* Print out some information for the user */
+ printf("el%d: 3c501 address %s\n",idev->id_unit,
+ ether_sprintf(sc->arpcom.ac_enaddr));
+
+ /* Finally, attach to bpf filter if it is present. */
+#if NBPFILTER > 0
+ dprintf(("Attaching to BPF...\n"));
+ bpfattach(&sc->bpf,ifp,DLT_EN10MB,sizeof(struct ether_header));
+#endif
+
+ dprintf(("el_attach() finished.\n"));
+ return(1);
+}
+
+/* This routine resets the interface. */
+void el_reset(int unit,int uban)
+{
+ int s;
+
+ dprintf(("elreset()\n"));
+ s = splimp();
+ el_stop(unit);
+ el_init(unit);
+ splx(s);
+}
+
+static void el_stop(int unit)
+{
+ struct el_softc *sc;
+
+ sc = &el_softc[unit];
+ outb(sc->el_base+EL_AC,0);
+}
+
+/* Do a hardware reset of the 3c501. Do not call until after el_probe()! */
+static inline void el_hardreset(int unit)
+{
+ register struct el_softc *sc;
+ register int base;
+ register int j;
+
+ sc = &el_softc[unit];
+ base = sc->el_base;
+
+ /* First reset the board */
+ outb(base+EL_AC,EL_AC_RESET);
+ DELAY(5);
+ outb(base+EL_AC,0);
+
+ /* Then give it back its ethernet address. Thanks to the mach
+ * source code for this undocumented goodie...
+ */
+ for(j=0;j<ETHER_ADDR_LEN;j++)
+ outb(base+j,sc->arpcom.ac_enaddr[j]);
+}
+
+/* Initialize interface. */
+void el_init(int unit)
+{
+ struct el_softc *sc;
+ struct ifnet *ifp;
+ int s;
+ u_short base;
+
+ /* Set up pointers */
+ sc = &el_softc[unit];
+ ifp = &sc->arpcom.ac_if;
+ base = sc->el_base;
+
+ /* If address not known, do nothing. */
+ if(ifp->if_addrlist == (struct ifaddr *)0)
+ return;
+
+ s = splimp();
+
+ /* First, reset the board. */
+ dprintf(("Resetting board...\n"));
+ el_hardreset(unit);
+
+ /* Configure rx */
+ dprintf(("Configuring rx...\n"));
+ if(ifp->if_flags & IFF_PROMISC)
+ outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
+ else
+ outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
+ outb(base+EL_RBC,0);
+
+ /* Configure TX */
+ dprintf(("Configuring tx...\n"));
+ outb(base+EL_TXC,0);
+
+ /* Start reception */
+ dprintf(("Starting reception...\n"));
+ outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
+
+ /* Set flags appropriately */
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ /* And start output. */
+ el_start(ifp);
+
+ splx(s);
+}
+
+/* Start output on interface. Get datagrams from the queue and output
+ * them, giving the receiver a chance between datagrams. Call only
+ * from splimp or interrupt level!
+ */
+void el_start(struct ifnet *ifp)
+{
+ struct el_softc *sc;
+ u_short base;
+ struct mbuf *m, *m0;
+ int s, i, len, retries, done;
+
+ /* Get things pointing in the right directions */
+ sc = &el_softc[ifp->if_unit];
+ base = sc->el_base;
+
+ dprintf(("el_start()...\n"));
+ s = splimp();
+
+ /* Don't do anything if output is active */
+ if(sc->arpcom.ac_if.if_flags & IFF_OACTIVE)
+ return;
+ sc->arpcom.ac_if.if_flags |= IFF_OACTIVE;
+
+ /* The main loop. They warned me against endless loops, but
+ * would I listen? NOOO....
+ */
+ while(1) {
+ /* Dequeue the next datagram */
+ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd,m0);
+
+ /* If there's nothing to send, return. */
+ if(m0 == NULL) {
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+ splx(s);
+ return;
+ }
+
+ /* Disable the receiver */
+ outb(base+EL_AC,EL_AC_HOST);
+ outb(base+EL_RBC,0);
+
+ /* Copy the datagram to the buffer. */
+ len = 0;
+ for(m = m0; m != NULL; m = m->m_next) {
+ if(m->m_len == 0)
+ continue;
+ bcopy(mtod(m,caddr_t),sc->el_pktbuf+len,m->m_len);
+ len += m->m_len;
+ }
+ m_freem(m0);
+
+ len = max(len,ETHER_MIN_LEN);
+
+ /* Give the packet to the bpf, if any */
+#if NBPFILTER > 0
+ if(sc->bpf)
+ bpf_tap(sc->bpf,sc->el_pktbuf,len);
+#endif
+
+ /* Transfer datagram to board */
+ dprintf(("el: xfr pkt length=%d...\n",len));
+ i = EL_BUFSIZ - len;
+ outb(base+EL_GPBL,(i & 0xff));
+ outb(base+EL_GPBH,((i>>8)&0xff));
+ outsb(base+EL_BUF,sc->el_pktbuf,len);
+
+ /* Now transmit the datagram */
+ retries=0;
+ done=0;
+ while(!done) {
+ if(el_xmit(sc,len)) { /* Something went wrong */
+ done = -1;
+ break;
+ }
+ /* Check out status */
+ i = inb(base+EL_TXS);
+ dprintf(("tx status=0x%x\n",i));
+ if(!(i & EL_TXS_READY)) {
+ dprintf(("el: err txs=%x\n",i));
+ sc->arpcom.ac_if.if_oerrors++;
+ if(i & (EL_TXS_COLL|EL_TXS_COLL16)) {
+ if((!(i & EL_TXC_DCOLL16)) && retries < 15) {
+ retries++;
+ outb(base+EL_AC,EL_AC_HOST);
+ }
+ }
+ else
+ done = 1;
+ }
+ else {
+ sc->arpcom.ac_if.if_opackets++;
+ done = 1;
+ }
+ }
+ if(done == -1) /* Packet not transmitted */
+ continue;
+
+ /* Now give the card a chance to receive.
+ * Gotta love 3c501s...
+ */
+ (void)inb(base+EL_AS);
+ outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
+ splx(s);
+ /* Interrupt here */
+ s = splimp();
+ }
+}
+
+/* This function actually attempts to transmit a datagram downloaded
+ * to the board. Call at splimp or interrupt, after downloading data!
+ * Returns 0 on success, non-0 on failure
+ */
+static int el_xmit(struct el_softc *sc,int len)
+{
+ int gpl;
+ int i;
+
+ gpl = EL_BUFSIZ - len;
+ dprintf(("el: xmit..."));
+ outb((sc->el_base)+EL_GPBL,(gpl & 0xff));
+ outb((sc->el_base)+EL_GPBH,((gpl>>8)&0xff));
+ outb((sc->el_base)+EL_AC,EL_AC_TXFRX);
+ i = 20000;
+ while((inb((sc->el_base)+EL_AS) & EL_AS_TXBUSY) && (i>0))
+ i--;
+ if(i == 0) {
+ dprintf(("tx not ready\n"));
+ sc->arpcom.ac_if.if_oerrors++;
+ return(-1);
+ }
+ dprintf(("%d cycles.\n",(20000-i)));
+ return(0);
+}
+
+/* controller interrupt */
+void elintr(int unit)
+{
+ register struct el_softc *sc;
+ register base;
+ int stat, rxstat, len, done;
+
+ /* Get things pointing properly */
+ sc = &el_softc[unit];
+ base = sc->el_base;
+
+ dprintf(("elintr: "));
+
+ /* Check board status */
+ stat = inb(base+EL_AS);
+ if(stat & EL_AS_RXBUSY) {
+ (void)inb(base+EL_RXC);
+ outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
+ return;
+ }
+
+ done = 0;
+ while(!done) {
+ rxstat = inb(base+EL_RXS);
+ if(rxstat & EL_RXS_STALE) {
+ (void)inb(base+EL_RXC);
+ outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
+ return;
+ }
+
+ /* If there's an overflow, reinit the board. */
+ if(!(rxstat & EL_RXS_NOFLOW)) {
+ dprintf(("overflow.\n"));
+ el_hardreset(unit);
+ /* Put board back into receive mode */
+ if(sc->arpcom.ac_if.if_flags & IFF_PROMISC)
+ outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
+ else
+ outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
+ (void)inb(base+EL_AS);
+ outb(base+EL_RBC,0);
+ (void)inb(base+EL_RXC);
+ outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
+ return;
+ }
+
+ /* Incoming packet */
+ len = inb(base+EL_RBL);
+ len |= inb(base+EL_RBH) << 8;
+ dprintf(("receive len=%d rxstat=%x ",len,rxstat));
+ outb(base+EL_AC,EL_AC_HOST);
+
+ /* If packet too short or too long, restore rx mode and return
+ */
+ if((len <= sizeof(struct ether_header)) || (len > ETHER_MAX_LEN)) {
+ if(sc->arpcom.ac_if.if_flags & IFF_PROMISC)
+ outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
+ else
+ outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
+ (void)inb(base+EL_AS);
+ outb(base+EL_RBC,0);
+ (void)inb(base+EL_RXC);
+ outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
+ return;
+ }
+
+ sc->arpcom.ac_if.if_ipackets++;
+
+ /* Copy the data into our buffer */
+ outb(base+EL_GPBL,0);
+ outb(base+EL_GPBH,0);
+ insb(base+EL_BUF,sc->el_pktbuf,len);
+ outb(base+EL_RBC,0);
+ outb(base+EL_AC,EL_AC_RX);
+ dprintf(("%s-->",ether_sprintf(sc->el_pktbuf+6)));
+ dprintf(("%s\n",ether_sprintf(sc->el_pktbuf)));
+
+ /* Pass data up to upper levels */
+ len -= sizeof(struct ether_header);
+ elread(sc,(caddr_t)(sc->el_pktbuf),len);
+
+ /* Is there another packet? */
+ stat = inb(base+EL_AS);
+
+ /* If so, do it all again (i.e. don't set done to 1) */
+ if(!(stat & EL_AS_RXBUSY))
+ dprintf(("<rescan> "));
+ else
+ done = 1;
+ }
+
+ (void)inb(base+EL_RXC);
+ outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
+ return;
+}
+
+/* Pass a packet up to the higher levels. Deal with trailer protocol. */
+static inline void elread(struct el_softc *sc,caddr_t buf,int len)
+{
+ register struct ether_header *eh;
+ struct mbuf *m;
+ int off, resid;
+
+ /* Deal with trailer protocol: if type is trailer type
+ * get true type from first 16-bit word past data.
+ * Remember that type was trailer by setting off.
+ */
+ eh = (struct ether_header *)buf;
+ eh->ether_type = ntohs((u_short)eh->ether_type);
+#define eldataaddr(eh,off,type) ((type)(((caddr_t)((eh)+1)+(off))))
+ if(eh->ether_type >= ETHERTYPE_TRAIL &&
+ eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
+ off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
+ if(off >= ETHERMTU)
+ return;
+ eh->ether_type = ntohs(*eldataaddr(eh,off,u_short *));
+ resid = ntohs(*(eldataaddr(eh,off+2,u_short *)));
+ if((off+resid) > len)
+ return;
+ len = off + resid;
+ }
+ else
+ off = 0;
+
+ if(len <= 0)
+ return;
+
+#if NBPFILTER > 0
+ /*
+ * Check if there's a bpf filter listening on this interface.
+ * If so, hand off the raw packet to bpf, which must deal with
+ * trailers in its own way.
+ */
+ if(sc->bpf) {
+ eh->ether_type = htons((u_short)eh->ether_type);
+ bpf_tap(sc->bpf,buf,len+sizeof(struct ether_header));
+ eh->ether_type = ntohs((u_short)eh->ether_type);
+
+ /*
+ * Note that the interface cannot be in promiscuous mode if
+ * there are no bpf listeners. And if el are in promiscuous
+ * mode, el have to check if this packet is really ours.
+ *
+ * This test does not support multicasts.
+ */
+ if((sc->arpcom.ac_if.if_flags & IFF_PROMISC)
+ && bcmp(eh->ether_dhost,sc->arpcom.ac_enaddr,
+ sizeof(eh->ether_dhost)) != 0
+ && bcmp(eh->ether_dhost,etherbroadcastaddr,
+ sizeof(eh->ether_dhost)) != 0)
+ return;
+ }
+#endif
+
+ /*
+ * Pull packet off interface. Off is nonzero if packet
+ * has trailing header; neget will then force this header
+ * information to be at the front, but we still have to drop
+ * the type and length which are at the front of any trailer data.
+ */
+ m = elget(buf,len,off,&sc->arpcom.ac_if);
+ if(m == 0)
+ return;
+
+ ether_input(&sc->arpcom.ac_if,eh,m);
+}
+
+/*
+ * Pull read data off a interface.
+ * Len is length of data, with local net header stripped.
+ * Off is non-zero if a trailer protocol was used, and
+ * gives the offset of the trailer information.
+ * We copy the trailer information and then all the normal
+ * data into mbufs. When full cluster sized units are present
+ * we copy into clusters.
+ */
+struct mbuf *
+elget(buf, totlen, off0, ifp)
+ caddr_t buf;
+ int totlen, off0;
+ struct ifnet *ifp;
+{
+ struct mbuf *top, **mp, *m, *p;
+ int off = off0, len;
+ register caddr_t cp = buf;
+ char *epkt;
+
+ buf += sizeof(struct ether_header);
+ cp = buf;
+ epkt = cp + totlen;
+
+
+ if (off) {
+ cp += off + 2 * sizeof(u_short);
+ totlen -= 2 * sizeof(u_short);
+ }
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ return (0);
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = totlen;
+ m->m_len = MHLEN;
+ top = 0;
+ mp = &top;
+ while (totlen > 0) {
+ if (top) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == 0) {
+ m_freem(top);
+ return (0);
+ }
+ m->m_len = MLEN;
+ }
+ len = min(totlen, epkt - cp);
+ if (len >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if (m->m_flags & M_EXT)
+ m->m_len = len = min(len, MCLBYTES);
+ else
+ len = m->m_len;
+ } else {
+ /*
+ * Place initial small packet/header at end of mbuf.
+ */
+ if (len < m->m_len) {
+ if (top == 0 && len + max_linkhdr <= m->m_len)
+ m->m_data += max_linkhdr;
+ m->m_len = len;
+ } else
+ len = m->m_len;
+ }
+ bcopy(cp, mtod(m, caddr_t), (unsigned)len);
+ cp += len;
+ *mp = m;
+ mp = &m->m_next;
+ totlen -= len;
+ if (cp == epkt)
+ cp = buf;
+ }
+ return (top);
+}
+
+/*
+ * Process an ioctl request. This code needs some work - it looks
+ * pretty ugly.
+ */
+int
+el_ioctl(ifp, command, data)
+ register struct ifnet *ifp;
+ int command;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *)data;
+ struct el_softc *sc = &el_softc[ifp->if_unit];
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s, error = 0;
+
+ s = splimp();
+
+ switch (command) {
+
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ el_init(ifp->if_unit); /* before arpwhohas */
+ /*
+ * See if another station has *our* IP address.
+ * i.e.: There is an address conflict! If a
+ * conflict exists, a message is sent to the
+ * console.
+ */
+ ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
+ break;
+#endif
+#ifdef NS
+ /*
+ * XXX - This code is probably wrong
+ */
+ case AF_NS:
+ {
+ register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+ if (ns_nullhost(*ina))
+ ina->x_host =
+ *(union ns_host *)(sc->arpcom.ac_enaddr);
+ else {
+ /*
+ *
+ */
+ bcopy((caddr_t)ina->x_host.c_host,
+ (caddr_t)sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr));
+ }
+ /*
+ * Set new address
+ */
+ el_init(ifp->if_unit);
+ break;
+ }
+#endif
+ default:
+ el_init(ifp->if_unit);
+ break;
+ }
+ break;
+
+ case SIOCGIFADDR:
+ {
+ struct sockaddr *sa;
+ sa = (struct sockaddr *)&ifr->ifr_data;
+ bcopy((caddr_t)sc->arpcom.ac_enaddr,
+ (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
+ }
+ break;
+
+ case SIOCSIFFLAGS:
+ /*
+ * If interface is marked down and it is running, then stop it
+ */
+ if (((ifp->if_flags & IFF_UP) == 0) &&
+ (ifp->if_flags & IFF_RUNNING)) {
+ el_stop(ifp->if_unit);
+ ifp->if_flags &= ~IFF_RUNNING;
+ } else {
+ /*
+ * If interface is marked up and it is stopped, then start it
+ */
+ if ((ifp->if_flags & IFF_UP) &&
+ ((ifp->if_flags & IFF_RUNNING) == 0))
+ el_init(ifp->if_unit);
+ }
+
+ default:
+ error = EINVAL;
+ }
+ (void) splx(s);
+ return (error);
+}
+
+/* Device timeout routine */
+void el_watchdog(int unit)
+{
+ struct el_softc *sc;
+
+ sc = &el_softc[unit];
+
+ log(LOG_ERR,"el%d: device timeout\n",unit);
+ sc->arpcom.ac_if.if_oerrors++;
+ el_reset(unit,0);
+}
+#endif
diff --git a/sys/i386/isa/if_elreg.h b/sys/i386/isa/if_elreg.h
new file mode 100644
index 0000000..806d6ff
--- /dev/null
+++ b/sys/i386/isa/if_elreg.h
@@ -0,0 +1,76 @@
+/* Copyright (c) 1994, Matthew E. Kimmel. Permission is hereby granted
+ * to use, copy, modify and distribute this software provided that both
+ * the copyright notice and this permission notice appear in all copies
+ * of the software, derivative works or modified versions, and any
+ * portions thereof.
+ */
+/* 3COM Etherlink 3C501 Register Definitions */
+
+/* I/O Ports */
+#define EL_RXS 0x6 /* Receive status register */
+#define EL_RXC 0x6 /* Receive command register */
+#define EL_TXS 0x7 /* Transmit status register */
+#define EL_TXC 0x7 /* Transmit command register */
+#define EL_GPBL 0x8 /* GP buffer ptr low byte */
+#define EL_GPBH 0x9 /* GP buffer ptr high byte */
+#define EL_RBL 0xa /* Receive buffer ptr low byte */
+#define EL_RBC 0xa /* Receive buffer clear */
+#define EL_RBH 0xb /* Receive buffer ptr high byte */
+#define EL_EAW 0xc /* Ethernet address window */
+#define EL_AS 0xe /* Auxillary status register */
+#define EL_AC 0xe /* Auxillary command register */
+#define EL_BUF 0xf /* Data buffer */
+
+/* Receive status register bits */
+#define EL_RXS_OFLOW 0x01 /* Overflow error */
+#define EL_RXS_FCS 0x02 /* FCS error */
+#define EL_RXS_DRIB 0x04 /* Dribble error */
+#define EL_RXS_SHORT 0x08 /* Short frame */
+#define EL_RXS_NOFLOW 0x10 /* No overflow */
+#define EL_RXS_GOOD 0x20 /* Received good frame */
+#define EL_RXS_STALE 0x80 /* Stale receive status */
+
+/* Receive command register bits */
+#define EL_RXC_DISABLE 0x00 /* Receiver disabled */
+#define EL_RXC_DOFLOW 0x01 /* Detect overflow */
+#define EL_RXC_DFCS 0x02 /* Detect FCS errs */
+#define EL_RXC_DDRIB 0x04 /* Detect dribble errors */
+#define EL_RXC_DSHORT 0x08 /* Detect short frames */
+#define EL_RXC_DNOFLOW 0x10 /* Detect frames w/o overflow ??? */
+#define EL_RXC_AGF 0x20 /* Accept Good Frames */
+#define EL_RXC_PROMISC 0x40 /* Promiscuous mode */
+#define EL_RXC_ABROAD 0x80 /* Accept address, broadcast */
+#define EL_RXC_AMULTI 0xc0 /* Accept address, multicast */
+
+/* Transmit status register bits */
+#define EL_TXS_UFLOW 0x01 /* Underflow */
+#define EL_TXS_COLL 0x02 /* Collision */
+#define EL_TXS_COLL16 0x04 /* Collision 16 */
+#define EL_TXS_READY 0x08 /* Ready for new frame */
+
+/* Transmit command register bits */
+#define EL_TXC_DUFLOW 0x01 /* Detect underflow */
+#define EL_TXC_DCOLL 0x02 /* Detect collisions */
+#define EL_TXC_DCOLL16 0x04 /* Detect collision 16 */
+#define EL_TXC_DSUCCESS 0x08 /* Detect success */
+
+/* Auxillary status register bits */
+#define EL_AS_RXBUSY 0x01 /* Receive busy */
+#define EL_AS_DMADONE 0x10 /* DMA finished */
+#define EL_AS_TXBUSY 0x80 /* Transmit busy */
+
+/* Auxillary command register bits */
+#define EL_AC_HOST 0x00 /* System bus can access buffer */
+#define EL_AC_IRQE 0x01 /* IRQ enable */
+#define EL_AC_TXBAD 0x02 /* Transmit frames with bad FCS */
+#define EL_AC_TXFRX 0x04 /* Transmit followed by receive */
+#define EL_AC_RX 0x08 /* Receive */
+#define EL_AC_LB 0x0c /* Loopback */
+#define EL_AC_DRQ 0x20 /* DMA request */
+#define EL_AC_RIDE 0x40 /* DRQ and IRQ enabled */
+#define EL_AC_RESET 0x80 /* Reset */
+
+/* Packet buffer size */
+#define EL_BUFSIZ 2048
+
+#define ETHER_ADDR_LEN 6
diff --git a/sys/i386/isa/if_ep.c b/sys/i386/isa/if_ep.c
new file mode 100644
index 0000000..b47f829
--- /dev/null
+++ b/sys/i386/isa/if_ep.c
@@ -0,0 +1,993 @@
+/*
+ * Copyright (c) 1993 Herb Peyerl <hpeyerl@novatel.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * From: if_ep.c,v 1.9 1994/01/25 10:46:29 deraadt Exp $
+ * $Id: if_ep.c,v 1.8 1994/03/15 01:58:22 wollman Exp $
+ */
+
+#include "ep.h"
+#if NEP > 0
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#if defined(__FreeBSD__)
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#endif
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#if defined(__NetBSD__)
+#include <sys/select.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#include <machine/pio.h>
+
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/icu.h>
+#include <i386/isa/if_epreg.h>
+
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1518
+#define ETHER_ADDR_LEN 6
+
+/*
+ * Ethernet software status per interface.
+ */
+struct ep_softc {
+ struct arpcom arpcom; /* Ethernet common part */
+ short ep_io_addr; /* i/o bus address */
+ char ep_connectors; /* Connectors on this card. */
+#define MAX_MBS 8 /* # of mbufs we keep around */
+ struct mbuf *mb[MAX_MBS]; /* spare mbuf storage. */
+ int next_mb; /* Which mbuf to use next. */
+ int last_mb; /* Last mbuf. */
+ int tx_start_thresh; /* Current TX_start_thresh. */
+ caddr_t bpf; /* BPF "magic cookie" */
+ char bus32bit; /* 32bit access possible */
+} ep_softc[NEP];
+
+static int epprobe __P((struct isa_device *));
+static int epattach __P((struct isa_device *));
+static int epioctl __P((struct ifnet * ifp, int, caddr_t));
+
+void epinit __P((int));
+void epintr __P((int));
+void epmbuffill __P((caddr_t));
+void epmbufempty __P((struct ep_softc *));
+void epread __P((struct ep_softc *));
+void epreset __P((int));
+void epstart __P((struct ifnet *));
+void epstop __P((int));
+void epwatchdog __P((int));
+
+struct isa_driver epdriver = {
+ epprobe,
+ epattach,
+ "ep"
+};
+
+static int send_ID_sequence __P((u_short));
+static u_short get_eeprom_data __P((int, int));
+static int is_eeprom_busy __P((struct isa_device *));
+
+/*
+ * Rudimentary support for multiple cards is here but is not
+ * currently handled. In the future we will have to add code
+ * for tagging the cards for later activation. We wanna do something
+ * about the id_port. We're limited due to current config procedure.
+ * Magnum config holds promise of a fix but we'll have to wait a bit.
+ */
+int
+epprobe(is)
+ struct isa_device *is;
+{
+ struct ep_softc *sc = &ep_softc[is->id_unit];
+ u_short k;
+ int id_port = 0x100; /* XXX */
+
+ outw(BASE + EP_COMMAND, GLOBAL_RESET);
+ DELAY(1000);
+ outb(id_port, 0xc0); /* Global reset to id_port. */
+ DELAY(1000);
+ send_ID_sequence(id_port);
+ DELAY(1000);
+
+ /*
+ * MFG_ID should have 0x6d50.
+ * PROD_ID should be 0x9[0-f]50
+ */
+ k = get_eeprom_data(id_port, EEPROM_MFG_ID);
+ if (k != MFG_ID)
+ return (0);
+ k = get_eeprom_data(id_port, EEPROM_PROD_ID);
+ if ((k & 0xf0ff) != (PROD_ID & 0xf0ff))
+ return (0);
+
+ k = get_eeprom_data(id_port, EEPROM_ADDR_CFG); /* get addr cfg */
+ k = (k & 0x1f) * 0x10 + 0x200; /* decode base addr. */
+ if (k != (u_short)is->id_iobase)
+ return (0);
+
+ k = get_eeprom_data(id_port, EEPROM_RESOURCE_CFG);
+ k >>= 12;
+ if (is->id_irq != (1 << ((k == 2) ? 9 : k)))
+ return (0);
+
+ outb(id_port, ACTIVATE_ADAPTER_TO_CONFIG);
+
+ return (0x10); /* 16 bytes of I/O space used. */
+}
+
+static int
+epattach(is)
+ struct isa_device *is;
+{
+ struct ep_softc *sc = &ep_softc[is->id_unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ u_short i;
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+
+ sc->ep_io_addr = is->id_iobase;
+
+ printf("ep%d: ", is->id_unit);
+
+ sc->ep_connectors = 0;
+ i = inw(is->id_iobase + EP_W0_CONFIG_CTRL);
+ if (i & IS_AUI) {
+ printf("aui");
+ sc->ep_connectors |= AUI;
+ }
+ if (i & IS_BNC) {
+ if (sc->ep_connectors)
+ printf("/");
+ printf("bnc");
+ sc->ep_connectors |= BNC;
+ }
+ if (i & IS_UTP) {
+ if (sc->ep_connectors)
+ printf("/");
+ printf("utp");
+ sc->ep_connectors |= UTP;
+ }
+ if (!sc->ep_connectors)
+ printf("no connectors!");
+
+ /*
+ * Read the station address from the eeprom
+ */
+ for (i = 0; i < 3; i++) {
+ u_short *p;
+ GO_WINDOW(0);
+ if (is_eeprom_busy(is))
+ return(0);
+ outw(BASE + EP_W0_EEPROM_COMMAND, READ_EEPROM | i);
+ if (is_eeprom_busy(is))
+ return(0);
+ p =(u_short *)&sc->arpcom.ac_enaddr[i*2];
+ *p = htons(inw(BASE + EP_W0_EEPROM_DATA));
+ GO_WINDOW(2);
+ outw(BASE + EP_W2_ADDR_0 + (i * 2), ntohs(*p));
+ }
+ printf(" address %s\n", ether_sprintf(sc->arpcom.ac_enaddr));
+
+ ifp->if_unit = is->id_unit;
+ ifp->if_name = "ep";
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
+ ifp->if_init = epinit;
+ ifp->if_output = ether_output;
+ ifp->if_start = epstart;
+ ifp->if_ioctl = epioctl;
+ ifp->if_watchdog = epwatchdog;
+
+ if_attach(ifp);
+
+ /*
+ * Fill the hardware address into ifa_addr if we find an
+ * AF_LINK entry. We need to do this so bpf's can get the hardware
+ * addr of this card. netstat likes this too!
+ */
+ ifa = ifp->if_addrlist;
+ while ((ifa != 0) && (ifa->ifa_addr != 0) &&
+ (ifa->ifa_addr->sa_family != AF_LINK))
+ ifa = ifa->ifa_next;
+
+ if ((ifa != 0) && (ifa->ifa_addr != 0)) {
+ sdl = (struct sockaddr_dl *) ifa->ifa_addr;
+ sdl->sdl_type = IFT_ETHER;
+ sdl->sdl_alen = ETHER_ADDR_LEN;
+ sdl->sdl_slen = 0;
+ bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN);
+ }
+#if NBPFILTER > 0
+ bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+ return 1;
+}
+
+
+/*
+ * The order in here seems important. Otherwise we may not receive
+ * interrupts. ?!
+ */
+void
+epinit(unit)
+ int unit;
+{
+ register struct ep_softc *sc = &ep_softc[unit];
+ register struct ifnet *ifp = &sc->arpcom.ac_if;
+ int s, i;
+
+ if (ifp->if_addrlist == (struct ifaddr *) 0)
+ return;
+
+ s = splimp();
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ ;
+
+ GO_WINDOW(0);
+
+ /* Disable the card */
+ outw(BASE + EP_W0_CONFIG_CTRL, 0);
+
+ /* Enable the card */
+ outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ);
+
+ GO_WINDOW(2);
+
+ /* Reload the ether_addr. */
+ for (i = 0; i < 6; i++)
+ outb(BASE + EP_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]);
+
+ outw(BASE + EP_COMMAND, RX_RESET);
+ outw(BASE + EP_COMMAND, TX_RESET);
+
+ /* Window 1 is operating window */
+ GO_WINDOW(1);
+ for (i = 0; i < 31; i++)
+ inb(BASE + EP_W1_TX_STATUS);
+
+ /* get rid of stray intr's */
+ outw(BASE + EP_COMMAND, ACK_INTR | 0xff);
+
+ outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
+ S_TX_COMPLETE | S_TX_AVAIL);
+ outw(BASE + EP_COMMAND, SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
+ S_TX_COMPLETE | S_TX_AVAIL);
+
+ outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL |
+ FIL_GROUP | FIL_BRDCST);
+
+ /*
+ * you can `ifconfig (link0|-link0) ep0' to get the following
+ * behaviour:
+ * -link0 disable AUI/UTP. enable BNC.
+ * link0 disable BNC. enable AUI. if the card has a UTP
+ * connector, that is enabled too. not sure, but it
+ * seems you have to be careful to not plug things
+ * into both AUI & UTP.
+ */
+#if defined(__NetBSD__)
+ if (!(ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & BNC)) {
+#else
+ if (!(ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & BNC)) {
+#endif
+ outw(BASE + EP_COMMAND, START_TRANSCEIVER);
+ DELAY(1000);
+ }
+#if defined(__NetBSD__)
+ if ((ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & UTP)) {
+#else
+ if ((ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & UTP)) {
+#endif
+ GO_WINDOW(4);
+ outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP);
+ GO_WINDOW(1);
+ }
+
+ outw(BASE + EP_COMMAND, RX_ENABLE);
+ outw(BASE + EP_COMMAND, TX_ENABLE);
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE; /* just in case */
+ sc->tx_start_thresh = 20; /* probably a good starting point. */
+ /*
+ * Store up a bunch of mbuf's for use later. (MAX_MBS). First we
+ * free up any that we had in case we're being called from intr or
+ * somewhere else.
+ */
+ sc->last_mb = 0;
+ sc->next_mb = 0;
+ epmbuffill((caddr_t)sc, 0);
+
+ epstart(ifp);
+
+ splx(s);
+}
+
+static const char padmap[] = {0, 3, 2, 1};
+
+void
+epstart(ifp)
+ struct ifnet *ifp;
+{
+ register struct ep_softc *sc = &ep_softc[ifp->if_unit];
+ struct mbuf *m, *top;
+ int s, len, pad;
+
+ s = splimp();
+ if (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) {
+ splx(s);
+ return;
+ }
+
+startagain:
+ /* Sneak a peek at the next packet */
+ m = sc->arpcom.ac_if.if_snd.ifq_head;
+ if (m == 0) {
+ splx(s);
+ return;
+ }
+#if 0
+ len = m->m_pkthdr.len;
+#else
+ for (len = 0, top = m; m; m = m->m_next)
+ len += m->m_len;
+#endif
+
+ pad = padmap[len & 3];
+
+ /*
+ * The 3c509 automatically pads short packets to minimum ethernet
+ * length, but we drop packets that are too large. Perhaps we should
+ * truncate them instead?
+ */
+ if (len + pad > ETHER_MAX_LEN) {
+ /* packet is obviously too large: toss it */
+ ++sc->arpcom.ac_if.if_oerrors;
+ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m);
+ m_freem(m);
+ goto readcheck;
+ }
+
+ if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) {
+ /* no room in FIFO */
+ outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4));
+ sc->arpcom.ac_if.if_flags |= IFF_OACTIVE;
+ splx(s);
+ return;
+ }
+ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m);
+ if (m == 0) { /* not really needed */
+ splx(s);
+ return;
+ }
+ outw(BASE + EP_COMMAND, SET_TX_START_THRESH |
+ (len / 4 + sc->tx_start_thresh));
+
+ outw(BASE + EP_W1_TX_PIO_WR_1, len);
+ outw(BASE + EP_W1_TX_PIO_WR_1, 0xffff); /* Second dword meaningless */
+
+ for (top = m; m != 0; m = m->m_next) {
+ if (sc->bus32bit) {
+ outsl(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t),
+ m->m_len/4);
+ if (m->m_len & 3)
+ outsb(BASE + EP_W1_TX_PIO_WR_1,
+ mtod(m, caddr_t) + m->m_len/4,
+ m->m_len & 3);
+ } else {
+ outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len/2);
+ if (m->m_len & 1)
+ outb(BASE + EP_W1_TX_PIO_WR_1,
+ *(mtod(m, caddr_t) + m->m_len - 1));
+ }
+ }
+ while (pad--)
+ outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */
+
+#if NBPFILTER > 0
+ if (sc->bpf) {
+ u_short etype;
+ int off, datasize, resid;
+ struct ether_header *eh;
+ struct trailer_header {
+ u_short ether_type;
+ u_short ether_residual;
+ } trailer_header;
+ char ether_packet[ETHER_MAX_LEN];
+ char *ep;
+
+ ep = ether_packet;
+
+ /*
+ * We handle trailers below:
+ * Copy ether header first, then residual data,
+ * then data. Put all this in a temporary buffer
+ * 'ether_packet' and send off to bpf. Since the
+ * system has generated this packet, we assume
+ * that all of the offsets in the packet are
+ * correct; if they're not, the system will almost
+ * certainly crash in m_copydata.
+ * We make no assumptions about how the data is
+ * arranged in the mbuf chain (i.e. how much
+ * data is in each mbuf, if mbuf clusters are
+ * used, etc.), which is why we use m_copydata
+ * to get the ether header rather than assume
+ * that this is located in the first mbuf.
+ */
+ /* copy ether header */
+ m_copydata(top, 0, sizeof(struct ether_header), ep);
+ eh = (struct ether_header *) ep;
+ ep += sizeof(struct ether_header);
+ eh->ether_type = etype = ntohs(eh->ether_type);
+ if (etype >= ETHERTYPE_TRAIL &&
+ etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) {
+ datasize = ((etype - ETHERTYPE_TRAIL) << 9);
+ off = datasize + sizeof(struct ether_header);
+
+ /* copy trailer_header into a data structure */
+ m_copydata(top, off, sizeof(struct trailer_header),
+ (caddr_t)&trailer_header.ether_type);
+
+ /* copy residual data */
+ resid = trailer_header.ether_residual -
+ sizeof(struct trailer_header);
+ resid = ntohs(resid);
+ m_copydata(top, off + sizeof(struct trailer_header),
+ resid, ep);
+ ep += resid;
+
+ /* copy data */
+ m_copydata(top, sizeof(struct ether_header),
+ datasize, ep);
+ ep += datasize;
+
+ /* restore original ether packet type */
+ eh->ether_type = trailer_header.ether_type;
+
+ bpf_tap(sc->bpf, ether_packet, ep - ether_packet);
+ } else
+ bpf_mtap(sc->bpf, top);
+ }
+#endif
+
+ m_freem(top);
+ ++sc->arpcom.ac_if.if_opackets;
+
+ /*
+ * Is another packet coming in? We don't want to overflow the
+ * tiny RX fifo.
+ */
+readcheck:
+ if (inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK) {
+ splx(s);
+ return;
+ }
+ goto startagain;
+}
+
+void
+epintr(unit)
+ int unit;
+{
+ int status, i;
+ register struct ep_softc *sc = &ep_softc[unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+
+ status = 0;
+checkintr:
+ status = inw(BASE + EP_STATUS) &
+ (S_TX_COMPLETE | S_TX_AVAIL | S_RX_COMPLETE | S_CARD_FAILURE);
+ if (status == 0) {
+ /* No interrupts. */
+ outw(BASE + EP_COMMAND, C_INTR_LATCH);
+ return;
+ }
+ /* important that we do this first. */
+ outw(BASE + EP_COMMAND, ACK_INTR | status);
+
+ if (status & S_TX_AVAIL) {
+ status &= ~S_TX_AVAIL;
+ inw(BASE + EP_W1_FREE_TX);
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+ epstart(&sc->arpcom.ac_if);
+ }
+ if (status & S_RX_COMPLETE) {
+ status &= ~S_RX_COMPLETE;
+ epread(sc);
+ }
+ if (status & S_CARD_FAILURE) {
+ printf("ep%d: reset (status: %x)\n", unit, status);
+ outw(BASE + EP_COMMAND, C_INTR_LATCH);
+ epinit(unit);
+ return;
+ }
+ if (status & S_TX_COMPLETE) {
+ status &= ~S_TX_COMPLETE;
+ /*
+ * We need to read TX_STATUS until we get a 0 status in
+ * order to turn off the interrupt flag.
+ */
+ while ((i = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE) {
+ outw(BASE + EP_W1_TX_STATUS, 0x0);
+ if (i & (TXS_MAX_COLLISION | TXS_JABBER | TXS_UNDERRUN)) {
+ if (i & TXS_MAX_COLLISION)
+ ++sc->arpcom.ac_if.if_collisions;
+ if (i & (TXS_JABBER | TXS_UNDERRUN)) {
+ outw(BASE + EP_COMMAND, TX_RESET);
+ if (i & TXS_UNDERRUN) {
+ if (sc->tx_start_thresh < ETHER_MAX_LEN) {
+ sc->tx_start_thresh += 20;
+ outw(BASE + EP_COMMAND,
+ SET_TX_START_THRESH |
+ sc->tx_start_thresh);
+ }
+ }
+ }
+ outw(BASE + EP_COMMAND, TX_ENABLE);
+ ++sc->arpcom.ac_if.if_oerrors;
+ }
+ }
+ epstart(ifp);
+ }
+ goto checkintr;
+}
+
+void
+epread(sc)
+ register struct ep_softc *sc;
+{
+ struct ether_header *eh;
+ struct mbuf *mcur, *m, *m0, *top;
+ int totlen, lenthisone;
+ int save_totlen;
+ u_short etype;
+ int off, resid;
+ int count, spinwait;
+ int i;
+
+ totlen = inw(BASE + EP_W1_RX_STATUS);
+ off = 0;
+ top = 0;
+
+ if (totlen & ERR_RX) {
+ ++sc->arpcom.ac_if.if_ierrors;
+ goto out;
+ }
+ save_totlen = totlen &= RX_BYTES_MASK; /* Lower 11 bits = RX bytes. */
+
+ m = sc->mb[sc->next_mb];
+ sc->mb[sc->next_mb] = 0;
+
+ if (m == 0) {
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ goto out;
+ } else {
+ /* Convert one of our saved mbuf's */
+ sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
+ m->m_data = m->m_pktdat;
+ m->m_flags = M_PKTHDR;
+ }
+
+ top = m0 = m; /* We assign top so we can "goto out" */
+#define EROUND ((sizeof(struct ether_header) + 3) & ~3)
+#define EOFF (EROUND - sizeof(struct ether_header))
+ m0->m_data += EOFF;
+ /* Read what should be the header. */
+ insw(BASE + EP_W1_RX_PIO_RD_1,
+ mtod(m0, caddr_t), sizeof(struct ether_header) / 2);
+ m->m_len = sizeof(struct ether_header);
+ totlen -= sizeof(struct ether_header);
+ /*
+ * mostly deal with trailer here. (untested)
+ * We do this in a couple of parts. First we check for a trailer, if
+ * we have one we convert the mbuf back to a regular mbuf and set the offset and
+ * subtract sizeof(struct ether_header) from the pktlen.
+ * After we've read the packet off the interface (all except for the trailer
+ * header, we then get a header mbuf, read the trailer into it, and fix up
+ * the mbuf pointer chain.
+ */
+ eh = mtod(m, struct ether_header *);
+ eh->ether_type = etype = ntohs((u_short) eh->ether_type);
+ if (etype >= ETHERTYPE_TRAIL &&
+ etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) {
+ m->m_data = m->m_dat; /* Convert back to regular mbuf. */
+ m->m_flags = 0; /* This sucks but non-trailers are the norm */
+ off = (etype - ETHERTYPE_TRAIL) * 512;
+ if (off >= ETHERMTU) {
+ m_freem(m);
+ return; /* sanity */
+ }
+ totlen -= sizeof(struct ether_header); /* We don't read the trailer */
+ m->m_data += 2 * sizeof(u_short); /* Get rid of type & len */
+ }
+ while (totlen > 0) {
+ lenthisone = min(totlen, M_TRAILINGSPACE(m));
+ if (lenthisone == 0) { /* no room in this one */
+ mcur = m;
+ m = sc->mb[sc->next_mb];
+ sc->mb[sc->next_mb] = 0;
+ if (!m) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ goto out;
+ } else {
+ timeout(epmbuffill, (caddr_t)sc, 0);
+ sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
+ }
+ if (totlen >= MINCLSIZE)
+ MCLGET(m, M_DONTWAIT);
+ m->m_len = 0;
+ mcur->m_next = m;
+ lenthisone = min(totlen, M_TRAILINGSPACE(m));
+ }
+ if (sc->bus32bit) {
+ insl(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len,
+ lenthisone / 4);
+ m->m_len += (lenthisone & ~3);
+ if (lenthisone & 3)
+ insb(BASE + EP_W1_RX_PIO_RD_1,
+ mtod(m, caddr_t) + m->m_len,
+ lenthisone & 3);
+ m->m_len += (lenthisone & 3);
+ } else {
+ insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len,
+ lenthisone / 2);
+ m->m_len += lenthisone;
+ if (lenthisone & 1)
+ *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1);
+ }
+ totlen -= lenthisone;
+ }
+ if (off) {
+ top = sc->mb[sc->next_mb];
+ sc->mb[sc->next_mb] = 0;
+ if (top == 0) {
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (top == 0) {
+ top = m0;
+ goto out;
+ }
+ } else {
+ /* Convert one of our saved mbuf's */
+ sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
+ top->m_data = top->m_pktdat;
+ top->m_flags = M_PKTHDR;
+ }
+ insw(BASE + EP_W1_RX_PIO_RD_1, mtod(top, caddr_t),
+ sizeof(struct ether_header));
+ top->m_next = m0;
+ top->m_len = sizeof(struct ether_header);
+ /* XXX Accomodate for type and len from beginning of trailer */
+ top->m_pkthdr.len = save_totlen - (2 * sizeof(u_short));
+ } else {
+ top = m0;
+ top->m_pkthdr.len = save_totlen;
+ }
+
+ top->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ ;
+ ++sc->arpcom.ac_if.if_ipackets;
+#if NBPFILTER > 0
+ if (sc->bpf) {
+ bpf_mtap(sc->bpf, top);
+
+ /*
+ * Note that the interface cannot be in promiscuous mode if
+ * there are no BPF listeners. And if we are in promiscuous
+ * mode, we have to check if this packet is really ours.
+ */
+ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) &&
+ (eh->ether_dhost[0] & 1) == 0 &&
+ bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
+ sizeof(eh->ether_dhost)) != 0 &&
+ bcmp(eh->ether_dhost, etherbroadcastaddr,
+ sizeof(eh->ether_dhost)) != 0) {
+ m_freem(top);
+ return;
+ }
+ }
+#endif
+ m_adj(top, sizeof(struct ether_header));
+ ether_input(&sc->arpcom.ac_if, eh, top);
+ return;
+
+out: outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ ;
+ if (top)
+ m_freem(top);
+
+}
+
+
+/*
+ * Look familiar?
+ */
+static int
+epioctl(ifp, cmd, data)
+ register struct ifnet *ifp;
+ int cmd;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *) data;
+ struct ep_softc *sc = &ep_softc[ifp->if_unit];
+ struct ifreq *ifr = (struct ifreq *) data;
+ int s, error = 0;
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ epinit(ifp->if_unit); /* before arpwhohas */
+ ((struct arpcom *) ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *) ifp, &IA_SIN(ifa)->sin_addr);
+ break;
+#endif
+#ifdef NS
+ case AF_NS:
+ {
+ register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+ if (ns_nullhost(*ina))
+ ina->x_host =
+ *(union ns_host *)(sc->arpcom.ac_enaddr);
+ else {
+ ifp->if_flags &= ~IFF_RUNNING;
+ bcopy((caddr_t) ina->x_host.c_host,
+ (caddr_t)sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr));
+ }
+ epinit(ifp->if_unit);
+ break;
+ }
+#endif
+ default:
+ epinit(ifp->if_unit);
+ break;
+ }
+ break;
+ case SIOCSIFFLAGS:
+ if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) {
+ ifp->if_flags &= ~IFF_RUNNING;
+ epstop(ifp->if_unit);
+ epmbufempty(sc);
+ break;
+ }
+ if (ifp->if_flags & IFF_UP && (ifp->if_flags & IFF_RUNNING) == 0)
+ epinit(ifp->if_unit);
+ break;
+#ifdef notdef
+ case SIOCGHWADDR:
+ bcopy((caddr_t) sc->sc_addr, (caddr_t) &ifr->ifr_data,
+ sizeof(sc->sc_addr));
+ break;
+#endif
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+
+void
+epreset(unit)
+ int unit;
+{
+ int s = splimp();
+
+ epstop(unit);
+ epinit(unit);
+ splx(s);
+}
+
+void
+epwatchdog(unit)
+ int unit;
+{
+ struct ep_softc *sc = &ep_softc[unit];
+
+ log(LOG_ERR, "ep%d: watchdog\n", unit);
+ ++sc->arpcom.ac_if.if_oerrors;
+
+ epreset(unit);
+}
+
+void
+epstop(unit)
+ int unit;
+{
+ struct ep_softc *sc = &ep_softc[unit];
+
+ outw(BASE + EP_COMMAND, RX_DISABLE);
+ outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ ;
+ outw(BASE + EP_COMMAND, TX_DISABLE);
+ outw(BASE + EP_COMMAND, STOP_TRANSCEIVER);
+ outw(BASE + EP_COMMAND, RX_RESET);
+ outw(BASE + EP_COMMAND, TX_RESET);
+ outw(BASE + EP_COMMAND, C_INTR_LATCH);
+ outw(BASE + EP_COMMAND, SET_RD_0_MASK);
+ outw(BASE + EP_COMMAND, SET_INTR_MASK);
+ outw(BASE + EP_COMMAND, SET_RX_FILTER);
+}
+
+
+/*
+ * This is adapted straight from the book. There's probably a better way.
+ */
+static int
+send_ID_sequence(port)
+ u_short port;
+{
+ char cx, al;
+
+ cx = 0x0ff;
+ al = 0x0ff;
+
+ outb(port, 0x0);
+ DELAY(1000);
+ outb(port, 0x0);
+ DELAY(1000);
+
+loop1: cx--;
+ outb(port, al);
+ if (!(al & 0x80)) {
+ al = al << 1;
+ goto loop1;
+ }
+ al = al << 1;
+ al ^= 0xcf;
+ if (cx)
+ goto loop1;
+
+ return(1);
+}
+
+
+/*
+ * We get eeprom data from the id_port given an offset into the
+ * eeprom. Basically; after the ID_sequence is sent to all of
+ * the cards; they enter the ID_CMD state where they will accept
+ * command requests. 0x80-0xbf loads the eeprom data. We then
+ * read the port 16 times and with every read; the cards check
+ * for contention (ie: if one card writes a 0 bit and another
+ * writes a 1 bit then the host sees a 0. At the end of the cycle;
+ * each card compares the data on the bus; if there is a difference
+ * then that card goes into ID_WAIT state again). In the meantime;
+ * one bit of data is returned in the AX register which is conveniently
+ * returned to us by inb(). Hence; we read 16 times getting one
+ * bit of data with each read.
+ */
+static u_short
+get_eeprom_data(id_port, offset)
+ int id_port;
+ int offset;
+{
+ int i, data = 0;
+ outb(id_port, 0x80 + offset);
+ DELAY(1000);
+ for (i = 0; i < 16; i++)
+ data = (data << 1) | (inw(id_port) & 1);
+ return (data);
+}
+
+static int
+is_eeprom_busy(is)
+ struct isa_device *is;
+{
+ int i = 0, j;
+ register struct ep_softc *sc = &ep_softc[is->id_unit];
+
+ while (i++ < 100) {
+ j = inw(BASE + EP_W0_EEPROM_COMMAND);
+ if (j & EEPROM_BUSY)
+ DELAY(100);
+ else
+ break;
+ }
+ if (i >= 100) {
+ printf("\nep%d: eeprom failed to come ready.\n", is->id_unit);
+ return (1);
+ }
+ if (j & EEPROM_TST_MODE) {
+ printf("\nep%d: 3c509 in test mode. Erase pencil mark!\n", is->id_unit);
+ return (1);
+ }
+ return (0);
+}
+
+void
+epmbuffill(sp)
+ caddr_t sp;
+{
+ struct ep_softc *sc = (struct ep_softc *)sp;
+ int s, i;
+
+ s = splimp();
+ i = sc->last_mb;
+ do {
+ if(sc->mb[i] == NULL)
+ MGET(sc->mb[i], M_DONTWAIT, MT_DATA);
+ if(sc->mb[i] == NULL)
+ break;
+ i = (i + 1) % MAX_MBS;
+ } while (i != sc->next_mb);
+ sc->last_mb = i;
+ splx(s);
+}
+
+static void
+epmbufempty(sc)
+ struct ep_softc *sc;
+{
+ int s, i;
+
+ s = splimp();
+ for (i = 0; i<MAX_MBS; i++) {
+ if (sc->mb[i]) {
+ m_freem(sc->mb[i]);
+ sc->mb[i] = NULL;
+ }
+ }
+ sc->last_mb = sc->next_mb = 0;
+ untimeout(epmbuffill, sc);
+ splx(s);
+}
+
+#endif /* NEP > 0 */
diff --git a/sys/i386/isa/if_epreg.h b/sys/i386/isa/if_epreg.h
new file mode 100644
index 0000000..f0b4cd9
--- /dev/null
+++ b/sys/i386/isa/if_epreg.h
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: if_epreg.h,v 1.1 1993/12/14 04:26:47 hpeyerl Exp $
+ */
+/**************************************************************************
+ * *
+ * These define the EEPROM data structure. They are used in the probe
+ * function to verify the existance of the adapter after having sent
+ * the ID_Sequence.
+ *
+ * There are others but only the ones we use are defined here.
+ *
+ **************************************************************************/
+
+#define EEPROM_NODE_ADDR_0 0x0 /* Word */
+#define EEPROM_NODE_ADDR_1 0x1 /* Word */
+#define EEPROM_NODE_ADDR_2 0x2 /* Word */
+#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */
+#define EEPROM_MFG_ID 0x7 /* 0x6d50 */
+#define EEPROM_ADDR_CFG 0x8 /* Base addr */
+#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */
+
+/**************************************************************************
+ * *
+ * These are the registers for the 3Com 3c509 and their bit patterns when *
+ * applicable. They have been taken out the the "EtherLink III Parallel *
+ * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual *
+ * from 3com. *
+ * *
+ **************************************************************************/
+
+#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a command reg. */
+#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status reg. */
+#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window reg. */
+/*
+ * Window 0 registers. Setup.
+ */
+ /* Write */
+#define EP_W0_EEPROM_DATA 0x0c
+#define EP_W0_EEPROM_COMMAND 0x0a
+#define EP_W0_RESOURCE_CFG 0x08
+#define EP_W0_ADDRESS_CFG 0x06
+#define EP_W0_CONFIG_CTRL 0x04
+ /* Read */
+#define EP_W0_PRODUCT_ID 0x02
+#define EP_W0_MFG_ID 0x00
+
+/*
+ * Window 1 registers. Operating Set.
+ */
+ /* Write */
+#define EP_W1_TX_PIO_WR_2 0x02
+#define EP_W1_TX_PIO_WR_1 0x00
+ /* Read */
+#define EP_W1_FREE_TX 0x0c
+#define EP_W1_TX_STATUS 0x0b /* byte */
+#define EP_W1_TIMER 0x0a /* byte */
+#define EP_W1_RX_STATUS 0x08
+#define EP_W1_RX_PIO_RD_2 0x02
+#define EP_W1_RX_PIO_RD_1 0x00
+
+/*
+ * Window 2 registers. Station Address Setup/Read
+ */
+ /* Read/Write */
+#define EP_W2_ADDR_5 0x05
+#define EP_W2_ADDR_4 0x04
+#define EP_W2_ADDR_3 0x03
+#define EP_W2_ADDR_2 0x02
+#define EP_W2_ADDR_1 0x01
+#define EP_W2_ADDR_0 0x00
+
+/*
+ * Window 3 registers. FIFO Management.
+ */
+ /* Read */
+#define EP_W3_FREE_TX 0x0c
+#define EP_W3_FREE_RX 0x0a
+
+/*
+ * Window 4 registers. Diagnostics.
+ */
+ /* Read/Write */
+#define EP_W4_MEDIA_TYPE 0x0a
+#define EP_W4_CTRLR_STATUS 0x08
+#define EP_W4_NET_DIAG 0x06
+#define EP_W4_FIFO_DIAG 0x04
+#define EP_W4_HOST_DIAG 0x02
+#define EP_W4_TX_DIAG 0x00
+
+/*
+ * Window 5 Registers. Results and Internal status.
+ */
+ /* Read */
+#define EP_W5_READ_0_MASK 0x0c
+#define EP_W5_INTR_MASK 0x0a
+#define EP_W5_RX_FILTER 0x08
+#define EP_W5_RX_EARLY_THRESH 0x06
+#define EP_W5_TX_AVAIL_THRESH 0x02
+#define EP_W5_TX_START_THRESH 0x00
+
+/*
+ * Window 6 registers. Statistics.
+ */
+ /* Read/Write */
+#define TX_TOTAL_OK 0x0c
+#define RX_TOTAL_OK 0x0a
+#define TX_DEFERRALS 0x08
+#define RX_FRAMES_OK 0x07
+#define TX_FRAMES_OK 0x06
+#define RX_OVERRUNS 0x05
+#define TX_COLLISIONS 0x04
+#define TX_AFTER_1_COLLISION 0x03
+#define TX_AFTER_X_COLLISIONS 0x02
+#define TX_NO_SQE 0x01
+#define TX_CD_LOST 0x00
+
+/****************************************
+ *
+ * Register definitions.
+ *
+ ****************************************/
+
+/*
+ * Command register. All windows.
+ *
+ * 16 bit register.
+ * 15-11: 5-bit code for command to be executed.
+ * 10-0: 11-bit arg if any. For commands with no args;
+ * this can be set to anything.
+ */
+#define GLOBAL_RESET (u_short) 0x0000 /* Wait at least 1ms after issuing */
+#define WINDOW_SELECT (u_short) (0x1<<11)
+#define START_TRANSCEIVER (u_short) (0x2<<11) /* Read ADDR_CFG reg to determine
+ whether this is needed. If so;
+ wait 800 uSec before using trans-
+ ceiver. */
+#define RX_DISABLE (u_short) (0x3<<11) /* state disabled on power-up */
+#define RX_ENABLE (u_short) (0x4<<11)
+#define RX_RESET (u_short) (0x5<<11)
+#define RX_DISCARD_TOP_PACK (u_short) (0x8<<11)
+#define TX_ENABLE (u_short) (0x9<<11)
+#define TX_DISABLE (u_short) (0xa<<11)
+#define TX_RESET (u_short) (0xb<<11)
+#define REQ_INTR (u_short) (0xc<<11)
+ /*
+ * The following C_* acknowledge the various interrupts.
+ * Some of them don't do anything. See the manual.
+ */
+#define ACK_INTR (u_short) (0x6800)
+# define C_INTR_LATCH (u_short) (ACK_INTR|0x1)
+# define C_CARD_FAILURE (u_short) (ACK_INTR|0x2)
+# define C_TX_COMPLETE (u_short) (ACK_INTR|0x4)
+# define C_TX_AVAIL (u_short) (ACK_INTR|0x8)
+# define C_RX_COMPLETE (u_short) (ACK_INTR|0x10)
+# define C_RX_EARLY (u_short) (ACK_INTR|0x20)
+# define C_INT_RQD (u_short) (ACK_INTR|0x40)
+# define C_UPD_STATS (u_short) (ACK_INTR|0x80)
+#define SET_INTR_MASK (u_short) (0xe<<11)
+#define SET_RD_0_MASK (u_short) (0xf<<11)
+#define SET_RX_FILTER (u_short) (0x10<<11)
+# define FIL_INDIVIDUAL (u_short) (0x1)
+# define FIL_GROUP (u_short) (0x2)
+# define FIL_BRDCST (u_short) (0x4)
+# define FIL_ALL (u_short) (0x8)
+#define SET_RX_EARLY_THRESH (u_short) (0x11<<11)
+#define SET_TX_AVAIL_THRESH (u_short) (0x12<<11)
+#define SET_TX_START_THRESH (u_short) (0x13<<11)
+#define STATS_ENABLE (u_short) (0x15<<11)
+#define STATS_DISABLE (u_short) (0x16<<11)
+#define STOP_TRANSCEIVER (u_short) (0x17<<11)
+
+/*
+ * Status register. All windows.
+ *
+ * 15-13: Window number(0-7).
+ * 12: Command_in_progress.
+ * 11: reserved.
+ * 10: reserved.
+ * 9: reserved.
+ * 8: reserved.
+ * 7: Update Statistics.
+ * 6: Interrupt Requested.
+ * 5: RX Early.
+ * 4: RX Complete.
+ * 3: TX Available.
+ * 2: TX Complete.
+ * 1: Adapter Failure.
+ * 0: Interrupt Latch.
+ */
+#define S_INTR_LATCH (u_short) (0x1)
+#define S_CARD_FAILURE (u_short) (0x2)
+#define S_TX_COMPLETE (u_short) (0x4)
+#define S_TX_AVAIL (u_short) (0x8)
+#define S_RX_COMPLETE (u_short) (0x10)
+#define S_RX_EARLY (u_short) (0x20)
+#define S_INT_RQD (u_short) (0x40)
+#define S_UPD_STATS (u_short) (0x80)
+#define S_COMMAND_IN_PROGRESS (u_short) (0x1000)
+
+/*
+ * FIFO Registers. RX Status.
+ *
+ * 15: Incomplete or FIFO empty.
+ * 14: 1: Error in RX Packet 0: Incomplete or no error.
+ * 13-11: Type of error.
+ * 1000 = Overrun.
+ * 1011 = Run Packet Error.
+ * 1100 = Alignment Error.
+ * 1101 = CRC Error.
+ * 1001 = Oversize Packet Error (>1514 bytes)
+ * 0010 = Dribble Bits.
+ * (all other error codes, no errors.)
+ *
+ * 10-0: RX Bytes (0-1514)
+ */
+#define ERR_INCOMPLETE (u_short) (0x8000)
+#define ERR_RX (u_short) (0x4000)
+#define ERR_RX_PACKET (u_short) (0x2000)
+#define ERR_OVERRUN (u_short) (0x1000)
+#define ERR_RUNT (u_short) (0x1300)
+#define ERR_ALIGNMENT (u_short) (0x1400)
+#define ERR_CRC (u_short) (0x1500)
+#define ERR_OVERSIZE (u_short) (0x1100)
+#define ERR_DRIBBLE (u_short) (0x200)
+
+/*
+ * TX Status
+ *
+ * Reports the transmit status of a completed transmission. Writing this
+ * register pops the transmit completion stack.
+ *
+ * Window 1/Port 0x0b.
+ *
+ * 7: Complete
+ * 6: Interrupt on successful transmission requested.
+ * 5: Jabber Error (TP Only, TX Reset required. )
+ * 4: Underrun (TX Reset required. )
+ * 3: Maximum Collisions.
+ * 2: TX Status Overflow.
+ * 1-0: Undefined.
+ *
+ */
+#define TXS_COMPLETE 0x80
+#define TXS_INTR_REQ 0x40
+#define TXS_JABBER 0x20
+#define TXS_UNDERRUN 0x10
+#define TXS_MAX_COLLISION 0x8
+#define TXS_STATUS_OVERFLOW 0x4
+
+/*
+ * Misc defines for various things.
+ */
+#define TAG_ADAPTER_0 0xd0
+#define ACTIVATE_ADAPTER_TO_CONFIG 0xff
+#define ENABLE_DRQ_IRQ 0x0001
+#define MFG_ID 0x6d50
+#define PROD_ID 0x9150
+#define BASE sc->ep_io_addr
+#define GO_WINDOW(x) outw(BASE+EP_COMMAND, WINDOW_SELECT|x)
+#define AUI 0x1
+#define BNC 0x2
+#define UTP 0x4
+#define IS_AUI (1<<13)
+#define IS_BNC (1<<12)
+#define IS_UTP (1<<9)
+#define EEPROM_BUSY (1<<15)
+#define EEPROM_TST_MODE (1<<14)
+#define READ_EEPROM (1<<7)
+#define ETHER_ADDR_LEN 6
+#define ETHER_MAX 1536
+#define ENABLE_UTP 0xc0
+#define DISABLE_UTP 0x0
+#define RX_BYTES_MASK (u_short) (0x07ff)
diff --git a/sys/i386/isa/if_ie.c b/sys/i386/isa/if_ie.c
new file mode 100644
index 0000000..fbb1d5e
--- /dev/null
+++ b/sys/i386/isa/if_ie.c
@@ -0,0 +1,1801 @@
+/*-
+ * Copyright (c) 1992, 1993, University of Vermont and State
+ * Agricultural College.
+ * Copyright (c) 1992, 1993, Garrett A. Wollman.
+ *
+ * Portions:
+ * Copyright (c) 1990, 1991, William F. Jolitz
+ * Copyright (c) 1990, The Regents of the University of California
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * Vermont and State Agricultural College and Garrett A. Wollman,
+ * by William F. Jolitz, by the University of California,
+ * Berkeley, by Larwence Berkeley Laboratory, and its contributors.
+ * 4. Neither the names of the Universities nor the names of the authors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR AUTHORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: if_ie.c,v 1.2 1993/11/25 01:31:36 wollman Exp $
+ */
+
+/*
+ * Intel 82586 Ethernet chip
+ * Register, bit, and structure definitions.
+ *
+ * Written by GAW with reference to the Clarkson Packet Driver code for this
+ * chip written by Russ Nelson and others.
+ *
+ * BPF support code stolen directly from hpdev/if_le.c, supplied with
+ * tcpdump.
+ */
+
+/*
+ * The i82586 is a very versatile chip, found in many implementations.
+ * Programming this chip is mostly the same, but certain details differ
+ * from card to card. This driver is written so that different cards
+ * can be automatically detected at run-time. Currently, only the
+ * AT&T EN100/StarLAN 10 series are supported.
+ */
+
+/*
+Mode of operation:
+
+We run the 82586 in a standard Ethernet mode. We keep NFRAMES received
+frame descriptors around for the receiver to use, and NBUFFS associated
+receive buffer descriptors, both in a circular list. Whenever a frame is
+received, we rotate both lists as necessary. (The 586 treats both lists
+as a simple queue.) We also keep a transmit command around so that packets
+can be sent off quickly.
+
+We configure the adapter in AL-LOC = 1 mode, which means that the
+Ethernet/802.3 MAC header is placed at the beginning of the receive buffer
+rather than being split off into various fields in the RFD. This also
+means that we must include this header in the transmit buffer as well.
+
+By convention, all transmit commands, and only transmit commands, shall
+have the I (IE_CMD_INTR) bit set in the command. This way, when an
+interrupt arrives at ieintr(), it is immediately possible to tell
+what precisely caused it. ANY OTHER command-sending routines should
+run at splimp(), and should post an acknowledgement to every interrupt
+they generate.
+
+The 82586 has a 24-bit address space internally, and the adaptor's
+memory is located at the top of this region. However, the value we are
+given in configuration is normally the *bottom* of the adaptor RAM. So,
+we must go through a few gyrations to come up with a kernel virtual address
+which represents the actual beginning of the 586 address space. First,
+we autosize the RAM by running through several possible sizes and trying
+to initialize the adapter under the assumption that the selected size
+is correct. Then, knowing the correct RAM size, we set up our pointers
+in ie_softc[unit]. `iomem' represents the computed base of the 586
+address space. `iomembot' represents the actual configured base
+of adapter RAM. Finally, `iosize' represents the calculated size
+of 586 RAM. Then, when laying out commands, we use the interval
+[iomembot, iomembot + iosize); to make 24-pointers, we subtract
+iomem, and to make 16-pointers, we subtract iomem and and with 0xffff.
+
+*/
+
+#include "ie.h"
+#if NIE > 0
+
+#include "param.h"
+#include "systm.h"
+#include "mbuf.h"
+#include "protosw.h"
+#include "socket.h"
+#include "ioctl.h"
+#include "errno.h"
+#include "syslog.h"
+
+#include "net/if.h"
+#include "net/if_types.h"
+#include "net/if_dl.h"
+#include "net/route.h"
+
+#include "bpfilter.h"
+
+#ifdef INET
+#include "netinet/in.h"
+#include "netinet/in_systm.h"
+#include "netinet/in_var.h"
+#include "netinet/ip.h"
+#include "netinet/if_ether.h"
+#endif
+
+#ifdef NS
+#include "netns/ns.h"
+#include "netns/ns_if.h"
+#endif
+
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/ic/i82586.h"
+#include "i386/isa/if_iereg.h"
+#include "i386/isa/icu.h"
+
+#include "vm/vm.h"
+
+#if NBPFILTER > 0
+#include "net/bpf.h"
+#include "net/bpfdesc.h"
+#endif
+
+#if (NBPFILTER > 0) || defined(MULTICAST)
+#define FILTER
+static struct mbuf *last_not_for_us;
+#endif
+
+#ifdef DEBUG
+#define IED_RINT 1
+#define IED_TINT 2
+#define IED_RNR 4
+#define IED_CNA 8
+#define IED_READFRAME 16
+int ie_debug = IED_RNR;
+#endif
+
+#ifndef ETHERMINLEN
+#define ETHERMINLEN 60
+#endif
+
+#define IE_BUF_LEN 1512 /* length of transmit buffer */
+
+/* Forward declaration */
+struct ie_softc;
+
+static int ieprobe(struct isa_device *dvp);
+static int ieattach(struct isa_device *dvp);
+static void ieinit(int unit);
+static int ieioctl(struct ifnet *ifp, int command, caddr_t data);
+static void iestart(struct ifnet *ifp);
+static void sl_reset_586(int unit);
+static void sl_chan_attn(int unit);
+static void iereset(int unit);
+static void ie_readframe(int unit, struct ie_softc *ie, int bufno);
+static void ie_drop_packet_buffer(int unit, struct ie_softc *ie);
+static void sl_read_ether(int unit, unsigned char addr[6]);
+static void find_ie_mem_size(int unit);
+static int command_and_wait(int unit, int command, void volatile *pcmd, int);
+static int ierint(int unit, struct ie_softc *ie);
+static int ietint(int unit, struct ie_softc *ie);
+static int iernr(int unit, struct ie_softc *ie);
+static void start_receiver(int unit);
+static int ieget(int, struct ie_softc *, struct mbuf **,
+ struct ether_header *, int *);
+static caddr_t setup_rfa(caddr_t ptr, struct ie_softc *ie);
+static int mc_setup(int, caddr_t, volatile struct ie_sys_ctl_block *);
+#ifdef MULTICAST
+static void ie_mc_reset(int unit);
+#endif
+
+#ifdef DEBUG
+void print_rbd(volatile struct ie_recv_buf_desc *rbd);
+
+int in_ierint = 0;
+int in_ietint = 0;
+#endif
+
+/*
+ * This tells the autoconf code how to set us up.
+ */
+struct isa_driver iedriver = {
+ ieprobe, ieattach, "ie",
+};
+
+enum ie_hardware {
+ IE_STARLAN10,
+ IE_EN100,
+ IE_SLFIBER,
+ IE_UNKNOWN
+};
+
+const char *ie_hardware_names[] = {
+ "StarLAN 10",
+ "EN100",
+ "StarLAN Fiber",
+ "Unknown"
+};
+
+/*
+sizeof(iscp) == 1+1+2+4 == 8
+sizeof(scb) == 2+2+2+2+2+2+2+2 == 16
+NFRAMES * sizeof(rfd) == NFRAMES*(2+2+2+2+6+6+2+2) == NFRAMES*24 == 384
+sizeof(xmit_cmd) == 2+2+2+2+6+2 == 18
+sizeof(transmit buffer) == 1512
+sizeof(transmit buffer desc) == 8
+-----
+1946
+
+NBUFFS * sizeof(rbd) == NBUFFS*(2+2+4+2+2) == NBUFFS*12
+NBUFFS * IE_RBUF_SIZE == NBUFFS*256
+
+NBUFFS should be (16384 - 1946) / (256 + 12) == 14438 / 268 == 53
+
+With NBUFFS == 48, this leaves us 1574 bytes for another command or
+more buffers. Another transmit command would be 18+8+1512 == 1538
+---just barely fits!
+
+Obviously all these would have to be reduced for smaller memory sizes.
+With a larger memory, it would be possible to roughly double the number of
+both transmit and receive buffers.
+*/
+
+#define NFRAMES 16 /* number of frames to allow for receive */
+#define NBUFFS 48 /* number of buffers to allocate */
+#define IE_RBUF_SIZE 256 /* size of each buffer, MUST BE POWER OF TWO */
+
+/*
+ * Ethernet status, per interface.
+ */
+struct ie_softc {
+ struct arpcom arpcom;
+ void (*ie_reset_586)(int);
+ void (*ie_chan_attn)(int);
+ enum ie_hardware hard_type;
+ int hard_vers;
+
+ u_short port;
+ caddr_t iomem;
+ caddr_t iomembot;
+ unsigned iosize;
+
+ int want_mcsetup;
+ int promisc;
+ volatile struct ie_int_sys_conf_ptr *iscp;
+ volatile struct ie_sys_ctl_block *scb;
+ volatile struct ie_recv_frame_desc *rframes[NFRAMES];
+ volatile struct ie_recv_buf_desc *rbuffs[NBUFFS];
+ volatile char *cbuffs[NBUFFS];
+ int rfhead, rftail, rbhead, rbtail;
+
+ volatile struct ie_xmit_cmd *xmit_cmds[2];
+ volatile struct ie_xmit_buf *xmit_buffs[2];
+ int xmit_count;
+ u_char *xmit_cbuffs[2];
+
+ struct ie_en_addr mcast_addrs[MAXMCAST + 1];
+ int mcast_count;
+
+#if NBPFILTER > 0
+ caddr_t ie_bpf;
+#endif
+
+} ie_softc[NIE];
+
+#define MK_24(base, ptr) ((caddr_t)((u_long)ptr - (u_long)base))
+#define MK_16(base, ptr) ((u_short)(u_long)MK_24(base, ptr))
+
+#define PORT ie_softc[unit].port
+#define MEM ie_softc[unit].iomem
+
+
+int ieprobe(dvp)
+ struct isa_device *dvp;
+{
+ int unit = dvp->id_unit;
+ u_char c;
+
+ ie_softc[unit].port = dvp->id_iobase;
+ ie_softc[unit].iomembot = dvp->id_maddr;
+ ie_softc[unit].iomem = 0;
+
+ c = inb(PORT + IEATT_REVISION);
+ switch(SL_BOARD(c)) {
+ case SL10_BOARD:
+ ie_softc[unit].hard_type = IE_STARLAN10;
+ ie_softc[unit].ie_reset_586 = sl_reset_586;
+ ie_softc[unit].ie_chan_attn = sl_chan_attn;
+ break;
+ case EN100_BOARD:
+ ie_softc[unit].hard_type = IE_EN100;
+ ie_softc[unit].ie_reset_586 = sl_reset_586;
+ ie_softc[unit].ie_chan_attn = sl_chan_attn;
+ break;
+ case SLFIBER_BOARD:
+ ie_softc[unit].hard_type = IE_SLFIBER;
+ ie_softc[unit].ie_reset_586 = sl_reset_586;
+ ie_softc[unit].ie_chan_attn = sl_chan_attn;
+ break;
+
+ /*
+ * Anything else is not recognized or cannot be used.
+ */
+ default:
+ return 0;
+ }
+
+ ie_softc[unit].hard_vers = SL_REV(c);
+
+ /*
+ * Divine memory size on-board the card. Ususally 16k.
+ */
+ find_ie_mem_size(unit);
+
+ if(!ie_softc[unit].iosize) {
+ return 0;
+ }
+
+ dvp->id_msize = ie_softc[unit].iosize;
+
+ switch(ie_softc[unit].hard_type) {
+ case IE_EN100:
+ case IE_STARLAN10:
+ case IE_SLFIBER:
+ sl_read_ether(unit, ie_softc[unit].arpcom.ac_enaddr);
+ break;
+
+ default:
+ printf("ie%d: unknown AT&T board type code %d\n", unit,
+ ie_softc[unit].hard_type);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Taken almost exactly from Bill's if_is.c, then modified beyond recognition.
+ */
+int
+ieattach(dvp)
+ struct isa_device *dvp;
+{
+ int unit = dvp->id_unit;
+ struct ie_softc *ie = &ie_softc[unit];
+ struct ifnet *ifp = &ie->arpcom.ac_if;
+
+ ifp->if_unit = unit;
+ ifp->if_name = iedriver.name;
+ ifp->if_mtu = ETHERMTU;
+ printf("<%s R%d> ethernet address %s",
+ ie_hardware_names[ie_softc[unit].hard_type],
+ ie_softc[unit].hard_vers + 1,
+ ether_sprintf(ie->arpcom.ac_enaddr));
+
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
+#ifdef MULTICAST
+ ifp->if_flags |= IFF_MULTICAST;
+#endif /* MULTICAST */
+
+ ifp->if_init = ieinit;
+ ifp->if_output = ether_output;
+ ifp->if_start = iestart;
+ ifp->if_ioctl = ieioctl;
+ ifp->if_reset = iereset;
+ ifp->if_type = IFT_ETHER;
+ ifp->if_addrlen = 6;
+ ifp->if_hdrlen = 14;
+
+#if NBPFILTER > 0
+ printf("\n");
+ bpfattach(&ie_softc[unit].ie_bpf, ifp, DLT_EN10MB,
+ sizeof(struct ether_header));
+#endif
+
+ if_attach(ifp);
+ {
+ struct ifaddr *ifa = ifp->if_addrlist;
+ struct sockaddr_dl *sdl;
+ while(ifa && ifa->ifa_addr && ifa->ifa_addr->sa_family != AF_LINK)
+ ifa = ifa->ifa_next;
+
+ if(!ifa || !ifa->ifa_addr) return 1;
+
+ /* Provide our ether address to the higher layers */
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ sdl->sdl_type = IFT_ETHER;
+ sdl->sdl_alen = 6;
+ sdl->sdl_slen = 0;
+ bcopy(ie->arpcom.ac_enaddr, LLADDR(sdl), 6);
+ return 1;
+ }
+}
+
+/*
+ * What to do upon receipt of an interrupt.
+ */
+int ieintr(unit)
+ int unit;
+{
+ register struct ie_softc *ie = &ie_softc[unit];
+ register u_short status;
+
+ status = ie->scb->ie_status;
+
+loop:
+ if(status & (IE_ST_RECV | IE_ST_RNR)) {
+#ifdef DEBUG
+ in_ierint++;
+ if(ie_debug & IED_RINT)
+ printf("ie%d: rint\n", unit);
+#endif
+ ierint(unit, ie);
+#ifdef DEBUG
+ in_ierint--;
+#endif
+ }
+
+ if(status & IE_ST_DONE) {
+#ifdef DEBUG
+ in_ietint++;
+ if(ie_debug & IED_TINT)
+ printf("ie%d: tint\n", unit);
+#endif
+ ietint(unit, ie);
+#ifdef DEBUG
+ in_ietint--;
+#endif
+ }
+
+ if(status & IE_ST_RNR) {
+#ifdef DEBUG
+ if(ie_debug & IED_RNR)
+ printf("ie%d: rnr\n", unit);
+#endif
+ iernr(unit, ie);
+ }
+
+#ifdef DEBUG
+ if((status & IE_ST_ALLDONE)
+ && (ie_debug & IED_CNA))
+ printf("ie%d: cna\n", unit);
+#endif
+
+ /* Don't ack interrupts which we didn't receive */
+ ie_ack(ie->scb, IE_ST_WHENCE & status, unit, ie->ie_chan_attn);
+
+ if((status = ie->scb->ie_status) & IE_ST_WHENCE)
+ goto loop;
+
+ return unit;
+}
+
+/*
+ * Process a received-frame interrupt.
+ */
+static int ierint(unit, ie)
+ int unit;
+ struct ie_softc *ie;
+{
+ int i, status;
+ static int timesthru = 1024;
+
+ i = ie->rfhead;
+ while(1) {
+ status = ie->rframes[i]->ie_fd_status;
+
+ if((status & IE_FD_COMPLETE) && (status & IE_FD_OK)) {
+ ie->arpcom.ac_if.if_ipackets++;
+ if(!--timesthru) {
+ ie->arpcom.ac_if.if_ierrors += ie->scb->ie_err_crc + ie->scb->ie_err_align +
+ ie->scb->ie_err_resource + ie->scb->ie_err_overrun;
+ ie->scb->ie_err_crc = 0;
+ ie->scb->ie_err_align = 0;
+ ie->scb->ie_err_resource = 0;
+ ie->scb->ie_err_overrun = 0;
+ timesthru = 1024;
+ }
+ ie_readframe(unit, ie, i);
+ } else {
+ if(status & IE_FD_RNR) {
+ if(!(ie->scb->ie_status & IE_RU_READY)) {
+ ie->rframes[0]->ie_fd_next = MK_16(MEM, ie->rbuffs[0]);
+ ie->scb->ie_recv_list = MK_16(MEM, ie->rframes[0]);
+ command_and_wait(unit, IE_RU_START, 0, 0);
+ }
+ }
+ break;
+ }
+ i = (i + 1) % NFRAMES;
+ }
+ return 0;
+}
+
+/*
+ * Process a command-complete interrupt. These are only generated by
+ * the transmission of frames. This routine is deceptively simple, since
+ * most of the real work is done by iestart().
+ */
+static int ietint(unit, ie)
+ int unit;
+ struct ie_softc *ie;
+{
+ int status;
+ int i;
+
+ ie->arpcom.ac_if.if_timer = 0;
+ ie->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+
+ for(i = 0; i < ie->xmit_count; i++) {
+ status = ie->xmit_cmds[i]->ie_xmit_status;
+
+ if(status & IE_XS_LATECOLL) {
+ printf("ie%d: late collision\n", unit);
+ ie->arpcom.ac_if.if_collisions++;
+ ie->arpcom.ac_if.if_oerrors++;
+ } else if(status & IE_XS_NOCARRIER) {
+ printf("ie%d: no carrier\n", unit);
+ ie->arpcom.ac_if.if_oerrors++;
+ } else if(status & IE_XS_LOSTCTS) {
+ printf("ie%d: lost CTS\n", unit);
+ ie->arpcom.ac_if.if_oerrors++;
+ } else if(status & IE_XS_UNDERRUN) {
+ printf("ie%d: DMA underrun\n", unit);
+ ie->arpcom.ac_if.if_oerrors++;
+ } else if(status & IE_XS_EXCMAX) {
+ printf("ie%d: too many collisions\n", unit);
+ ie->arpcom.ac_if.if_collisions += 16;
+ ie->arpcom.ac_if.if_oerrors++;
+ } else {
+ ie->arpcom.ac_if.if_opackets++;
+ ie->arpcom.ac_if.if_collisions += status & IE_XS_MAXCOLL;
+ }
+ }
+ ie->xmit_count = 0;
+
+ /*
+ * If multicast addresses were added or deleted while we were transmitting,
+ * ie_mc_reset() set the want_mcsetup flag indicating that we should do it.
+ */
+ if(ie->want_mcsetup) {
+ mc_setup(unit, (caddr_t)ie->xmit_cbuffs[0], ie->scb);
+ ie->want_mcsetup = 0;
+ }
+
+ /* Wish I knew why this seems to be necessary... */
+ ie->xmit_cmds[0]->ie_xmit_status |= IE_STAT_COMPL;
+
+ iestart(&ie->arpcom.ac_if);
+ return 0; /* shouldn't be necessary */
+}
+
+/*
+ * Process a receiver-not-ready interrupt. I believe that we get these
+ * when there aren't enough buffers to go around. For now (FIXME), we
+ * just restart the receiver, and hope everything's ok.
+ */
+static int iernr(unit, ie)
+ int unit;
+ struct ie_softc *ie;
+{
+#ifdef doesnt_work
+ setup_rfa((caddr_t)ie->rframes[0], ie);
+
+ ie->scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]);
+ command_and_wait(unit, IE_RU_START, 0, 0);
+#else
+ /* This doesn't work either, but it doesn't hang either. */
+ command_and_wait(unit, IE_RU_DISABLE, 0, 0); /* just in case */
+ setup_rfa((caddr_t)ie->rframes[0], ie); /* ignore cast-qual */
+
+ ie->scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]);
+ command_and_wait(unit, IE_RU_START, 0, 0); /* was ENABLE */
+
+#endif
+ ie_ack(ie->scb, IE_ST_WHENCE, unit, ie->ie_chan_attn);
+
+ ie->arpcom.ac_if.if_ierrors++;
+ return 0;
+}
+
+#ifdef FILTER
+/*
+ * Compare two Ether/802 addresses for equality, inlined and
+ * unrolled for speed. I'd love to have an inline assembler
+ * version of this...
+ */
+static inline int ether_equal(u_char *one, u_char *two) {
+ if(one[0] != two[0]) return 0;
+ if(one[1] != two[1]) return 0;
+ if(one[2] != two[2]) return 0;
+ if(one[3] != two[3]) return 0;
+ if(one[4] != two[4]) return 0;
+ if(one[5] != two[5]) return 0;
+ return 1;
+}
+
+/*
+ * Check for a valid address. to_bpf is filled in with one of the following:
+ * 0 -> BPF doesn't get this packet
+ * 1 -> BPF does get this packet
+ * 2 -> BPF does get this packet, but we don't
+ * Return value is true if the packet is for us, and false otherwise.
+ *
+ * This routine is a mess, but it's also critical that it be as fast
+ * as possible. It could be made cleaner if we can assume that the
+ * only client which will fiddle with IFF_PROMISC is BPF. This is
+ * probably a good assumption, but we do not make it here. (Yet.)
+ */
+static inline int check_eh(struct ie_softc *ie,
+ struct ether_header *eh,
+ int *to_bpf) {
+ int i;
+
+ switch(ie->promisc) {
+ case IFF_ALLMULTI:
+ /*
+ * Receiving all multicasts, but no unicasts except those destined for us.
+ */
+#if NBPFILTER > 0
+ *to_bpf = (ie->ie_bpf != 0); /* BPF gets this packet if anybody cares */
+#endif
+ if(eh->ether_dhost[0] & 1) {
+ return 1;
+ }
+ if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1;
+ return 0;
+
+ case IFF_PROMISC:
+ /*
+ * Receiving all packets. These need to be passed on to BPF.
+ */
+#if NBPFILTER > 0
+ *to_bpf = (ie->ie_bpf != 0);
+#endif
+ /* If for us, accept and hand up to BPF */
+ if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1;
+
+#if NBPFILTER > 0
+ if(*to_bpf) *to_bpf = 2; /* we don't need to see it */
+#endif
+
+#ifdef MULTICAST
+ /*
+ * Not a multicast, so BPF wants to see it but we don't.
+ */
+ if(!(eh->ether_dhost[0] & 1)) return 1;
+
+ /*
+ * If it's one of our multicast groups, accept it and pass it
+ * up.
+ */
+ for(i = 0; i < ie->mcast_count; i++) {
+ if(ether_equal(eh->ether_dhost, (u_char *)&ie->mcast_addrs[i])) {
+#if NBPFILTER > 0
+ if(*to_bpf) *to_bpf = 1;
+#endif
+ return 1;
+ }
+ }
+#endif /* MULTICAST */
+ return 1;
+
+ case IFF_ALLMULTI | IFF_PROMISC:
+ /*
+ * Acting as a multicast router, and BPF running at the same time.
+ * Whew! (Hope this is a fast machine...)
+ */
+#if NBPFILTER > 0
+ *to_bpf = (ie->ie_bpf != 0);
+#endif
+ /* We want to see multicasts. */
+ if(eh->ether_dhost[0] & 1) return 1;
+
+ /* We want to see our own packets */
+ if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1;
+
+ /* Anything else goes to BPF but nothing else. */
+#if NBPFILTER > 0
+ if(*to_bpf) *to_bpf = 2;
+#endif
+ return 1;
+
+ default:
+ /*
+ * Only accept unicast packets destined for us, or multicasts
+ * for groups that we belong to. For now, we assume that the
+ * '586 will only return packets that we asked it for. This
+ * isn't strictly true (it uses hashing for the multicast filter),
+ * but it will do in this case, and we want to get out of here
+ * as quickly as possible.
+ */
+#if NBPFILTER > 0
+ *to_bpf = (ie->ie_bpf != 0);
+#endif
+ return 1;
+ }
+ return 0;
+}
+#endif /* FILTER */
+
+/*
+ * We want to isolate the bits that have meaning... This assumes that
+ * IE_RBUF_SIZE is an even power of two. If somehow the act_len exceeds
+ * the size of the buffer, then we are screwed anyway.
+ */
+static inline int ie_buflen(struct ie_softc *ie, int head) {
+ return (ie->rbuffs[head]->ie_rbd_actual
+ & (IE_RBUF_SIZE | (IE_RBUF_SIZE - 1)));
+}
+
+static inline int ie_packet_len(int unit, struct ie_softc *ie) {
+ int i;
+ int head = ie->rbhead;
+ int acc = 0;
+
+ do {
+ if(!(ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_USED)) {
+#ifdef DEBUG
+ print_rbd(ie->rbuffs[ie->rbhead]);
+#endif
+ log(LOG_ERR, "ie%d: receive descriptors out of sync at %d\n",
+ unit, ie->rbhead);
+ iereset(unit);
+ return -1;
+ }
+
+ i = ie->rbuffs[head]->ie_rbd_actual & IE_RBD_LAST;
+
+ acc += ie_buflen(ie, head);
+ head = (head + 1) % NBUFFS;
+ } while(!i);
+
+ return acc;
+}
+
+/*
+ * Read data off the interface, and turn it into an mbuf chain.
+ *
+ * This code is DRAMATICALLY different from the previous version; this
+ * version tries to allocate the entire mbuf chain up front, given the
+ * length of the data available. This enables us to allocate mbuf
+ * clusters in many situations where before we would have had a long
+ * chain of partially-full mbufs. This should help to speed up the
+ * operation considerably. (Provided that it works, of course.)
+ */
+static inline int ieget(unit, ie, mp, ehp, to_bpf)
+ int unit;
+ struct ie_softc *ie;
+ struct mbuf **mp;
+ struct ether_header *ehp;
+ int *to_bpf;
+{
+ struct mbuf *m, *top, **mymp;
+ int i;
+ int offset;
+ int totlen, resid;
+ int thismboff;
+ int head;
+
+ totlen = ie_packet_len(unit, ie);
+ if(totlen <= 0) return -1;
+
+ i = ie->rbhead;
+
+ /*
+ * Snarf the Ethernet header.
+ */
+ bcopy((caddr_t)ie->cbuffs[i], (caddr_t)ehp, sizeof *ehp);
+ /* ignore cast-qual warning here */
+
+ /*
+ * As quickly as possible, check if this packet is for us.
+ * If not, don't waste a single cycle copying the rest of the
+ * packet in.
+ * This is only a consideration when FILTER is defined; i.e., when
+ * we are either running BPF or doing multicasting.
+ */
+#ifdef FILTER
+ if(!check_eh(ie, ehp, to_bpf)) {
+ ie_drop_packet_buffer(unit, ie);
+ ie->arpcom.ac_if.if_ierrors--; /* just this case, it's not an error */
+ return -1;
+ }
+#endif
+ totlen -= (offset = sizeof *ehp);
+
+ MGETHDR(*mp, M_DONTWAIT, MT_DATA);
+ if(!*mp) {
+ ie_drop_packet_buffer(unit, ie);
+ return -1;
+ }
+
+ m = *mp;
+ m->m_pkthdr.rcvif = &ie->arpcom.ac_if;
+ m->m_len = MHLEN;
+ resid = m->m_pkthdr.len = totlen;
+ top = 0;
+ mymp = &top;
+
+ /*
+ * This loop goes through and allocates mbufs for all the data we will
+ * be copying in. It does not actually do the copying yet.
+ */
+ do { /* while(resid > 0) */
+ /*
+ * Try to allocate an mbuf to hold the data that we have. If we
+ * already allocated one, just get another one and stick it on the
+ * end (eventually). If we don't already have one, try to allocate
+ * an mbuf cluster big enough to hold the whole packet, if we think it's
+ * reasonable, or a single mbuf which may or may not be big enough.
+ * Got that?
+ */
+ if(top) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if(!m) {
+ m_freem(top);
+ ie_drop_packet_buffer(unit, ie);
+ return -1;
+ }
+ m->m_len = MLEN;
+ }
+
+ if(resid >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if(m->m_flags & M_EXT)
+ m->m_len = min(resid, MCLBYTES);
+ } else {
+ if(resid < m->m_len) {
+ if(!top && resid + max_linkhdr <= m->m_len)
+ m->m_data += max_linkhdr;
+ m->m_len = resid;
+ }
+ }
+ resid -= m->m_len;
+ *mymp = m;
+ mymp = &m->m_next;
+ } while(resid > 0);
+
+ resid = totlen;
+ m = top;
+ thismboff = 0;
+ head = ie->rbhead;
+
+ /*
+ * Now we take the mbuf chain (hopefully only one mbuf most of the
+ * time) and stuff the data into it. There are no possible failures
+ * at or after this point.
+ */
+ while(resid > 0) { /* while there's stuff left */
+ int thislen = ie_buflen(ie, head) - offset;
+
+ /*
+ * If too much data for the current mbuf, then fill the current one
+ * up, go to the next one, and try again.
+ */
+ if(thislen > m->m_len - thismboff) {
+ int newlen = m->m_len - thismboff;
+ bcopy((caddr_t)(ie->cbuffs[head] + offset),
+ mtod(m, caddr_t) + thismboff, (unsigned)newlen);
+ /* ignore cast-qual warning */
+ m = m->m_next;
+ thismboff = 0; /* new mbuf, so no offset */
+ offset += newlen; /* we are now this far into the packet */
+ resid -= newlen; /* so there is this much left to get */
+ continue;
+ }
+
+ /*
+ * If there is more than enough space in the mbuf to hold the
+ * contents of this buffer, copy everything in, advance pointers,
+ * and so on.
+ */
+ if(thislen < m->m_len - thismboff) {
+ bcopy((caddr_t)(ie->cbuffs[head] + offset), /* ignore warning */
+ mtod(m, caddr_t) + thismboff, (unsigned)thislen);
+ thismboff += thislen; /* we are this far into the mbuf */
+ resid -= thislen; /* and this much is left */
+ goto nextbuf;
+ }
+
+ /*
+ * Otherwise, there is exactly enough space to put this buffer's
+ * contents into the current mbuf. Do the combination of the above
+ * actions.
+ */
+ bcopy((caddr_t)(ie->cbuffs[head] + offset), /* ignore warning */
+ mtod(m, caddr_t) + thismboff, (unsigned)thislen);
+ m = m->m_next;
+ thismboff = 0; /* new mbuf, start at the beginning */
+ resid -= thislen; /* and we are this far through */
+
+ /*
+ * Advance all the pointers. We can get here from either of the
+ * last two cases, but never the first.
+ */
+nextbuf:
+ offset = 0;
+ ie->rbuffs[head]->ie_rbd_actual = 0;
+ ie->rbuffs[head]->ie_rbd_length |= IE_RBD_LAST;
+ ie->rbhead = head = (head + 1) % NBUFFS;
+ ie->rbuffs[ie->rbtail]->ie_rbd_length &= ~IE_RBD_LAST;
+ ie->rbtail = (ie->rbtail + 1) % NBUFFS;
+ }
+
+ /*
+ * Unless something changed strangely while we were doing the copy,
+ * we have now copied everything in from the shared memory.
+ * This means that we are done.
+ */
+ return 0;
+}
+
+/*
+ * Read frame NUM from unit UNIT (pre-cached as IE).
+ *
+ * This routine reads the RFD at NUM, and copies in the buffers from
+ * the list of RBD, then rotates the RBD and RFD lists so that the receiver
+ * doesn't start complaining. Trailers are DROPPED---there's no point
+ * in wasting time on confusing code to deal with them. Hopefully,
+ * this machine will never ARP for trailers anyway.
+ */
+static void ie_readframe(unit, ie, num)
+ int unit;
+ struct ie_softc *ie;
+ int num; /* frame number to read */
+{
+ struct ie_recv_frame_desc rfd;
+ struct mbuf *m = 0;
+ struct ether_header eh;
+#if NBPFILTER > 0
+ int bpf_gets_it = 0;
+#endif
+
+ bcopy((caddr_t)(ie->rframes[num]), &rfd, sizeof(struct ie_recv_frame_desc));
+
+ /* Immediately advance the RFD list, since we we have copied ours now. */
+ ie->rframes[num]->ie_fd_status = 0;
+ ie->rframes[num]->ie_fd_last |= IE_FD_LAST;
+ ie->rframes[ie->rftail]->ie_fd_last &= ~IE_FD_LAST;
+ ie->rftail = (ie->rftail + 1) % NFRAMES;
+ ie->rfhead = (ie->rfhead + 1) % NFRAMES;
+
+ if(rfd.ie_fd_status & IE_FD_OK) {
+ if(
+#if NBPFILTER > 0
+ ieget(unit, ie, &m, &eh, &bpf_gets_it)
+#else
+ ieget(unit, ie, &m, &eh, (int *)0)
+#endif
+ ) {
+ ie->arpcom.ac_if.if_ierrors++; /* this counts as an error */
+ return;
+ }
+ }
+
+#ifdef DEBUG
+ if(ie_debug & IED_READFRAME) {
+ printf("ie%d: frame from ether %s type %x\n", unit,
+ ether_sprintf(eh.ether_shost), (unsigned)eh.ether_type);
+ }
+ if(ntohs(eh.ether_type) > ETHERTYPE_TRAIL
+ && ntohs(eh.ether_type) < (ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER))
+ printf("received trailer!\n");
+#endif
+
+ if(!m) return;
+
+#ifdef FILTER
+ if(last_not_for_us) {
+ m_freem(last_not_for_us);
+ last_not_for_us = 0;
+ }
+
+#if NBPFILTER > 0
+ /*
+ * Check for a BPF filter; if so, hand it up.
+ * Note that we have to stick an extra mbuf up front, because
+ * bpf_mtap expects to have the ether header at the front.
+ * It doesn't matter that this results in an ill-formatted mbuf chain,
+ * since BPF just looks at the data. (It doesn't try to free the mbuf,
+ * tho' it will make a copy for tcpdump.)
+ */
+ if(bpf_gets_it) {
+ struct mbuf m0;
+ m0.m_len = sizeof eh;
+ m0.m_data = (caddr_t)&eh;
+ m0.m_next = m;
+
+ /* Pass it up */
+ bpf_mtap(ie->ie_bpf, &m0);
+ }
+ /*
+ * A signal passed up from the filtering code indicating that the
+ * packet is intended for BPF but not for the protocol machinery.
+ * We can save a few cycles by not handing it off to them.
+ */
+ if(bpf_gets_it == 2) {
+ last_not_for_us = m;
+ return;
+ }
+#endif /* NBPFILTER > 0 */
+ /*
+ * In here there used to be code to check destination addresses upon
+ * receipt of a packet. We have deleted that code, and replaced it
+ * with code to check the address much earlier in the cycle, before
+ * copying the data in; this saves us valuable cycles when operating
+ * as a multicast router or when using BPF.
+ */
+#endif /* FILTER */
+
+ eh.ether_type = ntohs(eh.ether_type);
+
+ /*
+ * Finally pass this packet up to higher layers.
+ */
+ ether_input(&ie->arpcom.ac_if, &eh, m);
+}
+
+static void ie_drop_packet_buffer(int unit, struct ie_softc *ie) {
+ int i;
+
+ do {
+ /*
+ * This means we are somehow out of sync. So, we reset the
+ * adapter.
+ */
+ if(!(ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_USED)) {
+#ifdef DEBUG
+ print_rbd(ie->rbuffs[ie->rbhead]);
+#endif
+ log(LOG_ERR, "ie%d: receive descriptors out of sync at %d\n",
+ unit, ie->rbhead);
+ iereset(unit);
+ return;
+ }
+
+ i = ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_LAST;
+
+ ie->rbuffs[ie->rbhead]->ie_rbd_length |= IE_RBD_LAST;
+ ie->rbuffs[ie->rbhead]->ie_rbd_actual = 0;
+ ie->rbhead = (ie->rbhead + 1) % NBUFFS;
+ ie->rbuffs[ie->rbtail]->ie_rbd_length &= ~IE_RBD_LAST;
+ ie->rbtail = (ie->rbtail + 1) % NBUFFS;
+ } while(!i);
+}
+
+
+/*
+ * Start transmission on an interface.
+ */
+static void
+iestart(ifp)
+ struct ifnet *ifp;
+{
+ struct ie_softc *ie = &ie_softc[ifp->if_unit];
+ struct mbuf *m0, *m;
+ unsigned char *buffer;
+ u_short len;
+ /* This is not really volatile, in this routine, but it makes gcc happy. */
+ volatile u_short *bptr = &ie->scb->ie_command_list;
+
+ if(!(ifp->if_flags & IFF_RUNNING))
+ return;
+ if(ifp->if_flags & IFF_OACTIVE)
+ return;
+
+ do {
+ IF_DEQUEUE(&ie->arpcom.ac_if.if_snd, m);
+ if(!m)
+ break;
+
+ buffer = ie->xmit_cbuffs[ie->xmit_count];
+ len = 0;
+
+ for(m0 = m; m && len < IE_BUF_LEN; m = m->m_next) {
+ bcopy(mtod(m, caddr_t), buffer, m->m_len);
+ buffer += m->m_len;
+ len += m->m_len;
+ }
+
+ m_freem(m0);
+ len = max(len, ETHERMINLEN);
+
+#if NBPFILTER > 0
+ /*
+ * See if bpf is listening on this interface, let it see the packet
+ * before we commit it to the wire.
+ */
+ if(ie->ie_bpf)
+ bpf_tap(ie->ie_bpf, ie->xmit_cbuffs[ie->xmit_count], len);
+#endif
+
+ ie->xmit_buffs[ie->xmit_count]->ie_xmit_flags = IE_XMIT_LAST | len;
+ ie->xmit_buffs[ie->xmit_count]->ie_xmit_next = 0xffff;
+ ie->xmit_buffs[ie->xmit_count]->ie_xmit_buf =
+ MK_24(ie->iomem, ie->xmit_cbuffs[ie->xmit_count]);
+
+ ie->xmit_cmds[ie->xmit_count]->com.ie_cmd_cmd = IE_CMD_XMIT;
+ ie->xmit_cmds[ie->xmit_count]->ie_xmit_status = 0;
+ ie->xmit_cmds[ie->xmit_count]->ie_xmit_desc =
+ MK_16(ie->iomem, ie->xmit_buffs[ie->xmit_count]);
+
+ *bptr = MK_16(ie->iomem, ie->xmit_cmds[ie->xmit_count]);
+ bptr = &ie->xmit_cmds[ie->xmit_count]->com.ie_cmd_link;
+ ie->xmit_count++;
+ } while(ie->xmit_count < 2);
+
+ /*
+ * If we queued up anything for transmission, send it.
+ */
+ if(ie->xmit_count) {
+ ie->xmit_cmds[ie->xmit_count - 1]->com.ie_cmd_cmd |=
+ IE_CMD_LAST | IE_CMD_INTR;
+
+ /*
+ * By passing the command pointer as a null, we tell
+ * command_and_wait() to pretend that this isn't an action
+ * command. I wish I understood what was happening here.
+ */
+ command_and_wait(ifp->if_unit, IE_CU_START, 0, 0);
+ ifp->if_flags |= IFF_OACTIVE;
+ }
+
+ return;
+}
+
+/*
+ * Check to see if there's an 82586 out there.
+ */
+int check_ie_present(unit, where, size)
+ int unit;
+ caddr_t where;
+ unsigned size;
+{
+ volatile struct ie_sys_conf_ptr *scp;
+ volatile struct ie_int_sys_conf_ptr *iscp;
+ volatile struct ie_sys_ctl_block *scb;
+ u_long realbase;
+ int s;
+
+ s = splimp();
+
+ realbase = (u_long)where + size - (1 << 24);
+
+ scp = (volatile struct ie_sys_conf_ptr *)(realbase + IE_SCP_ADDR);
+ bzero((char *)scp, sizeof *scp); /* ignore cast-qual */
+
+ /*
+ * First we put the ISCP at the bottom of memory; this tests to make
+ * sure that our idea of the size of memory is the same as the controller's.
+ * This is NOT where the ISCP will be in normal operation.
+ */
+ iscp = (volatile struct ie_int_sys_conf_ptr *)where;
+ bzero((char *)iscp, sizeof *iscp); /* ignore cast-qual */
+
+ scb = (volatile struct ie_sys_ctl_block *)where;
+ bzero((char *)scb, sizeof *scb); /* ignore cast-qual */
+
+ scp->ie_bus_use = 0; /* 16-bit */
+ scp->ie_iscp_ptr = (caddr_t)((volatile caddr_t)iscp - /* ignore cast-qual */
+ (volatile caddr_t)realbase);
+
+ iscp->ie_busy = 1;
+ iscp->ie_scb_offset = MK_16(realbase, scb) + 256;
+
+ (*ie_softc[unit].ie_reset_586)(unit);
+ (*ie_softc[unit].ie_chan_attn)(unit);
+
+ DELAY(100); /* wait a while... */
+
+ if(iscp->ie_busy) {
+ splx(s);
+ return 0;
+ }
+
+ /*
+ * Now relocate the ISCP to its real home, and reset the controller
+ * again.
+ */
+ iscp = (void *)Align((caddr_t)(realbase + IE_SCP_ADDR -
+ sizeof(struct ie_int_sys_conf_ptr)));
+ bzero((char *)iscp, sizeof *iscp); /* ignore cast-qual */
+
+ scp->ie_iscp_ptr = (caddr_t)((caddr_t)iscp - (caddr_t)realbase);
+ /* ignore cast-qual */
+
+ iscp->ie_busy = 1;
+ iscp->ie_scb_offset = MK_16(realbase, scb);
+
+ (*ie_softc[unit].ie_reset_586)(unit);
+ (*ie_softc[unit].ie_chan_attn)(unit);
+
+ DELAY(100);
+
+ if(iscp->ie_busy) {
+ splx(s);
+ return 0;
+ }
+
+ ie_softc[unit].iosize = size;
+ ie_softc[unit].iomem = (caddr_t)realbase;
+
+ ie_softc[unit].iscp = iscp;
+ ie_softc[unit].scb = scb;
+
+ /*
+ * Acknowledge any interrupts we may have caused...
+ */
+ ie_ack(scb, IE_ST_WHENCE, unit, ie_softc[unit].ie_chan_attn);
+ splx(s);
+
+ return 1;
+}
+
+/*
+ * Divine the memory size of ie board UNIT.
+ * Better hope there's nothing important hiding just below the ie card...
+ */
+static void find_ie_mem_size(unit)
+ int unit;
+{
+ unsigned size;
+
+ ie_softc[unit].iosize = 0;
+
+ for(size = 65536; size >= 16384; size -= 16384) {
+ if(check_ie_present(unit, ie_softc[unit].iomembot, size)) {
+ return;
+ }
+ }
+
+ return;
+}
+
+void sl_reset_586(unit)
+ int unit;
+{
+ outb(PORT + IEATT_RESET, 0);
+}
+
+void sl_chan_attn(unit)
+ int unit;
+{
+ outb(PORT + IEATT_ATTN, 0);
+}
+
+void sl_read_ether(unit, addr)
+ int unit;
+ unsigned char addr[6];
+{
+ int i;
+
+ for(i = 0; i < 6; i++)
+ addr[i] = inb(PORT + i);
+}
+
+
+static void
+iereset(unit)
+ int unit;
+{
+ int s = splimp();
+
+ if(unit >= NIE) {
+ splx(s);
+ return;
+ }
+
+ printf("ie%d: reset\n", unit);
+ ie_softc[unit].arpcom.ac_if.if_flags &= ~IFF_UP;
+ ieioctl(&ie_softc[unit].arpcom.ac_if, SIOCSIFFLAGS, 0);
+
+ /*
+ * Stop i82586 dead in its tracks.
+ */
+ if(command_and_wait(unit, IE_RU_ABORT | IE_CU_ABORT, 0, 0))
+ printf("ie%d: abort commands timed out\n", unit);
+
+ if(command_and_wait(unit, IE_RU_DISABLE | IE_CU_STOP, 0, 0))
+ printf("ie%d: disable commands timed out\n", unit);
+
+#ifdef notdef
+ if(!check_ie_present(unit, ie_softc[unit].iomembot, ie_softc[unit].iosize))
+ panic("ie disappeared!\n");
+#endif
+
+ ie_softc[unit].arpcom.ac_if.if_flags |= IFF_UP;
+ ieioctl(&ie_softc[unit].arpcom.ac_if, SIOCSIFFLAGS, 0);
+
+ splx(s);
+ return;
+}
+
+/*
+ * This is called if we time out.
+ */
+static void
+chan_attn_timeout(rock)
+ caddr_t rock;
+{
+ *(int *)rock = 1;
+}
+
+/*
+ * Send a command to the controller and wait for it to either
+ * complete or be accepted, depending on the command. If the
+ * command pointer is null, then pretend that the command is
+ * not an action command. If the command pointer is not null,
+ * and the command is an action command, wait for
+ * ((volatile struct ie_cmd_common *)pcmd)->ie_cmd_status & MASK
+ * to become true.
+ */
+static int command_and_wait(unit, cmd, pcmd, mask)
+ int unit;
+ int cmd;
+ volatile void *pcmd;
+ int mask;
+{
+ volatile struct ie_cmd_common *cc = pcmd;
+ volatile int timedout = 0;
+ extern int hz;
+
+ ie_softc[unit].scb->ie_command = (u_short)cmd;
+
+ if(IE_ACTION_COMMAND(cmd) && pcmd) {
+ (*ie_softc[unit].ie_chan_attn)(unit);
+
+ /*
+ * According to the packet driver, the minimum timeout should be
+ * .369 seconds, which we round up to .37.
+ */
+ timeout(chan_attn_timeout, (caddr_t)&timedout, 37 * hz / 100);
+ /* ignore cast-qual */
+
+ /*
+ * Now spin-lock waiting for status. This is not a very nice
+ * thing to do, but I haven't figured out how, or indeed if, we
+ * can put the process waiting for action to sleep. (We may
+ * be getting called through some other timeout running in the
+ * kernel.)
+ */
+ while(1) {
+ if((cc->ie_cmd_status & mask) || timedout)
+ break;
+ }
+
+ untimeout(chan_attn_timeout, (caddr_t)&timedout);
+ /* ignore cast-qual */
+
+ return timedout;
+ } else {
+
+ /*
+ * Otherwise, just wait for the command to be accepted.
+ */
+ (*ie_softc[unit].ie_chan_attn)(unit);
+
+ while(ie_softc[unit].scb->ie_command)
+ ; /* spin lock */
+
+ return 0;
+ }
+}
+
+/*
+ * Run the time-domain reflectometer...
+ */
+static void run_tdr(unit, cmd)
+ int unit;
+ struct ie_tdr_cmd *cmd;
+{
+ int result;
+
+ cmd->com.ie_cmd_status = 0;
+ cmd->com.ie_cmd_cmd = IE_CMD_TDR | IE_CMD_LAST;
+ cmd->com.ie_cmd_link = 0xffff;
+ cmd->ie_tdr_time = 0;
+
+ ie_softc[unit].scb->ie_command_list = MK_16(MEM, cmd);
+ cmd->ie_tdr_time = 0;
+
+ if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL))
+ result = 0x2000;
+ else
+ result = cmd->ie_tdr_time;
+
+ ie_ack(ie_softc[unit].scb, IE_ST_WHENCE, unit,
+ ie_softc[unit].ie_chan_attn);
+
+ if(result & IE_TDR_SUCCESS)
+ return;
+
+ if(result & IE_TDR_XCVR) {
+ printf("ie%d: transceiver problem\n", unit);
+ } else if(result & IE_TDR_OPEN) {
+ printf("ie%d: TDR detected an open %d clocks away\n", unit,
+ result & IE_TDR_TIME);
+ } else if(result & IE_TDR_SHORT) {
+ printf("ie%d: TDR detected a short %d clocks away\n", unit,
+ result & IE_TDR_TIME);
+ } else {
+ printf("ie%d: TDR returned unknown status %x\n", result);
+ }
+}
+
+static void start_receiver(unit)
+ int unit;
+{
+ int s = splimp();
+
+ ie_softc[unit].scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]);
+ command_and_wait(unit, IE_RU_START, 0, 0);
+
+ ie_ack(ie_softc[unit].scb, IE_ST_WHENCE, unit, ie_softc[unit].ie_chan_attn);
+
+ splx(s);
+}
+
+/*
+ * Here is a helper routine for iernr() and ieinit(). This sets up
+ * the RFA.
+ */
+static caddr_t setup_rfa(caddr_t ptr, struct ie_softc *ie) {
+ volatile struct ie_recv_frame_desc *rfd = (void *)ptr;
+ volatile struct ie_recv_buf_desc *rbd;
+ int i;
+ int unit = ie - &ie_softc[0];
+
+ /* First lay them out */
+ for(i = 0; i < NFRAMES; i++) {
+ ie->rframes[i] = rfd;
+ bzero((char *)rfd, sizeof *rfd); /* ignore cast-qual */
+ rfd++;
+ }
+
+ ptr = (caddr_t)Align((caddr_t)rfd); /* ignore cast-qual */
+
+ /* Now link them together */
+ for(i = 0; i < NFRAMES; i++) {
+ ie->rframes[i]->ie_fd_next =
+ MK_16(MEM, ie->rframes[(i + 1) % NFRAMES]);
+ }
+
+ /* Finally, set the EOL bit on the last one. */
+ ie->rframes[NFRAMES - 1]->ie_fd_last |= IE_FD_LAST;
+
+ /*
+ * Now lay out some buffers for the incoming frames. Note that
+ * we set aside a bit of slop in each buffer, to make sure that
+ * we have enough space to hold a single frame in every buffer.
+ */
+ rbd = (void *)ptr;
+
+ for(i = 0; i < NBUFFS; i++) {
+ ie->rbuffs[i] = rbd;
+ bzero((char *)rbd, sizeof *rbd); /* ignore cast-qual */
+ ptr = (caddr_t)Align(ptr + sizeof *rbd);
+ rbd->ie_rbd_length = IE_RBUF_SIZE;
+ rbd->ie_rbd_buffer = MK_24(MEM, ptr);
+ ie->cbuffs[i] = (void *)ptr;
+ ptr += IE_RBUF_SIZE;
+ rbd = (void *)ptr;
+ }
+
+ /* Now link them together */
+ for(i = 0; i < NBUFFS; i++) {
+ ie->rbuffs[i]->ie_rbd_next = MK_16(MEM, ie->rbuffs[(i + 1) % NBUFFS]);
+ }
+
+ /* Tag EOF on the last one */
+ ie->rbuffs[NBUFFS - 1]->ie_rbd_length |= IE_RBD_LAST;
+
+ /* We use the head and tail pointers on receive to keep track of
+ * the order in which RFDs and RBDs are used. */
+ ie->rfhead = 0;
+ ie->rftail = NFRAMES - 1;
+ ie->rbhead = 0;
+ ie->rbtail = NBUFFS - 1;
+
+ ie->scb->ie_recv_list = MK_16(MEM, ie->rframes[0]);
+ ie->rframes[0]->ie_fd_buf_desc = MK_16(MEM, ie->rbuffs[0]);
+
+ ptr = Align(ptr);
+ return ptr;
+}
+
+/*
+ * Run the multicast setup command.
+ * Call at splimp().
+ */
+static int mc_setup(int unit, caddr_t ptr,
+ volatile struct ie_sys_ctl_block *scb) {
+ struct ie_softc *ie = &ie_softc[unit];
+ volatile struct ie_mcast_cmd *cmd = (void *)ptr;
+
+ cmd->com.ie_cmd_status = 0;
+ cmd->com.ie_cmd_cmd = IE_CMD_MCAST | IE_CMD_LAST;
+ cmd->com.ie_cmd_link = 0xffff;
+
+ /* ignore cast-qual */
+ bcopy((caddr_t)ie->mcast_addrs, (caddr_t)cmd->ie_mcast_addrs,
+ ie->mcast_count * sizeof *ie->mcast_addrs);
+
+ cmd->ie_mcast_bytes = ie->mcast_count * 6; /* grrr... */
+
+ scb->ie_command_list = MK_16(MEM, cmd);
+ if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL)
+ || !(cmd->com.ie_cmd_status & IE_STAT_OK)) {
+ printf("ie%d: multicast address setup command failed\n", unit);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * This routine takes the environment generated by check_ie_present()
+ * and adds to it all the other structures we need to operate the adapter.
+ * This includes executing the CONFIGURE, IA-SETUP, and MC-SETUP commands,
+ * starting the receiver unit, and clearing interrupts.
+ *
+ * THIS ROUTINE MUST BE CALLED AT splimp() OR HIGHER.
+ */
+static void
+ieinit(unit)
+ int unit;
+{
+ struct ie_softc *ie = &ie_softc[unit];
+ volatile struct ie_sys_ctl_block *scb = ie->scb;
+ caddr_t ptr;
+
+ ptr = (caddr_t)Align((caddr_t)scb + sizeof *scb); /* ignore cast-qual */
+
+ /*
+ * Send the configure command first.
+ */
+ {
+ volatile struct ie_config_cmd *cmd = (void *)ptr;
+
+ ie_setup_config(cmd, ie->promisc, ie->hard_type == IE_STARLAN10);
+ cmd->com.ie_cmd_status = 0;
+ cmd->com.ie_cmd_cmd = IE_CMD_CONFIG | IE_CMD_LAST;
+ cmd->com.ie_cmd_link = 0xffff;
+
+ scb->ie_command_list = MK_16(MEM, cmd);
+
+ if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL)
+ || !(cmd->com.ie_cmd_status & IE_STAT_OK)) {
+ printf("ie%d: configure command failed\n", unit);
+ return;
+ }
+ }
+ /*
+ * Now send the Individual Address Setup command.
+ */
+ {
+ volatile struct ie_iasetup_cmd *cmd = (void *)ptr;
+
+ cmd->com.ie_cmd_status = 0;
+ cmd->com.ie_cmd_cmd = IE_CMD_IASETUP | IE_CMD_LAST;
+ cmd->com.ie_cmd_link = 0xffff;
+
+ bcopy((char *)ie_softc[unit].arpcom.ac_enaddr, (char *)&cmd->ie_address,
+ sizeof cmd->ie_address); /* ignore cast-qual */
+
+ scb->ie_command_list = MK_16(MEM, cmd);
+ if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL)
+ || !(cmd->com.ie_cmd_status & IE_STAT_OK)) {
+ printf("ie%d: individual address setup command failed\n", unit);
+ return;
+ }
+ }
+
+ /*
+ * Now run the time-domain reflectometer.
+ */
+ run_tdr(unit, (void *)ptr);
+
+ /*
+ * Acknowledge any interrupts we have generated thus far.
+ */
+ ie_ack(ie->scb, IE_ST_WHENCE, unit, ie->ie_chan_attn);
+
+ /*
+ * Set up the RFA.
+ */
+ ptr = setup_rfa(ptr, ie);
+
+ /*
+ * Finally, the transmit command and buffer are the last little bit of work.
+ */
+ ie->xmit_cmds[0] = (void *)ptr;
+ ptr += sizeof *ie->xmit_cmds[0];
+ ptr = Align(ptr);
+ ie->xmit_buffs[0] = (void *)ptr;
+ ptr += sizeof *ie->xmit_buffs[0];
+ ptr = Align(ptr);
+
+ /* Second transmit command */
+ ie->xmit_cmds[1] = (void *)ptr;
+ ptr += sizeof *ie->xmit_cmds[1];
+ ptr = Align(ptr);
+ ie->xmit_buffs[1] = (void *)ptr;
+ ptr += sizeof *ie->xmit_buffs[1];
+ ptr = Align(ptr);
+
+ /* Both transmit buffers */
+ ie->xmit_cbuffs[0] = (void *)ptr;
+ ptr += IE_BUF_LEN;
+ ptr = Align(ptr);
+ ie->xmit_cbuffs[1] = (void *)ptr;
+
+ bzero((caddr_t)ie->xmit_cmds[0], sizeof *ie->xmit_cmds[0]); /* ignore */
+ bzero((caddr_t)ie->xmit_buffs[0], sizeof *ie->xmit_buffs[0]); /* cast-qual */
+ bzero((caddr_t)ie->xmit_cmds[1], sizeof *ie->xmit_cmds[0]); /* warnings */
+ bzero((caddr_t)ie->xmit_buffs[1], sizeof *ie->xmit_buffs[0]); /* here */
+
+ /*
+ * This must be coordinated with iestart() and ietint().
+ */
+ ie->xmit_cmds[0]->ie_xmit_status = IE_STAT_COMPL;
+
+ ie->arpcom.ac_if.if_flags |= IFF_RUNNING; /* tell higher levels that we are here */
+ start_receiver(unit);
+ return;
+}
+
+static void ie_stop(unit)
+ int unit;
+{
+ command_and_wait(unit, IE_RU_DISABLE, 0, 0);
+}
+
+static int
+ieioctl(ifp, command, data)
+ struct ifnet *ifp;
+ int command;
+ caddr_t data;
+{
+ struct ifaddr *ifa = (struct ifaddr *)data;
+ struct ie_softc *ie = &ie_softc[ifp->if_unit];
+ int s, error = 0;
+
+ s = splimp();
+
+ switch(command) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+
+ switch(ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ ieinit(ifp->if_unit);
+ ((struct arpcom *)ifp)->ac_ipaddr =
+ IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
+ break;
+#endif /* INET */
+
+#ifdef NS
+ /* This magic copied from if_is.c; I don't use XNS, so I have no
+ * way of telling if this actually works or not.
+ */
+ case AF_NS:
+ {
+ struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+ if(ns_nullhost(*ina)) {
+ ina->x_host = *(union ns_host *)(ie->arpcom.ac_enaddr);
+ } else {
+ ifp->if_flags &= ~IFF_RUNNING;
+ bcopy((caddr_t)ina->x_host.c_host,
+ (caddr_t)ie->arpcom.ac_enaddr,
+ sizeof ie->arpcom.ac_enaddr);
+ }
+
+ ieinit(ifp->if_unit);
+ }
+ break;
+#endif /* NS */
+
+ default:
+ ieinit(ifp->if_unit);
+ break;
+ }
+ break;
+
+ case SIOCSIFFLAGS:
+ /*
+ * Note that this device doesn't have an "all multicast" mode, so we
+ * must turn on promiscuous mode and do the filtering manually.
+ */
+ if((ifp->if_flags & IFF_UP) == 0 &&
+ (ifp->if_flags & IFF_RUNNING)) {
+ ifp->if_flags &= ~IFF_RUNNING;
+ ie_stop(ifp->if_unit);
+ } else if((ifp->if_flags & IFF_UP) &&
+ (ifp->if_flags & IFF_RUNNING) == 0) {
+ ie_softc[ifp->if_unit].promisc =
+ ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI);
+ ieinit(ifp->if_unit);
+ } else if(ie_softc[ifp->if_unit].promisc ^
+ (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI))) {
+ ie_softc[ifp->if_unit].promisc =
+ ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI);
+ ieinit(ifp->if_unit);
+ }
+ break;
+
+#ifdef MULTICAST
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ /*
+ * Update multicast listeners
+ */
+ error = ((command == SIOCADDMULTI)
+ ? ether_addmulti((struct ifreq *)data, &ie->arpcom)
+ : ether_delmulti((struct ifreq *)data, &ie->arpcom));
+
+ if(error == ENETRESET) {
+ /* reset multicast filtering */
+ ie_mc_reset(ifp->if_unit);
+ error = 0;
+ }
+ break;
+#endif /* MULTICAST */
+
+ default:
+ error = EINVAL;
+ }
+
+ splx(s);
+ return error;
+}
+
+#ifdef MULTICAST
+static void ie_mc_reset(int unit) {
+ struct ie_softc *ie = &ie_softc[unit];
+ struct ether_multi *enm;
+ struct ether_multistep step;
+
+ /*
+ * Step through the list of addresses.
+ */
+ ie->mcast_count = 0;
+ ETHER_FIRST_MULTI(step, &ie->arpcom, enm);
+ while(enm) {
+ if(ie->mcast_count >= MAXMCAST
+ || bcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) {
+ ie->arpcom.ac_if.if_flags |= IFF_ALLMULTI;
+ ieioctl(&ie->arpcom.ac_if, SIOCSIFFLAGS, (void *)0);
+ goto setflag;
+ }
+
+ bcopy(enm->enm_addrlo, &(ie->mcast_addrs[ie->mcast_count]), 6);
+ ie->mcast_count++;
+ ETHER_NEXT_MULTI(step, enm);
+ }
+
+setflag:
+ ie->want_mcsetup = 1;
+}
+
+#endif
+
+#ifdef DEBUG
+void print_rbd(volatile struct ie_recv_buf_desc *rbd) {
+ printf("RBD at %08lx:\n"
+ "actual %04x, next %04x, buffer %08x\n"
+ "length %04x, mbz %04x\n",
+ (unsigned long)rbd,
+ rbd->ie_rbd_actual, rbd->ie_rbd_next, rbd->ie_rbd_buffer,
+ rbd->ie_rbd_length, rbd->mbz);
+}
+#endif /* DEBUG */
+#endif /* NIE > 0 */
+
diff --git a/sys/i386/isa/if_iereg.h b/sys/i386/isa/if_iereg.h
new file mode 100644
index 0000000..3588b84
--- /dev/null
+++ b/sys/i386/isa/if_iereg.h
@@ -0,0 +1,24 @@
+/*
+ * $Id$
+ * definitions for AT&T StarLAN 10 etc...
+ */
+
+#define IEATT_RESET 0 /* any write here resets the 586 */
+#define IEATT_ATTN 1 /* any write here sends a Chan attn */
+#define IEATT_REVISION 6 /* read here to figure out this board */
+#define IEATT_ATTRIB 7 /* more information about this board */
+
+#define SL_BOARD(x) ((x) & 0x0f)
+#define SL_REV(x) ((x) >> 4)
+
+#define SL1_BOARD 0
+#define SL10_BOARD 1
+#define EN100_BOARD 2
+#define SLFIBER_BOARD 3
+
+#define SL_ATTR_WIDTH 0x04 /* bus width: clear -> 8-bit */
+#define SL_ATTR_SPEED 0x08 /* medium speed: clear -> 10 Mbps */
+#define SL_ATTR_CODING 0x10 /* encoding: clear -> Manchester */
+#define SL_ATTR_HBW 0x20 /* host bus width: clear -> 16-bit */
+#define SL_ATTR_TYPE 0x40 /* medium type: clear -> Ethernet */
+#define SL_ATTR_BOOTROM 0x80 /* set -> boot ROM present */
diff --git a/sys/i386/isa/if_is.c b/sys/i386/isa/if_is.c
new file mode 100644
index 0000000..fc0dcae
--- /dev/null
+++ b/sys/i386/isa/if_is.c
@@ -0,0 +1,1148 @@
+/*
+ * Isolan AT 4141-0 Ethernet driver
+ * Isolink 4110
+ *
+ * By Paul Richards
+ *
+ * Copyright (C) 1993, Paul Richards. This software may be used, modified,
+ * copied, distributed, and sold, in both source and binary form provided
+ * that the above copyright and these terms are retained. Under no
+ * circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ *
+*/
+
+/* TODO
+
+1) Add working multicast support
+2) Use better allocation of memory to card
+3) Advertise for more packets until all transmit buffers are full
+4) Add more of the timers/counters e.g. arpcom.opackets etc.
+*/
+
+#include "is.h"
+#if NIS > 0
+
+#include "bpfilter.h"
+
+#include "param.h"
+#include "systm.h"
+#include "errno.h"
+#include "ioctl.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "syslog.h"
+
+#include "net/if.h"
+#include "net/if_dl.h"
+#include "net/if_types.h"
+
+#ifdef INET
+#include "netinet/in.h"
+#include "netinet/in_systm.h"
+#include "netinet/in_var.h"
+#include "netinet/ip.h"
+#include "netinet/if_ether.h"
+#endif
+
+#ifdef NS
+#include "netns/ns.h"
+#include "netns/ns_if.h"
+#endif
+
+#if NBPFILTER > 0
+#include "net/bpf.h"
+#include "net/bpfdesc.h"
+#endif
+
+#include "i386/isa/isa_device.h"
+#include "i386/isa/if_isreg.h"
+#include "i386/isa/icu.h"
+
+#include "vm/vm.h"
+
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1518
+#define ETHER_ADDR_LEN 6
+
+char *card_type[] = {"Unknown",
+ "BICC Isolan",
+ "NE2100"};
+
+char *ic_type[] = {"Unknown",
+ "Am7990 LANCE",
+ "Am79960 PCnet_ISA"};
+
+
+struct is_softc {
+ struct arpcom arpcom; /* Ethernet common part */
+ int iobase;
+ int rap;
+ int rdp;
+ int ic_type; /* Am 7990 or Am79960 */
+ int card_type;
+ int is_debug;
+ struct init_block *init_block; /* Lance initialisation block */
+ struct mds *rd;
+ struct mds *td;
+ unsigned char *rbuf;
+ unsigned char *tbuf;
+ int last_rd;
+ int last_td;
+ int no_td;
+ caddr_t bpf; /* BPF "magic cookie" */
+
+} is_softc[NIS] ;
+
+
+/* Function prototypes */
+static int is_probe(struct isa_device *);
+static int is_attach(struct isa_device *);
+static void is_watchdog(int);
+static int is_ioctl(struct ifnet *, int, caddr_t);
+static void is_init(int);
+static void is_start(struct ifnet *);
+static void istint(int);
+static void recv_print(int, int);
+static void xmit_print(int, int);
+
+
+
+static inline void is_rint(int unit);
+static inline void isread(struct is_softc*, unsigned char*, int);
+
+struct mbuf *isget();
+
+struct isa_driver isdriver = {
+ is_probe,
+ is_attach,
+ "is"
+};
+
+void
+iswrcsr(unit,port,val)
+ int unit;
+ u_short port;
+ u_short val;
+{
+ outw(is_softc[unit].rap,port);
+ outw(is_softc[unit].rdp,val);
+}
+
+u_short isrdcsr(unit,port)
+ int unit;
+ u_short port;
+{
+ outw(is_softc[unit].rap,port);
+ return(inw(is_softc[unit].rdp));
+}
+
+int
+is_probe(isa_dev)
+ struct isa_device *isa_dev;
+{
+ int unit = isa_dev->id_unit ;
+ int nports;
+
+int i;
+ is_softc[unit].iobase = isa_dev->id_iobase;
+
+ /*
+ * It's impossible to do a non-invasive probe of the
+ * LANCE and PCnet_ISA. The LANCE requires setting the
+ * STOP bit to access the registers and the PCnet_ISA
+ * address port resets to an unknown state!!
+ */
+
+ /*
+ * Check for BICC cards first since for the NE2100 and
+ * PCnet-ISA cards this write will hit the Address PROM.
+ */
+
+#ifdef DEBUG
+ printf("Dumping io space for is%d starting at %x\n",unit,is_softc[unit].iobase);
+ for (i=0; i< 32; i++)
+ printf(" %x ",inb(is_softc[unit].iobase+i));
+ printf("\n");
+#endif /* DEBUG*/
+
+ if (nports = bicc_probe(unit))
+ return (nports);
+ if (nports = ne2100_probe(unit))
+ return (nports);
+
+
+ return (0);
+}
+
+int
+ne2100_probe(unit)
+ int unit;
+{
+struct is_softc *is = &is_softc[unit];
+int i;
+
+ is->rap = is->iobase + NE2100_RAP;
+ is->rdp = is->iobase + NE2100_RDP;
+
+ if (is->ic_type = lance_probe(unit)) {
+ is->card_type = NE2100;
+ /*
+ * Extract the physical MAC address from ROM
+ */
+ for(i=0;i<ETHER_ADDR_LEN;i++)
+ is->arpcom.ac_enaddr[i]=inb(is->iobase+i);
+
+ /*
+ * Return number of I/O ports used by card
+ */
+ return (24);
+ }
+ return (0);
+}
+
+
+int
+bicc_probe(unit)
+ int unit;
+{
+struct is_softc *is = &is_softc[unit];
+int i;
+
+ is->rap = is->iobase + BICC_RAP;
+ is->rdp = is->iobase + BICC_RDP;
+
+ if (is->ic_type = lance_probe(unit)) {
+ is->card_type = BICC;
+
+ /*
+ * Extract the physical ethernet address from ROM
+ */
+
+ for(i=0;i<ETHER_ADDR_LEN;i++)
+ is->arpcom.ac_enaddr[i]=inb(is->iobase+(i*2));
+
+ /*
+ * Return number of I/O ports used by card
+ */
+ return (16);
+ }
+ return (0);
+}
+
+
+/*
+ * Determine which, if any, of the LANCE or
+ * PCnet-ISA are present on the card.
+ */
+
+int
+lance_probe(unit)
+ int unit;
+{
+int type=0;
+
+ /*
+ * Have to reset the LANCE to get any
+ * stable information from it.
+ */
+
+ iswrcsr(unit,0,STOP);
+ DELAY(100);
+
+ if (isrdcsr(unit,0) != STOP)
+ /*
+ * This either isn't a LANCE
+ * or there's a major problem.
+ */
+ return(0);
+
+ /*
+ * Depending on which controller it is, CSR3 will have
+ * different settable bits. Write to them all and see which ones
+ * get set.
+ */
+
+ iswrcsr(unit,3, LANCE_MASK);
+
+ if (isrdcsr(unit,3) == LANCE_MASK)
+ type = LANCE;
+
+ if (isrdcsr(unit,3) == PCnet_ISA_MASK)
+ type = PCnet_ISA;
+
+ return (type);
+}
+
+/*
+ * Reset of interface.
+ */
+static void
+is_reset(int unit)
+{
+ int s;
+ struct is_softc *is = &is_softc[unit];
+
+ if (unit >= NIS)
+ return;
+ printf("is%d: reset\n", unit);
+ is_init(unit);
+}
+
+/*
+ * Interface exists: make available by filling in network interface
+ * record. System will initialize the interface when it is ready
+ * to accept packets. We get the ethernet address here.
+ */
+int
+is_attach(isa_dev)
+ struct isa_device *isa_dev;
+{
+ int unit = isa_dev->id_unit;
+ struct is_softc *is = &is_softc[unit];
+ struct ifnet *ifp = &is->arpcom.ac_if;
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+
+ ifp->if_unit = unit;
+ ifp->if_name = isdriver.name ;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
+ ifp->if_init = is_init;
+ ifp->if_output = ether_output;
+ ifp->if_start = is_start;
+ ifp->if_ioctl = is_ioctl;
+ ifp->if_reset = is_reset;
+ ifp->if_watchdog = is_watchdog;
+
+ /*
+ * XXX -- not sure this is right place to do this
+ * Allocate memory for use by Lance
+ * Memory allocated for:
+ * initialisation block,
+ * ring descriptors,
+ * transmit and receive buffers.
+ */
+
+ /*
+ * XXX - hopefully have better way to get dma'able memory later,
+ * this code assumes that the physical memory address returned
+ * from malloc will be below 16Mb. The Lance's address registers
+ * are only 16 bits wide!
+ */
+
+#define MAXMEM ((NRBUF+NTBUF)*(BUFSIZE) + (NRBUF+NTBUF)*sizeof(struct mds) \
+ + sizeof(struct init_block) + 8)
+ is->init_block = (struct init_block *)malloc(MAXMEM,M_TEMP,M_NOWAIT);
+ if (!is->init_block) {
+ printf("is%d : Couldn't allocate memory for card\n",unit);
+ }
+ /*
+ * XXX -- should take corrective action if not
+ * quadword alilgned, the 8 byte slew factor in MAXMEM
+ * allows for this.
+ */
+
+ if ((u_long)is->init_block & 0x3)
+ printf("is%d: memory allocated not quadword aligned\n");
+
+ /* Set up DMA */
+ isa_dmacascade(isa_dev->id_drq);
+
+ if_attach(ifp);
+
+ /*
+ * Search down the ifa address list looking
+ * for the AF_LINK type entry
+ */
+
+ ifa = ifp->if_addrlist;
+ while ((ifa != 0) && (ifa->ifa_addr != 0) &&
+ (ifa->ifa_addr->sa_family != AF_LINK))
+ ifa = ifa->ifa_next;
+
+ /*
+ * If we find an AF_LINK type entry, we will fill
+ * in the hardware address for this interface.
+ */
+
+ if ((ifa != 0) && (ifa->ifa_addr != 0)) {
+
+ /*
+ * Fill in the link level address for this interface
+ */
+
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ sdl->sdl_type = IFT_ETHER;
+ sdl->sdl_alen = ETHER_ADDR_LEN;
+ sdl->sdl_slen = 0;
+ bcopy(is->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN);
+ }
+
+ printf ("is%d: address %s\n", unit,
+ ether_sprintf(is->arpcom.ac_enaddr)) ;
+ printf("%s, %s\n",ic_type[is->ic_type],card_type[is->card_type]);
+
+#if NBPFILTER > 0
+ bpfattach(&is->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+ return 1;
+}
+
+static void
+is_watchdog(unit)
+ int unit;
+{
+ log(LOG_ERR, "is%d: device timeout\n", unit);
+ is_reset(unit);
+}
+
+
+/* Lance initialisation block set up */
+void
+init_mem(unit)
+ int unit;
+{
+ int i;
+ void *temp;
+ struct is_softc *is = &is_softc[unit];
+
+ /*
+ * At this point we assume that the
+ * memory allocated to the Lance is
+ * quadword aligned. If it isn't
+ * then the initialisation is going
+ * fail later on.
+ */
+
+
+ /*
+ * Set up lance initialisation block
+ */
+
+ temp = (void *)is->init_block;
+ temp += sizeof(struct init_block);
+ is->rd = (struct mds *) temp;
+ is->td = (struct mds *) (temp + (NRBUF*sizeof(struct mds)));
+ temp += (NRBUF+NTBUF) * sizeof(struct mds);
+
+ is->init_block->mode = 0;
+ for (i=0; i<ETHER_ADDR_LEN; i++)
+ is->init_block->padr[i] = is->arpcom.ac_enaddr[i];
+ for (i = 0; i < 8; ++i)
+ is->init_block->ladrf[i] = MULTI_INIT_ADDR;
+ is->init_block->rdra = kvtop(is->rd);
+ is->init_block->rlen = ((kvtop(is->rd) >> 16) & 0xff) | (RLEN<<13);
+ is->init_block->tdra = kvtop(is->td);
+ is->init_block->tlen = ((kvtop(is->td) >> 16) & 0xff) | (TLEN<<13);
+
+
+ /*
+ * Set up receive ring descriptors
+ */
+
+ is->rbuf = (unsigned char *)temp;
+ for (i=0; i<NRBUF; i++) {
+ (is->rd+i)->addr = kvtop(temp);
+ (is->rd+i)->flags= ((kvtop(temp) >> 16) & 0xff) | OWN;
+ (is->rd+i)->bcnt = -BUFSIZE;
+ (is->rd+i)->mcnt = 0;
+ temp += BUFSIZE;
+ }
+
+ /*
+ * Set up transmit ring descriptors
+ */
+
+ is->tbuf = (unsigned char *)temp;
+ for (i=0; i<NTBUF; i++) {
+ (is->td+i)->addr = kvtop(temp);
+ (is->td+i)->flags= ((kvtop(temp) >> 16) & 0xff);
+ (is->td+i)->bcnt = 0;
+ (is->td+i)->mcnt = 0;
+ temp += BUFSIZE;
+ }
+
+}
+
+/*
+ * Initialization of interface; set up initialization block
+ * and transmit/receive descriptor rings.
+ */
+
+static void
+is_init(unit)
+ int unit;
+{
+ register struct is_softc *is = &is_softc[unit];
+ struct ifnet *ifp = &is->arpcom.ac_if;
+ int s;
+ register i;
+
+ /* Address not known */
+ if (ifp->if_addrlist == (struct ifaddr *)0) return;
+
+ s = splimp();
+
+ /*
+ * Lance must be stopped
+ * to access registers.
+ */
+
+ iswrcsr(unit,0,STOP);
+
+ is->last_rd = is->last_td = is->no_td = 0;
+
+ /* Set up lance's memory area */
+ init_mem(unit);
+
+ /* No byte swapping etc */
+ iswrcsr(unit,3,0);
+
+ /* Give lance the physical address of its memory area */
+ iswrcsr(unit,1,kvtop(is->init_block));
+ iswrcsr(unit,2,(kvtop(is->init_block) >> 16) & 0xff);
+
+ /* OK, let's try and initialise the Lance */
+ iswrcsr(unit,0,INIT);
+
+ /* Wait for initialisation to finish */
+ for(i=0; i<1000; i++){
+ if (isrdcsr(unit,0)&IDON)
+ break;
+ }
+ if (isrdcsr(unit,0)&IDON) {
+ /* Start lance */
+ iswrcsr(unit,0,STRT|IDON|INEA);
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ is_start(ifp);
+ }
+ else
+ printf("is%d: card failed to initialise\n", unit);
+
+ (void) splx(s);
+}
+
+/*
+ * Setup output on interface.
+ * Get another datagram to send off of the interface queue,
+ * and map it to the interface before starting the output.
+ * called only at splimp or interrupt level.
+ */
+static void
+is_start(ifp)
+ struct ifnet *ifp;
+{
+ int unit = ifp->if_unit;
+ register struct is_softc *is = &is_softc[unit];
+ struct mbuf *m0, *m;
+ unsigned char *buffer;
+ u_short len;
+ int i;
+ struct mds *cdm;
+
+
+ if ((is->arpcom.ac_if.if_flags & IFF_RUNNING) == 0)
+ return;
+
+ do {
+ cdm = (is->td + is->last_td);
+ if (cdm->flags&OWN)
+ return;
+
+ IF_DEQUEUE(&is->arpcom.ac_if.if_snd, m);
+
+ if (m == 0)
+ return;
+
+ /*
+ * Copy the mbuf chain into the transmit buffer
+ */
+
+ buffer = is->tbuf+(BUFSIZE*is->last_td);
+ len=0;
+ for (m0=m; m != 0; m=m->m_next) {
+ bcopy(mtod(m,caddr_t),buffer,m->m_len);
+ buffer += m->m_len;
+ len += m->m_len;
+ }
+#if NBPFILTER > 0
+ if (is->bpf) {
+ u_short etype;
+ int off, datasize, resid;
+ struct ether_header *eh;
+ struct trailer_header {
+ u_short ether_type;
+ u_short ether_residual;
+ } trailer_header;
+ char ether_packet[ETHER_MAX_LEN];
+ char *ep;
+
+ ep = ether_packet;
+
+ /*
+ * We handle trailers below:
+ * Copy ether header first, then residual data,
+ * then data. Put all this in a temporary buffer
+ * 'ether_packet' and send off to bpf. Since the
+ * system has generated this packet, we assume
+ * that all of the offsets in the packet are
+ * correct; if they're not, the system will almost
+ * certainly crash in m_copydata.
+ * We make no assumptions about how the data is
+ * arranged in the mbuf chain (i.e. how much
+ * data is in each mbuf, if mbuf clusters are
+ * used, etc.), which is why we use m_copydata
+ * to get the ether header rather than assume
+ * that this is located in the first mbuf.
+ */
+ /* copy ether header */
+ m_copydata(m0, 0, sizeof(struct ether_header), ep);
+ eh = (struct ether_header *) ep;
+ ep += sizeof(struct ether_header);
+ etype = ntohs(eh->ether_type);
+ if (etype >= ETHERTYPE_TRAIL &&
+ etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
+ datasize = ((etype - ETHERTYPE_TRAIL) << 9);
+ off = datasize + sizeof(struct ether_header);
+
+ /* copy trailer_header into a data structure */
+ m_copydata(m0, off, sizeof(struct trailer_header),
+ (caddr_t)&trailer_header.ether_type);
+
+ /* copy residual data */
+ resid = trailer_header.ether_residual -
+ sizeof(struct trailer_header);
+ resid = ntohs(resid);
+ m_copydata(m0, off+sizeof(struct trailer_header),
+ resid, ep);
+ ep += resid;
+
+ /* copy data */
+ m_copydata(m0, sizeof(struct ether_header),
+ datasize, ep);
+ ep += datasize;
+
+ /* restore original ether packet type */
+ eh->ether_type = trailer_header.ether_type;
+
+ bpf_tap(is->bpf, ether_packet, ep - ether_packet);
+ } else
+ bpf_mtap(is->bpf, m0);
+ }
+#endif
+
+
+ m_freem(m0);
+ len = max(len,ETHER_MIN_LEN);
+
+ /*
+ * Init transmit registers, and set transmit start flag.
+ */
+
+ cdm->flags |= (OWN|STP|ENP);
+ cdm->bcnt = -len;
+ cdm->mcnt = 0;
+#ifdef ISDEBUG
+ if (is->is_debug)
+ xmit_print(unit,is->last_td);
+#endif
+
+ iswrcsr(unit,0,TDMD|INEA);
+ if (++is->last_td >= NTBUF)
+ is->last_td=0;
+ }while(++is->no_td < NTBUF);
+ is->no_td = NTBUF;
+ is->arpcom.ac_if.if_flags |= IFF_OACTIVE;
+#ifdef ISDEBUG
+ if (is->is_debug)
+ printf("no_td = %x, last_td = %x\n",is->no_td, is->last_td);
+#endif
+}
+
+
+/*
+ * Controller interrupt.
+ */
+void
+isintr(unit)
+ int unit;
+{
+ register struct is_softc *is = &is_softc[unit];
+ u_short isr;
+
+ while((isr=isrdcsr(unit,0))&INTR) {
+ if (isr&ERR) {
+ if (isr&BABL){
+ printf("is%d: BABL\n",unit);
+ is->arpcom.ac_if.if_oerrors++;
+ }
+ if (isr&CERR) {
+ printf("is%d: CERR\n",unit);
+ is->arpcom.ac_if.if_collisions++;
+ }
+ if (isr&MISS) {
+ printf("is%d: MISS\n",unit);
+ is->arpcom.ac_if.if_ierrors++;
+ }
+ if (isr&MERR)
+ printf("is%d: MERR\n",unit);
+ iswrcsr(unit,0,BABL|CERR|MISS|MERR|INEA);
+ }
+ if (!(isr&RXON)) {
+ printf("is%d: !(isr&RXON)\n", unit);
+ is->arpcom.ac_if.if_ierrors++;
+ is_reset(unit);
+ return;
+ }
+ if (!(isr&TXON)) {
+ printf("is%d: !(isr&TXON)\n", unit);
+ is->arpcom.ac_if.if_oerrors++;
+ is_reset(unit);
+ return;
+ }
+
+ if (isr&RINT) {
+ /* reset watchdog timer */
+ is->arpcom.ac_if.if_timer = 0;
+ is_rint(unit);
+ }
+ if (isr&TINT) {
+ /* reset watchdog timer */
+ is->arpcom.ac_if.if_timer = 0;
+ iswrcsr(unit,0,TINT|INEA);
+ istint(unit);
+ }
+ }
+}
+
+static void
+istint(unit)
+ int unit;
+{
+ struct is_softc *is = &is_softc[unit];
+ register struct ifnet *ifp = &is->arpcom.ac_if;
+ int i,loopcount=0;
+ struct mds *cdm;
+
+ is->arpcom.ac_if.if_opackets++;
+ do {
+ if ((i=is->last_td - is->no_td) < 0)
+ i+=NTBUF;
+ cdm = (is->td+i);
+#ifdef ISDEBUG
+ if (is->is_debug)
+ printf("Trans cdm = %x\n",cdm);
+#endif
+ if (cdm->flags&OWN) {
+ if (loopcount)
+ break;
+ return;
+ }
+ loopcount++;
+ is->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+ }while(--is->no_td > 0);
+ is_start(ifp);
+
+}
+
+#define NEXTRDS \
+ if (++rmd == NRBUF) rmd=0, cdm=is->rd; else ++cdm
+
+/* only called from one place, so may as well integrate */
+static inline void is_rint(int unit)
+{
+ register struct is_softc *is=&is_softc[unit];
+ register int rmd = is->last_rd;
+ struct mds *cdm = (is->rd + rmd);
+
+ /* Out of sync with hardware, should never happen */
+
+ if (cdm->flags & OWN) {
+ printf("is%d: error: out of sync\n",unit);
+ iswrcsr(unit,0,RINT|INEA);
+ return;
+ }
+
+ /* Process all buffers with valid data */
+ while (!(cdm->flags&OWN)) {
+ /* Clear interrupt to avoid race condition */
+ iswrcsr(unit,0,RINT|INEA);
+ if (cdm->flags&ERR) {
+ if (cdm->flags&FRAM)
+ printf("is%d: FRAM\n",unit);
+ if (cdm->flags&OFLO)
+ printf("is%d: OFLO\n",unit);
+ if (cdm->flags&CRC)
+ printf("is%d: CRC\n",unit);
+ if (cdm->flags&RBUFF)
+ printf("is%d: RBUFF\n",unit);
+ }else
+ if (cdm->flags&(STP|ENP) != (STP|ENP)) {
+ do {
+ iswrcsr(unit,0,RINT|INEA);
+ cdm->mcnt = 0;
+ cdm->flags |= OWN;
+ NEXTRDS;
+ }while (!(cdm->flags&(OWN|ERR|STP|ENP)));
+ is->last_rd = rmd;
+ printf("is%d: Chained buffer\n",unit);
+ if ((cdm->flags & (OWN|ERR|STP|ENP)) != ENP) {
+ is_reset(unit);
+ return;
+ }
+ }else
+ {
+#ifdef ISDEBUG
+ if (is->is_debug)
+ recv_print(unit,is->last_rd);
+#endif
+ isread(is,is->rbuf+(BUFSIZE*rmd),(int)cdm->mcnt);
+ is->arpcom.ac_if.if_ipackets++;
+ }
+
+ cdm->flags |= OWN;
+ cdm->mcnt = 0;
+ NEXTRDS;
+#ifdef ISDEBUG
+ if (is->is_debug)
+ printf("is->last_rd = %x, cdm = %x\n",is->last_rd,cdm);
+#endif
+ } /* while */
+ is->last_rd = rmd;
+} /* is_rint */
+
+
+/*
+ * Pass a packet to the higher levels.
+ * We deal with the trailer protocol here.
+ */
+static inline void
+isread(struct is_softc *is, unsigned char *buf, int len)
+{
+ register struct ether_header *eh;
+ struct mbuf *m;
+ int off, resid;
+ register struct ifqueue *inq;
+
+ /*
+ * Deal with trailer protocol: if type is trailer type
+ * get true type from first 16-bit word past data.
+ * Remember that type was trailer by setting off.
+ */
+ eh = (struct ether_header *)buf;
+ eh->ether_type = ntohs((u_short)eh->ether_type);
+ len = len - sizeof(struct ether_header) - 4;
+#define nedataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off))))
+ if (eh->ether_type >= ETHERTYPE_TRAIL &&
+ eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
+ off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
+ if (off >= ETHERMTU) return; /* sanity */
+ eh->ether_type = ntohs(*nedataaddr(eh, off, u_short *));
+ resid = ntohs(*(nedataaddr(eh, off+2, u_short *)));
+ if (off + resid > len) return; /* sanity */
+ len = off + resid;
+ } else off = 0;
+
+ if (len == 0) return;
+
+ /*
+ * Pull packet off interface. Off is nonzero if packet
+ * has trailing header; neget will then force this header
+ * information to be at the front, but we still have to drop
+ * the type and length which are at the front of any trailer data.
+ */
+ m = isget(buf, len, off, &is->arpcom.ac_if);
+ if (m == 0) return;
+#if NBPFILTER > 0
+ /*
+ * Check if there's a BPF listener on this interface.
+ * If so, hand off the raw packet to bpf.
+ */
+ if (is->bpf) {
+ bpf_mtap(is->bpf, m);
+
+ /*
+ * Note that the interface cannot be in promiscuous mode if
+ * there are no BPF listeners. And if we are in promiscuous
+ * mode, we have to check if this packet is really ours.
+ *
+ * XXX This test does not support multicasts.
+ */
+ if ((is->arpcom.ac_if.if_flags & IFF_PROMISC) &&
+ bcmp(eh->ether_dhost, is->arpcom.ac_enaddr,
+ sizeof(eh->ether_dhost)) != 0 &&
+ bcmp(eh->ether_dhost, etherbroadcastaddr,
+ sizeof(eh->ether_dhost)) != 0) {
+
+ m_freem(m);
+ return;
+ }
+ }
+#endif
+
+
+ ether_input(&is->arpcom.ac_if, eh, m);
+}
+
+/*
+ * Supporting routines
+ */
+
+/*
+ * Pull read data off a interface.
+ * Len is length of data, with local net header stripped.
+ * Off is non-zero if a trailer protocol was used, and
+ * gives the offset of the trailer information.
+ * We copy the trailer information and then all the normal
+ * data into mbufs. When full cluster sized units are present
+ * we copy into clusters.
+ */
+struct mbuf *
+isget(buf, totlen, off0, ifp)
+ caddr_t buf;
+ int totlen, off0;
+ struct ifnet *ifp;
+{
+ struct mbuf *top, **mp, *m, *p;
+ int off = off0, len;
+ register caddr_t cp = buf;
+ char *epkt;
+
+ buf += sizeof(struct ether_header);
+ cp = buf;
+ epkt = cp + totlen;
+
+
+ if (off) {
+ cp += off + 2 * sizeof(u_short);
+ totlen -= 2 * sizeof(u_short);
+ }
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ return (0);
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = totlen;
+ m->m_len = MHLEN;
+ top = 0;
+ mp = &top;
+ while (totlen > 0) {
+ if (top) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == 0) {
+ m_freem(top);
+ return (0);
+ }
+ m->m_len = MLEN;
+ }
+ len = min(totlen, epkt - cp);
+ if (len >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if (m->m_flags & M_EXT)
+ m->m_len = len = min(len, MCLBYTES);
+ else
+ len = m->m_len;
+ } else {
+ /*
+ * Place initial small packet/header at end of mbuf.
+ */
+ if (len < m->m_len) {
+ if (top == 0 && len + max_linkhdr <= m->m_len)
+ m->m_data += max_linkhdr;
+ m->m_len = len;
+ } else
+ len = m->m_len;
+ }
+ bcopy(cp, mtod(m, caddr_t), (unsigned)len);
+ cp += len;
+ *mp = m;
+ mp = &m->m_next;
+ totlen -= len;
+ if (cp == epkt)
+ cp = buf;
+ }
+ return (top);
+}
+
+
+/*
+ * Process an ioctl request.
+ */
+int
+is_ioctl(ifp, cmd, data)
+ register struct ifnet *ifp;
+ int cmd;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *)data;
+ int unit = ifp->if_unit;
+ struct is_softc *is = &is_softc[unit];
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s, error = 0;
+
+ s = splimp();
+
+ switch (cmd) {
+
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ is_init(ifp->if_unit); /* before arpwhohas */
+ /*
+ * See if another station has *our* IP address.
+ * i.e.: There is an address conflict! If a
+ * conflict exists, a message is sent to the
+ * console.
+ */
+ ((struct arpcom *)ifp)->ac_ipaddr =
+ IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
+ break;
+#endif
+#ifdef NS
+ /*
+ * XXX - This code is probably wrong
+ */
+ case AF_NS:
+ {
+ register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+ if (ns_nullhost(*ina))
+ ina->x_host =
+ *(union ns_host *)(is->arpcom.ac_enaddr);
+ else {
+ /*
+ *
+ */
+ bcopy((caddr_t)ina->x_host.c_host,
+ (caddr_t)is->arpcom.ac_enaddr,
+ sizeof(is->arpcom.ac_enaddr));
+ }
+ /*
+ * Set new address
+ */
+ is_init(ifp->if_unit);
+ break;
+ }
+#endif
+ default:
+ is_init(ifp->if_unit);
+ break;
+ }
+ break;
+
+ case SIOCSIFFLAGS:
+ /*
+ * If interface is marked down and it is running, then stop it
+ */
+ if ((ifp->if_flags & IFF_UP) == 0 &&
+ ifp->if_flags & IFF_RUNNING) {
+ iswrcsr(unit,0,STOP);
+ ifp->if_flags &= ~IFF_RUNNING;
+ } else {
+ /*
+ * If interface is marked up and it is stopped, then start it
+ */
+ if ((ifp->if_flags & IFF_UP) &&
+ (ifp->if_flags & IFF_RUNNING) == 0)
+ is_init(ifp->if_unit);
+ }
+#ifdef ISDEBUG
+ if (ifp->if_flags & IFF_DEBUG)
+ is->is_debug = 1;
+ else
+ is->is_debug = 0;
+#endif
+#if NBPFILTER > 0
+ if (ifp->if_flags & IFF_PROMISC) {
+ /*
+ * Set promiscuous mode on interface.
+ * XXX - for multicasts to work, we would need to
+ * write 1's in all bits of multicast
+ * hashing array. For now we assume that
+ * this was done in is_init().
+ */
+ is->init_block->mode = PROM;
+ } else
+ /*
+ * XXX - for multicasts to work, we would need to
+ * rewrite the multicast hashing array with the
+ * proper hash (would have been destroyed above).
+ */
+ { /* Don't know about this */};
+#endif
+ break;
+
+#ifdef notdef
+ case SIOCGHWADDR:
+ bcopy((caddr_t)is->arpcom.ac_enaddr, (caddr_t) &ifr->ifr_data,
+ sizeof(is->arpcom.ac_enaddr));
+ break;
+#endif
+
+ default:
+ error = EINVAL;
+ }
+ (void) splx(s);
+ return (error);
+}
+
+#ifdef ISDEBUG
+void
+recv_print(unit,no)
+ int unit,no;
+{
+ register struct is_softc *is=&is_softc[unit];
+ struct mds *rmd;
+ int len,i,printed=0;
+
+ rmd = (is->rd+no);
+ len = rmd->mcnt;
+ printf("is%d: Receive buffer %d, len = %d\n",unit,no,len);
+ printf("is%d: Status %x\n",unit,isrdcsr(unit,0));
+ for (i=0; i<len; i++) {
+ if (!printed) {
+ printed=1;
+ printf("is%d: data: ", unit);
+ }
+ printf("%x ",*(is->rbuf+(BUFSIZE*no)+i));
+ }
+ if (printed)
+ printf("\n");
+}
+
+void
+xmit_print(unit,no)
+ int unit,no;
+{
+ register struct is_softc *is=&is_softc[unit];
+ struct mds *rmd;
+ int i, printed=0;
+ u_short len;
+
+ rmd = (is->td+no);
+ len = -(rmd->bcnt);
+ printf("is%d: Transmit buffer %d, len = %d\n",unit,no,len);
+ printf("is%d: Status %x\n",unit,isrdcsr(unit,0));
+ printf("is%d: addr %x, flags %x, bcnt %x, mcnt %x\n",
+ unit,rmd->addr,rmd->flags,rmd->bcnt,rmd->mcnt);
+ for (i=0; i<len; i++) {
+ if (!printed) {
+ printed = 1;
+ printf("is%d: data: ", unit);
+ }
+ printf("%x ",*(is->tbuf+(BUFSIZE*no)+i));
+ }
+ if (printed)
+ printf("\n");
+}
+#endif /* ISDEBUG */
+
+#endif /* NIS > 0 */
diff --git a/sys/i386/isa/if_isreg.h b/sys/i386/isa/if_isreg.h
new file mode 100644
index 0000000..b4b4ece
--- /dev/null
+++ b/sys/i386/isa/if_isreg.h
@@ -0,0 +1,129 @@
+/*
+ * Isolan AT 4141-0 Ethernet driver header file
+ * Isolink 4110
+ *
+ * By Paul Richards
+ *
+ * Copyright (C) 1993, Paul Richards. This software may be used, modified,
+ * copied, distributed, and sold, in both source and binary form provided
+ * that the above copyright and these terms are retained. Under no
+ * circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ *
+ * $Id: if_isreg.h,v 1.3 1993/10/24 04:27:00 paul Exp $
+ */
+
+/*
+ * Initialize multicast address hashing registers to accept
+ * all multicasts (only used when in promiscuous mode)
+ */
+#if NBPFILTER > 0
+#define MULTI_INIT_ADDR 0xff
+#else
+#define MULTI_INIT_ADDR 0
+#endif
+
+/* Declarations specific to this driver */
+#define NTBUF 2
+#define TLEN 1
+#define NRBUF 8
+#define RLEN 3
+#define BUFSIZE 1518
+#define BICC_RDP 0xc
+#define BICC_RAP 0xe
+#define NE2100_RDP 0x10
+#define NE2100_RAP 0x12
+
+/* Board types */
+#define BICC 1
+#define NE2100 2
+
+/* Am7990 or Am79960 */
+#define LANCE 1
+#define LANCE_MASK 0x07
+#define PCnet_ISA 2
+#define PCnet_ISA_MASK 0x0
+
+
+/* Control and status register 0 flags */
+
+#define ERR 0x8000
+#define BABL 0x4000
+#define CERR 0x2000
+#define MISS 0x1000
+#define MERR 0x0800
+#define RINT 0x0400
+#define TINT 0x0200
+#define IDON 0x0100
+#define INTR 0x0080
+#define INEA 0x0040
+#define RXON 0x0020
+#define TXON 0x0010
+#define TDMD 0x0008
+#define STOP 0x0004
+#define STRT 0x0002
+#define INIT 0x0001
+
+/* Coontrol and status register 3 flags */
+
+#define BSWP 0x0004
+#define ACON 0x0002
+#define BCON 0x0001
+
+/* Initialisation block (must be on word boundary) */
+
+struct init_block {
+ u_short mode; /* Mode register */
+ u_char padr[6]; /* Ethernet address */
+ u_char ladrf[8]; /* Logical address filter (multicast) */
+ u_short rdra; /* Low order pointer to receive ring */
+ u_short rlen; /* High order pointer and no. rings */
+ u_short tdra; /* Low order pointer to transmit ring */
+ u_short tlen; /* High order pointer and no rings */
+ };
+
+/* Mode settings */
+
+#define PROM 0x8000 /* Promiscuous */
+#define INTL 0x0040 /* Internal loopback */
+#define DRTY 0x0020 /* Disable retry */
+#define COLL 0x0010 /* Force collision */
+#define DTCR 0x0008 /* Disable transmit crc */
+#define LOOP 0x0004 /* Loop back */
+#define DTX 0x0002 /* Disable transmitter */
+#define DRX 0x0001 /* Disable receiver */
+
+/* Message descriptor structure */
+
+struct mds {
+ u_short addr;
+ u_short flags;
+ u_short bcnt;
+ u_short mcnt;
+ };
+
+/* Receive ring status flags */
+
+#define OWN 0x8000 /* Owner bit, 0=host, 1=Lance */
+#define MDERR 0x4000 /* Error */
+#define FRAM 0x2000 /* Framing error error */
+#define OFLO 0x1000 /* Silo overflow */
+#define CRC 0x0800 /* CRC error */
+#define RBUFF 0x0400 /* Buffer error */
+#define STP 0x0200 /* Start of packet */
+#define ENP 0x0100 /* End of packet */
+
+/* Transmit ring flags */
+
+#define MORE 0x1000 /* More than 1 retry */
+#define ONE 0x0800 /* One retry */
+#define DEF 0x0400 /* Deferred transmit */
+
+/* Transmit errors */
+
+#define TBUFF 0x8000 /* Buffer error */
+#define UFLO 0x4000 /* Silo underflow */
+#define LCOL 0x1000 /* Late collision */
+#define LCAR 0x0800 /* Loss of carrier */
+#define RTRY 0x0400 /* Tried 16 times */
diff --git a/sys/i386/isa/if_ze.c b/sys/i386/isa/if_ze.c
new file mode 100644
index 0000000..6ffb96c
--- /dev/null
+++ b/sys/i386/isa/if_ze.c
@@ -0,0 +1,1951 @@
+/*-
+ * TODO:
+ * [1] integrate into current if_ed.c
+ * [2] parse tuples to find out where to map the shared memory buffer,
+ * and what to write into the configuration register
+ * [3] move pcic-specific code into a separate module.
+ *
+ * Device driver for IBM PCMCIA Credit Card Adapter for Ethernet,
+ * if_ze.c
+ *
+ * Based on the Device driver for National Semiconductor DS8390 ethernet
+ * adapters by David Greenman. Modifications for PCMCIA by Keith Moore.
+ * Adapted for FreeBSD 1.1.5 by Jordan Hubbard.
+ *
+ * Currently supports only the IBM Credit Card Adapter for Ethernet, but
+ * could probably work with other PCMCIA cards also, if it were modified
+ * to get the locations of the PCMCIA configuration option register (COR)
+ * by parsing the configuration tuples, rather than by hard-coding in
+ * the value expected by IBM's card.
+ *
+ * Sources for data on the PCMCIA/IBM CCAE specific portions of the driver:
+ *
+ * [1] _Local Area Network Credit Card Adapters Technical Reference_,
+ * IBM Corp., SC30-3585-00, part # 33G9243.
+ * [2] "pre-alpha" PCMCIA support code for Linux by Barry Jaspan.
+ * [3] Intel 82536SL PC Card Interface Controller Data Sheet, Intel
+ * Order Number 290423-002
+ * [4] National Semiconductor DP83902A ST-NIC (tm) Serial Network
+ * Interface Controller for Twisted Pair data sheet.
+ *
+ *
+ * Copyright (C) 1993, David Greenman. This software may be used, modified,
+ * copied, distributed, and sold, in both source and binary form provided
+ * that the above copyright and these terms are retained. Under no
+ * circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ */
+
+#include "ze.h"
+#if NZE > 0
+#include "bpfilter.h"
+
+#include "param.h"
+#include "systm.h"
+#include "errno.h"
+#include "ioctl.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "syslog.h"
+
+#include "net/if.h"
+#include "net/if_dl.h"
+#include "net/if_types.h"
+#include "net/netisr.h"
+
+#ifdef INET
+#include "netinet/in.h"
+#include "netinet/in_systm.h"
+#include "netinet/in_var.h"
+#include "netinet/ip.h"
+#include "netinet/if_ether.h"
+#endif
+
+#ifdef NS
+#include "netns/ns.h"
+#include "netns/ns_if.h"
+#endif
+
+#if NBPFILTER > 0
+#include "net/bpf.h"
+#include "net/bpfdesc.h"
+#endif
+
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/if_zereg.h"
+
+#include "i386/include/pio.h"
+
+
+
+/*****************************************************************************
+ * pcmcia controller chip (PCIC) support *
+ * (eventually, move this to a separate file) *
+ *****************************************************************************/
+#include "ic/i82365.h"
+
+/*
+ * Each PCIC chip (82365SL or clone) can handle two card slots, and there
+ * can be up to four PCICs in a system. (On some machines, not all of the
+ * address lines are decoded, so a card may appear to be in more than one
+ * slot.)
+ */
+#define MAXSLOT 8
+
+/*
+ * To access a register on the PCIC for a particular slot, you
+ * first write the correct OFFSET value for that slot in the
+ * INDEX register for the PCIC controller. You then read or write
+ * the value from or to the DATA register for that controller.
+ *
+ * The first pair of chips shares I/O addresss for DATA and INDEX,
+ * as does the second pair. (To the programmer, it looks like each
+ * pair is a single chip.) The i/o port addresses are hard-wired
+ * into the PCIC; so the following addresses should be valid for
+ * any machine that uses this chip.
+ */
+
+#define PCIC_INDEX_0 0x3E0 /* index reg, chips 0 and 1 */
+#define PCIC_DATA_0 0x3E1 /* data register, chips 0 and 1 */
+#define PCIC_INDEX_1 0x3E2 /* index reg, chips 1 and 2 */
+#define PCIC_DATA_1 0x3E3 /* data register, chips 1 and 2 */
+
+/*
+ * Given a slot number, calculate the INDEX and DATA registers
+ * to talk to that slot. OFFSET is added to the register number
+ * to address the registers for a particular slot.
+ */
+#define INDEX(slot) ((slot) < 4 ? PCIC_INDEX_0 : PCIC_INDEX_1)
+#define DATA(slot) ((slot) < 4 ? PCIC_DATA_0 : PCIC_DATA_1)
+#define OFFSET(slot) ((slot) % 4 * 0x40)
+
+/*
+ * There are 5 sets (windows) of memory mapping registers on the PCIC chip
+ * for each slot, numbered 0..4.
+ *
+ * They start at 10/50 hex within the chip's register space (not system
+ * I/O space), and are eight addresses apart. These are actually pairs of
+ * 8-bit-wide registers (low byte first, then high byte) since the
+ * address fields are actually 12 bits long. The upper bits are used
+ * for other things like 8/16-bit select and wait states.
+ *
+ * Memory mapping registers include start/stop addresses to define the
+ * region to be mapped (in terms of system memory addresses), and
+ * an offset register to allow for translation from system space
+ * to card space. The lower 12 bits aren't included in these, so memory is
+ * mapped in 4K chunks.
+ */
+#define MEM_START_ADDR(window) (((window) * 0x08) + 0x10)
+#define MEM_STOP_ADDR(window) (((window) * 0x08) + 0x12)
+#define MEM_OFFSET(window) (((window) * 0x08) + 0x14)
+/*
+ * this bit gets set in the address window enable register (PCIC_ADDRWINE)
+ * to enable a particular address window.
+ */
+#define MEM_ENABLE_BIT(window) ((1) << (window))
+
+/*
+ * There are two i/o port addressing windows. I/O ports cannot be
+ * relocated within system i/o space (unless the card doesn't decode
+ * all of the address bits); unlike card memory, there is no address
+ * translation offset.
+ */
+#define IO_START_ADDR(window) ((window) ? PCIC_IO1_STL : PCIC_IO0_STL)
+#define IO_STOP_ADDR(window) ((window) ? PCIC_IO1_SPL : PCIC_IO0_SPL)
+#define IO_ENABLE_BIT(window) ((window) ? PCIC_IO1_EN : PCIC_IO0_EN)
+#define IO_CS16_BIT(window) ((window) ? PCIC_IO1_CS16 : PCIC_IO0_CS16)
+
+/*
+ * read a byte from a pcic register for a particular slot
+ */
+static inline unsigned char
+pcic_getb (int slot, int reg)
+{
+ outb (INDEX(slot), OFFSET (slot) + reg);
+ return inb (DATA (slot));
+}
+
+/*
+ * write a byte to a pcic register for a particular slot
+ */
+static inline void
+pcic_putb (int slot, int reg, unsigned char val)
+{
+ outb (INDEX(slot), OFFSET (slot) + reg);
+ outb (DATA (slot), val);
+}
+
+/*
+ * read a word from a pcic register for a particular slot
+ */
+static inline unsigned short
+pcic_getw (int slot, int reg)
+{
+ return pcic_getb (slot, reg) | (pcic_getb (slot, reg+1) << 8);
+}
+
+/*
+ * write a word to a pcic register at a particular slot
+ */
+static inline void
+pcic_putw (int slot, int reg, unsigned short val)
+{
+ pcic_putb (slot, reg, val & 0xff);
+ pcic_putb (slot, reg + 1, (val >> 8) & 0xff);
+}
+
+static void
+pcic_print_regs (int slot)
+{
+ int i, j;
+
+ for (i = 0; i < 0x40; i += 16) {
+ for (j = 0; j < 16; ++j)
+ printf ("%02x ", pcic_getb (slot, i + j));
+ printf ("\n");
+ }
+}
+
+/*
+ * map a portion of the card's memory space into system memory
+ * space.
+ *
+ * slot = # of the slot the card is plugged into
+ * window = which pcic memory map registers to use (0..4)
+ * sys_addr = base system PHYSICAL memory address where we want it. must
+ * be on an appropriate boundary (lower 12 bits are zero).
+ * card_addr = the base address of the card's memory to correspond
+ * to sys_addr
+ * length = length of the segment to map (may be rounded up as necessary)
+ * type = which card memory space to map (attribute or shared)
+ * width = 1 for byte-wide mapping; 2 for word (16-bit) mapping.
+ */
+
+enum memtype { COMMON, ATTRIBUTE };
+
+static void
+pcic_map_memory (int slot, int window, unsigned long sys_addr,
+ unsigned long card_addr, unsigned long length,
+ enum memtype type, int width)
+{
+ unsigned short offset;
+ unsigned short mem_start_addr;
+ unsigned short mem_stop_addr;
+
+ sys_addr >>= 12;
+ card_addr >>= 12;
+ length >>= 12;
+ /*
+ * compute an offset for the chip such that
+ * (sys_addr + offset) = card_addr
+ * but the arithmetic is done modulo 2^14
+ */
+ offset = (card_addr - sys_addr) & 0x3FFF;
+ /*
+ * now OR in the bit for "attribute memory" if necessary
+ */
+ if (type == ATTRIBUTE) {
+ offset |= (PCIC_REG << 8);
+ /* REG == "region active" pin on card */
+ }
+ /*
+ * okay, set up the chip memory mapping registers, and turn
+ * on the enable bit for this window.
+ * if we are doing 16-bit wide accesses (width == 2),
+ * turn on the appropriate bit.
+ *
+ * XXX for now, we set all of the wait state bits to zero.
+ * Not really sure how they should be set.
+ */
+ mem_start_addr = sys_addr & 0xFFF;
+ if (width == 2)
+ mem_start_addr |= (PCIC_DATA16 << 8);
+ mem_stop_addr = (sys_addr + length) & 0xFFF;
+
+ pcic_putw (slot, MEM_START_ADDR(window), mem_start_addr);
+ pcic_putw (slot, MEM_STOP_ADDR(window), mem_stop_addr);
+ pcic_putw (slot, MEM_OFFSET(window), offset);
+ /*
+ * Assert the bit (PCIC_MEMCS16) that says to decode all of
+ * the address lines.
+ */
+ pcic_putb (slot, PCIC_ADDRWINE,
+ pcic_getb (slot, PCIC_ADDRWINE) |
+ MEM_ENABLE_BIT(window) | PCIC_MEMCS16);
+}
+
+static void
+pcic_unmap_memory (int slot, int window)
+{
+ /*
+ * seems like we need to turn off the enable bit first, after which
+ * we can clear the registers out just to be sure.
+ */
+ pcic_putb (slot, PCIC_ADDRWINE,
+ pcic_getb (slot, PCIC_ADDRWINE) & ~MEM_ENABLE_BIT(window));
+ pcic_putw (slot, MEM_START_ADDR(window), 0);
+ pcic_putw (slot, MEM_STOP_ADDR(window), 0);
+ pcic_putw (slot, MEM_OFFSET(window), 0);
+}
+
+/*
+ * map a range of addresses into system i/o space
+ * (no translation of i/o addresses is possible)
+ *
+ * 'width' is:
+ * + 0 to tell the PCIC to generate the ISA IOCS16* signal from
+ * the PCMCIA IOIS16* signal.
+ * + 1 to select 8-bit width
+ * + 2 to select 16-bit width
+ */
+
+static void
+pcic_map_io (int slot, int window, unsigned short base, unsigned short length,
+ unsigned short width)
+{
+ unsigned char x;
+
+ pcic_putw (slot, IO_START_ADDR(window), base);
+ pcic_putw (slot, IO_STOP_ADDR(window), base+length-1);
+ /*
+ * select the bits that determine whether
+ * an i/o operation is 8 or 16 bits wide
+ */
+ x = pcic_getb (slot, PCIC_IOCTL);
+ switch (width) {
+ case 0: /* PCMCIA card decides */
+ if (window)
+ x = (x & 0xf0) | PCIC_IO1_CS16;
+ else
+ x = (x & 0x0f) | PCIC_IO0_CS16;
+ break;
+ case 1: /* 8 bits wide */
+ break;
+ case 2: /* 16 bits wide */
+ if (window)
+ x = (x & 0xf0) | PCIC_IO1_16BIT;
+ else
+ x = (x & 0x0f) | PCIC_IO0_16BIT;
+ break;
+ }
+ pcic_putb (slot, PCIC_IOCTL, x);
+ pcic_putb (slot, PCIC_ADDRWINE,
+ pcic_getb (slot, PCIC_ADDRWINE) | IO_ENABLE_BIT(window));
+}
+
+#ifdef TEST
+static void
+pcic_unmap_io (int slot, int window)
+{
+ pcic_putb (slot, PCIC_ADDRWINE,
+ pcic_getb (slot, PCIC_ADDRWINE) & ~IO_ENABLE_BIT(window));
+ pcic_putw (slot, IO_START_ADDR(window), 0);
+ pcic_putw (slot, IO_STOP_ADDR(window), 0);
+}
+#endif /* TEST */
+
+/*
+ * tell the PCIC which irq we want to use. only the following are legal:
+ * 3, 4, 5, 7, 9, 10, 11, 12, 14, 15
+ *
+ * NB: 'irq' is an interrupt NUMBER, not a MASK as in struct isa_device.
+ */
+
+static void
+pcic_map_irq (int slot, int irq)
+{
+ if (irq < 3 || irq == 6 || irq == 8 || irq == 13 || irq > 15) {
+ printf ("ze: pcic_map_irq (slot %d): illegal irq %d\n", slot, irq);
+ return;
+ }
+ pcic_putb (slot, PCIC_INT_GEN,
+ pcic_getb (slot, PCIC_INT_GEN) | (irq & 0x0F));
+}
+
+static void
+pcic_power_on (int slot)
+{
+ pcic_putb (slot, PCIC_POWER,
+ pcic_getb (slot, PCIC_POWER) | PCIC_DISRST | PCIC_PCPWRE);
+ DELAY (50000);
+ pcic_putb (slot, PCIC_POWER,
+ pcic_getb (slot, PCIC_POWER) | PCIC_OUTENA);
+}
+
+static void
+pcic_reset (int slot)
+{
+ /* assert RESET (by clearing a bit!), wait a bit, and de-assert it */
+ pcic_putb (slot, PCIC_INT_GEN,
+ pcic_getb (slot, PCIC_INT_GEN) & ~PCIC_CARDRESET);
+ DELAY (50000);
+ pcic_putb (slot, PCIC_INT_GEN,
+ pcic_getb (slot, PCIC_INT_GEN) | PCIC_CARDRESET);
+}
+
+
+/*****************************************************************************
+ * Driver for Ethernet Adapter *
+ *****************************************************************************/
+/*
+ * ze_softc: per line info and status
+ */
+struct ze_softc {
+ struct arpcom arpcom; /* ethernet common */
+
+ char *type_str; /* pointer to type string */
+ char *mau; /* type of media access unit */
+#if 0
+ u_char vendor; /* interface vendor */
+ u_char type; /* interface type code */
+#endif
+
+#if 0
+ u_short vector; /* interrupt vector */
+#endif
+ u_short nic_addr; /* NIC (DS8390) I/O bus address */
+
+ caddr_t smem_start; /* shared memory start address */
+ caddr_t smem_end; /* shared memory end address */
+ u_long smem_size; /* total shared memory size */
+ caddr_t smem_ring; /* start of RX ring-buffer (in smem) */
+
+ caddr_t bpf; /* BPF "magic cookie" */
+
+ u_char memwidth; /* width of access to card mem 8 or 16 */
+ u_char xmit_busy; /* transmitter is busy */
+ u_char txb_cnt; /* Number of transmit buffers */
+ u_char txb_next; /* Pointer to next buffer ready to xmit */
+ u_short txb_next_len; /* next xmit buffer length */
+ u_char data_buffered; /* data has been buffered in interface memory */
+ u_char tx_page_start; /* first page of TX buffer area */
+
+ u_char rec_page_start; /* first page of RX ring-buffer */
+ u_char rec_page_stop; /* last page of RX ring-buffer */
+ u_char next_packet; /* pointer to next unread RX packet */
+} ze_softc[NZE];
+
+int ze_attach(), ze_ioctl(), ze_probe();
+void ze_init(), ze_start(), ze_stop(), ze_intr();
+void ze_reset(), ze_watchdog(), ze_get_packet();
+
+static inline void ze_rint();
+static inline void ze_xmit();
+static inline char *ze_ring_copy();
+
+extern int ether_output();
+
+struct isa_driver zedriver = {
+ ze_probe,
+ ze_attach,
+ "ze"
+};
+
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1518
+#define ETHER_ADDR_LEN 6
+#define ETHER_HDR_SIZE 14
+
+static unsigned char enet_addr[6];
+static unsigned char card_info[256];
+
+#define CARD_INFO "IBM Corp.~Ethernet~0933495"
+
+/*
+ * scan the card information structure looking for the version/product info
+ * tuple. when we find it, compare it to the string we are looking for.
+ * return 1 if we find it, 0 otherwise.
+ */
+
+static int
+ze_check_cis (unsigned char *scratch)
+{
+ int i,j,k;
+
+ card_info[0] = '\0';
+ i = 0;
+ while (scratch[i] != 0xff && i < 1024) {
+ unsigned char link = scratch[i+2];
+
+#if 0
+ printf ("[%02x] %02x ", i, link);
+ for (j = 4; j < 2 * link + 4 && j < 32; j += 2)
+ printf ("%02x ", scratch[j + i]);
+ printf ("\n");
+#endif
+ if (scratch[i] == 0x15) {
+ /*
+ * level 1 version/product info
+ * copy to card_info, translating '\0' to '~'
+ */
+ k = 0;
+ for (j = i+8; scratch[j] != 0xff; j += 2)
+ card_info[k++] = scratch[j] == '\0' ? '~' : scratch[j];
+ card_info[k++] = '\0';
+ return (memcmp (card_info, CARD_INFO, sizeof(CARD_INFO)-1) == 0);
+ }
+ i += 4 + 2 * link;
+ }
+ return 0;
+}
+
+/*
+ * Probe each slot looking for an IBM Credit Card Adapter for Ethernet
+ * For each card that we find, map its card information structure
+ * into system memory at 'scratch' and see whether it's one of ours.
+ * Return the slot number if we find a card, or -1 otherwise.
+ *
+ * Side effects:
+ * + On success, leaves CIS mapped into memory at 'scratch';
+ * caller must free it.
+ * + On success, leaves ethernet address in enet_addr.
+ * + Leaves product/vendor id of last card probed in 'card_info'
+ */
+
+static int
+ze_find_adapter (unsigned char *scratch)
+{
+ int slot;
+
+ for (slot = 0; slot < MAXSLOT; ++slot) {
+ /*
+ * see if there's a PCMCIA controller here
+ * Intel PCMCIA controllers use 0x82 and 0x83
+ * IBM clone chips use 0x88 and 0x89, apparently
+ */
+ unsigned char idbyte = pcic_getb (slot, PCIC_ID_REV);
+
+ if (idbyte != 0x82 && idbyte != 0x83 &&
+ idbyte != 0x88 && idbyte != 0x89) {
+#if 0
+ printf ("ibmccae: pcic slot %d: wierd id/rev code 0x%02x\n",
+ slot, idbyte);
+#endif
+ continue;
+ }
+ if ((pcic_getb (slot, PCIC_STATUS) & PCIC_CD) != PCIC_CD) {
+ printf ("ze: slot %d: no card in slot\n", slot);
+ /* no card in slot */
+ continue;
+ }
+ pcic_power_on (slot);
+ pcic_reset (slot);
+ /*
+ * map the card's attribute memory and examine its
+ * card information structure tuples for something
+ * we recognize.
+ */
+ pcic_map_memory (slot, 0, kvtop (scratch), 0L,
+ 0xFFFL, ATTRIBUTE, 1);
+
+ if ((ze_check_cis (scratch)) > 0) {
+ /* found it */
+ printf ("ze: found card in slot %d\n", slot);
+ return slot;
+ }
+ else
+ printf ("ze: pcmcia slot %d: %s\n", slot, card_info);
+ pcic_unmap_memory (slot, 0);
+ }
+ return -1;
+}
+
+
+/*
+ * macros to handle casting unsigned long to (char *) so we can
+ * read/write into physical memory space.
+ */
+
+#define PEEK(addr) (*((unsigned char *)(addr)))
+#define POKE(addr,val) do { PEEK(addr) = (val); } while (0)
+
+/*
+ * Determine if the device is present
+ *
+ * on entry:
+ * a pointer to an isa_device struct
+ * on exit:
+ * NULL if device not found
+ * or # of i/o addresses used (if found)
+ */
+int
+ze_probe(isa_dev)
+ struct isa_device *isa_dev;
+{
+ struct ze_softc *sc = &ze_softc[isa_dev->id_unit];
+ int i, x;
+ u_int memsize;
+ u_char iptr, memwidth, sum, tmp;
+ int slot;
+
+ if ((slot = ze_find_adapter (isa_dev->id_maddr)) < 0)
+ return NULL;
+
+ /*
+ * okay, we found a card, so set it up
+ */
+ /*
+ * Inhibit 16 bit memory delay.
+ * POINTETH.SYS apparently does this, for what reason I don't know.
+ */
+ pcic_putb (slot, PCIC_CDGC,
+ pcic_getb (slot, PCIC_CDGC) | PCIC_16_DL_INH);
+ /*
+ * things to map
+ * (1) card's EEPROM is already mapped by the find_adapter routine
+ * but we still need to get the card's ethernet address.
+ * after that we unmap that part of attribute memory.
+ * (2) card configuration registers need to be mapped in so we
+ * can set the configuration and socket # registers.
+ * (3) shared memory packet buffer
+ * (4) i/o ports
+ * (5) IRQ
+ */
+ /*
+ * Sigh. Location of the ethernet address isn't documented in [1].
+ * It was derived by doing a hex dump of all of attribute memory
+ * and looking for the IBM vendor prefix.
+ */
+ enet_addr[0] = PEEK(isa_dev->id_maddr+0xff0);
+ enet_addr[1] = PEEK(isa_dev->id_maddr+0xff2);
+ enet_addr[2] = PEEK(isa_dev->id_maddr+0xff4);
+ enet_addr[3] = PEEK(isa_dev->id_maddr+0xff6);
+ enet_addr[4] = PEEK(isa_dev->id_maddr+0xff8);
+ enet_addr[5] = PEEK(isa_dev->id_maddr+0xffa);
+ pcic_unmap_memory (slot, 0);
+
+ /*
+ * (2) map card configuration registers. these are offset
+ * in card memory space by 0x20000. normally we could get
+ * this offset from the card information structure, but I'm
+ * too lazy and am not quite sure if I understand the CIS anyway.
+ *
+ * XXX IF YOU'RE TRYING TO PORT THIS DRIVER FOR A DIFFERENT
+ * PCMCIA CARD, the most likely thing to change is the constant
+ * 0x20000 in the next statement. Oh yes, also change the
+ * card id string that we probe for.
+ */
+ pcic_map_memory (slot, 0, kvtop (isa_dev->id_maddr), 0x20000, 8L,
+ ATTRIBUTE, 1);
+ POKE(isa_dev->id_maddr, 0x80); /* reset the card (how long?) */
+ DELAY (10000);
+ /*
+ * Set the configuration index. According to [1], the adapter won't
+ * respond to any i/o signals until we do this; it uses the
+ * Memory Only interface (whatever that is; it's not documented).
+ * Also turn on "level" (not pulse) interrupts.
+ *
+ * XXX probably should init the socket and copy register also,
+ * so that we can deal with multiple instances of the same card.
+ */
+ POKE(isa_dev->id_maddr, 0x41);
+ pcic_unmap_memory (slot, 0);
+
+ /*
+ * (3) now map in the shared memory buffer. This has to be mapped
+ * as words, not bytes, and on a 16k boundary. The offset value
+ * was derived by installing IBM's POINTETH.SYS under DOS and
+ * looking at the PCIC registers; it's not documented in IBM's
+ * tech ref manual ([1]).
+ */
+ pcic_map_memory (slot, 0, kvtop (isa_dev->id_maddr), 0x4000L, 0x4000L,
+ COMMON, 2);
+
+ /*
+ * (4) map i/o ports.
+ *
+ * XXX is it possible that the config file leaves this unspecified,
+ * in which case we have to pick one?
+ *
+ * At least one PCMCIA device driver I'v seen maps a block
+ * of 32 consecutive i/o ports as two windows of 16 ports each.
+ * Maybe some other pcic chips are restricted to 16-port windows;
+ * the 82365SL doesn't seem to have that problem. But since
+ * we have an extra window anyway...
+ */
+#ifdef SHARED_MEMORY
+ pcic_map_io (slot, 0, isa_dev->id_iobase, 32, 1);
+#else
+ pcic_map_io (slot, 0, isa_dev->id_iobase, 16, 1);
+ pcic_map_io (slot, 1, isa_dev->id_iobase+16, 16, 2);
+#endif /* SHARED_MEMORY */
+
+ /*
+ * (5) configure the card for the desired interrupt
+ *
+ * XXX is it possible that the config file leaves this unspecified?
+ */
+ pcic_map_irq (slot, ffs (isa_dev->id_irq) - 1);
+
+ /* tell the PCIC that this is an I/O card (not memory) */
+ pcic_putb (slot, PCIC_INT_GEN,
+ pcic_getb (slot, PCIC_INT_GEN) | PCIC_CARDTYPE);
+
+#if 0
+ /* tell the PCIC to use level-mode interrupts */
+ /* XXX this register may not be present on all controllers */
+ pcic_putb (slot, PCIC_GLO_CTRL,
+ pcic_getb (slot, PCIC_GLO_CTRL) | PCIC_LVL_MODE);
+#endif
+
+#if 0
+ pcic_print_regs (slot);
+#endif
+ /*
+ * Setup i/o addresses
+ */
+ sc->nic_addr = isa_dev->id_iobase;
+#if 0
+ sc->vector = isa_dev->id_irq;
+#endif
+ sc->smem_start = (caddr_t)isa_dev->id_maddr;
+
+#if 0
+ sc->vendor = ZE_VENDOR_IBM;
+ sc->type = xxx;
+#endif
+
+ /* reset card to force it into a known state */
+ tmp = inb (isa_dev->id_iobase + ZE_RESET);
+ DELAY(5000);
+ outb (isa_dev->id_iobase + ZE_RESET, tmp);
+ DELAY(5000);
+
+ /*
+ * query MAM bit in misc register for 10base2
+ */
+ tmp = inb (isa_dev->id_iobase + ZE_MISC);
+ sc->mau = tmp & 0x09 ? "10base2" : "10baseT";
+
+ /* set width/size */
+ sc->type_str = "IBM PCMCIA";
+ memsize = 16*1024;
+ sc->memwidth = 16;
+
+ /* allocate 1 xmit buffer */
+ sc->smem_ring = sc->smem_start + (ZE_PAGE_SIZE * ZE_TXBUF_SIZE);
+ sc->txb_cnt = 1;
+ sc->rec_page_start = ZE_TXBUF_SIZE + ZE_PAGE_OFFSET;
+ sc->smem_size = memsize;
+ sc->smem_end = sc->smem_start + memsize;
+ sc->rec_page_stop = memsize / ZE_PAGE_SIZE + ZE_PAGE_OFFSET;
+ sc->tx_page_start = ZE_PAGE_OFFSET;
+
+ /* get station address */
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ sc->arpcom.ac_enaddr[i] = enet_addr[i];
+
+ isa_dev->id_msize = memsize;
+ return 32;
+}
+
+/*
+ * Install interface into kernel networking data structures
+ */
+int
+ze_attach(isa_dev)
+ struct isa_device *isa_dev;
+{
+ struct ze_softc *sc = &ze_softc[isa_dev->id_unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+
+ /*
+ * Set interface to stopped condition (reset)
+ */
+ ze_stop(isa_dev->id_unit);
+
+ /*
+ * Initialize ifnet structure
+ */
+ ifp->if_unit = isa_dev->id_unit;
+ ifp->if_name = "ze" ;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_init = ze_init;
+ ifp->if_output = ether_output;
+ ifp->if_start = ze_start;
+ ifp->if_ioctl = ze_ioctl;
+ ifp->if_reset = ze_reset;
+ ifp->if_watchdog = ze_watchdog;
+
+ /*
+ * Set default state for LLC0 flag (used to disable the tranceiver
+ * for AUI operation), based on compile-time config option.
+ */
+ if (isa_dev->id_flags & ZE_FLAGS_DISABLE_TRANCEIVER)
+ ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS
+ | IFF_LLC0);
+ else
+ ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS);
+
+ /*
+ * Attach the interface
+ */
+ if_attach(ifp);
+
+ /*
+ * Search down the ifa address list looking for the AF_LINK type entry
+ */
+ ifa = ifp->if_addrlist;
+ while ((ifa != 0) && (ifa->ifa_addr != 0) &&
+ (ifa->ifa_addr->sa_family != AF_LINK))
+ ifa = ifa->ifa_next;
+ /*
+ * If we find an AF_LINK type entry we fill in the hardware address.
+ * This is useful for netstat(1) to keep track of which interface
+ * is which.
+ */
+ if ((ifa != 0) && (ifa->ifa_addr != 0)) {
+ /*
+ * Fill in the link-level address for this interface
+ */
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ sdl->sdl_type = IFT_ETHER;
+ sdl->sdl_alen = ETHER_ADDR_LEN;
+ sdl->sdl_slen = 0;
+ bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN);
+ }
+
+ /*
+ * Print additional info when attached
+ */
+ printf("ze%d: address %s, type %s (%dbit)%s, MAU %s\n",
+ isa_dev->id_unit,
+ ether_sprintf(sc->arpcom.ac_enaddr), sc->type_str,
+ sc->memwidth,
+ (ifp->if_flags & IFF_LLC0 ? " [tranceiver disabled]" : ""),
+ sc->mau);
+
+ /*
+ * If BPF is in the kernel, call the attach for it
+ */
+#if NBPFILTER > 0
+ bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+ return 1;
+}
+
+/*
+ * Reset interface.
+ */
+void
+ze_reset(unit)
+ int unit;
+{
+ int s;
+
+ s = splnet();
+
+ /*
+ * Stop interface and re-initialize.
+ */
+ ze_stop(unit);
+ ze_init(unit);
+
+ (void) splx(s);
+}
+
+/*
+ * Take interface offline.
+ */
+void
+ze_stop(unit)
+ int unit;
+{
+ struct ze_softc *sc = &ze_softc[unit];
+ int n = 5000;
+
+ /*
+ * Stop everything on the interface, and select page 0 registers.
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STP);
+
+ /*
+ * Wait for interface to enter stopped state, but limit # of checks
+ * to 'n' (about 5ms). It shouldn't even take 5us on modern
+ * DS8390's, but just in case it's an old one.
+ */
+ while (((inb(sc->nic_addr + ZE_P0_ISR) & ZE_ISR_RST) == 0) && --n);
+
+}
+
+/*
+ * Device timeout/watchdog routine. Entered if the device neglects to
+ * generate an interrupt after a transmit has been started on it.
+ */
+void
+ze_watchdog(unit)
+ int unit;
+{
+#if 1
+ struct ze_softc *sc = &ze_softc[unit];
+ u_char isr, imr;
+ u_short imask;
+
+ /* select page zero */
+ outb (sc->nic_addr + ZE_P0_CR,
+ (inb (sc->nic_addr + ZE_P0_CR) & 0x3f) | ZE_CR_PAGE_0);
+
+ /* read interrupt status register */
+ isr = inb (sc->nic_addr + ZE_P0_ISR) & 0xff;
+
+ /* select page two */
+ outb (sc->nic_addr + ZE_P0_CR,
+ (inb (sc->nic_addr + ZE_P0_CR) & 0x3f) | ZE_CR_PAGE_2);
+
+ /* read interrupt mask register */
+ imr = inb (sc->nic_addr + ZE_P2_IMR) & 0xff;
+
+ imask = inb(IO_ICU2) << 8 | inb(IO_ICU1);
+
+ log (LOG_ERR, "ze%d: device timeout, isr=%02x, imr=%02x, imask=%04x\n",
+ unit, isr, imr, imask);
+#else
+ log(LOG_ERR, "ze%d: device timeout\n", unit);
+#endif
+
+ ze_reset(unit);
+}
+
+/*
+ * Initialize device.
+ */
+void
+ze_init(unit)
+ int unit;
+{
+ struct ze_softc *sc = &ze_softc[unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ int i, s;
+ u_char command;
+
+
+ /* address not known */
+ if (ifp->if_addrlist == (struct ifaddr *)0) return;
+
+ /*
+ * Initialize the NIC in the exact order outlined in the NS manual.
+ * This init procedure is "mandatory"...don't change what or when
+ * things happen.
+ */
+ s = splnet();
+
+ /* reset transmitter flags */
+ sc->data_buffered = 0;
+ sc->xmit_busy = 0;
+ sc->arpcom.ac_if.if_timer = 0;
+
+ sc->txb_next = 0;
+
+ /* This variable is used below - don't move this assignment */
+ sc->next_packet = sc->rec_page_start + 1;
+
+ /*
+ * Set interface for page 0, Remote DMA complete, Stopped
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STP);
+
+ if (sc->memwidth == 16) {
+ /*
+ * Set FIFO threshold to 8, No auto-init Remote DMA,
+ * byte order=80x86, word-wide DMA xfers
+ */
+ outb(sc->nic_addr + ZE_P0_DCR, ZE_DCR_FT1|ZE_DCR_WTS);
+ } else {
+ /*
+ * Same as above, but byte-wide DMA xfers
+ */
+ outb(sc->nic_addr + ZE_P0_DCR, ZE_DCR_FT1);
+ }
+
+ /*
+ * Clear Remote Byte Count Registers
+ */
+ outb(sc->nic_addr + ZE_P0_RBCR0, 0);
+ outb(sc->nic_addr + ZE_P0_RBCR1, 0);
+
+ /*
+ * Enable reception of broadcast packets
+ */
+ outb(sc->nic_addr + ZE_P0_RCR, ZE_RCR_AB);
+
+ /*
+ * Place NIC in internal loopback mode
+ */
+ outb(sc->nic_addr + ZE_P0_TCR, ZE_TCR_LB0);
+
+ /*
+ * Initialize transmit/receive (ring-buffer) Page Start
+ */
+ outb(sc->nic_addr + ZE_P0_TPSR, sc->tx_page_start);
+ outb(sc->nic_addr + ZE_P0_PSTART, sc->rec_page_start);
+
+ /*
+ * Initialize Receiver (ring-buffer) Page Stop and Boundry
+ */
+ outb(sc->nic_addr + ZE_P0_PSTOP, sc->rec_page_stop);
+ outb(sc->nic_addr + ZE_P0_BNRY, sc->rec_page_start);
+
+ /*
+ * Clear all interrupts. A '1' in each bit position clears the
+ * corresponding flag.
+ */
+ outb(sc->nic_addr + ZE_P0_ISR, 0xff);
+
+ /*
+ * Enable the following interrupts: receive/transmit complete,
+ * receive/transmit error, and Receiver OverWrite.
+ *
+ * Counter overflow and Remote DMA complete are *not* enabled.
+ */
+ outb(sc->nic_addr + ZE_P0_IMR,
+ ZE_IMR_PRXE|ZE_IMR_PTXE|ZE_IMR_RXEE|ZE_IMR_TXEE|ZE_IMR_OVWE);
+
+ /*
+ * Program Command Register for page 1
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_PAGE_1|ZE_CR_RD2|ZE_CR_STP);
+
+ /*
+ * Copy out our station address
+ */
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ outb(sc->nic_addr + ZE_P1_PAR0 + i, sc->arpcom.ac_enaddr[i]);
+
+#if NBPFILTER > 0
+ /*
+ * Initialize multicast address hashing registers to accept
+ * all multicasts (only used when in promiscuous mode)
+ */
+ for (i = 0; i < 8; ++i)
+ outb(sc->nic_addr + ZE_P1_MAR0 + i, 0xff);
+#endif
+
+ /*
+ * Set Current Page pointer to next_packet (initialized above)
+ */
+ outb(sc->nic_addr + ZE_P1_CURR, sc->next_packet);
+
+ /*
+ * Set Command Register for page 0, Remote DMA complete,
+ * and interface Start.
+ */
+ outb(sc->nic_addr + ZE_P1_CR, ZE_CR_RD2|ZE_CR_STA);
+
+ /*
+ * Take interface out of loopback
+ */
+ outb(sc->nic_addr + ZE_P0_TCR, 0);
+
+#if 0
+ /*
+ * If this is a 3Com board, the tranceiver must be software enabled
+ * (there is no settable hardware default).
+ */
+ if (sc->vendor == ZE_VENDOR_3COM) {
+ if (ifp->if_flags & IFF_LLC0) {
+ outb(sc->asic_addr + ZE_3COM_CR, 0);
+ } else {
+ outb(sc->asic_addr + ZE_3COM_CR, ZE_3COM_CR_XSEL);
+ }
+ }
+#endif
+
+ /*
+ * Set 'running' flag, and clear output active flag.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ /*
+ * ...and attempt to start output
+ */
+ ze_start(ifp);
+
+ (void) splx(s);
+}
+
+/*
+ * This routine actually starts the transmission on the interface
+ */
+static inline void
+ze_xmit(ifp)
+ struct ifnet *ifp;
+{
+ struct ze_softc *sc = &ze_softc[ifp->if_unit];
+ u_short len = sc->txb_next_len;
+
+ /*
+ * Set NIC for page 0 register access
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STA);
+
+ /*
+ * Set TX buffer start page
+ */
+ outb(sc->nic_addr + ZE_P0_TPSR, sc->tx_page_start +
+ sc->txb_next * ZE_TXBUF_SIZE);
+
+ /*
+ * Set TX length
+ */
+ outb(sc->nic_addr + ZE_P0_TBCR0, len & 0xff);
+ outb(sc->nic_addr + ZE_P0_TBCR1, len >> 8);
+
+ /*
+ * Set page 0, Remote DMA complete, Transmit Packet, and *Start*
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_TXP|ZE_CR_STA);
+
+ sc->xmit_busy = 1;
+ sc->data_buffered = 0;
+
+ /*
+ * Switch buffers if we are doing double-buffered transmits
+ */
+ if ((sc->txb_next == 0) && (sc->txb_cnt > 1))
+ sc->txb_next = 1;
+ else
+ sc->txb_next = 0;
+
+ /*
+ * Set a timer just in case we never hear from the board again
+ */
+ ifp->if_timer = 2;
+}
+
+/*
+ * Start output on interface.
+ * We make two assumptions here:
+ * 1) that the current priority is set to splnet _before_ this code
+ * is called *and* is returned to the appropriate priority after
+ * return
+ * 2) that the IFF_OACTIVE flag is checked before this code is called
+ * (i.e. that the output part of the interface is idle)
+ */
+void
+ze_start(ifp)
+ struct ifnet *ifp;
+{
+ struct ze_softc *sc = &ze_softc[ifp->if_unit];
+ struct mbuf *m0, *m;
+ caddr_t buffer;
+ int len;
+ u_char laar_tmp;
+
+outloop:
+ /*
+ * See if there is room to send more data (i.e. one or both of the
+ * buffers is empty).
+ */
+ if (sc->data_buffered)
+ if (sc->xmit_busy) {
+ /*
+ * No room. Indicate this to the outside world
+ * and exit.
+ */
+ ifp->if_flags |= IFF_OACTIVE;
+ return;
+ } else {
+ /*
+ * Data is buffered, but we're not transmitting, so
+ * start the xmit on the buffered data.
+ * Note that ze_xmit() resets the data_buffered flag
+ * before returning.
+ */
+ ze_xmit(ifp);
+ }
+
+ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m);
+ if (m == NULL) {
+ /*
+ * The following isn't pretty; we are using the !OACTIVE flag to
+ * indicate to the outside world that we can accept an additional
+ * packet rather than that the transmitter is _actually_
+ * active. Indeed, the transmitter may be active, but if we haven't
+ * filled the secondary buffer with data then we still want to
+ * accept more.
+ * Note that it isn't necessary to test the data_buffered flag -
+ * we wouldn't have tried to de-queue the packet in the first place
+ * if it was set.
+ */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ return;
+ }
+
+ /*
+ * Copy the mbuf chain into the transmit buffer
+ */
+#if 0
+ /*
+ * Enable 16bit access to shared memory on WD/SMC boards
+ */
+ if (sc->memwidth == 16)
+ if (sc->vendor == ZE_VENDOR_WD_SMC) {
+ laar_tmp = inb(sc->asic_addr + ZE_WD_LAAR);
+ outb(sc->asic_addr + ZE_WD_LAAR, laar_tmp | ZE_WD_LAAR_M16EN);
+ }
+#endif
+
+ buffer = sc->smem_start + (sc->txb_next * ZE_TXBUF_SIZE * ZE_PAGE_SIZE);
+ len = 0;
+ for (m0 = m; m != 0; m = m->m_next) {
+ bcopy(mtod(m, caddr_t), buffer, m->m_len);
+ buffer += m->m_len;
+ len += m->m_len;
+ }
+
+#if 0
+ /*
+ * Restore previous shared mem access type
+ */
+ if (sc->memwidth == 16)
+ if (sc->vendor == ZE_VENDOR_WD_SMC) {
+ outb(sc->asic_addr + ZE_WD_LAAR, laar_tmp);
+ }
+#endif
+
+ sc->txb_next_len = MAX(len, ETHER_MIN_LEN);
+
+ if (sc->txb_cnt > 1)
+ /*
+ * only set 'buffered' flag if doing multiple buffers
+ */
+ sc->data_buffered = 1;
+
+ if (sc->xmit_busy == 0)
+ ze_xmit(ifp);
+ /*
+ * If there is BPF support in the configuration, tap off here.
+ * The following has support for converting trailer packets
+ * back to normal.
+ */
+#if NBPFILTER > 0
+ if (sc->bpf) {
+ u_short etype;
+ int off, datasize, resid;
+ struct ether_header *eh;
+ struct trailer_header {
+ u_short ether_type;
+ u_short ether_residual;
+ } trailer_header;
+ char ether_packet[ETHER_MAX_LEN];
+ char *ep;
+
+ ep = ether_packet;
+
+ /*
+ * We handle trailers below:
+ * Copy ether header first, then residual data,
+ * then data. Put all this in a temporary buffer
+ * 'ether_packet' and send off to bpf. Since the
+ * system has generated this packet, we assume
+ * that all of the offsets in the packet are
+ * correct; if they're not, the system will almost
+ * certainly crash in m_copydata.
+ * We make no assumptions about how the data is
+ * arranged in the mbuf chain (i.e. how much
+ * data is in each mbuf, if mbuf clusters are
+ * used, etc.), which is why we use m_copydata
+ * to get the ether header rather than assume
+ * that this is located in the first mbuf.
+ */
+ /* copy ether header */
+ m_copydata(m0, 0, sizeof(struct ether_header), ep);
+ eh = (struct ether_header *) ep;
+ ep += sizeof(struct ether_header);
+ etype = ntohs(eh->ether_type);
+ if (etype >= ETHERTYPE_TRAIL &&
+ etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
+ datasize = ((etype - ETHERTYPE_TRAIL) << 9);
+ off = datasize + sizeof(struct ether_header);
+
+ /* copy trailer_header into a data structure */
+ m_copydata(m0, off, sizeof(struct trailer_header),
+ &trailer_header.ether_type);
+
+ /* copy residual data */
+ m_copydata(m0, off+sizeof(struct trailer_header),
+ resid = ntohs(trailer_header.ether_residual) -
+ sizeof(struct trailer_header), ep);
+ ep += resid;
+
+ /* copy data */
+ m_copydata(m0, sizeof(struct ether_header),
+ datasize, ep);
+ ep += datasize;
+
+ /* restore original ether packet type */
+ eh->ether_type = trailer_header.ether_type;
+
+ bpf_tap(sc->bpf, ether_packet, ep - ether_packet);
+ } else
+ bpf_mtap(sc->bpf, m0);
+ }
+#endif
+
+ m_freem(m0);
+
+ /*
+ * If we are doing double-buffering, a buffer might be free to
+ * fill with another packet, so loop back to the top.
+ */
+ if (sc->txb_cnt > 1)
+ goto outloop;
+ else {
+ ifp->if_flags |= IFF_OACTIVE;
+ return;
+ }
+}
+
+/*
+ * Ethernet interface receiver interrupt.
+ */
+static inline void /* only called from one place, so may as well integrate */
+ze_rint(unit)
+ int unit;
+{
+ register struct ze_softc *sc = &ze_softc[unit];
+ u_char boundry, current;
+ u_short len;
+ struct ze_ring *packet_ptr;
+
+ /*
+ * Set NIC to page 1 registers to get 'current' pointer
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_PAGE_1|ZE_CR_RD2|ZE_CR_STA);
+
+ /*
+ * 'sc->next_packet' is the logical beginning of the ring-buffer - i.e.
+ * it points to where new data has been buffered. The 'CURR'
+ * (current) register points to the logical end of the ring-buffer
+ * - i.e. it points to where additional new data will be added.
+ * We loop here until the logical beginning equals the logical
+ * end (or in other words, until the ring-buffer is empty).
+ */
+ while (sc->next_packet != inb(sc->nic_addr + ZE_P1_CURR)) {
+
+ /* get pointer to this buffer header structure */
+ packet_ptr = (struct ze_ring *)(sc->smem_ring +
+ (sc->next_packet - sc->rec_page_start) * ZE_PAGE_SIZE);
+
+ /*
+ * The byte count includes the FCS - Frame Check Sequence (a
+ * 32 bit CRC).
+ */
+ len = packet_ptr->count;
+ if ((len >= ETHER_MIN_LEN) && (len <= ETHER_MAX_LEN)) {
+ /*
+ * Go get packet. len - 4 removes CRC from length.
+ * (packet_ptr + 1) points to data just after the packet ring
+ * header (+4 bytes)
+ */
+ ze_get_packet(sc, (caddr_t)(packet_ptr + 1), len - 4);
+ ++sc->arpcom.ac_if.if_ipackets;
+ } else {
+ /*
+ * Really BAD...probably indicates that the ring pointers
+ * are corrupted. Also seen on early rev chips under
+ * high load - the byte order of the length gets switched.
+ */
+ log(LOG_ERR,
+ "ze%d: shared memory corrupt - invalid packet length %d\n",
+ unit, len);
+ ze_reset(unit);
+ return;
+ }
+
+ /*
+ * Update next packet pointer
+ */
+ sc->next_packet = packet_ptr->next_packet;
+
+ /*
+ * Update NIC boundry pointer - being careful to keep it
+ * one buffer behind. (as recommended by NS databook)
+ */
+ boundry = sc->next_packet - 1;
+ if (boundry < sc->rec_page_start)
+ boundry = sc->rec_page_stop - 1;
+
+ /*
+ * Set NIC to page 0 registers to update boundry register
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STA);
+
+ outb(sc->nic_addr + ZE_P0_BNRY, boundry);
+
+ /*
+ * Set NIC to page 1 registers before looping to top (prepare to
+ * get 'CURR' current pointer)
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_PAGE_1|ZE_CR_RD2|ZE_CR_STA);
+ }
+}
+
+/*
+ * Ethernet interface interrupt processor
+ */
+void
+zeintr(unit)
+ int unit;
+{
+ struct ze_softc *sc = &ze_softc[unit];
+ u_char isr;
+
+ /*
+ * Set NIC to page 0 registers
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STA);
+
+ /*
+ * loop until there are no more new interrupts
+ */
+ while (isr = inb(sc->nic_addr + ZE_P0_ISR)) {
+
+ /*
+ * reset all the bits that we are 'acknowleging'
+ * by writing a '1' to each bit position that was set
+ * (writing a '1' *clears* the bit)
+ */
+ outb(sc->nic_addr + ZE_P0_ISR, isr);
+
+ /*
+ * Transmit error. If a TX completed with an error, we end up
+ * throwing the packet away. Really the only error that is
+ * possible is excessive collisions, and in this case it is
+ * best to allow the automatic mechanisms of TCP to backoff
+ * the flow. Of course, with UDP we're screwed, but this is
+ * expected when a network is heavily loaded.
+ */
+ if (isr & ZE_ISR_TXE) {
+ u_char tsr = inb(sc->nic_addr + ZE_P0_TSR);
+ u_char ncr = inb(sc->nic_addr + ZE_P0_NCR);
+
+ /*
+ * Excessive collisions (16)
+ */
+ if ((tsr & ZE_TSR_ABT) && (ncr == 0)) {
+ /*
+ * When collisions total 16, the P0_NCR will
+ * indicate 0, and the TSR_ABT is set.
+ */
+ sc->arpcom.ac_if.if_collisions += 16;
+ } else
+ sc->arpcom.ac_if.if_collisions += ncr;
+
+ /*
+ * update output errors counter
+ */
+ ++sc->arpcom.ac_if.if_oerrors;
+
+ /*
+ * reset tx busy and output active flags
+ */
+ sc->xmit_busy = 0;
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+
+ /*
+ * clear watchdog timer
+ */
+ sc->arpcom.ac_if.if_timer = 0;
+ }
+
+
+ /*
+ * Receiver Error. One or more of: CRC error, frame alignment error
+ * FIFO overrun, or missed packet.
+ */
+ if (isr & ZE_ISR_RXE) {
+ ++sc->arpcom.ac_if.if_ierrors;
+#ifdef ZE_DEBUG
+#if 0
+ printf("ze%d: receive error %x\n", unit,
+ inb(sc->nic_addr + ZE_P0_RSR));
+#else
+ printf("ze%d: receive error %b\n", unit,
+ inb(sc->nic_addr + ZE_P0_RSR),
+ "\20\8DEF\7REC DISAB\6PHY/MC\5MISSED\4OVR\3ALIGN\2FCS\1RCVD");
+#endif
+#endif
+ }
+
+ /*
+ * Overwrite warning. In order to make sure that a lockup
+ * of the local DMA hasn't occurred, we reset and
+ * re-init the NIC. The NSC manual suggests only a
+ * partial reset/re-init is necessary - but some
+ * chips seem to want more. The DMA lockup has been
+ * seen only with early rev chips - Methinks this
+ * bug was fixed in later revs. -DG
+ */
+ if (isr & ZE_ISR_OVW) {
+ ++sc->arpcom.ac_if.if_ierrors;
+#if 0
+ /* sigh. this happens too often on our net */
+ log(LOG_WARNING,
+ "ze%d: warning - receiver ring buffer overrun\n",
+ unit);
+#endif
+ /*
+ * Stop/reset/re-init NIC
+ */
+ ze_reset(unit);
+ }
+
+ /*
+ * Transmission completed normally.
+ */
+ if (isr & ZE_ISR_PTX) {
+
+ /*
+ * reset tx busy and output active flags
+ */
+ sc->xmit_busy = 0;
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+
+ /*
+ * clear watchdog timer
+ */
+ sc->arpcom.ac_if.if_timer = 0;
+
+ /*
+ * Update total number of successfully transmitted
+ * packets.
+ */
+ ++sc->arpcom.ac_if.if_opackets;
+
+ /*
+ * Add in total number of collisions on last
+ * transmission.
+ */
+ sc->arpcom.ac_if.if_collisions += inb(sc->nic_addr +
+ ZE_P0_TBCR0);
+ }
+
+ /*
+ * Receive Completion. Go and get the packet.
+ * XXX - Doing this on an error is dubious because there
+ * shouldn't be any data to get (we've configured the
+ * interface to not accept packets with errors).
+ */
+ if (isr & (ZE_ISR_PRX|ZE_ISR_RXE)) {
+#if 0
+ /*
+ * Enable access to shared memory on WD/SMC boards
+ */
+ if (sc->memwidth == 16)
+ if (sc->vendor == ZE_VENDOR_WD_SMC) {
+ outb(sc->asic_addr + ZE_WD_LAAR,
+ inb(sc->asic_addr + ZE_WD_LAAR)
+ | ZE_WD_LAAR_M16EN);
+ }
+#endif
+ ze_rint (unit);
+
+#if 0
+ /*
+ * Disable access to shared memory
+ */
+ if (sc->memwidth == 16)
+ if (sc->vendor == ZE_VENDOR_WD_SMC) {
+ outb(sc->asic_addr + ZE_WD_LAAR,
+ inb(sc->asic_addr + ZE_WD_LAAR)
+ & ~ZE_WD_LAAR_M16EN);
+ }
+#endif
+ }
+
+ /*
+ * If it looks like the transmitter can take more data,
+ * attempt to start output on the interface. If data is
+ * already buffered and ready to go, send it first.
+ */
+ if ((sc->arpcom.ac_if.if_flags & IFF_OACTIVE) == 0) {
+ if (sc->data_buffered)
+ ze_xmit(&sc->arpcom.ac_if);
+ ze_start(&sc->arpcom.ac_if);
+ }
+
+ /*
+ * return NIC CR to standard state: page 0, remote DMA complete,
+ * start (toggling the TXP bit off, even if was just set
+ * in the transmit routine, is *okay* - it is 'edge'
+ * triggered from low to high)
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STA);
+
+ /*
+ * If the Network Talley Counters overflow, read them to
+ * reset them. It appears that old 8390's won't
+ * clear the ISR flag otherwise - resulting in an
+ * infinite loop.
+ */
+ if (isr & ZE_ISR_CNT) {
+ (void) inb(sc->nic_addr + ZE_P0_CNTR0);
+ (void) inb(sc->nic_addr + ZE_P0_CNTR1);
+ (void) inb(sc->nic_addr + ZE_P0_CNTR2);
+ }
+ }
+}
+
+/*
+ * Process an ioctl request. This code needs some work - it looks
+ * pretty ugly.
+ */
+int
+ze_ioctl(ifp, command, data)
+ register struct ifnet *ifp;
+ int command;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *)data;
+ struct ze_softc *sc = &ze_softc[ifp->if_unit];
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s, error = 0;
+
+ s = splnet();
+
+ switch (command) {
+
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ ze_init(ifp->if_unit); /* before arpwhohas */
+ /*
+ * See if another station has *our* IP address.
+ * i.e.: There is an address conflict! If a
+ * conflict exists, a message is sent to the
+ * console.
+ */
+ ((struct arpcom *)ifp)->ac_ipaddr =
+ IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
+ break;
+#endif
+#ifdef NS
+ /*
+ * XXX - This code is probably wrong
+ */
+ case AF_NS:
+ {
+ register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+ if (ns_nullhost(*ina))
+ ina->x_host =
+ *(union ns_host *)(sc->arpcom.ac_enaddr);
+ else {
+ /*
+ *
+ */
+ bcopy((caddr_t)ina->x_host.c_host,
+ (caddr_t)sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr));
+ }
+ /*
+ * Set new address
+ */
+ ze_init(ifp->if_unit);
+ break;
+ }
+#endif
+ default:
+ ze_init(ifp->if_unit);
+ break;
+ }
+ break;
+
+ case SIOCSIFFLAGS:
+ /*
+ * If interface is marked down and it is running, then stop it
+ */
+ if (((ifp->if_flags & IFF_UP) == 0) &&
+ (ifp->if_flags & IFF_RUNNING)) {
+ ze_stop(ifp->if_unit);
+ ifp->if_flags &= ~IFF_RUNNING;
+ } else {
+ /*
+ * If interface is marked up and it is stopped, then start it
+ */
+ if ((ifp->if_flags & IFF_UP) &&
+ ((ifp->if_flags & IFF_RUNNING) == 0))
+ ze_init(ifp->if_unit);
+ }
+#if NBPFILTER > 0
+ if (ifp->if_flags & IFF_PROMISC) {
+ /*
+ * Set promiscuous mode on interface.
+ * XXX - for multicasts to work, we would need to
+ * write 1's in all bits of multicast
+ * hashing array. For now we assume that
+ * this was done in ze_init().
+ */
+ outb(sc->nic_addr + ZE_P0_RCR,
+ ZE_RCR_PRO|ZE_RCR_AM|ZE_RCR_AB);
+ } else {
+ /*
+ * XXX - for multicasts to work, we would need to
+ * rewrite the multicast hashing array with the
+ * proper hash (would have been destroyed above).
+ */
+ outb(sc->nic_addr + ZE_P0_RCR, ZE_RCR_AB);
+ }
+#endif
+#if 0
+ /*
+ * An unfortunate hack to provide the (required) software control
+ * of the tranceiver for 3Com boards. The LLC0 flag disables
+ * the tranceiver if set.
+ */
+ if (sc->vendor == ZE_VENDOR_3COM) {
+ if (ifp->if_flags & IFF_LLC0) {
+ outb(sc->asic_addr + ZE_3COM_CR, 0);
+ } else {
+ outb(sc->asic_addr + ZE_3COM_CR, ZE_3COM_CR_XSEL);
+ }
+ }
+#endif
+
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ (void) splx(s);
+ return (error);
+}
+
+/*
+ * Macro to calculate a new address within shared memory when given an offset
+ * from an address, taking into account ring-wrap.
+ */
+#define ringoffset(sc, start, off, type) \
+ ((type)( ((caddr_t)(start)+(off) >= (sc)->smem_end) ? \
+ (((caddr_t)(start)+(off))) - (sc)->smem_end \
+ + (sc)->smem_ring: \
+ ((caddr_t)(start)+(off)) ))
+
+/*
+ * Retreive packet from shared memory and send to the next level up via
+ * ether_input(). If there is a BPF listener, give a copy to BPF, too.
+ */
+void
+ze_get_packet(sc, buf, len)
+ struct ze_softc *sc;
+ char *buf;
+ u_short len;
+{
+ struct ether_header *eh;
+ struct mbuf *m, *head = NULL, *ze_ring_to_mbuf();
+ u_short off;
+ int resid;
+ u_short etype;
+ struct trailer_header {
+ u_short trail_type;
+ u_short trail_residual;
+ } trailer_header;
+
+ /* Allocate a header mbuf */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ goto bad;
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ m->m_pkthdr.len = len;
+ m->m_len = 0;
+ head = m;
+
+ eh = (struct ether_header *)buf;
+
+ /* The following sillines is to make NFS happy */
+#define EROUND ((sizeof(struct ether_header) + 3) & ~3)
+#define EOFF (EROUND - sizeof(struct ether_header))
+
+ /*
+ * The following assumes there is room for
+ * the ether header in the header mbuf
+ */
+ head->m_data += EOFF;
+ bcopy(buf, mtod(head, caddr_t), sizeof(struct ether_header));
+ buf += sizeof(struct ether_header);
+ head->m_len += sizeof(struct ether_header);
+ len -= sizeof(struct ether_header);
+
+ etype = ntohs((u_short)eh->ether_type);
+
+ /*
+ * Deal with trailer protocol:
+ * If trailer protocol, calculate the datasize as 'off',
+ * which is also the offset to the trailer header.
+ * Set resid to the amount of packet data following the
+ * trailer header.
+ * Finally, copy residual data into mbuf chain.
+ */
+ if (etype >= ETHERTYPE_TRAIL &&
+ etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
+
+ off = (etype - ETHERTYPE_TRAIL) << 9;
+ if ((off + sizeof(struct trailer_header)) > len)
+ goto bad; /* insanity */
+
+ eh->ether_type = *ringoffset(sc, buf, off, u_short *);
+ resid = ntohs(*ringoffset(sc, buf, off+2, u_short *));
+
+ if ((off + resid) > len) goto bad; /* insanity */
+
+ resid -= sizeof(struct trailer_header);
+ if (resid < 0) goto bad; /* insanity */
+
+ m = ze_ring_to_mbuf(sc, ringoffset(sc, buf, off+4, char *), head, resid);
+ if (m == NULL) goto bad;
+
+ len = off;
+ head->m_pkthdr.len -= 4; /* subtract trailer header */
+ }
+
+ /*
+ * Pull packet off interface. Or if this was a trailer packet,
+ * the data portion is appended.
+ */
+ m = ze_ring_to_mbuf(sc, buf, m, len);
+ if (m == NULL) goto bad;
+
+#if NBPFILTER > 0
+ /*
+ * Check if there's a BPF listener on this interface.
+ * If so, hand off the raw packet to bpf.
+ */
+ if (sc->bpf) {
+ bpf_mtap(sc->bpf, head);
+
+ /*
+ * Note that the interface cannot be in promiscuous mode if
+ * there are no BPF listeners. And if we are in promiscuous
+ * mode, we have to check if this packet is really ours.
+ *
+ * XXX This test does not support multicasts.
+ */
+ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) &&
+ bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
+ sizeof(eh->ether_dhost)) != 0 &&
+ bcmp(eh->ether_dhost, etherbroadcastaddr,
+ sizeof(eh->ether_dhost)) != 0) {
+
+ m_freem(head);
+ return;
+ }
+ }
+#endif
+
+ /*
+ * Fix up data start offset in mbuf to point past ether header
+ */
+ m_adj(head, sizeof(struct ether_header));
+
+ /*
+ * silly ether_input routine needs 'type' in host byte order
+ */
+ eh->ether_type = ntohs(eh->ether_type);
+
+ ether_input(&sc->arpcom.ac_if, eh, head);
+ return;
+
+bad: if (head)
+ m_freem(head);
+ return;
+}
+
+/*
+ * Supporting routines
+ */
+
+/*
+ * Given a source and destination address, copy 'amount' of a packet from
+ * the ring buffer into a linear destination buffer. Takes into account
+ * ring-wrap.
+ */
+static inline char *
+ze_ring_copy(sc,src,dst,amount)
+ struct ze_softc *sc;
+ char *src;
+ char *dst;
+ u_short amount;
+{
+ u_short tmp_amount;
+
+ /* does copy wrap to lower addr in ring buffer? */
+ if (src + amount > sc->smem_end) {
+ tmp_amount = sc->smem_end - src;
+ bcopy(src,dst,tmp_amount); /* copy amount up to end of smem */
+ amount -= tmp_amount;
+ src = sc->smem_ring;
+ dst += tmp_amount;
+ }
+
+ bcopy(src, dst, amount);
+
+ return(src + amount);
+}
+
+/*
+ * Copy data from receive buffer to end of mbuf chain
+ * allocate additional mbufs as needed. return pointer
+ * to last mbuf in chain.
+ * sc = ze info (softc)
+ * src = pointer in ze ring buffer
+ * dst = pointer to last mbuf in mbuf chain to copy to
+ * amount = amount of data to copy
+ */
+struct mbuf *
+ze_ring_to_mbuf(sc,src,dst,total_len)
+ struct ze_softc *sc;
+ char *src;
+ struct mbuf *dst;
+ u_short total_len;
+{
+ register struct mbuf *m = dst;
+
+ while (total_len) {
+ register u_short amount = min(total_len, M_TRAILINGSPACE(m));
+
+ if (amount == 0) { /* no more data in this mbuf, alloc another */
+ /*
+ * If there is enough data for an mbuf cluster, attempt
+ * to allocate one of those, otherwise, a regular
+ * mbuf will do.
+ * Note that a regular mbuf is always required, even if
+ * we get a cluster - getting a cluster does not
+ * allocate any mbufs, and one is needed to assign
+ * the cluster to. The mbuf that has a cluster
+ * extension can not be used to contain data - only
+ * the cluster can contain data.
+ */
+ dst = m;
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return (0);
+
+ if (total_len >= MINCLSIZE)
+ MCLGET(m, M_DONTWAIT);
+
+ m->m_len = 0;
+ dst->m_next = m;
+ amount = min(total_len, M_TRAILINGSPACE(m));
+ }
+
+ src = ze_ring_copy(sc, src, mtod(m, caddr_t) + m->m_len, amount);
+
+ m->m_len += amount;
+ total_len -= amount;
+
+ }
+ return (m);
+}
+#endif
+
diff --git a/sys/i386/isa/if_zereg.h b/sys/i386/isa/if_zereg.h
new file mode 100644
index 0000000..3cd501f
--- /dev/null
+++ b/sys/i386/isa/if_zereg.h
@@ -0,0 +1,859 @@
+/*
+ * National Semiconductor DS8390 NIC register definitions
+ *
+ * if_edreg.h,v
+ * Revision 1.1.2.1 1993/07/21 13:50:04 cgd
+ * from davidg:
+ * Added config file override for memory size and added flags to force
+ * 8bit or 16bit operation, and a flag to disable transmitter double buffering.
+ * See the updated "ed.relnotes" file for information about how to set
+ * the flags.
+ * This should be considered the first "production" release. It still
+ * needs a manual page, though.
+ *
+ * Revision 1.1 1993/07/03 12:21:07 cgd
+ * add support for David Greenman "ed" driver
+ *
+ * Revision 1.2 93/06/23 03:03:05 davidg
+ * added some additional definitions for the 83C584 bus interface
+ * chip (SMC/WD boards)
+ *
+ * Revision 1.1 93/06/23 03:01:07 davidg
+ * Initial revision
+ *
+ */
+
+/*
+ * Page 0 register offsets
+ */
+#define ZE_P0_CR 0x00 /* Command Register */
+
+#define ZE_P0_CLDA0 0x01 /* Current Local DMA Addr low (read) */
+#define ZE_P0_PSTART 0x01 /* Page Start register (write) */
+
+#define ZE_P0_CLDA1 0x02 /* Current Local DMA Addr high (read) */
+#define ZE_P0_PSTOP 0x02 /* Page Stop register (write) */
+
+#define ZE_P0_BNRY 0x03 /* Boundary Pointer */
+
+#define ZE_P0_TSR 0x04 /* Transmit Status Register (read) */
+#define ZE_P0_TPSR 0x04 /* Transmit Page Start (write) */
+
+#define ZE_P0_NCR 0x05 /* Number of Collisions Reg (read) */
+#define ZE_P0_TBCR0 0x05 /* Transmit Byte count, low (write) */
+
+#define ZE_P0_FIFO 0x06 /* FIFO register (read) */
+#define ZE_P0_TBCR1 0x06 /* Transmit Byte count, high (write) */
+
+#define ZE_P0_ISR 0x07 /* Interrupt Status Register */
+
+#define ZE_P0_CRDA0 0x08 /* Current Remote DMA Addr low (read) */
+#define ZE_P0_RSAR0 0x08 /* Remote Start Address low (write) */
+
+#define ZE_P0_CRDA1 0x09 /* Current Remote DMA Addr high (read) */
+#define ZE_P0_RSAR1 0x09 /* Remote Start Address high (write) */
+
+#define ZE_P0_RBCR0 0x0a /* Remote Byte Count low (write) */
+
+#define ZE_P0_RBCR1 0x0b /* Remote Byte Count high (write) */
+
+#define ZE_P0_RSR 0x0c /* Receive Status (read) */
+#define ZE_P0_RCR 0x0c /* Receive Configuration Reg (write) */
+
+#define ZE_P0_CNTR0 0x0d /* frame alignment error counter (read) */
+#define ZE_P0_TCR 0x0d /* Transmit Configuration Reg (write) */
+
+#define ZE_P0_CNTR1 0x0e /* CRC error counter (read) */
+#define ZE_P0_DCR 0x0e /* Data Configuration Reg (write) */
+
+#define ZE_P0_CNTR2 0x0f /* missed packet counter (read) */
+#define ZE_P0_IMR 0x0f /* Interrupt Mask Register (write) */
+
+/*
+ * Page 1 register offsets
+ */
+#define ZE_P1_CR 0x00 /* Command Register */
+#define ZE_P1_PAR0 0x01 /* Physical Address Register 0 */
+#define ZE_P1_PAR1 0x02 /* Physical Address Register 1 */
+#define ZE_P1_PAR2 0x03 /* Physical Address Register 2 */
+#define ZE_P1_PAR3 0x04 /* Physical Address Register 3 */
+#define ZE_P1_PAR4 0x05 /* Physical Address Register 4 */
+#define ZE_P1_PAR5 0x06 /* Physical Address Register 5 */
+#define ZE_P1_CURR 0x07 /* Current RX ring-buffer page */
+#define ZE_P1_MAR0 0x08 /* Multicast Address Register 0 */
+#define ZE_P1_MAR1 0x09 /* Multicast Address Register 1 */
+#define ZE_P1_MAR2 0x0a /* Multicast Address Register 2 */
+#define ZE_P1_MAR3 0x0b /* Multicast Address Register 3 */
+#define ZE_P1_MAR4 0x0c /* Multicast Address Register 4 */
+#define ZE_P1_MAR5 0x0d /* Multicast Address Register 5 */
+#define ZE_P1_MAR6 0x0e /* Multicast Address Register 6 */
+#define ZE_P1_MAR7 0x0f /* Multicast Address Register 7 */
+
+/*
+ * Page 2 register offsets
+ */
+#define ZE_P2_CR 0x00 /* Command Register */
+#define ZE_P2_PSTART 0x01 /* Page Start (read) */
+#define ZE_P2_CLDA0 0x01 /* Current Local DMA Addr 0 (write) */
+#define ZE_P2_PSTOP 0x02 /* Page Stop (read) */
+#define ZE_P2_CLDA1 0x02 /* Current Local DMA Addr 1 (write) */
+#define ZE_P2_RNPP 0x03 /* Remote Next Packet Pointer */
+#define ZE_P2_TPSR 0x04 /* Transmit Page Start (read) */
+#define ZE_P2_LNPP 0x05 /* Local Next Packet Pointer */
+#define ZE_P2_ACU 0x06 /* Address Counter Upper */
+#define ZE_P2_ACL 0x07 /* Address Counter Lower */
+#define ZE_P2_RCR 0x0c /* Receive Configuration Register (read) */
+#define ZE_P2_TCR 0x0d /* Transmit Configuration Register (read) */
+#define ZE_P2_DCR 0x0e /* Data Configuration Register (read) */
+#define ZE_P2_IMR 0x0f /* Interrupt Mask Register (read) */
+
+/*
+ * Command Register (CR) definitions
+ */
+
+/*
+ * STP: SToP. Software reset command. Takes the controller offline. No
+ * packets will be received or transmitted. Any reception or
+ * transmission in progress will continue to completion before
+ * entering reset state. To exit this state, the STP bit must
+ * reset and the STA bit must be set. The software reset has
+ * executed only when indicated by the RST bit in the ISR being
+ * set.
+ */
+#define ZE_CR_STP 0x01
+
+/*
+ * STA: STArt. This bit is used to activate the NIC after either power-up,
+ * or when the NIC has been put in reset mode by software command
+ * or error.
+ */
+#define ZE_CR_STA 0x02
+
+/*
+ * TXP: Transmit Packet. This bit must be set to indicate transmission of
+ * a packet. TXP is internally reset either after the transmission is
+ * completed or aborted. This bit should be set only after the Transmit
+ * Byte Count and Transmit Page Start register have been programmed.
+ */
+#define ZE_CR_TXP 0x04
+
+/*
+ * RD0, RD1, RD2: Remote DMA Command. These three bits control the operation
+ * of the remote DMA channel. RD2 can be set to abort any remote DMA
+ * command in progress. The Remote Byte Count registers should be cleared
+ * when a remote DMA has been aborted. The Remote Start Addresses are not
+ * restored to the starting address if the remote DMA is aborted.
+ *
+ * RD2 RD1 RD0 function
+ * 0 0 0 not allowed
+ * 0 0 1 remote read
+ * 0 1 0 remote write
+ * 0 1 1 send packet
+ * 1 X X abort
+ */
+#define ZE_CR_RD0 0x08
+#define ZE_CR_RD1 0x10
+#define ZE_CR_RD2 0x20
+
+/*
+ * PS0, PS1: Page Select. The two bits select which register set or 'page' to
+ * access.
+ *
+ * PS1 PS0 page
+ * 0 0 0
+ * 0 1 1
+ * 1 0 2
+ * 1 1 reserved
+ */
+#define ZE_CR_PS0 0x40
+#define ZE_CR_PS1 0x80
+/* bit encoded aliases */
+#define ZE_CR_PAGE_0 0x00 /* (for consistency) */
+#define ZE_CR_PAGE_1 0x40
+#define ZE_CR_PAGE_2 0x80
+
+/*
+ * Interrupt Status Register (ISR) definitions
+ */
+
+/*
+ * PRX: Packet Received. Indicates packet received with no errors.
+ */
+#define ZE_ISR_PRX 0x01
+
+/*
+ * PTX: Packet Transmitted. Indicates packet transmitted with no errors.
+ */
+#define ZE_ISR_PTX 0x02
+
+/*
+ * RXE: Receive Error. Indicates that a packet was received with one or more
+ * the following errors: CRC error, frame alignment error, FIFO overrun,
+ * missed packet.
+ */
+#define ZE_ISR_RXE 0x04
+
+/*
+ * TXE: Transmission Error. Indicates that an attempt to transmit a packet
+ * resulted in one or more of the following errors: excessive
+ * collisions, FIFO underrun.
+ */
+#define ZE_ISR_TXE 0x08
+
+/*
+ * OVW: OverWrite. Indicates a receive ring-buffer overrun. Incoming network
+ * would exceed (has exceeded?) the boundry pointer, resulting in data
+ * that was previously received and not yet read from the buffer to be
+ * overwritten.
+ */
+#define ZE_ISR_OVW 0x10
+
+/*
+ * CNT: Counter Overflow. Set when the MSB of one or more of the Network Talley
+ * Counters has been set.
+ */
+#define ZE_ISR_CNT 0x20
+
+/*
+ * RDC: Remote Data Complete. Indicates that a Remote DMA operation has completed.
+ */
+#define ZE_ISR_RDC 0x40
+
+/*
+ * RST: Reset status. Set when the NIC enters the reset state and cleared when a
+ * Start Command is issued to the CR. This bit is also set when a receive
+ * ring-buffer overrun (OverWrite) occurs and is cleared when one or more
+ * packets have been removed from the ring. This is a read-only bit.
+ */
+#define ZE_ISR_RST 0x80
+
+/*
+ * Interrupt Mask Register (IMR) definitions
+ */
+
+/*
+ * PRXE: Packet Received interrupt Enable. If set, a received packet will cause
+ * an interrupt.
+ */
+#define ZE_IMR_PRXE 0x01
+
+/*
+ * PTXE: Packet Transmit interrupt Enable. If set, an interrupt is generated when
+ * a packet transmission completes.
+ */
+#define ZE_IMR_PTXE 0x02
+
+/*
+ * RXEE: Receive Error interrupt Enable. If set, an interrupt will occur whenever a
+ * packet is received with an error.
+ */
+#define ZE_IMR_RXEE 0x04
+
+/*
+ * TXEE: Transmit Error interrupt Enable. If set, an interrupt will occur whenever
+ * a transmission results in an error.
+ */
+#define ZE_IMR_TXEE 0x08
+
+/*
+ * OVWE: OverWrite error interrupt Enable. If set, an interrupt is generated whenever
+ * the receive ring-buffer is overrun. i.e. when the boundry pointer is exceeded.
+ */
+#define ZE_IMR_OVWE 0x10
+
+/*
+ * CNTE: Counter overflow interrupt Enable. If set, an interrupt is generated whenever
+ * the MSB of one or more of the Network Statistics counters has been set.
+ */
+#define ZE_IMR_CNTE 0x20
+
+/*
+ * RDCE: Remote DMA Complete interrupt Enable. If set, an interrupt is generated
+ * when a remote DMA transfer has completed.
+ */
+#define ZE_IMR_RDCE 0x40
+
+/*
+ * bit 7 is unused/reserved
+ */
+
+/*
+ * Data Configuration Register (DCR) definitions
+ */
+
+/*
+ * WTS: Word Transfer Select. WTS establishes byte or word transfers for
+ * both remote and local DMA transfers
+ */
+#define ZE_DCR_WTS 0x01
+
+/*
+ * BOS: Byte Order Select. BOS sets the byte order for the host.
+ * Should be 0 for 80x86, and 1 for 68000 series processors
+ */
+#define ZE_DCR_BOS 0x02
+
+/*
+ * LAS: Long Address Select. When LAS is 1, the contents of the remote
+ * DMA registers RSAR0 and RSAR1 are used to provide A16-A31
+ */
+#define ZE_DCR_LAS 0x04
+
+/*
+ * LS: Loopback Select. When 0, loopback mode is selected. Bits D1 and D2
+ * of the TCR must also be programmed for loopback operation.
+ * When 1, normal operation is selected.
+ */
+#define ZE_DCR_LS 0x08
+
+/*
+ * AR: Auto-initialize Remote. When 0, data must be removed from ring-buffer
+ * under program control. When 1, remote DMA is automatically initiated
+ * and the boundry pointer is automatically updated
+ */
+#define ZE_DCR_AR 0x10
+
+/*
+ * FT0, FT1: Fifo Threshold select.
+ * FT1 FT0 Word-width Byte-width
+ * 0 0 1 word 2 bytes
+ * 0 1 2 words 4 bytes
+ * 1 0 4 words 8 bytes
+ * 1 1 8 words 12 bytes
+ *
+ * During transmission, the FIFO threshold indicates the number of bytes
+ * or words that the FIFO has filled from the local DMA before BREQ is
+ * asserted. The transmission threshold is 16 bytes minus the receiver
+ * threshold.
+ */
+#define ZE_DCR_FT0 0x20
+#define ZE_DCR_FT1 0x40
+
+/*
+ * bit 7 (0x80) is unused/reserved
+ */
+
+/*
+ * Transmit Configuration Register (TCR) definitions
+ */
+
+/*
+ * CRC: Inhibit CRC. If 0, CRC will be appended by the transmitter, if 0, CRC
+ * is not appended by the transmitter.
+ */
+#define ZE_TCR_CRC 0x01
+
+/*
+ * LB0, LB1: Loopback control. These two bits set the type of loopback that is
+ * to be performed.
+ *
+ * LB1 LB0 mode
+ * 0 0 0 - normal operation (DCR_LS = 0)
+ * 0 1 1 - internal loopback (DCR_LS = 0)
+ * 1 0 2 - external loopback (DCR_LS = 1)
+ * 1 1 3 - external loopback (DCR_LS = 0)
+ */
+#define ZE_TCR_LB0 0x02
+#define ZE_TCR_LB1 0x04
+
+/*
+ * ATD: Auto Transmit Disable. Clear for normal operation. When set, allows
+ * another station to disable the NIC's transmitter by transmitting to
+ * a multicast address hashing to bit 62. Reception of a multicast address
+ * hashing to bit 63 enables the transmitter.
+ */
+#define ZE_TCR_ATD 0x08
+
+/*
+ * OFST: Collision Offset enable. This bit when set modifies the backoff
+ * algorithm to allow prioritization of nodes.
+ */
+#define ZE_TCR_OFST 0x10
+
+/*
+ * bits 5, 6, and 7 are unused/reserved
+ */
+
+/*
+ * Transmit Status Register (TSR) definitions
+ */
+
+/*
+ * PTX: Packet Transmitted. Indicates successful transmission of packet.
+ */
+#define ZE_TSR_PTX 0x01
+
+/*
+ * bit 1 (0x02) is unused/reserved
+ */
+
+/*
+ * COL: Transmit Collided. Indicates that the transmission collided at least
+ * once with another station on the network.
+ */
+#define ZE_TSR_COL 0x04
+
+/*
+ * ABT: Transmit aborted. Indicates that the transmission was aborted due to
+ * excessive collisions.
+ */
+#define ZE_TSR_ABT 0x08
+
+/*
+ * CRS: Carrier Sense Lost. Indicates that carrier was lost during the
+ * transmission of the packet. (Transmission is not aborted because
+ * of a loss of carrier)
+ */
+#define ZE_TSR_CRS 0x10
+
+/*
+ * FU: FIFO Underrun. Indicates that the NIC wasn't able to access bus/
+ * transmission memory before the FIFO emptied. Transmission of the
+ * packet was aborted.
+ */
+#define ZE_TSR_FU 0x20
+
+/*
+ * CDH: CD Heartbeat. Indicates that the collision detection circuitry
+ * isn't working correctly during a collision heartbeat test.
+ */
+#define ZE_TSR_CDH 0x40
+
+/*
+ * OWC: Out of Window Collision: Indicates that a collision occurred after
+ * a slot time (51.2us). The transmission is rescheduled just as in
+ * normal collisions.
+ */
+#define ZE_TSR_OWC 0x80
+
+/*
+ * Receiver Configuration Register (RCR) definitions
+ */
+
+/*
+ * SEP: Save Errored Packets. If 0, error packets are discarded. If set to 1,
+ * packets with CRC and frame errors are not discarded.
+ */
+#define ZE_RCR_SEP 0x01
+
+/*
+ * AR: Accept Runt packet. If 0, packet with less than 64 byte are discarded.
+ * If set to 1, packets with less than 64 byte are not discarded.
+ */
+#define ZE_RCR_AR 0x02
+
+/*
+ * AB: Accept Broadcast. If set, packets sent to the broadcast address will be
+ * accepted.
+ */
+#define ZE_RCR_AB 0x04
+
+/*
+ * AM: Accept Multicast. If set, packets sent to a multicast address are checked
+ * for a match in the hashing array. If clear, multicast packets are ignored.
+ */
+#define ZE_RCR_AM 0x08
+
+/*
+ * PRO: Promiscuous Physical. If set, all packets with a physical addresses are
+ * accepted. If clear, a physical destination address must match this
+ * station's address. Note: for full promiscuous mode, RCR_AB and RCR_AM
+ * must also be set. In addition, the multicast hashing array must be set
+ * to all 1's so that all multicast addresses are accepted.
+ */
+#define ZE_RCR_PRO 0x10
+
+/*
+ * MON: Monitor Mode. If set, packets will be checked for good CRC and framing,
+ * but are not stored in the ring-buffer. If clear, packets are stored (normal
+ * operation).
+ */
+#define ZE_RCR_MON 0x20
+
+/*
+ * bits 6 and 7 are unused/reserved.
+ */
+
+/*
+ * Receiver Status Register (RSR) definitions
+ */
+
+/*
+ * PRX: Packet Received without error.
+ */
+#define ZE_RSR_PRX 0x01
+
+/*
+ * CRC: CRC error. Indicates that a packet has a CRC error. Also set for frame
+ * alignment errors.
+ */
+#define ZE_RSR_CRC 0x02
+
+/*
+ * FAE: Frame Alignment Error. Indicates that the incoming packet did not end on
+ * a byte boundry and the CRC did not match at the last byte boundry.
+ */
+#define ZE_RSR_FAE 0x04
+
+/*
+ * FO: FIFO Overrun. Indicates that the FIFO was not serviced (during local DMA)
+ * causing it to overrun. Reception of the packet is aborted.
+ */
+#define ZE_RSR_FO 0x08
+
+/*
+ * MPA: Missed Packet. Indicates that the received packet couldn't be stored in
+ * the ring-buffer because of insufficient buffer space (exceeding the
+ * boundry pointer), or because the transfer to the ring-buffer was inhibited
+ * by RCR_MON - monitor mode.
+ */
+#define ZE_RSR_MPA 0x10
+
+/*
+ * PHY: Physical address. If 0, the packet received was sent to a physical address.
+ * If 1, the packet was accepted because of a multicast/broadcast address
+ * match.
+ */
+#define ZE_RSR_PHY 0x20
+
+/*
+ * DIS: Receiver Disabled. Set to indicate that the receiver has enetered monitor
+ * mode. Cleared when the receiver exits monitor mode.
+ */
+#define ZE_RSR_DIS 0x40
+
+/*
+ * DFR: Deferring. Set to indicate a 'jabber' condition. The CRS and COL inputs
+ * are active, and the transceiver has set the CD line as a result of the
+ * jabber.
+ */
+#define ZE_RSR_DFR 0x80
+
+/*
+ * receive ring discriptor
+ *
+ * The National Semiconductor DS8390 Network interface controller uses
+ * the following receive ring headers. The way this works is that the
+ * memory on the interface card is chopped up into 256 bytes blocks.
+ * A contiguous portion of those blocks are marked for receive packets
+ * by setting start and end block #'s in the NIC. For each packet that
+ * is put into the receive ring, one of these headers (4 bytes each) is
+ * tacked onto the front.
+ */
+struct ze_ring {
+ struct edr_status { /* received packet status */
+ u_char rs_prx:1, /* packet received intack */
+ rs_crc:1, /* crc error */
+ rs_fae:1, /* frame alignment error */
+ rs_fo:1, /* fifo overrun */
+ rs_mpa:1, /* packet received intack */
+ rs_phy:1, /* packet received intack */
+ rs_dis:1, /* packet received intack */
+ rs_dfr:1; /* packet received intack */
+ } ze_rcv_status; /* received packet status */
+ u_char next_packet; /* pointer to next packet */
+ u_short count; /* bytes in packet (length + 4) */
+};
+
+/*
+ * Common constants
+ */
+#define ZE_PAGE_SIZE 256 /* Size of RAM pages in bytes */
+#define ZE_TXBUF_SIZE 6 /* Size of TX buffer in pages */
+#define ZE_PAGE_OFFSET 0x40 /* mem buffer starts at 0x4000 */
+
+/*
+ * Vendor types
+ */
+#define ZE_VENDOR_WD_SMC 0x00 /* Western Digital/SMC */
+#define ZE_VENDOR_3COM 0x01 /* 3Com */
+
+/*
+ * Compile-time config flags
+ */
+/*
+ * this sets the default for enabling/disablng the tranceiver
+ */
+#define ZE_FLAGS_DISABLE_TRANCEIVER 0x01
+
+/*
+ * This forces the board to be used in 8/16bit mode even if it
+ * autoconfigs differently
+ */
+#define ZE_FLAGS_FORCE_8BIT_MODE 0x02
+#define ZE_FLAGS_FORCE_16BIT_MODE 0x04
+
+/*
+ * This disables the use of double transmit buffers.
+ */
+#define ZE_FLAGS_NO_DOUBLE_BUFFERING 0x08
+
+/*
+ * definitions for IBM credit card adapter for ethernet
+ */
+
+#define ZE_DATA_IO 0x10
+#define ZE_MISC 0x18
+#define ZE_RESET 0x1F
+
+#if 0
+/*
+ * Definitions for Western digital/SMC WD80x3 series ASIC
+ */
+/*
+ * Memory Select Register (MSR)
+ */
+#define ZE_WD_MSR 0
+
+#define ZE_WD_MSR_ADDR 0x3f /* Memory decode bits 18-13 */
+#define ZE_WD_MSR_MENB 0x40 /* Memory enable */
+#define ZE_WD_MSR_RST 0x80 /* Reset board */
+
+/*
+ * Interface Configuration Register (ICR)
+ */
+#define ZE_WD_ICR 1
+
+#define ZE_WD_ICR_16BIT 0x01 /* 16-bit interface */
+#define ZE_WD_ICR_OAR 0x02 /* select register. 0=BIO 1=EAR */
+#define ZE_WD_ICR_IR2 0x04 /* high order bit of encoded IRQ */
+#define ZE_WD_ICR_MSZ 0x08 /* memory size (0=8k 1=32k) */
+#define ZE_WD_ICR_RLA 0x10 /* recall LAN address */
+#define ZE_WD_ICR_RX7 0x20 /* recall all but i/o and LAN address */
+#define ZE_WD_ICR_RIO 0x40 /* recall i/o address */
+#define ZE_WD_ICR_STO 0x80 /* store to non-volatile memory */
+
+/*
+ * IO Address Register (IAR)
+ */
+#define ZE_WD_IAR 2
+
+/*
+ * EEROM Address Register
+ */
+#define ZE_WD_EAR 3
+
+/*
+ * Interrupt Request Register (IRR)
+ */
+#define ZE_WD_IRR 4
+
+#define ZE_WD_IRR_0WS 0x01 /* use 0 wait-states on 8 bit bus */
+#define ZE_WD_IRR_OUT1 0x02 /* WD83C584 pin 1 output */
+#define ZE_WD_IRR_OUT2 0x04 /* WD83C584 pin 2 output */
+#define ZE_WD_IRR_OUT3 0x08 /* WD83C584 pin 3 output */
+#define ZE_WD_IRR_FLASH 0x10 /* Flash RAM is in the ROM socket */
+
+/*
+ * The three bit of the encoded IRQ are decoded as follows:
+ *
+ * IR2 IR1 IR0 IRQ
+ * 0 0 0 2/9
+ * 0 0 1 3
+ * 0 1 0 5
+ * 0 1 1 7
+ * 1 0 0 10
+ * 1 0 1 11
+ * 1 1 0 15
+ * 1 1 1 4
+ */
+#define ZE_WD_IRR_IR0 0x20 /* bit 0 of encoded IRQ */
+#define ZE_WD_IRR_IR1 0x40 /* bit 1 of encoded IRQ */
+#define ZE_WD_IRR_IEN 0x80 /* Interrupt enable */
+
+/*
+ * LA Address Register (LAAR)
+ */
+#define ZE_WD_LAAR 5
+
+#define ZE_WD_LAAR_ADDRHI 0x1f /* bits 23-19 of RAM address */
+#define ZE_WD_LAAR_0WS16 0x20 /* enable 0 wait-states on 16 bit bus */
+#define ZE_WD_LAAR_L16EN 0x40 /* enable 16-bit operation */
+#define ZE_WD_LAAR_M16EN 0x80 /* enable 16-bit memory access */
+
+/* i/o base offset to station address/card-ID PROM */
+#define ZE_WD_PROM 8
+
+/* i/o base offset to CARD ID */
+#define ZE_WD_CARD_ID ZE_WD_PROM+6
+
+#define ZE_TYPE_WD8003S 0x02
+#define ZE_TYPE_WD8003E 0x03
+#define ZE_TYPE_WD8013EBT 0x05
+#define ZE_TYPE_WD8013EB 0x27
+#define ZE_TYPE_WD8013EBP 0x2c
+#define ZE_TYPE_WD8013EPC 0x29
+
+/* Bit definitions in card ID */
+#define ZE_WD_REV_MASK 0x1f /* Revision mask */
+#define ZE_WD_SOFTCONFIG 0x20 /* Soft config */
+#define ZE_WD_LARGERAM 0x40 /* Large RAM */
+#define ZE_MICROCHANEL 0x80 /* Microchannel bus (vs. isa) */
+
+/*
+ * Checksum total. All 8 bytes in station address PROM will add up to this
+ */
+#define ZE_WD_ROM_CHECKSUM_TOTAL 0xFF
+
+#define ZE_WD_NIC_OFFSET 0x10 /* I/O base offset to NIC */
+#define ZE_WD_ASIC_OFFSET 0 /* I/O base offset to ASIC */
+#define ZE_WD_IO_PORTS 32 /* # of i/o addresses used */
+
+#define ZE_WD_PAGE_OFFSET 0 /* page offset for NIC access to mem */
+
+/*
+ * Definitions for 3Com 3c503
+ */
+#define ZE_3COM_NIC_OFFSET 0
+#define ZE_3COM_ASIC_OFFSET 0x400 /* offset to nic i/o regs */
+
+/*
+ * XXX - The I/O address range is fragmented in the 3c503; this is the
+ * number of regs at iobase.
+ */
+#define ZE_3COM_IO_PORTS 16 /* # of i/o addresses used */
+
+#define ZE_3COM_PAGE_OFFSET 0x20 /* memory starts in second bank */
+
+/*
+ * Page Start Register. Must match PSTART in NIC
+ */
+#define ZE_3COM_PSTR 0
+
+/*
+ * Page Stop Register. Must match PSTOP in NIC
+ */
+#define ZE_3COM_PSPR 1
+
+/*
+ * Drq Timer Register. Determines number of bytes to be transfered during
+ * a DMA burst.
+ */
+#define ZE_3COM_DQTR 2
+
+/*
+ * Base Configuration Register. Read-only register which contains the
+ * board-configured I/O base address of the adapter. Bit encoded.
+ */
+#define ZE_3COM_BCFR 3
+
+#define ZE_3COM_BCFR_2E0 0x01
+#define ZE_3COM_BCFR_2A0 0x02
+#define ZE_3COM_BCFR_280 0x04
+#define ZE_3COM_BCFR_250 0x08
+#define ZE_3COM_BCFR_350 0x10
+#define ZE_3COM_BCFR_330 0x20
+#define ZE_3COM_BCFR_310 0x40
+#define ZE_3COM_BCFR_300 0x80
+
+/*
+ * EPROM Configuration Register. Read-only register which contains the
+ * board-configured memory base address. Bit encoded.
+ */
+#define ZE_3COM_PCFR 4
+
+#define ZE_3COM_PCFR_C8000 0x10
+#define ZE_3COM_PCFR_CC000 0x20
+#define ZE_3COM_PCFR_D8000 0x40
+#define ZE_3COM_PCFR_DC000 0x80
+
+/*
+ * GA Configuration Register. Gate-Array Configuration Register.
+ */
+#define ZE_3COM_GACFR 5
+
+/*
+ * mbs2 mbs1 mbs0 start address
+ * 0 0 0 0x0000
+ * 0 0 1 0x2000
+ * 0 1 0 0x4000
+ * 0 1 1 0x6000
+ *
+ * Note that with adapters with only 8K, the setting for 0x2000 must
+ * always be used.
+ */
+#define ZE_3COM_GACFR_MBS0 0x01
+#define ZE_3COM_GACFR_MBS1 0x02
+#define ZE_3COM_GACFR_MBS2 0x04
+
+#define ZE_3COM_GACFR_RSEL 0x08 /* enable shared memory */
+#define ZE_3COM_GACFR_TEST 0x10 /* for GA testing */
+#define ZE_3COM_GACFR_OWS 0x20 /* select 0WS access to GA */
+#define ZE_3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */
+#define ZE_3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */
+
+/*
+ * Control Register. Miscellaneous control functions.
+ */
+#define ZE_3COM_CR 6
+
+#define ZE_3COM_CR_RST 0x01 /* Reset GA and NIC */
+#define ZE_3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */
+#define ZE_3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */
+#define ZE_3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */
+#define ZE_3COM_CR_SHARE 0x10 /* select interrupt sharing option */
+#define ZE_3COM_CR_DBSEL 0x20 /* Double buffer select */
+#define ZE_3COM_CR_DDIR 0x40 /* DMA direction select */
+#define ZE_3COM_CR_START 0x80 /* Start DMA controller */
+
+/*
+ * Status Register. Miscellaneous status information.
+ */
+#define ZE_3COM_STREG 7
+
+#define ZE_3COM_STREG_REV 0x07 /* GA revision */
+#define ZE_3COM_STREG_DIP 0x08 /* DMA in progress */
+#define ZE_3COM_STREG_DTC 0x10 /* DMA terminal count */
+#define ZE_3COM_STREG_OFLW 0x20 /* Overflow */
+#define ZE_3COM_STREG_UFLW 0x40 /* Underflow */
+#define ZE_3COM_STREG_DPRDY 0x80 /* Data port ready */
+
+/*
+ * Interrupt/DMA Configuration Register
+ */
+#define ZE_3COM_IDCFR 8
+
+#define ZE_3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */
+#define ZE_3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */
+#define ZE_3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */
+#define ZE_3COM_IDCFR_UNUSED 0x08 /* not used */
+#define ZE_3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */
+#define ZE_3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */
+#define ZE_3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */
+#define ZE_3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */
+
+/*
+ * DMA Address Register MSB
+ */
+#define ZE_3COM_DAMSB 9
+
+/*
+ * DMA Address Register LSB
+ */
+#define ZE_3COM_DALSB 0x0a
+
+/*
+ * Vector Pointer Register 2
+ */
+#define ZE_3COM_VPTR2 0x0b
+
+/*
+ * Vector Pointer Register 1
+ */
+#define ZE_3COM_VPTR1 0x0c
+
+/*
+ * Vector Pointer Register 0
+ */
+#define ZE_3COM_VPTR0 0x0d
+
+/*
+ * Register File Access MSB
+ */
+#define ZE_3COM_RFMSB 0x0e
+
+/*
+ * Register File Access LSB
+ */
+#define ZE_3COM_RFLSB 0x0f
+#endif
diff --git a/sys/i386/isa/isa.c b/sys/i386/isa/isa.c
new file mode 100644
index 0000000..32e59e7
--- /dev/null
+++ b/sys/i386/isa/isa.c
@@ -0,0 +1,671 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)isa.c 7.2 (Berkeley) 5/13/91
+ * $Id: isa.c,v 1.16 1994/04/02 20:43:25 ache Exp $
+ */
+
+/*
+ * code to manage AT bus
+ *
+ * 92/08/18 Frank P. MacLachlan (fpm@crash.cts.com):
+ * Fixed uninitialized variable problem and added code to deal
+ * with DMA page boundaries in isa_dmarangecheck(). Fixed word
+ * mode DMA count compution and reorganized DMA setup code in
+ * isa_dmastart()
+ */
+
+#include "param.h"
+#include "systm.h" /* isn't it a joy */
+#include "kernel.h" /* to have three of these */
+#include "conf.h"
+#include "file.h"
+#include "buf.h"
+#include "uio.h"
+#include "syslog.h"
+#include "malloc.h"
+#include "rlist.h"
+#include "machine/segments.h"
+#include "vm/vm.h"
+#include <machine/spl.h>
+#include "i386/isa/isa_device.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/ic/i8237.h"
+#include "i386/isa/ic/i8042.h"
+
+/*
+** Register definitions for DMA controller 1 (channels 0..3):
+*/
+#define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */
+#define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */
+#define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */
+#define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */
+
+/*
+** Register definitions for DMA controller 2 (channels 4..7):
+*/
+#define DMA2_CHN(c) (IO_DMA2 + 2*(2*(c))) /* addr reg for channel c */
+#define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */
+#define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */
+#define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */
+
+void config_isadev __P((struct isa_device *, u_int *));
+
+/*
+ * print a conflict message
+ */
+void
+conflict(dvp, tmpdvp, item, reason, format)
+ struct isa_device *dvp, *tmpdvp;
+ int item;
+ char *reason;
+ char *format;
+{
+ printf("%s%d not probed due to %s conflict with %s%d at ",
+ dvp->id_driver->name, dvp->id_unit, reason,
+ tmpdvp->id_driver->name, tmpdvp->id_unit);
+ printf(format, item);
+ printf("\n");
+}
+
+/*
+ * Check to see if things are alread in use, like IRQ's, I/O addresses
+ * and Memory addresses.
+ */
+int
+haveseen(dvp, tmpdvp)
+ struct isa_device *dvp, *tmpdvp;
+{
+ int status = 0;
+
+ /*
+ * Only check against devices that have already been found
+ */
+ if (tmpdvp->id_alive) {
+ /*
+ * Check for I/O address conflict. We can only check the
+ * starting address of the device against the range of the
+ * device that has already been probed since we do not
+ * know how many I/O addresses this device uses.
+ */
+ if (tmpdvp->id_alive != -1) {
+ if ((dvp->id_iobase >= tmpdvp->id_iobase) &&
+ (dvp->id_iobase <=
+ (tmpdvp->id_iobase + tmpdvp->id_alive - 1))) {
+ conflict(dvp, tmpdvp, dvp->id_iobase,
+ "I/O address", "0x%x");
+ status = 1;
+ }
+ }
+ /*
+ * Check for Memory address conflict. We can check for
+ * range overlap, but it will not catch all cases since the
+ * driver may adjust the msize paramater during probe, for
+ * now we just check that the starting address does not
+ * fall within any allocated region.
+ * XXX could add a second check after the probe for overlap,
+ * since at that time we would know the full range.
+ * XXX KERNBASE is a hack, we should have vaddr in the table!
+ */
+ if(tmpdvp->id_maddr) {
+ if((KERNBASE + dvp->id_maddr >= tmpdvp->id_maddr) &&
+ (KERNBASE + dvp->id_maddr <=
+ (tmpdvp->id_maddr + tmpdvp->id_msize - 1))) {
+ conflict(dvp, tmpdvp, dvp->id_maddr, "maddr",
+ "0x%x");
+ status = 1;
+ }
+ }
+#ifndef COM_MULTIPORT
+ /*
+ * Check for IRQ conflicts.
+ */
+ if(tmpdvp->id_irq) {
+ if (tmpdvp->id_irq == dvp->id_irq) {
+ conflict(dvp, tmpdvp, ffs(dvp->id_irq) - 1,
+ "irq", "%d");
+ status = 1;
+ }
+ }
+#endif
+ /*
+ * Check for DRQ conflicts.
+ */
+ if(tmpdvp->id_drq != -1) {
+ if (tmpdvp->id_drq == dvp->id_drq) {
+ conflict(dvp, tmpdvp, dvp->id_drq,
+ "drq", "%d");
+ status = 1;
+ }
+ }
+ }
+ return (status);
+}
+
+/*
+ * Search through all the isa_devtab_* tables looking for anything that
+ * conflicts with the current device.
+ */
+int
+haveseen_isadev(dvp)
+ struct isa_device *dvp;
+{
+ struct isa_device *tmpdvp;
+ int status = 0;
+
+ for (tmpdvp = isa_devtab_tty; tmpdvp->id_driver; tmpdvp++) {
+ status |= haveseen(dvp, tmpdvp);
+ }
+ for (tmpdvp = isa_devtab_bio; tmpdvp->id_driver; tmpdvp++) {
+ status |= haveseen(dvp, tmpdvp);
+ }
+ for (tmpdvp = isa_devtab_net; tmpdvp->id_driver; tmpdvp++) {
+ status |= haveseen(dvp, tmpdvp);
+ }
+ for (tmpdvp = isa_devtab_null; tmpdvp->id_driver; tmpdvp++) {
+ status |= haveseen(dvp, tmpdvp);
+ }
+ return(status);
+}
+
+/*
+ * Configure all ISA devices
+ */
+void
+isa_configure() {
+ struct isa_device *dvp;
+
+ enable_intr();
+ splhigh();
+ INTREN(IRQ_SLAVE);
+ printf("Probing for devices on the ISA bus:\n");
+ for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) {
+ if (!haveseen_isadev(dvp))
+ config_isadev(dvp,&tty_imask);
+ }
+ for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) {
+ if (!haveseen_isadev(dvp))
+ config_isadev(dvp,&bio_imask);
+ }
+ for (dvp = isa_devtab_net; dvp->id_driver; dvp++) {
+ if (!haveseen_isadev(dvp))
+ config_isadev(dvp,&net_imask);
+ }
+ for (dvp = isa_devtab_null; dvp->id_driver; dvp++) {
+ if (!haveseen_isadev(dvp))
+ config_isadev(dvp,(u_int *) NULL);
+ }
+ bio_imask |= SWI_CLOCK_MASK;
+ net_imask |= SWI_NET_MASK;
+ tty_imask |= SWI_TTY_MASK;
+
+/*
+ * XXX we should really add the tty device to net_imask when the line is
+ * switched to SLIPDISC, and then remove it when it is switched away from
+ * SLIPDISC. No need to block out ALL ttys during a splimp when only one
+ * of them is running slip.
+ *
+ * XXX actually, blocking all ttys during a splimp doesn't matter so much
+ * with sio because the serial interrupt layer doesn't use tty_imask. Only
+ * non-serial ttys suffer. It's more stupid that ALL 'net's are blocked
+ * during spltty.
+ */
+#include "sl.h"
+#if NSL > 0
+ net_imask |= tty_imask;
+ tty_imask = net_imask;
+#endif
+ /* bio_imask |= tty_imask ; can some tty devices use buffers? */
+#ifdef DIAGNOSTIC
+ printf("bio_imask %x tty_imask %x net_imask %x\n",
+ bio_imask, tty_imask, net_imask);
+#endif
+ splnone();
+}
+
+/*
+ * Configure an ISA device.
+ */
+void
+config_isadev(isdp, mp)
+ struct isa_device *isdp;
+ u_int *mp;
+{
+ struct isa_driver *dp = isdp->id_driver;
+
+ if (isdp->id_maddr) {
+ extern u_int atdevbase;
+
+ isdp->id_maddr -= 0xa0000; /* XXX should be a define */
+ isdp->id_maddr += atdevbase;
+ }
+ isdp->id_alive = (*dp->probe)(isdp);
+ if (isdp->id_alive) {
+ /*
+ * Only print the I/O address range if id_alive != -1
+ * Right now this is a temporary fix just for the new
+ * NPX code so that if it finds a 486 that can use trap
+ * 16 it will not report I/O addresses.
+ * Rod Grimes 04/26/94
+ */
+ printf("%s%d", dp->name, isdp->id_unit);
+ if (isdp->id_alive != -1) {
+ printf(" at 0x%x", isdp->id_iobase);
+ if ((isdp->id_iobase + isdp->id_alive - 1) !=
+ isdp->id_iobase) {
+ printf("-0x%x",
+ isdp->id_iobase +
+ isdp->id_alive - 1);
+ }
+ }
+ if(isdp->id_irq)
+ printf(" irq %d", ffs(isdp->id_irq) - 1);
+ if (isdp->id_drq != -1)
+ printf(" drq %d", isdp->id_drq);
+ if (isdp->id_maddr)
+ printf(" maddr 0x%x", kvtop(isdp->id_maddr));
+ if (isdp->id_msize)
+ printf(" msize %d", isdp->id_msize);
+ if (isdp->id_flags)
+ printf(" flags 0x%x", isdp->id_flags);
+ if (isdp->id_iobase) {
+ if (isdp->id_iobase < 0x100) {
+ printf(" on motherboard\n");
+ } else {
+ if (isdp->id_iobase >= 0x1000) {
+ printf (" on eisa\n");
+ } else {
+ printf (" on isa\n");
+ }
+ }
+ }
+
+ (*dp->attach)(isdp);
+
+ if(isdp->id_irq) {
+ int intrno;
+
+ intrno = ffs(isdp->id_irq)-1;
+ setidt(ICU_OFFSET+intrno, isdp->id_intr,
+ SDT_SYS386IGT, SEL_KPL);
+ if(mp) {
+ INTRMASK(*mp,isdp->id_irq);
+ }
+ INTREN(isdp->id_irq);
+ }
+ } else {
+ printf("%s%d not found", dp->name, isdp->id_unit);
+ if (isdp->id_iobase) {
+ printf(" at 0x%x", isdp->id_iobase);
+ }
+ printf("\n");
+ }
+}
+
+#define IDTVEC(name) __CONCAT(X,name)
+/* default interrupt vector table entries */
+typedef void inthand_t();
+typedef void (*inthand_func_t)();
+extern inthand_t
+ IDTVEC(intr0), IDTVEC(intr1), IDTVEC(intr2), IDTVEC(intr3),
+ IDTVEC(intr4), IDTVEC(intr5), IDTVEC(intr6), IDTVEC(intr7),
+ IDTVEC(intr8), IDTVEC(intr9), IDTVEC(intr10), IDTVEC(intr11),
+ IDTVEC(intr12), IDTVEC(intr13), IDTVEC(intr14), IDTVEC(intr15);
+
+static inthand_func_t defvec[ICU_LEN] = {
+ &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3),
+ &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7),
+ &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11),
+ &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) };
+
+/*
+ * Fill in default interrupt table (in case of spuruious interrupt
+ * during configuration of kernel, setup interrupt control unit
+ */
+void
+isa_defaultirq()
+{
+ int i;
+
+ /* icu vectors */
+ for (i = 0; i < ICU_LEN; i++)
+ setidt(ICU_OFFSET + i, defvec[i], SDT_SYS386IGT, SEL_KPL);
+
+ /* initialize 8259's */
+ outb(IO_ICU1, 0x11); /* reset; program device, four bytes */
+ outb(IO_ICU1+1, NRSVIDT); /* starting at this vector index */
+ outb(IO_ICU1+1, 1<<2); /* slave on line 2 */
+#ifdef AUTO_EOI_1
+ outb(IO_ICU1+1, 2 | 1); /* auto EOI, 8086 mode */
+#else
+ outb(IO_ICU1+1, 1); /* 8086 mode */
+#endif
+ outb(IO_ICU1+1, 0xff); /* leave interrupts masked */
+ outb(IO_ICU1, 0x0a); /* default to IRR on read */
+ outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */
+
+ outb(IO_ICU2, 0x11); /* reset; program device, four bytes */
+ outb(IO_ICU2+1, NRSVIDT+8); /* staring at this vector index */
+ outb(IO_ICU2+1,2); /* my slave id is 2 */
+#ifdef AUTO_EOI_2
+ outb(IO_ICU2+1, 2 | 1); /* auto EOI, 8086 mode */
+#else
+ outb(IO_ICU2+1,1); /* 8086 mode */
+#endif
+ outb(IO_ICU2+1, 0xff); /* leave interrupts masked */
+ outb(IO_ICU2, 0x0a); /* default to IRR on read */
+}
+
+/* region of physical memory known to be contiguous */
+vm_offset_t isaphysmem;
+static caddr_t dma_bounce[8]; /* XXX */
+static char bounced[8]; /* XXX */
+#define MAXDMASZ 512 /* XXX */
+
+/* high byte of address is stored in this port for i-th dma channel */
+static short dmapageport[8] =
+ { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
+
+/*
+ * isa_dmacascade(): program 8237 DMA controller channel to accept
+ * external dma control by a board.
+ */
+void isa_dmacascade(unsigned chan)
+{
+ if (chan > 7)
+ panic("isa_dmacascade: impossible request");
+
+ /* set dma channel mode, and set dma channel mode */
+ if ((chan & 4) == 0) {
+ outb(DMA1_MODE, DMA37MD_CASCADE | chan);
+ outb(DMA1_SMSK, chan);
+ } else {
+ outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3));
+ outb(DMA2_SMSK, chan & 3);
+ }
+}
+
+/*
+ * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment
+ * problems by using a bounce buffer.
+ */
+void isa_dmastart(int flags, caddr_t addr, unsigned nbytes, unsigned chan)
+{ vm_offset_t phys;
+ int waport;
+ caddr_t newaddr;
+
+ if ( chan > 7
+ || (chan < 4 && nbytes > (1<<16))
+ || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1)))
+ panic("isa_dmastart: impossible request");
+
+ if (isa_dmarangecheck(addr, nbytes, chan)) {
+ if (dma_bounce[chan] == 0)
+ dma_bounce[chan] =
+ /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/
+ (caddr_t) isaphysmem + NBPG*chan;
+ bounced[chan] = 1;
+ newaddr = dma_bounce[chan];
+ *(int *) newaddr = 0; /* XXX */
+
+ /* copy bounce buffer on write */
+ if (!(flags & B_READ))
+ bcopy(addr, newaddr, nbytes);
+ addr = newaddr;
+ }
+
+ /* translate to physical */
+ phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr);
+
+ if ((chan & 4) == 0) {
+ /*
+ * Program one of DMA channels 0..3. These are
+ * byte mode channels.
+ */
+ /* set dma channel mode, and reset address ff */
+ if (flags & B_READ)
+ outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan);
+ else
+ outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan);
+ outb(DMA1_FFC, 0);
+
+ /* send start address */
+ waport = DMA1_CHN(chan);
+ outb(waport, phys);
+ outb(waport, phys>>8);
+ outb(dmapageport[chan], phys>>16);
+
+ /* send count */
+ outb(waport + 1, --nbytes);
+ outb(waport + 1, nbytes>>8);
+
+ /* unmask channel */
+ outb(DMA1_SMSK, chan);
+ } else {
+ /*
+ * Program one of DMA channels 4..7. These are
+ * word mode channels.
+ */
+ /* set dma channel mode, and reset address ff */
+ if (flags & B_READ)
+ outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3));
+ else
+ outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3));
+ outb(DMA2_FFC, 0);
+
+ /* send start address */
+ waport = DMA2_CHN(chan - 4);
+ outb(waport, phys>>1);
+ outb(waport, phys>>9);
+ outb(dmapageport[chan], phys>>16);
+
+ /* send count */
+ nbytes >>= 1;
+ outb(waport + 2, --nbytes);
+ outb(waport + 2, nbytes>>8);
+
+ /* unmask channel */
+ outb(DMA2_SMSK, chan & 3);
+ }
+}
+
+void isa_dmadone(int flags, caddr_t addr, int nbytes, int chan)
+{
+
+ /* copy bounce buffer on read */
+ /*if ((flags & (B_PHYS|B_READ)) == (B_PHYS|B_READ))*/
+ if (bounced[chan]) {
+ bcopy(dma_bounce[chan], addr, nbytes);
+ bounced[chan] = 0;
+ }
+}
+
+/*
+ * Check for problems with the address range of a DMA transfer
+ * (non-contiguous physical pages, outside of bus address space,
+ * crossing DMA page boundaries).
+ * Return true if special handling needed.
+ */
+
+int
+isa_dmarangecheck(caddr_t va, unsigned length, unsigned chan) {
+ vm_offset_t phys, priorpage = 0, endva;
+ u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1);
+
+ endva = (vm_offset_t)round_page(va + length);
+ for (; va < (caddr_t) endva ; va += NBPG) {
+ phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va));
+#define ISARAM_END RAM_END
+ if (phys == 0)
+ panic("isa_dmacheck: no physical page present");
+ if (phys >= ISARAM_END)
+ return (1);
+ if (priorpage) {
+ if (priorpage + NBPG != phys)
+ return (1);
+ /* check if crossing a DMA page boundary */
+ if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk)
+ return (1);
+ }
+ priorpage = phys;
+ }
+ return (0);
+}
+
+/* head of queue waiting for physmem to become available */
+struct buf isa_physmemq;
+
+/* blocked waiting for resource to become free for exclusive use */
+static isaphysmemflag;
+/* if waited for and call requested when free (B_CALL) */
+static void (*isaphysmemunblock)(); /* needs to be a list */
+
+/*
+ * Allocate contiguous physical memory for transfer, returning
+ * a *virtual* address to region. May block waiting for resource.
+ * (assumed to be called at splbio())
+ */
+caddr_t
+isa_allocphysmem(caddr_t va, unsigned length, void (*func)()) {
+
+ isaphysmemunblock = func;
+ while (isaphysmemflag & B_BUSY) {
+ isaphysmemflag |= B_WANTED;
+ tsleep((caddr_t)&isaphysmemflag, PRIBIO, "isaphys", 0);
+ }
+ isaphysmemflag |= B_BUSY;
+
+ return((caddr_t)isaphysmem);
+}
+
+/*
+ * Free contiguous physical memory used for transfer.
+ * (assumed to be called at splbio())
+ */
+void
+isa_freephysmem(caddr_t va, unsigned length) {
+
+ isaphysmemflag &= ~B_BUSY;
+ if (isaphysmemflag & B_WANTED) {
+ isaphysmemflag &= B_WANTED;
+ wakeup((caddr_t)&isaphysmemflag);
+ if (isaphysmemunblock)
+ (*isaphysmemunblock)();
+ }
+}
+
+/*
+ * Handle a NMI, possibly a machine check.
+ * return true to panic system, false to ignore.
+ */
+int
+isa_nmi(cd)
+ int cd;
+{
+
+ log(LOG_CRIT, "\nNMI port 61 %x, port 70 %x\n", inb(0x61), inb(0x70));
+ return(0);
+}
+
+/*
+ * Caught a stray interrupt, notify
+ */
+void
+isa_strayintr(d)
+ int d;
+{
+
+ /* DON'T BOTHER FOR NOW! */
+ /* for some reason, we get bursts of intr #7, even if not enabled! */
+ /*
+ * Well the reason you got bursts of intr #7 is because someone
+ * raised an interrupt line and dropped it before the 8259 could
+ * prioritize it. This is documented in the intel data book. This
+ * means you have BAD hardware! I have changed this so that only
+ * the first 5 get logged, then it quits logging them, and puts
+ * out a special message. rgrimes 3/25/1993
+ */
+ extern u_long intrcnt_stray;
+
+ intrcnt_stray++;
+ if (intrcnt_stray <= 5)
+ log(LOG_ERR,"ISA strayintr %x\n", d);
+ if (intrcnt_stray == 5)
+ log(LOG_CRIT,"Too many ISA strayintr not logging any more\n");
+}
+
+/*
+ * find an ISA device in a given isa_devtab_* table, given
+ * the table to search, the expected id_driver entry, and the unit number.
+ *
+ * this function is defined in isa_device.h, and this location is debatable;
+ * i put it there because it's useless w/o, and directly operates on
+ * the other stuff in that file.
+ *
+ */
+
+struct isa_device *find_isadev(table, driverp, unit)
+ struct isa_device *table;
+ struct isa_driver *driverp;
+ int unit;
+{
+ if (driverp == NULL) /* sanity check */
+ return NULL;
+
+ while ((table->id_driver != driverp) || (table->id_unit != unit)) {
+ if (table->id_driver == 0)
+ return NULL;
+
+ table++;
+ }
+
+ return table;
+}
+
+/*
+ * Return nonzero if a (masked) irq is pending for a given device.
+ */
+int
+isa_irq_pending(dvp)
+ struct isa_device *dvp;
+{
+ unsigned id_irq;
+
+ id_irq = (unsigned short) dvp->id_irq; /* XXX silly type in struct */
+ if (id_irq & 0xff)
+ return (inb(IO_ICU1) & id_irq);
+ return (inb(IO_ICU2) & (id_irq >> 8));
+}
diff --git a/sys/i386/isa/isa.h b/sys/i386/isa/isa.h
new file mode 100644
index 0000000..e2a26e7
--- /dev/null
+++ b/sys/i386/isa/isa.h
@@ -0,0 +1,181 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)isa.h 5.7 (Berkeley) 5/9/91
+ * $Id: isa.h,v 1.4 1994/01/05 15:03:28 rgrimes Exp $
+ */
+
+#ifndef _I386_ISA_ISA_H_
+#define _I386_ISA_ISA_H_ 1
+
+/*
+ * ISA Bus conventions
+ */
+
+#ifndef LOCORE
+#include <sys/cdefs.h>
+
+extern unsigned int atdevbase; /* offset in virtual memory of ISA io mem */
+unsigned char rtcin __P((int));
+#endif
+
+
+/*
+ * Input / Output Port Assignments
+ */
+
+#ifndef IO_BEGIN
+#define IO_ISABEGIN 0x000 /* 0x000 - Beginning of I/O Registers */
+
+ /* CPU Board */
+#define IO_DMA1 0x000 /* 8237A DMA Controller #1 */
+#define IO_ICU1 0x020 /* 8259A Interrupt Controller #1 */
+#define IO_TIMER1 0x040 /* 8253 Timer #1 */
+#define IO_TIMER2 0x048 /* 8253 Timer #2 */
+#define IO_KBD 0x060 /* 8042 Keyboard */
+#define IO_PPI 0x061 /* Programmabel Peripheral Interface */
+#define IO_RTC 0x070 /* RTC */
+#define IO_NMI IO_RTC /* NMI Control */
+#define IO_DMAPG 0x080 /* DMA Page Registers */
+#define IO_ICU2 0x0A0 /* 8259A Interrupt Controller #2 */
+#define IO_DMA2 0x0C0 /* 8237A DMA Controller #2 */
+#define IO_NPX 0x0F0 /* Numeric Coprocessor */
+
+ /* Cards */
+ /* 0x100 - 0x16F Open */
+
+#define IO_WD2 0x170 /* Secondary Fixed Disk Controller */
+
+ /* 0x178 - 0x1EF Open */
+
+#define IO_WD1 0x1f0 /* Primary Fixed Disk Controller */
+#define IO_GAME 0x200 /* Game Controller */
+
+ /* 0x208 - 0x277 Open */
+
+#define IO_LPT2 0x278 /* Parallel Port #2 */
+
+ /* 0x280 - 0x2E7 Open */
+
+#define IO_COM4 0x2e8 /* COM4 i/o address */
+
+ /* 0x2F0 - 0x2F7 Open */
+
+#define IO_COM2 0x2f8 /* COM2 i/o address */
+ /* 0x300 - 0x32F Open */
+
+#define IO_BT0 0x330 /* bustek 742a default addr. */
+#define IO_AHA0 0x330 /* adaptec 1542 default addr. */
+#define IO_UHA0 0x330 /* ultrastore 14f default addr. */
+#define IO_BT1 0x334 /* bustek 742a default addr. */
+#define IO_AHA1 0x334 /* adaptec 1542 default addr. */
+ /* 0x338 - 0x36F Open */
+
+#define IO_FD2 0x370 /* secondary base i/o address */
+#define IO_LPT1 0x378 /* Parallel Port #1 */
+
+ /* 0x380 - 0x3AF Open */
+
+#define IO_MDA 0x3B0 /* Monochome Adapter */
+#define IO_LPT3 0x3BC /* Monochome Adapter Printer Port */
+#define IO_VGA 0x3C0 /* E/VGA Ports */
+#define IO_CGA 0x3D0 /* CGA Ports */
+
+ /* 0x3E0 - 0x3E7 Open */
+
+#define IO_COM3 0x3e8 /* COM3 i/o address */
+#define IO_FD1 0x3f0 /* primary base i/o address */
+#define IO_COM1 0x3f8 /* COM1 i/o address */
+
+#define IO_ISAEND 0x3FF /* - 0x3FF End of I/O Registers */
+#endif IO_ISABEGIN
+
+/*
+ * Input / Output Port Sizes - these are from several sources, and tend
+ * to be the larger of what was found, ie COM ports can be 4, but some
+ * boards do not fully decode the address, thus 8 ports are used.
+ */
+
+#ifndef IO_ISASIZES
+#define IO_ISASIZES
+
+#define IO_COMSIZE 8 /* 8250, 16X50 com controllers (4?) */
+#define IO_CGASIZE 16 /* CGA controllers */
+#define IO_DMASIZE 16 /* 8237 DMA controllers */
+#define IO_DPGSIZE 32 /* 74LS612 DMA page reisters */
+#define IO_FDCSIZE 8 /* Nec765 floppy controllers */
+#define IO_WDCSIZE 8 /* WD compatible disk controllers */
+#define IO_GAMSIZE 16 /* AT compatible game controllers */
+#define IO_ICUSIZE 16 /* 8259A interrupt controllers */
+#define IO_KBDSIZE 16 /* 8042 Keyboard controllers */
+#define IO_LPTSIZE 8 /* LPT controllers, some use only 4 */
+#define IO_MDASIZE 16 /* Monochrome display controllers */
+#define IO_RTCSIZE 16 /* CMOS real time clock, NMI control */
+#define IO_TMRSIZE 16 /* 8253 programmable timers */
+#define IO_NPXSIZE 16 /* 80387/80487 NPX registers */
+#define IO_VGASIZE 16 /* VGA controllers */
+
+#endif /* IO_ISASIZES */
+
+/*
+ * Input / Output Memory Physical Addresses
+ */
+
+#ifndef IOM_BEGIN
+#define IOM_BEGIN 0x0a0000 /* Start of I/O Memory "hole" */
+#define IOM_END 0x100000 /* End of I/O Memory "hole" */
+#define IOM_SIZE (IOM_END - IOM_BEGIN)
+#endif IOM_BEGIN
+
+/*
+ * RAM Physical Address Space (ignoring the above mentioned "hole")
+ */
+
+#ifndef RAM_BEGIN
+#define RAM_BEGIN 0x0000000 /* Start of RAM Memory */
+#define RAM_END 0x1000000 /* End of RAM Memory */
+#define RAM_SIZE (RAM_END - RAM_BEGIN)
+#endif RAM_BEGIN
+
+/*
+ * Oddball Physical Memory Addresses
+ */
+#ifndef COMPAQ_RAMRELOC
+#define COMPAQ_RAMRELOC 0x80c00000 /* Compaq RAM relocation/diag */
+#define COMPAQ_RAMSETUP 0x80c00002 /* Compaq RAM setup */
+#define WEITEK_FPU 0xC0000000 /* WTL 2167 */
+#define CYRIX_EMC 0xC0000000 /* Cyrix EMC */
+#endif COMPAQ_RAMRELOC
+#endif /* _I386_ISA_ISA_H_ */
diff --git a/sys/i386/isa/isa_device.h b/sys/i386/isa/isa_device.h
new file mode 100644
index 0000000..16fac9a
--- /dev/null
+++ b/sys/i386/isa/isa_device.h
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)isa_device.h 7.1 (Berkeley) 5/9/91
+ * $Id: isa_device.h,v 1.4 1993/12/19 00:50:42 wollman Exp $
+ */
+
+#ifndef _I386_ISA_ISA_DEVICE_H_
+#define _I386_ISA_ISA_DEVICE_H_ 1
+
+/*
+ * ISA Bus Autoconfiguration
+ */
+
+/*
+ * Per device structure.
+ */
+struct isa_device {
+ struct isa_driver *id_driver;
+ short id_iobase; /* base i/o address */
+ u_short id_irq; /* interrupt request */
+ short id_drq; /* DMA request */
+ caddr_t id_maddr; /* physical i/o memory address on bus (if any)*/
+ int id_msize; /* size of i/o memory */
+ void (*id_intr)(); /* interrupt interface routine */
+ int id_unit; /* unit number */
+ int id_flags; /* flags */
+ int id_scsiid; /* scsi id if needed */
+ int id_alive; /* device is present */
+};
+
+/*
+ * Per-driver structure.
+ *
+ * Each device driver defines entries for a set of routines
+ * as well as an array of types which are acceptable to it.
+ * These are used at boot time by the configuration program.
+ */
+struct isa_driver {
+ int (*probe)(); /* test whether device is present */
+ int (*attach)(); /* setup driver for a device */
+ char *name; /* device name */
+};
+
+extern struct isa_device isa_devtab_bio[], isa_devtab_tty[], isa_devtab_net[],
+ isa_devtab_null[], isa_biotab_wdc[], isa_biotab_fdc[];
+
+extern struct isa_device *find_isadev(/* table, driver, unit*/);
+
+extern void isa_dmastart(int, caddr_t, unsigned, unsigned);
+extern void isa_dmadone(int, caddr_t, int, int);
+
+#endif /* _I386_ISA_ISA_DEVICE_H_ */
diff --git a/sys/i386/isa/iso8859.font b/sys/i386/isa/iso8859.font
new file mode 100644
index 0000000..89c9b29
--- /dev/null
+++ b/sys/i386/isa/iso8859.font
@@ -0,0 +1,1243 @@
+/*
+ * Copyright (C) 1992, 1993, 1994 Søren Schmidt
+ *
+ * This program is free software; you may redistribute it and/or
+ * modify it, provided that it retain the above copyright notice
+ * and the following disclaimer.
+ *
+ * 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.
+ *
+ * Søren Schmidt Email: sos@login.dkuug.dk
+ * Tritonvej 36 UUCP: ...uunet!dkuug!login!sos
+ * DK9210 Aalborg SO Phone: +45 9814 8076
+ *
+ * from:@(#)iso8859.font 1.1 940105
+ * $Id: iso8859.font,v 1.4 1993/10/28 06:15:05 rgrimes Exp $
+ */
+
+char font_8x8[256*8] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x7E, 0x81, 0xA5, 0x81, 0xBD, 0x99, 0x81, 0x7E,
+0x7E, 0xFF, 0xDB, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E,
+0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00,
+0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00,
+0x38, 0x7C, 0x38, 0xFE, 0xFE, 0x7C, 0x38, 0x7C,
+0x10, 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x7C,
+0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x00, 0x00,
+0xFF, 0xFF, 0xE7, 0xC3, 0xC3, 0xE7, 0xFF, 0xFF,
+0x00, 0x3C, 0x66, 0x42, 0x42, 0x66, 0x3C, 0x00,
+0xFF, 0xC3, 0x99, 0xBD, 0xBD, 0x99, 0xC3, 0xFF,
+0x0F, 0x07, 0x0F, 0x7D, 0xCC, 0xCC, 0xCC, 0x78,
+0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18,
+0x3F, 0x33, 0x3F, 0x30, 0x30, 0x70, 0xF0, 0xE0,
+0x7F, 0x63, 0x7F, 0x63, 0x63, 0x67, 0xE6, 0xC0,
+0x99, 0x5A, 0x3C, 0xE7, 0xE7, 0x3C, 0x5A, 0x99,
+0x80, 0xE0, 0xF8, 0xFE, 0xF8, 0xE0, 0x80, 0x00,
+0x02, 0x0E, 0x3E, 0xFE, 0x3E, 0x0E, 0x02, 0x00,
+0x18, 0x3C, 0x7E, 0x18, 0x18, 0x7E, 0x3C, 0x18,
+0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
+0x7F, 0xDB, 0xDB, 0x7B, 0x1B, 0x1B, 0x1B, 0x00,
+0x3E, 0x63, 0x38, 0x6C, 0x6C, 0x38, 0xCC, 0x78,
+0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x7E, 0x00,
+0x18, 0x3C, 0x7E, 0x18, 0x7E, 0x3C, 0x18, 0xFF,
+0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x00,
+0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00,
+0x00, 0x18, 0x0C, 0xFE, 0x0C, 0x18, 0x00, 0x00,
+0x00, 0x30, 0x60, 0xFE, 0x60, 0x30, 0x00, 0x00,
+0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xFE, 0x00, 0x00,
+0x00, 0x24, 0x66, 0xFF, 0x66, 0x24, 0x00, 0x00,
+0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x00, 0x00,
+0x00, 0xFF, 0xFF, 0x7E, 0x3C, 0x18, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
+0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00,
+0x30, 0x7C, 0xC0, 0x78, 0x0C, 0xF8, 0x30, 0x00,
+0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00,
+0x38, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0x76, 0x00,
+0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
+0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
+0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00,
+0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
+0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
+0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00,
+0x7C, 0xC6, 0xCE, 0xDE, 0xF6, 0xE6, 0x7C, 0x00,
+0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00,
+0x78, 0xCC, 0x0C, 0x38, 0x60, 0xCC, 0xFC, 0x00,
+0x78, 0xCC, 0x0C, 0x38, 0x0C, 0xCC, 0x78, 0x00,
+0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x1E, 0x00,
+0xFC, 0xC0, 0xF8, 0x0C, 0x0C, 0xCC, 0x78, 0x00,
+0x38, 0x60, 0xC0, 0xF8, 0xCC, 0xCC, 0x78, 0x00,
+0xFC, 0xCC, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00,
+0x78, 0xCC, 0xCC, 0x78, 0xCC, 0xCC, 0x78, 0x00,
+0x78, 0xCC, 0xCC, 0x7C, 0x0C, 0x18, 0x70, 0x00,
+0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
+0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
+0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x00,
+0x00, 0x00, 0xFC, 0x00, 0x00, 0xFC, 0x00, 0x00,
+0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00,
+0x78, 0xCC, 0x0C, 0x18, 0x30, 0x00, 0x30, 0x00,
+0x7C, 0xC6, 0xDE, 0xDE, 0xDE, 0xC0, 0x78, 0x00,
+0x30, 0x78, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0x00,
+0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00,
+0x3C, 0x66, 0xC0, 0xC0, 0xC0, 0x66, 0x3C, 0x00,
+0xF8, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00,
+0xFE, 0x62, 0x68, 0x78, 0x68, 0x62, 0xFE, 0x00,
+0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0xF0, 0x00,
+0x3C, 0x66, 0xC0, 0xC0, 0xCE, 0x66, 0x3E, 0x00,
+0xCC, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0xCC, 0x00,
+0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+0x1E, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00,
+0xE6, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0xE6, 0x00,
+0xF0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00,
+0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00,
+0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00,
+0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00,
+0xFC, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00,
+0x78, 0xCC, 0xCC, 0xCC, 0xDC, 0x78, 0x1C, 0x00,
+0xFC, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0xE6, 0x00,
+0x78, 0xCC, 0xE0, 0x70, 0x1C, 0xCC, 0x78, 0x00,
+0xFC, 0xB4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00,
+0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00,
+0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00,
+0xC6, 0xC6, 0x6C, 0x38, 0x38, 0x6C, 0xC6, 0x00,
+0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x78, 0x00,
+0xFE, 0xC6, 0x8C, 0x18, 0x32, 0x66, 0xFE, 0x00,
+0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
+0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00,
+0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
+0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00,
+0xE0, 0x60, 0x60, 0x7C, 0x66, 0x66, 0xDC, 0x00,
+0x00, 0x00, 0x78, 0xCC, 0xC0, 0xCC, 0x78, 0x00,
+0x1C, 0x0C, 0x0C, 0x7C, 0xCC, 0xCC, 0x76, 0x00,
+0x00, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00,
+0x38, 0x6C, 0x60, 0xF0, 0x60, 0x60, 0xF0, 0x00,
+0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8,
+0xE0, 0x60, 0x6C, 0x76, 0x66, 0x66, 0xE6, 0x00,
+0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+0x0C, 0x00, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78,
+0xE0, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0xE6, 0x00,
+0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+0x00, 0x00, 0xCC, 0xFE, 0xFE, 0xD6, 0xC6, 0x00,
+0x00, 0x00, 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00,
+0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00,
+0x00, 0x00, 0xDC, 0x66, 0x66, 0x7C, 0x60, 0xF0,
+0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0x1E,
+0x00, 0x00, 0xDC, 0x76, 0x66, 0x60, 0xF0, 0x00,
+0x00, 0x00, 0x7C, 0xC0, 0x78, 0x0C, 0xF8, 0x00,
+0x10, 0x30, 0x7C, 0x30, 0x30, 0x34, 0x18, 0x00,
+0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00,
+0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00,
+0x00, 0x00, 0xC6, 0xD6, 0xFE, 0xFE, 0x6C, 0x00,
+0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00,
+0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8,
+0x00, 0x00, 0xFC, 0x98, 0x30, 0x64, 0xFC, 0x00,
+0x1C, 0x30, 0x30, 0xE0, 0x30, 0x30, 0x1C, 0x00,
+0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
+0xE0, 0x30, 0x30, 0x1C, 0x30, 0x30, 0xE0, 0x00,
+0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3C, 0x7E, 0xFF, 0x7E, 0x18, 0x00,
+0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, 0xAA,
+0x44, 0x44, 0x44, 0x44, 0x1F, 0x04, 0x04, 0x04,
+0x7C, 0x40, 0x40, 0x40, 0x1F, 0x10, 0x10, 0x10,
+0x38, 0x44, 0x44, 0x38, 0x1E, 0x11, 0x14, 0x13,
+0x40, 0x40, 0x40, 0x7C, 0x1F, 0x10, 0x10, 0x10,
+0x38, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00,
+0x44, 0x64, 0x4C, 0x44, 0x10, 0x10, 0x10, 0x1F,
+0x44, 0x44, 0x28, 0x10, 0x1F, 0x04, 0x04, 0x04,
+0x18, 0x18, 0x18, 0x18, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xF8, 0x18, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, 0x00, 0x00,
+0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0xFF, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xFF, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x00, 0x0C, 0x30, 0x60, 0x18, 0x0C, 0x7E, 0x00,
+0x00, 0x30, 0x0C, 0x06, 0x18, 0x30, 0x7E, 0x00,
+0x00, 0x00, 0x03, 0x3E, 0x36, 0x36, 0x6C, 0x00,
+0x00, 0x00, 0x04, 0x7E, 0x10, 0x7E, 0x40, 0x00,
+0x00, 0x1C, 0x30, 0x30, 0x30, 0x30, 0x7E, 0x00,
+0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x30, 0x00, 0x30, 0x30, 0x78, 0x78, 0x30, 0x00,
+0x00, 0x00, 0x10, 0x7C, 0xC0, 0xC0, 0x7C, 0x10,
+0x00, 0x38, 0x60, 0x60, 0xF0, 0x60, 0xFC, 0x00,
+0x00, 0xC3, 0x3C, 0x66, 0x66, 0x3C, 0xC3, 0x00,
+0x00, 0xCC, 0xCC, 0x78, 0x30, 0xFC, 0x30, 0x00,
+0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
+0x7E, 0xC0, 0x7C, 0xC6, 0x7C, 0x06, 0xFC, 0x00,
+0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x7C, 0x82, 0xBA, 0xA2, 0xBA, 0x82, 0x7C, 0x00,
+0x1C, 0x06, 0x1E, 0x22, 0x1F, 0x3F, 0x00, 0x00,
+0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00,
+0x00, 0xFE, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x7C, 0x82, 0xBA, 0xB2, 0xAA, 0x82, 0x7C, 0x00,
+0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x38, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x10, 0x7C, 0x10, 0x00, 0x7C, 0x00, 0x00,
+0x1C, 0x36, 0x06, 0x18, 0x3E, 0x00, 0x00, 0x00,
+0x1E, 0x02, 0x0E, 0x02, 0x1E, 0x00, 0x00, 0x00,
+0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0xC0,
+0x7E, 0xCA, 0xCA, 0x7E, 0x0A, 0x0A, 0x0A, 0x00,
+0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30,
+0x06, 0x0E, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00,
+0x0E, 0x11, 0x11, 0x11, 0x0E, 0x1F, 0x00, 0x00,
+0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00,
+0x60, 0xE0, 0x66, 0x6C, 0x33, 0x67, 0x0F, 0x03,
+0x60, 0xE0, 0x66, 0x6C, 0x36, 0x6A, 0x04, 0x0E,
+0xF0, 0x20, 0x96, 0x6C, 0x33, 0x67, 0x0F, 0x03,
+0x30, 0x00, 0x30, 0x60, 0xC0, 0xCC, 0x78, 0x00,
+0x18, 0x0C, 0x30, 0x78, 0xCC, 0xFC, 0xCC, 0x00,
+0x60, 0xC0, 0x30, 0x78, 0xCC, 0xFC, 0xCC, 0x00,
+0x78, 0x84, 0x30, 0x78, 0xCC, 0xFC, 0xCC, 0x00,
+0x66, 0x98, 0x30, 0x78, 0xCC, 0xFC, 0xCC, 0x00,
+0xCC, 0x00, 0x30, 0x78, 0xCC, 0xFC, 0xCC, 0x00,
+0x30, 0x48, 0x30, 0x78, 0xCC, 0xFC, 0xCC, 0x00,
+0x3E, 0x78, 0x98, 0x9C, 0xF8, 0x98, 0x9E, 0x00,
+0x3C, 0x66, 0xC0, 0xC0, 0xC0, 0x66, 0x1C, 0x30,
+0x30, 0x18, 0xFE, 0x62, 0x78, 0x62, 0xFE, 0x00,
+0x18, 0x30, 0xFE, 0x62, 0x78, 0x62, 0xFE, 0x00,
+0x38, 0x44, 0xFE, 0x62, 0x78, 0x62, 0xFE, 0x00,
+0x66, 0x00, 0xFE, 0x62, 0x78, 0x62, 0xFE, 0x00,
+0x60, 0x30, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00,
+0x18, 0x30, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00,
+0x78, 0x84, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00,
+0xCC, 0x00, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00,
+0x78, 0x6C, 0x66, 0xF6, 0x66, 0x6C, 0x78, 0x00,
+0x66, 0x98, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0x00,
+0x30, 0x18, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
+0x18, 0x30, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
+0x38, 0x44, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
+0x66, 0x98, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
+0xC6, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
+0x00, 0xC6, 0x6C, 0x38, 0x38, 0x6C, 0xC6, 0x00,
+0x06, 0x7C, 0xCE, 0x9A, 0xB2, 0xE6, 0x78, 0xC0,
+0x60, 0x30, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00,
+0x18, 0x30, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00,
+0x78, 0x84, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00,
+0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00,
+0x18, 0x30, 0xCC, 0xCC, 0x78, 0x30, 0x78, 0x00,
+0x60, 0x78, 0x6C, 0x78, 0x60, 0x60, 0x60, 0x00,
+0x78, 0xCC, 0xC4, 0xDC, 0xC6, 0xC6, 0xDC, 0xC0,
+0x30, 0x18, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00,
+0x18, 0x30, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00,
+0x78, 0x84, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00,
+0x66, 0x98, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00,
+0xCC, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00,
+0x30, 0x48, 0x38, 0x0C, 0x7C, 0xCC, 0x76, 0x00,
+0x00, 0x00, 0xEC, 0x32, 0x7E, 0xB0, 0x6E, 0x00,
+0x00, 0x00, 0x3C, 0x66, 0xC0, 0x66, 0x1C, 0x30,
+0x30, 0x18, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00,
+0x18, 0x30, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00,
+0x78, 0x84, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00,
+0xCC, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00,
+0x60, 0x30, 0x00, 0x70, 0x30, 0x30, 0x78, 0x00,
+0x18, 0x30, 0x00, 0x70, 0x30, 0x30, 0x78, 0x00,
+0x70, 0x88, 0x00, 0x70, 0x30, 0x30, 0x78, 0x00,
+0xCC, 0x00, 0x00, 0x70, 0x30, 0x30, 0x78, 0x00,
+0x6C, 0x38, 0x6C, 0x0C, 0x6C, 0xCC, 0x78, 0x00,
+0x66, 0x98, 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00,
+0x60, 0x30, 0x00, 0x7C, 0xC6, 0xC6, 0x7C, 0x00,
+0x18, 0x30, 0x00, 0x7C, 0xC6, 0xC6, 0x7C, 0x00,
+0x38, 0x44, 0x00, 0x7C, 0xC6, 0xC6, 0x7C, 0x00,
+0x66, 0x98, 0x00, 0x7C, 0xC6, 0xC6, 0x7C, 0x00,
+0xC6, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x7C, 0x00,
+0x00, 0x00, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x00,
+0x00, 0x00, 0x06, 0x7C, 0xDE, 0xF6, 0x7C, 0xC0,
+0x60, 0x30, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00,
+0x18, 0x30, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00,
+0x30, 0x48, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00,
+0xCC, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00,
+0x18, 0x30, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8,
+0xE0, 0x78, 0x6C, 0x66, 0x6C, 0x78, 0xE0, 0x00,
+0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8
+};
+
+char font_8x14[256*14] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x7E, 0x81, 0xA5, 0x81, 0x81, 0xBD, 0x99, 0x81,
+0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xFF,
+0xDB, 0xFF, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x7F, 0x7F,
+0x7F, 0x7F, 0x3E, 0x1C, 0x08, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x08, 0x1C, 0x3E, 0x7F, 0x3E,
+0x1C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x3C, 0x3C, 0xE7, 0xE7, 0xE7, 0x18, 0x18,
+0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C,
+0x7E, 0xFF, 0xFF, 0x7E, 0x18, 0x18, 0x3C, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+0x3C, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xC3, 0xC3,
+0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
+0x00, 0x00, 0x3C, 0x24, 0x42, 0x42, 0x24, 0x3C,
+0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+0xC3, 0xDB, 0xBD, 0xBD, 0xDB, 0xC3, 0xFF, 0xFF,
+0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x07, 0x0D, 0x19,
+0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18,
+0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x0C, 0x0A, 0x09, 0x09, 0x09, 0x0A, 0x08, 0x38,
+0x78, 0x78, 0x30, 0x00, 0x00, 0x00, 0x1F, 0x11,
+0x1F, 0x11, 0x11, 0x11, 0x13, 0x37, 0x77, 0x72,
+0x20, 0x00, 0x00, 0x00, 0x18, 0x18, 0xDB, 0x3C,
+0xE7, 0x3C, 0xDB, 0x18, 0x18, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x40, 0x60, 0x70, 0x7C, 0x7F, 0x7C,
+0x70, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x01, 0x03, 0x07, 0x1F, 0x7F, 0x1F, 0x07, 0x03,
+0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C,
+0x7E, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x33, 0x33, 0x33, 0x33,
+0x33, 0x33, 0x00, 0x33, 0x33, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7F, 0xDB, 0xDB, 0xDB, 0x7B, 0x1B,
+0x1B, 0x1B, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x3E,
+0x63, 0x30, 0x1C, 0x36, 0x63, 0x63, 0x36, 0x1C,
+0x06, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x7F, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18,
+0x18, 0x18, 0x7E, 0x3C, 0x18, 0x7E, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C,
+0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x0C, 0x06, 0x7F, 0x06, 0x0C, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30,
+0x7F, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60,
+0x7F, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x24, 0x42, 0xFF, 0x42, 0x24, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x18, 0x3C, 0x7E, 0xFF, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+0x7E, 0x3C, 0x18, 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, 0x00, 0x18,
+0x18, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66,
+0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x7F, 0x36,
+0x36, 0x36, 0x7F, 0x36, 0x36, 0x00, 0x00, 0x00,
+0x08, 0x08, 0x3E, 0x63, 0x60, 0x60, 0x3E, 0x03,
+0x03, 0x63, 0x3E, 0x08, 0x08, 0x00, 0x00, 0x00,
+0x00, 0x61, 0x63, 0x06, 0x0C, 0x18, 0x30, 0x63,
+0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x36,
+0x36, 0x1C, 0x3B, 0x6E, 0x66, 0x66, 0x3B, 0x00,
+0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x60, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30,
+0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x18,
+0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x66, 0x3C, 0x7E, 0x3C, 0x66, 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,
+0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x06, 0x0C,
+0x18, 0x30, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3E, 0x63, 0x67, 0x6F, 0x7B, 0x73,
+0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x0C, 0x1C, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x63,
+0x03, 0x06, 0x0C, 0x18, 0x30, 0x63, 0x7F, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x3E, 0x63, 0x03, 0x03,
+0x1E, 0x03, 0x03, 0x63, 0x3E, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x06, 0x0E, 0x1E, 0x36, 0x66, 0x7F,
+0x06, 0x06, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x7E, 0x60, 0x60, 0x60, 0x7E, 0x03, 0x03, 0x63,
+0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x30,
+0x60, 0x60, 0x7E, 0x63, 0x63, 0x63, 0x3E, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x7F, 0x63, 0x03, 0x06,
+0x0C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3E, 0x63, 0x63, 0x63, 0x3E, 0x63,
+0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x3E, 0x63, 0x63, 0x63, 0x3F, 0x03, 0x03, 0x06,
+0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00,
+0x00, 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x30,
+0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x7E, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30,
+0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x3E, 0x63, 0x63, 0x06,
+0x0C, 0x0C, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3E, 0x63, 0x63, 0x6F, 0x6F, 0x6F,
+0x6E, 0x60, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x08, 0x1C, 0x36, 0x63, 0x63, 0x7F, 0x63, 0x63,
+0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x33,
+0x33, 0x33, 0x3E, 0x33, 0x33, 0x33, 0x7E, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x1E, 0x33, 0x60, 0x60,
+0x60, 0x60, 0x60, 0x33, 0x1E, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7C, 0x36, 0x33, 0x33, 0x33, 0x33,
+0x33, 0x36, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x7F, 0x33, 0x30, 0x34, 0x3C, 0x34, 0x30, 0x33,
+0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x33,
+0x30, 0x34, 0x3C, 0x34, 0x30, 0x30, 0x78, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x1E, 0x33, 0x60, 0x60,
+0x60, 0x6F, 0x63, 0x33, 0x1D, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x7F, 0x63,
+0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x06,
+0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3C, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x73, 0x33, 0x36, 0x36,
+0x3C, 0x36, 0x36, 0x33, 0x73, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30,
+0x30, 0x33, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x63, 0x77, 0x7F, 0x6B, 0x63, 0x63, 0x63, 0x63,
+0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x73,
+0x7B, 0x7F, 0x6F, 0x67, 0x63, 0x63, 0x63, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x1C, 0x36, 0x63, 0x63,
+0x63, 0x63, 0x63, 0x36, 0x1C, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7E, 0x33, 0x33, 0x33, 0x3E, 0x30,
+0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x1C, 0x36, 0x63, 0x63, 0x63, 0x63, 0x6B, 0x3E,
+0x1C, 0x06, 0x03, 0x00, 0x00, 0x00, 0x7E, 0x33,
+0x33, 0x33, 0x3E, 0x36, 0x33, 0x33, 0x73, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x3E, 0x63, 0x63, 0x30,
+0x1C, 0x06, 0x63, 0x63, 0x3E, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7E, 0x5A, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63,
+0x63, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x08, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63,
+0x63, 0x6B, 0x6B, 0x7F, 0x36, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x36,
+0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18,
+0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x63,
+0x06, 0x0C, 0x18, 0x30, 0x60, 0x63, 0x7F, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x3C, 0x30, 0x30, 0x30,
+0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x40, 0x60, 0x30, 0x18, 0x0C, 0x06,
+0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+0x3C, 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, 0xFF,
+0x18, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66,
+0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x30,
+0x30, 0x3E, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E,
+0x63, 0x60, 0x60, 0x63, 0x3E, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x0E, 0x06, 0x06, 0x3E, 0x66, 0x66,
+0x66, 0x66, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x3E, 0x63, 0x7F, 0x60, 0x63,
+0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x33,
+0x30, 0x7C, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B,
+0x66, 0x66, 0x66, 0x3E, 0x06, 0x66, 0x3C, 0x00,
+0x00, 0x00, 0x70, 0x30, 0x30, 0x36, 0x3B, 0x33,
+0x33, 0x33, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x0C, 0x0C, 0x00, 0x1C, 0x0C, 0x0C, 0x0C, 0x0C,
+0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C,
+0x00, 0x1C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC,
+0x78, 0x00, 0x00, 0x00, 0x70, 0x30, 0x30, 0x33,
+0x36, 0x3C, 0x36, 0x33, 0x73, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x1C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x66, 0x7F, 0x6B, 0x6B, 0x6B,
+0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x6E, 0x33, 0x33, 0x33, 0x33, 0x33, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E,
+0x63, 0x63, 0x63, 0x63, 0x3E, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x33, 0x33,
+0x33, 0x3E, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x3B, 0x66, 0x66, 0x66, 0x3E,
+0x06, 0x06, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x6E, 0x33, 0x30, 0x30, 0x30, 0x78, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E,
+0x63, 0x38, 0x0E, 0x63, 0x3E, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x08, 0x18, 0x7E, 0x18, 0x18,
+0x18, 0x1B, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66,
+0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x08, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63,
+0x63, 0x6B, 0x6B, 0x7F, 0x36, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x36, 0x1C,
+0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x63, 0x66, 0x66, 0x66, 0x3E,
+0x06, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x7F, 0x66, 0x0C, 0x18, 0x33, 0x7F, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x0E, 0x18, 0x18, 0x18,
+0x70, 0x18, 0x18, 0x18, 0x0E, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x70, 0x18, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x18,
+0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x6E,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C,
+0x36, 0x63, 0x63, 0x7F, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x7E, 0x3C,
+0x18, 0x00, 0x00, 0x00, 0x55, 0xAA, 0x55, 0xAA,
+0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA,
+0x55, 0xAA, 0x00, 0x44, 0x44, 0x7C, 0x44, 0x44,
+0x00, 0x1F, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00,
+0x00, 0x7C, 0x40, 0x78, 0x40, 0x40, 0x00, 0x1F,
+0x10, 0x1E, 0x10, 0x10, 0x00, 0x00, 0x00, 0x38,
+0x44, 0x40, 0x44, 0x38, 0x00, 0x1E, 0x11, 0x1E,
+0x14, 0x13, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40,
+0x40, 0x7C, 0x00, 0x1F, 0x10, 0x1E, 0x10, 0x10,
+0x00, 0x00, 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18,
+0x18, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x44,
+0x64, 0x54, 0x4C, 0x44, 0x00, 0x10, 0x10, 0x10,
+0x10, 0x1F, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44,
+0x28, 0x10, 0x00, 0x1F, 0x04, 0x04, 0x04, 0x04,
+0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x1F, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0xFF, 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, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x0C, 0x18,
+0x30, 0x60, 0x30, 0x18, 0x0C, 0x00, 0x7E, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0C, 0x06,
+0x0C, 0x18, 0x30, 0x00, 0x7E, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x03, 0x3E, 0x76, 0x36,
+0x36, 0x36, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x02, 0x04, 0x7E, 0x08, 0x10, 0x7E, 0x20,
+0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x36,
+0x30, 0x30, 0x7C, 0x30, 0x30, 0x73, 0x7E, 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,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x18, 0x00, 0x18, 0x18, 0x3C, 0x3C, 0x3C,
+0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+0x08, 0x3E, 0x63, 0x60, 0x60, 0x63, 0x3E, 0x08,
+0x08, 0x00, 0x00, 0x00, 0x1C, 0x36, 0x30, 0x30,
+0x7C, 0x30, 0x30, 0x73, 0x7E, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x42, 0x3C, 0x66, 0x66, 0x66,
+0x3C, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, 0x7E,
+0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
+0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00,
+0x00, 0x00, 0x00, 0x3E, 0x63, 0x30, 0x1C, 0x36,
+0x63, 0x63, 0x36, 0x1C, 0x06, 0x63, 0x3E, 0x00,
+0x00, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x3E, 0x41, 0x5D, 0x51, 0x51, 0x5D, 0x41,
+0x3E, 0x00, 0x00, 0x00, 0x00, 0x38, 0x04, 0x3C,
+0x44, 0x3C, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x36,
+0x6C, 0x36, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x03,
+0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E,
+0x41, 0x5D, 0x55, 0x59, 0x55, 0x41, 0x3E, 0x00,
+0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00,
+0x7E, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6C, 0x18,
+0x30, 0x64, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x7C, 0x0C, 0x38, 0x0C, 0x6C,
+0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x06, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66,
+0x3B, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x7F, 0xDB,
+0xDB, 0xDB, 0x7B, 0x1B, 0x1B, 0x1B, 0x1B, 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,
+0x00, 0x00, 0x0C, 0x0C, 0x06, 0x1C, 0x00, 0x30,
+0x70, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x44,
+0x44, 0x38, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x36,
+0x1B, 0x36, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x60, 0xE0, 0x63, 0x66, 0x6C, 0x18, 0x33,
+0x67, 0xCF, 0x1F, 0x03, 0x03, 0x00, 0x00, 0x60,
+0xE0, 0x63, 0x66, 0x6C, 0x18, 0x30, 0x6E, 0xC3,
+0x06, 0x0C, 0x1F, 0x00, 0x00, 0xF0, 0x30, 0x63,
+0xF6, 0x6C, 0x18, 0x33, 0x67, 0xCF, 0x1F, 0x03,
+0x03, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18,
+0x18, 0x30, 0x63, 0x63, 0x3E, 0x00, 0x00, 0x00,
+0x60, 0x30, 0x18, 0x00, 0x1C, 0x36, 0x63, 0x63,
+0x7F, 0x63, 0x63, 0x00, 0x00, 0x00, 0x03, 0x06,
+0x0C, 0x00, 0x1C, 0x36, 0x63, 0x63, 0x7F, 0x63,
+0x63, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x36, 0x00,
+0x1C, 0x36, 0x63, 0x63, 0x7F, 0x63, 0x63, 0x00,
+0x00, 0x00, 0x3B, 0x6E, 0x00, 0x08, 0x1C, 0x36,
+0x63, 0x63, 0x7F, 0x63, 0x63, 0x00, 0x00, 0x00,
+0x00, 0x63, 0x63, 0x08, 0x1C, 0x36, 0x63, 0x63,
+0x7F, 0x63, 0x63, 0x00, 0x00, 0x00, 0x1C, 0x36,
+0x1C, 0x00, 0x1C, 0x36, 0x63, 0x63, 0x7F, 0x63,
+0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x36,
+0x66, 0x66, 0x67, 0x7E, 0x66, 0x66, 0x67, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x1E, 0x33, 0x60, 0x60,
+0x60, 0x60, 0x60, 0x33, 0x1E, 0x0C, 0x06, 0x1C,
+0x60, 0x30, 0x18, 0x00, 0x7F, 0x33, 0x30, 0x3E,
+0x30, 0x33, 0x7F, 0x00, 0x00, 0x00, 0x06, 0x0C,
+0x18, 0x00, 0x7F, 0x33, 0x30, 0x3E, 0x30, 0x33,
+0x7F, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x36, 0x00,
+0x7F, 0x33, 0x30, 0x3E, 0x30, 0x33, 0x7F, 0x00,
+0x00, 0x00, 0x00, 0x63, 0x63, 0x00, 0x7F, 0x33,
+0x30, 0x3E, 0x30, 0x33, 0x7F, 0x00, 0x00, 0x00,
+0x60, 0x30, 0x18, 0x00, 0x3C, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x06, 0x0C,
+0x18, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x3C, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x66, 0x00,
+0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00,
+0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x3C, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3C, 0x36, 0x33, 0x33, 0x7B, 0x33,
+0x33, 0x36, 0x3C, 0x00, 0x00, 0x00, 0x3B, 0x6E,
+0x00, 0x63, 0x73, 0x7B, 0x7F, 0x6F, 0x67, 0x63,
+0x63, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00,
+0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00,
+0x00, 0x00, 0x03, 0x06, 0x0C, 0x00, 0x1C, 0x36,
+0x63, 0x63, 0x63, 0x36, 0x1C, 0x00, 0x00, 0x00,
+0x08, 0x1C, 0x36, 0x00, 0x1C, 0x36, 0x63, 0x63,
+0x63, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x3B,
+0x6E, 0x00, 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36,
+0x1C, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x00,
+0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x36, 0x1C,
+0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x03, 0x1E, 0x36, 0x67, 0x6F, 0x6B, 0x7B,
+0x73, 0x36, 0x3C, 0x60, 0x00, 0x00, 0x60, 0x30,
+0x18, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+0x3E, 0x00, 0x00, 0x00, 0x03, 0x06, 0x0C, 0x00,
+0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3E, 0x00,
+0x00, 0x00, 0x08, 0x1C, 0x36, 0x00, 0x63, 0x63,
+0x63, 0x63, 0x63, 0x63, 0x3E, 0x00, 0x00, 0x00,
+0x00, 0x63, 0x63, 0x00, 0x63, 0x63, 0x63, 0x63,
+0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x06, 0x0C,
+0x18, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18,
+0x3C, 0x00, 0x00, 0x00, 0x00, 0x78, 0x30, 0x3E,
+0x33, 0x33, 0x3E, 0x30, 0x30, 0x30, 0x78, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x1C, 0x36, 0x63, 0x66,
+0x6C, 0x67, 0x63, 0x6B, 0x6E, 0x60, 0x60, 0x00,
+0x00, 0x60, 0x30, 0x18, 0x00, 0x3C, 0x06, 0x3E,
+0x66, 0x66, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x06,
+0x0C, 0x18, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66,
+0x3B, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x36,
+0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x3B, 0x6E, 0x00, 0x3C,
+0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x66, 0x66, 0x00, 0x3C, 0x06, 0x3E,
+0x66, 0x66, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x1C,
+0x36, 0x1C, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66,
+0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x76, 0x0B, 0x3B, 0x6E, 0x68, 0x37, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E,
+0x63, 0x60, 0x60, 0x63, 0x3E, 0x0C, 0x06, 0x1C,
+0x00, 0x30, 0x18, 0x0C, 0x00, 0x3E, 0x63, 0x7F,
+0x60, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x03,
+0x06, 0x0C, 0x00, 0x3E, 0x63, 0x7F, 0x60, 0x63,
+0x3E, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x36,
+0x00, 0x3E, 0x63, 0x7F, 0x60, 0x63, 0x3E, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x00, 0x3E,
+0x63, 0x7F, 0x60, 0x63, 0x3E, 0x00, 0x00, 0x00,
+0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18,
+0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x06,
+0x0C, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18,
+0x3C, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x66,
+0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x38,
+0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x36, 0x1C, 0x36, 0x06, 0x3E, 0x66,
+0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x3B, 0x6E, 0x00, 0x6E, 0x33, 0x33, 0x33, 0x33,
+0x33, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18,
+0x00, 0x3E, 0x63, 0x63, 0x63, 0x63, 0x3E, 0x00,
+0x00, 0x00, 0x00, 0x06, 0x0C, 0x18, 0x00, 0x3E,
+0x63, 0x63, 0x63, 0x63, 0x3E, 0x00, 0x00, 0x00,
+0x00, 0x08, 0x1C, 0x36, 0x00, 0x3E, 0x63, 0x63,
+0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x3B, 0x6E, 0x00, 0x3E, 0x63, 0x63, 0x63, 0x63,
+0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63,
+0x00, 0x3E, 0x63, 0x63, 0x63, 0x63, 0x3E, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00,
+0x7E, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x03, 0x3E, 0x67, 0x6F,
+0x7B, 0x73, 0x3E, 0x60, 0x00, 0x00, 0x00, 0x60,
+0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66,
+0x3B, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0C, 0x18,
+0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3B, 0x00,
+0x00, 0x00, 0x00, 0x18, 0x3C, 0x66, 0x00, 0x66,
+0x66, 0x66, 0x66, 0x66, 0x3B, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x66, 0x66, 0x00, 0x66, 0x66, 0x66,
+0x66, 0x66, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x06,
+0x0C, 0x18, 0x00, 0x63, 0x66, 0x66, 0x66, 0x3E,
+0x06, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x78,
+0x30, 0x3E, 0x33, 0x33, 0x33, 0x33, 0x3E, 0x30,
+0x78, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x66,
+0x66, 0x66, 0x66, 0x3E, 0x06, 0x66, 0x3C, 0x00
+};
+
+char font_8x16[256*16] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7E, 0x81, 0xA5, 0x81, 0x81, 0xBD,
+0x99, 0x81, 0x81, 0x7E, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7E, 0xFF, 0xDB, 0xFF, 0xFF, 0xC3,
+0xE7, 0xFF, 0xFF, 0x7E, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x6C, 0xFE, 0xFE, 0xFE,
+0xFE, 0x7C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7C, 0xFE,
+0x7C, 0x38, 0x10, 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, 0x1E, 0x0E, 0x1A, 0x32, 0x78, 0xCC,
+0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C,
+0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3F, 0x33, 0x3F, 0x30, 0x30, 0x30,
+0x30, 0x70, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7F, 0x63, 0x7F, 0x63, 0x63, 0x63,
+0x63, 0x67, 0xE7, 0xE6, 0xC0, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x18, 0x18, 0xDB, 0x3C, 0xE7,
+0x3C, 0xDB, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFE, 0xF8,
+0xF0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x02, 0x06, 0x0E, 0x1E, 0x3E, 0xFE, 0x3E,
+0x1E, 0x0E, 0x06, 0x02, 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, 0x7F, 0xDB, 0xDB, 0xDB, 0x7B, 0x1B,
+0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x7C, 0xC6, 0x60, 0x38, 0x6C, 0xC6, 0xC6,
+0x6C, 0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0xFE, 0xFE, 0xFE, 0xFE, 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, 0x0C, 0xFE,
+0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xFE,
+0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0,
+0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xFF,
+0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7C,
+0x7C, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x7C, 0x7C,
+0x38, 0x38, 0x10, 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, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C,
+0x6C, 0xFE, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x18, 0x7C, 0xC6, 0xC2, 0xC0, 0x7C, 0x06,
+0x06, 0x86, 0xC6, 0x7C, 0x18, 0x18, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xC2, 0xC6, 0x0C, 0x18,
+0x30, 0x60, 0xC6, 0x86, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x76, 0xDC,
+0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30,
+0x30, 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x0C,
+0x0C, 0x0C, 0x18, 0x30, 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, 0x30, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE,
+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, 0x02, 0x06, 0x0C, 0x18,
+0x30, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xDB, 0xDB,
+0xC3, 0xC3, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x7E, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7C, 0xC6, 0x06, 0x0C, 0x18, 0x30,
+0x60, 0xC0, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7C, 0xC6, 0x06, 0x06, 0x3C, 0x06,
+0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x0C, 0x1C, 0x3C, 0x6C, 0xCC, 0xFE,
+0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xFE, 0xC0, 0xC0, 0xC0, 0xFC, 0x06,
+0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x60, 0xC0, 0xC0, 0xFC, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xFE, 0xC6, 0x06, 0x06, 0x0C, 0x18,
+0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7E, 0x06,
+0x06, 0x06, 0x0C, 0x78, 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, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60,
+0x30, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00,
+0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0C, 0x06,
+0x0C, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x0C, 0x18, 0x18,
+0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xDE, 0xDE,
+0xDE, 0xDC, 0xC0, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE,
+0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x66,
+0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xC0,
+0xC0, 0xC2, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x66,
+0x66, 0x66, 0x6C, 0xF8, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68,
+0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68,
+0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xDE,
+0xC6, 0xC6, 0x66, 0x3A, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xFE, 0xC6,
+0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xE6, 0x66, 0x66, 0x6C, 0x78, 0x78,
+0x6C, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60,
+0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xC3, 0xE7, 0xFF, 0xFF, 0xDB, 0xC3,
+0xC3, 0xC3, 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xC6, 0xE6, 0xF6, 0xFE, 0xDE, 0xCE,
+0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x60,
+0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
+0xC6, 0xD6, 0xDE, 0x7C, 0x0C, 0x0E, 0x00, 0x00,
+0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x6C,
+0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x60, 0x38, 0x0C,
+0x06, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xFF, 0xDB, 0x99, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
+0xC3, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xDB,
+0xDB, 0xFF, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xC3, 0xC3, 0x66, 0x3C, 0x18, 0x18,
+0x3C, 0x66, 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, 0x18,
+0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xFF, 0xC3, 0x86, 0x0C, 0x18, 0x30,
+0x60, 0xC1, 0xC3, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30,
+0x30, 0x30, 0x30, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0x70, 0x38,
+0x1C, 0x0E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+0x0C, 0x0C, 0x0C, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x10, 0x38, 0x6C, 0xC6, 0x00, 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,
+0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x7C,
+0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xE0, 0x60, 0x60, 0x78, 0x6C, 0x66,
+0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC0,
+0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x1C, 0x0C, 0x0C, 0x3C, 0x6C, 0xCC,
+0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xFE,
+0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60,
+0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC,
+0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xCC, 0x78, 0x00,
+0x00, 0x00, 0xE0, 0x60, 0x60, 0x6C, 0x76, 0x66,
+0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x06, 0x06, 0x00, 0x0E, 0x06, 0x06,
+0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3C, 0x00,
+0x00, 0x00, 0xE0, 0x60, 0x60, 0x66, 0x6C, 0x78,
+0x78, 0x6C, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xFF, 0xDB,
+0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66,
+0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66,
+0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC,
+0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0x0C, 0x1E, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x76, 0x66,
+0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0x60,
+0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x10, 0x30, 0x30, 0xFC, 0x30, 0x30,
+0x30, 0x30, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC,
+0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xC3, 0xC3,
+0xC3, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xC3, 0xC3,
+0xDB, 0xDB, 0xFF, 0x66, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x66, 0x3C,
+0x18, 0x3C, 0x66, 0xC3, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x0C, 0xF8, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xCC, 0x18,
+0x30, 0x60, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x0E, 0x18, 0x18, 0x18, 0x70, 0x18,
+0x18, 0x18, 0x18, 0x0E, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0E, 0x18,
+0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6,
+0xC6, 0xC6, 0xFE, 0x00, 0x00, 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, 0x7E, 0xFF,
+0xFF, 0x7E, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA,
+0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA,
+0x00, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x88, 0x00,
+0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+0x00, 0xF8, 0x80, 0xF0, 0x80, 0x80, 0x80, 0x00,
+0x3E, 0x20, 0x3C, 0x20, 0x20, 0x20, 0x00, 0x00,
+0x00, 0x70, 0x88, 0x80, 0x80, 0x88, 0x70, 0x00,
+0x3C, 0x22, 0x3C, 0x28, 0x24, 0x22, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF8, 0x00,
+0x3E, 0x20, 0x3C, 0x20, 0x20, 0x20, 0x00, 0x00,
+0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18,
+0x18, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x88, 0xC8, 0xA8, 0xA8, 0x98, 0x88, 0x00,
+0x20, 0x20, 0x20, 0x20, 0x20, 0x3E, 0x00, 0x00,
+0x00, 0x88, 0x88, 0x88, 0x88, 0x50, 0x20, 0x00,
+0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+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, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+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, 0x00, 0x02, 0x7E, 0xEC, 0x6C, 0x6C,
+0x6C, 0x6C, 0x6C, 0xCC, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x06, 0x0C, 0x7E, 0x18, 0x18,
+0x7E, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60, 0x60,
+0x60, 0x60, 0xE6, 0xFC, 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,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18,
+0x3C, 0x3C, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x18, 0x18, 0x7E, 0xC3, 0xC0, 0xC0, 0xC0,
+0xC3, 0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60, 0x60,
+0x60, 0x60, 0xE6, 0xFC, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x82, 0xC6, 0x7C, 0xC6, 0xC6,
+0xC6, 0xC6, 0x7C, 0xC6, 0x82, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xC3, 0x66, 0x3C, 0x18, 0xFF, 0x18,
+0xFF, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x7C, 0xC6, 0x60, 0x38, 0x6C, 0xC6, 0xC6,
+0x6C, 0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00,
+0x00, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7C, 0xC6, 0x82, 0xBA, 0xA2, 0xA2,
+0xBA, 0x82, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x70, 0x18, 0x78, 0xC8, 0x78, 0x00, 0xF8,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6C, 0xD8,
+0x6C, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x06,
+0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7C, 0xC6, 0x82, 0xBA, 0xAA, 0xB2,
+0xAA, 0x82, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18,
+0x18, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x70, 0xD8, 0x30, 0x60, 0xC8, 0xF8, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xF8, 0x18, 0x30, 0x18, 0xD8, 0x70, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66,
+0x66, 0x7E, 0x63, 0x60, 0xC0, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7F, 0xDB, 0xDB, 0xDB, 0x7B, 0x1B,
+0x1B, 0x1B, 0x1B, 0x1B, 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,
+0x00, 0x00, 0x18, 0x0C, 0x06, 0x3C, 0x00, 0x00,
+0x00, 0x60, 0xE0, 0x60, 0x60, 0x60, 0xF0, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x70, 0xD8, 0x88, 0xD8, 0x70, 0x00, 0xF8,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x6C, 0x36,
+0x6C, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xC0, 0xC0, 0xC2, 0xC6, 0xCC, 0x18, 0x30,
+0x66, 0xCE, 0x96, 0x3E, 0x06, 0x06, 0x00, 0x00,
+0x00, 0xC0, 0xC0, 0xC2, 0xC6, 0xCC, 0x18, 0x30,
+0x60, 0xCE, 0x9B, 0x06, 0x0C, 0x1F, 0x00, 0x00,
+0x00, 0xF0, 0x30, 0x72, 0x36, 0xEC, 0x18, 0x30,
+0x66, 0xCE, 0x96, 0x3E, 0x06, 0x06, 0x00, 0x00,
+0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60,
+0xC0, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x60, 0x30, 0x18, 0x00, 0x38, 0x6C, 0xC6, 0xC6,
+0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
+0x0C, 0x18, 0x30, 0x00, 0x38, 0x6C, 0xC6, 0xC6,
+0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
+0x10, 0x38, 0x6C, 0x00, 0x38, 0x6C, 0xC6, 0xC6,
+0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x76, 0xDC, 0x00, 0x38, 0x6C, 0xC6, 0xC6,
+0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xC6, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6,
+0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
+0x38, 0x6C, 0x38, 0x00, 0x38, 0x6C, 0xC6, 0xC6,
+0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3E, 0x6C, 0xCC, 0xCC, 0xFE, 0xCC,
+0xCC, 0xCC, 0xCC, 0xCE, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xC0,
+0xC2, 0x66, 0x3C, 0x0C, 0x06, 0x7C, 0x00, 0x00,
+0x30, 0x18, 0x0C, 0x00, 0xFE, 0x66, 0x60, 0x7C,
+0x60, 0x60, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x30, 0x60, 0x00, 0xFE, 0x66, 0x60, 0x7C,
+0x60, 0x60, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00,
+0x10, 0x38, 0x6C, 0x00, 0xFE, 0x66, 0x60, 0x7C,
+0x60, 0x60, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xC6, 0x00, 0xFE, 0x66, 0x60, 0x60, 0x7C,
+0x60, 0x60, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00,
+0x30, 0x18, 0x0C, 0x00, 0x3C, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x0C, 0x18, 0x30, 0x00, 0x3C, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x3C, 0x66, 0x00, 0x3C, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x66, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x78, 0x6C, 0x66, 0x66, 0x66, 0xF6,
+0x66, 0x66, 0x6C, 0x78, 0x00, 0x00, 0x00, 0x00,
+0x76, 0xDC, 0x00, 0xC6, 0xE6, 0xF6, 0xFE, 0xDE,
+0xCE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
+0x60, 0x30, 0x18, 0x00, 0x7C, 0xC6, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x0C, 0x18, 0x30, 0x00, 0x7C, 0xC6, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x10, 0x38, 0x6C, 0x00, 0x7C, 0xC6, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x76, 0xDC, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xC6, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x10,
+0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x02, 0x06, 0x7C, 0xCE, 0xCE, 0xDE, 0xD6, 0xD6,
+0xF6, 0xE6, 0xE6, 0x7C, 0xC0, 0x80, 0x00, 0x00,
+0x60, 0x30, 0x18, 0x00, 0xC6, 0xC6, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x0C, 0x18, 0x30, 0x00, 0xC6, 0xC6, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x10, 0x38, 0x6C, 0x00, 0xC6, 0xC6, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xC6, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x0C, 0x18, 0x30, 0x00, 0xC3, 0xC3, 0x66, 0x3C,
+0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0xF0, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x60,
+0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x6C, 0xC6, 0xCE, 0xDC, 0xD8,
+0xCC, 0xC6, 0xD6, 0xDC, 0xC0, 0x80, 0x00, 0x00,
+0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0C, 0x7C,
+0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0C, 0x7C,
+0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x10, 0x38, 0x6C, 0x00, 0x78, 0x0C, 0x7C,
+0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x76, 0xDC, 0x00, 0x78, 0x0C, 0x7C,
+0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xCC, 0x00, 0x00, 0x78, 0x0C, 0x7C,
+0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x38, 0x6C, 0x38, 0x00, 0x78, 0x0C, 0x7C,
+0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x3B, 0x1B,
+0x7E, 0xD8, 0xDC, 0x77, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x60, 0x60,
+0x66, 0x3C, 0x0C, 0x06, 0x3C, 0x00, 0x00, 0x00,
+0x00, 0x60, 0x30, 0x18, 0x00, 0x7C, 0xC6, 0xFE,
+0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x0C, 0x18, 0x30, 0x00, 0x7C, 0xC6, 0xFE,
+0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x10, 0x38, 0x6C, 0x00, 0x7C, 0xC6, 0xFE,
+0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xC6, 0x00, 0x00, 0x7C, 0xC6, 0xFE,
+0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x06, 0x0C, 0x18, 0x00, 0x38, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x18, 0x3C, 0x66, 0x00, 0x38, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x6C, 0x38, 0x38, 0x6C, 0x0C, 0x3C, 0x6C,
+0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x76, 0xDC, 0x00, 0xDC, 0x66, 0x66,
+0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x60, 0x30, 0x18, 0x00, 0x7C, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x18, 0x30, 0x60, 0x00, 0x7C, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x10, 0x38, 0x6C, 0x00, 0x7C, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x76, 0xDC, 0x00, 0x7C, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xC6, 0x00, 0x00, 0x7C, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7E,
+0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x06, 0x7C, 0xCE, 0xDE,
+0xD6, 0xF6, 0xE6, 0x7C, 0xC0, 0x80, 0x00, 0x00,
+0x00, 0x60, 0x30, 0x18, 0x00, 0xCC, 0xCC, 0xCC,
+0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x18, 0x30, 0x60, 0x00, 0xCC, 0xCC, 0xCC,
+0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x30, 0x78, 0xCC, 0x00, 0xCC, 0xCC, 0xCC,
+0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xCC, 0x00, 0x00, 0xCC, 0xCC, 0xCC,
+0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x18, 0x30, 0x60, 0x00, 0xC6, 0xC6, 0xC6,
+0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x0C, 0xF8, 0x00,
+0x00, 0x00, 0x00, 0xF0, 0x60, 0x7C, 0x66, 0x66,
+0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00,
+0x00, 0x00, 0x66, 0x66, 0x00, 0xEE, 0x66, 0x66,
+0x66, 0x66, 0x66, 0x3E, 0x06, 0x0C, 0x38, 0x00
+};
diff --git a/sys/i386/isa/kbd.h b/sys/i386/isa/kbd.h
new file mode 100644
index 0000000..b7b6009
--- /dev/null
+++ b/sys/i386/isa/kbd.h
@@ -0,0 +1,56 @@
+/*
+ * Keyboard definitions
+ * from: unknown origin, 386BSD 0.1
+ * $Id: kbd.h,v 1.2 1993/10/16 13:46:04 rgrimes Exp $
+ */
+
+#ifndef _I386_ISA_KBD_H_
+#define _I386_ISA_KBD_H_ 1
+
+/* Reference: IBM AT Technical Reference Manual,
+ * pp. 1-38 to 1-43, 4-3 to 4-22
+ */
+
+/* commands sent to KBCMDP */
+
+#define KBC_CMDREAD 0x20 /* read kbd cntrl command byte */
+#define KBC_CMDWRITE 0x60 /* == LD_CMDBYTE in kd.h, write command */
+#define KBC_SELFTEST 0xAA /* perform self test, returns 55 when ok */
+#define KBC_IFTEST 0xAB /* perform interface test */
+#define KBC_DIAGDUMP 0xAC /* send 19 status bytes to system */
+#define KBC_DISKBD 0xAD /* disable keyboard */
+#define KBC_ENAKBD 0xAE /* enable keyboard */
+#define KBC_RDINP 0xC0 /* read input port */
+#define KBC_RDID 0xC4 /* read keyboard ID */
+#define KBC_RDOUTP 0xD0 /* read output port */
+#define KBC_WROUTP 0xD1 /* write output port */
+#define KBC_RDTINP 0xE0 /* read test inputs */
+
+/* commands sent to KBDATAP */
+#define KBC_STSIND 0xED /* set keyboard status indicators */
+#define KBC_ECHO 0xEE /* reply with 0xEE */
+#define KBC_SETTPM 0xF3 /* Set typematic rate/delay */
+#define KBC_ENABLE 0xF4 /* Start scanning */
+#define KBC_SETDEFD 0xF5 /* =KBC_SETDEF, but disable scanning */
+#define KBC_SETDEF 0xF6 /* Set power on defaults */
+#define KBC_RESEND 0xFE /* system wants keyboard to resend last code */
+#define KBC_RESET 0xFF /* Reset the keyboard */
+
+/* responses */
+#define KBR_OVERRUN 0x00 /* Keyboard flooded */
+#define KBR_STOK 0x55 /* Selftest ok response */
+#define KBR_IFOK 0x00 /* Interface test ok */
+#define KBR_IFCL_SA0 0x01 /* Clock Stuck-at-0 fault */
+#define KBR_IFCL_SA1 0x02 /* Clock Stuck-at-1 fault */
+#define KBR_IFDA_SA0 0x03 /* Data Stuck-at-0 fault */
+#define KBR_IFDA_SA1 0x04 /* Data Stuck-at-1 fault */
+#define KBR_RSTDONE 0xAA /* Keyboard reset (BAT) complete */
+#define KBR_E0 0xE0 /* Extended prefix */
+#define KBR_E1 0xE1 /* BREAK'S HIT :-( */
+#define KBR_ECHO 0xEE /* Echo response */
+#define KBR_F0 0xF0 /* Break code prefix */
+#define KBR_ACK 0xFA /* Keyboard did receive command */
+#define KBR_BATFAIL 0xFC /* BAT failed */
+#define KBR_DIAGFAIL 0xFD /* Diagnostic failed response */
+#define KBR_RESEND 0xFE /* Keyboard needs resend of command */
+#endif /* _I386_ISA_KBD_H_ */
diff --git a/sys/i386/isa/kbdtables.h b/sys/i386/isa/kbdtables.h
new file mode 100644
index 0000000..a923c45
--- /dev/null
+++ b/sys/i386/isa/kbdtables.h
@@ -0,0 +1,859 @@
+/*
+ * Copyright (C) 1992, 1993, 1994 Søren Schmidt
+ *
+ * This program is free software; you may redistribute it and/or
+ * modify it, provided that it retain the above copyright notice
+ * and the following disclaimer.
+ *
+ * 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.
+ *
+ * Søren Schmidt Email: sos@login.dkuug.dk
+ * Tritonvej 36 UUCP: ...uunet!dkuug!login!sos
+ * DK9210 Aalborg SO Phone: +45 9814 8076
+ *
+ * @(#)kbdtables.h 1.3 940123
+ * $Id: kbdtables.h,v 1.11 1994/02/01 09:27:43 ache Exp $
+ */
+
+#define SET8 0x80 /* eight bit for emacs SET8-key */
+
+#ifdef DKKEYMAP
+keymap_t key_map = { 0x69, /* DK iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '"', 0x00, 0x00, '@', '"', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', '#', NOP, NOP, 0x9E, '#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', 0xA4, NOP, NOP, '$', 0xA4, NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '&', NOP, NOP, '6', '&', NOP, NOP, 0x33, 0x00,
+/* sc=08 */ '7', '/', NOP, NOP, '{', '/', NOP, NOP, 0x33, 0x00,
+/* sc=09 */ '8', '(', 0x1B, 0x1B, '[', '(', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ')', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=0b */ '0', '=', NOP, NOP, '}', '=', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '+', '?', NOP, NOP, '+', '?', NOP, NOP, 0x33, 0x00,
+/* sc=0d */ '\'', '`', NOP, NOP, '|', '`', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x00, 0x00,
+/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x33, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ 0xE5, 0xC5, NOP, NOP, 0x86, 0x8F, NOP, NOP, 0x33, 0x01,
+/* sc=1b */ '"', '^', 0x1E, 0x1E, '~', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ 0xE6, 0xC6, NOP, NOP, 0x91, 0x92, NOP, NOP, 0x33, 0x01,
+/* sc=28 */ 0xF8, 0xD8, NOP, NOP, 0x9B, 0x9D, NOP, NOP, 0x33, 0x01,
+/* sc=29 */ 0xBD, 0xA7, NOP, NOP, 0xBD, 0xA7, NOP, NOP, 0x33, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\'', '*', NOP, NOP, '\'', '*', NOP, NOP, 0x33, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00,
+/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', '-', '-', '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ NOP, '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02,
+/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ '<', '>', 0x1C, 0x1C, '\\', '>', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x00, 0x02,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x00,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+#endif
+
+#ifdef UKKEYMAP
+keymap_t key_map = { 0x69, /* uk iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, '`', '`', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', 0xA3, NOP, NOP, '#', '#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '^', 0x1E, 0x1E, '^', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=08 */ '7', '&', NOP, NOP, '[', '[', 0x1B, 0x1B, 0x30, 0x00,
+/* sc=09 */ '8', '*', NOP, NOP, '8', '*', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '9', '(', NOP, NOP, ']', ']', 0x1D, 0x1D, 0x30, 0x00,
+/* sc=0b */ '0', ')', NOP, NOP, '{', '{', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '-', '_', 0x1F, 0x1F, '|', '|', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=0d */ '=', '+', NOP, NOP, '}', '}', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=1b */ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ ';', ':', NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x00,
+/* sc=28 */ '\'', '@', 0x00, 0x00, '\'', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=29 */ '\\', '|', 0x1C, 0x1C, '\\', '\\', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '#', '~', NOP, NOP, '~', '~', NOP, NOP, 0x33, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', '<', NOP, NOP, ',', '<', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', '>', NOP, NOP, '.', '>', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '/', '?', NOP, NOP, '/', '?', NOP, NOP, 0x33, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00,
+/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02,
+/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+#endif
+
+#ifdef GRKEYMAP
+keymap_t key_map = { 0x69, /* german iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, '`', '`', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', 0xA7, NOP, NOP, '#', '#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '&', 0x1E, 0x1E, '^', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=08 */ '7', '/', 0x1B, 0x1B, '[', '[', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=09 */ '8', '(', NOP, NOP, '8', '(', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ']', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=0b */ '0', '=', NOP, NOP, '{', '{', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ 0xDF, '?', NOP, NOP, '|', '|', NOP, NOP, 0x33, 0x00,
+/* sc=0d */ 0x92, 0x93, NOP, NOP, '\'', '`', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ 0xFC, 0xDC, 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x01,
+/* sc=1b */ '+', '*', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ 0xF6, 0xD6, NOP, NOP, 0xF6, 0xD6, NOP, NOP, 0x33, 0x01,
+/* sc=28 */ 0xE4, 0xC4, NOP, NOP, 0xE4, 0xC4, NOP, NOP, 0x33, 0x01,
+/* sc=29 */ '<', '>', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '#', '^', 0x1E, 0x1E, '`', '~', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=2c */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00,
+/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02,
+/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+#endif
+
+#ifdef SWKEYMAP
+keymap_t key_map = { 0x69, /* swedish iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=03 */ '2', '"', NOP, NOP, '@', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=04 */ '3', '#', NOP, NOP, 0xA3, NOP, NOP, NOP, 0x37, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, 0xA4, NOP, NOP, NOP, 0x37, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=07 */ '6', '&', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=08 */ '7', '/', NOP, NOP, '{', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=09 */ '8', '(', NOP, NOP, '[', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=0a */ '9', ')', NOP, NOP, ']', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=0b */ '0', '=', NOP, NOP, '}', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=0c */ '+', '?', NOP, NOP, '\\', NOP, 0x1C, NOP, 0x35, 0x00,
+/* sc=0d */ 0x180, '`', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ 0xE5, 0xC5, NOP, NOP, '}', ']', NOP, NOP, 0x33, 0x01,
+/* sc=1b */ 0xA8, '^', NOP, NOP, '~', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ 0xF6, 0xD6, NOP, NOP, '|', '\\', NOP, NOP, 0x33, 0x01,
+/* sc=28 */ 0xE4, 0xC4, NOP, NOP, '{', '[', NOP, NOP, 0x33, 0x01,
+/* sc=29 */ 0xA7, 0xBD, NOP, NOP, '\\', '|', NOP, NOP, 0x33, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\'', '*', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', ';', NOP, NOP, NOP, '<', NOP, NOP, 0x3B, 0x00,
+/* sc=34 */ '.', ':', NOP, NOP, NOP, '>', NOP, NOP, 0x3B, 0x00,
+/* sc=35 */ '-', '_', 0x1F, NOP, '/', '?', NOP, NOP, 0x13, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00,
+/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02,
+/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ '<', '>', NOP, NOP, '|', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+#endif
+
+#ifdef RUKEYMAP
+keymap_t key_map = { 0xe9, /* keys number */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * -------------------------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, NOP, NOP, SET8|0x1B, SET8|0x1B, DBG, NOP, 0x33, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, SET8|'1', SET8|'!', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '@', 0x00, 0x00, SET8|'2', SET8|'@', SET8|0x00, SET8|0x00, 0x00, 0x00,
+/* sc=04 */ '3', '#', NOP, NOP, SET8|'3', SET8|'#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, SET8|'4', SET8|'$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, SET8|'5', SET8|'%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '^', 0x1E, 0x1E, SET8|'6', SET8|'^', SET8|0x1E, SET8|0x1E, 0x00, 0x00,
+/* sc=08 */ '7', '&', NOP, NOP, SET8|'7', SET8|'&', NOP, NOP, 0x33, 0x00,
+/* sc=09 */ '8', '*', NOP, NOP, SET8|'8', SET8|'*', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '9', '(', NOP, NOP, SET8|'9', SET8|'(', NOP, NOP, 0x33, 0x00,
+/* sc=0b */ '0', ')', NOP, NOP, SET8|'0', SET8|')', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '-', '_', 0x1F, 0x1F, SET8|'-', SET8|'_', SET8|0x1F, SET8|0x1F, 0x00, 0x00,
+/* sc=0d */ '=', '+', NOP, NOP, SET8|'=', SET8|'+', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, SET8|0x08, SET8|0x08, SET8|0x7F, SET8|0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, F(16), NOP, NOP, SET8|0x09, F(16), NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, SET8|'q', SET8|'Q', SET8|0x11, SET8|0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, SET8|'w', SET8|'W', SET8|0x17, SET8|0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, SET8|'e', SET8|'E', SET8|0x05, SET8|0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, SET8|'r', SET8|'R', SET8|0x12, SET8|0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, SET8|'t', SET8|'T', SET8|0x14, SET8|0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, SET8|'y', SET8|'Y', SET8|0x19, SET8|0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, SET8|'u', SET8|'U', SET8|0x15, SET8|0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, SET8|'i', SET8|'I', SET8|0x09, SET8|0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, SET8|'o', SET8|'O', SET8|0x0F, SET8|0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, SET8|'p', SET8|'P', SET8|0x10, SET8|0x10, 0x00, 0x01,
+/* sc=1a */ '[', '{', 0x1B, 0x1B, SET8|'[', SET8|'{', SET8|0x1B, SET8|0x1B, 0x00, 0x00,
+/* sc=1b */ ']', '}', 0x1D, 0x1D, SET8|']', SET8|'}', SET8|0x1D, SET8|0x1D, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, SET8|'a', SET8|'A', SET8|0x01, SET8|0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, SET8|'s', SET8|'S', SET8|0x13, SET8|0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, SET8|'d', SET8|'D', SET8|0x04, SET8|0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, SET8|'f', SET8|'F', SET8|0x06, SET8|0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, SET8|'g', SET8|'G', SET8|0x07, SET8|0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, SET8|'h', SET8|'H', SET8|0x08, SET8|0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, SET8|'j', SET8|'J', SET8|0x0A, SET8|0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, SET8|'k', SET8|'K', SET8|0x0B, SET8|0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, SET8|'l', SET8|'L', SET8|0x0C, SET8|0x0C, 0x00, 0x01,
+/* sc=27 */ ';', ':', NOP, NOP, SET8|';', SET8|':', NOP, NOP, 0x33, 0x00,
+/* sc=28 */ '\'', '"', NOP, NOP, SET8|'\'', SET8|'"', NOP, NOP, 0x33, 0x00,
+/* sc=29 */ '`', '~', NOP, NOP, SET8|'`', SET8|'~', NOP, NOP, 0x33, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\\', '|', 0x1C, 0x1C, SET8|'\\', SET8|'|', SET8|0x1C, SET8|0x1C, 0x00, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, SET8|'z', SET8|'Z', SET8|0x1A, SET8|0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, SET8|'x', SET8|'X', SET8|0x18, SET8|0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, SET8|'c', SET8|'C', SET8|0x03, SET8|0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, SET8|'v', SET8|'V', SET8|0x16, SET8|0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, SET8|'b', SET8|'B', SET8|0x02, SET8|0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, SET8|'n', SET8|'N', SET8|0x0E, SET8|0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, SET8|'m', SET8|'M', SET8|0x0D, SET8|0x0D, 0x00, 0x01,
+/* sc=33 */ ',', '<', NOP, NOP, SET8|',', SET8|'<', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', '>', NOP, NOP, SET8|'.', SET8|'>', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '/', '?', NOP, NOP, SET8|'/', SET8|'?', NOP, NOP, 0x33, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, SET8|'*', SET8|'*', SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', SET8|' ', SET8|' ', SET8|' ', SET8|' ', 0x00, 0x00,
+/* sc=3a */ ALK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00,
+/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', SET8|'7', SET8|'7', SET8|'7', SET8|'7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', SET8|'8', SET8|'8', SET8|'8', SET8|'8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', SET8|'9', SET8|'9', SET8|'9', SET8|'9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', '-', '-', SET8|'-', SET8|'-', SET8|'-', SET8|'-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', SET8|'4', SET8|'4', SET8|'4', SET8|'4', 0x80, 0x02,
+/* sc=4c */ F(48), '5', '5', '5', SET8|'5', SET8|'5', SET8|'5', SET8|'5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', '6', '6', SET8|'6', SET8|'6', SET8|'6', SET8|'6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', SET8|'+', SET8|'+', SET8|'+', SET8|'+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', SET8|'1', SET8|'1', SET8|'1', SET8|'1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', SET8|'2', SET8|'2', SET8|'2', SET8|'2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', SET8|'3', SET8|'3', SET8|'3', SET8|'3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', SET8|'0', SET8|'0', SET8|'0', SET8|'0', 0x80, 0x02,
+/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0x82, 0x02,
+/* sc=54 */ ALK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', NOP, NOP, SET8|'/', SET8|'/', NOP, NOP, 0x33, 0x00,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0xC2, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=69 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6b */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6c */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6d */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6e */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6f */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=70 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=71 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=72 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=73 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=74 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=75 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=76 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=77 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=78 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=79 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7b */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7c */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7d */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7e */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7f */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* extended (ALTGR LOCK keys) */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, NOP, NOP, SET8|0x1B, SET8|0x1B, DBG, NOP, 0x33, 0x00,
+/* sc=02 */ '!', '1', NOP, NOP, SET8|'1', SET8|'!', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '"', '2', 0x00, 0x00, SET8|'2', SET8|'@', SET8|0x00, SET8|0x00, 0x00, 0x00,
+/* sc=04 */ '\'', '3', NOP, NOP, SET8|'3', SET8|'#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ ';', '4', NOP, NOP, SET8|'4', SET8|'$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ ':', '5', NOP, NOP, SET8|'5', SET8|'%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ ',', '6', 0x1E, 0x1E, SET8|'6', SET8|'^', SET8|0x1E, SET8|0x1E, 0x00, 0x00,
+/* sc=08 */ '.', '7', NOP, NOP, SET8|'7', SET8|'&', NOP, NOP, 0x33, 0x00,
+/* sc=09 */ '*', '8', NOP, NOP, SET8|'8', SET8|'*', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '(', '9', NOP, NOP, SET8|'9', SET8|'(', NOP, NOP, 0x33, 0x00,
+/* sc=0b */ ')', '0', NOP, NOP, SET8|'0', SET8|')', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '-', '_', 0x1F, 0x1F, SET8|'-', SET8|'_', SET8|0x1F, SET8|0x1F, 0x00, 0x00,
+/* sc=0d */ '=', '+', NOP, NOP, SET8|'=', SET8|'+', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, SET8|0x08, SET8|0x08, SET8|0x7F, SET8|0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, F(16), NOP, NOP, SET8|0x09, F(16), NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 0xca, 0xea, 0x11, 0x11, SET8|'q', SET8|'Q', SET8|0x11, SET8|0x11, 0x00, 0x01,
+/* sc=11 */ 0xc3, 0xe3, 0x17, 0x17, SET8|'w', SET8|'W', SET8|0x17, SET8|0x17, 0x00, 0x01,
+/* sc=12 */ 0xd5, 0xf5, 0x05, 0x05, SET8|'e', SET8|'E', SET8|0x05, SET8|0x05, 0x00, 0x01,
+/* sc=13 */ 0xcb, 0xeb, 0x12, 0x12, SET8|'r', SET8|'R', SET8|0x12, SET8|0x12, 0x00, 0x01,
+/* sc=14 */ 0xc5, 0xe5, 0x14, 0x14, SET8|'t', SET8|'T', SET8|0x14, SET8|0x14, 0x00, 0x01,
+/* sc=15 */ 0xce, 0xee, 0x19, 0x19, SET8|'y', SET8|'Y', SET8|0x19, SET8|0x19, 0x00, 0x01,
+/* sc=16 */ 0xc7, 0xe7, 0x15, 0x15, SET8|'u', SET8|'U', SET8|0x15, SET8|0x15, 0x00, 0x01,
+/* sc=17 */ 0xdb, 0xfb, 0x09, 0x09, SET8|'i', SET8|'I', SET8|0x09, SET8|0x09, 0x00, 0x01,
+/* sc=18 */ 0xdd, 0xfd, 0x0F, 0x0F, SET8|'o', SET8|'O', SET8|0x0F, SET8|0x0F, 0x00, 0x01,
+/* sc=19 */ 0xda, 0xfa, 0x10, 0x10, SET8|'p', SET8|'P', SET8|0x10, SET8|0x10, 0x00, 0x01,
+/* sc=1a */ 0xc8, 0xe8, 0x1B, 0x1B, SET8|'[', SET8|'{', SET8|0x1B, SET8|0x1B, 0x00, 0x01,
+/* sc=1b */ 0xdf, 0xff, 0x1D, 0x1D, SET8|']', SET8|'}', SET8|0x1D, SET8|0x1D, 0x00, 0x01,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 0xc6, 0xe6, 0x01, 0x01, SET8|'a', SET8|'A', SET8|0x01, SET8|0x01, 0x00, 0x01,
+/* sc=1f */ 0xd9, 0xf9, 0x13, 0x13, SET8|'s', SET8|'S', SET8|0x13, SET8|0x13, 0x00, 0x01,
+/* sc=20 */ 0xd7, 0xf7, 0x04, 0x04, SET8|'d', SET8|'D', SET8|0x04, SET8|0x04, 0x00, 0x01,
+/* sc=21 */ 0xc1, 0xe1, 0x06, 0x06, SET8|'f', SET8|'F', SET8|0x06, SET8|0x06, 0x00, 0x01,
+/* sc=22 */ 0xd0, 0xf0, 0x07, 0x07, SET8|'g', SET8|'G', SET8|0x07, SET8|0x07, 0x00, 0x01,
+/* sc=23 */ 0xd2, 0xf2, 0x08, 0x08, SET8|'h', SET8|'H', SET8|0x08, SET8|0x08, 0x00, 0x01,
+/* sc=24 */ 0xcf, 0xef, 0x0A, 0x0A, SET8|'j', SET8|'J', SET8|0x0A, SET8|0x0A, 0x00, 0x01,
+/* sc=25 */ 0xcc, 0xec, 0x0B, 0x0B, SET8|'k', SET8|'K', SET8|0x0B, SET8|0x0B, 0x00, 0x01,
+/* sc=26 */ 0xc4, 0xe4, 0x0C, 0x0C, SET8|'l', SET8|'L', SET8|0x0C, SET8|0x0C, 0x00, 0x01,
+/* sc=27 */ 0xd6, 0xf6, NOP, NOP, SET8|';', SET8|':', NOP, NOP, 0x33, 0x01,
+/* sc=28 */ 0xdc, 0xfc, NOP, NOP, SET8|'\'', SET8|'"', NOP, NOP, 0x33, 0x01,
+/* sc=29 */ 0xa3, 0xb3, NOP, NOP, SET8|'`', SET8|'~', NOP, NOP, 0x33, 0x01,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\\', '|', 0x1C, 0x1C, SET8|'\\', SET8|'|', SET8|0x1C, SET8|0x1C, 0x00, 0x00,
+/* sc=2c */ 0xd1, 0xf1, 0x1A, 0x1A, SET8|'z', SET8|'Z', SET8|0x1A, SET8|0x1A, 0x00, 0x01,
+/* sc=2d */ 0xde, 0xfe, 0x18, 0x18, SET8|'x', SET8|'X', SET8|0x18, SET8|0x18, 0x00, 0x01,
+/* sc=2e */ 0xd3, 0xf3, 0x03, 0x03, SET8|'c', SET8|'C', SET8|0x03, SET8|0x03, 0x00, 0x01,
+/* sc=2f */ 0xcd, 0xed, 0x16, 0x16, SET8|'v', SET8|'V', SET8|0x16, SET8|0x16, 0x00, 0x01,
+/* sc=30 */ 0xc9, 0xe9, 0x02, 0x02, SET8|'b', SET8|'B', SET8|0x02, SET8|0x02, 0x00, 0x01,
+/* sc=31 */ 0xd4, 0xf4, 0x0E, 0x0E, SET8|'n', SET8|'N', SET8|0x0E, SET8|0x0E, 0x00, 0x01,
+/* sc=32 */ 0xd8, 0xf8, 0x0D, 0x0D, SET8|'m', SET8|'M', SET8|0x0D, SET8|0x0D, 0x00, 0x01,
+/* sc=33 */ 0xc2, 0xe2, NOP, NOP, SET8|',', SET8|'<', NOP, NOP, 0x33, 0x01,
+/* sc=34 */ 0xc0, 0xe0, NOP, NOP, SET8|'.', SET8|'>', NOP, NOP, 0x33, 0x01,
+/* sc=35 */ '/', '?', NOP, NOP, SET8|'/', SET8|'?', NOP, NOP, 0x33, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, SET8|'*', SET8|'*', SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', SET8|' ', SET8|' ', SET8|' ', SET8|' ', 0x00, 0x00,
+/* sc=3a */ ALK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00,
+/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', SET8|'7', SET8|'7', SET8|'7', SET8|'7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', SET8|'8', SET8|'8', SET8|'8', SET8|'8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', SET8|'9', SET8|'9', SET8|'9', SET8|'9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', '-', '-', SET8|'-', SET8|'-', SET8|'-', SET8|'-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', SET8|'4', SET8|'4', SET8|'4', SET8|'4', 0x80, 0x02,
+/* sc=4c */ F(48), '5', '5', '5', SET8|'5', SET8|'5', SET8|'5', SET8|'5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', '6', '6', SET8|'6', SET8|'6', SET8|'6', SET8|'6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', SET8|'+', SET8|'+', SET8|'+', SET8|'+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', SET8|'1', SET8|'1', SET8|'1', SET8|'1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', SET8|'2', SET8|'2', SET8|'2', SET8|'2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', SET8|'3', SET8|'3', SET8|'3', SET8|'3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', SET8|'0', SET8|'0', SET8|'0', SET8|'0', 0x80, 0x02,
+/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0x82, 0x02,
+/* sc=54 */ ALK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', NOP, NOP, SET8|'/', SET8|'/', NOP, NOP, 0x33, 0x00,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0xC2, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+
+#endif
+
+#if !defined(DKKEYMAP) && !defined(UKKEYMAP) && !defined(GRKEYMAP) && !defined(SWKEYMAP) && !defined(RUKEYMAP)
+keymap_t key_map = { 0x69, /* US iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '@', 0x00, 0x00, '2', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', '#', NOP, NOP, '3', '#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '^', 0x1E, 0x1E, '6', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=08 */ '7', '&', NOP, NOP, '7', '&', NOP, NOP, 0x33, 0x00,
+/* sc=09 */ '8', '*', NOP, NOP, '8', '*', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '9', '(', NOP, NOP, '9', '(', NOP, NOP, 0x33, 0x00,
+/* sc=0b */ '0', ')', NOP, NOP, '0', ')', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=0d */ '=', '+', NOP, NOP, '=', '+', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x33, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=1b */ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ ';', ':', NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x00,
+/* sc=28 */ '\'', '"', NOP, NOP, '\'', '"', NOP, NOP, 0x33, 0x00,
+/* sc=29 */ '`', '~', NOP, NOP, '`', '~', NOP, NOP, 0x33, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', '<', NOP, NOP, ',', '<', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', '>', NOP, NOP, '.', '>', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '/', '?', NOP, NOP, '/', '?', NOP, NOP, 0x33, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00,
+/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', '-', '-', '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ NOP, '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02,
+/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x00, 0x00,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x00,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+
+#endif
+
+fkeytab_t fkey_tab[60] = {
+/* 00-03 */ {"\033[M", 3}, {"\033[N", 3}, {"\033[O", 3}, {"\033[P", 3},
+/* 04-07 */ {"\033[Q", 3}, {"\033[R", 3}, {"\033[S", 3}, {"\033[T", 3},
+/* 08-0B */ {"\033[U", 3}, {"\033[V", 3}, {"\033[W", 3}, {"\033[X", 3},
+/* 0C-0F */ {"\033[W", 3}, {"\033[X", 3}, {"\033[Y", 3}, {"\033[Z", 3},
+/* 10-13 */ {"\033[a", 3}, {"\033[b", 3}, {"\033[c", 3}, {"\033[d", 3},
+/* 14-17 */ {"\033[e", 3}, {"\033[f", 3}, {"\033[g", 3}, {"\033[h", 3},
+/* 18-1B */ {"\033[g", 3}, {"\033[h", 3}, {"\033[i", 3}, {"\033[j", 3},
+/* 1C-1F */ {"\033[k", 3}, {"\033[l", 3}, {"\033[m", 3}, {"\033[n", 3},
+/* 20-23 */ {"\033[o", 3}, {"\033[p", 3}, {"\033[q", 3}, {"\033[r", 3},
+/* 24-27 */ {"\033[g", 3}, {"\033[h", 3}, {"\033[i", 3}, {"\033[j", 3},
+/* 28-2B */ {"\033[k", 3}, {"\033[l", 3}, {"\033[m", 3}, {"\033[n", 3},
+/* 2C-2F */ {"\033[o", 3}, {"\033[p", 3}, {"\033[q", 3}, {"\033[r", 3},
+/* 30-33 */ {"\033[H", 3}, {"\033[A", 3}, {"\033[I", 3}, {"-" , 1},
+/* 34-37 */ {"\033[D", 3}, {"\177" , 1}, {"\033[C", 3}, {"+" , 1},
+/* 38-3B */ {"\033[F", 3}, {"\033[B", 3}, {"\033[G", 3}, {"\033[L", 3}
+};
diff --git a/sys/i386/isa/lpt.c b/sys/i386/isa/lpt.c
new file mode 100644
index 0000000..88f492e
--- /dev/null
+++ b/sys/i386/isa/lpt.c
@@ -0,0 +1,653 @@
+/*
+ * Copyright (c) 1990 William F. Jolitz, TeleMuse
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This software is a component of "386BSD" developed by
+ * William F. Jolitz, TeleMuse.
+ * 4. Neither the name of the developer nor the name "386BSD"
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
+ * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
+ * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
+ * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
+ * NOT MAKE USE OF THIS WORK.
+ *
+ * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
+ * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN
+ * REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES
+ * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING
+ * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND
+ * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE
+ * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS
+ * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: unknown origin, 386BSD 0.1
+ * $Id: lpt.c,v 1.10 1994/04/06 16:42:33 csgr Exp $
+ */
+
+/*
+ * Device Driver for AT parallel printer port
+ * Written by William Jolitz 12/18/90
+ */
+
+#include "lpt.h"
+#if NLPT > 0
+
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "user.h"
+#include "buf.h"
+#include "kernel.h"
+#include "ioctl.h"
+#include "tty.h"
+#include "uio.h"
+#include "syslog.h"
+
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/lptreg.h"
+
+#include "i386/include/lpt.h"
+
+#define LPINITRDY 4 /* wait up to 4 seconds for a ready */
+#define LPTOUTTIME 4 /* wait up to 4 seconds for a ready */
+#define LPPRI (PZERO+8)
+#define BUFSIZE 1024
+
+
+/* BIOS printer list - used by BIOS probe*/
+#define BIOS_LPT_PORTS 0x408
+#define BIOS_PORTS (short *)(KERNBASE+BIOS_LPT_PORTS)
+#define BIOS_MAX_LPT 4
+
+
+#ifndef DEBUG
+#define lprintf (void)
+#else
+#define lprintf if (lptflag) printf
+int lptflag = 1;
+#endif
+
+#define LPTUNIT(s) ((s)&0x03)
+#define LPTFLAGS(s) ((s)&0xfc)
+
+struct lpt_softc {
+ short sc_port;
+ short sc_state;
+ /* default case: negative prime, negative ack, handshake strobe,
+ prime once */
+ u_char sc_control;
+ char sc_flags;
+#define LP_POS_INIT 0x04 /* if we are a postive init signal */
+#define LP_POS_ACK 0x08 /* if we are a positive going ack */
+#define LP_NO_PRIME 0x10 /* don't prime the printer at all */
+#define LP_PRIMEOPEN 0x20 /* prime on every open */
+#define LP_AUTOLF 0x40 /* tell printer to do an automatic lf */
+#define LP_BYPASS 0x80 /* bypass printer ready checks */
+ struct buf *sc_inbuf;
+ short sc_xfercnt ;
+ char sc_primed;
+ char *sc_cp ;
+ u_char sc_irq ; /* IRQ status of port */
+#define LP_HAS_IRQ 0x01 /* we have an irq available */
+#define LP_USE_IRQ 0x02 /* we are using our irq */
+#define LP_ENABLE_IRQ 0x04 /* enable IRQ on open */
+
+} lpt_sc[NLPT] ;
+
+/* bits for state */
+#define OPEN (1<<0) /* device is open */
+#define ASLP (1<<1) /* awaiting draining of printer */
+#define ERROR (1<<2) /* error was received from printer */
+#define OBUSY (1<<3) /* printer is busy doing output */
+#define LPTOUT (1<<4) /* timeout while not selected */
+#define TOUT (1<<5) /* timeout while not selected */
+#define INIT (1<<6) /* waiting to initialize for open */
+#define INTERRUPTED (1<<7) /* write call was interrupted */
+
+
+/* status masks to interrogate printer status */
+#define RDY_MASK (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR) /* ready ? */
+#define LP_READY (LPS_SEL|LPS_NBSY|LPS_NERR)
+
+/* Printer Ready condition - from lpa.c */
+/* Only used in polling code */
+#define LPS_INVERT (LPS_NBSY | LPS_NACK | LPS_SEL | LPS_NERR)
+#define LPS_MASK (LPS_NBSY | LPS_NACK | LPS_OUT | LPS_SEL | LPS_NERR)
+#define NOT_READY(x) ((inb(x)^LPS_INVERT)&LPS_MASK)
+
+#define MAX_SLEEP (hz*5) /* Timeout while waiting for device ready */
+#define MAX_SPIN 20 /* Max delay for device ready in usecs */
+
+
+static void lptout (struct lpt_softc * sc);
+int lptprobe (struct isa_device *dvp);
+int lptattach (struct isa_device *isdp);
+void lptintr (int unit);
+
+struct isa_driver lptdriver = {
+ lptprobe, lptattach, "lpt"
+};
+
+
+
+/*
+ * Internal routine to lptprobe to do port tests of one byte value
+ */
+static int
+lpt_port_test(short port, u_char data, u_char mask)
+{
+ int temp, timeout;
+
+ data = data & mask;
+ outb(port, data);
+ timeout = 10000;
+ do {
+ DELAY(10);
+ temp = inb(port) & mask;
+ }
+ while (temp != data && --timeout);
+ lprintf("Port 0x%x\tout=%x\tin=%x\ttout=%d\n",
+ port, data, temp, timeout);
+ return (temp == data);
+}
+
+/*
+ * New lpt port probe Geoff Rehmet - Rhodes University - 14/2/94
+ * Based partially on Rod Grimes' printer probe
+ *
+ * Logic:
+ * 1) If no port address was given, use the bios detected ports
+ * and autodetect what ports the printers are on.
+ * 2) Otherwise, probe the data port at the address given,
+ * using the method in Rod Grimes' port probe.
+ * (Much code ripped off directly from Rod's probe.)
+ *
+ * Comments from Rod's probe:
+ * Logic:
+ * 1) You should be able to write to and read back the same value
+ * to the data port. Do an alternating zeros, alternating ones,
+ * walking zero, and walking one test to check for stuck bits.
+ *
+ * 2) You should be able to write to and read back the same value
+ * to the control port lower 5 bits, the upper 3 bits are reserved
+ * per the IBM PC technical reference manauls and different boards
+ * do different things with them. Do an alternating zeros, alternating
+ * ones, walking zero, and walking one test to check for stuck bits.
+ *
+ * Some printers drag the strobe line down when the are powered off
+ * so this bit has been masked out of the control port test.
+ *
+ * XXX Some printers may not like a fast pulse on init or strobe, I
+ * don't know at this point, if that becomes a problem these bits
+ * should be turned off in the mask byte for the control port test.
+ *
+ * We are finally left with a mask of 0x14, due to some printers
+ * being adamant about holding other bits high ........
+ *
+ * Before probing the control port, we write a 0 to the data port -
+ * If not, some printers chuck out garbage when the strobe line
+ * gets toggled.
+ *
+ * 3) Set the data and control ports to a value of 0
+ *
+ * This probe routine has been tested on Epson Lx-800, HP LJ3P,
+ * Epson FX-1170 and C.Itoh 8510RM
+ * printers.
+ * Quick exit on fail added.
+ */
+int
+lptprobe(struct isa_device *dvp)
+{
+ short port;
+ static short next_bios_lpt = 0;
+ int status;
+ u_char data;
+ u_char mask;
+ int i;
+
+ /*
+ * Make sure there is some way for lptopen to see that
+ * the port is not configured
+ * This 0 will remain if the port isn't attached
+ */
+ (lpt_sc + dvp->id_unit)->sc_port = 0;
+
+ status = IO_LPTSIZE;
+ /* If port not specified, use bios list */
+ if(dvp->id_iobase < 0) { /* port? */
+ if((next_bios_lpt < BIOS_MAX_LPT) &&
+ (*(BIOS_PORTS+next_bios_lpt) != 0) ) {
+ dvp->id_iobase = *(BIOS_PORTS+next_bios_lpt++);
+ goto end_probe;
+ } else
+ return (0);
+ }
+
+ /* Port was explicitly specified */
+ /* This allows probing of ports unknown to the BIOS */
+
+ port = dvp->id_iobase + lpt_data;
+ mask = 0xff;
+ data = 0x55; /* Alternating zeros */
+ if (!lpt_port_test(port, data, mask))
+ { status = 0 ; goto end_probe ; }
+
+ data = 0xaa; /* Alternating ones */
+ if (!lpt_port_test(port, data, mask))
+ { status = 0 ; goto end_probe ; }
+
+ for (i = 0; i < 8; i++) { /* Walking zero */
+ data = ~(1 << i);
+ if (!lpt_port_test(port, data, mask))
+ { status = 0 ; goto end_probe ; }
+ }
+
+ for (i = 0; i < 8; i++) { /* Walking one */
+ data = (1 << i);
+ if (!lpt_port_test(port, data, mask))
+ { status = 0 ; goto end_probe ; }
+ }
+
+end_probe:
+ /* write 0's to control and data ports */
+ outb(dvp->id_iobase+lpt_data, 0);
+ outb(dvp->id_iobase+lpt_control, 0);
+
+ return (status);
+}
+
+/* XXX Todo - try and detect if interrupt is working */
+int
+lptattach(struct isa_device *isdp)
+{
+ struct lpt_softc *sc;
+
+ sc = lpt_sc + isdp->id_unit;
+ sc->sc_port = isdp->id_iobase;
+ sc->sc_primed = 0; /* not primed yet */
+ outb(sc->sc_port+lpt_control, LPC_NINIT);
+
+ /* check if we can use interrupt */
+ lprintf("oldirq %x\n", sc->sc_irq);
+ if(isdp->id_irq) {
+ sc->sc_irq = LP_HAS_IRQ | LP_USE_IRQ | LP_ENABLE_IRQ;
+ printf("lpt%d: Interrupt-driven port\n", isdp->id_unit);
+ } else {
+ sc->sc_irq = 0;
+ lprintf("lpt%d: Polled port\n", isdp->id_unit);
+ }
+ lprintf("irq %x\n", sc->sc_irq);
+
+ return (1);
+}
+
+/*
+ * lptopen -- reset the printer, then wait until it's selected and not busy.
+ */
+
+int
+lptopen(dev_t dev, int flag)
+{
+ struct lpt_softc *sc;
+ int s;
+ int trys, port;
+ u_int unit = LPTUNIT(minor(dev));
+
+ sc = lpt_sc + unit;
+ if ((unit >= NLPT) || (sc->sc_port == 0))
+ return (ENXIO);
+
+ if (sc->sc_state) {
+ lprintf("lp: still open\n") ;
+ lprintf("still open %x\n", sc->sc_state);
+ return(EBUSY);
+ } else sc->sc_state |= INIT;
+
+ s = spltty();
+ sc->sc_flags = LPTFLAGS(minor(dev));
+ lprintf("lp flags 0x%x\n", sc->sc_flags);
+ port = sc->sc_port;
+
+ /* set IRQ status according to ENABLE_IRQ flag */
+ if(sc->sc_irq & LP_ENABLE_IRQ)
+ sc->sc_irq |= LP_USE_IRQ;
+ else
+ sc->sc_irq &= ~LP_USE_IRQ;
+
+
+ /* init printer */
+ if((sc->sc_flags & LP_NO_PRIME) == 0) {
+ if((sc->sc_flags & LP_PRIMEOPEN) || sc->sc_primed == 0) {
+ outb(port+lpt_control, 0);
+ sc->sc_primed++;
+ DELAY(500);
+ }
+ }
+ outb(port+lpt_control, LPC_SEL|LPC_NINIT);
+
+ /* wait till ready (printer running diagnostics) */
+ trys = 0;
+ do {
+ /* ran out of waiting for the printer */
+ if (trys++ >= LPINITRDY*4) {
+ splx(s);
+ sc->sc_state = 0;
+ lprintf ("status %x\n", inb(port+lpt_status) );
+ return (EBUSY);
+ }
+
+ /* wait 1/4 second, give up if we get a signal */
+ if (tsleep ((caddr_t)sc, LPPRI|PCATCH, "lptinit",
+ hz/4) != EWOULDBLOCK) {
+ sc->sc_state = 0;
+ splx(s);
+ return (EBUSY);
+ }
+
+ /* is printer online and ready for output */
+ } while ((inb(port+lpt_status) & (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR)) !=
+ (LPS_SEL|LPS_NBSY|LPS_NERR));
+
+ sc->sc_control = LPC_SEL|LPC_NINIT;
+ if(sc->sc_flags&LP_AUTOLF)
+ sc->sc_control |= LPC_AUTOL;
+ /* enable interrupt if interrupt-driven */
+ if(sc->sc_irq & LP_USE_IRQ)
+ sc->sc_control |= LPC_ENA;
+
+ outb(port+lpt_control, sc->sc_control);
+
+ sc->sc_state = OPEN;
+ sc->sc_inbuf = geteblk(BUFSIZE);
+ sc->sc_xfercnt = 0;
+ splx(s);
+
+ /* only use timeout if using interrupt */
+ lprintf("irq %x\n", sc->sc_irq);
+ if(sc->sc_irq & LP_USE_IRQ) {
+ sc->sc_state |= TOUT;
+ timeout ((timeout_func_t)lptout, (caddr_t)sc, hz/2);
+ }
+ lprintf("opened.\n");
+ return(0);
+}
+
+static void
+lptout (struct lpt_softc * sc)
+{ int pl;
+
+ lprintf ("T %x ", inb(sc->sc_port+lpt_status));
+ if (sc->sc_state&OPEN)
+ timeout ((timeout_func_t)lptout, (caddr_t)sc, hz/2);
+ else sc->sc_state &= ~TOUT;
+
+ if (sc->sc_state & ERROR)
+ sc->sc_state &= ~ERROR;
+
+ /*
+ * Avoid possible hangs do to missed interrupts
+ */
+ if (sc->sc_xfercnt) {
+ pl = spltty();
+ lptintr(sc - lpt_sc);
+ splx(pl);
+ } else {
+ sc->sc_state &= ~OBUSY;
+ wakeup((caddr_t)sc);
+ }
+}
+
+/*
+ * lptclose -- close the device, free the local line buffer.
+ *
+ * Check for interrupted write call added.
+ */
+
+int
+lptclose(dev_t dev, int flag)
+{
+ struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev));
+ int port = sc->sc_port;
+
+ sc->sc_state &= ~OPEN;
+
+ /* if the last write was interrupted, don't complete it */
+ if((!(sc->sc_state & INTERRUPTED)) && (sc->sc_irq & LP_USE_IRQ))
+ while ((inb(port+lpt_status) & (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR)) !=
+ (LPS_SEL|LPS_NBSY|LPS_NERR) || sc->sc_xfercnt)
+ /* wait 1/4 second, give up if we get a signal */
+ if (tsleep ((caddr_t)sc, LPPRI|PCATCH,
+ "lpclose", hz) != EWOULDBLOCK)
+ break;
+
+ sc->sc_state = 0;
+ sc->sc_xfercnt = 0;
+ outb(sc->sc_port+lpt_control, LPC_NINIT);
+ brelse(sc->sc_inbuf);
+ lprintf("closed.\n");
+ return(0);
+}
+
+/*
+ * pushbytes()
+ * Workhorse for actually spinning and writing bytes to printer
+ * Derived from lpa.c
+ * Originally by ?
+ *
+ * This code is only used when we are polling the port
+ */
+static int
+pushbytes(struct lpt_softc * sc)
+{
+ int spin, err, tic;
+ char ch;
+ int port = sc->sc_port;
+
+ lprintf("p");
+ /* loop for every character .. */
+ while (sc->sc_xfercnt > 0) {
+ /* printer data */
+ ch = *(sc->sc_cp);
+ sc->sc_cp++;
+ sc->sc_xfercnt--;
+
+ /*
+ * Wait for printer ready.
+ * Loop 20 usecs testing BUSY bit, then sleep
+ * for exponentially increasing timeout. (vak)
+ */
+ for (spin=0; NOT_READY(port+lpt_status) && spin<MAX_SPIN; ++spin)
+ DELAY(1); /* XXX delay is NOT this accurate! */
+ if (spin >= MAX_SPIN) {
+ tic = 0;
+ while (NOT_READY(port+lpt_status)) {
+ /*
+ * Now sleep, every cycle a
+ * little longer ..
+ */
+ tic = tic + tic + 1;
+ /*
+ * But no more than 10 seconds. (vak)
+ */
+ if (tic > MAX_SLEEP)
+ tic = MAX_SLEEP;
+ err = tsleep((caddr_t)sc, LPPRI,
+ "lptpoll", tic);
+ if (err != EWOULDBLOCK) {
+ return (err);
+ }
+ }
+ }
+
+ /* output data */
+ outb(port+lpt_data, ch);
+ /* strobe */
+ outb(port+lpt_control, sc->sc_control|LPC_STB);
+ outb(port+lpt_control, sc->sc_control);
+
+ }
+ return(0);
+}
+
+/*
+ * lptwrite --copy a line from user space to a local buffer, then call
+ * putc to get the chars moved to the output queue.
+ *
+ * Flagging of interrupted write added.
+ */
+
+int
+lptwrite(dev_t dev, struct uio * uio)
+{
+ register unsigned n;
+ int pl, err;
+ struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev));
+
+ sc->sc_state &= ~INTERRUPTED;
+ while (n = min(BUFSIZE, uio->uio_resid)) {
+ sc->sc_cp = sc->sc_inbuf->b_un.b_addr ;
+ uiomove(sc->sc_cp, n, uio);
+ sc->sc_xfercnt = n ;
+ while ((sc->sc_xfercnt > 0)&&(sc->sc_irq & LP_USE_IRQ)) {
+ lprintf("i");
+ /* if the printer is ready for a char, */
+ /* give it one */
+ if ((sc->sc_state & OBUSY) == 0){
+ lprintf("\nC %d. ", sc->sc_xfercnt);
+ pl = spltty();
+ lptintr(sc - lpt_sc);
+ (void) splx(pl);
+ }
+ lprintf("W ");
+ if (sc->sc_state & OBUSY)
+ if (err = tsleep ((caddr_t)sc,
+ LPPRI|PCATCH, "lpwrite", 0)) {
+ sc->sc_state |= INTERRUPTED;
+ return(err);
+ }
+ }
+ /* check to see if we must do a polled write */
+ if(!(sc->sc_irq & LP_USE_IRQ) && (sc->sc_xfercnt)) {
+ lprintf("p");
+ if((err = pushbytes(sc)))
+ return(err);
+ }
+ }
+ return(0);
+}
+
+/*
+ * lptintr -- handle printer interrupts which occur when the printer is
+ * ready to accept another char.
+ *
+ * do checking for interrupted write call.
+ */
+
+void
+lptintr(int unit)
+{
+ struct lpt_softc *sc = lpt_sc + unit;
+ int port = sc->sc_port, sts;
+
+ /* is printer online and ready for output */
+ if (((sts=inb(port+lpt_status)) & RDY_MASK) == LP_READY) {
+ sc->sc_state = (sc->sc_state | OBUSY) & ~ERROR;
+
+ if (sc->sc_xfercnt) {
+ /* send char */
+ /*lprintf("%x ", *sc->sc_cp); */
+ outb(port+lpt_data, *sc->sc_cp++) ;
+ outb(port+lpt_control, sc->sc_control|LPC_STB);
+ /* DELAY(X) */
+ outb(port+lpt_control, sc->sc_control);
+
+ /* any more data for printer */
+ if(--(sc->sc_xfercnt) > 0) return;
+ }
+
+ /*
+ * No more data waiting for printer.
+ * Wakeup is not done if write call was interrupted.
+ */
+ sc->sc_state &= ~OBUSY;
+ if(!(sc->sc_state & INTERRUPTED))
+ wakeup((caddr_t)sc);
+ lprintf("w ");
+ return;
+ } else { /* check for error */
+ if(((sts & (LPS_NERR | LPS_OUT) ) != LPS_NERR) &&
+ (sc->sc_state & OPEN))
+ sc->sc_state |= ERROR;
+ }
+ lprintf("sts %x ", sts);
+}
+
+int
+lptioctl(dev_t dev, int cmd, caddr_t data, int flag)
+{
+ int error = 0;
+ struct lpt_softc *sc;
+ u_int unit = LPTUNIT(minor(dev));
+ u_char old_sc_irq; /* old printer IRQ status */
+
+ sc = lpt_sc + unit;
+
+ switch (cmd) {
+ case LPT_IRQ :
+ if(sc->sc_irq & LP_HAS_IRQ) {
+ /*
+ * NOTE:
+ * If the IRQ status is changed,
+ * this will only be visible on the
+ * next open.
+ *
+ * If interrupt status changes,
+ * this gets syslog'd.
+ */
+ old_sc_irq = sc->sc_irq;
+ if(*(int*)data == 0)
+ sc->sc_irq &= (~LP_ENABLE_IRQ);
+ else
+ sc->sc_irq |= LP_ENABLE_IRQ;
+ if (old_sc_irq != sc->sc_irq )
+ log(LOG_NOTICE, "lpt%c switched to %s mode\n",
+ (char)unit+'0',
+ (sc->sc_irq & LP_ENABLE_IRQ)?
+ "interrupt-driven":"polled");
+ } else /* polled port */
+ error = EOPNOTSUPP;
+ break;
+ default:
+ error = ENODEV;
+ }
+
+ return(error);
+}
+
+#endif /* NLPT */
diff --git a/sys/i386/isa/lptreg.h b/sys/i386/isa/lptreg.h
new file mode 100644
index 0000000..9e10ba9
--- /dev/null
+++ b/sys/i386/isa/lptreg.h
@@ -0,0 +1,33 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * form: @(#)lptreg.h 1.1 (Berkeley) 12/19/90
+ * $Id$
+ */
+
+/*
+ * AT Parallel Port (for lineprinter)
+ * Interface port and bit definitions
+ * Written by William Jolitz 12/18/90
+ * Copyright (C) William Jolitz 1990
+ */
+
+#define lpt_data 0 /* Data to/from printer (R/W) */
+
+#define lpt_status 1 /* Status of printer (R) */
+#define LPS_NERR 0x08 /* printer no error */
+#define LPS_SEL 0x10 /* printer selected */
+#define LPS_OUT 0x20 /* printer out of paper */
+#define LPS_NACK 0x40 /* printer no ack of data */
+#define LPS_NBSY 0x80 /* printer no ack of data */
+
+#define lpt_control 2 /* Control printer (R/W) */
+#define LPC_STB 0x01 /* strobe data to printer */
+#define LPC_AUTOL 0x02 /* automatic linefeed */
+#define LPC_NINIT 0x04 /* initialize printer */
+#define LPC_SEL 0x08 /* printer selected */
+#define LPC_ENA 0x10 /* printer out of paper */
diff --git a/sys/i386/isa/mcd.c b/sys/i386/isa/mcd.c
new file mode 100644
index 0000000..683b0e1
--- /dev/null
+++ b/sys/i386/isa/mcd.c
@@ -0,0 +1,1335 @@
+/*
+ * Copyright 1993 by Holger Veit (data part)
+ * Copyright 1993 by Brian Moore (audio part)
+ * Changes Copyright 1993 by Gary Clark II
+ *
+ * Rewrote probe routine to work on newer Mitsumi drives.
+ * Additional changes (C) 1994 by Jordan K. Hubbard
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This software was developed by Holger Veit and Brian Moore
+ * for use with "386BSD" and similar operating systems.
+ * "Similar operating systems" includes mainly non-profit oriented
+ * systems for research and education, including but not restricted to
+ * "NetBSD", "FreeBSD", "Mach" (by CMU).
+ * 4. Neither the name of the developer(s) nor the name "386BSD"
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER(S) BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: mcd.c,v 1.15 1994/04/20 07:06:41 davidg Exp $
+ */
+static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";
+
+#include "mcd.h"
+#if NMCD > 0
+#include "types.h"
+#include "param.h"
+#include "systm.h"
+#include "conf.h"
+#include "file.h"
+#include "buf.h"
+#include "stat.h"
+#include "uio.h"
+#include "ioctl.h"
+#include "cdio.h"
+#include "errno.h"
+#include "dkbad.h"
+#include "disklabel.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "mcdreg.h"
+
+/* user definable options */
+/*#define MCD_TO_WARNING_ON*/ /* define to get timeout messages */
+/*#define MCDMINI*/ /* define for a mini configuration for boot kernel */
+
+
+#ifdef MCDMINI
+#define MCD_TRACE(fmt,a,b,c,d)
+#ifdef MCD_TO_WARNING_ON
+#undef MCD_TO_WARNING_ON
+#endif
+#else
+#define MCD_TRACE(fmt,a,b,c,d) {if (mcd_data[unit].debug) {printf("mcd%d st=%02x: ",unit,mcd_data[unit].status); printf(fmt,a,b,c,d);}}
+#endif
+
+#define mcd_part(dev) ((minor(dev)) & 7)
+#define mcd_unit(dev) (((minor(dev)) & 0x38) >> 3)
+#define mcd_phys(dev) (((minor(dev)) & 0x40) >> 6)
+#define RAW_PART 0
+
+/* flags */
+#define MCDOPEN 0x0001 /* device opened */
+#define MCDVALID 0x0002 /* parameters loaded */
+#define MCDINIT 0x0004 /* device is init'd */
+#define MCDWAIT 0x0008 /* waiting for something */
+#define MCDLABEL 0x0010 /* label is read */
+#define MCDPROBING 0x0020 /* probing */
+#define MCDREADRAW 0x0040 /* read raw mode (2352 bytes) */
+#define MCDVOLINFO 0x0080 /* already read volinfo */
+#define MCDTOC 0x0100 /* already read toc */
+#define MCDMBXBSY 0x0200 /* local mbx is busy */
+
+/* status */
+#define MCDAUDIOBSY MCD_ST_AUDIOBSY /* playing audio */
+#define MCDDSKCHNG MCD_ST_DSKCHNG /* sensed change of disk */
+#define MCDDSKIN MCD_ST_DSKIN /* sensed disk in drive */
+#define MCDDOOROPEN MCD_ST_DOOROPEN /* sensed door open */
+
+/* These are apparently the different states a mitsumi can get up to */
+#define MCDCDABSENT 0x0030
+#define MCDCDPRESENT 0x0020
+#define MCDSCLOSED 0x0080
+#define MCDSOPEN 0x00a0
+
+/* toc */
+#define MCD_MAXTOCS 104 /* from the Linux driver */
+#define MCD_LASTPLUS1 170 /* special toc entry */
+
+struct mcd_mbx {
+ short unit;
+ short port;
+ short retry;
+ short nblk;
+ int sz;
+ u_long skip;
+ struct buf *bp;
+ int p_offset;
+ short count;
+};
+
+struct mcd_data {
+ short config;
+ short flags;
+ short status;
+ int blksize;
+ u_long disksize;
+ int iobase;
+ struct disklabel dlabel;
+ int partflags[MAXPARTITIONS];
+ int openflags;
+ struct mcd_volinfo volinfo;
+#ifndef MCDMINI
+ struct mcd_qchninfo toc[MCD_MAXTOCS];
+ short audio_status;
+ struct mcd_read2 lastpb;
+#endif
+ short debug;
+ struct buf head; /* head of buf queue */
+ struct mcd_mbx mbx;
+} mcd_data[NMCD];
+
+/* reader state machine */
+#define MCD_S_BEGIN 0
+#define MCD_S_BEGIN1 1
+#define MCD_S_WAITSTAT 2
+#define MCD_S_WAITMODE 3
+#define MCD_S_WAITREAD 4
+
+/* prototypes */
+int mcdopen(dev_t dev);
+int mcdclose(dev_t dev);
+void mcdstrategy(struct buf *bp);
+int mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags);
+int mcdsize(dev_t dev);
+static void mcd_done(struct mcd_mbx *mbx);
+static void mcd_start(int unit);
+static int mcd_getdisklabel(int unit);
+static void mcd_configure(struct mcd_data *cd);
+static int mcd_get(int unit, char *buf, int nmax);
+static void mcd_setflags(int unit,struct mcd_data *cd);
+static int mcd_getstat(int unit,int sflg);
+static int mcd_send(int unit, int cmd,int nretrys);
+static int bcd2bin(bcd_t b);
+static bcd_t bin2bcd(int b);
+static void hsg2msf(int hsg, bcd_t *msf);
+static int msf2hsg(bcd_t *msf);
+static int mcd_volinfo(int unit);
+static int mcd_waitrdy(int port,int dly);
+static void mcd_doread(int state, struct mcd_mbx *mbxin);
+#ifndef MCDMINI
+static int mcd_setmode(int unit, int mode);
+static int mcd_getqchan(int unit, struct mcd_qchninfo *q);
+static int mcd_subchan(int unit, struct ioc_read_subchannel *sc);
+static int mcd_toc_header(int unit, struct ioc_toc_header *th);
+static int mcd_read_toc(int unit);
+static int mcd_toc_entry(int unit, struct ioc_read_toc_entry *te);
+static int mcd_stop(int unit);
+static int mcd_playtracks(int unit, struct ioc_play_track *pt);
+static int mcd_play(int unit, struct mcd_read2 *pb);
+static int mcd_pause(int unit);
+static int mcd_resume(int unit);
+#endif
+
+extern int hz;
+extern int mcd_probe(struct isa_device *dev);
+extern int mcd_attach(struct isa_device *dev);
+struct isa_driver mcddriver = { mcd_probe, mcd_attach, "mcd" };
+
+#define mcd_put(port,byte) outb(port,byte)
+
+#define MCD_RETRYS 5
+#define MCD_RDRETRYS 8
+
+#define MCDBLK 2048 /* for cooked mode */
+#define MCDRBLK 2352 /* for raw mode */
+
+/* several delays */
+#define RDELAY_WAITSTAT 300
+#define RDELAY_WAITMODE 300
+#define RDELAY_WAITREAD 800
+
+#define DELAY_STATUS 10000l /* 10000 * 1us */
+#define DELAY_GETREPLY 200000l /* 200000 * 2us */
+#define DELAY_SEEKREAD 20000l /* 20000 * 1us */
+#define mcd_delay DELAY
+
+int mcd_attach(struct isa_device *dev)
+{
+ struct mcd_data *cd = mcd_data + dev->id_unit;
+ int i;
+
+ cd->iobase = dev->id_iobase;
+ cd->flags |= MCDINIT;
+ cd->openflags = 0;
+ for (i=0; i<MAXPARTITIONS; i++) cd->partflags[i] = 0;
+
+#ifdef NOTYET
+ /* wire controller for interrupts and dma */
+ mcd_configure(cd);
+#endif
+
+ return 1;
+}
+
+int mcdopen(dev_t dev)
+{
+ int unit,part,phys;
+ struct mcd_data *cd;
+
+ unit = mcd_unit(dev);
+ if (unit >= NMCD)
+ return ENXIO;
+
+ cd = mcd_data + unit;
+ part = mcd_part(dev);
+ phys = mcd_phys(dev);
+
+ /* not initialized*/
+ if (!(cd->flags & MCDINIT))
+ return ENXIO;
+
+ /* invalidated in the meantime? mark all open part's invalid */
+ if (!(cd->flags & MCDVALID) && cd->openflags)
+ return ENXIO;
+
+ if (mcd_getstat(unit,1) < 0)
+ return ENXIO;
+
+ /* XXX get a default disklabel */
+ mcd_getdisklabel(unit);
+
+ if (mcdsize(dev) < 0) {
+ printf("mcd%d: failed to get disk size\n",unit);
+ return ENXIO;
+ } else
+ cd->flags |= MCDVALID;
+
+MCD_TRACE("open: partition=%d, disksize = %d, blksize=%d\n",
+ part,cd->disksize,cd->blksize,0);
+
+ if (part == RAW_PART ||
+ (part < cd->dlabel.d_npartitions &&
+ cd->dlabel.d_partitions[part].p_fstype != FS_UNUSED)) {
+ cd->partflags[part] |= MCDOPEN;
+ cd->openflags |= (1<<part);
+ if (part == RAW_PART && phys != 0)
+ cd->partflags[part] |= MCDREADRAW;
+ return 0;
+ }
+
+ return ENXIO;
+}
+
+int mcdclose(dev_t dev)
+{
+ int unit,part,phys;
+ struct mcd_data *cd;
+
+ unit = mcd_unit(dev);
+ if (unit >= NMCD)
+ return ENXIO;
+
+ cd = mcd_data + unit;
+ part = mcd_part(dev);
+ phys = mcd_phys(dev);
+
+ if (!(cd->flags & MCDINIT))
+ return ENXIO;
+
+ mcd_getstat(unit,1); /* get status */
+
+ /* close channel */
+ cd->partflags[part] &= ~(MCDOPEN|MCDREADRAW);
+ cd->openflags &= ~(1<<part);
+ MCD_TRACE("close: partition=%d\n",part,0,0,0);
+
+ return 0;
+}
+
+void
+mcdstrategy(struct buf *bp)
+{
+ struct mcd_data *cd;
+ struct buf *qp;
+ int s;
+
+ int unit = mcd_unit(bp->b_dev);
+
+ cd = mcd_data + unit;
+
+ /* test validity */
+/*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n",
+ bp,unit,bp->b_blkno,bp->b_bcount);*/
+ if (unit >= NMCD || bp->b_blkno < 0) {
+ printf("mcdstrategy: unit = %d, blkno = %d, bcount = %d\n",
+ unit, bp->b_blkno, bp->b_bcount);
+ pg("mcd: mcdstratregy failure");
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ goto bad;
+ }
+
+ /* if device invalidated (e.g. media change, door open), error */
+ if (!(cd->flags & MCDVALID)) {
+MCD_TRACE("strategy: drive not valid\n",0,0,0,0);
+ bp->b_error = EIO;
+ goto bad;
+ }
+
+ /* read only */
+ if (!(bp->b_flags & B_READ)) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+
+ /* no data to read */
+ if (bp->b_bcount == 0)
+ goto done;
+
+ /* for non raw access, check partition limits */
+ if (mcd_part(bp->b_dev) != RAW_PART) {
+ if (!(cd->flags & MCDLABEL)) {
+ bp->b_error = EIO;
+ goto bad;
+ }
+ /* adjust transfer if necessary */
+ if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) {
+ goto done;
+ }
+ } else {
+ bp->b_pblkno = bp->b_blkno;
+ bp->b_resid = 0;
+ }
+
+ /* queue it */
+ qp = &cd->head;
+ s = splbio();
+ disksort(qp,bp);
+ splx(s);
+
+ /* now check whether we can perform processing */
+ mcd_start(unit);
+ return;
+
+bad:
+ bp->b_flags |= B_ERROR;
+done:
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+ return;
+}
+
+static void mcd_start(int unit)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ struct buf *bp, *qp = &cd->head;
+ struct partition *p;
+ int part;
+ register s = splbio();
+
+ if (cd->flags & MCDMBXBSY)
+ return;
+
+ if ((bp = qp->b_actf) != 0) {
+ /* block found to process, dequeue */
+ /*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/
+ qp->b_actf = bp->b_actf;
+ splx(s);
+ } else {
+ /* nothing to do */
+ splx(s);
+ return;
+ }
+
+ /* changed media? */
+ if (!(cd->flags & MCDVALID)) {
+ MCD_TRACE("mcd_start: drive not valid\n",0,0,0,0);
+ return;
+ }
+
+ p = cd->dlabel.d_partitions + mcd_part(bp->b_dev);
+
+ cd->flags |= MCDMBXBSY;
+ cd->mbx.unit = unit;
+ cd->mbx.port = cd->iobase;
+ cd->mbx.retry = MCD_RETRYS;
+ cd->mbx.bp = bp;
+ cd->mbx.p_offset = p->p_offset;
+
+ /* calling the read routine */
+ mcd_doread(MCD_S_BEGIN,&(cd->mbx));
+ /* triggers mcd_start, when successful finished */
+ return;
+}
+
+int mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags)
+{
+ struct mcd_data *cd;
+ int unit,part;
+
+ unit = mcd_unit(dev);
+ part = mcd_part(dev);
+ cd = mcd_data + unit;
+
+#ifdef MCDMINI
+ return ENOTTY;
+#else
+ if (!(cd->flags & MCDVALID))
+ return EIO;
+MCD_TRACE("ioctl called 0x%x\n",cmd,0,0,0);
+
+ switch (cmd) {
+ case DIOCSBAD:
+ return EINVAL;
+ case DIOCGDINFO:
+ case DIOCGPART:
+ case DIOCWDINFO:
+ case DIOCSDINFO:
+ case DIOCWLABEL:
+ return ENOTTY;
+ case CDIOCPLAYTRACKS:
+ return mcd_playtracks(unit, (struct ioc_play_track *) addr);
+ case CDIOCPLAYBLOCKS:
+ return mcd_play(unit, (struct mcd_read2 *) addr);
+ case CDIOCREADSUBCHANNEL:
+ return mcd_subchan(unit, (struct ioc_read_subchannel *) addr);
+ case CDIOREADTOCHEADER:
+ return mcd_toc_header(unit, (struct ioc_toc_header *) addr);
+ case CDIOREADTOCENTRYS:
+ return mcd_toc_entry(unit, (struct ioc_read_toc_entry *) addr);
+ case CDIOCSETPATCH:
+ case CDIOCGETVOL:
+ case CDIOCSETVOL:
+ case CDIOCSETMONO:
+ case CDIOCSETSTERIO:
+ case CDIOCSETMUTE:
+ case CDIOCSETLEFT:
+ case CDIOCSETRIGHT:
+ return EINVAL;
+ case CDIOCRESUME:
+ return mcd_resume(unit);
+ case CDIOCPAUSE:
+ return mcd_pause(unit);
+ case CDIOCSTART:
+ return EINVAL;
+ case CDIOCSTOP:
+ return mcd_stop(unit);
+ case CDIOCEJECT:
+ return EINVAL;
+ case CDIOCSETDEBUG:
+ cd->debug = 1;
+ return 0;
+ case CDIOCCLRDEBUG:
+ cd->debug = 0;
+ return 0;
+ case CDIOCRESET:
+ return EINVAL;
+ default:
+ return ENOTTY;
+ }
+ /*NOTREACHED*/
+#endif /*!MCDMINI*/
+}
+
+/* this could have been taken from scsi/cd.c, but it is not clear
+ * whether the scsi cd driver is linked in
+ */
+static int mcd_getdisklabel(int unit)
+{
+ struct mcd_data *cd = mcd_data + unit;
+
+ if (cd->flags & MCDLABEL)
+ return -1;
+
+ bzero(&cd->dlabel,sizeof(struct disklabel));
+ strncpy(cd->dlabel.d_typename,"Mitsumi CD ROM ",16);
+ strncpy(cd->dlabel.d_packname,"unknown ",16);
+ cd->dlabel.d_secsize = cd->blksize;
+ cd->dlabel.d_nsectors = 100;
+ cd->dlabel.d_ntracks = 1;
+ cd->dlabel.d_ncylinders = (cd->disksize/100)+1;
+ cd->dlabel.d_secpercyl = 100;
+ cd->dlabel.d_secperunit = cd->disksize;
+ cd->dlabel.d_rpm = 300;
+ cd->dlabel.d_interleave = 1;
+ cd->dlabel.d_flags = D_REMOVABLE;
+ cd->dlabel.d_npartitions= 1;
+ cd->dlabel.d_partitions[0].p_offset = 0;
+ cd->dlabel.d_partitions[0].p_size = cd->disksize;
+ cd->dlabel.d_partitions[0].p_fstype = 9;
+ cd->dlabel.d_magic = DISKMAGIC;
+ cd->dlabel.d_magic2 = DISKMAGIC;
+ cd->dlabel.d_checksum = dkcksum(&cd->dlabel);
+
+ cd->flags |= MCDLABEL;
+ return 0;
+}
+
+int mcdsize(dev_t dev)
+{
+ int size;
+ int unit = mcd_unit(dev);
+ struct mcd_data *cd = mcd_data + unit;
+
+ if (mcd_volinfo(unit) >= 0) {
+ cd->blksize = MCDBLK;
+ size = msf2hsg(cd->volinfo.vol_msf);
+ cd->disksize = size * (MCDBLK/DEV_BSIZE);
+ return 0;
+ }
+ return -1;
+}
+
+/***************************************************************
+ * lower level of driver starts here
+ **************************************************************/
+
+#ifdef NOTDEF
+static char
+irqs[] = {
+ 0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00,
+ 0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00
+};
+
+static char
+drqs[] = {
+ 0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07,
+};
+#endif
+
+static void
+mcd_configure(struct mcd_data *cd)
+{
+ outb(cd->iobase+mcd_config,cd->config);
+}
+
+/* Wait for non-busy - return 0 on timeout */
+static int
+twiddle_thumbs(int port, int unit, int count, char *whine)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (!(inb(port+MCD_FLAGS) & MCD_ST_BUSY)) {
+ return 1;
+ }
+ }
+#ifdef MCD_TO_WARNING_ON
+ printf("mcd%d: timeout %s\n", unit, whine);
+#endif
+ return 0;
+}
+
+/* check to see if a Mitsumi CD-ROM is attached to the ISA bus */
+
+int
+mcd_probe(struct isa_device *dev)
+{
+ int port = dev->id_iobase;
+ int unit = dev->id_unit;
+ int i, j;
+ int status;
+ unsigned char stbytes[3];
+
+ mcd_data[unit].flags = MCDPROBING;
+
+#ifdef NOTDEF
+ /* get irq/drq configuration word */
+ mcd_data[unit].config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/
+#else
+ mcd_data[unit].config = 0;
+#endif
+
+ /* send a reset */
+ outb(port+MCD_FLAGS, M_RESET);
+
+ /*
+ * delay awhile by getting any pending garbage (old data) and
+ * throwing it away.
+ */
+ for (i = 1000000; i != 0; i--) {
+ inb(port+MCD_FLAGS);
+ }
+
+ /* Get status */
+ outb(port+MCD_DATA, MCD_CMDGETSTAT);
+ if (!twiddle_thumbs(port, unit, 1000000, "getting status")) {
+ return 0; /* Timeout */
+ }
+ status = inb(port+MCD_DATA);
+ if (status != MCDCDABSENT && status != MCDCDPRESENT &&
+ status != MCDSOPEN && status != MCDSCLOSED)
+ return 0; /* Not actually a Mitsumi drive here */
+ /* Get version information */
+ outb(port+MCD_DATA, MCD_CMDCONTINFO);
+ for (j = 0; j < 3; j++) {
+ if (!twiddle_thumbs(port, unit, 3000, "getting version info")) {
+ return 0;
+ }
+ stbytes[j] = (inb(port+MCD_DATA) & 0xFF);
+ }
+ printf("mcd%d: version information is %x %c %x\n", unit,
+ stbytes[0], stbytes[1], stbytes[2]);
+ if (stbytes[1] >= 4) {
+ outb(port+MCD_CTRL, M_PICKLE);
+ printf("mcd%d: Adjusted for newer drive model\n", unit);
+ }
+ return 4;
+}
+
+
+static int
+mcd_waitrdy(int port,int dly)
+{
+ int i;
+
+ /* wait until xfer port senses data ready */
+ for (i=0; i<dly; i++) {
+ if ((inb(port+mcd_xfer) & MCD_ST_BUSY)==0)
+ return 0;
+ mcd_delay(1);
+ }
+ return -1;
+}
+
+static int
+mcd_getreply(int unit,int dly)
+{
+ int i;
+ struct mcd_data *cd = mcd_data + unit;
+ int port = cd->iobase;
+
+ /* wait data to become ready */
+ if (mcd_waitrdy(port,dly)<0) {
+#ifdef MCD_TO_WARNING_ON
+ printf("mcd%d: timeout getreply\n",unit);
+#endif
+ return -1;
+ }
+
+ /* get the data */
+ return inb(port+mcd_status) & 0xFF;
+}
+
+static int
+mcd_getstat(int unit,int sflg)
+{
+ int i;
+ struct mcd_data *cd = mcd_data + unit;
+ int port = cd->iobase;
+
+ /* get the status */
+ if (sflg)
+ outb(port+mcd_command, MCD_CMDGETSTAT);
+ i = mcd_getreply(unit,DELAY_GETREPLY);
+ if (i<0) return -1;
+
+ cd->status = i;
+
+ mcd_setflags(unit,cd);
+ return cd->status;
+}
+
+static void
+mcd_setflags(int unit, struct mcd_data *cd)
+{
+ /* check flags */
+ if (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) {
+ MCD_TRACE("getstat: sensed DSKCHNG or DOOROPEN\n",0,0,0,0);
+ cd->flags &= ~MCDVALID;
+ }
+
+#ifndef MCDMINI
+ if (cd->status & MCDAUDIOBSY)
+ cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
+ else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS)
+ cd->audio_status = CD_AS_PLAY_COMPLETED;
+#endif
+}
+
+static int
+mcd_get(int unit, char *buf, int nmax)
+{
+ int port = mcd_data[unit].iobase;
+ int i,k;
+
+ for (i=0; i<nmax; i++) {
+ /* wait for data */
+ if ((k = mcd_getreply(unit,DELAY_GETREPLY)) < 0) {
+#ifdef MCD_TO_WARNING_ON
+ printf("mcd%d: timeout mcd_get\n",unit);
+#endif
+ return -1;
+ }
+ buf[i] = k;
+ }
+ return i;
+}
+
+static int
+mcd_send(int unit, int cmd,int nretrys)
+{
+ int i,k;
+ int port = mcd_data[unit].iobase;
+
+/*MCD_TRACE("mcd_send: command = 0x%x\n",cmd,0,0,0);*/
+ for (i=0; i<nretrys; i++) {
+ outb(port+mcd_command, cmd);
+ if ((k=mcd_getstat(unit,0)) != -1) {
+ break;
+ }
+ }
+ if (i == nretrys) {
+ printf("mcd%d: mcd_send retry cnt exceeded\n",unit);
+ return -1;
+ }
+/*MCD_TRACE("mcd_send: status = 0x%x\n",k,0,0,0);*/
+ return 0;
+}
+
+static int
+bcd2bin(bcd_t b)
+{
+ return (b >> 4) * 10 + (b & 15);
+}
+
+static bcd_t
+bin2bcd(int b)
+{
+ return ((b / 10) << 4) | (b % 10);
+}
+
+static void
+hsg2msf(int hsg, bcd_t *msf)
+{
+ hsg += 150;
+ M_msf(msf) = bin2bcd(hsg / 4500);
+ hsg %= 4500;
+ S_msf(msf) = bin2bcd(hsg / 75);
+ F_msf(msf) = bin2bcd(hsg % 75);
+}
+
+static int
+msf2hsg(bcd_t *msf)
+{
+ return (bcd2bin(M_msf(msf)) * 60 +
+ bcd2bin(S_msf(msf))) * 75 +
+ bcd2bin(F_msf(msf)) - 150;
+}
+
+static int
+mcd_volinfo(int unit)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ int i;
+
+/*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/
+
+ /* Get the status, in case the disc has been changed */
+ if (mcd_getstat(unit, 1) < 0) return EIO;
+
+ /* Just return if we already have it */
+ if (cd->flags & MCDVOLINFO) return 0;
+
+ /* send volume info command */
+ if (mcd_send(unit,MCD_CMDGETVOLINFO,MCD_RETRYS) < 0)
+ return -1;
+
+ /* get data */
+ if (mcd_get(unit,(char*) &cd->volinfo,sizeof(struct mcd_volinfo)) < 0) {
+ printf("mcd%d: mcd_volinfo: error read data\n",unit);
+ return -1;
+ }
+
+ if (cd->volinfo.trk_low != 0 || cd->volinfo.trk_high != 0) {
+ cd->flags |= MCDVOLINFO; /* volinfo is OK */
+ return 0;
+ }
+
+ return -1;
+}
+
+void
+mcdintr(unit)
+ int unit;
+{
+ int port = mcd_data[unit].iobase;
+ u_int i;
+
+ MCD_TRACE("stray interrupt xfer=0x%x\n",inb(port+mcd_xfer),0,0,0);
+
+ /* just read out status and ignore the rest */
+ if ((inb(port+mcd_xfer)&0xFF) != 0xFF) {
+ i = inb(port+mcd_status);
+ }
+}
+
+/* state machine to process read requests
+ * initialize with MCD_S_BEGIN: calculate sizes, and read status
+ * MCD_S_WAITSTAT: wait for status reply, set mode
+ * MCD_S_WAITMODE: waits for status reply from set mode, set read command
+ * MCD_S_WAITREAD: wait for read ready, read data
+ */
+static struct mcd_mbx *mbxsave;
+
+static void
+mcd_doread(int state, struct mcd_mbx *mbxin)
+{
+ struct mcd_mbx *mbx = (state!=MCD_S_BEGIN) ? mbxsave : mbxin;
+ int unit = mbx->unit;
+ int port = mbx->port;
+ struct buf *bp = mbx->bp;
+ struct mcd_data *cd = mcd_data + unit;
+
+ int rm,i,k;
+ struct mcd_read2 rbuf;
+ int blknum;
+ caddr_t addr;
+
+loop:
+ switch (state) {
+ case MCD_S_BEGIN:
+ mbx = mbxsave = mbxin;
+
+ case MCD_S_BEGIN1:
+ /* get status */
+ outb(port+mcd_command, MCD_CMDGETSTAT);
+ mbx->count = RDELAY_WAITSTAT;
+ timeout((timeout_func_t)mcd_doread,
+ (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
+ return;
+ case MCD_S_WAITSTAT:
+ untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITSTAT);
+ if (mbx->count-- >= 0) {
+ if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
+ timeout((timeout_func_t)mcd_doread,
+ (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
+ return;
+ }
+ mcd_setflags(unit,cd);
+ MCD_TRACE("got WAITSTAT delay=%d\n",
+ RDELAY_WAITSTAT-mbx->count,0,0,0);
+ /* reject, if audio active */
+ if (cd->status & MCDAUDIOBSY) {
+ printf("mcd%d: audio is active\n",unit);
+ goto readerr;
+ }
+
+ /* to check for raw/cooked mode */
+ if (cd->flags & MCDREADRAW) {
+ rm = MCD_MD_RAW;
+ mbx->sz = MCDRBLK;
+ } else {
+ rm = MCD_MD_COOKED;
+ mbx->sz = cd->blksize;
+ }
+
+ mbx->count = RDELAY_WAITMODE;
+
+ mcd_put(port+mcd_command, MCD_CMDSETMODE);
+ mcd_put(port+mcd_command, rm);
+ timeout((timeout_func_t)mcd_doread,
+ (caddr_t)MCD_S_WAITMODE,hz/100); /* XXX */
+ return;
+ } else {
+#ifdef MCD_TO_WARNING_ON
+ printf("mcd%d: timeout getstatus\n",unit);
+#endif
+ goto readerr;
+ }
+
+ case MCD_S_WAITMODE:
+ untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE);
+ if (mbx->count-- < 0) {
+#ifdef MCD_TO_WARNING_ON
+ printf("mcd%d: timeout set mode\n",unit);
+#endif
+ goto readerr;
+ }
+ if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
+ timeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE,hz/100);
+ return;
+ }
+ mcd_setflags(unit,cd);
+ MCD_TRACE("got WAITMODE delay=%d\n",
+ RDELAY_WAITMODE-mbx->count,0,0,0);
+ /* for first block */
+ mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz;
+ mbx->skip = 0;
+
+nextblock:
+ blknum = (bp->b_blkno / (mbx->sz/DEV_BSIZE))
+ + mbx->p_offset + mbx->skip/mbx->sz;
+
+ MCD_TRACE("mcd_doread: read blknum=%d for bp=0x%x\n",
+ blknum,bp,0,0);
+
+ /* build parameter block */
+ hsg2msf(blknum,rbuf.start_msf);
+
+ /* send the read command */
+ mcd_put(port+mcd_command,MCD_CMDREAD2);
+ mcd_put(port+mcd_command,rbuf.start_msf[0]);
+ mcd_put(port+mcd_command,rbuf.start_msf[1]);
+ mcd_put(port+mcd_command,rbuf.start_msf[2]);
+ mcd_put(port+mcd_command,0);
+ mcd_put(port+mcd_command,0);
+ mcd_put(port+mcd_command,1);
+ mbx->count = RDELAY_WAITREAD;
+ timeout((timeout_func_t)mcd_doread,
+ (caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
+ return;
+ case MCD_S_WAITREAD:
+ untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITREAD);
+ if (mbx->count-- > 0) {
+ k = inb(port+mcd_xfer);
+ if ((k & 2)==0) {
+ MCD_TRACE("got data delay=%d\n",
+ RDELAY_WAITREAD-mbx->count,0,0,0);
+ /* data is ready */
+ addr = bp->b_un.b_addr + mbx->skip;
+ outb(port+mcd_ctl2,0x04); /* XXX */
+ for (i=0; i<mbx->sz; i++)
+ *addr++ = inb(port+mcd_rdata);
+ outb(port+mcd_ctl2,0x0c); /* XXX */
+
+ if (--mbx->nblk > 0) {
+ mbx->skip += mbx->sz;
+ goto nextblock;
+ }
+
+ /* return buffer */
+ bp->b_resid = 0;
+ biodone(bp);
+
+ cd->flags &= ~MCDMBXBSY;
+ mcd_start(mbx->unit);
+ return;
+ }
+ if ((k & 4)==0)
+ mcd_getstat(unit,0);
+ timeout((timeout_func_t)mcd_doread,
+ (caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
+ return;
+ } else {
+#ifdef MCD_TO_WARNING_ON
+ printf("mcd%d: timeout read data\n",unit);
+#endif
+ goto readerr;
+ }
+ }
+
+readerr:
+ if (mbx->retry-- > 0) {
+#ifdef MCD_TO_WARNING_ON
+ printf("mcd%d: retrying\n",unit);
+#endif
+ state = MCD_S_BEGIN1;
+ goto loop;
+ }
+
+ /* invalidate the buffer */
+ bp->b_flags |= B_ERROR;
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+ mcd_start(mbx->unit);
+ return;
+
+#ifdef NOTDEF
+ printf("mcd%d: unit timeout, resetting\n",mbx->unit);
+ outb(mbx->port+mcd_reset,MCD_CMDRESET);
+ DELAY(300000);
+ (void)mcd_getstat(mbx->unit,1);
+ (void)mcd_getstat(mbx->unit,1);
+ /*cd->status &= ~MCDDSKCHNG; */
+ cd->debug = 1; /* preventive set debug mode */
+
+#endif
+
+}
+
+#ifndef MCDMINI
+static int
+mcd_setmode(int unit, int mode)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ int port = cd->iobase;
+ int retry;
+
+ printf("mcd%d: setting mode to %d\n", unit, mode);
+ for(retry=0; retry<MCD_RETRYS; retry++)
+ {
+ outb(port+mcd_command, MCD_CMDSETMODE);
+ outb(port+mcd_command, mode);
+ if (mcd_getstat(unit, 0) != -1) return 0;
+ }
+
+ return -1;
+}
+
+static int
+mcd_toc_header(int unit, struct ioc_toc_header *th)
+{
+ struct mcd_data *cd = mcd_data + unit;
+
+ if (mcd_volinfo(unit) < 0) {
+ return ENXIO;
+ }
+
+ th->len = msf2hsg(cd->volinfo.vol_msf);
+ th->starting_track = bcd2bin(cd->volinfo.trk_low);
+ th->ending_track = bcd2bin(cd->volinfo.trk_high);
+
+ return 0;
+}
+
+static int
+mcd_read_toc(int unit)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ struct ioc_toc_header th;
+ struct mcd_qchninfo q;
+ int rc, trk, idx, retry;
+
+ /* Only read TOC if needed */
+ if (cd->flags & MCDTOC) {
+ return 0;
+ }
+
+ printf("mcd%d: reading toc header\n", unit);
+ if (mcd_toc_header(unit, &th) != 0) {
+ return ENXIO;
+ }
+
+ printf("mcd%d: stopping play\n", unit);
+ if ((rc=mcd_stop(unit)) != 0) {
+ return rc;
+ }
+
+ /* try setting the mode twice */
+ if (mcd_setmode(unit, MCD_MD_TOC) != 0) {
+ return EIO;
+ }
+ if (mcd_setmode(unit, MCD_MD_TOC) != 0) {
+ return EIO;
+ }
+
+ printf("mcd%d: get_toc reading qchannel info\n",unit);
+ for(trk=th.starting_track; trk<=th.ending_track; trk++)
+ cd->toc[trk].idx_no = 0;
+ trk = th.ending_track - th.starting_track + 1;
+ for(retry=0; retry<300 && trk>0; retry++)
+ {
+ if (mcd_getqchan(unit, &q) < 0) break;
+ idx = bcd2bin(q.idx_no);
+ if (idx>0 && idx < MCD_MAXTOCS && q.trk_no==0) {
+ if (cd->toc[idx].idx_no == 0) {
+ cd->toc[idx] = q;
+ trk--;
+ }
+ }
+ }
+
+ if (mcd_setmode(unit, MCD_MD_COOKED) != 0) {
+ return EIO;
+ }
+
+ if (trk != 0) {
+ return ENXIO;
+ }
+
+ /* add a fake last+1 */
+ idx = th.ending_track + 1;
+ cd->toc[idx].ctrl_adr = cd->toc[idx-1].ctrl_adr;
+ cd->toc[idx].trk_no = 0;
+ cd->toc[idx].idx_no = 0xAA;
+ cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0];
+ cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1];
+ cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2];
+
+ cd->flags |= MCDTOC;
+
+ return 0;
+}
+
+static int
+mcd_toc_entry(int unit, struct ioc_read_toc_entry *te)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ struct ret_toc {
+ struct ioc_toc_header th;
+ struct cd_toc_entry rt;
+ } ret_toc;
+ struct ioc_toc_header th;
+ int rc, i;
+
+ /* Make sure we have a valid toc */
+ if ((rc=mcd_read_toc(unit)) != 0) {
+ return rc;
+ }
+
+ /* find the toc to copy*/
+ i = te->starting_track;
+ if (i == MCD_LASTPLUS1) {
+ i = bcd2bin(cd->volinfo.trk_high) + 1;
+ }
+
+ /* verify starting track */
+ if (i < bcd2bin(cd->volinfo.trk_low) ||
+ i > bcd2bin(cd->volinfo.trk_high)+1) {
+ return EINVAL;
+ }
+
+ /* do we have room */
+ if (te->data_len < sizeof(struct ioc_toc_header) +
+ sizeof(struct cd_toc_entry)) {
+ return EINVAL;
+ }
+
+ /* Copy the toc header */
+ if (mcd_toc_header(unit, &th) < 0) {
+ return EIO;
+ }
+ ret_toc.th = th;
+
+ /* copy the toc data */
+ ret_toc.rt.control = cd->toc[i].ctrl_adr;
+ ret_toc.rt.addr_type = te->address_format;
+ ret_toc.rt.track = i;
+ if (te->address_format == CD_MSF_FORMAT) {
+ ret_toc.rt.addr.addr[1] = cd->toc[i].hd_pos_msf[0];
+ ret_toc.rt.addr.addr[2] = cd->toc[i].hd_pos_msf[1];
+ ret_toc.rt.addr.addr[3] = cd->toc[i].hd_pos_msf[2];
+ }
+
+ /* copy the data back */
+ copyout(&ret_toc, te->data, sizeof(struct cd_toc_entry)
+ + sizeof(struct ioc_toc_header));
+
+ return 0;
+}
+
+static int
+mcd_stop(int unit)
+{
+ struct mcd_data *cd = mcd_data + unit;
+
+ if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0) {
+ return ENXIO;
+ }
+ cd->audio_status = CD_AS_PLAY_COMPLETED;
+ return 0;
+}
+
+static int
+mcd_getqchan(int unit, struct mcd_qchninfo *q)
+{
+ struct mcd_data *cd = mcd_data + unit;
+
+ if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0) {
+ return -1;
+ }
+ if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0) {
+ return -1;
+ }
+ if (cd->debug) {
+ printf("mcd%d: qchannel ctl=%d, t=%d, i=%d, ttm=%d:%d.%d dtm=%d:%d.%d\n",
+ unit,
+ q->ctrl_adr, q->trk_no, q->idx_no,
+ q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2],
+ q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2]);
+ }
+ return 0;
+}
+
+static int
+mcd_subchan(int unit, struct ioc_read_subchannel *sc)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ struct mcd_qchninfo q;
+ struct cd_sub_channel_info data;
+
+ printf("mcd%d: subchan af=%d, df=%d\n", unit,
+ sc->address_format,
+ sc->data_format);
+ if (sc->address_format != CD_MSF_FORMAT) {
+ return EIO;
+ }
+ if (sc->data_format != CD_CURRENT_POSITION) {
+ return EIO;
+ }
+ if (mcd_getqchan(unit, &q) < 0) {
+ return EIO;
+ }
+
+ data.header.audio_status = cd->audio_status;
+ data.what.position.data_format = CD_MSF_FORMAT;
+ data.what.position.track_number = bcd2bin(q.trk_no);
+
+ if (copyout(&data, sc->data, sizeof(struct cd_sub_channel_info))!=0) {
+ return EFAULT;
+ }
+ return 0;
+}
+
+static int
+mcd_playtracks(int unit, struct ioc_play_track *pt)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ struct mcd_read2 pb;
+ int a = pt->start_track;
+ int z = pt->end_track;
+ int rc;
+
+ if ((rc = mcd_read_toc(unit)) != 0) {
+ return rc;
+ }
+ printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit,
+ a, pt->start_index, z, pt->end_index);
+
+ if (a < cd->volinfo.trk_low || a > cd->volinfo.trk_high || a > z ||
+ z < cd->volinfo.trk_low || z > cd->volinfo.trk_high) {
+ return EINVAL;
+ }
+
+ pb.start_msf[0] = cd->toc[a].hd_pos_msf[0];
+ pb.start_msf[1] = cd->toc[a].hd_pos_msf[1];
+ pb.start_msf[2] = cd->toc[a].hd_pos_msf[2];
+ pb.end_msf[0] = cd->toc[z+1].hd_pos_msf[0];
+ pb.end_msf[1] = cd->toc[z+1].hd_pos_msf[1];
+ pb.end_msf[2] = cd->toc[z+1].hd_pos_msf[2];
+
+ return mcd_play(unit, &pb);
+}
+
+static int
+mcd_play(int unit, struct mcd_read2 *pb)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ int port = cd->iobase;
+ int retry, st;
+
+ cd->lastpb = *pb;
+ for(retry=0; retry<MCD_RETRYS; retry++) {
+ outb(port+mcd_command, MCD_CMDREAD2);
+ outb(port+mcd_command, pb->start_msf[0]);
+ outb(port+mcd_command, pb->start_msf[1]);
+ outb(port+mcd_command, pb->start_msf[2]);
+ outb(port+mcd_command, pb->end_msf[0]);
+ outb(port+mcd_command, pb->end_msf[1]);
+ outb(port+mcd_command, pb->end_msf[2]);
+ if ((st=mcd_getstat(unit, 0)) != -1) {
+ break;
+ }
+ }
+
+ if (cd->debug) {
+ printf("mcd%d: mcd_play retry=%d, status=%d\n", unit, retry, st);
+ }
+ if (st == -1) {
+ return ENXIO;
+ }
+ cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
+ return 0;
+}
+
+static int
+mcd_pause(int unit)
+{
+ struct mcd_data *cd = mcd_data + unit;
+ struct mcd_qchninfo q;
+ int rc;
+
+ /* Verify current status */
+ if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) {
+ printf("mcd%d: pause attempted when not playing\n", unit);
+ return EINVAL;
+ }
+
+ /* Get the current position */
+ if (mcd_getqchan(unit, &q) < 0) {
+ return EIO;
+ }
+
+ /* Copy it into lastpb */
+ cd->lastpb.start_msf[0] = q.hd_pos_msf[0];
+ cd->lastpb.start_msf[1] = q.hd_pos_msf[1];
+ cd->lastpb.start_msf[2] = q.hd_pos_msf[2];
+
+ /* Stop playing */
+ if ((rc=mcd_stop(unit)) != 0) {
+ return rc;
+ }
+
+ /* Set the proper status and exit */
+ cd->audio_status = CD_AS_PLAY_PAUSED;
+ return 0;
+}
+
+static int
+mcd_resume(int unit)
+{
+ struct mcd_data *cd = mcd_data + unit;
+
+ if (cd->audio_status != CD_AS_PLAY_PAUSED) {
+ return EINVAL;
+ }
+ return mcd_play(unit, &cd->lastpb);
+}
+#endif /*!MCDMINI*/
+
+#endif /* NMCD > 0 */
diff --git a/sys/i386/isa/mcdreg.h b/sys/i386/isa/mcdreg.h
new file mode 100644
index 0000000..0ce5de7
--- /dev/null
+++ b/sys/i386/isa/mcdreg.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright 1993 by Holger Veit (data part)
+ * Copyright 1993 by Brian Moore (audio part)
+ * Changes Copyright 1993 by Gary Clark II
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This software was developed by Holger Veit and Brian Moore
+ * for use with "386BSD" and similar operating systems.
+ * "Similar operating systems" includes mainly non-profit oriented
+ * systems for research and education, including but not restricted to
+ * "NetBSD", "FreeBSD", "Mach" (by CMU).
+ * 4. Neither the name of the developer(s) nor the name "386BSD"
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER(S) BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file contains definitions for some cdrom control commands
+ * and status codes. This info was "inherited" from the DOS MTMCDE.SYS
+ * driver, and is thus not complete (and may even be wrong). Some day
+ * the manufacturer or anyone else might provide better documentation,
+ * so this file (and the driver) will then have a better quality.
+ *
+ * $Id: mcdreg.h,v 1.2 1994/01/16 23:34:17 jkh Exp $
+ */
+
+#ifndef MCD_H
+#define MCD_H
+
+#ifdef __GNUC__
+#if __GNUC__ >= 2
+#pragma pack(1)
+#endif
+#endif
+
+typedef unsigned char bcd_t;
+#define M_msf(msf) msf[0]
+#define S_msf(msf) msf[1]
+#define F_msf(msf) msf[2]
+
+/* io lines used */
+#define MCD_IO_BASE 0x300
+
+#define mcd_command 0
+#define mcd_status 0
+#define mcd_rdata 0
+
+#define mcd_reset 1
+#define mcd_xfer 1
+#define mcd_ctl2 2 /* XXX Is this right? */
+#define mcd_config 3
+
+#define MCD_MASK_DMA 0x07 /* bits 2-0 = DMA channel */
+#define MCD_MASK_IRQ 0x70 /* bits 6-4 = INT number */
+ /* 001 = int 2,9 */
+ /* 010 = int 3 */
+ /* 011 = int 5 */
+ /* 100 = int 10 */
+ /* 101 = int 11 */
+/* flags */
+#define STATUS_AVAIL 0xB
+#define DATA_AVAIL 0xF
+
+/* New Flags */
+#define M_STATUS_AVAIL 0xFB
+#define M_DATA_AVAIL 0xFD
+
+/* New Commands */
+#define M_RESET 0x00
+#define M_PICKLE 0x04
+
+/* ports */
+#define MCD_DATA 0
+#define MCD_FLAGS 1
+#define MCD_CTRL 2
+#define CHANNEL 3 /* XXX ??? */
+
+/* Status bits */
+#define MCD_ST_DOOROPEN 0x80
+#define MCD_ST_DSKIN 0x40
+#define MCD_ST_DSKCHNG 0x20
+#define MCD_ST_BUSY 0x04
+#define MCD_ST_AUDIOBSY 0x02
+
+/* commands known by the controller */
+#define MCD_CMDRESET 0x00
+#define MCD_CMDGETVOLINFO 0x10 /* gets mcd_volinfo */
+#define MCD_CMDGETQCHN 0x20 /* gets mcd_qchninfo */
+#define MCD_CMDGETSTAT 0x40 /* gets a byte of status */
+#define MCD_CMDSETMODE 0x50 /* set transmission mode, needs byte */
+#define MCD_MD_RAW 0x60
+#define MCD_MD_COOKED 0x01
+#define MCD_MD_TOC 0x05
+#define MCD_CMDSTOPAUDIO 0x70
+#define MCD_CMDGETVOLUME 0x8E /* gets mcd_volume */
+#define MCD_CMDSETVOLUME 0xAE /* sets mcd_volume */
+#define MCD_CMDREAD1 0xB0 /* read n sectors */
+#define MCD_CMDREAD2 0xC0 /* read from-to */
+#define MCD_CMDCONTINFO 0xDC /* Get controller info */
+#define MCD_CMDEJECTDISK 0xF6
+#define MCD_CMDCLOSETRAY 0xF8
+#define MCD_CMDLOCKDRV 0xFE /* needs byte */
+#define MCD_LK_UNLOCK 0x00
+#define MCD_LK_LOCK 0x01
+#define MCD_LK_TEST 0x02
+
+struct mcd_volinfo {
+ bcd_t trk_low;
+ bcd_t trk_high;
+ bcd_t vol_msf[3];
+ bcd_t trk1_msf[3];
+};
+
+struct mcd_qchninfo {
+ u_char ctrl_adr;
+ u_char trk_no;
+ u_char idx_no;
+ bcd_t trk_size_msf[3];
+ u_char :8;
+ bcd_t hd_pos_msf[3];
+};
+
+struct mcd_volume {
+ u_char v0l;
+ u_char v0rs;
+ u_char v0r;
+ u_char v0ls;
+};
+
+struct mcd_read1 {
+ bcd_t start_msf[3];
+ u_char nsec[3];
+};
+
+struct mcd_read2 {
+ bcd_t start_msf[3];
+ bcd_t end_msf[3];
+};
+#endif /* MCD_H */
diff --git a/sys/i386/isa/mse.c b/sys/i386/isa/mse.c
new file mode 100644
index 0000000..466e936
--- /dev/null
+++ b/sys/i386/isa/mse.c
@@ -0,0 +1,499 @@
+/*
+ * Copyright 1992 by the University of Guelph
+ *
+ * Permission to use, copy and modify this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation.
+ * University of Guelph makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+/*
+ * Driver for the Logitech and ATI Inport Bus mice for use with 386bsd and
+ * the X386 port, courtesy of
+ * Rick Macklem, rick@snowhite.cis.uoguelph.ca
+ * Caveats: The driver currently uses spltty(), but doesn't use any
+ * generic tty code. It could use splmse() (that only masks off the
+ * bus mouse interrupt, but that would require hacking in i386/isa/icu.s.
+ * (This may be worth the effort, since the Logitech generates 30/60
+ * interrupts/sec continuously while it is open.)
+ * NB: The ATI has NOT been tested yet!
+ */
+
+/*
+ * Modification history:
+ *
+ * Oct 19, 1992 -- E. Stark (stark@cs.sunysb.edu)
+ * fixes to make it work with Microsoft InPort busmouse
+ *
+ * Jan, 1993 -- E. Stark (stark@cs.sunysb.edu)
+ * added patches for new "select" interface
+ *
+ * May 4, 1993 -- E. Stark (stark@cs.sunysb.edu)
+ * changed position of some spl()'s in mseread
+ *
+ * October 8, 1993 -- E. Stark (stark@cs.sunysb.edu)
+ * limit maximum negative x/y value to -127 to work around XFree problem
+ * that causes spurious button pushes.
+ */
+
+#include "mse.h"
+#if NMSE > 0
+#include "param.h"
+#include "proc.h"
+#include "user.h"
+#include "buf.h"
+#include "systm.h"
+#include "kernel.h"
+#include "ioctl.h"
+#include "tty.h"
+#include "uio.h"
+
+#include "i386/isa/isa_device.h"
+#include "i386/isa/icu.h"
+
+static int mseprobe(struct isa_device *);
+static int mseattach(struct isa_device *);
+void mseintr(int);
+
+struct isa_driver msedriver = {
+ mseprobe, mseattach, "mse"
+};
+
+/*
+ * Software control structure for mouse. The sc_enablemouse(),
+ * sc_disablemouse() and sc_getmouse() routines must be called spl'd().
+ */
+#define PROTOBYTES 5
+struct mse_softc {
+ int sc_flags;
+ int sc_mousetype;
+ struct selinfo sc_selp;
+ u_int sc_port;
+ void (*sc_enablemouse)();
+ void (*sc_disablemouse)();
+ void (*sc_getmouse)();
+ int sc_deltax;
+ int sc_deltay;
+ int sc_obuttons;
+ int sc_buttons;
+ int sc_bytesread;
+ u_char sc_bytes[PROTOBYTES];
+} mse_sc[NMSE];
+
+/* Flags */
+#define MSESC_OPEN 0x1
+#define MSESC_WANT 0x2
+
+/* and Mouse Types */
+#define MSE_LOGITECH 0x1
+#define MSE_ATIINPORT 0x2
+
+#define MSE_PORTA 0
+#define MSE_PORTB 1
+#define MSE_PORTC 2
+#define MSE_PORTD 3
+
+#define MSE_UNIT(dev) (minor(dev) >> 1)
+#define MSE_NBLOCKIO(dev) (minor(dev) & 0x1)
+
+/*
+ * Logitech bus mouse definitions
+ */
+#define MSE_SETUP 0x91 /* What does this mean? */
+#define MSE_HOLD 0x80
+#define MSE_RXLOW 0x00
+#define MSE_RXHIGH 0x20
+#define MSE_RYLOW 0x40
+#define MSE_RYHIGH 0x60
+#define MSE_DISINTR 0x10
+#define MSE_INTREN 0x00
+
+static int mse_probelogi();
+static void mse_enablelogi(), mse_disablelogi(), mse_getlogi();
+
+/*
+ * ATI Inport mouse definitions
+ */
+#define MSE_INPORT_RESET 0x80
+#define MSE_INPORT_STATUS 0x00
+#define MSE_INPORT_DX 0x01
+#define MSE_INPORT_DY 0x02
+#define MSE_INPORT_MODE 0x07
+#define MSE_INPORT_HOLD 0x20
+#define MSE_INPORT_INTREN 0x09
+
+static int mse_probeati();
+static void mse_enableati(), mse_disableati(), mse_getati();
+
+#define MSEPRI (PZERO + 3)
+
+/*
+ * Table of mouse types.
+ * Keep the Logitech last, since I haven't figured out how to probe it
+ * properly yet. (Someday I'll have the documentation.)
+ */
+struct mse_types {
+ int m_type; /* Type of bus mouse */
+ int (*m_probe)(); /* Probe routine to test for it */
+ void (*m_enable)(); /* Start routine */
+ void (*m_disable)(); /* Disable interrupts routine */
+ void (*m_get)(); /* and get mouse status */
+} mse_types[] = {
+ { MSE_ATIINPORT, mse_probeati, mse_enableati, mse_disableati, mse_getati },
+ { MSE_LOGITECH, mse_probelogi, mse_enablelogi, mse_disablelogi, mse_getlogi },
+ { 0, },
+};
+
+int
+mseprobe(idp)
+ register struct isa_device *idp;
+{
+ register struct mse_softc *sc = &mse_sc[idp->id_unit];
+ register int i;
+
+ /*
+ * Check for each mouse type in the table.
+ */
+ i = 0;
+ while (mse_types[i].m_type) {
+ if ((*mse_types[i].m_probe)(idp)) {
+ sc->sc_mousetype = mse_types[i].m_type;
+ sc->sc_enablemouse = mse_types[i].m_enable;
+ sc->sc_disablemouse = mse_types[i].m_disable;
+ sc->sc_getmouse = mse_types[i].m_get;
+ return (1);
+ }
+ i++;
+ }
+ return (0);
+}
+
+int
+mseattach(idp)
+ struct isa_device *idp;
+{
+ struct mse_softc *sc = &mse_sc[idp->id_unit];
+
+ sc->sc_port = idp->id_iobase;
+ return (1);
+}
+
+/*
+ * Exclusive open the mouse, initialize it and enable interrupts.
+ */
+int
+mseopen(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ register struct mse_softc *sc;
+ int s;
+
+ if (MSE_UNIT(dev) >= NMSE)
+ return (ENXIO);
+ sc = &mse_sc[MSE_UNIT(dev)];
+ if (sc->sc_flags & MSESC_OPEN)
+ return (EBUSY);
+ sc->sc_flags |= MSESC_OPEN;
+ sc->sc_obuttons = sc->sc_buttons = 0x7;
+ sc->sc_deltax = sc->sc_deltay = 0;
+ sc->sc_bytesread = PROTOBYTES;
+
+ /*
+ * Initialize mouse interface and enable interrupts.
+ */
+ s = spltty();
+ (*sc->sc_enablemouse)(sc->sc_port);
+ splx(s);
+ return (0);
+}
+
+/*
+ * mseclose: just turn off mouse innterrupts.
+ */
+int
+mseclose(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
+ int s;
+
+ s = spltty();
+ (*sc->sc_disablemouse)(sc->sc_port);
+ sc->sc_flags &= ~MSESC_OPEN;
+ splx(s);
+ return(0);
+}
+
+/*
+ * mseread: return mouse info using the MSC serial protocol, but without
+ * using bytes 4 and 5.
+ * (Yes this is cheesy, but it makes the X386 server happy, so...)
+ */
+int
+mseread(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
+ int xfer, s, error;
+
+ /*
+ * If there are no protocol bytes to be read, set up a new protocol
+ * packet.
+ */
+ s = spltty(); /* XXX Should be its own spl, but where is imlXX() */
+ if (sc->sc_bytesread >= PROTOBYTES) {
+ while (sc->sc_deltax == 0 && sc->sc_deltay == 0 &&
+ (sc->sc_obuttons ^ sc->sc_buttons) == 0) {
+ if (MSE_NBLOCKIO(dev)) {
+ splx(s);
+ return (0);
+ }
+ sc->sc_flags |= MSESC_WANT;
+ if (error = tsleep((caddr_t)sc, MSEPRI | PCATCH,
+ "mseread", 0)) {
+ splx(s);
+ return (error);
+ }
+ }
+
+ /*
+ * Generate protocol bytes.
+ * For some reason X386 expects 5 bytes but never uses
+ * the fourth or fifth?
+ */
+ sc->sc_bytes[0] = 0x80 | (sc->sc_buttons & ~0xf8);
+ if (sc->sc_deltax > 127)
+ sc->sc_deltax = 127;
+ if (sc->sc_deltax < -127)
+ sc->sc_deltax = -127;
+ sc->sc_deltay = -sc->sc_deltay; /* Otherwise mousey goes wrong way */
+ if (sc->sc_deltay > 127)
+ sc->sc_deltay = 127;
+ if (sc->sc_deltay < -127)
+ sc->sc_deltay = -127;
+ sc->sc_bytes[1] = sc->sc_deltax;
+ sc->sc_bytes[2] = sc->sc_deltay;
+ sc->sc_bytes[3] = sc->sc_bytes[4] = 0;
+ sc->sc_obuttons = sc->sc_buttons;
+ sc->sc_deltax = sc->sc_deltay = 0;
+ sc->sc_bytesread = 0;
+ }
+ splx(s);
+ xfer = min(uio->uio_resid, PROTOBYTES - sc->sc_bytesread);
+ if (error = uiomove(&sc->sc_bytes[sc->sc_bytesread], xfer, uio))
+ return (error);
+ sc->sc_bytesread += xfer;
+ return(0);
+}
+
+/*
+ * mseselect: check for mouse input to be processed.
+ */
+int
+mseselect(dev, rw, p)
+ dev_t dev;
+ int rw;
+ struct proc *p;
+{
+ register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
+ int s;
+
+ s = spltty();
+ if (sc->sc_bytesread != PROTOBYTES || sc->sc_deltax != 0 ||
+ sc->sc_deltay != 0 || (sc->sc_obuttons ^ sc->sc_buttons) != 0) {
+ splx(s);
+ return (1);
+ }
+
+ /*
+ * Since this is an exclusive open device, any previous proc.
+ * pointer is trash now, so we can just assign it.
+ */
+ selrecord(p, &sc->sc_selp);
+ splx(s);
+ return (0);
+}
+
+/*
+ * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative.
+ */
+void
+mseintr(unit)
+ int unit;
+{
+ register struct mse_softc *sc = &mse_sc[unit];
+ pid_t p;
+
+#ifdef DEBUG
+ static int mse_intrcnt = 0;
+ if((mse_intrcnt++ % 10000) == 0)
+ printf("mseintr\n");
+#endif /* DEBUG */
+ if ((sc->sc_flags & MSESC_OPEN) == 0)
+ return;
+
+ (*sc->sc_getmouse)(sc->sc_port, &sc->sc_deltax, &sc->sc_deltay, &sc->sc_buttons);
+
+ /*
+ * If mouse state has changed, wake up anyone wanting to know.
+ */
+ if (sc->sc_deltax != 0 || sc->sc_deltay != 0 ||
+ (sc->sc_obuttons ^ sc->sc_buttons) != 0) {
+ if (sc->sc_flags & MSESC_WANT) {
+ sc->sc_flags &= ~MSESC_WANT;
+ wakeup((caddr_t)sc);
+ }
+ selwakeup(&sc->sc_selp);
+ }
+}
+
+/*
+ * Routines for the Logitech mouse.
+ */
+/*
+ * Test for a Logitech bus mouse and return 1 if it is.
+ * (until I know how to use the signature port properly, just disable
+ * interrupts and return 1)
+ */
+static int
+mse_probelogi(idp)
+ register struct isa_device *idp;
+{
+
+ outb(idp->id_iobase + MSE_PORTB, 0x55);
+ if (inb(idp->id_iobase + MSE_PORTB) == 0x55) {
+ outb(idp->id_iobase + MSE_PORTB, 0xaa);
+ if (inb(idp->id_iobase + MSE_PORTB) == 0xaa)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Initialize Logitech mouse and enable interrupts.
+ */
+static void
+mse_enablelogi(port)
+ register u_int port;
+{
+ int dx, dy, but;
+
+ outb(port + MSE_PORTD, MSE_SETUP);
+ mse_getlogi(port, &dx, &dy, &but);
+}
+
+/*
+ * Disable interrupts for Logitech mouse.
+ */
+static void
+mse_disablelogi(port)
+ register u_int port;
+{
+
+ outb(port + MSE_PORTC, MSE_DISINTR);
+}
+
+/*
+ * Get the current dx, dy and button up/down state.
+ */
+static void
+mse_getlogi(port, dx, dy, but)
+ register u_int port;
+ int *dx;
+ int *dy;
+ int *but;
+{
+ register char x, y;
+
+ outb(port + MSE_PORTC, MSE_HOLD | MSE_RXLOW);
+ x = inb(port + MSE_PORTA);
+ *but = (x >> 5) & 0x7;
+ x &= 0xf;
+ outb(port + MSE_PORTC, MSE_HOLD | MSE_RXHIGH);
+ x |= (inb(port + MSE_PORTA) << 4);
+ outb(port + MSE_PORTC, MSE_HOLD | MSE_RYLOW);
+ y = (inb(port + MSE_PORTA) & 0xf);
+ outb(port + MSE_PORTC, MSE_HOLD | MSE_RYHIGH);
+ y |= (inb(port + MSE_PORTA) << 4);
+ *dx += x;
+ *dy += y;
+ outb(port + MSE_PORTC, MSE_INTREN);
+}
+
+/*
+ * Routines for the ATI Inport bus mouse.
+ */
+/*
+ * Test for a ATI Inport bus mouse and return 1 if it is.
+ * (do not enable interrupts)
+ */
+static int
+mse_probeati(idp)
+ register struct isa_device *idp;
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ if (inb(idp->id_iobase + MSE_PORTC) == 0xde)
+ return (1);
+ return (0);
+}
+
+/*
+ * Initialize ATI Inport mouse and enable interrupts.
+ */
+static void
+mse_enableati(port)
+ register u_int port;
+{
+
+ outb(port + MSE_PORTA, MSE_INPORT_RESET);
+ outb(port + MSE_PORTA, MSE_INPORT_MODE);
+ outb(port + MSE_PORTB, MSE_INPORT_INTREN);
+}
+
+/*
+ * Disable interrupts for ATI Inport mouse.
+ */
+static void
+mse_disableati(port)
+ register u_int port;
+{
+
+ outb(port + MSE_PORTA, MSE_INPORT_MODE);
+ outb(port + MSE_PORTB, 0);
+}
+
+/*
+ * Get current dx, dy and up/down button state.
+ */
+static void
+mse_getati(port, dx, dy, but)
+ register u_int port;
+ int *dx;
+ int *dy;
+ int *but;
+{
+ register char byte;
+
+ outb(port + MSE_PORTA, MSE_INPORT_MODE);
+ outb(port + MSE_PORTB, MSE_INPORT_HOLD);
+ outb(port + MSE_PORTA, MSE_INPORT_STATUS);
+ *but = ~(inb(port + MSE_PORTB) & 0x7);
+ outb(port + MSE_PORTA, MSE_INPORT_DX);
+ byte = inb(port + MSE_PORTB);
+ *dx += byte;
+ outb(port + MSE_PORTA, MSE_INPORT_DY);
+ byte = inb(port + MSE_PORTB);
+ *dy += byte;
+ outb(port + MSE_PORTA, MSE_INPORT_MODE);
+ outb(port + MSE_PORTB, MSE_INPORT_INTREN);
+}
+#endif /* NMSE */
diff --git a/sys/i386/isa/npx.c b/sys/i386/isa/npx.c
new file mode 100644
index 0000000..a3ce1e2
--- /dev/null
+++ b/sys/i386/isa/npx.c
@@ -0,0 +1,554 @@
+/*-
+ * Copyright (c) 1990 William Jolitz.
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)npx.c 7.2 (Berkeley) 5/12/91
+ * $Id: npx.c,v 1.6 1994/01/03 07:55:43 davidg Exp $
+ */
+
+#include "npx.h"
+#if NNPX > 0
+
+#include "param.h"
+#include "systm.h"
+#include "conf.h"
+#include "file.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+#include "machine/trap.h"
+#include "ioctl.h"
+#include "machine/specialreg.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/isa.h"
+
+/*
+ * 387 and 287 Numeric Coprocessor Extension (NPX) Driver.
+ */
+
+#ifdef __GNUC__
+
+#define disable_intr() __asm("cli")
+#define enable_intr() __asm("sti")
+#define fldcw(addr) __asm("fldcw %0" : : "m" (*addr))
+#define fnclex() __asm("fnclex")
+#define fninit() __asm("fninit")
+#define fnsave(addr) __asm("fnsave %0" : "=m" (*addr) : "0" (*addr))
+#define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr) : "0" (*addr))
+#define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr) : "0" (*addr))
+#define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fwait")
+#define frstor(addr) __asm("frstor %0" : : "m" (*addr))
+#define fwait() __asm("fwait")
+#define read_eflags() ({u_long ef; \
+ __asm("pushf; popl %0" : "=a" (ef)); \
+ ef; })
+#define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \
+ : : "n" (CR0_TS) : "ax")
+#define stop_emulating() __asm("clts")
+#define write_eflags(ef) __asm("pushl %0; popf" : : "a" ((u_long) ef))
+
+#else /* not __GNUC__ */
+
+void disable_intr __P((void));
+void enable_intr __P((void));
+void fldcw __P((caddr_t addr));
+void fnclex __P((void));
+void fninit __P((void));
+void fnsave __P((caddr_t addr));
+void fnstcw __P((caddr_t addr));
+void fnstsw __P((caddr_t addr));
+void fp_divide_by_0 __P((void));
+void frstor __P((caddr_t addr));
+void fwait __P((void));
+u_long read_eflags __P((void));
+void start_emulating __P((void));
+void stop_emulating __P((void));
+void write_eflags __P((u_long ef));
+
+#endif /* __GNUC__ */
+
+typedef u_char bool_t;
+
+extern struct gate_descriptor idt[];
+
+int npxdna __P((void));
+void npxexit __P((struct proc *p));
+void npxinit __P((u_int control));
+void npxintr __P((struct intrframe frame));
+void npxsave __P((struct save87 *addr));
+static int npxattach __P((struct isa_device *dvp));
+static int npxprobe __P((struct isa_device *dvp));
+static int npxprobe1 __P((struct isa_device *dvp));
+
+struct isa_driver npxdriver = {
+ npxprobe, npxattach, "npx",
+};
+
+u_int npx0_imask;
+struct proc *npxproc;
+
+static bool_t npx_ex16;
+static bool_t npx_exists;
+static struct gate_descriptor npx_idt_probeintr;
+static int npx_intrno;
+static volatile u_int npx_intrs_while_probing;
+static bool_t npx_irq13;
+static volatile u_int npx_traps_while_probing;
+
+/*
+ * Special interrupt handlers. Someday intr0-intr15 will be used to count
+ * interrupts. We'll still need a special exception 16 handler. The busy
+ * latch stuff in probintr() can be moved to npxprobe().
+ */
+void probeintr(void);
+asm
+("
+ .text
+_probeintr:
+ ss
+ incl _npx_intrs_while_probing
+ pushl %eax
+ movb $0x20,%al # EOI (asm in strings loses cpp features)
+ outb %al,$0xa0 # IO_ICU2
+ outb %al,$0x20 #IO_ICU1
+ movb $0,%al
+ outb %al,$0xf0 # clear BUSY# latch
+ popl %eax
+ iret
+");
+
+void probetrap(void);
+asm
+("
+ .text
+_probetrap:
+ ss
+ incl _npx_traps_while_probing
+ fnclex
+ iret
+");
+
+/*
+ * Probe routine. Initialize cr0 to give correct behaviour for [f]wait
+ * whether the device exists or not (XXX should be elsewhere). Set flags
+ * to tell npxattach() what to do. Modify device struct if npx doesn't
+ * need to use interrupts. Return 1 if device exists.
+ */
+static int
+npxprobe(dvp)
+ struct isa_device *dvp;
+{
+ int result;
+ u_long save_eflags;
+ u_char save_icu1_mask;
+ u_char save_icu2_mask;
+ struct gate_descriptor save_idt_npxintr;
+ struct gate_descriptor save_idt_npxtrap;
+ /*
+ * This routine is now just a wrapper for npxprobe1(), to install
+ * special npx interrupt and trap handlers, to enable npx interrupts
+ * and to disable other interrupts. Someday isa_configure() will
+ * install suitable handlers and run with interrupts enabled so we
+ * won't need to do so much here.
+ */
+ npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1;
+ save_eflags = read_eflags();
+ disable_intr();
+ save_icu1_mask = inb(IO_ICU1 + 1);
+ save_icu2_mask = inb(IO_ICU2 + 1);
+ save_idt_npxintr = idt[npx_intrno];
+ save_idt_npxtrap = idt[16];
+ outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq));
+ outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8));
+ setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL);
+ setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL);
+ npx_idt_probeintr = idt[npx_intrno];
+ enable_intr();
+ result = npxprobe1(dvp);
+ disable_intr();
+ outb(IO_ICU1 + 1, save_icu1_mask);
+ outb(IO_ICU2 + 1, save_icu2_mask);
+ idt[npx_intrno] = save_idt_npxintr;
+ idt[16] = save_idt_npxtrap;
+ write_eflags(save_eflags);
+ return (result);
+}
+
+static int
+npxprobe1(dvp)
+ struct isa_device *dvp;
+{
+ int control;
+ int status;
+#ifdef lint
+ npxintr();
+#endif
+ /*
+ * Partially reset the coprocessor, if any. Some BIOS's don't reset
+ * it after a warm boot.
+ */
+ outb(0xf1, 0); /* full reset on some systems, NOP on others */
+ outb(0xf0, 0); /* clear BUSY# latch */
+ /*
+ * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT
+ * instructions. We must set the CR0_MP bit and use the CR0_TS
+ * bit to control the trap, because setting the CR0_EM bit does
+ * not cause WAIT instructions to trap. It's important to trap
+ * WAIT instructions - otherwise the "wait" variants of no-wait
+ * control instructions would degenerate to the "no-wait" variants
+ * after FP context switches but work correctly otherwise. It's
+ * particularly important to trap WAITs when there is no NPX -
+ * otherwise the "wait" variants would always degenerate.
+ *
+ * Try setting CR0_NE to get correct error reporting on 486DX's.
+ * Setting it should fail or do nothing on lesser processors.
+ */
+ load_cr0(rcr0() | CR0_MP | CR0_NE);
+ /*
+ * But don't trap while we're probing.
+ */
+ stop_emulating();
+ /*
+ * Finish resetting the coprocessor, if any. If there is an error
+ * pending, then we may get a bogus IRQ13, but probeintr() will handle
+ * it OK. Bogus halts have never been observed, but we enabled
+ * IRQ13 and cleared the BUSY# latch early to handle them anyway.
+ */
+ fninit();
+ DELAY(1000); /* wait for any IRQ13 (fwait might hang) */
+#ifdef DIAGNOSTIC
+ if (npx_intrs_while_probing != 0)
+ printf("fninit caused %u bogus npx interrupt(s)\n",
+ npx_intrs_while_probing);
+ if (npx_traps_while_probing != 0)
+ printf("fninit caused %u bogus npx trap(s)\n",
+ npx_traps_while_probing);
+#endif
+ /*
+ * Check for a status of mostly zero.
+ */
+ status = 0x5a5a;
+ fnstsw(&status);
+ if ((status & 0xb8ff) == 0) {
+ /*
+ * Good, now check for a proper control word.
+ */
+ control = 0x5a5a;
+ fnstcw(&control);
+ if ((control & 0x1f3f) == 0x033f) {
+ npx_exists = 1;
+ /*
+ * We have an npx, now divide by 0 to see if exception
+ * 16 works.
+ */
+ control &= ~(1 << 2); /* enable divide by 0 trap */
+ fldcw(&control);
+ npx_traps_while_probing = npx_intrs_while_probing = 0;
+ fp_divide_by_0();
+ if (npx_traps_while_probing != 0) {
+ /*
+ * Good, exception 16 works.
+ */
+ npx_ex16 = 1;
+ dvp->id_irq = 0; /* zap the interrupt */
+ /*
+ * special return value to flag that we do not
+ * actually use any I/O registers
+ */
+ return (-1);
+ }
+ if (npx_intrs_while_probing != 0) {
+ /*
+ * Bad, we are stuck with IRQ13.
+ */
+ npx_irq13 = 1;
+ npx0_imask = dvp->id_irq; /* npxattach too late */
+ return (IO_NPXSIZE);
+ }
+ /*
+ * Worse, even IRQ13 is broken. Use emulator.
+ */
+ }
+ }
+ /*
+ * Probe failed, but we want to get to npxattach to initialize the
+ * emulator and say that it has been installed. XXX handle devices
+ * that aren't really devices better.
+ */
+ dvp->id_irq = 0;
+ /*
+ * special return value to flag that we do not
+ * actually use any I/O registers
+ */
+ return (-1);
+}
+
+/*
+ * Attach routine - announce which it is, and wire into system
+ */
+int
+npxattach(dvp)
+ struct isa_device *dvp;
+{
+ if (!npx_ex16 && !npx_irq13) {
+ if (npx_exists) {
+ printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit);
+ npx_exists = 0;
+ } else {
+ printf("npx%d: 387 Emulator\n",dvp->id_unit);
+ }
+ }
+ npxinit(__INITIAL_NPXCW__);
+ return (1); /* XXX unused */
+}
+
+/*
+ * Initialize floating point unit.
+ */
+void
+npxinit(control)
+ u_int control;
+{
+ struct save87 dummy;
+
+ if (!npx_exists)
+ return;
+ /*
+ * fninit has the same h/w bugs as fnsave. Use the detoxified
+ * fnsave to throw away any junk in the fpu. fnsave initializes
+ * the fpu and sets npxproc = NULL as important side effects.
+ */
+ npxsave(&dummy);
+ stop_emulating();
+ fldcw(&control);
+ if (curpcb != NULL)
+ fnsave(&curpcb->pcb_savefpu);
+ start_emulating();
+}
+
+/*
+ * Free coprocessor (if we have it).
+ */
+void
+npxexit(p)
+ struct proc *p;
+{
+
+ if (p == npxproc) {
+ start_emulating();
+ npxproc = NULL;
+ }
+}
+
+/*
+ * Record the FPU state and reinitialize it all except for the control word.
+ * Then generate a SIGFPE.
+ *
+ * Reinitializing the state allows naive SIGFPE handlers to longjmp without
+ * doing any fixups.
+ *
+ * XXX there is currently no way to pass the full error state to signal
+ * handlers, and if this is a nested interrupt there is no way to pass even
+ * a status code! So there is no way to have a non-naive SIGFPE handler. At
+ * best a handler could do an fninit followed by an fldcw of a static value.
+ * fnclex would be of little use because it would leave junk on the FPU stack.
+ * Returning from the handler would be even less safe than usual because
+ * IRQ13 exception handling makes exceptions even less precise than usual.
+ */
+void
+npxintr(frame)
+ struct intrframe frame;
+{
+ int code;
+
+ if (npxproc == NULL || !npx_exists) {
+ /* XXX no %p in stand/printf.c. Cast to quiet gcc -Wall. */
+ printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n",
+ (u_long) npxproc, (u_long) curproc, npx_exists);
+ panic("npxintr from nowhere");
+ }
+ if (npxproc != curproc) {
+ printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n",
+ (u_long) npxproc, (u_long) curproc, npx_exists);
+ panic("npxintr from non-current process");
+ }
+ /*
+ * Save state. This does an implied fninit. It had better not halt
+ * the cpu or we'll hang.
+ */
+ outb(0xf0, 0);
+ fnsave(&curpcb->pcb_savefpu);
+ fwait();
+ /*
+ * Restore control word (was clobbered by fnsave).
+ */
+ fldcw(&curpcb->pcb_savefpu.sv_env.en_cw);
+ fwait();
+ /*
+ * Remember the exception status word and tag word. The current
+ * (almost fninit'ed) fpu state is in the fpu and the exception
+ * state just saved will soon be junk. However, the implied fninit
+ * doesn't change the error pointers or register contents, and we
+ * preserved the control word and will copy the status and tag
+ * words, so the complete exception state can be recovered.
+ */
+ curpcb->pcb_savefpu.sv_ex_sw = curpcb->pcb_savefpu.sv_env.en_sw;
+ curpcb->pcb_savefpu.sv_ex_tw = curpcb->pcb_savefpu.sv_env.en_tw;
+
+ /*
+ * Pass exception to process.
+ */
+ if (ISPL(frame.if_cs) == SEL_UPL) {
+ /*
+ * Interrupt is essentially a trap, so we can afford to call
+ * the SIGFPE handler (if any) as soon as the interrupt
+ * returns.
+ *
+ * XXX little or nothing is gained from this, and plenty is
+ * lost - the interrupt frame has to contain the trap frame
+ * (this is otherwise only necessary for the rescheduling trap
+ * in doreti, and the frame for that could easily be set up
+ * just before it is used).
+ */
+ curproc->p_md.md_regs = (int *)&frame.if_es;
+#ifdef notyet
+ /*
+ * Encode the appropriate code for detailed information on
+ * this exception.
+ */
+ code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw);
+#else
+ code = 0; /* XXX */
+#endif
+ trapsignal(curproc, SIGFPE, code);
+ } else {
+ /*
+ * Nested interrupt. These losers occur when:
+ * o an IRQ13 is bogusly generated at a bogus time, e.g.:
+ * o immediately after an fnsave or frstor of an
+ * error state.
+ * o a couple of 386 instructions after
+ * "fstpl _memvar" causes a stack overflow.
+ * These are especially nasty when combined with a
+ * trace trap.
+ * o an IRQ13 occurs at the same time as another higher-
+ * priority interrupt.
+ *
+ * Treat them like a true async interrupt.
+ */
+ psignal(npxproc, SIGFPE);
+ }
+}
+
+/*
+ * Implement device not available (DNA) exception
+ *
+ * It would be better to switch FP context here (only). This would require
+ * saving the state in the proc table instead of in the pcb.
+ */
+int
+npxdna()
+{
+ if (!npx_exists)
+ return (0);
+ if (npxproc != NULL) {
+ printf("npxdna: npxproc = %lx, curproc = %lx\n",
+ (u_long) npxproc, (u_long) curproc);
+ panic("npxdna");
+ }
+ stop_emulating();
+ /*
+ * Record new context early in case frstor causes an IRQ13.
+ */
+ npxproc = curproc;
+ /*
+ * The following frstor may cause an IRQ13 when the state being
+ * restored has a pending error. The error will appear to have been
+ * triggered by the current (npx) user instruction even when that
+ * instruction is a no-wait instruction that should not trigger an
+ * error (e.g., fnclex). On at least one 486 system all of the
+ * no-wait instructions are broken the same as frstor, so our
+ * treatment does not amplify the breakage. On at least one
+ * 386/Cyrix 387 system, fnclex works correctly while frstor and
+ * fnsave are broken, so our treatment breaks fnclex if it is the
+ * first FPU instruction after a context switch.
+ */
+ frstor(&curpcb->pcb_savefpu);
+
+ return (1);
+}
+
+/*
+ * Wrapper for fnsave instruction to handle h/w bugs. If there is an error
+ * pending, then fnsave generates a bogus IRQ13 on some systems. Force
+ * any IRQ13 to be handled immediately, and then ignore it. This routine is
+ * often called at splhigh so it must not use many system services. In
+ * particular, it's much easier to install a special handler than to
+ * guarantee that it's safe to use npxintr() and its supporting code.
+ */
+void
+npxsave(addr)
+ struct save87 *addr;
+{
+ u_char icu1_mask;
+ u_char icu2_mask;
+ u_char old_icu1_mask;
+ u_char old_icu2_mask;
+ struct gate_descriptor save_idt_npxintr;
+
+ disable_intr();
+ old_icu1_mask = inb(IO_ICU1 + 1);
+ old_icu2_mask = inb(IO_ICU2 + 1);
+ save_idt_npxintr = idt[npx_intrno];
+ outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask));
+ outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8));
+ idt[npx_intrno] = npx_idt_probeintr;
+ enable_intr();
+ stop_emulating();
+ fnsave(addr);
+ fwait();
+ start_emulating();
+ npxproc = NULL;
+ disable_intr();
+ icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */
+ icu2_mask = inb(IO_ICU2 + 1);
+ outb(IO_ICU1 + 1,
+ (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask));
+ outb(IO_ICU2 + 1,
+ (icu2_mask & ~(npx0_imask >> 8))
+ | (old_icu2_mask & (npx0_imask >> 8)));
+ idt[npx_intrno] = save_idt_npxintr;
+ enable_intr(); /* back to usual state */
+}
+
+#endif /* NNPX > 0 */
diff --git a/sys/i386/isa/pcaudio.c b/sys/i386/isa/pcaudio.c
new file mode 100644
index 0000000..32995b3
--- /dev/null
+++ b/sys/i386/isa/pcaudio.c
@@ -0,0 +1,403 @@
+/*-
+ * Copyright (c) 1994 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#include "param.h"
+#include "uio.h"
+#include "ioctl.h"
+#include "sound/ulaw.h"
+#include "machine/cpufunc.h"
+#include "machine/pio.h"
+#include "machine/pcaudioio.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/timerreg.h"
+
+#include "pca.h"
+#if NPCA > 0
+
+#define BUF_SIZE 8192
+#define SAMPLE_RATE 8000
+#define INTERRUPT_RATE 16000
+
+static struct pca_status {
+ char open; /* device open */
+ char queries; /* did others try opening */
+ unsigned char *buf[2]; /* double buffering */
+ unsigned char *buffer; /* current buffer ptr */
+ unsigned in_use[2]; /* buffers fill */
+ unsigned index; /* index in current buffer */
+ unsigned counter; /* sample counter */
+ unsigned scale; /* sample counter scale */
+ unsigned sample_rate; /* sample rate */
+ unsigned processed; /* samples processed */
+ unsigned volume; /* volume for pc-speaker */
+ char encoding; /* Ulaw, Alaw or linear */
+ char current; /* current buffer */
+ unsigned char oldval; /* old timer port value */
+ char timer_on; /* is playback running */
+} pca_status;
+
+static char buffer1[BUF_SIZE];
+static char buffer2[BUF_SIZE];
+static char volume_table[256];
+
+static int pca_sleep = 0;
+static int pca_initialized = 0;
+
+void pcaintr(int regs);
+int pcaprobe(struct isa_device *dvp);
+int pcaattach(struct isa_device *dvp);
+int pcaclose(dev_t dev, int flag);
+int pcaopen(dev_t dev, int flag);
+int pcawrite(dev_t dev, struct uio *uio, int flag);
+int pcaioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p);
+
+struct isa_driver pcadriver = {
+ pcaprobe, pcaattach, "pca",
+};
+
+
+inline void conv(const void *table, void *buff, unsigned long n)
+{
+ __asm__("1:\tmovb (%2), %3\n"
+ "\txlatb\n"
+ "\tmovb %3, (%2)\n"
+ "\tinc %2\n"
+ "\tdec %1\n"
+ "\tjnz 1b\n"
+ :
+ :"b" ((long)table), "c" (n), "D" ((long)buff), "a" ((char)n)
+ :"bx","cx","di","ax");
+}
+
+
+static void
+pca_volume(int volume)
+{
+ int i, j;
+
+ for (i=0; i<256; i++) {
+ j = ((i-128)*volume)/100;
+ if (j<-128)
+ j = -128;
+ if (j>127)
+ j = 127;
+ volume_table[i] = (((255-(j + 128))/4)+1);
+ }
+}
+
+
+static void
+pca_init()
+{
+ pca_status.open = 0;
+ pca_status.queries = 0;
+ pca_status.timer_on = 0;
+ pca_status.buf[0] = (unsigned char *)&buffer1[0];
+ pca_status.buf[1] = (unsigned char *)&buffer2[0];
+ pca_status.buffer = pca_status.buf[0];
+ pca_status.in_use[0] = pca_status.in_use[1] = 0;
+ pca_status.current = 0;
+ pca_status.sample_rate = SAMPLE_RATE;
+ pca_status.scale = (pca_status.sample_rate << 8) / INTERRUPT_RATE;
+ pca_status.encoding = AUDIO_ENCODING_ULAW;
+ pca_status.volume = 100;
+
+ pca_volume(pca_status.volume);
+}
+
+
+static int
+pca_start(void)
+{
+ /* use the first buffer */
+ pca_status.current = 0;
+ pca_status.index = 0;
+ pca_status.counter = 0;
+ pca_status.buffer = pca_status.buf[pca_status.current];
+ pca_status.oldval = inb(IO_PPI) | 0x03;
+ /* acquire the timers */
+ if (acquire_timer2(TIMER_LSB|TIMER_ONESHOT)) {
+ return -1;
+ }
+ if (acquire_timer0(INTERRUPT_RATE, pcaintr)) {
+ release_timer2();
+ return -1;
+ }
+ pca_status.timer_on = 1;
+ return 0;
+}
+
+
+static void
+pca_stop(void)
+{
+ /* release the timers */
+ release_timer0();
+ release_timer2();
+ /* reset the buffer */
+ pca_status.in_use[0] = pca_status.in_use[1] = 0;
+ pca_status.index = 0;
+ pca_status.counter = 0;
+ pca_status.current = 0;
+ pca_status.buffer = pca_status.buf[pca_status.current];
+ pca_status.timer_on = 0;
+}
+
+
+static void
+pca_pause()
+{
+ release_timer0();
+ release_timer2();
+ pca_status.timer_on = 0;
+}
+
+
+static void
+pca_continue()
+{
+ pca_status.oldval = inb(IO_PPI) | 0x03;
+ acquire_timer2(TIMER_LSB|TIMER_ONESHOT);
+ acquire_timer0(INTERRUPT_RATE, pcaintr);
+ pca_status.timer_on = 1;
+}
+
+
+static void
+pca_wait(void)
+{
+ while (pca_status.in_use[0] || pca_status.in_use[1]) {
+ pca_sleep = 1;
+ tsleep((caddr_t)&pca_sleep, PZERO|PCATCH, "pca_drain", 0);
+ }
+}
+
+
+int
+pcaprobe(struct isa_device *dvp)
+{
+ return(-1);
+}
+
+
+int
+pcaattach(struct isa_device *dvp)
+{
+ printf(" PCM audio driver\n", dvp->id_unit);
+ pca_init();
+ return 1;
+}
+
+
+int
+pcaopen(dev_t dev, int flag)
+{
+ /* audioctl device can always be opened */
+ if (minor(dev) == 128)
+ return 0;
+ if (minor(dev) > 0)
+ return ENXIO;
+
+ if (!pca_initialized) {
+ pca_init();
+ pca_initialized = 1;
+ }
+
+ /* audio device can only be open by one process */
+ if (pca_status.open) {
+ pca_status.queries = 1;
+ return EBUSY;
+ }
+ pca_status.buffer = pca_status.buf[0];
+ pca_status.in_use[0] = pca_status.in_use[1] = 0;
+ pca_status.timer_on = 0;
+ pca_status.open = 1;
+ pca_status.processed = 0;
+ return 0;
+}
+
+
+int
+pcaclose(dev_t dev, int flag)
+{
+ /* audioctl device can always be closed */
+ if (minor(dev) == 128)
+ return 0;
+ if (minor(dev) > 0)
+ return ENXIO;
+ /* audio device close drains all output and restores timers */
+ pca_wait();
+ pca_stop();
+ pca_status.open = 0;
+ return 0;
+}
+
+
+int
+pcawrite(dev_t dev, struct uio *uio, int flag)
+{
+ int count, which;
+
+ /* only audio device can be written */
+ if (minor(dev) > 0)
+ return ENXIO;
+
+ while ((count = min(BUF_SIZE, uio->uio_resid)) > 0) {
+ which = pca_status.in_use[0] ? 1 : 0;
+ if (count && !pca_status.in_use[which]) {
+ uiomove(pca_status.buf[which], count, uio);
+ pca_status.processed += count;
+ switch (pca_status.encoding) {
+ case AUDIO_ENCODING_ULAW:
+ conv(ulaw_dsp, pca_status.buf[which], count);
+ break;
+
+ case AUDIO_ENCODING_ALAW:
+ break;
+
+ case AUDIO_ENCODING_RAW:
+ break;
+ }
+ pca_status.in_use[which] = count;
+ if (!pca_status.timer_on)
+ if (pca_start())
+ return EBUSY;
+ }
+ if (pca_status.in_use[0] && pca_status.in_use[1]) {
+ pca_sleep = 1;
+ tsleep((caddr_t)&pca_sleep, PZERO|PCATCH, "pca_wait",0);
+ }
+ }
+ return 0;
+}
+
+
+int
+pcaioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
+{
+audio_info_t *auptr;
+
+ switch(cmd) {
+
+ case AUDIO_GETINFO:
+ auptr = (audio_info_t *)data;
+ auptr->play.sample_rate = pca_status.sample_rate;
+ auptr->play.channels = 1;
+ auptr->play.precision = 8;
+ auptr->play.encoding = pca_status.encoding;
+
+ auptr->play.gain = pca_status.volume;
+ auptr->play.port = 0;
+
+ auptr->play.samples = pca_status.processed;
+ auptr->play.eof = 0;
+ auptr->play.pause = !pca_status.timer_on;
+ auptr->play.error = 0;
+ auptr->play.waiting = pca_status.queries;
+
+ auptr->play.open = pca_status.open;
+ auptr->play.active = pca_status.timer_on;
+ return 0;
+
+ case AUDIO_SETINFO:
+ auptr = (audio_info_t *)data;
+ if (auptr->play.sample_rate != (unsigned int)~0) {
+ pca_status.sample_rate = auptr->play.sample_rate;
+ pca_status.scale =
+ (pca_status.sample_rate << 8) / INTERRUPT_RATE;
+ }
+ if (auptr->play.encoding != (unsigned int)~0) {
+ pca_status.encoding = auptr->play.encoding;
+ }
+ if (auptr->play.gain != (unsigned int)~0) {
+ pca_status.volume = auptr->play.gain;
+ pca_volume(pca_status.volume);
+ }
+ if (auptr->play.pause != (unsigned char)~0) {
+ if (auptr->play.pause)
+ pca_pause();
+ else
+ pca_continue();
+ }
+
+ return 0;
+
+ case AUDIO_DRAIN:
+ pca_wait();
+ return 0;
+
+ case AUDIO_FLUSH:
+ pca_stop();
+ return 0;
+
+ }
+ return ENXIO;
+}
+
+
+void
+pcaintr(int regs)
+{
+ if (pca_status.index < pca_status.in_use[pca_status.current]) {
+#if 1
+ disable_intr();
+ __asm__("outb %0,$0x61\n"
+ "andb $0xFE,%0\n"
+ "outb %0,$0x61"
+ : : "a" ((char)pca_status.oldval) );
+ __asm__("xlatb\n"
+ "outb %0,$0x42"
+ : : "a" ((char)pca_status.buffer[pca_status.index]),
+ "b" ((long)volume_table) );
+ enable_intr();
+#else
+ disable_intr();
+ outb(IO_PPI, pca_status.oldval);
+ outb(IO_PPI, pca_status.oldval & 0xFE);
+ outb(TIMER_CNTR2,
+ volume_table[pca_status.buffer[pca_status.index]]);
+ enable_intr();
+#endif
+ pca_status.counter += pca_status.scale;
+ pca_status.index = (pca_status.counter >> 8);
+ }
+ else {
+ pca_status.index = pca_status.counter = 0;
+ pca_status.in_use[pca_status.current] = 0;
+ pca_status.current ^= 1;
+ pca_status.buffer = pca_status.buf[pca_status.current];
+ if (pca_sleep) {
+ wakeup((caddr_t)&pca_sleep);
+ pca_sleep = 0;
+ }
+ }
+}
+
+#endif
diff --git a/sys/i386/isa/rtc.h b/sys/i386/isa/rtc.h
new file mode 100644
index 0000000..ba008b6
--- /dev/null
+++ b/sys/i386/isa/rtc.h
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)rtc.h 7.1 (Berkeley) 5/12/91
+ * $Id: rtc.h,v 1.3 1993/11/07 17:44:34 wollman Exp $
+ */
+
+#ifndef _I386_ISA_RTC_H_
+#define _I386_ISA_RTC_H_ 1
+
+/*
+ * RTC Register locations
+ */
+
+#define RTC_SEC 0x00 /* seconds */
+#define RTC_SECALRM 0x01 /* seconds alarm */
+#define RTC_MIN 0x02 /* minutes */
+#define RTC_MINALRM 0x03 /* minutes alarm */
+#define RTC_HRS 0x04 /* hours */
+#define RTC_HRSALRM 0x05 /* hours alarm */
+#define RTC_WDAY 0x06 /* week day */
+#define RTC_DAY 0x07 /* day of month */
+#define RTC_MONTH 0x08 /* month of year */
+#define RTC_YEAR 0x09 /* month of year */
+#define RTC_STATUSA 0x0a /* status register A */
+#define RTCSA_TUP 0x80 /* time update, don't look now */
+
+#define RTC_STATUSB 0x0b /* status register B */
+
+#define RTC_INTR 0x0c /* status register C (R) interrupt source */
+#define RTCIR_UPDATE 0x10 /* update intr */
+#define RTCIR_ALARM 0x20 /* alarm intr */
+#define RTCIR_PERIOD 0x40 /* periodic intr */
+#define RTCIR_INT 0x80 /* interrupt output signal */
+
+#define RTC_STATUSD 0x0d /* status register D (R) Lost Power */
+#define RTCSD_PWR 0x80 /* clock lost power */
+
+#define RTC_DIAG 0x0e /* status register E - bios diagnostic */
+#define RTCDG_BITS "\020\010clock_battery\007ROM_cksum\006config_unit\005memory_size\004fixed_disk\003invalid_time"
+
+#define RTC_RESET 0x0f /* status register F - reset code byte */
+#define RTCRS_RST 0x00 /* normal reset */
+#define RTCRS_LOAD 0x04 /* load system */
+
+#define RTC_FDISKETTE 0x10 /* diskette drive type in upper/lower nibble */
+#define RTCFDT_NONE 0 /* none present */
+#define RTCFDT_360K 0x10 /* 360K */
+#define RTCFDT_12M 0x20 /* 1.2M */
+#define RTCFDT_720K 0x30 /* 720K */
+#define RTCFDT_144M 0x40 /* 1.44M */
+
+#define RTC_BASELO 0x15 /* low byte of basemem size */
+#define RTC_BASEHI 0x16 /* high byte of basemem size */
+#define RTC_EXTLO 0x17 /* low byte of extended mem size */
+#define RTC_EXTHI 0x18 /* low byte of extended mem size */
+
+#define RTC_CENTURY 0x32 /* current century - please increment in Dec99*/
+#endif /* _I386_ISA_RTC_H_ */
diff --git a/sys/i386/isa/sio.c b/sys/i386/isa/sio.c
new file mode 100644
index 0000000..dd4ee55
--- /dev/null
+++ b/sys/i386/isa/sio.c
@@ -0,0 +1,1920 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)com.c 7.5 (Berkeley) 5/16/91
+ * $Id: sio.c,v 1.44 1994/04/03 12:25:57 ache Exp $
+ */
+
+#include "sio.h"
+#if NSIO > 0
+#define DONT_MALLOC_TTYS
+/*
+ * Serial driver, based on 386BSD-0.1 com driver.
+ * Mostly rewritten to use pseudo-DMA.
+ * Works for National Semiconductor NS8250-NS16550AF UARTs.
+ * COM driver, based on HP dca driver.
+ */
+#include "param.h"
+#include "systm.h"
+#include "ioctl.h"
+#include "tty.h"
+#include "proc.h"
+#include "user.h"
+#include "conf.h"
+#include "file.h"
+#include "uio.h"
+#include "kernel.h"
+#include "syslog.h"
+
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/sioreg.h"
+#include "i386/isa/ic/ns16550.h"
+
+#define FAKE_DCD(unit) ((unit) == comconsole)
+#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */
+#define RBSZ 1024
+#define RB_I_HIGH_WATER (RBSZ - 2 * RS_IBUFSIZE)
+#define RB_I_LOW_WATER ((RBSZ - 2 * RS_IBUFSIZE) * 7 / 8)
+#define RS_IBUFSIZE 256
+#define RS_OBUFSIZE 256
+#define TTY_BI TTY_FE /* XXX */
+#define TTY_OE TTY_PE /* XXX */
+
+#ifdef COM_BIDIR
+#define CALLOUT(x) (minor(x) & COM_CALLOUT_MASK)
+#define COM_CALLOUT_MASK 0x80
+#define COM_MINOR_MAGIC_MASK 0x80
+#else /* COM_BIDIR */
+#define COM_MINOR_MAGIC_MASK 0
+#endif /* COM_BIDIR */
+
+#define UNIT(x) (minor(x) & ~COM_MINOR_MAGIC_MASK)
+
+#ifdef COM_MULTIPORT
+/* checks in flags for multiport and which is multiport "master chip"
+ * for a given card
+ */
+#define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01)
+#define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff)
+#define COM_NOMASTER(dev) ((dev)->id_flags & 0x04)
+#endif /* COM_MULTIPORT */
+
+#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02)
+
+#ifndef FIFO_TRIGGER
+/*
+ * This driver is fast enough to work with any value and for high values
+ * to be only slightly more efficient. Low values may be better because
+ * they give lower latency.
+ * TODO: always use low values for low speeds. Mouse movements are jerky
+ * if more than one packet arrives at once. The low speeds used for
+ * serial mice help avoid this, but not if (large) fifos are enabled.
+ */
+#define FIFO_TRIGGER FIFO_TRIGGER_14
+#endif
+
+#define com_scr 7 /* scratch register for 16450-16550 (R/W) */
+
+#ifndef setsofttty
+#define OLD_INTERRUPT_HANDLING /* XXX FreeBSD-1.1 and earlier */
+#define setsofttty() (ipending |= 1 << 4) /* XXX requires owning IRQ4 */
+extern u_int ipending; /* XXX */
+void softsio1 __P((void));
+#endif
+
+/*
+ * Input buffer watermarks.
+ * The external device is asked to stop sending when the buffer exactly reaches
+ * high water, or when the high level requests it.
+ * The high level is notified immediately (rather than at a later clock tick)
+ * when this watermark is reached.
+ * The buffer size is chosen so the watermark should almost never be reached.
+ * The low watermark is invisibly 0 since the buffer is always emptied all at
+ * once.
+ */
+#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)
+
+/*
+ * com state bits.
+ * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
+ * than the other bits so that they can be tested as a group without masking
+ * off the low bits.
+ *
+ * The following com and tty flags correspond closely:
+ * TS_BUSY = CS_BUSY (maintained by comstart() and comflush())
+ * CS_TTGO = ~TS_TTSTOP (maintained by comstart() and siostop())
+ * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam())
+ * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam())
+ * TS_FLUSH is not used.
+ * Bug: I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON.
+ */
+#define CS_BUSY 0x80 /* output in progress */
+#define CS_TTGO 0x40 /* output not stopped by XOFF */
+#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */
+#define CS_CHECKMSR 1 /* check of MSR scheduled */
+#define CS_CTS_OFLOW 2 /* use CTS output flow control */
+#define CS_ODONE 4 /* output completed */
+#define CS_RTS_IFLOW 8 /* use RTS input flow control */
+
+static char *error_desc[] = {
+#define CE_OVERRUN 0
+ "silo overflow",
+#define CE_INTERRUPT_BUF_OVERFLOW 1
+ "interrupt-level buffer overflow",
+#define CE_TTY_BUF_OVERFLOW 2
+ "tty-level buffer overflow",
+};
+
+#define CE_NTYPES 3
+#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum])
+
+/* types. XXX - should be elsewhere */
+typedef u_int Port_t; /* hardware port */
+typedef u_char bool_t; /* boolean */
+
+/* com device structure */
+struct com_s {
+ u_char state; /* miscellaneous flag bits */
+ u_char cfcr_image; /* copy of value written to CFCR */
+ bool_t hasfifo; /* nonzero for 16550 UARTs */
+ u_char mcr_image; /* copy of value written to MCR */
+#ifdef COM_BIDIR
+ bool_t bidir; /* is this unit bidirectional? */
+ bool_t active; /* is the port active _at all_? */
+ bool_t active_in; /* is the incoming port in use? */
+ bool_t active_out; /* is the outgoing port in use? */
+#endif /* COM_BIDIR */
+#ifdef COM_MULTIPORT
+ bool_t multiport; /* is this unit part of a multiport device? */
+#endif /* COM_MULTIPORT */
+ int dtr_wait; /* time to hold DTR down on close (* 1/HZ) */
+ u_int tx_fifo_size;
+
+ /*
+ * The high level of the driver never reads status registers directly
+ * because there would be too many side effects to handle conveniently.
+ * Instead, it reads copies of the registers stored here by the
+ * interrupt handler.
+ */
+ u_char last_modem_status; /* last MSR read by intr handler */
+ u_char prev_modem_status; /* last MSR handled by high level */
+
+ u_char *ibuf; /* start of input buffer */
+ u_char *ibufend; /* end of input buffer */
+ u_char *ihighwater; /* threshold in input buffer */
+ u_char *iptr; /* next free spot in input buffer */
+
+ u_char *obufend; /* end of output buffer */
+ int ocount; /* original count for current output */
+ u_char *optr; /* next char to output */
+
+ Port_t data_port; /* i/o ports */
+ Port_t int_id_port;
+ Port_t iobase;
+ Port_t modem_ctl_port;
+ Port_t line_status_port;
+ Port_t modem_status_port;
+
+ struct tty *tp; /* cross reference */
+
+#ifdef TIOCTIMESTAMP
+ bool_t do_timestamp;
+ struct timeval timestamp;
+#endif
+
+ u_long bytes_in; /* statistics */
+ u_long bytes_out;
+ u_int delta_error_counts[CE_NTYPES];
+ u_int error_counts[CE_NTYPES];
+
+ /*
+ * Ping-pong input buffers. The extra factor of 2 in the sizes is
+ * to allow for an error byte for each input byte.
+ */
+#define CE_INPUT_OFFSET RS_IBUFSIZE
+ u_char ibuf1[2 * RS_IBUFSIZE];
+ u_char ibuf2[2 * RS_IBUFSIZE];
+ u_char obuf[RS_OBUFSIZE];
+};
+
+/*
+ * The public functions in the com module ought to be declared in a com-driver
+ * system header.
+ */
+
+/* Interrupt handling entry points. */
+void siointr __P((int unit));
+void siopoll __P((void));
+
+/* Device switch entry points. */
+int sioopen __P((dev_t dev, int oflags, int devtype,
+ struct proc *p));
+int sioclose __P((dev_t dev, int fflag, int devtype,
+ struct proc *p));
+int sioread __P((dev_t dev, struct uio *uio, int ioflag));
+int siowrite __P((dev_t dev, struct uio *uio, int ioflag));
+int sioioctl __P((dev_t dev, int cmd, caddr_t data,
+ int fflag, struct proc *p));
+void siostop __P((struct tty *tp, int rw));
+#define sioreset noreset
+int sioselect __P((dev_t dev, int rw, struct proc *p));
+#define siommap nommap
+#define siostrategy nostrategy
+
+/* Console device entry points. */
+int siocngetc __P((dev_t dev));
+struct consdev;
+void siocninit __P((struct consdev *cp));
+void siocnprobe __P((struct consdev *cp));
+void siocnputc __P((dev_t dev, int c));
+
+static int sioattach __P((struct isa_device *dev));
+static void comflush __P((struct com_s *com));
+static void comhardclose __P((struct com_s *com));
+static void siointr1 __P((struct com_s *com));
+static void commctl __P((struct com_s *com, int bits, int how));
+static int comparam __P((struct tty *tp, struct termios *t));
+static int sioprobe __P((struct isa_device *dev));
+static void comstart __P((struct tty *tp));
+static void comwakeup __P((caddr_t chan, int ticks));
+static int tiocm_xxx2mcr __P((int tiocm_xxx));
+
+/* table and macro for fast conversion from a unit number to its com struct */
+static struct com_s *p_com_addr[NSIO];
+#define com_addr(unit) (p_com_addr[unit])
+
+static struct com_s com_structs[NSIO];
+
+#ifdef TIOCTIMESTAMP
+static struct timeval intr_timestamp;
+#endif
+
+struct isa_driver siodriver = {
+ sioprobe, sioattach, "sio"
+};
+
+#ifdef COMCONSOLE
+static int comconsole = COMCONSOLE;
+#else
+static int comconsole = -1;
+#endif
+static speed_t comdefaultrate = TTYDEF_SPEED;
+static u_int com_events; /* input chars + weighted output completions */
+static int commajor;
+#define TB_OUT(tp) (&(tp)->t_outq)
+#define TB_RAW(tp) (&(tp)->t_rawq)
+struct tty sio_tty[NSIO];
+extern struct tty *constty;
+extern int tk_nin; /* XXX */
+extern int tk_rawcc; /* XXX */
+
+#ifdef KGDB
+#include "machine/remote-sl.h"
+
+extern int kgdb_dev;
+extern int kgdb_rate;
+extern int kgdb_debug_init;
+#endif
+
+static struct speedtab comspeedtab[] = {
+ 0, 0,
+ 50, COMBRD(50),
+ 75, COMBRD(75),
+ 110, COMBRD(110),
+ 134, COMBRD(134),
+ 150, COMBRD(150),
+ 200, COMBRD(200),
+ 300, COMBRD(300),
+ 600, COMBRD(600),
+ 1200, COMBRD(1200),
+ 1800, COMBRD(1800),
+ 2400, COMBRD(2400),
+ 4800, COMBRD(4800),
+ 9600, COMBRD(9600),
+ 19200, COMBRD(19200),
+ 38400, COMBRD(38400),
+ 57600, COMBRD(57600),
+ 115200, COMBRD(115200),
+ -1, -1
+};
+
+/* XXX - configure this list */
+static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, };
+
+static int
+sioprobe(dev)
+ struct isa_device *dev;
+{
+ static bool_t already_init;
+ Port_t *com_ptr;
+ Port_t iobase;
+ int result;
+
+ if (!already_init) {
+ /*
+ * Turn off MCR_IENABLE for all likely serial ports. An unused
+ * port with its MCR_IENABLE gate open will inhibit interrupts
+ * from any used port that shares the interrupt vector.
+ */
+ for (com_ptr = likely_com_ports;
+ com_ptr < &likely_com_ports[sizeof likely_com_ports
+ / sizeof likely_com_ports[0]];
+ ++com_ptr)
+ outb(*com_ptr + com_mcr, 0);
+ already_init = TRUE;
+ }
+ iobase = dev->id_iobase;
+ result = IO_COMSIZE;
+
+ /*
+ * We don't want to get actual interrupts, just masked ones.
+ * Interrupts from this line should already be masked in the ICU,
+ * but mask them in the processor as well in case there are some
+ * (misconfigured) shared interrupts.
+ */
+ disable_intr();
+
+ /*
+ * Initialize the speed so that any junk in the THR or output fifo will
+ * be transmitted in a known time. (There may be lots of junk after a
+ * soft reboot, and output interrupts don't work right after a master
+ * reset, at least for 16550s. (The speed is undefined after MR, but
+ * MR empties the THR and the TSR so it's not clear why this matters)).
+ * Enable output interrupts (only) and check the following:
+ * o the CFCR, IER and MCR in UART hold the values written to them
+ * (the values happen to be all distinct - this is good for
+ * avoiding false positive tests from bus echoes).
+ * o an output interrupt is generated and its vector is correct.
+ * o the interrupt goes away when the IIR in the UART is read.
+ */
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ outb(iobase + com_dlbl, COMBRD(9600) & 0xff);
+ outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8);
+ outb(iobase + com_cfcr, CFCR_8BITS); /* ensure IER is addressed */
+ outb(iobase + com_mcr, MCR_IENABLE); /* open gate early */
+ outb(iobase + com_ier, 0); /* ensure edge on next intr */
+ outb(iobase + com_ier, IER_ETXRDY); /* generate interrupt */
+ DELAY((16 + 1) * 9600 / 10); /* enough to drain 16 bytes */
+ if ( inb(iobase + com_cfcr) != CFCR_8BITS
+ || inb(iobase + com_ier) != IER_ETXRDY
+ || inb(iobase + com_mcr) != MCR_IENABLE
+#ifndef COM_MULTIPORT /* XXX - need to do more to enable interrupts */
+ || !isa_irq_pending(dev)
+#endif
+ || (inb(iobase + com_iir) & IIR_IMASK) != IIR_TXRDY
+ || isa_irq_pending(dev)
+ || (inb(iobase + com_iir) & IIR_IMASK) != IIR_NOPEND)
+ result = 0;
+
+ /*
+ * Turn off all device interrupts and check that they go off properly.
+ * Leave MCR_IENABLE set. It gates the OUT2 output of the UART to
+ * the ICU input. Closing the gate would give a floating ICU input
+ * (unless there is another device driving at) and spurious interrupts.
+ * (On the system that this was first tested on, the input floats high
+ * and gives a (masked) interrupt as soon as the gate is closed.)
+ */
+ outb(iobase + com_ier, 0);
+ outb(iobase + com_mcr, MCR_IENABLE); /* dummy to avoid bus echo */
+ if ( inb(iobase + com_ier) != 0
+ || isa_irq_pending(dev)
+ || (inb(iobase + com_iir) & IIR_IMASK) != IIR_NOPEND)
+ result = 0;
+ if (result == 0)
+ outb(iobase + com_mcr, 0);
+
+ enable_intr();
+ return (result);
+}
+
+static int
+sioattach(isdp)
+ struct isa_device *isdp;
+{
+ struct com_s *com;
+ static bool_t comwakeup_started = FALSE;
+ Port_t iobase;
+ int s;
+ int unit;
+
+ iobase = isdp->id_iobase;
+ unit = isdp->id_unit;
+ s = spltty();
+
+ /*
+ * sioprobe() has initialized the device registers as follows:
+ * o cfcr = CFCR_8BITS.
+ * It is most important that CFCR_DLAB is off, so that the
+ * data port is not hidden when we enable interrupts.
+ * o ier = 0.
+ * Interrupts are only enabled when the line is open.
+ * o mcr = MCR_IENABLE.
+ * Keeping MCR_DTR and MCR_RTS off might stop the external
+ * device from sending before we are ready.
+ */
+
+ com = &com_structs[unit]; /* XXX malloc it */
+ com->cfcr_image = CFCR_8BITS;
+ com->mcr_image = MCR_IENABLE;
+ com->dtr_wait = 3 * hz;
+ com->tx_fifo_size = 1;
+ com->iptr = com->ibuf = com->ibuf1;
+ com->ibufend = com->ibuf1 + RS_IBUFSIZE;
+ com->ihighwater = com->ibuf1 + RS_IHIGHWATER;
+ com->iobase = iobase;
+ com->data_port = iobase + com_data;
+ com->int_id_port = iobase + com_iir;
+ com->modem_ctl_port = iobase + com_mcr;
+ com->line_status_port = iobase + com_lsr;
+ com->modem_status_port = iobase + com_msr;
+#ifdef DONT_MALLOC_TTYS
+ com->tp = &sio_tty[unit];
+#endif
+
+ /* attempt to determine UART type */
+ printf("sio%d: type", unit);
+#ifdef COM_MULTIPORT
+ if (!COM_ISMULTIPORT(isdp))
+#endif
+ {
+ u_char scr;
+ u_char scr1;
+ u_char scr2;
+
+ scr = inb(iobase + com_scr);
+ outb(iobase + com_scr, 0xa5);
+ scr1 = inb(iobase + com_scr);
+ outb(iobase + com_scr, 0x5a);
+ scr2 = inb(iobase + com_scr);
+ outb(iobase + com_scr, scr);
+ if (scr1 != 0xa5 || scr2 != 0x5a) {
+ printf(" 8250");
+ goto determined_type;
+ }
+ }
+ outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER_14);
+ DELAY(100);
+ switch (inb(com->int_id_port) & IIR_FIFO_MASK) {
+ case FIFO_TRIGGER_1:
+ printf(" 16450");
+ break;
+ case FIFO_TRIGGER_4:
+ printf(" 16450?");
+ break;
+ case FIFO_TRIGGER_8:
+ printf(" 16550?");
+ break;
+ case FIFO_TRIGGER_14:
+ printf(" 16550A");
+ if (COM_NOFIFO(isdp))
+ printf(" fifo disabled");
+ else {
+ com->hasfifo = TRUE;
+ com->tx_fifo_size = 16;
+ }
+ break;
+ }
+ outb(iobase + com_fifo, 0);
+determined_type: ;
+
+#ifdef COM_MULTIPORT
+ if (COM_ISMULTIPORT(isdp)) {
+ com->multiport = TRUE;
+ printf(" (multiport)");
+
+ /* Note: some cards have no master port (e.g., BocaBoards) */
+ if (!COM_NOMASTER(isdp)) {
+ struct isa_device *masterdev;
+
+ /* set the master's common-interrupt-enable reg.,
+ * as appropriate. YYY See your manual
+ */
+ /* enable only common interrupt for port */
+ outb(com->modem_ctl_port, com->mcr_image = 0);
+
+ masterdev = find_isadev(isa_devtab_tty, &siodriver,
+ COM_MPMASTER(isdp));
+ outb(masterdev->id_iobase + com_scr, 0x80);
+ }
+
+ } else
+ com->multiport = FALSE;
+#endif /* COM_MULTIPORT */
+ printf("\n");
+
+#ifdef KGDB
+ if (kgdb_dev == makedev(commajor, unit)) {
+ if (comconsole == unit)
+ kgdb_dev = -1; /* can't debug over console port */
+ else {
+ int divisor;
+
+ /*
+ * XXX now unfinished and broken. Need to do
+ * something more like a full open(). There's no
+ * suitable interrupt handler so don't enable device
+ * interrupts. Watch out for null tp's.
+ */
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ divisor = ttspeedtab(kgdb_rate, comspeedtab);
+ outb(iobase + com_dlbl, divisor & 0xFF);
+ outb(iobase + com_dlbh, (u_int) divisor >> 8);
+ outb(iobase + com_cfcr, CFCR_8BITS);
+ outb(com->modem_status_port,
+ com->mcr_image |= MCR_DTR | MCR_RTS);
+
+ if (kgdb_debug_init) {
+ /*
+ * Print prefix of device name,
+ * let kgdb_connect print the rest.
+ */
+ printf("sio%d: ", unit);
+ kgdb_connect(1);
+ } else
+ printf("sio%d: kgdb enabled\n", unit);
+ }
+ }
+#endif
+
+ com_addr(unit) = com;
+ splx(s);
+ if (!comwakeup_started) {
+ comwakeup((caddr_t) NULL, 0);
+ comwakeup_started = TRUE;
+ }
+ return (1);
+}
+
+/* ARGSUSED */
+int
+sioopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+#ifdef COM_BIDIR
+ bool_t callout;
+#endif /* COM_BIDIR */
+ struct com_s *com;
+ int error = 0;
+ bool_t got_status = FALSE;
+ Port_t iobase;
+ int s;
+ struct tty *tp;
+ int unit;
+
+ unit = UNIT(dev);
+ if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL)
+ return (ENXIO);
+#ifdef COM_BIDIR
+ /* if it's a callout device, and bidir not possible on that dev, die */
+ callout = CALLOUT(dev);
+ if (callout && !(com->bidir))
+ return (ENXIO);
+#endif /* COM_BIDIR */
+
+#ifdef DONT_MALLOC_TTYS
+ tp = com->tp;
+#else
+ sio_tty[unit] = ttymalloc(sio_tty[unit]);
+ tp = com->tp = sio_tty[unit];
+#endif
+ s = spltty();
+
+#ifdef COM_BIDIR
+
+bidir_open_top:
+ got_status = FALSE;
+ /* if it's bidirectional, we've gotta deal with it... */
+ if (com->bidir) {
+ if (callout) {
+ if (com->active_in) {
+ /* it's busy. die */
+ splx(s);
+ return (EBUSY);
+ } else {
+ /* it's ours. lock it down, and set it up */
+ com->active_out = TRUE;
+ }
+ } else {
+ if (com->active_out) {
+ /* it's busy, outgoing. wait, if possible */
+ if (flag & O_NONBLOCK) {
+ /* can't wait; bail */
+ splx(s);
+ return (EBUSY);
+ } else {
+ /* wait for it... */
+ error = tsleep((caddr_t)&com->active_out,
+ TTIPRI|PCATCH,
+ "siooth",
+ 0);
+ /* if there was an error, take off. */
+ if (error != 0) {
+ splx(s);
+ return (error);
+ }
+ /* else take it from the top */
+ goto bidir_open_top;
+ }
+ }
+ disable_intr();
+ com->prev_modem_status =
+ com->last_modem_status = inb(com->modem_status_port);
+ enable_intr();
+ got_status = TRUE;
+ if (com->prev_modem_status & MSR_DCD
+ || FAKE_DCD(unit)) {
+ /* there's a carrier on the line; we win */
+ com->active_in = TRUE;
+ } else {
+ /* there is no carrier on the line */
+ if (flag & O_NONBLOCK) {
+ /* can't wait; let it open */
+ com->active_in = TRUE;
+ } else {
+ /* put DTR & RTS up */
+ /* XXX - bring up RTS earlier? */
+ commctl(com, MCR_DTR | MCR_RTS, DMSET);
+ outb(com->iobase + com_ier, IER_EMSC);
+
+ /* wait for it... */
+ error = tsleep((caddr_t)&com->active_in,
+ TTIPRI|PCATCH,
+ "siodcd",
+ 0);
+
+ /* if not active, turn intrs and DTR off */
+ if (!com->active) {
+ outb(com->iobase + com_ier, 0);
+ commctl(com, MCR_DTR, DMBIC);
+ }
+
+ /* if there was an error, take off. */
+ if (error != 0) {
+ splx(s);
+ return (error);
+ }
+ /* else take it from the top */
+ goto bidir_open_top;
+ }
+ }
+ }
+ }
+
+ com->active = TRUE;
+#endif /* COM_BIDIR */
+
+ tp->t_oproc = comstart;
+ tp->t_param = comparam;
+ tp->t_dev = dev;
+ if (!(tp->t_state & TS_ISOPEN)) {
+ tp->t_state |= TS_WOPEN;
+ ttychars(tp);
+ if (tp->t_ispeed == 0) {
+ /*
+ * We don't use all the flags from <sys/ttydefaults.h>
+ * since those are only relevant for logins. It's
+ * important to have echo off initially so that the
+ * line doesn't start blathering before the echo flag
+ * can be turned off.
+ */
+ tp->t_iflag = 0;
+ tp->t_oflag = 0;
+ tp->t_cflag = CREAD | CS8;
+#ifdef COM_BIDIR
+ if (com->bidir && !callout)
+ tp->t_cflag |= HUPCL;
+#endif
+ tp->t_lflag = 0;
+ tp->t_ispeed = tp->t_ospeed = comdefaultrate;
+ if (unit == comconsole) {
+ tp->t_iflag = TTYDEF_IFLAG;
+ tp->t_oflag = TTYDEF_OFLAG;
+ tp->t_cflag = TTYDEF_CFLAG;
+ tp->t_lflag = TTYDEF_LFLAG;
+ }
+ }
+
+ /*
+ * XXX the full state after a first open() needs to be
+ * programmable and separate for callin and callout.
+ */
+#ifdef COM_BIDIR
+ if (com->bidir) {
+ if (callout)
+ tp->t_cflag |= CLOCAL;
+ else
+ tp->t_cflag &= ~CLOCAL;
+ }
+#endif
+
+ commctl(com, MCR_DTR | MCR_RTS, DMSET);
+ error = comparam(tp, &tp->t_termios);
+ if (error != 0)
+ goto out;
+ ttsetwater(tp);
+ iobase = com->iobase;
+ if (com->hasfifo) {
+ /* (re)enable and drain FIFO */
+ outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER
+ | FIFO_RCV_RST | FIFO_XMT_RST);
+ DELAY(100);
+ }
+ disable_intr();
+ (void) inb(com->line_status_port);
+ (void) inb(com->data_port);
+ if (!got_status)
+ com->prev_modem_status =
+ com->last_modem_status = inb(com->modem_status_port);
+ outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS
+ | IER_EMSC);
+ enable_intr();
+ if (com->prev_modem_status & MSR_DCD || FAKE_DCD(unit))
+ tp->t_state |= TS_CARR_ON;
+ } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
+ splx(s);
+ return (EBUSY);
+ }
+ while (!(flag & O_NONBLOCK) && !(tp->t_cflag & CLOCAL)
+#ifdef COM_BIDIR
+ /* We went through a lot of trouble to open it,
+ * but it's certain we have a carrier now, so
+ * don't spend any time on it now.
+ */
+ && !(com->bidir)
+#endif /* COM_BIDIR */
+ && !(tp->t_state & TS_CARR_ON)) {
+ tp->t_state |= TS_WOPEN;
+ error = ttysleep(tp, (caddr_t)TB_RAW(tp), TTIPRI | PCATCH,
+ ttopen, 0);
+ if (error != 0)
+ break;
+ }
+out:
+ if (error == 0)
+ error = (*linesw[tp->t_line].l_open)(dev, tp);
+ splx(s);
+
+#ifdef COM_BIDIR
+ /* wakeup sleepers */
+ wakeup((caddr_t) &com->active_in);
+#endif /* COM_BIDIR */
+
+ /*
+ * XXX - the next step was once not done, so interrupts, DTR and RTS
+ * remained hot if the process was killed while it was sleeping
+ * waiting for carrier. Now there is the opposite problem. If several
+ * processes are sleeping waiting for carrier on the same line and one
+ * is killed, interrupts are turned off so the other processes will
+ * never see the carrier rise.
+ */
+ if (error != 0 && !(tp->t_state & TS_ISOPEN))
+ comhardclose(com);
+ tp->t_state &= ~TS_WOPEN;
+
+ return (error);
+}
+
+/*ARGSUSED*/
+int
+sioclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+ struct com_s *com;
+ int s;
+ struct tty *tp;
+
+ com = com_addr(UNIT(dev));
+ tp = com->tp;
+ s = spltty();
+ (*linesw[tp->t_line].l_close)(tp, flag);
+ siostop(tp, FREAD | FWRITE);
+ comhardclose(com);
+ ttyclose(tp);
+ splx(s);
+ return (0);
+}
+
+static void
+comhardclose(com)
+ struct com_s *com;
+{
+ Port_t iobase;
+ int s;
+ struct tty *tp;
+ int unit;
+
+ unit = com - &com_structs[0];
+ iobase = com->iobase;
+ s = spltty();
+#ifdef TIOCTIMESTAMP
+ com->do_timestamp = 0;
+#endif
+ outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
+#ifdef KGDB
+ /* do not disable interrupts or hang up if debugging */
+ if (kgdb_dev != makedev(commajor, unit))
+#endif
+ {
+ outb(iobase + com_ier, 0);
+ tp = com->tp;
+ if (tp->t_cflag & HUPCL || tp->t_state & TS_WOPEN
+#ifdef COM_BIDIR
+ /*
+ * XXX we will miss any carrier drop between here and the
+ * next open. Perhaps we should watch DCD even when the
+ * port is closed; it is not sufficient to check it at
+ * the next open because it might go up and down while
+ * we're not watching. And we shouldn't look at DCD if
+ * CLOCAL is set (here or for the dialin device ...).
+ * When the termios state is reinitialized for initial
+ * opens, the correct CLOCAL bit will be
+ * ((the bit now) & (the initial bit)).
+ */
+ || com->active_in
+ && !(com->prev_modem_status & MSR_DCD) && !FAKE_DCD(unit)
+#endif
+ || !(tp->t_state & TS_ISOPEN)) {
+ commctl(com, MCR_RTS, DMSET);
+ if (com->dtr_wait != 0)
+ /*
+ * Uninterruptible sleep since we want to
+ * wait a fixed time.
+ * XXX - delay in open() (if necessary),
+ * not here (always).
+ */
+ tsleep((caddr_t)&com->dtr_wait, TTIPRI,
+ "sioclose", com->dtr_wait);
+ }
+ }
+
+#ifdef COM_BIDIR
+ com->active = com->active_in = com->active_out = FALSE;
+
+ /* wakeup sleepers who are waiting for out to finish */
+ wakeup((caddr_t) &com->active_out);
+#endif /* COM_BIDIR */
+
+ splx(s);
+}
+
+int
+sioread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ struct tty *tp = com_addr(UNIT(dev))->tp;
+
+ return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
+}
+
+int
+siowrite(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ int unit = UNIT(dev);
+ struct tty *tp = com_addr(unit)->tp;
+
+ /*
+ * (XXX) We disallow virtual consoles if the physical console is
+ * a serial port. This is in case there is a display attached that
+ * is not the console. In that situation we don't need/want the X
+ * server taking over the console.
+ */
+ if (constty && unit == comconsole)
+ constty = NULL;
+ return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
+}
+
+#ifdef TIOCTIMESTAMP
+/* Interrupt routine for timekeeping purposes */
+void
+siointrts(unit)
+ int unit;
+{
+ microtime(&intr_timestamp);
+ siointr(unit);
+}
+#endif
+
+void
+siointr(unit)
+ int unit;
+{
+#ifndef COM_MULTIPORT
+ siointr1(com_addr(unit));
+#else /* COM_MULTIPORT */
+ bool_t possibly_more_intrs;
+ struct com_s *com;
+
+ /*
+ * Loop until there is no activity on any port. This is necessary
+ * to get an interrupt edge more than to avoid another interrupt.
+ * If the IRQ signal is just an OR of the IRQ signals from several
+ * devices, then the edge from one may be lost because another is
+ * on.
+ */
+ do {
+ possibly_more_intrs = FALSE;
+ for (unit = 0; unit < NSIO; ++unit) {
+ com = com_addr(unit);
+ if (com != NULL
+ && (inb(com->int_id_port) & IIR_IMASK)
+ != IIR_NOPEND) {
+ siointr1(com);
+ possibly_more_intrs = TRUE;
+ }
+ }
+ } while (possibly_more_intrs);
+#endif /* COM_MULTIPORT */
+}
+
+static void
+siointr1(com)
+ struct com_s *com;
+{
+ u_char line_status;
+ u_char modem_status;
+ u_char *ioptr;
+ u_char recv_data;
+
+#ifdef TIOCTIMESTAMP
+ if (com->do_timestamp)
+ /* XXX a little bloat here... */
+ com->timestamp = intr_timestamp;
+#endif
+ while (TRUE) {
+ line_status = inb(com->line_status_port);
+
+ /* input event? (check first to help avoid overruns) */
+ while (line_status & LSR_RCV_MASK) {
+ /* break/unnattached error bits or real input? */
+ if (!(line_status & LSR_RXRDY))
+ recv_data = 0;
+ else
+ recv_data = inb(com->data_port);
+ ++com->bytes_in;
+ /* XXX reduce SLIP input latency */
+#define FRAME_END 0xc0
+ if (recv_data == FRAME_END)
+ setsofttty();
+#ifdef KGDB
+ /* trap into kgdb? (XXX - needs testing and optim) */
+ if (recv_data == FRAME_END
+ && !(com->tp->t_state & TS_ISOPEN)
+ && kgdb_dev == makedev(commajor, unit)) {
+ kgdb_connect(0);
+ continue;
+ }
+#endif /* KGDB */
+ ioptr = com->iptr;
+ if (ioptr >= com->ibufend)
+ CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW);
+ else {
+ ++com_events;
+#if 0 /* for testing input latency vs efficiency */
+if (com->iptr - com->ibuf == 8)
+ setsofttty();
+#endif
+ ioptr[0] = recv_data;
+ ioptr[CE_INPUT_OFFSET] = line_status;
+ com->iptr = ++ioptr;
+ if (ioptr == com->ihighwater
+ && com->state & CS_RTS_IFLOW)
+ outb(com->modem_ctl_port,
+ com->mcr_image &= ~MCR_RTS);
+ /* XXX - move this out of isr */
+ if (line_status & LSR_OE)
+ CE_RECORD(com, CE_OVERRUN);
+ }
+
+ /*
+ * "& 0x7F" is to avoid the gcc-1.40 generating a slow
+ * jump from the top of the loop to here
+ */
+ line_status = inb(com->line_status_port) & 0x7F;
+ }
+
+ /* modem status change? (always check before doing output) */
+ modem_status = inb(com->modem_status_port);
+ if (modem_status != com->last_modem_status) {
+ /*
+ * Schedule high level to handle DCD changes. Note
+ * that we don't use the delta bits anywhere. Some
+ * UARTs mess them up, and it's easy to remember the
+ * previous bits and calculate the delta.
+ */
+ com->last_modem_status = modem_status;
+ if (!(com->state & CS_CHECKMSR)) {
+ com_events += LOTS_OF_EVENTS;
+ com->state |= CS_CHECKMSR;
+ setsofttty();
+ }
+
+ /* handle CTS change immediately for crisp flow ctl */
+ if (com->state & CS_CTS_OFLOW) {
+ if (modem_status & MSR_CTS)
+ com->state |= CS_ODEVREADY;
+ else
+ com->state &= ~CS_ODEVREADY;
+ }
+ }
+
+ /* output queued and everything ready? */
+ if (line_status & LSR_TXRDY
+ && com->state >= (CS_ODEVREADY | CS_BUSY | CS_TTGO)) {
+ ioptr = com->optr;
+ if (com->tx_fifo_size > 1) {
+ u_int ocount;
+
+ ocount = com->obufend - ioptr;
+ if (ocount > com->tx_fifo_size)
+ ocount = com->tx_fifo_size;
+ com->bytes_out += ocount;
+ do
+ outb(com->data_port, *ioptr++);
+ while (--ocount != 0);
+ } else {
+ outb(com->data_port, *ioptr++);
+ ++com->bytes_out;
+ }
+ com->optr = ioptr;
+ if (ioptr >= com->obufend) {
+ /* output just completed */
+ com_events += LOTS_OF_EVENTS;
+ com->state ^= (CS_ODONE | CS_BUSY);
+ setsofttty(); /* handle at high level ASAP */
+ }
+ }
+
+ /* finished? */
+#ifndef COM_MULTIPORT
+ if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND)
+#endif /* COM_MULTIPORT */
+ return;
+ }
+}
+
+static int
+tiocm_xxx2mcr(tiocm_xxx)
+ int tiocm_xxx;
+{
+ int mcr;
+
+ mcr = 0;
+ if (tiocm_xxx & TIOCM_DTR)
+ mcr |= MCR_DTR;
+ if (tiocm_xxx & TIOCM_RTS)
+ mcr |= MCR_RTS;
+ return (mcr);
+}
+
+int
+sioioctl(dev, cmd, data, flag, p)
+ dev_t dev;
+ int cmd;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+{
+ struct com_s *com;
+ int error;
+ Port_t iobase;
+ int mcr;
+ int msr;
+ int s;
+ int tiocm_xxx;
+ struct tty *tp;
+
+ com = com_addr(UNIT(dev));
+ tp = com->tp;
+ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
+ if (error >= 0)
+ return (error);
+ error = ttioctl(tp, cmd, data, flag);
+
+#ifdef COM_BIDIR
+ /* XXX: plug security hole while sticky bits not yet implemented */
+ if (com->bidir && com->active_in && p->p_ucred->cr_uid != 0)
+ tp->t_cflag &= ~CLOCAL;
+#endif
+
+ if (error >= 0)
+ return (error);
+
+ iobase = com->iobase;
+ s = spltty();
+ switch (cmd) {
+ case TIOCSBRK:
+ outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK);
+ break;
+ case TIOCCBRK:
+ outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
+ break;
+ case TIOCSDTR:
+ commctl(com, MCR_DTR, DMBIS);
+ break;
+ case TIOCCDTR:
+ commctl(com, MCR_DTR, DMBIC);
+ break;
+ case TIOCMSET:
+ commctl(com, tiocm_xxx2mcr(*(int *)data), DMSET);
+ break;
+ case TIOCMBIS:
+ commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIS);
+ break;
+ case TIOCMBIC:
+ commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIC);
+ break;
+ case TIOCMGET:
+ tiocm_xxx = TIOCM_LE; /* XXX - always enabled while open */
+ mcr = com->mcr_image;
+ if (mcr & MCR_DTR)
+ tiocm_xxx |= TIOCM_DTR;
+ if (mcr & MCR_RTS)
+ tiocm_xxx |= TIOCM_RTS;
+ msr = com->prev_modem_status;
+ if (msr & MSR_CTS)
+ tiocm_xxx |= TIOCM_CTS;
+ if (msr & MSR_DCD)
+ tiocm_xxx |= TIOCM_CD;
+ if (msr & MSR_DSR)
+ tiocm_xxx |= TIOCM_DSR;
+ /*
+ * XXX - MSR_RI is naturally volatile, and we make MSR_TERI
+ * more volatile by reading the modem status a lot. Perhaps
+ * we should latch both bits until the status is read here.
+ */
+ if (msr & (MSR_RI | MSR_TERI))
+ tiocm_xxx |= TIOCM_RI;
+ *(int *)data = tiocm_xxx;
+ break;
+#ifdef COM_BIDIR
+ case TIOCMSBIDIR:
+ /* must be root to set bidir. capability */
+ error = suser(p->p_ucred, &p->p_acflag);
+ if (error != 0) {
+ splx(s);
+ return(EPERM);
+ }
+
+ /* if it's the console, can't do it (XXX why?) */
+ if (UNIT(dev) == comconsole) {
+ splx(s);
+ return(ENOTTY);
+ }
+
+#if 0
+ /* XXX - can't do the next, for obvious reasons...
+ * but there are problems to be looked at...
+ */
+ /* if the port is active, don't do it */
+ if (com->active) {
+ splx(s);
+ return(EBUSY);
+ }
+#endif
+
+ com->bidir = *(int *)data;
+ break;
+ case TIOCMGBIDIR:
+ *(int *)data = com->bidir;
+ break;
+#endif /* COM_BIDIR */
+#if 0
+ case TIOCMSDTRWAIT:
+ /* must be root since the wait applies to following logins */
+ error = suser(p->p_ucred, &p->p_acflag);
+ if (error != 0) {
+ splx(s);
+ return(EPERM);
+ }
+
+ /* if it's the console, can't do it (XXX why?) */
+ if (UNIT(dev) == comconsole) {
+ splx(s);
+ return(ENOTTY);
+ }
+ com->dtr_wait = *(int *)data;
+ break;
+ case TIOCMGDTRWAIT:
+ *(int *)data = com->dtr_wait;
+ break;
+#endif
+#ifdef TIOCTIMESTAMP
+ case TIOCTIMESTAMP:
+ com->do_timestamp = TRUE;
+ *(struct timeval *)data = com->timestamp;
+ break;
+#endif
+ default:
+ splx(s);
+ return (ENOTTY);
+ }
+ splx(s);
+ return (0);
+}
+
+/* cancel pending output */
+static void
+comflush(com)
+ struct com_s *com;
+{
+ struct clist *rbp;
+
+ disable_intr();
+ if (com->state & CS_ODONE)
+ com_events -= LOTS_OF_EVENTS;
+ com->state &= ~(CS_ODONE | CS_BUSY);
+ enable_intr();
+ while( getc( TB_OUT(com->tp)) != -1);
+ com->ocount = 0;
+ com->tp->t_state &= ~TS_BUSY;
+}
+
+void
+siopoll()
+{
+#ifdef OLD_INTERRUPT_HANDLING
+ static bool_t awake = FALSE;
+ int s;
+#endif
+ int unit;
+
+ if (com_events == 0)
+ return;
+
+#ifdef OLD_INTERRUPT_HANDLING
+ disable_intr();
+ if (awake) {
+ enable_intr();
+ return;
+ }
+ awake = TRUE;
+ enable_intr();
+ s = spltty();
+#endif
+
+repeat:
+ for (unit = 0; unit < NSIO; ++unit) {
+ u_char *buf;
+ struct com_s *com;
+ u_char *ibuf;
+ int incc;
+ struct tty *tp;
+
+ com = com_addr(unit);
+ if (com == NULL)
+ continue;
+ tp = com->tp;
+#ifdef DONT_MALLOC_TTYS
+ if (tp == NULL)
+ continue;
+#endif
+
+ /* switch the role of the low-level input buffers */
+ if (com->iptr == (ibuf = com->ibuf)) {
+ buf = NULL; /* not used, but compiler can't tell */
+ incc = 0;
+ } else {
+ buf = ibuf;
+ disable_intr();
+ incc = com->iptr - buf;
+ com_events -= incc;
+ if (ibuf == com->ibuf1)
+ ibuf = com->ibuf2;
+ else
+ ibuf = com->ibuf1;
+ com->ibufend = ibuf + RS_IBUFSIZE;
+ com->ihighwater = ibuf + RS_IHIGHWATER;
+ com->iptr = ibuf;
+
+ /*
+ * There is now room for another low-level buffer full
+ * of input, so enable RTS if it is now disabled and
+ * there is room in the high-level buffer.
+ */
+ /*
+ * XXX this used not to look at CS_RTS_IFLOW. The
+ * change is to allow full control of MCR_RTS via
+ * ioctls after turning CS_RTS_IFLOW off. Check
+ * for races. We shouldn't allow the ioctls while
+ * CS_RTS_IFLOW is on.
+ */
+ if ((com->state & CS_RTS_IFLOW)
+ && !(com->mcr_image & MCR_RTS) /*
+ && !(tp->t_state & TS_RTS_IFLOW) */)
+ outb(com->modem_ctl_port,
+ com->mcr_image |= MCR_RTS);
+ enable_intr();
+ com->ibuf = ibuf;
+ }
+
+ if (com->state & CS_CHECKMSR) {
+ u_char delta_modem_status;
+
+ disable_intr();
+ delta_modem_status = com->last_modem_status
+ ^ com->prev_modem_status;
+ com->prev_modem_status = com->last_modem_status;
+ com_events -= LOTS_OF_EVENTS;
+ com->state &= ~CS_CHECKMSR;
+ enable_intr();
+ if (delta_modem_status & MSR_DCD && !FAKE_DCD(unit)) {
+ if (com->prev_modem_status & MSR_DCD) {
+ (*linesw[tp->t_line].l_modem)(tp, 1);
+#ifdef COM_BIDIR
+ wakeup((caddr_t) &com->active_in);
+#endif /* COM_BIDIR */
+ } else
+ (*linesw[tp->t_line].l_modem)(tp, 0);
+ }
+ }
+
+ /* XXX */
+ if (TRUE) {
+ u_int delta;
+ int errnum;
+ u_long total;
+
+ for (errnum = 0; errnum < CE_NTYPES; ++errnum) {
+ disable_intr();
+ delta = com->delta_error_counts[errnum];
+ com->delta_error_counts[errnum] = 0;
+ enable_intr();
+ if (delta != 0) {
+ total =
+ com->error_counts[errnum] += delta;
+ log(LOG_WARNING,
+ "sio%d: %u more %s%s (total %lu)\n",
+ unit, delta, error_desc[errnum],
+ delta == 1 ? "" : "s", total);
+ }
+ }
+ }
+ if (com->state & CS_ODONE) {
+ comflush(com);
+ /* XXX - why isn't the table used for t_line == 0? */
+ if (tp->t_line != 0)
+ (*linesw[tp->t_line].l_start)(tp);
+ else
+ comstart(tp);
+ }
+ if (incc <= 0 || !(tp->t_state & TS_ISOPEN))
+ continue;
+ if (com->state & CS_RTS_IFLOW
+ && TB_RAW(tp)->c_cc + incc >= RB_I_HIGH_WATER /*
+ && !(tp->t_state & TS_RTS_IFLOW) */
+ /*
+ * XXX - need RTS flow control for all line disciplines.
+ * Only have it in standard one now.
+ */
+ && linesw[tp->t_line].l_rint == ttyinput) {
+/* tp->t_state |= TS_RTS_IFLOW; */
+ ttstart(tp);
+ }
+#if 0
+ /*
+ * Avoid the grotesquely inefficient lineswitch routine
+ * (ttyinput) in "raw" mode. It usually takes about 450
+ * instructions (that's without canonical processing or echo!).
+ * slinput is reasonably fast (usually 40 instructions plus
+ * call overhead).
+ */
+ if (!(tp->t_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP
+ | IXOFF | IXON))
+ && !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG
+ | PENDIN))
+ && !(tp->t_state & (TS_CNTTB | TS_LNCH))
+ && linesw[tp->t_line].l_rint == ttyinput) {
+ tk_nin += incc;
+ tk_rawcc += incc;
+ tp->t_rawcc += incc;
+ com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
+ += incc - rb_write(TB_RAW(tp), (char *) buf,
+ incc);
+ ttwakeup(tp);
+ if (tp->t_state & TS_TTSTOP
+ && (tp->t_iflag & IXANY
+ || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
+ tp->t_state &= ~TS_TTSTOP;
+ tp->t_lflag &= ~FLUSHO;
+ ttstart(tp);
+ }
+ } else {
+#endif
+ do {
+ u_char line_status;
+ int recv_data;
+
+ line_status = (u_char) buf[CE_INPUT_OFFSET];
+ recv_data = (u_char) *buf++;
+ if (line_status
+ & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) {
+ if (line_status & LSR_BI)
+ recv_data |= TTY_BI;
+ if (line_status & LSR_FE)
+ recv_data |= TTY_FE;
+ if (line_status & LSR_OE)
+ recv_data |= TTY_OE;
+ if (line_status & LSR_PE)
+ recv_data |= TTY_PE;
+ }
+ (*linesw[tp->t_line].l_rint)(recv_data, tp);
+ } while (--incc > 0);
+#if 0
+ }
+#endif
+ if (com_events == 0)
+ break;
+ }
+ if (com_events >= LOTS_OF_EVENTS)
+ goto repeat;
+
+#ifdef OLD_INTERRUPT_HANDLING
+ splx(s);
+ awake = FALSE;
+#endif
+}
+
+static int
+comparam(tp, t)
+ struct tty *tp;
+ struct termios *t;
+{
+ u_int cfcr;
+ int cflag;
+ struct com_s *com;
+ int divisor;
+ int error;
+ Port_t iobase;
+ int s;
+ int unit;
+
+ /* check requested parameters */
+ divisor = ttspeedtab(t->c_ospeed, comspeedtab);
+ if (t->c_ispeed == 0)
+ t->c_ispeed = t->c_ospeed;
+ if (divisor < 0 || t->c_ispeed != t->c_ospeed)
+ return (EINVAL);
+
+ /* parameters are OK, convert them to the com struct and the device */
+ unit = UNIT(tp->t_dev);
+ com = com_addr(unit);
+ iobase = com->iobase;
+ s = spltty();
+ if (divisor == 0)
+ commctl(com, MCR_DTR, DMBIC); /* hang up line */
+ else
+ commctl(com, MCR_DTR, DMBIS);
+ cflag = t->c_cflag;
+ switch (cflag & CSIZE) {
+ case CS5:
+ cfcr = CFCR_5BITS;
+ break;
+ case CS6:
+ cfcr = CFCR_6BITS;
+ break;
+ case CS7:
+ cfcr = CFCR_7BITS;
+ break;
+ default:
+ cfcr = CFCR_8BITS;
+ break;
+ }
+ if (cflag & PARENB) {
+ cfcr |= CFCR_PENAB;
+ if (!(cflag & PARODD))
+ cfcr |= CFCR_PEVEN;
+ }
+ if (cflag & CSTOPB)
+ cfcr |= CFCR_STOPB;
+
+ /*
+ * Some UARTs lock up if the divisor latch registers are selected
+ * while the UART is doing output (they refuse to transmit anything
+ * more until given a hard reset). Fix this by stopping filling
+ * the device buffers and waiting for them to drain. Reading the
+ * line status port outside of siointr1() might lose some receiver
+ * error bits, but that is acceptable here.
+ */
+ disable_intr();
+retry:
+ com->state &= ~CS_TTGO;
+ enable_intr();
+ while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY)) {
+ error = ttysleep(tp, (caddr_t)TB_RAW(tp), TTIPRI | PCATCH,
+ "sioparam", 1);
+ if (error != 0 && error != EAGAIN) {
+ if (!(tp->t_state & TS_TTSTOP)) {
+ disable_intr();
+ com->state |= CS_TTGO;
+ enable_intr();
+ }
+ splx(s);
+ return (error);
+ }
+ }
+
+ disable_intr(); /* very important while com_data is hidden */
+
+ /*
+ * XXX - clearing CS_TTGO is not sufficient to stop further output,
+ * because siopoll() calls comstart() which usually sets it again
+ * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be
+ * sufficient, for similar reasons.
+ */
+ if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY))
+ goto retry;
+
+ if (divisor != 0) {
+ outb(iobase + com_cfcr, cfcr | CFCR_DLAB);
+ outb(iobase + com_dlbl, divisor & 0xFF);
+ outb(iobase + com_dlbh, (u_int) divisor >> 8);
+ }
+ outb(iobase + com_cfcr, com->cfcr_image = cfcr);
+ if (!(tp->t_state & TS_TTSTOP))
+ com->state |= CS_TTGO;
+ if (cflag & CRTS_IFLOW)
+ com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */
+ else
+ com->state &= ~CS_RTS_IFLOW;
+
+ /*
+ * Set up state to handle output flow control.
+ * XXX - worth handling MDMBUF (DCD) flow control at the lowest level?
+ * Now has 16+ msec latency, while CTS flow has 50- usec latency.
+ */
+ com->state &= ~CS_CTS_OFLOW;
+ com->state |= CS_ODEVREADY;
+ if (cflag & CCTS_OFLOW) {
+ com->state |= CS_CTS_OFLOW;
+ if (!(com->last_modem_status & MSR_CTS))
+ com->state &= ~CS_ODEVREADY;
+ }
+
+ /*
+ * Recover from fiddling with CS_TTGO. We used to call siointr1()
+ * unconditionally, but that defeated the careful discarding of
+ * stale input in sioopen().
+ *
+ * XXX sioopen() is not careful waiting for carrier for the callout
+ * case.
+ */
+ if (com->state >= (CS_BUSY | CS_TTGO))
+ siointr1(com);
+
+ enable_intr();
+ splx(s);
+ return (0);
+}
+
+static void
+comstart(tp)
+ struct tty *tp;
+{
+ struct com_s *com;
+ int s;
+ int unit;
+
+ unit = UNIT(tp->t_dev);
+ com = com_addr(unit);
+ s = spltty();
+ disable_intr();
+ if (tp->t_state & TS_TTSTOP)
+ com->state &= ~CS_TTGO;
+ else
+ com->state |= CS_TTGO;
+#if 0
+ if (tp->t_state & TS_RTS_IFLOW) {
+ if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW)
+ outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS);
+ } else {
+#endif
+ /*
+ * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it
+ * appropriately in comparam() if RTS-flow is being changed.
+ * Check for races.
+ */
+ if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater)
+ outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
+#if 0
+ }
+#endif
+ enable_intr();
+ if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))
+ goto out;
+ if (TB_OUT(tp)->c_cc <= tp->t_lowat) {
+ if (tp->t_state & TS_ASLEEP) {
+ tp->t_state &= ~TS_ASLEEP;
+ wakeup((caddr_t)TB_OUT(tp));
+ }
+ selwakeup(&tp->t_wsel);
+ }
+ if (com->ocount != 0) {
+ disable_intr();
+ siointr1(com);
+ enable_intr();
+ } else if (TB_OUT(tp)->c_cc != 0) {
+ tp->t_state |= TS_BUSY;
+ disable_intr();
+ com->ocount = q_to_b(TB_OUT(tp), com->obuf, sizeof com->obuf);
+ com->optr = com->obuf;
+ com->obufend = com->obuf + com->ocount;
+ com->state |= CS_BUSY;
+ siointr1(com); /* fake interrupt to start output */
+ enable_intr();
+ }
+out:
+ splx(s);
+}
+
+void
+siostop(tp, rw)
+ struct tty *tp;
+ int rw;
+{
+ struct com_s *com;
+
+ com = com_addr(UNIT(tp->t_dev));
+ if (rw & FWRITE)
+ comflush(com);
+ disable_intr();
+ if (rw & FREAD) {
+ com_events -= (com->iptr - com->ibuf);
+ com->iptr = com->ibuf;
+ }
+ if (tp->t_state & TS_TTSTOP)
+ com->state &= ~CS_TTGO;
+ else
+ com->state |= CS_TTGO;
+ enable_intr();
+}
+
+int
+sioselect(dev, rw, p)
+ dev_t dev;
+ int rw;
+ struct proc *p;
+{
+ return (ttselect(dev & ~COM_MINOR_MAGIC_MASK, rw, p));
+}
+
+static void
+commctl(com, bits, how)
+ struct com_s *com;
+ int bits;
+ int how;
+{
+ disable_intr();
+ switch (how) {
+ case DMSET:
+ outb(com->modem_ctl_port,
+ com->mcr_image = bits | (com->mcr_image & MCR_IENABLE));
+ break;
+ case DMBIS:
+ outb(com->modem_ctl_port, com->mcr_image |= bits);
+ break;
+ case DMBIC:
+ outb(com->modem_ctl_port, com->mcr_image &= ~bits);
+ break;
+ }
+ enable_intr();
+}
+
+static void
+comwakeup(chan, ticks)
+ caddr_t chan;
+ int ticks;
+{
+ int unit;
+
+ timeout((timeout_func_t)comwakeup, (caddr_t) NULL, hz / 100);
+
+ if (com_events != 0) {
+#ifndef OLD_INTERRUPT_HANDLING
+ int s = spltty();
+#endif
+ siopoll();
+#ifndef OLD_INTERRUPT_HANDLING
+ splx(s);
+#endif
+ }
+
+ /* recover from lost output interrupts */
+ for (unit = 0; unit < NSIO; ++unit) {
+ struct com_s *com;
+
+ com = com_addr(unit);
+ if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) {
+ disable_intr();
+ siointr1(com);
+ enable_intr();
+ }
+ }
+}
+
+#ifdef OLD_INTERRUPT_HANDLING
+void
+softsio1()
+{
+ siopoll();
+}
+#endif
+
+/*
+ * Following are all routines needed for SIO to act as console
+ */
+#include "i386/i386/cons.h"
+
+struct siocnstate {
+ u_char dlbl;
+ u_char dlbh;
+ u_char ier;
+ u_char cfcr;
+ u_char mcr;
+};
+
+static Port_t siocniobase;
+
+static void
+siocntxwait()
+{
+ int timo;
+
+ /*
+ * Wait for any pending transmission to finish. Required to avoid
+ * the UART lockup bug when the speed is changed, and for normal
+ * transmits.
+ */
+ timo = 100000;
+ while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY) && --timo != 0)
+ ;
+}
+
+static void
+siocnopen(sp)
+ struct siocnstate *sp;
+{
+ int divisor;
+ Port_t iobase;
+
+ /*
+ * Save all the device control registers except the fifo register
+ * and set our default ones (cs8 -parenb speed=comdefaultrate).
+ * We can't save the fifo register since it is read-only.
+ */
+ iobase = siocniobase;
+ sp->ier = inb(iobase + com_ier);
+ outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */
+ siocntxwait();
+ sp->cfcr = inb(iobase + com_cfcr);
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ sp->dlbl = inb(iobase + com_dlbl);
+ sp->dlbh = inb(iobase + com_dlbh);
+ divisor = ttspeedtab(comdefaultrate, comspeedtab);
+ outb(iobase + com_dlbl, divisor & 0xFF);
+ outb(iobase + com_dlbh, (u_int) divisor >> 8);
+ outb(iobase + com_cfcr, CFCR_8BITS);
+ sp->mcr = inb(iobase + com_mcr);
+ outb(iobase + com_mcr, MCR_DTR | MCR_RTS);
+}
+
+static void
+siocnclose(sp)
+ struct siocnstate *sp;
+{
+ Port_t iobase;
+
+ /*
+ * Restore the device control registers.
+ */
+ siocntxwait();
+ iobase = siocniobase;
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ outb(iobase + com_dlbl, sp->dlbl);
+ outb(iobase + com_dlbh, sp->dlbh);
+ outb(iobase + com_cfcr, sp->cfcr);
+ /*
+ * XXX damp osicllations of MCR_DTR or MCR_RTS by not restoring them.
+ */
+ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS);
+ outb(iobase + com_ier, sp->ier);
+}
+
+void
+siocnprobe(cp)
+ struct consdev *cp;
+{
+ int unit;
+
+ /* locate the major number */
+ /* XXX - should be elsewhere since KGDB uses it */
+ for (commajor = 0; commajor < nchrdev; commajor++)
+ if (cdevsw[commajor].d_open == sioopen)
+ break;
+
+ /* XXX: ick */
+ unit = UNIT(CONUNIT);
+ siocniobase = CONADDR;
+
+ /* make sure hardware exists? XXX */
+
+ /* initialize required fields */
+ cp->cn_dev = makedev(commajor, unit);
+#ifdef COMCONSOLE
+ cp->cn_pri = CN_REMOTE; /* Force a serial port console */
+#else
+ cp->cn_pri = CN_NORMAL;
+#endif
+}
+
+void
+siocninit(cp)
+ struct consdev *cp;
+{
+ /*
+ * XXX can delete more comconsole stuff now that i/o routines are
+ * fairly reentrant.
+ */
+ comconsole = UNIT(cp->cn_dev);
+}
+
+int
+siocngetc(dev)
+ dev_t dev;
+{
+ int c;
+ Port_t iobase;
+ int s;
+ struct siocnstate sp;
+
+ iobase = siocniobase;
+ s = spltty();
+ siocnopen(&sp);
+ while (!(inb(iobase + com_lsr) & LSR_RXRDY))
+ ;
+ c = inb(iobase + com_data);
+ siocnclose(&sp);
+ splx(s);
+ return (c);
+}
+
+void
+siocnputc(dev, c)
+ dev_t dev;
+ int c;
+{
+ int s;
+ struct siocnstate sp;
+
+ s = spltty();
+ siocnopen(&sp);
+ siocntxwait();
+ outb(siocniobase + com_data, c);
+ siocnclose(&sp);
+ splx(s);
+}
+
+#endif /* NSIO > 0 */
diff --git a/sys/i386/isa/sioreg.h b/sys/i386/isa/sioreg.h
new file mode 100644
index 0000000..4b0f1b6
--- /dev/null
+++ b/sys/i386/isa/sioreg.h
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)comreg.h 7.2 (Berkeley) 5/9/91
+ * $Id$
+ */
+
+
+/* 16 bit baud rate divisor (lower byte in dca_data, upper in dca_ier) */
+#define COMBRD(x) (1843200 / (16*(x)))
+
+/* interrupt enable register */
+#define IER_ERXRDY 0x1
+#define IER_ETXRDY 0x2
+#define IER_ERLS 0x4
+#define IER_EMSC 0x8
+
+/* interrupt identification register */
+#define IIR_IMASK 0xf
+#define IIR_RXTOUT 0xc
+#define IIR_RLS 0x6
+#define IIR_RXRDY 0x4
+#define IIR_TXRDY 0x2
+#define IIR_NOPEND 0x1
+#define IIR_MLSC 0x0
+#define IIR_FIFO_MASK 0xc0 /* set if FIFOs are enabled */
+
+/* fifo control register */
+#define FIFO_ENABLE 0x01
+#define FIFO_RCV_RST 0x02
+#define FIFO_XMT_RST 0x04
+#define FIFO_DMA_MODE 0x08
+#define FIFO_TRIGGER_1 0x00
+#define FIFO_TRIGGER_4 0x40
+#define FIFO_TRIGGER_8 0x80
+#define FIFO_TRIGGER_14 0xc0
+
+/* character format control register */
+#define CFCR_DLAB 0x80
+#define CFCR_SBREAK 0x40
+#define CFCR_PZERO 0x30
+#define CFCR_PONE 0x20
+#define CFCR_PEVEN 0x10
+#define CFCR_PODD 0x00
+#define CFCR_PENAB 0x08
+#define CFCR_STOPB 0x04
+#define CFCR_8BITS 0x03
+#define CFCR_7BITS 0x02
+#define CFCR_6BITS 0x01
+#define CFCR_5BITS 0x00
+
+/* modem control register */
+#define MCR_LOOPBACK 0x10
+#define MCR_IENABLE 0x08
+#define MCR_DRS 0x04
+#define MCR_RTS 0x02
+#define MCR_DTR 0x01
+
+/* line status register */
+#define LSR_RCV_FIFO 0x80
+#define LSR_TSRE 0x40
+#define LSR_TXRDY 0x20
+#define LSR_BI 0x10
+#define LSR_FE 0x08
+#define LSR_PE 0x04
+#define LSR_OE 0x02
+#define LSR_RXRDY 0x01
+#define LSR_RCV_MASK 0x1f
+
+/* modem status register */
+#define MSR_DCD 0x80
+#define MSR_RI 0x40
+#define MSR_DSR 0x20
+#define MSR_CTS 0x10
+#define MSR_DDCD 0x08
+#define MSR_TERI 0x04
+#define MSR_DDSR 0x02
+#define MSR_DCTS 0x01
+
+/*
+ * WARNING: Serial console is assumed to be at COM1 address
+ * and CONUNIT must be 0.
+ */
+#define CONADDR (0x3f8)
+#define CONUNIT (0)
diff --git a/sys/i386/isa/sound/CHANGELOG b/sys/i386/isa/sound/CHANGELOG
new file mode 100644
index 0000000..6a9bef1
--- /dev/null
+++ b/sys/i386/isa/sound/CHANGELOG
@@ -0,0 +1,75 @@
+Changelog for version 2.5
+-------------------------
+
+Since 2.5-beta2
+- Some fine tuning to the GUS v3.7 mixer code.
+- Fixed speed limits for the plain SB (1.0 to 2.0).
+
+Since 2.5-beta
+- Fixed OPL-3 detection with SB. Caused problems with PAS16.
+- GUS v3.7 mixer support.
+
+Since 2.4
+- Mixer support for Sound Galaxy NX Pro (define __SGNXPRO__ on your local.h).
+- Fixed truncated sound on /dev/dsp when the device is closed.
+- Linear volume mode for GUS
+- Pitch bends larger than +/- 2 octaves.
+- MIDI recording for SB and SB Pro. (Untested).
+- Some other fixes.
+- SB16 MIDI and DSP drivers only initialized if SB16 actually installed.
+- Implemented better detection for OPL-3. This should be usefull if you
+ have an old SB Pro (the non-OPL-3 one) or a SB 2.0 clone which has a OPL-3.
+- SVR4.2 support by Ian Hartas. Initial ALPHA TEST version (untested).
+
+Since 2.3b
+- Fixed bug which made it impossible to make long recordings to disk.
+ Recording was not restarted after a buffer overflow situation.
+- Limited mixer support for GUS.
+- Numerous improvements to the GUS driver by Andrew Robinson. Including
+ some click removal etc.
+
+Since 2.3
+- Fixed some minor bugs in the SB16 driver.
+
+Since 2.2b
+- Full SB16 DSP support. 8/16 bit, mono/stereo
+- The SCO and FreeBSD versions should be in sync now. There are some
+ problems with SB16 and GUS in the freebsd versions.
+ The DMA buffer allocation of the SCO version has been polished but
+ there could still be some problems. At least it hogs memory.
+ The DMA channel
+ configuration method used in the sco/System is a hack.
+- Support for the MPU emulation of the SB16.
+- Some big arrays are now allocated boot time. This makes the bss segment
+ smaller which makes it possible to use the full driver with
+ NetBSD. These arrays are not allocated if no suitable soundcard is available.
+- Fixed a bug in the compute_and_set_volume in gus_wave.c
+- Fixed the too fast mono playback problem of SB Pro and PAS16.
+
+Since 2.2
+- Stereo recording for SB Pro. Somehow it was missing and nobody
+ had noticed it earlier.
+- Minor polishing.
+- Interpreting of boot time arguments (sound=) for Linux.
+- Breakup of sb_dsp.c. Parts of the code has been moved to
+ sb_mixer.c and sb_midi.c
+
+Since 2.1
+- Preliminary support for SB16.
+ - The SB16 mixer is supported in it's native mode.
+ - Digitized voice capability up to 44.1 kHz/8 bit/mono
+ (16 bit and stereo support coming in the next release).
+- Fixed some bugs in the digitized voice driver for PAS16.
+- Proper initialization of the SB emulation of latest PAS16 models.
+
+- Significantly improved /dev/dsp and /dev/audio support.
+ - Now supports half duplex mode. It's now possible to record and
+ playback without closing and reopening the device.
+ - It's possible to use smaller buffers than earlier. There is a new
+ ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &n) where n should be 1, 2 or 4.
+ This call instructs the driver to use smaller buffers. The default
+ buffer size (0.5 to 1.0 seconds) is divided by n. Should be called
+ immediately after opening the device.
+
+Since 2.0
+Just cosmetic changes.
diff --git a/sys/i386/isa/sound/COPYING b/sys/i386/isa/sound/COPYING
new file mode 100644
index 0000000..d1509c5
--- /dev/null
+++ b/sys/i386/isa/sound/COPYING
@@ -0,0 +1,25 @@
+/*
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
diff --git a/sys/i386/isa/sound/HOWTO_MIDI b/sys/i386/isa/sound/HOWTO_MIDI
new file mode 100644
index 0000000..f0601e5
--- /dev/null
+++ b/sys/i386/isa/sound/HOWTO_MIDI
@@ -0,0 +1,51 @@
+The following file describes the procedure for adding modules to MIDI
+Please READ the main documentation files for the driver first!!!
+
+
+ Example: We have a sound card with a MIDI chip & port on it
+ and, we call it the 'MYBLASTER' card:
+
+ **************************************************************************
+
+ 0: Run 'configure'. Select the MIDI on CHIP support option.
+
+ 1: Write a midi driver module; 'blast_midi.c'
+ (with functions for open,close,read,write,attach.)
+
+ 1a: Write all functions except the 'attach' the way you want.
+
+ For the 'attach' function, look at a model in the 'pro_midi.c'
+ file. Just dup it in the same fashion. For the 'generic_midi_operations'
+ structure which is required, see file 'dev_table.h'.
+
+ 2: We called the 'attach' function: 'blast_attach'.
+
+ Go to the file 'dev_table.h' and add your driver name and the function
+ pointer ( which is 'blast_attach' ) to the 'midi_supported' table.
+
+ 3: You are almost set. Go and make a reference
+ to an 'exclude constant'; say EXLCUDE_BLAST_MIDI in your module
+ (refer to the 'pro_midi.c' file for model). Also make sure to
+ add the constant to the file 'sound_config.h' (for example, look
+ where the constant EXCLUDE_PRO_MIDI is in the file.)
+
+ 4: Add the line
+
+ #define ALL_EXTERNAL_TO_ME
+
+ as the 1st line of your 'blast_midi.c' file. This happily, makes
+ you ignorant of everything else specific to the driver! :).
+
+ 4a: And of course, don't forget to make a device :). Note that your
+ minor number should be = ( 15 + position of your driver in the
+ 'midi_supported' table in the 'dev_table.h' file ).
+
+ Eg: Your driver is the second one in the table. viz midi_supported[1].
+ Your device minor number should be ( 15 + 1 = 16 ). Then, make the
+ reference to your device as, say CMIDI_DEV_BLAST in the file
+ 'sound_config.h'. Also add this in 'soundcard.c' for the open, read,
+ write and close routines. See files for example using CMIDI_DEV_PRO
+ (which is the ProAudioSpectrum on chip MIDI).
+
+ 5: You are all set. If ever you have problems, follow the same model
+ as the file 'pro_midi.c', except for substituting your own functions!
diff --git a/sys/i386/isa/sound/README b/sys/i386/isa/sound/README
new file mode 100644
index 0000000..efb0b11
--- /dev/null
+++ b/sys/i386/isa/sound/README
@@ -0,0 +1,17 @@
+CAUTION!
+
+This is a prototype version of the Linux Sound Driver for FreeBSD.
+
+The official and supported version is 1.0c.
+
+This version 'should work' but there may be some bugs and the programmers
+API may change before the final version.
+
+There are some additional programs for GUS owners in the
+gustest subdirectory of this directory, namely a module
+(.MOD, .STM and .669) player and a patch file loader.
+Additionally, there is a midithru program which allows
+you to play the synth on the soundcard with a midi keyboard
+(also usable for OPL-3 owners).
+
+Hannu & FreeBSD team.
diff --git a/sys/i386/isa/sound/RELNOTES b/sys/i386/isa/sound/RELNOTES
new file mode 100644
index 0000000..03d492d
--- /dev/null
+++ b/sys/i386/isa/sound/RELNOTES
@@ -0,0 +1,38 @@
+Welcome to use the Linux sound driver for FreeBSD. This
+driver supports the SoundBlaster, SB Pro, Pro Audio Spectrum 16,
+AdLib and Gravis UltraSound sound cards.
+
+In addition there is rather limited support for MPU-401
+(and compatible) midi cards. Also, the OPL-3 synthesizer
+of the SB Pro and PAS16 cards is now supported in the 4 OP
+modes.
+
+Most of the features of the /dev/sequencer device file are
+available just for GUS owners.
+
+The SoundBlaster 16 and SB 16 ASP cards are not supported,
+though they may work in mono mode with speeds < 22 kHz.
+The OPL-3 chicp of the SB 16 should work (without problems?).
+Is there anybody willing to implement the SB 16 support
+(have the SB 16 and the SDK for it)?
+
+Since this driver is a sound driver, it does not contain support
+for SCSI/CD-ROM/Joystick -devices.
+
+Known bugs
+----------
+
+- It's not possible to open /dev/dsp (or /dev/audio) while the
+ /dev/sequencer is open for output and GUS is the only soundcard
+ installed. It's possible if /dev/dsp is opened before /dev/sequencer
+ but at this time the GUS is not available for access via /dev/sequencer.
+ This is a limitation of the driver.
+- MPU-401 driver hangs the computer on boot if there is no MPU-401 installed.
+ It uses by default the I/O port 0x330, which is also used by the
+ Adaptec 1542 SCSI adapter.
+- The /dev/sequencer playback to GUS sounds sometimes rather weird. Hitting
+ ^C and playing again should solve this problem. This is probably caused by
+ incompatibilities between the GUS and certain VLB motherboards. Try to avoid
+ switching between VTs while patches are being loaded to the GUS.
+- There is a skeleton of the patch manager support. It doesn't work in
+ this version.
diff --git a/sys/i386/isa/sound/RELNOTES.Linux b/sys/i386/isa/sound/RELNOTES.Linux
new file mode 100644
index 0000000..ea57d0a
--- /dev/null
+++ b/sys/i386/isa/sound/RELNOTES.Linux
@@ -0,0 +1,255 @@
+Release notes for the Linux Sound Driver 2.5
+--------------------------------------------
+There is also a version called 2.5-beta floating around the net. This
+version contains some fixes after it. Mainly to the SB and GUS code.
+
+CAUTION! The SVR4.2 port has not been tested much. Backup your system
+ carefully before trying it.
+
+This is mainly a bug fix release. There are couple of new things such as
+linear volume mode for GUS and MIDI recording for SB 2.0 and SB Pro.
+Also this version supports the mixer of GUS v3.7. (Support for GUS MAX and
+the 16-bit daughtercard is coming sooner or later).
+
+NOTE! The sound driver is a part of the Linux kernel distribution also.
+ Check that your kernel doesn't have more recent version than this
+ when installing a separately distributed sound driver. The
+ version number of this driver is defined in the makefile.
+
+This version contains a driver for the SB16 also.
+The SB16 driver requires separate DMA channels for the 8 and 16 bit
+modes. There should be a way to share the 8 bit DMA channels between
+these modes but this feature is not supported yet.
+The SB16 DSP support is by Joerg Schubert (jsb@sth.ruhr-uni-bochum.de).
+
+The SB16 driver has also the Midi input capability even at the same
+time with the /dev/dsp. Also the WaveBlaster daughter board is supported.
+No support for the ASP chip yet (the ASP chip can be installed but it's
+not used by the driver).
+
+You will need the snd-util-2.5.tar.gz and snd-data-0.1.tar.Z
+packages to use this driver. They should be in the same
+ftp site or BBS from where you got this driver. For
+example at nic.funet.fi:pub/OS/Linux/*.
+
+If you are looking for the installation instructions, please
+look at $OS/Readme.
+
+This version supports the following soundcards:
+GUS, SoundBlaster, SB Pro, SB16, Pro Audio Spectrum 16 and AdLib.
+In addition there is rather limited support for MPU-401.
+(and compatible) midi cards. Also the OPL-3 synthesizer
+Most of the features of the /dev/sequencer device file are
+available just for GUS owners.
+
+NOTE! There are separate driver for CD-ROMS supported by
+ some soundcards. The driver for CDU31A (Fusion 16) is
+ called cdu31a-0.6.diff.z. It will be contained in the
+ Linux version 0.99.12. The driver for the CD-ROM of SB Pro
+ is sbpcd0.4.tar.gz (these were the latest versions when I wrote
+ this). These files should be at least at sunsite.unc.edu.
+ Also the SCSI interface of the PAS16 should be supported by
+ Linux 0.99.13k and later.
+
+ There is also a driver for joystick. Look for file joystick-0.5.tar.gz
+ (sunsite).
+
+
+Compatibility with the earlier versions
+---------------------------------------
+
+In this version the ultrasound.h no longer includes the sys/soundcard.h
+You have to change the gmod.c of the snd-util-2.0 package and to add an
+include for it.
+
+IMPORTANT!!!!!!!!!!!!!!!!!!!!!!
+
+This version is not binary or source compatible with the version 1.0c.
+
+The ioctl() interface has changed completely since version 1.0c. All
+programs using this driver must be at least recompiled.
+The snd-util-2.0 package contains some utilities for this version.
+
+The version 1.0c and earlier used a 'nonportable' ioctl calling scheme
+where the input argument was passed by value and the output value was
+returned as the functional return. For example setting the speed of
+/dev/dsp were done as the following:
+
+ int actual_speed;
+ actual_speed = ioctl(fd, SOUND_PCM_WRITE_RATE, 44100);
+
+After version 1.99.0 this must be done as the following:
+
+ int actual_speed = 44100;
+ ioctl(fd, SOUND_PCM_WRITE_RATE, &actual_speed);
+
+If you have an application written for the version 1.0, you should search
+for the strings SNDCTL_ and SOUND_ and to check the parameters.
+The following ioctl calls have changed:
+
+ SNDCTL_SEQ_GETOUTCOUNT
+ SNDCTL_SEQ_GETINCOUNT
+ SNDCTL_SEQ_TESTMIDI
+ SNDCTL_DSP_SPEED
+ SNDCTL_DSP_STEREO
+ SNDCTL_DSP_GETBLKSIZE
+ SNDCTL_DSP_SAMPLESIZE
+ SOUND_PCM_WRITE_CHANNELS
+ SOUND_PCM_WRITE_FILTER
+ SOUND_PCM_READ_RATE
+ SOUND_PCM_READ_CHANNELS
+ SOUND_PCM_READ_BITS
+ SOUND_PCM_READ_FILTER
+ SOUND_PCM_WRITE_BITS
+ SOUND_PCM_WRITE_RATE
+ SOUND_MIXER_READ_* (several ones)
+ SOUND_MIXER_WRITE_* (several ones)
+
+Since the this version will support more than one synthesizer devices
+at the same time, the ioctl(SNDCTL_FM_LOAD_INSTR) is obsolete. In addition
+there is some new fields which must be initialized. Look at the sbiset.c in
+the snd-util-2.0 package for further info.
+
+This version is almost 100% compatible with the alpha test version (1.99.9). The
+difference is in the installation procedure.
+
+Using this driver with other operating systems than Linux
+---------------------------------------------------------
+
+This package contains just the Linux version. The version 2.3
+for SCO is available at nic.funet.fi:pub/OS/Linux/ALPHA/sound.
+The version 2.3 doesn't work well with xxxxxBSD. Use the version
+2.3 for them.
+
+/dev/sndstat
+------------
+
+The /dev/sndstat is now available in the SCO and BSD versions also.
+
+This is a new devicefile for debugging purposes. A better place for
+it is in the /proc -directory but I was just too lazy to implement it
+properly. The /dev/sndstat (major 14, minor 6) is a file which returns
+info about the current configuration (see the example below). If you
+send me a error/problem report, please include a printout from this
+device to your message (cat /dev/sndstat).
+
+Note! This device file is currently present only in the Linux version
+ of this driver.
+
+------ cut here --- cat /dev/sndstat example --------
+Sound Driver:1.99.7 (Fri Jul 9 17:01:47 GMT 1993 root@lucifer.savolai.fi)
+Config options: 0x00000d4b
+
+HW config:
+Type 4: Gravis Ultrasound at 0x210 irq 15 drq 6
+Type 3: ProAudioSpectrum at 0x388 irq 10 drq 3
+Type 2: SoundBlaster at 0x220 irq 7 drq 1
+Type 1: AdLib at 0x388 irq 0 drq 0
+
+PCM devices:
+00: Gravis UltraSound
+01: Pro Audio Spectrum
+02: SoundBlaster 2.0
+
+Synth devices:
+00: Gravis UltraSound
+01: Yamaha OPL-3
+
+Midi devices:
+00: Gravis UltraSound
+01: Pro Audio Spectrum
+
+Mixer(s) installed
+------ cut here ---- End of Example -----------
+
+Known bugs/limitations
+----------------------
+
+- High speed recording of long audio samples (>20 second) to disk
+ is not possible. Everything works until next sync() which delays the
+ recording process too much. A delay longer than 0.1 to 0.3 seconds is
+ too much.
+- The SB16 driver sometimes swaps the left and right channels together.
+- Midi input doesn't work with SB and SB Pro (SB16 works).
+- It's not possible to open /dev/dsp (or /dev/audio) while the
+ /dev/sequencer is open for output and GUS is the only soundcard
+ installed. It's possible if /dev/dsp is opened before /dev/sequencer
+ but at this time the GUS is not available for access via /dev/sequencer.
+ This is a limitation of the driver.
+- MPU-401 driver hangs the computer on boot if there is no MPU-401 installed.
+ It uses by default the I/O port 0x330 whic is used by Adaptec 1542 SCSI
+ adapter.
+- There are some problems in midi input with MPU-401 and the SB16 midi
+ (MPU-401 emulation). This makes it impossible to read long sysex dumps
+ using these devices.
+- The /dev/sequencer playback to GUS sounds sometimes rather weird. Hitting
+ ^C and playing again should solve this problem. This is propably caused by
+ incompatibilities between GUS and certain VLB motherboards (like mine).
+ Try to avoid
+ switching between VTs while patches are being loaded to the GUS.
+ This problem disappears completely if you define GUS_PATCH_NO_DMA in the
+ local.h (after make config in linux). The drawback is that patch loading
+ without DMA takes several times longer than with DMA.
+- There is a skeleton of the patch manager support. It don't work in
+ this version.
+
+
+Future development
+------------------
+
+- Since this driver is no longer just the Linux Sound Driver, it's time
+ to give it a new name. I have planned to use name VoxWare.
+- I'm writing a Hacker's guide to the VoxWare sound driver. Should
+ be ready within this(/next) year (alpha version).
+- Completion of the ISC, SCO and BSD ports. Port to SVR4.2.
+- I'm interested to implement/include support for new soundcards and
+ operating systems.
+
+ Hint for the soundcard and OS manufacturers:
+ I'm collecting soundcards (high end ones) and SDKs for them. In
+ addition I'm collecting PC operating systems. I will be happy if
+ somebody sends me such items. In addition such kind of donation
+ makes it easier to change the VoxWare driver to support your
+ soundcard or operating system. However, please contact me before
+ sending anything.
+
+I will propably release some fix versions within this and next year. At
+least when the non-Linux versions get ready. The next major release (3.0)
+will be quite complete rewrite and released after about a year (end of 94 or
+beginning of 95).
+
+
+Contributors
+------------
+
+This driver contains code by several contributors. In addition several other
+persons have given usefull suggestions. The following is a list of major
+contributors. (I could have forgotten some names.)
+
+ Craig Metz 1/2 of the PAS16 Mixer and PCM support
+ Rob Hooft Volume computation algorithm for the FM synth.
+ Mika Liljeberg uLaw encoding and decoding routines
+ Greg Lee Volume computation algorithm for the GUS and
+ lot's of valuable suggestions.
+ Andy Warner Initial ISC port
+ Jim Lowe Initial FreeBSD port
+ Anders Baekgaard Bughunting and valuable suggestions.
+ Joerg Schubert SB16 DSP support.
+ Andrew Robinson Improvements to the GUS driver
+ Megens SA MIDI recording for SB and SB Pro.
+ Mikael Nordqvist Linear volume support for GUS.
+ Ian Hartas SVR4.2 port
+ Markus Aroharju and
+ Risto Kankkunen Major contributions to the mixer support
+ of GUS v3.7.
+ Hunyue Yau Sound Galaxy NX Pro mixer support.
+
+Regards,
+
+Hannu Savolainen
+hannu@voxware.pp.fi, Hannu.Savolainen@Helsinki.fi
+
+Snail mail: Hannu Savolainen
+ Pallaksentie 4 A 2
+ 00970 Helsinki
+ Finland
diff --git a/sys/i386/isa/sound/adlib_card.c b/sys/i386/isa/sound/adlib_card.c
new file mode 100644
index 0000000..6365069
--- /dev/null
+++ b/sys/i386/isa/sound/adlib_card.c
@@ -0,0 +1,51 @@
+/*
+ * sound/adlib_card.c
+ *
+ * Detection routine for the AdLib card.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_YM3812)
+
+long
+attach_adlib_card (long mem_start, struct address_info *hw_config)
+{
+
+ if (opl3_detect (FM_MONO))
+ {
+ mem_start = opl3_init (mem_start);
+ }
+ return mem_start;
+}
+
+int
+probe_adlib (struct address_info *hw_config)
+{
+ return opl3_detect (FM_MONO);
+}
+
+#endif
diff --git a/sys/i386/isa/sound/audio.c b/sys/i386/isa/sound/audio.c
new file mode 100644
index 0000000..f27f9d5
--- /dev/null
+++ b/sys/i386/isa/sound/audio.c
@@ -0,0 +1,356 @@
+/*
+ * sound/audio.c
+ *
+ * Device file manager for /dev/audio
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+#ifndef EXCLUDE_AUDIO
+
+#include "ulaw.h"
+
+#define ON 1
+#define OFF 0
+
+static int wr_buff_no[MAX_DSP_DEV]; /* != -1, if there is a
+
+ * incomplete output block */
+static int wr_buff_size[MAX_DSP_DEV], wr_buff_ptr[MAX_DSP_DEV];
+
+static int audio_mode[MAX_DSP_DEV];
+
+#define AM_NONE 0
+#define AM_WRITE 1
+#define AM_READ 2
+
+static char *wr_dma_buf[MAX_DSP_DEV];
+
+int
+audio_open (int dev, struct fileinfo *file)
+{
+ int ret;
+ int bits;
+ int dev_type = dev & 0x0f;
+ int mode = file->mode & O_ACCMODE;
+
+ dev = dev >> 4;
+
+ if (dev_type == SND_DEV_DSP16)
+ bits = 16;
+ else
+ bits = 8;
+
+ if ((ret = DMAbuf_open (dev, mode)) < 0)
+ return ret;
+
+ if (DMAbuf_ioctl (dev, SNDCTL_DSP_SAMPLESIZE, bits, 1) != bits)
+ {
+ audio_release (dev, file);
+ return RET_ERROR (ENXIO);
+ }
+
+ wr_buff_no[dev] = -1;
+ audio_mode[dev] = AM_NONE;
+
+ return ret;
+}
+
+void
+audio_release (int dev, struct fileinfo *file)
+{
+ int mode;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+
+ if (wr_buff_no[dev] >= 0)
+ {
+ DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+ wr_buff_no[dev] = -1;
+ }
+
+ DMAbuf_release (dev, mode);
+}
+
+#ifdef NO_INLINE_ASM
+static void
+translate_bytes (const unsigned char *table, unsigned char *buff, unsigned long n)
+{
+ unsigned long i;
+
+ for (i = 0; i < n; ++i)
+ buff[i] = table[buff[i]];
+}
+
+#else
+extern inline void
+translate_bytes (const void *table, void *buff, unsigned long n)
+{
+ __asm__ ("cld\n"
+ "1:\tlodsb\n\t"
+ "xlatb\n\t"
+ "stosb\n\t"
+ "loop 1b\n\t":
+ :"b" ((long) table), "c" (n), "D" ((long) buff), "S" ((long) buff)
+ :"bx", "cx", "di", "si", "ax");
+}
+
+#endif
+
+int
+audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ int c, p, l;
+ int err;
+ int dev_type = dev & 0x0f;
+
+ dev = dev >> 4;
+
+ p = 0;
+ c = count;
+
+ if (audio_mode[dev] == AM_READ) /* Direction changed */
+ {
+ wr_buff_no[dev] = -1;
+ }
+
+ audio_mode[dev] = AM_WRITE;
+
+ if (!count) /* Flush output */
+ {
+ if (wr_buff_no[dev] >= 0)
+ {
+ DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+ wr_buff_no[dev] = -1;
+ }
+ return 0;
+ }
+
+ while (c)
+ { /* Perform output blocking */
+ if (wr_buff_no[dev] < 0) /* There is no incomplete buffers */
+ {
+ if ((wr_buff_no[dev] = DMAbuf_getwrbuffer (dev, &wr_dma_buf[dev], &wr_buff_size[dev])) < 0)
+ return wr_buff_no[dev];
+ wr_buff_ptr[dev] = 0;
+ }
+
+ l = c;
+ if (l > (wr_buff_size[dev] - wr_buff_ptr[dev]))
+ l = (wr_buff_size[dev] - wr_buff_ptr[dev]);
+
+ if (!dsp_devs[dev]->copy_from_user)
+ { /* No device specific copy routine */
+ COPY_FROM_USER (&wr_dma_buf[dev][wr_buff_ptr[dev]], buf, p, l);
+ }
+ else
+ dsp_devs[dev]->copy_from_user (dev,
+ wr_dma_buf[dev], wr_buff_ptr[dev], buf, p, l);
+
+
+ /* Insert local processing here */
+
+ if (dev_type == SND_DEV_AUDIO)
+ {
+#ifdef linux
+ /* This just allows interrupts while the conversion is running */
+ __asm__ ("sti");
+#endif
+ translate_bytes (ulaw_dsp, (unsigned char *) &wr_dma_buf[dev][wr_buff_ptr[dev]], l);
+ }
+
+ c -= l;
+ p += l;
+ wr_buff_ptr[dev] += l;
+
+ if (wr_buff_ptr[dev] >= wr_buff_size[dev])
+ {
+ if ((err = DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev])) < 0)
+ return err;
+
+ wr_buff_no[dev] = -1;
+ }
+
+ }
+
+ return count;
+}
+
+int
+audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ int c, p, l;
+ char *dmabuf;
+ int buff_no;
+ int dev_type = dev & 0x0f;
+
+ dev = dev >> 4;
+ p = 0;
+ c = count;
+
+ if (audio_mode[dev] == AM_WRITE)
+ {
+ if (wr_buff_no[dev] >= 0)
+ {
+ DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+ wr_buff_no[dev] = -1;
+ }
+ }
+
+ audio_mode[dev] = AM_READ;
+
+ while (c)
+ {
+ if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l)) < 0)
+ return buff_no;
+
+ if (l > c)
+ l = c;
+
+ /* Insert any local processing here. */
+
+ if (dev_type == SND_DEV_AUDIO)
+ {
+#ifdef linux
+ /* This just allows interrupts while the conversion is running */
+ __asm__ ("sti");
+#endif
+
+ translate_bytes (dsp_ulaw, (unsigned char *) dmabuf, l);
+ }
+
+ COPY_TO_USER (buf, p, dmabuf, l);
+
+ DMAbuf_rmchars (dev, buff_no, l);
+
+ p += l;
+ c -= l;
+ }
+
+ return count - c;
+}
+
+int
+audio_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+ int dev_type = dev & 0x0f;
+
+ dev = dev >> 4;
+
+ switch (cmd)
+ {
+ case SNDCTL_DSP_SYNC:
+ if (wr_buff_no[dev] >= 0)
+ {
+ DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+ wr_buff_no[dev] = -1;
+ }
+ return DMAbuf_ioctl (dev, cmd, arg, 0);
+ break;
+
+ case SNDCTL_DSP_POST:
+ if (wr_buff_no[dev] >= 0)
+ {
+ DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+ wr_buff_no[dev] = -1;
+ }
+ return 0;
+ break;
+
+ case SNDCTL_DSP_RESET:
+ wr_buff_no[dev] = -1;
+ return DMAbuf_ioctl (dev, cmd, arg, 0);
+ break;
+
+ default:
+ if (dev_type == SND_DEV_AUDIO)
+ return RET_ERROR (EIO);
+
+ return DMAbuf_ioctl (dev, cmd, arg, 0);
+ }
+}
+
+long
+audio_init (long mem_start)
+{
+ return mem_start;
+}
+
+#else
+/* Stub versions */
+
+int
+audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+audio_open (int dev, struct fileinfo *file)
+{
+ return RET_ERROR (ENXIO);
+}
+
+void
+audio_release (int dev, struct fileinfo *file)
+{
+};
+int
+audio_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig)
+{
+ return RET_ERROR (EIO);
+}
+
+long
+audio_init (long mem_start)
+{
+ return mem_start;
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/dev_table.c b/sys/i386/isa/sound/dev_table.c
new file mode 100644
index 0000000..7f7cae1
--- /dev/null
+++ b/sys/i386/isa/sound/dev_table.c
@@ -0,0 +1,217 @@
+/*
+ * sound/dev_table.c
+ *
+ * Device call tables.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#define _DEV_TABLE_C_
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+long
+sndtable_init (long mem_start)
+{
+ int i, n = sizeof (supported_drivers) / sizeof (struct card_info);
+
+ for (i = 0; i < (n - 1); i++)
+ if (supported_drivers[i].enabled)
+ if (supported_drivers[i].probe (&supported_drivers[i].config))
+ {
+#ifndef SHORT_BANNERS
+ printk ("snd%d",
+ supported_drivers[i].card_type);
+#endif
+
+ mem_start = supported_drivers[i].attach (mem_start, &supported_drivers[i].config);
+#ifndef SHORT_BANNERS
+ printk (" at 0x%x irq %d drq %d\n",
+ supported_drivers[i].config.io_base,
+ supported_drivers[i].config.irq,
+ supported_drivers[i].config.dma);
+#endif
+ }
+ else
+ supported_drivers[i].enabled = 0; /* Mark as not detected */
+ return mem_start;
+}
+
+int
+sndtable_probe (int unit, struct address_info *hw_config)
+{
+ int i, n = sizeof (supported_drivers) / sizeof (struct card_info);
+
+ if (!unit)
+ return TRUE;
+
+ for (i = 0; i < (n - 1); i++)
+ if (supported_drivers[i].card_type == unit)
+ {
+ supported_drivers[i].config.io_base = hw_config->io_base;
+ supported_drivers[i].config.irq = hw_config->irq;
+ supported_drivers[i].config.dma = hw_config->dma;
+ if (supported_drivers[i].probe (hw_config))
+ return 1;
+ supported_drivers[i].enabled = 0; /* Mark as not detected */
+ return 0;
+ }
+
+ return FALSE;
+}
+
+int
+sndtable_init_card (int unit, struct address_info *hw_config)
+{
+ int i, n = sizeof (supported_drivers) / sizeof (struct card_info);
+
+ if (!unit)
+ {
+ if (sndtable_init (0) != 0)
+ panic ("snd: Invalid memory allocation\n");
+ return TRUE;
+ }
+
+ for (i = 0; i < (n - 1); i++)
+ if (supported_drivers[i].card_type == unit)
+ {
+ supported_drivers[i].config.io_base = hw_config->io_base;
+ supported_drivers[i].config.irq = hw_config->irq;
+ supported_drivers[i].config.dma = hw_config->dma;
+
+ if (supported_drivers[i].attach (0, hw_config) != 0)
+ panic ("snd#: Invalid memory allocation\n");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+int
+sndtable_get_cardcount (void)
+{
+ return num_dspdevs + num_mixers + num_synths + num_midis;
+}
+
+#ifdef linux
+void
+sound_setup (char *str, int *ints)
+{
+ int i, n = sizeof (supported_drivers) / sizeof (struct card_info);
+
+ /*
+ * First disable all drivers
+ */
+
+ for (i = 0; i < n; i++)
+ supported_drivers[i].enabled = 0;
+
+ if (ints[0] == 0 || ints[1] == 0)
+ return;
+ /*
+ * Then enable them one by time
+ */
+
+ for (i = 1; i <= ints[0]; i++)
+ {
+ int card_type, ioaddr, irq, dma, ptr, j;
+ unsigned int val;
+
+ val = (unsigned int) ints[i];
+
+ card_type = (val & 0x0ff00000) >> 20;
+
+ if (card_type > 127)
+ {
+ /* Add any future extensions here */
+ return;
+ }
+
+ ioaddr = (val & 0x000fff00) >> 8;
+ irq = (val & 0x000000f0) >> 4;
+ dma = (val & 0x0000000f);
+
+ ptr = -1;
+ for (j = 0; j < n && ptr == -1; j++)
+ if (supported_drivers[j].card_type == card_type)
+ ptr = j;
+
+ if (ptr == -1)
+ printk ("Sound: Invalid setup parameter 0x%08x\n", val);
+ else
+ {
+ supported_drivers[ptr].enabled = 1;
+ supported_drivers[ptr].config.io_base = ioaddr;
+ supported_drivers[ptr].config.irq = irq;
+ supported_drivers[ptr].config.dma = dma;
+ }
+ }
+}
+
+#else
+void
+sound_chconf (int card_type, int ioaddr, int irq, int dma)
+{
+ int i, n = sizeof (supported_drivers) / sizeof (struct card_info);
+
+ int ptr, j;
+
+ ptr = -1;
+ for (j = 0; j < n && ptr == -1; j++)
+ if (supported_drivers[j].card_type == card_type)
+ ptr = j;
+
+ if (ptr != -1)
+ {
+ supported_drivers[ptr].enabled = 1;
+ if (ioaddr)
+ supported_drivers[ptr].config.io_base = ioaddr;
+ if (irq)
+ supported_drivers[ptr].config.irq = irq;
+ if (dma)
+ supported_drivers[ptr].config.dma = dma;
+ }
+}
+
+#endif
+
+struct address_info *
+sound_getconf (int card_type)
+{
+ int j, ptr;
+ int n = sizeof (supported_drivers) / sizeof (struct card_info);
+
+ ptr = -1;
+ for (j = 0; j < n && ptr == -1; j++)
+ if (supported_drivers[j].card_type == card_type)
+ ptr = j;
+
+ if (ptr == -1)
+ return (struct address_info *) NULL;
+
+ return &supported_drivers[ptr].config;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/dev_table.h b/sys/i386/isa/sound/dev_table.h
new file mode 100644
index 0000000..4b656ba
--- /dev/null
+++ b/sys/i386/isa/sound/dev_table.h
@@ -0,0 +1,273 @@
+/*
+ * dev_table.h
+ *
+ * Global definitions for device call tables
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+
+*/
+
+#ifndef _DEV_TABLE_H_
+#define _DEV_TABLE_H_
+
+/*
+ * NOTE! NOTE! NOTE! NOTE!
+ *
+ * If you modify this file, please check the dev_table.c also.
+ *
+ * NOTE! NOTE! NOTE! NOTE!
+ */
+
+struct card_info {
+ int card_type; /* From soundcard.c */
+ char *name;
+ long (*attach) (long mem_start, struct address_info *hw_config);
+ int (*probe) (struct address_info *hw_config);
+ struct address_info config;
+ int enabled;
+};
+
+/** UWM -- new MIDI structure here.. **/
+
+struct generic_midi_info{
+ char *name; /* Name of the MIDI device.. */
+ long (*attach) (long mem_start);
+};
+
+struct audio_operations {
+ char name[32];
+ int flags;
+#define NOTHING_SPECIAL 0
+#define NEEDS_RESTART 1
+ int (*open) (int dev, int mode);
+ void (*close) (int dev);
+ void (*output_block) (int dev, unsigned long buf,
+ int count, int intrflag, int dma_restart);
+ void (*start_input) (int dev, unsigned long buf,
+ int count, int intrflag, int dma_restart);
+ int (*ioctl) (int dev, unsigned int cmd, unsigned int arg, int local);
+ int (*prepare_for_input) (int dev, int bufsize, int nbufs);
+ int (*prepare_for_output) (int dev, int bufsize, int nbufs);
+ void (*reset) (int dev);
+ void (*halt_xfer) (int dev);
+ int (*has_output_drained)(int dev);
+ void (*copy_from_user)(int dev, char *localbuf, int localoffs,
+ snd_rw_buf *userbuf, int useroffs, int len);
+};
+
+struct mixer_operations {
+ int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
+};
+
+struct synth_operations {
+ struct synth_info *info;
+ int synth_type;
+ int synth_subtype;
+
+ int (*open) (int dev, int mode);
+ void (*close) (int dev);
+ int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
+ int (*kill_note) (int dev, int voice, int velocity);
+ int (*start_note) (int dev, int voice, int note, int velocity);
+ int (*set_instr) (int dev, int voice, int instr);
+ void (*reset) (int dev);
+ void (*hw_control) (int dev, unsigned char *event);
+ int (*load_patch) (int dev, int format, snd_rw_buf *addr,
+ int offs, int count, int pmgr_flag);
+ void (*aftertouch) (int dev, int voice, int pressure);
+ void (*controller) (int dev, int voice, int ctrl_num, int value);
+ void (*panning) (int dev, int voice, int value);
+ void (*volume_method) (int dev, int mode);
+ int (*pmgr_interface) (int dev, struct patmgr_info *info);
+};
+
+struct midi_operations {
+ struct midi_info info;
+ int (*open) (int dev, int mode,
+ void (*inputintr)(int dev, unsigned char data),
+ void (*outputintr)(int dev)
+ );
+ void (*close) (int dev);
+ int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
+ int (*putc) (int dev, unsigned char data);
+ int (*start_read) (int dev);
+ int (*end_read) (int dev);
+ void (*kick)(int dev);
+ int (*command) (int dev, unsigned char data);
+ int (*buffer_status) (int dev);
+};
+
+/** UWM -- new structure for MIDI **/
+
+struct generic_midi_operations {
+ struct midi_info info;
+ int (*open) (int dev, int mode);
+ void (*close) (int dev);
+ int (*write) (int dev, snd_rw_buf *data);
+ int (*read) (int dev, snd_rw_buf *data);
+};
+
+#ifndef ALL_EXTERNAL_TO_ME
+
+#ifdef _MIDI_TABLE_C_
+
+/** UWM **/
+ struct generic_midi_operations * generic_midi_devs[MAX_MIDI_DEV] = {NULL};
+ int num_generic_midis = 0, pro_midi_dev = 0;
+
+ struct generic_midi_info midi_supported[] = {
+
+#ifndef EXCLUDE_PRO_MIDI
+ {"ProAudioSpectrum MV101",pro_midi_attach}
+#endif
+ };
+
+ int num_midi_drivers =
+ sizeof (midi_supported) / sizeof(struct generic_midi_info);
+
+#endif
+
+
+#ifdef _DEV_TABLE_C_
+ struct audio_operations * dsp_devs[MAX_DSP_DEV] = {NULL}; int num_dspdevs = 0;
+ struct mixer_operations * mixer_devs[MAX_MIXER_DEV] = {NULL}; int num_mixers = 0;
+ struct synth_operations * synth_devs[MAX_SYNTH_DEV] = {NULL}; int num_synths = 0;
+ struct midi_operations * midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0;
+
+
+# ifndef EXCLUDE_MPU401
+ int mpu401_dev = 0;
+# endif
+
+/*
+ * Note! The detection order is significant. Don't change it.
+ */
+
+ struct card_info supported_drivers[] = {
+#if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI)
+ {SNDCARD_MPU401,"Roland MPU-401", attach_mpu401, probe_mpu401,
+ {MPU_BASE, MPU_IRQ, 0}, SND_DEFAULT_ENABLE},
+#endif
+
+#ifndef EXCLUDE_PAS
+ {SNDCARD_PAS, "ProAudioSpectrum", attach_pas_card, probe_pas,
+ {PAS_BASE, PAS_IRQ, PAS_DMA}, SND_DEFAULT_ENABLE},
+#endif
+
+#ifndef EXCLUDE_SB
+ {SNDCARD_SB, "SoundBlaster", attach_sb_card, probe_sb,
+ {SBC_BASE, SBC_IRQ, SBC_DMA}, SND_DEFAULT_ENABLE},
+#endif
+
+#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SBPRO)
+#ifndef EXCLUDE_AUDIO
+ {SNDCARD_SB16, "SoundBlaster16", sb16_dsp_init, sb16_dsp_detect,
+ {SBC_BASE, SBC_IRQ, SB16_DMA}, SND_DEFAULT_ENABLE},
+#endif
+#ifndef EXCLUDE_MIDI
+ {SNDCARD_SB16MIDI,"SB16 MPU-401", attach_sb16midi, probe_sb16midi,
+ {SB16MIDI_BASE, SBC_IRQ, 0}, SND_DEFAULT_ENABLE},
+#endif
+#endif
+
+#ifndef EXCLUDE_GUS
+ {SNDCARD_GUS, "Gravis Ultrasound", attach_gus_card, probe_gus,
+ {GUS_BASE, GUS_IRQ, GUS_DMA}, SND_DEFAULT_ENABLE},
+#endif
+
+#ifndef EXCLUDE_YM3812
+ {SNDCARD_ADLIB, "AdLib", attach_adlib_card, probe_adlib,
+ {FM_MONO, 0, 0}, SND_DEFAULT_ENABLE},
+#endif
+ {0, "*?*", NULL, 0}
+ };
+
+ int num_sound_drivers =
+ sizeof(supported_drivers) / sizeof (struct card_info);
+
+
+# ifndef EXCLUDE_AUDIO
+ int sound_buffcounts[MAX_DSP_DEV] = {0};
+ long sound_buffsizes[MAX_DSP_DEV] = {0};
+ int sound_dsp_dmachan[MAX_DSP_DEV] = {0};
+ int sound_dma_automode[MAX_DSP_DEV] = {0};
+# endif
+#else
+ extern struct audio_operations * dsp_devs[MAX_DSP_DEV]; int num_dspdevs;
+ extern struct mixer_operations * mixer_devs[MAX_MIXER_DEV]; extern int num_mixers;
+ extern struct synth_operations * synth_devs[MAX_SYNTH_DEV]; extern int num_synths;
+ extern struct midi_operations * midi_devs[MAX_MIDI_DEV]; extern int num_midis;
+# ifndef EXCLUDE_MPU401
+ extern int mpu401_dev;
+# endif
+
+ extern struct card_info supported_drivers[];
+ extern int num_sound_drivers;
+
+# ifndef EXCLUDE_AUDIO
+ extern int sound_buffcounts[MAX_DSP_DEV];
+ extern long sound_buffsizes[MAX_DSP_DEV];
+ extern int sound_dsp_dmachan[MAX_DSP_DEV];
+ extern int sound_dma_automode[MAX_DSP_DEV];
+# endif
+
+#endif
+
+long sndtable_init(long mem_start);
+int sndtable_get_cardcount (void);
+long CMIDI_init(long mem_start); /* */
+struct address_info *sound_getconf(int card_type);
+void sound_chconf(int card_type, int ioaddr, int irq, int dma);
+#endif
+
+#endif
+
+/* If external to me.... :) */
+
+#ifdef ALL_EXTERNAL_TO_ME
+
+ extern struct audio_operations * dsp_devs[MAX_DSP_DEV]; int num_dspdevs;
+ extern struct mixer_operations * mixer_devs[MAX_MIXER_DEV]; extern int num_mixers;
+ extern struct synth_operations * synth_devs[MAX_SYNTH_DEV]; extern int num_synths;
+ extern struct midi_operations * midi_devs[MAX_MIDI_DEV]; extern int num_midis;
+ extern struct generic_midi_operations *generic_midi_devs[];
+ extern int num_generic_midis, pro_midi_dev;
+
+#ifndef EXCLUDE_MPU401
+ extern int mpu401_dev;
+#endif
+
+ extern struct generic_midi_info midi_supported[];
+ extern struct card_info supported_drivers[];
+ extern int num_sound_drivers;
+ extern int num_midi_drivers;
+#ifndef EXCLUDE_AUDIO
+ extern int sound_buffcounts[MAX_DSP_DEV];
+ extern long sound_buffsizes[MAX_DSP_DEV];
+ extern int sound_dsp_dmachan[MAX_DSP_DEV];
+ extern int sound_dma_automode[MAX_DSP_DEV];
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/dmabuf.c b/sys/i386/isa/sound/dmabuf.c
new file mode 100644
index 0000000..851a70a1
--- /dev/null
+++ b/sys/i386/isa/sound/dmabuf.c
@@ -0,0 +1,902 @@
+/*
+ * sound/dmabuf.c
+ *
+ * The DMA buffer manager for digitized voice applications
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "sound_calls.h"
+
+#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS)
+
+#define MAX_SUB_BUFFERS (32*MAX_REALTIME_FACTOR)
+
+/*
+ * The DSP channel can be used either for input or output. Variable
+ * 'dma_mode' will be set when the program calls read or write first time
+ * after open. Current version doesn't support mode changes without closing
+ * and reopening the device. Support for this feature may be implemented in a
+ * future version of this driver.
+ */
+
+#define DMODE_NONE 0
+#define DMODE_OUTPUT 1
+#define DMODE_INPUT 2
+
+DEFINE_WAIT_QUEUES (dev_sleeper[MAX_DSP_DEV], dev_sleep_flag[MAX_DSP_DEV]);
+
+static int dma_mode[MAX_DSP_DEV] =
+{0}; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */
+
+static volatile int dmabuf_interrupted[MAX_DSP_DEV] =
+{0};
+
+/*
+ * Pointers to raw buffers
+ */
+
+char *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT] =
+{
+ {NULL}};
+unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT];
+int snd_raw_count[MAX_DSP_DEV];
+
+/*
+ * Device state tables
+ */
+
+static int dev_busy[MAX_DSP_DEV];
+static int dev_needs_restart[MAX_DSP_DEV];
+static int dev_modes[MAX_DSP_DEV];
+static int dev_active[MAX_DSP_DEV];
+static int dev_started[MAX_DSP_DEV];
+static int dev_qlen[MAX_DSP_DEV];
+static int dev_qhead[MAX_DSP_DEV];
+static int dev_qtail[MAX_DSP_DEV];
+static int dev_underrun[MAX_DSP_DEV];
+static int bufferalloc_done[MAX_DSP_DEV] =
+{0};
+
+/*
+ * Logical buffers for each devices
+ */
+
+static int dev_nbufs[MAX_DSP_DEV]; /* # of logical buffers ( >=
+
+ * sound_buffcounts[dev] */
+static int dev_counts[MAX_DSP_DEV][MAX_SUB_BUFFERS];
+static int dev_subdivision[MAX_DSP_DEV];
+static unsigned long dev_buf_phys[MAX_DSP_DEV][MAX_SUB_BUFFERS];
+static char *dev_buf[MAX_DSP_DEV][MAX_SUB_BUFFERS] =
+{
+ {NULL}};
+static int dev_buffsize[MAX_DSP_DEV];
+
+static void
+reorganize_buffers (int dev)
+{
+ /*
+ * This routine breaks the physical device buffers to logical ones.
+ */
+
+ unsigned i, p, n;
+ unsigned sr, nc, sz, bsz;
+
+ sr = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_RATE, 0, 1);
+ nc = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1);
+ sz = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_BITS, 0, 1);
+
+ if (sr < 1 || nc < 1 || sz < 1)
+ {
+ printk ("SOUND: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);
+ sr = DSP_DEFAULT_SPEED;
+ nc = 1;
+ sz = 8;
+ }
+
+ sz /= 8; /* Convert # of bits -> # of bytes */
+
+ sz = sr * nc * sz;
+
+ /*
+ * Compute a buffer size not exeeding 1 second.
+ */
+
+ bsz = sound_buffsizes[dev];
+
+ while (bsz > sz)
+ bsz >>= 1; /* Divide by 2 */
+
+ if (sound_buffcounts[dev] == 1 && bsz == sound_buffsizes[dev])
+ bsz >>= 1; /* Need at least 2 buffers */
+
+ if (dev_subdivision[dev] == 0)
+ dev_subdivision[dev] = 1; /* Default value */
+
+ bsz /= dev_subdivision[dev]; /* Use smaller buffers */
+
+ if (bsz == 0)
+ bsz = 4096; /* Just a sanity check */
+
+ while ((sound_buffsizes[dev] * sound_buffcounts[dev]) / bsz > MAX_SUB_BUFFERS)
+ bsz <<= 1; /* Too much buffers */
+
+ dev_buffsize[dev] = bsz;
+ n = 0;
+
+ /*
+ * Now computing addresses for the logical buffers
+ */
+
+ for (i = 0; i < snd_raw_count[dev]; i++)
+ {
+ p = 0;
+
+ while ((p + bsz) <= sound_buffsizes[dev])
+ {
+ dev_buf[dev][n] = snd_raw_buf[dev][i] + p;
+ dev_buf_phys[dev][n] = snd_raw_buf_phys[dev][i] + p;
+ p += bsz;
+ n++;
+ }
+ }
+
+ dev_nbufs[dev] = n;
+
+ for (i = 0; i < dev_nbufs[dev]; i++)
+ {
+ dev_counts[dev][i] = 0;
+ }
+
+ bufferalloc_done[dev] = 1;
+}
+
+static void
+dma_init_buffers (int dev)
+{
+ RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]);
+ dev_underrun[dev] = 0;
+
+ dev_busy[dev] = 1;
+
+ bufferalloc_done[dev] = 0;
+
+ dev_active[dev] = dev_qlen[dev] = dev_qtail[dev] = dev_qhead[dev] = 0;
+ dev_needs_restart[dev] = dev_started[dev] = 0;
+ dma_mode[dev] = DMODE_NONE;
+}
+
+int
+DMAbuf_open (int dev, int mode)
+{
+ int retval;
+
+ if (dev >= num_dspdevs)
+ {
+ printk ("PCM device %d not installed.\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ if (dev_busy[dev])
+ return RET_ERROR (EBUSY);
+
+ if (!dsp_devs[dev])
+ {
+ printk ("DSP device %d not initialized\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+#ifdef USE_RUNTIME_DMAMEM
+ sound_dma_malloc (dev);
+#endif
+
+ if (snd_raw_buf[dev][0] == NULL)
+ return RET_ERROR (ENOSPC); /* Memory allocation failed during boot */
+
+ if ((retval = dsp_devs[dev]->open (dev, mode)) < 0)
+ return retval;
+
+ dev_modes[dev] = mode;
+ dev_subdivision[dev] = 0;
+
+ dma_init_buffers (dev);
+ dsp_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_BITS, 8, 1);
+ dsp_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_CHANNELS, 1, 1);
+ dsp_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_RATE, DSP_DEFAULT_SPEED, 1);
+
+ return 0;
+}
+
+static void
+dma_reset (int dev)
+{
+ int retval;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ dsp_devs[dev]->reset (dev);
+ dsp_devs[dev]->close (dev);
+
+ if ((retval = dsp_devs[dev]->open (dev, dev_modes[dev])) < 0)
+ printk ("Sound: Reset failed - Can't reopen device\n");
+ RESTORE_INTR (flags);
+
+ dma_init_buffers (dev);
+ reorganize_buffers (dev);
+}
+
+static int
+dma_sync (int dev)
+{
+ unsigned long flags;
+
+ if (dma_mode[dev] == DMODE_OUTPUT)
+ {
+ DISABLE_INTR (flags);
+
+ while ((!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) ||
+ dmabuf_interrupted[dev]))
+ && dev_qlen[dev])
+ {
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 10 * HZ);
+ if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
+ return dev_qlen[dev];
+ }
+ RESTORE_INTR (flags);
+
+ /*
+ * Some devices such as GUS have huge amount of on board RAM for the
+ * audio data. We have to wait util the device has finished playing.
+ */
+
+ DISABLE_INTR (flags);
+ if (dsp_devs[dev]->has_output_drained) /* Device has hidden buffers */
+ {
+ while (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) ||
+ dmabuf_interrupted[dev])
+ && !dsp_devs[dev]->has_output_drained (dev))
+ {
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], HZ / 4);
+ }
+ }
+ RESTORE_INTR (flags);
+ }
+ return dev_qlen[dev];
+}
+
+int
+DMAbuf_release (int dev, int mode)
+{
+
+ if (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) ||
+ dmabuf_interrupted[dev])
+ && (dma_mode[dev] == DMODE_OUTPUT))
+ {
+ dma_sync (dev);
+ }
+
+#ifdef USE_RUNTIME_DMAMEM
+ sound_dma_free (dev);
+#endif
+
+ dsp_devs[dev]->reset (dev);
+
+ dsp_devs[dev]->close (dev);
+
+ dma_mode[dev] = DMODE_NONE;
+ dev_busy[dev] = 0;
+
+ return 0;
+}
+
+int
+DMAbuf_getrdbuffer (int dev, char **buf, int *len)
+{
+ unsigned long flags;
+ int err = EIO;
+
+ DISABLE_INTR (flags);
+ if (!dev_qlen[dev])
+ {
+ if (dev_needs_restart[dev])
+ {
+ dma_reset (dev);
+ dev_needs_restart[dev] = 0;
+ }
+
+ if (dma_mode[dev] == DMODE_OUTPUT) /* Was output -> direction change */
+ {
+ dma_sync (dev);
+ dma_reset (dev);
+ dma_mode[dev] = DMODE_NONE;
+ }
+
+ if (!bufferalloc_done[dev])
+ reorganize_buffers (dev);
+
+ if (!dma_mode[dev])
+ {
+ int err;
+
+ if ((err = dsp_devs[dev]->prepare_for_input (dev,
+ dev_buffsize[dev], dev_nbufs[dev])) < 0)
+ {
+ RESTORE_INTR (flags);
+ return err;
+ }
+ dma_mode[dev] = DMODE_INPUT;
+ }
+
+ if (!dev_active[dev])
+ {
+ dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]],
+ dev_buffsize[dev], 0,
+ !sound_dma_automode[dev] ||
+ !dev_started[dev]);
+ dev_active[dev] = 1;
+ dev_started[dev] = 1;
+ }
+
+ /* Wait for the next block */
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ);
+ if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ printk ("Sound: DMA timed out - IRQ/DRQ config error?\n");
+ err = EIO;
+ SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ else
+ err = EINTR;
+ }
+ RESTORE_INTR (flags);
+
+ if (!dev_qlen[dev])
+ return RET_ERROR (err);
+
+ *buf = &dev_buf[dev][dev_qhead[dev]][dev_counts[dev][dev_qhead[dev]]];
+ *len = dev_buffsize[dev] - dev_counts[dev][dev_qhead[dev]];
+
+ return dev_qhead[dev];
+}
+
+int
+DMAbuf_rmchars (int dev, int buff_no, int c)
+{
+ int p = dev_counts[dev][dev_qhead[dev]] + c;
+
+ if (p >= dev_buffsize[dev])
+ { /* This buffer is now empty */
+ dev_counts[dev][dev_qhead[dev]] = 0;
+ dev_qlen[dev]--;
+ dev_qhead[dev] = (dev_qhead[dev] + 1) % dev_nbufs[dev];
+ }
+ else
+ dev_counts[dev][dev_qhead[dev]] = p;
+
+ return 0;
+}
+
+int
+DMAbuf_read (int dev, snd_rw_buf * user_buf, int count)
+{
+ char *dmabuf;
+ int buff_no, c, err;
+
+ /*
+ * This routine returns at most 'count' bytes from the dsp input buffers.
+ * Returns negative value if there is an error.
+ */
+
+ if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &c)) < 0)
+ return buff_no;
+
+ if (c > count)
+ c = count;
+
+ COPY_TO_USER (user_buf, 0, dmabuf, c);
+
+ if ((err = DMAbuf_rmchars (dev, buff_no, c)) < 0)
+ return err;
+ return c;
+
+}
+
+int
+DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ switch (cmd)
+ {
+ case SNDCTL_DSP_RESET:
+ dma_reset (dev);
+ return 0;
+ break;
+
+ case SNDCTL_DSP_SYNC:
+ dma_sync (dev);
+ dma_reset (dev);
+ return 0;
+ break;
+
+ case SNDCTL_DSP_GETBLKSIZE:
+ if (!bufferalloc_done[dev])
+ reorganize_buffers (dev);
+
+ return IOCTL_OUT (arg, dev_buffsize[dev]);
+ break;
+
+ case SNDCTL_DSP_SUBDIVIDE:
+ {
+ int fact = IOCTL_IN (arg);
+
+ if (fact == 0)
+ {
+ fact = dev_subdivision[dev];
+ if (fact == 0)
+ fact = 1;
+ return IOCTL_OUT (arg, fact);
+ }
+
+ if (dev_subdivision[dev] != 0) /* Too late to change */
+ return RET_ERROR (EINVAL);
+
+ if (fact > MAX_REALTIME_FACTOR)
+ return RET_ERROR (EINVAL);
+
+ if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
+ return RET_ERROR (EINVAL);
+
+ dev_subdivision[dev] = fact;
+ return IOCTL_OUT (arg, fact);
+ }
+ break;
+
+ default:
+ return dsp_devs[dev]->ioctl (dev, cmd, arg, local);
+ }
+
+ /* NOTREACHED */
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_getwrbuffer (int dev, char **buf, int *size)
+{
+ unsigned long flags;
+ int err = EIO;
+
+ if (dma_mode[dev] == DMODE_INPUT) /* Was input -> Direction change */
+ {
+ dma_reset (dev);
+ dma_mode[dev] = DMODE_NONE;
+ }
+ else if (dev_needs_restart[dev]) /* Restart buffering */
+ {
+ dma_sync (dev);
+ dma_reset (dev);
+ }
+
+ dev_needs_restart[dev] = 0;
+
+ if (!bufferalloc_done[dev])
+ reorganize_buffers (dev);
+
+ if (!dma_mode[dev])
+ {
+ int err;
+
+ dma_mode[dev] = DMODE_OUTPUT;
+ if ((err = dsp_devs[dev]->prepare_for_output (dev,
+ dev_buffsize[dev], dev_nbufs[dev])) < 0)
+ return err;
+ }
+
+
+ DISABLE_INTR (flags);
+
+ RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]);
+
+ if (dev_qlen[dev] == dev_nbufs[dev])
+ {
+ if (!dev_active[dev])
+ {
+ printk ("Soundcard warning: DMA not activated %d/%d\n",
+ dev_qlen[dev], dev_nbufs[dev]);
+ return RET_ERROR (EIO);
+ }
+
+ /* Wait for free space */
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ);
+ if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ printk ("Sound: DMA timed out - IRQ/DRQ config error?\n");
+ err = EIO;
+ SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ else if (PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ err = EINTR;
+ }
+ RESTORE_INTR (flags);
+
+ if (dev_qlen[dev] == dev_nbufs[dev])
+ return RET_ERROR (err); /* We have got signal (?) */
+
+ *buf = dev_buf[dev][dev_qtail[dev]];
+ *size = dev_buffsize[dev];
+ dev_counts[dev][dev_qtail[dev]] = 0;
+
+ return dev_qtail[dev];
+}
+
+int
+DMAbuf_start_output (int dev, int buff_no, int l)
+{
+ if (buff_no != dev_qtail[dev])
+ printk ("Soundcard warning: DMA buffers out of sync %d != %d\n", buff_no, dev_qtail[dev]);
+
+ dev_qlen[dev]++;
+
+ dev_counts[dev][dev_qtail[dev]] = l;
+
+ dev_needs_restart[dev] = (l != dev_buffsize[dev]) &&
+ (sound_dma_automode[dev] || dsp_devs[dev]->flags & NEEDS_RESTART);
+
+ dev_qtail[dev] = (dev_qtail[dev] + 1) % dev_nbufs[dev];
+
+ if (!dev_active[dev])
+ {
+ dev_active[dev] = 1;
+ dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]],
+ dev_counts[dev][dev_qhead[dev]], 0,
+ !sound_dma_automode[dev] || !dev_started[dev]);
+ dev_started[dev] = 1;
+ }
+
+ return 0;
+}
+
+int
+DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+{
+ int chan = sound_dsp_dmachan[dev];
+ unsigned long flags;
+
+ /*
+ * This function is not as portable as it should be.
+ */
+
+ /*
+ * The count must be one less than the actual size. This is handled by
+ * set_dma_addr()
+ */
+
+ if (sound_dma_automode[dev])
+ { /* Auto restart mode. Transfer the whole
+ * buffer */
+#ifdef linux
+ DISABLE_INTR (flags);
+ disable_dma (chan);
+ clear_dma_ff (chan);
+ set_dma_mode (chan, dma_mode | DMA_AUTOINIT);
+ set_dma_addr (chan, snd_raw_buf_phys[dev][0]);
+ set_dma_count (chan, sound_buffsizes[dev]);
+ enable_dma (chan);
+ RESTORE_INTR (flags);
+#else /* linux */
+
+#ifdef __386BSD__
+ printk ("sound: Invalid DMA mode for device %d\n", dev);
+
+ isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
+ (caddr_t)snd_raw_buf_phys[dev][0],
+ sound_buffsizes[dev],
+ chan);
+#else /* __386BSD__ */
+#if defined(ISC) || defined(SCO) || defined(SVR42)
+#ifndef DMAMODE_AUTO
+ printk ("sound: Invalid DMA mode for device %d\n", dev);
+#endif /* DMAMODE_AUTO */
+ dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode)
+#ifdef DMAMODE_AUTO
+ | DMAMODE_AUTO
+#endif /* DMAMODE_AUTO */
+ ,
+ snd_raw_buf_phys[dev][0], count);
+ dma_enable (chan);
+#else /* SYSV */
+#error This routine is not valid for this OS.
+#endif /* SYSV */
+#endif /* __386BSD__ */
+
+#endif /* linux */
+ }
+ else
+ {
+#ifdef linux
+ DISABLE_INTR (flags);
+ disable_dma (chan);
+ clear_dma_ff (chan);
+ set_dma_mode (chan, dma_mode);
+ set_dma_addr (chan, physaddr);
+ set_dma_count (chan, count);
+ enable_dma (chan);
+ RESTORE_INTR (flags);
+#else /* linux */
+#ifdef __386BSD__
+ isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
+ (caddr_t)physaddr,
+ count,
+ chan);
+#else /* __386BSD__ */
+
+#if defined(ISC) || defined(SCO) || defined(SVR42)
+ dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode),
+ physaddr, count);
+ dma_enable (chan);
+#else /* SYSV */
+#error This routine is not valid for this OS.
+#endif /* SYSV */
+#endif /* __386BSD__ */
+
+#endif /* linux */
+ }
+
+ return count;
+}
+
+long
+DMAbuf_init (long mem_start)
+{
+ int i;
+
+ /*
+ * In this version the DMA buffer allocation is done by sound_mem_init()
+ * which is called by init/main.c
+ */
+
+ for (i = 0; i < MAX_DSP_DEV; i++)
+ {
+ dev_qlen[i] = 0;
+ dev_qhead[i] = 0;
+ dev_qtail[i] = 0;
+ dev_active[i] = 0;
+ dev_busy[i] = 0;
+ bufferalloc_done[i] = 0;
+ }
+
+ return mem_start;
+}
+
+void
+DMAbuf_outputintr (int dev, int underrun_flag)
+{
+ unsigned long flags;
+
+ dev_qlen[dev]--;
+ dev_qhead[dev] = (dev_qhead[dev] + 1) % dev_nbufs[dev];
+ dev_active[dev] = 0;
+
+ if (dev_qlen[dev])
+ {
+ dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]],
+ dev_counts[dev][dev_qhead[dev]], 1,
+ !sound_dma_automode[dev]);
+ dev_active[dev] = 1;
+ }
+ else if (underrun_flag)
+ {
+ dev_underrun[dev]++;
+ dsp_devs[dev]->halt_xfer (dev);
+ dev_needs_restart[dev] = (sound_dma_automode[dev] ||
+ dsp_devs[dev]->flags & NEEDS_RESTART);
+ }
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ RESTORE_INTR (flags);
+}
+
+void
+DMAbuf_inputintr (int dev)
+{
+ unsigned long flags;
+
+ if (!dev_busy[dev])
+ {
+ dsp_devs[dev]->close (dev);
+ }
+ else if (dev_qlen[dev] == (dev_nbufs[dev] - 1))
+ {
+ printk ("Sound: Recording overrun\n");
+ dev_underrun[dev]++;
+ dsp_devs[dev]->halt_xfer (dev);
+ dev_active[dev] = 0;
+ dev_needs_restart[dev] = sound_dma_automode[dev];
+ }
+ else
+ {
+ dev_qlen[dev]++;
+ dev_qtail[dev] = (dev_qtail[dev] + 1) % dev_nbufs[dev];
+
+ dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]],
+ dev_buffsize[dev], 1,
+ !sound_dma_automode[dev]);
+ dev_active[dev] = 1;
+ }
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ RESTORE_INTR (flags);
+}
+
+int
+DMAbuf_open_dma (int dev)
+{
+ unsigned long flags;
+ int chan = sound_dsp_dmachan[dev];
+
+ if (ALLOC_DMA_CHN (chan))
+ {
+ printk ("Unable to grab DMA%d for the audio driver\n", chan);
+ return 0;
+ }
+
+ DISABLE_INTR (flags);
+#ifdef linux
+ disable_dma (chan);
+ clear_dma_ff (chan);
+#endif
+ RESTORE_INTR (flags);
+
+ return 1;
+}
+
+void
+DMAbuf_close_dma (int dev)
+{
+ int chan = sound_dsp_dmachan[dev];
+
+ DMAbuf_reset_dma (chan);
+ RELEASE_DMA_CHN (chan);
+}
+
+void
+DMAbuf_reset_dma (int chan)
+{
+}
+
+/*
+ * The sound_mem_init() is called by mem_init() immediately after mem_map is
+ * initialized and before free_page_list is created.
+ *
+ * This routine allocates DMA buffers at the end of available physical memory (
+ * <16M) and marks pages reserved at mem_map.
+ */
+
+#else
+/* Stub versions if audio services not included */
+
+int
+DMAbuf_open (int dev, int mode)
+{
+ return RET_ERROR (ENXIO);
+}
+
+int
+DMAbuf_release (int dev, int mode)
+{
+ return 0;
+}
+
+int
+DMAbuf_read (int dev, snd_rw_buf * user_buf, int count)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_getwrbuffer (int dev, char **buf, int *size)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_getrdbuffer (int dev, char **buf, int *len)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_rmchars (int dev, int buff_no, int c)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_start_output (int dev, int buff_no, int l)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ return RET_ERROR (EIO);
+}
+
+long
+DMAbuf_init (long mem_start)
+{
+ return mem_start;
+}
+
+int
+DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_open_dma (int chan)
+{
+ return RET_ERROR (ENXIO);
+}
+
+void
+DMAbuf_close_dma (int chan)
+{
+ return;
+}
+
+void
+DMAbuf_reset_dma (int chan)
+{
+ return;
+}
+
+void
+DMAbuf_inputintr (int dev)
+{
+ return;
+}
+
+void
+DMAbuf_outputintr (int dev, int underrun_flag)
+{
+ return;
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/finetune.h b/sys/i386/isa/sound/finetune.h
new file mode 100644
index 0000000..b86a0eb
--- /dev/null
+++ b/sys/i386/isa/sound/finetune.h
@@ -0,0 +1,49 @@
+#ifdef SEQUENCER_C
+/*
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+ unsigned short finetune_table[128] =
+ {
+/* 0 */ 9439, 9447, 9456, 9464, 9473, 9481, 9490, 9499,
+/* 8 */ 9507, 9516, 9524, 9533, 9542, 9550, 9559, 9567,
+/* 16 */ 9576, 9585, 9593, 9602, 9611, 9619, 9628, 9637,
+/* 24 */ 9645, 9654, 9663, 9672, 9680, 9689, 9698, 9707,
+/* 32 */ 9715, 9724, 9733, 9742, 9750, 9759, 9768, 9777,
+/* 40 */ 9786, 9795, 9803, 9812, 9821, 9830, 9839, 9848,
+/* 48 */ 9857, 9866, 9874, 9883, 9892, 9901, 9910, 9919,
+/* 56 */ 9928, 9937, 9946, 9955, 9964, 9973, 9982, 9991,
+/* 64 */ 10000, 10009, 10018, 10027, 10036, 10045, 10054, 10063,
+/* 72 */ 10072, 10082, 10091, 10100, 10109, 10118, 10127, 10136,
+/* 80 */ 10145, 10155, 10164, 10173, 10182, 10191, 10201, 10210,
+/* 88 */ 10219, 10228, 10237, 10247, 10256, 10265, 10274, 10284,
+/* 96 */ 10293, 10302, 10312, 10321, 10330, 10340, 10349, 10358,
+/* 104 */ 10368, 10377, 10386, 10396, 10405, 10415, 10424, 10433,
+/* 112 */ 10443, 10452, 10462, 10471, 10481, 10490, 10499, 10509,
+/* 120 */ 10518, 10528, 10537, 10547, 10556, 10566, 10576, 10585
+ };
+#else
+ extern unsigned short finetune_table[128];
+#endif
diff --git a/sys/i386/isa/sound/gus_card.c b/sys/i386/isa/sound/gus_card.c
new file mode 100644
index 0000000..c7cfc0a
--- /dev/null
+++ b/sys/i386/isa/sound/gus_card.c
@@ -0,0 +1,142 @@
+/*
+ * sound/gus_card.c
+ *
+ * Detection routine for the Gravis Ultrasound.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS)
+
+#include "gus_hw.h"
+
+void gusintr (int);
+
+int gus_base, gus_irq, gus_dma;
+
+long
+attach_gus_card (long mem_start, struct address_info *hw_config)
+{
+ int io_addr;
+
+ snd_set_irq_handler (hw_config->irq, gusintr);
+
+ if (gus_wave_detect (hw_config->io_base)) /* Try first the default */
+ {
+ mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma);
+#ifndef EXCLUDE_MIDI
+ mem_start = gus_midi_init (mem_start);
+#endif
+ return mem_start;
+ }
+
+#ifndef EXCLUDE_GUS_IODETECT
+
+ /*
+ * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6)
+ */
+
+ for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10)
+ if (io_addr != hw_config->io_base) /* Already tested */
+ if (gus_wave_detect (io_addr))
+ {
+ printk (" WARNING! GUS found at %x, config was %x ", io_addr, hw_config->io_base);
+ mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma);
+#ifndef EXCLUDE_MIDI
+ mem_start = gus_midi_init (mem_start);
+#endif
+ return mem_start;
+ }
+
+#endif
+
+ return mem_start; /* Not detected */
+}
+
+int
+probe_gus (struct address_info *hw_config)
+{
+ int io_addr;
+
+ if (gus_wave_detect (hw_config->io_base))
+ return 1;
+
+#ifndef EXCLUDE_GUS_IODETECT
+
+ /*
+ * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6)
+ */
+
+ for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10)
+ if (io_addr != hw_config->io_base) /* Already tested */
+ if (gus_wave_detect (io_addr))
+ return 1;
+
+#endif
+
+ return 0;
+}
+
+void
+gusintr (int unit)
+{
+ unsigned char src;
+
+#ifdef linux
+ sti ();
+#endif
+
+ while (1)
+ {
+ if (!(src = INB (u_IrqStatus)))
+ return;
+
+ if (src & DMA_TC_IRQ)
+ {
+ guswave_dma_irq ();
+ }
+
+ if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ))
+ {
+#ifndef EXCLUDE_MIDI
+ gus_midi_interrupt (0);
+#endif
+ }
+
+ if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ))
+ {
+ printk ("T");
+ gus_write8 (0x45, 0); /* Timer control */
+ }
+
+ if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ))
+ {
+ gus_voice_irq ();
+ }
+ }
+}
+
+#endif
diff --git a/sys/i386/isa/sound/gus_hw.h b/sys/i386/isa/sound/gus_hw.h
new file mode 100644
index 0000000..f97a0b8
--- /dev/null
+++ b/sys/i386/isa/sound/gus_hw.h
@@ -0,0 +1,50 @@
+
+/*
+ * I/O addresses
+ */
+
+#define u_Base (gus_base + 0x000)
+#define u_Mixer u_Base
+#define u_Status (gus_base + 0x006)
+#define u_TimerControl (gus_base + 0x008)
+#define u_TimerData (gus_base + 0x009)
+#define u_IRQDMAControl (gus_base + 0x00b)
+#define u_MidiControl (gus_base + 0x100)
+#define MIDI_RESET 0x03
+#define MIDI_ENABLE_XMIT 0x20
+#define MIDI_ENABLE_RCV 0x80
+#define u_MidiStatus u_MidiControl
+#define MIDI_RCV_FULL 0x01
+#define MIDI_XMIT_EMPTY 0x02
+#define MIDI_FRAME_ERR 0x10
+#define MIDI_OVERRUN 0x20
+#define MIDI_IRQ_PEND 0x80
+#define u_MidiData (gus_base + 0x101)
+#define u_Voice (gus_base + 0x102)
+#define u_Command (gus_base + 0x103)
+#define u_DataLo (gus_base + 0x104)
+#define u_DataHi (gus_base + 0x105)
+#define u_MixData (gus_base + 0x106) /* Rev. 3.7+ mixing */
+#define u_MixSelect (gus_base + 0x506) /* registers. */
+#define u_IrqStatus u_Status
+# define MIDI_TX_IRQ 0x01 /* pending MIDI xmit IRQ */
+# define MIDI_RX_IRQ 0x02 /* pending MIDI recv IRQ */
+# define GF1_TIMER1_IRQ 0x04 /* general purpose timer */
+# define GF1_TIMER2_IRQ 0x08 /* general purpose timer */
+# define WAVETABLE_IRQ 0x20 /* pending wavetable IRQ */
+# define ENVELOPE_IRQ 0x40 /* pending volume envelope IRQ */
+# define DMA_TC_IRQ 0x80 /* pending dma tc IRQ */
+
+#define ICS2101 1
+# define ICS_MIXDEVS 6
+# define DEV_MIC 0
+# define DEV_LINE 1
+# define DEV_CD 2
+# define DEV_GF1 3
+# define DEV_UNUSED 4
+# define DEV_VOL 5
+
+# define CHN_LEFT 0
+# define CHN_RIGHT 1
+#define CS4231 2
+#define u_DRAMIO (gus_base + 0x107)
diff --git a/sys/i386/isa/sound/gus_linearvol.h b/sys/i386/isa/sound/gus_linearvol.h
new file mode 100644
index 0000000..7ad0c30
--- /dev/null
+++ b/sys/i386/isa/sound/gus_linearvol.h
@@ -0,0 +1,18 @@
+static unsigned short gus_linearvol[128] = {
+ 0x0000, 0x08ff, 0x09ff, 0x0a80, 0x0aff, 0x0b40, 0x0b80, 0x0bc0,
+ 0x0bff, 0x0c20, 0x0c40, 0x0c60, 0x0c80, 0x0ca0, 0x0cc0, 0x0ce0,
+ 0x0cff, 0x0d10, 0x0d20, 0x0d30, 0x0d40, 0x0d50, 0x0d60, 0x0d70,
+ 0x0d80, 0x0d90, 0x0da0, 0x0db0, 0x0dc0, 0x0dd0, 0x0de0, 0x0df0,
+ 0x0dff, 0x0e08, 0x0e10, 0x0e18, 0x0e20, 0x0e28, 0x0e30, 0x0e38,
+ 0x0e40, 0x0e48, 0x0e50, 0x0e58, 0x0e60, 0x0e68, 0x0e70, 0x0e78,
+ 0x0e80, 0x0e88, 0x0e90, 0x0e98, 0x0ea0, 0x0ea8, 0x0eb0, 0x0eb8,
+ 0x0ec0, 0x0ec8, 0x0ed0, 0x0ed8, 0x0ee0, 0x0ee8, 0x0ef0, 0x0ef8,
+ 0x0eff, 0x0f04, 0x0f08, 0x0f0c, 0x0f10, 0x0f14, 0x0f18, 0x0f1c,
+ 0x0f20, 0x0f24, 0x0f28, 0x0f2c, 0x0f30, 0x0f34, 0x0f38, 0x0f3c,
+ 0x0f40, 0x0f44, 0x0f48, 0x0f4c, 0x0f50, 0x0f54, 0x0f58, 0x0f5c,
+ 0x0f60, 0x0f64, 0x0f68, 0x0f6c, 0x0f70, 0x0f74, 0x0f78, 0x0f7c,
+ 0x0f80, 0x0f84, 0x0f88, 0x0f8c, 0x0f90, 0x0f94, 0x0f98, 0x0f9c,
+ 0x0fa0, 0x0fa4, 0x0fa8, 0x0fac, 0x0fb0, 0x0fb4, 0x0fb8, 0x0fbc,
+ 0x0fc0, 0x0fc4, 0x0fc8, 0x0fcc, 0x0fd0, 0x0fd4, 0x0fd8, 0x0fdc,
+ 0x0fe0, 0x0fe4, 0x0fe8, 0x0fec, 0x0ff0, 0x0ff4, 0x0ff8, 0x0ffc
+};
diff --git a/sys/i386/isa/sound/gus_midi.c b/sys/i386/isa/sound/gus_midi.c
new file mode 100644
index 0000000..935c5c9
--- /dev/null
+++ b/sys/i386/isa/sound/gus_midi.c
@@ -0,0 +1,283 @@
+/*
+ * sound/gus2_midi.c
+ *
+ * The low level driver for the GUS Midi Interface.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "gus_hw.h"
+
+#if !defined(EXCLUDE_GUS) && !defined(EXCLUDE_MIDI)
+
+static int midi_busy = 0, input_opened = 0;
+static int my_dev;
+static int output_used = 0;
+static volatile unsigned char gus_midi_control;
+
+static void (*midi_input_intr) (int dev, unsigned char data);
+
+static unsigned char tmp_queue[256];
+static volatile int qlen;
+static volatile unsigned char qhead, qtail;
+extern int gus_base, gus_irq, gus_dma;
+
+#define GUS_MIDI_STATUS() INB(u_MidiStatus)
+
+static int
+gus_midi_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+
+ if (midi_busy)
+ {
+ printk ("GUS: Midi busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ OUTB (MIDI_RESET, u_MidiControl);
+ gus_delay ();
+
+ gus_midi_control = 0;
+ input_opened = 0;
+
+ if (mode == OPEN_READ || mode == OPEN_READWRITE)
+ {
+ gus_midi_control |= MIDI_ENABLE_RCV;
+ input_opened = 1;
+ }
+
+ if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+ {
+ gus_midi_control |= MIDI_ENABLE_XMIT;
+ }
+
+ OUTB (gus_midi_control, u_MidiControl); /* Enable */
+
+ midi_busy = 1;
+ qlen = qhead = qtail = output_used = 0;
+ midi_input_intr = input;
+
+ return 0;
+}
+
+static int
+dump_to_midi (unsigned char midi_byte)
+{
+ unsigned long flags;
+ int ok = 0;
+
+ output_used = 1;
+
+ DISABLE_INTR (flags);
+
+ if (GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY)
+ {
+ ok = 1;
+ OUTB (midi_byte, u_MidiData);
+ }
+ else
+ {
+ /* Enable Midi xmit interrupts (again) */
+ gus_midi_control |= MIDI_ENABLE_XMIT;
+ OUTB (gus_midi_control, u_MidiControl);
+ }
+
+ RESTORE_INTR (flags);
+ return ok;
+}
+
+static void
+gus_midi_close (int dev)
+{
+ /* Reset FIFO pointers, disable intrs */
+
+ OUTB (MIDI_RESET, u_MidiControl);
+ midi_busy = 0;
+}
+
+static int
+gus_midi_out (int dev, unsigned char midi_byte)
+{
+
+ unsigned long flags;
+
+ /*
+ * Drain the local queue first
+ */
+
+ DISABLE_INTR (flags);
+
+ while (qlen && dump_to_midi (tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+
+ RESTORE_INTR (flags);
+
+ /*
+ * Output the byte if the local queue is empty.
+ */
+
+ if (!qlen)
+ if (dump_to_midi (midi_byte))
+ return 1; /* OK */
+
+ /*
+ * Put to the local queue
+ */
+
+ if (qlen >= 256)
+ return 0; /* Local queue full */
+
+ DISABLE_INTR (flags);
+
+ tmp_queue[qtail] = midi_byte;
+ qlen++;
+ qtail++;
+
+ RESTORE_INTR (flags);
+
+ return 1;
+}
+
+static int
+gus_midi_start_read (int dev)
+{
+ return 0;
+}
+
+static int
+gus_midi_end_read (int dev)
+{
+ return 0;
+}
+
+static int
+gus_midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+gus_midi_kick (int dev)
+{
+}
+
+static int
+gus_midi_buffer_status (int dev)
+{
+ unsigned long flags;
+
+ if (!output_used)
+ return 0;
+
+ DISABLE_INTR (flags);
+
+ if (qlen && dump_to_midi (tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+
+ RESTORE_INTR (flags);
+
+ return (qlen > 0) | !(GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY);
+}
+
+static struct midi_operations gus_midi_operations =
+{
+ {"Gravis UltraSound", 0, 0, SNDCARD_GUS},
+ gus_midi_open,
+ gus_midi_close,
+ gus_midi_ioctl,
+ gus_midi_out,
+ gus_midi_start_read,
+ gus_midi_end_read,
+ gus_midi_kick,
+ NULL, /* command */
+ gus_midi_buffer_status
+};
+
+long
+gus_midi_init (long mem_start)
+{
+ OUTB (MIDI_RESET, u_MidiControl);
+
+ my_dev = num_midis;
+ midi_devs[num_midis++] = &gus_midi_operations;
+ return mem_start;
+}
+
+void
+gus_midi_interrupt (int dummy)
+{
+ unsigned char stat, data;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ stat = GUS_MIDI_STATUS ();
+
+ if (stat & MIDI_RCV_FULL)
+ {
+ data = INB (u_MidiData);
+ if (input_opened)
+ midi_input_intr (my_dev, data);
+ }
+
+ if (stat & MIDI_XMIT_EMPTY)
+ {
+ while (qlen && dump_to_midi (tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+
+ if (!qlen)
+ {
+ /* Disable Midi output interrupts, since no data in the buffer */
+ gus_midi_control &= ~MIDI_ENABLE_XMIT;
+ OUTB (gus_midi_control, u_MidiControl);
+ }
+ }
+
+ if (stat & MIDI_FRAME_ERR)
+ printk ("Midi framing error\n");
+ if (stat & MIDI_OVERRUN && input_opened)
+ printk ("GUS: Midi input overrun\n");
+
+ RESTORE_INTR (flags);
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/gus_vol.c b/sys/i386/isa/sound/gus_vol.c
new file mode 100644
index 0000000..055a117
--- /dev/null
+++ b/sys/i386/isa/sound/gus_vol.c
@@ -0,0 +1,147 @@
+/*
+ * gus_vol.c - Compute volume for GUS.
+ *
+ * Greg Lee 1993.
+ */
+#include "sound_config.h"
+#ifndef EXCLUDE_GUS
+#include "gus_linearvol.h"
+
+#define GUS_VOLUME gus_wave_volume
+
+
+extern int gus_wave_volume;
+
+/*
+ * Calculate gus volume from note velocity, main volume, expression, and
+ * intrinsic patch volume given in patch library. Expression is multiplied
+ * in, so it emphasizes differences in note velocity, while main volume is
+ * added in -- I don't know whether this is right, but it seems reasonable to
+ * me. (In the previous stage, main volume controller messages were changed
+ * to expression controller messages, if they were found to be used for
+ * dynamic volume adjustments, so here, main volume can be assumed to be
+ * constant throughout a song.)
+ *
+ * Intrinsic patch volume is added in, but if over 64 is also multiplied in, so
+ * we can give a big boost to very weak voices like nylon guitar and the
+ * basses. The normal value is 64. Strings are assigned lower values.
+ */
+unsigned short
+gus_adagio_vol (int vel, int mainv, int xpn, int voicev)
+{
+ int i, m, n, x;
+
+
+ /*
+ * A voice volume of 64 is considered neutral, so adjust the main volume if
+ * something other than this neutral value was assigned in the patch
+ * library.
+ */
+ x = 256 + 6 * (voicev - 64);
+
+ /*
+ * Boost expression by voice volume above neutral.
+ */
+ if (voicev > 65)
+ xpn += voicev - 64;
+ xpn += (voicev - 64) / 2;
+
+ /*
+ * Combine multiplicative and level components.
+ */
+ x = vel * xpn * 6 + (voicev / 4) * x;
+
+#ifdef GUS_VOLUME
+ /*
+ * Further adjustment by installation-specific master volume control
+ * (default 60).
+ */
+ x = (x * GUS_VOLUME * GUS_VOLUME) / 10000;
+#endif
+
+#ifdef GUS_USE_CHN_MAIN_VOLUME
+ /*
+ * Experimental support for the channel main volume
+ */
+
+ mainv = (mainv / 2) + 64; /* Scale to 64 to 127 */
+ x = (x * mainv * mainv) / 16384;
+#endif
+
+ if (x < 2)
+ return (0);
+ else if (x >= 65535)
+ return ((15 << 8) | 255);
+
+ /*
+ * Convert to gus's logarithmic form with 4 bit exponent i and 8 bit
+ * mantissa m.
+ */
+ n = x;
+ i = 7;
+ if (n < 128)
+ {
+ while (i > 0 && n < (1 << i))
+ i--;
+ }
+ else
+ while (n > 255)
+ {
+ n >>= 1;
+ i++;
+ }
+ /*
+ * Mantissa is part of linear volume not expressed in exponent. (This is
+ * not quite like real logs -- I wonder if it's right.)
+ */
+ m = x - (1 << i);
+
+ /*
+ * Adjust mantissa to 8 bits.
+ */
+ if (m > 0)
+ {
+ if (i > 8)
+ m >>= i - 8;
+ else if (i < 8)
+ m <<= 8 - i;
+ }
+
+ return ((i << 8) + m);
+}
+
+/*
+ * Volume-values are interpreted as linear values. Volume is based on the
+ * value supplied with SEQ_START_NOTE(), channel main volume (if compiled in)
+ * and the volume set by the mixer-device (default 60%).
+ */
+
+unsigned short
+gus_linear_vol (int vol, int mainvol)
+{
+ int mixer_mainvol;
+
+ if (vol <= 0)
+ vol = 0;
+ else if (vol >= 127)
+ vol = 127;
+
+#ifdef GUS_VOLUME
+ mixer_mainvol = GUS_VOLUME;
+#else
+ mixer_mainvol = 100;
+#endif
+
+#ifdef GUS_USE_CHN_MAIN_VOLUME
+ if (mainvol <= 0)
+ mainvol = 0;
+ else if (mainvol >= 127)
+ mainvol = 127;
+#else
+ mainvol = 128;
+#endif
+
+ return gus_linearvol[(((vol * mainvol) / 128) * mixer_mainvol) / 100];
+}
+
+#endif
diff --git a/sys/i386/isa/sound/gus_wave.c b/sys/i386/isa/sound/gus_wave.c
new file mode 100644
index 0000000..9f6442e
--- /dev/null
+++ b/sys/i386/isa/sound/gus_wave.c
@@ -0,0 +1,3575 @@
+/*
+ * sound/gus_wave.c
+ *
+ * Driver for the Gravis UltraSound wave table synth.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+#ifdef __FreeBSD__
+#include <machine/ultrasound.h>
+#else
+#include "ultrasound.h"
+#endif
+#include "gus_hw.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS)
+
+#define MAX_SAMPLE 128
+#define MAX_PATCH 256
+
+struct voice_info
+ {
+ unsigned long orig_freq;
+ unsigned long current_freq;
+ unsigned long mode;
+ int bender;
+ int bender_range;
+ int panning;
+ int midi_volume;
+ unsigned int initial_volume;
+ unsigned int current_volume;
+ int loop_irq_mode, loop_irq_parm;
+#define LMODE_FINISH 1
+#define LMODE_PCM 2
+#define LMODE_PCM_STOP 3
+ int volume_irq_mode, volume_irq_parm;
+#define VMODE_HALT 1
+#define VMODE_ENVELOPE 2
+#define VMODE_START_NOTE 3
+
+ int env_phase;
+ unsigned char env_rate[6];
+ unsigned char env_offset[6];
+
+ /*
+ * Volume computation parameters for gus_adagio_vol()
+ */
+ int main_vol, expression_vol, patch_vol;
+
+ /* Variables for "Ultraclick" removal */
+ int dev_pending, note_pending, volume_pending, sample_pending;
+ char kill_pending;
+ long offset_pending;
+
+ };
+
+extern int gus_base;
+extern int gus_irq, gus_dma;
+extern char *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT];
+extern unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT];
+extern int snd_raw_count[MAX_DSP_DEV];
+static long gus_mem_size = 0;
+static long free_mem_ptr = 0;
+static int gus_busy = 0;
+static int nr_voices = 0;
+static int gus_devnum = 0;
+static int volume_base, volume_scale, volume_method;
+static int gus_line_vol = 100, gus_mic_vol = 0;
+static int gus_recmask = SOUND_MASK_MIC;
+static int recording_active = 0;
+
+int gus_wave_volume = 60;
+int gus_pcm_volume = 80;
+static unsigned char mix_image = 0x00;
+
+/*
+ * Current version of this driver doesn't allow synth and PCM functions
+ * at the same time. The active_device specifies the active driver
+ */
+static int active_device = 0;
+
+#define GUS_DEV_WAVE 1 /*
+ * * * Wave table synth */
+#define GUS_DEV_PCM_DONE 2 /*
+ * * * PCM device, transfer done */
+#define GUS_DEV_PCM_CONTINUE 3 /*
+ * * * PCM device, transfer the
+ * second * * * chn */
+
+static int gus_sampling_speed;
+static int gus_sampling_channels;
+static int gus_sampling_bits;
+
+DEFINE_WAIT_QUEUE (dram_sleeper, dram_sleep_flag);
+
+/*
+ * Variables and buffers for PCM output
+ */
+#define MAX_PCM_BUFFERS (32*MAX_REALTIME_FACTOR) /*
+ * * * Don't
+ * * * change
+ *
+ */
+
+static int pcm_bsize, /*
+ * Current blocksize
+ */
+ pcm_nblk, /*
+ * Current # of blocks
+ */
+ pcm_banksize; /*
+
+
+ * * * * # bytes allocated for channels */
+static int pcm_datasize[MAX_PCM_BUFFERS]; /*
+
+
+ * * * * Actual # of bytes
+ * in blk * */
+static volatile int pcm_head, pcm_tail, pcm_qlen; /*
+
+
+ * * * * DRAM queue
+ * */
+static volatile int pcm_active;
+static int pcm_opened = 0;
+static int pcm_current_dev;
+static int pcm_current_block;
+static unsigned long pcm_current_buf;
+static int pcm_current_count;
+static int pcm_current_intrflag;
+
+struct voice_info voices[32];
+
+static int freq_div_table[] =
+{
+ 44100, /*
+ * 14
+ */
+ 41160, /*
+ * 15
+ */
+ 38587, /*
+ * 16
+ */
+ 36317, /*
+ * 17
+ */
+ 34300, /*
+ * 18
+ */
+ 32494, /*
+ * 19
+ */
+ 30870, /*
+ * 20
+ */
+ 29400, /*
+ * 21
+ */
+ 28063, /*
+ * 22
+ */
+ 26843, /*
+ * 23
+ */
+ 25725, /*
+ * 24
+ */
+ 24696, /*
+ * 25
+ */
+ 23746, /*
+ * 26
+ */
+ 22866, /*
+ * 27
+ */
+ 22050, /*
+ * 28
+ */
+ 21289, /*
+ * 29
+ */
+ 20580, /*
+ * 30
+ */
+ 19916, /*
+ * 31
+ */
+ 19293 /*
+ * 32
+ */
+};
+
+static struct patch_info *samples;
+static long sample_ptrs[MAX_SAMPLE + 1];
+static int sample_map[32];
+static int free_sample;
+
+
+static int patch_table[MAX_PATCH];
+static int patch_map[32];
+
+static struct synth_info gus_info =
+{"Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH};
+
+static void gus_poke (long addr, unsigned char data);
+static void compute_and_set_volume (int voice, int volume, int ramp_time);
+extern unsigned short gus_adagio_vol (int vel, int mainv, int xpn, int voicev);
+extern unsigned short gus_linear_vol (int vol, int mainvol);
+static void compute_volume (int voice, int volume);
+static void do_volume_irq (int voice);
+static void set_input_volumes (void);
+
+#define INSTANT_RAMP -1 /*
+ * * * Dont use ramping */
+#define FAST_RAMP 0 /*
+ * * * Fastest possible ramp */
+
+static void
+reset_sample_memory (void)
+{
+ int i;
+
+ for (i = 0; i <= MAX_SAMPLE; i++)
+ sample_ptrs[i] = -1;
+ for (i = 0; i < 32; i++)
+ sample_map[i] = -1;
+ for (i = 0; i < 32; i++)
+ patch_map[i] = -1;
+
+ gus_poke (0, 0); /*
+ * Put silence here
+ */
+ gus_poke (1, 0);
+
+ free_mem_ptr = 2;
+ free_sample = 0;
+
+ for (i = 0; i < MAX_PATCH; i++)
+ patch_table[i] = -1;
+}
+
+void
+gus_delay (void)
+{
+ int i;
+
+ for (i = 0; i < 7; i++)
+ INB (u_DRAMIO);
+}
+
+static void
+gus_poke (long addr, unsigned char data)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ OUTB (0x43, u_Command);
+ OUTB (addr & 0xff, u_DataLo);
+ OUTB ((addr >> 8) & 0xff, u_DataHi);
+
+ OUTB (0x44, u_Command);
+ OUTB ((addr >> 16) & 0xff, u_DataHi);
+ OUTB (data, u_DRAMIO);
+ RESTORE_INTR (flags);
+}
+
+static unsigned char
+gus_peek (long addr)
+{
+ unsigned long flags;
+ unsigned char tmp;
+
+ DISABLE_INTR (flags);
+ OUTB (0x43, u_Command);
+ OUTB (addr & 0xff, u_DataLo);
+ OUTB ((addr >> 8) & 0xff, u_DataHi);
+
+ OUTB (0x44, u_Command);
+ OUTB ((addr >> 16) & 0xff, u_DataHi);
+ tmp = INB (u_DRAMIO);
+ RESTORE_INTR (flags);
+
+ return tmp;
+}
+
+void
+gus_write8 (int reg, unsigned int data)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ OUTB (reg, u_Command);
+ OUTB ((unsigned char) (data & 0xff), u_DataHi);
+
+ RESTORE_INTR (flags);
+}
+
+unsigned char
+gus_read8 (int reg)
+{
+ unsigned long flags;
+ unsigned char val;
+
+ DISABLE_INTR (flags);
+ OUTB (reg | 0x80, u_Command);
+ val = INB (u_DataHi);
+ RESTORE_INTR (flags);
+
+ return val;
+}
+
+unsigned char
+gus_look8 (int reg)
+{
+ unsigned long flags;
+ unsigned char val;
+
+ DISABLE_INTR (flags);
+ OUTB (reg, u_Command);
+ val = INB (u_DataHi);
+ RESTORE_INTR (flags);
+
+ return val;
+}
+
+void
+gus_write16 (int reg, unsigned int data)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ OUTB (reg, u_Command);
+
+ OUTB ((unsigned char) (data & 0xff), u_DataLo);
+ OUTB ((unsigned char) ((data >> 8) & 0xff), u_DataHi);
+
+ RESTORE_INTR (flags);
+}
+
+unsigned short
+gus_read16 (int reg)
+{
+ unsigned long flags;
+ unsigned char hi, lo;
+
+ DISABLE_INTR (flags);
+
+ OUTB (reg | 0x80, u_Command);
+
+ lo = INB (u_DataLo);
+ hi = INB (u_DataHi);
+
+ RESTORE_INTR (flags);
+
+ return ((hi << 8) & 0xff00) | lo;
+}
+
+void
+gus_write_addr (int reg, unsigned long address, int is16bit)
+{
+ unsigned long hold_address;
+
+ if (is16bit)
+ {
+ /*
+ * Special processing required for 16 bit patches
+ */
+
+ hold_address = address;
+ address = address >> 1;
+ address &= 0x0001ffffL;
+ address |= (hold_address & 0x000c0000L);
+ }
+
+ gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff));
+ gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff));
+ /* Could writing twice fix problems with GUS_VOICE_POS() ? Lets try... */
+ gus_delay ();
+ gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff));
+ gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff));
+}
+
+static void
+gus_select_voice (int voice)
+{
+ if (voice < 0 || voice > 31)
+ return;
+
+ OUTB (voice, u_Voice);
+}
+
+static void
+gus_select_max_voices (int nvoices)
+{
+ if (nvoices < 14)
+ nvoices = 14;
+ if (nvoices > 32)
+ nvoices = 32;
+
+ nr_voices = nvoices;
+
+ gus_write8 (0x0e, (nvoices - 1) | 0xc0);
+}
+
+static void
+gus_voice_on (unsigned int mode)
+{
+ gus_write8 (0x00, (unsigned char) (mode & 0xfc));
+ gus_delay ();
+ gus_write8 (0x00, (unsigned char) (mode & 0xfc));
+}
+
+static void
+gus_voice_off (void)
+{
+ gus_write8 (0x00, gus_read8 (0x00) | 0x03);
+}
+
+static void
+gus_voice_mode (unsigned int m)
+{
+ unsigned char mode = (unsigned char) (m & 0xff);
+
+ gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); /*
+ * Don't
+ * start
+ * or
+ * stop
+ * *
+ * voice
+ */
+ gus_delay ();
+ gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc));
+}
+
+static void
+gus_voice_freq (unsigned long freq)
+{
+ unsigned long divisor = freq_div_table[nr_voices - 14];
+ unsigned short fc;
+
+ fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor);
+ fc = fc << 1;
+
+ gus_write16 (0x01, fc);
+}
+
+static void
+gus_voice_volume (unsigned int vol)
+{
+ gus_write8 (0x0d, 0x03); /*
+ * Stop ramp before setting volume
+ */
+ gus_write16 (0x09, (unsigned short) (vol << 4));
+}
+
+static void
+gus_voice_balance (unsigned int balance)
+{
+ gus_write8 (0x0c, (unsigned char) (balance & 0xff));
+}
+
+static void
+gus_ramp_range (unsigned int low, unsigned int high)
+{
+ gus_write8 (0x07, (unsigned char) ((low >> 4) & 0xff));
+ gus_write8 (0x08, (unsigned char) ((high >> 4) & 0xff));
+}
+
+static void
+gus_ramp_rate (unsigned int scale, unsigned int rate)
+{
+ gus_write8 (0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f)));
+}
+
+static void
+gus_rampon (unsigned int m)
+{
+ unsigned char mode = (unsigned char) (m & 0xff);
+
+ gus_write8 (0x0d, mode & 0xfc);
+ gus_delay ();
+ gus_write8 (0x0d, mode & 0xfc);
+}
+
+static void
+gus_ramp_mode (unsigned int m)
+{
+ unsigned char mode = (unsigned char) (m & 0xff);
+
+ gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); /*
+ * Don't
+ * start
+ * or
+ * stop
+ * *
+ * ramping
+ */
+ gus_delay ();
+ gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc));
+}
+
+static void
+gus_rampoff (void)
+{
+ gus_write8 (0x0d, 0x03);
+}
+
+static void
+gus_set_voice_pos (int voice, long position)
+{
+ int sample_no;
+
+ if ((sample_no = sample_map[voice]) != -1)
+ if (position < samples[sample_no].len)
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ voices[voice].offset_pending = position;
+ else
+ gus_write_addr (0x0a, sample_ptrs[sample_no] + position,
+ samples[sample_no].mode & WAVE_16_BITS);
+}
+
+static void
+gus_voice_init (int voice)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_volume (0);
+ gus_write_addr (0x0a, 0, 0); /*
+ * Set current position to 0
+ */
+ gus_write8 (0x00, 0x03); /*
+ * Voice off
+ */
+ gus_write8 (0x0d, 0x03); /*
+ * Ramping off
+ */
+ RESTORE_INTR (flags);
+
+}
+
+static void
+gus_voice_init2 (int voice)
+{
+ voices[voice].panning = 0;
+ voices[voice].mode = 0;
+ voices[voice].orig_freq = 20000;
+ voices[voice].current_freq = 20000;
+ voices[voice].bender = 0;
+ voices[voice].bender_range = 200;
+ voices[voice].initial_volume = 0;
+ voices[voice].current_volume = 0;
+ voices[voice].loop_irq_mode = 0;
+ voices[voice].loop_irq_parm = 0;
+ voices[voice].volume_irq_mode = 0;
+ voices[voice].volume_irq_parm = 0;
+ voices[voice].env_phase = 0;
+ voices[voice].main_vol = 127;
+ voices[voice].patch_vol = 127;
+ voices[voice].expression_vol = 127;
+ voices[voice].sample_pending = -1;
+}
+
+static void
+step_envelope (int voice)
+{
+ unsigned vol, prev_vol, phase;
+ unsigned char rate;
+ long int flags;
+
+ if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2)
+ {
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_rampoff ();
+ RESTORE_INTR (flags);
+ return; /*
+ * Sustain
+ */
+ }
+
+ if (voices[voice].env_phase >= 5)
+ {
+ /*
+ * Shoot the voice off
+ */
+
+ gus_voice_init (voice);
+ return;
+ }
+
+ prev_vol = voices[voice].current_volume;
+ phase = ++voices[voice].env_phase;
+ compute_volume (voice, voices[voice].midi_volume);
+ vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255;
+ rate = voices[voice].env_rate[phase];
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+
+ gus_voice_volume (prev_vol);
+
+
+ gus_write8 (0x06, rate); /*
+ * Ramping rate
+ */
+
+ voices[voice].volume_irq_mode = VMODE_ENVELOPE;
+
+ if (((vol - prev_vol) / 64) == 0) /*
+ * No significant volume change
+ */
+ {
+ RESTORE_INTR (flags);
+ step_envelope (voice); /*
+ * Continue with the next phase
+ */
+ return;
+ }
+
+ if (vol > prev_vol)
+ {
+ if (vol >= (4096 - 64))
+ vol = 4096 - 65;
+ gus_ramp_range (0, vol);
+ gus_rampon (0x20); /*
+ * Increasing, irq
+ */
+ }
+ else
+ {
+ if (vol <= 64)
+ vol = 65;
+ gus_ramp_range (vol, 4030);
+ gus_rampon (0x60); /*
+ * Decreasing, irq
+ */
+ }
+ voices[voice].current_volume = vol;
+ RESTORE_INTR (flags);
+}
+
+static void
+init_envelope (int voice)
+{
+ voices[voice].env_phase = -1;
+ voices[voice].current_volume = 64;
+
+ step_envelope (voice);
+}
+
+static void
+start_release (int voice, long int flags)
+{
+ if (gus_read8 (0x00) & 0x03)
+ return; /*
+ * Voice already stopped
+ */
+
+ voices[voice].env_phase = 2; /*
+ * Will be incremented by step_envelope
+ */
+
+ voices[voice].current_volume =
+ voices[voice].initial_volume =
+ gus_read16 (0x09) >> 4; /*
+ * Get current volume
+ */
+
+ voices[voice].mode &= ~WAVE_SUSTAIN_ON;
+ gus_rampoff ();
+ RESTORE_INTR (flags);
+ step_envelope (voice);
+}
+
+static void
+gus_voice_fade (int voice)
+{
+ int instr_no = sample_map[voice], is16bits;
+ long int flags;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+
+ if (instr_no < 0 || instr_no > MAX_SAMPLE)
+ {
+ gus_write8 (0x00, 0x03); /*
+ * Hard stop
+ */
+ RESTORE_INTR (flags);
+ return;
+ }
+
+ is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /*
+ * 8 or 16
+ * bit
+ * samples
+ */
+
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ start_release (voice, flags);
+ return;
+ }
+
+ /*
+ * Ramp the volume down but not too quickly.
+ */
+ if ((gus_read16 (0x09) >> 4) < 100) /*
+ * Get current volume
+ */
+ {
+ gus_voice_off ();
+ gus_rampoff ();
+ gus_voice_init (voice);
+ return;
+ }
+
+ gus_ramp_range (65, 4030);
+ gus_ramp_rate (2, 4);
+ gus_rampon (0x40 | 0x20); /*
+ * Down, once, irq
+ */
+ voices[voice].volume_irq_mode = VMODE_HALT;
+ RESTORE_INTR (flags);
+}
+
+static void
+gus_reset (void)
+{
+ int i;
+
+ gus_select_max_voices (24);
+ volume_base = 3071;
+ volume_scale = 4;
+ volume_method = VOL_METHOD_ADAGIO;
+
+ for (i = 0; i < 32; i++)
+ {
+ gus_voice_init (i); /*
+ * Turn voice off
+ */
+ gus_voice_init2 (i);
+ }
+
+ INB (u_Status); /*
+ * Touch the status register
+ */
+
+ gus_look8 (0x41); /*
+ * Clear any pending DMA IRQs
+ */
+ gus_look8 (0x49); /*
+ * Clear any pending sample IRQs
+ */
+ gus_read8 (0x0f); /*
+ * Clear pending IRQs
+ */
+
+}
+
+static void
+gus_initialize (void)
+{
+ unsigned long flags;
+ register unsigned char dma_image, irq_image, tmp;
+
+ static unsigned char gus_irq_map[16] =
+ {0, 0, 1, 3, 0, 2, 0, 4, 0, 0, 0, 5, 6, 0, 0, 7};
+
+ static unsigned char gus_dma_map[8] =
+ {0, 1, 0, 2, 0, 3, 4, 5};
+
+ DISABLE_INTR (flags);
+
+ gus_write8 (0x4c, 0); /*
+ * Reset GF1
+ */
+ gus_delay ();
+ gus_delay ();
+
+ gus_write8 (0x4c, 1); /*
+ * Release Reset
+ */
+ gus_delay ();
+ gus_delay ();
+
+ /*
+ * Clear all interrupts
+ */
+
+ gus_write8 (0x41, 0); /*
+ * DMA control
+ */
+ gus_write8 (0x45, 0); /*
+ * Timer control
+ */
+ gus_write8 (0x49, 0); /*
+ * Sample control
+ */
+
+ gus_select_max_voices (24);
+
+ INB (u_Status); /*
+ * Touch the status register
+ */
+
+ gus_look8 (0x41); /*
+ * Clear any pending DMA IRQs
+ */
+ gus_look8 (0x49); /*
+ * Clear any pending sample IRQs
+ */
+ gus_read8 (0x0f); /*
+ * Clear pending IRQs
+ */
+
+ gus_reset (); /*
+ * Resets all voices
+ */
+
+ gus_look8 (0x41); /*
+ * Clear any pending DMA IRQs
+ */
+ gus_look8 (0x49); /*
+ * Clear any pending sample IRQs
+ */
+ gus_read8 (0x0f); /*
+ * Clear pending IRQs
+ */
+
+ gus_write8 (0x4c, 7); /*
+ * Master reset | DAC enable | IRQ enable
+ */
+
+ /*
+ * Set up for Digital ASIC
+ */
+
+ OUTB (0x05, gus_base + 0x0f);
+
+ mix_image |= 0x02; /*
+ * Disable line out
+ */
+ OUTB (mix_image, u_Mixer);
+
+ OUTB (0x00, u_IRQDMAControl);
+
+ OUTB (0x00, gus_base + 0x0f);
+
+ /*
+ * Now set up the DMA and IRQ interface
+ *
+ * The GUS supports two IRQs and two DMAs.
+ *
+ * Just one DMA channel is used. This prevents simultaneous ADC and DAC.
+ * Adding this support requires significant changes to the dmabuf.c, dsp.c
+ * and audio.c also.
+ */
+
+ irq_image = 0;
+ tmp = gus_irq_map[gus_irq];
+ if (!tmp)
+ printk ("Warning! GUS IRQ not selected\n");
+ irq_image |= tmp;
+ irq_image |= 0x40; /*
+ * Combine IRQ1 (GF1) and IRQ2 (Midi)
+ */
+
+ dma_image = 0x40; /*
+ * Combine DMA1 (DRAM) and IRQ2 (ADC)
+ */
+ tmp = gus_dma_map[gus_dma];
+ if (!tmp)
+ printk ("Warning! GUS DMA not selected\n");
+ dma_image |= tmp;
+
+ /*
+ * For some reason the IRQ and DMA addresses must be written twice
+ */
+
+ /*
+ * Doing it first time
+ */
+
+ OUTB (mix_image, u_Mixer); /*
+ * Select DMA control
+ */
+ OUTB (dma_image | 0x80, u_IRQDMAControl); /*
+ * Set DMA address
+ */
+
+ OUTB (mix_image | 0x40, u_Mixer); /*
+ * Select IRQ control
+ */
+ OUTB (irq_image, u_IRQDMAControl); /*
+ * Set IRQ address
+ */
+
+ /*
+ * Doing it second time
+ */
+
+ OUTB (mix_image, u_Mixer); /*
+ * Select DMA control
+ */
+ OUTB (dma_image, u_IRQDMAControl); /*
+ * Set DMA address
+ */
+
+ OUTB (mix_image | 0x40, u_Mixer); /*
+ * Select IRQ control
+ */
+ OUTB (irq_image, u_IRQDMAControl); /*
+ * Set IRQ address
+ */
+
+ gus_select_voice (0); /*
+ * This disables writes to IRQ/DMA reg
+ */
+
+ mix_image &= ~0x02; /*
+ * Enable line out
+ */
+ mix_image |= 0x08; /*
+ * Enable IRQ
+ */
+ OUTB (mix_image, u_Mixer); /*
+ * Turn mixer channels on
+ * Note! Mic in is left off.
+ */
+
+ gus_select_voice (0); /*
+ * This disables writes to IRQ/DMA reg
+ */
+
+ gusintr (0); /*
+ * Serve pending interrupts
+ */
+ RESTORE_INTR (flags);
+}
+
+int
+gus_wave_detect (int baseaddr)
+{
+ unsigned long i;
+ unsigned long loc;
+
+ gus_base = baseaddr;
+
+ gus_write8 (0x4c, 0); /* Reset GF1 */
+ gus_delay ();
+ gus_delay ();
+
+ gus_write8 (0x4c, 1); /* Release Reset */
+ gus_delay ();
+ gus_delay ();
+
+ /* See if there is first block there.... */
+ gus_poke (0L, 0xaa);
+ if (gus_peek (0L) != 0xaa)
+ return (0);
+
+ /* Now zero it out so that I can check for mirroring .. */
+ gus_poke (0L, 0x00);
+ for (i = 1L; i < 1024L; i++)
+ {
+ int n, failed;
+
+ /* check for mirroring ... */
+ if (gus_peek (0L) != 0)
+ break;
+ loc = i << 10;
+
+ for (n = loc - 1, failed = 0; n <= loc; n++)
+ {
+ gus_poke (loc, 0xaa);
+ if (gus_peek (loc) != 0xaa)
+ failed = 1;
+
+ gus_poke (loc, 0x55);
+ if (gus_peek (loc) != 0x55)
+ failed = 1;
+ }
+
+ if (failed)
+ break;
+ }
+ gus_mem_size = i << 10;
+ return 1;
+}
+
+static int
+guswave_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+
+ switch (cmd)
+ {
+ case SNDCTL_SYNTH_INFO:
+ gus_info.nr_voices = nr_voices;
+ IOCTL_TO_USER ((char *) arg, 0, &gus_info, sizeof (gus_info));
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_RESETSAMPLES:
+ reset_sample_memory ();
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_PERCMODE:
+ return 0;
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ return gus_mem_size - free_mem_ptr - 32;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+}
+
+static int
+guswave_set_instr (int dev, int voice, int instr_no)
+{
+ int sample_no;
+
+ if (instr_no < 0 || instr_no > MAX_PATCH)
+ return RET_ERROR (EINVAL);
+
+ if (voice < 0 || voice > 31)
+ return RET_ERROR (EINVAL);
+
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ {
+ voices[voice].sample_pending = instr_no;
+ return 0;
+ }
+
+ sample_no = patch_table[instr_no];
+ patch_map[voice] = -1;
+
+ if (sample_no < 0)
+ {
+ printk ("GUS: Undefined patch %d for voice %d\n", instr_no, voice);
+ return RET_ERROR (EINVAL);/*
+ * Patch not defined
+ */
+ }
+
+ if (sample_ptrs[sample_no] == -1) /*
+ * Sample not loaded
+ */
+ {
+ printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice);
+ return RET_ERROR (EINVAL);
+ }
+
+ sample_map[voice] = sample_no;
+ patch_map[voice] = instr_no;
+ return 0;
+}
+
+static int
+#ifdef FUTURE_VERSION
+guswave_kill_note (int dev, int voice, int note, int velocity)
+#else
+guswave_kill_note (int dev, int voice, int velocity)
+#endif
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ {
+ voices[voice].kill_pending = 1;
+ RESTORE_INTR (flags);
+ }
+ else
+ {
+ RESTORE_INTR (flags);
+ gus_voice_fade (voice);
+ }
+
+ return 0;
+}
+
+static void
+guswave_aftertouch (int dev, int voice, int pressure)
+{
+ short lo_limit, hi_limit;
+ unsigned long flags;
+
+ return; /*
+ * Currently disabled
+ */
+
+ if (voice < 0 || voice > 31)
+ return;
+
+ if (voices[voice].mode & WAVE_ENVELOPES && voices[voice].env_phase != 2)
+ return; /*
+ * Don't mix with envelopes
+ */
+
+ if (pressure < 32)
+ {
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_rampoff ();
+ compute_and_set_volume (voice, 255, 0); /*
+ * Back to original volume
+ */
+ RESTORE_INTR (flags);
+ return;
+ }
+
+ hi_limit = voices[voice].current_volume;
+ lo_limit = hi_limit * 99 / 100;
+ if (lo_limit < 65)
+ lo_limit = 65;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ if (hi_limit > (4095 - 65))
+ {
+ hi_limit = 4095 - 65;
+ gus_voice_volume (hi_limit);
+ }
+ gus_ramp_range (lo_limit, hi_limit);
+ gus_ramp_rate (3, 8);
+ gus_rampon (0x58); /*
+ * Bidirectional, Down, Loop
+ */
+ RESTORE_INTR (flags);
+}
+
+static void
+guswave_panning (int dev, int voice, int value)
+{
+ if (voice >= 0 || voice < 32)
+ voices[voice].panning = value;
+}
+
+static void
+guswave_volume_method (int dev, int mode)
+{
+ if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO)
+ volume_method = mode;
+}
+
+static void
+compute_volume (int voice, int volume)
+{
+ if (volume < 128)
+ voices[voice].midi_volume = volume;
+
+ switch (volume_method)
+ {
+ case VOL_METHOD_ADAGIO:
+ voices[voice].initial_volume =
+ gus_adagio_vol (voices[voice].midi_volume, voices[voice].main_vol,
+ voices[voice].expression_vol,
+ voices[voice].patch_vol);
+ break;
+
+ case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */
+ voices[voice].initial_volume =
+ gus_linear_vol (volume, voices[voice].main_vol);
+ break;
+
+ default:
+ voices[voice].initial_volume = volume_base +
+ (voices[voice].midi_volume * volume_scale);
+ }
+
+ if (voices[voice].initial_volume > 4030)
+ voices[voice].initial_volume = 4030;
+}
+
+static void
+compute_and_set_volume (int voice, int volume, int ramp_time)
+{
+ int current, target, rate;
+ unsigned long flags;
+
+ compute_volume (voice, volume);
+ voices[voice].current_volume = voices[voice].initial_volume;
+
+ DISABLE_INTR (flags);
+ /*
+ * CAUTION! Interrupts disabled. Enable them before returning
+ */
+
+ gus_select_voice (voice);
+
+ current = gus_read16 (0x09) >> 4;
+ target = voices[voice].initial_volume;
+
+ if (ramp_time == INSTANT_RAMP)
+ {
+ gus_rampoff ();
+ gus_voice_volume (target);
+ RESTORE_INTR (flags);
+ return;
+ }
+
+ if (ramp_time == FAST_RAMP)
+ rate = 63;
+ else
+ rate = 16;
+ gus_ramp_rate (0, rate);
+
+ if ((target - current) / 64 == 0) /*
+ * Too close
+ */
+ {
+ gus_rampoff ();
+ gus_voice_volume (target);
+ RESTORE_INTR (flags);
+ return;
+ }
+
+ if (target > current)
+ {
+ if (target > (4095 - 65))
+ target = 4095 - 65;
+ gus_ramp_range (current, target);
+ gus_rampon (0x00); /*
+ * Ramp up, once, no irq
+ */
+ }
+ else
+ {
+ if (target < 65)
+ target = 65;
+
+ gus_ramp_range (target, current);
+ gus_rampon (0x40); /*
+ * Ramp down, once, no irq
+ */
+ }
+ RESTORE_INTR (flags);
+}
+
+static void
+dynamic_volume_change (int voice)
+{
+ unsigned char status;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ status = gus_read8 (0x00); /*
+ * Voice status
+ */
+ RESTORE_INTR (flags);
+
+ if (status & 0x03)
+ return; /*
+ * Voice not started
+ */
+
+ if (!(voices[voice].mode & WAVE_ENVELOPES))
+ {
+ compute_and_set_volume (voice, voices[voice].midi_volume, 1);
+ return;
+ }
+
+ /*
+ * Voice is running and has envelopes.
+ */
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ status = gus_read8 (0x0d); /*
+ * Ramping status
+ */
+ RESTORE_INTR (flags);
+
+ if (status & 0x03) /*
+ * Sustain phase?
+ */
+ {
+ compute_and_set_volume (voice, voices[voice].midi_volume, 1);
+ return;
+ }
+
+ if (voices[voice].env_phase < 0)
+ return;
+
+ compute_volume (voice, voices[voice].midi_volume);
+
+#if 0 /*
+ * * * Is this really required */
+ voices[voice].current_volume =
+ gus_read16 (0x09) >> 4; /*
+ * Get current volume
+ */
+
+ voices[voice].env_phase--;
+ step_envelope (voice);
+#endif
+}
+
+static void
+guswave_controller (int dev, int voice, int ctrl_num, int value)
+{
+ unsigned long flags;
+ unsigned long freq;
+
+ if (voice < 0 || voice > 31)
+ return;
+
+ switch (ctrl_num)
+ {
+ case CTRL_PITCH_BENDER:
+ voices[voice].bender = value;
+
+ if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
+ {
+ freq = compute_finetune (voices[voice].orig_freq, value, voices[voice].bender_range);
+ voices[voice].current_freq = freq;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_freq (freq);
+ RESTORE_INTR (flags);
+ }
+ break;
+
+ case CTRL_PITCH_BENDER_RANGE:
+ voices[voice].bender_range = value;
+ break;
+#ifdef FUTURE_VERSION
+ case CTL_EXPRESSION:
+ value /= 128;
+#endif
+ case CTRL_EXPRESSION:
+ if (volume_method == VOL_METHOD_ADAGIO)
+ {
+ voices[voice].expression_vol = value;
+ if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
+ dynamic_volume_change (voice);
+ }
+ break;
+
+#ifdef FUTURE_VERSION
+ case CTL_PAN:
+ voices[voice].panning = (value * 2) - 128;
+ break;
+
+ case CTL_MAIN_VOLUME:
+ value = (value * 100) / 16383;
+#endif
+
+ case CTRL_MAIN_VOLUME:
+ voices[voice].main_vol = value;
+ if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
+ dynamic_volume_change (voice);
+ break;
+
+ default: /*
+ * Ignore
+ */
+ break;
+ }
+}
+
+static int
+guswave_start_note2 (int dev, int voice, int note_num, int volume)
+{
+ int sample, best_sample, best_delta, delta_freq;
+ int is16bits, samplep, patch, pan;
+ unsigned long note_freq, base_note, freq, flags;
+ unsigned char mode = 0;
+
+ if (voice < 0 || voice > 31)
+ {
+ printk ("GUS: Invalid voice\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ if (note_num == 255)
+ {
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ voices[voice].midi_volume = volume;
+ dynamic_volume_change (voice);
+ return 0;
+ }
+
+ compute_and_set_volume (voice, volume, 1);
+ return 0;
+ }
+
+ if ((patch = patch_map[voice]) == -1)
+ {
+ return RET_ERROR (EINVAL);
+ }
+
+ if ((samplep = patch_table[patch]) == -1)
+ {
+ return RET_ERROR (EINVAL);
+ }
+
+ note_freq = note_to_freq (note_num);
+
+ /*
+ * Find a sample within a patch so that the note_freq is between low_note
+ * and high_note.
+ */
+ sample = -1;
+
+ best_sample = samplep;
+ best_delta = 1000000;
+ while (samplep >= 0 && sample == -1)
+ {
+ delta_freq = note_freq - samples[samplep].base_note;
+ if (delta_freq < 0)
+ delta_freq = -delta_freq;
+ if (delta_freq < best_delta)
+ {
+ best_sample = samplep;
+ best_delta = delta_freq;
+ }
+ if (samples[samplep].low_note <= note_freq && note_freq <= samples[samplep].high_note)
+ sample = samplep;
+ else
+ samplep = samples[samplep].key; /*
+ * Follow link
+ */
+ }
+ if (sample == -1)
+ sample = best_sample;
+
+ if (sample == -1)
+ {
+ printk ("GUS: Patch %d not defined for note %d\n", patch, note_num);
+ return 0; /*
+ * Should play default patch ???
+ */
+ }
+
+ is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; /*
+ * 8 or 16
+ * bit
+ * samples
+ */
+ voices[voice].mode = samples[sample].mode;
+ voices[voice].patch_vol = samples[sample].volume;
+
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ int i;
+
+ for (i = 0; i < 6; i++)
+ {
+ voices[voice].env_rate[i] = samples[sample].env_rate[i];
+ voices[voice].env_offset[i] = samples[sample].env_offset[i];
+ }
+ }
+
+ sample_map[voice] = sample;
+
+ base_note = samples[sample].base_note / 100; /*
+ * To avoid overflows
+ */
+ note_freq /= 100;
+
+ freq = samples[sample].base_freq * note_freq / base_note;
+
+ voices[voice].orig_freq = freq;
+
+ /*
+ * Since the pitch bender may have been set before playing the note, we
+ * have to calculate the bending now.
+ */
+
+ freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range);
+ voices[voice].current_freq = freq;
+
+ pan = (samples[sample].panning + voices[voice].panning) / 32;
+ pan += 7;
+ if (pan < 0)
+ pan = 0;
+ if (pan > 15)
+ pan = 15;
+
+ if (samples[sample].mode & WAVE_16_BITS)
+ {
+ mode |= 0x04; /*
+ * 16 bits
+ */
+ if ((sample_ptrs[sample] >> 18) !=
+ ((sample_ptrs[sample] + samples[sample].len) >> 18))
+ printk ("GUS: Sample address error\n");
+ }
+
+ /*************************************************************************
+ * CAUTION! Interrupts disabled. Don't return before enabling
+ *************************************************************************/
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_off (); /*
+ * It may still be running
+ */
+ gus_rampoff ();
+
+ RESTORE_INTR (flags);
+
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ compute_volume (voice, volume);
+ init_envelope (voice);
+ }
+ else
+ compute_and_set_volume (voice, volume, 0);
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+
+ if (samples[sample].mode & WAVE_LOOP_BACK)
+ gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].len -
+ voices[voice].offset_pending, is16bits); /* Sample
+ * start=end */
+ else
+ gus_write_addr (0x0a, sample_ptrs[sample] + voices[voice].offset_pending,
+ is16bits); /* Sample start=begin */
+
+ if (samples[sample].mode & WAVE_LOOPING)
+ {
+ mode |= 0x08; /*
+ * Looping on
+ */
+
+ if (samples[sample].mode & WAVE_BIDIR_LOOP)
+ mode |= 0x10; /*
+ * Bidirectional looping on
+ */
+
+ if (samples[sample].mode & WAVE_LOOP_BACK)
+ {
+ gus_write_addr (0x0a,
+ sample_ptrs[sample] + samples[sample].loop_end -
+ voices[voice].offset_pending, is16bits);
+ mode |= 0x40;
+ }
+
+ gus_write_addr (0x02, sample_ptrs[sample] + samples[sample].loop_start, is16bits); /*
+ * Loop
+ * start
+ * location
+ */
+ gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].loop_end, is16bits); /*
+ * Loop
+ * end
+ * location
+ */
+ }
+ else
+ {
+ mode |= 0x20; /*
+ * Loop irq at the end
+ */
+ voices[voice].loop_irq_mode = LMODE_FINISH; /*
+ * Ramp it down at
+ * the * end
+ */
+ voices[voice].loop_irq_parm = 1;
+ gus_write_addr (0x02, sample_ptrs[sample], is16bits); /*
+ * Loop start
+ * location
+ */
+ gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].len - 1, is16bits); /*
+ * Loop
+ * end
+ * location
+ */
+ }
+ gus_voice_freq (freq);
+ gus_voice_balance (pan);
+ gus_voice_on (mode);
+ RESTORE_INTR (flags);
+
+ return 0;
+}
+
+/*
+ * * New guswave_start_note by Andrew J. Robinson attempts to minimize
+ * clicking * when the note playing on the voice is changed. It uses volume
+ * ramping. */
+
+static int
+guswave_start_note (int dev, int voice, int note_num, int volume)
+{
+ long int flags;
+ int mode;
+ int ret_val = 0;
+
+ DISABLE_INTR (flags);
+ if (note_num == 255)
+ {
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ voices[voice].volume_pending = volume;
+ else
+ {
+ RESTORE_INTR (flags);
+ ret_val = guswave_start_note2 (dev, voice, note_num, volume);
+ }
+ }
+ else
+ {
+ gus_select_voice (voice);
+ mode = gus_read8 (0x00);
+ if (mode & 0x20)
+ gus_write8 (0x00, mode & 0xdf); /* No interrupt! */
+
+ voices[voice].offset_pending = 0;
+ voices[voice].kill_pending = 0;
+ voices[voice].volume_irq_mode = 0;
+ voices[voice].loop_irq_mode = 0;
+
+ if (voices[voice].sample_pending >= 0)
+ {
+ RESTORE_INTR (flags);
+ guswave_set_instr (voices[voice].dev_pending, voice,
+ voices[voice].sample_pending);
+ voices[voice].sample_pending = -1;
+ DISABLE_INTR (flags);
+ }
+
+ if ((mode & 0x01) || ((gus_read16 (0x09) >> 4) < 2065))
+ {
+ ret_val = guswave_start_note2 (dev, voice, note_num, volume);
+ }
+ else
+ {
+ voices[voice].dev_pending = dev;
+ voices[voice].note_pending = note_num;
+ voices[voice].volume_pending = volume;
+ voices[voice].volume_irq_mode = VMODE_START_NOTE;
+
+ gus_rampoff ();
+ gus_ramp_range (2000, 4065);
+ gus_ramp_rate (0, 63);/* Fastest possible rate */
+ gus_rampon (0x20 | 0x40); /* Ramp down, once, irq */
+ RESTORE_INTR (flags);
+ }
+ }
+ return ret_val;
+}
+
+static void
+guswave_reset (int dev)
+{
+ int i;
+
+ for (i = 0; i < 32; i++)
+ {
+ gus_voice_init (i);
+ gus_voice_init2 (i);
+ }
+}
+
+static int
+guswave_open (int dev, int mode)
+{
+ int err;
+
+ if (gus_busy)
+ return RET_ERROR (EBUSY);
+
+ gus_initialize ();
+
+ if ((err = DMAbuf_open_dma (gus_devnum)))
+ return err;
+
+ RESET_WAIT_QUEUE (dram_sleeper, dram_sleep_flag);
+ gus_busy = 1;
+ active_device = GUS_DEV_WAVE;
+
+ gus_reset ();
+
+ return 0;
+}
+
+static void
+guswave_close (int dev)
+{
+ gus_busy = 0;
+ active_device = 0;
+ gus_reset ();
+
+ DMAbuf_close_dma (gus_devnum);
+}
+
+static int
+guswave_load_patch (int dev, int format, snd_rw_buf * addr,
+ int offs, int count, int pmgr_flag)
+{
+ struct patch_info patch;
+ int instr;
+ long sizeof_patch;
+
+ unsigned long blk_size, blk_end, left, src_offs, target;
+
+ sizeof_patch = (long) &patch.data[0] - (long) &patch; /*
+ * Size of
+ * the header
+ * * info
+ */
+
+ if (format != GUS_PATCH)
+ {
+ printk ("GUS Error: Invalid patch format (key) 0x%x\n", format);
+ return RET_ERROR (EINVAL);
+ }
+
+ if (count < sizeof_patch)
+ {
+ printk ("GUS Error: Patch header too short\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ count -= sizeof_patch;
+
+ if (free_sample >= MAX_SAMPLE)
+ {
+ printk ("GUS: Sample table full\n");
+ return RET_ERROR (ENOSPC);
+ }
+
+ /*
+ * Copy the header from user space but ignore the first bytes which have
+ * been transferred already.
+ */
+
+ COPY_FROM_USER (&((char *) &patch)[offs], addr, offs, sizeof_patch - offs);
+
+ instr = patch.instr_no;
+
+ if (instr < 0 || instr > MAX_PATCH)
+ {
+ printk ("GUS: Invalid patch number %d\n", instr);
+ return RET_ERROR (EINVAL);
+ }
+
+ if (count < patch.len)
+ {
+ printk ("GUS Warning: Patch record too short (%d<%d)\n",
+ count, (int) patch.len);
+ patch.len = count;
+ }
+
+ if (patch.len <= 0 || patch.len > gus_mem_size)
+ {
+ printk ("GUS: Invalid sample length %d\n", (int) patch.len);
+ return RET_ERROR (EINVAL);
+ }
+
+ if (patch.mode & WAVE_LOOPING)
+ {
+ if (patch.loop_start < 0 || patch.loop_start >= patch.len)
+ {
+ printk ("GUS: Invalid loop start\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len)
+ {
+ printk ("GUS: Invalid loop end\n");
+ return RET_ERROR (EINVAL);
+ }
+ }
+
+ free_mem_ptr = (free_mem_ptr + 31) & ~31; /*
+ * Alignment 32 bytes
+ */
+
+#define GUS_BANK_SIZE (256*1024)
+
+ if (patch.mode & WAVE_16_BITS)
+ {
+ /*
+ * 16 bit samples must fit one 256k bank.
+ */
+ if (patch.len >= GUS_BANK_SIZE)
+ {
+ printk ("GUS: Sample (16 bit) too long %d\n", (int) patch.len);
+ return RET_ERROR (ENOSPC);
+ }
+
+ if ((free_mem_ptr / GUS_BANK_SIZE) !=
+ ((free_mem_ptr + patch.len) / GUS_BANK_SIZE))
+ {
+ unsigned long tmp_mem = /*
+ * Align to 256K*N
+ */
+ ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE;
+
+ if ((tmp_mem + patch.len) > gus_mem_size)
+ return RET_ERROR (ENOSPC);
+
+ free_mem_ptr = tmp_mem; /*
+ * This leaves unusable memory
+ */
+ }
+ }
+
+ if ((free_mem_ptr + patch.len) > gus_mem_size)
+ return RET_ERROR (ENOSPC);
+
+ sample_ptrs[free_sample] = free_mem_ptr;
+
+ /*
+ * Tremolo is not possible with envelopes
+ */
+
+ if (patch.mode & WAVE_ENVELOPES)
+ patch.mode &= ~WAVE_TREMOLO;
+
+ memcpy ((char *) &samples[free_sample], &patch, sizeof_patch);
+
+ /*
+ * Link this_one sample to the list of samples for patch 'instr'.
+ */
+
+ samples[free_sample].key = patch_table[instr];
+ patch_table[instr] = free_sample;
+
+ /*
+ * Use DMA to transfer the wave data to the DRAM
+ */
+
+ left = patch.len;
+ src_offs = 0;
+ target = free_mem_ptr;
+
+ while (left) /*
+ * Not all moved
+ */
+ {
+ blk_size = sound_buffsizes[gus_devnum];
+ if (blk_size > left)
+ blk_size = left;
+
+ /*
+ * DMA cannot cross 256k bank boundaries. Check for that.
+ */
+ blk_end = target + blk_size;
+
+ if ((target >> 18) != (blk_end >> 18))
+ { /*
+ * Have to split the block
+ */
+
+ blk_end &= ~(256 * 1024 - 1);
+ blk_size = blk_end - target;
+ }
+
+#if defined(GUS_NO_DMA) || defined(GUS_PATCH_NO_DMA)
+ /*
+ * For some reason the DMA is not possible. We have to use PIO.
+ */
+ {
+ long i;
+ unsigned char data;
+
+ for (i = 0; i < blk_size; i++)
+ {
+ GET_BYTE_FROM_USER (data, addr, sizeof_patch + i);
+ if (patch.mode & WAVE_UNSIGNED)
+
+ if (!(patch.mode & WAVE_16_BITS) || (i & 0x01))
+ data ^= 0x80; /*
+ * Convert to signed
+ */
+ gus_poke (target + i, data);
+ }
+ }
+#else /*
+ * * * GUS_NO_DMA */
+ {
+ unsigned long address, hold_address;
+ unsigned char dma_command;
+ unsigned long flags;
+
+ /*
+ * OK, move now. First in and then out.
+ */
+
+ COPY_FROM_USER (snd_raw_buf[gus_devnum][0],
+ addr, sizeof_patch + src_offs,
+ blk_size);
+
+ DISABLE_INTR (flags); /******** INTERRUPTS DISABLED NOW ********/
+ gus_write8 (0x41, 0); /*
+ * Disable GF1 DMA
+ */
+ DMAbuf_start_dma (gus_devnum, snd_raw_buf_phys[gus_devnum][0],
+ blk_size, DMA_MODE_WRITE);
+
+ /*
+ * Set the DRAM address for the wave data
+ */
+
+ address = target;
+
+ if (sound_dsp_dmachan[gus_devnum] > 3)
+ {
+ hold_address = address;
+ address = address >> 1;
+ address &= 0x0001ffffL;
+ address |= (hold_address & 0x000c0000L);
+ }
+
+ gus_write16 (0x42, (address >> 4) & 0xffff); /*
+ * DRAM DMA address
+ */
+
+ /*
+ * Start the DMA transfer
+ */
+
+ dma_command = 0x21; /*
+ * IRQ enable, DMA start
+ */
+ if (patch.mode & WAVE_UNSIGNED)
+ dma_command |= 0x80; /*
+ * Invert MSB
+ */
+ if (patch.mode & WAVE_16_BITS)
+ dma_command |= 0x40; /*
+ * 16 bit _DATA_
+ */
+ if (sound_dsp_dmachan[gus_devnum] > 3)
+ dma_command |= 0x04; /*
+ * 16 bit DMA channel
+ */
+
+ gus_write8 (0x41, dma_command); /*
+ * Let's go luteet (=bugs)
+ */
+
+ /*
+ * Sleep here until the DRAM DMA done interrupt is served
+ */
+ active_device = GUS_DEV_WAVE;
+
+ DO_SLEEP (dram_sleeper, dram_sleep_flag, HZ);
+ if (TIMED_OUT (dram_sleeper, dram_sleep_flag))
+ printk ("GUS: DMA Transfer timed out\n");
+ RESTORE_INTR (flags);
+ }
+#endif /*
+ * * * GUS_NO_DMA */
+
+ /*
+ * Now the next part
+ */
+
+ left -= blk_size;
+ src_offs += blk_size;
+ target += blk_size;
+
+ gus_write8 (0x41, 0); /*
+ * Stop DMA
+ */
+ }
+
+ free_mem_ptr += patch.len;
+
+ if (!pmgr_flag)
+ pmgr_inform (dev, PM_E_PATCH_LOADED, instr, free_sample, 0, 0);
+ free_sample++;
+ return 0;
+}
+
+static void
+guswave_hw_control (int dev, unsigned char *event)
+{
+ int voice, cmd;
+ unsigned short p1, p2;
+ unsigned long plong, flags;
+
+ cmd = event[2];
+ voice = event[3];
+ p1 = *(unsigned short *) &event[4];
+ p2 = *(unsigned short *) &event[6];
+ plong = *(unsigned long *) &event[4];
+
+ if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) &&
+ (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS))
+ do_volume_irq (voice);
+
+ switch (cmd)
+ {
+
+ case _GUS_NUMVOICES:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_select_max_voices (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICESAMPLE:
+ guswave_set_instr (dev, voice, p1);
+ break;
+
+ case _GUS_VOICEON:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ p1 &= ~0x20; /*
+ * Disable intr
+ */
+ gus_voice_on (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEOFF:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_off ();
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEFADE:
+ gus_voice_fade (voice);
+ break;
+
+ case _GUS_VOICEMODE:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ p1 &= ~0x20; /*
+ * Disable intr
+ */
+ gus_voice_mode (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEBALA:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_balance (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEFREQ:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_freq (plong);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEVOL:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_volume (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEVOL2: /*
+ * Just update the voice value
+ */
+ voices[voice].initial_volume =
+ voices[voice].current_volume = p1;
+ break;
+
+ case _GUS_RAMPRANGE:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /*
+ * NO-NO
+ */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_ramp_range (p1, p2);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_RAMPRATE:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /*
+ * NO-NO
+ */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_ramp_rate (p1, p2);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_RAMPMODE:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /*
+ * NO-NO
+ */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ p1 &= ~0x20; /*
+ * Disable intr
+ */
+ gus_ramp_mode (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_RAMPON:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /*
+ * NO-NO
+ */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ p1 &= ~0x20; /*
+ * Disable intr
+ */
+ gus_rampon (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_RAMPOFF:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /*
+ * NO-NO
+ */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_rampoff ();
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOLUME_SCALE:
+ volume_base = p1;
+ volume_scale = p2;
+ break;
+
+ case _GUS_VOICE_POS:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_set_voice_pos (voice, plong);
+ RESTORE_INTR (flags);
+ break;
+
+ default:;
+ }
+}
+
+static int
+gus_sampling_set_speed (int speed)
+{
+ if (speed <= 0)
+ return gus_sampling_speed;
+
+ if (speed > 44100)
+ speed = 44100;
+
+ gus_sampling_speed = speed;
+ return speed;
+}
+
+static int
+gus_sampling_set_channels (int channels)
+{
+ if (!channels)
+ return gus_sampling_channels;
+ if (channels > 2)
+ channels = 2;
+ if (channels < 1)
+ channels = 1;
+ gus_sampling_channels = channels;
+ return channels;
+}
+
+static int
+gus_sampling_set_bits (int bits)
+{
+ if (!bits)
+ return gus_sampling_bits;
+
+ if (bits != 8 && bits != 16)
+ bits = 8;
+
+ gus_sampling_bits = bits;
+ return bits;
+}
+
+static int
+gus_sampling_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return gus_sampling_set_speed (arg);
+ return IOCTL_OUT (arg, gus_sampling_set_speed (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return gus_sampling_speed;
+ return IOCTL_OUT (arg, gus_sampling_speed);
+ break;
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return gus_sampling_set_channels (arg + 1) - 1;
+ return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg) + 1) - 1);
+ break;
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return gus_sampling_set_channels (arg);
+ return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return gus_sampling_channels;
+ return IOCTL_OUT (arg, gus_sampling_channels);
+ break;
+
+ case SNDCTL_DSP_SAMPLESIZE:
+ if (local)
+ return gus_sampling_set_bits (arg);
+ return IOCTL_OUT (arg, gus_sampling_set_bits (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return gus_sampling_bits;
+ return IOCTL_OUT (arg, gus_sampling_bits);
+
+ case SOUND_PCM_WRITE_FILTER: /*
+ * NOT YET IMPLEMENTED
+ */
+ return IOCTL_OUT (arg, RET_ERROR (EINVAL));
+ break;
+
+ case SOUND_PCM_READ_FILTER:
+ return IOCTL_OUT (arg, RET_ERROR (EINVAL));
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ return RET_ERROR (EINVAL);
+}
+
+static void
+gus_sampling_reset (int dev)
+{
+}
+
+static int
+gus_sampling_open (int dev, int mode)
+{
+#ifdef GUS_NO_DMA
+ printk ("GUS: DMA mode not enabled. Device not supported\n");
+ return RET_ERROR (ENXIO);
+#endif
+
+ if (gus_busy)
+ return RET_ERROR (EBUSY);
+
+ gus_initialize ();
+
+ gus_busy = 1;
+ active_device = 0;
+
+ gus_reset ();
+ reset_sample_memory ();
+ gus_select_max_voices (14);
+
+ pcm_active = 0;
+ pcm_opened = 1;
+ if (mode & OPEN_READ)
+ {
+ recording_active = 1;
+ set_input_volumes ();
+ }
+
+ return 0;
+}
+
+static void
+gus_sampling_close (int dev)
+{
+ gus_reset ();
+ gus_busy = 0;
+ pcm_opened = 0;
+ active_device = 0;
+
+ if (recording_active)
+ set_input_volumes ();
+
+ recording_active = 0;
+}
+
+static void
+gus_sampling_update_volume (void)
+{
+ unsigned long flags;
+ int voice;
+
+ DISABLE_INTR (flags);
+ if (pcm_active && pcm_opened)
+ for (voice = 0; voice < gus_sampling_channels; voice++)
+ {
+ gus_select_voice (voice);
+ gus_rampoff ();
+ gus_voice_volume (1530 + (25 * gus_pcm_volume));
+ gus_ramp_range (65, 1530 + (25 * gus_pcm_volume));
+ }
+ RESTORE_INTR (flags);
+}
+
+static void
+play_next_pcm_block (void)
+{
+ unsigned long flags;
+ int speed = gus_sampling_speed;
+ int this_one, is16bits, chn;
+ unsigned long dram_loc;
+ unsigned char mode[2], ramp_mode[2];
+
+ if (!pcm_qlen)
+ return;
+
+ this_one = pcm_head;
+
+ for (chn = 0; chn < gus_sampling_channels; chn++)
+ {
+ mode[chn] = 0x00;
+ ramp_mode[chn] = 0x03; /*
+ * Ramping and rollover off
+ */
+
+ if (chn == 0)
+ {
+ mode[chn] |= 0x20; /*
+ * Loop irq
+ */
+ voices[chn].loop_irq_mode = LMODE_PCM;
+ }
+
+ if (gus_sampling_bits != 8)
+ {
+ is16bits = 1;
+ mode[chn] |= 0x04; /*
+ * 16 bit data
+ */
+ }
+ else
+ is16bits = 0;
+
+ dram_loc = this_one * pcm_bsize;
+ dram_loc += chn * pcm_banksize;
+
+ if (this_one == (pcm_nblk - 1)) /*
+ * Last of the DRAM buffers
+ */
+ {
+ mode[chn] |= 0x08; /*
+ * Enable loop
+ */
+ ramp_mode[chn] = 0x03;/*
+ * Disable rollover
+ */
+ }
+ else
+ {
+ if (chn == 0)
+ ramp_mode[chn] = 0x04; /*
+ * Enable rollover bit
+ */
+ }
+
+ DISABLE_INTR (flags);
+ gus_select_voice (chn);
+ gus_voice_freq (speed);
+
+ if (gus_sampling_channels == 1)
+ gus_voice_balance (7); /*
+ * mono
+ */
+ else if (chn == 0)
+ gus_voice_balance (0); /*
+ * left
+ */
+ else
+ gus_voice_balance (15); /*
+ * right
+ */
+
+ if (!pcm_active) /*
+ * Voice not started yet
+ */
+ {
+ /*
+ * The playback was not started yet (or there has been a pause).
+ * Start the voice (again) and ask for a rollover irq at the end of
+ * this_one block. If this_one one is last of the buffers, use just
+ * the normal loop with irq.
+ */
+
+ gus_voice_off (); /*
+ * It could already be running
+ */
+ gus_rampoff ();
+ gus_voice_volume (1530 + (25 * gus_pcm_volume));
+ gus_ramp_range (65, 1530 + (25 * gus_pcm_volume));
+
+ gus_write_addr (0x0a, dram_loc, is16bits); /*
+ * Starting position
+ */
+ gus_write_addr (0x02, chn * pcm_banksize, is16bits); /*
+ * Loop start
+ * location
+ */
+
+ if (chn != 0)
+ gus_write_addr (0x04, pcm_banksize + (pcm_bsize * pcm_nblk),
+ is16bits); /*
+ * Loop end location
+ */
+ }
+
+ if (chn == 0)
+ gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits); /*
+ * Loop
+ * end
+ * location
+ */
+ else
+ mode[chn] |= 0x08; /*
+ * Enable loop
+ */
+
+ if (pcm_datasize[this_one] != pcm_bsize)
+ {
+ /*
+ * Incomplete block. Possibly the last one.
+ */
+ if (chn == 0)
+ {
+ mode[chn] &= ~0x08; /*
+ * Disable loop
+ */
+ mode[chn] |= 0x20;/*
+ * Enable loop IRQ
+ */
+ voices[0].loop_irq_mode = LMODE_PCM_STOP;
+ ramp_mode[chn] = 0x03; /*
+ * No rollover bit
+ */
+ }
+ else
+ {
+ gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits); /*
+ * Loop
+ * end
+ * location
+ */
+ mode[chn] &= ~0x08; /*
+ * Disable loop
+ */
+ }
+ }
+
+ RESTORE_INTR (flags);
+ }
+
+ for (chn = 0; chn < gus_sampling_channels; chn++)
+ {
+ DISABLE_INTR (flags);
+ gus_select_voice (chn);
+ gus_write8 (0x0d, ramp_mode[chn]);
+ gus_voice_on (mode[chn]);
+ RESTORE_INTR (flags);
+ }
+
+ pcm_active = 1;
+}
+
+static void
+gus_transfer_output_block (int dev, unsigned long buf,
+ int total_count, int intrflag, int chn)
+{
+ /*
+ * This routine transfers one block of audio data to the DRAM. In mono mode
+ * it's called just once. When in stereo mode, this_one routine is called
+ * once for both channels.
+ *
+ * The left/mono channel data is transferred to the beginning of dram and the
+ * right data to the area pointed by gus_page_size.
+ */
+
+ int this_one, count;
+ unsigned long flags;
+ unsigned char dma_command;
+ unsigned long address, hold_address;
+
+ DISABLE_INTR (flags);
+
+ count = total_count / gus_sampling_channels;
+
+ if (chn == 0)
+ {
+ if (pcm_qlen >= pcm_nblk)
+ printk ("GUS Warning: PCM buffers out of sync\n");
+
+ this_one = pcm_current_block = pcm_tail;
+ pcm_qlen++;
+ pcm_tail = (pcm_tail + 1) % pcm_nblk;
+ pcm_datasize[this_one] = count;
+ }
+ else
+ this_one = pcm_current_block;
+
+ gus_write8 (0x41, 0); /*
+ * Disable GF1 DMA
+ */
+ DMAbuf_start_dma (dev, buf + (chn * count), count, DMA_MODE_WRITE);
+
+ address = this_one * pcm_bsize;
+ address += chn * pcm_banksize;
+
+ if (sound_dsp_dmachan[dev] > 3)
+ {
+ hold_address = address;
+ address = address >> 1;
+ address &= 0x0001ffffL;
+ address |= (hold_address & 0x000c0000L);
+ }
+
+ gus_write16 (0x42, (address >> 4) & 0xffff); /*
+ * DRAM DMA address
+ */
+
+ dma_command = 0x21; /*
+ * IRQ enable, DMA start
+ */
+
+ if (gus_sampling_bits != 8)
+ dma_command |= 0x40; /*
+ * 16 bit _DATA_
+ */
+ else
+ dma_command |= 0x80; /*
+ * Invert MSB
+ */
+
+ if (sound_dsp_dmachan[dev] > 3)
+ dma_command |= 0x04; /*
+ * 16 bit DMA channel
+ */
+
+ gus_write8 (0x41, dma_command); /*
+ * Kick on
+ */
+
+ if (chn == (gus_sampling_channels - 1)) /*
+ * Last channel
+ */
+ {
+ /*
+ * Last (right or mono) channel data
+ */
+ active_device = GUS_DEV_PCM_DONE;
+ if (!pcm_active && (pcm_qlen > 2 || count < pcm_bsize))
+ {
+ play_next_pcm_block ();
+ }
+ }
+ else /*
+ * * * Left channel data. The right channel
+ * is * * * transferred after DMA interrupt */
+ active_device = GUS_DEV_PCM_CONTINUE;
+
+ RESTORE_INTR (flags);
+}
+
+static void
+gus_sampling_output_block (int dev, unsigned long buf, int total_count,
+ int intrflag, int restart_dma)
+{
+ pcm_current_buf = buf;
+ pcm_current_count = total_count;
+ pcm_current_intrflag = intrflag;
+ pcm_current_dev = dev;
+ gus_transfer_output_block (dev, buf, total_count, intrflag, 0);
+}
+
+static void
+gus_sampling_start_input (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
+{
+ unsigned long flags;
+ unsigned char mode;
+
+ DISABLE_INTR (flags);
+
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+
+ mode = 0xa0; /*
+ * DMA IRQ enable, invert MSB
+ */
+
+ if (sound_dsp_dmachan[dev] > 3)
+ mode |= 0x04; /*
+ * 16 bit DMA channel
+ */
+ if (gus_sampling_channels > 1)
+ mode |= 0x02; /*
+ * Stereo
+ */
+ mode |= 0x01; /*
+ * DMA enable
+ */
+
+ gus_write8 (0x49, mode);
+
+ RESTORE_INTR (flags);
+}
+
+static int
+gus_sampling_prepare_for_input (int dev, int bsize, int bcount)
+{
+ unsigned int rate;
+
+ rate = (9878400 / (gus_sampling_speed + 2)) / 16;
+
+ gus_write8 (0x48, rate & 0xff); /*
+ * Set sampling frequency
+ */
+
+ if (gus_sampling_bits != 8)
+ {
+ printk ("GUS Error: 16 bit recording not supported\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ return 0;
+}
+
+static int
+gus_sampling_prepare_for_output (int dev, int bsize, int bcount)
+{
+ int i;
+
+ long mem_ptr, mem_size;
+
+ mem_ptr = 0;
+ mem_size = gus_mem_size / gus_sampling_channels;
+
+ if (mem_size > (256 * 1024))
+ mem_size = 256 * 1024;
+
+ pcm_bsize = bsize / gus_sampling_channels;
+ pcm_head = pcm_tail = pcm_qlen = 0;
+
+ pcm_nblk = MAX_PCM_BUFFERS;
+ if ((pcm_bsize * pcm_nblk) > mem_size)
+ pcm_nblk = mem_size / pcm_bsize;
+
+ for (i = 0; i < pcm_nblk; i++)
+ pcm_datasize[i] = 0;
+
+ pcm_banksize = pcm_nblk * pcm_bsize;
+
+ if (gus_sampling_bits != 8 && pcm_banksize == (256 * 1024))
+ pcm_nblk--;
+
+ return 0;
+}
+
+static int
+gus_has_output_drained (int dev)
+{
+ return !pcm_qlen;
+}
+
+static void
+gus_copy_from_user (int dev, char *localbuf, int localoffs,
+ snd_rw_buf * userbuf, int useroffs, int len)
+{
+ if (gus_sampling_channels == 1)
+ {
+ COPY_FROM_USER (&localbuf[localoffs], userbuf, useroffs, len);
+ }
+ else if (gus_sampling_bits == 8)
+ {
+ int in_left = useroffs;
+ int in_right = useroffs + 1;
+ char *out_left, *out_right;
+ int i;
+
+ len /= 2;
+ localoffs /= 2;
+ out_left = &localbuf[localoffs];
+ out_right = out_left + pcm_bsize;
+
+ for (i = 0; i < len; i++)
+ {
+ GET_BYTE_FROM_USER (*out_left++, userbuf, in_left);
+ in_left += 2;
+ GET_BYTE_FROM_USER (*out_right++, userbuf, in_right);
+ in_right += 2;
+ }
+ }
+ else
+ {
+ int in_left = useroffs;
+ int in_right = useroffs + 1;
+ short *out_left, *out_right;
+ int i;
+
+ len /= 4;
+ localoffs /= 4;
+
+ out_left = (short *) &localbuf[localoffs];
+ out_right = out_left + (pcm_bsize / 2);
+
+ for (i = 0; i < len; i++)
+ {
+ GET_SHORT_FROM_USER (*out_left++, (short *) userbuf, in_left);
+ in_left += 2;
+ GET_SHORT_FROM_USER (*out_right++, (short *) userbuf, in_right);
+ in_right += 2;
+ }
+ }
+}
+
+static struct audio_operations gus_sampling_operations =
+{
+ "Gravis UltraSound",
+ NEEDS_RESTART,
+ gus_sampling_open,
+ gus_sampling_close,
+ gus_sampling_output_block,
+ gus_sampling_start_input,
+ gus_sampling_ioctl,
+ gus_sampling_prepare_for_input,
+ gus_sampling_prepare_for_output,
+ gus_sampling_reset,
+ gus_sampling_reset,
+ gus_has_output_drained,
+ gus_copy_from_user
+};
+
+#ifdef FUTURE_VERSION
+static void
+guswave_bender (int dev, int voice, int value)
+{
+ int freq;
+ unsigned long flags;
+
+ voices[voice].bender = value - 8192;
+ freq = compute_finetune (voices[voice].orig_freq, value, voices[voice].bender_range);
+ voices[voice].current_freq = freq;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_freq (freq);
+ RESTORE_INTR (flags);
+}
+
+#endif
+
+static int
+guswave_patchmgr (int dev, struct patmgr_info *rec)
+{
+ int i, n;
+
+ switch (rec->command)
+ {
+ case PM_GET_DEVTYPE:
+ rec->parm1 = PMTYPE_WAVE;
+ return 0;
+ break;
+
+ case PM_GET_NRPGM:
+ rec->parm1 = MAX_PATCH;
+ return 0;
+ break;
+
+ case PM_GET_PGMMAP:
+ rec->parm1 = MAX_PATCH;
+
+ for (i = 0; i < MAX_PATCH; i++)
+ {
+ int ptr = patch_table[i];
+
+ rec->data.data8[i] = 0;
+
+ while (ptr >= 0 && ptr < free_sample)
+ {
+ rec->data.data8[i]++;
+ ptr = samples[ptr].key; /*
+ * Follow link
+ */
+ }
+ }
+ return 0;
+ break;
+
+ case PM_GET_PGM_PATCHES:
+ {
+ int ptr = patch_table[rec->parm1];
+
+ n = 0;
+
+ while (ptr >= 0 && ptr < free_sample)
+ {
+ rec->data.data32[n++] = ptr;
+ ptr = samples[ptr].key; /*
+ * Follow link
+ */
+ }
+ }
+ rec->parm1 = n;
+ return 0;
+ break;
+
+ case PM_GET_PATCH:
+ {
+ int ptr = rec->parm1;
+ struct patch_info *pat;
+
+ if (ptr < 0 || ptr >= free_sample)
+ return RET_ERROR (EINVAL);
+
+ memcpy (rec->data.data8, (char *) &samples[ptr],
+ sizeof (struct patch_info));
+
+ pat = (struct patch_info *) rec->data.data8;
+
+ pat->key = GUS_PATCH; /*
+ * Restore patch type
+ */
+ rec->parm1 = sample_ptrs[ptr]; /*
+ * DRAM address
+ */
+ rec->parm2 = sizeof (struct patch_info);
+ }
+ return 0;
+ break;
+
+ case PM_SET_PATCH:
+ {
+ int ptr = rec->parm1;
+ struct patch_info *pat;
+
+ if (ptr < 0 || ptr >= free_sample)
+ return RET_ERROR (EINVAL);
+
+ pat = (struct patch_info *) rec->data.data8;
+
+ if (pat->len > samples[ptr].len) /*
+ * Cannot expand sample
+ */
+ return RET_ERROR (EINVAL);
+
+ pat->key = samples[ptr].key; /*
+ * Ensure the link is correct
+ */
+
+ memcpy ((char *) &samples[ptr], rec->data.data8,
+ sizeof (struct patch_info));
+
+ pat->key = GUS_PATCH;
+ }
+ return 0;
+ break;
+
+ case PM_READ_PATCH: /*
+ * Returns a block of wave data from the DRAM
+ */
+ {
+ int sample = rec->parm1;
+ int n;
+ long offs = rec->parm2;
+ int l = rec->parm3;
+
+ if (sample < 0 || sample >= free_sample)
+ return RET_ERROR (EINVAL);
+
+ if (offs < 0 || offs >= samples[sample].len)
+ return RET_ERROR (EINVAL); /*
+ * Invalid offset
+ */
+
+ n = samples[sample].len - offs; /*
+ * Nr of bytes left
+ */
+
+ if (l > n)
+ l = n;
+
+ if (l > sizeof (rec->data.data8))
+ l = sizeof (rec->data.data8);
+
+ if (l <= 0)
+ return RET_ERROR (EINVAL); /*
+ * Was there a bug?
+ */
+
+ offs += sample_ptrs[sample]; /*
+ * Begin offsess + offset to DRAM
+ */
+
+ for (n = 0; n < l; n++)
+ rec->data.data8[n] = gus_peek (offs++);
+ rec->parm1 = n; /*
+ * Nr of bytes copied
+ */
+ }
+ return 0;
+ break;
+
+ case PM_WRITE_PATCH: /*
+ * Writes a block of wave data to the DRAM
+ */
+ {
+ int sample = rec->parm1;
+ int n;
+ long offs = rec->parm2;
+ int l = rec->parm3;
+
+ if (sample < 0 || sample >= free_sample)
+ return RET_ERROR (EINVAL);
+
+ if (offs < 0 || offs >= samples[sample].len)
+ return RET_ERROR (EINVAL); /*
+ * Invalid offset
+ */
+
+ n = samples[sample].len - offs; /*
+ * Nr of bytes left
+ */
+
+ if (l > n)
+ l = n;
+
+ if (l > sizeof (rec->data.data8))
+ l = sizeof (rec->data.data8);
+
+ if (l <= 0)
+ return RET_ERROR (EINVAL); /*
+ * Was there a bug?
+ */
+
+ offs += sample_ptrs[sample]; /*
+ * Begin offsess + offset to DRAM
+ */
+
+ for (n = 0; n < l; n++)
+ gus_poke (offs++, rec->data.data8[n]);
+ rec->parm1 = n; /*
+ * Nr of bytes copied
+ */
+ }
+ return 0;
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+}
+
+static struct synth_operations guswave_operations =
+{
+ &gus_info,
+#ifdef FUTURE_VERSION
+ 0,
+#endif
+ SYNTH_TYPE_SAMPLE,
+ SAMPLE_TYPE_GUS,
+ guswave_open,
+ guswave_close,
+ guswave_ioctl,
+ guswave_kill_note,
+ guswave_start_note,
+ guswave_set_instr,
+ guswave_reset,
+ guswave_hw_control,
+ guswave_load_patch,
+ guswave_aftertouch,
+ guswave_controller,
+ guswave_panning,
+ guswave_volume_method,
+ guswave_patchmgr,
+#ifdef FUTURE_VERSION
+ guswave_bender
+#endif
+};
+
+static void
+set_input_volumes (void)
+{
+ unsigned long flags;
+ unsigned char mask = 0xff & ~0x06; /* Just line out enabled */
+
+ DISABLE_INTR (flags);
+
+ /*
+ * Enable channels having vol > 10%
+ * Note! bit 0x01 means line in DISABLED while 0x04 means
+ * mic in ENABLED.
+ */
+ if (gus_line_vol > 10)
+ mask &= ~0x01;
+ if (gus_mic_vol > 10)
+ mask |= 0x04;
+
+ if (recording_active)
+ {
+ /*
+ * Disable channel, if not selected for recording
+ */
+ if (!(gus_recmask & SOUND_MASK_LINE))
+ mask |= 0x01;
+ if (!(gus_recmask & SOUND_MASK_MIC))
+ mask &= ~0x04;
+ }
+
+ mix_image &= ~0x07;
+ mix_image |= mask & 0x07;
+ OUTB (mix_image, u_Mixer);
+
+ RESTORE_INTR (flags);
+}
+
+int
+gus_default_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
+{
+#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \
+ SOUND_MASK_SYNTH|SOUND_MASK_PCM)
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ if (cmd & IOC_IN)
+ switch (cmd & 0xff)
+ {
+ case SOUND_MIXER_RECSRC:
+ gus_recmask = IOCTL_IN (arg) & MIX_DEVS;
+ if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE)))
+ gus_recmask = SOUND_MASK_MIC;
+ /* Note! Input volumes are updated during next open for recording */
+ return IOCTL_OUT (arg, gus_recmask);
+ break;
+
+ case SOUND_MIXER_MIC:
+ {
+ int vol = IOCTL_IN (arg) & 0xff;
+
+ if (vol < 0)
+ vol = 0;
+ if (vol > 100)
+ vol = 100;
+ gus_mic_vol = vol;
+ set_input_volumes ();
+ return IOCTL_OUT (arg, vol | (vol << 8));
+ }
+ break;
+
+ case SOUND_MIXER_LINE:
+ {
+ int vol = IOCTL_IN (arg) & 0xff;
+
+ if (vol < 0)
+ vol = 0;
+ if (vol > 100)
+ vol = 100;
+ gus_line_vol = vol;
+ set_input_volumes ();
+ return IOCTL_OUT (arg, vol | (vol << 8));
+ }
+ break;
+
+ case SOUND_MIXER_PCM:
+ gus_pcm_volume = IOCTL_IN (arg) & 0xff;
+ if (gus_pcm_volume < 0)
+ gus_pcm_volume = 0;
+ if (gus_pcm_volume > 100)
+ gus_pcm_volume = 100;
+ gus_sampling_update_volume ();
+ return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8));
+ break;
+
+ case SOUND_MIXER_SYNTH:
+ {
+ int voice;
+
+ gus_wave_volume = IOCTL_IN (arg) & 0xff;
+
+ if (gus_wave_volume < 0)
+ gus_wave_volume = 0;
+ if (gus_wave_volume > 100)
+ gus_wave_volume = 100;
+
+ if (active_device == GUS_DEV_WAVE)
+ for (voice = 0; voice < nr_voices; voice++)
+ dynamic_volume_change (voice); /*
+ * Apply the new
+ * volume
+ */
+
+ return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8));
+ }
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ else
+ switch (cmd & 0xff) /*
+ * Return parameters
+ */
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return IOCTL_OUT (arg, gus_recmask);
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return IOCTL_OUT (arg, MIX_DEVS);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ return IOCTL_OUT (arg, 0);
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return IOCTL_OUT (arg, SOUND_MASK_MIC | SOUND_MASK_LINE);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return IOCTL_OUT (arg, 0);
+ break;
+
+ case SOUND_MIXER_MIC:
+ return IOCTL_OUT (arg, gus_mic_vol | (gus_mic_vol << 8));
+ break;
+
+ case SOUND_MIXER_LINE:
+ return IOCTL_OUT (arg, gus_line_vol | (gus_line_vol << 8));
+ break;
+
+ case SOUND_MIXER_PCM:
+ return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8));
+ break;
+
+ case SOUND_MIXER_SYNTH:
+ return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8));
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ }
+ else
+ return RET_ERROR (EINVAL);
+}
+
+static struct mixer_operations gus_mixer_operations =
+{
+ gus_default_mixer_ioctl
+};
+
+static long
+gus_default_mixer_init (long mem_start)
+{
+ if (num_mixers < MAX_MIXER_DEV) /*
+ * Don't install if there is another
+ * mixer
+ */
+ mixer_devs[num_mixers++] = &gus_mixer_operations;
+
+ return mem_start;
+}
+
+long
+gus_wave_init (long mem_start, int irq, int dma)
+{
+ unsigned long flags;
+ unsigned char val;
+ char *model_num = "2.4";
+ int gus_type = 0x24; /* 2.4 */
+ int mixer_type = 0;
+
+ /*
+ * Try to identify the GUS model.
+ *
+ * Versions < 3.6 don't have the digital ASIC. Try to probe it first.
+ */
+
+ DISABLE_INTR (flags);
+ OUTB (0x20, gus_base + 0x0f);
+ val = INB (gus_base + 0x0f);
+ RESTORE_INTR (flags);
+
+ if (val != 0xff && (val & 0x06)) /* Should be 0x02? */
+ {
+ /*
+ * It has the digital ASIC so the card is at least v3.4.
+ * Next try to detect the true model.
+ */
+
+ val = INB (u_MixSelect);
+
+ /*
+ * Value 255 means pre-3.7 which don't have mixer.
+ * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer.
+ * 10 and above is GUS MAX which has the CS4231 codec/mixer.
+ *
+ * Sorry. No GUS max support yet but it should be available
+ * soon after the SDK for GUS MAX is available.
+ */
+
+ if (val == 255 || val < 5)
+ {
+ model_num = "3.4";
+ gus_type = 0x34;
+ }
+ else if (val < 10)
+ {
+ model_num = "3.7";
+ gus_type = 0x37;
+ mixer_type = ICS2101;
+ }
+ else
+ {
+ model_num = "MAX";
+ gus_type = 0x40;
+ mixer_type = CS4231;
+ }
+ }
+ else
+ {
+ /*
+ * ASIC not detected so the card must be 2.2 or 2.4.
+ * There could still be the 16-bit/mixer daughter card.
+ * It has the same codec/mixer than MAX.
+ * At this time there is no support for it but it will appear soon.
+ */
+ }
+
+
+#ifdef __FreeBSD__
+ printk ("snd4: <Gravis UltraSound %s (%dk)>", model_num, (int) gus_mem_size / 1024);
+#else
+ printk (" <Gravis UltraSound %s (%dk)>", model_num, (int) gus_mem_size / 1024);
+#endif
+
+#ifndef SCO
+ sprintf (gus_info.name, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024);
+#endif
+
+ if (irq < 0 || irq > 15)
+ {
+ printk ("ERROR! Invalid IRQ#%d. GUS Disabled", irq);
+ return mem_start;
+ }
+
+ if (dma < 0 || dma > 7)
+ {
+ printk ("ERROR! Invalid DMA#%d. GUS Disabled", dma);
+ return mem_start;
+ }
+
+ gus_irq = irq;
+ gus_dma = dma;
+
+ if (num_synths >= MAX_SYNTH_DEV)
+ printk ("GUS Error: Too many synthesizers\n");
+ else
+ synth_devs[num_synths++] = &guswave_operations;
+
+ PERMANENT_MALLOC (struct patch_info *, samples,
+ (MAX_SAMPLE + 1) * sizeof (*samples), mem_start);
+
+ reset_sample_memory ();
+
+ gus_initialize ();
+
+ if (num_dspdevs < MAX_DSP_DEV)
+ {
+ dsp_devs[gus_devnum = num_dspdevs++] = &gus_sampling_operations;
+ sound_dsp_dmachan[gus_devnum] = dma;
+ sound_buffcounts[gus_devnum] = DSP_BUFFCOUNT;
+ sound_buffsizes[gus_devnum] = DSP_BUFFSIZE;
+ sound_dma_automode[gus_devnum] = 0;
+ }
+ else
+ printk ("GUS: Too many PCM devices available\n");
+
+ /*
+ * Mixer dependent initialization.
+ */
+
+ switch (mixer_type)
+ {
+ case ICS2101:
+ gus_line_vol=gus_mic_vol=gus_wave_volume = gus_pcm_volume = 100;
+ return ics2101_mixer_init (mem_start);
+
+ case CS4231:
+ /* Available soon */
+ default:
+ return gus_default_mixer_init (mem_start);
+ }
+
+ return mem_start;
+}
+
+static void
+do_loop_irq (int voice)
+{
+ unsigned char tmp;
+ int mode, parm;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+
+ tmp = gus_read8 (0x00);
+ tmp &= ~0x20; /*
+ * Disable wave IRQ for this_one voice
+ */
+ gus_write8 (0x00, tmp);
+
+ mode = voices[voice].loop_irq_mode;
+ voices[voice].loop_irq_mode = 0;
+ parm = voices[voice].loop_irq_parm;
+
+ switch (mode)
+ {
+
+ case LMODE_FINISH: /*
+ * Final loop finished, shoot volume down
+ */
+
+ if ((gus_read16 (0x09) >> 4) < 100) /*
+ * Get current volume
+ */
+ {
+ gus_voice_off ();
+ gus_rampoff ();
+ gus_voice_init (voice);
+ break;
+ }
+ gus_ramp_range (65, 4065);
+ gus_ramp_rate (0, 63); /*
+ * Fastest possible rate
+ */
+ gus_rampon (0x20 | 0x40); /*
+ * Ramp down, once, irq
+ */
+ voices[voice].volume_irq_mode = VMODE_HALT;
+ break;
+
+ case LMODE_PCM_STOP:
+ pcm_active = 0; /*
+ * Requires extensive processing
+ */
+ case LMODE_PCM:
+ {
+ int orig_qlen = pcm_qlen;
+
+ pcm_qlen--;
+ pcm_head = (pcm_head + 1) % pcm_nblk;
+ if (pcm_qlen)
+ {
+ play_next_pcm_block ();
+ }
+ else
+ { /*
+ * Out of data. Just stop the voice
+ */
+ gus_voice_off ();
+ gus_rampoff ();
+ pcm_active = 0;
+ }
+
+ if (orig_qlen == pcm_nblk)
+ {
+ DMAbuf_outputintr (gus_devnum, 0);
+ }
+ }
+ break;
+
+ default:;
+ }
+ RESTORE_INTR (flags);
+}
+
+static void
+do_volume_irq (int voice)
+{
+ unsigned char tmp;
+ int mode, parm;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ gus_select_voice (voice);
+
+ tmp = gus_read8 (0x0d);
+ tmp &= ~0x20; /*
+ * Disable volume ramp IRQ
+ */
+ gus_write8 (0x0d, tmp);
+
+ mode = voices[voice].volume_irq_mode;
+ voices[voice].volume_irq_mode = 0;
+ parm = voices[voice].volume_irq_parm;
+
+ switch (mode)
+ {
+ case VMODE_HALT: /*
+ * Decay phase finished
+ */
+ RESTORE_INTR (flags);
+ gus_voice_init (voice);
+ break;
+
+ case VMODE_ENVELOPE:
+ gus_rampoff ();
+ RESTORE_INTR (flags);
+ step_envelope (voice);
+ break;
+
+ case VMODE_START_NOTE:
+ RESTORE_INTR (flags);
+ guswave_start_note2 (voices[voice].dev_pending, voice,
+ voices[voice].note_pending, voices[voice].volume_pending);
+ if (voices[voice].kill_pending)
+ guswave_kill_note (voices[voice].dev_pending, voice, 0);
+ if (voices[voice].sample_pending >= 0)
+ {
+ guswave_set_instr (voices[voice].dev_pending, voice,
+ voices[voice].sample_pending);
+ voices[voice].sample_pending = -1;
+ }
+ break;
+
+ default:;
+ }
+}
+
+void
+gus_voice_irq (void)
+{
+ unsigned long wave_ignore = 0, volume_ignore = 0;
+ unsigned long voice_bit;
+
+ unsigned char src, voice;
+
+ while (1)
+ {
+ src = gus_read8 (0x0f); /*
+ * Get source info
+ */
+ voice = src & 0x1f;
+ src &= 0xc0;
+
+ if (src == (0x80 | 0x40))
+ return; /*
+ * No interrupt
+ */
+
+ voice_bit = 1 << voice;
+
+ if (!(src & 0x80)) /*
+ * Wave IRQ pending
+ */
+ if (!(wave_ignore & voice_bit) && voice < nr_voices) /*
+ * Not done
+ * yet
+ */
+ {
+ wave_ignore |= voice_bit;
+ do_loop_irq (voice);
+ }
+
+ if (!(src & 0x40)) /*
+ * Volume IRQ pending
+ */
+ if (!(volume_ignore & voice_bit) && voice < nr_voices) /*
+ * Not done
+ * yet
+ */
+ {
+ volume_ignore |= voice_bit;
+ do_volume_irq (voice);
+ }
+ }
+}
+
+void
+guswave_dma_irq (void)
+{
+ unsigned char status;
+
+ status = gus_look8 (0x41); /*
+ * Get DMA IRQ Status
+ */
+ if (status & 0x40) /*
+ * DMA Irq pending
+ */
+ switch (active_device)
+ {
+ case GUS_DEV_WAVE:
+ if (SOMEONE_WAITING (dram_sleeper, dram_sleep_flag))
+ WAKE_UP (dram_sleeper, dram_sleep_flag);
+ break;
+
+ case GUS_DEV_PCM_CONTINUE:
+ gus_transfer_output_block (pcm_current_dev, pcm_current_buf,
+ pcm_current_count,
+ pcm_current_intrflag, 1);
+ break;
+
+ case GUS_DEV_PCM_DONE:
+ if (pcm_qlen < pcm_nblk)
+ {
+ DMAbuf_outputintr (gus_devnum, pcm_qlen == 0);
+ }
+ break;
+
+ default:;
+ }
+
+ status = gus_look8 (0x49); /*
+ * Get Sampling IRQ Status
+ */
+ if (status & 0x40) /*
+ * Sampling Irq pending
+ */
+ {
+ DMAbuf_inputintr (gus_devnum);
+ }
+
+}
+
+#endif
diff --git a/sys/i386/isa/sound/gustest/Makefile b/sys/i386/isa/sound/gustest/Makefile
new file mode 100644
index 0000000..d161e5b
--- /dev/null
+++ b/sys/i386/isa/sound/gustest/Makefile
@@ -0,0 +1,16 @@
+all: gustest gusload gmod midithru
+
+gustest: gustest.c
+ $(CC) -o gustest gustest.c -lm
+
+gusload: gusload.c
+ $(CC) -o gusload gusload.c
+
+gmod: gmod.c
+ $(CC) -o gmod gmod.c
+
+midithru: midithru.c
+ $(CC) -o midithru midithru.c
+
+clean:
+ rm -f gusload gustest gmod midithru *.o
diff --git a/sys/i386/isa/sound/gustest/Readme b/sys/i386/isa/sound/gustest/Readme
new file mode 100644
index 0000000..7640bf8
--- /dev/null
+++ b/sys/i386/isa/sound/gustest/Readme
@@ -0,0 +1,67 @@
+The programs in this directory are more or less incompletely implemented.
+I have used them for debugging purposes while developing the driver.
+
+Files in this directory:
+
+
+../ultrasound.h (sys/ultrasound.h)
+ This file contains some macros which are similar than
+ the procedures provided by GUSUNIT.PAS. See gustest.c
+ for more information.
+ INSTALL THIS FILE TO YOUR /usr/include/sys !!!!!!!!!!!!
+
+gusload.c This program can be used to load patches (samples) to
+ the DRAM of GUS. It understands the format used in the
+ .pat files shipped with GUS.
+
+ Usage: gusload pgm# patchfile.
+ or gusload reset #Removes all patches from memory.
+
+ You should load just the patches you will need to play
+ a Midi file, since the memory capacity of GUS is rather
+ limited (256k-1M).
+
+ Example:
+
+ gusload 0 acpiano.pat
+ gusload 1 britepno.pat
+ gusload 19 church.pat
+
+ This program is not required if the adagio package is
+ used. It can do the patch uploading itself.
+
+gmod.c This is a simple module player which demonstrates
+ programming with GUS. It doesn't try to interpret
+ most of the effect commands. In fact this program
+ may interpret the modules incorrectly since I am
+ not a module player expert.
+ This version plays .MOD, .STM and .669 modules.
+
+midithru.c This program reads messages from the Midi interface
+ and plays the notes with an internal synthesizer
+ (FM or GUS). The program accepts one argument, the
+ synthesizer device number. In addition to the note on
+ and note off messages it interprets also program changes
+ and channel pressure messages.
+ If you need an example on programming the /dev/sequencer,
+ this is a good one. The voice allocation algorithm is
+ not good so don't look at it.
+
+ NOTE! This program is useful with gmod. Jus load
+ a module with gmod. Wait until the module has
+ finished or hit ^C. Now you can play the samples
+ with the midithru program.
+
+ NOTE2! You need a Midi keyboard to use this program. In
+ addition the Midi interface of GUS is not supported
+ yet which means you need also PAS16 or MPU-401.
+
+pmtest.c
+gpatinfo.c ******* For information only *******
+ These programs demonstrate the patch manager interface
+ which will be included to some later driver version.
+ This interface is not complete in version 1.99.9.
+ Using pmtest will hang your system sooner or later.
+
+Hannu Savolainen
+hsavolai@cs.helsinki.fi
diff --git a/sys/i386/isa/sound/gustest/gmidi.h b/sys/i386/isa/sound/gustest/gmidi.h
new file mode 100644
index 0000000..ab951e7
--- /dev/null
+++ b/sys/i386/isa/sound/gustest/gmidi.h
@@ -0,0 +1,131 @@
+ char patch_names[][9] =
+ {
+ /* 0 */ "acpiano",
+ /* 1 */ "britepno",
+ /* 2 */ "synpiano",
+ /* 3 */ "honktonk",
+ /* 4 */ "epiano1",
+ /* 5 */ "epiano2",
+ /* 6 */ "hrpschrd",
+ /* 7 */ "clavinet",
+ /* 8 */ "celeste",
+ /* 9 */ "glocken",
+ /* 10 */ "musicbox",
+ /* 11 */ "vibes",
+ /* 12 */ "marimba",
+ /* 13 */ "xylophon",
+ /* 14 */ "tubebell",
+ /* 15 */ "santur",
+ /* 16 */ "homeorg",
+ /* 17 */ "percorg",
+ /* 18 */ "rockorg",
+ /* 19 */ "church",
+ /* 20 */ "reedorg",
+ /* 21 */ "accordn",
+ /* 22 */ "harmonca",
+ /* 23 */ "concrtna",
+ /* 24 */ "nyguitar",
+ /* 25 */ "acguitar",
+ /* 26 */ "jazzgtr",
+ /* 27 */ "cleangtr",
+ /* 28 */ "mutegtr",
+ /* 29 */ "odguitar",
+ /* 30 */ "distgtr",
+ /* 31 */ "gtrharm",
+ /* 32 */ "acbass",
+ /* 33 */ "fngrbass",
+ /* 34 */ "pickbass",
+ /* 35 */ "fretless",
+ /* 36 */ "slapbas1",
+ /* 37 */ "slapbas2",
+ /* 38 */ "synbass1",
+ /* 39 */ "synbass2",
+ /* 40 */ "violin",
+ /* 41 */ "viola",
+ /* 42 */ "cello",
+ /* 43 */ "contraba",
+ /* 44 */ "marcato",
+ /* 45 */ "pizzcato",
+ /* 46 */ "harp",
+ /* 47 */ "timpani",
+ /* 48 */ "marcato",
+ /* 49 */ "slowstr",
+ /* 50 */ "synstr1",
+ /* 51 */ "synstr2",
+ /* 52 */ "choir",
+ /* 53 */ "doo",
+ /* 54 */ "voices",
+ /* 55 */ "orchhit",
+ /* 56 */ "trumpet",
+ /* 57 */ "trombone",
+ /* 58 */ "tuba",
+ /* 59 */ "mutetrum",
+ /* 60 */ "frenchrn",
+ /* 61 */ "hitbrass",
+ /* 62 */ "synbras1",
+ /* 63 */ "synbras2",
+ /* 64 */ "sprnosax",
+ /* 65 */ "altosax",
+ /* 66 */ "tenorsax",
+ /* 67 */ "barisax",
+ /* 68 */ "oboe",
+ /* 69 */ "englhorn",
+ /* 70 */ "bassoon",
+ /* 71 */ "clarinet",
+ /* 72 */ "piccolo",
+ /* 73 */ "flute",
+ /* 74 */ "recorder",
+ /* 75 */ "woodflut",
+ /* 76 */ "bottle",
+ /* 77 */ "shakazul",
+ /* 78 */ "whistle",
+ /* 79 */ "ocarina",
+ /* 80 */ "sqrwave",
+ /* 81 */ "sawwave",
+ /* 82 */ "calliope",
+ /* 83 */ "chiflead",
+ /* 84 */ "voxlead",
+ /* 85 */ "voxlead",
+ /* 86 */ "lead5th",
+ /* 87 */ "basslead",
+ /* 88 */ "fantasia",
+ /* 89 */ "warmpad",
+ /* 90 */ "polysyn",
+ /* 91 */ "ghostie",
+ /* 92 */ "bowglass",
+ /* 93 */ "metalpad",
+ /* 94 */ "halopad",
+ /* 95 */ "sweeper",
+ /* 96 */ "aurora",
+ /* 97 */ "soundtrk",
+ /* 98 */ "crystal",
+ /* 99 */ "atmosphr",
+ /* 100 */ "freshair",
+ /* 101 */ "unicorn",
+ /* 102 */ "sweeper",
+ /* 103 */ "startrak",
+ /* 104 */ "sitar",
+ /* 105 */ "banjo",
+ /* 106 */ "shamisen",
+ /* 107 */ "koto",
+ /* 108 */ "kalimba",
+ /* 109 */ "bagpipes",
+ /* 110 */ "fiddle",
+ /* 111 */ "Shannai",
+ /* 112 */ "carillon",
+ /* 113 */ "agogo",
+ /* 114 */ "steeldrm",
+ /* 115 */ "woodblk",
+ /* 116 */ "taiko",
+ /* 117 */ "toms",
+ /* 118 */ "syntom",
+ /* 119 */ "revcym",
+ /* 120 */ "fx-fret",
+ /* 121 */ "fx-blow",
+ /* 122 */ "seashore",
+ /* 123 */ "jungle",
+ /* 124 */ "telephon",
+ /* 125 */ "helicptr",
+ /* 126 */ "applause",
+ /* 127 */ "ringwhsl"
+ };
diff --git a/sys/i386/isa/sound/gustest/gmod.c b/sys/i386/isa/sound/gustest/gmod.c
new file mode 100644
index 0000000..a1184a0
--- /dev/null
+++ b/sys/i386/isa/sound/gustest/gmod.c
@@ -0,0 +1,1588 @@
+/*
+ * gmod.c - Module player for GUS and Linux.
+ * (C) Hannu Savolainen, 1993
+ *
+ * NOTE! This program doesn't try to be a complete module player.
+ * It's just a too I used while developing the driver. In
+ * addition it can be used as an example on programming
+ * the LInux Sound Driver with GUS.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <machine/ultrasound.h>
+#include <fcntl.h>
+#include <math.h>
+#include <string.h>
+
+#define CMD_ARPEG 0x00
+#define CMD_SLIDEUP 0x01
+#define CMD_SLIDEDOWN 0x02
+#define CMD_SLIDETO 0x03
+#define SLIDE_SIZE 8
+#define CMD_VOLSLIDE 0x0a
+#define CMD_JUMP 0x0b
+#define CMD_VOLUME 0x0c
+#define CMD_BREAK 0x0d
+#define CMD_SPEED 0x0f
+#define CMD_NOP 0xfe
+#define CMD_NONOTE 0xff
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+#define MAX_TRACK 8
+#define MAX_PATTERN 128
+#define MAX_POSITION 128
+
+struct note_info
+ {
+ unsigned char note;
+ unsigned char vol;
+ unsigned char sample;
+ unsigned char command;
+ short parm1, parm2;
+ };
+
+struct voice_info
+ {
+ int sample;
+ int note;
+ int volume;
+ int pitchbender;
+
+ /* Pitch sliding */
+
+ int slide_pitch;
+ int slide_goal;
+ int slide_rate;
+
+ int volslide;
+ };
+
+typedef struct note_info pattern[MAX_TRACK][64];
+int pattern_len[MAX_POSITION];
+int pattern_tempo[MAX_POSITION];
+pattern *pattern_table[MAX_PATTERN];
+
+struct voice_info voices[MAX_TRACK];
+
+int nr_channels, nr_patterns, songlength;
+int tune[MAX_POSITION];
+double tick_duration;
+
+int period_table[] =
+{
+ 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
+ 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
+ 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113
+};
+
+SEQ_DEFINEBUF (2048);
+
+int seqfd;
+int sample_ok[128], sample_vol[128];
+int tmp, gus_dev;
+double this_time, next_time;
+int ticks_per_division;
+double clock_rate; /* HZ */
+
+/*
+ * The function seqbuf_dump() must always be provided
+ */
+
+void play_module (char *name);
+int load_module (char *name);
+int play_note (int channel, struct note_info *pat);
+void lets_play_voice (int channel, struct voice_info *v);
+
+void
+seqbuf_dump ()
+{
+ if (_seqbufptr)
+ if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ {
+ perror ("write /dev/sequencer");
+ exit (-1);
+ }
+ _seqbufptr = 0;
+}
+
+void
+init_voices ()
+{
+ int i;
+
+ for (i = 0; i < MAX_TRACK; i++)
+ {
+ voices[i].sample = 0;
+ voices[i].note = 0;
+ voices[i].volume = 64;
+
+ voices[i].slide_pitch = 0;
+ voices[i].slide_goal = 0;
+ voices[i].slide_rate = 0;
+ voices[i].pitchbender = 0;
+
+ voices[i].volslide = 0;
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ int i, n, j;
+ struct synth_info info;
+
+ if ((seqfd = open ("/dev/sequencer", O_WRONLY, 0)) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+ if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ info.device = i;
+
+ if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+ if (info.synth_type == SYNTH_TYPE_SAMPLE
+ && info.synth_subtype == SAMPLE_TYPE_GUS)
+ gus_dev = i;
+ }
+
+ if (gus_dev == -1)
+ {
+ fprintf (stderr, "Gravis Ultrasound not detected\n");
+ exit (-1);
+ }
+
+ GUS_NUMVOICES (gus_dev, 14);
+
+ for (i = 1; i < argc; i++)
+ {
+ for (j = 0; j < MAX_PATTERN; j++)
+ pattern_table[j] = NULL;
+
+ if (load_module (argv[i]))
+ {
+ tick_duration = 100.0 / clock_rate;
+ play_module (argv[i]);
+ }
+
+ }
+
+ SEQ_DUMPBUF ();
+ close (seqfd);
+
+ exit (0);
+}
+
+unsigned short
+intelize (unsigned short v)
+{
+ return ((v & 0xff) << 8) | ((v >> 8) & 0xff);
+}
+
+unsigned long
+intelize4 (unsigned long v)
+{
+ return
+ (((v >> 16) & 0xff) << 8) | (((v >> 16) >> 8) & 0xff) |
+ (((v & 0xff) << 8) | ((v >> 8) & 0xff) << 16);
+}
+
+int
+load_stm_module (int mod_fd, char *name)
+{
+
+ struct sample_header
+ {
+ char name[12];
+ unsigned char instr_disk;
+ unsigned short reserved1;
+ unsigned short length; /* In bytes */
+ unsigned short loop_start;
+ unsigned short loop_end;
+ unsigned char volume;
+ unsigned char reserved2;
+ unsigned short C2_speed;
+ unsigned short reserved3;
+
+ };
+
+ int i, total_mem;
+ int sample_ptr;
+
+ int position;
+
+ unsigned char *tune_ptr; /* array 0-127 */
+
+ char header[1105], sname[21];
+
+ int nr_samples; /* 16 or 32 samples (or 64 or ???) */
+ int slen, npat;
+
+ fprintf (stderr, "Loading .STM module: %s\n", name);
+
+ if (read (mod_fd, header, sizeof (header)) != sizeof (header))
+ {
+ fprintf (stderr, "%s: Short file (header)\n", name);
+ close (mod_fd);
+ return 0;
+ }
+
+ strncpy (sname, header, 20);
+
+ fprintf (stderr, "\nModule: %s - ", sname);
+
+ if (header[28] != 0x1a)
+ {
+ fprintf (stderr, "Not a STM module\n");
+ close (mod_fd);
+ return 0;
+ }
+
+ npat = header[33];
+ slen = 0;
+ tune_ptr = &header[48 + (31 * 32)];
+
+ for (i = 0; i < 64; i++)
+ {
+ tune[i] = tune_ptr[i];
+ if (tune[i] < npat)
+ slen = i;
+ }
+
+ fprintf (stderr, "Song lenght %d, %d patterns.\n", slen, npat);
+
+ nr_samples = 31;
+
+ sample_ptr = 48 + (31 * 32) + 64 + (npat * 1024); /* Location where the
+ * first sample is
+ * stored */
+ total_mem = 0;
+
+ for (i = 0; i < 32; i++)
+ sample_ok[i] = 0;
+
+ for (i = 0; i < nr_samples; i++)
+ {
+ int len, loop_start, loop_end, base_freq;
+ unsigned short loop_flags = 0;
+
+ struct sample_header *sample;
+
+ struct patch_info *patch;
+
+ sample = (struct sample_header *) &header[48 + (i * 32)];
+
+ len = sample->length;
+ loop_start = sample->loop_start;
+ loop_end = sample->loop_end;
+ base_freq = sample->C2_speed;
+
+ if (strlen (sample->name) > 21)
+ {
+ fprintf (stderr, "\nInvalid name for sample #%d\n", i);
+ close (mod_fd);
+ return 0;
+ }
+
+ if (len > 0)
+ {
+ int x;
+
+ if (loop_end > len)
+ loop_end = 1;
+ else if (loop_end < loop_start)
+ {
+ loop_start = 0;
+ loop_end = 0;
+ }
+ else
+ loop_flags = WAVE_LOOPING;
+
+ total_mem += len;
+ patch = (struct patch_info *) malloc (sizeof (*patch) + len);
+
+ patch->key = GUS_PATCH;
+ patch->device_no = gus_dev;
+ patch->instr_no = i;
+ patch->mode = loop_flags;
+ patch->len = len;
+ patch->loop_start = loop_start;
+ patch->loop_end = loop_end;
+ patch->base_freq = base_freq;
+ patch->base_note = 261630; /* Mid C */
+ patch->low_note = 0;
+ patch->high_note = 0x7fffffff;
+ patch->volume = 120;
+
+ if (lseek (mod_fd, sample_ptr, 0) == -1)
+ {
+ perror (name);
+ close (mod_fd);
+ free (patch);
+ return 0;
+ }
+
+ sample_ptr += len;
+
+ if ((x = read (mod_fd, patch->data, len)) != len)
+ {
+ fprintf (stderr, "Short file (sample at %d (%d!=%d)\n", sample_ptr, x, len);
+ close (mod_fd);
+ free (patch);
+ return 0;
+ }
+
+ fprintf (stderr, "Sample %02d: %05d, %05d, %05d, %07d %s\n",
+ i,
+ len,
+ loop_start,
+ loop_end,
+ base_freq,
+ sample->name);
+
+ if (write (seqfd, patch, sizeof (*patch) + len) == -1)
+ {
+ perror ("ioctl /dev/sequencer");
+ exit (-1);
+ }
+ else
+ sample_ok[i] = 1;
+
+ free (patch);
+ }
+ }
+
+ nr_patterns = slen;
+ songlength = slen;
+ nr_channels = 4;
+
+ for (position = 0; position < npat; position++)
+ {
+ unsigned char patterns[64][4][4];
+ int pat, channel, x;
+
+ int pp = 1104 + (position * 1024);
+
+ if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL)
+ {
+ fprintf (stderr, "Can't allocate memory for a pattern\n");
+ return 0;
+ }
+
+ if (lseek (mod_fd, pp, 0) == -1)
+ {
+ perror (name);
+ close (mod_fd);
+ return 0;
+ }
+
+ if ((x = read (mod_fd, patterns, 1024)) != 1024)
+ {
+ fprintf (stderr, "Short file (pattern at %d), %d!=%d\n", pp, x, 1024);
+ close (mod_fd);
+ return 0;
+ }
+
+ for (pat = 0; pat < 64; pat++)
+ {
+
+ for (channel = 0; channel < 4; channel++)
+ {
+ unsigned char *p;
+
+ unsigned vol, note, octave, sample, effect, params;
+
+ p = &patterns[pat][channel][0];
+
+ if (p[0] < 251)
+ {
+ note = p[0] & 15;
+ octave = p[0] / 16;
+
+ note = 48 + octave * 12 + note;
+
+ sample = p[1] / 8;
+ vol = (p[1] & 7) + (p[2] / 2);
+ effect = p[2] & 0xF;
+ params = p[3];
+ }
+ else
+ {
+ note = 0;
+ octave = 0;
+
+ sample = 0;
+ vol = 0;
+ effect = CMD_NONOTE;
+ params = 0;
+ }
+
+ (*pattern_table[position])[channel][pat].note = note;
+ (*pattern_table[position])[channel][pat].sample = sample;
+ (*pattern_table[position])[channel][pat].command = effect;
+ (*pattern_table[position])[channel][pat].parm1 = params;
+ (*pattern_table[position])[channel][pat].parm2 = 0;
+ (*pattern_table[position])[channel][pat].vol = vol;
+ }
+
+ }
+
+ }
+
+ close (mod_fd);
+ return 1;
+}
+
+int
+load_669_module (int mod_fd, char *name)
+{
+ struct sample_header
+ {
+ char name[13];
+ unsigned long length; /* In bytes */
+ unsigned long loop_start;
+ unsigned long loop_end;
+ };
+
+ int i, total_mem;
+ int sample_ptr;
+
+ int position;
+
+ unsigned char *tune_ptr, *len_ptr, *tempo_ptr; /* array 0-127 */
+
+ char header[1084];
+ char msg[110];
+
+ int nr_samples; /* 16 or 32 samples */
+ int slen, npat;
+
+ clock_rate = 25.0;
+
+ fprintf (stderr, "Loading .669 module: %s\n", name);
+
+ if (read (mod_fd, header, sizeof (header)) != sizeof (header))
+ {
+ fprintf (stderr, "%s: Short file (header)\n", name);
+ close (mod_fd);
+ return 0;
+ }
+
+ if (*(unsigned short *) &header[0] != 0x6669)
+ {
+ fprintf (stderr, "Not a 669 file\n");
+ close (mod_fd);
+ return 0;
+ }
+
+ strncpy (msg, &header[2], 108);
+
+ for (i = 0; i < strlen (msg); i++)
+ if ((msg[i] >= ' ' && msg[i] <= 'z') || msg[i] == '\n')
+ printf ("%c", msg[i]);
+ printf ("\n");
+
+ npat = header[0x6f];
+
+ tune_ptr = &header[0x71];
+
+ for (slen = 0; slen < 128 && tune_ptr[slen] != 0xff; slen++);
+ slen--;
+
+ for (i = 0; i < slen; i++)
+ tune[i] = tune_ptr[i];
+
+ len_ptr = &header[0x171];
+ for (i = 0; i < slen; i++)
+ pattern_len[i] = len_ptr[i] - 1;
+
+ tempo_ptr = &header[0xf1];
+ for (i = 0; i < slen; i++)
+ pattern_tempo[i] = tempo_ptr[i];
+
+ nr_samples = header[0x6e];
+
+ fprintf (stderr, "Song lenght %d, %d patterns, %d samples.\n", slen, npat, nr_samples);
+
+ sample_ptr = 0x1f1 + (nr_samples * 0x19) + (npat * 0x600); /* Location where the
+ * first sample is
+ * stored */
+ total_mem = 0;
+
+ for (i = 0; i < 64; i++)
+ sample_ok[i] = 0;
+
+ for (i = 0; i < nr_samples; i++)
+ {
+ int len, loop_start, loop_end;
+ unsigned short loop_flags = 0;
+
+ struct sample_header *sample;
+ char sname[14];
+
+ struct patch_info *patch;
+
+ sample = (struct sample_header *) &header[0x1f1 + (i * 0x19)];
+
+ len = *(unsigned long *) &sample->name[13];
+ loop_start = *(unsigned long *) &sample->name[17];
+ loop_end = *(unsigned long *) &sample->name[21];
+ if (loop_end > len)
+ loop_end = 1;
+ else if (loop_end == len)
+ loop_end--;
+
+ if (loop_end < loop_start)
+ {
+ loop_start = 0;
+ loop_end = 0;
+ }
+
+ strncpy (sname, sample->name, 13);
+
+ if (len > 0 && len < 200000)
+ {
+ total_mem += len;
+
+ fprintf (stderr, "Sample %02d: %05d, %05d, %05d %s\n",
+ i,
+ len,
+ loop_start,
+ loop_end,
+ sname);
+
+ patch = (struct patch_info *) malloc (sizeof (*patch) + len);
+
+ if (loop_end == 0)
+ loop_end = 1;
+ if (loop_end >= len)
+ loop_end = 1;
+
+ if (loop_end > 1) loop_flags = WAVE_LOOPING;
+
+ patch->key = GUS_PATCH;
+ patch->device_no = gus_dev;
+ patch->instr_no = i;
+ patch->mode = WAVE_UNSIGNED | loop_flags;
+ patch->len = len;
+ patch->loop_start = loop_start;
+ patch->loop_end = loop_end;
+ patch->base_freq = 8448;
+ patch->base_note = 261630;
+ patch->low_note = 1000;
+ patch->high_note = 0x7fffffff;
+ patch->volume = 120;
+
+ if (lseek (mod_fd, sample_ptr, 0) == -1)
+ {
+ fprintf (stderr, "Seek failed\n");
+ perror (name);
+ close (mod_fd);
+ free (patch);
+ return 0;
+ }
+
+ sample_ptr += len;
+
+ if (read (mod_fd, patch->data, len) != len)
+ {
+ fprintf (stderr, "Short file (sample at %d)\n", sample_ptr);
+ close (mod_fd);
+ free (patch);
+ return 0;
+ }
+
+ if (write (seqfd, patch, sizeof (*patch) + len) == -1)
+ {
+ perror ("ioctl /dev/sequencer");
+ /* exit (-1); */
+ }
+ else
+ sample_ok[i] = 1;
+
+ free (patch);
+ }
+ }
+
+ nr_patterns = slen;
+ songlength = slen;
+ nr_channels = 8;
+
+ for (position = 0; position < npat; position++)
+ {
+ unsigned char patterns[0x600];
+ int pat, channel, x;
+
+ int pp = 0x1f1 + (nr_samples * 0x19) + (position * 0x600);
+
+ if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL)
+ {
+ fprintf (stderr, "Can't allocate memory for a pattern\n");
+ return 0;
+ }
+
+
+ if (lseek (mod_fd, pp, 0) == -1)
+ {
+ perror (name);
+ close (mod_fd);
+ return 0;
+ }
+
+ if ((x = read (mod_fd, patterns, 1024)) != 1024)
+ {
+ fprintf (stderr, "Short file (pattern at %d) %d!=1024\n", pp, x);
+ close (mod_fd);
+ return 0;
+ }
+
+ for (pat = 0; pat < 64; pat++)
+ {
+
+ for (channel = 0; channel < 8; channel++)
+ {
+ unsigned char *p;
+
+ unsigned vol, period, sample, effect, params;
+
+ p = &patterns[pat * 24 + channel * 3];
+
+ if (p[0] >= 0xfe ||
+ (p[0] == 0xff && p[1] == 0xff && p[2] == 0xff) ||
+ (p[0] == 0 && p[1] == 0 && p[2] == 0) ||
+ *(int *) p == -1)
+ {
+ period = 0;
+ effect = CMD_NONOTE;
+ sample = 0;
+ vol = 0;
+ params = 0;
+
+ if (p[0] == 0)
+ {
+ effect = CMD_BREAK;
+ params = -2;
+ }
+ }
+ else
+ {
+ period = (p[0] >> 2) + 48;
+ effect = (p[2] >> 4);
+ params = p[2] & 0x0f;
+ vol = p[1] & 0x0f;
+
+ if (p[2] == 0xfe)
+ {
+ effect = CMD_VOLUME;
+ params = vol;
+ }
+ else if (p[2] == 0xff)
+ {
+ effect = CMD_NOP;
+ }
+ else
+ switch (effect)
+ {
+ case 0: /* a - Portamento up */
+ effect = CMD_SLIDEUP;
+ break;
+
+ case 1: /* b - Portamento down */
+ effect = CMD_SLIDEDOWN;
+ break;
+
+ case 2: /* c - Port to note */
+ effect = CMD_SLIDETO;
+ break;
+
+ case 3: /* d - Frequency adjust */
+ effect = CMD_NOP; /* To be implemented */
+ break;
+
+ case 4: /* e - Frequency vibrato */
+ effect = CMD_NOP; /* To be implemented */
+ break;
+
+ case 5: /* f - Set tempo */
+ effect = CMD_SPEED;
+ break;
+
+ default:
+ effect = CMD_NOP;
+ }
+
+ sample = (((p[0] << 4) & 0x30) | ((p[1] >> 4) & 0x0f)) + 1;
+ }
+
+ (*pattern_table[position])[channel][pat].note = period;
+ (*pattern_table[position])[channel][pat].sample = sample;
+ (*pattern_table[position])[channel][pat].command = effect;
+ (*pattern_table[position])[channel][pat].parm1 = params;
+ (*pattern_table[position])[channel][pat].parm2 = 0;
+ (*pattern_table[position])[channel][pat].vol = vol;
+ }
+
+ }
+
+ }
+
+ close (mod_fd);
+ return 1;
+}
+
+int
+load_mmd0_module (int mod_fd, char *name)
+{
+
+ struct sample_header
+ {
+ unsigned short loop_start;
+ unsigned short loop_end;
+ unsigned char midich;
+ unsigned char midipreset;
+ unsigned char volume;
+ unsigned char strans;
+ };
+
+ int i, total_mem;
+ int sample_ptr;
+
+ int position;
+
+ unsigned char *tune_ptr; /* array 0-127 */
+
+ char header[1105];
+
+ int nr_samples; /* 16 or 32 samples (or 64 or ???) */
+ int slen, npat;
+
+ fprintf (stderr, "Loading .MED module: %s\n", name);
+
+ if (read (mod_fd, header, sizeof (header)) != sizeof (header))
+ {
+ fprintf (stderr, "%s: Short file (header)\n", name);
+ close (mod_fd);
+ return 0;
+ }
+
+ if (strncmp (header, "MMD0", 4))
+ {
+ fprintf (stderr, "Not a MED module\n");
+ close (mod_fd);
+ return 0;
+ }
+
+ printf ("Module len %d\n", intelize4 (*(long *) &header[4]));
+ printf ("Song info %d\n", intelize4 (*(long *) &header[8]));
+ printf ("Song len %d\n", intelize4 (*(long *) &header[12]));
+ printf ("Blockarr %x\n", intelize4 (*(long *) &header[16]));
+ printf ("Blockarr len %d\n", intelize4 (*(long *) &header[20]));
+ printf ("Sample array %x\n", intelize4 (*(long *) &header[24]));
+ printf ("Sample array len %d\n", intelize4 (*(long *) &header[28]));
+ printf ("Exp data %x\n", intelize4 (*(long *) &header[32]));
+ printf ("Exp size %d\n", intelize4 (*(long *) &header[36]));
+ printf ("Pstate %d\n", intelize (*(long *) &header[40]));
+ printf ("Pblock %d\n", intelize (*(long *) &header[42]));
+
+ return 0;
+
+ npat = header[33];
+ slen = 0;
+ tune_ptr = &header[48 + (31 * 32)];
+
+ for (i = 0; i < 64; i++)
+ {
+ tune[i] = tune_ptr[i];
+ if (tune[i] < npat)
+ slen = i;
+ }
+
+ fprintf (stderr, "Song lenght %d, %d patterns.\n", slen, npat);
+
+ nr_samples = 31;
+
+ sample_ptr = 48 + (31 * 32) + 64 + (npat * 1024); /* Location where the
+ * first sample is
+ * stored */
+ total_mem = 0;
+
+ for (i = 0; i < 32; i++)
+ sample_ok[i] = 0;
+
+ for (i = 0; i < nr_samples; i++)
+ {
+ int len, loop_start, loop_end, base_freq;
+ unsigned short loop_flags = 0;
+
+ struct sample_header *sample;
+
+ struct patch_info *patch;
+
+ sample = (struct sample_header *) &header[48 + (i * 32)];
+
+ /*
+ * len = sample->length; loop_start = sample->loop_start; loop_end =
+ * sample->loop_end; base_freq = sample->C2_speed;
+ *
+ * if (strlen (sample->name) > 21) { fprintf (stderr, "\nInvalid name for
+ * sample #%d\n", i); close (mod_fd); return 0; }
+ */
+ if (len > 0)
+ {
+ int x;
+
+ if (loop_end > len)
+ loop_end = 1;
+
+ if (loop_end < loop_start)
+ {
+ loop_start = 0;
+ loop_end = 0;
+ }
+
+ if (loop_end > 2) loop_flags = WAVE_LOOPING;
+
+ total_mem += len;
+ patch = (struct patch_info *) malloc (sizeof (*patch) + len);
+
+ patch->key = GUS_PATCH;
+ patch->device_no = gus_dev;
+ patch->instr_no = i;
+ patch->mode = loop_flags;
+ patch->len = len;
+ patch->loop_start = loop_start;
+ patch->loop_end = loop_end;
+ patch->base_freq = base_freq;
+ patch->base_note = 261630; /* Mid C */
+ patch->low_note = 0;
+ patch->high_note = 0x7fffffff;
+ patch->volume = 120;
+
+ if (lseek (mod_fd, sample_ptr, 0) == -1)
+ {
+ perror (name);
+ close (mod_fd);
+ free (patch);
+ return 0;
+ }
+
+ sample_ptr += len;
+
+ if ((x = read (mod_fd, patch->data, len)) != len)
+ {
+ fprintf (stderr, "Short file (sample at %d (%d!=%d)\n", sample_ptr, x, len);
+ close (mod_fd);
+ free (patch);
+ return 0;
+ }
+ /*
+ * fprintf (stderr, "Sample %02d: %05d, %05d, %05d, %07d %s\n", i,
+ * len, loop_start, loop_end, base_freq, sample->name);
+ */
+ if (write (seqfd, patch, sizeof (*patch) + len) == -1)
+ {
+ perror ("ioctl /dev/sequencer");
+ exit (-1);
+ }
+ else
+ sample_ok[i] = 1;
+
+ free (patch);
+ }
+ }
+
+ nr_patterns = slen;
+ songlength = slen;
+ nr_channels = 4;
+
+ for (position = 0; position < npat; position++)
+ {
+ unsigned char patterns[64][4][4];
+ int pat, channel, x;
+
+ int pp = 1104 + (position * 1024);
+
+ if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL)
+ {
+ fprintf (stderr, "Can't allocate memory for a pattern\n");
+ return 0;
+ }
+
+ if (lseek (mod_fd, pp, 0) == -1)
+ {
+ perror (name);
+ close (mod_fd);
+ return 0;
+ }
+
+ if ((x = read (mod_fd, patterns, 1024)) != 1024)
+ {
+ fprintf (stderr, "Short file (pattern at %d), %d!=%d\n", pp, x, 1024);
+ close (mod_fd);
+ return 0;
+ }
+
+ for (pat = 0; pat < 64; pat++)
+ {
+
+ for (channel = 0; channel < 4; channel++)
+ {
+ unsigned char *p;
+
+ unsigned vol, note, octave, sample, effect, params;
+
+ p = &patterns[pat][channel][0];
+
+ if (p[0] < 251)
+ {
+ note = p[0] & 15;
+ octave = p[0] / 16;
+
+ note = 48 + octave * 12 + note;
+
+ sample = p[1] / 8;
+ vol = (p[1] & 7) + (p[2] / 2);
+ effect = p[2] & 0xF;
+ params = p[3];
+ }
+ else
+ {
+ note = 0;
+ octave = 0;
+
+ sample = 0;
+ vol = 0;
+ effect = CMD_NONOTE;
+ params = 0;
+ }
+
+ (*pattern_table[position])[channel][pat].note = note;
+ (*pattern_table[position])[channel][pat].sample = sample;
+ (*pattern_table[position])[channel][pat].command = effect;
+ (*pattern_table[position])[channel][pat].parm1 = params;
+ (*pattern_table[position])[channel][pat].parm2 = 0;
+ (*pattern_table[position])[channel][pat].vol = vol;
+ }
+
+ }
+
+ }
+
+ close (mod_fd);
+ return 1;
+}
+
+int
+load_module (char *name)
+{
+
+ struct sample_header
+ {
+ char name[22];
+ unsigned short length; /* In words */
+
+ unsigned char finetune;
+ unsigned char volume;
+
+ unsigned short repeat_point; /* In words */
+ unsigned short repeat_length; /* In words */
+ };
+
+ int i, mod_fd, total_mem;
+ int sample_ptr, pattern_loc;
+
+ int position;
+
+ unsigned char *tune_ptr; /* array 0-127 */
+
+ char header[1084];
+
+ int nr_samples; /* 16 or 32 samples */
+ int slen, npat;
+ char mname[23];
+
+ ioctl (seqfd, SNDCTL_SEQ_SYNC, 0);
+ ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev);
+
+ clock_rate = 50.0;
+
+ for (i = 0; i < MAX_POSITION; i++)
+ pattern_len[i] = 64;
+
+ for (i = 0; i < MAX_POSITION; i++)
+ pattern_tempo[i] = 0;
+
+ if ((mod_fd = open (name, O_RDONLY, 0)) == -1)
+ {
+ perror (name);
+ return 0;
+ }
+
+ if (read (mod_fd, header, sizeof (header)) != sizeof (header))
+ {
+ fprintf (stderr, "%s: Short file (header)\n", name);
+ close (mod_fd);
+ return 0;
+ }
+
+ if (lseek (mod_fd, 0, 0) == -1)
+ {
+ perror (name);
+ close (mod_fd);
+ return 0;
+ }
+
+ if (header[28] == 0x1a)
+ return load_stm_module (mod_fd, name);
+
+ if (*(unsigned short *) &header[0] == 0x6669)
+ return load_669_module (mod_fd, name);
+
+ if (!strncmp (header, "MMD0", 4))
+ return load_mmd0_module (mod_fd, name);
+
+ fprintf (stderr, "Loading .MOD module: %s\n", name);
+
+ strncpy (mname, header, 22);
+ fprintf (stderr, "\nModule: %s - ", mname);
+
+ if (!strncmp (&header[1080], "M.K.", 4) || !strncmp (&header[1080], "FLT8", 4))
+ {
+ fprintf (stderr, "31 samples\n");
+ nr_samples = 31;
+ }
+ else
+ {
+ fprintf (stderr, "15 samples\n");
+ nr_samples = 15;
+ }
+
+ if (nr_samples == 31)
+ {
+ sample_ptr = pattern_loc = 1084;
+ slen = header[950];
+ tune_ptr = (unsigned char *) &header[952];
+ }
+ else
+ {
+ sample_ptr = pattern_loc = 600;
+ slen = header[470];
+ tune_ptr = (unsigned char *) &header[472];
+ }
+
+ npat = 0;
+ for (i = 0; i < 128; i++)
+ {
+ tune[i] = tune_ptr[i];
+
+ if (tune_ptr[i] > npat)
+ npat = tune_ptr[i];
+ }
+ npat++;
+
+ fprintf (stderr, "Song lenght %d, %d patterns.\n", slen, npat);
+
+ sample_ptr += (npat * 1024); /* Location where the first sample is stored */
+ total_mem = 0;
+
+ for (i = 0; i < 32; i++)
+ sample_ok[i] = 0;
+
+ for (i = 0; i < nr_samples; i++)
+ {
+ int len, loop_start, loop_end;
+ unsigned short loop_flags = 0;
+ char pname[22];
+
+ struct sample_header *sample;
+
+ struct patch_info *patch;
+
+ sample = (struct sample_header *) &header[20 + (i * 30)];
+
+ len = intelize (sample->length) * 2;
+ loop_start = intelize (sample->repeat_point) * 2;
+ loop_end = loop_start + (intelize (sample->repeat_length) * 2);
+
+ if (loop_start > len)
+ loop_start = 0;
+ if (loop_end > len)
+ loop_end = len;
+
+ if (loop_end <= loop_start)
+ loop_end = loop_start + 1;
+
+ if (loop_end > 2 && loop_end > loop_start)
+ loop_flags = WAVE_LOOPING;
+
+ strncpy (pname, sample->name, 20);
+
+ if (len > 0)
+ {
+ fprintf (stderr, "Sample %02d: L%05d, S%05d, E%05d V%02d %s\n",
+ i,
+ len,
+ loop_start,
+ loop_end,
+ sample->volume,
+ pname);
+
+ total_mem += len;
+
+ patch = (struct patch_info *) malloc (sizeof (*patch) + len);
+
+ patch->key = GUS_PATCH;
+ patch->device_no = gus_dev;
+ patch->instr_no = i;
+ patch->mode = loop_flags;
+ patch->len = len;
+ patch->loop_start = loop_start;
+ patch->loop_end = loop_end;
+ patch->base_note = 261630; /* Middle C */
+ patch->base_freq = 8448;
+ patch->low_note = 0;
+ patch->high_note = 20000000;
+ patch->volume = 120;
+ patch->panning = 0;
+
+ if (lseek (mod_fd, sample_ptr, 0) == -1)
+ {
+ perror (name);
+ close (mod_fd);
+ free (patch);
+ return 0;
+ }
+
+ sample_ptr += len;
+
+ if (read (mod_fd, patch->data, len) != len)
+ {
+ fprintf (stderr, "Short file (sample) %d\n", sample_ptr);
+ close (mod_fd);
+ free (patch);
+ return 0;
+ }
+
+ SEQ_WRPATCH (patch, sizeof (*patch) + len);
+
+ sample_ok[i] = 1;
+ if (sample->volume == 0) sample->volume = 64;
+ sample_vol[i] = sample->volume;
+
+ free (patch);
+ }
+ }
+
+ nr_patterns = npat;
+ songlength = slen;
+ nr_channels = 4;
+
+ for (position = 0; position < npat; position++)
+ {
+ unsigned char patterns[64][4][4];
+ int pat, channel;
+
+ int pp = pattern_loc + (position * 1024);
+
+ if (lseek (mod_fd, pp, 0) == -1)
+ {
+ perror (name);
+ close (mod_fd);
+ return 0;
+ }
+
+ if (read (mod_fd, patterns, 1024) != 1024)
+ {
+ fprintf (stderr, "Short file (pattern %d) %d\n", tune[position], pp);
+ close (mod_fd);
+ return 0;
+ }
+
+ if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL)
+ {
+ fprintf (stderr, "Can't allocate memory for a pattern\n");
+ return 0;
+ }
+
+ for (pat = 0; pat < 64; pat++)
+ {
+ for (channel = 0; channel < 4; channel++)
+ {
+ unsigned short tmp;
+ unsigned char *p;
+
+ unsigned period, sample, effect, params, note, vol;
+
+ p = &patterns[pat][channel][0];
+
+ tmp = (p[0] << 8) | p[1];
+ sample = (tmp >> 8) & 0x10;
+ period =
+ MIN (tmp & 0xFFF, 1023);
+ tmp = (p[2] << 8) | p[3];
+ sample |= tmp >> 12;
+ effect = (tmp >> 8) & 0xF;
+ params = tmp & 0xFF;
+
+ note = 0;
+
+ if (period)
+ {
+ /*
+ * Convert period to a Midi note number
+ */
+
+ for (note = 0; note < 37 && period != period_table[note]; note++);
+ if (note >= 37)
+ note = 0;
+
+ note += 48;
+ }
+
+ vol = 64;
+
+ if (sample)
+ if (effect == 0xc)
+ {
+ vol = params;
+ }
+ else
+ vol = sample_vol[sample - 1];
+
+ vol *= 2;
+ if (vol>64)vol--;
+
+ (*pattern_table[position])[channel][pat].note = note;
+ (*pattern_table[position])[channel][pat].sample = sample;
+ (*pattern_table[position])[channel][pat].command = effect;
+ (*pattern_table[position])[channel][pat].parm1 = params;
+ (*pattern_table[position])[channel][pat].parm2 = 0;
+ (*pattern_table[position])[channel][pat].vol = vol;
+ }
+ }
+ }
+
+ close (mod_fd);
+ return 1;
+}
+
+int
+panning (int ch)
+{
+ static int panning_tab[] =
+ {-110, 110, 110, -110};
+
+ return panning_tab[ch % 4];
+}
+
+void
+set_speed (int parm)
+{
+ if (!parm)
+ parm = 1;
+
+ if (parm < 32)
+ {
+ ticks_per_division = parm;
+ }
+ else
+ {
+ tick_duration = (60.0 / parm) * 10.0;
+ }
+
+}
+
+void
+play_module (char *name)
+{
+ int i, position, jump_to_pos;
+
+ init_voices ();
+
+ SEQ_START_TIMER ();
+#if 1
+ for (i=0;i<32;i++)
+ {
+ SEQ_EXPRESSION(gus_dev, i, 127);
+ SEQ_MAIN_VOLUME(gus_dev, i, 100);
+ }
+#endif
+ next_time = 0.0;
+
+ set_speed (6);
+
+ for (position = 0; position < songlength; position++)
+ {
+ int tick, pattern, channel, pos, go_to;
+
+ pos = tune[position];
+ if (pattern_tempo[position])
+ set_speed (pattern_tempo[position]);
+
+ jump_to_pos = -1;
+ for (pattern = 0; pattern < pattern_len[position] && jump_to_pos == -1; pattern++)
+ {
+ this_time = 0.0;
+
+ for (channel = 0; channel < nr_channels; channel++)
+ {
+ if ((go_to = play_note (channel, &(*pattern_table[pos])[channel][pattern])) != -1)
+ jump_to_pos = go_to;
+
+ }
+
+ next_time += tick_duration;
+
+ for (tick = 1; tick < ticks_per_division; tick++)
+ {
+ for (channel = 0; channel < nr_channels; channel++)
+ lets_play_voice (channel, &voices[channel]);
+ next_time += tick_duration;
+ }
+
+ }
+
+ if (jump_to_pos >= 0)
+ position = jump_to_pos;
+ }
+
+ SEQ_WAIT_TIME ((int) next_time + 200); /* Wait extra 2 secs */
+
+ for (i = 0; i < nr_channels; i++)
+ SEQ_STOP_NOTE (gus_dev, i, 0, 127);
+ SEQ_DUMPBUF ();
+
+ for (i = 0; i < nr_patterns; i++)
+ free (pattern_table[i]);
+}
+
+void
+sync_time ()
+{
+ if (next_time > this_time)
+ {
+ SEQ_WAIT_TIME ((long) next_time);
+ this_time = next_time;
+ }
+}
+
+void
+set_volslide (int channel, struct note_info *pat)
+{
+ int n;
+
+ voices[channel].volslide = 0;
+
+ if ((n = (pat->parm1 & 0xf0) >> 4))
+ voices[channel].volslide = n;
+ else
+ voices[channel].volslide = pat->parm1 & 0xf;
+}
+
+void
+set_slideto (int channel, struct note_info *pat)
+{
+ int size, rate, dir, range = 200;
+
+ rate = pat->parm1;
+ size = voices[channel].note - pat->note;
+ if (!size)
+ return;
+
+ if (size < 0)
+ {
+ size *= -1;
+ dir = -1;
+ }
+ else
+ dir = 1;
+
+ if (size > 2)
+ {
+ range = size * 100;
+ rate = rate * size / 200;
+ }
+
+ rate = pat->parm1 * dir / 30;
+ if (!rate)
+ rate = 1;
+
+ voices[channel].slide_pitch = 1;
+ voices[channel].slide_goal = (dir * 8192 * 200 * 2 / size) / range;
+ voices[channel].pitchbender = 0;
+ voices[channel].slide_rate = rate;
+ SEQ_BENDER_RANGE (gus_dev, channel, range);
+}
+
+int
+play_note (int channel, struct note_info *pat)
+{
+ int jump = -1;
+ int sample;
+
+ if (pat->sample == 0x3f)
+ pat->sample = 0;
+
+ if (pat->command == CMD_NONOTE)
+ return -1; /* Undefined */
+
+ sample = pat->sample;
+
+ if (sample && !pat->note)
+ {
+ pat->note = voices[channel].note;
+ }
+
+ if (sample)
+ voices[channel].sample = sample;
+ else
+ sample = voices[channel].sample;
+
+ sample--;
+
+ if (pat->note && pat->command != 3) /* Have a note -> play */
+ {
+ if (sample < 0)
+ sample = voices[channel].sample - 1;
+
+ if (!sample_ok[sample])
+ sample = voices[channel].sample - 1;
+
+ if (sample < 0)
+ sample = 0;
+
+ if (sample_ok[sample])
+ {
+ sync_time ();
+
+ if (pat->vol > 127) pat->vol=127;
+ SEQ_SET_PATCH (gus_dev, channel, sample);
+ SEQ_PANNING (gus_dev, channel, panning (channel));
+ SEQ_PITCHBEND (gus_dev, channel, 0);
+ SEQ_START_NOTE (gus_dev, channel, pat->note, pat->vol);
+
+ voices[channel].volume = pat->vol;
+ voices[channel].note = pat->note;
+ voices[channel].slide_pitch = 0;
+ }
+ else
+ SEQ_STOP_NOTE (gus_dev, channel, pat->note, pat->vol);
+ }
+
+ switch (pat->command)
+ {
+
+ case CMD_NOP:;
+ break;
+
+ case CMD_JUMP:
+ jump = pat->parm1;
+ break;
+
+ case CMD_BREAK:
+ jump = -2;
+ break;
+
+ case CMD_SPEED:
+ set_speed (pat->parm1);
+ break;
+
+ case CMD_SLIDEUP:
+ voices[channel].slide_pitch = 1;
+ voices[channel].slide_goal = 8191;
+ voices[channel].pitchbender = 0;
+ voices[channel].slide_rate = pat->parm1 * SLIDE_SIZE;
+ SEQ_BENDER_RANGE (gus_dev, channel, 200);
+ break;
+
+ case CMD_SLIDEDOWN:
+ voices[channel].slide_pitch = 1;
+ voices[channel].slide_goal = -8192;
+ voices[channel].pitchbender = 0;
+ voices[channel].slide_rate = -pat->parm1 * SLIDE_SIZE;
+ SEQ_BENDER_RANGE (gus_dev, channel, 200);
+ break;
+
+ case CMD_SLIDETO:
+ set_slideto (channel, pat);
+ break;
+
+ case CMD_VOLUME:
+ {
+ int vol = pat->parm1*2;
+ if (vol>127) vol=127;
+ if (pat->note && pat->command != 3)
+ break;
+ SEQ_START_NOTE (gus_dev, channel, 255, vol);
+ }
+ break;
+
+ case CMD_ARPEG:
+ break;
+
+ case 0x0e:
+ /* printf ("Cmd 0xE%02x\n", pat->parm1); */
+ break;
+
+ case CMD_VOLSLIDE:
+ set_slideto (channel, pat);
+ break;
+
+ default:
+ /* printf ("Command %x %02x\n", pat->command, pat->parm1); */
+ }
+
+ return jump;
+}
+
+void
+lets_play_voice (int channel, struct voice_info *v)
+{
+ if (v->slide_pitch)
+ {
+ v->pitchbender += v->slide_rate;
+ if (v->slide_goal < 0)
+ {
+ if (v->pitchbender <= v->slide_goal)
+ {
+ v->pitchbender = v->slide_goal;
+ v->slide_pitch = 0; /* Stop */
+ }
+ }
+ else
+ {
+ if (v->pitchbender >= v->slide_goal)
+ {
+ v->pitchbender = v->slide_goal;
+ v->slide_pitch = 0; /* Stop */
+ }
+ }
+
+ sync_time ();
+ SEQ_PITCHBEND (gus_dev, channel, v->pitchbender);
+ }
+
+ if (v->volslide)
+ {
+ v->volume += v->volslide;
+ sync_time ();
+
+ if (v->volume > 127) v->volume = 127;
+ SEQ_START_NOTE (gus_dev, channel, 255, v->volume);
+ }
+}
diff --git a/sys/i386/isa/sound/gustest/gpatinfo.c b/sys/i386/isa/sound/gustest/gpatinfo.c
new file mode 100644
index 0000000..121020d
--- /dev/null
+++ b/sys/i386/isa/sound/gustest/gpatinfo.c
@@ -0,0 +1,175 @@
+/*
+ * gpatinfo.c: This program demonstrates the patch management
+ * interface of the GUS driver.
+ *
+ * NOTE! The patch manager interface is highly device dependent,
+ * currently incompletely implemented prototype and
+ * will change before final implementation.
+ *
+ */
+
+#include <stdio.h>
+#include <machine/ultrasound.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "gmidi.h"
+
+#define GUS_DEV gus_dev
+
+#define patch_access(cmd, rec) \
+ rec.command = cmd;\
+ rec.device = gus_dev;\
+ if (ioctl(seqfd, SNDCTL_PMGR_IFACE, &rec)==-1)\
+ {\
+ perror("/dev/sequencer(SNDCTL_PMGR_IFACE/" #cmd ")");\
+ exit(-1);\
+ }
+
+SEQ_DEFINEBUF (2048);
+
+int seqfd;
+
+int gus_dev = -1;
+
+/*
+ * The function seqbuf_dump() must always be provided
+ */
+
+void
+seqbuf_dump ()
+{
+ if (_seqbufptr)
+ if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ {
+ perror ("write /dev/sequencer");
+ exit (-1);
+ }
+ _seqbufptr = 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int i, j, n;
+ struct synth_info info;
+ struct patch_info *patch;
+ struct patmgr_info mgr, mgr2, mgr3;
+
+ if ((seqfd = open ("/dev/sequencer", O_WRONLY, 0)) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+ if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+/*
+ * First locate the GUS device
+ */
+
+ for (i = 0; i < n; i++)
+ {
+ info.device = i;
+
+ if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+ if (info.synth_type == SYNTH_TYPE_SAMPLE
+ && info.synth_subtype == SAMPLE_TYPE_GUS)
+ gus_dev = i;
+ }
+
+ if (gus_dev == -1)
+ {
+ fprintf (stderr, "Error: Gravis Ultrasound not detected\n");
+ exit (-1);
+ }
+
+ printf("Gravis UltraSound device = %d\n", gus_dev);
+
+ /*
+ * Get type of the Patch Manager interface of the GUS device
+ */
+
+ patch_access(PM_GET_DEVTYPE, mgr);
+ printf("Patch manager type: %d\n", mgr.parm1);
+
+ if (mgr.parm1 != PMTYPE_WAVE)
+ {
+ fprintf(stderr, "Hups, this program seems to be obsolete\n");
+ exit(-1);
+ }
+
+ /*
+ * The GUS driver supports up to 256 different midi program numbers but
+ * this limit can be changed before compiling the driver. The following
+ * call returns the value compiled to the driver.
+ */
+
+ patch_access(PM_GET_PGMMAP, mgr);
+ printf("Device supports %d midi programs.\n", mgr.parm1);
+
+ /*
+ * Each program can be undefined or it may have one or more patches.
+ * A patch consists of header and the waveform data. If there is more
+ * than one patch in a program, the right one is selected by checking the
+ * note number when the program is played.
+ *
+ * The following call reads an array indexed by program number. Each
+ * element defines the number of patches defined for the corresponding
+ * program.
+ */
+ printf("Loaded programs:\n");
+
+ for (i=0;i<mgr.parm1;i++)
+ if (mgr.data.data8[i])
+ {
+ printf("%03d: %2d patches\n", i, mgr.data.data8[i]);
+
+ /*
+ * Next get the magic keys of the patches associated with this program.
+ * This key can be used to access the patc data.
+ */
+ mgr2.parm1=i;
+ patch_access(PM_GET_PGM_PATCHES, mgr2);
+ for (j = 0;j<mgr2.parm1;j++)
+ {
+ printf("\tPatch %d: %3d ", j, mgr2.data.data32[j]);
+
+ /*
+ * The last step is to read the patch header (without wave data).
+ * The header is returned in the mgr3.data. The field parm1 returns
+ * address of the wave data in tge GUS DRAM. Parm2 returns
+ * size of the struct patch_info in the kernel.
+ *
+ * There is also the PM_SET_PATCH call which allows modification of the
+ * header data. The only limitation is that the sample len cannot be
+ * increased.
+ */
+ mgr3.parm1 = mgr2.data.data32[j];
+ patch_access(PM_GET_PATCH, mgr3);
+ patch = (struct patch_info *)&mgr3.data; /* Pointer to the patch hdr */
+
+ printf("DRAM ptr = %7d, sample len =%6d bytes.\n",
+ mgr3.parm1, patch->len);
+
+ }
+ }
+
+ i = gus_dev;
+
+ if (ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &i)==-1) exit(-1);
+ printf("%d bytes of DRAM available for wave data\n", i);
+
+
+ exit(0);
+}
diff --git a/sys/i386/isa/sound/gustest/gusload.c b/sys/i386/isa/sound/gustest/gusload.c
new file mode 100644
index 0000000..1e04a7d
--- /dev/null
+++ b/sys/i386/isa/sound/gustest/gusload.c
@@ -0,0 +1,349 @@
+/*
+ * patutil.c - A sample program which loads patches to the Gravis
+ * Ultrasound
+ *
+ */
+
+#ifndef PATCH_PATH
+#define PATCH_PATH "/D/ultrasnd/midi"
+#endif
+
+#include <stdio.h>
+#include <machine/ultrasound.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "gmidi.h"
+
+struct pat_header
+ {
+ char magic[12];
+ char version[10];
+ char description[60];
+ unsigned char instruments;
+ char voices;
+ char channels;
+ unsigned short nr_waveforms;
+ unsigned short master_volume;
+ unsigned long data_size;
+ };
+
+struct sample_header
+ {
+ char name[7];
+ unsigned char fractions;
+ long len;
+ long loop_start;
+ long loop_end;
+ unsigned short base_freq;
+ long low_note;
+ long high_note;
+ long base_note;
+ short detune;
+ unsigned char panning;
+
+ unsigned char envelope_rate[6];
+ unsigned char envelope_offset[6];
+
+ unsigned char tremolo_sweep;
+ unsigned char tremolo_rate;
+ unsigned char tremolo_depth;
+
+ unsigned char vibrato_sweep;
+ unsigned char vibrato_rate;
+ unsigned char vibrato_depth;
+
+ char modes;
+
+ short scale_frequency;
+ unsigned short scale_factor;
+ };
+
+#define GUS_DEV gus_dev
+
+SEQ_DEFINEBUF (2048);
+
+int seqfd;
+
+int gus_dev = -1;
+
+struct patch_info *patch;
+
+/*
+ * The function seqbuf_dump() must always be provided
+ */
+
+void
+seqbuf_dump ()
+{
+ if (_seqbufptr)
+ if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ {
+ perror ("write /dev/sequencer");
+ exit (-1);
+ }
+ _seqbufptr = 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int i, n, patfd, pgm, print_only = 0;
+ struct synth_info info;
+ struct pat_header header;
+ struct sample_header sample;
+ char buf[256];
+ char name[256];
+ long offset;
+
+ if ((seqfd = open ("/dev/sequencer", O_WRONLY, 0)) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+ if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+
+ for (i = 0; i < n; i++)
+ {
+ info.device = i;
+
+ if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+ if (info.synth_type == SYNTH_TYPE_SAMPLE
+ && info.synth_subtype == SAMPLE_TYPE_GUS)
+ gus_dev = i;
+ }
+
+ if (gus_dev == -1)
+ {
+ fprintf (stderr, "Error: Gravis Ultrasound not detected\n");
+ exit (-1);
+ }
+
+ if (argc == 2)
+ {
+ if (!strcmp (argv[1], "reset"))
+ if (ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1)
+ perror ("Sample reset");
+ exit (0);
+ }
+
+ if (argc != 3)
+ {
+ fprintf (stderr, "Usage: %s pgm# patchfile\n", argv[0]);
+ fprintf (stderr, " or : %s pgm# GM\n", argv[0]);
+ fprintf (stderr, " or : %s pgm# -l\n", argv[0]);
+ fprintf (stderr, " or : %s reset\n", argv[0]);
+ fprintf (stderr, " or : %s -l patchfile\n", argv[0]);
+ exit (-1);
+ }
+
+ pgm = atoi (argv[1]);
+ strcpy (name, argv[2]);
+
+ if (strcmp (name, "GM") == 0 || strcmp(name, "-l")==0)
+ {
+ if (strcmp (name, "-l") == 0) print_only = 1;
+ if (pgm < 0 || pgm > 127)
+ {
+ fprintf (stderr, "pgm# must be between 0 and 127\n");
+ exit (-1);
+ }
+
+ sprintf (name, PATCH_PATH "/%s.pat", patch_names[pgm]);
+
+ if (!print_only)
+ fprintf (stderr, "Loading program %d from %s\n", pgm, name);
+ }
+ else if (strcmp (argv[1], "-l") == 0)
+ print_only = 1;
+
+ if ((patfd = open (name, O_RDONLY, 0)) == -1)
+ {
+ perror (name);
+ exit (-1);
+ }
+
+ if (read (patfd, buf, 0xef) != 0xef)
+ {
+ fprintf (stderr, "%s: Short file\n", name);
+ exit (-1);
+ }
+
+ memcpy ((char *) &header, buf, sizeof (header));
+
+ if (strncmp (header.magic, "GF1PATCH110", 12))
+ {
+ fprintf (stderr, "%s: Not a patch file\n", name);
+ exit (-1);
+ }
+
+ if (strncmp (header.version, "ID#000002", 10))
+ {
+ fprintf (stderr, "%s: Incompatible patch file version\n", name);
+ exit (-1);
+ }
+
+ header.nr_waveforms = *(unsigned short *) &buf[85];
+ header.master_volume = *(unsigned short *) &buf[87];
+
+ if (print_only)
+ {
+ printf ("Patch file: %s contains %d samples\n\n", name, header.nr_waveforms);
+ printf ("Master volume: %d\n", header.master_volume);
+ }
+
+ offset = 0xef;
+
+ for (i = 0; i < header.nr_waveforms; i++)
+ {
+ if (lseek (patfd, offset, 0) == -1)
+ {
+ perror (name);
+ exit (-1);
+ }
+
+ if (read (patfd, &buf, sizeof (sample)) != sizeof (sample))
+ {
+ fprintf (stderr, "%s: Short file\n", name);
+ exit (-1);
+ }
+
+ memcpy ((char *) &sample, buf, sizeof (sample));
+
+ /*
+ * Since some fields of the patch record are not 32bit aligned, we must
+ * handle them specially.
+ */
+ sample.low_note = *(long *) &buf[22];
+ sample.high_note = *(long *) &buf[26];
+ sample.base_note = *(long *) &buf[30];
+ sample.detune = *(short *) &buf[34];
+ sample.panning = (unsigned char) buf[36];
+
+ memcpy (sample.envelope_rate, &buf[37], 6);
+ memcpy (sample.envelope_offset, &buf[43], 6);
+
+ sample.tremolo_sweep = (unsigned char) buf[49];
+ sample.tremolo_rate = (unsigned char) buf[50];
+ sample.tremolo_depth = (unsigned char) buf[51];
+
+ sample.vibrato_sweep = (unsigned char) buf[52];
+ sample.vibrato_rate = (unsigned char) buf[53];
+ sample.vibrato_depth = (unsigned char) buf[54];
+ sample.modes = (unsigned char) buf[55];
+ sample.scale_frequency = *(short *) &buf[56];
+ sample.scale_factor = *(unsigned short *) &buf[58];
+
+ if (print_only)
+ {
+ printf("\nSample: %03d / %s\n", i, sample.name);
+ printf ("Len: %d, Loop start: %d, Loop end: %d\n", sample.len, sample.loop_start, sample.loop_end);
+ printf ("Flags: ");
+ if (sample.modes & WAVE_16_BITS)
+ printf ("16 bit ");
+ if (sample.modes & WAVE_UNSIGNED)
+ printf ("unsigned ");
+ if (sample.modes & WAVE_LOOP_BACK)
+ printf("reverse ");
+ if (sample.modes & WAVE_BIDIR_LOOP)
+ printf("bidir ");
+ if (sample.modes & WAVE_LOOPING)
+ printf ("looping "); else printf("one_shot" );
+ if (sample.modes & WAVE_SUSTAIN_ON)
+ printf ("sustain ");
+ if (sample.modes & WAVE_ENVELOPES)
+ printf ("enveloped ");
+ printf ("\n");
+
+ if (sample.modes & WAVE_ENVELOPES)
+ {
+ int i;
+
+ printf ("Envelope info: ");
+ for (i = 0; i < 6; i++)
+ {
+ printf ("%d/%d ", sample.envelope_rate[i],
+ sample.envelope_offset[i]);
+ }
+ printf ("\n");
+ }
+
+ printf("Tremolo: sweep=%d, rate=%d, depth=%d\n",
+ sample.tremolo_sweep,
+ sample.tremolo_rate,
+ sample.tremolo_depth);
+
+ printf("Vibrato: sweep=%d, rate=%d, depth=%d\n",
+ sample.vibrato_sweep,
+ sample.vibrato_rate,
+ sample.vibrato_depth);
+ }
+
+ offset = offset + 96;
+ patch = (struct patch_info *) malloc (sizeof (*patch) + sample.len);
+
+ patch->key = GUS_PATCH;
+ patch->device_no = GUS_DEV;
+ patch->instr_no = pgm;
+ patch->mode = sample.modes | WAVE_TREMOLO |
+ WAVE_VIBRATO | WAVE_SCALE;
+ patch->len = sample.len;
+ patch->loop_start = sample.loop_start;
+ patch->loop_end = sample.loop_end;
+ patch->base_note = sample.base_note;
+ patch->high_note = sample.high_note;
+ patch->low_note = sample.low_note;
+ patch->base_freq = sample.base_freq;
+ patch->detuning = sample.detune;
+ patch->panning = (sample.panning - 7) * 16;
+
+ memcpy (patch->env_rate, sample.envelope_rate, 6);
+ memcpy (patch->env_offset, sample.envelope_offset, 6);
+
+ patch->tremolo_sweep = sample.tremolo_sweep;
+ patch->tremolo_rate = sample.tremolo_rate;
+ patch->tremolo_depth = sample.tremolo_depth;
+
+ patch->vibrato_sweep = sample.vibrato_sweep;
+ patch->vibrato_rate = sample.vibrato_rate;
+ patch->vibrato_depth = sample.vibrato_depth;
+
+ patch->scale_frequency = sample.scale_frequency;
+ patch->scale_factor = sample.scale_factor;
+
+ patch->volume = header.master_volume;
+
+ if (lseek (patfd, offset, 0) == -1)
+ {
+ perror (name);
+ exit (-1);
+ }
+
+ if (!print_only)
+ {
+ if (read (patfd, patch->data, sample.len) != sample.len)
+ {
+ fprintf (stderr, "%s: Short file\n", name);
+ exit (-1);
+ }
+
+ SEQ_WRPATCH (patch, sizeof (*patch) + sample.len);
+ }
+
+ offset = offset + sample.len;
+ }
+
+ exit (0);
+}
diff --git a/sys/i386/isa/sound/gustest/midithru.c b/sys/i386/isa/sound/gustest/midithru.c
new file mode 100644
index 0000000..78f58c1
--- /dev/null
+++ b/sys/i386/isa/sound/gustest/midithru.c
@@ -0,0 +1,325 @@
+#include <stdio.h>
+#include <machine/soundcard.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/errno.h>
+
+SEQ_DEFINEBUF (2048);
+SEQ_PM_DEFINES;
+
+int seqfd, dev = 0;
+unsigned char buf[100];
+int bufp;
+
+/* LRU list for free operators */
+
+unsigned char free_list[256];
+int fhead=0, ftail=0, flen=0;
+
+/* LRU list for still playing notes */
+
+unsigned char note_list[256];
+int nhead=0, ntail=0, nlen=0;
+unsigned char oper_note[32];
+
+int pgm = 0;
+int num_voices;
+int bender = 0; /* Initially off */
+
+void
+seqbuf_dump ()
+{
+ if (_seqbufptr)
+ if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ {
+ perror ("write /dev/sequencer");
+ exit (-1);
+ }
+ _seqbufptr = 0;
+}
+
+void
+stop_note(int note, int velocity)
+{
+ int i, op;
+
+ op=255;
+
+ for (i=0;i<num_voices && op==255;i++)
+ {
+ if (oper_note[i]== note) op=i;
+ }
+
+ if (op==255)
+ {
+ fprintf(stderr, "Note %d off, note not started\n", note);
+ fprintf(stderr, "%d, %d\n", flen, nlen);
+ return; /* Has already been killed ??? */
+ }
+
+ SEQ_STOP_NOTE(dev, op, note, velocity);
+ SEQ_DUMPBUF();
+
+ oper_note[op] = 255;
+
+ free_list[ftail]=op;
+ flen++;
+ ftail = (ftail+1) % num_voices;
+
+ for (i=0;i<16;i++)
+ if (note_list[i] == op) note_list[i] = 255;
+
+ while (nlen && note_list[nhead] == 255)
+ {
+ nlen--;
+ /* printf("Remove from note queue %d, len %d\n", nhead, nlen); */
+ nhead = (nhead+1) % 256;
+ }
+}
+
+void
+kill_one_note()
+{
+ int oldest;
+
+ if (!nlen) {fprintf(stderr, "Free list empty but no notes playing\n");return;} /* No notes playing */
+
+ oldest = note_list[nhead];
+ nlen--;
+ nhead = (nhead+1) % 256;
+
+ fprintf(stderr, "Killing oper %d, note %d\n", oldest, oper_note[oldest]);
+
+ if (oldest== 255) return; /* Was already stopped. Why? */
+
+ stop_note(oper_note[oldest], 127);
+}
+
+void
+start_note(int note, int velocity)
+{
+ int free;
+
+ if (!flen) kill_one_note();
+
+ if (!flen) {printf("** no free voices\n");return;} /* Panic??? */
+
+ free = free_list[fhead];
+ flen--;
+ fhead = (fhead+1) % num_voices;
+
+ note_list[ntail] = free;
+
+ if (nlen>255)
+ {
+#if 0
+ fprintf(stderr, "Note list overflow %d, %d, %d\n",
+ nlen, nhead, ntail);
+#endif
+ nlen=0; /* Overflow -> hard reset */
+ }
+ nlen++;
+ ntail = (ntail+1) % 256;
+
+ oper_note[free] = note;
+
+ SEQ_SET_PATCH(dev, free, pgm);
+ SEQ_PITCHBEND(dev, free, bender);
+ SEQ_START_NOTE(dev, free, note, velocity);
+ SEQ_DUMPBUF();
+}
+
+void
+channel_pressure(int ch, int pressure)
+{
+ int i;
+
+ for (i=0;i<num_voices;i++)
+ {
+ if (oper_note[i] != 255)
+ {
+#if 1
+ SEQ_CHN_PRESSURE(dev, i, pressure);
+#else
+ SEQ_EXPRESSION(dev, i, pressure);
+#endif
+ SEQ_DUMPBUF();
+ }
+ }
+}
+
+void
+pitch_bender(int ch, int value)
+{
+ int i;
+
+ value -= 8192;
+
+ bender = value;
+
+ for (i=0;i<num_voices;i++)
+ {
+ if (oper_note[i] != 255)
+ {
+ bender = value;
+ SEQ_PITCHBEND(dev, i, value);
+ SEQ_DUMPBUF();
+ }
+ }
+}
+
+void
+do_buf()
+{
+ int ch = buf[0] & 0x0f;
+ int value;
+
+ switch (buf[0] & 0xf0)
+ {
+ case 0x90: /* Note on */
+ if (bufp < 3) break;
+ /* printf("Note on %d %d %d\n", ch, buf[1], buf[2]); */
+ if (buf[2])
+ start_note(buf[1], buf[2]);
+ else
+ stop_note(buf[1], buf[2]);
+ bufp=1;
+ break;
+
+ case 0xb0: /* Control change */
+ if (bufp < 3) break;
+ /* printf("Control change %d %d %d\n", ch, buf[1], buf[2]); */
+ bufp=1;
+ break;
+
+ case 0x80: /* Note off */
+ if (bufp < 3) break;
+ /* printf("Note off %d %d %d\n", ch, buf[1], buf[2]); */
+ stop_note(buf[1], buf[2]);
+ bufp=1;
+ break;
+
+ case 0xe0: /* Pitch bender */
+ if (bufp < 3) break;
+ value = ((buf[2] & 0x7f) << 7) | (buf[1] & 0x7f);
+ /* printf("Pitch bender %d %d\n", ch, value >> 7); */
+ pitch_bender(ch, value);
+ bufp=1;
+ break;
+
+ case 0xc0: /* Pgm change */
+ if (bufp < 2) break;
+ /* printf("Pgm change %d %d\n", ch, buf[1]); */
+ pgm = buf[1];
+ if (PM_LOAD_PATCH(dev, 0, pgm) < 0)
+ if (errno != ESRCH) /* No such process */
+ perror("PM_LOAD_PATCH");
+ bufp=0;
+ break;
+
+ case 0xd0: /* Channel pressure */
+ if (bufp < 2) break;
+ /* printf("Channel pressure %d %d\n", ch, buf[1]); */
+ channel_pressure(ch, buf[1]);
+ bufp=1;
+ break;
+
+ default:
+ bufp=0;
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ int i, n, max_voice = 999;
+
+ struct synth_info info;
+
+ unsigned char ev[4], *p;
+
+ if (argc >= 2) dev = atoi(argv[1]);
+
+ for (i=0;i<16;i++) oper_note[i] = 255;
+
+ if ((seqfd = open ("/dev/sequencer", O_RDWR, 0)) == -1)
+ {
+ perror ("open /dev/sequencer");
+ exit (-1);
+ }
+
+ if (argc >= 3)
+ {
+ int d = dev;
+ ioctl(seqfd, SNDCTL_FM_4OP_ENABLE, &d);
+ }
+
+ info.device = dev;
+
+ if (ioctl(seqfd, SNDCTL_SYNTH_INFO, &info)==-1)
+ {
+ perror ("info /dev/sequencer");
+ exit (-1);
+ }
+
+ num_voices = info.nr_voices;
+ if (num_voices>max_voice)num_voices = max_voice;
+ fprintf(stderr, "Output to synth device %d (%s)\n", dev, info.name);
+ fprintf(stderr, "%d voices available\n", num_voices);
+
+ for (i=0;i<num_voices;i++)
+ {
+ flen++;
+ free_list[fhead] = i;
+ fhead = (fhead+1) % num_voices;
+ }
+
+ bufp = 0;
+ if (PM_LOAD_PATCH(dev, 0, 0) < 0) /* Load the default instrument */
+ if (errno != ESRCH) /* No such process */
+ perror("PM_LOAD_PATCH");
+
+ while (1)
+ {
+ if ((n = read (seqfd, ev, sizeof (ev))) == -1)
+ {
+ perror ("read /dev/sequencer");
+ exit (-1);
+ }
+
+ for (i = 0; i <= (n / 4); i++)
+ {
+ p = &ev[i * 4];
+
+ if (p[0] == SEQ_MIDIPUTC && p[2] == 0 /* Midi if# == 0 */)
+ {
+/* printf("%02x ", p[1]);fflush(stdout); */
+ if (p[1] & 0x80) /* Status */
+ {
+ if (bufp)
+ do_buf ();
+ buf[0] = p[1];
+ bufp = 1;
+ }
+ else if (bufp)
+ {
+ buf[bufp++] = p[1];
+ if ((buf[0] & 0xf0) == 0x90 || (buf[0] & 0xf0) == 0x80 || (buf[0] & 0xf0) == 0xb0 ||
+ (buf[0] & 0xf0) == 0xe0)
+ {
+ if (bufp == 3)
+ do_buf ();
+ }
+ else
+ if ((buf[0] & 0xf0) == 0xc0 || (buf[0] & 0xf0) == 0xd0)
+ {
+ if (bufp == 2) do_buf();
+ }
+ }
+ }
+ }
+ }
+
+ exit (0);
+}
diff --git a/sys/i386/isa/sound/gustest/pmtest.c b/sys/i386/isa/sound/gustest/pmtest.c
new file mode 100644
index 0000000..05b5b9e
--- /dev/null
+++ b/sys/i386/isa/sound/gustest/pmtest.c
@@ -0,0 +1,409 @@
+/*
+ * CAUTION! This program is just an incompletely implemented version
+ * of the patch manager daemon for GUS. Using this program
+ * with the driver version 1.99.9 will hang your system
+ * completely (sooner or later).
+ *
+ * This program is for information only. The final
+ * implementation of the patch manager will not be
+ * compatible with this one.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <machine/ultrasound.h>
+#include <strings.h>
+#include <sys/errno.h>
+#include "gmidi.h"
+
+#ifndef PATCH_PATH
+#define PATCH_PATH "/D/ultrasnd/midi"
+#endif
+
+char loadmap[256] =
+{0}; /* 1 if the patch is already loaded */
+
+struct pat_header
+ {
+ char magic[12];
+ char version[10];
+ char description[60];
+ unsigned char instruments;
+ char voices;
+ char channels;
+ unsigned short nr_waveforms;
+ unsigned short master_volume;
+ unsigned long data_size;
+ };
+
+struct sample_header
+ {
+ char name[7];
+ unsigned char fractions;
+ long len;
+ long loop_start;
+ long loop_end;
+ unsigned short base_freq;
+ long low_note;
+ long high_note;
+ long base_note;
+ short detune;
+ unsigned char panning;
+
+ unsigned char envelope_rate[6];
+ unsigned char envelope_offset[6];
+
+ unsigned char tremolo_sweep;
+ unsigned char tremolo_rate;
+ unsigned char tremolo_depth;
+
+ unsigned char vibrato_sweep;
+ unsigned char vibrato_rate;
+ unsigned char vibrato_depth;
+
+ char modes;
+
+ short scale_frequency;
+ unsigned short scale_factor;
+ };
+int seqfd = 0, gus_dev = -1;
+
+struct patch_info *patch;
+
+int
+do_load_patch (struct patmgr_info *rec)
+{
+ int i, patfd, pgm, print_only = 0;
+ struct pat_header header;
+ struct sample_header sample;
+ char buf[256];
+ char name[256];
+ long offset;
+
+ pgm = rec->data.data8[0];
+
+ if (loadmap[pgm])
+ return 0; /* Already loaded */
+
+ sprintf (name, PATCH_PATH "/%s.pat", patch_names[pgm]);
+
+ if ((patfd = open (name, O_RDONLY, 0)) == -1)
+ {
+ perror (name);
+ return errno;
+ }
+
+ if (read (patfd, buf, 0xef) != 0xef)
+ {
+ fprintf (stderr, "%s: Short file\n", name);
+ return EIO;
+ }
+
+ memcpy ((char *) &header, buf, sizeof (header));
+
+ if (strncmp (header.magic, "GF1PATCH110", 12))
+ {
+ fprintf (stderr, "%s: Not a patch file\n", name);
+ return EINVAL;
+ }
+
+ if (strncmp (header.version, "ID#000002", 10))
+ {
+ fprintf (stderr, "%s: Incompatible patch file version\n", name);
+ return EINVAL;
+ }
+
+ header.nr_waveforms = *(unsigned short *) &buf[85];
+ header.master_volume = *(unsigned short *) &buf[87];
+
+ printf ("GUS: Loading: %s\n", name);
+
+ offset = 0xef;
+
+ for (i = 0; i < header.nr_waveforms; i++)
+ {
+ if (lseek (patfd, offset, 0) == -1)
+ {
+ perror (name);
+ return errno;
+ }
+
+ if (read (patfd, &buf, sizeof (sample)) != sizeof (sample))
+ {
+ fprintf (stderr, "%s: Short file\n", name);
+ return EIO;
+ }
+
+ memcpy ((char *) &sample, buf, sizeof (sample));
+
+ /*
+ * Since some fields of the patch record are not 32bit aligned, we must
+ * handle them specially.
+ */
+ sample.low_note = *(long *) &buf[22];
+ sample.high_note = *(long *) &buf[26];
+ sample.base_note = *(long *) &buf[30];
+ sample.detune = *(short *) &buf[34];
+ sample.panning = (unsigned char) buf[36];
+
+ memcpy (sample.envelope_rate, &buf[37], 6);
+ memcpy (sample.envelope_offset, &buf[43], 6);
+
+ sample.tremolo_sweep = (unsigned char) buf[49];
+ sample.tremolo_rate = (unsigned char) buf[50];
+ sample.tremolo_depth = (unsigned char) buf[51];
+
+ sample.vibrato_sweep = (unsigned char) buf[52];
+ sample.vibrato_rate = (unsigned char) buf[53];
+ sample.vibrato_depth = (unsigned char) buf[54];
+ sample.modes = (unsigned char) buf[55];
+ sample.scale_frequency = *(short *) &buf[56];
+ sample.scale_factor = *(unsigned short *) &buf[58];
+
+ if (print_only)
+ {
+ printf ("\nSample: %03d / %s\n", i, sample.name);
+ printf ("Len: %d, Loop start: %d, Loop end: %d\n", sample.len, sample.loop_start, sample.loop_end);
+ printf ("Flags: ");
+ if (sample.modes & WAVE_16_BITS)
+ printf ("16 bit ");
+ if (sample.modes & WAVE_UNSIGNED)
+ printf ("unsigned ");
+ if (sample.modes & WAVE_LOOP_BACK)
+ printf ("reverse ");
+ if (sample.modes & WAVE_BIDIR_LOOP)
+ printf ("bidir ");
+ if (sample.modes & WAVE_LOOPING)
+ printf ("looping ");
+ else
+ printf ("one_shot");
+ if (sample.modes & WAVE_SUSTAIN_ON)
+ printf ("sustain ");
+ if (sample.modes & WAVE_ENVELOPES)
+ printf ("enveloped ");
+ printf ("\n");
+
+ if (sample.modes & WAVE_ENVELOPES)
+ {
+ int i;
+
+ printf ("Envelope info: ");
+ for (i = 0; i < 6; i++)
+ {
+ printf ("%d/%d ", sample.envelope_rate[i],
+ sample.envelope_offset[i]);
+ }
+ printf ("\n");
+ }
+
+ printf ("Tremolo: sweep=%d, rate=%d, depth=%d\n",
+ sample.tremolo_sweep,
+ sample.tremolo_rate,
+ sample.tremolo_depth);
+
+ printf ("Vibrato: sweep=%d, rate=%d, depth=%d\n",
+ sample.vibrato_sweep,
+ sample.vibrato_rate,
+ sample.vibrato_depth);
+ }
+
+ offset = offset + 96;
+ patch = (struct patch_info *) malloc (sizeof (*patch) + sample.len);
+
+ patch->key = GUS_PATCH;
+ patch->device_no = gus_dev;
+ patch->instr_no = pgm;
+ patch->mode = sample.modes | WAVE_TREMOLO |
+ WAVE_VIBRATO | WAVE_SCALE;
+ patch->len = sample.len;
+ patch->loop_start = sample.loop_start;
+ patch->loop_end = sample.loop_end;
+ patch->base_note = sample.base_note;
+ patch->high_note = sample.high_note;
+ patch->low_note = sample.low_note;
+ patch->base_freq = sample.base_freq;
+ patch->detuning = sample.detune;
+ patch->panning = (sample.panning - 7) * 16;
+
+ memcpy (patch->env_rate, sample.envelope_rate, 6);
+ memcpy (patch->env_offset, sample.envelope_offset, 6);
+
+ patch->tremolo_sweep = sample.tremolo_sweep;
+ patch->tremolo_rate = sample.tremolo_rate;
+ patch->tremolo_depth = sample.tremolo_depth;
+
+ patch->vibrato_sweep = sample.vibrato_sweep;
+ patch->vibrato_rate = sample.vibrato_rate;
+ patch->vibrato_depth = sample.vibrato_depth;
+
+ patch->scale_frequency = sample.scale_frequency;
+ patch->scale_factor = sample.scale_factor;
+
+ patch->volume = header.master_volume;
+
+ if (lseek (patfd, offset, 0) == -1)
+ {
+ perror (name);
+ return errno;
+ }
+
+ if (!print_only)
+ {
+ if (read (patfd, patch->data, sample.len) != sample.len)
+ {
+ fprintf (stderr, "%s: Short file\n", name);
+ return EIO;
+ }
+
+ if (write (seqfd, patch, sizeof (*patch) + sample.len) == -1)
+ {
+ perror ("/dev/pmgr0");
+ return errno;
+ }
+ }
+
+ offset = offset + sample.len;
+ }
+
+ loadmap[pgm] = 1;
+ return 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+ struct patmgr_info inf;
+ int err, i, n;
+ struct synth_info info;
+
+ if ((seqfd = open ("/dev/patmgr0", O_RDWR, 0)) == -1)
+ {
+ fprintf (stderr, "Cannot open\n");
+ perror ("/dev/patmgr0");
+ exit (-1);
+ }
+
+ if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1)
+ {
+ perror ("NRSYNTH: /dev/patmgr0");
+ exit (-1);
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ info.device = i;
+
+ if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1)
+ {
+ perror ("SYNTH_INFO: /dev/patmgr0");
+ exit (-1);
+ }
+
+ if (info.synth_type == SYNTH_TYPE_SAMPLE
+ && info.synth_subtype == SAMPLE_TYPE_GUS)
+ gus_dev = i;
+ }
+
+ if (gus_dev == -1)
+ {
+ fprintf (stderr, "Error: Gravis Ultrasound not detected\n");
+ exit (-1);
+ }
+
+ if (ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1)
+ perror ("Sample reset");
+
+ for (i = 0; i < 256; i++)
+ loadmap[i] = 0;
+
+ while (1)
+ {
+ if (read (seqfd, (char *) &inf, sizeof (inf)) != sizeof (inf))
+ {
+ perror ("Read");
+ exit (-1);
+ }
+
+ if (inf.key == PM_K_EVENT)
+ switch (inf.command)
+ {
+ case PM_E_OPENED:
+ printf ("Opened\n");
+ break;
+
+ case PM_E_CLOSED:
+ printf ("Closed\n");
+ if (ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1)
+ perror ("Sample reset");
+ for (i = 0; i < 256; i++)
+ loadmap[i] = 0;
+ break;
+
+ case PM_E_PATCH_RESET:
+ printf ("Patch reset called\n");
+ for (i = 0; i < 256; i++)
+ loadmap[i] = 0;
+ break;
+
+ case PM_E_PATCH_LOADED:
+ printf ("Patch loaded by client\n");
+ break;
+
+ default:
+ printf ("Unknown event %d\n", inf.command);
+ inf.key = PM_ERROR;
+ inf.parm1 = EINVAL;
+ }
+ else if (inf.key == PM_K_COMMAND)
+ switch (inf.command)
+ {
+ case _PM_LOAD_PATCH:
+ if ((err = do_load_patch (&inf)))
+ if (err == ENOSPC)
+ {
+ if (ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1)
+ {
+ perror ("Sample reset");
+ return errno;
+ }
+
+ for (i = 0; i < 256; i++)
+ loadmap[i] = 0;
+ err = do_load_patch (&inf);
+ }
+
+ if (err)
+ {
+ inf.key = PM_ERROR;
+ inf.parm1 = err;
+ printf("Error = %d\n", err);
+ }
+ else
+ {
+ inf.key = PM_K_COMMAND;
+ inf.parm1 = 0;
+ }
+ break;
+
+ default:
+ printf ("Unknown command %d\n", inf.command);
+ inf.key = PM_ERROR;
+ inf.parm1 = EINVAL;
+ }
+ else
+ {
+ printf ("Unknown event %d/%d\n", inf.key, inf.command);
+ inf.key = PM_ERROR;
+ inf.parm1 = EINVAL;
+ }
+
+ if (write (seqfd, (char *) &inf, sizeof (inf)) != sizeof (inf))
+ {
+ perror ("write");
+ exit (-1);
+ }
+ }
+
+ exit (0);
+}
diff --git a/sys/i386/isa/sound/ics2101.c b/sys/i386/isa/sound/ics2101.c
new file mode 100644
index 0000000..0e54c60
--- /dev/null
+++ b/sys/i386/isa/sound/ics2101.c
@@ -0,0 +1,265 @@
+/*
+ * sound/ics2101.c
+ *
+ * Driver for the ICS2101 mixer of GUS v3.7.
+ *
+ * Copyright by Hannu Savolainen 1994
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS)
+
+#ifdef __FreeBSD__
+#include <machine/ultrasound.h>
+#else
+#include "ultrasound.h"
+#endif
+#include "gus_hw.h"
+
+#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \
+ SOUND_MASK_SYNTH| \
+ SOUND_MASK_CD | SOUND_MASK_VOLUME)
+
+extern int gus_base;
+static int volumes[ICS_MIXDEVS];
+static int left_fix[ICS_MIXDEVS] =
+{1, 1, 1, 2, 1, 2};
+static int right_fix[ICS_MIXDEVS] =
+{2, 2, 2, 1, 2, 1};
+
+static int
+scale_vol(int vol)
+{
+#if 1
+/*
+ * Experimental volume scaling by Risto Kankkunen.
+ * This should give smoother volume response than just
+ * a plain multiplication.
+ */
+ int e;
+
+ if (vol < 0)
+ vol = 0;
+ if (vol > 100)
+ vol = 100;
+ vol = (31 * vol + 50) / 100;
+ e = 0;
+ if (vol) {
+ while (vol < 16) {
+ vol <<= 1;
+ e--;
+ }
+ vol -= 16;
+ e += 7;
+ }
+ return ((e << 4) + vol);
+#else
+ return ((vol*127)+50)/100;
+#endif
+}
+
+static void
+write_mix (int dev, int chn, int vol)
+{
+ int *selector;
+ unsigned long flags;
+ int ctrl_addr = dev << 3;
+ int attn_addr = dev << 3;
+
+ vol=scale_vol(vol);
+
+ if (chn == CHN_LEFT)
+ {
+ selector = left_fix;
+ ctrl_addr |= 0x00;
+ attn_addr |= 0x02;
+ }
+ else
+ {
+ selector = right_fix;
+ ctrl_addr |= 0x01;
+ attn_addr |= 0x03;
+ }
+
+ DISABLE_INTR (flags);
+ OUTB (ctrl_addr, u_MixSelect);
+ OUTB (selector[dev], u_MixData);
+ OUTB (attn_addr, u_MixSelect);
+ OUTB ((unsigned char) vol, u_MixData);
+ RESTORE_INTR (flags);
+}
+
+static int
+set_volumes (int dev, int vol)
+{
+ int left = vol & 0x00ff;
+ int right = (vol >> 8) & 0x00ff;
+
+ if (left < 0)
+ left = 0;
+ if (left > 100)
+ left = 100;
+ if (right < 0)
+ right = 0;
+ if (right > 100)
+ right = 100;
+
+ write_mix (dev, CHN_LEFT, left);
+ write_mix (dev, CHN_RIGHT, right);
+
+ vol = left + (right << 8);
+ volumes[dev] = vol;
+ return vol;
+}
+
+static int
+ics2101_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
+{
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ if (cmd & IOC_IN)
+ switch (cmd & 0xff)
+ {
+ case SOUND_MIXER_RECSRC:
+ return gus_default_mixer_ioctl (dev, cmd, arg);
+ break;
+
+ case SOUND_MIXER_MIC:
+ return IOCTL_OUT (arg, set_volumes (DEV_MIC, IOCTL_IN (arg)));
+ break;
+
+ case SOUND_MIXER_CD:
+ return IOCTL_OUT (arg, set_volumes (DEV_CD, IOCTL_IN (arg)));
+ break;
+
+ case SOUND_MIXER_LINE:
+ return IOCTL_OUT (arg, set_volumes (DEV_LINE, IOCTL_IN (arg)));
+ break;
+
+ case SOUND_MIXER_SYNTH:
+ return IOCTL_OUT (arg, set_volumes (DEV_GF1, IOCTL_IN (arg)));
+ break;
+
+ case SOUND_MIXER_VOLUME:
+ return IOCTL_OUT (arg, set_volumes (DEV_VOL, IOCTL_IN (arg)));
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ else
+ switch (cmd & 0xff) /*
+ * Return parameters
+ */
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return gus_default_mixer_ioctl (dev, cmd, arg);
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return IOCTL_OUT (arg, MIX_DEVS);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ return IOCTL_OUT (arg, SOUND_MASK_LINE | SOUND_MASK_CD |
+ SOUND_MASK_SYNTH | SOUND_MASK_VOLUME|
+ SOUND_MASK_MIC);
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return IOCTL_OUT (arg, SOUND_MASK_MIC | SOUND_MASK_LINE);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return IOCTL_OUT (arg, 0);
+ break;
+
+ case SOUND_MIXER_MIC:
+ return IOCTL_OUT (arg, volumes[DEV_MIC]);
+ break;
+
+ case SOUND_MIXER_LINE:
+ return IOCTL_OUT (arg, volumes[DEV_LINE]);
+ break;
+
+ case SOUND_MIXER_CD:
+ return IOCTL_OUT (arg, volumes[DEV_CD]);
+ break;
+
+ case SOUND_MIXER_VOLUME:
+ return IOCTL_OUT (arg, volumes[DEV_VOL]);
+ break;
+
+ case SOUND_MIXER_SYNTH:
+ return IOCTL_OUT (arg, volumes[DEV_GF1]);
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static struct mixer_operations ics2101_mixer_operations =
+{
+ ics2101_mixer_ioctl
+};
+
+long
+ics2101_mixer_init (long mem_start)
+{
+ int i;
+
+ if (num_mixers < MAX_MIXER_DEV)
+ {
+ mixer_devs[num_mixers++] = &ics2101_mixer_operations;
+
+ /*
+ * Some GUS v3.7 cards had some channels flipped. Disable
+ * the flipping feature if the model id is other than 5.
+ */
+
+ if (INB (u_MixSelect) != 5)
+ {
+ for (i = 0; i < ICS_MIXDEVS; i++)
+ left_fix[i] = 1;
+ for (i = 0; i < ICS_MIXDEVS; i++)
+ right_fix[i] = 2;
+ }
+
+ set_volumes (DEV_GF1, 0x5a5a);
+ set_volumes (DEV_CD, 0x5a5a);
+ set_volumes (DEV_MIC, 0x0000);
+ set_volumes (DEV_LINE, 0x5a5a);
+ set_volumes (DEV_VOL, 0x5a5a);
+ set_volumes (DEV_UNUSED, 0x0000);
+ }
+
+ return mem_start;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/local.h b/sys/i386/isa/sound/local.h
new file mode 100644
index 0000000..36092e3
--- /dev/null
+++ b/sys/i386/isa/sound/local.h
@@ -0,0 +1,15 @@
+/* for FreeBSD */
+#include "snd.h"
+
+#if NSND > 0
+#define KERNEL_SOUNDCARD
+#endif
+
+#define DSP_BUFFSIZE 65536
+#define NO_AUTODMA /* still */
+#define SELECTED_SOUND_OPTIONS 0xffffffff
+#define SOUND_VERSION_STRING "2.5"
+#define SOUND_CONFIG_DATE "Sat Apr 23 07:45:17 MSD 1994"
+#define SOUND_CONFIG_BY "ache"
+#define SOUND_CONFIG_HOST "dream.demos.su"
+#define SOUND_CONFIG_DOMAIN ""
diff --git a/sys/i386/isa/sound/midi.c b/sys/i386/isa/sound/midi.c
new file mode 100644
index 0000000..6ea51b0
--- /dev/null
+++ b/sys/i386/isa/sound/midi.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright by UWM - comments to soft-eng@cs.uwm.edu
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#define _MIDI_TABLE_C_
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#ifndef EXCLUDE_CHIP_MIDI
+
+
+static int generic_midi_busy[MAX_MIDI_DEV];
+
+long
+CMIDI_init (long mem_start)
+{
+
+ int i;
+ int n = num_midi_drivers;
+
+ /*
+ * int n = sizeof (midi_supported) / sizeof( struct generic_midi_info );
+ */
+ for (i = 0; i < n; i++)
+ {
+ if (midi_supported[i].attach (mem_start))
+ {
+ printk ("MIDI: Successfully attached %s\n", midi_supported[i].name);
+ }
+
+ }
+ return (mem_start);
+}
+
+
+int
+CMIDI_open (int dev, struct fileinfo *file)
+{
+
+ int mode, err, retval;
+
+ dev = dev >> 4;
+
+ mode = file->mode & O_ACCMODE;
+
+
+ if (generic_midi_busy[dev])
+ return (RET_ERROR (EBUSY));
+
+
+ if (dev >= num_generic_midis)
+ {
+ printk (" MIDI device %d not installed.\n", dev);
+ return (ENXIO);
+ }
+
+ if (!generic_midi_devs[dev])
+ {
+ printk (" MIDI device %d not initialized\n", dev);
+ return (ENXIO);
+ }
+
+ /* If all good and healthy, go ahead and issue call! */
+
+
+ retval = generic_midi_devs[dev]->open (dev, mode);
+
+ /* If everything ok, set device as busy */
+
+ if (retval >= 0)
+ generic_midi_busy[dev] = 1;
+
+ return (retval);
+
+}
+
+int
+CMIDI_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+
+ int retval;
+
+ dev = dev >> 4;
+
+ if (dev >= num_generic_midis)
+ {
+ printk (" MIDI device %d not installed.\n", dev);
+ return (ENXIO);
+ }
+
+ /*
+ * Make double sure of healthiness -- doubt Need we check this again??
+ *
+ */
+
+ if (!generic_midi_devs[dev])
+ {
+ printk (" MIDI device %d not initialized\n", dev);
+ return (ENXIO);
+ }
+
+ /* If all good and healthy, go ahead and issue call! */
+
+
+ retval = generic_midi_devs[dev]->write (dev, buf);
+
+ return (retval);
+
+}
+
+int
+CMIDI_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ int retval;
+
+ dev = dev >> 4;
+
+ if (dev >= num_generic_midis)
+ {
+ printk (" MIDI device %d not installed.\n", dev);
+ return (ENXIO);
+ }
+
+ /*
+ * Make double sure of healthiness -- doubt Need we check this again??
+ *
+ */
+
+ if (!generic_midi_devs[dev])
+ {
+ printk (" MIDI device %d not initialized\n", dev);
+ return (ENXIO);
+ }
+
+ /* If all good and healthy, go ahead and issue call! */
+
+
+ retval = generic_midi_devs[dev]->read (dev, buf);
+
+ return (retval);
+
+}
+
+int
+CMIDI_close (int dev, struct fileinfo *file)
+{
+
+ int retval;
+
+ dev = dev >> 4;
+
+ if (dev >= num_generic_midis)
+ {
+ printk (" MIDI device %d not installed.\n", dev);
+ return (ENXIO);
+ }
+
+ /*
+ * Make double sure of healthiness -- doubt Need we check this again??
+ *
+ */
+
+ if (!generic_midi_devs[dev])
+ {
+ printk (" MIDI device %d not initialized\n", dev);
+ return (ENXIO);
+ }
+
+ /* If all good and healthy, go ahead and issue call! */
+
+
+ generic_midi_devs[dev]->close (dev);
+
+ generic_midi_busy[dev] = 0; /* Free the device */
+
+ return (0);
+
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/midibuf.c b/sys/i386/isa/sound/midibuf.c
new file mode 100644
index 0000000..7dadb3f
--- /dev/null
+++ b/sys/i386/isa/sound/midibuf.c
@@ -0,0 +1,123 @@
+/*
+ * sound/midibuf.c
+ *
+ * Device file manager for /dev/midi
+ *
+ * NOTE! This part of the driver is currently just a stub.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MPU401)
+
+#if 0
+#include "midiioctl.h"
+#include "midivar.h"
+#endif
+
+static int midibuf_busy = 0;
+
+int
+MIDIbuf_open (int dev, struct fileinfo *file)
+{
+ int mode, err;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+
+ if (midibuf_busy)
+ return RET_ERROR (EBUSY);
+
+ if (!mpu401_dev)
+ {
+ printk ("Midi: MPU-401 compatible Midi interface not present\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if ((err = midi_devs[mpu401_dev]->open (mpu401_dev, mode, NULL, NULL)) < 0)
+ return err;
+
+ midibuf_busy = 1;
+
+ return RET_ERROR (ENXIO);
+}
+
+void
+MIDIbuf_release (int dev, struct fileinfo *file)
+{
+ int mode;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+
+ midi_devs[mpu401_dev]->close (mpu401_dev);
+ midibuf_busy = 0;
+}
+
+int
+MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+
+ dev = dev >> 4;
+
+ return count;
+}
+
+
+int
+MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ dev = dev >> 4;
+
+ return RET_ERROR (EIO);
+}
+
+int
+MIDIbuf_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+ dev = dev >> 4;
+
+ switch (cmd)
+ {
+
+ default:
+ return midi_devs[0]->ioctl (dev, cmd, arg);
+ }
+}
+
+void
+MIDIbuf_bytes_received (int dev, unsigned char *buf, int count)
+{
+}
+
+long
+MIDIbuf_init (long mem_start)
+{
+ return mem_start;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/mpu401.c b/sys/i386/isa/sound/mpu401.c
new file mode 100644
index 0000000..38ba486
--- /dev/null
+++ b/sys/i386/isa/sound/mpu401.c
@@ -0,0 +1,282 @@
+/*
+ * sound/mpu401.c
+ *
+ * The low level driver for Roland MPU-401 compatible Midi cards.
+ *
+ * This version supports just the DUMB UART mode.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI)
+
+#define DATAPORT (mpu401_base)/* MPU-401 Data I/O Port on IBM */
+#define COMDPORT (mpu401_base+1) /* MPU-401 Command Port on IBM */
+#define STATPORT (mpu401_base+1) /* MPU-401 Status Port on IBM */
+
+#define mpu401_status() INB(STATPORT)
+#define input_avail() (!(mpu401_status()&INPUT_AVAIL))
+#define output_ready() (!(mpu401_status()&OUTPUT_READY))
+#define mpu401_cmd(cmd) OUTB(cmd, COMDPORT)
+#define mpu401_read() INB(DATAPORT)
+#define mpu401_write(byte) OUTB(byte, DATAPORT)
+
+#define OUTPUT_READY 0x40 /* Mask for Data Read Redy Bit */
+#define INPUT_AVAIL 0x80 /* Mask for Data Send Ready Bit */
+#define MPU_ACK 0xFE /* MPU-401 Acknowledge Response */
+#define MPU_RESET 0xFF /* MPU-401 Total Reset Command */
+#define UART_MODE_ON 0x3F /* MPU-401 "Dumb UART Mode" */
+
+static int mpu401_opened = 0;
+static int mpu401_base = 0x330;
+static int mpu401_irq;
+static int mpu401_detected = 0;
+static int my_dev;
+
+static int reset_mpu401 (void);
+static void (*midi_input_intr) (int dev, unsigned char data);
+
+void
+mpuintr (int unit)
+{
+ while (input_avail ())
+ {
+ unsigned char c = mpu401_read ();
+
+ if (mpu401_opened & OPEN_READ)
+ midi_input_intr (my_dev, c);
+ }
+}
+
+static int
+mpu401_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ if (mpu401_opened)
+ {
+ printk ("MPU-401: Midi busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ mpuintr (0);
+
+ midi_input_intr = input;
+ mpu401_opened = mode;
+
+ return 0;
+}
+
+static void
+mpu401_close (int dev)
+{
+ mpu401_opened = 0;
+}
+
+static int
+mpu401_out (int dev, unsigned char midi_byte)
+{
+ int timeout;
+ unsigned long flags;
+
+ /*
+ * Test for input since pending input seems to block the output.
+ */
+
+ DISABLE_INTR (flags);
+
+ if (input_avail ())
+ mpuintr (0);
+
+ RESTORE_INTR (flags);
+
+ /*
+ * Sometimes it takes about 13000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
+
+ for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /* Wait */
+
+ if (!output_ready ())
+ {
+ printk ("MPU-401: Timeout\n");
+ return 0;
+ }
+
+ mpu401_write (midi_byte);
+ return 1;
+}
+
+static int
+mpu401_command (int dev, unsigned char midi_byte)
+{
+ return 1;
+}
+
+static int
+mpu401_start_read (int dev)
+{
+ return 0;
+}
+
+static int
+mpu401_end_read (int dev)
+{
+ return 0;
+}
+
+static int
+mpu401_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+mpu401_kick (int dev)
+{
+}
+
+static int
+mpu401_buffer_status (int dev)
+{
+ return 0; /* No data in buffers */
+}
+
+static struct midi_operations mpu401_operations =
+{
+ {"MPU-401", 0, 0, SNDCARD_MPU401},
+ mpu401_open,
+ mpu401_close,
+ mpu401_ioctl,
+ mpu401_out,
+ mpu401_start_read,
+ mpu401_end_read,
+ mpu401_kick,
+ mpu401_command,
+ mpu401_buffer_status
+};
+
+
+long
+attach_mpu401 (long mem_start, struct address_info *hw_config)
+{
+ int ok, timeout;
+ unsigned long flags;
+
+ mpu401_base = hw_config->io_base;
+ mpu401_irq = hw_config->irq;
+
+ if (!mpu401_detected)
+ return RET_ERROR (EIO);
+
+ DISABLE_INTR (flags);
+ for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* Wait */
+ mpu401_cmd (UART_MODE_ON);
+
+ ok = 0;
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (input_avail ())
+ if (mpu401_read () == MPU_ACK)
+ ok = 1;
+
+ RESTORE_INTR (flags);
+
+#ifdef __FreeBSD__
+ printk ("snd5: <Roland MPU-401>");
+#else
+ printk (" <Roland MPU-401>");
+#endif
+
+ my_dev = num_midis;
+ mpu401_dev = num_midis;
+ midi_devs[num_midis++] = &mpu401_operations;
+ return mem_start;
+}
+
+static int
+reset_mpu401 (void)
+{
+ unsigned long flags;
+ int ok, timeout, n;
+
+ /*
+ * Send the RESET command. Try again if no success at the first time.
+ */
+
+ ok = 0;
+
+ DISABLE_INTR (flags);
+
+ for (n = 0; n < 2 && !ok; n++)
+ {
+ for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* Wait */
+ mpu401_cmd (MPU_RESET); /* Send MPU-401 RESET Command */
+
+ /*
+ * Wait at least 25 msec. This method is not accurate so let's make the
+ * loop bit longer. Cannot sleep since this is called during boot.
+ */
+
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (input_avail ())
+ if (mpu401_read () == MPU_ACK)
+ ok = 1;
+
+ }
+
+ mpu401_opened = 0;
+ if (ok)
+ mpuintr (0); /* Flush input before enabling interrupts */
+
+ RESTORE_INTR (flags);
+
+ return ok;
+}
+
+
+int
+probe_mpu401 (struct address_info *hw_config)
+{
+ int ok = 0;
+
+ mpu401_base = hw_config->io_base;
+ mpu401_irq = hw_config->irq;
+
+ if (snd_set_irq_handler (mpu401_irq, mpuintr) < 0)
+ return 0;
+
+ ok = reset_mpu401 ();
+
+ mpu401_detected = ok;
+ return ok;
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/opl3.c b/sys/i386/isa/sound/opl3.c
new file mode 100644
index 0000000..6e3dcca
--- /dev/null
+++ b/sys/i386/isa/sound/opl3.c
@@ -0,0 +1,960 @@
+/*
+ * sound/opl3.c
+ *
+ * A low level driver for Yamaha YM3812 and OPL-3 -chips
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* Major improvements to the FM handling 30AUG92 by Rob Hooft, */
+/* hooft@chem.ruu.nl */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_YM3812)
+
+#include "opl3.h"
+
+#define MAX_VOICE 18
+#define OFFS_4OP 11 /* Definitions for the operators OP3 and OP4
+ * begin here */
+
+static int opl3_enabled = 0;
+static int left_address = 0x388, right_address = 0x388, both_address = 0;
+
+static int nr_voices = 9;
+static int logical_voices[MAX_VOICE] =
+{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
+
+struct voice_info
+ {
+ unsigned char keyon_byte;
+ long bender;
+ long bender_range;
+ unsigned long orig_freq;
+ unsigned long current_freq;
+ int mode;
+ };
+
+static struct voice_info voices[MAX_VOICE];
+
+static struct sbi_instrument *instrmap;
+static struct sbi_instrument *active_instrument[MAX_VOICE] =
+{NULL};
+
+static struct synth_info fm_info =
+{"AdLib", 0, SYNTH_TYPE_FM, FM_TYPE_ADLIB, 0, 9, 0, SBFM_MAXINSTR, 0};
+
+static int already_initialized = 0;
+
+static int opl3_ok = 0;
+static int opl3_busy = 0;
+static int fm_model = 0; /* 0=no fm, 1=mono, 2=SB Pro 1, 3=SB Pro 2 */
+
+static int store_instr (int instr_no, struct sbi_instrument *instr);
+static void freq_to_fnum (int freq, int *block, int *fnum);
+static void opl3_command (int io_addr, unsigned int addr, unsigned int val);
+static int opl3_kill_note (int dev, int voice, int velocity);
+static unsigned char connection_mask = 0x00;
+
+void
+enable_opl3_mode (int left, int right, int both)
+{
+ if (opl3_enabled)
+ return;
+
+ opl3_enabled = 1;
+ left_address = left;
+ right_address = right;
+ both_address = both;
+ fm_info.capabilities = SYNTH_CAP_OPL3;
+ fm_info.synth_subtype = FM_TYPE_OPL3;
+}
+
+static void
+enter_4op_mode (void)
+{
+ int i;
+ static int voices_4op[MAX_VOICE] =
+ {0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17};
+
+ connection_mask = 0x3f;
+ opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x3f); /* Select all 4-OP
+ * voices */
+ for (i = 0; i < 3; i++)
+ physical_voices[i].voice_mode = 4;
+ for (i = 3; i < 6; i++)
+ physical_voices[i].voice_mode = 0;
+
+ for (i = 9; i < 12; i++)
+ physical_voices[i].voice_mode = 4;
+ for (i = 12; i < 15; i++)
+ physical_voices[i].voice_mode = 0;
+
+ for (i = 0; i < 12; i++)
+ logical_voices[i] = voices_4op[i];
+ nr_voices = 12;
+}
+
+static int
+opl3_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+ switch (cmd)
+ {
+
+ case SNDCTL_FM_LOAD_INSTR:
+ {
+ struct sbi_instrument ins;
+
+ IOCTL_FROM_USER ((char *) &ins, (char *) arg, 0, sizeof (ins));
+
+ if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
+ {
+ printk ("FM Error: Invalid instrument number %d\n", ins.channel);
+ return RET_ERROR (EINVAL);
+ }
+
+ pmgr_inform (dev, PM_E_PATCH_LOADED, ins.channel, 0, 0, 0);
+ return store_instr (ins.channel, &ins);
+ }
+ break;
+
+ case SNDCTL_SYNTH_INFO:
+ fm_info.nr_voices = (nr_voices == 12) ? 6 : nr_voices;
+
+ IOCTL_TO_USER ((char *) arg, 0, &fm_info, sizeof (fm_info));
+ return 0;
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ return 0x7fffffff;
+ break;
+
+ case SNDCTL_FM_4OP_ENABLE:
+ if (opl3_enabled)
+ enter_4op_mode ();
+ return 0;
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+}
+
+int
+opl3_detect (int ioaddr)
+{
+ /*
+ * This function returns 1 if the FM chicp is present at the given I/O port
+ * The detection algorithm plays with the timer built in the FM chip and
+ * looks for a change in the status register.
+ *
+ * Note! The timers of the FM chip are not connected to AdLib (and compatible)
+ * boards.
+ *
+ * Note2! The chip is initialized if detected.
+ */
+
+ unsigned char stat1, stat2;
+ int i;
+
+ if (already_initialized)
+ {
+ return 0; /* Do avoid duplicate initializations */
+ }
+
+ if (opl3_enabled)
+ ioaddr = left_address;
+
+ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); /* Reset timers 1 and 2 */
+ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); /* Reset the IRQ of FM
+ * chicp */
+
+ stat1 = INB (ioaddr); /* Read status register */
+
+ if ((stat1 & 0xE0) != 0x00)
+ {
+ return 0; /* Should be 0x00 */
+ }
+
+ opl3_command (ioaddr, TIMER1_REGISTER, 0xff); /* Set timer 1 to 0xff */
+ opl3_command (ioaddr, TIMER_CONTROL_REGISTER,
+ TIMER2_MASK | TIMER1_START); /* Unmask and start timer 1 */
+
+ /*
+ * Now we have to delay at least 80 msec
+ */
+
+ for (i = 0; i < 50; i++)
+ tenmicrosec (); /* To be sure */
+
+ stat2 = INB (ioaddr); /* Read status after timers have expired */
+
+ /* Stop the timers */
+
+ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); /* Reset timers 1 and 2 */
+ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); /* Reset the IRQ of FM
+ * chicp */
+
+ if ((stat2 & 0xE0) != 0xc0)
+ {
+ return 0; /* There is no YM3812 */
+ }
+
+ /* There is a FM chicp in this address. Now set some default values. */
+
+ for (i = 0; i < 9; i++)
+ opl3_command (ioaddr, KEYON_BLOCK + i, 0); /* Note off */
+
+ opl3_command (ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT);
+ opl3_command (ioaddr, PERCUSSION_REGISTER, 0x00); /* Melodic mode. */
+
+ return 1;
+}
+
+static int
+opl3_kill_note (int dev, int voice, int velocity)
+{
+ struct physical_voice_info *map;
+
+ if (voice < 0 || voice >= nr_voices)
+ return 0;
+
+ map = &physical_voices[logical_voices[voice]];
+
+ DEB (printk ("Kill note %d\n", voice));
+
+ if (map->voice_mode == 0)
+ return 0;
+
+ opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, voices[voice].keyon_byte & ~0x20);
+
+ voices[voice].keyon_byte = 0;
+ voices[voice].bender = 0;
+ voices[voice].bender_range = 200; /* 200 cents = 2 semitones */
+ voices[voice].orig_freq = 0;
+ voices[voice].current_freq = 0;
+ voices[voice].mode = 0;
+
+ return 0;
+}
+
+#define HIHAT 0
+#define CYMBAL 1
+#define TOMTOM 2
+#define SNARE 3
+#define BDRUM 4
+#define UNDEFINED TOMTOM
+#define DEFAULT TOMTOM
+
+static int
+store_instr (int instr_no, struct sbi_instrument *instr)
+{
+
+ if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || !opl3_enabled))
+ printk ("FM warning: Invalid patch format field (key) 0x%x\n", instr->key);
+ memcpy ((char *) &(instrmap[instr_no]), (char *) instr, sizeof (*instr));
+
+ return 0;
+}
+
+static int
+opl3_set_instr (int dev, int voice, int instr_no)
+{
+ if (voice < 0 || voice >= nr_voices)
+ return 0;
+
+ if (instr_no < 0 || instr_no >= SBFM_MAXINSTR)
+ return 0;
+
+ active_instrument[voice] = &instrmap[instr_no];
+ return 0;
+}
+
+/*
+ * The next table looks magical, but it certainly is not. Its values have
+ * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception
+ * for i=0. This log-table converts a linear volume-scaling (0..127) to a
+ * logarithmic scaling as present in the FM-synthesizer chips. so : Volume
+ * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative
+ * volume -8 it was implemented as a table because it is only 128 bytes and
+ * it saves a lot of log() calculations. (RH)
+ */
+char fm_volume_table[128] =
+{-64, -48, -40, -35, -32, -29, -27, -26, /* 0 - 7 */
+ -24, -23, -21, -20, -19, -18, -18, -17, /* 8 - 15 */
+ -16, -15, -15, -14, -13, -13, -12, -12, /* 16 - 23 */
+ -11, -11, -10, -10, -10, -9, -9, -8, /* 24 - 31 */
+ -8, -8, -7, -7, -7, -6, -6, -6,/* 32 - 39 */
+ -5, -5, -5, -5, -4, -4, -4, -4,/* 40 - 47 */
+ -3, -3, -3, -3, -2, -2, -2, -2,/* 48 - 55 */
+ -2, -1, -1, -1, -1, 0, 0, 0, /* 56 - 63 */
+ 0, 0, 0, 1, 1, 1, 1, 1, /* 64 - 71 */
+ 1, 2, 2, 2, 2, 2, 2, 2, /* 72 - 79 */
+ 3, 3, 3, 3, 3, 3, 3, 4, /* 80 - 87 */
+ 4, 4, 4, 4, 4, 4, 4, 5, /* 88 - 95 */
+ 5, 5, 5, 5, 5, 5, 5, 5, /* 96 - 103 */
+ 6, 6, 6, 6, 6, 6, 6, 6, /* 104 - 111 */
+ 6, 7, 7, 7, 7, 7, 7, 7, /* 112 - 119 */
+ 7, 7, 7, 8, 8, 8, 8, 8}; /* 120 - 127 */
+
+static void
+calc_vol (unsigned char *regbyte, int volume)
+{
+ int level = (~*regbyte & 0x3f);
+
+ if (level)
+ level += fm_volume_table[volume];
+
+ if (level > 0x3f)
+ level = 0x3f;
+ if (level < 0)
+ level = 0;
+
+ *regbyte = (*regbyte & 0xc0) | (~level & 0x3f);
+}
+
+static void
+set_voice_volume (int voice, int volume)
+{
+ unsigned char vol1, vol2, vol3, vol4;
+ struct sbi_instrument *instr;
+ struct physical_voice_info *map;
+
+ if (voice < 0 || voice >= nr_voices)
+ return;
+
+ map = &physical_voices[logical_voices[voice]];
+
+ instr = active_instrument[voice];
+
+ if (!instr)
+ instr = &instrmap[0];
+
+ if (instr->channel < 0)
+ return;
+
+ if (voices[voice].mode == 0)
+ return;
+
+ if (voices[voice].mode == 2)
+ { /* 2 OP voice */
+
+ vol1 = instr->operators[2];
+ vol2 = instr->operators[3];
+
+ if ((instr->operators[10] & 0x01))
+ { /* Additive synthesis */
+ calc_vol (&vol1, volume);
+ calc_vol (&vol2, volume);
+ }
+ else
+ { /* FM synthesis */
+ calc_vol (&vol2, volume);
+ }
+
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1); /* Modulator volume */
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2); /* Carrier volume */
+ }
+ else
+ { /* 4 OP voice */
+ int connection;
+
+ vol1 = instr->operators[2];
+ vol2 = instr->operators[3];
+ vol3 = instr->operators[OFFS_4OP + 2];
+ vol4 = instr->operators[OFFS_4OP + 3];
+
+ /*
+ * The connection method for 4 OP voices is defined by the rightmost
+ * bits at the offsets 10 and 10+OFFS_4OP
+ */
+
+ connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+
+ switch (connection)
+ {
+ case 0:
+ calc_vol (&vol4, volume); /* Just the OP 4 is carrier */
+ break;
+
+ case 1:
+ calc_vol (&vol2, volume);
+ calc_vol (&vol4, volume);
+ break;
+
+ case 2:
+ calc_vol (&vol1, volume);
+ calc_vol (&vol4, volume);
+ break;
+
+ case 3:
+ calc_vol (&vol1, volume);
+ calc_vol (&vol3, volume);
+ calc_vol (&vol4, volume);
+ break;
+
+ default:/* Why ?? */ ;
+ }
+
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1);
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2);
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], vol3);
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], vol4);
+ }
+}
+
+static int
+opl3_start_note (int dev, int voice, int note, int volume)
+{
+ unsigned char data, fpc;
+ int block, fnum, freq, voice_mode;
+ struct sbi_instrument *instr;
+ struct physical_voice_info *map;
+
+ if (voice < 0 || voice >= nr_voices)
+ return 0;
+
+ map = &physical_voices[logical_voices[voice]];
+
+ if (map->voice_mode == 0)
+ return 0;
+
+ if (note == 255) /* Just change the volume */
+ {
+ set_voice_volume (voice, volume);
+ return 0;
+ }
+
+ /* Kill previous note before playing */
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /* Carrier volume to min */
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /* Modulator volume to */
+
+ if (map->voice_mode == 4)
+ {
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], 0xff);
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], 0xff);
+ }
+
+ opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /* Note off */
+
+ instr = active_instrument[voice];
+
+ if (!instr)
+ instr = &instrmap[0];
+
+ if (instr->channel < 0)
+ {
+ printk (
+ "OPL3: Initializing voice %d with undefined instrument\n",
+ voice);
+ return 0;
+ }
+
+ if (map->voice_mode == 2 && instr->key == OPL3_PATCH)
+ return 0; /* Cannot play */
+
+ voice_mode = map->voice_mode;
+
+ if (voice_mode == 4)
+ {
+ int voice_shift;
+
+ voice_shift = (map->ioaddr == left_address) ? 0 : 3;
+ voice_shift += map->voice_num;
+
+ if (instr->key != OPL3_PATCH) /* Just 2 OP patch */
+ {
+ voice_mode = 2;
+ connection_mask &= ~(1 << voice_shift);
+ }
+ else
+ {
+ connection_mask |= (1 << voice_shift);
+ }
+
+ opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask);
+ }
+
+ /* Set Sound Characteristics */
+ opl3_command (map->ioaddr, AM_VIB + map->op[0], instr->operators[0]);
+ opl3_command (map->ioaddr, AM_VIB + map->op[1], instr->operators[1]);
+
+ /* Set Attack/Decay */
+ opl3_command (map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]);
+ opl3_command (map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]);
+
+ /* Set Sustain/Release */
+ opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]);
+ opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]);
+
+ /* Set Wave Select */
+ opl3_command (map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]);
+ opl3_command (map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]);
+
+ /* Set Feedback/Connection */
+ fpc = instr->operators[10];
+ if (!(fpc & 0x30))
+ fpc |= 0x30; /* Ensure that at least one chn is enabled */
+ opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num,
+ fpc);
+
+ /*
+ * If the voice is a 4 OP one, initialize the operators 3 and 4 also
+ */
+
+ if (voice_mode == 4)
+ {
+
+ /* Set Sound Characteristics */
+ opl3_command (map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]);
+ opl3_command (map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]);
+
+ /* Set Attack/Decay */
+ opl3_command (map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]);
+ opl3_command (map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]);
+
+ /* Set Sustain/Release */
+ opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]);
+ opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]);
+
+ /* Set Wave Select */
+ opl3_command (map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]);
+ opl3_command (map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]);
+
+ /* Set Feedback/Connection */
+ fpc = instr->operators[OFFS_4OP + 10];
+ if (!(fpc & 0x30))
+ fpc |= 0x30; /* Ensure that at least one chn is enabled */
+ opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc);
+ }
+
+ voices[voice].mode = voice_mode;
+
+ set_voice_volume (voice, volume);
+
+ freq = voices[voice].orig_freq = note_to_freq (note) / 1000;
+
+ /*
+ * Since the pitch bender may have been set before playing the note, we
+ * have to calculate the bending now.
+ */
+
+ freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range);
+ voices[voice].current_freq = freq;
+
+ freq_to_fnum (freq, &block, &fnum);
+
+ /* Play note */
+
+ data = fnum & 0xff; /* Least significant bits of fnumber */
+ opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data);
+
+ data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
+ voices[voice].keyon_byte = data;
+ opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data);
+ if (voice_mode == 4)
+ opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data);
+
+ return 0;
+}
+
+static void
+freq_to_fnum (int freq, int *block, int *fnum)
+{
+ int f, octave;
+
+ /* Converts the note frequency to block and fnum values for the FM chip */
+ /* First try to compute the block -value (octave) where the note belongs */
+
+ f = freq;
+
+ octave = 5;
+
+ if (f == 0)
+ octave = 0;
+ else if (f < 261)
+ {
+ while (f < 261)
+ {
+ octave--;
+ f <<= 1;
+ }
+ }
+ else if (f > 493)
+ {
+ while (f > 493)
+ {
+ octave++;
+ f >>= 1;
+ }
+ }
+
+ if (octave > 7)
+ octave = 7;
+
+ *fnum = freq * (1 << (20 - octave)) / 49716;
+ *block = octave;
+}
+
+static void
+opl3_command (int io_addr, unsigned int addr, unsigned int val)
+{
+ int i;
+
+ /*
+ * The original 2-OP synth requires a quite long delay after writing to a
+ * register. The OPL-3 survives with just two INBs
+ */
+
+ OUTB ((unsigned char) (addr & 0xff), io_addr); /* Select register */
+
+ if (!opl3_enabled)
+ tenmicrosec ();
+ else
+ for (i = 0; i < 2; i++)
+ INB (io_addr);
+
+ OUTB ((unsigned char) (val & 0xff), io_addr + 1); /* Write to register */
+
+ if (!opl3_enabled)
+ {
+ tenmicrosec ();
+ tenmicrosec ();
+ tenmicrosec ();
+ }
+ else
+ for (i = 0; i < 2; i++)
+ INB (io_addr);
+}
+
+static void
+opl3_reset (int dev)
+{
+ int i;
+
+ for (i = 0; i < nr_voices; i++)
+ {
+ opl3_command (physical_voices[logical_voices[i]].ioaddr,
+ KSL_LEVEL + physical_voices[logical_voices[i]].op[0], 0xff); /* OP1 volume to min */
+
+ opl3_command (physical_voices[logical_voices[i]].ioaddr,
+ KSL_LEVEL + physical_voices[logical_voices[i]].op[1], 0xff); /* OP2 volume to min */
+
+ if (physical_voices[logical_voices[i]].voice_mode == 4) /* 4 OP voice */
+ {
+ opl3_command (physical_voices[logical_voices[i]].ioaddr,
+ KSL_LEVEL + physical_voices[logical_voices[i]].op[2], 0xff); /* OP3 volume to min */
+
+ opl3_command (physical_voices[logical_voices[i]].ioaddr,
+ KSL_LEVEL + physical_voices[logical_voices[i]].op[3], 0xff); /* OP4 volume to min */
+ }
+
+ opl3_kill_note (dev, i, 64);
+ }
+
+ if (opl3_enabled)
+ {
+ nr_voices = 18;
+
+ for (i = 0; i < 18; i++)
+ logical_voices[i] = i;
+
+ for (i = 0; i < 18; i++)
+ physical_voices[i].voice_mode = 2;
+
+ }
+
+}
+
+static int
+opl3_open (int dev, int mode)
+{
+ if (!opl3_ok)
+ return RET_ERROR (ENXIO);
+ if (opl3_busy)
+ return RET_ERROR (EBUSY);
+ opl3_busy = 1;
+
+ connection_mask = 0x00; /* Just 2 OP voices */
+ if (opl3_enabled)
+ opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask);
+ return 0;
+}
+
+static void
+opl3_close (int dev)
+{
+ opl3_busy = 0;
+ nr_voices = opl3_enabled ? 18 : 9;
+ fm_info.nr_drums = 0;
+ fm_info.perc_mode = 0;
+
+ opl3_reset (dev);
+}
+
+static void
+opl3_hw_control (int dev, unsigned char *event)
+{
+}
+
+static int
+opl3_load_patch (int dev, int format, snd_rw_buf * addr,
+ int offs, int count, int pmgr_flag)
+{
+ struct sbi_instrument ins;
+
+ if (count < sizeof (ins))
+ {
+ printk ("FM Error: Patch record too short\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ COPY_FROM_USER (&((char *) &ins)[offs], (char *) addr, offs, sizeof (ins) - offs);
+
+ if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
+ {
+ printk ("FM Error: Invalid instrument number %d\n", ins.channel);
+ return RET_ERROR (EINVAL);
+ }
+ ins.key = format;
+
+ return store_instr (ins.channel, &ins);
+}
+
+static void
+opl3_panning (int dev, int voice, int pressure)
+{
+}
+
+static void
+opl3_volume_method (int dev, int mode)
+{
+}
+
+#define SET_VIBRATO(cell) { \
+ tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \
+ if (pressure > 110) \
+ tmp |= 0x40; /* Vibrato on */ \
+ opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);}
+
+static void
+opl3_aftertouch (int dev, int voice, int pressure)
+{
+ int tmp;
+ struct sbi_instrument *instr;
+ struct physical_voice_info *map;
+
+ if (voice < 0 || voice >= nr_voices)
+ return;
+
+ map = &physical_voices[logical_voices[voice]];
+
+ DEB (printk ("Aftertouch %d\n", voice));
+
+ if (map->voice_mode == 0)
+ return;
+
+ /*
+ * Adjust the amount of vibrato depending the pressure
+ */
+
+ instr = active_instrument[voice];
+
+ if (!instr)
+ instr = &instrmap[0];
+
+ if (voices[voice].mode == 4)
+ {
+ int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+
+ switch (connection)
+ {
+ case 0:
+ SET_VIBRATO (4);
+ break;
+
+ case 1:
+ SET_VIBRATO (2);
+ SET_VIBRATO (4);
+ break;
+
+ case 2:
+ SET_VIBRATO (1);
+ SET_VIBRATO (4);
+ break;
+
+ case 3:
+ SET_VIBRATO (1);
+ SET_VIBRATO (3);
+ SET_VIBRATO (4);
+ break;
+
+ }
+ /* Not implemented yet */
+ }
+ else
+ {
+ SET_VIBRATO (1);
+
+ if ((instr->operators[10] & 0x01)) /* Additive synthesis */
+ SET_VIBRATO (2);
+ }
+}
+
+#undef SET_VIBRATO
+
+static void
+opl3_controller (int dev, int voice, int ctrl_num, int value)
+{
+ unsigned char data;
+ int block, fnum, freq;
+ struct physical_voice_info *map;
+
+ if (voice < 0 || voice >= nr_voices)
+ return;
+
+ map = &physical_voices[logical_voices[voice]];
+
+ if (map->voice_mode == 0)
+ return;
+
+ switch (ctrl_num)
+ {
+ case CTRL_PITCH_BENDER:
+ voices[voice].bender = value;
+ if (!value)
+ return;
+ if (!(voices[voice].keyon_byte & 0x20))
+ return; /* Not keyed on */
+
+ freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range);
+ voices[voice].current_freq = freq;
+
+ freq_to_fnum (freq, &block, &fnum);
+
+ data = fnum & 0xff; /* Least significant bits of fnumber */
+ opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data);
+
+ data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); /* KEYON|OCTAVE|MS bits
+ * of f-num */
+ voices[voice].keyon_byte = data;
+ opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data);
+ break;
+
+ case CTRL_PITCH_BENDER_RANGE:
+ voices[voice].bender_range = value;
+ break;
+ }
+}
+
+static int
+opl3_patchmgr (int dev, struct patmgr_info *rec)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static struct synth_operations opl3_operations =
+{
+ &fm_info,
+ SYNTH_TYPE_FM,
+ FM_TYPE_ADLIB,
+ opl3_open,
+ opl3_close,
+ opl3_ioctl,
+ opl3_kill_note,
+ opl3_start_note,
+ opl3_set_instr,
+ opl3_reset,
+ opl3_hw_control,
+ opl3_load_patch,
+ opl3_aftertouch,
+ opl3_controller,
+ opl3_panning,
+ opl3_volume_method,
+ opl3_patchmgr
+};
+
+long
+opl3_init (long mem_start)
+{
+ int i;
+
+ PERMANENT_MALLOC (struct sbi_instrument *, instrmap,
+ SBFM_MAXINSTR * sizeof (*instrmap), mem_start);
+
+ synth_devs[num_synths++] = &opl3_operations;
+ fm_model = 0;
+ opl3_ok = 1;
+ if (opl3_enabled)
+ {
+#ifdef __FreeBSD__
+ printk ("snd1: <Yamaha OPL-3 FM>");
+#else
+ printk (" <Yamaha OPL-3 FM>");
+#endif
+ fm_model = 2;
+ nr_voices = 18;
+ fm_info.nr_drums = 0;
+ fm_info.capabilities |= SYNTH_CAP_OPL3;
+#ifndef SCO
+ strcpy (fm_info.name, "Yamaha OPL-3");
+#endif
+
+ for (i = 0; i < 18; i++)
+ if (physical_voices[i].ioaddr == USE_LEFT)
+ physical_voices[i].ioaddr = left_address;
+ else
+ physical_voices[i].ioaddr = right_address;
+
+
+ opl3_command (right_address, OPL3_MODE_REGISTER, OPL3_ENABLE); /* Enable OPL-3 mode */
+ opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x00); /* Select all 2-OP
+ * voices */
+ }
+ else
+ {
+#ifdef __FreeBSD__
+ printk ("snd1: <Yamaha 2-OP FM>");
+#else
+ printk (" <Yamaha 2-OP FM>");
+#endif
+ fm_model = 1;
+ nr_voices = 9;
+ fm_info.nr_drums = 0;
+
+ for (i = 0; i < 18; i++)
+ physical_voices[i].ioaddr = left_address;
+ };
+
+ already_initialized = 1;
+ for (i = 0; i < SBFM_MAXINSTR; i++)
+ instrmap[i].channel = -1;
+
+ return mem_start;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/opl3.h b/sys/i386/isa/sound/opl3.h
new file mode 100644
index 0000000..ea7901f
--- /dev/null
+++ b/sys/i386/isa/sound/opl3.h
@@ -0,0 +1,260 @@
+/*
+ * opl3.h - Definitions of the OPL-3 registers
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * The OPL-3 mode is switched on by writing 0x01, to the offset 5
+ * of the right side.
+ *
+ * Another special register at the right side is at offset 4. It contains
+ * a bit mask defining which voices are used as 4 OP voices.
+ *
+ * The percussive mode is implemented in the left side only.
+ *
+ * With the above exeptions the both sides can be operated independently.
+ *
+ * A 4 OP voice can be created by setting the corresponding
+ * bit at offset 4 of the right side.
+ *
+ * For example setting the rightmost bit (0x01) changes the
+ * first voice on the right side to the 4 OP mode. The fourth
+ * voice is made inaccessible.
+ *
+ * If a voice is set to the 2 OP mode, it works like 2 OP modes
+ * of the original YM3812 (AdLib). In addition the voice can
+ * be connected the left, right or both stereo channels. It can
+ * even be left unconnected. This works with 4 OP voices also.
+ *
+ * The stereo connection bits are located in the FEEDBACK_CONNECTION
+ * register of the voice (0xC0-0xC8). In 4 OP voices these bits are
+ * in the second half of the voice.
+ */
+
+/*
+ * Register numbers for the global registers
+ */
+
+#define TEST_REGISTER 0x01
+#define ENABLE_WAVE_SELECT 0x20
+
+#define TIMER1_REGISTER 0x02
+#define TIMER2_REGISTER 0x03
+#define TIMER_CONTROL_REGISTER 0x04 /* Left side */
+#define IRQ_RESET 0x80
+#define TIMER1_MASK 0x40
+#define TIMER2_MASK 0x20
+#define TIMER1_START 0x01
+#define TIMER2_START 0x02
+
+#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */
+#define RIGHT_4OP_0 0x01
+#define RIGHT_4OP_1 0x02
+#define RIGHT_4OP_2 0x04
+#define LEFT_4OP_0 0x08
+#define LEFT_4OP_1 0x10
+#define LEFT_4OP_2 0x20
+
+#define OPL3_MODE_REGISTER 0x05 /* Right side */
+#define OPL3_ENABLE 0x01
+
+#define KBD_SPLIT_REGISTER 0x08 /* Left side */
+#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */
+#define KEYBOARD_SPLIT 0x40
+
+#define PERCUSSION_REGISTER 0xbd /* Left side only */
+#define TREMOLO_DEPTH 0x80
+#define VIBRATO_DEPTH 0x40
+#define PERCUSSION_ENABLE 0x20
+#define BASSDRUM_ON 0x10
+#define SNAREDRUM_ON 0x08
+#define TOMTOM_ON 0x04
+#define CYMBAL_ON 0x02
+#define HIHAT_ON 0x01
+
+/*
+ * Offsets to the register banks for operators. To get the
+ * register number just add the operator offset to the bank offset
+ *
+ * AM/VIB/EG/KSR/Multiple (0x20 to 0x35)
+ */
+ #define AM_VIB 0x20
+ #define TREMOLO_ON 0x80
+ #define VIBRATO_ON 0x40
+ #define SUSTAIN_ON 0x20
+ #define KSR 0x10 /* Key scaling rate */
+ #define MULTIPLE_MASK 0x0f /* Frequency multiplier */
+
+ /*
+ * KSL/Total level (0x40 to 0x55)
+ */
+#define KSL_LEVEL 0x40
+#define KSL_MASK 0xc0 /* Envelope scaling bits */
+#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */
+
+/*
+ * Attack / Decay rate (0x60 to 0x75)
+ */
+#define ATTACK_DECAY 0x60
+#define ATTACK_MASK 0xf0
+#define DECAY_MASK 0x0f
+
+/*
+ * Sustain level / Release rate (0x80 to 0x95)
+ */
+#define SUSTAIN_RELEASE 0x80
+#define SUSTAIN_MASK 0xf0
+#define RELEASE_MASK 0x0f
+
+/*
+ * Wave select (0xE0 to 0xF5)
+ */
+#define WAVE_SELECT 0xe0
+
+/*
+ * Offsets to the register banks for voices. Just add to the
+ * voice number to get the register number.
+ *
+ * F-Number low bits (0xA0 to 0xA8).
+ */
+#define FNUM_LOW 0xa0
+
+/*
+ * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8)
+ */
+#define KEYON_BLOCK 0xb0
+#define KEYON_BIT 0x20
+#define BLOCKNUM_MASK 0x1c
+#define FNUM_HIGH_MASK 0x03
+
+/*
+ * Feedback / Connection (0xc0 to 0xc8)
+ *
+ * These registers have two new bits when the OPL-3 mode
+ * is selected. These bits controls connecting the voice
+ * to the stereo channels. For 4 OP voices this bit is
+ * defined in the second half of the voice (add 3 to the
+ * register offset).
+ *
+ * For 4 OP voices the connection bit is used in the
+ * both halfs (gives 4 ways to connect the operators).
+ */
+#define FEEDBACK_CONNECTION 0xc0
+#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */
+#define CONNECTION_BIT 0x01
+/*
+ * In the 4 OP mode there is four possible configurations how the
+ * operators can be connected together (in 2 OP modes there is just
+ * AM or FM). The 4 OP connection mode is defined by the rightmost
+ * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halfs.
+ *
+ * First half Second half Mode
+ *
+ * +---+
+ * v |
+ * 0 0 >+-1-+--2--3--4-->
+ *
+ *
+ *
+ * +---+
+ * | |
+ * 0 1 >+-1-+--2-+
+ * |->
+ * >--3----4-+
+ *
+ * +---+
+ * | |
+ * 1 0 >+-1-+-----+
+ * |->
+ * >--2--3--4-+
+ *
+ * +---+
+ * | |
+ * 1 1 >+-1-+--+
+ * |
+ * >--2--3-+->
+ * |
+ * >--4----+
+ */
+#define STEREO_BITS 0x30 /* OPL-3 only */
+#define VOICE_TO_LEFT 0x10
+#define VOICE_TO_RIGHT 0x20
+
+/*
+ * Definition table for the physical voices
+ */
+
+struct physical_voice_info {
+ unsigned char voice_num;
+ unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */
+ unsigned short ioaddr; /* I/O port (left or right side) */
+ unsigned char op[4]; /* Operator offsets */
+ };
+
+/*
+ * There is 18 possible 2 OP voices
+ * (9 in the left and 9 in the right).
+ * The first OP is the modulator and 2nd is the carrier.
+ *
+ * The first three voices in the both sides may be connected
+ * with another voice to a 4 OP voice. For example voice 0
+ * can be connected with voice 3. The operators of voice 3 are
+ * used as operators 3 and 4 of the new 4 OP voice.
+ * In this case the 2 OP voice number 0 is the 'first half' and
+ * voice 3 is the second.
+ */
+
+#define USE_LEFT 0
+#define USE_RIGHT 1
+
+static struct physical_voice_info physical_voices[18] =
+{
+/* No Mode Side OP1 OP2 OP3 OP4 */
+/* --------------------------------------------------- */
+ { 0, 2, USE_LEFT, {0x00, 0x03, 0x08, 0x0b}},
+ { 1, 2, USE_LEFT, {0x01, 0x04, 0x09, 0x0c}},
+ { 2, 2, USE_LEFT, {0x02, 0x05, 0x0a, 0x0d}},
+
+ { 3, 2, USE_LEFT, {0x08, 0x0b, 0x00, 0x00}},
+ { 4, 2, USE_LEFT, {0x09, 0x0c, 0x00, 0x00}},
+ { 5, 2, USE_LEFT, {0x0a, 0x0d, 0x00, 0x00}},
+
+ { 6, 2, USE_LEFT, {0x10, 0x13, 0x00, 0x00}}, /* Used by percussive voices */
+ { 7, 2, USE_LEFT, {0x11, 0x14, 0x00, 0x00}}, /* if the percussive mode */
+ { 8, 2, USE_LEFT, {0x12, 0x15, 0x00, 0x00}}, /* is selected */
+
+ { 0, 2, USE_RIGHT, {0x00, 0x03, 0x08, 0x0b}},
+ { 1, 2, USE_RIGHT, {0x01, 0x04, 0x09, 0x0c}},
+ { 2, 2, USE_RIGHT, {0x02, 0x05, 0x0a, 0x0d}},
+
+ { 3, 2, USE_RIGHT, {0x08, 0x0b, 0x00, 0x00}},
+ { 4, 2, USE_RIGHT, {0x09, 0x0c, 0x00, 0x00}},
+ { 5, 2, USE_RIGHT, {0x0a, 0x0d, 0x00, 0x00}},
+
+ { 6, 2, USE_RIGHT, {0x10, 0x13, 0x00, 0x00}},
+ { 7, 2, USE_RIGHT, {0x11, 0x14, 0x00, 0x00}},
+ { 8, 2, USE_RIGHT, {0x12, 0x15, 0x00, 0x00}}
+};
diff --git a/sys/i386/isa/sound/os.h b/sys/i386/isa/sound/os.h
new file mode 100644
index 0000000..fea169b
--- /dev/null
+++ b/sys/i386/isa/sound/os.h
@@ -0,0 +1,319 @@
+#ifndef _OS_H_
+#define _OS_H_
+/*
+ * OS specific settings for FreeBSD
+ *
+ * Copyright by UWM - comments to soft-eng@cs.uwm.edu
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This chould be used as an example when porting the driver to a new
+ * operating systems.
+ *
+ * What you should do is to rewrite the soundcard.c and os.h (this file).
+ * You should create a new subdirectory and put these two files there.
+ * In addition you have to do a makefile.<OS>.
+ *
+ * If you have to make changes to other than these two files, please contact me
+ * before making the changes. It's possible that I have already made the
+ * change.
+ */
+
+/*
+ * Insert here the includes required by your kernel.
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "ioctl.h"
+#include "tty.h"
+#include "proc.h"
+#include "user.h"
+#include "conf.h"
+#include "file.h"
+#include "uio.h"
+#include "kernel.h"
+#include "syslog.h"
+#include "errno.h"
+#include "malloc.h"
+#include "buf.h"
+#include "i386/isa/isa_device.h"
+
+/*
+ * Rest of the file is compiled only if the driver is really required.
+ */
+#ifdef CONFIGURE_SOUNDCARD
+
+/*
+ * select() is currently implemented in Linux specific way. Don't enable.
+ * I don't remember what the SHORT_BANNERS means so forget it.
+ */
+
+#undef ALLOW_SELECT
+#define SHORT_BANNERS
+
+/* The soundcard.h could be in a nonstandard place so inclyde it here. */
+#include <machine/soundcard.h>
+
+/*
+ * Here is the first portability problem. Every OS has it's own way to
+ * pass a pointer to the buffer in read() and write() calls. In Linux it's
+ * just a char*. In BSD it's struct uio. This parameter is passed to
+ * all functions called from read() or write(). Since nothing can be
+ * assumed about this structure, the driver uses set of macros for
+ * accessing the user buffer.
+ *
+ * The driver reads/writes bytes in the user buffer sequentially which
+ * means that calls like uiomove() can be used.
+ *
+ * snd_rw_buf is the type which is passed to the device file specific
+ * read() and write() calls.
+ *
+ * The following macros are used to move date to and from the
+ * user buffer. These macros should be used only when the
+ * target or source parameter has snd_rw_buf type.
+ * The offs parameter is a offset relative to the beginning of
+ * the user buffer. In Linux the offset is required but for example
+ * BSD passes the offset info in the uio structure. It could be usefull
+ * if these macros verify that the offs parameter and the value in
+ * the snd_rw_buf structure are equal.
+ */
+typedef struct uio snd_rw_buf;
+
+/*
+ * Move bytes from the buffer which the application given in a
+ * write() call.
+ * offs is position relative to the beginning of the buffer in
+ * user space. The count is number of bytes to be moved.
+ */
+#define COPY_FROM_USER(target, source, offs, count) \
+ do { if (uiomove(target, count, (struct uio *)source)) { \
+ printf ("sb: Bad copyin()!\n"); \
+ } } while(0)
+/* Like COPY_FOM_USER but for writes. */
+#define COPY_TO_USER(target, offs, source, count) \
+ do { if (uiomove(source, count, (struct uio *)target)) { \
+ printf ("sb: Bad copyout()!\n"); \
+ } } while(0)
+/*
+ * The following macros are like COPY_*_USER but work just with one byte (8bit),
+ * short (16 bit) or long (32 bit) at a time.
+ * The same restrictions apply than for COPY_*_USER
+ */
+#define GET_BYTE_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 1, (struct uio *)addr);}
+#define GET_SHORT_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 2, (struct uio *)addr);}
+#define GET_WORD_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 4, (struct uio *)addr);}
+#define PUT_WORD_TO_USER(addr, offs, data) {uiomove((char*)&(data), 4, (struct uio *)addr);}
+
+/*
+ * The way how the ioctl arguments are passed is another nonportable thing.
+ * In Linux the argument is just a pointer directly to the user segment. On
+ * 386bsd the data is already moved to the kernel space. The following
+ * macros should handle the difference.
+ */
+
+/*
+ * IOCTL_FROM_USER is used to copy a record pointed by the argument to
+ * a buffer in the kernel space. On 386bsd it can be done just by calling
+ * memcpy. With Linux a memcpy_from_fs should be called instead.
+ * Parameters of the following macros are like in the COPY_*_USER macros.
+ */
+
+/*
+ * When the ioctl argument points to a record or array (longer than 32 bits),
+ * the macros IOCTL_*_USER are used. It's assumed that the source and target
+ * parameters are direct memory addresses.
+ */
+#define IOCTL_FROM_USER(target, source, offs, count) {memcpy(target, &((source)[offs]), count);}
+#define IOCTL_TO_USER(target, offs, source, count) {memcpy(&((target)[offs]), source, count);}
+/* The following macros are used if the ioctl argument points to 32 bit int */
+#define IOCTL_IN(arg) (*(int*)arg)
+#define IOCTL_OUT(arg, ret) *(int*)arg = ret
+
+/*
+ * When the driver displays something to the console, printk() will be called.
+ * The name can be changed here.
+ */
+#define printk printf
+
+/*
+ * The following macros define an interface to the process management.
+ */
+
+struct snd_wait {
+ int mode; int aborting;
+ };
+
+/*
+ * DEFINE_WAIT_QUEUE is used where a wait queue is required. It must define
+ * a structure which can be passed as a parameter to a sleep(). The second
+ * parameter is name of a flag variable (must be defined as int).
+ */
+#define DEFINE_WAIT_QUEUE(qname, flag) static int *qname = NULL; \
+ static volatile struct snd_wait flag = {0}
+/* Like the above but defines an array of wait queues and flags */
+#define DEFINE_WAIT_QUEUES(qname, flag) static int *qname = {NULL}; \
+ static volatile struct snd_wait flag = {{0}}
+
+#define RESET_WAIT_QUEUE(q, f) {f.aborting = 0;f.mode = WK_NONE;}
+#define SET_ABORT_FLAG(q, f) f.aborting = 1
+#define TIMED_OUT(q, f) (f.mode & WK_TIMEOUT)
+#define SOMEONE_WAITING(q, f) (f.mode & WK_SLEEP)
+/*
+ * This driver handles interrupts little bit nonstandard way. The following
+ * macro is used to test if the current process has received a signal which
+ * is aborts the process. This macro is called from close() to see if the
+ * buffers should be discarded. If this kind info is not available, a constant
+ * 1 or 0 could be returned (1 should be better than 0).
+ * I'm not sure if the following is correct for FreeBSD.
+ */
+#define PROCESS_ABORTING(q, f) (f.aborting | curproc->p_siglist)
+
+/*
+ * The following macro calls sleep. It should be implemented such that
+ * the process is resumed if it receives a signal. The following is propably
+ * not the way how it should be done on 386bsd.
+ * The on_what parameter is a wait_queue defined with DEFINE_WAIT_QUEUE(),
+ * and the second is a workarea parameter. The third is a timeout
+ * in ticks. Zero means no timeout.
+ */
+#define DO_SLEEP(q, f, time_limit) \
+ { \
+ int flag, chn; \
+ f.mode = WK_SLEEP; \
+ q = &chn; \
+ flag=tsleep((caddr_t)&(chn), (PRIBIO-5)|PCATCH, "sndint", time_limit); \
+ if(flag == ERESTART) f.aborting = 1;\
+ else f.aborting = 0;\
+ f.mode &= ~WK_SLEEP; \
+ }
+/* An the following wakes up a process */
+#define WAKE_UP(q, f) {f.mode = WK_WAKEUP;wakeup((caddr_t)q);}
+
+/*
+ * Timing macros. This driver assumes that there is a timer running in the
+ * kernel. The timer should return a value which is increased once at every
+ * timer tick. The macro HZ should return the number of such ticks/sec.
+ */
+
+#ifndef HZ
+extern int hz;
+#define HZ hz
+#endif
+
+/*
+ * GET_TIME() returns current value of the counter incremented at timer
+ * ticks. This can overflow, so the timeout might be real big...
+ *
+ */
+extern unsigned long get_time(void);
+#define GET_TIME() get_time()
+/*#define GET_TIME() (lbolt) */ /* Returns current time (1/HZ secs since boot) */
+
+/*
+ * The following three macros are called before and after atomic
+ * code sequences. The flags parameter has always type of unsigned long.
+ * The macro DISABLE_INTR() should ensure that all interrupts which
+ * may invoke any part of the driver (timer, soundcard interrupts) are
+ * disabled.
+ * RESTORE_INTR() should return the interrupt status back to the
+ * state when DISABLE_INTR() was called. The flags parameter is
+ * a variable which can carry 32 bits of state information between
+ * DISABLE_INTR() and RESTORE_INTR() calls.
+ */
+#define DISABLE_INTR(flags) flags = splhigh()
+#define RESTORE_INTR(flags) splx(flags)
+
+/*
+ * INB() and OUTB() should be obvious. NOTE! The order of
+ * paratemeters of OUTB() is different than on some other
+ * operating systems.
+ */
+
+#define INB inb
+/*
+ * The outb(0, 0x80) is just for slowdown. It's bit unsafe since
+ * this address could be used for something usefull.
+ */
+#define OUTB(addr, data) {outb(data, addr);outb(0, 0x80);}
+
+/* memcpy() was not defined og 386bsd. Lets define it here */
+#define memcpy(d, s, c) bcopy(s, d, c)
+
+/*
+ * When a error (such as EINVAL) is returned by a function,
+ * the following macro is used. The driver assumes that a
+ * error is signalled by returning a negative value.
+ */
+
+#define RET_ERROR(err) -(err)
+
+/*
+ KERNEL_MALLOC() allocates requested number of memory and
+ KERNEL_FREE is used to free it.
+ These macros are never called from interrupt, in addition the
+ nbytes will never be more than 4096 bytes. Generally the driver
+ will allocate memory in blocks of 4k. If the kernel has just a
+ page level memory allocation, 4K can be safely used as the size
+ (the nbytes parameter can be ignored).
+*/
+#define KERNEL_MALLOC(nbytes) malloc(nbytes, M_TEMP, M_WAITOK)
+#define KERNEL_FREE(addr) free(addr, M_TEMP)
+
+/*
+ * The macro PERMANENT_MALLOC(typecast, mem_ptr, size, linux_ptr)
+ * returns size bytes of
+ * (kernel virtual) memory which will never get freed by the driver.
+ * This macro is called only during boot. The linux_ptr is a linux specific
+ * parameter which should be ignored in other operating systems.
+ * The mem_ptr is a pointer variable where the macro assigns pointer to the
+ * memory area. The type is the type of the mem_ptr.
+ */
+#define PERMANENT_MALLOC(typecast, mem_ptr, size, linux_ptr) \
+ (mem_ptr) = (typecast)malloc((size), M_TEMP, M_WAITOK)
+
+/*
+ * The macro DEFINE_TIMER defines variables for the ACTIVATE_TIMER if
+ * required. The name is the variable/name to be used and the proc is
+ * the procedure to be called when the timer expires.
+ */
+
+#define DEFINE_TIMER(name, proc)
+
+/*
+ * The ACTIVATE_TIMER requests system to call 'proc' after 'time' ticks.
+ */
+
+#define ACTIVATE_TIMER(name, proc, time) \
+ timeout((timeout_func_t)proc, 0, time);
+/*
+ * The rest of this file is not complete yet. The functions using these
+ * macros will not work
+ */
+#define ALLOC_DMA_CHN(chn) ({ 0; })
+#define RELEASE_DMA_CHN(chn) ({ 0; })
+#define DMA_MODE_READ 0
+#define DMA_MODE_WRITE 1
+#define RELEASE_IRQ(irq_no)
+
+#endif
+#endif
diff --git a/sys/i386/isa/sound/pas.h b/sys/i386/isa/sound/pas.h
new file mode 100644
index 0000000..9902e03
--- /dev/null
+++ b/sys/i386/isa/sound/pas.h
@@ -0,0 +1,250 @@
+/* */
+/* Port addresses and bit fields for the Media Vision Pro AudioSpectrum second generation sound cards. */
+/* */
+/* Feel free to use this header file in any application you create that has support for the Media Vision */
+/* Pro AudioSpectrum second generation sound cards. Other uses prohibited without prior permission. */
+/* */
+/* - cmetz@thor.tjhsst.edu */
+/* */
+/* Notes: */
+/* */
+/* * All of these ports go into the MVD101 multimedia controller chip, which then signals the other chips to do */
+/* the actual work. Many ports like the FM ones functionally attach directly to the destination chip though */
+/* they don't actually have a direct connection. */
+/* */
+/* * The PAS2 series cards have an MVD101 multimedia controller chip, the original PAS cards don't. The original */
+/* PAS cards are pretty defunct now, so no attempt is made here to support them. */
+/* */
+/* * The PAS2 series cards are all really different at the hardware level, though the MVD101 hides some of the */
+/* incompatibilities, there still are differences that need to be accounted for. */
+/* */
+/* Card CD-ROM interface PCM chip Mixer chip FM chip */
+/* PAS Plus Sony proprietary (Crystal?) 8-bit DAC National OPL3 */
+/* PAS 16 Zilog SCSI MVA416 16-bit Codec MVA508 OPL3 */
+/* CDPC Sony proprietary Sony 16-bit Codec National OPL3 */
+/* Fusion CD 16 Sony proprietary MVA416 16-bit Codec MVA508 OPL3 */
+/* Fusion CD Sony proprietary (Crystal?) 8-bit DAC National OPL3 */
+/* */
+#define PAS_DEFAULT_BASE 0x388
+
+/* Symbolic Name Value R W Subsystem Description */
+#define SPEAKER_CONTROL 0x61 /* W PC speaker Control register */
+#define SPEAKER_CONTROL_GHOST 0x738B /* R W PC speaker Control ghost register */
+#define SPEAKER_TIMER_CONTROL 0x43 /* W PC speaker Timer control register */
+#define SPEAKER_TIMER_CONTROL_GHOST 0x778B /* R W PC speaker Timer control register ghost */
+#define SPEAKER_TIMER_DATA 0x42 /* W PC speaker Timer data register */
+#define SPEAKER_TIMER_DATA_GHOST 0x138A /* R W PC speaker Timer data register ghost */
+
+#define WARM_BOOT 0x41 /* W Control Used to detect system warm boot */
+#define WARM_BOOT_GHOST 0x7789 /* ? W Control Use to get the card to fake warm boot */
+#define MASTER_DECODE 0x9A01 /* W Control Address >> 2 of card base address */
+#define PRESCALE_DIVIDER 0xBF8A /* R W PCM Ration between Codec clock and master clock */
+#define WAIT_STATE 0xBF88 /* R W Control Four-bit bus wait-state count (~140ns ea.) */
+#define BOARD_REV_ID 0x2789 /* R Control Extended Board Revision ID */
+
+#define SYSTEM_CONFIGURATION_1 0x8388 /* R W Control */
+ #define S_C_1_PCS_ENABLE 0x01 /* R W PC speaker 1=enable, 0=disable PC speaker emulation */
+ #define S_C_1_PCM_CLOCK_SELECT 0x02 /* R W PCM 1=14.31818Mhz/12, 0=28.224Mhz master clock */
+ #define S_C_1_FM_EMULATE_CLOCK 0x04 /* R W FM 1=use 28.224Mhz/2, 0=use 14.31818Mhz clock */
+ #define S_C_1_PCS_STEREO 0x10 /* R W PC speaker 1=enable PC speaker stereo effect, 0=disable */
+ #define S_C_1_PCS_REALSOUND 0x20 /* R W PC speaker 1=enable RealSound enhancement, 0=disable */
+ #define S_C_1_FORCE_EXT_RESET 0x40 /* R W Control Force external reset */
+ #define S_C_1_FORCE_INT_RESET 0x80 /* R W Control Force internal reset */
+#define SYSTEM_CONFIGURATION_2 0x8389 /* R W Control */
+ #define S_C_2_PCM_OVERSAMPLING 0x03 /* R W PCM 00=0x, 01=2x, 10=4x, 11=reserved */
+ #define S_C_2_PCM_16_BIT 0x04 /* R W PCM 1=16-bit, 0=8-bit samples */
+#define SYSTEM_CONFIGURATION_3 0x838A /* R W Control */
+ #define S_C_3_PCM_CLOCK_SELECT 0x02 /* R W PCM 1=use 1.008Mhz clock for PCM, 0=don't */
+#define SYSTEM_CONFIGURATION_4 0x838B /* R W Control CD-ROM interface controls */
+
+#define IO_CONFIGURATION_1 0xF388 /* R W Control */
+ #define I_C_1_BOOT_RESET_ENABLE 0x80 /* R W Control 1=reset board on warm boot, 0=don't */
+#define IO_CONFIGURATION_2 0xF389 /* R W Control */
+ #define I_C_2_PCM_DMA_DISABLED 0x00 /* R W PCM PCM DMA disabled */
+#define IO_CONFIGURATION_3 0xF38A /* R W Control */
+ #define I_C_3_PCM_IRQ_DISABLED 0x00 /* R W PCM PCM IRQ disabled */
+
+#define COMPATIBILITY_ENABLE 0xF788 /* R W Control */
+ #define C_E_MPU401_ENABLE 0x01 /* R W MIDI 1=enable, 0=disable MPU401 MIDI emulation */
+ #define C_E_SB_ENABLE 0x02 /* R W PCM 1=enable, 0=disable Sound Blaster emulation */
+ #define C_E_SB_ACTIVE 0x04 /* R PCM "Sound Blaster Interrupt active" */
+ #define C_E_MPU401_ACTIVE 0x08 /* R MIDI "MPU UART mode active" */
+ #define C_E_PCM_COMPRESSION 0x10 /* R W PCM 1=enable, 0=disabled compression */
+#define EMULATION_ADDRESS 0xF789 /* R W Control */
+ #define E_A_SB_BASE 0x0f /* R W PCM bits A4-A7 for SB base port */
+ #define E_A_MPU401_BASE 0xf0 /* R W MIDI bits A4-A7 for MPU401 base port */
+#define EMULATION_CONFIGURATION 0xFB8A /* R W ***** Only valid on newer PAS2 cards (?) ***** */
+ #define E_C_MPU401_IRQ 0x07 /* R W MIDI MPU401 emulation IRQ */
+ #define E_C_SB_IRQ 0x38 /* R W PCM SB emulation IRQ */
+ #define E_C_SB_DMA 0xC0 /* R W PCM SB emulation DMA */
+
+#define OPERATION_MODE_1 0xEF8B /* R Control */
+ #define O_M_1_CDROM_TYPE 0x03 /* R CD-ROM 3=SCSI, 2=Sony, 0=no CD-ROM interface */
+ #define O_M_1_FM_TYPE 0x04 /* R FM 1=sterero, 0=mono FM chip */
+ #define O_M_1_PCM_TYPE 0x08 /* R PCM 1=16-bit Codec, 0=8-bit DAC */
+#define OPERATION_MODE_2 0xFF8B /* R Control */
+ #define O_M_2_PCS_ENABLED 0x02 /* R PC speaker PC speaker emulation 1=enabled, 0=disabled */
+ #define O_M_2_BUS_TIMING 0x10 /* R Control 1=AT bus timing, 0=XT bus timing */
+ #define O_M_2_BOARD_REVISION 0xe0 /* R Control Board revision */
+
+#define INTERRUPT_MASK 0x0B8B /* R W Control */
+ #define I_M_FM_LEFT_IRQ_ENABLE 0x01 /* R W FM Enable FM left interrupt */
+ #define I_M_FM_RIGHT_IRQ_ENABLE 0x02 /* R W FM Enable FM right interrupt */
+ #define I_M_PCM_RATE_IRQ_ENABLE 0x04 /* R W PCM Enable Sample Rate interrupt */
+ #define I_M_PCM_BUFFER_IRQ_ENABLE 0x08 /* R W PCM Enable Sample Buffer interrupt */
+ #define I_M_MIDI_IRQ_ENABLE 0x10 /* R W MIDI Enable MIDI interrupt */
+ #define I_M_BOARD_REV 0xE0 /* R Control Board revision */
+
+#define INTERRUPT_STATUS 0x0B89 /* R W Control */
+ #define I_S_FM_LEFT_IRQ 0x01 /* R W FM Left FM Interrupt Pending */
+ #define I_S_FM_RIGHT_IRQ 0x02 /* R W FM Right FM Interrupt Pending */
+ #define I_S_PCM_SAMPLE_RATE_IRQ 0x04 /* R W PCM Sample Rate Interrupt Pending */
+ #define I_S_PCM_SAMPLE_BUFFER_IRQ 0x08 /* R W PCM Sample Buffer Interrupt Pending */
+ #define I_S_MIDI_IRQ 0x10 /* R W MIDI MIDI Interrupt Pending */
+ #define I_S_PCM_CHANNEL 0x20 /* R W PCM 1=right, 0=left */
+ #define I_S_RESET_ACTIVE 0x40 /* R W Control Reset is active (Timed pulse not finished) */
+ #define I_S_PCM_CLIPPING 0x80 /* R W PCM Clipping has occurred */
+
+#define FILTER_FREQUENCY 0x0B8A /* R W Control */
+ #define F_F_FILTER_DISABLED 0x00 /* R W Mixer No filter */
+#if 0
+ struct { /* R W Mixer Filter translation */
+ unsigned int freq:24;
+ unsigned int value:8;
+ } F_F_FILTER_translate[] =
+ { { 73500, 0x01 }, /* 73500Hz - divide by 16 */
+ { 65333, 0x02 }, /* 65333Hz - divide by 18 */
+ { 49000, 0x09 }, /* 49000Hz - divide by 24 */
+ { 36750, 0x11 }, /* 36750Hz - divide by 32 */
+ { 24500, 0x19 }, /* 24500Hz - divide by 48 */
+ { 18375, 0x07 }, /* 18375Hz - divide by 64 */
+ { 12783, 0x0f }, /* 12783Hz - divide by 92 */
+ { 12250, 0x04 }, /* 12250Hz - divide by 96 */
+ { 9188, 0x17 }, /* 9188Hz - divide by 128 */
+ { 6125, 0x1f }, /* 6125Hz - divide by 192 */
+ };
+#endif
+ #define F_F_MIXER_UNMUTE 0x20 /* R W Mixer 1=disable, 0=enable board mute */
+ #define F_F_PCM_RATE_COUNTER 0x40 /* R W PCM 1=enable, 0=disable sample rate counter */
+ #define F_F_PCM_BUFFER_COUNTER 0x80 /* R W PCM 1=enable, 0=disable sample buffer counter */
+
+#define PAS_NONE 0
+#define PAS_PLUS 1
+#define PAS_CDPC 2
+#define PAS_16 3
+#define PAS_16D 4
+
+#ifdef DEFINE_TRANSLATIONS
+ char I_C_2_PCM_DMA_translate[] = /* R W PCM PCM DMA channel value translations */
+ { 4, 1, 2, 3, 0, 5, 6, 7 };
+ char I_C_3_PCM_IRQ_translate[] = /* R W PCM PCM IRQ level value translation */
+ { 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11 };
+ char E_C_MPU401_IRQ_translate[] = /* R W MIDI MPU401 emulation IRQ value translation */
+ { 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x01, 0x05, 0x06, 0x07 };
+ char E_C_SB_IRQ_translate[] = /* R W PCM SB emulation IRQ translate */
+ { 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0 };
+ char E_C_SB_DMA_translate[] = /* R W PCM SB emulation DMA translate */
+ { 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0 };
+ char O_M_1_to_card[] = /* R W Control Translate (OM1 & 0x0f) to card type */
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4, 0, 2, 3 };
+#else
+ extern char I_C_2_PCM_DMA_translate[]; /* R W PCM PCM DMA channel value translations */
+ extern char I_C_3_PCM_IRQ_translate[]; /* R W PCM PCM IRQ level value translation */
+ extern char E_C_MPU401_IRQ_translate[]; /* R W MIDI MPU401 emulation IRQ value translation */
+ extern char E_C_SB_IRQ_translate[]; /* R W PCM SB emulation IRQ translate */
+ extern char E_C_SB_DMA_translate[]; /* R W PCM SB emulation DMA translate */
+ extern char O_M_1_to_card[]; /* R W Control Translate (OM1 & 0x0f) to card type */
+#endif
+
+#define PARALLEL_MIXER 0x078B /* W Mixer Documented for MVD101 as FM Mono Right decode?? */
+ #define P_M_MV508_ADDRESS 0x80 /* W Mixer MVD508 Address/mixer select */
+ #define P_M_MV508_DATA 0x00
+ #define P_M_MV508_LEFT 0x20 /* W Mixer MVD508 Left channel select */
+ #define P_M_MV508_RIGHT 0x40 /* W Mixer MVD508 Right channel select */
+ #define P_M_MV508_BOTH 0x00 /* W Mixer MVD508 Both channel select */
+ #define P_M_MV508_MIXER 0x10 /* W Mixer MVD508 Select a mixer (rather than a volume) */
+ #define P_M_MV508_VOLUME 0x00
+
+ #define P_M_MV508_INPUTMIX 0x20 /* W Mixer MVD508 Select mixer A */
+ #define P_M_MV508_OUTPUTMIX 0x00 /* W Mixer MVD508 Select mixer B */
+
+ #define P_M_MV508_MASTER_A 0x01 /* W Mixer MVD508 Master volume control A (output) */
+ #define P_M_MV508_MASTER_B 0x02 /* W Mixer MVD508 Master volume control B (DSP input) */
+ #define P_M_MV508_BASS 0x03 /* W Mixer MVD508 Bass control */
+ #define P_M_MV508_TREBLE 0x04 /* W Mixer MVD508 Treble control */
+ #define P_M_MV508_MODE 0x05 /* W Mixer MVD508 Master mode control */
+
+ #define P_M_MV508_LOUDNESS 0x04 /* W Mixer MVD508 Mode control - Loudness filter */
+ #define P_M_MV508_ENHANCE_BITS 0x03
+ #define P_M_MV508_ENHANCE_NONE 0x00 /* W Mixer MVD508 Mode control - No stereo enhancement */
+ #define P_M_MV508_ENHANCE_40 0x01 /* W Mixer MVD508 Mode control - 40% stereo enhancement */
+ #define P_M_MV508_ENHANCE_60 0x02 /* W Mixer MVD508 Mode control - 60% stereo enhancement */
+ #define P_M_MV508_ENHANCE_80 0x03 /* W Mixer MVD508 Mode control - 80% stereo enhancement */
+
+ #define P_M_MV508_FM 0x00 /* W Mixer MVD508 Channel 0 - FM */
+ #define P_M_MV508_IMIXER 0x01 /* W Mixer MVD508 Channel 1 - Input mixer (rec monitor) */
+ #define P_M_MV508_LINE 0x02 /* W Mixer MVD508 Channel 2 - Line in */
+ #define P_M_MV508_CDROM 0x03 /* W Mixer MVD508 Channel 3 - CD-ROM */
+ #define P_M_MV508_MIC 0x04 /* W Mixer MVD508 Channel 4 - Microphone */
+ #define P_M_MV508_PCM 0x05 /* W Mixer MVD508 Channel 5 - PCM */
+ #define P_M_MV508_SPEAKER 0x06 /* W Mixer MVD508 Channel 6 - PC Speaker */
+ #define P_M_MV508_SB 0x07 /* W Mixer MVD508 Channel 7 - SB DSP */
+
+#define SERIAL_MIXER 0xB88 /* R W Control Serial mixer control (used other ways) */
+ #define S_M_PCM_RESET 0x01 /* R W PCM Codec/DSP reset */
+ #define S_M_FM_RESET 0x02 /* R W FM FM chip reset */
+ #define S_M_SB_RESET 0x04 /* R W PCM SB emulation chip reset */
+ #define S_M_MIXER_RESET 0x10 /* R W Mixer Mixer chip reset */
+ #define S_M_INTEGRATOR_ENABLE 0x40 /* R W Speaker Enable PC speaker integrator (FORCE RealSound) */
+ #define S_M_OPL3_DUAL_MONO 0x80 /* R W FM Set the OPL-3 to dual mono mode */
+
+#define PCM_CONTROL 0xF8A /* R W PCM PCM Control Register */
+ #define P_C_MIXER_CROSS_FIELD 0x0f
+ #define P_C_MIXER_CROSS_R_TO_R 0x01 /* R W Mixer Connect Right to Right */
+ #define P_C_MIXER_CROSS_L_TO_R 0x02 /* R W Mixer Connect Left to Right */
+ #define P_C_MIXER_CROSS_R_TO_L 0x04 /* R W Mixer Connect Right to Left */
+ #define P_C_MIXER_CROSS_L_TO_L 0x08 /* R W Mixer Connect Left to Left */
+ #define P_C_PCM_DAC_MODE 0x10 /* R W PCM Playback (DAC) mode */
+ #define P_C_PCM_ADC_MODE 0x00 /* R W PCM Record (ADC) mode */
+ #define P_C_PCM_MONO 0x20 /* R W PCM Mono mode */
+ #define P_C_PCM_STEREO 0x00 /* R W PCM Stereo mode */
+ #define P_C_PCM_ENABLE 0x40 /* R W PCM Enable PCM engine */
+ #define P_C_PCM_DMA_ENABLE 0x80 /* R W PCM Enable DRQ */
+
+#define SAMPLE_COUNTER_CONTROL 0x138B /* R W PCM Sample counter control register */
+ #define S_C_C_SQUARE_WAVE 0x04 /* R W PCM Square wave generator (use for sample rate) */
+ #define S_C_C_RATE 0x06 /* R W PCM Rate generator (use for sample buffer count) */
+ #define S_C_C_LSB_THEN_MSB 0x30 /* R W PCM Change all 16 bits, LSB first, then MSB */
+
+ /* MVD101 and SDK documentations have S_C_C_SAMPLE_RATE and S_C_C_SAMPLE_BUFFER transposed. Only one works :-) */
+ #define S_C_C_SAMPLE_RATE 0x00 /* R W PCM Select sample rate timer */
+ #define S_C_C_SAMPLE_BUFFER 0x40 /* R W PCM Select sample buffer counter */
+
+ #define S_C_C_PC_SPEAKER 0x80 /* R W PCM Select PC speaker counter */
+
+#define SAMPLE_RATE_TIMER 0x1388 /* W PCM Sample rate timer register (PCM wait interval) */
+#define SAMPLE_BUFFER_COUNTER 0x1389 /* R W PCM Sample buffer counter (DMA buffer size) */
+
+#define MIDI_CONTROL 0x178b /* R W MIDI Midi control register */
+ #define M_C_ENA_TSTAMP_IRQ 0x01 /* R W MIDI Enable Time Stamp Interrupts */
+ #define M_C_ENA_TME_COMP_IRQ 0x02 /* R W MIDI Enable time compare interrupts */
+ #define M_C_ENA_INPUT_IRQ 0x04 /* R W MIDI Enable input FIFO interrupts */
+ #define M_C_ENA_OUTPUT_IRQ 0x08 /* R W MIDI Enable output FIFO interrupts */
+ #define M_C_ENA_OUTPUT_HALF_IRQ 0x10 /* R W MIDI Enable output FIFO half full interrupts */
+ #define M_C_RESET_INPUT_FIFO 0x20 /* R W MIDI Reset input FIFO pointer */
+ #define M_C_RESET_OUTPUT_FIFO 0x40 /* R W MIDI Reset output FIFO pointer */
+ #define M_C_ENA_THRU_MODE 0x80 /* R W MIDI Echo input to output (THRU) */
+
+#define MIDI_STATUS 0x1B88 /* R W MIDI Midi (interrupt) status register */
+ #define M_S_TIMESTAMP 0x01 /* R W MIDI Midi time stamp interrupt occurred */
+ #define M_S_COMPARE 0x02 /* R W MIDI Midi compare time interrupt occurred */
+ #define M_S_INPUT_AVAIL 0x04 /* R W MIDI Midi input data available interrupt occurred */
+ #define M_S_OUTPUT_EMPTY 0x08 /* R W MIDI Midi output FIFO empty interrupt occurred */
+ #define M_S_OUTPUT_HALF_EMPTY 0x10 /* R W MIDI Midi output FIFO half empty interrupt occurred */
+ #define M_S_INPUT_OVERRUN 0x20 /* R W MIDI Midi input overrun error occurred */
+ #define M_S_OUTPUT_OVERRUN 0x40 /* R W MIDI Midi output overrun error occurred */
+ #define M_S_FRAMING_ERROR 0x80 /* R W MIDI Midi input framing error occurred */
+
+#define MIDI_FIFO_STATUS 0x1B89 /* R W MIDI Midi fifo status */
+#define MIDI_DATA 0x178A /* R W MIDI Midi data register */
+#define MIDI_INPUT_AVAILABLE 0x0f /* RW MIDI */
diff --git a/sys/i386/isa/sound/pas2_card.c b/sys/i386/isa/sound/pas2_card.c
new file mode 100644
index 0000000..cc99a9e
--- /dev/null
+++ b/sys/i386/isa/sound/pas2_card.c
@@ -0,0 +1,383 @@
+#define _PAS2_CARD_C_
+#define SND_SA_INTERRUPT
+/*
+ * sound/pas2_card.c
+ *
+ * Detection routine for the Pro Audio Spectrum cards.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS)
+
+#define DEFINE_TRANSLATIONS
+#include "pas.h"
+
+/*
+ * The Address Translation code is used to convert I/O register addresses to
+ * be relative to the given base -register
+ */
+
+int translat_code;
+static int pas_intr_mask = 0;
+static int pas_irq = 0;
+
+static char pas_model;
+static unsigned char board_rev_id;
+#define PAS_REVD_BOARD_ID 127
+static char *pas_model_names[] =
+{"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16", "Pro AudioSpectrum 16D"};
+
+/* pas_read() and pas_write() are equivalents of INB() and OUTB() */
+/* These routines perform the I/O address translation required */
+/* to support other than the default base address */
+
+unsigned char
+pas_read (int ioaddr)
+{
+ return INB (ioaddr ^ translat_code);
+}
+
+void
+pas_write (unsigned char data, int ioaddr)
+{
+ OUTB (data, ioaddr ^ translat_code);
+}
+
+/*
+ * The Revision D cards have a problem with their MVA508 interface. The
+ * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and
+ * MSBs out of the output byte and to do a 16-bit out to the mixer port -
+ * 1.
+ */
+
+void
+mix_write (unsigned char data, int ioaddr)
+{
+ if (board_rev_id >= PAS_REVD_BOARD_ID) {
+ outw ((ioaddr ^ translat_code) - 1, data | (data << 8));
+ outb (0, 0x80);
+ } else
+ OUTB (data, ioaddr ^ translat_code);
+}
+
+void
+pas2_msg (char *foo)
+{
+ printk (" PAS2: %s.\n", foo);
+}
+
+/******************* Begin of the Interrupt Handler ********************/
+
+void
+pasintr (int unused)
+{
+ int status;
+
+ status = pas_read (INTERRUPT_STATUS);
+ pas_write (status, INTERRUPT_STATUS); /* Clear interrupt */
+
+ if (status & I_S_PCM_SAMPLE_BUFFER_IRQ)
+ {
+#ifndef EXCLUDE_AUDIO
+ pas_pcm_interrupt (status, 1);
+#endif
+ status &= ~I_S_PCM_SAMPLE_BUFFER_IRQ;
+ }
+ if (status & I_S_MIDI_IRQ)
+ {
+#ifndef EXCLUDE_MIDI
+#ifdef EXCLUDE_PRO_MIDI
+ pas_midi_interrupt ();
+#endif
+#endif
+ status &= ~I_S_MIDI_IRQ;
+ }
+
+}
+
+int
+pas_set_intr (int mask)
+{
+ int err;
+
+ if (!mask)
+ return 0;
+
+ if (!pas_intr_mask)
+ {
+ if ((err = snd_set_irq_handler (pas_irq, pasintr)) < 0)
+ return err;
+ }
+ pas_intr_mask |= mask;
+
+ pas_write (pas_intr_mask, INTERRUPT_MASK);
+ return 0;
+}
+
+int
+pas_remove_intr (int mask)
+{
+ if (!mask)
+ return 0;
+
+ pas_intr_mask &= ~mask;
+ pas_write (pas_intr_mask, INTERRUPT_MASK);
+
+ if (!pas_intr_mask)
+ {
+ snd_release_irq (pas_irq);
+ }
+ return 0;
+}
+
+/******************* End of the Interrupt handler **********************/
+
+/******************* Begin of the Initialization Code ******************/
+
+int
+config_pas_hw (struct address_info *hw_config)
+{
+ char ok = 1;
+
+ pas_irq = hw_config->irq;
+
+ pas_write (0x00, INTERRUPT_MASK);
+
+ pas_write (0x36, SAMPLE_COUNTER_CONTROL); /* Local timer control
+ * register */
+
+ pas_write (0x36, SAMPLE_RATE_TIMER); /* Sample rate timer (16 bit) */
+ pas_write (0, SAMPLE_RATE_TIMER);
+
+ pas_write (0x74, SAMPLE_COUNTER_CONTROL); /* Local timer control
+ * register */
+
+ pas_write (0x74, SAMPLE_BUFFER_COUNTER); /* Sample count register (16
+ * bit) */
+ pas_write (0, SAMPLE_BUFFER_COUNTER);
+
+ pas_write (F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER | F_F_MIXER_UNMUTE | 1, FILTER_FREQUENCY);
+ pas_write (P_C_PCM_DMA_ENABLE | P_C_PCM_MONO | P_C_PCM_DAC_MODE | P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R, PCM_CONTROL);
+ pas_write (S_M_PCM_RESET | S_M_FM_RESET | S_M_SB_RESET | S_M_MIXER_RESET /* | S_M_OPL3_DUAL_MONO */ , SERIAL_MIXER);
+
+ pas_write (I_C_1_BOOT_RESET_ENABLE, IO_CONFIGURATION_1);
+
+ if (pas_irq < 0 || pas_irq > 15)
+ {
+ printk ("PAS2: Invalid IRQ %d", pas_irq);
+ ok = 0;
+ }
+ else
+ {
+ pas_write (I_C_3_PCM_IRQ_translate[pas_irq], IO_CONFIGURATION_3);
+ if (!I_C_3_PCM_IRQ_translate[pas_irq])
+ {
+ printk ("PAS2: Invalid IRQ %d", pas_irq);
+ ok = 0;
+ }
+ }
+
+ if (hw_config->dma < 0 || hw_config->dma > 7)
+ {
+ printk ("PAS2: Invalid DMA selection %d", hw_config->dma);
+ ok = 0;
+ }
+ else
+ {
+ pas_write (I_C_2_PCM_DMA_translate[hw_config->dma], IO_CONFIGURATION_2);
+ if (!I_C_2_PCM_DMA_translate[hw_config->dma])
+ {
+ printk ("PAS2: Invalid DMA selection %d", hw_config->dma);
+ ok = 0;
+ }
+ }
+
+ /*
+ * This fixes the timing problems of the PAS due to the Symphony chipset
+ * as per Media Vision. Only define this if your PAS doesn't work correctly.
+ */
+#ifdef SYMPHONY_PAS
+ OUTB (0x05, 0xa8);
+ OUTB (0x60, 0xa9);
+#endif
+
+#ifdef BROKEN_BUS_CLOCK
+ pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND | S_C_1_FM_EMULATE_CLOCK, SYSTEM_CONFIGURATION_1);
+#else
+ /* pas_write(S_C_1_PCS_ENABLE, SYSTEM_CONFIGURATION_1); */
+ pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND, SYSTEM_CONFIGURATION_1);
+#endif
+ pas_write (0x18, SYSTEM_CONFIGURATION_3); /* ??? */
+
+ pas_write (F_F_MIXER_UNMUTE | 0x01, FILTER_FREQUENCY); /* Sets mute off and
+ * selects filter rate
+ * of 17.897 kHz */
+
+ if (pas_model == PAS_16 || pas_model == PAS_16D)
+ pas_write (8, PRESCALE_DIVIDER);
+ else
+ pas_write (0, PRESCALE_DIVIDER);
+
+ mix_write (P_M_MV508_ADDRESS | 5, PARALLEL_MIXER);
+ mix_write (5, PARALLEL_MIXER);
+
+#if !defined(EXCLUDE_SB_EMULATION) || !defined(EXCLUDE_SB)
+
+ {
+ struct address_info *sb_config;
+
+ if ((sb_config = sound_getconf (SNDCARD_SB)))
+ {
+ unsigned char irq_dma;
+
+ /* Turn on Sound Blaster compatibility */
+ /* bit 1 = SB emulation */
+ /* bit 0 = MPU401 emulation (CDPC only :-( ) */
+ pas_write (0x02, COMPATIBILITY_ENABLE);
+
+ /* "Emulation address" */
+ pas_write ((sb_config->io_base >> 4) & 0x0f, EMULATION_ADDRESS);
+
+ if (!E_C_SB_DMA_translate[sb_config->dma])
+ printk ("\n\nPAS16 Warning: Invalid SB DMA %d\n\n",
+ sb_config->dma);
+
+ if (!E_C_SB_IRQ_translate[sb_config->irq])
+ printk ("\n\nPAS16 Warning: Invalid SB IRQ %d\n\n",
+ sb_config->irq);
+
+ irq_dma = E_C_SB_DMA_translate[sb_config->dma] |
+ E_C_SB_IRQ_translate[sb_config->irq];
+
+ pas_write (irq_dma, EMULATION_CONFIGURATION);
+ }
+ }
+#endif
+
+ if (!ok)
+ pas2_msg ("Driver not enabled");
+
+ return ok;
+}
+
+int
+detect_pas_hw (struct address_info *hw_config)
+{
+ unsigned char board_id, foo;
+
+ /*
+ * WARNING: Setting an option like W:1 or so that disables warm boot reset
+ * of the card will screw up this detect code something fierce. Adding code
+ * to handle this means possibly interfering with other cards on the bus if
+ * you have something on base port 0x388. SO be forewarned.
+ */
+
+ OUTB (0xBC, MASTER_DECODE); /* Talk to first board */
+ OUTB (hw_config->io_base >> 2, MASTER_DECODE); /* Set base address */
+ translat_code = PAS_DEFAULT_BASE ^ hw_config->io_base;
+ pas_write (1, WAIT_STATE); /* One wait-state */
+
+ board_id = pas_read (INTERRUPT_MASK);
+
+ if (board_id == 0xff)
+ return 0;
+
+ /*
+ * We probably have a PAS-series board, now check for a PAS2-series board
+ * by trying to change the board revision bits. PAS2-series hardware won't
+ * let you do this - the bits are read-only.
+ */
+
+ foo = board_id ^ 0xe0;
+
+ pas_write (foo, INTERRUPT_MASK);
+ foo = INB (INTERRUPT_MASK);
+ pas_write (board_id, INTERRUPT_MASK);
+
+ if (board_id != foo) /* Not a PAS2 */
+ return 0;
+
+ pas_model = O_M_1_to_card[pas_read (OPERATION_MODE_1) & 0x0f];
+
+ return pas_model;
+}
+
+long
+attach_pas_card (long mem_start, struct address_info *hw_config)
+{
+ pas_irq = hw_config->irq;
+
+ if (detect_pas_hw (hw_config))
+ {
+
+ board_rev_id = pas_read (BOARD_REV_ID);
+ if ((pas_model = O_M_1_to_card[pas_read (OPERATION_MODE_1) & 0x0f]))
+ {
+#ifdef __FreeBSD__
+ printk ("snd3: <%s rev %d>", pas_model_names[(int) pas_model], board_rev_id);
+#else
+ printk (" <%s rev %d>", pas_model_names[(int) pas_model], board_rev_id);
+#endif
+ }
+
+ if (config_pas_hw (hw_config))
+ {
+
+#ifndef EXCLUDE_AUDIO
+ mem_start = pas_pcm_init (mem_start, hw_config);
+#endif
+
+#if !defined(EXCLUDE_SB_EMULATION) && !defined(EXCLUDE_SB)
+
+ sb_dsp_disable_midi (); /* The SB emulation don't support
+ * midi */
+#endif
+
+#ifndef EXCLUDE_YM3812
+ enable_opl3_mode (0x388, 0x38a, 0);
+#endif
+
+#ifndef EXCLUDE_MIDI
+#ifdef EXCLUDE_PRO_MIDI
+ mem_start = pas_midi_init (mem_start);
+#endif
+#endif
+
+ pas_init_mixer ();
+ }
+ }
+
+ return mem_start;
+}
+
+int
+probe_pas (struct address_info *hw_config)
+{
+ return detect_pas_hw (hw_config);
+}
+
+#endif
diff --git a/sys/i386/isa/sound/pas2_midi.c b/sys/i386/isa/sound/pas2_midi.c
new file mode 100644
index 0000000..4a07b0b
--- /dev/null
+++ b/sys/i386/isa/sound/pas2_midi.c
@@ -0,0 +1,295 @@
+/*
+ * sound/pas2_midi.c
+ *
+ * The low level driver for the PAS Midi Interface.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "pas.h"
+
+#if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_MIDI) && defined(EXCLUDE_PRO_MIDI)
+
+static int midi_busy = 0, input_opened = 0;
+static int my_dev;
+static volatile int ofifo_bytes = 0;
+
+static unsigned char tmp_queue[256];
+static volatile int qlen;
+static volatile unsigned char qhead, qtail;
+
+static void (*midi_input_intr) (int dev, unsigned char data);
+
+static int
+pas_midi_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ int err;
+ unsigned long flags;
+ unsigned char ctrl;
+
+
+ if (midi_busy)
+ {
+ printk ("PAS2: Midi busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ /* Reset input and output FIFO pointers */
+ pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO,
+ MIDI_CONTROL);
+
+ DISABLE_INTR (flags);
+
+ if ((err = pas_set_intr (I_M_MIDI_IRQ_ENABLE)) < 0)
+ return err;
+
+ /* Enable input available and output FIFO empty interrupts */
+
+ ctrl = 0;
+ input_opened = 0;
+ midi_input_intr = input;
+
+ if (mode == OPEN_READ || mode == OPEN_READWRITE)
+ {
+ ctrl |= M_C_ENA_INPUT_IRQ;/* Enable input */
+ input_opened = 1;
+ }
+
+ if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+ {
+ ctrl |= M_C_ENA_OUTPUT_IRQ | /* Enable output */
+ M_C_ENA_OUTPUT_HALF_IRQ;
+ }
+
+ pas_write (ctrl,
+ MIDI_CONTROL);
+
+ /* Acknowledge any pending interrupts */
+
+ pas_write (0xff, MIDI_STATUS);
+ ofifo_bytes = 0;
+
+ RESTORE_INTR (flags);
+
+ midi_busy = 1;
+ qlen = qhead = qtail = 0;
+ return 0;
+}
+
+static void
+pas_midi_close (int dev)
+{
+
+ /* Reset FIFO pointers, disable intrs */
+ pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO, MIDI_CONTROL);
+
+ pas_remove_intr (I_M_MIDI_IRQ_ENABLE);
+ midi_busy = 0;
+}
+
+static int
+dump_to_midi (unsigned char midi_byte)
+{
+ int fifo_space, x;
+
+ fifo_space = ((x = pas_read (MIDI_FIFO_STATUS)) >> 4) & 0x0f;
+
+ if (fifo_space == 15 || (fifo_space < 2 && ofifo_bytes > 13)) /* Fifo full */
+ {
+ return 0; /* Upper layer will call again */
+ }
+
+ ofifo_bytes++;
+
+ pas_write (midi_byte, MIDI_DATA);
+
+ return 1;
+}
+
+static int
+pas_midi_out (int dev, unsigned char midi_byte)
+{
+
+ unsigned long flags;
+
+ /*
+ * Drain the local queue first
+ */
+
+ DISABLE_INTR (flags);
+
+ while (qlen && dump_to_midi (tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+
+ RESTORE_INTR (flags);
+
+ /*
+ * Output the byte if the local queue is empty.
+ */
+
+ if (!qlen)
+ if (dump_to_midi (midi_byte))
+ return 1; /* OK */
+
+ /*
+ * Put to the local queue
+ */
+
+ if (qlen >= 256)
+ return 0; /* Local queue full */
+
+ DISABLE_INTR (flags);
+
+ tmp_queue[qtail] = midi_byte;
+ qlen++;
+ qtail++;
+
+ RESTORE_INTR (flags);
+
+ return 1;
+}
+
+static int
+pas_midi_start_read (int dev)
+{
+ return 0;
+}
+
+static int
+pas_midi_end_read (int dev)
+{
+ return 0;
+}
+
+static int
+pas_midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+pas_midi_kick (int dev)
+{
+ ofifo_bytes = 0;
+}
+
+static int
+pas_buffer_status (int dev)
+{
+ return !qlen;
+}
+
+static struct midi_operations pas_midi_operations =
+{
+ {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS},
+ pas_midi_open,
+ pas_midi_close,
+ pas_midi_ioctl,
+ pas_midi_out,
+ pas_midi_start_read,
+ pas_midi_end_read,
+ pas_midi_kick,
+ NULL, /* command */
+ pas_buffer_status
+};
+
+long
+pas_midi_init (long mem_start)
+{
+ my_dev = num_midis;
+ midi_devs[num_midis++] = &pas_midi_operations;
+ return mem_start;
+}
+
+void
+pas_midi_interrupt (void)
+{
+ unsigned char stat;
+ int i, incount;
+ unsigned long flags;
+
+ stat = pas_read (MIDI_STATUS);
+
+ if (stat & M_S_INPUT_AVAIL) /* Input byte available */
+ {
+ incount = pas_read (MIDI_FIFO_STATUS) & 0x0f; /* Input FIFO count */
+ if (!incount)
+ incount = 16;
+
+ for (i = 0; i < incount; i++)
+ if (input_opened)
+ {
+ midi_input_intr (my_dev, pas_read (MIDI_DATA));
+ }
+ else
+ pas_read (MIDI_DATA); /* Flush */
+ }
+
+ if (stat & (M_S_OUTPUT_EMPTY | M_S_OUTPUT_HALF_EMPTY))
+ {
+ if (!(stat & M_S_OUTPUT_EMPTY))
+ {
+ ofifo_bytes = 8;
+ }
+ else
+ {
+ ofifo_bytes = 0;
+ }
+
+ DISABLE_INTR (flags);
+
+ while (qlen && dump_to_midi (tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+
+ RESTORE_INTR (flags);
+ }
+
+ if (stat & M_S_FRAMING_ERROR)
+ printk ("MIDI framing error\n");
+
+ if (stat & M_S_OUTPUT_OVERRUN)
+ {
+ printk ("MIDI output overrun %x,%x,%d \n", pas_read (MIDI_FIFO_STATUS), stat, ofifo_bytes);
+ ofifo_bytes = 100;
+ }
+
+ pas_write (stat, MIDI_STATUS);/* Acknowledge interrupts */
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/pas2_mixer.c b/sys/i386/isa/sound/pas2_mixer.c
new file mode 100644
index 0000000..b386877
--- /dev/null
+++ b/sys/i386/isa/sound/pas2_mixer.c
@@ -0,0 +1,492 @@
+#define _PAS2_MIXER_C_
+
+/*
+ * sound/pas2_mixer.c
+ *
+ * Mixer routines for the Pro Audio Spectrum cards.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS)
+
+#include "pas.h"
+
+#define TRACE(what) /* (what) */
+
+extern int translat_code;
+
+static int rec_devices = (SOUND_MASK_MIC); /* Default recording source */
+static int mode_control = 0;
+
+#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_ALTPCM)
+
+#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \
+ SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV | \
+ SOUND_MASK_MUTE | SOUND_MASK_ENHANCE | SOUND_MASK_LOUD)
+
+static unsigned short levels[SOUND_MIXER_NRDEVICES] =
+{
+ 0x3232, /* Master Volume */
+ 0x3232, /* Bass */
+ 0x3232, /* Treble */
+ 0x5050, /* FM */
+ 0x4b4b, /* PCM */
+ 0x3232, /* PC Speaker */
+ 0x4b4b, /* Ext Line */
+ 0x4b4b, /* Mic */
+ 0x4b4b, /* CD */
+ 0x6464, /* Recording monitor */
+ 0x4b4b, /* SB PCM */
+ 0x6464}; /* Recording level */
+
+static int
+mixer_output (int right_vol, int left_vol, int div, int bits,
+ int mixer /* Input or output mixer */ )
+{
+ int left = left_vol * div / 100;
+ int right = right_vol * div / 100;
+
+ if (bits & P_M_MV508_MIXER)
+ { /* Select input or output mixer */
+ left |= mixer;
+ right |= mixer;
+ }
+
+ if (bits == P_M_MV508_BASS || bits == P_M_MV508_TREBLE)
+ { /* Bass and trebble are mono devices */
+ mix_write (P_M_MV508_ADDRESS | bits, PARALLEL_MIXER);
+ mix_write (left, PARALLEL_MIXER);
+ right_vol = left_vol;
+ }
+ else
+ {
+ mix_write (P_M_MV508_ADDRESS | P_M_MV508_LEFT | bits, PARALLEL_MIXER);
+ mix_write (left, PARALLEL_MIXER);
+ mix_write (P_M_MV508_ADDRESS | P_M_MV508_RIGHT | bits, PARALLEL_MIXER);
+ mix_write (right, PARALLEL_MIXER);
+ }
+
+ return (left_vol | (right_vol << 8));
+}
+
+void
+set_mode (int new_mode)
+{
+ mix_write (P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER);
+ mix_write (new_mode, PARALLEL_MIXER);
+
+ mode_control = new_mode;
+}
+
+static int
+pas_mixer_set (int whichDev, unsigned int level)
+{
+ int left, right, devmask, changed, i, mixer = 0;
+
+ TRACE (printk ("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level));
+
+ left = level & 0x7f;
+ right = (level & 0x7f00) >> 8;
+
+ if (whichDev < SOUND_MIXER_NRDEVICES)
+ if ((1 << whichDev) & rec_devices)
+ mixer = P_M_MV508_INPUTMIX;
+ else
+ mixer = P_M_MV508_OUTPUTMIX;
+
+ switch (whichDev)
+ {
+ case SOUND_MIXER_VOLUME: /* Master volume (0-63) */
+ levels[whichDev] = mixer_output (right, left, 63, P_M_MV508_MASTER_A, 0);
+ break;
+
+ /*
+ * Note! Bass and Treble are mono devices. Will use just the left
+ * channel.
+ */
+ case SOUND_MIXER_BASS: /* Bass (0-12) */
+ levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_BASS, 0);
+ break;
+ case SOUND_MIXER_TREBLE: /* Treble (0-12) */
+ levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_TREBLE, 0);
+ break;
+
+ case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-31) */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_FM, mixer);
+ break;
+ case SOUND_MIXER_PCM: /* PAS PCM (0-31) */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_PCM, mixer);
+ break;
+ case SOUND_MIXER_ALTPCM: /* SB PCM (0-31) */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SB, mixer);
+ break;
+ case SOUND_MIXER_SPEAKER: /* PC speaker (0-31) */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SPEAKER, mixer);
+ break;
+ case SOUND_MIXER_LINE: /* External line (0-31) */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_LINE, mixer);
+ break;
+ case SOUND_MIXER_CD: /* CD (0-31) */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_CDROM, mixer);
+ break;
+ case SOUND_MIXER_MIC: /* External microphone (0-31) */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_MIC, mixer);
+ break;
+ case SOUND_MIXER_IMIX: /* Recording monitor (0-31) (Only available
+ * on the Output Mixer) */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_IMIXER,
+ P_M_MV508_OUTPUTMIX);
+ break;
+ case SOUND_MIXER_RECLEV: /* Recording level (0-15) */
+ levels[whichDev] = mixer_output (right, left, 15, P_M_MV508_MASTER_B, 0);
+ break;
+
+ case SOUND_MIXER_MUTE:
+ return 0;
+ break;
+
+ case SOUND_MIXER_ENHANCE:
+ i = 0;
+ level &= 0x7f;
+ if (level)
+ i = (level / 20) - 1;
+
+ mode_control &= ~P_M_MV508_ENHANCE_BITS;
+ mode_control |= P_M_MV508_ENHANCE_BITS;
+ set_mode (mode_control);
+
+ if (i)
+ i = (i + 1) * 20;
+ return i;
+ break;
+
+ case SOUND_MIXER_LOUD:
+ mode_control &= ~P_M_MV508_LOUDNESS;
+ if (level)
+ mode_control |= P_M_MV508_LOUDNESS;
+ set_mode (mode_control);
+ return !!level; /* 0 or 1 */
+ break;
+
+ case SOUND_MIXER_RECSRC:
+ devmask = level & POSSIBLE_RECORDING_DEVICES;
+
+ changed = devmask ^ rec_devices;
+ rec_devices = devmask;
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (changed & (1 << i))
+ {
+ pas_mixer_set (i, levels[i]);
+ }
+ return rec_devices;
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return (levels[whichDev]);
+}
+
+/*****/
+
+static int
+mixer_set_levels (struct sb_mixer_levels *user_l)
+{
+#define cmix(v) ((((v.r*100+7)/15)<<8)| ((v.l*100+7)/15))
+
+ struct sb_mixer_levels l;
+
+ IOCTL_FROM_USER ((char *) &l, (char *) user_l, 0, sizeof (l));
+
+ if (l.master.l & ~0xF || l.master.r & ~0xF
+ || l.line.l & ~0xF || l.line.r & ~0xF
+ || l.voc.l & ~0xF || l.voc.r & ~0xF
+ || l.fm.l & ~0xF || l.fm.r & ~0xF
+ || l.cd.l & ~0xF || l.cd.r & ~0xF
+ || l.mic & ~0x7)
+ return (RET_ERROR (EINVAL));
+
+ pas_mixer_set (SOUND_MIXER_VOLUME, cmix (l.master));
+ pas_mixer_set (SOUND_MIXER_LINE, cmix (l.line));
+ pas_mixer_set (SOUND_MIXER_PCM, cmix (l.voc));
+ pas_mixer_set (SOUND_MIXER_ALTPCM, cmix (l.voc));
+ pas_mixer_set (SOUND_MIXER_SYNTH, cmix (l.fm));
+ pas_mixer_set (SOUND_MIXER_CD, cmix (l.cd));
+ pas_mixer_set (SOUND_MIXER_MIC, ((l.mic * 100 + 3) / 7) | (((l.mic * 100 + 3) / 7) << 8));
+ return (0);
+}
+
+/*
+ * This sets aspects of the Mixer that are not volume levels. (Recording
+ * source, filter level, I/O filtering, and stereo.)
+ */
+static int
+mixer_set_params (struct sb_mixer_params *user_p)
+{
+ struct sb_mixer_params p;
+ S_BYTE val;
+ int src;
+ unsigned long flags;
+
+ IOCTL_FROM_USER ((char *) &p, (char *) user_p, 0, sizeof (p));
+
+ if (p.record_source != SRC_MIC
+ && p.record_source != SRC_CD
+ && p.record_source != SRC_LINE)
+ return (RET_ERROR (EINVAL));
+
+ /*
+ * I'm not sure if this is The Right Thing. Should stereo be entirely
+ * under control of DSP? I like being able to toggle it while a sound is
+ * playing, so I do this... because I can.
+ */
+
+ DISABLE_INTR (flags);
+
+ val = (pas_read (PCM_CONTROL) & ~P_C_MIXER_CROSS_FIELD) | P_C_MIXER_CROSS_R_TO_R | P_C_MIXER_CROSS_L_TO_L;
+ if (!p.dsp_stereo)
+ val |= (P_C_MIXER_CROSS_R_TO_L | P_C_MIXER_CROSS_L_TO_R); /* Mono */
+ pas_write (val, PCM_CONTROL);
+
+ RESTORE_INTR (flags);
+
+ switch (p.record_source)
+ {
+ case SRC_CD:
+ src = SOUND_MASK_CD;
+ break;
+
+ case SRC_LINE:
+ src = SOUND_MASK_LINE;
+ break;
+
+ default:
+ src = SOUND_MASK_MIC;
+ break;
+ }
+
+ pas_mixer_set (SOUND_MIXER_RECSRC, src);
+
+ /*
+ * setmixer (OUT_FILTER, ((dsp_stereo ? STEREO_DAC : MONO_DAC) |
+ * (p.filter_output ? FILT_ON : FILT_OFF)));
+ */
+ return (0);
+}
+
+static int
+getmixer (int dev, int chn)
+{
+ if (chn == P_M_MV508_RIGHT)
+ {
+ return (levels[dev] >> 8) & 0x7f;
+ }
+ else
+ {
+ return levels[dev] & 0x7f;
+ }
+}
+
+/* Read the current mixer level settings into the user's struct. */
+static int
+mixer_get_levels (struct sb_mixer_levels *user_l)
+{
+
+ struct sb_mixer_levels l;
+
+ l.master.r = ((((levels[SOUND_MIXER_VOLUME] >> 8) & 0x7f) * 15) + 50) / 100; /* Master */
+ l.master.l = (((levels[SOUND_MIXER_VOLUME] & 0x7f) * 15) + 50) / 100; /* Master */
+
+ l.line.r = ((getmixer (SOUND_MIXER_LINE, P_M_MV508_RIGHT) * 15) + 50) / 100; /* Line */
+ l.line.l = ((getmixer (SOUND_MIXER_LINE, P_M_MV508_LEFT) * 15) + 50) / 100;
+
+ l.voc.r = ((getmixer (SOUND_MIXER_PCM, P_M_MV508_RIGHT) * 15) + 50) / 100; /* DAC */
+ l.voc.l = ((getmixer (SOUND_MIXER_PCM, P_M_MV508_LEFT) * 15) + 50) / 100;
+
+ l.fm.r = ((getmixer (SOUND_MIXER_SYNTH, P_M_MV508_RIGHT) * 15) + 50) / 100; /* FM */
+ l.fm.l = ((getmixer (SOUND_MIXER_SYNTH, P_M_MV508_LEFT) * 15) + 50) / 100;
+
+ l.cd.r = ((getmixer (SOUND_MIXER_CD, P_M_MV508_RIGHT) * 15) + 50) / 100; /* CD */
+ l.cd.l = ((getmixer (SOUND_MIXER_CD, P_M_MV508_LEFT) * 15) + 50) / 100;
+
+ l.mic = ((getmixer (SOUND_MIXER_MIC, P_M_MV508_LEFT) * 7) + 50) / 100; /* Microphone */
+
+ IOCTL_TO_USER ((char *) user_l, 0, (char *) &l, sizeof (l));
+ return (0);
+}
+
+/* Read the current mixer parameters into the user's struct. */
+static int
+mixer_get_params (struct sb_mixer_params *user_params)
+{
+ S_BYTE val;
+ struct sb_mixer_params params;
+
+ switch (rec_devices)
+ {
+ case SOUND_MASK_CD:
+ params.record_source = SRC_CD;
+ break;
+
+ case SOUND_MASK_LINE:
+ params.record_source = SRC_LINE;
+ break;
+
+ case SOUND_MASK_MIC:
+ params.record_source = SRC_MIC;
+ break;
+
+ default:
+ params.record_source = SRC_MIC;
+ pas_mixer_set (SOUND_MIXER_RECSRC, SOUND_MASK_MIC); /* Adjust */
+ }
+
+ params.hifreq_filter = OFF;
+ params.filter_input = OFF;
+ params.filter_output = OFF;
+
+ val = INB (PCM_CONTROL);
+ params.dsp_stereo = ((val & P_C_MIXER_CROSS_FIELD) == (P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R));
+
+ IOCTL_TO_USER ((char *) user_params, 0, (char *) &params, sizeof (params));
+ return (0);
+}
+
+/*****/
+
+static void
+pas_mixer_reset (void)
+{
+ int foo;
+
+ TRACE (printk ("pas2_mixer.c: void pas_mixer_reset(void)\n"));
+
+ for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
+ pas_mixer_set (foo, levels[foo]);
+
+ set_mode (P_M_MV508_LOUDNESS | P_M_MV508_ENHANCE_40);
+}
+
+int
+pas_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
+{
+ TRACE (printk ("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
+
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ if (cmd & IOC_IN)
+ return IOCTL_OUT (arg, pas_mixer_set (cmd & 0xff, IOCTL_IN (arg)));
+ else
+ { /* Read parameters */
+
+ switch (cmd & 0xff)
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return IOCTL_OUT (arg, rec_devices);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE));
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES);
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return IOCTL_OUT (arg, POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return IOCTL_OUT (arg, 0); /* No special capabilities */
+ break;
+
+ case SOUND_MIXER_MUTE:
+ return IOCTL_OUT (arg, 0); /* No mute yet */
+ break;
+
+ case SOUND_MIXER_ENHANCE:
+ if (!(mode_control & P_M_MV508_ENHANCE_BITS))
+ return IOCTL_OUT (arg, 0);
+ return IOCTL_OUT (arg, ((mode_control & P_M_MV508_ENHANCE_BITS) + 1) * 20);
+ break;
+
+ case SOUND_MIXER_LOUD:
+ if (mode_control & P_M_MV508_LOUDNESS)
+ return IOCTL_OUT (arg, 1);
+ return IOCTL_OUT (arg, 0);
+ break;
+
+ default:
+ return IOCTL_OUT (arg, levels[cmd & 0xff]);
+ }
+ }
+ }
+ else
+ {
+ switch (cmd)
+ {
+ case MIXER_IOCTL_SET_LEVELS:
+ mixer_set_levels ((struct sb_mixer_levels *) arg);
+ return mixer_get_levels ((struct sb_mixer_levels *) arg);
+ case MIXER_IOCTL_SET_PARAMS:
+ mixer_set_params ((struct sb_mixer_params *) arg);
+ return mixer_get_params ((struct sb_mixer_params *) arg);
+ case MIXER_IOCTL_READ_LEVELS:
+ return mixer_get_levels ((struct sb_mixer_levels *) arg);
+ case MIXER_IOCTL_READ_PARAMS:
+ return mixer_get_params ((struct sb_mixer_params *) arg);
+ case MIXER_IOCTL_RESET:
+ pas_mixer_reset ();
+ return (0);
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ }
+ return RET_ERROR (EINVAL);
+}
+
+static struct mixer_operations pas_mixer_operations =
+{
+ pas_mixer_ioctl
+};
+
+int
+pas_init_mixer (void)
+{
+ pas_mixer_reset ();
+
+ mixer_devs[num_mixers++] = &pas_mixer_operations;
+ return 1;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/pas2_pcm.c b/sys/i386/isa/sound/pas2_pcm.c
new file mode 100644
index 0000000..ec571c5
--- /dev/null
+++ b/sys/i386/isa/sound/pas2_pcm.c
@@ -0,0 +1,429 @@
+#define _PAS2_PCM_C_
+/*
+ * sound/pas2_pcm.c
+ *
+ * The low level driver for the Pro Audio Spectrum ADC/DAC.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "pas.h"
+
+#if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_AUDIO)
+
+#define TRACE(WHAT) /* (WHAT) */
+
+#define PAS_PCM_INTRBITS (0x08)
+/* Sample buffer timer interrupt enable */
+
+#define PCM_NON 0
+#define PCM_DAC 1
+#define PCM_ADC 2
+
+static unsigned long pcm_speed = 0; /* sampling rate */
+static unsigned char pcm_channels = 1; /* channels/sample (1 or 2) */
+static unsigned char pcm_bits = 8; /* bits/sample (8 or 16) */
+static unsigned char pcm_filter = 0; /* filter FLAG */
+static unsigned char pcm_mode = PCM_NON;
+static unsigned long pcm_count = 0;
+static unsigned short pcm_bitsok = 8; /* mask of OK bits */
+static int my_devnum = 0;
+
+int
+pcm_set_speed (int arg)
+{
+ int foo, tmp;
+ unsigned long flags;
+
+ if (arg > 44100)
+ arg = 44100;
+ if (arg < 5000)
+ arg = 5000;
+
+ foo = 1193180 / arg;
+ arg = 1193180 / foo;
+
+ if (pcm_channels & 2)
+ foo = foo >> 1;
+
+ pcm_speed = arg;
+
+ tmp = pas_read (FILTER_FREQUENCY);
+
+ DISABLE_INTR (flags);
+
+ pas_write (tmp & ~(F_F_PCM_RATE_COUNTER | F_F_PCM_BUFFER_COUNTER), FILTER_FREQUENCY);
+ pas_write (S_C_C_SAMPLE_RATE | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
+ pas_write (foo & 0xff, SAMPLE_RATE_TIMER);
+ pas_write ((foo >> 8) & 0xff, SAMPLE_RATE_TIMER);
+ pas_write (tmp, FILTER_FREQUENCY);
+
+ RESTORE_INTR (flags);
+
+ return pcm_speed;
+}
+
+int
+pcm_set_channels (int arg)
+{
+
+ if ((arg != 1) && (arg != 2))
+ return pcm_channels;
+
+ if (arg != pcm_channels)
+ {
+ pas_write (pas_read (PCM_CONTROL) ^ P_C_PCM_MONO, PCM_CONTROL);
+
+ pcm_channels = arg;
+ pcm_set_speed (pcm_speed);/* The speed must be reinitialized */
+ }
+
+ return pcm_channels;
+}
+
+int
+pcm_set_bits (int arg)
+{
+ if ((arg & pcm_bitsok) != arg)
+ return pcm_bits;
+
+ if (arg != pcm_bits)
+ {
+ pas_write (pas_read (SYSTEM_CONFIGURATION_2) ^ S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2);
+
+ pcm_bits = arg;
+ }
+
+ return pcm_bits;
+}
+
+static int
+pas_pcm_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ TRACE (printk ("pas2_pcm.c: static int pas_pcm_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
+
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return pcm_set_speed (arg);
+ return IOCTL_OUT (arg, pcm_set_speed (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return pcm_speed;
+ return IOCTL_OUT (arg, pcm_speed);
+ break;
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return pcm_set_channels (arg + 1) - 1;
+ return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg) + 1) - 1);
+ break;
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return pcm_set_channels (arg);
+ return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return pcm_channels;
+ return IOCTL_OUT (arg, pcm_channels);
+ break;
+
+ case SNDCTL_DSP_SAMPLESIZE:
+ if (local)
+ return pcm_set_bits (arg);
+ return IOCTL_OUT (arg, pcm_set_bits (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return pcm_bits;
+ return IOCTL_OUT (arg, pcm_bits);
+
+ case SOUND_PCM_WRITE_FILTER: /* NOT YET IMPLEMENTED */
+ if (IOCTL_IN (arg) > 1)
+ return IOCTL_OUT (arg, RET_ERROR (EINVAL));
+ break;
+
+ pcm_filter = IOCTL_IN (arg);
+ case SOUND_PCM_READ_FILTER:
+ return IOCTL_OUT (arg, pcm_filter);
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static void
+pas_pcm_reset (int dev)
+{
+ TRACE (printk ("pas2_pcm.c: static void pas_pcm_reset(void)\n"));
+
+ pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL);
+}
+
+static int
+pas_pcm_open (int dev, int mode)
+{
+ int err;
+
+ TRACE (printk ("pas2_pcm.c: static int pas_pcm_open(int mode = %X)\n", mode));
+
+ if ((err = pas_set_intr (PAS_PCM_INTRBITS)) < 0)
+ return err;
+
+ if (!DMAbuf_open_dma (dev))
+ {
+ pas_remove_intr (PAS_PCM_INTRBITS);
+ return RET_ERROR (EBUSY);
+ }
+
+ pcm_count = 0;
+
+ return 0;
+}
+
+static void
+pas_pcm_close (int dev)
+{
+ unsigned long flags;
+
+ TRACE (printk ("pas2_pcm.c: static void pas_pcm_close(void)\n"));
+
+ DISABLE_INTR (flags);
+
+ pas_pcm_reset (dev);
+ DMAbuf_close_dma (dev);
+ pas_remove_intr (PAS_PCM_INTRBITS);
+ pcm_mode = PCM_NON;
+
+ RESTORE_INTR (flags);
+}
+
+static void
+pas_pcm_output_block (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
+{
+ unsigned long flags, cnt;
+
+ TRACE (printk ("pas2_pcm.c: static void pas_pcm_output_block(char *buf = %P, int count = %X)\n", buf, count));
+
+ cnt = count;
+ if (sound_dsp_dmachan[dev] > 3)
+ cnt >>= 1;
+
+ if (sound_dma_automode[dev] &&
+ intrflag &&
+ cnt == pcm_count)
+ return; /* Auto mode on. No need to react */
+
+ DISABLE_INTR (flags);
+
+ pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE,
+ PCM_CONTROL);
+
+ if (restart_dma)
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+
+ if (sound_dsp_dmachan[dev] > 3)
+ count >>= 1;
+
+ if (count != pcm_count)
+ {
+ pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
+ pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
+ pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER);
+ pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER);
+ pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
+
+ pcm_count = count;
+ }
+ pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
+ pas_write (pas_read (PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL);
+
+ pcm_mode = PCM_DAC;
+
+ RESTORE_INTR (flags);
+}
+
+static void
+pas_pcm_start_input (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
+{
+ unsigned long flags;
+ int cnt;
+
+ TRACE (printk ("pas2_pcm.c: static void pas_pcm_start_input(char *buf = %P, int count = %X)\n", buf, count));
+
+ cnt = count;
+ if (sound_dsp_dmachan[dev] > 3)
+ cnt >>= 1;
+
+ if (sound_dma_automode[my_devnum] &&
+ intrflag &&
+ cnt == pcm_count)
+ return; /* Auto mode on. No need to react */
+
+ DISABLE_INTR (flags);
+
+ if (restart_dma)
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+
+ if (sound_dsp_dmachan[dev] > 3)
+ count >>= 1;
+
+ if (count != pcm_count)
+ {
+ pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
+ pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
+ pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER);
+ pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER);
+ pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
+
+ pcm_count = count;
+ }
+ pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
+ pas_write ((pas_read (PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL);
+
+ pcm_mode = PCM_ADC;
+
+ RESTORE_INTR (flags);
+}
+
+static int
+pas_pcm_prepare_for_input (int dev, int bsize, int bcount)
+{
+ return 0;
+}
+static int
+pas_pcm_prepare_for_output (int dev, int bsize, int bcount)
+{
+ return 0;
+}
+
+static struct audio_operations pas_pcm_operations =
+{
+ "Pro Audio Spectrum",
+ NOTHING_SPECIAL,
+ pas_pcm_open,
+ pas_pcm_close,
+ pas_pcm_output_block,
+ pas_pcm_start_input,
+ pas_pcm_ioctl,
+ pas_pcm_prepare_for_input,
+ pas_pcm_prepare_for_output,
+ pas_pcm_reset,
+ pas_pcm_reset, /* halt_xfer */
+ NULL, /* has_output_drained */
+ NULL /* copy_from_user */
+};
+
+long
+pas_pcm_init (long mem_start, struct address_info *hw_config)
+{
+ TRACE (printk ("pas2_pcm.c: long pas_pcm_init(long mem_start = %X)\n", mem_start));
+
+ pcm_bitsok = 8;
+ if (pas_read (OPERATION_MODE_1) & O_M_1_PCM_TYPE)
+ pcm_bitsok |= 16;
+
+ pcm_set_speed (DSP_DEFAULT_SPEED);
+
+ if (num_dspdevs < MAX_DSP_DEV)
+ {
+ dsp_devs[my_devnum = num_dspdevs++] = &pas_pcm_operations;
+ sound_dsp_dmachan[my_devnum] = hw_config->dma;
+#ifndef NO_AUTODMA
+ if (hw_config->dma > 3)
+ {
+ sound_buffcounts[my_devnum] = 1;
+ sound_buffsizes[my_devnum] = 2 * 65536;
+ sound_dma_automode[my_devnum] = 1;
+ }
+ else
+ {
+ sound_buffcounts[my_devnum] = 1;
+ sound_buffsizes[my_devnum] = DSP_BUFFSIZE;
+ sound_dma_automode[my_devnum] = 1;
+ }
+#else
+ sound_buffcounts[my_devnum] = DSP_BUFFCOUNT;
+ sound_buffsizes[my_devnum] = DSP_BUFFSIZE;
+ sound_dma_automode[my_devnum] = 0;
+#endif
+ }
+ else
+ printk ("PAS2: Too many PCM devices available\n");
+
+ return mem_start;
+}
+
+void
+pas_pcm_interrupt (unsigned char status, int cause)
+{
+ if (cause == 1) /* PCM buffer done */
+ {
+ /*
+ * Halt the PCM first. Otherwise we don't have time to start a new
+ * block before the PCM chip proceeds to the next sample
+ */
+
+ if (!sound_dma_automode[my_devnum])
+ {
+ pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE,
+ PCM_CONTROL);
+ }
+
+ switch (pcm_mode)
+ {
+
+ case PCM_DAC:
+ DMAbuf_outputintr (my_devnum, 1);
+ break;
+
+ case PCM_ADC:
+ DMAbuf_inputintr (my_devnum);
+ break;
+
+ default:
+ printk ("PAS: Unexpected PCM interrupt\n");
+ }
+ }
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/patmgr.c b/sys/i386/isa/sound/patmgr.c
new file mode 100644
index 0000000..042d42d
--- /dev/null
+++ b/sys/i386/isa/sound/patmgr.c
@@ -0,0 +1,262 @@
+/*
+ * sound/patmgr.c
+ *
+ * The patch maneger interface for the /dev/sequencer
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#define PATMGR_C
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SEQUENCER)
+
+DEFINE_WAIT_QUEUES (server_procs[MAX_SYNTH_DEV],
+ server_wait_flag[MAX_SYNTH_DEV]);
+
+static struct patmgr_info *mbox[MAX_SYNTH_DEV] =
+{NULL};
+static volatile int msg_direction[MAX_SYNTH_DEV] =
+{0};
+
+static int pmgr_opened[MAX_SYNTH_DEV] =
+{0};
+
+#define A_TO_S 1
+#define S_TO_A 2
+
+DEFINE_WAIT_QUEUE (appl_proc, appl_wait_flag);
+
+int
+pmgr_open (int dev)
+{
+ if (dev < 0 || dev >= num_synths)
+ return RET_ERROR (ENXIO);
+
+ if (pmgr_opened[dev])
+ return RET_ERROR (EBUSY);
+ pmgr_opened[dev] = 1;
+
+ RESET_WAIT_QUEUE (server_procs[dev], server_wait_flag[dev]);
+
+ return 0;
+}
+
+void
+pmgr_release (int dev)
+{
+
+ if (mbox[dev]) /* Killed in action. Inform the client */
+ {
+
+ mbox[dev]->key = PM_ERROR;
+ mbox[dev]->parm1 = RET_ERROR (EIO);
+
+ if (SOMEONE_WAITING (appl_proc, appl_wait_flag))
+ WAKE_UP (appl_proc, appl_wait_flag);
+ }
+
+ pmgr_opened[dev] = 0;
+}
+
+int
+pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ unsigned long flags;
+ int ok = 0;
+
+ if (count != sizeof (struct patmgr_info))
+ {
+ printk ("PATMGR%d: Invalid read count\n", dev);
+ return RET_ERROR (EIO);
+ }
+
+ while (!ok && !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev]))
+ {
+ DISABLE_INTR (flags);
+
+ while (!(mbox[dev] && msg_direction[dev] == A_TO_S) &&
+ !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev]))
+ {
+ DO_SLEEP (server_procs[dev], server_wait_flag[dev], 0);
+ }
+
+ if (mbox[dev] && msg_direction[dev] == A_TO_S)
+ {
+ COPY_TO_USER (buf, 0, (char *) mbox[dev], count);
+ msg_direction[dev] = 0;
+ ok = 1;
+ }
+
+ RESTORE_INTR (flags);
+
+ }
+
+ if (!ok)
+ return RET_ERROR (EINTR);
+ return count;
+}
+
+int
+pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ unsigned long flags;
+
+ if (count < 4)
+ {
+ printk ("PATMGR%d: Write count < 4\n", dev);
+ return RET_ERROR (EIO);
+ }
+
+ COPY_FROM_USER (mbox[dev], buf, 0, 4);
+
+ if (*(unsigned char *) mbox[dev] == SEQ_FULLSIZE)
+ {
+ int tmp_dev;
+
+ tmp_dev = ((unsigned short *) mbox[dev])[2];
+ if (tmp_dev != dev)
+ return RET_ERROR (ENXIO);
+
+ return synth_devs[dev]->load_patch (dev, *(unsigned short *) mbox[dev],
+ buf, 4, count, 1);
+ }
+
+ if (count != sizeof (struct patmgr_info))
+ {
+ printk ("PATMGR%d: Invalid write count\n", dev);
+ return RET_ERROR (EIO);
+ }
+
+ /*
+ * If everything went OK, there should be a preallocated buffer in the
+ * mailbox and a client waiting.
+ */
+
+ DISABLE_INTR (flags);
+
+ if (mbox[dev] && !msg_direction[dev])
+ {
+ COPY_FROM_USER (&((char *) mbox[dev])[4], buf, 4, count - 4);
+ msg_direction[dev] = S_TO_A;
+
+ if (SOMEONE_WAITING (appl_proc, appl_wait_flag))
+ {
+ WAKE_UP (appl_proc, appl_wait_flag);
+ }
+ }
+
+ RESTORE_INTR (flags);
+
+ return count;
+}
+
+int
+pmgr_access (int dev, struct patmgr_info *rec)
+{
+ unsigned long flags;
+ int err = 0;
+
+ DISABLE_INTR (flags);
+
+ if (mbox[dev])
+ printk (" PATMGR: Server %d mbox full. Why?\n", dev);
+ else
+ {
+ rec->key = PM_K_COMMAND;
+ mbox[dev] = rec;
+ msg_direction[dev] = A_TO_S;
+
+ if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev]))
+ {
+ WAKE_UP (server_procs[dev], server_wait_flag[dev]);
+ }
+
+ DO_SLEEP (appl_proc, appl_wait_flag, 0);
+
+ if (msg_direction[dev] != S_TO_A)
+ {
+ rec->key = PM_ERROR;
+ rec->parm1 = RET_ERROR (EIO);
+ }
+ else if (rec->key == PM_ERROR)
+ {
+ err = rec->parm1;
+ if (err > 0)
+ err = -err;
+ }
+
+ mbox[dev] = NULL;
+ msg_direction[dev] = 0;
+ }
+
+ RESTORE_INTR (flags);
+
+ return err;
+}
+
+int
+pmgr_inform (int dev, int event, unsigned long p1, unsigned long p2,
+ unsigned long p3, unsigned long p4)
+{
+ unsigned long flags;
+ int err = 0;
+
+ if (!pmgr_opened[dev])
+ return 0;
+
+ DISABLE_INTR (flags);
+
+ if (mbox[dev])
+ printk (" PATMGR: Server %d mbox full. Why?\n", dev);
+ else
+ {
+ mbox[dev] =
+ (struct patmgr_info *) KERNEL_MALLOC (sizeof (struct patmgr_info));
+
+ mbox[dev]->key = PM_K_EVENT;
+ mbox[dev]->command = event;
+ mbox[dev]->parm1 = p1;
+ mbox[dev]->parm2 = p2;
+ mbox[dev]->parm3 = p3;
+ msg_direction[dev] = A_TO_S;
+
+ if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev]))
+ {
+ WAKE_UP (server_procs[dev], server_wait_flag[dev]);
+ }
+
+ DO_SLEEP (appl_proc, appl_wait_flag, 0);
+ if (mbox[dev])
+ KERNEL_FREE (mbox[dev]);
+ mbox[dev] = NULL;
+ msg_direction[dev] = 0;
+ }
+
+ RESTORE_INTR (flags);
+
+ return err;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/pro_midi.c b/sys/i386/isa/sound/pro_midi.c
new file mode 100644
index 0000000..b9ffa26
--- /dev/null
+++ b/sys/i386/isa/sound/pro_midi.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright by UWM - comments to soft-eng@cs.uwm.edu
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#define ALL_EXTERNAL_TO_ME
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "pas.h"
+#define ESUCCESS 0
+
+#if !defined(EXCLUDE_PRO_MIDI) && !defined(EXCLUDE_CHIP_MIDI)
+
+
+/** Structure for handling operations **/
+
+
+static struct generic_midi_operations pro_midi_operations =
+{
+
+ {"Pro_Audio_Spectrum 16 MV101", 0},
+ pro_midi_open,
+ pro_midi_close,
+ pro_midi_write,
+ pro_midi_read
+};
+
+/*
+ * Note! Note! Note! Follow the same model for any other attach function you
+ * may write
+ */
+
+long
+pro_midi_attach (long mem_start)
+{
+ pro_midi_dev = num_generic_midis;
+ generic_midi_devs[num_generic_midis++] = &pro_midi_operations;
+ return mem_start;
+}
+
+int
+pro_midi_open (int dev, int mode)
+{
+
+ int intr_mask, s;
+
+
+ s = splhigh ();
+
+
+ /* Reset the input and output FIFO pointers */
+
+
+ outb (MIDI_CONTROL, M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO);
+
+ /* Get the interrupt status */
+
+ intr_mask = inb (INTERRUPT_MASK);
+
+
+ /* Enable MIDI IRQ */
+
+ intr_mask |= I_M_MIDI_IRQ_ENABLE;
+ outb (INTERRUPT_MASK, intr_mask);
+
+
+ /* Enable READ/WRITE on MIDI port. This part is quite unsure though */
+
+ outb (MIDI_CONTROL, M_C_ENA_OUTPUT_IRQ | M_C_ENA_INPUT_IRQ);
+
+ /* Acknowledge pending interrupts */
+
+ outb (MIDI_STATUS, 0xff);
+
+
+ splx (s);
+
+ return (ESUCCESS);
+
+
+}
+
+
+void
+pro_midi_close (int dev)
+{
+
+ int intr_mask;
+
+ /* Clean up */
+
+ outb (MIDI_CONTROL, M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO);
+ intr_mask = inb (INTERRUPT_MASK);
+ intr_mask &= ~I_M_MIDI_IRQ_ENABLE;
+ outb (INTERRUPT_MASK, intr_mask);
+
+ return;
+}
+
+int
+pro_midi_write (int dev, struct uio *uio)
+{
+
+ int s;
+ unsigned char data;
+
+ /* printf("midi: Going to do write routine..\n"); */
+ while (uio->uio_resid)
+ {
+
+ if (uiomove (&data, 1, uio))
+ return (ENOTTY);
+
+ s = splhigh ();
+
+ DELAY (30);
+ outb (MIDI_DATA, data);
+ DELAY (70); /* Ze best pause.. find a better one if you
+ * can :) */
+ splx (s);
+ }
+
+ return (ESUCCESS);
+
+}
+
+
+int
+pro_midi_read (int dev, struct uio *uio)
+{
+
+ int s;
+ unsigned char data;
+
+ s = splhigh ();
+
+ /* For each uio_iov[] entry .... */
+
+ while (uio->uio_resid)
+ {
+
+ if (((inb (MIDI_STATUS) & M_S_INPUT_AVAIL) == 0) &&
+ ((inb (MIDI_FIFO_STATUS) & MIDI_INPUT_AVAILABLE) == 0))
+
+ data = 0xfe;
+ else
+ data = inb (MIDI_DATA);
+
+ if (uiomove (&data, 1, uio))
+ {
+
+ printf ("midi: Bad copyout()!\n");
+ return (ENOTTY);
+
+ }
+
+ }
+ splx (s);
+ return (ESUCCESS);
+
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/sb.h b/sys/i386/isa/sound/sb.h
new file mode 100644
index 0000000..bb8ae12
--- /dev/null
+++ b/sys/i386/isa/sound/sb.h
@@ -0,0 +1,28 @@
+#define DSP_RESET (sbc_base + 0x6)
+#define DSP_READ (sbc_base + 0xA)
+#define DSP_WRITE (sbc_base + 0xC)
+#define DSP_COMMAND (sbc_base + 0xC)
+#define DSP_STATUS (sbc_base + 0xC)
+#define DSP_DATA_AVAIL (sbc_base + 0xE)
+#define DSP_DATA_AVL16 (sbc_base + 0xF)
+#define MIXER_ADDR (sbc_base + 0x4)
+#define MIXER_DATA (sbc_base + 0x5)
+#define OPL3_LEFT (sbc_base + 0x0)
+#define OPL3_RIGHT (sbc_base + 0x2)
+#define OPL3_BOTH (sbc_base + 0x8)
+/* DSP Commands */
+
+#define DSP_CMD_SPKON 0xD1
+#define DSP_CMD_SPKOFF 0xD3
+#define DSP_CMD_DMAON 0xD0
+#define DSP_CMD_DMAOFF 0xD4
+
+#define IMODE_NONE 0
+#define IMODE_OUTPUT 1
+#define IMODE_INPUT 2
+#define IMODE_INIT 3
+#define IMODE_MIDI 4
+
+#define NORMAL_MIDI 0
+#define UART_MIDI 1
+
diff --git a/sys/i386/isa/sound/sb16_dsp.c b/sys/i386/isa/sound/sb16_dsp.c
new file mode 100644
index 0000000..b545f8c
--- /dev/null
+++ b/sys/i386/isa/sound/sb16_dsp.c
@@ -0,0 +1,627 @@
+/*
+ * sound/sb16_dsp.c
+ *
+ * The low level driver for the SoundBlaster DSP chip.
+ *
+ * (C) 1993 J. Schubert (jsb@sth.ruhr-uni-bochum.de)
+ *
+ * based on SB-driver by (C) Hannu Savolainen
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#define DEB(x)
+#define DEB1(x)
+/*
+ #define DEB_DMARES
+ */
+#include "sound_config.h"
+#include "sb.h"
+#include "sb_mixer.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_AUDIO) && !defined(EXCLUDE_SBPRO)
+
+extern int sbc_base, sbc_minor, sbc_major;
+
+static int sb16_dsp_ok = 0;/* Set to 1 after successful initialization */
+static int dsp_16bit = 0;
+static int dsp_stereo = 0;
+static int dsp_current_speed = 8000; /*DSP_DEFAULT_SPEED; */
+static int dsp_busy = 0;
+static int dma16, dma8;
+static unsigned long dsp_count = 0;
+
+static int irq_mode = IMODE_NONE; /* IMODE_INPUT, IMODE_OUTPUT or
+
+ IMODE_NONE */
+static int my_dev = 0;
+
+static volatile int intr_active = 0;
+
+static int sb16_dsp_open (int dev, int mode);
+static void sb16_dsp_close (int dev);
+static void sb16_dsp_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
+static void sb16_dsp_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
+static int sb16_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local);
+static int sb16_dsp_prepare_for_input (int dev, int bsize, int bcount);
+static int sb16_dsp_prepare_for_output (int dev, int bsize, int bcount);
+static void sb16_dsp_reset (int dev);
+static void sb16_dsp_halt (int dev);
+static int dsp_set_speed (int);
+static int dsp_set_stereo (int);
+static void dsp_cleanup (void);
+int sb_reset_dsp (void);
+
+static struct audio_operations sb16_dsp_operations =
+{
+ "SoundBlaster 16",
+ NOTHING_SPECIAL,
+ sb16_dsp_open,
+ sb16_dsp_close,
+ sb16_dsp_output_block,
+ sb16_dsp_start_input,
+ sb16_dsp_ioctl,
+ sb16_dsp_prepare_for_input,
+ sb16_dsp_prepare_for_output,
+ sb16_dsp_reset,
+ sb16_dsp_halt,
+ NULL,
+ NULL
+};
+
+static int
+sb_dsp_command01 (unsigned char val)
+{
+ int i = 1 << 16;
+
+ while (--i & (!INB (DSP_STATUS) & 0x80));
+ if (!i)
+ printk ("SB16 sb_dsp_command01 Timeout\n");
+ return sb_dsp_command (val);
+}
+
+static int
+wait_data_avail (unsigned long t)
+{
+ int loopc = 5000000;
+
+ t += GET_TIME ();
+ do
+ {
+ if (INB (DSP_DATA_AVAIL) & 0x80)
+ return 1;
+ }
+ while (--loopc && GET_TIME () < t);
+ printk ("!data_avail l=%d\n", loopc);
+ return 0;
+}
+
+static int
+read_dsp (int t)
+{
+ if (!wait_data_avail ((unsigned long) t))
+ return -1;
+ else
+ return INB (DSP_READ);
+}
+
+static int
+dsp_ini2 (void)
+{
+#if 0
+ /* sb_setmixer(0x83, sb_getmixer(0x83) | 0x03); */
+ sb_dsp_command (0xe2);
+ sb_dsp_command (0x76); /* E0 ??? */
+ sb_dsp_command (0xe2);
+ sb_dsp_command (0x30); /* A0 ??? */
+ sb_dsp_command (0xe4);
+ sb_dsp_command (0xaa);
+ sb_dsp_command (0xe8);
+ if (read_dsp (100) != 0xaa)
+ printk ("Error dsp_ini2\n");
+#endif
+ return 0;
+}
+
+/*
+ static char *dsp_getmessage(unsigned char command,int maxn)
+ {
+ static char buff[100];
+ int n=0;
+
+ sb_dsp_command(command);
+ while(n<maxn && wait_data_avail(2L)) {
+ buff[++n]=INB(DSP_READ);
+ if(!buff[n])
+ break;
+ }
+ buff[0]=n;
+ return buff;
+ }
+
+ static void dsp_showmessage(unsigned char command,int len)
+ {
+ int n;
+ unsigned char *c;
+ c=dsp_getmessage(command,len);
+ printk("DSP C=%x l=%d,lr=%d b=",command,len,c[0]);
+ for(n=1;n<=c[0];n++)
+ if(c[n]>=' ' & c[n]<='z')
+ printk("%c",c[n]);
+ else
+ printk("|%x|",c[n]);
+ printk("\n");
+ }
+ */
+static int
+dsp_set_speed (int mode)
+{
+ DEB (printk ("dsp_set_speed(%d)\n", mode));
+ if (mode)
+ {
+ if (mode < 5000)
+ mode = 5000;
+ if (mode > 44100)
+ mode = 44100;
+ dsp_current_speed = mode;
+ }
+ return mode;
+}
+
+static int
+dsp_set_stereo (int mode)
+{
+ DEB (printk ("dsp_set_stereo(%d)\n", mode));
+
+ dsp_stereo = mode;
+
+ return mode;
+}
+
+static int
+dsp_set_bits (int arg)
+{
+ DEB (printk ("dsp_set_bits(%d)\n", arg));
+
+ if (arg)
+ switch (arg)
+ {
+ case 8:
+ dsp_16bit = 0;
+ break;
+ case 16:
+ dsp_16bit = 1;
+ break;
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ return dsp_16bit ? 16 : 8;
+}
+
+static int
+sb16_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return dsp_set_speed (arg);
+ return IOCTL_OUT (arg, dsp_set_speed (IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return dsp_current_speed;
+ return IOCTL_OUT (arg, dsp_current_speed);
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return dsp_set_stereo (arg);
+ return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg)));
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return dsp_set_stereo (arg - 1) + 1;
+ return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1);
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return dsp_stereo + 1;
+ return IOCTL_OUT (arg, dsp_stereo + 1);
+
+ case SNDCTL_DSP_SAMPLESIZE:
+ if (local)
+ return dsp_set_bits (arg);
+ return IOCTL_OUT (arg, dsp_set_bits (IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return dsp_16bit ? 16 : 8;
+ return IOCTL_OUT (arg, dsp_16bit ? 16 : 8);
+
+ case SOUND_PCM_WRITE_FILTER: /* NOT YET IMPLEMENTED */
+ if (IOCTL_IN (arg) > 1)
+ return IOCTL_OUT (arg, RET_ERROR (EINVAL));
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static int
+sb16_dsp_open (int dev, int mode)
+{
+ int retval;
+
+ DEB (printk ("sb16_dsp_open()\n"));
+ if (!sb16_dsp_ok)
+ {
+ printk ("SB16 Error: SoundBlaster board not installed\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if (intr_active)
+ return RET_ERROR (EBUSY);
+
+ retval = sb_get_irq ();
+ if (retval < 0)
+ return retval;
+
+ if (ALLOC_DMA_CHN (dma8))
+ {
+ printk ("SB16: Unable to grab DMA%d\n", dma8);
+ sb_free_irq ();
+ return RET_ERROR (EBUSY);
+ }
+
+ if (dma16 != dma8)
+ if (ALLOC_DMA_CHN (dma16))
+ {
+ printk ("SB16: Unable to grab DMA%d\n", dma16);
+ sb_free_irq ();
+ RELEASE_DMA_CHN (dma8);
+ return RET_ERROR (EBUSY);
+ }
+
+ dsp_ini2 ();
+
+ irq_mode = IMODE_NONE;
+ dsp_busy = 1;
+
+ return 0;
+}
+
+static void
+sb16_dsp_close (int dev)
+{
+ unsigned long flags;
+
+ DEB (printk ("sb16_dsp_close()\n"));
+ sb_dsp_command01 (0xd9);
+ sb_dsp_command01 (0xd5);
+
+ DISABLE_INTR (flags);
+ RELEASE_DMA_CHN (dma8);
+
+ if (dma16 != dma8)
+ RELEASE_DMA_CHN (dma16);
+ sb_free_irq ();
+ dsp_cleanup ();
+ dsp_busy = 0;
+ RESTORE_INTR (flags);
+}
+
+static void
+sb16_dsp_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+{
+ unsigned long flags, cnt;
+
+ cnt = count;
+ if (dsp_16bit)
+ cnt >>= 1;
+ cnt--;
+
+#ifdef DEB_DMARES
+ printk ("output_block: %x %d %d\n", buf, count, intrflag);
+ if (intrflag)
+ {
+ int pos, chan = sound_dsp_dmachan[dev];
+
+ DISABLE_INTR (flags);
+ clear_dma_ff (chan);
+ disable_dma (chan);
+ pos = get_dma_residue (chan);
+ enable_dma (chan);
+ RESTORE_INTR (flags);
+ printk ("dmapos=%d %x\n", pos, pos);
+ }
+#endif
+ if (sound_dma_automode[dev] &&
+ intrflag &&
+ cnt == dsp_count)
+ {
+ irq_mode = IMODE_OUTPUT;
+ intr_active = 1;
+ return; /* Auto mode on. No need to react */
+ }
+ DISABLE_INTR (flags);
+
+ if (dma_restart)
+ {
+ sb16_dsp_halt (dev);
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+ }
+ sb_dsp_command (0x41);
+ sb_dsp_command ((unsigned char) ((dsp_current_speed >> 8) & 0xff));
+ sb_dsp_command ((unsigned char) (dsp_current_speed & 0xff));
+ sb_dsp_command ((unsigned char) (dsp_16bit ? 0xb6 : 0xc6));
+ sb_dsp_command ((unsigned char) ((dsp_stereo ? 0x20 : 0) +
+ (dsp_16bit ? 0x10 : 0)));
+ sb_dsp_command01 ((unsigned char) (cnt & 0xff));
+ sb_dsp_command ((unsigned char) (cnt >> 8));
+ /* sb_dsp_command (0);
+ sb_dsp_command (0); */
+
+ RESTORE_INTR (flags);
+ dsp_count = cnt;
+ irq_mode = IMODE_OUTPUT;
+ intr_active = 1;
+}
+
+static void
+sb16_dsp_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+{
+ unsigned long flags, cnt;
+
+ cnt = count;
+ if (dsp_16bit)
+ cnt >>= 1;
+ cnt--;
+
+#ifdef DEB_DMARES
+ printk ("start_input: %x %d %d\n", buf, count, intrflag);
+ if (intrflag)
+ {
+ int pos, chan = sound_dsp_dmachan[dev];
+
+ DISABLE_INTR (flags);
+ clear_dma_ff (chan);
+ disable_dma (chan);
+ pos = get_dma_residue (chan);
+ enable_dma (chan);
+ RESTORE_INTR (flags);
+ printk ("dmapos=%d %x\n", pos, pos);
+ }
+#endif
+ if (sound_dma_automode[dev] &&
+ intrflag &&
+ cnt == dsp_count)
+ {
+ irq_mode = IMODE_INPUT;
+ intr_active = 1;
+ return; /* Auto mode on. No need to react */
+ }
+ DISABLE_INTR (flags);
+
+ if (dma_restart)
+ {
+ sb16_dsp_halt (dev);
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+ }
+
+ sb_dsp_command (0x42);
+ sb_dsp_command ((unsigned char) ((dsp_current_speed >> 8) & 0xff));
+ sb_dsp_command ((unsigned char) (dsp_current_speed & 0xff));
+ sb_dsp_command ((unsigned char) (dsp_16bit ? 0xbe : 0xce));
+ sb_dsp_command ((unsigned char) ((dsp_stereo ? 0x20 : 0) +
+ (dsp_16bit ? 0x10 : 0)));
+ sb_dsp_command01 ((unsigned char) (cnt & 0xff));
+ sb_dsp_command ((unsigned char) (cnt >> 8));
+
+ /* sb_dsp_command (0);
+ sb_dsp_command (0); */
+ RESTORE_INTR (flags);
+ dsp_count = cnt;
+ irq_mode = IMODE_INPUT;
+ intr_active = 1;
+}
+
+static int
+sb16_dsp_prepare_for_input (int dev, int bsize, int bcount)
+{
+ sound_dsp_dmachan[my_dev] = dsp_16bit ? dma16 : dma8;
+ dsp_count = 0;
+ dsp_cleanup ();
+ return 0;
+}
+
+static int
+sb16_dsp_prepare_for_output (int dev, int bsize, int bcount)
+{
+ sound_dsp_dmachan[my_dev] = dsp_16bit ? dma16 : dma8;
+ dsp_count = 0;
+ dsp_cleanup ();
+ return 0;
+}
+
+static void
+dsp_cleanup (void)
+{
+ irq_mode = IMODE_NONE;
+ intr_active = 0;
+}
+
+static void
+sb16_dsp_reset (int dev)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ sb_reset_dsp ();
+ dsp_cleanup ();
+
+ RESTORE_INTR (flags);
+}
+
+static void
+sb16_dsp_halt (int dev)
+{
+ if (dsp_16bit)
+ {
+ sb_dsp_command01 (0xd9);
+ sb_dsp_command01 (0xd5);
+ }
+ else
+ {
+ sb_dsp_command01 (0xda);
+ sb_dsp_command01 (0xd0);
+ }
+}
+
+static void
+set_irq_hw (int level)
+{
+ int ival;
+
+ switch (level)
+ {
+ case 5:
+ ival = 2;
+ break;
+ case 7:
+ ival = 4;
+ break;
+ case 10:
+ ival = 8;
+ break;
+ default:
+ printk ("SB16_IRQ_LEVEL %d does not exist\n", level);
+ return;
+ }
+ sb_setmixer (IRQ_NR, ival);
+}
+
+long
+sb16_dsp_init (long mem_start, struct address_info *hw_config)
+{
+ if (sbc_major < 4)
+ return mem_start;
+
+#ifndef SCO
+ sprintf (sb16_dsp_operations.name, "SoundBlaster 16 %d.%d", sbc_major, sbc_minor);
+#endif
+
+#ifdef __FreeBSD__
+ printk ("snd6: <%s>", sb16_dsp_operations.name);
+#else
+ printk (" <%s>", sb16_dsp_operations.name);
+#endif
+
+ if (num_dspdevs < MAX_DSP_DEV)
+ {
+ dsp_devs[my_dev = num_dspdevs++] = &sb16_dsp_operations;
+ sound_dsp_dmachan[my_dev] = hw_config->dma;
+#ifndef NO_AUTODMA
+ sound_buffcounts[my_dev] = 1;
+ sound_dma_automode[my_dev] = 1;
+#else
+ sound_buffcounts[my_dev] = DSP_BUFFCOUNT;
+ sound_dma_automode[my_dev] = 0;
+#endif
+ sound_buffsizes[my_dev] = DSP_BUFFSIZE;
+ }
+ else
+ printk ("SB: Too many DSP devices available\n");
+ sb16_dsp_ok = 1;
+ return mem_start;
+}
+
+int
+sb16_dsp_detect (struct address_info *hw_config)
+{
+ struct address_info *sb_config;
+
+ if (sb16_dsp_ok)
+ return 1; /* Already initialized */
+
+ if (!(sb_config = sound_getconf (SNDCARD_SB)))
+ {
+ printk ("SB16 Error: Plain SB not configured\n");
+ return 0;
+ }
+
+ /* sb_setmixer(OPSW,0xf);
+ if(sb_getmixer(OPSW)!=0xf)
+ return 0; */
+
+ if (!sb_reset_dsp ())
+ return 0;
+
+ if (hw_config->dma < 4)
+ if (hw_config->dma != sb_config->dma)
+ {
+ printk ("SB16 Error: Invalid DMA channel %d/%d\n",
+ sb_config->dma, hw_config->dma);
+ return 0;
+ }
+
+ dma16 = hw_config->dma;
+ dma8 = sb_config->dma;
+ set_irq_hw (sb_config->irq);
+ sb_setmixer (DMA_NR, (1 << hw_config->dma) | (1 << sb_config->dma));
+
+ DEB (printk ("SoundBlaster 16: IRQ %d DMA %d OK\n", sb_config->irq, hw_config->dma));
+
+ /*
+ dsp_showmessage(0xe3,99);
+ */
+ sb16_dsp_ok = 1;
+ return 1;
+}
+
+void
+sb16_dsp_interrupt (int unused)
+{
+ int data;
+
+ data = INB (DSP_DATA_AVL16); /* Interrupt acknowledge */
+
+ if (intr_active)
+ switch (irq_mode)
+ {
+ case IMODE_OUTPUT:
+ intr_active = 0;
+ DMAbuf_outputintr (my_dev, 1);
+ break;
+
+ case IMODE_INPUT:
+ intr_active = 0;
+ DMAbuf_inputintr (my_dev);
+ break;
+
+ default:
+ printk ("SoundBlaster: Unexpected interrupt\n");
+ }
+}
+
+#endif
diff --git a/sys/i386/isa/sound/sb16_midi.c b/sys/i386/isa/sound/sb16_midi.c
new file mode 100644
index 0000000..39808c8
--- /dev/null
+++ b/sys/i386/isa/sound/sb16_midi.c
@@ -0,0 +1,287 @@
+/*
+ * sound/sb16_midi.c
+ *
+ * The low level driver for the MPU-401 UART emulation of the SB16.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_MIDI)
+
+#define DATAPORT (sb16midi_base) /* MPU-401 Data I/O Port on IBM */
+#define COMDPORT (sb16midi_base+1) /* MPU-401 Command Port on IBM */
+#define STATPORT (sb16midi_base+1) /* MPU-401 Status Port on IBM */
+
+#define sb16midi_status() INB(STATPORT)
+#define input_avail() (!(sb16midi_status()&INPUT_AVAIL))
+#define output_ready() (!(sb16midi_status()&OUTPUT_READY))
+#define sb16midi_cmd(cmd) OUTB(cmd, COMDPORT)
+#define sb16midi_read() INB(DATAPORT)
+#define sb16midi_write(byte) OUTB(byte, DATAPORT)
+
+#define OUTPUT_READY 0x40 /* Mask for Data Read Redy Bit */
+#define INPUT_AVAIL 0x80 /* Mask for Data Send Ready Bit */
+#define MPU_ACK 0xFE /* MPU-401 Acknowledge Response */
+#define MPU_RESET 0xFF /* MPU-401 Total Reset Command */
+#define UART_MODE_ON 0x3F /* MPU-401 "Dumb UART Mode" */
+
+static int sb16midi_opened = 0;
+static int sb16midi_base = 0x330;
+static int sb16midi_detected = 0;
+static int my_dev;
+
+static int reset_sb16midi (void);
+static void (*midi_input_intr) (int dev, unsigned char data);
+
+extern int sbc_major;
+
+static void
+sb16midi_input_loop (void)
+{
+
+ while (input_avail ())
+ {
+ unsigned char c = sb16midi_read ();
+
+ if (sb16midi_opened & OPEN_READ)
+ midi_input_intr (my_dev, c);
+ }
+}
+
+void
+sb16midiintr (int unit)
+{
+ if (input_avail ())
+ sb16midi_input_loop ();
+}
+
+static int
+sb16midi_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ if (sb16midi_opened)
+ {
+ return RET_ERROR (EBUSY);
+ }
+
+ sb16midi_input_loop ();
+
+ midi_input_intr = input;
+ sb16midi_opened = mode;
+
+ return 0;
+}
+
+static void
+sb16midi_close (int dev)
+{
+ sb16midi_opened = 0;
+}
+
+static int
+sb16midi_out (int dev, unsigned char midi_byte)
+{
+ int timeout;
+ unsigned long flags;
+
+ /*
+ * Test for input since pending input seems to block the output.
+ */
+
+ DISABLE_INTR (flags);
+
+ if (input_avail ())
+ sb16midi_input_loop ();
+
+ RESTORE_INTR (flags);
+
+ /*
+ * Sometimes it takes about 13000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
+
+ for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /* Wait */
+
+ if (!output_ready ())
+ {
+ printk ("MPU-401: Timeout\n");
+ return 0;
+ }
+
+ sb16midi_write (midi_byte);
+ return 1;
+}
+
+static int
+sb16midi_command (int dev, unsigned char midi_byte)
+{
+ return 1;
+}
+
+static int
+sb16midi_start_read (int dev)
+{
+ return 0;
+}
+
+static int
+sb16midi_end_read (int dev)
+{
+ return 0;
+}
+
+static int
+sb16midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+sb16midi_kick (int dev)
+{
+}
+
+static int
+sb16midi_buffer_status (int dev)
+{
+ return 0; /* No data in buffers */
+}
+
+static struct midi_operations sb16midi_operations =
+{
+ {"SoundBlaster MPU-401", 0, 0, SNDCARD_SB16MIDI},
+ sb16midi_open,
+ sb16midi_close,
+ sb16midi_ioctl,
+ sb16midi_out,
+ sb16midi_start_read,
+ sb16midi_end_read,
+ sb16midi_kick,
+ sb16midi_command,
+ sb16midi_buffer_status
+};
+
+
+long
+attach_sb16midi (long mem_start, struct address_info *hw_config)
+{
+ int ok, timeout;
+ unsigned long flags;
+
+ sb16midi_base = hw_config->io_base;
+
+ if (!sb16midi_detected)
+ return RET_ERROR (EIO);
+
+ DISABLE_INTR (flags);
+ for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* Wait */
+ sb16midi_cmd (UART_MODE_ON);
+
+ ok = 0;
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (input_avail ())
+ if (sb16midi_read () == MPU_ACK)
+ ok = 1;
+
+ RESTORE_INTR (flags);
+
+#ifdef __FreeBSD__
+ printk ("snd7: <SoundBlaster MPU-401>");
+#else
+ printk (" <SoundBlaster MPU-401>");
+#endif
+
+ my_dev = num_midis;
+ midi_devs[num_midis++] = &sb16midi_operations;
+ return mem_start;
+}
+
+static int
+reset_sb16midi (void)
+{
+ unsigned long flags;
+ int ok, timeout, n;
+
+ /*
+ * Send the RESET command. Try again if no success at the first time.
+ */
+
+ ok = 0;
+
+ DISABLE_INTR (flags);
+
+ for (n = 0; n < 2 && !ok; n++)
+ {
+ for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* Wait */
+ sb16midi_cmd (MPU_RESET); /* Send MPU-401 RESET Command */
+
+ /*
+ * Wait at least 25 msec. This method is not accurate so let's make the
+ * loop bit longer. Cannot sleep since this is called during boot.
+ */
+
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (input_avail ())
+ if (sb16midi_read () == MPU_ACK)
+ ok = 1;
+
+ }
+
+ sb16midi_opened = 0;
+ if (ok)
+ sb16midi_input_loop (); /* Flush input before enabling interrupts */
+
+ RESTORE_INTR (flags);
+
+ return ok;
+}
+
+
+int
+probe_sb16midi (struct address_info *hw_config)
+{
+ int ok = 0;
+
+ sb16midi_base = hw_config->io_base;
+ if (sbc_major < 4)
+ return 0; /* SB16 not detected */
+
+ if (sb_get_irq () < 0)
+ return 0;
+
+ ok = reset_sb16midi ();
+
+ sb16midi_detected = ok;
+ return ok;
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/sb_card.c b/sys/i386/isa/sound/sb_card.c
new file mode 100644
index 0000000..f7588e1
--- /dev/null
+++ b/sys/i386/isa/sound/sb_card.c
@@ -0,0 +1,52 @@
+/*
+ * sound/sb_card.c
+ *
+ * Detection routine for the SoundBlaster cards.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB)
+
+long
+attach_sb_card (long mem_start, struct address_info *hw_config)
+{
+#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_MIDI)
+ if (!sb_dsp_detect (hw_config))
+ return mem_start;
+ mem_start = sb_dsp_init (mem_start, hw_config);
+#endif
+
+ return mem_start;
+}
+
+int
+probe_sb (struct address_info *hw_config)
+{
+ return sb_dsp_detect (hw_config);
+}
+
+#endif
diff --git a/sys/i386/isa/sound/sb_dsp.c b/sys/i386/isa/sound/sb_dsp.c
new file mode 100644
index 0000000..17fb4b7
--- /dev/null
+++ b/sys/i386/isa/sound/sb_dsp.c
@@ -0,0 +1,785 @@
+/*
+ * sound/sb_dsp.c
+ *
+ * The low level driver for the SoundBlaster DSP chip.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Modified:
+ * Hunyue Yau Jan 6 1994
+ * Added code to support Sound Galaxy NX Pro
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB)
+
+#include "sb.h"
+#include "sb_mixer.h"
+#undef SB_TEST_IRQ
+
+int sbc_base = 0;
+static int sbc_irq = 0;
+static int open_mode=0;
+
+/*
+ * The DSP channel can be used either for input or output. Variable
+ * 'sb_irq_mode' will be set when the program calls read or write first time
+ * after open. Current version doesn't support mode changes without closing
+ * and reopening the device. Support for this feature may be implemented in a
+ * future version of this driver.
+ */
+
+int sb_dsp_ok = 0; /* Set to 1 after successful initialization */
+static int midi_disabled = 0;
+int sb_dsp_highspeed = 0;
+int sbc_major = 1;
+int sbc_minor = 0; /* DSP version */
+static int dsp_stereo = 0;
+static int dsp_current_speed = DSP_DEFAULT_SPEED;
+static int sb16 = 0;
+static int irq_verified = 0;
+
+int sb_midi_mode = NORMAL_MIDI;
+int sb_midi_busy = 0; /* 1 if the process has output to MIDI */
+int sb_dsp_busy = 0;
+
+volatile int sb_irq_mode = IMODE_NONE; /* IMODE_INPUT, IMODE_OUTPUT
+
+ * or IMODE_NONE */
+static volatile int irq_ok = 0;
+
+int sb_duplex_midi = 0;
+static int my_dev = 0;
+
+volatile int sb_intr_active = 0;
+
+static int dsp_speed (int);
+static int dsp_set_stereo (int mode);
+int sb_dsp_command (unsigned char val);
+
+#if !defined(EXCLUDE_MIDI) || !defined(EXCLUDE_AUDIO)
+
+/* Common code for the midi and pcm functions */
+
+int
+sb_dsp_command (unsigned char val)
+{
+ int i;
+ unsigned long limit;
+
+ limit = GET_TIME () + HZ / 10;/* The timeout is 0.1 secods */
+
+ /*
+ * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes
+ * called while interrupts are disabled. This means that the timer is
+ * disabled also. However the timeout situation is a abnormal condition.
+ * Normally the DSP should be ready to accept commands after just couple of
+ * loops.
+ */
+
+ for (i = 0; i < 500000 && GET_TIME () < limit; i++)
+ {
+ if ((INB (DSP_STATUS) & 0x80) == 0)
+ {
+ OUTB (val, DSP_COMMAND);
+ return 1;
+ }
+ }
+
+ printk ("SoundBlaster: DSP Command(%x) Timeout.\n", val);
+ printk ("IRQ conflict???\n");
+ return 0;
+}
+
+void
+sbintr (int unit)
+{
+ int status;
+
+#ifndef EXCLUDE_SBPRO
+ if (sb16)
+ {
+ unsigned char src = sb_getmixer (IRQ_STAT); /* Interrupt source register */
+
+#ifndef EXCLUDE_SB16
+ if (src & 3)
+ sb16_dsp_interrupt (unit);
+
+#ifndef EXCLUDE_MIDI
+ if (src & 4)
+ sb16midiintr (unit); /* MPU401 interrupt */
+#endif
+
+#endif
+
+ if (!(src & 1))
+ return; /* Not a DSP interupt */
+ }
+#endif
+
+ status = INB (DSP_DATA_AVAIL);/* Clear interrupt */
+
+ if (sb_intr_active)
+ switch (sb_irq_mode)
+ {
+ case IMODE_OUTPUT:
+ sb_intr_active = 0;
+ DMAbuf_outputintr (my_dev, 1);
+ break;
+
+ case IMODE_INPUT:
+ sb_intr_active = 0;
+ DMAbuf_inputintr (my_dev);
+ /* A complete buffer has been input. Let's start new one */
+ break;
+
+ case IMODE_INIT:
+ sb_intr_active = 0;
+ irq_ok = 1;
+ break;
+
+ case IMODE_MIDI:
+ sb_midi_interrupt (unit);
+ break;
+
+ default:
+ printk ("SoundBlaster: Unexpected interrupt\n");
+ }
+}
+
+static int sb_irq_usecount = 0;
+
+int
+sb_get_irq (void)
+{
+ int ok;
+
+ if (!sb_irq_usecount)
+ if ((ok = snd_set_irq_handler (sbc_irq, sbintr)) < 0)
+ return ok;
+
+ sb_irq_usecount++;
+
+ return 0;
+}
+
+void
+sb_free_irq (void)
+{
+ if (!sb_irq_usecount)
+ return;
+
+ sb_irq_usecount--;
+
+ if (!sb_irq_usecount)
+ snd_release_irq (sbc_irq);
+}
+
+int
+sb_reset_dsp (void)
+{
+ int loopc;
+
+ OUTB (1, DSP_RESET);
+ tenmicrosec ();
+ OUTB (0, DSP_RESET);
+ tenmicrosec ();
+ tenmicrosec ();
+ tenmicrosec ();
+
+ for (loopc = 0; loopc < 1000 && !(INB (DSP_DATA_AVAIL) & 0x80); loopc++); /* Wait for data
+ * available status */
+
+ if (INB (DSP_READ) != 0xAA)
+ return 0; /* Sorry */
+
+ return 1;
+}
+
+#endif
+
+#ifndef EXCLUDE_AUDIO
+
+static void
+dsp_speaker (char state)
+{
+ if (state)
+ sb_dsp_command (DSP_CMD_SPKON);
+ else
+ sb_dsp_command (DSP_CMD_SPKOFF);
+}
+
+static int
+dsp_speed (int speed)
+{
+ unsigned char tconst;
+ unsigned long flags;
+ int max_speed = 44100;
+
+ if (speed < 4000)
+ speed = 4000;
+
+ /*
+ * Older SB models don't support higher speeds than 22050.
+ */
+
+ if (sbc_major < 2 ||
+ (sbc_major == 2 && sbc_minor == 0))
+ max_speed = 22050;
+
+ /*
+ * SB models earlier than SB Pro have low limit for the input speed.
+ */
+ if (open_mode != OPEN_WRITE) /* Recording is possible */
+ if (sbc_major < 3) /* Limited input speed with these cards */
+ if (sbc_major == 2 && sbc_minor > 0)
+ max_speed = 15000;
+ else
+ max_speed = 13000;
+
+ if (speed > max_speed)
+ speed = max_speed; /* Invalid speed */
+
+ if (dsp_stereo && speed > 22050)
+ speed = 22050;
+ /* Max. stereo speed is 22050 */
+
+ if ((speed > 22050) && sb_midi_busy)
+ {
+ printk ("SB Warning: High speed DSP not possible simultaneously with MIDI output\n");
+ speed = 22050;
+ }
+
+ if (dsp_stereo)
+ speed *= 2;
+
+ /* Now the speed should be valid */
+
+ if (speed > 22050)
+ { /* High speed mode */
+ int tmp;
+
+ tconst = (unsigned char) ((65536 -
+ ((256000000 + speed / 2) / speed)) >> 8);
+ sb_dsp_highspeed = 1;
+
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x40))
+ sb_dsp_command (tconst);
+ RESTORE_INTR (flags);
+
+ tmp = 65536 - (tconst << 8);
+ speed = (256000000 + tmp / 2) / tmp;
+ }
+ else
+ {
+ int tmp;
+
+ sb_dsp_highspeed = 0;
+ tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff;
+
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x40))/* Set time constant */
+ sb_dsp_command (tconst);
+ RESTORE_INTR (flags);
+
+ tmp = 256 - tconst;
+ speed = (1000000 + tmp / 2) / tmp;
+ }
+
+ if (dsp_stereo)
+ speed /= 2;
+
+ dsp_current_speed = speed;
+ return speed;
+}
+
+static int
+dsp_set_stereo (int mode)
+{
+ dsp_stereo = 0;
+
+#ifdef EXCLUDE_SBPRO
+ return 0;
+#else
+ if (sbc_major < 3 || sb16)
+ return 0; /* Sorry no stereo */
+
+ if (mode && sb_midi_busy)
+ {
+ printk ("SB Warning: Stereo DSP not possible simultaneously with MIDI output\n");
+ return 0;
+ }
+
+ dsp_stereo = !!mode;
+ return dsp_stereo;
+#endif
+}
+
+static void
+sb_dsp_output_block (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
+{
+ unsigned long flags;
+
+ if (!sb_irq_mode)
+ dsp_speaker (ON);
+
+ sb_irq_mode = IMODE_OUTPUT;
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+
+ if (sound_dsp_dmachan[dev] > 3)
+ count >>= 1;
+ count--;
+
+ if (sb_dsp_highspeed)
+ {
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x48))/* High speed size */
+ {
+ sb_dsp_command ((unsigned char) (count & 0xff));
+ sb_dsp_command ((unsigned char) ((count >> 8) & 0xff));
+ sb_dsp_command (0x91);/* High speed 8 bit DAC */
+ }
+ else
+ printk ("SB Error: Unable to start (high speed) DAC\n");
+ RESTORE_INTR (flags);
+ }
+ else
+ {
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x14))/* 8-bit DAC (DMA) */
+ {
+ sb_dsp_command ((unsigned char) (count & 0xff));
+ sb_dsp_command ((unsigned char) ((count >> 8) & 0xff));
+ }
+ else
+ printk ("SB Error: Unable to start DAC\n");
+ RESTORE_INTR (flags);
+ }
+ sb_intr_active = 1;
+}
+
+static void
+sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag,
+ int restart_dma)
+{
+ /* Start a DMA input to the buffer pointed by dmaqtail */
+
+ unsigned long flags;
+
+ if (!sb_irq_mode)
+ dsp_speaker (OFF);
+
+ sb_irq_mode = IMODE_INPUT;
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+
+ if (sound_dsp_dmachan[dev] > 3)
+ count >>= 1;
+ count--;
+
+ if (sb_dsp_highspeed)
+ {
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x48))/* High speed size */
+ {
+ sb_dsp_command ((unsigned char) (count & 0xff));
+ sb_dsp_command ((unsigned char) ((count >> 8) & 0xff));
+ sb_dsp_command (0x99);/* High speed 8 bit ADC */
+ }
+ else
+ printk ("SB Error: Unable to start (high speed) ADC\n");
+ RESTORE_INTR (flags);
+ }
+ else
+ {
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x24))/* 8-bit ADC (DMA) */
+ {
+ sb_dsp_command ((unsigned char) (count & 0xff));
+ sb_dsp_command ((unsigned char) ((count >> 8) & 0xff));
+ }
+ else
+ printk ("SB Error: Unable to start ADC\n");
+ RESTORE_INTR (flags);
+ }
+
+ sb_intr_active = 1;
+}
+
+static void
+dsp_cleanup (void)
+{
+ sb_intr_active = 0;
+}
+
+static int
+sb_dsp_prepare_for_input (int dev, int bsize, int bcount)
+{
+ dsp_cleanup ();
+ dsp_speaker (OFF);
+
+ if (sbc_major == 3) /* SB Pro */
+ {
+ if (dsp_stereo)
+ sb_dsp_command (0xa8);
+ else
+ sb_dsp_command (0xa0);
+
+ dsp_speed (dsp_current_speed); /* Speed must be recalculated if #channels
+ * changes */
+ }
+ return 0;
+}
+
+static int
+sb_dsp_prepare_for_output (int dev, int bsize, int bcount)
+{
+ dsp_cleanup ();
+ dsp_speaker (ON);
+
+#ifndef EXCLUDE_SBPRO
+ if (sbc_major == 3) /* SB Pro */
+ {
+ sb_mixer_set_stereo (dsp_stereo);
+ dsp_speed (dsp_current_speed); /* Speed must be recalculated if #channels
+ * changes */
+ }
+#endif
+ return 0;
+}
+
+static void
+sb_dsp_halt_xfer (int dev)
+{
+}
+
+static int
+verify_irq (void)
+{
+#if 0
+ DEFINE_WAIT_QUEUE (testq, testf);
+
+ irq_ok = 0;
+
+ if (sb_get_irq () == -1)
+ {
+ printk ("*** SB Error: Irq %d already in use\n", sbc_irq);
+ return 0;
+ }
+
+
+ sb_irq_mode = IMODE_INIT;
+
+ sb_dsp_command (0xf2); /* This should cause immediate interrupt */
+
+ DO_SLEEP (testq, testf, HZ / 5);
+
+ sb_free_irq ();
+
+ if (!irq_ok)
+ {
+ printk ("SB Warning: IRQ%d test not passed!", sbc_irq);
+ irq_ok = 1;
+ }
+#else
+ irq_ok = 1;
+#endif
+ return irq_ok;
+}
+
+static int
+sb_dsp_open (int dev, int mode)
+{
+ int retval;
+
+ if (!sb_dsp_ok)
+ {
+ printk ("SB Error: SoundBlaster board not installed\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if (sb_intr_active || (sb_midi_busy && sb_midi_mode == UART_MIDI))
+ {
+ printk ("SB: PCM not possible during MIDI input\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ if (!irq_verified)
+ {
+ verify_irq ();
+ irq_verified = 1;
+ }
+ else if (!irq_ok)
+ printk ("SB Warning: Incorrect IRQ setting %d\n",
+ sbc_irq);
+
+ retval = sb_get_irq ();
+ if (retval)
+ return retval;
+
+ if (!DMAbuf_open_dma (dev))
+ {
+ sb_free_irq ();
+ printk ("SB: DMA Busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ sb_irq_mode = IMODE_NONE;
+
+ sb_dsp_busy = 1;
+ open_mode = mode;
+
+ return 0;
+}
+
+static void
+sb_dsp_close (int dev)
+{
+ DMAbuf_close_dma (dev);
+ sb_free_irq ();
+ dsp_cleanup ();
+ dsp_speaker (OFF);
+ sb_dsp_busy = 0;
+ sb_dsp_highspeed = 0;
+ open_mode = 0;
+}
+
+static int
+sb_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return dsp_speed (arg);
+ return IOCTL_OUT (arg, dsp_speed (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return dsp_current_speed;
+ return IOCTL_OUT (arg, dsp_current_speed);
+ break;
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return dsp_set_stereo (arg - 1) + 1;
+ return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1);
+ break;
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return dsp_stereo + 1;
+ return IOCTL_OUT (arg, dsp_stereo + 1);
+ break;
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return dsp_set_stereo (arg);
+ return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_WRITE_BITS:
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return 8;
+ return IOCTL_OUT (arg, 8);/* Only 8 bits/sample supported */
+ break;
+
+ case SOUND_PCM_WRITE_FILTER:
+ case SOUND_PCM_READ_FILTER:
+ return RET_ERROR (EINVAL);
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static void
+sb_dsp_reset (int dev)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ sb_reset_dsp ();
+ dsp_speed (dsp_current_speed);
+ dsp_cleanup ();
+
+ RESTORE_INTR (flags);
+}
+
+#endif
+
+int
+sb_dsp_detect (struct address_info *hw_config)
+{
+ sbc_base = hw_config->io_base;
+ sbc_irq = hw_config->irq;
+
+ if (sb_dsp_ok)
+ return 0; /* Already initialized */
+
+ if (!sb_reset_dsp ())
+ return 0;
+
+ return 1; /* Detected */
+}
+
+static char card_name[32] = "SoundBlaster";
+
+#ifndef EXCLUDE_AUDIO
+static struct audio_operations sb_dsp_operations =
+{
+ "SoundBlaster",
+ NOTHING_SPECIAL,
+ sb_dsp_open,
+ sb_dsp_close,
+ sb_dsp_output_block,
+ sb_dsp_start_input,
+ sb_dsp_ioctl,
+ sb_dsp_prepare_for_input,
+ sb_dsp_prepare_for_output,
+ sb_dsp_reset,
+ sb_dsp_halt_xfer,
+ NULL, /* has_output_drained */
+ NULL /* copy_from_user */
+};
+
+#endif
+
+long
+sb_dsp_init (long mem_start, struct address_info *hw_config)
+{
+ int i;
+ int prostat = 0;
+
+ sbc_major = sbc_minor = 0;
+ sb_dsp_command (0xe1); /* Get version */
+
+ for (i = 1000; i; i--)
+ {
+ if (INB (DSP_DATA_AVAIL) & 0x80)
+ { /* wait for Data Ready */
+ if (sbc_major == 0)
+ sbc_major = INB (DSP_READ);
+ else
+ {
+ sbc_minor = INB (DSP_READ);
+ break;
+ }
+ }
+ }
+
+ if (sbc_major == 2 || sbc_major == 3) /* SB 2.0 or SB Pro */
+ sb_duplex_midi = 1;
+
+ if (sbc_major == 4)
+ sb16 = 1;
+
+#ifndef EXCLUDE_SBPRO
+ if (sbc_major >= 3 ||
+ (sbc_major == 2 && sbc_minor == 1)) /* Sound Galaxy ??? */
+ prostat = sb_mixer_init (sbc_major);
+#endif
+
+#ifndef EXCLUDE_YM3812
+ if (sbc_major > 3 ||
+ (sbc_major == 3 && INB (0x388) == 0x00)) /* Non OPL-3 should return 0x06 */
+ enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH);
+#endif
+
+ if (sbc_major >= 3)
+ {
+#ifndef SCO
+ if (prostat)
+ {
+#ifndef EXCLUDE_AUDIO
+ sprintf (sb_dsp_operations.name, "Sound Galaxy NX Pro %d.%d", sbc_major, sbc_minor);
+#endif
+ sprintf (card_name, "Sound Galaxy NX Pro %d.%d", sbc_major, sbc_minor);
+ }
+ else
+ {
+#ifndef EXCLUDE_AUDIO
+ sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor);
+#endif
+ sprintf (card_name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor);
+ }
+#endif
+ }
+ else
+ {
+#ifndef SCO
+#ifndef EXCLUDE_AUDIO
+ sprintf (sb_dsp_operations.name, "SoundBlaster %d.%d", sbc_major, sbc_minor);
+#endif
+ sprintf (card_name, "SoundBlaster %d.%d", sbc_major, sbc_minor);
+#endif
+ }
+
+#ifdef __FreeBSD__
+ printk ("snd2: <%s>", card_name);
+#else
+ printk (" <%s>", card_name);
+#endif
+
+#ifndef EXCLUDE_AUDIO
+#if !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SBPRO)
+ if (!sb16) /* There is a better driver for SB16 */
+#endif
+ if (num_dspdevs < MAX_DSP_DEV)
+ {
+ dsp_devs[my_dev = num_dspdevs++] = &sb_dsp_operations;
+ sound_buffcounts[my_dev] = DSP_BUFFCOUNT;
+ sound_buffsizes[my_dev] = DSP_BUFFSIZE;
+ sound_dsp_dmachan[my_dev] = hw_config->dma;
+ sound_dma_automode[my_dev] = 0;
+ }
+ else
+ printk ("SB: Too many DSP devices available\n");
+#endif
+
+#ifndef EXCLUDE_MIDI
+ if (!midi_disabled && !sb16) /* Midi don't work in the SB emulation mode
+ * of PAS, SB16 has better midi interface */
+ sb_midi_init (sbc_major);
+#endif
+
+ sb_dsp_ok = 1;
+ return mem_start;
+}
+
+void
+sb_dsp_disable_midi (void)
+{
+ midi_disabled = 1;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/sb_midi.c b/sys/i386/isa/sound/sb_midi.c
new file mode 100644
index 0000000..fed19ab
--- /dev/null
+++ b/sys/i386/isa/sound/sb_midi.c
@@ -0,0 +1,224 @@
+/*
+ * sound/sb_dsp.c
+ *
+ * The low level driver for the SoundBlaster DS chips.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_MIDI)
+
+#include "sb.h"
+#undef SB_TEST_IRQ
+
+/*
+ * The DSP channel can be used either for input or output. Variable
+ * 'sb_irq_mode' will be set when the program calls read or write first time
+ * after open. Current version doesn't support mode changes without closing
+ * and reopening the device. Support for this feature may be implemented in a
+ * future version of this driver.
+ */
+
+extern int sb_dsp_ok; /* Set to 1 after successful initialization */
+
+extern int sb_midi_mode;
+extern int sb_midi_busy; /* 1 if the process has output to MIDI */
+extern int sb_dsp_busy;
+extern int sb_dsp_highspeed;
+
+extern volatile int sb_irq_mode;/* IMODE_INPUT, IMODE_OUTPUT
+
+ * or IMODE_NONE */
+extern int sb_duplex_midi;
+extern int sb_intr_active;
+extern int sbc_base;
+
+static int input_opened = 0;
+static void (*midi_input_intr) (int dev, unsigned char data);
+static int my_dev = 0;
+
+static int
+sb_midi_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ int ret;
+
+ if (!sb_dsp_ok)
+ {
+ printk ("SB Error: MIDI hardware not installed\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if (mode != OPEN_WRITE && !sb_duplex_midi)
+ {
+ if (num_midis == 1)
+ printk ("SoundBlaster: MIDI input not supported with plain SB\n");
+ return RET_ERROR (EPERM);
+ }
+
+ sb_midi_mode = NORMAL_MIDI;
+ if (mode != OPEN_WRITE)
+ {
+ if (sb_dsp_busy || sb_intr_active)
+ return RET_ERROR (EBUSY);
+ sb_midi_mode = UART_MIDI;
+ }
+
+ if (sb_dsp_highspeed)
+ {
+ printk ("SB Error: Midi output not possible during stereo or high speed audio\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ if (sb_midi_mode == UART_MIDI)
+ {
+ sb_irq_mode = IMODE_MIDI;
+
+ sb_reset_dsp ();
+
+ if (!sb_dsp_command (0xf2)) /* This is undodumented, isn't it */
+ return RET_ERROR (EIO); /* be nice to DSP */
+
+ if (!sb_dsp_command (0x35))
+ return RET_ERROR (EIO); /* Enter the UART mode */
+ sb_intr_active = 1;
+
+ if ((ret = sb_get_irq ()) < 0)
+ {
+ sb_reset_dsp ();
+ return 0; /* IRQ not free */
+ }
+ input_opened = 1;
+ my_dev = dev;
+ midi_input_intr = input;
+ }
+
+ sb_midi_busy = 1;
+
+ return 0;
+}
+
+static void
+sb_midi_close (int dev)
+{
+ if (sb_midi_mode == UART_MIDI)
+ {
+ sb_reset_dsp (); /* The only way to kill the UART mode */
+ sb_free_irq ();
+ }
+ sb_intr_active = 0;
+ sb_midi_busy = 0;
+ input_opened = 0;
+}
+
+static int
+sb_midi_out (int dev, unsigned char midi_byte)
+{
+ unsigned long flags;
+
+ sb_midi_busy = 1; /* Kill all notes after close */
+
+ if (sb_midi_mode == NORMAL_MIDI)
+ {
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x38))
+ sb_dsp_command (midi_byte);
+ else
+ printk ("SB Error: Unable to send a MIDI byte\n");
+ RESTORE_INTR (flags);
+ }
+ else
+ sb_dsp_command (midi_byte); /* UART write */
+
+ return 1;
+}
+
+static int
+sb_midi_start_read (int dev)
+{
+ if (sb_midi_mode != UART_MIDI)
+ {
+ printk ("SoundBlaster: MIDI input not implemented.\n");
+ return RET_ERROR (EPERM);
+ }
+ return 0;
+}
+
+static int
+sb_midi_end_read (int dev)
+{
+ if (sb_midi_mode == UART_MIDI)
+ {
+ sb_reset_dsp ();
+ sb_intr_active = 0;
+ }
+ return 0;
+}
+
+static int
+sb_midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EPERM);
+}
+
+void
+sb_midi_interrupt (int dummy)
+{
+ unsigned long flags;
+ unsigned char data;
+
+ DISABLE_INTR (flags);
+
+ data = INB (DSP_READ);
+ if (input_opened)
+ midi_input_intr (my_dev, data);
+
+ RESTORE_INTR (flags);
+}
+
+static struct midi_operations sb_midi_operations =
+{
+ {"SoundBlaster", 0, 0, SNDCARD_SB},
+ sb_midi_open,
+ sb_midi_close,
+ sb_midi_ioctl,
+ sb_midi_out,
+ sb_midi_start_read,
+ sb_midi_end_read,
+ NULL, /* Kick */
+ NULL, /* command */
+ NULL /* buffer_status */
+};
+
+void
+sb_midi_init (int model)
+{
+ midi_devs[num_midis++] = &sb_midi_operations;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/sb_mixer.c b/sys/i386/isa/sound/sb_mixer.c
new file mode 100644
index 0000000..39b97ca
--- /dev/null
+++ b/sys/i386/isa/sound/sb_mixer.c
@@ -0,0 +1,422 @@
+
+/*
+ * sound/sb_mixer.c
+ *
+ * The low level mixer driver for the SoundBlaster Pro and SB16 cards.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Modified:
+ * Hunyue Yau Jan 6 1994
+ * Added code to support the Sound Galaxy NX Pro mixer.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_SBPRO)
+#define __SB_MIXER_C__
+
+#include "sb.h"
+#include "sb_mixer.h"
+#undef SB_TEST_IRQ
+
+extern int sbc_base;
+
+static int mixer_initialized = 0;
+
+static int supported_rec_devices;
+static int supported_devices;
+static int recmask = 0;
+static int mixer_model;
+static int mixer_caps;
+static mixer_tab *iomap;
+
+void
+sb_setmixer (unsigned int port, unsigned int value)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ OUTB ((unsigned char) (port & 0xff), MIXER_ADDR); /* Select register */
+ tenmicrosec ();
+ OUTB ((unsigned char) (value & 0xff), MIXER_DATA);
+ tenmicrosec ();
+ RESTORE_INTR (flags);
+}
+
+int
+sb_getmixer (unsigned int port)
+{
+ int val;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ OUTB ((unsigned char) (port & 0xff), MIXER_ADDR); /* Select register */
+ tenmicrosec ();
+ val = INB (MIXER_DATA);
+ tenmicrosec ();
+ RESTORE_INTR (flags);
+
+ return val;
+}
+
+void
+sb_mixer_set_stereo (int mode)
+{
+ if (!mixer_initialized)
+ return;
+
+ sb_setmixer (OUT_FILTER, ((sb_getmixer (OUT_FILTER) & ~STEREO_DAC)
+ | (mode ? STEREO_DAC : MONO_DAC)));
+}
+
+/*
+ * Returns:
+ * 0 No mixer detected.
+ * 1 Only a plain Sound Blaster Pro style mixer detected.
+ * 2 The Sound Galaxy NX Pro mixer detected.
+ */
+static int
+detect_mixer (void)
+{
+#ifdef __SGNXPRO__
+ int oldbass, oldtreble;
+
+#endif
+ int retcode = 1;
+
+ /*
+ * Detect the mixer by changing parameters of two volume channels. If the
+ * values read back match with the values written, the mixer is there (is
+ * it?)
+ */
+ sb_setmixer (FM_VOL, 0xff);
+ sb_setmixer (VOC_VOL, 0x33);
+
+ if (sb_getmixer (FM_VOL) != 0xff)
+ return 0; /* No match */
+ if (sb_getmixer (VOC_VOL) != 0x33)
+ return 0;
+
+#ifdef __SGNXPRO__
+ /* Attempt to detect the SG NX Pro by check for valid bass/treble
+ * registers.
+ */
+ oldbass = sb_getmixer (BASS_LVL);
+ oldtreble = sb_getmixer (TREBLE_LVL);
+
+ sb_setmixer (BASS_LVL, 0xaa);
+ sb_setmixer (TREBLE_LVL, 0x55);
+
+ if ((sb_getmixer (BASS_LVL) != 0xaa) ||
+ (sb_getmixer (TREBLE_LVL) != 0x55))
+ {
+ retcode = 1; /* 1 == Only SB Pro detected */
+ }
+ else
+ retcode = 2; /* 2 == SG NX Pro detected */
+ /* Restore register in either case since SG NX Pro has EEPROM with
+ * 'preferred' values stored.
+ */
+ sb_setmixer (BASS_LVL, oldbass);
+ sb_setmixer (TREBLE_LVL, oldtreble);
+#endif
+ return retcode;
+}
+
+static void
+change_bits (unsigned char *regval, int dev, int chn, int newval)
+{
+ unsigned char mask;
+ int shift;
+
+ mask = (1 << (*iomap)[dev][chn].nbits) - 1;
+ newval = ((newval * mask) + 50) / 100; /* Scale it */
+
+ shift = (*iomap)[dev][chn].bitoffs - (*iomap)[dev][LEFT_CHN].nbits + 1;
+
+ *regval &= ~(mask << shift); /* Filter out the previous value */
+ *regval |= (newval & mask) << shift; /* Set the new value */
+}
+
+static int
+sb_mixer_get (int dev)
+{
+ if (!((1 << dev) & supported_devices))
+ return RET_ERROR (EINVAL);
+
+ return levels[dev];
+}
+
+static int
+sb_mixer_set (int dev, int value)
+{
+ int left = value & 0x000000ff;
+ int right = (value & 0x0000ff00) >> 8;
+
+ int regoffs;
+ unsigned char val;
+
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+
+ if (dev > 31)
+ return RET_ERROR (EINVAL);
+
+ if (!(supported_devices & (1 << dev))) /* Not supported */
+ return RET_ERROR (EINVAL);
+
+ regoffs = (*iomap)[dev][LEFT_CHN].regno;
+
+ if (regoffs == 0)
+ return RET_ERROR (EINVAL);
+
+ val = sb_getmixer (regoffs);
+ change_bits (&val, dev, LEFT_CHN, left);
+
+ levels[dev] = left | (left << 8);
+
+ if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) /* Change register */
+ {
+ sb_setmixer (regoffs, val); /* Save the old one */
+ regoffs = (*iomap)[dev][RIGHT_CHN].regno;
+
+ if (regoffs == 0)
+ return left | (left << 8); /* Just left channel present */
+
+ val = sb_getmixer (regoffs); /* Read the new one */
+ }
+
+ change_bits (&val, dev, RIGHT_CHN, right);
+ sb_setmixer (regoffs, val);
+
+ levels[dev] = left | (right << 8);
+ return left | (right << 8);
+}
+
+static void
+set_recsrc (int src)
+{
+ sb_setmixer (RECORD_SRC, (sb_getmixer (RECORD_SRC) & ~7) | (src & 0x7));
+}
+
+static int
+set_recmask (int mask)
+{
+ int devmask, i;
+ unsigned char regimageL, regimageR;
+
+ devmask = mask & supported_rec_devices;
+
+ switch (mixer_model)
+ {
+ case 3:
+
+ if (devmask != SOUND_MASK_MIC &&
+ devmask != SOUND_MASK_LINE &&
+ devmask != SOUND_MASK_CD)
+ { /* More than one devices selected. Drop the
+ * previous selection */
+ devmask &= ~recmask;
+ }
+
+ if (devmask != SOUND_MASK_MIC &&
+ devmask != SOUND_MASK_LINE &&
+ devmask != SOUND_MASK_CD)
+ { /* More than one devices selected. Default to
+ * mic */
+ devmask = SOUND_MASK_MIC;
+ }
+
+
+ if (devmask ^ recmask) /* Input source changed */
+ {
+ switch (devmask)
+ {
+
+ case SOUND_MASK_MIC:
+ set_recsrc (SRC_MIC);
+ break;
+
+ case SOUND_MASK_LINE:
+ set_recsrc (SRC_LINE);
+ break;
+
+ case SOUND_MASK_CD:
+ set_recsrc (SRC_CD);
+ break;
+
+ default:
+ set_recsrc (SRC_MIC);
+ }
+ }
+
+ break;
+
+ case 4:
+ if (!devmask)
+ devmask = SOUND_MASK_MIC;
+
+ regimageL = regimageR = 0;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((1 << i) & devmask)
+ {
+ regimageL |= sb16_recmasks_L[i];
+ regimageR |= sb16_recmasks_R[i];
+ }
+ sb_setmixer (SB16_IMASK_L, regimageL);
+ sb_setmixer (SB16_IMASK_R, regimageR);
+ break;
+ }
+
+ recmask = devmask;
+ return recmask;
+}
+
+static int
+sb_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
+{
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ if (cmd & IOC_IN)
+ switch (cmd & 0xff)
+ {
+ case SOUND_MIXER_RECSRC:
+ return IOCTL_OUT (arg, set_recmask (IOCTL_IN (arg)));
+ break;
+
+ default:
+ return IOCTL_OUT (arg, sb_mixer_set (cmd & 0xff, IOCTL_IN (arg)));
+ }
+ else
+ switch (cmd & 0xff) /* Return parameters */
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return IOCTL_OUT (arg, recmask);
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return IOCTL_OUT (arg, supported_devices);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ return IOCTL_OUT (arg, supported_devices &
+ ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER));
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return IOCTL_OUT (arg, supported_rec_devices);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return IOCTL_OUT (arg, mixer_caps);
+ break;
+
+ default:
+ return IOCTL_OUT (arg, sb_mixer_get (cmd & 0xff));
+ }
+ }
+ else
+ return RET_ERROR (EINVAL);
+}
+
+static struct mixer_operations sb_mixer_operations =
+{
+ sb_mixer_ioctl
+};
+
+static void
+sb_mixer_reset (void)
+{
+ int i;
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ sb_mixer_set (i, levels[i]);
+ set_recmask (SOUND_MASK_MIC);
+}
+
+/*
+ * Returns a code depending on whether a SG NX Pro was detected.
+ * 0 == Plain SB 16 or SB Pro
+ * 1 == SG NX Pro detected.
+ *
+ * Used to update message.
+ */
+int
+sb_mixer_init (int major_model)
+{
+ int mixerstat;
+
+ sb_setmixer (0x00, 0); /* Reset mixer */
+
+ mixerstat = detect_mixer ();
+
+ if (!mixerstat)
+ return 0; /* No mixer. Why? */
+
+ mixer_initialized = 1;
+ mixer_model = major_model;
+
+ switch (major_model)
+ {
+ case 3:
+ mixer_caps = SOUND_CAP_EXCL_INPUT;
+#ifdef __SGNXPRO__
+ if (mixerstat == 2)
+ { /* A SGNXPRO was detected */
+ supported_devices = SGNXPRO_MIXER_DEVICES;
+ supported_rec_devices = SGNXPRO_RECORDING_DEVICES;
+ iomap = &sgnxpro_mix;
+ }
+ else
+#endif
+ { /* Otherwise plain SB Pro */
+ supported_devices = SBPRO_MIXER_DEVICES;
+ supported_rec_devices = SBPRO_RECORDING_DEVICES;
+ iomap = &sbpro_mix;
+ }
+
+ break;
+
+ case 4:
+ mixer_caps = 0;
+ supported_devices = SB16_MIXER_DEVICES;
+ supported_rec_devices = SB16_RECORDING_DEVICES;
+ iomap = &sb16_mix;
+ break;
+
+ default:
+ printk ("SB Warning: Unsupported mixer type\n");
+ return 0;
+ }
+
+ mixer_devs[num_mixers++] = &sb_mixer_operations;
+ sb_mixer_reset ();
+ return (mixerstat == 2);
+}
+
+#endif
diff --git a/sys/i386/isa/sound/sb_mixer.h b/sys/i386/isa/sound/sb_mixer.h
new file mode 100644
index 0000000..4caf773
--- /dev/null
+++ b/sys/i386/isa/sound/sb_mixer.h
@@ -0,0 +1,212 @@
+/*
+ * sound/sb_mixer.h
+ *
+ * Definitions for the SB Pro and SB16 mixers
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Modified:
+ * Hunyue Yau Jan 6 1994
+ * Added defines for the Sound Galaxy NX Pro mixer.
+ *
+ */
+
+#define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
+
+/* Same as SB Pro, unless I find otherwise */
+#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
+
+#define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_VOLUME)
+
+/* SG NX Pro has treble and bass settings on the mixer. The 'speaker'
+ * channel is the COVOX/DisneySoundSource emulation volume control
+ * on the mixer. It does NOT control speaker volume. Should have own
+ * mask eventually?
+ */
+#define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \
+ SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER )
+
+#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD)
+
+#define SB16_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_RECLEV | \
+ SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE)
+
+/*
+ * Mixer registers
+ *
+ * NOTE! RECORD_SRC == IN_FILTER
+ */
+
+/*
+ * Mixer registers of SB Pro
+ */
+#define VOC_VOL 0x04
+#define MIC_VOL 0x0A
+#define MIC_MIX 0x0A
+#define RECORD_SRC 0x0C
+#define IN_FILTER 0x0C
+#define OUT_FILTER 0x0E
+#define MASTER_VOL 0x22
+#define FM_VOL 0x26
+#define CD_VOL 0x28
+#define LINE_VOL 0x2E
+#define IRQ_NR 0x80
+#define DMA_NR 0x81
+#define IRQ_STAT 0x82
+#define OPSW 0x3c
+
+/*
+ * Additional registers on the SG NX Pro
+ */
+#define COVOX_VOL 0x42
+#define TREBLE_LVL 0x44
+#define BASS_LVL 0x46
+
+#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */
+#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */
+#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */
+#define FILT_OFF (1 << 5)
+
+#define MONO_DAC 0x00
+#define STEREO_DAC 0x02
+
+/*
+ * Mixer registers of SB16
+ */
+#define SB16_IMASK_L 0x3d
+#define SB16_IMASK_R 0x3e
+
+#define LEFT_CHN 0
+#define RIGHT_CHN 1
+
+struct mixer_def {
+ unsigned int regno: 8;
+ unsigned int bitoffs:4;
+ unsigned int nbits:4;
+};
+
+
+typedef struct mixer_def mixer_tab[32][2];
+typedef struct mixer_def mixer_ent;
+
+#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r) \
+ {{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}}
+
+#ifdef __SB_MIXER_C__
+mixer_tab sbpro_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
+};
+
+#ifdef __SGNXPRO__
+mixer_tab sgnxpro_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS, 0x46, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER, 0x42, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
+};
+#endif
+
+mixer_tab sb16_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5),
+MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4),
+MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4),
+MIX_ENT(SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5),
+MIX_ENT(SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5),
+MIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5),
+MIX_ENT(SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5),
+MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2)
+};
+
+static unsigned short levels[SOUND_MIXER_NRDEVICES] =
+{
+ 0x5a5a, /* Master Volume */
+ 0x3232, /* Bass */
+ 0x3232, /* Treble */
+ 0x4b4b, /* FM */
+ 0x4b4b, /* PCM */
+ 0x4b4b, /* PC Speaker */
+ 0x4b4b, /* Ext Line */
+ 0x0000, /* Mic */
+ 0x4b4b, /* CD */
+ 0x4b4b, /* Recording monitor */
+ 0x4b4b, /* SB PCM */
+ 0x4b4b}; /* Recording level */
+
+static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] =
+{
+ 0x00, /* SOUND_MIXER_VOLUME */
+ 0x00, /* SOUND_MIXER_BASS */
+ 0x00, /* SOUND_MIXER_TREBLE */
+ 0x40, /* SOUND_MIXER_SYNTH */
+ 0x00, /* SOUND_MIXER_PCM */
+ 0x00, /* SOUND_MIXER_SPEAKER */
+ 0x10, /* SOUND_MIXER_LINE */
+ 0x01, /* SOUND_MIXER_MIC */
+ 0x04, /* SOUND_MIXER_CD */
+ 0x00, /* SOUND_MIXER_IMIX */
+ 0x00, /* SOUND_MIXER_ALTPCM */
+ 0x00 /* SOUND_MIXER_RECLEV */
+};
+
+static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] =
+{
+ 0x00, /* SOUND_MIXER_VOLUME */
+ 0x00, /* SOUND_MIXER_BASS */
+ 0x00, /* SOUND_MIXER_TREBLE */
+ 0x20, /* SOUND_MIXER_SYNTH */
+ 0x00, /* SOUND_MIXER_PCM */
+ 0x00, /* SOUND_MIXER_SPEAKER */
+ 0x08, /* SOUND_MIXER_LINE */
+ 0x01, /* SOUND_MIXER_MIC */
+ 0x02, /* SOUND_MIXER_CD */
+ 0x00, /* SOUND_MIXER_IMIX */
+ 0x00, /* SOUND_MIXER_ALTPCM */
+ 0x00 /* SOUND_MIXER_RECLEV */
+};
+#endif
diff --git a/sys/i386/isa/sound/sequencer.c b/sys/i386/isa/sound/sequencer.c
new file mode 100644
index 0000000..9885473
--- /dev/null
+++ b/sys/i386/isa/sound/sequencer.c
@@ -0,0 +1,1167 @@
+/*
+ * sound/sequencer.c
+ *
+ * The sequencer personality manager.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#define SEQUENCER_C
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#ifndef EXCLUDE_SEQUENCER
+
+static int sequencer_ok = 0;
+
+DEFINE_WAIT_QUEUE (seq_sleeper, seq_sleep_flag);
+/* DEFINE_WAIT_QUEUE (midi_sleeper, midi_sleep_flag); */
+#define midi_sleeper seq_sleeper
+#define midi_sleep_flag seq_sleep_flag
+
+static int midi_opened[MAX_MIDI_DEV] =
+{0}; /* 1 if the process has opened MIDI */
+static int midi_written[MAX_MIDI_DEV] =
+{0};
+
+unsigned long seq_time = 0; /* Reference point for the timer */
+
+#include "tuning.h"
+
+#define EV_SZ 8
+#define IEV_SZ 4
+static unsigned char *queue = NULL; /* SEQ_MAX_QUEUE * EV_SZ bytes */
+static unsigned char *iqueue = NULL; /* SEQ_MAX_QUEUE * IEV_SZ bytes */
+
+static volatile int qhead = 0, qtail = 0, qlen = 0;
+static volatile int iqhead = 0, iqtail = 0, iqlen = 0;
+static volatile int seq_playing = 0;
+static int sequencer_busy = 0;
+static int output_treshold;
+static unsigned synth_open_mask;
+
+static int seq_queue (unsigned char *note);
+static void seq_startplay (void);
+static int seq_sync (void);
+static void seq_reset (void);
+static int pmgr_present[MAX_SYNTH_DEV] =
+{0};
+
+#if MAX_SYNTH_DEV > 15
+#error Too many synthesizer devices
+#endif
+
+int
+sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ int c = count, p = 0;
+
+ dev = dev >> 4;
+
+ if (dev) /* Patch manager device */
+ return pmgr_read (dev - 1, file, buf, count);
+
+ while (c > 3)
+ {
+ if (!iqlen)
+ {
+ if (c != count) /* Some data has been received */
+ return count - c; /* Return what we have */
+
+ DO_SLEEP (midi_sleeper, midi_sleep_flag, 0);
+
+ if (!iqlen)
+ return count - c;
+ }
+
+ COPY_TO_USER (buf, p, &iqueue[iqhead * IEV_SZ], IEV_SZ);
+ p += 4;
+ c -= 4;
+
+ iqhead = (iqhead + 1) % SEQ_MAX_QUEUE;
+ iqlen--;
+ }
+
+ return count - c;
+}
+
+static void
+sequencer_midi_output (int dev)
+{
+ /* Currently NOP */
+}
+
+static void
+copy_to_input (unsigned char *event)
+{
+ unsigned long flags;
+
+ if (iqlen >= (SEQ_MAX_QUEUE - 1))
+ return; /* Overflow */
+
+ memcpy (&iqueue[iqtail * IEV_SZ], event, IEV_SZ);
+ iqlen++;
+ iqtail = (iqtail + 1) % SEQ_MAX_QUEUE;
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (midi_sleeper, midi_sleep_flag))
+ {
+ WAKE_UP (midi_sleeper, midi_sleep_flag);
+ }
+ RESTORE_INTR (flags);
+}
+
+static void
+sequencer_midi_input (int dev, unsigned char data)
+{
+ int tstamp;
+ unsigned char event[4];
+
+ if (data == 0xfe) /* Active sensing */
+ return; /* Ignore */
+
+ tstamp = GET_TIME () - seq_time; /* Time since open() */
+ tstamp = (tstamp << 8) | SEQ_WAIT;
+
+ copy_to_input ((unsigned char *) &tstamp);
+
+ event[0] = SEQ_MIDIPUTC;
+ event[1] = data;
+ event[2] = dev;
+ event[3] = 0;
+
+ copy_to_input (event);
+}
+
+int
+sequencer_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ unsigned char event[EV_SZ], ev_code;
+ int p = 0, c, ev_size;
+ int err;
+ int mode = file->mode & O_ACCMODE;
+
+ dev = dev >> 4;
+
+ DEB (printk ("sequencer_write(dev=%d, count=%d)\n", dev, count));
+
+ if (mode == OPEN_READ)
+ return RET_ERROR (EIO);
+
+ if (dev) /* Patch manager device */
+ return pmgr_write (dev - 1, file, buf, count);
+
+ c = count;
+
+ while (c >= 4)
+ {
+ COPY_FROM_USER (event, buf, p, 4);
+ ev_code = event[0];
+
+ if (ev_code == SEQ_FULLSIZE)
+ {
+ int err;
+
+ dev = *(unsigned short *) &event[2];
+ if (dev < 0 || dev >= num_synths)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)))
+ return RET_ERROR (ENXIO);
+
+ err = synth_devs[dev]->load_patch (dev, *(short *) &event[0], buf, p + 4, c, 0);
+ if (err < 0)
+ return err;
+
+ return err;
+ }
+
+ if (ev_code == SEQ_EXTENDED || ev_code == SEQ_PRIVATE)
+ {
+
+ ev_size = 8;
+
+ if (c < ev_size)
+ {
+ if (!seq_playing)
+ seq_startplay ();
+ return count - c;
+ }
+
+ COPY_FROM_USER (&event[4], buf, p + 4, 4);
+
+ }
+ else
+ ev_size = 4;
+
+ if (event[0] == SEQ_MIDIPUTC)
+ {
+
+ if (!midi_opened[event[2]])
+ {
+ int mode;
+ int dev = event[2];
+
+ if (dev >= num_midis)
+ {
+ printk ("Sequencer Error: Nonexistent MIDI device %d\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ mode = file->mode & O_ACCMODE;
+
+ if ((err = midi_devs[dev]->open (dev, mode,
+ sequencer_midi_input, sequencer_midi_output)) < 0)
+ {
+ seq_reset ();
+ printk ("Sequencer Error: Unable to open Midi #%d\n", dev);
+ return err;
+ }
+
+ midi_opened[dev] = 1;
+ }
+
+ }
+
+ if (!seq_queue (event))
+ {
+
+ if (!seq_playing)
+ seq_startplay ();
+ return count - c;
+ }
+
+ p += ev_size;
+ c -= ev_size;
+ }
+
+ if (!seq_playing)
+ seq_startplay ();
+
+ return count;
+}
+
+static int
+seq_queue (unsigned char *note)
+{
+
+ /* Test if there is space in the queue */
+
+ if (qlen >= SEQ_MAX_QUEUE)
+ if (!seq_playing)
+ seq_startplay (); /* Give chance to drain the queue */
+
+ if (qlen >= SEQ_MAX_QUEUE && !SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ {
+ /* Sleep until there is enough space on the queue */
+ DO_SLEEP (seq_sleeper, seq_sleep_flag, 0);
+ }
+
+ if (qlen >= SEQ_MAX_QUEUE)
+ return 0; /* To be sure */
+
+ memcpy (&queue[qtail * EV_SZ], note, EV_SZ);
+
+ qtail = (qtail + 1) % SEQ_MAX_QUEUE;
+ qlen++;
+
+ return 1;
+}
+
+static int
+extended_event (unsigned char *q)
+{
+ int dev = q[2];
+
+ if (dev < 0 || dev >= num_synths)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)))
+ return RET_ERROR (ENXIO);
+
+ switch (q[1])
+ {
+ case SEQ_NOTEOFF:
+ synth_devs[dev]->kill_note (dev, q[3], q[5]);
+ break;
+
+ case SEQ_NOTEON:
+ if (q[4] > 127 && q[4] != 255)
+ return 0;
+
+ synth_devs[dev]->start_note (dev, q[3], q[4], q[5]);
+ break;
+
+ case SEQ_PGMCHANGE:
+ synth_devs[dev]->set_instr (dev, q[3], q[4]);
+ break;
+
+ case SEQ_AFTERTOUCH:
+ synth_devs[dev]->aftertouch (dev, q[3], q[4]);
+ break;
+
+ case SEQ_BALANCE:
+ synth_devs[dev]->panning (dev, q[3], (char) q[4]);
+ break;
+
+ case SEQ_CONTROLLER:
+ synth_devs[dev]->controller (dev, q[3], q[4], *(short *) &q[5]);
+ break;
+
+ case SEQ_VOLMODE:
+ synth_devs[dev]->volume_method (dev, q[3]);
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return 0;
+}
+
+static void
+seq_startplay (void)
+{
+ int this_one;
+ unsigned long *delay;
+ unsigned char *q;
+
+ while (qlen > 0)
+ {
+ qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE;
+ qlen--;
+
+ q = &queue[this_one * EV_SZ];
+
+ switch (q[0])
+ {
+ case SEQ_NOTEOFF:
+ if (synth_open_mask & (1 << 0))
+ if (synth_devs[0])
+ synth_devs[0]->kill_note (0, q[1], q[3]);
+ break;
+
+ case SEQ_NOTEON:
+ if (q[4] < 128 || q[4] == 255)
+ if (synth_open_mask & (1 << 0))
+ if (synth_devs[0])
+ synth_devs[0]->start_note (0, q[1], q[2], q[3]);
+ break;
+
+ case SEQ_WAIT:
+ delay = (unsigned long *) q; /* Bytes 1 to 3 are containing the
+ * delay in GET_TIME() */
+ *delay = (*delay >> 8) & 0xffffff;
+
+ if (*delay > 0)
+ {
+ long time;
+
+ seq_playing = 1;
+ time = *delay;
+
+ request_sound_timer (time);
+
+ if ((SEQ_MAX_QUEUE - qlen) >= output_treshold)
+ {
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ {
+ WAKE_UP (seq_sleeper, seq_sleep_flag);
+ }
+ RESTORE_INTR (flags);
+ }
+ return; /* Stop here. Timer routine will continue
+ * playing after the delay */
+ }
+ break;
+
+ case SEQ_PGMCHANGE:
+ if (synth_open_mask & (1 << 0))
+ if (synth_devs[0])
+ synth_devs[0]->set_instr (0, q[1], q[2]);
+ break;
+
+ case SEQ_SYNCTIMER: /* Reset timer */
+ seq_time = GET_TIME ();
+ break;
+
+ case SEQ_MIDIPUTC: /* Put a midi character */
+ if (midi_opened[q[2]])
+ {
+ int dev;
+
+ dev = q[2];
+
+ if (!midi_devs[dev]->putc (dev, q[1]))
+ {
+ /*
+ * Output FIFO is full. Wait one timer cycle and try again.
+ */
+
+ qlen++;
+ qhead = this_one; /* Restore queue */
+ seq_playing = 1;
+ request_sound_timer (-1);
+ return;
+ }
+ else
+ midi_written[dev] = 1;
+ }
+ break;
+
+ case SEQ_ECHO:
+ copy_to_input (q); /* Echo back to the process */
+ break;
+
+ case SEQ_PRIVATE:
+ if (q[1] < num_synths)
+ synth_devs[q[1]]->hw_control (q[1], q);
+ break;
+
+ case SEQ_EXTENDED:
+ extended_event (q);
+ break;
+
+ default:;
+ }
+
+ }
+
+ seq_playing = 0;
+
+ if ((SEQ_MAX_QUEUE - qlen) >= output_treshold)
+ {
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ {
+ WAKE_UP (seq_sleeper, seq_sleep_flag);
+ }
+ RESTORE_INTR (flags);
+ }
+
+}
+
+int
+sequencer_open (int dev, struct fileinfo *file)
+{
+ int retval, mode, i;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+
+ DEB (printk ("sequencer_open(dev=%d)\n", dev));
+
+ if (!sequencer_ok)
+ {
+ printk ("Soundcard: Sequencer not initialized\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if (dev) /* Patch manager device */
+ {
+ int err;
+
+ dev--;
+ if (pmgr_present[dev])
+ return RET_ERROR (EBUSY);
+ if ((err = pmgr_open (dev)) < 0)
+ return err; /* Failed */
+
+ pmgr_present[dev] = 1;
+ return err;
+ }
+
+ if (sequencer_busy)
+ {
+ printk ("Sequencer busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ if (!(num_synths + num_midis))
+ return RET_ERROR (ENXIO);
+
+ synth_open_mask = 0;
+
+ if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+ for (i = 0; i < num_synths; i++) /* Open synth devices */
+ if (synth_devs[i]->open (i, mode) < 0)
+ printk ("Sequencer: Warning! Cannot open synth device #%d\n", i);
+ else
+ synth_open_mask |= (1 << i);
+
+ seq_time = GET_TIME ();
+
+ for (i = 0; i < num_midis; i++)
+ {
+ midi_opened[i] = 0;
+ midi_written[i] = 0;
+ }
+
+ if (mode == OPEN_READ || mode == OPEN_READWRITE)
+ { /* Initialize midi input devices */
+ if (!num_midis)
+ {
+ printk ("Sequencer: No Midi devices. Input not possible\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ for (i = 0; i < num_midis; i++)
+ {
+ if ((retval = midi_devs[i]->open (i, mode,
+ sequencer_midi_input, sequencer_midi_output)) >= 0)
+ midi_opened[i] = 1;
+ }
+ }
+
+ sequencer_busy = 1;
+ RESET_WAIT_QUEUE (seq_sleeper, seq_sleep_flag);
+ RESET_WAIT_QUEUE (midi_sleeper, midi_sleep_flag);
+ output_treshold = SEQ_MAX_QUEUE / 2;
+
+ for (i = 0; i < num_synths; i++)
+ if (pmgr_present[i])
+ pmgr_inform (i, PM_E_OPENED, 0, 0, 0, 0);
+
+ return 0;
+}
+
+void
+seq_drain_midi_queues (void)
+{
+ int i, n;
+
+ /*
+ * Give the Midi drivers time to drain their output queues
+ */
+
+ n = 1;
+
+ while (!PROCESS_ABORTING (midi_sleeper, midi_sleep_flag) && n)
+ {
+ n = 0;
+
+ for (i = 0; i < num_midis; i++)
+ if (midi_opened[i] && midi_written[i])
+ if (midi_devs[i]->buffer_status != NULL)
+ if (midi_devs[i]->buffer_status (i))
+ n++;
+
+ /*
+ * Let's have a delay
+ */
+ if (n)
+ {
+ DO_SLEEP (seq_sleeper, seq_sleep_flag, HZ / 10);
+ }
+ }
+}
+
+void
+sequencer_release (int dev, struct fileinfo *file)
+{
+ int i;
+ int mode = file->mode & O_ACCMODE;
+
+ dev = dev >> 4;
+
+ DEB (printk ("sequencer_release(dev=%d)\n", dev));
+
+ if (dev) /* Patch manager device */
+ {
+ dev--;
+ pmgr_release (dev);
+ pmgr_present[dev] = 0;
+ return;
+ }
+
+ /*
+ * Wait until the queue is empty
+ */
+
+ while (!PROCESS_ABORTING (seq_sleeper, seq_sleep_flag) && qlen)
+ {
+ seq_sync ();
+ }
+
+ if (mode != OPEN_READ)
+ seq_drain_midi_queues (); /* Ensure the output queues are empty */
+ seq_reset ();
+ if (mode != OPEN_READ)
+ seq_drain_midi_queues (); /* Flush the all notes off messages */
+
+ for (i = 0; i < num_midis; i++)
+ if (midi_opened[i])
+ midi_devs[i]->close (i);
+
+ if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+ for (i = 0; i < num_synths; i++)
+ if (synth_open_mask & (1 << i)) /* Actually opened */
+ if (synth_devs[i])
+ synth_devs[i]->close (i);
+
+ for (i = 0; i < num_synths; i++)
+ if (pmgr_present[i])
+ pmgr_inform (i, PM_E_CLOSED, 0, 0, 0, 0);
+
+ sequencer_busy = 0;
+}
+
+static int
+seq_sync (void)
+{
+ if (qlen && !seq_playing && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag))
+ seq_startplay ();
+
+ if (qlen && !SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) /* Queue not empty */
+ {
+ DO_SLEEP (seq_sleeper, seq_sleep_flag, 0);
+ }
+
+ return qlen;
+}
+
+static void
+midi_outc (int dev, unsigned char data)
+{
+ /*
+ * NOTE! Calls sleep(). Don't call this from interrupt.
+ */
+
+ int n;
+
+ /* This routine sends one byte to the Midi channel. */
+ /* If the output Fifo is full, it waits until there */
+ /* is space in the queue */
+
+ n = 300; /* Timeout in jiffies */
+
+ while (n && !midi_devs[dev]->putc (dev, data))
+ {
+ DO_SLEEP (seq_sleeper, seq_sleep_flag, 4);
+ n--;
+ }
+}
+
+static void
+seq_reset (void)
+{
+ /*
+ * NOTE! Calls sleep(). Don't call this from interrupt.
+ */
+
+ int i, chn;
+
+ sound_stop_timer ();
+
+ qlen = qhead = qtail = 0;
+ iqlen = iqhead = iqtail = 0;
+
+ for (i = 0; i < num_synths; i++)
+ if (synth_open_mask & (1 << i))
+ if (synth_devs[i])
+ synth_devs[i]->reset (i);
+
+ for (i = 0; i < num_midis; i++)
+ if (midi_written[i]) /* Midi used. Some notes may still be playing */
+ {
+ for (chn = 0; chn < 16; chn++)
+ {
+ midi_outc (i,
+ (unsigned char) (0xb0 + (chn & 0xff))); /* Channel msg */
+ midi_outc (i, 0x7b);/* All notes off */
+ midi_outc (i, 0); /* Dummy parameter */
+ }
+
+ midi_devs[i]->close (i);
+
+ midi_written[i] = 0;
+ midi_opened[i] = 0;
+ }
+
+ seq_playing = 0;
+
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ printk ("Sequencer Warning: Unexpected sleeping process\n");
+
+}
+
+int
+sequencer_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+ int midi_dev, orig_dev;
+ int mode = file->mode & O_ACCMODE;
+
+ orig_dev = dev = dev >> 4;
+
+ switch (cmd)
+ {
+
+ case SNDCTL_SEQ_SYNC:
+ if (dev) /* Patch manager */
+ return RET_ERROR (EIO);
+
+ if (mode == OPEN_READ)
+ return 0;
+ while (qlen && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag))
+ seq_sync ();
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_RESET:
+ if (dev) /* Patch manager */
+ return RET_ERROR (EIO);
+
+ seq_reset ();
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_TESTMIDI:
+ if (dev) /* Patch manager */
+ return RET_ERROR (EIO);
+
+ midi_dev = IOCTL_IN (arg);
+ if (midi_dev >= num_midis)
+ return RET_ERROR (ENXIO);
+
+ if (!midi_opened[midi_dev])
+ {
+ int err, mode;
+
+ mode = file->mode & O_ACCMODE;
+ if ((err = midi_devs[midi_dev]->open (midi_dev, mode,
+ sequencer_midi_input,
+ sequencer_midi_output)) < 0)
+ return err;
+ }
+
+ midi_opened[midi_dev] = 1;
+
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_GETINCOUNT:
+ if (dev) /* Patch manager */
+ return RET_ERROR (EIO);
+
+ if (mode == OPEN_WRITE)
+ return 0;
+ return IOCTL_OUT (arg, iqlen);
+ break;
+
+ case SNDCTL_SEQ_GETOUTCOUNT:
+
+ if (mode == OPEN_READ)
+ return 0;
+ return IOCTL_OUT (arg, SEQ_MAX_QUEUE - qlen);
+ break;
+
+ case SNDCTL_SEQ_CTRLRATE:
+ if (dev) /* Patch manager */
+ return RET_ERROR (EIO);
+
+ /* If *arg == 0, just return the current rate */
+ return IOCTL_OUT (arg, HZ);
+ break;
+
+ case SNDCTL_SEQ_RESETSAMPLES:
+ dev = IOCTL_IN (arg);
+ if (dev < 0 || dev >= num_synths)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+ return RET_ERROR (EBUSY);
+
+ if (!orig_dev && pmgr_present[dev])
+ pmgr_inform (dev, PM_E_PATCH_RESET, 0, 0, 0, 0);
+
+ return synth_devs[dev]->ioctl (dev, cmd, arg);
+ break;
+
+ case SNDCTL_SEQ_NRSYNTHS:
+ return IOCTL_OUT (arg, num_synths);
+ break;
+
+ case SNDCTL_SEQ_NRMIDIS:
+ return IOCTL_OUT (arg, num_midis);
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ {
+ int dev = IOCTL_IN (arg);
+
+ if (dev < 0 || dev >= num_synths)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+ return RET_ERROR (EBUSY);
+
+ return IOCTL_OUT (arg, synth_devs[dev]->ioctl (dev, cmd, arg));
+ }
+ break;
+
+ case SNDCTL_FM_4OP_ENABLE:
+ {
+ int dev = IOCTL_IN (arg);
+
+ if (dev < 0 || dev >= num_synths)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)))
+ return RET_ERROR (ENXIO);
+
+ synth_devs[dev]->ioctl (dev, cmd, arg);
+ return 0;
+ }
+ break;
+
+ case SNDCTL_SYNTH_INFO:
+ {
+ struct synth_info inf;
+ int dev;
+
+ IOCTL_FROM_USER ((char *) &inf, (char *) arg, 0, sizeof (inf));
+ dev = inf.device;
+
+ if (dev < 0 || dev >= num_synths)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+ return RET_ERROR (EBUSY);
+
+ return synth_devs[dev]->ioctl (dev, cmd, arg);
+ }
+ break;
+
+ case SNDCTL_MIDI_INFO:
+ {
+ struct midi_info inf;
+ int dev;
+
+ IOCTL_FROM_USER ((char *) &inf, (char *) arg, 0, sizeof (inf));
+ dev = inf.device;
+
+ if (dev < 0 || dev >= num_midis)
+ return RET_ERROR (ENXIO);
+
+ IOCTL_TO_USER ((char *) arg, 0, (char *) &(midi_devs[dev]->info), sizeof (inf));
+ return 0;
+ }
+ break;
+
+ case SNDCTL_PMGR_IFACE:
+ {
+ struct patmgr_info *inf;
+ int dev, err;
+
+ inf = (struct patmgr_info *) KERNEL_MALLOC (sizeof (*inf));
+
+ IOCTL_FROM_USER ((char *) inf, (char *) arg, 0, sizeof (*inf));
+ dev = inf->device;
+
+ if (dev < 0 || dev >= num_synths)
+ {
+ KERNEL_FREE (inf);
+ return RET_ERROR (ENXIO);
+ }
+
+ if (!synth_devs[dev]->pmgr_interface)
+ {
+ KERNEL_FREE (inf);
+ return RET_ERROR (ENXIO);
+ }
+
+ if ((err = synth_devs[dev]->pmgr_interface (dev, inf)) == -1)
+ {
+ KERNEL_FREE (inf);
+ return err;
+ }
+
+ IOCTL_TO_USER ((char *) arg, 0, (char *) inf, sizeof (*inf));
+ KERNEL_FREE (inf);
+ return 0;
+ }
+ break;
+
+ case SNDCTL_PMGR_ACCESS:
+ {
+ struct patmgr_info *inf;
+ int dev, err;
+
+ inf = (struct patmgr_info *) KERNEL_MALLOC (sizeof (*inf));
+
+ IOCTL_FROM_USER ((char *) inf, (char *) arg, 0, sizeof (*inf));
+ dev = inf->device;
+
+ if (dev < 0 || dev >= num_synths)
+ {
+ KERNEL_FREE (inf);
+ return RET_ERROR (ENXIO);
+ }
+
+ if (!pmgr_present[dev])
+ {
+ KERNEL_FREE (inf);
+ return RET_ERROR (ESRCH);
+ }
+
+ if ((err = pmgr_access (dev, inf)) < 0)
+ {
+ KERNEL_FREE (inf);
+ return err;
+ }
+
+ IOCTL_TO_USER ((char *) arg, 0, (char *) inf, sizeof (*inf));
+ KERNEL_FREE (inf);
+ return 0;
+ }
+ break;
+
+ case SNDCTL_SEQ_TRESHOLD:
+ {
+ int tmp = IOCTL_IN (arg);
+
+ if (dev) /* Patch manager */
+ return RET_ERROR (EIO);
+
+ if (tmp < 1)
+ tmp = 1;
+ if (tmp >= SEQ_MAX_QUEUE)
+ tmp = SEQ_MAX_QUEUE - 1;
+ output_treshold = tmp;
+ return 0;
+ }
+ break;
+
+ default:
+ if (dev) /* Patch manager */
+ return RET_ERROR (EIO);
+
+ if (mode == OPEN_READ)
+ return RET_ERROR (EIO);
+
+ if (!synth_devs[0])
+ return RET_ERROR (ENXIO);
+ if (!(synth_open_mask & (1 << 0)))
+ return RET_ERROR (ENXIO);
+ return synth_devs[0]->ioctl (0, cmd, arg);
+ break;
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+#ifdef ALLOW_SELECT
+int
+sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * wait)
+{
+ dev = dev >> 4;
+
+ switch (sel_type)
+ {
+ case SEL_IN:
+ if (!iqlen)
+ {
+ select_wait (&midi_sleeper, wait);
+ return 0;
+ }
+ return 1;
+
+ break;
+
+ case SEL_OUT:
+ if (qlen >= SEQ_MAX_QUEUE)
+ {
+ select_wait (&seq_sleeper, wait);
+ return 0;
+ }
+ return 1;
+ break;
+
+ case SEL_EX:
+ return 0;
+ }
+
+ return 0;
+}
+
+#endif
+
+void
+sequencer_timer (void)
+{
+ seq_startplay ();
+}
+
+int
+note_to_freq (int note_num)
+{
+
+ /*
+ * This routine converts a midi note to a frequency (multiplied by 1000)
+ */
+
+ int note, octave, note_freq;
+ int notes[] =
+ {
+ 261632, 277189, 293671, 311132, 329632, 349232,
+ 369998, 391998, 415306, 440000, 466162, 493880
+ }; /* Note freq*1000 for octave 5 */
+
+#define BASE_OCTAVE 5
+
+ octave = note_num / 12;
+ note = note_num % 12;
+
+ note_freq = notes[note];
+
+ if (octave < BASE_OCTAVE)
+ note_freq >>= (BASE_OCTAVE - octave);
+ else if (octave > BASE_OCTAVE)
+ note_freq <<= (octave - BASE_OCTAVE);
+
+ /* note_freq >>= 1; */
+
+ return note_freq;
+}
+
+unsigned long
+compute_finetune (unsigned long base_freq, int bend, int range)
+{
+ unsigned long amount;
+ int negative, semitones, cents, multiplier = 1;
+
+ if (!bend)
+ return base_freq;
+ if (!range)
+ return base_freq;
+
+ if (!base_freq)
+ return base_freq;
+
+ if (range >= 8192)
+ range = 8191;
+
+ bend = bend * range / 8192;
+ if (!bend)
+ return base_freq;
+
+ negative = bend < 0 ? 1 : 0;
+
+ if (bend < 0)
+ bend *= -1;
+ if (bend > range)
+ bend = range;
+
+ /*
+ if (bend > 2399)
+ bend = 2399;
+ */
+ while (bend > 2399)
+ {
+ multiplier *= 4;
+ bend -= 2400;
+ }
+
+ semitones = bend / 100;
+ cents = bend % 100;
+
+ amount = semitone_tuning[semitones] * multiplier * cent_tuning[cents] / 10000;
+
+ if (negative)
+ return (base_freq * 10000) / amount; /* Bend down */
+ else
+ return (base_freq * amount) / 10000; /* Bend up */
+}
+
+
+long
+sequencer_init (long mem_start)
+{
+
+ sequencer_ok = 1;
+ PERMANENT_MALLOC (unsigned char *, queue, SEQ_MAX_QUEUE * EV_SZ, mem_start);
+ PERMANENT_MALLOC (unsigned char *, iqueue, SEQ_MAX_QUEUE * IEV_SZ, mem_start);
+
+ return mem_start;
+}
+
+#else
+/* Stub version */
+int
+sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+sequencer_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+sequencer_open (int dev, struct fileinfo *file)
+{
+ return RET_ERROR (ENXIO);
+}
+
+void
+sequencer_release (int dev, struct fileinfo *file)
+{
+}
+int
+sequencer_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig)
+{
+ return RET_ERROR (EIO);
+}
+
+long
+sequencer_init (long mem_start)
+{
+ return mem_start;
+}
+
+int
+sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * wait)
+{
+ return RET_ERROR (EIO);
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/sound_calls.h b/sys/i386/isa/sound/sound_calls.h
new file mode 100644
index 0000000..abc8200
--- /dev/null
+++ b/sys/i386/isa/sound/sound_calls.h
@@ -0,0 +1,208 @@
+/*
+ * DMA buffer calls
+ */
+
+int DMAbuf_open(int dev, int mode);
+int DMAbuf_release(int dev, int mode);
+int DMAbuf_read (int dev, snd_rw_buf *user_buf, int count);
+int DMAbuf_getwrbuffer(int dev, char **buf, int *size);
+int DMAbuf_getrdbuffer(int dev, char **buf, int *len);
+int DMAbuf_rmchars(int dev, int buff_no, int c);
+int DMAbuf_start_output(int dev, int buff_no, int l);
+int DMAbuf_ioctl(int dev, unsigned int cmd, unsigned int arg, int local);
+long DMAbuf_init(long mem_start);
+int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
+int DMAbuf_open_dma (int chan);
+void DMAbuf_close_dma (int chan);
+void DMAbuf_reset_dma (int chan);
+void DMAbuf_inputintr(int dev);
+void DMAbuf_outputintr(int dev, int underflow_flag);
+
+/*
+ * System calls for /dev/dsp and /dev/audio
+ */
+
+int audio_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int audio_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int audio_open (int dev, struct fileinfo *file);
+void audio_release (int dev, struct fileinfo *file);
+int audio_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg);
+int audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+long audio_init (long mem_start);
+
+/*
+ * System calls for the /dev/sequencer
+ */
+
+int sequencer_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int sequencer_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int sequencer_open (int dev, struct fileinfo *file);
+void sequencer_release (int dev, struct fileinfo *file);
+int sequencer_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg);
+int sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+long sequencer_init (long mem_start);
+void sequencer_timer(void);
+int note_to_freq(int note_num);
+unsigned long compute_finetune(unsigned long base_freq, int bend, int range);
+
+#ifdef ALLOW_SELECT
+int sequencer_select(int dev, struct fileinfo *file, int sel_type, select_table * wait);
+#endif
+
+/*
+ * System calls for the /dev/midi
+ */
+
+int MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int MIDIbuf_open (int dev, struct fileinfo *file);
+void MIDIbuf_release (int dev, struct fileinfo *file);
+int MIDIbuf_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg);
+int MIDIbuf_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count);
+long MIDIbuf_init(long mem_start);
+
+/*
+ * System calls for the generic midi interface.
+ *
+ */
+
+long CMIDI_init (long mem_start);
+int CMIDI_open (int dev, struct fileinfo *file);
+int CMIDI_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int CMIDI_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int CMIDI_close (int dev, struct fileinfo *file);
+
+/*
+ *
+ * Misc calls from various sources
+ */
+
+/* From pro_midi.c */
+
+long pro_midi_attach(long mem_start);
+int pro_midi_open(int dev, int mode);
+void pro_midi_close(int dev);
+int pro_midi_write(int dev, snd_rw_buf *uio);
+int pro_midi_read(int dev, snd_rw_buf *uio);
+
+/* From soundcard.c */
+long soundcard_init(long mem_start);
+void tenmicrosec(void);
+void request_sound_timer (int count);
+void sound_stop_timer(void);
+int snd_ioctl_return(int *addr, int value);
+int snd_set_irq_handler (int interrupt_level, void(*hndlr)(int));
+void snd_release_irq(int vect);
+void sound_dma_malloc(int dev);
+void sound_dma_free(int dev);
+
+/* From sound_switch.c */
+int sound_read_sw (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int sound_write_sw (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int sound_open_sw (int dev, struct fileinfo *file);
+void sound_release_sw (int dev, struct fileinfo *file);
+int sound_ioctl_sw (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned long arg);
+
+/* From sb_dsp.c */
+int sb_dsp_detect (struct address_info *hw_config);
+long sb_dsp_init (long mem_start, struct address_info *hw_config);
+void sb_dsp_disable_midi(void);
+int sb_get_irq(void);
+void sb_free_irq(void);
+int sb_dsp_command (unsigned char val);
+int sb_reset_dsp (void);
+
+/* From sb16_dsp.c */
+void sb16_dsp_interrupt (int unused);
+long sb16_dsp_init(long mem_start, struct address_info *hw_config);
+int sb16_dsp_detect(struct address_info *hw_config);
+
+/* From sb16_midi.c */
+void sb16midiintr (int unit);
+long attach_sb16midi(long mem_start, struct address_info * hw_config);
+int probe_sb16midi(struct address_info *hw_config);
+
+/* From sb_midi.c */
+void sb_midi_init(int model);
+void sb_midi_interrupt(int dummy);
+
+/* From sb_mixer.c */
+void sb_setmixer (unsigned int port, unsigned int value);
+int sb_getmixer (unsigned int port);
+void sb_mixer_set_stereo(int mode);
+int sb_mixer_init(int major_model);
+
+/* From opl3.c */
+int opl3_detect (int ioaddr);
+long opl3_init(long mem_start);
+
+/* From sb_card.c */
+long attach_sb_card(long mem_start, struct address_info *hw_config);
+int probe_sb(struct address_info *hw_config);
+
+/* From adlib_card.c */
+long attach_adlib_card(long mem_start, struct address_info *hw_config);
+int probe_adlib(struct address_info *hw_config);
+
+/* From pas_card.c */
+long attach_pas_card(long mem_start, struct address_info *hw_config);
+int probe_pas(struct address_info *hw_config);
+int pas_set_intr(int mask);
+int pas_remove_intr(int mask);
+unsigned char pas_read(int ioaddr);
+void pas_write(unsigned char data, int ioaddr);
+
+/* From pas_audio.c */
+void pas_pcm_interrupt(unsigned char status, int cause);
+long pas_pcm_init(long mem_start, struct address_info *hw_config);
+
+/* From pas_mixer.c */
+int pas_init_mixer(void);
+
+/* From pas_midi.c */
+long pas_midi_init(long mem_start);
+void pas_midi_interrupt(void);
+
+/* From gus_card.c */
+long attach_gus_card(long mem_start, struct address_info * hw_config);
+int probe_gus(struct address_info *hw_config);
+int gus_set_midi_irq(int num);
+void gusintr(int);
+
+/* From gus_wave.c */
+int gus_wave_detect(int baseaddr);
+long gus_wave_init(long mem_start, int irq, int dma);
+void gus_voice_irq(void);
+unsigned char gus_read8 (int reg);
+void gus_write8(int reg, unsigned int data);
+void guswave_dma_irq(void);
+void gus_delay(void);
+int gus_default_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg);
+
+/* From gus_midi.c */
+long gus_midi_init(long mem_start);
+void gus_midi_interrupt(int dummy);
+
+/* From mpu401.c */
+long attach_mpu401(long mem_start, struct address_info * hw_config);
+int probe_mpu401(struct address_info *hw_config);
+
+/* From opl3.c */
+void enable_opl3_mode(int left, int right, int both);
+
+/* From patmgr.c */
+int pmgr_open(int dev);
+void pmgr_release(int dev);
+int pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count);
+int pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count);
+int pmgr_access(int dev, struct patmgr_info *rec);
+int pmgr_inform(int dev, int event, unsigned long parm1, unsigned long parm2,
+ unsigned long parm3, unsigned long parm4);
+
+/* From ics2101.c */
+long ics2101_mixer_init(long mem_start);
diff --git a/sys/i386/isa/sound/sound_config.h b/sys/i386/isa/sound/sound_config.h
new file mode 100644
index 0000000..0d30c12
--- /dev/null
+++ b/sys/i386/isa/sound/sound_config.h
@@ -0,0 +1,241 @@
+/* sound_config.h
+ *
+ * A driver for Soundcards, misc configuration parameters.
+ *
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "local.h"
+
+
+#undef CONFIGURE_SOUNDCARD
+#undef DYNAMIC_BUFFER
+
+#ifdef KERNEL_SOUNDCARD
+#define CONFIGURE_SOUNDCARD
+#define DYNAMIC_BUFFER
+#undef LOADABLE_SOUNDCARD
+#endif
+
+#ifdef EXCLUDE_SEQUENCER
+#ifndef EXCLUDE_MIDI
+#define EXCLUDE_MIDI
+#endif
+#ifndef EXCLUDE_YM3812
+#define EXCLUDE_YM3812
+#endif
+#ifndef EXCLUDE_OPL3
+#define EXCLUDE_OPL3
+#endif
+#endif
+
+#ifndef SND_DEFAULT_ENABLE
+#define SND_DEFAULT_ENABLE 1
+#endif
+
+/** UWM - new MIDI stuff **/
+
+#ifdef EXCLUDE_CHIP_MIDI
+#ifndef EXCLUDE_PRO_MIDI
+#define EXCLUDE_PRO_MIDI
+#endif
+#endif
+
+/** UWM - stuff **/
+
+#if defined(EXCLUDE_SEQUENCER) && defined(EXCLUDE_AUDIO)
+#undef CONFIGURE_SOUNDCARD
+#endif
+
+#ifdef CONFIGURE_SOUNDCARD
+
+/* ****** IO-address, DMA and IRQ settings ****
+
+If your card has nonstandard I/O address or IRQ number, change defines
+ for the following settings in your kernel Makefile */
+
+#ifndef SBC_BASE
+#define SBC_BASE 0x220 /* 0x220 is the factory default. */
+#endif
+
+#ifndef SBC_IRQ
+#define SBC_IRQ 7 /* IQR7 is the factory default. */
+#endif
+
+#ifndef SBC_DMA
+#define SBC_DMA 1
+#endif
+
+#ifndef SB16_DMA
+#define SB16_DMA 6
+#endif
+
+#ifndef SB16MIDI_BASE
+#define SB16MIDI_BASE 0x300
+#endif
+
+#ifndef PAS_BASE
+#define PAS_BASE 0x388
+#endif
+
+#ifndef PAS_IRQ
+#define PAS_IRQ 5
+#endif
+
+#ifndef PAS_DMA
+#define PAS_DMA 3
+#endif
+
+#ifndef GUS_BASE
+#define GUS_BASE 0x220
+#endif
+
+#ifndef GUS_IRQ
+#define GUS_IRQ 15
+#endif
+
+#ifndef GUS_MIDI_IRQ
+#define GUS_MIDI_IRQ GUS_IRQ
+#endif
+
+#ifndef GUS_DMA
+#define GUS_DMA 6
+#endif
+
+#ifndef MPU_BASE
+#define MPU_BASE 0x330
+#endif
+
+#ifndef MPU_IRQ
+#define MPU_IRQ 6
+#endif
+
+#ifndef MAX_REALTIME_FACTOR
+#define MAX_REALTIME_FACTOR 4
+#endif
+
+/************* PCM DMA buffer sizes *******************/
+
+/* If you are using high playback or recording speeds, the default buffersize
+ is too small. DSP_BUFFSIZE must be 64k or less.
+
+ A rule of thumb is 64k for PAS16, 32k for PAS+, 16k for SB Pro and
+ 4k for SB.
+
+ If you change the DSP_BUFFSIZE, don't modify this file.
+ Use the make config command instead. */
+
+#ifndef DSP_BUFFSIZE
+#define DSP_BUFFSIZE (4096)
+#endif
+
+#ifndef DSP_BUFFCOUNT
+#define DSP_BUFFCOUNT 2 /* 2 is recommended. */
+#endif
+
+#define DMA_AUTOINIT 0x10
+
+#define FM_MONO 0x388 /* This is the I/O address used by AdLib */
+
+/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the
+ driver. (There is no need to alter this) */
+#define SEQ_MAX_QUEUE 1024
+
+#define SBFM_MAXINSTR (256) /* Size of the FM Instrument
+ bank */
+/* 128 instruments for general MIDI setup and 16 unassigned */
+
+#define SND_NDEVS 50 /* Number of supported devices */
+#define SND_DEV_CTL 0 /* Control port /dev/mixer */
+#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
+ synthesizer and MIDI output) */
+#define SND_DEV_MIDIN 2 /* MIDI input /dev/midin (not implemented
+ yet) */
+#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */
+#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */
+#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */
+#define SND_DEV_STATUS 6 /* /dev/sndstatus */
+
+/* UWM ... note add new MIDI devices here..
+ * Also do not forget to add table midi_supported[]
+ * Minor numbers for on-chip midi devices start from 15.. and
+ * should be contiguous.. viz. 15,16,17....
+ * ERROR!!!!!!!!! NO NO. Minor numbers above 15 are reserved!!!!!! Hannu
+ * Also note the max # of midi devices as MAX_MIDI_DEV
+ */
+
+#define CMIDI_DEV_PRO 15 /* Chip midi device == /dev/pro_midi */
+
+/*
+ * Add other midis here...
+ .
+ .
+ .
+ .
+ */
+
+#define DSP_DEFAULT_SPEED 8000
+
+#define ON 1
+#define OFF 0
+
+#define MAX_DSP_DEV 4
+#define MAX_MIXER_DEV 2
+#define MAX_SYNTH_DEV 3
+#define MAX_MIDI_DEV 4
+
+struct fileinfo {
+ int mode; /* Open mode */
+ };
+
+struct address_info {
+ int io_base;
+ int irq;
+ int dma;
+};
+
+/*
+ * Process wakeup reasons
+ */
+#define WK_NONE 0x00
+#define WK_WAKEUP 0x01
+#define WK_TIMEOUT 0x02
+#define WK_SIGNAL 0x04
+#define WK_SLEEP 0x08
+
+#define OPEN_READ 1
+#define OPEN_WRITE 2
+#define OPEN_READWRITE 3
+
+#include "os.h"
+#include "sound_calls.h"
+#include "dev_table.h"
+
+#ifndef DEB
+#define DEB(x)
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/sound_switch.c b/sys/i386/isa/sound/sound_switch.c
new file mode 100644
index 0000000..68c7575
--- /dev/null
+++ b/sys/i386/isa/sound/sound_switch.c
@@ -0,0 +1,445 @@
+/*
+ * sound/sound_switch.c
+ *
+ * The system call switch
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+struct sbc_device
+ {
+ int usecount;
+ };
+
+static struct sbc_device sbc_devices[SND_NDEVS] =
+{
+ {0}};
+
+static int in_use = 0; /* Total # of open device files (excluding
+
+ * minor 0) */
+
+/*
+ * /dev/sndstatus -device
+ */
+static char *status_buf = NULL;
+static int status_len, status_ptr;
+static int status_busy = 0;
+
+static int
+put_status (char *s)
+{
+ int l;
+
+ for (l = 0; l < 256, s[l]; l++); /* l=strlen(s); */
+
+ if (status_len + l >= 4000)
+ return 0;
+
+ memcpy (&status_buf[status_len], s, l);
+ status_len += l;
+
+ return 1;
+}
+
+static int
+put_status_int (unsigned int val, int radix)
+{
+ int l, v;
+
+ static char hx[] = "0123456789abcdef";
+ char buf[11];
+
+ if (!val)
+ return put_status ("0");
+
+ l = 0;
+ buf[10] = 0;
+
+ while (val)
+ {
+ v = val % radix;
+ val = val / radix;
+
+ buf[9 - l] = hx[v];
+ l++;
+ }
+
+ if (status_len + l >= 4000)
+ return 0;
+
+ memcpy (&status_buf[status_len], &buf[10 - l], l);
+ status_len += l;
+
+ return 1;
+}
+
+static void
+init_status (void)
+{
+ /*
+ * Write the status information to the status_buf and update status_len.
+ * There is a limit of 4000 bytes for the data.
+ */
+
+ int i;
+
+ status_ptr = 0;
+
+ put_status ("Sound Driver:" SOUND_VERSION_STRING
+ " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY "@"
+ SOUND_CONFIG_HOST "." SOUND_CONFIG_DOMAIN ")"
+ "\n");
+
+ if (!put_status ("Config options: "))
+ return;
+ if (!put_status_int (SELECTED_SOUND_OPTIONS, 16))
+ return;
+
+ if (!put_status ("\n\nHW config: \n"))
+ return;
+
+ for (i = 0; i < (num_sound_drivers - 1); i++)
+ {
+ if (!supported_drivers[i].enabled)
+ if (!put_status ("("))
+ return;
+
+ if (!put_status ("Type "))
+ return;
+ if (!put_status_int (supported_drivers[i].card_type, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (supported_drivers[i].name))
+ return;
+ if (!put_status (" at 0x"))
+ return;
+ if (!put_status_int (supported_drivers[i].config.io_base, 16))
+ return;
+ if (!put_status (" irq "))
+ return;
+ if (!put_status_int (supported_drivers[i].config.irq, 10))
+ return;
+ if (!put_status (" drq "))
+ return;
+ if (!put_status_int (supported_drivers[i].config.dma, 10))
+ return;
+
+ if (!supported_drivers[i].enabled)
+ if (!put_status (")"))
+ return;
+
+ if (!put_status ("\n"))
+ return;
+ }
+
+ if (!put_status ("\nPCM devices:\n"))
+ return;
+
+ for (i = 0; i < num_dspdevs; i++)
+ {
+ if (!put_status_int (i, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (dsp_devs[i]->name))
+ return;
+ if (!put_status ("\n"))
+ return;
+ }
+
+ if (!put_status ("\nSynth devices:\n"))
+ return;
+
+ for (i = 0; i < num_synths; i++)
+ {
+ if (!put_status_int (i, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (synth_devs[i]->info->name))
+ return;
+ if (!put_status ("\n"))
+ return;
+ }
+
+ if (!put_status ("\nMidi devices:\n"))
+ return;
+
+ for (i = 0; i < num_midis; i++)
+ {
+ if (!put_status_int (i, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (midi_devs[i]->info.name))
+ return;
+ if (!put_status ("\n"))
+ return;
+ }
+
+ if (num_mixers)
+ {
+ if (!put_status ("\nMixer(s) installed\n"))
+ return;
+ }
+ else
+ {
+ if (!put_status ("\nNo mixers installed\n"))
+ return;
+ }
+}
+
+static int
+read_status (snd_rw_buf * buf, int count)
+{
+ /*
+ * Return at most 'count' bytes from the status_buf.
+ */
+ int l, c;
+
+ l = count;
+ c = status_len - status_ptr;
+
+ if (l > c)
+ l = c;
+ if (l <= 0)
+ return 0;
+
+ COPY_TO_USER (buf, 0, &status_buf[status_ptr], l);
+ status_ptr += l;
+
+ return l;
+}
+
+int
+sound_read_sw (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ DEB (printk ("sound_read_sw(dev=%d, count=%d)\n", dev, count));
+
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ return read_status (buf, count);
+ break;
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return audio_read (dev, file, buf, count);
+ break;
+
+ case SND_DEV_SEQ:
+ return sequencer_read (dev, file, buf, count);
+ break;
+
+#ifndef EXCLUDE_MPU401
+ case SND_DEV_MIDIN:
+ return MIDIbuf_read (dev, file, buf, count);
+#endif
+
+ default:
+ printk ("Sound: Undefined minor device %d\n", dev);
+ }
+
+ return RET_ERROR (EPERM);
+}
+
+int
+sound_write_sw (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+
+ DEB (printk ("sound_write_sw(dev=%d, count=%d)\n", dev, count));
+
+ switch (dev & 0x0f)
+ {
+
+ case SND_DEV_SEQ:
+ return sequencer_write (dev, file, buf, count);
+ break;
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return audio_write (dev, file, buf, count);
+ break;
+
+ default:
+ return RET_ERROR (EPERM);
+ }
+
+ return count;
+}
+
+int
+sound_open_sw (int dev, struct fileinfo *file)
+{
+ int retval;
+
+ DEB (printk ("sound_open_sw(dev=%d) : usecount=%d\n", dev, sbc_devices[dev].usecount));
+
+ if ((dev >= SND_NDEVS) || (dev < 0))
+ {
+ printk ("Invalid minor device %d\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ if (status_busy)
+ return RET_ERROR (EBUSY);
+ status_busy = 1;
+ if ((status_buf = (char *) KERNEL_MALLOC (4000)) == NULL)
+ return RET_ERROR (EIO);
+ status_len = status_ptr = 0;
+ init_status ();
+ break;
+
+ case SND_DEV_CTL:
+ return 0;
+ break;
+
+ case SND_DEV_SEQ:
+ if ((retval = sequencer_open (dev, file)) < 0)
+ return retval;
+ break;
+
+#ifndef EXCLUDE_MPU401
+ case SND_DEV_MIDIN:
+ if ((retval = MIDIbuf_open (dev, file)) < 0)
+ return retval;
+ break;
+#endif
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ if ((retval = audio_open (dev, file)) < 0)
+ return retval;
+ break;
+
+ default:
+ printk ("Invalid minor device %d\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ sbc_devices[dev].usecount++;
+ in_use++;
+
+ return 0;
+}
+
+void
+sound_release_sw (int dev, struct fileinfo *file)
+{
+
+ DEB (printk ("sound_release_sw(dev=%d)\n", dev));
+
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ if (status_buf)
+ KERNEL_FREE (status_buf);
+ status_buf = NULL;
+ status_busy = 0;
+ break;
+
+ case SND_DEV_CTL:
+ break;
+
+ case SND_DEV_SEQ:
+ sequencer_release (dev, file);
+ break;
+
+#ifndef EXCLUDE_MPU401
+ case SND_DEV_MIDIN:
+ MIDIbuf_release (dev, file);
+ break;
+#endif
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ audio_release (dev, file);
+ break;
+
+ default:
+ printk ("Sound error: Releasing unknown device 0x%02x\n", dev);
+ }
+
+ sbc_devices[dev].usecount--;
+ in_use--;
+}
+
+int
+sound_ioctl_sw (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned long arg)
+{
+ DEB (printk ("sound_ioctl_sw(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
+
+ switch (dev & 0x0f)
+ {
+
+ case SND_DEV_CTL:
+
+ if (!num_mixers)
+ return RET_ERROR (ENXIO);
+
+ if ((dev >> 4) >= num_mixers)
+ return RET_ERROR (ENXIO);
+
+ return mixer_devs[dev >> 4]->ioctl (dev >> 4, cmd, arg);
+ break;
+
+ case SND_DEV_SEQ:
+ return sequencer_ioctl (dev, file, cmd, arg);
+ break;
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return audio_ioctl (dev, file, cmd, arg);
+ break;
+
+#ifndef EXCLUDE_MPU401
+ case SND_DEV_MIDIN:
+ return MIDIbuf_ioctl (dev, file, cmd, arg);
+ break;
+#endif
+
+ default:
+ return RET_ERROR (EPERM);
+ break;
+ }
+
+ return RET_ERROR (EPERM);
+}
+
+#endif
diff --git a/sys/i386/isa/sound/soundcard.c b/sys/i386/isa/sound/soundcard.c
new file mode 100644
index 0000000..9691e4f
--- /dev/null
+++ b/sys/i386/isa/sound/soundcard.c
@@ -0,0 +1,394 @@
+/*
+ * sound/386bsd/soundcard.c
+ *
+ * Soundcard driver for FreeBSD.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "dev_table.h"
+
+u_int snd1_imask;
+u_int snd2_imask;
+u_int snd3_imask;
+u_int snd4_imask;
+u_int snd5_imask;
+u_int snd6_imask;
+u_int snd7_imask;
+u_int snd8_imask;
+u_int snd9_imask;
+
+#define FIX_RETURN(ret) {if ((ret)<0) return -(ret); else return 0;}
+
+static int timer_running = 0;
+
+static int soundcards_installed = 0; /* Number of installed
+ * soundcards */
+static int soundcard_configured = 0;
+extern char *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT];
+extern unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT];
+extern int snd_raw_count[MAX_DSP_DEV];
+
+static struct fileinfo files[SND_NDEVS];
+
+int sndprobe (struct isa_device *dev);
+int sndattach (struct isa_device *dev);
+int sndopen (dev_t dev, int flags);
+int sndclose (dev_t dev, int flags);
+int sndioctl (dev_t dev, int cmd, caddr_t arg, int mode);
+int sndread (int dev, struct uio *uio);
+int sndwrite (int dev, struct uio *uio);
+int sndselect (int dev, int rw);
+static void sound_mem_init(void);
+
+unsigned
+long
+get_time(void)
+{
+extern struct timeval time;
+struct timeval timecopy;
+int x;
+
+ x = splclock();
+ timecopy = time;
+ splx(x);
+ return timecopy.tv_usec/(1000000/HZ) +
+ (unsigned long)timecopy.tv_sec*HZ;
+}
+
+
+int
+sndread (int dev, struct uio *buf)
+{
+ int count = buf->uio_resid;
+
+ dev = minor (dev);
+
+ FIX_RETURN (sound_read_sw (dev, &files[dev], buf, count));
+}
+
+int
+sndwrite (int dev, struct uio *buf)
+{
+ int count = buf->uio_resid;
+
+ dev = minor (dev);
+
+ FIX_RETURN (sound_write_sw (dev, &files[dev], buf, count));
+}
+
+int
+sndopen (dev_t dev, int flags)
+{
+ int retval;
+
+ dev = minor (dev);
+
+ if (!soundcard_configured && dev)
+ {
+ printk ("SoundCard Error: The soundcard system has not been configured\n");
+ FIX_RETURN (-ENODEV);
+ }
+
+ files[dev].mode = 0;
+
+ if (flags & FREAD && flags & FWRITE)
+ files[dev].mode = OPEN_READWRITE;
+ else if (flags & FREAD)
+ files[dev].mode = OPEN_READ;
+ else if (flags & FWRITE)
+ files[dev].mode = OPEN_WRITE;
+
+ FIX_RETURN(sound_open_sw (dev, &files[dev]));
+}
+
+int
+sndclose (dev_t dev, int flags)
+{
+
+ dev = minor (dev);
+
+ sound_release_sw(dev, &files[dev]);
+ FIX_RETURN (0);
+}
+
+int
+sndioctl (dev_t dev, int cmd, caddr_t arg, int mode)
+{
+ dev = minor (dev);
+
+ FIX_RETURN (sound_ioctl_sw (dev, &files[dev], cmd, (unsigned int) arg));
+}
+
+int
+sndselect (int dev, int rw)
+{
+ dev = minor (dev);
+
+ DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
+
+ FIX_RETURN (0);
+}
+
+static unsigned short
+ipri_to_irq (unsigned short ipri)
+{
+ /*
+ * Converts the ipri (bitmask) to the corresponding irq number
+ */
+ int irq;
+
+ for (irq = 0; irq < 16; irq++)
+ if (ipri == (1 << irq))
+ return irq;
+
+ return -1; /* Invalid argument */
+}
+
+int
+sndprobe (struct isa_device *dev)
+{
+ struct address_info hw_config;
+
+ hw_config.io_base = dev->id_iobase;
+ hw_config.irq = ipri_to_irq (dev->id_irq);
+ hw_config.dma = dev->id_drq;
+
+ return sndtable_probe (dev->id_unit, &hw_config);
+}
+
+int
+sndattach (struct isa_device *dev)
+{
+ int i;
+ static int dsp_initialized = 0;
+ static int midi_initialized = 0;
+ static int seq_initialized = 0;
+ static int generic_midi_initialized = 0;
+ unsigned long mem_start = 0xefffffffUL;
+ struct address_info hw_config;
+
+ hw_config.io_base = dev->id_iobase;
+ hw_config.irq = ipri_to_irq (dev->id_irq);
+ hw_config.dma = dev->id_drq;
+
+ if (dev->id_unit) /* Card init */
+ if (!sndtable_init_card (dev->id_unit, &hw_config))
+ {
+ printf (" <Driver not configured>");
+ return FALSE;
+ }
+
+ /*
+ * Init the high level sound driver
+ */
+
+ if (!(soundcards_installed = sndtable_get_cardcount ()))
+ {
+ printf (" <No such hardware>");
+ return FALSE; /* No cards detected */
+ }
+
+ printf("\n");
+
+#ifndef EXCLUDE_AUDIO
+ soundcard_configured = 1;
+ if (num_dspdevs)
+ sound_mem_init ();
+#endif
+
+ if (num_dspdevs && !dsp_initialized) /* Audio devices present */
+ {
+ dsp_initialized = 1;
+ mem_start = DMAbuf_init (mem_start);
+ mem_start = audio_init (mem_start);
+ }
+
+/** UWM stuff **/
+
+#ifndef EXCLUDE_CHIP_MIDI
+
+ if (!generic_midi_initialized)
+ {
+ generic_midi_initialized = 1;
+ mem_start = CMIDI_init (mem_start);
+ }
+
+#endif
+
+#ifndef EXCLUDE_MPU401
+ if (num_midis && !midi_initialized)
+ {
+ midi_initialized = 1;
+ mem_start = MIDIbuf_init (mem_start);
+ }
+#endif
+
+ if ((num_midis + num_synths) && !seq_initialized)
+ {
+ seq_initialized = 1;
+ mem_start = sequencer_init (mem_start);
+ }
+
+ return TRUE;
+}
+
+void
+tenmicrosec (void)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ inb (0x80);
+}
+
+#ifdef EXCLUDE_GUS
+void
+gusintr (int unit)
+{
+ return;
+}
+#endif
+
+void
+request_sound_timer (int count)
+{
+ static int current = 0;
+ int tmp = count;
+
+ if (count < 0)
+ timeout ((timeout_func_t)sequencer_timer, 0, -count);
+ else
+ {
+
+ if (count < current)
+ current = 0; /* Timer restarted */
+
+ count = count - current;
+
+ current = tmp;
+
+ if (!count)
+ count = 1;
+
+ timeout ((timeout_func_t)sequencer_timer, 0, count);
+ }
+ timer_running = 1;
+}
+
+void
+sound_stop_timer (void)
+{
+ if (timer_running)
+ untimeout ((timeout_func_t)sequencer_timer, 0);
+ timer_running = 0;
+}
+
+#ifndef EXCLUDE_AUDIO
+static void
+sound_mem_init (void)
+{
+ int i, dev;
+ unsigned long dma_pagesize;
+ static unsigned long dsp_init_mask = 0;
+
+ for (dev = 0; dev < num_dspdevs; dev++) /* Enumerate devices */
+ if (!(dsp_init_mask & (1 << dev))) /* Not already done */
+ if (sound_buffcounts[dev] > 0 && sound_dsp_dmachan[dev] > 0)
+ {
+ dsp_init_mask |= (1 << dev);
+
+ if (sound_dma_automode[dev])
+ sound_buffcounts[dev] = 1;
+
+ if (sound_dsp_dmachan[dev] > 3 && sound_buffsizes[dev] > 65536)
+ dma_pagesize = 131072; /* 128k */
+ else
+ dma_pagesize = 65536;
+
+ /* More sanity checks */
+
+ if (sound_buffsizes[dev] > dma_pagesize)
+ sound_buffsizes[dev] = dma_pagesize;
+ sound_buffsizes[dev] &= ~0xfff; /* Truncate to n*4k */
+ if (sound_buffsizes[dev] < 4096)
+ sound_buffsizes[dev] = 4096;
+
+ /* Now allocate the buffers */
+
+ for (snd_raw_count[dev] = 0; snd_raw_count[dev] < sound_buffcounts[dev]; snd_raw_count[dev]++)
+ {
+ char *tmpbuf = contigmalloc (sound_buffsizes[dev], M_DEVBUF, M_NOWAIT,
+ 0xFFFFFFul, 0ul, dma_pagesize - 1);
+
+ if (tmpbuf == NULL)
+ {
+ printk ("snd: Unable to allocate %d bytes of buffer\n",
+ sound_buffsizes[dev]);
+ return;
+ }
+
+ snd_raw_buf[dev][snd_raw_count[dev]] = tmpbuf;
+ /*
+ * Use virtual address as the physical address, since
+ * isa_dmastart performs the phys address computation.
+ */
+ snd_raw_buf_phys[dev][snd_raw_count[dev]] =
+ (unsigned long) snd_raw_buf[dev][snd_raw_count[dev]];
+ }
+ } /* for dev */
+
+}
+
+#endif
+
+struct isa_driver snddriver =
+{sndprobe, sndattach, "snd"};
+
+int
+snd_ioctl_return (int *addr, int value)
+{
+ if (value < 0)
+ return value; /* Error */
+ suword (addr, value);
+ return 0;
+}
+
+int
+snd_set_irq_handler (int interrupt_level, void(*hndlr)(int))
+{
+ return 1;
+}
+
+void
+snd_release_irq(int vect)
+{
+}
+
+#endif
diff --git a/sys/i386/isa/sound/tuning.h b/sys/i386/isa/sound/tuning.h
new file mode 100644
index 0000000..858e1fe
--- /dev/null
+++ b/sys/i386/isa/sound/tuning.h
@@ -0,0 +1,29 @@
+#ifdef SEQUENCER_C
+
+unsigned short semitone_tuning[24] =
+{
+/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983,
+/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784,
+/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755
+};
+
+unsigned short cent_tuning[100] =
+{
+/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041,
+/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087,
+/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134,
+/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181,
+/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228,
+/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275,
+/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323,
+/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371,
+/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419,
+/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467,
+/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515,
+/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564,
+/* 96 */ 10570, 10576, 10582, 10589
+};
+#else
+extern unsigned short semitone_tuning[24];
+extern unsigned short cent_tuning[100];
+#endif
diff --git a/sys/i386/isa/sound/ulaw.h b/sys/i386/isa/sound/ulaw.h
new file mode 100644
index 0000000..be9f92d
--- /dev/null
+++ b/sys/i386/isa/sound/ulaw.h
@@ -0,0 +1,69 @@
+static unsigned char ulaw_dsp[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 2,
+ 5, 9, 13, 17, 21, 25, 29, 33,
+ 37, 41, 45, 49, 53, 57, 61, 65,
+ 68, 70, 72, 74, 76, 78, 80, 82,
+ 84, 86, 88, 90, 92, 94, 96, 98,
+ 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115,
+ 115, 116, 116, 117, 117, 118, 118, 119,
+ 119, 120, 120, 121, 121, 122, 122, 123,
+ 123, 123, 124, 124, 124, 124, 125, 125,
+ 125, 125, 126, 126, 126, 126, 127, 127,
+ 127, 127, 127, 127, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 252, 248, 244, 240, 236, 232, 228, 224,
+ 220, 216, 212, 208, 204, 200, 196, 192,
+ 189, 187, 185, 183, 181, 179, 177, 175,
+ 173, 171, 169, 167, 165, 163, 161, 159,
+ 157, 156, 155, 154, 153, 152, 151, 150,
+ 149, 148, 147, 146, 145, 144, 143, 142,
+ 142, 141, 141, 140, 140, 139, 139, 138,
+ 138, 137, 137, 136, 136, 135, 135, 134,
+ 134, 134, 133, 133, 133, 133, 132, 132,
+ 132, 132, 131, 131, 131, 131, 130, 130,
+ 130, 130, 130, 130, 129, 129, 129, 129,
+ 129, 129, 129, 129, 128, 128, 128, 128,
+};
+
+static unsigned char dsp_ulaw[] = {
+ 31, 31, 31, 32, 32, 32, 32, 33,
+ 33, 33, 33, 34, 34, 34, 34, 35,
+ 35, 35, 35, 36, 36, 36, 36, 37,
+ 37, 37, 37, 38, 38, 38, 38, 39,
+ 39, 39, 39, 40, 40, 40, 40, 41,
+ 41, 41, 41, 42, 42, 42, 42, 43,
+ 43, 43, 43, 44, 44, 44, 44, 45,
+ 45, 45, 45, 46, 46, 46, 46, 47,
+ 47, 47, 47, 48, 48, 49, 49, 50,
+ 50, 51, 51, 52, 52, 53, 53, 54,
+ 54, 55, 55, 56, 56, 57, 57, 58,
+ 58, 59, 59, 60, 60, 61, 61, 62,
+ 62, 63, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, 76,
+ 77, 78, 79, 81, 83, 85, 87, 89,
+ 91, 93, 95, 99, 103, 107, 111, 119,
+ 255, 247, 239, 235, 231, 227, 223, 221,
+ 219, 217, 215, 213, 211, 209, 207, 206,
+ 205, 204, 203, 202, 201, 200, 199, 198,
+ 197, 196, 195, 194, 193, 192, 191, 191,
+ 190, 190, 189, 189, 188, 188, 187, 187,
+ 186, 186, 185, 185, 184, 184, 183, 183,
+ 182, 182, 181, 181, 180, 180, 179, 179,
+ 178, 178, 177, 177, 176, 176, 175, 175,
+ 175, 175, 174, 174, 174, 174, 173, 173,
+ 173, 173, 172, 172, 172, 172, 171, 171,
+ 171, 171, 170, 170, 170, 170, 169, 169,
+ 169, 169, 168, 168, 168, 168, 167, 167,
+ 167, 167, 166, 166, 166, 166, 165, 165,
+ 165, 165, 164, 164, 164, 164, 163, 163,
+ 163, 163, 162, 162, 162, 162, 161, 161,
+ 161, 161, 160, 160, 160, 160, 159, 159,
+};
diff --git a/sys/i386/isa/spkr.c b/sys/i386/isa/spkr.c
new file mode 100644
index 0000000..d273f31
--- /dev/null
+++ b/sys/i386/isa/spkr.c
@@ -0,0 +1,541 @@
+/*
+ * spkr.c -- device driver for console speaker
+ *
+ * v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993
+ * modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su>
+ *
+ * $Id: spkr.c,v 1.7 1994/01/25 23:04:27 ache Exp $
+ */
+
+#include "speaker.h"
+
+#if NSPEAKER > 0
+
+#include "param.h"
+#include "systm.h"
+#include "kernel.h"
+#include "errno.h"
+#include "buf.h"
+#include "uio.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/timerreg.h"
+#include "machine/speaker.h"
+
+/**************** MACHINE DEPENDENT PART STARTS HERE *************************
+ *
+ * This section defines a function tone() which causes a tone of given
+ * frequency and duration from the 80x86's console speaker.
+ * Another function endtone() is defined to force sound off, and there is
+ * also a rest() entry point to do pauses.
+ *
+ * Audible sound is generated using the Programmable Interval Timer (PIT) and
+ * Programmable Peripheral Interface (PPI) attached to the 80x86's speaker. The
+ * PPI controls whether sound is passed through at all; the PIT's channel 2 is
+ * used to generate clicks (a square wave) of whatever frequency is desired.
+ */
+
+/*
+ * PIT and PPI port addresses and control values
+ *
+ * Most of the magic is hidden in the TIMER_PREP value, which selects PIT
+ * channel 2, frequency LSB first, square-wave mode and binary encoding.
+ * The encoding is as follows:
+ *
+ * +----------+----------+---------------+-----+
+ * | 1 0 | 1 1 | 0 1 1 | 0 |
+ * | SC1 SC0 | RW1 RW0 | M2 M1 M0 | BCD |
+ * +----------+----------+---------------+-----+
+ * Counter Write Mode 3 Binary
+ * Channel 2 LSB first, (Square Wave) Encoding
+ * MSB second
+ */
+#define PPI_SPKR 0x03 /* turn these PPI bits on to pass sound */
+#define PIT_MODE 0xB6 /* set timer mode for sound generation */
+
+/*
+ * Magic numbers for timer control.
+ */
+#define TIMER_CLK 1193180L /* corresponds to 18.2 MHz tick rate */
+
+#define SPKRPRI PSOCK
+static char endtone, endrest;
+
+static void tone(thz, ticks)
+/* emit tone of frequency thz for given number of ticks */
+unsigned int thz, ticks;
+{
+ unsigned int divisor = TIMER_CLK / thz;
+ int sps;
+
+#ifdef DEBUG
+ (void) printf("tone: thz=%d ticks=%d\n", thz, ticks);
+#endif /* DEBUG */
+
+ /* set timer to generate clicks at given frequency in Hertz */
+ sps = spltty();
+
+ if (acquire_timer2(PIT_MODE)) {
+ /* enter list of waiting procs ??? */
+ return;
+ }
+ outb(TIMER_CNTR2, (divisor & 0xff)); /* send lo byte */
+ outb(TIMER_CNTR2, (divisor >> 8)); /* send hi byte */
+ splx(sps);
+
+ /* turn the speaker on */
+ outb(IO_PPI, inb(IO_PPI) | PPI_SPKR);
+
+ /*
+ * Set timeout to endtone function, then give up the timeslice.
+ * This is so other processes can execute while the tone is being
+ * emitted.
+ */
+ (void) tsleep((caddr_t)&endtone, SPKRPRI | PCATCH, "spkrtn", ticks);
+ outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR);
+ release_timer2();
+}
+
+static void rest(ticks)
+/* rest for given number of ticks */
+int ticks;
+{
+ /*
+ * Set timeout to endrest function, then give up the timeslice.
+ * This is so other processes can execute while the rest is being
+ * waited out.
+ */
+#ifdef DEBUG
+ (void) printf("rest: %d\n", ticks);
+#endif /* DEBUG */
+ (void) tsleep((caddr_t)&endrest, SPKRPRI | PCATCH, "spkrrs", ticks);
+}
+
+/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
+ *
+ * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
+ * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave-
+ * tracking facility are added.
+ * Requires tone(), rest(), and endtone(). String play is not interruptible
+ * except possibly at physical block boundaries.
+ */
+
+typedef int bool;
+#define TRUE 1
+#define FALSE 0
+
+#define toupper(c) ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z')))
+#define isdigit(c) (((c) >= '0') && ((c) <= '9'))
+#define dtoi(c) ((c) - '0')
+
+static int octave; /* currently selected octave */
+static int whole; /* whole-note time at current tempo, in ticks */
+static int value; /* whole divisor for note time, quarter note = 1 */
+static int fill; /* controls spacing of notes */
+static bool octtrack; /* octave-tracking on? */
+static bool octprefix; /* override current octave-tracking state? */
+
+/*
+ * Magic number avoidance...
+ */
+#define SECS_PER_MIN 60 /* seconds per minute */
+#define WHOLE_NOTE 4 /* quarter notes per whole note */
+#define MIN_VALUE 64 /* the most we can divide a note by */
+#define DFLT_VALUE 4 /* default value (quarter-note) */
+#define FILLTIME 8 /* for articulation, break note in parts */
+#define STACCATO 6 /* 6/8 = 3/4 of note is filled */
+#define NORMAL 7 /* 7/8ths of note interval is filled */
+#define LEGATO 8 /* all of note interval is filled */
+#define DFLT_OCTAVE 4 /* default octave */
+#define MIN_TEMPO 32 /* minimum tempo */
+#define DFLT_TEMPO 120 /* default tempo */
+#define MAX_TEMPO 255 /* max tempo */
+#define NUM_MULT 3 /* numerator of dot multiplier */
+#define DENOM_MULT 2 /* denominator of dot multiplier */
+
+/* letter to half-tone: A B C D E F G */
+static int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
+
+/*
+ * This is the American Standard A440 Equal-Tempered scale with frequencies
+ * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
+ * our octave 0 is standard octave 2.
+ */
+#define OCTAVE_NOTES 12 /* semitones per octave */
+static int pitchtab[] =
+{
+/* C C# D D# E F F# G G# A A# B*/
+/* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123,
+/* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
+/* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
+/* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
+/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
+/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
+/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
+};
+
+static void playinit()
+{
+ octave = DFLT_OCTAVE;
+ whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
+ fill = NORMAL;
+ value = DFLT_VALUE;
+ octtrack = FALSE;
+ octprefix = TRUE; /* act as though there was an initial O(n) */
+}
+
+static void playtone(pitch, value, sustain)
+/* play tone of proper duration for current rhythm signature */
+int pitch, value, sustain;
+{
+ register int sound, silence, snum = 1, sdenom = 1;
+
+ /* this weirdness avoids floating-point arithmetic */
+ for (; sustain; sustain--)
+ {
+ /* See the BUGS section in the man page for discussion */
+ snum *= NUM_MULT;
+ sdenom *= DENOM_MULT;
+ }
+
+ if (pitch == -1)
+ rest(whole * snum / (value * sdenom));
+ else
+ {
+ sound = (whole * snum) / (value * sdenom)
+ - (whole * (FILLTIME - fill)) / (value * FILLTIME);
+ silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
+
+#ifdef DEBUG
+ (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
+ pitch, sound, silence);
+#endif /* DEBUG */
+
+ tone(pitchtab[pitch], sound);
+ if (fill != LEGATO)
+ rest(silence);
+ }
+}
+
+static int abs(n)
+int n;
+{
+ if (n < 0)
+ return(-n);
+ else
+ return(n);
+}
+
+static void playstring(cp, slen)
+/* interpret and play an item from a notation string */
+char *cp;
+size_t slen;
+{
+ int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
+
+#define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \
+ {v = v * 10 + (*++cp - '0'); slen--;}
+ for (; slen--; cp++)
+ {
+ int sustain, timeval, tempo;
+ register char c = toupper(*cp);
+
+#ifdef DEBUG
+ (void) printf("playstring: %c (%x)\n", c, c);
+#endif /* DEBUG */
+
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+
+ /* compute pitch */
+ pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
+
+ /* this may be followed by an accidental sign */
+ if (cp[1] == '#' || cp[1] == '+')
+ {
+ ++pitch;
+ ++cp;
+ slen--;
+ }
+ else if (cp[1] == '-')
+ {
+ --pitch;
+ ++cp;
+ slen--;
+ }
+
+ /*
+ * If octave-tracking mode is on, and there has been no octave-
+ * setting prefix, find the version of the current letter note
+ * closest to the last regardless of octave.
+ */
+ if (octtrack && !octprefix)
+ {
+ if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
+ {
+ ++octave;
+ pitch += OCTAVE_NOTES;
+ }
+
+ if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
+ {
+ --octave;
+ pitch -= OCTAVE_NOTES;
+ }
+ }
+ octprefix = FALSE;
+ lastpitch = pitch;
+
+ /* ...which may in turn be followed by an override time value */
+ GETNUM(cp, timeval);
+ if (timeval <= 0 || timeval > MIN_VALUE)
+ timeval = value;
+
+ /* ...and/or sustain dots */
+ for (sustain = 0; cp[1] == '.'; cp++)
+ {
+ slen--;
+ sustain++;
+ }
+
+ /* ...and/or a slur mark */
+ oldfill = fill;
+ if (cp[1] == '_')
+ {
+ fill = LEGATO;
+ ++cp;
+ slen--;
+ }
+
+ /* time to emit the actual tone */
+ playtone(pitch, timeval, sustain);
+
+ fill = oldfill;
+ break;
+
+ case 'O':
+ if (cp[1] == 'N' || cp[1] == 'n')
+ {
+ octprefix = octtrack = FALSE;
+ ++cp;
+ slen--;
+ }
+ else if (cp[1] == 'L' || cp[1] == 'l')
+ {
+ octtrack = TRUE;
+ ++cp;
+ slen--;
+ }
+ else
+ {
+ GETNUM(cp, octave);
+ if (octave >= sizeof(pitchtab) / OCTAVE_NOTES)
+ octave = DFLT_OCTAVE;
+ octprefix = TRUE;
+ }
+ break;
+
+ case '>':
+ if (octave < sizeof(pitchtab) / OCTAVE_NOTES - 1)
+ octave++;
+ octprefix = TRUE;
+ break;
+
+ case '<':
+ if (octave > 0)
+ octave--;
+ octprefix = TRUE;
+ break;
+
+ case 'N':
+ GETNUM(cp, pitch);
+ for (sustain = 0; cp[1] == '.'; cp++)
+ {
+ slen--;
+ sustain++;
+ }
+ oldfill = fill;
+ if (cp[1] == '_')
+ {
+ fill = LEGATO;
+ ++cp;
+ slen--;
+ }
+ playtone(pitch - 1, value, sustain);
+ fill = oldfill;
+ break;
+
+ case 'L':
+ GETNUM(cp, value);
+ if (value <= 0 || value > MIN_VALUE)
+ value = DFLT_VALUE;
+ break;
+
+ case 'P':
+ case '~':
+ /* this may be followed by an override time value */
+ GETNUM(cp, timeval);
+ if (timeval <= 0 || timeval > MIN_VALUE)
+ timeval = value;
+ for (sustain = 0; cp[1] == '.'; cp++)
+ {
+ slen--;
+ sustain++;
+ }
+ playtone(-1, timeval, sustain);
+ break;
+
+ case 'T':
+ GETNUM(cp, tempo);
+ if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
+ tempo = DFLT_TEMPO;
+ whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
+ break;
+
+ case 'M':
+ if (cp[1] == 'N' || cp[1] == 'n')
+ {
+ fill = NORMAL;
+ ++cp;
+ slen--;
+ }
+ else if (cp[1] == 'L' || cp[1] == 'l')
+ {
+ fill = LEGATO;
+ ++cp;
+ slen--;
+ }
+ else if (cp[1] == 'S' || cp[1] == 's')
+ {
+ fill = STACCATO;
+ ++cp;
+ slen--;
+ }
+ break;
+ }
+ }
+}
+
+/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
+ *
+ * This section implements driver hooks to run playstring() and the tone(),
+ * endtone(), and rest() functions defined above.
+ */
+
+static int spkr_active = FALSE; /* exclusion flag */
+static struct buf *spkr_inbuf; /* incoming buf */
+
+int spkropen(dev)
+dev_t dev;
+{
+#ifdef DEBUG
+ (void) printf("spkropen: entering with dev = %x\n", dev);
+#endif /* DEBUG */
+
+ if (minor(dev) != 0)
+ return(ENXIO);
+ else if (spkr_active)
+ return(EBUSY);
+ else
+ {
+#ifdef DEBUG
+ (void) printf("spkropen: about to perform play initialization\n");
+#endif /* DEBUG */
+ playinit();
+ spkr_inbuf = geteblk(DEV_BSIZE);
+ spkr_active = TRUE;
+ return(0);
+ }
+}
+
+int spkrwrite(dev, uio)
+dev_t dev;
+struct uio *uio;
+{
+#ifdef DEBUG
+ printf("spkrwrite: entering with dev = %x, count = %d\n",
+ dev, uio->uio_resid);
+#endif /* DEBUG */
+
+ if (minor(dev) != 0)
+ return(ENXIO);
+ else if (uio->uio_resid > DEV_BSIZE) /* prevent system crashes */
+ return(E2BIG);
+ else
+ {
+ unsigned n;
+ char *cp;
+ int error;
+
+ n = uio->uio_resid;
+ cp = spkr_inbuf->b_un.b_addr;
+ if (!(error = uiomove(cp, n, uio)))
+ playstring(cp, n);
+ return(error);
+ }
+}
+
+int spkrclose(dev)
+dev_t dev;
+{
+#ifdef DEBUG
+ (void) printf("spkrclose: entering with dev = %x\n", dev);
+#endif /* DEBUG */
+
+ if (minor(dev) != 0)
+ return(ENXIO);
+ else
+ {
+ wakeup((caddr_t)&endtone);
+ wakeup((caddr_t)&endrest);
+ brelse(spkr_inbuf);
+ spkr_active = FALSE;
+ return(0);
+ }
+}
+
+int spkrioctl(dev, cmd, cmdarg)
+dev_t dev;
+int cmd;
+caddr_t cmdarg;
+{
+#ifdef DEBUG
+ (void) printf("spkrioctl: entering with dev = %x, cmd = %x\n");
+#endif /* DEBUG */
+
+ if (minor(dev) != 0)
+ return(ENXIO);
+ else if (cmd == SPKRTONE)
+ {
+ tone_t *tp = (tone_t *)cmdarg;
+
+ if (tp->frequency == 0)
+ rest(tp->duration);
+ else
+ tone(tp->frequency, tp->duration);
+ return 0;
+ }
+ else if (cmd == SPKRTUNE)
+ {
+ tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg);
+ tone_t ttp;
+ int error;
+
+ for (; ; tp++) {
+ error = copyin(tp, &ttp, sizeof(tone_t));
+ if (error)
+ return(error);
+ if (ttp.duration == 0)
+ break;
+ if (ttp.frequency == 0)
+ rest(ttp.duration);
+ else
+ tone(ttp.frequency, ttp.duration);
+ }
+ return(0);
+ }
+ return(EINVAL);
+}
+
+#endif /* NSPEAKER > 0 */
+/* spkr.c ends here */
diff --git a/sys/i386/isa/syscons.c b/sys/i386/isa/syscons.c
new file mode 100644
index 0000000..a2a931c
--- /dev/null
+++ b/sys/i386/isa/syscons.c
@@ -0,0 +1,2660 @@
+/*-
+ * Copyright (c) 1992-1994 Søren Schmidt
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz and Don Ahn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from:@(#)syscons.c 1.3 940129
+ * $Id: syscons.c,v 1.46 1994/05/25 08:59:56 rgrimes Exp $
+ *
+ */
+
+#if !defined(__FreeBSD__)
+#define FAT_CURSOR
+#endif
+
+#include "param.h"
+#include <sys/systm.h>
+#include "conf.h"
+#include "ioctl.h"
+#include "proc.h"
+#include "user.h"
+#include "tty.h"
+#include "uio.h"
+#include "callout.h"
+#include "kernel.h"
+#include "syslog.h"
+#include "errno.h"
+#include "malloc.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/timerreg.h"
+#include "i386/i386/cons.h"
+#include "machine/console.h"
+#include "machine/psl.h"
+#include "machine/frame.h"
+#include "machine/pc/display.h"
+#include "iso8859.font"
+#include "kbdtables.h"
+#include "sc.h"
+
+#if NSC > 0
+
+#if !defined(NCONS)
+#define NCONS 12
+#endif
+
+/* status flags */
+#define LOCK_KEY_MASK 0x0000F
+#define LED_MASK 0x00007
+#define UNKNOWN_MODE 0x00010
+#define KBD_RAW_MODE 0x00020
+#define SWITCH_WAIT_REL 0x00040
+#define SWITCH_WAIT_ACQ 0x00080
+
+/* video hardware memory addresses */
+#define VIDEOMEM 0x000A0000
+
+/* misc defines */
+#define MAX_ESC_PAR 3
+#define TEXT80x25 1
+#define TEXT80x50 2
+#define COL 80
+#define ROW 25
+#define BELL_DURATION 5
+#define BELL_PITCH 800
+#define TIMER_FREQ 1193182 /* should be in isa.h */
+#define PCBURST 256
+
+/* defines related to hardware addresses */
+#define MONO_BASE 0x3B4 /* crt controller base mono */
+#define COLOR_BASE 0x3D4 /* crt controller base color */
+#define ATC IO_VGA+0x00 /* attribute controller */
+#define TSIDX IO_VGA+0x04 /* timing sequencer idx */
+#define TSREG IO_VGA+0x05 /* timing sequencer data */
+#define PIXMASK IO_VGA+0x06 /* pixel write mask */
+#define PALRADR IO_VGA+0x07 /* palette read address */
+#define PALWADR IO_VGA+0x08 /* palette write address */
+#define PALDATA IO_VGA+0x09 /* palette data register */
+#define GDCIDX IO_VGA+0x0E /* graph data controller idx */
+#define GDCREG IO_VGA+0x0F /* graph data controller data */
+
+/* special characters */
+#define cntlc 0x03
+#define cntld 0x04
+#define bs 0x08
+#define lf 0x0a
+#define cr 0x0d
+#define del 0x7f
+
+typedef struct term_stat {
+ int esc; /* processing escape sequence */
+ int num_param; /* # of parameters to ESC */
+ int last_param; /* last parameter # */
+ int param[MAX_ESC_PAR]; /* contains ESC parameters */
+ int cur_attr; /* current attributes */
+ int std_attr; /* normal attributes */
+ int rev_attr; /* reverse attributes */
+} term_stat;
+
+typedef struct scr_stat {
+ u_short *crt_base; /* address of screen memory */
+ u_short *scr_buf; /* buffer when off screen */
+ u_short *crtat; /* cursor address */
+ int xpos; /* current X position */
+ int ypos; /* current Y position */
+ int xsize; /* X size */
+ int ysize; /* Y size */
+ term_stat term; /* terminal emulation stuff */
+ char cursor_start; /* cursor start line # */
+ char cursor_end; /* cursor end line # */
+ u_char border; /* border color */
+ u_short bell_duration;
+ u_short bell_pitch;
+ u_short status; /* status (bitfield) */
+ u_short mode; /* mode */
+ pid_t pid; /* pid of controlling proc */
+ struct proc *proc; /* proc* of controlling proc */
+ struct vt_mode smode; /* switch mode */
+} scr_stat;
+
+typedef struct default_attr {
+ int std_attr; /* normal attributes */
+ int rev_attr; /* reverse attributes */
+} default_attr;
+
+static default_attr user_default = {
+ (FG_LIGHTGREY | BG_BLACK) << 8,
+ (FG_BLACK | BG_LIGHTGREY) << 8
+};
+
+static default_attr kernel_default = {
+ (FG_WHITE | BG_BLACK) << 8,
+ (FG_BLACK | BG_LIGHTGREY) << 8
+};
+
+#define CONSOLE_BUFFER_SIZE 1024
+int console_buffer_count;
+char console_buffer[CONSOLE_BUFFER_SIZE];
+
+static scr_stat console[NCONS];
+static scr_stat *cur_console = &console[0];
+static scr_stat *new_scp, *old_scp;
+static term_stat kernel_console;
+static default_attr *current_default;
+static int switch_in_progress = 0;
+static u_short *crtat = 0;
+static u_int crtc_addr = MONO_BASE;
+static char crtc_vga = 0;
+static u_char shfts = 0, ctls = 0, alts = 0, agrs = 0, metas = 0;
+static u_char nlkcnt = 0, clkcnt = 0, slkcnt = 0, alkcnt = 0;
+static char palette[3*256];
+static const u_int n_fkey_tab = sizeof(fkey_tab) / sizeof(*fkey_tab);
+static int cur_cursor_pos = -1;
+static char in_putc = 0;
+static char polling = 0;
+static int delayed_next_scr;
+static char saved_console = -1; /* saved console number */
+static long scrn_blank_time = 0; /* screen saver timout value */
+static int scrn_blanked = 0; /* screen saver active flag */
+static int scrn_saver = 0; /* screen saver routine */
+static long scrn_time_stamp;
+static u_char scr_map[256];
+extern int hz;
+extern struct timeval time;
+
+/* function prototypes */
+int pcprobe(struct isa_device *dev);
+int pcattach(struct isa_device *dev);
+int pcopen(dev_t dev, int flag, int mode, struct proc *p);
+int pcclose(dev_t dev, int flag, int mode, struct proc *p);
+int pcread(dev_t dev, struct uio *uio, int flag);
+int pcwrite(dev_t dev, struct uio *uio, int flag);
+int pcparam(struct tty *tp, struct termios *t);
+int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p);
+void pcxint(dev_t dev);
+void pcstart(struct tty *tp);
+void pccnprobe(struct consdev *cp);
+void pccninit(struct consdev *cp);
+void pccnputc(dev_t dev, char c);
+int pccngetc(dev_t dev);
+void scintr(int unit);
+int pcmmap(dev_t dev, int offset, int nprot);
+u_int sgetc(int noblock);
+int getchar(void);
+static void scinit(void);
+static void scput(u_char c);
+static u_int scgetc(int noblock);
+static struct tty *get_tty_ptr(dev_t dev);
+static scr_stat *get_scr_stat(dev_t dev);
+static int get_scr_num();
+static void cursor_shape(int start, int end);
+static void get_cursor_shape(int *start, int *end);
+static void cursor_pos(int force);
+static void clear_screen(scr_stat *scp);
+static int switch_scr(u_int next_scr);
+static void exchange_scr(void);
+static void move_crsr(scr_stat *scp, int x, int y);
+static void move_up(u_short *s, u_short *d, u_int len);
+static void move_down(u_short *s, u_short *d, u_int len);
+static void scan_esc(scr_stat *scp, u_char c);
+static void ansi_put(scr_stat *scp, u_char c);
+static u_char *get_fstr(u_int c, u_int *len);
+static void update_leds(int which);
+static void kbd_wait(void);
+static void kbd_cmd(u_char command);
+static void kbd_cmd2(u_char command, u_char arg);
+static int kbd_reply(void);
+static void set_mode(scr_stat *scp);
+static void set_border(int color);
+static void load_font(int segment, int size, char* font);
+static void save_palette(void);
+static void load_palette(void);
+static void change_winsize(struct tty *tp, int x, int y);
+
+
+/* available screen savers */
+
+static void none_saver(int test);
+static void blank_saver(int test);
+static void fade_saver(int test);
+static void star_saver(int test);
+static void snake_saver(int test);
+
+static const struct {
+ char *name;
+ void (*routine)();
+} screen_savers[] = {
+ { "none", none_saver }, /* 0 */
+ { "blank", blank_saver }, /* 1 */
+ { "fade", fade_saver }, /* 2 */
+ { "star", star_saver }, /* 3 */
+ { "snake", snake_saver }, /* 4 */
+};
+#define SCRN_SAVER(arg) (*screen_savers[scrn_saver].routine)(arg)
+#define NUM_SCRN_SAVERS (sizeof(screen_savers) / sizeof(screen_savers[0]))
+
+/* OS specific stuff */
+
+#if defined(NetBSD)
+#define VIRTUAL_TTY(x) pc_tty[x] ? (pc_tty[x]) : (pc_tty[x] = ttymalloc())
+#define CONSOLE_TTY pc_tty[NCONS] ? (pc_tty[NCONS]) : (pc_tty[NCONS] = ttymalloc())
+#define frametype struct trapframe
+#define eflags tf_eflags
+extern u_short *Crtat;
+struct tty *pc_tty[NCONS+1];
+int ttrstrt();
+#endif
+
+#if defined(__FreeBSD__)
+#if 0
+#define VIRTUAL_TTY(x) (pccons[x] = ttymalloc(pccons[x]))
+#define CONSOLE_TTY (pccons[NCONS] = ttymalloc(pccons[NCONS]))
+struct tty *pccons[NCONS+1];
+#else
+#define VIRTUAL_TTY(x) &pccons[x]
+#define CONSOLE_TTY &pccons[NCONS]
+struct tty pccons[NCONS+1];
+#endif
+#define timeout_t timeout_func_t
+#define frametype struct trapframe
+#define eflags tf_eflags
+#define MONO_BUF (KERNBASE+0xB0000)
+#define CGA_BUF (KERNBASE+0xB8000)
+#endif
+
+#if defined(__386BSD__) && !defined(__FreeBSD__)
+#define VIRTUAL_TTY(x) &pccons[x]
+#define CONSOLE_TTY &pccons[NCONS]
+#define frametype struct syscframe
+#define eflags sf_eflags
+#define timeout_t caddr_t
+#define MONO_BUF (0xFE0B0000)
+#define CGA_BUF (0xFE0B8000)
+struct tty pccons[NCONS+1];
+#endif
+
+#if defined(__386BSD__) || defined(__FreeBSD__)
+u_short *Crtat = (u_short *)MONO_BUF;
+void consinit(void) {scinit();}
+#include "ddb.h"
+#if NDDB > 0
+#define DDB 1
+#endif
+#endif
+
+struct isa_driver scdriver = {
+ pcprobe, pcattach, "sc",
+};
+
+
+int pcprobe(struct isa_device *dev)
+{
+ /* Enable interrupts and keyboard controller */
+ kbd_wait();
+ outb(KB_STAT, KB_WRITE);
+ kbd_cmd(0x4D);
+
+ /* Start keyboard stuff RESET */
+ for (;;) {
+ kbd_cmd(KB_RESET);
+ if (kbd_reply() == KB_ACK && /* command accepted */
+ kbd_reply() == 0xaa) /* self test passed */
+ break;
+ printf("Keyboard reset failed\n");
+ }
+ return (IO_KBDSIZE);
+}
+
+
+int pcattach(struct isa_device *dev)
+{
+ scr_stat *scp;
+ int start = -1, end = -1, i;
+
+ printf("sc%d: ", dev->id_unit);
+ if (crtc_vga)
+ if (crtc_addr == MONO_BASE)
+ printf("VGA mono");
+ else
+ printf("VGA color");
+ else
+ if (crtc_addr == MONO_BASE)
+ printf("MDA/hercules");
+ else
+ printf("CGA/EGA");
+
+ if (NCONS > 1)
+ printf(" <%d virtual consoles>\n", NCONS);
+ else
+ printf("\n");
+#if defined(FAT_CURSOR)
+ start = 0;
+ end = 18;
+ if (crtc_vga) {
+#else
+ if (crtc_vga) {
+ get_cursor_shape(&start, &end);
+#endif
+ save_palette();
+ load_font(0, 16, font_8x16);
+ load_font(1, 8, font_8x8);
+ load_font(2, 14, font_8x14);
+ }
+ current_default = &user_default;
+ for (i = 0; i < NCONS; i++) {
+ scp = &console[i];
+ scp->scr_buf = (u_short *)malloc(COL * ROW * 2, M_DEVBUF, M_NOWAIT);
+ scp->mode = TEXT80x25;
+ scp->term.esc = 0;
+ scp->term.std_attr = current_default->std_attr;
+ scp->term.rev_attr = current_default->rev_attr;
+ scp->term.cur_attr = scp->term.std_attr;
+ scp->border = BG_BLACK;
+ scp->cursor_start = start;
+ scp->cursor_end = end;
+ scp->xsize = COL;
+ scp->ysize = ROW;
+ scp->bell_pitch = BELL_PITCH;
+ scp->bell_duration = BELL_DURATION;
+ scp->status = 0;
+ scp->pid = 0;
+ scp->proc = NULL;
+ scp->smode.mode = VT_AUTO;
+ if (i > 0) {
+ scp->crt_base = scp->crtat = scp->scr_buf;
+ fillw(scp->term.cur_attr|scr_map[0x20], scp->scr_buf, COL*ROW);
+ }
+ }
+ /* get cursor going */
+#if defined(FAT_CURSOR)
+ cursor_shape(console[0].cursor_start,
+ console[0].cursor_end);
+#endif
+ cursor_pos(1);
+ return 0;
+}
+
+
+static struct tty *get_tty_ptr(dev_t dev)
+{
+ int unit = minor(dev);
+
+ if (unit > NCONS)
+ return(NULL);
+ if (unit == NCONS)
+ return(CONSOLE_TTY);
+ return(VIRTUAL_TTY(unit));
+}
+
+
+static scr_stat *get_scr_stat(dev_t dev)
+{
+ int unit = minor(dev);
+
+ if (unit > NCONS)
+ return(NULL);
+ if (unit == NCONS)
+ return(&console[0]);
+ return(&console[unit]);
+}
+
+
+static int get_scr_num()
+{
+ int i = 0;
+
+ while ((i < NCONS) && (cur_console != &console[i])) i++;
+ return i < NCONS ? i : 0;
+}
+
+int pcopen(dev_t dev, int flag, int mode, struct proc *p)
+{
+ struct tty *tp = get_tty_ptr(dev);
+
+ if (!tp)
+ return(ENXIO);
+
+ tp->t_oproc = pcstart;
+ tp->t_param = pcparam;
+ tp->t_dev = dev;
+ if (!(tp->t_state & TS_ISOPEN)) {
+ tp->t_state |= TS_WOPEN;
+ ttychars(tp);
+ tp->t_iflag = TTYDEF_IFLAG;
+ tp->t_oflag = TTYDEF_OFLAG;
+ tp->t_cflag = TTYDEF_CFLAG;
+ tp->t_lflag = TTYDEF_LFLAG;
+ tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
+ pcparam(tp, &tp->t_termios);
+ ttsetwater(tp);
+ } else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0)
+ return(EBUSY);
+ tp->t_state |= TS_CARR_ON;
+ tp->t_cflag |= CLOCAL;
+ return((*linesw[tp->t_line].l_open)(dev, tp));
+}
+
+
+int pcclose(dev_t dev, int flag, int mode, struct proc *p)
+{
+ struct tty *tp = get_tty_ptr(dev);
+ struct scr_stat *scp;
+
+ if (!tp)
+ return(ENXIO);
+ if (minor(dev) < NCONS) {
+ scp = get_scr_stat(tp->t_dev);
+ if (scp->status & SWITCH_WAIT_ACQ)
+ wakeup((caddr_t)&scp->smode);
+ scp->pid = 0;
+ scp->proc = NULL;
+ scp->smode.mode = VT_AUTO;
+ }
+ (*linesw[tp->t_line].l_close)(tp, flag);
+ ttyclose(tp);
+ return(0);
+}
+
+
+int pcread(dev_t dev, struct uio *uio, int flag)
+{
+ struct tty *tp = get_tty_ptr(dev);
+
+ if (!tp)
+ return(ENXIO);
+ return((*linesw[tp->t_line].l_read)(tp, uio, flag));
+}
+
+
+int pcwrite(dev_t dev, struct uio *uio, int flag)
+{
+ struct tty *tp = get_tty_ptr(dev);
+
+ if (!tp)
+ return(ENXIO);
+ return((*linesw[tp->t_line].l_write)(tp, uio, flag));
+}
+
+
+/*
+ * Got a console interrupt, keyboard action !
+ * Catch the character, and see who it goes to.
+ */
+void scintr(int unit)
+{
+ static struct tty *cur_tty;
+ int c, len;
+ u_char *cp;
+
+ /* make screensaver happy */
+ scrn_time_stamp = time.tv_sec;
+ if (scrn_blanked)
+ SCRN_SAVER(0);
+
+ c = scgetc(1);
+
+ cur_tty = VIRTUAL_TTY(get_scr_num());
+ if (!(cur_tty->t_state & TS_ISOPEN))
+ cur_tty = CONSOLE_TTY;
+
+ if (!(cur_tty->t_state & TS_ISOPEN) || polling)
+ return;
+
+ switch (c & 0xff00) {
+ case 0x0000: /* normal key */
+ (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty);
+ break;
+ case NOKEY: /* nothing there */
+ break;
+ case FKEY: /* function key, return string */
+ if (cp = get_fstr((u_int)c, (u_int *)&len)) {
+ while (len-- > 0)
+ (*linesw[cur_tty->t_line].l_rint)
+ (*cp++ & 0xFF, cur_tty);
+ }
+ break;
+ case MKEY: /* meta is active, prepend ESC */
+ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty);
+ (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty);
+ break;
+ }
+}
+
+
+/*
+ * Set line parameters
+ */
+int pcparam(struct tty *tp, struct termios *t)
+{
+ int cflag = t->c_cflag;
+
+ /* and copy to tty */
+ tp->t_ispeed = t->c_ispeed;
+ tp->t_ospeed = t->c_ospeed;
+ tp->t_cflag = cflag;
+ return 0;
+}
+
+
+int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
+{
+ int i, error;
+ struct tty *tp;
+ frametype *fp;
+ scr_stat *scp;
+
+ tp = get_tty_ptr(dev);
+ if (!tp)
+ return ENXIO;
+ scp = get_scr_stat(tp->t_dev);
+
+ switch (cmd) { /* process console hardware related ioctl's */
+
+ case CONS_BLANKTIME: /* set screen saver timeout (0 = no saver) */
+ scrn_blank_time = *(int*)data;
+ return 0;
+ case CONS_SSAVER: /* set screen saver */
+ {
+ register ssaver_t *sav = (ssaver_t *)data;
+ if (sav->num < 0 || sav->num >= NUM_SCRN_SAVERS)
+ return EIO;
+ SCRN_SAVER(0);
+ scrn_saver = sav->num;
+ scrn_blank_time = sav->time;
+ return 0;
+ }
+ case CONS_GSAVER: /* get screen saver info */
+ {
+ register ssaver_t *sav = (ssaver_t *)data;
+ if (sav->num < 0)
+ sav->num = scrn_saver;
+ else if (sav->num >= NUM_SCRN_SAVERS)
+ return EIO;
+ sav->time = scrn_blank_time;
+ strcpy(sav->name, screen_savers[sav->num].name);
+ return 0;
+ }
+ case CONS_80x25TEXT: /* set 80x25 text mode */
+ if (!crtc_vga)
+ return ENXIO;
+ scp->mode = TEXT80x25;
+ scp->ysize = 25;
+ free(scp->scr_buf, M_DEVBUF);
+ scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*2,
+ M_DEVBUF, M_NOWAIT);
+ if (scp != cur_console)
+ scp->crt_base = scp->scr_buf;
+ set_mode(scp);
+ clear_screen(scp);
+ change_winsize(tp, scp->xsize, scp->ysize);
+ return 0;
+
+ case CONS_80x50TEXT: /* set 80x50 text mode */
+ if (!crtc_vga)
+ return ENXIO;
+ scp->mode = TEXT80x50;
+ scp->ysize = 50;
+ free(scp->scr_buf, M_DEVBUF);
+ scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*2,
+ M_DEVBUF, M_NOWAIT);
+ if (scp != cur_console)
+ scp->crt_base = scp->scr_buf;
+ set_mode(scp);
+ clear_screen(scp);
+ change_winsize(tp, scp->xsize, scp->ysize);
+ return 0;
+
+ case CONS_GETVERS: /* get version number */
+ *(int*)data = 0x103; /* version 1.3 */
+ return 0;
+
+ case CONS_GETINFO: /* get current (virtual) console info */
+ {
+ vid_info_t *ptr = (vid_info_t*)data;
+ if (ptr->size == sizeof(struct vid_info)) {
+ ptr->m_num = get_scr_num();
+ ptr->mv_col = scp->xpos;
+ ptr->mv_row = scp->ypos;
+ ptr->mv_csz = scp->xsize;
+ ptr->mv_rsz = scp->ysize;
+ ptr->mv_norm.fore = (scp->term.std_attr & 0x0f00)>>8;
+ ptr->mv_norm.back = (scp->term.std_attr & 0xf000)>>12;
+ ptr->mv_rev.fore = (scp->term.rev_attr & 0x0f00)>>8;
+ ptr->mv_rev.back = (scp->term.rev_attr & 0xf000)>>12;
+ ptr->mv_grfc.fore = 0; /* not supported */
+ ptr->mv_grfc.back = 0; /* not supported */
+ ptr->mv_ovscan = scp->border;
+ ptr->mk_keylock = scp->status & LOCK_KEY_MASK;
+ return 0;
+ }
+ return EINVAL;
+ }
+
+ case VT_SETMODE: /* set screen switcher mode */
+ bcopy(data, &scp->smode, sizeof(struct vt_mode));
+ if (scp->smode.mode == VT_PROCESS) {
+ scp->proc = p;
+ scp->pid = scp->proc->p_pid;
+ }
+ return 0;
+
+ case VT_GETMODE: /* get screen switcher mode */
+ bcopy(&scp->smode, data, sizeof(struct vt_mode));
+ return 0;
+
+ case VT_RELDISP: /* screen switcher ioctl */
+ switch(*data) {
+ case VT_FALSE: /* user refuses to release screen, abort */
+ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) {
+ old_scp->status &= ~SWITCH_WAIT_REL;
+ switch_in_progress = 0;
+ return 0;
+ }
+ return EINVAL;
+
+ case VT_TRUE: /* user has released screen, go on */
+ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) {
+ scp->status &= ~SWITCH_WAIT_REL;
+ exchange_scr();
+ if (new_scp->smode.mode == VT_PROCESS) {
+ new_scp->status |= SWITCH_WAIT_ACQ;
+ psignal(new_scp->proc,
+ new_scp->smode.acqsig);
+ }
+ else
+ switch_in_progress = 0;
+ return 0;
+ }
+ return EINVAL;
+
+ case VT_ACKACQ: /* acquire acknowledged, switch completed */
+ if (scp == new_scp && (scp->status & SWITCH_WAIT_ACQ)) {
+ scp->status &= ~SWITCH_WAIT_ACQ;
+ switch_in_progress = 0;
+ return 0;
+ }
+ return EINVAL;
+
+ default:
+ return EINVAL;
+ }
+ /* NOT REACHED */
+
+ case VT_OPENQRY: /* return free virtual console */
+ for (i = 0; i < NCONS; i++) {
+ tp = VIRTUAL_TTY(i);
+ if (!(tp->t_state & TS_ISOPEN)) {
+ *data = i + 1;
+ return 0;
+ }
+ }
+ return EINVAL;
+
+ case VT_ACTIVATE: /* switch to screen *data */
+ return switch_scr((*data) - 1);
+
+ case VT_WAITACTIVE: /* wait for switch to occur */
+ if (*data > NCONS)
+ return EINVAL;
+ if (minor(dev) == (*data) - 1)
+ return 0;
+ if (*data == 0) {
+ if (scp == cur_console)
+ return 0;
+ while ((error=tsleep((caddr_t)&scp->smode,
+ PZERO|PCATCH, "waitvt", 0)) == ERESTART) ;
+ }
+ else
+ while ((error=tsleep(
+ (caddr_t)&console[*(data-1)].smode,
+ PZERO|PCATCH, "waitvt", 0)) == ERESTART) ;
+ return error;
+
+ case VT_GETACTIVE:
+ *data = get_scr_num()+1;
+ return 0;
+
+ case KDENABIO: /* allow io operations */
+ fp = (frametype *)p->p_md.md_regs;
+ fp->eflags |= PSL_IOPL;
+ return 0;
+
+ case KDDISABIO: /* disallow io operations (default) */
+ fp = (frametype *)p->p_md.md_regs;
+ fp->eflags &= ~PSL_IOPL;
+ return 0;
+
+ case KDSETMODE: /* set current mode of this (virtual) console */
+ switch (*data) {
+ case KD_TEXT: /* switch to TEXT (known) mode */
+ /* restore fonts & palette ! */
+ if (crtc_vga) {
+ load_font(0, 16, font_8x16);
+ load_font(1, 8, font_8x8);
+ load_font(2, 14, font_8x14);
+ load_palette();
+ }
+ /* FALL THROUGH */
+
+ case KD_TEXT1: /* switch to TEXT (known) mode */
+ /* no restore fonts & palette */
+ scp->status &= ~UNKNOWN_MODE;
+ set_mode(scp);
+ clear_screen(scp);
+ return 0;
+
+ case KD_GRAPHICS:/* switch to GRAPHICS (unknown) mode */
+ scp->status |= UNKNOWN_MODE;
+ return 0;
+ default:
+ return EINVAL;
+ }
+ /* NOT REACHED */
+
+ case KDGETMODE: /* get current mode of this (virtual) console */
+ *data = (scp->status & UNKNOWN_MODE) ? KD_GRAPHICS : KD_TEXT;
+ return 0;
+
+ case KDSBORDER: /* set border color of this (virtual) console */
+ if (!crtc_vga)
+ return ENXIO;
+ scp->border = *data;
+ if (scp == cur_console)
+ set_border(scp->border);
+ return 0;
+
+ case KDSKBSTATE: /* set keyboard state (locks) */
+ if (*data >= 0 && *data <= LOCK_KEY_MASK) {
+ scp->status &= ~LOCK_KEY_MASK;
+ scp->status |= *data;
+ if (scp == cur_console)
+ update_leds(scp->status);
+ return 0;
+ }
+ return EINVAL;
+
+ case KDGKBSTATE: /* get keyboard state (locks) */
+ *data = scp->status & LOCK_KEY_MASK;
+ return 0;
+
+ case KDSETRAD: /* set keyboard repeat & delay rates */
+ if (*data & 0x80)
+ return EINVAL;
+ kbd_cmd2(KB_SETRAD, *data);
+ return 0;
+
+ case KDSKBMODE: /* set keyboard mode */
+ switch (*data) {
+ case K_RAW: /* switch to RAW scancode mode */
+ scp->status |= KBD_RAW_MODE;
+ return 0;
+
+ case K_XLATE: /* switch to XLT ascii mode */
+ if (scp == cur_console && scp->status == KBD_RAW_MODE)
+ shfts = ctls = alts = agrs = metas = 0;
+ scp->status &= ~KBD_RAW_MODE;
+ return 0;
+ default:
+ return EINVAL;
+ }
+ /* NOT REACHED */
+
+ case KDGKBMODE: /* get keyboard mode */
+ *data = (scp->status & KBD_RAW_MODE) ? K_RAW : K_XLATE;
+ return 0;
+
+ case KDMKTONE: /* sound the bell */
+ if (scp == cur_console)
+ sysbeep(scp->bell_pitch, scp->bell_duration);
+ return 0;
+
+ case KIOCSOUND: /* make tone (*data) hz */
+ if (scp == cur_console) {
+ if (*(int*)data) {
+ int pitch = TIMER_FREQ/(*(int*)data);
+ /* set command for counter 2, 2 byte write */
+ if (acquire_timer2(TIMER_16BIT|TIMER_SQWAVE)) {
+ return EBUSY;
+ }
+ /* set pitch */
+ outb(TIMER_CNTR2, pitch);
+ outb(TIMER_CNTR2, (pitch>>8));
+ /* enable counter 2 output to speaker */
+ outb(IO_PPI, inb(IO_PPI) | 3);
+ }
+ else {
+ /* disable counter 2 output to speaker */
+ outb(IO_PPI, inb(IO_PPI) & 0xFC);
+ release_timer2();
+ }
+ }
+ return 0;
+
+ case KDGKBTYPE: /* get keyboard type */
+ *data = 0; /* type not known (yet) */
+ return 0;
+
+ case KDSETLED: /* set keyboard LED status */
+ if (*data >= 0 && *data <= LED_MASK) {
+ scp->status &= ~LED_MASK;
+ scp->status |= *data;
+ if (scp == cur_console)
+ update_leds(scp->status);
+ return 0;
+ }
+ return EINVAL;
+
+ case KDGETLED: /* get keyboard LED status */
+ *data = scp->status & LED_MASK;
+ return 0;
+
+ case GETFKEY: /* get functionkey string */
+ if (*(u_short*)data < n_fkey_tab) {
+ fkeyarg_t *ptr = (fkeyarg_t*)data;
+ bcopy(&fkey_tab[ptr->keynum].str,
+ ptr->keydef,
+ fkey_tab[ptr->keynum].len);
+ ptr->flen = fkey_tab[ptr->keynum].len;
+ return 0;
+ }
+ else
+ return EINVAL;
+
+ case SETFKEY: /* set functionkey string */
+ if (*(u_short*)data < n_fkey_tab) {
+ fkeyarg_t *ptr = (fkeyarg_t*)data;
+ bcopy(ptr->keydef,
+ &fkey_tab[ptr->keynum].str,
+ min(ptr->flen, MAXFK));
+ fkey_tab[ptr->keynum].len = min(ptr->flen, MAXFK);
+ return 0;
+ }
+ else
+ return EINVAL;
+
+ case GIO_SCRNMAP: /* get output translation table */
+ bcopy(&scr_map, data, sizeof(scr_map));
+ return 0;
+
+ case PIO_SCRNMAP: /* set output translation table */
+ bcopy(data, &scr_map, sizeof(scr_map));
+ return 0;
+
+ case GIO_KEYMAP: /* get keyboard translation table */
+ bcopy(&key_map, data, sizeof(key_map));
+ return 0;
+
+ case PIO_KEYMAP: /* set keyboard translation table */
+ bcopy(data, &key_map, sizeof(key_map));
+ return 0;
+
+ case PIO_FONT8x8: /* set 8x8 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(data, &font_8x8, sizeof(font_8x8));
+ load_font(1, 8, font_8x8);
+ return 0;
+
+ case GIO_FONT8x8: /* get 8x8 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(&font_8x8, data, sizeof(font_8x8));
+ return 0;
+
+ case PIO_FONT8x14: /* set 8x14 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(data, &font_8x14, sizeof(font_8x14));
+ load_font(2, 14, font_8x14);
+ return 0;
+
+ case GIO_FONT8x14: /* get 8x14 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(&font_8x14, data, sizeof(font_8x14));
+ return 0;
+
+ case PIO_FONT8x16: /* set 8x16 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(data, &font_8x16, sizeof(font_8x16));
+ load_font(0, 16, font_8x16);
+ return 0;
+
+ case GIO_FONT8x16: /* get 8x16 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(&font_8x16, data, sizeof(font_8x16));
+ return 0;
+
+ case CONSOLE_X_MODE_ON: /* just to be compatible */
+ if (saved_console < 0) {
+ saved_console = get_scr_num();
+ switch_scr(minor(dev));
+ fp = (frametype *)p->p_md.md_regs;
+ fp->eflags |= PSL_IOPL;
+ scp->status |= UNKNOWN_MODE;
+ scp->status |= KBD_RAW_MODE;
+ return 0;
+ }
+ return EAGAIN;
+
+ case CONSOLE_X_MODE_OFF:/* just to be compatible */
+ fp = (frametype *)p->p_md.md_regs;
+ fp->eflags &= ~PSL_IOPL;
+ if (crtc_vga) {
+ load_font(0, 16, font_8x16);
+ load_font(1, 8, font_8x8);
+ load_font(2, 14, font_8x14);
+ load_palette();
+ }
+ scp->status &= ~UNKNOWN_MODE;
+ set_mode(scp);
+ clear_screen(scp);
+ scp->status &= ~KBD_RAW_MODE;
+ switch_scr(saved_console);
+ saved_console = -1;
+ return 0;
+
+ case CONSOLE_X_BELL: /* more compatibility */
+ /*
+ * if set, data is a pointer to a length 2 array of
+ * integers. data[0] is the pitch in Hz and data[1]
+ * is the duration in msec.
+ */
+ if (data)
+ sysbeep(TIMER_FREQ/((int*)data)[0],
+ ((int*)data)[1]*hz/3000);
+ else
+ sysbeep(scp->bell_pitch, scp->bell_duration);
+ return 0;
+
+ default:
+ break;
+ }
+
+ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
+ if (error >= 0)
+ return(error);
+ error = ttioctl(tp, cmd, data, flag);
+ if (error >= 0)
+ return(error);
+ return(ENOTTY);
+}
+
+
+void pcxint(dev_t dev)
+{
+ struct tty *tp = get_tty_ptr(dev);
+
+ if (!tp)
+ return;
+ tp->t_state &= ~TS_BUSY;
+ if (tp->t_line)
+ (*linesw[tp->t_line].l_start)(tp);
+ else
+ pcstart(tp);
+}
+
+
+void pcstart(struct tty *tp)
+{
+#if defined(NetBSD) || defined(__FreeBSD__)
+ struct clist *rbp;
+ int i, s, len;
+ u_char buf[PCBURST];
+ scr_stat *scp = get_scr_stat(tp->t_dev);
+
+ if (scp->status & SLKED)
+ return;
+ s = spltty(); /* Isn't start always called at spltty? */
+ if (!(tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))) {
+ tp->t_state |= TS_BUSY;
+ splx(s);
+ rbp = &tp->t_outq;
+ while (rbp->c_cc) {
+ len = q_to_b(rbp, buf, PCBURST);
+ for (i=0; i<len; i++)
+ if (buf[i]) ansi_put(scp, buf[i]);
+ }
+ s = spltty();
+ tp->t_state &= ~TS_BUSY;
+#if 0
+ if (rbp->c_cc) {
+ tp->t_state |= TS_TIMEOUT;
+ timeout((timeout_t)ttrstrt, (caddr_t)tp, 1);
+ }
+#endif
+ if (rbp->c_cc <= tp->t_lowat) {
+ if (tp->t_state & TS_ASLEEP) {
+ tp->t_state &= ~TS_ASLEEP;
+ wakeup((caddr_t)rbp);
+ }
+ selwakeup(&tp->t_wsel);
+ }
+ }
+ splx(s);
+
+#else /* __386BSD__ */
+
+ int c, s, len, i;
+ scr_stat *scp = get_scr_stat(tp->t_dev);
+ u_char buf[PCBURST];
+
+ if (scp->status & SLKED)
+ return;
+ s = spltty();
+ if (!(tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))) {
+ for (;;) {
+ if (RB_LEN(tp->t_out) <= tp->t_lowat) {
+ if (tp->t_state & TS_ASLEEP) {
+ tp->t_state &= ~TS_ASLEEP;
+ wakeup((caddr_t)tp->t_out);
+ }
+ selwakeup(&tp->t_wsel);
+ }
+ if (RB_LEN(tp->t_out) == 0)
+ break;
+ if (scp->status & SLKED)
+ break;
+ len = 0;
+ while( len < PCBURST) {
+ buf[len++] = getc(tp->t_out);
+ if( RB_LEN(tp->t_out) == 0)
+ break;
+ }
+ tp->t_state |= TS_BUSY;
+ splx(s);
+ for(i=0;i<len;i++)
+ ansi_put(scp, buf[i]);
+ s = spltty();
+ tp->t_state &= ~TS_BUSY;
+ }
+ tp->t_state |= TS_BUSY;
+ if( in_putc == 0) {
+ int i;
+ for(i=0;i<console_buffer_count;i++) {
+ scput(console_buffer[i]);
+ }
+ console_buffer_count = 0;
+ }
+ tp->t_state &= ~TS_BUSY;
+ }
+ splx(s);
+#endif
+}
+
+
+void pccnprobe(struct consdev *cp)
+{
+ int maj;
+
+ /* locate the major number */
+ for (maj = 0; maj < nchrdev; maj++)
+ if ((void*)cdevsw[maj].d_open == (void*)pcopen)
+ break;
+
+ /* initialize required fields */
+ cp->cn_dev = makedev(maj, NCONS);
+ cp->cn_pri = CN_INTERNAL;
+#if defined(__386BSD__) && !defined(__FreeBSD__)
+ cp->cn_tp = CONSOLE_TTY;
+#endif
+}
+
+
+void pccninit(struct consdev *cp)
+{
+ scinit();
+}
+
+
+void pccnputc(dev_t dev, char c)
+{
+ if (c == '\n')
+ scput('\r');
+ scput(c);
+ if (cur_console == &console[0]) {
+ int pos = cur_console->crtat - cur_console->crt_base;
+ if (pos != cur_cursor_pos) {
+ cur_cursor_pos = pos;
+ outb(crtc_addr,14);
+ outb(crtc_addr+1,pos >> 8);
+ outb(crtc_addr,15);
+ outb(crtc_addr+1,pos&0xff);
+ }
+ }
+}
+
+
+int pccngetc(dev_t dev)
+{
+ int s = spltty(); /* block scintr while we poll */
+ int c = scgetc(0);
+ splx(s);
+ if (c == '\r') c = '\n';
+ return(c);
+}
+
+static void none_saver(int test)
+{
+}
+
+static void fade_saver(int test)
+{
+ static int count = 0;
+ int i;
+
+ if (test) {
+ scrn_blanked = 1;
+ if (count < 64) {
+ outb(PIXMASK, 0xFF); /* no pixelmask */
+ outb(PALWADR, 0x00);
+ outb(PALDATA, 0);
+ outb(PALDATA, 0);
+ outb(PALDATA, 0);
+ for (i = 3; i < 768; i++) {
+ if (palette[i] - count > 15)
+ outb(PALDATA, palette[i]-count);
+ else
+ outb(PALDATA, 15);
+ }
+ inb(crtc_addr+6); /* reset flip/flop */
+ outb(ATC, 0x20); /* enable palette */
+ count++;
+ }
+ }
+ else {
+ count = scrn_blanked = 0;
+ load_palette();
+ }
+}
+
+static void blank_saver(int test)
+{
+ u_char val;
+ if (test) {
+ scrn_blanked = 1;
+ outb(TSIDX, 0x01); val = inb(TSREG);
+ outb(TSIDX, 0x01); outb(TSREG, val | 0x20);
+ }
+ else {
+ scrn_blanked = 0;
+ outb(TSIDX, 0x01); val = inb(TSREG);
+ outb(TSIDX, 0x01); outb(TSREG, val & 0xDF);
+ }
+}
+
+static u_long rand_next = 1;
+
+static int rand()
+{
+ return ((rand_next = rand_next * 1103515245 + 12345) & 0x7FFFFFFF);
+}
+
+/*
+ * Alternate saver that got its inspiration from a well known utility
+ * package for an unfamous OS.
+ */
+
+#define NUM_STARS 50
+
+static void star_saver(int test)
+{
+ scr_stat *scp = cur_console;
+ int cell, i;
+ char pattern[] = {"...........++++*** "};
+ char colors[] = {FG_DARKGREY, FG_LIGHTGREY,
+ FG_WHITE, FG_LIGHTCYAN};
+ static u_short stars[NUM_STARS][2];
+
+ if (test) {
+ if (!scrn_blanked) {
+ bcopy(Crtat, scp->scr_buf,
+ scp->xsize * scp->ysize * 2);
+ fillw((FG_LIGHTGREY|BG_BLACK)<<8 | scr_map[0x20], Crtat,
+ scp->xsize * scp->ysize);
+ set_border(0);
+ i = scp->ysize * scp->xsize + 5;
+ outb(crtc_addr, 14);
+ outb(crtc_addr+1, i >> 8);
+ outb(crtc_addr, 15);
+ outb(crtc_addr+1, i & 0xff);
+ scrn_blanked = 1;
+ for(i=0; i<NUM_STARS; i++) {
+ stars[i][0] =
+ rand() % (scp->xsize*scp->ysize);
+ stars[i][1] = 0;
+ }
+ }
+ cell = rand() % NUM_STARS;
+ *((u_short*)(Crtat + stars[cell][0])) =
+ scr_map[pattern[stars[cell][1]]] |
+ colors[rand()%sizeof(colors)] << 8;
+ if ((stars[cell][1]+=(rand()%4)) >= sizeof(pattern)-1) {
+ stars[cell][0] = rand() % (scp->xsize*scp->ysize);
+ stars[cell][1] = 0;
+ }
+ }
+ else {
+ if (scrn_blanked) {
+ bcopy(scp->scr_buf, Crtat, scp->xsize*scp->ysize*2);
+ cur_cursor_pos = -1;
+ set_border(scp->border);
+ scrn_blanked = 0;
+ }
+ }
+}
+
+
+static void snake_saver(int test)
+{
+ const char saves[] = {"FreeBSD"};
+ static u_char *savs[sizeof(saves)-1];
+ static int dirx, diry;
+ int f;
+ scr_stat *scp = cur_console;
+
+ if (test) {
+ if (!scrn_blanked) {
+ bcopy(Crtat, scp->scr_buf,
+ scp->xsize * scp->ysize * 2);
+ fillw((FG_LIGHTGREY|BG_BLACK)<<8 | scr_map[0x20],
+ Crtat, scp->xsize * scp->ysize);
+ set_border(0);
+ dirx = (scp->xpos ? 1 : -1);
+ diry = (scp->ypos ?
+ scp->xsize : -scp->xsize);
+ for (f=0; f< sizeof(saves)-1; f++)
+ savs[f] = (u_char *)Crtat + 2 *
+ (scp->xpos+scp->ypos*scp->xsize);
+ *(savs[0]) = scr_map[*saves];
+ f = scp->ysize * scp->xsize + 5;
+ outb(crtc_addr, 14);
+ outb(crtc_addr+1, f >> 8);
+ outb(crtc_addr, 15);
+ outb(crtc_addr+1, f & 0xff);
+ scrn_blanked = 1;
+ }
+ if (scrn_blanked++ < 4)
+ return;
+ scrn_blanked = 1;
+ *(savs[sizeof(saves)-2]) = scr_map[0x20];
+ for (f=sizeof(saves)-2; f > 0; f--)
+ savs[f] = savs[f-1];
+ f = (savs[0] - (u_char *)Crtat) / 2;
+ if ((f % scp->xsize) == 0 ||
+ (f % scp->xsize) == scp->xsize - 1 ||
+ (rand() % 50) == 0)
+ dirx = -dirx;
+ if ((f / scp->xsize) == 0 ||
+ (f / scp->xsize) == scp->ysize - 1 ||
+ (rand() % 20) == 0)
+ diry = -diry;
+ savs[0] += 2*dirx + 2*diry;
+ for (f=sizeof(saves)-2; f>=0; f--)
+ *(savs[f]) = scr_map[saves[f]];
+ }
+ else {
+ if (scrn_blanked) {
+ bcopy(scp->scr_buf, Crtat,
+ scp->xsize * scp->ysize * 2);
+ cur_cursor_pos = -1;
+ set_border(scp->border);
+ scrn_blanked = 0;
+ }
+ }
+}
+
+static void cursor_shape(int start, int end)
+{
+ outb(crtc_addr, 10);
+ outb(crtc_addr+1, start & 0xFF);
+ outb(crtc_addr, 11);
+ outb(crtc_addr+1, end & 0xFF);
+}
+
+
+#if !defined(FAT_CURSOR)
+static void get_cursor_shape(int *start, int *end)
+{
+ outb(crtc_addr, 10);
+ *start = inb(crtc_addr+1) & 0x1F;
+ outb(crtc_addr, 11);
+ *end = inb(crtc_addr+1) & 0x1F;
+}
+#endif
+
+
+static void cursor_pos(int force)
+{
+ int pos;
+
+ if (cur_console->status & UNKNOWN_MODE)
+ return;
+ if (scrn_blank_time && (time.tv_sec > scrn_time_stamp+scrn_blank_time))
+ SCRN_SAVER(1);
+ pos = cur_console->crtat - cur_console->crt_base;
+ if (force || (!scrn_blanked && pos != cur_cursor_pos)) {
+ cur_cursor_pos = pos;
+ outb(crtc_addr, 14);
+ outb(crtc_addr+1, pos>>8);
+ outb(crtc_addr, 15);
+ outb(crtc_addr+1, pos&0xff);
+ }
+ timeout((timeout_t)cursor_pos, 0, hz/20);
+}
+
+
+static void clear_screen(scr_stat *scp)
+{
+ move_crsr(scp, 0, 0);
+ fillw(scp->term.cur_attr | scr_map[0x20], scp->crt_base,
+ scp->xsize * scp->ysize);
+}
+
+
+static int switch_scr(u_int next_scr)
+{
+ if (in_putc) { /* delay switch if in putc */
+ delayed_next_scr = next_scr+1;
+ return 0;
+ }
+ if (switch_in_progress &&
+ (cur_console->proc != pfind(cur_console->pid)))
+ switch_in_progress = 0;
+
+ if (next_scr >= NCONS || switch_in_progress) {
+ sysbeep(BELL_PITCH, BELL_DURATION);
+ return EINVAL;
+ }
+
+ /* is the wanted virtual console open ? */
+ if (next_scr) {
+ struct tty *tp = VIRTUAL_TTY(next_scr);
+ if (!(tp->t_state & TS_ISOPEN)) {
+ sysbeep(BELL_PITCH, BELL_DURATION);
+ return EINVAL;
+ }
+ }
+
+ switch_in_progress = 1;
+ old_scp = cur_console;
+ new_scp = &console[next_scr];
+ wakeup((caddr_t)&new_scp->smode);
+ if (new_scp == old_scp) {
+ switch_in_progress = 0;
+ return 0;
+ }
+
+ /* has controlling process died? */
+ if (old_scp->proc && (old_scp->proc != pfind(old_scp->pid)))
+ old_scp->smode.mode = VT_AUTO;
+ if (new_scp->proc && (new_scp->proc != pfind(new_scp->pid)))
+ new_scp->smode.mode = VT_AUTO;
+
+ /* check the modes and switch approbiatly */
+ if (old_scp->smode.mode == VT_PROCESS) {
+ old_scp->status |= SWITCH_WAIT_REL;
+ psignal(old_scp->proc, old_scp->smode.relsig);
+ }
+ else {
+ exchange_scr();
+ if (new_scp->smode.mode == VT_PROCESS) {
+ new_scp->status |= SWITCH_WAIT_ACQ;
+ psignal(new_scp->proc, new_scp->smode.acqsig);
+ }
+ else
+ switch_in_progress = 0;
+ }
+ return 0;
+}
+
+
+static void exchange_scr(void)
+{
+ struct tty *tp;
+
+ bcopy(Crtat, old_scp->scr_buf, old_scp->xsize * old_scp->ysize * 2);
+ old_scp->crt_base = old_scp->scr_buf;
+ move_crsr(old_scp, old_scp->xpos, old_scp->ypos);
+ cur_console = new_scp;
+ set_mode(new_scp);
+ new_scp->crt_base = Crtat;
+ move_crsr(new_scp, new_scp->xpos, new_scp->ypos);
+ bcopy(new_scp->scr_buf, Crtat, new_scp->xsize * new_scp->ysize * 2);
+ update_leds(new_scp->status);
+ if ((old_scp->status & UNKNOWN_MODE) && crtc_vga) {
+ load_font(0, 16, font_8x16);
+ load_font(1, 8, font_8x8);
+ load_font(2, 14, font_8x14);
+ load_palette();
+ }
+ if (old_scp->status & KBD_RAW_MODE || new_scp->status & KBD_RAW_MODE)
+ shfts = ctls = alts = agrs = metas = 0;
+ delayed_next_scr = 0;
+}
+
+
+static void move_crsr(scr_stat *scp, int x, int y)
+{
+ if (x < 0 || y < 0 || x >= scp->xsize || y >= scp->ysize)
+ return;
+ scp->xpos = x;
+ scp->ypos = y;
+ scp->crtat = scp->crt_base + scp->ypos * scp->xsize + scp->xpos;
+}
+
+static void move_up(u_short *s, u_short *d, u_int len)
+{
+ s += len;
+ d += len;
+ while (len-- > 0)
+ *--d = *--s;
+}
+
+static void move_down(u_short *s, u_short *d, u_int len)
+{
+ while (len-- > 0)
+ *d++ = *s++;
+}
+
+static void scan_esc(scr_stat *scp, u_char c)
+{
+ static u_char ansi_col[16] =
+ {0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15};
+ int i, n;
+ u_short *src, *dst, count;
+
+ if (scp->term.esc == 1) {
+ switch (c) {
+
+ case '[': /* Start ESC [ sequence */
+ scp->term.esc = 2;
+ scp->term.last_param = -1;
+ for (i = scp->term.num_param; i < MAX_ESC_PAR; i++)
+ scp->term.param[i] = 1;
+ scp->term.num_param = 0;
+ return;
+
+ case 'M': /* Move cursor up 1 line, scroll if at top */
+ if (scp->ypos > 0)
+ move_crsr(scp, scp->xpos, scp->ypos - 1);
+ else {
+ move_up(scp->crt_base,
+ scp->crt_base + scp->xsize,
+ (scp->ysize - 1) * scp->xsize);
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base, scp->xsize);
+ }
+ break;
+#if notyet
+ case 'Q':
+ scp->term.esc = 4;
+ break;
+#endif
+ case 'c': /* Clear screen & home */
+ clear_screen(scp);
+ break;
+ }
+ }
+ else if (scp->term.esc == 2) {
+ if (c >= '0' && c <= '9') {
+ if (scp->term.num_param < MAX_ESC_PAR) {
+ if (scp->term.last_param != scp->term.num_param) {
+ scp->term.last_param = scp->term.num_param;
+ scp->term.param[scp->term.num_param] = 0;
+ }
+ else
+ scp->term.param[scp->term.num_param] *= 10;
+ scp->term.param[scp->term.num_param] += c - '0';
+ return;
+ }
+ }
+ scp->term.num_param = scp->term.last_param + 1;
+ switch (c) {
+
+ case ';':
+ if (scp->term.num_param < MAX_ESC_PAR)
+ return;
+ break;
+
+ case '=':
+ scp->term.esc = 3;
+ scp->term.last_param = -1;
+ for (i = scp->term.num_param; i < MAX_ESC_PAR; i++)
+ scp->term.param[i] = 1;
+ scp->term.num_param = 0;
+ return;
+
+ case 'A': /* up n rows */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos, scp->ypos - n);
+ break;
+
+ case 'B': /* down n rows */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos, scp->ypos + n);
+ break;
+
+ case 'C': /* right n columns */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos + n, scp->ypos);
+ break;
+
+ case 'D': /* left n columns */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos - n, scp->ypos);
+ break;
+
+ case 'E': /* cursor to start of line n lines down */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, 0, scp->ypos + n);
+ break;
+
+ case 'F': /* cursor to start of line n lines up */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, 0, scp->ypos - n);
+ break;
+
+ case 'f': /* System V consoles .. */
+ case 'H': /* Cursor move */
+ if (scp->term.num_param == 0)
+ move_crsr(scp, 0, 0);
+ else if (scp->term.num_param == 2)
+ move_crsr(scp, scp->term.param[1] - 1,
+ scp->term.param[0] - 1);
+ break;
+
+ case 'J': /* Clear all or part of display */
+ if (scp->term.num_param == 0)
+ n = 0;
+ else
+ n = scp->term.param[0];
+ switch (n) {
+ case 0: /* clear form cursor to end of display */
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crtat, scp->crt_base +
+ scp->xsize * scp->ysize -
+ scp->crtat);
+ break;
+ case 1: /* clear from beginning of display to cursor */
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base,
+ scp->crtat - scp->crt_base);
+ break;
+ case 2: /* clear entire display */
+ clear_screen(scp);
+ break;
+ }
+ break;
+
+ case 'K': /* Clear all or part of line */
+ if (scp->term.num_param == 0)
+ n = 0;
+ else
+ n = scp->term.param[0];
+ switch (n) {
+ case 0: /* clear form cursor to end of line */
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crtat, scp->xsize - scp->xpos);
+ break;
+ case 1: /* clear from beginning of line to cursor */
+ fillw(scp->term.cur_attr|scr_map[0x20],
+ scp->crtat - (scp->xsize - scp->xpos),
+ (scp->xsize - scp->xpos) + 1);
+ break;
+ case 2: /* clear entire line */
+ fillw(scp->term.cur_attr|scr_map[0x20],
+ scp->crtat - (scp->xsize - scp->xpos),
+ scp->xsize);
+ break;
+ }
+ break;
+
+ case 'L': /* Insert n lines */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->ysize - scp->ypos)
+ n = scp->ysize - scp->ypos;
+ src = scp->crt_base + scp->ypos * scp->xsize;
+ dst = src + n * scp->xsize;
+ count = scp->ysize - (scp->ypos + n);
+ move_up(src, dst, count * scp->xsize);
+ fillw(scp->term.cur_attr | scr_map[0x20], src,
+ n * scp->xsize);
+ break;
+
+ case 'M': /* Delete n lines */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->ysize - scp->ypos)
+ n = scp->ysize - scp->ypos;
+ dst = scp->crt_base + scp->ypos * scp->xsize;
+ src = dst + n * scp->xsize;
+ count = scp->ysize - (scp->ypos + n);
+ move_down(src, dst, count * scp->xsize);
+ src = dst + count * scp->xsize;
+ fillw(scp->term.cur_attr | scr_map[0x20], src,
+ n * scp->xsize);
+ break;
+
+ case 'P': /* Delete n chars */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->xsize - scp->xpos)
+ n = scp->xsize - scp->xpos;
+ dst = scp->crtat;
+ src = dst + n;
+ count = scp->xsize - (scp->xpos + n);
+ move_down(src, dst, count);
+ src = dst + count;
+ fillw(scp->term.cur_attr | scr_map[0x20], src, n);
+ break;
+
+ case '@': /* Insert n chars */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->xsize - scp->xpos)
+ n = scp->xsize - scp->xpos;
+ src = scp->crtat;
+ dst = src + n;
+ count = scp->xsize - (scp->xpos + n);
+ move_up(src, dst, count);
+ fillw(scp->term.cur_attr | scr_map[0x20], src, n);
+ break;
+
+ case 'S': /* scroll up n lines */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->ypos)
+ n = scp->ypos;
+ bcopy(scp->crt_base + (scp->xsize * n),
+ scp->crt_base,
+ scp->xsize * (scp->ysize - n) *
+ sizeof(u_short));
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base + scp->xsize *
+ (scp->ysize - 1),
+ scp->xsize);
+ break;
+
+ case 'T': /* scroll down n lines */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->ysize - scp->ypos)
+ n = scp->ysize - scp->ypos;
+ bcopy(scp->crt_base,
+ scp->crt_base + (scp->xsize * n),
+ scp->xsize * (scp->ysize - n) *
+ sizeof(u_short));
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base, scp->xsize);
+ break;
+
+ case 'X': /* delete n characters in line */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->xsize - scp->xpos)
+ n = scp->xsize - scp->xpos;
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base + scp->xpos +
+ ((scp->xsize*scp->ypos) * sizeof(u_short)), n);
+ break;
+
+ case 'Z': /* move n tabs backwards */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if ((i = scp->xpos & 0xf8) == scp->xpos)
+ i -= 8*n;
+ else
+ i -= 8*(n-1);
+ if (i < 0)
+ i = 0;
+ move_crsr(scp, i, scp->ypos);
+ break;
+
+ case '`': /* move cursor to column n */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, n, scp->ypos);
+ break;
+
+ case 'a': /* move cursor n columns to the right */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos + n, scp->ypos);
+ break;
+
+ case 'd': /* move cursor to row n */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos, n);
+ break;
+
+ case 'e': /* move cursor n rows down */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos, scp->ypos + n);
+ break;
+
+ case 'm': /* change attribute */
+ if (scp->term.num_param == 0)
+ n = 0;
+ else
+ n = scp->term.param[0];
+ switch (n) {
+ case 0: /* back to normal */
+ scp->term.cur_attr = scp->term.std_attr;
+ break;
+ case 1: /* highlight (bold) */
+ scp->term.cur_attr &= 0xFF00;
+ scp->term.cur_attr |= 0x0800;
+ break;
+ case 4: /* highlight (underline) */
+ scp->term.cur_attr &= 0x0F00;
+ scp->term.cur_attr |= 0x0800;
+ break;
+ case 5: /* blink */
+ scp->term.cur_attr &= 0xFF00;
+ scp->term.cur_attr |= 0x8000;
+ break;
+ case 7: /* reverse video */
+ scp->term.cur_attr = scp->term.rev_attr;
+ break;
+ case 30: case 31: case 32: case 33: /* set fg color */
+ case 34: case 35: case 36: case 37:
+ scp->term.cur_attr = (scp->term.cur_attr & 0xF0FF)
+ | (ansi_col[(n - 30) & 7] << 8);
+ break;
+ case 40: case 41: case 42: case 43: /* set bg color */
+ case 44: case 45: case 46: case 47:
+ scp->term.cur_attr = (scp->term.cur_attr & 0x0FFF)
+ | (ansi_col[(n - 40) & 7] << 12);
+ break;
+ }
+ break;
+
+ case 'x':
+ if (scp->term.num_param == 0)
+ n = 0;
+ else
+ n = scp->term.param[0];
+ switch (n) {
+ case 0: /* reset attributes */
+ scp->term.cur_attr = scp->term.std_attr =
+ current_default->std_attr;
+ scp->term.rev_attr = current_default->rev_attr;
+ break;
+ case 1: /* set ansi background */
+ scp->term.cur_attr = scp->term.std_attr =
+ (scp->term.std_attr & 0x0F00) |
+ (ansi_col[(scp->term.param[1])&0x0F]<<12);
+ break;
+ case 2: /* set ansi foreground */
+ scp->term.cur_attr = scp->term.std_attr =
+ (scp->term.std_attr & 0xF000) |
+ (ansi_col[(scp->term.param[1])&0x0F]<<8);
+ break;
+ case 3: /* set ansi attribute directly */
+ scp->term.cur_attr = scp->term.std_attr =
+ (scp->term.param[1]&0xFF)<<8;
+ break;
+ case 5: /* set ansi reverse video background */
+ scp->term.rev_attr =
+ (scp->term.rev_attr & 0x0F00) |
+ (ansi_col[(scp->term.param[1])&0x0F]<<12);
+ break;
+ case 6: /* set ansi reverse video foreground */
+ scp->term.rev_attr =
+ (scp->term.rev_attr & 0xF000) |
+ (ansi_col[(scp->term.param[1])&0x0F]<<8);
+ break;
+ case 7: /* set ansi reverse video directly */
+ scp->term.rev_attr = (scp->term.param[1]&0xFF)<<8;
+ break;
+ }
+ break;
+
+ case 'z': /* switch to (virtual) console n */
+ if (scp->term.num_param == 1)
+ switch_scr(scp->term.param[0]);
+ break;
+ }
+ }
+ else if (scp->term.esc == 3) {
+ if (c >= '0' && c <= '9') {
+ if (scp->term.num_param < MAX_ESC_PAR) {
+ if (scp->term.last_param != scp->term.num_param) {
+ scp->term.last_param = scp->term.num_param;
+ scp->term.param[scp->term.num_param] = 0;
+ }
+ else
+ scp->term.param[scp->term.num_param] *= 10;
+ scp->term.param[scp->term.num_param] += c - '0';
+ return;
+ }
+ }
+ scp->term.num_param = scp->term.last_param + 1;
+ switch (c) {
+
+ case ';':
+ if (scp->term.num_param < MAX_ESC_PAR)
+ return;
+ break;
+
+ case 'A': /* set display border color */
+ if (scp->term.num_param == 1)
+ scp->border=scp->term.param[0] & 0xff;
+ if (scp == cur_console)
+ set_border(scp->border);
+ break;
+
+ case 'B': /* set bell pitch and duration */
+ if (scp->term.num_param == 2) {
+ scp->bell_pitch = scp->term.param[0];
+ scp->bell_duration = scp->term.param[1]*10;
+ }
+ break;
+
+ case 'C': /* set cursor shape (start & end line) */
+ if (scp->term.num_param == 2) {
+ scp->cursor_start = scp->term.param[0] & 0x1F;
+ scp->cursor_end = scp->term.param[1] & 0x1F;
+ if (scp == cur_console)
+ cursor_shape(scp->cursor_start,
+ scp->cursor_end);
+ }
+ break;
+
+ case 'F': /* set ansi foreground */
+ if (scp->term.num_param == 1)
+ scp->term.cur_attr = scp->term.std_attr =
+ (scp->term.std_attr & 0xF000)
+ | ((scp->term.param[0] & 0x0F) << 8);
+ break;
+
+ case 'G': /* set ansi background */
+ if (scp->term.num_param == 1)
+ scp->term.cur_attr = scp->term.std_attr =
+ (scp->term.std_attr & 0x0F00)
+ | ((scp->term.param[0] & 0x0F) << 12);
+ break;
+
+ case 'H': /* set ansi reverse video foreground */
+ if (scp->term.num_param == 1)
+ scp->term.rev_attr =
+ (scp->term.rev_attr & 0xF000)
+ | ((scp->term.param[0] & 0x0F) << 8);
+ break;
+
+ case 'I': /* set ansi reverse video background */
+ if (scp->term.num_param == 1)
+ scp->term.rev_attr =
+ (scp->term.rev_attr & 0x0F00)
+ | ((scp->term.param[0] & 0x0F) << 12);
+ break;
+ }
+ }
+ scp->term.esc = 0;
+}
+
+
+static void ansi_put(scr_stat *scp, u_char c)
+{
+ if (scp->status & UNKNOWN_MODE)
+ return;
+
+ /* make screensaver happy */
+ if (scp == cur_console) {
+ scrn_time_stamp = time.tv_sec;
+ if (scrn_blanked)
+ SCRN_SAVER(0);
+ }
+ in_putc++;
+ if (scp->term.esc)
+ scan_esc(scp, c);
+ else switch(c) {
+ case 0x1B: /* start escape sequence */
+ scp->term.esc = 1;
+ scp->term.num_param = 0;
+ break;
+ case 0x07:
+ if (scp == cur_console)
+ sysbeep(scp->bell_pitch, scp->bell_duration);
+ break;
+ case '\t': /* non-destructive tab */
+ scp->crtat += (8 - scp->xpos % 8);
+ scp->xpos += (8 - scp->xpos % 8);
+ break;
+ case '\b': /* non-destructive backspace */
+ if (scp->crtat > scp->crt_base) {
+ scp->crtat--;
+ if (scp->xpos > 0)
+ scp->xpos--;
+ else {
+ scp->xpos += scp->xsize - 1;
+ scp->ypos--;
+ }
+ }
+ break;
+ case '\r': /* return to pos 0 */
+ move_crsr(scp, 0, scp->ypos);
+ break;
+ case '\n': /* newline, same pos */
+ scp->crtat += scp->xsize;
+ scp->ypos++;
+ break;
+ case '\f': /* form feed, clears screen */
+ clear_screen(scp);
+ break;
+ default:
+ /* Print only printables */
+ *scp->crtat = (scp->term.cur_attr | scr_map[c]);
+ scp->crtat++;
+ if (++scp->xpos >= scp->xsize) {
+ scp->xpos = 0;
+ scp->ypos++;
+ }
+ break;
+ }
+ if (scp->crtat >= scp->crt_base + scp->ysize * scp->xsize) {
+ bcopy(scp->crt_base + scp->xsize, scp->crt_base,
+ scp->xsize * (scp->ysize - 1) * sizeof(u_short));
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base + scp->xsize * (scp->ysize - 1),
+ scp->xsize);
+ scp->crtat -= scp->xsize;
+ scp->ypos--;
+ }
+ in_putc--;
+ if (delayed_next_scr)
+ switch_scr(delayed_next_scr - 1);
+}
+
+static void scinit(void)
+{
+ u_short volatile *cp = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short), was;
+ unsigned cursorat;
+ int i;
+
+ /*
+ * catch that once in a blue moon occurence when scinit is called
+ * TWICE, adding the CGA_BUF offset again -> poooff
+ */
+ if (crtat != 0)
+ return;
+ /*
+ * Crtat initialized to point to MONO buffer, if not present change
+ * to CGA_BUF offset. ONLY ADD the difference since locore.s adds
+ * in the remapped offset at the "right" time
+ */
+ was = *cp;
+ *cp = (u_short) 0xA55A;
+ if (*cp != 0xA55A) {
+ crtc_addr = MONO_BASE;
+ } else {
+ *cp = was;
+ crtc_addr = COLOR_BASE;
+ Crtat = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short);
+ }
+
+ /* Extract cursor location */
+ outb(crtc_addr,14);
+ cursorat = inb(crtc_addr+1)<<8 ;
+ outb(crtc_addr,15);
+ cursorat |= inb(crtc_addr+1);
+ crtat = Crtat + cursorat;
+
+ /* is this a VGA or higher ? */
+ outb(crtc_addr, 7);
+ if (inb(crtc_addr) == 7)
+ crtc_vga = 1;
+
+ current_default = &user_default;
+ console[0].crtat = crtat;
+ console[0].crt_base = Crtat;
+ console[0].term.esc = 0;
+ console[0].term.std_attr = current_default->std_attr;
+ console[0].term.rev_attr = current_default->rev_attr;
+ console[0].term.cur_attr = current_default->std_attr;
+ console[0].xpos = cursorat % COL;
+ console[0].ypos = cursorat / COL;
+ console[0].border = BG_BLACK;;
+ console[0].xsize = COL;
+ console[0].ysize = ROW;
+ console[0].status = 0;
+ console[0].pid = 0;
+ console[0].proc = NULL;
+ console[0].smode.mode = VT_AUTO;
+ console[0].bell_pitch = BELL_PITCH;
+ console[0].bell_duration = BELL_DURATION;
+ kernel_console.esc = 0;
+ kernel_console.std_attr = kernel_default.std_attr;
+ kernel_console.rev_attr = kernel_default.rev_attr;
+ kernel_console.cur_attr = kernel_default.std_attr;
+ /* initialize mapscrn array to a one to one map */
+ for (i=0; i<sizeof(scr_map); i++)
+ scr_map[i] = i;
+ clear_screen(&console[0]);
+}
+
+
+static void scput(u_char c)
+{
+ scr_stat *scp = &console[0];
+ term_stat save;
+
+ if (crtat == 0)
+ scinit();
+ if( in_putc == 0) {
+ ++in_putc;
+ save = scp->term;
+ scp->term = kernel_console;
+ current_default = &kernel_default;
+ ansi_put(scp, c);
+ kernel_console = scp->term;
+ current_default = &user_default;
+ scp->term = save;
+ --in_putc;
+ } else {
+ if( console_buffer_count < CONSOLE_BUFFER_SIZE)
+ console_buffer[console_buffer_count++] = c;
+ }
+}
+
+
+static u_char *get_fstr(u_int c, u_int *len)
+{
+ u_int i;
+
+ if (!(c & FKEY))
+ return(NULL);
+ i = (c & 0xFF) - F_FN;
+ if (i > n_fkey_tab)
+ return(NULL);
+ *len = fkey_tab[i].len;
+ return(fkey_tab[i].str);
+}
+
+
+static void update_leds(int which)
+{
+ static u_char xlate_leds[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+
+ /* replace CAPS led with ALTGR led for ALTGR keyboards */
+ if (key_map.n_keys > ALTGR_OFFSET) {
+ if (which & ALKED)
+ which |= CLKED;
+ else
+ which &= ~CLKED;
+ }
+ kbd_cmd2(KB_SETLEDS, xlate_leds[which & LED_MASK]);
+}
+
+
+/*
+ * scgetc(noblock) : get a character from the keyboard.
+ * If noblock = 0 wait until a key is gotten. Otherwise return NOKEY.
+ */
+u_int scgetc(int noblock)
+{
+ u_char val, code, release;
+ u_int state, action;
+ struct key_t *key;
+ static u_char esc_flag = 0, compose = 0;
+ static u_int chr = 0;
+
+next_code:
+ kbd_wait();
+ /* First see if there is something in the keyboard port */
+ if (inb(KB_STAT) & KB_BUF_FULL)
+ val = inb(KB_DATA);
+ else if (noblock)
+ return(NOKEY);
+ else
+ goto next_code;
+
+ if (cur_console->status & KBD_RAW_MODE)
+ return val;
+
+ code = val & 0x7F;
+ release = val & 0x80;
+
+ switch (esc_flag) {
+ case 0x00: /* normal scancode */
+ switch(code) {
+ case 0x38: /* left alt (compose key) */
+ if (release && compose) {
+ compose = 0;
+ if (chr > 255) {
+ sysbeep(BELL_PITCH, BELL_DURATION);
+ chr = 0;
+ }
+ }
+ else {
+ if (!compose) {
+ compose = 1;
+ chr = 0;
+ }
+ }
+ break;
+ case 0x60:
+ case 0x61:
+ esc_flag = code;
+ goto next_code;
+ }
+ break;
+ case 0x60: /* 0xE0 prefix */
+ esc_flag = 0;
+ switch (code) {
+ case 0x1c: /* right enter key */
+ code = 0x59;
+ break;
+ case 0x1d: /* right ctrl key */
+ code = 0x5a;
+ break;
+ case 0x35: /* keypad divide key */
+ code = 0x5b;
+ break;
+ case 0x37: /* print scrn key */
+ code = 0x5c;
+ break;
+ case 0x38: /* right alt key (alt gr) */
+ code = 0x5d;
+ break;
+ case 0x47: /* grey home key */
+ code = 0x5e;
+ break;
+ case 0x48: /* grey up arrow key */
+ code = 0x5f;
+ break;
+ case 0x49: /* grey page up key */
+ code = 0x60;
+ break;
+ case 0x4b: /* grey left arrow key */
+ code = 0x61;
+ break;
+ case 0x4d: /* grey right arrow key */
+ code = 0x62;
+ break;
+ case 0x4f: /* grey end key */
+ code = 0x63;
+ break;
+ case 0x50: /* grey down arrow key */
+ code = 0x64;
+ break;
+ case 0x51: /* grey page down key */
+ code = 0x65;
+ break;
+ case 0x52: /* grey insert key */
+ code = 0x66;
+ break;
+ case 0x53: /* grey delete key */
+ code = 0x67;
+ break;
+ default: /* ignore everything else */
+ goto next_code;
+ }
+ break;
+ case 0x61: /* 0xE1 prefix */
+ esc_flag = 0;
+ if (code == 0x1D)
+ esc_flag = 0x1D;
+ goto next_code;
+ /* NOT REACHED */
+ case 0x1D: /* pause / break */
+ esc_flag = 0;
+ if (code != 0x45)
+ goto next_code;
+ code = 0x68;
+ break;
+ }
+
+ if (compose) {
+ switch (code) {
+ case 0x47:
+ case 0x48: /* keypad 7,8,9 */
+ case 0x49:
+ if (!release)
+ chr = (code - 0x40) + chr*10;
+ goto next_code;
+ case 0x4b:
+ case 0x4c: /* keypad 4,5,6 */
+ case 0x4d:
+ if (!release)
+ chr = (code - 0x47) + chr*10;
+ goto next_code;
+ case 0x4f:
+ case 0x50: /* keypad 1,2,3 */
+ case 0x51:
+ if (!release)
+ chr = (code - 0x4e) + chr*10;
+ goto next_code;
+ case 0x52: /* keypad 0 */
+ if (!release)
+ chr *= 10;
+ goto next_code;
+ case 0x38: /* left alt key */
+ break;
+ default:
+ if (chr) {
+ compose = chr = 0;
+ sysbeep(BELL_PITCH, BELL_DURATION);
+ goto next_code;
+ }
+ break;
+ }
+ }
+
+ state = (shfts ? 1 : 0 ) | (2 * (ctls ? 1 : 0)) | (4 * (alts ? 1 : 0));
+ if ((!agrs && (cur_console->status & ALKED))
+ || (agrs && !(cur_console->status & ALKED)))
+ code += ALTGR_OFFSET;
+ key = &key_map.key[code];
+ if ( ((key->flgs & FLAG_LOCK_C) && (cur_console->status & CLKED))
+ || ((key->flgs & FLAG_LOCK_N) && (cur_console->status & NLKED)) )
+ state ^= 1;
+
+ /* Check for make/break */
+ action = key->map[state];
+ if (release) { /* key released */
+ if (key->spcl & 0x80) {
+ switch (action) {
+ case LSH:
+ shfts &= ~1;
+ break;
+ case RSH:
+ shfts &= ~2;
+ break;
+ case LCTR:
+ ctls &= ~1;
+ break;
+ case RCTR:
+ ctls &= ~2;
+ break;
+ case LALT:
+ alts &= ~1;
+ break;
+ case RALT:
+ alts &= ~2;
+ break;
+ case NLK:
+ nlkcnt = 0;
+ break;
+ case CLK:
+ clkcnt = 0;
+ break;
+ case SLK:
+ slkcnt = 0;
+ break;
+ case ASH:
+ agrs = 0;
+ break;
+ case ALK:
+ alkcnt = 0;
+ break;
+ case META:
+ metas = 0;
+ break;
+ }
+ }
+ if (chr && !compose) {
+ action = chr;
+ chr = 0;
+ return(action);
+ }
+ } else {
+ /* key pressed */
+ if (key->spcl & (0x80>>state)) {
+ switch (action) {
+ /* LOCKING KEYS */
+ case NLK:
+ if (!nlkcnt) {
+ nlkcnt++;
+ if (cur_console->status & NLKED)
+ cur_console->status &= ~NLKED;
+ else
+ cur_console->status |= NLKED;
+ update_leds(cur_console->status);
+ }
+ break;
+ case CLK:
+ if (!clkcnt) {
+ clkcnt++;
+ if (cur_console->status & CLKED)
+ cur_console->status &= ~CLKED;
+ else
+ cur_console->status |= CLKED;
+ update_leds(cur_console->status);
+ }
+ break;
+ case SLK:
+ if (!slkcnt) {
+ slkcnt++;
+ if (cur_console->status & SLKED) {
+ cur_console->status &= ~SLKED;
+ pcstart(VIRTUAL_TTY(get_scr_num()));
+ }
+ else
+ cur_console->status |= SLKED;
+ update_leds(cur_console->status);
+ }
+ break;
+ case ALK:
+ if (!alkcnt) {
+ alkcnt++;
+ if (cur_console->status & ALKED)
+ cur_console->status &= ~ALKED;
+ else
+ cur_console->status |= ALKED;
+ update_leds(cur_console->status);
+ }
+ break;
+
+ /* NON-LOCKING KEYS */
+ case NOP:
+ break;
+ case RBT:
+#if defined(__FreeBSD__)
+ shutdown_nice();
+#else
+ cpu_reset();
+#endif
+ break;
+ case DBG:
+#if DDB > 0 /* try to switch to console 0 */
+ if (cur_console->smode.mode == VT_AUTO &&
+ console[0].smode.mode == VT_AUTO)
+ switch_scr(0);
+ Debugger("manual escape to debugger");
+ return(NOKEY);
+#else
+ printf("No debugger in kernel\n");
+#endif
+ break;
+ case LSH:
+ shfts |= 1;
+ break;
+ case RSH:
+ shfts |= 2;
+ break;
+ case LCTR:
+ ctls |= 1;
+ break;
+ case RCTR:
+ ctls |= 2;
+ break;
+ case LALT:
+ alts |= 1;
+ break;
+ case RALT:
+ alts |= 2;
+ break;
+ case ASH:
+ agrs = 1;
+ break;
+ case META:
+ metas = 1;
+ break;
+ case NEXT:
+ switch_scr((get_scr_num()+1)%NCONS);
+ break;
+ default:
+ if (action >= F_SCR && action <= L_SCR) {
+ switch_scr(action - F_SCR);
+ break;
+ }
+ if (action >= F_FN && action <= L_FN)
+ action |= FKEY;
+ return(action);
+ }
+ }
+ else {
+ if (metas)
+ action |= MKEY;
+ return(action);
+ }
+ }
+ goto next_code;
+}
+
+
+int getchar(void)
+{
+ u_char thechar;
+ int s;
+
+ polling = 1;
+ s = splhigh();
+ scput('>');
+ thechar = (u_char) scgetc(0);
+ polling = 0;
+ splx(s);
+ switch (thechar) {
+ default:
+ if (thechar >= scr_map[0x20])
+ scput(thechar);
+ return(thechar);
+ case cr:
+ case lf:
+ scput(cr); scput(lf);
+ return(lf);
+ case bs:
+ case del:
+ scput(bs); scput(scr_map[0x20]); scput(bs);
+ return(thechar);
+ case cntld:
+ scput('^'); scput('D'); scput('\r'); scput('\n');
+ return(0);
+ }
+}
+
+
+u_int sgetc(int noblock)
+{
+ return (scgetc(noblock) & 0xff);
+}
+
+int pcmmap(dev_t dev, int offset, int nprot)
+{
+ if (offset > 0x20000)
+ return EINVAL;
+ return i386_btop((VIDEOMEM + offset));
+}
+
+
+static void kbd_wait(void)
+{
+ int i;
+
+ for (i=0; i<1000; i++) { /* up to 10 msec */
+ if ((inb(KB_STAT) & KB_READY) == 0)
+ break;
+ DELAY (10);
+ }
+}
+
+
+static void kbd_cmd(u_char command)
+{
+ kbd_wait();
+ outb(KB_DATA, command);
+}
+
+
+static void kbd_cmd2(u_char command, u_char arg)
+{
+ int r, s = spltty();
+ do {
+ kbd_cmd(command);
+ r = kbd_reply();
+ if (r == KB_ACK) {
+ kbd_cmd(arg & 0x7f);
+ r = kbd_reply();
+ }
+ } while (r != KB_ACK);
+ splx(s);
+}
+
+
+static int kbd_reply()
+{
+ int i;
+
+ kbd_wait();
+ for (i=0; i<60000; i++) { /* at least 300 msec, 600 msec enough */
+ if (inb(KB_STAT) & KB_BUF_FULL)
+ return ((u_char) inb(KB_DATA));
+ DELAY (10);
+ }
+ return(-1);
+}
+
+
+static void set_mode(scr_stat *scp)
+{
+ u_char byte;
+ int s;
+
+ if (scp != cur_console)
+ return;
+
+ /* (re)activate cursor */
+ untimeout((timeout_t)cursor_pos, 0);
+ cursor_pos(1);
+
+ /* change cursor type if set */
+ if (scp->cursor_start != -1 && scp->cursor_end != -1)
+ cursor_shape(scp->cursor_start, scp->cursor_end);
+
+ /* mode change only on VGA's */
+ if (!crtc_vga)
+ return;
+
+ /* setup video hardware for the given mode */
+ s = splhigh();
+ switch(scp->mode) {
+ case TEXT80x25:
+ outb(crtc_addr, 9); byte = inb(crtc_addr+1);
+ outb(crtc_addr, 9); outb(crtc_addr+1, byte | 0x0F);
+ outb(TSIDX, 0x03); outb(TSREG, 0x00); /* select font 0 */
+ break;
+ case TEXT80x50:
+ outb(crtc_addr, 9); byte = inb(crtc_addr+1);
+ outb(crtc_addr, 9); outb(crtc_addr+1, (byte & 0xF0) | 0x07);
+ outb(TSIDX, 0x03); outb(TSREG, 0x05); /* select font 1 */
+ break;
+ default:
+ break;
+ }
+ splx(s);
+
+ /* set border color for this (virtual) console */
+ set_border(scp->border);
+ return;
+}
+
+
+static void set_border(int color)
+{
+ inb(crtc_addr+6); /* reset flip-flop */
+ outb(ATC, 0x11); outb(ATC, color);
+ inb(crtc_addr+6); /* reset flip-flop */
+ outb(ATC, 0x20); /* enable Palette */
+}
+
+static void load_font(int segment, int size, char* font)
+{
+ int ch, line, s;
+ u_char val;
+
+ outb(TSIDX, 0x01); val = inb(TSREG); /* blank screen */
+ outb(TSIDX, 0x01); outb(TSREG, val | 0x20);
+
+ /* setup vga for loading fonts (graphics plane mode) */
+ s = splhigh();
+ inb(crtc_addr+6); /* reset flip/flop */
+ outb(ATC, 0x30); outb(ATC, 0x01);
+ outb(TSIDX, 0x02); outb(TSREG, 0x04);
+ outb(TSIDX, 0x04); outb(TSREG, 0x06);
+ outb(GDCIDX, 0x04); outb(GDCREG, 0x02);
+ outb(GDCIDX, 0x05); outb(GDCREG, 0x00);
+ outb(GDCIDX, 0x06); outb(GDCREG, 0x05); /* addr = a0000, 64kb */
+ splx(s);
+ for (ch=0; ch < 256; ch++)
+ for (line=0; line < size; line++)
+ *((char *)atdevbase+(segment*0x4000)+(ch*32)+line) =
+ font[(ch*size)+line];
+ /* setup vga for text mode again */
+ s = splhigh();
+ inb(crtc_addr+6); /* reset flip/flop */
+ outb(ATC, 0x30); outb(ATC, 0x0C);
+ outb(TSIDX, 0x02); outb(TSREG, 0x03);
+ outb(TSIDX, 0x04); outb(TSREG, 0x02);
+ outb(GDCIDX, 0x04); outb(GDCREG, 0x00);
+ outb(GDCIDX, 0x05); outb(GDCREG, 0x10);
+ if (crtc_addr == MONO_BASE) {
+ outb(GDCIDX, 0x06); outb(GDCREG, 0x0A); /* addr = b0000, 32kb */
+ }
+ else {
+ outb(GDCIDX, 0x06); outb(GDCREG, 0x0E); /* addr = b8000, 32kb */
+ }
+ splx(s);
+ outb(TSIDX, 0x01); val = inb(TSREG); /* unblank screen */
+ outb(TSIDX, 0x01); outb(TSREG, val & 0xDF);
+}
+
+
+static void load_palette(void)
+{
+ int i;
+
+ outb(PIXMASK, 0xFF); /* no pixelmask */
+ outb(PALWADR, 0x00);
+ for (i=0x00; i<0x300; i++)
+ outb(PALDATA, palette[i]);
+ inb(crtc_addr+6); /* reset flip/flop */
+ outb(ATC, 0x20); /* enable palette */
+}
+
+static void save_palette(void)
+{
+ int i;
+
+ outb(PALRADR, 0x00);
+ for (i=0x00; i<0x300; i++)
+ palette[i] = inb(PALDATA);
+ inb(crtc_addr+6); /* reset flip/flop */
+}
+
+
+static void change_winsize(struct tty *tp, int x, int y)
+{
+ if (tp->t_winsize.ws_col != x || tp->t_winsize.ws_row != y) {
+ tp->t_winsize.ws_col = x;
+ tp->t_winsize.ws_row = y;
+ pgsignal(tp->t_pgrp, SIGWINCH, 1);
+ }
+}
+
+#endif /* NSC */
diff --git a/sys/i386/isa/timerreg.h b/sys/i386/isa/timerreg.h
new file mode 100644
index 0000000..5742f66
--- /dev/null
+++ b/sys/i386/isa/timerreg.h
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: Header: timerreg.h,v 1.2 93/02/28 15:08:58 mccanne Exp
+ * $Id$
+ */
+
+/*
+ *
+ * Register definitions for the Intel 8253 Programmable Interval Timer.
+ *
+ * This chip has three independent 16-bit down counters that can be
+ * read on the fly. There are three mode registers and three countdown
+ * registers. The countdown registers are addressed directly, via the
+ * first three I/O ports. The three mode registers are accessed via
+ * the fourth I/O port, with two bits in the mode byte indicating the
+ * register. (Why are hardware interfaces always so braindead?).
+ *
+ * To write a value into the countdown register, the mode register
+ * is first programmed with a command indicating the which byte of
+ * the two byte register is to be modified. The three possibilities
+ * are load msb (TMR_MR_MSB), load lsb (TMR_MR_LSB), or load lsb then
+ * msb (TMR_MR_BOTH).
+ *
+ * To read the current value ("on the fly") from the countdown register,
+ * you write a "latch" command into the mode register, then read the stable
+ * value from the corresponding I/O port. For example, you write
+ * TMR_MR_LATCH into the corresponding mode register. Presumably,
+ * after doing this, a write operation to the I/O port would result
+ * in undefined behavior (but hopefully not fry the chip).
+ * Reading in this manner has no side effects.
+ *
+ * The outputs of the three timers are connected as follows:
+ *
+ * timer 0 -> irq 0
+ * timer 1 -> dma chan 0 (for dram refresh)
+ * timer 2 -> speaker (via keyboard controller)
+ *
+ * Timer 0 is used to call hardclock.
+ * Timer 2 is used to generate console beeps.
+ */
+
+/*
+ * Macros for specifying values to be written into a mode register.
+ */
+#define TIMER_CNTR0 (IO_TIMER1 + 0) /* timer 0 counter port */
+#define TIMER_CNTR1 (IO_TIMER1 + 1) /* timer 1 counter port */
+#define TIMER_CNTR2 (IO_TIMER1 + 2) /* timer 2 counter port */
+#define TIMER_MODE (IO_TIMER1 + 3) /* timer mode port */
+#define TIMER_SEL0 0x00 /* select counter 0 */
+#define TIMER_SEL1 0x40 /* select counter 1 */
+#define TIMER_SEL2 0x80 /* select counter 2 */
+#define TIMER_INTTC 0x00 /* mode 0, intr on terminal cnt */
+#define TIMER_ONESHOT 0x02 /* mode 1, one shot */
+#define TIMER_RATEGEN 0x04 /* mode 2, rate generator */
+#define TIMER_SQWAVE 0x06 /* mode 3, square wave */
+#define TIMER_SWSTROBE 0x08 /* mode 4, s/w triggered strobe */
+#define TIMER_HWSTROBE 0x0a /* mode 5, h/w triggered strobe */
+#define TIMER_LATCH 0x00 /* latch counter for reading */
+#define TIMER_LSB 0x10 /* r/w counter LSB */
+#define TIMER_MSB 0x20 /* r/w counter MSB */
+#define TIMER_16BIT 0x30 /* r/w counter 16 bits, LSB first */
+#define TIMER_BCD 0x01 /* count in BCD */
+
diff --git a/sys/i386/isa/ultra14f.c b/sys/i386/isa/ultra14f.c
new file mode 100644
index 0000000..aa99917
--- /dev/null
+++ b/sys/i386/isa/ultra14f.c
@@ -0,0 +1,1155 @@
+/*
+ * Ported for use with the UltraStor 14f by Gary Close (gclose@wvnvms.wvnet.edu)
+ * Slight fixes to timeouts to run with the 34F
+ * Thanks to Julian Elischer for advice and help with this port.
+ *
+ * Written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * commenced: Sun Sep 27 18:14:01 PDT 1992
+ * slight mod to make work with 34F as well: Wed Jun 2 18:05:48 WST 1993
+ *
+ * $Id: ultra14f.c,v 1.16 1994/03/20 00:30:04 wollman Exp $
+ */
+
+#include <sys/types.h>
+
+#ifdef KERNEL /* don't laugh.. this compiles to a program too.. look */
+#include <uha.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+
+#include <i386/include/pio.h>
+#include <i386/isa/isa_device.h>
+#endif /*KERNEL */
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+/* */
+
+#ifdef KERNEL
+#include "ddb.h"
+#else /*KERNEL */
+#define NUHA 1
+#endif /*KERNEL */
+
+#ifndef NetBSD
+typedef timeout_func_t timeout_t;
+#endif
+
+typedef struct {
+ unsigned char addr[4];
+} physaddr;
+typedef struct {
+ unsigned char len[4];
+} physlen;
+
+#define KVTOPHYS(x) vtophys(x)
+
+#define UHA_MSCP_MAX 32 /* store up to 32MSCPs at any one time
+ * MAX = ?
+ */
+#define MSCP_HASH_SIZE 32 /* when we have a physical addr. for
+ * a mscp and need to find the mscp in
+ * space, look it up in the hash table
+ */
+#define MSCP_HASH_SHIFT 9 /* only hash on multiples of 512 */
+#define MSCP_HASH(x) ((((long int)(x))>>MSCP_HASH_SHIFT) % MSCP_HASH_SIZE)
+
+extern int hz;
+#define UHA_NSEG 33 /* number of dma segments supported */
+
+/************************** board definitions *******************************/
+/*
+ * I/O Port Interface
+ */
+#define UHA_LMASK (0x000) /* local doorbell mask reg */
+#define UHA_LINT (0x001) /* local doorbell int/stat reg */
+#define UHA_SMASK (0x002) /* system doorbell mask reg */
+#define UHA_SINT (0x003) /* system doorbell int/stat reg */
+#define UHA_ID0 (0x004) /* product id reg 0 */
+#define UHA_ID1 (0x005) /* product id reg 1 */
+#define UHA_CONF1 (0x006) /* config reg 1 */
+#define UHA_CONF2 (0x007) /* config reg 2 */
+#define UHA_OGM0 (0x008) /* outgoing mail ptr 0 least sig */
+#define UHA_OGM1 (0x009) /* outgoing mail ptr 1 least mid */
+#define UHA_OGM2 (0x00a) /* outgoing mail ptr 2 most mid */
+#define UHA_OGM3 (0x00b) /* outgoing mail ptr 3 most sig */
+#define UHA_ICM0 (0x00c) /* incoming mail ptr 0 */
+#define UHA_ICM1 (0x00d) /* incoming mail ptr 1 */
+#define UHA_ICM2 (0x00e) /* incoming mail ptr 2 */
+#define UHA_ICM3 (0x00f) /* incoming mail ptr 3 */
+
+/*
+ * UHA_LMASK bits (read only)
+ */
+
+#define UHA_LDIE 0x80 /* local doorbell int enabled */
+#define UHA_SRSTE 0x40 /* soft reset enabled */
+#define UHA_ABORTEN 0x10 /* abort MSCP enabled */
+#define UHA_OGMINTEN 0x01 /* outgoing mail interrupt enabled */
+
+/*
+ * UHA_LINT bits (read)
+ */
+
+#define UHA_LDIP 0x80 /* local doorbell int pending */
+
+/*
+ * UHA_LINT bits (write)
+ */
+
+#define UHA_ADRST 0x40 /* adapter soft reset */
+#define UHA_SBRST 0x20 /* scsi bus reset */
+#define UHA_ASRST 0x60 /* adapter and scsi reset */
+#define UHA_ABORT 0x10 /* abort MSCP */
+#define UHA_OGMINT 0x01 /* tell adapter to get mail */
+
+/*
+ * UHA_SMASK bits (read)
+ */
+
+#define UHA_SINTEN 0x80 /* system doorbell interupt Enabled */
+#define UHA_ABORT_COMPLETE_EN 0x10 /* abort MSCP command complete int Enabled */
+#define UHA_ICM_ENABLED 0x01 /* ICM interrupt enabled */
+
+/*
+ * UHA_SMASK bits (write)
+ */
+
+#define UHA_ENSINT 0x80 /* enable system doorbell interrupt */
+#define UHA_EN_ABORT_COMPLETE 0x10 /* enable abort MSCP complete int */
+#define UHA_ENICM 0x01 /* enable ICM interrupt */
+
+/*
+ * UHA_SINT bits (read)
+ */
+
+#define UHA_SINTP 0x80 /* system doorbell int pending */
+#define UHA_ABORT_SUCC 0x10 /* abort MSCP successful */
+#define UHA_ABORT_FAIL 0x18 /* abort MSCP failed */
+
+/*
+ * UHA_SINT bits (write)
+ */
+
+#define UHA_ABORT_ACK 0x18 /* acknowledge status and clear */
+#define UHA_ICM_ACK 0x01 /* acknowledge ICM and clear */
+
+/*
+ * UHA_CONF1 bits (read only)
+ */
+
+#define UHA_DMA_CH5 0x00 /* DMA channel 5 */
+#define UHA_DMA_CH6 0x40 /* 6 */
+#define UHA_DMA_CH7 0x80 /* 7 */
+#define UHA_IRQ15 0x00 /* IRQ 15 */
+#define UHA_IRQ14 0x10 /* 14 */
+#define UHA_IRQ11 0x20 /* 11 */
+#define UHA_IRQ10 0x30 /* 10 */
+
+/*
+ * ha_status error codes
+ */
+
+#define UHA_NO_ERR 0x00 /* No error supposedly */
+#define UHA_SBUS_ABORT_ERR 0x84 /* scsi bus abort error */
+#define UHA_SBUS_TIMEOUT 0x91 /* scsi bus selection timeout */
+#define UHA_SBUS_OVER_UNDER 0x92 /* scsi bus over/underrun */
+#define UHA_BAD_SCSI_CMD 0x96 /* illegal scsi command */
+#define UHA_AUTO_SENSE_ERR 0x9b /* auto request sense err */
+#define UHA_SBUS_RES_ERR 0xa3 /* scsi bus reset error */
+#define UHA_BAD_SG_LIST 0xff /* invalid scatter gath list */
+
+struct uha_dma_seg {
+ physaddr addr;
+ physlen len;
+};
+
+struct mscp {
+ unsigned char opcode:3;
+#define U14_HAC 0x01 /* host adapter command */
+#define U14_TSP 0x02 /* target scsi pass through command */
+#define U14_SDR 0x04 /* scsi device reset */
+ unsigned char xdir:2; /* xfer direction */
+#define U14_SDET 0x00 /* determined by scsi command */
+#define U14_SDIN 0x01 /* scsi data in */
+#define U14_SDOUT 0x02 /* scsi data out */
+#define U14_NODATA 0x03 /* no data xfer */
+ unsigned char dcn:1; /* disable disconnect for this command */
+ unsigned char ca:1; /* cache control */
+ unsigned char sgth:1; /* scatter gather flag */
+ unsigned char target:3;
+ unsigned char chan:2; /* scsi channel (always 0 for 14f) */
+ unsigned char lun:3;
+ physaddr data;
+ physlen datalen;
+ physaddr link;
+ unsigned char link_id;
+ unsigned char sg_num; /*number of scat gath segs */
+ /*in s-g list if sg flag is */
+ /*set. starts at 1, 8bytes per */
+ unsigned char senselen;
+ unsigned char cdblen;
+ unsigned char cdb[12];
+ unsigned char ha_status;
+ unsigned char targ_status;
+ physaddr sense; /* if 0 no auto sense */
+ /*-----------------end of hardware supported fields----------------*/
+ struct mscp *next; /* in free list */
+ struct scsi_xfer *xs; /* the scsi_xfer for this cmd */
+ int flags;
+#define MSCP_FREE 0
+#define MSCP_ACTIVE 1
+#define MSCP_ABORTED 2
+ struct uha_dma_seg uha_dma[UHA_NSEG];
+ struct scsi_sense_data mscp_sense;
+ struct mscp *nexthash;
+ long int hashkey;
+};
+
+struct uha_data {
+ int flags;
+#define UHA_INIT 0x01;
+ int baseport;
+ struct mscp *mscphash[MSCP_HASH_SIZE];
+ struct mscp *free_mscp;
+ int our_id; /* our scsi id */
+ int vect;
+ int dma;
+ int nummscps;
+ struct scsi_link sc_link;
+} *uhadata[NUHA];
+
+int uhaprobe();
+int uha_attach();
+int uhaintr();
+int32 uha_scsi_cmd();
+void uha_timeout(caddr_t);
+void uha_free_mscp();
+int uha_abort();
+void uhaminphys();
+void uha_done();
+u_int32 uha_adapter_info();
+struct mscp *uha_mscp_phys_kv();
+
+struct mscp *cheat;
+unsigned long int scratch;
+static uha_unit = 0;
+#define UHA_SHOWMSCPS 0x01
+#define UHA_SHOWINTS 0x02
+#define UHA_SHOWCMDS 0x04
+#define UHA_SHOWMISC 0x08
+#define FAIL 1
+#define SUCCESS 0
+#define PAGESIZ 4096
+
+#ifdef KERNEL
+struct isa_driver uhadriver =
+{
+ uhaprobe,
+ uha_attach,
+ "uha"
+};
+
+struct scsi_adapter uha_switch =
+{
+ uha_scsi_cmd,
+ uhaminphys,
+ 0,
+ 0,
+ uha_adapter_info,
+ "uha",
+ 0, 0
+};
+
+/* the below structure is so we have a default dev struct for out link struct */
+struct scsi_device uha_dev =
+{
+ NULL, /* Use default error handler */
+ NULL, /* have a queue, served by this */
+ NULL, /* have no async handler */
+ NULL, /* Use default 'done' routine */
+ "uha",
+ 0,
+ 0, 0
+};
+
+#endif /*KERNEL */
+
+#ifndef KERNEL
+main()
+{
+ printf("uha_data is %d bytes\n", sizeof(struct uha_data));
+ printf("mscp is %d bytes\n", sizeof(struct mscp));
+}
+
+#else /*KERNEL*/
+/*
+ * Function to send a command out through a mailbox
+ */
+void
+uha_send_mbox(int unit, struct mscp *mscp)
+{
+ struct uha_data *uha = uhadata[unit];
+ int port = uha->baseport;
+ int spincount = 100000; /* 1s should be enough */
+ int s = splbio();
+
+ while (--spincount) {
+ if ((inb(port + UHA_LINT) & UHA_LDIP) == 0)
+ break;
+ DELAY(100);
+ }
+ if (spincount == 0) {
+ printf("uha%d: uha_send_mbox, board not responding\n", unit);
+ Debugger("ultra14f");
+ }
+ outl(port + UHA_OGM0, KVTOPHYS(mscp));
+ outb(port + UHA_LINT, (UHA_OGMINT));
+ splx(s);
+}
+
+/*
+ * Function to send abort to 14f
+ */
+int
+uha_abort(int unit, struct mscp *mscp)
+{
+ struct uha_data *uha = uhadata[unit];
+ int port = uha->baseport;
+ int spincount = 100; /* 1 mSec */
+ int abortcount = 200000; /*2 secs */
+ int s = splbio();
+
+ while (--spincount) {
+ if ((inb(port + UHA_LINT) & UHA_LDIP) == 0)
+ break;
+ DELAY(10);
+ }
+ if (spincount == 0) {
+ printf("uha%d: uha_abort, board not responding\n", unit);
+ Debugger("ultra14f");
+ }
+ outl(port + UHA_OGM0, KVTOPHYS(mscp));
+ outb(port + UHA_LINT, UHA_ABORT);
+
+ while (--abortcount) {
+ if (inb(port + UHA_SINT) & UHA_ABORT_FAIL)
+ break;
+ DELAY(10);
+ }
+ if (abortcount == 0) {
+ printf("uha%d: uha_abort, board not responding\n", unit);
+ Debugger("ultra14f");
+ }
+ if ((inb(port + UHA_SINT) & 0x10) != 0) {
+ outb(port + UHA_SINT, UHA_ABORT_ACK);
+ splx(s);
+ return (1);
+ } else {
+ outb(port + UHA_SINT, UHA_ABORT_ACK);
+ splx(s);
+ return (0);
+ }
+}
+
+/*
+ * Function to poll for command completion when in poll mode.
+ *
+ * wait = timeout in msec
+ */
+int
+uha_poll(int unit, int wait)
+{
+ struct uha_data *uha = uhadata[unit];
+ int port = uha->baseport;
+ int stport = port + UHA_SINT;
+
+ retry:
+ while (--wait) {
+ if (inb(stport) & UHA_SINTP)
+ break;
+ DELAY(1000); /* 1 mSec per loop */
+ }
+ if (wait == 0) {
+ printf("uha%d: uha_poll, board not responding\n", unit);
+ return (EIO);
+ }
+ uhaintr(unit);
+ return (0);
+}
+
+/*
+ * Check if the device can be found at the port given and if so, set it up
+ * ready for further work as an argument, takes the isa_device structure
+ * from autoconf.c
+ */
+int
+uhaprobe(dev)
+ struct isa_device *dev;
+{
+ int unit = uha_unit;
+ struct uha_data *uha;
+
+ dev->id_unit = unit;
+
+ /*
+ * find unit and check we have that many defined
+ */
+ if (unit >= NUHA) {
+ printf("uha: unit number (%d) too high\n", unit);
+ return (0);
+ }
+ dev->id_unit = unit;
+
+ /*
+ * Allocate a storage area for us
+ */
+ if (uhadata[unit]) {
+ printf("uha%d: memory already allocated\n", unit);
+ return 0;
+ }
+ uha = malloc(sizeof(struct uha_data), M_TEMP, M_NOWAIT);
+ if (!uha) {
+ printf("uha%d: cannot malloc!\n", unit);
+ return 0;
+ }
+ bzero(uha, sizeof(struct uha_data));
+ uhadata[unit] = uha;
+ uha->baseport = dev->id_iobase;
+ /*
+ * Try initialise a unit at this location
+ * sets up dma and bus speed, loads uha->vect
+ */
+ if (uha_init(unit) != 0) {
+ uhadata[unit] = NULL;
+ free(uha, M_TEMP);
+ return (0);
+ }
+ /* if it's there put in its interrupt and DRQ vectors */
+ dev->id_irq = (1 << uha->vect);
+ dev->id_drq = uha->dma;
+
+ uha_unit++;
+ return (16);
+}
+
+/*
+ * Attach all the sub-devices we can find
+ */
+int
+uha_attach(dev)
+ struct isa_device *dev;
+{
+ int unit = dev->id_unit;
+ struct uha_data *uha = uhadata[unit];
+
+ /*
+ * fill in the prototype scsi_link.
+ */
+ uha->sc_link.adapter_unit = unit;
+ uha->sc_link.adapter_targ = uha->our_id;
+ uha->sc_link.adapter = &uha_switch;
+ uha->sc_link.device = &uha_dev;
+ uha->sc_link.flags = SDEV_BOUNCE;
+
+ /*
+ * ask the adapter what subunits are present
+ */
+ scsi_attachdevs(&(uha->sc_link));
+
+ return 1;
+}
+
+/*
+ * Return some information to the caller about
+ * the adapter and it's capabilities
+ */
+u_int32
+uha_adapter_info(unit)
+ int unit;
+{
+ return (2); /* 2 outstanding requests at a time per device */
+}
+
+/*
+ * Catch an interrupt from the adaptor
+ */
+int
+uhaintr(unit)
+ int unit;
+{
+ struct uha_data *uha = uhadata[unit];
+ struct mscp *mscp;
+ u_char uhastat;
+ unsigned long int mboxval;
+
+ int port = uha->baseport;
+
+#ifdef UHADEBUG
+ printf("uhaintr ");
+#endif /*UHADEBUG */
+
+ while (inb(port + UHA_SINT) & UHA_SINTP) {
+ /*
+ * First get all the information and then
+ * acknowledge the interrupt
+ */
+ uhastat = inb(port + UHA_SINT);
+ mboxval = inl(port + UHA_ICM0);
+ outb(port + UHA_SINT, UHA_ICM_ACK);
+
+#ifdef UHADEBUG
+ printf("status = 0x%x ", uhastat);
+#endif /*UHADEBUG*/
+ /*
+ * Process the completed operation
+ */
+
+ mscp = uha_mscp_phys_kv(uha, mboxval);
+ if (!mscp) {
+ printf("uha: BAD MSCP RETURNED\n");
+ return (0); /* whatever it was, it'll timeout */
+ }
+ untimeout((timeout_t)uha_timeout, (caddr_t)mscp);
+
+ uha_done(unit, mscp);
+ }
+ return (1);
+}
+
+/*
+ * We have a mscp which has been processed by the adaptor, now we look to see
+ * how the operation went.
+ */
+void
+uha_done(unit, mscp)
+ int unit;
+ struct mscp *mscp;
+{
+ struct uha_data *uha = uhadata[unit];
+ struct scsi_sense_data *s1, *s2;
+ struct scsi_xfer *xs = mscp->xs;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB2, ("uha_done\n"));
+ /*
+ * Otherwise, put the results of the operation
+ * into the xfer and call whoever started it
+ */
+ if ((mscp->ha_status == UHA_NO_ERR) || (xs->flags & SCSI_ERR_OK)) { /* All went correctly OR errors expected */
+ xs->resid = 0;
+ xs->error = 0;
+ } else {
+
+ s1 = &(mscp->mscp_sense);
+ s2 = &(xs->sense);
+
+ if (mscp->ha_status != UHA_NO_ERR) {
+ switch (mscp->ha_status) {
+ case UHA_SBUS_TIMEOUT: /* No response */
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("timeout reported back\n"));
+ xs->error = XS_TIMEOUT;
+ break;
+ case UHA_SBUS_OVER_UNDER:
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("scsi bus xfer over/underrun\n"));
+ xs->error = XS_DRIVER_STUFFUP;
+ break;
+ case UHA_BAD_SG_LIST:
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("bad sg list reported back\n"));
+ xs->error = XS_DRIVER_STUFFUP;
+ break;
+ default: /* Other scsi protocol messes */
+ xs->error = XS_DRIVER_STUFFUP;
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("unexpected ha_status: %x\n",
+ mscp->ha_status));
+ }
+ } else {
+
+ if (mscp->targ_status != 0)
+/*
+ * I have no information for any possible value of target status field
+ * other than 0 means no error!! So I guess any error is unexpected in that
+ * event!!
+ */
+
+ {
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("unexpected targ_status: %x\n",
+ mscp->targ_status));
+ xs->error = XS_DRIVER_STUFFUP;
+ }
+ }
+ }
+ done:
+ xs->flags |= ITSDONE;
+ uha_free_mscp(unit, mscp, xs->flags);
+ scsi_done(xs);
+}
+
+/*
+ * A mscp (and hence a mbx-out) is put onto the free list.
+ */
+void
+uha_free_mscp(unit, mscp, flags)
+ int unit;
+ struct mscp *mscp;
+ int flags;
+{
+ struct uha_data *uha = uhadata[unit];
+ unsigned int opri = 0;
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+
+ mscp->next = uha->free_mscp;
+ uha->free_mscp = mscp;
+ mscp->flags = MSCP_FREE;
+ /*
+ * If there were none, wake abybody waiting for
+ * one to come free, starting with queued entries
+ */
+ if (!mscp->next) {
+ wakeup((caddr_t)&uha->free_mscp);
+ }
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+}
+
+/*
+ * Get a free mscp
+ *
+ * If there are none, see if we can allocate a new one. If so, put it in the
+ * hash table too otherwise either return an error or sleep.
+ */
+struct mscp *
+uha_get_mscp(unit, flags)
+ int unit, flags;
+{
+ struct uha_data *uha = uhadata[unit];
+ unsigned opri = 0;
+ struct mscp *mscpp;
+ int hashnum;
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+ /*
+ * If we can and have to, sleep waiting for one to come free
+ * but only if we can't allocate a new one
+ */
+ while (!(mscpp = uha->free_mscp)) {
+ if (uha->nummscps < UHA_MSCP_MAX) {
+ if (mscpp = (struct mscp *)malloc(sizeof(struct mscp),
+ M_TEMP,
+ M_NOWAIT)) {
+ bzero(mscpp, sizeof(struct mscp));
+ uha->nummscps++;
+ mscpp->flags = MSCP_ACTIVE;
+ /*
+ * put in the phystokv hash table
+ * Never gets taken out.
+ */
+ mscpp->hashkey = KVTOPHYS(mscpp);
+ hashnum = MSCP_HASH(mscpp->hashkey);
+ mscpp->nexthash = uha->mscphash[hashnum];
+ uha->mscphash[hashnum] = mscpp;
+ } else {
+ printf("uha%d: Can't malloc MSCP\n", unit);
+ }
+ goto gottit;
+ } else {
+ if (!(flags & SCSI_NOSLEEP)) {
+ tsleep((caddr_t)&uha->free_mscp, PRIBIO,
+ "uhamscp", 0);
+ }
+ }
+ }
+ if (mscpp) {
+ /* Get MSCP from from free list */
+ uha->free_mscp = mscpp->next;
+ mscpp->flags = MSCP_ACTIVE;
+ }
+ gottit:
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+
+ return (mscpp);
+}
+
+/*
+ * given a physical address, find the mscp that it corresponds to.
+ */
+struct mscp *
+uha_mscp_phys_kv(uha, mscp_phys)
+ struct uha_data *uha;
+ long int mscp_phys;
+{
+ int hashnum = MSCP_HASH(mscp_phys);
+ struct mscp *mscpp = uha->mscphash[hashnum];
+
+ while (mscpp) {
+ if (mscpp->hashkey == mscp_phys)
+ break;
+ mscpp = mscpp->nexthash;
+ }
+ return mscpp;
+}
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+uha_init(unit)
+ int unit;
+{
+ struct uha_data *uha = uhadata[unit];
+ unsigned char ad[4];
+ volatile unsigned char model;
+ volatile unsigned char submodel;
+ unsigned char config_reg1;
+ unsigned char config_reg2;
+ unsigned char dma_ch;
+ unsigned char irq_ch;
+ unsigned char uha_id;
+ int port = uha->baseport;
+ int i;
+ int resetcount = 4000; /* 4 secs? */
+
+ model = inb(port + UHA_ID0);
+ submodel = inb(port + UHA_ID1);
+ if ((model != 0x56) & (submodel != 0x40)) {
+ printf("uha%d: uha_init, board not responding\n", unit);
+ return (ENXIO);
+ }
+ printf("uha%d: reading board settings, ", unit);
+
+ config_reg1 = inb(port + UHA_CONF1);
+ config_reg2 = inb(port + UHA_CONF2);
+ dma_ch = (config_reg1 & 0xc0);
+ irq_ch = (config_reg1 & 0x30);
+ uha_id = (config_reg2 & 0x07);
+
+ switch (dma_ch) {
+ case UHA_DMA_CH5:
+ uha->dma = 5;
+ printf("dma=5 ");
+ break;
+ case UHA_DMA_CH6:
+ uha->dma = 6;
+ printf("dma=6 ");
+ break;
+ case UHA_DMA_CH7:
+ uha->dma = 7;
+ printf("dma=7 ");
+ break;
+ default:
+ printf("illegal dma jumper setting\n");
+ return (EIO);
+ }
+ switch (irq_ch) {
+ case UHA_IRQ10:
+ uha->vect = 10;
+ printf("int=10 ");
+ break;
+ case UHA_IRQ11:
+ uha->vect = 11;
+ printf("int=11 ");
+ break;
+ case UHA_IRQ14:
+ uha->vect = 14;
+ printf("int=14 ");
+ break;
+ case UHA_IRQ15:
+ uha->vect = 15;
+ printf("int=15 ");
+ break;
+ default:
+ printf("illegal int jumper setting\n");
+ return (EIO);
+ }
+
+ /* who are we on the scsi bus */
+ printf("id=%x\n", uha_id);
+ uha->our_id = uha_id;
+
+ /*
+ * Note that we are going and return (to probe)
+ */
+ outb(port + UHA_LINT, UHA_ASRST);
+ while (--resetcount) {
+ if (inb(port + UHA_LINT))
+ break;
+ DELAY(1000); /* 1 mSec per loop */
+ }
+ if (resetcount == 0) {
+ printf("uha%d: board timed out during reset\n", unit);
+ return (ENXIO);
+ }
+ outb(port + UHA_SMASK, 0x81); /* make sure interrupts are enabled */
+ uha->flags |= UHA_INIT;
+ return (0);
+}
+
+#ifndef min
+#define min(x,y) (x < y ? x : y)
+#endif /* min */
+
+void
+uhaminphys(bp)
+ struct buf *bp;
+{
+ if (bp->b_bcount > ((UHA_NSEG - 1) * PAGESIZ)) {
+ bp->b_bcount = ((UHA_NSEG - 1) * PAGESIZ);
+ }
+}
+
+/*
+ * start a scsi operation given the command and the data address. Also
+ * needs the unit, target and lu.
+ */
+int32
+uha_scsi_cmd(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_sense_data *s1, *s2;
+ struct mscp *mscp;
+ struct uha_dma_seg *sg;
+ int seg; /* scatter gather seg being worked on */
+ int i = 0;
+ int rc = 0;
+ int thiskv;
+ unsigned long int thisphys, nextphys;
+ int unit = xs->sc_link->adapter_unit;
+ int bytes_this_seg, bytes_this_page, datalen, flags;
+ struct iovec *iovp;
+ struct uha_data *uha = uhadata[unit];
+ int s;
+ unsigned int stat;
+ int port = uha->baseport;
+ unsigned long int templen;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB2, ("uha_scsi_cmd\n"));
+ /*
+ * get a mscp (mbox-out) to use. If the transfer
+ * is from a buf (possibly from interrupt time)
+ * then we can't allow it to sleep
+ */
+ flags = xs->flags;
+ if (xs->bp)
+ flags |= (SCSI_NOSLEEP); /* just to be sure */
+ if (flags & ITSDONE) {
+ printf("uha%d: Already done?", unit);
+ xs->flags &= ~ITSDONE;
+ }
+ if (!(flags & INUSE)) {
+ printf("uha%d: Not in use?", unit);
+ xs->flags |= INUSE;
+ }
+ if (!(mscp = uha_get_mscp(unit, flags))) {
+ xs->error = XS_DRIVER_STUFFUP;
+ return (TRY_AGAIN_LATER);
+ }
+ cheat = mscp;
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("start mscp(%x)\n", mscp));
+ mscp->xs = xs;
+
+ /*
+ * Put all the arguments for the xfer in the mscp
+ */
+ if (flags & SCSI_RESET) {
+ mscp->opcode = 0x04;
+ mscp->ca = 0x01;
+ } else {
+ mscp->opcode = 0x02;
+ mscp->ca = 0x01;
+ }
+ if (flags & SCSI_DATA_IN) {
+ mscp->xdir = 0x01;
+ }
+ if (flags & SCSI_DATA_OUT) {
+ mscp->xdir = 0x02;
+ }
+#ifdef GOTTABEJOKING
+ if (xs->sc_link->lun != 0) {
+ xs->error = XS_DRIVER_STUFFUP;
+ uha_free_mscp(unit, mscp, flags);
+ return (HAD_ERROR);
+ }
+#endif
+ mscp->dcn = 0x00;
+ mscp->chan = 0x00;
+ mscp->target = xs->sc_link->target;
+ mscp->lun = xs->sc_link->lun;
+ mscp->link.addr[0] = 0x00;
+ mscp->link.addr[1] = 0x00;
+ mscp->link.addr[2] = 0x00;
+ mscp->link.addr[3] = 0x00;
+ mscp->link_id = 0x00;
+ mscp->cdblen = xs->cmdlen;
+ scratch = KVTOPHYS(&(mscp->mscp_sense));
+ mscp->sense.addr[0] = (scratch & 0xff);
+ mscp->sense.addr[1] = ((scratch >> 8) & 0xff);
+ mscp->sense.addr[2] = ((scratch >> 16) & 0xff);
+ mscp->sense.addr[3] = ((scratch >> 24) & 0xff);
+ mscp->senselen = sizeof(mscp->mscp_sense);
+ mscp->ha_status = 0x00;
+ mscp->targ_status = 0x00;
+
+ if (xs->datalen) { /* should use S/G only if not zero length */
+ scratch = KVTOPHYS(mscp->uha_dma);
+ mscp->data.addr[0] = (scratch & 0xff);
+ mscp->data.addr[1] = ((scratch >> 8) & 0xff);
+ mscp->data.addr[2] = ((scratch >> 16) & 0xff);
+ mscp->data.addr[3] = ((scratch >> 24) & 0xff);
+ sg = mscp->uha_dma;
+ seg = 0;
+ mscp->sgth = 0x01;
+
+#ifdef TFS
+ if (flags & SCSI_DATA_UIO) {
+ iovp = ((struct uio *) xs->data)->uio_iov;
+ datalen = ((struct uio *) xs->data)->uio_iovcnt;
+ xs->datalen = 0;
+ while ((datalen) && (seg < UHA_NSEG)) {
+ scratch = (unsigned long) iovp->iov_base;
+ sg->addr.addr[0] = (scratch & 0xff);
+ sg->addr.addr[1] = ((scratch >> 8) & 0xff);
+ sg->addr.addr[2] = ((scratch >> 16) & 0xff);
+ sg->addr.addr[3] = ((scratch >> 24) & 0xff);
+ xs->datalen += *(unsigned long *) sg->len.len = iovp->iov_len;
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("(0x%x@0x%x)",
+ iovp->iov_len,
+ iovp->iov_base));
+ sg++;
+ iovp++;
+ seg++;
+ datalen--;
+ }
+ } else
+#endif /*TFS */
+ {
+ /*
+ * Set up the scatter gather block
+ */
+
+ SC_DEBUG(xs->sc_link, SDEV_DB4,
+ ("%d @0x%x:- ", xs->datalen, xs->data));
+ datalen = xs->datalen;
+ thiskv = (int) xs->data;
+ thisphys = KVTOPHYS(thiskv);
+ templen = 0;
+
+ while ((datalen) && (seg < UHA_NSEG)) {
+ bytes_this_seg = 0;
+
+ /* put in the base address */
+ sg->addr.addr[0] = (thisphys & 0xff);
+ sg->addr.addr[1] = ((thisphys >> 8) & 0xff);
+ sg->addr.addr[2] = ((thisphys >> 16) & 0xff);
+ sg->addr.addr[3] = ((thisphys >> 24) & 0xff);
+
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("0x%x", thisphys));
+
+ /* do it at least once */
+ nextphys = thisphys;
+ while ((datalen) && (thisphys == nextphys))
+ /*
+ * This page is contiguous (physically) with
+ * the the last, just extend the length
+ */
+ {
+ /* how far to the end of the page */
+ nextphys = (thisphys & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ bytes_this_page = nextphys - thisphys;
+ /**** or the data ****/
+ bytes_this_page = min(bytes_this_page
+ ,datalen);
+ bytes_this_seg += bytes_this_page;
+ datalen -= bytes_this_page;
+
+ /* get more ready for the next page */
+ thiskv = (thiskv & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ if (datalen)
+ thisphys = KVTOPHYS(thiskv);
+ }
+ /*
+ * next page isn't contiguous, finish the seg
+ */
+ SC_DEBUGN(xs->sc_link, SDEV_DB4,
+ ("(0x%x)", bytes_this_seg));
+ sg->len.len[0] = (bytes_this_seg & 0xff);
+ sg->len.len[1] = ((bytes_this_seg >> 8) & 0xff);
+ sg->len.len[2] = ((bytes_this_seg >> 16) & 0xff);
+ sg->len.len[3] = ((bytes_this_seg >> 24) & 0xff);
+ templen += bytes_this_seg;
+ sg++;
+ seg++;
+ }
+ }
+
+ /* end of iov/kv decision */
+ mscp->datalen.len[0] = (templen & 0xff);
+ mscp->datalen.len[1] = ((templen >> 8) & 0xff);
+ mscp->datalen.len[2] = ((templen >> 16) & 0xff);
+ mscp->datalen.len[3] = ((templen >> 24) & 0xff);
+ mscp->sg_num = seg;
+
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("\n"));
+ if (datalen) { /* there's still data, must have run out of segs! */
+ printf("uha%d: uha_scsi_cmd, more than %d DMA segs\n",
+ unit, UHA_NSEG);
+ xs->error = XS_DRIVER_STUFFUP;
+ uha_free_mscp(unit, mscp, flags);
+ return (HAD_ERROR);
+ }
+ } else { /* No data xfer, use non S/G values */
+ mscp->data.addr[0] = 0x00;
+ mscp->data.addr[1] = 0x00;
+ mscp->data.addr[2] = 0x00;
+ mscp->data.addr[3] = 0x00;
+ mscp->datalen.len[0] = 0x00;
+ mscp->datalen.len[1] = 0x00;
+ mscp->datalen.len[2] = 0x00;
+ mscp->datalen.len[3] = 0x00;
+ mscp->xdir = 0x03;
+ mscp->sgth = 0x00;
+ mscp->sg_num = 0x00;
+ }
+
+ /*
+ * Put the scsi command in the mscp and start it
+ */
+ bcopy(xs->cmd, mscp->cdb, xs->cmdlen);
+
+ /*
+ * Usually return SUCCESSFULLY QUEUED
+ */
+ if (!(flags & SCSI_NOMASK)) {
+ s = splbio();
+ uha_send_mbox(unit, mscp);
+ timeout((timeout_t)uha_timeout, (caddr_t)mscp, (xs->timeout * hz) / 1000);
+ splx(s);
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_sent\n"));
+ return (SUCCESSFULLY_QUEUED);
+ }
+
+ /*
+ * If we can't use interrupts, poll on completion
+ */
+ uha_send_mbox(unit, mscp);
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_wait\n"));
+ do {
+ if (uha_poll(unit, xs->timeout)) {
+ if (!(xs->flags & SCSI_SILENT))
+ printf("uha%d: cmd fail\n", unit);
+ if (!(uha_abort(unit, mscp))) {
+ printf("uha%d: abort failed in wait\n", unit);
+ uha_free_mscp(unit, mscp, flags);
+ }
+ xs->error = XS_DRIVER_STUFFUP;
+ return (HAD_ERROR);
+ }
+ }
+ while (!(xs->flags & ITSDONE)); /* something (?) else finished */
+ if (xs->error) {
+ return (HAD_ERROR);
+ }
+ return (COMPLETE);
+}
+
+void
+uha_timeout(caddr_t arg1)
+{
+ struct mscp *mscp = (struct mscp *)arg1;
+ int unit;
+ struct uha_data *uha;
+ int s = splbio();
+ /*int port = uha->baseport; */
+
+ unit = mscp->xs->sc_link->adapter_unit;
+ uha = uhadata[unit];
+ printf("uha%d:%d:%d (%s%d) timed out ", unit
+ ,mscp->xs->sc_link->target
+ ,mscp->xs->sc_link->lun
+ ,mscp->xs->sc_link->device->name
+ ,mscp->xs->sc_link->dev_unit);
+
+#ifdef UHADEBUG
+ uha_print_active_mscp(unit);
+#endif /*UHADEBUG */
+
+ if ((uha_abort(unit, mscp) != 1) || (mscp->flags = MSCP_ABORTED)) {
+ printf("AGAIN");
+ mscp->xs->retries = 0; /* I MEAN IT ! */
+ uha_done(unit, mscp, FAIL);
+ } else { /* abort the operation that has timed out */
+ printf("\n");
+ timeout((timeout_t)uha_timeout, (caddr_t)mscp, 2 * hz);
+ mscp->flags = MSCP_ABORTED;
+ }
+ splx(s);
+}
+
+#ifdef UHADEBUG
+void
+uha_print_mscp(mscp)
+ struct mscp *mscp;
+{
+ printf("mscp:%x op:%x cmdlen:%d senlen:%d\n"
+ ,mscp
+ ,mscp->opcode
+ ,mscp->cdblen
+ ,mscp->senselen);
+ printf(" sg:%d sgnum:%x datlen:%d hstat:%x tstat:%x flags:%x\n"
+ ,mscp->sgth
+ ,mscp->sg_num
+ ,mscp->datalen
+ ,mscp->ha_status
+ ,mscp->targ_status
+ ,mscp->flags);
+ show_scsi_cmd(mscp->xs);
+}
+
+void
+uha_print_active_mscp(int unit)
+{
+ struct uha_data *uha = uhadata[unit];
+ struct mscp *mscp;
+ int i = 0;
+
+ while (i < MSCP_HASH_SIZE) {
+ mscp = uha->mscphash[i];
+ while (mscp) {
+ if (mscp->flags != MSCP_FREE) {
+ uha_print_mscp(mscp);
+ }
+ mscp = mscp->nexthash;
+ }
+ i++;
+ }
+}
+#endif /*UHADEBUG */
+#endif /*KERNEL */
diff --git a/sys/i386/isa/vector.s b/sys/i386/isa/vector.s
new file mode 100644
index 0000000..7135ae7
--- /dev/null
+++ b/sys/i386/isa/vector.s
@@ -0,0 +1,360 @@
+/*
+ * from: vector.s, 386BSD 0.1 unknown origin
+ * $Id: vector.s,v 1.6 1994/01/10 23:15:09 ache Exp $
+ */
+
+#include "i386/isa/icu.h"
+#include "i386/isa/isa.h"
+#include "vector.h"
+
+#define ICU_EOI 0x20 /* XXX - define elsewhere */
+
+#define IRQ_BIT(irq_num) (1 << ((irq_num) % 8))
+#define IRQ_BYTE(irq_num) ((irq_num) / 8)
+
+#ifdef AUTO_EOI_1
+#define ENABLE_ICU1 /* use auto-EOI to reduce i/o */
+#else
+#define ENABLE_ICU1 \
+ movb $ICU_EOI,%al ; /* as soon as possible send EOI ... */ \
+ FASTER_NOP ; /* ... ASAP ... */ \
+ outb %al,$IO_ICU1 /* ... to clear in service bit */
+#endif
+
+#ifdef AUTO_EOI_2
+/*
+ * The data sheet says no auto-EOI on slave, but it sometimes works.
+ */
+#define ENABLE_ICU1_AND_2 ENABLE_ICU1
+#else
+#define ENABLE_ICU1_AND_2 \
+ movb $ICU_EOI,%al ; /* as above */ \
+ FASTER_NOP ; \
+ outb %al,$IO_ICU2 ; /* but do second icu first */ \
+ FASTER_NOP ; \
+ outb %al,$IO_ICU1 /* then first icu */
+#endif
+
+#ifdef FAST_INTR_HANDLER_USES_ES
+#define ACTUALLY_PUSHED 1
+#define MAYBE_MOVW_AX_ES movl %ax,%es
+#define MAYBE_POPL_ES popl %es
+#define MAYBE_PUSHL_ES pushl %es
+#else
+/*
+ * We can usually skip loading %es for fastintr handlers. %es should
+ * only be used for string instructions, and fastintr handlers shouldn't
+ * do anything slow enough to justify using a string instruction.
+ */
+#define ACTUALLY_PUSHED 0
+#define MAYBE_MOVW_AX_ES
+#define MAYBE_POPL_ES
+#define MAYBE_PUSHL_ES
+#endif
+
+/*
+ * Macros for interrupt interrupt entry, call to handler, and exit.
+ *
+ * XXX - the interrupt frame is set up to look like a trap frame. This is
+ * usually a waste of time. The only interrupt handlers that want a frame
+ * are the clock handler (it wants a clock frame), the npx handler (it's
+ * easier to do right all in assembler). The interrupt return routine
+ * needs a trap frame for rare AST's (it could easily convert the frame).
+ * The direct costs of setting up a trap frame are two pushl's (error
+ * code and trap number), an addl to get rid of these, and pushing and
+ * popping the call-saved regs %esi, %edi and %ebp twice, The indirect
+ * costs are making the driver interface nonuniform so unpending of
+ * interrupts is more complicated and slower (call_driver(unit) would
+ * be easier than ensuring an interrupt frame for all handlers. Finally,
+ * there are some struct copies in the npx handler and maybe in the clock
+ * handler that could be avoided by working more with pointers to frames
+ * instead of frames.
+ *
+ * XXX - should we do a cld on every system entry to avoid the requirement
+ * for scattered cld's?
+ *
+ * Coding notes for *.s:
+ *
+ * If possible, avoid operations that involve an operand size override.
+ * Word-sized operations might be smaller, but the operand size override
+ * makes them slower on on 486's and no faster on 386's unless perhaps
+ * the instruction pipeline is depleted. E.g.,
+ *
+ * Use movl to seg regs instead of the equivalent but more descriptive
+ * movw - gas generates an irelevant (slower) operand size override.
+ *
+ * Use movl to ordinary regs in preference to movw and especially
+ * in preference to movz[bw]l. Use unsigned (long) variables with the
+ * top bits clear instead of unsigned short variables to provide more
+ * opportunities for movl.
+ *
+ * If possible, use byte-sized operations. They are smaller and no slower.
+ *
+ * Use (%reg) instead of 0(%reg) - gas generates larger code for the latter.
+ *
+ * If the interrupt frame is made more flexible, INTR can push %eax first
+ * and decide the ipending case with less overhead, e.g., by avoiding
+ * loading segregs.
+ */
+
+#define FAST_INTR(unit, irq_num, id_num, handler, enable_icus) \
+ pushl %eax ; /* save only call-used registers */ \
+ pushl %ecx ; \
+ pushl %edx ; \
+ pushl %ds ; \
+ MAYBE_PUSHL_ES ; \
+ movl $KDSEL,%eax ; \
+ movl %ax,%ds ; \
+ MAYBE_MOVW_AX_ES ; \
+ FAKE_MCOUNT((4+ACTUALLY_PUSHED)*4(%esp)) ; \
+ pushl $unit ; \
+ call handler ; /* do the work ASAP */ \
+ enable_icus ; /* (re)enable ASAP (helps edge trigger?) */ \
+ addl $4,%esp ; \
+ incl _cnt+V_INTR ; /* book-keeping can wait */ \
+ incl _intrcnt_actv + (id_num) * 4 ; \
+ movl _cpl,%eax ; /* are we unmasking pending HWIs or SWIs? */ \
+ notl %eax ; \
+ andl _ipending,%eax ; \
+ jne 1f ; /* yes, handle them */ \
+ MEXITCOUNT ; \
+ MAYBE_POPL_ES ; \
+ popl %ds ; \
+ popl %edx ; \
+ popl %ecx ; \
+ popl %eax ; \
+ iret ; \
+; \
+ ALIGN_TEXT ; \
+1: ; \
+ movl _cpl,%eax ; \
+ movl $HWI_MASK|SWI_MASK,_cpl ; /* limit nesting ... */ \
+ sti ; /* ... to do this as early as possible */ \
+ MAYBE_POPL_ES ; /* discard most of thin frame ... */ \
+ popl %ecx ; /* ... original %ds ... */ \
+ popl %edx ; \
+ xchgl %eax,(1+ACTUALLY_PUSHED)*4(%esp) ; /* orig %eax; save cpl */ \
+ pushal ; /* build fat frame (grrr) ... */ \
+ pushl %ecx ; /* ... actually %ds ... */ \
+ pushl %es ; \
+ movl $KDSEL,%eax ; \
+ movl %ax,%es ; \
+ movl (2+8+0)*4(%esp),%ecx ; /* ... %ecx from thin frame ... */ \
+ movl %ecx,(2+6)*4(%esp) ; /* ... to fat frame ... */ \
+ movl (2+8+1)*4(%esp),%eax ; /* ... cpl from thin frame */ \
+ pushl %eax ; \
+ subl $4,%esp ; /* junk for unit number */ \
+ MEXITCOUNT ; \
+ jmp _doreti
+
+#define INTR(unit, irq_num, id_num, mask, handler, icu, enable_icus, reg, stray) \
+ pushl $0 ; /* dumby error code */ \
+ pushl $0 ; /* dumby trap type */ \
+ pushal ; \
+ pushl %ds ; /* save our data and extra segments ... */ \
+ pushl %es ; \
+ movl $KDSEL,%eax ; /* ... and reload with kernel's own ... */ \
+ movl %ax,%ds ; /* ... early for obsolete reasons */ \
+ movl %ax,%es ; \
+ movb _imen + IRQ_BYTE(irq_num),%al ; \
+ orb $IRQ_BIT(irq_num),%al ; \
+ movb %al,_imen + IRQ_BYTE(irq_num) ; \
+ FASTER_NOP ; \
+ outb %al,$icu+1 ; \
+ enable_icus ; \
+ incl _cnt+V_INTR ; /* tally interrupts */ \
+ movl _cpl,%eax ; \
+ testb $IRQ_BIT(irq_num),%reg ; \
+ jne 2f ; \
+1: ; \
+ FAKE_MCOUNT(12*4(%esp)) ; /* XXX late to avoid double count */ \
+ incl _intrcnt_actv + (id_num) * 4 ; \
+ movl _cpl,%eax ; \
+ pushl %eax ; \
+ pushl $unit ; \
+ orl mask,%eax ; \
+ movl %eax,_cpl ; \
+ sti ; \
+ call handler ; \
+ movb _imen + IRQ_BYTE(irq_num),%al ; \
+ andb $~IRQ_BIT(irq_num),%al ; \
+ movb %al,_imen + IRQ_BYTE(irq_num) ; \
+ FASTER_NOP ; \
+ outb %al,$icu+1 ; \
+ MEXITCOUNT ; \
+ /* We could usually avoid the following jmp by inlining some of */ \
+ /* _doreti, but it's probably better to use less cache. */ \
+ jmp _doreti ; \
+; \
+ ALIGN_TEXT ; \
+2: ; \
+ /* XXX skip mcounting here to avoid double count */ \
+ movl $1b,%eax ; /* register resume address */ \
+ /* XXX - someday do it at attach time */ \
+ movl %eax,ihandlers + (irq_num) * 4 ; \
+ orb $IRQ_BIT(irq_num),_ipending + IRQ_BYTE(irq_num) ; \
+ popl %es ; \
+ popl %ds ; \
+ popal ; \
+ addl $4+4,%esp ; \
+ iret
+
+/*
+ * vector.h has defined a macro 'BUILD_VECTORS' containing a big list of info
+ * about vectors, including a submacro 'BUILD_VECTOR' that operates on the
+ * info about each vector. We redefine 'BUILD_VECTOR' to expand the info
+ * in different ways. Here we expand it to a list of interrupt handlers.
+ * This order is of course unimportant. Elsewhere we expand it to inline
+ * linear search code for which the order is a little more important and
+ * concatenating the code with no holes is very important.
+ *
+ * XXX - now there is BUILD_FAST_VECTOR as well as BUILD_VECTOR.
+ *
+ * The info consists of the following items for each vector:
+ *
+ * name (identifier): name of the vector; used to build labels
+ * unit (expression): unit number to call the device driver with
+ * irq_num (number): number of the IRQ to handled (0-15)
+ * id_num (number): uniq numeric id for handler (assigned by config)
+ * mask (blank-ident): priority mask used
+ * handler (blank-ident): interrupt handler to call
+ * icu_num (number): (1 + irq_num / 8) converted for label building
+ * icu_enables (number): 1 for icu_num == 1, 1_AND_2 for icu_num == 2
+ * reg (blank-ident): al for icu_num == 1, ah for icu_num == 2
+ *
+ * 'irq_num' is converted in several ways at config time to get around
+ * limitations in cpp. The macros have blanks after commas iff they would
+ * not mess up identifiers and numbers.
+ */
+
+#undef BUILD_FAST_VECTOR
+#define BUILD_FAST_VECTOR(name, unit, irq_num, id_num, mask, handler, \
+ icu_num, icu_enables, reg) \
+ .globl handler ; \
+ .text ; \
+ .globl _V/**/name ; \
+ SUPERALIGN_TEXT ; \
+_V/**/name: ; \
+ FAST_INTR(unit, irq_num,id_num, handler, ENABLE_ICU/**/icu_enables)
+
+#undef BUILD_VECTOR
+#define BUILD_VECTOR(name, unit, irq_num, id_num, mask, handler, \
+ icu_num, icu_enables, reg) \
+ .globl handler ; \
+ .text ; \
+ .globl _V/**/name ; \
+ SUPERALIGN_TEXT ; \
+_V/**/name: ; \
+ INTR(unit,irq_num, id_num, mask, handler, IO_ICU/**/icu_num, \
+ ENABLE_ICU/**/icu_enables, reg,)
+
+MCOUNT_LABEL(bintr)
+ BUILD_VECTORS
+
+ /* hardware interrupt catcher (IDT 32 - 47) */
+ .globl _isa_strayintr
+
+#define STRAYINTR(irq_num, icu_num, icu_enables, reg) \
+IDTVEC(intr/**/irq_num) ; \
+ INTR(irq_num,irq_num,irq_num, _high_imask, _isa_strayintr, \
+ IO_ICU/**/icu_num, ENABLE_ICU/**/icu_enables, reg,stray)
+
+/*
+ * XXX - the mask (1 << 2) == IRQ_SLAVE will be generated for IRQ 2, instead
+ * of the mask IRQ2 (defined as IRQ9 == (1 << 9)). But IRQ 2 "can't happen".
+ * In fact, all stray interrupts "can't happen" except for bugs. The
+ * "stray" IRQ 7 is documented behaviour of the 8259. It happens when there
+ * is a glitch on any of its interrupt inputs. Does it really interrupt when
+ * IRQ 7 is masked?
+ *
+ * XXX - unpend doesn't work for these, it sends them to the real handler.
+ *
+ * XXX - the race bug during initialization may be because I changed the
+ * order of switching from the stray to the real interrupt handler to before
+ * enabling interrupts. The old order looked unsafe but maybe it is OK with
+ * the stray interrupt handler installed. But these handlers only reduce
+ * the window of vulnerability - it is still open at the end of
+ * isa_configure().
+ *
+ * XXX - many comments are stale.
+ */
+
+ STRAYINTR(0,1,1, al)
+ STRAYINTR(1,1,1, al)
+ STRAYINTR(2,1,1, al)
+ STRAYINTR(3,1,1, al)
+ STRAYINTR(4,1,1, al)
+ STRAYINTR(5,1,1, al)
+ STRAYINTR(6,1,1, al)
+ STRAYINTR(7,1,1, al)
+ STRAYINTR(8,2,1_AND_2, ah)
+ STRAYINTR(9,2,1_AND_2, ah)
+ STRAYINTR(10,2,1_AND_2, ah)
+ STRAYINTR(11,2,1_AND_2, ah)
+ STRAYINTR(12,2,1_AND_2, ah)
+ STRAYINTR(13,2,1_AND_2, ah)
+ STRAYINTR(14,2,1_AND_2, ah)
+ STRAYINTR(15,2,1_AND_2, ah)
+#if 0
+ INTRSTRAY(255, _highmask, 255) ; call _isa_strayintr ; INTREXIT2
+#endif
+MCOUNT_LABEL(eintr)
+
+/*
+ * These are the interrupt counters, I moved them here from icu.s so that
+ * they are with the name table. rgrimes
+ *
+ * There are now lots of counters, this has been redone to work with
+ * Bruce Evans intr-0.1 code, which I modified some more to make it all
+ * work with vmstat.
+ */
+ .data
+ihandlers: /* addresses of interrupt handlers */
+ .space NHWI*4 /* actually resumption addresses for HWI's */
+ .long swi_tty, swi_net, 0, 0, 0, 0, 0, 0
+ .long 0, 0, 0, 0, 0, 0, swi_clock, swi_ast
+imasks: /* masks for interrupt handlers */
+ .space NHWI*4 /* padding; HWI masks are elsewhere */
+ .long SWI_TTY_MASK, SWI_NET_MASK, 0, 0, 0, 0, 0, 0
+ .long 0, 0, 0, 0, 0, 0, SWI_CLOCK_MASK, SWI_AST_MASK
+
+ .globl _intrcnt
+_intrcnt: /* used by vmstat to calc size of table */
+ .globl _intrcnt_bad7
+_intrcnt_bad7: .space 4 /* glitches on irq 7 */
+ .globl _intrcnt_bad15
+_intrcnt_bad15: .space 4 /* glitches on irq 15 */
+ .globl _intrcnt_stray
+_intrcnt_stray: .space 4 /* total count of stray interrupts */
+ .globl _intrcnt_actv
+_intrcnt_actv: .space NR_REAL_INT_HANDLERS * 4 /* active interrupts */
+ .globl _eintrcnt
+_eintrcnt: /* used by vmstat to calc size of table */
+
+/*
+ * Build the interrupt name table for vmstat
+ */
+
+#undef BUILD_FAST_VECTOR
+#define BUILD_FAST_VECTOR BUILD_VECTOR
+
+#undef BUILD_VECTOR
+#define BUILD_VECTOR(name, unit, irq_num, id_num, mask, handler, \
+ icu_num, icu_enables, reg) \
+ .ascii "name irq" ; \
+ .asciz "irq_num"
+/*
+ * XXX - use the __STRING and __CONCAT macros from <sys/cdefs.h> to stringize
+ * and concatenate names above and elsewhere. Note that __CONCAT doesn't
+ * work when nested.
+ */
+
+ .text
+ .globl _intrnames, _eintrnames
+_intrnames:
+ BUILD_VECTOR(bad,,7,,,,,,)
+ BUILD_VECTOR(bad,,15,,,,,,)
+ BUILD_VECTOR(stray,,,,,,,,)
+ BUILD_VECTORS
+
+_eintrnames:
diff --git a/sys/i386/isa/wd.c b/sys/i386/isa/wd.c
new file mode 100644
index 0000000..2794ecd
--- /dev/null
+++ b/sys/i386/isa/wd.c
@@ -0,0 +1,1827 @@
+#define WD_COUNT_RETRIES
+static int wdtest = 0;
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)wd.c 7.2 (Berkeley) 5/9/91
+ * $Id: wd.c,v 1.37 1994/04/10 11:17:13 csgr Exp $
+ */
+
+/* TODO:
+ * o Bump error count after timeout.
+ * o Satisfy ATA timing in all cases.
+ * o Finish merging berry/sos timeout code (bump error count...).
+ * o Merge/fix TIH/NetBSD bad144 code.
+ * o Merge/fix Dyson/NetBSD clustering code.
+ * o Don't use polling except for initialization. Need to
+ * reorganize the state machine. Then "extra" interrupts
+ * shouldn't happen (except maybe one for initialization).
+ * o Fix disklabel, boot and driver inconsistencies with
+ * bad144 in standard versions.
+ * o Support extended DOS partitions.
+ * o Support swapping to DOS partitions.
+ * o Look at latest linux clustering methods. Our disksort()
+ * gets in the way of clustering.
+ * o Handle bad sectors, clustering, disklabelling, DOS
+ * partitions and swapping driver-independently. Use
+ * i386/dkbad.c for bad sectors. Swapping will need new
+ * driver entries for polled reinit and polled write).
+ */
+
+#include "wd.h"
+#if NWDC > 0
+
+#include "param.h"
+#include "dkbad.h"
+#include "systm.h"
+#include "kernel.h"
+#include "conf.h"
+#include "file.h"
+#include "stat.h"
+#include "ioctl.h"
+#include "disklabel.h"
+#include "buf.h"
+#include "uio.h"
+#include "malloc.h"
+#include "machine/cpu.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/wdreg.h"
+#include "syslog.h"
+#include "vm/vm.h"
+
+#define TIMEOUT 10000
+#define RETRIES 5 /* number of retries before giving up */
+#define RECOVERYTIME 500000 /* usec for controller to recover after err */
+#define MAXTRANSFER 256 /* max size of transfer in sectors */
+#define BAD144_NO_CYL 0xffff /* XXX should be in dkbad.h; bad144.c uses -1 */
+
+#ifdef notyet
+#define wdnoreloc(dev) (minor(dev) & 0x80) /* ignore partition table */
+#endif
+#define wddospart(dev) (minor(dev) & 0x40) /* use dos partitions */
+#define wdunit(dev) ((minor(dev) & 0x38) >> 3)
+#define wdpart(dev) (minor(dev) & 0x7)
+#define makewddev(maj, unit, part) (makedev(maj,((unit<<3)+part)))
+#define WDRAW 3 /* 'd' partition isn't a partition! */
+
+/* Cylinder number for doing IO to. Shares an entry in the buf struct. */
+#define b_cylin b_resid
+
+/*
+ * This biotab field doubles as a field for the physical unit number on
+ * the controller.
+ */
+#define id_physid id_scsiid
+
+/*
+ * Drive states. Used to initialize drive.
+ */
+
+#define CLOSED 0 /* disk is closed. */
+#define WANTOPEN 1 /* open requested, not started */
+#define RECAL 2 /* doing restore */
+#define OPEN 3 /* done with open */
+
+/*
+ * The structure of a disk drive.
+ */
+struct disk {
+ long dk_bc; /* byte count left */
+ short dk_skip; /* blocks already transferred */
+ char dk_ctrlr; /* physical controller number */
+ char dk_unit; /* physical unit number */
+ char dk_lunit; /* logical unit number */
+ char dk_state; /* control state */
+ u_char dk_status; /* copy of status reg. */
+ u_char dk_error; /* copy of error reg. */
+ u_char dk_timeout; /* countdown to next timeout */
+ short dk_port; /* i/o port base */
+
+ u_long dk_copenpart; /* character units open on this drive */
+ u_long dk_bopenpart; /* block units open on this drive */
+ u_long dk_openpart; /* all units open on this drive */
+ short dk_wlabel; /* label writable? */
+ short dk_flags; /* drive characteistics found */
+#define DKFL_DOSPART 0x00001 /* has DOS partition table */
+#define DKFL_SINGLE 0x00004 /* sector at a time mode */
+#define DKFL_ERROR 0x00008 /* processing a disk error */
+#define DKFL_BSDLABEL 0x00010 /* has a BSD disk label */
+#define DKFL_BADSECT 0x00020 /* has a bad144 badsector table */
+#define DKFL_WRITEPROT 0x00040 /* manual unit write protect */
+#define DKFL_LABELLING 0x00080 /* readdisklabel() in progress */
+ struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */
+ struct disklabel dk_dd; /* device configuration data */
+ struct disklabel dk_dd2; /* DOS view converted to label */
+ struct dos_partition
+ dk_dospartitions[NDOSPART]; /* DOS view of disk */
+ struct dkbad dk_bad; /* bad sector table */
+ long dk_badsect[127]; /* 126 plus trailing -1 marker */
+};
+
+static struct disk *wddrives[NWD]; /* table of units */
+static struct buf wdtab[NWDC];
+static struct buf wdutab[NWD]; /* head of queue per drive */
+#ifdef notyet
+static struct buf rwdbuf[NWD]; /* buffers for raw IO */
+#endif
+static long wdxfer[NWD]; /* count of transfers */
+
+
+static void bad144intern(struct disk *);
+static int wdprobe(struct isa_device *dvp);
+static int wdattach(struct isa_device *dvp);
+static void wdustart(struct disk *du);
+static void wdstart(int ctrlr);
+static int wdcontrol(struct buf *bp);
+static int wdcommand(struct disk *du, u_int cylinder, u_int head,
+ u_int sector, u_int count, u_int command);
+static int wdsetctlr(struct disk *du);
+static int wdwsetctlr(struct disk *du);
+static int wdgetctlr(struct disk *du);
+static void wderror(struct buf *bp, struct disk *du, char *mesg);
+static void wdflushirq(struct disk *du, int old_ipl);
+static int wdreset(struct disk *du);
+static void wdsleep(int ctrlr, char *wmesg);
+static void wdtimeout(caddr_t cdu);
+static int wdunwedge(struct disk *du);
+static int wdwait(struct disk *du, u_char bits_wanted, int timeout);
+
+struct isa_driver wdcdriver = {
+ wdprobe, wdattach, "wdc",
+};
+
+extern char *readdisklabel();
+
+/*
+ * Probe for controller.
+ */
+static int
+wdprobe(struct isa_device *dvp)
+{
+ int unit = dvp->id_unit;
+ struct disk *du;
+
+ if (unit >= NWDC)
+ return (0);
+ du = malloc(sizeof *du, M_TEMP, M_NOWAIT);
+ if (du == NULL)
+ return (0);
+ bzero(du, sizeof *du);
+ du->dk_ctrlr = dvp->id_unit;
+ du->dk_port = dvp->id_iobase;
+
+ /* check if we have registers that work */
+ outb(du->dk_port + wd_cyl_lo, 0xa5); /* wd_cyl_lo is read/write */
+ if (inb(du->dk_port + wd_cyl_lo) != 0xa5)
+ goto nodevice;
+
+ if (wdreset(du) != 0 && (DELAY(RECOVERYTIME), wdreset(du)) != 0)
+ goto nodevice;
+
+ /* execute a controller only command */
+ if (wdcommand(du, 0, 0, 0, 0, WDCC_DIAGNOSE) != 0
+ || wdwait(du, 0, TIMEOUT) < 0)
+ goto nodevice;
+
+ /*
+ * drive(s) did not time out during diagnostic :
+ * Get error status and check that both drives are OK.
+ * Table 9-2 of ATA specs suggests that we must check for
+ * a value of 0x01
+ *
+ * Strangely, some controllers will return a status of
+ * 0x81 (drive 0 OK, drive 1 failure), and then when
+ * the DRV bit is set, return status of 0x01 (OK) for
+ * drive 2. (This seems to contradict the ATA spec.)
+ */
+ du->dk_error = inb(du->dk_port + wd_error);
+ /* printf("Error : %x\n", du->dk_error); */
+ if(du->dk_error != 0x01) {
+ if(du->dk_error & 0x80) { /* drive 1 failure */
+
+ /* first set the DRV bit */
+ u_int sdh;
+ sdh = inb(du->dk_port+ wd_sdh);
+ sdh = sdh | 0x10;
+ outb(du->dk_port+ wd_sdh, sdh);
+
+ /* Wait, to make sure drv 1 has completed diags */
+ if ( wdwait(du, 0, TIMEOUT) < 0)
+ goto nodevice;
+
+ /* Get status for drive 1 */
+ du->dk_error = inb(du->dk_port + wd_error);
+ /* printf("Error (drv 1) : %x\n", du->dk_error); */
+
+ if(du->dk_error != 0x01)
+ goto nodevice;
+ } else /* drive 0 fail */
+ goto nodevice;
+ }
+
+
+ free(du, M_TEMP);
+ return (IO_WDCSIZE);
+
+nodevice:
+ free(du, M_TEMP);
+ return (0);
+}
+
+/*
+ * Attach each drive if possible.
+ */
+static int
+wdattach(struct isa_device *dvp)
+{
+ int unit, lunit;
+ struct isa_device *wdup;
+ struct disk *du;
+
+ if (dvp->id_unit >= NWDC)
+ return (0);
+
+ for (wdup = isa_biotab_wdc; wdup->id_driver != 0; wdup++) {
+ if (wdup->id_iobase != dvp->id_iobase)
+ continue;
+ lunit = wdup->id_unit;
+ if (lunit >= NWD)
+ continue;
+ unit = wdup->id_physid;
+
+ du = malloc(sizeof *du, M_TEMP, M_NOWAIT);
+ if (du == NULL)
+ continue;
+ if (wddrives[lunit] != NULL)
+ panic("drive attached twice");
+ wddrives[lunit] = du;
+ bzero(du, sizeof *du);
+ du->dk_ctrlr = dvp->id_unit;
+ du->dk_unit = unit;
+ du->dk_lunit = lunit;
+ du->dk_port = dvp->id_iobase;
+
+ /*
+ * Print out description of drive.
+ * wdp_model can be [0..40] bytes, thus \0 can be missing so
+ * so copy it and add a null before printing.
+ */
+ if (wdgetctlr(du) == 0) {
+ char buf[sizeof(du->dk_params.wdp_model) + 1];
+ bcopy(du->dk_params.wdp_model, buf, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+ printf("wdc%d: unit %d (wd%d): <%s>\n",
+ dvp->id_unit, unit, lunit, buf);
+ if (du->dk_params.wdp_heads == 0)
+ printf("wd%d: size unknown\n", lunit);
+ else
+ printf("wd%d: %luMB (%lu total sec), ",
+ lunit,
+ du->dk_dd.d_secperunit
+ * du->dk_dd.d_secsize / (1024 * 1024),
+ du->dk_dd.d_secperunit);
+ printf("%lu cyl, %lu head, %lu sec, bytes/sec %lu\n",
+ du->dk_dd.d_ncylinders,
+ du->dk_dd.d_ntracks,
+ du->dk_dd.d_nsectors,
+ du->dk_dd.d_secsize);
+ /*
+ * Start timeout routine for this drive.
+ * XXX timeout should be per controller.
+ */
+ wdtimeout((caddr_t)du);
+ } else {
+ free(du, M_TEMP);
+ wddrives[lunit] = NULL;
+ }
+ }
+
+ /*
+ * Discard any interrupts generated by wdgetctlr(). wdflushirq()
+ * doesn't work now because the ambient ipl is too high.
+ */
+ wdtab[dvp->id_unit].b_active = 2;
+
+ return (1);
+}
+
+/* Read/write routine for a buffer. Finds the proper unit, range checks
+ * arguments, and schedules the transfer. Does not wait for the transfer
+ * to complete. Multi-page transfers are supported. All I/O requests must
+ * be a multiple of a sector in length.
+ */
+void
+wdstrategy(register struct buf *bp)
+{
+ register struct buf *dp;
+ struct disk *du;
+ int lunit = wdunit(bp->b_dev);
+ int s;
+
+ /* valid unit, controller, and request? */
+ if (lunit >= NWD || bp->b_blkno < 0 || (du = wddrives[lunit]) == NULL) {
+
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ goto done;
+ }
+
+ /* "soft" write protect check */
+ if ((du->dk_flags & DKFL_WRITEPROT) && (bp->b_flags & B_READ) == 0) {
+ bp->b_error = EROFS;
+ bp->b_flags |= B_ERROR;
+ goto done;
+ }
+
+ /*
+ * Do bounds checking, adjust transfer, and set b_cylin.
+ */
+ if (bounds_check_with_label(bp, wddospart(bp->b_dev)
+ ? &du->dk_dd2 : &du->dk_dd,
+ du->dk_wlabel) <= 0)
+ goto done;
+
+ /*
+ * Check for *any* block on this transfer being on the bad block list
+ * if it is, then flag the block as a transfer that requires
+ * bad block handling. Also, used as a hint for low level disksort
+ * clustering code to keep from coalescing a bad transfer into
+ * a normal transfer. Single block transfers for a large number of
+ * blocks associated with a cluster I/O are undersirable.
+ */
+ if( du->dk_flags & DKFL_BADSECT) {
+ int i;
+ int nsecs = howmany(bp->b_bcount, DEV_BSIZE);
+ int blkend = bp->b_pblkno + nsecs;
+ for(i=0;du->dk_badsect[i] != -1 && du->dk_badsect[i] < blkend;i++) {
+ if( du->dk_badsect[i] >= bp->b_pblkno) {
+ bp->b_flags |= B_BAD;
+ break;
+ }
+ }
+ }
+
+ /* queue transfer on drive, activate drive and controller if idle */
+ dp = &wdutab[lunit];
+ s = splbio();
+
+ /* cldisksort(dp, bp, 254*DEV_BSIZE); */
+ disksort(dp, bp);
+
+ if (dp->b_active == 0)
+ wdustart(du); /* start drive */
+
+ /* Pick up changes made by readdisklabel(). */
+ if (du->dk_flags & DKFL_LABELLING && du->dk_state > RECAL) {
+ wdsleep(du->dk_ctrlr, "wdlab");
+ du->dk_state = WANTOPEN;
+ }
+
+ if (wdtab[du->dk_ctrlr].b_active == 0)
+ wdstart(du->dk_ctrlr); /* start controller */
+ splx(s);
+ return;
+
+done:
+ s = splbio();
+ /* toss transfer, we're done early */
+ biodone(bp);
+ splx(s);
+}
+
+/*
+ * Routine to queue a command to the controller. The unit's
+ * request is linked into the active list for the controller.
+ * If the controller is idle, the transfer is started.
+ */
+static void
+wdustart(register struct disk *du)
+{
+ register struct buf *bp, *dp = &wdutab[du->dk_lunit];
+ int ctrlr = du->dk_ctrlr;
+
+ /* unit already active? */
+ if (dp->b_active)
+ return;
+
+ /* anything to start? */
+ bp = dp->b_actf;
+ if (bp == NULL)
+ return;
+
+ dp->b_actf = bp->b_actf;
+ bp->b_actf = NULL;
+ /* link onto controller queue */
+ if (wdtab[ctrlr].b_actf == NULL) {
+ wdtab[ctrlr].b_actf = bp;
+ } else {
+ *wdtab[ctrlr].b_actb = bp;
+ }
+ wdtab[ctrlr].b_actb = &bp->b_actf;
+
+ /* mark the drive unit as busy */
+ dp->b_active = 1;
+}
+
+/*
+ * Controller startup routine. This does the calculation, and starts
+ * a single-sector read or write operation. Called to start a transfer,
+ * or from the interrupt routine to continue a multi-sector transfer.
+ * RESTRICTIONS:
+ * 1. The transfer length must be an exact multiple of the sector size.
+ */
+
+static void
+wdstart(int ctrlr)
+{
+ register struct disk *du;
+ register struct buf *bp;
+ struct disklabel *lp;
+ struct buf *dp;
+ register struct bt_bad *bt_ptr;
+ long blknum, cylin, head, sector;
+ long secpertrk, secpercyl;
+ int lunit;
+
+loop:
+ /* is there a drive for the controller to do a transfer with? */
+ bp = wdtab[ctrlr].b_actf;
+ if (bp == NULL)
+ return;
+
+ /* obtain controller and drive information */
+ lunit = wdunit(bp->b_dev);
+ du = wddrives[lunit];
+
+ /* if not really a transfer, do control operations specially */
+ if (du->dk_state < OPEN) {
+ if (du->dk_state != WANTOPEN)
+ printf("wd%d: wdstart: weird dk_state %d\n",
+ du->dk_lunit, du->dk_state);
+ if (wdcontrol(bp) != 0)
+ printf("wd%d: wdstart: wdcontrol returned nonzero, state = %d\n",
+ du->dk_lunit, du->dk_state);
+ return;
+ }
+
+ /* calculate transfer details */
+ blknum = bp->b_pblkno + du->dk_skip;
+#ifdef WDDEBUG
+ if (du->dk_skip == 0)
+ printf("wd%d: wdstart: %s %d@%d; map ", lunit,
+ (bp->b_flags & B_READ) ? "read" : "write",
+ bp->b_bcount, blknum);
+ else
+ printf(" %d)%x", du->dk_skip, inb(du->dk_port + wd_altsts));
+#endif
+
+ lp = &du->dk_dd;
+ secpertrk = lp->d_nsectors;
+ secpercyl = lp->d_secpercyl;
+
+ if (du->dk_skip == 0) {
+ du->dk_bc = bp->b_bcount;
+ if (bp->b_flags & B_BAD) {
+ du->dk_flags |= DKFL_SINGLE;
+ }
+ }
+
+ if ((du->dk_flags & (DKFL_SINGLE|DKFL_BADSECT)) /* 19 Aug 92*/
+ == (DKFL_SINGLE|DKFL_BADSECT)) {
+ int i;
+
+ for(i=0;
+ du->dk_badsect[i] != -1 && du->dk_badsect[i] <= blknum;
+ i++) {
+
+ if( du->dk_badsect[i] == blknum) {
+ /*
+ * XXX the offset of the bad sector table ought
+ * to be stored in the in-core copy of the table.
+ */
+#define BAD144_PART 2 /* XXX scattered magic numbers */
+#define BSD_PART 0 /* XXX should be 2 but bad144.c uses 0 */
+ if (lp->d_partitions[BSD_PART].p_offset != 0)
+ blknum = lp->d_partitions[BAD144_PART].p_offset
+ + lp->d_partitions[BAD144_PART].p_size;
+ else
+ blknum = lp->d_secperunit;
+ blknum -= lp->d_nsectors + i + 1;
+
+ break;
+ }
+ }
+ }
+
+
+ cylin = blknum / secpercyl;
+ head = (blknum % secpercyl) / secpertrk;
+ sector = blknum % secpertrk;
+
+ wdtab[ctrlr].b_active = 1; /* mark controller active */
+
+ /* if starting a multisector transfer, or doing single transfers */
+ if (du->dk_skip == 0 || (du->dk_flags & DKFL_SINGLE)) {
+ u_int command;
+ u_int count;
+
+ if (wdtab[ctrlr].b_errcnt && (bp->b_flags & B_READ) == 0)
+ du->dk_bc += DEV_BSIZE;
+
+#ifdef B_FORMAT
+ if (bp->b_flags & B_FORMAT) {
+ command = WDCC_FORMAT;
+ count = lp->d_nsectors;
+ sector = lp->d_gap3 - 1; /* + 1 later */
+ } else
+#endif
+ {
+ if (du->dk_flags & DKFL_SINGLE)
+ count = 1;
+ else
+ count = howmany(du->dk_bc, DEV_BSIZE);
+ command = (bp->b_flags & B_READ)
+ ? WDCC_READ : WDCC_WRITE;
+ }
+
+ /*
+ * XXX this loop may never terminate. The code to handle
+ * counting down of retries and eventually failing the i/o
+ * is in wdintr() and we can't get there from here.
+ */
+ if (wdtest != 0) {
+ if (--wdtest == 0) {
+ wdtest = 100;
+ printf("dummy wdunwedge\n");
+ wdunwedge(du);
+ }
+ }
+ while (wdcommand(du, cylin, head, sector, count, command)
+ != 0) {
+ wderror(bp, du,
+ "wdstart: timeout waiting to give command");
+ wdunwedge(du);
+ }
+#ifdef WDDEBUG
+ printf("cylin %ld head %ld sector %ld addr %x sts %x\n",
+ cylin, head, sector,
+ (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE,
+ inb(du->dk_port + wd_altsts));
+#endif
+ }
+
+ /*
+ * Schedule wdtimeout() to wake up after a few seconds. Retrying
+ * unmarked bad blocks can take 3 seconds! Then it is not good that
+ * we retry 5 times.
+ *
+ * XXX wdtimeout() doesn't increment the error count so we may loop
+ * forever. More seriously, the loop isn't forever but causes a
+ * crash.
+ *
+ * TODO fix b_resid bug elsewhere (fd.c....). Fix short but positive
+ * counts being discarded after there is an error (in physio I
+ * think). Discarding them would be OK if the (special) file offset
+ * was not advanced.
+ */
+ du->dk_timeout = 1 + 3;
+
+ /* If this is a read operation, just go away until it's done. */
+ if (bp->b_flags & B_READ)
+ return;
+
+ /* Ready to send data? */
+ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) < 0) {
+ wderror(bp, du, "wdstart: timeout waiting for DRQ");
+ /*
+ * XXX what do we do now? If we've just issued the command,
+ * then we can treat this failure the same as a command
+ * failure. But if we are continuing a multi-sector write,
+ * the command was issued ages ago, so we can't simply
+ * restart it.
+ *
+ * XXX we waste a lot of time unnecessarily translating block
+ * numbers to cylin/head/sector for continued i/o's.
+ */
+ }
+
+ /* then send it! */
+ outsw(du->dk_port + wd_data,
+ (void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE),
+ DEV_BSIZE / sizeof(short));
+ du->dk_bc -= DEV_BSIZE;
+}
+
+/* Interrupt routine for the controller. Acknowledge the interrupt, check for
+ * errors on the current operation, mark it done if necessary, and start
+ * the next request. Also check for a partially done transfer, and
+ * continue with the next chunk if so.
+ */
+void
+wdintr(int unit)
+{
+ register struct disk *du;
+ register struct buf *bp, *dp;
+
+ if (wdtab[unit].b_active == 2)
+ return; /* intr in wdflushirq() */
+ if (!wdtab[unit].b_active) {
+#ifndef LAPTOP
+ printf("wdc%d: extra interrupt\n", unit);
+#endif
+ return;
+ }
+
+ bp = wdtab[unit].b_actf;
+ du = wddrives[wdunit(bp->b_dev)];
+ dp = &wdutab[du->dk_lunit];
+
+ du->dk_timeout = 0;
+
+ if (wdwait(du, 0, TIMEOUT) < 0) {
+ wderror(bp, du, "wdintr: timeout waiting for status");
+ du->dk_status |= WDCS_ERR; /* XXX */
+ }
+
+ /* is it not a transfer, but a control operation? */
+ if (du->dk_state < OPEN) {
+ wdtab[unit].b_active = 0;
+ switch (wdcontrol(bp)) {
+ case 0:
+ return;
+ case 1:
+ wdstart(unit);
+ return;
+ case 2:
+ goto done;
+ }
+ }
+
+ /* have we an error? */
+ if (du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) {
+oops:
+#ifdef WDDEBUG
+ wderror(bp, du, "wdintr");
+#endif
+ if ((du->dk_flags & DKFL_SINGLE) == 0) {
+ du->dk_flags |= DKFL_ERROR;
+ goto outt;
+ }
+#ifdef B_FORMAT
+ if (bp->b_flags & B_FORMAT) {
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ goto done;
+ }
+#endif
+
+ /* error or error correction? */
+ if (du->dk_status & WDCS_ERR) {
+ if (++wdtab[unit].b_errcnt < RETRIES) {
+ wdtab[unit].b_active = 0;
+ } else {
+ wderror(bp, du, "hard error");
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR; /* flag the error */
+ }
+ } else
+ wderror(bp, du, "soft ecc");
+ }
+
+ /*
+ * If this was a successful read operation, fetch the data.
+ */
+ if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ)
+ && wdtab[unit].b_active) {
+ int chk, dummy;
+
+ chk = min(DEV_BSIZE / sizeof(short), du->dk_bc / sizeof(short));
+
+ /* ready to receive data? */
+ if ((du->dk_status & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ))
+ != (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ))
+ wderror(bp, du, "wdintr: read intr arrived early");
+ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) {
+ wderror(bp, du, "wdintr: read error detected late");
+ goto oops;
+ }
+
+ /* suck in data */
+ insw(du->dk_port + wd_data,
+ (void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE),
+ chk);
+ du->dk_bc -= chk * sizeof(short);
+
+ /* XXX for obsolete fractional sector reads. */
+ while (chk++ < DEV_BSIZE / sizeof(short))
+ insw(du->dk_port + wd_data, &dummy, 1);
+ }
+
+ wdxfer[du->dk_lunit]++;
+outt:
+ if (wdtab[unit].b_active) {
+ if ((bp->b_flags & B_ERROR) == 0) {
+ du->dk_skip++; /* add to successful sectors */
+ if (wdtab[unit].b_errcnt)
+ wderror(bp, du, "soft error");
+ wdtab[unit].b_errcnt = 0;
+
+ /* see if more to transfer */
+ if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) {
+ wdtab[unit].b_active = 0;
+ wdstart(unit);
+ return; /* next chunk is started */
+ } else if ((du->dk_flags & (DKFL_SINGLE | DKFL_ERROR))
+ == DKFL_ERROR) {
+ du->dk_skip = 0;
+ du->dk_flags &= ~DKFL_ERROR;
+ du->dk_flags |= DKFL_SINGLE;
+ wdtab[unit].b_active = 0;
+ wdstart(unit);
+ return; /* redo xfer sector by sector */
+ }
+ }
+
+done: ;
+ /* done with this transfer, with or without error */
+ du->dk_flags &= ~DKFL_SINGLE;
+ wdtab[unit].b_actf = bp->b_actf;
+ wdtab[unit].b_errcnt = 0;
+ bp->b_resid = bp->b_bcount - du->dk_skip * DEV_BSIZE;
+ dp->b_active = 0;
+ dp->b_errcnt = 0;
+ du->dk_skip = 0;
+ biodone(bp);
+ }
+
+ /* controller idle */
+ wdtab[unit].b_active = 0;
+
+ /* anything more on drive queue? */
+ wdustart(du);
+ /* anything more for controller to do? */
+ if (wdtab[unit].b_actf)
+ wdstart(unit);
+}
+
+/*
+ * Initialize a drive.
+ */
+int
+wdopen(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ register unsigned int lunit;
+ register struct disk *du;
+ int part = wdpart(dev), mask = 1 << part;
+ struct partition *pp;
+ char *msg;
+ struct disklabel save_label;
+
+ lunit = wdunit(dev);
+ if (lunit >= NWD)
+ return (ENXIO);
+ du = wddrives[lunit];
+ if (du == NULL)
+ return (ENXIO);
+
+ /* Finish flushing IRQs left over from wdattach(). */
+ if (wdtab[du->dk_ctrlr].b_active == 2)
+ wdtab[du->dk_ctrlr].b_active = 0;
+
+ /*
+ * That's all for valid DOS partitions. We don't need a BSD label.
+ * The openmask is only used for checking BSD partitions so we don't
+ * need to maintain it.
+ */
+ if (wddospart(dev)) {
+ /* XXX we do need a disklabel for now. */
+ if ((du->dk_flags & DKFL_BSDLABEL) == 0)
+ return (ENXIO);
+
+ return (part > NDOSPART ? ENXIO : 0);
+ }
+
+ while (du->dk_flags & DKFL_LABELLING)
+ tsleep((caddr_t)&du->dk_flags, PZERO - 1, "wdopen", 1);
+ if ((du->dk_flags & DKFL_BSDLABEL) == 0) {
+ /*
+ * wdtab[ctrlr].b_active != 0 implies
+ * wdutab[lunit].b_actf == NULL (?)
+ * so the following guards most things (until the next i/o).
+ * It doesn't guard against a new i/o starting and being
+ * affected by the label being changed. Sigh.
+ */
+ wdsleep(du->dk_ctrlr, "wdopn1");
+
+ du->dk_flags |= DKFL_LABELLING | DKFL_WRITEPROT;
+ du->dk_state = WANTOPEN;
+ wdutab[lunit].b_actf = NULL;
+
+ /*
+ * Read label using WDRAW partition.
+ *
+ * If the drive has an MBR, then the current geometry (from
+ * wdgetctlr()) is used to read it; then the BIOS/DOS
+ * geometry is inferred and used to read the label off the
+ * 'c' partition. Otherwise the label is read using the
+ * current geometry. The label gives the final geometry.
+ * If bad sector handling is enabled, then this geometry
+ * is used to read the bad sector table. The geometry
+ * changes occur inside readdisklabel() and are propagated
+ * to the driver by resetting the state machine.
+ */
+ save_label = du->dk_dd;
+ du->dk_dd.d_partitions[WDRAW].p_offset = 0;
+ du->dk_dd.d_partitions[WDRAW].p_size = 0x7fffffff;/* XXX */
+#define WDSTRATEGY ((int (*)(struct buf *)) wdstrategy) /* XXX */
+ msg = readdisklabel(makewddev(major(dev), lunit, WDRAW),
+ WDSTRATEGY, &du->dk_dd,
+ du->dk_dospartitions, &du->dk_bad);
+/*
+ msg = readdisklabel(makewddev(major(dev), lunit, WDRAW),
+ WDSTRATEGY, &du->dk_dd);
+*/
+ du->dk_flags &= ~DKFL_LABELLING;
+ if (msg != NULL) {
+ du->dk_dd = save_label;
+ log(LOG_WARNING, "wd%d: cannot find label (%s)\n",
+ lunit, msg);
+ if (part != WDRAW)
+ return (EINVAL); /* XXX needs translation */
+ } else {
+ int dospart;
+ unsigned long newsize, offset, size;
+
+ du->dk_flags |= DKFL_BSDLABEL;
+ du->dk_flags &= ~DKFL_WRITEPROT;
+ if (du->dk_dd.d_flags & D_BADSECT) {
+ du->dk_flags |= DKFL_BADSECT;
+ bad144intern(du);
+ }
+
+ /*
+ * Force WDRAW partition to be the whole disk.
+ */
+ offset = du->dk_dd.d_partitions[WDRAW].p_offset;
+ if (offset != 0) {
+ printf(
+ "wd%d: changing offset of 'd' partition from %lu to 0\n",
+ du->dk_lunit, offset);
+ du->dk_dd.d_partitions[WDRAW].p_offset = 0;
+ }
+ size = du->dk_dd.d_partitions[WDRAW].p_size;
+ newsize = du->dk_dd.d_secperunit; /* XXX */
+ if (size != newsize) {
+ printf(
+ "wd%d: changing size of 'd' partition from %lu to %lu\n",
+ du->dk_lunit, size, newsize);
+ du->dk_dd.d_partitions[WDRAW].p_size = newsize;
+ }
+
+ /*
+ * Convert DOS partition data to a label.
+ */
+ du->dk_dd2 = du->dk_dd;
+ bzero(du->dk_dd2.d_partitions,
+ sizeof du->dk_dd2.d_partitions);
+ du->dk_dd2.d_partitions[0].p_size
+ = du->dk_dd.d_secperunit; /* XXX */
+ for (dospart = 1; dospart <= NDOSPART; dospart++) {
+ du->dk_dd2.d_partitions[dospart].p_offset =
+ du->dk_dospartitions[dospart - 1].dp_start;
+ du->dk_dd2.d_partitions[dospart].p_size =
+ du->dk_dospartitions[dospart - 1].dp_size;
+ }
+ }
+
+ /* Pick up changes made by readdisklabel(). */
+ wdsleep(du->dk_ctrlr, "wdopn2");
+ du->dk_state = WANTOPEN;
+ }
+
+ /*
+ * Warn if a partion is opened that overlaps another partition which
+ * is open unless one is the "raw" partition (whole disk).
+ */
+ if ((du->dk_openpart & mask) == 0 && part != WDRAW) {
+ int start, end;
+
+ pp = &du->dk_dd.d_partitions[part];
+ start = pp->p_offset;
+ end = pp->p_offset + pp->p_size;
+ for (pp = du->dk_dd.d_partitions;
+ pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions];
+ pp++) {
+ if (pp->p_offset + pp->p_size <= start ||
+ pp->p_offset >= end)
+ continue;
+ if (pp - du->dk_dd.d_partitions == WDRAW)
+ continue;
+ if (du->dk_openpart
+ & (1 << (pp - du->dk_dd.d_partitions)))
+ log(LOG_WARNING,
+ "wd%d%c: overlaps open partition (%c)\n",
+ lunit, part + 'a',
+ pp - du->dk_dd.d_partitions + 'a');
+ }
+ }
+ if (part >= du->dk_dd.d_npartitions && part != WDRAW)
+ return (ENXIO);
+
+ switch (fmt) {
+ case S_IFCHR:
+ du->dk_copenpart |= mask;
+ break;
+ case S_IFBLK:
+ du->dk_bopenpart |= mask;
+ break;
+ }
+ du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
+
+ return (0);
+}
+
+/*
+ * Implement operations other than read/write.
+ * Called from wdstart or wdintr during opens and formats.
+ * Uses finite-state-machine to track progress of operation in progress.
+ * Returns 0 if operation still in progress, 1 if completed, 2 if error.
+ */
+static int
+wdcontrol(register struct buf *bp)
+{
+ register struct disk *du;
+ int ctrlr;
+
+ du = wddrives[wdunit(bp->b_dev)];
+ ctrlr = du->dk_ctrlr;
+
+ switch (du->dk_state) {
+ case WANTOPEN:
+tryagainrecal:
+ wdtab[ctrlr].b_active = 1;
+ if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0) {
+ wderror(bp, du, "wdcontrol: wdcommand failed");
+ goto maybe_retry;
+ }
+ du->dk_state = RECAL;
+ return (0);
+ case RECAL:
+ if (du->dk_status & WDCS_ERR || wdsetctlr(du) != 0) {
+ wderror(bp, du, "wdcontrol: recal failed");
+maybe_retry:
+ if (du->dk_status & WDCS_ERR)
+ wdunwedge(du);
+ du->dk_state = WANTOPEN;
+ if (++wdtab[ctrlr].b_errcnt < RETRIES)
+ goto tryagainrecal;
+ bp->b_error = ENXIO; /* XXX needs translation */
+ bp->b_flags |= B_ERROR;
+ return (2);
+ }
+ wdtab[ctrlr].b_errcnt = 0;
+ du->dk_state = OPEN;
+ /*
+ * The rest of the initialization can be done by normal
+ * means.
+ */
+ return (1);
+ }
+ panic("wdcontrol");
+ return (2);
+}
+
+/*
+ * Wait uninterruptibly until controller is not busy, then send it a command.
+ * The wait usually terminates immediately because we waited for the previous
+ * command to terminate.
+ */
+static int
+wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector,
+ u_int count, u_int command)
+{
+ u_int wdc;
+
+ if (wdwait(du, 0, TIMEOUT) < 0)
+ return (1);
+ wdc = du->dk_port;
+ outb(wdc + wd_precomp, du->dk_dd.d_precompcyl / 4);
+ outb(wdc + wd_cyl_lo, cylinder);
+ outb(wdc + wd_cyl_hi, cylinder >> 8);
+ outb(wdc + wd_sdh, WDSD_IBM | (du->dk_unit << 4) | head);
+ outb(wdc + wd_sector, sector + 1);
+ outb(wdc + wd_seccnt, count);
+ if (wdwait(du, command == WDCC_DIAGNOSE || command == WDCC_IDC
+ ? 0 : WDCS_READY, TIMEOUT) < 0)
+ return (1);
+ outb(wdc + wd_command, command);
+ return (0);
+}
+
+/*
+ * issue IDC to drive to tell it just what geometry it is to be.
+ */
+static int
+wdsetctlr(struct disk *du)
+{
+ int error = 0;
+#ifdef WDDEBUG
+ printf("wd(%d,%d): wdsetctlr: C %lu H %lu S %lu\n",
+ du->dk_ctrlr, du->dk_unit,
+ du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks,
+ du->dk_dd.d_nsectors);
+#endif
+ if (du->dk_dd.d_ntracks == 0 || du->dk_dd.d_ntracks > 16) {
+ struct wdparams *wp;
+
+ printf("wd%d: can't handle %lu heads from partition table ",
+ du->dk_lunit, du->dk_dd.d_ntracks);
+ /* obtain parameters */
+ wp = &du->dk_params;
+ if (wp->wdp_heads > 0 && wp->wdp_heads <= 16) {
+ printf("(controller value %lu restored)\n",
+ wp->wdp_heads);
+ du->dk_dd.d_ntracks = wp->wdp_heads;
+ }
+ else {
+ printf("(truncating to 16)\n");
+ du->dk_dd.d_ntracks = 16;
+ }
+ }
+
+ if (du->dk_dd.d_nsectors == 0 || du->dk_dd.d_nsectors > 255) {
+ printf("wd%d: cannot handle %lu sectors (max 255)\n",
+ du->dk_lunit, du->dk_dd.d_nsectors);
+ error = 1;
+ }
+ if (error) {
+ wdtab[du->dk_ctrlr].b_errcnt += RETRIES;
+ return (1);
+ }
+ if (wdcommand(du, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks - 1, 0,
+ du->dk_dd.d_nsectors, WDCC_IDC) != 0
+ || wdwait(du, WDCS_READY, TIMEOUT) < 0) {
+ wderror((struct buf *)NULL, du, "wdsetctlr failed");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Wait until driver is inactive, then set up controller.
+ */
+static int
+wdwsetctlr(struct disk *du)
+{
+ int stat;
+ int x;
+
+ wdsleep(du->dk_ctrlr, "wdwset");
+ x = splbio();
+ stat = wdsetctlr(du);
+ wdflushirq(du, x);
+ splx(x);
+ return (stat);
+}
+
+/*
+ * issue READP to drive to ask it what it is.
+ */
+static int
+wdgetctlr(struct disk *du)
+{
+ int i;
+ char tb[DEV_BSIZE];
+ struct wdparams *wp;
+
+ if (wdcommand(du, 0, 0, 0, 0, WDCC_READP) != 0
+ || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) {
+ /* XXX need to check error status after final transfer. */
+ /*
+ * Old drives don't support WDCC_READP. Try a seek to 0.
+ * Some IDE controllers return trash if there is no drive
+ * attached, so first test that the drive can be selected.
+ * This also avoids long waits for nonexistent drives.
+ */
+ if (wdwait(du, 0, TIMEOUT) < 0)
+ return (1);
+ outb(du->dk_port + wd_sdh, WDSD_IBM | (du->dk_unit << 4));
+ DELAY(5000); /* usually unnecessary; drive select is fast */
+ if ((inb(du->dk_port + wd_status) & (WDCS_BUSY | WDCS_READY))
+ != WDCS_READY
+ || wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0
+ || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0)
+ return (1);
+
+ /*
+ * Fake minimal drive geometry for reading the MBR.
+ * readdisklabel() may enlarge it to read the label and the
+ * bad sector table.
+ */
+ du->dk_dd.d_secsize = DEV_BSIZE;
+ du->dk_dd.d_nsectors = 17;
+ du->dk_dd.d_ntracks = 1;
+ du->dk_dd.d_ncylinders = 1;
+ du->dk_dd.d_secpercyl = 17;
+ du->dk_dd.d_secperunit = 17;
+
+ /*
+ * Fake maximal drive size for writing the label.
+ */
+ du->dk_dd.d_partitions[WDRAW].p_size = 64 * 16 * 1024;
+
+ /*
+ * Fake some more of the label for printing by disklabel(1)
+ * in case there is no real label.
+ */
+ du->dk_dd.d_type = DTYPE_ST506;
+ du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
+ strncpy(du->dk_dd.d_typename, "Fake geometry",
+ sizeof du->dk_dd.d_typename);
+
+ /* Fake the model name for printing by wdattach(). */
+ strncpy(du->dk_params.wdp_model, "unknown",
+ sizeof du->dk_params.wdp_model);
+
+ return (0);
+ }
+
+ /* obtain parameters */
+ wp = &du->dk_params;
+ insw(du->dk_port + wd_data, tb, sizeof(tb) / sizeof(short));
+ bcopy(tb, wp, sizeof(struct wdparams));
+
+ /* shuffle string byte order */
+ for (i = 0; i < sizeof(wp->wdp_model); i += 2) {
+ u_short *p;
+
+ p = (u_short *) (wp->wdp_model + i);
+ *p = ntohs(*p);
+ }
+ /*
+ * Clean up the wdp_model by converting nulls to spaces, and
+ * then removing the trailing spaces.
+ */
+ for (i=0; i < sizeof(wp->wdp_model); i++) {
+ if (wp->wdp_model[i] == '\0') {
+ wp->wdp_model[i] = ' ';
+ }
+ }
+ for (i=sizeof(wp->wdp_model)-1; i>=0 && wp->wdp_model[i]==' '; i--) {
+ wp->wdp_model[i] = '\0';
+ }
+
+#ifdef WDDEBUG
+ printf(
+"\nwd(%d,%d): wdgetctlr: gc %x cyl %d trk %d sec %d type %d sz %d model %s\n",
+ du->dk_ctrlr, du->dk_unit, wp->wdp_config,
+ wp->wdp_fixedcyl + wp->wdp_removcyl, wp->wdp_heads,
+ wp->wdp_sectors, wp->wdp_cntype, wp->wdp_cnsbsz,
+ wp->wdp_model);
+#endif
+
+ /* update disklabel given drive information */
+ du->dk_dd.d_secsize = DEV_BSIZE;
+ du->dk_dd.d_ncylinders = wp->wdp_fixedcyl + wp->wdp_removcyl /*+- 1*/ ;
+ du->dk_dd.d_ntracks = wp->wdp_heads;
+ du->dk_dd.d_nsectors = wp->wdp_sectors;
+ du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors;
+ du->dk_dd.d_partitions[WDRAW].p_size = du->dk_dd.d_secperunit
+ = du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders;
+ /* dubious ... */
+ bcopy("ESDI/IDE", du->dk_dd.d_typename, 9);
+ bcopy(wp->wdp_model + 20, du->dk_dd.d_packname, 14 - 1);
+ /* better ... */
+ du->dk_dd.d_type = DTYPE_ESDI;
+ du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
+
+ return (0);
+}
+
+
+/* ARGSUSED */
+int
+wdclose(dev_t dev, int flags, int fmt)
+{
+ register struct disk *du;
+ int part = wdpart(dev), mask = 1 << part;
+
+ if (wddospart(dev))
+ return (0);
+
+ du = wddrives[wdunit(dev)];
+
+ switch (fmt) {
+ case S_IFCHR:
+ du->dk_copenpart &= ~mask;
+ break;
+ case S_IFBLK:
+ du->dk_bopenpart &= ~mask;
+ break;
+ }
+ du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
+
+ return (0);
+}
+
+int
+wdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
+{
+ int lunit = wdunit(dev);
+ register struct disk *du;
+ int error = 0;
+#ifdef notyet
+ struct uio auio;
+ struct iovec aiov;
+#endif
+
+ du = wddrives[lunit];
+
+ switch (cmd) {
+
+ case DIOCSBAD:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else
+ du->dk_bad = *(struct dkbad *)addr;
+ break;
+
+ case DIOCGDINFO:
+ *(struct disklabel *)addr = du->dk_dd;
+ break;
+
+ case DIOCGPART:
+ if (wddospart(dev))
+ return (EINVAL);
+ ((struct partinfo *)addr)->disklab = &du->dk_dd;
+ ((struct partinfo *)addr)->part =
+ &du->dk_dd.d_partitions[wdpart(dev)];
+ break;
+
+ case DIOCSDINFO:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else
+ error = setdisklabel(&du->dk_dd,
+ (struct disklabel *)addr,
+#if 0
+ /*
+ * XXX setdisklabel() uses the
+ * openmask to allow it to reject
+ * changing open partitions. Why
+ * are we pretending nothing is
+ * open?
+ */
+ du->dk_flags & DKFL_BSDLABEL
+ ? du->dk_openpart :
+#endif
+ 0,
+ du->dk_dospartitions);
+ if (error == 0) {
+ du->dk_flags |= DKFL_BSDLABEL;
+ wdwsetctlr(du); /* XXX - check */
+ }
+ break;
+
+ case DIOCWLABEL:
+ du->dk_flags &= ~DKFL_WRITEPROT;
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else
+ du->dk_wlabel = *(int *)addr;
+ break;
+
+ case DIOCWDINFO:
+ du->dk_flags &= ~DKFL_WRITEPROT;
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else if ((error = setdisklabel(&du->dk_dd,
+ (struct disklabel *)addr,
+#if 0
+ du->dk_flags & DKFL_BSDLABEL
+ ? du->dk_openpart :
+#endif
+ 0,
+ du->dk_dospartitions)) == 0) {
+ int wlab;
+
+ du->dk_flags |= DKFL_BSDLABEL;
+ wdwsetctlr(du); /* XXX - check */
+
+ /* simulate opening partition 0 so write succeeds */
+ du->dk_openpart |= (1 << 0); /* XXX */
+ wlab = du->dk_wlabel;
+ du->dk_wlabel = 1;
+ error = writedisklabel(dev, WDSTRATEGY,
+ &du->dk_dd, du->dk_dospartitions);
+ du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
+ du->dk_wlabel = wlab;
+ }
+ break;
+
+#ifdef notyet
+ case DIOCGDINFOP:
+ *(struct disklabel **)addr = &(du->dk_dd);
+ break;
+
+ case DIOCWFORMAT:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else {
+ register struct format_op *fop;
+
+ fop = (struct format_op *)addr;
+ aiov.iov_base = fop->df_buf;
+ aiov.iov_len = fop->df_count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = fop->df_count;
+ auio.uio_segflg = 0;
+ auio.uio_offset =
+ fop->df_startblk * du->dk_dd.d_secsize;
+#error /* XXX the 386BSD interface is different */
+ error = physio(wdformat, &rwdbuf[lunit], 0, dev,
+ B_WRITE, minphys, &auio);
+ fop->df_count -= auio.uio_resid;
+ fop->df_reg[0] = du->dk_status;
+ fop->df_reg[1] = du->dk_error;
+ }
+ break;
+#endif
+
+ default:
+ error = ENOTTY;
+ break;
+ }
+ return (error);
+}
+
+#ifdef B_FORMAT
+int
+wdformat(struct buf *bp)
+{
+
+ bp->b_flags |= B_FORMAT;
+ return (wdstrategy(bp));
+}
+#endif
+
+int
+wdsize(dev_t dev)
+{
+ int lunit = wdunit(dev), part = wdpart(dev), val;
+ struct disk *du;
+ int size;
+
+ if (lunit >= NWD || wddospart(dev) || (du = wddrives[lunit]) == NULL) {
+ return (-1);
+ }
+ val = 0;
+ if (du->dk_state == CLOSED) {
+ val = wdopen(makewddev(major(dev), lunit, WDRAW),
+ FREAD, S_IFBLK, 0);
+ }
+ if (val != 0 || du->dk_flags & DKFL_WRITEPROT) {
+ return (-1);
+ }
+ size = ((int)du->dk_dd.d_partitions[part].p_size);
+ return size;
+}
+
+extern char *ptvmmap; /* poor name! */
+
+/*
+ * Dump core after a system crash.
+ */
+int
+wddump(dev_t dev)
+{
+ register struct disk *du;
+ register struct bt_bad *bt_ptr;
+ struct disklabel *lp;
+ long num; /* number of sectors to write */
+ int lunit, part;
+ long blkoff, blknum;
+ long blkchk, blkcnt, blknext;
+ long cylin, head, sector;
+ long secpertrk, secpercyl, nblocks;
+ char *addr;
+ extern int Maxmem;
+ static int wddoingadump = 0;
+ extern caddr_t CADDR1;
+
+ /* Toss any characters present prior to dump. */
+ while (sgetc(1))
+ ;
+
+ /* Check for acceptable device. */
+ /* XXX should reset to maybe allow du->dk_state < OPEN. */
+ lunit = wdunit(dev); /* eventually support floppies? */
+ part = wdpart(dev);
+ if (lunit >= NWD || wddospart(dev) || (du = wddrives[lunit]) == NULL
+ || du->dk_state < OPEN || du->dk_flags & DKFL_WRITEPROT)
+ return (ENXIO);
+
+ /* Size of memory to dump, in disk sectors. */
+ num = (u_long)Maxmem * NBPG / du->dk_dd.d_secsize;
+
+ secpertrk = du->dk_dd.d_nsectors;
+ secpercyl = du->dk_dd.d_secpercyl;
+ nblocks = du->dk_dd.d_partitions[part].p_size;
+ blkoff = du->dk_dd.d_partitions[part].p_offset;
+
+#if 0
+ pg("part %x, nblocks %d, dumplo %d num %d\n",
+ part, nblocks, dumplo, num);
+#endif
+
+ /* Check transfer bounds against partition size. */
+ if (dumplo < 0 || dumplo + num > nblocks)
+ return (EINVAL);
+
+ /* Check if we are being called recursively. */
+ if (wddoingadump)
+ return (EFAULT);
+
+#if 0
+ /* Mark controller active for if we panic during the dump. */
+ wdtab[du->dk_ctrlr].b_active = 1;
+#endif
+ wddoingadump = 1;
+
+ /* Recalibrate the drive. */
+ DELAY(5); /* ATA spec XXX NOT */
+ if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0
+ || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0
+ || wdsetctlr(du) != 0) {
+ wderror((struct buf *)NULL, du, "wddump: recalibrate failed");
+ return (EIO);
+ }
+
+ du->dk_flags |= DKFL_SINGLE;
+ addr = (char *) 0;
+ blknum = dumplo + blkoff;
+ while (num > 0) {
+ blkcnt = num;
+ if (blkcnt > MAXTRANSFER)
+ blkcnt = MAXTRANSFER;
+ /* Keep transfer within current cylinder. */
+ if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl)
+ blkcnt = secpercyl - (blknum % secpercyl);
+ blknext = blknum + blkcnt;
+
+ /*
+ * See if one of the sectors is in the bad sector list
+ * (if we have one). If the first sector is bad, then
+ * reduce the transfer to this one bad sector; if another
+ * sector is bad, then reduce reduce the transfer to
+ * avoid any bad sectors.
+ */
+ if ((du->dk_flags & (DKFL_SINGLE | DKFL_BADSECT))
+ == (DKFL_SINGLE | DKFL_BADSECT))
+ for (blkchk = blknum; blkchk < blknum + blkcnt; blkchk++) {
+ cylin = blkchk / secpercyl;
+ head = (blkchk % secpercyl) / secpertrk;
+ sector = blkchk % secpertrk;
+ for (bt_ptr = du->dk_bad.bt_bad;
+ bt_ptr->bt_cyl != BAD144_NO_CYL; bt_ptr++) {
+ if (bt_ptr->bt_cyl > cylin)
+ /*
+ * Sorted list, and we passed our cylinder.
+ * quit.
+ */
+ break;
+ if (bt_ptr->bt_cyl == cylin &&
+ bt_ptr->bt_trksec == (head << 8) + sector) {
+ /* Found bad block. */
+ blkcnt = blkchk - blknum;
+ if (blkcnt > 0) {
+ blknext = blknum + blkcnt;
+ goto out;
+ }
+ blkcnt = 1;
+ blknext = blknum + blkcnt;
+ /*
+ * Found bad block. Calculate new block number.
+ * This starts at the end of the disk (skip the
+ * last track which is used for the bad block list),
+ * and works backwards to the front of the disk.
+ */
+ /* XXX as usual. */
+#ifdef WDDEBUG
+ printf("--- badblock code -> Old = %ld; ",
+ blknum);
+#endif
+ lp = &du->dk_dd;
+ if (lp->d_partitions[BSD_PART].p_offset != 0)
+ blknum = lp->d_partitions[BAD144_PART]
+ .p_offset
+ + lp->d_partitions[BAD144_PART]
+ .p_size;
+ else
+ blknum = lp->d_secperunit;
+ blknum -= lp->d_nsectors
+ + (bt_ptr - du->dk_bad.bt_bad) + 1;
+#ifdef WDDEBUG
+ printf("new = %ld\n", blknum);
+#endif
+ break;
+ }
+ }
+ }
+out:
+
+ /* Compute disk address. */
+ cylin = blknum / secpercyl;
+ head = (blknum % secpercyl) / secpertrk;
+ sector = blknum % secpertrk;
+
+#if 0
+ /* Let's just talk about this first... */
+ pg("cylin l%d head %ld sector %ld addr 0x%x count %ld",
+ cylin, head, sector, addr, blkcnt);
+#endif
+
+ /* Do the write. */
+ if (wdcommand(du, cylin, head, sector, blkcnt, WDCC_WRITE)
+ != 0) {
+ wderror((struct buf *)NULL, du,
+ "wddump: timeout waiting to to give command");
+ return (EIO);
+ }
+ while (blkcnt != 0) {
+ pmap_enter(kernel_pmap, (vm_offset_t)CADDR1, trunc_page(addr),
+ VM_PROT_READ, TRUE);
+
+ /* Ready to send data? */
+ DELAY(5); /* ATA spec */
+ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT)
+ < 0) {
+ wderror((struct buf *)NULL, du,
+ "wddump: timeout waiting for DRQ");
+ return (EIO);
+ }
+ outsw(du->dk_port + wd_data,
+ CADDR1 + ((int)addr & (NBPG - 1)),
+ DEV_BSIZE / sizeof(short));
+ addr += DEV_BSIZE;
+ if ((unsigned)addr % (1024 * 1024) == 0)
+ printf("%ld ", num / (1024 * 1024 / DEV_BSIZE));
+ num--;
+ blkcnt--;
+ }
+
+ /* Wait for completion. */
+ DELAY(5); /* ATA spec XXX NOT */
+ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) < 0) {
+ wderror((struct buf *)NULL, du,
+ "wddump: timeout waiting for status");
+ return (EIO);
+ }
+
+ /* Check final status. */
+ if (du->dk_status
+ & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ | WDCS_ERR)
+ != (WDCS_READY | WDCS_SEEKCMPLT)) {
+ wderror((struct buf *)NULL, du,
+ "wddump: extra DRQ, or error");
+ return (EIO);
+ }
+
+ /* Update block count. */
+ blknum = blknext;
+
+ /* Operator aborting dump? */
+ if (sgetc(1) & 0xff) /* EWS: A hack to work with syscons */
+ return (EINTR);
+ }
+ return (0);
+}
+
+static void
+wderror(struct buf *bp, struct disk *du, char *mesg)
+{
+ if (bp == NULL)
+ printf("wd%d: %s:\n", du->dk_lunit, mesg);
+ else
+ diskerr(bp, "wd", mesg, LOG_PRINTF, du->dk_skip, &du->dk_dd);
+ printf("wd%d: status %b error %b\n", du->dk_lunit,
+ du->dk_status, WDCS_BITS, du->dk_error, WDERR_BITS);
+}
+
+/*
+ * Discard any interrupts that were latched by the interrupt system while
+ * we were doing polled i/o.
+ */
+static void
+wdflushirq(struct disk *du, int old_ipl)
+{
+ wdtab[du->dk_ctrlr].b_active = 2;
+ splx(old_ipl);
+ (void)splbio();
+ wdtab[du->dk_ctrlr].b_active = 0;
+}
+
+/*
+ * Reset the controller.
+ */
+static int
+wdreset(struct disk *du)
+{
+ int wdc;
+
+ wdc = du->dk_port;
+ (void)wdwait(du, 0, TIMEOUT);
+ outb(wdc + wd_ctlr, WDCTL_IDS | WDCTL_RST);
+ DELAY(10 * 1000);
+ outb(wdc + wd_ctlr, WDCTL_IDS);
+ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0
+ || (du->dk_error = inb(wdc + wd_error)) != 0x01)
+ return (1);
+ outb(wdc + wd_ctlr, WDCTL_4BIT);
+ return (0);
+}
+
+/*
+ * Sleep until driver is inactive.
+ * This is used only for avoiding rare race conditions, so it is unimportant
+ * that the sleep may be far too short or too long.
+ */
+static void
+wdsleep(int ctrlr, char *wmesg)
+{
+ while (wdtab[ctrlr].b_active)
+ tsleep((caddr_t)&wdtab[ctrlr].b_active, PZERO - 1, wmesg, 1);
+}
+
+static void
+wdtimeout(caddr_t cdu)
+{
+ struct disk *du;
+ int x;
+
+ du = (struct disk *)cdu;
+ x = splbio();
+ if (du->dk_timeout != 0 && --du->dk_timeout == 0) {
+ wderror((struct buf *)NULL, du, "interrupt timeout");
+ wdunwedge(du);
+ wdflushirq(du, x);
+ du->dk_skip = 0;
+ du->dk_flags |= DKFL_SINGLE;
+ wdstart(du->dk_ctrlr);
+ }
+ timeout((timeout_func_t)wdtimeout, cdu, hz);
+ splx(x);
+}
+
+/*
+ * Reset the controller after it has become wedged. This is different from
+ * wdreset() so that wdreset() can be used in the probe and so that this
+ * can restore the geometry .
+ */
+static int
+wdunwedge(struct disk *du)
+{
+ struct disk *du1;
+ int lunit;
+
+ /* Schedule other drives for recalibration. */
+ for (lunit = 0; lunit < NWD; lunit++)
+ if ((du1 = wddrives[lunit]) != NULL && du1 != du
+ && du1->dk_ctrlr == du->dk_ctrlr
+ && du1->dk_state > WANTOPEN)
+ du1->dk_state = WANTOPEN;
+
+ DELAY(RECOVERYTIME);
+ if (wdreset(du) == 0) {
+ /*
+ * XXX - recalibrate current drive now because some callers
+ * aren't prepared to have its state change.
+ */
+ if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) == 0
+ && wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) == 0
+ && wdsetctlr(du) == 0)
+ return (0);
+ }
+ wderror((struct buf *)NULL, du, "wdunwedge failed");
+ return (1);
+}
+
+/*
+ * Wait uninterruptibly until controller is not busy and either certain
+ * status bits are set or an error has occurred.
+ * The wait is usually short unless it is for the controller to process
+ * an entire critical command.
+ * Return 1 for (possibly stale) controller errors, -1 for timeout errors,
+ * or 0 for no errors.
+ * Return controller status in du->dk_status and, if there was a controller
+ * error, return the error code in du->dk_error.
+ */
+#ifdef WD_COUNT_RETRIES
+static int min_retries[NWDC];
+#endif
+
+static int
+wdwait(struct disk *du, u_char bits_wanted, int timeout)
+{
+ int wdc;
+ u_char status;
+
+#define POLLING 1000
+
+ wdc = du->dk_port;
+ timeout += POLLING;
+ do {
+#ifdef WD_COUNT_RETRIES
+ if (min_retries[du->dk_ctrlr] > timeout
+ || min_retries[du->dk_ctrlr] == 0)
+ min_retries[du->dk_ctrlr] = timeout;
+#endif
+ DELAY(5); /* ATA spec XXX NOT */
+ du->dk_status = status = inb(wdc + wd_status);
+ if (!(status & WDCS_BUSY)) {
+ if (status & WDCS_ERR) {
+ du->dk_error = inb(wdc + wd_error);
+ /*
+ * We once returned here. This is wrong
+ * because the error bit is apparently only
+ * valid after the controller has interrupted
+ * (e.g., the error bit is stale when we wait
+ * for DRQ for writes). So we can't depend
+ * on the error bit at all when polling for
+ * command completion.
+ */
+ }
+ if ((status & bits_wanted) == bits_wanted)
+ return (status & WDCS_ERR);
+ }
+ if (timeout < TIMEOUT)
+ /*
+ * Switch to a polling rate of about 1 KHz so that
+ * the timeout is almost machine-independent. The
+ * controller is taking a long time to respond, so
+ * an extra msec won't matter.
+ */
+ DELAY(1000);
+ } while (--timeout != 0);
+ return (-1);
+}
+
+/*
+ * Internalize the bad sector table.
+ */
+void bad144intern(struct disk *du) {
+ int i;
+ if (du->dk_flags & DKFL_BADSECT) {
+ for (i = 0; i < 127; i++) {
+ du->dk_badsect[i] = -1;
+ }
+ for (i = 0; i < 126; i++) {
+ if (du->dk_bad.bt_bad[i].bt_cyl == 0xffff) {
+ break;
+ } else {
+ du->dk_badsect[i] =
+ du->dk_bad.bt_bad[i].bt_cyl * du->dk_dd.d_secpercyl +
+ (du->dk_bad.bt_bad[i].bt_trksec >> 8) * du->dk_dd.d_nsectors
++
+ (du->dk_bad.bt_bad[i].bt_trksec & 0x00ff);
+ }
+ }
+ }
+}
+
+#endif /* NWDC > 0 */
diff --git a/sys/i386/isa/wdreg.h b/sys/i386/isa/wdreg.h
new file mode 100644
index 0000000..2694bed
--- /dev/null
+++ b/sys/i386/isa/wdreg.h
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)wdreg.h 7.1 (Berkeley) 5/9/91
+ * $Id: wxreg.h,v 1.1 1993/10/26 22:26:39 nate Exp $
+ */
+
+/*
+ * Disk Controller register definitions.
+ */
+#define wd_data 0x0 /* data register (R/W - 16 bits) */
+#define wd_error 0x1 /* error register (R) */
+#define wd_precomp wd_error /* write precompensation (W) */
+#define wd_seccnt 0x2 /* sector count (R/W) */
+#define wd_sector 0x3 /* first sector number (R/W) */
+#define wd_cyl_lo 0x4 /* cylinder address, low byte (R/W) */
+#define wd_cyl_hi 0x5 /* cylinder address, high byte (R/W)*/
+#define wd_sdh 0x6 /* sector size/drive/head (R/W)*/
+#define wd_command 0x7 /* command register (W) */
+#define wd_status wd_command /* immediate status (R) */
+
+#define wd_altsts 0x206 /*alternate fixed disk status(via 1015) (R)*/
+#define wd_ctlr 0x206 /*fixed disk controller control(via 1015) (W)*/
+#define WDCTL_4BIT 0x8 /* use four head bits (wd1003) */
+#define WDCTL_RST 0x4 /* reset the controller */
+#define WDCTL_IDS 0x2 /* disable controller interrupts */
+#define wd_digin 0x207 /* disk controller input(via 1015) (R)*/
+
+/*
+ * Status Bits.
+ */
+#define WDCS_BUSY 0x80 /* Controller busy bit. */
+#define WDCS_READY 0x40 /* Selected drive is ready */
+#define WDCS_WRTFLT 0x20 /* Write fault */
+#define WDCS_SEEKCMPLT 0x10 /* Seek complete */
+#define WDCS_DRQ 0x08 /* Data request bit. */
+#define WDCS_ECCCOR 0x04 /* ECC correction made in data */
+#define WDCS_INDEX 0x02 /* Index pulse from selected drive */
+#define WDCS_ERR 0x01 /* Error detect bit. */
+
+#define WDCS_BITS "\020\010busy\006rdy\006wrtflt\005seekdone\004drq\003ecc_cor\002index\001err"
+
+#define WDERR_BITS "\020\010badblk\007uncorr\006id_crc\005no_id\003abort\002tr000\001no_dam"
+
+/*
+ * Commands for Disk Controller.
+ */
+#define WDCC_RESTORE 0x10 /* disk restore code -- resets cntlr */
+
+#define WDCC_READ 0x20 /* disk read code */
+#define WDCC_WRITE 0x30 /* disk write code */
+#define WDCC__LONG 0x02 /* modifier -- access ecc bytes */
+#define WDCC__NORETRY 0x01 /* modifier -- no retrys */
+
+#define WDCC_FORMAT 0x50 /* disk format code */
+#define WDCC_DIAGNOSE 0x90 /* controller diagnostic */
+#define WDCC_IDC 0x91 /* initialize drive command */
+
+#define WDCC_EXTDCMD 0xE0 /* send extended command */
+#define WDCC_READP 0xEC /* read parameters from controller */
+#define WDCC_CACHEC 0xEF /* cache control */
+
+#define WD_STEP 0 /* winchester- default 35us step */
+
+#define WDSD_IBM 0xa0 /* forced to 512 byte sector, ecc */
+
+
+#ifdef KERNEL
+/*
+ * read parameters command returns this:
+ */
+struct wdparams {
+ /* drive info */
+ short wdp_config; /* general configuration */
+ short wdp_fixedcyl; /* number of non-removable cylinders */
+ short wdp_removcyl; /* number of removable cylinders */
+ short wdp_heads; /* number of heads */
+ short wdp_unfbytespertrk; /* number of unformatted bytes/track */
+ short wdp_unfbytes; /* number of unformatted bytes/sector */
+ short wdp_sectors; /* number of sectors */
+ short wdp_minisg; /* minimum bytes in inter-sector gap*/
+ short wdp_minplo; /* minimum bytes in postamble */
+ short wdp_vendstat; /* number of words of vendor status */
+ /* controller info */
+ char wdp_cnsn[20]; /* controller serial number */
+ short wdp_cntype; /* controller type */
+#define WDTYPE_SINGLEPORTSECTOR 1 /* single port, single sector buffer */
+#define WDTYPE_DUALPORTMULTI 2 /* dual port, multiple sector buffer */
+#define WDTYPE_DUALPORTMULTICACHE 3 /* above plus track cache */
+ short wdp_cnsbsz; /* sector buffer size, in sectors */
+ short wdp_necc; /* ecc bytes appended */
+ char wdp_rev[8]; /* firmware revision */
+ char wdp_model[40]; /* model name */
+ short wdp_nsecperint; /* sectors per interrupt */
+ short wdp_usedmovsd; /* can use double word read/write? */
+};
+
+/*
+ * wd driver entry points
+ */
+void wdstrategy(struct buf *bp);
+void wdintr(int unit);
+int wdopen(dev_t dev, int flags, int fmt, struct proc *p);
+int wdclose(dev_t dev, int flags, int fmt);
+int wdioctl(dev_t dev, int cmd, caddr_t addr, int flag);
+#ifdef B_FORMAT
+int wdformat(struct buf *bp);
+#endif
+int wdsize(dev_t dev);
+int wddump(dev_t dev);
+
+#endif KERNEL
diff --git a/sys/i386/isa/wt.c b/sys/i386/isa/wt.c
new file mode 100644
index 0000000..21a6ed9
--- /dev/null
+++ b/sys/i386/isa/wt.c
@@ -0,0 +1,902 @@
+/*
+ * Streamer tape driver for 386bsd and FreeBSD.
+ * Supports Archive and Wangtek compatible QIC-02/QIC-36 boards.
+ *
+ * Copyright (C) 1993 by:
+ * Sergey Ryzhkov <sir@kiae.su>
+ * Serge Vakulenko <vak@zebub.msk.su>
+ *
+ * This software is distributed with NO WARRANTIES, not even the implied
+ * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Authors grant any other persons or organisations permission to use
+ * or modify this software as long as this message is kept with the software,
+ * all derivative works or modified versions.
+ *
+ * This driver is derived from the old 386bsd Wangtek streamer tape driver,
+ * made by Robert Baron at CMU, based on Intel sources.
+ * Authors thank Robert Baron, CMU and Intel and retain here
+ * the original CMU copyright notice.
+ *
+ * Version 1.3, Thu Nov 11 12:09:13 MSK 1993
+ * $Id: wt.c,v 1.4 1993/12/13 18:38:43 alm Exp $
+ *
+ */
+
+/*
+ * Copyright (c) 1989 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Robert Baron
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include "wt.h"
+#if NWT > 0
+
+#include "sys/param.h"
+#include "systm.h"
+#include "kernel.h"
+#include "sys/buf.h"
+#include "sys/fcntl.h"
+#include "sys/malloc.h"
+#include "sys/ioctl.h"
+#include "sys/mtio.h"
+#include "vm/vm_param.h"
+#include "i386/include/pio.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/wtreg.h"
+
+/*
+ * Uncomment this to enable internal device tracing.
+ */
+#define DEBUG(s) /* printf s */
+
+#define WTPRI (PZERO+10) /* sleep priority */
+
+/*
+ * Wangtek controller ports
+ */
+#define WT_CTLPORT(base) ((base)+0) /* control, write only */
+#define WT_STATPORT(base) ((base)+0) /* status, read only */
+#define WT_CMDPORT(base) ((base)+1) /* command, write only */
+#define WT_DATAPORT(base) ((base)+1) /* data, read only */
+#define WT_NPORT 2 /* 2 i/o ports */
+
+/* status port bits */
+#define WT_BUSY 0x01 /* not ready bit define */
+#define WT_NOEXCEP 0x02 /* no exception bit define */
+#define WT_RESETMASK 0x07 /* to check after reset */
+#define WT_RESETVAL 0x05 /* state after reset */
+
+/* control port bits */
+#define WT_ONLINE 0x01 /* device selected */
+#define WT_RESET 0x02 /* reset command */
+#define WT_REQUEST 0x04 /* request command */
+#define WT_IEN 0x08 /* enable dma */
+
+/*
+ * Archive controller ports
+ */
+#define AV_DATAPORT(base) ((base)+0) /* data, read only */
+#define AV_CMDPORT(base) ((base)+0) /* command, write only */
+#define AV_STATPORT(base) ((base)+1) /* status, read only */
+#define AV_CTLPORT(base) ((base)+1) /* control, write only */
+#define AV_SDMAPORT(base) ((base)+2) /* start dma */
+#define AV_RDMAPORT(base) ((base)+3) /* reset dma */
+#define AV_NPORT 4 /* 4 i/o ports */
+
+/* status port bits */
+#define AV_BUSY 0x40 /* not ready bit define */
+#define AV_NOEXCEP 0x20 /* no exception bit define */
+#define AV_RESETMASK 0xf8 /* to check after reset */
+#define AV_RESETVAL 0x50 /* state after reset */
+
+/* control port bits */
+#define AV_RESET 0x80 /* reset command */
+#define AV_REQUEST 0x40 /* request command */
+#define AV_IEN 0x20 /* enable interrupts */
+
+enum wttype {
+ UNKNOWN = 0, /* unknown type, driver disabled */
+ ARCHIVE, /* Archive Viper SC499, SC402 etc */
+ WANGTEK, /* Wangtek */
+};
+
+typedef struct {
+ unsigned short err; /* code for error encountered */
+ unsigned short ercnt; /* number of error blocks */
+ unsigned short urcnt; /* number of underruns */
+} wtstatus_t;
+
+typedef struct {
+ enum wttype type; /* type of controller */
+ unsigned unit; /* unit number */
+ unsigned port; /* base i/o port */
+ unsigned chan; /* dma channel number, 1..3 */
+ unsigned flags; /* state of tape drive */
+ unsigned dens; /* tape density */
+ int bsize; /* tape block size */
+ void *buf; /* internal i/o buffer */
+
+ void *dmavaddr; /* virtual address of dma i/o buffer */
+ unsigned dmatotal; /* size of i/o buffer */
+ unsigned dmaflags; /* i/o direction, B_READ or B_WRITE */
+ unsigned dmacount; /* resulting length of dma i/o */
+
+ wtstatus_t error; /* status of controller */
+
+ unsigned short DATAPORT, CMDPORT, STATPORT, CTLPORT, SDMAPORT, RDMAPORT;
+ unsigned char BUSY, NOEXCEP, RESETMASK, RESETVAL;
+ unsigned char ONLINE, RESET, REQUEST, IEN;
+} wtinfo_t;
+
+wtinfo_t wttab[NWT]; /* tape info by unit number */
+
+static int wtwait (wtinfo_t *t, int catch, char *msg);
+static int wtcmd (wtinfo_t *t, int cmd);
+static int wtstart (wtinfo_t *t, unsigned mode, void *vaddr, unsigned len);
+static void wtdma (wtinfo_t *t);
+static void wtimer (caddr_t);
+static void wtclock (wtinfo_t *t);
+static int wtreset (wtinfo_t *t);
+static int wtsense (wtinfo_t *t, int verb, int ignor);
+static int wtstatus (wtinfo_t *t);
+static void wtrewind (wtinfo_t *t);
+static int wtreadfm (wtinfo_t *t);
+static int wtwritefm (wtinfo_t *t);
+static int wtpoll (wtinfo_t *t, int mask, int bits);
+
+/* XXX */
+extern void DELAY (int usec);
+
+/*
+ * Probe for the presence of the device.
+ */
+int wtprobe (struct isa_device *id)
+{
+ wtinfo_t *t = wttab + id->id_unit;
+
+ t->unit = id->id_unit;
+ t->chan = id->id_drq;
+ t->port = id->id_iobase;
+ if (t->chan<1 || t->chan>3) {
+ printf ("wt%d: Bad drq=%d, should be 1..3\n", t->unit, t->chan);
+ return (0);
+ }
+
+ /* Try Wangtek. */
+ t->type = WANGTEK;
+ t->CTLPORT = WT_CTLPORT (t->port); t->STATPORT = WT_STATPORT (t->port);
+ t->CMDPORT = WT_CMDPORT (t->port); t->DATAPORT = WT_DATAPORT (t->port);
+ t->SDMAPORT = 0; t->RDMAPORT = 0;
+ t->BUSY = WT_BUSY; t->NOEXCEP = WT_NOEXCEP;
+ t->RESETMASK = WT_RESETMASK; t->RESETVAL = WT_RESETVAL;
+ t->ONLINE = WT_ONLINE; t->RESET = WT_RESET;
+ t->REQUEST = WT_REQUEST; t->IEN = WT_IEN;
+ if (wtreset (t))
+ return (WT_NPORT);
+
+ /* Try Archive. */
+ t->type = ARCHIVE;
+ t->CTLPORT = AV_CTLPORT (t->port); t->STATPORT = AV_STATPORT (t->port);
+ t->CMDPORT = AV_CMDPORT (t->port); t->DATAPORT = AV_DATAPORT (t->port);
+ t->SDMAPORT = AV_SDMAPORT (t->port); t->RDMAPORT = AV_RDMAPORT (t->port);
+ t->BUSY = AV_BUSY; t->NOEXCEP = AV_NOEXCEP;
+ t->RESETMASK = AV_RESETMASK; t->RESETVAL = AV_RESETVAL;
+ t->ONLINE = 0; t->RESET = AV_RESET;
+ t->REQUEST = AV_REQUEST; t->IEN = AV_IEN;
+ if (wtreset (t))
+ return (AV_NPORT);
+
+ /* Tape controller not found. */
+ t->type = UNKNOWN;
+ return (0);
+}
+
+/*
+ * Device is found, configure it.
+ */
+int wtattach (struct isa_device *id)
+{
+ wtinfo_t *t = wttab + id->id_unit;
+
+ if (t->type == ARCHIVE) {
+ printf ("wt%d: type <Archive>\n", t->unit);
+ outb (t->RDMAPORT, 0); /* reset dma */
+ } else
+ printf ("wt%d: type <Wangtek>\n", t->unit);
+ t->flags = TPSTART; /* tape is rewound */
+ t->dens = -1; /* unknown density */
+ return (1);
+}
+
+struct isa_driver wtdriver = { wtprobe, wtattach, "wt", };
+
+int wtdump (int dev)
+{
+ /* Not implemented */
+ return (EINVAL);
+}
+
+int wtsize (int dev)
+{
+ /* Not implemented */
+ return (-1);
+}
+
+/*
+ * Open routine, called on every device open.
+ */
+int wtopen (int dev, int flag)
+{
+ int u = minor (dev) & T_UNIT;
+ wtinfo_t *t = wttab + u;
+ int error;
+
+ if (u >= NWT || t->type == UNKNOWN)
+ return (ENXIO);
+
+ /* Check that device is not in use */
+ if (t->flags & TPINUSE)
+ return (EBUSY);
+
+ /* If the tape is in rewound state, check the status and set density. */
+ if (t->flags & TPSTART) {
+ /* If rewind is going on, wait */
+ if (error = wtwait (t, PCATCH, "wtrew"))
+ return (error);
+
+ /* Check the controller status */
+ if (! wtsense (t, 0, (flag & FWRITE) ? 0 : TP_WRP)) {
+ /* Bad status, reset the controller */
+ if (! wtreset (t))
+ return (EIO);
+ if (! wtsense (t, 1, (flag & FWRITE) ? 0 : TP_WRP))
+ return (EIO);
+ }
+
+ /* Set up tape density. */
+ if (t->dens != (minor (dev) & WT_DENSEL)) {
+ int d = 0;
+
+ switch (minor (dev) & WT_DENSEL) {
+ case WT_DENSDFLT: default: break; /* default density */
+ case WT_QIC11: d = QIC_FMT11; break; /* minor 010 */
+ case WT_QIC24: d = QIC_FMT24; break; /* minor 020 */
+ case WT_QIC120: d = QIC_FMT120; break; /* minor 030 */
+ case WT_QIC150: d = QIC_FMT150; break; /* minor 040 */
+ case WT_QIC300: d = QIC_FMT300; break; /* minor 050 */
+ case WT_QIC600: d = QIC_FMT600; break; /* minor 060 */
+ }
+ if (d) {
+ /* Change tape density. */
+ if (! wtcmd (t, d))
+ return (EIO);
+ if (! wtsense (t, 1, TP_WRP | TP_ILL))
+ return (EIO);
+
+ /* Check the status of the controller. */
+ if (t->error.err & TP_ILL) {
+ printf ("wt%d: invalid tape density\n", t->unit);
+ return (ENODEV);
+ }
+ }
+ t->dens = minor (dev) & WT_DENSEL;
+ }
+ t->flags &= ~TPSTART;
+ } else if (t->dens != (minor (dev) & WT_DENSEL))
+ return (ENXIO);
+
+ t->bsize = (minor (dev) & WT_BSIZE) ? 1024 : 512;
+ t->buf = malloc (t->bsize, M_TEMP, M_WAITOK);
+ if (! t->buf)
+ return (EAGAIN);
+
+ t->flags = TPINUSE;
+ if (flag & FREAD)
+ t->flags |= TPREAD;
+ if (flag & FWRITE)
+ t->flags |= TPWRITE;
+ return (0);
+}
+
+/*
+ * Close routine, called on last device close.
+ */
+int wtclose (int dev)
+{
+ int u = minor (dev) & T_UNIT;
+ wtinfo_t *t = wttab + u;
+
+ if (u >= NWT || t->type == UNKNOWN)
+ return (ENXIO);
+
+ /* If rewind is pending, do nothing */
+ if (t->flags & TPREW)
+ goto done;
+
+ /* If seek forward is pending and no rewind on close, do nothing */
+ if (t->flags & TPRMARK) {
+ if (minor (dev) & T_NOREWIND)
+ goto done;
+
+ /* If read file mark is going on, wait */
+ wtwait (t, 0, "wtrfm");
+ }
+
+ if (t->flags & TPWANY)
+ /* Tape was written. Write file mark. */
+ wtwritefm (t);
+
+ if (! (minor (dev) & T_NOREWIND)) {
+ /* Rewind tape to beginning of tape. */
+ /* Don't wait until rewind, though. */
+ wtrewind (t);
+ goto done;
+ }
+ if ((t->flags & TPRANY) && ! (t->flags & (TPVOL | TPWANY)))
+ /* Space forward to after next file mark if no writing done. */
+ /* Don't wait for completion. */
+ wtreadfm (t);
+done:
+ t->flags &= TPREW | TPRMARK | TPSTART | TPTIMER;
+ free (t->buf, M_TEMP);
+ return (0);
+}
+
+/*
+ * Ioctl routine. Compatible with BSD ioctls.
+ * Direct QIC-02 commands ERASE and RETENSION added.
+ * There are three possible ioctls:
+ * ioctl (int fd, MTIOCGET, struct mtget *buf) -- get status
+ * ioctl (int fd, MTIOCTOP, struct mtop *buf) -- do BSD-like op
+ * ioctl (int fd, WTQICMD, int qicop) -- do QIC op
+ */
+int wtioctl (int dev, int cmd, void *arg, int mode)
+{
+ int u = minor (dev) & T_UNIT;
+ wtinfo_t *t = wttab + u;
+ int error, count, op;
+
+ if (u >= NWT || t->type == UNKNOWN)
+ return (ENXIO);
+
+ switch (cmd) {
+ default:
+ return (EINVAL);
+ case WTQICMD: /* direct QIC command */
+ op = (int) *(void**)arg;
+ switch (op) {
+ default:
+ return (EINVAL);
+ case QIC_ERASE: /* erase the whole tape */
+ if (! (t->flags & TPWRITE) || (t->flags & TPWP))
+ return (EACCES);
+ if (error = wtwait (t, PCATCH, "wterase"))
+ return (error);
+ break;
+ case QIC_RETENS: /* retension the tape */
+ if (error = wtwait (t, PCATCH, "wtretens"))
+ return (error);
+ break;
+ }
+ /* Both ERASE and RETENS operations work like REWIND. */
+ /* Simulate the rewind operation here. */
+ t->flags &= ~(TPRO | TPWO | TPVOL);
+ if (! wtcmd (t, op))
+ return (EIO);
+ t->flags |= TPSTART | TPREW;
+ if (op == QIC_ERASE)
+ t->flags |= TPWANY;
+ wtclock (t);
+ return (0);
+ case MTIOCIEOT: /* ignore EOT errors */
+ case MTIOCEEOT: /* enable EOT errors */
+ return (0);
+ case MTIOCGET:
+ ((struct mtget*)arg)->mt_type =
+ t->type == ARCHIVE ? MT_ISVIPER1 : 0x11;
+ ((struct mtget*)arg)->mt_dsreg = t->flags; /* status */
+ ((struct mtget*)arg)->mt_erreg = t->error.err; /* errors */
+ ((struct mtget*)arg)->mt_resid = 0;
+ ((struct mtget*)arg)->mt_fileno = 0; /* file */
+ ((struct mtget*)arg)->mt_blkno = 0; /* block */
+ return (0);
+ case MTIOCTOP:
+ break;
+ }
+ switch ((short) ((struct mtop*)arg)->mt_op) {
+ default:
+ case MTFSR: /* forward space record */
+ case MTBSR: /* backward space record */
+ case MTBSF: /* backward space file */
+ break;
+ case MTNOP: /* no operation, sets status only */
+ case MTCACHE: /* enable controller cache */
+ case MTNOCACHE: /* disable controller cache */
+ return (0);
+ case MTREW: /* rewind */
+ case MTOFFL: /* rewind and put the drive offline */
+ if (t->flags & TPREW) /* rewind is running */
+ return (0);
+ if (error = wtwait (t, PCATCH, "wtorew"))
+ return (error);
+ wtrewind (t);
+ return (0);
+ case MTFSF: /* forward space file */
+ for (count=((struct mtop*)arg)->mt_count; count>0; --count) {
+ if (error = wtwait (t, PCATCH, "wtorfm"))
+ return (error);
+ if (error = wtreadfm (t))
+ return (error);
+ }
+ return (0);
+ case MTWEOF: /* write an end-of-file record */
+ if (! (t->flags & TPWRITE) || (t->flags & TPWP))
+ return (EACCES);
+ if (error = wtwait (t, PCATCH, "wtowfm"))
+ return (error);
+ if (error = wtwritefm (t))
+ return (error);
+ return (0);
+ }
+ return (EINVAL);
+}
+
+/*
+ * Strategy routine.
+ */
+void wtstrategy (struct buf *bp)
+{
+ int u = minor (bp->b_dev) & T_UNIT;
+ wtinfo_t *t = wttab + u;
+ int s;
+
+ bp->b_resid = bp->b_bcount;
+ if (u >= NWT || t->type == UNKNOWN)
+ goto errxit;
+
+ /* at file marks and end of tape, we just return '0 bytes available' */
+ if (t->flags & TPVOL)
+ goto xit;
+
+ if (bp->b_flags & B_READ) {
+ /* Check read access and no previous write to this tape. */
+ if (! (t->flags & TPREAD) || (t->flags & TPWANY))
+ goto errxit;
+
+ /* For now, we assume that all data will be copied out */
+ /* If read command outstanding, just skip down */
+ if (! (t->flags & TPRO)) {
+ if (! wtsense (t, 1, TP_WRP)) /* clear status */
+ goto errxit;
+ if (! wtcmd (t, QIC_RDDATA)) { /* sed read mode */
+ wtsense (t, 1, TP_WRP);
+ goto errxit;
+ }
+ t->flags |= TPRO | TPRANY;
+ }
+ } else {
+ /* Check write access and write protection. */
+ /* No previous read from this tape allowed. */
+ if (! (t->flags & TPWRITE) || (t->flags & (TPWP | TPRANY)))
+ goto errxit;
+
+ /* If write command outstanding, just skip down */
+ if (! (t->flags & TPWO)) {
+ if (! wtsense (t, 1, 0)) /* clear status */
+ goto errxit;
+ if (! wtcmd (t, QIC_WRTDATA)) { /* set write mode */
+ wtsense (t, 1, 0);
+ goto errxit;
+ }
+ t->flags |= TPWO | TPWANY;
+ }
+ }
+
+ if (! bp->b_bcount)
+ goto xit;
+
+ t->flags &= ~TPEXCEP;
+ s = splbio ();
+ if (wtstart (t, bp->b_flags, bp->b_un.b_addr, bp->b_bcount)) {
+ wtwait (t, 0, (bp->b_flags & B_READ) ? "wtread" : "wtwrite");
+ bp->b_resid -= t->dmacount;
+ }
+ splx (s);
+
+ if (t->flags & TPEXCEP) {
+errxit: bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ }
+xit: biodone (bp);
+ return;
+}
+
+/*
+ * Interrupt routine.
+ */
+void wtintr (int u)
+{
+ wtinfo_t *t = wttab + u;
+ unsigned char s;
+
+ if (u >= NWT || t->type == UNKNOWN) {
+ DEBUG (("wtintr() -- device not configured\n"));
+ return;
+ }
+
+ s = inb (t->STATPORT); /* get status */
+ DEBUG (("wtintr() status=0x%x -- ", s));
+ if ((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP)) {
+ DEBUG (("busy\n"));
+ return; /* device is busy */
+ }
+
+ /*
+ * Check if rewind finished.
+ */
+ if (t->flags & TPREW) {
+ DEBUG (((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP) ?
+ "rewind busy?\n" : "rewind finished\n"));
+ t->flags &= ~TPREW; /* Rewind finished. */
+ wtsense (t, 1, TP_WRP);
+ wakeup ((caddr_t)t);
+ return;
+ }
+
+ /*
+ * Check if writing/reading of file mark finished.
+ */
+ if (t->flags & (TPRMARK | TPWMARK)) {
+ DEBUG (((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP) ?
+ "marker r/w busy?\n" : "marker r/w finished\n"));
+ if (! (s & t->NOEXCEP)) /* operation failed */
+ wtsense (t, 1, (t->flags & TPRMARK) ? TP_WRP : 0);
+ t->flags &= ~(TPRMARK | TPWMARK); /* operation finished */
+ wakeup ((caddr_t)t);
+ return;
+ }
+
+ /*
+ * Do we started any i/o? If no, just return.
+ */
+ if (! (t->flags & TPACTIVE)) {
+ DEBUG (("unexpected interrupt\n"));
+ return;
+ }
+ t->flags &= ~TPACTIVE;
+ t->dmacount += t->bsize; /* increment counter */
+
+ /*
+ * Clean up dma.
+ */
+ if ((t->dmaflags & B_READ) && (t->dmatotal - t->dmacount) < t->bsize) {
+ /* If reading short block, copy the internal buffer
+ * to the user memory. */
+ isa_dmadone (t->dmaflags, t->buf, t->bsize, t->chan);
+ bcopy (t->buf, t->dmavaddr, t->dmatotal - t->dmacount);
+ } else
+ isa_dmadone (t->dmaflags, t->dmavaddr, t->bsize, t->chan);
+
+ /*
+ * On exception, check for end of file and end of volume.
+ */
+ if (! (s & t->NOEXCEP)) {
+ DEBUG (("i/o exception\n"));
+ wtsense (t, 1, (t->dmaflags & B_READ) ? TP_WRP : 0);
+ if (t->error.err & (TP_EOM | TP_FIL))
+ t->flags |= TPVOL; /* end of file */
+ else
+ t->flags |= TPEXCEP; /* i/o error */
+ wakeup ((caddr_t)t);
+ return;
+ }
+
+ if (t->dmacount < t->dmatotal) { /* continue i/o */
+ t->dmavaddr += t->bsize;
+ wtdma (t);
+ DEBUG (("continue i/o, %d\n", t->dmacount));
+ return;
+ }
+ if (t->dmacount > t->dmatotal) /* short last block */
+ t->dmacount = t->dmatotal;
+ wakeup ((caddr_t)t); /* wake up user level */
+ DEBUG (("i/o finished, %d\n", t->dmacount));
+}
+
+/* start the rewind operation */
+static void wtrewind (wtinfo_t *t)
+{
+ int rwmode = (t->flags & (TPRO | TPWO));
+
+ t->flags &= ~(TPRO | TPWO | TPVOL);
+ /*
+ * Wangtek strictly follows QIC-02 standard:
+ * clearing ONLINE in read/write modes causes rewind.
+ * REWIND command is not allowed in read/write mode
+ * and gives `illegal command' error.
+ */
+ if (t->type==WANGTEK && rwmode) {
+ outb (t->CTLPORT, 0);
+ } else if (! wtcmd (t, QIC_REWIND))
+ return;
+ t->flags |= TPSTART | TPREW;
+ wtclock (t);
+}
+
+/* start the `read marker' operation */
+static int wtreadfm (wtinfo_t *t)
+{
+ t->flags &= ~(TPRO | TPWO | TPVOL);
+ if (! wtcmd (t, QIC_READFM)) {
+ wtsense (t, 1, TP_WRP);
+ return (EIO);
+ }
+ t->flags |= TPRMARK | TPRANY;
+ wtclock (t);
+ /* Don't wait for completion here. */
+ return (0);
+}
+
+/* write marker to the tape */
+static int wtwritefm (wtinfo_t *t)
+{
+ tsleep ((caddr_t)wtwritefm, WTPRI, "wtwfm", hz); /* timeout: 1 second */
+ t->flags &= ~(TPRO | TPWO);
+ if (! wtcmd (t, QIC_WRITEFM)) {
+ wtsense (t, 1, 0);
+ return (EIO);
+ }
+ t->flags |= TPWMARK | TPWANY;
+ wtclock (t);
+ return (wtwait (t, 0, "wtwfm"));
+}
+
+/* while controller status & mask == bits continue waiting */
+static int wtpoll (wtinfo_t *t, int mask, int bits)
+{
+ int s, i;
+
+ /* Poll status port, waiting for specified bits. */
+ for (i=0; i<1000; ++i) { /* up to 1 msec */
+ s = inb (t->STATPORT);
+ if ((s & mask) != bits)
+ return (s);
+ DELAY (1);
+ }
+ for (i=0; i<100; ++i) { /* up to 10 msec */
+ s = inb (t->STATPORT);
+ if ((s & mask) != bits)
+ return (s);
+ DELAY (100);
+ }
+ for (;;) { /* forever */
+ s = inb (t->STATPORT);
+ if ((s & mask) != bits)
+ return (s);
+ tsleep ((caddr_t)wtpoll, WTPRI, "wtpoll", 1); /* timeout: 1 tick */
+ }
+}
+
+/* execute QIC command */
+static int wtcmd (wtinfo_t *t, int cmd)
+{
+ int s;
+
+ DEBUG (("wtcmd() cmd=0x%x\n", cmd));
+ s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */
+ if (! (s & t->NOEXCEP)) /* error */
+ return (0);
+
+ outb (t->CMDPORT, cmd); /* output the command */
+
+ outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */
+ wtpoll (t, t->BUSY, t->BUSY); /* wait for ready */
+ outb (t->CTLPORT, t->IEN | t->ONLINE); /* reset request */
+ wtpoll (t, t->BUSY, 0); /* wait for not ready */
+ return (1);
+}
+
+/* wait for the end of i/o, seeking marker or rewind operation */
+static int wtwait (wtinfo_t *t, int catch, char *msg)
+{
+ int error;
+
+ DEBUG (("wtwait() `%s'\n", msg));
+ while (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK))
+ if (error = tsleep ((caddr_t)t, WTPRI | catch, msg, 0))
+ return (error);
+ return (0);
+}
+
+/* initialize dma for the i/o operation */
+static void wtdma (wtinfo_t *t)
+{
+ t->flags |= TPACTIVE;
+ wtclock (t);
+
+ if (t->type == ARCHIVE)
+ outb (t->SDMAPORT, 0); /* set dma */
+
+ if ((t->dmaflags & B_READ) && (t->dmatotal - t->dmacount) < t->bsize)
+ /* Reading short block. Do it through the internal buffer. */
+ isa_dmastart (t->dmaflags, t->buf, t->bsize, t->chan);
+ else
+ isa_dmastart (t->dmaflags, t->dmavaddr, t->bsize, t->chan);
+}
+
+/* start i/o operation */
+static int wtstart (wtinfo_t *t, unsigned flags, void *vaddr, unsigned len)
+{
+ int s;
+
+ DEBUG (("wtstart()\n"));
+ s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */
+ if (! (s & t->NOEXCEP)) {
+ t->flags |= TPEXCEP; /* error */
+ return (0);
+ }
+ t->flags &= ~TPEXCEP; /* clear exception flag */
+ t->dmavaddr = vaddr;
+ t->dmatotal = len;
+ t->dmacount = 0;
+ t->dmaflags = flags;
+ wtdma (t);
+ return (1);
+}
+
+/* start timer */
+static void wtclock (wtinfo_t *t)
+{
+ if (! (t->flags & TPTIMER)) {
+ t->flags |= TPTIMER;
+ /* Some controllers seem to lose dma interrupts too often.
+ * To make the tape stream we need 1 tick timeout. */
+ timeout ((timeout_func_t)wtimer, (caddr_t)t, (t->flags & TPACTIVE) ? 1 : hz);
+ }
+}
+
+/*
+ * Simulate an interrupt periodically while i/o is going.
+ * This is necessary in case interrupts get eaten due to
+ * multiple devices on a single IRQ line.
+ */
+static void wtimer (caddr_t xt)
+{
+ wtinfo_t *t = (wtinfo_t *)xt;
+ int s;
+
+ t->flags &= ~TPTIMER;
+ if (! (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK)))
+ return;
+
+ /* If i/o going, simulate interrupt. */
+ s = splbio ();
+ if ((inb (t->STATPORT) & (t->BUSY | t->NOEXCEP)) != (t->BUSY | t->NOEXCEP)) {
+ DEBUG (("wtimer() -- "));
+ wtintr (t->unit);
+ }
+ splx (s);
+
+ /* Restart timer if i/o pending. */
+ if (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK))
+ wtclock (t);
+}
+
+/* reset the controller */
+static int wtreset (wtinfo_t *t)
+{
+ /* Perform QIC-02 and QIC-36 compatible reset sequence. */
+ /* Thanks to Mikael Hybsch <micke@dynas.se>. */
+ int s, i;
+
+ outb (t->CTLPORT, t->RESET | t->ONLINE); /* send reset */
+ DELAY (30);
+ outb (t->CTLPORT, t->ONLINE); /* turn off reset */
+ DELAY (30);
+
+ /* Read the controller status. */
+ s = inb (t->STATPORT);
+ if (s == 0xff) /* no port at this address? */
+ return (0);
+
+ /* Wait 3 sec for reset to complete. Needed for QIC-36 boards? */
+ for (i=0; i<3000; ++i) {
+ if (! (s & t->BUSY) || ! (s & t->NOEXCEP))
+ break;
+ DELAY (1000);
+ s = inb (t->STATPORT);
+ }
+ return ((s & t->RESETMASK) == t->RESETVAL);
+}
+
+/* get controller status information */
+/* return 0 if user i/o request should receive an i/o error code */
+static int wtsense (wtinfo_t *t, int verb, int ignor)
+{
+ char *msg = 0;
+ int err;
+
+ DEBUG (("wtsense() ignor=0x%x\n", ignor));
+ t->flags &= ~(TPRO | TPWO);
+ if (! wtstatus (t))
+ return (0);
+ if (! (t->error.err & TP_ST0))
+ t->error.err &= ~TP_ST0MASK;
+ if (! (t->error.err & TP_ST1))
+ t->error.err &= ~TP_ST1MASK;
+ t->error.err &= ~ignor; /* ignore certain errors */
+ err = t->error.err & (TP_FIL | TP_BNL | TP_UDA | TP_EOM | TP_WRP |
+ TP_USL | TP_CNI | TP_MBD | TP_NDT | TP_ILL);
+ if (! err)
+ return (1);
+ if (! verb)
+ return (0);
+
+ /* lifted from tdriver.c from Wangtek */
+ if (err & TP_USL) msg = "Drive not online";
+ else if (err & TP_CNI) msg = "No cartridge";
+ else if ((err & TP_WRP) && !(t->flags & TPWP)) {
+ msg = "Tape is write protected";
+ t->flags |= TPWP;
+ }
+ else if (err & TP_FIL) msg = 0 /*"Filemark detected"*/;
+ else if (err & TP_EOM) msg = 0 /*"End of tape"*/;
+ else if (err & TP_BNL) msg = "Block not located";
+ else if (err & TP_UDA) msg = "Unrecoverable data error";
+ else if (err & TP_NDT) msg = "No data detected";
+ else if (err & TP_ILL) msg = "Illegal command";
+ if (msg)
+ printf ("wt%d: %s\n", t->unit, msg);
+ return (0);
+}
+
+/* get controller status information */
+static int wtstatus (wtinfo_t *t)
+{
+ char *p;
+
+ wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */
+ outb (t->CMDPORT, QIC_RDSTAT); /* send `read status' command */
+
+ outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */
+ wtpoll (t, t->BUSY, t->BUSY); /* wait for ready */
+ outb (t->CTLPORT, t->ONLINE); /* reset request */
+ wtpoll (t, t->BUSY, 0); /* wait for not ready */
+
+ p = (char*) &t->error;
+ while (p < (char*)&t->error + 6) {
+ int s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP);
+ if (! (s & t->NOEXCEP)) /* error */
+ return (0);
+
+ *p++ = inb (t->DATAPORT); /* read status byte */
+
+ outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */
+ wtpoll (t, t->BUSY, 0); /* wait for not ready */
+ outb (t->CTLPORT, t->ONLINE); /* unset request */
+ }
+ return (1);
+}
+#endif /* NWT */
diff --git a/sys/i386/isa/wtreg.h b/sys/i386/isa/wtreg.h
new file mode 100644
index 0000000..e45e514
--- /dev/null
+++ b/sys/i386/isa/wtreg.h
@@ -0,0 +1,125 @@
+/*
+ * Streamer tape driver for 386bsd and FreeBSD.
+ * Supports Archive and Wangtek compatible QIC-02/QIC-36 boards.
+ *
+ * Copyright (C) 1993 by:
+ * Sergey Ryzhkov <sir@kiae.su>
+ * Serge Vakulenko <vak@zebub.msk.su>
+ *
+ * This software is distributed with NO WARRANTIES, not even the implied
+ * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Authors grant any other persons or organisations permission to use
+ * or modify this software as long as this message is kept with the software,
+ * all derivative works or modified versions.
+ *
+ * This driver is derived from the old 386bsd Wangtek streamer tape driver,
+ * made by Robert Baron at CMU, based on Intel sources.
+ * Authors thank Robert Baron, CMU and Intel and retain here
+ * the original CMU copyright notice.
+ *
+ * Version 1.3, Thu Nov 11 12:09:13 MSK 1993
+ * $Id$
+ *
+ */
+
+/*
+ * Copyright (c) 1989 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Robert Baron
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/* ioctl for direct QIC commands */
+#define WTQICMD _IO('W', 0)
+
+/* QIC-02 commands allowed for WTQICMD */
+#define QIC_ERASE 0x22 /* erase the tape */
+#define QIC_RETENS 0x24 /* retension the tape */
+
+/* internal QIC-02 commands */
+#define QIC_RDDATA 0x80 /* read data */
+#define QIC_READFM 0xa0 /* read file mark */
+#define QIC_WRTDATA 0x40 /* write data */
+#define QIC_WRITEFM 0x60 /* write file mark */
+#define QIC_RDSTAT 0xc0 /* read status command */
+#define QIC_REWIND 0x21 /* rewind command (position+bot) */
+#define QIC_FMT11 0x26 /* set format QIC-11 */
+#define QIC_FMT24 0x27 /* set format QIC-24 */
+#define QIC_FMT120 0x28 /* set format QIC-120 */
+#define QIC_FMT150 0x29 /* set format QIC-150 */
+#define QIC_FMT300 0x2a /* set format QIC-300/QIC-2100 */
+#define QIC_FMT600 0x2b /* set format QIC-600/QIC-2200 */
+
+/* tape driver flags */
+#define TPINUSE 0x0001 /* tape is already open */
+#define TPREAD 0x0002 /* tape is only open for reading */
+#define TPWRITE 0x0004 /* tape is only open for writing */
+#define TPSTART 0x0008 /* tape must be rewound and reset */
+#define TPRMARK 0x0010 /* read file mark command outstanding */
+#define TPWMARK 0x0020 /* write file mark command outstanding */
+#define TPREW 0x0040 /* rewind command outstanding */
+#define TPEXCEP 0x0080 /* i/o exception flag */
+#define TPVOL 0x0100 /* read file mark or hit end of tape */
+#define TPWO 0x0200 /* write command outstanding */
+#define TPRO 0x0400 /* read command outstanding */
+#define TPWANY 0x0800 /* write command requested */
+#define TPRANY 0x1000 /* read command requested */
+#define TPWP 0x2000 /* write protect error seen */
+#define TPTIMER 0x4000 /* timer() is active */
+#define TPACTIVE 0x8000 /* dma i/o active */
+
+/* controller error register bits */
+#define TP_FIL 0x0001 /* File mark detected */
+#define TP_BNL 0x0002 /* Block not located */
+#define TP_UDA 0x0004 /* Unrecoverable data error */
+#define TP_EOM 0x0008 /* End of media */
+#define TP_WRP 0x0010 /* Write protected cartridge */
+#define TP_USL 0x0020 /* Unselected drive */
+#define TP_CNI 0x0040 /* Cartridge not in place */
+#define TP_ST0 0x0080 /* Status byte 0 bits */
+#define TP_ST0MASK 0x00ff /* Status byte 0 mask */
+#define TP_POR 0x0100 /* Power on/reset occurred */
+#define TP_ERM 0x0200 /* Reserved for end of recorded media */
+#define TP_BPE 0x0400 /* Reserved for bus parity error */
+#define TP_BOM 0x0800 /* Beginning of media */
+#define TP_MBD 0x1000 /* Marginal block detected */
+#define TP_NDT 0x2000 /* No data detected */
+#define TP_ILL 0x4000 /* Illegal command - should not happen! */
+#define TP_ST1 0x8000 /* Status byte 1 bits */
+#define TP_ST1MASK 0xff00 /* Status byte 1 mask */
+
+/* formats for printing flags and error values */
+#define WTDS_BITS "\20\1inuse\2read\3write\4start\5rmark\6wmark\7rew\10excep\11vol\12wo\13ro\14wany\15rany\16wp\17timer\20active"
+#define WTER_BITS "\20\1eof\2bnl\3uda\4eom\5wrp\6usl\7cni\11por\12erm\13bpe\14bom\15mbd\16ndt\17ill"
+
+/* device minor number */
+#define WT_BSIZE 0100 /* long block flag */
+#define WT_DENSEL 0070 /* density select mask */
+#define WT_DENSDFLT 0000 /* default density */
+#define WT_QIC11 0010 /* 11 megabytes? */
+#define WT_QIC24 0020 /* 60 megabytes */
+#define WT_QIC120 0030 /* 120 megabytes */
+#define WT_QIC150 0040 /* 150 megabytes */
+#define WT_QIC300 0050 /* 300 megabytes? */
+#define WT_QIC600 0060 /* 600 megabytes? */
diff --git a/sys/isa/atrtc.c b/sys/isa/atrtc.c
new file mode 100644
index 0000000..e40079a
--- /dev/null
+++ b/sys/isa/atrtc.c
@@ -0,0 +1,442 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz and Don Ahn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)clock.c 7.2 (Berkeley) 5/12/91
+ * $Id: clock.c,v 1.6 1994/02/06 22:48:13 davidg Exp $
+ */
+
+/*
+ * Primitive clock interrupt routines.
+ */
+#include "param.h"
+#include "systm.h"
+#include "time.h"
+#include "kernel.h"
+#include "machine/segments.h"
+#include "machine/frame.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/rtc.h"
+#include "i386/isa/timerreg.h"
+#include <machine/cpu.h>
+
+/* X-tals being what they are, it's nice to be able to fudge this one... */
+/* Note, the name changed here from XTALSPEED to TIMER_FREQ rgrimes 4/26/93 */
+#ifndef TIMER_FREQ
+#define TIMER_FREQ 1193182 /* XXX - should be in isa.h */
+#endif
+#define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x))
+
+void hardclock();
+static int beeping;
+int timer0_divisor = TIMER_DIV(100); /* XXX should be hz */
+u_int timer0_prescale;
+static char timer0_state = 0, timer2_state = 0;
+static char timer0_reprogram = 0;
+static void (*timer_func)() = hardclock;
+static void (*new_function)();
+static u_int new_rate;
+static u_int hardclock_divisor;
+
+
+void
+clkintr(frame)
+ struct clockframe frame;
+{
+ hardclock(&frame);
+}
+
+#if 0
+void
+timerintr(struct clockframe frame)
+{
+ timer_func(&frame);
+ switch (timer0_state) {
+ case 0:
+ break;
+ case 1:
+ if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) {
+ hardclock(&frame);
+ timer0_prescale = 0;
+ }
+ break;
+ case 2:
+ disable_intr();
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+ outb(TIMER_CNTR0, TIMER_DIV(new_rate)%256);
+ outb(TIMER_CNTR0, TIMER_DIV(new_rate)/256);
+ enable_intr();
+ timer0_divisor = TIMER_DIV(new_rate);
+ timer0_prescale = 0;
+ timer_func = new_function;
+ timer0_state = 1;
+ break;
+ case 3:
+ if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) {
+ hardclock(&frame);
+ disable_intr();
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+ outb(TIMER_CNTR0, TIMER_DIV(hz)%256);
+ outb(TIMER_CNTR0, TIMER_DIV(hz)/256);
+ enable_intr();
+ timer0_divisor = TIMER_DIV(hz);
+ timer0_prescale = 0;
+ timer_func = hardclock;;
+ timer0_state = 0;
+ }
+ break;
+ }
+}
+
+#endif
+
+int
+acquire_timer0(int rate, void (*function)() )
+{
+ if (timer0_state || !function)
+ return -1;
+
+ new_function = function;
+ new_rate = rate;
+ timer0_state = 2;
+ return 0;
+}
+
+
+int
+acquire_timer2(int mode)
+{
+ if (timer2_state)
+ return -1;
+ timer2_state = 1;
+ outb(TIMER_MODE, TIMER_SEL2 | (mode &0x3f));
+ return 0;
+}
+
+
+int
+release_timer0()
+{
+ if (!timer0_state)
+ return -1;
+ timer0_state = 3;
+ return 0;
+}
+
+
+int
+release_timer2()
+{
+ if (!timer2_state)
+ return -1;
+ timer2_state = 0;
+ outb(TIMER_MODE, TIMER_SEL2|TIMER_SQWAVE|TIMER_16BIT);
+ return 0;
+}
+
+
+static int
+getit()
+{
+ int high, low;
+
+ disable_intr();
+ /* select timer0 and latch counter value */
+ outb(TIMER_MODE, TIMER_SEL0);
+ low = inb(TIMER_CNTR0);
+ high = inb(TIMER_CNTR0);
+ enable_intr();
+ return ((high << 8) | low);
+}
+
+
+/*
+ * Wait "n" microseconds.
+ * Relies on timer 1 counting down from (TIMER_FREQ / hz)
+ * Note: timer had better have been programmed before this is first used!
+ */
+void
+DELAY(int n)
+{
+ int counter_limit, prev_tick, tick, ticks_left, sec, usec;
+
+#ifdef DELAYDEBUG
+ int getit_calls = 1;
+ int n1;
+ static int state = 0;
+
+ if (state == 0) {
+ state = 1;
+ for (n1 = 1; n1 <= 10000000; n1 *= 10)
+ DELAY(n1);
+ state = 2;
+ }
+ if (state == 1)
+ printf("DELAY(%d)...", n);
+#endif
+ /*
+ * Read the counter first, so that the rest of the setup overhead is
+ * counted. Guess the initial overhead is 20 usec (on most systems it
+ * takes about 1.5 usec for each of the i/o's in getit(). The loop
+ * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The
+ * multiplications and divisions to scale the count take a while).
+ */
+ prev_tick = getit(0, 0);
+ n -= 20;
+ /*
+ * Calculate (n * (TIMER_FREQ / 1e6)) without using floating point
+ * and without any avoidable overflows.
+ */
+ sec = n / 1000000;
+ usec = n - sec * 1000000;
+ ticks_left = sec * TIMER_FREQ
+ + usec * (TIMER_FREQ / 1000000)
+ + usec * ((TIMER_FREQ % 1000000) / 1000) / 1000
+ + usec * (TIMER_FREQ % 1000) / 1000000;
+
+ while (ticks_left > 0) {
+ tick = getit(0, 0);
+#ifdef DELAYDEBUG
+ ++getit_calls;
+#endif
+ if (tick > prev_tick)
+ ticks_left -= prev_tick - (tick - timer0_divisor);
+ else
+ ticks_left -= prev_tick - tick;
+ prev_tick = tick;
+ }
+#ifdef DELAYDEBUG
+ if (state == 1)
+ printf(" %d calls to getit() at %d usec each\n",
+ getit_calls, (n + 5) / getit_calls);
+#endif
+}
+
+
+static void
+sysbeepstop()
+{
+ outb(IO_PPI, inb(IO_PPI)&0xFC); /* disable counter2 output to speaker */
+ release_timer2();
+ beeping = 0;
+}
+
+
+int
+sysbeep(int pitch, int period)
+{
+
+ if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT))
+ return -1;
+ disable_intr();
+ outb(TIMER_CNTR2, pitch);
+ outb(TIMER_CNTR2, (pitch>>8));
+ enable_intr();
+ if (!beeping) {
+ outb(IO_PPI, inb(IO_PPI) | 3); /* enable counter2 output to speaker */
+ beeping = period;
+ timeout(sysbeepstop, 0, period);
+ }
+ return 0;
+}
+
+
+void
+startrtclock()
+{
+ int s;
+
+ /* initialize 8253 clock */
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+
+ /* Correct rounding will buy us a better precision in timekeeping */
+ outb (IO_TIMER1, TIMER_DIV(hz)%256);
+ outb (IO_TIMER1, TIMER_DIV(hz)/256);
+ timer0_divisor = hardclock_divisor = TIMER_DIV(hz);
+
+ /* initialize brain-dead battery powered clock */
+ outb (IO_RTC, RTC_STATUSA);
+ outb (IO_RTC+1, 0x26);
+ outb (IO_RTC, RTC_STATUSB);
+ outb (IO_RTC+1, 2);
+
+ outb (IO_RTC, RTC_DIAG);
+ if (s = inb (IO_RTC+1))
+ printf("RTC BIOS diagnostic error %b\n", s, RTCDG_BITS);
+}
+
+
+/* convert 2 digit BCD number */
+int
+bcd(int i)
+{
+ return ((i/16)*10 + (i%16));
+}
+
+
+/* convert years to seconds (from 1970) */
+unsigned long
+ytos(int y)
+{
+ int i;
+ unsigned long ret;
+
+ ret = 0;
+ for(i = 1970; i < y; i++) {
+ if (i % 4) ret += 365*24*60*60;
+ else ret += 366*24*60*60;
+ }
+ return ret;
+}
+
+
+/* convert months to seconds */
+unsigned long
+mtos(int m, int leap)
+{
+ int i;
+ unsigned long ret;
+
+ ret = 0;
+ for(i=1; i<m; i++) {
+ switch(i){
+ case 1: case 3: case 5: case 7: case 8: case 10: case 12:
+ ret += 31*24*60*60; break;
+ case 4: case 6: case 9: case 11:
+ ret += 30*24*60*60; break;
+ case 2:
+ if (leap) ret += 29*24*60*60;
+ else ret += 28*24*60*60;
+ }
+ }
+ return ret;
+}
+
+
+/*
+ * Initialize the time of day register, based on the time base which is, e.g.
+ * from a filesystem.
+ */
+void
+inittodr(time_t base)
+{
+ unsigned long sec;
+ int leap, day_week, t, yd;
+ int sa,s;
+
+ /* do we have a realtime clock present? (otherwise we loop below) */
+ sa = rtcin(RTC_STATUSA);
+ if (sa == 0xff || sa == 0) return;
+
+ /* ready for a read? */
+ while ((sa&RTCSA_TUP) == RTCSA_TUP)
+ sa = rtcin(RTC_STATUSA);
+
+ sec = bcd(rtcin(RTC_YEAR)) + 1900;
+ if (sec < 1970)
+ sec += 100;
+
+ leap = !(sec % 4); sec = ytos(sec); /* year */
+ yd = mtos(bcd(rtcin(RTC_MONTH)),leap); sec+=yd; /* month */
+ t = (bcd(rtcin(RTC_DAY))-1) * 24*60*60; sec+=t; yd+=t; /* date */
+ day_week = rtcin(RTC_WDAY); /* day */
+ sec += bcd(rtcin(RTC_HRS)) * 60*60; /* hour */
+ sec += bcd(rtcin(RTC_MIN)) * 60; /* minutes */
+ sec += bcd(rtcin(RTC_SEC)); /* seconds */
+ sec += tz.tz_minuteswest * 60;
+ time.tv_sec = sec;
+}
+
+
+#ifdef garbage
+/*
+ * Initialze the time of day register, based on the time base which is, e.g.
+ * from a filesystem.
+ */
+test_inittodr(time_t base)
+{
+
+ outb(IO_RTC,9); /* year */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,8); /* month */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,7); /* day */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,4); /* hour */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,2); /* minutes */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,0); /* seconds */
+ printf("%d\n",bcd(inb(IO_RTC+1)));
+
+ time.tv_sec = base;
+}
+#endif
+
+/*
+ * Wire clock interrupt in.
+ */
+#define V(s) __CONCAT(V, s)
+extern void V(clk)();
+
+
+void
+enablertclock()
+{
+ setidt(ICU_OFFSET+0, &V(clk), SDT_SYS386IGT, SEL_KPL);
+ INTREN(IRQ0);
+}
+
+
+/*
+ * Delay for some number of milliseconds.
+ */
+void
+spinwait(int millisecs)
+{
+ DELAY(1000 * millisecs);
+}
+
+void
+cpu_initclocks()
+{
+ startrtclock();
+ enablertclock();
+}
+
+void
+setstatclockrate(int newhz)
+{
+}
diff --git a/sys/isa/fd.c b/sys/isa/fd.c
new file mode 100644
index 0000000..259d451
--- /dev/null
+++ b/sys/isa/fd.c
@@ -0,0 +1,1255 @@
+/*#define DEBUG 1*/
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Don Ahn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)fd.c 7.4 (Berkeley) 5/25/91
+ * $Id: fd.c,v 1.24 1994/03/08 16:25:29 nate Exp $
+ *
+ */
+
+#include "ft.h"
+#if NFT < 1
+#undef NFDC
+#endif
+#include "fd.h"
+
+#if NFDC > 0
+
+#include <sys/param.h>
+#include <sys/dkbad.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <machine/ioctl_fd.h>
+#include <sys/disklabel.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/syslog.h>
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/fdreg.h"
+#include "i386/isa/fdc.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/rtc.h"
+
+#if NFT > 0
+extern int ftopen(), ftintr(), ftattach(), ftclose(), ftioctl();
+#endif
+
+#define b_cylin b_resid
+#define FDBLK 512
+
+/* misuse a flag to identify format operation */
+#define B_FORMAT B_XXX
+
+#define NUMTYPES 14
+#define NUMDENS (NUMTYPES - 6)
+
+/* This defines (-1) must match index for fd_types */
+#define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */
+#define NO_TYPE 0 /* must match NO_TYPE in ft.c */
+#define FD_1720 1
+#define FD_1480 2
+#define FD_1440 3
+#define FD_1200 4
+#define FD_820 5
+#define FD_800 6
+#define FD_720 7
+#define FD_360 8
+
+#define FD_1480in5_25 9
+#define FD_1440in5_25 10
+#define FD_820in5_25 11
+#define FD_800in5_25 12
+#define FD_720in5_25 13
+#define FD_360in5_25 14
+
+
+struct fd_type fd_types[NUMTYPES] =
+{
+{ 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
+{ 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
+{ 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
+{ 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */
+{ 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */
+{ 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */
+{ 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */
+{ 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */
+
+{ 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
+{ 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
+{ 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */
+{ 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */
+{ 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */
+{ 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */
+};
+
+#define DRVS_PER_CTLR 2 /* 2 floppies */
+/***********************************************************************\
+* Per controller structure. *
+\***********************************************************************/
+struct fdc_data fdc_data[NFDC];
+
+/***********************************************************************\
+* Per drive structure. *
+* N per controller (DRVS_PER_CTLR) *
+\***********************************************************************/
+struct fd_data {
+ struct fdc_data *fdc; /* pointer to controller structure */
+ int fdsu; /* this units number on this controller */
+ int type; /* Drive type (HD, DD */
+ struct fd_type *ft; /* pointer to the type descriptor */
+ int flags;
+#define FD_OPEN 0x01 /* it's open */
+#define FD_ACTIVE 0x02 /* it's active */
+#define FD_MOTOR 0x04 /* motor should be on */
+#define FD_MOTOR_WAIT 0x08 /* motor coming up */
+ int skip;
+ int hddrv;
+ int track; /* where we think the head is */
+} fd_data[NFD];
+
+/***********************************************************************\
+* Throughout this file the following conventions will be used: *
+* fd is a pointer to the fd_data struct for the drive in question *
+* fdc is a pointer to the fdc_data struct for the controller *
+* fdu is the floppy drive unit number *
+* fdcu is the floppy controller unit number *
+* fdsu is the floppy drive unit number on that controller. (sub-unit) *
+\***********************************************************************/
+
+#define id_physid id_scsiid /* this biotab field doubles as a field */
+ /* for the physical unit number on the controller */
+
+static int retrier(fdcu_t);
+
+#define DEVIDLE 0
+#define FINDWORK 1
+#define DOSEEK 2
+#define SEEKCOMPLETE 3
+#define IOCOMPLETE 4
+#define RECALCOMPLETE 5
+#define STARTRECAL 6
+#define RESETCTLR 7
+#define SEEKWAIT 8
+#define RECALWAIT 9
+#define MOTORWAIT 10
+#define IOTIMEDOUT 11
+
+#ifdef DEBUG
+char *fdstates[] =
+{
+"DEVIDLE",
+"FINDWORK",
+"DOSEEK",
+"SEEKCOMPLETE",
+"IOCOMPLETE",
+"RECALCOMPLETE",
+"STARTRECAL",
+"RESETCTLR",
+"SEEKWAIT",
+"RECALWAIT",
+"MOTORWAIT",
+"IOTIMEDOUT"
+};
+
+
+int fd_debug = 1;
+#define TRACE0(arg) if(fd_debug) printf(arg)
+#define TRACE1(arg1,arg2) if(fd_debug) printf(arg1,arg2)
+#else /* DEBUG */
+#define TRACE0(arg)
+#define TRACE1(arg1,arg2)
+#endif /* DEBUG */
+
+static void fdstart(fdcu_t);
+void fdintr(fdcu_t);
+static void fd_turnoff(caddr_t);
+
+/****************************************************************************/
+/* autoconfiguration stuff */
+/****************************************************************************/
+static int fdprobe(struct isa_device *);
+static int fdattach(struct isa_device *);
+
+struct isa_driver fdcdriver = {
+ fdprobe, fdattach, "fdc",
+};
+
+/*
+ * probe for existance of controller
+ */
+int
+fdprobe(dev)
+ struct isa_device *dev;
+{
+ fdcu_t fdcu = dev->id_unit;
+ if(fdc_data[fdcu].flags & FDC_ATTACHED)
+ {
+ printf("fdc: same unit (%d) used multiple times\n",fdcu);
+ return 0;
+ }
+
+ fdc_data[fdcu].baseport = dev->id_iobase;
+
+ /* First - lets reset the floppy controller */
+
+ outb(dev->id_iobase+fdout,0);
+ DELAY(100);
+ outb(dev->id_iobase+fdout,FDO_FRST);
+
+ /* see if it can handle a command */
+ if (out_fdc(fdcu,NE7CMD_SPECIFY) < 0)
+ {
+ return(0);
+ }
+ out_fdc(fdcu,0xDF);
+ out_fdc(fdcu,2);
+ return (IO_FDCSIZE);
+}
+
+/*
+ * wire controller into system, look for floppy units
+ */
+int
+fdattach(dev)
+ struct isa_device *dev;
+{
+ unsigned fdt,st0, cyl;
+ int hdr;
+ fdu_t fdu;
+ fdcu_t fdcu = dev->id_unit;
+ fdc_p fdc = fdc_data + fdcu;
+ fd_p fd;
+ int fdsu;
+ struct isa_device *fdup;
+
+ fdc->fdcu = fdcu;
+ fdc->flags |= FDC_ATTACHED;
+ fdc->dmachan = dev->id_drq;
+ fdc->state = DEVIDLE;
+ hdr = 0;
+ printf("fdc%d:", fdcu);
+
+ /* check for each floppy drive */
+ for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) {
+ if (fdup->id_iobase != dev->id_iobase)
+ continue;
+ fdu = fdup->id_unit;
+ fd = &fd_data[fdu];
+ if (fdu >= (NFD+NFT))
+ continue;
+ fdsu = fdup->id_physid;
+ /* look up what bios thinks we have */
+ switch (fdu) {
+ case 0: fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
+ break;
+ case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
+ break;
+ default: fdt = RTCFDT_NONE;
+ break;
+ }
+ /* is there a unit? */
+ if ((fdt == RTCFDT_NONE)
+#if NFT > 0
+ || (fdsu >= DRVS_PER_CTLR)) {
+#else
+ ) {
+ fd->type = NO_TYPE;
+#endif
+#if NFT > 0
+ /* If BIOS says no floppy, or > 2nd device */
+ /* Probe for and attach a floppy tape. */
+ if (ftattach(dev, fdup))
+ continue;
+ if (fdsu < DRVS_PER_CTLR)
+ fd->type = NO_TYPE;
+#endif
+ continue;
+ }
+
+#ifdef notyet
+ /* select it */
+ fd_turnon1(fdu);
+ spinwait(1000); /* 1 sec */
+ out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */
+ out_fdc(fdcu,fdsu);
+ spinwait(1000); /* 1 sec */
+
+ /* anything responding */
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ if (st0 & 0xd0)
+ continue;
+
+#endif
+ fd->track = -2;
+ fd->fdc = fdc;
+ fd->fdsu = fdsu;
+ printf(" [%d: fd%d: ", fdsu, fdu);
+
+ switch (fdt) {
+ case RTCFDT_12M:
+ printf("1.2MB 5.25in]");
+ fd->type = FD_1200;
+ break;
+ case RTCFDT_144M:
+ printf("1.44MB 3.5in]");
+ fd->type = FD_1440;
+ break;
+ case RTCFDT_360K:
+ printf("360KB 5.25in]");
+ fd->type = FD_360;
+ break;
+ case RTCFDT_720K:
+ printf("720KB 3.5in]");
+ fd->type = FD_720;
+ break;
+ default:
+ printf("unknown]");
+ fd->type = NO_TYPE;
+ break;
+ }
+
+ fd_turnoff((caddr_t)fdu);
+ hdr = 1;
+ }
+ printf("\n");
+
+ /* Set transfer to 500kbps */
+ outb(fdc->baseport+fdctl,0); /*XXX*/
+ return 1;
+}
+
+int
+fdsize(dev)
+ dev_t dev;
+{
+ return(0);
+}
+
+/****************************************************************************/
+/* fdstrategy */
+/****************************************************************************/
+void fdstrategy(struct buf *bp)
+{
+ register struct buf *dp,*dp0,*dp1;
+ long nblocks,blknum;
+ int s;
+ fdcu_t fdcu;
+ fdu_t fdu;
+ fdc_p fdc;
+ fd_p fd;
+
+ fdu = FDUNIT(minor(bp->b_dev));
+ fd = &fd_data[fdu];
+ fdc = fd->fdc;
+ fdcu = fdc->fdcu;
+
+#if NFT > 0
+ /* check for controller already busy with tape */
+ if (fdc->flags & FDC_TAPE_BUSY) {
+ bp->b_error = EBUSY;
+ bp->b_flags |= B_ERROR;
+ return;
+ }
+#endif
+ if ((fdu >= NFD) || (bp->b_blkno < 0)) {
+ printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n",
+ fdu, bp->b_blkno, bp->b_bcount);
+ pg("fd:error in fdstrategy");
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ goto bad;
+ }
+ /*
+ * Set up block calculations.
+ */
+ blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK;
+ nblocks = fd->ft->size;
+ if (blknum + (bp->b_bcount / FDBLK) > nblocks) {
+ if (blknum == nblocks) {
+ bp->b_resid = bp->b_bcount;
+ } else {
+ bp->b_error = ENOSPC;
+ bp->b_flags |= B_ERROR;
+ }
+ goto bad;
+ }
+ bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads);
+ bp->b_pblkno = bp->b_blkno;
+ dp = &(fdc->head);
+ s = splbio();
+ disksort(dp, bp);
+ untimeout((timeout_func_t)fd_turnoff, (caddr_t)fdu); /* a good idea */
+ fdstart(fdcu);
+ splx(s);
+ return;
+
+bad:
+ biodone(bp);
+ return;
+}
+
+/****************************************************************************/
+/* motor control stuff */
+/* remember to not deselect the drive we're working on */
+/****************************************************************************/
+void
+set_motor(fdcu, fdu, reset)
+ fdcu_t fdcu;
+ fdu_t fdu;
+ int reset;
+{
+ int m0,m1;
+ int selunit;
+ fd_p fd;
+ if(fd = fdc_data[fdcu].fd)/* yes an assign! */
+ {
+ selunit = fd->fdsu;
+ }
+ else
+ {
+ selunit = 0;
+ }
+ m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR;
+ m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR;
+ outb(fdc_data[fdcu].baseport+fdout,
+ selunit
+ | (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
+ | (m0 ? FDO_MOEN0 : 0)
+ | (m1 ? FDO_MOEN1 : 0));
+ TRACE1("[0x%x->fdout]",(
+ selunit
+ | (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
+ | (m0 ? FDO_MOEN0 : 0)
+ | (m1 ? FDO_MOEN1 : 0)));
+}
+
+static void
+fd_turnoff(caddr_t arg1)
+{
+ fdu_t fdu = (fdu_t)arg1;
+ int s;
+
+ fd_p fd = fd_data + fdu;
+ s = splbio();
+ fd->flags &= ~FD_MOTOR;
+ set_motor(fd->fdc->fdcu,fd->fdsu,0);
+ splx(s);
+}
+
+void
+fd_motor_on(caddr_t arg1)
+{
+ fdu_t fdu = (fdu_t)arg1;
+ int s;
+
+ fd_p fd = fd_data + fdu;
+ s = splbio();
+ fd->flags &= ~FD_MOTOR_WAIT;
+ if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
+ {
+ fdintr(fd->fdc->fdcu);
+ }
+ splx(s);
+}
+
+static void fd_turnon1(fdu_t);
+
+void
+fd_turnon(fdu)
+ fdu_t fdu;
+{
+ fd_p fd = fd_data + fdu;
+ if(!(fd->flags & FD_MOTOR))
+ {
+ fd_turnon1(fdu);
+ fd->flags |= FD_MOTOR_WAIT;
+ timeout((timeout_func_t)fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */
+ }
+}
+
+static void
+fd_turnon1(fdu_t fdu)
+{
+ fd_p fd = fd_data + fdu;
+ fd->flags |= FD_MOTOR;
+ set_motor(fd->fdc->fdcu,fd->fdsu,0);
+}
+
+/****************************************************************************/
+/* fdc in/out */
+/****************************************************************************/
+int
+in_fdc(fdcu)
+ fdcu_t fdcu;
+{
+ int baseport = fdc_data[fdcu].baseport;
+ int i, j = 100000;
+ while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM))
+ != (NE7_DIO|NE7_RQM) && j-- > 0)
+ if (i == NE7_RQM) return -1;
+ if (j <= 0)
+ return(-1);
+#ifdef DEBUG
+ i = inb(baseport+fddata);
+ TRACE1("[fddata->0x%x]",(unsigned char)i);
+ return(i);
+#else
+ return inb(baseport+fddata);
+#endif
+}
+
+int
+out_fdc(fdcu, x)
+ fdcu_t fdcu;
+ int x;
+{
+ int baseport = fdc_data[fdcu].baseport;
+ int i;
+
+ /* Check that the direction bit is set */
+ i = 100000;
+ while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0);
+ if (i <= 0) return (-1); /* Floppy timed out */
+
+ /* Check that the floppy controller is ready for a command */
+ i = 100000;
+ while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0);
+ if (i <= 0) return (-1); /* Floppy timed out */
+
+ /* Send the command and return */
+ outb(baseport+fddata,x);
+ TRACE1("[0x%x->fddata]",x);
+ return (0);
+}
+
+/****************************************************************************/
+/* fdopen/fdclose */
+/****************************************************************************/
+int
+Fdopen(dev, flags)
+ dev_t dev;
+ int flags;
+{
+ fdu_t fdu = FDUNIT(minor(dev));
+ int type = FDTYPE(minor(dev));
+ fdc_p fdc;
+
+#if NFT > 0
+ /* check for a tape open */
+ if (type & F_TAPE_TYPE)
+ return(ftopen(dev, flags));
+#endif
+ /* check bounds */
+ if (fdu >= NFD)
+ return(ENXIO);
+ fdc = fd_data[fdu].fdc;
+ if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE))
+ return(ENXIO);
+ if (type > NUMDENS)
+ return(ENXIO);
+ if (type == 0)
+ type = fd_data[fdu].type;
+ else {
+ if (type != fd_data[fdu].type) {
+ switch (fd_data[fdu].type) {
+ case FD_360:
+ return(ENXIO);
+ case FD_720:
+ if ( type != FD_820
+ && type != FD_800
+ )
+ return(ENXIO);
+ break;
+ case FD_1200:
+ switch (type) {
+ case FD_1480:
+ type = FD_1480in5_25;
+ break;
+ case FD_1440:
+ type = FD_1440in5_25;
+ break;
+ case FD_820:
+ type = FD_820in5_25;
+ break;
+ case FD_800:
+ type = FD_800in5_25;
+ break;
+ case FD_720:
+ type = FD_720in5_25;
+ break;
+ case FD_360:
+ type = FD_360in5_25;
+ break;
+ default:
+ return(ENXIO);
+ }
+ break;
+ case FD_1440:
+ if ( type != FD_1720
+ && type != FD_1480
+ && type != FD_1200
+ && type != FD_820
+ && type != FD_800
+ && type != FD_720
+ )
+ return(ENXIO);
+ break;
+ }
+ }
+ }
+ fd_data[fdu].ft = fd_types + type - 1;
+ fd_data[fdu].flags |= FD_OPEN;
+
+ return 0;
+}
+
+int
+fdclose(dev, flags)
+ dev_t dev;
+ int flags;
+{
+ fdu_t fdu = FDUNIT(minor(dev));
+ int type = FDTYPE(minor(dev));
+
+#if NFT > 0
+ if (type & F_TAPE_TYPE)
+ return ftclose(0);
+#endif
+ fd_data[fdu].flags &= ~FD_OPEN;
+ return(0);
+}
+
+
+/***************************************************************\
+* fdstart *
+* We have just queued something.. if the controller is not busy *
+* then simulate the case where it has just finished a command *
+* So that it (the interrupt routine) looks on the queue for more*
+* work to do and picks up what we just added. *
+* If the controller is already busy, we need do nothing, as it *
+* will pick up our work when the present work completes *
+\***************************************************************/
+static void
+fdstart(fdcu)
+ fdcu_t fdcu;
+{
+ register struct buf *dp,*bp;
+ int s;
+ fdu_t fdu;
+
+ s = splbio();
+ if(fdc_data[fdcu].state == DEVIDLE)
+ {
+ fdintr(fdcu);
+ }
+ splx(s);
+}
+
+static void
+fd_timeout(caddr_t arg1)
+{
+ fdcu_t fdcu = (fdcu_t)arg1;
+ fdu_t fdu = fdc_data[fdcu].fdu;
+ int st0, st3, cyl;
+ struct buf *dp,*bp;
+ int s;
+
+ dp = &fdc_data[fdcu].head;
+ s = splbio();
+ bp = dp->b_actf;
+
+ out_fdc(fdcu,NE7CMD_SENSED);
+ out_fdc(fdcu,fd_data[fdu].hddrv);
+ st3 = in_fdc(fdcu);
+
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n",
+ fdu,
+ st0,
+ NE7_ST0BITS,
+ cyl,
+ st3,
+ NE7_ST3BITS);
+
+ if (bp)
+ {
+ retrier(fdcu);
+ fdc_data[fdcu].status[0] = 0xc0;
+ fdc_data[fdcu].state = IOTIMEDOUT;
+ if( fdc_data[fdcu].retry < 6)
+ fdc_data[fdcu].retry = 6;
+ }
+ else
+ {
+ fdc_data[fdcu].fd = (fd_p) 0;
+ fdc_data[fdcu].fdu = -1;
+ fdc_data[fdcu].state = DEVIDLE;
+ }
+ fdintr(fdcu);
+ splx(s);
+}
+
+/* just ensure it has the right spl */
+static void
+fd_pseudointr(caddr_t arg1, int arg2)
+{
+ fdcu_t fdcu = (fdcu_t)arg1;
+ int s;
+ s = splbio();
+ fdintr(fdcu);
+ splx(s);
+}
+
+/***********************************************************************\
+* fdintr *
+* keep calling the state machine until it returns a 0 *
+* ALWAYS called at SPLBIO *
+\***********************************************************************/
+void
+fdintr(fdcu_t fdcu)
+{
+ fdc_p fdc = fdc_data + fdcu;
+#if NFT > 0
+ fdu_t fdu = fdc->fdu;
+
+ if (fdc->flags & FDC_TAPE_BUSY)
+ (ftintr(fdu));
+ else
+#endif
+ while(fdstate(fdcu, fdc))
+ ;
+}
+
+/***********************************************************************\
+* The controller state machine. *
+* if it returns a non zero value, it should be called again immediatly *
+\***********************************************************************/
+int
+fdstate(fdcu, fdc)
+ fdcu_t fdcu;
+ fdc_p fdc;
+{
+ int read, format, head, trac, sec = 0, i = 0, s, sectrac, cyl, st0;
+ unsigned long blknum;
+ fdu_t fdu = fdc->fdu;
+ fd_p fd;
+ register struct buf *dp,*bp;
+ struct fd_formb *finfo = NULL;
+
+ dp = &(fdc->head);
+ bp = dp->b_actf;
+ if(!bp)
+ {
+ /***********************************************\
+ * nothing left for this controller to do *
+ * Force into the IDLE state, *
+ \***********************************************/
+ fdc->state = DEVIDLE;
+ if(fdc->fd)
+ {
+ printf("unexpected valid fd pointer (fdu = %d)\n"
+ ,fdc->fdu);
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ }
+ TRACE1("[fdc%d IDLE]",fdcu);
+ return(0);
+ }
+ fdu = FDUNIT(minor(bp->b_dev));
+ fd = fd_data + fdu;
+ if (fdc->fd && (fd != fdc->fd))
+ {
+ printf("confused fd pointers\n");
+ }
+ read = bp->b_flags & B_READ;
+ format = bp->b_flags & B_FORMAT;
+ if(format)
+ finfo = (struct fd_formb *)bp->b_un.b_addr;
+ TRACE1("fd%d",fdu);
+ TRACE1("[%s]",fdstates[fdc->state]);
+ TRACE1("(0x%x)",fd->flags);
+ untimeout((timeout_func_t)fd_turnoff, (caddr_t)fdu);
+ timeout((timeout_func_t)fd_turnoff, (caddr_t)fdu, 4 * hz);
+ switch (fdc->state)
+ {
+ case DEVIDLE:
+ case FINDWORK: /* we have found new work */
+ fdc->retry = 0;
+ fd->skip = 0;
+ fdc->fd = fd;
+ fdc->fdu = fdu;
+ outb(fdc->baseport+fdctl, fd->ft->trans);
+ /*******************************************************\
+ * If the next drive has a motor startup pending, then *
+ * it will start up in it's own good time *
+ \*******************************************************/
+ if(fd->flags & FD_MOTOR_WAIT)
+ {
+ fdc->state = MOTORWAIT;
+ return(0); /* come back later */
+ }
+ /*******************************************************\
+ * Maybe if it's not starting, it SHOULD be starting *
+ \*******************************************************/
+ if (!(fd->flags & FD_MOTOR))
+ {
+ fdc->state = MOTORWAIT;
+ fd_turnon(fdu);
+ return(0);
+ }
+ else /* at least make sure we are selected */
+ {
+ set_motor(fdcu,fd->fdsu,0);
+ }
+ fdc->state = DOSEEK;
+ break;
+ case DOSEEK:
+ if (bp->b_cylin == fd->track)
+ {
+ fdc->state = SEEKCOMPLETE;
+ break;
+ }
+ out_fdc(fdcu,NE7CMD_SEEK); /* Seek function */
+ out_fdc(fdcu,fd->fdsu); /* Drive number */
+ out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac);
+ fd->track = -2;
+ fdc->state = SEEKWAIT;
+ timeout((timeout_func_t)fd_timeout, (caddr_t)fdcu, 2 * hz);
+ return(0); /* will return later */
+ case SEEKWAIT:
+ untimeout((timeout_func_t)fd_timeout, (caddr_t)fdcu);
+ /* allow heads to settle */
+ timeout((timeout_func_t)fd_pseudointr, (caddr_t)fdcu, hz / 50);
+ fdc->state = SEEKCOMPLETE;
+ return(0); /* will return later */
+ break;
+
+ case SEEKCOMPLETE : /* SEEK DONE, START DMA */
+ /* Make sure seek really happened*/
+ if(fd->track == -2)
+ {
+ int descyl = bp->b_cylin * fd->ft->steptrac;
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ i = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ if (cyl != descyl)
+ {
+ printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
+ fdu, descyl, cyl, i, NE7_ST0BITS);
+ return(retrier(fdcu));
+ }
+ }
+
+ fd->track = bp->b_cylin;
+ if(format)
+ fd->skip = (char *)&(finfo->fd_formb_cylno(0))
+ - (char *)finfo;
+ isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip,
+ format ? bp->b_bcount : FDBLK, fdc->dmachan);
+ blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
+ + fd->skip/FDBLK;
+ sectrac = fd->ft->sectrac;
+ sec = blknum % (sectrac * fd->ft->heads);
+ head = sec / sectrac;
+ sec = sec % sectrac + 1;
+/*XXX*/ fd->hddrv = ((head&1)<<2)+fdu;
+
+ if(format)
+ {
+ /* formatting */
+ out_fdc(fdcu,/* NE7CMD_FORMAT */ 0x4d);
+ out_fdc(fdcu,head << 2 | fdu);
+ out_fdc(fdcu,finfo->fd_formb_secshift);
+ out_fdc(fdcu,finfo->fd_formb_nsecs);
+ out_fdc(fdcu,finfo->fd_formb_gaplen);
+ out_fdc(fdcu,finfo->fd_formb_fillbyte);
+ }
+ else
+ {
+ if (read)
+ {
+ out_fdc(fdcu,NE7CMD_READ); /* READ */
+ }
+ else
+ {
+ out_fdc(fdcu,NE7CMD_WRITE); /* WRITE */
+ }
+ out_fdc(fdcu,head << 2 | fdu); /* head & unit */
+ out_fdc(fdcu,fd->track); /* track */
+ out_fdc(fdcu,head);
+ out_fdc(fdcu,sec); /* sector XXX +1? */
+ out_fdc(fdcu,fd->ft->secsize); /* sector size */
+ out_fdc(fdcu,sectrac); /* sectors/track */
+ out_fdc(fdcu,fd->ft->gap); /* gap size */
+ out_fdc(fdcu,fd->ft->datalen); /* data length */
+ }
+ fdc->state = IOCOMPLETE;
+ timeout((timeout_func_t)fd_timeout, (caddr_t)fdcu, 2 * hz);
+ return(0); /* will return later */
+ case IOCOMPLETE: /* IO DONE, post-analyze */
+ untimeout((timeout_func_t)fd_timeout, (caddr_t)fdcu);
+ for(i=0;i<7;i++)
+ {
+ fdc->status[i] = in_fdc(fdcu);
+ }
+ case IOTIMEDOUT: /*XXX*/
+ isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip,
+ format ? bp->b_bcount : FDBLK, fdc->dmachan);
+ if (fdc->status[0]&0xF8)
+ {
+ if (fdc->status[1] & 0x10) {
+ /*
+ * Operation not completed in reasonable time.
+ * Just restart it, don't increment retry count.
+ * (vak)
+ */
+ fdc->state = SEEKCOMPLETE;
+ return (1);
+ }
+ return(retrier(fdcu));
+ }
+ /* All OK */
+ fd->skip += FDBLK;
+ if (!format && fd->skip < bp->b_bcount)
+ {
+ /* set up next transfer */
+ blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
+ + fd->skip/FDBLK;
+ bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads));
+ fdc->state = DOSEEK;
+ }
+ else
+ {
+ /* ALL DONE */
+ fd->skip = 0;
+ bp->b_resid = 0;
+ dp->b_actf = bp->b_actf;
+ biodone(bp);
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ fdc->state = FINDWORK;
+ }
+ return(1);
+ case RESETCTLR:
+ /* Try a reset, keep motor on */
+ set_motor(fdcu,fd->fdsu,1);
+ DELAY(100);
+ set_motor(fdcu,fd->fdsu,0);
+ outb(fdc->baseport+fdctl,fd->ft->trans);
+ TRACE1("[0x%x->fdctl]",fd->ft->trans);
+ fdc->retry++;
+ fdc->state = STARTRECAL;
+ break;
+ case STARTRECAL:
+ out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */
+ out_fdc(fdcu,0xDF);
+ out_fdc(fdcu,2);
+ out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */
+ out_fdc(fdcu,fdu);
+ fdc->state = RECALWAIT;
+ return(0); /* will return later */
+ case RECALWAIT:
+ /* allow heads to settle */
+ timeout((timeout_func_t)fd_pseudointr, (caddr_t)fdcu, hz / 30);
+ fdc->state = RECALCOMPLETE;
+ return(0); /* will return later */
+ case RECALCOMPLETE:
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ if (cyl != 0)
+ {
+ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu,
+ st0, NE7_ST0BITS, cyl);
+ return(retrier(fdcu));
+ }
+ fd->track = 0;
+ /* Seek (probably) necessary */
+ fdc->state = DOSEEK;
+ return(1); /* will return immediatly */
+ case MOTORWAIT:
+ if(fd->flags & FD_MOTOR_WAIT)
+ {
+ return(0); /* time's not up yet */
+ }
+ fdc->state = DOSEEK;
+ return(1); /* will return immediatly */
+ default:
+ printf("Unexpected FD int->");
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ printf("ST0 = %lx, PCN = %lx\n",i,sec);
+ out_fdc(fdcu,0x4A);
+ out_fdc(fdcu,fd->fdsu);
+ for(i=0;i<7;i++) {
+ fdc->status[i] = in_fdc(fdcu);
+ }
+ printf("intr status :%lx %lx %lx %lx %lx %lx %lx ",
+ fdc->status[0],
+ fdc->status[1],
+ fdc->status[2],
+ fdc->status[3],
+ fdc->status[4],
+ fdc->status[5],
+ fdc->status[6] );
+ return(0);
+ }
+ return(1); /* Come back immediatly to new state */
+}
+
+static int
+retrier(fdcu)
+ fdcu_t fdcu;
+{
+ fdc_p fdc = fdc_data + fdcu;
+ register struct buf *dp,*bp;
+
+ dp = &(fdc->head);
+ bp = dp->b_actf;
+
+ switch(fdc->retry)
+ {
+ case 0: case 1: case 2:
+ fdc->state = SEEKCOMPLETE;
+ break;
+ case 3: case 4: case 5:
+ fdc->state = STARTRECAL;
+ break;
+ case 6:
+ fdc->state = RESETCTLR;
+ break;
+ case 7:
+ break;
+ default:
+ {
+ dev_t sav_b_dev = bp->b_dev;
+ /* Trick diskerr */
+ bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|3);
+ diskerr(bp, "fd", "hard error", LOG_PRINTF,
+ fdc->fd->skip, (struct disklabel *)NULL);
+ bp->b_dev = sav_b_dev;
+ printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS);
+ printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS);
+ printf(" ST2 %b ", fdc->status[2], NE7_ST2BITS);
+ printf("cyl %d hd %d sec %d)\n",
+ fdc->status[3], fdc->status[4], fdc->status[5]);
+ }
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ bp->b_resid = bp->b_bcount - fdc->fd->skip;
+ dp->b_actf = bp->b_actf;
+ fdc->fd->skip = 0;
+ biodone(bp);
+ fdc->state = FINDWORK;
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ /* XXX abort current command, if any. */
+ return(1);
+ }
+ fdc->retry++;
+ return(1);
+}
+
+static int
+fdformat(dev, finfo, p)
+ dev_t dev;
+ struct fd_formb *finfo;
+ struct proc *p;
+{
+ fdu_t fdu;
+ fd_p fd;
+
+ struct buf *bp;
+ int rv = 0, s;
+
+ fdu = FDUNIT(minor(dev));
+ fd = &fd_data[fdu];
+
+ /* set up a buffer header for fdstrategy() */
+ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
+ if(bp == 0)
+ return ENOBUFS;
+ bzero((void *)bp, sizeof(struct buf));
+ bp->b_flags = B_BUSY | B_PHYS | B_FORMAT;
+ bp->b_proc = p;
+ bp->b_dev = dev;
+
+ /*
+ * calculate a fake blkno, so fdstrategy() would initiate a
+ * seek to the requested cylinder
+ */
+ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
+ + finfo->head * fd->ft->sectrac) * FDBLK / DEV_BSIZE;
+
+ bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
+ bp->b_un.b_addr = (caddr_t)finfo;
+
+ /* now do the format */
+ fdstrategy(bp);
+
+ /* ...and wait for it to complete */
+ s = splbio();
+ while(!(bp->b_flags & B_DONE))
+ {
+ rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
+ if(rv == EWOULDBLOCK)
+ break;
+ }
+ splx(s);
+
+ if(rv == EWOULDBLOCK)
+ {
+ /* timed out */
+ biodone(bp);
+ rv = EIO;
+ }
+ free(bp, M_TEMP);
+ return rv;
+}
+
+/*
+ * fdioctl() from jc@irbs.UUCP (John Capo)
+ * i386/i386/conf.c needs to have fdioctl() declared and remove the line that
+ * defines fdioctl to be enxio.
+ *
+ * TODO: Reformat.
+ * Think about allocating buffer off stack.
+ * Don't pass uncast 0's and NULL's to read/write/setdisklabel().
+ * Watch out for NetBSD's different *disklabel() interface.
+ *
+ * Added functionality for floppy formatting
+ * joerg_wunsch@uriah.sax.de (Joerg Wunsch)
+ */
+
+int
+fdioctl (dev, cmd, addr, flag, p)
+ dev_t dev;
+ int cmd;
+ caddr_t addr;
+ int flag;
+ struct proc *p;
+{
+ struct fd_type *fdt;
+ struct disklabel *dl;
+ char buffer[DEV_BSIZE];
+ int error;
+
+#if NFT > 0
+ int type = FDTYPE(minor(dev));
+
+ /* check for a tape ioctl */
+ if (type & F_TAPE_TYPE)
+ return ftioctl(dev, cmd, addr, flag, p);
+#endif
+
+ error = 0;
+
+ switch (cmd)
+ {
+ case DIOCGDINFO:
+ bzero(buffer, sizeof (buffer));
+ dl = (struct disklabel *)buffer;
+ dl->d_secsize = FDBLK;
+ fdt = fd_data[FDUNIT(minor(dev))].ft;
+ dl->d_secpercyl = fdt->size / fdt->tracks;
+ dl->d_type = DTYPE_FLOPPY;
+
+ if (readdisklabel(dev, fdstrategy, dl, NULL, 0, 0) == NULL)
+ error = 0;
+ else
+ error = EINVAL;
+
+ *(struct disklabel *)addr = *dl;
+ break;
+
+ case DIOCSDINFO:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ break;
+
+ case DIOCWLABEL:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ break;
+
+ case DIOCWDINFO:
+ if ((flag & FWRITE) == 0)
+ {
+ error = EBADF;
+ break;
+ }
+
+ dl = (struct disklabel *)addr;
+
+ if (error = setdisklabel ((struct disklabel *)buffer,
+ dl, 0, NULL))
+ break;
+
+ error = writedisklabel(dev, fdstrategy,
+ (struct disklabel *)buffer, NULL);
+ break;
+
+ case FD_FORM:
+ if((flag & FWRITE) == 0)
+ error = EBADF; /* must be opened for writing */
+ else if(((struct fd_formb *)addr)->format_version !=
+ FD_FORMAT_VERSION)
+ error = EINVAL; /* wrong version of formatting prog */
+ else
+ error = fdformat(dev, (struct fd_formb *)addr, p);
+ break;
+
+ case FD_GTYPE: /* get drive type */
+ *(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft;
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ return (error);
+}
+
+#endif
diff --git a/sys/isa/fdc.h b/sys/isa/fdc.h
new file mode 100644
index 0000000..1542f0e
--- /dev/null
+++ b/sys/isa/fdc.h
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)fd.c 7.4 (Berkeley) 5/25/91
+ * $Id:$
+ *
+ */
+
+
+/***********************************************************************\
+* Per controller structure. *
+\***********************************************************************/
+struct fdc_data
+{
+ int fdcu; /* our unit number */
+ int baseport;
+ int dmachan;
+ int flags;
+#define FDC_ATTACHED 0x01
+#define FDC_HASFTAPE 0x02
+#define FDC_TAPE_BUSY 0x04
+ struct fd_data *fd;
+ int fdu; /* the active drive */
+ struct buf head; /* Head of buf chain */
+ struct buf rhead; /* Raw head of buf chain */
+ int state;
+ int retry;
+ int status[7]; /* copy of the registers */
+};
+
+/***********************************************************************\
+* Throughout this file the following conventions will be used: *
+* fd is a pointer to the fd_data struct for the drive in question *
+* fdc is a pointer to the fdc_data struct for the controller *
+* fdu is the floppy drive unit number *
+* fdcu is the floppy controller unit number *
+* fdsu is the floppy drive unit number on that controller. (sub-unit) *
+\***********************************************************************/
+typedef int fdu_t;
+typedef int fdcu_t;
+typedef int fdsu_t;
+typedef struct fd_data *fd_p;
+typedef struct fdc_data *fdc_p;
+
+#define FDUNIT(s) (((s)>>6)&03)
+#define FDTYPE(s) ((s)&077)
diff --git a/sys/isa/fdreg.h b/sys/isa/fdreg.h
new file mode 100644
index 0000000..5deb02c
--- /dev/null
+++ b/sys/isa/fdreg.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)fdreg.h 7.1 (Berkeley) 5/9/91
+ * $Id: fdreg.h,v 1.3 1994/02/07 04:27:10 alm Exp $
+ */
+
+/*
+ * AT floppy controller registers and bitfields
+ */
+
+/* uses NEC765 controller */
+#include "../i386/isa/ic/nec765.h"
+
+/* registers */
+#define fdout 2 /* Digital Output Register (W) */
+#define FDO_FDSEL 0x03 /* floppy device select */
+#define FDO_FRST 0x04 /* floppy controller reset */
+#define FDO_FDMAEN 0x08 /* enable floppy DMA and Interrupt */
+#define FDO_MOEN0 0x10 /* motor enable drive 0 */
+#define FDO_MOEN1 0x20 /* motor enable drive 1 */
+#define FDO_MOEN2 0x30 /* motor enable drive 2 */
+#define FDO_MOEN3 0x40 /* motor enable drive 3 */
+
+#define fdsts 4 /* NEC 765 Main Status Register (R) */
+#define fddata 5 /* NEC 765 Data Register (R/W) */
+
+#define fdctl 7 /* Control Register (W) */
+#define FDC_500KBPS 0x00 /* 500KBPS MFM drive transfer rate */
+#define FDC_300KBPS 0x01 /* 300KBPS MFM drive transfer rate */
+#define FDC_250KBPS 0x02 /* 250KBPS MFM drive transfer rate */
+#define FDC_125KBPS 0x03 /* 125KBPS FM drive transfer rate */
+
+#define fdin 7 /* Digital Input Register (R) */
+#define FDI_DCHG 0x80 /* diskette has been changed */
+
diff --git a/sys/isa/ic/nec765.h b/sys/isa/ic/nec765.h
new file mode 100644
index 0000000..1895db7
--- /dev/null
+++ b/sys/isa/ic/nec765.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)nec765.h 7.1 (Berkeley) 5/9/91
+ * $Id$
+ */
+
+/*
+ * Nec 765 floppy disc controller definitions
+ */
+
+/* Main status register */
+#define NE7_DAB 0x01 /* Diskette drive A is seeking, thus busy */
+#define NE7_DBB 0x02 /* Diskette drive B is seeking, thus busy */
+#define NE7_CB 0x10 /* Diskette Controller Busy */
+#define NE7_NDM 0x20 /* Diskette Controller in Non Dma Mode */
+#define NE7_DIO 0x40 /* Diskette Controller Data register I/O */
+#define NE7_RQM 0x80 /* Diskette Controller ReQuest for Master */
+
+/* Status register ST0 */
+#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005drv_chck\004drive_rdy\003top_head"
+
+/* Status register ST1 */
+#define NE7_ST1BITS "\020\010end_of_cyl\006bad_crc\005data_overrun\003sec_not_fnd\002write_protect\001no_am"
+
+/* Status register ST2 */
+#define NE7_ST2BITS "\020\007ctrl_mrk\006bad_crc\005wrong_cyl\004scn_eq\003scn_not_fnd\002bad_cyl\001no_dam"
+
+/* Status register ST3 */
+#define NE7_ST3BITS "\020\010fault\007write_protect\006drdy\005tk0\004two_side\003side_sel\002"
+
+/* Commands */
+#define NE7CMD_SPECIFY 3 /* specify drive parameters - requires unit
+ parameters byte */
+#define NE7CMD_SENSED 4 /* sense drive - requires unit select byte */
+#define NE7CMD_WRITE 0xc5 /* write - requires eight additional bytes */
+#define NE7CMD_READ 0xe6 /* read - requires eight additional bytes */
+#define NE7CMD_FORMAT 0x4c /* format - requires five additional bytes */
+#define NE7CMD_RECAL 7 /* recalibrate drive - requires
+ unit select byte */
+#define NE7CMD_SENSEI 8 /* sense controller interrupt status */
+#define NE7CMD_SEEK 15 /* seek drive - requires unit select byte
+ and new cyl byte */
diff --git a/sys/isa/ic/ns16550.h b/sys/isa/ic/ns16550.h
new file mode 100644
index 0000000..ff59757
--- /dev/null
+++ b/sys/isa/ic/ns16550.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)ns16550.h 7.1 (Berkeley) 5/9/91
+ * $Id$
+ */
+
+/*
+ * NS16550 UART registers
+ */
+
+#define com_data 0 /* data register (R/W) */
+#define com_dlbl 0 /* divisor latch low (W) */
+#define com_dlbh 1 /* divisor latch high (W) */
+#define com_ier 1 /* interrupt enable (W) */
+#define com_iir 2 /* interrupt identification (R) */
+#define com_fifo 2 /* FIFO control (W) */
+#define com_lctl 3 /* line control register (R/W) */
+#define com_cfcr 3 /* line control register (R/W) */
+#define com_mcr 4 /* modem control register (R/W) */
+#define com_lsr 5 /* line status register (R/W) */
+#define com_msr 6 /* modem status register (R/W) */
diff --git a/sys/isa/kbdtables.h b/sys/isa/kbdtables.h
new file mode 100644
index 0000000..a923c45
--- /dev/null
+++ b/sys/isa/kbdtables.h
@@ -0,0 +1,859 @@
+/*
+ * Copyright (C) 1992, 1993, 1994 Søren Schmidt
+ *
+ * This program is free software; you may redistribute it and/or
+ * modify it, provided that it retain the above copyright notice
+ * and the following disclaimer.
+ *
+ * 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.
+ *
+ * Søren Schmidt Email: sos@login.dkuug.dk
+ * Tritonvej 36 UUCP: ...uunet!dkuug!login!sos
+ * DK9210 Aalborg SO Phone: +45 9814 8076
+ *
+ * @(#)kbdtables.h 1.3 940123
+ * $Id: kbdtables.h,v 1.11 1994/02/01 09:27:43 ache Exp $
+ */
+
+#define SET8 0x80 /* eight bit for emacs SET8-key */
+
+#ifdef DKKEYMAP
+keymap_t key_map = { 0x69, /* DK iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '"', 0x00, 0x00, '@', '"', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', '#', NOP, NOP, 0x9E, '#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', 0xA4, NOP, NOP, '$', 0xA4, NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '&', NOP, NOP, '6', '&', NOP, NOP, 0x33, 0x00,
+/* sc=08 */ '7', '/', NOP, NOP, '{', '/', NOP, NOP, 0x33, 0x00,
+/* sc=09 */ '8', '(', 0x1B, 0x1B, '[', '(', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ')', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=0b */ '0', '=', NOP, NOP, '}', '=', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '+', '?', NOP, NOP, '+', '?', NOP, NOP, 0x33, 0x00,
+/* sc=0d */ '\'', '`', NOP, NOP, '|', '`', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x00, 0x00,
+/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x33, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ 0xE5, 0xC5, NOP, NOP, 0x86, 0x8F, NOP, NOP, 0x33, 0x01,
+/* sc=1b */ '"', '^', 0x1E, 0x1E, '~', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ 0xE6, 0xC6, NOP, NOP, 0x91, 0x92, NOP, NOP, 0x33, 0x01,
+/* sc=28 */ 0xF8, 0xD8, NOP, NOP, 0x9B, 0x9D, NOP, NOP, 0x33, 0x01,
+/* sc=29 */ 0xBD, 0xA7, NOP, NOP, 0xBD, 0xA7, NOP, NOP, 0x33, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\'', '*', NOP, NOP, '\'', '*', NOP, NOP, 0x33, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00,
+/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', '-', '-', '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ NOP, '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02,
+/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ '<', '>', 0x1C, 0x1C, '\\', '>', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x00, 0x02,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x00,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+#endif
+
+#ifdef UKKEYMAP
+keymap_t key_map = { 0x69, /* uk iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, '`', '`', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', 0xA3, NOP, NOP, '#', '#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '^', 0x1E, 0x1E, '^', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=08 */ '7', '&', NOP, NOP, '[', '[', 0x1B, 0x1B, 0x30, 0x00,
+/* sc=09 */ '8', '*', NOP, NOP, '8', '*', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '9', '(', NOP, NOP, ']', ']', 0x1D, 0x1D, 0x30, 0x00,
+/* sc=0b */ '0', ')', NOP, NOP, '{', '{', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '-', '_', 0x1F, 0x1F, '|', '|', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=0d */ '=', '+', NOP, NOP, '}', '}', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=1b */ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ ';', ':', NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x00,
+/* sc=28 */ '\'', '@', 0x00, 0x00, '\'', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=29 */ '\\', '|', 0x1C, 0x1C, '\\', '\\', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '#', '~', NOP, NOP, '~', '~', NOP, NOP, 0x33, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', '<', NOP, NOP, ',', '<', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', '>', NOP, NOP, '.', '>', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '/', '?', NOP, NOP, '/', '?', NOP, NOP, 0x33, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00,
+/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02,
+/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+#endif
+
+#ifdef GRKEYMAP
+keymap_t key_map = { 0x69, /* german iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, '`', '`', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', 0xA7, NOP, NOP, '#', '#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '&', 0x1E, 0x1E, '^', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=08 */ '7', '/', 0x1B, 0x1B, '[', '[', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=09 */ '8', '(', NOP, NOP, '8', '(', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ']', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=0b */ '0', '=', NOP, NOP, '{', '{', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ 0xDF, '?', NOP, NOP, '|', '|', NOP, NOP, 0x33, 0x00,
+/* sc=0d */ 0x92, 0x93, NOP, NOP, '\'', '`', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ 0xFC, 0xDC, 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x01,
+/* sc=1b */ '+', '*', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ 0xF6, 0xD6, NOP, NOP, 0xF6, 0xD6, NOP, NOP, 0x33, 0x01,
+/* sc=28 */ 0xE4, 0xC4, NOP, NOP, 0xE4, 0xC4, NOP, NOP, 0x33, 0x01,
+/* sc=29 */ '<', '>', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '#', '^', 0x1E, 0x1E, '`', '~', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=2c */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00,
+/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02,
+/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+#endif
+
+#ifdef SWKEYMAP
+keymap_t key_map = { 0x69, /* swedish iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=03 */ '2', '"', NOP, NOP, '@', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=04 */ '3', '#', NOP, NOP, 0xA3, NOP, NOP, NOP, 0x37, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, 0xA4, NOP, NOP, NOP, 0x37, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=07 */ '6', '&', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=08 */ '7', '/', NOP, NOP, '{', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=09 */ '8', '(', NOP, NOP, '[', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=0a */ '9', ')', NOP, NOP, ']', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=0b */ '0', '=', NOP, NOP, '}', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=0c */ '+', '?', NOP, NOP, '\\', NOP, 0x1C, NOP, 0x35, 0x00,
+/* sc=0d */ 0x180, '`', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ 0xE5, 0xC5, NOP, NOP, '}', ']', NOP, NOP, 0x33, 0x01,
+/* sc=1b */ 0xA8, '^', NOP, NOP, '~', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ 0xF6, 0xD6, NOP, NOP, '|', '\\', NOP, NOP, 0x33, 0x01,
+/* sc=28 */ 0xE4, 0xC4, NOP, NOP, '{', '[', NOP, NOP, 0x33, 0x01,
+/* sc=29 */ 0xA7, 0xBD, NOP, NOP, '\\', '|', NOP, NOP, 0x33, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\'', '*', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', ';', NOP, NOP, NOP, '<', NOP, NOP, 0x3B, 0x00,
+/* sc=34 */ '.', ':', NOP, NOP, NOP, '>', NOP, NOP, 0x3B, 0x00,
+/* sc=35 */ '-', '_', 0x1F, NOP, '/', '?', NOP, NOP, 0x13, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00,
+/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02,
+/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ '<', '>', NOP, NOP, '|', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+#endif
+
+#ifdef RUKEYMAP
+keymap_t key_map = { 0xe9, /* keys number */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * -------------------------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, NOP, NOP, SET8|0x1B, SET8|0x1B, DBG, NOP, 0x33, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, SET8|'1', SET8|'!', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '@', 0x00, 0x00, SET8|'2', SET8|'@', SET8|0x00, SET8|0x00, 0x00, 0x00,
+/* sc=04 */ '3', '#', NOP, NOP, SET8|'3', SET8|'#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, SET8|'4', SET8|'$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, SET8|'5', SET8|'%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '^', 0x1E, 0x1E, SET8|'6', SET8|'^', SET8|0x1E, SET8|0x1E, 0x00, 0x00,
+/* sc=08 */ '7', '&', NOP, NOP, SET8|'7', SET8|'&', NOP, NOP, 0x33, 0x00,
+/* sc=09 */ '8', '*', NOP, NOP, SET8|'8', SET8|'*', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '9', '(', NOP, NOP, SET8|'9', SET8|'(', NOP, NOP, 0x33, 0x00,
+/* sc=0b */ '0', ')', NOP, NOP, SET8|'0', SET8|')', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '-', '_', 0x1F, 0x1F, SET8|'-', SET8|'_', SET8|0x1F, SET8|0x1F, 0x00, 0x00,
+/* sc=0d */ '=', '+', NOP, NOP, SET8|'=', SET8|'+', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, SET8|0x08, SET8|0x08, SET8|0x7F, SET8|0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, F(16), NOP, NOP, SET8|0x09, F(16), NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, SET8|'q', SET8|'Q', SET8|0x11, SET8|0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, SET8|'w', SET8|'W', SET8|0x17, SET8|0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, SET8|'e', SET8|'E', SET8|0x05, SET8|0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, SET8|'r', SET8|'R', SET8|0x12, SET8|0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, SET8|'t', SET8|'T', SET8|0x14, SET8|0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, SET8|'y', SET8|'Y', SET8|0x19, SET8|0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, SET8|'u', SET8|'U', SET8|0x15, SET8|0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, SET8|'i', SET8|'I', SET8|0x09, SET8|0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, SET8|'o', SET8|'O', SET8|0x0F, SET8|0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, SET8|'p', SET8|'P', SET8|0x10, SET8|0x10, 0x00, 0x01,
+/* sc=1a */ '[', '{', 0x1B, 0x1B, SET8|'[', SET8|'{', SET8|0x1B, SET8|0x1B, 0x00, 0x00,
+/* sc=1b */ ']', '}', 0x1D, 0x1D, SET8|']', SET8|'}', SET8|0x1D, SET8|0x1D, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, SET8|'a', SET8|'A', SET8|0x01, SET8|0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, SET8|'s', SET8|'S', SET8|0x13, SET8|0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, SET8|'d', SET8|'D', SET8|0x04, SET8|0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, SET8|'f', SET8|'F', SET8|0x06, SET8|0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, SET8|'g', SET8|'G', SET8|0x07, SET8|0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, SET8|'h', SET8|'H', SET8|0x08, SET8|0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, SET8|'j', SET8|'J', SET8|0x0A, SET8|0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, SET8|'k', SET8|'K', SET8|0x0B, SET8|0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, SET8|'l', SET8|'L', SET8|0x0C, SET8|0x0C, 0x00, 0x01,
+/* sc=27 */ ';', ':', NOP, NOP, SET8|';', SET8|':', NOP, NOP, 0x33, 0x00,
+/* sc=28 */ '\'', '"', NOP, NOP, SET8|'\'', SET8|'"', NOP, NOP, 0x33, 0x00,
+/* sc=29 */ '`', '~', NOP, NOP, SET8|'`', SET8|'~', NOP, NOP, 0x33, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\\', '|', 0x1C, 0x1C, SET8|'\\', SET8|'|', SET8|0x1C, SET8|0x1C, 0x00, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, SET8|'z', SET8|'Z', SET8|0x1A, SET8|0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, SET8|'x', SET8|'X', SET8|0x18, SET8|0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, SET8|'c', SET8|'C', SET8|0x03, SET8|0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, SET8|'v', SET8|'V', SET8|0x16, SET8|0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, SET8|'b', SET8|'B', SET8|0x02, SET8|0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, SET8|'n', SET8|'N', SET8|0x0E, SET8|0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, SET8|'m', SET8|'M', SET8|0x0D, SET8|0x0D, 0x00, 0x01,
+/* sc=33 */ ',', '<', NOP, NOP, SET8|',', SET8|'<', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', '>', NOP, NOP, SET8|'.', SET8|'>', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '/', '?', NOP, NOP, SET8|'/', SET8|'?', NOP, NOP, 0x33, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, SET8|'*', SET8|'*', SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', SET8|' ', SET8|' ', SET8|' ', SET8|' ', 0x00, 0x00,
+/* sc=3a */ ALK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00,
+/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', SET8|'7', SET8|'7', SET8|'7', SET8|'7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', SET8|'8', SET8|'8', SET8|'8', SET8|'8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', SET8|'9', SET8|'9', SET8|'9', SET8|'9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', '-', '-', SET8|'-', SET8|'-', SET8|'-', SET8|'-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', SET8|'4', SET8|'4', SET8|'4', SET8|'4', 0x80, 0x02,
+/* sc=4c */ F(48), '5', '5', '5', SET8|'5', SET8|'5', SET8|'5', SET8|'5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', '6', '6', SET8|'6', SET8|'6', SET8|'6', SET8|'6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', SET8|'+', SET8|'+', SET8|'+', SET8|'+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', SET8|'1', SET8|'1', SET8|'1', SET8|'1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', SET8|'2', SET8|'2', SET8|'2', SET8|'2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', SET8|'3', SET8|'3', SET8|'3', SET8|'3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', SET8|'0', SET8|'0', SET8|'0', SET8|'0', 0x80, 0x02,
+/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0x82, 0x02,
+/* sc=54 */ ALK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', NOP, NOP, SET8|'/', SET8|'/', NOP, NOP, 0x33, 0x00,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0xC2, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=69 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6b */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6c */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6d */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6e */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6f */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=70 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=71 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=72 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=73 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=74 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=75 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=76 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=77 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=78 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=79 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7b */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7c */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7d */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7e */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7f */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* extended (ALTGR LOCK keys) */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, NOP, NOP, SET8|0x1B, SET8|0x1B, DBG, NOP, 0x33, 0x00,
+/* sc=02 */ '!', '1', NOP, NOP, SET8|'1', SET8|'!', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '"', '2', 0x00, 0x00, SET8|'2', SET8|'@', SET8|0x00, SET8|0x00, 0x00, 0x00,
+/* sc=04 */ '\'', '3', NOP, NOP, SET8|'3', SET8|'#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ ';', '4', NOP, NOP, SET8|'4', SET8|'$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ ':', '5', NOP, NOP, SET8|'5', SET8|'%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ ',', '6', 0x1E, 0x1E, SET8|'6', SET8|'^', SET8|0x1E, SET8|0x1E, 0x00, 0x00,
+/* sc=08 */ '.', '7', NOP, NOP, SET8|'7', SET8|'&', NOP, NOP, 0x33, 0x00,
+/* sc=09 */ '*', '8', NOP, NOP, SET8|'8', SET8|'*', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '(', '9', NOP, NOP, SET8|'9', SET8|'(', NOP, NOP, 0x33, 0x00,
+/* sc=0b */ ')', '0', NOP, NOP, SET8|'0', SET8|')', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '-', '_', 0x1F, 0x1F, SET8|'-', SET8|'_', SET8|0x1F, SET8|0x1F, 0x00, 0x00,
+/* sc=0d */ '=', '+', NOP, NOP, SET8|'=', SET8|'+', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, SET8|0x08, SET8|0x08, SET8|0x7F, SET8|0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, F(16), NOP, NOP, SET8|0x09, F(16), NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 0xca, 0xea, 0x11, 0x11, SET8|'q', SET8|'Q', SET8|0x11, SET8|0x11, 0x00, 0x01,
+/* sc=11 */ 0xc3, 0xe3, 0x17, 0x17, SET8|'w', SET8|'W', SET8|0x17, SET8|0x17, 0x00, 0x01,
+/* sc=12 */ 0xd5, 0xf5, 0x05, 0x05, SET8|'e', SET8|'E', SET8|0x05, SET8|0x05, 0x00, 0x01,
+/* sc=13 */ 0xcb, 0xeb, 0x12, 0x12, SET8|'r', SET8|'R', SET8|0x12, SET8|0x12, 0x00, 0x01,
+/* sc=14 */ 0xc5, 0xe5, 0x14, 0x14, SET8|'t', SET8|'T', SET8|0x14, SET8|0x14, 0x00, 0x01,
+/* sc=15 */ 0xce, 0xee, 0x19, 0x19, SET8|'y', SET8|'Y', SET8|0x19, SET8|0x19, 0x00, 0x01,
+/* sc=16 */ 0xc7, 0xe7, 0x15, 0x15, SET8|'u', SET8|'U', SET8|0x15, SET8|0x15, 0x00, 0x01,
+/* sc=17 */ 0xdb, 0xfb, 0x09, 0x09, SET8|'i', SET8|'I', SET8|0x09, SET8|0x09, 0x00, 0x01,
+/* sc=18 */ 0xdd, 0xfd, 0x0F, 0x0F, SET8|'o', SET8|'O', SET8|0x0F, SET8|0x0F, 0x00, 0x01,
+/* sc=19 */ 0xda, 0xfa, 0x10, 0x10, SET8|'p', SET8|'P', SET8|0x10, SET8|0x10, 0x00, 0x01,
+/* sc=1a */ 0xc8, 0xe8, 0x1B, 0x1B, SET8|'[', SET8|'{', SET8|0x1B, SET8|0x1B, 0x00, 0x01,
+/* sc=1b */ 0xdf, 0xff, 0x1D, 0x1D, SET8|']', SET8|'}', SET8|0x1D, SET8|0x1D, 0x00, 0x01,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 0xc6, 0xe6, 0x01, 0x01, SET8|'a', SET8|'A', SET8|0x01, SET8|0x01, 0x00, 0x01,
+/* sc=1f */ 0xd9, 0xf9, 0x13, 0x13, SET8|'s', SET8|'S', SET8|0x13, SET8|0x13, 0x00, 0x01,
+/* sc=20 */ 0xd7, 0xf7, 0x04, 0x04, SET8|'d', SET8|'D', SET8|0x04, SET8|0x04, 0x00, 0x01,
+/* sc=21 */ 0xc1, 0xe1, 0x06, 0x06, SET8|'f', SET8|'F', SET8|0x06, SET8|0x06, 0x00, 0x01,
+/* sc=22 */ 0xd0, 0xf0, 0x07, 0x07, SET8|'g', SET8|'G', SET8|0x07, SET8|0x07, 0x00, 0x01,
+/* sc=23 */ 0xd2, 0xf2, 0x08, 0x08, SET8|'h', SET8|'H', SET8|0x08, SET8|0x08, 0x00, 0x01,
+/* sc=24 */ 0xcf, 0xef, 0x0A, 0x0A, SET8|'j', SET8|'J', SET8|0x0A, SET8|0x0A, 0x00, 0x01,
+/* sc=25 */ 0xcc, 0xec, 0x0B, 0x0B, SET8|'k', SET8|'K', SET8|0x0B, SET8|0x0B, 0x00, 0x01,
+/* sc=26 */ 0xc4, 0xe4, 0x0C, 0x0C, SET8|'l', SET8|'L', SET8|0x0C, SET8|0x0C, 0x00, 0x01,
+/* sc=27 */ 0xd6, 0xf6, NOP, NOP, SET8|';', SET8|':', NOP, NOP, 0x33, 0x01,
+/* sc=28 */ 0xdc, 0xfc, NOP, NOP, SET8|'\'', SET8|'"', NOP, NOP, 0x33, 0x01,
+/* sc=29 */ 0xa3, 0xb3, NOP, NOP, SET8|'`', SET8|'~', NOP, NOP, 0x33, 0x01,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\\', '|', 0x1C, 0x1C, SET8|'\\', SET8|'|', SET8|0x1C, SET8|0x1C, 0x00, 0x00,
+/* sc=2c */ 0xd1, 0xf1, 0x1A, 0x1A, SET8|'z', SET8|'Z', SET8|0x1A, SET8|0x1A, 0x00, 0x01,
+/* sc=2d */ 0xde, 0xfe, 0x18, 0x18, SET8|'x', SET8|'X', SET8|0x18, SET8|0x18, 0x00, 0x01,
+/* sc=2e */ 0xd3, 0xf3, 0x03, 0x03, SET8|'c', SET8|'C', SET8|0x03, SET8|0x03, 0x00, 0x01,
+/* sc=2f */ 0xcd, 0xed, 0x16, 0x16, SET8|'v', SET8|'V', SET8|0x16, SET8|0x16, 0x00, 0x01,
+/* sc=30 */ 0xc9, 0xe9, 0x02, 0x02, SET8|'b', SET8|'B', SET8|0x02, SET8|0x02, 0x00, 0x01,
+/* sc=31 */ 0xd4, 0xf4, 0x0E, 0x0E, SET8|'n', SET8|'N', SET8|0x0E, SET8|0x0E, 0x00, 0x01,
+/* sc=32 */ 0xd8, 0xf8, 0x0D, 0x0D, SET8|'m', SET8|'M', SET8|0x0D, SET8|0x0D, 0x00, 0x01,
+/* sc=33 */ 0xc2, 0xe2, NOP, NOP, SET8|',', SET8|'<', NOP, NOP, 0x33, 0x01,
+/* sc=34 */ 0xc0, 0xe0, NOP, NOP, SET8|'.', SET8|'>', NOP, NOP, 0x33, 0x01,
+/* sc=35 */ '/', '?', NOP, NOP, SET8|'/', SET8|'?', NOP, NOP, 0x33, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, SET8|'*', SET8|'*', SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', SET8|' ', SET8|' ', SET8|' ', SET8|' ', 0x00, 0x00,
+/* sc=3a */ ALK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00,
+/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', SET8|'7', SET8|'7', SET8|'7', SET8|'7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', SET8|'8', SET8|'8', SET8|'8', SET8|'8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', SET8|'9', SET8|'9', SET8|'9', SET8|'9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', '-', '-', SET8|'-', SET8|'-', SET8|'-', SET8|'-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', SET8|'4', SET8|'4', SET8|'4', SET8|'4', 0x80, 0x02,
+/* sc=4c */ F(48), '5', '5', '5', SET8|'5', SET8|'5', SET8|'5', SET8|'5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', '6', '6', SET8|'6', SET8|'6', SET8|'6', SET8|'6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', SET8|'+', SET8|'+', SET8|'+', SET8|'+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', SET8|'1', SET8|'1', SET8|'1', SET8|'1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', SET8|'2', SET8|'2', SET8|'2', SET8|'2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', SET8|'3', SET8|'3', SET8|'3', SET8|'3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', SET8|'0', SET8|'0', SET8|'0', SET8|'0', 0x80, 0x02,
+/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0x82, 0x02,
+/* sc=54 */ ALK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', NOP, NOP, SET8|'/', SET8|'/', NOP, NOP, 0x33, 0x00,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0xC2, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+
+#endif
+
+#if !defined(DKKEYMAP) && !defined(UKKEYMAP) && !defined(GRKEYMAP) && !defined(SWKEYMAP) && !defined(RUKEYMAP)
+keymap_t key_map = { 0x69, /* US iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '@', 0x00, 0x00, '2', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', '#', NOP, NOP, '3', '#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '^', 0x1E, 0x1E, '6', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=08 */ '7', '&', NOP, NOP, '7', '&', NOP, NOP, 0x33, 0x00,
+/* sc=09 */ '8', '*', NOP, NOP, '8', '*', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '9', '(', NOP, NOP, '9', '(', NOP, NOP, 0x33, 0x00,
+/* sc=0b */ '0', ')', NOP, NOP, '0', ')', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=0d */ '=', '+', NOP, NOP, '=', '+', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x33, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=1b */ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ ';', ':', NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x00,
+/* sc=28 */ '\'', '"', NOP, NOP, '\'', '"', NOP, NOP, 0x33, 0x00,
+/* sc=29 */ '`', '~', NOP, NOP, '`', '~', NOP, NOP, 0x33, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', '<', NOP, NOP, ',', '<', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', '>', NOP, NOP, '.', '>', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '/', '?', NOP, NOP, '/', '?', NOP, NOP, 0x33, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00,
+/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', '-', '-', '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ NOP, '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02,
+/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x00, 0x00,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x00,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+};
+
+#endif
+
+fkeytab_t fkey_tab[60] = {
+/* 00-03 */ {"\033[M", 3}, {"\033[N", 3}, {"\033[O", 3}, {"\033[P", 3},
+/* 04-07 */ {"\033[Q", 3}, {"\033[R", 3}, {"\033[S", 3}, {"\033[T", 3},
+/* 08-0B */ {"\033[U", 3}, {"\033[V", 3}, {"\033[W", 3}, {"\033[X", 3},
+/* 0C-0F */ {"\033[W", 3}, {"\033[X", 3}, {"\033[Y", 3}, {"\033[Z", 3},
+/* 10-13 */ {"\033[a", 3}, {"\033[b", 3}, {"\033[c", 3}, {"\033[d", 3},
+/* 14-17 */ {"\033[e", 3}, {"\033[f", 3}, {"\033[g", 3}, {"\033[h", 3},
+/* 18-1B */ {"\033[g", 3}, {"\033[h", 3}, {"\033[i", 3}, {"\033[j", 3},
+/* 1C-1F */ {"\033[k", 3}, {"\033[l", 3}, {"\033[m", 3}, {"\033[n", 3},
+/* 20-23 */ {"\033[o", 3}, {"\033[p", 3}, {"\033[q", 3}, {"\033[r", 3},
+/* 24-27 */ {"\033[g", 3}, {"\033[h", 3}, {"\033[i", 3}, {"\033[j", 3},
+/* 28-2B */ {"\033[k", 3}, {"\033[l", 3}, {"\033[m", 3}, {"\033[n", 3},
+/* 2C-2F */ {"\033[o", 3}, {"\033[p", 3}, {"\033[q", 3}, {"\033[r", 3},
+/* 30-33 */ {"\033[H", 3}, {"\033[A", 3}, {"\033[I", 3}, {"-" , 1},
+/* 34-37 */ {"\033[D", 3}, {"\177" , 1}, {"\033[C", 3}, {"+" , 1},
+/* 38-3B */ {"\033[F", 3}, {"\033[B", 3}, {"\033[G", 3}, {"\033[L", 3}
+};
diff --git a/sys/isa/rtc.h b/sys/isa/rtc.h
new file mode 100644
index 0000000..ba008b6
--- /dev/null
+++ b/sys/isa/rtc.h
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)rtc.h 7.1 (Berkeley) 5/12/91
+ * $Id: rtc.h,v 1.3 1993/11/07 17:44:34 wollman Exp $
+ */
+
+#ifndef _I386_ISA_RTC_H_
+#define _I386_ISA_RTC_H_ 1
+
+/*
+ * RTC Register locations
+ */
+
+#define RTC_SEC 0x00 /* seconds */
+#define RTC_SECALRM 0x01 /* seconds alarm */
+#define RTC_MIN 0x02 /* minutes */
+#define RTC_MINALRM 0x03 /* minutes alarm */
+#define RTC_HRS 0x04 /* hours */
+#define RTC_HRSALRM 0x05 /* hours alarm */
+#define RTC_WDAY 0x06 /* week day */
+#define RTC_DAY 0x07 /* day of month */
+#define RTC_MONTH 0x08 /* month of year */
+#define RTC_YEAR 0x09 /* month of year */
+#define RTC_STATUSA 0x0a /* status register A */
+#define RTCSA_TUP 0x80 /* time update, don't look now */
+
+#define RTC_STATUSB 0x0b /* status register B */
+
+#define RTC_INTR 0x0c /* status register C (R) interrupt source */
+#define RTCIR_UPDATE 0x10 /* update intr */
+#define RTCIR_ALARM 0x20 /* alarm intr */
+#define RTCIR_PERIOD 0x40 /* periodic intr */
+#define RTCIR_INT 0x80 /* interrupt output signal */
+
+#define RTC_STATUSD 0x0d /* status register D (R) Lost Power */
+#define RTCSD_PWR 0x80 /* clock lost power */
+
+#define RTC_DIAG 0x0e /* status register E - bios diagnostic */
+#define RTCDG_BITS "\020\010clock_battery\007ROM_cksum\006config_unit\005memory_size\004fixed_disk\003invalid_time"
+
+#define RTC_RESET 0x0f /* status register F - reset code byte */
+#define RTCRS_RST 0x00 /* normal reset */
+#define RTCRS_LOAD 0x04 /* load system */
+
+#define RTC_FDISKETTE 0x10 /* diskette drive type in upper/lower nibble */
+#define RTCFDT_NONE 0 /* none present */
+#define RTCFDT_360K 0x10 /* 360K */
+#define RTCFDT_12M 0x20 /* 1.2M */
+#define RTCFDT_720K 0x30 /* 720K */
+#define RTCFDT_144M 0x40 /* 1.44M */
+
+#define RTC_BASELO 0x15 /* low byte of basemem size */
+#define RTC_BASEHI 0x16 /* high byte of basemem size */
+#define RTC_EXTLO 0x17 /* low byte of extended mem size */
+#define RTC_EXTHI 0x18 /* low byte of extended mem size */
+
+#define RTC_CENTURY 0x32 /* current century - please increment in Dec99*/
+#endif /* _I386_ISA_RTC_H_ */
diff --git a/sys/isa/sio.c b/sys/isa/sio.c
new file mode 100644
index 0000000..dd4ee55
--- /dev/null
+++ b/sys/isa/sio.c
@@ -0,0 +1,1920 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)com.c 7.5 (Berkeley) 5/16/91
+ * $Id: sio.c,v 1.44 1994/04/03 12:25:57 ache Exp $
+ */
+
+#include "sio.h"
+#if NSIO > 0
+#define DONT_MALLOC_TTYS
+/*
+ * Serial driver, based on 386BSD-0.1 com driver.
+ * Mostly rewritten to use pseudo-DMA.
+ * Works for National Semiconductor NS8250-NS16550AF UARTs.
+ * COM driver, based on HP dca driver.
+ */
+#include "param.h"
+#include "systm.h"
+#include "ioctl.h"
+#include "tty.h"
+#include "proc.h"
+#include "user.h"
+#include "conf.h"
+#include "file.h"
+#include "uio.h"
+#include "kernel.h"
+#include "syslog.h"
+
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/sioreg.h"
+#include "i386/isa/ic/ns16550.h"
+
+#define FAKE_DCD(unit) ((unit) == comconsole)
+#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */
+#define RBSZ 1024
+#define RB_I_HIGH_WATER (RBSZ - 2 * RS_IBUFSIZE)
+#define RB_I_LOW_WATER ((RBSZ - 2 * RS_IBUFSIZE) * 7 / 8)
+#define RS_IBUFSIZE 256
+#define RS_OBUFSIZE 256
+#define TTY_BI TTY_FE /* XXX */
+#define TTY_OE TTY_PE /* XXX */
+
+#ifdef COM_BIDIR
+#define CALLOUT(x) (minor(x) & COM_CALLOUT_MASK)
+#define COM_CALLOUT_MASK 0x80
+#define COM_MINOR_MAGIC_MASK 0x80
+#else /* COM_BIDIR */
+#define COM_MINOR_MAGIC_MASK 0
+#endif /* COM_BIDIR */
+
+#define UNIT(x) (minor(x) & ~COM_MINOR_MAGIC_MASK)
+
+#ifdef COM_MULTIPORT
+/* checks in flags for multiport and which is multiport "master chip"
+ * for a given card
+ */
+#define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01)
+#define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff)
+#define COM_NOMASTER(dev) ((dev)->id_flags & 0x04)
+#endif /* COM_MULTIPORT */
+
+#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02)
+
+#ifndef FIFO_TRIGGER
+/*
+ * This driver is fast enough to work with any value and for high values
+ * to be only slightly more efficient. Low values may be better because
+ * they give lower latency.
+ * TODO: always use low values for low speeds. Mouse movements are jerky
+ * if more than one packet arrives at once. The low speeds used for
+ * serial mice help avoid this, but not if (large) fifos are enabled.
+ */
+#define FIFO_TRIGGER FIFO_TRIGGER_14
+#endif
+
+#define com_scr 7 /* scratch register for 16450-16550 (R/W) */
+
+#ifndef setsofttty
+#define OLD_INTERRUPT_HANDLING /* XXX FreeBSD-1.1 and earlier */
+#define setsofttty() (ipending |= 1 << 4) /* XXX requires owning IRQ4 */
+extern u_int ipending; /* XXX */
+void softsio1 __P((void));
+#endif
+
+/*
+ * Input buffer watermarks.
+ * The external device is asked to stop sending when the buffer exactly reaches
+ * high water, or when the high level requests it.
+ * The high level is notified immediately (rather than at a later clock tick)
+ * when this watermark is reached.
+ * The buffer size is chosen so the watermark should almost never be reached.
+ * The low watermark is invisibly 0 since the buffer is always emptied all at
+ * once.
+ */
+#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)
+
+/*
+ * com state bits.
+ * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
+ * than the other bits so that they can be tested as a group without masking
+ * off the low bits.
+ *
+ * The following com and tty flags correspond closely:
+ * TS_BUSY = CS_BUSY (maintained by comstart() and comflush())
+ * CS_TTGO = ~TS_TTSTOP (maintained by comstart() and siostop())
+ * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam())
+ * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam())
+ * TS_FLUSH is not used.
+ * Bug: I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON.
+ */
+#define CS_BUSY 0x80 /* output in progress */
+#define CS_TTGO 0x40 /* output not stopped by XOFF */
+#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */
+#define CS_CHECKMSR 1 /* check of MSR scheduled */
+#define CS_CTS_OFLOW 2 /* use CTS output flow control */
+#define CS_ODONE 4 /* output completed */
+#define CS_RTS_IFLOW 8 /* use RTS input flow control */
+
+static char *error_desc[] = {
+#define CE_OVERRUN 0
+ "silo overflow",
+#define CE_INTERRUPT_BUF_OVERFLOW 1
+ "interrupt-level buffer overflow",
+#define CE_TTY_BUF_OVERFLOW 2
+ "tty-level buffer overflow",
+};
+
+#define CE_NTYPES 3
+#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum])
+
+/* types. XXX - should be elsewhere */
+typedef u_int Port_t; /* hardware port */
+typedef u_char bool_t; /* boolean */
+
+/* com device structure */
+struct com_s {
+ u_char state; /* miscellaneous flag bits */
+ u_char cfcr_image; /* copy of value written to CFCR */
+ bool_t hasfifo; /* nonzero for 16550 UARTs */
+ u_char mcr_image; /* copy of value written to MCR */
+#ifdef COM_BIDIR
+ bool_t bidir; /* is this unit bidirectional? */
+ bool_t active; /* is the port active _at all_? */
+ bool_t active_in; /* is the incoming port in use? */
+ bool_t active_out; /* is the outgoing port in use? */
+#endif /* COM_BIDIR */
+#ifdef COM_MULTIPORT
+ bool_t multiport; /* is this unit part of a multiport device? */
+#endif /* COM_MULTIPORT */
+ int dtr_wait; /* time to hold DTR down on close (* 1/HZ) */
+ u_int tx_fifo_size;
+
+ /*
+ * The high level of the driver never reads status registers directly
+ * because there would be too many side effects to handle conveniently.
+ * Instead, it reads copies of the registers stored here by the
+ * interrupt handler.
+ */
+ u_char last_modem_status; /* last MSR read by intr handler */
+ u_char prev_modem_status; /* last MSR handled by high level */
+
+ u_char *ibuf; /* start of input buffer */
+ u_char *ibufend; /* end of input buffer */
+ u_char *ihighwater; /* threshold in input buffer */
+ u_char *iptr; /* next free spot in input buffer */
+
+ u_char *obufend; /* end of output buffer */
+ int ocount; /* original count for current output */
+ u_char *optr; /* next char to output */
+
+ Port_t data_port; /* i/o ports */
+ Port_t int_id_port;
+ Port_t iobase;
+ Port_t modem_ctl_port;
+ Port_t line_status_port;
+ Port_t modem_status_port;
+
+ struct tty *tp; /* cross reference */
+
+#ifdef TIOCTIMESTAMP
+ bool_t do_timestamp;
+ struct timeval timestamp;
+#endif
+
+ u_long bytes_in; /* statistics */
+ u_long bytes_out;
+ u_int delta_error_counts[CE_NTYPES];
+ u_int error_counts[CE_NTYPES];
+
+ /*
+ * Ping-pong input buffers. The extra factor of 2 in the sizes is
+ * to allow for an error byte for each input byte.
+ */
+#define CE_INPUT_OFFSET RS_IBUFSIZE
+ u_char ibuf1[2 * RS_IBUFSIZE];
+ u_char ibuf2[2 * RS_IBUFSIZE];
+ u_char obuf[RS_OBUFSIZE];
+};
+
+/*
+ * The public functions in the com module ought to be declared in a com-driver
+ * system header.
+ */
+
+/* Interrupt handling entry points. */
+void siointr __P((int unit));
+void siopoll __P((void));
+
+/* Device switch entry points. */
+int sioopen __P((dev_t dev, int oflags, int devtype,
+ struct proc *p));
+int sioclose __P((dev_t dev, int fflag, int devtype,
+ struct proc *p));
+int sioread __P((dev_t dev, struct uio *uio, int ioflag));
+int siowrite __P((dev_t dev, struct uio *uio, int ioflag));
+int sioioctl __P((dev_t dev, int cmd, caddr_t data,
+ int fflag, struct proc *p));
+void siostop __P((struct tty *tp, int rw));
+#define sioreset noreset
+int sioselect __P((dev_t dev, int rw, struct proc *p));
+#define siommap nommap
+#define siostrategy nostrategy
+
+/* Console device entry points. */
+int siocngetc __P((dev_t dev));
+struct consdev;
+void siocninit __P((struct consdev *cp));
+void siocnprobe __P((struct consdev *cp));
+void siocnputc __P((dev_t dev, int c));
+
+static int sioattach __P((struct isa_device *dev));
+static void comflush __P((struct com_s *com));
+static void comhardclose __P((struct com_s *com));
+static void siointr1 __P((struct com_s *com));
+static void commctl __P((struct com_s *com, int bits, int how));
+static int comparam __P((struct tty *tp, struct termios *t));
+static int sioprobe __P((struct isa_device *dev));
+static void comstart __P((struct tty *tp));
+static void comwakeup __P((caddr_t chan, int ticks));
+static int tiocm_xxx2mcr __P((int tiocm_xxx));
+
+/* table and macro for fast conversion from a unit number to its com struct */
+static struct com_s *p_com_addr[NSIO];
+#define com_addr(unit) (p_com_addr[unit])
+
+static struct com_s com_structs[NSIO];
+
+#ifdef TIOCTIMESTAMP
+static struct timeval intr_timestamp;
+#endif
+
+struct isa_driver siodriver = {
+ sioprobe, sioattach, "sio"
+};
+
+#ifdef COMCONSOLE
+static int comconsole = COMCONSOLE;
+#else
+static int comconsole = -1;
+#endif
+static speed_t comdefaultrate = TTYDEF_SPEED;
+static u_int com_events; /* input chars + weighted output completions */
+static int commajor;
+#define TB_OUT(tp) (&(tp)->t_outq)
+#define TB_RAW(tp) (&(tp)->t_rawq)
+struct tty sio_tty[NSIO];
+extern struct tty *constty;
+extern int tk_nin; /* XXX */
+extern int tk_rawcc; /* XXX */
+
+#ifdef KGDB
+#include "machine/remote-sl.h"
+
+extern int kgdb_dev;
+extern int kgdb_rate;
+extern int kgdb_debug_init;
+#endif
+
+static struct speedtab comspeedtab[] = {
+ 0, 0,
+ 50, COMBRD(50),
+ 75, COMBRD(75),
+ 110, COMBRD(110),
+ 134, COMBRD(134),
+ 150, COMBRD(150),
+ 200, COMBRD(200),
+ 300, COMBRD(300),
+ 600, COMBRD(600),
+ 1200, COMBRD(1200),
+ 1800, COMBRD(1800),
+ 2400, COMBRD(2400),
+ 4800, COMBRD(4800),
+ 9600, COMBRD(9600),
+ 19200, COMBRD(19200),
+ 38400, COMBRD(38400),
+ 57600, COMBRD(57600),
+ 115200, COMBRD(115200),
+ -1, -1
+};
+
+/* XXX - configure this list */
+static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, };
+
+static int
+sioprobe(dev)
+ struct isa_device *dev;
+{
+ static bool_t already_init;
+ Port_t *com_ptr;
+ Port_t iobase;
+ int result;
+
+ if (!already_init) {
+ /*
+ * Turn off MCR_IENABLE for all likely serial ports. An unused
+ * port with its MCR_IENABLE gate open will inhibit interrupts
+ * from any used port that shares the interrupt vector.
+ */
+ for (com_ptr = likely_com_ports;
+ com_ptr < &likely_com_ports[sizeof likely_com_ports
+ / sizeof likely_com_ports[0]];
+ ++com_ptr)
+ outb(*com_ptr + com_mcr, 0);
+ already_init = TRUE;
+ }
+ iobase = dev->id_iobase;
+ result = IO_COMSIZE;
+
+ /*
+ * We don't want to get actual interrupts, just masked ones.
+ * Interrupts from this line should already be masked in the ICU,
+ * but mask them in the processor as well in case there are some
+ * (misconfigured) shared interrupts.
+ */
+ disable_intr();
+
+ /*
+ * Initialize the speed so that any junk in the THR or output fifo will
+ * be transmitted in a known time. (There may be lots of junk after a
+ * soft reboot, and output interrupts don't work right after a master
+ * reset, at least for 16550s. (The speed is undefined after MR, but
+ * MR empties the THR and the TSR so it's not clear why this matters)).
+ * Enable output interrupts (only) and check the following:
+ * o the CFCR, IER and MCR in UART hold the values written to them
+ * (the values happen to be all distinct - this is good for
+ * avoiding false positive tests from bus echoes).
+ * o an output interrupt is generated and its vector is correct.
+ * o the interrupt goes away when the IIR in the UART is read.
+ */
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ outb(iobase + com_dlbl, COMBRD(9600) & 0xff);
+ outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8);
+ outb(iobase + com_cfcr, CFCR_8BITS); /* ensure IER is addressed */
+ outb(iobase + com_mcr, MCR_IENABLE); /* open gate early */
+ outb(iobase + com_ier, 0); /* ensure edge on next intr */
+ outb(iobase + com_ier, IER_ETXRDY); /* generate interrupt */
+ DELAY((16 + 1) * 9600 / 10); /* enough to drain 16 bytes */
+ if ( inb(iobase + com_cfcr) != CFCR_8BITS
+ || inb(iobase + com_ier) != IER_ETXRDY
+ || inb(iobase + com_mcr) != MCR_IENABLE
+#ifndef COM_MULTIPORT /* XXX - need to do more to enable interrupts */
+ || !isa_irq_pending(dev)
+#endif
+ || (inb(iobase + com_iir) & IIR_IMASK) != IIR_TXRDY
+ || isa_irq_pending(dev)
+ || (inb(iobase + com_iir) & IIR_IMASK) != IIR_NOPEND)
+ result = 0;
+
+ /*
+ * Turn off all device interrupts and check that they go off properly.
+ * Leave MCR_IENABLE set. It gates the OUT2 output of the UART to
+ * the ICU input. Closing the gate would give a floating ICU input
+ * (unless there is another device driving at) and spurious interrupts.
+ * (On the system that this was first tested on, the input floats high
+ * and gives a (masked) interrupt as soon as the gate is closed.)
+ */
+ outb(iobase + com_ier, 0);
+ outb(iobase + com_mcr, MCR_IENABLE); /* dummy to avoid bus echo */
+ if ( inb(iobase + com_ier) != 0
+ || isa_irq_pending(dev)
+ || (inb(iobase + com_iir) & IIR_IMASK) != IIR_NOPEND)
+ result = 0;
+ if (result == 0)
+ outb(iobase + com_mcr, 0);
+
+ enable_intr();
+ return (result);
+}
+
+static int
+sioattach(isdp)
+ struct isa_device *isdp;
+{
+ struct com_s *com;
+ static bool_t comwakeup_started = FALSE;
+ Port_t iobase;
+ int s;
+ int unit;
+
+ iobase = isdp->id_iobase;
+ unit = isdp->id_unit;
+ s = spltty();
+
+ /*
+ * sioprobe() has initialized the device registers as follows:
+ * o cfcr = CFCR_8BITS.
+ * It is most important that CFCR_DLAB is off, so that the
+ * data port is not hidden when we enable interrupts.
+ * o ier = 0.
+ * Interrupts are only enabled when the line is open.
+ * o mcr = MCR_IENABLE.
+ * Keeping MCR_DTR and MCR_RTS off might stop the external
+ * device from sending before we are ready.
+ */
+
+ com = &com_structs[unit]; /* XXX malloc it */
+ com->cfcr_image = CFCR_8BITS;
+ com->mcr_image = MCR_IENABLE;
+ com->dtr_wait = 3 * hz;
+ com->tx_fifo_size = 1;
+ com->iptr = com->ibuf = com->ibuf1;
+ com->ibufend = com->ibuf1 + RS_IBUFSIZE;
+ com->ihighwater = com->ibuf1 + RS_IHIGHWATER;
+ com->iobase = iobase;
+ com->data_port = iobase + com_data;
+ com->int_id_port = iobase + com_iir;
+ com->modem_ctl_port = iobase + com_mcr;
+ com->line_status_port = iobase + com_lsr;
+ com->modem_status_port = iobase + com_msr;
+#ifdef DONT_MALLOC_TTYS
+ com->tp = &sio_tty[unit];
+#endif
+
+ /* attempt to determine UART type */
+ printf("sio%d: type", unit);
+#ifdef COM_MULTIPORT
+ if (!COM_ISMULTIPORT(isdp))
+#endif
+ {
+ u_char scr;
+ u_char scr1;
+ u_char scr2;
+
+ scr = inb(iobase + com_scr);
+ outb(iobase + com_scr, 0xa5);
+ scr1 = inb(iobase + com_scr);
+ outb(iobase + com_scr, 0x5a);
+ scr2 = inb(iobase + com_scr);
+ outb(iobase + com_scr, scr);
+ if (scr1 != 0xa5 || scr2 != 0x5a) {
+ printf(" 8250");
+ goto determined_type;
+ }
+ }
+ outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER_14);
+ DELAY(100);
+ switch (inb(com->int_id_port) & IIR_FIFO_MASK) {
+ case FIFO_TRIGGER_1:
+ printf(" 16450");
+ break;
+ case FIFO_TRIGGER_4:
+ printf(" 16450?");
+ break;
+ case FIFO_TRIGGER_8:
+ printf(" 16550?");
+ break;
+ case FIFO_TRIGGER_14:
+ printf(" 16550A");
+ if (COM_NOFIFO(isdp))
+ printf(" fifo disabled");
+ else {
+ com->hasfifo = TRUE;
+ com->tx_fifo_size = 16;
+ }
+ break;
+ }
+ outb(iobase + com_fifo, 0);
+determined_type: ;
+
+#ifdef COM_MULTIPORT
+ if (COM_ISMULTIPORT(isdp)) {
+ com->multiport = TRUE;
+ printf(" (multiport)");
+
+ /* Note: some cards have no master port (e.g., BocaBoards) */
+ if (!COM_NOMASTER(isdp)) {
+ struct isa_device *masterdev;
+
+ /* set the master's common-interrupt-enable reg.,
+ * as appropriate. YYY See your manual
+ */
+ /* enable only common interrupt for port */
+ outb(com->modem_ctl_port, com->mcr_image = 0);
+
+ masterdev = find_isadev(isa_devtab_tty, &siodriver,
+ COM_MPMASTER(isdp));
+ outb(masterdev->id_iobase + com_scr, 0x80);
+ }
+
+ } else
+ com->multiport = FALSE;
+#endif /* COM_MULTIPORT */
+ printf("\n");
+
+#ifdef KGDB
+ if (kgdb_dev == makedev(commajor, unit)) {
+ if (comconsole == unit)
+ kgdb_dev = -1; /* can't debug over console port */
+ else {
+ int divisor;
+
+ /*
+ * XXX now unfinished and broken. Need to do
+ * something more like a full open(). There's no
+ * suitable interrupt handler so don't enable device
+ * interrupts. Watch out for null tp's.
+ */
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ divisor = ttspeedtab(kgdb_rate, comspeedtab);
+ outb(iobase + com_dlbl, divisor & 0xFF);
+ outb(iobase + com_dlbh, (u_int) divisor >> 8);
+ outb(iobase + com_cfcr, CFCR_8BITS);
+ outb(com->modem_status_port,
+ com->mcr_image |= MCR_DTR | MCR_RTS);
+
+ if (kgdb_debug_init) {
+ /*
+ * Print prefix of device name,
+ * let kgdb_connect print the rest.
+ */
+ printf("sio%d: ", unit);
+ kgdb_connect(1);
+ } else
+ printf("sio%d: kgdb enabled\n", unit);
+ }
+ }
+#endif
+
+ com_addr(unit) = com;
+ splx(s);
+ if (!comwakeup_started) {
+ comwakeup((caddr_t) NULL, 0);
+ comwakeup_started = TRUE;
+ }
+ return (1);
+}
+
+/* ARGSUSED */
+int
+sioopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+#ifdef COM_BIDIR
+ bool_t callout;
+#endif /* COM_BIDIR */
+ struct com_s *com;
+ int error = 0;
+ bool_t got_status = FALSE;
+ Port_t iobase;
+ int s;
+ struct tty *tp;
+ int unit;
+
+ unit = UNIT(dev);
+ if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL)
+ return (ENXIO);
+#ifdef COM_BIDIR
+ /* if it's a callout device, and bidir not possible on that dev, die */
+ callout = CALLOUT(dev);
+ if (callout && !(com->bidir))
+ return (ENXIO);
+#endif /* COM_BIDIR */
+
+#ifdef DONT_MALLOC_TTYS
+ tp = com->tp;
+#else
+ sio_tty[unit] = ttymalloc(sio_tty[unit]);
+ tp = com->tp = sio_tty[unit];
+#endif
+ s = spltty();
+
+#ifdef COM_BIDIR
+
+bidir_open_top:
+ got_status = FALSE;
+ /* if it's bidirectional, we've gotta deal with it... */
+ if (com->bidir) {
+ if (callout) {
+ if (com->active_in) {
+ /* it's busy. die */
+ splx(s);
+ return (EBUSY);
+ } else {
+ /* it's ours. lock it down, and set it up */
+ com->active_out = TRUE;
+ }
+ } else {
+ if (com->active_out) {
+ /* it's busy, outgoing. wait, if possible */
+ if (flag & O_NONBLOCK) {
+ /* can't wait; bail */
+ splx(s);
+ return (EBUSY);
+ } else {
+ /* wait for it... */
+ error = tsleep((caddr_t)&com->active_out,
+ TTIPRI|PCATCH,
+ "siooth",
+ 0);
+ /* if there was an error, take off. */
+ if (error != 0) {
+ splx(s);
+ return (error);
+ }
+ /* else take it from the top */
+ goto bidir_open_top;
+ }
+ }
+ disable_intr();
+ com->prev_modem_status =
+ com->last_modem_status = inb(com->modem_status_port);
+ enable_intr();
+ got_status = TRUE;
+ if (com->prev_modem_status & MSR_DCD
+ || FAKE_DCD(unit)) {
+ /* there's a carrier on the line; we win */
+ com->active_in = TRUE;
+ } else {
+ /* there is no carrier on the line */
+ if (flag & O_NONBLOCK) {
+ /* can't wait; let it open */
+ com->active_in = TRUE;
+ } else {
+ /* put DTR & RTS up */
+ /* XXX - bring up RTS earlier? */
+ commctl(com, MCR_DTR | MCR_RTS, DMSET);
+ outb(com->iobase + com_ier, IER_EMSC);
+
+ /* wait for it... */
+ error = tsleep((caddr_t)&com->active_in,
+ TTIPRI|PCATCH,
+ "siodcd",
+ 0);
+
+ /* if not active, turn intrs and DTR off */
+ if (!com->active) {
+ outb(com->iobase + com_ier, 0);
+ commctl(com, MCR_DTR, DMBIC);
+ }
+
+ /* if there was an error, take off. */
+ if (error != 0) {
+ splx(s);
+ return (error);
+ }
+ /* else take it from the top */
+ goto bidir_open_top;
+ }
+ }
+ }
+ }
+
+ com->active = TRUE;
+#endif /* COM_BIDIR */
+
+ tp->t_oproc = comstart;
+ tp->t_param = comparam;
+ tp->t_dev = dev;
+ if (!(tp->t_state & TS_ISOPEN)) {
+ tp->t_state |= TS_WOPEN;
+ ttychars(tp);
+ if (tp->t_ispeed == 0) {
+ /*
+ * We don't use all the flags from <sys/ttydefaults.h>
+ * since those are only relevant for logins. It's
+ * important to have echo off initially so that the
+ * line doesn't start blathering before the echo flag
+ * can be turned off.
+ */
+ tp->t_iflag = 0;
+ tp->t_oflag = 0;
+ tp->t_cflag = CREAD | CS8;
+#ifdef COM_BIDIR
+ if (com->bidir && !callout)
+ tp->t_cflag |= HUPCL;
+#endif
+ tp->t_lflag = 0;
+ tp->t_ispeed = tp->t_ospeed = comdefaultrate;
+ if (unit == comconsole) {
+ tp->t_iflag = TTYDEF_IFLAG;
+ tp->t_oflag = TTYDEF_OFLAG;
+ tp->t_cflag = TTYDEF_CFLAG;
+ tp->t_lflag = TTYDEF_LFLAG;
+ }
+ }
+
+ /*
+ * XXX the full state after a first open() needs to be
+ * programmable and separate for callin and callout.
+ */
+#ifdef COM_BIDIR
+ if (com->bidir) {
+ if (callout)
+ tp->t_cflag |= CLOCAL;
+ else
+ tp->t_cflag &= ~CLOCAL;
+ }
+#endif
+
+ commctl(com, MCR_DTR | MCR_RTS, DMSET);
+ error = comparam(tp, &tp->t_termios);
+ if (error != 0)
+ goto out;
+ ttsetwater(tp);
+ iobase = com->iobase;
+ if (com->hasfifo) {
+ /* (re)enable and drain FIFO */
+ outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER
+ | FIFO_RCV_RST | FIFO_XMT_RST);
+ DELAY(100);
+ }
+ disable_intr();
+ (void) inb(com->line_status_port);
+ (void) inb(com->data_port);
+ if (!got_status)
+ com->prev_modem_status =
+ com->last_modem_status = inb(com->modem_status_port);
+ outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS
+ | IER_EMSC);
+ enable_intr();
+ if (com->prev_modem_status & MSR_DCD || FAKE_DCD(unit))
+ tp->t_state |= TS_CARR_ON;
+ } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
+ splx(s);
+ return (EBUSY);
+ }
+ while (!(flag & O_NONBLOCK) && !(tp->t_cflag & CLOCAL)
+#ifdef COM_BIDIR
+ /* We went through a lot of trouble to open it,
+ * but it's certain we have a carrier now, so
+ * don't spend any time on it now.
+ */
+ && !(com->bidir)
+#endif /* COM_BIDIR */
+ && !(tp->t_state & TS_CARR_ON)) {
+ tp->t_state |= TS_WOPEN;
+ error = ttysleep(tp, (caddr_t)TB_RAW(tp), TTIPRI | PCATCH,
+ ttopen, 0);
+ if (error != 0)
+ break;
+ }
+out:
+ if (error == 0)
+ error = (*linesw[tp->t_line].l_open)(dev, tp);
+ splx(s);
+
+#ifdef COM_BIDIR
+ /* wakeup sleepers */
+ wakeup((caddr_t) &com->active_in);
+#endif /* COM_BIDIR */
+
+ /*
+ * XXX - the next step was once not done, so interrupts, DTR and RTS
+ * remained hot if the process was killed while it was sleeping
+ * waiting for carrier. Now there is the opposite problem. If several
+ * processes are sleeping waiting for carrier on the same line and one
+ * is killed, interrupts are turned off so the other processes will
+ * never see the carrier rise.
+ */
+ if (error != 0 && !(tp->t_state & TS_ISOPEN))
+ comhardclose(com);
+ tp->t_state &= ~TS_WOPEN;
+
+ return (error);
+}
+
+/*ARGSUSED*/
+int
+sioclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+ struct com_s *com;
+ int s;
+ struct tty *tp;
+
+ com = com_addr(UNIT(dev));
+ tp = com->tp;
+ s = spltty();
+ (*linesw[tp->t_line].l_close)(tp, flag);
+ siostop(tp, FREAD | FWRITE);
+ comhardclose(com);
+ ttyclose(tp);
+ splx(s);
+ return (0);
+}
+
+static void
+comhardclose(com)
+ struct com_s *com;
+{
+ Port_t iobase;
+ int s;
+ struct tty *tp;
+ int unit;
+
+ unit = com - &com_structs[0];
+ iobase = com->iobase;
+ s = spltty();
+#ifdef TIOCTIMESTAMP
+ com->do_timestamp = 0;
+#endif
+ outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
+#ifdef KGDB
+ /* do not disable interrupts or hang up if debugging */
+ if (kgdb_dev != makedev(commajor, unit))
+#endif
+ {
+ outb(iobase + com_ier, 0);
+ tp = com->tp;
+ if (tp->t_cflag & HUPCL || tp->t_state & TS_WOPEN
+#ifdef COM_BIDIR
+ /*
+ * XXX we will miss any carrier drop between here and the
+ * next open. Perhaps we should watch DCD even when the
+ * port is closed; it is not sufficient to check it at
+ * the next open because it might go up and down while
+ * we're not watching. And we shouldn't look at DCD if
+ * CLOCAL is set (here or for the dialin device ...).
+ * When the termios state is reinitialized for initial
+ * opens, the correct CLOCAL bit will be
+ * ((the bit now) & (the initial bit)).
+ */
+ || com->active_in
+ && !(com->prev_modem_status & MSR_DCD) && !FAKE_DCD(unit)
+#endif
+ || !(tp->t_state & TS_ISOPEN)) {
+ commctl(com, MCR_RTS, DMSET);
+ if (com->dtr_wait != 0)
+ /*
+ * Uninterruptible sleep since we want to
+ * wait a fixed time.
+ * XXX - delay in open() (if necessary),
+ * not here (always).
+ */
+ tsleep((caddr_t)&com->dtr_wait, TTIPRI,
+ "sioclose", com->dtr_wait);
+ }
+ }
+
+#ifdef COM_BIDIR
+ com->active = com->active_in = com->active_out = FALSE;
+
+ /* wakeup sleepers who are waiting for out to finish */
+ wakeup((caddr_t) &com->active_out);
+#endif /* COM_BIDIR */
+
+ splx(s);
+}
+
+int
+sioread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ struct tty *tp = com_addr(UNIT(dev))->tp;
+
+ return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
+}
+
+int
+siowrite(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ int unit = UNIT(dev);
+ struct tty *tp = com_addr(unit)->tp;
+
+ /*
+ * (XXX) We disallow virtual consoles if the physical console is
+ * a serial port. This is in case there is a display attached that
+ * is not the console. In that situation we don't need/want the X
+ * server taking over the console.
+ */
+ if (constty && unit == comconsole)
+ constty = NULL;
+ return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
+}
+
+#ifdef TIOCTIMESTAMP
+/* Interrupt routine for timekeeping purposes */
+void
+siointrts(unit)
+ int unit;
+{
+ microtime(&intr_timestamp);
+ siointr(unit);
+}
+#endif
+
+void
+siointr(unit)
+ int unit;
+{
+#ifndef COM_MULTIPORT
+ siointr1(com_addr(unit));
+#else /* COM_MULTIPORT */
+ bool_t possibly_more_intrs;
+ struct com_s *com;
+
+ /*
+ * Loop until there is no activity on any port. This is necessary
+ * to get an interrupt edge more than to avoid another interrupt.
+ * If the IRQ signal is just an OR of the IRQ signals from several
+ * devices, then the edge from one may be lost because another is
+ * on.
+ */
+ do {
+ possibly_more_intrs = FALSE;
+ for (unit = 0; unit < NSIO; ++unit) {
+ com = com_addr(unit);
+ if (com != NULL
+ && (inb(com->int_id_port) & IIR_IMASK)
+ != IIR_NOPEND) {
+ siointr1(com);
+ possibly_more_intrs = TRUE;
+ }
+ }
+ } while (possibly_more_intrs);
+#endif /* COM_MULTIPORT */
+}
+
+static void
+siointr1(com)
+ struct com_s *com;
+{
+ u_char line_status;
+ u_char modem_status;
+ u_char *ioptr;
+ u_char recv_data;
+
+#ifdef TIOCTIMESTAMP
+ if (com->do_timestamp)
+ /* XXX a little bloat here... */
+ com->timestamp = intr_timestamp;
+#endif
+ while (TRUE) {
+ line_status = inb(com->line_status_port);
+
+ /* input event? (check first to help avoid overruns) */
+ while (line_status & LSR_RCV_MASK) {
+ /* break/unnattached error bits or real input? */
+ if (!(line_status & LSR_RXRDY))
+ recv_data = 0;
+ else
+ recv_data = inb(com->data_port);
+ ++com->bytes_in;
+ /* XXX reduce SLIP input latency */
+#define FRAME_END 0xc0
+ if (recv_data == FRAME_END)
+ setsofttty();
+#ifdef KGDB
+ /* trap into kgdb? (XXX - needs testing and optim) */
+ if (recv_data == FRAME_END
+ && !(com->tp->t_state & TS_ISOPEN)
+ && kgdb_dev == makedev(commajor, unit)) {
+ kgdb_connect(0);
+ continue;
+ }
+#endif /* KGDB */
+ ioptr = com->iptr;
+ if (ioptr >= com->ibufend)
+ CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW);
+ else {
+ ++com_events;
+#if 0 /* for testing input latency vs efficiency */
+if (com->iptr - com->ibuf == 8)
+ setsofttty();
+#endif
+ ioptr[0] = recv_data;
+ ioptr[CE_INPUT_OFFSET] = line_status;
+ com->iptr = ++ioptr;
+ if (ioptr == com->ihighwater
+ && com->state & CS_RTS_IFLOW)
+ outb(com->modem_ctl_port,
+ com->mcr_image &= ~MCR_RTS);
+ /* XXX - move this out of isr */
+ if (line_status & LSR_OE)
+ CE_RECORD(com, CE_OVERRUN);
+ }
+
+ /*
+ * "& 0x7F" is to avoid the gcc-1.40 generating a slow
+ * jump from the top of the loop to here
+ */
+ line_status = inb(com->line_status_port) & 0x7F;
+ }
+
+ /* modem status change? (always check before doing output) */
+ modem_status = inb(com->modem_status_port);
+ if (modem_status != com->last_modem_status) {
+ /*
+ * Schedule high level to handle DCD changes. Note
+ * that we don't use the delta bits anywhere. Some
+ * UARTs mess them up, and it's easy to remember the
+ * previous bits and calculate the delta.
+ */
+ com->last_modem_status = modem_status;
+ if (!(com->state & CS_CHECKMSR)) {
+ com_events += LOTS_OF_EVENTS;
+ com->state |= CS_CHECKMSR;
+ setsofttty();
+ }
+
+ /* handle CTS change immediately for crisp flow ctl */
+ if (com->state & CS_CTS_OFLOW) {
+ if (modem_status & MSR_CTS)
+ com->state |= CS_ODEVREADY;
+ else
+ com->state &= ~CS_ODEVREADY;
+ }
+ }
+
+ /* output queued and everything ready? */
+ if (line_status & LSR_TXRDY
+ && com->state >= (CS_ODEVREADY | CS_BUSY | CS_TTGO)) {
+ ioptr = com->optr;
+ if (com->tx_fifo_size > 1) {
+ u_int ocount;
+
+ ocount = com->obufend - ioptr;
+ if (ocount > com->tx_fifo_size)
+ ocount = com->tx_fifo_size;
+ com->bytes_out += ocount;
+ do
+ outb(com->data_port, *ioptr++);
+ while (--ocount != 0);
+ } else {
+ outb(com->data_port, *ioptr++);
+ ++com->bytes_out;
+ }
+ com->optr = ioptr;
+ if (ioptr >= com->obufend) {
+ /* output just completed */
+ com_events += LOTS_OF_EVENTS;
+ com->state ^= (CS_ODONE | CS_BUSY);
+ setsofttty(); /* handle at high level ASAP */
+ }
+ }
+
+ /* finished? */
+#ifndef COM_MULTIPORT
+ if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND)
+#endif /* COM_MULTIPORT */
+ return;
+ }
+}
+
+static int
+tiocm_xxx2mcr(tiocm_xxx)
+ int tiocm_xxx;
+{
+ int mcr;
+
+ mcr = 0;
+ if (tiocm_xxx & TIOCM_DTR)
+ mcr |= MCR_DTR;
+ if (tiocm_xxx & TIOCM_RTS)
+ mcr |= MCR_RTS;
+ return (mcr);
+}
+
+int
+sioioctl(dev, cmd, data, flag, p)
+ dev_t dev;
+ int cmd;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+{
+ struct com_s *com;
+ int error;
+ Port_t iobase;
+ int mcr;
+ int msr;
+ int s;
+ int tiocm_xxx;
+ struct tty *tp;
+
+ com = com_addr(UNIT(dev));
+ tp = com->tp;
+ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
+ if (error >= 0)
+ return (error);
+ error = ttioctl(tp, cmd, data, flag);
+
+#ifdef COM_BIDIR
+ /* XXX: plug security hole while sticky bits not yet implemented */
+ if (com->bidir && com->active_in && p->p_ucred->cr_uid != 0)
+ tp->t_cflag &= ~CLOCAL;
+#endif
+
+ if (error >= 0)
+ return (error);
+
+ iobase = com->iobase;
+ s = spltty();
+ switch (cmd) {
+ case TIOCSBRK:
+ outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK);
+ break;
+ case TIOCCBRK:
+ outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
+ break;
+ case TIOCSDTR:
+ commctl(com, MCR_DTR, DMBIS);
+ break;
+ case TIOCCDTR:
+ commctl(com, MCR_DTR, DMBIC);
+ break;
+ case TIOCMSET:
+ commctl(com, tiocm_xxx2mcr(*(int *)data), DMSET);
+ break;
+ case TIOCMBIS:
+ commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIS);
+ break;
+ case TIOCMBIC:
+ commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIC);
+ break;
+ case TIOCMGET:
+ tiocm_xxx = TIOCM_LE; /* XXX - always enabled while open */
+ mcr = com->mcr_image;
+ if (mcr & MCR_DTR)
+ tiocm_xxx |= TIOCM_DTR;
+ if (mcr & MCR_RTS)
+ tiocm_xxx |= TIOCM_RTS;
+ msr = com->prev_modem_status;
+ if (msr & MSR_CTS)
+ tiocm_xxx |= TIOCM_CTS;
+ if (msr & MSR_DCD)
+ tiocm_xxx |= TIOCM_CD;
+ if (msr & MSR_DSR)
+ tiocm_xxx |= TIOCM_DSR;
+ /*
+ * XXX - MSR_RI is naturally volatile, and we make MSR_TERI
+ * more volatile by reading the modem status a lot. Perhaps
+ * we should latch both bits until the status is read here.
+ */
+ if (msr & (MSR_RI | MSR_TERI))
+ tiocm_xxx |= TIOCM_RI;
+ *(int *)data = tiocm_xxx;
+ break;
+#ifdef COM_BIDIR
+ case TIOCMSBIDIR:
+ /* must be root to set bidir. capability */
+ error = suser(p->p_ucred, &p->p_acflag);
+ if (error != 0) {
+ splx(s);
+ return(EPERM);
+ }
+
+ /* if it's the console, can't do it (XXX why?) */
+ if (UNIT(dev) == comconsole) {
+ splx(s);
+ return(ENOTTY);
+ }
+
+#if 0
+ /* XXX - can't do the next, for obvious reasons...
+ * but there are problems to be looked at...
+ */
+ /* if the port is active, don't do it */
+ if (com->active) {
+ splx(s);
+ return(EBUSY);
+ }
+#endif
+
+ com->bidir = *(int *)data;
+ break;
+ case TIOCMGBIDIR:
+ *(int *)data = com->bidir;
+ break;
+#endif /* COM_BIDIR */
+#if 0
+ case TIOCMSDTRWAIT:
+ /* must be root since the wait applies to following logins */
+ error = suser(p->p_ucred, &p->p_acflag);
+ if (error != 0) {
+ splx(s);
+ return(EPERM);
+ }
+
+ /* if it's the console, can't do it (XXX why?) */
+ if (UNIT(dev) == comconsole) {
+ splx(s);
+ return(ENOTTY);
+ }
+ com->dtr_wait = *(int *)data;
+ break;
+ case TIOCMGDTRWAIT:
+ *(int *)data = com->dtr_wait;
+ break;
+#endif
+#ifdef TIOCTIMESTAMP
+ case TIOCTIMESTAMP:
+ com->do_timestamp = TRUE;
+ *(struct timeval *)data = com->timestamp;
+ break;
+#endif
+ default:
+ splx(s);
+ return (ENOTTY);
+ }
+ splx(s);
+ return (0);
+}
+
+/* cancel pending output */
+static void
+comflush(com)
+ struct com_s *com;
+{
+ struct clist *rbp;
+
+ disable_intr();
+ if (com->state & CS_ODONE)
+ com_events -= LOTS_OF_EVENTS;
+ com->state &= ~(CS_ODONE | CS_BUSY);
+ enable_intr();
+ while( getc( TB_OUT(com->tp)) != -1);
+ com->ocount = 0;
+ com->tp->t_state &= ~TS_BUSY;
+}
+
+void
+siopoll()
+{
+#ifdef OLD_INTERRUPT_HANDLING
+ static bool_t awake = FALSE;
+ int s;
+#endif
+ int unit;
+
+ if (com_events == 0)
+ return;
+
+#ifdef OLD_INTERRUPT_HANDLING
+ disable_intr();
+ if (awake) {
+ enable_intr();
+ return;
+ }
+ awake = TRUE;
+ enable_intr();
+ s = spltty();
+#endif
+
+repeat:
+ for (unit = 0; unit < NSIO; ++unit) {
+ u_char *buf;
+ struct com_s *com;
+ u_char *ibuf;
+ int incc;
+ struct tty *tp;
+
+ com = com_addr(unit);
+ if (com == NULL)
+ continue;
+ tp = com->tp;
+#ifdef DONT_MALLOC_TTYS
+ if (tp == NULL)
+ continue;
+#endif
+
+ /* switch the role of the low-level input buffers */
+ if (com->iptr == (ibuf = com->ibuf)) {
+ buf = NULL; /* not used, but compiler can't tell */
+ incc = 0;
+ } else {
+ buf = ibuf;
+ disable_intr();
+ incc = com->iptr - buf;
+ com_events -= incc;
+ if (ibuf == com->ibuf1)
+ ibuf = com->ibuf2;
+ else
+ ibuf = com->ibuf1;
+ com->ibufend = ibuf + RS_IBUFSIZE;
+ com->ihighwater = ibuf + RS_IHIGHWATER;
+ com->iptr = ibuf;
+
+ /*
+ * There is now room for another low-level buffer full
+ * of input, so enable RTS if it is now disabled and
+ * there is room in the high-level buffer.
+ */
+ /*
+ * XXX this used not to look at CS_RTS_IFLOW. The
+ * change is to allow full control of MCR_RTS via
+ * ioctls after turning CS_RTS_IFLOW off. Check
+ * for races. We shouldn't allow the ioctls while
+ * CS_RTS_IFLOW is on.
+ */
+ if ((com->state & CS_RTS_IFLOW)
+ && !(com->mcr_image & MCR_RTS) /*
+ && !(tp->t_state & TS_RTS_IFLOW) */)
+ outb(com->modem_ctl_port,
+ com->mcr_image |= MCR_RTS);
+ enable_intr();
+ com->ibuf = ibuf;
+ }
+
+ if (com->state & CS_CHECKMSR) {
+ u_char delta_modem_status;
+
+ disable_intr();
+ delta_modem_status = com->last_modem_status
+ ^ com->prev_modem_status;
+ com->prev_modem_status = com->last_modem_status;
+ com_events -= LOTS_OF_EVENTS;
+ com->state &= ~CS_CHECKMSR;
+ enable_intr();
+ if (delta_modem_status & MSR_DCD && !FAKE_DCD(unit)) {
+ if (com->prev_modem_status & MSR_DCD) {
+ (*linesw[tp->t_line].l_modem)(tp, 1);
+#ifdef COM_BIDIR
+ wakeup((caddr_t) &com->active_in);
+#endif /* COM_BIDIR */
+ } else
+ (*linesw[tp->t_line].l_modem)(tp, 0);
+ }
+ }
+
+ /* XXX */
+ if (TRUE) {
+ u_int delta;
+ int errnum;
+ u_long total;
+
+ for (errnum = 0; errnum < CE_NTYPES; ++errnum) {
+ disable_intr();
+ delta = com->delta_error_counts[errnum];
+ com->delta_error_counts[errnum] = 0;
+ enable_intr();
+ if (delta != 0) {
+ total =
+ com->error_counts[errnum] += delta;
+ log(LOG_WARNING,
+ "sio%d: %u more %s%s (total %lu)\n",
+ unit, delta, error_desc[errnum],
+ delta == 1 ? "" : "s", total);
+ }
+ }
+ }
+ if (com->state & CS_ODONE) {
+ comflush(com);
+ /* XXX - why isn't the table used for t_line == 0? */
+ if (tp->t_line != 0)
+ (*linesw[tp->t_line].l_start)(tp);
+ else
+ comstart(tp);
+ }
+ if (incc <= 0 || !(tp->t_state & TS_ISOPEN))
+ continue;
+ if (com->state & CS_RTS_IFLOW
+ && TB_RAW(tp)->c_cc + incc >= RB_I_HIGH_WATER /*
+ && !(tp->t_state & TS_RTS_IFLOW) */
+ /*
+ * XXX - need RTS flow control for all line disciplines.
+ * Only have it in standard one now.
+ */
+ && linesw[tp->t_line].l_rint == ttyinput) {
+/* tp->t_state |= TS_RTS_IFLOW; */
+ ttstart(tp);
+ }
+#if 0
+ /*
+ * Avoid the grotesquely inefficient lineswitch routine
+ * (ttyinput) in "raw" mode. It usually takes about 450
+ * instructions (that's without canonical processing or echo!).
+ * slinput is reasonably fast (usually 40 instructions plus
+ * call overhead).
+ */
+ if (!(tp->t_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP
+ | IXOFF | IXON))
+ && !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG
+ | PENDIN))
+ && !(tp->t_state & (TS_CNTTB | TS_LNCH))
+ && linesw[tp->t_line].l_rint == ttyinput) {
+ tk_nin += incc;
+ tk_rawcc += incc;
+ tp->t_rawcc += incc;
+ com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
+ += incc - rb_write(TB_RAW(tp), (char *) buf,
+ incc);
+ ttwakeup(tp);
+ if (tp->t_state & TS_TTSTOP
+ && (tp->t_iflag & IXANY
+ || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
+ tp->t_state &= ~TS_TTSTOP;
+ tp->t_lflag &= ~FLUSHO;
+ ttstart(tp);
+ }
+ } else {
+#endif
+ do {
+ u_char line_status;
+ int recv_data;
+
+ line_status = (u_char) buf[CE_INPUT_OFFSET];
+ recv_data = (u_char) *buf++;
+ if (line_status
+ & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) {
+ if (line_status & LSR_BI)
+ recv_data |= TTY_BI;
+ if (line_status & LSR_FE)
+ recv_data |= TTY_FE;
+ if (line_status & LSR_OE)
+ recv_data |= TTY_OE;
+ if (line_status & LSR_PE)
+ recv_data |= TTY_PE;
+ }
+ (*linesw[tp->t_line].l_rint)(recv_data, tp);
+ } while (--incc > 0);
+#if 0
+ }
+#endif
+ if (com_events == 0)
+ break;
+ }
+ if (com_events >= LOTS_OF_EVENTS)
+ goto repeat;
+
+#ifdef OLD_INTERRUPT_HANDLING
+ splx(s);
+ awake = FALSE;
+#endif
+}
+
+static int
+comparam(tp, t)
+ struct tty *tp;
+ struct termios *t;
+{
+ u_int cfcr;
+ int cflag;
+ struct com_s *com;
+ int divisor;
+ int error;
+ Port_t iobase;
+ int s;
+ int unit;
+
+ /* check requested parameters */
+ divisor = ttspeedtab(t->c_ospeed, comspeedtab);
+ if (t->c_ispeed == 0)
+ t->c_ispeed = t->c_ospeed;
+ if (divisor < 0 || t->c_ispeed != t->c_ospeed)
+ return (EINVAL);
+
+ /* parameters are OK, convert them to the com struct and the device */
+ unit = UNIT(tp->t_dev);
+ com = com_addr(unit);
+ iobase = com->iobase;
+ s = spltty();
+ if (divisor == 0)
+ commctl(com, MCR_DTR, DMBIC); /* hang up line */
+ else
+ commctl(com, MCR_DTR, DMBIS);
+ cflag = t->c_cflag;
+ switch (cflag & CSIZE) {
+ case CS5:
+ cfcr = CFCR_5BITS;
+ break;
+ case CS6:
+ cfcr = CFCR_6BITS;
+ break;
+ case CS7:
+ cfcr = CFCR_7BITS;
+ break;
+ default:
+ cfcr = CFCR_8BITS;
+ break;
+ }
+ if (cflag & PARENB) {
+ cfcr |= CFCR_PENAB;
+ if (!(cflag & PARODD))
+ cfcr |= CFCR_PEVEN;
+ }
+ if (cflag & CSTOPB)
+ cfcr |= CFCR_STOPB;
+
+ /*
+ * Some UARTs lock up if the divisor latch registers are selected
+ * while the UART is doing output (they refuse to transmit anything
+ * more until given a hard reset). Fix this by stopping filling
+ * the device buffers and waiting for them to drain. Reading the
+ * line status port outside of siointr1() might lose some receiver
+ * error bits, but that is acceptable here.
+ */
+ disable_intr();
+retry:
+ com->state &= ~CS_TTGO;
+ enable_intr();
+ while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY)) {
+ error = ttysleep(tp, (caddr_t)TB_RAW(tp), TTIPRI | PCATCH,
+ "sioparam", 1);
+ if (error != 0 && error != EAGAIN) {
+ if (!(tp->t_state & TS_TTSTOP)) {
+ disable_intr();
+ com->state |= CS_TTGO;
+ enable_intr();
+ }
+ splx(s);
+ return (error);
+ }
+ }
+
+ disable_intr(); /* very important while com_data is hidden */
+
+ /*
+ * XXX - clearing CS_TTGO is not sufficient to stop further output,
+ * because siopoll() calls comstart() which usually sets it again
+ * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be
+ * sufficient, for similar reasons.
+ */
+ if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY))
+ goto retry;
+
+ if (divisor != 0) {
+ outb(iobase + com_cfcr, cfcr | CFCR_DLAB);
+ outb(iobase + com_dlbl, divisor & 0xFF);
+ outb(iobase + com_dlbh, (u_int) divisor >> 8);
+ }
+ outb(iobase + com_cfcr, com->cfcr_image = cfcr);
+ if (!(tp->t_state & TS_TTSTOP))
+ com->state |= CS_TTGO;
+ if (cflag & CRTS_IFLOW)
+ com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */
+ else
+ com->state &= ~CS_RTS_IFLOW;
+
+ /*
+ * Set up state to handle output flow control.
+ * XXX - worth handling MDMBUF (DCD) flow control at the lowest level?
+ * Now has 16+ msec latency, while CTS flow has 50- usec latency.
+ */
+ com->state &= ~CS_CTS_OFLOW;
+ com->state |= CS_ODEVREADY;
+ if (cflag & CCTS_OFLOW) {
+ com->state |= CS_CTS_OFLOW;
+ if (!(com->last_modem_status & MSR_CTS))
+ com->state &= ~CS_ODEVREADY;
+ }
+
+ /*
+ * Recover from fiddling with CS_TTGO. We used to call siointr1()
+ * unconditionally, but that defeated the careful discarding of
+ * stale input in sioopen().
+ *
+ * XXX sioopen() is not careful waiting for carrier for the callout
+ * case.
+ */
+ if (com->state >= (CS_BUSY | CS_TTGO))
+ siointr1(com);
+
+ enable_intr();
+ splx(s);
+ return (0);
+}
+
+static void
+comstart(tp)
+ struct tty *tp;
+{
+ struct com_s *com;
+ int s;
+ int unit;
+
+ unit = UNIT(tp->t_dev);
+ com = com_addr(unit);
+ s = spltty();
+ disable_intr();
+ if (tp->t_state & TS_TTSTOP)
+ com->state &= ~CS_TTGO;
+ else
+ com->state |= CS_TTGO;
+#if 0
+ if (tp->t_state & TS_RTS_IFLOW) {
+ if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW)
+ outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS);
+ } else {
+#endif
+ /*
+ * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it
+ * appropriately in comparam() if RTS-flow is being changed.
+ * Check for races.
+ */
+ if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater)
+ outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
+#if 0
+ }
+#endif
+ enable_intr();
+ if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))
+ goto out;
+ if (TB_OUT(tp)->c_cc <= tp->t_lowat) {
+ if (tp->t_state & TS_ASLEEP) {
+ tp->t_state &= ~TS_ASLEEP;
+ wakeup((caddr_t)TB_OUT(tp));
+ }
+ selwakeup(&tp->t_wsel);
+ }
+ if (com->ocount != 0) {
+ disable_intr();
+ siointr1(com);
+ enable_intr();
+ } else if (TB_OUT(tp)->c_cc != 0) {
+ tp->t_state |= TS_BUSY;
+ disable_intr();
+ com->ocount = q_to_b(TB_OUT(tp), com->obuf, sizeof com->obuf);
+ com->optr = com->obuf;
+ com->obufend = com->obuf + com->ocount;
+ com->state |= CS_BUSY;
+ siointr1(com); /* fake interrupt to start output */
+ enable_intr();
+ }
+out:
+ splx(s);
+}
+
+void
+siostop(tp, rw)
+ struct tty *tp;
+ int rw;
+{
+ struct com_s *com;
+
+ com = com_addr(UNIT(tp->t_dev));
+ if (rw & FWRITE)
+ comflush(com);
+ disable_intr();
+ if (rw & FREAD) {
+ com_events -= (com->iptr - com->ibuf);
+ com->iptr = com->ibuf;
+ }
+ if (tp->t_state & TS_TTSTOP)
+ com->state &= ~CS_TTGO;
+ else
+ com->state |= CS_TTGO;
+ enable_intr();
+}
+
+int
+sioselect(dev, rw, p)
+ dev_t dev;
+ int rw;
+ struct proc *p;
+{
+ return (ttselect(dev & ~COM_MINOR_MAGIC_MASK, rw, p));
+}
+
+static void
+commctl(com, bits, how)
+ struct com_s *com;
+ int bits;
+ int how;
+{
+ disable_intr();
+ switch (how) {
+ case DMSET:
+ outb(com->modem_ctl_port,
+ com->mcr_image = bits | (com->mcr_image & MCR_IENABLE));
+ break;
+ case DMBIS:
+ outb(com->modem_ctl_port, com->mcr_image |= bits);
+ break;
+ case DMBIC:
+ outb(com->modem_ctl_port, com->mcr_image &= ~bits);
+ break;
+ }
+ enable_intr();
+}
+
+static void
+comwakeup(chan, ticks)
+ caddr_t chan;
+ int ticks;
+{
+ int unit;
+
+ timeout((timeout_func_t)comwakeup, (caddr_t) NULL, hz / 100);
+
+ if (com_events != 0) {
+#ifndef OLD_INTERRUPT_HANDLING
+ int s = spltty();
+#endif
+ siopoll();
+#ifndef OLD_INTERRUPT_HANDLING
+ splx(s);
+#endif
+ }
+
+ /* recover from lost output interrupts */
+ for (unit = 0; unit < NSIO; ++unit) {
+ struct com_s *com;
+
+ com = com_addr(unit);
+ if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) {
+ disable_intr();
+ siointr1(com);
+ enable_intr();
+ }
+ }
+}
+
+#ifdef OLD_INTERRUPT_HANDLING
+void
+softsio1()
+{
+ siopoll();
+}
+#endif
+
+/*
+ * Following are all routines needed for SIO to act as console
+ */
+#include "i386/i386/cons.h"
+
+struct siocnstate {
+ u_char dlbl;
+ u_char dlbh;
+ u_char ier;
+ u_char cfcr;
+ u_char mcr;
+};
+
+static Port_t siocniobase;
+
+static void
+siocntxwait()
+{
+ int timo;
+
+ /*
+ * Wait for any pending transmission to finish. Required to avoid
+ * the UART lockup bug when the speed is changed, and for normal
+ * transmits.
+ */
+ timo = 100000;
+ while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY) && --timo != 0)
+ ;
+}
+
+static void
+siocnopen(sp)
+ struct siocnstate *sp;
+{
+ int divisor;
+ Port_t iobase;
+
+ /*
+ * Save all the device control registers except the fifo register
+ * and set our default ones (cs8 -parenb speed=comdefaultrate).
+ * We can't save the fifo register since it is read-only.
+ */
+ iobase = siocniobase;
+ sp->ier = inb(iobase + com_ier);
+ outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */
+ siocntxwait();
+ sp->cfcr = inb(iobase + com_cfcr);
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ sp->dlbl = inb(iobase + com_dlbl);
+ sp->dlbh = inb(iobase + com_dlbh);
+ divisor = ttspeedtab(comdefaultrate, comspeedtab);
+ outb(iobase + com_dlbl, divisor & 0xFF);
+ outb(iobase + com_dlbh, (u_int) divisor >> 8);
+ outb(iobase + com_cfcr, CFCR_8BITS);
+ sp->mcr = inb(iobase + com_mcr);
+ outb(iobase + com_mcr, MCR_DTR | MCR_RTS);
+}
+
+static void
+siocnclose(sp)
+ struct siocnstate *sp;
+{
+ Port_t iobase;
+
+ /*
+ * Restore the device control registers.
+ */
+ siocntxwait();
+ iobase = siocniobase;
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ outb(iobase + com_dlbl, sp->dlbl);
+ outb(iobase + com_dlbh, sp->dlbh);
+ outb(iobase + com_cfcr, sp->cfcr);
+ /*
+ * XXX damp osicllations of MCR_DTR or MCR_RTS by not restoring them.
+ */
+ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS);
+ outb(iobase + com_ier, sp->ier);
+}
+
+void
+siocnprobe(cp)
+ struct consdev *cp;
+{
+ int unit;
+
+ /* locate the major number */
+ /* XXX - should be elsewhere since KGDB uses it */
+ for (commajor = 0; commajor < nchrdev; commajor++)
+ if (cdevsw[commajor].d_open == sioopen)
+ break;
+
+ /* XXX: ick */
+ unit = UNIT(CONUNIT);
+ siocniobase = CONADDR;
+
+ /* make sure hardware exists? XXX */
+
+ /* initialize required fields */
+ cp->cn_dev = makedev(commajor, unit);
+#ifdef COMCONSOLE
+ cp->cn_pri = CN_REMOTE; /* Force a serial port console */
+#else
+ cp->cn_pri = CN_NORMAL;
+#endif
+}
+
+void
+siocninit(cp)
+ struct consdev *cp;
+{
+ /*
+ * XXX can delete more comconsole stuff now that i/o routines are
+ * fairly reentrant.
+ */
+ comconsole = UNIT(cp->cn_dev);
+}
+
+int
+siocngetc(dev)
+ dev_t dev;
+{
+ int c;
+ Port_t iobase;
+ int s;
+ struct siocnstate sp;
+
+ iobase = siocniobase;
+ s = spltty();
+ siocnopen(&sp);
+ while (!(inb(iobase + com_lsr) & LSR_RXRDY))
+ ;
+ c = inb(iobase + com_data);
+ siocnclose(&sp);
+ splx(s);
+ return (c);
+}
+
+void
+siocnputc(dev, c)
+ dev_t dev;
+ int c;
+{
+ int s;
+ struct siocnstate sp;
+
+ s = spltty();
+ siocnopen(&sp);
+ siocntxwait();
+ outb(siocniobase + com_data, c);
+ siocnclose(&sp);
+ splx(s);
+}
+
+#endif /* NSIO > 0 */
diff --git a/sys/isa/sioreg.h b/sys/isa/sioreg.h
new file mode 100644
index 0000000..4b0f1b6
--- /dev/null
+++ b/sys/isa/sioreg.h
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)comreg.h 7.2 (Berkeley) 5/9/91
+ * $Id$
+ */
+
+
+/* 16 bit baud rate divisor (lower byte in dca_data, upper in dca_ier) */
+#define COMBRD(x) (1843200 / (16*(x)))
+
+/* interrupt enable register */
+#define IER_ERXRDY 0x1
+#define IER_ETXRDY 0x2
+#define IER_ERLS 0x4
+#define IER_EMSC 0x8
+
+/* interrupt identification register */
+#define IIR_IMASK 0xf
+#define IIR_RXTOUT 0xc
+#define IIR_RLS 0x6
+#define IIR_RXRDY 0x4
+#define IIR_TXRDY 0x2
+#define IIR_NOPEND 0x1
+#define IIR_MLSC 0x0
+#define IIR_FIFO_MASK 0xc0 /* set if FIFOs are enabled */
+
+/* fifo control register */
+#define FIFO_ENABLE 0x01
+#define FIFO_RCV_RST 0x02
+#define FIFO_XMT_RST 0x04
+#define FIFO_DMA_MODE 0x08
+#define FIFO_TRIGGER_1 0x00
+#define FIFO_TRIGGER_4 0x40
+#define FIFO_TRIGGER_8 0x80
+#define FIFO_TRIGGER_14 0xc0
+
+/* character format control register */
+#define CFCR_DLAB 0x80
+#define CFCR_SBREAK 0x40
+#define CFCR_PZERO 0x30
+#define CFCR_PONE 0x20
+#define CFCR_PEVEN 0x10
+#define CFCR_PODD 0x00
+#define CFCR_PENAB 0x08
+#define CFCR_STOPB 0x04
+#define CFCR_8BITS 0x03
+#define CFCR_7BITS 0x02
+#define CFCR_6BITS 0x01
+#define CFCR_5BITS 0x00
+
+/* modem control register */
+#define MCR_LOOPBACK 0x10
+#define MCR_IENABLE 0x08
+#define MCR_DRS 0x04
+#define MCR_RTS 0x02
+#define MCR_DTR 0x01
+
+/* line status register */
+#define LSR_RCV_FIFO 0x80
+#define LSR_TSRE 0x40
+#define LSR_TXRDY 0x20
+#define LSR_BI 0x10
+#define LSR_FE 0x08
+#define LSR_PE 0x04
+#define LSR_OE 0x02
+#define LSR_RXRDY 0x01
+#define LSR_RCV_MASK 0x1f
+
+/* modem status register */
+#define MSR_DCD 0x80
+#define MSR_RI 0x40
+#define MSR_DSR 0x20
+#define MSR_CTS 0x10
+#define MSR_DDCD 0x08
+#define MSR_TERI 0x04
+#define MSR_DDSR 0x02
+#define MSR_DCTS 0x01
+
+/*
+ * WARNING: Serial console is assumed to be at COM1 address
+ * and CONUNIT must be 0.
+ */
+#define CONADDR (0x3f8)
+#define CONUNIT (0)
diff --git a/sys/isa/syscons.c b/sys/isa/syscons.c
new file mode 100644
index 0000000..a2a931c
--- /dev/null
+++ b/sys/isa/syscons.c
@@ -0,0 +1,2660 @@
+/*-
+ * Copyright (c) 1992-1994 Søren Schmidt
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz and Don Ahn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from:@(#)syscons.c 1.3 940129
+ * $Id: syscons.c,v 1.46 1994/05/25 08:59:56 rgrimes Exp $
+ *
+ */
+
+#if !defined(__FreeBSD__)
+#define FAT_CURSOR
+#endif
+
+#include "param.h"
+#include <sys/systm.h>
+#include "conf.h"
+#include "ioctl.h"
+#include "proc.h"
+#include "user.h"
+#include "tty.h"
+#include "uio.h"
+#include "callout.h"
+#include "kernel.h"
+#include "syslog.h"
+#include "errno.h"
+#include "malloc.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/timerreg.h"
+#include "i386/i386/cons.h"
+#include "machine/console.h"
+#include "machine/psl.h"
+#include "machine/frame.h"
+#include "machine/pc/display.h"
+#include "iso8859.font"
+#include "kbdtables.h"
+#include "sc.h"
+
+#if NSC > 0
+
+#if !defined(NCONS)
+#define NCONS 12
+#endif
+
+/* status flags */
+#define LOCK_KEY_MASK 0x0000F
+#define LED_MASK 0x00007
+#define UNKNOWN_MODE 0x00010
+#define KBD_RAW_MODE 0x00020
+#define SWITCH_WAIT_REL 0x00040
+#define SWITCH_WAIT_ACQ 0x00080
+
+/* video hardware memory addresses */
+#define VIDEOMEM 0x000A0000
+
+/* misc defines */
+#define MAX_ESC_PAR 3
+#define TEXT80x25 1
+#define TEXT80x50 2
+#define COL 80
+#define ROW 25
+#define BELL_DURATION 5
+#define BELL_PITCH 800
+#define TIMER_FREQ 1193182 /* should be in isa.h */
+#define PCBURST 256
+
+/* defines related to hardware addresses */
+#define MONO_BASE 0x3B4 /* crt controller base mono */
+#define COLOR_BASE 0x3D4 /* crt controller base color */
+#define ATC IO_VGA+0x00 /* attribute controller */
+#define TSIDX IO_VGA+0x04 /* timing sequencer idx */
+#define TSREG IO_VGA+0x05 /* timing sequencer data */
+#define PIXMASK IO_VGA+0x06 /* pixel write mask */
+#define PALRADR IO_VGA+0x07 /* palette read address */
+#define PALWADR IO_VGA+0x08 /* palette write address */
+#define PALDATA IO_VGA+0x09 /* palette data register */
+#define GDCIDX IO_VGA+0x0E /* graph data controller idx */
+#define GDCREG IO_VGA+0x0F /* graph data controller data */
+
+/* special characters */
+#define cntlc 0x03
+#define cntld 0x04
+#define bs 0x08
+#define lf 0x0a
+#define cr 0x0d
+#define del 0x7f
+
+typedef struct term_stat {
+ int esc; /* processing escape sequence */
+ int num_param; /* # of parameters to ESC */
+ int last_param; /* last parameter # */
+ int param[MAX_ESC_PAR]; /* contains ESC parameters */
+ int cur_attr; /* current attributes */
+ int std_attr; /* normal attributes */
+ int rev_attr; /* reverse attributes */
+} term_stat;
+
+typedef struct scr_stat {
+ u_short *crt_base; /* address of screen memory */
+ u_short *scr_buf; /* buffer when off screen */
+ u_short *crtat; /* cursor address */
+ int xpos; /* current X position */
+ int ypos; /* current Y position */
+ int xsize; /* X size */
+ int ysize; /* Y size */
+ term_stat term; /* terminal emulation stuff */
+ char cursor_start; /* cursor start line # */
+ char cursor_end; /* cursor end line # */
+ u_char border; /* border color */
+ u_short bell_duration;
+ u_short bell_pitch;
+ u_short status; /* status (bitfield) */
+ u_short mode; /* mode */
+ pid_t pid; /* pid of controlling proc */
+ struct proc *proc; /* proc* of controlling proc */
+ struct vt_mode smode; /* switch mode */
+} scr_stat;
+
+typedef struct default_attr {
+ int std_attr; /* normal attributes */
+ int rev_attr; /* reverse attributes */
+} default_attr;
+
+static default_attr user_default = {
+ (FG_LIGHTGREY | BG_BLACK) << 8,
+ (FG_BLACK | BG_LIGHTGREY) << 8
+};
+
+static default_attr kernel_default = {
+ (FG_WHITE | BG_BLACK) << 8,
+ (FG_BLACK | BG_LIGHTGREY) << 8
+};
+
+#define CONSOLE_BUFFER_SIZE 1024
+int console_buffer_count;
+char console_buffer[CONSOLE_BUFFER_SIZE];
+
+static scr_stat console[NCONS];
+static scr_stat *cur_console = &console[0];
+static scr_stat *new_scp, *old_scp;
+static term_stat kernel_console;
+static default_attr *current_default;
+static int switch_in_progress = 0;
+static u_short *crtat = 0;
+static u_int crtc_addr = MONO_BASE;
+static char crtc_vga = 0;
+static u_char shfts = 0, ctls = 0, alts = 0, agrs = 0, metas = 0;
+static u_char nlkcnt = 0, clkcnt = 0, slkcnt = 0, alkcnt = 0;
+static char palette[3*256];
+static const u_int n_fkey_tab = sizeof(fkey_tab) / sizeof(*fkey_tab);
+static int cur_cursor_pos = -1;
+static char in_putc = 0;
+static char polling = 0;
+static int delayed_next_scr;
+static char saved_console = -1; /* saved console number */
+static long scrn_blank_time = 0; /* screen saver timout value */
+static int scrn_blanked = 0; /* screen saver active flag */
+static int scrn_saver = 0; /* screen saver routine */
+static long scrn_time_stamp;
+static u_char scr_map[256];
+extern int hz;
+extern struct timeval time;
+
+/* function prototypes */
+int pcprobe(struct isa_device *dev);
+int pcattach(struct isa_device *dev);
+int pcopen(dev_t dev, int flag, int mode, struct proc *p);
+int pcclose(dev_t dev, int flag, int mode, struct proc *p);
+int pcread(dev_t dev, struct uio *uio, int flag);
+int pcwrite(dev_t dev, struct uio *uio, int flag);
+int pcparam(struct tty *tp, struct termios *t);
+int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p);
+void pcxint(dev_t dev);
+void pcstart(struct tty *tp);
+void pccnprobe(struct consdev *cp);
+void pccninit(struct consdev *cp);
+void pccnputc(dev_t dev, char c);
+int pccngetc(dev_t dev);
+void scintr(int unit);
+int pcmmap(dev_t dev, int offset, int nprot);
+u_int sgetc(int noblock);
+int getchar(void);
+static void scinit(void);
+static void scput(u_char c);
+static u_int scgetc(int noblock);
+static struct tty *get_tty_ptr(dev_t dev);
+static scr_stat *get_scr_stat(dev_t dev);
+static int get_scr_num();
+static void cursor_shape(int start, int end);
+static void get_cursor_shape(int *start, int *end);
+static void cursor_pos(int force);
+static void clear_screen(scr_stat *scp);
+static int switch_scr(u_int next_scr);
+static void exchange_scr(void);
+static void move_crsr(scr_stat *scp, int x, int y);
+static void move_up(u_short *s, u_short *d, u_int len);
+static void move_down(u_short *s, u_short *d, u_int len);
+static void scan_esc(scr_stat *scp, u_char c);
+static void ansi_put(scr_stat *scp, u_char c);
+static u_char *get_fstr(u_int c, u_int *len);
+static void update_leds(int which);
+static void kbd_wait(void);
+static void kbd_cmd(u_char command);
+static void kbd_cmd2(u_char command, u_char arg);
+static int kbd_reply(void);
+static void set_mode(scr_stat *scp);
+static void set_border(int color);
+static void load_font(int segment, int size, char* font);
+static void save_palette(void);
+static void load_palette(void);
+static void change_winsize(struct tty *tp, int x, int y);
+
+
+/* available screen savers */
+
+static void none_saver(int test);
+static void blank_saver(int test);
+static void fade_saver(int test);
+static void star_saver(int test);
+static void snake_saver(int test);
+
+static const struct {
+ char *name;
+ void (*routine)();
+} screen_savers[] = {
+ { "none", none_saver }, /* 0 */
+ { "blank", blank_saver }, /* 1 */
+ { "fade", fade_saver }, /* 2 */
+ { "star", star_saver }, /* 3 */
+ { "snake", snake_saver }, /* 4 */
+};
+#define SCRN_SAVER(arg) (*screen_savers[scrn_saver].routine)(arg)
+#define NUM_SCRN_SAVERS (sizeof(screen_savers) / sizeof(screen_savers[0]))
+
+/* OS specific stuff */
+
+#if defined(NetBSD)
+#define VIRTUAL_TTY(x) pc_tty[x] ? (pc_tty[x]) : (pc_tty[x] = ttymalloc())
+#define CONSOLE_TTY pc_tty[NCONS] ? (pc_tty[NCONS]) : (pc_tty[NCONS] = ttymalloc())
+#define frametype struct trapframe
+#define eflags tf_eflags
+extern u_short *Crtat;
+struct tty *pc_tty[NCONS+1];
+int ttrstrt();
+#endif
+
+#if defined(__FreeBSD__)
+#if 0
+#define VIRTUAL_TTY(x) (pccons[x] = ttymalloc(pccons[x]))
+#define CONSOLE_TTY (pccons[NCONS] = ttymalloc(pccons[NCONS]))
+struct tty *pccons[NCONS+1];
+#else
+#define VIRTUAL_TTY(x) &pccons[x]
+#define CONSOLE_TTY &pccons[NCONS]
+struct tty pccons[NCONS+1];
+#endif
+#define timeout_t timeout_func_t
+#define frametype struct trapframe
+#define eflags tf_eflags
+#define MONO_BUF (KERNBASE+0xB0000)
+#define CGA_BUF (KERNBASE+0xB8000)
+#endif
+
+#if defined(__386BSD__) && !defined(__FreeBSD__)
+#define VIRTUAL_TTY(x) &pccons[x]
+#define CONSOLE_TTY &pccons[NCONS]
+#define frametype struct syscframe
+#define eflags sf_eflags
+#define timeout_t caddr_t
+#define MONO_BUF (0xFE0B0000)
+#define CGA_BUF (0xFE0B8000)
+struct tty pccons[NCONS+1];
+#endif
+
+#if defined(__386BSD__) || defined(__FreeBSD__)
+u_short *Crtat = (u_short *)MONO_BUF;
+void consinit(void) {scinit();}
+#include "ddb.h"
+#if NDDB > 0
+#define DDB 1
+#endif
+#endif
+
+struct isa_driver scdriver = {
+ pcprobe, pcattach, "sc",
+};
+
+
+int pcprobe(struct isa_device *dev)
+{
+ /* Enable interrupts and keyboard controller */
+ kbd_wait();
+ outb(KB_STAT, KB_WRITE);
+ kbd_cmd(0x4D);
+
+ /* Start keyboard stuff RESET */
+ for (;;) {
+ kbd_cmd(KB_RESET);
+ if (kbd_reply() == KB_ACK && /* command accepted */
+ kbd_reply() == 0xaa) /* self test passed */
+ break;
+ printf("Keyboard reset failed\n");
+ }
+ return (IO_KBDSIZE);
+}
+
+
+int pcattach(struct isa_device *dev)
+{
+ scr_stat *scp;
+ int start = -1, end = -1, i;
+
+ printf("sc%d: ", dev->id_unit);
+ if (crtc_vga)
+ if (crtc_addr == MONO_BASE)
+ printf("VGA mono");
+ else
+ printf("VGA color");
+ else
+ if (crtc_addr == MONO_BASE)
+ printf("MDA/hercules");
+ else
+ printf("CGA/EGA");
+
+ if (NCONS > 1)
+ printf(" <%d virtual consoles>\n", NCONS);
+ else
+ printf("\n");
+#if defined(FAT_CURSOR)
+ start = 0;
+ end = 18;
+ if (crtc_vga) {
+#else
+ if (crtc_vga) {
+ get_cursor_shape(&start, &end);
+#endif
+ save_palette();
+ load_font(0, 16, font_8x16);
+ load_font(1, 8, font_8x8);
+ load_font(2, 14, font_8x14);
+ }
+ current_default = &user_default;
+ for (i = 0; i < NCONS; i++) {
+ scp = &console[i];
+ scp->scr_buf = (u_short *)malloc(COL * ROW * 2, M_DEVBUF, M_NOWAIT);
+ scp->mode = TEXT80x25;
+ scp->term.esc = 0;
+ scp->term.std_attr = current_default->std_attr;
+ scp->term.rev_attr = current_default->rev_attr;
+ scp->term.cur_attr = scp->term.std_attr;
+ scp->border = BG_BLACK;
+ scp->cursor_start = start;
+ scp->cursor_end = end;
+ scp->xsize = COL;
+ scp->ysize = ROW;
+ scp->bell_pitch = BELL_PITCH;
+ scp->bell_duration = BELL_DURATION;
+ scp->status = 0;
+ scp->pid = 0;
+ scp->proc = NULL;
+ scp->smode.mode = VT_AUTO;
+ if (i > 0) {
+ scp->crt_base = scp->crtat = scp->scr_buf;
+ fillw(scp->term.cur_attr|scr_map[0x20], scp->scr_buf, COL*ROW);
+ }
+ }
+ /* get cursor going */
+#if defined(FAT_CURSOR)
+ cursor_shape(console[0].cursor_start,
+ console[0].cursor_end);
+#endif
+ cursor_pos(1);
+ return 0;
+}
+
+
+static struct tty *get_tty_ptr(dev_t dev)
+{
+ int unit = minor(dev);
+
+ if (unit > NCONS)
+ return(NULL);
+ if (unit == NCONS)
+ return(CONSOLE_TTY);
+ return(VIRTUAL_TTY(unit));
+}
+
+
+static scr_stat *get_scr_stat(dev_t dev)
+{
+ int unit = minor(dev);
+
+ if (unit > NCONS)
+ return(NULL);
+ if (unit == NCONS)
+ return(&console[0]);
+ return(&console[unit]);
+}
+
+
+static int get_scr_num()
+{
+ int i = 0;
+
+ while ((i < NCONS) && (cur_console != &console[i])) i++;
+ return i < NCONS ? i : 0;
+}
+
+int pcopen(dev_t dev, int flag, int mode, struct proc *p)
+{
+ struct tty *tp = get_tty_ptr(dev);
+
+ if (!tp)
+ return(ENXIO);
+
+ tp->t_oproc = pcstart;
+ tp->t_param = pcparam;
+ tp->t_dev = dev;
+ if (!(tp->t_state & TS_ISOPEN)) {
+ tp->t_state |= TS_WOPEN;
+ ttychars(tp);
+ tp->t_iflag = TTYDEF_IFLAG;
+ tp->t_oflag = TTYDEF_OFLAG;
+ tp->t_cflag = TTYDEF_CFLAG;
+ tp->t_lflag = TTYDEF_LFLAG;
+ tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
+ pcparam(tp, &tp->t_termios);
+ ttsetwater(tp);
+ } else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0)
+ return(EBUSY);
+ tp->t_state |= TS_CARR_ON;
+ tp->t_cflag |= CLOCAL;
+ return((*linesw[tp->t_line].l_open)(dev, tp));
+}
+
+
+int pcclose(dev_t dev, int flag, int mode, struct proc *p)
+{
+ struct tty *tp = get_tty_ptr(dev);
+ struct scr_stat *scp;
+
+ if (!tp)
+ return(ENXIO);
+ if (minor(dev) < NCONS) {
+ scp = get_scr_stat(tp->t_dev);
+ if (scp->status & SWITCH_WAIT_ACQ)
+ wakeup((caddr_t)&scp->smode);
+ scp->pid = 0;
+ scp->proc = NULL;
+ scp->smode.mode = VT_AUTO;
+ }
+ (*linesw[tp->t_line].l_close)(tp, flag);
+ ttyclose(tp);
+ return(0);
+}
+
+
+int pcread(dev_t dev, struct uio *uio, int flag)
+{
+ struct tty *tp = get_tty_ptr(dev);
+
+ if (!tp)
+ return(ENXIO);
+ return((*linesw[tp->t_line].l_read)(tp, uio, flag));
+}
+
+
+int pcwrite(dev_t dev, struct uio *uio, int flag)
+{
+ struct tty *tp = get_tty_ptr(dev);
+
+ if (!tp)
+ return(ENXIO);
+ return((*linesw[tp->t_line].l_write)(tp, uio, flag));
+}
+
+
+/*
+ * Got a console interrupt, keyboard action !
+ * Catch the character, and see who it goes to.
+ */
+void scintr(int unit)
+{
+ static struct tty *cur_tty;
+ int c, len;
+ u_char *cp;
+
+ /* make screensaver happy */
+ scrn_time_stamp = time.tv_sec;
+ if (scrn_blanked)
+ SCRN_SAVER(0);
+
+ c = scgetc(1);
+
+ cur_tty = VIRTUAL_TTY(get_scr_num());
+ if (!(cur_tty->t_state & TS_ISOPEN))
+ cur_tty = CONSOLE_TTY;
+
+ if (!(cur_tty->t_state & TS_ISOPEN) || polling)
+ return;
+
+ switch (c & 0xff00) {
+ case 0x0000: /* normal key */
+ (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty);
+ break;
+ case NOKEY: /* nothing there */
+ break;
+ case FKEY: /* function key, return string */
+ if (cp = get_fstr((u_int)c, (u_int *)&len)) {
+ while (len-- > 0)
+ (*linesw[cur_tty->t_line].l_rint)
+ (*cp++ & 0xFF, cur_tty);
+ }
+ break;
+ case MKEY: /* meta is active, prepend ESC */
+ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty);
+ (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty);
+ break;
+ }
+}
+
+
+/*
+ * Set line parameters
+ */
+int pcparam(struct tty *tp, struct termios *t)
+{
+ int cflag = t->c_cflag;
+
+ /* and copy to tty */
+ tp->t_ispeed = t->c_ispeed;
+ tp->t_ospeed = t->c_ospeed;
+ tp->t_cflag = cflag;
+ return 0;
+}
+
+
+int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
+{
+ int i, error;
+ struct tty *tp;
+ frametype *fp;
+ scr_stat *scp;
+
+ tp = get_tty_ptr(dev);
+ if (!tp)
+ return ENXIO;
+ scp = get_scr_stat(tp->t_dev);
+
+ switch (cmd) { /* process console hardware related ioctl's */
+
+ case CONS_BLANKTIME: /* set screen saver timeout (0 = no saver) */
+ scrn_blank_time = *(int*)data;
+ return 0;
+ case CONS_SSAVER: /* set screen saver */
+ {
+ register ssaver_t *sav = (ssaver_t *)data;
+ if (sav->num < 0 || sav->num >= NUM_SCRN_SAVERS)
+ return EIO;
+ SCRN_SAVER(0);
+ scrn_saver = sav->num;
+ scrn_blank_time = sav->time;
+ return 0;
+ }
+ case CONS_GSAVER: /* get screen saver info */
+ {
+ register ssaver_t *sav = (ssaver_t *)data;
+ if (sav->num < 0)
+ sav->num = scrn_saver;
+ else if (sav->num >= NUM_SCRN_SAVERS)
+ return EIO;
+ sav->time = scrn_blank_time;
+ strcpy(sav->name, screen_savers[sav->num].name);
+ return 0;
+ }
+ case CONS_80x25TEXT: /* set 80x25 text mode */
+ if (!crtc_vga)
+ return ENXIO;
+ scp->mode = TEXT80x25;
+ scp->ysize = 25;
+ free(scp->scr_buf, M_DEVBUF);
+ scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*2,
+ M_DEVBUF, M_NOWAIT);
+ if (scp != cur_console)
+ scp->crt_base = scp->scr_buf;
+ set_mode(scp);
+ clear_screen(scp);
+ change_winsize(tp, scp->xsize, scp->ysize);
+ return 0;
+
+ case CONS_80x50TEXT: /* set 80x50 text mode */
+ if (!crtc_vga)
+ return ENXIO;
+ scp->mode = TEXT80x50;
+ scp->ysize = 50;
+ free(scp->scr_buf, M_DEVBUF);
+ scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*2,
+ M_DEVBUF, M_NOWAIT);
+ if (scp != cur_console)
+ scp->crt_base = scp->scr_buf;
+ set_mode(scp);
+ clear_screen(scp);
+ change_winsize(tp, scp->xsize, scp->ysize);
+ return 0;
+
+ case CONS_GETVERS: /* get version number */
+ *(int*)data = 0x103; /* version 1.3 */
+ return 0;
+
+ case CONS_GETINFO: /* get current (virtual) console info */
+ {
+ vid_info_t *ptr = (vid_info_t*)data;
+ if (ptr->size == sizeof(struct vid_info)) {
+ ptr->m_num = get_scr_num();
+ ptr->mv_col = scp->xpos;
+ ptr->mv_row = scp->ypos;
+ ptr->mv_csz = scp->xsize;
+ ptr->mv_rsz = scp->ysize;
+ ptr->mv_norm.fore = (scp->term.std_attr & 0x0f00)>>8;
+ ptr->mv_norm.back = (scp->term.std_attr & 0xf000)>>12;
+ ptr->mv_rev.fore = (scp->term.rev_attr & 0x0f00)>>8;
+ ptr->mv_rev.back = (scp->term.rev_attr & 0xf000)>>12;
+ ptr->mv_grfc.fore = 0; /* not supported */
+ ptr->mv_grfc.back = 0; /* not supported */
+ ptr->mv_ovscan = scp->border;
+ ptr->mk_keylock = scp->status & LOCK_KEY_MASK;
+ return 0;
+ }
+ return EINVAL;
+ }
+
+ case VT_SETMODE: /* set screen switcher mode */
+ bcopy(data, &scp->smode, sizeof(struct vt_mode));
+ if (scp->smode.mode == VT_PROCESS) {
+ scp->proc = p;
+ scp->pid = scp->proc->p_pid;
+ }
+ return 0;
+
+ case VT_GETMODE: /* get screen switcher mode */
+ bcopy(&scp->smode, data, sizeof(struct vt_mode));
+ return 0;
+
+ case VT_RELDISP: /* screen switcher ioctl */
+ switch(*data) {
+ case VT_FALSE: /* user refuses to release screen, abort */
+ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) {
+ old_scp->status &= ~SWITCH_WAIT_REL;
+ switch_in_progress = 0;
+ return 0;
+ }
+ return EINVAL;
+
+ case VT_TRUE: /* user has released screen, go on */
+ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) {
+ scp->status &= ~SWITCH_WAIT_REL;
+ exchange_scr();
+ if (new_scp->smode.mode == VT_PROCESS) {
+ new_scp->status |= SWITCH_WAIT_ACQ;
+ psignal(new_scp->proc,
+ new_scp->smode.acqsig);
+ }
+ else
+ switch_in_progress = 0;
+ return 0;
+ }
+ return EINVAL;
+
+ case VT_ACKACQ: /* acquire acknowledged, switch completed */
+ if (scp == new_scp && (scp->status & SWITCH_WAIT_ACQ)) {
+ scp->status &= ~SWITCH_WAIT_ACQ;
+ switch_in_progress = 0;
+ return 0;
+ }
+ return EINVAL;
+
+ default:
+ return EINVAL;
+ }
+ /* NOT REACHED */
+
+ case VT_OPENQRY: /* return free virtual console */
+ for (i = 0; i < NCONS; i++) {
+ tp = VIRTUAL_TTY(i);
+ if (!(tp->t_state & TS_ISOPEN)) {
+ *data = i + 1;
+ return 0;
+ }
+ }
+ return EINVAL;
+
+ case VT_ACTIVATE: /* switch to screen *data */
+ return switch_scr((*data) - 1);
+
+ case VT_WAITACTIVE: /* wait for switch to occur */
+ if (*data > NCONS)
+ return EINVAL;
+ if (minor(dev) == (*data) - 1)
+ return 0;
+ if (*data == 0) {
+ if (scp == cur_console)
+ return 0;
+ while ((error=tsleep((caddr_t)&scp->smode,
+ PZERO|PCATCH, "waitvt", 0)) == ERESTART) ;
+ }
+ else
+ while ((error=tsleep(
+ (caddr_t)&console[*(data-1)].smode,
+ PZERO|PCATCH, "waitvt", 0)) == ERESTART) ;
+ return error;
+
+ case VT_GETACTIVE:
+ *data = get_scr_num()+1;
+ return 0;
+
+ case KDENABIO: /* allow io operations */
+ fp = (frametype *)p->p_md.md_regs;
+ fp->eflags |= PSL_IOPL;
+ return 0;
+
+ case KDDISABIO: /* disallow io operations (default) */
+ fp = (frametype *)p->p_md.md_regs;
+ fp->eflags &= ~PSL_IOPL;
+ return 0;
+
+ case KDSETMODE: /* set current mode of this (virtual) console */
+ switch (*data) {
+ case KD_TEXT: /* switch to TEXT (known) mode */
+ /* restore fonts & palette ! */
+ if (crtc_vga) {
+ load_font(0, 16, font_8x16);
+ load_font(1, 8, font_8x8);
+ load_font(2, 14, font_8x14);
+ load_palette();
+ }
+ /* FALL THROUGH */
+
+ case KD_TEXT1: /* switch to TEXT (known) mode */
+ /* no restore fonts & palette */
+ scp->status &= ~UNKNOWN_MODE;
+ set_mode(scp);
+ clear_screen(scp);
+ return 0;
+
+ case KD_GRAPHICS:/* switch to GRAPHICS (unknown) mode */
+ scp->status |= UNKNOWN_MODE;
+ return 0;
+ default:
+ return EINVAL;
+ }
+ /* NOT REACHED */
+
+ case KDGETMODE: /* get current mode of this (virtual) console */
+ *data = (scp->status & UNKNOWN_MODE) ? KD_GRAPHICS : KD_TEXT;
+ return 0;
+
+ case KDSBORDER: /* set border color of this (virtual) console */
+ if (!crtc_vga)
+ return ENXIO;
+ scp->border = *data;
+ if (scp == cur_console)
+ set_border(scp->border);
+ return 0;
+
+ case KDSKBSTATE: /* set keyboard state (locks) */
+ if (*data >= 0 && *data <= LOCK_KEY_MASK) {
+ scp->status &= ~LOCK_KEY_MASK;
+ scp->status |= *data;
+ if (scp == cur_console)
+ update_leds(scp->status);
+ return 0;
+ }
+ return EINVAL;
+
+ case KDGKBSTATE: /* get keyboard state (locks) */
+ *data = scp->status & LOCK_KEY_MASK;
+ return 0;
+
+ case KDSETRAD: /* set keyboard repeat & delay rates */
+ if (*data & 0x80)
+ return EINVAL;
+ kbd_cmd2(KB_SETRAD, *data);
+ return 0;
+
+ case KDSKBMODE: /* set keyboard mode */
+ switch (*data) {
+ case K_RAW: /* switch to RAW scancode mode */
+ scp->status |= KBD_RAW_MODE;
+ return 0;
+
+ case K_XLATE: /* switch to XLT ascii mode */
+ if (scp == cur_console && scp->status == KBD_RAW_MODE)
+ shfts = ctls = alts = agrs = metas = 0;
+ scp->status &= ~KBD_RAW_MODE;
+ return 0;
+ default:
+ return EINVAL;
+ }
+ /* NOT REACHED */
+
+ case KDGKBMODE: /* get keyboard mode */
+ *data = (scp->status & KBD_RAW_MODE) ? K_RAW : K_XLATE;
+ return 0;
+
+ case KDMKTONE: /* sound the bell */
+ if (scp == cur_console)
+ sysbeep(scp->bell_pitch, scp->bell_duration);
+ return 0;
+
+ case KIOCSOUND: /* make tone (*data) hz */
+ if (scp == cur_console) {
+ if (*(int*)data) {
+ int pitch = TIMER_FREQ/(*(int*)data);
+ /* set command for counter 2, 2 byte write */
+ if (acquire_timer2(TIMER_16BIT|TIMER_SQWAVE)) {
+ return EBUSY;
+ }
+ /* set pitch */
+ outb(TIMER_CNTR2, pitch);
+ outb(TIMER_CNTR2, (pitch>>8));
+ /* enable counter 2 output to speaker */
+ outb(IO_PPI, inb(IO_PPI) | 3);
+ }
+ else {
+ /* disable counter 2 output to speaker */
+ outb(IO_PPI, inb(IO_PPI) & 0xFC);
+ release_timer2();
+ }
+ }
+ return 0;
+
+ case KDGKBTYPE: /* get keyboard type */
+ *data = 0; /* type not known (yet) */
+ return 0;
+
+ case KDSETLED: /* set keyboard LED status */
+ if (*data >= 0 && *data <= LED_MASK) {
+ scp->status &= ~LED_MASK;
+ scp->status |= *data;
+ if (scp == cur_console)
+ update_leds(scp->status);
+ return 0;
+ }
+ return EINVAL;
+
+ case KDGETLED: /* get keyboard LED status */
+ *data = scp->status & LED_MASK;
+ return 0;
+
+ case GETFKEY: /* get functionkey string */
+ if (*(u_short*)data < n_fkey_tab) {
+ fkeyarg_t *ptr = (fkeyarg_t*)data;
+ bcopy(&fkey_tab[ptr->keynum].str,
+ ptr->keydef,
+ fkey_tab[ptr->keynum].len);
+ ptr->flen = fkey_tab[ptr->keynum].len;
+ return 0;
+ }
+ else
+ return EINVAL;
+
+ case SETFKEY: /* set functionkey string */
+ if (*(u_short*)data < n_fkey_tab) {
+ fkeyarg_t *ptr = (fkeyarg_t*)data;
+ bcopy(ptr->keydef,
+ &fkey_tab[ptr->keynum].str,
+ min(ptr->flen, MAXFK));
+ fkey_tab[ptr->keynum].len = min(ptr->flen, MAXFK);
+ return 0;
+ }
+ else
+ return EINVAL;
+
+ case GIO_SCRNMAP: /* get output translation table */
+ bcopy(&scr_map, data, sizeof(scr_map));
+ return 0;
+
+ case PIO_SCRNMAP: /* set output translation table */
+ bcopy(data, &scr_map, sizeof(scr_map));
+ return 0;
+
+ case GIO_KEYMAP: /* get keyboard translation table */
+ bcopy(&key_map, data, sizeof(key_map));
+ return 0;
+
+ case PIO_KEYMAP: /* set keyboard translation table */
+ bcopy(data, &key_map, sizeof(key_map));
+ return 0;
+
+ case PIO_FONT8x8: /* set 8x8 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(data, &font_8x8, sizeof(font_8x8));
+ load_font(1, 8, font_8x8);
+ return 0;
+
+ case GIO_FONT8x8: /* get 8x8 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(&font_8x8, data, sizeof(font_8x8));
+ return 0;
+
+ case PIO_FONT8x14: /* set 8x14 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(data, &font_8x14, sizeof(font_8x14));
+ load_font(2, 14, font_8x14);
+ return 0;
+
+ case GIO_FONT8x14: /* get 8x14 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(&font_8x14, data, sizeof(font_8x14));
+ return 0;
+
+ case PIO_FONT8x16: /* set 8x16 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(data, &font_8x16, sizeof(font_8x16));
+ load_font(0, 16, font_8x16);
+ return 0;
+
+ case GIO_FONT8x16: /* get 8x16 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(&font_8x16, data, sizeof(font_8x16));
+ return 0;
+
+ case CONSOLE_X_MODE_ON: /* just to be compatible */
+ if (saved_console < 0) {
+ saved_console = get_scr_num();
+ switch_scr(minor(dev));
+ fp = (frametype *)p->p_md.md_regs;
+ fp->eflags |= PSL_IOPL;
+ scp->status |= UNKNOWN_MODE;
+ scp->status |= KBD_RAW_MODE;
+ return 0;
+ }
+ return EAGAIN;
+
+ case CONSOLE_X_MODE_OFF:/* just to be compatible */
+ fp = (frametype *)p->p_md.md_regs;
+ fp->eflags &= ~PSL_IOPL;
+ if (crtc_vga) {
+ load_font(0, 16, font_8x16);
+ load_font(1, 8, font_8x8);
+ load_font(2, 14, font_8x14);
+ load_palette();
+ }
+ scp->status &= ~UNKNOWN_MODE;
+ set_mode(scp);
+ clear_screen(scp);
+ scp->status &= ~KBD_RAW_MODE;
+ switch_scr(saved_console);
+ saved_console = -1;
+ return 0;
+
+ case CONSOLE_X_BELL: /* more compatibility */
+ /*
+ * if set, data is a pointer to a length 2 array of
+ * integers. data[0] is the pitch in Hz and data[1]
+ * is the duration in msec.
+ */
+ if (data)
+ sysbeep(TIMER_FREQ/((int*)data)[0],
+ ((int*)data)[1]*hz/3000);
+ else
+ sysbeep(scp->bell_pitch, scp->bell_duration);
+ return 0;
+
+ default:
+ break;
+ }
+
+ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
+ if (error >= 0)
+ return(error);
+ error = ttioctl(tp, cmd, data, flag);
+ if (error >= 0)
+ return(error);
+ return(ENOTTY);
+}
+
+
+void pcxint(dev_t dev)
+{
+ struct tty *tp = get_tty_ptr(dev);
+
+ if (!tp)
+ return;
+ tp->t_state &= ~TS_BUSY;
+ if (tp->t_line)
+ (*linesw[tp->t_line].l_start)(tp);
+ else
+ pcstart(tp);
+}
+
+
+void pcstart(struct tty *tp)
+{
+#if defined(NetBSD) || defined(__FreeBSD__)
+ struct clist *rbp;
+ int i, s, len;
+ u_char buf[PCBURST];
+ scr_stat *scp = get_scr_stat(tp->t_dev);
+
+ if (scp->status & SLKED)
+ return;
+ s = spltty(); /* Isn't start always called at spltty? */
+ if (!(tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))) {
+ tp->t_state |= TS_BUSY;
+ splx(s);
+ rbp = &tp->t_outq;
+ while (rbp->c_cc) {
+ len = q_to_b(rbp, buf, PCBURST);
+ for (i=0; i<len; i++)
+ if (buf[i]) ansi_put(scp, buf[i]);
+ }
+ s = spltty();
+ tp->t_state &= ~TS_BUSY;
+#if 0
+ if (rbp->c_cc) {
+ tp->t_state |= TS_TIMEOUT;
+ timeout((timeout_t)ttrstrt, (caddr_t)tp, 1);
+ }
+#endif
+ if (rbp->c_cc <= tp->t_lowat) {
+ if (tp->t_state & TS_ASLEEP) {
+ tp->t_state &= ~TS_ASLEEP;
+ wakeup((caddr_t)rbp);
+ }
+ selwakeup(&tp->t_wsel);
+ }
+ }
+ splx(s);
+
+#else /* __386BSD__ */
+
+ int c, s, len, i;
+ scr_stat *scp = get_scr_stat(tp->t_dev);
+ u_char buf[PCBURST];
+
+ if (scp->status & SLKED)
+ return;
+ s = spltty();
+ if (!(tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))) {
+ for (;;) {
+ if (RB_LEN(tp->t_out) <= tp->t_lowat) {
+ if (tp->t_state & TS_ASLEEP) {
+ tp->t_state &= ~TS_ASLEEP;
+ wakeup((caddr_t)tp->t_out);
+ }
+ selwakeup(&tp->t_wsel);
+ }
+ if (RB_LEN(tp->t_out) == 0)
+ break;
+ if (scp->status & SLKED)
+ break;
+ len = 0;
+ while( len < PCBURST) {
+ buf[len++] = getc(tp->t_out);
+ if( RB_LEN(tp->t_out) == 0)
+ break;
+ }
+ tp->t_state |= TS_BUSY;
+ splx(s);
+ for(i=0;i<len;i++)
+ ansi_put(scp, buf[i]);
+ s = spltty();
+ tp->t_state &= ~TS_BUSY;
+ }
+ tp->t_state |= TS_BUSY;
+ if( in_putc == 0) {
+ int i;
+ for(i=0;i<console_buffer_count;i++) {
+ scput(console_buffer[i]);
+ }
+ console_buffer_count = 0;
+ }
+ tp->t_state &= ~TS_BUSY;
+ }
+ splx(s);
+#endif
+}
+
+
+void pccnprobe(struct consdev *cp)
+{
+ int maj;
+
+ /* locate the major number */
+ for (maj = 0; maj < nchrdev; maj++)
+ if ((void*)cdevsw[maj].d_open == (void*)pcopen)
+ break;
+
+ /* initialize required fields */
+ cp->cn_dev = makedev(maj, NCONS);
+ cp->cn_pri = CN_INTERNAL;
+#if defined(__386BSD__) && !defined(__FreeBSD__)
+ cp->cn_tp = CONSOLE_TTY;
+#endif
+}
+
+
+void pccninit(struct consdev *cp)
+{
+ scinit();
+}
+
+
+void pccnputc(dev_t dev, char c)
+{
+ if (c == '\n')
+ scput('\r');
+ scput(c);
+ if (cur_console == &console[0]) {
+ int pos = cur_console->crtat - cur_console->crt_base;
+ if (pos != cur_cursor_pos) {
+ cur_cursor_pos = pos;
+ outb(crtc_addr,14);
+ outb(crtc_addr+1,pos >> 8);
+ outb(crtc_addr,15);
+ outb(crtc_addr+1,pos&0xff);
+ }
+ }
+}
+
+
+int pccngetc(dev_t dev)
+{
+ int s = spltty(); /* block scintr while we poll */
+ int c = scgetc(0);
+ splx(s);
+ if (c == '\r') c = '\n';
+ return(c);
+}
+
+static void none_saver(int test)
+{
+}
+
+static void fade_saver(int test)
+{
+ static int count = 0;
+ int i;
+
+ if (test) {
+ scrn_blanked = 1;
+ if (count < 64) {
+ outb(PIXMASK, 0xFF); /* no pixelmask */
+ outb(PALWADR, 0x00);
+ outb(PALDATA, 0);
+ outb(PALDATA, 0);
+ outb(PALDATA, 0);
+ for (i = 3; i < 768; i++) {
+ if (palette[i] - count > 15)
+ outb(PALDATA, palette[i]-count);
+ else
+ outb(PALDATA, 15);
+ }
+ inb(crtc_addr+6); /* reset flip/flop */
+ outb(ATC, 0x20); /* enable palette */
+ count++;
+ }
+ }
+ else {
+ count = scrn_blanked = 0;
+ load_palette();
+ }
+}
+
+static void blank_saver(int test)
+{
+ u_char val;
+ if (test) {
+ scrn_blanked = 1;
+ outb(TSIDX, 0x01); val = inb(TSREG);
+ outb(TSIDX, 0x01); outb(TSREG, val | 0x20);
+ }
+ else {
+ scrn_blanked = 0;
+ outb(TSIDX, 0x01); val = inb(TSREG);
+ outb(TSIDX, 0x01); outb(TSREG, val & 0xDF);
+ }
+}
+
+static u_long rand_next = 1;
+
+static int rand()
+{
+ return ((rand_next = rand_next * 1103515245 + 12345) & 0x7FFFFFFF);
+}
+
+/*
+ * Alternate saver that got its inspiration from a well known utility
+ * package for an unfamous OS.
+ */
+
+#define NUM_STARS 50
+
+static void star_saver(int test)
+{
+ scr_stat *scp = cur_console;
+ int cell, i;
+ char pattern[] = {"...........++++*** "};
+ char colors[] = {FG_DARKGREY, FG_LIGHTGREY,
+ FG_WHITE, FG_LIGHTCYAN};
+ static u_short stars[NUM_STARS][2];
+
+ if (test) {
+ if (!scrn_blanked) {
+ bcopy(Crtat, scp->scr_buf,
+ scp->xsize * scp->ysize * 2);
+ fillw((FG_LIGHTGREY|BG_BLACK)<<8 | scr_map[0x20], Crtat,
+ scp->xsize * scp->ysize);
+ set_border(0);
+ i = scp->ysize * scp->xsize + 5;
+ outb(crtc_addr, 14);
+ outb(crtc_addr+1, i >> 8);
+ outb(crtc_addr, 15);
+ outb(crtc_addr+1, i & 0xff);
+ scrn_blanked = 1;
+ for(i=0; i<NUM_STARS; i++) {
+ stars[i][0] =
+ rand() % (scp->xsize*scp->ysize);
+ stars[i][1] = 0;
+ }
+ }
+ cell = rand() % NUM_STARS;
+ *((u_short*)(Crtat + stars[cell][0])) =
+ scr_map[pattern[stars[cell][1]]] |
+ colors[rand()%sizeof(colors)] << 8;
+ if ((stars[cell][1]+=(rand()%4)) >= sizeof(pattern)-1) {
+ stars[cell][0] = rand() % (scp->xsize*scp->ysize);
+ stars[cell][1] = 0;
+ }
+ }
+ else {
+ if (scrn_blanked) {
+ bcopy(scp->scr_buf, Crtat, scp->xsize*scp->ysize*2);
+ cur_cursor_pos = -1;
+ set_border(scp->border);
+ scrn_blanked = 0;
+ }
+ }
+}
+
+
+static void snake_saver(int test)
+{
+ const char saves[] = {"FreeBSD"};
+ static u_char *savs[sizeof(saves)-1];
+ static int dirx, diry;
+ int f;
+ scr_stat *scp = cur_console;
+
+ if (test) {
+ if (!scrn_blanked) {
+ bcopy(Crtat, scp->scr_buf,
+ scp->xsize * scp->ysize * 2);
+ fillw((FG_LIGHTGREY|BG_BLACK)<<8 | scr_map[0x20],
+ Crtat, scp->xsize * scp->ysize);
+ set_border(0);
+ dirx = (scp->xpos ? 1 : -1);
+ diry = (scp->ypos ?
+ scp->xsize : -scp->xsize);
+ for (f=0; f< sizeof(saves)-1; f++)
+ savs[f] = (u_char *)Crtat + 2 *
+ (scp->xpos+scp->ypos*scp->xsize);
+ *(savs[0]) = scr_map[*saves];
+ f = scp->ysize * scp->xsize + 5;
+ outb(crtc_addr, 14);
+ outb(crtc_addr+1, f >> 8);
+ outb(crtc_addr, 15);
+ outb(crtc_addr+1, f & 0xff);
+ scrn_blanked = 1;
+ }
+ if (scrn_blanked++ < 4)
+ return;
+ scrn_blanked = 1;
+ *(savs[sizeof(saves)-2]) = scr_map[0x20];
+ for (f=sizeof(saves)-2; f > 0; f--)
+ savs[f] = savs[f-1];
+ f = (savs[0] - (u_char *)Crtat) / 2;
+ if ((f % scp->xsize) == 0 ||
+ (f % scp->xsize) == scp->xsize - 1 ||
+ (rand() % 50) == 0)
+ dirx = -dirx;
+ if ((f / scp->xsize) == 0 ||
+ (f / scp->xsize) == scp->ysize - 1 ||
+ (rand() % 20) == 0)
+ diry = -diry;
+ savs[0] += 2*dirx + 2*diry;
+ for (f=sizeof(saves)-2; f>=0; f--)
+ *(savs[f]) = scr_map[saves[f]];
+ }
+ else {
+ if (scrn_blanked) {
+ bcopy(scp->scr_buf, Crtat,
+ scp->xsize * scp->ysize * 2);
+ cur_cursor_pos = -1;
+ set_border(scp->border);
+ scrn_blanked = 0;
+ }
+ }
+}
+
+static void cursor_shape(int start, int end)
+{
+ outb(crtc_addr, 10);
+ outb(crtc_addr+1, start & 0xFF);
+ outb(crtc_addr, 11);
+ outb(crtc_addr+1, end & 0xFF);
+}
+
+
+#if !defined(FAT_CURSOR)
+static void get_cursor_shape(int *start, int *end)
+{
+ outb(crtc_addr, 10);
+ *start = inb(crtc_addr+1) & 0x1F;
+ outb(crtc_addr, 11);
+ *end = inb(crtc_addr+1) & 0x1F;
+}
+#endif
+
+
+static void cursor_pos(int force)
+{
+ int pos;
+
+ if (cur_console->status & UNKNOWN_MODE)
+ return;
+ if (scrn_blank_time && (time.tv_sec > scrn_time_stamp+scrn_blank_time))
+ SCRN_SAVER(1);
+ pos = cur_console->crtat - cur_console->crt_base;
+ if (force || (!scrn_blanked && pos != cur_cursor_pos)) {
+ cur_cursor_pos = pos;
+ outb(crtc_addr, 14);
+ outb(crtc_addr+1, pos>>8);
+ outb(crtc_addr, 15);
+ outb(crtc_addr+1, pos&0xff);
+ }
+ timeout((timeout_t)cursor_pos, 0, hz/20);
+}
+
+
+static void clear_screen(scr_stat *scp)
+{
+ move_crsr(scp, 0, 0);
+ fillw(scp->term.cur_attr | scr_map[0x20], scp->crt_base,
+ scp->xsize * scp->ysize);
+}
+
+
+static int switch_scr(u_int next_scr)
+{
+ if (in_putc) { /* delay switch if in putc */
+ delayed_next_scr = next_scr+1;
+ return 0;
+ }
+ if (switch_in_progress &&
+ (cur_console->proc != pfind(cur_console->pid)))
+ switch_in_progress = 0;
+
+ if (next_scr >= NCONS || switch_in_progress) {
+ sysbeep(BELL_PITCH, BELL_DURATION);
+ return EINVAL;
+ }
+
+ /* is the wanted virtual console open ? */
+ if (next_scr) {
+ struct tty *tp = VIRTUAL_TTY(next_scr);
+ if (!(tp->t_state & TS_ISOPEN)) {
+ sysbeep(BELL_PITCH, BELL_DURATION);
+ return EINVAL;
+ }
+ }
+
+ switch_in_progress = 1;
+ old_scp = cur_console;
+ new_scp = &console[next_scr];
+ wakeup((caddr_t)&new_scp->smode);
+ if (new_scp == old_scp) {
+ switch_in_progress = 0;
+ return 0;
+ }
+
+ /* has controlling process died? */
+ if (old_scp->proc && (old_scp->proc != pfind(old_scp->pid)))
+ old_scp->smode.mode = VT_AUTO;
+ if (new_scp->proc && (new_scp->proc != pfind(new_scp->pid)))
+ new_scp->smode.mode = VT_AUTO;
+
+ /* check the modes and switch approbiatly */
+ if (old_scp->smode.mode == VT_PROCESS) {
+ old_scp->status |= SWITCH_WAIT_REL;
+ psignal(old_scp->proc, old_scp->smode.relsig);
+ }
+ else {
+ exchange_scr();
+ if (new_scp->smode.mode == VT_PROCESS) {
+ new_scp->status |= SWITCH_WAIT_ACQ;
+ psignal(new_scp->proc, new_scp->smode.acqsig);
+ }
+ else
+ switch_in_progress = 0;
+ }
+ return 0;
+}
+
+
+static void exchange_scr(void)
+{
+ struct tty *tp;
+
+ bcopy(Crtat, old_scp->scr_buf, old_scp->xsize * old_scp->ysize * 2);
+ old_scp->crt_base = old_scp->scr_buf;
+ move_crsr(old_scp, old_scp->xpos, old_scp->ypos);
+ cur_console = new_scp;
+ set_mode(new_scp);
+ new_scp->crt_base = Crtat;
+ move_crsr(new_scp, new_scp->xpos, new_scp->ypos);
+ bcopy(new_scp->scr_buf, Crtat, new_scp->xsize * new_scp->ysize * 2);
+ update_leds(new_scp->status);
+ if ((old_scp->status & UNKNOWN_MODE) && crtc_vga) {
+ load_font(0, 16, font_8x16);
+ load_font(1, 8, font_8x8);
+ load_font(2, 14, font_8x14);
+ load_palette();
+ }
+ if (old_scp->status & KBD_RAW_MODE || new_scp->status & KBD_RAW_MODE)
+ shfts = ctls = alts = agrs = metas = 0;
+ delayed_next_scr = 0;
+}
+
+
+static void move_crsr(scr_stat *scp, int x, int y)
+{
+ if (x < 0 || y < 0 || x >= scp->xsize || y >= scp->ysize)
+ return;
+ scp->xpos = x;
+ scp->ypos = y;
+ scp->crtat = scp->crt_base + scp->ypos * scp->xsize + scp->xpos;
+}
+
+static void move_up(u_short *s, u_short *d, u_int len)
+{
+ s += len;
+ d += len;
+ while (len-- > 0)
+ *--d = *--s;
+}
+
+static void move_down(u_short *s, u_short *d, u_int len)
+{
+ while (len-- > 0)
+ *d++ = *s++;
+}
+
+static void scan_esc(scr_stat *scp, u_char c)
+{
+ static u_char ansi_col[16] =
+ {0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15};
+ int i, n;
+ u_short *src, *dst, count;
+
+ if (scp->term.esc == 1) {
+ switch (c) {
+
+ case '[': /* Start ESC [ sequence */
+ scp->term.esc = 2;
+ scp->term.last_param = -1;
+ for (i = scp->term.num_param; i < MAX_ESC_PAR; i++)
+ scp->term.param[i] = 1;
+ scp->term.num_param = 0;
+ return;
+
+ case 'M': /* Move cursor up 1 line, scroll if at top */
+ if (scp->ypos > 0)
+ move_crsr(scp, scp->xpos, scp->ypos - 1);
+ else {
+ move_up(scp->crt_base,
+ scp->crt_base + scp->xsize,
+ (scp->ysize - 1) * scp->xsize);
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base, scp->xsize);
+ }
+ break;
+#if notyet
+ case 'Q':
+ scp->term.esc = 4;
+ break;
+#endif
+ case 'c': /* Clear screen & home */
+ clear_screen(scp);
+ break;
+ }
+ }
+ else if (scp->term.esc == 2) {
+ if (c >= '0' && c <= '9') {
+ if (scp->term.num_param < MAX_ESC_PAR) {
+ if (scp->term.last_param != scp->term.num_param) {
+ scp->term.last_param = scp->term.num_param;
+ scp->term.param[scp->term.num_param] = 0;
+ }
+ else
+ scp->term.param[scp->term.num_param] *= 10;
+ scp->term.param[scp->term.num_param] += c - '0';
+ return;
+ }
+ }
+ scp->term.num_param = scp->term.last_param + 1;
+ switch (c) {
+
+ case ';':
+ if (scp->term.num_param < MAX_ESC_PAR)
+ return;
+ break;
+
+ case '=':
+ scp->term.esc = 3;
+ scp->term.last_param = -1;
+ for (i = scp->term.num_param; i < MAX_ESC_PAR; i++)
+ scp->term.param[i] = 1;
+ scp->term.num_param = 0;
+ return;
+
+ case 'A': /* up n rows */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos, scp->ypos - n);
+ break;
+
+ case 'B': /* down n rows */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos, scp->ypos + n);
+ break;
+
+ case 'C': /* right n columns */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos + n, scp->ypos);
+ break;
+
+ case 'D': /* left n columns */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos - n, scp->ypos);
+ break;
+
+ case 'E': /* cursor to start of line n lines down */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, 0, scp->ypos + n);
+ break;
+
+ case 'F': /* cursor to start of line n lines up */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, 0, scp->ypos - n);
+ break;
+
+ case 'f': /* System V consoles .. */
+ case 'H': /* Cursor move */
+ if (scp->term.num_param == 0)
+ move_crsr(scp, 0, 0);
+ else if (scp->term.num_param == 2)
+ move_crsr(scp, scp->term.param[1] - 1,
+ scp->term.param[0] - 1);
+ break;
+
+ case 'J': /* Clear all or part of display */
+ if (scp->term.num_param == 0)
+ n = 0;
+ else
+ n = scp->term.param[0];
+ switch (n) {
+ case 0: /* clear form cursor to end of display */
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crtat, scp->crt_base +
+ scp->xsize * scp->ysize -
+ scp->crtat);
+ break;
+ case 1: /* clear from beginning of display to cursor */
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base,
+ scp->crtat - scp->crt_base);
+ break;
+ case 2: /* clear entire display */
+ clear_screen(scp);
+ break;
+ }
+ break;
+
+ case 'K': /* Clear all or part of line */
+ if (scp->term.num_param == 0)
+ n = 0;
+ else
+ n = scp->term.param[0];
+ switch (n) {
+ case 0: /* clear form cursor to end of line */
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crtat, scp->xsize - scp->xpos);
+ break;
+ case 1: /* clear from beginning of line to cursor */
+ fillw(scp->term.cur_attr|scr_map[0x20],
+ scp->crtat - (scp->xsize - scp->xpos),
+ (scp->xsize - scp->xpos) + 1);
+ break;
+ case 2: /* clear entire line */
+ fillw(scp->term.cur_attr|scr_map[0x20],
+ scp->crtat - (scp->xsize - scp->xpos),
+ scp->xsize);
+ break;
+ }
+ break;
+
+ case 'L': /* Insert n lines */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->ysize - scp->ypos)
+ n = scp->ysize - scp->ypos;
+ src = scp->crt_base + scp->ypos * scp->xsize;
+ dst = src + n * scp->xsize;
+ count = scp->ysize - (scp->ypos + n);
+ move_up(src, dst, count * scp->xsize);
+ fillw(scp->term.cur_attr | scr_map[0x20], src,
+ n * scp->xsize);
+ break;
+
+ case 'M': /* Delete n lines */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->ysize - scp->ypos)
+ n = scp->ysize - scp->ypos;
+ dst = scp->crt_base + scp->ypos * scp->xsize;
+ src = dst + n * scp->xsize;
+ count = scp->ysize - (scp->ypos + n);
+ move_down(src, dst, count * scp->xsize);
+ src = dst + count * scp->xsize;
+ fillw(scp->term.cur_attr | scr_map[0x20], src,
+ n * scp->xsize);
+ break;
+
+ case 'P': /* Delete n chars */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->xsize - scp->xpos)
+ n = scp->xsize - scp->xpos;
+ dst = scp->crtat;
+ src = dst + n;
+ count = scp->xsize - (scp->xpos + n);
+ move_down(src, dst, count);
+ src = dst + count;
+ fillw(scp->term.cur_attr | scr_map[0x20], src, n);
+ break;
+
+ case '@': /* Insert n chars */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->xsize - scp->xpos)
+ n = scp->xsize - scp->xpos;
+ src = scp->crtat;
+ dst = src + n;
+ count = scp->xsize - (scp->xpos + n);
+ move_up(src, dst, count);
+ fillw(scp->term.cur_attr | scr_map[0x20], src, n);
+ break;
+
+ case 'S': /* scroll up n lines */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->ypos)
+ n = scp->ypos;
+ bcopy(scp->crt_base + (scp->xsize * n),
+ scp->crt_base,
+ scp->xsize * (scp->ysize - n) *
+ sizeof(u_short));
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base + scp->xsize *
+ (scp->ysize - 1),
+ scp->xsize);
+ break;
+
+ case 'T': /* scroll down n lines */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->ysize - scp->ypos)
+ n = scp->ysize - scp->ypos;
+ bcopy(scp->crt_base,
+ scp->crt_base + (scp->xsize * n),
+ scp->xsize * (scp->ysize - n) *
+ sizeof(u_short));
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base, scp->xsize);
+ break;
+
+ case 'X': /* delete n characters in line */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->xsize - scp->xpos)
+ n = scp->xsize - scp->xpos;
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base + scp->xpos +
+ ((scp->xsize*scp->ypos) * sizeof(u_short)), n);
+ break;
+
+ case 'Z': /* move n tabs backwards */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if ((i = scp->xpos & 0xf8) == scp->xpos)
+ i -= 8*n;
+ else
+ i -= 8*(n-1);
+ if (i < 0)
+ i = 0;
+ move_crsr(scp, i, scp->ypos);
+ break;
+
+ case '`': /* move cursor to column n */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, n, scp->ypos);
+ break;
+
+ case 'a': /* move cursor n columns to the right */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos + n, scp->ypos);
+ break;
+
+ case 'd': /* move cursor to row n */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos, n);
+ break;
+
+ case 'e': /* move cursor n rows down */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos, scp->ypos + n);
+ break;
+
+ case 'm': /* change attribute */
+ if (scp->term.num_param == 0)
+ n = 0;
+ else
+ n = scp->term.param[0];
+ switch (n) {
+ case 0: /* back to normal */
+ scp->term.cur_attr = scp->term.std_attr;
+ break;
+ case 1: /* highlight (bold) */
+ scp->term.cur_attr &= 0xFF00;
+ scp->term.cur_attr |= 0x0800;
+ break;
+ case 4: /* highlight (underline) */
+ scp->term.cur_attr &= 0x0F00;
+ scp->term.cur_attr |= 0x0800;
+ break;
+ case 5: /* blink */
+ scp->term.cur_attr &= 0xFF00;
+ scp->term.cur_attr |= 0x8000;
+ break;
+ case 7: /* reverse video */
+ scp->term.cur_attr = scp->term.rev_attr;
+ break;
+ case 30: case 31: case 32: case 33: /* set fg color */
+ case 34: case 35: case 36: case 37:
+ scp->term.cur_attr = (scp->term.cur_attr & 0xF0FF)
+ | (ansi_col[(n - 30) & 7] << 8);
+ break;
+ case 40: case 41: case 42: case 43: /* set bg color */
+ case 44: case 45: case 46: case 47:
+ scp->term.cur_attr = (scp->term.cur_attr & 0x0FFF)
+ | (ansi_col[(n - 40) & 7] << 12);
+ break;
+ }
+ break;
+
+ case 'x':
+ if (scp->term.num_param == 0)
+ n = 0;
+ else
+ n = scp->term.param[0];
+ switch (n) {
+ case 0: /* reset attributes */
+ scp->term.cur_attr = scp->term.std_attr =
+ current_default->std_attr;
+ scp->term.rev_attr = current_default->rev_attr;
+ break;
+ case 1: /* set ansi background */
+ scp->term.cur_attr = scp->term.std_attr =
+ (scp->term.std_attr & 0x0F00) |
+ (ansi_col[(scp->term.param[1])&0x0F]<<12);
+ break;
+ case 2: /* set ansi foreground */
+ scp->term.cur_attr = scp->term.std_attr =
+ (scp->term.std_attr & 0xF000) |
+ (ansi_col[(scp->term.param[1])&0x0F]<<8);
+ break;
+ case 3: /* set ansi attribute directly */
+ scp->term.cur_attr = scp->term.std_attr =
+ (scp->term.param[1]&0xFF)<<8;
+ break;
+ case 5: /* set ansi reverse video background */
+ scp->term.rev_attr =
+ (scp->term.rev_attr & 0x0F00) |
+ (ansi_col[(scp->term.param[1])&0x0F]<<12);
+ break;
+ case 6: /* set ansi reverse video foreground */
+ scp->term.rev_attr =
+ (scp->term.rev_attr & 0xF000) |
+ (ansi_col[(scp->term.param[1])&0x0F]<<8);
+ break;
+ case 7: /* set ansi reverse video directly */
+ scp->term.rev_attr = (scp->term.param[1]&0xFF)<<8;
+ break;
+ }
+ break;
+
+ case 'z': /* switch to (virtual) console n */
+ if (scp->term.num_param == 1)
+ switch_scr(scp->term.param[0]);
+ break;
+ }
+ }
+ else if (scp->term.esc == 3) {
+ if (c >= '0' && c <= '9') {
+ if (scp->term.num_param < MAX_ESC_PAR) {
+ if (scp->term.last_param != scp->term.num_param) {
+ scp->term.last_param = scp->term.num_param;
+ scp->term.param[scp->term.num_param] = 0;
+ }
+ else
+ scp->term.param[scp->term.num_param] *= 10;
+ scp->term.param[scp->term.num_param] += c - '0';
+ return;
+ }
+ }
+ scp->term.num_param = scp->term.last_param + 1;
+ switch (c) {
+
+ case ';':
+ if (scp->term.num_param < MAX_ESC_PAR)
+ return;
+ break;
+
+ case 'A': /* set display border color */
+ if (scp->term.num_param == 1)
+ scp->border=scp->term.param[0] & 0xff;
+ if (scp == cur_console)
+ set_border(scp->border);
+ break;
+
+ case 'B': /* set bell pitch and duration */
+ if (scp->term.num_param == 2) {
+ scp->bell_pitch = scp->term.param[0];
+ scp->bell_duration = scp->term.param[1]*10;
+ }
+ break;
+
+ case 'C': /* set cursor shape (start & end line) */
+ if (scp->term.num_param == 2) {
+ scp->cursor_start = scp->term.param[0] & 0x1F;
+ scp->cursor_end = scp->term.param[1] & 0x1F;
+ if (scp == cur_console)
+ cursor_shape(scp->cursor_start,
+ scp->cursor_end);
+ }
+ break;
+
+ case 'F': /* set ansi foreground */
+ if (scp->term.num_param == 1)
+ scp->term.cur_attr = scp->term.std_attr =
+ (scp->term.std_attr & 0xF000)
+ | ((scp->term.param[0] & 0x0F) << 8);
+ break;
+
+ case 'G': /* set ansi background */
+ if (scp->term.num_param == 1)
+ scp->term.cur_attr = scp->term.std_attr =
+ (scp->term.std_attr & 0x0F00)
+ | ((scp->term.param[0] & 0x0F) << 12);
+ break;
+
+ case 'H': /* set ansi reverse video foreground */
+ if (scp->term.num_param == 1)
+ scp->term.rev_attr =
+ (scp->term.rev_attr & 0xF000)
+ | ((scp->term.param[0] & 0x0F) << 8);
+ break;
+
+ case 'I': /* set ansi reverse video background */
+ if (scp->term.num_param == 1)
+ scp->term.rev_attr =
+ (scp->term.rev_attr & 0x0F00)
+ | ((scp->term.param[0] & 0x0F) << 12);
+ break;
+ }
+ }
+ scp->term.esc = 0;
+}
+
+
+static void ansi_put(scr_stat *scp, u_char c)
+{
+ if (scp->status & UNKNOWN_MODE)
+ return;
+
+ /* make screensaver happy */
+ if (scp == cur_console) {
+ scrn_time_stamp = time.tv_sec;
+ if (scrn_blanked)
+ SCRN_SAVER(0);
+ }
+ in_putc++;
+ if (scp->term.esc)
+ scan_esc(scp, c);
+ else switch(c) {
+ case 0x1B: /* start escape sequence */
+ scp->term.esc = 1;
+ scp->term.num_param = 0;
+ break;
+ case 0x07:
+ if (scp == cur_console)
+ sysbeep(scp->bell_pitch, scp->bell_duration);
+ break;
+ case '\t': /* non-destructive tab */
+ scp->crtat += (8 - scp->xpos % 8);
+ scp->xpos += (8 - scp->xpos % 8);
+ break;
+ case '\b': /* non-destructive backspace */
+ if (scp->crtat > scp->crt_base) {
+ scp->crtat--;
+ if (scp->xpos > 0)
+ scp->xpos--;
+ else {
+ scp->xpos += scp->xsize - 1;
+ scp->ypos--;
+ }
+ }
+ break;
+ case '\r': /* return to pos 0 */
+ move_crsr(scp, 0, scp->ypos);
+ break;
+ case '\n': /* newline, same pos */
+ scp->crtat += scp->xsize;
+ scp->ypos++;
+ break;
+ case '\f': /* form feed, clears screen */
+ clear_screen(scp);
+ break;
+ default:
+ /* Print only printables */
+ *scp->crtat = (scp->term.cur_attr | scr_map[c]);
+ scp->crtat++;
+ if (++scp->xpos >= scp->xsize) {
+ scp->xpos = 0;
+ scp->ypos++;
+ }
+ break;
+ }
+ if (scp->crtat >= scp->crt_base + scp->ysize * scp->xsize) {
+ bcopy(scp->crt_base + scp->xsize, scp->crt_base,
+ scp->xsize * (scp->ysize - 1) * sizeof(u_short));
+ fillw(scp->term.cur_attr | scr_map[0x20],
+ scp->crt_base + scp->xsize * (scp->ysize - 1),
+ scp->xsize);
+ scp->crtat -= scp->xsize;
+ scp->ypos--;
+ }
+ in_putc--;
+ if (delayed_next_scr)
+ switch_scr(delayed_next_scr - 1);
+}
+
+static void scinit(void)
+{
+ u_short volatile *cp = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short), was;
+ unsigned cursorat;
+ int i;
+
+ /*
+ * catch that once in a blue moon occurence when scinit is called
+ * TWICE, adding the CGA_BUF offset again -> poooff
+ */
+ if (crtat != 0)
+ return;
+ /*
+ * Crtat initialized to point to MONO buffer, if not present change
+ * to CGA_BUF offset. ONLY ADD the difference since locore.s adds
+ * in the remapped offset at the "right" time
+ */
+ was = *cp;
+ *cp = (u_short) 0xA55A;
+ if (*cp != 0xA55A) {
+ crtc_addr = MONO_BASE;
+ } else {
+ *cp = was;
+ crtc_addr = COLOR_BASE;
+ Crtat = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short);
+ }
+
+ /* Extract cursor location */
+ outb(crtc_addr,14);
+ cursorat = inb(crtc_addr+1)<<8 ;
+ outb(crtc_addr,15);
+ cursorat |= inb(crtc_addr+1);
+ crtat = Crtat + cursorat;
+
+ /* is this a VGA or higher ? */
+ outb(crtc_addr, 7);
+ if (inb(crtc_addr) == 7)
+ crtc_vga = 1;
+
+ current_default = &user_default;
+ console[0].crtat = crtat;
+ console[0].crt_base = Crtat;
+ console[0].term.esc = 0;
+ console[0].term.std_attr = current_default->std_attr;
+ console[0].term.rev_attr = current_default->rev_attr;
+ console[0].term.cur_attr = current_default->std_attr;
+ console[0].xpos = cursorat % COL;
+ console[0].ypos = cursorat / COL;
+ console[0].border = BG_BLACK;;
+ console[0].xsize = COL;
+ console[0].ysize = ROW;
+ console[0].status = 0;
+ console[0].pid = 0;
+ console[0].proc = NULL;
+ console[0].smode.mode = VT_AUTO;
+ console[0].bell_pitch = BELL_PITCH;
+ console[0].bell_duration = BELL_DURATION;
+ kernel_console.esc = 0;
+ kernel_console.std_attr = kernel_default.std_attr;
+ kernel_console.rev_attr = kernel_default.rev_attr;
+ kernel_console.cur_attr = kernel_default.std_attr;
+ /* initialize mapscrn array to a one to one map */
+ for (i=0; i<sizeof(scr_map); i++)
+ scr_map[i] = i;
+ clear_screen(&console[0]);
+}
+
+
+static void scput(u_char c)
+{
+ scr_stat *scp = &console[0];
+ term_stat save;
+
+ if (crtat == 0)
+ scinit();
+ if( in_putc == 0) {
+ ++in_putc;
+ save = scp->term;
+ scp->term = kernel_console;
+ current_default = &kernel_default;
+ ansi_put(scp, c);
+ kernel_console = scp->term;
+ current_default = &user_default;
+ scp->term = save;
+ --in_putc;
+ } else {
+ if( console_buffer_count < CONSOLE_BUFFER_SIZE)
+ console_buffer[console_buffer_count++] = c;
+ }
+}
+
+
+static u_char *get_fstr(u_int c, u_int *len)
+{
+ u_int i;
+
+ if (!(c & FKEY))
+ return(NULL);
+ i = (c & 0xFF) - F_FN;
+ if (i > n_fkey_tab)
+ return(NULL);
+ *len = fkey_tab[i].len;
+ return(fkey_tab[i].str);
+}
+
+
+static void update_leds(int which)
+{
+ static u_char xlate_leds[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+
+ /* replace CAPS led with ALTGR led for ALTGR keyboards */
+ if (key_map.n_keys > ALTGR_OFFSET) {
+ if (which & ALKED)
+ which |= CLKED;
+ else
+ which &= ~CLKED;
+ }
+ kbd_cmd2(KB_SETLEDS, xlate_leds[which & LED_MASK]);
+}
+
+
+/*
+ * scgetc(noblock) : get a character from the keyboard.
+ * If noblock = 0 wait until a key is gotten. Otherwise return NOKEY.
+ */
+u_int scgetc(int noblock)
+{
+ u_char val, code, release;
+ u_int state, action;
+ struct key_t *key;
+ static u_char esc_flag = 0, compose = 0;
+ static u_int chr = 0;
+
+next_code:
+ kbd_wait();
+ /* First see if there is something in the keyboard port */
+ if (inb(KB_STAT) & KB_BUF_FULL)
+ val = inb(KB_DATA);
+ else if (noblock)
+ return(NOKEY);
+ else
+ goto next_code;
+
+ if (cur_console->status & KBD_RAW_MODE)
+ return val;
+
+ code = val & 0x7F;
+ release = val & 0x80;
+
+ switch (esc_flag) {
+ case 0x00: /* normal scancode */
+ switch(code) {
+ case 0x38: /* left alt (compose key) */
+ if (release && compose) {
+ compose = 0;
+ if (chr > 255) {
+ sysbeep(BELL_PITCH, BELL_DURATION);
+ chr = 0;
+ }
+ }
+ else {
+ if (!compose) {
+ compose = 1;
+ chr = 0;
+ }
+ }
+ break;
+ case 0x60:
+ case 0x61:
+ esc_flag = code;
+ goto next_code;
+ }
+ break;
+ case 0x60: /* 0xE0 prefix */
+ esc_flag = 0;
+ switch (code) {
+ case 0x1c: /* right enter key */
+ code = 0x59;
+ break;
+ case 0x1d: /* right ctrl key */
+ code = 0x5a;
+ break;
+ case 0x35: /* keypad divide key */
+ code = 0x5b;
+ break;
+ case 0x37: /* print scrn key */
+ code = 0x5c;
+ break;
+ case 0x38: /* right alt key (alt gr) */
+ code = 0x5d;
+ break;
+ case 0x47: /* grey home key */
+ code = 0x5e;
+ break;
+ case 0x48: /* grey up arrow key */
+ code = 0x5f;
+ break;
+ case 0x49: /* grey page up key */
+ code = 0x60;
+ break;
+ case 0x4b: /* grey left arrow key */
+ code = 0x61;
+ break;
+ case 0x4d: /* grey right arrow key */
+ code = 0x62;
+ break;
+ case 0x4f: /* grey end key */
+ code = 0x63;
+ break;
+ case 0x50: /* grey down arrow key */
+ code = 0x64;
+ break;
+ case 0x51: /* grey page down key */
+ code = 0x65;
+ break;
+ case 0x52: /* grey insert key */
+ code = 0x66;
+ break;
+ case 0x53: /* grey delete key */
+ code = 0x67;
+ break;
+ default: /* ignore everything else */
+ goto next_code;
+ }
+ break;
+ case 0x61: /* 0xE1 prefix */
+ esc_flag = 0;
+ if (code == 0x1D)
+ esc_flag = 0x1D;
+ goto next_code;
+ /* NOT REACHED */
+ case 0x1D: /* pause / break */
+ esc_flag = 0;
+ if (code != 0x45)
+ goto next_code;
+ code = 0x68;
+ break;
+ }
+
+ if (compose) {
+ switch (code) {
+ case 0x47:
+ case 0x48: /* keypad 7,8,9 */
+ case 0x49:
+ if (!release)
+ chr = (code - 0x40) + chr*10;
+ goto next_code;
+ case 0x4b:
+ case 0x4c: /* keypad 4,5,6 */
+ case 0x4d:
+ if (!release)
+ chr = (code - 0x47) + chr*10;
+ goto next_code;
+ case 0x4f:
+ case 0x50: /* keypad 1,2,3 */
+ case 0x51:
+ if (!release)
+ chr = (code - 0x4e) + chr*10;
+ goto next_code;
+ case 0x52: /* keypad 0 */
+ if (!release)
+ chr *= 10;
+ goto next_code;
+ case 0x38: /* left alt key */
+ break;
+ default:
+ if (chr) {
+ compose = chr = 0;
+ sysbeep(BELL_PITCH, BELL_DURATION);
+ goto next_code;
+ }
+ break;
+ }
+ }
+
+ state = (shfts ? 1 : 0 ) | (2 * (ctls ? 1 : 0)) | (4 * (alts ? 1 : 0));
+ if ((!agrs && (cur_console->status & ALKED))
+ || (agrs && !(cur_console->status & ALKED)))
+ code += ALTGR_OFFSET;
+ key = &key_map.key[code];
+ if ( ((key->flgs & FLAG_LOCK_C) && (cur_console->status & CLKED))
+ || ((key->flgs & FLAG_LOCK_N) && (cur_console->status & NLKED)) )
+ state ^= 1;
+
+ /* Check for make/break */
+ action = key->map[state];
+ if (release) { /* key released */
+ if (key->spcl & 0x80) {
+ switch (action) {
+ case LSH:
+ shfts &= ~1;
+ break;
+ case RSH:
+ shfts &= ~2;
+ break;
+ case LCTR:
+ ctls &= ~1;
+ break;
+ case RCTR:
+ ctls &= ~2;
+ break;
+ case LALT:
+ alts &= ~1;
+ break;
+ case RALT:
+ alts &= ~2;
+ break;
+ case NLK:
+ nlkcnt = 0;
+ break;
+ case CLK:
+ clkcnt = 0;
+ break;
+ case SLK:
+ slkcnt = 0;
+ break;
+ case ASH:
+ agrs = 0;
+ break;
+ case ALK:
+ alkcnt = 0;
+ break;
+ case META:
+ metas = 0;
+ break;
+ }
+ }
+ if (chr && !compose) {
+ action = chr;
+ chr = 0;
+ return(action);
+ }
+ } else {
+ /* key pressed */
+ if (key->spcl & (0x80>>state)) {
+ switch (action) {
+ /* LOCKING KEYS */
+ case NLK:
+ if (!nlkcnt) {
+ nlkcnt++;
+ if (cur_console->status & NLKED)
+ cur_console->status &= ~NLKED;
+ else
+ cur_console->status |= NLKED;
+ update_leds(cur_console->status);
+ }
+ break;
+ case CLK:
+ if (!clkcnt) {
+ clkcnt++;
+ if (cur_console->status & CLKED)
+ cur_console->status &= ~CLKED;
+ else
+ cur_console->status |= CLKED;
+ update_leds(cur_console->status);
+ }
+ break;
+ case SLK:
+ if (!slkcnt) {
+ slkcnt++;
+ if (cur_console->status & SLKED) {
+ cur_console->status &= ~SLKED;
+ pcstart(VIRTUAL_TTY(get_scr_num()));
+ }
+ else
+ cur_console->status |= SLKED;
+ update_leds(cur_console->status);
+ }
+ break;
+ case ALK:
+ if (!alkcnt) {
+ alkcnt++;
+ if (cur_console->status & ALKED)
+ cur_console->status &= ~ALKED;
+ else
+ cur_console->status |= ALKED;
+ update_leds(cur_console->status);
+ }
+ break;
+
+ /* NON-LOCKING KEYS */
+ case NOP:
+ break;
+ case RBT:
+#if defined(__FreeBSD__)
+ shutdown_nice();
+#else
+ cpu_reset();
+#endif
+ break;
+ case DBG:
+#if DDB > 0 /* try to switch to console 0 */
+ if (cur_console->smode.mode == VT_AUTO &&
+ console[0].smode.mode == VT_AUTO)
+ switch_scr(0);
+ Debugger("manual escape to debugger");
+ return(NOKEY);
+#else
+ printf("No debugger in kernel\n");
+#endif
+ break;
+ case LSH:
+ shfts |= 1;
+ break;
+ case RSH:
+ shfts |= 2;
+ break;
+ case LCTR:
+ ctls |= 1;
+ break;
+ case RCTR:
+ ctls |= 2;
+ break;
+ case LALT:
+ alts |= 1;
+ break;
+ case RALT:
+ alts |= 2;
+ break;
+ case ASH:
+ agrs = 1;
+ break;
+ case META:
+ metas = 1;
+ break;
+ case NEXT:
+ switch_scr((get_scr_num()+1)%NCONS);
+ break;
+ default:
+ if (action >= F_SCR && action <= L_SCR) {
+ switch_scr(action - F_SCR);
+ break;
+ }
+ if (action >= F_FN && action <= L_FN)
+ action |= FKEY;
+ return(action);
+ }
+ }
+ else {
+ if (metas)
+ action |= MKEY;
+ return(action);
+ }
+ }
+ goto next_code;
+}
+
+
+int getchar(void)
+{
+ u_char thechar;
+ int s;
+
+ polling = 1;
+ s = splhigh();
+ scput('>');
+ thechar = (u_char) scgetc(0);
+ polling = 0;
+ splx(s);
+ switch (thechar) {
+ default:
+ if (thechar >= scr_map[0x20])
+ scput(thechar);
+ return(thechar);
+ case cr:
+ case lf:
+ scput(cr); scput(lf);
+ return(lf);
+ case bs:
+ case del:
+ scput(bs); scput(scr_map[0x20]); scput(bs);
+ return(thechar);
+ case cntld:
+ scput('^'); scput('D'); scput('\r'); scput('\n');
+ return(0);
+ }
+}
+
+
+u_int sgetc(int noblock)
+{
+ return (scgetc(noblock) & 0xff);
+}
+
+int pcmmap(dev_t dev, int offset, int nprot)
+{
+ if (offset > 0x20000)
+ return EINVAL;
+ return i386_btop((VIDEOMEM + offset));
+}
+
+
+static void kbd_wait(void)
+{
+ int i;
+
+ for (i=0; i<1000; i++) { /* up to 10 msec */
+ if ((inb(KB_STAT) & KB_READY) == 0)
+ break;
+ DELAY (10);
+ }
+}
+
+
+static void kbd_cmd(u_char command)
+{
+ kbd_wait();
+ outb(KB_DATA, command);
+}
+
+
+static void kbd_cmd2(u_char command, u_char arg)
+{
+ int r, s = spltty();
+ do {
+ kbd_cmd(command);
+ r = kbd_reply();
+ if (r == KB_ACK) {
+ kbd_cmd(arg & 0x7f);
+ r = kbd_reply();
+ }
+ } while (r != KB_ACK);
+ splx(s);
+}
+
+
+static int kbd_reply()
+{
+ int i;
+
+ kbd_wait();
+ for (i=0; i<60000; i++) { /* at least 300 msec, 600 msec enough */
+ if (inb(KB_STAT) & KB_BUF_FULL)
+ return ((u_char) inb(KB_DATA));
+ DELAY (10);
+ }
+ return(-1);
+}
+
+
+static void set_mode(scr_stat *scp)
+{
+ u_char byte;
+ int s;
+
+ if (scp != cur_console)
+ return;
+
+ /* (re)activate cursor */
+ untimeout((timeout_t)cursor_pos, 0);
+ cursor_pos(1);
+
+ /* change cursor type if set */
+ if (scp->cursor_start != -1 && scp->cursor_end != -1)
+ cursor_shape(scp->cursor_start, scp->cursor_end);
+
+ /* mode change only on VGA's */
+ if (!crtc_vga)
+ return;
+
+ /* setup video hardware for the given mode */
+ s = splhigh();
+ switch(scp->mode) {
+ case TEXT80x25:
+ outb(crtc_addr, 9); byte = inb(crtc_addr+1);
+ outb(crtc_addr, 9); outb(crtc_addr+1, byte | 0x0F);
+ outb(TSIDX, 0x03); outb(TSREG, 0x00); /* select font 0 */
+ break;
+ case TEXT80x50:
+ outb(crtc_addr, 9); byte = inb(crtc_addr+1);
+ outb(crtc_addr, 9); outb(crtc_addr+1, (byte & 0xF0) | 0x07);
+ outb(TSIDX, 0x03); outb(TSREG, 0x05); /* select font 1 */
+ break;
+ default:
+ break;
+ }
+ splx(s);
+
+ /* set border color for this (virtual) console */
+ set_border(scp->border);
+ return;
+}
+
+
+static void set_border(int color)
+{
+ inb(crtc_addr+6); /* reset flip-flop */
+ outb(ATC, 0x11); outb(ATC, color);
+ inb(crtc_addr+6); /* reset flip-flop */
+ outb(ATC, 0x20); /* enable Palette */
+}
+
+static void load_font(int segment, int size, char* font)
+{
+ int ch, line, s;
+ u_char val;
+
+ outb(TSIDX, 0x01); val = inb(TSREG); /* blank screen */
+ outb(TSIDX, 0x01); outb(TSREG, val | 0x20);
+
+ /* setup vga for loading fonts (graphics plane mode) */
+ s = splhigh();
+ inb(crtc_addr+6); /* reset flip/flop */
+ outb(ATC, 0x30); outb(ATC, 0x01);
+ outb(TSIDX, 0x02); outb(TSREG, 0x04);
+ outb(TSIDX, 0x04); outb(TSREG, 0x06);
+ outb(GDCIDX, 0x04); outb(GDCREG, 0x02);
+ outb(GDCIDX, 0x05); outb(GDCREG, 0x00);
+ outb(GDCIDX, 0x06); outb(GDCREG, 0x05); /* addr = a0000, 64kb */
+ splx(s);
+ for (ch=0; ch < 256; ch++)
+ for (line=0; line < size; line++)
+ *((char *)atdevbase+(segment*0x4000)+(ch*32)+line) =
+ font[(ch*size)+line];
+ /* setup vga for text mode again */
+ s = splhigh();
+ inb(crtc_addr+6); /* reset flip/flop */
+ outb(ATC, 0x30); outb(ATC, 0x0C);
+ outb(TSIDX, 0x02); outb(TSREG, 0x03);
+ outb(TSIDX, 0x04); outb(TSREG, 0x02);
+ outb(GDCIDX, 0x04); outb(GDCREG, 0x00);
+ outb(GDCIDX, 0x05); outb(GDCREG, 0x10);
+ if (crtc_addr == MONO_BASE) {
+ outb(GDCIDX, 0x06); outb(GDCREG, 0x0A); /* addr = b0000, 32kb */
+ }
+ else {
+ outb(GDCIDX, 0x06); outb(GDCREG, 0x0E); /* addr = b8000, 32kb */
+ }
+ splx(s);
+ outb(TSIDX, 0x01); val = inb(TSREG); /* unblank screen */
+ outb(TSIDX, 0x01); outb(TSREG, val & 0xDF);
+}
+
+
+static void load_palette(void)
+{
+ int i;
+
+ outb(PIXMASK, 0xFF); /* no pixelmask */
+ outb(PALWADR, 0x00);
+ for (i=0x00; i<0x300; i++)
+ outb(PALDATA, palette[i]);
+ inb(crtc_addr+6); /* reset flip/flop */
+ outb(ATC, 0x20); /* enable palette */
+}
+
+static void save_palette(void)
+{
+ int i;
+
+ outb(PALRADR, 0x00);
+ for (i=0x00; i<0x300; i++)
+ palette[i] = inb(PALDATA);
+ inb(crtc_addr+6); /* reset flip/flop */
+}
+
+
+static void change_winsize(struct tty *tp, int x, int y)
+{
+ if (tp->t_winsize.ws_col != x || tp->t_winsize.ws_row != y) {
+ tp->t_winsize.ws_col = x;
+ tp->t_winsize.ws_row = y;
+ pgsignal(tp->t_pgrp, SIGWINCH, 1);
+ }
+}
+
+#endif /* NSC */
diff --git a/sys/isa/timerreg.h b/sys/isa/timerreg.h
new file mode 100644
index 0000000..5742f66
--- /dev/null
+++ b/sys/isa/timerreg.h
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: Header: timerreg.h,v 1.2 93/02/28 15:08:58 mccanne Exp
+ * $Id$
+ */
+
+/*
+ *
+ * Register definitions for the Intel 8253 Programmable Interval Timer.
+ *
+ * This chip has three independent 16-bit down counters that can be
+ * read on the fly. There are three mode registers and three countdown
+ * registers. The countdown registers are addressed directly, via the
+ * first three I/O ports. The three mode registers are accessed via
+ * the fourth I/O port, with two bits in the mode byte indicating the
+ * register. (Why are hardware interfaces always so braindead?).
+ *
+ * To write a value into the countdown register, the mode register
+ * is first programmed with a command indicating the which byte of
+ * the two byte register is to be modified. The three possibilities
+ * are load msb (TMR_MR_MSB), load lsb (TMR_MR_LSB), or load lsb then
+ * msb (TMR_MR_BOTH).
+ *
+ * To read the current value ("on the fly") from the countdown register,
+ * you write a "latch" command into the mode register, then read the stable
+ * value from the corresponding I/O port. For example, you write
+ * TMR_MR_LATCH into the corresponding mode register. Presumably,
+ * after doing this, a write operation to the I/O port would result
+ * in undefined behavior (but hopefully not fry the chip).
+ * Reading in this manner has no side effects.
+ *
+ * The outputs of the three timers are connected as follows:
+ *
+ * timer 0 -> irq 0
+ * timer 1 -> dma chan 0 (for dram refresh)
+ * timer 2 -> speaker (via keyboard controller)
+ *
+ * Timer 0 is used to call hardclock.
+ * Timer 2 is used to generate console beeps.
+ */
+
+/*
+ * Macros for specifying values to be written into a mode register.
+ */
+#define TIMER_CNTR0 (IO_TIMER1 + 0) /* timer 0 counter port */
+#define TIMER_CNTR1 (IO_TIMER1 + 1) /* timer 1 counter port */
+#define TIMER_CNTR2 (IO_TIMER1 + 2) /* timer 2 counter port */
+#define TIMER_MODE (IO_TIMER1 + 3) /* timer mode port */
+#define TIMER_SEL0 0x00 /* select counter 0 */
+#define TIMER_SEL1 0x40 /* select counter 1 */
+#define TIMER_SEL2 0x80 /* select counter 2 */
+#define TIMER_INTTC 0x00 /* mode 0, intr on terminal cnt */
+#define TIMER_ONESHOT 0x02 /* mode 1, one shot */
+#define TIMER_RATEGEN 0x04 /* mode 2, rate generator */
+#define TIMER_SQWAVE 0x06 /* mode 3, square wave */
+#define TIMER_SWSTROBE 0x08 /* mode 4, s/w triggered strobe */
+#define TIMER_HWSTROBE 0x0a /* mode 5, h/w triggered strobe */
+#define TIMER_LATCH 0x00 /* latch counter for reading */
+#define TIMER_LSB 0x10 /* r/w counter LSB */
+#define TIMER_MSB 0x20 /* r/w counter MSB */
+#define TIMER_16BIT 0x30 /* r/w counter 16 bits, LSB first */
+#define TIMER_BCD 0x01 /* count in BCD */
+
diff --git a/sys/isofs/cd9660/TODO b/sys/isofs/cd9660/TODO
new file mode 100644
index 0000000..555d26a
--- /dev/null
+++ b/sys/isofs/cd9660/TODO
@@ -0,0 +1,77 @@
+# $Id: TODO,v 1.4 1993/09/07 15:40:51 ws Exp $
+
+ 1) should understand "older", original High Sierra ("CDROM001") type
+
+ Not yet. ( I don't have this technical information, yet. )
+
+ 2) should understand Rock Ridge
+
+ Yes, we have follows function.
+
+ o Symbolic Link
+ o Real Name(long name)
+ o File Attribute
+ o Time stamp
+ o uid, gid
+ o Devices
+ o Relocated directories
+
+ Except follows:
+
+ o POSIX device number mapping
+
+ There is some preliminary stuff in there that (ab-)uses the mknod
+ system call, but this needs a writable filesystem
+
+ 3) should be called cdfs, as there are other ISO file system soon possible
+
+ Not yet. Probably we should make another file system when the ECMA draft
+ is valid and do it. For doing Rock Ridge Support, I can use almost same
+ code. So I just use the same file system interface...
+
+ 4) should have file handles implemented for use with NFS, etc
+
+ Yes. we have already this one, and I based it for this release.
+
+ 5) should have name translation enabled by mount flag
+
+ Yes. we can disable the Rock Ridge Extension by follows option;
+
+ "mount -t isofs -o -norrip /dev/cd0d /cdrom"
+
+ 6) should run as a user process, and not take up kernel space (cdroms
+ are slow)
+
+ Not yet.
+
+ 7) ECMA support.
+
+ Not yet. we need not only a technical spec but also ECMA format
+ cd-rom itself!
+
+ 8) Character set change by SVD ( multi SVD support )
+
+ Not yet. We should also hack the other part of system as 8 bit
+ clean. As far as I know, if you export the cdrom by NFS, the client
+ can access the 8 bit clean (ie. Solaris Japanese with EUC code )
+
+ 9) Access checks in isofs_access
+
+ Not yet.
+
+ 10) Support for generation numbers
+
+ Yes. Default is to list only the last file (the one with the highest
+ generation number). If you mount with -gen, all files are shown with
+ their generation numbers. In both cases you can specify the generation
+ number on opening files (if you happen to know it) or leave it off,
+ when it will again find the last file.
+
+ 11) Support for extended attributes
+
+ Yes. Since this requires an extra block buffer for the attributes
+ this must be enabled on mounting with the option -extattr.
+
+----------
+Last update July 19, '93 by Atsushi Murai. (amurai@spec.co.jp)
+Last update August 19, '93 by Wolfgang Solfrank. (ws@tools.de)
diff --git a/sys/isofs/cd9660/TODO.hibler b/sys/isofs/cd9660/TODO.hibler
new file mode 100644
index 0000000..3501aa2
--- /dev/null
+++ b/sys/isofs/cd9660/TODO.hibler
@@ -0,0 +1,22 @@
+1. Investiate making ISOFS another UFS shared filesystem (ala FFS/MFS/LFS).
+ Since it was modelled after the inode code, we might be able to merge
+ them back. It looks like a seperate (but very similar) lookup routine
+ will be needed due to the associated file stuff.
+
+2. Make filesystem exportable. This comes for free if stacked with UFS.
+ Otherwise, the ufs_export routines need to be elevated to vfs_* routines.
+ [ DONE - hibler ]
+
+3. If it can't be merged with UFS, at least get them in sync. For example,
+ it could use the same style hashing routines as in ufs/ufs_ihash.c
+
+4. It would be nice to be able to use the vfs_cluster code.
+ Unfortunately, if the logical block size is smaller than the page size,
+ it won't work. Also, if throughtput is relatively constant for any
+ block size (as it is for the HP drive--150kbs) then clustering may not
+ buy much (or may even hurt when vfs_cluster comes up with a large sync
+ cluster).
+
+5. Seems like there should be a "notrans" or some such mount option to show
+ filenames as they really are without lower-casing, stripping of version
+ numbers, etc. Does this make sense?
diff --git a/sys/isofs/cd9660/cd9660_bmap.c b/sys/isofs/cd9660/cd9660_bmap.c
new file mode 100644
index 0000000..911eedf
--- /dev/null
+++ b/sys/isofs/cd9660/cd9660_bmap.c
@@ -0,0 +1,102 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cd9660_bmap.c 8.3 (Berkeley) 1/23/94
+ */
+
+#include <sys/param.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+
+#include <isofs/cd9660/iso.h>
+#include <isofs/cd9660/cd9660_node.h>
+
+/*
+ * Bmap converts a the logical block number of a file to its physical block
+ * number on the disk. The conversion is done by using the logical block
+ * number to index into the data block (extent) for the file.
+ */
+int
+cd9660_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap;
+{
+ struct iso_node *ip = VTOI(ap->a_vp);
+ daddr_t lblkno = ap->a_bn;
+ long bsize;
+
+ /*
+ * Check for underlying vnode requests and ensure that logical
+ * to physical mapping is requested.
+ */
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = ip->i_devvp;
+ if (ap->a_bnp == NULL)
+ return (0);
+
+ /*
+ * Compute the requested block number
+ */
+ bsize = ip->i_mnt->logical_block_size;
+ *ap->a_bnp = (ip->iso_start + lblkno) * btodb(bsize);
+
+ /*
+ * Determine maximum number of readahead blocks following the
+ * requested block.
+ */
+ if (ap->a_runp) {
+ int nblk;
+
+ nblk = (ip->i_size - (lblkno + 1) * bsize) / bsize;
+ if (nblk <= 0)
+ *ap->a_runp = 0;
+ else if (nblk >= MAXBSIZE/bsize)
+ *ap->a_runp = MAXBSIZE/bsize - 1;
+ else
+ *ap->a_runp = nblk;
+ }
+
+ return 0;
+}
diff --git a/sys/isofs/cd9660/cd9660_lookup.c b/sys/isofs/cd9660/cd9660_lookup.c
new file mode 100644
index 0000000..36daffd
--- /dev/null
+++ b/sys/isofs/cd9660/cd9660_lookup.c
@@ -0,0 +1,467 @@
+/*-
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)ufs_lookup.c 7.33 (Berkeley) 5/19/91
+ *
+ * @(#)cd9660_lookup.c 8.2 (Berkeley) 1/23/94
+ */
+
+#include <sys/param.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+
+#include <isofs/cd9660/iso.h>
+#include <isofs/cd9660/cd9660_node.h>
+#include <isofs/cd9660/iso_rrip.h>
+#include <isofs/cd9660/cd9660_rrip.h>
+
+struct nchstats iso_nchstats;
+
+/*
+ * Convert a component of a pathname into a pointer to a locked inode.
+ * This is a very central and rather complicated routine.
+ * If the file system is not maintained in a strict tree hierarchy,
+ * this can result in a deadlock situation (see comments in code below).
+ *
+ * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
+ * whether the name is to be looked up, created, renamed, or deleted.
+ * When CREATE, RENAME, or DELETE is specified, information usable in
+ * creating, renaming, or deleting a directory entry may be calculated.
+ * If flag has LOCKPARENT or'ed into it and the target of the pathname
+ * exists, lookup returns both the target and its parent directory locked.
+ * When creating or renaming and LOCKPARENT is specified, the target may
+ * not be ".". When deleting and LOCKPARENT is specified, the target may
+ * be "."., but the caller must check to ensure it does an vrele and iput
+ * instead of two iputs.
+ *
+ * Overall outline of ufs_lookup:
+ *
+ * check accessibility of directory
+ * look for name in cache, if found, then if at end of path
+ * and deleting or creating, drop it, else return name
+ * search for name in directory, to found or notfound
+ * notfound:
+ * if creating, return locked directory, leaving info on available slots
+ * else return error
+ * found:
+ * if at end of path and deleting, return information to allow delete
+ * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
+ * inode and return info to allow rewrite
+ * if not at end, add name to cache; if at end and neither creating
+ * nor deleting, add name to cache
+ *
+ * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode unlocked.
+ */
+int
+cd9660_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ register struct vnode *vdp; /* vnode for directory being searched */
+ register struct iso_node *dp; /* inode for directory being searched */
+ register struct iso_mnt *imp; /* file system that directory is in */
+ struct buf *bp; /* a buffer of directory entries */
+ struct iso_directory_record *ep = 0;/* the current directory entry */
+ int entryoffsetinblock; /* offset of ep in bp's buffer */
+ int saveoffset = 0; /* offset of last directory entry in dir */
+ int numdirpasses; /* strategy for directory search */
+ doff_t endsearch; /* offset to end directory search */
+ struct iso_node *pdp; /* saved dp during symlink work */
+ struct iso_node *tdp; /* returned by iget */
+ int lockparent; /* 1 => lockparent flag is set */
+ int wantparent; /* 1 => wantparent or lockparent flag */
+ int error;
+ ino_t ino = 0;
+ int reclen;
+ u_short namelen;
+ char altname[NAME_MAX];
+ int res;
+ int assoc, len;
+ char *name;
+ struct vnode **vpp = ap->a_vpp;
+ struct componentname *cnp = ap->a_cnp;
+ struct ucred *cred = cnp->cn_cred;
+ int flags = cnp->cn_flags;
+ int nameiop = cnp->cn_nameiop;
+
+ bp = NULL;
+ *vpp = NULL;
+ vdp = ap->a_dvp;
+ dp = VTOI(vdp);
+ imp = dp->i_mnt;
+ lockparent = flags & LOCKPARENT;
+ wantparent = flags & (LOCKPARENT|WANTPARENT);
+
+ /*
+ * Check accessiblity of directory.
+ */
+ if (vdp->v_type != VDIR)
+ return (ENOTDIR);
+ if (error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc))
+ return (error);
+
+ /*
+ * We now have a segment name to search for, and a directory to search.
+ *
+ * Before tediously performing a linear scan of the directory,
+ * check the name cache to see if the directory/name pair
+ * we are looking for is known already.
+ */
+ if (error = cache_lookup(vdp, vpp, cnp)) {
+ int vpid; /* capability number of vnode */
+
+ if (error == ENOENT)
+ return (error);
+#ifdef PARANOID
+ if ((vdp->v_flag & VROOT) && (flags & ISDOTDOT))
+ panic("ufs_lookup: .. through root");
+#endif
+ /*
+ * Get the next vnode in the path.
+ * See comment below starting `Step through' for
+ * an explaination of the locking protocol.
+ */
+ pdp = dp;
+ dp = VTOI(*vpp);
+ vdp = *vpp;
+ vpid = vdp->v_id;
+ if (pdp == dp) {
+ VREF(vdp);
+ error = 0;
+ } else if (flags & ISDOTDOT) {
+ ISO_IUNLOCK(pdp);
+ error = vget(vdp, 1);
+ if (!error && lockparent && (flags & ISLASTCN))
+ ISO_ILOCK(pdp);
+ } else {
+ error = vget(vdp, 1);
+ if (!lockparent || error || !(flags & ISLASTCN))
+ ISO_IUNLOCK(pdp);
+ }
+ /*
+ * Check that the capability number did not change
+ * while we were waiting for the lock.
+ */
+ if (!error) {
+ if (vpid == vdp->v_id)
+ return (0);
+ iso_iput(dp);
+ if (lockparent && pdp != dp && (flags & ISLASTCN))
+ ISO_IUNLOCK(pdp);
+ }
+ ISO_ILOCK(pdp);
+ dp = pdp;
+ vdp = ITOV(dp);
+ *vpp = NULL;
+ }
+
+ len = cnp->cn_namelen;
+ name = cnp->cn_nameptr;
+ /*
+ * A leading `=' means, we are looking for an associated file
+ */
+ if (assoc = (imp->iso_ftype != ISO_FTYPE_RRIP && *name == ASSOCCHAR)) {
+ len--;
+ name++;
+ }
+
+ /*
+ * If there is cached information on a previous search of
+ * this directory, pick up where we last left off.
+ * We cache only lookups as these are the most common
+ * and have the greatest payoff. Caching CREATE has little
+ * benefit as it usually must search the entire directory
+ * to determine that the entry does not exist. Caching the
+ * location of the last DELETE or RENAME has not reduced
+ * profiling time and hence has been removed in the interest
+ * of simplicity.
+ */
+ if (nameiop != LOOKUP || dp->i_diroff == 0 ||
+ dp->i_diroff > dp->i_size) {
+ entryoffsetinblock = 0;
+ dp->i_offset = 0;
+ numdirpasses = 1;
+ } else {
+ dp->i_offset = dp->i_diroff;
+ entryoffsetinblock = iso_blkoff(imp, dp->i_offset);
+ if (entryoffsetinblock != 0) {
+ if (error = iso_blkatoff(dp, dp->i_offset, &bp))
+ return (error);
+ }
+ numdirpasses = 2;
+ iso_nchstats.ncs_2passes++;
+ }
+ endsearch = roundup(dp->i_size, imp->logical_block_size);
+
+searchloop:
+ while (dp->i_offset < endsearch) {
+ /*
+ * If offset is on a block boundary,
+ * read the next directory block.
+ * Release previous if it exists.
+ */
+ if (iso_blkoff(imp, dp->i_offset) == 0) {
+ if (bp != NULL)
+ brelse(bp);
+ if (error = iso_blkatoff(dp, dp->i_offset, &bp))
+ return (error);
+ entryoffsetinblock = 0;
+ }
+ /*
+ * Get pointer to next entry.
+ */
+ ep = (struct iso_directory_record *)
+ (bp->b_un.b_addr + entryoffsetinblock);
+
+ reclen = isonum_711 (ep->length);
+ if (reclen == 0) {
+ /* skip to next block, if any */
+ dp->i_offset =
+ roundup(dp->i_offset, imp->logical_block_size);
+ continue;
+ }
+
+ if (reclen < ISO_DIRECTORY_RECORD_SIZE)
+ /* illegal entry, stop */
+ break;
+
+ if (entryoffsetinblock + reclen > imp->logical_block_size)
+ /* entries are not allowed to cross boundaries */
+ break;
+
+ /*
+ * Check for a name match.
+ */
+ namelen = isonum_711(ep->name_len);
+
+ if (reclen < ISO_DIRECTORY_RECORD_SIZE + namelen)
+ /* illegal entry, stop */
+ break;
+
+ switch (imp->iso_ftype) {
+ default:
+ if ((!(isonum_711(ep->flags)&4)) == !assoc) {
+ if ((len == 1
+ && *name == '.')
+ || (flags & ISDOTDOT)) {
+ if (namelen == 1
+ && ep->name[0] == ((flags & ISDOTDOT) ? 1 : 0)) {
+ /*
+ * Save directory entry's inode number and
+ * reclen in ndp->ni_ufs area, and release
+ * directory buffer.
+ */
+ isodirino(&dp->i_ino,ep,imp);
+ goto found;
+ }
+ if (namelen != 1
+ || ep->name[0] != 0)
+ goto notfound;
+ } else if (!(res = isofncmp(name,len,
+ ep->name,namelen))) {
+ if (isonum_711(ep->flags)&2)
+ isodirino(&ino,ep,imp);
+ else
+ ino = dbtob(bp->b_blkno)
+ + entryoffsetinblock;
+ saveoffset = dp->i_offset;
+ } else if (ino)
+ goto foundino;
+#ifdef NOSORTBUG /* On some CDs directory entries are not sorted correctly */
+ else if (res < 0)
+ goto notfound;
+ else if (res > 0 && numdirpasses == 2)
+ numdirpasses++;
+#endif
+ }
+ break;
+ case ISO_FTYPE_RRIP:
+ if (isonum_711(ep->flags)&2)
+ isodirino(&ino,ep,imp);
+ else
+ ino = dbtob(bp->b_blkno) + entryoffsetinblock;
+ dp->i_ino = ino;
+ cd9660_rrip_getname(ep,altname,&namelen,&dp->i_ino,imp);
+ if (namelen == cnp->cn_namelen
+ && !bcmp(name,altname,namelen))
+ goto found;
+ ino = 0;
+ break;
+ }
+ dp->i_offset += reclen;
+ entryoffsetinblock += reclen;
+ }
+ if (ino) {
+foundino:
+ dp->i_ino = ino;
+ if (saveoffset != dp->i_offset) {
+ if (iso_lblkno(imp,dp->i_offset)
+ != iso_lblkno(imp,saveoffset)) {
+ if (bp != NULL)
+ brelse(bp);
+ if (error = iso_blkatoff(dp, saveoffset, &bp))
+ return (error);
+ }
+ ep = (struct iso_directory_record *)(bp->b_un.b_addr
+ + iso_blkoff(imp,saveoffset));
+ dp->i_offset = saveoffset;
+ }
+ goto found;
+ }
+notfound:
+ /*
+ * If we started in the middle of the directory and failed
+ * to find our target, we must check the beginning as well.
+ */
+ if (numdirpasses == 2) {
+ numdirpasses--;
+ dp->i_offset = 0;
+ endsearch = dp->i_diroff;
+ goto searchloop;
+ }
+ if (bp != NULL)
+ brelse(bp);
+ /*
+ * Insert name into cache (as non-existent) if appropriate.
+ */
+ if (cnp->cn_flags & MAKEENTRY)
+ cache_enter(vdp, *vpp, cnp);
+ if (nameiop == CREATE || nameiop == RENAME)
+ return (EJUSTRETURN);
+ return (ENOENT);
+
+found:
+ if (numdirpasses == 2)
+ iso_nchstats.ncs_pass2++;
+ if (bp != NULL)
+ brelse(bp);
+
+ /*
+ * Found component in pathname.
+ * If the final component of path name, save information
+ * in the cache as to where the entry was found.
+ */
+ if ((flags & ISLASTCN) && nameiop == LOOKUP)
+ dp->i_diroff = dp->i_offset;
+
+ /*
+ * Step through the translation in the name. We do not `iput' the
+ * directory because we may need it again if a symbolic link
+ * is relative to the current directory. Instead we save it
+ * unlocked as "pdp". We must get the target inode before unlocking
+ * the directory to insure that the inode will not be removed
+ * before we get it. We prevent deadlock by always fetching
+ * inodes from the root, moving down the directory tree. Thus
+ * when following backward pointers ".." we must unlock the
+ * parent directory before getting the requested directory.
+ * There is a potential race condition here if both the current
+ * and parent directories are removed before the `iget' for the
+ * inode associated with ".." returns. We hope that this occurs
+ * infrequently since we cannot avoid this race condition without
+ * implementing a sophisticated deadlock detection algorithm.
+ * Note also that this simple deadlock detection scheme will not
+ * work if the file system has any hard links other than ".."
+ * that point backwards in the directory structure.
+ */
+ pdp = dp;
+ /*
+ * If ino is different from dp->i_ino,
+ * it's a relocated directory.
+ */
+ if (flags & ISDOTDOT) {
+ ISO_IUNLOCK(pdp); /* race to get the inode */
+ if (error = iso_iget(dp,dp->i_ino,
+ dp->i_ino != ino,
+ &tdp,ep)) {
+ ISO_ILOCK(pdp);
+ return (error);
+ }
+ if (lockparent && (flags & ISLASTCN))
+ ISO_ILOCK(pdp);
+ *vpp = ITOV(tdp);
+ } else if (dp->i_number == dp->i_ino) {
+ VREF(vdp); /* we want ourself, ie "." */
+ *vpp = vdp;
+ } else {
+ if (error = iso_iget(dp,dp->i_ino,dp->i_ino!=ino,&tdp,ep))
+ return (error);
+ if (!lockparent || !(flags & ISLASTCN))
+ ISO_IUNLOCK(pdp);
+ *vpp = ITOV(tdp);
+ }
+
+ /*
+ * Insert name into cache if appropriate.
+ */
+ if (cnp->cn_flags & MAKEENTRY)
+ cache_enter(vdp, *vpp, cnp);
+ return (0);
+}
+
+/*
+ * Return buffer with contents of block "offset"
+ * from the beginning of directory "ip". If "res"
+ * is non-zero, fill it in with a pointer to the
+ * remaining space in the directory.
+ */
+int
+iso_blkatoff(ip, offset, bpp)
+ struct iso_node *ip;
+ doff_t offset;
+ struct buf **bpp;
+{
+ register struct iso_mnt *imp = ip->i_mnt;
+ daddr_t lbn = iso_lblkno(imp,offset);
+ int bsize = iso_blksize(imp,ip,lbn);
+ struct buf *bp;
+ int error;
+
+ if (error = bread(ITOV(ip),lbn,bsize,NOCRED,&bp)) {
+ brelse(bp);
+ *bpp = 0;
+ return (error);
+ }
+ *bpp = bp;
+
+ return (0);
+}
diff --git a/sys/isofs/cd9660/cd9660_node.c b/sys/isofs/cd9660/cd9660_node.c
new file mode 100644
index 0000000..f9641ff
--- /dev/null
+++ b/sys/isofs/cd9660/cd9660_node.c
@@ -0,0 +1,658 @@
+/*-
+ * Copyright (c) 1982, 1986, 1989, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cd9660_node.c 8.2 (Berkeley) 1/23/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/stat.h>
+
+#include <isofs/cd9660/iso.h>
+#include <isofs/cd9660/cd9660_node.h>
+#include <isofs/cd9660/iso_rrip.h>
+
+#define INOHSZ 512
+#if ((INOHSZ&(INOHSZ-1)) == 0)
+#define INOHASH(dev,ino) (((dev)+((ino)>>12))&(INOHSZ-1))
+#else
+#define INOHASH(dev,ino) (((unsigned)((dev)+((ino)>>12)))%INOHSZ)
+#endif
+
+union iso_ihead {
+ union iso_ihead *ih_head[2];
+ struct iso_node *ih_chain[2];
+} iso_ihead[INOHSZ];
+
+#ifdef ISODEVMAP
+#define DNOHSZ 64
+#if ((DNOHSZ&(DNOHSZ-1)) == 0)
+#define DNOHASH(dev,ino) (((dev)+((ino)>>12))&(DNOHSZ-1))
+#else
+#define DNOHASH(dev,ino) (((unsigned)((dev)+((ino)>>12)))%DNOHSZ)
+#endif
+
+union iso_dhead {
+ union iso_dhead *dh_head[2];
+ struct iso_dnode *dh_chain[2];
+} iso_dhead[DNOHSZ];
+#endif
+
+int prtactive; /* 1 => print out reclaim of active vnodes */
+
+/*
+ * Initialize hash links for inodes and dnodes.
+ */
+int
+cd9660_init()
+{
+ register int i;
+ register union iso_ihead *ih = iso_ihead;
+#ifdef ISODEVMAP
+ register union iso_dhead *dh = iso_dhead;
+#endif
+
+ for (i = INOHSZ; --i >= 0; ih++) {
+ ih->ih_head[0] = ih;
+ ih->ih_head[1] = ih;
+ }
+#ifdef ISODEVMAP
+ for (i = DNOHSZ; --i >= 0; dh++) {
+ dh->dh_head[0] = dh;
+ dh->dh_head[1] = dh;
+ }
+#endif
+ return (0);
+}
+
+#ifdef ISODEVMAP
+/*
+ * Enter a new node into the device hash list
+ */
+struct iso_dnode *
+iso_dmap(dev,ino,create)
+ dev_t dev;
+ ino_t ino;
+ int create;
+{
+ struct iso_dnode *dp;
+ union iso_dhead *dh;
+
+ dh = &iso_dhead[DNOHASH(dev, ino)];
+ for (dp = dh->dh_chain[0];
+ dp != (struct iso_dnode *)dh;
+ dp = dp->d_forw)
+ if (ino == dp->i_number && dev == dp->i_dev)
+ return dp;
+
+ if (!create)
+ return (struct iso_dnode *)0;
+
+ MALLOC(dp,struct iso_dnode *,sizeof(struct iso_dnode),M_CACHE,M_WAITOK);
+ dp->i_dev = dev;
+ dp->i_number = ino;
+ insque(dp,dh);
+
+ return dp;
+}
+
+void
+iso_dunmap(dev)
+ dev_t dev;
+{
+ struct iso_dnode *dp, *dq;
+ union iso_dhead *dh;
+
+ for (dh = iso_dhead; dh < iso_dhead + DNOHSZ; dh++) {
+ for (dp = dh->dh_chain[0];
+ dp != (struct iso_dnode *)dh;
+ dp = dq) {
+ dq = dp->d_forw;
+ if (dev == dp->i_dev) {
+ remque(dp);
+ FREE(dp,M_CACHE);
+ }
+ }
+ }
+}
+#endif
+
+/*
+ * Look up a ISOFS dinode number to find its incore vnode.
+ * If it is not in core, read it in from the specified device.
+ * If it is in core, wait for the lock bit to clear, then
+ * return the inode locked. Detection and handling of mount
+ * points must be done by the calling routine.
+ */
+int
+iso_iget(xp, ino, relocated, ipp, isodir)
+ struct iso_node *xp;
+ ino_t ino;
+ int relocated;
+ struct iso_node **ipp;
+ struct iso_directory_record *isodir;
+{
+ dev_t dev = xp->i_dev;
+ struct mount *mntp = ITOV(xp)->v_mount;
+ register struct iso_node *ip, *iq;
+ register struct vnode *vp;
+ register struct iso_dnode *dp;
+ struct vnode *nvp;
+ struct buf *bp = NULL, *bp2 = NULL;
+ union iso_ihead *ih;
+ union iso_dhead *dh;
+ int i, error, result;
+ struct iso_mnt *imp;
+ ino_t defino;
+
+ ih = &iso_ihead[INOHASH(dev, ino)];
+loop:
+ for (ip = ih->ih_chain[0];
+ ip != (struct iso_node *)ih;
+ ip = ip->i_forw) {
+ if (ino != ip->i_number || dev != ip->i_dev)
+ continue;
+ if ((ip->i_flag&ILOCKED) != 0) {
+ ip->i_flag |= IWANT;
+ sleep((caddr_t)ip, PINOD);
+ goto loop;
+ }
+ if (vget(ITOV(ip), 1))
+ goto loop;
+ *ipp = ip;
+ return 0;
+ }
+ /*
+ * Allocate a new vnode/iso_node.
+ */
+ if (error = getnewvnode(VT_ISOFS, mntp, cd9660_vnodeop_p, &nvp)) {
+ *ipp = 0;
+ return error;
+ }
+ MALLOC(ip, struct iso_node *, sizeof(struct iso_node),
+ M_ISOFSNODE, M_WAITOK);
+ bzero((caddr_t)ip, sizeof(struct iso_node));
+ nvp->v_data = ip;
+ ip->i_vnode = nvp;
+ ip->i_flag = 0;
+ ip->i_devvp = 0;
+ ip->i_diroff = 0;
+ ip->i_lockf = 0;
+
+ /*
+ * Put it onto its hash chain and lock it so that other requests for
+ * this inode will block if they arrive while we are sleeping waiting
+ * for old data structures to be purged or for the contents of the
+ * disk portion of this inode to be read.
+ */
+ ip->i_dev = dev;
+ ip->i_number = ino;
+ insque(ip, ih);
+ ISO_ILOCK(ip);
+
+ imp = VFSTOISOFS (mntp);
+ ip->i_mnt = imp;
+ ip->i_devvp = imp->im_devvp;
+ VREF(ip->i_devvp);
+
+ if (relocated) {
+ /*
+ * On relocated directories we must
+ * read the `.' entry out of a dir.
+ */
+ ip->iso_start = ino >> imp->im_bshift;
+ if (error = iso_blkatoff(ip,0,&bp)) {
+ vrele(ip->i_devvp);
+ remque(ip);
+ ip->i_forw = ip;
+ ip->i_back = ip;
+ iso_iput(ip);
+ *ipp = 0;
+ return error;
+ }
+ isodir = (struct iso_directory_record *)bp->b_un.b_addr;
+ }
+
+ ip->iso_extent = isonum_733(isodir->extent);
+ ip->i_size = isonum_733(isodir->size);
+ ip->iso_start = isonum_711(isodir->ext_attr_length) + ip->iso_extent;
+
+ vp = ITOV(ip);
+
+ /*
+ * Setup time stamp, attribute
+ */
+ vp->v_type = VNON;
+ switch (imp->iso_ftype) {
+ default: /* ISO_FTYPE_9660 */
+ if ((imp->im_flags&ISOFSMNT_EXTATT)
+ && isonum_711(isodir->ext_attr_length))
+ iso_blkatoff(ip,-isonum_711(isodir->ext_attr_length),
+ &bp2);
+ cd9660_defattr(isodir,ip,bp2 );
+ cd9660_deftstamp(isodir,ip,bp2 );
+ break;
+ case ISO_FTYPE_RRIP:
+ result = cd9660_rrip_analyze(isodir,ip,imp);
+ break;
+ }
+ if (bp2)
+ brelse(bp2);
+ if (bp)
+ brelse(bp);
+
+ /*
+ * Initialize the associated vnode
+ */
+ vp->v_type = IFTOVT(ip->inode.iso_mode);
+
+ if ( vp->v_type == VFIFO ) {
+#ifdef FIFO
+ extern int (**cd9660_fifoop_p)();
+ vp->v_op = cd9660_fifoop_p;
+#else
+ iso_iput(ip);
+ *ipp = 0;
+ return EOPNOTSUPP;
+#endif /* FIFO */
+ } else if ( vp->v_type == VCHR || vp->v_type == VBLK ) {
+ extern int (**cd9660_specop_p)();
+
+ /*
+ * if device, look at device number table for translation
+ */
+#ifdef ISODEVMAP
+ if (dp = iso_dmap(dev,ino,0))
+ ip->inode.iso_rdev = dp->d_dev;
+#endif
+ vp->v_op = cd9660_specop_p;
+ if (nvp = checkalias(vp, ip->inode.iso_rdev, mntp)) {
+ /*
+ * Reinitialize aliased inode.
+ */
+ vp = nvp;
+ iq = VTOI(vp);
+ iq->i_vnode = vp;
+ iq->i_flag = 0;
+ ISO_ILOCK(iq);
+ iq->i_dev = dev;
+ iq->i_number = ino;
+ iq->i_mnt = ip->i_mnt;
+ bcopy(&ip->iso_extent,&iq->iso_extent,
+ (char *)(ip + 1) - (char *)&ip->iso_extent);
+ insque(iq, ih);
+ /*
+ * Discard unneeded vnode
+ * (This introduces the need of INACTIVE modification)
+ */
+ ip->inode.iso_mode = 0;
+ iso_iput(ip);
+ ip = iq;
+ }
+ }
+
+ if (ip->iso_extent == imp->root_extent)
+ vp->v_flag |= VROOT;
+
+ *ipp = ip;
+ return 0;
+}
+
+/*
+ * Unlock and decrement the reference count of an inode structure.
+ */
+int
+iso_iput(ip)
+ register struct iso_node *ip;
+{
+
+ if ((ip->i_flag & ILOCKED) == 0)
+ panic("iso_iput");
+ ISO_IUNLOCK(ip);
+ vrele(ITOV(ip));
+ return (0);
+}
+
+/*
+ * Last reference to an inode, write the inode out and if necessary,
+ * truncate and deallocate the file.
+ */
+int
+cd9660_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ register struct iso_node *ip = VTOI(vp);
+ int mode, error = 0;
+
+ if (prtactive && vp->v_usecount != 0)
+ vprint("cd9660_inactive: pushing active", vp);
+
+ ip->i_flag = 0;
+ /*
+ * If we are done with the inode, reclaim it
+ * so that it can be reused immediately.
+ */
+ if (vp->v_usecount == 0 && ip->inode.iso_mode == 0)
+ vgone(vp);
+ return error;
+}
+
+/*
+ * Reclaim an inode so that it can be used for other purposes.
+ */
+int
+cd9660_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct iso_node *ip = VTOI(vp);
+ int i;
+
+ if (prtactive && vp->v_usecount != 0)
+ vprint("cd9660_reclaim: pushing active", vp);
+ /*
+ * Remove the inode from its hash chain.
+ */
+ remque(ip);
+ ip->i_forw = ip;
+ ip->i_back = ip;
+ /*
+ * Purge old data structures associated with the inode.
+ */
+ cache_purge(vp);
+ if (ip->i_devvp) {
+ vrele(ip->i_devvp);
+ ip->i_devvp = 0;
+ }
+ FREE(vp->v_data, M_ISOFSNODE);
+ vp->v_data = NULL;
+ return 0;
+}
+
+/*
+ * Lock an inode. If its already locked, set the WANT bit and sleep.
+ */
+int
+iso_ilock(ip)
+ register struct iso_node *ip;
+{
+
+ while (ip->i_flag & ILOCKED) {
+ ip->i_flag |= IWANT;
+ if (ip->i_spare0 == curproc->p_pid)
+ panic("locking against myself");
+ ip->i_spare1 = curproc->p_pid;
+ (void) sleep((caddr_t)ip, PINOD);
+ }
+ ip->i_spare1 = 0;
+ ip->i_spare0 = curproc->p_pid;
+ ip->i_flag |= ILOCKED;
+ return (0);
+}
+
+/*
+ * Unlock an inode. If WANT bit is on, wakeup.
+ */
+int
+iso_iunlock(ip)
+ register struct iso_node *ip;
+{
+
+ if ((ip->i_flag & ILOCKED) == 0)
+ vprint("iso_iunlock: unlocked inode", ITOV(ip));
+ ip->i_spare0 = 0;
+ ip->i_flag &= ~ILOCKED;
+ if (ip->i_flag&IWANT) {
+ ip->i_flag &= ~IWANT;
+ wakeup((caddr_t)ip);
+ }
+ return (0);
+}
+
+/*
+ * File attributes
+ */
+void
+cd9660_defattr(isodir,inop,bp)
+ struct iso_directory_record *isodir;
+ struct iso_node *inop;
+ struct buf *bp;
+{
+ struct buf *bp2 = NULL;
+ struct iso_mnt *imp;
+ struct iso_extended_attributes *ap = NULL;
+ int off;
+
+ if (isonum_711(isodir->flags)&2) {
+ inop->inode.iso_mode = S_IFDIR;
+ /*
+ * If we return 2, fts() will assume there are no subdirectories
+ * (just links for the path and .), so instead we return 1.
+ */
+ inop->inode.iso_links = 1;
+ } else {
+ inop->inode.iso_mode = S_IFREG;
+ inop->inode.iso_links = 1;
+ }
+ if (!bp
+ && ((imp = inop->i_mnt)->im_flags&ISOFSMNT_EXTATT)
+ && (off = isonum_711(isodir->ext_attr_length))) {
+ iso_blkatoff(inop,-off * imp->logical_block_size,&bp2);
+ bp = bp2;
+ }
+ if (bp) {
+ ap = (struct iso_extended_attributes *)bp->b_un.b_addr;
+
+ if (isonum_711(ap->version) == 1) {
+ if (!(ap->perm[0]&0x40))
+ inop->inode.iso_mode |= VEXEC >> 6;
+ if (!(ap->perm[0]&0x10))
+ inop->inode.iso_mode |= VREAD >> 6;
+ if (!(ap->perm[0]&4))
+ inop->inode.iso_mode |= VEXEC >> 3;
+ if (!(ap->perm[0]&1))
+ inop->inode.iso_mode |= VREAD >> 3;
+ if (!(ap->perm[1]&0x40))
+ inop->inode.iso_mode |= VEXEC;
+ if (!(ap->perm[1]&0x10))
+ inop->inode.iso_mode |= VREAD;
+ inop->inode.iso_uid = isonum_723(ap->owner); /* what about 0? */
+ inop->inode.iso_gid = isonum_723(ap->group); /* what about 0? */
+ } else
+ ap = NULL;
+ }
+ if (!ap) {
+ inop->inode.iso_mode |= VREAD|VEXEC|(VREAD|VEXEC)>>3|(VREAD|VEXEC)>>6;
+ inop->inode.iso_uid = (uid_t)0;
+ inop->inode.iso_gid = (gid_t)0;
+ }
+ if (bp2)
+ brelse(bp2);
+}
+
+/*
+ * Time stamps
+ */
+void
+cd9660_deftstamp(isodir,inop,bp)
+ struct iso_directory_record *isodir;
+ struct iso_node *inop;
+ struct buf *bp;
+{
+ struct buf *bp2 = NULL;
+ struct iso_mnt *imp;
+ struct iso_extended_attributes *ap = NULL;
+ int off;
+
+ if (!bp
+ && ((imp = inop->i_mnt)->im_flags&ISOFSMNT_EXTATT)
+ && (off = isonum_711(isodir->ext_attr_length))) {
+ iso_blkatoff(inop,-off * imp->logical_block_size,&bp2);
+ bp = bp2;
+ }
+ if (bp) {
+ ap = (struct iso_extended_attributes *)bp->b_un.b_addr;
+
+ if (isonum_711(ap->version) == 1) {
+ if (!cd9660_tstamp_conv17(ap->ftime,&inop->inode.iso_atime))
+ cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_atime);
+ if (!cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_ctime))
+ inop->inode.iso_ctime = inop->inode.iso_atime;
+ if (!cd9660_tstamp_conv17(ap->mtime,&inop->inode.iso_mtime))
+ inop->inode.iso_mtime = inop->inode.iso_ctime;
+ } else
+ ap = NULL;
+ }
+ if (!ap) {
+ cd9660_tstamp_conv7(isodir->date,&inop->inode.iso_ctime);
+ inop->inode.iso_atime = inop->inode.iso_ctime;
+ inop->inode.iso_mtime = inop->inode.iso_ctime;
+ }
+ if (bp2)
+ brelse(bp2);
+}
+
+int
+cd9660_tstamp_conv7(pi,pu)
+char *pi;
+struct timeval *pu;
+{
+ int i;
+ int crtime, days;
+ int y, m, d, hour, minute, second, tz;
+
+ y = pi[0] + 1900;
+ m = pi[1];
+ d = pi[2];
+ hour = pi[3];
+ minute = pi[4];
+ second = pi[5];
+ tz = pi[6];
+
+ if (y < 1970) {
+ pu->tv_sec = 0;
+ pu->tv_usec = 0;
+ return 0;
+ } else {
+#ifdef ORIGINAL
+ /* computes day number relative to Sept. 19th,1989 */
+ /* don't even *THINK* about changing formula. It works! */
+ days = 367*(y-1980)-7*(y+(m+9)/12)/4-3*((y+(m-9)/7)/100+1)/4+275*m/9+d-100;
+#else
+ /*
+ * Changed :-) to make it relative to Jan. 1st, 1970
+ * and to disambiguate negative division
+ */
+ days = 367*(y-1960)-7*(y+(m+9)/12)/4-3*((y+(m+9)/12-1)/100+1)/4+275*m/9+d-239;
+#endif
+ crtime = ((((days * 24) + hour) * 60 + minute) * 60) + second;
+
+ /* timezone offset is unreliable on some disks */
+ if (-48 <= tz && tz <= 52)
+ crtime += tz * 15 * 60;
+ }
+ pu->tv_sec = crtime;
+ pu->tv_usec = 0;
+ return 1;
+}
+
+static unsigned
+cd9660_chars2ui(begin,len)
+ unsigned char *begin;
+ int len;
+{
+ unsigned rc;
+
+ for (rc = 0; --len >= 0;) {
+ rc *= 10;
+ rc += *begin++ - '0';
+ }
+ return rc;
+}
+
+int
+cd9660_tstamp_conv17(pi,pu)
+ unsigned char *pi;
+ struct timeval *pu;
+{
+ unsigned char buf[7];
+
+ /* year:"0001"-"9999" -> -1900 */
+ buf[0] = cd9660_chars2ui(pi,4) - 1900;
+
+ /* month: " 1"-"12" -> 1 - 12 */
+ buf[1] = cd9660_chars2ui(pi + 4,2);
+
+ /* day: " 1"-"31" -> 1 - 31 */
+ buf[2] = cd9660_chars2ui(pi + 6,2);
+
+ /* hour: " 0"-"23" -> 0 - 23 */
+ buf[3] = cd9660_chars2ui(pi + 8,2);
+
+ /* minute:" 0"-"59" -> 0 - 59 */
+ buf[4] = cd9660_chars2ui(pi + 10,2);
+
+ /* second:" 0"-"59" -> 0 - 59 */
+ buf[5] = cd9660_chars2ui(pi + 12,2);
+
+ /* difference of GMT */
+ buf[6] = pi[16];
+
+ return cd9660_tstamp_conv7(buf,pu);
+}
+
+void
+isodirino(inump,isodir,imp)
+ ino_t *inump;
+ struct iso_directory_record *isodir;
+ struct iso_mnt *imp;
+{
+ *inump = (isonum_733(isodir->extent) + isonum_711(isodir->ext_attr_length))
+ * imp->logical_block_size;
+}
diff --git a/sys/isofs/cd9660/cd9660_node.h b/sys/isofs/cd9660/cd9660_node.h
new file mode 100644
index 0000000..45de67f
--- /dev/null
+++ b/sys/isofs/cd9660/cd9660_node.h
@@ -0,0 +1,143 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cd9660_node.h 8.2 (Berkeley) 1/23/94
+ */
+
+/*
+ * Theoretically, directories can be more than 2Gb in length,
+ * however, in practice this seems unlikely. So, we define
+ * the type doff_t as a long to keep down the cost of doing
+ * lookup on a 32-bit machine. If you are porting to a 64-bit
+ * architecture, you should make doff_t the same as off_t.
+ */
+#define doff_t long
+
+typedef struct {
+ struct timespec iso_atime; /* time of last access */
+ struct timespec iso_mtime; /* time of last modification */
+ struct timespec iso_ctime; /* time file changed */
+ u_short iso_mode; /* files access mode and type */
+ uid_t iso_uid; /* owner user id */
+ gid_t iso_gid; /* owner group id */
+ short iso_links; /* links of file */
+ dev_t iso_rdev; /* Major/Minor number for special */
+} ISO_RRIP_INODE;
+
+#ifdef ISODEVMAP
+/*
+ * FOr device# (major,minor) translation table
+ */
+struct iso_dnode {
+ struct iso_dnode *d_chain[2]; /* hash chain, MUST be first */
+ dev_t i_dev; /* device where dnode resides */
+ ino_t i_number; /* the identity of the inode */
+ dev_t d_dev; /* device # for translation */
+};
+#define d_forw d_chain[0]
+#define d_back d_chain[1]
+#endif
+
+struct iso_node {
+ struct iso_node *i_chain[2]; /* hash chain, MUST be first */
+ struct vnode *i_vnode; /* vnode associated with this inode */
+ struct vnode *i_devvp; /* vnode for block I/O */
+ u_long i_flag; /* see below */
+ dev_t i_dev; /* device where inode resides */
+ ino_t i_number; /* the identity of the inode */
+ /* we use the actual starting block of the file */
+ struct iso_mnt *i_mnt; /* filesystem associated with this inode */
+ struct lockf *i_lockf; /* head of byte-level lock list */
+ doff_t i_endoff; /* end of useful stuff in directory */
+ doff_t i_diroff; /* offset in dir, where we found last entry */
+ doff_t i_offset; /* offset of free space in directory */
+ ino_t i_ino; /* inode number of found directory */
+ long i_spare0;
+ long i_spare1;
+
+ long iso_extent; /* extent of file */
+ long i_size;
+ long iso_start; /* actual start of data of file (may be different */
+ /* from iso_extent, if file has extended attributes) */
+ ISO_RRIP_INODE inode;
+};
+
+#define i_forw i_chain[0]
+#define i_back i_chain[1]
+
+/* flags */
+#define ILOCKED 0x0001 /* inode is locked */
+#define IWANT 0x0002 /* some process waiting on lock */
+#define IACC 0x0020 /* inode access time to be updated */
+
+#define VTOI(vp) ((struct iso_node *)(vp)->v_data)
+#define ITOV(ip) ((ip)->i_vnode)
+
+#define ISO_ILOCK(ip) iso_ilock(ip)
+#define ISO_IUNLOCK(ip) iso_iunlock(ip)
+
+/*
+ * Prototypes for ISOFS vnode operations
+ */
+int cd9660_lookup __P((struct vop_lookup_args *));
+int cd9660_open __P((struct vop_open_args *));
+int cd9660_close __P((struct vop_close_args *));
+int cd9660_access __P((struct vop_access_args *));
+int cd9660_getattr __P((struct vop_getattr_args *));
+int cd9660_read __P((struct vop_read_args *));
+int cd9660_ioctl __P((struct vop_ioctl_args *));
+int cd9660_select __P((struct vop_select_args *));
+int cd9660_mmap __P((struct vop_mmap_args *));
+int cd9660_seek __P((struct vop_seek_args *));
+int cd9660_readdir __P((struct vop_readdir_args *));
+int cd9660_abortop __P((struct vop_abortop_args *));
+int cd9660_inactive __P((struct vop_inactive_args *));
+int cd9660_reclaim __P((struct vop_reclaim_args *));
+int cd9660_bmap __P((struct vop_bmap_args *));
+int cd9660_lock __P((struct vop_lock_args *));
+int cd9660_unlock __P((struct vop_unlock_args *));
+int cd9660_strategy __P((struct vop_strategy_args *));
+int cd9660_print __P((struct vop_print_args *));
+int cd9660_islocked __P((struct vop_islocked_args *));
+void cd9660_defattr __P((struct iso_directory_record *,
+ struct iso_node *, struct buf *));
+void cd9660_deftstamp __P((struct iso_directory_record *,
+ struct iso_node *, struct buf *));
+#ifdef ISODEVMAP
+struct iso_dnode *iso_dmap __P((dev_t, ino_t, int));
+void iso_dunmap __P((dev_t));
+#endif
diff --git a/sys/isofs/cd9660/cd9660_rrip.c b/sys/isofs/cd9660/cd9660_rrip.c
new file mode 100644
index 0000000..0923fa0
--- /dev/null
+++ b/sys/isofs/cd9660/cd9660_rrip.c
@@ -0,0 +1,685 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cd9660_rrip.c 8.2 (Berkeley) 1/23/94
+ */
+
+#include <sys/param.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <sys/time.h>
+
+#include <isofs/cd9660/iso.h>
+#include <isofs/cd9660/cd9660_node.h>
+#include <isofs/cd9660/cd9660_rrip.h>
+#include <isofs/cd9660/iso_rrip.h>
+
+/*
+ * POSIX file attribute
+ */
+static int
+cd9660_rrip_attr(p,ana)
+ ISO_RRIP_ATTR *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ ana->inop->inode.iso_mode = isonum_731(p->mode_l);
+ ana->inop->inode.iso_uid = (uid_t)isonum_731(p->uid_l);
+ ana->inop->inode.iso_gid = (gid_t)isonum_731(p->gid_l);
+ ana->inop->inode.iso_links = isonum_731(p->links_l);
+ ana->fields &= ~ISO_SUSP_ATTR;
+ return ISO_SUSP_ATTR;
+}
+
+static void
+cd9660_rrip_defattr(isodir,ana)
+ struct iso_directory_record *isodir;
+ ISO_RRIP_ANALYZE *ana;
+{
+ /* But this is a required field! */
+ printf("RRIP without PX field?\n");
+ cd9660_defattr(isodir,ana->inop,NULL);
+}
+
+/*
+ * Symbolic Links
+ */
+static int
+cd9660_rrip_slink(p,ana)
+ ISO_RRIP_SLINK *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ register ISO_RRIP_SLINK_COMPONENT *pcomp;
+ register ISO_RRIP_SLINK_COMPONENT *pcompe;
+ int len, wlen, cont;
+ char *outbuf, *inbuf;
+
+ pcomp = (ISO_RRIP_SLINK_COMPONENT *)p->component;
+ pcompe = (ISO_RRIP_SLINK_COMPONENT *)((char *)p + isonum_711(p->h.length));
+ len = *ana->outlen;
+ outbuf = ana->outbuf;
+ cont = ana->cont;
+
+ /*
+ * Gathering a Symbolic name from each component with path
+ */
+ for (;
+ pcomp < pcompe;
+ pcomp = (ISO_RRIP_SLINK_COMPONENT *)((char *)pcomp + ISO_RRIP_SLSIZ
+ + isonum_711(pcomp->clen))) {
+
+ if (!cont) {
+ if (len < ana->maxlen) {
+ len++;
+ *outbuf++ = '/';
+ }
+ }
+ cont = 0;
+
+ inbuf = "..";
+ wlen = 0;
+
+ switch (*pcomp->cflag) {
+
+ case ISO_SUSP_CFLAG_CURRENT:
+ /* Inserting Current */
+ wlen = 1;
+ break;
+
+ case ISO_SUSP_CFLAG_PARENT:
+ /* Inserting Parent */
+ wlen = 2;
+ break;
+
+ case ISO_SUSP_CFLAG_ROOT:
+ /* Inserting slash for ROOT */
+ /* start over from beginning(?) */
+ outbuf -= len;
+ len = 0;
+ break;
+
+ case ISO_SUSP_CFLAG_VOLROOT:
+ /* Inserting a mount point i.e. "/cdrom" */
+ /* same as above */
+ outbuf -= len;
+ len = 0;
+ inbuf = ana->imp->im_mountp->mnt_stat.f_mntonname;
+ wlen = strlen(inbuf);
+ break;
+
+ case ISO_SUSP_CFLAG_HOST:
+ /* Inserting hostname i.e. "kurt.tools.de" */
+ inbuf = hostname;
+ wlen = hostnamelen;
+ break;
+
+ case ISO_SUSP_CFLAG_CONTINUE:
+ cont = 1;
+ /* fall thru */
+ case 0:
+ /* Inserting component */
+ wlen = isonum_711(pcomp->clen);
+ inbuf = pcomp->name;
+ break;
+ default:
+ printf("RRIP with incorrect flags?");
+ wlen = ana->maxlen + 1;
+ break;
+ }
+
+ if (len + wlen > ana->maxlen) {
+ /* indicate error to caller */
+ ana->cont = 1;
+ ana->fields = 0;
+ ana->outbuf -= *ana->outlen;
+ *ana->outlen = 0;
+ return 0;
+ }
+
+ bcopy(inbuf,outbuf,wlen);
+ outbuf += wlen;
+ len += wlen;
+
+ }
+ ana->outbuf = outbuf;
+ *ana->outlen = len;
+ ana->cont = cont;
+
+ if (!isonum_711(p->flags)) {
+ ana->fields &= ~ISO_SUSP_SLINK;
+ return ISO_SUSP_SLINK;
+ }
+ return 0;
+}
+
+/*
+ * Alternate name
+ */
+static int
+cd9660_rrip_altname(p,ana)
+ ISO_RRIP_ALTNAME *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ char *inbuf;
+ int wlen;
+ int cont;
+
+ inbuf = "..";
+ wlen = 0;
+ cont = 0;
+
+ switch (*p->flags) {
+ case ISO_SUSP_CFLAG_CURRENT:
+ /* Inserting Current */
+ wlen = 1;
+ break;
+
+ case ISO_SUSP_CFLAG_PARENT:
+ /* Inserting Parent */
+ wlen = 2;
+ break;
+
+ case ISO_SUSP_CFLAG_HOST:
+ /* Inserting hostname i.e. "kurt.tools.de" */
+ inbuf = hostname;
+ wlen = hostnamelen;
+ break;
+
+ case ISO_SUSP_CFLAG_CONTINUE:
+ cont = 1;
+ /* fall thru */
+ case 0:
+ /* Inserting component */
+ wlen = isonum_711(p->h.length) - 5;
+ inbuf = (char *)p + 5;
+ break;
+
+ default:
+ printf("RRIP with incorrect NM flags?\n");
+ wlen = ana->maxlen + 1;
+ break;
+ }
+
+ if ((*ana->outlen += wlen) > ana->maxlen) {
+ /* treat as no name field */
+ ana->fields &= ~ISO_SUSP_ALTNAME;
+ ana->outbuf -= *ana->outlen - wlen;
+ *ana->outlen = 0;
+ return 0;
+ }
+
+ bcopy(inbuf,ana->outbuf,wlen);
+ ana->outbuf += wlen;
+
+ if (!cont) {
+ ana->fields &= ~ISO_SUSP_ALTNAME;
+ return ISO_SUSP_ALTNAME;
+ }
+ return 0;
+}
+
+static void
+cd9660_rrip_defname(isodir,ana)
+ struct iso_directory_record *isodir;
+ ISO_RRIP_ANALYZE *ana;
+{
+ strcpy(ana->outbuf,"..");
+ switch (*isodir->name) {
+ default:
+ isofntrans(isodir->name,isonum_711(isodir->name_len),
+ ana->outbuf,ana->outlen,
+ 1,isonum_711(isodir->flags)&4);
+ break;
+ case 0:
+ *ana->outlen = 1;
+ break;
+ case 1:
+ *ana->outlen = 2;
+ break;
+ }
+}
+
+/*
+ * Parent or Child Link
+ */
+static int
+cd9660_rrip_pclink(p,ana)
+ ISO_RRIP_CLINK *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ *ana->inump = isonum_733(p->dir_loc) << ana->imp->im_bshift;
+ ana->fields &= ~(ISO_SUSP_CLINK|ISO_SUSP_PLINK);
+ return *p->h.type == 'C' ? ISO_SUSP_CLINK : ISO_SUSP_PLINK;
+}
+
+/*
+ * Relocated directory
+ */
+static int
+cd9660_rrip_reldir(p,ana)
+ ISO_RRIP_RELDIR *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ /* special hack to make caller aware of RE field */
+ *ana->outlen = 0;
+ ana->fields = 0;
+ return ISO_SUSP_RELDIR|ISO_SUSP_ALTNAME|ISO_SUSP_CLINK|ISO_SUSP_PLINK;
+}
+
+static int
+cd9660_rrip_tstamp(p,ana)
+ ISO_RRIP_TSTAMP *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ unsigned char *ptime;
+
+ ptime = p->time;
+
+ /* Check a format of time stamp (7bytes/17bytes) */
+ if (!(*p->flags&ISO_SUSP_TSTAMP_FORM17)) {
+ if (*p->flags&ISO_SUSP_TSTAMP_CREAT)
+ ptime += 7;
+
+ if (*p->flags&ISO_SUSP_TSTAMP_MODIFY) {
+ cd9660_tstamp_conv7(ptime,&ana->inop->inode.iso_mtime);
+ ptime += 7;
+ } else
+ bzero(&ana->inop->inode.iso_mtime,sizeof(struct timeval));
+
+ if (*p->flags&ISO_SUSP_TSTAMP_ACCESS) {
+ cd9660_tstamp_conv7(ptime,&ana->inop->inode.iso_atime);
+ ptime += 7;
+ } else
+ ana->inop->inode.iso_atime = ana->inop->inode.iso_mtime;
+
+ if (*p->flags&ISO_SUSP_TSTAMP_ATTR)
+ cd9660_tstamp_conv7(ptime,&ana->inop->inode.iso_ctime);
+ else
+ ana->inop->inode.iso_ctime = ana->inop->inode.iso_mtime;
+
+ } else {
+ if (*p->flags&ISO_SUSP_TSTAMP_CREAT)
+ ptime += 17;
+
+ if (*p->flags&ISO_SUSP_TSTAMP_MODIFY) {
+ cd9660_tstamp_conv17(ptime,&ana->inop->inode.iso_mtime);
+ ptime += 17;
+ } else
+ bzero(&ana->inop->inode.iso_mtime,sizeof(struct timeval));
+
+ if (*p->flags&ISO_SUSP_TSTAMP_ACCESS) {
+ cd9660_tstamp_conv17(ptime,&ana->inop->inode.iso_atime);
+ ptime += 17;
+ } else
+ ana->inop->inode.iso_atime = ana->inop->inode.iso_mtime;
+
+ if (*p->flags&ISO_SUSP_TSTAMP_ATTR)
+ cd9660_tstamp_conv17(ptime,&ana->inop->inode.iso_ctime);
+ else
+ ana->inop->inode.iso_ctime = ana->inop->inode.iso_mtime;
+
+ }
+ ana->fields &= ~ISO_SUSP_TSTAMP;
+ return ISO_SUSP_TSTAMP;
+}
+
+static void
+cd9660_rrip_deftstamp(isodir,ana)
+ struct iso_directory_record *isodir;
+ ISO_RRIP_ANALYZE *ana;
+{
+ cd9660_deftstamp(isodir,ana->inop,NULL);
+}
+
+/*
+ * POSIX device modes
+ */
+static int
+cd9660_rrip_device(p,ana)
+ ISO_RRIP_DEVICE *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ unsigned high, low;
+
+ high = isonum_733(p->dev_t_high_l);
+ low = isonum_733(p->dev_t_low_l);
+
+ if ( high == 0 ) {
+ ana->inop->inode.iso_rdev = makedev( major(low), minor(low) );
+ } else {
+ ana->inop->inode.iso_rdev = makedev( high, minor(low) );
+ }
+ ana->fields &= ~ISO_SUSP_DEVICE;
+ return ISO_SUSP_DEVICE;
+}
+
+/*
+ * Flag indicating
+ */
+static int
+cd9660_rrip_idflag(p,ana)
+ ISO_RRIP_IDFLAG *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ ana->fields &= isonum_711(p->flags)|~0xff; /* don't touch high bits */
+ /* special handling of RE field */
+ if (ana->fields&ISO_SUSP_RELDIR)
+ return cd9660_rrip_reldir(p,ana);
+
+ return ISO_SUSP_IDFLAG;
+}
+
+/*
+ * Continuation pointer
+ */
+static int
+cd9660_rrip_cont(p,ana)
+ ISO_RRIP_CONT *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ ana->iso_ce_blk = isonum_733(p->location);
+ ana->iso_ce_off = isonum_733(p->offset);
+ ana->iso_ce_len = isonum_733(p->length);
+ return ISO_SUSP_CONT;
+}
+
+/*
+ * System Use end
+ */
+static int
+cd9660_rrip_stop(p,ana)
+ ISO_SUSP_HEADER *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ /* stop analyzing */
+ ana->fields = 0;
+ return ISO_SUSP_STOP;
+}
+
+/*
+ * Extension reference
+ */
+static int
+cd9660_rrip_extref(p,ana)
+ ISO_RRIP_EXTREF *p;
+ ISO_RRIP_ANALYZE *ana;
+{
+ if (isonum_711(p->len_id) != 10
+ || bcmp((char *)p + 8,"RRIP_1991A",10)
+ || isonum_711(p->version) != 1)
+ return 0;
+ ana->fields &= ~ISO_SUSP_EXTREF;
+ return ISO_SUSP_EXTREF;
+}
+
+typedef struct {
+ char type[2];
+ int (*func)();
+ void (*func2)();
+ int result;
+} RRIP_TABLE;
+
+static int
+cd9660_rrip_loop(isodir,ana,table)
+ struct iso_directory_record *isodir;
+ ISO_RRIP_ANALYZE *ana;
+ RRIP_TABLE *table;
+{
+ register RRIP_TABLE *ptable;
+ register ISO_SUSP_HEADER *phead;
+ register ISO_SUSP_HEADER *pend;
+ struct buf *bp = NULL;
+ int i;
+ char *pwhead;
+ int result;
+
+ /*
+ * Note: If name length is odd,
+ * it will be padding 1 byte after the name
+ */
+ pwhead = isodir->name + isonum_711(isodir->name_len);
+ if (!(isonum_711(isodir->name_len)&1))
+ pwhead++;
+
+ /* If it's not the '.' entry of the root dir obey SP field */
+ if (*isodir->name != 0
+ || isonum_733(isodir->extent) != ana->imp->root_extent)
+ pwhead += ana->imp->rr_skip;
+ else
+ pwhead += ana->imp->rr_skip0;
+
+ phead = (ISO_SUSP_HEADER *)pwhead;
+ pend = (ISO_SUSP_HEADER *)((char *)isodir + isonum_711(isodir->length));
+
+ result = 0;
+ while (1) {
+ ana->iso_ce_len = 0;
+ /*
+ * Note: "pend" should be more than one SUSP header
+ */
+ while (pend >= phead + 1) {
+ if (isonum_711(phead->version) == 1) {
+ for (ptable = table; ptable->func; ptable++) {
+ if (*phead->type == *ptable->type
+ && phead->type[1] == ptable->type[1]) {
+ result |= ptable->func(phead,ana);
+ break;
+ }
+ }
+ if (!ana->fields)
+ break;
+ }
+ /*
+ * move to next SUSP
+ * Hopefully this works with newer versions, too
+ */
+ phead = (ISO_SUSP_HEADER *)((char *)phead + isonum_711(phead->length));
+ }
+
+ if ( ana->fields && ana->iso_ce_len ) {
+ if (ana->iso_ce_blk >= ana->imp->volume_space_size
+ || ana->iso_ce_off + ana->iso_ce_len > ana->imp->logical_block_size
+ || bread(ana->imp->im_devvp,
+ ana->iso_ce_blk * ana->imp->logical_block_size / DEV_BSIZE,
+ ana->imp->logical_block_size,NOCRED,&bp))
+ /* what to do now? */
+ break;
+ phead = (ISO_SUSP_HEADER *)(bp->b_un.b_addr + ana->iso_ce_off);
+ pend = (ISO_SUSP_HEADER *) ((char *)phead + ana->iso_ce_len);
+ } else
+ break;
+ }
+ if (bp)
+ brelse(bp);
+ /*
+ * If we don't find the Basic SUSP stuffs, just set default value
+ * ( attribute/time stamp )
+ */
+ for (ptable = table; ptable->func2; ptable++)
+ if (!(ptable->result&result))
+ ptable->func2(isodir,ana);
+
+ return result;
+}
+
+static RRIP_TABLE rrip_table_analyze[] = {
+ { "PX", cd9660_rrip_attr, cd9660_rrip_defattr, ISO_SUSP_ATTR },
+ { "TF", cd9660_rrip_tstamp, cd9660_rrip_deftstamp, ISO_SUSP_TSTAMP },
+ { "PN", cd9660_rrip_device, 0, ISO_SUSP_DEVICE },
+ { "RR", cd9660_rrip_idflag, 0, ISO_SUSP_IDFLAG },
+ { "CE", cd9660_rrip_cont, 0, ISO_SUSP_CONT },
+ { "ST", cd9660_rrip_stop, 0, ISO_SUSP_STOP },
+ { "", 0, 0, 0 }
+};
+
+int
+cd9660_rrip_analyze(isodir,inop,imp)
+ struct iso_directory_record *isodir;
+ struct iso_node *inop;
+ struct iso_mnt *imp;
+{
+ ISO_RRIP_ANALYZE analyze;
+
+ analyze.inop = inop;
+ analyze.imp = imp;
+ analyze.fields = ISO_SUSP_ATTR|ISO_SUSP_TSTAMP|ISO_SUSP_DEVICE;
+
+ return cd9660_rrip_loop(isodir,&analyze,rrip_table_analyze);
+}
+
+/*
+ * Get Alternate Name from 'AL' record
+ * If either no AL record or 0 length,
+ * it will be return the translated ISO9660 name,
+ */
+static RRIP_TABLE rrip_table_getname[] = {
+ { "NM", cd9660_rrip_altname, cd9660_rrip_defname, ISO_SUSP_ALTNAME },
+ { "CL", cd9660_rrip_pclink, 0, ISO_SUSP_CLINK|ISO_SUSP_PLINK },
+ { "PL", cd9660_rrip_pclink, 0, ISO_SUSP_CLINK|ISO_SUSP_PLINK },
+ { "RE", cd9660_rrip_reldir, 0, ISO_SUSP_RELDIR },
+ { "RR", cd9660_rrip_idflag, 0, ISO_SUSP_IDFLAG },
+ { "CE", cd9660_rrip_cont, 0, ISO_SUSP_CONT },
+ { "ST", cd9660_rrip_stop, 0, ISO_SUSP_STOP },
+ { "", 0, 0, 0 }
+};
+
+int
+cd9660_rrip_getname(isodir,outbuf,outlen,inump,imp)
+ struct iso_directory_record *isodir;
+ char *outbuf;
+ u_short *outlen;
+ ino_t *inump;
+ struct iso_mnt *imp;
+{
+ ISO_RRIP_ANALYZE analyze;
+ RRIP_TABLE *tab;
+
+ analyze.outbuf = outbuf;
+ analyze.outlen = outlen;
+ analyze.maxlen = NAME_MAX;
+ analyze.inump = inump;
+ analyze.imp = imp;
+ analyze.fields = ISO_SUSP_ALTNAME|ISO_SUSP_RELDIR|ISO_SUSP_CLINK|ISO_SUSP_PLINK;
+ *outlen = 0;
+
+ tab = rrip_table_getname;
+ if (*isodir->name == 0
+ || *isodir->name == 1) {
+ cd9660_rrip_defname(isodir,&analyze);
+
+ analyze.fields &= ~ISO_SUSP_ALTNAME;
+ tab++;
+ }
+
+ return cd9660_rrip_loop(isodir,&analyze,tab);
+}
+
+/*
+ * Get Symbolic Name from 'SL' record
+ *
+ * Note: isodir should contains SL record!
+ */
+static RRIP_TABLE rrip_table_getsymname[] = {
+ { "SL", cd9660_rrip_slink, 0, ISO_SUSP_SLINK },
+ { "RR", cd9660_rrip_idflag, 0, ISO_SUSP_IDFLAG },
+ { "CE", cd9660_rrip_cont, 0, ISO_SUSP_CONT },
+ { "ST", cd9660_rrip_stop, 0, ISO_SUSP_STOP },
+ { "", 0, 0, 0 }
+};
+
+int
+cd9660_rrip_getsymname(isodir,outbuf,outlen,imp)
+ struct iso_directory_record *isodir;
+ char *outbuf;
+ u_short *outlen;
+ struct iso_mnt *imp;
+{
+ ISO_RRIP_ANALYZE analyze;
+
+ analyze.outbuf = outbuf;
+ analyze.outlen = outlen;
+ *outlen = 0;
+ analyze.maxlen = MAXPATHLEN;
+ analyze.cont = 1; /* don't start with a slash */
+ analyze.imp = imp;
+ analyze.fields = ISO_SUSP_SLINK;
+
+ return (cd9660_rrip_loop(isodir,&analyze,rrip_table_getsymname)&ISO_SUSP_SLINK);
+}
+
+static RRIP_TABLE rrip_table_extref[] = {
+ { "ER", cd9660_rrip_extref, 0, ISO_SUSP_EXTREF },
+ { "CE", cd9660_rrip_cont, 0, ISO_SUSP_CONT },
+ { "ST", cd9660_rrip_stop, 0, ISO_SUSP_STOP },
+ { "", 0, 0, 0 }
+};
+
+/*
+ * Check for Rock Ridge Extension and return offset of its fields.
+ * Note: We require the ER field.
+ */
+int
+cd9660_rrip_offset(isodir,imp)
+ struct iso_directory_record *isodir;
+ struct iso_mnt *imp;
+{
+ ISO_RRIP_OFFSET *p;
+ ISO_RRIP_ANALYZE analyze;
+
+ imp->rr_skip0 = 0;
+ p = (ISO_RRIP_OFFSET *)(isodir->name + 1);
+ if (bcmp(p,"SP\7\1\276\357",6)) {
+ /* Maybe, it's a CDROM XA disc? */
+ imp->rr_skip0 = 15;
+ p = (ISO_RRIP_OFFSET *)((char *)p + 15);
+ if (bcmp(p,"SP\7\1\276\357",6))
+ return -1;
+ }
+
+ analyze.imp = imp;
+ analyze.fields = ISO_SUSP_EXTREF;
+ if (!(cd9660_rrip_loop(isodir,&analyze,rrip_table_extref)&ISO_SUSP_EXTREF))
+ return -1;
+
+ return isonum_711(p->skip);
+}
diff --git a/sys/isofs/cd9660/cd9660_rrip.h b/sys/isofs/cd9660/cd9660_rrip.h
new file mode 100644
index 0000000..b401728
--- /dev/null
+++ b/sys/isofs/cd9660/cd9660_rrip.h
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cd9660_rrip.h 8.1 (Berkeley) 1/21/94
+ */
+
+typedef struct {
+ char type [ISODCL ( 0, 1)];
+ unsigned char length [ISODCL ( 2, 2)]; /* 711 */
+ unsigned char version [ISODCL ( 3, 3)];
+} ISO_SUSP_HEADER;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char mode_l [ISODCL ( 4, 7)]; /* 731 */
+ char mode_m [ISODCL ( 8, 11)]; /* 732 */
+ char links_l [ISODCL ( 12, 15)]; /* 731 */
+ char links_m [ISODCL ( 16, 19)]; /* 732 */
+ char uid_l [ISODCL ( 20, 23)]; /* 731 */
+ char uid_m [ISODCL ( 24, 27)]; /* 732 */
+ char gid_l [ISODCL ( 28, 31)]; /* 731 */
+ char gid_m [ISODCL ( 32, 35)]; /* 732 */
+} ISO_RRIP_ATTR;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char dev_t_high_l [ISODCL ( 4, 7)]; /* 731 */
+ char dev_t_high_m [ISODCL ( 8, 11)]; /* 732 */
+ char dev_t_low_l [ISODCL ( 12, 15)]; /* 731 */
+ char dev_t_low_m [ISODCL ( 16, 19)]; /* 732 */
+} ISO_RRIP_DEVICE;
+
+#define ISO_SUSP_CFLAG_CONTINUE 0x01
+#define ISO_SUSP_CFLAG_CURRENT 0x02
+#define ISO_SUSP_CFLAG_PARENT 0x04
+#define ISO_SUSP_CFLAG_ROOT 0x08
+#define ISO_SUSP_CFLAG_VOLROOT 0x10
+#define ISO_SUSP_CFLAG_HOST 0x20
+
+typedef struct {
+ u_char cflag [ISODCL ( 1, 1)];
+ u_char clen [ISODCL ( 2, 2)];
+ u_char name [0];
+} ISO_RRIP_SLINK_COMPONENT;
+#define ISO_RRIP_SLSIZ 2
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char flags [ISODCL ( 4, 4)];
+ u_char component [ISODCL ( 5, 5)];
+} ISO_RRIP_SLINK;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char flags [ISODCL ( 4, 4)];
+} ISO_RRIP_ALTNAME;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char dir_loc [ISODCL ( 4, 11)]; /* 733 */
+} ISO_RRIP_CLINK;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char dir_loc [ISODCL ( 4, 11)]; /* 733 */
+} ISO_RRIP_PLINK;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+} ISO_RRIP_RELDIR;
+
+#define ISO_SUSP_TSTAMP_FORM17 0x80
+#define ISO_SUSP_TSTAMP_FORM7 0x00
+#define ISO_SUSP_TSTAMP_CREAT 0x01
+#define ISO_SUSP_TSTAMP_MODIFY 0x02
+#define ISO_SUSP_TSTAMP_ACCESS 0x04
+#define ISO_SUSP_TSTAMP_ATTR 0x08
+#define ISO_SUSP_TSTAMP_BACKUP 0x10
+#define ISO_SUSP_TSTAMP_EXPIRE 0x20
+#define ISO_SUSP_TSTAMP_EFFECT 0x40
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ unsigned char flags [ISODCL ( 4, 4)];
+ unsigned char time [ISODCL ( 5, 5)];
+} ISO_RRIP_TSTAMP;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ unsigned char flags [ISODCL ( 4, 4)];
+} ISO_RRIP_IDFLAG;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char len_id [ISODCL ( 4, 4)];
+ char len_des [ISODCL ( 5, 5)];
+ char len_src [ISODCL ( 6, 6)];
+ char version [ISODCL ( 7, 7)];
+} ISO_RRIP_EXTREF;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char check [ISODCL ( 4, 5)];
+ char skip [ISODCL ( 6, 6)];
+} ISO_RRIP_OFFSET;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char location [ISODCL ( 4, 11)];
+ char offset [ISODCL ( 12, 19)];
+ char length [ISODCL ( 20, 27)];
+} ISO_RRIP_CONT;
diff --git a/sys/isofs/cd9660/cd9660_util.c b/sys/isofs/cd9660/cd9660_util.c
new file mode 100644
index 0000000..39c5fe4
--- /dev/null
+++ b/sys/isofs/cd9660/cd9660_util.c
@@ -0,0 +1,236 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cd9660_util.c 8.1 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/resourcevar.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <miscfs/specfs/specdev.h> /* XXX */
+#include <miscfs/fifofs/fifo.h> /* XXX */
+#include <sys/malloc.h>
+#include <sys/dir.h>
+
+#include <isofs/cd9660/iso.h>
+
+#ifdef __notanymore__
+int
+isonum_711 (p)
+unsigned char *p;
+{
+ return (*p);
+}
+
+int
+isonum_712 (p)
+signed char *p;
+{
+ return (*p);
+}
+
+int
+isonum_721 (p)
+unsigned char *p;
+{
+ /* little endian short */
+#if BYTE_ORDER != LITTLE_ENDIAN
+ printf ("isonum_721 called on non little-endian machine!\n");
+#endif
+
+ return *(short *)p;
+}
+
+int
+isonum_722 (p)
+unsigned char *p;
+{
+ /* big endian short */
+#if BYTE_ORDER != BIG_ENDIAN
+ printf ("isonum_722 called on non big-endian machine!\n");
+#endif
+
+ return *(short *)p;
+}
+
+int
+isonum_723 (p)
+unsigned char *p;
+{
+#if BYTE_ORDER == BIG_ENDIAN
+ return isonum_722 (p + 2);
+#elif BYTE_ORDER == LITTLE_ENDIAN
+ return isonum_721 (p);
+#else
+ printf ("isonum_723 unsupported byte order!\n");
+ return 0;
+#endif
+}
+
+int
+isonum_731 (p)
+unsigned char *p;
+{
+ /* little endian long */
+#if BYTE_ORDER != LITTLE_ENDIAN
+ printf ("isonum_731 called on non little-endian machine!\n");
+#endif
+
+ return *(long *)p;
+}
+
+int
+isonum_732 (p)
+unsigned char *p;
+{
+ /* big endian long */
+#if BYTE_ORDER != BIG_ENDIAN
+ printf ("isonum_732 called on non big-endian machine!\n");
+#endif
+
+ return *(long *)p;
+}
+
+int
+isonum_733 (p)
+unsigned char *p;
+{
+#if BYTE_ORDER == BIG_ENDIAN
+ return isonum_732 (p + 4);
+#elif BYTE_ORDER == LITTLE_ENDIAN
+ return isonum_731 (p);
+#else
+ printf ("isonum_733 unsupported byte order!\n");
+ return 0;
+#endif
+}
+#endif /* __notanymore__ */
+
+/*
+ * translate and compare a filename
+ * Note: Version number plus ';' may be omitted.
+ */
+int
+isofncmp(unsigned char *fn,int fnlen,unsigned char *isofn,int isolen)
+{
+ int i, j;
+ unsigned char c;
+
+ while (--fnlen >= 0) {
+ if (--isolen < 0)
+ return *fn;
+ if ((c = *isofn++) == ';') {
+ switch (*fn++) {
+ default:
+ return *--fn;
+ case 0:
+ return 0;
+ case ';':
+ break;
+ }
+ for (i = 0; --fnlen >= 0; i = i * 10 + *fn++ - '0') {
+ if (*fn < '0' || *fn > '9') {
+ return -1;
+ }
+ }
+ for (j = 0; --isolen >= 0; j = j * 10 + *isofn++ - '0');
+ return i - j;
+ }
+ if (c != *fn) {
+ if (c >= 'A' && c <= 'Z') {
+ if (c + ('a' - 'A') != *fn) {
+ if (*fn >= 'a' && *fn <= 'z')
+ return *fn - ('a' - 'A') - c;
+ else
+ return *fn - c;
+ }
+ } else
+ return *fn - c;
+ }
+ fn++;
+ }
+ if (isolen > 0) {
+ switch (*isofn) {
+ default:
+ return -1;
+ case '.':
+ if (isofn[1] != ';')
+ return -1;
+ case ';':
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/*
+ * translate a filename
+ */
+void
+isofntrans(unsigned char *infn,int infnlen,
+ unsigned char *outfn,unsigned short *outfnlen,
+ int original,int assoc)
+{
+ int fnidx = 0;
+
+ if (assoc) {
+ *outfn++ = ASSOCCHAR;
+ fnidx++;
+ }
+ for (; fnidx < infnlen; fnidx++) {
+ char c = *infn++;
+
+ if (!original && c >= 'A' && c <= 'Z')
+ *outfn++ = c + ('a' - 'A');
+ else if (!original && c == '.' && *infn == ';')
+ break;
+ else if (!original && c == ';')
+ break;
+ else
+ *outfn++ = c;
+ }
+ *outfnlen = fnidx;
+}
diff --git a/sys/isofs/cd9660/cd9660_vfsops.c b/sys/isofs/cd9660/cd9660_vfsops.c
new file mode 100644
index 0000000..bc48367
--- /dev/null
+++ b/sys/isofs/cd9660/cd9660_vfsops.c
@@ -0,0 +1,688 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cd9660_vfsops.c 8.3 (Berkeley) 1/31/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/specfs/specdev.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/dkbad.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+
+#include <isofs/cd9660/iso.h>
+#include <isofs/cd9660/cd9660_node.h>
+
+extern int enodev ();
+
+struct vfsops cd9660_vfsops = {
+ cd9660_mount,
+ cd9660_start,
+ cd9660_unmount,
+ cd9660_root,
+ cd9660_quotactl,
+ cd9660_statfs,
+ cd9660_sync,
+ cd9660_vget,
+ cd9660_fhtovp,
+ cd9660_vptofh,
+ cd9660_init,
+};
+
+/*
+ * Called by vfs_mountroot when iso is going to be mounted as root.
+ *
+ * Name is updated by mount(8) after booting.
+ */
+#define ROOTNAME "root_device"
+
+static iso_mountfs();
+
+int
+cd9660_mountroot()
+{
+ register struct mount *mp;
+ extern struct vnode *rootvp;
+ struct proc *p = curproc; /* XXX */
+ struct iso_mnt *imp;
+ register struct fs *fs;
+ u_int size;
+ int error;
+ struct iso_args args;
+
+ /*
+ * Get vnodes for swapdev and rootdev.
+ */
+ if (bdevvp(swapdev, &swapdev_vp) || bdevvp(rootdev, &rootvp))
+ panic("cd9660_mountroot: can't setup bdevvp's");
+
+ mp = malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK);
+ bzero((char *)mp, (u_long)sizeof(struct mount));
+ mp->mnt_op = &cd9660_vfsops;
+ mp->mnt_flag = MNT_RDONLY;
+ args.flags = ISOFSMNT_ROOT;
+ if (error = iso_mountfs(rootvp, mp, p, &args)) {
+ free(mp, M_MOUNT);
+ return (error);
+ }
+ if (error = vfs_lock(mp)) {
+ (void)cd9660_unmount(mp, 0, p);
+ free(mp, M_MOUNT);
+ return (error);
+ }
+ TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
+ mp->mnt_flag |= MNT_ROOTFS;
+ mp->mnt_vnodecovered = NULLVP;
+ imp = VFSTOISOFS(mp);
+ bzero(imp->im_fsmnt, sizeof(imp->im_fsmnt));
+ imp->im_fsmnt[0] = '/';
+ bcopy((caddr_t)imp->im_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname,
+ MNAMELEN);
+ (void) copystr(ROOTNAME, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ (void) cd9660_statfs(mp, &mp->mnt_stat, p);
+ vfs_unlock(mp);
+ return (0);
+}
+
+/*
+ * Flag to allow forcible unmounting.
+ */
+int iso_doforce = 1;
+
+/*
+ * VFS Operations.
+ *
+ * mount system call
+ */
+int
+cd9660_mount(mp, path, data, ndp, p)
+ register struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ struct vnode *devvp;
+ struct iso_args args;
+ u_int size;
+ int error;
+ struct iso_mnt *imp = 0;
+
+ if (error = copyin(data, (caddr_t)&args, sizeof (struct iso_args)))
+ return (error);
+
+ if ((mp->mnt_flag & MNT_RDONLY) == 0)
+ return (EROFS);
+
+ /*
+ * If updating, check whether changing from read-only to
+ * read/write; if there is no device name, that's all we do.
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ imp = VFSTOISOFS(mp);
+ if (args.fspec == 0)
+ return (vfs_export(mp, &imp->im_export, &args.export));
+ }
+ /*
+ * Not an update, or updating the name: look up the name
+ * and verify that it refers to a sensible block device.
+ */
+ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
+ if (error = namei(ndp))
+ return (error);
+ devvp = ndp->ni_vp;
+
+ if (devvp->v_type != VBLK) {
+ vrele(devvp);
+ return ENOTBLK;
+ }
+ if (major(devvp->v_rdev) >= nblkdev) {
+ vrele(devvp);
+ return ENXIO;
+ }
+ if ((mp->mnt_flag & MNT_UPDATE) == 0)
+ error = iso_mountfs(devvp, mp, p, &args);
+ else {
+ if (devvp != imp->im_devvp)
+ error = EINVAL; /* needs translation */
+ else
+ vrele(devvp);
+ }
+ if (error) {
+ vrele(devvp);
+ return error;
+ }
+ imp = VFSTOISOFS(mp);
+ (void) copyinstr(path, imp->im_fsmnt, sizeof(imp->im_fsmnt)-1, &size);
+ bzero(imp->im_fsmnt + size, sizeof(imp->im_fsmnt) - size);
+ bcopy((caddr_t)imp->im_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname,
+ MNAMELEN);
+ (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ (void) cd9660_statfs(mp, &mp->mnt_stat, p);
+ return 0;
+}
+
+/*
+ * Common code for mount and mountroot
+ */
+static int
+iso_mountfs(devvp, mp, p, argp)
+ register struct vnode *devvp;
+ struct mount *mp;
+ struct proc *p;
+ struct iso_args *argp;
+{
+ register struct iso_mnt *isomp = (struct iso_mnt *)0;
+ struct buf *bp = NULL;
+ dev_t dev = devvp->v_rdev;
+ caddr_t base, space;
+ int havepart = 0, blks;
+ int error = EINVAL, i, size;
+ int needclose = 0;
+ int ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
+ extern struct vnode *rootvp;
+ int j;
+ int iso_bsize;
+ int iso_blknum;
+ struct iso_volume_descriptor *vdp;
+ struct iso_primary_descriptor *pri;
+ struct iso_directory_record *rootp;
+ int logical_block_size;
+
+ if (!ronly)
+ return EROFS;
+
+ /*
+ * Disallow multiple mounts of the same device.
+ * Disallow mounting of a device that is currently in use
+ * (except for root, which might share swap device for miniroot).
+ * Flush out any old buffers remaining from a previous use.
+ */
+ if (error = vfs_mountedon(devvp))
+ return error;
+ if (vcount(devvp) > 1 && devvp != rootvp)
+ return EBUSY;
+ if (error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0))
+ return (error);
+
+ if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p))
+ return error;
+ needclose = 1;
+
+ /* This is the "logical sector size". The standard says this
+ * should be 2048 or the physical sector size on the device,
+ * whichever is greater. For now, we'll just use a constant.
+ */
+ iso_bsize = ISO_DEFAULT_BLOCK_SIZE;
+
+ for (iso_blknum = 16; iso_blknum < 100; iso_blknum++) {
+ if (error = bread (devvp, btodb(iso_blknum * iso_bsize),
+ iso_bsize, NOCRED, &bp))
+ goto out;
+
+ vdp = (struct iso_volume_descriptor *)bp->b_un.b_addr;
+ if (bcmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) != 0) {
+ error = EINVAL;
+ goto out;
+ }
+
+ if (isonum_711 (vdp->type) == ISO_VD_END) {
+ error = EINVAL;
+ goto out;
+ }
+
+ if (isonum_711 (vdp->type) == ISO_VD_PRIMARY)
+ break;
+ brelse(bp);
+ }
+
+ if (isonum_711 (vdp->type) != ISO_VD_PRIMARY) {
+ error = EINVAL;
+ goto out;
+ }
+
+ pri = (struct iso_primary_descriptor *)vdp;
+
+ logical_block_size = isonum_723 (pri->logical_block_size);
+
+ if (logical_block_size < DEV_BSIZE || logical_block_size > MAXBSIZE
+ || (logical_block_size & (logical_block_size - 1)) != 0) {
+ error = EINVAL;
+ goto out;
+ }
+
+ rootp = (struct iso_directory_record *)pri->root_directory_record;
+
+ isomp = malloc(sizeof *isomp, M_ISOFSMNT, M_WAITOK);
+ bzero((caddr_t)isomp, sizeof *isomp);
+ isomp->logical_block_size = logical_block_size;
+ isomp->volume_space_size = isonum_733 (pri->volume_space_size);
+ bcopy (rootp, isomp->root, sizeof isomp->root);
+ isomp->root_extent = isonum_733 (rootp->extent);
+ isomp->root_size = isonum_733 (rootp->size);
+
+ isomp->im_bmask = logical_block_size - 1;
+ isomp->im_bshift = 0;
+ while ((1 << isomp->im_bshift) < isomp->logical_block_size)
+ isomp->im_bshift++;
+
+ bp->b_flags |= B_AGE;
+ brelse(bp);
+ bp = NULL;
+
+ mp->mnt_data = (qaddr_t)isomp;
+ mp->mnt_stat.f_fsid.val[0] = (long)dev;
+ mp->mnt_stat.f_fsid.val[1] = MOUNT_CD9660;
+ mp->mnt_maxsymlinklen = 0;
+ mp->mnt_flag |= MNT_LOCAL;
+ isomp->im_mountp = mp;
+ isomp->im_dev = dev;
+ isomp->im_devvp = devvp;
+
+ devvp->v_specflags |= SI_MOUNTEDON;
+
+ /* Check the Rock Ridge Extention support */
+ if (!(argp->flags & ISOFSMNT_NORRIP)) {
+ if (error = bread (isomp->im_devvp,
+ (isomp->root_extent + isonum_711(rootp->ext_attr_length))
+ * isomp->logical_block_size / DEV_BSIZE,
+ isomp->logical_block_size,NOCRED,&bp))
+ goto out;
+
+ rootp = (struct iso_directory_record *)bp->b_un.b_addr;
+
+ if ((isomp->rr_skip = cd9660_rrip_offset(rootp,isomp)) < 0) {
+ argp->flags |= ISOFSMNT_NORRIP;
+ } else {
+ argp->flags &= ~ISOFSMNT_GENS;
+ }
+
+ /*
+ * The contents are valid,
+ * but they will get reread as part of another vnode, so...
+ */
+ bp->b_flags |= B_AGE;
+ brelse(bp);
+ bp = NULL;
+ }
+ isomp->im_flags = argp->flags&(ISOFSMNT_NORRIP|ISOFSMNT_GENS|ISOFSMNT_EXTATT);
+ switch (isomp->im_flags&(ISOFSMNT_NORRIP|ISOFSMNT_GENS)) {
+ default:
+ isomp->iso_ftype = ISO_FTYPE_DEFAULT;
+ break;
+ case ISOFSMNT_GENS|ISOFSMNT_NORRIP:
+ isomp->iso_ftype = ISO_FTYPE_9660;
+ break;
+ case 0:
+ isomp->iso_ftype = ISO_FTYPE_RRIP;
+ break;
+ }
+
+ return 0;
+out:
+ if (bp)
+ brelse(bp);
+ if (needclose)
+ (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p);
+ if (isomp) {
+ free((caddr_t)isomp, M_ISOFSMNT);
+ mp->mnt_data = (qaddr_t)0;
+ }
+ return error;
+}
+
+/*
+ * Make a filesystem operational.
+ * Nothing to do at the moment.
+ */
+/* ARGSUSED */
+int
+cd9660_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ return 0;
+}
+
+/*
+ * unmount system call
+ */
+int
+cd9660_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ register struct iso_mnt *isomp;
+ int i, error, ronly, flags = 0;
+
+ if (mntflags & MNT_FORCE) {
+ if (!iso_doforce || (mp->mnt_flag & MNT_ROOTFS))
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+#if 0
+ mntflushbuf(mp, 0);
+ if (mntinvalbuf(mp))
+ return EBUSY;
+#endif
+ if (error = vflush(mp, NULLVP, flags))
+ return (error);
+
+ isomp = VFSTOISOFS(mp);
+
+#ifdef ISODEVMAP
+ if (isomp->iso_ftype == ISO_FTYPE_RRIP)
+ iso_dunmap(isomp->im_dev);
+#endif
+
+ isomp->im_devvp->v_specflags &= ~SI_MOUNTEDON;
+ error = VOP_CLOSE(isomp->im_devvp, FREAD, NOCRED, p);
+ vrele(isomp->im_devvp);
+ free((caddr_t)isomp, M_ISOFSMNT);
+ mp->mnt_data = (qaddr_t)0;
+ mp->mnt_flag &= ~MNT_LOCAL;
+ return (error);
+}
+
+/*
+ * Return root of a filesystem
+ */
+int
+cd9660_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ register struct iso_node *ip;
+ struct iso_node tip, *nip;
+ struct vnode tvp;
+ int error;
+ struct iso_mnt *imp = VFSTOISOFS (mp);
+ struct iso_directory_record *dp;
+
+ tvp.v_mount = mp;
+ tvp.v_data = &tip;
+ ip = VTOI(&tvp);
+ ip->i_vnode = &tvp;
+ ip->i_dev = imp->im_dev;
+ ip->i_diroff = 0;
+ dp = (struct iso_directory_record *)imp->root;
+ isodirino(&ip->i_number,dp,imp);
+
+ /*
+ * With RRIP we must use the `.' entry of the root directory.
+ * Simply tell iget, that it's a relocated directory.
+ */
+ error = iso_iget(ip,ip->i_number,
+ imp->iso_ftype == ISO_FTYPE_RRIP,
+ &nip,dp);
+ if (error)
+ return error;
+ *vpp = ITOV(nip);
+ return 0;
+}
+
+/*
+ * Do operations associated with quotas, not supported
+ */
+/* ARGSUSED */
+int
+cd9660_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * Get file system statistics.
+ */
+int
+cd9660_statfs(mp, sbp, p)
+ struct mount *mp;
+ register struct statfs *sbp;
+ struct proc *p;
+{
+ register struct iso_mnt *isomp;
+ register struct fs *fs;
+
+ isomp = VFSTOISOFS(mp);
+
+ sbp->f_type = MOUNT_CD9660;
+ sbp->f_bsize = isomp->logical_block_size;
+ sbp->f_iosize = sbp->f_bsize; /* XXX */
+ sbp->f_blocks = isomp->volume_space_size;
+ sbp->f_bfree = 0; /* total free blocks */
+ sbp->f_bavail = 0; /* blocks free for non superuser */
+ sbp->f_files = 0; /* total files */
+ sbp->f_ffree = 0; /* free file nodes */
+ if (sbp != &mp->mnt_stat) {
+ bcopy((caddr_t)mp->mnt_stat.f_mntonname,
+ (caddr_t)&sbp->f_mntonname[0], MNAMELEN);
+ bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
+ (caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
+ }
+ /* Use the first spare for flags: */
+ sbp->f_spare[0] = isomp->im_flags;
+ return 0;
+}
+
+/* ARGSUSED */
+int
+cd9660_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+ return (0);
+}
+
+/*
+ * Flat namespace lookup.
+ * Currently unsupported.
+ */
+/* ARGSUSED */
+int
+cd9660_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * File handle to vnode
+ *
+ * Have to be really careful about stale file handles:
+ * - check that the inode number is in range
+ * - call iget() to get the locked inode
+ * - check for an unallocated inode (i_mode == 0)
+ * - check that the generation number matches
+ */
+
+struct ifid {
+ ushort ifid_len;
+ ushort ifid_pad;
+ int ifid_ino;
+ long ifid_start;
+};
+
+/* ARGSUSED */
+int
+cd9660_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
+ register struct mount *mp;
+ struct fid *fhp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred **credanonp;
+{
+ struct vnode tvp;
+ int error;
+ int lbn, off;
+ struct ifid *ifhp;
+ struct iso_mnt *imp;
+ struct buf *bp;
+ struct iso_directory_record *dirp;
+ struct iso_node tip, *ip, *nip;
+ struct netcred *np;
+
+ imp = VFSTOISOFS (mp);
+ ifhp = (struct ifid *)fhp;
+
+#ifdef ISOFS_DBG
+ printf("fhtovp: ino %d, start %ld\n",
+ ifhp->ifid_ino, ifhp->ifid_start);
+#endif
+
+ np = vfs_export_lookup(mp, &imp->im_export, nam);
+ if (np == NULL)
+ return (EACCES);
+
+ lbn = iso_lblkno(imp, ifhp->ifid_ino);
+ if (lbn >= imp->volume_space_size) {
+ printf("fhtovp: lbn exceed volume space %d\n", lbn);
+ return (ESTALE);
+ }
+
+ off = iso_blkoff(imp, ifhp->ifid_ino);
+ if (off + ISO_DIRECTORY_RECORD_SIZE > imp->logical_block_size) {
+ printf("fhtovp: crosses block boundary %d\n",
+ off + ISO_DIRECTORY_RECORD_SIZE);
+ return (ESTALE);
+ }
+
+ error = bread(imp->im_devvp, btodb(lbn * imp->logical_block_size),
+ imp->logical_block_size, NOCRED, &bp);
+ if (error) {
+ printf("fhtovp: bread error %d\n",error);
+ brelse(bp);
+ return (error);
+ }
+
+ dirp = (struct iso_directory_record *)(bp->b_un.b_addr + off);
+ if (off + isonum_711(dirp->length) > imp->logical_block_size) {
+ brelse(bp);
+ printf("fhtovp: directory crosses block boundary %d[off=%d/len=%d]\n",
+ off+isonum_711(dirp->length), off,
+ isonum_711(dirp->length));
+ return (ESTALE);
+ }
+
+ if (isonum_733(dirp->extent) + isonum_711(dirp->ext_attr_length) !=
+ ifhp->ifid_start) {
+ brelse(bp);
+ printf("fhtovp: file start miss %d vs %d\n",
+ isonum_733(dirp->extent)+isonum_711(dirp->ext_attr_length),
+ ifhp->ifid_start);
+ return (ESTALE);
+ }
+ brelse(bp);
+
+ ip = &tip;
+ tvp.v_mount = mp;
+ tvp.v_data = ip;
+ ip->i_vnode = &tvp;
+ ip->i_dev = imp->im_dev;
+ if (error = iso_iget(ip, ifhp->ifid_ino, 0, &nip, dirp)) {
+ *vpp = NULLVP;
+ printf("fhtovp: failed to get inode\n");
+ return (error);
+ }
+ ip = nip;
+ /*
+ * XXX need generation number?
+ */
+ if (ip->inode.iso_mode == 0) {
+ iso_iput(ip);
+ *vpp = NULLVP;
+ printf("fhtovp: inode mode == 0\n");
+ return (ESTALE);
+ }
+ *vpp = ITOV(ip);
+ *exflagsp = np->netc_exflags;
+ *credanonp = &np->netc_anon;
+ return 0;
+}
+
+/*
+ * Vnode pointer to File handle
+ */
+/* ARGSUSED */
+int
+cd9660_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+ register struct iso_node *ip = VTOI(vp);
+ register struct ifid *ifhp;
+ register struct iso_mnt *mp = ip->i_mnt;
+
+ ifhp = (struct ifid *)fhp;
+ ifhp->ifid_len = sizeof(struct ifid);
+
+ ifhp->ifid_ino = ip->i_number;
+ ifhp->ifid_start = ip->iso_start;
+
+#ifdef ISOFS_DBG
+ printf("vptofh: ino %d, start %ld\n",
+ ifhp->ifid_ino,ifhp->ifid_start);
+#endif
+ return 0;
+}
diff --git a/sys/isofs/cd9660/cd9660_vnops.c b/sys/isofs/cd9660/cd9660_vnops.c
new file mode 100644
index 0000000..7a2964b
--- /dev/null
+++ b/sys/isofs/cd9660/cd9660_vnops.c
@@ -0,0 +1,1041 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cd9660_vnops.c 8.3 (Berkeley) 1/23/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/resourcevar.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <miscfs/specfs/specdev.h>
+#include <miscfs/fifofs/fifo.h>
+#include <sys/malloc.h>
+#include <sys/dir.h>
+
+#include <isofs/cd9660/iso.h>
+#include <isofs/cd9660/cd9660_node.h>
+#include <isofs/cd9660/iso_rrip.h>
+
+#if 0
+/*
+ * Mknod vnode call
+ * Actually remap the device number
+ */
+cd9660_mknod(ndp, vap, cred, p)
+ struct nameidata *ndp;
+ struct ucred *cred;
+ struct vattr *vap;
+ struct proc *p;
+{
+#ifndef ISODEVMAP
+ free(ndp->ni_pnbuf, M_NAMEI);
+ vput(ndp->ni_dvp);
+ vput(ndp->ni_vp);
+ return EINVAL;
+#else
+ register struct vnode *vp;
+ struct iso_node *ip;
+ struct iso_dnode *dp;
+ int error;
+
+ vp = ndp->ni_vp;
+ ip = VTOI(vp);
+
+ if (ip->i_mnt->iso_ftype != ISO_FTYPE_RRIP
+ || vap->va_type != vp->v_type
+ || (vap->va_type != VCHR && vap->va_type != VBLK)) {
+ free(ndp->ni_pnbuf, M_NAMEI);
+ vput(ndp->ni_dvp);
+ vput(ndp->ni_vp);
+ return EINVAL;
+ }
+
+ dp = iso_dmap(ip->i_dev,ip->i_number,1);
+ if (ip->inode.iso_rdev == vap->va_rdev || vap->va_rdev == VNOVAL) {
+ /* same as the unmapped one, delete the mapping */
+ remque(dp);
+ FREE(dp,M_CACHE);
+ } else
+ /* enter new mapping */
+ dp->d_dev = vap->va_rdev;
+
+ /*
+ * Remove inode so that it will be reloaded by iget and
+ * checked to see if it is an alias of an existing entry
+ * in the inode cache.
+ */
+ vput(vp);
+ vp->v_type = VNON;
+ vgone(vp);
+ return (0);
+#endif
+}
+#endif
+
+/*
+ * Open called.
+ *
+ * Nothing to do.
+ */
+/* ARGSUSED */
+int
+cd9660_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ return (0);
+}
+
+/*
+ * Close called
+ *
+ * Update the times on the inode on writeable file systems.
+ */
+/* ARGSUSED */
+int
+cd9660_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ return (0);
+}
+
+/*
+ * Check mode permission on inode pointer. Mode is READ, WRITE or EXEC.
+ * The mode is shifted to select the owner/group/other fields. The
+ * super user is granted all permissions.
+ */
+/* ARGSUSED */
+int
+cd9660_access(ap)
+ struct vop_access_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ return (0);
+}
+
+int
+cd9660_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+
+{
+ struct vnode *vp = ap->a_vp;
+ register struct vattr *vap = ap->a_vap;
+ register struct iso_node *ip = VTOI(vp);
+ int i;
+
+ vap->va_fsid = ip->i_dev;
+ vap->va_fileid = ip->i_number;
+
+ vap->va_mode = ip->inode.iso_mode;
+ vap->va_nlink = ip->inode.iso_links;
+ vap->va_uid = ip->inode.iso_uid;
+ vap->va_gid = ip->inode.iso_gid;
+ vap->va_atime = ip->inode.iso_atime;
+ vap->va_mtime = ip->inode.iso_mtime;
+ vap->va_ctime = ip->inode.iso_ctime;
+ vap->va_rdev = ip->inode.iso_rdev;
+
+ vap->va_size = (u_quad_t) ip->i_size;
+ vap->va_flags = 0;
+ vap->va_gen = 1;
+ vap->va_blocksize = ip->i_mnt->logical_block_size;
+ vap->va_bytes = (u_quad_t) ip->i_size;
+ vap->va_type = vp->v_type;
+ return (0);
+}
+
+#if ISO_DEFAULT_BLOCK_SIZE >= NBPG
+#ifdef DEBUG
+extern int doclusterread;
+#else
+#define doclusterread 1
+#endif
+#else
+/* XXX until cluster routines can handle block sizes less than one page */
+#define doclusterread 0
+#endif
+
+/*
+ * Vnode op for reading.
+ */
+int
+cd9660_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ register struct uio *uio = ap->a_uio;
+ register struct iso_node *ip = VTOI(vp);
+ register struct iso_mnt *imp;
+ struct buf *bp;
+ daddr_t lbn, bn, rablock;
+ off_t diff;
+ int rasize, error = 0;
+ long size, n, on;
+
+ if (uio->uio_resid == 0)
+ return (0);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+ ip->i_flag |= IACC;
+ imp = ip->i_mnt;
+ do {
+ lbn = iso_lblkno(imp, uio->uio_offset);
+ on = iso_blkoff(imp, uio->uio_offset);
+ n = min((unsigned)(imp->logical_block_size - on),
+ uio->uio_resid);
+ diff = (off_t)ip->i_size - uio->uio_offset;
+ if (diff <= 0)
+ return (0);
+ if (diff < n)
+ n = diff;
+ size = iso_blksize(imp, ip, lbn);
+ rablock = lbn + 1;
+ if (doclusterread) {
+ if (iso_lblktosize(imp, rablock) <= ip->i_size)
+ error = cluster_read(vp, (off_t)ip->i_size,
+ lbn, size, NOCRED, &bp);
+ else
+ error = bread(vp, lbn, size, NOCRED, &bp);
+ } else {
+ if (vp->v_lastr + 1 == lbn &&
+ iso_lblktosize(imp, rablock) < ip->i_size) {
+ rasize = iso_blksize(imp, ip, rablock);
+ error = breadn(vp, lbn, size, &rablock,
+ &rasize, 1, NOCRED, &bp);
+ } else
+ error = bread(vp, lbn, size, NOCRED, &bp);
+ }
+ vp->v_lastr = lbn;
+ n = min(n, size - bp->b_resid);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+
+ error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
+ if (n + on == imp->logical_block_size ||
+ uio->uio_offset == (off_t)ip->i_size)
+ bp->b_flags |= B_AGE;
+ brelse(bp);
+ } while (error == 0 && uio->uio_resid > 0 && n != 0);
+ return (error);
+}
+
+/* ARGSUSED */
+int
+cd9660_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ printf("You did ioctl for isofs !!\n");
+ return (ENOTTY);
+}
+
+/* ARGSUSED */
+int
+cd9660_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ /*
+ * We should really check to see if I/O is possible.
+ */
+ return (1);
+}
+
+/*
+ * Mmap a file
+ *
+ * NB Currently unsupported.
+ */
+/* ARGSUSED */
+int
+cd9660_mmap(ap)
+ struct vop_mmap_args /* {
+ struct vnode *a_vp;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (EINVAL);
+}
+
+/*
+ * Seek on a file
+ *
+ * Nothing to do, so just return.
+ */
+/* ARGSUSED */
+int
+cd9660_seek(ap)
+ struct vop_seek_args /* {
+ struct vnode *a_vp;
+ off_t a_oldoff;
+ off_t a_newoff;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * Structure for reading directories
+ */
+struct isoreaddir {
+ struct dirent saveent;
+ struct dirent assocent;
+ struct dirent current;
+ off_t saveoff;
+ off_t assocoff;
+ off_t curroff;
+ struct uio *uio;
+ off_t uio_off;
+ u_int *cookiep;
+ int ncookies;
+ int eof;
+};
+
+static int
+iso_uiodir(idp,dp,off)
+ struct isoreaddir *idp;
+ struct dirent *dp;
+ off_t off;
+{
+ int error;
+
+ dp->d_name[dp->d_namlen] = 0;
+ dp->d_reclen = DIRSIZ(dp);
+
+ if (idp->uio->uio_resid < dp->d_reclen) {
+ idp->eof = 0;
+ return -1;
+ }
+
+ if (idp->cookiep) {
+ if (idp->ncookies <= 0) {
+ idp->eof = 0;
+ return -1;
+ }
+
+ *idp->cookiep++ = off;
+ --idp->ncookies;
+ }
+
+ if (error = uiomove(dp,dp->d_reclen,idp->uio))
+ return error;
+ idp->uio_off = off;
+ return 0;
+}
+
+static int
+iso_shipdir(idp)
+ struct isoreaddir *idp;
+{
+ struct dirent *dp;
+ int cl, sl, assoc;
+ int error;
+ char *cname, *sname;
+
+ cl = idp->current.d_namlen;
+ cname = idp->current.d_name;
+ if (assoc = cl > 1 && *cname == ASSOCCHAR) {
+ cl--;
+ cname++;
+ }
+
+ dp = &idp->saveent;
+ sname = dp->d_name;
+ if (!(sl = dp->d_namlen)) {
+ dp = &idp->assocent;
+ sname = dp->d_name + 1;
+ sl = dp->d_namlen - 1;
+ }
+ if (sl > 0) {
+ if (sl != cl
+ || bcmp(sname,cname,sl)) {
+ if (idp->assocent.d_namlen) {
+ if (error = iso_uiodir(idp,&idp->assocent,idp->assocoff))
+ return error;
+ idp->assocent.d_namlen = 0;
+ }
+ if (idp->saveent.d_namlen) {
+ if (error = iso_uiodir(idp,&idp->saveent,idp->saveoff))
+ return error;
+ idp->saveent.d_namlen = 0;
+ }
+ }
+ }
+ idp->current.d_reclen = DIRSIZ(&idp->current);
+ if (assoc) {
+ idp->assocoff = idp->curroff;
+ bcopy(&idp->current,&idp->assocent,idp->current.d_reclen);
+ } else {
+ idp->saveoff = idp->curroff;
+ bcopy(&idp->current,&idp->saveent,idp->current.d_reclen);
+ }
+ return 0;
+}
+
+/*
+ * Vnode op for readdir
+ * XXX make sure everything still works now that eofflagp and cookiep
+ * are no longer args.
+ */
+int
+cd9660_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct uio *uio = ap->a_uio;
+ struct isoreaddir *idp;
+ int entryoffsetinblock;
+ int error = 0;
+ int endsearch;
+ struct iso_directory_record *ep;
+ u_short elen;
+ int reclen;
+ struct iso_mnt *imp;
+ struct iso_node *ip;
+ struct buf *bp = NULL;
+
+ ip = VTOI(ap->a_vp);
+ imp = ip->i_mnt;
+
+ MALLOC(idp,struct isoreaddir *,sizeof(*idp),M_TEMP,M_WAITOK);
+ idp->saveent.d_namlen = 0;
+ idp->assocent.d_namlen = 0;
+ idp->uio = uio;
+#if 0
+ idp->cookiep = cookies;
+ idp->ncookies = ncookies;
+ idp->eof = 1;
+#else
+ idp->cookiep = 0;
+#endif
+ idp->curroff = uio->uio_offset;
+
+ entryoffsetinblock = iso_blkoff(imp, idp->curroff);
+ if (entryoffsetinblock != 0) {
+ if (error = iso_blkatoff(ip, idp->curroff, &bp)) {
+ FREE(idp,M_TEMP);
+ return (error);
+ }
+ }
+
+ endsearch = ip->i_size;
+
+ while (idp->curroff < endsearch) {
+ /*
+ * If offset is on a block boundary,
+ * read the next directory block.
+ * Release previous if it exists.
+ */
+
+ if (iso_blkoff(imp, idp->curroff) == 0) {
+ if (bp != NULL)
+ brelse(bp);
+ if (error = iso_blkatoff(ip, idp->curroff, &bp))
+ break;
+ entryoffsetinblock = 0;
+ }
+ /*
+ * Get pointer to next entry.
+ */
+
+ ep = (struct iso_directory_record *)
+ (bp->b_un.b_addr + entryoffsetinblock);
+
+ reclen = isonum_711 (ep->length);
+ if (reclen == 0) {
+ /* skip to next block, if any */
+ idp->curroff = roundup (idp->curroff,
+ imp->logical_block_size);
+ continue;
+ }
+
+ if (reclen < ISO_DIRECTORY_RECORD_SIZE) {
+ error = EINVAL;
+ /* illegal entry, stop */
+ break;
+ }
+
+ if (entryoffsetinblock + reclen > imp->logical_block_size) {
+ error = EINVAL;
+ /* illegal directory, so stop looking */
+ break;
+ }
+
+ idp->current.d_namlen = isonum_711 (ep->name_len);
+ if (isonum_711(ep->flags)&2)
+ isodirino(&idp->current.d_fileno,ep,imp);
+ else
+ idp->current.d_fileno = dbtob(bp->b_blkno) +
+ idp->curroff;
+
+ if (reclen < ISO_DIRECTORY_RECORD_SIZE + idp->current.d_namlen) {
+ error = EINVAL;
+ /* illegal entry, stop */
+ break;
+ }
+
+ idp->curroff += reclen;
+ /*
+ *
+ */
+ switch (imp->iso_ftype) {
+ case ISO_FTYPE_RRIP:
+ cd9660_rrip_getname(ep,idp->current.d_name,
+ (u_short *)&idp->current.d_namlen,
+ &idp->current.d_fileno,imp);
+ if (idp->current.d_namlen)
+ error = iso_uiodir(idp,&idp->current,idp->curroff);
+ break;
+ default: /* ISO_FTYPE_DEFAULT || ISO_FTYPE_9660 */
+ strcpy(idp->current.d_name,"..");
+ switch (ep->name[0]) {
+ case 0:
+ idp->current.d_namlen = 1;
+ error = iso_uiodir(idp,&idp->current,idp->curroff);
+ break;
+ case 1:
+ idp->current.d_namlen = 2;
+ error = iso_uiodir(idp,&idp->current,idp->curroff);
+ break;
+ default:
+ isofntrans(ep->name,idp->current.d_namlen,
+ idp->current.d_name, &elen,
+ imp->iso_ftype == ISO_FTYPE_9660,
+ isonum_711(ep->flags)&4);
+ idp->current.d_namlen = (u_char)elen;
+ if (imp->iso_ftype == ISO_FTYPE_DEFAULT)
+ error = iso_shipdir(idp);
+ else
+ error = iso_uiodir(idp,&idp->current,idp->curroff);
+ break;
+ }
+ }
+ if (error)
+ break;
+
+ entryoffsetinblock += reclen;
+ }
+
+ if (!error && imp->iso_ftype == ISO_FTYPE_DEFAULT) {
+ idp->current.d_namlen = 0;
+ error = iso_shipdir(idp);
+ }
+ if (error < 0)
+ error = 0;
+
+ if (bp)
+ brelse (bp);
+
+ uio->uio_offset = idp->uio_off;
+#if 0
+ *eofflagp = idp->eof;
+#endif
+
+ FREE(idp,M_TEMP);
+
+ return (error);
+}
+
+/*
+ * Return target name of a symbolic link
+ * Shouldn't we get the parent vnode and read the data from there?
+ * This could eventually result in deadlocks in cd9660_lookup.
+ * But otherwise the block read here is in the block buffer two times.
+ */
+typedef struct iso_directory_record ISODIR;
+typedef struct iso_node ISONODE;
+typedef struct iso_mnt ISOMNT;
+int
+cd9660_readlink(ap)
+ struct vop_readlink_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ ISONODE *ip;
+ ISODIR *dirp;
+ ISOMNT *imp;
+ struct buf *bp;
+ u_short symlen;
+ int error;
+ char *symname;
+ ino_t ino;
+
+ ip = VTOI(ap->a_vp);
+ imp = ip->i_mnt;
+
+ if (imp->iso_ftype != ISO_FTYPE_RRIP)
+ return EINVAL;
+
+ /*
+ * Get parents directory record block that this inode included.
+ */
+ error = bread(imp->im_devvp,
+ (daddr_t)(ip->i_number / DEV_BSIZE),
+ imp->logical_block_size,
+ NOCRED,
+ &bp);
+ if (error) {
+ brelse(bp);
+ return EINVAL;
+ }
+
+ /*
+ * Setup the directory pointer for this inode
+ */
+ dirp = (ISODIR *)(bp->b_un.b_addr + (ip->i_number & imp->im_bmask));
+#ifdef DEBUG
+ printf("lbn=%d,off=%d,bsize=%d,DEV_BSIZE=%d, dirp= %08x, b_addr=%08x, offset=%08x(%08x)\n",
+ (daddr_t)(ip->i_number >> imp->im_bshift),
+ ip->i_number & imp->im_bmask,
+ imp->logical_block_size,
+ DEV_BSIZE,
+ dirp,
+ bp->b_un.b_addr,
+ ip->i_number,
+ ip->i_number & imp->im_bmask );
+#endif
+
+ /*
+ * Just make sure, we have a right one....
+ * 1: Check not cross boundary on block
+ */
+ if ((ip->i_number & imp->im_bmask) + isonum_711(dirp->length)
+ > imp->logical_block_size) {
+ brelse(bp);
+ return EINVAL;
+ }
+
+ /*
+ * Now get a buffer
+ * Abuse a namei buffer for now.
+ */
+ MALLOC(symname,char *,MAXPATHLEN,M_NAMEI,M_WAITOK);
+
+ /*
+ * Ok, we just gathering a symbolic name in SL record.
+ */
+ if (cd9660_rrip_getsymname(dirp,symname,&symlen,imp) == 0) {
+ FREE(symname,M_NAMEI);
+ brelse(bp);
+ return EINVAL;
+ }
+ /*
+ * Don't forget before you leave from home ;-)
+ */
+ brelse(bp);
+
+ /*
+ * return with the symbolic name to caller's.
+ */
+ error = uiomove(symname,symlen,ap->a_uio);
+
+ FREE(symname,M_NAMEI);
+
+ return error;
+}
+
+/*
+ * Ufs abort op, called after namei() when a CREATE/DELETE isn't actually
+ * done. If a buffer has been saved in anticipation of a CREATE, delete it.
+ */
+int
+cd9660_abortop(ap)
+ struct vop_abortop_args /* {
+ struct vnode *a_dvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
+ FREE(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ return 0;
+}
+
+/*
+ * Lock an inode.
+ */
+int
+cd9660_lock(ap)
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct iso_node *ip = VTOI(ap->a_vp);
+
+ ISO_ILOCK(ip);
+ return 0;
+}
+
+/*
+ * Unlock an inode.
+ */
+int
+cd9660_unlock(ap)
+ struct vop_unlock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct iso_node *ip = VTOI(ap->a_vp);
+
+ if (!(ip->i_flag & ILOCKED))
+ panic("cd9660_unlock NOT LOCKED");
+ ISO_IUNLOCK(ip);
+ return 0;
+}
+
+/*
+ * Check for a locked inode.
+ */
+int
+cd9660_islocked(ap)
+ struct vop_islocked_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ if (VTOI(ap->a_vp)->i_flag & ILOCKED)
+ return 1;
+ return 0;
+}
+
+/*
+ * Calculate the logical to physical mapping if not done already,
+ * then call the device strategy routine.
+ */
+int
+cd9660_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ register struct buf *bp = ap->a_bp;
+ register struct vnode *vp = bp->b_vp;
+ register struct iso_node *ip;
+ int error;
+
+ ip = VTOI(vp);
+ if (vp->v_type == VBLK || vp->v_type == VCHR)
+ panic("cd9660_strategy: spec");
+ if (bp->b_blkno == bp->b_lblkno) {
+ if (error =
+ VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL)) {
+ bp->b_error = error;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return (error);
+ }
+ if ((long)bp->b_blkno == -1)
+ clrbuf(bp);
+ }
+ if ((long)bp->b_blkno == -1) {
+ biodone(bp);
+ return (0);
+ }
+ vp = ip->i_devvp;
+ bp->b_dev = vp->v_rdev;
+ VOCALL (vp->v_op, VOFFSET(vop_strategy), ap);
+ return (0);
+}
+
+/*
+ * Print out the contents of an inode.
+ */
+int
+cd9660_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ printf("tag VT_ISOFS, isofs vnode\n");
+ return 0;
+}
+
+/*
+ * Unsupported operation
+ */
+int
+cd9660_enotsupp()
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * Global vfs data structures for isofs
+ */
+#define cd9660_create \
+ ((int (*) __P((struct vop_create_args *)))cd9660_enotsupp)
+#define cd9660_mknod ((int (*) __P((struct vop_mknod_args *)))cd9660_enotsupp)
+#define cd9660_setattr \
+ ((int (*) __P((struct vop_setattr_args *)))cd9660_enotsupp)
+#define cd9660_write ((int (*) __P((struct vop_write_args *)))cd9660_enotsupp)
+#define cd9660_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
+#define cd9660_remove \
+ ((int (*) __P((struct vop_remove_args *)))cd9660_enotsupp)
+#define cd9660_link ((int (*) __P((struct vop_link_args *)))cd9660_enotsupp)
+#define cd9660_rename \
+ ((int (*) __P((struct vop_rename_args *)))cd9660_enotsupp)
+#define cd9660_mkdir ((int (*) __P((struct vop_mkdir_args *)))cd9660_enotsupp)
+#define cd9660_rmdir ((int (*) __P((struct vop_rmdir_args *)))cd9660_enotsupp)
+#define cd9660_symlink \
+ ((int (*) __P((struct vop_symlink_args *)))cd9660_enotsupp)
+#define cd9660_pathconf \
+ ((int (*) __P((struct vop_pathconf_args *)))cd9660_enotsupp)
+#define cd9660_advlock \
+ ((int (*) __P((struct vop_advlock_args *)))cd9660_enotsupp)
+#define cd9660_blkatoff \
+ ((int (*) __P((struct vop_blkatoff_args *)))cd9660_enotsupp)
+#define cd9660_valloc ((int(*) __P(( \
+ struct vnode *pvp, \
+ int mode, \
+ struct ucred *cred, \
+ struct vnode **vpp))) cd9660_enotsupp)
+#define cd9660_vfree ((int (*) __P((struct vop_vfree_args *)))cd9660_enotsupp)
+#define cd9660_truncate \
+ ((int (*) __P((struct vop_truncate_args *)))cd9660_enotsupp)
+#define cd9660_update \
+ ((int (*) __P((struct vop_update_args *)))cd9660_enotsupp)
+#define cd9660_bwrite \
+ ((int (*) __P((struct vop_bwrite_args *)))cd9660_enotsupp)
+
+/*
+ * Global vfs data structures for nfs
+ */
+int (**cd9660_vnodeop_p)();
+struct vnodeopv_entry_desc cd9660_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, cd9660_lookup }, /* lookup */
+ { &vop_create_desc, cd9660_create }, /* create */
+ { &vop_mknod_desc, cd9660_mknod }, /* mknod */
+ { &vop_open_desc, cd9660_open }, /* open */
+ { &vop_close_desc, cd9660_close }, /* close */
+ { &vop_access_desc, cd9660_access }, /* access */
+ { &vop_getattr_desc, cd9660_getattr }, /* getattr */
+ { &vop_setattr_desc, cd9660_setattr }, /* setattr */
+ { &vop_read_desc, cd9660_read }, /* read */
+ { &vop_write_desc, cd9660_write }, /* write */
+ { &vop_ioctl_desc, cd9660_ioctl }, /* ioctl */
+ { &vop_select_desc, cd9660_select }, /* select */
+ { &vop_mmap_desc, cd9660_mmap }, /* mmap */
+ { &vop_fsync_desc, cd9660_fsync }, /* fsync */
+ { &vop_seek_desc, cd9660_seek }, /* seek */
+ { &vop_remove_desc, cd9660_remove }, /* remove */
+ { &vop_link_desc, cd9660_link }, /* link */
+ { &vop_rename_desc, cd9660_rename }, /* rename */
+ { &vop_mkdir_desc, cd9660_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, cd9660_rmdir }, /* rmdir */
+ { &vop_symlink_desc, cd9660_symlink }, /* symlink */
+ { &vop_readdir_desc, cd9660_readdir }, /* readdir */
+ { &vop_readlink_desc, cd9660_readlink },/* readlink */
+ { &vop_abortop_desc, cd9660_abortop }, /* abortop */
+ { &vop_inactive_desc, cd9660_inactive },/* inactive */
+ { &vop_reclaim_desc, cd9660_reclaim }, /* reclaim */
+ { &vop_lock_desc, cd9660_lock }, /* lock */
+ { &vop_unlock_desc, cd9660_unlock }, /* unlock */
+ { &vop_bmap_desc, cd9660_bmap }, /* bmap */
+ { &vop_strategy_desc, cd9660_strategy },/* strategy */
+ { &vop_print_desc, cd9660_print }, /* print */
+ { &vop_islocked_desc, cd9660_islocked },/* islocked */
+ { &vop_pathconf_desc, cd9660_pathconf },/* pathconf */
+ { &vop_advlock_desc, cd9660_advlock }, /* advlock */
+ { &vop_blkatoff_desc, cd9660_blkatoff },/* blkatoff */
+ { &vop_valloc_desc, cd9660_valloc }, /* valloc */
+ { &vop_vfree_desc, cd9660_vfree }, /* vfree */
+ { &vop_truncate_desc, cd9660_truncate },/* truncate */
+ { &vop_update_desc, cd9660_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc cd9660_vnodeop_opv_desc =
+ { &cd9660_vnodeop_p, cd9660_vnodeop_entries };
+
+/*
+ * Special device vnode ops
+ */
+int (**cd9660_specop_p)();
+struct vnodeopv_entry_desc cd9660_specop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, spec_lookup }, /* lookup */
+ { &vop_create_desc, cd9660_create }, /* create */
+ { &vop_mknod_desc, cd9660_mknod }, /* mknod */
+ { &vop_open_desc, spec_open }, /* open */
+ { &vop_close_desc, spec_close }, /* close */
+ { &vop_access_desc, cd9660_access }, /* access */
+ { &vop_getattr_desc, cd9660_getattr }, /* getattr */
+ { &vop_setattr_desc, cd9660_setattr }, /* setattr */
+ { &vop_read_desc, spec_read }, /* read */
+ { &vop_write_desc, spec_write }, /* write */
+ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */
+ { &vop_select_desc, spec_select }, /* select */
+ { &vop_mmap_desc, spec_mmap }, /* mmap */
+ { &vop_fsync_desc, spec_fsync }, /* fsync */
+ { &vop_seek_desc, spec_seek }, /* seek */
+ { &vop_remove_desc, cd9660_remove }, /* remove */
+ { &vop_link_desc, cd9660_link }, /* link */
+ { &vop_rename_desc, cd9660_rename }, /* rename */
+ { &vop_mkdir_desc, cd9660_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, cd9660_rmdir }, /* rmdir */
+ { &vop_symlink_desc, cd9660_symlink }, /* symlink */
+ { &vop_readdir_desc, spec_readdir }, /* readdir */
+ { &vop_readlink_desc, spec_readlink }, /* readlink */
+ { &vop_abortop_desc, spec_abortop }, /* abortop */
+ { &vop_inactive_desc, cd9660_inactive },/* inactive */
+ { &vop_reclaim_desc, cd9660_reclaim }, /* reclaim */
+ { &vop_lock_desc, cd9660_lock }, /* lock */
+ { &vop_unlock_desc, cd9660_unlock }, /* unlock */
+ { &vop_bmap_desc, spec_bmap }, /* bmap */
+ /* XXX strategy: panics, should be notsupp instead? */
+ { &vop_strategy_desc, cd9660_strategy },/* strategy */
+ { &vop_print_desc, cd9660_print }, /* print */
+ { &vop_islocked_desc, cd9660_islocked },/* islocked */
+ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */
+ { &vop_advlock_desc, spec_advlock }, /* advlock */
+ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, spec_valloc }, /* valloc */
+ { &vop_vfree_desc, spec_vfree }, /* vfree */
+ { &vop_truncate_desc, spec_truncate }, /* truncate */
+ { &vop_update_desc, cd9660_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc cd9660_specop_opv_desc =
+ { &cd9660_specop_p, cd9660_specop_entries };
+
+#ifdef FIFO
+int (**cd9660_fifoop_p)();
+struct vnodeopv_entry_desc cd9660_fifoop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, fifo_lookup }, /* lookup */
+ { &vop_create_desc, cd9660_create }, /* create */
+ { &vop_mknod_desc, cd9660_mknod }, /* mknod */
+ { &vop_open_desc, fifo_open }, /* open */
+ { &vop_close_desc, fifo_close }, /* close */
+ { &vop_access_desc, cd9660_access }, /* access */
+ { &vop_getattr_desc, cd9660_getattr }, /* getattr */
+ { &vop_setattr_desc, cd9660_setattr }, /* setattr */
+ { &vop_read_desc, fifo_read }, /* read */
+ { &vop_write_desc, fifo_write }, /* write */
+ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */
+ { &vop_select_desc, fifo_select }, /* select */
+ { &vop_mmap_desc, fifo_mmap }, /* mmap */
+ { &vop_fsync_desc, fifo_fsync }, /* fsync */
+ { &vop_seek_desc, fifo_seek }, /* seek */
+ { &vop_remove_desc, cd9660_remove }, /* remove */
+ { &vop_link_desc, cd9660_link }, /* link */
+ { &vop_rename_desc, cd9660_rename }, /* rename */
+ { &vop_mkdir_desc, cd9660_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, cd9660_rmdir }, /* rmdir */
+ { &vop_symlink_desc, cd9660_symlink }, /* symlink */
+ { &vop_readdir_desc, fifo_readdir }, /* readdir */
+ { &vop_readlink_desc, fifo_readlink }, /* readlink */
+ { &vop_abortop_desc, fifo_abortop }, /* abortop */
+ { &vop_inactive_desc, cd9660_inactive },/* inactive */
+ { &vop_reclaim_desc, cd9660_reclaim }, /* reclaim */
+ { &vop_lock_desc, cd9660_lock }, /* lock */
+ { &vop_unlock_desc, cd9660_unlock }, /* unlock */
+ { &vop_bmap_desc, fifo_bmap }, /* bmap */
+ { &vop_strategy_desc, fifo_badop }, /* strategy */
+ { &vop_print_desc, cd9660_print }, /* print */
+ { &vop_islocked_desc, cd9660_islocked },/* islocked */
+ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */
+ { &vop_advlock_desc, fifo_advlock }, /* advlock */
+ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, fifo_valloc }, /* valloc */
+ { &vop_vfree_desc, fifo_vfree }, /* vfree */
+ { &vop_truncate_desc, fifo_truncate }, /* truncate */
+ { &vop_update_desc, cd9660_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc cd9660_fifoop_opv_desc =
+ { &cd9660_fifoop_p, cd9660_fifoop_entries };
+#endif /* FIFO */
diff --git a/sys/isofs/cd9660/iso.h b/sys/isofs/cd9660/iso.h
new file mode 100644
index 0000000..e356706
--- /dev/null
+++ b/sys/isofs/cd9660/iso.h
@@ -0,0 +1,256 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)iso.h 8.2 (Berkeley) 1/23/94
+ */
+
+#define ISODCL(from, to) (to - from + 1)
+
+struct iso_volume_descriptor {
+ char type[ISODCL(1,1)]; /* 711 */
+ char id[ISODCL(2,6)];
+ char version[ISODCL(7,7)];
+ char data[ISODCL(8,2048)];
+};
+
+/* volume descriptor types */
+#define ISO_VD_PRIMARY 1
+#define ISO_VD_END 255
+
+#define ISO_STANDARD_ID "CD001"
+#define ISO_ECMA_ID "CDW01"
+
+struct iso_primary_descriptor {
+ char type [ISODCL ( 1, 1)]; /* 711 */
+ char id [ISODCL ( 2, 6)];
+ char version [ISODCL ( 7, 7)]; /* 711 */
+ char unused1 [ISODCL ( 8, 8)];
+ char system_id [ISODCL ( 9, 40)]; /* achars */
+ char volume_id [ISODCL ( 41, 72)]; /* dchars */
+ char unused2 [ISODCL ( 73, 80)];
+ char volume_space_size [ISODCL ( 81, 88)]; /* 733 */
+ char unused3 [ISODCL ( 89, 120)];
+ char volume_set_size [ISODCL (121, 124)]; /* 723 */
+ char volume_sequence_number [ISODCL (125, 128)]; /* 723 */
+ char logical_block_size [ISODCL (129, 132)]; /* 723 */
+ char path_table_size [ISODCL (133, 140)]; /* 733 */
+ char type_l_path_table [ISODCL (141, 144)]; /* 731 */
+ char opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */
+ char type_m_path_table [ISODCL (149, 152)]; /* 732 */
+ char opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */
+ char root_directory_record [ISODCL (157, 190)]; /* 9.1 */
+ char volume_set_id [ISODCL (191, 318)]; /* dchars */
+ char publisher_id [ISODCL (319, 446)]; /* achars */
+ char preparer_id [ISODCL (447, 574)]; /* achars */
+ char application_id [ISODCL (575, 702)]; /* achars */
+ char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */
+ char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */
+ char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */
+ char creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */
+ char modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */
+ char expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */
+ char effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */
+ char file_structure_version [ISODCL (882, 882)]; /* 711 */
+ char unused4 [ISODCL (883, 883)];
+ char application_data [ISODCL (884, 1395)];
+ char unused5 [ISODCL (1396, 2048)];
+};
+#define ISO_DEFAULT_BLOCK_SIZE 2048
+
+struct iso_directory_record {
+ char length [ISODCL (1, 1)]; /* 711 */
+ char ext_attr_length [ISODCL (2, 2)]; /* 711 */
+ unsigned char extent [ISODCL (3, 10)]; /* 733 */
+ unsigned char size [ISODCL (11, 18)]; /* 733 */
+ char date [ISODCL (19, 25)]; /* 7 by 711 */
+ char flags [ISODCL (26, 26)];
+ char file_unit_size [ISODCL (27, 27)]; /* 711 */
+ char interleave [ISODCL (28, 28)]; /* 711 */
+ char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
+ char name_len [ISODCL (33, 33)]; /* 711 */
+ char name [0];
+};
+/* can't take sizeof(iso_directory_record), because of possible alignment
+ of the last entry (34 instead of 33) */
+#define ISO_DIRECTORY_RECORD_SIZE 33
+
+struct iso_extended_attributes {
+ unsigned char owner [ISODCL (1, 4)]; /* 723 */
+ unsigned char group [ISODCL (5, 8)]; /* 723 */
+ unsigned char perm [ISODCL (9, 10)]; /* 9.5.3 */
+ char ctime [ISODCL (11, 27)]; /* 8.4.26.1 */
+ char mtime [ISODCL (28, 44)]; /* 8.4.26.1 */
+ char xtime [ISODCL (45, 61)]; /* 8.4.26.1 */
+ char ftime [ISODCL (62, 78)]; /* 8.4.26.1 */
+ char recfmt [ISODCL (79, 79)]; /* 711 */
+ char recattr [ISODCL (80, 80)]; /* 711 */
+ unsigned char reclen [ISODCL (81, 84)]; /* 723 */
+ char system_id [ISODCL (85, 116)]; /* achars */
+ char system_use [ISODCL (117, 180)];
+ char version [ISODCL (181, 181)]; /* 711 */
+ char len_esc [ISODCL (182, 182)]; /* 711 */
+ char reserved [ISODCL (183, 246)];
+ unsigned char len_au [ISODCL (247, 250)]; /* 723 */
+};
+
+/* CD-ROM Format type */
+enum ISO_FTYPE { ISO_FTYPE_DEFAULT, ISO_FTYPE_9660, ISO_FTYPE_RRIP, ISO_FTYPE_ECMA };
+
+#ifndef ISOFSMNT_ROOT
+#define ISOFSMNT_ROOT 0
+#endif
+
+struct iso_mnt {
+ int im_flags;
+
+ struct mount *im_mountp;
+ dev_t im_dev;
+ struct vnode *im_devvp;
+
+ int logical_block_size;
+ int im_bshift;
+ int im_bmask;
+
+ int volume_space_size;
+ char im_fsmnt[50];
+ struct netexport im_export;
+
+ char root[ISODCL (157, 190)];
+ int root_extent;
+ int root_size;
+ enum ISO_FTYPE iso_ftype;
+
+ int rr_skip;
+ int rr_skip0;
+};
+
+#define VFSTOISOFS(mp) ((struct iso_mnt *)((mp)->mnt_data))
+
+#define iso_blkoff(imp, loc) ((loc) & (imp)->im_bmask)
+#define iso_lblkno(imp, loc) ((loc) >> (imp)->im_bshift)
+#define iso_blksize(imp, ip, lbn) ((imp)->logical_block_size)
+#define iso_lblktosize(imp, blk) ((blk) << (imp)->im_bshift)
+
+int cd9660_mount __P((struct mount *,
+ char *, caddr_t, struct nameidata *, struct proc *));
+int cd9660_start __P((struct mount *, int, struct proc *));
+int cd9660_unmount __P((struct mount *, int, struct proc *));
+int cd9660_root __P((struct mount *, struct vnode **));
+int cd9660_quotactl __P((struct mount *, int, uid_t, caddr_t, struct proc *));
+int cd9660_statfs __P((struct mount *, struct statfs *, struct proc *));
+int cd9660_sync __P((struct mount *, int, struct ucred *, struct proc *));
+int cd9660_vget __P((struct mount *, ino_t, struct vnode **));
+int cd9660_fhtovp __P((struct mount *, struct fid *, struct mbuf *,
+ struct vnode **, int *, struct ucred **));
+int cd9660_vptofh __P((struct vnode *, struct fid *));
+int cd9660_init __P(());
+
+struct iso_node;
+int iso_blkatoff __P((struct iso_node *ip, long offset, struct buf **bpp));
+int iso_iget __P((struct iso_node *xp, ino_t ino, int relocated,
+ struct iso_node **ipp, struct iso_directory_record *isodir));
+int iso_iput __P((struct iso_node *ip));
+int iso_ilock __P((struct iso_node *ip));
+int iso_iunlock __P((struct iso_node *ip));
+int cd9660_mountroot __P((void));
+
+extern int (**cd9660_vnodeop_p)();
+
+extern inline int
+isonum_711(p)
+ unsigned char *p;
+{
+ return *p;
+}
+
+extern inline int
+isonum_712(p)
+ char *p;
+{
+ return *p;
+}
+
+extern inline int
+isonum_721(p)
+ unsigned char *p;
+{
+ return *p|((char)p[1] << 8);
+}
+
+extern inline int
+isonum_722(p)
+ unsigned char *p;
+{
+ return ((char)*p << 8)|p[1];
+}
+
+extern inline int
+isonum_723(p)
+ unsigned char *p;
+{
+ return isonum_721(p);
+}
+
+extern inline int
+isonum_731(p)
+ unsigned char *p;
+{
+ return *p|(p[1] << 8)|(p[2] << 16)|(p[3] << 24);
+}
+
+extern inline int
+isonum_732(p)
+ unsigned char *p;
+{
+ return (*p << 24)|(p[1] << 16)|(p[2] << 8)|p[3];
+}
+
+extern inline int
+isonum_733(p)
+ unsigned char *p;
+{
+ return isonum_731(p);
+}
+
+int isofncmp __P((unsigned char *, int, unsigned char *, int));
+void isofntrans __P((unsigned char *, int, unsigned char *, unsigned short *,
+ int, int));
+
+/*
+ * Associated files have a leading '='.
+ */
+#define ASSOCCHAR '='
diff --git a/sys/isofs/cd9660/iso_rrip.h b/sys/isofs/cd9660/iso_rrip.h
new file mode 100644
index 0000000..78e4a77
--- /dev/null
+++ b/sys/isofs/cd9660/iso_rrip.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)iso_rrip.h 8.2 (Berkeley) 1/23/94
+ */
+
+
+/*
+ * Analyze function flag (similar to RR field bits)
+ */
+#define ISO_SUSP_ATTR 0x0001
+#define ISO_SUSP_DEVICE 0x0002
+#define ISO_SUSP_SLINK 0x0004
+#define ISO_SUSP_ALTNAME 0x0008
+#define ISO_SUSP_CLINK 0x0010
+#define ISO_SUSP_PLINK 0x0020
+#define ISO_SUSP_RELDIR 0x0040
+#define ISO_SUSP_TSTAMP 0x0080
+#define ISO_SUSP_IDFLAG 0x0100
+#define ISO_SUSP_EXTREF 0x0200
+#define ISO_SUSP_CONT 0x0400
+#define ISO_SUSP_OFFSET 0x0800
+#define ISO_SUSP_STOP 0x1000
+#define ISO_SUSP_UNKNOWN 0x8000
+
+typedef struct {
+ struct iso_node *inop;
+ int fields; /* interesting fields in this analysis */
+ daddr_t iso_ce_blk; /* block of continuation area */
+ off_t iso_ce_off; /* offset of continuation area */
+ int iso_ce_len; /* length of continuation area */
+ struct iso_mnt *imp; /* mount structure */
+ ino_t *inump; /* inode number pointer */
+ char *outbuf; /* name/symbolic link output area */
+ u_short *outlen; /* length of above */
+ u_short maxlen; /* maximum length of above */
+ int cont; /* continuation of above */
+} ISO_RRIP_ANALYZE;
+
+int cd9660_rrip_analyze __P((struct iso_directory_record *isodir,
+ struct iso_node *inop, struct iso_mnt *imp));
+int cd9660_rrip_getname __P((struct iso_directory_record *isodir,
+ char *outbuf, u_short *outlen,
+ ino_t *inump, struct iso_mnt *imp));
+int cd9660_rrip_getsymname __P((struct iso_directory_record *isodir,
+ char *outbuf, u_short *outlen,
+ struct iso_mnt *imp));
+int cd9660_rrip_offset __P((struct iso_directory_record *isodir,
+ struct iso_mnt *imp));
diff --git a/sys/kern/Make.tags.inc b/sys/kern/Make.tags.inc
new file mode 100644
index 0000000..1563c41
--- /dev/null
+++ b/sys/kern/Make.tags.inc
@@ -0,0 +1,18 @@
+# @(#)Make.tags.inc 8.1 (Berkeley) 6/11/93
+
+# Common files for "make tags".
+# Included by the Makefile for each architecture.
+
+# Put the ../sys stuff near the end so that subroutine definitions win when
+# there is a struct tag with the same name (eg., vmmeter). The real
+# solution would probably be for ctags to generate "struct vmmeter" tags.
+
+COMM= /sys/conf/*.[ch] \
+ /sys/dev/*.[ch] /sys/dev/scsi/*.[ch] \
+ /sys/kern/*.[ch] /sys/libkern/*.[ch] \
+ /sys/miscfs/*/*.[ch] \
+ /sys/net/*.[ch] /sys/netccitt/*.[ch] /sys/netinet/*.[ch] \
+ /sys/netiso/*.[ch] /sys/netns/*.[ch] \
+ /sys/nfs/*.[ch] /sys/sys/*.[ch] \
+ /sys/ufs/*/*.[ch] \
+ /sys/vm/*.[ch]
diff --git a/sys/kern/Makefile b/sys/kern/Makefile
new file mode 100644
index 0000000..cfe962a
--- /dev/null
+++ b/sys/kern/Makefile
@@ -0,0 +1,50 @@
+# @(#)Makefile 8.2 (Berkeley) 3/21/94
+
+# Makefile for kernel tags files, init_sysent, etc.
+
+ARCH= hp300 i386 luna68k news3400 pmax sparc tahoe vax
+
+all:
+ @echo "make tags, make links or init_sysent.c only"
+
+init_sysent.c syscalls.c ../sys/syscall.h: makesyscalls.sh syscalls.master
+ -mv -f init_sysent.c init_sysent.c.bak
+ -mv -f syscalls.c syscalls.c.bak
+ -mv -f ../sys/syscall.h ../sys/syscall.h.bak
+ sh makesyscalls.sh syscalls.master
+
+# Kernel tags:
+# Tags files are built in the top-level directory for each architecture,
+# with a makefile listing the architecture-dependent files, etc. The list
+# of common files is in ./Make.tags.inc. Links to the correct tags file
+# are placed in each source directory. We need to have links to tags files
+# from the generic directories that are relative to the machine type, even
+# via remote mounts; therefore we use symlinks to $SYSTAGS, which points at
+# ${SYSDIR}/${MACHINE}/tags.
+
+SYSTAGS=/var/db/sys_tags
+SYSDIR=/sys
+
+# Directories in which to place tags links (other than machine-dependent)
+DGEN= conf \
+ dev dev/scsi \
+ hp hp/dev hp/hpux \
+ kern libkern \
+ miscfs miscfs/deadfs miscfs/fdesc miscfs/fifofs miscfs/kernfs \
+ miscfs/lofs miscfs/nullfs miscfs/portal miscfs/procfs \
+ miscfs/specfs miscfs/umapfs miscfs/union \
+ net netccitt netinet netiso netns nfs scripts sys \
+ ufs ufs/ffs ufs/lfs ufs/mfs ufs/ufs \
+ vm
+
+tags::
+ -for i in ${ARCH}; do \
+ (cd ../$$i && make ${MFLAGS} tags); done
+
+links::
+ rm -f ${SYSTAGS}
+ ln -s ${SYSDIR}/${MACHINE}/tags ${SYSTAGS}
+ -for i in ${DGEN}; do \
+ (cd ../$$i && { rm -f tags; ln -s ${SYSTAGS} tags; }) done
+ -for i in ${ARCH}; do \
+ (cd ../$$i && make ${MFLAGS} SYSTAGS=${SYSTAGS} links); done
diff --git a/sys/kern/imgact_aout.c b/sys/kern/imgact_aout.c
new file mode 100644
index 0000000..4be4e50
--- /dev/null
+++ b/sys/kern/imgact_aout.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 1993, David Greenman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by David Greenman
+ * 4. The name of the developer may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: imgact_aout.c,v 1.3 1993/12/30 01:39:29 davidg Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/resourcevar.h>
+#include <sys/exec.h>
+#include <sys/mman.h>
+#include <sys/imgact.h>
+#include <sys/kernel.h>
+
+#include <vm/vm.h>
+
+int
+exec_aout_imgact(iparams)
+ struct image_params *iparams;
+{
+ struct exec *a_out = (struct exec *) iparams->image_header;
+ struct vmspace *vmspace = iparams->proc->p_vmspace;
+ unsigned long vmaddr, virtual_offset, file_offset;
+ unsigned long bss_size;
+ int error, len;
+
+ /*
+ * Set file/virtual offset based on a.out variant.
+ * We do two cases: host byte order and network byte order
+ * (for NetBSD compatibility)
+ */
+ switch ((int)(a_out->a_magic & 0xffff)) {
+ case ZMAGIC:
+ virtual_offset = 0;
+ if (a_out->a_text) {
+ file_offset = NBPG;
+ } else {
+ /* Bill's "screwball mode" */
+ file_offset = 0;
+ }
+ break;
+ case QMAGIC:
+ virtual_offset = NBPG;
+ file_offset = 0;
+ break;
+ default:
+ /* NetBSD compatibility */
+ switch ((int)(ntohl(a_out->a_magic) & 0xffff)) {
+ case ZMAGIC:
+ case QMAGIC:
+ virtual_offset = NBPG;
+ file_offset = 0;
+ break;
+ default:
+ return (-1);
+ }
+ }
+
+ bss_size = roundup(a_out->a_bss, NBPG);
+
+ /*
+ * Check various fields in header for validity/bounds.
+ */
+ if (/* entry point must lay with text region */
+ a_out->a_entry < virtual_offset ||
+ a_out->a_entry >= virtual_offset + a_out->a_text ||
+
+ /* text and data size must each be page rounded */
+ a_out->a_text % NBPG ||
+ a_out->a_data % NBPG)
+ return (-1);
+
+ /* text + data can't exceed file size */
+ if (a_out->a_data + a_out->a_text > iparams->attr->va_size)
+ return (EFAULT);
+
+ /*
+ * text/data/bss must not exceed limits
+ */
+ if (/* text can't exceed maximum text size */
+ a_out->a_text > MAXTSIZ ||
+
+ /* data + bss can't exceed maximum data size */
+ a_out->a_data + bss_size > MAXDSIZ ||
+
+ /* data + bss can't exceed rlimit */
+ a_out->a_data + bss_size >
+ iparams->proc->p_rlimit[RLIMIT_DATA].rlim_cur)
+ return (ENOMEM);
+
+ /* copy in arguments and/or environment from old process */
+ error = exec_extract_strings(iparams);
+ if (error)
+ return (error);
+
+ /*
+ * Destroy old process VM and create a new one (with a new stack)
+ */
+ exec_new_vmspace(iparams);
+
+ /*
+ * Map text read/execute
+ */
+ vmaddr = virtual_offset;
+ error =
+ vm_mmap(&vmspace->vm_map, /* map */
+ &vmaddr, /* address */
+ a_out->a_text, /* size */
+ VM_PROT_READ | VM_PROT_EXECUTE, /* protection */
+ VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_WRITE, /* max protection */
+ MAP_PRIVATE | MAP_FIXED, /* flags */
+ (caddr_t)iparams->vnodep, /* vnode */
+ file_offset); /* offset */
+ if (error)
+ return (error);
+
+ /*
+ * Map data read/write (if text is 0, assume text is in data area
+ * [Bill's screwball mode])
+ */
+ vmaddr = virtual_offset + a_out->a_text;
+ error =
+ vm_mmap(&vmspace->vm_map,
+ &vmaddr,
+ a_out->a_data,
+ VM_PROT_READ | VM_PROT_WRITE | (a_out->a_text ? 0 : VM_PROT_EXECUTE),
+ VM_PROT_ALL, MAP_PRIVATE | MAP_FIXED,
+ (caddr_t) iparams->vnodep,
+ file_offset + a_out->a_text);
+ if (error)
+ return (error);
+
+ /*
+ * Allocate demand-zeroed area for uninitialized data
+ * "bss" = 'block started by symbol' - named after the IBM 7090
+ * instruction of the same name.
+ */
+ vmaddr = virtual_offset + a_out->a_text + a_out->a_data;
+ error = vm_allocate(&vmspace->vm_map, &vmaddr, bss_size, FALSE);
+ if (error)
+ return (error);
+
+ /* Fill in process VM information */
+ vmspace->vm_tsize = a_out->a_text >> PAGE_SHIFT;
+ vmspace->vm_dsize = (a_out->a_data + bss_size) >> PAGE_SHIFT;
+ vmspace->vm_taddr = (caddr_t) virtual_offset;
+ vmspace->vm_daddr = (caddr_t) virtual_offset + a_out->a_text;
+
+ /* Fill in image_params */
+ iparams->interpreted = 0;
+ iparams->entry_addr = a_out->a_entry;
+
+ return (0);
+}
+
+/*
+ * Tell kern_execve.c about it, with a little help from the linker.
+ * Since `const' objects end up in the text segment, TEXT_SET is the
+ * correct directive to use.
+ */
+static const struct execsw aout_execsw = { exec_aout_imgact };
+TEXT_SET(execsw_set, aout_execsw);
+
diff --git a/sys/kern/imgact_shell.c b/sys/kern/imgact_shell.c
new file mode 100644
index 0000000..e4f4d95
--- /dev/null
+++ b/sys/kern/imgact_shell.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1993, David Greenman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by David Greenman
+ * 4. The name of the developer may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: imgact_shell.c,v 1.1 1993/12/20 16:16:46 wollman Exp $
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "resourcevar.h"
+#include <sys/exec.h>
+#include "imgact.h"
+#include "kernel.h"
+#include "machine/endian.h"
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define SHELLMAGIC 0x2123 /* #! */
+#else
+#define SHELLMAGIC 0x2321
+#endif
+
+#define MAXSHELLCMDLEN 64
+
+/*
+ * Shell interpreter image activator. A interpreter name beginning
+ * at iparams->stringbase is the minimal successful exit requirement.
+ */
+int
+exec_shell_imgact(iparams)
+ struct image_params *iparams;
+{
+ const char *image_header = iparams->image_header;
+ const char *ihp, *line_endp;
+ int length;
+ char *interp;
+ char **argv;
+
+ /* a shell script? */
+ if (((short *) image_header)[0] != SHELLMAGIC)
+ return(-1);
+
+ /*
+ * Don't allow a shell script to be the shell for a shell
+ * script. :-)
+ */
+ if (iparams->interpreted)
+ return(ENOEXEC);
+
+ iparams->interpreted = 1;
+
+ /*
+ * Copy shell name and arguments from image_header into string
+ * buffer.
+ */
+
+ /*
+ * Find end of line; return if the line > MAXSHELLCMDLEN long.
+ */
+ for (ihp = &image_header[2]; *ihp != '\n'; ++ihp) {
+ if (ihp >= &image_header[MAXSHELLCMDLEN])
+ return(ENOEXEC);
+ }
+ line_endp = ihp;
+
+ /* reset for another pass */
+ ihp = &image_header[2];
+
+ /* Skip over leading spaces - until the interpreter name */
+ while ((*ihp == ' ') || (*ihp == '\t')) ihp++;
+
+ /* copy the interpreter name */
+ interp = iparams->interpreter_name;
+ while ((ihp < line_endp) && (*ihp != ' ') && (*ihp != '\t'))
+ *interp++ = *ihp++;
+ *interp = '\0';
+
+ /* Disallow a null interpreter filename */
+ if (*iparams->interpreter_name == '\0')
+ return(ENOEXEC);
+
+ /* reset for another pass */
+ ihp = &image_header[2];
+
+ /* copy the interpreter name and arguments */
+ while (ihp < line_endp) {
+ /* Skip over leading spaces */
+ while ((*ihp == ' ') || (*ihp == '\t')) ihp++;
+
+ if (ihp < line_endp) {
+ /*
+ * Copy to end of token. No need to watch stringspace
+ * because this is at the front of the string buffer
+ * and the maximum shell command length is tiny.
+ */
+ while ((ihp < line_endp) && (*ihp != ' ') && (*ihp != '\t')) {
+ *iparams->stringp++ = *ihp++;
+ iparams->stringspace--;
+ }
+
+ *iparams->stringp++ = 0;
+ iparams->stringspace--;
+
+ iparams->argc++;
+ }
+ }
+
+ /* set argv[0] to point to original file name */
+ suword(iparams->uap->argv, (int)iparams->uap->fname);
+
+ return(0);
+}
+
+/*
+ * Tell kern_execve.c about it, with a little help from the linker.
+ * Since `const' objects end up in the text segment, TEXT_SET is the
+ * correct directive to use.
+ */
+static const struct execsw shell_execsw = { exec_shell_imgact };
+TEXT_SET(execsw_set, shell_execsw);
diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c
new file mode 100644
index 0000000..ea604ab
--- /dev/null
+++ b/sys/kern/init_main.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1991, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)init_main.c 8.9 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/filedesc.h>
+#include <sys/errno.h>
+#include <sys/exec.h>
+#include <sys/kernel.h>
+#include <sys/mount.h>
+#include <sys/map.h>
+#include <sys/proc.h>
+#include <sys/resourcevar.h>
+#include <sys/signalvar.h>
+#include <sys/systm.h>
+#include <sys/vnode.h>
+#include <sys/conf.h>
+#include <sys/buf.h>
+#include <sys/clist.h>
+#include <sys/device.h>
+#include <sys/protosw.h>
+#include <sys/reboot.h>
+#include <sys/user.h>
+
+#include <ufs/ufs/quota.h>
+
+#include <machine/cpu.h>
+
+#include <vm/vm.h>
+
+#ifdef HPFPLIB
+char copyright[] =
+"Copyright (c) 1982, 1986, 1989, 1991, 1993\n\tThe Regents of the University of California.\nCopyright (c) 1992 Hewlett-Packard Company\nCopyright (c) 1992 Motorola Inc.\nAll rights reserved.\n\n";
+#else
+char copyright[] =
+"Copyright (c) 1982, 1986, 1989, 1991, 1993\n\tThe Regents of the University of California. All rights reserved.\n\n";
+#endif
+
+/* Components of the first process -- never freed. */
+struct session session0;
+struct pgrp pgrp0;
+struct proc proc0;
+struct pcred cred0;
+struct filedesc0 filedesc0;
+struct plimit limit0;
+struct vmspace vmspace0;
+struct proc *curproc = &proc0;
+struct proc *initproc, *pageproc, *updateproc;
+
+int cmask = CMASK;
+extern struct user *proc0paddr;
+
+struct vnode *rootvp, *swapdev_vp;
+int boothowto;
+struct timeval boottime;
+struct timeval runtime;
+
+static void start_init __P((struct proc *p, void *framep));
+
+#if __GNUC__ >= 2
+void __main() {}
+#endif
+
+/*
+ * This table is filled in by the linker with functions that need to be
+ * called to initialize various pseudo-devices and whatnot.
+ */
+typedef void (*pseudo_func_t)(void);
+extern const struct linker_set pseudo_set;
+static const pseudo_func_t *pseudos =
+ (const pseudo_func_t *)&pseudo_set.ls_items[0];
+
+/*
+ * System startup; initialize the world, create process 0, mount root
+ * filesystem, and fork to create init and pagedaemon. Most of the
+ * hard work is done in the lower-level initialization routines including
+ * startup(), which does memory initialization and autoconfiguration.
+ */
+void
+main(framep)
+ void *framep;
+{
+ register struct proc *p;
+ register struct filedesc0 *fdp;
+ register struct pdevinit *pdev;
+ register int i;
+ int s, rval[2];
+ extern int (*mountroot) __P((void));
+ extern struct pdevinit pdevinit[];
+ extern void roundrobin __P((void *));
+ extern void schedcpu __P((void *));
+
+ /*
+ * Initialize the current process pointer (curproc) before
+ * any possible traps/probes to simplify trap processing.
+ */
+ p = &proc0;
+ curproc = p;
+ /*
+ * Attempt to find console and initialize
+ * in case of early panic or other messages.
+ */
+ consinit();
+ printf(copyright);
+
+ vm_mem_init();
+ kmeminit();
+ cpu_startup();
+
+ /*
+ * Create process 0 (the swapper).
+ */
+ allproc = (volatile struct proc *)p;
+ p->p_prev = (struct proc **)&allproc;
+ p->p_pgrp = &pgrp0;
+ pgrphash[0] = &pgrp0;
+ pgrp0.pg_mem = p;
+ pgrp0.pg_session = &session0;
+ session0.s_count = 1;
+ session0.s_leader = p;
+
+ p->p_flag = P_INMEM | P_SYSTEM;
+ p->p_stat = SRUN;
+ p->p_nice = NZERO;
+ bcopy("swapper", p->p_comm, sizeof ("swapper"));
+
+ /* Create credentials. */
+ cred0.p_refcnt = 1;
+ p->p_cred = &cred0;
+ p->p_ucred = crget();
+ p->p_ucred->cr_ngroups = 1; /* group 0 */
+
+ /* Create the file descriptor table. */
+ fdp = &filedesc0;
+ p->p_fd = &fdp->fd_fd;
+ fdp->fd_fd.fd_refcnt = 1;
+ fdp->fd_fd.fd_cmask = cmask;
+ fdp->fd_fd.fd_ofiles = fdp->fd_dfiles;
+ fdp->fd_fd.fd_ofileflags = fdp->fd_dfileflags;
+ fdp->fd_fd.fd_nfiles = NDFILE;
+
+ /* Create the limits structures. */
+ p->p_limit = &limit0;
+ for (i = 0; i < sizeof(p->p_rlimit)/sizeof(p->p_rlimit[0]); i++)
+ limit0.pl_rlimit[i].rlim_cur =
+ limit0.pl_rlimit[i].rlim_max = RLIM_INFINITY;
+ limit0.pl_rlimit[RLIMIT_NOFILE].rlim_cur = NOFILE;
+ limit0.pl_rlimit[RLIMIT_NPROC].rlim_cur = MAXUPRC;
+ i = ptoa(cnt.v_free_count);
+ limit0.pl_rlimit[RLIMIT_RSS].rlim_max = i;
+ limit0.pl_rlimit[RLIMIT_MEMLOCK].rlim_max = i;
+ limit0.pl_rlimit[RLIMIT_MEMLOCK].rlim_cur = i / 3;
+ limit0.p_refcnt = 1;
+
+ /* Allocate a prototype map so we have something to fork. */
+ p->p_vmspace = &vmspace0;
+ vmspace0.vm_refcnt = 1;
+ pmap_pinit(&vmspace0.vm_pmap);
+ vm_map_init(&vmspace0.vm_map, round_page(VM_MIN_ADDRESS),
+ trunc_page(VM_MAX_ADDRESS), TRUE);
+ vmspace0.vm_map.pmap = &vmspace0.vm_pmap;
+ p->p_addr = proc0paddr; /* XXX */
+
+ /*
+ * We continue to place resource usage info and signal
+ * actions in the user struct so they're pageable.
+ */
+ p->p_stats = &p->p_addr->u_stats;
+ p->p_sigacts = &p->p_addr->u_sigacts;
+
+ /*
+ * Initialize per uid information structure and charge
+ * root for one process.
+ */
+ usrinfoinit();
+ (void)chgproccnt(0, 1);
+
+ rqinit();
+
+ /* Configure virtual memory system, set vm rlimits. */
+ vm_init_limits(p);
+
+ /* Initialize the file systems. */
+ vfsinit();
+
+ /* Start real time and statistics clocks. */
+ initclocks();
+
+ /* Initialize mbuf's. */
+ mbinit();
+
+ /* Initialize clists. */
+ clist_init();
+
+ /*
+ * Attach pseudo-devices.
+ */
+ while(*pseudos) {
+ (**pseudos++)();
+ }
+
+ /*
+ * Initialize protocols. Block reception of incoming packets
+ * until everything is ready.
+ */
+ s = splimp();
+ ifinit();
+ domaininit();
+ splx(s);
+
+#ifdef GPROF
+ /* Initialize kernel profiling. */
+ kmstartup();
+#endif
+
+ /* Kick off timeout driven events by calling first time. */
+ roundrobin(NULL);
+ schedcpu(NULL);
+
+ /* Mount the root file system. */
+ if ((*mountroot)())
+ panic("cannot mount root");
+
+ /* Get the vnode for '/'. Set fdp->fd_fd.fd_cdir to reference it. */
+ if (VFS_ROOT(mountlist.tqh_first, &rootvnode))
+ panic("cannot find root vnode");
+ fdp->fd_fd.fd_cdir = rootvnode;
+ VREF(fdp->fd_fd.fd_cdir);
+ VOP_UNLOCK(rootvnode);
+ fdp->fd_fd.fd_rdir = NULL;
+ swapinit();
+
+ /*
+ * Now can look at time, having had a chance to verify the time
+ * from the file system. Reset p->p_rtime as it may have been
+ * munched in mi_switch() after the time got set.
+ */
+ p->p_stats->p_start = runtime = mono_time = boottime = time;
+ p->p_rtime.tv_sec = p->p_rtime.tv_usec = 0;
+
+ /* Initialize signal state for process 0. */
+ siginit(p);
+
+ /* Create process 1 (init(8)). */
+ if (fork(p, NULL, rval))
+ panic("fork init");
+ if (rval[1]) {
+ start_init(curproc, framep);
+ return;
+ }
+
+ /* Create process 2 (the pageout daemon). */
+ if (fork(p, NULL, rval))
+ panic("fork pager");
+ if (rval[1]) {
+ /*
+ * Now in process 2.
+ */
+ p = curproc;
+ pageproc = p;
+ p->p_flag |= P_INMEM | P_SYSTEM; /* XXX */
+ bcopy("pagedaemon", curproc->p_comm, sizeof ("pagedaemon"));
+ vm_pageout();
+ /* NOTREACHED */
+ }
+
+ /*
+ * Start update daemon (process 3).
+ */
+#ifndef LAPTOP
+ if (fork(p, (void *) NULL, rval))
+ panic("failed fork update daemon");
+ if (rval[1]) {
+ p = curproc;
+ updateproc = p;
+ p->p_flag |= P_INMEM | P_SYSTEM;
+ bcopy("update", p->p_comm, sizeof("update"));
+ vfs_update();
+ /*NOTREACHED*/
+ }
+#endif
+
+ /* The scheduler is an infinite loop. */
+ scheduler();
+ /* NOTREACHED */
+}
+
+/*
+ * List of paths to try when searching for "init".
+ */
+static char *initpaths[] = {
+ "/sbin/init",
+ "/sbin/oinit",
+ "/sbin/init.bak",
+ NULL,
+};
+
+/*
+ * Start the initial user process; try exec'ing each pathname in "initpaths".
+ * The program is invoked with one argument containing the boot flags.
+ */
+static void
+start_init(p, framep)
+ struct proc *p;
+ void *framep;
+{
+ vm_offset_t addr;
+ struct execve_args args;
+ int options, i, retval[2], error;
+ char **pathp, *path, *ucp, **uap, *arg0, *arg1;
+
+ initproc = p;
+
+ /*
+ * We need to set the system call frame as if we were entered through
+ * a syscall() so that when we call execve() below, it will be able
+ * to set the entry point (see setregs) when it tries to exec. The
+ * startup code in "locore.s" has allocated space for the frame and
+ * passed a pointer to that space as main's argument.
+ */
+ cpu_set_init_frame(p, framep);
+
+ /*
+ * Need just enough stack to hold the faked-up "execve()" arguments.
+ */
+ addr = trunc_page(VM_MAXUSER_ADDRESS - PAGE_SIZE);
+ if (vm_allocate(&p->p_vmspace->vm_map, &addr, PAGE_SIZE, FALSE) != 0)
+ panic("init: couldn't allocate argument space");
+ p->p_vmspace->vm_maxsaddr = (caddr_t)addr;
+ p->p_vmspace->vm_ssize = 1;
+
+ for (pathp = &initpaths[0]; (path = *pathp) != NULL; pathp++) {
+ /*
+ * Move out the boot flag argument.
+ */
+ options = 0;
+ ucp = (char *)USRSTACK;
+ (void)subyte(--ucp, 0); /* trailing zero */
+ if (boothowto & RB_SINGLE) {
+ (void)subyte(--ucp, 's');
+ options = 1;
+ }
+#ifdef notyet
+ if (boothowto & RB_FASTBOOT) {
+ (void)subyte(--ucp, 'f');
+ options = 1;
+ }
+#endif
+ if (options == 0)
+ (void)subyte(--ucp, '-');
+ (void)subyte(--ucp, '-'); /* leading hyphen */
+ arg1 = ucp;
+
+ /*
+ * Move out the file name (also arg 0).
+ */
+ for (i = strlen(path) + 1; i >= 0; i--)
+ (void)subyte(--ucp, path[i]);
+ arg0 = ucp;
+
+ /*
+ * Move out the arg pointers.
+ */
+ uap = (char **)((int)ucp & ~(NBPW-1));
+ (void)suword((caddr_t)--uap, 0); /* terminator */
+ (void)suword((caddr_t)--uap, (int)arg1);
+ (void)suword((caddr_t)--uap, (int)arg0);
+
+ /*
+ * Point at the arguments.
+ */
+ args.fname = arg0;
+ args.argv = uap;
+ args.envv = NULL;
+
+ /*
+ * Now try to exec the program. If can't for any reason
+ * other than it doesn't exist, complain.
+ */
+ if ((error = execve(p, &args, &retval)) == 0)
+ return;
+ if (error != ENOENT)
+ printf("exec %s: error %d\n", path, error);
+ }
+ printf("init: not found\n");
+ panic("no init");
+}
diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c
new file mode 100644
index 0000000..80cab90
--- /dev/null
+++ b/sys/kern/init_sysent.c
@@ -0,0 +1,484 @@
+/*
+ * System call switch table.
+ *
+ * DO NOT EDIT-- this file is automatically generated.
+ * created from @(#)syscalls.master 8.2 (Berkeley) 1/13/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+int nosys();
+
+int nosys();
+int exit();
+int fork();
+int read();
+int write();
+int open();
+int close();
+int wait4();
+int link();
+int unlink();
+int chdir();
+int fchdir();
+int mknod();
+int chmod();
+int chown();
+int obreak();
+int getfsstat();
+int getpid();
+int mount();
+int unmount();
+int setuid();
+int getuid();
+int geteuid();
+int ptrace();
+int recvmsg();
+int sendmsg();
+int recvfrom();
+int accept();
+int getpeername();
+int getsockname();
+int access();
+int chflags();
+int fchflags();
+int sync();
+int kill();
+int getppid();
+int dup();
+int pipe();
+int getegid();
+int profil();
+#ifdef KTRACE
+int ktrace();
+#else
+#endif
+int sigaction();
+int getgid();
+int sigprocmask();
+int getlogin();
+int setlogin();
+int acct();
+int sigpending();
+int sigaltstack();
+int ioctl();
+int reboot();
+int revoke();
+int symlink();
+int readlink();
+int execve();
+int umask();
+int chroot();
+int msync();
+int vfork();
+int sbrk();
+int sstk();
+int ovadvise();
+int munmap();
+int mprotect();
+int madvise();
+int mincore();
+int getgroups();
+int setgroups();
+int getpgrp();
+int setpgid();
+int setitimer();
+int swapon();
+int getitimer();
+int getdtablesize();
+int dup2();
+int fcntl();
+int select();
+int fsync();
+int setpriority();
+int socket();
+int connect();
+int getpriority();
+int sigreturn();
+int bind();
+int setsockopt();
+int listen();
+int sigsuspend();
+#ifdef TRACE
+int vtrace();
+#else
+#endif
+int gettimeofday();
+int getrusage();
+int getsockopt();
+#ifdef vax
+int resuba();
+#else
+#endif
+int readv();
+int writev();
+int settimeofday();
+int fchown();
+int fchmod();
+int rename();
+int flock();
+int mkfifo();
+int sendto();
+int shutdown();
+int socketpair();
+int mkdir();
+int rmdir();
+int utimes();
+int adjtime();
+int setsid();
+int quotactl();
+#ifdef NFS
+int nfssvc();
+#else
+#endif
+int statfs();
+int fstatfs();
+#ifdef NFS
+int getfh();
+#else
+#endif
+int getdomainname();
+int setdomainname();
+int uname();
+int sysarch();
+#ifdef SYSVSHM
+int shmsys();
+#else
+#endif
+int setgid();
+int setegid();
+int seteuid();
+#ifdef LFS
+int lfs_bmapv();
+int lfs_markv();
+int lfs_segclean();
+int lfs_segwait();
+#else
+#endif
+int stat();
+int fstat();
+int lstat();
+int pathconf();
+int fpathconf();
+int getrlimit();
+int setrlimit();
+int getdirentries();
+int mmap();
+int nosys();
+int lseek();
+int truncate();
+int ftruncate();
+int __sysctl();
+int mlock();
+int munlock();
+
+#ifdef COMPAT_43
+#define compat(n, name) n, __CONCAT(o,name)
+
+int ocreat();
+int olseek();
+int ostat();
+int olstat();
+#ifdef KTRACE
+#else
+#endif
+int ofstat();
+int ogetkerninfo();
+int ogetpagesize();
+int ommap();
+int owait();
+int ogethostname();
+int osethostname();
+int oaccept();
+int osend();
+int orecv();
+int osigvec();
+int osigblock();
+int osigsetmask();
+int osigstack();
+int orecvmsg();
+int osendmsg();
+#ifdef TRACE
+#else
+#endif
+#ifdef vax
+#else
+#endif
+int orecvfrom();
+int osetreuid();
+int osetregid();
+int otruncate();
+int oftruncate();
+int ogetpeername();
+int ogethostid();
+int osethostid();
+int ogetrlimit();
+int osetrlimit();
+int okillpg();
+int oquota();
+int ogetsockname();
+#ifdef NFS
+#else
+#endif
+int ogetdirentries();
+#ifdef NFS
+#else
+#endif
+#ifdef SYSVSHM
+#else
+#endif
+#ifdef LFS
+#else
+#endif
+
+#else /* COMPAT_43 */
+#define compat(n, name) 0, nosys
+#endif /* COMPAT_43 */
+
+struct sysent sysent[] = {
+ { 0, nosys }, /* 0 = syscall */
+ { 1, exit }, /* 1 = exit */
+ { 0, fork }, /* 2 = fork */
+ { 3, read }, /* 3 = read */
+ { 3, write }, /* 4 = write */
+ { 3, open }, /* 5 = open */
+ { 1, close }, /* 6 = close */
+ { 4, wait4 }, /* 7 = wait4 */
+ { compat(2,creat) }, /* 8 = old creat */
+ { 2, link }, /* 9 = link */
+ { 1, unlink }, /* 10 = unlink */
+ { 0, nosys }, /* 11 = obsolete execv */
+ { 1, chdir }, /* 12 = chdir */
+ { 1, fchdir }, /* 13 = fchdir */
+ { 3, mknod }, /* 14 = mknod */
+ { 2, chmod }, /* 15 = chmod */
+ { 3, chown }, /* 16 = chown */
+ { 1, obreak }, /* 17 = break */
+ { 3, getfsstat }, /* 18 = getfsstat */
+ { compat(3,lseek) }, /* 19 = old lseek */
+ { 0, getpid }, /* 20 = getpid */
+ { 4, mount }, /* 21 = mount */
+ { 2, unmount }, /* 22 = unmount */
+ { 1, setuid }, /* 23 = setuid */
+ { 0, getuid }, /* 24 = getuid */
+ { 0, geteuid }, /* 25 = geteuid */
+ { 4, ptrace }, /* 26 = ptrace */
+ { 3, recvmsg }, /* 27 = recvmsg */
+ { 3, sendmsg }, /* 28 = sendmsg */
+ { 6, recvfrom }, /* 29 = recvfrom */
+ { 3, accept }, /* 30 = accept */
+ { 3, getpeername }, /* 31 = getpeername */
+ { 3, getsockname }, /* 32 = getsockname */
+ { 2, access }, /* 33 = access */
+ { 2, chflags }, /* 34 = chflags */
+ { 2, fchflags }, /* 35 = fchflags */
+ { 0, sync }, /* 36 = sync */
+ { 2, kill }, /* 37 = kill */
+ { compat(2,stat) }, /* 38 = old stat */
+ { 0, getppid }, /* 39 = getppid */
+ { compat(2,lstat) }, /* 40 = old lstat */
+ { 2, dup }, /* 41 = dup */
+ { 0, pipe }, /* 42 = pipe */
+ { 0, getegid }, /* 43 = getegid */
+ { 4, profil }, /* 44 = profil */
+#ifdef KTRACE
+ { 4, ktrace }, /* 45 = ktrace */
+#else
+ { 0, nosys }, /* 45 = ktrace */
+#endif
+ { 3, sigaction }, /* 46 = sigaction */
+ { 0, getgid }, /* 47 = getgid */
+ { 2, sigprocmask }, /* 48 = sigprocmask */
+ { 2, getlogin }, /* 49 = getlogin */
+ { 1, setlogin }, /* 50 = setlogin */
+ { 1, acct }, /* 51 = acct */
+ { 0, sigpending }, /* 52 = sigpending */
+ { 2, sigaltstack }, /* 53 = sigaltstack */
+ { 3, ioctl }, /* 54 = ioctl */
+ { 1, reboot }, /* 55 = reboot */
+ { 1, revoke }, /* 56 = revoke */
+ { 2, symlink }, /* 57 = symlink */
+ { 3, readlink }, /* 58 = readlink */
+ { 3, execve }, /* 59 = execve */
+ { 1, umask }, /* 60 = umask */
+ { 1, chroot }, /* 61 = chroot */
+ { compat(2,fstat) }, /* 62 = old fstat */
+ { compat(4,getkerninfo) }, /* 63 = old getkerninfo */
+ { compat(0,getpagesize) }, /* 64 = old getpagesize */
+ { 2, msync }, /* 65 = msync */
+ { 0, vfork }, /* 66 = vfork */
+ { 0, nosys }, /* 67 = obsolete vread */
+ { 0, nosys }, /* 68 = obsolete vwrite */
+ { 1, sbrk }, /* 69 = sbrk */
+ { 1, sstk }, /* 70 = sstk */
+ { compat(6,mmap) }, /* 71 = old mmap */
+ { 1, ovadvise }, /* 72 = vadvise */
+ { 2, munmap }, /* 73 = munmap */
+ { 3, mprotect }, /* 74 = mprotect */
+ { 3, madvise }, /* 75 = madvise */
+ { 0, nosys }, /* 76 = obsolete vhangup */
+ { 0, nosys }, /* 77 = obsolete vlimit */
+ { 3, mincore }, /* 78 = mincore */
+ { 2, getgroups }, /* 79 = getgroups */
+ { 2, setgroups }, /* 80 = setgroups */
+ { 0, getpgrp }, /* 81 = getpgrp */
+ { 2, setpgid }, /* 82 = setpgid */
+ { 3, setitimer }, /* 83 = setitimer */
+ { compat(0,wait) }, /* 84 = old wait */
+ { 1, swapon }, /* 85 = swapon */
+ { 2, getitimer }, /* 86 = getitimer */
+ { compat(2,gethostname) }, /* 87 = old gethostname */
+ { compat(2,sethostname) }, /* 88 = old sethostname */
+ { 0, getdtablesize }, /* 89 = getdtablesize */
+ { 2, dup2 }, /* 90 = dup2 */
+ { 0, nosys }, /* 91 = getdopt */
+ { 3, fcntl }, /* 92 = fcntl */
+ { 5, select }, /* 93 = select */
+ { 0, nosys }, /* 94 = setdopt */
+ { 1, fsync }, /* 95 = fsync */
+ { 3, setpriority }, /* 96 = setpriority */
+ { 3, socket }, /* 97 = socket */
+ { 3, connect }, /* 98 = connect */
+ { compat(3,accept) }, /* 99 = old accept */
+ { 2, getpriority }, /* 100 = getpriority */
+ { compat(4,send) }, /* 101 = old send */
+ { compat(4,recv) }, /* 102 = old recv */
+ { 1, sigreturn }, /* 103 = sigreturn */
+ { 3, bind }, /* 104 = bind */
+ { 5, setsockopt }, /* 105 = setsockopt */
+ { 2, listen }, /* 106 = listen */
+ { 0, nosys }, /* 107 = obsolete vtimes */
+ { compat(3,sigvec) }, /* 108 = old sigvec */
+ { compat(1,sigblock) }, /* 109 = old sigblock */
+ { compat(1,sigsetmask) }, /* 110 = old sigsetmask */
+ { 1, sigsuspend }, /* 111 = sigsuspend */
+ { compat(2,sigstack) }, /* 112 = old sigstack */
+ { compat(3,recvmsg) }, /* 113 = old recvmsg */
+ { compat(3,sendmsg) }, /* 114 = old sendmsg */
+#ifdef TRACE
+ { 2, vtrace }, /* 115 = vtrace */
+#else
+ { 0, nosys }, /* 115 = obsolete vtrace */
+#endif
+ { 2, gettimeofday }, /* 116 = gettimeofday */
+ { 2, getrusage }, /* 117 = getrusage */
+ { 5, getsockopt }, /* 118 = getsockopt */
+#ifdef vax
+ { 1, resuba }, /* 119 = resuba */
+#else
+ { 0, nosys }, /* 119 = nosys */
+#endif
+ { 3, readv }, /* 120 = readv */
+ { 3, writev }, /* 121 = writev */
+ { 2, settimeofday }, /* 122 = settimeofday */
+ { 3, fchown }, /* 123 = fchown */
+ { 2, fchmod }, /* 124 = fchmod */
+ { compat(6,recvfrom) }, /* 125 = old recvfrom */
+ { compat(2,setreuid) }, /* 126 = old setreuid */
+ { compat(2,setregid) }, /* 127 = old setregid */
+ { 2, rename }, /* 128 = rename */
+ { compat(2,truncate) }, /* 129 = old truncate */
+ { compat(2,ftruncate) }, /* 130 = old ftruncate */
+ { 2, flock }, /* 131 = flock */
+ { 2, mkfifo }, /* 132 = mkfifo */
+ { 6, sendto }, /* 133 = sendto */
+ { 2, shutdown }, /* 134 = shutdown */
+ { 5, socketpair }, /* 135 = socketpair */
+ { 2, mkdir }, /* 136 = mkdir */
+ { 1, rmdir }, /* 137 = rmdir */
+ { 2, utimes }, /* 138 = utimes */
+ { 0, nosys }, /* 139 = obsolete 4.2 sigreturn */
+ { 2, adjtime }, /* 140 = adjtime */
+ { compat(3,getpeername) }, /* 141 = old getpeername */
+ { compat(0,gethostid) }, /* 142 = old gethostid */
+ { compat(1,sethostid) }, /* 143 = old sethostid */
+ { compat(2,getrlimit) }, /* 144 = old getrlimit */
+ { compat(2,setrlimit) }, /* 145 = old setrlimit */
+ { compat(2,killpg) }, /* 146 = old killpg */
+ { 0, setsid }, /* 147 = setsid */
+ { 4, quotactl }, /* 148 = quotactl */
+ { compat(4,quota) }, /* 149 = old quota */
+ { compat(3,getsockname) }, /* 150 = old getsockname */
+ { 0, nosys }, /* 151 = nosys */
+ { 0, nosys }, /* 152 = nosys */
+ { 0, nosys }, /* 153 = nosys */
+ { 0, nosys }, /* 154 = nosys */
+#ifdef NFS
+ { 2, nfssvc }, /* 155 = nfssvc */
+#else
+ { 0, nosys }, /* 155 = nosys */
+#endif
+ { compat(4,getdirentries) }, /* 156 = old getdirentries */
+ { 2, statfs }, /* 157 = statfs */
+ { 2, fstatfs }, /* 158 = fstatfs */
+ { 0, nosys }, /* 159 = nosys */
+ { 0, nosys }, /* 160 = nosys */
+#ifdef NFS
+ { 2, getfh }, /* 161 = getfh */
+#else
+ { 0, nosys }, /* 161 = nosys */
+#endif
+ { 2, getdomainname }, /* 162 = getdomainname */
+ { 2, setdomainname }, /* 163 = setdomainname */
+ { 1, uname }, /* 164 = uname */
+ { 2, sysarch }, /* 165 = sysarch */
+ { 0, nosys }, /* 166 = nosys */
+ { 0, nosys }, /* 167 = nosys */
+ { 0, nosys }, /* 168 = nosys */
+ { 0, nosys }, /* 169 = nosys */
+ { 0, nosys }, /* 170 = nosys */
+#ifdef SYSVSHM
+ { 4, shmsys }, /* 171 = shmsys */
+#else
+ { 0, nosys }, /* 171 = nosys */
+#endif
+ { 0, nosys }, /* 172 = nosys */
+ { 0, nosys }, /* 173 = nosys */
+ { 0, nosys }, /* 174 = nosys */
+ { 0, nosys }, /* 175 = nosys */
+ { 0, nosys }, /* 176 = nosys */
+ { 0, nosys }, /* 177 = nosys */
+ { 0, nosys }, /* 178 = nosys */
+ { 0, nosys }, /* 179 = nosys */
+ { 0, nosys }, /* 180 = nosys */
+ { 1, setgid }, /* 181 = setgid */
+ { 1, setegid }, /* 182 = setegid */
+ { 1, seteuid }, /* 183 = seteuid */
+#ifdef LFS
+ { 3, lfs_bmapv }, /* 184 = lfs_bmapv */
+ { 3, lfs_markv }, /* 185 = lfs_markv */
+ { 2, lfs_segclean }, /* 186 = lfs_segclean */
+ { 2, lfs_segwait }, /* 187 = lfs_segwait */
+#else
+ { 0, nosys }, /* 184 = nosys */
+ { 0, nosys }, /* 185 = nosys */
+ { 0, nosys }, /* 186 = nosys */
+ { 0, nosys }, /* 187 = nosys */
+#endif
+ { 2, stat }, /* 188 = stat */
+ { 2, fstat }, /* 189 = fstat */
+ { 2, lstat }, /* 190 = lstat */
+ { 2, pathconf }, /* 191 = pathconf */
+ { 2, fpathconf }, /* 192 = fpathconf */
+ { 0, nosys }, /* 193 = nosys */
+ { 2, getrlimit }, /* 194 = getrlimit */
+ { 2, setrlimit }, /* 195 = setrlimit */
+ { 4, getdirentries }, /* 196 = getdirentries */
+ { 8, mmap }, /* 197 = mmap */
+ { 0, nosys }, /* 198 = __syscall */
+ { 5, lseek }, /* 199 = lseek */
+ { 4, truncate }, /* 200 = truncate */
+ { 4, ftruncate }, /* 201 = ftruncate */
+ { 6, __sysctl }, /* 202 = __sysctl */
+ { 2, mlock }, /* 203 = mlock */
+ { 2, munlock }, /* 204 = munlock */
+ { 0, nosys }, /* 205 = nosys */
+ { 0, nosys }, /* 206 = nosys */
+ { 0, nosys }, /* 207 = nosys */
+ { 0, nosys }, /* 208 = nosys */
+ { 0, nosys }, /* 209 = nosys */
+ { 0, nosys }, /* 210 = nosys */
+};
+
+int nsysent = sizeof(sysent) / sizeof(sysent[0]);
diff --git a/sys/kern/kern_acct.c b/sys/kern/kern_acct.c
new file mode 100644
index 0000000..3cbde51
--- /dev/null
+++ b/sys/kern/kern_acct.c
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)kern_acct.c 8.1 (Berkeley) 6/14/93
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/syslog.h>
+#include <sys/kernel.h>
+
+struct acct_args {
+ char *fname;
+};
+int
+acct(a1, a2, a3)
+ struct proc *a1;
+ struct acct_args *a2;
+ int *a3;
+{
+ /*
+ * Body deleted.
+ */
+ return (ENOSYS);
+}
+
+void
+acct_process(a1)
+ struct proc *a1;
+{
+
+ /*
+ * Body deleted.
+ */
+ return;
+}
+
+/*
+ * Periodically check the file system to see if accounting
+ * should be turned on or off.
+ */
+
+/*
+ * Values associated with enabling and disabling accounting
+ */
+int acctsuspend = 2; /* stop accounting when < 2% free space left */
+int acctresume = 4; /* resume when free space risen to > 4% */
+int acctchkfreq = 15; /* frequency (in seconds) to check space */
+
+/*
+ * SHOULD REPLACE THIS WITH A DRIVER THAT CAN BE READ TO SIMPLIFY.
+ */
+struct vnode *acctp;
+struct vnode *savacctp;
+
+/* ARGSUSED */
+void
+acctwatch(a)
+ void *a;
+{
+ struct statfs sb;
+
+ if (savacctp) {
+ (void)VFS_STATFS(savacctp->v_mount, &sb, (struct proc *)0);
+ if (sb.f_bavail > acctresume * sb.f_blocks / 100) {
+ acctp = savacctp;
+ savacctp = NULL;
+ log(LOG_NOTICE, "Accounting resumed\n");
+ }
+ } else {
+ if (acctp == NULL)
+ return;
+ (void)VFS_STATFS(acctp->v_mount, &sb, (struct proc *)0);
+ if (sb.f_bavail <= acctsuspend * sb.f_blocks / 100) {
+ savacctp = acctp;
+ acctp = NULL;
+ log(LOG_NOTICE, "Accounting suspended\n");
+ }
+ }
+ timeout(acctwatch, NULL, acctchkfreq * hz);
+}
diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c
new file mode 100644
index 0000000..4017e96
--- /dev/null
+++ b/sys/kern/kern_clock.c
@@ -0,0 +1,529 @@
+/*-
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_clock.c 8.5 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/dkstat.h>
+#include <sys/callout.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/resourcevar.h>
+
+#include <machine/cpu.h>
+
+#ifdef GPROF
+#include <sys/gmon.h>
+#endif
+
+/*
+ * Clock handling routines.
+ *
+ * This code is written to operate with two timers that run independently of
+ * each other. The main clock, running hz times per second, is used to keep
+ * track of real time. The second timer handles kernel and user profiling,
+ * and does resource use estimation. If the second timer is programmable,
+ * it is randomized to avoid aliasing between the two clocks. For example,
+ * the randomization prevents an adversary from always giving up the cpu
+ * just before its quantum expires. Otherwise, it would never accumulate
+ * cpu ticks. The mean frequency of the second timer is stathz.
+ *
+ * If no second timer exists, stathz will be zero; in this case we drive
+ * profiling and statistics off the main clock. This WILL NOT be accurate;
+ * do not do it unless absolutely necessary.
+ *
+ * The statistics clock may (or may not) be run at a higher rate while
+ * profiling. This profile clock runs at profhz. We require that profhz
+ * be an integral multiple of stathz.
+ *
+ * If the statistics clock is running fast, it must be divided by the ratio
+ * profhz/stathz for statistics. (For profiling, every tick counts.)
+ */
+
+/*
+ * TODO:
+ * allocate more timeout table slots when table overflows.
+ */
+
+/*
+ * Bump a timeval by a small number of usec's.
+ */
+#define BUMPTIME(t, usec) { \
+ register volatile struct timeval *tp = (t); \
+ register long us; \
+ \
+ tp->tv_usec = us = tp->tv_usec + (usec); \
+ if (us >= 1000000) { \
+ tp->tv_usec = us - 1000000; \
+ tp->tv_sec++; \
+ } \
+}
+
+int stathz;
+int profhz;
+int profprocs;
+int ticks;
+static int psdiv, pscnt; /* prof => stat divider */
+int psratio; /* ratio: prof / stat */
+
+volatile struct timeval time;
+volatile struct timeval mono_time;
+
+/*
+ * Initialize clock frequencies and start both clocks running.
+ */
+void
+initclocks()
+{
+ register int i;
+
+ /*
+ * Set divisors to 1 (normal case) and let the machine-specific
+ * code do its bit.
+ */
+ psdiv = pscnt = 1;
+ cpu_initclocks();
+
+ /*
+ * Compute profhz/stathz, and fix profhz if needed.
+ */
+ i = stathz ? stathz : hz;
+ if (profhz == 0)
+ profhz = i;
+ psratio = profhz / i;
+}
+
+/*
+ * The real-time timer, interrupting hz times per second.
+ */
+void
+hardclock(frame)
+ register struct clockframe *frame;
+{
+ register struct callout *p1;
+ register struct proc *p;
+ register int delta, needsoft;
+ extern int tickdelta;
+ extern long timedelta;
+
+ /*
+ * Update real-time timeout queue.
+ * At front of queue are some number of events which are ``due''.
+ * The time to these is <= 0 and if negative represents the
+ * number of ticks which have passed since it was supposed to happen.
+ * The rest of the q elements (times > 0) are events yet to happen,
+ * where the time for each is given as a delta from the previous.
+ * Decrementing just the first of these serves to decrement the time
+ * to all events.
+ */
+ needsoft = 0;
+ for (p1 = calltodo.c_next; p1 != NULL; p1 = p1->c_next) {
+ if (--p1->c_time > 0)
+ break;
+ needsoft = 1;
+ if (p1->c_time == 0)
+ break;
+ }
+
+ p = curproc;
+ if (p) {
+ register struct pstats *pstats;
+
+ /*
+ * Run current process's virtual and profile time, as needed.
+ */
+ pstats = p->p_stats;
+ if (CLKF_USERMODE(frame) &&
+ timerisset(&pstats->p_timer[ITIMER_VIRTUAL].it_value) &&
+ itimerdecr(&pstats->p_timer[ITIMER_VIRTUAL], tick) == 0)
+ psignal(p, SIGVTALRM);
+ if (timerisset(&pstats->p_timer[ITIMER_PROF].it_value) &&
+ itimerdecr(&pstats->p_timer[ITIMER_PROF], tick) == 0)
+ psignal(p, SIGPROF);
+ }
+
+ /*
+ * If no separate statistics clock is available, run it from here.
+ */
+ if (stathz == 0)
+ statclock(frame);
+
+ /*
+ * Increment the time-of-day. The increment is just ``tick'' unless
+ * we are still adjusting the clock; see adjtime().
+ */
+ ticks++;
+ if (timedelta == 0)
+ delta = tick;
+ else {
+ delta = tick + tickdelta;
+ timedelta -= tickdelta;
+ }
+ BUMPTIME(&time, delta);
+ BUMPTIME(&mono_time, delta);
+
+ /*
+ * Process callouts at a very low cpu priority, so we don't keep the
+ * relatively high clock interrupt priority any longer than necessary.
+ */
+ if (needsoft) {
+ if (CLKF_BASEPRI(frame)) {
+ /*
+ * Save the overhead of a software interrupt;
+ * it will happen as soon as we return, so do it now.
+ */
+ (void)splsoftclock();
+ softclock();
+ } else
+ setsoftclock();
+ }
+}
+
+/*
+ * Software (low priority) clock interrupt.
+ * Run periodic events from timeout queue.
+ */
+/*ARGSUSED*/
+void
+softclock()
+{
+ register struct callout *c;
+ register void *arg;
+ register void (*func) __P((void *));
+ register int s;
+
+ s = splhigh();
+ while ((c = calltodo.c_next) != NULL && c->c_time <= 0) {
+ func = c->c_func;
+ arg = c->c_arg;
+ calltodo.c_next = c->c_next;
+ c->c_next = callfree;
+ callfree = c;
+ splx(s);
+ (*func)(arg);
+ (void) splhigh();
+ }
+ splx(s);
+}
+
+/*
+ * timeout --
+ * Execute a function after a specified length of time.
+ *
+ * untimeout --
+ * Cancel previous timeout function call.
+ *
+ * See AT&T BCI Driver Reference Manual for specification. This
+ * implementation differs from that one in that no identification
+ * value is returned from timeout, rather, the original arguments
+ * to timeout are used to identify entries for untimeout.
+ */
+void
+timeout(ftn, arg, ticks)
+ void (*ftn) __P((void *));
+ void *arg;
+ register int ticks;
+{
+ register struct callout *new, *p, *t;
+ register int s;
+
+ if (ticks <= 0)
+ ticks = 1;
+
+ /* Lock out the clock. */
+ s = splhigh();
+
+ /* Fill in the next free callout structure. */
+ if (callfree == NULL)
+ panic("timeout table full");
+ new = callfree;
+ callfree = new->c_next;
+ new->c_arg = arg;
+ new->c_func = ftn;
+
+ /*
+ * The time for each event is stored as a difference from the time
+ * of the previous event on the queue. Walk the queue, correcting
+ * the ticks argument for queue entries passed. Correct the ticks
+ * value for the queue entry immediately after the insertion point
+ * as well. Watch out for negative c_time values; these represent
+ * overdue events.
+ */
+ for (p = &calltodo;
+ (t = p->c_next) != NULL && ticks > t->c_time; p = t)
+ if (t->c_time > 0)
+ ticks -= t->c_time;
+ new->c_time = ticks;
+ if (t != NULL)
+ t->c_time -= ticks;
+
+ /* Insert the new entry into the queue. */
+ p->c_next = new;
+ new->c_next = t;
+ splx(s);
+}
+
+void
+untimeout(ftn, arg)
+ void (*ftn) __P((void *));
+ void *arg;
+{
+ register struct callout *p, *t;
+ register int s;
+
+ s = splhigh();
+ for (p = &calltodo; (t = p->c_next) != NULL; p = t)
+ if (t->c_func == ftn && t->c_arg == arg) {
+ /* Increment next entry's tick count. */
+ if (t->c_next && t->c_time > 0)
+ t->c_next->c_time += t->c_time;
+
+ /* Move entry from callout queue to callfree queue. */
+ p->c_next = t->c_next;
+ t->c_next = callfree;
+ callfree = t;
+ break;
+ }
+ splx(s);
+}
+
+/*
+ * Compute number of hz until specified time. Used to
+ * compute third argument to timeout() from an absolute time.
+ */
+int
+hzto(tv)
+ struct timeval *tv;
+{
+ register long ticks, sec;
+ int s;
+
+ /*
+ * If number of milliseconds will fit in 32 bit arithmetic,
+ * then compute number of milliseconds to time and scale to
+ * ticks. Otherwise just compute number of hz in time, rounding
+ * times greater than representible to maximum value.
+ *
+ * Delta times less than 25 days can be computed ``exactly''.
+ * Maximum value for any timeout in 10ms ticks is 250 days.
+ */
+ s = splhigh();
+ sec = tv->tv_sec - time.tv_sec;
+ if (sec <= 0x7fffffff / 1000 - 1000)
+ ticks = ((tv->tv_sec - time.tv_sec) * 1000 +
+ (tv->tv_usec - time.tv_usec) / 1000) / (tick / 1000);
+ else if (sec <= 0x7fffffff / hz)
+ ticks = sec * hz;
+ else
+ ticks = 0x7fffffff;
+ splx(s);
+ return (ticks);
+}
+
+/*
+ * Start profiling on a process.
+ *
+ * Kernel profiling passes proc0 which never exits and hence
+ * keeps the profile clock running constantly.
+ */
+void
+startprofclock(p)
+ register struct proc *p;
+{
+ int s;
+
+ if ((p->p_flag & P_PROFIL) == 0) {
+ p->p_flag |= P_PROFIL;
+ if (++profprocs == 1 && stathz != 0) {
+ s = splstatclock();
+ psdiv = pscnt = psratio;
+ setstatclockrate(profhz);
+ splx(s);
+ }
+ }
+}
+
+/*
+ * Stop profiling on a process.
+ */
+void
+stopprofclock(p)
+ register struct proc *p;
+{
+ int s;
+
+ if (p->p_flag & P_PROFIL) {
+ p->p_flag &= ~P_PROFIL;
+ if (--profprocs == 0 && stathz != 0) {
+ s = splstatclock();
+ psdiv = pscnt = 1;
+ setstatclockrate(stathz);
+ splx(s);
+ }
+ }
+}
+
+int dk_ndrive = DK_NDRIVE;
+
+/*
+ * Statistics clock. Grab profile sample, and if divider reaches 0,
+ * do process and kernel statistics.
+ */
+void
+statclock(frame)
+ register struct clockframe *frame;
+{
+#ifdef GPROF
+ register struct gmonparam *g;
+#endif
+ register struct proc *p;
+ register int i;
+
+ if (CLKF_USERMODE(frame)) {
+ p = curproc;
+ if (p->p_flag & P_PROFIL)
+ addupc_intr(p, CLKF_PC(frame), 1);
+ if (--pscnt > 0)
+ return;
+ /*
+ * Came from user mode; CPU was in user state.
+ * If this process is being profiled record the tick.
+ */
+ p->p_uticks++;
+ if (p->p_nice > NZERO)
+ cp_time[CP_NICE]++;
+ else
+ cp_time[CP_USER]++;
+ } else {
+#ifdef GPROF
+ /*
+ * Kernel statistics are just like addupc_intr, only easier.
+ */
+ g = &_gmonparam;
+ if (g->state == GMON_PROF_ON) {
+ i = CLKF_PC(frame) - g->lowpc;
+ if (i < g->textsize) {
+ i /= HISTFRACTION * sizeof(*g->kcount);
+ g->kcount[i]++;
+ }
+ }
+#endif
+ if (--pscnt > 0)
+ return;
+ /*
+ * Came from kernel mode, so we were:
+ * - handling an interrupt,
+ * - doing syscall or trap work on behalf of the current
+ * user process, or
+ * - spinning in the idle loop.
+ * Whichever it is, charge the time as appropriate.
+ * Note that we charge interrupts to the current process,
+ * regardless of whether they are ``for'' that process,
+ * so that we know how much of its real time was spent
+ * in ``non-process'' (i.e., interrupt) work.
+ */
+ p = curproc;
+ if (CLKF_INTR(frame)) {
+ if (p != NULL)
+ p->p_iticks++;
+ cp_time[CP_INTR]++;
+ } else if (p != NULL) {
+ p->p_sticks++;
+ cp_time[CP_SYS]++;
+ } else
+ cp_time[CP_IDLE]++;
+ }
+ pscnt = psdiv;
+
+ /*
+ * We maintain statistics shown by user-level statistics
+ * programs: the amount of time in each cpu state, and
+ * the amount of time each of DK_NDRIVE ``drives'' is busy.
+ *
+ * XXX should either run linked list of drives, or (better)
+ * grab timestamps in the start & done code.
+ */
+ for (i = 0; i < DK_NDRIVE; i++)
+ if (dk_busy & (1 << i))
+ dk_time[i]++;
+
+ /*
+ * We adjust the priority of the current process. The priority of
+ * a process gets worse as it accumulates CPU time. The cpu usage
+ * estimator (p_estcpu) is increased here. The formula for computing
+ * priorities (in kern_synch.c) will compute a different value each
+ * time p_estcpu increases by 4. The cpu usage estimator ramps up
+ * quite quickly when the process is running (linearly), and decays
+ * away exponentially, at a rate which is proportionally slower when
+ * the system is busy. The basic principal is that the system will
+ * 90% forget that the process used a lot of CPU time in 5 * loadav
+ * seconds. This causes the system to favor processes which haven't
+ * run much recently, and to round-robin among other processes.
+ */
+ if (p != NULL) {
+ p->p_cpticks++;
+ if (++p->p_estcpu == 0)
+ p->p_estcpu--;
+ if ((p->p_estcpu & 3) == 0) {
+ resetpriority(p);
+ if (p->p_priority >= PUSER)
+ p->p_priority = p->p_usrpri;
+ }
+ }
+}
+
+/*
+ * Return information about system clocks.
+ */
+int
+sysctl_clockrate(where, sizep)
+ register char *where;
+ size_t *sizep;
+{
+ struct clockinfo clkinfo;
+
+ /*
+ * Construct clockinfo structure.
+ */
+ clkinfo.hz = hz;
+ clkinfo.tick = tick;
+ clkinfo.profhz = profhz;
+ clkinfo.stathz = stathz ? stathz : hz;
+ return (sysctl_rdstruct(where, sizep, NULL, &clkinfo, sizeof(clkinfo)));
+}
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
new file mode 100644
index 0000000..9258e81
--- /dev/null
+++ b/sys/kern/kern_descrip.c
@@ -0,0 +1,959 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_descrip.c 8.6 (Berkeley) 4/19/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/filedesc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/malloc.h>
+#include <sys/syslog.h>
+#include <sys/unistd.h>
+#include <sys/resourcevar.h>
+
+/*
+ * Descriptor management.
+ */
+struct file *filehead; /* head of list of open files */
+int nfiles; /* actual number of open files */
+
+/*
+ * System calls on descriptors.
+ */
+struct getdtablesize_args {
+ int dummy;
+};
+/* ARGSUSED */
+int
+getdtablesize(p, uap, retval)
+ struct proc *p;
+ struct getdtablesize_args *uap;
+ int *retval;
+{
+
+ *retval = min((int)p->p_rlimit[RLIMIT_NOFILE].rlim_cur, maxfiles);
+ return (0);
+}
+
+/*
+ * Duplicate a file descriptor.
+ */
+struct dup_args {
+ u_int fd;
+};
+/* ARGSUSED */
+int
+dup(p, uap, retval)
+ struct proc *p;
+ struct dup_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp;
+ u_int old;
+ int new, error;
+
+ old = uap->fd;
+ /*
+ * XXX Compatibility
+ */
+ if (old &~ 077) { uap->fd &= 077; return (dup2(p, uap, retval)); }
+
+ fdp = p->p_fd;
+ if (old >= fdp->fd_nfiles || fdp->fd_ofiles[old] == NULL)
+ return (EBADF);
+ if (error = fdalloc(p, 0, &new))
+ return (error);
+ return (finishdup(fdp, (int)old, new, retval));
+}
+
+/*
+ * Duplicate a file descriptor to a particular value.
+ */
+struct dup2_args {
+ u_int from;
+ u_int to;
+};
+/* ARGSUSED */
+int
+dup2(p, uap, retval)
+ struct proc *p;
+ struct dup2_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ register u_int old = uap->from, new = uap->to;
+ int i, error;
+
+ if (old >= fdp->fd_nfiles ||
+ fdp->fd_ofiles[old] == NULL ||
+ new >= p->p_rlimit[RLIMIT_NOFILE].rlim_cur ||
+ new >= maxfiles)
+ return (EBADF);
+ if (old == new) {
+ *retval = new;
+ return (0);
+ }
+ if (new >= fdp->fd_nfiles) {
+ if (error = fdalloc(p, new, &i))
+ return (error);
+ if (new != i)
+ panic("dup2: fdalloc");
+ } else if (fdp->fd_ofiles[new]) {
+ if (fdp->fd_ofileflags[new] & UF_MAPPED)
+ (void) munmapfd(p, new);
+ /*
+ * dup2() must succeed even if the close has an error.
+ */
+ (void) closef(fdp->fd_ofiles[new], p);
+ }
+ return (finishdup(fdp, (int)old, (int)new, retval));
+}
+
+/*
+ * The file control system call.
+ */
+struct fcntl_args {
+ int fd;
+ int cmd;
+ int arg;
+};
+/* ARGSUSED */
+int
+fcntl(p, uap, retval)
+ struct proc *p;
+ register struct fcntl_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ register struct file *fp;
+ register char *pop;
+ struct vnode *vp;
+ int i, tmp, error, flg = F_POSIX;
+ struct flock fl;
+ u_int newmin;
+
+ if ((unsigned)uap->fd >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL)
+ return (EBADF);
+ pop = &fdp->fd_ofileflags[uap->fd];
+ switch (uap->cmd) {
+
+ case F_DUPFD:
+ newmin = uap->arg;
+ if (newmin >= p->p_rlimit[RLIMIT_NOFILE].rlim_cur ||
+ newmin >= maxfiles)
+ return (EINVAL);
+ if (error = fdalloc(p, newmin, &i))
+ return (error);
+ return (finishdup(fdp, uap->fd, i, retval));
+
+ case F_GETFD:
+ *retval = *pop & 1;
+ return (0);
+
+ case F_SETFD:
+ *pop = (*pop &~ 1) | (uap->arg & 1);
+ return (0);
+
+ case F_GETFL:
+ *retval = OFLAGS(fp->f_flag);
+ return (0);
+
+ case F_SETFL:
+ fp->f_flag &= ~FCNTLFLAGS;
+ fp->f_flag |= FFLAGS(uap->arg) & FCNTLFLAGS;
+ tmp = fp->f_flag & FNONBLOCK;
+ error = (*fp->f_ops->fo_ioctl)(fp, FIONBIO, (caddr_t)&tmp, p);
+ if (error)
+ return (error);
+ tmp = fp->f_flag & FASYNC;
+ error = (*fp->f_ops->fo_ioctl)(fp, FIOASYNC, (caddr_t)&tmp, p);
+ if (!error)
+ return (0);
+ fp->f_flag &= ~FNONBLOCK;
+ tmp = 0;
+ (void) (*fp->f_ops->fo_ioctl)(fp, FIONBIO, (caddr_t)&tmp, p);
+ return (error);
+
+ case F_GETOWN:
+ if (fp->f_type == DTYPE_SOCKET) {
+ *retval = ((struct socket *)fp->f_data)->so_pgid;
+ return (0);
+ }
+ error = (*fp->f_ops->fo_ioctl)
+ (fp, (int)TIOCGPGRP, (caddr_t)retval, p);
+ *retval = -*retval;
+ return (error);
+
+ case F_SETOWN:
+ if (fp->f_type == DTYPE_SOCKET) {
+ ((struct socket *)fp->f_data)->so_pgid = uap->arg;
+ return (0);
+ }
+ if (uap->arg <= 0) {
+ uap->arg = -uap->arg;
+ } else {
+ struct proc *p1 = pfind(uap->arg);
+ if (p1 == 0)
+ return (ESRCH);
+ uap->arg = p1->p_pgrp->pg_id;
+ }
+ return ((*fp->f_ops->fo_ioctl)
+ (fp, (int)TIOCSPGRP, (caddr_t)&uap->arg, p));
+
+ case F_SETLKW:
+ flg |= F_WAIT;
+ /* Fall into F_SETLK */
+
+ case F_SETLK:
+ if (fp->f_type != DTYPE_VNODE)
+ return (EBADF);
+ vp = (struct vnode *)fp->f_data;
+ /* Copy in the lock structure */
+ error = copyin((caddr_t)uap->arg, (caddr_t)&fl, sizeof (fl));
+ if (error)
+ return (error);
+ if (fl.l_whence == SEEK_CUR)
+ fl.l_start += fp->f_offset;
+ switch (fl.l_type) {
+
+ case F_RDLCK:
+ if ((fp->f_flag & FREAD) == 0)
+ return (EBADF);
+ p->p_flag |= P_ADVLOCK;
+ return (VOP_ADVLOCK(vp, (caddr_t)p, F_SETLK, &fl, flg));
+
+ case F_WRLCK:
+ if ((fp->f_flag & FWRITE) == 0)
+ return (EBADF);
+ p->p_flag |= P_ADVLOCK;
+ return (VOP_ADVLOCK(vp, (caddr_t)p, F_SETLK, &fl, flg));
+
+ case F_UNLCK:
+ return (VOP_ADVLOCK(vp, (caddr_t)p, F_UNLCK, &fl,
+ F_POSIX));
+
+ default:
+ return (EINVAL);
+ }
+
+ case F_GETLK:
+ if (fp->f_type != DTYPE_VNODE)
+ return (EBADF);
+ vp = (struct vnode *)fp->f_data;
+ /* Copy in the lock structure */
+ error = copyin((caddr_t)uap->arg, (caddr_t)&fl, sizeof (fl));
+ if (error)
+ return (error);
+ if (fl.l_whence == SEEK_CUR)
+ fl.l_start += fp->f_offset;
+ if (error = VOP_ADVLOCK(vp, (caddr_t)p, F_GETLK, &fl, F_POSIX))
+ return (error);
+ return (copyout((caddr_t)&fl, (caddr_t)uap->arg, sizeof (fl)));
+
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Common code for dup, dup2, and fcntl(F_DUPFD).
+ */
+int
+finishdup(fdp, old, new, retval)
+ register struct filedesc *fdp;
+ register int old, new, *retval;
+{
+ register struct file *fp;
+
+ fp = fdp->fd_ofiles[old];
+ fdp->fd_ofiles[new] = fp;
+ fdp->fd_ofileflags[new] = fdp->fd_ofileflags[old] &~ UF_EXCLOSE;
+ fp->f_count++;
+ if (new > fdp->fd_lastfile)
+ fdp->fd_lastfile = new;
+ *retval = new;
+ return (0);
+}
+
+/*
+ * Close a file descriptor.
+ */
+struct close_args {
+ int fd;
+};
+/* ARGSUSED */
+int
+close(p, uap, retval)
+ struct proc *p;
+ struct close_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ register struct file *fp;
+ register int fd = uap->fd;
+ register u_char *pf;
+
+ if ((unsigned)fd >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[fd]) == NULL)
+ return (EBADF);
+ pf = (u_char *)&fdp->fd_ofileflags[fd];
+ if (*pf & UF_MAPPED)
+ (void) munmapfd(p, fd);
+ fdp->fd_ofiles[fd] = NULL;
+ while (fdp->fd_lastfile > 0 && fdp->fd_ofiles[fdp->fd_lastfile] == NULL)
+ fdp->fd_lastfile--;
+ if (fd < fdp->fd_freefile)
+ fdp->fd_freefile = fd;
+ *pf = 0;
+ return (closef(fp, p));
+}
+
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+/*
+ * Return status information about a file descriptor.
+ */
+struct ofstat_args {
+ int fd;
+ struct ostat *sb;
+};
+/* ARGSUSED */
+int
+ofstat(p, uap, retval)
+ struct proc *p;
+ register struct ofstat_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ register struct file *fp;
+ struct stat ub;
+ struct ostat oub;
+ int error;
+
+ if ((unsigned)uap->fd >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL)
+ return (EBADF);
+ switch (fp->f_type) {
+
+ case DTYPE_VNODE:
+ error = vn_stat((struct vnode *)fp->f_data, &ub, p);
+ break;
+
+ case DTYPE_SOCKET:
+ error = soo_stat((struct socket *)fp->f_data, &ub);
+ break;
+
+ default:
+ panic("ofstat");
+ /*NOTREACHED*/
+ }
+ cvtstat(&ub, &oub);
+ if (error == 0)
+ error = copyout((caddr_t)&oub, (caddr_t)uap->sb, sizeof (oub));
+ return (error);
+}
+#endif /* COMPAT_43 || COMPAT_SUNOS */
+
+/*
+ * Return status information about a file descriptor.
+ */
+struct fstat_args {
+ int fd;
+ struct stat *sb;
+};
+/* ARGSUSED */
+int
+fstat(p, uap, retval)
+ struct proc *p;
+ register struct fstat_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ register struct file *fp;
+ struct stat ub;
+ int error;
+
+ if ((unsigned)uap->fd >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL)
+ return (EBADF);
+ switch (fp->f_type) {
+
+ case DTYPE_VNODE:
+ error = vn_stat((struct vnode *)fp->f_data, &ub, p);
+ break;
+
+ case DTYPE_SOCKET:
+ error = soo_stat((struct socket *)fp->f_data, &ub);
+ break;
+
+ default:
+ panic("fstat");
+ /*NOTREACHED*/
+ }
+ if (error == 0)
+ error = copyout((caddr_t)&ub, (caddr_t)uap->sb, sizeof (ub));
+ return (error);
+}
+
+/*
+ * Return pathconf information about a file descriptor.
+ */
+struct fpathconf_args {
+ int fd;
+ int name;
+};
+/* ARGSUSED */
+int
+fpathconf(p, uap, retval)
+ struct proc *p;
+ register struct fpathconf_args *uap;
+ int *retval;
+{
+ struct filedesc *fdp = p->p_fd;
+ struct file *fp;
+ struct vnode *vp;
+
+ if ((unsigned)uap->fd >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL)
+ return (EBADF);
+ switch (fp->f_type) {
+
+ case DTYPE_SOCKET:
+ if (uap->name != _PC_PIPE_BUF)
+ return (EINVAL);
+ *retval = PIPE_BUF;
+ return (0);
+
+ case DTYPE_VNODE:
+ vp = (struct vnode *)fp->f_data;
+ return (VOP_PATHCONF(vp, uap->name, retval));
+
+ default:
+ panic("fpathconf");
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Allocate a file descriptor for the process.
+ */
+int fdexpand;
+
+int
+fdalloc(p, want, result)
+ struct proc *p;
+ int want;
+ int *result;
+{
+ register struct filedesc *fdp = p->p_fd;
+ register int i;
+ int lim, last, nfiles;
+ struct file **newofile;
+ char *newofileflags;
+
+ /*
+ * Search for a free descriptor starting at the higher
+ * of want or fd_freefile. If that fails, consider
+ * expanding the ofile array.
+ */
+ lim = min((int)p->p_rlimit[RLIMIT_NOFILE].rlim_cur, maxfiles);
+ for (;;) {
+ last = min(fdp->fd_nfiles, lim);
+ if ((i = want) < fdp->fd_freefile)
+ i = fdp->fd_freefile;
+ for (; i < last; i++) {
+ if (fdp->fd_ofiles[i] == NULL) {
+ fdp->fd_ofileflags[i] = 0;
+ if (i > fdp->fd_lastfile)
+ fdp->fd_lastfile = i;
+ if (want <= fdp->fd_freefile)
+ fdp->fd_freefile = i;
+ *result = i;
+ return (0);
+ }
+ }
+
+ /*
+ * No space in current array. Expand?
+ */
+ if (fdp->fd_nfiles >= lim)
+ return (EMFILE);
+ if (fdp->fd_nfiles < NDEXTENT)
+ nfiles = NDEXTENT;
+ else
+ nfiles = 2 * fdp->fd_nfiles;
+ MALLOC(newofile, struct file **, nfiles * OFILESIZE,
+ M_FILEDESC, M_WAITOK);
+ newofileflags = (char *) &newofile[nfiles];
+ /*
+ * Copy the existing ofile and ofileflags arrays
+ * and zero the new portion of each array.
+ */
+ bcopy(fdp->fd_ofiles, newofile,
+ (i = sizeof(struct file *) * fdp->fd_nfiles));
+ bzero((char *)newofile + i, nfiles * sizeof(struct file *) - i);
+ bcopy(fdp->fd_ofileflags, newofileflags,
+ (i = sizeof(char) * fdp->fd_nfiles));
+ bzero(newofileflags + i, nfiles * sizeof(char) - i);
+ if (fdp->fd_nfiles > NDFILE)
+ FREE(fdp->fd_ofiles, M_FILEDESC);
+ fdp->fd_ofiles = newofile;
+ fdp->fd_ofileflags = newofileflags;
+ fdp->fd_nfiles = nfiles;
+ fdexpand++;
+ }
+ return (0);
+}
+
+/*
+ * Check to see whether n user file descriptors
+ * are available to the process p.
+ */
+int
+fdavail(p, n)
+ struct proc *p;
+ register int n;
+{
+ register struct filedesc *fdp = p->p_fd;
+ register struct file **fpp;
+ register int i, lim;
+
+ lim = min((int)p->p_rlimit[RLIMIT_NOFILE].rlim_cur, maxfiles);
+ if ((i = lim - fdp->fd_nfiles) > 0 && (n -= i) <= 0)
+ return (1);
+ fpp = &fdp->fd_ofiles[fdp->fd_freefile];
+ for (i = fdp->fd_nfiles - fdp->fd_freefile; --i >= 0; fpp++)
+ if (*fpp == NULL && --n <= 0)
+ return (1);
+ return (0);
+}
+
+/*
+ * Create a new open file structure and allocate
+ * a file decriptor for the process that refers to it.
+ */
+int
+falloc(p, resultfp, resultfd)
+ register struct proc *p;
+ struct file **resultfp;
+ int *resultfd;
+{
+ register struct file *fp, *fq, **fpp;
+ int error, i;
+
+ if (error = fdalloc(p, 0, &i))
+ return (error);
+ if (nfiles >= maxfiles) {
+ tablefull("file");
+ return (ENFILE);
+ }
+ /*
+ * Allocate a new file descriptor.
+ * If the process has file descriptor zero open, add to the list
+ * of open files at that point, otherwise put it at the front of
+ * the list of open files.
+ */
+ nfiles++;
+ MALLOC(fp, struct file *, sizeof(struct file), M_FILE, M_WAITOK);
+ bzero(fp, sizeof(struct file));
+ if (fq = p->p_fd->fd_ofiles[0])
+ fpp = &fq->f_filef;
+ else
+ fpp = &filehead;
+ p->p_fd->fd_ofiles[i] = fp;
+ if (fq = *fpp)
+ fq->f_fileb = &fp->f_filef;
+ fp->f_filef = fq;
+ fp->f_fileb = fpp;
+ *fpp = fp;
+ fp->f_count = 1;
+ fp->f_cred = p->p_ucred;
+ crhold(fp->f_cred);
+ if (resultfp)
+ *resultfp = fp;
+ if (resultfd)
+ *resultfd = i;
+ return (0);
+}
+
+/*
+ * Free a file descriptor.
+ */
+void
+ffree(fp)
+ register struct file *fp;
+{
+ register struct file *fq;
+
+ if (fq = fp->f_filef)
+ fq->f_fileb = fp->f_fileb;
+ *fp->f_fileb = fq;
+ crfree(fp->f_cred);
+#ifdef DIAGNOSTIC
+ fp->f_filef = NULL;
+ fp->f_fileb = NULL;
+ fp->f_count = 0;
+#endif
+ nfiles--;
+ FREE(fp, M_FILE);
+}
+
+/*
+ * Copy a filedesc structure.
+ */
+struct filedesc *
+fdcopy(p)
+ struct proc *p;
+{
+ register struct filedesc *newfdp, *fdp = p->p_fd;
+ register struct file **fpp;
+ register int i;
+
+ MALLOC(newfdp, struct filedesc *, sizeof(struct filedesc0),
+ M_FILEDESC, M_WAITOK);
+ bcopy(fdp, newfdp, sizeof(struct filedesc));
+ VREF(newfdp->fd_cdir);
+ if (newfdp->fd_rdir)
+ VREF(newfdp->fd_rdir);
+ newfdp->fd_refcnt = 1;
+
+ /*
+ * If the number of open files fits in the internal arrays
+ * of the open file structure, use them, otherwise allocate
+ * additional memory for the number of descriptors currently
+ * in use.
+ */
+ if (newfdp->fd_lastfile < NDFILE) {
+ newfdp->fd_ofiles = ((struct filedesc0 *) newfdp)->fd_dfiles;
+ newfdp->fd_ofileflags =
+ ((struct filedesc0 *) newfdp)->fd_dfileflags;
+ i = NDFILE;
+ } else {
+ /*
+ * Compute the smallest multiple of NDEXTENT needed
+ * for the file descriptors currently in use,
+ * allowing the table to shrink.
+ */
+ i = newfdp->fd_nfiles;
+ while (i > 2 * NDEXTENT && i > newfdp->fd_lastfile * 2)
+ i /= 2;
+ MALLOC(newfdp->fd_ofiles, struct file **, i * OFILESIZE,
+ M_FILEDESC, M_WAITOK);
+ newfdp->fd_ofileflags = (char *) &newfdp->fd_ofiles[i];
+ }
+ newfdp->fd_nfiles = i;
+ bcopy(fdp->fd_ofiles, newfdp->fd_ofiles, i * sizeof(struct file **));
+ bcopy(fdp->fd_ofileflags, newfdp->fd_ofileflags, i * sizeof(char));
+ fpp = newfdp->fd_ofiles;
+ for (i = newfdp->fd_lastfile; i-- >= 0; fpp++)
+ if (*fpp != NULL)
+ (*fpp)->f_count++;
+ return (newfdp);
+}
+
+/*
+ * Release a filedesc structure.
+ */
+void
+fdfree(p)
+ struct proc *p;
+{
+ register struct filedesc *fdp = p->p_fd;
+ struct file **fpp;
+ register int i;
+
+ if (--fdp->fd_refcnt > 0)
+ return;
+ fpp = fdp->fd_ofiles;
+ for (i = fdp->fd_lastfile; i-- >= 0; fpp++)
+ if (*fpp)
+ (void) closef(*fpp, p);
+ if (fdp->fd_nfiles > NDFILE)
+ FREE(fdp->fd_ofiles, M_FILEDESC);
+ vrele(fdp->fd_cdir);
+ if (fdp->fd_rdir)
+ vrele(fdp->fd_rdir);
+ FREE(fdp, M_FILEDESC);
+}
+
+/*
+ * Close any files on exec?
+ */
+void
+fdcloseexec(p)
+ struct proc *p;
+{
+ struct filedesc *fdp = p->p_fd;
+ struct file **fpp;
+ char *fdfp;
+ register int i;
+
+ fpp = fdp->fd_ofiles;
+ fdfp = fdp->fd_ofileflags;
+ for (i = 0; i <= fdp->fd_lastfile; i++, fpp++, fdfp++)
+ if (*fpp != NULL && (*fdfp & UF_EXCLOSE)) {
+ if (*fdfp & UF_MAPPED)
+ (void) munmapfd(i);
+ (void) closef(*fpp, p);
+ *fpp = NULL;
+ *fdfp = 0;
+ if (i < fdp->fd_freefile)
+ fdp->fd_freefile = i;
+ }
+ while (fdp->fd_lastfile > 0 && fdp->fd_ofiles[fdp->fd_lastfile] == NULL)
+ fdp->fd_lastfile--;
+}
+
+/*
+ * Internal form of close.
+ * Decrement reference count on file structure.
+ * Note: p may be NULL when closing a file
+ * that was being passed in a message.
+ */
+int
+closef(fp, p)
+ register struct file *fp;
+ register struct proc *p;
+{
+ struct vnode *vp;
+ struct flock lf;
+ int error;
+
+ if (fp == NULL)
+ return (0);
+ /*
+ * POSIX record locking dictates that any close releases ALL
+ * locks owned by this process. This is handled by setting
+ * a flag in the unlock to free ONLY locks obeying POSIX
+ * semantics, and not to free BSD-style file locks.
+ * If the descriptor was in a message, POSIX-style locks
+ * aren't passed with the descriptor.
+ */
+ if (p && (p->p_flag & P_ADVLOCK) && fp->f_type == DTYPE_VNODE) {
+ lf.l_whence = SEEK_SET;
+ lf.l_start = 0;
+ lf.l_len = 0;
+ lf.l_type = F_UNLCK;
+ vp = (struct vnode *)fp->f_data;
+ (void) VOP_ADVLOCK(vp, (caddr_t)p, F_UNLCK, &lf, F_POSIX);
+ }
+ if (--fp->f_count > 0)
+ return (0);
+ if (fp->f_count < 0)
+ panic("closef: count < 0");
+ if ((fp->f_flag & FHASLOCK) && fp->f_type == DTYPE_VNODE) {
+ lf.l_whence = SEEK_SET;
+ lf.l_start = 0;
+ lf.l_len = 0;
+ lf.l_type = F_UNLCK;
+ vp = (struct vnode *)fp->f_data;
+ (void) VOP_ADVLOCK(vp, (caddr_t)fp, F_UNLCK, &lf, F_FLOCK);
+ }
+ if (fp->f_ops)
+ error = (*fp->f_ops->fo_close)(fp, p);
+ else
+ error = 0;
+ ffree(fp);
+ return (error);
+}
+
+/*
+ * Apply an advisory lock on a file descriptor.
+ *
+ * Just attempt to get a record lock of the requested type on
+ * the entire file (l_whence = SEEK_SET, l_start = 0, l_len = 0).
+ */
+struct flock_args {
+ int fd;
+ int how;
+};
+/* ARGSUSED */
+int
+flock(p, uap, retval)
+ struct proc *p;
+ register struct flock_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ register struct file *fp;
+ struct vnode *vp;
+ struct flock lf;
+
+ if ((unsigned)uap->fd >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL)
+ return (EBADF);
+ if (fp->f_type != DTYPE_VNODE)
+ return (EOPNOTSUPP);
+ vp = (struct vnode *)fp->f_data;
+ lf.l_whence = SEEK_SET;
+ lf.l_start = 0;
+ lf.l_len = 0;
+ if (uap->how & LOCK_UN) {
+ lf.l_type = F_UNLCK;
+ fp->f_flag &= ~FHASLOCK;
+ return (VOP_ADVLOCK(vp, (caddr_t)fp, F_UNLCK, &lf, F_FLOCK));
+ }
+ if (uap->how & LOCK_EX)
+ lf.l_type = F_WRLCK;
+ else if (uap->how & LOCK_SH)
+ lf.l_type = F_RDLCK;
+ else
+ return (EBADF);
+ fp->f_flag |= FHASLOCK;
+ if (uap->how & LOCK_NB)
+ return (VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, F_FLOCK));
+ return (VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, F_FLOCK|F_WAIT));
+}
+
+/*
+ * File Descriptor pseudo-device driver (/dev/fd/).
+ *
+ * Opening minor device N dup()s the file (if any) connected to file
+ * descriptor N belonging to the calling process. Note that this driver
+ * consists of only the ``open()'' routine, because all subsequent
+ * references to this file will be direct to the other driver.
+ */
+/* ARGSUSED */
+int
+fdopen(dev, mode, type, p)
+ dev_t dev;
+ int mode, type;
+ struct proc *p;
+{
+
+ /*
+ * XXX Kludge: set curproc->p_dupfd to contain the value of the
+ * the file descriptor being sought for duplication. The error
+ * return ensures that the vnode for this device will be released
+ * by vn_open. Open will detect this special error and take the
+ * actions in dupfdopen below. Other callers of vn_open or VOP_OPEN
+ * will simply report the error.
+ */
+ p->p_dupfd = minor(dev);
+ return (ENODEV);
+}
+
+/*
+ * Duplicate the specified descriptor to a free descriptor.
+ */
+int
+dupfdopen(fdp, indx, dfd, mode, error)
+ register struct filedesc *fdp;
+ register int indx, dfd;
+ int mode;
+ int error;
+{
+ register struct file *wfp;
+ struct file *fp;
+
+ /*
+ * If the to-be-dup'd fd number is greater than the allowed number
+ * of file descriptors, or the fd to be dup'd has already been
+ * closed, reject. Note, check for new == old is necessary as
+ * falloc could allocate an already closed to-be-dup'd descriptor
+ * as the new descriptor.
+ */
+ fp = fdp->fd_ofiles[indx];
+ if ((u_int)dfd >= fdp->fd_nfiles ||
+ (wfp = fdp->fd_ofiles[dfd]) == NULL || fp == wfp)
+ return (EBADF);
+
+ /*
+ * There are two cases of interest here.
+ *
+ * For ENODEV simply dup (dfd) to file descriptor
+ * (indx) and return.
+ *
+ * For ENXIO steal away the file structure from (dfd) and
+ * store it in (indx). (dfd) is effectively closed by
+ * this operation.
+ *
+ * Any other error code is just returned.
+ */
+ switch (error) {
+ case ENODEV:
+ /*
+ * Check that the mode the file is being opened for is a
+ * subset of the mode of the existing descriptor.
+ */
+ if (((mode & (FREAD|FWRITE)) | wfp->f_flag) != wfp->f_flag)
+ return (EACCES);
+ fdp->fd_ofiles[indx] = wfp;
+ fdp->fd_ofileflags[indx] = fdp->fd_ofileflags[dfd];
+ wfp->f_count++;
+ if (indx > fdp->fd_lastfile)
+ fdp->fd_lastfile = indx;
+ return (0);
+
+ case ENXIO:
+ /*
+ * Steal away the file pointer from dfd, and stuff it into indx.
+ */
+ fdp->fd_ofiles[indx] = fdp->fd_ofiles[dfd];
+ fdp->fd_ofiles[dfd] = NULL;
+ fdp->fd_ofileflags[indx] = fdp->fd_ofileflags[dfd];
+ fdp->fd_ofileflags[dfd] = 0;
+ /*
+ * Complete the clean up of the filedesc structure by
+ * recomputing the various hints.
+ */
+ if (indx > fdp->fd_lastfile)
+ fdp->fd_lastfile = indx;
+ else
+ while (fdp->fd_lastfile > 0 &&
+ fdp->fd_ofiles[fdp->fd_lastfile] == NULL)
+ fdp->fd_lastfile--;
+ if (dfd < fdp->fd_freefile)
+ fdp->fd_freefile = dfd;
+ return (0);
+
+ default:
+ return (error);
+ }
+ /* NOTREACHED */
+}
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c
new file mode 100644
index 0000000..6717e4e
--- /dev/null
+++ b/sys/kern/kern_exec.c
@@ -0,0 +1,529 @@
+/*
+ * Copyright (c) 1993, David Greenman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by David Greenman
+ * 4. The name of the developer may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: kern_execve.c,v 1.20 1994/03/26 12:24:27 davidg Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/signalvar.h>
+#include <sys/resourcevar.h>
+#include <sys/kernel.h>
+#include <sys/mount.h>
+#include <sys/file.h>
+#include <sys/acct.h>
+#include <sys/exec.h>
+#include <sys/imgact.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/malloc.h>
+#include <sys/syslog.h>
+
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+
+#include <machine/reg.h>
+
+int exec_extract_strings __P((struct image_params *));
+int *exec_copyout_strings __P((struct image_params *));
+
+/*
+ * execsw_set is constructed for us by the linker. Each of the items
+ * is a pointer to a `const struct execsw', hence the double pointer here.
+ */
+extern const struct linker_set execsw_set;
+const struct execsw **execsw = (const struct execsw **)&execsw_set.ls_items[0];
+
+/*
+ * execve() system call.
+ */
+int
+execve(p, uap, retval)
+ struct proc *p;
+ register struct execve_args *uap;
+ int *retval;
+{
+ struct nameidata nd, *ndp;
+ char *stringbase, *stringp;
+ int *stack_base;
+ int error, resid, len, i;
+ struct image_params image_params, *iparams;
+ struct vnode *vnodep;
+ struct vattr attr;
+ char *image_header;
+
+ iparams = &image_params;
+ bzero((caddr_t)iparams, sizeof(struct image_params));
+ image_header = (char *)0;
+
+ /*
+ * Initialize a few constants in the common area
+ */
+ iparams->proc = p;
+ iparams->uap = uap;
+ iparams->attr = &attr;
+
+ /*
+ * Allocate temporary demand zeroed space for argument and
+ * environment strings
+ */
+ error = vm_allocate(kernel_map, (vm_offset_t *)&iparams->stringbase,
+ ARG_MAX, TRUE);
+ if (error) {
+ log(LOG_WARNING, "execve: failed to allocate string space\n");
+ return (error);
+ }
+
+ if (!iparams->stringbase) {
+ error = ENOMEM;
+ goto exec_fail;
+ }
+ iparams->stringp = iparams->stringbase;
+ iparams->stringspace = ARG_MAX;
+
+ /*
+ * Translate the file name. namei() returns a vnode pointer
+ * in ni_vp amoung other things.
+ */
+ ndp = &nd;
+ ndp->ni_cnd.cn_nameiop = LOOKUP;
+ ndp->ni_cnd.cn_flags = LOCKLEAF | FOLLOW | SAVENAME;
+ ndp->ni_cnd.cn_proc = curproc;
+ ndp->ni_cnd.cn_cred = curproc->p_cred->pc_ucred;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->fname;
+
+interpret:
+
+ error = namei(ndp);
+ if (error) {
+ vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase,
+ ARG_MAX);
+ goto exec_fail;
+ }
+
+ iparams->vnodep = vnodep = ndp->ni_vp;
+
+ if (vnodep == NULL) {
+ error = ENOEXEC;
+ goto exec_fail_dealloc;
+ }
+
+ /*
+ * Check file permissions (also 'opens' file)
+ */
+ error = exec_check_permissions(iparams);
+ if (error)
+ goto exec_fail_dealloc;
+
+ /*
+ * Map the image header (first page) of the file into
+ * kernel address space
+ */
+ error = vm_mmap(kernel_map, /* map */
+ (vm_offset_t *)&image_header, /* address */
+ PAGE_SIZE, /* size */
+ VM_PROT_READ, /* protection */
+ VM_PROT_READ, /* max protection */
+ 0, /* flags */
+ (caddr_t)vnodep, /* vnode */
+ 0); /* offset */
+ if (error) {
+ uprintf("mmap failed: %d\n",error);
+ goto exec_fail_dealloc;
+ }
+ iparams->image_header = image_header;
+
+ /*
+ * Loop through list of image activators, calling each one.
+ * If there is no match, the activator returns -1. If there
+ * is a match, but there was an error during the activation,
+ * the error is returned. Otherwise 0 means success. If the
+ * image is interpreted, loop back up and try activating
+ * the interpreter.
+ */
+ for (i = 0; execsw[i]; ++i) {
+ if (execsw[i]->ex_imgact)
+ error = (*execsw[i]->ex_imgact)(iparams);
+ else
+ continue;
+
+ if (error == -1)
+ continue;
+ if (error)
+ goto exec_fail_dealloc;
+ if (iparams->interpreted) {
+ /* free old vnode and name buffer */
+ vput(ndp->ni_vp);
+ FREE(ndp->ni_cnd.cn_pnbuf, M_NAMEI);
+ if (vm_deallocate(kernel_map,
+ (vm_offset_t)image_header, PAGE_SIZE))
+ panic("execve: header dealloc failed (1)");
+
+ /* set new name to that of the interpreter */
+ ndp->ni_segflg = UIO_SYSSPACE;
+ ndp->ni_dirp = iparams->interpreter_name;
+ ndp->ni_cnd.cn_nameiop = LOOKUP;
+ ndp->ni_cnd.cn_flags = LOCKLEAF | FOLLOW | SAVENAME;
+ ndp->ni_cnd.cn_proc = curproc;
+ ndp->ni_cnd.cn_cred = curproc->p_cred->pc_ucred;
+ goto interpret;
+ }
+ break;
+ }
+ /* If we made it through all the activators and none matched, exit. */
+ if (error == -1) {
+ error = ENOEXEC;
+ goto exec_fail_dealloc;
+ }
+
+ /*
+ * Copy out strings (args and env) and initialize stack base
+ */
+ stack_base = exec_copyout_strings(iparams);
+ p->p_vmspace->vm_minsaddr = (char *)stack_base;
+
+ /*
+ * Stuff argument count as first item on stack
+ */
+ *(--stack_base) = iparams->argc;
+
+ /* close files on exec */
+ fdcloseexec(p);
+
+ /* reset caught signals */
+ execsigs(p);
+
+ /* name this process - nameiexec(p, ndp) */
+ len = min(ndp->ni_cnd.cn_namelen,MAXCOMLEN);
+ bcopy(ndp->ni_cnd.cn_nameptr, p->p_comm, len);
+ p->p_comm[len] = 0;
+
+ /*
+ * mark as executable, wakeup any process that was vforked and tell
+ * it that it now has it's own resources back
+ */
+ p->p_flag |= P_EXEC;
+ if (p->p_pptr && (p->p_flag & P_PPWAIT)) {
+ p->p_flag &= ~P_PPWAIT;
+ wakeup((caddr_t)p->p_pptr);
+ }
+
+ /* implement set userid/groupid */
+ p->p_flag &= ~P_SUGID;
+
+ /*
+ * Turn off kernel tracing for set-id programs, except for
+ * root.
+ */
+ if (p->p_tracep && (attr.va_mode & (VSUID | VSGID)) &&
+ suser(p->p_ucred, &p->p_acflag)) {
+ p->p_traceflag = 0;
+ vrele(p->p_tracep);
+ p->p_tracep = 0;
+ }
+ if ((attr.va_mode & VSUID) && (p->p_flag & P_TRACED) == 0) {
+ p->p_ucred = crcopy(p->p_ucred);
+ p->p_ucred->cr_uid = attr.va_uid;
+ p->p_flag |= P_SUGID;
+ }
+ if ((attr.va_mode & VSGID) && (p->p_flag & P_TRACED) == 0) {
+ p->p_ucred = crcopy(p->p_ucred);
+ p->p_ucred->cr_groups[0] = attr.va_gid;
+ p->p_flag |= P_SUGID;
+ }
+
+ /*
+ * Implement correct POSIX saved uid behavior.
+ */
+ p->p_cred->p_svuid = p->p_ucred->cr_uid;
+ p->p_cred->p_svgid = p->p_ucred->cr_gid;
+
+ /* mark vnode pure text */
+ ndp->ni_vp->v_flag |= VTEXT;
+
+ /*
+ * If tracing the process, trap to debugger so breakpoints
+ * can be set before the program executes.
+ */
+ if (p->p_flag & P_TRACED)
+ psignal(p, SIGTRAP);
+
+ /* clear "fork but no exec" flag, as we _are_ execing */
+ p->p_acflag &= ~AFORK;
+
+ /* Set entry address */
+ setregs(p, iparams->entry_addr, stack_base);
+
+ /*
+ * free various allocated resources
+ */
+ if (vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase, ARG_MAX))
+ panic("execve: string buffer dealloc failed (1)");
+ if (vm_deallocate(kernel_map, (vm_offset_t)image_header, PAGE_SIZE))
+ panic("execve: header dealloc failed (2)");
+ vput(ndp->ni_vp);
+ FREE(ndp->ni_cnd.cn_pnbuf, M_NAMEI);
+
+ return (0);
+
+exec_fail_dealloc:
+ if (iparams->stringbase && iparams->stringbase != (char *)-1)
+ if (vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase,
+ ARG_MAX))
+ panic("execve: string buffer dealloc failed (2)");
+ if (iparams->image_header && iparams->image_header != (char *)-1)
+ if (vm_deallocate(kernel_map,
+ (vm_offset_t)iparams->image_header, PAGE_SIZE))
+ panic("execve: header dealloc failed (3)");
+ vput(ndp->ni_vp);
+ FREE(ndp->ni_cnd.cn_pnbuf, M_NAMEI);
+
+exec_fail:
+ if (iparams->vmspace_destroyed) {
+ /* sorry, no more process anymore. exit gracefully */
+#if 0 /* XXX */
+ vm_deallocate(&vs->vm_map, USRSTACK - MAXSSIZ, MAXSSIZ);
+#endif
+ exit1(p, W_EXITCODE(0, SIGABRT));
+ /* NOT REACHED */
+ return(0);
+ } else {
+ return(error);
+ }
+}
+
+/*
+ * Destroy old address space, and allocate a new stack
+ * The new stack is only SGROWSIZ large because it is grown
+ * automatically in trap.c.
+ */
+int
+exec_new_vmspace(iparams)
+ struct image_params *iparams;
+{
+ int error;
+ struct vmspace *vmspace = iparams->proc->p_vmspace;
+ caddr_t stack_addr = (caddr_t) (USRSTACK - SGROWSIZ);
+
+ iparams->vmspace_destroyed = 1;
+
+ /* Blow away entire process VM */
+ vm_deallocate(&vmspace->vm_map, 0, USRSTACK);
+
+ /* Allocate a new stack */
+ error = vm_allocate(&vmspace->vm_map, (vm_offset_t *)&stack_addr,
+ SGROWSIZ, FALSE);
+ if (error)
+ return(error);
+
+ vmspace->vm_ssize = SGROWSIZ >> PAGE_SHIFT;
+
+ /* Initialize maximum stack address */
+ vmspace->vm_maxsaddr = (char *)USRSTACK - MAXSSIZ;
+
+ return(0);
+}
+
+/*
+ * Copy out argument and environment strings from the old process
+ * address space into the temporary string buffer.
+ */
+int
+exec_extract_strings(iparams)
+ struct image_params *iparams;
+{
+ char **argv, **envv;
+ char *argp, *envp;
+ int length;
+
+ /*
+ * extract arguments first
+ */
+
+ argv = iparams->uap->argv;
+
+ if (argv)
+ while (argp = (caddr_t) fuword(argv++)) {
+ if (argp == (caddr_t) -1)
+ return (EFAULT);
+ if (copyinstr(argp, iparams->stringp, iparams->stringspace,
+ &length) == ENAMETOOLONG)
+ return(E2BIG);
+ iparams->stringspace -= length;
+ iparams->stringp += length;
+ iparams->argc++;
+ }
+
+ /*
+ * extract environment strings
+ */
+
+ envv = iparams->uap->envv;
+
+ if (envv)
+ while (envp = (caddr_t) fuword(envv++)) {
+ if (envp == (caddr_t) -1)
+ return (EFAULT);
+ if (copyinstr(envp, iparams->stringp, iparams->stringspace,
+ &length) == ENAMETOOLONG)
+ return(E2BIG);
+ iparams->stringspace -= length;
+ iparams->stringp += length;
+ iparams->envc++;
+ }
+
+ return (0);
+}
+
+/*
+ * Copy strings out to the new process address space, constructing
+ * new arg and env vector tables. Return a pointer to the base
+ * so that it can be used as the initial stack pointer.
+ */
+int *
+exec_copyout_strings(iparams)
+ struct image_params *iparams;
+{
+ int argc, envc;
+ char **vectp;
+ char *stringp, *destp;
+ int *stack_base;
+ int vect_table_size, string_table_size;
+
+ /*
+ * Calculate string base and vector table pointers.
+ */
+ destp = (caddr_t) ((caddr_t)USRSTACK -
+ roundup((ARG_MAX - iparams->stringspace), sizeof(char *)));
+ /*
+ * The '+ 2' is for the null pointers at the end of each of the
+ * arg and env vector sets
+ */
+ vectp = (char **) (destp -
+ (iparams->argc + iparams->envc + 2) * sizeof(char *));
+
+ /*
+ * vectp also becomes our initial stack base
+ */
+ stack_base = (int *)vectp;
+
+ stringp = iparams->stringbase;
+ argc = iparams->argc;
+ envc = iparams->envc;
+
+ for (; argc > 0; --argc) {
+ *(vectp++) = destp;
+ while (*destp++ = *stringp++);
+ }
+
+ /* a null vector table pointer seperates the argp's from the envp's */
+ *(vectp++) = NULL;
+
+ for (; envc > 0; --envc) {
+ *(vectp++) = destp;
+ while (*destp++ = *stringp++);
+ }
+
+ /* end of vector table is a null pointer */
+ *vectp = NULL;
+
+ return (stack_base);
+}
+
+/*
+ * Check permissions of file to execute.
+ * Return 0 for success or error code on failure.
+ */
+int
+exec_check_permissions(iparams)
+ struct image_params *iparams;
+{
+ struct proc *p = iparams->proc;
+ struct vnode *vnodep = iparams->vnodep;
+ struct vattr *attr = iparams->attr;
+ int error;
+
+ /*
+ * Check number of open-for-writes on the file and deny execution
+ * if there are any.
+ */
+ if (vnodep->v_writecount) {
+ return (ETXTBSY);
+ }
+
+ /* Get file attributes */
+ error = VOP_GETATTR(vnodep, attr, p->p_ucred, p);
+ if (error)
+ return (error);
+
+ /*
+ * 1) Check if file execution is disabled for the filesystem that this
+ * file resides on.
+ * 2) Insure that at least one execute bit is on - otherwise root
+ * will always succeed, and we don't want to happen unless the
+ * file really is executable.
+ * 3) Insure that the file is a regular file.
+ */
+ if ((vnodep->v_mount->mnt_flag & MNT_NOEXEC) ||
+ ((attr->va_mode & 0111) == 0) ||
+ (attr->va_type != VREG)) {
+ return (EACCES);
+ }
+
+ /*
+ * Zero length files can't be exec'd
+ */
+ if (attr->va_size == 0)
+ return (ENOEXEC);
+
+ /*
+ * Disable setuid/setgid if the filesystem prohibits it or if
+ * the process is being traced.
+ */
+ if ((vnodep->v_mount->mnt_flag & MNT_NOSUID) || (p->p_flag & P_TRACED))
+ attr->va_mode &= ~(VSUID | VSGID);
+
+ /*
+ * Check for execute permission to file based on current credentials.
+ * Then call filesystem specific open routine (which does nothing
+ * in the general case).
+ */
+ error = VOP_ACCESS(vnodep, VEXEC, p->p_ucred, p);
+ if (error)
+ return (error);
+
+ error = VOP_OPEN(vnodep, FREAD, p->p_ucred, p);
+ if (error)
+ return (error);
+
+ return (0);
+}
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
new file mode 100644
index 0000000..7db9830
--- /dev/null
+++ b/sys/kern/kern_exit.c
@@ -0,0 +1,495 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_exit.c 8.7 (Berkeley) 2/12/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/map.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+#include <sys/tty.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/kernel.h>
+#include <sys/buf.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/syslog.h>
+#include <sys/malloc.h>
+#include <sys/resourcevar.h>
+#include <sys/ptrace.h>
+
+#include <machine/cpu.h>
+#ifdef COMPAT_43
+#include <machine/reg.h>
+#include <machine/psl.h>
+#endif
+
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+
+__dead void cpu_exit __P((struct proc *));
+__dead void exit1 __P((struct proc *, int));
+
+/*
+ * exit --
+ * Death of process.
+ */
+struct rexit_args {
+ int rval;
+};
+__dead void
+exit(p, uap, retval)
+ struct proc *p;
+ struct rexit_args *uap;
+ int *retval;
+{
+
+ exit1(p, W_EXITCODE(uap->rval, 0));
+ /* NOTREACHED */
+ while (1);
+}
+
+/*
+ * Exit: deallocate address space and other resources, change proc state
+ * to zombie, and unlink proc from allproc and parent's lists. Save exit
+ * status and rusage for wait(). Check for child processes and orphan them.
+ */
+__dead void
+exit1(p, rv)
+ register struct proc *p;
+ int rv;
+{
+ register struct proc *q, *nq;
+ register struct proc **pp;
+ register struct vmspace *vm;
+
+ if (p->p_pid == 1)
+ panic("init died (signal %d, exit %d)",
+ WTERMSIG(rv), WEXITSTATUS(rv));
+#ifdef PGINPROF
+ vmsizmon();
+#endif
+ if (p->p_flag & P_PROFIL)
+ stopprofclock(p);
+ MALLOC(p->p_ru, struct rusage *, sizeof(struct rusage),
+ M_ZOMBIE, M_WAITOK);
+ /*
+ * If parent is waiting for us to exit or exec,
+ * P_PPWAIT is set; we will wakeup the parent below.
+ */
+ p->p_flag &= ~(P_TRACED | P_PPWAIT);
+ p->p_flag |= P_WEXIT;
+ p->p_sigignore = ~0;
+ p->p_siglist = 0;
+ untimeout(realitexpire, (caddr_t)p);
+
+ /*
+ * Close open files and release open-file table.
+ * This may block!
+ */
+ fdfree(p);
+
+ /* The next two chunks should probably be moved to vmspace_exit. */
+ vm = p->p_vmspace;
+#ifdef SYSVSHM
+ if (vm->vm_shm)
+ shmexit(p);
+#endif
+ /*
+ * Release user portion of address space.
+ * This releases references to vnodes,
+ * which could cause I/O if the file has been unlinked.
+ * Need to do this early enough that we can still sleep.
+ * Can't free the entire vmspace as the kernel stack
+ * may be mapped within that space also.
+ */
+ if (vm->vm_refcnt == 1)
+ (void) vm_map_remove(&vm->vm_map, VM_MIN_ADDRESS,
+ VM_MAXUSER_ADDRESS);
+
+ if (SESS_LEADER(p)) {
+ register struct session *sp = p->p_session;
+
+ if (sp->s_ttyvp) {
+ /*
+ * Controlling process.
+ * Signal foreground pgrp,
+ * drain controlling terminal
+ * and revoke access to controlling terminal.
+ */
+ if (sp->s_ttyp->t_session == sp) {
+ if (sp->s_ttyp->t_pgrp)
+ pgsignal(sp->s_ttyp->t_pgrp, SIGHUP, 1);
+ (void) ttywait(sp->s_ttyp);
+ /*
+ * The tty could have been revoked
+ * if we blocked.
+ */
+ if (sp->s_ttyvp)
+ vgoneall(sp->s_ttyvp);
+ }
+ if (sp->s_ttyvp)
+ vrele(sp->s_ttyvp);
+ sp->s_ttyvp = NULL;
+ /*
+ * s_ttyp is not zero'd; we use this to indicate
+ * that the session once had a controlling terminal.
+ * (for logging and informational purposes)
+ */
+ }
+ sp->s_leader = NULL;
+ }
+ fixjobc(p, p->p_pgrp, 0);
+ p->p_rlimit[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
+ (void)acct_process(p);
+#ifdef KTRACE
+ /*
+ * release trace file
+ */
+ p->p_traceflag = 0; /* don't trace the vrele() */
+ if (p->p_tracep)
+ vrele(p->p_tracep);
+#endif
+ /*
+ * Remove proc from allproc queue and pidhash chain.
+ * Place onto zombproc. Unlink from parent's child list.
+ */
+ if (*p->p_prev = p->p_next)
+ p->p_next->p_prev = p->p_prev;
+ if (p->p_next = zombproc)
+ p->p_next->p_prev = &p->p_next;
+ p->p_prev = &zombproc;
+ zombproc = p;
+ p->p_stat = SZOMB;
+
+ for (pp = &pidhash[PIDHASH(p->p_pid)]; *pp; pp = &(*pp)->p_hash)
+ if (*pp == p) {
+ *pp = p->p_hash;
+ goto done;
+ }
+ panic("exit");
+done:
+
+ if (p->p_cptr) /* only need this if any child is S_ZOMB */
+ wakeup((caddr_t) initproc);
+ for (q = p->p_cptr; q != NULL; q = nq) {
+ nq = q->p_osptr;
+ if (nq != NULL)
+ nq->p_ysptr = NULL;
+ if (initproc->p_cptr)
+ initproc->p_cptr->p_ysptr = q;
+ q->p_osptr = initproc->p_cptr;
+ q->p_ysptr = NULL;
+ initproc->p_cptr = q;
+
+ q->p_pptr = initproc;
+ /*
+ * Traced processes are killed
+ * since their existence means someone is screwing up.
+ */
+ if (q->p_flag & P_TRACED) {
+ q->p_flag &= ~P_TRACED;
+ psignal(q, SIGKILL);
+ }
+ }
+ p->p_cptr = NULL;
+
+ /*
+ * Save exit status and final rusage info, adding in child rusage
+ * info and self times.
+ */
+ p->p_xstat = rv;
+ *p->p_ru = p->p_stats->p_ru;
+ calcru(p, &p->p_ru->ru_utime, &p->p_ru->ru_stime, NULL);
+ ruadd(p->p_ru, &p->p_stats->p_cru);
+
+ /*
+ * Notify parent that we're gone.
+ */
+ psignal(p->p_pptr, SIGCHLD);
+ wakeup((caddr_t)p->p_pptr);
+#if defined(tahoe)
+ /* move this to cpu_exit */
+ p->p_addr->u_pcb.pcb_savacc.faddr = (float *)NULL;
+#endif
+ /*
+ * Clear curproc after we've done all operations
+ * that could block, and before tearing down the rest
+ * of the process state that might be used from clock, etc.
+ * Also, can't clear curproc while we're still runnable,
+ * as we're not on a run queue (we are current, just not
+ * a proper proc any longer!).
+ *
+ * Other substructures are freed from wait().
+ */
+ curproc = NULL;
+ if (--p->p_limit->p_refcnt == 0)
+ FREE(p->p_limit, M_SUBPROC);
+
+ /*
+ * Finally, call machine-dependent code to release the remaining
+ * resources including address space, the kernel stack and pcb.
+ * The address space is released by "vmspace_free(p->p_vmspace)";
+ * This is machine-dependent, as we may have to change stacks
+ * or ensure that the current one isn't reallocated before we
+ * finish. cpu_exit will end with a call to cpu_swtch(), finishing
+ * our execution (pun intended).
+ */
+ cpu_exit(p);
+}
+
+struct wait_args {
+ int pid;
+ int *status;
+ int options;
+ struct rusage *rusage;
+#ifdef COMPAT_43
+ int compat; /* pseudo */
+#endif
+};
+
+#ifdef COMPAT_43
+#if defined(hp300) || defined(luna68k)
+#include <machine/frame.h>
+#define GETPS(rp) ((struct frame *)(rp))->f_sr
+#else
+#define GETPS(rp) (rp)[PS]
+#endif
+
+int
+owait(p, uap, retval)
+ struct proc *p;
+ register struct wait_args *uap;
+ int *retval;
+{
+
+#ifdef PSL_ALLCC
+ if ((GETPS(p->p_md.md_regs) & PSL_ALLCC) != PSL_ALLCC) {
+ uap->options = 0;
+ uap->rusage = NULL;
+ } else {
+ uap->options = p->p_md.md_regs[R0];
+ uap->rusage = (struct rusage *)p->p_md.md_regs[R1];
+ }
+#else
+ uap->options = 0;
+ uap->rusage = NULL;
+#endif
+ uap->pid = WAIT_ANY;
+ uap->status = NULL;
+ uap->compat = 1;
+ return (wait1(p, uap, retval));
+}
+
+int
+wait4(p, uap, retval)
+ struct proc *p;
+ struct wait_args *uap;
+ int *retval;
+{
+
+ uap->compat = 0;
+ return (wait1(p, uap, retval));
+}
+#else
+#define wait1 wait4
+#endif
+
+int
+wait1(q, uap, retval)
+ register struct proc *q;
+ register struct wait_args *uap;
+ int retval[];
+{
+ register int nfound;
+ register struct proc *p, *t;
+ int status, error;
+
+ if (uap->pid == 0)
+ uap->pid = -q->p_pgid;
+#ifdef notyet
+ if (uap->options &~ (WUNTRACED|WNOHANG))
+ return (EINVAL);
+#endif
+loop:
+ nfound = 0;
+ for (p = q->p_cptr; p; p = p->p_osptr) {
+ if (uap->pid != WAIT_ANY &&
+ p->p_pid != uap->pid && p->p_pgid != -uap->pid)
+ continue;
+ nfound++;
+ if (p->p_stat == SZOMB) {
+ retval[0] = p->p_pid;
+#ifdef COMPAT_43
+ if (uap->compat)
+ retval[1] = p->p_xstat;
+ else
+#endif
+ if (uap->status) {
+ status = p->p_xstat; /* convert to int */
+ if (error = copyout((caddr_t)&status,
+ (caddr_t)uap->status, sizeof(status)))
+ return (error);
+ }
+ if (uap->rusage && (error = copyout((caddr_t)p->p_ru,
+ (caddr_t)uap->rusage, sizeof (struct rusage))))
+ return (error);
+ /*
+ * If we got the child via a ptrace 'attach',
+ * we need to give it back to the old parent.
+ */
+ if (p->p_oppid && (t = pfind(p->p_oppid))) {
+ p->p_oppid = 0;
+ proc_reparent(p, t);
+ psignal(t, SIGCHLD);
+ wakeup((caddr_t)t);
+ return (0);
+ }
+ p->p_xstat = 0;
+ ruadd(&q->p_stats->p_cru, p->p_ru);
+ FREE(p->p_ru, M_ZOMBIE);
+
+ /*
+ * Decrement the count of procs running with this uid.
+ */
+ (void)chgproccnt(p->p_cred->p_ruid, -1);
+
+ /*
+ * Free up credentials.
+ */
+ if (--p->p_cred->p_refcnt == 0) {
+ crfree(p->p_cred->pc_ucred);
+ FREE(p->p_cred, M_SUBPROC);
+ }
+
+ /*
+ * Release reference to text vnode
+ */
+ if (p->p_textvp)
+ vrele(p->p_textvp);
+
+ /*
+ * Finally finished with old proc entry.
+ * Unlink it from its process group and free it.
+ */
+ leavepgrp(p);
+ if (*p->p_prev = p->p_next) /* off zombproc */
+ p->p_next->p_prev = p->p_prev;
+ if (q = p->p_ysptr)
+ q->p_osptr = p->p_osptr;
+ if (q = p->p_osptr)
+ q->p_ysptr = p->p_ysptr;
+ if ((q = p->p_pptr)->p_cptr == p)
+ q->p_cptr = p->p_osptr;
+
+ /*
+ * Give machine-dependent layer a chance
+ * to free anything that cpu_exit couldn't
+ * release while still running in process context.
+ */
+ cpu_wait(p);
+ FREE(p, M_PROC);
+ nprocs--;
+ return (0);
+ }
+ if (p->p_stat == SSTOP && (p->p_flag & P_WAITED) == 0 &&
+ (p->p_flag & P_TRACED || uap->options & WUNTRACED)) {
+ p->p_flag |= P_WAITED;
+ retval[0] = p->p_pid;
+#ifdef COMPAT_43
+ if (uap->compat) {
+ retval[1] = W_STOPCODE(p->p_xstat);
+ error = 0;
+ } else
+#endif
+ if (uap->status) {
+ status = W_STOPCODE(p->p_xstat);
+ error = copyout((caddr_t)&status,
+ (caddr_t)uap->status, sizeof(status));
+ } else
+ error = 0;
+ return (error);
+ }
+ }
+ if (nfound == 0)
+ return (ECHILD);
+ if (uap->options & WNOHANG) {
+ retval[0] = 0;
+ return (0);
+ }
+ if (error = tsleep((caddr_t)q, PWAIT | PCATCH, "wait", 0))
+ return (error);
+ goto loop;
+}
+
+/*
+ * make process 'parent' the new parent of process 'child'.
+ */
+void
+proc_reparent(child, parent)
+ register struct proc *child;
+ register struct proc *parent;
+{
+ register struct proc *o;
+ register struct proc *y;
+
+ if (child->p_pptr == parent)
+ return;
+
+ /* fix up the child linkage for the old parent */
+ o = child->p_osptr;
+ y = child->p_ysptr;
+ if (y)
+ y->p_osptr = o;
+ if (o)
+ o->p_ysptr = y;
+ if (child->p_pptr->p_cptr == child)
+ child->p_pptr->p_cptr = o;
+
+ /* fix up child linkage for new parent */
+ o = parent->p_cptr;
+ if (o)
+ o->p_ysptr = child;
+ child->p_osptr = o;
+ child->p_ysptr = NULL;
+ parent->p_cptr = child;
+ child->p_pptr = parent;
+}
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
new file mode 100644
index 0000000..c285017
--- /dev/null
+++ b/sys/kern/kern_fork.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_fork.c 8.6 (Berkeley) 4/8/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/map.h>
+#include <sys/filedesc.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/resourcevar.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/acct.h>
+#include <sys/ktrace.h>
+
+struct fork_args {
+ int dummy;
+};
+/* ARGSUSED */
+int
+fork(p, uap, retval)
+ struct proc *p;
+ struct fork_args *uap;
+ int retval[];
+{
+
+ return (fork1(p, 0, retval));
+}
+
+/* ARGSUSED */
+int
+vfork(p, uap, retval)
+ struct proc *p;
+ struct fork_args *uap;
+ int retval[];
+{
+
+ return (fork1(p, 1, retval));
+}
+
+int nprocs = 1; /* process 0 */
+
+int
+fork1(p1, isvfork, retval)
+ register struct proc *p1;
+ int isvfork, retval[];
+{
+ register struct proc *p2;
+ register uid_t uid;
+ struct proc *newproc;
+ struct proc **hash;
+ int count;
+ static int nextpid, pidchecked = 0;
+
+ /*
+ * Although process entries are dynamically created, we still keep
+ * a global limit on the maximum number we will create. Don't allow
+ * a nonprivileged user to use the last process; don't let root
+ * exceed the limit. The variable nprocs is the current number of
+ * processes, maxproc is the limit.
+ */
+ uid = p1->p_cred->p_ruid;
+ if ((nprocs >= maxproc - 1 && uid != 0) || nprocs >= maxproc) {
+ tablefull("proc");
+ return (EAGAIN);
+ }
+ /*
+ * Increment the count of procs running with this uid. Don't allow
+ * a nonprivileged user to exceed their current limit.
+ */
+ count = chgproccnt(uid, 1);
+ if (uid != 0 && count > p1->p_rlimit[RLIMIT_NPROC].rlim_cur) {
+ (void)chgproccnt(uid, -1);
+ return (EAGAIN);
+ }
+
+ /* Allocate new proc. */
+ MALLOC(newproc, struct proc *, sizeof(struct proc), M_PROC, M_WAITOK);
+
+ /*
+ * Find an unused process ID. We remember a range of unused IDs
+ * ready to use (from nextpid+1 through pidchecked-1).
+ */
+ nextpid++;
+retry:
+ /*
+ * If the process ID prototype has wrapped around,
+ * restart somewhat above 0, as the low-numbered procs
+ * tend to include daemons that don't exit.
+ */
+ if (nextpid >= PID_MAX) {
+ nextpid = 100;
+ pidchecked = 0;
+ }
+ if (nextpid >= pidchecked) {
+ int doingzomb = 0;
+
+ pidchecked = PID_MAX;
+ /*
+ * Scan the active and zombie procs to check whether this pid
+ * is in use. Remember the lowest pid that's greater
+ * than nextpid, so we can avoid checking for a while.
+ */
+ p2 = (struct proc *)allproc;
+again:
+ for (; p2 != NULL; p2 = p2->p_next) {
+ while (p2->p_pid == nextpid ||
+ p2->p_pgrp->pg_id == nextpid) {
+ nextpid++;
+ if (nextpid >= pidchecked)
+ goto retry;
+ }
+ if (p2->p_pid > nextpid && pidchecked > p2->p_pid)
+ pidchecked = p2->p_pid;
+ if (p2->p_pgrp->pg_id > nextpid &&
+ pidchecked > p2->p_pgrp->pg_id)
+ pidchecked = p2->p_pgrp->pg_id;
+ }
+ if (!doingzomb) {
+ doingzomb = 1;
+ p2 = zombproc;
+ goto again;
+ }
+ }
+
+
+ /*
+ * Link onto allproc (this should probably be delayed).
+ * Heavy use of volatile here to prevent the compiler from
+ * rearranging code. Yes, it *is* terribly ugly, but at least
+ * it works.
+ */
+ nprocs++;
+ p2 = newproc;
+#define Vp2 ((volatile struct proc *)p2)
+ Vp2->p_stat = SIDL; /* protect against others */
+ Vp2->p_pid = nextpid;
+ /*
+ * This is really:
+ * p2->p_next = allproc;
+ * allproc->p_prev = &p2->p_next;
+ * p2->p_prev = &allproc;
+ * allproc = p2;
+ * The assignment via allproc is legal since it is never NULL.
+ */
+ *(volatile struct proc **)&Vp2->p_next = allproc;
+ *(volatile struct proc ***)&allproc->p_prev =
+ (volatile struct proc **)&Vp2->p_next;
+ *(volatile struct proc ***)&Vp2->p_prev = &allproc;
+ allproc = Vp2;
+#undef Vp2
+ p2->p_forw = p2->p_back = NULL; /* shouldn't be necessary */
+
+ /* Insert on the hash chain. */
+ hash = &pidhash[PIDHASH(p2->p_pid)];
+ p2->p_hash = *hash;
+ *hash = p2;
+
+ /*
+ * Make a proc table entry for the new process.
+ * Start by zeroing the section of proc that is zero-initialized,
+ * then copy the section that is copied directly from the parent.
+ */
+ bzero(&p2->p_startzero,
+ (unsigned) ((caddr_t)&p2->p_endzero - (caddr_t)&p2->p_startzero));
+ bcopy(&p1->p_startcopy, &p2->p_startcopy,
+ (unsigned) ((caddr_t)&p2->p_endcopy - (caddr_t)&p2->p_startcopy));
+
+ /*
+ * Duplicate sub-structures as needed.
+ * Increase reference counts on shared objects.
+ * The p_stats and p_sigacts substructs are set in vm_fork.
+ */
+ p2->p_flag = P_INMEM;
+ if (p1->p_flag & P_PROFIL)
+ startprofclock(p2);
+ MALLOC(p2->p_cred, struct pcred *, sizeof(struct pcred),
+ M_SUBPROC, M_WAITOK);
+ bcopy(p1->p_cred, p2->p_cred, sizeof(*p2->p_cred));
+ p2->p_cred->p_refcnt = 1;
+ crhold(p1->p_ucred);
+
+ /* bump references to the text vnode (for procfs) */
+ p2->p_textvp = p1->p_textvp;
+ if (p2->p_textvp)
+ VREF(p2->p_textvp);
+
+ p2->p_fd = fdcopy(p1);
+ /*
+ * If p_limit is still copy-on-write, bump refcnt,
+ * otherwise get a copy that won't be modified.
+ * (If PL_SHAREMOD is clear, the structure is shared
+ * copy-on-write.)
+ */
+ if (p1->p_limit->p_lflags & PL_SHAREMOD)
+ p2->p_limit = limcopy(p1->p_limit);
+ else {
+ p2->p_limit = p1->p_limit;
+ p2->p_limit->p_refcnt++;
+ }
+
+ if (p1->p_session->s_ttyvp != NULL && p1->p_flag & P_CONTROLT)
+ p2->p_flag |= P_CONTROLT;
+ if (isvfork)
+ p2->p_flag |= P_PPWAIT;
+ p2->p_pgrpnxt = p1->p_pgrpnxt;
+ p1->p_pgrpnxt = p2;
+ p2->p_pptr = p1;
+ p2->p_osptr = p1->p_cptr;
+ if (p1->p_cptr)
+ p1->p_cptr->p_ysptr = p2;
+ p1->p_cptr = p2;
+#ifdef KTRACE
+ /*
+ * Copy traceflag and tracefile if enabled.
+ * If not inherited, these were zeroed above.
+ */
+ if (p1->p_traceflag&KTRFAC_INHERIT) {
+ p2->p_traceflag = p1->p_traceflag;
+ if ((p2->p_tracep = p1->p_tracep) != NULL)
+ VREF(p2->p_tracep);
+ }
+#endif
+
+ /*
+ * This begins the section where we must prevent the parent
+ * from being swapped.
+ */
+ p1->p_flag |= P_NOSWAP;
+ /*
+ * Set return values for child before vm_fork,
+ * so they can be copied to child stack.
+ * We return parent pid, and mark as child in retval[1].
+ * NOTE: the kernel stack may be at a different location in the child
+ * process, and thus addresses of automatic variables (including retval)
+ * may be invalid after vm_fork returns in the child process.
+ */
+ retval[0] = p1->p_pid;
+ retval[1] = 1;
+ if (vm_fork(p1, p2, isvfork)) {
+ /*
+ * Child process. Set start time and get to work.
+ */
+ (void) splclock();
+ p2->p_stats->p_start = time;
+ (void) spl0();
+ p2->p_acflag = AFORK;
+ return (0);
+ }
+
+ /*
+ * Make child runnable and add to run queue.
+ */
+ (void) splhigh();
+ p2->p_stat = SRUN;
+ setrunqueue(p2);
+ (void) spl0();
+
+ /*
+ * Now can be swapped.
+ */
+ p1->p_flag &= ~P_NOSWAP;
+
+ /*
+ * Preserve synchronization semantics of vfork. If waiting for
+ * child to exec or exit, set P_PPWAIT on child, and sleep on our
+ * proc (in case of exit).
+ */
+ if (isvfork)
+ while (p2->p_flag & P_PPWAIT)
+ tsleep(p1, PWAIT, "ppwait", 0);
+
+ /*
+ * Return child pid to parent process,
+ * marking us as parent via retval[1].
+ */
+ retval[0] = p2->p_pid;
+ retval[1] = 0;
+ return (0);
+}
diff --git a/sys/kern/kern_ktrace.c b/sys/kern/kern_ktrace.c
new file mode 100644
index 0000000..4b6f721
--- /dev/null
+++ b/sys/kern/kern_ktrace.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_ktrace.c 8.2 (Berkeley) 9/23/93
+ */
+
+#ifdef KTRACE
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/ktrace.h>
+#include <sys/malloc.h>
+#include <sys/syslog.h>
+
+void ktrwrite __P((struct vnode *, struct ktr_header *));
+
+struct ktr_header *
+ktrgetheader(type)
+ int type;
+{
+ register struct ktr_header *kth;
+ struct proc *p = curproc; /* XXX */
+
+ MALLOC(kth, struct ktr_header *, sizeof (struct ktr_header),
+ M_TEMP, M_WAITOK);
+ kth->ktr_type = type;
+ microtime(&kth->ktr_time);
+ kth->ktr_pid = p->p_pid;
+ bcopy(p->p_comm, kth->ktr_comm, MAXCOMLEN);
+ return (kth);
+}
+
+void
+ktrsyscall(vp, code, narg, args)
+ struct vnode *vp;
+ int code, narg, args[];
+{
+ struct ktr_header *kth;
+ struct ktr_syscall *ktp;
+ register len = sizeof(struct ktr_syscall) + (narg * sizeof(int));
+ struct proc *p = curproc; /* XXX */
+ int *argp, i;
+
+ p->p_traceflag |= KTRFAC_ACTIVE;
+ kth = ktrgetheader(KTR_SYSCALL);
+ MALLOC(ktp, struct ktr_syscall *, len, M_TEMP, M_WAITOK);
+ ktp->ktr_code = code;
+ ktp->ktr_narg = narg;
+ argp = (int *)((char *)ktp + sizeof(struct ktr_syscall));
+ for (i = 0; i < narg; i++)
+ *argp++ = args[i];
+ kth->ktr_buf = (caddr_t)ktp;
+ kth->ktr_len = len;
+ ktrwrite(vp, kth);
+ FREE(ktp, M_TEMP);
+ FREE(kth, M_TEMP);
+ p->p_traceflag &= ~KTRFAC_ACTIVE;
+}
+
+void
+ktrsysret(vp, code, error, retval)
+ struct vnode *vp;
+ int code, error, retval;
+{
+ struct ktr_header *kth;
+ struct ktr_sysret ktp;
+ struct proc *p = curproc; /* XXX */
+
+ p->p_traceflag |= KTRFAC_ACTIVE;
+ kth = ktrgetheader(KTR_SYSRET);
+ ktp.ktr_code = code;
+ ktp.ktr_error = error;
+ ktp.ktr_retval = retval; /* what about val2 ? */
+
+ kth->ktr_buf = (caddr_t)&ktp;
+ kth->ktr_len = sizeof(struct ktr_sysret);
+
+ ktrwrite(vp, kth);
+ FREE(kth, M_TEMP);
+ p->p_traceflag &= ~KTRFAC_ACTIVE;
+}
+
+void
+ktrnamei(vp, path)
+ struct vnode *vp;
+ char *path;
+{
+ struct ktr_header *kth;
+ struct proc *p = curproc; /* XXX */
+
+ p->p_traceflag |= KTRFAC_ACTIVE;
+ kth = ktrgetheader(KTR_NAMEI);
+ kth->ktr_len = strlen(path);
+ kth->ktr_buf = path;
+
+ ktrwrite(vp, kth);
+ FREE(kth, M_TEMP);
+ p->p_traceflag &= ~KTRFAC_ACTIVE;
+}
+
+void
+ktrgenio(vp, fd, rw, iov, len, error)
+ struct vnode *vp;
+ int fd;
+ enum uio_rw rw;
+ register struct iovec *iov;
+ int len, error;
+{
+ struct ktr_header *kth;
+ register struct ktr_genio *ktp;
+ register caddr_t cp;
+ register int resid = len, cnt;
+ struct proc *p = curproc; /* XXX */
+
+ if (error)
+ return;
+ p->p_traceflag |= KTRFAC_ACTIVE;
+ kth = ktrgetheader(KTR_GENIO);
+ MALLOC(ktp, struct ktr_genio *, sizeof(struct ktr_genio) + len,
+ M_TEMP, M_WAITOK);
+ ktp->ktr_fd = fd;
+ ktp->ktr_rw = rw;
+ cp = (caddr_t)((char *)ktp + sizeof (struct ktr_genio));
+ while (resid > 0) {
+ if ((cnt = iov->iov_len) > resid)
+ cnt = resid;
+ if (copyin(iov->iov_base, cp, (unsigned)cnt))
+ goto done;
+ cp += cnt;
+ resid -= cnt;
+ iov++;
+ }
+ kth->ktr_buf = (caddr_t)ktp;
+ kth->ktr_len = sizeof (struct ktr_genio) + len;
+
+ ktrwrite(vp, kth);
+done:
+ FREE(kth, M_TEMP);
+ FREE(ktp, M_TEMP);
+ p->p_traceflag &= ~KTRFAC_ACTIVE;
+}
+
+void
+ktrpsig(vp, sig, action, mask, code)
+ struct vnode *vp;
+ int sig;
+ sig_t action;
+ int mask, code;
+{
+ struct ktr_header *kth;
+ struct ktr_psig kp;
+ struct proc *p = curproc; /* XXX */
+
+ p->p_traceflag |= KTRFAC_ACTIVE;
+ kth = ktrgetheader(KTR_PSIG);
+ kp.signo = (char)sig;
+ kp.action = action;
+ kp.mask = mask;
+ kp.code = code;
+ kth->ktr_buf = (caddr_t)&kp;
+ kth->ktr_len = sizeof (struct ktr_psig);
+
+ ktrwrite(vp, kth);
+ FREE(kth, M_TEMP);
+ p->p_traceflag &= ~KTRFAC_ACTIVE;
+}
+
+void
+ktrcsw(vp, out, user)
+ struct vnode *vp;
+ int out, user;
+{
+ struct ktr_header *kth;
+ struct ktr_csw kc;
+ struct proc *p = curproc; /* XXX */
+
+ p->p_traceflag |= KTRFAC_ACTIVE;
+ kth = ktrgetheader(KTR_CSW);
+ kc.out = out;
+ kc.user = user;
+ kth->ktr_buf = (caddr_t)&kc;
+ kth->ktr_len = sizeof (struct ktr_csw);
+
+ ktrwrite(vp, kth);
+ FREE(kth, M_TEMP);
+ p->p_traceflag &= ~KTRFAC_ACTIVE;
+}
+
+/* Interface and common routines */
+
+/*
+ * ktrace system call
+ */
+struct ktrace_args {
+ char *fname;
+ int ops;
+ int facs;
+ int pid;
+};
+/* ARGSUSED */
+int
+ktrace(curp, uap, retval)
+ struct proc *curp;
+ register struct ktrace_args *uap;
+ int *retval;
+{
+ register struct vnode *vp = NULL;
+ register struct proc *p;
+ struct pgrp *pg;
+ int facs = uap->facs & ~KTRFAC_ROOT;
+ int ops = KTROP(uap->ops);
+ int descend = uap->ops & KTRFLAG_DESCEND;
+ int ret = 0;
+ int error = 0;
+ struct nameidata nd;
+
+ curp->p_traceflag |= KTRFAC_ACTIVE;
+ if (ops != KTROP_CLEAR) {
+ /*
+ * an operation which requires a file argument.
+ */
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->fname, curp);
+ if (error = vn_open(&nd, FREAD|FWRITE, 0)) {
+ curp->p_traceflag &= ~KTRFAC_ACTIVE;
+ return (error);
+ }
+ vp = nd.ni_vp;
+ VOP_UNLOCK(vp);
+ if (vp->v_type != VREG) {
+ (void) vn_close(vp, FREAD|FWRITE, curp->p_ucred, curp);
+ curp->p_traceflag &= ~KTRFAC_ACTIVE;
+ return (EACCES);
+ }
+ }
+ /*
+ * Clear all uses of the tracefile
+ */
+ if (ops == KTROP_CLEARFILE) {
+ for (p = (struct proc *)allproc; p != NULL; p = p->p_next) {
+ if (p->p_tracep == vp) {
+ if (ktrcanset(curp, p)) {
+ p->p_tracep = NULL;
+ p->p_traceflag = 0;
+ (void) vn_close(vp, FREAD|FWRITE,
+ p->p_ucred, p);
+ } else
+ error = EPERM;
+ }
+ }
+ goto done;
+ }
+ /*
+ * need something to (un)trace (XXX - why is this here?)
+ */
+ if (!facs) {
+ error = EINVAL;
+ goto done;
+ }
+ /*
+ * do it
+ */
+ if (uap->pid < 0) {
+ /*
+ * by process group
+ */
+ pg = pgfind(-uap->pid);
+ if (pg == NULL) {
+ error = ESRCH;
+ goto done;
+ }
+ for (p = pg->pg_mem; p != NULL; p = p->p_pgrpnxt)
+ if (descend)
+ ret |= ktrsetchildren(curp, p, ops, facs, vp);
+ else
+ ret |= ktrops(curp, p, ops, facs, vp);
+
+ } else {
+ /*
+ * by pid
+ */
+ p = pfind(uap->pid);
+ if (p == NULL) {
+ error = ESRCH;
+ goto done;
+ }
+ if (descend)
+ ret |= ktrsetchildren(curp, p, ops, facs, vp);
+ else
+ ret |= ktrops(curp, p, ops, facs, vp);
+ }
+ if (!ret)
+ error = EPERM;
+done:
+ if (vp != NULL)
+ (void) vn_close(vp, FWRITE, curp->p_ucred, curp);
+ curp->p_traceflag &= ~KTRFAC_ACTIVE;
+ return (error);
+}
+
+int
+ktrops(curp, p, ops, facs, vp)
+ struct proc *p, *curp;
+ int ops, facs;
+ struct vnode *vp;
+{
+
+ if (!ktrcanset(curp, p))
+ return (0);
+ if (ops == KTROP_SET) {
+ if (p->p_tracep != vp) {
+ /*
+ * if trace file already in use, relinquish
+ */
+ if (p->p_tracep != NULL)
+ vrele(p->p_tracep);
+ VREF(vp);
+ p->p_tracep = vp;
+ }
+ p->p_traceflag |= facs;
+ if (curp->p_ucred->cr_uid == 0)
+ p->p_traceflag |= KTRFAC_ROOT;
+ } else {
+ /* KTROP_CLEAR */
+ if (((p->p_traceflag &= ~facs) & KTRFAC_MASK) == 0) {
+ /* no more tracing */
+ p->p_traceflag = 0;
+ if (p->p_tracep != NULL) {
+ vrele(p->p_tracep);
+ p->p_tracep = NULL;
+ }
+ }
+ }
+
+ return (1);
+}
+
+int
+ktrsetchildren(curp, top, ops, facs, vp)
+ struct proc *curp, *top;
+ int ops, facs;
+ struct vnode *vp;
+{
+ register struct proc *p;
+ register int ret = 0;
+
+ p = top;
+ for (;;) {
+ ret |= ktrops(curp, p, ops, facs, vp);
+ /*
+ * If this process has children, descend to them next,
+ * otherwise do any siblings, and if done with this level,
+ * follow back up the tree (but not past top).
+ */
+ if (p->p_cptr)
+ p = p->p_cptr;
+ else if (p == top)
+ return (ret);
+ else if (p->p_osptr)
+ p = p->p_osptr;
+ else for (;;) {
+ p = p->p_pptr;
+ if (p == top)
+ return (ret);
+ if (p->p_osptr) {
+ p = p->p_osptr;
+ break;
+ }
+ }
+ }
+ /*NOTREACHED*/
+}
+
+void
+ktrwrite(vp, kth)
+ struct vnode *vp;
+ register struct ktr_header *kth;
+{
+ struct uio auio;
+ struct iovec aiov[2];
+ register struct proc *p = curproc; /* XXX */
+ int error;
+
+ if (vp == NULL)
+ return;
+ auio.uio_iov = &aiov[0];
+ auio.uio_offset = 0;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_WRITE;
+ aiov[0].iov_base = (caddr_t)kth;
+ aiov[0].iov_len = sizeof(struct ktr_header);
+ auio.uio_resid = sizeof(struct ktr_header);
+ auio.uio_iovcnt = 1;
+ auio.uio_procp = (struct proc *)0;
+ if (kth->ktr_len > 0) {
+ auio.uio_iovcnt++;
+ aiov[1].iov_base = kth->ktr_buf;
+ aiov[1].iov_len = kth->ktr_len;
+ auio.uio_resid += kth->ktr_len;
+ }
+ VOP_LOCK(vp);
+ error = VOP_WRITE(vp, &auio, IO_UNIT|IO_APPEND, p->p_ucred);
+ VOP_UNLOCK(vp);
+ if (!error)
+ return;
+ /*
+ * If error encountered, give up tracing on this vnode.
+ */
+ log(LOG_NOTICE, "ktrace write failed, errno %d, tracing stopped\n",
+ error);
+ for (p = (struct proc *)allproc; p != NULL; p = p->p_next) {
+ if (p->p_tracep == vp) {
+ p->p_tracep = NULL;
+ p->p_traceflag = 0;
+ vrele(vp);
+ }
+ }
+}
+
+/*
+ * Return true if caller has permission to set the ktracing state
+ * of target. Essentially, the target can't possess any
+ * more permissions than the caller. KTRFAC_ROOT signifies that
+ * root previously set the tracing status on the target process, and
+ * so, only root may further change it.
+ *
+ * TODO: check groups. use caller effective gid.
+ */
+int
+ktrcanset(callp, targetp)
+ struct proc *callp, *targetp;
+{
+ register struct pcred *caller = callp->p_cred;
+ register struct pcred *target = targetp->p_cred;
+
+ if ((caller->pc_ucred->cr_uid == target->p_ruid &&
+ target->p_ruid == target->p_svuid &&
+ caller->p_rgid == target->p_rgid && /* XXX */
+ target->p_rgid == target->p_svgid &&
+ (targetp->p_traceflag & KTRFAC_ROOT) == 0) ||
+ caller->pc_ucred->cr_uid == 0)
+ return (1);
+
+ return (0);
+}
+
+#endif
diff --git a/sys/kern/kern_malloc.c b/sys/kern/kern_malloc.c
new file mode 100644
index 0000000..3da06d9
--- /dev/null
+++ b/sys/kern/kern_malloc.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 1987, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_malloc.c 8.3 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/map.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+
+struct kmembuckets bucket[MINBUCKET + 16];
+struct kmemstats kmemstats[M_LAST];
+struct kmemusage *kmemusage;
+char *kmembase, *kmemlimit;
+char *memname[] = INITKMEMNAMES;
+
+#ifdef DIAGNOSTIC
+/*
+ * This structure provides a set of masks to catch unaligned frees.
+ */
+long addrmask[] = { 0,
+ 0x00000001, 0x00000003, 0x00000007, 0x0000000f,
+ 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
+ 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
+ 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
+};
+
+/*
+ * The WEIRD_ADDR is used as known text to copy into free objects so
+ * that modifications after frees can be detected.
+ */
+#define WEIRD_ADDR 0xdeadbeef
+#define MAX_COPY 32
+
+/*
+ * Normally the first word of the structure is used to hold the list
+ * pointer for free objects. However, when running with diagnostics,
+ * we use the third and fourth fields, so as to catch modifications
+ * in the most commonly trashed first two words.
+ */
+struct freelist {
+ long spare0;
+ short type;
+ long spare1;
+ caddr_t next;
+};
+#else /* !DIAGNOSTIC */
+struct freelist {
+ caddr_t next;
+};
+#endif /* DIAGNOSTIC */
+
+/*
+ * Allocate a block of memory
+ */
+void *
+malloc(size, type, flags)
+ unsigned long size;
+ int type, flags;
+{
+ register struct kmembuckets *kbp;
+ register struct kmemusage *kup;
+ register struct freelist *freep;
+ long indx, npg, allocsize;
+ int s;
+ caddr_t va, cp, savedlist;
+#ifdef DIAGNOSTIC
+ long *end, *lp;
+ int copysize;
+ char *savedtype;
+#endif
+#ifdef KMEMSTATS
+ register struct kmemstats *ksp = &kmemstats[type];
+
+ if (((unsigned long)type) > M_LAST)
+ panic("malloc - bogus type");
+#endif
+ indx = BUCKETINDX(size);
+ kbp = &bucket[indx];
+ s = splimp();
+#ifdef KMEMSTATS
+ while (ksp->ks_memuse >= ksp->ks_limit) {
+ if (flags & M_NOWAIT) {
+ splx(s);
+ return ((void *) NULL);
+ }
+ if (ksp->ks_limblocks < 65535)
+ ksp->ks_limblocks++;
+ tsleep((caddr_t)ksp, PSWP+2, memname[type], 0);
+ }
+ ksp->ks_size |= 1 << indx;
+#endif
+#ifdef DIAGNOSTIC
+ copysize = 1 << indx < MAX_COPY ? 1 << indx : MAX_COPY;
+#endif
+ if (kbp->kb_next == NULL) {
+ kbp->kb_last = NULL;
+ if (size > MAXALLOCSAVE)
+ allocsize = roundup(size, CLBYTES);
+ else
+ allocsize = 1 << indx;
+ npg = clrnd(btoc(allocsize));
+ va = (caddr_t) kmem_malloc(kmem_map, (vm_size_t)ctob(npg),
+ !(flags & M_NOWAIT));
+ if (va == NULL) {
+ splx(s);
+ return ((void *) NULL);
+ }
+#ifdef KMEMSTATS
+ kbp->kb_total += kbp->kb_elmpercl;
+#endif
+ kup = btokup(va);
+ kup->ku_indx = indx;
+ if (allocsize > MAXALLOCSAVE) {
+ if (npg > 65535)
+ panic("malloc: allocation too large");
+ kup->ku_pagecnt = npg;
+#ifdef KMEMSTATS
+ ksp->ks_memuse += allocsize;
+#endif
+ goto out;
+ }
+#ifdef KMEMSTATS
+ kup->ku_freecnt = kbp->kb_elmpercl;
+ kbp->kb_totalfree += kbp->kb_elmpercl;
+#endif
+ /*
+ * Just in case we blocked while allocating memory,
+ * and someone else also allocated memory for this
+ * bucket, don't assume the list is still empty.
+ */
+ savedlist = kbp->kb_next;
+ kbp->kb_next = cp = va + (npg * NBPG) - allocsize;
+ for (;;) {
+ freep = (struct freelist *)cp;
+#ifdef DIAGNOSTIC
+ /*
+ * Copy in known text to detect modification
+ * after freeing.
+ */
+ end = (long *)&cp[copysize];
+ for (lp = (long *)cp; lp < end; lp++)
+ *lp = WEIRD_ADDR;
+ freep->type = M_FREE;
+#endif /* DIAGNOSTIC */
+ if (cp <= va)
+ break;
+ cp -= allocsize;
+ freep->next = cp;
+ }
+ freep->next = savedlist;
+ if (kbp->kb_last == NULL)
+ kbp->kb_last = (caddr_t)freep;
+ }
+ va = kbp->kb_next;
+ kbp->kb_next = ((struct freelist *)va)->next;
+#ifdef DIAGNOSTIC
+ freep = (struct freelist *)va;
+ savedtype = (unsigned)freep->type < M_LAST ?
+ memname[freep->type] : "???";
+ if (kbp->kb_next &&
+ !kernacc(kbp->kb_next, sizeof(struct freelist), 0)) {
+ printf("%s of object 0x%x size %d %s %s (invalid addr 0x%x)\n",
+ "Data modified on freelist: word 2.5", va, size,
+ "previous type", savedtype, kbp->kb_next);
+ kbp->kb_next = NULL;
+ }
+#if BYTE_ORDER == BIG_ENDIAN
+ freep->type = WEIRD_ADDR >> 16;
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN
+ freep->type = (short)WEIRD_ADDR;
+#endif
+ if (((long)(&freep->next)) & 0x2)
+ freep->next = (caddr_t)((WEIRD_ADDR >> 16)|(WEIRD_ADDR << 16));
+ else
+ freep->next = (caddr_t)WEIRD_ADDR;
+ end = (long *)&va[copysize];
+ for (lp = (long *)va; lp < end; lp++) {
+ if (*lp == WEIRD_ADDR)
+ continue;
+ printf("%s %d of object 0x%x size %d %s %s (0x%x != 0x%x)\n",
+ "Data modified on freelist: word", lp - (long *)va,
+ va, size, "previous type", savedtype, *lp, WEIRD_ADDR);
+ break;
+ }
+ freep->spare0 = 0;
+#endif /* DIAGNOSTIC */
+#ifdef KMEMSTATS
+ kup = btokup(va);
+ if (kup->ku_indx != indx)
+ panic("malloc: wrong bucket");
+ if (kup->ku_freecnt == 0)
+ panic("malloc: lost data");
+ kup->ku_freecnt--;
+ kbp->kb_totalfree--;
+ ksp->ks_memuse += 1 << indx;
+out:
+ kbp->kb_calls++;
+ ksp->ks_inuse++;
+ ksp->ks_calls++;
+ if (ksp->ks_memuse > ksp->ks_maxused)
+ ksp->ks_maxused = ksp->ks_memuse;
+#else
+out:
+#endif
+ splx(s);
+ return ((void *) va);
+}
+
+/*
+ * Free a block of memory allocated by malloc.
+ */
+void
+free(addr, type)
+ void *addr;
+ int type;
+{
+ register struct kmembuckets *kbp;
+ register struct kmemusage *kup;
+ register struct freelist *freep;
+ long size;
+ int s;
+#ifdef DIAGNOSTIC
+ caddr_t cp;
+ long *end, *lp, alloc, copysize;
+#endif
+#ifdef KMEMSTATS
+ register struct kmemstats *ksp = &kmemstats[type];
+#endif
+
+ kup = btokup(addr);
+ size = 1 << kup->ku_indx;
+ kbp = &bucket[kup->ku_indx];
+ s = splimp();
+#ifdef DIAGNOSTIC
+ /*
+ * Check for returns of data that do not point to the
+ * beginning of the allocation.
+ */
+ if (size > NBPG * CLSIZE)
+ alloc = addrmask[BUCKETINDX(NBPG * CLSIZE)];
+ else
+ alloc = addrmask[kup->ku_indx];
+ if (((u_long)addr & alloc) != 0)
+ panic("free: unaligned addr 0x%x, size %d, type %s, mask %d\n",
+ addr, size, memname[type], alloc);
+#endif /* DIAGNOSTIC */
+ if (size > MAXALLOCSAVE) {
+ kmem_free(kmem_map, (vm_offset_t)addr, ctob(kup->ku_pagecnt));
+#ifdef KMEMSTATS
+ size = kup->ku_pagecnt << PGSHIFT;
+ ksp->ks_memuse -= size;
+ kup->ku_indx = 0;
+ kup->ku_pagecnt = 0;
+ if (ksp->ks_memuse + size >= ksp->ks_limit &&
+ ksp->ks_memuse < ksp->ks_limit)
+ wakeup((caddr_t)ksp);
+ ksp->ks_inuse--;
+ kbp->kb_total -= 1;
+#endif
+ splx(s);
+ return;
+ }
+ freep = (struct freelist *)addr;
+#ifdef DIAGNOSTIC
+ /*
+ * Check for multiple frees. Use a quick check to see if
+ * it looks free before laboriously searching the freelist.
+ */
+ if (freep->spare0 == WEIRD_ADDR) {
+ for (cp = kbp->kb_next; cp; cp = *(caddr_t *)cp) {
+ if (addr != cp)
+ continue;
+ printf("multiply freed item 0x%x\n", addr);
+ panic("free: duplicated free");
+ }
+ }
+ /*
+ * Copy in known text to detect modification after freeing
+ * and to make it look free. Also, save the type being freed
+ * so we can list likely culprit if modification is detected
+ * when the object is reallocated.
+ */
+ copysize = size < MAX_COPY ? size : MAX_COPY;
+ end = (long *)&((caddr_t)addr)[copysize];
+ for (lp = (long *)addr; lp < end; lp++)
+ *lp = WEIRD_ADDR;
+ freep->type = type;
+#endif /* DIAGNOSTIC */
+#ifdef KMEMSTATS
+ kup->ku_freecnt++;
+ if (kup->ku_freecnt >= kbp->kb_elmpercl)
+ if (kup->ku_freecnt > kbp->kb_elmpercl)
+ panic("free: multiple frees");
+ else if (kbp->kb_totalfree > kbp->kb_highwat)
+ kbp->kb_couldfree++;
+ kbp->kb_totalfree++;
+ ksp->ks_memuse -= size;
+ if (ksp->ks_memuse + size >= ksp->ks_limit &&
+ ksp->ks_memuse < ksp->ks_limit)
+ wakeup((caddr_t)ksp);
+ ksp->ks_inuse--;
+#endif
+ if (kbp->kb_next == NULL)
+ kbp->kb_next = addr;
+ else
+ ((struct freelist *)kbp->kb_last)->next = addr;
+ freep->next = NULL;
+ kbp->kb_last = addr;
+ splx(s);
+}
+
+/*
+ * Initialize the kernel memory allocator
+ */
+void
+kmeminit()
+{
+ register long indx;
+ int npg;
+
+#if ((MAXALLOCSAVE & (MAXALLOCSAVE - 1)) != 0)
+ ERROR!_kmeminit:_MAXALLOCSAVE_not_power_of_2
+#endif
+#if (MAXALLOCSAVE > MINALLOCSIZE * 32768)
+ ERROR!_kmeminit:_MAXALLOCSAVE_too_big
+#endif
+#if (MAXALLOCSAVE < CLBYTES)
+ ERROR!_kmeminit:_MAXALLOCSAVE_too_small
+#endif
+ npg = VM_KMEM_SIZE/ NBPG;
+ kmemusage = (struct kmemusage *) kmem_alloc(kernel_map,
+ (vm_size_t)(npg * sizeof(struct kmemusage)));
+ kmem_map = kmem_suballoc(kernel_map, (vm_offset_t *)&kmembase,
+ (vm_offset_t *)&kmemlimit, (vm_size_t)(npg * NBPG), FALSE);
+#ifdef KMEMSTATS
+ for (indx = 0; indx < MINBUCKET + 16; indx++) {
+ if (1 << indx >= CLBYTES)
+ bucket[indx].kb_elmpercl = 1;
+ else
+ bucket[indx].kb_elmpercl = CLBYTES / (1 << indx);
+ bucket[indx].kb_highwat = 5 * bucket[indx].kb_elmpercl;
+ }
+ for (indx = 0; indx < M_LAST; indx++)
+ kmemstats[indx].ks_limit = npg * NBPG * 6 / 10;
+#endif
+}
diff --git a/sys/kern/kern_physio.c b/sys/kern/kern_physio.c
new file mode 100644
index 0000000..9e0405f
--- /dev/null
+++ b/sys/kern/kern_physio.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 1994 John S. Dyson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Absolutely no warranty of function or purpose is made by the author
+ * John S. Dyson.
+ * 4. Modifications may be freely made to this file if the above conditions
+ * are met.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+#include <vm/vm.h>
+
+static void physwakeup();
+
+int
+physio(strategy, bp, dev, rw, minp, uio)
+ int (*strategy)();
+ struct buf *bp;
+ dev_t dev;
+ int rw;
+ u_int (*minp)();
+ struct uio *uio;
+{
+ int i;
+ int bp_alloc = (bp == 0);
+ int bufflags = rw?B_READ:0;
+ int error;
+ int spl;
+
+/*
+ * keep the process from being swapped
+ */
+ curproc->p_flag |= P_PHYSIO;
+
+ /* create and build a buffer header for a transfer */
+
+ if (bp_alloc) {
+ bp = (struct buf *)getpbuf();
+ } else {
+ spl = splbio();
+ while (bp->b_flags & B_BUSY) {
+ bp->b_flags |= B_WANTED;
+ tsleep((caddr_t)bp, PRIBIO, "physbw", 0);
+ }
+ bp->b_flags |= B_BUSY;
+ splx(spl);
+ }
+
+ bp->b_proc = curproc;
+ bp->b_dev = dev;
+ error = bp->b_error = 0;
+
+ for(i=0;i<uio->uio_iovcnt;i++) {
+ while( uio->uio_iov[i].iov_len) {
+ vm_offset_t v, lastv, pa;
+ caddr_t adr;
+
+ bp->b_bcount = uio->uio_iov[i].iov_len;
+ bp->b_bufsize = bp->b_bcount;
+ bp->b_flags = B_BUSY | B_PHYS | B_CALL | bufflags;
+ bp->b_iodone = physwakeup;
+ bp->b_data = uio->uio_iov[i].iov_base;
+ bp->b_blkno = btodb(uio->uio_offset);
+
+
+ if (rw && !useracc(bp->b_data, bp->b_bufsize, B_WRITE)) {
+ error = EFAULT;
+ goto doerror;
+ }
+ if (!rw && !useracc(bp->b_data, bp->b_bufsize, B_READ)) {
+ error = EFAULT;
+ goto doerror;
+ }
+
+ vmapbuf(bp);
+
+ /* perform transfer */
+ (*strategy)(bp);
+
+ spl = splbio();
+ while ((bp->b_flags & B_DONE) == 0)
+ tsleep((caddr_t)bp, PRIBIO, "physstr", 0);
+ splx(spl);
+
+ vunmapbuf(bp);
+
+ /*
+ * update the uio data
+ */
+ {
+ int iolen = bp->b_bcount - bp->b_resid;
+ uio->uio_iov[i].iov_len -= iolen;
+ uio->uio_iov[i].iov_base += iolen;
+ uio->uio_resid -= iolen;
+ uio->uio_offset += iolen;
+ }
+
+ /*
+ * check for an error
+ */
+ if( bp->b_flags & B_ERROR) {
+ error = bp->b_error;
+ goto doerror;
+ }
+ }
+ }
+
+
+doerror:
+ if (bp_alloc) {
+ relpbuf(bp);
+ } else {
+ bp->b_flags &= ~(B_BUSY|B_PHYS);
+ if( bp->b_flags & B_WANTED) {
+ bp->b_flags &= ~B_WANTED;
+ wakeup((caddr_t)bp);
+ }
+ }
+/*
+ * allow the process to be swapped
+ */
+ curproc->p_flag &= ~P_PHYSIO;
+
+ return (error);
+}
+
+u_int
+minphys(struct buf *bp)
+{
+
+ if( bp->b_bcount > MAXBSIZE) {
+ bp->b_bcount = MAXBSIZE;
+ }
+ return bp->b_bcount;
+}
+
+int
+rawread(dev_t dev, struct uio *uio)
+{
+ return (physio(cdevsw[major(dev)].d_strategy, (struct buf *)NULL,
+ dev, 1, minphys, uio));
+}
+
+int
+rawwrite(dev_t dev, struct uio *uio)
+{
+ return (physio(cdevsw[major(dev)].d_strategy, (struct buf *)NULL,
+ dev, 0, minphys, uio));
+}
+
+static void
+physwakeup(bp)
+ struct buf *bp;
+{
+ wakeup((caddr_t) bp);
+ bp->b_flags &= ~B_CALL;
+}
diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c
new file mode 100644
index 0000000..63a22c9
--- /dev/null
+++ b/sys/kern/kern_proc.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_proc.c 8.4 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/map.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/acct.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+#include <ufs/ufs/quota.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+
+void pgdelete __P((struct pgrp *));
+void fixjobc __P((struct proc *, struct pgrp *, int));
+
+/*
+ * Structure associated with user cacheing.
+ */
+struct uidinfo {
+ struct uidinfo *ui_next;
+ struct uidinfo **ui_prev;
+ uid_t ui_uid;
+ long ui_proccnt;
+} **uihashtbl;
+u_long uihash; /* size of hash table - 1 */
+#define UIHASH(uid) ((uid) & uihash)
+
+/*
+ * Allocate a hash table.
+ */
+void
+usrinfoinit()
+{
+
+ uihashtbl = hashinit(maxproc / 16, M_PROC, &uihash);
+}
+
+/*
+ * Change the count associated with number of processes
+ * a given user is using.
+ */
+int
+chgproccnt(uid, diff)
+ uid_t uid;
+ int diff;
+{
+ register struct uidinfo **uipp, *uip, *uiq;
+
+ uipp = &uihashtbl[UIHASH(uid)];
+ for (uip = *uipp; uip; uip = uip->ui_next)
+ if (uip->ui_uid == uid)
+ break;
+ if (uip) {
+ uip->ui_proccnt += diff;
+ if (uip->ui_proccnt > 0)
+ return (uip->ui_proccnt);
+ if (uip->ui_proccnt < 0)
+ panic("chgproccnt: procs < 0");
+ if (uiq = uip->ui_next)
+ uiq->ui_prev = uip->ui_prev;
+ *uip->ui_prev = uiq;
+ FREE(uip, M_PROC);
+ return (0);
+ }
+ if (diff <= 0) {
+ if (diff == 0)
+ return(0);
+ panic("chgproccnt: lost user");
+ }
+ MALLOC(uip, struct uidinfo *, sizeof(*uip), M_PROC, M_WAITOK);
+ if (uiq = *uipp)
+ uiq->ui_prev = &uip->ui_next;
+ uip->ui_next = uiq;
+ uip->ui_prev = uipp;
+ *uipp = uip;
+ uip->ui_uid = uid;
+ uip->ui_proccnt = diff;
+ return (diff);
+}
+
+/*
+ * Is p an inferior of the current process?
+ */
+int
+inferior(p)
+ register struct proc *p;
+{
+
+ for (; p != curproc; p = p->p_pptr)
+ if (p->p_pid == 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * Locate a process by number
+ */
+struct proc *
+pfind(pid)
+ register pid_t pid;
+{
+ register struct proc *p;
+
+ for (p = pidhash[PIDHASH(pid)]; p != NULL; p = p->p_hash)
+ if (p->p_pid == pid)
+ return (p);
+ return (NULL);
+}
+
+/*
+ * Locate a process group by number
+ */
+struct pgrp *
+pgfind(pgid)
+ register pid_t pgid;
+{
+ register struct pgrp *pgrp;
+
+ for (pgrp = pgrphash[PIDHASH(pgid)];
+ pgrp != NULL; pgrp = pgrp->pg_hforw)
+ if (pgrp->pg_id == pgid)
+ return (pgrp);
+ return (NULL);
+}
+
+/*
+ * Move p to a new or existing process group (and session)
+ */
+int
+enterpgrp(p, pgid, mksess)
+ register struct proc *p;
+ pid_t pgid;
+ int mksess;
+{
+ register struct pgrp *pgrp = pgfind(pgid);
+ register struct proc **pp;
+ int n;
+
+#ifdef DIAGNOSTIC
+ if (pgrp != NULL && mksess) /* firewalls */
+ panic("enterpgrp: setsid into non-empty pgrp");
+ if (SESS_LEADER(p))
+ panic("enterpgrp: session leader attempted setpgrp");
+#endif
+ if (pgrp == NULL) {
+ pid_t savepid = p->p_pid;
+ struct proc *np;
+ /*
+ * new process group
+ */
+#ifdef DIAGNOSTIC
+ if (p->p_pid != pgid)
+ panic("enterpgrp: new pgrp and pid != pgid");
+#endif
+ MALLOC(pgrp, struct pgrp *, sizeof(struct pgrp), M_PGRP,
+ M_WAITOK);
+ if ((np = pfind(savepid)) == NULL || np != p)
+ return (ESRCH);
+ if (mksess) {
+ register struct session *sess;
+
+ /*
+ * new session
+ */
+ MALLOC(sess, struct session *, sizeof(struct session),
+ M_SESSION, M_WAITOK);
+ sess->s_leader = p;
+ sess->s_count = 1;
+ sess->s_ttyvp = NULL;
+ sess->s_ttyp = NULL;
+ bcopy(p->p_session->s_login, sess->s_login,
+ sizeof(sess->s_login));
+ p->p_flag &= ~P_CONTROLT;
+ pgrp->pg_session = sess;
+#ifdef DIAGNOSTIC
+ if (p != curproc)
+ panic("enterpgrp: mksession and p != curproc");
+#endif
+ } else {
+ pgrp->pg_session = p->p_session;
+ pgrp->pg_session->s_count++;
+ }
+ pgrp->pg_id = pgid;
+ pgrp->pg_hforw = pgrphash[n = PIDHASH(pgid)];
+ pgrphash[n] = pgrp;
+ pgrp->pg_jobc = 0;
+ pgrp->pg_mem = NULL;
+ } else if (pgrp == p->p_pgrp)
+ return (0);
+
+ /*
+ * Adjust eligibility of affected pgrps to participate in job control.
+ * Increment eligibility counts before decrementing, otherwise we
+ * could reach 0 spuriously during the first call.
+ */
+ fixjobc(p, pgrp, 1);
+ fixjobc(p, p->p_pgrp, 0);
+
+ /*
+ * unlink p from old process group
+ */
+ for (pp = &p->p_pgrp->pg_mem; *pp; pp = &(*pp)->p_pgrpnxt) {
+ if (*pp == p) {
+ *pp = p->p_pgrpnxt;
+ break;
+ }
+ }
+#ifdef DIAGNOSTIC
+ if (pp == NULL)
+ panic("enterpgrp: can't find p on old pgrp");
+#endif
+ /*
+ * delete old if empty
+ */
+ if (p->p_pgrp->pg_mem == 0)
+ pgdelete(p->p_pgrp);
+ /*
+ * link into new one
+ */
+ p->p_pgrp = pgrp;
+ p->p_pgrpnxt = pgrp->pg_mem;
+ pgrp->pg_mem = p;
+ return (0);
+}
+
+/*
+ * remove process from process group
+ */
+int
+leavepgrp(p)
+ register struct proc *p;
+{
+ register struct proc **pp = &p->p_pgrp->pg_mem;
+
+ for (; *pp; pp = &(*pp)->p_pgrpnxt) {
+ if (*pp == p) {
+ *pp = p->p_pgrpnxt;
+ break;
+ }
+ }
+#ifdef DIAGNOSTIC
+ if (pp == NULL)
+ panic("leavepgrp: can't find p in pgrp");
+#endif
+ if (!p->p_pgrp->pg_mem)
+ pgdelete(p->p_pgrp);
+ p->p_pgrp = 0;
+ return (0);
+}
+
+/*
+ * delete a process group
+ */
+void
+pgdelete(pgrp)
+ register struct pgrp *pgrp;
+{
+ register struct pgrp **pgp = &pgrphash[PIDHASH(pgrp->pg_id)];
+
+ if (pgrp->pg_session->s_ttyp != NULL &&
+ pgrp->pg_session->s_ttyp->t_pgrp == pgrp)
+ pgrp->pg_session->s_ttyp->t_pgrp = NULL;
+ for (; *pgp; pgp = &(*pgp)->pg_hforw) {
+ if (*pgp == pgrp) {
+ *pgp = pgrp->pg_hforw;
+ break;
+ }
+ }
+#ifdef DIAGNOSTIC
+ if (pgp == NULL)
+ panic("pgdelete: can't find pgrp on hash chain");
+#endif
+ if (--pgrp->pg_session->s_count == 0)
+ FREE(pgrp->pg_session, M_SESSION);
+ FREE(pgrp, M_PGRP);
+}
+
+static void orphanpg();
+
+/*
+ * Adjust pgrp jobc counters when specified process changes process group.
+ * We count the number of processes in each process group that "qualify"
+ * the group for terminal job control (those with a parent in a different
+ * process group of the same session). If that count reaches zero, the
+ * process group becomes orphaned. Check both the specified process'
+ * process group and that of its children.
+ * entering == 0 => p is leaving specified group.
+ * entering == 1 => p is entering specified group.
+ */
+void
+fixjobc(p, pgrp, entering)
+ register struct proc *p;
+ register struct pgrp *pgrp;
+ int entering;
+{
+ register struct pgrp *hispgrp;
+ register struct session *mysession = pgrp->pg_session;
+
+ /*
+ * Check p's parent to see whether p qualifies its own process
+ * group; if so, adjust count for p's process group.
+ */
+ if ((hispgrp = p->p_pptr->p_pgrp) != pgrp &&
+ hispgrp->pg_session == mysession)
+ if (entering)
+ pgrp->pg_jobc++;
+ else if (--pgrp->pg_jobc == 0)
+ orphanpg(pgrp);
+
+ /*
+ * Check this process' children to see whether they qualify
+ * their process groups; if so, adjust counts for children's
+ * process groups.
+ */
+ for (p = p->p_cptr; p; p = p->p_osptr)
+ if ((hispgrp = p->p_pgrp) != pgrp &&
+ hispgrp->pg_session == mysession &&
+ p->p_stat != SZOMB)
+ if (entering)
+ hispgrp->pg_jobc++;
+ else if (--hispgrp->pg_jobc == 0)
+ orphanpg(hispgrp);
+}
+
+/*
+ * A process group has become orphaned;
+ * if there are any stopped processes in the group,
+ * hang-up all process in that group.
+ */
+static void
+orphanpg(pg)
+ struct pgrp *pg;
+{
+ register struct proc *p;
+
+ for (p = pg->pg_mem; p; p = p->p_pgrpnxt) {
+ if (p->p_stat == SSTOP) {
+ for (p = pg->pg_mem; p; p = p->p_pgrpnxt) {
+ psignal(p, SIGHUP);
+ psignal(p, SIGCONT);
+ }
+ return;
+ }
+ }
+}
+
+#ifdef debug
+/* DEBUG */
+pgrpdump()
+{
+ register struct pgrp *pgrp;
+ register struct proc *p;
+ register i;
+
+ for (i=0; i<PIDHSZ; i++) {
+ if (pgrphash[i]) {
+ printf("\tindx %d\n", i);
+ for (pgrp=pgrphash[i]; pgrp; pgrp=pgrp->pg_hforw) {
+ printf("\tpgrp %x, pgid %d, sess %x, sesscnt %d, mem %x\n",
+ pgrp, pgrp->pg_id, pgrp->pg_session,
+ pgrp->pg_session->s_count, pgrp->pg_mem);
+ for (p=pgrp->pg_mem; p; p=p->p_pgrpnxt) {
+ printf("\t\tpid %d addr %x pgrp %x\n",
+ p->p_pid, p, p->p_pgrp);
+ }
+ }
+
+ }
+ }
+}
+#endif /* debug */
diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c
new file mode 100644
index 0000000..1e205ee
--- /dev/null
+++ b/sys/kern/kern_prot.c
@@ -0,0 +1,588 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1990, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_prot.c 8.6 (Berkeley) 1/21/94
+ */
+
+/*
+ * System calls related to processes and protection
+ */
+
+#include <sys/param.h>
+#include <sys/acct.h>
+#include <sys/systm.h>
+#include <sys/ucred.h>
+#include <sys/proc.h>
+#include <sys/timeb.h>
+#include <sys/times.h>
+#include <sys/malloc.h>
+
+struct args {
+ int dummy;
+};
+
+/* ARGSUSED */
+int
+getpid(p, uap, retval)
+ struct proc *p;
+ struct args *uap;
+ int *retval;
+{
+
+ *retval = p->p_pid;
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+ retval[1] = p->p_pptr->p_pid;
+#endif
+ return (0);
+}
+
+/* ARGSUSED */
+int
+getppid(p, uap, retval)
+ struct proc *p;
+ struct args *uap;
+ int *retval;
+{
+
+ *retval = p->p_pptr->p_pid;
+ return (0);
+}
+
+/* Get process group ID; note that POSIX getpgrp takes no parameter */
+int
+getpgrp(p, uap, retval)
+ struct proc *p;
+ struct args *uap;
+ int *retval;
+{
+
+ *retval = p->p_pgrp->pg_id;
+ return (0);
+}
+
+/* ARGSUSED */
+int
+getuid(p, uap, retval)
+ struct proc *p;
+ struct args *uap;
+ int *retval;
+{
+
+ *retval = p->p_cred->p_ruid;
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+ retval[1] = p->p_ucred->cr_uid;
+#endif
+ return (0);
+}
+
+/* ARGSUSED */
+int
+geteuid(p, uap, retval)
+ struct proc *p;
+ struct args *uap;
+ int *retval;
+{
+
+ *retval = p->p_ucred->cr_uid;
+ return (0);
+}
+
+/* ARGSUSED */
+int
+getgid(p, uap, retval)
+ struct proc *p;
+ struct args *uap;
+ int *retval;
+{
+
+ *retval = p->p_cred->p_rgid;
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+ retval[1] = p->p_ucred->cr_groups[0];
+#endif
+ return (0);
+}
+
+/*
+ * Get effective group ID. The "egid" is groups[0], and could be obtained
+ * via getgroups. This syscall exists because it is somewhat painful to do
+ * correctly in a library function.
+ */
+/* ARGSUSED */
+int
+getegid(p, uap, retval)
+ struct proc *p;
+ struct args *uap;
+ int *retval;
+{
+
+ *retval = p->p_ucred->cr_groups[0];
+ return (0);
+}
+
+struct getgroups_args {
+ u_int gidsetsize;
+ gid_t *gidset;
+};
+int
+getgroups(p, uap, retval)
+ struct proc *p;
+ register struct getgroups_args *uap;
+ int *retval;
+{
+ register struct pcred *pc = p->p_cred;
+ register u_int ngrp;
+ int error;
+
+ if ((ngrp = uap->gidsetsize) == 0) {
+ *retval = pc->pc_ucred->cr_ngroups;
+ return (0);
+ }
+ if (ngrp < pc->pc_ucred->cr_ngroups)
+ return (EINVAL);
+ ngrp = pc->pc_ucred->cr_ngroups;
+ if (error = copyout((caddr_t)pc->pc_ucred->cr_groups,
+ (caddr_t)uap->gidset, ngrp * sizeof(gid_t)))
+ return (error);
+ *retval = ngrp;
+ return (0);
+}
+
+/* ARGSUSED */
+int
+setsid(p, uap, retval)
+ register struct proc *p;
+ struct args *uap;
+ int *retval;
+{
+
+ if (p->p_pgid == p->p_pid || pgfind(p->p_pid)) {
+ return (EPERM);
+ } else {
+ (void)enterpgrp(p, p->p_pid, 1);
+ *retval = p->p_pid;
+ return (0);
+ }
+}
+
+/*
+ * set process group (setpgid/old setpgrp)
+ *
+ * caller does setpgid(targpid, targpgid)
+ *
+ * pid must be caller or child of caller (ESRCH)
+ * if a child
+ * pid must be in same session (EPERM)
+ * pid can't have done an exec (EACCES)
+ * if pgid != pid
+ * there must exist some pid in same session having pgid (EPERM)
+ * pid must not be session leader (EPERM)
+ */
+struct setpgid_args {
+ int pid; /* target process id */
+ int pgid; /* target pgrp id */
+};
+/* ARGSUSED */
+int
+setpgid(curp, uap, retval)
+ struct proc *curp;
+ register struct setpgid_args *uap;
+ int *retval;
+{
+ register struct proc *targp; /* target process */
+ register struct pgrp *pgrp; /* target pgrp */
+
+ if (uap->pid != 0 && uap->pid != curp->p_pid) {
+ if ((targp = pfind(uap->pid)) == 0 || !inferior(targp))
+ return (ESRCH);
+ if (targp->p_session != curp->p_session)
+ return (EPERM);
+ if (targp->p_flag & P_EXEC)
+ return (EACCES);
+ } else
+ targp = curp;
+ if (SESS_LEADER(targp))
+ return (EPERM);
+ if (uap->pgid == 0)
+ uap->pgid = targp->p_pid;
+ else if (uap->pgid != targp->p_pid)
+ if ((pgrp = pgfind(uap->pgid)) == 0 ||
+ pgrp->pg_session != curp->p_session)
+ return (EPERM);
+ return (enterpgrp(targp, uap->pgid, 0));
+}
+
+struct setuid_args {
+ uid_t uid;
+};
+/* ARGSUSED */
+int
+setuid(p, uap, retval)
+ struct proc *p;
+ struct setuid_args *uap;
+ int *retval;
+{
+ register struct pcred *pc = p->p_cred;
+ register uid_t uid;
+ int error;
+
+ uid = uap->uid;
+ if (uid != pc->p_ruid &&
+ (error = suser(pc->pc_ucred, &p->p_acflag)))
+ return (error);
+ /*
+ * Everything's okay, do it.
+ * Transfer proc count to new user.
+ * Copy credentials so other references do not see our changes.
+ */
+ (void)chgproccnt(pc->p_ruid, -1);
+ (void)chgproccnt(uid, 1);
+ pc->pc_ucred = crcopy(pc->pc_ucred);
+ pc->pc_ucred->cr_uid = uid;
+ pc->p_ruid = uid;
+ pc->p_svuid = uid;
+ p->p_flag |= P_SUGID;
+ return (0);
+}
+
+struct seteuid_args {
+ uid_t euid;
+};
+/* ARGSUSED */
+int
+seteuid(p, uap, retval)
+ struct proc *p;
+ struct seteuid_args *uap;
+ int *retval;
+{
+ register struct pcred *pc = p->p_cred;
+ register uid_t euid;
+ int error;
+
+ euid = uap->euid;
+ if (euid != pc->p_ruid && euid != pc->p_svuid &&
+ (error = suser(pc->pc_ucred, &p->p_acflag)))
+ return (error);
+ /*
+ * Everything's okay, do it. Copy credentials so other references do
+ * not see our changes.
+ */
+ pc->pc_ucred = crcopy(pc->pc_ucred);
+ pc->pc_ucred->cr_uid = euid;
+ p->p_flag |= P_SUGID;
+ return (0);
+}
+
+struct setgid_args {
+ gid_t gid;
+};
+/* ARGSUSED */
+int
+setgid(p, uap, retval)
+ struct proc *p;
+ struct setgid_args *uap;
+ int *retval;
+{
+ register struct pcred *pc = p->p_cred;
+ register gid_t gid;
+ int error;
+
+ gid = uap->gid;
+ if (gid != pc->p_rgid && (error = suser(pc->pc_ucred, &p->p_acflag)))
+ return (error);
+ pc->pc_ucred = crcopy(pc->pc_ucred);
+ pc->pc_ucred->cr_groups[0] = gid;
+ pc->p_rgid = gid;
+ pc->p_svgid = gid; /* ??? */
+ p->p_flag |= P_SUGID;
+ return (0);
+}
+
+struct setegid_args {
+ gid_t egid;
+};
+/* ARGSUSED */
+int
+setegid(p, uap, retval)
+ struct proc *p;
+ struct setegid_args *uap;
+ int *retval;
+{
+ register struct pcred *pc = p->p_cred;
+ register gid_t egid;
+ int error;
+
+ egid = uap->egid;
+ if (egid != pc->p_rgid && egid != pc->p_svgid &&
+ (error = suser(pc->pc_ucred, &p->p_acflag)))
+ return (error);
+ pc->pc_ucred = crcopy(pc->pc_ucred);
+ pc->pc_ucred->cr_groups[0] = egid;
+ p->p_flag |= P_SUGID;
+ return (0);
+}
+
+struct setgroups_args {
+ u_int gidsetsize;
+ gid_t *gidset;
+};
+/* ARGSUSED */
+int
+setgroups(p, uap, retval)
+ struct proc *p;
+ struct setgroups_args *uap;
+ int *retval;
+{
+ register struct pcred *pc = p->p_cred;
+ register u_int ngrp;
+ int error;
+
+ if (error = suser(pc->pc_ucred, &p->p_acflag))
+ return (error);
+ if ((ngrp = uap->gidsetsize) > NGROUPS)
+ return (EINVAL);
+ pc->pc_ucred = crcopy(pc->pc_ucred);
+ if (error = copyin((caddr_t)uap->gidset,
+ (caddr_t)pc->pc_ucred->cr_groups, ngrp * sizeof(gid_t)))
+ return (error);
+ pc->pc_ucred->cr_ngroups = ngrp;
+ p->p_flag |= P_SUGID;
+ return (0);
+}
+
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+struct setreuid_args {
+ int ruid;
+ int euid;
+};
+/* ARGSUSED */
+int
+osetreuid(p, uap, retval)
+ register struct proc *p;
+ struct setreuid_args *uap;
+ int *retval;
+{
+ register struct pcred *pc = p->p_cred;
+ struct seteuid_args args;
+
+ /*
+ * we assume that the intent of setting ruid is to be able to get
+ * back ruid priviledge. So we make sure that we will be able to
+ * do so, but do not actually set the ruid.
+ */
+ if (uap->ruid != (uid_t)-1 && uap->ruid != pc->p_ruid &&
+ uap->ruid != pc->p_svuid)
+ return (EPERM);
+ if (uap->euid == (uid_t)-1)
+ return (0);
+ args.euid = uap->euid;
+ return (seteuid(p, &args, retval));
+}
+
+struct setregid_args {
+ int rgid;
+ int egid;
+};
+/* ARGSUSED */
+int
+osetregid(p, uap, retval)
+ register struct proc *p;
+ struct setregid_args *uap;
+ int *retval;
+{
+ register struct pcred *pc = p->p_cred;
+ struct setegid_args args;
+
+ /*
+ * we assume that the intent of setting rgid is to be able to get
+ * back rgid priviledge. So we make sure that we will be able to
+ * do so, but do not actually set the rgid.
+ */
+ if (uap->rgid != (gid_t)-1 && uap->rgid != pc->p_rgid &&
+ uap->rgid != pc->p_svgid)
+ return (EPERM);
+ if (uap->egid == (gid_t)-1)
+ return (0);
+ args.egid = uap->egid;
+ return (setegid(p, &args, retval));
+}
+#endif /* defined(COMPAT_43) || defined(COMPAT_SUNOS) */
+
+/*
+ * Check if gid is a member of the group set.
+ */
+int
+groupmember(gid, cred)
+ gid_t gid;
+ register struct ucred *cred;
+{
+ register gid_t *gp;
+ gid_t *egp;
+
+ egp = &(cred->cr_groups[cred->cr_ngroups]);
+ for (gp = cred->cr_groups; gp < egp; gp++)
+ if (*gp == gid)
+ return (1);
+ return (0);
+}
+
+/*
+ * Test whether the specified credentials imply "super-user"
+ * privilege; if so, and we have accounting info, set the flag
+ * indicating use of super-powers.
+ * Returns 0 or error.
+ */
+int
+suser(cred, acflag)
+ struct ucred *cred;
+ short *acflag;
+{
+ if (cred->cr_uid == 0) {
+ if (acflag)
+ *acflag |= ASU;
+ return (0);
+ }
+ return (EPERM);
+}
+
+/*
+ * Allocate a zeroed cred structure.
+ */
+struct ucred *
+crget()
+{
+ register struct ucred *cr;
+
+ MALLOC(cr, struct ucred *, sizeof(*cr), M_CRED, M_WAITOK);
+ bzero((caddr_t)cr, sizeof(*cr));
+ cr->cr_ref = 1;
+ return (cr);
+}
+
+/*
+ * Free a cred structure.
+ * Throws away space when ref count gets to 0.
+ */
+void
+crfree(cr)
+ struct ucred *cr;
+{
+ int s;
+
+ s = splimp(); /* ??? */
+ if (--cr->cr_ref == 0)
+ FREE((caddr_t)cr, M_CRED);
+ (void) splx(s);
+}
+
+/*
+ * Copy cred structure to a new one and free the old one.
+ */
+struct ucred *
+crcopy(cr)
+ struct ucred *cr;
+{
+ struct ucred *newcr;
+
+ if (cr->cr_ref == 1)
+ return (cr);
+ newcr = crget();
+ *newcr = *cr;
+ crfree(cr);
+ newcr->cr_ref = 1;
+ return (newcr);
+}
+
+/*
+ * Dup cred struct to a new held one.
+ */
+struct ucred *
+crdup(cr)
+ struct ucred *cr;
+{
+ struct ucred *newcr;
+
+ newcr = crget();
+ *newcr = *cr;
+ newcr->cr_ref = 1;
+ return (newcr);
+}
+
+/*
+ * Get login name, if available.
+ */
+struct getlogin_args {
+ char *namebuf;
+ u_int namelen;
+};
+/* ARGSUSED */
+int
+getlogin(p, uap, retval)
+ struct proc *p;
+ struct getlogin_args *uap;
+ int *retval;
+{
+
+ if (uap->namelen > sizeof (p->p_pgrp->pg_session->s_login))
+ uap->namelen = sizeof (p->p_pgrp->pg_session->s_login);
+ return (copyout((caddr_t) p->p_pgrp->pg_session->s_login,
+ (caddr_t) uap->namebuf, uap->namelen));
+}
+
+/*
+ * Set login name.
+ */
+struct setlogin_args {
+ char *namebuf;
+};
+/* ARGSUSED */
+int
+setlogin(p, uap, retval)
+ struct proc *p;
+ struct setlogin_args *uap;
+ int *retval;
+{
+ int error;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ error = copyinstr((caddr_t) uap->namebuf,
+ (caddr_t) p->p_pgrp->pg_session->s_login,
+ sizeof (p->p_pgrp->pg_session->s_login) - 1, (u_int *)0);
+ if (error == ENAMETOOLONG)
+ error = EINVAL;
+ return (error);
+}
diff --git a/sys/kern/kern_resource.c b/sys/kern/kern_resource.c
new file mode 100644
index 0000000..e38471a
--- /dev/null
+++ b/sys/kern/kern_resource.c
@@ -0,0 +1,487 @@
+/*-
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_resource.c 8.5 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/resourcevar.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <vm/vm.h>
+
+/*
+ * Resource controls and accounting.
+ */
+
+struct getpriority_args {
+ int which;
+ int who;
+};
+int
+getpriority(curp, uap, retval)
+ struct proc *curp;
+ register struct getpriority_args *uap;
+ int *retval;
+{
+ register struct proc *p;
+ register int low = PRIO_MAX + 1;
+
+ switch (uap->which) {
+
+ case PRIO_PROCESS:
+ if (uap->who == 0)
+ p = curp;
+ else
+ p = pfind(uap->who);
+ if (p == 0)
+ break;
+ low = p->p_nice;
+ break;
+
+ case PRIO_PGRP: {
+ register struct pgrp *pg;
+
+ if (uap->who == 0)
+ pg = curp->p_pgrp;
+ else if ((pg = pgfind(uap->who)) == NULL)
+ break;
+ for (p = pg->pg_mem; p != NULL; p = p->p_pgrpnxt) {
+ if (p->p_nice < low)
+ low = p->p_nice;
+ }
+ break;
+ }
+
+ case PRIO_USER:
+ if (uap->who == 0)
+ uap->who = curp->p_ucred->cr_uid;
+ for (p = (struct proc *)allproc; p != NULL; p = p->p_next) {
+ if (p->p_ucred->cr_uid == uap->who &&
+ p->p_nice < low)
+ low = p->p_nice;
+ }
+ break;
+
+ default:
+ return (EINVAL);
+ }
+ if (low == PRIO_MAX + 1)
+ return (ESRCH);
+ *retval = low;
+ return (0);
+}
+
+struct setpriority_args {
+ int which;
+ int who;
+ int prio;
+};
+/* ARGSUSED */
+int
+setpriority(curp, uap, retval)
+ struct proc *curp;
+ register struct setpriority_args *uap;
+ int *retval;
+{
+ register struct proc *p;
+ int found = 0, error = 0;
+
+ switch (uap->which) {
+
+ case PRIO_PROCESS:
+ if (uap->who == 0)
+ p = curp;
+ else
+ p = pfind(uap->who);
+ if (p == 0)
+ break;
+ error = donice(curp, p, uap->prio);
+ found++;
+ break;
+
+ case PRIO_PGRP: {
+ register struct pgrp *pg;
+
+ if (uap->who == 0)
+ pg = curp->p_pgrp;
+ else if ((pg = pgfind(uap->who)) == NULL)
+ break;
+ for (p = pg->pg_mem; p != NULL; p = p->p_pgrpnxt) {
+ error = donice(curp, p, uap->prio);
+ found++;
+ }
+ break;
+ }
+
+ case PRIO_USER:
+ if (uap->who == 0)
+ uap->who = curp->p_ucred->cr_uid;
+ for (p = (struct proc *)allproc; p != NULL; p = p->p_next)
+ if (p->p_ucred->cr_uid == uap->who) {
+ error = donice(curp, p, uap->prio);
+ found++;
+ }
+ break;
+
+ default:
+ return (EINVAL);
+ }
+ if (found == 0)
+ return (ESRCH);
+ return (error);
+}
+
+int
+donice(curp, chgp, n)
+ register struct proc *curp, *chgp;
+ register int n;
+{
+ register struct pcred *pcred = curp->p_cred;
+
+ if (pcred->pc_ucred->cr_uid && pcred->p_ruid &&
+ pcred->pc_ucred->cr_uid != chgp->p_ucred->cr_uid &&
+ pcred->p_ruid != chgp->p_ucred->cr_uid)
+ return (EPERM);
+ if (n > PRIO_MAX)
+ n = PRIO_MAX;
+ if (n < PRIO_MIN)
+ n = PRIO_MIN;
+ if (n < chgp->p_nice && suser(pcred->pc_ucred, &curp->p_acflag))
+ return (EACCES);
+ chgp->p_nice = n;
+ (void)resetpriority(chgp);
+ return (0);
+}
+
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+struct setrlimit_args {
+ u_int which;
+ struct orlimit *lim;
+};
+/* ARGSUSED */
+int
+osetrlimit(p, uap, retval)
+ struct proc *p;
+ register struct setrlimit_args *uap;
+ int *retval;
+{
+ struct orlimit olim;
+ struct rlimit lim;
+ int error;
+
+ if (error =
+ copyin((caddr_t)uap->lim, (caddr_t)&olim, sizeof (struct orlimit)))
+ return (error);
+ lim.rlim_cur = olim.rlim_cur;
+ lim.rlim_max = olim.rlim_max;
+ return (dosetrlimit(p, uap->which, &lim));
+}
+
+struct getrlimit_args {
+ u_int which;
+ struct orlimit *rlp;
+};
+/* ARGSUSED */
+int
+ogetrlimit(p, uap, retval)
+ struct proc *p;
+ register struct getrlimit_args *uap;
+ int *retval;
+{
+ struct orlimit olim;
+
+ if (uap->which >= RLIM_NLIMITS)
+ return (EINVAL);
+ olim.rlim_cur = p->p_rlimit[uap->which].rlim_cur;
+ if (olim.rlim_cur == -1)
+ olim.rlim_cur = 0x7fffffff;
+ olim.rlim_max = p->p_rlimit[uap->which].rlim_max;
+ if (olim.rlim_max == -1)
+ olim.rlim_max = 0x7fffffff;
+ return (copyout((caddr_t)&olim, (caddr_t)uap->rlp, sizeof(olim)));
+}
+#endif /* COMPAT_43 || COMPAT_SUNOS */
+
+struct __setrlimit_args {
+ u_int which;
+ struct rlimit *lim;
+};
+/* ARGSUSED */
+int
+setrlimit(p, uap, retval)
+ struct proc *p;
+ register struct __setrlimit_args *uap;
+ int *retval;
+{
+ struct rlimit alim;
+ int error;
+
+ if (error =
+ copyin((caddr_t)uap->lim, (caddr_t)&alim, sizeof (struct rlimit)))
+ return (error);
+ return (dosetrlimit(p, uap->which, &alim));
+}
+
+int
+dosetrlimit(p, which, limp)
+ struct proc *p;
+ u_int which;
+ struct rlimit *limp;
+{
+ register struct rlimit *alimp;
+ int error;
+
+ if (which >= RLIM_NLIMITS)
+ return (EINVAL);
+ alimp = &p->p_rlimit[which];
+ if (limp->rlim_cur > alimp->rlim_max ||
+ limp->rlim_max > alimp->rlim_max)
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ if (limp->rlim_cur > limp->rlim_max)
+ limp->rlim_cur = limp->rlim_max;
+ if (p->p_limit->p_refcnt > 1 &&
+ (p->p_limit->p_lflags & PL_SHAREMOD) == 0) {
+ p->p_limit->p_refcnt--;
+ p->p_limit = limcopy(p->p_limit);
+ alimp = &p->p_rlimit[which];
+ }
+
+ switch (which) {
+
+ case RLIMIT_DATA:
+ if (limp->rlim_cur > MAXDSIZ)
+ limp->rlim_cur = MAXDSIZ;
+ if (limp->rlim_max > MAXDSIZ)
+ limp->rlim_max = MAXDSIZ;
+ break;
+
+ case RLIMIT_STACK:
+ if (limp->rlim_cur > MAXSSIZ)
+ limp->rlim_cur = MAXSSIZ;
+ if (limp->rlim_max > MAXSSIZ)
+ limp->rlim_max = MAXSSIZ;
+ /*
+ * Stack is allocated to the max at exec time with only
+ * "rlim_cur" bytes accessible. If stack limit is going
+ * up make more accessible, if going down make inaccessible.
+ */
+ if (limp->rlim_cur != alimp->rlim_cur) {
+ vm_offset_t addr;
+ vm_size_t size;
+ vm_prot_t prot;
+
+ if (limp->rlim_cur > alimp->rlim_cur) {
+ prot = VM_PROT_ALL;
+ size = limp->rlim_cur - alimp->rlim_cur;
+ addr = USRSTACK - limp->rlim_cur;
+ } else {
+ prot = VM_PROT_NONE;
+ size = alimp->rlim_cur - limp->rlim_cur;
+ addr = USRSTACK - alimp->rlim_cur;
+ }
+ addr = trunc_page(addr);
+ size = round_page(size);
+ (void) vm_map_protect(&p->p_vmspace->vm_map,
+ addr, addr+size, prot, FALSE);
+ }
+ break;
+
+ case RLIMIT_NOFILE:
+ if (limp->rlim_cur > maxfiles)
+ limp->rlim_cur = maxfiles;
+ if (limp->rlim_max > maxfiles)
+ limp->rlim_max = maxfiles;
+ break;
+
+ case RLIMIT_NPROC:
+ if (limp->rlim_cur > maxproc)
+ limp->rlim_cur = maxproc;
+ if (limp->rlim_max > maxproc)
+ limp->rlim_max = maxproc;
+ break;
+ }
+ *alimp = *limp;
+ return (0);
+}
+
+struct __getrlimit_args {
+ u_int which;
+ struct rlimit *rlp;
+};
+/* ARGSUSED */
+int
+getrlimit(p, uap, retval)
+ struct proc *p;
+ register struct __getrlimit_args *uap;
+ int *retval;
+{
+
+ if (uap->which >= RLIM_NLIMITS)
+ return (EINVAL);
+ return (copyout((caddr_t)&p->p_rlimit[uap->which], (caddr_t)uap->rlp,
+ sizeof (struct rlimit)));
+}
+
+/*
+ * Transform the running time and tick information in proc p into user,
+ * system, and interrupt time usage.
+ */
+void
+calcru(p, up, sp, ip)
+ register struct proc *p;
+ register struct timeval *up;
+ register struct timeval *sp;
+ register struct timeval *ip;
+{
+ register u_quad_t u, st, ut, it, tot;
+ register u_long sec, usec;
+ register int s;
+ struct timeval tv;
+
+ s = splstatclock();
+ st = p->p_sticks;
+ ut = p->p_uticks;
+ it = p->p_iticks;
+ splx(s);
+
+ tot = st + ut + it;
+ if (tot == 0) {
+ up->tv_sec = up->tv_usec = 0;
+ sp->tv_sec = sp->tv_usec = 0;
+ if (ip != NULL)
+ ip->tv_sec = ip->tv_usec = 0;
+ return;
+ }
+
+ sec = p->p_rtime.tv_sec;
+ usec = p->p_rtime.tv_usec;
+ if (p == curproc) {
+ /*
+ * Adjust for the current time slice. This is actually fairly
+ * important since the error here is on the order of a time
+ * quantum, which is much greater than the sampling error.
+ */
+ microtime(&tv);
+ sec += tv.tv_sec - runtime.tv_sec;
+ usec += tv.tv_usec - runtime.tv_usec;
+ }
+ u = sec * 1000000 + usec;
+ st = (u * st) / tot;
+ sp->tv_sec = st / 1000000;
+ sp->tv_usec = st % 1000000;
+ ut = (u * ut) / tot;
+ up->tv_sec = ut / 1000000;
+ up->tv_usec = ut % 1000000;
+ if (ip != NULL) {
+ it = (u * it) / tot;
+ ip->tv_sec = it / 1000000;
+ ip->tv_usec = it % 1000000;
+ }
+}
+
+struct getrusage_args {
+ int who;
+ struct rusage *rusage;
+};
+/* ARGSUSED */
+int
+getrusage(p, uap, retval)
+ register struct proc *p;
+ register struct getrusage_args *uap;
+ int *retval;
+{
+ register struct rusage *rup;
+
+ switch (uap->who) {
+
+ case RUSAGE_SELF:
+ rup = &p->p_stats->p_ru;
+ calcru(p, &rup->ru_utime, &rup->ru_stime, NULL);
+ break;
+
+ case RUSAGE_CHILDREN:
+ rup = &p->p_stats->p_cru;
+ break;
+
+ default:
+ return (EINVAL);
+ }
+ return (copyout((caddr_t)rup, (caddr_t)uap->rusage,
+ sizeof (struct rusage)));
+}
+
+void
+ruadd(ru, ru2)
+ register struct rusage *ru, *ru2;
+{
+ register long *ip, *ip2;
+ register int i;
+
+ timevaladd(&ru->ru_utime, &ru2->ru_utime);
+ timevaladd(&ru->ru_stime, &ru2->ru_stime);
+ if (ru->ru_maxrss < ru2->ru_maxrss)
+ ru->ru_maxrss = ru2->ru_maxrss;
+ ip = &ru->ru_first; ip2 = &ru2->ru_first;
+ for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--)
+ *ip++ += *ip2++;
+}
+
+/*
+ * Make a copy of the plimit structure.
+ * We share these structures copy-on-write after fork,
+ * and copy when a limit is changed.
+ */
+struct plimit *
+limcopy(lim)
+ struct plimit *lim;
+{
+ register struct plimit *copy;
+
+ MALLOC(copy, struct plimit *, sizeof(struct plimit),
+ M_SUBPROC, M_WAITOK);
+ bcopy(lim->pl_rlimit, copy->pl_rlimit,
+ sizeof(struct rlimit) * RLIM_NLIMITS);
+ copy->p_lflags = 0;
+ copy->p_refcnt = 1;
+ return (copy);
+}
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
new file mode 100644
index 0000000..f778c36
--- /dev/null
+++ b/sys/kern/kern_sig.c
@@ -0,0 +1,1220 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_sig.c 8.7 (Berkeley) 4/18/94
+ */
+
+#define SIGPROP /* include signal properties table */
+#include <sys/param.h>
+#include <sys/signalvar.h>
+#include <sys/resourcevar.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/timeb.h>
+#include <sys/times.h>
+#include <sys/buf.h>
+#include <sys/acct.h>
+#include <sys/file.h>
+#include <sys/kernel.h>
+#include <sys/wait.h>
+#include <sys/ktrace.h>
+#include <sys/syslog.h>
+#include <sys/stat.h>
+
+#include <machine/cpu.h>
+
+#include <vm/vm.h>
+#include <sys/user.h> /* for coredump */
+
+void setsigvec __P((struct proc *, int, struct sigaction *));
+void stop __P((struct proc *));
+void sigexit __P((struct proc *, int));
+
+/*
+ * Can process p, with pcred pc, send the signal signum to process q?
+ */
+#define CANSIGNAL(p, pc, q, signum) \
+ ((pc)->pc_ucred->cr_uid == 0 || \
+ (pc)->p_ruid == (q)->p_cred->p_ruid || \
+ (pc)->pc_ucred->cr_uid == (q)->p_cred->p_ruid || \
+ (pc)->p_ruid == (q)->p_ucred->cr_uid || \
+ (pc)->pc_ucred->cr_uid == (q)->p_ucred->cr_uid || \
+ ((signum) == SIGCONT && (q)->p_session == (p)->p_session))
+
+struct sigaction_args {
+ int signum;
+ struct sigaction *nsa;
+ struct sigaction *osa;
+};
+/* ARGSUSED */
+int
+sigaction(p, uap, retval)
+ struct proc *p;
+ register struct sigaction_args *uap;
+ int *retval;
+{
+ struct sigaction vec;
+ register struct sigaction *sa;
+ register struct sigacts *ps = p->p_sigacts;
+ register int signum;
+ int bit, error;
+
+ signum = uap->signum;
+ if (signum <= 0 || signum >= NSIG ||
+ signum == SIGKILL || signum == SIGSTOP)
+ return (EINVAL);
+ sa = &vec;
+ if (uap->osa) {
+ sa->sa_handler = ps->ps_sigact[signum];
+ sa->sa_mask = ps->ps_catchmask[signum];
+ bit = sigmask(signum);
+ sa->sa_flags = 0;
+ if ((ps->ps_sigonstack & bit) != 0)
+ sa->sa_flags |= SA_ONSTACK;
+ if ((ps->ps_sigintr & bit) == 0)
+ sa->sa_flags |= SA_RESTART;
+ if (p->p_flag & P_NOCLDSTOP)
+ sa->sa_flags |= SA_NOCLDSTOP;
+ if (error = copyout((caddr_t)sa, (caddr_t)uap->osa,
+ sizeof (vec)))
+ return (error);
+ }
+ if (uap->nsa) {
+ if (error = copyin((caddr_t)uap->nsa, (caddr_t)sa,
+ sizeof (vec)))
+ return (error);
+ setsigvec(p, signum, sa);
+ }
+ return (0);
+}
+
+void
+setsigvec(p, signum, sa)
+ register struct proc *p;
+ int signum;
+ register struct sigaction *sa;
+{
+ register struct sigacts *ps = p->p_sigacts;
+ register int bit;
+
+ bit = sigmask(signum);
+ /*
+ * Change setting atomically.
+ */
+ (void) splhigh();
+ ps->ps_sigact[signum] = sa->sa_handler;
+ ps->ps_catchmask[signum] = sa->sa_mask &~ sigcantmask;
+ if ((sa->sa_flags & SA_RESTART) == 0)
+ ps->ps_sigintr |= bit;
+ else
+ ps->ps_sigintr &= ~bit;
+ if (sa->sa_flags & SA_ONSTACK)
+ ps->ps_sigonstack |= bit;
+ else
+ ps->ps_sigonstack &= ~bit;
+#ifdef COMPAT_SUNOS
+ if (sa->sa_flags & SA_USERTRAMP)
+ ps->ps_usertramp |= bit;
+ else
+ ps->ps_usertramp &= ~bit;
+#endif
+ if (signum == SIGCHLD) {
+ if (sa->sa_flags & SA_NOCLDSTOP)
+ p->p_flag |= P_NOCLDSTOP;
+ else
+ p->p_flag &= ~P_NOCLDSTOP;
+ }
+ /*
+ * Set bit in p_sigignore for signals that are set to SIG_IGN,
+ * and for signals set to SIG_DFL where the default is to ignore.
+ * However, don't put SIGCONT in p_sigignore,
+ * as we have to restart the process.
+ */
+ if (sa->sa_handler == SIG_IGN ||
+ (sigprop[signum] & SA_IGNORE && sa->sa_handler == SIG_DFL)) {
+ p->p_siglist &= ~bit; /* never to be seen again */
+ if (signum != SIGCONT)
+ p->p_sigignore |= bit; /* easier in psignal */
+ p->p_sigcatch &= ~bit;
+ } else {
+ p->p_sigignore &= ~bit;
+ if (sa->sa_handler == SIG_DFL)
+ p->p_sigcatch &= ~bit;
+ else
+ p->p_sigcatch |= bit;
+ }
+ (void) spl0();
+}
+
+/*
+ * Initialize signal state for process 0;
+ * set to ignore signals that are ignored by default.
+ */
+void
+siginit(p)
+ struct proc *p;
+{
+ register int i;
+
+ for (i = 0; i < NSIG; i++)
+ if (sigprop[i] & SA_IGNORE && i != SIGCONT)
+ p->p_sigignore |= sigmask(i);
+}
+
+/*
+ * Reset signals for an exec of the specified process.
+ */
+void
+execsigs(p)
+ register struct proc *p;
+{
+ register struct sigacts *ps = p->p_sigacts;
+ register int nc, mask;
+
+ /*
+ * Reset caught signals. Held signals remain held
+ * through p_sigmask (unless they were caught,
+ * and are now ignored by default).
+ */
+ while (p->p_sigcatch) {
+ nc = ffs((long)p->p_sigcatch);
+ mask = sigmask(nc);
+ p->p_sigcatch &= ~mask;
+ if (sigprop[nc] & SA_IGNORE) {
+ if (nc != SIGCONT)
+ p->p_sigignore |= mask;
+ p->p_siglist &= ~mask;
+ }
+ ps->ps_sigact[nc] = SIG_DFL;
+ }
+ /*
+ * Reset stack state to the user stack.
+ * Clear set of signals caught on the signal stack.
+ */
+ ps->ps_sigstk.ss_flags = SA_DISABLE;
+ ps->ps_sigstk.ss_size = 0;
+ ps->ps_sigstk.ss_base = 0;
+ ps->ps_flags = 0;
+}
+
+/*
+ * Manipulate signal mask.
+ * Note that we receive new mask, not pointer,
+ * and return old mask as return value;
+ * the library stub does the rest.
+ */
+struct sigprocmask_args {
+ int how;
+ sigset_t mask;
+};
+int
+sigprocmask(p, uap, retval)
+ register struct proc *p;
+ struct sigprocmask_args *uap;
+ int *retval;
+{
+ int error = 0;
+
+ *retval = p->p_sigmask;
+ (void) splhigh();
+
+ switch (uap->how) {
+ case SIG_BLOCK:
+ p->p_sigmask |= uap->mask &~ sigcantmask;
+ break;
+
+ case SIG_UNBLOCK:
+ p->p_sigmask &= ~uap->mask;
+ break;
+
+ case SIG_SETMASK:
+ p->p_sigmask = uap->mask &~ sigcantmask;
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ (void) spl0();
+ return (error);
+}
+
+struct sigpending_args {
+ int dummy;
+};
+/* ARGSUSED */
+int
+sigpending(p, uap, retval)
+ struct proc *p;
+ struct sigpending_args *uap;
+ int *retval;
+{
+
+ *retval = p->p_siglist;
+ return (0);
+}
+
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+/*
+ * Generalized interface signal handler, 4.3-compatible.
+ */
+struct osigvec_args {
+ int signum;
+ struct sigvec *nsv;
+ struct sigvec *osv;
+};
+/* ARGSUSED */
+int
+osigvec(p, uap, retval)
+ struct proc *p;
+ register struct osigvec_args *uap;
+ int *retval;
+{
+ struct sigvec vec;
+ register struct sigacts *ps = p->p_sigacts;
+ register struct sigvec *sv;
+ register int signum;
+ int bit, error;
+
+ signum = uap->signum;
+ if (signum <= 0 || signum >= NSIG ||
+ signum == SIGKILL || signum == SIGSTOP)
+ return (EINVAL);
+ sv = &vec;
+ if (uap->osv) {
+ *(sig_t *)&sv->sv_handler = ps->ps_sigact[signum];
+ sv->sv_mask = ps->ps_catchmask[signum];
+ bit = sigmask(signum);
+ sv->sv_flags = 0;
+ if ((ps->ps_sigonstack & bit) != 0)
+ sv->sv_flags |= SV_ONSTACK;
+ if ((ps->ps_sigintr & bit) != 0)
+ sv->sv_flags |= SV_INTERRUPT;
+#ifndef COMPAT_SUNOS
+ if (p->p_flag & P_NOCLDSTOP)
+ sv->sv_flags |= SA_NOCLDSTOP;
+#endif
+ if (error = copyout((caddr_t)sv, (caddr_t)uap->osv,
+ sizeof (vec)))
+ return (error);
+ }
+ if (uap->nsv) {
+ if (error = copyin((caddr_t)uap->nsv, (caddr_t)sv,
+ sizeof (vec)))
+ return (error);
+#ifdef COMPAT_SUNOS
+ /*
+ * SunOS uses this bit (4, aka SA_DISABLE) as SV_RESETHAND,
+ * `reset to SIG_DFL on delivery'. We have no such option
+ * now or ever!
+ */
+ if (sv->sv_flags & SA_DISABLE)
+ return (EINVAL);
+ sv->sv_flags |= SA_USERTRAMP;
+#endif
+ sv->sv_flags ^= SA_RESTART; /* opposite of SV_INTERRUPT */
+ setsigvec(p, signum, (struct sigaction *)sv);
+ }
+ return (0);
+}
+
+struct osigblock_args {
+ int mask;
+};
+int
+osigblock(p, uap, retval)
+ register struct proc *p;
+ struct osigblock_args *uap;
+ int *retval;
+{
+
+ (void) splhigh();
+ *retval = p->p_sigmask;
+ p->p_sigmask |= uap->mask &~ sigcantmask;
+ (void) spl0();
+ return (0);
+}
+
+struct osigsetmask_args {
+ int mask;
+};
+int
+osigsetmask(p, uap, retval)
+ struct proc *p;
+ struct osigsetmask_args *uap;
+ int *retval;
+{
+
+ (void) splhigh();
+ *retval = p->p_sigmask;
+ p->p_sigmask = uap->mask &~ sigcantmask;
+ (void) spl0();
+ return (0);
+}
+#endif /* COMPAT_43 || COMPAT_SUNOS */
+
+/*
+ * Suspend process until signal, providing mask to be set
+ * in the meantime. Note nonstandard calling convention:
+ * libc stub passes mask, not pointer, to save a copyin.
+ */
+struct sigsuspend_args {
+ sigset_t mask;
+};
+/* ARGSUSED */
+int
+sigsuspend(p, uap, retval)
+ register struct proc *p;
+ struct sigsuspend_args *uap;
+ int *retval;
+{
+ register struct sigacts *ps = p->p_sigacts;
+
+ /*
+ * When returning from sigpause, we want
+ * the old mask to be restored after the
+ * signal handler has finished. Thus, we
+ * save it here and mark the sigacts structure
+ * to indicate this.
+ */
+ ps->ps_oldmask = p->p_sigmask;
+ ps->ps_flags |= SAS_OLDMASK;
+ p->p_sigmask = uap->mask &~ sigcantmask;
+ while (tsleep((caddr_t) ps, PPAUSE|PCATCH, "pause", 0) == 0)
+ /* void */;
+ /* always return EINTR rather than ERESTART... */
+ return (EINTR);
+}
+
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+struct osigstack_args {
+ struct sigstack *nss;
+ struct sigstack *oss;
+};
+/* ARGSUSED */
+int
+osigstack(p, uap, retval)
+ struct proc *p;
+ register struct osigstack_args *uap;
+ int *retval;
+{
+ struct sigstack ss;
+ struct sigacts *psp;
+ int error = 0;
+
+ psp = p->p_sigacts;
+ ss.ss_sp = psp->ps_sigstk.ss_base;
+ ss.ss_onstack = psp->ps_sigstk.ss_flags & SA_ONSTACK;
+ if (uap->oss && (error = copyout((caddr_t)&ss, (caddr_t)uap->oss,
+ sizeof (struct sigstack))))
+ return (error);
+ if (uap->nss && (error = copyin((caddr_t)uap->nss, (caddr_t)&ss,
+ sizeof (ss))) == 0) {
+ psp->ps_sigstk.ss_base = ss.ss_sp;
+ psp->ps_sigstk.ss_size = 0;
+ psp->ps_sigstk.ss_flags |= ss.ss_onstack & SA_ONSTACK;
+ psp->ps_flags |= SAS_ALTSTACK;
+ }
+ return (error);
+}
+#endif /* COMPAT_43 || COMPAT_SUNOS */
+
+struct sigaltstack_args {
+ struct sigaltstack *nss;
+ struct sigaltstack *oss;
+};
+/* ARGSUSED */
+int
+sigaltstack(p, uap, retval)
+ struct proc *p;
+ register struct sigaltstack_args *uap;
+ int *retval;
+{
+ struct sigacts *psp;
+ struct sigaltstack ss;
+ int error;
+
+ psp = p->p_sigacts;
+ if ((psp->ps_flags & SAS_ALTSTACK) == 0)
+ psp->ps_sigstk.ss_flags |= SA_DISABLE;
+ if (uap->oss && (error = copyout((caddr_t)&psp->ps_sigstk,
+ (caddr_t)uap->oss, sizeof (struct sigaltstack))))
+ return (error);
+ if (uap->nss == 0)
+ return (0);
+ if (error = copyin((caddr_t)uap->nss, (caddr_t)&ss, sizeof (ss)))
+ return (error);
+ if (ss.ss_flags & SA_DISABLE) {
+ if (psp->ps_sigstk.ss_flags & SA_ONSTACK)
+ return (EINVAL);
+ psp->ps_flags &= ~SAS_ALTSTACK;
+ psp->ps_sigstk.ss_flags = ss.ss_flags;
+ return (0);
+ }
+ if (ss.ss_size < MINSIGSTKSZ)
+ return (ENOMEM);
+ psp->ps_flags |= SAS_ALTSTACK;
+ psp->ps_sigstk= ss;
+ return (0);
+}
+
+struct kill_args {
+ int pid;
+ int signum;
+};
+/* ARGSUSED */
+int
+kill(cp, uap, retval)
+ register struct proc *cp;
+ register struct kill_args *uap;
+ int *retval;
+{
+ register struct proc *p;
+ register struct pcred *pc = cp->p_cred;
+
+ if ((u_int)uap->signum >= NSIG)
+ return (EINVAL);
+ if (uap->pid > 0) {
+ /* kill single process */
+ if ((p = pfind(uap->pid)) == NULL)
+ return (ESRCH);
+ if (!CANSIGNAL(cp, pc, p, uap->signum))
+ return (EPERM);
+ if (uap->signum)
+ psignal(p, uap->signum);
+ return (0);
+ }
+ switch (uap->pid) {
+ case -1: /* broadcast signal */
+ return (killpg1(cp, uap->signum, 0, 1));
+ case 0: /* signal own process group */
+ return (killpg1(cp, uap->signum, 0, 0));
+ default: /* negative explicit process group */
+ return (killpg1(cp, uap->signum, -uap->pid, 0));
+ }
+ /* NOTREACHED */
+}
+
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+struct okillpg_args {
+ int pgid;
+ int signum;
+};
+/* ARGSUSED */
+int
+okillpg(p, uap, retval)
+ struct proc *p;
+ register struct okillpg_args *uap;
+ int *retval;
+{
+
+ if ((u_int)uap->signum >= NSIG)
+ return (EINVAL);
+ return (killpg1(p, uap->signum, uap->pgid, 0));
+}
+#endif /* COMPAT_43 || COMPAT_SUNOS */
+
+/*
+ * Common code for kill process group/broadcast kill.
+ * cp is calling process.
+ */
+int
+killpg1(cp, signum, pgid, all)
+ register struct proc *cp;
+ int signum, pgid, all;
+{
+ register struct proc *p;
+ register struct pcred *pc = cp->p_cred;
+ struct pgrp *pgrp;
+ int nfound = 0;
+
+ if (all)
+ /*
+ * broadcast
+ */
+ for (p = (struct proc *)allproc; p != NULL; p = p->p_next) {
+ if (p->p_pid <= 1 || p->p_flag & P_SYSTEM ||
+ p == cp || !CANSIGNAL(cp, pc, p, signum))
+ continue;
+ nfound++;
+ if (signum)
+ psignal(p, signum);
+ }
+ else {
+ if (pgid == 0)
+ /*
+ * zero pgid means send to my process group.
+ */
+ pgrp = cp->p_pgrp;
+ else {
+ pgrp = pgfind(pgid);
+ if (pgrp == NULL)
+ return (ESRCH);
+ }
+ for (p = pgrp->pg_mem; p != NULL; p = p->p_pgrpnxt) {
+ if (p->p_pid <= 1 || p->p_flag & P_SYSTEM ||
+ p->p_stat == SZOMB ||
+ !CANSIGNAL(cp, pc, p, signum))
+ continue;
+ nfound++;
+ if (signum)
+ psignal(p, signum);
+ }
+ }
+ return (nfound ? 0 : ESRCH);
+}
+
+/*
+ * Send a signal to a process group.
+ */
+void
+gsignal(pgid, signum)
+ int pgid, signum;
+{
+ struct pgrp *pgrp;
+
+ if (pgid && (pgrp = pgfind(pgid)))
+ pgsignal(pgrp, signum, 0);
+}
+
+/*
+ * Send a signal to a process group. If checktty is 1,
+ * limit to members which have a controlling terminal.
+ */
+void
+pgsignal(pgrp, signum, checkctty)
+ struct pgrp *pgrp;
+ int signum, checkctty;
+{
+ register struct proc *p;
+
+ if (pgrp)
+ for (p = pgrp->pg_mem; p != NULL; p = p->p_pgrpnxt)
+ if (checkctty == 0 || p->p_flag & P_CONTROLT)
+ psignal(p, signum);
+}
+
+/*
+ * Send a signal caused by a trap to the current process.
+ * If it will be caught immediately, deliver it with correct code.
+ * Otherwise, post it normally.
+ */
+void
+trapsignal(p, signum, code)
+ struct proc *p;
+ register int signum;
+ u_int code;
+{
+ register struct sigacts *ps = p->p_sigacts;
+ int mask;
+
+ mask = sigmask(signum);
+ if ((p->p_flag & P_TRACED) == 0 && (p->p_sigcatch & mask) != 0 &&
+ (p->p_sigmask & mask) == 0) {
+ p->p_stats->p_ru.ru_nsignals++;
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_PSIG))
+ ktrpsig(p->p_tracep, signum, ps->ps_sigact[signum],
+ p->p_sigmask, code);
+#endif
+ sendsig(ps->ps_sigact[signum], signum, p->p_sigmask, code);
+ p->p_sigmask |= ps->ps_catchmask[signum] | mask;
+ } else {
+ ps->ps_code = code; /* XXX for core dump/debugger */
+ psignal(p, signum);
+ }
+}
+
+/*
+ * Send the signal to the process. If the signal has an action, the action
+ * is usually performed by the target process rather than the caller; we add
+ * the signal to the set of pending signals for the process.
+ *
+ * Exceptions:
+ * o When a stop signal is sent to a sleeping process that takes the
+ * default action, the process is stopped without awakening it.
+ * o SIGCONT restarts stopped processes (or puts them back to sleep)
+ * regardless of the signal action (eg, blocked or ignored).
+ *
+ * Other ignored signals are discarded immediately.
+ */
+void
+psignal(p, signum)
+ register struct proc *p;
+ register int signum;
+{
+ register int s, prop;
+ register sig_t action;
+ int mask;
+
+ if ((u_int)signum >= NSIG || signum == 0)
+ panic("psignal signal number");
+ mask = sigmask(signum);
+ prop = sigprop[signum];
+
+ /*
+ * If proc is traced, always give parent a chance.
+ */
+ if (p->p_flag & P_TRACED)
+ action = SIG_DFL;
+ else {
+ /*
+ * If the signal is being ignored,
+ * then we forget about it immediately.
+ * (Note: we don't set SIGCONT in p_sigignore,
+ * and if it is set to SIG_IGN,
+ * action will be SIG_DFL here.)
+ */
+ if (p->p_sigignore & mask)
+ return;
+ if (p->p_sigmask & mask)
+ action = SIG_HOLD;
+ else if (p->p_sigcatch & mask)
+ action = SIG_CATCH;
+ else
+ action = SIG_DFL;
+ }
+
+ if (p->p_nice > NZERO && action == SIG_DFL && (prop & SA_KILL) &&
+ (p->p_flag & P_TRACED) == 0)
+ p->p_nice = NZERO;
+
+ if (prop & SA_CONT)
+ p->p_siglist &= ~stopsigmask;
+
+ if (prop & SA_STOP) {
+ /*
+ * If sending a tty stop signal to a member of an orphaned
+ * process group, discard the signal here if the action
+ * is default; don't stop the process below if sleeping,
+ * and don't clear any pending SIGCONT.
+ */
+ if (prop & SA_TTYSTOP && p->p_pgrp->pg_jobc == 0 &&
+ action == SIG_DFL)
+ return;
+ p->p_siglist &= ~contsigmask;
+ }
+ p->p_siglist |= mask;
+
+ /*
+ * Defer further processing for signals which are held,
+ * except that stopped processes must be continued by SIGCONT.
+ */
+ if (action == SIG_HOLD && ((prop & SA_CONT) == 0 || p->p_stat != SSTOP))
+ return;
+ s = splhigh();
+ switch (p->p_stat) {
+
+ case SSLEEP:
+ /*
+ * If process is sleeping uninterruptibly
+ * we can't interrupt the sleep... the signal will
+ * be noticed when the process returns through
+ * trap() or syscall().
+ */
+ if ((p->p_flag & P_SINTR) == 0)
+ goto out;
+ /*
+ * Process is sleeping and traced... make it runnable
+ * so it can discover the signal in issignal() and stop
+ * for the parent.
+ */
+ if (p->p_flag & P_TRACED)
+ goto run;
+ /*
+ * If SIGCONT is default (or ignored) and process is
+ * asleep, we are finished; the process should not
+ * be awakened.
+ */
+ if ((prop & SA_CONT) && action == SIG_DFL) {
+ p->p_siglist &= ~mask;
+ goto out;
+ }
+ /*
+ * When a sleeping process receives a stop
+ * signal, process immediately if possible.
+ * All other (caught or default) signals
+ * cause the process to run.
+ */
+ if (prop & SA_STOP) {
+ if (action != SIG_DFL)
+ goto runfast;
+ /*
+ * If a child holding parent blocked,
+ * stopping could cause deadlock.
+ */
+ if (p->p_flag & P_PPWAIT)
+ goto out;
+ p->p_siglist &= ~mask;
+ p->p_xstat = signum;
+ if ((p->p_pptr->p_flag & P_NOCLDSTOP) == 0)
+ psignal(p->p_pptr, SIGCHLD);
+ stop(p);
+ goto out;
+ } else
+ goto runfast;
+ /*NOTREACHED*/
+
+ case SSTOP:
+ /*
+ * If traced process is already stopped,
+ * then no further action is necessary.
+ */
+ if (p->p_flag & P_TRACED)
+ goto out;
+
+ /*
+ * Kill signal always sets processes running.
+ */
+ if (signum == SIGKILL)
+ goto runfast;
+
+ if (prop & SA_CONT) {
+ /*
+ * If SIGCONT is default (or ignored), we continue the
+ * process but don't leave the signal in p_siglist, as
+ * it has no further action. If SIGCONT is held, we
+ * continue the process and leave the signal in
+ * p_siglist. If the process catches SIGCONT, let it
+ * handle the signal itself. If it isn't waiting on
+ * an event, then it goes back to run state.
+ * Otherwise, process goes back to sleep state.
+ */
+ if (action == SIG_DFL)
+ p->p_siglist &= ~mask;
+ if (action == SIG_CATCH)
+ goto runfast;
+ if (p->p_wchan == 0)
+ goto run;
+ p->p_stat = SSLEEP;
+ goto out;
+ }
+
+ if (prop & SA_STOP) {
+ /*
+ * Already stopped, don't need to stop again.
+ * (If we did the shell could get confused.)
+ */
+ p->p_siglist &= ~mask; /* take it away */
+ goto out;
+ }
+
+ /*
+ * If process is sleeping interruptibly, then simulate a
+ * wakeup so that when it is continued, it will be made
+ * runnable and can look at the signal. But don't make
+ * the process runnable, leave it stopped.
+ */
+ if (p->p_wchan && p->p_flag & P_SINTR)
+ unsleep(p);
+ goto out;
+
+ default:
+ /*
+ * SRUN, SIDL, SZOMB do nothing with the signal,
+ * other than kicking ourselves if we are running.
+ * It will either never be noticed, or noticed very soon.
+ */
+ if (p == curproc)
+ signotify(p);
+ goto out;
+ }
+ /*NOTREACHED*/
+
+runfast:
+ /*
+ * Raise priority to at least PUSER.
+ */
+ if (p->p_priority > PUSER)
+ p->p_priority = PUSER;
+run:
+ setrunnable(p);
+out:
+ splx(s);
+}
+
+/*
+ * If the current process has received a signal (should be caught or cause
+ * termination, should interrupt current syscall), return the signal number.
+ * Stop signals with default action are processed immediately, then cleared;
+ * they aren't returned. This is checked after each entry to the system for
+ * a syscall or trap (though this can usually be done without calling issignal
+ * by checking the pending signal masks in the CURSIG macro.) The normal call
+ * sequence is
+ *
+ * while (signum = CURSIG(curproc))
+ * postsig(signum);
+ */
+int
+issignal(p)
+ register struct proc *p;
+{
+ register int signum, mask, prop;
+
+ for (;;) {
+ mask = p->p_siglist & ~p->p_sigmask;
+ if (p->p_flag & P_PPWAIT)
+ mask &= ~stopsigmask;
+ if (mask == 0) /* no signal to send */
+ return (0);
+ signum = ffs((long)mask);
+ mask = sigmask(signum);
+ prop = sigprop[signum];
+ /*
+ * We should see pending but ignored signals
+ * only if P_TRACED was on when they were posted.
+ */
+ if (mask & p->p_sigignore && (p->p_flag & P_TRACED) == 0) {
+ p->p_siglist &= ~mask;
+ continue;
+ }
+ if (p->p_flag & P_TRACED && (p->p_flag & P_PPWAIT) == 0) {
+ /*
+ * If traced, always stop, and stay
+ * stopped until released by the parent.
+ */
+ p->p_xstat = signum;
+ psignal(p->p_pptr, SIGCHLD);
+ do {
+ stop(p);
+ mi_switch();
+ } while (!trace_req(p) && p->p_flag & P_TRACED);
+
+ /*
+ * If the traced bit got turned off, go back up
+ * to the top to rescan signals. This ensures
+ * that p_sig* and ps_sigact are consistent.
+ */
+ if ((p->p_flag & P_TRACED) == 0)
+ continue;
+
+ /*
+ * If parent wants us to take the signal,
+ * then it will leave it in p->p_xstat;
+ * otherwise we just look for signals again.
+ */
+ p->p_siglist &= ~mask; /* clear the old signal */
+ signum = p->p_xstat;
+ if (signum == 0)
+ continue;
+
+ /*
+ * Put the new signal into p_siglist. If the
+ * signal is being masked, look for other signals.
+ */
+ mask = sigmask(signum);
+ p->p_siglist |= mask;
+ if (p->p_sigmask & mask)
+ continue;
+ }
+
+ /*
+ * Decide whether the signal should be returned.
+ * Return the signal's number, or fall through
+ * to clear it from the pending mask.
+ */
+ switch ((int)p->p_sigacts->ps_sigact[signum]) {
+
+ case SIG_DFL:
+ /*
+ * Don't take default actions on system processes.
+ */
+ if (p->p_pid <= 1) {
+#ifdef DIAGNOSTIC
+ /*
+ * Are you sure you want to ignore SIGSEGV
+ * in init? XXX
+ */
+ printf("Process (pid %d) got signal %d\n",
+ p->p_pid, signum);
+#endif
+ break; /* == ignore */
+ }
+ /*
+ * If there is a pending stop signal to process
+ * with default action, stop here,
+ * then clear the signal. However,
+ * if process is member of an orphaned
+ * process group, ignore tty stop signals.
+ */
+ if (prop & SA_STOP) {
+ if (p->p_flag & P_TRACED ||
+ (p->p_pgrp->pg_jobc == 0 &&
+ prop & SA_TTYSTOP))
+ break; /* == ignore */
+ p->p_xstat = signum;
+ stop(p);
+ if ((p->p_pptr->p_flag & P_NOCLDSTOP) == 0)
+ psignal(p->p_pptr, SIGCHLD);
+ mi_switch();
+ break;
+ } else if (prop & SA_IGNORE) {
+ /*
+ * Except for SIGCONT, shouldn't get here.
+ * Default action is to ignore; drop it.
+ */
+ break; /* == ignore */
+ } else
+ return (signum);
+ /*NOTREACHED*/
+
+ case SIG_IGN:
+ /*
+ * Masking above should prevent us ever trying
+ * to take action on an ignored signal other
+ * than SIGCONT, unless process is traced.
+ */
+ if ((prop & SA_CONT) == 0 &&
+ (p->p_flag & P_TRACED) == 0)
+ printf("issignal\n");
+ break; /* == ignore */
+
+ default:
+ /*
+ * This signal has an action, let
+ * postsig() process it.
+ */
+ return (signum);
+ }
+ p->p_siglist &= ~mask; /* take the signal! */
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Put the argument process into the stopped state and notify the parent
+ * via wakeup. Signals are handled elsewhere. The process must not be
+ * on the run queue.
+ */
+void
+stop(p)
+ register struct proc *p;
+{
+
+ p->p_stat = SSTOP;
+ p->p_flag &= ~P_WAITED;
+ wakeup((caddr_t)p->p_pptr);
+}
+
+/*
+ * Take the action for the specified signal
+ * from the current set of pending signals.
+ */
+void
+postsig(signum)
+ register int signum;
+{
+ register struct proc *p = curproc;
+ register struct sigacts *ps = p->p_sigacts;
+ register sig_t action;
+ int code, mask, returnmask;
+
+#ifdef DIAGNOSTIC
+ if (signum == 0)
+ panic("postsig");
+#endif
+ mask = sigmask(signum);
+ p->p_siglist &= ~mask;
+ action = ps->ps_sigact[signum];
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_PSIG))
+ ktrpsig(p->p_tracep,
+ signum, action, ps->ps_flags & SAS_OLDMASK ?
+ ps->ps_oldmask : p->p_sigmask, 0);
+#endif
+ if (action == SIG_DFL) {
+ /*
+ * Default action, where the default is to kill
+ * the process. (Other cases were ignored above.)
+ */
+ sigexit(p, signum);
+ /* NOTREACHED */
+ } else {
+ /*
+ * If we get here, the signal must be caught.
+ */
+#ifdef DIAGNOSTIC
+ if (action == SIG_IGN || (p->p_sigmask & mask))
+ panic("postsig action");
+#endif
+ /*
+ * Set the new mask value and also defer further
+ * occurences of this signal.
+ *
+ * Special case: user has done a sigpause. Here the
+ * current mask is not of interest, but rather the
+ * mask from before the sigpause is what we want
+ * restored after the signal processing is completed.
+ */
+ (void) splhigh();
+ if (ps->ps_flags & SAS_OLDMASK) {
+ returnmask = ps->ps_oldmask;
+ ps->ps_flags &= ~SAS_OLDMASK;
+ } else
+ returnmask = p->p_sigmask;
+ p->p_sigmask |= ps->ps_catchmask[signum] | mask;
+ (void) spl0();
+ p->p_stats->p_ru.ru_nsignals++;
+ if (ps->ps_sig != signum) {
+ code = 0;
+ } else {
+ code = ps->ps_code;
+ ps->ps_code = 0;
+ }
+ sendsig(action, signum, returnmask, code);
+ }
+}
+
+/*
+ * Kill the current process for stated reason.
+ */
+void
+killproc(p, why)
+ struct proc *p;
+ char *why;
+{
+
+ log(LOG_ERR, "pid %d was killed: %s\n", p->p_pid, why);
+ uprintf("sorry, pid %d was killed: %s\n", p->p_pid, why);
+ psignal(p, SIGKILL);
+}
+
+/*
+ * Force the current process to exit with the specified signal, dumping core
+ * if appropriate. We bypass the normal tests for masked and caught signals,
+ * allowing unrecoverable failures to terminate the process without changing
+ * signal state. Mark the accounting record with the signal termination.
+ * If dumping core, save the signal number for the debugger. Calls exit and
+ * does not return.
+ */
+void
+sigexit(p, signum)
+ register struct proc *p;
+ int signum;
+{
+
+ p->p_acflag |= AXSIG;
+ if (sigprop[signum] & SA_CORE) {
+ p->p_sigacts->ps_sig = signum;
+ if (coredump(p) == 0)
+ signum |= WCOREFLAG;
+ }
+ exit1(p, W_EXITCODE(0, signum));
+ /* NOTREACHED */
+}
+
+/*
+ * Dump core, into a file named "progname.core", unless the process was
+ * setuid/setgid.
+ */
+int
+coredump(p)
+ register struct proc *p;
+{
+ register struct vnode *vp;
+ register struct pcred *pcred = p->p_cred;
+ register struct ucred *cred = pcred->pc_ucred;
+ register struct vmspace *vm = p->p_vmspace;
+ struct nameidata nd;
+ struct vattr vattr;
+ int error, error1;
+ char name[MAXCOMLEN+6]; /* progname.core */
+
+ if (pcred->p_svuid != pcred->p_ruid || pcred->p_svgid != pcred->p_rgid)
+ return (EFAULT);
+ if (ctob(UPAGES + vm->vm_dsize + vm->vm_ssize) >=
+ p->p_rlimit[RLIMIT_CORE].rlim_cur)
+ return (EFAULT);
+ sprintf(name, "%s.core", p->p_comm);
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, name, p);
+ if (error = vn_open(&nd,
+ O_CREAT | FWRITE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))
+ return (error);
+ vp = nd.ni_vp;
+
+ /* Don't dump to non-regular files or files with links. */
+ if (vp->v_type != VREG ||
+ VOP_GETATTR(vp, &vattr, cred, p) || vattr.va_nlink != 1) {
+ error = EFAULT;
+ goto out;
+ }
+ VATTR_NULL(&vattr);
+ vattr.va_size = 0;
+ LEASE_CHECK(vp, p, cred, LEASE_WRITE);
+ VOP_SETATTR(vp, &vattr, cred, p);
+ p->p_acflag |= ACORE;
+ bcopy(p, &p->p_addr->u_kproc.kp_proc, sizeof(struct proc));
+ fill_eproc(p, &p->p_addr->u_kproc.kp_eproc);
+ error = cpu_coredump(p, vp, cred);
+ if (error == 0)
+ error = vn_rdwr(UIO_WRITE, vp, vm->vm_daddr,
+ (int)ctob(vm->vm_dsize), (off_t)ctob(UPAGES), UIO_USERSPACE,
+ IO_NODELOCKED|IO_UNIT, cred, (int *) NULL, p);
+ if (error == 0)
+ error = vn_rdwr(UIO_WRITE, vp,
+ (caddr_t) trunc_page(USRSTACK - ctob(vm->vm_ssize)),
+ round_page(ctob(vm->vm_ssize)),
+ (off_t)ctob(UPAGES) + ctob(vm->vm_dsize), UIO_USERSPACE,
+ IO_NODELOCKED|IO_UNIT, cred, (int *) NULL, p);
+out:
+ VOP_UNLOCK(vp);
+ error1 = vn_close(vp, FWRITE, cred, p);
+ if (error == 0)
+ error = error1;
+ return (error);
+}
+
+/*
+ * Nonexistent system call-- signal process (may want to handle it).
+ * Flag error in case process won't see signal immediately (blocked or ignored).
+ */
+struct nosys_args {
+ int dummy;
+};
+/* ARGSUSED */
+int
+nosys(p, args, retval)
+ struct proc *p;
+ struct nosys_args *args;
+ int *retval;
+{
+
+ psignal(p, SIGSYS);
+ return (EINVAL);
+}
diff --git a/sys/kern/kern_subr.c b/sys/kern/kern_subr.c
new file mode 100644
index 0000000..8eb4dee
--- /dev/null
+++ b/sys/kern/kern_subr.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_subr.c 8.3 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+
+int
+uiomove(cp, n, uio)
+ register caddr_t cp;
+ register int n;
+ register struct uio *uio;
+{
+ register struct iovec *iov;
+ u_int cnt;
+ int error = 0;
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_READ && uio->uio_rw != UIO_WRITE)
+ panic("uiomove: mode");
+ if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
+ panic("uiomove proc");
+#endif
+ while (n > 0 && uio->uio_resid) {
+ iov = uio->uio_iov;
+ cnt = iov->iov_len;
+ if (cnt == 0) {
+ uio->uio_iov++;
+ uio->uio_iovcnt--;
+ continue;
+ }
+ if (cnt > n)
+ cnt = n;
+ switch (uio->uio_segflg) {
+
+ case UIO_USERSPACE:
+ case UIO_USERISPACE:
+ if (uio->uio_rw == UIO_READ)
+ error = copyout(cp, iov->iov_base, cnt);
+ else
+ error = copyin(iov->iov_base, cp, cnt);
+ if (error)
+ return (error);
+ break;
+
+ case UIO_SYSSPACE:
+ if (uio->uio_rw == UIO_READ)
+ bcopy((caddr_t)cp, iov->iov_base, cnt);
+ else
+ bcopy(iov->iov_base, (caddr_t)cp, cnt);
+ break;
+ }
+ iov->iov_base += cnt;
+ iov->iov_len -= cnt;
+ uio->uio_resid -= cnt;
+ uio->uio_offset += cnt;
+ cp += cnt;
+ n -= cnt;
+ }
+ return (error);
+}
+
+/*
+ * Give next character to user as result of read.
+ */
+int
+ureadc(c, uio)
+ register int c;
+ register struct uio *uio;
+{
+ register struct iovec *iov;
+
+again:
+ if (uio->uio_iovcnt == 0 || uio->uio_resid == 0)
+ panic("ureadc");
+ iov = uio->uio_iov;
+ if (iov->iov_len == 0) {
+ uio->uio_iovcnt--;
+ uio->uio_iov++;
+ goto again;
+ }
+ switch (uio->uio_segflg) {
+
+ case UIO_USERSPACE:
+ if (subyte(iov->iov_base, c) < 0)
+ return (EFAULT);
+ break;
+
+ case UIO_SYSSPACE:
+ *iov->iov_base = c;
+ break;
+
+ case UIO_USERISPACE:
+ if (suibyte(iov->iov_base, c) < 0)
+ return (EFAULT);
+ break;
+ }
+ iov->iov_base++;
+ iov->iov_len--;
+ uio->uio_resid--;
+ uio->uio_offset++;
+ return (0);
+}
+
+#ifdef vax /* unused except by ct.c, other oddities XXX */
+/*
+ * Get next character written in by user from uio.
+ */
+int
+uwritec(uio)
+ struct uio *uio;
+{
+ register struct iovec *iov;
+ register int c;
+
+ if (uio->uio_resid <= 0)
+ return (-1);
+again:
+ if (uio->uio_iovcnt <= 0)
+ panic("uwritec");
+ iov = uio->uio_iov;
+ if (iov->iov_len == 0) {
+ uio->uio_iov++;
+ if (--uio->uio_iovcnt == 0)
+ return (-1);
+ goto again;
+ }
+ switch (uio->uio_segflg) {
+
+ case UIO_USERSPACE:
+ c = fubyte(iov->iov_base);
+ break;
+
+ case UIO_SYSSPACE:
+ c = *(u_char *) iov->iov_base;
+ break;
+
+ case UIO_USERISPACE:
+ c = fuibyte(iov->iov_base);
+ break;
+ }
+ if (c < 0)
+ return (-1);
+ iov->iov_base++;
+ iov->iov_len--;
+ uio->uio_resid--;
+ uio->uio_offset++;
+ return (c);
+}
+#endif /* vax */
+
+/*
+ * General routine to allocate a hash table.
+ */
+void *
+hashinit(elements, type, hashmask)
+ int elements, type;
+ u_long *hashmask;
+{
+ long hashsize;
+ LIST_HEAD(generic, generic) *hashtbl;
+ int i;
+
+ if (elements <= 0)
+ panic("hashinit: bad cnt");
+ for (hashsize = 1; hashsize <= elements; hashsize <<= 1)
+ continue;
+ hashsize >>= 1;
+ hashtbl = malloc((u_long)hashsize * sizeof(*hashtbl), type, M_WAITOK);
+ for (i = 0; i < hashsize; i++)
+ LIST_INIT(&hashtbl[i]);
+ *hashmask = hashsize - 1;
+ return (hashtbl);
+}
diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c
new file mode 100644
index 0000000..a299dba
--- /dev/null
+++ b/sys/kern/kern_synch.c
@@ -0,0 +1,667 @@
+/*-
+ * Copyright (c) 1982, 1986, 1990, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_synch.c 8.6 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/buf.h>
+#include <sys/signalvar.h>
+#include <sys/resourcevar.h>
+#include <sys/vmmeter.h>
+#ifdef KTRACE
+#include <sys/ktrace.h>
+#endif
+
+#include <machine/cpu.h>
+
+u_char curpriority; /* usrpri of curproc */
+int lbolt; /* once a second sleep address */
+
+/*
+ * Force switch among equal priority processes every 100ms.
+ */
+/* ARGSUSED */
+void
+roundrobin(arg)
+ void *arg;
+{
+
+ need_resched();
+ timeout(roundrobin, NULL, hz / 10);
+}
+
+/*
+ * Constants for digital decay and forget:
+ * 90% of (p_estcpu) usage in 5 * loadav time
+ * 95% of (p_pctcpu) usage in 60 seconds (load insensitive)
+ * Note that, as ps(1) mentions, this can let percentages
+ * total over 100% (I've seen 137.9% for 3 processes).
+ *
+ * Note that hardclock updates p_estcpu and p_cpticks independently.
+ *
+ * We wish to decay away 90% of p_estcpu in (5 * loadavg) seconds.
+ * That is, the system wants to compute a value of decay such
+ * that the following for loop:
+ * for (i = 0; i < (5 * loadavg); i++)
+ * p_estcpu *= decay;
+ * will compute
+ * p_estcpu *= 0.1;
+ * for all values of loadavg:
+ *
+ * Mathematically this loop can be expressed by saying:
+ * decay ** (5 * loadavg) ~= .1
+ *
+ * The system computes decay as:
+ * decay = (2 * loadavg) / (2 * loadavg + 1)
+ *
+ * We wish to prove that the system's computation of decay
+ * will always fulfill the equation:
+ * decay ** (5 * loadavg) ~= .1
+ *
+ * If we compute b as:
+ * b = 2 * loadavg
+ * then
+ * decay = b / (b + 1)
+ *
+ * We now need to prove two things:
+ * 1) Given factor ** (5 * loadavg) ~= .1, prove factor == b/(b+1)
+ * 2) Given b/(b+1) ** power ~= .1, prove power == (5 * loadavg)
+ *
+ * Facts:
+ * For x close to zero, exp(x) =~ 1 + x, since
+ * exp(x) = 0! + x**1/1! + x**2/2! + ... .
+ * therefore exp(-1/b) =~ 1 - (1/b) = (b-1)/b.
+ * For x close to zero, ln(1+x) =~ x, since
+ * ln(1+x) = x - x**2/2 + x**3/3 - ... -1 < x < 1
+ * therefore ln(b/(b+1)) = ln(1 - 1/(b+1)) =~ -1/(b+1).
+ * ln(.1) =~ -2.30
+ *
+ * Proof of (1):
+ * Solve (factor)**(power) =~ .1 given power (5*loadav):
+ * solving for factor,
+ * ln(factor) =~ (-2.30/5*loadav), or
+ * factor =~ exp(-1/((5/2.30)*loadav)) =~ exp(-1/(2*loadav)) =
+ * exp(-1/b) =~ (b-1)/b =~ b/(b+1). QED
+ *
+ * Proof of (2):
+ * Solve (factor)**(power) =~ .1 given factor == (b/(b+1)):
+ * solving for power,
+ * power*ln(b/(b+1)) =~ -2.30, or
+ * power =~ 2.3 * (b + 1) = 4.6*loadav + 2.3 =~ 5*loadav. QED
+ *
+ * Actual power values for the implemented algorithm are as follows:
+ * loadav: 1 2 3 4
+ * power: 5.68 10.32 14.94 19.55
+ */
+
+/* calculations for digital decay to forget 90% of usage in 5*loadav sec */
+#define loadfactor(loadav) (2 * (loadav))
+#define decay_cpu(loadfac, cpu) (((loadfac) * (cpu)) / ((loadfac) + FSCALE))
+
+/* decay 95% of `p_pctcpu' in 60 seconds; see CCPU_SHIFT before changing */
+fixpt_t ccpu = 0.95122942450071400909 * FSCALE; /* exp(-1/20) */
+
+/*
+ * If `ccpu' is not equal to `exp(-1/20)' and you still want to use the
+ * faster/more-accurate formula, you'll have to estimate CCPU_SHIFT below
+ * and possibly adjust FSHIFT in "param.h" so that (FSHIFT >= CCPU_SHIFT).
+ *
+ * To estimate CCPU_SHIFT for exp(-1/20), the following formula was used:
+ * 1 - exp(-1/20) ~= 0.0487 ~= 0.0488 == 1 (fixed pt, *11* bits).
+ *
+ * If you dont want to bother with the faster/more-accurate formula, you
+ * can set CCPU_SHIFT to (FSHIFT + 1) which will use a slower/less-accurate
+ * (more general) method of calculating the %age of CPU used by a process.
+ */
+#define CCPU_SHIFT 11
+
+/*
+ * Recompute process priorities, every hz ticks.
+ */
+/* ARGSUSED */
+void
+schedcpu(arg)
+ void *arg;
+{
+ register fixpt_t loadfac = loadfactor(averunnable.ldavg[0]);
+ register struct proc *p;
+ register int s;
+ register unsigned int newcpu;
+
+ wakeup((caddr_t)&lbolt);
+ for (p = (struct proc *)allproc; p != NULL; p = p->p_next) {
+ /*
+ * Increment time in/out of memory and sleep time
+ * (if sleeping). We ignore overflow; with 16-bit int's
+ * (remember them?) overflow takes 45 days.
+ */
+ p->p_swtime++;
+ if (p->p_stat == SSLEEP || p->p_stat == SSTOP)
+ p->p_slptime++;
+ p->p_pctcpu = (p->p_pctcpu * ccpu) >> FSHIFT;
+ /*
+ * If the process has slept the entire second,
+ * stop recalculating its priority until it wakes up.
+ */
+ if (p->p_slptime > 1)
+ continue;
+ s = splstatclock(); /* prevent state changes */
+ /*
+ * p_pctcpu is only for ps.
+ */
+#if (FSHIFT >= CCPU_SHIFT)
+ p->p_pctcpu += (hz == 100)?
+ ((fixpt_t) p->p_cpticks) << (FSHIFT - CCPU_SHIFT):
+ 100 * (((fixpt_t) p->p_cpticks)
+ << (FSHIFT - CCPU_SHIFT)) / hz;
+#else
+ p->p_pctcpu += ((FSCALE - ccpu) *
+ (p->p_cpticks * FSCALE / hz)) >> FSHIFT;
+#endif
+ p->p_cpticks = 0;
+ newcpu = (u_int) decay_cpu(loadfac, p->p_estcpu) + p->p_nice;
+ p->p_estcpu = min(newcpu, UCHAR_MAX);
+ resetpriority(p);
+ if (p->p_priority >= PUSER) {
+#define PPQ (128 / NQS) /* priorities per queue */
+ if ((p != curproc) &&
+ p->p_stat == SRUN &&
+ (p->p_flag & P_INMEM) &&
+ (p->p_priority / PPQ) != (p->p_usrpri / PPQ)) {
+ remrq(p);
+ p->p_priority = p->p_usrpri;
+ setrunqueue(p);
+ } else
+ p->p_priority = p->p_usrpri;
+ }
+ splx(s);
+ }
+ vmmeter();
+ if (bclnlist != NULL)
+ wakeup((caddr_t)pageproc);
+ timeout(schedcpu, (void *)0, hz);
+}
+
+/*
+ * Recalculate the priority of a process after it has slept for a while.
+ * For all load averages >= 1 and max p_estcpu of 255, sleeping for at
+ * least six times the loadfactor will decay p_estcpu to zero.
+ */
+void
+updatepri(p)
+ register struct proc *p;
+{
+ register unsigned int newcpu = p->p_estcpu;
+ register fixpt_t loadfac = loadfactor(averunnable.ldavg[0]);
+
+ if (p->p_slptime > 5 * loadfac)
+ p->p_estcpu = 0;
+ else {
+ p->p_slptime--; /* the first time was done in schedcpu */
+ while (newcpu && --p->p_slptime)
+ newcpu = (int) decay_cpu(loadfac, newcpu);
+ p->p_estcpu = min(newcpu, UCHAR_MAX);
+ }
+ resetpriority(p);
+}
+
+/*
+ * We're only looking at 7 bits of the address; everything is
+ * aligned to 4, lots of things are aligned to greater powers
+ * of 2. Shift right by 8, i.e. drop the bottom 256 worth.
+ */
+#define TABLESIZE 128
+#define LOOKUP(x) (((int)(x) >> 8) & (TABLESIZE - 1))
+struct slpque {
+ struct proc *sq_head;
+ struct proc **sq_tailp;
+} slpque[TABLESIZE];
+
+/*
+ * During autoconfiguration or after a panic, a sleep will simply
+ * lower the priority briefly to allow interrupts, then return.
+ * The priority to be used (safepri) is machine-dependent, thus this
+ * value is initialized and maintained in the machine-dependent layers.
+ * This priority will typically be 0, or the lowest priority
+ * that is safe for use on the interrupt stack; it can be made
+ * higher to block network software interrupts after panics.
+ */
+int safepri;
+
+/*
+ * General sleep call. Suspends the current process until a wakeup is
+ * performed on the specified identifier. The process will then be made
+ * runnable with the specified priority. Sleeps at most timo/hz seconds
+ * (0 means no timeout). If pri includes PCATCH flag, signals are checked
+ * before and after sleeping, else signals are not checked. Returns 0 if
+ * awakened, EWOULDBLOCK if the timeout expires. If PCATCH is set and a
+ * signal needs to be delivered, ERESTART is returned if the current system
+ * call should be restarted if possible, and EINTR is returned if the system
+ * call should be interrupted by the signal (return EINTR).
+ */
+int
+tsleep(ident, priority, wmesg, timo)
+ void *ident;
+ int priority, timo;
+ char *wmesg;
+{
+ register struct proc *p = curproc;
+ register struct slpque *qp;
+ register s;
+ int sig, catch = priority & PCATCH;
+ extern int cold;
+ void endtsleep __P((void *));
+
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_CSW))
+ ktrcsw(p->p_tracep, 1, 0);
+#endif
+ s = splhigh();
+ if (cold || panicstr) {
+ /*
+ * After a panic, or during autoconfiguration,
+ * just give interrupts a chance, then just return;
+ * don't run any other procs or panic below,
+ * in case this is the idle process and already asleep.
+ */
+ splx(safepri);
+ splx(s);
+ return (0);
+ }
+#ifdef DIAGNOSTIC
+ if (ident == NULL || p->p_stat != SRUN || p->p_back)
+ panic("tsleep");
+#endif
+ p->p_wchan = ident;
+ p->p_wmesg = wmesg;
+ p->p_slptime = 0;
+ p->p_priority = priority & PRIMASK;
+ qp = &slpque[LOOKUP(ident)];
+ if (qp->sq_head == 0)
+ qp->sq_head = p;
+ else
+ *qp->sq_tailp = p;
+ *(qp->sq_tailp = &p->p_forw) = 0;
+ if (timo)
+ timeout(endtsleep, (void *)p, timo);
+ /*
+ * We put ourselves on the sleep queue and start our timeout
+ * before calling CURSIG, as we could stop there, and a wakeup
+ * or a SIGCONT (or both) could occur while we were stopped.
+ * A SIGCONT would cause us to be marked as SSLEEP
+ * without resuming us, thus we must be ready for sleep
+ * when CURSIG is called. If the wakeup happens while we're
+ * stopped, p->p_wchan will be 0 upon return from CURSIG.
+ */
+ if (catch) {
+ p->p_flag |= P_SINTR;
+ if (sig = CURSIG(p)) {
+ if (p->p_wchan)
+ unsleep(p);
+ p->p_stat = SRUN;
+ goto resume;
+ }
+ if (p->p_wchan == 0) {
+ catch = 0;
+ goto resume;
+ }
+ } else
+ sig = 0;
+ p->p_stat = SSLEEP;
+ p->p_stats->p_ru.ru_nvcsw++;
+ mi_switch();
+resume:
+ curpriority = p->p_usrpri;
+ splx(s);
+ p->p_flag &= ~P_SINTR;
+ if (p->p_flag & P_TIMEOUT) {
+ p->p_flag &= ~P_TIMEOUT;
+ if (sig == 0) {
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_CSW))
+ ktrcsw(p->p_tracep, 0, 0);
+#endif
+ return (EWOULDBLOCK);
+ }
+ } else if (timo)
+ untimeout(endtsleep, (void *)p);
+ if (catch && (sig != 0 || (sig = CURSIG(p)))) {
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_CSW))
+ ktrcsw(p->p_tracep, 0, 0);
+#endif
+ if (p->p_sigacts->ps_sigintr & sigmask(sig))
+ return (EINTR);
+ return (ERESTART);
+ }
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_CSW))
+ ktrcsw(p->p_tracep, 0, 0);
+#endif
+ return (0);
+}
+
+/*
+ * Implement timeout for tsleep.
+ * If process hasn't been awakened (wchan non-zero),
+ * set timeout flag and undo the sleep. If proc
+ * is stopped, just unsleep so it will remain stopped.
+ */
+void
+endtsleep(arg)
+ void *arg;
+{
+ register struct proc *p;
+ int s;
+
+ p = (struct proc *)arg;
+ s = splhigh();
+ if (p->p_wchan) {
+ if (p->p_stat == SSLEEP)
+ setrunnable(p);
+ else
+ unsleep(p);
+ p->p_flag |= P_TIMEOUT;
+ }
+ splx(s);
+}
+
+/*
+ * Short-term, non-interruptable sleep.
+ */
+void
+sleep(ident, priority)
+ void *ident;
+ int priority;
+{
+ register struct proc *p = curproc;
+ register struct slpque *qp;
+ register s;
+ extern int cold;
+
+#ifdef DIAGNOSTIC
+ if (priority > PZERO) {
+ printf("sleep called with priority %d > PZERO, wchan: %x\n",
+ priority, ident);
+ panic("old sleep");
+ }
+#endif
+ s = splhigh();
+ if (cold || panicstr) {
+ /*
+ * After a panic, or during autoconfiguration,
+ * just give interrupts a chance, then just return;
+ * don't run any other procs or panic below,
+ * in case this is the idle process and already asleep.
+ */
+ splx(safepri);
+ splx(s);
+ return;
+ }
+#ifdef DIAGNOSTIC
+ if (ident == NULL || p->p_stat != SRUN || p->p_back)
+ panic("sleep");
+#endif
+ p->p_wchan = ident;
+ p->p_wmesg = NULL;
+ p->p_slptime = 0;
+ p->p_priority = priority;
+ qp = &slpque[LOOKUP(ident)];
+ if (qp->sq_head == 0)
+ qp->sq_head = p;
+ else
+ *qp->sq_tailp = p;
+ *(qp->sq_tailp = &p->p_forw) = 0;
+ p->p_stat = SSLEEP;
+ p->p_stats->p_ru.ru_nvcsw++;
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_CSW))
+ ktrcsw(p->p_tracep, 1, 0);
+#endif
+ mi_switch();
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_CSW))
+ ktrcsw(p->p_tracep, 0, 0);
+#endif
+ curpriority = p->p_usrpri;
+ splx(s);
+}
+
+/*
+ * Remove a process from its wait queue
+ */
+void
+unsleep(p)
+ register struct proc *p;
+{
+ register struct slpque *qp;
+ register struct proc **hp;
+ int s;
+
+ s = splhigh();
+ if (p->p_wchan) {
+ hp = &(qp = &slpque[LOOKUP(p->p_wchan)])->sq_head;
+ while (*hp != p)
+ hp = &(*hp)->p_forw;
+ *hp = p->p_forw;
+ if (qp->sq_tailp == &p->p_forw)
+ qp->sq_tailp = hp;
+ p->p_wchan = 0;
+ }
+ splx(s);
+}
+
+/*
+ * Make all processes sleeping on the specified identifier runnable.
+ */
+void
+wakeup(ident)
+ register void *ident;
+{
+ register struct slpque *qp;
+ register struct proc *p, **q;
+ int s;
+
+ s = splhigh();
+ qp = &slpque[LOOKUP(ident)];
+restart:
+ for (q = &qp->sq_head; p = *q; ) {
+#ifdef DIAGNOSTIC
+ if (p->p_back || p->p_stat != SSLEEP && p->p_stat != SSTOP)
+ panic("wakeup");
+#endif
+ if (p->p_wchan == ident) {
+ p->p_wchan = 0;
+ *q = p->p_forw;
+ if (qp->sq_tailp == &p->p_forw)
+ qp->sq_tailp = q;
+ if (p->p_stat == SSLEEP) {
+ /* OPTIMIZED EXPANSION OF setrunnable(p); */
+ if (p->p_slptime > 1)
+ updatepri(p);
+ p->p_slptime = 0;
+ p->p_stat = SRUN;
+ if (p->p_flag & P_INMEM)
+ setrunqueue(p);
+ /*
+ * Since curpriority is a user priority,
+ * p->p_priority is always better than
+ * curpriority.
+ */
+ if ((p->p_flag & P_INMEM) == 0)
+ wakeup((caddr_t)&proc0);
+ else
+ need_resched();
+ /* END INLINE EXPANSION */
+ goto restart;
+ }
+ } else
+ q = &p->p_forw;
+ }
+ splx(s);
+}
+
+/*
+ * The machine independent parts of mi_switch().
+ * Must be called at splstatclock() or higher.
+ */
+void
+mi_switch()
+{
+ register struct proc *p = curproc; /* XXX */
+ register struct rlimit *rlim;
+ register long s, u;
+ struct timeval tv;
+
+ /*
+ * Compute the amount of time during which the current
+ * process was running, and add that to its total so far.
+ */
+ microtime(&tv);
+ u = p->p_rtime.tv_usec + (tv.tv_usec - runtime.tv_usec);
+ s = p->p_rtime.tv_sec + (tv.tv_sec - runtime.tv_sec);
+ if (u < 0) {
+ u += 1000000;
+ s--;
+ } else if (u >= 1000000) {
+ u -= 1000000;
+ s++;
+ }
+ p->p_rtime.tv_usec = u;
+ p->p_rtime.tv_sec = s;
+
+ /*
+ * Check if the process exceeds its cpu resource allocation.
+ * If over max, kill it. In any case, if it has run for more
+ * than 10 minutes, reduce priority to give others a chance.
+ */
+ rlim = &p->p_rlimit[RLIMIT_CPU];
+ if (s >= rlim->rlim_cur) {
+ if (s >= rlim->rlim_max)
+ psignal(p, SIGKILL);
+ else {
+ psignal(p, SIGXCPU);
+ if (rlim->rlim_cur < rlim->rlim_max)
+ rlim->rlim_cur += 5;
+ }
+ }
+ if (s > 10 * 60 && p->p_ucred->cr_uid && p->p_nice == NZERO) {
+ p->p_nice = NZERO + 4;
+ resetpriority(p);
+ }
+
+ /*
+ * Pick a new current process and record its start time.
+ */
+ cnt.v_swtch++;
+ cpu_switch(p);
+ microtime(&runtime);
+}
+
+/*
+ * Initialize the (doubly-linked) run queues
+ * to be empty.
+ */
+void
+rqinit()
+{
+ register int i;
+
+ for (i = 0; i < NQS; i++)
+ qs[i].ph_link = qs[i].ph_rlink = (struct proc *)&qs[i];
+}
+
+/*
+ * Change process state to be runnable,
+ * placing it on the run queue if it is in memory,
+ * and awakening the swapper if it isn't in memory.
+ */
+void
+setrunnable(p)
+ register struct proc *p;
+{
+ register int s;
+
+ s = splhigh();
+ switch (p->p_stat) {
+ case 0:
+ case SRUN:
+ case SZOMB:
+ default:
+ panic("setrunnable");
+ case SSTOP:
+ case SSLEEP:
+ unsleep(p); /* e.g. when sending signals */
+ break;
+
+ case SIDL:
+ break;
+ }
+ p->p_stat = SRUN;
+ if (p->p_flag & P_INMEM)
+ setrunqueue(p);
+ splx(s);
+ if (p->p_slptime > 1)
+ updatepri(p);
+ p->p_slptime = 0;
+ if ((p->p_flag & P_INMEM) == 0)
+ wakeup((caddr_t)&proc0);
+ else if (p->p_priority < curpriority)
+ need_resched();
+}
+
+/*
+ * Compute the priority of a process when running in user mode.
+ * Arrange to reschedule if the resulting priority is better
+ * than that of the current process.
+ */
+void
+resetpriority(p)
+ register struct proc *p;
+{
+ register unsigned int newpriority;
+
+ newpriority = PUSER + p->p_estcpu / 4 + 2 * p->p_nice;
+ newpriority = min(newpriority, MAXPRI);
+ p->p_usrpri = newpriority;
+ if (newpriority < curpriority)
+ need_resched();
+}
diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c
new file mode 100644
index 0000000..7e5f196
--- /dev/null
+++ b/sys/kern/kern_sysctl.c
@@ -0,0 +1,798 @@
+/*-
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Karels at Berkeley Software Design, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_sysctl.c 8.4 (Berkeley) 4/14/94
+ */
+
+/*
+ * sysctl system call.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/unistd.h>
+#include <sys/buf.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <vm/vm.h>
+#include <sys/sysctl.h>
+
+sysctlfn kern_sysctl;
+sysctlfn hw_sysctl;
+#ifdef DEBUG
+sysctlfn debug_sysctl;
+#endif
+extern sysctlfn vm_sysctl;
+extern sysctlfn fs_sysctl;
+extern sysctlfn net_sysctl;
+extern sysctlfn cpu_sysctl;
+
+/*
+ * Locking and stats
+ */
+static struct sysctl_lock {
+ int sl_lock;
+ int sl_want;
+ int sl_locked;
+} memlock;
+
+struct sysctl_args {
+ int *name;
+ u_int namelen;
+ void *old;
+ size_t *oldlenp;
+ void *new;
+ size_t newlen;
+};
+
+int
+__sysctl(p, uap, retval)
+ struct proc *p;
+ register struct sysctl_args *uap;
+ int *retval;
+{
+ int error, dolock = 1;
+ u_int savelen = 0, oldlen = 0;
+ sysctlfn *fn;
+ int name[CTL_MAXNAME];
+
+ if (uap->new != NULL && (error = suser(p->p_ucred, &p->p_acflag)))
+ return (error);
+ /*
+ * all top-level sysctl names are non-terminal
+ */
+ if (uap->namelen > CTL_MAXNAME || uap->namelen < 2)
+ return (EINVAL);
+ if (error = copyin(uap->name, &name, uap->namelen * sizeof(int)))
+ return (error);
+
+ switch (name[0]) {
+ case CTL_KERN:
+ fn = kern_sysctl;
+ if (name[2] != KERN_VNODE) /* XXX */
+ dolock = 0;
+ break;
+ case CTL_HW:
+ fn = hw_sysctl;
+ break;
+ case CTL_VM:
+ fn = vm_sysctl;
+ break;
+ case CTL_NET:
+ fn = net_sysctl;
+ break;
+#ifdef notyet
+ case CTL_FS:
+ fn = fs_sysctl;
+ break;
+#endif
+ case CTL_MACHDEP:
+ fn = cpu_sysctl;
+ break;
+#ifdef DEBUG
+ case CTL_DEBUG:
+ fn = debug_sysctl;
+ break;
+#endif
+ default:
+ return (EOPNOTSUPP);
+ }
+
+ if (uap->oldlenp &&
+ (error = copyin(uap->oldlenp, &oldlen, sizeof(oldlen))))
+ return (error);
+ if (uap->old != NULL) {
+ if (!useracc(uap->old, oldlen, B_WRITE))
+ return (EFAULT);
+ while (memlock.sl_lock) {
+ memlock.sl_want = 1;
+ sleep((caddr_t)&memlock, PRIBIO+1);
+ memlock.sl_locked++;
+ }
+ memlock.sl_lock = 1;
+ if (dolock)
+ vslock(uap->old, oldlen);
+ savelen = oldlen;
+ }
+ error = (*fn)(name + 1, uap->namelen - 1, uap->old, &oldlen,
+ uap->new, uap->newlen, p);
+ if (uap->old != NULL) {
+ if (dolock)
+ vsunlock(uap->old, savelen, B_WRITE);
+ memlock.sl_lock = 0;
+ if (memlock.sl_want) {
+ memlock.sl_want = 0;
+ wakeup((caddr_t)&memlock);
+ }
+ }
+ if (error)
+ return (error);
+ if (uap->oldlenp)
+ error = copyout(&oldlen, uap->oldlenp, sizeof(oldlen));
+ *retval = oldlen;
+ return (0);
+}
+
+/*
+ * Attributes stored in the kernel.
+ */
+char hostname[MAXHOSTNAMELEN];
+int hostnamelen;
+long hostid;
+int securelevel;
+
+/*
+ * kernel related system variables.
+ */
+int
+kern_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+ struct proc *p;
+{
+ int error, level, inthostid;
+ extern char ostype[], osrelease[], version[];
+
+ /* all sysctl names at this level are terminal */
+ if (namelen != 1 && !(name[0] == KERN_PROC || name[0] == KERN_PROF))
+ return (ENOTDIR); /* overloaded */
+
+ switch (name[0]) {
+ case KERN_OSTYPE:
+ return (sysctl_rdstring(oldp, oldlenp, newp, ostype));
+ case KERN_OSRELEASE:
+ return (sysctl_rdstring(oldp, oldlenp, newp, osrelease));
+ case KERN_OSREV:
+ return (sysctl_rdint(oldp, oldlenp, newp, BSD));
+ case KERN_VERSION:
+ return (sysctl_rdstring(oldp, oldlenp, newp, version));
+ case KERN_MAXVNODES:
+ return(sysctl_int(oldp, oldlenp, newp, newlen, &desiredvnodes));
+ case KERN_MAXPROC:
+ return (sysctl_int(oldp, oldlenp, newp, newlen, &maxproc));
+ case KERN_MAXFILES:
+ return (sysctl_int(oldp, oldlenp, newp, newlen, &maxfiles));
+ case KERN_ARGMAX:
+ return (sysctl_rdint(oldp, oldlenp, newp, ARG_MAX));
+ case KERN_SECURELVL:
+ level = securelevel;
+ if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &level)) ||
+ newp == NULL)
+ return (error);
+ if (level < securelevel && p->p_pid != 1)
+ return (EPERM);
+ securelevel = level;
+ return (0);
+ case KERN_HOSTNAME:
+ error = sysctl_string(oldp, oldlenp, newp, newlen,
+ hostname, sizeof(hostname));
+ if (newp && !error)
+ hostnamelen = newlen;
+ return (error);
+ case KERN_HOSTID:
+ inthostid = hostid; /* XXX assumes sizeof long <= sizeof int */
+ error = sysctl_int(oldp, oldlenp, newp, newlen, &inthostid);
+ hostid = inthostid;
+ return (error);
+ case KERN_CLOCKRATE:
+ return (sysctl_clockrate(oldp, oldlenp));
+ case KERN_BOOTTIME:
+ return (sysctl_rdstruct(oldp, oldlenp, newp, &boottime,
+ sizeof(struct timeval)));
+ case KERN_VNODE:
+ return (sysctl_vnode(oldp, oldlenp));
+ case KERN_PROC:
+ return (sysctl_doproc(name + 1, namelen - 1, oldp, oldlenp));
+ case KERN_FILE:
+ return (sysctl_file(oldp, oldlenp));
+#ifdef GPROF
+ case KERN_PROF:
+ return (sysctl_doprof(name + 1, namelen - 1, oldp, oldlenp,
+ newp, newlen));
+#endif
+ case KERN_POSIX1:
+ return (sysctl_rdint(oldp, oldlenp, newp, _POSIX_VERSION));
+ case KERN_NGROUPS:
+ return (sysctl_rdint(oldp, oldlenp, newp, NGROUPS_MAX));
+ case KERN_JOB_CONTROL:
+ return (sysctl_rdint(oldp, oldlenp, newp, 1));
+ case KERN_SAVED_IDS:
+#ifdef _POSIX_SAVED_IDS
+ return (sysctl_rdint(oldp, oldlenp, newp, 1));
+#else
+ return (sysctl_rdint(oldp, oldlenp, newp, 0));
+#endif
+ default:
+ return (EOPNOTSUPP);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * hardware related system variables.
+ */
+int
+hw_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+ struct proc *p;
+{
+ extern char machine[], cpu_model[];
+
+ /* all sysctl names at this level are terminal */
+ if (namelen != 1)
+ return (ENOTDIR); /* overloaded */
+
+ switch (name[0]) {
+ case HW_MACHINE:
+ return (sysctl_rdstring(oldp, oldlenp, newp, machine));
+ case HW_MODEL:
+ return (sysctl_rdstring(oldp, oldlenp, newp, cpu_model));
+ case HW_NCPU:
+ return (sysctl_rdint(oldp, oldlenp, newp, 1)); /* XXX */
+ case HW_BYTEORDER:
+ return (sysctl_rdint(oldp, oldlenp, newp, BYTE_ORDER));
+ case HW_PHYSMEM:
+ return (sysctl_rdint(oldp, oldlenp, newp, ctob(physmem)));
+ case HW_USERMEM:
+ return (sysctl_rdint(oldp, oldlenp, newp,
+ ctob(physmem - cnt.v_wire_count)));
+ case HW_PAGESIZE:
+ return (sysctl_rdint(oldp, oldlenp, newp, PAGE_SIZE));
+ default:
+ return (EOPNOTSUPP);
+ }
+ /* NOTREACHED */
+}
+
+#ifdef DEBUG
+/*
+ * Debugging related system variables.
+ */
+struct ctldebug debug0, debug1, debug2, debug3, debug4;
+struct ctldebug debug5, debug6, debug7, debug8, debug9;
+struct ctldebug debug10, debug11, debug12, debug13, debug14;
+struct ctldebug debug15, debug16, debug17, debug18, debug19;
+static struct ctldebug *debugvars[CTL_DEBUG_MAXID] = {
+ &debug0, &debug1, &debug2, &debug3, &debug4,
+ &debug5, &debug6, &debug7, &debug8, &debug9,
+ &debug10, &debug11, &debug12, &debug13, &debug14,
+ &debug15, &debug16, &debug17, &debug18, &debug19,
+};
+int
+debug_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+ struct proc *p;
+{
+ struct ctldebug *cdp;
+
+ /* all sysctl names at this level are name and field */
+ if (namelen != 2)
+ return (ENOTDIR); /* overloaded */
+ cdp = debugvars[name[0]];
+ if (cdp->debugname == 0)
+ return (EOPNOTSUPP);
+ switch (name[1]) {
+ case CTL_DEBUG_NAME:
+ return (sysctl_rdstring(oldp, oldlenp, newp, cdp->debugname));
+ case CTL_DEBUG_VALUE:
+ return (sysctl_int(oldp, oldlenp, newp, newlen, cdp->debugvar));
+ default:
+ return (EOPNOTSUPP);
+ }
+ /* NOTREACHED */
+}
+#endif /* DEBUG */
+
+/*
+ * Validate parameters and get old / set new parameters
+ * for an integer-valued sysctl function.
+ */
+int
+sysctl_int(oldp, oldlenp, newp, newlen, valp)
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+ int *valp;
+{
+ int error = 0;
+
+ if (oldp && *oldlenp < sizeof(int))
+ return (ENOMEM);
+ if (newp && newlen != sizeof(int))
+ return (EINVAL);
+ *oldlenp = sizeof(int);
+ if (oldp)
+ error = copyout(valp, oldp, sizeof(int));
+ if (error == 0 && newp)
+ error = copyin(newp, valp, sizeof(int));
+ return (error);
+}
+
+/*
+ * As above, but read-only.
+ */
+int
+sysctl_rdint(oldp, oldlenp, newp, val)
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ int val;
+{
+ int error = 0;
+
+ if (oldp && *oldlenp < sizeof(int))
+ return (ENOMEM);
+ if (newp)
+ return (EPERM);
+ *oldlenp = sizeof(int);
+ if (oldp)
+ error = copyout((caddr_t)&val, oldp, sizeof(int));
+ return (error);
+}
+
+/*
+ * Validate parameters and get old / set new parameters
+ * for a string-valued sysctl function.
+ */
+int
+sysctl_string(oldp, oldlenp, newp, newlen, str, maxlen)
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+ char *str;
+ int maxlen;
+{
+ int len, error = 0;
+
+ len = strlen(str) + 1;
+ if (oldp && *oldlenp < len)
+ return (ENOMEM);
+ if (newp && newlen >= maxlen)
+ return (EINVAL);
+ if (oldp) {
+ *oldlenp = len;
+ error = copyout(str, oldp, len);
+ }
+ if (error == 0 && newp) {
+ error = copyin(newp, str, newlen);
+ str[newlen] = 0;
+ }
+ return (error);
+}
+
+/*
+ * As above, but read-only.
+ */
+int
+sysctl_rdstring(oldp, oldlenp, newp, str)
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ char *str;
+{
+ int len, error = 0;
+
+ len = strlen(str) + 1;
+ if (oldp && *oldlenp < len)
+ return (ENOMEM);
+ if (newp)
+ return (EPERM);
+ *oldlenp = len;
+ if (oldp)
+ error = copyout(str, oldp, len);
+ return (error);
+}
+
+/*
+ * Validate parameters and get old / set new parameters
+ * for a structure oriented sysctl function.
+ */
+int
+sysctl_struct(oldp, oldlenp, newp, newlen, sp, len)
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+ void *sp;
+ int len;
+{
+ int error = 0;
+
+ if (oldp && *oldlenp < len)
+ return (ENOMEM);
+ if (newp && newlen > len)
+ return (EINVAL);
+ if (oldp) {
+ *oldlenp = len;
+ error = copyout(sp, oldp, len);
+ }
+ if (error == 0 && newp)
+ error = copyin(newp, sp, len);
+ return (error);
+}
+
+/*
+ * Validate parameters and get old parameters
+ * for a structure oriented sysctl function.
+ */
+int
+sysctl_rdstruct(oldp, oldlenp, newp, sp, len)
+ void *oldp;
+ size_t *oldlenp;
+ void *newp, *sp;
+ int len;
+{
+ int error = 0;
+
+ if (oldp && *oldlenp < len)
+ return (ENOMEM);
+ if (newp)
+ return (EPERM);
+ *oldlenp = len;
+ if (oldp)
+ error = copyout(sp, oldp, len);
+ return (error);
+}
+
+/*
+ * Get file structures.
+ */
+int
+sysctl_file(where, sizep)
+ char *where;
+ size_t *sizep;
+{
+ int buflen, error;
+ struct file *fp;
+ char *start = where;
+
+ buflen = *sizep;
+ if (where == NULL) {
+ /*
+ * overestimate by 10 files
+ */
+ *sizep = sizeof(filehead) + (nfiles + 10) * sizeof(struct file);
+ return (0);
+ }
+
+ /*
+ * first copyout filehead
+ */
+ if (buflen < sizeof(filehead)) {
+ *sizep = 0;
+ return (0);
+ }
+ if (error = copyout((caddr_t)&filehead, where, sizeof(filehead)))
+ return (error);
+ buflen -= sizeof(filehead);
+ where += sizeof(filehead);
+
+ /*
+ * followed by an array of file structures
+ */
+ for (fp = filehead; fp != NULL; fp = fp->f_filef) {
+ if (buflen < sizeof(struct file)) {
+ *sizep = where - start;
+ return (ENOMEM);
+ }
+ if (error = copyout((caddr_t)fp, where, sizeof (struct file)))
+ return (error);
+ buflen -= sizeof(struct file);
+ where += sizeof(struct file);
+ }
+ *sizep = where - start;
+ return (0);
+}
+
+/*
+ * try over estimating by 5 procs
+ */
+#define KERN_PROCSLOP (5 * sizeof (struct kinfo_proc))
+
+int
+sysctl_doproc(name, namelen, where, sizep)
+ int *name;
+ u_int namelen;
+ char *where;
+ size_t *sizep;
+{
+ register struct proc *p;
+ register struct kinfo_proc *dp = (struct kinfo_proc *)where;
+ register int needed = 0;
+ int buflen = where != NULL ? *sizep : 0;
+ int doingzomb;
+ struct eproc eproc;
+ int error = 0;
+
+ if (namelen != 2 && !(namelen == 1 && name[0] == KERN_PROC_ALL))
+ return (EINVAL);
+ p = (struct proc *)allproc;
+ doingzomb = 0;
+again:
+ for (; p != NULL; p = p->p_next) {
+ /*
+ * Skip embryonic processes.
+ */
+ if (p->p_stat == SIDL)
+ continue;
+ /*
+ * TODO - make more efficient (see notes below).
+ * do by session.
+ */
+ switch (name[0]) {
+
+ case KERN_PROC_PID:
+ /* could do this with just a lookup */
+ if (p->p_pid != (pid_t)name[1])
+ continue;
+ break;
+
+ case KERN_PROC_PGRP:
+ /* could do this by traversing pgrp */
+ if (p->p_pgrp->pg_id != (pid_t)name[1])
+ continue;
+ break;
+
+ case KERN_PROC_TTY:
+ if ((p->p_flag & P_CONTROLT) == 0 ||
+ p->p_session->s_ttyp == NULL ||
+ p->p_session->s_ttyp->t_dev != (dev_t)name[1])
+ continue;
+ break;
+
+ case KERN_PROC_UID:
+ if (p->p_ucred->cr_uid != (uid_t)name[1])
+ continue;
+ break;
+
+ case KERN_PROC_RUID:
+ if (p->p_cred->p_ruid != (uid_t)name[1])
+ continue;
+ break;
+ }
+ if (buflen >= sizeof(struct kinfo_proc)) {
+ fill_eproc(p, &eproc);
+ if (error = copyout((caddr_t)p, &dp->kp_proc,
+ sizeof(struct proc)))
+ return (error);
+ if (error = copyout((caddr_t)&eproc, &dp->kp_eproc,
+ sizeof(eproc)))
+ return (error);
+ dp++;
+ buflen -= sizeof(struct kinfo_proc);
+ }
+ needed += sizeof(struct kinfo_proc);
+ }
+ if (doingzomb == 0) {
+ p = zombproc;
+ doingzomb++;
+ goto again;
+ }
+ if (where != NULL) {
+ *sizep = (caddr_t)dp - where;
+ if (needed > *sizep)
+ return (ENOMEM);
+ } else {
+ needed += KERN_PROCSLOP;
+ *sizep = needed;
+ }
+ return (0);
+}
+
+/*
+ * Fill in an eproc structure for the specified process.
+ */
+void
+fill_eproc(p, ep)
+ register struct proc *p;
+ register struct eproc *ep;
+{
+ register struct tty *tp;
+
+ ep->e_paddr = p;
+ ep->e_sess = p->p_pgrp->pg_session;
+ ep->e_pcred = *p->p_cred;
+ ep->e_ucred = *p->p_ucred;
+ if (p->p_stat == SIDL || p->p_stat == SZOMB) {
+ ep->e_vm.vm_rssize = 0;
+ ep->e_vm.vm_tsize = 0;
+ ep->e_vm.vm_dsize = 0;
+ ep->e_vm.vm_ssize = 0;
+#ifndef sparc
+ /* ep->e_vm.vm_pmap = XXX; */
+#endif
+ } else {
+ register struct vmspace *vm = p->p_vmspace;
+
+#ifdef pmap_resident_count
+ ep->e_vm.vm_rssize = pmap_resident_count(&vm->vm_pmap); /*XXX*/
+#else
+ ep->e_vm.vm_rssize = vm->vm_rssize;
+#endif
+ ep->e_vm.vm_tsize = vm->vm_tsize;
+ ep->e_vm.vm_dsize = vm->vm_dsize;
+ ep->e_vm.vm_ssize = vm->vm_ssize;
+#ifndef sparc
+ ep->e_vm.vm_pmap = vm->vm_pmap;
+#endif
+ }
+ if (p->p_pptr)
+ ep->e_ppid = p->p_pptr->p_pid;
+ else
+ ep->e_ppid = 0;
+ ep->e_pgid = p->p_pgrp->pg_id;
+ ep->e_jobc = p->p_pgrp->pg_jobc;
+ if ((p->p_flag & P_CONTROLT) &&
+ (tp = ep->e_sess->s_ttyp)) {
+ ep->e_tdev = tp->t_dev;
+ ep->e_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID;
+ ep->e_tsess = tp->t_session;
+ } else
+ ep->e_tdev = NODEV;
+ ep->e_flag = ep->e_sess->s_ttyvp ? EPROC_CTTY : 0;
+ if (SESS_LEADER(p))
+ ep->e_flag |= EPROC_SLEADER;
+ if (p->p_wmesg)
+ strncpy(ep->e_wmesg, p->p_wmesg, WMESGLEN);
+ ep->e_xsize = ep->e_xrssize = 0;
+ ep->e_xccount = ep->e_xswrss = 0;
+}
+
+#ifdef COMPAT_43
+#include <sys/socket.h>
+#define KINFO_PROC (0<<8)
+#define KINFO_RT (1<<8)
+#define KINFO_VNODE (2<<8)
+#define KINFO_FILE (3<<8)
+#define KINFO_METER (4<<8)
+#define KINFO_LOADAVG (5<<8)
+#define KINFO_CLOCKRATE (6<<8)
+
+struct getkerninfo_args {
+ int op;
+ char *where;
+ int *size;
+ int arg;
+};
+
+int
+ogetkerninfo(p, uap, retval)
+ struct proc *p;
+ register struct getkerninfo_args *uap;
+ int *retval;
+{
+ int error, name[5];
+ u_int size;
+
+ if (uap->size &&
+ (error = copyin((caddr_t)uap->size, (caddr_t)&size, sizeof(size))))
+ return (error);
+
+ switch (uap->op & 0xff00) {
+
+ case KINFO_RT:
+ name[0] = PF_ROUTE;
+ name[1] = 0;
+ name[2] = (uap->op & 0xff0000) >> 16;
+ name[3] = uap->op & 0xff;
+ name[4] = uap->arg;
+ error = net_sysctl(name, 5, uap->where, &size, NULL, 0, p);
+ break;
+
+ case KINFO_VNODE:
+ name[0] = KERN_VNODE;
+ error = kern_sysctl(name, 1, uap->where, &size, NULL, 0, p);
+ break;
+
+ case KINFO_PROC:
+ name[0] = KERN_PROC;
+ name[1] = uap->op & 0xff;
+ name[2] = uap->arg;
+ error = kern_sysctl(name, 3, uap->where, &size, NULL, 0, p);
+ break;
+
+ case KINFO_FILE:
+ name[0] = KERN_FILE;
+ error = kern_sysctl(name, 1, uap->where, &size, NULL, 0, p);
+ break;
+
+ case KINFO_METER:
+ name[0] = VM_METER;
+ error = vm_sysctl(name, 1, uap->where, &size, NULL, 0, p);
+ break;
+
+ case KINFO_LOADAVG:
+ name[0] = VM_LOADAVG;
+ error = vm_sysctl(name, 1, uap->where, &size, NULL, 0, p);
+ break;
+
+ case KINFO_CLOCKRATE:
+ name[0] = KERN_CLOCKRATE;
+ error = kern_sysctl(name, 1, uap->where, &size, NULL, 0, p);
+ break;
+
+ default:
+ return (EOPNOTSUPP);
+ }
+ if (error)
+ return (error);
+ *retval = size;
+ if (uap->size)
+ error = copyout((caddr_t)&size, (caddr_t)uap->size,
+ sizeof(size));
+ return (error);
+}
+#endif /* COMPAT_43 */
diff --git a/sys/kern/kern_tc.c b/sys/kern/kern_tc.c
new file mode 100644
index 0000000..4017e96
--- /dev/null
+++ b/sys/kern/kern_tc.c
@@ -0,0 +1,529 @@
+/*-
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_clock.c 8.5 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/dkstat.h>
+#include <sys/callout.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/resourcevar.h>
+
+#include <machine/cpu.h>
+
+#ifdef GPROF
+#include <sys/gmon.h>
+#endif
+
+/*
+ * Clock handling routines.
+ *
+ * This code is written to operate with two timers that run independently of
+ * each other. The main clock, running hz times per second, is used to keep
+ * track of real time. The second timer handles kernel and user profiling,
+ * and does resource use estimation. If the second timer is programmable,
+ * it is randomized to avoid aliasing between the two clocks. For example,
+ * the randomization prevents an adversary from always giving up the cpu
+ * just before its quantum expires. Otherwise, it would never accumulate
+ * cpu ticks. The mean frequency of the second timer is stathz.
+ *
+ * If no second timer exists, stathz will be zero; in this case we drive
+ * profiling and statistics off the main clock. This WILL NOT be accurate;
+ * do not do it unless absolutely necessary.
+ *
+ * The statistics clock may (or may not) be run at a higher rate while
+ * profiling. This profile clock runs at profhz. We require that profhz
+ * be an integral multiple of stathz.
+ *
+ * If the statistics clock is running fast, it must be divided by the ratio
+ * profhz/stathz for statistics. (For profiling, every tick counts.)
+ */
+
+/*
+ * TODO:
+ * allocate more timeout table slots when table overflows.
+ */
+
+/*
+ * Bump a timeval by a small number of usec's.
+ */
+#define BUMPTIME(t, usec) { \
+ register volatile struct timeval *tp = (t); \
+ register long us; \
+ \
+ tp->tv_usec = us = tp->tv_usec + (usec); \
+ if (us >= 1000000) { \
+ tp->tv_usec = us - 1000000; \
+ tp->tv_sec++; \
+ } \
+}
+
+int stathz;
+int profhz;
+int profprocs;
+int ticks;
+static int psdiv, pscnt; /* prof => stat divider */
+int psratio; /* ratio: prof / stat */
+
+volatile struct timeval time;
+volatile struct timeval mono_time;
+
+/*
+ * Initialize clock frequencies and start both clocks running.
+ */
+void
+initclocks()
+{
+ register int i;
+
+ /*
+ * Set divisors to 1 (normal case) and let the machine-specific
+ * code do its bit.
+ */
+ psdiv = pscnt = 1;
+ cpu_initclocks();
+
+ /*
+ * Compute profhz/stathz, and fix profhz if needed.
+ */
+ i = stathz ? stathz : hz;
+ if (profhz == 0)
+ profhz = i;
+ psratio = profhz / i;
+}
+
+/*
+ * The real-time timer, interrupting hz times per second.
+ */
+void
+hardclock(frame)
+ register struct clockframe *frame;
+{
+ register struct callout *p1;
+ register struct proc *p;
+ register int delta, needsoft;
+ extern int tickdelta;
+ extern long timedelta;
+
+ /*
+ * Update real-time timeout queue.
+ * At front of queue are some number of events which are ``due''.
+ * The time to these is <= 0 and if negative represents the
+ * number of ticks which have passed since it was supposed to happen.
+ * The rest of the q elements (times > 0) are events yet to happen,
+ * where the time for each is given as a delta from the previous.
+ * Decrementing just the first of these serves to decrement the time
+ * to all events.
+ */
+ needsoft = 0;
+ for (p1 = calltodo.c_next; p1 != NULL; p1 = p1->c_next) {
+ if (--p1->c_time > 0)
+ break;
+ needsoft = 1;
+ if (p1->c_time == 0)
+ break;
+ }
+
+ p = curproc;
+ if (p) {
+ register struct pstats *pstats;
+
+ /*
+ * Run current process's virtual and profile time, as needed.
+ */
+ pstats = p->p_stats;
+ if (CLKF_USERMODE(frame) &&
+ timerisset(&pstats->p_timer[ITIMER_VIRTUAL].it_value) &&
+ itimerdecr(&pstats->p_timer[ITIMER_VIRTUAL], tick) == 0)
+ psignal(p, SIGVTALRM);
+ if (timerisset(&pstats->p_timer[ITIMER_PROF].it_value) &&
+ itimerdecr(&pstats->p_timer[ITIMER_PROF], tick) == 0)
+ psignal(p, SIGPROF);
+ }
+
+ /*
+ * If no separate statistics clock is available, run it from here.
+ */
+ if (stathz == 0)
+ statclock(frame);
+
+ /*
+ * Increment the time-of-day. The increment is just ``tick'' unless
+ * we are still adjusting the clock; see adjtime().
+ */
+ ticks++;
+ if (timedelta == 0)
+ delta = tick;
+ else {
+ delta = tick + tickdelta;
+ timedelta -= tickdelta;
+ }
+ BUMPTIME(&time, delta);
+ BUMPTIME(&mono_time, delta);
+
+ /*
+ * Process callouts at a very low cpu priority, so we don't keep the
+ * relatively high clock interrupt priority any longer than necessary.
+ */
+ if (needsoft) {
+ if (CLKF_BASEPRI(frame)) {
+ /*
+ * Save the overhead of a software interrupt;
+ * it will happen as soon as we return, so do it now.
+ */
+ (void)splsoftclock();
+ softclock();
+ } else
+ setsoftclock();
+ }
+}
+
+/*
+ * Software (low priority) clock interrupt.
+ * Run periodic events from timeout queue.
+ */
+/*ARGSUSED*/
+void
+softclock()
+{
+ register struct callout *c;
+ register void *arg;
+ register void (*func) __P((void *));
+ register int s;
+
+ s = splhigh();
+ while ((c = calltodo.c_next) != NULL && c->c_time <= 0) {
+ func = c->c_func;
+ arg = c->c_arg;
+ calltodo.c_next = c->c_next;
+ c->c_next = callfree;
+ callfree = c;
+ splx(s);
+ (*func)(arg);
+ (void) splhigh();
+ }
+ splx(s);
+}
+
+/*
+ * timeout --
+ * Execute a function after a specified length of time.
+ *
+ * untimeout --
+ * Cancel previous timeout function call.
+ *
+ * See AT&T BCI Driver Reference Manual for specification. This
+ * implementation differs from that one in that no identification
+ * value is returned from timeout, rather, the original arguments
+ * to timeout are used to identify entries for untimeout.
+ */
+void
+timeout(ftn, arg, ticks)
+ void (*ftn) __P((void *));
+ void *arg;
+ register int ticks;
+{
+ register struct callout *new, *p, *t;
+ register int s;
+
+ if (ticks <= 0)
+ ticks = 1;
+
+ /* Lock out the clock. */
+ s = splhigh();
+
+ /* Fill in the next free callout structure. */
+ if (callfree == NULL)
+ panic("timeout table full");
+ new = callfree;
+ callfree = new->c_next;
+ new->c_arg = arg;
+ new->c_func = ftn;
+
+ /*
+ * The time for each event is stored as a difference from the time
+ * of the previous event on the queue. Walk the queue, correcting
+ * the ticks argument for queue entries passed. Correct the ticks
+ * value for the queue entry immediately after the insertion point
+ * as well. Watch out for negative c_time values; these represent
+ * overdue events.
+ */
+ for (p = &calltodo;
+ (t = p->c_next) != NULL && ticks > t->c_time; p = t)
+ if (t->c_time > 0)
+ ticks -= t->c_time;
+ new->c_time = ticks;
+ if (t != NULL)
+ t->c_time -= ticks;
+
+ /* Insert the new entry into the queue. */
+ p->c_next = new;
+ new->c_next = t;
+ splx(s);
+}
+
+void
+untimeout(ftn, arg)
+ void (*ftn) __P((void *));
+ void *arg;
+{
+ register struct callout *p, *t;
+ register int s;
+
+ s = splhigh();
+ for (p = &calltodo; (t = p->c_next) != NULL; p = t)
+ if (t->c_func == ftn && t->c_arg == arg) {
+ /* Increment next entry's tick count. */
+ if (t->c_next && t->c_time > 0)
+ t->c_next->c_time += t->c_time;
+
+ /* Move entry from callout queue to callfree queue. */
+ p->c_next = t->c_next;
+ t->c_next = callfree;
+ callfree = t;
+ break;
+ }
+ splx(s);
+}
+
+/*
+ * Compute number of hz until specified time. Used to
+ * compute third argument to timeout() from an absolute time.
+ */
+int
+hzto(tv)
+ struct timeval *tv;
+{
+ register long ticks, sec;
+ int s;
+
+ /*
+ * If number of milliseconds will fit in 32 bit arithmetic,
+ * then compute number of milliseconds to time and scale to
+ * ticks. Otherwise just compute number of hz in time, rounding
+ * times greater than representible to maximum value.
+ *
+ * Delta times less than 25 days can be computed ``exactly''.
+ * Maximum value for any timeout in 10ms ticks is 250 days.
+ */
+ s = splhigh();
+ sec = tv->tv_sec - time.tv_sec;
+ if (sec <= 0x7fffffff / 1000 - 1000)
+ ticks = ((tv->tv_sec - time.tv_sec) * 1000 +
+ (tv->tv_usec - time.tv_usec) / 1000) / (tick / 1000);
+ else if (sec <= 0x7fffffff / hz)
+ ticks = sec * hz;
+ else
+ ticks = 0x7fffffff;
+ splx(s);
+ return (ticks);
+}
+
+/*
+ * Start profiling on a process.
+ *
+ * Kernel profiling passes proc0 which never exits and hence
+ * keeps the profile clock running constantly.
+ */
+void
+startprofclock(p)
+ register struct proc *p;
+{
+ int s;
+
+ if ((p->p_flag & P_PROFIL) == 0) {
+ p->p_flag |= P_PROFIL;
+ if (++profprocs == 1 && stathz != 0) {
+ s = splstatclock();
+ psdiv = pscnt = psratio;
+ setstatclockrate(profhz);
+ splx(s);
+ }
+ }
+}
+
+/*
+ * Stop profiling on a process.
+ */
+void
+stopprofclock(p)
+ register struct proc *p;
+{
+ int s;
+
+ if (p->p_flag & P_PROFIL) {
+ p->p_flag &= ~P_PROFIL;
+ if (--profprocs == 0 && stathz != 0) {
+ s = splstatclock();
+ psdiv = pscnt = 1;
+ setstatclockrate(stathz);
+ splx(s);
+ }
+ }
+}
+
+int dk_ndrive = DK_NDRIVE;
+
+/*
+ * Statistics clock. Grab profile sample, and if divider reaches 0,
+ * do process and kernel statistics.
+ */
+void
+statclock(frame)
+ register struct clockframe *frame;
+{
+#ifdef GPROF
+ register struct gmonparam *g;
+#endif
+ register struct proc *p;
+ register int i;
+
+ if (CLKF_USERMODE(frame)) {
+ p = curproc;
+ if (p->p_flag & P_PROFIL)
+ addupc_intr(p, CLKF_PC(frame), 1);
+ if (--pscnt > 0)
+ return;
+ /*
+ * Came from user mode; CPU was in user state.
+ * If this process is being profiled record the tick.
+ */
+ p->p_uticks++;
+ if (p->p_nice > NZERO)
+ cp_time[CP_NICE]++;
+ else
+ cp_time[CP_USER]++;
+ } else {
+#ifdef GPROF
+ /*
+ * Kernel statistics are just like addupc_intr, only easier.
+ */
+ g = &_gmonparam;
+ if (g->state == GMON_PROF_ON) {
+ i = CLKF_PC(frame) - g->lowpc;
+ if (i < g->textsize) {
+ i /= HISTFRACTION * sizeof(*g->kcount);
+ g->kcount[i]++;
+ }
+ }
+#endif
+ if (--pscnt > 0)
+ return;
+ /*
+ * Came from kernel mode, so we were:
+ * - handling an interrupt,
+ * - doing syscall or trap work on behalf of the current
+ * user process, or
+ * - spinning in the idle loop.
+ * Whichever it is, charge the time as appropriate.
+ * Note that we charge interrupts to the current process,
+ * regardless of whether they are ``for'' that process,
+ * so that we know how much of its real time was spent
+ * in ``non-process'' (i.e., interrupt) work.
+ */
+ p = curproc;
+ if (CLKF_INTR(frame)) {
+ if (p != NULL)
+ p->p_iticks++;
+ cp_time[CP_INTR]++;
+ } else if (p != NULL) {
+ p->p_sticks++;
+ cp_time[CP_SYS]++;
+ } else
+ cp_time[CP_IDLE]++;
+ }
+ pscnt = psdiv;
+
+ /*
+ * We maintain statistics shown by user-level statistics
+ * programs: the amount of time in each cpu state, and
+ * the amount of time each of DK_NDRIVE ``drives'' is busy.
+ *
+ * XXX should either run linked list of drives, or (better)
+ * grab timestamps in the start & done code.
+ */
+ for (i = 0; i < DK_NDRIVE; i++)
+ if (dk_busy & (1 << i))
+ dk_time[i]++;
+
+ /*
+ * We adjust the priority of the current process. The priority of
+ * a process gets worse as it accumulates CPU time. The cpu usage
+ * estimator (p_estcpu) is increased here. The formula for computing
+ * priorities (in kern_synch.c) will compute a different value each
+ * time p_estcpu increases by 4. The cpu usage estimator ramps up
+ * quite quickly when the process is running (linearly), and decays
+ * away exponentially, at a rate which is proportionally slower when
+ * the system is busy. The basic principal is that the system will
+ * 90% forget that the process used a lot of CPU time in 5 * loadav
+ * seconds. This causes the system to favor processes which haven't
+ * run much recently, and to round-robin among other processes.
+ */
+ if (p != NULL) {
+ p->p_cpticks++;
+ if (++p->p_estcpu == 0)
+ p->p_estcpu--;
+ if ((p->p_estcpu & 3) == 0) {
+ resetpriority(p);
+ if (p->p_priority >= PUSER)
+ p->p_priority = p->p_usrpri;
+ }
+ }
+}
+
+/*
+ * Return information about system clocks.
+ */
+int
+sysctl_clockrate(where, sizep)
+ register char *where;
+ size_t *sizep;
+{
+ struct clockinfo clkinfo;
+
+ /*
+ * Construct clockinfo structure.
+ */
+ clkinfo.hz = hz;
+ clkinfo.tick = tick;
+ clkinfo.profhz = profhz;
+ clkinfo.stathz = stathz ? stathz : hz;
+ return (sysctl_rdstruct(where, sizep, NULL, &clkinfo, sizeof(clkinfo)));
+}
diff --git a/sys/kern/kern_time.c b/sys/kern/kern_time.c
new file mode 100644
index 0000000..2e86376
--- /dev/null
+++ b/sys/kern/kern_time.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_time.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/resourcevar.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+
+#include <machine/cpu.h>
+
+void timevaladd __P((struct timeval *, struct timeval *));
+void timevalsub __P((struct timeval *, struct timeval *));
+void timevalfix __P((struct timeval *));
+
+/*
+ * Time of day and interval timer support.
+ *
+ * These routines provide the kernel entry points to get and set
+ * the time-of-day and per-process interval timers. Subroutines
+ * here provide support for adding and subtracting timeval structures
+ * and decrementing interval timers, optionally reloading the interval
+ * timers when they expire.
+ */
+
+struct gettimeofday_args {
+ struct timeval *tp;
+ struct timezone *tzp;
+};
+/* ARGSUSED */
+int
+gettimeofday(p, uap, retval)
+ struct proc *p;
+ register struct gettimeofday_args *uap;
+ int *retval;
+{
+ struct timeval atv;
+ int error = 0;
+
+ if (uap->tp) {
+ microtime(&atv);
+ if (error = copyout((caddr_t)&atv, (caddr_t)uap->tp,
+ sizeof (atv)))
+ return (error);
+ }
+ if (uap->tzp)
+ error = copyout((caddr_t)&tz, (caddr_t)uap->tzp,
+ sizeof (tz));
+ return (error);
+}
+
+struct settimeofday_args {
+ struct timeval *tv;
+ struct timezone *tzp;
+};
+/* ARGSUSED */
+int
+settimeofday(p, uap, retval)
+ struct proc *p;
+ struct settimeofday_args *uap;
+ int *retval;
+{
+ struct timeval atv, delta;
+ struct timezone atz;
+ int error, s;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ /* Verify all parameters before changing time. */
+ if (uap->tv &&
+ (error = copyin((caddr_t)uap->tv, (caddr_t)&atv, sizeof(atv))))
+ return (error);
+ if (uap->tzp &&
+ (error = copyin((caddr_t)uap->tzp, (caddr_t)&atz, sizeof(atz))))
+ return (error);
+ if (uap->tv) {
+ /* WHAT DO WE DO ABOUT PENDING REAL-TIME TIMEOUTS??? */
+ s = splclock();
+ /* nb. delta.tv_usec may be < 0, but this is OK here */
+ delta.tv_sec = atv.tv_sec - time.tv_sec;
+ delta.tv_usec = atv.tv_usec - time.tv_usec;
+ time = atv;
+ (void) splsoftclock();
+ timevaladd(&boottime, &delta);
+ timevalfix(&boottime);
+ timevaladd(&runtime, &delta);
+ timevalfix(&runtime);
+ LEASE_UPDATETIME(delta.tv_sec);
+ splx(s);
+ resettodr();
+ }
+ if (uap->tzp)
+ tz = atz;
+ return (0);
+}
+
+extern int tickadj; /* "standard" clock skew, us./tick */
+int tickdelta; /* current clock skew, us. per tick */
+long timedelta; /* unapplied time correction, us. */
+long bigadj = 1000000; /* use 10x skew above bigadj us. */
+
+struct adjtime_args {
+ struct timeval *delta;
+ struct timeval *olddelta;
+};
+/* ARGSUSED */
+int
+adjtime(p, uap, retval)
+ struct proc *p;
+ register struct adjtime_args *uap;
+ int *retval;
+{
+ struct timeval atv;
+ register long ndelta, ntickdelta, odelta;
+ int s, error;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ if (error =
+ copyin((caddr_t)uap->delta, (caddr_t)&atv, sizeof(struct timeval)))
+ return (error);
+
+ /*
+ * Compute the total correction and the rate at which to apply it.
+ * Round the adjustment down to a whole multiple of the per-tick
+ * delta, so that after some number of incremental changes in
+ * hardclock(), tickdelta will become zero, lest the correction
+ * overshoot and start taking us away from the desired final time.
+ */
+ ndelta = atv.tv_sec * 1000000 + atv.tv_usec;
+ if (ndelta > bigadj)
+ ntickdelta = 10 * tickadj;
+ else
+ ntickdelta = tickadj;
+ if (ndelta % ntickdelta)
+ ndelta = ndelta / ntickdelta * ntickdelta;
+
+ /*
+ * To make hardclock()'s job easier, make the per-tick delta negative
+ * if we want time to run slower; then hardclock can simply compute
+ * tick + tickdelta, and subtract tickdelta from timedelta.
+ */
+ if (ndelta < 0)
+ ntickdelta = -ntickdelta;
+ s = splclock();
+ odelta = timedelta;
+ timedelta = ndelta;
+ tickdelta = ntickdelta;
+ splx(s);
+
+ if (uap->olddelta) {
+ atv.tv_sec = odelta / 1000000;
+ atv.tv_usec = odelta % 1000000;
+ (void) copyout((caddr_t)&atv, (caddr_t)uap->olddelta,
+ sizeof(struct timeval));
+ }
+ return (0);
+}
+
+/*
+ * Get value of an interval timer. The process virtual and
+ * profiling virtual time timers are kept in the p_stats area, since
+ * they can be swapped out. These are kept internally in the
+ * way they are specified externally: in time until they expire.
+ *
+ * The real time interval timer is kept in the process table slot
+ * for the process, and its value (it_value) is kept as an
+ * absolute time rather than as a delta, so that it is easy to keep
+ * periodic real-time signals from drifting.
+ *
+ * Virtual time timers are processed in the hardclock() routine of
+ * kern_clock.c. The real time timer is processed by a timeout
+ * routine, called from the softclock() routine. Since a callout
+ * may be delayed in real time due to interrupt processing in the system,
+ * it is possible for the real time timeout routine (realitexpire, given below),
+ * to be delayed in real time past when it is supposed to occur. It
+ * does not suffice, therefore, to reload the real timer .it_value from the
+ * real time timers .it_interval. Rather, we compute the next time in
+ * absolute time the timer should go off.
+ */
+struct getitimer_args {
+ u_int which;
+ struct itimerval *itv;
+};
+/* ARGSUSED */
+int
+getitimer(p, uap, retval)
+ struct proc *p;
+ register struct getitimer_args *uap;
+ int *retval;
+{
+ struct itimerval aitv;
+ int s;
+
+ if (uap->which > ITIMER_PROF)
+ return (EINVAL);
+ s = splclock();
+ if (uap->which == ITIMER_REAL) {
+ /*
+ * Convert from absoulte to relative time in .it_value
+ * part of real time timer. If time for real time timer
+ * has passed return 0, else return difference between
+ * current time and time for the timer to go off.
+ */
+ aitv = p->p_realtimer;
+ if (timerisset(&aitv.it_value))
+ if (timercmp(&aitv.it_value, &time, <))
+ timerclear(&aitv.it_value);
+ else
+ timevalsub(&aitv.it_value,
+ (struct timeval *)&time);
+ } else
+ aitv = p->p_stats->p_timer[uap->which];
+ splx(s);
+ return (copyout((caddr_t)&aitv, (caddr_t)uap->itv,
+ sizeof (struct itimerval)));
+}
+
+struct setitimer_args {
+ u_int which;
+ struct itimerval *itv, *oitv;
+};
+/* ARGSUSED */
+int
+setitimer(p, uap, retval)
+ struct proc *p;
+ register struct setitimer_args *uap;
+ int *retval;
+{
+ struct itimerval aitv;
+ register struct itimerval *itvp;
+ int s, error;
+
+ if (uap->which > ITIMER_PROF)
+ return (EINVAL);
+ itvp = uap->itv;
+ if (itvp && (error = copyin((caddr_t)itvp, (caddr_t)&aitv,
+ sizeof(struct itimerval))))
+ return (error);
+ if ((uap->itv = uap->oitv) && (error = getitimer(p, uap, retval)))
+ return (error);
+ if (itvp == 0)
+ return (0);
+ if (itimerfix(&aitv.it_value) || itimerfix(&aitv.it_interval))
+ return (EINVAL);
+ s = splclock();
+ if (uap->which == ITIMER_REAL) {
+ untimeout(realitexpire, (caddr_t)p);
+ if (timerisset(&aitv.it_value)) {
+ timevaladd(&aitv.it_value, (struct timeval *)&time);
+ timeout(realitexpire, (caddr_t)p, hzto(&aitv.it_value));
+ }
+ p->p_realtimer = aitv;
+ } else
+ p->p_stats->p_timer[uap->which] = aitv;
+ splx(s);
+ return (0);
+}
+
+/*
+ * Real interval timer expired:
+ * send process whose timer expired an alarm signal.
+ * If time is not set up to reload, then just return.
+ * Else compute next time timer should go off which is > current time.
+ * This is where delay in processing this timeout causes multiple
+ * SIGALRM calls to be compressed into one.
+ */
+void
+realitexpire(arg)
+ void *arg;
+{
+ register struct proc *p;
+ int s;
+
+ p = (struct proc *)arg;
+ psignal(p, SIGALRM);
+ if (!timerisset(&p->p_realtimer.it_interval)) {
+ timerclear(&p->p_realtimer.it_value);
+ return;
+ }
+ for (;;) {
+ s = splclock();
+ timevaladd(&p->p_realtimer.it_value,
+ &p->p_realtimer.it_interval);
+ if (timercmp(&p->p_realtimer.it_value, &time, >)) {
+ timeout(realitexpire, (caddr_t)p,
+ hzto(&p->p_realtimer.it_value));
+ splx(s);
+ return;
+ }
+ splx(s);
+ }
+}
+
+/*
+ * Check that a proposed value to load into the .it_value or
+ * .it_interval part of an interval timer is acceptable, and
+ * fix it to have at least minimal value (i.e. if it is less
+ * than the resolution of the clock, round it up.)
+ */
+int
+itimerfix(tv)
+ struct timeval *tv;
+{
+
+ if (tv->tv_sec < 0 || tv->tv_sec > 100000000 ||
+ tv->tv_usec < 0 || tv->tv_usec >= 1000000)
+ return (EINVAL);
+ if (tv->tv_sec == 0 && tv->tv_usec != 0 && tv->tv_usec < tick)
+ tv->tv_usec = tick;
+ return (0);
+}
+
+/*
+ * Decrement an interval timer by a specified number
+ * of microseconds, which must be less than a second,
+ * i.e. < 1000000. If the timer expires, then reload
+ * it. In this case, carry over (usec - old value) to
+ * reduce the value reloaded into the timer so that
+ * the timer does not drift. This routine assumes
+ * that it is called in a context where the timers
+ * on which it is operating cannot change in value.
+ */
+int
+itimerdecr(itp, usec)
+ register struct itimerval *itp;
+ int usec;
+{
+
+ if (itp->it_value.tv_usec < usec) {
+ if (itp->it_value.tv_sec == 0) {
+ /* expired, and already in next interval */
+ usec -= itp->it_value.tv_usec;
+ goto expire;
+ }
+ itp->it_value.tv_usec += 1000000;
+ itp->it_value.tv_sec--;
+ }
+ itp->it_value.tv_usec -= usec;
+ usec = 0;
+ if (timerisset(&itp->it_value))
+ return (1);
+ /* expired, exactly at end of interval */
+expire:
+ if (timerisset(&itp->it_interval)) {
+ itp->it_value = itp->it_interval;
+ itp->it_value.tv_usec -= usec;
+ if (itp->it_value.tv_usec < 0) {
+ itp->it_value.tv_usec += 1000000;
+ itp->it_value.tv_sec--;
+ }
+ } else
+ itp->it_value.tv_usec = 0; /* sec is already 0 */
+ return (0);
+}
+
+/*
+ * Add and subtract routines for timevals.
+ * N.B.: subtract routine doesn't deal with
+ * results which are before the beginning,
+ * it just gets very confused in this case.
+ * Caveat emptor.
+ */
+void
+timevaladd(t1, t2)
+ struct timeval *t1, *t2;
+{
+
+ t1->tv_sec += t2->tv_sec;
+ t1->tv_usec += t2->tv_usec;
+ timevalfix(t1);
+}
+
+void
+timevalsub(t1, t2)
+ struct timeval *t1, *t2;
+{
+
+ t1->tv_sec -= t2->tv_sec;
+ t1->tv_usec -= t2->tv_usec;
+ timevalfix(t1);
+}
+
+void
+timevalfix(t1)
+ struct timeval *t1;
+{
+
+ if (t1->tv_usec < 0) {
+ t1->tv_sec--;
+ t1->tv_usec += 1000000;
+ }
+ if (t1->tv_usec >= 1000000) {
+ t1->tv_sec++;
+ t1->tv_usec -= 1000000;
+ }
+}
diff --git a/sys/kern/kern_timeout.c b/sys/kern/kern_timeout.c
new file mode 100644
index 0000000..4017e96
--- /dev/null
+++ b/sys/kern/kern_timeout.c
@@ -0,0 +1,529 @@
+/*-
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_clock.c 8.5 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/dkstat.h>
+#include <sys/callout.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/resourcevar.h>
+
+#include <machine/cpu.h>
+
+#ifdef GPROF
+#include <sys/gmon.h>
+#endif
+
+/*
+ * Clock handling routines.
+ *
+ * This code is written to operate with two timers that run independently of
+ * each other. The main clock, running hz times per second, is used to keep
+ * track of real time. The second timer handles kernel and user profiling,
+ * and does resource use estimation. If the second timer is programmable,
+ * it is randomized to avoid aliasing between the two clocks. For example,
+ * the randomization prevents an adversary from always giving up the cpu
+ * just before its quantum expires. Otherwise, it would never accumulate
+ * cpu ticks. The mean frequency of the second timer is stathz.
+ *
+ * If no second timer exists, stathz will be zero; in this case we drive
+ * profiling and statistics off the main clock. This WILL NOT be accurate;
+ * do not do it unless absolutely necessary.
+ *
+ * The statistics clock may (or may not) be run at a higher rate while
+ * profiling. This profile clock runs at profhz. We require that profhz
+ * be an integral multiple of stathz.
+ *
+ * If the statistics clock is running fast, it must be divided by the ratio
+ * profhz/stathz for statistics. (For profiling, every tick counts.)
+ */
+
+/*
+ * TODO:
+ * allocate more timeout table slots when table overflows.
+ */
+
+/*
+ * Bump a timeval by a small number of usec's.
+ */
+#define BUMPTIME(t, usec) { \
+ register volatile struct timeval *tp = (t); \
+ register long us; \
+ \
+ tp->tv_usec = us = tp->tv_usec + (usec); \
+ if (us >= 1000000) { \
+ tp->tv_usec = us - 1000000; \
+ tp->tv_sec++; \
+ } \
+}
+
+int stathz;
+int profhz;
+int profprocs;
+int ticks;
+static int psdiv, pscnt; /* prof => stat divider */
+int psratio; /* ratio: prof / stat */
+
+volatile struct timeval time;
+volatile struct timeval mono_time;
+
+/*
+ * Initialize clock frequencies and start both clocks running.
+ */
+void
+initclocks()
+{
+ register int i;
+
+ /*
+ * Set divisors to 1 (normal case) and let the machine-specific
+ * code do its bit.
+ */
+ psdiv = pscnt = 1;
+ cpu_initclocks();
+
+ /*
+ * Compute profhz/stathz, and fix profhz if needed.
+ */
+ i = stathz ? stathz : hz;
+ if (profhz == 0)
+ profhz = i;
+ psratio = profhz / i;
+}
+
+/*
+ * The real-time timer, interrupting hz times per second.
+ */
+void
+hardclock(frame)
+ register struct clockframe *frame;
+{
+ register struct callout *p1;
+ register struct proc *p;
+ register int delta, needsoft;
+ extern int tickdelta;
+ extern long timedelta;
+
+ /*
+ * Update real-time timeout queue.
+ * At front of queue are some number of events which are ``due''.
+ * The time to these is <= 0 and if negative represents the
+ * number of ticks which have passed since it was supposed to happen.
+ * The rest of the q elements (times > 0) are events yet to happen,
+ * where the time for each is given as a delta from the previous.
+ * Decrementing just the first of these serves to decrement the time
+ * to all events.
+ */
+ needsoft = 0;
+ for (p1 = calltodo.c_next; p1 != NULL; p1 = p1->c_next) {
+ if (--p1->c_time > 0)
+ break;
+ needsoft = 1;
+ if (p1->c_time == 0)
+ break;
+ }
+
+ p = curproc;
+ if (p) {
+ register struct pstats *pstats;
+
+ /*
+ * Run current process's virtual and profile time, as needed.
+ */
+ pstats = p->p_stats;
+ if (CLKF_USERMODE(frame) &&
+ timerisset(&pstats->p_timer[ITIMER_VIRTUAL].it_value) &&
+ itimerdecr(&pstats->p_timer[ITIMER_VIRTUAL], tick) == 0)
+ psignal(p, SIGVTALRM);
+ if (timerisset(&pstats->p_timer[ITIMER_PROF].it_value) &&
+ itimerdecr(&pstats->p_timer[ITIMER_PROF], tick) == 0)
+ psignal(p, SIGPROF);
+ }
+
+ /*
+ * If no separate statistics clock is available, run it from here.
+ */
+ if (stathz == 0)
+ statclock(frame);
+
+ /*
+ * Increment the time-of-day. The increment is just ``tick'' unless
+ * we are still adjusting the clock; see adjtime().
+ */
+ ticks++;
+ if (timedelta == 0)
+ delta = tick;
+ else {
+ delta = tick + tickdelta;
+ timedelta -= tickdelta;
+ }
+ BUMPTIME(&time, delta);
+ BUMPTIME(&mono_time, delta);
+
+ /*
+ * Process callouts at a very low cpu priority, so we don't keep the
+ * relatively high clock interrupt priority any longer than necessary.
+ */
+ if (needsoft) {
+ if (CLKF_BASEPRI(frame)) {
+ /*
+ * Save the overhead of a software interrupt;
+ * it will happen as soon as we return, so do it now.
+ */
+ (void)splsoftclock();
+ softclock();
+ } else
+ setsoftclock();
+ }
+}
+
+/*
+ * Software (low priority) clock interrupt.
+ * Run periodic events from timeout queue.
+ */
+/*ARGSUSED*/
+void
+softclock()
+{
+ register struct callout *c;
+ register void *arg;
+ register void (*func) __P((void *));
+ register int s;
+
+ s = splhigh();
+ while ((c = calltodo.c_next) != NULL && c->c_time <= 0) {
+ func = c->c_func;
+ arg = c->c_arg;
+ calltodo.c_next = c->c_next;
+ c->c_next = callfree;
+ callfree = c;
+ splx(s);
+ (*func)(arg);
+ (void) splhigh();
+ }
+ splx(s);
+}
+
+/*
+ * timeout --
+ * Execute a function after a specified length of time.
+ *
+ * untimeout --
+ * Cancel previous timeout function call.
+ *
+ * See AT&T BCI Driver Reference Manual for specification. This
+ * implementation differs from that one in that no identification
+ * value is returned from timeout, rather, the original arguments
+ * to timeout are used to identify entries for untimeout.
+ */
+void
+timeout(ftn, arg, ticks)
+ void (*ftn) __P((void *));
+ void *arg;
+ register int ticks;
+{
+ register struct callout *new, *p, *t;
+ register int s;
+
+ if (ticks <= 0)
+ ticks = 1;
+
+ /* Lock out the clock. */
+ s = splhigh();
+
+ /* Fill in the next free callout structure. */
+ if (callfree == NULL)
+ panic("timeout table full");
+ new = callfree;
+ callfree = new->c_next;
+ new->c_arg = arg;
+ new->c_func = ftn;
+
+ /*
+ * The time for each event is stored as a difference from the time
+ * of the previous event on the queue. Walk the queue, correcting
+ * the ticks argument for queue entries passed. Correct the ticks
+ * value for the queue entry immediately after the insertion point
+ * as well. Watch out for negative c_time values; these represent
+ * overdue events.
+ */
+ for (p = &calltodo;
+ (t = p->c_next) != NULL && ticks > t->c_time; p = t)
+ if (t->c_time > 0)
+ ticks -= t->c_time;
+ new->c_time = ticks;
+ if (t != NULL)
+ t->c_time -= ticks;
+
+ /* Insert the new entry into the queue. */
+ p->c_next = new;
+ new->c_next = t;
+ splx(s);
+}
+
+void
+untimeout(ftn, arg)
+ void (*ftn) __P((void *));
+ void *arg;
+{
+ register struct callout *p, *t;
+ register int s;
+
+ s = splhigh();
+ for (p = &calltodo; (t = p->c_next) != NULL; p = t)
+ if (t->c_func == ftn && t->c_arg == arg) {
+ /* Increment next entry's tick count. */
+ if (t->c_next && t->c_time > 0)
+ t->c_next->c_time += t->c_time;
+
+ /* Move entry from callout queue to callfree queue. */
+ p->c_next = t->c_next;
+ t->c_next = callfree;
+ callfree = t;
+ break;
+ }
+ splx(s);
+}
+
+/*
+ * Compute number of hz until specified time. Used to
+ * compute third argument to timeout() from an absolute time.
+ */
+int
+hzto(tv)
+ struct timeval *tv;
+{
+ register long ticks, sec;
+ int s;
+
+ /*
+ * If number of milliseconds will fit in 32 bit arithmetic,
+ * then compute number of milliseconds to time and scale to
+ * ticks. Otherwise just compute number of hz in time, rounding
+ * times greater than representible to maximum value.
+ *
+ * Delta times less than 25 days can be computed ``exactly''.
+ * Maximum value for any timeout in 10ms ticks is 250 days.
+ */
+ s = splhigh();
+ sec = tv->tv_sec - time.tv_sec;
+ if (sec <= 0x7fffffff / 1000 - 1000)
+ ticks = ((tv->tv_sec - time.tv_sec) * 1000 +
+ (tv->tv_usec - time.tv_usec) / 1000) / (tick / 1000);
+ else if (sec <= 0x7fffffff / hz)
+ ticks = sec * hz;
+ else
+ ticks = 0x7fffffff;
+ splx(s);
+ return (ticks);
+}
+
+/*
+ * Start profiling on a process.
+ *
+ * Kernel profiling passes proc0 which never exits and hence
+ * keeps the profile clock running constantly.
+ */
+void
+startprofclock(p)
+ register struct proc *p;
+{
+ int s;
+
+ if ((p->p_flag & P_PROFIL) == 0) {
+ p->p_flag |= P_PROFIL;
+ if (++profprocs == 1 && stathz != 0) {
+ s = splstatclock();
+ psdiv = pscnt = psratio;
+ setstatclockrate(profhz);
+ splx(s);
+ }
+ }
+}
+
+/*
+ * Stop profiling on a process.
+ */
+void
+stopprofclock(p)
+ register struct proc *p;
+{
+ int s;
+
+ if (p->p_flag & P_PROFIL) {
+ p->p_flag &= ~P_PROFIL;
+ if (--profprocs == 0 && stathz != 0) {
+ s = splstatclock();
+ psdiv = pscnt = 1;
+ setstatclockrate(stathz);
+ splx(s);
+ }
+ }
+}
+
+int dk_ndrive = DK_NDRIVE;
+
+/*
+ * Statistics clock. Grab profile sample, and if divider reaches 0,
+ * do process and kernel statistics.
+ */
+void
+statclock(frame)
+ register struct clockframe *frame;
+{
+#ifdef GPROF
+ register struct gmonparam *g;
+#endif
+ register struct proc *p;
+ register int i;
+
+ if (CLKF_USERMODE(frame)) {
+ p = curproc;
+ if (p->p_flag & P_PROFIL)
+ addupc_intr(p, CLKF_PC(frame), 1);
+ if (--pscnt > 0)
+ return;
+ /*
+ * Came from user mode; CPU was in user state.
+ * If this process is being profiled record the tick.
+ */
+ p->p_uticks++;
+ if (p->p_nice > NZERO)
+ cp_time[CP_NICE]++;
+ else
+ cp_time[CP_USER]++;
+ } else {
+#ifdef GPROF
+ /*
+ * Kernel statistics are just like addupc_intr, only easier.
+ */
+ g = &_gmonparam;
+ if (g->state == GMON_PROF_ON) {
+ i = CLKF_PC(frame) - g->lowpc;
+ if (i < g->textsize) {
+ i /= HISTFRACTION * sizeof(*g->kcount);
+ g->kcount[i]++;
+ }
+ }
+#endif
+ if (--pscnt > 0)
+ return;
+ /*
+ * Came from kernel mode, so we were:
+ * - handling an interrupt,
+ * - doing syscall or trap work on behalf of the current
+ * user process, or
+ * - spinning in the idle loop.
+ * Whichever it is, charge the time as appropriate.
+ * Note that we charge interrupts to the current process,
+ * regardless of whether they are ``for'' that process,
+ * so that we know how much of its real time was spent
+ * in ``non-process'' (i.e., interrupt) work.
+ */
+ p = curproc;
+ if (CLKF_INTR(frame)) {
+ if (p != NULL)
+ p->p_iticks++;
+ cp_time[CP_INTR]++;
+ } else if (p != NULL) {
+ p->p_sticks++;
+ cp_time[CP_SYS]++;
+ } else
+ cp_time[CP_IDLE]++;
+ }
+ pscnt = psdiv;
+
+ /*
+ * We maintain statistics shown by user-level statistics
+ * programs: the amount of time in each cpu state, and
+ * the amount of time each of DK_NDRIVE ``drives'' is busy.
+ *
+ * XXX should either run linked list of drives, or (better)
+ * grab timestamps in the start & done code.
+ */
+ for (i = 0; i < DK_NDRIVE; i++)
+ if (dk_busy & (1 << i))
+ dk_time[i]++;
+
+ /*
+ * We adjust the priority of the current process. The priority of
+ * a process gets worse as it accumulates CPU time. The cpu usage
+ * estimator (p_estcpu) is increased here. The formula for computing
+ * priorities (in kern_synch.c) will compute a different value each
+ * time p_estcpu increases by 4. The cpu usage estimator ramps up
+ * quite quickly when the process is running (linearly), and decays
+ * away exponentially, at a rate which is proportionally slower when
+ * the system is busy. The basic principal is that the system will
+ * 90% forget that the process used a lot of CPU time in 5 * loadav
+ * seconds. This causes the system to favor processes which haven't
+ * run much recently, and to round-robin among other processes.
+ */
+ if (p != NULL) {
+ p->p_cpticks++;
+ if (++p->p_estcpu == 0)
+ p->p_estcpu--;
+ if ((p->p_estcpu & 3) == 0) {
+ resetpriority(p);
+ if (p->p_priority >= PUSER)
+ p->p_priority = p->p_usrpri;
+ }
+ }
+}
+
+/*
+ * Return information about system clocks.
+ */
+int
+sysctl_clockrate(where, sizep)
+ register char *where;
+ size_t *sizep;
+{
+ struct clockinfo clkinfo;
+
+ /*
+ * Construct clockinfo structure.
+ */
+ clkinfo.hz = hz;
+ clkinfo.tick = tick;
+ clkinfo.profhz = profhz;
+ clkinfo.stathz = stathz ? stathz : hz;
+ return (sysctl_rdstruct(where, sizep, NULL, &clkinfo, sizeof(clkinfo)));
+}
diff --git a/sys/kern/kern_xxx.c b/sys/kern/kern_xxx.c
new file mode 100644
index 0000000..656430d
--- /dev/null
+++ b/sys/kern/kern_xxx.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_xxx.c 8.2 (Berkeley) 11/14/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/reboot.h>
+#include <vm/vm.h>
+#include <sys/sysctl.h>
+#include <sys/utsname.h>
+
+char domainname[MAXHOSTNAMELEN];
+int domainnamelen;
+
+struct reboot_args {
+ int opt;
+};
+/* ARGSUSED */
+int
+reboot(p, uap, retval)
+ struct proc *p;
+ struct reboot_args *uap;
+ int *retval;
+{
+ int error;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ boot(uap->opt);
+ return (0);
+}
+
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+
+struct gethostname_args {
+ char *hostname;
+ u_int len;
+};
+/* ARGSUSED */
+int
+ogethostname(p, uap, retval)
+ struct proc *p;
+ struct gethostname_args *uap;
+ int *retval;
+{
+ int name;
+
+ name = KERN_HOSTNAME;
+ return (kern_sysctl(&name, 1, uap->hostname, &uap->len, 0, 0));
+}
+
+struct sethostname_args {
+ char *hostname;
+ u_int len;
+};
+/* ARGSUSED */
+int
+osethostname(p, uap, retval)
+ struct proc *p;
+ register struct sethostname_args *uap;
+ int *retval;
+{
+ int name;
+ int error;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ name = KERN_HOSTNAME;
+ return (kern_sysctl(&name, 1, 0, 0, uap->hostname, uap->len));
+}
+
+extern long hostid;
+
+struct gethostid_args {
+ int dummy;
+};
+/* ARGSUSED */
+int
+ogethostid(p, uap, retval)
+ struct proc *p;
+ struct gethostid_args *uap;
+ int *retval;
+{
+
+ *(long *)retval = hostid;
+ return (0);
+}
+#endif /* COMPAT_43 || COMPAT_SUNOS */
+
+#ifdef COMPAT_43
+struct sethostid_args {
+ long hostid;
+};
+/* ARGSUSED */
+int
+osethostid(p, uap, retval)
+ struct proc *p;
+ struct sethostid_args *uap;
+ int *retval;
+{
+ int error;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ hostid = uap->hostid;
+ return (0);
+}
+
+int
+oquota()
+{
+
+ return (ENOSYS);
+}
+#endif /* COMPAT_43 */
+
+void
+shutdown_nice(void)
+{
+ register struct proc *p;
+
+ /* Send a signal to init(8) and have it shutdown the world */
+ p = pfind(1);
+ psignal(p, SIGINT);
+
+ return;
+}
+
+
+struct uname_args {
+ struct utsname *name;
+};
+
+/* ARGSUSED */
+int
+uname(p, uap, retval)
+ struct proc *p;
+ struct uname_args *uap;
+ int *retval;
+{
+ int name;
+ int len;
+ int rtval;
+ char *s, *us;
+
+ name = KERN_OSTYPE;
+ len = sizeof uap->name->sysname;
+ rtval = kern_sysctl(&name, 1, uap->name->sysname, &len, 0, 0, p);
+ if( rtval) return rtval;
+ subyte( uap->name->sysname + sizeof(uap->name->sysname) - 1, 0);
+
+ name = KERN_HOSTNAME;
+ len = sizeof uap->name->nodename;
+ rtval = kern_sysctl(&name, 1, uap->name->nodename, &len, 0, 0, p);
+ if( rtval) return rtval;
+ subyte( uap->name->nodename + sizeof(uap->name->nodename) - 1, 0);
+
+ name = KERN_OSRELEASE;
+ len = sizeof uap->name->release;
+ rtval = kern_sysctl(&name, 1, uap->name->release, &len, 0, 0, p);
+ if( rtval) return rtval;
+ subyte( uap->name->release + sizeof(uap->name->release) - 1, 0);
+
+/*
+ name = KERN_VERSION;
+ len = sizeof uap->name->version;
+ rtval = kern_sysctl(&name, 1, uap->name->version, &len, 0, 0, p);
+ if( rtval) return rtval;
+ subyte( uap->name->version + sizeof(uap->name->version) - 1, 0);
+*/
+
+/*
+ * this stupid hackery to make the version field look like FreeBSD 1.1
+ */
+ for(s = version; *s && *s != '#'; s++);
+
+ for(us = uap->name->version; *s && *s != ':'; s++) {
+ rtval = subyte( us++, *s);
+ if( rtval)
+ return rtval;
+ }
+ rtval = subyte( us++, 0);
+ if( rtval)
+ return rtval;
+
+ name = HW_MACHINE;
+ len = sizeof uap->name->machine;
+ rtval = hw_sysctl(&name, 1, uap->name->machine, &len, 0, 0, p);
+ if( rtval) return rtval;
+ subyte( uap->name->machine + sizeof(uap->name->machine) - 1, 0);
+
+ return 0;
+}
+
+struct getdomainname_args {
+ char *domainname;
+ u_int len;
+};
+
+/* ARGSUSED */
+int
+getdomainname(p, uap, retval)
+ struct proc *p;
+ struct getdomainname_args *uap;
+ int *retval;
+{
+ if (uap->len > domainnamelen + 1)
+ uap->len = domainnamelen + 1;
+ return (copyout((caddr_t)domainname, (caddr_t)uap->domainname, uap->len));
+}
+
+struct setdomainname_args {
+ char *domainname;
+ u_int len;
+};
+
+/* ARGSUSED */
+int
+setdomainname(p, uap, retval)
+ struct proc *p;
+ struct setdomainname_args *uap;
+ int *retval;
+{
+ int error;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ if (uap->len > sizeof (domainname) - 1)
+ return EINVAL;
+ domainnamelen = uap->len;
+ error = copyin((caddr_t)uap->domainname, domainname, uap->len);
+ domainname[domainnamelen] = 0;
+ return (error);
+}
+
diff --git a/sys/kern/makesyscalls.sh b/sys/kern/makesyscalls.sh
new file mode 100644
index 0000000..0ddea0c
--- /dev/null
+++ b/sys/kern/makesyscalls.sh
@@ -0,0 +1,171 @@
+#! /bin/sh -
+# @(#)makesyscalls.sh 8.1 (Berkeley) 6/10/93
+
+set -e
+
+# name of compat option:
+compat=COMPAT_43
+
+# output files:
+sysnames="syscalls.c"
+syshdr="../sys/syscall.h"
+syssw="init_sysent.c"
+
+# tmp files:
+sysdcl="sysent.dcl"
+syscompat="sysent.compat"
+sysent="sysent.switch"
+
+trap "rm $sysdcl $syscompat $sysent" 0
+
+case $# in
+ 0) echo "Usage: $0 input-file" 1>&2
+ exit 1
+ ;;
+esac
+
+awk < $1 "
+ BEGIN {
+ sysdcl = \"$sysdcl\"
+ syscompat = \"$syscompat\"
+ sysent = \"$sysent\"
+ sysnames = \"$sysnames\"
+ syshdr = \"$syshdr\"
+ compat = \"$compat\"
+ infile = \"$1\"
+ "'
+
+ printf "/*\n * System call switch table.\n *\n" > sysdcl
+ printf " * DO NOT EDIT-- this file is automatically generated.\n" > sysdcl
+
+ printf "\n#ifdef %s\n", compat > syscompat
+ printf "#define compat(n, name) n, __CONCAT(o,name)\n\n" > syscompat
+
+ printf "/*\n * System call names.\n *\n" > sysnames
+ printf " * DO NOT EDIT-- this file is automatically generated.\n" > sysnames
+
+ printf "/*\n * System call numbers.\n *\n" > syshdr
+ printf " * DO NOT EDIT-- this file is automatically generated.\n" > syshdr
+ }
+ NR == 1 {
+ printf " * created from%s\n */\n\n", $0 > sysdcl
+ printf "#include <sys/param.h>\n" > sysdcl
+ printf "#include <sys/systm.h>\n\n" > sysdcl
+ printf "int\tnosys();\n\n" > sysdcl
+
+ printf "struct sysent sysent[] = {\n" > sysent
+
+ printf " * created from%s\n */\n\n", $0 > sysnames
+ printf "char *syscallnames[] = {\n" > sysnames
+
+ printf " * created from%s\n */\n\n", $0 > syshdr
+ next
+ }
+ NF == 0 || $1 ~ /^;/ {
+ next
+ }
+ $1 ~ /^#[ ]*if/ {
+ print > sysent
+ print > sysdcl
+ print > syscompat
+ print > sysnames
+ savesyscall = syscall
+ next
+ }
+ $1 ~ /^#[ ]*else/ {
+ print > sysent
+ print > sysdcl
+ print > syscompat
+ print > sysnames
+ syscall = savesyscall
+ next
+ }
+ $1 ~ /^#/ {
+ print > sysent
+ print > sysdcl
+ print > syscompat
+ print > sysnames
+ next
+ }
+ syscall != $1 {
+ printf "%s: line %d: syscall number out of sync at %d\n", \
+ infile, NR, syscall
+ printf "line is:\n"
+ print
+ exit 1
+ }
+ { comment = $4
+ for (i = 5; i <= NF; i++)
+ comment = comment " " $i
+ if (NF < 5)
+ $5 = $4
+ }
+ $2 == "STD" {
+ printf("int\t%s();\n", $4) > sysdcl
+ printf("\t{ %d, %s },\t\t\t/* %d = %s */\n", \
+ $3, $4, syscall, $5) > sysent
+ printf("\t\"%s\",\t\t\t/* %d = %s */\n", \
+ $5, syscall, $5) > sysnames
+ printf("#define\tSYS_%s\t%d\n", \
+ $5, syscall) > syshdr
+ syscall++
+ next
+ }
+ $2 == "COMPAT" {
+ printf("int\to%s();\n", $4) > syscompat
+ printf("\t{ compat(%d,%s) },\t\t/* %d = old %s */\n", \
+ $3, $4, syscall, $5) > sysent
+ printf("\t\"old.%s\",\t\t/* %d = old %s */\n", \
+ $5, syscall, $5) > sysnames
+ printf("\t\t\t\t/* %d is old %s */\n", \
+ syscall, comment) > syshdr
+ syscall++
+ next
+ }
+ $2 == "LIBCOMPAT" {
+ printf("int\to%s();\n", $4) > syscompat
+ printf("\t{ compat(%d,%s) },\t\t/* %d = old %s */\n", \
+ $3, $4, syscall, $5) > sysent
+ printf("\t\"old.%s\",\t\t/* %d = old %s */\n", \
+ $5, syscall, $5) > sysnames
+ printf("#define\tSYS_%s\t%d\t/* compatibility; still used by libc */\n", \
+ $5, syscall) > syshdr
+ syscall++
+ next
+ }
+ $2 == "OBSOL" {
+ printf("\t{ 0, nosys },\t\t\t/* %d = obsolete %s */\n", \
+ syscall, comment) > sysent
+ printf("\t\"obs_%s\",\t\t\t/* %d = obsolete %s */\n", \
+ $4, syscall, comment) > sysnames
+ printf("\t\t\t\t/* %d is obsolete %s */\n", \
+ syscall, comment) > syshdr
+ syscall++
+ next
+ }
+ $2 == "UNIMPL" {
+ printf("\t{ 0, nosys },\t\t\t/* %d = %s */\n", \
+ syscall, comment) > sysent
+ printf("\t\"#%d\",\t\t\t/* %d = %s */\n", \
+ syscall, syscall, comment) > sysnames
+ syscall++
+ next
+ }
+ {
+ printf "%s: line %d: unrecognized keyword %s\n", infile, NR, $2
+ exit 1
+ }
+ END {
+ printf("\n#else /* %s */\n", compat) > syscompat
+ printf("#define compat(n, name) 0, nosys\n") > syscompat
+ printf("#endif /* %s */\n\n", compat) > syscompat
+
+ printf("};\n\n") > sysent
+ printf("int\tnsysent = sizeof(sysent) / sizeof(sysent[0]);\n") > sysent
+
+ printf("};\n") > sysnames
+ } '
+
+cat $sysdcl $syscompat $sysent >$syssw
+
+chmod 444 $sysnames $syshdr $syssw
diff --git a/sys/kern/subr_autoconf.c b/sys/kern/subr_autoconf.c
new file mode 100644
index 0000000..af17988
--- /dev/null
+++ b/sys/kern/subr_autoconf.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratories.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)subr_autoconf.c 8.1 (Berkeley) 6/10/93
+ *
+ * from: $Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp $ (LBL)
+ */
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+
+/*
+ * Autoconfiguration subroutines.
+ */
+
+/*
+ * ioconf.c exports exactly two names: cfdata and cfroots. All system
+ * devices and drivers are found via these tables.
+ */
+extern struct cfdata cfdata[];
+extern short cfroots[];
+
+#define ROOT ((struct device *)NULL)
+
+struct matchinfo {
+ cfmatch_t fn;
+ struct device *parent;
+ void *aux;
+ struct cfdata *match;
+ int pri;
+};
+
+/*
+ * Apply the matching function and choose the best. This is used
+ * a few times and we want to keep the code small.
+ */
+static void
+mapply(m, cf)
+ register struct matchinfo *m;
+ register struct cfdata *cf;
+{
+ register int pri;
+
+ if (m->fn != NULL)
+ pri = (*m->fn)(m->parent, cf, m->aux);
+ else
+ pri = (*cf->cf_driver->cd_match)(m->parent, cf, m->aux);
+ if (pri > m->pri) {
+ m->match = cf;
+ m->pri = pri;
+ }
+}
+
+/*
+ * Iterate over all potential children of some device, calling the given
+ * function (default being the child's match function) for each one.
+ * Nonzero returns are matches; the highest value returned is considered
+ * the best match. Return the `found child' if we got a match, or NULL
+ * otherwise. The `aux' pointer is simply passed on through.
+ *
+ * Note that this function is designed so that it can be used to apply
+ * an arbitrary function to all potential children (its return value
+ * can be ignored).
+ */
+struct cfdata *
+config_search(fn, parent, aux)
+ cfmatch_t fn;
+ register struct device *parent;
+ void *aux;
+{
+ register struct cfdata *cf;
+ register short *p;
+ struct matchinfo m;
+
+ m.fn = fn;
+ m.parent = parent;
+ m.aux = aux;
+ m.match = NULL;
+ m.pri = 0;
+ for (cf = cfdata; cf->cf_driver; cf++) {
+ /*
+ * Skip cf if no longer eligible, otherwise scan through
+ * parents for one matching `parent', and try match function.
+ */
+ if (cf->cf_fstate == FSTATE_FOUND)
+ continue;
+ for (p = cf->cf_parents; *p >= 0; p++)
+ if (parent->dv_cfdata == &cfdata[*p])
+ mapply(&m, cf);
+ }
+ return (m.match);
+}
+
+/*
+ * Find the given root device.
+ * This is much like config_search, but there is no parent.
+ */
+struct cfdata *
+config_rootsearch(fn, rootname, aux)
+ register cfmatch_t fn;
+ register char *rootname;
+ register void *aux;
+{
+ register struct cfdata *cf;
+ register short *p;
+ struct matchinfo m;
+
+ m.fn = fn;
+ m.parent = ROOT;
+ m.aux = aux;
+ m.match = NULL;
+ m.pri = 0;
+ /*
+ * Look at root entries for matching name. We do not bother
+ * with found-state here since only one root should ever be
+ * searched (and it must be done first).
+ */
+ for (p = cfroots; *p >= 0; p++) {
+ cf = &cfdata[*p];
+ if (strcmp(cf->cf_driver->cd_name, rootname) == 0)
+ mapply(&m, cf);
+ }
+ return (m.match);
+}
+
+static char *msgs[3] = { "", " not configured\n", " unsupported\n" };
+
+/*
+ * The given `aux' argument describes a device that has been found
+ * on the given parent, but not necessarily configured. Locate the
+ * configuration data for that device (using the cd_match configuration
+ * driver function) and attach it, and return true. If the device was
+ * not configured, call the given `print' function and return 0.
+ */
+int
+config_found(parent, aux, print)
+ struct device *parent;
+ void *aux;
+ cfprint_t print;
+{
+ struct cfdata *cf;
+
+ if ((cf = config_search((cfmatch_t)NULL, parent, aux)) != NULL) {
+ config_attach(parent, cf, aux, print);
+ return (1);
+ }
+ printf(msgs[(*print)(aux, parent->dv_xname)]);
+ return (0);
+}
+
+/*
+ * As above, but for root devices.
+ */
+int
+config_rootfound(rootname, aux)
+ char *rootname;
+ void *aux;
+{
+ struct cfdata *cf;
+
+ if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) {
+ config_attach(ROOT, cf, aux, (cfprint_t)NULL);
+ return (1);
+ }
+ printf("root device %s not configured\n", rootname);
+ return (0);
+}
+
+/* just like sprintf(buf, "%d") except that it works from the end */
+static char *
+number(ep, n)
+ register char *ep;
+ register int n;
+{
+
+ *--ep = 0;
+ while (n >= 10) {
+ *--ep = (n % 10) + '0';
+ n /= 10;
+ }
+ *--ep = n + '0';
+ return (ep);
+}
+
+/*
+ * Attach a found device. Allocates memory for device variables.
+ */
+void
+config_attach(parent, cf, aux, print)
+ register struct device *parent;
+ register struct cfdata *cf;
+ register void *aux;
+ cfprint_t print;
+{
+ register struct device *dev;
+ register struct cfdriver *cd;
+ register size_t lname, lunit;
+ register char *xunit;
+ int myunit;
+ char num[10];
+ static struct device **nextp = &alldevs;
+
+ cd = cf->cf_driver;
+ if (cd->cd_devsize < sizeof(struct device))
+ panic("config_attach");
+ myunit = cf->cf_unit;
+ if (cf->cf_fstate == FSTATE_NOTFOUND)
+ cf->cf_fstate = FSTATE_FOUND;
+ else
+ cf->cf_unit++;
+
+ /* compute length of name and decimal expansion of unit number */
+ lname = strlen(cd->cd_name);
+ xunit = number(&num[sizeof num], myunit);
+ lunit = &num[sizeof num] - xunit;
+ if (lname + lunit >= sizeof(dev->dv_xname))
+ panic("config_attach: device name too long");
+
+ /* get memory for all device vars */
+ dev = (struct device *)malloc(cd->cd_devsize, M_DEVBUF, M_WAITOK);
+ /* XXX cannot wait! */
+ bzero(dev, cd->cd_devsize);
+ *nextp = dev; /* link up */
+ nextp = &dev->dv_next;
+ dev->dv_class = cd->cd_class;
+ dev->dv_cfdata = cf;
+ dev->dv_unit = myunit;
+ bcopy(cd->cd_name, dev->dv_xname, lname);
+ bcopy(xunit, dev->dv_xname + lname, lunit);
+ dev->dv_parent = parent;
+ if (parent == ROOT)
+ printf("%s (root)", dev->dv_xname);
+ else {
+ printf("%s at %s", dev->dv_xname, parent->dv_xname);
+ (void) (*print)(aux, (char *)0);
+ }
+
+ /* put this device in the devices array */
+ if (dev->dv_unit >= cd->cd_ndevs) {
+ /*
+ * Need to expand the array.
+ */
+ int old = cd->cd_ndevs, oldbytes, new, newbytes;
+ void **nsp;
+
+ if (old == 0) {
+ nsp = malloc(MINALLOCSIZE, M_DEVBUF, M_WAITOK); /*XXX*/
+ bzero(nsp, MINALLOCSIZE);
+ cd->cd_ndevs = MINALLOCSIZE / sizeof(void *);
+ } else {
+ new = cd->cd_ndevs;
+ do {
+ new *= 2;
+ } while (new <= dev->dv_unit);
+ cd->cd_ndevs = new;
+ oldbytes = old * sizeof(void *);
+ newbytes = new * sizeof(void *);
+ nsp = malloc(newbytes, M_DEVBUF, M_WAITOK); /*XXX*/
+ bcopy(cd->cd_devs, nsp, oldbytes);
+ bzero(&nsp[old], newbytes - oldbytes);
+ free(cd->cd_devs, M_DEVBUF);
+ }
+ cd->cd_devs = nsp;
+ }
+ if (cd->cd_devs[dev->dv_unit])
+ panic("config_attach: duplicate %s", dev->dv_xname);
+ cd->cd_devs[dev->dv_unit] = dev;
+
+ /*
+ * Before attaching, clobber any unfound devices that are
+ * otherwise identical.
+ */
+ for (cf = cfdata; cf->cf_driver; cf++)
+ if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit &&
+ cf->cf_fstate == FSTATE_NOTFOUND)
+ cf->cf_fstate = FSTATE_FOUND;
+ (*cd->cd_attach)(parent, dev, aux);
+}
+
+/*
+ * Attach an event. These must come from initially-zero space (see
+ * commented-out assignments below), but that occurs naturally for
+ * device instance variables.
+ */
+void
+evcnt_attach(dev, name, ev)
+ struct device *dev;
+ const char *name;
+ struct evcnt *ev;
+{
+ static struct evcnt **nextp = &allevents;
+
+#ifdef DIAGNOSTIC
+ if (strlen(name) >= sizeof(ev->ev_name))
+ panic("evcnt_attach");
+#endif
+ /* ev->ev_next = NULL; */
+ ev->ev_dev = dev;
+ /* ev->ev_count = 0; */
+ strcpy(ev->ev_name, name);
+ *nextp = ev;
+ nextp = &ev->ev_next;
+}
diff --git a/sys/kern/subr_clist.c b/sys/kern/subr_clist.c
new file mode 100644
index 0000000..d0b1496
--- /dev/null
+++ b/sys/kern/subr_clist.c
@@ -0,0 +1,567 @@
+/*
+ * Copyright (C) 1994, David Greenman. This software may be used, modified,
+ * copied, distributed, and sold, in both source and binary form provided
+ * that the above copyright and these terms are retained. Under no
+ * circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/clist.h>
+#include <sys/malloc.h>
+
+struct cblock *cfreelist = 0;
+int cfreecount = 0;
+
+#ifndef INITIAL_CBLOCKS
+#define INITIAL_CBLOCKS 50
+#endif
+
+void cblock_alloc_cblocks __P((int));
+
+#define MBUF_DIAG
+#ifdef MBUF_DIAG
+void
+print_nblocks()
+{
+ printf("There are currently %d bytes in cblocks\n", cfreecount);
+}
+#endif
+
+/*
+ * Called from init_main.c
+ */
+void
+clist_init()
+{
+ /*
+ * Allocate an initial base set of cblocks as a 'slush'.
+ * We allocate more with each ttyopen().
+ */
+ cblock_alloc_cblocks(INITIAL_CBLOCKS);
+ return;
+}
+
+/*
+ * Remove a cblock from the cfreelist queue and return a pointer
+ * to it.
+ */
+static inline struct cblock *
+cblock_alloc()
+{
+ struct cblock *cblockp;
+
+ cblockp = cfreelist;
+ if (!cblockp) {
+ /* XXX should syslog a message that we're out! */
+ return (0);
+ }
+ cfreelist = cblockp->c_next;
+ cblockp->c_next = NULL;
+ cfreecount -= CBSIZE;
+ return (cblockp);
+}
+
+/*
+ * Add a cblock to the cfreelist queue.
+ */
+static inline void
+cblock_free(cblockp)
+ struct cblock *cblockp;
+{
+ cblockp->c_next = cfreelist;
+ cfreelist = cblockp;
+ cfreecount += CBSIZE;
+ return;
+}
+
+/*
+ * Allocate some cblocks for the cfreelist queue.
+ */
+void
+cblock_alloc_cblocks(number)
+ int number;
+{
+ int i;
+ struct cblock *tmp;
+
+ for (i = 0; i < number; ++i) {
+ tmp = malloc(sizeof(struct cblock), M_TTYS, M_NOWAIT);
+ if (!tmp) {
+ printf("cblock_alloc_cblocks: could not malloc cblock");
+ break;
+ }
+ bzero((char *)tmp, sizeof(struct cblock));
+ cblock_free(tmp);
+ }
+ return;
+}
+
+/*
+ * Free some cblocks from the cfreelist queue back to the
+ * system malloc pool.
+ */
+void
+cblock_free_cblocks(number)
+ int number;
+{
+ int i;
+ struct cblock *tmp;
+
+ for (i = 0; i < number; ++i) {
+ tmp = cblock_alloc();
+ free(tmp, M_TTYS);
+ }
+}
+
+
+/*
+ * Get a character from the head of a clist.
+ */
+int
+getc(clistp)
+ struct clist *clistp;
+{
+ int chr = -1;
+ int s;
+ struct cblock *cblockp;
+
+ s = spltty();
+
+ /* If there are characters in the list, get one */
+ if (clistp->c_cc) {
+ cblockp = (struct cblock *)((long)clistp->c_cf & ~CROUND);
+ chr = (u_char)*clistp->c_cf;
+
+ /*
+ * If this char is quoted, set the flag.
+ */
+ if (isset(cblockp->c_quote, clistp->c_cf - (char *)cblockp->c_info))
+ chr |= TTY_QUOTE;
+
+ /*
+ * Advance to next character.
+ */
+ clistp->c_cf++;
+ clistp->c_cc--;
+ /*
+ * If we have advanced the 'first' character pointer
+ * past the end of this cblock, advance to the next one.
+ * If there are no more characters, set the first and
+ * last pointers to NULL. In either case, free the
+ * current cblock.
+ */
+ if ((clistp->c_cf >= (char *)(cblockp+1)) || (clistp->c_cc == 0)) {
+ if (clistp->c_cc > 0) {
+ clistp->c_cf = cblockp->c_next->c_info;
+ } else {
+ clistp->c_cf = clistp->c_cl = NULL;
+ }
+ cblock_free(cblockp);
+ }
+ }
+
+ splx(s);
+ return (chr);
+}
+
+/*
+ * Copy 'amount' of chars, beginning at head of clist 'clistp' to
+ * destination linear buffer 'dest'. Return number of characters
+ * actually copied.
+ */
+int
+q_to_b(clistp, dest, amount)
+ struct clist *clistp;
+ char *dest;
+ int amount;
+{
+ struct cblock *cblockp;
+ struct cblock *cblockn;
+ char *dest_orig = dest;
+ int numc;
+ int s;
+
+ s = spltty();
+
+ while (clistp && amount && (clistp->c_cc > 0)) {
+ cblockp = (struct cblock *)((long)clistp->c_cf & ~CROUND);
+ cblockn = cblockp + 1; /* pointer arithmetic! */
+ numc = min(amount, (char *)cblockn - clistp->c_cf);
+ numc = min(numc, clistp->c_cc);
+ bcopy(clistp->c_cf, dest, numc);
+ amount -= numc;
+ clistp->c_cf += numc;
+ clistp->c_cc -= numc;
+ dest += numc;
+ /*
+ * If this cblock has been emptied, advance to the next
+ * one. If there are no more characters, set the first
+ * and last pointer to NULL. In either case, free the
+ * current cblock.
+ */
+ if ((clistp->c_cf >= (char *)cblockn) || (clistp->c_cc == 0)) {
+ if (clistp->c_cc > 0) {
+ clistp->c_cf = cblockp->c_next->c_info;
+ } else {
+ clistp->c_cf = clistp->c_cl = NULL;
+ }
+ cblock_free(cblockp);
+ }
+ }
+
+ splx(s);
+ return (dest - dest_orig);
+}
+
+/*
+ * Flush 'amount' of chars, beginning at head of clist 'clistp'.
+ */
+void
+ndflush(clistp, amount)
+ struct clist *clistp;
+ int amount;
+{
+ struct cblock *cblockp;
+ struct cblock *cblockn;
+ int numc;
+ int s;
+
+ s = spltty();
+
+ while (amount && (clistp->c_cc > 0)) {
+ cblockp = (struct cblock *)((long)clistp->c_cf & ~CROUND);
+ cblockn = cblockp + 1; /* pointer arithmetic! */
+ numc = min(amount, (char *)cblockn - clistp->c_cf);
+ numc = min(numc, clistp->c_cc);
+ amount -= numc;
+ clistp->c_cf += numc;
+ clistp->c_cc -= numc;
+ /*
+ * If this cblock has been emptied, advance to the next
+ * one. If there are no more characters, set the first
+ * and last pointer to NULL. In either case, free the
+ * current cblock.
+ */
+ if ((clistp->c_cf >= (char *)cblockn) || (clistp->c_cc == 0)) {
+ if (clistp->c_cc > 0) {
+ clistp->c_cf = cblockp->c_next->c_info;
+ } else {
+ clistp->c_cf = clistp->c_cl = NULL;
+ }
+ cblock_free(cblockp);
+ }
+ }
+
+ splx(s);
+ return;
+}
+
+/*
+ * Add a character to the end of a clist. Return -1 is no
+ * more clists, or 0 for success.
+ */
+int
+putc(chr, clistp)
+ int chr;
+ struct clist *clistp;
+{
+ struct cblock *cblockp, *bclockn;
+ int s;
+
+ s = spltty();
+
+ cblockp = (struct cblock *)((long)clistp->c_cl & ~CROUND);
+
+ if (clistp->c_cl == NULL) {
+ cblockp = cblock_alloc();
+ if (cblockp) {
+ clistp->c_cf = clistp->c_cl = cblockp->c_info;
+ clistp->c_cc = 0;
+ } else {
+ splx(s);
+ return (-1);
+ }
+ } else {
+ if (((long)clistp->c_cl & CROUND) == 0) {
+ struct cblock *prev = (cblockp - 1);
+ cblockp = cblock_alloc();
+ if (cblockp) {
+ prev->c_next = cblockp;
+ clistp->c_cl = cblockp->c_info;
+ } else {
+ splx(s);
+ return (-1);
+ }
+ }
+ }
+
+ /*
+ * If this character is quoted, set the quote bit, if not, clear it.
+ */
+ if (chr & TTY_QUOTE)
+ setbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info);
+ else
+ clrbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info);
+
+ *clistp->c_cl++ = chr;
+ clistp->c_cc++;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Copy data from linear buffer to clist chain. Return the
+ * number of characters not copied.
+ */
+int
+b_to_q(src, amount, clistp)
+ char *src;
+ int amount;
+ struct clist *clistp;
+{
+ struct cblock *cblockp, *bclockn;
+ char *firstbyte, *lastbyte;
+ u_char startmask, endmask;
+ int startbit, endbit, num_between, numc;
+ int s;
+
+ s = spltty();
+
+ /*
+ * If there are no cblocks assigned to this clist yet,
+ * then get one.
+ */
+ if (clistp->c_cl == NULL) {
+ cblockp = cblock_alloc();
+ if (cblockp) {
+ clistp->c_cf = clistp->c_cl = cblockp->c_info;
+ clistp->c_cc = 0;
+ } else {
+ splx(s);
+ return (amount);
+ }
+ } else {
+ cblockp = (struct cblock *)((long)clistp->c_cl & ~CROUND);
+ }
+
+ while (amount) {
+ /*
+ * Get another cblock if needed.
+ */
+ if (((long)clistp->c_cl & CROUND) == 0) {
+ struct cblock *prev = cblockp - 1;
+ cblockp = cblock_alloc();
+ if (cblockp) {
+ prev->c_next = cblockp;
+ clistp->c_cl = cblockp->c_info;
+ } else {
+ splx(s);
+ return (amount);
+ }
+ }
+
+ /*
+ * Copy a chunk of the linear buffer up to the end
+ * of this cblock.
+ */
+ numc = min(amount, (char *)(cblockp + 1) - clistp->c_cl);
+ bcopy(src, clistp->c_cl, numc);
+
+ /*
+ * Clear quote bits. The following could probably be made into
+ * a seperate "bitzero()" routine, but why bother?
+ */
+ startbit = clistp->c_cl - (char *)cblockp->c_info;
+ endbit = startbit + numc - 1;
+
+ firstbyte = (u_char *)cblockp->c_quote + (startbit / NBBY);
+ lastbyte = (u_char *)cblockp->c_quote + (endbit / NBBY);
+
+ /*
+ * Calculate mask of bits to preserve in first and
+ * last bytes.
+ */
+ startmask = NBBY - (startbit % NBBY);
+ startmask = 0xff >> startmask;
+ endmask = (endbit % NBBY);
+ endmask = 0xff << (endmask + 1);
+
+ if (firstbyte != lastbyte) {
+ *firstbyte &= startmask;
+ *lastbyte &= endmask;
+
+ num_between = lastbyte - firstbyte - 1;
+ if (num_between)
+ bzero(firstbyte + 1, num_between);
+ } else {
+ *firstbyte &= (startmask | endmask);
+ }
+
+ /*
+ * ...and update pointer for the next chunk.
+ */
+ src += numc;
+ clistp->c_cl += numc;
+ clistp->c_cc += numc;
+ amount -= numc;
+ /*
+ * If we go through the loop again, it's always
+ * for data in the next cblock, so by adding one (cblock),
+ * (which makes the pointer 1 beyond the end of this
+ * cblock) we prepare for the assignment of 'prev'
+ * above.
+ */
+ cblockp += 1;
+
+ }
+
+ splx(s);
+ return (amount);
+}
+
+/*
+ * Get the next character in the clist. Store it at dst. Don't
+ * advance any clist pointers, but return a pointer to the next
+ * character position.
+ */
+char *
+nextc(clistp, cp, dst)
+ struct clist *clistp;
+ char *cp;
+ int *dst;
+{
+ struct cblock *cblockp;
+
+ ++cp;
+ /*
+ * See if the next character is beyond the end of
+ * the clist.
+ */
+ if (clistp->c_cc && (cp != clistp->c_cl)) {
+ /*
+ * If the next character is beyond the end of this
+ * cblock, advance to the next cblock.
+ */
+ if (((long)cp & CROUND) == 0)
+ cp = ((struct cblock *)cp - 1)->c_next->c_info;
+ cblockp = (struct cblock *)((long)cp & ~CROUND);
+
+ /*
+ * Get the character. Set the quote flag if this character
+ * is quoted.
+ */
+ *dst = (u_char)*cp | (isset(cblockp->c_quote, cp - (char *)cblockp->c_info) ? TTY_QUOTE : 0);
+
+ return (cp);
+ }
+
+ return (NULL);
+}
+
+/*
+ * "Unput" a character from a clist.
+ */
+int
+unputc(clistp)
+ struct clist *clistp;
+{
+ struct cblock *cblockp = 0, *cbp = 0;
+ int s;
+ int chr = -1;
+
+
+ s = spltty();
+
+ if (clistp->c_cc) {
+ --clistp->c_cc;
+ --clistp->c_cl;
+
+ chr = (u_char)*clistp->c_cl;
+
+ cblockp = (struct cblock *)((long)clistp->c_cl & ~CROUND);
+
+ /*
+ * Set quote flag if this character was quoted.
+ */
+ if (isset(cblockp->c_quote, (u_char *)clistp->c_cl - cblockp->c_info))
+ chr |= TTY_QUOTE;
+
+ /*
+ * If all of the characters have been unput in this
+ * cblock, then find the previous one and free this
+ * one.
+ */
+ if (clistp->c_cc && (clistp->c_cl <= (char *)cblockp->c_info)) {
+ cbp = (struct cblock *)((long)clistp->c_cf & ~CROUND);
+
+ while (cbp->c_next != cblockp)
+ cbp = cbp->c_next;
+
+ /*
+ * When the previous cblock is at the end, the 'last'
+ * pointer always points (invalidly) one past.
+ */
+ clistp->c_cl = (char *)(cbp+1);
+ cblock_free(cblockp);
+ cbp->c_next = NULL;
+ }
+ }
+
+ /*
+ * If there are no more characters on the list, then
+ * free the last cblock.
+ */
+ if ((clistp->c_cc == 0) && clistp->c_cl) {
+ cblockp = (struct cblock *)((long)clistp->c_cl & ~CROUND);
+ cblock_free(cblockp);
+ clistp->c_cf = clistp->c_cl = NULL;
+ }
+
+ splx(s);
+ return (chr);
+}
+
+/*
+ * Move characters in source clist to destination clist,
+ * preserving quote bits.
+ */
+void
+catq(src_clistp, dest_clistp)
+ struct clist *src_clistp, *dest_clistp;
+{
+ int chr, s;
+
+ s = spltty();
+ /*
+ * If the destination clist is empty (has no cblocks atttached),
+ * then we simply assign the current clist to the destination.
+ */
+ if (!dest_clistp->c_cf) {
+ dest_clistp->c_cf = src_clistp->c_cf;
+ dest_clistp->c_cl = src_clistp->c_cl;
+ src_clistp->c_cf = src_clistp->c_cl = NULL;
+
+ dest_clistp->c_cc = src_clistp->c_cc;
+ src_clistp->c_cc = 0;
+
+ splx(s);
+ return;
+ }
+ splx(s);
+
+ /*
+ * XXX This should probably be optimized to more than one
+ * character at a time.
+ */
+ while ((chr = getc(src_clistp)) != -1)
+ putc(chr, dest_clistp);
+
+ return;
+}
diff --git a/sys/kern/subr_disklabel.c b/sys/kern/subr_disklabel.c
new file mode 100644
index 0000000..cc0f28d
--- /dev/null
+++ b/sys/kern/subr_disklabel.c
@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_disksubr.c 8.5 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/disklabel.h>
+#include <sys/syslog.h>
+#include <sys/dkbad.h>
+
+/*
+ * Seek sort for disks. We depend on the driver which calls us using b_resid
+ * as the current cylinder number.
+ *
+ * The argument ap structure holds a b_actf activity chain pointer on which we
+ * keep two queues, sorted in ascending cylinder order. The first queue holds
+ * those requests which are positioned after the current cylinder (in the first
+ * request); the second holds requests which came in after their cylinder number
+ * was passed. Thus we implement a one way scan, retracting after reaching the
+ * end of the drive to the first request on the second queue, at which time it
+ * becomes the first queue.
+ *
+ * A one-way scan is natural because of the way UNIX read-ahead blocks are
+ * allocated.
+ */
+
+/*
+ * For portability with historic industry practice, the
+ * cylinder number has to be maintained in the `b_resid'
+ * field.
+ */
+#define b_cylinder b_resid
+
+void
+disksort(ap, bp)
+ register struct buf *ap, *bp;
+{
+ register struct buf *bq;
+
+ /* If the queue is empty, then it's easy. */
+ if (ap->b_actf == NULL) {
+ bp->b_actf = NULL;
+ ap->b_actf = bp;
+ return;
+ }
+
+ /*
+ * If we lie after the first (currently active) request, then we
+ * must locate the second request list and add ourselves to it.
+ */
+ bq = ap->b_actf;
+ if (bp->b_cylinder < bq->b_cylinder) {
+ while (bq->b_actf) {
+ /*
+ * Check for an ``inversion'' in the normally ascending
+ * cylinder numbers, indicating the start of the second
+ * request list.
+ */
+ if (bq->b_actf->b_cylinder < bq->b_cylinder) {
+ /*
+ * Search the second request list for the first
+ * request at a larger cylinder number. We go
+ * before that; if there is no such request, we
+ * go at end.
+ */
+ do {
+ if (bp->b_cylinder <
+ bq->b_actf->b_cylinder)
+ goto insert;
+ if (bp->b_cylinder ==
+ bq->b_actf->b_cylinder &&
+ bp->b_blkno < bq->b_actf->b_blkno)
+ goto insert;
+ bq = bq->b_actf;
+ } while (bq->b_actf);
+ goto insert; /* after last */
+ }
+ bq = bq->b_actf;
+ }
+ /*
+ * No inversions... we will go after the last, and
+ * be the first request in the second request list.
+ */
+ goto insert;
+ }
+ /*
+ * Request is at/after the current request...
+ * sort in the first request list.
+ */
+ while (bq->b_actf) {
+ /*
+ * We want to go after the current request if there is an
+ * inversion after it (i.e. it is the end of the first
+ * request list), or if the next request is a larger cylinder
+ * than our request.
+ */
+ if (bq->b_actf->b_cylinder < bq->b_cylinder ||
+ bp->b_cylinder < bq->b_actf->b_cylinder ||
+ (bp->b_cylinder == bq->b_actf->b_cylinder &&
+ bp->b_blkno < bq->b_actf->b_blkno))
+ goto insert;
+ bq = bq->b_actf;
+ }
+ /*
+ * Neither a second list nor a larger request... we go at the end of
+ * the first list, which is the same as the end of the whole schebang.
+ */
+insert: bp->b_actf = bq->b_actf;
+ bq->b_actf = bp;
+}
+
+/*
+ * Attempt to read a disk label from a device using the indicated stategy
+ * routine. The label must be partly set up before this: secpercyl and
+ * anything required in the strategy routine (e.g., sector size) must be
+ * filled in before calling us. Returns NULL on success and an error
+ * string on failure.
+ */
+char *
+readdisklabel(dev, strat, lp, dp, bdp)
+ dev_t dev;
+ int (*strat)();
+ register struct disklabel *lp;
+ struct dos_partition *dp;
+ struct dkbad *bdp;
+{
+ register struct buf *bp;
+ struct disklabel *dlp;
+ char *msg = NULL;
+ int dospartoff;
+ int i;
+ int cyl;
+
+ if (lp->d_secperunit == 0)
+ lp->d_secperunit = 0x1fffffff;
+ lp->d_npartitions = 1;
+ if (lp->d_partitions[0].p_size == 0)
+ lp->d_partitions[0].p_size = 0x1fffffff;
+ lp->d_partitions[0].p_offset = 0;
+
+ bp = geteblk((int)lp->d_secsize);
+ /* do dos partitions in the process of getting disklabel? */
+ dospartoff = 0;
+ cyl = LABELSECTOR / lp->d_secpercyl;
+ if (dp) {
+ struct dos_partition *ap;
+
+ /* read master boot record */
+ bp->b_dev = dev;
+ bp->b_blkno = DOSBBSECTOR;
+ bp->b_bcount = lp->d_secsize;
+ bp->b_flags = B_BUSY | B_READ;
+ bp->b_cylinder = DOSBBSECTOR / lp->d_secpercyl;
+ (*strat)(bp);
+
+ /* if successful, wander through dos partition table */
+ if (biowait(bp)) {
+ msg = "dos partition I/O error";
+ goto done;
+ } else {
+ /* XXX how do we check veracity/bounds of this? */
+ bcopy(bp->b_un.b_addr + DOSPARTOFF, dp,
+ NDOSPART * sizeof(*dp));
+ for (i = 0; i < NDOSPART; i++, dp++)
+ /* is this ours? */
+ if (dp->dp_size &&
+ dp->dp_typ == DOSPTYP_386BSD
+ && dospartoff == 0) {
+
+ /* need sector address for SCSI/IDE,
+ cylinder for ESDI/ST506/RLL */
+ dospartoff = dp->dp_start;
+ cyl = DPCYL(dp->dp_scyl, dp->dp_ssect);
+
+ /* update disklabel with details */
+ lp->d_partitions[0].p_size =
+ dp->dp_size;
+ lp->d_partitions[0].p_offset =
+ dp->dp_start;
+ lp->d_ntracks = dp->dp_ehd + 1;
+ lp->d_nsectors = DPSECT(dp->dp_esect);
+ lp->d_subtype |= (lp->d_subtype & 3)
+ + i | DSTYPE_INDOSPART;
+ lp->d_secpercyl = lp->d_ntracks *
+ lp->d_nsectors;
+ }
+ }
+
+ }
+
+ /* next, dig out disk label */
+ bp->b_blkno = dospartoff + LABELSECTOR;
+ bp->b_dev = dev;
+ bp->b_bcount = lp->d_secsize;
+ bp->b_flags = B_BUSY | B_READ;
+ bp->b_cylinder = cyl;
+ (*strat)(bp);
+ if (biowait(bp))
+ msg = "I/O error";
+ else for (dlp = (struct disklabel *)bp->b_data;
+ dlp <= (struct disklabel *)((char *)bp->b_data +
+ DEV_BSIZE - sizeof(*dlp));
+ dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
+ if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) {
+ if (msg == NULL)
+ msg = "no disk label";
+ } else if (dlp->d_npartitions > MAXPARTITIONS ||
+ dkcksum(dlp) != 0)
+ msg = "disk label corrupted";
+ else {
+ *lp = *dlp;
+ msg = NULL;
+ break;
+ }
+ }
+ if (msg)
+ goto done;
+
+ /* obtain bad sector table if requested and present */
+ if (bdp && (lp->d_flags & D_BADSECT)) {
+ struct dkbad *db;
+
+ printf("d_secsize: %d\n", lp->d_secsize);
+ i = 0;
+ do {
+ /* read a bad sector table */
+ bp->b_flags = B_BUSY | B_READ;
+ bp->b_blkno = lp->d_secperunit - lp->d_nsectors + i;
+ if (lp->d_secsize > DEV_BSIZE)
+ bp->b_blkno *= lp->d_secsize / DEV_BSIZE;
+ else
+ bp->b_blkno /= DEV_BSIZE / lp->d_secsize;
+ bp->b_bcount = lp->d_secsize;
+ bp->b_cylinder = lp->d_ncylinders - 1;
+ (*strat)(bp);
+
+ /* if successful, validate, otherwise try another */
+ if (biowait(bp)) {
+ msg = "bad sector table I/O error";
+ } else {
+ db = (struct dkbad *)(bp->b_un.b_addr);
+#define DKBAD_MAGIC 0x4321
+ if (db->bt_mbz == 0
+ && db->bt_flag == DKBAD_MAGIC) {
+ msg = NULL;
+ *bdp = *db;
+ break;
+ } else
+ msg = "bad sector table corrupted";
+ }
+ } while ((bp->b_flags & B_ERROR) && (i += 2) < 10 &&
+ i < lp->d_nsectors);
+ }
+
+done:
+ bp->b_flags = B_INVAL | B_AGE;
+ brelse(bp);
+ return (msg);
+}
+
+/*
+ * Check new disk label for sensibility before setting it.
+ */
+int
+setdisklabel(olp, nlp, openmask)
+ register struct disklabel *olp, *nlp;
+ u_long openmask;
+{
+ register i;
+ register struct partition *opp, *npp;
+
+ if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC ||
+ dkcksum(nlp) != 0)
+ return (EINVAL);
+ while ((i = ffs((long)openmask)) != 0) {
+ i--;
+ openmask &= ~(1 << i);
+ if (nlp->d_npartitions <= i)
+ return (EBUSY);
+ opp = &olp->d_partitions[i];
+ npp = &nlp->d_partitions[i];
+ if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size)
+ return (EBUSY);
+ /*
+ * Copy internally-set partition information
+ * if new label doesn't include it. XXX
+ */
+ if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) {
+ npp->p_fstype = opp->p_fstype;
+ npp->p_fsize = opp->p_fsize;
+ npp->p_frag = opp->p_frag;
+ npp->p_cpg = opp->p_cpg;
+ }
+ }
+ nlp->d_checksum = 0;
+ nlp->d_checksum = dkcksum(nlp);
+ *olp = *nlp;
+ return (0);
+}
+
+/* encoding of disk minor numbers, should be elsewhere... */
+#define dkunit(dev) (minor(dev) >> 3)
+#define dkpart(dev) (minor(dev) & 07)
+#define dkminor(unit, part) (((unit) << 3) | (part))
+
+/*
+ * Write disk label back to device after modification.
+ */
+int
+writedisklabel(dev, strat, lp)
+ dev_t dev;
+ int (*strat)();
+ register struct disklabel *lp;
+{
+ struct buf *bp;
+ struct disklabel *dlp;
+ int labelpart;
+ int error = 0;
+
+ labelpart = dkpart(dev);
+ if (lp->d_partitions[labelpart].p_offset != 0) {
+ if (lp->d_partitions[0].p_offset != 0)
+ return (EXDEV); /* not quite right */
+ labelpart = 0;
+ }
+ bp = geteblk((int)lp->d_secsize);
+ bp->b_dev = makedev(major(dev), dkminor(dkunit(dev), labelpart));
+ bp->b_blkno = LABELSECTOR;
+ bp->b_bcount = lp->d_secsize;
+ bp->b_flags = B_READ;
+ (*strat)(bp);
+ if (error = biowait(bp))
+ goto done;
+ for (dlp = (struct disklabel *)bp->b_data;
+ dlp <= (struct disklabel *)
+ ((char *)bp->b_data + lp->d_secsize - sizeof(*dlp));
+ dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
+ if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC &&
+ dkcksum(dlp) == 0) {
+ *dlp = *lp;
+ bp->b_flags = B_WRITE;
+ (*strat)(bp);
+ error = biowait(bp);
+ goto done;
+ }
+ }
+ error = ESRCH;
+done:
+ brelse(bp);
+ return (error);
+}
+
+/*
+ * Compute checksum for disk label.
+ */
+int
+dkcksum(lp)
+ register struct disklabel *lp;
+{
+ register u_short *start, *end;
+ register u_short sum = 0;
+
+ start = (u_short *)lp;
+ end = (u_short *)&lp->d_partitions[lp->d_npartitions];
+ while (start < end)
+ sum ^= *start++;
+ return (sum);
+}
+
+/*
+ * Disk error is the preface to plaintive error messages
+ * about failing disk transfers. It prints messages of the form
+
+hp0g: hard error reading fsbn 12345 of 12344-12347 (hp0 bn %d cn %d tn %d sn %d)
+
+ * if the offset of the error in the transfer and a disk label
+ * are both available. blkdone should be -1 if the position of the error
+ * is unknown; the disklabel pointer may be null from drivers that have not
+ * been converted to use them. The message is printed with printf
+ * if pri is LOG_PRINTF, otherwise it uses log at the specified priority.
+ * The message should be completed (with at least a newline) with printf
+ * or addlog, respectively. There is no trailing space.
+ */
+void
+diskerr(bp, dname, what, pri, blkdone, lp)
+ register struct buf *bp;
+ char *dname, *what;
+ int pri, blkdone;
+ register struct disklabel *lp;
+{
+ int unit = dkunit(bp->b_dev), part = dkpart(bp->b_dev);
+ register void (*pr) __P((const char *, ...));
+ char partname = 'a' + part;
+ int sn;
+
+ if (pri != LOG_PRINTF) {
+ log(pri, "");
+ pr = addlog;
+ } else
+ pr = printf;
+ (*pr)("%s%d%c: %s %sing fsbn ", dname, unit, partname, what,
+ bp->b_flags & B_READ ? "read" : "writ");
+ sn = bp->b_blkno;
+ if (bp->b_bcount <= DEV_BSIZE)
+ (*pr)("%d", sn);
+ else {
+ if (blkdone >= 0) {
+ sn += blkdone;
+ (*pr)("%d of ", sn);
+ }
+ (*pr)("%d-%d", bp->b_blkno,
+ bp->b_blkno + (bp->b_bcount - 1) / DEV_BSIZE);
+ }
+ if (lp && (blkdone >= 0 || bp->b_bcount <= lp->d_secsize)) {
+#ifdef tahoe
+ sn *= DEV_BSIZE / lp->d_secsize; /* XXX */
+#endif
+ sn += lp->d_partitions[part].p_offset;
+ (*pr)(" (%s%d bn %d; cn %d", dname, unit, sn,
+ sn / lp->d_secpercyl);
+ sn %= lp->d_secpercyl;
+ (*pr)(" tn %d sn %d)", sn / lp->d_nsectors, sn % lp->d_nsectors);
+ }
+}
diff --git a/sys/kern/subr_log.c b/sys/kern/subr_log.c
new file mode 100644
index 0000000..92e4543
--- /dev/null
+++ b/sys/kern/subr_log.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)subr_log.c 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Error log buffer for kernel printf's.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/ioctl.h>
+#include <sys/msgbuf.h>
+#include <sys/file.h>
+
+#define LOG_RDPRI (PZERO + 1)
+
+#define LOG_ASYNC 0x04
+#define LOG_RDWAIT 0x08
+
+struct logsoftc {
+ int sc_state; /* see above for possibilities */
+ struct selinfo sc_selp; /* process waiting on select call */
+ int sc_pgid; /* process/group for async I/O */
+} logsoftc;
+
+int log_open; /* also used in log() */
+
+/*ARGSUSED*/
+int
+logopen(dev, flags, mode, p)
+ dev_t dev;
+ int flags, mode;
+ struct proc *p;
+{
+ register struct msgbuf *mbp = msgbufp;
+
+ if (log_open)
+ return (EBUSY);
+ log_open = 1;
+ logsoftc.sc_pgid = p->p_pid; /* signal process only */
+ /*
+ * Potential race here with putchar() but since putchar should be
+ * called by autoconf, msg_magic should be initialized by the time
+ * we get here.
+ */
+ if (mbp->msg_magic != MSG_MAGIC) {
+ register int i;
+
+ mbp->msg_magic = MSG_MAGIC;
+ mbp->msg_bufx = mbp->msg_bufr = 0;
+ for (i=0; i < MSG_BSIZE; i++)
+ mbp->msg_bufc[i] = 0;
+ }
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+logclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+
+ log_open = 0;
+ logsoftc.sc_state = 0;
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+logread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ register struct msgbuf *mbp = msgbufp;
+ register long l;
+ register int s;
+ int error = 0;
+
+ s = splhigh();
+ while (mbp->msg_bufr == mbp->msg_bufx) {
+ if (flag & IO_NDELAY) {
+ splx(s);
+ return (EWOULDBLOCK);
+ }
+ logsoftc.sc_state |= LOG_RDWAIT;
+ if (error = tsleep((caddr_t)mbp, LOG_RDPRI | PCATCH,
+ "klog", 0)) {
+ splx(s);
+ return (error);
+ }
+ }
+ splx(s);
+ logsoftc.sc_state &= ~LOG_RDWAIT;
+
+ while (uio->uio_resid > 0) {
+ l = mbp->msg_bufx - mbp->msg_bufr;
+ if (l < 0)
+ l = MSG_BSIZE - mbp->msg_bufr;
+ l = min(l, uio->uio_resid);
+ if (l == 0)
+ break;
+ error = uiomove((caddr_t)&mbp->msg_bufc[mbp->msg_bufr],
+ (int)l, uio);
+ if (error)
+ break;
+ mbp->msg_bufr += l;
+ if (mbp->msg_bufr < 0 || mbp->msg_bufr >= MSG_BSIZE)
+ mbp->msg_bufr = 0;
+ }
+ return (error);
+}
+
+/*ARGSUSED*/
+int
+logselect(dev, rw, p)
+ dev_t dev;
+ int rw;
+ struct proc *p;
+{
+ int s = splhigh();
+
+ switch (rw) {
+
+ case FREAD:
+ if (msgbufp->msg_bufr != msgbufp->msg_bufx) {
+ splx(s);
+ return (1);
+ }
+ selrecord(p, &logsoftc.sc_selp);
+ break;
+ }
+ splx(s);
+ return (0);
+}
+
+void
+logwakeup()
+{
+ struct proc *p;
+
+ if (!log_open)
+ return;
+ selwakeup(&logsoftc.sc_selp);
+ if (logsoftc.sc_state & LOG_ASYNC) {
+ if (logsoftc.sc_pgid < 0)
+ gsignal(-logsoftc.sc_pgid, SIGIO);
+ else if (p = pfind(logsoftc.sc_pgid))
+ psignal(p, SIGIO);
+ }
+ if (logsoftc.sc_state & LOG_RDWAIT) {
+ wakeup((caddr_t)msgbufp);
+ logsoftc.sc_state &= ~LOG_RDWAIT;
+ }
+}
+
+/*ARGSUSED*/
+int
+logioctl(dev, com, data, flag, p)
+ dev_t dev;
+ int com;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+{
+ long l;
+ int s;
+
+ switch (com) {
+
+ /* return number of characters immediately available */
+ case FIONREAD:
+ s = splhigh();
+ l = msgbufp->msg_bufx - msgbufp->msg_bufr;
+ splx(s);
+ if (l < 0)
+ l += MSG_BSIZE;
+ *(int *)data = l;
+ break;
+
+ case FIONBIO:
+ break;
+
+ case FIOASYNC:
+ if (*(int *)data)
+ logsoftc.sc_state |= LOG_ASYNC;
+ else
+ logsoftc.sc_state &= ~LOG_ASYNC;
+ break;
+
+ case TIOCSPGRP:
+ logsoftc.sc_pgid = *(int *)data;
+ break;
+
+ case TIOCGPGRP:
+ *(int *)data = logsoftc.sc_pgid;
+ break;
+
+ default:
+ return (-1);
+ }
+ return (0);
+}
diff --git a/sys/kern/subr_param.c b/sys/kern/subr_param.c
new file mode 100644
index 0000000..c871594
--- /dev/null
+++ b/sys/kern/subr_param.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 1980, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)param.c 8.2 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/callout.h>
+#include <sys/clist.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+
+#include <ufs/ufs/quota.h>
+
+#ifdef SYSVSHM
+#include <machine/vmparam.h>
+#include <sys/shm.h>
+#endif
+
+/*
+ * System parameter formulae.
+ *
+ * This file is copied into each directory where we compile
+ * the kernel; it should be modified there to suit local taste
+ * if necessary.
+ *
+ * Compiled with -DHZ=xx -DTIMEZONE=x -DDST=x -DMAXUSERS=xx
+ */
+
+#ifndef HZ
+#define HZ 100
+#endif
+int hz = HZ;
+int tick = 1000000 / HZ;
+int tickadj = 30000 / (60 * HZ); /* can adjust 30ms in 60s */
+struct timezone tz = { TIMEZONE, DST };
+#define NPROC (20 + 16 * MAXUSERS)
+int maxproc = NPROC;
+#define NTEXT NPROC
+int vm_cache_max = NTEXT/2 + 16;
+#define NVNODE (NPROC + NTEXT + 100)
+int desiredvnodes = NVNODE;
+int maxfiles = 3 * (NPROC + MAXUSERS) + 80;
+int ncallout = 16 + NPROC;
+int nclist = 60 + 12 * MAXUSERS;
+int nmbclusters = NMBCLUSTERS;
+int fscale = FSCALE; /* kernel uses `FSCALE', user uses `fscale' */
+
+/*
+ * Values in support of System V compatible shared memory. XXX
+ */
+#ifdef SYSVSHM
+#define SHMMAX (SHMMAXPGS*NBPG)
+#define SHMMIN 1
+#define SHMMNI 32 /* <= SHMMMNI in shm.h */
+#define SHMSEG 8
+#define SHMALL (SHMMAXPGS/CLSIZE)
+
+struct shminfo shminfo = {
+ SHMMAX,
+ SHMMIN,
+ SHMMNI,
+ SHMSEG,
+ SHMALL
+};
+#endif
+
+/*
+ * These are initialized at bootstrap time
+ * to values dependent on memory size
+ */
+int nbuf, nswbuf;
+
+/*
+ * These have to be allocated somewhere; allocating
+ * them here forces loader errors if this file is omitted
+ * (if they've been externed everywhere else; hah!).
+ */
+struct callout *callout;
+struct cblock *cfree;
+struct buf *buf, *swbuf;
+char *buffers;
+
+/*
+ * Proc/pgrp hashing.
+ * Here so that hash table sizes can depend on MAXUSERS/NPROC.
+ * Hash size must be a power of two.
+ * NOW omission of this file will cause loader errors!
+ */
+
+#if NPROC > 1024
+#define PIDHSZ 512
+#else
+#if NPROC > 512
+#define PIDHSZ 256
+#else
+#if NPROC > 256
+#define PIDHSZ 128
+#else
+#define PIDHSZ 64
+#endif
+#endif
+#endif
+
+struct proc *pidhash[PIDHSZ];
+struct pgrp *pgrphash[PIDHSZ];
+int pidhashmask = PIDHSZ - 1;
diff --git a/sys/kern/subr_prf.c b/sys/kern/subr_prf.c
new file mode 100644
index 0000000..5ef4925
--- /dev/null
+++ b/sys/kern/subr_prf.c
@@ -0,0 +1,607 @@
+/*-
+ * Copyright (c) 1986, 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)subr_prf.c 8.3 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/reboot.h>
+#include <sys/msgbuf.h>
+#include <sys/proc.h>
+#include <sys/ioctl.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/tty.h>
+#include <sys/tprintf.h>
+#include <sys/syslog.h>
+#include <sys/malloc.h>
+
+/*
+ * Note that stdarg.h and the ANSI style va_start macro is used for both
+ * ANSI and traditional C compilers.
+ */
+#include <machine/stdarg.h>
+
+#ifdef KADB
+#include <machine/kdbparam.h>
+#endif
+
+#define TOCONS 0x01
+#define TOTTY 0x02
+#define TOLOG 0x04
+
+struct tty *constty; /* pointer to console "window" tty */
+
+extern cnputc(); /* standard console putc */
+int (*v_putc)() = cnputc; /* routine to putc on virtual console */
+
+void logpri __P((int level));
+static void putchar __P((int ch, int flags, struct tty *tp));
+static char *ksprintn __P((u_long num, int base, int *len));
+void kprintf __P((const char *fmt, int flags, struct tty *tp, va_list ap));
+
+int consintr = 1; /* Ok to handle console interrupts? */
+
+/*
+ * Variable panicstr contains argument to first call to panic; used as flag
+ * to indicate that the kernel has already called panic.
+ */
+const char *panicstr;
+
+/*
+ * Panic is called on unresolvable fatal errors. It prints "panic: mesg",
+ * and then reboots. If we are called twice, then we avoid trying to sync
+ * the disks as this often leads to recursive panics.
+ */
+#ifdef __GNUC__
+volatile void boot(int flags); /* boot() does not return */
+volatile /* panic() does not return */
+#endif
+void
+#ifdef __STDC__
+panic(const char *fmt, ...)
+#else
+panic(fmt, va_alist)
+ char *fmt;
+#endif
+{
+ int bootopt;
+ va_list ap;
+
+ bootopt = RB_AUTOBOOT | RB_DUMP;
+ if (panicstr)
+ bootopt |= RB_NOSYNC;
+ else
+ panicstr = fmt;
+
+ va_start(ap, fmt);
+ printf("panic: %r\n", fmt, ap);
+ va_end(ap);
+
+#ifdef KGDB
+ kgdb_panic();
+#endif
+#ifdef KADB
+ if (boothowto & RB_KDB)
+ kdbpanic();
+#endif
+#include "ddb.h"
+#if NDDB > 0
+ Debugger ("panic");
+#endif
+ boot(bootopt);
+}
+
+/*
+ * Warn that a system table is full.
+ */
+void
+tablefull(tab)
+ const char *tab;
+{
+
+ log(LOG_ERR, "%s: table is full\n", tab);
+}
+
+/*
+ * Uprintf prints to the controlling terminal for the current process.
+ * It may block if the tty queue is overfull. No message is printed if
+ * the queue does not clear in a reasonable time.
+ */
+void
+#ifdef __STDC__
+uprintf(const char *fmt, ...)
+#else
+uprintf(fmt, va_alist)
+ char *fmt;
+#endif
+{
+ register struct proc *p = curproc;
+ va_list ap;
+
+ if (p->p_flag & P_CONTROLT && p->p_session->s_ttyvp) {
+ va_start(ap, fmt);
+ kprintf(fmt, TOTTY, p->p_session->s_ttyp, ap);
+ va_end(ap);
+ }
+}
+
+tpr_t
+tprintf_open(p)
+ register struct proc *p;
+{
+
+ if (p->p_flag & P_CONTROLT && p->p_session->s_ttyvp) {
+ SESSHOLD(p->p_session);
+ return ((tpr_t) p->p_session);
+ }
+ return ((tpr_t) NULL);
+}
+
+void
+tprintf_close(sess)
+ tpr_t sess;
+{
+
+ if (sess)
+ SESSRELE((struct session *) sess);
+}
+
+/*
+ * tprintf prints on the controlling terminal associated
+ * with the given session.
+ */
+void
+#ifdef __STDC__
+tprintf(tpr_t tpr, const char *fmt, ...)
+#else
+tprintf(tpr, fmt, va_alist)
+ tpr_t tpr;
+ char *fmt;
+#endif
+{
+ register struct session *sess = (struct session *)tpr;
+ struct tty *tp = NULL;
+ int flags = TOLOG;
+ va_list ap;
+
+ logpri(LOG_INFO);
+ if (sess && sess->s_ttyvp && ttycheckoutq(sess->s_ttyp, 0)) {
+ flags |= TOTTY;
+ tp = sess->s_ttyp;
+ }
+ va_start(ap, fmt);
+ kprintf(fmt, flags, tp, ap);
+ va_end(ap);
+ logwakeup();
+}
+
+/*
+ * Ttyprintf displays a message on a tty; it should be used only by
+ * the tty driver, or anything that knows the underlying tty will not
+ * be revoke(2)'d away. Other callers should use tprintf.
+ */
+void
+#ifdef __STDC__
+ttyprintf(struct tty *tp, const char *fmt, ...)
+#else
+ttyprintf(tp, fmt, va_alist)
+ struct tty *tp;
+ char *fmt;
+#endif
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ kprintf(fmt, TOTTY, tp, ap);
+ va_end(ap);
+}
+
+extern int log_open;
+
+/*
+ * Log writes to the log buffer, and guarantees not to sleep (so can be
+ * called by interrupt routines). If there is no process reading the
+ * log yet, it writes to the console also.
+ */
+void
+#ifdef __STDC__
+log(int level, const char *fmt, ...)
+#else
+log(level, fmt, va_alist)
+ int level;
+ char *fmt;
+#endif
+{
+ register int s;
+ va_list ap;
+
+ s = splhigh();
+ logpri(level);
+ va_start(ap, fmt);
+ kprintf(fmt, TOLOG, NULL, ap);
+ splx(s);
+ va_end(ap);
+ if (!log_open) {
+ va_start(ap, fmt);
+ kprintf(fmt, TOCONS, NULL, ap);
+ va_end(ap);
+ }
+ logwakeup();
+}
+
+void
+logpri(level)
+ int level;
+{
+ register int ch;
+ register char *p;
+
+ putchar('<', TOLOG, NULL);
+ for (p = ksprintn((u_long)level, 10, NULL); ch = *p--;)
+ putchar(ch, TOLOG, NULL);
+ putchar('>', TOLOG, NULL);
+}
+
+void
+#ifdef __STDC__
+addlog(const char *fmt, ...)
+#else
+addlog(fmt, va_alist)
+ char *fmt;
+#endif
+{
+ register int s;
+ va_list ap;
+
+ s = splhigh();
+ va_start(ap, fmt);
+ kprintf(fmt, TOLOG, NULL, ap);
+ splx(s);
+ va_end(ap);
+ if (!log_open) {
+ va_start(ap, fmt);
+ kprintf(fmt, TOCONS, NULL, ap);
+ va_end(ap);
+ }
+ logwakeup();
+}
+
+void
+#ifdef __STDC__
+printf(const char *fmt, ...)
+#else
+printf(fmt, va_alist)
+ char *fmt;
+#endif
+{
+ va_list ap;
+ register int savintr;
+
+ savintr = consintr; /* disable interrupts */
+ consintr = 0;
+ va_start(ap, fmt);
+ kprintf(fmt, TOCONS | TOLOG, NULL, ap);
+ va_end(ap);
+ if (!panicstr)
+ logwakeup();
+ consintr = savintr; /* reenable interrupts */
+}
+
+/*
+ * Scaled down version of printf(3).
+ *
+ * Two additional formats:
+ *
+ * The format %b is supported to decode error registers.
+ * Its usage is:
+ *
+ * printf("reg=%b\n", regval, "<base><arg>*");
+ *
+ * where <base> is the output base expressed as a control character, e.g.
+ * \10 gives octal; \20 gives hex. Each arg is a sequence of characters,
+ * the first of which gives the bit number to be inspected (origin 1), and
+ * the next characters (up to a control character, i.e. a character <= 32),
+ * give the name of the register. Thus:
+ *
+ * kprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
+ *
+ * would produce output:
+ *
+ * reg=3<BITTWO,BITONE>
+ *
+ * The format %r passes an additional format string and argument list
+ * recursively. Its usage is:
+ *
+ * fn(char *fmt, ...)
+ * {
+ * va_list ap;
+ * va_start(ap, fmt);
+ * printf("prefix: %r: suffix\n", fmt, ap);
+ * va_end(ap);
+ * }
+ *
+ * Space or zero padding and a field width are supported for the numeric
+ * formats only.
+ */
+void
+kprintf(fmt, flags, tp, ap)
+ register const char *fmt;
+ int flags;
+ struct tty *tp;
+ va_list ap;
+{
+ register char *p, *q;
+ register int ch, n;
+ u_long ul;
+ int base, lflag, tmp, width;
+ char padc;
+
+ for (;;) {
+ padc = ' ';
+ width = 0;
+ while ((ch = *(u_char *)fmt++) != '%') {
+ if (ch == '\0')
+ return;
+ putchar(ch, flags, tp);
+ }
+ lflag = 0;
+reswitch: switch (ch = *(u_char *)fmt++) {
+ case '0':
+ padc = '0';
+ goto reswitch;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ for (width = 0;; ++fmt) {
+ width = width * 10 + ch - '0';
+ ch = *fmt;
+ if (ch < '0' || ch > '9')
+ break;
+ }
+ goto reswitch;
+ case 'l':
+ lflag = 1;
+ goto reswitch;
+ case 'b':
+ ul = va_arg(ap, int);
+ p = va_arg(ap, char *);
+ for (q = ksprintn(ul, *p++, NULL); ch = *q--;)
+ putchar(ch, flags, tp);
+
+ if (!ul)
+ break;
+
+ for (tmp = 0; n = *p++;) {
+ if (ul & (1 << (n - 1))) {
+ putchar(tmp ? ',' : '<', flags, tp);
+ for (; (n = *p) > ' '; ++p)
+ putchar(n, flags, tp);
+ tmp = 1;
+ } else
+ for (; *p > ' '; ++p)
+ continue;
+ }
+ if (tmp)
+ putchar('>', flags, tp);
+ break;
+ case 'c':
+ putchar(va_arg(ap, int), flags, tp);
+ break;
+ case 'r':
+ p = va_arg(ap, char *);
+ kprintf(p, flags, tp, va_arg(ap, va_list));
+ break;
+ case 's':
+ p = va_arg(ap, char *);
+ while (ch = *p++)
+ putchar(ch, flags, tp);
+ break;
+ case 'd':
+ ul = lflag ? va_arg(ap, long) : va_arg(ap, int);
+ if ((long)ul < 0) {
+ putchar('-', flags, tp);
+ ul = -(long)ul;
+ }
+ base = 10;
+ goto number;
+ case 'o':
+ ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
+ base = 8;
+ goto number;
+ case 'u':
+ ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
+ base = 10;
+ goto number;
+ case 'x':
+ ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
+ base = 16;
+number: p = ksprintn(ul, base, &tmp);
+ if (width && (width -= tmp) > 0)
+ while (width--)
+ putchar(padc, flags, tp);
+ while (ch = *p--)
+ putchar(ch, flags, tp);
+ break;
+ default:
+ putchar('%', flags, tp);
+ if (lflag)
+ putchar('l', flags, tp);
+ /* FALLTHROUGH */
+ case '%':
+ putchar(ch, flags, tp);
+ }
+ }
+}
+
+/*
+ * Print a character on console or users terminal. If destination is
+ * the console then the last MSGBUFS characters are saved in msgbuf for
+ * inspection later.
+ */
+static void
+putchar(c, flags, tp)
+ register int c;
+ int flags;
+ struct tty *tp;
+{
+ extern int msgbufmapped;
+ register struct msgbuf *mbp;
+
+ if (panicstr)
+ constty = NULL;
+ if ((flags & TOCONS) && tp == NULL && constty) {
+ tp = constty;
+ flags |= TOTTY;
+ }
+ if ((flags & TOTTY) && tp && tputchar(c, tp) < 0 &&
+ (flags & TOCONS) && tp == constty)
+ constty = NULL;
+ if ((flags & TOLOG) &&
+ c != '\0' && c != '\r' && c != 0177 && msgbufmapped) {
+ mbp = msgbufp;
+ if (mbp->msg_magic != MSG_MAGIC) {
+ bzero((caddr_t)mbp, sizeof(*mbp));
+ mbp->msg_magic = MSG_MAGIC;
+ }
+ mbp->msg_bufc[mbp->msg_bufx++] = c;
+ if (mbp->msg_bufx < 0 || mbp->msg_bufx >= MSG_BSIZE)
+ mbp->msg_bufx = 0;
+ }
+ if ((flags & TOCONS) && constty == NULL && c != '\0')
+ (*v_putc)(c);
+}
+
+/*
+ * Scaled down version of sprintf(3).
+ */
+#ifdef __STDC__
+int
+sprintf(char *buf, const char *cfmt, ...)
+#else
+int
+sprintf(buf, cfmt, va_alist)
+ char *buf, *cfmt;
+#endif
+{
+ register const char *fmt = cfmt;
+ register char *p, *bp;
+ register int ch, base;
+ u_long ul;
+ int lflag;
+ va_list ap;
+
+ va_start(ap, cfmt);
+ for (bp = buf; ; ) {
+ while ((ch = *(u_char *)fmt++) != '%')
+ if ((*bp++ = ch) == '\0')
+ return ((bp - buf) - 1);
+
+ lflag = 0;
+reswitch: switch (ch = *(u_char *)fmt++) {
+ case 'l':
+ lflag = 1;
+ goto reswitch;
+ case 'c':
+ *bp++ = va_arg(ap, int);
+ break;
+ case 's':
+ p = va_arg(ap, char *);
+ while (*bp++ = *p++)
+ continue;
+ --bp;
+ break;
+ case 'd':
+ ul = lflag ? va_arg(ap, long) : va_arg(ap, int);
+ if ((long)ul < 0) {
+ *bp++ = '-';
+ ul = -(long)ul;
+ }
+ base = 10;
+ goto number;
+ break;
+ case 'o':
+ ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
+ base = 8;
+ goto number;
+ break;
+ case 'u':
+ ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
+ base = 10;
+ goto number;
+ break;
+ case 'x':
+ ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
+ base = 16;
+number: for (p = ksprintn(ul, base, NULL); ch = *p--;)
+ *bp++ = ch;
+ break;
+ default:
+ *bp++ = '%';
+ if (lflag)
+ *bp++ = 'l';
+ /* FALLTHROUGH */
+ case '%':
+ *bp++ = ch;
+ }
+ }
+ va_end(ap);
+}
+
+/*
+ * Put a number (base <= 16) in a buffer in reverse order; return an
+ * optional length and a pointer to the NULL terminated (preceded?)
+ * buffer.
+ */
+static char *
+ksprintn(ul, base, lenp)
+ register u_long ul;
+ register int base, *lenp;
+{ /* A long in base 8, plus NULL. */
+ static char buf[sizeof(long) * NBBY / 3 + 2];
+ register char *p;
+
+ p = buf;
+ do {
+ *++p = "0123456789abcdef"[ul % base];
+ } while (ul /= base);
+ if (lenp)
+ *lenp = p - buf;
+ return (p);
+}
diff --git a/sys/kern/subr_prof.c b/sys/kern/subr_prof.c
new file mode 100644
index 0000000..efe56b0
--- /dev/null
+++ b/sys/kern/subr_prof.c
@@ -0,0 +1,257 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)subr_prof.c 8.3 (Berkeley) 9/23/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <machine/cpu.h>
+
+#ifdef GPROF
+#include <sys/malloc.h>
+#include <sys/gmon.h>
+
+/*
+ * Froms is actually a bunch of unsigned shorts indexing tos
+ */
+struct gmonparam _gmonparam = { GMON_PROF_OFF };
+
+extern char etext[];
+
+kmstartup()
+{
+ char *cp;
+ struct gmonparam *p = &_gmonparam;
+ /*
+ * Round lowpc and highpc to multiples of the density we're using
+ * so the rest of the scaling (here and in gprof) stays in ints.
+ */
+ p->lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER));
+ p->highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER));
+ p->textsize = p->highpc - p->lowpc;
+ printf("Profiling kernel, textsize=%d [%x..%x]\n",
+ p->textsize, p->lowpc, p->highpc);
+ p->kcountsize = p->textsize / HISTFRACTION;
+ p->hashfraction = HASHFRACTION;
+ p->fromssize = p->textsize / HASHFRACTION;
+ p->tolimit = p->textsize * ARCDENSITY / 100;
+ if (p->tolimit < MINARCS)
+ p->tolimit = MINARCS;
+ else if (p->tolimit > MAXARCS)
+ p->tolimit = MAXARCS;
+ p->tossize = p->tolimit * sizeof(struct tostruct);
+ cp = (char *)malloc(p->kcountsize + p->fromssize + p->tossize,
+ M_GPROF, M_NOWAIT);
+ if (cp == 0) {
+ printf("No memory for profiling.\n");
+ return;
+ }
+ bzero(cp, p->kcountsize + p->tossize + p->fromssize);
+ p->tos = (struct tostruct *)cp;
+ cp += p->tossize;
+ p->kcount = (u_short *)cp;
+ cp += p->kcountsize;
+ p->froms = (u_short *)cp;
+}
+
+/*
+ * Return kernel profiling information.
+ */
+sysctl_doprof(name, namelen, oldp, oldlenp, newp, newlen, p)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+{
+ struct gmonparam *gp = &_gmonparam;
+ int error;
+
+ /* all sysctl names at this level are terminal */
+ if (namelen != 1)
+ return (ENOTDIR); /* overloaded */
+
+ switch (name[0]) {
+ case GPROF_STATE:
+ error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state);
+ if (error)
+ return (error);
+ if (gp->state == GMON_PROF_OFF)
+ stopprofclock(&proc0);
+ else
+ startprofclock(&proc0);
+ return (0);
+ case GPROF_COUNT:
+ return (sysctl_struct(oldp, oldlenp, newp, newlen,
+ gp->kcount, gp->kcountsize));
+ case GPROF_FROMS:
+ return (sysctl_struct(oldp, oldlenp, newp, newlen,
+ gp->froms, gp->fromssize));
+ case GPROF_TOS:
+ return (sysctl_struct(oldp, oldlenp, newp, newlen,
+ gp->tos, gp->tossize));
+ case GPROF_GMONPARAM:
+ return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp));
+ default:
+ return (EOPNOTSUPP);
+ }
+ /* NOTREACHED */
+}
+#endif /* GPROF */
+
+/*
+ * Profiling system call.
+ *
+ * The scale factor is a fixed point number with 16 bits of fraction, so that
+ * 1.0 is represented as 0x10000. A scale factor of 0 turns off profiling.
+ */
+struct profil_args {
+ caddr_t samples;
+ u_int size;
+ u_int offset;
+ u_int scale;
+};
+/* ARGSUSED */
+int
+profil(p, uap, retval)
+ struct proc *p;
+ register struct profil_args *uap;
+ int *retval;
+{
+ register struct uprof *upp;
+ int s;
+
+ if (uap->scale > (1 << 16))
+ return (EINVAL);
+ if (uap->scale == 0) {
+ stopprofclock(p);
+ return (0);
+ }
+ upp = &p->p_stats->p_prof;
+
+ /* Block profile interrupts while changing state. */
+ s = splstatclock();
+ upp->pr_off = uap->offset;
+ upp->pr_scale = uap->scale;
+ upp->pr_base = uap->samples;
+ upp->pr_size = uap->size;
+ startprofclock(p);
+ splx(s);
+
+ return (0);
+}
+
+/*
+ * Scale is a fixed-point number with the binary point 16 bits
+ * into the value, and is <= 1.0. pc is at most 32 bits, so the
+ * intermediate result is at most 48 bits.
+ */
+#define PC_TO_INDEX(pc, prof) \
+ ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \
+ (u_quad_t)((prof)->pr_scale)) >> 16) & ~1)
+
+/*
+ * Collect user-level profiling statistics; called on a profiling tick,
+ * when a process is running in user-mode. This routine may be called
+ * from an interrupt context. We try to update the user profiling buffers
+ * cheaply with fuswintr() and suswintr(). If that fails, we revert to
+ * an AST that will vector us to trap() with a context in which copyin
+ * and copyout will work. Trap will then call addupc_task().
+ *
+ * Note that we may (rarely) not get around to the AST soon enough, and
+ * lose profile ticks when the next tick overwrites this one, but in this
+ * case the system is overloaded and the profile is probably already
+ * inaccurate.
+ */
+void
+addupc_intr(p, pc, ticks)
+ register struct proc *p;
+ register u_long pc;
+ u_int ticks;
+{
+ register struct uprof *prof;
+ register caddr_t addr;
+ register u_int i;
+ register int v;
+
+ if (ticks == 0)
+ return;
+ prof = &p->p_stats->p_prof;
+ if (pc < prof->pr_off ||
+ (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size)
+ return; /* out of range; ignore */
+
+ addr = prof->pr_base + i;
+ if ((v = fuswintr(addr)) == -1 || suswintr(addr, v + ticks) == -1) {
+ prof->pr_addr = pc;
+ prof->pr_ticks = ticks;
+ need_proftick(p);
+ }
+}
+
+/*
+ * Much like before, but we can afford to take faults here. If the
+ * update fails, we simply turn off profiling.
+ */
+void
+addupc_task(p, pc, ticks)
+ register struct proc *p;
+ register u_long pc;
+ u_int ticks;
+{
+ register struct uprof *prof;
+ register caddr_t addr;
+ register u_int i;
+ u_short v;
+
+ /* Testing P_PROFIL may be unnecessary, but is certainly safe. */
+ if ((p->p_flag & P_PROFIL) == 0 || ticks == 0)
+ return;
+
+ prof = &p->p_stats->p_prof;
+ if (pc < prof->pr_off ||
+ (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size)
+ return;
+
+ addr = prof->pr_base + i;
+ if (copyin(addr, (caddr_t)&v, sizeof(v)) == 0) {
+ v += ticks;
+ if (copyout((caddr_t)&v, addr, sizeof(v)) == 0)
+ return;
+ }
+ stopprofclock(p);
+}
diff --git a/sys/kern/subr_rlist.c b/sys/kern/subr_rlist.c
new file mode 100644
index 0000000..4dd156c
--- /dev/null
+++ b/sys/kern/subr_rlist.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 1992 William F. Jolitz, TeleMuse
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This software is a component of "386BSD" developed by
+ William F. Jolitz, TeleMuse.
+ * 4. Neither the name of the developer nor the name "386BSD"
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
+ * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
+ * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
+ * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
+ * NOT MAKE USE THIS WORK.
+ *
+ * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
+ * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN
+ * REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES
+ * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING
+ * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND
+ * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE
+ * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS
+ * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "cdefs.h"
+#include "malloc.h"
+#include "rlist.h"
+#include "vm/vm.h"
+#include "vm/vm_map.h"
+
+extern vm_map_t kernel_map;
+
+/*
+ * Resource lists.
+ */
+
+#define RLIST_MIN 128
+static int rlist_count=0;
+static struct rlist *rlfree;
+int rlist_active;
+
+static struct rlist *
+rlist_malloc()
+{
+ struct rlist *rl;
+ int i;
+ while( rlist_count < RLIST_MIN) {
+ extern vm_map_t kmem_map;
+ int s = splhigh();
+ rl = (struct rlist *)kmem_malloc(kmem_map, NBPG, 0);
+ splx(s);
+ if( !rl)
+ break;
+
+ for(i=0;i<(NBPG/(sizeof *rl));i++) {
+ rl->rl_next = rlfree;
+ rlfree = rl;
+ rlist_count++;
+ rl++;
+ }
+ }
+
+ if( (rl = rlfree) == 0 )
+ panic("Cannot get an rlist entry");
+
+ --rlist_count;
+ rlfree = rl->rl_next;
+ return rl;
+}
+
+inline static void
+rlist_mfree( struct rlist *rl)
+{
+ rl->rl_next = rlfree;
+ rlfree = rl;
+ ++rlist_count;
+}
+
+
+/*
+ * Add space to a resource list. Used to either
+ * initialize a list or return free space to it.
+ */
+void
+rlist_free (rlp, start, end)
+ register struct rlist **rlp;
+ unsigned start, end;
+{
+ struct rlist *head;
+ register struct rlist *olp = 0;
+ int s;
+
+ s = splhigh();
+ while( rlist_active)
+ tsleep((caddr_t)&rlist_active, PSWP, "rlistf", 0);
+ rlist_active = 1;
+ splx(s);
+
+ head = *rlp;
+
+loop:
+ /* if nothing here, insert (tail of list) */
+ if (*rlp == 0) {
+ *rlp = rlist_malloc();
+ (*rlp)->rl_start = start;
+ (*rlp)->rl_end = end;
+ (*rlp)->rl_next = 0;
+ rlist_active = 0;
+ wakeup((caddr_t)&rlist_active);
+ return;
+ }
+
+ /* if new region overlaps something currently present, panic */
+ if (start >= (*rlp)->rl_start && start <= (*rlp)->rl_end) {
+ printf("Frag %d:%d, ent %d:%d ", start, end,
+ (*rlp)->rl_start, (*rlp)->rl_end);
+ panic("overlapping front rlist_free: freed twice?");
+ }
+ if (end >= (*rlp)->rl_start && end <= (*rlp)->rl_end) {
+ printf("Frag %d:%d, ent %d:%d ", start, end,
+ (*rlp)->rl_start, (*rlp)->rl_end);
+ panic("overlapping tail rlist_free: freed twice?");
+ }
+
+ /* are we adjacent to this element? (in front) */
+ if (end+1 == (*rlp)->rl_start) {
+ /* coalesce */
+ (*rlp)->rl_start = start;
+ goto scan;
+ }
+
+ /* are we before this element? */
+ if (end < (*rlp)->rl_start) {
+ register struct rlist *nlp;
+
+ nlp = rlist_malloc();
+ nlp->rl_start = start;
+ nlp->rl_end = end;
+ nlp->rl_next = *rlp;
+ /*
+ * If the new element is in front of the list,
+ * adjust *rlp, else don't.
+ */
+ if( olp) {
+ olp->rl_next = nlp;
+ } else {
+ *rlp = nlp;
+ }
+ rlist_active = 0;
+ wakeup((caddr_t)&rlist_active);
+ return;
+ }
+
+ /* are we adjacent to this element? (at tail) */
+ if ((*rlp)->rl_end + 1 == start) {
+ /* coalesce */
+ (*rlp)->rl_end = end;
+ goto scan;
+ }
+
+ /* are we after this element */
+ if (start > (*rlp)->rl_end) {
+ olp = *rlp;
+ rlp = &((*rlp)->rl_next);
+ goto loop;
+ } else
+ panic("rlist_free: can't happen");
+
+scan:
+ /* can we coalesce list now that we've filled a void? */
+ {
+ register struct rlist *lp, *lpn;
+
+ for (lp = head; lp->rl_next ;) {
+ lpn = lp->rl_next;
+
+ /* coalesce ? */
+ if (lp->rl_end + 1 == lpn->rl_start) {
+ lp->rl_end = lpn->rl_end;
+ lp->rl_next = lpn->rl_next;
+ rlist_mfree(lpn);
+ } else
+ lp = lp->rl_next;
+ }
+ }
+ rlist_active = 0;
+ wakeup((caddr_t)&rlist_active);
+}
+
+/*
+ * Obtain a region of desired size from a resource list.
+ * If nothing available of that size, return 0. Otherwise,
+ * return a value of 1 and set resource start location with
+ * "*loc". (Note: loc can be zero if we don't wish the value)
+ */
+int rlist_alloc (rlp, size, loc)
+ struct rlist **rlp;
+ unsigned size, *loc;
+{
+ register struct rlist *lp;
+ int s;
+ register struct rlist *olp = 0;
+
+ s = splhigh();
+ while( rlist_active)
+ tsleep((caddr_t)&rlist_active, PSWP, "rlista", 0);
+ rlist_active = 1;
+ splx(s);
+
+ /* walk list, allocating first thing that's big enough (first fit) */
+ for (; *rlp; rlp = &((*rlp)->rl_next))
+ if(size <= (*rlp)->rl_end - (*rlp)->rl_start + 1) {
+
+ /* hand it to the caller */
+ if (loc) *loc = (*rlp)->rl_start;
+ (*rlp)->rl_start += size;
+
+ /* did we eat this element entirely? */
+ if ((*rlp)->rl_start > (*rlp)->rl_end) {
+ lp = (*rlp)->rl_next;
+ rlist_mfree(*rlp);
+ /*
+ * if the deleted element was in fromt
+ * of the list, adjust *rlp, else don't.
+ */
+ if (olp) {
+ olp->rl_next = lp;
+ } else {
+ *rlp = lp;
+ }
+ }
+
+ rlist_active = 0;
+ wakeup((caddr_t)&rlist_active);
+ return (1);
+ } else {
+ olp = *rlp;
+ }
+
+ rlist_active = 0;
+ wakeup((caddr_t)&rlist_active);
+ /* nothing in list that's big enough */
+ return (0);
+}
+
+/*
+ * Finished with this resource list, reclaim all space and
+ * mark it as being empty.
+ */
+void
+rlist_destroy (rlp)
+ struct rlist **rlp;
+{
+ struct rlist *lp, *nlp;
+
+ lp = *rlp;
+ *rlp = 0;
+ for (; lp; lp = nlp) {
+ nlp = lp->rl_next;
+ rlist_mfree(lp);
+ }
+}
diff --git a/sys/kern/subr_rmap.c b/sys/kern/subr_rmap.c
new file mode 100644
index 0000000..2f31173
--- /dev/null
+++ b/sys/kern/subr_rmap.c
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)subr_rmap.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/map.h>
+#include <sys/proc.h>
+
+void
+rminit(a1, a2, a3, a4, a5)
+ struct map *a1;
+ long a2, a3;
+ char *a4;
+ int a5;
+{
+
+ /*
+ * Body deleted.
+ */
+ return;
+}
+
+long
+rmalloc(a1, a2)
+ struct map *a1;
+ long a2;
+{
+
+ /*
+ * Body deleted.
+ */
+ return (0);
+}
+
+void
+rmfree(a1, a2, a3)
+ struct map *a1;
+ long a2, a3;
+{
+
+ /*
+ * Body deleted.
+ */
+ return;
+}
diff --git a/sys/kern/subr_trap.c b/sys/kern/subr_trap.c
new file mode 100644
index 0000000..7084791
--- /dev/null
+++ b/sys/kern/subr_trap.c
@@ -0,0 +1,692 @@
+/*-
+ * Copyright (C) 1994, David Greenman
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the University of Utah, and William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)trap.c 7.4 (Berkeley) 5/13/91
+ * $Id: trap.c,v 1.26 1994/06/11 05:13:33 davidg Exp $
+ */
+
+/*
+ * 386 Trap and System call handling
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/acct.h>
+#include <sys/kernel.h>
+#include <sys/syscall.h>
+#ifdef KTRACE
+#include <sys/ktrace.h>
+#endif
+
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+#include <vm/vm_map.h>
+#include <vm/vm_page.h>
+
+#include <machine/cpu.h>
+#include <machine/psl.h>
+#include <machine/reg.h>
+#include <machine/eflags.h>
+
+#include <machine/trap.h>
+
+#include "isa.h"
+#include "npx.h"
+#include "ddb.h"
+
+int trap_pfault __P((struct trapframe *, int));
+void trap_fatal __P((struct trapframe *));
+
+extern int grow(struct proc *,u_int);
+
+struct sysent sysent[];
+int nsysent;
+
+#define MAX_TRAP_MSG 27
+char *trap_msg[] = {
+ "reserved addressing fault", /* 0 T_RESADFLT */
+ "privileged instruction fault", /* 1 T_PRIVINFLT */
+ "reserved operand fault", /* 2 T_RESOPFLT */
+ "breakpoint instruction fault", /* 3 T_BPTFLT */
+ "", /* 4 unused */
+ "system call trap", /* 5 T_SYSCALL */
+ "arithmetic trap", /* 6 T_ARITHTRAP */
+ "system forced exception", /* 7 T_ASTFLT */
+ "segmentation (limit) fault", /* 8 T_SEGFLT */
+ "general protection fault", /* 9 T_PROTFLT */
+ "trace trap", /* 10 T_TRCTRAP */
+ "", /* 11 unused */
+ "page fault", /* 12 T_PAGEFLT */
+ "page table fault", /* 13 T_TABLEFLT */
+ "alignment fault", /* 14 T_ALIGNFLT */
+ "kernel stack pointer not valid", /* 15 T_KSPNOTVAL */
+ "bus error", /* 16 T_BUSERR */
+ "kernel debugger fault", /* 17 T_KDBTRAP */
+ "integer divide fault", /* 18 T_DIVIDE */
+ "non-maskable interrupt trap", /* 19 T_NMI */
+ "overflow trap", /* 20 T_OFLOW */
+ "FPU bounds check fault", /* 21 T_BOUND */
+ "FPU device not available", /* 22 T_DNA */
+ "double fault", /* 23 T_DOUBLEFLT */
+ "FPU operand fetch fault", /* 24 T_FPOPFLT */
+ "invalid TSS fault", /* 25 T_TSSFLT */
+ "segment not present fault", /* 26 T_SEGNPFLT */
+ "stack fault", /* 27 T_STKFLT */
+};
+
+static inline void
+userret(p, frame, oticks)
+ struct proc *p;
+ struct trapframe *frame;
+ u_quad_t oticks;
+{
+ int sig, s;
+
+ while (sig = CURSIG(p))
+ postsig(sig);
+ p->p_priority = p->p_usrpri;
+ if (want_resched) {
+ /*
+ * Since we are curproc, clock will normally just change
+ * our priority without moving us from one queue to another
+ * (since the running process is not on a queue.)
+ * If that happened after we setrunqueue ourselves but before we
+ * mi_switch()'ed, we might not be on the queue indicated by
+ * our priority.
+ */
+ s = splclock();
+ setrunqueue(p);
+ p->p_stats->p_ru.ru_nivcsw++;
+ mi_switch();
+ splx(s);
+ while (sig = CURSIG(p))
+ postsig(sig);
+ }
+ if (p->p_stats->p_prof.pr_scale) {
+ u_quad_t ticks = p->p_sticks - oticks;
+
+ if (ticks) {
+#ifdef PROFTIMER
+ extern int profscale;
+ addupc(frame->tf_eip, &p->p_stats->p_prof,
+ ticks * profscale);
+#else
+ addupc(frame->tf_eip, &p->p_stats->p_prof, ticks);
+#endif
+ }
+ }
+ curpriority = p->p_priority;
+}
+
+/*
+ * trap(frame):
+ * Exception, fault, and trap interface to the FreeBSD kernel.
+ * This common code is called from assembly language IDT gate entry
+ * routines that prepare a suitable stack frame, and restore this
+ * frame after the exception has been processed.
+ */
+
+/*ARGSUSED*/
+void
+trap(frame)
+ struct trapframe frame;
+{
+ struct proc *p = curproc;
+ u_quad_t sticks = 0;
+ int i = 0, ucode = 0, type, code, eva, fault_type;
+
+ frame.tf_eflags &= ~PSL_NT; /* clear nested trap XXX */
+ type = frame.tf_trapno;
+ code = frame.tf_err;
+
+ if (ISPL(frame.tf_cs) == SEL_UPL) {
+ /* user trap */
+
+ sticks = p->p_sticks;
+ p->p_md.md_regs = (int *)&frame;
+
+ switch (type) {
+ case T_RESADFLT: /* reserved addressing fault */
+ case T_PRIVINFLT: /* privileged instruction fault */
+ case T_RESOPFLT: /* reserved operand fault */
+ ucode = type;
+ i = SIGILL;
+ break;
+
+ case T_BPTFLT: /* bpt instruction fault */
+ case T_TRCTRAP: /* trace trap */
+ frame.tf_eflags &= ~PSL_T;
+ i = SIGTRAP;
+ break;
+
+ case T_ARITHTRAP: /* arithmetic trap */
+ ucode = code;
+ i = SIGFPE;
+ break;
+
+ case T_ASTFLT: /* Allow process switch */
+ astoff();
+ cnt.v_soft++;
+ if ((p->p_flag & P_OWEUPC) && p->p_stats->p_prof.pr_scale) {
+ addupc(frame.tf_eip, &p->p_stats->p_prof, 1);
+ p->p_flag &= ~P_OWEUPC;
+ }
+ goto out;
+
+ case T_PROTFLT: /* general protection fault */
+ case T_SEGNPFLT: /* segment not present fault */
+ case T_STKFLT: /* stack fault */
+ ucode = code + BUS_SEGM_FAULT ;
+ i = SIGBUS;
+ break;
+
+ case T_PAGEFLT: /* page fault */
+ i = trap_pfault(&frame, TRUE);
+ if (i == 0)
+ goto out;
+
+ ucode = T_PAGEFLT;
+ break;
+
+ case T_DIVIDE: /* integer divide fault */
+ ucode = FPE_INTDIV_TRAP;
+ i = SIGFPE;
+ break;
+
+#if NISA > 0
+ case T_NMI:
+#if NDDB > 0
+ /* NMI can be hooked up to a pushbutton for debugging */
+ printf ("NMI ... going to debugger\n");
+ if (kdb_trap (type, 0, &frame))
+ return;
+#endif
+ /* machine/parity/power fail/"kitchen sink" faults */
+ if (isa_nmi(code) == 0) return;
+ /* FALL THROUGH */
+#endif
+
+ case T_OFLOW: /* integer overflow fault */
+ ucode = FPE_INTOVF_TRAP;
+ i = SIGFPE;
+ break;
+
+ case T_BOUND: /* bounds check fault */
+ ucode = FPE_SUBRNG_TRAP;
+ i = SIGFPE;
+ break;
+
+ case T_DNA:
+#if NNPX > 0
+ /* if a transparent fault (due to context switch "late") */
+ if (npxdna())
+ return;
+#endif /* NNPX > 0 */
+
+#if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE)
+ i = math_emulate(&frame);
+ if (i == 0) return;
+#else /* MATH_EMULATE || GPL_MATH_EMULATE */
+ panic("trap: math emulation necessary!");
+#endif /* MATH_EMULATE || GPL_MATH_EMULATE */
+ ucode = FPE_FPU_NP_TRAP;
+ break;
+
+ case T_FPOPFLT: /* FPU operand fetch fault */
+ ucode = T_FPOPFLT;
+ i = SIGILL;
+ break;
+
+ default:
+ trap_fatal(&frame);
+ }
+ } else {
+ /* kernel trap */
+
+ switch (type) {
+ case T_PAGEFLT: /* page fault */
+ (void) trap_pfault(&frame, FALSE);
+ return;
+
+ case T_PROTFLT: /* general protection fault */
+ case T_SEGNPFLT: /* segment not present fault */
+ if (curpcb && curpcb->pcb_onfault) {
+ frame.tf_eip = (int)curpcb->pcb_onfault;
+ return;
+ }
+ break;
+
+#if NDDB > 0
+ case T_BPTFLT:
+ case T_TRCTRAP:
+ if (kdb_trap (type, 0, &frame))
+ return;
+ break;
+#else
+ case T_TRCTRAP: /* trace trap -- someone single stepping lcall's */
+ /* Q: how do we turn it on again? */
+ frame.tf_eflags &= ~PSL_T;
+ return;
+#endif
+
+#if NISA > 0
+ case T_NMI:
+#if NDDB > 0
+ /* NMI can be hooked up to a pushbutton for debugging */
+ printf ("NMI ... going to debugger\n");
+ if (kdb_trap (type, 0, &frame))
+ return;
+#endif
+ /* machine/parity/power fail/"kitchen sink" faults */
+ if (isa_nmi(code) == 0) return;
+ /* FALL THROUGH */
+#endif
+ }
+
+ trap_fatal(&frame);
+ }
+
+ trapsignal(p, i, ucode);
+
+#ifdef DIAGNOSTIC
+ eva = rcr2();
+ if (type <= MAX_TRAP_MSG) {
+ uprintf("fatal process exception: %s",
+ trap_msg[type]);
+ if ((type == T_PAGEFLT) || (type == T_PROTFLT))
+ uprintf(", fault VA = 0x%x", eva);
+ uprintf("\n");
+ }
+#endif
+
+out:
+ userret(p, &frame, sticks);
+}
+
+int
+trap_pfault(frame, usermode)
+ struct trapframe *frame;
+ int usermode;
+{
+ vm_offset_t va;
+ struct vmspace *vm;
+ vm_map_t map = 0;
+ int rv = 0, oldflags;
+ vm_prot_t ftype;
+ extern vm_map_t kernel_map;
+ int eva;
+ struct proc *p = curproc;
+
+ eva = rcr2();
+ va = trunc_page((vm_offset_t)eva);
+
+ /*
+ * Don't allow user-mode faults in kernel address space
+ */
+ if (usermode && (va >= KERNBASE)) {
+ goto nogo;
+ }
+
+ if ((p == 0) || (va >= KERNBASE)) {
+ vm = 0;
+ map = kernel_map;
+ } else {
+ vm = p->p_vmspace;
+ map = &vm->vm_map;
+ }
+
+ if (frame->tf_err & PGEX_W)
+ ftype = VM_PROT_READ | VM_PROT_WRITE;
+ else
+ ftype = VM_PROT_READ;
+
+ if (map != kernel_map) {
+ vm_offset_t pa;
+ vm_offset_t v = (vm_offset_t) vtopte(va);
+ vm_page_t ptepg;
+
+ /*
+ * Keep swapout from messing with us during this
+ * critical time.
+ */
+ ++p->p_lock;
+
+ /*
+ * Grow the stack if necessary
+ */
+ if ((caddr_t)va > vm->vm_maxsaddr
+ && (caddr_t)va < (caddr_t)USRSTACK) {
+ if (!grow(p, va)) {
+ rv = KERN_FAILURE;
+ --p->p_lock;
+ goto nogo;
+ }
+ }
+
+ /*
+ * Check if page table is mapped, if not,
+ * fault it first
+ */
+
+ /* Fault the pte only if needed: */
+ *(volatile char *)v += 0;
+
+ ptepg = (vm_page_t) pmap_pte_vm_page(vm_map_pmap(map), v);
+ if( ptepg->hold_count == 0)
+ ptepg->act_count += 3;
+ vm_page_hold(ptepg);
+
+ /* Fault in the user page: */
+ rv = vm_fault(map, va, ftype, FALSE);
+
+ vm_page_unhold(ptepg);
+
+ /*
+ * page table pages don't need to be kept if they
+ * are not held
+ */
+ if( ptepg->hold_count == 0 && ptepg->wire_count == 0) {
+ pmap_page_protect( VM_PAGE_TO_PHYS(ptepg),
+ VM_PROT_NONE);
+ vm_page_free(ptepg);
+ }
+
+ --p->p_lock;
+ } else {
+ /*
+ * Since we know that kernel virtual address addresses
+ * always have pte pages mapped, we just have to fault
+ * the page.
+ */
+ rv = vm_fault(map, va, ftype, FALSE);
+ }
+
+ if (rv == KERN_SUCCESS)
+ return (0);
+nogo:
+ if (!usermode) {
+ if (curpcb->pcb_onfault) {
+ frame->tf_eip = (int)curpcb->pcb_onfault;
+ return (0);
+ }
+ trap_fatal(frame);
+ }
+
+ /* kludge to pass faulting virtual address to sendsig */
+ frame->tf_err = eva;
+
+ return((rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV);
+}
+
+void
+trap_fatal(frame)
+ struct trapframe *frame;
+{
+ int code, type, eva;
+
+ code = frame->tf_err;
+ type = frame->tf_trapno;
+ eva = rcr2();
+
+ if (type <= MAX_TRAP_MSG)
+ printf("\n\nFatal trap %d: %s while in %s mode\n",
+ type, trap_msg[type],
+ ISPL(frame->tf_cs) == SEL_UPL ? "user" : "kernel");
+ if (type == T_PAGEFLT) {
+ printf("fault virtual address = 0x%x\n", eva);
+ printf("fault code = %s %s, %s\n",
+ code & PGEX_U ? "user" : "supervisor",
+ code & PGEX_W ? "write" : "read",
+ code & PGEX_P ? "protection violation" : "page not present");
+ }
+ printf("instruction pointer = 0x%x\n", frame->tf_eip);
+ printf("processor eflags = ");
+ if (frame->tf_eflags & EFL_TF)
+ printf("trace/trap, ");
+ if (frame->tf_eflags & EFL_IF)
+ printf("interrupt enabled, ");
+ if (frame->tf_eflags & EFL_NT)
+ printf("nested task, ");
+ if (frame->tf_eflags & EFL_RF)
+ printf("resume, ");
+ if (frame->tf_eflags & EFL_VM)
+ printf("vm86, ");
+ printf("IOPL = %d\n", (frame->tf_eflags & EFL_IOPL) >> 12);
+ printf("current process = ");
+ if (curproc) {
+ printf("%d (%s)\n",
+ curproc->p_pid, curproc->p_comm ?
+ curproc->p_comm : "");
+ } else {
+ printf("Idle\n");
+ }
+ printf("interrupt mask = ");
+ if ((cpl & net_imask) == net_imask)
+ printf("net ");
+ if ((cpl & tty_imask) == tty_imask)
+ printf("tty ");
+ if ((cpl & bio_imask) == bio_imask)
+ printf("bio ");
+ if (cpl == 0)
+ printf("none");
+ printf("\n");
+
+#ifdef KDB
+ if (kdb_trap(&psl))
+ return;
+#endif
+#if NDDB > 0
+ if (kdb_trap (type, 0, frame))
+ return;
+#endif
+ if (type <= MAX_TRAP_MSG)
+ panic(trap_msg[type]);
+ else
+ panic("unknown/reserved trap");
+}
+
+/*
+ * Compensate for 386 brain damage (missing URKR).
+ * This is a little simpler than the pagefault handler in trap() because
+ * it the page tables have already been faulted in and high addresses
+ * are thrown out early for other reasons.
+ */
+int trapwrite(addr)
+ unsigned addr;
+{
+ struct proc *p;
+ vm_offset_t va, v;
+ struct vmspace *vm;
+ int oldflags;
+ int rv;
+
+ va = trunc_page((vm_offset_t)addr);
+ /*
+ * XXX - MAX is END. Changed > to >= for temp. fix.
+ */
+ if (va >= VM_MAXUSER_ADDRESS)
+ return (1);
+
+ p = curproc;
+ vm = p->p_vmspace;
+
+ ++p->p_lock;
+
+ if ((caddr_t)va >= vm->vm_maxsaddr
+ && (caddr_t)va < (caddr_t)USRSTACK) {
+ if (!grow(p, va)) {
+ --p->p_lock;
+ return (1);
+ }
+ }
+
+ v = trunc_page(vtopte(va));
+
+ /*
+ * wire the pte page
+ */
+ if (va < USRSTACK) {
+ vm_map_pageable(&vm->vm_map, v, round_page(v+1), FALSE);
+ }
+
+ /*
+ * fault the data page
+ */
+ rv = vm_fault(&vm->vm_map, va, VM_PROT_READ|VM_PROT_WRITE, FALSE);
+
+ /*
+ * unwire the pte page
+ */
+ if (va < USRSTACK) {
+ vm_map_pageable(&vm->vm_map, v, round_page(v+1), TRUE);
+ }
+
+ --p->p_lock;
+
+ if (rv != KERN_SUCCESS)
+ return 1;
+
+ return (0);
+}
+
+/*
+ * syscall(frame):
+ * System call request from POSIX system call gate interface to kernel.
+ * Like trap(), argument is call by reference.
+ */
+/*ARGSUSED*/
+void
+syscall(frame)
+ struct trapframe frame;
+{
+ caddr_t params;
+ int i;
+ struct sysent *callp;
+ struct proc *p = curproc;
+ u_quad_t sticks;
+ int error, opc;
+ int args[8], rval[2];
+ u_int code;
+
+ sticks = p->p_sticks;
+ if (ISPL(frame.tf_cs) != SEL_UPL)
+ panic("syscall");
+
+ code = frame.tf_eax;
+ p->p_md.md_regs = (int *)&frame;
+ params = (caddr_t)frame.tf_esp + sizeof (int) ;
+
+ /*
+ * Reconstruct pc, assuming lcall $X,y is 7 bytes, as it is always.
+ */
+ opc = frame.tf_eip - 7;
+ /*
+ * Need to check if this is a 32 bit or 64 bit syscall.
+ */
+ if (code == SYS_syscall) {
+ /*
+ * Code is first argument, followed by actual args.
+ */
+ code = fuword(params);
+ params += sizeof (int);
+ } else if (code == SYS___syscall) {
+ /*
+ * Like syscall, but code is a quad, so as to maintain
+ * quad alignment for the rest of the arguments.
+ */
+ code = fuword(params + _QUAD_LOWWORD * sizeof(int));
+ params += sizeof(quad_t);
+ }
+
+ if (code >= nsysent)
+ callp = &sysent[0];
+ else
+ callp = &sysent[code];
+
+ if ((i = callp->sy_narg * sizeof (int)) &&
+ (error = copyin(params, (caddr_t)args, (u_int)i))) {
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_SYSCALL))
+ ktrsyscall(p->p_tracep, code, callp->sy_narg, args);
+#endif
+ goto bad;
+ }
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_SYSCALL))
+ ktrsyscall(p->p_tracep, code, callp->sy_narg, args);
+#endif
+ rval[0] = 0;
+ rval[1] = frame.tf_edx;
+
+ error = (*callp->sy_call)(p, args, rval);
+
+ switch (error) {
+
+ case 0:
+ /*
+ * Reinitialize proc pointer `p' as it may be different
+ * if this is a child returning from fork syscall.
+ */
+ p = curproc;
+ frame.tf_eax = rval[0];
+ frame.tf_edx = rval[1];
+ frame.tf_eflags &= ~PSL_C; /* carry bit */
+ break;
+
+ case ERESTART:
+ frame.tf_eip = opc;
+ break;
+
+ case EJUSTRETURN:
+ break;
+
+ default:
+ bad:
+ frame.tf_eax = error;
+ frame.tf_eflags |= PSL_C; /* carry bit */
+ break;
+ }
+
+ userret(p, &frame, sticks);
+
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_SYSRET))
+ ktrsysret(p->p_tracep, code, error, rval[0]);
+#endif
+}
diff --git a/sys/kern/subr_xxx.c b/sys/kern/subr_xxx.c
new file mode 100644
index 0000000..3304d57
--- /dev/null
+++ b/sys/kern/subr_xxx.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)subr_xxx.c 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Miscellaneous trivial functions, including many
+ * that are often inline-expanded or done in assembler.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <machine/cpu.h>
+
+/*
+ * Unsupported device function (e.g. writing to read-only device).
+ */
+int
+enodev()
+{
+
+ return (ENODEV);
+}
+
+/*
+ * Unconfigured device function; driver not configured.
+ */
+int
+enxio()
+{
+
+ return (ENXIO);
+}
+
+/*
+ * Unsupported ioctl function.
+ */
+int
+enoioctl()
+{
+
+ return (ENOTTY);
+}
+
+/*
+ * Unsupported system function.
+ * This is used for an otherwise-reasonable operation
+ * that is not supported by the current system binary.
+ */
+int
+enosys()
+{
+
+ return (ENOSYS);
+}
+
+/*
+ * Return error for operation not supported
+ * on a specific object or file type.
+ */
+int
+eopnotsupp()
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * Generic null operation, always returns success.
+ */
+int
+nullop()
+{
+
+ return (0);
+}
diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c
new file mode 100644
index 0000000..919c866
--- /dev/null
+++ b/sys/kern/sys_generic.c
@@ -0,0 +1,684 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)sys_generic.c 8.5 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/filedesc.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/proc.h>
+#include <sys/socketvar.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+#include <sys/stat.h>
+#include <sys/malloc.h>
+#ifdef KTRACE
+#include <sys/ktrace.h>
+#endif
+
+/*
+ * Read system call.
+ */
+struct read_args {
+ int fd;
+ char *buf;
+ u_int nbyte;
+};
+/* ARGSUSED */
+int
+read(p, uap, retval)
+ struct proc *p;
+ register struct read_args *uap;
+ int *retval;
+{
+ register struct file *fp;
+ register struct filedesc *fdp = p->p_fd;
+ struct uio auio;
+ struct iovec aiov;
+ long cnt, error = 0;
+#ifdef KTRACE
+ struct iovec ktriov;
+#endif
+
+ if (((u_int)uap->fd) >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL ||
+ (fp->f_flag & FREAD) == 0)
+ return (EBADF);
+ aiov.iov_base = (caddr_t)uap->buf;
+ aiov.iov_len = uap->nbyte;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = uap->nbyte;
+ auio.uio_rw = UIO_READ;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_procp = p;
+#ifdef KTRACE
+ /*
+ * if tracing, save a copy of iovec
+ */
+ if (KTRPOINT(p, KTR_GENIO))
+ ktriov = aiov;
+#endif
+ cnt = uap->nbyte;
+ if (error = (*fp->f_ops->fo_read)(fp, &auio, fp->f_cred))
+ if (auio.uio_resid != cnt && (error == ERESTART ||
+ error == EINTR || error == EWOULDBLOCK))
+ error = 0;
+ cnt -= auio.uio_resid;
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_GENIO) && error == 0)
+ ktrgenio(p->p_tracep, uap->fd, UIO_READ, &ktriov, cnt, error);
+#endif
+ *retval = cnt;
+ return (error);
+}
+
+/*
+ * Scatter read system call.
+ */
+struct readv_args {
+ int fdes;
+ struct iovec *iovp;
+ u_int iovcnt;
+};
+int
+readv(p, uap, retval)
+ struct proc *p;
+ register struct readv_args *uap;
+ int *retval;
+{
+ register struct file *fp;
+ register struct filedesc *fdp = p->p_fd;
+ struct uio auio;
+ register struct iovec *iov;
+ struct iovec *needfree;
+ struct iovec aiov[UIO_SMALLIOV];
+ long i, cnt, error = 0;
+ u_int iovlen;
+#ifdef KTRACE
+ struct iovec *ktriov = NULL;
+#endif
+
+ if (((u_int)uap->fdes) >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fdes]) == NULL ||
+ (fp->f_flag & FREAD) == 0)
+ return (EBADF);
+ /* note: can't use iovlen until iovcnt is validated */
+ iovlen = uap->iovcnt * sizeof (struct iovec);
+ if (uap->iovcnt > UIO_SMALLIOV) {
+ if (uap->iovcnt > UIO_MAXIOV)
+ return (EINVAL);
+ MALLOC(iov, struct iovec *, iovlen, M_IOV, M_WAITOK);
+ needfree = iov;
+ } else {
+ iov = aiov;
+ needfree = NULL;
+ }
+ auio.uio_iov = iov;
+ auio.uio_iovcnt = uap->iovcnt;
+ auio.uio_rw = UIO_READ;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_procp = p;
+ if (error = copyin((caddr_t)uap->iovp, (caddr_t)iov, iovlen))
+ goto done;
+ auio.uio_resid = 0;
+ for (i = 0; i < uap->iovcnt; i++) {
+ auio.uio_resid += iov->iov_len;
+ if (auio.uio_resid < 0) {
+ error = EINVAL;
+ goto done;
+ }
+ iov++;
+ }
+#ifdef KTRACE
+ /*
+ * if tracing, save a copy of iovec
+ */
+ if (KTRPOINT(p, KTR_GENIO)) {
+ MALLOC(ktriov, struct iovec *, iovlen, M_TEMP, M_WAITOK);
+ bcopy((caddr_t)auio.uio_iov, (caddr_t)ktriov, iovlen);
+ }
+#endif
+ cnt = auio.uio_resid;
+ if (error = (*fp->f_ops->fo_read)(fp, &auio, fp->f_cred))
+ if (auio.uio_resid != cnt && (error == ERESTART ||
+ error == EINTR || error == EWOULDBLOCK))
+ error = 0;
+ cnt -= auio.uio_resid;
+#ifdef KTRACE
+ if (ktriov != NULL) {
+ if (error == 0)
+ ktrgenio(p->p_tracep, uap->fdes, UIO_READ, ktriov,
+ cnt, error);
+ FREE(ktriov, M_TEMP);
+ }
+#endif
+ *retval = cnt;
+done:
+ if (needfree)
+ FREE(needfree, M_IOV);
+ return (error);
+}
+
+/*
+ * Write system call
+ */
+struct write_args {
+ int fd;
+ char *buf;
+ u_int nbyte;
+};
+int
+write(p, uap, retval)
+ struct proc *p;
+ register struct write_args *uap;
+ int *retval;
+{
+ register struct file *fp;
+ register struct filedesc *fdp = p->p_fd;
+ struct uio auio;
+ struct iovec aiov;
+ long cnt, error = 0;
+ int i;
+#ifdef KTRACE
+ struct iovec ktriov;
+#endif
+
+ if (((u_int)uap->fd) >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL ||
+ (fp->f_flag & FWRITE) == 0)
+ return (EBADF);
+ aiov.iov_base = (caddr_t)uap->buf;
+ aiov.iov_len = uap->nbyte;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = uap->nbyte;
+ auio.uio_rw = UIO_WRITE;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_procp = p;
+#ifdef KTRACE
+ /*
+ * if tracing, save a copy of iovec
+ */
+ if (KTRPOINT(p, KTR_GENIO))
+ ktriov = aiov;
+#endif
+ cnt = uap->nbyte;
+ if (error = (*fp->f_ops->fo_write)(fp, &auio, fp->f_cred)) {
+ if (auio.uio_resid != cnt && (error == ERESTART ||
+ error == EINTR || error == EWOULDBLOCK))
+ error = 0;
+ if (error == EPIPE)
+ psignal(p, SIGPIPE);
+ }
+ cnt -= auio.uio_resid;
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_GENIO) && error == 0)
+ ktrgenio(p->p_tracep, uap->fd, UIO_WRITE,
+ &ktriov, cnt, error);
+#endif
+ *retval = cnt;
+ return (error);
+}
+
+/*
+ * Gather write system call
+ */
+struct writev_args {
+ int fd;
+ struct iovec *iovp;
+ u_int iovcnt;
+};
+int
+writev(p, uap, retval)
+ struct proc *p;
+ register struct writev_args *uap;
+ int *retval;
+{
+ register struct file *fp;
+ register struct filedesc *fdp = p->p_fd;
+ struct uio auio;
+ register struct iovec *iov;
+ struct iovec *needfree;
+ struct iovec aiov[UIO_SMALLIOV];
+ long i, cnt, error = 0;
+ u_int iovlen;
+#ifdef KTRACE
+ struct iovec *ktriov = NULL;
+#endif
+
+ if (((u_int)uap->fd) >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL ||
+ (fp->f_flag & FWRITE) == 0)
+ return (EBADF);
+ /* note: can't use iovlen until iovcnt is validated */
+ iovlen = uap->iovcnt * sizeof (struct iovec);
+ if (uap->iovcnt > UIO_SMALLIOV) {
+ if (uap->iovcnt > UIO_MAXIOV)
+ return (EINVAL);
+ MALLOC(iov, struct iovec *, iovlen, M_IOV, M_WAITOK);
+ needfree = iov;
+ } else {
+ iov = aiov;
+ needfree = NULL;
+ }
+ auio.uio_iov = iov;
+ auio.uio_iovcnt = uap->iovcnt;
+ auio.uio_rw = UIO_WRITE;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_procp = p;
+ if (error = copyin((caddr_t)uap->iovp, (caddr_t)iov, iovlen))
+ goto done;
+ auio.uio_resid = 0;
+ for (i = 0; i < uap->iovcnt; i++) {
+ auio.uio_resid += iov->iov_len;
+ if (auio.uio_resid < 0) {
+ error = EINVAL;
+ goto done;
+ }
+ iov++;
+ }
+#ifdef KTRACE
+ /*
+ * if tracing, save a copy of iovec
+ */
+ if (KTRPOINT(p, KTR_GENIO)) {
+ MALLOC(ktriov, struct iovec *, iovlen, M_TEMP, M_WAITOK);
+ bcopy((caddr_t)auio.uio_iov, (caddr_t)ktriov, iovlen);
+ }
+#endif
+ cnt = auio.uio_resid;
+ if (error = (*fp->f_ops->fo_write)(fp, &auio, fp->f_cred)) {
+ if (auio.uio_resid != cnt && (error == ERESTART ||
+ error == EINTR || error == EWOULDBLOCK))
+ error = 0;
+ if (error == EPIPE)
+ psignal(p, SIGPIPE);
+ }
+ cnt -= auio.uio_resid;
+#ifdef KTRACE
+ if (ktriov != NULL) {
+ if (error == 0)
+ ktrgenio(p->p_tracep, uap->fd, UIO_WRITE,
+ ktriov, cnt, error);
+ FREE(ktriov, M_TEMP);
+ }
+#endif
+ *retval = cnt;
+done:
+ if (needfree)
+ FREE(needfree, M_IOV);
+ return (error);
+}
+
+/*
+ * Ioctl system call
+ */
+struct ioctl_args {
+ int fd;
+ int com;
+ caddr_t data;
+};
+/* ARGSUSED */
+int
+ioctl(p, uap, retval)
+ struct proc *p;
+ register struct ioctl_args *uap;
+ int *retval;
+{
+ register struct file *fp;
+ register struct filedesc *fdp;
+ register int com, error;
+ register u_int size;
+ caddr_t data, memp;
+ int tmp;
+#define STK_PARAMS 128
+ char stkbuf[STK_PARAMS];
+
+ fdp = p->p_fd;
+ if ((u_int)uap->fd >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL)
+ return (EBADF);
+
+ if ((fp->f_flag & (FREAD | FWRITE)) == 0)
+ return (EBADF);
+
+ switch (com = uap->com) {
+ case FIONCLEX:
+ fdp->fd_ofileflags[uap->fd] &= ~UF_EXCLOSE;
+ return (0);
+ case FIOCLEX:
+ fdp->fd_ofileflags[uap->fd] |= UF_EXCLOSE;
+ return (0);
+ }
+
+ /*
+ * Interpret high order word to find amount of data to be
+ * copied to/from the user's address space.
+ */
+ size = IOCPARM_LEN(com);
+ if (size > IOCPARM_MAX)
+ return (ENOTTY);
+ memp = NULL;
+ if (size > sizeof (stkbuf)) {
+ memp = (caddr_t)malloc((u_long)size, M_IOCTLOPS, M_WAITOK);
+ data = memp;
+ } else
+ data = stkbuf;
+ if (com&IOC_IN) {
+ if (size) {
+ error = copyin(uap->data, data, (u_int)size);
+ if (error) {
+ if (memp)
+ free(memp, M_IOCTLOPS);
+ return (error);
+ }
+ } else
+ *(caddr_t *)data = uap->data;
+ } else if ((com&IOC_OUT) && size)
+ /*
+ * Zero the buffer so the user always
+ * gets back something deterministic.
+ */
+ bzero(data, size);
+ else if (com&IOC_VOID)
+ *(caddr_t *)data = uap->data;
+
+ switch (com) {
+
+ case FIONBIO:
+ if (tmp = *(int *)data)
+ fp->f_flag |= FNONBLOCK;
+ else
+ fp->f_flag &= ~FNONBLOCK;
+ error = (*fp->f_ops->fo_ioctl)(fp, FIONBIO, (caddr_t)&tmp, p);
+ break;
+
+ case FIOASYNC:
+ if (tmp = *(int *)data)
+ fp->f_flag |= FASYNC;
+ else
+ fp->f_flag &= ~FASYNC;
+ error = (*fp->f_ops->fo_ioctl)(fp, FIOASYNC, (caddr_t)&tmp, p);
+ break;
+
+ case FIOSETOWN:
+ tmp = *(int *)data;
+ if (fp->f_type == DTYPE_SOCKET) {
+ ((struct socket *)fp->f_data)->so_pgid = tmp;
+ error = 0;
+ break;
+ }
+ if (tmp <= 0) {
+ tmp = -tmp;
+ } else {
+ struct proc *p1 = pfind(tmp);
+ if (p1 == 0) {
+ error = ESRCH;
+ break;
+ }
+ tmp = p1->p_pgrp->pg_id;
+ }
+ error = (*fp->f_ops->fo_ioctl)
+ (fp, (int)TIOCSPGRP, (caddr_t)&tmp, p);
+ break;
+
+ case FIOGETOWN:
+ if (fp->f_type == DTYPE_SOCKET) {
+ error = 0;
+ *(int *)data = ((struct socket *)fp->f_data)->so_pgid;
+ break;
+ }
+ error = (*fp->f_ops->fo_ioctl)(fp, (int)TIOCGPGRP, data, p);
+ *(int *)data = -*(int *)data;
+ break;
+
+ default:
+ error = (*fp->f_ops->fo_ioctl)(fp, com, data, p);
+ /*
+ * Copy any data to user, size was
+ * already set and checked above.
+ */
+ if (error == 0 && (com&IOC_OUT) && size)
+ error = copyout(data, uap->data, (u_int)size);
+ break;
+ }
+ if (memp)
+ free(memp, M_IOCTLOPS);
+ return (error);
+}
+
+int selwait, nselcoll;
+
+/*
+ * Select system call.
+ */
+struct select_args {
+ u_int nd;
+ fd_set *in, *ou, *ex;
+ struct timeval *tv;
+};
+int
+select(p, uap, retval)
+ register struct proc *p;
+ register struct select_args *uap;
+ int *retval;
+{
+ fd_set ibits[3], obits[3];
+ struct timeval atv;
+ int s, ncoll, error = 0, timo;
+ u_int ni;
+
+ bzero((caddr_t)ibits, sizeof(ibits));
+ bzero((caddr_t)obits, sizeof(obits));
+ if (uap->nd > FD_SETSIZE)
+ return (EINVAL);
+ if (uap->nd > p->p_fd->fd_nfiles)
+ uap->nd = p->p_fd->fd_nfiles; /* forgiving; slightly wrong */
+ ni = howmany(uap->nd, NFDBITS) * sizeof(fd_mask);
+
+#define getbits(name, x) \
+ if (uap->name && \
+ (error = copyin((caddr_t)uap->name, (caddr_t)&ibits[x], ni))) \
+ goto done;
+ getbits(in, 0);
+ getbits(ou, 1);
+ getbits(ex, 2);
+#undef getbits
+
+ if (uap->tv) {
+ error = copyin((caddr_t)uap->tv, (caddr_t)&atv,
+ sizeof (atv));
+ if (error)
+ goto done;
+ if (itimerfix(&atv)) {
+ error = EINVAL;
+ goto done;
+ }
+ s = splclock();
+ timevaladd(&atv, (struct timeval *)&time);
+ timo = hzto(&atv);
+ /*
+ * Avoid inadvertently sleeping forever.
+ */
+ if (timo == 0)
+ timo = 1;
+ splx(s);
+ } else
+ timo = 0;
+retry:
+ ncoll = nselcoll;
+ p->p_flag |= P_SELECT;
+ error = selscan(p, ibits, obits, uap->nd, retval);
+ if (error || *retval)
+ goto done;
+ s = splhigh();
+ /* this should be timercmp(&time, &atv, >=) */
+ if (uap->tv && (time.tv_sec > atv.tv_sec ||
+ time.tv_sec == atv.tv_sec && time.tv_usec >= atv.tv_usec)) {
+ splx(s);
+ goto done;
+ }
+ if ((p->p_flag & P_SELECT) == 0 || nselcoll != ncoll) {
+ splx(s);
+ goto retry;
+ }
+ p->p_flag &= ~P_SELECT;
+ error = tsleep((caddr_t)&selwait, PSOCK | PCATCH, "select", timo);
+ splx(s);
+ if (error == 0)
+ goto retry;
+done:
+ p->p_flag &= ~P_SELECT;
+ /* select is not restarted after signals... */
+ if (error == ERESTART)
+ error = EINTR;
+ if (error == EWOULDBLOCK)
+ error = 0;
+#define putbits(name, x) \
+ if (uap->name && \
+ (error2 = copyout((caddr_t)&obits[x], (caddr_t)uap->name, ni))) \
+ error = error2;
+ if (error == 0) {
+ int error2;
+
+ putbits(in, 0);
+ putbits(ou, 1);
+ putbits(ex, 2);
+#undef putbits
+ }
+ return (error);
+}
+
+int
+selscan(p, ibits, obits, nfd, retval)
+ struct proc *p;
+ fd_set *ibits, *obits;
+ int nfd, *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ register int msk, i, j, fd;
+ register fd_mask bits;
+ struct file *fp;
+ int n = 0;
+ static int flag[3] = { FREAD, FWRITE, 0 };
+
+ for (msk = 0; msk < 3; msk++) {
+ for (i = 0; i < nfd; i += NFDBITS) {
+ bits = ibits[msk].fds_bits[i/NFDBITS];
+ while ((j = ffs(bits)) && (fd = i + --j) < nfd) {
+ bits &= ~(1 << j);
+ fp = fdp->fd_ofiles[fd];
+ if (fp == NULL)
+ return (EBADF);
+ if ((*fp->f_ops->fo_select)(fp, flag[msk], p)) {
+ FD_SET(fd, &obits[msk]);
+ n++;
+ }
+ }
+ }
+ }
+ *retval = n;
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+seltrue(dev, flag, p)
+ dev_t dev;
+ int flag;
+ struct proc *p;
+{
+
+ return (1);
+}
+
+/*
+ * Record a select request.
+ */
+void
+selrecord(selector, sip)
+ struct proc *selector;
+ struct selinfo *sip;
+{
+ struct proc *p;
+ pid_t mypid;
+
+ mypid = selector->p_pid;
+ if (sip->si_pid == mypid)
+ return;
+ if (sip->si_pid && (p = pfind(sip->si_pid)) &&
+ p->p_wchan == (caddr_t)&selwait)
+ sip->si_flags |= SI_COLL;
+ else
+ sip->si_pid = mypid;
+}
+
+/*
+ * Do a wakeup when a selectable event occurs.
+ */
+void
+selwakeup(sip)
+ register struct selinfo *sip;
+{
+ register struct proc *p;
+ int s;
+
+ if (sip->si_pid == 0)
+ return;
+ if (sip->si_flags & SI_COLL) {
+ nselcoll++;
+ sip->si_flags &= ~SI_COLL;
+ wakeup((caddr_t)&selwait);
+ }
+ p = pfind(sip->si_pid);
+ sip->si_pid = 0;
+ if (p != NULL) {
+ s = splhigh();
+ if (p->p_wchan == (caddr_t)&selwait) {
+ if (p->p_stat == SSLEEP)
+ setrunnable(p);
+ else
+ unsleep(p);
+ } else if (p->p_flag & P_SELECT)
+ p->p_flag &= ~P_SELECT;
+ splx(s);
+ }
+}
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
new file mode 100644
index 0000000..527371d
--- /dev/null
+++ b/sys/kern/sys_process.c
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)sys_process.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/errno.h>
+
+/*
+ * Process debugging system call.
+ */
+struct ptrace_args {
+ int req;
+ pid_t pid;
+ caddr_t addr;
+ int data;
+};
+int
+ptrace(a1, a2, a3)
+ struct proc *a1;
+ struct ptrace_args *a2;
+ int *a3;
+{
+
+ /*
+ * Body deleted.
+ */
+ return (ENOSYS);
+}
+
+int
+trace_req(a1)
+ struct proc *a1;
+{
+
+ /*
+ * Body deleted.
+ */
+ return (0);
+}
diff --git a/sys/kern/sys_socket.c b/sys/kern/sys_socket.c
new file mode 100644
index 0000000..63f5298
--- /dev/null
+++ b/sys/kern/sys_socket.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 1982, 1986, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)sys_socket.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+struct fileops socketops =
+ { soo_read, soo_write, soo_ioctl, soo_select, soo_close };
+
+/* ARGSUSED */
+int
+soo_read(fp, uio, cred)
+ struct file *fp;
+ struct uio *uio;
+ struct ucred *cred;
+{
+
+ return (soreceive((struct socket *)fp->f_data, (struct mbuf **)0,
+ uio, (struct mbuf **)0, (struct mbuf **)0, (int *)0));
+}
+
+/* ARGSUSED */
+int
+soo_write(fp, uio, cred)
+ struct file *fp;
+ struct uio *uio;
+ struct ucred *cred;
+{
+
+ return (sosend((struct socket *)fp->f_data, (struct mbuf *)0,
+ uio, (struct mbuf *)0, (struct mbuf *)0, 0));
+}
+
+int
+soo_ioctl(fp, cmd, data, p)
+ struct file *fp;
+ int cmd;
+ register caddr_t data;
+ struct proc *p;
+{
+ register struct socket *so = (struct socket *)fp->f_data;
+
+ switch (cmd) {
+
+ case FIONBIO:
+ if (*(int *)data)
+ so->so_state |= SS_NBIO;
+ else
+ so->so_state &= ~SS_NBIO;
+ return (0);
+
+ case FIOASYNC:
+ if (*(int *)data) {
+ so->so_state |= SS_ASYNC;
+ so->so_rcv.sb_flags |= SB_ASYNC;
+ so->so_snd.sb_flags |= SB_ASYNC;
+ } else {
+ so->so_state &= ~SS_ASYNC;
+ so->so_rcv.sb_flags &= ~SB_ASYNC;
+ so->so_snd.sb_flags &= ~SB_ASYNC;
+ }
+ return (0);
+
+ case FIONREAD:
+ *(int *)data = so->so_rcv.sb_cc;
+ return (0);
+
+ case SIOCSPGRP:
+ so->so_pgid = *(int *)data;
+ return (0);
+
+ case SIOCGPGRP:
+ *(int *)data = so->so_pgid;
+ return (0);
+
+ case SIOCATMARK:
+ *(int *)data = (so->so_state&SS_RCVATMARK) != 0;
+ return (0);
+ }
+ /*
+ * Interface/routing/protocol specific ioctls:
+ * interface and routing ioctls should have a
+ * different entry since a socket's unnecessary
+ */
+ if (IOCGROUP(cmd) == 'i')
+ return (ifioctl(so, cmd, data, p));
+ if (IOCGROUP(cmd) == 'r')
+ return (rtioctl(cmd, data, p));
+ return ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
+ (struct mbuf *)cmd, (struct mbuf *)data, (struct mbuf *)0));
+}
+
+int
+soo_select(fp, which, p)
+ struct file *fp;
+ int which;
+ struct proc *p;
+{
+ register struct socket *so = (struct socket *)fp->f_data;
+ register int s = splnet();
+
+ switch (which) {
+
+ case FREAD:
+ if (soreadable(so)) {
+ splx(s);
+ return (1);
+ }
+ selrecord(p, &so->so_rcv.sb_sel);
+ so->so_rcv.sb_flags |= SB_SEL;
+ break;
+
+ case FWRITE:
+ if (sowriteable(so)) {
+ splx(s);
+ return (1);
+ }
+ selrecord(p, &so->so_snd.sb_sel);
+ so->so_snd.sb_flags |= SB_SEL;
+ break;
+
+ case 0:
+ if (so->so_oobmark || (so->so_state & SS_RCVATMARK)) {
+ splx(s);
+ return (1);
+ }
+ selrecord(p, &so->so_rcv.sb_sel);
+ so->so_rcv.sb_flags |= SB_SEL;
+ break;
+ }
+ splx(s);
+ return (0);
+}
+
+int
+soo_stat(so, ub)
+ register struct socket *so;
+ register struct stat *ub;
+{
+
+ bzero((caddr_t)ub, sizeof (*ub));
+ ub->st_mode = S_IFSOCK;
+ return ((*so->so_proto->pr_usrreq)(so, PRU_SENSE,
+ (struct mbuf *)ub, (struct mbuf *)0,
+ (struct mbuf *)0));
+}
+
+/* ARGSUSED */
+int
+soo_close(fp, p)
+ struct file *fp;
+ struct proc *p;
+{
+ int error = 0;
+
+ if (fp->f_data)
+ error = soclose((struct socket *)fp->f_data);
+ fp->f_data = 0;
+ return (error);
+}
diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c
new file mode 100644
index 0000000..339b997
--- /dev/null
+++ b/sys/kern/syscalls.c
@@ -0,0 +1,251 @@
+/*
+ * System call names.
+ *
+ * DO NOT EDIT-- this file is automatically generated.
+ * created from @(#)syscalls.master 8.2 (Berkeley) 1/13/94
+ */
+
+char *syscallnames[] = {
+ "syscall", /* 0 = syscall */
+ "exit", /* 1 = exit */
+ "fork", /* 2 = fork */
+ "read", /* 3 = read */
+ "write", /* 4 = write */
+ "open", /* 5 = open */
+ "close", /* 6 = close */
+ "wait4", /* 7 = wait4 */
+ "old.creat", /* 8 = old creat */
+ "link", /* 9 = link */
+ "unlink", /* 10 = unlink */
+ "obs_execv", /* 11 = obsolete execv */
+ "chdir", /* 12 = chdir */
+ "fchdir", /* 13 = fchdir */
+ "mknod", /* 14 = mknod */
+ "chmod", /* 15 = chmod */
+ "chown", /* 16 = chown */
+ "break", /* 17 = break */
+ "getfsstat", /* 18 = getfsstat */
+ "old.lseek", /* 19 = old lseek */
+ "getpid", /* 20 = getpid */
+ "mount", /* 21 = mount */
+ "unmount", /* 22 = unmount */
+ "setuid", /* 23 = setuid */
+ "getuid", /* 24 = getuid */
+ "geteuid", /* 25 = geteuid */
+ "ptrace", /* 26 = ptrace */
+ "recvmsg", /* 27 = recvmsg */
+ "sendmsg", /* 28 = sendmsg */
+ "recvfrom", /* 29 = recvfrom */
+ "accept", /* 30 = accept */
+ "getpeername", /* 31 = getpeername */
+ "getsockname", /* 32 = getsockname */
+ "access", /* 33 = access */
+ "chflags", /* 34 = chflags */
+ "fchflags", /* 35 = fchflags */
+ "sync", /* 36 = sync */
+ "kill", /* 37 = kill */
+ "old.stat", /* 38 = old stat */
+ "getppid", /* 39 = getppid */
+ "old.lstat", /* 40 = old lstat */
+ "dup", /* 41 = dup */
+ "pipe", /* 42 = pipe */
+ "getegid", /* 43 = getegid */
+ "profil", /* 44 = profil */
+#ifdef KTRACE
+ "ktrace", /* 45 = ktrace */
+#else
+ "#45", /* 45 = ktrace */
+#endif
+ "sigaction", /* 46 = sigaction */
+ "getgid", /* 47 = getgid */
+ "sigprocmask", /* 48 = sigprocmask */
+ "getlogin", /* 49 = getlogin */
+ "setlogin", /* 50 = setlogin */
+ "acct", /* 51 = acct */
+ "sigpending", /* 52 = sigpending */
+ "sigaltstack", /* 53 = sigaltstack */
+ "ioctl", /* 54 = ioctl */
+ "reboot", /* 55 = reboot */
+ "revoke", /* 56 = revoke */
+ "symlink", /* 57 = symlink */
+ "readlink", /* 58 = readlink */
+ "execve", /* 59 = execve */
+ "umask", /* 60 = umask */
+ "chroot", /* 61 = chroot */
+ "old.fstat", /* 62 = old fstat */
+ "old.getkerninfo", /* 63 = old getkerninfo */
+ "old.getpagesize", /* 64 = old getpagesize */
+ "msync", /* 65 = msync */
+ "vfork", /* 66 = vfork */
+ "obs_vread", /* 67 = obsolete vread */
+ "obs_vwrite", /* 68 = obsolete vwrite */
+ "sbrk", /* 69 = sbrk */
+ "sstk", /* 70 = sstk */
+ "old.mmap", /* 71 = old mmap */
+ "vadvise", /* 72 = vadvise */
+ "munmap", /* 73 = munmap */
+ "mprotect", /* 74 = mprotect */
+ "madvise", /* 75 = madvise */
+ "obs_vhangup", /* 76 = obsolete vhangup */
+ "obs_vlimit", /* 77 = obsolete vlimit */
+ "mincore", /* 78 = mincore */
+ "getgroups", /* 79 = getgroups */
+ "setgroups", /* 80 = setgroups */
+ "getpgrp", /* 81 = getpgrp */
+ "setpgid", /* 82 = setpgid */
+ "setitimer", /* 83 = setitimer */
+ "old.wait", /* 84 = old wait */
+ "swapon", /* 85 = swapon */
+ "getitimer", /* 86 = getitimer */
+ "old.gethostname", /* 87 = old gethostname */
+ "old.sethostname", /* 88 = old sethostname */
+ "getdtablesize", /* 89 = getdtablesize */
+ "dup2", /* 90 = dup2 */
+ "#91", /* 91 = getdopt */
+ "fcntl", /* 92 = fcntl */
+ "select", /* 93 = select */
+ "#94", /* 94 = setdopt */
+ "fsync", /* 95 = fsync */
+ "setpriority", /* 96 = setpriority */
+ "socket", /* 97 = socket */
+ "connect", /* 98 = connect */
+ "old.accept", /* 99 = old accept */
+ "getpriority", /* 100 = getpriority */
+ "old.send", /* 101 = old send */
+ "old.recv", /* 102 = old recv */
+ "sigreturn", /* 103 = sigreturn */
+ "bind", /* 104 = bind */
+ "setsockopt", /* 105 = setsockopt */
+ "listen", /* 106 = listen */
+ "obs_vtimes", /* 107 = obsolete vtimes */
+ "old.sigvec", /* 108 = old sigvec */
+ "old.sigblock", /* 109 = old sigblock */
+ "old.sigsetmask", /* 110 = old sigsetmask */
+ "sigsuspend", /* 111 = sigsuspend */
+ "old.sigstack", /* 112 = old sigstack */
+ "old.recvmsg", /* 113 = old recvmsg */
+ "old.sendmsg", /* 114 = old sendmsg */
+#ifdef TRACE
+ "vtrace", /* 115 = vtrace */
+#else
+ "obs_vtrace", /* 115 = obsolete vtrace */
+#endif
+ "gettimeofday", /* 116 = gettimeofday */
+ "getrusage", /* 117 = getrusage */
+ "getsockopt", /* 118 = getsockopt */
+#ifdef vax
+ "resuba", /* 119 = resuba */
+#else
+ "#119", /* 119 = nosys */
+#endif
+ "readv", /* 120 = readv */
+ "writev", /* 121 = writev */
+ "settimeofday", /* 122 = settimeofday */
+ "fchown", /* 123 = fchown */
+ "fchmod", /* 124 = fchmod */
+ "old.recvfrom", /* 125 = old recvfrom */
+ "old.setreuid", /* 126 = old setreuid */
+ "old.setregid", /* 127 = old setregid */
+ "rename", /* 128 = rename */
+ "old.truncate", /* 129 = old truncate */
+ "old.ftruncate", /* 130 = old ftruncate */
+ "flock", /* 131 = flock */
+ "mkfifo", /* 132 = mkfifo */
+ "sendto", /* 133 = sendto */
+ "shutdown", /* 134 = shutdown */
+ "socketpair", /* 135 = socketpair */
+ "mkdir", /* 136 = mkdir */
+ "rmdir", /* 137 = rmdir */
+ "utimes", /* 138 = utimes */
+ "obs_4.2", /* 139 = obsolete 4.2 sigreturn */
+ "adjtime", /* 140 = adjtime */
+ "old.getpeername", /* 141 = old getpeername */
+ "old.gethostid", /* 142 = old gethostid */
+ "old.sethostid", /* 143 = old sethostid */
+ "old.getrlimit", /* 144 = old getrlimit */
+ "old.setrlimit", /* 145 = old setrlimit */
+ "old.killpg", /* 146 = old killpg */
+ "setsid", /* 147 = setsid */
+ "quotactl", /* 148 = quotactl */
+ "old.quota", /* 149 = old quota */
+ "old.getsockname", /* 150 = old getsockname */
+ "#151", /* 151 = nosys */
+ "#152", /* 152 = nosys */
+ "#153", /* 153 = nosys */
+ "#154", /* 154 = nosys */
+#ifdef NFS
+ "nfssvc", /* 155 = nfssvc */
+#else
+ "#155", /* 155 = nosys */
+#endif
+ "old.getdirentries", /* 156 = old getdirentries */
+ "statfs", /* 157 = statfs */
+ "fstatfs", /* 158 = fstatfs */
+ "#159", /* 159 = nosys */
+ "#160", /* 160 = nosys */
+#ifdef NFS
+ "getfh", /* 161 = getfh */
+#else
+ "#161", /* 161 = nosys */
+#endif
+ "getdomainname", /* 162 = getdomainname */
+ "setdomainname", /* 163 = setdomainname */
+ "uname", /* 164 = uname */
+ "sysarch", /* 165 = sysarch */
+ "#166", /* 166 = nosys */
+ "#167", /* 167 = nosys */
+ "#168", /* 168 = nosys */
+ "#169", /* 169 = nosys */
+ "#170", /* 170 = nosys */
+#ifdef SYSVSHM
+ "shmsys", /* 171 = shmsys */
+#else
+ "#171", /* 171 = nosys */
+#endif
+ "#172", /* 172 = nosys */
+ "#173", /* 173 = nosys */
+ "#174", /* 174 = nosys */
+ "#175", /* 175 = nosys */
+ "#176", /* 176 = nosys */
+ "#177", /* 177 = nosys */
+ "#178", /* 178 = nosys */
+ "#179", /* 179 = nosys */
+ "#180", /* 180 = nosys */
+ "setgid", /* 181 = setgid */
+ "setegid", /* 182 = setegid */
+ "seteuid", /* 183 = seteuid */
+#ifdef LFS
+ "lfs_bmapv", /* 184 = lfs_bmapv */
+ "lfs_markv", /* 185 = lfs_markv */
+ "lfs_segclean", /* 186 = lfs_segclean */
+ "lfs_segwait", /* 187 = lfs_segwait */
+#else
+ "#184", /* 184 = nosys */
+ "#185", /* 185 = nosys */
+ "#186", /* 186 = nosys */
+ "#187", /* 187 = nosys */
+#endif
+ "stat", /* 188 = stat */
+ "fstat", /* 189 = fstat */
+ "lstat", /* 190 = lstat */
+ "pathconf", /* 191 = pathconf */
+ "fpathconf", /* 192 = fpathconf */
+ "#193", /* 193 = nosys */
+ "getrlimit", /* 194 = getrlimit */
+ "setrlimit", /* 195 = setrlimit */
+ "getdirentries", /* 196 = getdirentries */
+ "mmap", /* 197 = mmap */
+ "__syscall", /* 198 = __syscall */
+ "lseek", /* 199 = lseek */
+ "truncate", /* 200 = truncate */
+ "ftruncate", /* 201 = ftruncate */
+ "__sysctl", /* 202 = __sysctl */
+ "mlock", /* 203 = mlock */
+ "munlock", /* 204 = munlock */
+ "#205", /* 205 = nosys */
+ "#206", /* 206 = nosys */
+ "#207", /* 207 = nosys */
+ "#208", /* 208 = nosys */
+ "#209", /* 209 = nosys */
+ "#210", /* 210 = nosys */
+};
diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master
new file mode 100644
index 0000000..4ba7df2
--- /dev/null
+++ b/sys/kern/syscalls.master
@@ -0,0 +1,276 @@
+ @(#)syscalls.master 8.2 (Berkeley) 1/13/94
+; System call name/number master file.
+; Processed to created init_sysent.c, syscalls.c and syscall.h.
+
+; Columns: number type nargs name altname/comments
+; number system call number, must be in order
+; type one of STD, OBSOL, UNIMPL, COMPAT
+; nargs number of arguments
+; name name of syscall routine
+; altname name of system call if different
+; for UNIMPL/OBSOL, name continues with comments
+
+; types:
+; STD always included
+; COMPAT included on COMPAT #ifdef
+; LIBCOMPAT included on COMPAT #ifdef, and placed in syscall.h
+; OBSOL obsolete, not included in system, only specifies name
+; UNIMPL not implemented, placeholder only
+
+; #ifdef's, etc. may be included, and are copied to the output files.
+
+; Reserved/unimplemented system calls in the range 0-150 inclusive
+; are reserved for use in future Berkeley releases.
+; Additional system calls implemented in vendor and other
+; redistributions should be placed in the reserved range at the end
+; of the current calls.
+
+0 STD 0 nosys syscall
+1 STD 1 exit
+2 STD 0 fork
+3 STD 3 read
+4 STD 3 write
+5 STD 3 open
+6 STD 1 close
+7 STD 4 wait4
+8 COMPAT 2 creat
+9 STD 2 link
+10 STD 1 unlink
+11 OBSOL 2 execv
+12 STD 1 chdir
+13 STD 1 fchdir
+14 STD 3 mknod
+15 STD 2 chmod
+16 STD 3 chown
+17 STD 1 obreak break
+18 STD 3 getfsstat
+19 COMPAT 3 lseek
+20 STD 0 getpid
+21 STD 4 mount
+22 STD 2 unmount
+23 STD 1 setuid
+24 STD 0 getuid
+25 STD 0 geteuid
+26 STD 4 ptrace
+27 STD 3 recvmsg
+28 STD 3 sendmsg
+29 STD 6 recvfrom
+30 STD 3 accept
+31 STD 3 getpeername
+32 STD 3 getsockname
+33 STD 2 access
+34 STD 2 chflags
+35 STD 2 fchflags
+36 STD 0 sync
+37 STD 2 kill
+38 COMPAT 2 stat
+39 STD 0 getppid
+40 COMPAT 2 lstat
+41 STD 2 dup
+42 STD 0 pipe
+43 STD 0 getegid
+44 STD 4 profil
+#ifdef KTRACE
+45 STD 4 ktrace
+#else
+45 UNIMPL 0 ktrace
+#endif
+46 STD 3 sigaction
+47 STD 0 getgid
+48 STD 2 sigprocmask
+49 STD 2 getlogin
+50 STD 1 setlogin
+51 STD 1 acct
+52 STD 0 sigpending
+53 STD 2 sigaltstack
+54 STD 3 ioctl
+55 STD 1 reboot
+56 STD 1 revoke
+57 STD 2 symlink
+58 STD 3 readlink
+59 STD 3 execve
+60 STD 1 umask
+61 STD 1 chroot
+62 COMPAT 2 fstat
+63 COMPAT 4 getkerninfo
+64 COMPAT 0 getpagesize
+65 STD 2 msync
+66 STD 0 vfork
+67 OBSOL 0 vread
+68 OBSOL 0 vwrite
+69 STD 1 sbrk
+70 STD 1 sstk
+71 COMPAT 6 mmap
+72 STD 1 ovadvise vadvise
+73 STD 2 munmap
+74 STD 3 mprotect
+75 STD 3 madvise
+76 OBSOL 0 vhangup
+77 OBSOL 0 vlimit
+78 STD 3 mincore
+79 STD 2 getgroups
+80 STD 2 setgroups
+81 STD 0 getpgrp
+82 STD 2 setpgid
+83 STD 3 setitimer
+84 COMPAT 0 wait
+85 STD 1 swapon
+86 STD 2 getitimer
+87 COMPAT 2 gethostname
+88 COMPAT 2 sethostname
+89 STD 0 getdtablesize
+90 STD 2 dup2
+91 UNIMPL 2 getdopt
+92 STD 3 fcntl
+93 STD 5 select
+94 UNIMPL 2 setdopt
+95 STD 1 fsync
+96 STD 3 setpriority
+97 STD 3 socket
+98 STD 3 connect
+99 COMPAT 3 accept
+100 STD 2 getpriority
+101 COMPAT 4 send
+102 COMPAT 4 recv
+103 STD 1 sigreturn
+104 STD 3 bind
+105 STD 5 setsockopt
+106 STD 2 listen
+107 OBSOL 0 vtimes
+108 COMPAT 3 sigvec
+109 COMPAT 1 sigblock
+110 COMPAT 1 sigsetmask
+111 STD 1 sigsuspend
+112 COMPAT 2 sigstack
+113 COMPAT 3 recvmsg
+114 COMPAT 3 sendmsg
+#ifdef TRACE
+115 STD 2 vtrace
+#else
+115 OBSOL 2 vtrace
+#endif
+116 STD 2 gettimeofday
+117 STD 2 getrusage
+118 STD 5 getsockopt
+#ifdef vax
+119 STD 1 resuba
+#else
+119 UNIMPL 0 nosys
+#endif
+120 STD 3 readv
+121 STD 3 writev
+122 STD 2 settimeofday
+123 STD 3 fchown
+124 STD 2 fchmod
+125 COMPAT 6 recvfrom
+126 COMPAT 2 setreuid
+127 COMPAT 2 setregid
+128 STD 2 rename
+129 COMPAT 2 truncate
+130 COMPAT 2 ftruncate
+131 STD 2 flock
+132 STD 2 mkfifo
+133 STD 6 sendto
+134 STD 2 shutdown
+135 STD 5 socketpair
+136 STD 2 mkdir
+137 STD 1 rmdir
+138 STD 2 utimes
+139 OBSOL 0 4.2 sigreturn
+140 STD 2 adjtime
+141 COMPAT 3 getpeername
+142 COMPAT 0 gethostid
+143 COMPAT 1 sethostid
+144 COMPAT 2 getrlimit
+145 COMPAT 2 setrlimit
+146 COMPAT 2 killpg
+147 STD 0 setsid
+148 STD 4 quotactl
+149 COMPAT 4 quota
+150 COMPAT 3 getsockname
+
+; Syscalls 151-180 inclusive are reserved for vendor-specific
+; system calls. (This includes various calls added for compatibity
+; with other Unix variants.)
+; Some of these calls are now supported by BSD...
+151 UNIMPL 0 nosys
+152 UNIMPL 0 nosys
+153 UNIMPL 0 nosys
+154 UNIMPL 0 nosys
+#ifdef NFS
+155 STD 2 nfssvc
+#else
+155 UNIMPL 0 nosys
+#endif
+156 COMPAT 4 getdirentries
+157 STD 2 statfs
+158 STD 2 fstatfs
+159 UNIMPL 0 nosys
+160 UNIMPL 0 nosys
+#ifdef NFS
+161 STD 2 getfh
+#else
+161 UNIMPL 0 nosys
+#endif
+162 STD 2 getdomainname
+163 STD 2 setdomainname
+164 STD 1 uname
+165 STD 2 sysarch
+166 UNIMPL 0 nosys
+167 UNIMPL 0 nosys
+168 UNIMPL 0 nosys
+169 UNIMPL 0 nosys
+170 UNIMPL 0 nosys
+#ifdef SYSVSHM
+171 STD 4 shmsys
+#else
+171 UNIMPL 0 nosys
+#endif
+172 UNIMPL 0 nosys
+173 UNIMPL 0 nosys
+174 UNIMPL 0 nosys
+175 UNIMPL 0 nosys
+176 UNIMPL 0 nosys
+177 UNIMPL 0 nosys
+178 UNIMPL 0 nosys
+179 UNIMPL 0 nosys
+180 UNIMPL 0 nosys
+
+; Syscalls 180-199 are used by/reserved for BSD
+181 STD 1 setgid
+182 STD 1 setegid
+183 STD 1 seteuid
+#ifdef LFS
+184 STD 3 lfs_bmapv
+185 STD 3 lfs_markv
+186 STD 2 lfs_segclean
+187 STD 2 lfs_segwait
+#else
+184 UNIMPL 0 nosys
+185 UNIMPL 0 nosys
+186 UNIMPL 0 nosys
+187 UNIMPL 0 nosys
+#endif
+188 STD 2 stat
+189 STD 2 fstat
+190 STD 2 lstat
+191 STD 2 pathconf
+192 STD 2 fpathconf
+193 UNIMPL 0 nosys
+194 STD 2 getrlimit
+195 STD 2 setrlimit
+196 STD 4 getdirentries
+197 STD 8 mmap
+198 STD 0 nosys __syscall
+199 STD 5 lseek
+200 STD 4 truncate
+201 STD 4 ftruncate
+202 STD 6 __sysctl
+203 STD 2 mlock
+204 STD 2 munlock
+205 UNIMPL 0 nosys
+206 UNIMPL 0 nosys
+207 UNIMPL 0 nosys
+208 UNIMPL 0 nosys
+209 UNIMPL 0 nosys
+210 UNIMPL 0 nosys
diff --git a/sys/kern/tty.c b/sys/kern/tty.c
new file mode 100644
index 0000000..fd3c051
--- /dev/null
+++ b/sys/kern/tty.c
@@ -0,0 +1,1940 @@
+/*-
+ * Copyright (c) 1982, 1986, 1990, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tty.c 8.8 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+#define TTYDEFCHARS
+#include <sys/tty.h>
+#undef TTYDEFCHARS
+#include <sys/file.h>
+#include <sys/conf.h>
+#include <sys/dkstat.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <sys/syslog.h>
+
+#include <vm/vm.h>
+
+static int proc_compare __P((struct proc *p1, struct proc *p2));
+static int ttnread __P((struct tty *));
+static void ttyblock __P((struct tty *tp));
+static void ttyecho __P((int, struct tty *tp));
+static void ttyrubo __P((struct tty *, int));
+
+/* Symbolic sleep message strings. */
+char ttclos[] = "ttycls";
+char ttopen[] = "ttyopn";
+char ttybg[] = "ttybg";
+char ttybuf[] = "ttybuf";
+char ttyin[] = "ttyin";
+char ttyout[] = "ttyout";
+
+#ifndef CBLOCKS_PER_TTY
+#define CBLOCKS_PER_TTY 10
+#endif
+
+/*
+ * Table with character classes and parity. The 8th bit indicates parity,
+ * the 7th bit indicates the character is an alphameric or underscore (for
+ * ALTWERASE), and the low 6 bits indicate delay type. If the low 6 bits
+ * are 0 then the character needs no special processing on output; classes
+ * other than 0 might be translated or (not currently) require delays.
+ */
+#define E 0x00 /* Even parity. */
+#define O 0x80 /* Odd parity. */
+#define PARITY(c) (char_type[c] & O)
+
+#define ALPHA 0x40 /* Alpha or underscore. */
+#define ISALPHA(c) (char_type[(c) & TTY_CHARMASK] & ALPHA)
+
+#define CCLASSMASK 0x3f
+#define CCLASS(c) (char_type[c] & CCLASSMASK)
+
+#define BS BACKSPACE
+#define CC CONTROL
+#define CR RETURN
+#define NA ORDINARY | ALPHA
+#define NL NEWLINE
+#define NO ORDINARY
+#define TB TAB
+#define VT VTAB
+
+char const char_type[] = {
+ E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* nul - bel */
+ O|BS, E|TB, E|NL, O|CC, E|VT, O|CR, O|CC, E|CC, /* bs - si */
+ O|CC, E|CC, E|CC, O|CC, E|CC, O|CC, O|CC, E|CC, /* dle - etb */
+ E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* can - us */
+ O|NO, E|NO, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* sp - ' */
+ E|NO, O|NO, O|NO, E|NO, O|NO, E|NO, E|NO, O|NO, /* ( - / */
+ E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* 0 - 7 */
+ O|NA, E|NA, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* 8 - ? */
+ O|NO, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* @ - G */
+ E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* H - O */
+ E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* P - W */
+ O|NA, E|NA, E|NA, O|NO, E|NO, O|NO, O|NO, O|NA, /* X - _ */
+ E|NO, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* ` - g */
+ O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* h - o */
+ O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* p - w */
+ E|NA, O|NA, O|NA, E|NO, O|NO, E|NO, E|NO, O|CC, /* x - del */
+ /*
+ * Meta chars; should be settable per character set;
+ * for now, treat them all as normal characters.
+ */
+ NA, NA, NA, NA, NA, NA, NA, NA,
+ NA, NA, NA, NA, NA, NA, NA, NA,
+ NA, NA, NA, NA, NA, NA, NA, NA,
+ NA, NA, NA, NA, NA, NA, NA, NA,
+ NA, NA, NA, NA, NA, NA, NA, NA,
+ NA, NA, NA, NA, NA, NA, NA, NA,
+ NA, NA, NA, NA, NA, NA, NA, NA,
+ NA, NA, NA, NA, NA, NA, NA, NA,
+ NA, NA, NA, NA, NA, NA, NA, NA,
+ NA, NA, NA, NA, NA, NA, NA, NA,
+ NA, NA, NA, NA, NA, NA, NA, NA,
+ NA, NA, NA, NA, NA, NA, NA, NA,
+ NA, NA, NA, NA, NA, NA, NA, NA,
+ NA, NA, NA, NA, NA, NA, NA, NA,
+ NA, NA, NA, NA, NA, NA, NA, NA,
+ NA, NA, NA, NA, NA, NA, NA, NA,
+};
+#undef BS
+#undef CC
+#undef CR
+#undef NA
+#undef NL
+#undef NO
+#undef TB
+#undef VT
+
+/* Macros to clear/set/test flags. */
+#define SET(t, f) (t) |= (f)
+#define CLR(t, f) (t) &= ~(f)
+#define ISSET(t, f) ((t) & (f))
+
+/*
+ * Initial open of tty, or (re)entry to standard tty line discipline.
+ */
+int
+ttyopen(device, tp)
+ dev_t device;
+ register struct tty *tp;
+{
+ int s;
+
+ s = spltty();
+ tp->t_dev = device;
+ if (!ISSET(tp->t_state, TS_ISOPEN)) {
+ SET(tp->t_state, TS_ISOPEN);
+ bzero(&tp->t_winsize, sizeof(tp->t_winsize));
+ /*
+ * Add some cblocks to the clistfree pool.
+ */
+ cblock_alloc_cblocks(CBLOCKS_PER_TTY);
+ }
+ CLR(tp->t_state, TS_WOPEN);
+ splx(s);
+ return (0);
+}
+
+/*
+ * Handle close() on a tty line: flush and set to initial state,
+ * bumping generation number so that pending read/write calls
+ * can detect recycling of the tty.
+ */
+int
+ttyclose(tp)
+ register struct tty *tp;
+{
+ extern struct tty *constty; /* Temporary virtual console. */
+
+ if (constty == tp)
+ constty = NULL;
+
+ ttyflush(tp, FREAD | FWRITE);
+
+ tp->t_gen++;
+ tp->t_pgrp = NULL;
+ tp->t_session = NULL;
+ /*
+ * If the tty has not already been closed, free the cblocks
+ * that were allocated in ttyopen() back to the system malloc
+ * pool.
+ */
+ if (ISSET(tp->t_state, (TS_ISOPEN|TS_WOPEN)))
+ cblock_free_cblocks(CBLOCKS_PER_TTY);
+ tp->t_state = 0;
+ return (0);
+}
+
+#define FLUSHQ(q) { \
+ if ((q)->c_cc) \
+ ndflush(q, (q)->c_cc); \
+}
+
+/* Is 'c' a line delimiter ("break" character)? */
+#define TTBREAKC(c) \
+ ((c) == '\n' || ((c) == cc[VEOF] || \
+ (c) == cc[VEOL] || (c) == cc[VEOL2]) && (c) != _POSIX_VDISABLE)
+
+
+/*
+ * Process input of a single character received on a tty.
+ */
+int
+ttyinput(c, tp)
+ register int c;
+ register struct tty *tp;
+{
+ register int iflag, lflag;
+ register u_char *cc;
+ int i, err;
+
+ /*
+ * If input is pending take it first.
+ */
+ lflag = tp->t_lflag;
+ if (ISSET(lflag, PENDIN))
+ ttypend(tp);
+ /*
+ * Gather stats.
+ */
+ if (ISSET(lflag, ICANON)) {
+ ++tk_cancc;
+ ++tp->t_cancc;
+ } else {
+ ++tk_rawcc;
+ ++tp->t_rawcc;
+ }
+ ++tk_nin;
+
+ /* Handle exceptional conditions (break, parity, framing). */
+ cc = tp->t_cc;
+ iflag = tp->t_iflag;
+ if (err = (ISSET(c, TTY_ERRORMASK))) {
+ CLR(c, TTY_ERRORMASK);
+ if (ISSET(err, TTY_FE) && !c) { /* Break. */
+ if (ISSET(iflag, IGNBRK))
+ goto endcase;
+ else if (ISSET(iflag, BRKINT) &&
+ ISSET(lflag, ISIG) &&
+ (cc[VINTR] != _POSIX_VDISABLE))
+ c = cc[VINTR];
+ else if (ISSET(iflag, PARMRK))
+ goto parmrk;
+ } else if (ISSET(err, TTY_PE) &&
+ ISSET(iflag, INPCK) || ISSET(err, TTY_FE)) {
+ if (ISSET(iflag, IGNPAR))
+ goto endcase;
+ else if (ISSET(iflag, PARMRK)) {
+parmrk: (void)putc(0377 | TTY_QUOTE, &tp->t_rawq);
+ (void)putc(0 | TTY_QUOTE, &tp->t_rawq);
+ (void)putc(c | TTY_QUOTE, &tp->t_rawq);
+ goto endcase;
+ } else
+ c = 0;
+ }
+ }
+ /*
+ * In tandem mode, check high water mark.
+ */
+ if (ISSET(iflag, IXOFF))
+ ttyblock(tp);
+ if (!ISSET(tp->t_state, TS_TYPEN) && ISSET(iflag, ISTRIP))
+ CLR(c, 0x80);
+ if (!ISSET(lflag, EXTPROC)) {
+ /*
+ * Check for literal nexting very first
+ */
+ if (ISSET(tp->t_state, TS_LNCH)) {
+ SET(c, TTY_QUOTE);
+ CLR(tp->t_state, TS_LNCH);
+ }
+ /*
+ * Scan for special characters. This code
+ * is really just a big case statement with
+ * non-constant cases. The bottom of the
+ * case statement is labeled ``endcase'', so goto
+ * it after a case match, or similar.
+ */
+
+ /*
+ * Control chars which aren't controlled
+ * by ICANON, ISIG, or IXON.
+ */
+ if (ISSET(lflag, IEXTEN)) {
+ if (CCEQ(cc[VLNEXT], c)) {
+ if (ISSET(lflag, ECHO)) {
+ if (ISSET(lflag, ECHOE)) {
+ (void)ttyoutput('^', tp);
+ (void)ttyoutput('\b', tp);
+ } else
+ ttyecho(c, tp);
+ }
+ SET(tp->t_state, TS_LNCH);
+ goto endcase;
+ }
+ if (CCEQ(cc[VDISCARD], c)) {
+ if (ISSET(lflag, FLUSHO))
+ CLR(tp->t_lflag, FLUSHO);
+ else {
+ ttyflush(tp, FWRITE);
+ ttyecho(c, tp);
+ if (tp->t_rawq.c_cc + tp->t_canq.c_cc)
+ ttyretype(tp);
+ SET(tp->t_lflag, FLUSHO);
+ }
+ goto startoutput;
+ }
+ }
+ /*
+ * Signals.
+ */
+ if (ISSET(lflag, ISIG)) {
+ if (CCEQ(cc[VINTR], c) || CCEQ(cc[VQUIT], c)) {
+ if (!ISSET(lflag, NOFLSH))
+ ttyflush(tp, FREAD | FWRITE);
+ ttyecho(c, tp);
+ pgsignal(tp->t_pgrp,
+ CCEQ(cc[VINTR], c) ? SIGINT : SIGQUIT, 1);
+ goto endcase;
+ }
+ if (CCEQ(cc[VSUSP], c)) {
+ if (!ISSET(lflag, NOFLSH))
+ ttyflush(tp, FREAD);
+ ttyecho(c, tp);
+ pgsignal(tp->t_pgrp, SIGTSTP, 1);
+ goto endcase;
+ }
+ }
+ /*
+ * Handle start/stop characters.
+ */
+ if (ISSET(iflag, IXON)) {
+ if (CCEQ(cc[VSTOP], c)) {
+ if (!ISSET(tp->t_state, TS_TTSTOP)) {
+ SET(tp->t_state, TS_TTSTOP);
+#ifdef sun4c /* XXX */
+ (*tp->t_stop)(tp, 0);
+#else
+ (*cdevsw[major(tp->t_dev)].d_stop)(tp,
+ 0);
+#endif
+ return (0);
+ }
+ if (!CCEQ(cc[VSTART], c))
+ return (0);
+ /*
+ * if VSTART == VSTOP then toggle
+ */
+ goto endcase;
+ }
+ if (CCEQ(cc[VSTART], c))
+ goto restartoutput;
+ }
+ /*
+ * IGNCR, ICRNL, & INLCR
+ */
+ if (c == '\r') {
+ if (ISSET(iflag, IGNCR))
+ goto endcase;
+ else if (ISSET(iflag, ICRNL))
+ c = '\n';
+ } else if (c == '\n' && ISSET(iflag, INLCR))
+ c = '\r';
+ }
+ if (!ISSET(tp->t_lflag, EXTPROC) && ISSET(lflag, ICANON)) {
+ /*
+ * From here on down canonical mode character
+ * processing takes place.
+ */
+ /*
+ * erase (^H / ^?)
+ */
+ if (CCEQ(cc[VERASE], c)) {
+ if (tp->t_rawq.c_cc)
+ ttyrub(unputc(&tp->t_rawq), tp);
+ goto endcase;
+ }
+ /*
+ * kill (^U)
+ */
+ if (CCEQ(cc[VKILL], c)) {
+ if (ISSET(lflag, ECHOKE) &&
+ tp->t_rawq.c_cc == tp->t_rocount &&
+ !ISSET(lflag, ECHOPRT))
+ while (tp->t_rawq.c_cc)
+ ttyrub(unputc(&tp->t_rawq), tp);
+ else {
+ ttyecho(c, tp);
+ if (ISSET(lflag, ECHOK) ||
+ ISSET(lflag, ECHOKE))
+ ttyecho('\n', tp);
+ FLUSHQ(&tp->t_rawq);
+ tp->t_rocount = 0;
+ }
+ CLR(tp->t_state, TS_LOCAL);
+ goto endcase;
+ }
+ /*
+ * word erase (^W)
+ */
+ if (CCEQ(cc[VWERASE], c)) {
+ int alt = ISSET(lflag, ALTWERASE);
+ int ctype;
+
+ /*
+ * erase whitespace
+ */
+ while ((c = unputc(&tp->t_rawq)) == ' ' || c == '\t')
+ ttyrub(c, tp);
+ if (c == -1)
+ goto endcase;
+ /*
+ * erase last char of word and remember the
+ * next chars type (for ALTWERASE)
+ */
+ ttyrub(c, tp);
+ c = unputc(&tp->t_rawq);
+ if (c == -1)
+ goto endcase;
+ if (c == ' ' || c == '\t') {
+ (void)putc(c, &tp->t_rawq);
+ goto endcase;
+ }
+ ctype = ISALPHA(c);
+ /*
+ * erase rest of word
+ */
+ do {
+ ttyrub(c, tp);
+ c = unputc(&tp->t_rawq);
+ if (c == -1)
+ goto endcase;
+ } while (c != ' ' && c != '\t' &&
+ (alt == 0 || ISALPHA(c) == ctype));
+ (void)putc(c, &tp->t_rawq);
+ goto endcase;
+ }
+ /*
+ * reprint line (^R)
+ */
+ if (CCEQ(cc[VREPRINT], c)) {
+ ttyretype(tp);
+ goto endcase;
+ }
+ /*
+ * ^T - kernel info and generate SIGINFO
+ */
+ if (CCEQ(cc[VSTATUS], c)) {
+ if (ISSET(lflag, ISIG))
+ pgsignal(tp->t_pgrp, SIGINFO, 1);
+ if (!ISSET(lflag, NOKERNINFO))
+ ttyinfo(tp);
+ goto endcase;
+ }
+ }
+ /*
+ * Check for input buffer overflow
+ */
+ if (tp->t_rawq.c_cc + tp->t_canq.c_cc >= TTYHOG) {
+ if (ISSET(iflag, IMAXBEL)) {
+ if (tp->t_outq.c_cc < tp->t_hiwat)
+ (void)ttyoutput(CTRL('g'), tp);
+ } else
+ ttyflush(tp, FREAD | FWRITE);
+ goto endcase;
+ }
+ /*
+ * Put data char in q for user and
+ * wakeup on seeing a line delimiter.
+ */
+ if (putc(c, &tp->t_rawq) >= 0) {
+ if (!ISSET(lflag, ICANON)) {
+ ttwakeup(tp);
+ ttyecho(c, tp);
+ goto endcase;
+ }
+ if (TTBREAKC(c)) {
+ tp->t_rocount = 0;
+ catq(&tp->t_rawq, &tp->t_canq);
+ ttwakeup(tp);
+ } else if (tp->t_rocount++ == 0)
+ tp->t_rocol = tp->t_column;
+ if (ISSET(tp->t_state, TS_ERASE)) {
+ /*
+ * end of prterase \.../
+ */
+ CLR(tp->t_state, TS_ERASE);
+ (void)ttyoutput('/', tp);
+ }
+ i = tp->t_column;
+ ttyecho(c, tp);
+ if (CCEQ(cc[VEOF], c) && ISSET(lflag, ECHO)) {
+ /*
+ * Place the cursor over the '^' of the ^D.
+ */
+ i = min(2, tp->t_column - i);
+ while (i > 0) {
+ (void)ttyoutput('\b', tp);
+ i--;
+ }
+ }
+ }
+endcase:
+ /*
+ * IXANY means allow any character to restart output.
+ */
+ if (ISSET(tp->t_state, TS_TTSTOP) &&
+ !ISSET(iflag, IXANY) && cc[VSTART] != cc[VSTOP])
+ return (0);
+restartoutput:
+ CLR(tp->t_lflag, FLUSHO);
+ CLR(tp->t_state, TS_TTSTOP);
+startoutput:
+ return (ttstart(tp));
+}
+
+/*
+ * Output a single character on a tty, doing output processing
+ * as needed (expanding tabs, newline processing, etc.).
+ * Returns < 0 if succeeds, otherwise returns char to resend.
+ * Must be recursive.
+ */
+int
+ttyoutput(c, tp)
+ register int c;
+ register struct tty *tp;
+{
+ register long oflag;
+ register int col, s;
+
+ oflag = tp->t_oflag;
+ if (!ISSET(oflag, OPOST)) {
+ if (ISSET(tp->t_lflag, FLUSHO))
+ return (-1);
+ if (putc(c, &tp->t_outq))
+ return (c);
+ tk_nout++;
+ tp->t_outcc++;
+ return (-1);
+ }
+ /*
+ * Do tab expansion if OXTABS is set. Special case if we external
+ * processing, we don't do the tab expansion because we'll probably
+ * get it wrong. If tab expansion needs to be done, let it happen
+ * externally.
+ */
+ CLR(c, ~TTY_CHARMASK);
+ if (c == '\t' &&
+ ISSET(oflag, OXTABS) && !ISSET(tp->t_lflag, EXTPROC)) {
+ c = 8 - (tp->t_column & 7);
+ if (!ISSET(tp->t_lflag, FLUSHO)) {
+ s = spltty(); /* Don't interrupt tabs. */
+ c -= b_to_q(" ", c, &tp->t_outq);
+ tk_nout += c;
+ tp->t_outcc += c;
+ splx(s);
+ }
+ tp->t_column += c;
+ return (c ? -1 : '\t');
+ }
+ if (c == CEOT && ISSET(oflag, ONOEOT))
+ return (-1);
+
+ /*
+ * Newline translation: if ONLCR is set,
+ * translate newline into "\r\n".
+ */
+ if (c == '\n' && ISSET(tp->t_oflag, ONLCR)) {
+ tk_nout++;
+ tp->t_outcc++;
+ if (putc('\r', &tp->t_outq))
+ return (c);
+ }
+ tk_nout++;
+ tp->t_outcc++;
+ if (!ISSET(tp->t_lflag, FLUSHO) && putc(c, &tp->t_outq))
+ return (c);
+
+ col = tp->t_column;
+ switch (CCLASS(c)) {
+ case BACKSPACE:
+ if (col > 0)
+ --col;
+ break;
+ case CONTROL:
+ break;
+ case NEWLINE:
+ case RETURN:
+ col = 0;
+ break;
+ case ORDINARY:
+ ++col;
+ break;
+ case TAB:
+ col = (col + 8) & ~7;
+ break;
+ }
+ tp->t_column = col;
+ return (-1);
+}
+
+/*
+ * Ioctls for all tty devices. Called after line-discipline specific ioctl
+ * has been called to do discipline-specific functions and/or reject any
+ * of these ioctl commands.
+ */
+/* ARGSUSED */
+int
+ttioctl(tp, cmd, data, flag)
+ register struct tty *tp;
+ int cmd, flag;
+ void *data;
+{
+ extern struct tty *constty; /* Temporary virtual console. */
+ extern int nlinesw;
+ register struct proc *p;
+ int s, error;
+
+ p = curproc; /* XXX */
+
+ /* If the ioctl involves modification, hang if in the background. */
+ switch (cmd) {
+ case TIOCFLUSH:
+ case TIOCSETA:
+ case TIOCSETD:
+ case TIOCSETAF:
+ case TIOCSETAW:
+#ifdef notdef
+ case TIOCSPGRP:
+#endif
+ case TIOCSTI:
+ case TIOCSWINSZ:
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+ case TIOCLBIC:
+ case TIOCLBIS:
+ case TIOCLSET:
+ case TIOCSETC:
+ case OTIOCSETD:
+ case TIOCSETN:
+ case TIOCSETP:
+ case TIOCSLTC:
+#endif
+ while (isbackground(curproc, tp) &&
+ p->p_pgrp->pg_jobc && (p->p_flag & P_PPWAIT) == 0 &&
+ (p->p_sigignore & sigmask(SIGTTOU)) == 0 &&
+ (p->p_sigmask & sigmask(SIGTTOU)) == 0) {
+ pgsignal(p->p_pgrp, SIGTTOU, 1);
+ if (error = ttysleep(tp,
+ &lbolt, TTOPRI | PCATCH, ttybg, 0))
+ return (error);
+ }
+ break;
+ }
+
+ switch (cmd) { /* Process the ioctl. */
+ case FIOASYNC: /* set/clear async i/o */
+ s = spltty();
+ if (*(int *)data)
+ SET(tp->t_state, TS_ASYNC);
+ else
+ CLR(tp->t_state, TS_ASYNC);
+ splx(s);
+ break;
+ case FIONBIO: /* set/clear non-blocking i/o */
+ break; /* XXX: delete. */
+ case FIONREAD: /* get # bytes to read */
+ *(int *)data = ttnread(tp);
+ break;
+ case TIOCEXCL: /* set exclusive use of tty */
+ s = spltty();
+ SET(tp->t_state, TS_XCLUDE);
+ splx(s);
+ break;
+ case TIOCFLUSH: { /* flush buffers */
+ register int flags = *(int *)data;
+
+ if (flags == 0)
+ flags = FREAD | FWRITE;
+ else
+ flags &= FREAD | FWRITE;
+ ttyflush(tp, flags);
+ break;
+ }
+ case TIOCCONS: /* become virtual console */
+ if (*(int *)data) {
+ if (constty && constty != tp &&
+ ISSET(constty->t_state, TS_CARR_ON | TS_ISOPEN) ==
+ (TS_CARR_ON | TS_ISOPEN))
+ return (EBUSY);
+#ifndef UCONSOLE
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+#endif
+ constty = tp;
+ } else if (tp == constty)
+ constty = NULL;
+ break;
+ case TIOCDRAIN: /* wait till output drained */
+ if (error = ttywait(tp))
+ return (error);
+ break;
+ case TIOCGETA: { /* get termios struct */
+ struct termios *t = (struct termios *)data;
+
+ bcopy(&tp->t_termios, t, sizeof(struct termios));
+ break;
+ }
+ case TIOCGETD: /* get line discipline */
+ *(int *)data = tp->t_line;
+ break;
+ case TIOCGWINSZ: /* get window size */
+ *(struct winsize *)data = tp->t_winsize;
+ break;
+ case TIOCGPGRP: /* get pgrp of tty */
+ if (!isctty(p, tp))
+ return (ENOTTY);
+ *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID;
+ break;
+#ifdef TIOCHPCL
+ case TIOCHPCL: /* hang up on last close */
+ s = spltty();
+ SET(tp->t_cflag, HUPCL);
+ splx(s);
+ break;
+#endif
+ case TIOCNXCL: /* reset exclusive use of tty */
+ s = spltty();
+ CLR(tp->t_state, TS_XCLUDE);
+ splx(s);
+ break;
+ case TIOCOUTQ: /* output queue size */
+ *(int *)data = tp->t_outq.c_cc;
+ break;
+ case TIOCSETA: /* set termios struct */
+ case TIOCSETAW: /* drain output, set */
+ case TIOCSETAF: { /* drn out, fls in, set */
+ register struct termios *t = (struct termios *)data;
+
+ s = spltty();
+ if (cmd == TIOCSETAW || cmd == TIOCSETAF) {
+ if (error = ttywait(tp)) {
+ splx(s);
+ return (error);
+ }
+ if (cmd == TIOCSETAF)
+ ttyflush(tp, FREAD);
+ }
+ if (!ISSET(t->c_cflag, CIGNORE)) {
+ /*
+ * Set device hardware.
+ */
+ if (tp->t_param && (error = (*tp->t_param)(tp, t))) {
+ splx(s);
+ return (error);
+ } else {
+ if (!ISSET(tp->t_state, TS_CARR_ON) &&
+ ISSET(tp->t_cflag, CLOCAL) &&
+ !ISSET(t->c_cflag, CLOCAL)) {
+ CLR(tp->t_state, TS_ISOPEN);
+ SET(tp->t_state, TS_WOPEN);
+ ttwakeup(tp);
+ }
+ tp->t_cflag = t->c_cflag;
+ tp->t_ispeed = t->c_ispeed;
+ tp->t_ospeed = t->c_ospeed;
+ }
+ ttsetwater(tp);
+ }
+ if (cmd != TIOCSETAF) {
+ if (ISSET(t->c_lflag, ICANON) !=
+ ISSET(tp->t_lflag, ICANON))
+ if (ISSET(t->c_lflag, ICANON)) {
+ SET(tp->t_lflag, PENDIN);
+ ttwakeup(tp);
+ } else {
+ struct clist tq;
+
+ catq(&tp->t_rawq, &tp->t_canq);
+ tq = tp->t_rawq;
+ tp->t_rawq = tp->t_canq;
+ tp->t_canq = tq;
+ CLR(tp->t_lflag, PENDIN);
+ }
+ }
+ tp->t_iflag = t->c_iflag;
+ tp->t_oflag = t->c_oflag;
+ /*
+ * Make the EXTPROC bit read only.
+ */
+ if (ISSET(tp->t_lflag, EXTPROC))
+ SET(t->c_lflag, EXTPROC);
+ else
+ CLR(t->c_lflag, EXTPROC);
+ tp->t_lflag = t->c_lflag | ISSET(tp->t_lflag, PENDIN);
+ bcopy(t->c_cc, tp->t_cc, sizeof(t->c_cc));
+ splx(s);
+ break;
+ }
+ case TIOCSETD: { /* set line discipline */
+ register int t = *(int *)data;
+ dev_t device = tp->t_dev;
+
+ if ((u_int)t >= nlinesw)
+ return (ENXIO);
+ if (t != tp->t_line) {
+ s = spltty();
+ (*linesw[tp->t_line].l_close)(tp, flag);
+ error = (*linesw[t].l_open)(device, tp);
+ if (error) {
+ (void)(*linesw[tp->t_line].l_open)(device, tp);
+ splx(s);
+ return (error);
+ }
+ tp->t_line = t;
+ splx(s);
+ }
+ break;
+ }
+ case TIOCSTART: /* start output, like ^Q */
+ s = spltty();
+ if (ISSET(tp->t_state, TS_TTSTOP) ||
+ ISSET(tp->t_lflag, FLUSHO)) {
+ CLR(tp->t_lflag, FLUSHO);
+ CLR(tp->t_state, TS_TTSTOP);
+ ttstart(tp);
+ }
+ splx(s);
+ break;
+ case TIOCSTI: /* simulate terminal input */
+ if (p->p_ucred->cr_uid && (flag & FREAD) == 0)
+ return (EPERM);
+ if (p->p_ucred->cr_uid && !isctty(p, tp))
+ return (EACCES);
+ (*linesw[tp->t_line].l_rint)(*(u_char *)data, tp);
+ break;
+ case TIOCSTOP: /* stop output, like ^S */
+ s = spltty();
+ if (!ISSET(tp->t_state, TS_TTSTOP)) {
+ SET(tp->t_state, TS_TTSTOP);
+#ifdef sun4c /* XXX */
+ (*tp->t_stop)(tp, 0);
+#else
+ (*cdevsw[major(tp->t_dev)].d_stop)(tp, 0);
+#endif
+ }
+ splx(s);
+ break;
+ case TIOCSCTTY: /* become controlling tty */
+ /* Session ctty vnode pointer set in vnode layer. */
+ if (!SESS_LEADER(p) ||
+ (p->p_session->s_ttyvp || tp->t_session) &&
+ (tp->t_session != p->p_session))
+ return (EPERM);
+ tp->t_session = p->p_session;
+ tp->t_pgrp = p->p_pgrp;
+ p->p_session->s_ttyp = tp;
+ p->p_flag |= P_CONTROLT;
+ break;
+ case TIOCSPGRP: { /* set pgrp of tty */
+ register struct pgrp *pgrp = pgfind(*(int *)data);
+
+ if (!isctty(p, tp))
+ return (ENOTTY);
+ else if (pgrp == NULL || pgrp->pg_session != p->p_session)
+ return (EPERM);
+ tp->t_pgrp = pgrp;
+ break;
+ }
+ case TIOCSWINSZ: /* set window size */
+ if (bcmp((caddr_t)&tp->t_winsize, data,
+ sizeof (struct winsize))) {
+ tp->t_winsize = *(struct winsize *)data;
+ pgsignal(tp->t_pgrp, SIGWINCH, 1);
+ }
+ break;
+ default:
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+ return (ttcompat(tp, cmd, data, flag));
+#else
+ return (-1);
+#endif
+ }
+ return (0);
+}
+
+int
+ttselect(device, rw, p)
+ dev_t device;
+ int rw;
+ struct proc *p;
+{
+ register struct tty *tp;
+ int nread, s;
+
+ tp = &cdevsw[major(device)].d_ttys[minor(device)];
+
+ s = spltty();
+ switch (rw) {
+ case FREAD:
+ nread = ttnread(tp);
+ if (nread > 0 || !ISSET(tp->t_cflag, CLOCAL) &&
+ !ISSET(tp->t_state, TS_CARR_ON))
+ goto win;
+ selrecord(p, &tp->t_rsel);
+ break;
+ case FWRITE:
+ if (tp->t_outq.c_cc <= tp->t_lowat) {
+win: splx(s);
+ return (1);
+ }
+ selrecord(p, &tp->t_wsel);
+ break;
+ }
+ splx(s);
+ return (0);
+}
+
+static int
+ttnread(tp)
+ struct tty *tp;
+{
+ int nread;
+
+ if (ISSET(tp->t_lflag, PENDIN))
+ ttypend(tp);
+ nread = tp->t_canq.c_cc;
+ if (!ISSET(tp->t_lflag, ICANON))
+ nread += tp->t_rawq.c_cc;
+ return (nread);
+}
+
+/*
+ * Wait for output to drain.
+ */
+int
+ttywait(tp)
+ register struct tty *tp;
+{
+ int error, s;
+
+ error = 0;
+ s = spltty();
+ while ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) &&
+ (ISSET(tp->t_state, TS_CARR_ON) || ISSET(tp->t_cflag, CLOCAL))
+ && tp->t_oproc) {
+ (*tp->t_oproc)(tp);
+ SET(tp->t_state, TS_ASLEEP);
+ if (error = ttysleep(tp,
+ &tp->t_outq, TTOPRI | PCATCH, ttyout, 0))
+ break;
+ }
+ splx(s);
+ return (error);
+}
+
+/*
+ * Flush if successfully wait.
+ */
+int
+ttywflush(tp)
+ struct tty *tp;
+{
+ int error;
+
+ if ((error = ttywait(tp)) == 0)
+ ttyflush(tp, FREAD);
+ return (error);
+}
+
+/*
+ * Flush tty read and/or write queues, notifying anyone waiting.
+ */
+void
+ttyflush(tp, rw)
+ register struct tty *tp;
+ int rw;
+{
+ register int s;
+
+ s = spltty();
+ if (rw & FREAD) {
+ FLUSHQ(&tp->t_canq);
+ FLUSHQ(&tp->t_rawq);
+ tp->t_rocount = 0;
+ tp->t_rocol = 0;
+ CLR(tp->t_state, TS_LOCAL);
+ ttwakeup(tp);
+ }
+ if (rw & FWRITE) {
+ CLR(tp->t_state, TS_TTSTOP);
+#ifdef sun4c /* XXX */
+ (*tp->t_stop)(tp, rw);
+#else
+ (*cdevsw[major(tp->t_dev)].d_stop)(tp, rw);
+#endif
+ FLUSHQ(&tp->t_outq);
+ wakeup((caddr_t)&tp->t_outq);
+ selwakeup(&tp->t_wsel);
+ }
+ splx(s);
+}
+
+/*
+ * Copy in the default termios characters.
+ */
+void
+ttychars(tp)
+ struct tty *tp;
+{
+
+ bcopy(ttydefchars, tp->t_cc, sizeof(ttydefchars));
+}
+
+/*
+ * Send stop character on input overflow.
+ */
+static void
+ttyblock(tp)
+ register struct tty *tp;
+{
+ register int total;
+
+ total = tp->t_rawq.c_cc + tp->t_canq.c_cc;
+ if (tp->t_rawq.c_cc > TTYHOG) {
+ ttyflush(tp, FREAD | FWRITE);
+ CLR(tp->t_state, TS_TBLOCK);
+ }
+ /*
+ * Block further input iff: current input > threshold
+ * AND input is available to user program.
+ */
+ if (total >= TTYHOG / 2 &&
+ !ISSET(tp->t_state, TS_TBLOCK) &&
+ !ISSET(tp->t_lflag, ICANON) || tp->t_canq.c_cc > 0 &&
+ tp->t_cc[VSTOP] != _POSIX_VDISABLE) {
+ if (putc(tp->t_cc[VSTOP], &tp->t_outq) == 0) {
+ SET(tp->t_state, TS_TBLOCK);
+ ttstart(tp);
+ }
+ }
+}
+
+void
+ttrstrt(tp_arg)
+ void *tp_arg;
+{
+ struct tty *tp;
+ int s;
+
+#ifdef DIAGNOSTIC
+ if (tp_arg == NULL)
+ panic("ttrstrt");
+#endif
+ tp = tp_arg;
+ s = spltty();
+
+ CLR(tp->t_state, TS_TIMEOUT);
+ ttstart(tp);
+
+ splx(s);
+}
+
+int
+ttstart(tp)
+ struct tty *tp;
+{
+
+ if (tp->t_oproc != NULL) /* XXX: Kludge for pty. */
+ (*tp->t_oproc)(tp);
+ return (0);
+}
+
+/*
+ * "close" a line discipline
+ */
+int
+ttylclose(tp, flag)
+ struct tty *tp;
+ int flag;
+{
+
+ if (flag & IO_NDELAY)
+ ttyflush(tp, FREAD | FWRITE);
+ else
+ ttywflush(tp);
+ return (0);
+}
+
+/*
+ * Handle modem control transition on a tty.
+ * Flag indicates new state of carrier.
+ * Returns 0 if the line should be turned off, otherwise 1.
+ */
+int
+ttymodem(tp, flag)
+ register struct tty *tp;
+ int flag;
+{
+
+ if (!ISSET(tp->t_state, TS_WOPEN) && ISSET(tp->t_cflag, MDMBUF)) {
+ /*
+ * MDMBUF: do flow control according to carrier flag
+ */
+ if (flag) {
+ CLR(tp->t_state, TS_TTSTOP);
+ ttstart(tp);
+ } else if (!ISSET(tp->t_state, TS_TTSTOP)) {
+ SET(tp->t_state, TS_TTSTOP);
+#ifdef sun4c /* XXX */
+ (*tp->t_stop)(tp, 0);
+#else
+ (*cdevsw[major(tp->t_dev)].d_stop)(tp, 0);
+#endif
+ }
+ } else if (flag == 0) {
+ /*
+ * Lost carrier.
+ */
+ CLR(tp->t_state, TS_CARR_ON);
+ if (ISSET(tp->t_state, TS_ISOPEN) &&
+ !ISSET(tp->t_cflag, CLOCAL)) {
+ if (tp->t_session && tp->t_session->s_leader)
+ psignal(tp->t_session->s_leader, SIGHUP);
+ ttyflush(tp, FREAD | FWRITE);
+ return (0);
+ }
+ } else {
+ /*
+ * Carrier now on.
+ */
+ SET(tp->t_state, TS_CARR_ON);
+ ttwakeup(tp);
+ }
+ return (1);
+}
+
+/*
+ * Default modem control routine (for other line disciplines).
+ * Return argument flag, to turn off device on carrier drop.
+ */
+int
+nullmodem(tp, flag)
+ register struct tty *tp;
+ int flag;
+{
+
+ if (flag)
+ SET(tp->t_state, TS_CARR_ON);
+ else {
+ CLR(tp->t_state, TS_CARR_ON);
+ if (!ISSET(tp->t_cflag, CLOCAL)) {
+ if (tp->t_session && tp->t_session->s_leader)
+ psignal(tp->t_session->s_leader, SIGHUP);
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Reinput pending characters after state switch
+ * call at spltty().
+ */
+void
+ttypend(tp)
+ register struct tty *tp;
+{
+ struct clist tq;
+ register c;
+
+ CLR(tp->t_lflag, PENDIN);
+ SET(tp->t_state, TS_TYPEN);
+ tq = tp->t_rawq;
+ tp->t_rawq.c_cc = 0;
+ tp->t_rawq.c_cf = tp->t_rawq.c_cl = 0;
+ while ((c = getc(&tq)) >= 0)
+ ttyinput(c, tp);
+ CLR(tp->t_state, TS_TYPEN);
+}
+
+/*
+ * Process a read call on a tty device.
+ */
+int
+ttread(tp, uio, flag)
+ register struct tty *tp;
+ struct uio *uio;
+ int flag;
+{
+ register struct clist *qp;
+ register int c;
+ register long lflag;
+ register u_char *cc = tp->t_cc;
+ register struct proc *p = curproc;
+ int s, first, error = 0;
+
+loop: lflag = tp->t_lflag;
+ s = spltty();
+ /*
+ * take pending input first
+ */
+ if (ISSET(lflag, PENDIN))
+ ttypend(tp);
+ splx(s);
+
+ /*
+ * Hang process if it's in the background.
+ */
+ if (isbackground(p, tp)) {
+ if ((p->p_sigignore & sigmask(SIGTTIN)) ||
+ (p->p_sigmask & sigmask(SIGTTIN)) ||
+ p->p_flag & P_PPWAIT || p->p_pgrp->pg_jobc == 0)
+ return (EIO);
+ pgsignal(p->p_pgrp, SIGTTIN, 1);
+ if (error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, ttybg, 0))
+ return (error);
+ goto loop;
+ }
+
+ /*
+ * If canonical, use the canonical queue,
+ * else use the raw queue.
+ *
+ * (should get rid of clists...)
+ */
+ qp = ISSET(lflag, ICANON) ? &tp->t_canq : &tp->t_rawq;
+
+ /*
+ * If there is no input, sleep on rawq
+ * awaiting hardware receipt and notification.
+ * If we have data, we don't need to check for carrier.
+ */
+ s = spltty();
+ if (qp->c_cc <= 0) {
+ int carrier;
+
+ carrier = ISSET(tp->t_state, TS_CARR_ON) ||
+ ISSET(tp->t_cflag, CLOCAL);
+ if (!carrier && ISSET(tp->t_state, TS_ISOPEN)) {
+ splx(s);
+ return (0); /* EOF */
+ }
+ if (flag & IO_NDELAY) {
+ splx(s);
+ return (EWOULDBLOCK);
+ }
+ error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH,
+ carrier ? ttyin : ttopen, 0);
+ splx(s);
+ if (error)
+ return (error);
+ goto loop;
+ }
+ splx(s);
+
+ /*
+ * Input present, check for input mapping and processing.
+ */
+ first = 1;
+ while ((c = getc(qp)) >= 0) {
+ /*
+ * delayed suspend (^Y)
+ */
+ if (CCEQ(cc[VDSUSP], c) && ISSET(lflag, ISIG)) {
+ pgsignal(tp->t_pgrp, SIGTSTP, 1);
+ if (first) {
+ if (error = ttysleep(tp,
+ &lbolt, TTIPRI | PCATCH, ttybg, 0))
+ break;
+ goto loop;
+ }
+ break;
+ }
+ /*
+ * Interpret EOF only in canonical mode.
+ */
+ if (CCEQ(cc[VEOF], c) && ISSET(lflag, ICANON))
+ break;
+ /*
+ * Give user character.
+ */
+ error = ureadc(c, uio);
+ if (error)
+ break;
+ if (uio->uio_resid == 0)
+ break;
+ /*
+ * In canonical mode check for a "break character"
+ * marking the end of a "line of input".
+ */
+ if (ISSET(lflag, ICANON) && TTBREAKC(c))
+ break;
+ first = 0;
+ }
+ /*
+ * Look to unblock output now that (presumably)
+ * the input queue has gone down.
+ */
+ s = spltty();
+ if (ISSET(tp->t_state, TS_TBLOCK) && tp->t_rawq.c_cc < TTYHOG/5) {
+ if (cc[VSTART] != _POSIX_VDISABLE &&
+ putc(cc[VSTART], &tp->t_outq) == 0) {
+ CLR(tp->t_state, TS_TBLOCK);
+ ttstart(tp);
+ }
+ }
+ splx(s);
+ return (error);
+}
+
+/*
+ * Check the output queue on tp for space for a kernel message (from uprintf
+ * or tprintf). Allow some space over the normal hiwater mark so we don't
+ * lose messages due to normal flow control, but don't let the tty run amok.
+ * Sleeps here are not interruptible, but we return prematurely if new signals
+ * arrive.
+ */
+int
+ttycheckoutq(tp, wait)
+ register struct tty *tp;
+ int wait;
+{
+ int hiwat, s, oldsig;
+
+ hiwat = tp->t_hiwat;
+ s = spltty();
+ oldsig = wait ? curproc->p_siglist : 0;
+ if (tp->t_outq.c_cc > hiwat + 200)
+ while (tp->t_outq.c_cc > hiwat) {
+ ttstart(tp);
+ if (wait == 0 || curproc->p_siglist != oldsig) {
+ splx(s);
+ return (0);
+ }
+ timeout((void (*)__P((void *)))wakeup,
+ (void *)&tp->t_outq, hz);
+ SET(tp->t_state, TS_ASLEEP);
+ sleep((caddr_t)&tp->t_outq, PZERO - 1);
+ }
+ splx(s);
+ return (1);
+}
+
+/*
+ * Process a write call on a tty device.
+ */
+int
+ttwrite(tp, uio, flag)
+ register struct tty *tp;
+ register struct uio *uio;
+ int flag;
+{
+ register char *cp = 0;
+ register int cc, ce;
+ register struct proc *p;
+ int i, hiwat, cnt, error, s;
+ char obuf[OBUFSIZ];
+
+ hiwat = tp->t_hiwat;
+ cnt = uio->uio_resid;
+ error = 0;
+ cc = 0;
+loop:
+ s = spltty();
+ if (!ISSET(tp->t_state, TS_CARR_ON) &&
+ !ISSET(tp->t_cflag, CLOCAL)) {
+ if (ISSET(tp->t_state, TS_ISOPEN)) {
+ splx(s);
+ return (EIO);
+ } else if (flag & IO_NDELAY) {
+ splx(s);
+ error = EWOULDBLOCK;
+ goto out;
+ } else {
+ /* Sleep awaiting carrier. */
+ error = ttysleep(tp,
+ &tp->t_rawq, TTIPRI | PCATCH,ttopen, 0);
+ splx(s);
+ if (error)
+ goto out;
+ goto loop;
+ }
+ }
+ splx(s);
+ /*
+ * Hang the process if it's in the background.
+ */
+ p = curproc;
+ if (isbackground(p, tp) &&
+ ISSET(tp->t_lflag, TOSTOP) && (p->p_flag & P_PPWAIT) == 0 &&
+ (p->p_sigignore & sigmask(SIGTTOU)) == 0 &&
+ (p->p_sigmask & sigmask(SIGTTOU)) == 0 &&
+ p->p_pgrp->pg_jobc) {
+ pgsignal(p->p_pgrp, SIGTTOU, 1);
+ if (error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, ttybg, 0))
+ goto out;
+ goto loop;
+ }
+ /*
+ * Process the user's data in at most OBUFSIZ chunks. Perform any
+ * output translation. Keep track of high water mark, sleep on
+ * overflow awaiting device aid in acquiring new space.
+ */
+ while (uio->uio_resid > 0 || cc > 0) {
+ if (ISSET(tp->t_lflag, FLUSHO)) {
+ uio->uio_resid = 0;
+ return (0);
+ }
+ if (tp->t_outq.c_cc > hiwat)
+ goto ovhiwat;
+ /*
+ * Grab a hunk of data from the user, unless we have some
+ * leftover from last time.
+ */
+ if (cc == 0) {
+ cc = min(uio->uio_resid, OBUFSIZ);
+ cp = obuf;
+ error = uiomove(cp, cc, uio);
+ if (error) {
+ cc = 0;
+ break;
+ }
+ }
+ /*
+ * If nothing fancy need be done, grab those characters we
+ * can handle without any of ttyoutput's processing and
+ * just transfer them to the output q. For those chars
+ * which require special processing (as indicated by the
+ * bits in char_type), call ttyoutput. After processing
+ * a hunk of data, look for FLUSHO so ^O's will take effect
+ * immediately.
+ */
+ while (cc > 0) {
+ if (!ISSET(tp->t_oflag, OPOST))
+ ce = cc;
+ else {
+ ce = cc - scanc((u_int)cc, (u_char *)cp,
+ (u_char *)char_type, CCLASSMASK);
+ /*
+ * If ce is zero, then we're processing
+ * a special character through ttyoutput.
+ */
+ if (ce == 0) {
+ tp->t_rocount = 0;
+ if (ttyoutput(*cp, tp) >= 0) {
+ /* No Clists, wait a bit. */
+ ttstart(tp);
+ if (error = ttysleep(tp, &lbolt,
+ TTOPRI | PCATCH, ttybuf, 0))
+ break;
+ goto loop;
+ }
+ cp++;
+ cc--;
+ if (ISSET(tp->t_lflag, FLUSHO) ||
+ tp->t_outq.c_cc > hiwat)
+ goto ovhiwat;
+ continue;
+ }
+ }
+ /*
+ * A bunch of normal characters have been found.
+ * Transfer them en masse to the output queue and
+ * continue processing at the top of the loop.
+ * If there are any further characters in this
+ * <= OBUFSIZ chunk, the first should be a character
+ * requiring special handling by ttyoutput.
+ */
+ tp->t_rocount = 0;
+ i = b_to_q(cp, ce, &tp->t_outq);
+ ce -= i;
+ tp->t_column += ce;
+ cp += ce, cc -= ce, tk_nout += ce;
+ tp->t_outcc += ce;
+ if (i > 0) {
+ /* No Clists, wait a bit. */
+ ttstart(tp);
+ if (error = ttysleep(tp,
+ &lbolt, TTOPRI | PCATCH, ttybuf, 0))
+ break;
+ goto loop;
+ }
+ if (ISSET(tp->t_lflag, FLUSHO) ||
+ tp->t_outq.c_cc > hiwat)
+ break;
+ }
+ ttstart(tp);
+ }
+out:
+ /*
+ * If cc is nonzero, we leave the uio structure inconsistent, as the
+ * offset and iov pointers have moved forward, but it doesn't matter
+ * (the call will either return short or restart with a new uio).
+ */
+ uio->uio_resid += cc;
+ return (error);
+
+ovhiwat:
+ ttstart(tp);
+ s = spltty();
+ /*
+ * This can only occur if FLUSHO is set in t_lflag,
+ * or if ttstart/oproc is synchronous (or very fast).
+ */
+ if (tp->t_outq.c_cc <= hiwat) {
+ splx(s);
+ goto loop;
+ }
+ if (flag & IO_NDELAY) {
+ splx(s);
+ uio->uio_resid += cc;
+ return (uio->uio_resid == cnt ? EWOULDBLOCK : 0);
+ }
+ if (tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) {
+ SET(tp->t_state, TS_ASLEEP);
+ error = ttysleep(tp, &tp->t_outq, TTOPRI | PCATCH, ttyout, 0);
+ }
+ splx(s);
+ if (error)
+ goto out;
+ goto loop;
+}
+
+/*
+ * Rubout one character from the rawq of tp
+ * as cleanly as possible.
+ */
+void
+ttyrub(c, tp)
+ register int c;
+ register struct tty *tp;
+{
+ register char *cp;
+ register int savecol;
+ int tabc, s;
+
+ if (!ISSET(tp->t_lflag, ECHO) || ISSET(tp->t_lflag, EXTPROC))
+ return;
+ CLR(tp->t_lflag, FLUSHO);
+ if (ISSET(tp->t_lflag, ECHOE)) {
+ if (tp->t_rocount == 0) {
+ /*
+ * Screwed by ttwrite; retype
+ */
+ ttyretype(tp);
+ return;
+ }
+ if (c == ('\t' | TTY_QUOTE) || c == ('\n' | TTY_QUOTE))
+ ttyrubo(tp, 2);
+ else {
+ CLR(c, ~TTY_CHARMASK);
+ switch (CCLASS(c)) {
+ case ORDINARY:
+ ttyrubo(tp, 1);
+ break;
+ case BACKSPACE:
+ case CONTROL:
+ case NEWLINE:
+ case RETURN:
+ case VTAB:
+ if (ISSET(tp->t_lflag, ECHOCTL))
+ ttyrubo(tp, 2);
+ break;
+ case TAB:
+ if (tp->t_rocount < tp->t_rawq.c_cc) {
+ ttyretype(tp);
+ return;
+ }
+ s = spltty();
+ savecol = tp->t_column;
+ SET(tp->t_state, TS_CNTTB);
+ SET(tp->t_lflag, FLUSHO);
+ tp->t_column = tp->t_rocol;
+ cp = tp->t_rawq.c_cf;
+ if (cp)
+ tabc = *cp; /* XXX FIX NEXTC */
+ for (; cp; cp = nextc(&tp->t_rawq, cp, &tabc))
+ ttyecho(tabc, tp);
+ CLR(tp->t_lflag, FLUSHO);
+ CLR(tp->t_state, TS_CNTTB);
+ splx(s);
+
+ /* savecol will now be length of the tab. */
+ savecol -= tp->t_column;
+ tp->t_column += savecol;
+ if (savecol > 8)
+ savecol = 8; /* overflow screw */
+ while (--savecol >= 0)
+ (void)ttyoutput('\b', tp);
+ break;
+ default: /* XXX */
+#define PANICSTR "ttyrub: would panic c = %d, val = %d\n"
+ (void)printf(PANICSTR, c, CCLASS(c));
+#ifdef notdef
+ panic(PANICSTR, c, CCLASS(c));
+#endif
+ }
+ }
+ } else if (ISSET(tp->t_lflag, ECHOPRT)) {
+ if (!ISSET(tp->t_state, TS_ERASE)) {
+ SET(tp->t_state, TS_ERASE);
+ (void)ttyoutput('\\', tp);
+ }
+ ttyecho(c, tp);
+ } else
+ ttyecho(tp->t_cc[VERASE], tp);
+ --tp->t_rocount;
+}
+
+/*
+ * Back over cnt characters, erasing them.
+ */
+static void
+ttyrubo(tp, cnt)
+ register struct tty *tp;
+ int cnt;
+{
+
+ while (cnt-- > 0) {
+ (void)ttyoutput('\b', tp);
+ (void)ttyoutput(' ', tp);
+ (void)ttyoutput('\b', tp);
+ }
+}
+
+/*
+ * ttyretype --
+ * Reprint the rawq line. Note, it is assumed that c_cc has already
+ * been checked.
+ */
+void
+ttyretype(tp)
+ register struct tty *tp;
+{
+ register char *cp;
+ int s, c;
+
+ /* Echo the reprint character. */
+ if (tp->t_cc[VREPRINT] != _POSIX_VDISABLE)
+ ttyecho(tp->t_cc[VREPRINT], tp);
+
+ (void)ttyoutput('\n', tp);
+
+ /*
+ * XXX
+ * FIX: NEXTC IS BROKEN - DOESN'T CHECK QUOTE
+ * BIT OF FIRST CHAR.
+ */
+ s = spltty();
+ for (cp = tp->t_canq.c_cf, c = (cp != NULL ? *cp : 0);
+ cp != NULL; cp = nextc(&tp->t_canq, cp, &c))
+ ttyecho(c, tp);
+ for (cp = tp->t_rawq.c_cf, c = (cp != NULL ? *cp : 0);
+ cp != NULL; cp = nextc(&tp->t_rawq, cp, &c))
+ ttyecho(c, tp);
+ CLR(tp->t_state, TS_ERASE);
+ splx(s);
+
+ tp->t_rocount = tp->t_rawq.c_cc;
+ tp->t_rocol = 0;
+}
+
+/*
+ * Echo a typed character to the terminal.
+ */
+static void
+ttyecho(c, tp)
+ register int c;
+ register struct tty *tp;
+{
+
+ if (!ISSET(tp->t_state, TS_CNTTB))
+ CLR(tp->t_lflag, FLUSHO);
+ if ((!ISSET(tp->t_lflag, ECHO) &&
+ (!ISSET(tp->t_lflag, ECHONL) || c == '\n')) ||
+ ISSET(tp->t_lflag, EXTPROC))
+ return;
+ if (ISSET(tp->t_lflag, ECHOCTL) &&
+ (ISSET(c, TTY_CHARMASK) <= 037 && c != '\t' && c != '\n' ||
+ ISSET(c, TTY_CHARMASK) == 0177)) {
+ (void)ttyoutput('^', tp);
+ CLR(c, ~TTY_CHARMASK);
+ if (c == 0177)
+ c = '?';
+ else
+ c += 'A' - 1;
+ }
+ (void)ttyoutput(c, tp);
+}
+
+/*
+ * Wake up any readers on a tty.
+ */
+void
+ttwakeup(tp)
+ register struct tty *tp;
+{
+
+ selwakeup(&tp->t_rsel);
+ if (ISSET(tp->t_state, TS_ASYNC))
+ pgsignal(tp->t_pgrp, SIGIO, 1);
+ wakeup((caddr_t)&tp->t_rawq);
+}
+
+/*
+ * Look up a code for a specified speed in a conversion table;
+ * used by drivers to map software speed values to hardware parameters.
+ */
+int
+ttspeedtab(speed, table)
+ int speed;
+ register struct speedtab *table;
+{
+
+ for ( ; table->sp_speed != -1; table++)
+ if (table->sp_speed == speed)
+ return (table->sp_code);
+ return (-1);
+}
+
+/*
+ * Set tty hi and low water marks.
+ *
+ * Try to arrange the dynamics so there's about one second
+ * from hi to low water.
+ *
+ */
+void
+ttsetwater(tp)
+ struct tty *tp;
+{
+ register int cps, x;
+
+#define CLAMP(x, h, l) ((x) > h ? h : ((x) < l) ? l : (x))
+
+ cps = tp->t_ospeed / 10;
+ tp->t_lowat = x = CLAMP(cps / 2, TTMAXLOWAT, TTMINLOWAT);
+ x += cps;
+ x = CLAMP(x, TTMAXHIWAT, TTMINHIWAT);
+ tp->t_hiwat = roundup(x, CBSIZE);
+#undef CLAMP
+}
+
+/*
+ * Report on state of foreground process group.
+ */
+void
+ttyinfo(tp)
+ register struct tty *tp;
+{
+ register struct proc *p, *pick;
+ struct timeval utime, stime;
+ int tmp;
+
+ if (ttycheckoutq(tp,0) == 0)
+ return;
+
+ /* Print load average. */
+ tmp = (averunnable.ldavg[0] * 100 + FSCALE / 2) >> FSHIFT;
+ ttyprintf(tp, "load: %d.%02d ", tmp / 100, tmp % 100);
+
+ if (tp->t_session == NULL)
+ ttyprintf(tp, "not a controlling terminal\n");
+ else if (tp->t_pgrp == NULL)
+ ttyprintf(tp, "no foreground process group\n");
+ else if ((p = tp->t_pgrp->pg_mem) == NULL)
+ ttyprintf(tp, "empty foreground process group\n");
+ else {
+ /* Pick interesting process. */
+ for (pick = NULL; p != NULL; p = p->p_pgrpnxt)
+ if (proc_compare(pick, p))
+ pick = p;
+
+ ttyprintf(tp, " cmd: %s %d [%s] ", pick->p_comm, pick->p_pid,
+ pick->p_stat == SRUN ? "running" :
+ pick->p_wmesg ? pick->p_wmesg : "iowait");
+
+ calcru(pick, &utime, &stime, NULL);
+
+ /* Print user time. */
+ ttyprintf(tp, "%d.%02du ",
+ utime.tv_sec, (utime.tv_usec + 5000) / 10000);
+
+ /* Print system time. */
+ ttyprintf(tp, "%d.%02ds ",
+ stime.tv_sec, (stime.tv_usec + 5000) / 10000);
+
+#define pgtok(a) (((a) * NBPG) / 1024)
+ /* Print percentage cpu, resident set size. */
+ tmp = pick->p_pctcpu * 10000 + FSCALE / 2 >> FSHIFT;
+ ttyprintf(tp, "%d%% %dk\n",
+ tmp / 100,
+ pick->p_stat == SIDL || pick->p_stat == SZOMB ? 0 :
+#ifdef pmap_resident_count
+ pgtok(pmap_resident_count(&pick->p_vmspace->vm_pmap))
+#else
+ pgtok(pick->p_vmspace->vm_rssize)
+#endif
+ );
+ }
+ tp->t_rocount = 0; /* so pending input will be retyped if BS */
+}
+
+/*
+ * Returns 1 if p2 is "better" than p1
+ *
+ * The algorithm for picking the "interesting" process is thus:
+ *
+ * 1) Only foreground processes are eligible - implied.
+ * 2) Runnable processes are favored over anything else. The runner
+ * with the highest cpu utilization is picked (p_estcpu). Ties are
+ * broken by picking the highest pid.
+ * 3) The sleeper with the shortest sleep time is next. With ties,
+ * we pick out just "short-term" sleepers (P_SINTR == 0).
+ * 4) Further ties are broken by picking the highest pid.
+ */
+#define ISRUN(p) (((p)->p_stat == SRUN) || ((p)->p_stat == SIDL))
+#define TESTAB(a, b) ((a)<<1 | (b))
+#define ONLYA 2
+#define ONLYB 1
+#define BOTH 3
+
+static int
+proc_compare(p1, p2)
+ register struct proc *p1, *p2;
+{
+
+ if (p1 == NULL)
+ return (1);
+ /*
+ * see if at least one of them is runnable
+ */
+ switch (TESTAB(ISRUN(p1), ISRUN(p2))) {
+ case ONLYA:
+ return (0);
+ case ONLYB:
+ return (1);
+ case BOTH:
+ /*
+ * tie - favor one with highest recent cpu utilization
+ */
+ if (p2->p_estcpu > p1->p_estcpu)
+ return (1);
+ if (p1->p_estcpu > p2->p_estcpu)
+ return (0);
+ return (p2->p_pid > p1->p_pid); /* tie - return highest pid */
+ }
+ /*
+ * weed out zombies
+ */
+ switch (TESTAB(p1->p_stat == SZOMB, p2->p_stat == SZOMB)) {
+ case ONLYA:
+ return (1);
+ case ONLYB:
+ return (0);
+ case BOTH:
+ return (p2->p_pid > p1->p_pid); /* tie - return highest pid */
+ }
+ /*
+ * pick the one with the smallest sleep time
+ */
+ if (p2->p_slptime > p1->p_slptime)
+ return (0);
+ if (p1->p_slptime > p2->p_slptime)
+ return (1);
+ /*
+ * favor one sleeping in a non-interruptible sleep
+ */
+ if (p1->p_flag & P_SINTR && (p2->p_flag & P_SINTR) == 0)
+ return (1);
+ if (p2->p_flag & P_SINTR && (p1->p_flag & P_SINTR) == 0)
+ return (0);
+ return (p2->p_pid > p1->p_pid); /* tie - return highest pid */
+}
+
+/*
+ * Output char to tty; console putchar style.
+ */
+int
+tputchar(c, tp)
+ int c;
+ struct tty *tp;
+{
+ register int s;
+
+ s = spltty();
+ if (ISSET(tp->t_state,
+ TS_CARR_ON | TS_ISOPEN) != (TS_CARR_ON | TS_ISOPEN)) {
+ splx(s);
+ return (-1);
+ }
+ if (c == '\n')
+ (void)ttyoutput('\r', tp);
+ (void)ttyoutput(c, tp);
+ ttstart(tp);
+ splx(s);
+ return (0);
+}
+
+/*
+ * Sleep on chan, returning ERESTART if tty changed while we napped and
+ * returning any errors (e.g. EINTR/ETIMEDOUT) reported by tsleep. If
+ * the tty is revoked, restarting a pending call will redo validation done
+ * at the start of the call.
+ */
+int
+ttysleep(tp, chan, pri, wmesg, timo)
+ struct tty *tp;
+ void *chan;
+ int pri, timo;
+ char *wmesg;
+{
+ int error;
+ short gen;
+
+ gen = tp->t_gen;
+ if (error = tsleep(chan, pri, wmesg, timo))
+ return (error);
+ return (tp->t_gen == gen ? 0 : ERESTART);
+}
diff --git a/sys/kern/tty_compat.c b/sys/kern/tty_compat.c
new file mode 100644
index 0000000..7047230
--- /dev/null
+++ b/sys/kern/tty_compat.c
@@ -0,0 +1,418 @@
+/*-
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tty_compat.c 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * mapping routines for old line discipline (yuck)
+ */
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+#include <sys/tty.h>
+#include <sys/termios.h>
+#include <sys/file.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+void ttcompatsetflags __P((struct tty *, struct termios *));
+void ttcompatsetlflags __P((struct tty *, struct termios *));
+
+int ttydebug = 0;
+
+static struct speedtab compatspeeds[] = {
+ { 38400, 15 },
+ { 19200, 14 },
+ { 9600, 13 },
+ { 4800, 12 },
+ { 2400, 11 },
+ { 1800, 10 },
+ { 1200, 9 },
+ { 600, 8 },
+ { 300, 7 },
+ { 200, 6 },
+ { 150, 5 },
+ { 134, 4 },
+ { 110, 3 },
+ { 75, 2 },
+ { 50, 1 },
+ { 0, 0 },
+ { -1, -1 },
+};
+static int compatspcodes[16] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
+ 1800, 2400, 4800, 9600, 19200, 38400,
+};
+
+/*ARGSUSED*/
+int
+ttcompat(tp, com, data, flag)
+ register struct tty *tp;
+ int com;
+ caddr_t data;
+ int flag;
+{
+
+ switch (com) {
+ case TIOCGETP: {
+ register struct sgttyb *sg = (struct sgttyb *)data;
+ register u_char *cc = tp->t_cc;
+ register speed;
+
+ speed = ttspeedtab(tp->t_ospeed, compatspeeds);
+ sg->sg_ospeed = (speed == -1) ? 15 : speed;
+ if (tp->t_ispeed == 0)
+ sg->sg_ispeed = sg->sg_ospeed;
+ else {
+ speed = ttspeedtab(tp->t_ispeed, compatspeeds);
+ sg->sg_ispeed = (speed == -1) ? 15 : speed;
+ }
+ sg->sg_erase = cc[VERASE];
+ sg->sg_kill = cc[VKILL];
+ sg->sg_flags = ttcompatgetflags(tp);
+ break;
+ }
+
+ case TIOCSETP:
+ case TIOCSETN: {
+ register struct sgttyb *sg = (struct sgttyb *)data;
+ struct termios term;
+ int speed;
+
+ term = tp->t_termios;
+ if ((speed = sg->sg_ispeed) > 15 || speed < 0)
+ term.c_ispeed = speed;
+ else
+ term.c_ispeed = compatspcodes[speed];
+ if ((speed = sg->sg_ospeed) > 15 || speed < 0)
+ term.c_ospeed = speed;
+ else
+ term.c_ospeed = compatspcodes[speed];
+ term.c_cc[VERASE] = sg->sg_erase;
+ term.c_cc[VKILL] = sg->sg_kill;
+ tp->t_flags = tp->t_flags&0xffff0000 | sg->sg_flags&0xffff;
+ ttcompatsetflags(tp, &term);
+ return (ttioctl(tp, com == TIOCSETP ? TIOCSETAF : TIOCSETA,
+ &term, flag));
+ }
+
+ case TIOCGETC: {
+ struct tchars *tc = (struct tchars *)data;
+ register u_char *cc = tp->t_cc;
+
+ tc->t_intrc = cc[VINTR];
+ tc->t_quitc = cc[VQUIT];
+ tc->t_startc = cc[VSTART];
+ tc->t_stopc = cc[VSTOP];
+ tc->t_eofc = cc[VEOF];
+ tc->t_brkc = cc[VEOL];
+ break;
+ }
+ case TIOCSETC: {
+ struct tchars *tc = (struct tchars *)data;
+ register u_char *cc = tp->t_cc;
+
+ cc[VINTR] = tc->t_intrc;
+ cc[VQUIT] = tc->t_quitc;
+ cc[VSTART] = tc->t_startc;
+ cc[VSTOP] = tc->t_stopc;
+ cc[VEOF] = tc->t_eofc;
+ cc[VEOL] = tc->t_brkc;
+ if (tc->t_brkc == -1)
+ cc[VEOL2] = _POSIX_VDISABLE;
+ break;
+ }
+ case TIOCSLTC: {
+ struct ltchars *ltc = (struct ltchars *)data;
+ register u_char *cc = tp->t_cc;
+
+ cc[VSUSP] = ltc->t_suspc;
+ cc[VDSUSP] = ltc->t_dsuspc;
+ cc[VREPRINT] = ltc->t_rprntc;
+ cc[VDISCARD] = ltc->t_flushc;
+ cc[VWERASE] = ltc->t_werasc;
+ cc[VLNEXT] = ltc->t_lnextc;
+ break;
+ }
+ case TIOCGLTC: {
+ struct ltchars *ltc = (struct ltchars *)data;
+ register u_char *cc = tp->t_cc;
+
+ ltc->t_suspc = cc[VSUSP];
+ ltc->t_dsuspc = cc[VDSUSP];
+ ltc->t_rprntc = cc[VREPRINT];
+ ltc->t_flushc = cc[VDISCARD];
+ ltc->t_werasc = cc[VWERASE];
+ ltc->t_lnextc = cc[VLNEXT];
+ break;
+ }
+ case TIOCLBIS:
+ case TIOCLBIC:
+ case TIOCLSET: {
+ struct termios term;
+
+ term = tp->t_termios;
+ if (com == TIOCLSET)
+ tp->t_flags = (tp->t_flags&0xffff) | *(int *)data<<16;
+ else {
+ tp->t_flags =
+ (ttcompatgetflags(tp)&0xffff0000)|(tp->t_flags&0xffff);
+ if (com == TIOCLBIS)
+ tp->t_flags |= *(int *)data<<16;
+ else
+ tp->t_flags &= ~(*(int *)data<<16);
+ }
+ ttcompatsetlflags(tp, &term);
+ return (ttioctl(tp, TIOCSETA, &term, flag));
+ }
+ case TIOCLGET:
+ *(int *)data = ttcompatgetflags(tp)>>16;
+ if (ttydebug)
+ printf("CLGET: returning %x\n", *(int *)data);
+ break;
+
+ case OTIOCGETD:
+ *(int *)data = tp->t_line ? tp->t_line : 2;
+ break;
+
+ case OTIOCSETD: {
+ int ldisczero = 0;
+
+ return (ttioctl(tp, TIOCSETD,
+ *(int *)data == 2 ? (caddr_t)&ldisczero : data, flag));
+ }
+
+ case OTIOCCONS:
+ *(int *)data = 1;
+ return (ttioctl(tp, TIOCCONS, data, flag));
+
+ default:
+ return (-1);
+ }
+ return (0);
+}
+
+int
+ttcompatgetflags(tp)
+ register struct tty *tp;
+{
+ register long iflag = tp->t_iflag;
+ register long lflag = tp->t_lflag;
+ register long oflag = tp->t_oflag;
+ register long cflag = tp->t_cflag;
+ register flags = 0;
+
+ if (iflag&IXOFF)
+ flags |= TANDEM;
+ if (iflag&ICRNL || oflag&ONLCR)
+ flags |= CRMOD;
+ if (cflag&PARENB) {
+ if (iflag&INPCK) {
+ if (cflag&PARODD)
+ flags |= ODDP;
+ else
+ flags |= EVENP;
+ } else
+ flags |= EVENP | ODDP;
+ } else {
+ if ((tp->t_flags&LITOUT) && !(oflag&OPOST))
+ flags |= LITOUT;
+ if (tp->t_flags&PASS8)
+ flags |= PASS8;
+ }
+
+ if ((lflag&ICANON) == 0) {
+ /* fudge */
+ if (iflag&IXON || lflag&ISIG || lflag&IEXTEN || cflag&PARENB)
+ flags |= CBREAK;
+ else
+ flags |= RAW;
+ }
+ if (cflag&MDMBUF)
+ flags |= MDMBUF;
+ if ((cflag&HUPCL) == 0)
+ flags |= NOHANG;
+ if (oflag&OXTABS)
+ flags |= XTABS;
+ if (lflag&ECHOE)
+ flags |= CRTERA|CRTBS;
+ if (lflag&ECHOKE)
+ flags |= CRTKIL|CRTBS;
+ if (lflag&ECHOPRT)
+ flags |= PRTERA;
+ if (lflag&ECHOCTL)
+ flags |= CTLECH;
+ if ((iflag&IXANY) == 0)
+ flags |= DECCTQ;
+ flags |= lflag&(ECHO|TOSTOP|FLUSHO|PENDIN|NOFLSH);
+if (ttydebug)
+ printf("getflags: %x\n", flags);
+ return (flags);
+}
+
+void
+ttcompatsetflags(tp, t)
+ register struct tty *tp;
+ register struct termios *t;
+{
+ register flags = tp->t_flags;
+ register long iflag = t->c_iflag;
+ register long oflag = t->c_oflag;
+ register long lflag = t->c_lflag;
+ register long cflag = t->c_cflag;
+
+ if (flags & RAW) {
+ iflag &= IXOFF;
+ oflag &= ~OPOST;
+ lflag &= ~(ECHOCTL|ISIG|ICANON|IEXTEN);
+ } else {
+ iflag |= BRKINT|IXON|IMAXBEL;
+ oflag |= OPOST;
+ lflag |= ISIG|IEXTEN|ECHOCTL; /* XXX was echoctl on ? */
+ if (flags & XTABS)
+ oflag |= OXTABS;
+ else
+ oflag &= ~OXTABS;
+ if (flags & CBREAK)
+ lflag &= ~ICANON;
+ else
+ lflag |= ICANON;
+ if (flags&CRMOD) {
+ iflag |= ICRNL;
+ oflag |= ONLCR;
+ } else {
+ iflag &= ~ICRNL;
+ oflag &= ~ONLCR;
+ }
+ }
+ if (flags&ECHO)
+ lflag |= ECHO;
+ else
+ lflag &= ~ECHO;
+
+ if (flags&(RAW|LITOUT|PASS8)) {
+ cflag &= ~(CSIZE|PARENB);
+ cflag |= CS8;
+ if ((flags&(RAW|PASS8)) == 0)
+ iflag |= ISTRIP;
+ else
+ iflag &= ~ISTRIP;
+ } else {
+ cflag &= ~CSIZE;
+ cflag |= CS7|PARENB;
+ iflag |= ISTRIP;
+ }
+ if ((flags&(EVENP|ODDP)) == EVENP) {
+ iflag |= INPCK;
+ cflag &= ~PARODD;
+ } else if ((flags&(EVENP|ODDP)) == ODDP) {
+ iflag |= INPCK;
+ cflag |= PARODD;
+ } else
+ iflag &= ~INPCK;
+ if (flags&LITOUT)
+ oflag &= ~OPOST; /* move earlier ? */
+ if (flags&TANDEM)
+ iflag |= IXOFF;
+ else
+ iflag &= ~IXOFF;
+ t->c_iflag = iflag;
+ t->c_oflag = oflag;
+ t->c_lflag = lflag;
+ t->c_cflag = cflag;
+}
+
+void
+ttcompatsetlflags(tp, t)
+ register struct tty *tp;
+ register struct termios *t;
+{
+ register flags = tp->t_flags;
+ register long iflag = t->c_iflag;
+ register long oflag = t->c_oflag;
+ register long lflag = t->c_lflag;
+ register long cflag = t->c_cflag;
+
+ if (flags&CRTERA)
+ lflag |= ECHOE;
+ else
+ lflag &= ~ECHOE;
+ if (flags&CRTKIL)
+ lflag |= ECHOKE;
+ else
+ lflag &= ~ECHOKE;
+ if (flags&PRTERA)
+ lflag |= ECHOPRT;
+ else
+ lflag &= ~ECHOPRT;
+ if (flags&CTLECH)
+ lflag |= ECHOCTL;
+ else
+ lflag &= ~ECHOCTL;
+ if ((flags&DECCTQ) == 0)
+ iflag |= IXANY;
+ else
+ iflag &= ~IXANY;
+ if (flags & MDMBUF)
+ cflag |= MDMBUF;
+ else
+ cflag &= ~MDMBUF;
+ if (flags&NOHANG)
+ cflag &= ~HUPCL;
+ else
+ cflag |= HUPCL;
+ lflag &= ~(TOSTOP|FLUSHO|PENDIN|NOFLSH);
+ lflag |= flags&(TOSTOP|FLUSHO|PENDIN|NOFLSH);
+ if (flags&(LITOUT|PASS8)) {
+ iflag &= ~ISTRIP;
+ cflag &= ~(CSIZE|PARENB);
+ cflag |= CS8;
+ if (flags&LITOUT)
+ oflag &= ~OPOST;
+ if ((flags&(PASS8|RAW)) == 0)
+ iflag |= ISTRIP;
+ } else if ((flags&RAW) == 0) {
+ cflag &= ~CSIZE;
+ cflag |= CS7|PARENB;
+ oflag |= OPOST;
+ }
+ t->c_iflag = iflag;
+ t->c_oflag = oflag;
+ t->c_lflag = lflag;
+ t->c_cflag = cflag;
+}
+#endif /* COMPAT_43 || COMPAT_SUNOS */
diff --git a/sys/kern/tty_conf.c b/sys/kern/tty_conf.c
new file mode 100644
index 0000000..f517a37
--- /dev/null
+++ b/sys/kern/tty_conf.c
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tty_conf.c 8.4 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+#include <sys/tty.h>
+#include <sys/conf.h>
+
+#define ttynodisc ((int (*) __P((dev_t, struct tty *)))enodev)
+#define ttyerrclose ((int (*) __P((struct tty *, int flags)))enodev)
+#define ttyerrio ((int (*) __P((struct tty *, struct uio *, int)))enodev)
+#define ttyerrinput ((int (*) __P((int c, struct tty *)))enodev)
+#define ttyerrstart ((int (*) __P((struct tty *)))enodev)
+
+int nullioctl __P((struct tty *tp, int cmd, caddr_t data,
+ int flag, struct proc *p));
+
+#include "tb.h"
+#if NTB > 0
+int tbopen __P((dev_t dev, struct tty *tp));
+int tbclose __P((struct tty *tp, int flags));
+int tbread __P((struct tty *, struct uio *, int flags));
+int tbioctl __P((struct tty *tp, int cmd, caddr_t data,
+ int flag, struct proc *p));
+int tbinput __P((int c, struct tty *tp));
+#endif
+
+#include "sl.h"
+#if NSL > 0
+int slopen __P((dev_t dev, struct tty *tp));
+int slclose __P((struct tty *tp, int flags));
+int sltioctl __P((struct tty *tp, int cmd, caddr_t data,
+ int flag, struct proc *p));
+int slinput __P((int c, struct tty *tp));
+int slstart __P((struct tty *tp));
+#endif
+
+
+struct linesw linesw[] =
+{
+ { ttyopen, ttylclose, ttread, ttwrite, nullioctl,
+ ttyinput, ttstart, ttymodem }, /* 0- termios */
+
+ { ttynodisc, ttyerrclose, ttyerrio, ttyerrio, nullioctl,
+ ttyerrinput, ttyerrstart, nullmodem }, /* 1- defunct */
+
+#ifdef COMPAT_43
+ { ttyopen, ttylclose, ttread, ttwrite, nullioctl,
+ ttyinput, ttstart, ttymodem }, /* 2- NTTYDISC */
+#else
+ { ttynodisc, ttyerrclose, ttyerrio, ttyerrio, nullioctl,
+ ttyerrinput, ttyerrstart, nullmodem },
+#endif
+
+#if NTB > 0
+ { tbopen, tbclose, tbread, enodev, tbioctl,
+ tbinput, ttstart, nullmodem }, /* 3- TABLDISC */
+#else
+ { ttynodisc, ttyerrclose, ttyerrio, ttyerrio, nullioctl,
+ ttyerrinput, ttyerrstart, nullmodem },
+#endif
+
+#if NSL > 0
+ { slopen, slclose, ttyerrio, ttyerrio, sltioctl,
+ slinput, slstart, nullmodem }, /* 4- SLIPDISC */
+#else
+ { ttynodisc, ttyerrclose, ttyerrio, ttyerrio, nullioctl,
+ ttyerrinput, ttyerrstart, nullmodem },
+#endif
+};
+
+int nlinesw = sizeof (linesw) / sizeof (linesw[0]);
+
+/*
+ * Do nothing specific version of line
+ * discipline specific ioctl command.
+ */
+/*ARGSUSED*/
+int
+nullioctl(tp, cmd, data, flags, p)
+ struct tty *tp;
+ int cmd;
+ char *data;
+ int flags;
+ struct proc *p;
+{
+
+#ifdef lint
+ tp = tp; data = data; flags = flags; p = p;
+#endif
+ return (-1);
+}
diff --git a/sys/kern/tty_cons.c b/sys/kern/tty_cons.c
new file mode 100644
index 0000000..ceb4b39
--- /dev/null
+++ b/sys/kern/tty_cons.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 1988 University of Utah.
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)cons.c 7.2 (Berkeley) 5/9/91
+ * $Id: cons.c,v 1.10 1994/01/23 19:17:17 davidg Exp $
+ */
+
+
+#include "sys/param.h"
+#include <sys/systm.h>
+#include "sys/proc.h"
+#include "sys/user.h"
+#include "sys/buf.h"
+#include "sys/ioctl.h"
+#include "sys/tty.h"
+#include "sys/file.h"
+#include "sys/conf.h"
+#include "sys/vnode.h"
+#include "machine/stdarg.h"
+
+#include "machine/cons.h"
+
+/* XXX - all this could be autoconfig()ed */
+int pccnprobe(), pccninit(), pccngetc(), pccnputc();
+
+#include "sio.h"
+#if NSIO > 0
+int siocnprobe(), siocninit(), siocngetc(), siocnputc();
+#endif
+
+#include "com.h"
+#if NCOM > 0
+int comcnprobe(), comcninit(), comcngetc(), comcnputc();
+#endif
+
+struct consdev constab[] = {
+ { pccnprobe, pccninit, pccngetc, pccnputc },
+#if NSIO > 0
+ { siocnprobe, siocninit, siocngetc, siocnputc },
+#endif
+#if NCOM > 0
+ { comcnprobe, comcninit, comcngetc, comcnputc },
+#endif
+ { 0 },
+};
+/* end XXX */
+
+struct tty *constty = 0; /* virtual console output device */
+struct consdev *cn_tab; /* physical console device info */
+struct tty *cn_tty; /* XXX: console tty struct for tprintf */
+
+void
+cninit()
+{
+ register struct consdev *cp;
+
+ /*
+ * Collect information about all possible consoles
+ * and find the one with highest priority
+ */
+ for (cp = constab; cp->cn_probe; cp++) {
+ (*cp->cn_probe)(cp);
+ if (cp->cn_pri > CN_DEAD &&
+ (cn_tab == NULL || cp->cn_pri > cn_tab->cn_pri))
+ cn_tab = cp;
+ }
+ /*
+ * No console, we can handle it
+ */
+ if ((cp = cn_tab) == NULL)
+ return;
+ /*
+ * Turn on console
+ */
+ cn_tty = cp->cn_tp;
+ (*cp->cn_init)(cp);
+}
+
+int
+cnopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+ struct vnode *vp = 0;
+
+ if (cn_tab == NULL)
+ return (0);
+
+ dev = cn_tab->cn_dev;
+ if (vfinddev(dev, VCHR, &vp) && vcount(vp))
+ return (0);
+
+ return ((*cdevsw[major(dev)].d_open)(dev, flag, mode, p));
+}
+
+int
+cnclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+ struct vnode *vp = 0;
+
+ if (cn_tab == NULL)
+ return (0);
+
+ dev = cn_tab->cn_dev;
+ if (vfinddev(dev, VCHR, &vp) && vcount(vp))
+ return (0);
+
+ return ((*cdevsw[major(dev)].d_close)(dev, flag, mode, p));
+}
+
+int
+cnread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ if (cn_tab == NULL)
+ return (0);
+ dev = cn_tab->cn_dev;
+ return ((*cdevsw[major(dev)].d_read)(dev, uio, flag));
+}
+
+int
+cnwrite(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ if (cn_tab == NULL)
+ return (0);
+ if (constty)
+ dev = constty->t_dev;
+ else
+ dev = cn_tab->cn_dev;
+ return ((*cdevsw[major(dev)].d_write)(dev, uio, flag));
+}
+
+int
+cnioctl(dev, cmd, data, flag, p)
+ dev_t dev;
+ int cmd;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+{
+ int error;
+
+ if (cn_tab == NULL)
+ return (0);
+ /*
+ * Superuser can always use this to wrest control of console
+ * output from the "virtual" console.
+ */
+ if (cmd == TIOCCONS && constty) {
+ error = suser(p->p_ucred, (u_short *) NULL);
+ if (error)
+ return (error);
+ constty = NULL;
+ return (0);
+ }
+ dev = cn_tab->cn_dev;
+ return ((*cdevsw[major(dev)].d_ioctl)(dev, cmd, data, flag, p));
+}
+
+/*ARGSUSED*/
+int
+cnselect(dev, rw, p)
+ dev_t dev;
+ int rw;
+ struct proc *p;
+{
+ if (cn_tab == NULL)
+ return (1);
+ return (ttselect(cn_tab->cn_dev, rw, p));
+}
+
+int
+cngetc()
+{
+ if (cn_tab == NULL)
+ return (0);
+ return ((*cn_tab->cn_getc)(cn_tab->cn_dev));
+}
+
+void
+cnputc(c)
+ register int c;
+{
+ if (cn_tab == NULL)
+ return;
+ if (c) {
+ (*cn_tab->cn_putc)(cn_tab->cn_dev, c);
+ if (c == '\n')
+ (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r');
+ }
+}
+
+int
+pg(const char *p, ...) {
+ va_list args;
+ va_start(args, p);
+ printf("%r\n>", p, args);
+ return(cngetc());
+}
+
+
diff --git a/sys/kern/tty_pty.c b/sys/kern/tty_pty.c
new file mode 100644
index 0000000..50f0581
--- /dev/null
+++ b/sys/kern/tty_pty.c
@@ -0,0 +1,704 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tty_pty.c 8.2 (Berkeley) 9/23/93
+ */
+
+/*
+ * Pseudo-teletype Driver
+ * (Actually two drivers, requiring two entries in 'cdevsw')
+ */
+#include "pty.h" /* XXX */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+#include <sys/tty.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+
+#if NPTY == 1
+#undef NPTY
+#define NPTY 32 /* crude XXX */
+#endif
+
+#define BUFSIZ 100 /* Chunk size iomoved to/from user */
+
+/*
+ * pts == /dev/tty[pqrs]?
+ * ptc == /dev/pty[pqrs]?
+ */
+struct tty pt_tty[NPTY]; /* XXX */
+struct pt_ioctl {
+ int pt_flags;
+ struct selinfo pt_selr, pt_selw;
+ u_char pt_send;
+ u_char pt_ucntl;
+} pt_ioctl[NPTY]; /* XXX */
+int npty = NPTY; /* for pstat -t */
+
+#define PF_PKT 0x08 /* packet mode */
+#define PF_STOPPED 0x10 /* user told stopped */
+#define PF_REMOTE 0x20 /* remote and flow controlled input */
+#define PF_NOSTOP 0x40
+#define PF_UCNTL 0x80 /* user control mode */
+
+void ptsstop __P((struct tty *, int));
+void ptcwakeup __P((struct tty *, int));
+
+/*
+ * Establish n (or default if n is 1) ptys in the system.
+ *
+ * XXX cdevsw & pstat require the array `pty[]' to be an array
+ */
+void
+ptyattach(n)
+ int n;
+{
+#ifdef notyet
+ char *mem;
+ register u_long ntb;
+#define DEFAULT_NPTY 32
+
+ /* maybe should allow 0 => none? */
+ if (n <= 1)
+ n = DEFAULT_NPTY;
+ ntb = n * sizeof(struct tty);
+ mem = malloc(ntb + ALIGNBYTES + n * sizeof(struct pt_ioctl),
+ M_DEVBUF, M_WAITOK);
+ pt_tty = (struct tty *)mem;
+ mem = (char *)ALIGN(mem + ntb);
+ pt_ioctl = (struct pt_ioctl *)mem;
+ npty = n;
+#endif
+}
+
+/*ARGSUSED*/
+int
+ptsopen(dev, flag, devtype, p)
+ dev_t dev;
+ int flag, devtype;
+ struct proc *p;
+{
+ register struct tty *tp;
+ int error;
+
+ if (minor(dev) >= npty)
+ return (ENXIO);
+ tp = &pt_tty[minor(dev)];
+ if ((tp->t_state & TS_ISOPEN) == 0) {
+ tp->t_state |= TS_WOPEN;
+ ttychars(tp); /* Set up default chars */
+ tp->t_iflag = TTYDEF_IFLAG;
+ tp->t_oflag = TTYDEF_OFLAG;
+ tp->t_lflag = TTYDEF_LFLAG;
+ tp->t_cflag = TTYDEF_CFLAG;
+ tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
+ ttsetwater(tp); /* would be done in xxparam() */
+ } else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0)
+ return (EBUSY);
+ if (tp->t_oproc) /* Ctrlr still around. */
+ tp->t_state |= TS_CARR_ON;
+ while ((tp->t_state & TS_CARR_ON) == 0) {
+ tp->t_state |= TS_WOPEN;
+ if (flag&FNONBLOCK)
+ break;
+ if (error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH,
+ ttopen, 0))
+ return (error);
+ }
+ error = (*linesw[tp->t_line].l_open)(dev, tp);
+ ptcwakeup(tp, FREAD|FWRITE);
+ return (error);
+}
+
+int
+ptsclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+ register struct tty *tp;
+ int err;
+
+ tp = &pt_tty[minor(dev)];
+ err = (*linesw[tp->t_line].l_close)(tp, flag);
+ err |= ttyclose(tp);
+ ptcwakeup(tp, FREAD|FWRITE);
+ return (err);
+}
+
+int
+ptsread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ struct proc *p = curproc;
+ register struct tty *tp = &pt_tty[minor(dev)];
+ register struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
+ int error = 0;
+
+again:
+ if (pti->pt_flags & PF_REMOTE) {
+ while (isbackground(p, tp)) {
+ if ((p->p_sigignore & sigmask(SIGTTIN)) ||
+ (p->p_sigmask & sigmask(SIGTTIN)) ||
+ p->p_pgrp->pg_jobc == 0 ||
+ p->p_flag & P_PPWAIT)
+ return (EIO);
+ pgsignal(p->p_pgrp, SIGTTIN, 1);
+ if (error = ttysleep(tp, (caddr_t)&lbolt,
+ TTIPRI | PCATCH, ttybg, 0))
+ return (error);
+ }
+ if (tp->t_canq.c_cc == 0) {
+ if (flag & IO_NDELAY)
+ return (EWOULDBLOCK);
+ if (error = ttysleep(tp, (caddr_t)&tp->t_canq,
+ TTIPRI | PCATCH, ttyin, 0))
+ return (error);
+ goto again;
+ }
+ while (tp->t_canq.c_cc > 1 && uio->uio_resid > 0)
+ if (ureadc(getc(&tp->t_canq), uio) < 0) {
+ error = EFAULT;
+ break;
+ }
+ if (tp->t_canq.c_cc == 1)
+ (void) getc(&tp->t_canq);
+ if (tp->t_canq.c_cc)
+ return (error);
+ } else
+ if (tp->t_oproc)
+ error = (*linesw[tp->t_line].l_read)(tp, uio, flag);
+ ptcwakeup(tp, FWRITE);
+ return (error);
+}
+
+/*
+ * Write to pseudo-tty.
+ * Wakeups of controlling tty will happen
+ * indirectly, when tty driver calls ptsstart.
+ */
+int
+ptswrite(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ register struct tty *tp;
+
+ tp = &pt_tty[minor(dev)];
+ if (tp->t_oproc == 0)
+ return (EIO);
+ return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
+}
+
+/*
+ * Start output on pseudo-tty.
+ * Wake up process selecting or sleeping for input from controlling tty.
+ */
+void
+ptsstart(tp)
+ struct tty *tp;
+{
+ register struct pt_ioctl *pti = &pt_ioctl[minor(tp->t_dev)];
+
+ if (tp->t_state & TS_TTSTOP)
+ return;
+ if (pti->pt_flags & PF_STOPPED) {
+ pti->pt_flags &= ~PF_STOPPED;
+ pti->pt_send = TIOCPKT_START;
+ }
+ ptcwakeup(tp, FREAD);
+}
+
+void
+ptcwakeup(tp, flag)
+ struct tty *tp;
+ int flag;
+{
+ struct pt_ioctl *pti = &pt_ioctl[minor(tp->t_dev)];
+
+ if (flag & FREAD) {
+ selwakeup(&pti->pt_selr);
+ wakeup((caddr_t)&tp->t_outq.c_cf);
+ }
+ if (flag & FWRITE) {
+ selwakeup(&pti->pt_selw);
+ wakeup((caddr_t)&tp->t_rawq.c_cf);
+ }
+}
+
+/*ARGSUSED*/
+#ifdef __STDC__
+int
+ptcopen(dev_t dev, int flag, int devtype, struct proc *p)
+#else
+int
+ptcopen(dev, flag, devtype, p)
+ dev_t dev;
+ int flag, devtype;
+ struct proc *p;
+#endif
+{
+ register struct tty *tp;
+ struct pt_ioctl *pti;
+
+ if (minor(dev) >= npty)
+ return (ENXIO);
+ tp = &pt_tty[minor(dev)];
+ if (tp->t_oproc)
+ return (EIO);
+ tp->t_oproc = ptsstart;
+#ifdef sun4c
+ tp->t_stop = ptsstop;
+#endif
+ (void)(*linesw[tp->t_line].l_modem)(tp, 1);
+ tp->t_lflag &= ~EXTPROC;
+ pti = &pt_ioctl[minor(dev)];
+ pti->pt_flags = 0;
+ pti->pt_send = 0;
+ pti->pt_ucntl = 0;
+ return (0);
+}
+
+int
+ptcclose(dev)
+ dev_t dev;
+{
+ register struct tty *tp;
+
+ tp = &pt_tty[minor(dev)];
+ (void)(*linesw[tp->t_line].l_modem)(tp, 0);
+ tp->t_state &= ~TS_CARR_ON;
+ tp->t_oproc = 0; /* mark closed */
+ tp->t_session = 0;
+ return (0);
+}
+
+int
+ptcread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ register struct tty *tp = &pt_tty[minor(dev)];
+ struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
+ char buf[BUFSIZ];
+ int error = 0, cc;
+
+ /*
+ * We want to block until the slave
+ * is open, and there's something to read;
+ * but if we lost the slave or we're NBIO,
+ * then return the appropriate error instead.
+ */
+ for (;;) {
+ if (tp->t_state&TS_ISOPEN) {
+ if (pti->pt_flags&PF_PKT && pti->pt_send) {
+ error = ureadc((int)pti->pt_send, uio);
+ if (error)
+ return (error);
+ if (pti->pt_send & TIOCPKT_IOCTL) {
+ cc = min(uio->uio_resid,
+ sizeof(tp->t_termios));
+ uiomove(&tp->t_termios, cc, uio);
+ }
+ pti->pt_send = 0;
+ return (0);
+ }
+ if (pti->pt_flags&PF_UCNTL && pti->pt_ucntl) {
+ error = ureadc((int)pti->pt_ucntl, uio);
+ if (error)
+ return (error);
+ pti->pt_ucntl = 0;
+ return (0);
+ }
+ if (tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0)
+ break;
+ }
+ if ((tp->t_state&TS_CARR_ON) == 0)
+ return (0); /* EOF */
+ if (flag & IO_NDELAY)
+ return (EWOULDBLOCK);
+ if (error = tsleep((caddr_t)&tp->t_outq.c_cf, TTIPRI | PCATCH,
+ ttyin, 0))
+ return (error);
+ }
+ if (pti->pt_flags & (PF_PKT|PF_UCNTL))
+ error = ureadc(0, uio);
+ while (uio->uio_resid > 0 && error == 0) {
+ cc = q_to_b(&tp->t_outq, buf, min(uio->uio_resid, BUFSIZ));
+ if (cc <= 0)
+ break;
+ error = uiomove(buf, cc, uio);
+ }
+ if (tp->t_outq.c_cc <= tp->t_lowat) {
+ if (tp->t_state&TS_ASLEEP) {
+ tp->t_state &= ~TS_ASLEEP;
+ wakeup((caddr_t)&tp->t_outq);
+ }
+ selwakeup(&tp->t_wsel);
+ }
+ return (error);
+}
+
+void
+ptsstop(tp, flush)
+ register struct tty *tp;
+ int flush;
+{
+ struct pt_ioctl *pti = &pt_ioctl[minor(tp->t_dev)];
+ int flag;
+
+ /* note: FLUSHREAD and FLUSHWRITE already ok */
+ if (flush == 0) {
+ flush = TIOCPKT_STOP;
+ pti->pt_flags |= PF_STOPPED;
+ } else
+ pti->pt_flags &= ~PF_STOPPED;
+ pti->pt_send |= flush;
+ /* change of perspective */
+ flag = 0;
+ if (flush & FREAD)
+ flag |= FWRITE;
+ if (flush & FWRITE)
+ flag |= FREAD;
+ ptcwakeup(tp, flag);
+}
+
+int
+ptcselect(dev, rw, p)
+ dev_t dev;
+ int rw;
+ struct proc *p;
+{
+ register struct tty *tp = &pt_tty[minor(dev)];
+ struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
+ int s;
+
+ if ((tp->t_state&TS_CARR_ON) == 0)
+ return (1);
+ switch (rw) {
+
+ case FREAD:
+ /*
+ * Need to block timeouts (ttrstart).
+ */
+ s = spltty();
+ if ((tp->t_state&TS_ISOPEN) &&
+ tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0) {
+ splx(s);
+ return (1);
+ }
+ splx(s);
+ /* FALLTHROUGH */
+
+ case 0: /* exceptional */
+ if ((tp->t_state&TS_ISOPEN) &&
+ (pti->pt_flags&PF_PKT && pti->pt_send ||
+ pti->pt_flags&PF_UCNTL && pti->pt_ucntl))
+ return (1);
+ selrecord(p, &pti->pt_selr);
+ break;
+
+
+ case FWRITE:
+ if (tp->t_state&TS_ISOPEN) {
+ if (pti->pt_flags & PF_REMOTE) {
+ if (tp->t_canq.c_cc == 0)
+ return (1);
+ } else {
+ if (tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG-2)
+ return (1);
+ if (tp->t_canq.c_cc == 0 && (tp->t_iflag&ICANON))
+ return (1);
+ }
+ }
+ selrecord(p, &pti->pt_selw);
+ break;
+
+ }
+ return (0);
+}
+
+int
+ptcwrite(dev, uio, flag)
+ dev_t dev;
+ register struct uio *uio;
+ int flag;
+{
+ register struct tty *tp = &pt_tty[minor(dev)];
+ register u_char *cp = 0;
+ register int cc = 0;
+ u_char locbuf[BUFSIZ];
+ int cnt = 0;
+ struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
+ int error = 0;
+
+again:
+ if ((tp->t_state&TS_ISOPEN) == 0)
+ goto block;
+ if (pti->pt_flags & PF_REMOTE) {
+ if (tp->t_canq.c_cc)
+ goto block;
+ while (uio->uio_resid > 0 && tp->t_canq.c_cc < TTYHOG - 1) {
+ if (cc == 0) {
+ cc = min(uio->uio_resid, BUFSIZ);
+ cc = min(cc, TTYHOG - 1 - tp->t_canq.c_cc);
+ cp = locbuf;
+ error = uiomove((caddr_t)cp, cc, uio);
+ if (error)
+ return (error);
+ /* check again for safety */
+ if ((tp->t_state&TS_ISOPEN) == 0)
+ return (EIO);
+ }
+ if (cc)
+ (void) b_to_q((char *)cp, cc, &tp->t_canq);
+ cc = 0;
+ }
+ (void) putc(0, &tp->t_canq);
+ ttwakeup(tp);
+ wakeup((caddr_t)&tp->t_canq);
+ return (0);
+ }
+ while (uio->uio_resid > 0) {
+ if (cc == 0) {
+ cc = min(uio->uio_resid, BUFSIZ);
+ cp = locbuf;
+ error = uiomove((caddr_t)cp, cc, uio);
+ if (error)
+ return (error);
+ /* check again for safety */
+ if ((tp->t_state&TS_ISOPEN) == 0)
+ return (EIO);
+ }
+ while (cc > 0) {
+ if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG - 2 &&
+ (tp->t_canq.c_cc > 0 || !(tp->t_iflag&ICANON))) {
+ wakeup((caddr_t)&tp->t_rawq);
+ goto block;
+ }
+ (*linesw[tp->t_line].l_rint)(*cp++, tp);
+ cnt++;
+ cc--;
+ }
+ cc = 0;
+ }
+ return (0);
+block:
+ /*
+ * Come here to wait for slave to open, for space
+ * in outq, or space in rawq.
+ */
+ if ((tp->t_state&TS_CARR_ON) == 0)
+ return (EIO);
+ if (flag & IO_NDELAY) {
+ /* adjust for data copied in but not written */
+ uio->uio_resid += cc;
+ if (cnt == 0)
+ return (EWOULDBLOCK);
+ return (0);
+ }
+ if (error = tsleep((caddr_t)&tp->t_rawq.c_cf, TTOPRI | PCATCH,
+ ttyout, 0)) {
+ /* adjust for data copied in but not written */
+ uio->uio_resid += cc;
+ return (error);
+ }
+ goto again;
+}
+
+/*ARGSUSED*/
+int
+ptyioctl(dev, cmd, data, flag, p)
+ dev_t dev;
+ int cmd;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+{
+ register struct tty *tp = &pt_tty[minor(dev)];
+ register struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
+ register u_char *cc = tp->t_cc;
+ int stop, error;
+
+ /*
+ * IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG.
+ * ttywflush(tp) will hang if there are characters in the outq.
+ */
+ if (cmd == TIOCEXT) {
+ /*
+ * When the EXTPROC bit is being toggled, we need
+ * to send an TIOCPKT_IOCTL if the packet driver
+ * is turned on.
+ */
+ if (*(int *)data) {
+ if (pti->pt_flags & PF_PKT) {
+ pti->pt_send |= TIOCPKT_IOCTL;
+ ptcwakeup(tp, FREAD);
+ }
+ tp->t_lflag |= EXTPROC;
+ } else {
+ if ((tp->t_state & EXTPROC) &&
+ (pti->pt_flags & PF_PKT)) {
+ pti->pt_send |= TIOCPKT_IOCTL;
+ ptcwakeup(tp, FREAD);
+ }
+ tp->t_lflag &= ~EXTPROC;
+ }
+ return(0);
+ } else
+ if (cdevsw[major(dev)].d_open == ptcopen)
+ switch (cmd) {
+
+ case TIOCGPGRP:
+ /*
+ * We aviod calling ttioctl on the controller since,
+ * in that case, tp must be the controlling terminal.
+ */
+ *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : 0;
+ return (0);
+
+ case TIOCPKT:
+ if (*(int *)data) {
+ if (pti->pt_flags & PF_UCNTL)
+ return (EINVAL);
+ pti->pt_flags |= PF_PKT;
+ } else
+ pti->pt_flags &= ~PF_PKT;
+ return (0);
+
+ case TIOCUCNTL:
+ if (*(int *)data) {
+ if (pti->pt_flags & PF_PKT)
+ return (EINVAL);
+ pti->pt_flags |= PF_UCNTL;
+ } else
+ pti->pt_flags &= ~PF_UCNTL;
+ return (0);
+
+ case TIOCREMOTE:
+ if (*(int *)data)
+ pti->pt_flags |= PF_REMOTE;
+ else
+ pti->pt_flags &= ~PF_REMOTE;
+ ttyflush(tp, FREAD|FWRITE);
+ return (0);
+
+#ifdef COMPAT_43
+ case TIOCSETP:
+ case TIOCSETN:
+#endif
+ case TIOCSETD:
+ case TIOCSETA:
+ case TIOCSETAW:
+ case TIOCSETAF:
+ ndflush(&tp->t_outq, tp->t_outq.c_cc);
+ break;
+
+ case TIOCSIG:
+ if (*(unsigned int *)data >= NSIG)
+ return(EINVAL);
+ if ((tp->t_lflag&NOFLSH) == 0)
+ ttyflush(tp, FREAD|FWRITE);
+ pgsignal(tp->t_pgrp, *(unsigned int *)data, 1);
+ if ((*(unsigned int *)data == SIGINFO) &&
+ ((tp->t_lflag&NOKERNINFO) == 0))
+ ttyinfo(tp);
+ return(0);
+ }
+ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
+ if (error < 0)
+ error = ttioctl(tp, cmd, data, flag);
+ if (error < 0) {
+ if (pti->pt_flags & PF_UCNTL &&
+ (cmd & ~0xff) == UIOCCMD(0)) {
+ if (cmd & 0xff) {
+ pti->pt_ucntl = (u_char)cmd;
+ ptcwakeup(tp, FREAD);
+ }
+ return (0);
+ }
+ error = ENOTTY;
+ }
+ /*
+ * If external processing and packet mode send ioctl packet.
+ */
+ if ((tp->t_lflag&EXTPROC) && (pti->pt_flags & PF_PKT)) {
+ switch(cmd) {
+ case TIOCSETA:
+ case TIOCSETAW:
+ case TIOCSETAF:
+#ifdef COMPAT_43
+ case TIOCSETP:
+ case TIOCSETN:
+#endif
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+ case TIOCSETC:
+ case TIOCSLTC:
+ case TIOCLBIS:
+ case TIOCLBIC:
+ case TIOCLSET:
+#endif
+ pti->pt_send |= TIOCPKT_IOCTL;
+ ptcwakeup(tp, FREAD);
+ default:
+ break;
+ }
+ }
+ stop = (tp->t_iflag & IXON) && CCEQ(cc[VSTOP], CTRL('s'))
+ && CCEQ(cc[VSTART], CTRL('q'));
+ if (pti->pt_flags & PF_NOSTOP) {
+ if (stop) {
+ pti->pt_send &= ~TIOCPKT_NOSTOP;
+ pti->pt_send |= TIOCPKT_DOSTOP;
+ pti->pt_flags &= ~PF_NOSTOP;
+ ptcwakeup(tp, FREAD);
+ }
+ } else {
+ if (!stop) {
+ pti->pt_send &= ~TIOCPKT_DOSTOP;
+ pti->pt_send |= TIOCPKT_NOSTOP;
+ pti->pt_flags |= PF_NOSTOP;
+ ptcwakeup(tp, FREAD);
+ }
+ }
+ return (error);
+}
diff --git a/sys/kern/tty_subr.c b/sys/kern/tty_subr.c
new file mode 100644
index 0000000..d0b1496
--- /dev/null
+++ b/sys/kern/tty_subr.c
@@ -0,0 +1,567 @@
+/*
+ * Copyright (C) 1994, David Greenman. This software may be used, modified,
+ * copied, distributed, and sold, in both source and binary form provided
+ * that the above copyright and these terms are retained. Under no
+ * circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/clist.h>
+#include <sys/malloc.h>
+
+struct cblock *cfreelist = 0;
+int cfreecount = 0;
+
+#ifndef INITIAL_CBLOCKS
+#define INITIAL_CBLOCKS 50
+#endif
+
+void cblock_alloc_cblocks __P((int));
+
+#define MBUF_DIAG
+#ifdef MBUF_DIAG
+void
+print_nblocks()
+{
+ printf("There are currently %d bytes in cblocks\n", cfreecount);
+}
+#endif
+
+/*
+ * Called from init_main.c
+ */
+void
+clist_init()
+{
+ /*
+ * Allocate an initial base set of cblocks as a 'slush'.
+ * We allocate more with each ttyopen().
+ */
+ cblock_alloc_cblocks(INITIAL_CBLOCKS);
+ return;
+}
+
+/*
+ * Remove a cblock from the cfreelist queue and return a pointer
+ * to it.
+ */
+static inline struct cblock *
+cblock_alloc()
+{
+ struct cblock *cblockp;
+
+ cblockp = cfreelist;
+ if (!cblockp) {
+ /* XXX should syslog a message that we're out! */
+ return (0);
+ }
+ cfreelist = cblockp->c_next;
+ cblockp->c_next = NULL;
+ cfreecount -= CBSIZE;
+ return (cblockp);
+}
+
+/*
+ * Add a cblock to the cfreelist queue.
+ */
+static inline void
+cblock_free(cblockp)
+ struct cblock *cblockp;
+{
+ cblockp->c_next = cfreelist;
+ cfreelist = cblockp;
+ cfreecount += CBSIZE;
+ return;
+}
+
+/*
+ * Allocate some cblocks for the cfreelist queue.
+ */
+void
+cblock_alloc_cblocks(number)
+ int number;
+{
+ int i;
+ struct cblock *tmp;
+
+ for (i = 0; i < number; ++i) {
+ tmp = malloc(sizeof(struct cblock), M_TTYS, M_NOWAIT);
+ if (!tmp) {
+ printf("cblock_alloc_cblocks: could not malloc cblock");
+ break;
+ }
+ bzero((char *)tmp, sizeof(struct cblock));
+ cblock_free(tmp);
+ }
+ return;
+}
+
+/*
+ * Free some cblocks from the cfreelist queue back to the
+ * system malloc pool.
+ */
+void
+cblock_free_cblocks(number)
+ int number;
+{
+ int i;
+ struct cblock *tmp;
+
+ for (i = 0; i < number; ++i) {
+ tmp = cblock_alloc();
+ free(tmp, M_TTYS);
+ }
+}
+
+
+/*
+ * Get a character from the head of a clist.
+ */
+int
+getc(clistp)
+ struct clist *clistp;
+{
+ int chr = -1;
+ int s;
+ struct cblock *cblockp;
+
+ s = spltty();
+
+ /* If there are characters in the list, get one */
+ if (clistp->c_cc) {
+ cblockp = (struct cblock *)((long)clistp->c_cf & ~CROUND);
+ chr = (u_char)*clistp->c_cf;
+
+ /*
+ * If this char is quoted, set the flag.
+ */
+ if (isset(cblockp->c_quote, clistp->c_cf - (char *)cblockp->c_info))
+ chr |= TTY_QUOTE;
+
+ /*
+ * Advance to next character.
+ */
+ clistp->c_cf++;
+ clistp->c_cc--;
+ /*
+ * If we have advanced the 'first' character pointer
+ * past the end of this cblock, advance to the next one.
+ * If there are no more characters, set the first and
+ * last pointers to NULL. In either case, free the
+ * current cblock.
+ */
+ if ((clistp->c_cf >= (char *)(cblockp+1)) || (clistp->c_cc == 0)) {
+ if (clistp->c_cc > 0) {
+ clistp->c_cf = cblockp->c_next->c_info;
+ } else {
+ clistp->c_cf = clistp->c_cl = NULL;
+ }
+ cblock_free(cblockp);
+ }
+ }
+
+ splx(s);
+ return (chr);
+}
+
+/*
+ * Copy 'amount' of chars, beginning at head of clist 'clistp' to
+ * destination linear buffer 'dest'. Return number of characters
+ * actually copied.
+ */
+int
+q_to_b(clistp, dest, amount)
+ struct clist *clistp;
+ char *dest;
+ int amount;
+{
+ struct cblock *cblockp;
+ struct cblock *cblockn;
+ char *dest_orig = dest;
+ int numc;
+ int s;
+
+ s = spltty();
+
+ while (clistp && amount && (clistp->c_cc > 0)) {
+ cblockp = (struct cblock *)((long)clistp->c_cf & ~CROUND);
+ cblockn = cblockp + 1; /* pointer arithmetic! */
+ numc = min(amount, (char *)cblockn - clistp->c_cf);
+ numc = min(numc, clistp->c_cc);
+ bcopy(clistp->c_cf, dest, numc);
+ amount -= numc;
+ clistp->c_cf += numc;
+ clistp->c_cc -= numc;
+ dest += numc;
+ /*
+ * If this cblock has been emptied, advance to the next
+ * one. If there are no more characters, set the first
+ * and last pointer to NULL. In either case, free the
+ * current cblock.
+ */
+ if ((clistp->c_cf >= (char *)cblockn) || (clistp->c_cc == 0)) {
+ if (clistp->c_cc > 0) {
+ clistp->c_cf = cblockp->c_next->c_info;
+ } else {
+ clistp->c_cf = clistp->c_cl = NULL;
+ }
+ cblock_free(cblockp);
+ }
+ }
+
+ splx(s);
+ return (dest - dest_orig);
+}
+
+/*
+ * Flush 'amount' of chars, beginning at head of clist 'clistp'.
+ */
+void
+ndflush(clistp, amount)
+ struct clist *clistp;
+ int amount;
+{
+ struct cblock *cblockp;
+ struct cblock *cblockn;
+ int numc;
+ int s;
+
+ s = spltty();
+
+ while (amount && (clistp->c_cc > 0)) {
+ cblockp = (struct cblock *)((long)clistp->c_cf & ~CROUND);
+ cblockn = cblockp + 1; /* pointer arithmetic! */
+ numc = min(amount, (char *)cblockn - clistp->c_cf);
+ numc = min(numc, clistp->c_cc);
+ amount -= numc;
+ clistp->c_cf += numc;
+ clistp->c_cc -= numc;
+ /*
+ * If this cblock has been emptied, advance to the next
+ * one. If there are no more characters, set the first
+ * and last pointer to NULL. In either case, free the
+ * current cblock.
+ */
+ if ((clistp->c_cf >= (char *)cblockn) || (clistp->c_cc == 0)) {
+ if (clistp->c_cc > 0) {
+ clistp->c_cf = cblockp->c_next->c_info;
+ } else {
+ clistp->c_cf = clistp->c_cl = NULL;
+ }
+ cblock_free(cblockp);
+ }
+ }
+
+ splx(s);
+ return;
+}
+
+/*
+ * Add a character to the end of a clist. Return -1 is no
+ * more clists, or 0 for success.
+ */
+int
+putc(chr, clistp)
+ int chr;
+ struct clist *clistp;
+{
+ struct cblock *cblockp, *bclockn;
+ int s;
+
+ s = spltty();
+
+ cblockp = (struct cblock *)((long)clistp->c_cl & ~CROUND);
+
+ if (clistp->c_cl == NULL) {
+ cblockp = cblock_alloc();
+ if (cblockp) {
+ clistp->c_cf = clistp->c_cl = cblockp->c_info;
+ clistp->c_cc = 0;
+ } else {
+ splx(s);
+ return (-1);
+ }
+ } else {
+ if (((long)clistp->c_cl & CROUND) == 0) {
+ struct cblock *prev = (cblockp - 1);
+ cblockp = cblock_alloc();
+ if (cblockp) {
+ prev->c_next = cblockp;
+ clistp->c_cl = cblockp->c_info;
+ } else {
+ splx(s);
+ return (-1);
+ }
+ }
+ }
+
+ /*
+ * If this character is quoted, set the quote bit, if not, clear it.
+ */
+ if (chr & TTY_QUOTE)
+ setbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info);
+ else
+ clrbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info);
+
+ *clistp->c_cl++ = chr;
+ clistp->c_cc++;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Copy data from linear buffer to clist chain. Return the
+ * number of characters not copied.
+ */
+int
+b_to_q(src, amount, clistp)
+ char *src;
+ int amount;
+ struct clist *clistp;
+{
+ struct cblock *cblockp, *bclockn;
+ char *firstbyte, *lastbyte;
+ u_char startmask, endmask;
+ int startbit, endbit, num_between, numc;
+ int s;
+
+ s = spltty();
+
+ /*
+ * If there are no cblocks assigned to this clist yet,
+ * then get one.
+ */
+ if (clistp->c_cl == NULL) {
+ cblockp = cblock_alloc();
+ if (cblockp) {
+ clistp->c_cf = clistp->c_cl = cblockp->c_info;
+ clistp->c_cc = 0;
+ } else {
+ splx(s);
+ return (amount);
+ }
+ } else {
+ cblockp = (struct cblock *)((long)clistp->c_cl & ~CROUND);
+ }
+
+ while (amount) {
+ /*
+ * Get another cblock if needed.
+ */
+ if (((long)clistp->c_cl & CROUND) == 0) {
+ struct cblock *prev = cblockp - 1;
+ cblockp = cblock_alloc();
+ if (cblockp) {
+ prev->c_next = cblockp;
+ clistp->c_cl = cblockp->c_info;
+ } else {
+ splx(s);
+ return (amount);
+ }
+ }
+
+ /*
+ * Copy a chunk of the linear buffer up to the end
+ * of this cblock.
+ */
+ numc = min(amount, (char *)(cblockp + 1) - clistp->c_cl);
+ bcopy(src, clistp->c_cl, numc);
+
+ /*
+ * Clear quote bits. The following could probably be made into
+ * a seperate "bitzero()" routine, but why bother?
+ */
+ startbit = clistp->c_cl - (char *)cblockp->c_info;
+ endbit = startbit + numc - 1;
+
+ firstbyte = (u_char *)cblockp->c_quote + (startbit / NBBY);
+ lastbyte = (u_char *)cblockp->c_quote + (endbit / NBBY);
+
+ /*
+ * Calculate mask of bits to preserve in first and
+ * last bytes.
+ */
+ startmask = NBBY - (startbit % NBBY);
+ startmask = 0xff >> startmask;
+ endmask = (endbit % NBBY);
+ endmask = 0xff << (endmask + 1);
+
+ if (firstbyte != lastbyte) {
+ *firstbyte &= startmask;
+ *lastbyte &= endmask;
+
+ num_between = lastbyte - firstbyte - 1;
+ if (num_between)
+ bzero(firstbyte + 1, num_between);
+ } else {
+ *firstbyte &= (startmask | endmask);
+ }
+
+ /*
+ * ...and update pointer for the next chunk.
+ */
+ src += numc;
+ clistp->c_cl += numc;
+ clistp->c_cc += numc;
+ amount -= numc;
+ /*
+ * If we go through the loop again, it's always
+ * for data in the next cblock, so by adding one (cblock),
+ * (which makes the pointer 1 beyond the end of this
+ * cblock) we prepare for the assignment of 'prev'
+ * above.
+ */
+ cblockp += 1;
+
+ }
+
+ splx(s);
+ return (amount);
+}
+
+/*
+ * Get the next character in the clist. Store it at dst. Don't
+ * advance any clist pointers, but return a pointer to the next
+ * character position.
+ */
+char *
+nextc(clistp, cp, dst)
+ struct clist *clistp;
+ char *cp;
+ int *dst;
+{
+ struct cblock *cblockp;
+
+ ++cp;
+ /*
+ * See if the next character is beyond the end of
+ * the clist.
+ */
+ if (clistp->c_cc && (cp != clistp->c_cl)) {
+ /*
+ * If the next character is beyond the end of this
+ * cblock, advance to the next cblock.
+ */
+ if (((long)cp & CROUND) == 0)
+ cp = ((struct cblock *)cp - 1)->c_next->c_info;
+ cblockp = (struct cblock *)((long)cp & ~CROUND);
+
+ /*
+ * Get the character. Set the quote flag if this character
+ * is quoted.
+ */
+ *dst = (u_char)*cp | (isset(cblockp->c_quote, cp - (char *)cblockp->c_info) ? TTY_QUOTE : 0);
+
+ return (cp);
+ }
+
+ return (NULL);
+}
+
+/*
+ * "Unput" a character from a clist.
+ */
+int
+unputc(clistp)
+ struct clist *clistp;
+{
+ struct cblock *cblockp = 0, *cbp = 0;
+ int s;
+ int chr = -1;
+
+
+ s = spltty();
+
+ if (clistp->c_cc) {
+ --clistp->c_cc;
+ --clistp->c_cl;
+
+ chr = (u_char)*clistp->c_cl;
+
+ cblockp = (struct cblock *)((long)clistp->c_cl & ~CROUND);
+
+ /*
+ * Set quote flag if this character was quoted.
+ */
+ if (isset(cblockp->c_quote, (u_char *)clistp->c_cl - cblockp->c_info))
+ chr |= TTY_QUOTE;
+
+ /*
+ * If all of the characters have been unput in this
+ * cblock, then find the previous one and free this
+ * one.
+ */
+ if (clistp->c_cc && (clistp->c_cl <= (char *)cblockp->c_info)) {
+ cbp = (struct cblock *)((long)clistp->c_cf & ~CROUND);
+
+ while (cbp->c_next != cblockp)
+ cbp = cbp->c_next;
+
+ /*
+ * When the previous cblock is at the end, the 'last'
+ * pointer always points (invalidly) one past.
+ */
+ clistp->c_cl = (char *)(cbp+1);
+ cblock_free(cblockp);
+ cbp->c_next = NULL;
+ }
+ }
+
+ /*
+ * If there are no more characters on the list, then
+ * free the last cblock.
+ */
+ if ((clistp->c_cc == 0) && clistp->c_cl) {
+ cblockp = (struct cblock *)((long)clistp->c_cl & ~CROUND);
+ cblock_free(cblockp);
+ clistp->c_cf = clistp->c_cl = NULL;
+ }
+
+ splx(s);
+ return (chr);
+}
+
+/*
+ * Move characters in source clist to destination clist,
+ * preserving quote bits.
+ */
+void
+catq(src_clistp, dest_clistp)
+ struct clist *src_clistp, *dest_clistp;
+{
+ int chr, s;
+
+ s = spltty();
+ /*
+ * If the destination clist is empty (has no cblocks atttached),
+ * then we simply assign the current clist to the destination.
+ */
+ if (!dest_clistp->c_cf) {
+ dest_clistp->c_cf = src_clistp->c_cf;
+ dest_clistp->c_cl = src_clistp->c_cl;
+ src_clistp->c_cf = src_clistp->c_cl = NULL;
+
+ dest_clistp->c_cc = src_clistp->c_cc;
+ src_clistp->c_cc = 0;
+
+ splx(s);
+ return;
+ }
+ splx(s);
+
+ /*
+ * XXX This should probably be optimized to more than one
+ * character at a time.
+ */
+ while ((chr = getc(src_clistp)) != -1)
+ putc(chr, dest_clistp);
+
+ return;
+}
diff --git a/sys/kern/tty_tb.c b/sys/kern/tty_tb.c
new file mode 100644
index 0000000..242301a
--- /dev/null
+++ b/sys/kern/tty_tb.c
@@ -0,0 +1,366 @@
+/*-
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tty_tb.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include "tb.h"
+#if NTB > 0
+
+/*
+ * Line discipline for RS232 tablets;
+ * supplies binary coordinate data.
+ */
+#include <sys/param.h>
+#include <sys/tablet.h>
+#include <sys/tty.h>
+
+/*
+ * Tablet configuration table.
+ */
+struct tbconf {
+ short tbc_recsize; /* input record size in bytes */
+ short tbc_uiosize; /* size of data record returned user */
+ int tbc_sync; /* mask for finding sync byte/bit */
+ int (*tbc_decode)();/* decoding routine */
+ char *tbc_run; /* enter run mode sequence */
+ char *tbc_point; /* enter point mode sequence */
+ char *tbc_stop; /* stop sequence */
+ char *tbc_start; /* start/restart sequence */
+ int tbc_flags;
+#define TBF_POL 0x1 /* polhemus hack */
+#define TBF_INPROX 0x2 /* tablet has proximity info */
+};
+
+static int tbdecode(), gtcodecode(), poldecode();
+static int tblresdecode(), tbhresdecode();
+
+struct tbconf tbconf[TBTYPE] = {
+{ 0 },
+{ 5, sizeof (struct tbpos), 0200, tbdecode, "6", "4" },
+{ 5, sizeof (struct tbpos), 0200, tbdecode, "\1CN", "\1RT", "\2", "\4" },
+{ 8, sizeof (struct gtcopos), 0200, gtcodecode },
+{17, sizeof (struct polpos), 0200, poldecode, 0, 0, "\21", "\5\22\2\23",
+ TBF_POL },
+{ 5, sizeof (struct tbpos), 0100, tblresdecode, "\1CN", "\1PT", "\2", "\4",
+ TBF_INPROX },
+{ 6, sizeof (struct tbpos), 0200, tbhresdecode, "\1CN", "\1PT", "\2", "\4",
+ TBF_INPROX },
+{ 5, sizeof (struct tbpos), 0100, tblresdecode, "\1CL\33", "\1PT\33", 0, 0},
+{ 6, sizeof (struct tbpos), 0200, tbhresdecode, "\1CL\33", "\1PT\33", 0, 0},
+};
+
+/*
+ * Tablet state
+ */
+struct tb {
+ int tbflags; /* mode & type bits */
+#define TBMAXREC 17 /* max input record size */
+ char cbuf[TBMAXREC]; /* input buffer */
+ union {
+ struct tbpos tbpos;
+ struct gtcopos gtcopos;
+ struct polpos polpos;
+ } rets; /* processed state */
+#define NTBS 16
+} tb[NTBS];
+
+/*
+ * Open as tablet discipline; called on discipline change.
+ */
+/*ARGSUSED*/
+tbopen(dev, tp)
+ dev_t dev;
+ register struct tty *tp;
+{
+ register struct tb *tbp;
+
+ if (tp->t_line == TABLDISC)
+ return (ENODEV);
+ ttywflush(tp);
+ for (tbp = tb; tbp < &tb[NTBS]; tbp++)
+ if (tbp->tbflags == 0)
+ break;
+ if (tbp >= &tb[NTBS])
+ return (EBUSY);
+ tbp->tbflags = TBTIGER|TBPOINT; /* default */
+ tp->t_cp = tbp->cbuf;
+ tp->t_inbuf = 0;
+ bzero((caddr_t)&tbp->rets, sizeof (tbp->rets));
+ tp->T_LINEP = (caddr_t)tbp;
+ tp->t_flags |= LITOUT;
+ return (0);
+}
+
+/*
+ * Line discipline change or last device close.
+ */
+tbclose(tp)
+ register struct tty *tp;
+{
+ register int s;
+ int modebits = TBPOINT|TBSTOP;
+
+ tbioctl(tp, BIOSMODE, &modebits, 0);
+ s = spltty();
+ ((struct tb *)tp->T_LINEP)->tbflags = 0;
+ tp->t_cp = 0;
+ tp->t_inbuf = 0;
+ tp->t_rawq.c_cc = 0; /* clear queues -- paranoid */
+ tp->t_canq.c_cc = 0;
+ tp->t_line = 0; /* paranoid: avoid races */
+ splx(s);
+}
+
+/*
+ * Read from a tablet line.
+ * Characters have been buffered in a buffer and decoded.
+ */
+tbread(tp, uio)
+ register struct tty *tp;
+ struct uio *uio;
+{
+ register struct tb *tbp = (struct tb *)tp->T_LINEP;
+ register struct tbconf *tc = &tbconf[tbp->tbflags & TBTYPE];
+ int ret;
+
+ if ((tp->t_state&TS_CARR_ON) == 0)
+ return (EIO);
+ ret = uiomove(&tbp->rets, tc->tbc_uiosize, uio);
+ if (tc->tbc_flags&TBF_POL)
+ tbp->rets.polpos.p_key = ' ';
+ return (ret);
+}
+
+/*
+ * Low level character input routine.
+ * Stuff the character in the buffer, and decode
+ * if all the chars are there.
+ *
+ * This routine could be expanded in-line in the receiver
+ * interrupt routine to make it run as fast as possible.
+ */
+tbinput(c, tp)
+ register int c;
+ register struct tty *tp;
+{
+ register struct tb *tbp = (struct tb *)tp->T_LINEP;
+ register struct tbconf *tc = &tbconf[tbp->tbflags & TBTYPE];
+
+ if (tc->tbc_recsize == 0 || tc->tbc_decode == 0) /* paranoid? */
+ return;
+ /*
+ * Locate sync bit/byte or reset input buffer.
+ */
+ if (c&tc->tbc_sync || tp->t_inbuf == tc->tbc_recsize) {
+ tp->t_cp = tbp->cbuf;
+ tp->t_inbuf = 0;
+ }
+ *tp->t_cp++ = c&0177;
+ /*
+ * Call decode routine only if a full record has been collected.
+ */
+ if (++tp->t_inbuf == tc->tbc_recsize)
+ (*tc->tbc_decode)(tc, tbp->cbuf, &tbp->rets);
+}
+
+/*
+ * Decode GTCO 8 byte format (high res, tilt, and pressure).
+ */
+static
+gtcodecode(tc, cp, tbpos)
+ struct tbconf *tc;
+ register char *cp;
+ register struct gtcopos *tbpos;
+{
+
+ tbpos->pressure = *cp >> 2;
+ tbpos->status = (tbpos->pressure > 16) | TBINPROX; /* half way down */
+ tbpos->xpos = (*cp++ & 03) << 14;
+ tbpos->xpos |= *cp++ << 7;
+ tbpos->xpos |= *cp++;
+ tbpos->ypos = (*cp++ & 03) << 14;
+ tbpos->ypos |= *cp++ << 7;
+ tbpos->ypos |= *cp++;
+ tbpos->xtilt = *cp++;
+ tbpos->ytilt = *cp++;
+ tbpos->scount++;
+}
+
+/*
+ * Decode old Hitachi 5 byte format (low res).
+ */
+static
+tbdecode(tc, cp, tbpos)
+ struct tbconf *tc;
+ register char *cp;
+ register struct tbpos *tbpos;
+{
+ register char byte;
+
+ byte = *cp++;
+ tbpos->status = (byte&0100) ? TBINPROX : 0;
+ byte &= ~0100;
+ if (byte > 036)
+ tbpos->status |= 1 << ((byte-040)/2);
+ tbpos->xpos = *cp++ << 7;
+ tbpos->xpos |= *cp++;
+ if (tbpos->xpos < 256) /* tablet wraps around at 256 */
+ tbpos->status &= ~TBINPROX; /* make it out of proximity */
+ tbpos->ypos = *cp++ << 7;
+ tbpos->ypos |= *cp++;
+ tbpos->scount++;
+}
+
+/*
+ * Decode new Hitach 5-byte format (low res).
+ */
+static
+tblresdecode(tc, cp, tbpos)
+ struct tbconf *tc;
+ register char *cp;
+ register struct tbpos *tbpos;
+{
+
+ *cp &= ~0100; /* mask sync bit */
+ tbpos->status = (*cp++ >> 2) | TBINPROX;
+ if (tc->tbc_flags&TBF_INPROX && tbpos->status&020)
+ tbpos->status &= ~(020|TBINPROX);
+ tbpos->xpos = *cp++;
+ tbpos->xpos |= *cp++ << 6;
+ tbpos->ypos = *cp++;
+ tbpos->ypos |= *cp++ << 6;
+ tbpos->scount++;
+}
+
+/*
+ * Decode new Hitach 6-byte format (high res).
+ */
+static
+tbhresdecode(tc, cp, tbpos)
+ struct tbconf *tc;
+ register char *cp;
+ register struct tbpos *tbpos;
+{
+ char byte;
+
+ byte = *cp++;
+ tbpos->xpos = (byte & 03) << 14;
+ tbpos->xpos |= *cp++ << 7;
+ tbpos->xpos |= *cp++;
+ tbpos->ypos = *cp++ << 14;
+ tbpos->ypos |= *cp++ << 7;
+ tbpos->ypos |= *cp++;
+ tbpos->status = (byte >> 2) | TBINPROX;
+ if (tc->tbc_flags&TBF_INPROX && tbpos->status&020)
+ tbpos->status &= ~(020|TBINPROX);
+ tbpos->scount++;
+}
+
+/*
+ * Polhemus decode.
+ */
+static
+poldecode(tc, cp, polpos)
+ struct tbconf *tc;
+ register char *cp;
+ register struct polpos *polpos;
+{
+
+ polpos->p_x = cp[4] | cp[3]<<7 | (cp[9] & 0x03) << 14;
+ polpos->p_y = cp[6] | cp[5]<<7 | (cp[9] & 0x0c) << 12;
+ polpos->p_z = cp[8] | cp[7]<<7 | (cp[9] & 0x30) << 10;
+ polpos->p_azi = cp[11] | cp[10]<<7 | (cp[16] & 0x03) << 14;
+ polpos->p_pit = cp[13] | cp[12]<<7 | (cp[16] & 0x0c) << 12;
+ polpos->p_rol = cp[15] | cp[14]<<7 | (cp[16] & 0x30) << 10;
+ polpos->p_stat = cp[1] | cp[0]<<7;
+ if (cp[2] != ' ')
+ polpos->p_key = cp[2];
+}
+
+/*ARGSUSED*/
+tbioctl(tp, cmd, data, flag)
+ struct tty *tp;
+ caddr_t data;
+{
+ register struct tb *tbp = (struct tb *)tp->T_LINEP;
+
+ switch (cmd) {
+
+ case BIOGMODE:
+ *(int *)data = tbp->tbflags & TBMODE;
+ break;
+
+ case BIOSTYPE:
+ if (tbconf[*(int *)data & TBTYPE].tbc_recsize == 0 ||
+ tbconf[*(int *)data & TBTYPE].tbc_decode == 0)
+ return (EINVAL);
+ tbp->tbflags &= ~TBTYPE;
+ tbp->tbflags |= *(int *)data & TBTYPE;
+ /* fall thru... to set mode bits */
+
+ case BIOSMODE: {
+ register struct tbconf *tc;
+
+ tbp->tbflags &= ~TBMODE;
+ tbp->tbflags |= *(int *)data & TBMODE;
+ tc = &tbconf[tbp->tbflags & TBTYPE];
+ if (tbp->tbflags&TBSTOP) {
+ if (tc->tbc_stop)
+ ttyout(tc->tbc_stop, tp);
+ } else if (tc->tbc_start)
+ ttyout(tc->tbc_start, tp);
+ if (tbp->tbflags&TBPOINT) {
+ if (tc->tbc_point)
+ ttyout(tc->tbc_point, tp);
+ } else if (tc->tbc_run)
+ ttyout(tc->tbc_run, tp);
+ ttstart(tp);
+ break;
+ }
+
+ case BIOGTYPE:
+ *(int *)data = tbp->tbflags & TBTYPE;
+ break;
+
+ case TIOCSETD:
+ case TIOCGETD:
+ case TIOCGETP:
+ case TIOCGETC:
+ return (-1); /* pass thru... */
+
+ default:
+ return (ENOTTY);
+ }
+ return (0);
+}
+#endif
diff --git a/sys/kern/tty_tty.c b/sys/kern/tty_tty.c
new file mode 100644
index 0000000..6baba99
--- /dev/null
+++ b/sys/kern/tty_tty.c
@@ -0,0 +1,152 @@
+/*-
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tty_tty.c 8.2 (Berkeley) 9/23/93
+ */
+
+/*
+ * Indirect driver for controlling tty.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+#include <sys/tty.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+
+#define cttyvp(p) ((p)->p_flag & P_CONTROLT ? (p)->p_session->s_ttyvp : NULL)
+
+/*ARGSUSED*/
+int
+cttyopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+ struct vnode *ttyvp = cttyvp(p);
+ int error;
+
+ if (ttyvp == NULL)
+ return (ENXIO);
+ VOP_LOCK(ttyvp);
+#ifdef PARANOID
+ /*
+ * Since group is tty and mode is 620 on most terminal lines
+ * and since sessions protect terminals from processes outside
+ * your session, this check is probably no longer necessary.
+ * Since it inhibits setuid root programs that later switch
+ * to another user from accessing /dev/tty, we have decided
+ * to delete this test. (mckusick 5/93)
+ */
+ error = VOP_ACCESS(ttyvp,
+ (flag&FREAD ? VREAD : 0) | (flag&FWRITE ? VWRITE : 0), p->p_ucred, p);
+ if (!error)
+#endif /* PARANOID */
+ error = VOP_OPEN(ttyvp, flag, NOCRED, p);
+ VOP_UNLOCK(ttyvp);
+ return (error);
+}
+
+/*ARGSUSED*/
+int
+cttyread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ register struct vnode *ttyvp = cttyvp(uio->uio_procp);
+ int error;
+
+ if (ttyvp == NULL)
+ return (EIO);
+ VOP_LOCK(ttyvp);
+ error = VOP_READ(ttyvp, uio, flag, NOCRED);
+ VOP_UNLOCK(ttyvp);
+ return (error);
+}
+
+/*ARGSUSED*/
+int
+cttywrite(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ register struct vnode *ttyvp = cttyvp(uio->uio_procp);
+ int error;
+
+ if (ttyvp == NULL)
+ return (EIO);
+ VOP_LOCK(ttyvp);
+ error = VOP_WRITE(ttyvp, uio, flag, NOCRED);
+ VOP_UNLOCK(ttyvp);
+ return (error);
+}
+
+/*ARGSUSED*/
+int
+cttyioctl(dev, cmd, addr, flag, p)
+ dev_t dev;
+ int cmd;
+ caddr_t addr;
+ int flag;
+ struct proc *p;
+{
+ struct vnode *ttyvp = cttyvp(p);
+
+ if (ttyvp == NULL)
+ return (EIO);
+ if (cmd == TIOCNOTTY) {
+ if (!SESS_LEADER(p)) {
+ p->p_flag &= ~P_CONTROLT;
+ return (0);
+ } else
+ return (EINVAL);
+ }
+ return (VOP_IOCTL(ttyvp, cmd, addr, flag, NOCRED, p));
+}
+
+/*ARGSUSED*/
+int
+cttyselect(dev, flag, p)
+ dev_t dev;
+ int flag;
+ struct proc *p;
+{
+ struct vnode *ttyvp = cttyvp(p);
+
+ if (ttyvp == NULL)
+ return (1); /* try operation to get EOF/failure */
+ return (VOP_SELECT(ttyvp, flag, FREAD|FWRITE, NOCRED, p));
+}
diff --git a/sys/kern/uipc_domain.c b/sys/kern/uipc_domain.c
new file mode 100644
index 0000000..db082c7
--- /dev/null
+++ b/sys/kern/uipc_domain.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)uipc_domain.c 8.2 (Berkeley) 10/18/93
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/mbuf.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <vm/vm.h>
+#include <sys/sysctl.h>
+
+void pffasttimo __P((void *));
+void pfslowtimo __P((void *));
+
+#define ADDDOMAIN(x) { \
+ extern struct domain __CONCAT(x,domain); \
+ __CONCAT(x,domain.dom_next) = domains; \
+ domains = &__CONCAT(x,domain); \
+}
+
+void
+domaininit()
+{
+ register struct domain *dp;
+ register struct protosw *pr;
+
+#undef unix
+#ifndef lint
+ ADDDOMAIN(unix);
+ ADDDOMAIN(route);
+#ifdef INET
+ ADDDOMAIN(inet);
+#endif
+#ifdef NS
+ ADDDOMAIN(ns);
+#endif
+#ifdef ISO
+ ADDDOMAIN(iso);
+#endif
+#ifdef CCITT
+ ADDDOMAIN(ccitt);
+#endif
+#include "imp.h"
+#if NIMP > 0
+ ADDDOMAIN(imp);
+#endif
+#endif
+
+ for (dp = domains; dp; dp = dp->dom_next) {
+ if (dp->dom_init)
+ (*dp->dom_init)();
+ for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
+ if (pr->pr_init)
+ (*pr->pr_init)();
+ }
+
+if (max_linkhdr < 16) /* XXX */
+max_linkhdr = 16;
+ max_hdr = max_linkhdr + max_protohdr;
+ max_datalen = MHLEN - max_hdr;
+ timeout(pffasttimo, (void *)0, 1);
+ timeout(pfslowtimo, (void *)0, 1);
+}
+
+struct protosw *
+pffindtype(family, type)
+ int family, type;
+{
+ register struct domain *dp;
+ register struct protosw *pr;
+
+ for (dp = domains; dp; dp = dp->dom_next)
+ if (dp->dom_family == family)
+ goto found;
+ return (0);
+found:
+ for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
+ if (pr->pr_type && pr->pr_type == type)
+ return (pr);
+ return (0);
+}
+
+struct protosw *
+pffindproto(family, protocol, type)
+ int family, protocol, type;
+{
+ register struct domain *dp;
+ register struct protosw *pr;
+ struct protosw *maybe = 0;
+
+ if (family == 0)
+ return (0);
+ for (dp = domains; dp; dp = dp->dom_next)
+ if (dp->dom_family == family)
+ goto found;
+ return (0);
+found:
+ for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) {
+ if ((pr->pr_protocol == protocol) && (pr->pr_type == type))
+ return (pr);
+
+ if (type == SOCK_RAW && pr->pr_type == SOCK_RAW &&
+ pr->pr_protocol == 0 && maybe == (struct protosw *)0)
+ maybe = pr;
+ }
+ return (maybe);
+}
+
+int
+net_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+ struct proc *p;
+{
+ register struct domain *dp;
+ register struct protosw *pr;
+ int family, protocol;
+
+ /*
+ * All sysctl names at this level are nonterminal;
+ * next two components are protocol family and protocol number,
+ * then at least one addition component.
+ */
+ if (namelen < 3)
+ return (EISDIR); /* overloaded */
+ family = name[0];
+ protocol = name[1];
+
+ if (family == 0)
+ return (0);
+ for (dp = domains; dp; dp = dp->dom_next)
+ if (dp->dom_family == family)
+ goto found;
+ return (ENOPROTOOPT);
+found:
+ for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
+ if (pr->pr_protocol == protocol && pr->pr_sysctl)
+ return ((*pr->pr_sysctl)(name + 2, namelen - 2,
+ oldp, oldlenp, newp, newlen));
+ return (ENOPROTOOPT);
+}
+
+void
+pfctlinput(cmd, sa)
+ int cmd;
+ struct sockaddr *sa;
+{
+ register struct domain *dp;
+ register struct protosw *pr;
+
+ for (dp = domains; dp; dp = dp->dom_next)
+ for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
+ if (pr->pr_ctlinput)
+ (*pr->pr_ctlinput)(cmd, sa, (caddr_t)0);
+}
+
+void
+pfslowtimo(arg)
+ void *arg;
+{
+ register struct domain *dp;
+ register struct protosw *pr;
+
+ for (dp = domains; dp; dp = dp->dom_next)
+ for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
+ if (pr->pr_slowtimo)
+ (*pr->pr_slowtimo)();
+ timeout(pfslowtimo, (void *)0, hz/2);
+}
+
+void
+pffasttimo(arg)
+ void *arg;
+{
+ register struct domain *dp;
+ register struct protosw *pr;
+
+ for (dp = domains; dp; dp = dp->dom_next)
+ for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
+ if (pr->pr_fasttimo)
+ (*pr->pr_fasttimo)();
+ timeout(pffasttimo, (void *)0, hz/5);
+}
diff --git a/sys/kern/uipc_mbuf.c b/sys/kern/uipc_mbuf.c
new file mode 100644
index 0000000..5569bea
--- /dev/null
+++ b/sys/kern/uipc_mbuf.c
@@ -0,0 +1,663 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/map.h>
+#define MBTYPES
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+
+#include <vm/vm.h>
+
+void m_reclaim __P(());
+
+extern vm_map_t mb_map;
+struct mbuf *mbutl;
+char *mclrefcnt;
+
+void
+mbinit()
+{
+ int s;
+
+#if CLBYTES < 4096
+#define NCL_INIT (4096/CLBYTES)
+#else
+#define NCL_INIT 1
+#endif
+ s = splimp();
+ if (m_clalloc(NCL_INIT, M_DONTWAIT) == 0)
+ goto bad;
+ splx(s);
+ return;
+bad:
+ panic("mbinit");
+}
+
+/*
+ * Allocate some number of mbuf clusters
+ * and place on cluster free list.
+ * Must be called at splimp.
+ */
+/* ARGSUSED */
+int
+m_clalloc(ncl, nowait)
+ register int ncl;
+ int nowait;
+{
+ static int logged;
+ register caddr_t p;
+ register int i;
+ int npg;
+
+ npg = ncl * CLSIZE;
+ p = (caddr_t)kmem_malloc(mb_map, ctob(npg), !nowait);
+ if (p == NULL) {
+ if (logged == 0) {
+ logged++;
+ log(LOG_ERR, "mb_map full\n");
+ }
+ return (0);
+ }
+ ncl = ncl * CLBYTES / MCLBYTES;
+ for (i = 0; i < ncl; i++) {
+ ((union mcluster *)p)->mcl_next = mclfree;
+ mclfree = (union mcluster *)p;
+ p += MCLBYTES;
+ mbstat.m_clfree++;
+ }
+ mbstat.m_clusters += ncl;
+ return (1);
+}
+
+/*
+ * When MGET failes, ask protocols to free space when short of memory,
+ * then re-attempt to allocate an mbuf.
+ */
+struct mbuf *
+m_retry(i, t)
+ int i, t;
+{
+ register struct mbuf *m;
+
+ m_reclaim();
+#define m_retry(i, t) (struct mbuf *)0
+ MGET(m, i, t);
+#undef m_retry
+ return (m);
+}
+
+/*
+ * As above; retry an MGETHDR.
+ */
+struct mbuf *
+m_retryhdr(i, t)
+ int i, t;
+{
+ register struct mbuf *m;
+
+ m_reclaim();
+#define m_retryhdr(i, t) (struct mbuf *)0
+ MGETHDR(m, i, t);
+#undef m_retryhdr
+ return (m);
+}
+
+void
+m_reclaim()
+{
+ register struct domain *dp;
+ register struct protosw *pr;
+ int s = splimp();
+
+ for (dp = domains; dp; dp = dp->dom_next)
+ for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
+ if (pr->pr_drain)
+ (*pr->pr_drain)();
+ splx(s);
+ mbstat.m_drain++;
+}
+
+/*
+ * Space allocation routines.
+ * These are also available as macros
+ * for critical paths.
+ */
+struct mbuf *
+m_get(nowait, type)
+ int nowait, type;
+{
+ register struct mbuf *m;
+
+ MGET(m, nowait, type);
+ return (m);
+}
+
+struct mbuf *
+m_gethdr(nowait, type)
+ int nowait, type;
+{
+ register struct mbuf *m;
+
+ MGETHDR(m, nowait, type);
+ return (m);
+}
+
+struct mbuf *
+m_getclr(nowait, type)
+ int nowait, type;
+{
+ register struct mbuf *m;
+
+ MGET(m, nowait, type);
+ if (m == 0)
+ return (0);
+ bzero(mtod(m, caddr_t), MLEN);
+ return (m);
+}
+
+struct mbuf *
+m_free(m)
+ struct mbuf *m;
+{
+ register struct mbuf *n;
+
+ MFREE(m, n);
+ return (n);
+}
+
+void
+m_freem(m)
+ register struct mbuf *m;
+{
+ register struct mbuf *n;
+
+ if (m == NULL)
+ return;
+ do {
+ MFREE(m, n);
+ } while (m = n);
+}
+
+/*
+ * Mbuffer utility routines.
+ */
+
+/*
+ * Lesser-used path for M_PREPEND:
+ * allocate new mbuf to prepend to chain,
+ * copy junk along.
+ */
+struct mbuf *
+m_prepend(m, len, how)
+ register struct mbuf *m;
+ int len, how;
+{
+ struct mbuf *mn;
+
+ MGET(mn, how, m->m_type);
+ if (mn == (struct mbuf *)NULL) {
+ m_freem(m);
+ return ((struct mbuf *)NULL);
+ }
+ if (m->m_flags & M_PKTHDR) {
+ M_COPY_PKTHDR(mn, m);
+ m->m_flags &= ~M_PKTHDR;
+ }
+ mn->m_next = m;
+ m = mn;
+ if (len < MHLEN)
+ MH_ALIGN(m, len);
+ m->m_len = len;
+ return (m);
+}
+
+/*
+ * Make a copy of an mbuf chain starting "off0" bytes from the beginning,
+ * continuing for "len" bytes. If len is M_COPYALL, copy to end of mbuf.
+ * The wait parameter is a choice of M_WAIT/M_DONTWAIT from caller.
+ */
+int MCFail;
+
+struct mbuf *
+m_copym(m, off0, len, wait)
+ register struct mbuf *m;
+ int off0, wait;
+ register int len;
+{
+ register struct mbuf *n, **np;
+ register int off = off0;
+ struct mbuf *top;
+ int copyhdr = 0;
+
+ if (off < 0 || len < 0)
+ panic("m_copym");
+ if (off == 0 && m->m_flags & M_PKTHDR)
+ copyhdr = 1;
+ while (off > 0) {
+ if (m == 0)
+ panic("m_copym");
+ if (off < m->m_len)
+ break;
+ off -= m->m_len;
+ m = m->m_next;
+ }
+ np = &top;
+ top = 0;
+ while (len > 0) {
+ if (m == 0) {
+ if (len != M_COPYALL)
+ panic("m_copym");
+ break;
+ }
+ MGET(n, wait, m->m_type);
+ *np = n;
+ if (n == 0)
+ goto nospace;
+ if (copyhdr) {
+ M_COPY_PKTHDR(n, m);
+ if (len == M_COPYALL)
+ n->m_pkthdr.len -= off0;
+ else
+ n->m_pkthdr.len = len;
+ copyhdr = 0;
+ }
+ n->m_len = min(len, m->m_len - off);
+ if (m->m_flags & M_EXT) {
+ n->m_data = m->m_data + off;
+ mclrefcnt[mtocl(m->m_ext.ext_buf)]++;
+ n->m_ext = m->m_ext;
+ n->m_flags |= M_EXT;
+ } else
+ bcopy(mtod(m, caddr_t)+off, mtod(n, caddr_t),
+ (unsigned)n->m_len);
+ if (len != M_COPYALL)
+ len -= n->m_len;
+ off = 0;
+ m = m->m_next;
+ np = &n->m_next;
+ }
+ if (top == 0)
+ MCFail++;
+ return (top);
+nospace:
+ m_freem(top);
+ MCFail++;
+ return (0);
+}
+
+/*
+ * Copy data from an mbuf chain starting "off" bytes from the beginning,
+ * continuing for "len" bytes, into the indicated buffer.
+ */
+void
+m_copydata(m, off, len, cp)
+ register struct mbuf *m;
+ register int off;
+ register int len;
+ caddr_t cp;
+{
+ register unsigned count;
+
+ if (off < 0 || len < 0)
+ panic("m_copydata");
+ while (off > 0) {
+ if (m == 0)
+ panic("m_copydata");
+ if (off < m->m_len)
+ break;
+ off -= m->m_len;
+ m = m->m_next;
+ }
+ while (len > 0) {
+ if (m == 0)
+ panic("m_copydata");
+ count = min(m->m_len - off, len);
+ bcopy(mtod(m, caddr_t) + off, cp, count);
+ len -= count;
+ cp += count;
+ off = 0;
+ m = m->m_next;
+ }
+}
+
+/*
+ * Concatenate mbuf chain n to m.
+ * Both chains must be of the same type (e.g. MT_DATA).
+ * Any m_pkthdr is not updated.
+ */
+void
+m_cat(m, n)
+ register struct mbuf *m, *n;
+{
+ while (m->m_next)
+ m = m->m_next;
+ while (n) {
+ if (m->m_flags & M_EXT ||
+ m->m_data + m->m_len + n->m_len >= &m->m_dat[MLEN]) {
+ /* just join the two chains */
+ m->m_next = n;
+ return;
+ }
+ /* splat the data from one into the other */
+ bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len,
+ (u_int)n->m_len);
+ m->m_len += n->m_len;
+ n = m_free(n);
+ }
+}
+
+void
+m_adj(mp, req_len)
+ struct mbuf *mp;
+ int req_len;
+{
+ register int len = req_len;
+ register struct mbuf *m;
+ register count;
+
+ if ((m = mp) == NULL)
+ return;
+ if (len >= 0) {
+ /*
+ * Trim from head.
+ */
+ while (m != NULL && len > 0) {
+ if (m->m_len <= len) {
+ len -= m->m_len;
+ m->m_len = 0;
+ m = m->m_next;
+ } else {
+ m->m_len -= len;
+ m->m_data += len;
+ len = 0;
+ }
+ }
+ m = mp;
+ if (mp->m_flags & M_PKTHDR)
+ m->m_pkthdr.len -= (req_len - len);
+ } else {
+ /*
+ * Trim from tail. Scan the mbuf chain,
+ * calculating its length and finding the last mbuf.
+ * If the adjustment only affects this mbuf, then just
+ * adjust and return. Otherwise, rescan and truncate
+ * after the remaining size.
+ */
+ len = -len;
+ count = 0;
+ for (;;) {
+ count += m->m_len;
+ if (m->m_next == (struct mbuf *)0)
+ break;
+ m = m->m_next;
+ }
+ if (m->m_len >= len) {
+ m->m_len -= len;
+ if (mp->m_flags & M_PKTHDR)
+ mp->m_pkthdr.len -= len;
+ return;
+ }
+ count -= len;
+ if (count < 0)
+ count = 0;
+ /*
+ * Correct length for chain is "count".
+ * Find the mbuf with last data, adjust its length,
+ * and toss data from remaining mbufs on chain.
+ */
+ m = mp;
+ if (m->m_flags & M_PKTHDR)
+ m->m_pkthdr.len = count;
+ for (; m; m = m->m_next) {
+ if (m->m_len >= count) {
+ m->m_len = count;
+ break;
+ }
+ count -= m->m_len;
+ }
+ while (m = m->m_next)
+ m->m_len = 0;
+ }
+}
+
+/*
+ * Rearange an mbuf chain so that len bytes are contiguous
+ * and in the data area of an mbuf (so that mtod and dtom
+ * will work for a structure of size len). Returns the resulting
+ * mbuf chain on success, frees it and returns null on failure.
+ * If there is room, it will add up to max_protohdr-len extra bytes to the
+ * contiguous region in an attempt to avoid being called next time.
+ */
+int MPFail;
+
+struct mbuf *
+m_pullup(n, len)
+ register struct mbuf *n;
+ int len;
+{
+ register struct mbuf *m;
+ register int count;
+ int space;
+
+ /*
+ * If first mbuf has no cluster, and has room for len bytes
+ * without shifting current data, pullup into it,
+ * otherwise allocate a new mbuf to prepend to the chain.
+ */
+ if ((n->m_flags & M_EXT) == 0 &&
+ n->m_data + len < &n->m_dat[MLEN] && n->m_next) {
+ if (n->m_len >= len)
+ return (n);
+ m = n;
+ n = n->m_next;
+ len -= m->m_len;
+ } else {
+ if (len > MHLEN)
+ goto bad;
+ MGET(m, M_DONTWAIT, n->m_type);
+ if (m == 0)
+ goto bad;
+ m->m_len = 0;
+ if (n->m_flags & M_PKTHDR) {
+ M_COPY_PKTHDR(m, n);
+ n->m_flags &= ~M_PKTHDR;
+ }
+ }
+ space = &m->m_dat[MLEN] - (m->m_data + m->m_len);
+ do {
+ count = min(min(max(len, max_protohdr), space), n->m_len);
+ bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len,
+ (unsigned)count);
+ len -= count;
+ m->m_len += count;
+ n->m_len -= count;
+ space -= count;
+ if (n->m_len)
+ n->m_data += count;
+ else
+ n = m_free(n);
+ } while (len > 0 && n);
+ if (len > 0) {
+ (void) m_free(m);
+ goto bad;
+ }
+ m->m_next = n;
+ return (m);
+bad:
+ m_freem(n);
+ MPFail++;
+ return (0);
+}
+
+/*
+ * Partition an mbuf chain in two pieces, returning the tail --
+ * all but the first len0 bytes. In case of failure, it returns NULL and
+ * attempts to restore the chain to its original state.
+ */
+struct mbuf *
+m_split(m0, len0, wait)
+ register struct mbuf *m0;
+ int len0, wait;
+{
+ register struct mbuf *m, *n;
+ unsigned len = len0, remain;
+
+ for (m = m0; m && len > m->m_len; m = m->m_next)
+ len -= m->m_len;
+ if (m == 0)
+ return (0);
+ remain = m->m_len - len;
+ if (m0->m_flags & M_PKTHDR) {
+ MGETHDR(n, wait, m0->m_type);
+ if (n == 0)
+ return (0);
+ n->m_pkthdr.rcvif = m0->m_pkthdr.rcvif;
+ n->m_pkthdr.len = m0->m_pkthdr.len - len0;
+ m0->m_pkthdr.len = len0;
+ if (m->m_flags & M_EXT)
+ goto extpacket;
+ if (remain > MHLEN) {
+ /* m can't be the lead packet */
+ MH_ALIGN(n, 0);
+ n->m_next = m_split(m, len, wait);
+ if (n->m_next == 0) {
+ (void) m_free(n);
+ return (0);
+ } else
+ return (n);
+ } else
+ MH_ALIGN(n, remain);
+ } else if (remain == 0) {
+ n = m->m_next;
+ m->m_next = 0;
+ return (n);
+ } else {
+ MGET(n, wait, m->m_type);
+ if (n == 0)
+ return (0);
+ M_ALIGN(n, remain);
+ }
+extpacket:
+ if (m->m_flags & M_EXT) {
+ n->m_flags |= M_EXT;
+ n->m_ext = m->m_ext;
+ mclrefcnt[mtocl(m->m_ext.ext_buf)]++;
+ m->m_ext.ext_size = 0; /* For Accounting XXXXXX danger */
+ n->m_data = m->m_data + len;
+ } else {
+ bcopy(mtod(m, caddr_t) + len, mtod(n, caddr_t), remain);
+ }
+ n->m_len = remain;
+ m->m_len = len;
+ n->m_next = m->m_next;
+ m->m_next = 0;
+ return (n);
+}
+/*
+ * Routine to copy from device local memory into mbufs.
+ */
+struct mbuf *
+m_devget(buf, totlen, off0, ifp, copy)
+ char *buf;
+ int totlen, off0;
+ struct ifnet *ifp;
+ void (*copy)();
+{
+ register struct mbuf *m;
+ struct mbuf *top = 0, **mp = &top;
+ register int off = off0, len;
+ register char *cp;
+ char *epkt;
+
+ cp = buf;
+ epkt = cp + totlen;
+ if (off) {
+ cp += off + 2 * sizeof(u_short);
+ totlen -= 2 * sizeof(u_short);
+ }
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ return (0);
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = totlen;
+ m->m_len = MHLEN;
+
+ while (totlen > 0) {
+ if (top) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == 0) {
+ m_freem(top);
+ return (0);
+ }
+ m->m_len = MLEN;
+ }
+ len = min(totlen, epkt - cp);
+ if (len >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if (m->m_flags & M_EXT)
+ m->m_len = len = min(len, MCLBYTES);
+ else
+ len = m->m_len;
+ } else {
+ /*
+ * Place initial small packet/header at end of mbuf.
+ */
+ if (len < m->m_len) {
+ if (top == 0 && len + max_linkhdr <= m->m_len)
+ m->m_data += max_linkhdr;
+ m->m_len = len;
+ } else
+ len = m->m_len;
+ }
+ if (copy)
+ copy(cp, mtod(m, caddr_t), (unsigned)len);
+ else
+ bcopy(cp, mtod(m, caddr_t), (unsigned)len);
+ cp += len;
+ *mp = m;
+ mp = &m->m_next;
+ totlen -= len;
+ if (cp == epkt)
+ cp = buf;
+ }
+ return (top);
+}
diff --git a/sys/kern/uipc_proto.c b/sys/kern/uipc_proto.c
new file mode 100644
index 0000000..da9828a
--- /dev/null
+++ b/sys/kern/uipc_proto.c
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)uipc_proto.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/mbuf.h>
+
+/*
+ * Definitions of protocols supported in the UNIX domain.
+ */
+
+int uipc_usrreq(), raw_usrreq();
+void raw_init(),raw_input(),raw_ctlinput();
+extern struct domain unixdomain; /* or at least forward */
+
+struct protosw unixsw[] = {
+{ SOCK_STREAM, &unixdomain, 0, PR_CONNREQUIRED|PR_WANTRCVD|PR_RIGHTS,
+ 0, 0, 0, 0,
+ uipc_usrreq,
+ 0, 0, 0, 0,
+},
+{ SOCK_DGRAM, &unixdomain, 0, PR_ATOMIC|PR_ADDR|PR_RIGHTS,
+ 0, 0, 0, 0,
+ uipc_usrreq,
+ 0, 0, 0, 0,
+},
+{ 0, 0, 0, 0,
+ raw_input, 0, raw_ctlinput, 0,
+ raw_usrreq,
+ raw_init, 0, 0, 0,
+}
+};
+
+int unp_externalize(), unp_dispose();
+
+struct domain unixdomain =
+ { AF_UNIX, "unix", 0, unp_externalize, unp_dispose,
+ unixsw, &unixsw[sizeof(unixsw)/sizeof(unixsw[0])] };
diff --git a/sys/kern/uipc_sockbuf.c b/sys/kern/uipc_sockbuf.c
new file mode 100644
index 0000000..dc153bd
--- /dev/null
+++ b/sys/kern/uipc_sockbuf.c
@@ -0,0 +1,787 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)uipc_socket2.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/buf.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+void soqinsque __P((struct socket *, struct socket *, int));
+void sowakeup __P((struct socket *, struct sockbuf *));
+void sbrelease __P((struct sockbuf *));
+void sbappendrecord __P((struct sockbuf *, struct mbuf *));
+void sbcompress __P((struct sockbuf *, struct mbuf *, struct mbuf *));
+void sbflush __P((struct sockbuf *));
+void sbdrop __P((struct sockbuf *, int));
+
+/*
+ * Primitive routines for operating on sockets and socket buffers
+ */
+
+/* strings for sleep message: */
+char netio[] = "netio";
+char netcon[] = "netcon";
+char netcls[] = "netcls";
+
+u_long sb_max = SB_MAX; /* patchable */
+
+/*
+ * Procedures to manipulate state flags of socket
+ * and do appropriate wakeups. Normal sequence from the
+ * active (originating) side is that soisconnecting() is
+ * called during processing of connect() call,
+ * resulting in an eventual call to soisconnected() if/when the
+ * connection is established. When the connection is torn down
+ * soisdisconnecting() is called during processing of disconnect() call,
+ * and soisdisconnected() is called when the connection to the peer
+ * is totally severed. The semantics of these routines are such that
+ * connectionless protocols can call soisconnected() and soisdisconnected()
+ * only, bypassing the in-progress calls when setting up a ``connection''
+ * takes no time.
+ *
+ * From the passive side, a socket is created with
+ * two queues of sockets: so_q0 for connections in progress
+ * and so_q for connections already made and awaiting user acceptance.
+ * As a protocol is preparing incoming connections, it creates a socket
+ * structure queued on so_q0 by calling sonewconn(). When the connection
+ * is established, soisconnected() is called, and transfers the
+ * socket structure to so_q, making it available to accept().
+ *
+ * If a socket is closed with sockets on either
+ * so_q0 or so_q, these sockets are dropped.
+ *
+ * If higher level protocols are implemented in
+ * the kernel, the wakeups done here will sometimes
+ * cause software-interrupt process scheduling.
+ */
+
+void
+soisconnecting(so)
+ register struct socket *so;
+{
+
+ so->so_state &= ~(SS_ISCONNECTED|SS_ISDISCONNECTING);
+ so->so_state |= SS_ISCONNECTING;
+}
+
+void
+soisconnected(so)
+ register struct socket *so;
+{
+ register struct socket *head = so->so_head;
+
+ so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING|SS_ISCONFIRMING);
+ so->so_state |= SS_ISCONNECTED;
+ if (head && soqremque(so, 0)) {
+ soqinsque(head, so, 1);
+ sorwakeup(head);
+ wakeup((caddr_t)&head->so_timeo);
+ } else {
+ wakeup((caddr_t)&so->so_timeo);
+ sorwakeup(so);
+ sowwakeup(so);
+ }
+}
+
+void
+soisdisconnecting(so)
+ register struct socket *so;
+{
+
+ so->so_state &= ~SS_ISCONNECTING;
+ so->so_state |= (SS_ISDISCONNECTING|SS_CANTRCVMORE|SS_CANTSENDMORE);
+ wakeup((caddr_t)&so->so_timeo);
+ sowwakeup(so);
+ sorwakeup(so);
+}
+
+void
+soisdisconnected(so)
+ register struct socket *so;
+{
+
+ so->so_state &= ~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING);
+ so->so_state |= (SS_CANTRCVMORE|SS_CANTSENDMORE);
+ wakeup((caddr_t)&so->so_timeo);
+ sowwakeup(so);
+ sorwakeup(so);
+}
+
+/*
+ * When an attempt at a new connection is noted on a socket
+ * which accepts connections, sonewconn is called. If the
+ * connection is possible (subject to space constraints, etc.)
+ * then we allocate a new structure, propoerly linked into the
+ * data structure of the original socket, and return this.
+ * Connstatus may be 0, or SO_ISCONFIRMING, or SO_ISCONNECTED.
+ *
+ * Currently, sonewconn() is defined as sonewconn1() in socketvar.h
+ * to catch calls that are missing the (new) second parameter.
+ */
+struct socket *
+sonewconn1(head, connstatus)
+ register struct socket *head;
+ int connstatus;
+{
+ register struct socket *so;
+ int soqueue = connstatus ? 1 : 0;
+
+ if (head->so_qlen + head->so_q0len > 3 * head->so_qlimit / 2)
+ return ((struct socket *)0);
+ MALLOC(so, struct socket *, sizeof(*so), M_SOCKET, M_DONTWAIT);
+ if (so == NULL)
+ return ((struct socket *)0);
+ bzero((caddr_t)so, sizeof(*so));
+ so->so_type = head->so_type;
+ so->so_options = head->so_options &~ SO_ACCEPTCONN;
+ so->so_linger = head->so_linger;
+ so->so_state = head->so_state | SS_NOFDREF;
+ so->so_proto = head->so_proto;
+ so->so_timeo = head->so_timeo;
+ so->so_pgid = head->so_pgid;
+ (void) soreserve(so, head->so_snd.sb_hiwat, head->so_rcv.sb_hiwat);
+ soqinsque(head, so, soqueue);
+ if ((*so->so_proto->pr_usrreq)(so, PRU_ATTACH,
+ (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0)) {
+ (void) soqremque(so, soqueue);
+ (void) free((caddr_t)so, M_SOCKET);
+ return ((struct socket *)0);
+ }
+ if (connstatus) {
+ sorwakeup(head);
+ wakeup((caddr_t)&head->so_timeo);
+ so->so_state |= connstatus;
+ }
+ return (so);
+}
+
+void
+soqinsque(head, so, q)
+ register struct socket *head, *so;
+ int q;
+{
+
+ register struct socket **prev;
+ so->so_head = head;
+ if (q == 0) {
+ head->so_q0len++;
+ so->so_q0 = 0;
+ for (prev = &(head->so_q0); *prev; )
+ prev = &((*prev)->so_q0);
+ } else {
+ head->so_qlen++;
+ so->so_q = 0;
+ for (prev = &(head->so_q); *prev; )
+ prev = &((*prev)->so_q);
+ }
+ *prev = so;
+}
+
+int
+soqremque(so, q)
+ register struct socket *so;
+ int q;
+{
+ register struct socket *head, *prev, *next;
+
+ head = so->so_head;
+ prev = head;
+ for (;;) {
+ next = q ? prev->so_q : prev->so_q0;
+ if (next == so)
+ break;
+ if (next == 0)
+ return (0);
+ prev = next;
+ }
+ if (q == 0) {
+ prev->so_q0 = next->so_q0;
+ head->so_q0len--;
+ } else {
+ prev->so_q = next->so_q;
+ head->so_qlen--;
+ }
+ next->so_q0 = next->so_q = 0;
+ next->so_head = 0;
+ return (1);
+}
+
+/*
+ * Socantsendmore indicates that no more data will be sent on the
+ * socket; it would normally be applied to a socket when the user
+ * informs the system that no more data is to be sent, by the protocol
+ * code (in case PRU_SHUTDOWN). Socantrcvmore indicates that no more data
+ * will be received, and will normally be applied to the socket by a
+ * protocol when it detects that the peer will send no more data.
+ * Data queued for reading in the socket may yet be read.
+ */
+
+void
+socantsendmore(so)
+ struct socket *so;
+{
+
+ so->so_state |= SS_CANTSENDMORE;
+ sowwakeup(so);
+}
+
+void
+socantrcvmore(so)
+ struct socket *so;
+{
+
+ so->so_state |= SS_CANTRCVMORE;
+ sorwakeup(so);
+}
+
+/*
+ * Wait for data to arrive at/drain from a socket buffer.
+ */
+int
+sbwait(sb)
+ struct sockbuf *sb;
+{
+
+ sb->sb_flags |= SB_WAIT;
+ return (tsleep((caddr_t)&sb->sb_cc,
+ (sb->sb_flags & SB_NOINTR) ? PSOCK : PSOCK | PCATCH, netio,
+ sb->sb_timeo));
+}
+
+/*
+ * Lock a sockbuf already known to be locked;
+ * return any error returned from sleep (EINTR).
+ */
+int
+sb_lock(sb)
+ register struct sockbuf *sb;
+{
+ int error;
+
+ while (sb->sb_flags & SB_LOCK) {
+ sb->sb_flags |= SB_WANT;
+ if (error = tsleep((caddr_t)&sb->sb_flags,
+ (sb->sb_flags & SB_NOINTR) ? PSOCK : PSOCK|PCATCH,
+ netio, 0))
+ return (error);
+ }
+ sb->sb_flags |= SB_LOCK;
+ return (0);
+}
+
+/*
+ * Wakeup processes waiting on a socket buffer.
+ * Do asynchronous notification via SIGIO
+ * if the socket has the SS_ASYNC flag set.
+ */
+void
+sowakeup(so, sb)
+ register struct socket *so;
+ register struct sockbuf *sb;
+{
+ struct proc *p;
+
+ selwakeup(&sb->sb_sel);
+ sb->sb_flags &= ~SB_SEL;
+ if (sb->sb_flags & SB_WAIT) {
+ sb->sb_flags &= ~SB_WAIT;
+ wakeup((caddr_t)&sb->sb_cc);
+ }
+ if (so->so_state & SS_ASYNC) {
+ if (so->so_pgid < 0)
+ gsignal(-so->so_pgid, SIGIO);
+ else if (so->so_pgid > 0 && (p = pfind(so->so_pgid)) != 0)
+ psignal(p, SIGIO);
+ }
+}
+
+/*
+ * Socket buffer (struct sockbuf) utility routines.
+ *
+ * Each socket contains two socket buffers: one for sending data and
+ * one for receiving data. Each buffer contains a queue of mbufs,
+ * information about the number of mbufs and amount of data in the
+ * queue, and other fields allowing select() statements and notification
+ * on data availability to be implemented.
+ *
+ * Data stored in a socket buffer is maintained as a list of records.
+ * Each record is a list of mbufs chained together with the m_next
+ * field. Records are chained together with the m_nextpkt field. The upper
+ * level routine soreceive() expects the following conventions to be
+ * observed when placing information in the receive buffer:
+ *
+ * 1. If the protocol requires each message be preceded by the sender's
+ * name, then a record containing that name must be present before
+ * any associated data (mbuf's must be of type MT_SONAME).
+ * 2. If the protocol supports the exchange of ``access rights'' (really
+ * just additional data associated with the message), and there are
+ * ``rights'' to be received, then a record containing this data
+ * should be present (mbuf's must be of type MT_RIGHTS).
+ * 3. If a name or rights record exists, then it must be followed by
+ * a data record, perhaps of zero length.
+ *
+ * Before using a new socket structure it is first necessary to reserve
+ * buffer space to the socket, by calling sbreserve(). This should commit
+ * some of the available buffer space in the system buffer pool for the
+ * socket (currently, it does nothing but enforce limits). The space
+ * should be released by calling sbrelease() when the socket is destroyed.
+ */
+
+int
+soreserve(so, sndcc, rcvcc)
+ register struct socket *so;
+ u_long sndcc, rcvcc;
+{
+
+ if (sbreserve(&so->so_snd, sndcc) == 0)
+ goto bad;
+ if (sbreserve(&so->so_rcv, rcvcc) == 0)
+ goto bad2;
+ if (so->so_rcv.sb_lowat == 0)
+ so->so_rcv.sb_lowat = 1;
+ if (so->so_snd.sb_lowat == 0)
+ so->so_snd.sb_lowat = MCLBYTES;
+ if (so->so_snd.sb_lowat > so->so_snd.sb_hiwat)
+ so->so_snd.sb_lowat = so->so_snd.sb_hiwat;
+ return (0);
+bad2:
+ sbrelease(&so->so_snd);
+bad:
+ return (ENOBUFS);
+}
+
+/*
+ * Allot mbufs to a sockbuf.
+ * Attempt to scale mbmax so that mbcnt doesn't become limiting
+ * if buffering efficiency is near the normal case.
+ */
+int
+sbreserve(sb, cc)
+ struct sockbuf *sb;
+ u_long cc;
+{
+
+ if (cc > sb_max * MCLBYTES / (MSIZE + MCLBYTES))
+ return (0);
+ sb->sb_hiwat = cc;
+ sb->sb_mbmax = min(cc * 2, sb_max);
+ if (sb->sb_lowat > sb->sb_hiwat)
+ sb->sb_lowat = sb->sb_hiwat;
+ return (1);
+}
+
+/*
+ * Free mbufs held by a socket, and reserved mbuf space.
+ */
+void
+sbrelease(sb)
+ struct sockbuf *sb;
+{
+
+ sbflush(sb);
+ sb->sb_hiwat = sb->sb_mbmax = 0;
+}
+
+/*
+ * Routines to add and remove
+ * data from an mbuf queue.
+ *
+ * The routines sbappend() or sbappendrecord() are normally called to
+ * append new mbufs to a socket buffer, after checking that adequate
+ * space is available, comparing the function sbspace() with the amount
+ * of data to be added. sbappendrecord() differs from sbappend() in
+ * that data supplied is treated as the beginning of a new record.
+ * To place a sender's address, optional access rights, and data in a
+ * socket receive buffer, sbappendaddr() should be used. To place
+ * access rights and data in a socket receive buffer, sbappendrights()
+ * should be used. In either case, the new data begins a new record.
+ * Note that unlike sbappend() and sbappendrecord(), these routines check
+ * for the caller that there will be enough space to store the data.
+ * Each fails if there is not enough space, or if it cannot find mbufs
+ * to store additional information in.
+ *
+ * Reliable protocols may use the socket send buffer to hold data
+ * awaiting acknowledgement. Data is normally copied from a socket
+ * send buffer in a protocol with m_copy for output to a peer,
+ * and then removing the data from the socket buffer with sbdrop()
+ * or sbdroprecord() when the data is acknowledged by the peer.
+ */
+
+/*
+ * Append mbuf chain m to the last record in the
+ * socket buffer sb. The additional space associated
+ * the mbuf chain is recorded in sb. Empty mbufs are
+ * discarded and mbufs are compacted where possible.
+ */
+void
+sbappend(sb, m)
+ struct sockbuf *sb;
+ struct mbuf *m;
+{
+ register struct mbuf *n;
+
+ if (m == 0)
+ return;
+ if (n = sb->sb_mb) {
+ while (n->m_nextpkt)
+ n = n->m_nextpkt;
+ do {
+ if (n->m_flags & M_EOR) {
+ sbappendrecord(sb, m); /* XXXXXX!!!! */
+ return;
+ }
+ } while (n->m_next && (n = n->m_next));
+ }
+ sbcompress(sb, m, n);
+}
+
+#ifdef SOCKBUF_DEBUG
+void
+sbcheck(sb)
+ register struct sockbuf *sb;
+{
+ register struct mbuf *m;
+ register int len = 0, mbcnt = 0;
+
+ for (m = sb->sb_mb; m; m = m->m_next) {
+ len += m->m_len;
+ mbcnt += MSIZE;
+ if (m->m_flags & M_EXT)
+ mbcnt += m->m_ext.ext_size;
+ if (m->m_nextpkt)
+ panic("sbcheck nextpkt");
+ }
+ if (len != sb->sb_cc || mbcnt != sb->sb_mbcnt) {
+ printf("cc %d != %d || mbcnt %d != %d\n", len, sb->sb_cc,
+ mbcnt, sb->sb_mbcnt);
+ panic("sbcheck");
+ }
+}
+#endif
+
+/*
+ * As above, except the mbuf chain
+ * begins a new record.
+ */
+void
+sbappendrecord(sb, m0)
+ register struct sockbuf *sb;
+ register struct mbuf *m0;
+{
+ register struct mbuf *m;
+
+ if (m0 == 0)
+ return;
+ if (m = sb->sb_mb)
+ while (m->m_nextpkt)
+ m = m->m_nextpkt;
+ /*
+ * Put the first mbuf on the queue.
+ * Note this permits zero length records.
+ */
+ sballoc(sb, m0);
+ if (m)
+ m->m_nextpkt = m0;
+ else
+ sb->sb_mb = m0;
+ m = m0->m_next;
+ m0->m_next = 0;
+ if (m && (m0->m_flags & M_EOR)) {
+ m0->m_flags &= ~M_EOR;
+ m->m_flags |= M_EOR;
+ }
+ sbcompress(sb, m, m0);
+}
+
+/*
+ * As above except that OOB data
+ * is inserted at the beginning of the sockbuf,
+ * but after any other OOB data.
+ */
+void
+sbinsertoob(sb, m0)
+ register struct sockbuf *sb;
+ register struct mbuf *m0;
+{
+ register struct mbuf *m;
+ register struct mbuf **mp;
+
+ if (m0 == 0)
+ return;
+ for (mp = &sb->sb_mb; m = *mp; mp = &((*mp)->m_nextpkt)) {
+ again:
+ switch (m->m_type) {
+
+ case MT_OOBDATA:
+ continue; /* WANT next train */
+
+ case MT_CONTROL:
+ if (m = m->m_next)
+ goto again; /* inspect THIS train further */
+ }
+ break;
+ }
+ /*
+ * Put the first mbuf on the queue.
+ * Note this permits zero length records.
+ */
+ sballoc(sb, m0);
+ m0->m_nextpkt = *mp;
+ *mp = m0;
+ m = m0->m_next;
+ m0->m_next = 0;
+ if (m && (m0->m_flags & M_EOR)) {
+ m0->m_flags &= ~M_EOR;
+ m->m_flags |= M_EOR;
+ }
+ sbcompress(sb, m, m0);
+}
+
+/*
+ * Append address and data, and optionally, control (ancillary) data
+ * to the receive queue of a socket. If present,
+ * m0 must include a packet header with total length.
+ * Returns 0 if no space in sockbuf or insufficient mbufs.
+ */
+int
+sbappendaddr(sb, asa, m0, control)
+ register struct sockbuf *sb;
+ struct sockaddr *asa;
+ struct mbuf *m0, *control;
+{
+ register struct mbuf *m, *n;
+ int space = asa->sa_len;
+
+if (m0 && (m0->m_flags & M_PKTHDR) == 0)
+panic("sbappendaddr");
+ if (m0)
+ space += m0->m_pkthdr.len;
+ for (n = control; n; n = n->m_next) {
+ space += n->m_len;
+ if (n->m_next == 0) /* keep pointer to last control buf */
+ break;
+ }
+ if (space > sbspace(sb))
+ return (0);
+ if (asa->sa_len > MLEN)
+ return (0);
+ MGET(m, M_DONTWAIT, MT_SONAME);
+ if (m == 0)
+ return (0);
+ m->m_len = asa->sa_len;
+ bcopy((caddr_t)asa, mtod(m, caddr_t), asa->sa_len);
+ if (n)
+ n->m_next = m0; /* concatenate data to control */
+ else
+ control = m0;
+ m->m_next = control;
+ for (n = m; n; n = n->m_next)
+ sballoc(sb, n);
+ if (n = sb->sb_mb) {
+ while (n->m_nextpkt)
+ n = n->m_nextpkt;
+ n->m_nextpkt = m;
+ } else
+ sb->sb_mb = m;
+ return (1);
+}
+
+int
+sbappendcontrol(sb, m0, control)
+ struct sockbuf *sb;
+ struct mbuf *control, *m0;
+{
+ register struct mbuf *m, *n;
+ int space = 0;
+
+ if (control == 0)
+ panic("sbappendcontrol");
+ for (m = control; ; m = m->m_next) {
+ space += m->m_len;
+ if (m->m_next == 0)
+ break;
+ }
+ n = m; /* save pointer to last control buffer */
+ for (m = m0; m; m = m->m_next)
+ space += m->m_len;
+ if (space > sbspace(sb))
+ return (0);
+ n->m_next = m0; /* concatenate data to control */
+ for (m = control; m; m = m->m_next)
+ sballoc(sb, m);
+ if (n = sb->sb_mb) {
+ while (n->m_nextpkt)
+ n = n->m_nextpkt;
+ n->m_nextpkt = control;
+ } else
+ sb->sb_mb = control;
+ return (1);
+}
+
+/*
+ * Compress mbuf chain m into the socket
+ * buffer sb following mbuf n. If n
+ * is null, the buffer is presumed empty.
+ */
+void
+sbcompress(sb, m, n)
+ register struct sockbuf *sb;
+ register struct mbuf *m, *n;
+{
+ register int eor = 0;
+ register struct mbuf *o;
+
+ while (m) {
+ eor |= m->m_flags & M_EOR;
+ if (m->m_len == 0 &&
+ (eor == 0 ||
+ (((o = m->m_next) || (o = n)) &&
+ o->m_type == m->m_type))) {
+ m = m_free(m);
+ continue;
+ }
+ if (n && (n->m_flags & (M_EXT | M_EOR)) == 0 &&
+ (n->m_data + n->m_len + m->m_len) < &n->m_dat[MLEN] &&
+ n->m_type == m->m_type) {
+ bcopy(mtod(m, caddr_t), mtod(n, caddr_t) + n->m_len,
+ (unsigned)m->m_len);
+ n->m_len += m->m_len;
+ sb->sb_cc += m->m_len;
+ m = m_free(m);
+ continue;
+ }
+ if (n)
+ n->m_next = m;
+ else
+ sb->sb_mb = m;
+ sballoc(sb, m);
+ n = m;
+ m->m_flags &= ~M_EOR;
+ m = m->m_next;
+ n->m_next = 0;
+ }
+ if (eor) {
+ if (n)
+ n->m_flags |= eor;
+ else
+ printf("semi-panic: sbcompress\n");
+ }
+}
+
+/*
+ * Free all mbufs in a sockbuf.
+ * Check that all resources are reclaimed.
+ */
+void
+sbflush(sb)
+ register struct sockbuf *sb;
+{
+
+ if (sb->sb_flags & SB_LOCK)
+ panic("sbflush");
+ while (sb->sb_mbcnt)
+ sbdrop(sb, (int)sb->sb_cc);
+ if (sb->sb_cc || sb->sb_mb)
+ panic("sbflush 2");
+}
+
+/*
+ * Drop data from (the front of) a sockbuf.
+ */
+void
+sbdrop(sb, len)
+ register struct sockbuf *sb;
+ register int len;
+{
+ register struct mbuf *m, *mn;
+ struct mbuf *next;
+
+ next = (m = sb->sb_mb) ? m->m_nextpkt : 0;
+ while (len > 0) {
+ if (m == 0) {
+ if (next == 0)
+ panic("sbdrop");
+ m = next;
+ next = m->m_nextpkt;
+ continue;
+ }
+ if (m->m_len > len) {
+ m->m_len -= len;
+ m->m_data += len;
+ sb->sb_cc -= len;
+ break;
+ }
+ len -= m->m_len;
+ sbfree(sb, m);
+ MFREE(m, mn);
+ m = mn;
+ }
+ while (m && m->m_len == 0) {
+ sbfree(sb, m);
+ MFREE(m, mn);
+ m = mn;
+ }
+ if (m) {
+ sb->sb_mb = m;
+ m->m_nextpkt = next;
+ } else
+ sb->sb_mb = next;
+}
+
+/*
+ * Drop a record off the front of a sockbuf
+ * and move the next record to the front.
+ */
+void
+sbdroprecord(sb)
+ register struct sockbuf *sb;
+{
+ register struct mbuf *m, *mn;
+
+ m = sb->sb_mb;
+ if (m) {
+ sb->sb_mb = m->m_nextpkt;
+ do {
+ sbfree(sb, m);
+ MFREE(m, mn);
+ } while (m = mn);
+ }
+}
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
new file mode 100644
index 0000000..510556c
--- /dev/null
+++ b/sys/kern/uipc_socket.c
@@ -0,0 +1,1035 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)uipc_socket.c 8.3 (Berkeley) 4/15/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/kernel.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/resourcevar.h>
+
+void sofree __P((struct socket *));
+void sorflush __P((struct socket *));
+
+/*
+ * Socket operation routines.
+ * These routines are called by the routines in
+ * sys_socket.c or from a system process, and
+ * implement the semantics of socket operations by
+ * switching out to the protocol specific routines.
+ */
+/*ARGSUSED*/
+int
+socreate(dom, aso, type, proto)
+ int dom;
+ struct socket **aso;
+ register int type;
+ int proto;
+{
+ struct proc *p = curproc; /* XXX */
+ register struct protosw *prp;
+ register struct socket *so;
+ register int error;
+
+ if (proto)
+ prp = pffindproto(dom, proto, type);
+ else
+ prp = pffindtype(dom, type);
+ if (prp == 0 || prp->pr_usrreq == 0)
+ return (EPROTONOSUPPORT);
+ if (prp->pr_type != type)
+ return (EPROTOTYPE);
+ MALLOC(so, struct socket *, sizeof(*so), M_SOCKET, M_WAIT);
+ bzero((caddr_t)so, sizeof(*so));
+ so->so_type = type;
+ if (p->p_ucred->cr_uid == 0)
+ so->so_state = SS_PRIV;
+ so->so_proto = prp;
+ error =
+ (*prp->pr_usrreq)(so, PRU_ATTACH,
+ (struct mbuf *)0, (struct mbuf *)proto, (struct mbuf *)0);
+ if (error) {
+ so->so_state |= SS_NOFDREF;
+ sofree(so);
+ return (error);
+ }
+ *aso = so;
+ return (0);
+}
+
+int
+sobind(so, nam)
+ struct socket *so;
+ struct mbuf *nam;
+{
+ int s = splnet();
+ int error;
+
+ error =
+ (*so->so_proto->pr_usrreq)(so, PRU_BIND,
+ (struct mbuf *)0, nam, (struct mbuf *)0);
+ splx(s);
+ return (error);
+}
+
+int
+solisten(so, backlog)
+ register struct socket *so;
+ int backlog;
+{
+ int s = splnet(), error;
+
+ error =
+ (*so->so_proto->pr_usrreq)(so, PRU_LISTEN,
+ (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0);
+ if (error) {
+ splx(s);
+ return (error);
+ }
+ if (so->so_q == 0)
+ so->so_options |= SO_ACCEPTCONN;
+ if (backlog < 0)
+ backlog = 0;
+ so->so_qlimit = min(backlog, SOMAXCONN);
+ splx(s);
+ return (0);
+}
+
+void
+sofree(so)
+ register struct socket *so;
+{
+
+ if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0)
+ return;
+ if (so->so_head) {
+ if (!soqremque(so, 0) && !soqremque(so, 1))
+ panic("sofree dq");
+ so->so_head = 0;
+ }
+ sbrelease(&so->so_snd);
+ sorflush(so);
+ FREE(so, M_SOCKET);
+}
+
+/*
+ * Close a socket on last file table reference removal.
+ * Initiate disconnect if connected.
+ * Free socket when disconnect complete.
+ */
+int
+soclose(so)
+ register struct socket *so;
+{
+ int s = splnet(); /* conservative */
+ int error = 0;
+
+ if (so->so_options & SO_ACCEPTCONN) {
+ while (so->so_q0)
+ (void) soabort(so->so_q0);
+ while (so->so_q)
+ (void) soabort(so->so_q);
+ }
+ if (so->so_pcb == 0)
+ goto discard;
+ if (so->so_state & SS_ISCONNECTED) {
+ if ((so->so_state & SS_ISDISCONNECTING) == 0) {
+ error = sodisconnect(so);
+ if (error)
+ goto drop;
+ }
+ if (so->so_options & SO_LINGER) {
+ if ((so->so_state & SS_ISDISCONNECTING) &&
+ (so->so_state & SS_NBIO))
+ goto drop;
+ while (so->so_state & SS_ISCONNECTED)
+ if (error = tsleep((caddr_t)&so->so_timeo,
+ PSOCK | PCATCH, netcls, so->so_linger))
+ break;
+ }
+ }
+drop:
+ if (so->so_pcb) {
+ int error2 =
+ (*so->so_proto->pr_usrreq)(so, PRU_DETACH,
+ (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0);
+ if (error == 0)
+ error = error2;
+ }
+discard:
+ if (so->so_state & SS_NOFDREF)
+ panic("soclose: NOFDREF");
+ so->so_state |= SS_NOFDREF;
+ sofree(so);
+ splx(s);
+ return (error);
+}
+
+/*
+ * Must be called at splnet...
+ */
+int
+soabort(so)
+ struct socket *so;
+{
+
+ return (
+ (*so->so_proto->pr_usrreq)(so, PRU_ABORT,
+ (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0));
+}
+
+int
+soaccept(so, nam)
+ register struct socket *so;
+ struct mbuf *nam;
+{
+ int s = splnet();
+ int error;
+
+ if ((so->so_state & SS_NOFDREF) == 0)
+ panic("soaccept: !NOFDREF");
+ so->so_state &= ~SS_NOFDREF;
+ error = (*so->so_proto->pr_usrreq)(so, PRU_ACCEPT,
+ (struct mbuf *)0, nam, (struct mbuf *)0);
+ splx(s);
+ return (error);
+}
+
+int
+soconnect(so, nam)
+ register struct socket *so;
+ struct mbuf *nam;
+{
+ int s;
+ int error;
+
+ if (so->so_options & SO_ACCEPTCONN)
+ return (EOPNOTSUPP);
+ s = splnet();
+ /*
+ * If protocol is connection-based, can only connect once.
+ * Otherwise, if connected, try to disconnect first.
+ * This allows user to disconnect by connecting to, e.g.,
+ * a null address.
+ */
+ if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING) &&
+ ((so->so_proto->pr_flags & PR_CONNREQUIRED) ||
+ (error = sodisconnect(so))))
+ error = EISCONN;
+ else
+ error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT,
+ (struct mbuf *)0, nam, (struct mbuf *)0);
+ splx(s);
+ return (error);
+}
+
+int
+soconnect2(so1, so2)
+ register struct socket *so1;
+ struct socket *so2;
+{
+ int s = splnet();
+ int error;
+
+ error = (*so1->so_proto->pr_usrreq)(so1, PRU_CONNECT2,
+ (struct mbuf *)0, (struct mbuf *)so2, (struct mbuf *)0);
+ splx(s);
+ return (error);
+}
+
+int
+sodisconnect(so)
+ register struct socket *so;
+{
+ int s = splnet();
+ int error;
+
+ if ((so->so_state & SS_ISCONNECTED) == 0) {
+ error = ENOTCONN;
+ goto bad;
+ }
+ if (so->so_state & SS_ISDISCONNECTING) {
+ error = EALREADY;
+ goto bad;
+ }
+ error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT,
+ (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0);
+bad:
+ splx(s);
+ return (error);
+}
+
+#define SBLOCKWAIT(f) (((f) & MSG_DONTWAIT) ? M_NOWAIT : M_WAITOK)
+/*
+ * Send on a socket.
+ * If send must go all at once and message is larger than
+ * send buffering, then hard error.
+ * Lock against other senders.
+ * If must go all at once and not enough room now, then
+ * inform user that this would block and do nothing.
+ * Otherwise, if nonblocking, send as much as possible.
+ * The data to be sent is described by "uio" if nonzero,
+ * otherwise by the mbuf chain "top" (which must be null
+ * if uio is not). Data provided in mbuf chain must be small
+ * enough to send all at once.
+ *
+ * Returns nonzero on error, timeout or signal; callers
+ * must check for short counts if EINTR/ERESTART are returned.
+ * Data and control buffers are freed on return.
+ */
+int
+sosend(so, addr, uio, top, control, flags)
+ register struct socket *so;
+ struct mbuf *addr;
+ struct uio *uio;
+ struct mbuf *top;
+ struct mbuf *control;
+ int flags;
+{
+ struct proc *p = curproc; /* XXX */
+ struct mbuf **mp;
+ register struct mbuf *m;
+ register long space, len, resid;
+ int clen = 0, error, s, dontroute, mlen;
+ int atomic = sosendallatonce(so) || top;
+
+ if (uio)
+ resid = uio->uio_resid;
+ else
+ resid = top->m_pkthdr.len;
+ /*
+ * In theory resid should be unsigned.
+ * However, space must be signed, as it might be less than 0
+ * if we over-committed, and we must use a signed comparison
+ * of space and resid. On the other hand, a negative resid
+ * causes us to loop sending 0-length segments to the protocol.
+ */
+ if (resid < 0)
+ return (EINVAL);
+ dontroute =
+ (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 &&
+ (so->so_proto->pr_flags & PR_ATOMIC);
+ p->p_stats->p_ru.ru_msgsnd++;
+ if (control)
+ clen = control->m_len;
+#define snderr(errno) { error = errno; splx(s); goto release; }
+
+restart:
+ if (error = sblock(&so->so_snd, SBLOCKWAIT(flags)))
+ goto out;
+ do {
+ s = splnet();
+ if (so->so_state & SS_CANTSENDMORE)
+ snderr(EPIPE);
+ if (so->so_error)
+ snderr(so->so_error);
+ if ((so->so_state & SS_ISCONNECTED) == 0) {
+ if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
+ if ((so->so_state & SS_ISCONFIRMING) == 0 &&
+ !(resid == 0 && clen != 0))
+ snderr(ENOTCONN);
+ } else if (addr == 0)
+ snderr(EDESTADDRREQ);
+ }
+ space = sbspace(&so->so_snd);
+ if (flags & MSG_OOB)
+ space += 1024;
+ if (atomic && resid > so->so_snd.sb_hiwat ||
+ clen > so->so_snd.sb_hiwat)
+ snderr(EMSGSIZE);
+ if (space < resid + clen && uio &&
+ (atomic || space < so->so_snd.sb_lowat || space < clen)) {
+ if (so->so_state & SS_NBIO)
+ snderr(EWOULDBLOCK);
+ sbunlock(&so->so_snd);
+ error = sbwait(&so->so_snd);
+ splx(s);
+ if (error)
+ goto out;
+ goto restart;
+ }
+ splx(s);
+ mp = &top;
+ space -= clen;
+ do {
+ if (uio == NULL) {
+ /*
+ * Data is prepackaged in "top".
+ */
+ resid = 0;
+ if (flags & MSG_EOR)
+ top->m_flags |= M_EOR;
+ } else do {
+ if (top == 0) {
+ MGETHDR(m, M_WAIT, MT_DATA);
+ mlen = MHLEN;
+ m->m_pkthdr.len = 0;
+ m->m_pkthdr.rcvif = (struct ifnet *)0;
+ } else {
+ MGET(m, M_WAIT, MT_DATA);
+ mlen = MLEN;
+ }
+ if (resid >= MINCLSIZE) {
+ MCLGET(m, M_WAIT);
+ if ((m->m_flags & M_EXT) == 0)
+ goto nopages;
+ mlen = MCLBYTES;
+ len = min(min(mlen, resid), space);
+ } else {
+nopages:
+ len = min(min(mlen, resid), space);
+ /*
+ * For datagram protocols, leave room
+ * for protocol headers in first mbuf.
+ */
+ if (atomic && top == 0 && len < mlen)
+ MH_ALIGN(m, len);
+ }
+ space -= len;
+ error = uiomove(mtod(m, caddr_t), (int)len, uio);
+ resid = uio->uio_resid;
+ m->m_len = len;
+ *mp = m;
+ top->m_pkthdr.len += len;
+ if (error)
+ goto release;
+ mp = &m->m_next;
+ if (resid <= 0) {
+ if (flags & MSG_EOR)
+ top->m_flags |= M_EOR;
+ break;
+ }
+ } while (space > 0 && atomic);
+ if (dontroute)
+ so->so_options |= SO_DONTROUTE;
+ s = splnet(); /* XXX */
+ error = (*so->so_proto->pr_usrreq)(so,
+ (flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND,
+ top, addr, control);
+ splx(s);
+ if (dontroute)
+ so->so_options &= ~SO_DONTROUTE;
+ clen = 0;
+ control = 0;
+ top = 0;
+ mp = &top;
+ if (error)
+ goto release;
+ } while (resid && space > 0);
+ } while (resid);
+
+release:
+ sbunlock(&so->so_snd);
+out:
+ if (top)
+ m_freem(top);
+ if (control)
+ m_freem(control);
+ return (error);
+}
+
+/*
+ * Implement receive operations on a socket.
+ * We depend on the way that records are added to the sockbuf
+ * by sbappend*. In particular, each record (mbufs linked through m_next)
+ * must begin with an address if the protocol so specifies,
+ * followed by an optional mbuf or mbufs containing ancillary data,
+ * and then zero or more mbufs of data.
+ * In order to avoid blocking network interrupts for the entire time here,
+ * we splx() while doing the actual copy to user space.
+ * Although the sockbuf is locked, new data may still be appended,
+ * and thus we must maintain consistency of the sockbuf during that time.
+ *
+ * The caller may receive the data as a single mbuf chain by supplying
+ * an mbuf **mp0 for use in returning the chain. The uio is then used
+ * only for the count in uio_resid.
+ */
+int
+soreceive(so, paddr, uio, mp0, controlp, flagsp)
+ register struct socket *so;
+ struct mbuf **paddr;
+ struct uio *uio;
+ struct mbuf **mp0;
+ struct mbuf **controlp;
+ int *flagsp;
+{
+ register struct mbuf *m, **mp;
+ register int flags, len, error, s, offset;
+ struct protosw *pr = so->so_proto;
+ struct mbuf *nextrecord;
+ int moff, type = 0;
+ int orig_resid = uio->uio_resid;
+
+ mp = mp0;
+ if (paddr)
+ *paddr = 0;
+ if (controlp)
+ *controlp = 0;
+ if (flagsp)
+ flags = *flagsp &~ MSG_EOR;
+ else
+ flags = 0;
+ if (flags & MSG_OOB) {
+ m = m_get(M_WAIT, MT_DATA);
+ error = (*pr->pr_usrreq)(so, PRU_RCVOOB,
+ m, (struct mbuf *)(flags & MSG_PEEK), (struct mbuf *)0);
+ if (error)
+ goto bad;
+ do {
+ error = uiomove(mtod(m, caddr_t),
+ (int) min(uio->uio_resid, m->m_len), uio);
+ m = m_free(m);
+ } while (uio->uio_resid && error == 0 && m);
+bad:
+ if (m)
+ m_freem(m);
+ return (error);
+ }
+ if (mp)
+ *mp = (struct mbuf *)0;
+ if (so->so_state & SS_ISCONFIRMING && uio->uio_resid)
+ (*pr->pr_usrreq)(so, PRU_RCVD, (struct mbuf *)0,
+ (struct mbuf *)0, (struct mbuf *)0);
+
+restart:
+ if (error = sblock(&so->so_rcv, SBLOCKWAIT(flags)))
+ return (error);
+ s = splnet();
+
+ m = so->so_rcv.sb_mb;
+ /*
+ * If we have less data than requested, block awaiting more
+ * (subject to any timeout) if:
+ * 1. the current count is less than the low water mark, or
+ * 2. MSG_WAITALL is set, and it is possible to do the entire
+ * receive operation at once if we block (resid <= hiwat).
+ * 3. MSG_DONTWAIT is not set
+ * If MSG_WAITALL is set but resid is larger than the receive buffer,
+ * we have to do the receive in sections, and thus risk returning
+ * a short count if a timeout or signal occurs after we start.
+ */
+ if (m == 0 || ((flags & MSG_DONTWAIT) == 0 &&
+ so->so_rcv.sb_cc < uio->uio_resid) &&
+ (so->so_rcv.sb_cc < so->so_rcv.sb_lowat ||
+ ((flags & MSG_WAITALL) && uio->uio_resid <= so->so_rcv.sb_hiwat)) &&
+ m->m_nextpkt == 0 && (pr->pr_flags & PR_ATOMIC) == 0) {
+#ifdef DIAGNOSTIC
+ if (m == 0 && so->so_rcv.sb_cc)
+ panic("receive 1");
+#endif
+ if (so->so_error) {
+ if (m)
+ goto dontblock;
+ error = so->so_error;
+ if ((flags & MSG_PEEK) == 0)
+ so->so_error = 0;
+ goto release;
+ }
+ if (so->so_state & SS_CANTRCVMORE) {
+ if (m)
+ goto dontblock;
+ else
+ goto release;
+ }
+ for (; m; m = m->m_next)
+ if (m->m_type == MT_OOBDATA || (m->m_flags & M_EOR)) {
+ m = so->so_rcv.sb_mb;
+ goto dontblock;
+ }
+ if ((so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0 &&
+ (so->so_proto->pr_flags & PR_CONNREQUIRED)) {
+ error = ENOTCONN;
+ goto release;
+ }
+ if (uio->uio_resid == 0)
+ goto release;
+ if ((so->so_state & SS_NBIO) || (flags & MSG_DONTWAIT)) {
+ error = EWOULDBLOCK;
+ goto release;
+ }
+ sbunlock(&so->so_rcv);
+ error = sbwait(&so->so_rcv);
+ splx(s);
+ if (error)
+ return (error);
+ goto restart;
+ }
+dontblock:
+ if (uio->uio_procp)
+ uio->uio_procp->p_stats->p_ru.ru_msgrcv++;
+ nextrecord = m->m_nextpkt;
+ if (pr->pr_flags & PR_ADDR) {
+#ifdef DIAGNOSTIC
+ if (m->m_type != MT_SONAME)
+ panic("receive 1a");
+#endif
+ orig_resid = 0;
+ if (flags & MSG_PEEK) {
+ if (paddr)
+ *paddr = m_copy(m, 0, m->m_len);
+ m = m->m_next;
+ } else {
+ sbfree(&so->so_rcv, m);
+ if (paddr) {
+ *paddr = m;
+ so->so_rcv.sb_mb = m->m_next;
+ m->m_next = 0;
+ m = so->so_rcv.sb_mb;
+ } else {
+ MFREE(m, so->so_rcv.sb_mb);
+ m = so->so_rcv.sb_mb;
+ }
+ }
+ }
+ while (m && m->m_type == MT_CONTROL && error == 0) {
+ if (flags & MSG_PEEK) {
+ if (controlp)
+ *controlp = m_copy(m, 0, m->m_len);
+ m = m->m_next;
+ } else {
+ sbfree(&so->so_rcv, m);
+ if (controlp) {
+ if (pr->pr_domain->dom_externalize &&
+ mtod(m, struct cmsghdr *)->cmsg_type ==
+ SCM_RIGHTS)
+ error = (*pr->pr_domain->dom_externalize)(m);
+ *controlp = m;
+ so->so_rcv.sb_mb = m->m_next;
+ m->m_next = 0;
+ m = so->so_rcv.sb_mb;
+ } else {
+ MFREE(m, so->so_rcv.sb_mb);
+ m = so->so_rcv.sb_mb;
+ }
+ }
+ if (controlp) {
+ orig_resid = 0;
+ controlp = &(*controlp)->m_next;
+ }
+ }
+ if (m) {
+ if ((flags & MSG_PEEK) == 0)
+ m->m_nextpkt = nextrecord;
+ type = m->m_type;
+ if (type == MT_OOBDATA)
+ flags |= MSG_OOB;
+ }
+ moff = 0;
+ offset = 0;
+ while (m && uio->uio_resid > 0 && error == 0) {
+ if (m->m_type == MT_OOBDATA) {
+ if (type != MT_OOBDATA)
+ break;
+ } else if (type == MT_OOBDATA)
+ break;
+#ifdef DIAGNOSTIC
+ else if (m->m_type != MT_DATA && m->m_type != MT_HEADER)
+ panic("receive 3");
+#endif
+ so->so_state &= ~SS_RCVATMARK;
+ len = uio->uio_resid;
+ if (so->so_oobmark && len > so->so_oobmark - offset)
+ len = so->so_oobmark - offset;
+ if (len > m->m_len - moff)
+ len = m->m_len - moff;
+ /*
+ * If mp is set, just pass back the mbufs.
+ * Otherwise copy them out via the uio, then free.
+ * Sockbuf must be consistent here (points to current mbuf,
+ * it points to next record) when we drop priority;
+ * we must note any additions to the sockbuf when we
+ * block interrupts again.
+ */
+ if (mp == 0) {
+ splx(s);
+ error = uiomove(mtod(m, caddr_t) + moff, (int)len, uio);
+ s = splnet();
+ } else
+ uio->uio_resid -= len;
+ if (len == m->m_len - moff) {
+ if (m->m_flags & M_EOR)
+ flags |= MSG_EOR;
+ if (flags & MSG_PEEK) {
+ m = m->m_next;
+ moff = 0;
+ } else {
+ nextrecord = m->m_nextpkt;
+ sbfree(&so->so_rcv, m);
+ if (mp) {
+ *mp = m;
+ mp = &m->m_next;
+ so->so_rcv.sb_mb = m = m->m_next;
+ *mp = (struct mbuf *)0;
+ } else {
+ MFREE(m, so->so_rcv.sb_mb);
+ m = so->so_rcv.sb_mb;
+ }
+ if (m)
+ m->m_nextpkt = nextrecord;
+ }
+ } else {
+ if (flags & MSG_PEEK)
+ moff += len;
+ else {
+ if (mp)
+ *mp = m_copym(m, 0, len, M_WAIT);
+ m->m_data += len;
+ m->m_len -= len;
+ so->so_rcv.sb_cc -= len;
+ }
+ }
+ if (so->so_oobmark) {
+ if ((flags & MSG_PEEK) == 0) {
+ so->so_oobmark -= len;
+ if (so->so_oobmark == 0) {
+ so->so_state |= SS_RCVATMARK;
+ break;
+ }
+ } else {
+ offset += len;
+ if (offset == so->so_oobmark)
+ break;
+ }
+ }
+ if (flags & MSG_EOR)
+ break;
+ /*
+ * If the MSG_WAITALL flag is set (for non-atomic socket),
+ * we must not quit until "uio->uio_resid == 0" or an error
+ * termination. If a signal/timeout occurs, return
+ * with a short count but without error.
+ * Keep sockbuf locked against other readers.
+ */
+ while (flags & MSG_WAITALL && m == 0 && uio->uio_resid > 0 &&
+ !sosendallatonce(so) && !nextrecord) {
+ if (so->so_error || so->so_state & SS_CANTRCVMORE)
+ break;
+ error = sbwait(&so->so_rcv);
+ if (error) {
+ sbunlock(&so->so_rcv);
+ splx(s);
+ return (0);
+ }
+ if (m = so->so_rcv.sb_mb)
+ nextrecord = m->m_nextpkt;
+ }
+ }
+
+ if (m && pr->pr_flags & PR_ATOMIC) {
+ flags |= MSG_TRUNC;
+ if ((flags & MSG_PEEK) == 0)
+ (void) sbdroprecord(&so->so_rcv);
+ }
+ if ((flags & MSG_PEEK) == 0) {
+ if (m == 0)
+ so->so_rcv.sb_mb = nextrecord;
+ if (pr->pr_flags & PR_WANTRCVD && so->so_pcb)
+ (*pr->pr_usrreq)(so, PRU_RCVD, (struct mbuf *)0,
+ (struct mbuf *)flags, (struct mbuf *)0,
+ (struct mbuf *)0);
+ }
+ if (orig_resid == uio->uio_resid && orig_resid &&
+ (flags & MSG_EOR) == 0 && (so->so_state & SS_CANTRCVMORE) == 0) {
+ sbunlock(&so->so_rcv);
+ splx(s);
+ goto restart;
+ }
+
+ if (flagsp)
+ *flagsp |= flags;
+release:
+ sbunlock(&so->so_rcv);
+ splx(s);
+ return (error);
+}
+
+int
+soshutdown(so, how)
+ register struct socket *so;
+ register int how;
+{
+ register struct protosw *pr = so->so_proto;
+
+ how++;
+ if (how & FREAD)
+ sorflush(so);
+ if (how & FWRITE)
+ return ((*pr->pr_usrreq)(so, PRU_SHUTDOWN,
+ (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0));
+ return (0);
+}
+
+void
+sorflush(so)
+ register struct socket *so;
+{
+ register struct sockbuf *sb = &so->so_rcv;
+ register struct protosw *pr = so->so_proto;
+ register int s;
+ struct sockbuf asb;
+
+ sb->sb_flags |= SB_NOINTR;
+ (void) sblock(sb, M_WAITOK);
+ s = splimp();
+ socantrcvmore(so);
+ sbunlock(sb);
+ asb = *sb;
+ bzero((caddr_t)sb, sizeof (*sb));
+ splx(s);
+ if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose)
+ (*pr->pr_domain->dom_dispose)(asb.sb_mb);
+ sbrelease(&asb);
+}
+
+int
+sosetopt(so, level, optname, m0)
+ register struct socket *so;
+ int level, optname;
+ struct mbuf *m0;
+{
+ int error = 0;
+ register struct mbuf *m = m0;
+
+ if (level != SOL_SOCKET) {
+ if (so->so_proto && so->so_proto->pr_ctloutput)
+ return ((*so->so_proto->pr_ctloutput)
+ (PRCO_SETOPT, so, level, optname, &m0));
+ error = ENOPROTOOPT;
+ } else {
+ switch (optname) {
+
+ case SO_LINGER:
+ if (m == NULL || m->m_len != sizeof (struct linger)) {
+ error = EINVAL;
+ goto bad;
+ }
+ so->so_linger = mtod(m, struct linger *)->l_linger;
+ /* fall thru... */
+
+ case SO_DEBUG:
+ case SO_KEEPALIVE:
+ case SO_DONTROUTE:
+ case SO_USELOOPBACK:
+ case SO_BROADCAST:
+ case SO_REUSEADDR:
+ case SO_REUSEPORT:
+ case SO_OOBINLINE:
+ if (m == NULL || m->m_len < sizeof (int)) {
+ error = EINVAL;
+ goto bad;
+ }
+ if (*mtod(m, int *))
+ so->so_options |= optname;
+ else
+ so->so_options &= ~optname;
+ break;
+
+ case SO_SNDBUF:
+ case SO_RCVBUF:
+ case SO_SNDLOWAT:
+ case SO_RCVLOWAT:
+ if (m == NULL || m->m_len < sizeof (int)) {
+ error = EINVAL;
+ goto bad;
+ }
+ switch (optname) {
+
+ case SO_SNDBUF:
+ case SO_RCVBUF:
+ if (sbreserve(optname == SO_SNDBUF ?
+ &so->so_snd : &so->so_rcv,
+ (u_long) *mtod(m, int *)) == 0) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ break;
+
+ case SO_SNDLOWAT:
+ so->so_snd.sb_lowat = *mtod(m, int *);
+ break;
+ case SO_RCVLOWAT:
+ so->so_rcv.sb_lowat = *mtod(m, int *);
+ break;
+ }
+ break;
+
+ case SO_SNDTIMEO:
+ case SO_RCVTIMEO:
+ {
+ struct timeval *tv;
+ short val;
+
+ if (m == NULL || m->m_len < sizeof (*tv)) {
+ error = EINVAL;
+ goto bad;
+ }
+ tv = mtod(m, struct timeval *);
+ if (tv->tv_sec > SHRT_MAX / hz - hz) {
+ error = EDOM;
+ goto bad;
+ }
+ val = tv->tv_sec * hz + tv->tv_usec / tick;
+
+ switch (optname) {
+
+ case SO_SNDTIMEO:
+ so->so_snd.sb_timeo = val;
+ break;
+ case SO_RCVTIMEO:
+ so->so_rcv.sb_timeo = val;
+ break;
+ }
+ break;
+ }
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ if (error == 0 && so->so_proto && so->so_proto->pr_ctloutput) {
+ (void) ((*so->so_proto->pr_ctloutput)
+ (PRCO_SETOPT, so, level, optname, &m0));
+ m = NULL; /* freed by protocol */
+ }
+ }
+bad:
+ if (m)
+ (void) m_free(m);
+ return (error);
+}
+
+int
+sogetopt(so, level, optname, mp)
+ register struct socket *so;
+ int level, optname;
+ struct mbuf **mp;
+{
+ register struct mbuf *m;
+
+ if (level != SOL_SOCKET) {
+ if (so->so_proto && so->so_proto->pr_ctloutput) {
+ return ((*so->so_proto->pr_ctloutput)
+ (PRCO_GETOPT, so, level, optname, mp));
+ } else
+ return (ENOPROTOOPT);
+ } else {
+ m = m_get(M_WAIT, MT_SOOPTS);
+ m->m_len = sizeof (int);
+
+ switch (optname) {
+
+ case SO_LINGER:
+ m->m_len = sizeof (struct linger);
+ mtod(m, struct linger *)->l_onoff =
+ so->so_options & SO_LINGER;
+ mtod(m, struct linger *)->l_linger = so->so_linger;
+ break;
+
+ case SO_USELOOPBACK:
+ case SO_DONTROUTE:
+ case SO_DEBUG:
+ case SO_KEEPALIVE:
+ case SO_REUSEADDR:
+ case SO_REUSEPORT:
+ case SO_BROADCAST:
+ case SO_OOBINLINE:
+ *mtod(m, int *) = so->so_options & optname;
+ break;
+
+ case SO_TYPE:
+ *mtod(m, int *) = so->so_type;
+ break;
+
+ case SO_ERROR:
+ *mtod(m, int *) = so->so_error;
+ so->so_error = 0;
+ break;
+
+ case SO_SNDBUF:
+ *mtod(m, int *) = so->so_snd.sb_hiwat;
+ break;
+
+ case SO_RCVBUF:
+ *mtod(m, int *) = so->so_rcv.sb_hiwat;
+ break;
+
+ case SO_SNDLOWAT:
+ *mtod(m, int *) = so->so_snd.sb_lowat;
+ break;
+
+ case SO_RCVLOWAT:
+ *mtod(m, int *) = so->so_rcv.sb_lowat;
+ break;
+
+ case SO_SNDTIMEO:
+ case SO_RCVTIMEO:
+ {
+ int val = (optname == SO_SNDTIMEO ?
+ so->so_snd.sb_timeo : so->so_rcv.sb_timeo);
+
+ m->m_len = sizeof(struct timeval);
+ mtod(m, struct timeval *)->tv_sec = val / hz;
+ mtod(m, struct timeval *)->tv_usec =
+ (val % hz) / tick;
+ break;
+ }
+
+ default:
+ (void)m_free(m);
+ return (ENOPROTOOPT);
+ }
+ *mp = m;
+ return (0);
+ }
+}
+
+void
+sohasoutofband(so)
+ register struct socket *so;
+{
+ struct proc *p;
+
+ if (so->so_pgid < 0)
+ gsignal(-so->so_pgid, SIGURG);
+ else if (so->so_pgid > 0 && (p = pfind(so->so_pgid)) != 0)
+ psignal(p, SIGURG);
+ selwakeup(&so->so_rcv.sb_sel);
+}
diff --git a/sys/kern/uipc_socket2.c b/sys/kern/uipc_socket2.c
new file mode 100644
index 0000000..dc153bd
--- /dev/null
+++ b/sys/kern/uipc_socket2.c
@@ -0,0 +1,787 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)uipc_socket2.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/buf.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+void soqinsque __P((struct socket *, struct socket *, int));
+void sowakeup __P((struct socket *, struct sockbuf *));
+void sbrelease __P((struct sockbuf *));
+void sbappendrecord __P((struct sockbuf *, struct mbuf *));
+void sbcompress __P((struct sockbuf *, struct mbuf *, struct mbuf *));
+void sbflush __P((struct sockbuf *));
+void sbdrop __P((struct sockbuf *, int));
+
+/*
+ * Primitive routines for operating on sockets and socket buffers
+ */
+
+/* strings for sleep message: */
+char netio[] = "netio";
+char netcon[] = "netcon";
+char netcls[] = "netcls";
+
+u_long sb_max = SB_MAX; /* patchable */
+
+/*
+ * Procedures to manipulate state flags of socket
+ * and do appropriate wakeups. Normal sequence from the
+ * active (originating) side is that soisconnecting() is
+ * called during processing of connect() call,
+ * resulting in an eventual call to soisconnected() if/when the
+ * connection is established. When the connection is torn down
+ * soisdisconnecting() is called during processing of disconnect() call,
+ * and soisdisconnected() is called when the connection to the peer
+ * is totally severed. The semantics of these routines are such that
+ * connectionless protocols can call soisconnected() and soisdisconnected()
+ * only, bypassing the in-progress calls when setting up a ``connection''
+ * takes no time.
+ *
+ * From the passive side, a socket is created with
+ * two queues of sockets: so_q0 for connections in progress
+ * and so_q for connections already made and awaiting user acceptance.
+ * As a protocol is preparing incoming connections, it creates a socket
+ * structure queued on so_q0 by calling sonewconn(). When the connection
+ * is established, soisconnected() is called, and transfers the
+ * socket structure to so_q, making it available to accept().
+ *
+ * If a socket is closed with sockets on either
+ * so_q0 or so_q, these sockets are dropped.
+ *
+ * If higher level protocols are implemented in
+ * the kernel, the wakeups done here will sometimes
+ * cause software-interrupt process scheduling.
+ */
+
+void
+soisconnecting(so)
+ register struct socket *so;
+{
+
+ so->so_state &= ~(SS_ISCONNECTED|SS_ISDISCONNECTING);
+ so->so_state |= SS_ISCONNECTING;
+}
+
+void
+soisconnected(so)
+ register struct socket *so;
+{
+ register struct socket *head = so->so_head;
+
+ so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING|SS_ISCONFIRMING);
+ so->so_state |= SS_ISCONNECTED;
+ if (head && soqremque(so, 0)) {
+ soqinsque(head, so, 1);
+ sorwakeup(head);
+ wakeup((caddr_t)&head->so_timeo);
+ } else {
+ wakeup((caddr_t)&so->so_timeo);
+ sorwakeup(so);
+ sowwakeup(so);
+ }
+}
+
+void
+soisdisconnecting(so)
+ register struct socket *so;
+{
+
+ so->so_state &= ~SS_ISCONNECTING;
+ so->so_state |= (SS_ISDISCONNECTING|SS_CANTRCVMORE|SS_CANTSENDMORE);
+ wakeup((caddr_t)&so->so_timeo);
+ sowwakeup(so);
+ sorwakeup(so);
+}
+
+void
+soisdisconnected(so)
+ register struct socket *so;
+{
+
+ so->so_state &= ~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING);
+ so->so_state |= (SS_CANTRCVMORE|SS_CANTSENDMORE);
+ wakeup((caddr_t)&so->so_timeo);
+ sowwakeup(so);
+ sorwakeup(so);
+}
+
+/*
+ * When an attempt at a new connection is noted on a socket
+ * which accepts connections, sonewconn is called. If the
+ * connection is possible (subject to space constraints, etc.)
+ * then we allocate a new structure, propoerly linked into the
+ * data structure of the original socket, and return this.
+ * Connstatus may be 0, or SO_ISCONFIRMING, or SO_ISCONNECTED.
+ *
+ * Currently, sonewconn() is defined as sonewconn1() in socketvar.h
+ * to catch calls that are missing the (new) second parameter.
+ */
+struct socket *
+sonewconn1(head, connstatus)
+ register struct socket *head;
+ int connstatus;
+{
+ register struct socket *so;
+ int soqueue = connstatus ? 1 : 0;
+
+ if (head->so_qlen + head->so_q0len > 3 * head->so_qlimit / 2)
+ return ((struct socket *)0);
+ MALLOC(so, struct socket *, sizeof(*so), M_SOCKET, M_DONTWAIT);
+ if (so == NULL)
+ return ((struct socket *)0);
+ bzero((caddr_t)so, sizeof(*so));
+ so->so_type = head->so_type;
+ so->so_options = head->so_options &~ SO_ACCEPTCONN;
+ so->so_linger = head->so_linger;
+ so->so_state = head->so_state | SS_NOFDREF;
+ so->so_proto = head->so_proto;
+ so->so_timeo = head->so_timeo;
+ so->so_pgid = head->so_pgid;
+ (void) soreserve(so, head->so_snd.sb_hiwat, head->so_rcv.sb_hiwat);
+ soqinsque(head, so, soqueue);
+ if ((*so->so_proto->pr_usrreq)(so, PRU_ATTACH,
+ (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0)) {
+ (void) soqremque(so, soqueue);
+ (void) free((caddr_t)so, M_SOCKET);
+ return ((struct socket *)0);
+ }
+ if (connstatus) {
+ sorwakeup(head);
+ wakeup((caddr_t)&head->so_timeo);
+ so->so_state |= connstatus;
+ }
+ return (so);
+}
+
+void
+soqinsque(head, so, q)
+ register struct socket *head, *so;
+ int q;
+{
+
+ register struct socket **prev;
+ so->so_head = head;
+ if (q == 0) {
+ head->so_q0len++;
+ so->so_q0 = 0;
+ for (prev = &(head->so_q0); *prev; )
+ prev = &((*prev)->so_q0);
+ } else {
+ head->so_qlen++;
+ so->so_q = 0;
+ for (prev = &(head->so_q); *prev; )
+ prev = &((*prev)->so_q);
+ }
+ *prev = so;
+}
+
+int
+soqremque(so, q)
+ register struct socket *so;
+ int q;
+{
+ register struct socket *head, *prev, *next;
+
+ head = so->so_head;
+ prev = head;
+ for (;;) {
+ next = q ? prev->so_q : prev->so_q0;
+ if (next == so)
+ break;
+ if (next == 0)
+ return (0);
+ prev = next;
+ }
+ if (q == 0) {
+ prev->so_q0 = next->so_q0;
+ head->so_q0len--;
+ } else {
+ prev->so_q = next->so_q;
+ head->so_qlen--;
+ }
+ next->so_q0 = next->so_q = 0;
+ next->so_head = 0;
+ return (1);
+}
+
+/*
+ * Socantsendmore indicates that no more data will be sent on the
+ * socket; it would normally be applied to a socket when the user
+ * informs the system that no more data is to be sent, by the protocol
+ * code (in case PRU_SHUTDOWN). Socantrcvmore indicates that no more data
+ * will be received, and will normally be applied to the socket by a
+ * protocol when it detects that the peer will send no more data.
+ * Data queued for reading in the socket may yet be read.
+ */
+
+void
+socantsendmore(so)
+ struct socket *so;
+{
+
+ so->so_state |= SS_CANTSENDMORE;
+ sowwakeup(so);
+}
+
+void
+socantrcvmore(so)
+ struct socket *so;
+{
+
+ so->so_state |= SS_CANTRCVMORE;
+ sorwakeup(so);
+}
+
+/*
+ * Wait for data to arrive at/drain from a socket buffer.
+ */
+int
+sbwait(sb)
+ struct sockbuf *sb;
+{
+
+ sb->sb_flags |= SB_WAIT;
+ return (tsleep((caddr_t)&sb->sb_cc,
+ (sb->sb_flags & SB_NOINTR) ? PSOCK : PSOCK | PCATCH, netio,
+ sb->sb_timeo));
+}
+
+/*
+ * Lock a sockbuf already known to be locked;
+ * return any error returned from sleep (EINTR).
+ */
+int
+sb_lock(sb)
+ register struct sockbuf *sb;
+{
+ int error;
+
+ while (sb->sb_flags & SB_LOCK) {
+ sb->sb_flags |= SB_WANT;
+ if (error = tsleep((caddr_t)&sb->sb_flags,
+ (sb->sb_flags & SB_NOINTR) ? PSOCK : PSOCK|PCATCH,
+ netio, 0))
+ return (error);
+ }
+ sb->sb_flags |= SB_LOCK;
+ return (0);
+}
+
+/*
+ * Wakeup processes waiting on a socket buffer.
+ * Do asynchronous notification via SIGIO
+ * if the socket has the SS_ASYNC flag set.
+ */
+void
+sowakeup(so, sb)
+ register struct socket *so;
+ register struct sockbuf *sb;
+{
+ struct proc *p;
+
+ selwakeup(&sb->sb_sel);
+ sb->sb_flags &= ~SB_SEL;
+ if (sb->sb_flags & SB_WAIT) {
+ sb->sb_flags &= ~SB_WAIT;
+ wakeup((caddr_t)&sb->sb_cc);
+ }
+ if (so->so_state & SS_ASYNC) {
+ if (so->so_pgid < 0)
+ gsignal(-so->so_pgid, SIGIO);
+ else if (so->so_pgid > 0 && (p = pfind(so->so_pgid)) != 0)
+ psignal(p, SIGIO);
+ }
+}
+
+/*
+ * Socket buffer (struct sockbuf) utility routines.
+ *
+ * Each socket contains two socket buffers: one for sending data and
+ * one for receiving data. Each buffer contains a queue of mbufs,
+ * information about the number of mbufs and amount of data in the
+ * queue, and other fields allowing select() statements and notification
+ * on data availability to be implemented.
+ *
+ * Data stored in a socket buffer is maintained as a list of records.
+ * Each record is a list of mbufs chained together with the m_next
+ * field. Records are chained together with the m_nextpkt field. The upper
+ * level routine soreceive() expects the following conventions to be
+ * observed when placing information in the receive buffer:
+ *
+ * 1. If the protocol requires each message be preceded by the sender's
+ * name, then a record containing that name must be present before
+ * any associated data (mbuf's must be of type MT_SONAME).
+ * 2. If the protocol supports the exchange of ``access rights'' (really
+ * just additional data associated with the message), and there are
+ * ``rights'' to be received, then a record containing this data
+ * should be present (mbuf's must be of type MT_RIGHTS).
+ * 3. If a name or rights record exists, then it must be followed by
+ * a data record, perhaps of zero length.
+ *
+ * Before using a new socket structure it is first necessary to reserve
+ * buffer space to the socket, by calling sbreserve(). This should commit
+ * some of the available buffer space in the system buffer pool for the
+ * socket (currently, it does nothing but enforce limits). The space
+ * should be released by calling sbrelease() when the socket is destroyed.
+ */
+
+int
+soreserve(so, sndcc, rcvcc)
+ register struct socket *so;
+ u_long sndcc, rcvcc;
+{
+
+ if (sbreserve(&so->so_snd, sndcc) == 0)
+ goto bad;
+ if (sbreserve(&so->so_rcv, rcvcc) == 0)
+ goto bad2;
+ if (so->so_rcv.sb_lowat == 0)
+ so->so_rcv.sb_lowat = 1;
+ if (so->so_snd.sb_lowat == 0)
+ so->so_snd.sb_lowat = MCLBYTES;
+ if (so->so_snd.sb_lowat > so->so_snd.sb_hiwat)
+ so->so_snd.sb_lowat = so->so_snd.sb_hiwat;
+ return (0);
+bad2:
+ sbrelease(&so->so_snd);
+bad:
+ return (ENOBUFS);
+}
+
+/*
+ * Allot mbufs to a sockbuf.
+ * Attempt to scale mbmax so that mbcnt doesn't become limiting
+ * if buffering efficiency is near the normal case.
+ */
+int
+sbreserve(sb, cc)
+ struct sockbuf *sb;
+ u_long cc;
+{
+
+ if (cc > sb_max * MCLBYTES / (MSIZE + MCLBYTES))
+ return (0);
+ sb->sb_hiwat = cc;
+ sb->sb_mbmax = min(cc * 2, sb_max);
+ if (sb->sb_lowat > sb->sb_hiwat)
+ sb->sb_lowat = sb->sb_hiwat;
+ return (1);
+}
+
+/*
+ * Free mbufs held by a socket, and reserved mbuf space.
+ */
+void
+sbrelease(sb)
+ struct sockbuf *sb;
+{
+
+ sbflush(sb);
+ sb->sb_hiwat = sb->sb_mbmax = 0;
+}
+
+/*
+ * Routines to add and remove
+ * data from an mbuf queue.
+ *
+ * The routines sbappend() or sbappendrecord() are normally called to
+ * append new mbufs to a socket buffer, after checking that adequate
+ * space is available, comparing the function sbspace() with the amount
+ * of data to be added. sbappendrecord() differs from sbappend() in
+ * that data supplied is treated as the beginning of a new record.
+ * To place a sender's address, optional access rights, and data in a
+ * socket receive buffer, sbappendaddr() should be used. To place
+ * access rights and data in a socket receive buffer, sbappendrights()
+ * should be used. In either case, the new data begins a new record.
+ * Note that unlike sbappend() and sbappendrecord(), these routines check
+ * for the caller that there will be enough space to store the data.
+ * Each fails if there is not enough space, or if it cannot find mbufs
+ * to store additional information in.
+ *
+ * Reliable protocols may use the socket send buffer to hold data
+ * awaiting acknowledgement. Data is normally copied from a socket
+ * send buffer in a protocol with m_copy for output to a peer,
+ * and then removing the data from the socket buffer with sbdrop()
+ * or sbdroprecord() when the data is acknowledged by the peer.
+ */
+
+/*
+ * Append mbuf chain m to the last record in the
+ * socket buffer sb. The additional space associated
+ * the mbuf chain is recorded in sb. Empty mbufs are
+ * discarded and mbufs are compacted where possible.
+ */
+void
+sbappend(sb, m)
+ struct sockbuf *sb;
+ struct mbuf *m;
+{
+ register struct mbuf *n;
+
+ if (m == 0)
+ return;
+ if (n = sb->sb_mb) {
+ while (n->m_nextpkt)
+ n = n->m_nextpkt;
+ do {
+ if (n->m_flags & M_EOR) {
+ sbappendrecord(sb, m); /* XXXXXX!!!! */
+ return;
+ }
+ } while (n->m_next && (n = n->m_next));
+ }
+ sbcompress(sb, m, n);
+}
+
+#ifdef SOCKBUF_DEBUG
+void
+sbcheck(sb)
+ register struct sockbuf *sb;
+{
+ register struct mbuf *m;
+ register int len = 0, mbcnt = 0;
+
+ for (m = sb->sb_mb; m; m = m->m_next) {
+ len += m->m_len;
+ mbcnt += MSIZE;
+ if (m->m_flags & M_EXT)
+ mbcnt += m->m_ext.ext_size;
+ if (m->m_nextpkt)
+ panic("sbcheck nextpkt");
+ }
+ if (len != sb->sb_cc || mbcnt != sb->sb_mbcnt) {
+ printf("cc %d != %d || mbcnt %d != %d\n", len, sb->sb_cc,
+ mbcnt, sb->sb_mbcnt);
+ panic("sbcheck");
+ }
+}
+#endif
+
+/*
+ * As above, except the mbuf chain
+ * begins a new record.
+ */
+void
+sbappendrecord(sb, m0)
+ register struct sockbuf *sb;
+ register struct mbuf *m0;
+{
+ register struct mbuf *m;
+
+ if (m0 == 0)
+ return;
+ if (m = sb->sb_mb)
+ while (m->m_nextpkt)
+ m = m->m_nextpkt;
+ /*
+ * Put the first mbuf on the queue.
+ * Note this permits zero length records.
+ */
+ sballoc(sb, m0);
+ if (m)
+ m->m_nextpkt = m0;
+ else
+ sb->sb_mb = m0;
+ m = m0->m_next;
+ m0->m_next = 0;
+ if (m && (m0->m_flags & M_EOR)) {
+ m0->m_flags &= ~M_EOR;
+ m->m_flags |= M_EOR;
+ }
+ sbcompress(sb, m, m0);
+}
+
+/*
+ * As above except that OOB data
+ * is inserted at the beginning of the sockbuf,
+ * but after any other OOB data.
+ */
+void
+sbinsertoob(sb, m0)
+ register struct sockbuf *sb;
+ register struct mbuf *m0;
+{
+ register struct mbuf *m;
+ register struct mbuf **mp;
+
+ if (m0 == 0)
+ return;
+ for (mp = &sb->sb_mb; m = *mp; mp = &((*mp)->m_nextpkt)) {
+ again:
+ switch (m->m_type) {
+
+ case MT_OOBDATA:
+ continue; /* WANT next train */
+
+ case MT_CONTROL:
+ if (m = m->m_next)
+ goto again; /* inspect THIS train further */
+ }
+ break;
+ }
+ /*
+ * Put the first mbuf on the queue.
+ * Note this permits zero length records.
+ */
+ sballoc(sb, m0);
+ m0->m_nextpkt = *mp;
+ *mp = m0;
+ m = m0->m_next;
+ m0->m_next = 0;
+ if (m && (m0->m_flags & M_EOR)) {
+ m0->m_flags &= ~M_EOR;
+ m->m_flags |= M_EOR;
+ }
+ sbcompress(sb, m, m0);
+}
+
+/*
+ * Append address and data, and optionally, control (ancillary) data
+ * to the receive queue of a socket. If present,
+ * m0 must include a packet header with total length.
+ * Returns 0 if no space in sockbuf or insufficient mbufs.
+ */
+int
+sbappendaddr(sb, asa, m0, control)
+ register struct sockbuf *sb;
+ struct sockaddr *asa;
+ struct mbuf *m0, *control;
+{
+ register struct mbuf *m, *n;
+ int space = asa->sa_len;
+
+if (m0 && (m0->m_flags & M_PKTHDR) == 0)
+panic("sbappendaddr");
+ if (m0)
+ space += m0->m_pkthdr.len;
+ for (n = control; n; n = n->m_next) {
+ space += n->m_len;
+ if (n->m_next == 0) /* keep pointer to last control buf */
+ break;
+ }
+ if (space > sbspace(sb))
+ return (0);
+ if (asa->sa_len > MLEN)
+ return (0);
+ MGET(m, M_DONTWAIT, MT_SONAME);
+ if (m == 0)
+ return (0);
+ m->m_len = asa->sa_len;
+ bcopy((caddr_t)asa, mtod(m, caddr_t), asa->sa_len);
+ if (n)
+ n->m_next = m0; /* concatenate data to control */
+ else
+ control = m0;
+ m->m_next = control;
+ for (n = m; n; n = n->m_next)
+ sballoc(sb, n);
+ if (n = sb->sb_mb) {
+ while (n->m_nextpkt)
+ n = n->m_nextpkt;
+ n->m_nextpkt = m;
+ } else
+ sb->sb_mb = m;
+ return (1);
+}
+
+int
+sbappendcontrol(sb, m0, control)
+ struct sockbuf *sb;
+ struct mbuf *control, *m0;
+{
+ register struct mbuf *m, *n;
+ int space = 0;
+
+ if (control == 0)
+ panic("sbappendcontrol");
+ for (m = control; ; m = m->m_next) {
+ space += m->m_len;
+ if (m->m_next == 0)
+ break;
+ }
+ n = m; /* save pointer to last control buffer */
+ for (m = m0; m; m = m->m_next)
+ space += m->m_len;
+ if (space > sbspace(sb))
+ return (0);
+ n->m_next = m0; /* concatenate data to control */
+ for (m = control; m; m = m->m_next)
+ sballoc(sb, m);
+ if (n = sb->sb_mb) {
+ while (n->m_nextpkt)
+ n = n->m_nextpkt;
+ n->m_nextpkt = control;
+ } else
+ sb->sb_mb = control;
+ return (1);
+}
+
+/*
+ * Compress mbuf chain m into the socket
+ * buffer sb following mbuf n. If n
+ * is null, the buffer is presumed empty.
+ */
+void
+sbcompress(sb, m, n)
+ register struct sockbuf *sb;
+ register struct mbuf *m, *n;
+{
+ register int eor = 0;
+ register struct mbuf *o;
+
+ while (m) {
+ eor |= m->m_flags & M_EOR;
+ if (m->m_len == 0 &&
+ (eor == 0 ||
+ (((o = m->m_next) || (o = n)) &&
+ o->m_type == m->m_type))) {
+ m = m_free(m);
+ continue;
+ }
+ if (n && (n->m_flags & (M_EXT | M_EOR)) == 0 &&
+ (n->m_data + n->m_len + m->m_len) < &n->m_dat[MLEN] &&
+ n->m_type == m->m_type) {
+ bcopy(mtod(m, caddr_t), mtod(n, caddr_t) + n->m_len,
+ (unsigned)m->m_len);
+ n->m_len += m->m_len;
+ sb->sb_cc += m->m_len;
+ m = m_free(m);
+ continue;
+ }
+ if (n)
+ n->m_next = m;
+ else
+ sb->sb_mb = m;
+ sballoc(sb, m);
+ n = m;
+ m->m_flags &= ~M_EOR;
+ m = m->m_next;
+ n->m_next = 0;
+ }
+ if (eor) {
+ if (n)
+ n->m_flags |= eor;
+ else
+ printf("semi-panic: sbcompress\n");
+ }
+}
+
+/*
+ * Free all mbufs in a sockbuf.
+ * Check that all resources are reclaimed.
+ */
+void
+sbflush(sb)
+ register struct sockbuf *sb;
+{
+
+ if (sb->sb_flags & SB_LOCK)
+ panic("sbflush");
+ while (sb->sb_mbcnt)
+ sbdrop(sb, (int)sb->sb_cc);
+ if (sb->sb_cc || sb->sb_mb)
+ panic("sbflush 2");
+}
+
+/*
+ * Drop data from (the front of) a sockbuf.
+ */
+void
+sbdrop(sb, len)
+ register struct sockbuf *sb;
+ register int len;
+{
+ register struct mbuf *m, *mn;
+ struct mbuf *next;
+
+ next = (m = sb->sb_mb) ? m->m_nextpkt : 0;
+ while (len > 0) {
+ if (m == 0) {
+ if (next == 0)
+ panic("sbdrop");
+ m = next;
+ next = m->m_nextpkt;
+ continue;
+ }
+ if (m->m_len > len) {
+ m->m_len -= len;
+ m->m_data += len;
+ sb->sb_cc -= len;
+ break;
+ }
+ len -= m->m_len;
+ sbfree(sb, m);
+ MFREE(m, mn);
+ m = mn;
+ }
+ while (m && m->m_len == 0) {
+ sbfree(sb, m);
+ MFREE(m, mn);
+ m = mn;
+ }
+ if (m) {
+ sb->sb_mb = m;
+ m->m_nextpkt = next;
+ } else
+ sb->sb_mb = next;
+}
+
+/*
+ * Drop a record off the front of a sockbuf
+ * and move the next record to the front.
+ */
+void
+sbdroprecord(sb)
+ register struct sockbuf *sb;
+{
+ register struct mbuf *m, *mn;
+
+ m = sb->sb_mb;
+ if (m) {
+ sb->sb_mb = m->m_nextpkt;
+ do {
+ sbfree(sb, m);
+ MFREE(m, mn);
+ } while (m = mn);
+ }
+}
diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c
new file mode 100644
index 0000000..5113f78
--- /dev/null
+++ b/sys/kern/uipc_syscalls.c
@@ -0,0 +1,1245 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)uipc_syscalls.c 8.4 (Berkeley) 2/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/filedesc.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/buf.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#ifdef KTRACE
+#include <sys/ktrace.h>
+#endif
+
+/*
+ * System call interface to the socket abstraction.
+ */
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+#define COMPAT_OLDSOCK
+#endif
+
+extern struct fileops socketops;
+
+struct socket_args {
+ int domain;
+ int type;
+ int protocol;
+};
+int
+socket(p, uap, retval)
+ struct proc *p;
+ register struct socket_args *uap;
+ int *retval;
+{
+ struct filedesc *fdp = p->p_fd;
+ struct socket *so;
+ struct file *fp;
+ int fd, error;
+
+ if (error = falloc(p, &fp, &fd))
+ return (error);
+ fp->f_flag = FREAD|FWRITE;
+ fp->f_type = DTYPE_SOCKET;
+ fp->f_ops = &socketops;
+ if (error = socreate(uap->domain, &so, uap->type, uap->protocol)) {
+ fdp->fd_ofiles[fd] = 0;
+ ffree(fp);
+ } else {
+ fp->f_data = (caddr_t)so;
+ *retval = fd;
+ }
+ return (error);
+}
+
+struct bind_args {
+ int s;
+ caddr_t name;
+ int namelen;
+};
+/* ARGSUSED */
+int
+bind(p, uap, retval)
+ struct proc *p;
+ register struct bind_args *uap;
+ int *retval;
+{
+ struct file *fp;
+ struct mbuf *nam;
+ int error;
+
+ if (error = getsock(p->p_fd, uap->s, &fp))
+ return (error);
+ if (error = sockargs(&nam, uap->name, uap->namelen, MT_SONAME))
+ return (error);
+ error = sobind((struct socket *)fp->f_data, nam);
+ m_freem(nam);
+ return (error);
+}
+
+struct listen_args {
+ int s;
+ int backlog;
+};
+/* ARGSUSED */
+int
+listen(p, uap, retval)
+ struct proc *p;
+ register struct listen_args *uap;
+ int *retval;
+{
+ struct file *fp;
+ int error;
+
+ if (error = getsock(p->p_fd, uap->s, &fp))
+ return (error);
+ return (solisten((struct socket *)fp->f_data, uap->backlog));
+}
+
+struct accept_args {
+ int s;
+ caddr_t name;
+ int *anamelen;
+#ifdef COMPAT_OLDSOCK
+ int compat_43; /* pseudo */
+#endif
+};
+
+#ifdef COMPAT_OLDSOCK
+int
+accept(p, uap, retval)
+ struct proc *p;
+ struct accept_args *uap;
+ int *retval;
+{
+
+ uap->compat_43 = 0;
+ return (accept1(p, uap, retval));
+}
+
+int
+oaccept(p, uap, retval)
+ struct proc *p;
+ struct accept_args *uap;
+ int *retval;
+{
+
+ uap->compat_43 = 1;
+ return (accept1(p, uap, retval));
+}
+#else /* COMPAT_OLDSOCK */
+
+#define accept1 accept
+#endif
+
+int
+accept1(p, uap, retval)
+ struct proc *p;
+ register struct accept_args *uap;
+ int *retval;
+{
+ struct file *fp;
+ struct mbuf *nam;
+ int namelen, error, s;
+ register struct socket *so;
+
+ if (uap->name && (error = copyin((caddr_t)uap->anamelen,
+ (caddr_t)&namelen, sizeof (namelen))))
+ return (error);
+ if (error = getsock(p->p_fd, uap->s, &fp))
+ return (error);
+ s = splnet();
+ so = (struct socket *)fp->f_data;
+ if ((so->so_options & SO_ACCEPTCONN) == 0) {
+ splx(s);
+ return (EINVAL);
+ }
+ if ((so->so_state & SS_NBIO) && so->so_qlen == 0) {
+ splx(s);
+ return (EWOULDBLOCK);
+ }
+ while (so->so_qlen == 0 && so->so_error == 0) {
+ if (so->so_state & SS_CANTRCVMORE) {
+ so->so_error = ECONNABORTED;
+ break;
+ }
+ if (error = tsleep((caddr_t)&so->so_timeo, PSOCK | PCATCH,
+ netcon, 0)) {
+ splx(s);
+ return (error);
+ }
+ }
+ if (so->so_error) {
+ error = so->so_error;
+ so->so_error = 0;
+ splx(s);
+ return (error);
+ }
+ if (error = falloc(p, &fp, retval)) {
+ splx(s);
+ return (error);
+ }
+ { struct socket *aso = so->so_q;
+ if (soqremque(aso, 1) == 0)
+ panic("accept");
+ so = aso;
+ }
+ fp->f_type = DTYPE_SOCKET;
+ fp->f_flag = FREAD|FWRITE;
+ fp->f_ops = &socketops;
+ fp->f_data = (caddr_t)so;
+ nam = m_get(M_WAIT, MT_SONAME);
+ (void) soaccept(so, nam);
+ if (uap->name) {
+#ifdef COMPAT_OLDSOCK
+ if (uap->compat_43)
+ mtod(nam, struct osockaddr *)->sa_family =
+ mtod(nam, struct sockaddr *)->sa_family;
+#endif
+ if (namelen > nam->m_len)
+ namelen = nam->m_len;
+ /* SHOULD COPY OUT A CHAIN HERE */
+ if ((error = copyout(mtod(nam, caddr_t), (caddr_t)uap->name,
+ (u_int)namelen)) == 0)
+ error = copyout((caddr_t)&namelen,
+ (caddr_t)uap->anamelen, sizeof (*uap->anamelen));
+ }
+ m_freem(nam);
+ splx(s);
+ return (error);
+}
+
+struct connect_args {
+ int s;
+ caddr_t name;
+ int namelen;
+};
+/* ARGSUSED */
+int
+connect(p, uap, retval)
+ struct proc *p;
+ register struct connect_args *uap;
+ int *retval;
+{
+ struct file *fp;
+ register struct socket *so;
+ struct mbuf *nam;
+ int error, s;
+
+ if (error = getsock(p->p_fd, uap->s, &fp))
+ return (error);
+ so = (struct socket *)fp->f_data;
+ if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING))
+ return (EALREADY);
+ if (error = sockargs(&nam, uap->name, uap->namelen, MT_SONAME))
+ return (error);
+ error = soconnect(so, nam);
+ if (error)
+ goto bad;
+ if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) {
+ m_freem(nam);
+ return (EINPROGRESS);
+ }
+ s = splnet();
+ while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0)
+ if (error = tsleep((caddr_t)&so->so_timeo, PSOCK | PCATCH,
+ netcon, 0))
+ break;
+ if (error == 0) {
+ error = so->so_error;
+ so->so_error = 0;
+ }
+ splx(s);
+bad:
+ so->so_state &= ~SS_ISCONNECTING;
+ m_freem(nam);
+ if (error == ERESTART)
+ error = EINTR;
+ return (error);
+}
+
+struct socketpair_args {
+ int domain;
+ int type;
+ int protocol;
+ int *rsv;
+};
+int
+socketpair(p, uap, retval)
+ struct proc *p;
+ register struct socketpair_args *uap;
+ int retval[];
+{
+ register struct filedesc *fdp = p->p_fd;
+ struct file *fp1, *fp2;
+ struct socket *so1, *so2;
+ int fd, error, sv[2];
+
+ if (error = socreate(uap->domain, &so1, uap->type, uap->protocol))
+ return (error);
+ if (error = socreate(uap->domain, &so2, uap->type, uap->protocol))
+ goto free1;
+ if (error = falloc(p, &fp1, &fd))
+ goto free2;
+ sv[0] = fd;
+ fp1->f_flag = FREAD|FWRITE;
+ fp1->f_type = DTYPE_SOCKET;
+ fp1->f_ops = &socketops;
+ fp1->f_data = (caddr_t)so1;
+ if (error = falloc(p, &fp2, &fd))
+ goto free3;
+ fp2->f_flag = FREAD|FWRITE;
+ fp2->f_type = DTYPE_SOCKET;
+ fp2->f_ops = &socketops;
+ fp2->f_data = (caddr_t)so2;
+ sv[1] = fd;
+ if (error = soconnect2(so1, so2))
+ goto free4;
+ if (uap->type == SOCK_DGRAM) {
+ /*
+ * Datagram socket connection is asymmetric.
+ */
+ if (error = soconnect2(so2, so1))
+ goto free4;
+ }
+ error = copyout((caddr_t)sv, (caddr_t)uap->rsv, 2 * sizeof (int));
+ retval[0] = sv[0]; /* XXX ??? */
+ retval[1] = sv[1]; /* XXX ??? */
+ return (error);
+free4:
+ ffree(fp2);
+ fdp->fd_ofiles[sv[1]] = 0;
+free3:
+ ffree(fp1);
+ fdp->fd_ofiles[sv[0]] = 0;
+free2:
+ (void)soclose(so2);
+free1:
+ (void)soclose(so1);
+ return (error);
+}
+
+struct sendto_args {
+ int s;
+ caddr_t buf;
+ size_t len;
+ int flags;
+ caddr_t to;
+ int tolen;
+};
+int
+sendto(p, uap, retval)
+ struct proc *p;
+ register struct sendto_args *uap;
+ int *retval;
+{
+ struct msghdr msg;
+ struct iovec aiov;
+
+ msg.msg_name = uap->to;
+ msg.msg_namelen = uap->tolen;
+ msg.msg_iov = &aiov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = 0;
+#ifdef COMPAT_OLDSOCK
+ msg.msg_flags = 0;
+#endif
+ aiov.iov_base = uap->buf;
+ aiov.iov_len = uap->len;
+ return (sendit(p, uap->s, &msg, uap->flags, retval));
+}
+
+#ifdef COMPAT_OLDSOCK
+struct osend_args {
+ int s;
+ caddr_t buf;
+ int len;
+ int flags;
+};
+int
+osend(p, uap, retval)
+ struct proc *p;
+ register struct osend_args *uap;
+ int *retval;
+{
+ struct msghdr msg;
+ struct iovec aiov;
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &aiov;
+ msg.msg_iovlen = 1;
+ aiov.iov_base = uap->buf;
+ aiov.iov_len = uap->len;
+ msg.msg_control = 0;
+ msg.msg_flags = 0;
+ return (sendit(p, uap->s, &msg, uap->flags, retval));
+}
+
+#define MSG_COMPAT 0x8000
+struct osendmsg_args {
+ int s;
+ caddr_t msg;
+ int flags;
+};
+int
+osendmsg(p, uap, retval)
+ struct proc *p;
+ register struct osendmsg_args *uap;
+ int *retval;
+{
+ struct msghdr msg;
+ struct iovec aiov[UIO_SMALLIOV], *iov;
+ int error;
+
+ if (error = copyin(uap->msg, (caddr_t)&msg, sizeof (struct omsghdr)))
+ return (error);
+ if ((u_int)msg.msg_iovlen >= UIO_SMALLIOV) {
+ if ((u_int)msg.msg_iovlen >= UIO_MAXIOV)
+ return (EMSGSIZE);
+ MALLOC(iov, struct iovec *,
+ sizeof(struct iovec) * (u_int)msg.msg_iovlen, M_IOV,
+ M_WAITOK);
+ } else
+ iov = aiov;
+ if (error = copyin((caddr_t)msg.msg_iov, (caddr_t)iov,
+ (unsigned)(msg.msg_iovlen * sizeof (struct iovec))))
+ goto done;
+ msg.msg_flags = MSG_COMPAT;
+ msg.msg_iov = iov;
+ error = sendit(p, uap->s, &msg, uap->flags, retval);
+done:
+ if (iov != aiov)
+ FREE(iov, M_IOV);
+ return (error);
+}
+#endif
+
+struct sendmsg_args {
+ int s;
+ caddr_t msg;
+ int flags;
+};
+int
+sendmsg(p, uap, retval)
+ struct proc *p;
+ register struct sendmsg_args *uap;
+ int *retval;
+{
+ struct msghdr msg;
+ struct iovec aiov[UIO_SMALLIOV], *iov;
+ int error;
+
+ if (error = copyin(uap->msg, (caddr_t)&msg, sizeof (msg)))
+ return (error);
+ if ((u_int)msg.msg_iovlen >= UIO_SMALLIOV) {
+ if ((u_int)msg.msg_iovlen >= UIO_MAXIOV)
+ return (EMSGSIZE);
+ MALLOC(iov, struct iovec *,
+ sizeof(struct iovec) * (u_int)msg.msg_iovlen, M_IOV,
+ M_WAITOK);
+ } else
+ iov = aiov;
+ if (msg.msg_iovlen &&
+ (error = copyin((caddr_t)msg.msg_iov, (caddr_t)iov,
+ (unsigned)(msg.msg_iovlen * sizeof (struct iovec)))))
+ goto done;
+ msg.msg_iov = iov;
+#ifdef COMPAT_OLDSOCK
+ msg.msg_flags = 0;
+#endif
+ error = sendit(p, uap->s, &msg, uap->flags, retval);
+done:
+ if (iov != aiov)
+ FREE(iov, M_IOV);
+ return (error);
+}
+
+int
+sendit(p, s, mp, flags, retsize)
+ register struct proc *p;
+ int s;
+ register struct msghdr *mp;
+ int flags, *retsize;
+{
+ struct file *fp;
+ struct uio auio;
+ register struct iovec *iov;
+ register int i;
+ struct mbuf *to, *control;
+ int len, error;
+#ifdef KTRACE
+ struct iovec *ktriov = NULL;
+#endif
+
+ if (error = getsock(p->p_fd, s, &fp))
+ return (error);
+ auio.uio_iov = mp->msg_iov;
+ auio.uio_iovcnt = mp->msg_iovlen;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_rw = UIO_WRITE;
+ auio.uio_procp = p;
+ auio.uio_offset = 0; /* XXX */
+ auio.uio_resid = 0;
+ iov = mp->msg_iov;
+ for (i = 0; i < mp->msg_iovlen; i++, iov++) {
+ if ((auio.uio_resid += iov->iov_len) < 0)
+ return (EINVAL);
+ }
+ if (mp->msg_name) {
+ if (error = sockargs(&to, mp->msg_name, mp->msg_namelen,
+ MT_SONAME))
+ return (error);
+ } else
+ to = 0;
+ if (mp->msg_control) {
+ if (mp->msg_controllen < sizeof(struct cmsghdr)
+#ifdef COMPAT_OLDSOCK
+ && mp->msg_flags != MSG_COMPAT
+#endif
+ ) {
+ error = EINVAL;
+ goto bad;
+ }
+ if (error = sockargs(&control, mp->msg_control,
+ mp->msg_controllen, MT_CONTROL))
+ goto bad;
+#ifdef COMPAT_OLDSOCK
+ if (mp->msg_flags == MSG_COMPAT) {
+ register struct cmsghdr *cm;
+
+ M_PREPEND(control, sizeof(*cm), M_WAIT);
+ if (control == 0) {
+ error = ENOBUFS;
+ goto bad;
+ } else {
+ cm = mtod(control, struct cmsghdr *);
+ cm->cmsg_len = control->m_len;
+ cm->cmsg_level = SOL_SOCKET;
+ cm->cmsg_type = SCM_RIGHTS;
+ }
+ }
+#endif
+ } else
+ control = 0;
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_GENIO)) {
+ int iovlen = auio.uio_iovcnt * sizeof (struct iovec);
+
+ MALLOC(ktriov, struct iovec *, iovlen, M_TEMP, M_WAITOK);
+ bcopy((caddr_t)auio.uio_iov, (caddr_t)ktriov, iovlen);
+ }
+#endif
+ len = auio.uio_resid;
+ if (error = sosend((struct socket *)fp->f_data, to, &auio,
+ (struct mbuf *)0, control, flags)) {
+ if (auio.uio_resid != len && (error == ERESTART ||
+ error == EINTR || error == EWOULDBLOCK))
+ error = 0;
+ if (error == EPIPE)
+ psignal(p, SIGPIPE);
+ }
+ if (error == 0)
+ *retsize = len - auio.uio_resid;
+#ifdef KTRACE
+ if (ktriov != NULL) {
+ if (error == 0)
+ ktrgenio(p->p_tracep, s, UIO_WRITE,
+ ktriov, *retsize, error);
+ FREE(ktriov, M_TEMP);
+ }
+#endif
+bad:
+ if (to)
+ m_freem(to);
+ return (error);
+}
+
+struct recvfrom_args {
+ int s;
+ caddr_t buf;
+ size_t len;
+ int flags;
+ caddr_t from;
+ int *fromlenaddr;
+};
+
+#ifdef COMPAT_OLDSOCK
+int
+orecvfrom(p, uap, retval)
+ struct proc *p;
+ struct recvfrom_args *uap;
+ int *retval;
+{
+
+ uap->flags |= MSG_COMPAT;
+ return (recvfrom(p, uap, retval));
+}
+#endif
+
+int
+recvfrom(p, uap, retval)
+ struct proc *p;
+ register struct recvfrom_args *uap;
+ int *retval;
+{
+ struct msghdr msg;
+ struct iovec aiov;
+ int error;
+
+ if (uap->fromlenaddr) {
+ if (error = copyin((caddr_t)uap->fromlenaddr,
+ (caddr_t)&msg.msg_namelen, sizeof (msg.msg_namelen)))
+ return (error);
+ } else
+ msg.msg_namelen = 0;
+ msg.msg_name = uap->from;
+ msg.msg_iov = &aiov;
+ msg.msg_iovlen = 1;
+ aiov.iov_base = uap->buf;
+ aiov.iov_len = uap->len;
+ msg.msg_control = 0;
+ msg.msg_flags = uap->flags;
+ return (recvit(p, uap->s, &msg, (caddr_t)uap->fromlenaddr, retval));
+}
+
+#ifdef COMPAT_OLDSOCK
+struct orecv_args {
+ int s;
+ caddr_t buf;
+ int len;
+ int flags;
+};
+int
+orecv(p, uap, retval)
+ struct proc *p;
+ register struct orecv_args *uap;
+ int *retval;
+{
+ struct msghdr msg;
+ struct iovec aiov;
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &aiov;
+ msg.msg_iovlen = 1;
+ aiov.iov_base = uap->buf;
+ aiov.iov_len = uap->len;
+ msg.msg_control = 0;
+ msg.msg_flags = uap->flags;
+ return (recvit(p, uap->s, &msg, (caddr_t)0, retval));
+}
+
+/*
+ * Old recvmsg. This code takes advantage of the fact that the old msghdr
+ * overlays the new one, missing only the flags, and with the (old) access
+ * rights where the control fields are now.
+ */
+struct orecvmsg_args {
+ int s;
+ struct omsghdr *msg;
+ int flags;
+};
+int
+orecvmsg(p, uap, retval)
+ struct proc *p;
+ register struct orecvmsg_args *uap;
+ int *retval;
+{
+ struct msghdr msg;
+ struct iovec aiov[UIO_SMALLIOV], *iov;
+ int error;
+
+ if (error = copyin((caddr_t)uap->msg, (caddr_t)&msg,
+ sizeof (struct omsghdr)))
+ return (error);
+ if ((u_int)msg.msg_iovlen >= UIO_SMALLIOV) {
+ if ((u_int)msg.msg_iovlen >= UIO_MAXIOV)
+ return (EMSGSIZE);
+ MALLOC(iov, struct iovec *,
+ sizeof(struct iovec) * (u_int)msg.msg_iovlen, M_IOV,
+ M_WAITOK);
+ } else
+ iov = aiov;
+ msg.msg_flags = uap->flags | MSG_COMPAT;
+ if (error = copyin((caddr_t)msg.msg_iov, (caddr_t)iov,
+ (unsigned)(msg.msg_iovlen * sizeof (struct iovec))))
+ goto done;
+ msg.msg_iov = iov;
+ error = recvit(p, uap->s, &msg, (caddr_t)&uap->msg->msg_namelen, retval);
+
+ if (msg.msg_controllen && error == 0)
+ error = copyout((caddr_t)&msg.msg_controllen,
+ (caddr_t)&uap->msg->msg_accrightslen, sizeof (int));
+done:
+ if (iov != aiov)
+ FREE(iov, M_IOV);
+ return (error);
+}
+#endif
+
+struct recvmsg_args {
+ int s;
+ struct msghdr *msg;
+ int flags;
+};
+int
+recvmsg(p, uap, retval)
+ struct proc *p;
+ register struct recvmsg_args *uap;
+ int *retval;
+{
+ struct msghdr msg;
+ struct iovec aiov[UIO_SMALLIOV], *uiov, *iov;
+ register int error;
+
+ if (error = copyin((caddr_t)uap->msg, (caddr_t)&msg, sizeof (msg)))
+ return (error);
+ if ((u_int)msg.msg_iovlen >= UIO_SMALLIOV) {
+ if ((u_int)msg.msg_iovlen >= UIO_MAXIOV)
+ return (EMSGSIZE);
+ MALLOC(iov, struct iovec *,
+ sizeof(struct iovec) * (u_int)msg.msg_iovlen, M_IOV,
+ M_WAITOK);
+ } else
+ iov = aiov;
+#ifdef COMPAT_OLDSOCK
+ msg.msg_flags = uap->flags &~ MSG_COMPAT;
+#else
+ msg.msg_flags = uap->flags;
+#endif
+ uiov = msg.msg_iov;
+ msg.msg_iov = iov;
+ if (error = copyin((caddr_t)uiov, (caddr_t)iov,
+ (unsigned)(msg.msg_iovlen * sizeof (struct iovec))))
+ goto done;
+ if ((error = recvit(p, uap->s, &msg, (caddr_t)0, retval)) == 0) {
+ msg.msg_iov = uiov;
+ error = copyout((caddr_t)&msg, (caddr_t)uap->msg, sizeof(msg));
+ }
+done:
+ if (iov != aiov)
+ FREE(iov, M_IOV);
+ return (error);
+}
+
+int
+recvit(p, s, mp, namelenp, retsize)
+ register struct proc *p;
+ int s;
+ register struct msghdr *mp;
+ caddr_t namelenp;
+ int *retsize;
+{
+ struct file *fp;
+ struct uio auio;
+ register struct iovec *iov;
+ register int i;
+ int len, error;
+ struct mbuf *from = 0, *control = 0;
+#ifdef KTRACE
+ struct iovec *ktriov = NULL;
+#endif
+
+ if (error = getsock(p->p_fd, s, &fp))
+ return (error);
+ auio.uio_iov = mp->msg_iov;
+ auio.uio_iovcnt = mp->msg_iovlen;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_rw = UIO_READ;
+ auio.uio_procp = p;
+ auio.uio_offset = 0; /* XXX */
+ auio.uio_resid = 0;
+ iov = mp->msg_iov;
+ for (i = 0; i < mp->msg_iovlen; i++, iov++) {
+ if ((auio.uio_resid += iov->iov_len) < 0)
+ return (EINVAL);
+ }
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_GENIO)) {
+ int iovlen = auio.uio_iovcnt * sizeof (struct iovec);
+
+ MALLOC(ktriov, struct iovec *, iovlen, M_TEMP, M_WAITOK);
+ bcopy((caddr_t)auio.uio_iov, (caddr_t)ktriov, iovlen);
+ }
+#endif
+ len = auio.uio_resid;
+ if (error = soreceive((struct socket *)fp->f_data, &from, &auio,
+ (struct mbuf **)0, mp->msg_control ? &control : (struct mbuf **)0,
+ &mp->msg_flags)) {
+ if (auio.uio_resid != len && (error == ERESTART ||
+ error == EINTR || error == EWOULDBLOCK))
+ error = 0;
+ }
+#ifdef KTRACE
+ if (ktriov != NULL) {
+ if (error == 0)
+ ktrgenio(p->p_tracep, s, UIO_READ,
+ ktriov, len - auio.uio_resid, error);
+ FREE(ktriov, M_TEMP);
+ }
+#endif
+ if (error)
+ goto out;
+ *retsize = len - auio.uio_resid;
+ if (mp->msg_name) {
+ len = mp->msg_namelen;
+ if (len <= 0 || from == 0)
+ len = 0;
+ else {
+#ifdef COMPAT_OLDSOCK
+ if (mp->msg_flags & MSG_COMPAT)
+ mtod(from, struct osockaddr *)->sa_family =
+ mtod(from, struct sockaddr *)->sa_family;
+#endif
+ if (len > from->m_len)
+ len = from->m_len;
+ /* else if len < from->m_len ??? */
+ if (error = copyout(mtod(from, caddr_t),
+ (caddr_t)mp->msg_name, (unsigned)len))
+ goto out;
+ }
+ mp->msg_namelen = len;
+ if (namelenp &&
+ (error = copyout((caddr_t)&len, namelenp, sizeof (int)))) {
+#ifdef COMPAT_OLDSOCK
+ if (mp->msg_flags & MSG_COMPAT)
+ error = 0; /* old recvfrom didn't check */
+ else
+#endif
+ goto out;
+ }
+ }
+ if (mp->msg_control) {
+#ifdef COMPAT_OLDSOCK
+ /*
+ * We assume that old recvmsg calls won't receive access
+ * rights and other control info, esp. as control info
+ * is always optional and those options didn't exist in 4.3.
+ * If we receive rights, trim the cmsghdr; anything else
+ * is tossed.
+ */
+ if (control && mp->msg_flags & MSG_COMPAT) {
+ if (mtod(control, struct cmsghdr *)->cmsg_level !=
+ SOL_SOCKET ||
+ mtod(control, struct cmsghdr *)->cmsg_type !=
+ SCM_RIGHTS) {
+ mp->msg_controllen = 0;
+ goto out;
+ }
+ control->m_len -= sizeof (struct cmsghdr);
+ control->m_data += sizeof (struct cmsghdr);
+ }
+#endif
+ len = mp->msg_controllen;
+ if (len <= 0 || control == 0)
+ len = 0;
+ else {
+ if (len >= control->m_len)
+ len = control->m_len;
+ else
+ mp->msg_flags |= MSG_CTRUNC;
+ error = copyout((caddr_t)mtod(control, caddr_t),
+ (caddr_t)mp->msg_control, (unsigned)len);
+ }
+ mp->msg_controllen = len;
+ }
+out:
+ if (from)
+ m_freem(from);
+ if (control)
+ m_freem(control);
+ return (error);
+}
+
+struct shutdown_args {
+ int s;
+ int how;
+};
+/* ARGSUSED */
+int
+shutdown(p, uap, retval)
+ struct proc *p;
+ register struct shutdown_args *uap;
+ int *retval;
+{
+ struct file *fp;
+ int error;
+
+ if (error = getsock(p->p_fd, uap->s, &fp))
+ return (error);
+ return (soshutdown((struct socket *)fp->f_data, uap->how));
+}
+
+struct setsockopt_args {
+ int s;
+ int level;
+ int name;
+ caddr_t val;
+ int valsize;
+};
+/* ARGSUSED */
+int
+setsockopt(p, uap, retval)
+ struct proc *p;
+ register struct setsockopt_args *uap;
+ int *retval;
+{
+ struct file *fp;
+ struct mbuf *m = NULL;
+ int error;
+
+ if (error = getsock(p->p_fd, uap->s, &fp))
+ return (error);
+ if (uap->valsize > MLEN)
+ return (EINVAL);
+ if (uap->val) {
+ m = m_get(M_WAIT, MT_SOOPTS);
+ if (m == NULL)
+ return (ENOBUFS);
+ if (error = copyin(uap->val, mtod(m, caddr_t),
+ (u_int)uap->valsize)) {
+ (void) m_free(m);
+ return (error);
+ }
+ m->m_len = uap->valsize;
+ }
+ return (sosetopt((struct socket *)fp->f_data, uap->level,
+ uap->name, m));
+}
+
+struct getsockopt_args {
+ int s;
+ int level;
+ int name;
+ caddr_t val;
+ int *avalsize;
+};
+/* ARGSUSED */
+int
+getsockopt(p, uap, retval)
+ struct proc *p;
+ register struct getsockopt_args *uap;
+ int *retval;
+{
+ struct file *fp;
+ struct mbuf *m = NULL;
+ int valsize, error;
+
+ if (error = getsock(p->p_fd, uap->s, &fp))
+ return (error);
+ if (uap->val) {
+ if (error = copyin((caddr_t)uap->avalsize, (caddr_t)&valsize,
+ sizeof (valsize)))
+ return (error);
+ } else
+ valsize = 0;
+ if ((error = sogetopt((struct socket *)fp->f_data, uap->level,
+ uap->name, &m)) == 0 && uap->val && valsize && m != NULL) {
+ if (valsize > m->m_len)
+ valsize = m->m_len;
+ error = copyout(mtod(m, caddr_t), uap->val, (u_int)valsize);
+ if (error == 0)
+ error = copyout((caddr_t)&valsize,
+ (caddr_t)uap->avalsize, sizeof (valsize));
+ }
+ if (m != NULL)
+ (void) m_free(m);
+ return (error);
+}
+
+struct pipe_args {
+ int dummy;
+};
+/* ARGSUSED */
+int
+pipe(p, uap, retval)
+ struct proc *p;
+ struct pipe_args *uap;
+ int retval[];
+{
+ register struct filedesc *fdp = p->p_fd;
+ struct file *rf, *wf;
+ struct socket *rso, *wso;
+ int fd, error;
+
+ if (error = socreate(AF_UNIX, &rso, SOCK_STREAM, 0))
+ return (error);
+ if (error = socreate(AF_UNIX, &wso, SOCK_STREAM, 0))
+ goto free1;
+ if (error = falloc(p, &rf, &fd))
+ goto free2;
+ retval[0] = fd;
+ rf->f_flag = FREAD;
+ rf->f_type = DTYPE_SOCKET;
+ rf->f_ops = &socketops;
+ rf->f_data = (caddr_t)rso;
+ if (error = falloc(p, &wf, &fd))
+ goto free3;
+ wf->f_flag = FWRITE;
+ wf->f_type = DTYPE_SOCKET;
+ wf->f_ops = &socketops;
+ wf->f_data = (caddr_t)wso;
+ retval[1] = fd;
+ if (error = unp_connect2(wso, rso))
+ goto free4;
+ return (0);
+free4:
+ ffree(wf);
+ fdp->fd_ofiles[retval[1]] = 0;
+free3:
+ ffree(rf);
+ fdp->fd_ofiles[retval[0]] = 0;
+free2:
+ (void)soclose(wso);
+free1:
+ (void)soclose(rso);
+ return (error);
+}
+
+/*
+ * Get socket name.
+ */
+struct getsockname_args {
+ int fdes;
+ caddr_t asa;
+ int *alen;
+#ifdef COMPAT_OLDSOCK
+ int compat_43; /* pseudo */
+#endif
+};
+#ifdef COMPAT_OLDSOCK
+int
+getsockname(p, uap, retval)
+ struct proc *p;
+ struct getsockname_args *uap;
+ int *retval;
+{
+
+ uap->compat_43 = 0;
+ return (getsockname1(p, uap, retval));
+}
+
+int
+ogetsockname(p, uap, retval)
+ struct proc *p;
+ struct getsockname_args *uap;
+ int *retval;
+{
+
+ uap->compat_43 = 1;
+ return (getsockname1(p, uap, retval));
+}
+#else /* COMPAT_OLDSOCK */
+
+#define getsockname1 getsockname
+#endif
+
+/* ARGSUSED */
+int
+getsockname1(p, uap, retval)
+ struct proc *p;
+ register struct getsockname_args *uap;
+ int *retval;
+{
+ struct file *fp;
+ register struct socket *so;
+ struct mbuf *m;
+ int len, error;
+
+ if (error = getsock(p->p_fd, uap->fdes, &fp))
+ return (error);
+ if (error = copyin((caddr_t)uap->alen, (caddr_t)&len, sizeof (len)))
+ return (error);
+ so = (struct socket *)fp->f_data;
+ m = m_getclr(M_WAIT, MT_SONAME);
+ if (m == NULL)
+ return (ENOBUFS);
+ if (error = (*so->so_proto->pr_usrreq)(so, PRU_SOCKADDR, 0, m, 0))
+ goto bad;
+ if (len > m->m_len)
+ len = m->m_len;
+#ifdef COMPAT_OLDSOCK
+ if (uap->compat_43)
+ mtod(m, struct osockaddr *)->sa_family =
+ mtod(m, struct sockaddr *)->sa_family;
+#endif
+ error = copyout(mtod(m, caddr_t), (caddr_t)uap->asa, (u_int)len);
+ if (error == 0)
+ error = copyout((caddr_t)&len, (caddr_t)uap->alen,
+ sizeof (len));
+bad:
+ m_freem(m);
+ return (error);
+}
+
+/*
+ * Get name of peer for connected socket.
+ */
+struct getpeername_args {
+ int fdes;
+ caddr_t asa;
+ int *alen;
+#ifdef COMPAT_OLDSOCK
+ int compat_43; /* pseudo */
+#endif
+};
+
+#ifdef COMPAT_OLDSOCK
+int
+getpeername(p, uap, retval)
+ struct proc *p;
+ struct getpeername_args *uap;
+ int *retval;
+{
+
+ uap->compat_43 = 0;
+ return (getpeername1(p, uap, retval));
+}
+
+int
+ogetpeername(p, uap, retval)
+ struct proc *p;
+ struct getpeername_args *uap;
+ int *retval;
+{
+
+ uap->compat_43 = 1;
+ return (getpeername1(p, uap, retval));
+}
+#else /* COMPAT_OLDSOCK */
+
+#define getpeername1 getpeername
+#endif
+
+/* ARGSUSED */
+int
+getpeername1(p, uap, retval)
+ struct proc *p;
+ register struct getpeername_args *uap;
+ int *retval;
+{
+ struct file *fp;
+ register struct socket *so;
+ struct mbuf *m;
+ int len, error;
+
+ if (error = getsock(p->p_fd, uap->fdes, &fp))
+ return (error);
+ so = (struct socket *)fp->f_data;
+ if ((so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0)
+ return (ENOTCONN);
+ if (error = copyin((caddr_t)uap->alen, (caddr_t)&len, sizeof (len)))
+ return (error);
+ m = m_getclr(M_WAIT, MT_SONAME);
+ if (m == NULL)
+ return (ENOBUFS);
+ if (error = (*so->so_proto->pr_usrreq)(so, PRU_PEERADDR, 0, m, 0))
+ goto bad;
+ if (len > m->m_len)
+ len = m->m_len;
+#ifdef COMPAT_OLDSOCK
+ if (uap->compat_43)
+ mtod(m, struct osockaddr *)->sa_family =
+ mtod(m, struct sockaddr *)->sa_family;
+#endif
+ if (error = copyout(mtod(m, caddr_t), (caddr_t)uap->asa, (u_int)len))
+ goto bad;
+ error = copyout((caddr_t)&len, (caddr_t)uap->alen, sizeof (len));
+bad:
+ m_freem(m);
+ return (error);
+}
+
+int
+sockargs(mp, buf, buflen, type)
+ struct mbuf **mp;
+ caddr_t buf;
+ int buflen, type;
+{
+ register struct sockaddr *sa;
+ register struct mbuf *m;
+ int error;
+
+ if ((u_int)buflen > MLEN) {
+#ifdef COMPAT_OLDSOCK
+ if (type == MT_SONAME && (u_int)buflen <= 112)
+ buflen = MLEN; /* unix domain compat. hack */
+ else
+#endif
+ return (EINVAL);
+ }
+ m = m_get(M_WAIT, type);
+ if (m == NULL)
+ return (ENOBUFS);
+ m->m_len = buflen;
+ error = copyin(buf, mtod(m, caddr_t), (u_int)buflen);
+ if (error)
+ (void) m_free(m);
+ else {
+ *mp = m;
+ if (type == MT_SONAME) {
+ sa = mtod(m, struct sockaddr *);
+
+#if defined(COMPAT_OLDSOCK) && BYTE_ORDER != BIG_ENDIAN
+ if (sa->sa_family == 0 && sa->sa_len < AF_MAX)
+ sa->sa_family = sa->sa_len;
+#endif
+ sa->sa_len = buflen;
+ }
+ }
+ return (error);
+}
+
+int
+getsock(fdp, fdes, fpp)
+ struct filedesc *fdp;
+ int fdes;
+ struct file **fpp;
+{
+ register struct file *fp;
+
+ if ((unsigned)fdes >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[fdes]) == NULL)
+ return (EBADF);
+ if (fp->f_type != DTYPE_SOCKET)
+ return (ENOTSOCK);
+ *fpp = fp;
+ return (0);
+}
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
new file mode 100644
index 0000000..b61e9f4
--- /dev/null
+++ b/sys/kern/uipc_usrreq.c
@@ -0,0 +1,847 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)uipc_usrreq.c 8.3 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/filedesc.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/unpcb.h>
+#include <sys/un.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mbuf.h>
+
+void unp_detach __P((struct unpcb *));
+void unp_disconnect __P((struct unpcb *));
+void unp_shutdown __P((struct unpcb *));
+void unp_drop __P((struct unpcb *, int));
+void unp_gc __P((void));
+void unp_scan __P((struct mbuf *, void (*)(struct file *)));
+void unp_mark __P((struct file *));
+void unp_discard __P((struct file *));
+
+/*
+ * Unix communications domain.
+ *
+ * TODO:
+ * SEQPACKET, RDM
+ * rethink name space problems
+ * need a proper out-of-band
+ */
+struct sockaddr sun_noname = { sizeof(sun_noname), AF_UNIX };
+ino_t unp_ino; /* prototype for fake inode numbers */
+
+/*ARGSUSED*/
+int
+uipc_usrreq(so, req, m, nam, control)
+ struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *control;
+{
+ struct unpcb *unp = sotounpcb(so);
+ register struct socket *so2;
+ register int error = 0;
+ struct proc *p = curproc; /* XXX */
+
+ if (req == PRU_CONTROL)
+ return (EOPNOTSUPP);
+ if (req != PRU_SEND && control && control->m_len) {
+ error = EOPNOTSUPP;
+ goto release;
+ }
+ if (unp == 0 && req != PRU_ATTACH) {
+ error = EINVAL;
+ goto release;
+ }
+ switch (req) {
+
+ case PRU_ATTACH:
+ if (unp) {
+ error = EISCONN;
+ break;
+ }
+ error = unp_attach(so);
+ break;
+
+ case PRU_DETACH:
+ unp_detach(unp);
+ break;
+
+ case PRU_BIND:
+ error = unp_bind(unp, nam, p);
+ break;
+
+ case PRU_LISTEN:
+ if (unp->unp_vnode == 0)
+ error = EINVAL;
+ break;
+
+ case PRU_CONNECT:
+ error = unp_connect(so, nam, p);
+ break;
+
+ case PRU_CONNECT2:
+ error = unp_connect2(so, (struct socket *)nam);
+ break;
+
+ case PRU_DISCONNECT:
+ unp_disconnect(unp);
+ break;
+
+ case PRU_ACCEPT:
+ /*
+ * Pass back name of connected socket,
+ * if it was bound and we are still connected
+ * (our peer may have closed already!).
+ */
+ if (unp->unp_conn && unp->unp_conn->unp_addr) {
+ nam->m_len = unp->unp_conn->unp_addr->m_len;
+ bcopy(mtod(unp->unp_conn->unp_addr, caddr_t),
+ mtod(nam, caddr_t), (unsigned)nam->m_len);
+ } else {
+ nam->m_len = sizeof(sun_noname);
+ *(mtod(nam, struct sockaddr *)) = sun_noname;
+ }
+ break;
+
+ case PRU_SHUTDOWN:
+ socantsendmore(so);
+ unp_shutdown(unp);
+ break;
+
+ case PRU_RCVD:
+ switch (so->so_type) {
+
+ case SOCK_DGRAM:
+ panic("uipc 1");
+ /*NOTREACHED*/
+
+ case SOCK_STREAM:
+#define rcv (&so->so_rcv)
+#define snd (&so2->so_snd)
+ if (unp->unp_conn == 0)
+ break;
+ so2 = unp->unp_conn->unp_socket;
+ /*
+ * Adjust backpressure on sender
+ * and wakeup any waiting to write.
+ */
+ snd->sb_mbmax += unp->unp_mbcnt - rcv->sb_mbcnt;
+ unp->unp_mbcnt = rcv->sb_mbcnt;
+ snd->sb_hiwat += unp->unp_cc - rcv->sb_cc;
+ unp->unp_cc = rcv->sb_cc;
+ sowwakeup(so2);
+#undef snd
+#undef rcv
+ break;
+
+ default:
+ panic("uipc 2");
+ }
+ break;
+
+ case PRU_SEND:
+ if (control && (error = unp_internalize(control, p)))
+ break;
+ switch (so->so_type) {
+
+ case SOCK_DGRAM: {
+ struct sockaddr *from;
+
+ if (nam) {
+ if (unp->unp_conn) {
+ error = EISCONN;
+ break;
+ }
+ error = unp_connect(so, nam, p);
+ if (error)
+ break;
+ } else {
+ if (unp->unp_conn == 0) {
+ error = ENOTCONN;
+ break;
+ }
+ }
+ so2 = unp->unp_conn->unp_socket;
+ if (unp->unp_addr)
+ from = mtod(unp->unp_addr, struct sockaddr *);
+ else
+ from = &sun_noname;
+ if (sbappendaddr(&so2->so_rcv, from, m, control)) {
+ sorwakeup(so2);
+ m = 0;
+ control = 0;
+ } else
+ error = ENOBUFS;
+ if (nam)
+ unp_disconnect(unp);
+ break;
+ }
+
+ case SOCK_STREAM:
+#define rcv (&so2->so_rcv)
+#define snd (&so->so_snd)
+ if (so->so_state & SS_CANTSENDMORE) {
+ error = EPIPE;
+ break;
+ }
+ if (unp->unp_conn == 0)
+ panic("uipc 3");
+ so2 = unp->unp_conn->unp_socket;
+ /*
+ * Send to paired receive port, and then reduce
+ * send buffer hiwater marks to maintain backpressure.
+ * Wake up readers.
+ */
+ if (control) {
+ if (sbappendcontrol(rcv, m, control))
+ control = 0;
+ } else
+ sbappend(rcv, m);
+ snd->sb_mbmax -=
+ rcv->sb_mbcnt - unp->unp_conn->unp_mbcnt;
+ unp->unp_conn->unp_mbcnt = rcv->sb_mbcnt;
+ snd->sb_hiwat -= rcv->sb_cc - unp->unp_conn->unp_cc;
+ unp->unp_conn->unp_cc = rcv->sb_cc;
+ sorwakeup(so2);
+ m = 0;
+#undef snd
+#undef rcv
+ break;
+
+ default:
+ panic("uipc 4");
+ }
+ break;
+
+ case PRU_ABORT:
+ unp_drop(unp, ECONNABORTED);
+ break;
+
+ case PRU_SENSE:
+ ((struct stat *) m)->st_blksize = so->so_snd.sb_hiwat;
+ if (so->so_type == SOCK_STREAM && unp->unp_conn != 0) {
+ so2 = unp->unp_conn->unp_socket;
+ ((struct stat *) m)->st_blksize += so2->so_rcv.sb_cc;
+ }
+ ((struct stat *) m)->st_dev = NODEV;
+ if (unp->unp_ino == 0)
+ unp->unp_ino = unp_ino++;
+ ((struct stat *) m)->st_ino = unp->unp_ino;
+ return (0);
+
+ case PRU_RCVOOB:
+ return (EOPNOTSUPP);
+
+ case PRU_SENDOOB:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_SOCKADDR:
+ if (unp->unp_addr) {
+ nam->m_len = unp->unp_addr->m_len;
+ bcopy(mtod(unp->unp_addr, caddr_t),
+ mtod(nam, caddr_t), (unsigned)nam->m_len);
+ } else
+ nam->m_len = 0;
+ break;
+
+ case PRU_PEERADDR:
+ if (unp->unp_conn && unp->unp_conn->unp_addr) {
+ nam->m_len = unp->unp_conn->unp_addr->m_len;
+ bcopy(mtod(unp->unp_conn->unp_addr, caddr_t),
+ mtod(nam, caddr_t), (unsigned)nam->m_len);
+ } else
+ nam->m_len = 0;
+ break;
+
+ case PRU_SLOWTIMO:
+ break;
+
+ default:
+ panic("piusrreq");
+ }
+release:
+ if (control)
+ m_freem(control);
+ if (m)
+ m_freem(m);
+ return (error);
+}
+
+/*
+ * Both send and receive buffers are allocated PIPSIZ bytes of buffering
+ * for stream sockets, although the total for sender and receiver is
+ * actually only PIPSIZ.
+ * Datagram sockets really use the sendspace as the maximum datagram size,
+ * and don't really want to reserve the sendspace. Their recvspace should
+ * be large enough for at least one max-size datagram plus address.
+ */
+#define PIPSIZ 4096
+u_long unpst_sendspace = PIPSIZ;
+u_long unpst_recvspace = PIPSIZ;
+u_long unpdg_sendspace = 2*1024; /* really max datagram size */
+u_long unpdg_recvspace = 4*1024;
+
+int unp_rights; /* file descriptors in flight */
+
+int
+unp_attach(so)
+ struct socket *so;
+{
+ register struct mbuf *m;
+ register struct unpcb *unp;
+ int error;
+
+ if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
+ switch (so->so_type) {
+
+ case SOCK_STREAM:
+ error = soreserve(so, unpst_sendspace, unpst_recvspace);
+ break;
+
+ case SOCK_DGRAM:
+ error = soreserve(so, unpdg_sendspace, unpdg_recvspace);
+ break;
+
+ default:
+ panic("unp_attach");
+ }
+ if (error)
+ return (error);
+ }
+ m = m_getclr(M_DONTWAIT, MT_PCB);
+ if (m == NULL)
+ return (ENOBUFS);
+ unp = mtod(m, struct unpcb *);
+ so->so_pcb = (caddr_t)unp;
+ unp->unp_socket = so;
+ return (0);
+}
+
+void
+unp_detach(unp)
+ register struct unpcb *unp;
+{
+
+ if (unp->unp_vnode) {
+ unp->unp_vnode->v_socket = 0;
+ vrele(unp->unp_vnode);
+ unp->unp_vnode = 0;
+ }
+ if (unp->unp_conn)
+ unp_disconnect(unp);
+ while (unp->unp_refs)
+ unp_drop(unp->unp_refs, ECONNRESET);
+ soisdisconnected(unp->unp_socket);
+ unp->unp_socket->so_pcb = 0;
+ m_freem(unp->unp_addr);
+ (void) m_free(dtom(unp));
+ if (unp_rights) {
+ /*
+ * Normally the receive buffer is flushed later,
+ * in sofree, but if our receive buffer holds references
+ * to descriptors that are now garbage, we will dispose
+ * of those descriptor references after the garbage collector
+ * gets them (resulting in a "panic: closef: count < 0").
+ */
+ sorflush(unp->unp_socket);
+ unp_gc();
+ }
+}
+
+int
+unp_bind(unp, nam, p)
+ struct unpcb *unp;
+ struct mbuf *nam;
+ struct proc *p;
+{
+ struct sockaddr_un *soun = mtod(nam, struct sockaddr_un *);
+ register struct vnode *vp;
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, CREATE, FOLLOW | LOCKPARENT, UIO_SYSSPACE,
+ soun->sun_path, p);
+ if (unp->unp_vnode != NULL)
+ return (EINVAL);
+ if (nam->m_len == MLEN) {
+ if (*(mtod(nam, caddr_t) + nam->m_len - 1) != 0)
+ return (EINVAL);
+ } else
+ *(mtod(nam, caddr_t) + nam->m_len) = 0;
+/* SHOULD BE ABLE TO ADOPT EXISTING AND wakeup() ALA FIFO's */
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (vp != NULL) {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vrele(vp);
+ return (EADDRINUSE);
+ }
+ VATTR_NULL(&vattr);
+ vattr.va_type = VSOCK;
+ vattr.va_mode = ACCESSPERMS;
+ LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ if (error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr))
+ return (error);
+ vp = nd.ni_vp;
+ vp->v_socket = unp->unp_socket;
+ unp->unp_vnode = vp;
+ unp->unp_addr = m_copy(nam, 0, (int)M_COPYALL);
+ VOP_UNLOCK(vp);
+ return (0);
+}
+
+int
+unp_connect(so, nam, p)
+ struct socket *so;
+ struct mbuf *nam;
+ struct proc *p;
+{
+ register struct sockaddr_un *soun = mtod(nam, struct sockaddr_un *);
+ register struct vnode *vp;
+ register struct socket *so2, *so3;
+ struct unpcb *unp2, *unp3;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, soun->sun_path, p);
+ if (nam->m_data + nam->m_len == &nam->m_dat[MLEN]) { /* XXX */
+ if (*(mtod(nam, caddr_t) + nam->m_len - 1) != 0)
+ return (EMSGSIZE);
+ } else
+ *(mtod(nam, caddr_t) + nam->m_len) = 0;
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (vp->v_type != VSOCK) {
+ error = ENOTSOCK;
+ goto bad;
+ }
+ if (error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p))
+ goto bad;
+ so2 = vp->v_socket;
+ if (so2 == 0) {
+ error = ECONNREFUSED;
+ goto bad;
+ }
+ if (so->so_type != so2->so_type) {
+ error = EPROTOTYPE;
+ goto bad;
+ }
+ if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
+ if ((so2->so_options & SO_ACCEPTCONN) == 0 ||
+ (so3 = sonewconn(so2, 0)) == 0) {
+ error = ECONNREFUSED;
+ goto bad;
+ }
+ unp2 = sotounpcb(so2);
+ unp3 = sotounpcb(so3);
+ if (unp2->unp_addr)
+ unp3->unp_addr =
+ m_copy(unp2->unp_addr, 0, (int)M_COPYALL);
+ so2 = so3;
+ }
+ error = unp_connect2(so, so2);
+bad:
+ vput(vp);
+ return (error);
+}
+
+int
+unp_connect2(so, so2)
+ register struct socket *so;
+ register struct socket *so2;
+{
+ register struct unpcb *unp = sotounpcb(so);
+ register struct unpcb *unp2;
+
+ if (so2->so_type != so->so_type)
+ return (EPROTOTYPE);
+ unp2 = sotounpcb(so2);
+ unp->unp_conn = unp2;
+ switch (so->so_type) {
+
+ case SOCK_DGRAM:
+ unp->unp_nextref = unp2->unp_refs;
+ unp2->unp_refs = unp;
+ soisconnected(so);
+ break;
+
+ case SOCK_STREAM:
+ unp2->unp_conn = unp;
+ soisconnected(so);
+ soisconnected(so2);
+ break;
+
+ default:
+ panic("unp_connect2");
+ }
+ return (0);
+}
+
+void
+unp_disconnect(unp)
+ struct unpcb *unp;
+{
+ register struct unpcb *unp2 = unp->unp_conn;
+
+ if (unp2 == 0)
+ return;
+ unp->unp_conn = 0;
+ switch (unp->unp_socket->so_type) {
+
+ case SOCK_DGRAM:
+ if (unp2->unp_refs == unp)
+ unp2->unp_refs = unp->unp_nextref;
+ else {
+ unp2 = unp2->unp_refs;
+ for (;;) {
+ if (unp2 == 0)
+ panic("unp_disconnect");
+ if (unp2->unp_nextref == unp)
+ break;
+ unp2 = unp2->unp_nextref;
+ }
+ unp2->unp_nextref = unp->unp_nextref;
+ }
+ unp->unp_nextref = 0;
+ unp->unp_socket->so_state &= ~SS_ISCONNECTED;
+ break;
+
+ case SOCK_STREAM:
+ soisdisconnected(unp->unp_socket);
+ unp2->unp_conn = 0;
+ soisdisconnected(unp2->unp_socket);
+ break;
+ }
+}
+
+#ifdef notdef
+void
+unp_abort(unp)
+ struct unpcb *unp;
+{
+
+ unp_detach(unp);
+}
+#endif
+
+void
+unp_shutdown(unp)
+ struct unpcb *unp;
+{
+ struct socket *so;
+
+ if (unp->unp_socket->so_type == SOCK_STREAM && unp->unp_conn &&
+ (so = unp->unp_conn->unp_socket))
+ socantrcvmore(so);
+}
+
+void
+unp_drop(unp, errno)
+ struct unpcb *unp;
+ int errno;
+{
+ struct socket *so = unp->unp_socket;
+
+ so->so_error = errno;
+ unp_disconnect(unp);
+ if (so->so_head) {
+ so->so_pcb = (caddr_t) 0;
+ m_freem(unp->unp_addr);
+ (void) m_free(dtom(unp));
+ sofree(so);
+ }
+}
+
+#ifdef notdef
+void
+unp_drain()
+{
+
+}
+#endif
+
+int
+unp_externalize(rights)
+ struct mbuf *rights;
+{
+ struct proc *p = curproc; /* XXX */
+ register int i;
+ register struct cmsghdr *cm = mtod(rights, struct cmsghdr *);
+ register struct file **rp = (struct file **)(cm + 1);
+ register struct file *fp;
+ int newfds = (cm->cmsg_len - sizeof(*cm)) / sizeof (int);
+ int f;
+
+ if (!fdavail(p, newfds)) {
+ for (i = 0; i < newfds; i++) {
+ fp = *rp;
+ unp_discard(fp);
+ *rp++ = 0;
+ }
+ return (EMSGSIZE);
+ }
+ for (i = 0; i < newfds; i++) {
+ if (fdalloc(p, 0, &f))
+ panic("unp_externalize");
+ fp = *rp;
+ p->p_fd->fd_ofiles[f] = fp;
+ fp->f_msgcount--;
+ unp_rights--;
+ *(int *)rp++ = f;
+ }
+ return (0);
+}
+
+int
+unp_internalize(control, p)
+ struct mbuf *control;
+ struct proc *p;
+{
+ struct filedesc *fdp = p->p_fd;
+ register struct cmsghdr *cm = mtod(control, struct cmsghdr *);
+ register struct file **rp;
+ register struct file *fp;
+ register int i, fd;
+ int oldfds;
+
+ if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET ||
+ cm->cmsg_len != control->m_len)
+ return (EINVAL);
+ oldfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int);
+ rp = (struct file **)(cm + 1);
+ for (i = 0; i < oldfds; i++) {
+ fd = *(int *)rp++;
+ if ((unsigned)fd >= fdp->fd_nfiles ||
+ fdp->fd_ofiles[fd] == NULL)
+ return (EBADF);
+ }
+ rp = (struct file **)(cm + 1);
+ for (i = 0; i < oldfds; i++) {
+ fp = fdp->fd_ofiles[*(int *)rp];
+ *rp++ = fp;
+ fp->f_count++;
+ fp->f_msgcount++;
+ unp_rights++;
+ }
+ return (0);
+}
+
+int unp_defer, unp_gcing;
+extern struct domain unixdomain;
+
+void
+unp_gc()
+{
+ register struct file *fp, *nextfp;
+ register struct socket *so;
+ struct file **extra_ref, **fpp;
+ int nunref, i;
+
+ if (unp_gcing)
+ return;
+ unp_gcing = 1;
+ unp_defer = 0;
+ for (fp = filehead; fp; fp = fp->f_filef)
+ fp->f_flag &= ~(FMARK|FDEFER);
+ do {
+ for (fp = filehead; fp; fp = fp->f_filef) {
+ if (fp->f_count == 0)
+ continue;
+ if (fp->f_flag & FDEFER) {
+ fp->f_flag &= ~FDEFER;
+ unp_defer--;
+ } else {
+ if (fp->f_flag & FMARK)
+ continue;
+ if (fp->f_count == fp->f_msgcount)
+ continue;
+ fp->f_flag |= FMARK;
+ }
+ if (fp->f_type != DTYPE_SOCKET ||
+ (so = (struct socket *)fp->f_data) == 0)
+ continue;
+ if (so->so_proto->pr_domain != &unixdomain ||
+ (so->so_proto->pr_flags&PR_RIGHTS) == 0)
+ continue;
+#ifdef notdef
+ if (so->so_rcv.sb_flags & SB_LOCK) {
+ /*
+ * This is problematical; it's not clear
+ * we need to wait for the sockbuf to be
+ * unlocked (on a uniprocessor, at least),
+ * and it's also not clear what to do
+ * if sbwait returns an error due to receipt
+ * of a signal. If sbwait does return
+ * an error, we'll go into an infinite
+ * loop. Delete all of this for now.
+ */
+ (void) sbwait(&so->so_rcv);
+ goto restart;
+ }
+#endif
+ unp_scan(so->so_rcv.sb_mb, unp_mark);
+ }
+ } while (unp_defer);
+ /*
+ * We grab an extra reference to each of the file table entries
+ * that are not otherwise accessible and then free the rights
+ * that are stored in messages on them.
+ *
+ * The bug in the orginal code is a little tricky, so I'll describe
+ * what's wrong with it here.
+ *
+ * It is incorrect to simply unp_discard each entry for f_msgcount
+ * times -- consider the case of sockets A and B that contain
+ * references to each other. On a last close of some other socket,
+ * we trigger a gc since the number of outstanding rights (unp_rights)
+ * is non-zero. If during the sweep phase the gc code un_discards,
+ * we end up doing a (full) closef on the descriptor. A closef on A
+ * results in the following chain. Closef calls soo_close, which
+ * calls soclose. Soclose calls first (through the switch
+ * uipc_usrreq) unp_detach, which re-invokes unp_gc. Unp_gc simply
+ * returns because the previous instance had set unp_gcing, and
+ * we return all the way back to soclose, which marks the socket
+ * with SS_NOFDREF, and then calls sofree. Sofree calls sorflush
+ * to free up the rights that are queued in messages on the socket A,
+ * i.e., the reference on B. The sorflush calls via the dom_dispose
+ * switch unp_dispose, which unp_scans with unp_discard. This second
+ * instance of unp_discard just calls closef on B.
+ *
+ * Well, a similar chain occurs on B, resulting in a sorflush on B,
+ * which results in another closef on A. Unfortunately, A is already
+ * being closed, and the descriptor has already been marked with
+ * SS_NOFDREF, and soclose panics at this point.
+ *
+ * Here, we first take an extra reference to each inaccessible
+ * descriptor. Then, we call sorflush ourself, since we know
+ * it is a Unix domain socket anyhow. After we destroy all the
+ * rights carried in messages, we do a last closef to get rid
+ * of our extra reference. This is the last close, and the
+ * unp_detach etc will shut down the socket.
+ *
+ * 91/09/19, bsy@cs.cmu.edu
+ */
+ extra_ref = malloc(nfiles * sizeof(struct file *), M_FILE, M_WAITOK);
+ for (nunref = 0, fp = filehead, fpp = extra_ref; fp; fp = nextfp) {
+ nextfp = fp->f_filef;
+ if (fp->f_count == 0)
+ continue;
+ if (fp->f_count == fp->f_msgcount && !(fp->f_flag & FMARK)) {
+ *fpp++ = fp;
+ nunref++;
+ fp->f_count++;
+ }
+ }
+ for (i = nunref, fpp = extra_ref; --i >= 0; ++fpp)
+ sorflush((struct socket *)(*fpp)->f_data);
+ for (i = nunref, fpp = extra_ref; --i >= 0; ++fpp)
+ closef(*fpp);
+ free((caddr_t)extra_ref, M_FILE);
+ unp_gcing = 0;
+}
+
+void
+unp_dispose(m)
+ struct mbuf *m;
+{
+ if (m)
+ unp_scan(m, unp_discard);
+}
+
+void
+unp_scan(m0, op)
+ register struct mbuf *m0;
+ void (*op)(struct file *);
+{
+ register struct mbuf *m;
+ register struct file **rp;
+ register struct cmsghdr *cm;
+ register int i;
+ int qfds;
+
+ while (m0) {
+ for (m = m0; m; m = m->m_next)
+ if (m->m_type == MT_CONTROL &&
+ m->m_len >= sizeof(*cm)) {
+ cm = mtod(m, struct cmsghdr *);
+ if (cm->cmsg_level != SOL_SOCKET ||
+ cm->cmsg_type != SCM_RIGHTS)
+ continue;
+ qfds = (cm->cmsg_len - sizeof *cm)
+ / sizeof (struct file *);
+ rp = (struct file **)(cm + 1);
+ for (i = 0; i < qfds; i++)
+ (*op)(*rp++);
+ break; /* XXX, but saves time */
+ }
+ m0 = m0->m_act;
+ }
+}
+
+void
+unp_mark(fp)
+ struct file *fp;
+{
+
+ if (fp->f_flag & FMARK)
+ return;
+ unp_defer++;
+ fp->f_flag |= (FMARK|FDEFER);
+}
+
+void
+unp_discard(fp)
+ struct file *fp;
+{
+
+ fp->f_msgcount--;
+ unp_rights--;
+ (void) closef(fp, (struct proc *)NULL);
+}
diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c
new file mode 100644
index 0000000..b3ab4ca
--- /dev/null
+++ b/sys/kern/vfs_bio.c
@@ -0,0 +1,710 @@
+/*
+ * Copyright (c) 1994 John S. Dyson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Absolutely no warranty of function or purpose is made by the author
+ * John S. Dyson.
+ * 4. Modifications may be freely made to this file if the above conditions
+ * are met.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/buf.h>
+#include <sys/mount.h>
+#include <sys/malloc.h>
+#include <sys/resourcevar.h>
+#include <vm/vm.h>
+#include <vm/vm_pageout.h>
+
+#include <miscfs/specfs/specdev.h>
+
+struct buf *buf; /* buffer header pool */
+int nbuf; /* number of buffer headers calculated elsewhere */
+
+extern vm_map_t buffer_map, io_map;
+
+void vm_hold_free_pages(vm_offset_t from, vm_offset_t to);
+void vm_hold_load_pages(vm_offset_t from, vm_offset_t to);
+
+int needsbuffer;
+
+/*
+ * Internal update daemon, process 3
+ * The variable vfs_update_wakeup allows for internal syncs.
+ */
+int vfs_update_wakeup;
+
+/*
+ * Initialize buffer headers and related structures.
+ */
+void bufinit()
+{
+ struct buf *bp;
+ int i;
+
+ TAILQ_INIT(&bswlist);
+ LIST_INIT(&invalhash);
+
+ /* first, make a null hash table */
+ for(i=0;i<BUFHSZ;i++)
+ LIST_INIT(&bufhashtbl[i]);
+
+ /* next, make a null set of free lists */
+ for(i=0;i<BUFFER_QUEUES;i++)
+ TAILQ_INIT(&bufqueues[i]);
+
+ /* finally, initialize each buffer header and stick on empty q */
+ for(i=0;i<nbuf;i++) {
+ bp = &buf[i];
+ bzero(bp, sizeof *bp);
+ bp->b_flags = B_INVAL; /* we're just an empty header */
+ bp->b_dev = NODEV;
+ bp->b_vp = NULL;
+ bp->b_rcred = NOCRED;
+ bp->b_wcred = NOCRED;
+ bp->b_qindex = QUEUE_EMPTY;
+ bp->b_vnbufs.le_next = NOLIST;
+ bp->b_data = (caddr_t)kmem_alloc_pageable(buffer_map, MAXBSIZE);
+ TAILQ_INSERT_TAIL(&bufqueues[QUEUE_EMPTY], bp, b_freelist);
+ LIST_INSERT_HEAD(&invalhash, bp, b_hash);
+ }
+}
+
+/*
+ * remove the buffer from the appropriate free list
+ */
+void
+bremfree(struct buf *bp)
+{
+ int s = splbio();
+ if( bp->b_qindex != QUEUE_NONE) {
+ TAILQ_REMOVE(&bufqueues[bp->b_qindex], bp, b_freelist);
+ bp->b_qindex = QUEUE_NONE;
+ } else {
+ panic("bremfree: removing a buffer when not on a queue");
+ }
+ splx(s);
+}
+
+/*
+ * Get a buffer with the specified data. Look in the cache first.
+ */
+int
+bread(struct vnode *vp, daddr_t blkno, int size, struct ucred *cred,
+ struct buf **bpp)
+{
+ struct buf *bp;
+
+ bp = getblk (vp, blkno, size, 0, 0);
+ *bpp = bp;
+
+ /* if not found in cache, do some I/O */
+ if ((bp->b_flags & B_CACHE) == 0) {
+ if (curproc && curproc->p_stats) /* count block I/O */
+ curproc->p_stats->p_ru.ru_inblock++;
+ bp->b_flags |= B_READ;
+ bp->b_flags &= ~(B_DONE|B_ERROR|B_INVAL);
+ if( bp->b_rcred == NOCRED) {
+ if (cred != NOCRED)
+ crhold(cred);
+ bp->b_rcred = cred;
+ }
+ VOP_STRATEGY(bp);
+ return( biowait (bp));
+ }
+
+ return (0);
+}
+
+/*
+ * Operates like bread, but also starts asynchronous I/O on
+ * read-ahead blocks.
+ */
+int
+breadn(struct vnode *vp, daddr_t blkno, int size,
+ daddr_t *rablkno, int *rabsize,
+ int cnt, struct ucred *cred, struct buf **bpp)
+{
+ struct buf *bp, *rabp;
+ int i;
+ int rv = 0, readwait = 0;
+
+ *bpp = bp = getblk (vp, blkno, size, 0, 0);
+
+ /* if not found in cache, do some I/O */
+ if ((bp->b_flags & B_CACHE) == 0) {
+ if (curproc && curproc->p_stats) /* count block I/O */
+ curproc->p_stats->p_ru.ru_inblock++;
+ bp->b_flags |= B_READ;
+ bp->b_flags &= ~(B_DONE|B_ERROR|B_INVAL);
+ if( bp->b_rcred == NOCRED) {
+ if (cred != NOCRED)
+ crhold(cred);
+ bp->b_rcred = cred;
+ }
+ VOP_STRATEGY(bp);
+ ++readwait;
+ }
+
+ for(i=0;i<cnt;i++, rablkno++, rabsize++) {
+ if( incore(vp, *rablkno)) {
+ continue;
+ }
+ rabp = getblk (vp, *rablkno, *rabsize, 0, 0);
+
+ if ((rabp->b_flags & B_CACHE) == 0) {
+ if (curproc && curproc->p_stats)
+ curproc->p_stats->p_ru.ru_inblock++;
+ rabp->b_flags |= B_READ | B_ASYNC;
+ rabp->b_flags &= ~(B_DONE|B_ERROR|B_INVAL);
+ if( rabp->b_rcred == NOCRED) {
+ if (cred != NOCRED)
+ crhold(cred);
+ rabp->b_rcred = cred;
+ }
+ VOP_STRATEGY(rabp);
+ } else {
+ brelse(rabp);
+ }
+ }
+
+ if( readwait) {
+ rv = biowait (bp);
+ }
+
+ return (rv);
+}
+
+/*
+ * Write, release buffer on completion. (Done by iodone
+ * if async.)
+ */
+int
+bwrite(struct buf *bp)
+{
+ int oldflags = bp->b_flags;
+
+ if(bp->b_flags & B_INVAL) {
+ brelse(bp);
+ return (0);
+ }
+
+ if(!(bp->b_flags & B_BUSY))
+ panic("bwrite: buffer is not busy???");
+
+ bp->b_flags &= ~(B_READ|B_DONE|B_ERROR|B_DELWRI);
+ bp->b_flags |= B_WRITEINPROG;
+
+ if (oldflags & B_ASYNC) {
+ if (oldflags & B_DELWRI) {
+ reassignbuf(bp, bp->b_vp);
+ } else if( curproc) {
+ ++curproc->p_stats->p_ru.ru_oublock;
+ }
+ }
+
+ bp->b_vp->v_numoutput++;
+ VOP_STRATEGY(bp);
+
+ if( (oldflags & B_ASYNC) == 0) {
+ int rtval = biowait(bp);
+ if (oldflags & B_DELWRI) {
+ reassignbuf(bp, bp->b_vp);
+ } else if( curproc) {
+ ++curproc->p_stats->p_ru.ru_oublock;
+ }
+ brelse(bp);
+ return (rtval);
+ }
+
+ return(0);
+}
+
+int
+vn_bwrite(ap)
+ struct vop_bwrite_args *ap;
+{
+ return (bwrite(ap->a_bp));
+}
+
+/*
+ * Delayed write. (Buffer is marked dirty).
+ */
+void
+bdwrite(struct buf *bp)
+{
+
+ if((bp->b_flags & B_BUSY) == 0) {
+ panic("bdwrite: buffer is not busy");
+ }
+
+ if(bp->b_flags & B_INVAL) {
+ brelse(bp);
+ return;
+ }
+
+ if(bp->b_flags & B_TAPE) {
+ bawrite(bp);
+ return;
+ }
+
+ bp->b_flags &= ~B_READ;
+ if( (bp->b_flags & B_DELWRI) == 0) {
+ if( curproc)
+ ++curproc->p_stats->p_ru.ru_oublock;
+ bp->b_flags |= B_DONE|B_DELWRI;
+ reassignbuf(bp, bp->b_vp);
+ }
+ brelse(bp);
+ return;
+}
+
+/*
+ * Asynchronous write.
+ * Start output on a buffer, but do not wait for it to complete.
+ * The buffer is released when the output completes.
+ */
+void
+bawrite(struct buf *bp)
+{
+ bp->b_flags |= B_ASYNC;
+ (void) bwrite(bp);
+}
+
+/*
+ * Release a buffer.
+ */
+void
+brelse(struct buf *bp)
+{
+ int x;
+
+ /* anyone need a "free" block? */
+ x=splbio();
+ if (needsbuffer) {
+ needsbuffer = 0;
+ wakeup((caddr_t)&needsbuffer);
+ }
+ /* anyone need this very block? */
+ if (bp->b_flags & B_WANTED) {
+ bp->b_flags &= ~(B_WANTED|B_AGE);
+ wakeup((caddr_t)bp);
+ }
+
+ if (bp->b_flags & B_LOCKED)
+ bp->b_flags &= ~B_ERROR;
+
+ if ((bp->b_flags & (B_NOCACHE|B_INVAL|B_ERROR)) ||
+ (bp->b_bufsize <= 0)) {
+ bp->b_flags |= B_INVAL;
+ bp->b_flags &= ~(B_DELWRI|B_CACHE);
+ if(bp->b_vp)
+ brelvp(bp);
+ }
+
+ if( bp->b_qindex != QUEUE_NONE)
+ panic("brelse: free buffer onto another queue???");
+
+ /* enqueue */
+ /* buffers with junk contents */
+ if(bp->b_bufsize == 0) {
+ bp->b_qindex = QUEUE_EMPTY;
+ TAILQ_INSERT_HEAD(&bufqueues[QUEUE_EMPTY], bp, b_freelist);
+ LIST_REMOVE(bp, b_hash);
+ LIST_INSERT_HEAD(&invalhash, bp, b_hash);
+ bp->b_dev = NODEV;
+ } else if(bp->b_flags & (B_ERROR|B_INVAL|B_NOCACHE)) {
+ bp->b_qindex = QUEUE_AGE;
+ TAILQ_INSERT_HEAD(&bufqueues[QUEUE_AGE], bp, b_freelist);
+ LIST_REMOVE(bp, b_hash);
+ LIST_INSERT_HEAD(&invalhash, bp, b_hash);
+ bp->b_dev = NODEV;
+ /* buffers that are locked */
+ } else if(bp->b_flags & B_LOCKED) {
+ bp->b_qindex = QUEUE_LOCKED;
+ TAILQ_INSERT_TAIL(&bufqueues[QUEUE_LOCKED], bp, b_freelist);
+ /* buffers with stale but valid contents */
+ } else if(bp->b_flags & B_AGE) {
+ bp->b_qindex = QUEUE_AGE;
+ TAILQ_INSERT_TAIL(&bufqueues[QUEUE_AGE], bp, b_freelist);
+ /* buffers with valid and quite potentially reuseable contents */
+ } else {
+ bp->b_qindex = QUEUE_LRU;
+ TAILQ_INSERT_TAIL(&bufqueues[QUEUE_LRU], bp, b_freelist);
+ }
+
+ /* unlock */
+ bp->b_flags &= ~(B_WANTED|B_BUSY|B_ASYNC|B_NOCACHE|B_AGE);
+ splx(x);
+}
+
+int freebufspace;
+int allocbufspace;
+
+/*
+ * Find a buffer header which is available for use.
+ */
+struct buf *
+getnewbuf(int slpflag, int slptimeo)
+{
+ struct buf *bp;
+ int x;
+ x = splbio();
+start:
+ /* can we constitute a new buffer? */
+ if (bp = bufqueues[QUEUE_EMPTY].tqh_first) {
+ if( bp->b_qindex != QUEUE_EMPTY)
+ panic("getnewbuf: inconsistent EMPTY queue");
+ bremfree(bp);
+ goto fillbuf;
+ }
+
+tryfree:
+ if (bp = bufqueues[QUEUE_AGE].tqh_first) {
+ if( bp->b_qindex != QUEUE_AGE)
+ panic("getnewbuf: inconsistent AGE queue");
+ bremfree(bp);
+ } else if (bp = bufqueues[QUEUE_LRU].tqh_first) {
+ if( bp->b_qindex != QUEUE_LRU)
+ panic("getnewbuf: inconsistent LRU queue");
+ bremfree(bp);
+ } else {
+ /* wait for a free buffer of any kind */
+ needsbuffer = 1;
+ tsleep((caddr_t)&needsbuffer, PRIBIO, "newbuf", 0);
+ splx(x);
+ return (0);
+ }
+
+
+ /* if we are a delayed write, convert to an async write */
+ if (bp->b_flags & B_DELWRI) {
+ bp->b_flags |= B_BUSY;
+ bawrite (bp);
+ goto start;
+ }
+
+ if(bp->b_vp)
+ brelvp(bp);
+
+ /* we are not free, nor do we contain interesting data */
+ if (bp->b_rcred != NOCRED)
+ crfree(bp->b_rcred);
+ if (bp->b_wcred != NOCRED)
+ crfree(bp->b_wcred);
+fillbuf:
+ bp->b_flags = B_BUSY;
+ LIST_REMOVE(bp, b_hash);
+ LIST_INSERT_HEAD(&invalhash, bp, b_hash);
+ splx(x);
+ bp->b_dev = NODEV;
+ bp->b_vp = NULL;
+ bp->b_blkno = bp->b_lblkno = 0;
+ bp->b_iodone = 0;
+ bp->b_error = 0;
+ bp->b_resid = 0;
+ bp->b_bcount = 0;
+ bp->b_wcred = bp->b_rcred = NOCRED;
+ bp->b_dirtyoff = bp->b_dirtyend = 0;
+ bp->b_validoff = bp->b_validend = 0;
+ return (bp);
+}
+
+/*
+ * Check to see if a block is currently memory resident.
+ */
+struct buf *
+incore(struct vnode *vp, daddr_t blkno)
+{
+ struct buf *bp;
+ struct bufhashhdr *bh;
+
+ int s = splbio();
+
+ bh = BUFHASH(vp, blkno);
+ bp = bh->lh_first;
+
+ /* Search hash chain */
+ while (bp) {
+ if( (bp < buf) || (bp >= buf + nbuf)) {
+ printf("incore: buf out of range: %lx, hash: %d\n",
+ bp, bh - bufhashtbl);
+ panic("incore: buf fault");
+ }
+ /* hit */
+ if (bp->b_lblkno == blkno && bp->b_vp == vp
+ && (bp->b_flags & B_INVAL) == 0) {
+ splx(s);
+ return (bp);
+ }
+ bp = bp->b_hash.le_next;
+ }
+ splx(s);
+
+ return(0);
+}
+
+/*
+ * Get a block given a specified block and offset into a file/device.
+ */
+struct buf *
+getblk(struct vnode *vp, daddr_t blkno, int size, int slpflag, int slptimeo)
+{
+ struct buf *bp;
+ int x;
+ struct bufhashhdr *bh;
+
+ x = splbio();
+loop:
+ if (bp = incore(vp, blkno)) {
+ if (bp->b_flags & B_BUSY) {
+ bp->b_flags |= B_WANTED;
+ tsleep ((caddr_t)bp, PRIBIO, "getblk", 0);
+ goto loop;
+ }
+ bp->b_flags |= B_BUSY | B_CACHE;
+ bremfree(bp);
+ /*
+ * check for size inconsistancies
+ */
+ if (bp->b_bcount != size) {
+ printf("getblk: invalid buffer size: %d\n", bp->b_bcount);
+ bp->b_flags |= B_INVAL;
+ bwrite(bp);
+ goto loop;
+ }
+ } else {
+
+ if ((bp = getnewbuf(0, 0)) == 0)
+ goto loop;
+ allocbuf(bp, size);
+ /*
+ * have to check again, because of a possible
+ * race condition.
+ */
+ if (incore( vp, blkno)) {
+ allocbuf(bp, 0);
+ bp->b_flags |= B_INVAL;
+ brelse(bp);
+ goto loop;
+ }
+ bp->b_blkno = bp->b_lblkno = blkno;
+ bgetvp(vp, bp);
+ LIST_REMOVE(bp, b_hash);
+ bh = BUFHASH(vp, blkno);
+ LIST_INSERT_HEAD(bh, bp, b_hash);
+ }
+ splx(x);
+ return (bp);
+}
+
+/*
+ * Get an empty, disassociated buffer of given size.
+ */
+struct buf *
+geteblk(int size)
+{
+ struct buf *bp;
+ while ((bp = getnewbuf(0, 0)) == 0)
+ ;
+ allocbuf(bp, size);
+ bp->b_flags |= B_INVAL;
+ return (bp);
+}
+
+/*
+ * Modify the length of a buffer's underlying buffer storage without
+ * destroying information (unless, of course the buffer is shrinking).
+ */
+void
+allocbuf(struct buf *bp, int size)
+{
+
+ int newbsize = round_page(size);
+
+ if( newbsize == bp->b_bufsize) {
+ bp->b_bcount = size;
+ return;
+ } else if( newbsize < bp->b_bufsize) {
+ vm_hold_free_pages(
+ (vm_offset_t) bp->b_data + newbsize,
+ (vm_offset_t) bp->b_data + bp->b_bufsize);
+ } else if( newbsize > bp->b_bufsize) {
+ vm_hold_load_pages(
+ (vm_offset_t) bp->b_data + bp->b_bufsize,
+ (vm_offset_t) bp->b_data + newbsize);
+ }
+
+ /* adjust buffer cache's idea of memory allocated to buffer contents */
+ freebufspace -= newbsize - bp->b_bufsize;
+ allocbufspace += newbsize - bp->b_bufsize;
+
+ bp->b_bufsize = newbsize;
+ bp->b_bcount = size;
+}
+
+/*
+ * Wait for buffer I/O completion, returning error status.
+ */
+int
+biowait(register struct buf *bp)
+{
+ int x;
+
+ x = splbio();
+ while ((bp->b_flags & B_DONE) == 0)
+ tsleep((caddr_t)bp, PRIBIO, "biowait", 0);
+ if((bp->b_flags & B_ERROR) || bp->b_error) {
+ if ((bp->b_flags & B_INVAL) == 0) {
+ bp->b_flags |= B_INVAL;
+ bp->b_dev = NODEV;
+ LIST_REMOVE(bp, b_hash);
+ LIST_INSERT_HEAD(&invalhash, bp, b_hash);
+ }
+ if (!bp->b_error)
+ bp->b_error = EIO;
+ else
+ bp->b_flags |= B_ERROR;
+ splx(x);
+ return (bp->b_error);
+ } else {
+ splx(x);
+ return (0);
+ }
+}
+
+/*
+ * Finish I/O on a buffer, calling an optional function.
+ * This is usually called from interrupt level, so process blocking
+ * is not *a good idea*.
+ */
+void
+biodone(register struct buf *bp)
+{
+ int s;
+ s = splbio();
+ bp->b_flags |= B_DONE;
+
+ if ((bp->b_flags & B_READ) == 0) {
+ vwakeup(bp);
+ }
+
+ /* call optional completion function if requested */
+ if (bp->b_flags & B_CALL) {
+ bp->b_flags &= ~B_CALL;
+ (*bp->b_iodone)(bp);
+ splx(s);
+ return;
+ }
+
+/*
+ * For asynchronous completions, release the buffer now. The brelse
+ * checks for B_WANTED and will do the wakeup there if necessary -
+ * so no need to do a wakeup here in the async case.
+ */
+
+ if (bp->b_flags & B_ASYNC) {
+ brelse(bp);
+ } else {
+ bp->b_flags &= ~B_WANTED;
+ wakeup((caddr_t) bp);
+ }
+ splx(s);
+}
+
+int
+count_lock_queue()
+{
+ int count;
+ struct buf *bp;
+
+ count = 0;
+ for(bp = bufqueues[QUEUE_LOCKED].tqh_first;
+ bp != NULL;
+ bp = bp->b_freelist.tqe_next)
+ count++;
+ return(count);
+}
+
+#ifndef UPDATE_INTERVAL
+int vfs_update_interval = 30;
+#else
+int vfs_update_interval = UPDATE_INTERVAL;
+#endif
+
+void
+vfs_update() {
+ (void) spl0();
+ while(1) {
+ tsleep((caddr_t)&vfs_update_wakeup, PRIBIO, "update",
+ hz * vfs_update_interval);
+ vfs_update_wakeup = 0;
+ sync(curproc, NULL, NULL);
+ }
+}
+
+/*
+ * these routines are not in the correct place (yet)
+ * also they work *ONLY* for kernel_pmap!!!
+ */
+void
+vm_hold_load_pages(vm_offset_t froma, vm_offset_t toa) {
+ vm_offset_t pg;
+ vm_page_t p;
+ vm_offset_t from = round_page(froma);
+ vm_offset_t to = round_page(toa);
+
+ for(pg = from ; pg < to ; pg += PAGE_SIZE) {
+ vm_offset_t pa;
+
+ tryagain:
+ p = vm_page_alloc(kernel_object, pg - VM_MIN_KERNEL_ADDRESS);
+ if( !p) {
+ VM_WAIT;
+ goto tryagain;
+ }
+
+ vm_page_wire(p);
+ pmap_enter(kernel_pmap, pg, VM_PAGE_TO_PHYS(p),
+ VM_PROT_READ|VM_PROT_WRITE, 1);
+ }
+}
+
+void
+vm_hold_free_pages(vm_offset_t froma, vm_offset_t toa) {
+ vm_offset_t pg;
+ vm_page_t p;
+ vm_offset_t from = round_page(froma);
+ vm_offset_t to = round_page(toa);
+
+ for(pg = from ; pg < to ; pg += PAGE_SIZE) {
+ vm_offset_t pa;
+ pa = pmap_kextract(pg);
+ if( !pa) {
+ printf("No pa for va: %x\n", pg);
+ } else {
+ p = PHYS_TO_VM_PAGE( pa);
+ pmap_remove(kernel_pmap, pg, pg + PAGE_SIZE);
+ vm_page_free(p);
+ }
+ }
+}
+
+void
+bufstats()
+{
+}
+
diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c
new file mode 100644
index 0000000..2ddf644
--- /dev/null
+++ b/sys/kern/vfs_cache.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vfs_cache.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+
+/*
+ * Name caching works as follows:
+ *
+ * Names found by directory scans are retained in a cache
+ * for future reference. It is managed LRU, so frequently
+ * used names will hang around. Cache is indexed by hash value
+ * obtained from (vp, name) where vp refers to the directory
+ * containing name.
+ *
+ * For simplicity (and economy of storage), names longer than
+ * a maximum length of NCHNAMLEN are not cached; they occur
+ * infrequently in any case, and are almost never of interest.
+ *
+ * Upon reaching the last segment of a path, if the reference
+ * is for DELETE, or NOCACHE is set (rewrite), and the
+ * name is located in the cache, it will be dropped.
+ */
+
+/*
+ * Structures associated with name cacheing.
+ */
+struct namecache **nchashtbl;
+u_long nchash; /* size of hash table - 1 */
+long numcache; /* number of cache entries allocated */
+struct namecache *nchhead, **nchtail; /* LRU chain pointers */
+struct nchstats nchstats; /* cache effectiveness statistics */
+
+int doingcache = 1; /* 1 => enable the cache */
+
+/*
+ * Look for a the name in the cache. We don't do this
+ * if the segment name is long, simply so the cache can avoid
+ * holding long names (which would either waste space, or
+ * add greatly to the complexity).
+ *
+ * Lookup is called with ni_dvp pointing to the directory to search,
+ * ni_ptr pointing to the name of the entry being sought, ni_namelen
+ * tells the length of the name, and ni_hash contains a hash of
+ * the name. If the lookup succeeds, the vnode is returned in ni_vp
+ * and a status of -1 is returned. If the lookup determines that
+ * the name does not exist (negative cacheing), a status of ENOENT
+ * is returned. If the lookup fails, a status of zero is returned.
+ */
+int
+cache_lookup(dvp, vpp, cnp)
+ struct vnode *dvp;
+ struct vnode **vpp;
+ struct componentname *cnp;
+{
+ register struct namecache *ncp, *ncq, **ncpp;
+
+ if (!doingcache)
+ return (0);
+ if (cnp->cn_namelen > NCHNAMLEN) {
+ nchstats.ncs_long++;
+ cnp->cn_flags &= ~MAKEENTRY;
+ return (0);
+ }
+ ncpp = &nchashtbl[cnp->cn_hash & nchash];
+ for (ncp = *ncpp; ncp; ncp = ncp->nc_forw) {
+ if (ncp->nc_dvp == dvp &&
+ ncp->nc_dvpid == dvp->v_id &&
+ ncp->nc_nlen == cnp->cn_namelen &&
+ !bcmp(ncp->nc_name, cnp->cn_nameptr, (u_int)ncp->nc_nlen))
+ break;
+ }
+ if (ncp == NULL) {
+ nchstats.ncs_miss++;
+ return (0);
+ }
+ if (!(cnp->cn_flags & MAKEENTRY)) {
+ nchstats.ncs_badhits++;
+ } else if (ncp->nc_vp == NULL) {
+ if (cnp->cn_nameiop != CREATE) {
+ nchstats.ncs_neghits++;
+ /*
+ * Move this slot to end of LRU chain,
+ * if not already there.
+ */
+ if (ncp->nc_nxt) {
+ /* remove from LRU chain */
+ *ncp->nc_prev = ncp->nc_nxt;
+ ncp->nc_nxt->nc_prev = ncp->nc_prev;
+ /* and replace at end of it */
+ ncp->nc_nxt = NULL;
+ ncp->nc_prev = nchtail;
+ *nchtail = ncp;
+ nchtail = &ncp->nc_nxt;
+ }
+ return (ENOENT);
+ }
+ } else if (ncp->nc_vpid != ncp->nc_vp->v_id) {
+ nchstats.ncs_falsehits++;
+ } else {
+ nchstats.ncs_goodhits++;
+ /*
+ * move this slot to end of LRU chain, if not already there
+ */
+ if (ncp->nc_nxt) {
+ /* remove from LRU chain */
+ *ncp->nc_prev = ncp->nc_nxt;
+ ncp->nc_nxt->nc_prev = ncp->nc_prev;
+ /* and replace at end of it */
+ ncp->nc_nxt = NULL;
+ ncp->nc_prev = nchtail;
+ *nchtail = ncp;
+ nchtail = &ncp->nc_nxt;
+ }
+ *vpp = ncp->nc_vp;
+ return (-1);
+ }
+
+ /*
+ * Last component and we are renaming or deleting,
+ * the cache entry is invalid, or otherwise don't
+ * want cache entry to exist.
+ */
+ /* remove from LRU chain */
+ if (ncq = ncp->nc_nxt)
+ ncq->nc_prev = ncp->nc_prev;
+ else
+ nchtail = ncp->nc_prev;
+ *ncp->nc_prev = ncq;
+ /* remove from hash chain */
+ if (ncq = ncp->nc_forw)
+ ncq->nc_back = ncp->nc_back;
+ *ncp->nc_back = ncq;
+ /* and make a dummy hash chain */
+ ncp->nc_forw = NULL;
+ ncp->nc_back = NULL;
+ /* insert at head of LRU list (first to grab) */
+ if (ncq = nchhead)
+ ncq->nc_prev = &ncp->nc_nxt;
+ else
+ nchtail = &ncp->nc_nxt;
+ nchhead = ncp;
+ ncp->nc_nxt = ncq;
+ ncp->nc_prev = &nchhead;
+ return (0);
+}
+
+/*
+ * Add an entry to the cache
+ */
+void
+cache_enter(dvp, vp, cnp)
+ struct vnode *dvp;
+ struct vnode *vp;
+ struct componentname *cnp;
+{
+ register struct namecache *ncp, *ncq, **ncpp;
+
+#ifdef DIAGNOSTIC
+ if (cnp->cn_namelen > NCHNAMLEN)
+ panic("cache_enter: name too long");
+#endif
+ if (!doingcache)
+ return;
+ /*
+ * Free the cache slot at head of lru chain.
+ */
+ if (numcache < desiredvnodes) {
+ ncp = (struct namecache *)
+ malloc((u_long)sizeof *ncp, M_CACHE, M_WAITOK);
+ bzero((char *)ncp, sizeof *ncp);
+ numcache++;
+ } else if (ncp = nchhead) {
+ /* remove from lru chain */
+ if (ncq = ncp->nc_nxt)
+ ncq->nc_prev = ncp->nc_prev;
+ else
+ nchtail = ncp->nc_prev;
+ *ncp->nc_prev = ncq;
+ /* remove from old hash chain, if on one */
+ if (ncp->nc_back) {
+ if (ncq = ncp->nc_forw)
+ ncq->nc_back = ncp->nc_back;
+ *ncp->nc_back = ncq;
+ ncp->nc_forw = NULL;
+ ncp->nc_back = NULL;
+ }
+ } else
+ return;
+ /* grab the vnode we just found */
+ ncp->nc_vp = vp;
+ if (vp)
+ ncp->nc_vpid = vp->v_id;
+ else
+ ncp->nc_vpid = 0;
+ /* fill in cache info */
+ ncp->nc_dvp = dvp;
+ ncp->nc_dvpid = dvp->v_id;
+ ncp->nc_nlen = cnp->cn_namelen;
+ bcopy(cnp->cn_nameptr, ncp->nc_name, (unsigned)ncp->nc_nlen);
+ /* link at end of lru chain */
+ ncp->nc_nxt = NULL;
+ ncp->nc_prev = nchtail;
+ *nchtail = ncp;
+ nchtail = &ncp->nc_nxt;
+ /* and insert on hash chain */
+ ncpp = &nchashtbl[cnp->cn_hash & nchash];
+ if (ncq = *ncpp)
+ ncq->nc_back = &ncp->nc_forw;
+ ncp->nc_forw = ncq;
+ ncp->nc_back = ncpp;
+ *ncpp = ncp;
+}
+
+/*
+ * Name cache initialization, from vfs_init() when we are booting
+ */
+void
+nchinit()
+{
+
+ nchtail = &nchhead;
+ nchashtbl = hashinit(desiredvnodes, M_CACHE, &nchash);
+}
+
+/*
+ * Cache flush, a particular vnode; called when a vnode is renamed to
+ * hide entries that would now be invalid
+ */
+void
+cache_purge(vp)
+ struct vnode *vp;
+{
+ struct namecache *ncp, **ncpp;
+
+ vp->v_id = ++nextvnodeid;
+ if (nextvnodeid != 0)
+ return;
+ for (ncpp = &nchashtbl[nchash]; ncpp >= nchashtbl; ncpp--) {
+ for (ncp = *ncpp; ncp; ncp = ncp->nc_forw) {
+ ncp->nc_vpid = 0;
+ ncp->nc_dvpid = 0;
+ }
+ }
+ vp->v_id = ++nextvnodeid;
+}
+
+/*
+ * Cache flush, a whole filesystem; called when filesys is umounted to
+ * remove entries that would now be invalid
+ *
+ * The line "nxtcp = nchhead" near the end is to avoid potential problems
+ * if the cache lru chain is modified while we are dumping the
+ * inode. This makes the algorithm O(n^2), but do you think I care?
+ */
+void
+cache_purgevfs(mp)
+ struct mount *mp;
+{
+ register struct namecache *ncp, *nxtcp;
+
+ for (ncp = nchhead; ncp; ncp = nxtcp) {
+ if (ncp->nc_dvp == NULL || ncp->nc_dvp->v_mount != mp) {
+ nxtcp = ncp->nc_nxt;
+ continue;
+ }
+ /* free the resources we had */
+ ncp->nc_vp = NULL;
+ ncp->nc_dvp = NULL;
+ /* remove from old hash chain, if on one */
+ if (ncp->nc_back) {
+ if (nxtcp = ncp->nc_forw)
+ nxtcp->nc_back = ncp->nc_back;
+ *ncp->nc_back = nxtcp;
+ ncp->nc_forw = NULL;
+ ncp->nc_back = NULL;
+ }
+ /* delete this entry from LRU chain */
+ if (nxtcp = ncp->nc_nxt)
+ nxtcp->nc_prev = ncp->nc_prev;
+ else
+ nchtail = ncp->nc_prev;
+ *ncp->nc_prev = nxtcp;
+ /* cause rescan of list, it may have altered */
+ /* also put the now-free entry at head of LRU */
+ if (nxtcp = nchhead)
+ nxtcp->nc_prev = &ncp->nc_nxt;
+ else
+ nchtail = &ncp->nc_nxt;
+ nchhead = ncp;
+ ncp->nc_nxt = nxtcp;
+ ncp->nc_prev = &nchhead;
+ }
+}
diff --git a/sys/kern/vfs_cluster.c b/sys/kern/vfs_cluster.c
new file mode 100644
index 0000000..40fa3be
--- /dev/null
+++ b/sys/kern/vfs_cluster.c
@@ -0,0 +1,747 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vfs_cluster.c 8.7 (Berkeley) 2/13/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/trace.h>
+#include <sys/malloc.h>
+#include <sys/resourcevar.h>
+
+#ifdef DEBUG
+#include <vm/vm.h>
+#include <sys/sysctl.h>
+int doreallocblks = 1;
+struct ctldebug debug13 = { "doreallocblks", &doreallocblks };
+#else
+/* XXX for cluster_write */
+#define doreallocblks 1
+#endif
+
+/*
+ * Local declarations
+ */
+struct buf *cluster_newbuf __P((struct vnode *, struct buf *, long, daddr_t,
+ daddr_t, long, int));
+struct buf *cluster_rbuild __P((struct vnode *, u_quad_t, struct buf *,
+ daddr_t, daddr_t, long, int, long));
+void cluster_wbuild __P((struct vnode *, struct buf *, long,
+ daddr_t, int, daddr_t));
+struct cluster_save *cluster_collectbufs __P((struct vnode *, struct buf *));
+
+#ifdef DIAGNOSTIC
+/*
+ * Set to 1 if reads of block zero should cause readahead to be done.
+ * Set to 0 treats a read of block zero as a non-sequential read.
+ *
+ * Setting to one assumes that most reads of block zero of files are due to
+ * sequential passes over the files (e.g. cat, sum) where additional blocks
+ * will soon be needed. Setting to zero assumes that the majority are
+ * surgical strikes to get particular info (e.g. size, file) where readahead
+ * blocks will not be used and, in fact, push out other potentially useful
+ * blocks from the cache. The former seems intuitive, but some quick tests
+ * showed that the latter performed better from a system-wide point of view.
+ */
+int doclusterraz = 0;
+#define ISSEQREAD(vp, blk) \
+ (((blk) != 0 || doclusterraz) && \
+ ((blk) == (vp)->v_lastr + 1 || (blk) == (vp)->v_lastr))
+#else
+#define ISSEQREAD(vp, blk) \
+ ((blk) != 0 && ((blk) == (vp)->v_lastr + 1 || (blk) == (vp)->v_lastr))
+#endif
+
+/*
+ * This replaces bread. If this is a bread at the beginning of a file and
+ * lastr is 0, we assume this is the first read and we'll read up to two
+ * blocks if they are sequential. After that, we'll do regular read ahead
+ * in clustered chunks.
+ *
+ * There are 4 or 5 cases depending on how you count:
+ * Desired block is in the cache:
+ * 1 Not sequential access (0 I/Os).
+ * 2 Access is sequential, do read-ahead (1 ASYNC).
+ * Desired block is not in cache:
+ * 3 Not sequential access (1 SYNC).
+ * 4 Sequential access, next block is contiguous (1 SYNC).
+ * 5 Sequential access, next block is not contiguous (1 SYNC, 1 ASYNC)
+ *
+ * There are potentially two buffers that require I/O.
+ * bp is the block requested.
+ * rbp is the read-ahead block.
+ * If either is NULL, then you don't have to do the I/O.
+ */
+int
+cluster_read(vp, filesize, lblkno, size, cred, bpp)
+ struct vnode *vp;
+ u_quad_t filesize;
+ daddr_t lblkno;
+ long size;
+ struct ucred *cred;
+ struct buf **bpp;
+{
+ struct buf *bp, *rbp;
+ daddr_t blkno, ioblkno;
+ long flags;
+ int error, num_ra, alreadyincore;
+
+#ifdef DIAGNOSTIC
+ if (size == 0)
+ panic("cluster_read: size = 0");
+#endif
+
+ error = 0;
+ flags = B_READ;
+ *bpp = bp = getblk(vp, lblkno, size, 0, 0);
+ if (bp->b_flags & B_CACHE) {
+ /*
+ * Desired block is in cache; do any readahead ASYNC.
+ * Case 1, 2.
+ */
+ trace(TR_BREADHIT, pack(vp, size), lblkno);
+ flags |= B_ASYNC;
+ ioblkno = lblkno + (vp->v_ralen ? vp->v_ralen : 1);
+ alreadyincore = (int)incore(vp, ioblkno);
+ bp = NULL;
+ } else {
+ /* Block wasn't in cache, case 3, 4, 5. */
+ trace(TR_BREADMISS, pack(vp, size), lblkno);
+ bp->b_flags |= B_READ;
+ ioblkno = lblkno;
+ alreadyincore = 0;
+ curproc->p_stats->p_ru.ru_inblock++; /* XXX */
+ }
+ /*
+ * XXX
+ * Replace 1 with a window size based on some permutation of
+ * maxcontig and rot_delay. This will let you figure out how
+ * many blocks you should read-ahead (case 2, 4, 5).
+ *
+ * If the access isn't sequential, reset the window to 1.
+ * Note that a read to the same block is considered sequential.
+ * This catches the case where the file is being read sequentially,
+ * but at smaller than the filesystem block size.
+ */
+ rbp = NULL;
+ if (!ISSEQREAD(vp, lblkno)) {
+ vp->v_ralen = 0;
+ vp->v_maxra = lblkno;
+ } else if ((ioblkno + 1) * size <= filesize && !alreadyincore &&
+ !(error = VOP_BMAP(vp, ioblkno, NULL, &blkno, &num_ra)) &&
+ blkno != -1) {
+ /*
+ * Reading sequentially, and the next block is not in the
+ * cache. We are going to try reading ahead.
+ */
+ if (num_ra) {
+ /*
+ * If our desired readahead block had been read
+ * in a previous readahead but is no longer in
+ * core, then we may be reading ahead too far
+ * or are not using our readahead very rapidly.
+ * In this case we scale back the window.
+ */
+ if (!alreadyincore && ioblkno <= vp->v_maxra)
+ vp->v_ralen = max(vp->v_ralen >> 1, 1);
+ /*
+ * There are more sequential blocks than our current
+ * window allows, scale up. Ideally we want to get
+ * in sync with the filesystem maxcontig value.
+ */
+ else if (num_ra > vp->v_ralen && lblkno != vp->v_lastr)
+ vp->v_ralen = vp->v_ralen ?
+ min(num_ra, vp->v_ralen << 1) : 1;
+
+ if (num_ra > vp->v_ralen)
+ num_ra = vp->v_ralen;
+ }
+
+ if (num_ra) /* case 2, 4 */
+ rbp = cluster_rbuild(vp, filesize,
+ bp, ioblkno, blkno, size, num_ra, flags);
+ else if (ioblkno == lblkno) {
+ bp->b_blkno = blkno;
+ /* Case 5: check how many blocks to read ahead */
+ ++ioblkno;
+ if ((ioblkno + 1) * size > filesize ||
+ incore(vp, ioblkno) || (error = VOP_BMAP(vp,
+ ioblkno, NULL, &blkno, &num_ra)) || blkno == -1)
+ goto skip_readahead;
+ /*
+ * Adjust readahead as above
+ */
+ if (num_ra) {
+ if (!alreadyincore && ioblkno <= vp->v_maxra)
+ vp->v_ralen = max(vp->v_ralen >> 1, 1);
+ else if (num_ra > vp->v_ralen &&
+ lblkno != vp->v_lastr)
+ vp->v_ralen = vp->v_ralen ?
+ min(num_ra,vp->v_ralen<<1) : 1;
+ if (num_ra > vp->v_ralen)
+ num_ra = vp->v_ralen;
+ }
+ flags |= B_ASYNC;
+ if (num_ra)
+ rbp = cluster_rbuild(vp, filesize,
+ NULL, ioblkno, blkno, size, num_ra, flags);
+ else {
+ rbp = getblk(vp, ioblkno, size, 0, 0);
+ rbp->b_flags |= flags;
+ rbp->b_blkno = blkno;
+ }
+ } else {
+ /* case 2; read ahead single block */
+ rbp = getblk(vp, ioblkno, size, 0, 0);
+ rbp->b_flags |= flags;
+ rbp->b_blkno = blkno;
+ }
+
+ if (rbp == bp) /* case 4 */
+ rbp = NULL;
+ else if (rbp) { /* case 2, 5 */
+ trace(TR_BREADMISSRA,
+ pack(vp, (num_ra + 1) * size), ioblkno);
+ curproc->p_stats->p_ru.ru_inblock++; /* XXX */
+ }
+ }
+
+ /* XXX Kirk, do we need to make sure the bp has creds? */
+skip_readahead:
+ if (bp)
+ if (bp->b_flags & (B_DONE | B_DELWRI))
+ panic("cluster_read: DONE bp");
+ else
+ error = VOP_STRATEGY(bp);
+
+ if (rbp)
+ if (error || rbp->b_flags & (B_DONE | B_DELWRI)) {
+ rbp->b_flags &= ~(B_ASYNC | B_READ);
+ brelse(rbp);
+ } else
+ (void) VOP_STRATEGY(rbp);
+
+ /*
+ * Recalculate our maximum readahead
+ */
+ if (rbp == NULL)
+ rbp = bp;
+ if (rbp)
+ vp->v_maxra = rbp->b_lblkno + (rbp->b_bufsize / size) - 1;
+
+ if (bp)
+ return(biowait(bp));
+ return(error);
+}
+
+/*
+ * If blocks are contiguous on disk, use this to provide clustered
+ * read ahead. We will read as many blocks as possible sequentially
+ * and then parcel them up into logical blocks in the buffer hash table.
+ */
+struct buf *
+cluster_rbuild(vp, filesize, bp, lbn, blkno, size, run, flags)
+ struct vnode *vp;
+ u_quad_t filesize;
+ struct buf *bp;
+ daddr_t lbn;
+ daddr_t blkno;
+ long size;
+ int run;
+ long flags;
+{
+ struct cluster_save *b_save;
+ struct buf *tbp;
+ daddr_t bn;
+ int i, inc;
+
+#ifdef DIAGNOSTIC
+ if (size != vp->v_mount->mnt_stat.f_iosize)
+ panic("cluster_rbuild: size %d != filesize %d\n",
+ size, vp->v_mount->mnt_stat.f_iosize);
+#endif
+ if (size * (lbn + run + 1) > filesize)
+ --run;
+ if (run == 0) {
+ if (!bp) {
+ bp = getblk(vp, lbn, size, 0, 0);
+ bp->b_blkno = blkno;
+ bp->b_flags |= flags;
+ }
+ return(bp);
+ }
+
+ bp = cluster_newbuf(vp, bp, flags, blkno, lbn, size, run + 1);
+ if (bp->b_flags & (B_DONE | B_DELWRI))
+ return (bp);
+
+ b_save = malloc(sizeof(struct buf *) * run + sizeof(struct cluster_save),
+ M_SEGMENT, M_WAITOK);
+ b_save->bs_bufsize = b_save->bs_bcount = size;
+ b_save->bs_nchildren = 0;
+ b_save->bs_children = (struct buf **)(b_save + 1);
+ b_save->bs_saveaddr = bp->b_saveaddr;
+ bp->b_saveaddr = (caddr_t) b_save;
+
+ inc = btodb(size);
+ for (bn = blkno + inc, i = 1; i <= run; ++i, bn += inc) {
+ if (incore(vp, lbn + i)) {
+ if (i == 1) {
+ bp->b_saveaddr = b_save->bs_saveaddr;
+ bp->b_flags &= ~B_CALL;
+ bp->b_iodone = NULL;
+ allocbuf(bp, size);
+ free(b_save, M_SEGMENT);
+ } else
+ allocbuf(bp, size * i);
+ break;
+ }
+ tbp = getblk(vp, lbn + i, 0, 0, 0);
+ /*
+ * getblk may return some memory in the buffer if there were
+ * no empty buffers to shed it to. If there is currently
+ * memory in the buffer, we move it down size bytes to make
+ * room for the valid pages that cluster_callback will insert.
+ * We do this now so we don't have to do it at interrupt time
+ * in the callback routine.
+ */
+ if (tbp->b_bufsize != 0) {
+ caddr_t bdata = (char *)tbp->b_data;
+
+ if (tbp->b_bufsize + size > MAXBSIZE)
+ panic("cluster_rbuild: too much memory");
+ if (tbp->b_bufsize > size) {
+ /*
+ * XXX if the source and destination regions
+ * overlap we have to copy backward to avoid
+ * clobbering any valid pages (i.e. pagemove
+ * implementations typically can't handle
+ * overlap).
+ */
+ bdata += tbp->b_bufsize;
+ while (bdata > (char *)tbp->b_data) {
+ bdata -= CLBYTES;
+ pagemove(bdata, bdata + size, CLBYTES);
+ }
+ } else
+ pagemove(bdata, bdata + size, tbp->b_bufsize);
+ }
+ tbp->b_blkno = bn;
+ tbp->b_flags |= flags | B_READ | B_ASYNC;
+ ++b_save->bs_nchildren;
+ b_save->bs_children[i - 1] = tbp;
+ }
+ return(bp);
+}
+
+/*
+ * Either get a new buffer or grow the existing one.
+ */
+struct buf *
+cluster_newbuf(vp, bp, flags, blkno, lblkno, size, run)
+ struct vnode *vp;
+ struct buf *bp;
+ long flags;
+ daddr_t blkno;
+ daddr_t lblkno;
+ long size;
+ int run;
+{
+ if (!bp) {
+ bp = getblk(vp, lblkno, size, 0, 0);
+ if (bp->b_flags & (B_DONE | B_DELWRI)) {
+ bp->b_blkno = blkno;
+ return(bp);
+ }
+ }
+ allocbuf(bp, run * size);
+ bp->b_blkno = blkno;
+ bp->b_iodone = cluster_callback;
+ bp->b_flags |= flags | B_CALL;
+ return(bp);
+}
+
+/*
+ * Cleanup after a clustered read or write.
+ * This is complicated by the fact that any of the buffers might have
+ * extra memory (if there were no empty buffer headers at allocbuf time)
+ * that we will need to shift around.
+ */
+void
+cluster_callback(bp)
+ struct buf *bp;
+{
+ struct cluster_save *b_save;
+ struct buf **bpp, *tbp;
+ long bsize;
+ caddr_t cp;
+ int error = 0;
+
+ /*
+ * Must propogate errors to all the components.
+ */
+ if (bp->b_flags & B_ERROR)
+ error = bp->b_error;
+
+ b_save = (struct cluster_save *)(bp->b_saveaddr);
+ bp->b_saveaddr = b_save->bs_saveaddr;
+
+ bsize = b_save->bs_bufsize;
+ cp = (char *)bp->b_data + bsize;
+ /*
+ * Move memory from the large cluster buffer into the component
+ * buffers and mark IO as done on these.
+ */
+ for (bpp = b_save->bs_children; b_save->bs_nchildren--; ++bpp) {
+ tbp = *bpp;
+ pagemove(cp, tbp->b_data, bsize);
+ tbp->b_bufsize += bsize;
+ tbp->b_bcount = bsize;
+ if (error) {
+ tbp->b_flags |= B_ERROR;
+ tbp->b_error = error;
+ }
+ biodone(tbp);
+ bp->b_bufsize -= bsize;
+ cp += bsize;
+ }
+ /*
+ * If there was excess memory in the cluster buffer,
+ * slide it up adjacent to the remaining valid data.
+ */
+ if (bp->b_bufsize != bsize) {
+ if (bp->b_bufsize < bsize)
+ panic("cluster_callback: too little memory");
+ pagemove(cp, (char *)bp->b_data + bsize, bp->b_bufsize - bsize);
+ }
+ bp->b_bcount = bsize;
+ bp->b_iodone = NULL;
+ free(b_save, M_SEGMENT);
+ if (bp->b_flags & B_ASYNC)
+ brelse(bp);
+ else {
+ bp->b_flags &= ~B_WANTED;
+ wakeup((caddr_t)bp);
+ }
+}
+
+/*
+ * Do clustered write for FFS.
+ *
+ * Three cases:
+ * 1. Write is not sequential (write asynchronously)
+ * Write is sequential:
+ * 2. beginning of cluster - begin cluster
+ * 3. middle of a cluster - add to cluster
+ * 4. end of a cluster - asynchronously write cluster
+ */
+void
+cluster_write(bp, filesize)
+ struct buf *bp;
+ u_quad_t filesize;
+{
+ struct vnode *vp;
+ daddr_t lbn;
+ int maxclen, cursize;
+
+ vp = bp->b_vp;
+ lbn = bp->b_lblkno;
+
+ /* Initialize vnode to beginning of file. */
+ if (lbn == 0)
+ vp->v_lasta = vp->v_clen = vp->v_cstart = vp->v_lastw = 0;
+
+ if (vp->v_clen == 0 || lbn != vp->v_lastw + 1 ||
+ (bp->b_blkno != vp->v_lasta + btodb(bp->b_bcount))) {
+ maxclen = MAXBSIZE / vp->v_mount->mnt_stat.f_iosize - 1;
+ if (vp->v_clen != 0) {
+ /*
+ * Next block is not sequential.
+ *
+ * If we are not writing at end of file, the process
+ * seeked to another point in the file since its
+ * last write, or we have reached our maximum
+ * cluster size, then push the previous cluster.
+ * Otherwise try reallocating to make it sequential.
+ */
+ cursize = vp->v_lastw - vp->v_cstart + 1;
+ if (!doreallocblks ||
+ (lbn + 1) * bp->b_bcount != filesize ||
+ lbn != vp->v_lastw + 1 || vp->v_clen <= cursize) {
+ cluster_wbuild(vp, NULL, bp->b_bcount,
+ vp->v_cstart, cursize, lbn);
+ } else {
+ struct buf **bpp, **endbp;
+ struct cluster_save *buflist;
+
+ buflist = cluster_collectbufs(vp, bp);
+ endbp = &buflist->bs_children
+ [buflist->bs_nchildren - 1];
+ if (VOP_REALLOCBLKS(vp, buflist)) {
+ /*
+ * Failed, push the previous cluster.
+ */
+ for (bpp = buflist->bs_children;
+ bpp < endbp; bpp++)
+ brelse(*bpp);
+ free(buflist, M_SEGMENT);
+ cluster_wbuild(vp, NULL, bp->b_bcount,
+ vp->v_cstart, cursize, lbn);
+ } else {
+ /*
+ * Succeeded, keep building cluster.
+ */
+ for (bpp = buflist->bs_children;
+ bpp <= endbp; bpp++)
+ bdwrite(*bpp);
+ free(buflist, M_SEGMENT);
+ vp->v_lastw = lbn;
+ vp->v_lasta = bp->b_blkno;
+ return;
+ }
+ }
+ }
+ /*
+ * Consider beginning a cluster.
+ * If at end of file, make cluster as large as possible,
+ * otherwise find size of existing cluster.
+ */
+ if ((lbn + 1) * bp->b_bcount != filesize &&
+ (VOP_BMAP(vp, lbn, NULL, &bp->b_blkno, &maxclen) ||
+ bp->b_blkno == -1)) {
+ bawrite(bp);
+ vp->v_clen = 0;
+ vp->v_lasta = bp->b_blkno;
+ vp->v_cstart = lbn + 1;
+ vp->v_lastw = lbn;
+ return;
+ }
+ vp->v_clen = maxclen;
+ if (maxclen == 0) { /* I/O not contiguous */
+ vp->v_cstart = lbn + 1;
+ bawrite(bp);
+ } else { /* Wait for rest of cluster */
+ vp->v_cstart = lbn;
+ bdwrite(bp);
+ }
+ } else if (lbn == vp->v_cstart + vp->v_clen) {
+ /*
+ * At end of cluster, write it out.
+ */
+ cluster_wbuild(vp, bp, bp->b_bcount, vp->v_cstart,
+ vp->v_clen + 1, lbn);
+ vp->v_clen = 0;
+ vp->v_cstart = lbn + 1;
+ } else
+ /*
+ * In the middle of a cluster, so just delay the
+ * I/O for now.
+ */
+ bdwrite(bp);
+ vp->v_lastw = lbn;
+ vp->v_lasta = bp->b_blkno;
+}
+
+
+/*
+ * This is an awful lot like cluster_rbuild...wish they could be combined.
+ * The last lbn argument is the current block on which I/O is being
+ * performed. Check to see that it doesn't fall in the middle of
+ * the current block (if last_bp == NULL).
+ */
+void
+cluster_wbuild(vp, last_bp, size, start_lbn, len, lbn)
+ struct vnode *vp;
+ struct buf *last_bp;
+ long size;
+ daddr_t start_lbn;
+ int len;
+ daddr_t lbn;
+{
+ struct cluster_save *b_save;
+ struct buf *bp, *tbp;
+ caddr_t cp;
+ int i, s;
+
+#ifdef DIAGNOSTIC
+ if (size != vp->v_mount->mnt_stat.f_iosize)
+ panic("cluster_wbuild: size %d != filesize %d\n",
+ size, vp->v_mount->mnt_stat.f_iosize);
+#endif
+redo:
+ while ((!incore(vp, start_lbn) || start_lbn == lbn) && len) {
+ ++start_lbn;
+ --len;
+ }
+
+ /* Get more memory for current buffer */
+ if (len <= 1) {
+ if (last_bp) {
+ bawrite(last_bp);
+ } else if (len) {
+ bp = getblk(vp, start_lbn, size, 0, 0);
+ bawrite(bp);
+ }
+ return;
+ }
+
+ bp = getblk(vp, start_lbn, size, 0, 0);
+ if (!(bp->b_flags & B_DELWRI)) {
+ ++start_lbn;
+ --len;
+ brelse(bp);
+ goto redo;
+ }
+
+ /*
+ * Extra memory in the buffer, punt on this buffer.
+ * XXX we could handle this in most cases, but we would have to
+ * push the extra memory down to after our max possible cluster
+ * size and then potentially pull it back up if the cluster was
+ * terminated prematurely--too much hassle.
+ */
+ if (bp->b_bcount != bp->b_bufsize) {
+ ++start_lbn;
+ --len;
+ bawrite(bp);
+ goto redo;
+ }
+
+ --len;
+ b_save = malloc(sizeof(struct buf *) * len + sizeof(struct cluster_save),
+ M_SEGMENT, M_WAITOK);
+ b_save->bs_bcount = bp->b_bcount;
+ b_save->bs_bufsize = bp->b_bufsize;
+ b_save->bs_nchildren = 0;
+ b_save->bs_children = (struct buf **)(b_save + 1);
+ b_save->bs_saveaddr = bp->b_saveaddr;
+ bp->b_saveaddr = (caddr_t) b_save;
+
+ bp->b_flags |= B_CALL;
+ bp->b_iodone = cluster_callback;
+ cp = (char *)bp->b_data + size;
+ for (++start_lbn, i = 0; i < len; ++i, ++start_lbn) {
+ /*
+ * Block is not in core or the non-sequential block
+ * ending our cluster was part of the cluster (in which
+ * case we don't want to write it twice).
+ */
+ if (!incore(vp, start_lbn) ||
+ last_bp == NULL && start_lbn == lbn)
+ break;
+
+ /*
+ * Get the desired block buffer (unless it is the final
+ * sequential block whose buffer was passed in explictly
+ * as last_bp).
+ */
+ if (last_bp == NULL || start_lbn != lbn) {
+ tbp = getblk(vp, start_lbn, size, 0, 0);
+ if (!(tbp->b_flags & B_DELWRI)) {
+ brelse(tbp);
+ break;
+ }
+ } else
+ tbp = last_bp;
+
+ ++b_save->bs_nchildren;
+
+ /* Move memory from children to parent */
+ if (tbp->b_blkno != (bp->b_blkno + btodb(bp->b_bufsize))) {
+ printf("Clustered Block: %d addr %x bufsize: %d\n",
+ bp->b_lblkno, bp->b_blkno, bp->b_bufsize);
+ printf("Child Block: %d addr: %x\n", tbp->b_lblkno,
+ tbp->b_blkno);
+ panic("Clustered write to wrong blocks");
+ }
+
+ pagemove(tbp->b_data, cp, size);
+ bp->b_bcount += size;
+ bp->b_bufsize += size;
+
+ tbp->b_bufsize -= size;
+ tbp->b_flags &= ~(B_READ | B_DONE | B_ERROR | B_DELWRI);
+ tbp->b_flags |= (B_ASYNC | B_AGE);
+ s = splbio();
+ reassignbuf(tbp, tbp->b_vp); /* put on clean list */
+ ++tbp->b_vp->v_numoutput;
+ splx(s);
+ b_save->bs_children[i] = tbp;
+
+ cp += size;
+ }
+
+ if (i == 0) {
+ /* None to cluster */
+ bp->b_saveaddr = b_save->bs_saveaddr;
+ bp->b_flags &= ~B_CALL;
+ bp->b_iodone = NULL;
+ free(b_save, M_SEGMENT);
+ }
+ bawrite(bp);
+ if (i < len) {
+ len -= i + 1;
+ start_lbn += 1;
+ goto redo;
+ }
+}
+
+/*
+ * Collect together all the buffers in a cluster.
+ * Plus add one additional buffer.
+ */
+struct cluster_save *
+cluster_collectbufs(vp, last_bp)
+ struct vnode *vp;
+ struct buf *last_bp;
+{
+ struct cluster_save *buflist;
+ daddr_t lbn;
+ int i, len;
+
+ len = vp->v_lastw - vp->v_cstart + 1;
+ buflist = malloc(sizeof(struct buf *) * (len + 1) + sizeof(*buflist),
+ M_SEGMENT, M_WAITOK);
+ buflist->bs_nchildren = 0;
+ buflist->bs_children = (struct buf **)(buflist + 1);
+ for (lbn = vp->v_cstart, i = 0; i < len; lbn++, i++)
+ (void)bread(vp, lbn, last_bp->b_bcount, NOCRED,
+ &buflist->bs_children[i]);
+ buflist->bs_children[i] = last_bp;
+ buflist->bs_nchildren = i + 1;
+ return (buflist);
+}
diff --git a/sys/kern/vfs_conf.c b/sys/kern/vfs_conf.c
new file mode 100644
index 0000000..2fe39eb
--- /dev/null
+++ b/sys/kern/vfs_conf.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vfs_conf.c 8.8 (Berkeley) 3/31/94
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+
+#ifdef FFS
+#include <ufs/ffs/ffs_extern.h>
+
+/*
+ * This specifies the filesystem used to mount the root.
+ * This specification should be done by /etc/config.
+ */
+int (*mountroot)() = ffs_mountroot;
+#endif
+
+/*
+ * These define the root filesystem and device.
+ */
+struct mount *rootfs;
+struct vnode *rootvnode;
+
+/*
+ * Set up the filesystem operations for vnodes.
+ * The types are defined in mount.h.
+ */
+#ifdef FFS
+extern struct vfsops ufs_vfsops;
+#define UFS_VFSOPS &ufs_vfsops
+#else
+#define UFS_VFSOPS NULL
+#endif
+
+#ifdef LFS
+extern struct vfsops lfs_vfsops;
+#define LFS_VFSOPS &lfs_vfsops
+#else
+#define LFS_VFSOPS NULL
+#endif
+
+#ifdef MFS
+extern struct vfsops mfs_vfsops;
+#define MFS_VFSOPS &mfs_vfsops
+#else
+#define MFS_VFSOPS NULL
+#endif
+
+#ifdef NFS
+extern struct vfsops nfs_vfsops;
+#define NFS_VFSOPS &nfs_vfsops
+#else
+#define NFS_VFSOPS NULL
+#endif
+
+#ifdef FDESC
+extern struct vfsops fdesc_vfsops;
+#define FDESC_VFSOPS &fdesc_vfsops
+#else
+#define FDESC_VFSOPS NULL
+#endif
+
+#ifdef PORTAL
+extern struct vfsops portal_vfsops;
+#define PORTAL_VFSOPS &portal_vfsops
+#else
+#define PORTAL_VFSOPS NULL
+#endif
+
+#ifdef NULLFS
+extern struct vfsops null_vfsops;
+#define NULL_VFSOPS &null_vfsops
+#else
+#define NULL_VFSOPS NULL
+#endif
+
+#ifdef UMAPFS
+extern struct vfsops umap_vfsops;
+#define UMAP_VFSOPS &umap_vfsops
+#else
+#define UMAP_VFSOPS NULL
+#endif
+
+#ifdef KERNFS
+extern struct vfsops kernfs_vfsops;
+#define KERNFS_VFSOPS &kernfs_vfsops
+#else
+#define KERNFS_VFSOPS NULL
+#endif
+
+#ifdef PROCFS
+extern struct vfsops procfs_vfsops;
+#define PROCFS_VFSOPS &procfs_vfsops
+#else
+#define PROCFS_VFSOPS NULL
+#endif
+
+#ifdef AFS
+extern struct vfsops afs_vfsops;
+#define AFS_VFSOPS &afs_vfsops
+#else
+#define AFS_VFSOPS NULL
+#endif
+
+#ifdef CD9660
+extern struct vfsops cd9660_vfsops;
+#define CD9660_VFSOPS &cd9660_vfsops
+#else
+#define CD9660_VFSOPS NULL
+#endif
+
+#ifdef UNION
+extern struct vfsops union_vfsops;
+#define UNION_VFSOPS &union_vfsops
+#else
+#define UNION_VFSOPS NULL
+#endif
+
+struct vfsops *vfssw[] = {
+ NULL, /* 0 = MOUNT_NONE */
+ UFS_VFSOPS, /* 1 = MOUNT_UFS */
+ NFS_VFSOPS, /* 2 = MOUNT_NFS */
+ MFS_VFSOPS, /* 3 = MOUNT_MFS */
+ NULL, /* 4 = MOUNT_PC */
+ LFS_VFSOPS, /* 5 = MOUNT_LFS */
+ NULL, /* 6 = MOUNT_LOFS */
+ FDESC_VFSOPS, /* 7 = MOUNT_FDESC */
+ PORTAL_VFSOPS, /* 8 = MOUNT_PORTAL */
+ NULL_VFSOPS, /* 9 = MOUNT_NULL */
+ UMAP_VFSOPS, /* 10 = MOUNT_UMAP */
+ KERNFS_VFSOPS, /* 11 = MOUNT_KERNFS */
+ PROCFS_VFSOPS, /* 12 = MOUNT_PROCFS */
+ AFS_VFSOPS, /* 13 = MOUNT_AFS */
+ CD9660_VFSOPS, /* 14 = MOUNT_CD9660 */
+ UNION_VFSOPS, /* 15 = MOUNT_UNION */
+ 0
+};
+
+
+/*
+ *
+ * vfs_opv_descs enumerates the list of vnode classes, each with it's own
+ * vnode operation vector. It is consulted at system boot to build operation
+ * vectors. It is NULL terminated.
+ *
+ */
+extern struct vnodeopv_desc ffs_vnodeop_opv_desc;
+extern struct vnodeopv_desc ffs_specop_opv_desc;
+extern struct vnodeopv_desc ffs_fifoop_opv_desc;
+extern struct vnodeopv_desc lfs_vnodeop_opv_desc;
+extern struct vnodeopv_desc lfs_specop_opv_desc;
+extern struct vnodeopv_desc lfs_fifoop_opv_desc;
+extern struct vnodeopv_desc mfs_vnodeop_opv_desc;
+extern struct vnodeopv_desc dead_vnodeop_opv_desc;
+extern struct vnodeopv_desc fifo_vnodeop_opv_desc;
+extern struct vnodeopv_desc spec_vnodeop_opv_desc;
+extern struct vnodeopv_desc nfsv2_vnodeop_opv_desc;
+extern struct vnodeopv_desc spec_nfsv2nodeop_opv_desc;
+extern struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc;
+extern struct vnodeopv_desc fdesc_vnodeop_opv_desc;
+extern struct vnodeopv_desc portal_vnodeop_opv_desc;
+extern struct vnodeopv_desc null_vnodeop_opv_desc;
+extern struct vnodeopv_desc umap_vnodeop_opv_desc;
+extern struct vnodeopv_desc kernfs_vnodeop_opv_desc;
+extern struct vnodeopv_desc procfs_vnodeop_opv_desc;
+extern struct vnodeopv_desc cd9660_vnodeop_opv_desc;
+extern struct vnodeopv_desc cd9660_specop_opv_desc;
+extern struct vnodeopv_desc cd9660_fifoop_opv_desc;
+extern struct vnodeopv_desc union_vnodeop_opv_desc;
+
+struct vnodeopv_desc *vfs_opv_descs[] = {
+ &ffs_vnodeop_opv_desc,
+ &ffs_specop_opv_desc,
+#ifdef FIFO
+ &ffs_fifoop_opv_desc,
+#endif
+ &dead_vnodeop_opv_desc,
+#ifdef FIFO
+ &fifo_vnodeop_opv_desc,
+#endif
+ &spec_vnodeop_opv_desc,
+#ifdef LFS
+ &lfs_vnodeop_opv_desc,
+ &lfs_specop_opv_desc,
+#ifdef FIFO
+ &lfs_fifoop_opv_desc,
+#endif
+#endif
+#ifdef MFS
+ &mfs_vnodeop_opv_desc,
+#endif
+#ifdef NFS
+ &nfsv2_vnodeop_opv_desc,
+ &spec_nfsv2nodeop_opv_desc,
+#ifdef FIFO
+ &fifo_nfsv2nodeop_opv_desc,
+#endif
+#endif
+#ifdef FDESC
+ &fdesc_vnodeop_opv_desc,
+#endif
+#ifdef PORTAL
+ &portal_vnodeop_opv_desc,
+#endif
+#ifdef NULLFS
+ &null_vnodeop_opv_desc,
+#endif
+#ifdef UMAPFS
+ &umap_vnodeop_opv_desc,
+#endif
+#ifdef KERNFS
+ &kernfs_vnodeop_opv_desc,
+#endif
+#ifdef PROCFS
+ &procfs_vnodeop_opv_desc,
+#endif
+#ifdef CD9660
+ &cd9660_vnodeop_opv_desc,
+ &cd9660_specop_opv_desc,
+#ifdef FIFO
+ &cd9660_fifoop_opv_desc,
+#endif
+#endif
+#ifdef UNION
+ &union_vnodeop_opv_desc,
+#endif
+ NULL
+};
diff --git a/sys/kern/vfs_export.c b/sys/kern/vfs_export.c
new file mode 100644
index 0000000..9535b8a
--- /dev/null
+++ b/sys/kern/vfs_export.c
@@ -0,0 +1,1351 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vfs_subr.c 8.13 (Berkeley) 4/18/94
+ */
+
+/*
+ * External virtual filesystem routines
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/namei.h>
+#include <sys/ucred.h>
+#include <sys/buf.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/domain.h>
+#include <sys/mbuf.h>
+
+#include <vm/vm.h>
+#include <sys/sysctl.h>
+
+#include <miscfs/specfs/specdev.h>
+
+void insmntque __P((struct vnode *, struct mount *));
+
+enum vtype iftovt_tab[16] = {
+ VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON,
+ VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VBAD,
+};
+int vttoif_tab[9] = {
+ 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK,
+ S_IFSOCK, S_IFIFO, S_IFMT,
+};
+
+/*
+ * Insq/Remq for the vnode usage lists.
+ */
+#define bufinsvn(bp, dp) LIST_INSERT_HEAD(dp, bp, b_vnbufs)
+#define bufremvn(bp) { \
+ LIST_REMOVE(bp, b_vnbufs); \
+ (bp)->b_vnbufs.le_next = NOLIST; \
+}
+
+TAILQ_HEAD(freelst, vnode) vnode_free_list; /* vnode free list */
+struct mntlist mountlist; /* mounted filesystem list */
+
+/*
+ * Initialize the vnode management data structures.
+ */
+void
+vntblinit()
+{
+
+ TAILQ_INIT(&vnode_free_list);
+ TAILQ_INIT(&mountlist);
+}
+
+/*
+ * Lock a filesystem.
+ * Used to prevent access to it while mounting and unmounting.
+ */
+int
+vfs_lock(mp)
+ register struct mount *mp;
+{
+
+ while(mp->mnt_flag & MNT_MLOCK) {
+ mp->mnt_flag |= MNT_MWAIT;
+ sleep((caddr_t)mp, PVFS);
+ }
+ mp->mnt_flag |= MNT_MLOCK;
+ return (0);
+}
+
+/*
+ * Unlock a locked filesystem.
+ * Panic if filesystem is not locked.
+ */
+void
+vfs_unlock(mp)
+ register struct mount *mp;
+{
+
+ if ((mp->mnt_flag & MNT_MLOCK) == 0)
+ panic("vfs_unlock: not locked");
+ mp->mnt_flag &= ~MNT_MLOCK;
+ if (mp->mnt_flag & MNT_MWAIT) {
+ mp->mnt_flag &= ~MNT_MWAIT;
+ wakeup((caddr_t)mp);
+ }
+}
+
+/*
+ * Mark a mount point as busy.
+ * Used to synchronize access and to delay unmounting.
+ */
+int
+vfs_busy(mp)
+ register struct mount *mp;
+{
+
+ while(mp->mnt_flag & MNT_MPBUSY) {
+ mp->mnt_flag |= MNT_MPWANT;
+ sleep((caddr_t)&mp->mnt_flag, PVFS);
+ }
+ if (mp->mnt_flag & MNT_UNMOUNT)
+ return (1);
+ mp->mnt_flag |= MNT_MPBUSY;
+ return (0);
+}
+
+/*
+ * Free a busy filesystem.
+ * Panic if filesystem is not busy.
+ */
+void
+vfs_unbusy(mp)
+ register struct mount *mp;
+{
+
+ if ((mp->mnt_flag & MNT_MPBUSY) == 0)
+ panic("vfs_unbusy: not busy");
+ mp->mnt_flag &= ~MNT_MPBUSY;
+ if (mp->mnt_flag & MNT_MPWANT) {
+ mp->mnt_flag &= ~MNT_MPWANT;
+ wakeup((caddr_t)&mp->mnt_flag);
+ }
+}
+
+/*
+ * Lookup a mount point by filesystem identifier.
+ */
+struct mount *
+getvfs(fsid)
+ fsid_t *fsid;
+{
+ register struct mount *mp;
+
+ for (mp = mountlist.tqh_first; mp != NULL; mp = mp->mnt_list.tqe_next) {
+ if (mp->mnt_stat.f_fsid.val[0] == fsid->val[0] &&
+ mp->mnt_stat.f_fsid.val[1] == fsid->val[1])
+ return (mp);
+ }
+ return ((struct mount *)0);
+}
+
+/*
+ * Get a new unique fsid
+ */
+void
+getnewfsid(mp, mtype)
+ struct mount *mp;
+ int mtype;
+{
+static u_short xxxfs_mntid;
+
+ fsid_t tfsid;
+
+ mp->mnt_stat.f_fsid.val[0] = makedev(nblkdev + mtype, 0);
+ mp->mnt_stat.f_fsid.val[1] = mtype;
+ if (xxxfs_mntid == 0)
+ ++xxxfs_mntid;
+ tfsid.val[0] = makedev(nblkdev + mtype, xxxfs_mntid);
+ tfsid.val[1] = mtype;
+ if (mountlist.tqh_first != NULL) {
+ while (getvfs(&tfsid)) {
+ tfsid.val[0]++;
+ xxxfs_mntid++;
+ }
+ }
+ mp->mnt_stat.f_fsid.val[0] = tfsid.val[0];
+}
+
+/*
+ * Set vnode attributes to VNOVAL
+ */
+void
+vattr_null(vap)
+ register struct vattr *vap;
+{
+
+ vap->va_type = VNON;
+ vap->va_size = VNOVAL;
+ vap->va_bytes = VNOVAL;
+ vap->va_mode = vap->va_nlink = vap->va_uid = vap->va_gid =
+ vap->va_fsid = vap->va_fileid =
+ vap->va_blocksize = vap->va_rdev =
+ vap->va_atime.ts_sec = vap->va_atime.ts_nsec =
+ vap->va_mtime.ts_sec = vap->va_mtime.ts_nsec =
+ vap->va_ctime.ts_sec = vap->va_ctime.ts_nsec =
+ vap->va_flags = vap->va_gen = VNOVAL;
+ vap->va_vaflags = 0;
+}
+
+/*
+ * Routines having to do with the management of the vnode table.
+ */
+extern int (**dead_vnodeop_p)();
+extern void vclean();
+long numvnodes;
+extern struct vattr va_null;
+
+/*
+ * Return the next vnode from the free list.
+ */
+int
+getnewvnode(tag, mp, vops, vpp)
+ enum vtagtype tag;
+ struct mount *mp;
+ int (**vops)();
+ struct vnode **vpp;
+{
+ register struct vnode *vp;
+ int s;
+
+ if ((vnode_free_list.tqh_first == NULL &&
+ numvnodes < 2 * desiredvnodes) ||
+ numvnodes < desiredvnodes) {
+ vp = (struct vnode *)malloc((u_long)sizeof *vp,
+ M_VNODE, M_WAITOK);
+ bzero((char *)vp, sizeof *vp);
+ numvnodes++;
+ } else {
+ if ((vp = vnode_free_list.tqh_first) == NULL) {
+ tablefull("vnode");
+ *vpp = 0;
+ return (ENFILE);
+ }
+ if (vp->v_usecount)
+ panic("free vnode isn't");
+ TAILQ_REMOVE(&vnode_free_list, vp, v_freelist);
+ /* see comment on why 0xdeadb is set at end of vgone (below) */
+ vp->v_freelist.tqe_prev = (struct vnode **)0xdeadb;
+ vp->v_lease = NULL;
+ if (vp->v_type != VBAD)
+ vgone(vp);
+#ifdef DIAGNOSTIC
+ if (vp->v_data)
+ panic("cleaned vnode isn't");
+ s = splbio();
+ if (vp->v_numoutput)
+ panic("Clean vnode has pending I/O's");
+ splx(s);
+#endif
+ vp->v_flag = 0;
+ vp->v_lastr = 0;
+ vp->v_ralen = 0;
+ vp->v_maxra = 0;
+ vp->v_lastw = 0;
+ vp->v_lasta = 0;
+ vp->v_cstart = 0;
+ vp->v_clen = 0;
+ vp->v_socket = 0;
+ }
+ vp->v_type = VNON;
+ cache_purge(vp);
+ vp->v_tag = tag;
+ vp->v_op = vops;
+ insmntque(vp, mp);
+ *vpp = vp;
+ vp->v_usecount = 1;
+ vp->v_data = 0;
+ return (0);
+}
+
+/*
+ * Move a vnode from one mount queue to another.
+ */
+void
+insmntque(vp, mp)
+ register struct vnode *vp;
+ register struct mount *mp;
+{
+
+ /*
+ * Delete from old mount point vnode list, if on one.
+ */
+ if (vp->v_mount != NULL)
+ LIST_REMOVE(vp, v_mntvnodes);
+ /*
+ * Insert into list of vnodes for the new mount point, if available.
+ */
+ if ((vp->v_mount = mp) == NULL)
+ return;
+ LIST_INSERT_HEAD(&mp->mnt_vnodelist, vp, v_mntvnodes);
+}
+
+/*
+ * Update outstanding I/O count and do wakeup if requested.
+ */
+void
+vwakeup(bp)
+ register struct buf *bp;
+{
+ register struct vnode *vp;
+
+ bp->b_flags &= ~B_WRITEINPROG;
+ if (vp = bp->b_vp) {
+ vp->v_numoutput--;
+ if (vp->v_numoutput < 0)
+ panic("vwakeup: neg numoutput");
+ if ((vp->v_flag & VBWAIT) && vp->v_numoutput <= 0) {
+ if (vp->v_numoutput < 0)
+ panic("vwakeup: neg numoutput");
+ vp->v_flag &= ~VBWAIT;
+ wakeup((caddr_t)&vp->v_numoutput);
+ }
+ }
+}
+
+/*
+ * Flush out and invalidate all buffers associated with a vnode.
+ * Called with the underlying object locked.
+ */
+int
+vinvalbuf(vp, flags, cred, p, slpflag, slptimeo)
+ register struct vnode *vp;
+ int flags;
+ struct ucred *cred;
+ struct proc *p;
+ int slpflag, slptimeo;
+{
+ register struct buf *bp;
+ struct buf *nbp, *blist;
+ int s, error;
+
+ if (flags & V_SAVE) {
+ if (error = VOP_FSYNC(vp, cred, MNT_WAIT, p))
+ return (error);
+ if (vp->v_dirtyblkhd.lh_first != NULL)
+ panic("vinvalbuf: dirty bufs");
+ }
+ for (;;) {
+ if ((blist = vp->v_cleanblkhd.lh_first) && flags & V_SAVEMETA)
+ while (blist && blist->b_lblkno < 0)
+ blist = blist->b_vnbufs.le_next;
+ if (!blist && (blist = vp->v_dirtyblkhd.lh_first) &&
+ (flags & V_SAVEMETA))
+ while (blist && blist->b_lblkno < 0)
+ blist = blist->b_vnbufs.le_next;
+ if (!blist)
+ break;
+
+ for (bp = blist; bp; bp = nbp) {
+ nbp = bp->b_vnbufs.le_next;
+ if (flags & V_SAVEMETA && bp->b_lblkno < 0)
+ continue;
+ s = splbio();
+ if (bp->b_flags & B_BUSY) {
+ bp->b_flags |= B_WANTED;
+ error = tsleep((caddr_t)bp,
+ slpflag | (PRIBIO + 1), "vinvalbuf",
+ slptimeo);
+ splx(s);
+ if (error)
+ return (error);
+ break;
+ }
+ bremfree(bp);
+ bp->b_flags |= B_BUSY;
+ splx(s);
+ /*
+ * XXX Since there are no node locks for NFS, I believe
+ * there is a slight chance that a delayed write will
+ * occur while sleeping just above, so check for it.
+ */
+ if ((bp->b_flags & B_DELWRI) && (flags & V_SAVE)) {
+ (void) VOP_BWRITE(bp);
+ break;
+ }
+ bp->b_flags |= B_INVAL;
+ brelse(bp);
+ }
+ }
+ if (!(flags & V_SAVEMETA) &&
+ (vp->v_dirtyblkhd.lh_first || vp->v_cleanblkhd.lh_first))
+ panic("vinvalbuf: flush failed");
+ return (0);
+}
+
+/*
+ * Associate a buffer with a vnode.
+ */
+void
+bgetvp(vp, bp)
+ register struct vnode *vp;
+ register struct buf *bp;
+{
+
+ if (bp->b_vp)
+ panic("bgetvp: not free");
+ VHOLD(vp);
+ bp->b_vp = vp;
+ if (vp->v_type == VBLK || vp->v_type == VCHR)
+ bp->b_dev = vp->v_rdev;
+ else
+ bp->b_dev = NODEV;
+ /*
+ * Insert onto list for new vnode.
+ */
+ bufinsvn(bp, &vp->v_cleanblkhd);
+}
+
+/*
+ * Disassociate a buffer from a vnode.
+ */
+void
+brelvp(bp)
+ register struct buf *bp;
+{
+ struct vnode *vp;
+
+ if (bp->b_vp == (struct vnode *) 0)
+ panic("brelvp: NULL");
+ /*
+ * Delete from old vnode list, if on one.
+ */
+ if (bp->b_vnbufs.le_next != NOLIST)
+ bufremvn(bp);
+ vp = bp->b_vp;
+ bp->b_vp = (struct vnode *) 0;
+ HOLDRELE(vp);
+}
+
+/*
+ * Reassign a buffer from one vnode to another.
+ * Used to assign file specific control information
+ * (indirect blocks) to the vnode to which they belong.
+ */
+void
+reassignbuf(bp, newvp)
+ register struct buf *bp;
+ register struct vnode *newvp;
+{
+ register struct buflists *listheadp;
+
+ if (newvp == NULL) {
+ printf("reassignbuf: NULL");
+ return;
+ }
+ /*
+ * Delete from old vnode list, if on one.
+ */
+ if (bp->b_vnbufs.le_next != NOLIST)
+ bufremvn(bp);
+ /*
+ * If dirty, put on list of dirty buffers;
+ * otherwise insert onto list of clean buffers.
+ */
+ if (bp->b_flags & B_DELWRI)
+ listheadp = &newvp->v_dirtyblkhd;
+ else
+ listheadp = &newvp->v_cleanblkhd;
+ bufinsvn(bp, listheadp);
+}
+
+/*
+ * Create a vnode for a block device.
+ * Used for root filesystem, argdev, and swap areas.
+ * Also used for memory file system special devices.
+ */
+int
+bdevvp(dev, vpp)
+ dev_t dev;
+ struct vnode **vpp;
+{
+ register struct vnode *vp;
+ struct vnode *nvp;
+ int error;
+
+ if (dev == NODEV)
+ return (0);
+ error = getnewvnode(VT_NON, (struct mount *)0, spec_vnodeop_p, &nvp);
+ if (error) {
+ *vpp = 0;
+ return (error);
+ }
+ vp = nvp;
+ vp->v_type = VBLK;
+ if (nvp = checkalias(vp, dev, (struct mount *)0)) {
+ vput(vp);
+ vp = nvp;
+ }
+ *vpp = vp;
+ return (0);
+}
+
+/*
+ * Check to see if the new vnode represents a special device
+ * for which we already have a vnode (either because of
+ * bdevvp() or because of a different vnode representing
+ * the same block device). If such an alias exists, deallocate
+ * the existing contents and return the aliased vnode. The
+ * caller is responsible for filling it with its new contents.
+ */
+struct vnode *
+checkalias(nvp, nvp_rdev, mp)
+ register struct vnode *nvp;
+ dev_t nvp_rdev;
+ struct mount *mp;
+{
+ register struct vnode *vp;
+ struct vnode **vpp;
+
+ if (nvp->v_type != VBLK && nvp->v_type != VCHR)
+ return (NULLVP);
+
+ vpp = &speclisth[SPECHASH(nvp_rdev)];
+loop:
+ for (vp = *vpp; vp; vp = vp->v_specnext) {
+ if (nvp_rdev != vp->v_rdev || nvp->v_type != vp->v_type)
+ continue;
+ /*
+ * Alias, but not in use, so flush it out.
+ */
+ if (vp->v_usecount == 0) {
+ vgone(vp);
+ goto loop;
+ }
+ if (vget(vp, 1))
+ goto loop;
+ break;
+ }
+ if (vp == NULL || vp->v_tag != VT_NON) {
+ MALLOC(nvp->v_specinfo, struct specinfo *,
+ sizeof(struct specinfo), M_VNODE, M_WAITOK);
+ nvp->v_rdev = nvp_rdev;
+ nvp->v_hashchain = vpp;
+ nvp->v_specnext = *vpp;
+ nvp->v_specflags = 0;
+ *vpp = nvp;
+ if (vp != NULL) {
+ nvp->v_flag |= VALIASED;
+ vp->v_flag |= VALIASED;
+ vput(vp);
+ }
+ return (NULLVP);
+ }
+ VOP_UNLOCK(vp);
+ vclean(vp, 0);
+ vp->v_op = nvp->v_op;
+ vp->v_tag = nvp->v_tag;
+ nvp->v_type = VNON;
+ insmntque(vp, mp);
+ return (vp);
+}
+
+/*
+ * Grab a particular vnode from the free list, increment its
+ * reference count and lock it. The vnode lock bit is set the
+ * vnode is being eliminated in vgone. The process is awakened
+ * when the transition is completed, and an error returned to
+ * indicate that the vnode is no longer usable (possibly having
+ * been changed to a new file system type).
+ */
+int
+vget(vp, lockflag)
+ register struct vnode *vp;
+ int lockflag;
+{
+
+ /*
+ * If the vnode is in the process of being cleaned out for
+ * another use, we wait for the cleaning to finish and then
+ * return failure. Cleaning is determined either by checking
+ * that the VXLOCK flag is set, or that the use count is
+ * zero with the back pointer set to show that it has been
+ * removed from the free list by getnewvnode. The VXLOCK
+ * flag may not have been set yet because vclean is blocked in
+ * the VOP_LOCK call waiting for the VOP_INACTIVE to complete.
+ */
+ if ((vp->v_flag & VXLOCK) ||
+ (vp->v_usecount == 0 &&
+ vp->v_freelist.tqe_prev == (struct vnode **)0xdeadb)) {
+ vp->v_flag |= VXWANT;
+ sleep((caddr_t)vp, PINOD);
+ return (1);
+ }
+ if (vp->v_usecount == 0)
+ TAILQ_REMOVE(&vnode_free_list, vp, v_freelist);
+ vp->v_usecount++;
+ if (lockflag)
+ VOP_LOCK(vp);
+ return (0);
+}
+
+/*
+ * Vnode reference, just increment the count
+ */
+void
+vref(vp)
+ struct vnode *vp;
+{
+
+ if (vp->v_usecount <= 0)
+ panic("vref used where vget required");
+ vp->v_usecount++;
+}
+
+/*
+ * vput(), just unlock and vrele()
+ */
+void
+vput(vp)
+ register struct vnode *vp;
+{
+
+ VOP_UNLOCK(vp);
+ vrele(vp);
+}
+
+/*
+ * Vnode release.
+ * If count drops to zero, call inactive routine and return to freelist.
+ */
+void
+vrele(vp)
+ register struct vnode *vp;
+{
+
+#ifdef DIAGNOSTIC
+ if (vp == NULL)
+ panic("vrele: null vp");
+#endif
+ vp->v_usecount--;
+ if (vp->v_usecount > 0)
+ return;
+#ifdef DIAGNOSTIC
+ if (vp->v_usecount != 0 || vp->v_writecount != 0) {
+ vprint("vrele: bad ref count", vp);
+ panic("vrele: ref cnt");
+ }
+#endif
+ /*
+ * insert at tail of LRU list
+ */
+ TAILQ_INSERT_TAIL(&vnode_free_list, vp, v_freelist);
+ VOP_INACTIVE(vp);
+}
+
+/*
+ * Page or buffer structure gets a reference.
+ */
+void
+vhold(vp)
+ register struct vnode *vp;
+{
+
+ vp->v_holdcnt++;
+}
+
+/*
+ * Page or buffer structure frees a reference.
+ */
+void
+holdrele(vp)
+ register struct vnode *vp;
+{
+
+ if (vp->v_holdcnt <= 0)
+ panic("holdrele: holdcnt");
+ vp->v_holdcnt--;
+}
+
+/*
+ * Remove any vnodes in the vnode table belonging to mount point mp.
+ *
+ * If MNT_NOFORCE is specified, there should not be any active ones,
+ * return error if any are found (nb: this is a user error, not a
+ * system error). If MNT_FORCE is specified, detach any active vnodes
+ * that are found.
+ */
+#ifdef DIAGNOSTIC
+int busyprt = 0; /* print out busy vnodes */
+struct ctldebug debug1 = { "busyprt", &busyprt };
+#endif
+
+int
+vflush(mp, skipvp, flags)
+ struct mount *mp;
+ struct vnode *skipvp;
+ int flags;
+{
+ register struct vnode *vp, *nvp;
+ int busy = 0;
+
+ if ((mp->mnt_flag & MNT_MPBUSY) == 0)
+ panic("vflush: not busy");
+loop:
+ for (vp = mp->mnt_vnodelist.lh_first; vp; vp = nvp) {
+ if (vp->v_mount != mp)
+ goto loop;
+ nvp = vp->v_mntvnodes.le_next;
+ /*
+ * Skip over a selected vnode.
+ */
+ if (vp == skipvp)
+ continue;
+ /*
+ * Skip over a vnodes marked VSYSTEM.
+ */
+ if ((flags & SKIPSYSTEM) && (vp->v_flag & VSYSTEM))
+ continue;
+ /*
+ * If WRITECLOSE is set, only flush out regular file
+ * vnodes open for writing.
+ */
+ if ((flags & WRITECLOSE) &&
+ (vp->v_writecount == 0 || vp->v_type != VREG))
+ continue;
+ /*
+ * With v_usecount == 0, all we need to do is clear
+ * out the vnode data structures and we are done.
+ */
+ if (vp->v_usecount == 0) {
+ vgone(vp);
+ continue;
+ }
+ /*
+ * If FORCECLOSE is set, forcibly close the vnode.
+ * For block or character devices, revert to an
+ * anonymous device. For all other files, just kill them.
+ */
+ if (flags & FORCECLOSE) {
+ if (vp->v_type != VBLK && vp->v_type != VCHR) {
+ vgone(vp);
+ } else {
+ vclean(vp, 0);
+ vp->v_op = spec_vnodeop_p;
+ insmntque(vp, (struct mount *)0);
+ }
+ continue;
+ }
+#ifdef DIAGNOSTIC
+ if (busyprt)
+ vprint("vflush: busy vnode", vp);
+#endif
+ busy++;
+ }
+ if (busy)
+ return (EBUSY);
+ return (0);
+}
+
+/*
+ * Disassociate the underlying file system from a vnode.
+ */
+void
+vclean(vp, flags)
+ register struct vnode *vp;
+ int flags;
+{
+ int active;
+
+ /*
+ * Check to see if the vnode is in use.
+ * If so we have to reference it before we clean it out
+ * so that its count cannot fall to zero and generate a
+ * race against ourselves to recycle it.
+ */
+ if (active = vp->v_usecount)
+ VREF(vp);
+ /*
+ * Even if the count is zero, the VOP_INACTIVE routine may still
+ * have the object locked while it cleans it out. The VOP_LOCK
+ * ensures that the VOP_INACTIVE routine is done with its work.
+ * For active vnodes, it ensures that no other activity can
+ * occur while the underlying object is being cleaned out.
+ */
+ VOP_LOCK(vp);
+ /*
+ * Prevent the vnode from being recycled or
+ * brought into use while we clean it out.
+ */
+ if (vp->v_flag & VXLOCK)
+ panic("vclean: deadlock");
+ vp->v_flag |= VXLOCK;
+ /*
+ * Clean out any buffers associated with the vnode.
+ */
+ if (flags & DOCLOSE)
+ vinvalbuf(vp, V_SAVE, NOCRED, NULL, 0, 0);
+ /*
+ * Any other processes trying to obtain this lock must first
+ * wait for VXLOCK to clear, then call the new lock operation.
+ */
+ VOP_UNLOCK(vp);
+ /*
+ * If purging an active vnode, it must be closed and
+ * deactivated before being reclaimed.
+ */
+ if (active) {
+ if (flags & DOCLOSE)
+ VOP_CLOSE(vp, IO_NDELAY, NOCRED, NULL);
+ VOP_INACTIVE(vp);
+ }
+ /*
+ * Reclaim the vnode.
+ */
+ if (VOP_RECLAIM(vp))
+ panic("vclean: cannot reclaim");
+ if (active)
+ vrele(vp);
+
+ /*
+ * Done with purge, notify sleepers of the grim news.
+ */
+ vp->v_op = dead_vnodeop_p;
+ vp->v_tag = VT_NON;
+ vp->v_flag &= ~VXLOCK;
+ if (vp->v_flag & VXWANT) {
+ vp->v_flag &= ~VXWANT;
+ wakeup((caddr_t)vp);
+ }
+}
+
+/*
+ * Eliminate all activity associated with the requested vnode
+ * and with all vnodes aliased to the requested vnode.
+ */
+void
+vgoneall(vp)
+ register struct vnode *vp;
+{
+ register struct vnode *vq;
+
+ if (vp->v_flag & VALIASED) {
+ /*
+ * If a vgone (or vclean) is already in progress,
+ * wait until it is done and return.
+ */
+ if (vp->v_flag & VXLOCK) {
+ vp->v_flag |= VXWANT;
+ sleep((caddr_t)vp, PINOD);
+ return;
+ }
+ /*
+ * Ensure that vp will not be vgone'd while we
+ * are eliminating its aliases.
+ */
+ vp->v_flag |= VXLOCK;
+ while (vp->v_flag & VALIASED) {
+ for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) {
+ if (vq->v_rdev != vp->v_rdev ||
+ vq->v_type != vp->v_type || vp == vq)
+ continue;
+ vgone(vq);
+ break;
+ }
+ }
+ /*
+ * Remove the lock so that vgone below will
+ * really eliminate the vnode after which time
+ * vgone will awaken any sleepers.
+ */
+ vp->v_flag &= ~VXLOCK;
+ }
+ vgone(vp);
+}
+
+/*
+ * Eliminate all activity associated with a vnode
+ * in preparation for reuse.
+ */
+void
+vgone(vp)
+ register struct vnode *vp;
+{
+ register struct vnode *vq;
+ struct vnode *vx;
+
+ /*
+ * If a vgone (or vclean) is already in progress,
+ * wait until it is done and return.
+ */
+ if (vp->v_flag & VXLOCK) {
+ vp->v_flag |= VXWANT;
+ sleep((caddr_t)vp, PINOD);
+ return;
+ }
+ /*
+ * Clean out the filesystem specific data.
+ */
+ vclean(vp, DOCLOSE);
+ /*
+ * Delete from old mount point vnode list, if on one.
+ */
+ if (vp->v_mount != NULL) {
+ LIST_REMOVE(vp, v_mntvnodes);
+ vp->v_mount = NULL;
+ }
+ /*
+ * If special device, remove it from special device alias list.
+ */
+ if (vp->v_type == VBLK || vp->v_type == VCHR) {
+ if (*vp->v_hashchain == vp) {
+ *vp->v_hashchain = vp->v_specnext;
+ } else {
+ for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) {
+ if (vq->v_specnext != vp)
+ continue;
+ vq->v_specnext = vp->v_specnext;
+ break;
+ }
+ if (vq == NULL)
+ panic("missing bdev");
+ }
+ if (vp->v_flag & VALIASED) {
+ vx = NULL;
+ for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) {
+ if (vq->v_rdev != vp->v_rdev ||
+ vq->v_type != vp->v_type)
+ continue;
+ if (vx)
+ break;
+ vx = vq;
+ }
+ if (vx == NULL)
+ panic("missing alias");
+ if (vq == NULL)
+ vx->v_flag &= ~VALIASED;
+ vp->v_flag &= ~VALIASED;
+ }
+ FREE(vp->v_specinfo, M_VNODE);
+ vp->v_specinfo = NULL;
+ }
+ /*
+ * If it is on the freelist and not already at the head,
+ * move it to the head of the list. The test of the back
+ * pointer and the reference count of zero is because
+ * it will be removed from the free list by getnewvnode,
+ * but will not have its reference count incremented until
+ * after calling vgone. If the reference count were
+ * incremented first, vgone would (incorrectly) try to
+ * close the previous instance of the underlying object.
+ * So, the back pointer is explicitly set to `0xdeadb' in
+ * getnewvnode after removing it from the freelist to ensure
+ * that we do not try to move it here.
+ */
+ if (vp->v_usecount == 0 &&
+ vp->v_freelist.tqe_prev != (struct vnode **)0xdeadb &&
+ vnode_free_list.tqh_first != vp) {
+ TAILQ_REMOVE(&vnode_free_list, vp, v_freelist);
+ TAILQ_INSERT_HEAD(&vnode_free_list, vp, v_freelist);
+ }
+ vp->v_type = VBAD;
+}
+
+/*
+ * Lookup a vnode by device number.
+ */
+int
+vfinddev(dev, type, vpp)
+ dev_t dev;
+ enum vtype type;
+ struct vnode **vpp;
+{
+ register struct vnode *vp;
+
+ for (vp = speclisth[SPECHASH(dev)]; vp; vp = vp->v_specnext) {
+ if (dev != vp->v_rdev || type != vp->v_type)
+ continue;
+ *vpp = vp;
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Calculate the total number of references to a special device.
+ */
+int
+vcount(vp)
+ register struct vnode *vp;
+{
+ register struct vnode *vq, *vnext;
+ int count;
+
+loop:
+ if ((vp->v_flag & VALIASED) == 0)
+ return (vp->v_usecount);
+ for (count = 0, vq = *vp->v_hashchain; vq; vq = vnext) {
+ vnext = vq->v_specnext;
+ if (vq->v_rdev != vp->v_rdev || vq->v_type != vp->v_type)
+ continue;
+ /*
+ * Alias, but not in use, so flush it out.
+ */
+ if (vq->v_usecount == 0 && vq != vp) {
+ vgone(vq);
+ goto loop;
+ }
+ count += vq->v_usecount;
+ }
+ return (count);
+}
+
+/*
+ * Print out a description of a vnode.
+ */
+static char *typename[] =
+ { "VNON", "VREG", "VDIR", "VBLK", "VCHR", "VLNK", "VSOCK", "VFIFO", "VBAD" };
+
+void
+vprint(label, vp)
+ char *label;
+ register struct vnode *vp;
+{
+ char buf[64];
+
+ if (label != NULL)
+ printf("%s: ", label);
+ printf("type %s, usecount %d, writecount %d, refcount %d,",
+ typename[vp->v_type], vp->v_usecount, vp->v_writecount,
+ vp->v_holdcnt);
+ buf[0] = '\0';
+ if (vp->v_flag & VROOT)
+ strcat(buf, "|VROOT");
+ if (vp->v_flag & VTEXT)
+ strcat(buf, "|VTEXT");
+ if (vp->v_flag & VSYSTEM)
+ strcat(buf, "|VSYSTEM");
+ if (vp->v_flag & VXLOCK)
+ strcat(buf, "|VXLOCK");
+ if (vp->v_flag & VXWANT)
+ strcat(buf, "|VXWANT");
+ if (vp->v_flag & VBWAIT)
+ strcat(buf, "|VBWAIT");
+ if (vp->v_flag & VALIASED)
+ strcat(buf, "|VALIASED");
+ if (buf[0] != '\0')
+ printf(" flags (%s)", &buf[1]);
+ if (vp->v_data == NULL) {
+ printf("\n");
+ } else {
+ printf("\n\t");
+ VOP_PRINT(vp);
+ }
+}
+
+#ifdef DEBUG
+/*
+ * List all of the locked vnodes in the system.
+ * Called when debugging the kernel.
+ */
+void
+printlockedvnodes()
+{
+ register struct mount *mp;
+ register struct vnode *vp;
+
+ printf("Locked vnodes\n");
+ for (mp = mountlist.tqh_first; mp != NULL; mp = mp->mnt_list.tqe_next) {
+ for (vp = mp->mnt_vnodelist.lh_first;
+ vp != NULL;
+ vp = vp->v_mntvnodes.le_next)
+ if (VOP_ISLOCKED(vp))
+ vprint((char *)0, vp);
+ }
+}
+#endif
+
+int kinfo_vdebug = 1;
+int kinfo_vgetfailed;
+#define KINFO_VNODESLOP 10
+/*
+ * Dump vnode list (via sysctl).
+ * Copyout address of vnode followed by vnode.
+ */
+/* ARGSUSED */
+int
+sysctl_vnode(where, sizep)
+ char *where;
+ size_t *sizep;
+{
+ register struct mount *mp, *nmp;
+ struct vnode *vp;
+ register char *bp = where, *savebp;
+ char *ewhere;
+ int error;
+
+#define VPTRSZ sizeof (struct vnode *)
+#define VNODESZ sizeof (struct vnode)
+ if (where == NULL) {
+ *sizep = (numvnodes + KINFO_VNODESLOP) * (VPTRSZ + VNODESZ);
+ return (0);
+ }
+ ewhere = where + *sizep;
+
+ for (mp = mountlist.tqh_first; mp != NULL; mp = nmp) {
+ nmp = mp->mnt_list.tqe_next;
+ if (vfs_busy(mp))
+ continue;
+ savebp = bp;
+again:
+ for (vp = mp->mnt_vnodelist.lh_first;
+ vp != NULL;
+ vp = vp->v_mntvnodes.le_next) {
+ /*
+ * Check that the vp is still associated with
+ * this filesystem. RACE: could have been
+ * recycled onto the same filesystem.
+ */
+ if (vp->v_mount != mp) {
+ if (kinfo_vdebug)
+ printf("kinfo: vp changed\n");
+ bp = savebp;
+ goto again;
+ }
+ if (bp + VPTRSZ + VNODESZ > ewhere) {
+ *sizep = bp - where;
+ return (ENOMEM);
+ }
+ if ((error = copyout((caddr_t)&vp, bp, VPTRSZ)) ||
+ (error = copyout((caddr_t)vp, bp + VPTRSZ, VNODESZ)))
+ return (error);
+ bp += VPTRSZ + VNODESZ;
+ }
+ vfs_unbusy(mp);
+ }
+
+ *sizep = bp - where;
+ return (0);
+}
+
+/*
+ * Check to see if a filesystem is mounted on a block device.
+ */
+int
+vfs_mountedon(vp)
+ register struct vnode *vp;
+{
+ register struct vnode *vq;
+
+ if (vp->v_specflags & SI_MOUNTEDON)
+ return (EBUSY);
+ if (vp->v_flag & VALIASED) {
+ for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) {
+ if (vq->v_rdev != vp->v_rdev ||
+ vq->v_type != vp->v_type)
+ continue;
+ if (vq->v_specflags & SI_MOUNTEDON)
+ return (EBUSY);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Build hash lists of net addresses and hang them off the mount point.
+ * Called by ufs_mount() to set up the lists of export addresses.
+ */
+static int
+vfs_hang_addrlist(mp, nep, argp)
+ struct mount *mp;
+ struct netexport *nep;
+ struct export_args *argp;
+{
+ register struct netcred *np;
+ register struct radix_node_head *rnh;
+ register int i;
+ struct radix_node *rn;
+ struct sockaddr *saddr, *smask = 0;
+ struct domain *dom;
+ int error;
+
+ if (argp->ex_addrlen == 0) {
+ if (mp->mnt_flag & MNT_DEFEXPORTED)
+ return (EPERM);
+ np = &nep->ne_defexported;
+ np->netc_exflags = argp->ex_flags;
+ np->netc_anon = argp->ex_anon;
+ np->netc_anon.cr_ref = 1;
+ mp->mnt_flag |= MNT_DEFEXPORTED;
+ return (0);
+ }
+ i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen;
+ np = (struct netcred *)malloc(i, M_NETADDR, M_WAITOK);
+ bzero((caddr_t)np, i);
+ saddr = (struct sockaddr *)(np + 1);
+ if (error = copyin(argp->ex_addr, (caddr_t)saddr, argp->ex_addrlen))
+ goto out;
+ if (saddr->sa_len > argp->ex_addrlen)
+ saddr->sa_len = argp->ex_addrlen;
+ if (argp->ex_masklen) {
+ smask = (struct sockaddr *)((caddr_t)saddr + argp->ex_addrlen);
+ error = copyin(argp->ex_addr, (caddr_t)smask, argp->ex_masklen);
+ if (error)
+ goto out;
+ if (smask->sa_len > argp->ex_masklen)
+ smask->sa_len = argp->ex_masklen;
+ }
+ i = saddr->sa_family;
+ if ((rnh = nep->ne_rtable[i]) == 0) {
+ /*
+ * Seems silly to initialize every AF when most are not
+ * used, do so on demand here
+ */
+ for (dom = domains; dom; dom = dom->dom_next)
+ if (dom->dom_family == i && dom->dom_rtattach) {
+ dom->dom_rtattach((void **)&nep->ne_rtable[i],
+ dom->dom_rtoffset);
+ break;
+ }
+ if ((rnh = nep->ne_rtable[i]) == 0) {
+ error = ENOBUFS;
+ goto out;
+ }
+ }
+ rn = (*rnh->rnh_addaddr)((caddr_t)saddr, (caddr_t)smask, rnh,
+ np->netc_rnodes);
+ if (rn == 0 || np != (struct netcred *)rn) { /* already exists */
+ error = EPERM;
+ goto out;
+ }
+ np->netc_exflags = argp->ex_flags;
+ np->netc_anon = argp->ex_anon;
+ np->netc_anon.cr_ref = 1;
+ return (0);
+out:
+ free(np, M_NETADDR);
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+vfs_free_netcred(rn, w)
+ struct radix_node *rn;
+ caddr_t w;
+{
+ register struct radix_node_head *rnh = (struct radix_node_head *)w;
+
+ (*rnh->rnh_deladdr)(rn->rn_key, rn->rn_mask, rnh);
+ free((caddr_t)rn, M_NETADDR);
+ return (0);
+}
+
+/*
+ * Free the net address hash lists that are hanging off the mount points.
+ */
+static void
+vfs_free_addrlist(nep)
+ struct netexport *nep;
+{
+ register int i;
+ register struct radix_node_head *rnh;
+
+ for (i = 0; i <= AF_MAX; i++)
+ if (rnh = nep->ne_rtable[i]) {
+ (*rnh->rnh_walktree)(rnh, vfs_free_netcred,
+ (caddr_t)rnh);
+ free((caddr_t)rnh, M_RTABLE);
+ nep->ne_rtable[i] = 0;
+ }
+}
+
+int
+vfs_export(mp, nep, argp)
+ struct mount *mp;
+ struct netexport *nep;
+ struct export_args *argp;
+{
+ int error;
+
+ if (argp->ex_flags & MNT_DELEXPORT) {
+ vfs_free_addrlist(nep);
+ mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED);
+ }
+ if (argp->ex_flags & MNT_EXPORTED) {
+ if (error = vfs_hang_addrlist(mp, nep, argp))
+ return (error);
+ mp->mnt_flag |= MNT_EXPORTED;
+ }
+ return (0);
+}
+
+struct netcred *
+vfs_export_lookup(mp, nep, nam)
+ register struct mount *mp;
+ struct netexport *nep;
+ struct mbuf *nam;
+{
+ register struct netcred *np;
+ register struct radix_node_head *rnh;
+ struct sockaddr *saddr;
+
+ np = NULL;
+ if (mp->mnt_flag & MNT_EXPORTED) {
+ /*
+ * Lookup in the export list first.
+ */
+ if (nam != NULL) {
+ saddr = mtod(nam, struct sockaddr *);
+ rnh = nep->ne_rtable[saddr->sa_family];
+ if (rnh != NULL) {
+ np = (struct netcred *)
+ (*rnh->rnh_matchaddr)((caddr_t)saddr,
+ rnh);
+ if (np && np->netc_rnodes->rn_flags & RNF_ROOT)
+ np = NULL;
+ }
+ }
+ /*
+ * If no address match, use the default if it exists.
+ */
+ if (np == NULL && mp->mnt_flag & MNT_DEFEXPORTED)
+ np = &nep->ne_defexported;
+ }
+ return (np);
+}
diff --git a/sys/kern/vfs_extattr.c b/sys/kern/vfs_extattr.c
new file mode 100644
index 0000000..f5c3d78
--- /dev/null
+++ b/sys/kern/vfs_extattr.c
@@ -0,0 +1,2155 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/filedesc.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+
+#include <vm/vm.h>
+#include <sys/sysctl.h>
+
+void cvtstat __P((struct stat *, struct ostat *));
+static int change_dir __P((struct nameidata *ndp, struct proc *p));
+
+/*
+ * Virtual File System System Calls
+ */
+
+/*
+ * Mount a file system.
+ */
+struct mount_args {
+ int type;
+ char *path;
+ int flags;
+ caddr_t data;
+};
+/* ARGSUSED */
+int
+mount(p, uap, retval)
+ struct proc *p;
+ register struct mount_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ register struct mount *mp;
+ int error, flag = 0;
+ struct nameidata nd;
+
+ /*
+ * Must be super user
+ */
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ /*
+ * Get vnode to be covered
+ */
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (uap->flags & MNT_UPDATE) {
+ if ((vp->v_flag & VROOT) == 0) {
+ vput(vp);
+ return (EINVAL);
+ }
+ mp = vp->v_mount;
+ flag = mp->mnt_flag;
+ /*
+ * We only allow the filesystem to be reloaded if it
+ * is currently mounted read-only.
+ */
+ if ((uap->flags & MNT_RELOAD) &&
+ ((mp->mnt_flag & MNT_RDONLY) == 0)) {
+ vput(vp);
+ return (EOPNOTSUPP); /* Needs translation */
+ }
+ mp->mnt_flag |=
+ uap->flags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE);
+ VOP_UNLOCK(vp);
+ goto update;
+ }
+ if (error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0))
+ return (error);
+ if (vp->v_type != VDIR) {
+ vput(vp);
+ return (ENOTDIR);
+ }
+ if ((u_long)uap->type > MOUNT_MAXTYPE || vfssw[uap->type] == NULL) {
+ vput(vp);
+ return (ENODEV);
+ }
+
+ /*
+ * Allocate and initialize the file system.
+ */
+ mp = (struct mount *)malloc((u_long)sizeof(struct mount),
+ M_MOUNT, M_WAITOK);
+ bzero((char *)mp, (u_long)sizeof(struct mount));
+ mp->mnt_op = vfssw[uap->type];
+ if (error = vfs_lock(mp)) {
+ free((caddr_t)mp, M_MOUNT);
+ vput(vp);
+ return (error);
+ }
+ if (vp->v_mountedhere != NULL) {
+ vfs_unlock(mp);
+ free((caddr_t)mp, M_MOUNT);
+ vput(vp);
+ return (EBUSY);
+ }
+ vp->v_mountedhere = mp;
+ mp->mnt_vnodecovered = vp;
+update:
+ /*
+ * Set the mount level flags.
+ */
+ if (uap->flags & MNT_RDONLY)
+ mp->mnt_flag |= MNT_RDONLY;
+ else if (mp->mnt_flag & MNT_RDONLY)
+ mp->mnt_flag |= MNT_WANTRDWR;
+ mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV |
+ MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC);
+ mp->mnt_flag |= uap->flags & (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV |
+ MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC);
+ /*
+ * Mount the filesystem.
+ */
+ error = VFS_MOUNT(mp, uap->path, uap->data, &nd, p);
+ if (mp->mnt_flag & MNT_UPDATE) {
+ vrele(vp);
+ if (mp->mnt_flag & MNT_WANTRDWR)
+ mp->mnt_flag &= ~MNT_RDONLY;
+ mp->mnt_flag &=~
+ (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_WANTRDWR);
+ if (error)
+ mp->mnt_flag = flag;
+ return (error);
+ }
+ /*
+ * Put the new filesystem on the mount list after root.
+ */
+ cache_purge(vp);
+ if (!error) {
+ TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
+ VOP_UNLOCK(vp);
+ vfs_unlock(mp);
+ error = VFS_START(mp, 0, p);
+ } else {
+ mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0;
+ vfs_unlock(mp);
+ free((caddr_t)mp, M_MOUNT);
+ vput(vp);
+ }
+ return (error);
+}
+
+/*
+ * Unmount a file system.
+ *
+ * Note: unmount takes a path to the vnode mounted on as argument,
+ * not special file (as before).
+ */
+struct unmount_args {
+ char *path;
+ int flags;
+};
+/* ARGSUSED */
+int
+unmount(p, uap, retval)
+ struct proc *p;
+ register struct unmount_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct mount *mp;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+
+ /*
+ * Unless this is a user mount, then must
+ * have suser privilege.
+ */
+ if (((vp->v_mount->mnt_flag & MNT_USER) == 0) &&
+ (error = suser(p->p_ucred, &p->p_acflag))) {
+ vput(vp);
+ return (error);
+ }
+
+ /*
+ * Must be the root of the filesystem
+ */
+ if ((vp->v_flag & VROOT) == 0) {
+ vput(vp);
+ return (EINVAL);
+ }
+ mp = vp->v_mount;
+ vput(vp);
+ return (dounmount(mp, uap->flags, p));
+}
+
+/*
+ * Do the actual file system unmount.
+ */
+int
+dounmount(mp, flags, p)
+ register struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ struct vnode *coveredvp;
+ int error;
+
+ coveredvp = mp->mnt_vnodecovered;
+ if (vfs_busy(mp))
+ return (EBUSY);
+ mp->mnt_flag |= MNT_UNMOUNT;
+ if (error = vfs_lock(mp))
+ return (error);
+
+ mp->mnt_flag &=~ MNT_ASYNC;
+ vnode_pager_umount(mp); /* release cached vnodes */
+ cache_purgevfs(mp); /* remove cache entries for this file sys */
+ if ((error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0 ||
+ (flags & MNT_FORCE))
+ error = VFS_UNMOUNT(mp, flags, p);
+ mp->mnt_flag &= ~MNT_UNMOUNT;
+ vfs_unbusy(mp);
+ if (error) {
+ vfs_unlock(mp);
+ } else {
+ vrele(coveredvp);
+ TAILQ_REMOVE(&mountlist, mp, mnt_list);
+ mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0;
+ vfs_unlock(mp);
+ if (mp->mnt_vnodelist.lh_first != NULL)
+ panic("unmount: dangling vnode");
+ free((caddr_t)mp, M_MOUNT);
+ }
+ return (error);
+}
+
+/*
+ * Sync each mounted filesystem.
+ */
+#ifdef DIAGNOSTIC
+int syncprt = 0;
+struct ctldebug debug0 = { "syncprt", &syncprt };
+#endif
+
+struct sync_args {
+ int dummy;
+};
+/* ARGSUSED */
+int
+sync(p, uap, retval)
+ struct proc *p;
+ struct sync_args *uap;
+ int *retval;
+{
+ register struct mount *mp, *nmp;
+ int asyncflag;
+
+ for (mp = mountlist.tqh_first; mp != NULL; mp = nmp) {
+ nmp = mp->mnt_list.tqe_next;
+ /*
+ * The lock check below is to avoid races with mount
+ * and unmount.
+ */
+ if ((mp->mnt_flag & (MNT_MLOCK|MNT_RDONLY|MNT_MPBUSY)) == 0 &&
+ !vfs_busy(mp)) {
+ asyncflag = mp->mnt_flag & MNT_ASYNC;
+ mp->mnt_flag &= ~MNT_ASYNC;
+ VFS_SYNC(mp, MNT_NOWAIT, p->p_ucred, p);
+ if (asyncflag)
+ mp->mnt_flag |= MNT_ASYNC;
+ vfs_unbusy(mp);
+ }
+ }
+#ifdef DIAGNOSTIC
+ if (syncprt)
+ vfs_bufstats();
+#endif /* DIAGNOSTIC */
+ return (0);
+}
+
+/*
+ * Change filesystem quotas.
+ */
+struct quotactl_args {
+ char *path;
+ int cmd;
+ int uid;
+ caddr_t arg;
+};
+/* ARGSUSED */
+int
+quotactl(p, uap, retval)
+ struct proc *p;
+ register struct quotactl_args *uap;
+ int *retval;
+{
+ register struct mount *mp;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ mp = nd.ni_vp->v_mount;
+ vrele(nd.ni_vp);
+ return (VFS_QUOTACTL(mp, uap->cmd, uap->uid, uap->arg, p));
+}
+
+/*
+ * Get filesystem statistics.
+ */
+struct statfs_args {
+ char *path;
+ struct statfs *buf;
+};
+/* ARGSUSED */
+int
+statfs(p, uap, retval)
+ struct proc *p;
+ register struct statfs_args *uap;
+ int *retval;
+{
+ register struct mount *mp;
+ register struct statfs *sp;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ mp = nd.ni_vp->v_mount;
+ sp = &mp->mnt_stat;
+ vrele(nd.ni_vp);
+ if (error = VFS_STATFS(mp, sp, p))
+ return (error);
+ sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
+ return (copyout((caddr_t)sp, (caddr_t)uap->buf, sizeof(*sp)));
+}
+
+/*
+ * Get filesystem statistics.
+ */
+struct fstatfs_args {
+ int fd;
+ struct statfs *buf;
+};
+/* ARGSUSED */
+int
+fstatfs(p, uap, retval)
+ struct proc *p;
+ register struct fstatfs_args *uap;
+ int *retval;
+{
+ struct file *fp;
+ struct mount *mp;
+ register struct statfs *sp;
+ int error;
+
+ if (error = getvnode(p->p_fd, uap->fd, &fp))
+ return (error);
+ mp = ((struct vnode *)fp->f_data)->v_mount;
+ sp = &mp->mnt_stat;
+ if (error = VFS_STATFS(mp, sp, p))
+ return (error);
+ sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
+ return (copyout((caddr_t)sp, (caddr_t)uap->buf, sizeof(*sp)));
+}
+
+/*
+ * Get statistics on all filesystems.
+ */
+struct getfsstat_args {
+ struct statfs *buf;
+ long bufsize;
+ int flags;
+};
+int
+getfsstat(p, uap, retval)
+ struct proc *p;
+ register struct getfsstat_args *uap;
+ int *retval;
+{
+ register struct mount *mp, *nmp;
+ register struct statfs *sp;
+ caddr_t sfsp;
+ long count, maxcount, error;
+
+ maxcount = uap->bufsize / sizeof(struct statfs);
+ sfsp = (caddr_t)uap->buf;
+ for (count = 0, mp = mountlist.tqh_first; mp != NULL; mp = nmp) {
+ nmp = mp->mnt_list.tqe_next;
+ if (sfsp && count < maxcount &&
+ ((mp->mnt_flag & MNT_MLOCK) == 0)) {
+ sp = &mp->mnt_stat;
+ /*
+ * If MNT_NOWAIT is specified, do not refresh the
+ * fsstat cache. MNT_WAIT overrides MNT_NOWAIT.
+ */
+ if (((uap->flags & MNT_NOWAIT) == 0 ||
+ (uap->flags & MNT_WAIT)) &&
+ (error = VFS_STATFS(mp, sp, p)))
+ continue;
+ sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
+ if (error = copyout((caddr_t)sp, sfsp, sizeof(*sp)))
+ return (error);
+ sfsp += sizeof(*sp);
+ }
+ count++;
+ }
+ if (sfsp && count > maxcount)
+ *retval = maxcount;
+ else
+ *retval = count;
+ return (0);
+}
+
+/*
+ * Change current working directory to a given file descriptor.
+ */
+struct fchdir_args {
+ int fd;
+};
+/* ARGSUSED */
+int
+fchdir(p, uap, retval)
+ struct proc *p;
+ struct fchdir_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ register struct vnode *vp;
+ struct file *fp;
+ int error;
+
+ if (error = getvnode(fdp, uap->fd, &fp))
+ return (error);
+ vp = (struct vnode *)fp->f_data;
+ VOP_LOCK(vp);
+ if (vp->v_type != VDIR)
+ error = ENOTDIR;
+ else
+ error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p);
+ VOP_UNLOCK(vp);
+ if (error)
+ return (error);
+ VREF(vp);
+ vrele(fdp->fd_cdir);
+ fdp->fd_cdir = vp;
+ return (0);
+}
+
+/*
+ * Change current working directory (``.'').
+ */
+struct chdir_args {
+ char *path;
+};
+/* ARGSUSED */
+int
+chdir(p, uap, retval)
+ struct proc *p;
+ struct chdir_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = change_dir(&nd, p))
+ return (error);
+ vrele(fdp->fd_cdir);
+ fdp->fd_cdir = nd.ni_vp;
+ return (0);
+}
+
+/*
+ * Change notion of root (``/'') directory.
+ */
+struct chroot_args {
+ char *path;
+};
+/* ARGSUSED */
+int
+chroot(p, uap, retval)
+ struct proc *p;
+ struct chroot_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ int error;
+ struct nameidata nd;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = change_dir(&nd, p))
+ return (error);
+ if (fdp->fd_rdir != NULL)
+ vrele(fdp->fd_rdir);
+ fdp->fd_rdir = nd.ni_vp;
+ return (0);
+}
+
+/*
+ * Common routine for chroot and chdir.
+ */
+static int
+change_dir(ndp, p)
+ register struct nameidata *ndp;
+ struct proc *p;
+{
+ struct vnode *vp;
+ int error;
+
+ if (error = namei(ndp))
+ return (error);
+ vp = ndp->ni_vp;
+ if (vp->v_type != VDIR)
+ error = ENOTDIR;
+ else
+ error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p);
+ VOP_UNLOCK(vp);
+ if (error)
+ vrele(vp);
+ return (error);
+}
+
+/*
+ * Check permissions, allocate an open file structure,
+ * and call the device open routine if any.
+ */
+struct open_args {
+ char *path;
+ int flags;
+ int mode;
+};
+int
+open(p, uap, retval)
+ struct proc *p;
+ register struct open_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ register struct file *fp;
+ register struct vnode *vp;
+ int flags, cmode;
+ struct file *nfp;
+ int type, indx, error;
+ struct flock lf;
+ struct nameidata nd;
+ extern struct fileops vnops;
+
+ if (error = falloc(p, &nfp, &indx))
+ return (error);
+ fp = nfp;
+ flags = FFLAGS(uap->flags);
+ cmode = ((uap->mode &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT;
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ p->p_dupfd = -indx - 1; /* XXX check for fdopen */
+ if (error = vn_open(&nd, flags, cmode)) {
+ ffree(fp);
+ if ((error == ENODEV || error == ENXIO) &&
+ p->p_dupfd >= 0 && /* XXX from fdopen */
+ (error =
+ dupfdopen(fdp, indx, p->p_dupfd, flags, error)) == 0) {
+ *retval = indx;
+ return (0);
+ }
+ if (error == ERESTART)
+ error = EINTR;
+ fdp->fd_ofiles[indx] = NULL;
+ return (error);
+ }
+ p->p_dupfd = 0;
+ vp = nd.ni_vp;
+ fp->f_flag = flags & FMASK;
+ fp->f_type = DTYPE_VNODE;
+ fp->f_ops = &vnops;
+ fp->f_data = (caddr_t)vp;
+ if (flags & (O_EXLOCK | O_SHLOCK)) {
+ lf.l_whence = SEEK_SET;
+ lf.l_start = 0;
+ lf.l_len = 0;
+ if (flags & O_EXLOCK)
+ lf.l_type = F_WRLCK;
+ else
+ lf.l_type = F_RDLCK;
+ type = F_FLOCK;
+ if ((flags & FNONBLOCK) == 0)
+ type |= F_WAIT;
+ VOP_UNLOCK(vp);
+ if (error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) {
+ (void) vn_close(vp, fp->f_flag, fp->f_cred, p);
+ ffree(fp);
+ fdp->fd_ofiles[indx] = NULL;
+ return (error);
+ }
+ VOP_LOCK(vp);
+ fp->f_flag |= FHASLOCK;
+ }
+ VOP_UNLOCK(vp);
+ *retval = indx;
+ return (0);
+}
+
+#ifdef COMPAT_43
+/*
+ * Create a file.
+ */
+struct ocreat_args {
+ char *path;
+ int mode;
+};
+int
+ocreat(p, uap, retval)
+ struct proc *p;
+ register struct ocreat_args *uap;
+ int *retval;
+{
+ struct open_args openuap;
+
+ openuap.path = uap->path;
+ openuap.mode = uap->mode;
+ openuap.flags = O_WRONLY | O_CREAT | O_TRUNC;
+ return (open(p, &openuap, retval));
+}
+#endif /* COMPAT_43 */
+
+/*
+ * Create a special file.
+ */
+struct mknod_args {
+ char *path;
+ int mode;
+ int dev;
+};
+/* ARGSUSED */
+int
+mknod(p, uap, retval)
+ struct proc *p;
+ register struct mknod_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (vp != NULL)
+ error = EEXIST;
+ else {
+ VATTR_NULL(&vattr);
+ vattr.va_mode = (uap->mode & ALLPERMS) &~ p->p_fd->fd_cmask;
+ vattr.va_rdev = uap->dev;
+
+ switch (uap->mode & S_IFMT) {
+ case S_IFMT: /* used by badsect to flag bad sectors */
+ vattr.va_type = VBAD;
+ break;
+ case S_IFCHR:
+ vattr.va_type = VCHR;
+ break;
+ case S_IFBLK:
+ vattr.va_type = VBLK;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ }
+ if (!error) {
+ LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ if (vp)
+ vrele(vp);
+ }
+ return (error);
+}
+
+/*
+ * Create named pipe.
+ */
+struct mkfifo_args {
+ char *path;
+ int mode;
+};
+/* ARGSUSED */
+int
+mkfifo(p, uap, retval)
+ struct proc *p;
+ register struct mkfifo_args *uap;
+ int *retval;
+{
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+#ifndef FIFO
+ return (EOPNOTSUPP);
+#else
+ NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ if (nd.ni_vp != NULL) {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vrele(nd.ni_vp);
+ return (EEXIST);
+ }
+ VATTR_NULL(&vattr);
+ vattr.va_type = VFIFO;
+ vattr.va_mode = (uap->mode & ALLPERMS) &~ p->p_fd->fd_cmask;
+ LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ return (VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr));
+#endif /* FIFO */
+}
+
+/*
+ * Make a hard file link.
+ */
+struct link_args {
+ char *path;
+ char *link;
+};
+/* ARGSUSED */
+int
+link(p, uap, retval)
+ struct proc *p;
+ register struct link_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct nameidata nd;
+ int error;
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (vp->v_type != VDIR ||
+ (error = suser(p->p_ucred, &p->p_acflag)) == 0) {
+ nd.ni_cnd.cn_nameiop = CREATE;
+ nd.ni_cnd.cn_flags = LOCKPARENT;
+ nd.ni_dirp = uap->link;
+ if ((error = namei(&nd)) == 0) {
+ if (nd.ni_vp != NULL)
+ error = EEXIST;
+ if (!error) {
+ LEASE_CHECK(nd.ni_dvp,
+ p, p->p_ucred, LEASE_WRITE);
+ LEASE_CHECK(vp,
+ p, p->p_ucred, LEASE_WRITE);
+ error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ if (nd.ni_vp)
+ vrele(nd.ni_vp);
+ }
+ }
+ }
+ vrele(vp);
+ return (error);
+}
+
+/*
+ * Make a symbolic link.
+ */
+struct symlink_args {
+ char *path;
+ char *link;
+};
+/* ARGSUSED */
+int
+symlink(p, uap, retval)
+ struct proc *p;
+ register struct symlink_args *uap;
+ int *retval;
+{
+ struct vattr vattr;
+ char *path;
+ int error;
+ struct nameidata nd;
+
+ MALLOC(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
+ if (error = copyinstr(uap->path, path, MAXPATHLEN, NULL))
+ goto out;
+ NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->link, p);
+ if (error = namei(&nd))
+ goto out;
+ if (nd.ni_vp) {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vrele(nd.ni_vp);
+ error = EEXIST;
+ goto out;
+ }
+ VATTR_NULL(&vattr);
+ vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask;
+ LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path);
+out:
+ FREE(path, M_NAMEI);
+ return (error);
+}
+
+/*
+ * Delete a name from the filesystem.
+ */
+struct unlink_args {
+ char *path;
+};
+/* ARGSUSED */
+int
+unlink(p, uap, retval)
+ struct proc *p;
+ struct unlink_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, DELETE, LOCKPARENT, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+
+ if (vp->v_type != VDIR ||
+ (error = suser(p->p_ucred, &p->p_acflag)) == 0) {
+ /*
+ * The root of a mounted filesystem cannot be deleted.
+ */
+ if (vp->v_flag & VROOT)
+ error = EBUSY;
+ else
+ (void)vnode_pager_uncache(vp);
+ }
+
+ if (!error) {
+ LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vput(vp);
+ }
+ return (error);
+}
+
+/*
+ * Reposition read/write file offset.
+ */
+struct lseek_args {
+ int fd;
+ int pad;
+ off_t offset;
+ int whence;
+};
+int
+lseek(p, uap, retval)
+ struct proc *p;
+ register struct lseek_args *uap;
+ int *retval;
+{
+ struct ucred *cred = p->p_ucred;
+ register struct filedesc *fdp = p->p_fd;
+ register struct file *fp;
+ struct vattr vattr;
+ int error;
+
+ if ((u_int)uap->fd >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL)
+ return (EBADF);
+ if (fp->f_type != DTYPE_VNODE)
+ return (ESPIPE);
+ switch (uap->whence) {
+ case L_INCR:
+ fp->f_offset += uap->offset;
+ break;
+ case L_XTND:
+ if (error =
+ VOP_GETATTR((struct vnode *)fp->f_data, &vattr, cred, p))
+ return (error);
+ fp->f_offset = uap->offset + vattr.va_size;
+ break;
+ case L_SET:
+ fp->f_offset = uap->offset;
+ break;
+ default:
+ return (EINVAL);
+ }
+ *(off_t *)retval = fp->f_offset;
+ return (0);
+}
+
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+/*
+ * Reposition read/write file offset.
+ */
+struct olseek_args {
+ int fd;
+ long offset;
+ int whence;
+};
+int
+olseek(p, uap, retval)
+ struct proc *p;
+ register struct olseek_args *uap;
+ int *retval;
+{
+ struct lseek_args nuap;
+ off_t qret;
+ int error;
+
+ nuap.fd = uap->fd;
+ nuap.offset = uap->offset;
+ nuap.whence = uap->whence;
+ error = lseek(p, &nuap, &qret);
+ *(long *)retval = qret;
+ return (error);
+}
+#endif /* COMPAT_43 */
+
+/*
+ * Check access permissions.
+ */
+struct access_args {
+ char *path;
+ int flags;
+};
+int
+access(p, uap, retval)
+ struct proc *p;
+ register struct access_args *uap;
+ int *retval;
+{
+ register struct ucred *cred = p->p_ucred;
+ register struct vnode *vp;
+ int error, flags, t_gid, t_uid;
+ struct nameidata nd;
+
+ t_uid = cred->cr_uid;
+ t_gid = cred->cr_groups[0];
+ cred->cr_uid = p->p_cred->p_ruid;
+ cred->cr_groups[0] = p->p_cred->p_rgid;
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ goto out1;
+ vp = nd.ni_vp;
+
+ /* Flags == 0 means only check for existence. */
+ if (uap->flags) {
+ flags = 0;
+ if (uap->flags & R_OK)
+ flags |= VREAD;
+ if (uap->flags & W_OK)
+ flags |= VWRITE;
+ if (uap->flags & X_OK)
+ flags |= VEXEC;
+ if ((flags & VWRITE) == 0 || (error = vn_writechk(vp)) == 0)
+ error = VOP_ACCESS(vp, flags, cred, p);
+ }
+ vput(vp);
+out1:
+ cred->cr_uid = t_uid;
+ cred->cr_groups[0] = t_gid;
+ return (error);
+}
+
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+/*
+ * Get file status; this version follows links.
+ */
+struct ostat_args {
+ char *path;
+ struct ostat *ub;
+};
+/* ARGSUSED */
+int
+ostat(p, uap, retval)
+ struct proc *p;
+ register struct ostat_args *uap;
+ int *retval;
+{
+ struct stat sb;
+ struct ostat osb;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ error = vn_stat(nd.ni_vp, &sb, p);
+ vput(nd.ni_vp);
+ if (error)
+ return (error);
+ cvtstat(&sb, &osb);
+ error = copyout((caddr_t)&osb, (caddr_t)uap->ub, sizeof (osb));
+ return (error);
+}
+
+/*
+ * Get file status; this version does not follow links.
+ */
+struct olstat_args {
+ char *path;
+ struct ostat *ub;
+};
+/* ARGSUSED */
+int
+olstat(p, uap, retval)
+ struct proc *p;
+ register struct olstat_args *uap;
+ int *retval;
+{
+ struct stat sb;
+ struct ostat osb;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ error = vn_stat(nd.ni_vp, &sb, p);
+ vput(nd.ni_vp);
+ if (error)
+ return (error);
+ cvtstat(&sb, &osb);
+ error = copyout((caddr_t)&osb, (caddr_t)uap->ub, sizeof (osb));
+ return (error);
+}
+
+/*
+ * Convert from an old to a new stat structure.
+ */
+void
+cvtstat(st, ost)
+ struct stat *st;
+ struct ostat *ost;
+{
+
+ ost->st_dev = st->st_dev;
+ ost->st_ino = st->st_ino;
+ ost->st_mode = st->st_mode;
+ ost->st_nlink = st->st_nlink;
+ ost->st_uid = st->st_uid;
+ ost->st_gid = st->st_gid;
+ ost->st_rdev = st->st_rdev;
+ if (st->st_size < (quad_t)1 << 32)
+ ost->st_size = st->st_size;
+ else
+ ost->st_size = -2;
+ ost->st_atime = st->st_atime;
+ ost->st_mtime = st->st_mtime;
+ ost->st_ctime = st->st_ctime;
+ ost->st_blksize = st->st_blksize;
+ ost->st_blocks = st->st_blocks;
+ ost->st_flags = st->st_flags;
+ ost->st_gen = st->st_gen;
+}
+#endif /* COMPAT_43 || COMPAT_SUNOS */
+
+/*
+ * Get file status; this version follows links.
+ */
+struct stat_args {
+ char *path;
+ struct stat *ub;
+};
+/* ARGSUSED */
+int
+stat(p, uap, retval)
+ struct proc *p;
+ register struct stat_args *uap;
+ int *retval;
+{
+ struct stat sb;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ error = vn_stat(nd.ni_vp, &sb, p);
+ vput(nd.ni_vp);
+ if (error)
+ return (error);
+ error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb));
+ return (error);
+}
+
+/*
+ * Get file status; this version does not follow links.
+ */
+struct lstat_args {
+ char *path;
+ struct stat *ub;
+};
+/* ARGSUSED */
+int
+lstat(p, uap, retval)
+ struct proc *p;
+ register struct lstat_args *uap;
+ int *retval;
+{
+ int error;
+ struct vnode *vp, *dvp;
+ struct stat sb, sb1;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | LOCKPARENT, UIO_USERSPACE,
+ uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ /*
+ * For symbolic links, always return the attributes of its
+ * containing directory, except for mode, size, and links.
+ */
+ vp = nd.ni_vp;
+ dvp = nd.ni_dvp;
+ if (vp->v_type != VLNK) {
+ if (dvp == vp)
+ vrele(dvp);
+ else
+ vput(dvp);
+ error = vn_stat(vp, &sb, p);
+ vput(vp);
+ if (error)
+ return (error);
+ } else {
+ error = vn_stat(dvp, &sb, p);
+ vput(dvp);
+ if (error) {
+ vput(vp);
+ return (error);
+ }
+ error = vn_stat(vp, &sb1, p);
+ vput(vp);
+ if (error)
+ return (error);
+ sb.st_mode &= ~S_IFDIR;
+ sb.st_mode |= S_IFLNK;
+ sb.st_nlink = sb1.st_nlink;
+ sb.st_size = sb1.st_size;
+ sb.st_blocks = sb1.st_blocks;
+ }
+ error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb));
+ return (error);
+}
+
+/*
+ * Get configurable pathname variables.
+ */
+struct pathconf_args {
+ char *path;
+ int name;
+};
+/* ARGSUSED */
+int
+pathconf(p, uap, retval)
+ struct proc *p;
+ register struct pathconf_args *uap;
+ int *retval;
+{
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ error = VOP_PATHCONF(nd.ni_vp, uap->name, retval);
+ vput(nd.ni_vp);
+ return (error);
+}
+
+/*
+ * Return target name of a symbolic link.
+ */
+struct readlink_args {
+ char *path;
+ char *buf;
+ int count;
+};
+/* ARGSUSED */
+int
+readlink(p, uap, retval)
+ struct proc *p;
+ register struct readlink_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct iovec aiov;
+ struct uio auio;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (vp->v_type != VLNK)
+ error = EINVAL;
+ else {
+ aiov.iov_base = uap->buf;
+ aiov.iov_len = uap->count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_offset = 0;
+ auio.uio_rw = UIO_READ;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_procp = p;
+ auio.uio_resid = uap->count;
+ error = VOP_READLINK(vp, &auio, p->p_ucred);
+ }
+ vput(vp);
+ *retval = uap->count - auio.uio_resid;
+ return (error);
+}
+
+/*
+ * Change flags of a file given a path name.
+ */
+struct chflags_args {
+ char *path;
+ int flags;
+};
+/* ARGSUSED */
+int
+chflags(p, uap, retval)
+ struct proc *p;
+ register struct chflags_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ error = EROFS;
+ else {
+ VATTR_NULL(&vattr);
+ vattr.va_flags = uap->flags;
+ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+ }
+ vput(vp);
+ return (error);
+}
+
+/*
+ * Change flags of a file given a file descriptor.
+ */
+struct fchflags_args {
+ int fd;
+ int flags;
+};
+/* ARGSUSED */
+int
+fchflags(p, uap, retval)
+ struct proc *p;
+ register struct fchflags_args *uap;
+ int *retval;
+{
+ struct vattr vattr;
+ struct vnode *vp;
+ struct file *fp;
+ int error;
+
+ if (error = getvnode(p->p_fd, uap->fd, &fp))
+ return (error);
+ vp = (struct vnode *)fp->f_data;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ error = EROFS;
+ else {
+ VATTR_NULL(&vattr);
+ vattr.va_flags = uap->flags;
+ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+ }
+ VOP_UNLOCK(vp);
+ return (error);
+}
+
+/*
+ * Change mode of a file given path name.
+ */
+struct chmod_args {
+ char *path;
+ int mode;
+};
+/* ARGSUSED */
+int
+chmod(p, uap, retval)
+ struct proc *p;
+ register struct chmod_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ error = EROFS;
+ else {
+ VATTR_NULL(&vattr);
+ vattr.va_mode = uap->mode & ALLPERMS;
+ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+ }
+ vput(vp);
+ return (error);
+}
+
+/*
+ * Change mode of a file given a file descriptor.
+ */
+struct fchmod_args {
+ int fd;
+ int mode;
+};
+/* ARGSUSED */
+int
+fchmod(p, uap, retval)
+ struct proc *p;
+ register struct fchmod_args *uap;
+ int *retval;
+{
+ struct vattr vattr;
+ struct vnode *vp;
+ struct file *fp;
+ int error;
+
+ if (error = getvnode(p->p_fd, uap->fd, &fp))
+ return (error);
+ vp = (struct vnode *)fp->f_data;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ error = EROFS;
+ else {
+ VATTR_NULL(&vattr);
+ vattr.va_mode = uap->mode & ALLPERMS;
+ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+ }
+ VOP_UNLOCK(vp);
+ return (error);
+}
+
+/*
+ * Set ownership given a path name.
+ */
+struct chown_args {
+ char *path;
+ int uid;
+ int gid;
+};
+/* ARGSUSED */
+int
+chown(p, uap, retval)
+ struct proc *p;
+ register struct chown_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ error = EROFS;
+ else {
+ VATTR_NULL(&vattr);
+ vattr.va_uid = uap->uid;
+ vattr.va_gid = uap->gid;
+ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+ }
+ vput(vp);
+ return (error);
+}
+
+/*
+ * Set ownership given a file descriptor.
+ */
+struct fchown_args {
+ int fd;
+ int uid;
+ int gid;
+};
+/* ARGSUSED */
+int
+fchown(p, uap, retval)
+ struct proc *p;
+ register struct fchown_args *uap;
+ int *retval;
+{
+ struct vattr vattr;
+ struct vnode *vp;
+ struct file *fp;
+ int error;
+
+ if (error = getvnode(p->p_fd, uap->fd, &fp))
+ return (error);
+ vp = (struct vnode *)fp->f_data;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ error = EROFS;
+ else {
+ VATTR_NULL(&vattr);
+ vattr.va_uid = uap->uid;
+ vattr.va_gid = uap->gid;
+ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+ }
+ VOP_UNLOCK(vp);
+ return (error);
+}
+
+/*
+ * Set the access and modification times of a file.
+ */
+struct utimes_args {
+ char *path;
+ struct timeval *tptr;
+};
+/* ARGSUSED */
+int
+utimes(p, uap, retval)
+ struct proc *p;
+ register struct utimes_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct timeval tv[2];
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ VATTR_NULL(&vattr);
+ if (uap->tptr == NULL) {
+ microtime(&tv[0]);
+ tv[1] = tv[0];
+ vattr.va_vaflags |= VA_UTIMES_NULL;
+ } else if (error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv)))
+ return (error);
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ error = EROFS;
+ else {
+ vattr.va_atime.ts_sec = tv[0].tv_sec;
+ vattr.va_atime.ts_nsec = tv[0].tv_usec * 1000;
+ vattr.va_mtime.ts_sec = tv[1].tv_sec;
+ vattr.va_mtime.ts_nsec = tv[1].tv_usec * 1000;
+ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+ }
+ vput(vp);
+ return (error);
+}
+
+/*
+ * Truncate a file given its path name.
+ */
+struct truncate_args {
+ char *path;
+ int pad;
+ off_t length;
+};
+/* ARGSUSED */
+int
+truncate(p, uap, retval)
+ struct proc *p;
+ register struct truncate_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_type == VDIR)
+ error = EISDIR;
+ else if ((error = vn_writechk(vp)) == 0 &&
+ (error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) == 0) {
+ VATTR_NULL(&vattr);
+ vattr.va_size = uap->length;
+ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+ }
+ vput(vp);
+ return (error);
+}
+
+/*
+ * Truncate a file given a file descriptor.
+ */
+struct ftruncate_args {
+ int fd;
+ int pad;
+ off_t length;
+};
+/* ARGSUSED */
+int
+ftruncate(p, uap, retval)
+ struct proc *p;
+ register struct ftruncate_args *uap;
+ int *retval;
+{
+ struct vattr vattr;
+ struct vnode *vp;
+ struct file *fp;
+ int error;
+
+ if (error = getvnode(p->p_fd, uap->fd, &fp))
+ return (error);
+ if ((fp->f_flag & FWRITE) == 0)
+ return (EINVAL);
+ vp = (struct vnode *)fp->f_data;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_type == VDIR)
+ error = EISDIR;
+ else if ((error = vn_writechk(vp)) == 0) {
+ VATTR_NULL(&vattr);
+ vattr.va_size = uap->length;
+ error = VOP_SETATTR(vp, &vattr, fp->f_cred, p);
+ }
+ VOP_UNLOCK(vp);
+ return (error);
+}
+
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+/*
+ * Truncate a file given its path name.
+ */
+struct otruncate_args {
+ char *path;
+ long length;
+};
+/* ARGSUSED */
+int
+otruncate(p, uap, retval)
+ struct proc *p;
+ register struct otruncate_args *uap;
+ int *retval;
+{
+ struct truncate_args nuap;
+
+ nuap.path = uap->path;
+ nuap.length = uap->length;
+ return (truncate(p, &nuap, retval));
+}
+
+/*
+ * Truncate a file given a file descriptor.
+ */
+struct oftruncate_args {
+ int fd;
+ long length;
+};
+/* ARGSUSED */
+int
+oftruncate(p, uap, retval)
+ struct proc *p;
+ register struct oftruncate_args *uap;
+ int *retval;
+{
+ struct ftruncate_args nuap;
+
+ nuap.fd = uap->fd;
+ nuap.length = uap->length;
+ return (ftruncate(p, &nuap, retval));
+}
+#endif /* COMPAT_43 || COMPAT_SUNOS */
+
+/*
+ * Sync an open file.
+ */
+struct fsync_args {
+ int fd;
+};
+/* ARGSUSED */
+int
+fsync(p, uap, retval)
+ struct proc *p;
+ struct fsync_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct file *fp;
+ int error;
+
+ if (error = getvnode(p->p_fd, uap->fd, &fp))
+ return (error);
+ vp = (struct vnode *)fp->f_data;
+ VOP_LOCK(vp);
+ error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, p);
+ VOP_UNLOCK(vp);
+ return (error);
+}
+
+/*
+ * Rename files. Source and destination must either both be directories,
+ * or both not be directories. If target is a directory, it must be empty.
+ */
+struct rename_args {
+ char *from;
+ char *to;
+};
+/* ARGSUSED */
+int
+rename(p, uap, retval)
+ struct proc *p;
+ register struct rename_args *uap;
+ int *retval;
+{
+ register struct vnode *tvp, *fvp, *tdvp;
+ struct nameidata fromnd, tond;
+ int error;
+
+ NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, UIO_USERSPACE,
+ uap->from, p);
+ if (error = namei(&fromnd))
+ return (error);
+ fvp = fromnd.ni_vp;
+ NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART,
+ UIO_USERSPACE, uap->to, p);
+ if (error = namei(&tond)) {
+ VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
+ vrele(fromnd.ni_dvp);
+ vrele(fvp);
+ goto out1;
+ }
+ tdvp = tond.ni_dvp;
+ tvp = tond.ni_vp;
+ if (tvp != NULL) {
+ if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto out;
+ } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) {
+ error = EISDIR;
+ goto out;
+ }
+ }
+ if (fvp == tdvp)
+ error = EINVAL;
+ /*
+ * If source is the same as the destination (that is the
+ * same inode number with the same name in the same directory),
+ * then there is nothing to do.
+ */
+ if (fvp == tvp && fromnd.ni_dvp == tdvp &&
+ fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen &&
+ !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr,
+ fromnd.ni_cnd.cn_namelen))
+ error = -1;
+out:
+ if (!error) {
+ LEASE_CHECK(tdvp, p, p->p_ucred, LEASE_WRITE);
+ if (fromnd.ni_dvp != tdvp)
+ LEASE_CHECK(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ if (tvp)
+ LEASE_CHECK(tvp, p, p->p_ucred, LEASE_WRITE);
+ error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd,
+ tond.ni_dvp, tond.ni_vp, &tond.ni_cnd);
+ } else {
+ VOP_ABORTOP(tond.ni_dvp, &tond.ni_cnd);
+ if (tdvp == tvp)
+ vrele(tdvp);
+ else
+ vput(tdvp);
+ if (tvp)
+ vput(tvp);
+ VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
+ vrele(fromnd.ni_dvp);
+ vrele(fvp);
+ }
+ vrele(tond.ni_startdir);
+ FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI);
+out1:
+ if (fromnd.ni_startdir)
+ vrele(fromnd.ni_startdir);
+ FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI);
+ if (error == -1)
+ return (0);
+ return (error);
+}
+
+/*
+ * Make a directory file.
+ */
+struct mkdir_args {
+ char *path;
+ int mode;
+};
+/* ARGSUSED */
+int
+mkdir(p, uap, retval)
+ struct proc *p;
+ register struct mkdir_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (vp != NULL) {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vrele(vp);
+ return (EEXIST);
+ }
+ VATTR_NULL(&vattr);
+ vattr.va_type = VDIR;
+ vattr.va_mode = (uap->mode & ACCESSPERMS) &~ p->p_fd->fd_cmask;
+ LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
+ if (!error)
+ vput(nd.ni_vp);
+ return (error);
+}
+
+/*
+ * Remove a directory file.
+ */
+struct rmdir_args {
+ char *path;
+};
+/* ARGSUSED */
+int
+rmdir(p, uap, retval)
+ struct proc *p;
+ struct rmdir_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (vp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto out;
+ }
+ /*
+ * No rmdir "." please.
+ */
+ if (nd.ni_dvp == vp) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * The root of a mounted filesystem cannot be deleted.
+ */
+ if (vp->v_flag & VROOT)
+ error = EBUSY;
+out:
+ if (!error) {
+ LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vput(vp);
+ }
+ return (error);
+}
+
+#ifdef COMPAT_43
+/*
+ * Read a block of directory entries in a file system independent format.
+ */
+struct ogetdirentries_args {
+ int fd;
+ char *buf;
+ u_int count;
+ long *basep;
+};
+int
+ogetdirentries(p, uap, retval)
+ struct proc *p;
+ register struct ogetdirentries_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct file *fp;
+ struct uio auio, kuio;
+ struct iovec aiov, kiov;
+ struct dirent *dp, *edp;
+ caddr_t dirbuf;
+ int error, readcnt;
+ long loff;
+
+ if (error = getvnode(p->p_fd, uap->fd, &fp))
+ return (error);
+ if ((fp->f_flag & FREAD) == 0)
+ return (EBADF);
+ vp = (struct vnode *)fp->f_data;
+ if (vp->v_type != VDIR)
+ return (EINVAL);
+ aiov.iov_base = uap->buf;
+ aiov.iov_len = uap->count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_rw = UIO_READ;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_procp = p;
+ auio.uio_resid = uap->count;
+ VOP_LOCK(vp);
+ loff = auio.uio_offset = fp->f_offset;
+# if (BYTE_ORDER != LITTLE_ENDIAN)
+ if (vp->v_mount->mnt_maxsymlinklen <= 0) {
+ error = VOP_READDIR(vp, &auio, fp->f_cred);
+ fp->f_offset = auio.uio_offset;
+ } else
+# endif
+ {
+ kuio = auio;
+ kuio.uio_iov = &kiov;
+ kuio.uio_segflg = UIO_SYSSPACE;
+ kiov.iov_len = uap->count;
+ MALLOC(dirbuf, caddr_t, uap->count, M_TEMP, M_WAITOK);
+ kiov.iov_base = dirbuf;
+ error = VOP_READDIR(vp, &kuio, fp->f_cred);
+ fp->f_offset = kuio.uio_offset;
+ if (error == 0) {
+ readcnt = uap->count - kuio.uio_resid;
+ edp = (struct dirent *)&dirbuf[readcnt];
+ for (dp = (struct dirent *)dirbuf; dp < edp; ) {
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ /*
+ * The expected low byte of
+ * dp->d_namlen is our dp->d_type.
+ * The high MBZ byte of dp->d_namlen
+ * is our dp->d_namlen.
+ */
+ dp->d_type = dp->d_namlen;
+ dp->d_namlen = 0;
+# else
+ /*
+ * The dp->d_type is the high byte
+ * of the expected dp->d_namlen,
+ * so must be zero'ed.
+ */
+ dp->d_type = 0;
+# endif
+ if (dp->d_reclen > 0) {
+ dp = (struct dirent *)
+ ((char *)dp + dp->d_reclen);
+ } else {
+ error = EIO;
+ break;
+ }
+ }
+ if (dp >= edp)
+ error = uiomove(dirbuf, readcnt, &auio);
+ }
+ FREE(dirbuf, M_TEMP);
+ }
+ VOP_UNLOCK(vp);
+ if (error)
+ return (error);
+ error = copyout((caddr_t)&loff, (caddr_t)uap->basep, sizeof(long));
+ *retval = uap->count - auio.uio_resid;
+ return (error);
+}
+#endif
+
+/*
+ * Read a block of directory entries in a file system independent format.
+ */
+struct getdirentries_args {
+ int fd;
+ char *buf;
+ u_int count;
+ long *basep;
+};
+int
+getdirentries(p, uap, retval)
+ struct proc *p;
+ register struct getdirentries_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct file *fp;
+ struct uio auio;
+ struct iovec aiov;
+ long loff;
+ int error;
+
+ if (error = getvnode(p->p_fd, uap->fd, &fp))
+ return (error);
+ if ((fp->f_flag & FREAD) == 0)
+ return (EBADF);
+ vp = (struct vnode *)fp->f_data;
+unionread:
+ if (vp->v_type != VDIR)
+ return (EINVAL);
+ aiov.iov_base = uap->buf;
+ aiov.iov_len = uap->count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_rw = UIO_READ;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_procp = p;
+ auio.uio_resid = uap->count;
+ VOP_LOCK(vp);
+ loff = auio.uio_offset = fp->f_offset;
+ error = VOP_READDIR(vp, &auio, fp->f_cred);
+ fp->f_offset = auio.uio_offset;
+ VOP_UNLOCK(vp);
+ if (error)
+ return (error);
+
+#ifdef UNION
+{
+ extern int (**union_vnodeop_p)();
+ extern struct vnode *union_lowervp __P((struct vnode *));
+
+ if ((uap->count == auio.uio_resid) &&
+ (vp->v_op == union_vnodeop_p)) {
+ struct vnode *tvp = vp;
+
+ vp = union_lowervp(vp);
+ if (vp != NULLVP) {
+ VOP_LOCK(vp);
+ error = VOP_OPEN(vp, FREAD);
+ VOP_UNLOCK(vp);
+
+ if (error) {
+ vrele(vp);
+ return (error);
+ }
+ fp->f_data = (caddr_t) vp;
+ fp->f_offset = 0;
+ error = vn_close(tvp, FREAD, fp->f_cred, p);
+ if (error)
+ return (error);
+ goto unionread;
+ }
+ }
+}
+#endif
+
+ if ((uap->count == auio.uio_resid) &&
+ (vp->v_flag & VROOT) &&
+ (vp->v_mount->mnt_flag & MNT_UNION)) {
+ struct vnode *tvp = vp;
+ vp = vp->v_mount->mnt_vnodecovered;
+ VREF(vp);
+ fp->f_data = (caddr_t) vp;
+ fp->f_offset = 0;
+ vrele(tvp);
+ goto unionread;
+ }
+ error = copyout((caddr_t)&loff, (caddr_t)uap->basep, sizeof(long));
+ *retval = uap->count - auio.uio_resid;
+ return (error);
+}
+
+/*
+ * Set the mode mask for creation of filesystem nodes.
+ */
+struct umask_args {
+ int newmask;
+};
+mode_t /* XXX */
+umask(p, uap, retval)
+ struct proc *p;
+ struct umask_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp;
+
+ fdp = p->p_fd;
+ *retval = fdp->fd_cmask;
+ fdp->fd_cmask = uap->newmask & ALLPERMS;
+ return (0);
+}
+
+/*
+ * Void all references to file by ripping underlying filesystem
+ * away from vnode.
+ */
+struct revoke_args {
+ char *path;
+};
+/* ARGSUSED */
+int
+revoke(p, uap, retval)
+ struct proc *p;
+ register struct revoke_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (vp->v_type != VCHR && vp->v_type != VBLK) {
+ error = EINVAL;
+ goto out;
+ }
+ if (error = VOP_GETATTR(vp, &vattr, p->p_ucred, p))
+ goto out;
+ if (p->p_ucred->cr_uid != vattr.va_uid &&
+ (error = suser(p->p_ucred, &p->p_acflag)))
+ goto out;
+ if (vp->v_usecount > 1 || (vp->v_flag & VALIASED))
+ vgoneall(vp);
+out:
+ vrele(vp);
+ return (error);
+}
+
+/*
+ * Convert a user file descriptor to a kernel file entry.
+ */
+int
+getvnode(fdp, fd, fpp)
+ struct filedesc *fdp;
+ struct file **fpp;
+ int fd;
+{
+ struct file *fp;
+
+ if ((u_int)fd >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[fd]) == NULL)
+ return (EBADF);
+ if (fp->f_type != DTYPE_VNODE)
+ return (EINVAL);
+ *fpp = fp;
+ return (0);
+}
diff --git a/sys/kern/vfs_init.c b/sys/kern/vfs_init.c
new file mode 100644
index 0000000..3ab520d
--- /dev/null
+++ b/sys/kern/vfs_init.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed
+ * to Berkeley by John Heidemann of the UCLA Ficus project.
+ *
+ * Source: * @(#)i405_init.c 2.10 92/04/27 UCLA Ficus project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vfs_init.c 8.3 (Berkeley) 1/4/94
+ */
+
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/namei.h>
+#include <sys/ucred.h>
+#include <sys/buf.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+
+/*
+ * Sigh, such primitive tools are these...
+ */
+#if 0
+#define DODEBUG(A) A
+#else
+#define DODEBUG(A)
+#endif
+
+extern struct vnodeopv_desc *vfs_opv_descs[];
+ /* a list of lists of vnodeops defns */
+extern struct vnodeop_desc *vfs_op_descs[];
+ /* and the operations they perform */
+/*
+ * This code doesn't work if the defn is **vnodop_defns with cc.
+ * The problem is because of the compiler sometimes putting in an
+ * extra level of indirection for arrays. It's an interesting
+ * "feature" of C.
+ */
+int vfs_opv_numops;
+
+typedef (*PFI)(); /* the standard Pointer to a Function returning an Int */
+
+/*
+ * A miscellaneous routine.
+ * A generic "default" routine that just returns an error.
+ */
+int
+vn_default_error()
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * vfs_init.c
+ *
+ * Allocate and fill in operations vectors.
+ *
+ * An undocumented feature of this approach to defining operations is that
+ * there can be multiple entries in vfs_opv_descs for the same operations
+ * vector. This allows third parties to extend the set of operations
+ * supported by another layer in a binary compatibile way. For example,
+ * assume that NFS needed to be modified to support Ficus. NFS has an entry
+ * (probably nfs_vnopdeop_decls) declaring all the operations NFS supports by
+ * default. Ficus could add another entry (ficus_nfs_vnodeop_decl_entensions)
+ * listing those new operations Ficus adds to NFS, all without modifying the
+ * NFS code. (Of couse, the OTW NFS protocol still needs to be munged, but
+ * that is a(whole)nother story.) This is a feature.
+ */
+void
+vfs_opv_init()
+{
+ int i, j, k;
+ int (***opv_desc_vector_p)();
+ int (**opv_desc_vector)();
+ struct vnodeopv_entry_desc *opve_descp;
+
+ /*
+ * Allocate the dynamic vectors and fill them in.
+ */
+ for (i=0; vfs_opv_descs[i]; i++) {
+ opv_desc_vector_p = vfs_opv_descs[i]->opv_desc_vector_p;
+ /*
+ * Allocate and init the vector, if it needs it.
+ * Also handle backwards compatibility.
+ */
+ if (*opv_desc_vector_p == NULL) {
+ /* XXX - shouldn't be M_VNODE */
+ MALLOC(*opv_desc_vector_p, PFI*,
+ vfs_opv_numops*sizeof(PFI), M_VNODE, M_WAITOK);
+ bzero (*opv_desc_vector_p, vfs_opv_numops*sizeof(PFI));
+ DODEBUG(printf("vector at %x allocated\n",
+ opv_desc_vector_p));
+ }
+ opv_desc_vector = *opv_desc_vector_p;
+ for (j=0; vfs_opv_descs[i]->opv_desc_ops[j].opve_op; j++) {
+ opve_descp = &(vfs_opv_descs[i]->opv_desc_ops[j]);
+
+ /*
+ * Sanity check: is this operation listed
+ * in the list of operations? We check this
+ * by seeing if its offest is zero. Since
+ * the default routine should always be listed
+ * first, it should be the only one with a zero
+ * offset. Any other operation with a zero
+ * offset is probably not listed in
+ * vfs_op_descs, and so is probably an error.
+ *
+ * A panic here means the layer programmer
+ * has committed the all-too common bug
+ * of adding a new operation to the layer's
+ * list of vnode operations but
+ * not adding the operation to the system-wide
+ * list of supported operations.
+ */
+ if (opve_descp->opve_op->vdesc_offset == 0 &&
+ opve_descp->opve_op->vdesc_offset !=
+ VOFFSET(vop_default)) {
+ printf("operation %s not listed in %s.\n",
+ opve_descp->opve_op->vdesc_name,
+ "vfs_op_descs");
+ panic ("vfs_opv_init: bad operation");
+ }
+ /*
+ * Fill in this entry.
+ */
+ opv_desc_vector[opve_descp->opve_op->vdesc_offset] =
+ opve_descp->opve_impl;
+ }
+ }
+ /*
+ * Finally, go back and replace unfilled routines
+ * with their default. (Sigh, an O(n^3) algorithm. I
+ * could make it better, but that'd be work, and n is small.)
+ */
+ for (i = 0; vfs_opv_descs[i]; i++) {
+ opv_desc_vector = *(vfs_opv_descs[i]->opv_desc_vector_p);
+ /*
+ * Force every operations vector to have a default routine.
+ */
+ if (opv_desc_vector[VOFFSET(vop_default)]==NULL) {
+ panic("vfs_opv_init: operation vector without default routine.");
+ }
+ for (k = 0; k<vfs_opv_numops; k++)
+ if (opv_desc_vector[k] == NULL)
+ opv_desc_vector[k] =
+ opv_desc_vector[VOFFSET(vop_default)];
+ }
+}
+
+/*
+ * Initialize known vnode operations vectors.
+ */
+void
+vfs_op_init()
+{
+ int i;
+
+ DODEBUG(printf("Vnode_interface_init.\n"));
+ /*
+ * Set all vnode vectors to a well known value.
+ */
+ for (i = 0; vfs_opv_descs[i]; i++)
+ *(vfs_opv_descs[i]->opv_desc_vector_p) = NULL;
+ /*
+ * Figure out how many ops there are by counting the table,
+ * and assign each its offset.
+ */
+ for (vfs_opv_numops = 0, i = 0; vfs_op_descs[i]; i++) {
+ vfs_op_descs[i]->vdesc_offset = vfs_opv_numops;
+ vfs_opv_numops++;
+ }
+ DODEBUG(printf ("vfs_opv_numops=%d\n", vfs_opv_numops));
+}
+
+/*
+ * Routines having to do with the management of the vnode table.
+ */
+extern struct vnodeops dead_vnodeops;
+extern struct vnodeops spec_vnodeops;
+extern void vclean();
+struct vattr va_null;
+
+/*
+ * Initialize the vnode structures and initialize each file system type.
+ */
+void
+vfsinit()
+{
+ struct vfsops **vfsp;
+
+ /*
+ * Initialize the vnode table
+ */
+ vntblinit();
+ /*
+ * Initialize the vnode name cache
+ */
+ nchinit();
+ /*
+ * Build vnode operation vectors.
+ */
+ vfs_op_init();
+ vfs_opv_init(); /* finish the job */
+ /*
+ * Initialize each file system type.
+ */
+ vattr_null(&va_null);
+ for (vfsp = &vfssw[0]; vfsp <= &vfssw[MOUNT_MAXTYPE]; vfsp++) {
+ if (*vfsp == NULL)
+ continue;
+ (*(*vfsp)->vfs_init)();
+ }
+}
diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c
new file mode 100644
index 0000000..0fa5aa1
--- /dev/null
+++ b/sys/kern/vfs_lookup.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vfs_lookup.c 8.4 (Berkeley) 2/16/94
+ */
+
+#include <sys/param.h>
+#include <sys/syslimits.h>
+#include <sys/time.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/filedesc.h>
+#include <sys/proc.h>
+
+#ifdef KTRACE
+#include <sys/ktrace.h>
+#endif
+
+/*
+ * Convert a pathname into a pointer to a locked inode.
+ *
+ * The FOLLOW flag is set when symbolic links are to be followed
+ * when they occur at the end of the name translation process.
+ * Symbolic links are always followed for all other pathname
+ * components other than the last.
+ *
+ * The segflg defines whether the name is to be copied from user
+ * space or kernel space.
+ *
+ * Overall outline of namei:
+ *
+ * copy in name
+ * get starting directory
+ * while (!done && !error) {
+ * call lookup to search path.
+ * if symbolic link, massage name in buffer and continue
+ * }
+ */
+int
+namei(ndp)
+ register struct nameidata *ndp;
+{
+ register struct filedesc *fdp; /* pointer to file descriptor state */
+ register char *cp; /* pointer into pathname argument */
+ register struct vnode *dp; /* the directory we are searching */
+ struct iovec aiov; /* uio for reading symbolic links */
+ struct uio auio;
+ int error, linklen;
+ struct componentname *cnp = &ndp->ni_cnd;
+
+ ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred;
+#ifdef DIAGNOSTIC
+ if (!cnp->cn_cred || !cnp->cn_proc)
+ panic ("namei: bad cred/proc");
+ if (cnp->cn_nameiop & (~OPMASK))
+ panic ("namei: nameiop contaminated with flags");
+ if (cnp->cn_flags & OPMASK)
+ panic ("namei: flags contaminated with nameiops");
+#endif
+ fdp = cnp->cn_proc->p_fd;
+
+ /*
+ * Get a buffer for the name to be translated, and copy the
+ * name into the buffer.
+ */
+ if ((cnp->cn_flags & HASBUF) == 0)
+ MALLOC(cnp->cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
+ if (ndp->ni_segflg == UIO_SYSSPACE)
+ error = copystr(ndp->ni_dirp, cnp->cn_pnbuf,
+ MAXPATHLEN, &ndp->ni_pathlen);
+ else
+ error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf,
+ MAXPATHLEN, &ndp->ni_pathlen);
+ if (error) {
+ free(cnp->cn_pnbuf, M_NAMEI);
+ ndp->ni_vp = NULL;
+ return (error);
+ }
+ ndp->ni_loopcnt = 0;
+#ifdef KTRACE
+ if (KTRPOINT(cnp->cn_proc, KTR_NAMEI))
+ ktrnamei(cnp->cn_proc->p_tracep, cnp->cn_pnbuf);
+#endif
+
+ /*
+ * Get starting point for the translation.
+ */
+ if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL)
+ ndp->ni_rootdir = rootvnode;
+ dp = fdp->fd_cdir;
+ VREF(dp);
+ for (;;) {
+ /*
+ * Check if root directory should replace current directory.
+ * Done at start of translation and after symbolic link.
+ */
+ cnp->cn_nameptr = cnp->cn_pnbuf;
+ if (*(cnp->cn_nameptr) == '/') {
+ vrele(dp);
+ while (*(cnp->cn_nameptr) == '/') {
+ cnp->cn_nameptr++;
+ ndp->ni_pathlen--;
+ }
+ dp = ndp->ni_rootdir;
+ VREF(dp);
+ }
+ ndp->ni_startdir = dp;
+ if (error = lookup(ndp)) {
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ return (error);
+ }
+ /*
+ * Check for symbolic link
+ */
+ if ((cnp->cn_flags & ISSYMLINK) == 0) {
+ if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0)
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ else
+ cnp->cn_flags |= HASBUF;
+ return (0);
+ }
+ if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
+ VOP_UNLOCK(ndp->ni_dvp);
+ if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
+ error = ELOOP;
+ break;
+ }
+ if (ndp->ni_pathlen > 1)
+ MALLOC(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
+ else
+ cp = cnp->cn_pnbuf;
+ aiov.iov_base = cp;
+ aiov.iov_len = MAXPATHLEN;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_offset = 0;
+ auio.uio_rw = UIO_READ;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_procp = (struct proc *)0;
+ auio.uio_resid = MAXPATHLEN;
+ if (error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred)) {
+ if (ndp->ni_pathlen > 1)
+ free(cp, M_NAMEI);
+ break;
+ }
+ linklen = MAXPATHLEN - auio.uio_resid;
+ if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
+ if (ndp->ni_pathlen > 1)
+ free(cp, M_NAMEI);
+ error = ENAMETOOLONG;
+ break;
+ }
+ if (ndp->ni_pathlen > 1) {
+ bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ cnp->cn_pnbuf = cp;
+ } else
+ cnp->cn_pnbuf[linklen] = '\0';
+ ndp->ni_pathlen += linklen;
+ vput(ndp->ni_vp);
+ dp = ndp->ni_dvp;
+ }
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ vrele(ndp->ni_dvp);
+ vput(ndp->ni_vp);
+ ndp->ni_vp = NULL;
+ return (error);
+}
+
+/*
+ * Search a pathname.
+ * This is a very central and rather complicated routine.
+ *
+ * The pathname is pointed to by ni_ptr and is of length ni_pathlen.
+ * The starting directory is taken from ni_startdir. The pathname is
+ * descended until done, or a symbolic link is encountered. The variable
+ * ni_more is clear if the path is completed; it is set to one if a
+ * symbolic link needing interpretation is encountered.
+ *
+ * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
+ * whether the name is to be looked up, created, renamed, or deleted.
+ * When CREATE, RENAME, or DELETE is specified, information usable in
+ * creating, renaming, or deleting a directory entry may be calculated.
+ * If flag has LOCKPARENT or'ed into it, the parent directory is returned
+ * locked. If flag has WANTPARENT or'ed into it, the parent directory is
+ * returned unlocked. Otherwise the parent directory is not returned. If
+ * the target of the pathname exists and LOCKLEAF is or'ed into the flag
+ * the target is returned locked, otherwise it is returned unlocked.
+ * When creating or renaming and LOCKPARENT is specified, the target may not
+ * be ".". When deleting and LOCKPARENT is specified, the target may be ".".
+ *
+ * Overall outline of lookup:
+ *
+ * dirloop:
+ * identify next component of name at ndp->ni_ptr
+ * handle degenerate case where name is null string
+ * if .. and crossing mount points and on mounted filesys, find parent
+ * call VOP_LOOKUP routine for next component name
+ * directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set
+ * component vnode returned in ni_vp (if it exists), locked.
+ * if result vnode is mounted on and crossing mount points,
+ * find mounted on vnode
+ * if more components of name, do next level at dirloop
+ * return the answer in ni_vp, locked if LOCKLEAF set
+ * if LOCKPARENT set, return locked parent in ni_dvp
+ * if WANTPARENT set, return unlocked parent in ni_dvp
+ */
+int
+lookup(ndp)
+ register struct nameidata *ndp;
+{
+ register char *cp; /* pointer into pathname argument */
+ register struct vnode *dp = 0; /* the directory we are searching */
+ struct vnode *tdp; /* saved dp */
+ struct mount *mp; /* mount table entry */
+ int docache; /* == 0 do not cache last component */
+ int wantparent; /* 1 => wantparent or lockparent flag */
+ int rdonly; /* lookup read-only flag bit */
+ int error = 0;
+ struct componentname *cnp = &ndp->ni_cnd;
+
+ /*
+ * Setup: break out flag bits into variables.
+ */
+ wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
+ docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
+ if (cnp->cn_nameiop == DELETE ||
+ (wantparent && cnp->cn_nameiop != CREATE))
+ docache = 0;
+ rdonly = cnp->cn_flags & RDONLY;
+ ndp->ni_dvp = NULL;
+ cnp->cn_flags &= ~ISSYMLINK;
+ dp = ndp->ni_startdir;
+ ndp->ni_startdir = NULLVP;
+ VOP_LOCK(dp);
+
+dirloop:
+ /*
+ * Search a new directory.
+ *
+ * The cn_hash value is for use by vfs_cache.
+ * The last component of the filename is left accessible via
+ * cnp->cn_nameptr for callers that need the name. Callers needing
+ * the name set the SAVENAME flag. When done, they assume
+ * responsibility for freeing the pathname buffer.
+ */
+ cnp->cn_consume = 0;
+ cnp->cn_hash = 0;
+ for (cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++)
+ cnp->cn_hash += (unsigned char)*cp;
+ cnp->cn_namelen = cp - cnp->cn_nameptr;
+ if (cnp->cn_namelen > NAME_MAX) {
+ error = ENAMETOOLONG;
+ goto bad;
+ }
+#ifdef NAMEI_DIAGNOSTIC
+ { char c = *cp;
+ *cp = '\0';
+ printf("{%s}: ", cnp->cn_nameptr);
+ *cp = c; }
+#endif
+ ndp->ni_pathlen -= cnp->cn_namelen;
+ ndp->ni_next = cp;
+ cnp->cn_flags |= MAKEENTRY;
+ if (*cp == '\0' && docache == 0)
+ cnp->cn_flags &= ~MAKEENTRY;
+ if (cnp->cn_namelen == 2 &&
+ cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
+ cnp->cn_flags |= ISDOTDOT;
+ else
+ cnp->cn_flags &= ~ISDOTDOT;
+ if (*ndp->ni_next == 0)
+ cnp->cn_flags |= ISLASTCN;
+ else
+ cnp->cn_flags &= ~ISLASTCN;
+
+
+ /*
+ * Check for degenerate name (e.g. / or "")
+ * which is a way of talking about a directory,
+ * e.g. like "/." or ".".
+ */
+ if (cnp->cn_nameptr[0] == '\0') {
+ if (cnp->cn_nameiop != LOOKUP) {
+ error = EISDIR;
+ goto bad;
+ }
+ if (dp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto bad;
+ }
+ if (wantparent) {
+ ndp->ni_dvp = dp;
+ VREF(dp);
+ }
+ ndp->ni_vp = dp;
+ if (!(cnp->cn_flags & (LOCKPARENT | LOCKLEAF)))
+ VOP_UNLOCK(dp);
+ if (cnp->cn_flags & SAVESTART)
+ panic("lookup: SAVESTART");
+ return (0);
+ }
+
+ /*
+ * Handle "..": two special cases.
+ * 1. If at root directory (e.g. after chroot)
+ * or at absolute root directory
+ * then ignore it so can't get out.
+ * 2. If this vnode is the root of a mounted
+ * filesystem, then replace it with the
+ * vnode which was mounted on so we take the
+ * .. in the other file system.
+ */
+ if (cnp->cn_flags & ISDOTDOT) {
+ for (;;) {
+ if (dp == ndp->ni_rootdir || dp == rootvnode) {
+ ndp->ni_dvp = dp;
+ ndp->ni_vp = dp;
+ VREF(dp);
+ goto nextname;
+ }
+ if ((dp->v_flag & VROOT) == 0 ||
+ (cnp->cn_flags & NOCROSSMOUNT))
+ break;
+ tdp = dp;
+ dp = dp->v_mount->mnt_vnodecovered;
+ vput(tdp);
+ VREF(dp);
+ VOP_LOCK(dp);
+ }
+ }
+
+ /*
+ * We now have a segment name to search for, and a directory to search.
+ */
+unionlookup:
+ ndp->ni_dvp = dp;
+ if (error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) {
+#ifdef DIAGNOSTIC
+ if (ndp->ni_vp != NULL)
+ panic("leaf should be empty");
+#endif
+#ifdef NAMEI_DIAGNOSTIC
+ printf("not found\n");
+#endif
+ if ((error == ENOENT) &&
+ (dp->v_flag & VROOT) &&
+ (dp->v_mount->mnt_flag & MNT_UNION)) {
+ tdp = dp;
+ dp = dp->v_mount->mnt_vnodecovered;
+ vput(tdp);
+ VREF(dp);
+ VOP_LOCK(dp);
+ goto unionlookup;
+ }
+
+ if (error != EJUSTRETURN)
+ goto bad;
+ /*
+ * If creating and at end of pathname, then can consider
+ * allowing file to be created.
+ */
+ if (rdonly || (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY)) {
+ error = EROFS;
+ goto bad;
+ }
+ /*
+ * We return with ni_vp NULL to indicate that the entry
+ * doesn't currently exist, leaving a pointer to the
+ * (possibly locked) directory inode in ndp->ni_dvp.
+ */
+ if (cnp->cn_flags & SAVESTART) {
+ ndp->ni_startdir = ndp->ni_dvp;
+ VREF(ndp->ni_startdir);
+ }
+ return (0);
+ }
+#ifdef NAMEI_DIAGNOSTIC
+ printf("found\n");
+#endif
+
+ /*
+ * Take into account any additional components consumed by
+ * the underlying filesystem.
+ */
+ if (cnp->cn_consume > 0) {
+ cnp->cn_nameptr += cnp->cn_consume;
+ ndp->ni_next += cnp->cn_consume;
+ ndp->ni_pathlen -= cnp->cn_consume;
+ cnp->cn_consume = 0;
+ }
+
+ dp = ndp->ni_vp;
+ /*
+ * Check for symbolic link
+ */
+ if ((dp->v_type == VLNK) &&
+ ((cnp->cn_flags & FOLLOW) || *ndp->ni_next == '/')) {
+ cnp->cn_flags |= ISSYMLINK;
+ return (0);
+ }
+
+ /*
+ * Check to see if the vnode has been mounted on;
+ * if so find the root of the mounted file system.
+ */
+ while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
+ (cnp->cn_flags & NOCROSSMOUNT) == 0) {
+ if (mp->mnt_flag & MNT_MLOCK) {
+ mp->mnt_flag |= MNT_MWAIT;
+ sleep((caddr_t)mp, PVFS);
+ continue;
+ }
+ if (error = VFS_ROOT(dp->v_mountedhere, &tdp))
+ goto bad2;
+ vput(dp);
+ ndp->ni_vp = dp = tdp;
+ }
+
+nextname:
+ /*
+ * Not a symbolic link. If more pathname,
+ * continue at next component, else return.
+ */
+ if (*ndp->ni_next == '/') {
+ cnp->cn_nameptr = ndp->ni_next;
+ while (*cnp->cn_nameptr == '/') {
+ cnp->cn_nameptr++;
+ ndp->ni_pathlen--;
+ }
+ vrele(ndp->ni_dvp);
+ goto dirloop;
+ }
+ /*
+ * Check for read-only file systems.
+ */
+ if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
+ /*
+ * Disallow directory write attempts on read-only
+ * file systems.
+ */
+ if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) ||
+ (wantparent &&
+ (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY))) {
+ error = EROFS;
+ goto bad2;
+ }
+ }
+ if (cnp->cn_flags & SAVESTART) {
+ ndp->ni_startdir = ndp->ni_dvp;
+ VREF(ndp->ni_startdir);
+ }
+ if (!wantparent)
+ vrele(ndp->ni_dvp);
+ if ((cnp->cn_flags & LOCKLEAF) == 0)
+ VOP_UNLOCK(dp);
+ return (0);
+
+bad2:
+ if ((cnp->cn_flags & LOCKPARENT) && *ndp->ni_next == '\0')
+ VOP_UNLOCK(ndp->ni_dvp);
+ vrele(ndp->ni_dvp);
+bad:
+ vput(dp);
+ ndp->ni_vp = NULL;
+ return (error);
+}
+
+
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c
new file mode 100644
index 0000000..2fe39eb
--- /dev/null
+++ b/sys/kern/vfs_mount.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vfs_conf.c 8.8 (Berkeley) 3/31/94
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+
+#ifdef FFS
+#include <ufs/ffs/ffs_extern.h>
+
+/*
+ * This specifies the filesystem used to mount the root.
+ * This specification should be done by /etc/config.
+ */
+int (*mountroot)() = ffs_mountroot;
+#endif
+
+/*
+ * These define the root filesystem and device.
+ */
+struct mount *rootfs;
+struct vnode *rootvnode;
+
+/*
+ * Set up the filesystem operations for vnodes.
+ * The types are defined in mount.h.
+ */
+#ifdef FFS
+extern struct vfsops ufs_vfsops;
+#define UFS_VFSOPS &ufs_vfsops
+#else
+#define UFS_VFSOPS NULL
+#endif
+
+#ifdef LFS
+extern struct vfsops lfs_vfsops;
+#define LFS_VFSOPS &lfs_vfsops
+#else
+#define LFS_VFSOPS NULL
+#endif
+
+#ifdef MFS
+extern struct vfsops mfs_vfsops;
+#define MFS_VFSOPS &mfs_vfsops
+#else
+#define MFS_VFSOPS NULL
+#endif
+
+#ifdef NFS
+extern struct vfsops nfs_vfsops;
+#define NFS_VFSOPS &nfs_vfsops
+#else
+#define NFS_VFSOPS NULL
+#endif
+
+#ifdef FDESC
+extern struct vfsops fdesc_vfsops;
+#define FDESC_VFSOPS &fdesc_vfsops
+#else
+#define FDESC_VFSOPS NULL
+#endif
+
+#ifdef PORTAL
+extern struct vfsops portal_vfsops;
+#define PORTAL_VFSOPS &portal_vfsops
+#else
+#define PORTAL_VFSOPS NULL
+#endif
+
+#ifdef NULLFS
+extern struct vfsops null_vfsops;
+#define NULL_VFSOPS &null_vfsops
+#else
+#define NULL_VFSOPS NULL
+#endif
+
+#ifdef UMAPFS
+extern struct vfsops umap_vfsops;
+#define UMAP_VFSOPS &umap_vfsops
+#else
+#define UMAP_VFSOPS NULL
+#endif
+
+#ifdef KERNFS
+extern struct vfsops kernfs_vfsops;
+#define KERNFS_VFSOPS &kernfs_vfsops
+#else
+#define KERNFS_VFSOPS NULL
+#endif
+
+#ifdef PROCFS
+extern struct vfsops procfs_vfsops;
+#define PROCFS_VFSOPS &procfs_vfsops
+#else
+#define PROCFS_VFSOPS NULL
+#endif
+
+#ifdef AFS
+extern struct vfsops afs_vfsops;
+#define AFS_VFSOPS &afs_vfsops
+#else
+#define AFS_VFSOPS NULL
+#endif
+
+#ifdef CD9660
+extern struct vfsops cd9660_vfsops;
+#define CD9660_VFSOPS &cd9660_vfsops
+#else
+#define CD9660_VFSOPS NULL
+#endif
+
+#ifdef UNION
+extern struct vfsops union_vfsops;
+#define UNION_VFSOPS &union_vfsops
+#else
+#define UNION_VFSOPS NULL
+#endif
+
+struct vfsops *vfssw[] = {
+ NULL, /* 0 = MOUNT_NONE */
+ UFS_VFSOPS, /* 1 = MOUNT_UFS */
+ NFS_VFSOPS, /* 2 = MOUNT_NFS */
+ MFS_VFSOPS, /* 3 = MOUNT_MFS */
+ NULL, /* 4 = MOUNT_PC */
+ LFS_VFSOPS, /* 5 = MOUNT_LFS */
+ NULL, /* 6 = MOUNT_LOFS */
+ FDESC_VFSOPS, /* 7 = MOUNT_FDESC */
+ PORTAL_VFSOPS, /* 8 = MOUNT_PORTAL */
+ NULL_VFSOPS, /* 9 = MOUNT_NULL */
+ UMAP_VFSOPS, /* 10 = MOUNT_UMAP */
+ KERNFS_VFSOPS, /* 11 = MOUNT_KERNFS */
+ PROCFS_VFSOPS, /* 12 = MOUNT_PROCFS */
+ AFS_VFSOPS, /* 13 = MOUNT_AFS */
+ CD9660_VFSOPS, /* 14 = MOUNT_CD9660 */
+ UNION_VFSOPS, /* 15 = MOUNT_UNION */
+ 0
+};
+
+
+/*
+ *
+ * vfs_opv_descs enumerates the list of vnode classes, each with it's own
+ * vnode operation vector. It is consulted at system boot to build operation
+ * vectors. It is NULL terminated.
+ *
+ */
+extern struct vnodeopv_desc ffs_vnodeop_opv_desc;
+extern struct vnodeopv_desc ffs_specop_opv_desc;
+extern struct vnodeopv_desc ffs_fifoop_opv_desc;
+extern struct vnodeopv_desc lfs_vnodeop_opv_desc;
+extern struct vnodeopv_desc lfs_specop_opv_desc;
+extern struct vnodeopv_desc lfs_fifoop_opv_desc;
+extern struct vnodeopv_desc mfs_vnodeop_opv_desc;
+extern struct vnodeopv_desc dead_vnodeop_opv_desc;
+extern struct vnodeopv_desc fifo_vnodeop_opv_desc;
+extern struct vnodeopv_desc spec_vnodeop_opv_desc;
+extern struct vnodeopv_desc nfsv2_vnodeop_opv_desc;
+extern struct vnodeopv_desc spec_nfsv2nodeop_opv_desc;
+extern struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc;
+extern struct vnodeopv_desc fdesc_vnodeop_opv_desc;
+extern struct vnodeopv_desc portal_vnodeop_opv_desc;
+extern struct vnodeopv_desc null_vnodeop_opv_desc;
+extern struct vnodeopv_desc umap_vnodeop_opv_desc;
+extern struct vnodeopv_desc kernfs_vnodeop_opv_desc;
+extern struct vnodeopv_desc procfs_vnodeop_opv_desc;
+extern struct vnodeopv_desc cd9660_vnodeop_opv_desc;
+extern struct vnodeopv_desc cd9660_specop_opv_desc;
+extern struct vnodeopv_desc cd9660_fifoop_opv_desc;
+extern struct vnodeopv_desc union_vnodeop_opv_desc;
+
+struct vnodeopv_desc *vfs_opv_descs[] = {
+ &ffs_vnodeop_opv_desc,
+ &ffs_specop_opv_desc,
+#ifdef FIFO
+ &ffs_fifoop_opv_desc,
+#endif
+ &dead_vnodeop_opv_desc,
+#ifdef FIFO
+ &fifo_vnodeop_opv_desc,
+#endif
+ &spec_vnodeop_opv_desc,
+#ifdef LFS
+ &lfs_vnodeop_opv_desc,
+ &lfs_specop_opv_desc,
+#ifdef FIFO
+ &lfs_fifoop_opv_desc,
+#endif
+#endif
+#ifdef MFS
+ &mfs_vnodeop_opv_desc,
+#endif
+#ifdef NFS
+ &nfsv2_vnodeop_opv_desc,
+ &spec_nfsv2nodeop_opv_desc,
+#ifdef FIFO
+ &fifo_nfsv2nodeop_opv_desc,
+#endif
+#endif
+#ifdef FDESC
+ &fdesc_vnodeop_opv_desc,
+#endif
+#ifdef PORTAL
+ &portal_vnodeop_opv_desc,
+#endif
+#ifdef NULLFS
+ &null_vnodeop_opv_desc,
+#endif
+#ifdef UMAPFS
+ &umap_vnodeop_opv_desc,
+#endif
+#ifdef KERNFS
+ &kernfs_vnodeop_opv_desc,
+#endif
+#ifdef PROCFS
+ &procfs_vnodeop_opv_desc,
+#endif
+#ifdef CD9660
+ &cd9660_vnodeop_opv_desc,
+ &cd9660_specop_opv_desc,
+#ifdef FIFO
+ &cd9660_fifoop_opv_desc,
+#endif
+#endif
+#ifdef UNION
+ &union_vnodeop_opv_desc,
+#endif
+ NULL
+};
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
new file mode 100644
index 0000000..9535b8a
--- /dev/null
+++ b/sys/kern/vfs_subr.c
@@ -0,0 +1,1351 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vfs_subr.c 8.13 (Berkeley) 4/18/94
+ */
+
+/*
+ * External virtual filesystem routines
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/namei.h>
+#include <sys/ucred.h>
+#include <sys/buf.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/domain.h>
+#include <sys/mbuf.h>
+
+#include <vm/vm.h>
+#include <sys/sysctl.h>
+
+#include <miscfs/specfs/specdev.h>
+
+void insmntque __P((struct vnode *, struct mount *));
+
+enum vtype iftovt_tab[16] = {
+ VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON,
+ VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VBAD,
+};
+int vttoif_tab[9] = {
+ 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK,
+ S_IFSOCK, S_IFIFO, S_IFMT,
+};
+
+/*
+ * Insq/Remq for the vnode usage lists.
+ */
+#define bufinsvn(bp, dp) LIST_INSERT_HEAD(dp, bp, b_vnbufs)
+#define bufremvn(bp) { \
+ LIST_REMOVE(bp, b_vnbufs); \
+ (bp)->b_vnbufs.le_next = NOLIST; \
+}
+
+TAILQ_HEAD(freelst, vnode) vnode_free_list; /* vnode free list */
+struct mntlist mountlist; /* mounted filesystem list */
+
+/*
+ * Initialize the vnode management data structures.
+ */
+void
+vntblinit()
+{
+
+ TAILQ_INIT(&vnode_free_list);
+ TAILQ_INIT(&mountlist);
+}
+
+/*
+ * Lock a filesystem.
+ * Used to prevent access to it while mounting and unmounting.
+ */
+int
+vfs_lock(mp)
+ register struct mount *mp;
+{
+
+ while(mp->mnt_flag & MNT_MLOCK) {
+ mp->mnt_flag |= MNT_MWAIT;
+ sleep((caddr_t)mp, PVFS);
+ }
+ mp->mnt_flag |= MNT_MLOCK;
+ return (0);
+}
+
+/*
+ * Unlock a locked filesystem.
+ * Panic if filesystem is not locked.
+ */
+void
+vfs_unlock(mp)
+ register struct mount *mp;
+{
+
+ if ((mp->mnt_flag & MNT_MLOCK) == 0)
+ panic("vfs_unlock: not locked");
+ mp->mnt_flag &= ~MNT_MLOCK;
+ if (mp->mnt_flag & MNT_MWAIT) {
+ mp->mnt_flag &= ~MNT_MWAIT;
+ wakeup((caddr_t)mp);
+ }
+}
+
+/*
+ * Mark a mount point as busy.
+ * Used to synchronize access and to delay unmounting.
+ */
+int
+vfs_busy(mp)
+ register struct mount *mp;
+{
+
+ while(mp->mnt_flag & MNT_MPBUSY) {
+ mp->mnt_flag |= MNT_MPWANT;
+ sleep((caddr_t)&mp->mnt_flag, PVFS);
+ }
+ if (mp->mnt_flag & MNT_UNMOUNT)
+ return (1);
+ mp->mnt_flag |= MNT_MPBUSY;
+ return (0);
+}
+
+/*
+ * Free a busy filesystem.
+ * Panic if filesystem is not busy.
+ */
+void
+vfs_unbusy(mp)
+ register struct mount *mp;
+{
+
+ if ((mp->mnt_flag & MNT_MPBUSY) == 0)
+ panic("vfs_unbusy: not busy");
+ mp->mnt_flag &= ~MNT_MPBUSY;
+ if (mp->mnt_flag & MNT_MPWANT) {
+ mp->mnt_flag &= ~MNT_MPWANT;
+ wakeup((caddr_t)&mp->mnt_flag);
+ }
+}
+
+/*
+ * Lookup a mount point by filesystem identifier.
+ */
+struct mount *
+getvfs(fsid)
+ fsid_t *fsid;
+{
+ register struct mount *mp;
+
+ for (mp = mountlist.tqh_first; mp != NULL; mp = mp->mnt_list.tqe_next) {
+ if (mp->mnt_stat.f_fsid.val[0] == fsid->val[0] &&
+ mp->mnt_stat.f_fsid.val[1] == fsid->val[1])
+ return (mp);
+ }
+ return ((struct mount *)0);
+}
+
+/*
+ * Get a new unique fsid
+ */
+void
+getnewfsid(mp, mtype)
+ struct mount *mp;
+ int mtype;
+{
+static u_short xxxfs_mntid;
+
+ fsid_t tfsid;
+
+ mp->mnt_stat.f_fsid.val[0] = makedev(nblkdev + mtype, 0);
+ mp->mnt_stat.f_fsid.val[1] = mtype;
+ if (xxxfs_mntid == 0)
+ ++xxxfs_mntid;
+ tfsid.val[0] = makedev(nblkdev + mtype, xxxfs_mntid);
+ tfsid.val[1] = mtype;
+ if (mountlist.tqh_first != NULL) {
+ while (getvfs(&tfsid)) {
+ tfsid.val[0]++;
+ xxxfs_mntid++;
+ }
+ }
+ mp->mnt_stat.f_fsid.val[0] = tfsid.val[0];
+}
+
+/*
+ * Set vnode attributes to VNOVAL
+ */
+void
+vattr_null(vap)
+ register struct vattr *vap;
+{
+
+ vap->va_type = VNON;
+ vap->va_size = VNOVAL;
+ vap->va_bytes = VNOVAL;
+ vap->va_mode = vap->va_nlink = vap->va_uid = vap->va_gid =
+ vap->va_fsid = vap->va_fileid =
+ vap->va_blocksize = vap->va_rdev =
+ vap->va_atime.ts_sec = vap->va_atime.ts_nsec =
+ vap->va_mtime.ts_sec = vap->va_mtime.ts_nsec =
+ vap->va_ctime.ts_sec = vap->va_ctime.ts_nsec =
+ vap->va_flags = vap->va_gen = VNOVAL;
+ vap->va_vaflags = 0;
+}
+
+/*
+ * Routines having to do with the management of the vnode table.
+ */
+extern int (**dead_vnodeop_p)();
+extern void vclean();
+long numvnodes;
+extern struct vattr va_null;
+
+/*
+ * Return the next vnode from the free list.
+ */
+int
+getnewvnode(tag, mp, vops, vpp)
+ enum vtagtype tag;
+ struct mount *mp;
+ int (**vops)();
+ struct vnode **vpp;
+{
+ register struct vnode *vp;
+ int s;
+
+ if ((vnode_free_list.tqh_first == NULL &&
+ numvnodes < 2 * desiredvnodes) ||
+ numvnodes < desiredvnodes) {
+ vp = (struct vnode *)malloc((u_long)sizeof *vp,
+ M_VNODE, M_WAITOK);
+ bzero((char *)vp, sizeof *vp);
+ numvnodes++;
+ } else {
+ if ((vp = vnode_free_list.tqh_first) == NULL) {
+ tablefull("vnode");
+ *vpp = 0;
+ return (ENFILE);
+ }
+ if (vp->v_usecount)
+ panic("free vnode isn't");
+ TAILQ_REMOVE(&vnode_free_list, vp, v_freelist);
+ /* see comment on why 0xdeadb is set at end of vgone (below) */
+ vp->v_freelist.tqe_prev = (struct vnode **)0xdeadb;
+ vp->v_lease = NULL;
+ if (vp->v_type != VBAD)
+ vgone(vp);
+#ifdef DIAGNOSTIC
+ if (vp->v_data)
+ panic("cleaned vnode isn't");
+ s = splbio();
+ if (vp->v_numoutput)
+ panic("Clean vnode has pending I/O's");
+ splx(s);
+#endif
+ vp->v_flag = 0;
+ vp->v_lastr = 0;
+ vp->v_ralen = 0;
+ vp->v_maxra = 0;
+ vp->v_lastw = 0;
+ vp->v_lasta = 0;
+ vp->v_cstart = 0;
+ vp->v_clen = 0;
+ vp->v_socket = 0;
+ }
+ vp->v_type = VNON;
+ cache_purge(vp);
+ vp->v_tag = tag;
+ vp->v_op = vops;
+ insmntque(vp, mp);
+ *vpp = vp;
+ vp->v_usecount = 1;
+ vp->v_data = 0;
+ return (0);
+}
+
+/*
+ * Move a vnode from one mount queue to another.
+ */
+void
+insmntque(vp, mp)
+ register struct vnode *vp;
+ register struct mount *mp;
+{
+
+ /*
+ * Delete from old mount point vnode list, if on one.
+ */
+ if (vp->v_mount != NULL)
+ LIST_REMOVE(vp, v_mntvnodes);
+ /*
+ * Insert into list of vnodes for the new mount point, if available.
+ */
+ if ((vp->v_mount = mp) == NULL)
+ return;
+ LIST_INSERT_HEAD(&mp->mnt_vnodelist, vp, v_mntvnodes);
+}
+
+/*
+ * Update outstanding I/O count and do wakeup if requested.
+ */
+void
+vwakeup(bp)
+ register struct buf *bp;
+{
+ register struct vnode *vp;
+
+ bp->b_flags &= ~B_WRITEINPROG;
+ if (vp = bp->b_vp) {
+ vp->v_numoutput--;
+ if (vp->v_numoutput < 0)
+ panic("vwakeup: neg numoutput");
+ if ((vp->v_flag & VBWAIT) && vp->v_numoutput <= 0) {
+ if (vp->v_numoutput < 0)
+ panic("vwakeup: neg numoutput");
+ vp->v_flag &= ~VBWAIT;
+ wakeup((caddr_t)&vp->v_numoutput);
+ }
+ }
+}
+
+/*
+ * Flush out and invalidate all buffers associated with a vnode.
+ * Called with the underlying object locked.
+ */
+int
+vinvalbuf(vp, flags, cred, p, slpflag, slptimeo)
+ register struct vnode *vp;
+ int flags;
+ struct ucred *cred;
+ struct proc *p;
+ int slpflag, slptimeo;
+{
+ register struct buf *bp;
+ struct buf *nbp, *blist;
+ int s, error;
+
+ if (flags & V_SAVE) {
+ if (error = VOP_FSYNC(vp, cred, MNT_WAIT, p))
+ return (error);
+ if (vp->v_dirtyblkhd.lh_first != NULL)
+ panic("vinvalbuf: dirty bufs");
+ }
+ for (;;) {
+ if ((blist = vp->v_cleanblkhd.lh_first) && flags & V_SAVEMETA)
+ while (blist && blist->b_lblkno < 0)
+ blist = blist->b_vnbufs.le_next;
+ if (!blist && (blist = vp->v_dirtyblkhd.lh_first) &&
+ (flags & V_SAVEMETA))
+ while (blist && blist->b_lblkno < 0)
+ blist = blist->b_vnbufs.le_next;
+ if (!blist)
+ break;
+
+ for (bp = blist; bp; bp = nbp) {
+ nbp = bp->b_vnbufs.le_next;
+ if (flags & V_SAVEMETA && bp->b_lblkno < 0)
+ continue;
+ s = splbio();
+ if (bp->b_flags & B_BUSY) {
+ bp->b_flags |= B_WANTED;
+ error = tsleep((caddr_t)bp,
+ slpflag | (PRIBIO + 1), "vinvalbuf",
+ slptimeo);
+ splx(s);
+ if (error)
+ return (error);
+ break;
+ }
+ bremfree(bp);
+ bp->b_flags |= B_BUSY;
+ splx(s);
+ /*
+ * XXX Since there are no node locks for NFS, I believe
+ * there is a slight chance that a delayed write will
+ * occur while sleeping just above, so check for it.
+ */
+ if ((bp->b_flags & B_DELWRI) && (flags & V_SAVE)) {
+ (void) VOP_BWRITE(bp);
+ break;
+ }
+ bp->b_flags |= B_INVAL;
+ brelse(bp);
+ }
+ }
+ if (!(flags & V_SAVEMETA) &&
+ (vp->v_dirtyblkhd.lh_first || vp->v_cleanblkhd.lh_first))
+ panic("vinvalbuf: flush failed");
+ return (0);
+}
+
+/*
+ * Associate a buffer with a vnode.
+ */
+void
+bgetvp(vp, bp)
+ register struct vnode *vp;
+ register struct buf *bp;
+{
+
+ if (bp->b_vp)
+ panic("bgetvp: not free");
+ VHOLD(vp);
+ bp->b_vp = vp;
+ if (vp->v_type == VBLK || vp->v_type == VCHR)
+ bp->b_dev = vp->v_rdev;
+ else
+ bp->b_dev = NODEV;
+ /*
+ * Insert onto list for new vnode.
+ */
+ bufinsvn(bp, &vp->v_cleanblkhd);
+}
+
+/*
+ * Disassociate a buffer from a vnode.
+ */
+void
+brelvp(bp)
+ register struct buf *bp;
+{
+ struct vnode *vp;
+
+ if (bp->b_vp == (struct vnode *) 0)
+ panic("brelvp: NULL");
+ /*
+ * Delete from old vnode list, if on one.
+ */
+ if (bp->b_vnbufs.le_next != NOLIST)
+ bufremvn(bp);
+ vp = bp->b_vp;
+ bp->b_vp = (struct vnode *) 0;
+ HOLDRELE(vp);
+}
+
+/*
+ * Reassign a buffer from one vnode to another.
+ * Used to assign file specific control information
+ * (indirect blocks) to the vnode to which they belong.
+ */
+void
+reassignbuf(bp, newvp)
+ register struct buf *bp;
+ register struct vnode *newvp;
+{
+ register struct buflists *listheadp;
+
+ if (newvp == NULL) {
+ printf("reassignbuf: NULL");
+ return;
+ }
+ /*
+ * Delete from old vnode list, if on one.
+ */
+ if (bp->b_vnbufs.le_next != NOLIST)
+ bufremvn(bp);
+ /*
+ * If dirty, put on list of dirty buffers;
+ * otherwise insert onto list of clean buffers.
+ */
+ if (bp->b_flags & B_DELWRI)
+ listheadp = &newvp->v_dirtyblkhd;
+ else
+ listheadp = &newvp->v_cleanblkhd;
+ bufinsvn(bp, listheadp);
+}
+
+/*
+ * Create a vnode for a block device.
+ * Used for root filesystem, argdev, and swap areas.
+ * Also used for memory file system special devices.
+ */
+int
+bdevvp(dev, vpp)
+ dev_t dev;
+ struct vnode **vpp;
+{
+ register struct vnode *vp;
+ struct vnode *nvp;
+ int error;
+
+ if (dev == NODEV)
+ return (0);
+ error = getnewvnode(VT_NON, (struct mount *)0, spec_vnodeop_p, &nvp);
+ if (error) {
+ *vpp = 0;
+ return (error);
+ }
+ vp = nvp;
+ vp->v_type = VBLK;
+ if (nvp = checkalias(vp, dev, (struct mount *)0)) {
+ vput(vp);
+ vp = nvp;
+ }
+ *vpp = vp;
+ return (0);
+}
+
+/*
+ * Check to see if the new vnode represents a special device
+ * for which we already have a vnode (either because of
+ * bdevvp() or because of a different vnode representing
+ * the same block device). If such an alias exists, deallocate
+ * the existing contents and return the aliased vnode. The
+ * caller is responsible for filling it with its new contents.
+ */
+struct vnode *
+checkalias(nvp, nvp_rdev, mp)
+ register struct vnode *nvp;
+ dev_t nvp_rdev;
+ struct mount *mp;
+{
+ register struct vnode *vp;
+ struct vnode **vpp;
+
+ if (nvp->v_type != VBLK && nvp->v_type != VCHR)
+ return (NULLVP);
+
+ vpp = &speclisth[SPECHASH(nvp_rdev)];
+loop:
+ for (vp = *vpp; vp; vp = vp->v_specnext) {
+ if (nvp_rdev != vp->v_rdev || nvp->v_type != vp->v_type)
+ continue;
+ /*
+ * Alias, but not in use, so flush it out.
+ */
+ if (vp->v_usecount == 0) {
+ vgone(vp);
+ goto loop;
+ }
+ if (vget(vp, 1))
+ goto loop;
+ break;
+ }
+ if (vp == NULL || vp->v_tag != VT_NON) {
+ MALLOC(nvp->v_specinfo, struct specinfo *,
+ sizeof(struct specinfo), M_VNODE, M_WAITOK);
+ nvp->v_rdev = nvp_rdev;
+ nvp->v_hashchain = vpp;
+ nvp->v_specnext = *vpp;
+ nvp->v_specflags = 0;
+ *vpp = nvp;
+ if (vp != NULL) {
+ nvp->v_flag |= VALIASED;
+ vp->v_flag |= VALIASED;
+ vput(vp);
+ }
+ return (NULLVP);
+ }
+ VOP_UNLOCK(vp);
+ vclean(vp, 0);
+ vp->v_op = nvp->v_op;
+ vp->v_tag = nvp->v_tag;
+ nvp->v_type = VNON;
+ insmntque(vp, mp);
+ return (vp);
+}
+
+/*
+ * Grab a particular vnode from the free list, increment its
+ * reference count and lock it. The vnode lock bit is set the
+ * vnode is being eliminated in vgone. The process is awakened
+ * when the transition is completed, and an error returned to
+ * indicate that the vnode is no longer usable (possibly having
+ * been changed to a new file system type).
+ */
+int
+vget(vp, lockflag)
+ register struct vnode *vp;
+ int lockflag;
+{
+
+ /*
+ * If the vnode is in the process of being cleaned out for
+ * another use, we wait for the cleaning to finish and then
+ * return failure. Cleaning is determined either by checking
+ * that the VXLOCK flag is set, or that the use count is
+ * zero with the back pointer set to show that it has been
+ * removed from the free list by getnewvnode. The VXLOCK
+ * flag may not have been set yet because vclean is blocked in
+ * the VOP_LOCK call waiting for the VOP_INACTIVE to complete.
+ */
+ if ((vp->v_flag & VXLOCK) ||
+ (vp->v_usecount == 0 &&
+ vp->v_freelist.tqe_prev == (struct vnode **)0xdeadb)) {
+ vp->v_flag |= VXWANT;
+ sleep((caddr_t)vp, PINOD);
+ return (1);
+ }
+ if (vp->v_usecount == 0)
+ TAILQ_REMOVE(&vnode_free_list, vp, v_freelist);
+ vp->v_usecount++;
+ if (lockflag)
+ VOP_LOCK(vp);
+ return (0);
+}
+
+/*
+ * Vnode reference, just increment the count
+ */
+void
+vref(vp)
+ struct vnode *vp;
+{
+
+ if (vp->v_usecount <= 0)
+ panic("vref used where vget required");
+ vp->v_usecount++;
+}
+
+/*
+ * vput(), just unlock and vrele()
+ */
+void
+vput(vp)
+ register struct vnode *vp;
+{
+
+ VOP_UNLOCK(vp);
+ vrele(vp);
+}
+
+/*
+ * Vnode release.
+ * If count drops to zero, call inactive routine and return to freelist.
+ */
+void
+vrele(vp)
+ register struct vnode *vp;
+{
+
+#ifdef DIAGNOSTIC
+ if (vp == NULL)
+ panic("vrele: null vp");
+#endif
+ vp->v_usecount--;
+ if (vp->v_usecount > 0)
+ return;
+#ifdef DIAGNOSTIC
+ if (vp->v_usecount != 0 || vp->v_writecount != 0) {
+ vprint("vrele: bad ref count", vp);
+ panic("vrele: ref cnt");
+ }
+#endif
+ /*
+ * insert at tail of LRU list
+ */
+ TAILQ_INSERT_TAIL(&vnode_free_list, vp, v_freelist);
+ VOP_INACTIVE(vp);
+}
+
+/*
+ * Page or buffer structure gets a reference.
+ */
+void
+vhold(vp)
+ register struct vnode *vp;
+{
+
+ vp->v_holdcnt++;
+}
+
+/*
+ * Page or buffer structure frees a reference.
+ */
+void
+holdrele(vp)
+ register struct vnode *vp;
+{
+
+ if (vp->v_holdcnt <= 0)
+ panic("holdrele: holdcnt");
+ vp->v_holdcnt--;
+}
+
+/*
+ * Remove any vnodes in the vnode table belonging to mount point mp.
+ *
+ * If MNT_NOFORCE is specified, there should not be any active ones,
+ * return error if any are found (nb: this is a user error, not a
+ * system error). If MNT_FORCE is specified, detach any active vnodes
+ * that are found.
+ */
+#ifdef DIAGNOSTIC
+int busyprt = 0; /* print out busy vnodes */
+struct ctldebug debug1 = { "busyprt", &busyprt };
+#endif
+
+int
+vflush(mp, skipvp, flags)
+ struct mount *mp;
+ struct vnode *skipvp;
+ int flags;
+{
+ register struct vnode *vp, *nvp;
+ int busy = 0;
+
+ if ((mp->mnt_flag & MNT_MPBUSY) == 0)
+ panic("vflush: not busy");
+loop:
+ for (vp = mp->mnt_vnodelist.lh_first; vp; vp = nvp) {
+ if (vp->v_mount != mp)
+ goto loop;
+ nvp = vp->v_mntvnodes.le_next;
+ /*
+ * Skip over a selected vnode.
+ */
+ if (vp == skipvp)
+ continue;
+ /*
+ * Skip over a vnodes marked VSYSTEM.
+ */
+ if ((flags & SKIPSYSTEM) && (vp->v_flag & VSYSTEM))
+ continue;
+ /*
+ * If WRITECLOSE is set, only flush out regular file
+ * vnodes open for writing.
+ */
+ if ((flags & WRITECLOSE) &&
+ (vp->v_writecount == 0 || vp->v_type != VREG))
+ continue;
+ /*
+ * With v_usecount == 0, all we need to do is clear
+ * out the vnode data structures and we are done.
+ */
+ if (vp->v_usecount == 0) {
+ vgone(vp);
+ continue;
+ }
+ /*
+ * If FORCECLOSE is set, forcibly close the vnode.
+ * For block or character devices, revert to an
+ * anonymous device. For all other files, just kill them.
+ */
+ if (flags & FORCECLOSE) {
+ if (vp->v_type != VBLK && vp->v_type != VCHR) {
+ vgone(vp);
+ } else {
+ vclean(vp, 0);
+ vp->v_op = spec_vnodeop_p;
+ insmntque(vp, (struct mount *)0);
+ }
+ continue;
+ }
+#ifdef DIAGNOSTIC
+ if (busyprt)
+ vprint("vflush: busy vnode", vp);
+#endif
+ busy++;
+ }
+ if (busy)
+ return (EBUSY);
+ return (0);
+}
+
+/*
+ * Disassociate the underlying file system from a vnode.
+ */
+void
+vclean(vp, flags)
+ register struct vnode *vp;
+ int flags;
+{
+ int active;
+
+ /*
+ * Check to see if the vnode is in use.
+ * If so we have to reference it before we clean it out
+ * so that its count cannot fall to zero and generate a
+ * race against ourselves to recycle it.
+ */
+ if (active = vp->v_usecount)
+ VREF(vp);
+ /*
+ * Even if the count is zero, the VOP_INACTIVE routine may still
+ * have the object locked while it cleans it out. The VOP_LOCK
+ * ensures that the VOP_INACTIVE routine is done with its work.
+ * For active vnodes, it ensures that no other activity can
+ * occur while the underlying object is being cleaned out.
+ */
+ VOP_LOCK(vp);
+ /*
+ * Prevent the vnode from being recycled or
+ * brought into use while we clean it out.
+ */
+ if (vp->v_flag & VXLOCK)
+ panic("vclean: deadlock");
+ vp->v_flag |= VXLOCK;
+ /*
+ * Clean out any buffers associated with the vnode.
+ */
+ if (flags & DOCLOSE)
+ vinvalbuf(vp, V_SAVE, NOCRED, NULL, 0, 0);
+ /*
+ * Any other processes trying to obtain this lock must first
+ * wait for VXLOCK to clear, then call the new lock operation.
+ */
+ VOP_UNLOCK(vp);
+ /*
+ * If purging an active vnode, it must be closed and
+ * deactivated before being reclaimed.
+ */
+ if (active) {
+ if (flags & DOCLOSE)
+ VOP_CLOSE(vp, IO_NDELAY, NOCRED, NULL);
+ VOP_INACTIVE(vp);
+ }
+ /*
+ * Reclaim the vnode.
+ */
+ if (VOP_RECLAIM(vp))
+ panic("vclean: cannot reclaim");
+ if (active)
+ vrele(vp);
+
+ /*
+ * Done with purge, notify sleepers of the grim news.
+ */
+ vp->v_op = dead_vnodeop_p;
+ vp->v_tag = VT_NON;
+ vp->v_flag &= ~VXLOCK;
+ if (vp->v_flag & VXWANT) {
+ vp->v_flag &= ~VXWANT;
+ wakeup((caddr_t)vp);
+ }
+}
+
+/*
+ * Eliminate all activity associated with the requested vnode
+ * and with all vnodes aliased to the requested vnode.
+ */
+void
+vgoneall(vp)
+ register struct vnode *vp;
+{
+ register struct vnode *vq;
+
+ if (vp->v_flag & VALIASED) {
+ /*
+ * If a vgone (or vclean) is already in progress,
+ * wait until it is done and return.
+ */
+ if (vp->v_flag & VXLOCK) {
+ vp->v_flag |= VXWANT;
+ sleep((caddr_t)vp, PINOD);
+ return;
+ }
+ /*
+ * Ensure that vp will not be vgone'd while we
+ * are eliminating its aliases.
+ */
+ vp->v_flag |= VXLOCK;
+ while (vp->v_flag & VALIASED) {
+ for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) {
+ if (vq->v_rdev != vp->v_rdev ||
+ vq->v_type != vp->v_type || vp == vq)
+ continue;
+ vgone(vq);
+ break;
+ }
+ }
+ /*
+ * Remove the lock so that vgone below will
+ * really eliminate the vnode after which time
+ * vgone will awaken any sleepers.
+ */
+ vp->v_flag &= ~VXLOCK;
+ }
+ vgone(vp);
+}
+
+/*
+ * Eliminate all activity associated with a vnode
+ * in preparation for reuse.
+ */
+void
+vgone(vp)
+ register struct vnode *vp;
+{
+ register struct vnode *vq;
+ struct vnode *vx;
+
+ /*
+ * If a vgone (or vclean) is already in progress,
+ * wait until it is done and return.
+ */
+ if (vp->v_flag & VXLOCK) {
+ vp->v_flag |= VXWANT;
+ sleep((caddr_t)vp, PINOD);
+ return;
+ }
+ /*
+ * Clean out the filesystem specific data.
+ */
+ vclean(vp, DOCLOSE);
+ /*
+ * Delete from old mount point vnode list, if on one.
+ */
+ if (vp->v_mount != NULL) {
+ LIST_REMOVE(vp, v_mntvnodes);
+ vp->v_mount = NULL;
+ }
+ /*
+ * If special device, remove it from special device alias list.
+ */
+ if (vp->v_type == VBLK || vp->v_type == VCHR) {
+ if (*vp->v_hashchain == vp) {
+ *vp->v_hashchain = vp->v_specnext;
+ } else {
+ for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) {
+ if (vq->v_specnext != vp)
+ continue;
+ vq->v_specnext = vp->v_specnext;
+ break;
+ }
+ if (vq == NULL)
+ panic("missing bdev");
+ }
+ if (vp->v_flag & VALIASED) {
+ vx = NULL;
+ for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) {
+ if (vq->v_rdev != vp->v_rdev ||
+ vq->v_type != vp->v_type)
+ continue;
+ if (vx)
+ break;
+ vx = vq;
+ }
+ if (vx == NULL)
+ panic("missing alias");
+ if (vq == NULL)
+ vx->v_flag &= ~VALIASED;
+ vp->v_flag &= ~VALIASED;
+ }
+ FREE(vp->v_specinfo, M_VNODE);
+ vp->v_specinfo = NULL;
+ }
+ /*
+ * If it is on the freelist and not already at the head,
+ * move it to the head of the list. The test of the back
+ * pointer and the reference count of zero is because
+ * it will be removed from the free list by getnewvnode,
+ * but will not have its reference count incremented until
+ * after calling vgone. If the reference count were
+ * incremented first, vgone would (incorrectly) try to
+ * close the previous instance of the underlying object.
+ * So, the back pointer is explicitly set to `0xdeadb' in
+ * getnewvnode after removing it from the freelist to ensure
+ * that we do not try to move it here.
+ */
+ if (vp->v_usecount == 0 &&
+ vp->v_freelist.tqe_prev != (struct vnode **)0xdeadb &&
+ vnode_free_list.tqh_first != vp) {
+ TAILQ_REMOVE(&vnode_free_list, vp, v_freelist);
+ TAILQ_INSERT_HEAD(&vnode_free_list, vp, v_freelist);
+ }
+ vp->v_type = VBAD;
+}
+
+/*
+ * Lookup a vnode by device number.
+ */
+int
+vfinddev(dev, type, vpp)
+ dev_t dev;
+ enum vtype type;
+ struct vnode **vpp;
+{
+ register struct vnode *vp;
+
+ for (vp = speclisth[SPECHASH(dev)]; vp; vp = vp->v_specnext) {
+ if (dev != vp->v_rdev || type != vp->v_type)
+ continue;
+ *vpp = vp;
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Calculate the total number of references to a special device.
+ */
+int
+vcount(vp)
+ register struct vnode *vp;
+{
+ register struct vnode *vq, *vnext;
+ int count;
+
+loop:
+ if ((vp->v_flag & VALIASED) == 0)
+ return (vp->v_usecount);
+ for (count = 0, vq = *vp->v_hashchain; vq; vq = vnext) {
+ vnext = vq->v_specnext;
+ if (vq->v_rdev != vp->v_rdev || vq->v_type != vp->v_type)
+ continue;
+ /*
+ * Alias, but not in use, so flush it out.
+ */
+ if (vq->v_usecount == 0 && vq != vp) {
+ vgone(vq);
+ goto loop;
+ }
+ count += vq->v_usecount;
+ }
+ return (count);
+}
+
+/*
+ * Print out a description of a vnode.
+ */
+static char *typename[] =
+ { "VNON", "VREG", "VDIR", "VBLK", "VCHR", "VLNK", "VSOCK", "VFIFO", "VBAD" };
+
+void
+vprint(label, vp)
+ char *label;
+ register struct vnode *vp;
+{
+ char buf[64];
+
+ if (label != NULL)
+ printf("%s: ", label);
+ printf("type %s, usecount %d, writecount %d, refcount %d,",
+ typename[vp->v_type], vp->v_usecount, vp->v_writecount,
+ vp->v_holdcnt);
+ buf[0] = '\0';
+ if (vp->v_flag & VROOT)
+ strcat(buf, "|VROOT");
+ if (vp->v_flag & VTEXT)
+ strcat(buf, "|VTEXT");
+ if (vp->v_flag & VSYSTEM)
+ strcat(buf, "|VSYSTEM");
+ if (vp->v_flag & VXLOCK)
+ strcat(buf, "|VXLOCK");
+ if (vp->v_flag & VXWANT)
+ strcat(buf, "|VXWANT");
+ if (vp->v_flag & VBWAIT)
+ strcat(buf, "|VBWAIT");
+ if (vp->v_flag & VALIASED)
+ strcat(buf, "|VALIASED");
+ if (buf[0] != '\0')
+ printf(" flags (%s)", &buf[1]);
+ if (vp->v_data == NULL) {
+ printf("\n");
+ } else {
+ printf("\n\t");
+ VOP_PRINT(vp);
+ }
+}
+
+#ifdef DEBUG
+/*
+ * List all of the locked vnodes in the system.
+ * Called when debugging the kernel.
+ */
+void
+printlockedvnodes()
+{
+ register struct mount *mp;
+ register struct vnode *vp;
+
+ printf("Locked vnodes\n");
+ for (mp = mountlist.tqh_first; mp != NULL; mp = mp->mnt_list.tqe_next) {
+ for (vp = mp->mnt_vnodelist.lh_first;
+ vp != NULL;
+ vp = vp->v_mntvnodes.le_next)
+ if (VOP_ISLOCKED(vp))
+ vprint((char *)0, vp);
+ }
+}
+#endif
+
+int kinfo_vdebug = 1;
+int kinfo_vgetfailed;
+#define KINFO_VNODESLOP 10
+/*
+ * Dump vnode list (via sysctl).
+ * Copyout address of vnode followed by vnode.
+ */
+/* ARGSUSED */
+int
+sysctl_vnode(where, sizep)
+ char *where;
+ size_t *sizep;
+{
+ register struct mount *mp, *nmp;
+ struct vnode *vp;
+ register char *bp = where, *savebp;
+ char *ewhere;
+ int error;
+
+#define VPTRSZ sizeof (struct vnode *)
+#define VNODESZ sizeof (struct vnode)
+ if (where == NULL) {
+ *sizep = (numvnodes + KINFO_VNODESLOP) * (VPTRSZ + VNODESZ);
+ return (0);
+ }
+ ewhere = where + *sizep;
+
+ for (mp = mountlist.tqh_first; mp != NULL; mp = nmp) {
+ nmp = mp->mnt_list.tqe_next;
+ if (vfs_busy(mp))
+ continue;
+ savebp = bp;
+again:
+ for (vp = mp->mnt_vnodelist.lh_first;
+ vp != NULL;
+ vp = vp->v_mntvnodes.le_next) {
+ /*
+ * Check that the vp is still associated with
+ * this filesystem. RACE: could have been
+ * recycled onto the same filesystem.
+ */
+ if (vp->v_mount != mp) {
+ if (kinfo_vdebug)
+ printf("kinfo: vp changed\n");
+ bp = savebp;
+ goto again;
+ }
+ if (bp + VPTRSZ + VNODESZ > ewhere) {
+ *sizep = bp - where;
+ return (ENOMEM);
+ }
+ if ((error = copyout((caddr_t)&vp, bp, VPTRSZ)) ||
+ (error = copyout((caddr_t)vp, bp + VPTRSZ, VNODESZ)))
+ return (error);
+ bp += VPTRSZ + VNODESZ;
+ }
+ vfs_unbusy(mp);
+ }
+
+ *sizep = bp - where;
+ return (0);
+}
+
+/*
+ * Check to see if a filesystem is mounted on a block device.
+ */
+int
+vfs_mountedon(vp)
+ register struct vnode *vp;
+{
+ register struct vnode *vq;
+
+ if (vp->v_specflags & SI_MOUNTEDON)
+ return (EBUSY);
+ if (vp->v_flag & VALIASED) {
+ for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) {
+ if (vq->v_rdev != vp->v_rdev ||
+ vq->v_type != vp->v_type)
+ continue;
+ if (vq->v_specflags & SI_MOUNTEDON)
+ return (EBUSY);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Build hash lists of net addresses and hang them off the mount point.
+ * Called by ufs_mount() to set up the lists of export addresses.
+ */
+static int
+vfs_hang_addrlist(mp, nep, argp)
+ struct mount *mp;
+ struct netexport *nep;
+ struct export_args *argp;
+{
+ register struct netcred *np;
+ register struct radix_node_head *rnh;
+ register int i;
+ struct radix_node *rn;
+ struct sockaddr *saddr, *smask = 0;
+ struct domain *dom;
+ int error;
+
+ if (argp->ex_addrlen == 0) {
+ if (mp->mnt_flag & MNT_DEFEXPORTED)
+ return (EPERM);
+ np = &nep->ne_defexported;
+ np->netc_exflags = argp->ex_flags;
+ np->netc_anon = argp->ex_anon;
+ np->netc_anon.cr_ref = 1;
+ mp->mnt_flag |= MNT_DEFEXPORTED;
+ return (0);
+ }
+ i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen;
+ np = (struct netcred *)malloc(i, M_NETADDR, M_WAITOK);
+ bzero((caddr_t)np, i);
+ saddr = (struct sockaddr *)(np + 1);
+ if (error = copyin(argp->ex_addr, (caddr_t)saddr, argp->ex_addrlen))
+ goto out;
+ if (saddr->sa_len > argp->ex_addrlen)
+ saddr->sa_len = argp->ex_addrlen;
+ if (argp->ex_masklen) {
+ smask = (struct sockaddr *)((caddr_t)saddr + argp->ex_addrlen);
+ error = copyin(argp->ex_addr, (caddr_t)smask, argp->ex_masklen);
+ if (error)
+ goto out;
+ if (smask->sa_len > argp->ex_masklen)
+ smask->sa_len = argp->ex_masklen;
+ }
+ i = saddr->sa_family;
+ if ((rnh = nep->ne_rtable[i]) == 0) {
+ /*
+ * Seems silly to initialize every AF when most are not
+ * used, do so on demand here
+ */
+ for (dom = domains; dom; dom = dom->dom_next)
+ if (dom->dom_family == i && dom->dom_rtattach) {
+ dom->dom_rtattach((void **)&nep->ne_rtable[i],
+ dom->dom_rtoffset);
+ break;
+ }
+ if ((rnh = nep->ne_rtable[i]) == 0) {
+ error = ENOBUFS;
+ goto out;
+ }
+ }
+ rn = (*rnh->rnh_addaddr)((caddr_t)saddr, (caddr_t)smask, rnh,
+ np->netc_rnodes);
+ if (rn == 0 || np != (struct netcred *)rn) { /* already exists */
+ error = EPERM;
+ goto out;
+ }
+ np->netc_exflags = argp->ex_flags;
+ np->netc_anon = argp->ex_anon;
+ np->netc_anon.cr_ref = 1;
+ return (0);
+out:
+ free(np, M_NETADDR);
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+vfs_free_netcred(rn, w)
+ struct radix_node *rn;
+ caddr_t w;
+{
+ register struct radix_node_head *rnh = (struct radix_node_head *)w;
+
+ (*rnh->rnh_deladdr)(rn->rn_key, rn->rn_mask, rnh);
+ free((caddr_t)rn, M_NETADDR);
+ return (0);
+}
+
+/*
+ * Free the net address hash lists that are hanging off the mount points.
+ */
+static void
+vfs_free_addrlist(nep)
+ struct netexport *nep;
+{
+ register int i;
+ register struct radix_node_head *rnh;
+
+ for (i = 0; i <= AF_MAX; i++)
+ if (rnh = nep->ne_rtable[i]) {
+ (*rnh->rnh_walktree)(rnh, vfs_free_netcred,
+ (caddr_t)rnh);
+ free((caddr_t)rnh, M_RTABLE);
+ nep->ne_rtable[i] = 0;
+ }
+}
+
+int
+vfs_export(mp, nep, argp)
+ struct mount *mp;
+ struct netexport *nep;
+ struct export_args *argp;
+{
+ int error;
+
+ if (argp->ex_flags & MNT_DELEXPORT) {
+ vfs_free_addrlist(nep);
+ mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED);
+ }
+ if (argp->ex_flags & MNT_EXPORTED) {
+ if (error = vfs_hang_addrlist(mp, nep, argp))
+ return (error);
+ mp->mnt_flag |= MNT_EXPORTED;
+ }
+ return (0);
+}
+
+struct netcred *
+vfs_export_lookup(mp, nep, nam)
+ register struct mount *mp;
+ struct netexport *nep;
+ struct mbuf *nam;
+{
+ register struct netcred *np;
+ register struct radix_node_head *rnh;
+ struct sockaddr *saddr;
+
+ np = NULL;
+ if (mp->mnt_flag & MNT_EXPORTED) {
+ /*
+ * Lookup in the export list first.
+ */
+ if (nam != NULL) {
+ saddr = mtod(nam, struct sockaddr *);
+ rnh = nep->ne_rtable[saddr->sa_family];
+ if (rnh != NULL) {
+ np = (struct netcred *)
+ (*rnh->rnh_matchaddr)((caddr_t)saddr,
+ rnh);
+ if (np && np->netc_rnodes->rn_flags & RNF_ROOT)
+ np = NULL;
+ }
+ }
+ /*
+ * If no address match, use the default if it exists.
+ */
+ if (np == NULL && mp->mnt_flag & MNT_DEFEXPORTED)
+ np = &nep->ne_defexported;
+ }
+ return (np);
+}
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
new file mode 100644
index 0000000..f5c3d78
--- /dev/null
+++ b/sys/kern/vfs_syscalls.c
@@ -0,0 +1,2155 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/filedesc.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+
+#include <vm/vm.h>
+#include <sys/sysctl.h>
+
+void cvtstat __P((struct stat *, struct ostat *));
+static int change_dir __P((struct nameidata *ndp, struct proc *p));
+
+/*
+ * Virtual File System System Calls
+ */
+
+/*
+ * Mount a file system.
+ */
+struct mount_args {
+ int type;
+ char *path;
+ int flags;
+ caddr_t data;
+};
+/* ARGSUSED */
+int
+mount(p, uap, retval)
+ struct proc *p;
+ register struct mount_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ register struct mount *mp;
+ int error, flag = 0;
+ struct nameidata nd;
+
+ /*
+ * Must be super user
+ */
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ /*
+ * Get vnode to be covered
+ */
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (uap->flags & MNT_UPDATE) {
+ if ((vp->v_flag & VROOT) == 0) {
+ vput(vp);
+ return (EINVAL);
+ }
+ mp = vp->v_mount;
+ flag = mp->mnt_flag;
+ /*
+ * We only allow the filesystem to be reloaded if it
+ * is currently mounted read-only.
+ */
+ if ((uap->flags & MNT_RELOAD) &&
+ ((mp->mnt_flag & MNT_RDONLY) == 0)) {
+ vput(vp);
+ return (EOPNOTSUPP); /* Needs translation */
+ }
+ mp->mnt_flag |=
+ uap->flags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE);
+ VOP_UNLOCK(vp);
+ goto update;
+ }
+ if (error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0))
+ return (error);
+ if (vp->v_type != VDIR) {
+ vput(vp);
+ return (ENOTDIR);
+ }
+ if ((u_long)uap->type > MOUNT_MAXTYPE || vfssw[uap->type] == NULL) {
+ vput(vp);
+ return (ENODEV);
+ }
+
+ /*
+ * Allocate and initialize the file system.
+ */
+ mp = (struct mount *)malloc((u_long)sizeof(struct mount),
+ M_MOUNT, M_WAITOK);
+ bzero((char *)mp, (u_long)sizeof(struct mount));
+ mp->mnt_op = vfssw[uap->type];
+ if (error = vfs_lock(mp)) {
+ free((caddr_t)mp, M_MOUNT);
+ vput(vp);
+ return (error);
+ }
+ if (vp->v_mountedhere != NULL) {
+ vfs_unlock(mp);
+ free((caddr_t)mp, M_MOUNT);
+ vput(vp);
+ return (EBUSY);
+ }
+ vp->v_mountedhere = mp;
+ mp->mnt_vnodecovered = vp;
+update:
+ /*
+ * Set the mount level flags.
+ */
+ if (uap->flags & MNT_RDONLY)
+ mp->mnt_flag |= MNT_RDONLY;
+ else if (mp->mnt_flag & MNT_RDONLY)
+ mp->mnt_flag |= MNT_WANTRDWR;
+ mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV |
+ MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC);
+ mp->mnt_flag |= uap->flags & (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV |
+ MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC);
+ /*
+ * Mount the filesystem.
+ */
+ error = VFS_MOUNT(mp, uap->path, uap->data, &nd, p);
+ if (mp->mnt_flag & MNT_UPDATE) {
+ vrele(vp);
+ if (mp->mnt_flag & MNT_WANTRDWR)
+ mp->mnt_flag &= ~MNT_RDONLY;
+ mp->mnt_flag &=~
+ (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_WANTRDWR);
+ if (error)
+ mp->mnt_flag = flag;
+ return (error);
+ }
+ /*
+ * Put the new filesystem on the mount list after root.
+ */
+ cache_purge(vp);
+ if (!error) {
+ TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
+ VOP_UNLOCK(vp);
+ vfs_unlock(mp);
+ error = VFS_START(mp, 0, p);
+ } else {
+ mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0;
+ vfs_unlock(mp);
+ free((caddr_t)mp, M_MOUNT);
+ vput(vp);
+ }
+ return (error);
+}
+
+/*
+ * Unmount a file system.
+ *
+ * Note: unmount takes a path to the vnode mounted on as argument,
+ * not special file (as before).
+ */
+struct unmount_args {
+ char *path;
+ int flags;
+};
+/* ARGSUSED */
+int
+unmount(p, uap, retval)
+ struct proc *p;
+ register struct unmount_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct mount *mp;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+
+ /*
+ * Unless this is a user mount, then must
+ * have suser privilege.
+ */
+ if (((vp->v_mount->mnt_flag & MNT_USER) == 0) &&
+ (error = suser(p->p_ucred, &p->p_acflag))) {
+ vput(vp);
+ return (error);
+ }
+
+ /*
+ * Must be the root of the filesystem
+ */
+ if ((vp->v_flag & VROOT) == 0) {
+ vput(vp);
+ return (EINVAL);
+ }
+ mp = vp->v_mount;
+ vput(vp);
+ return (dounmount(mp, uap->flags, p));
+}
+
+/*
+ * Do the actual file system unmount.
+ */
+int
+dounmount(mp, flags, p)
+ register struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ struct vnode *coveredvp;
+ int error;
+
+ coveredvp = mp->mnt_vnodecovered;
+ if (vfs_busy(mp))
+ return (EBUSY);
+ mp->mnt_flag |= MNT_UNMOUNT;
+ if (error = vfs_lock(mp))
+ return (error);
+
+ mp->mnt_flag &=~ MNT_ASYNC;
+ vnode_pager_umount(mp); /* release cached vnodes */
+ cache_purgevfs(mp); /* remove cache entries for this file sys */
+ if ((error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0 ||
+ (flags & MNT_FORCE))
+ error = VFS_UNMOUNT(mp, flags, p);
+ mp->mnt_flag &= ~MNT_UNMOUNT;
+ vfs_unbusy(mp);
+ if (error) {
+ vfs_unlock(mp);
+ } else {
+ vrele(coveredvp);
+ TAILQ_REMOVE(&mountlist, mp, mnt_list);
+ mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0;
+ vfs_unlock(mp);
+ if (mp->mnt_vnodelist.lh_first != NULL)
+ panic("unmount: dangling vnode");
+ free((caddr_t)mp, M_MOUNT);
+ }
+ return (error);
+}
+
+/*
+ * Sync each mounted filesystem.
+ */
+#ifdef DIAGNOSTIC
+int syncprt = 0;
+struct ctldebug debug0 = { "syncprt", &syncprt };
+#endif
+
+struct sync_args {
+ int dummy;
+};
+/* ARGSUSED */
+int
+sync(p, uap, retval)
+ struct proc *p;
+ struct sync_args *uap;
+ int *retval;
+{
+ register struct mount *mp, *nmp;
+ int asyncflag;
+
+ for (mp = mountlist.tqh_first; mp != NULL; mp = nmp) {
+ nmp = mp->mnt_list.tqe_next;
+ /*
+ * The lock check below is to avoid races with mount
+ * and unmount.
+ */
+ if ((mp->mnt_flag & (MNT_MLOCK|MNT_RDONLY|MNT_MPBUSY)) == 0 &&
+ !vfs_busy(mp)) {
+ asyncflag = mp->mnt_flag & MNT_ASYNC;
+ mp->mnt_flag &= ~MNT_ASYNC;
+ VFS_SYNC(mp, MNT_NOWAIT, p->p_ucred, p);
+ if (asyncflag)
+ mp->mnt_flag |= MNT_ASYNC;
+ vfs_unbusy(mp);
+ }
+ }
+#ifdef DIAGNOSTIC
+ if (syncprt)
+ vfs_bufstats();
+#endif /* DIAGNOSTIC */
+ return (0);
+}
+
+/*
+ * Change filesystem quotas.
+ */
+struct quotactl_args {
+ char *path;
+ int cmd;
+ int uid;
+ caddr_t arg;
+};
+/* ARGSUSED */
+int
+quotactl(p, uap, retval)
+ struct proc *p;
+ register struct quotactl_args *uap;
+ int *retval;
+{
+ register struct mount *mp;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ mp = nd.ni_vp->v_mount;
+ vrele(nd.ni_vp);
+ return (VFS_QUOTACTL(mp, uap->cmd, uap->uid, uap->arg, p));
+}
+
+/*
+ * Get filesystem statistics.
+ */
+struct statfs_args {
+ char *path;
+ struct statfs *buf;
+};
+/* ARGSUSED */
+int
+statfs(p, uap, retval)
+ struct proc *p;
+ register struct statfs_args *uap;
+ int *retval;
+{
+ register struct mount *mp;
+ register struct statfs *sp;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ mp = nd.ni_vp->v_mount;
+ sp = &mp->mnt_stat;
+ vrele(nd.ni_vp);
+ if (error = VFS_STATFS(mp, sp, p))
+ return (error);
+ sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
+ return (copyout((caddr_t)sp, (caddr_t)uap->buf, sizeof(*sp)));
+}
+
+/*
+ * Get filesystem statistics.
+ */
+struct fstatfs_args {
+ int fd;
+ struct statfs *buf;
+};
+/* ARGSUSED */
+int
+fstatfs(p, uap, retval)
+ struct proc *p;
+ register struct fstatfs_args *uap;
+ int *retval;
+{
+ struct file *fp;
+ struct mount *mp;
+ register struct statfs *sp;
+ int error;
+
+ if (error = getvnode(p->p_fd, uap->fd, &fp))
+ return (error);
+ mp = ((struct vnode *)fp->f_data)->v_mount;
+ sp = &mp->mnt_stat;
+ if (error = VFS_STATFS(mp, sp, p))
+ return (error);
+ sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
+ return (copyout((caddr_t)sp, (caddr_t)uap->buf, sizeof(*sp)));
+}
+
+/*
+ * Get statistics on all filesystems.
+ */
+struct getfsstat_args {
+ struct statfs *buf;
+ long bufsize;
+ int flags;
+};
+int
+getfsstat(p, uap, retval)
+ struct proc *p;
+ register struct getfsstat_args *uap;
+ int *retval;
+{
+ register struct mount *mp, *nmp;
+ register struct statfs *sp;
+ caddr_t sfsp;
+ long count, maxcount, error;
+
+ maxcount = uap->bufsize / sizeof(struct statfs);
+ sfsp = (caddr_t)uap->buf;
+ for (count = 0, mp = mountlist.tqh_first; mp != NULL; mp = nmp) {
+ nmp = mp->mnt_list.tqe_next;
+ if (sfsp && count < maxcount &&
+ ((mp->mnt_flag & MNT_MLOCK) == 0)) {
+ sp = &mp->mnt_stat;
+ /*
+ * If MNT_NOWAIT is specified, do not refresh the
+ * fsstat cache. MNT_WAIT overrides MNT_NOWAIT.
+ */
+ if (((uap->flags & MNT_NOWAIT) == 0 ||
+ (uap->flags & MNT_WAIT)) &&
+ (error = VFS_STATFS(mp, sp, p)))
+ continue;
+ sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
+ if (error = copyout((caddr_t)sp, sfsp, sizeof(*sp)))
+ return (error);
+ sfsp += sizeof(*sp);
+ }
+ count++;
+ }
+ if (sfsp && count > maxcount)
+ *retval = maxcount;
+ else
+ *retval = count;
+ return (0);
+}
+
+/*
+ * Change current working directory to a given file descriptor.
+ */
+struct fchdir_args {
+ int fd;
+};
+/* ARGSUSED */
+int
+fchdir(p, uap, retval)
+ struct proc *p;
+ struct fchdir_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ register struct vnode *vp;
+ struct file *fp;
+ int error;
+
+ if (error = getvnode(fdp, uap->fd, &fp))
+ return (error);
+ vp = (struct vnode *)fp->f_data;
+ VOP_LOCK(vp);
+ if (vp->v_type != VDIR)
+ error = ENOTDIR;
+ else
+ error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p);
+ VOP_UNLOCK(vp);
+ if (error)
+ return (error);
+ VREF(vp);
+ vrele(fdp->fd_cdir);
+ fdp->fd_cdir = vp;
+ return (0);
+}
+
+/*
+ * Change current working directory (``.'').
+ */
+struct chdir_args {
+ char *path;
+};
+/* ARGSUSED */
+int
+chdir(p, uap, retval)
+ struct proc *p;
+ struct chdir_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = change_dir(&nd, p))
+ return (error);
+ vrele(fdp->fd_cdir);
+ fdp->fd_cdir = nd.ni_vp;
+ return (0);
+}
+
+/*
+ * Change notion of root (``/'') directory.
+ */
+struct chroot_args {
+ char *path;
+};
+/* ARGSUSED */
+int
+chroot(p, uap, retval)
+ struct proc *p;
+ struct chroot_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ int error;
+ struct nameidata nd;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = change_dir(&nd, p))
+ return (error);
+ if (fdp->fd_rdir != NULL)
+ vrele(fdp->fd_rdir);
+ fdp->fd_rdir = nd.ni_vp;
+ return (0);
+}
+
+/*
+ * Common routine for chroot and chdir.
+ */
+static int
+change_dir(ndp, p)
+ register struct nameidata *ndp;
+ struct proc *p;
+{
+ struct vnode *vp;
+ int error;
+
+ if (error = namei(ndp))
+ return (error);
+ vp = ndp->ni_vp;
+ if (vp->v_type != VDIR)
+ error = ENOTDIR;
+ else
+ error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p);
+ VOP_UNLOCK(vp);
+ if (error)
+ vrele(vp);
+ return (error);
+}
+
+/*
+ * Check permissions, allocate an open file structure,
+ * and call the device open routine if any.
+ */
+struct open_args {
+ char *path;
+ int flags;
+ int mode;
+};
+int
+open(p, uap, retval)
+ struct proc *p;
+ register struct open_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ register struct file *fp;
+ register struct vnode *vp;
+ int flags, cmode;
+ struct file *nfp;
+ int type, indx, error;
+ struct flock lf;
+ struct nameidata nd;
+ extern struct fileops vnops;
+
+ if (error = falloc(p, &nfp, &indx))
+ return (error);
+ fp = nfp;
+ flags = FFLAGS(uap->flags);
+ cmode = ((uap->mode &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT;
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ p->p_dupfd = -indx - 1; /* XXX check for fdopen */
+ if (error = vn_open(&nd, flags, cmode)) {
+ ffree(fp);
+ if ((error == ENODEV || error == ENXIO) &&
+ p->p_dupfd >= 0 && /* XXX from fdopen */
+ (error =
+ dupfdopen(fdp, indx, p->p_dupfd, flags, error)) == 0) {
+ *retval = indx;
+ return (0);
+ }
+ if (error == ERESTART)
+ error = EINTR;
+ fdp->fd_ofiles[indx] = NULL;
+ return (error);
+ }
+ p->p_dupfd = 0;
+ vp = nd.ni_vp;
+ fp->f_flag = flags & FMASK;
+ fp->f_type = DTYPE_VNODE;
+ fp->f_ops = &vnops;
+ fp->f_data = (caddr_t)vp;
+ if (flags & (O_EXLOCK | O_SHLOCK)) {
+ lf.l_whence = SEEK_SET;
+ lf.l_start = 0;
+ lf.l_len = 0;
+ if (flags & O_EXLOCK)
+ lf.l_type = F_WRLCK;
+ else
+ lf.l_type = F_RDLCK;
+ type = F_FLOCK;
+ if ((flags & FNONBLOCK) == 0)
+ type |= F_WAIT;
+ VOP_UNLOCK(vp);
+ if (error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) {
+ (void) vn_close(vp, fp->f_flag, fp->f_cred, p);
+ ffree(fp);
+ fdp->fd_ofiles[indx] = NULL;
+ return (error);
+ }
+ VOP_LOCK(vp);
+ fp->f_flag |= FHASLOCK;
+ }
+ VOP_UNLOCK(vp);
+ *retval = indx;
+ return (0);
+}
+
+#ifdef COMPAT_43
+/*
+ * Create a file.
+ */
+struct ocreat_args {
+ char *path;
+ int mode;
+};
+int
+ocreat(p, uap, retval)
+ struct proc *p;
+ register struct ocreat_args *uap;
+ int *retval;
+{
+ struct open_args openuap;
+
+ openuap.path = uap->path;
+ openuap.mode = uap->mode;
+ openuap.flags = O_WRONLY | O_CREAT | O_TRUNC;
+ return (open(p, &openuap, retval));
+}
+#endif /* COMPAT_43 */
+
+/*
+ * Create a special file.
+ */
+struct mknod_args {
+ char *path;
+ int mode;
+ int dev;
+};
+/* ARGSUSED */
+int
+mknod(p, uap, retval)
+ struct proc *p;
+ register struct mknod_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (vp != NULL)
+ error = EEXIST;
+ else {
+ VATTR_NULL(&vattr);
+ vattr.va_mode = (uap->mode & ALLPERMS) &~ p->p_fd->fd_cmask;
+ vattr.va_rdev = uap->dev;
+
+ switch (uap->mode & S_IFMT) {
+ case S_IFMT: /* used by badsect to flag bad sectors */
+ vattr.va_type = VBAD;
+ break;
+ case S_IFCHR:
+ vattr.va_type = VCHR;
+ break;
+ case S_IFBLK:
+ vattr.va_type = VBLK;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ }
+ if (!error) {
+ LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ if (vp)
+ vrele(vp);
+ }
+ return (error);
+}
+
+/*
+ * Create named pipe.
+ */
+struct mkfifo_args {
+ char *path;
+ int mode;
+};
+/* ARGSUSED */
+int
+mkfifo(p, uap, retval)
+ struct proc *p;
+ register struct mkfifo_args *uap;
+ int *retval;
+{
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+#ifndef FIFO
+ return (EOPNOTSUPP);
+#else
+ NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ if (nd.ni_vp != NULL) {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vrele(nd.ni_vp);
+ return (EEXIST);
+ }
+ VATTR_NULL(&vattr);
+ vattr.va_type = VFIFO;
+ vattr.va_mode = (uap->mode & ALLPERMS) &~ p->p_fd->fd_cmask;
+ LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ return (VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr));
+#endif /* FIFO */
+}
+
+/*
+ * Make a hard file link.
+ */
+struct link_args {
+ char *path;
+ char *link;
+};
+/* ARGSUSED */
+int
+link(p, uap, retval)
+ struct proc *p;
+ register struct link_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct nameidata nd;
+ int error;
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (vp->v_type != VDIR ||
+ (error = suser(p->p_ucred, &p->p_acflag)) == 0) {
+ nd.ni_cnd.cn_nameiop = CREATE;
+ nd.ni_cnd.cn_flags = LOCKPARENT;
+ nd.ni_dirp = uap->link;
+ if ((error = namei(&nd)) == 0) {
+ if (nd.ni_vp != NULL)
+ error = EEXIST;
+ if (!error) {
+ LEASE_CHECK(nd.ni_dvp,
+ p, p->p_ucred, LEASE_WRITE);
+ LEASE_CHECK(vp,
+ p, p->p_ucred, LEASE_WRITE);
+ error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ if (nd.ni_vp)
+ vrele(nd.ni_vp);
+ }
+ }
+ }
+ vrele(vp);
+ return (error);
+}
+
+/*
+ * Make a symbolic link.
+ */
+struct symlink_args {
+ char *path;
+ char *link;
+};
+/* ARGSUSED */
+int
+symlink(p, uap, retval)
+ struct proc *p;
+ register struct symlink_args *uap;
+ int *retval;
+{
+ struct vattr vattr;
+ char *path;
+ int error;
+ struct nameidata nd;
+
+ MALLOC(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
+ if (error = copyinstr(uap->path, path, MAXPATHLEN, NULL))
+ goto out;
+ NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->link, p);
+ if (error = namei(&nd))
+ goto out;
+ if (nd.ni_vp) {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vrele(nd.ni_vp);
+ error = EEXIST;
+ goto out;
+ }
+ VATTR_NULL(&vattr);
+ vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask;
+ LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path);
+out:
+ FREE(path, M_NAMEI);
+ return (error);
+}
+
+/*
+ * Delete a name from the filesystem.
+ */
+struct unlink_args {
+ char *path;
+};
+/* ARGSUSED */
+int
+unlink(p, uap, retval)
+ struct proc *p;
+ struct unlink_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, DELETE, LOCKPARENT, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+
+ if (vp->v_type != VDIR ||
+ (error = suser(p->p_ucred, &p->p_acflag)) == 0) {
+ /*
+ * The root of a mounted filesystem cannot be deleted.
+ */
+ if (vp->v_flag & VROOT)
+ error = EBUSY;
+ else
+ (void)vnode_pager_uncache(vp);
+ }
+
+ if (!error) {
+ LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vput(vp);
+ }
+ return (error);
+}
+
+/*
+ * Reposition read/write file offset.
+ */
+struct lseek_args {
+ int fd;
+ int pad;
+ off_t offset;
+ int whence;
+};
+int
+lseek(p, uap, retval)
+ struct proc *p;
+ register struct lseek_args *uap;
+ int *retval;
+{
+ struct ucred *cred = p->p_ucred;
+ register struct filedesc *fdp = p->p_fd;
+ register struct file *fp;
+ struct vattr vattr;
+ int error;
+
+ if ((u_int)uap->fd >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL)
+ return (EBADF);
+ if (fp->f_type != DTYPE_VNODE)
+ return (ESPIPE);
+ switch (uap->whence) {
+ case L_INCR:
+ fp->f_offset += uap->offset;
+ break;
+ case L_XTND:
+ if (error =
+ VOP_GETATTR((struct vnode *)fp->f_data, &vattr, cred, p))
+ return (error);
+ fp->f_offset = uap->offset + vattr.va_size;
+ break;
+ case L_SET:
+ fp->f_offset = uap->offset;
+ break;
+ default:
+ return (EINVAL);
+ }
+ *(off_t *)retval = fp->f_offset;
+ return (0);
+}
+
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+/*
+ * Reposition read/write file offset.
+ */
+struct olseek_args {
+ int fd;
+ long offset;
+ int whence;
+};
+int
+olseek(p, uap, retval)
+ struct proc *p;
+ register struct olseek_args *uap;
+ int *retval;
+{
+ struct lseek_args nuap;
+ off_t qret;
+ int error;
+
+ nuap.fd = uap->fd;
+ nuap.offset = uap->offset;
+ nuap.whence = uap->whence;
+ error = lseek(p, &nuap, &qret);
+ *(long *)retval = qret;
+ return (error);
+}
+#endif /* COMPAT_43 */
+
+/*
+ * Check access permissions.
+ */
+struct access_args {
+ char *path;
+ int flags;
+};
+int
+access(p, uap, retval)
+ struct proc *p;
+ register struct access_args *uap;
+ int *retval;
+{
+ register struct ucred *cred = p->p_ucred;
+ register struct vnode *vp;
+ int error, flags, t_gid, t_uid;
+ struct nameidata nd;
+
+ t_uid = cred->cr_uid;
+ t_gid = cred->cr_groups[0];
+ cred->cr_uid = p->p_cred->p_ruid;
+ cred->cr_groups[0] = p->p_cred->p_rgid;
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ goto out1;
+ vp = nd.ni_vp;
+
+ /* Flags == 0 means only check for existence. */
+ if (uap->flags) {
+ flags = 0;
+ if (uap->flags & R_OK)
+ flags |= VREAD;
+ if (uap->flags & W_OK)
+ flags |= VWRITE;
+ if (uap->flags & X_OK)
+ flags |= VEXEC;
+ if ((flags & VWRITE) == 0 || (error = vn_writechk(vp)) == 0)
+ error = VOP_ACCESS(vp, flags, cred, p);
+ }
+ vput(vp);
+out1:
+ cred->cr_uid = t_uid;
+ cred->cr_groups[0] = t_gid;
+ return (error);
+}
+
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+/*
+ * Get file status; this version follows links.
+ */
+struct ostat_args {
+ char *path;
+ struct ostat *ub;
+};
+/* ARGSUSED */
+int
+ostat(p, uap, retval)
+ struct proc *p;
+ register struct ostat_args *uap;
+ int *retval;
+{
+ struct stat sb;
+ struct ostat osb;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ error = vn_stat(nd.ni_vp, &sb, p);
+ vput(nd.ni_vp);
+ if (error)
+ return (error);
+ cvtstat(&sb, &osb);
+ error = copyout((caddr_t)&osb, (caddr_t)uap->ub, sizeof (osb));
+ return (error);
+}
+
+/*
+ * Get file status; this version does not follow links.
+ */
+struct olstat_args {
+ char *path;
+ struct ostat *ub;
+};
+/* ARGSUSED */
+int
+olstat(p, uap, retval)
+ struct proc *p;
+ register struct olstat_args *uap;
+ int *retval;
+{
+ struct stat sb;
+ struct ostat osb;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ error = vn_stat(nd.ni_vp, &sb, p);
+ vput(nd.ni_vp);
+ if (error)
+ return (error);
+ cvtstat(&sb, &osb);
+ error = copyout((caddr_t)&osb, (caddr_t)uap->ub, sizeof (osb));
+ return (error);
+}
+
+/*
+ * Convert from an old to a new stat structure.
+ */
+void
+cvtstat(st, ost)
+ struct stat *st;
+ struct ostat *ost;
+{
+
+ ost->st_dev = st->st_dev;
+ ost->st_ino = st->st_ino;
+ ost->st_mode = st->st_mode;
+ ost->st_nlink = st->st_nlink;
+ ost->st_uid = st->st_uid;
+ ost->st_gid = st->st_gid;
+ ost->st_rdev = st->st_rdev;
+ if (st->st_size < (quad_t)1 << 32)
+ ost->st_size = st->st_size;
+ else
+ ost->st_size = -2;
+ ost->st_atime = st->st_atime;
+ ost->st_mtime = st->st_mtime;
+ ost->st_ctime = st->st_ctime;
+ ost->st_blksize = st->st_blksize;
+ ost->st_blocks = st->st_blocks;
+ ost->st_flags = st->st_flags;
+ ost->st_gen = st->st_gen;
+}
+#endif /* COMPAT_43 || COMPAT_SUNOS */
+
+/*
+ * Get file status; this version follows links.
+ */
+struct stat_args {
+ char *path;
+ struct stat *ub;
+};
+/* ARGSUSED */
+int
+stat(p, uap, retval)
+ struct proc *p;
+ register struct stat_args *uap;
+ int *retval;
+{
+ struct stat sb;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ error = vn_stat(nd.ni_vp, &sb, p);
+ vput(nd.ni_vp);
+ if (error)
+ return (error);
+ error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb));
+ return (error);
+}
+
+/*
+ * Get file status; this version does not follow links.
+ */
+struct lstat_args {
+ char *path;
+ struct stat *ub;
+};
+/* ARGSUSED */
+int
+lstat(p, uap, retval)
+ struct proc *p;
+ register struct lstat_args *uap;
+ int *retval;
+{
+ int error;
+ struct vnode *vp, *dvp;
+ struct stat sb, sb1;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | LOCKPARENT, UIO_USERSPACE,
+ uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ /*
+ * For symbolic links, always return the attributes of its
+ * containing directory, except for mode, size, and links.
+ */
+ vp = nd.ni_vp;
+ dvp = nd.ni_dvp;
+ if (vp->v_type != VLNK) {
+ if (dvp == vp)
+ vrele(dvp);
+ else
+ vput(dvp);
+ error = vn_stat(vp, &sb, p);
+ vput(vp);
+ if (error)
+ return (error);
+ } else {
+ error = vn_stat(dvp, &sb, p);
+ vput(dvp);
+ if (error) {
+ vput(vp);
+ return (error);
+ }
+ error = vn_stat(vp, &sb1, p);
+ vput(vp);
+ if (error)
+ return (error);
+ sb.st_mode &= ~S_IFDIR;
+ sb.st_mode |= S_IFLNK;
+ sb.st_nlink = sb1.st_nlink;
+ sb.st_size = sb1.st_size;
+ sb.st_blocks = sb1.st_blocks;
+ }
+ error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb));
+ return (error);
+}
+
+/*
+ * Get configurable pathname variables.
+ */
+struct pathconf_args {
+ char *path;
+ int name;
+};
+/* ARGSUSED */
+int
+pathconf(p, uap, retval)
+ struct proc *p;
+ register struct pathconf_args *uap;
+ int *retval;
+{
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ error = VOP_PATHCONF(nd.ni_vp, uap->name, retval);
+ vput(nd.ni_vp);
+ return (error);
+}
+
+/*
+ * Return target name of a symbolic link.
+ */
+struct readlink_args {
+ char *path;
+ char *buf;
+ int count;
+};
+/* ARGSUSED */
+int
+readlink(p, uap, retval)
+ struct proc *p;
+ register struct readlink_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct iovec aiov;
+ struct uio auio;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (vp->v_type != VLNK)
+ error = EINVAL;
+ else {
+ aiov.iov_base = uap->buf;
+ aiov.iov_len = uap->count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_offset = 0;
+ auio.uio_rw = UIO_READ;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_procp = p;
+ auio.uio_resid = uap->count;
+ error = VOP_READLINK(vp, &auio, p->p_ucred);
+ }
+ vput(vp);
+ *retval = uap->count - auio.uio_resid;
+ return (error);
+}
+
+/*
+ * Change flags of a file given a path name.
+ */
+struct chflags_args {
+ char *path;
+ int flags;
+};
+/* ARGSUSED */
+int
+chflags(p, uap, retval)
+ struct proc *p;
+ register struct chflags_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ error = EROFS;
+ else {
+ VATTR_NULL(&vattr);
+ vattr.va_flags = uap->flags;
+ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+ }
+ vput(vp);
+ return (error);
+}
+
+/*
+ * Change flags of a file given a file descriptor.
+ */
+struct fchflags_args {
+ int fd;
+ int flags;
+};
+/* ARGSUSED */
+int
+fchflags(p, uap, retval)
+ struct proc *p;
+ register struct fchflags_args *uap;
+ int *retval;
+{
+ struct vattr vattr;
+ struct vnode *vp;
+ struct file *fp;
+ int error;
+
+ if (error = getvnode(p->p_fd, uap->fd, &fp))
+ return (error);
+ vp = (struct vnode *)fp->f_data;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ error = EROFS;
+ else {
+ VATTR_NULL(&vattr);
+ vattr.va_flags = uap->flags;
+ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+ }
+ VOP_UNLOCK(vp);
+ return (error);
+}
+
+/*
+ * Change mode of a file given path name.
+ */
+struct chmod_args {
+ char *path;
+ int mode;
+};
+/* ARGSUSED */
+int
+chmod(p, uap, retval)
+ struct proc *p;
+ register struct chmod_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ error = EROFS;
+ else {
+ VATTR_NULL(&vattr);
+ vattr.va_mode = uap->mode & ALLPERMS;
+ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+ }
+ vput(vp);
+ return (error);
+}
+
+/*
+ * Change mode of a file given a file descriptor.
+ */
+struct fchmod_args {
+ int fd;
+ int mode;
+};
+/* ARGSUSED */
+int
+fchmod(p, uap, retval)
+ struct proc *p;
+ register struct fchmod_args *uap;
+ int *retval;
+{
+ struct vattr vattr;
+ struct vnode *vp;
+ struct file *fp;
+ int error;
+
+ if (error = getvnode(p->p_fd, uap->fd, &fp))
+ return (error);
+ vp = (struct vnode *)fp->f_data;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ error = EROFS;
+ else {
+ VATTR_NULL(&vattr);
+ vattr.va_mode = uap->mode & ALLPERMS;
+ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+ }
+ VOP_UNLOCK(vp);
+ return (error);
+}
+
+/*
+ * Set ownership given a path name.
+ */
+struct chown_args {
+ char *path;
+ int uid;
+ int gid;
+};
+/* ARGSUSED */
+int
+chown(p, uap, retval)
+ struct proc *p;
+ register struct chown_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ error = EROFS;
+ else {
+ VATTR_NULL(&vattr);
+ vattr.va_uid = uap->uid;
+ vattr.va_gid = uap->gid;
+ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+ }
+ vput(vp);
+ return (error);
+}
+
+/*
+ * Set ownership given a file descriptor.
+ */
+struct fchown_args {
+ int fd;
+ int uid;
+ int gid;
+};
+/* ARGSUSED */
+int
+fchown(p, uap, retval)
+ struct proc *p;
+ register struct fchown_args *uap;
+ int *retval;
+{
+ struct vattr vattr;
+ struct vnode *vp;
+ struct file *fp;
+ int error;
+
+ if (error = getvnode(p->p_fd, uap->fd, &fp))
+ return (error);
+ vp = (struct vnode *)fp->f_data;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ error = EROFS;
+ else {
+ VATTR_NULL(&vattr);
+ vattr.va_uid = uap->uid;
+ vattr.va_gid = uap->gid;
+ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+ }
+ VOP_UNLOCK(vp);
+ return (error);
+}
+
+/*
+ * Set the access and modification times of a file.
+ */
+struct utimes_args {
+ char *path;
+ struct timeval *tptr;
+};
+/* ARGSUSED */
+int
+utimes(p, uap, retval)
+ struct proc *p;
+ register struct utimes_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct timeval tv[2];
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ VATTR_NULL(&vattr);
+ if (uap->tptr == NULL) {
+ microtime(&tv[0]);
+ tv[1] = tv[0];
+ vattr.va_vaflags |= VA_UTIMES_NULL;
+ } else if (error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv)))
+ return (error);
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ error = EROFS;
+ else {
+ vattr.va_atime.ts_sec = tv[0].tv_sec;
+ vattr.va_atime.ts_nsec = tv[0].tv_usec * 1000;
+ vattr.va_mtime.ts_sec = tv[1].tv_sec;
+ vattr.va_mtime.ts_nsec = tv[1].tv_usec * 1000;
+ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+ }
+ vput(vp);
+ return (error);
+}
+
+/*
+ * Truncate a file given its path name.
+ */
+struct truncate_args {
+ char *path;
+ int pad;
+ off_t length;
+};
+/* ARGSUSED */
+int
+truncate(p, uap, retval)
+ struct proc *p;
+ register struct truncate_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_type == VDIR)
+ error = EISDIR;
+ else if ((error = vn_writechk(vp)) == 0 &&
+ (error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) == 0) {
+ VATTR_NULL(&vattr);
+ vattr.va_size = uap->length;
+ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+ }
+ vput(vp);
+ return (error);
+}
+
+/*
+ * Truncate a file given a file descriptor.
+ */
+struct ftruncate_args {
+ int fd;
+ int pad;
+ off_t length;
+};
+/* ARGSUSED */
+int
+ftruncate(p, uap, retval)
+ struct proc *p;
+ register struct ftruncate_args *uap;
+ int *retval;
+{
+ struct vattr vattr;
+ struct vnode *vp;
+ struct file *fp;
+ int error;
+
+ if (error = getvnode(p->p_fd, uap->fd, &fp))
+ return (error);
+ if ((fp->f_flag & FWRITE) == 0)
+ return (EINVAL);
+ vp = (struct vnode *)fp->f_data;
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ if (vp->v_type == VDIR)
+ error = EISDIR;
+ else if ((error = vn_writechk(vp)) == 0) {
+ VATTR_NULL(&vattr);
+ vattr.va_size = uap->length;
+ error = VOP_SETATTR(vp, &vattr, fp->f_cred, p);
+ }
+ VOP_UNLOCK(vp);
+ return (error);
+}
+
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+/*
+ * Truncate a file given its path name.
+ */
+struct otruncate_args {
+ char *path;
+ long length;
+};
+/* ARGSUSED */
+int
+otruncate(p, uap, retval)
+ struct proc *p;
+ register struct otruncate_args *uap;
+ int *retval;
+{
+ struct truncate_args nuap;
+
+ nuap.path = uap->path;
+ nuap.length = uap->length;
+ return (truncate(p, &nuap, retval));
+}
+
+/*
+ * Truncate a file given a file descriptor.
+ */
+struct oftruncate_args {
+ int fd;
+ long length;
+};
+/* ARGSUSED */
+int
+oftruncate(p, uap, retval)
+ struct proc *p;
+ register struct oftruncate_args *uap;
+ int *retval;
+{
+ struct ftruncate_args nuap;
+
+ nuap.fd = uap->fd;
+ nuap.length = uap->length;
+ return (ftruncate(p, &nuap, retval));
+}
+#endif /* COMPAT_43 || COMPAT_SUNOS */
+
+/*
+ * Sync an open file.
+ */
+struct fsync_args {
+ int fd;
+};
+/* ARGSUSED */
+int
+fsync(p, uap, retval)
+ struct proc *p;
+ struct fsync_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct file *fp;
+ int error;
+
+ if (error = getvnode(p->p_fd, uap->fd, &fp))
+ return (error);
+ vp = (struct vnode *)fp->f_data;
+ VOP_LOCK(vp);
+ error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, p);
+ VOP_UNLOCK(vp);
+ return (error);
+}
+
+/*
+ * Rename files. Source and destination must either both be directories,
+ * or both not be directories. If target is a directory, it must be empty.
+ */
+struct rename_args {
+ char *from;
+ char *to;
+};
+/* ARGSUSED */
+int
+rename(p, uap, retval)
+ struct proc *p;
+ register struct rename_args *uap;
+ int *retval;
+{
+ register struct vnode *tvp, *fvp, *tdvp;
+ struct nameidata fromnd, tond;
+ int error;
+
+ NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, UIO_USERSPACE,
+ uap->from, p);
+ if (error = namei(&fromnd))
+ return (error);
+ fvp = fromnd.ni_vp;
+ NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART,
+ UIO_USERSPACE, uap->to, p);
+ if (error = namei(&tond)) {
+ VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
+ vrele(fromnd.ni_dvp);
+ vrele(fvp);
+ goto out1;
+ }
+ tdvp = tond.ni_dvp;
+ tvp = tond.ni_vp;
+ if (tvp != NULL) {
+ if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto out;
+ } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) {
+ error = EISDIR;
+ goto out;
+ }
+ }
+ if (fvp == tdvp)
+ error = EINVAL;
+ /*
+ * If source is the same as the destination (that is the
+ * same inode number with the same name in the same directory),
+ * then there is nothing to do.
+ */
+ if (fvp == tvp && fromnd.ni_dvp == tdvp &&
+ fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen &&
+ !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr,
+ fromnd.ni_cnd.cn_namelen))
+ error = -1;
+out:
+ if (!error) {
+ LEASE_CHECK(tdvp, p, p->p_ucred, LEASE_WRITE);
+ if (fromnd.ni_dvp != tdvp)
+ LEASE_CHECK(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ if (tvp)
+ LEASE_CHECK(tvp, p, p->p_ucred, LEASE_WRITE);
+ error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd,
+ tond.ni_dvp, tond.ni_vp, &tond.ni_cnd);
+ } else {
+ VOP_ABORTOP(tond.ni_dvp, &tond.ni_cnd);
+ if (tdvp == tvp)
+ vrele(tdvp);
+ else
+ vput(tdvp);
+ if (tvp)
+ vput(tvp);
+ VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
+ vrele(fromnd.ni_dvp);
+ vrele(fvp);
+ }
+ vrele(tond.ni_startdir);
+ FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI);
+out1:
+ if (fromnd.ni_startdir)
+ vrele(fromnd.ni_startdir);
+ FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI);
+ if (error == -1)
+ return (0);
+ return (error);
+}
+
+/*
+ * Make a directory file.
+ */
+struct mkdir_args {
+ char *path;
+ int mode;
+};
+/* ARGSUSED */
+int
+mkdir(p, uap, retval)
+ struct proc *p;
+ register struct mkdir_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (vp != NULL) {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vrele(vp);
+ return (EEXIST);
+ }
+ VATTR_NULL(&vattr);
+ vattr.va_type = VDIR;
+ vattr.va_mode = (uap->mode & ACCESSPERMS) &~ p->p_fd->fd_cmask;
+ LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
+ if (!error)
+ vput(nd.ni_vp);
+ return (error);
+}
+
+/*
+ * Remove a directory file.
+ */
+struct rmdir_args {
+ char *path;
+};
+/* ARGSUSED */
+int
+rmdir(p, uap, retval)
+ struct proc *p;
+ struct rmdir_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (vp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto out;
+ }
+ /*
+ * No rmdir "." please.
+ */
+ if (nd.ni_dvp == vp) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * The root of a mounted filesystem cannot be deleted.
+ */
+ if (vp->v_flag & VROOT)
+ error = EBUSY;
+out:
+ if (!error) {
+ LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+ error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vput(vp);
+ }
+ return (error);
+}
+
+#ifdef COMPAT_43
+/*
+ * Read a block of directory entries in a file system independent format.
+ */
+struct ogetdirentries_args {
+ int fd;
+ char *buf;
+ u_int count;
+ long *basep;
+};
+int
+ogetdirentries(p, uap, retval)
+ struct proc *p;
+ register struct ogetdirentries_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct file *fp;
+ struct uio auio, kuio;
+ struct iovec aiov, kiov;
+ struct dirent *dp, *edp;
+ caddr_t dirbuf;
+ int error, readcnt;
+ long loff;
+
+ if (error = getvnode(p->p_fd, uap->fd, &fp))
+ return (error);
+ if ((fp->f_flag & FREAD) == 0)
+ return (EBADF);
+ vp = (struct vnode *)fp->f_data;
+ if (vp->v_type != VDIR)
+ return (EINVAL);
+ aiov.iov_base = uap->buf;
+ aiov.iov_len = uap->count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_rw = UIO_READ;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_procp = p;
+ auio.uio_resid = uap->count;
+ VOP_LOCK(vp);
+ loff = auio.uio_offset = fp->f_offset;
+# if (BYTE_ORDER != LITTLE_ENDIAN)
+ if (vp->v_mount->mnt_maxsymlinklen <= 0) {
+ error = VOP_READDIR(vp, &auio, fp->f_cred);
+ fp->f_offset = auio.uio_offset;
+ } else
+# endif
+ {
+ kuio = auio;
+ kuio.uio_iov = &kiov;
+ kuio.uio_segflg = UIO_SYSSPACE;
+ kiov.iov_len = uap->count;
+ MALLOC(dirbuf, caddr_t, uap->count, M_TEMP, M_WAITOK);
+ kiov.iov_base = dirbuf;
+ error = VOP_READDIR(vp, &kuio, fp->f_cred);
+ fp->f_offset = kuio.uio_offset;
+ if (error == 0) {
+ readcnt = uap->count - kuio.uio_resid;
+ edp = (struct dirent *)&dirbuf[readcnt];
+ for (dp = (struct dirent *)dirbuf; dp < edp; ) {
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ /*
+ * The expected low byte of
+ * dp->d_namlen is our dp->d_type.
+ * The high MBZ byte of dp->d_namlen
+ * is our dp->d_namlen.
+ */
+ dp->d_type = dp->d_namlen;
+ dp->d_namlen = 0;
+# else
+ /*
+ * The dp->d_type is the high byte
+ * of the expected dp->d_namlen,
+ * so must be zero'ed.
+ */
+ dp->d_type = 0;
+# endif
+ if (dp->d_reclen > 0) {
+ dp = (struct dirent *)
+ ((char *)dp + dp->d_reclen);
+ } else {
+ error = EIO;
+ break;
+ }
+ }
+ if (dp >= edp)
+ error = uiomove(dirbuf, readcnt, &auio);
+ }
+ FREE(dirbuf, M_TEMP);
+ }
+ VOP_UNLOCK(vp);
+ if (error)
+ return (error);
+ error = copyout((caddr_t)&loff, (caddr_t)uap->basep, sizeof(long));
+ *retval = uap->count - auio.uio_resid;
+ return (error);
+}
+#endif
+
+/*
+ * Read a block of directory entries in a file system independent format.
+ */
+struct getdirentries_args {
+ int fd;
+ char *buf;
+ u_int count;
+ long *basep;
+};
+int
+getdirentries(p, uap, retval)
+ struct proc *p;
+ register struct getdirentries_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct file *fp;
+ struct uio auio;
+ struct iovec aiov;
+ long loff;
+ int error;
+
+ if (error = getvnode(p->p_fd, uap->fd, &fp))
+ return (error);
+ if ((fp->f_flag & FREAD) == 0)
+ return (EBADF);
+ vp = (struct vnode *)fp->f_data;
+unionread:
+ if (vp->v_type != VDIR)
+ return (EINVAL);
+ aiov.iov_base = uap->buf;
+ aiov.iov_len = uap->count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_rw = UIO_READ;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_procp = p;
+ auio.uio_resid = uap->count;
+ VOP_LOCK(vp);
+ loff = auio.uio_offset = fp->f_offset;
+ error = VOP_READDIR(vp, &auio, fp->f_cred);
+ fp->f_offset = auio.uio_offset;
+ VOP_UNLOCK(vp);
+ if (error)
+ return (error);
+
+#ifdef UNION
+{
+ extern int (**union_vnodeop_p)();
+ extern struct vnode *union_lowervp __P((struct vnode *));
+
+ if ((uap->count == auio.uio_resid) &&
+ (vp->v_op == union_vnodeop_p)) {
+ struct vnode *tvp = vp;
+
+ vp = union_lowervp(vp);
+ if (vp != NULLVP) {
+ VOP_LOCK(vp);
+ error = VOP_OPEN(vp, FREAD);
+ VOP_UNLOCK(vp);
+
+ if (error) {
+ vrele(vp);
+ return (error);
+ }
+ fp->f_data = (caddr_t) vp;
+ fp->f_offset = 0;
+ error = vn_close(tvp, FREAD, fp->f_cred, p);
+ if (error)
+ return (error);
+ goto unionread;
+ }
+ }
+}
+#endif
+
+ if ((uap->count == auio.uio_resid) &&
+ (vp->v_flag & VROOT) &&
+ (vp->v_mount->mnt_flag & MNT_UNION)) {
+ struct vnode *tvp = vp;
+ vp = vp->v_mount->mnt_vnodecovered;
+ VREF(vp);
+ fp->f_data = (caddr_t) vp;
+ fp->f_offset = 0;
+ vrele(tvp);
+ goto unionread;
+ }
+ error = copyout((caddr_t)&loff, (caddr_t)uap->basep, sizeof(long));
+ *retval = uap->count - auio.uio_resid;
+ return (error);
+}
+
+/*
+ * Set the mode mask for creation of filesystem nodes.
+ */
+struct umask_args {
+ int newmask;
+};
+mode_t /* XXX */
+umask(p, uap, retval)
+ struct proc *p;
+ struct umask_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp;
+
+ fdp = p->p_fd;
+ *retval = fdp->fd_cmask;
+ fdp->fd_cmask = uap->newmask & ALLPERMS;
+ return (0);
+}
+
+/*
+ * Void all references to file by ripping underlying filesystem
+ * away from vnode.
+ */
+struct revoke_args {
+ char *path;
+};
+/* ARGSUSED */
+int
+revoke(p, uap, retval)
+ struct proc *p;
+ register struct revoke_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ struct vattr vattr;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (vp->v_type != VCHR && vp->v_type != VBLK) {
+ error = EINVAL;
+ goto out;
+ }
+ if (error = VOP_GETATTR(vp, &vattr, p->p_ucred, p))
+ goto out;
+ if (p->p_ucred->cr_uid != vattr.va_uid &&
+ (error = suser(p->p_ucred, &p->p_acflag)))
+ goto out;
+ if (vp->v_usecount > 1 || (vp->v_flag & VALIASED))
+ vgoneall(vp);
+out:
+ vrele(vp);
+ return (error);
+}
+
+/*
+ * Convert a user file descriptor to a kernel file entry.
+ */
+int
+getvnode(fdp, fd, fpp)
+ struct filedesc *fdp;
+ struct file **fpp;
+ int fd;
+{
+ struct file *fp;
+
+ if ((u_int)fd >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[fd]) == NULL)
+ return (EBADF);
+ if (fp->f_type != DTYPE_VNODE)
+ return (EINVAL);
+ *fpp = fp;
+ return (0);
+}
diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c
new file mode 100644
index 0000000..d63a39c
--- /dev/null
+++ b/sys/kern/vfs_vnops.c
@@ -0,0 +1,432 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vfs_vnops.c 8.2 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+
+#include <vm/vm.h>
+
+struct fileops vnops =
+ { vn_read, vn_write, vn_ioctl, vn_select, vn_closefile };
+
+/*
+ * Common code for vnode open operations.
+ * Check permissions, and call the VOP_OPEN or VOP_CREATE routine.
+ */
+int
+vn_open(ndp, fmode, cmode)
+ register struct nameidata *ndp;
+ int fmode, cmode;
+{
+ register struct vnode *vp;
+ register struct proc *p = ndp->ni_cnd.cn_proc;
+ register struct ucred *cred = p->p_ucred;
+ struct vattr vat;
+ struct vattr *vap = &vat;
+ int error;
+
+ if (fmode & O_CREAT) {
+ ndp->ni_cnd.cn_nameiop = CREATE;
+ ndp->ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
+ if ((fmode & O_EXCL) == 0)
+ ndp->ni_cnd.cn_flags |= FOLLOW;
+ if (error = namei(ndp))
+ return (error);
+ if (ndp->ni_vp == NULL) {
+ VATTR_NULL(vap);
+ vap->va_type = VREG;
+ vap->va_mode = cmode;
+ LEASE_CHECK(ndp->ni_dvp, p, cred, LEASE_WRITE);
+ if (error = VOP_CREATE(ndp->ni_dvp, &ndp->ni_vp,
+ &ndp->ni_cnd, vap))
+ return (error);
+ fmode &= ~O_TRUNC;
+ vp = ndp->ni_vp;
+ } else {
+ VOP_ABORTOP(ndp->ni_dvp, &ndp->ni_cnd);
+ if (ndp->ni_dvp == ndp->ni_vp)
+ vrele(ndp->ni_dvp);
+ else
+ vput(ndp->ni_dvp);
+ ndp->ni_dvp = NULL;
+ vp = ndp->ni_vp;
+ if (fmode & O_EXCL) {
+ error = EEXIST;
+ goto bad;
+ }
+ fmode &= ~O_CREAT;
+ }
+ } else {
+ ndp->ni_cnd.cn_nameiop = LOOKUP;
+ ndp->ni_cnd.cn_flags = FOLLOW | LOCKLEAF;
+ if (error = namei(ndp))
+ return (error);
+ vp = ndp->ni_vp;
+ }
+ if (vp->v_type == VSOCK) {
+ error = EOPNOTSUPP;
+ goto bad;
+ }
+ if ((fmode & O_CREAT) == 0) {
+ if (fmode & FREAD) {
+ if (error = VOP_ACCESS(vp, VREAD, cred, p))
+ goto bad;
+ }
+ if (fmode & (FWRITE | O_TRUNC)) {
+ if (vp->v_type == VDIR) {
+ error = EISDIR;
+ goto bad;
+ }
+ if ((error = vn_writechk(vp)) ||
+ (error = VOP_ACCESS(vp, VWRITE, cred, p)))
+ goto bad;
+ }
+ }
+ if (fmode & O_TRUNC) {
+ VOP_UNLOCK(vp); /* XXX */
+ LEASE_CHECK(vp, p, cred, LEASE_WRITE);
+ VOP_LOCK(vp); /* XXX */
+ VATTR_NULL(vap);
+ vap->va_size = 0;
+ if (error = VOP_SETATTR(vp, vap, cred, p))
+ goto bad;
+ }
+ if (error = VOP_OPEN(vp, fmode, cred, p))
+ goto bad;
+ if (fmode & FWRITE)
+ vp->v_writecount++;
+ return (0);
+bad:
+ vput(vp);
+ return (error);
+}
+
+/*
+ * Check for write permissions on the specified vnode.
+ * The read-only status of the file system is checked.
+ * Also, prototype text segments cannot be written.
+ */
+int
+vn_writechk(vp)
+ register struct vnode *vp;
+{
+
+ /*
+ * Disallow write attempts on read-only file systems;
+ * unless the file is a socket or a block or character
+ * device resident on the file system.
+ */
+ if (vp->v_mount->mnt_flag & MNT_RDONLY) {
+ switch (vp->v_type) {
+ case VREG: case VDIR: case VLNK:
+ return (EROFS);
+ }
+ }
+ /*
+ * If there's shared text associated with
+ * the vnode, try to free it up once. If
+ * we fail, we can't allow writing.
+ */
+ if ((vp->v_flag & VTEXT) && !vnode_pager_uncache(vp))
+ return (ETXTBSY);
+ return (0);
+}
+
+/*
+ * Vnode close call
+ */
+int
+vn_close(vp, flags, cred, p)
+ register struct vnode *vp;
+ int flags;
+ struct ucred *cred;
+ struct proc *p;
+{
+ int error;
+
+ if (flags & FWRITE)
+ vp->v_writecount--;
+ error = VOP_CLOSE(vp, flags, cred, p);
+ vrele(vp);
+ return (error);
+}
+
+/*
+ * Package up an I/O request on a vnode into a uio and do it.
+ */
+int
+vn_rdwr(rw, vp, base, len, offset, segflg, ioflg, cred, aresid, p)
+ enum uio_rw rw;
+ struct vnode *vp;
+ caddr_t base;
+ int len;
+ off_t offset;
+ enum uio_seg segflg;
+ int ioflg;
+ struct ucred *cred;
+ int *aresid;
+ struct proc *p;
+{
+ struct uio auio;
+ struct iovec aiov;
+ int error;
+
+ if ((ioflg & IO_NODELOCKED) == 0)
+ VOP_LOCK(vp);
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ aiov.iov_base = base;
+ aiov.iov_len = len;
+ auio.uio_resid = len;
+ auio.uio_offset = offset;
+ auio.uio_segflg = segflg;
+ auio.uio_rw = rw;
+ auio.uio_procp = p;
+ if (rw == UIO_READ) {
+ error = VOP_READ(vp, &auio, ioflg, cred);
+ } else {
+ error = VOP_WRITE(vp, &auio, ioflg, cred);
+ }
+ if (aresid)
+ *aresid = auio.uio_resid;
+ else
+ if (auio.uio_resid && error == 0)
+ error = EIO;
+ if ((ioflg & IO_NODELOCKED) == 0)
+ VOP_UNLOCK(vp);
+ return (error);
+}
+
+/*
+ * File table vnode read routine.
+ */
+int
+vn_read(fp, uio, cred)
+ struct file *fp;
+ struct uio *uio;
+ struct ucred *cred;
+{
+ register struct vnode *vp = (struct vnode *)fp->f_data;
+ int count, error;
+
+ LEASE_CHECK(vp, uio->uio_procp, cred, LEASE_READ);
+ VOP_LOCK(vp);
+ uio->uio_offset = fp->f_offset;
+ count = uio->uio_resid;
+ error = VOP_READ(vp, uio, (fp->f_flag & FNONBLOCK) ? IO_NDELAY : 0,
+ cred);
+ fp->f_offset += count - uio->uio_resid;
+ VOP_UNLOCK(vp);
+ return (error);
+}
+
+/*
+ * File table vnode write routine.
+ */
+int
+vn_write(fp, uio, cred)
+ struct file *fp;
+ struct uio *uio;
+ struct ucred *cred;
+{
+ register struct vnode *vp = (struct vnode *)fp->f_data;
+ int count, error, ioflag = 0;
+
+ if (vp->v_type == VREG && (fp->f_flag & O_APPEND))
+ ioflag |= IO_APPEND;
+ if (fp->f_flag & FNONBLOCK)
+ ioflag |= IO_NDELAY;
+ LEASE_CHECK(vp, uio->uio_procp, cred, LEASE_WRITE);
+ VOP_LOCK(vp);
+ uio->uio_offset = fp->f_offset;
+ count = uio->uio_resid;
+ error = VOP_WRITE(vp, uio, ioflag, cred);
+ if (ioflag & IO_APPEND)
+ fp->f_offset = uio->uio_offset;
+ else
+ fp->f_offset += count - uio->uio_resid;
+ VOP_UNLOCK(vp);
+ return (error);
+}
+
+/*
+ * File table vnode stat routine.
+ */
+int
+vn_stat(vp, sb, p)
+ struct vnode *vp;
+ register struct stat *sb;
+ struct proc *p;
+{
+ struct vattr vattr;
+ register struct vattr *vap;
+ int error;
+ u_short mode;
+
+ vap = &vattr;
+ error = VOP_GETATTR(vp, vap, p->p_ucred, p);
+ if (error)
+ return (error);
+ /*
+ * Copy from vattr table
+ */
+ sb->st_dev = vap->va_fsid;
+ sb->st_ino = vap->va_fileid;
+ mode = vap->va_mode;
+ switch (vp->v_type) {
+ case VREG:
+ mode |= S_IFREG;
+ break;
+ case VDIR:
+ mode |= S_IFDIR;
+ break;
+ case VBLK:
+ mode |= S_IFBLK;
+ break;
+ case VCHR:
+ mode |= S_IFCHR;
+ break;
+ case VLNK:
+ mode |= S_IFLNK;
+ break;
+ case VSOCK:
+ mode |= S_IFSOCK;
+ break;
+ case VFIFO:
+ mode |= S_IFIFO;
+ break;
+ default:
+ return (EBADF);
+ };
+ sb->st_mode = mode;
+ sb->st_nlink = vap->va_nlink;
+ sb->st_uid = vap->va_uid;
+ sb->st_gid = vap->va_gid;
+ sb->st_rdev = vap->va_rdev;
+ sb->st_size = vap->va_size;
+ sb->st_atimespec = vap->va_atime;
+ sb->st_mtimespec= vap->va_mtime;
+ sb->st_ctimespec = vap->va_ctime;
+ sb->st_blksize = vap->va_blocksize;
+ sb->st_flags = vap->va_flags;
+ sb->st_gen = vap->va_gen;
+ sb->st_blocks = vap->va_bytes / S_BLKSIZE;
+ return (0);
+}
+
+/*
+ * File table vnode ioctl routine.
+ */
+int
+vn_ioctl(fp, com, data, p)
+ struct file *fp;
+ int com;
+ caddr_t data;
+ struct proc *p;
+{
+ register struct vnode *vp = ((struct vnode *)fp->f_data);
+ struct vattr vattr;
+ int error;
+
+ switch (vp->v_type) {
+
+ case VREG:
+ case VDIR:
+ if (com == FIONREAD) {
+ if (error = VOP_GETATTR(vp, &vattr, p->p_ucred, p))
+ return (error);
+ *(int *)data = vattr.va_size - fp->f_offset;
+ return (0);
+ }
+ if (com == FIONBIO || com == FIOASYNC) /* XXX */
+ return (0); /* XXX */
+ /* fall into ... */
+
+ default:
+ return (ENOTTY);
+
+ case VFIFO:
+ case VCHR:
+ case VBLK:
+ error = VOP_IOCTL(vp, com, data, fp->f_flag, p->p_ucred, p);
+ if (error == 0 && com == TIOCSCTTY) {
+ p->p_session->s_ttyvp = vp;
+ VREF(vp);
+ }
+ return (error);
+ }
+}
+
+/*
+ * File table vnode select routine.
+ */
+int
+vn_select(fp, which, p)
+ struct file *fp;
+ int which;
+ struct proc *p;
+{
+
+ return (VOP_SELECT(((struct vnode *)fp->f_data), which, fp->f_flag,
+ fp->f_cred, p));
+}
+
+/*
+ * File table vnode close routine.
+ */
+int
+vn_closefile(fp, p)
+ struct file *fp;
+ struct proc *p;
+{
+
+ return (vn_close(((struct vnode *)fp->f_data), fp->f_flag,
+ fp->f_cred, p));
+}
diff --git a/sys/kern/vnode_if.pl b/sys/kern/vnode_if.pl
new file mode 100644
index 0000000..e190fa0
--- /dev/null
+++ b/sys/kern/vnode_if.pl
@@ -0,0 +1,433 @@
+#!/bin/sh -
+#
+# Copyright (c) 1992, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)vnode_if.sh 8.1 (Berkeley) 6/10/93
+#
+
+# Script to produce VFS front-end sugar.
+#
+# usage: vnode_if.sh srcfile
+# (where srcfile is currently /sys/kern/vnode_if.src)
+#
+# These awk scripts are not particularly well written, specifically they
+# don't use arrays well and figure out the same information repeatedly.
+# Please rewrite them if you actually understand how to use awk. Note,
+# they use nawk extensions and gawk's toupper.
+
+if [ $# -ne 1 ] ; then
+ echo 'usage: vnode_if.sh srcfile'
+ exit 1
+fi
+
+# Name of the source file.
+SRC=$1
+
+# Names of the created files.
+CFILE=vnode_if.c
+HEADER=vnode_if.h
+
+# Awk program (must support nawk extensions and gawk's "toupper")
+# Use "awk" at Berkeley, "gawk" elsewhere.
+AWK=awk
+
+# Print out header information for vnode_if.h.
+cat << END_OF_LEADING_COMMENT > $HEADER
+/*
+ * This file is produced automatically.
+ * Do not modify anything in here by hand.
+ *
+ * Created from @(#)vnode_if.sh 8.1 (Berkeley) 6/10/93
+ */
+
+extern struct vnodeop_desc vop_default_desc;
+END_OF_LEADING_COMMENT
+
+# Awk script to take vnode_if.src and turn it into vnode_if.h.
+$AWK '
+ NF == 0 || $0 ~ "^#" {
+ next;
+ }
+ {
+ # Get the function name.
+ name = $1;
+ uname = toupper(name);
+
+ # Get the function arguments.
+ for (c1 = 0;; ++c1) {
+ if (getline <= 0)
+ exit
+ if ($0 ~ "^};")
+ break;
+ a[c1] = $0;
+ }
+
+ # Print out the vop_F_args structure.
+ printf("struct %s_args {\n\tstruct vnodeop_desc *a_desc;\n",
+ name);
+ for (c2 = 0; c2 < c1; ++c2) {
+ c3 = split(a[c2], t);
+ printf("\t");
+ if (t[2] ~ "WILLRELE")
+ c4 = 3;
+ else
+ c4 = 2;
+ for (; c4 < c3; ++c4)
+ printf("%s ", t[c4]);
+ beg = match(t[c3], "[^*]");
+ printf("%sa_%s\n",
+ substr(t[c4], 0, beg - 1), substr(t[c4], beg));
+ }
+ printf("};\n");
+
+ # Print out extern declaration.
+ printf("extern struct vnodeop_desc %s_desc;\n", name);
+
+ # Print out inline struct.
+ printf("static inline int %s(", uname);
+ sep = ", ";
+ for (c2 = 0; c2 < c1; ++c2) {
+ if (c2 == c1 - 1)
+ sep = ")\n";
+ c3 = split(a[c2], t);
+ beg = match(t[c3], "[^*]");
+ end = match(t[c3], ";");
+ printf("%s%s", substr(t[c3], beg, end - beg), sep);
+ }
+ for (c2 = 0; c2 < c1; ++c2) {
+ c3 = split(a[c2], t);
+ printf("\t");
+ if (t[2] ~ "WILLRELE")
+ c4 = 3;
+ else
+ c4 = 2;
+ for (; c4 < c3; ++c4)
+ printf("%s ", t[c4]);
+ beg = match(t[c3], "[^*]");
+ printf("%s%s\n",
+ substr(t[c4], 0, beg - 1), substr(t[c4], beg));
+ }
+ printf("{\n\tstruct %s_args a;\n\n", name);
+ printf("\ta.a_desc = VDESC(%s);\n", name);
+ for (c2 = 0; c2 < c1; ++c2) {
+ c3 = split(a[c2], t);
+ printf("\t");
+ beg = match(t[c3], "[^*]");
+ end = match(t[c3], ";");
+ printf("a.a_%s = %s\n",
+ substr(t[c3], beg, end - beg), substr(t[c3], beg));
+ }
+ c1 = split(a[0], t);
+ beg = match(t[c1], "[^*]");
+ end = match(t[c1], ";");
+ printf("\treturn (VCALL(%s, VOFFSET(%s), &a));\n}\n",
+ substr(t[c1], beg, end - beg), name);
+ }' < $SRC >> $HEADER
+
+# Print out header information for vnode_if.c.
+cat << END_OF_LEADING_COMMENT > $CFILE
+/*
+ * This file is produced automatically.
+ * Do not modify anything in here by hand.
+ *
+ * Created from @(#)vnode_if.sh 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+
+struct vnodeop_desc vop_default_desc = {
+ 0,
+ "default",
+ 0,
+ NULL,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ NULL,
+};
+
+END_OF_LEADING_COMMENT
+
+# Awk script to take vnode_if.src and turn it into vnode_if.c.
+$AWK 'function kill_surrounding_ws (s) {
+ sub (/^[ \t]*/, "", s);
+ sub (/[ \t]*$/, "", s);
+ return s;
+ }
+
+ function read_args() {
+ numargs = 0;
+ while (getline ln) {
+ if (ln ~ /}/) {
+ break;
+ };
+
+ # Delete comments, if any.
+ gsub (/\/\*.*\*\//, "", ln);
+
+ # Delete leading/trailing space.
+ ln = kill_surrounding_ws(ln);
+
+ # Pick off direction.
+ if (1 == sub(/^INOUT[ \t]+/, "", ln))
+ dir = "INOUT";
+ else if (1 == sub(/^IN[ \t]+/, "", ln))
+ dir = "IN";
+ else if (1 == sub(/^OUT[ \t]+/, "", ln))
+ dir = "OUT";
+ else
+ bail("No IN/OUT direction for \"" ln "\".");
+
+ # check for "WILLRELE"
+ if (1 == sub(/^WILLRELE[ \t]+/, "", ln)) {
+ rele = "WILLRELE";
+ } else {
+ rele = "WONTRELE";
+ };
+
+ # kill trailing ;
+ if (1 != sub (/;$/, "", ln)) {
+ bail("Missing end-of-line ; in \"" ln "\".");
+ };
+
+ # pick off variable name
+ if (!(i = match(ln, /[A-Za-z0-9_]+$/))) {
+ bail("Missing var name \"a_foo\" in \"" ln "\".");
+ };
+ arg = substr (ln, i);
+ # Want to <<substr(ln, i) = "";>>, but nawk cannot.
+ # Hack around this.
+ ln = substr(ln, 1, i-1);
+
+ # what is left must be type
+ # (put clean it up some)
+ type = ln;
+ gsub (/[ \t]+/, " ", type); # condense whitespace
+ type = kill_surrounding_ws(type);
+
+ # (boy this was easier in Perl)
+
+ numargs++;
+ dirs[numargs] = dir;
+ reles[numargs] = rele;
+ types[numargs] = type;
+ args[numargs] = arg;
+ };
+ }
+
+ function generate_operation_vp_offsets() {
+ printf ("int %s_vp_offsets[] = {\n", name);
+ # as a side effect, figure out the releflags
+ releflags = "";
+ vpnum = 0;
+ for (i=1; i<=numargs; i++) {
+ if (types[i] == "struct vnode *") {
+ printf ("\tVOPARG_OFFSETOF(struct %s_args,a_%s),\n",
+ name, args[i]);
+ if (reles[i] == "WILLRELE") {
+ releflags = releflags "|VDESC_VP" vpnum "_WILLRELE";
+ };
+ vpnum++;
+ };
+ };
+ sub (/^\|/, "", releflags);
+ print "\tVDESC_NO_OFFSET";
+ print "};";
+ }
+
+ function find_arg_with_type (type) {
+ for (i=1; i<=numargs; i++) {
+ if (types[i] == type) {
+ return "VOPARG_OFFSETOF(struct " name "_args,a_" args[i] ")";
+ };
+ };
+ return "VDESC_NO_OFFSET";
+ }
+
+ function generate_operation_desc() {
+ printf ("struct vnodeop_desc %s_desc = {\n", name);
+ # offset
+ printf ("\t0,\n");
+ # printable name
+ printf ("\t\"%s\",\n", name);
+ # flags
+ vppwillrele = "";
+ for (i=1; i<=numargs; i++) {
+ if (types[i] == "struct vnode **" &&
+ (reles[i] == "WILLRELE")) {
+ vppwillrele = "|VDESC_VPP_WILLRELE";
+ };
+ };
+ if (releflags == "") {
+ printf ("\t0%s,\n", vppwillrele);
+ } else {
+ printf ("\t%s%s,\n", releflags, vppwillrele);
+ };
+ # vp offsets
+ printf ("\t%s_vp_offsets,\n", name);
+ # vpp (if any)
+ printf ("\t%s,\n", find_arg_with_type("struct vnode **"));
+ # cred (if any)
+ printf ("\t%s,\n", find_arg_with_type("struct ucred *"));
+ # proc (if any)
+ printf ("\t%s,\n", find_arg_with_type("struct proc *"));
+ # componentname
+ printf ("\t%s,\n", find_arg_with_type("struct componentname *"));
+ # transport layer information
+ printf ("\tNULL,\n};\n");
+ }
+
+ NF == 0 || $0 ~ "^#" {
+ next;
+ }
+ {
+ # get the function name
+ name = $1;
+
+ # get the function arguments
+ read_args();
+
+ # Print out the vop_F_vp_offsets structure. This all depends
+ # on naming conventions and nothing else.
+ generate_operation_vp_offsets();
+
+ # Print out the vnodeop_desc structure.
+ generate_operation_desc();
+
+ printf "\n";
+
+ }' < $SRC >> $CFILE
+# THINGS THAT DON'T WORK RIGHT YET.
+#
+# Two existing BSD vnodeops (bwrite and strategy) don't take any vnodes as
+# arguments. This means that these operations can't function successfully
+# through a bypass routine.
+#
+# Bwrite and strategy will be replaced when the VM page/buffer cache
+# integration happens.
+#
+# To get around this problem for now we handle these ops as special cases.
+
+cat << END_OF_SPECIAL_CASES >> $HEADER
+#include <sys/buf.h>
+struct vop_strategy_args {
+ struct vnodeop_desc *a_desc;
+ struct buf *a_bp;
+};
+extern struct vnodeop_desc vop_strategy_desc;
+static inline int VOP_STRATEGY(bp)
+ struct buf *bp;
+{
+ struct vop_strategy_args a;
+
+ a.a_desc = VDESC(vop_strategy);
+ a.a_bp = bp;
+ return (VCALL((bp)->b_vp, VOFFSET(vop_strategy), &a));
+}
+
+struct vop_bwrite_args {
+ struct vnodeop_desc *a_desc;
+ struct buf *a_bp;
+};
+extern struct vnodeop_desc vop_bwrite_desc;
+static inline int VOP_BWRITE(bp)
+ struct buf *bp;
+{
+ struct vop_bwrite_args a;
+
+ a.a_desc = VDESC(vop_bwrite);
+ a.a_bp = bp;
+ return (VCALL((bp)->b_vp, VOFFSET(vop_bwrite), &a));
+}
+END_OF_SPECIAL_CASES
+
+cat << END_OF_SPECIAL_CASES >> $CFILE
+int vop_strategy_vp_offsets[] = {
+ VDESC_NO_OFFSET
+};
+struct vnodeop_desc vop_strategy_desc = {
+ 0,
+ "vop_strategy",
+ 0,
+ vop_strategy_vp_offsets,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ NULL,
+};
+int vop_bwrite_vp_offsets[] = {
+ VDESC_NO_OFFSET
+};
+struct vnodeop_desc vop_bwrite_desc = {
+ 0,
+ "vop_bwrite",
+ 0,
+ vop_bwrite_vp_offsets,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ NULL,
+};
+END_OF_SPECIAL_CASES
+
+# Add the vfs_op_descs array to the C file.
+$AWK '
+ BEGIN {
+ printf("\nstruct vnodeop_desc *vfs_op_descs[] = {\n");
+ printf("\t&vop_default_desc, /* MUST BE FIRST */\n");
+ printf("\t&vop_strategy_desc, /* XXX: SPECIAL CASE */\n");
+ printf("\t&vop_bwrite_desc, /* XXX: SPECIAL CASE */\n");
+ }
+ END {
+ printf("\tNULL\n};\n");
+ }
+ NF == 0 || $0 ~ "^#" {
+ next;
+ }
+ {
+ # Get the function name.
+ printf("\t&%s_desc,\n", $1);
+
+ # Skip the function arguments.
+ for (;;) {
+ if (getline <= 0)
+ exit
+ if ($0 ~ "^};")
+ break;
+ }
+ }' < $SRC >> $CFILE
+
diff --git a/sys/kern/vnode_if.sh b/sys/kern/vnode_if.sh
new file mode 100644
index 0000000..e190fa0
--- /dev/null
+++ b/sys/kern/vnode_if.sh
@@ -0,0 +1,433 @@
+#!/bin/sh -
+#
+# Copyright (c) 1992, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)vnode_if.sh 8.1 (Berkeley) 6/10/93
+#
+
+# Script to produce VFS front-end sugar.
+#
+# usage: vnode_if.sh srcfile
+# (where srcfile is currently /sys/kern/vnode_if.src)
+#
+# These awk scripts are not particularly well written, specifically they
+# don't use arrays well and figure out the same information repeatedly.
+# Please rewrite them if you actually understand how to use awk. Note,
+# they use nawk extensions and gawk's toupper.
+
+if [ $# -ne 1 ] ; then
+ echo 'usage: vnode_if.sh srcfile'
+ exit 1
+fi
+
+# Name of the source file.
+SRC=$1
+
+# Names of the created files.
+CFILE=vnode_if.c
+HEADER=vnode_if.h
+
+# Awk program (must support nawk extensions and gawk's "toupper")
+# Use "awk" at Berkeley, "gawk" elsewhere.
+AWK=awk
+
+# Print out header information for vnode_if.h.
+cat << END_OF_LEADING_COMMENT > $HEADER
+/*
+ * This file is produced automatically.
+ * Do not modify anything in here by hand.
+ *
+ * Created from @(#)vnode_if.sh 8.1 (Berkeley) 6/10/93
+ */
+
+extern struct vnodeop_desc vop_default_desc;
+END_OF_LEADING_COMMENT
+
+# Awk script to take vnode_if.src and turn it into vnode_if.h.
+$AWK '
+ NF == 0 || $0 ~ "^#" {
+ next;
+ }
+ {
+ # Get the function name.
+ name = $1;
+ uname = toupper(name);
+
+ # Get the function arguments.
+ for (c1 = 0;; ++c1) {
+ if (getline <= 0)
+ exit
+ if ($0 ~ "^};")
+ break;
+ a[c1] = $0;
+ }
+
+ # Print out the vop_F_args structure.
+ printf("struct %s_args {\n\tstruct vnodeop_desc *a_desc;\n",
+ name);
+ for (c2 = 0; c2 < c1; ++c2) {
+ c3 = split(a[c2], t);
+ printf("\t");
+ if (t[2] ~ "WILLRELE")
+ c4 = 3;
+ else
+ c4 = 2;
+ for (; c4 < c3; ++c4)
+ printf("%s ", t[c4]);
+ beg = match(t[c3], "[^*]");
+ printf("%sa_%s\n",
+ substr(t[c4], 0, beg - 1), substr(t[c4], beg));
+ }
+ printf("};\n");
+
+ # Print out extern declaration.
+ printf("extern struct vnodeop_desc %s_desc;\n", name);
+
+ # Print out inline struct.
+ printf("static inline int %s(", uname);
+ sep = ", ";
+ for (c2 = 0; c2 < c1; ++c2) {
+ if (c2 == c1 - 1)
+ sep = ")\n";
+ c3 = split(a[c2], t);
+ beg = match(t[c3], "[^*]");
+ end = match(t[c3], ";");
+ printf("%s%s", substr(t[c3], beg, end - beg), sep);
+ }
+ for (c2 = 0; c2 < c1; ++c2) {
+ c3 = split(a[c2], t);
+ printf("\t");
+ if (t[2] ~ "WILLRELE")
+ c4 = 3;
+ else
+ c4 = 2;
+ for (; c4 < c3; ++c4)
+ printf("%s ", t[c4]);
+ beg = match(t[c3], "[^*]");
+ printf("%s%s\n",
+ substr(t[c4], 0, beg - 1), substr(t[c4], beg));
+ }
+ printf("{\n\tstruct %s_args a;\n\n", name);
+ printf("\ta.a_desc = VDESC(%s);\n", name);
+ for (c2 = 0; c2 < c1; ++c2) {
+ c3 = split(a[c2], t);
+ printf("\t");
+ beg = match(t[c3], "[^*]");
+ end = match(t[c3], ";");
+ printf("a.a_%s = %s\n",
+ substr(t[c3], beg, end - beg), substr(t[c3], beg));
+ }
+ c1 = split(a[0], t);
+ beg = match(t[c1], "[^*]");
+ end = match(t[c1], ";");
+ printf("\treturn (VCALL(%s, VOFFSET(%s), &a));\n}\n",
+ substr(t[c1], beg, end - beg), name);
+ }' < $SRC >> $HEADER
+
+# Print out header information for vnode_if.c.
+cat << END_OF_LEADING_COMMENT > $CFILE
+/*
+ * This file is produced automatically.
+ * Do not modify anything in here by hand.
+ *
+ * Created from @(#)vnode_if.sh 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+
+struct vnodeop_desc vop_default_desc = {
+ 0,
+ "default",
+ 0,
+ NULL,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ NULL,
+};
+
+END_OF_LEADING_COMMENT
+
+# Awk script to take vnode_if.src and turn it into vnode_if.c.
+$AWK 'function kill_surrounding_ws (s) {
+ sub (/^[ \t]*/, "", s);
+ sub (/[ \t]*$/, "", s);
+ return s;
+ }
+
+ function read_args() {
+ numargs = 0;
+ while (getline ln) {
+ if (ln ~ /}/) {
+ break;
+ };
+
+ # Delete comments, if any.
+ gsub (/\/\*.*\*\//, "", ln);
+
+ # Delete leading/trailing space.
+ ln = kill_surrounding_ws(ln);
+
+ # Pick off direction.
+ if (1 == sub(/^INOUT[ \t]+/, "", ln))
+ dir = "INOUT";
+ else if (1 == sub(/^IN[ \t]+/, "", ln))
+ dir = "IN";
+ else if (1 == sub(/^OUT[ \t]+/, "", ln))
+ dir = "OUT";
+ else
+ bail("No IN/OUT direction for \"" ln "\".");
+
+ # check for "WILLRELE"
+ if (1 == sub(/^WILLRELE[ \t]+/, "", ln)) {
+ rele = "WILLRELE";
+ } else {
+ rele = "WONTRELE";
+ };
+
+ # kill trailing ;
+ if (1 != sub (/;$/, "", ln)) {
+ bail("Missing end-of-line ; in \"" ln "\".");
+ };
+
+ # pick off variable name
+ if (!(i = match(ln, /[A-Za-z0-9_]+$/))) {
+ bail("Missing var name \"a_foo\" in \"" ln "\".");
+ };
+ arg = substr (ln, i);
+ # Want to <<substr(ln, i) = "";>>, but nawk cannot.
+ # Hack around this.
+ ln = substr(ln, 1, i-1);
+
+ # what is left must be type
+ # (put clean it up some)
+ type = ln;
+ gsub (/[ \t]+/, " ", type); # condense whitespace
+ type = kill_surrounding_ws(type);
+
+ # (boy this was easier in Perl)
+
+ numargs++;
+ dirs[numargs] = dir;
+ reles[numargs] = rele;
+ types[numargs] = type;
+ args[numargs] = arg;
+ };
+ }
+
+ function generate_operation_vp_offsets() {
+ printf ("int %s_vp_offsets[] = {\n", name);
+ # as a side effect, figure out the releflags
+ releflags = "";
+ vpnum = 0;
+ for (i=1; i<=numargs; i++) {
+ if (types[i] == "struct vnode *") {
+ printf ("\tVOPARG_OFFSETOF(struct %s_args,a_%s),\n",
+ name, args[i]);
+ if (reles[i] == "WILLRELE") {
+ releflags = releflags "|VDESC_VP" vpnum "_WILLRELE";
+ };
+ vpnum++;
+ };
+ };
+ sub (/^\|/, "", releflags);
+ print "\tVDESC_NO_OFFSET";
+ print "};";
+ }
+
+ function find_arg_with_type (type) {
+ for (i=1; i<=numargs; i++) {
+ if (types[i] == type) {
+ return "VOPARG_OFFSETOF(struct " name "_args,a_" args[i] ")";
+ };
+ };
+ return "VDESC_NO_OFFSET";
+ }
+
+ function generate_operation_desc() {
+ printf ("struct vnodeop_desc %s_desc = {\n", name);
+ # offset
+ printf ("\t0,\n");
+ # printable name
+ printf ("\t\"%s\",\n", name);
+ # flags
+ vppwillrele = "";
+ for (i=1; i<=numargs; i++) {
+ if (types[i] == "struct vnode **" &&
+ (reles[i] == "WILLRELE")) {
+ vppwillrele = "|VDESC_VPP_WILLRELE";
+ };
+ };
+ if (releflags == "") {
+ printf ("\t0%s,\n", vppwillrele);
+ } else {
+ printf ("\t%s%s,\n", releflags, vppwillrele);
+ };
+ # vp offsets
+ printf ("\t%s_vp_offsets,\n", name);
+ # vpp (if any)
+ printf ("\t%s,\n", find_arg_with_type("struct vnode **"));
+ # cred (if any)
+ printf ("\t%s,\n", find_arg_with_type("struct ucred *"));
+ # proc (if any)
+ printf ("\t%s,\n", find_arg_with_type("struct proc *"));
+ # componentname
+ printf ("\t%s,\n", find_arg_with_type("struct componentname *"));
+ # transport layer information
+ printf ("\tNULL,\n};\n");
+ }
+
+ NF == 0 || $0 ~ "^#" {
+ next;
+ }
+ {
+ # get the function name
+ name = $1;
+
+ # get the function arguments
+ read_args();
+
+ # Print out the vop_F_vp_offsets structure. This all depends
+ # on naming conventions and nothing else.
+ generate_operation_vp_offsets();
+
+ # Print out the vnodeop_desc structure.
+ generate_operation_desc();
+
+ printf "\n";
+
+ }' < $SRC >> $CFILE
+# THINGS THAT DON'T WORK RIGHT YET.
+#
+# Two existing BSD vnodeops (bwrite and strategy) don't take any vnodes as
+# arguments. This means that these operations can't function successfully
+# through a bypass routine.
+#
+# Bwrite and strategy will be replaced when the VM page/buffer cache
+# integration happens.
+#
+# To get around this problem for now we handle these ops as special cases.
+
+cat << END_OF_SPECIAL_CASES >> $HEADER
+#include <sys/buf.h>
+struct vop_strategy_args {
+ struct vnodeop_desc *a_desc;
+ struct buf *a_bp;
+};
+extern struct vnodeop_desc vop_strategy_desc;
+static inline int VOP_STRATEGY(bp)
+ struct buf *bp;
+{
+ struct vop_strategy_args a;
+
+ a.a_desc = VDESC(vop_strategy);
+ a.a_bp = bp;
+ return (VCALL((bp)->b_vp, VOFFSET(vop_strategy), &a));
+}
+
+struct vop_bwrite_args {
+ struct vnodeop_desc *a_desc;
+ struct buf *a_bp;
+};
+extern struct vnodeop_desc vop_bwrite_desc;
+static inline int VOP_BWRITE(bp)
+ struct buf *bp;
+{
+ struct vop_bwrite_args a;
+
+ a.a_desc = VDESC(vop_bwrite);
+ a.a_bp = bp;
+ return (VCALL((bp)->b_vp, VOFFSET(vop_bwrite), &a));
+}
+END_OF_SPECIAL_CASES
+
+cat << END_OF_SPECIAL_CASES >> $CFILE
+int vop_strategy_vp_offsets[] = {
+ VDESC_NO_OFFSET
+};
+struct vnodeop_desc vop_strategy_desc = {
+ 0,
+ "vop_strategy",
+ 0,
+ vop_strategy_vp_offsets,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ NULL,
+};
+int vop_bwrite_vp_offsets[] = {
+ VDESC_NO_OFFSET
+};
+struct vnodeop_desc vop_bwrite_desc = {
+ 0,
+ "vop_bwrite",
+ 0,
+ vop_bwrite_vp_offsets,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ NULL,
+};
+END_OF_SPECIAL_CASES
+
+# Add the vfs_op_descs array to the C file.
+$AWK '
+ BEGIN {
+ printf("\nstruct vnodeop_desc *vfs_op_descs[] = {\n");
+ printf("\t&vop_default_desc, /* MUST BE FIRST */\n");
+ printf("\t&vop_strategy_desc, /* XXX: SPECIAL CASE */\n");
+ printf("\t&vop_bwrite_desc, /* XXX: SPECIAL CASE */\n");
+ }
+ END {
+ printf("\tNULL\n};\n");
+ }
+ NF == 0 || $0 ~ "^#" {
+ next;
+ }
+ {
+ # Get the function name.
+ printf("\t&%s_desc,\n", $1);
+
+ # Skip the function arguments.
+ for (;;) {
+ if (getline <= 0)
+ exit
+ if ($0 ~ "^};")
+ break;
+ }
+ }' < $SRC >> $CFILE
+
diff --git a/sys/kern/vnode_if.src b/sys/kern/vnode_if.src
new file mode 100644
index 0000000..caee21d
--- /dev/null
+++ b/sys/kern/vnode_if.src
@@ -0,0 +1,296 @@
+#
+# Copyright (c) 1992, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)vnode_if.src 8.3 (Berkeley) 2/3/94
+#
+vop_lookup {
+ IN struct vnode *dvp;
+ INOUT struct vnode **vpp;
+ IN struct componentname *cnp;
+};
+
+vop_create {
+ IN WILLRELE struct vnode *dvp;
+ OUT struct vnode **vpp;
+ IN struct componentname *cnp;
+ IN struct vattr *vap;
+};
+
+vop_mknod {
+ IN WILLRELE struct vnode *dvp;
+ OUT WILLRELE struct vnode **vpp;
+ IN struct componentname *cnp;
+ IN struct vattr *vap;
+};
+
+vop_open {
+ IN struct vnode *vp;
+ IN int mode;
+ IN struct ucred *cred;
+ IN struct proc *p;
+};
+
+vop_close {
+ IN struct vnode *vp;
+ IN int fflag;
+ IN struct ucred *cred;
+ IN struct proc *p;
+};
+
+vop_access {
+ IN struct vnode *vp;
+ IN int mode;
+ IN struct ucred *cred;
+ IN struct proc *p;
+};
+
+vop_getattr {
+ IN struct vnode *vp;
+ IN struct vattr *vap;
+ IN struct ucred *cred;
+ IN struct proc *p;
+};
+
+vop_setattr {
+ IN struct vnode *vp;
+ IN struct vattr *vap;
+ IN struct ucred *cred;
+ IN struct proc *p;
+};
+
+vop_read {
+ IN struct vnode *vp;
+ INOUT struct uio *uio;
+ IN int ioflag;
+ IN struct ucred *cred;
+};
+
+vop_write {
+ IN struct vnode *vp;
+ INOUT struct uio *uio;
+ IN int ioflag;
+ IN struct ucred *cred;
+};
+
+vop_ioctl {
+ IN struct vnode *vp;
+ IN int command;
+ IN caddr_t data;
+ IN int fflag;
+ IN struct ucred *cred;
+ IN struct proc *p;
+};
+
+# Needs work? (fflags)
+vop_select {
+ IN struct vnode *vp;
+ IN int which;
+ IN int fflags;
+ IN struct ucred *cred;
+ IN struct proc *p;
+};
+
+vop_mmap {
+ IN struct vnode *vp;
+ IN int fflags;
+ IN struct ucred *cred;
+ IN struct proc *p;
+};
+
+vop_fsync {
+ IN struct vnode *vp;
+ IN struct ucred *cred;
+ IN int waitfor;
+ IN struct proc *p;
+};
+
+# Needs word: Is newoff right? What's it mean?
+vop_seek {
+ IN struct vnode *vp;
+ IN off_t oldoff;
+ IN off_t newoff;
+ IN struct ucred *cred;
+};
+
+vop_remove {
+ IN WILLRELE struct vnode *dvp;
+ IN WILLRELE struct vnode *vp;
+ IN struct componentname *cnp;
+};
+
+vop_link {
+ IN WILLRELE struct vnode *vp;
+ IN struct vnode *tdvp;
+ IN struct componentname *cnp;
+};
+
+vop_rename {
+ IN WILLRELE struct vnode *fdvp;
+ IN WILLRELE struct vnode *fvp;
+ IN struct componentname *fcnp;
+ IN WILLRELE struct vnode *tdvp;
+ IN WILLRELE struct vnode *tvp;
+ IN struct componentname *tcnp;
+};
+
+vop_mkdir {
+ IN WILLRELE struct vnode *dvp;
+ OUT struct vnode **vpp;
+ IN struct componentname *cnp;
+ IN struct vattr *vap;
+};
+
+vop_rmdir {
+ IN WILLRELE struct vnode *dvp;
+ IN WILLRELE struct vnode *vp;
+ IN struct componentname *cnp;
+};
+
+vop_symlink {
+ IN WILLRELE struct vnode *dvp;
+ OUT WILLRELE struct vnode **vpp;
+ IN struct componentname *cnp;
+ IN struct vattr *vap;
+ IN char *target;
+};
+
+vop_readdir {
+ IN struct vnode *vp;
+ INOUT struct uio *uio;
+ IN struct ucred *cred;
+};
+
+vop_readlink {
+ IN struct vnode *vp;
+ INOUT struct uio *uio;
+ IN struct ucred *cred;
+};
+
+vop_abortop {
+ IN struct vnode *dvp;
+ IN struct componentname *cnp;
+};
+
+vop_inactive {
+ IN struct vnode *vp;
+};
+
+vop_reclaim {
+ IN struct vnode *vp;
+};
+
+vop_lock {
+ IN struct vnode *vp;
+};
+
+vop_unlock {
+ IN struct vnode *vp;
+};
+
+vop_bmap {
+ IN struct vnode *vp;
+ IN daddr_t bn;
+ OUT struct vnode **vpp;
+ IN daddr_t *bnp;
+ OUT int *runp;
+};
+
+#vop_strategy {
+# IN struct buf *bp;
+#};
+
+vop_print {
+ IN struct vnode *vp;
+};
+
+vop_islocked {
+ IN struct vnode *vp;
+};
+
+vop_pathconf {
+ IN struct vnode *vp;
+ IN int name;
+ OUT int *retval;
+};
+
+vop_advlock {
+ IN struct vnode *vp;
+ IN caddr_t id;
+ IN int op;
+ IN struct flock *fl;
+ IN int flags;
+};
+
+vop_blkatoff {
+ IN struct vnode *vp;
+ IN off_t offset;
+ OUT char **res;
+ OUT struct buf **bpp;
+};
+
+vop_valloc {
+ IN struct vnode *pvp;
+ IN int mode;
+ IN struct ucred *cred;
+ OUT struct vnode **vpp;
+};
+
+vop_reallocblks {
+ IN struct vnode *vp;
+ IN struct cluster_save *buflist;
+};
+
+vop_vfree {
+ IN struct vnode *pvp;
+ IN ino_t ino;
+ IN int mode;
+};
+
+vop_truncate {
+ IN struct vnode *vp;
+ IN off_t length;
+ IN int flags;
+ IN struct ucred *cred;
+ IN struct proc *p;
+};
+
+vop_update {
+ IN struct vnode *vp;
+ IN struct timeval *access;
+ IN struct timeval *modify;
+ IN int waitfor;
+};
+
+# Needs work: no vp?
+#vop_bwrite {
+# IN struct buf *bp;
+#};
diff --git a/sys/libkern/Makefile b/sys/libkern/Makefile
new file mode 100644
index 0000000..991a435
--- /dev/null
+++ b/sys/libkern/Makefile
@@ -0,0 +1,20 @@
+# @(#)Makefile 7.9 (Berkeley) 6/1/93
+
+LIB= kern
+CFLAGS+= -I${.CURDIR} -I${.CURDIR}/..
+SRCS= adddi3.c anddi3.c ashldi3.c ashrdi3.c bcmp.c cmpdi2.c divdi3.c \
+ ffs.c iordi3.c locc.c lshldi3.c lshrdi3.c mcount.c moddi3.c \
+ muldi3.c negdi2.c notdi2.c qdivrem.c random.c rindex.c scanc.c \
+ skpc.c strcat.c strcmp.c strcpy.c strlen.c strncpy.c subdi3.c \
+ ucmpdi2.c udivdi3.c umoddi3.c xordi3.c
+
+.if exists(${.CURDIR}/${MACHINE}/Makefile.inc)
+.PATH: ${.CURDIR}/${MACHINE}
+.include "${.CURDIR}/${MACHINE}/Makefile.inc"
+.endif
+
+# mcount cannot be compiled with profiling
+mcount.po: mcount.o
+ cp mcount.o mcount.po
+
+.include <bsd.lib.mk>
diff --git a/sys/libkern/adddi3.c b/sys/libkern/adddi3.c
new file mode 100644
index 0000000..d10da47
--- /dev/null
+++ b/sys/libkern/adddi3.c
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)adddi3.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Add two quads. This is trivial since a one-bit carry from a single
+ * u_long addition x+y occurs if and only if the sum x+y is less than
+ * either x or y (the choice to compare with x or y is arbitrary).
+ */
+quad_t
+__adddi3(a, b)
+ quad_t a, b;
+{
+ union uu aa, bb, sum;
+
+ aa.q = a;
+ bb.q = b;
+ sum.ul[L] = aa.ul[L] + bb.ul[L];
+ sum.ul[H] = aa.ul[H] + bb.ul[H] + (sum.ul[L] < bb.ul[L]);
+ return (sum.q);
+}
diff --git a/sys/libkern/anddi3.c b/sys/libkern/anddi3.c
new file mode 100644
index 0000000..5ae45ac
--- /dev/null
+++ b/sys/libkern/anddi3.c
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)anddi3.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Return a & b, in quad.
+ */
+quad_t
+__anddi3(a, b)
+ quad_t a, b;
+{
+ union uu aa, bb;
+
+ aa.q = a;
+ bb.q = b;
+ aa.ul[0] &= bb.ul[0];
+ aa.ul[1] &= bb.ul[1];
+ return (aa.q);
+}
diff --git a/sys/libkern/ashldi3.c b/sys/libkern/ashldi3.c
new file mode 100644
index 0000000..72501ad
--- /dev/null
+++ b/sys/libkern/ashldi3.c
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)ashldi3.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Shift a (signed) quad value left (arithmetic shift left).
+ * This is the same as logical shift left!
+ */
+quad_t
+__ashldi3(a, shift)
+ quad_t a;
+ qshift_t shift;
+{
+ union uu aa;
+
+ aa.q = a;
+ if (shift >= LONG_BITS) {
+ aa.ul[H] = shift >= QUAD_BITS ? 0 :
+ aa.ul[L] << (shift - LONG_BITS);
+ aa.ul[L] = 0;
+ } else if (shift > 0) {
+ aa.ul[H] = (aa.ul[H] << shift) |
+ (aa.ul[L] >> (LONG_BITS - shift));
+ aa.ul[L] <<= shift;
+ }
+ return (aa.q);
+}
diff --git a/sys/libkern/ashrdi3.c b/sys/libkern/ashrdi3.c
new file mode 100644
index 0000000..9ffa5ed
--- /dev/null
+++ b/sys/libkern/ashrdi3.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)ashrdi3.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Shift a (signed) quad value right (arithmetic shift right).
+ */
+quad_t
+__ashrdi3(a, shift)
+ quad_t a;
+ qshift_t shift;
+{
+ union uu aa;
+
+ aa.q = a;
+ if (shift >= LONG_BITS) {
+ long s;
+
+ /*
+ * Smear bits rightward using the machine's right-shift
+ * method, whether that is sign extension or zero fill,
+ * to get the `sign word' s. Note that shifting by
+ * LONG_BITS is undefined, so we shift (LONG_BITS-1),
+ * then 1 more, to get our answer.
+ */
+ s = (aa.sl[H] >> (LONG_BITS - 1)) >> 1;
+ aa.ul[L] = shift >= QUAD_BITS ? s :
+ aa.sl[H] >> (shift - LONG_BITS);
+ aa.ul[H] = s;
+ } else if (shift > 0) {
+ aa.ul[L] = (aa.ul[L] >> shift) |
+ (aa.ul[H] << (LONG_BITS - shift));
+ aa.sl[H] >>= shift;
+ }
+ return (aa.q);
+}
diff --git a/sys/libkern/bcmp.c b/sys/libkern/bcmp.c
new file mode 100644
index 0000000..5a3ae61
--- /dev/null
+++ b/sys/libkern/bcmp.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bcmp.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <string.h>
+
+/*
+ * bcmp -- vax cmpc3 instruction
+ */
+int
+bcmp(b1, b2, length)
+ const void *b1, *b2;
+ register size_t length;
+{
+ register char *p1, *p2;
+
+ if (length == 0)
+ return(0);
+ p1 = (char *)b1;
+ p2 = (char *)b2;
+ do
+ if (*p1++ != *p2++)
+ break;
+ while (--length);
+ return(length);
+}
diff --git a/sys/libkern/cmpdi2.c b/sys/libkern/cmpdi2.c
new file mode 100644
index 0000000..f6e4bdd
--- /dev/null
+++ b/sys/libkern/cmpdi2.c
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)cmpdi2.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Return 0, 1, or 2 as a <, =, > b respectively.
+ * Both a and b are considered signed---which means only the high word is
+ * signed.
+ */
+int
+__cmpdi2(a, b)
+ quad_t a, b;
+{
+ union uu aa, bb;
+
+ aa.q = a;
+ bb.q = b;
+ return (aa.sl[H] < bb.sl[H] ? 0 : aa.sl[H] > bb.sl[H] ? 2 :
+ aa.ul[L] < bb.ul[L] ? 0 : aa.ul[L] > bb.ul[L] ? 2 : 1);
+}
diff --git a/sys/libkern/divdi3.c b/sys/libkern/divdi3.c
new file mode 100644
index 0000000..da7b2fc
--- /dev/null
+++ b/sys/libkern/divdi3.c
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)divdi3.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Divide two signed quads.
+ * ??? if -1/2 should produce -1 on this machine, this code is wrong
+ */
+quad_t
+__divdi3(a, b)
+ quad_t a, b;
+{
+ u_quad_t ua, ub, uq;
+ int neg;
+
+ if (a < 0)
+ ua = -(u_quad_t)a, neg = 1;
+ else
+ ua = a, neg = 0;
+ if (b < 0)
+ ub = -(u_quad_t)b, neg ^= 1;
+ else
+ ub = b;
+ uq = __qdivrem(ua, ub, (u_quad_t *)0);
+ return (neg ? -uq : uq);
+}
diff --git a/sys/libkern/ffs.c b/sys/libkern/ffs.c
new file mode 100644
index 0000000..099ff8e
--- /dev/null
+++ b/sys/libkern/ffs.c
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)ffs.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <string.h>
+
+/*
+ * ffs -- vax ffs instruction
+ */
+int
+ffs(mask)
+ register int mask;
+{
+ register int bit;
+
+ if (mask == 0)
+ return(0);
+ for (bit = 1; !(mask & 1); bit++)
+ mask >>= 1;
+ return(bit);
+}
diff --git a/sys/libkern/fnmatch.c b/sys/libkern/fnmatch.c
new file mode 100644
index 0000000..dc7f151
--- /dev/null
+++ b/sys/libkern/fnmatch.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)fnmatch.c 8.2 (Berkeley) 4/16/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
+ * Compares a filename or pathname to a pattern.
+ */
+
+#include <fnmatch.h>
+#include <string.h>
+
+#define EOS '\0'
+
+static const char *rangematch __P((const char *, int, int));
+
+int
+fnmatch(pattern, string, flags)
+ const char *pattern, *string;
+ int flags;
+{
+ const char *stringstart;
+ char c, test;
+
+ for (stringstart = string;;)
+ switch (c = *pattern++) {
+ case EOS:
+ return (*string == EOS ? 0 : FNM_NOMATCH);
+ case '?':
+ if (*string == EOS)
+ return (FNM_NOMATCH);
+ if (*string == '/' && (flags & FNM_PATHNAME))
+ return (FNM_NOMATCH);
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return (FNM_NOMATCH);
+ ++string;
+ break;
+ case '*':
+ c = *pattern;
+ /* Collapse multiple stars. */
+ while (c == '*')
+ c = *++pattern;
+
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return (FNM_NOMATCH);
+
+ /* Optimize for pattern with * at end or before /. */
+ if (c == EOS)
+ if (flags & FNM_PATHNAME)
+ return (strchr(string, '/') == NULL ?
+ 0 : FNM_NOMATCH);
+ else
+ return (0);
+ else if (c == '/' && flags & FNM_PATHNAME) {
+ if ((string = strchr(string, '/')) == NULL)
+ return (FNM_NOMATCH);
+ break;
+ }
+
+ /* General case, use recursion. */
+ while ((test = *string) != EOS) {
+ if (!fnmatch(pattern, string, flags & ~FNM_PERIOD))
+ return (0);
+ if (test == '/' && flags & FNM_PATHNAME)
+ break;
+ ++string;
+ }
+ return (FNM_NOMATCH);
+ case '[':
+ if (*string == EOS)
+ return (FNM_NOMATCH);
+ if (*string == '/' && flags & FNM_PATHNAME)
+ return (FNM_NOMATCH);
+ if ((pattern =
+ rangematch(pattern, *string, flags)) == NULL)
+ return (FNM_NOMATCH);
+ ++string;
+ break;
+ case '\\':
+ if (!(flags & FNM_NOESCAPE)) {
+ if ((c = *pattern++) == EOS) {
+ c = '\\';
+ --pattern;
+ }
+ }
+ /* FALLTHROUGH */
+ default:
+ if (c != *string++)
+ return (FNM_NOMATCH);
+ break;
+ }
+ /* NOTREACHED */
+}
+
+static const char *
+rangematch(pattern, test, flags)
+ const char *pattern;
+ int test, flags;
+{
+ int negate, ok;
+ char c, c2;
+
+ /*
+ * A bracket expression starting with an unquoted circumflex
+ * character produces unspecified results (IEEE 1003.2-1992,
+ * 3.13.2). This implementation treats it like '!', for
+ * consistency with the regular expression syntax.
+ * J.T. Conklin (conklin@ngai.kaleida.com)
+ */
+ if (negate = (*pattern == '!' || *pattern == '^'))
+ ++pattern;
+
+ for (ok = 0; (c = *pattern++) != ']';) {
+ if (c == '\\' && !(flags & FNM_NOESCAPE))
+ c = *pattern++;
+ if (c == EOS)
+ return (NULL);
+ if (*pattern == '-'
+ && (c2 = *(pattern+1)) != EOS && c2 != ']') {
+ pattern += 2;
+ if (c2 == '\\' && !(flags & FNM_NOESCAPE))
+ c2 = *pattern++;
+ if (c2 == EOS)
+ return (NULL);
+ if (c <= test && test <= c2)
+ ok = 1;
+ } else if (c == test)
+ ok = 1;
+ }
+ return (ok == negate ? NULL : pattern);
+}
diff --git a/sys/libkern/iordi3.c b/sys/libkern/iordi3.c
new file mode 100644
index 0000000..e225005
--- /dev/null
+++ b/sys/libkern/iordi3.c
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)iordi3.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Return a | b, in quad.
+ */
+quad_t
+__iordi3(a, b)
+ quad_t a, b;
+{
+ union uu aa, bb;
+
+ aa.q = a;
+ bb.q = b;
+ aa.ul[0] |= bb.ul[0];
+ aa.ul[1] |= bb.ul[1];
+ return (aa.q);
+}
diff --git a/sys/libkern/libkern.h b/sys/libkern/libkern.h
new file mode 100644
index 0000000..0e465e0
--- /dev/null
+++ b/sys/libkern/libkern.h
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)libkern.h 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/types.h>
+
+static inline int
+imax(a, b)
+ int a, b;
+{
+ return (a > b ? a : b);
+}
+static inline int
+imin(a, b)
+ int a, b;
+{
+ return (a < b ? a : b);
+}
+static inline long
+lmax(a, b)
+ long a, b;
+{
+ return (a > b ? a : b);
+}
+static inline long
+lmin(a, b)
+ long a, b;
+{
+ return (a < b ? a : b);
+}
+static inline u_int
+max(a, b)
+ u_int a, b;
+{
+ return (a > b ? a : b);
+}
+static inline u_int
+min(a, b)
+ u_int a, b;
+{
+ return (a < b ? a : b);
+}
+static inline u_long
+ulmax(a, b)
+ u_long a, b;
+{
+ return (a > b ? a : b);
+}
+static inline u_long
+ulmin(a, b)
+ u_long a, b;
+{
+ return (a < b ? a : b);
+}
+
+/* Prototypes for non-quad routines. */
+int bcmp __P((const void *, const void *, size_t));
+int ffs __P((int));
+int locc __P((int, char *, u_int));
+u_long random __P((void));
+char *rindex __P((const char *, int));
+int scanc __P((u_int, u_char *, u_char *, int));
+int skpc __P((int, int, char *));
+char *strcat __P((char *, const char *));
+char *strcpy __P((char *, const char *));
+size_t strlen __P((const char *));
+char *strncpy __P((char *, const char *, size_t));
diff --git a/sys/libkern/locc.c b/sys/libkern/locc.c
new file mode 100644
index 0000000..3767222
--- /dev/null
+++ b/sys/libkern/locc.c
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)locc.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <libkern/libkern.h>
+
+int
+locc(mask0, cp0, size)
+ int mask0;
+ char *cp0;
+ u_int size;
+{
+ register u_char *cp, *end, mask;
+
+ mask = mask0;
+ cp = (u_char *)cp0;
+ for (end = &cp[size]; cp < end && *cp != mask; ++cp);
+ return (end - cp);
+}
diff --git a/sys/libkern/lshldi3.c b/sys/libkern/lshldi3.c
new file mode 100644
index 0000000..0af6051
--- /dev/null
+++ b/sys/libkern/lshldi3.c
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)lshldi3.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Shift an (unsigned) quad value left (logical shift left).
+ * This is the same as arithmetic shift left!
+ */
+quad_t
+__lshldi3(a, shift)
+ quad_t a;
+ qshift_t shift;
+{
+ union uu aa;
+
+ aa.q = a;
+ if (shift >= LONG_BITS) {
+ aa.ul[H] = shift >= QUAD_BITS ? 0 :
+ aa.ul[L] << (shift - LONG_BITS);
+ aa.ul[L] = 0;
+ } else if (shift > 0) {
+ aa.ul[H] = (aa.ul[H] << shift) |
+ (aa.ul[L] >> (LONG_BITS - shift));
+ aa.ul[L] <<= shift;
+ }
+ return (aa.q);
+}
diff --git a/sys/libkern/lshrdi3.c b/sys/libkern/lshrdi3.c
new file mode 100644
index 0000000..add2eda
--- /dev/null
+++ b/sys/libkern/lshrdi3.c
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)lshrdi3.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Shift an (unsigned) quad value right (logical shift right).
+ */
+quad_t
+__lshrdi3(a, shift)
+ quad_t a;
+ qshift_t shift;
+{
+ union uu aa;
+
+ aa.q = a;
+ if (shift >= LONG_BITS) {
+ aa.ul[L] = shift >= QUAD_BITS ? 0 :
+ aa.ul[H] >> (shift - LONG_BITS);
+ aa.ul[H] = 0;
+ } else if (shift > 0) {
+ aa.ul[L] = (aa.ul[L] >> shift) |
+ (aa.ul[H] << (LONG_BITS - shift));
+ aa.ul[H] >>= shift;
+ }
+ return (aa.q);
+}
diff --git a/sys/libkern/mcount.c b/sys/libkern/mcount.c
new file mode 100644
index 0000000..523217d
--- /dev/null
+++ b/sys/libkern/mcount.c
@@ -0,0 +1,178 @@
+/*-
+ * Copyright (c) 1983, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !defined(lint) && !defined(KERNEL) && defined(LIBC_SCCS)
+static char sccsid[] = "@(#)mcount.c 8.1 (Berkeley) 6/4/93";
+#endif
+
+#include <sys/param.h>
+#include <sys/gmon.h>
+
+/*
+ * mcount is called on entry to each function compiled with the profiling
+ * switch set. _mcount(), which is declared in a machine-dependent way
+ * with _MCOUNT_DECL, does the actual work and is either inlined into a
+ * C routine or called by an assembly stub. In any case, this magic is
+ * taken care of by the MCOUNT definition in <machine/profile.h>.
+ *
+ * _mcount updates data structures that represent traversals of the
+ * program's call graph edges. frompc and selfpc are the return
+ * address and function address that represents the given call graph edge.
+ *
+ * Note: the original BSD code used the same variable (frompcindex) for
+ * both frompcindex and frompc. Any reasonable, modern compiler will
+ * perform this optimization.
+ */
+_MCOUNT_DECL(frompc, selfpc) /* _mcount; may be static, inline, etc */
+ register u_long frompc, selfpc;
+{
+ register u_short *frompcindex;
+ register struct tostruct *top, *prevtop;
+ register struct gmonparam *p;
+ register long toindex;
+#ifdef KERNEL
+ register int s;
+#endif
+
+ p = &_gmonparam;
+ /*
+ * check that we are profiling
+ * and that we aren't recursively invoked.
+ */
+ if (p->state != GMON_PROF_ON)
+ return;
+#ifdef KERNEL
+ MCOUNT_ENTER;
+#else
+ p->state = GMON_PROF_BUSY;
+#endif
+ /*
+ * check that frompcindex is a reasonable pc value.
+ * for example: signal catchers get called from the stack,
+ * not from text space. too bad.
+ */
+ frompc -= p->lowpc;
+ if (frompc > p->textsize)
+ goto done;
+
+ frompcindex = &p->froms[frompc / (p->hashfraction * sizeof(*p->froms))];
+ toindex = *frompcindex;
+ if (toindex == 0) {
+ /*
+ * first time traversing this arc
+ */
+ toindex = ++p->tos[0].link;
+ if (toindex >= p->tolimit)
+ /* halt further profiling */
+ goto overflow;
+
+ *frompcindex = toindex;
+ top = &p->tos[toindex];
+ top->selfpc = selfpc;
+ top->count = 1;
+ top->link = 0;
+ goto done;
+ }
+ top = &p->tos[toindex];
+ if (top->selfpc == selfpc) {
+ /*
+ * arc at front of chain; usual case.
+ */
+ top->count++;
+ goto done;
+ }
+ /*
+ * have to go looking down chain for it.
+ * top points to what we are looking at,
+ * prevtop points to previous top.
+ * we know it is not at the head of the chain.
+ */
+ for (; /* goto done */; ) {
+ if (top->link == 0) {
+ /*
+ * top is end of the chain and none of the chain
+ * had top->selfpc == selfpc.
+ * so we allocate a new tostruct
+ * and link it to the head of the chain.
+ */
+ toindex = ++p->tos[0].link;
+ if (toindex >= p->tolimit)
+ goto overflow;
+
+ top = &p->tos[toindex];
+ top->selfpc = selfpc;
+ top->count = 1;
+ top->link = *frompcindex;
+ *frompcindex = toindex;
+ goto done;
+ }
+ /*
+ * otherwise, check the next arc on the chain.
+ */
+ prevtop = top;
+ top = &p->tos[top->link];
+ if (top->selfpc == selfpc) {
+ /*
+ * there it is.
+ * increment its count
+ * move it to the head of the chain.
+ */
+ top->count++;
+ toindex = prevtop->link;
+ prevtop->link = top->link;
+ top->link = *frompcindex;
+ *frompcindex = toindex;
+ goto done;
+ }
+
+ }
+done:
+#ifdef KERNEL
+ MCOUNT_EXIT;
+#else
+ p->state = GMON_PROF_ON;
+#endif
+ return;
+overflow:
+ p->state = GMON_PROF_ERROR;
+#ifdef KERNEL
+ MCOUNT_EXIT;
+#endif
+ return;
+}
+
+/*
+ * Actual definition of mcount function. Defined in <machine/profile.h>,
+ * which is included by <sys/gmon.h>.
+ */
+MCOUNT
diff --git a/sys/libkern/moddi3.c b/sys/libkern/moddi3.c
new file mode 100644
index 0000000..f31c6e8
--- /dev/null
+++ b/sys/libkern/moddi3.c
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)moddi3.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Return remainder after dividing two signed quads.
+ *
+ * XXX
+ * If -1/2 should produce -1 on this machine, this code is wrong.
+ */
+quad_t
+__moddi3(a, b)
+ quad_t a, b;
+{
+ u_quad_t ua, ub, ur;
+ int neg;
+
+ if (a < 0)
+ ua = -(u_quad_t)a, neg = 1;
+ else
+ ua = a, neg = 0;
+ if (b < 0)
+ ub = -(u_quad_t)b, neg ^= 1;
+ else
+ ub = b;
+ (void)__qdivrem(ua, ub, &ur);
+ return (neg ? -ur : ur);
+}
diff --git a/sys/libkern/muldi3.c b/sys/libkern/muldi3.c
new file mode 100644
index 0000000..a8d7cfc
--- /dev/null
+++ b/sys/libkern/muldi3.c
@@ -0,0 +1,246 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)muldi3.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Multiply two quads.
+ *
+ * Our algorithm is based on the following. Split incoming quad values
+ * u and v (where u,v >= 0) into
+ *
+ * u = 2^n u1 * u0 (n = number of bits in `u_long', usu. 32)
+ *
+ * and
+ *
+ * v = 2^n v1 * v0
+ *
+ * Then
+ *
+ * uv = 2^2n u1 v1 + 2^n u1 v0 + 2^n v1 u0 + u0 v0
+ * = 2^2n u1 v1 + 2^n (u1 v0 + v1 u0) + u0 v0
+ *
+ * Now add 2^n u1 v1 to the first term and subtract it from the middle,
+ * and add 2^n u0 v0 to the last term and subtract it from the middle.
+ * This gives:
+ *
+ * uv = (2^2n + 2^n) (u1 v1) +
+ * (2^n) (u1 v0 - u1 v1 + u0 v1 - u0 v0) +
+ * (2^n + 1) (u0 v0)
+ *
+ * Factoring the middle a bit gives us:
+ *
+ * uv = (2^2n + 2^n) (u1 v1) + [u1v1 = high]
+ * (2^n) (u1 - u0) (v0 - v1) + [(u1-u0)... = mid]
+ * (2^n + 1) (u0 v0) [u0v0 = low]
+ *
+ * The terms (u1 v1), (u1 - u0) (v0 - v1), and (u0 v0) can all be done
+ * in just half the precision of the original. (Note that either or both
+ * of (u1 - u0) or (v0 - v1) may be negative.)
+ *
+ * This algorithm is from Knuth vol. 2 (2nd ed), section 4.3.3, p. 278.
+ *
+ * Since C does not give us a `long * long = quad' operator, we split
+ * our input quads into two longs, then split the two longs into two
+ * shorts. We can then calculate `short * short = long' in native
+ * arithmetic.
+ *
+ * Our product should, strictly speaking, be a `long quad', with 128
+ * bits, but we are going to discard the upper 64. In other words,
+ * we are not interested in uv, but rather in (uv mod 2^2n). This
+ * makes some of the terms above vanish, and we get:
+ *
+ * (2^n)(high) + (2^n)(mid) + (2^n + 1)(low)
+ *
+ * or
+ *
+ * (2^n)(high + mid + low) + low
+ *
+ * Furthermore, `high' and `mid' can be computed mod 2^n, as any factor
+ * of 2^n in either one will also vanish. Only `low' need be computed
+ * mod 2^2n, and only because of the final term above.
+ */
+static quad_t __lmulq(u_long, u_long);
+
+quad_t
+__muldi3(a, b)
+ quad_t a, b;
+{
+ union uu u, v, low, prod;
+ register u_long high, mid, udiff, vdiff;
+ register int negall, negmid;
+#define u1 u.ul[H]
+#define u0 u.ul[L]
+#define v1 v.ul[H]
+#define v0 v.ul[L]
+
+ /*
+ * Get u and v such that u, v >= 0. When this is finished,
+ * u1, u0, v1, and v0 will be directly accessible through the
+ * longword fields.
+ */
+ if (a >= 0)
+ u.q = a, negall = 0;
+ else
+ u.q = -a, negall = 1;
+ if (b >= 0)
+ v.q = b;
+ else
+ v.q = -b, negall ^= 1;
+
+ if (u1 == 0 && v1 == 0) {
+ /*
+ * An (I hope) important optimization occurs when u1 and v1
+ * are both 0. This should be common since most numbers
+ * are small. Here the product is just u0*v0.
+ */
+ prod.q = __lmulq(u0, v0);
+ } else {
+ /*
+ * Compute the three intermediate products, remembering
+ * whether the middle term is negative. We can discard
+ * any upper bits in high and mid, so we can use native
+ * u_long * u_long => u_long arithmetic.
+ */
+ low.q = __lmulq(u0, v0);
+
+ if (u1 >= u0)
+ negmid = 0, udiff = u1 - u0;
+ else
+ negmid = 1, udiff = u0 - u1;
+ if (v0 >= v1)
+ vdiff = v0 - v1;
+ else
+ vdiff = v1 - v0, negmid ^= 1;
+ mid = udiff * vdiff;
+
+ high = u1 * v1;
+
+ /*
+ * Assemble the final product.
+ */
+ prod.ul[H] = high + (negmid ? -mid : mid) + low.ul[L] +
+ low.ul[H];
+ prod.ul[L] = low.ul[L];
+ }
+ return (negall ? -prod.q : prod.q);
+#undef u1
+#undef u0
+#undef v1
+#undef v0
+}
+
+/*
+ * Multiply two 2N-bit longs to produce a 4N-bit quad, where N is half
+ * the number of bits in a long (whatever that is---the code below
+ * does not care as long as quad.h does its part of the bargain---but
+ * typically N==16).
+ *
+ * We use the same algorithm from Knuth, but this time the modulo refinement
+ * does not apply. On the other hand, since N is half the size of a long,
+ * we can get away with native multiplication---none of our input terms
+ * exceeds (ULONG_MAX >> 1).
+ *
+ * Note that, for u_long l, the quad-precision result
+ *
+ * l << N
+ *
+ * splits into high and low longs as HHALF(l) and LHUP(l) respectively.
+ */
+static quad_t
+__lmulq(u_long u, u_long v)
+{
+ u_long u1, u0, v1, v0, udiff, vdiff, high, mid, low;
+ u_long prodh, prodl, was;
+ union uu prod;
+ int neg;
+
+ u1 = HHALF(u);
+ u0 = LHALF(u);
+ v1 = HHALF(v);
+ v0 = LHALF(v);
+
+ low = u0 * v0;
+
+ /* This is the same small-number optimization as before. */
+ if (u1 == 0 && v1 == 0)
+ return (low);
+
+ if (u1 >= u0)
+ udiff = u1 - u0, neg = 0;
+ else
+ udiff = u0 - u1, neg = 1;
+ if (v0 >= v1)
+ vdiff = v0 - v1;
+ else
+ vdiff = v1 - v0, neg ^= 1;
+ mid = udiff * vdiff;
+
+ high = u1 * v1;
+
+ /* prod = (high << 2N) + (high << N); */
+ prodh = high + HHALF(high);
+ prodl = LHUP(high);
+
+ /* if (neg) prod -= mid << N; else prod += mid << N; */
+ if (neg) {
+ was = prodl;
+ prodl -= LHUP(mid);
+ prodh -= HHALF(mid) + (prodl > was);
+ } else {
+ was = prodl;
+ prodl += LHUP(mid);
+ prodh += HHALF(mid) + (prodl < was);
+ }
+
+ /* prod += low << N */
+ was = prodl;
+ prodl += LHUP(low);
+ prodh += HHALF(low) + (prodl < was);
+ /* ... + low; */
+ if ((prodl += low) < low)
+ prodh++;
+
+ /* return 4N-bit product */
+ prod.ul[H] = prodh;
+ prod.ul[L] = prodl;
+ return (prod.q);
+}
diff --git a/sys/libkern/negdi2.c b/sys/libkern/negdi2.c
new file mode 100644
index 0000000..bb8670d
--- /dev/null
+++ b/sys/libkern/negdi2.c
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)negdi2.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Return -a (or, equivalently, 0 - a), in quad. See subdi3.c.
+ */
+quad_t
+__negdi2(a)
+ quad_t a;
+{
+ union uu aa, res;
+
+ aa.q = a;
+ res.ul[L] = -aa.ul[L];
+ res.ul[H] = -aa.ul[H] - (res.ul[L] > 0);
+ return (res.q);
+}
diff --git a/sys/libkern/notdi2.c b/sys/libkern/notdi2.c
new file mode 100644
index 0000000..d624733
--- /dev/null
+++ b/sys/libkern/notdi2.c
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)notdi2.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Return ~a. For some reason gcc calls this `one's complement' rather
+ * than `not'.
+ */
+quad_t
+__one_cmpldi2(a)
+ quad_t a;
+{
+ union uu aa;
+
+ aa.q = a;
+ aa.ul[0] = ~aa.ul[0];
+ aa.ul[1] = ~aa.ul[1];
+ return (aa.q);
+}
diff --git a/sys/libkern/qdivrem.c b/sys/libkern/qdivrem.c
new file mode 100644
index 0000000..34b94ce
--- /dev/null
+++ b/sys/libkern/qdivrem.c
@@ -0,0 +1,279 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)qdivrem.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Multiprecision divide. This algorithm is from Knuth vol. 2 (2nd ed),
+ * section 4.3.1, pp. 257--259.
+ */
+
+#include "quad.h"
+
+#define B (1 << HALF_BITS) /* digit base */
+
+/* Combine two `digits' to make a single two-digit number. */
+#define COMBINE(a, b) (((u_long)(a) << HALF_BITS) | (b))
+
+/* select a type for digits in base B: use unsigned short if they fit */
+#if ULONG_MAX == 0xffffffff && USHRT_MAX >= 0xffff
+typedef unsigned short digit;
+#else
+typedef u_long digit;
+#endif
+
+/*
+ * Shift p[0]..p[len] left `sh' bits, ignoring any bits that
+ * `fall out' the left (there never will be any such anyway).
+ * We may assume len >= 0. NOTE THAT THIS WRITES len+1 DIGITS.
+ */
+static void
+shl(register digit *p, register int len, register int sh)
+{
+ register int i;
+
+ for (i = 0; i < len; i++)
+ p[i] = LHALF(p[i] << sh) | (p[i + 1] >> (HALF_BITS - sh));
+ p[i] = LHALF(p[i] << sh);
+}
+
+/*
+ * __qdivrem(u, v, rem) returns u/v and, optionally, sets *rem to u%v.
+ *
+ * We do this in base 2-sup-HALF_BITS, so that all intermediate products
+ * fit within u_long. As a consequence, the maximum length dividend and
+ * divisor are 4 `digits' in this base (they are shorter if they have
+ * leading zeros).
+ */
+u_quad_t
+__qdivrem(uq, vq, arq)
+ u_quad_t uq, vq, *arq;
+{
+ union uu tmp;
+ digit *u, *v, *q;
+ register digit v1, v2;
+ u_long qhat, rhat, t;
+ int m, n, d, j, i;
+ digit uspace[5], vspace[5], qspace[5];
+
+ /*
+ * Take care of special cases: divide by zero, and u < v.
+ */
+ if (vq == 0) {
+ /* divide by zero. */
+ static volatile const unsigned int zero = 0;
+
+ tmp.ul[H] = tmp.ul[L] = 1 / zero;
+ if (arq)
+ *arq = uq;
+ return (tmp.q);
+ }
+ if (uq < vq) {
+ if (arq)
+ *arq = uq;
+ return (0);
+ }
+ u = &uspace[0];
+ v = &vspace[0];
+ q = &qspace[0];
+
+ /*
+ * Break dividend and divisor into digits in base B, then
+ * count leading zeros to determine m and n. When done, we
+ * will have:
+ * u = (u[1]u[2]...u[m+n]) sub B
+ * v = (v[1]v[2]...v[n]) sub B
+ * v[1] != 0
+ * 1 < n <= 4 (if n = 1, we use a different division algorithm)
+ * m >= 0 (otherwise u < v, which we already checked)
+ * m + n = 4
+ * and thus
+ * m = 4 - n <= 2
+ */
+ tmp.uq = uq;
+ u[0] = 0;
+ u[1] = HHALF(tmp.ul[H]);
+ u[2] = LHALF(tmp.ul[H]);
+ u[3] = HHALF(tmp.ul[L]);
+ u[4] = LHALF(tmp.ul[L]);
+ tmp.uq = vq;
+ v[1] = HHALF(tmp.ul[H]);
+ v[2] = LHALF(tmp.ul[H]);
+ v[3] = HHALF(tmp.ul[L]);
+ v[4] = LHALF(tmp.ul[L]);
+ for (n = 4; v[1] == 0; v++) {
+ if (--n == 1) {
+ u_long rbj; /* r*B+u[j] (not root boy jim) */
+ digit q1, q2, q3, q4;
+
+ /*
+ * Change of plan, per exercise 16.
+ * r = 0;
+ * for j = 1..4:
+ * q[j] = floor((r*B + u[j]) / v),
+ * r = (r*B + u[j]) % v;
+ * We unroll this completely here.
+ */
+ t = v[2]; /* nonzero, by definition */
+ q1 = u[1] / t;
+ rbj = COMBINE(u[1] % t, u[2]);
+ q2 = rbj / t;
+ rbj = COMBINE(rbj % t, u[3]);
+ q3 = rbj / t;
+ rbj = COMBINE(rbj % t, u[4]);
+ q4 = rbj / t;
+ if (arq)
+ *arq = rbj % t;
+ tmp.ul[H] = COMBINE(q1, q2);
+ tmp.ul[L] = COMBINE(q3, q4);
+ return (tmp.q);
+ }
+ }
+
+ /*
+ * By adjusting q once we determine m, we can guarantee that
+ * there is a complete four-digit quotient at &qspace[1] when
+ * we finally stop.
+ */
+ for (m = 4 - n; u[1] == 0; u++)
+ m--;
+ for (i = 4 - m; --i >= 0;)
+ q[i] = 0;
+ q += 4 - m;
+
+ /*
+ * Here we run Program D, translated from MIX to C and acquiring
+ * a few minor changes.
+ *
+ * D1: choose multiplier 1 << d to ensure v[1] >= B/2.
+ */
+ d = 0;
+ for (t = v[1]; t < B / 2; t <<= 1)
+ d++;
+ if (d > 0) {
+ shl(&u[0], m + n, d); /* u <<= d */
+ shl(&v[1], n - 1, d); /* v <<= d */
+ }
+ /*
+ * D2: j = 0.
+ */
+ j = 0;
+ v1 = v[1]; /* for D3 -- note that v[1..n] are constant */
+ v2 = v[2]; /* for D3 */
+ do {
+ register digit uj0, uj1, uj2;
+
+ /*
+ * D3: Calculate qhat (\^q, in TeX notation).
+ * Let qhat = min((u[j]*B + u[j+1])/v[1], B-1), and
+ * let rhat = (u[j]*B + u[j+1]) mod v[1].
+ * While rhat < B and v[2]*qhat > rhat*B+u[j+2],
+ * decrement qhat and increase rhat correspondingly.
+ * Note that if rhat >= B, v[2]*qhat < rhat*B.
+ */
+ uj0 = u[j + 0]; /* for D3 only -- note that u[j+...] change */
+ uj1 = u[j + 1]; /* for D3 only */
+ uj2 = u[j + 2]; /* for D3 only */
+ if (uj0 == v1) {
+ qhat = B;
+ rhat = uj1;
+ goto qhat_too_big;
+ } else {
+ u_long n = COMBINE(uj0, uj1);
+ qhat = n / v1;
+ rhat = n % v1;
+ }
+ while (v2 * qhat > COMBINE(rhat, uj2)) {
+ qhat_too_big:
+ qhat--;
+ if ((rhat += v1) >= B)
+ break;
+ }
+ /*
+ * D4: Multiply and subtract.
+ * The variable `t' holds any borrows across the loop.
+ * We split this up so that we do not require v[0] = 0,
+ * and to eliminate a final special case.
+ */
+ for (t = 0, i = n; i > 0; i--) {
+ t = u[i + j] - v[i] * qhat - t;
+ u[i + j] = LHALF(t);
+ t = (B - HHALF(t)) & (B - 1);
+ }
+ t = u[j] - t;
+ u[j] = LHALF(t);
+ /*
+ * D5: test remainder.
+ * There is a borrow if and only if HHALF(t) is nonzero;
+ * in that (rare) case, qhat was too large (by exactly 1).
+ * Fix it by adding v[1..n] to u[j..j+n].
+ */
+ if (HHALF(t)) {
+ qhat--;
+ for (t = 0, i = n; i > 0; i--) { /* D6: add back. */
+ t += u[i + j] + v[i];
+ u[i + j] = LHALF(t);
+ t = HHALF(t);
+ }
+ u[j] = LHALF(u[j] + t);
+ }
+ q[j] = qhat;
+ } while (++j <= m); /* D7: loop on j. */
+
+ /*
+ * If caller wants the remainder, we have to calculate it as
+ * u[m..m+n] >> d (this is at most n digits and thus fits in
+ * u[m+1..m+n], but we may need more source digits).
+ */
+ if (arq) {
+ if (d) {
+ for (i = m + n; i > m; --i)
+ u[i] = (u[i] >> d) |
+ LHALF(u[i - 1] << (HALF_BITS - d));
+ u[i] = 0;
+ }
+ tmp.ul[H] = COMBINE(uspace[1], uspace[2]);
+ tmp.ul[L] = COMBINE(uspace[3], uspace[4]);
+ *arq = tmp.q;
+ }
+
+ tmp.ul[H] = COMBINE(qspace[1], qspace[2]);
+ tmp.ul[L] = COMBINE(qspace[3], qspace[4]);
+ return (tmp.q);
+}
diff --git a/sys/libkern/quad.h b/sys/libkern/quad.h
new file mode 100644
index 0000000..bc6a2f8
--- /dev/null
+++ b/sys/libkern/quad.h
@@ -0,0 +1,110 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)quad.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * Quad arithmetic.
+ *
+ * This library makes the following assumptions:
+ *
+ * - The type long long (aka quad_t) exists.
+ *
+ * - A quad variable is exactly twice as long as `long'.
+ *
+ * - The machine's arithmetic is two's complement.
+ *
+ * This library can provide 128-bit arithmetic on a machine with 128-bit
+ * quads and 64-bit longs, for instance, or 96-bit arithmetic on machines
+ * with 48-bit longs.
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+
+/*
+ * Depending on the desired operation, we view a `long long' (aka quad_t) in
+ * one or more of the following formats.
+ */
+union uu {
+ quad_t q; /* as a (signed) quad */
+ quad_t uq; /* as an unsigned quad */
+ long sl[2]; /* as two signed longs */
+ u_long ul[2]; /* as two unsigned longs */
+};
+
+/*
+ * Define high and low longwords.
+ */
+#define H _QUAD_HIGHWORD
+#define L _QUAD_LOWWORD
+
+/*
+ * Total number of bits in a quad_t and in the pieces that make it up.
+ * These are used for shifting, and also below for halfword extraction
+ * and assembly.
+ */
+#define QUAD_BITS (sizeof(quad_t) * CHAR_BIT)
+#define LONG_BITS (sizeof(long) * CHAR_BIT)
+#define HALF_BITS (sizeof(long) * CHAR_BIT / 2)
+
+/*
+ * Extract high and low shortwords from longword, and move low shortword of
+ * longword to upper half of long, i.e., produce the upper longword of
+ * ((quad_t)(x) << (number_of_bits_in_long/2)). (`x' must actually be u_long.)
+ *
+ * These are used in the multiply code, to split a longword into upper
+ * and lower halves, and to reassemble a product as a quad_t, shifted left
+ * (sizeof(long)*CHAR_BIT/2).
+ */
+#define HHALF(x) ((x) >> HALF_BITS)
+#define LHALF(x) ((x) & ((1 << HALF_BITS) - 1))
+#define LHUP(x) ((x) << HALF_BITS)
+
+extern u_quad_t __qdivrem __P((u_quad_t u, u_quad_t v, u_quad_t *rem));
+
+/*
+ * XXX
+ * Compensate for gcc 1 vs gcc 2. Gcc 1 defines ?sh?di3's second argument
+ * as u_quad_t, while gcc 2 correctly uses int. Unfortunately, we still use
+ * both compilers.
+ */
+#if __GNUC__ >= 2
+typedef unsigned int qshift_t;
+#else
+typedef u_quad_t qshift_t;
+#endif
diff --git a/sys/libkern/random.c b/sys/libkern/random.c
new file mode 100644
index 0000000..5153124
--- /dev/null
+++ b/sys/libkern/random.c
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)random.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <libkern/libkern.h>
+
+/*
+ * Pseudo-random number generator for randomizing the profiling clock,
+ * and whatever else we might use it for. The result is uniform on
+ * [0, 2^31 - 1].
+ */
+u_long
+random()
+{
+ static u_long randseed = 1;
+ register long x, hi, lo, t;
+
+ /*
+ * Compute x[n + 1] = (7^5 * x[n]) mod (2^31 - 1).
+ * From "Random number generators: good ones are hard to find",
+ * Park and Miller, Communications of the ACM, vol. 31, no. 10,
+ * October 1988, p. 1195.
+ */
+ x = randseed;
+ hi = x / 127773;
+ lo = x % 127773;
+ t = 16807 * lo - 2836 * hi;
+ if (t <= 0)
+ t += 0x7fffffff;
+ randseed = t;
+ return (t);
+}
diff --git a/sys/libkern/rindex.c b/sys/libkern/rindex.c
new file mode 100644
index 0000000..69dced4
--- /dev/null
+++ b/sys/libkern/rindex.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rindex.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stddef.h>
+#include <string.h>
+
+char *
+#ifdef STRRCHR
+strrchr(p, ch)
+#else
+rindex(p, ch)
+#endif
+ register const char *p;
+ register int ch;
+{
+ register char *save;
+
+ for (save = NULL;; ++p) {
+ if (*p == ch)
+ save = (char *)p;
+ if (!*p)
+ return(save);
+ }
+ /* NOTREACHED */
+}
diff --git a/sys/libkern/scanc.c b/sys/libkern/scanc.c
new file mode 100644
index 0000000..2d8b6a0
--- /dev/null
+++ b/sys/libkern/scanc.c
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)scanc.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <libkern/libkern.h>
+
+int
+scanc(size, cp, table, mask0)
+ u_int size;
+ register u_char *cp, table[];
+ int mask0;
+{
+ register u_char *end;
+ register u_char mask;
+
+ mask = mask0;
+ for (end = &cp[size]; cp < end && (table[*cp] & mask) == 0; ++cp);
+ return (end - cp);
+}
diff --git a/sys/libkern/skpc.c b/sys/libkern/skpc.c
new file mode 100644
index 0000000..11b269e
--- /dev/null
+++ b/sys/libkern/skpc.c
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)skpc.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <libkern/libkern.h>
+
+int
+skpc(mask0, size, cp0)
+ int mask0;
+ int size;
+ char *cp0;
+{
+ register u_char *cp, *end, mask;
+
+ mask = mask0;
+ cp = (u_char *)cp0;
+ for (end = &cp[size]; cp < end && *cp == mask; ++cp);
+ return (end - cp);
+}
diff --git a/sys/libkern/strcat.c b/sys/libkern/strcat.c
new file mode 100644
index 0000000..3436967
--- /dev/null
+++ b/sys/libkern/strcat.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strcat.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <string.h>
+
+char *
+strcat(s, append)
+ register char *s;
+ register const char *append;
+{
+ char *save = s;
+
+ for (; *s; ++s);
+ while (*s++ = *append++);
+ return(save);
+}
diff --git a/sys/libkern/strcmp.c b/sys/libkern/strcmp.c
new file mode 100644
index 0000000..79cfaa8
--- /dev/null
+++ b/sys/libkern/strcmp.c
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strcmp.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/cdefs.h>
+#include <string.h>
+
+/*
+ * Compare strings.
+ */
+int
+strcmp(s1, s2)
+ register const char *s1, *s2;
+{
+ while (*s1 == *s2++)
+ if (*s1++ == 0)
+ return (0);
+ return (*(unsigned char *)s1 - *(unsigned char *)--s2);
+}
diff --git a/sys/libkern/strcpy.c b/sys/libkern/strcpy.c
new file mode 100644
index 0000000..d1791dd
--- /dev/null
+++ b/sys/libkern/strcpy.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strcpy.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/cdefs.h>
+#include <string.h>
+
+char *
+strcpy(to, from)
+ register char *to;
+ register const char *from;
+{
+ char *save = to;
+
+ for (; *to = *from; ++from, ++to);
+ return(save);
+}
diff --git a/sys/libkern/strlen.c b/sys/libkern/strlen.c
new file mode 100644
index 0000000..323fbe4
--- /dev/null
+++ b/sys/libkern/strlen.c
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strlen.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/cdefs.h>
+#include <string.h>
+
+size_t
+strlen(str)
+ const char *str;
+{
+ register const char *s;
+
+ for (s = str; *s; ++s);
+ return(s - str);
+}
+
diff --git a/sys/libkern/strncpy.c b/sys/libkern/strncpy.c
new file mode 100644
index 0000000..9e72740
--- /dev/null
+++ b/sys/libkern/strncpy.c
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strncpy.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/cdefs.h>
+#include <string.h>
+
+/*
+ * Copy src to dst, truncating or null-padding to always copy n bytes.
+ * Return dst.
+ */
+char *
+strncpy(dst, src, n)
+ char *dst;
+ const char *src;
+ register size_t n;
+{
+ if (n != 0) {
+ register char *d = dst;
+ register const char *s = src;
+
+ do {
+ if ((*d++ = *s++) == 0) {
+ /* NUL pad the remaining n-1 bytes */
+ while (--n != 0)
+ *d++ = 0;
+ break;
+ }
+ } while (--n != 0);
+ }
+ return (dst);
+}
diff --git a/sys/libkern/strsep.c b/sys/libkern/strsep.c
new file mode 100644
index 0000000..18dc398
--- /dev/null
+++ b/sys/libkern/strsep.c
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <string.h>
+#include <stdio.h>
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strsep.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Get next token from string *stringp, where tokens are possibly-empty
+ * strings separated by characters from delim.
+ *
+ * Writes NULs into the string at *stringp to end tokens.
+ * delim need not remain constant from call to call.
+ * On return, *stringp points past the last NUL written (if there might
+ * be further tokens), or is NULL (if there are definitely no more tokens).
+ *
+ * If *stringp is NULL, strsep returns NULL.
+ */
+char *
+strsep(stringp, delim)
+ register char **stringp;
+ register const char *delim;
+{
+ register char *s;
+ register const char *spanp;
+ register int c, sc;
+ char *tok;
+
+ if ((s = *stringp) == NULL)
+ return (NULL);
+ for (tok = s;;) {
+ c = *s++;
+ spanp = delim;
+ do {
+ if ((sc = *spanp++) == c) {
+ if (c == 0)
+ s = NULL;
+ else
+ s[-1] = 0;
+ *stringp = s;
+ return (tok);
+ }
+ } while (sc != 0);
+ }
+ /* NOTREACHED */
+}
diff --git a/sys/libkern/subdi3.c b/sys/libkern/subdi3.c
new file mode 100644
index 0000000..e976345
--- /dev/null
+++ b/sys/libkern/subdi3.c
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)subdi3.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Subtract two quad values. This is trivial since a one-bit carry
+ * from a single u_long difference x-y occurs if and only if (x-y) > x.
+ */
+quad_t
+__subdi3(a, b)
+ quad_t a, b;
+{
+ union uu aa, bb, diff;
+
+ aa.q = a;
+ bb.q = b;
+ diff.ul[L] = aa.ul[L] - bb.ul[L];
+ diff.ul[H] = aa.ul[H] - bb.ul[H] - (diff.ul[L] > aa.ul[L]);
+ return (diff.q);
+}
diff --git a/sys/libkern/ucmpdi2.c b/sys/libkern/ucmpdi2.c
new file mode 100644
index 0000000..e5dfc43
--- /dev/null
+++ b/sys/libkern/ucmpdi2.c
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)ucmpdi2.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Return 0, 1, or 2 as a <, =, > b respectively.
+ * Neither a nor b are considered signed.
+ */
+int
+__ucmpdi2(a, b)
+ u_quad_t a, b;
+{
+ union uu aa, bb;
+
+ aa.uq = a;
+ bb.uq = b;
+ return (aa.ul[H] < bb.ul[H] ? 0 : aa.ul[H] > bb.ul[H] ? 2 :
+ aa.ul[L] < bb.ul[L] ? 0 : aa.ul[L] > bb.ul[L] ? 2 : 1);
+}
diff --git a/sys/libkern/udivdi3.c b/sys/libkern/udivdi3.c
new file mode 100644
index 0000000..8ddd559
--- /dev/null
+++ b/sys/libkern/udivdi3.c
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)udivdi3.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Divide two unsigned quads.
+ */
+u_quad_t
+__udivdi3(a, b)
+ u_quad_t a, b;
+{
+
+ return (__qdivrem(a, b, (u_quad_t *)0));
+}
diff --git a/sys/libkern/umoddi3.c b/sys/libkern/umoddi3.c
new file mode 100644
index 0000000..2a85f76
--- /dev/null
+++ b/sys/libkern/umoddi3.c
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)umoddi3.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Return remainder after dividing two unsigned quads.
+ */
+u_quad_t
+__umoddi3(a, b)
+ u_quad_t a, b;
+{
+ u_quad_t r;
+
+ (void)__qdivrem(a, b, &r);
+ return (r);
+}
diff --git a/sys/libkern/xordi3.c b/sys/libkern/xordi3.c
new file mode 100644
index 0000000..e3a8588
--- /dev/null
+++ b/sys/libkern/xordi3.c
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)xordi3.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "quad.h"
+
+/*
+ * Return a ^ b, in quad.
+ */
+quad_t
+__xordi3(a, b)
+ quad_t a, b;
+{
+ union uu aa, bb;
+
+ aa.q = a;
+ bb.q = b;
+ aa.ul[0] ^= bb.ul[0];
+ aa.ul[1] ^= bb.ul[1];
+ return (aa.q);
+}
diff --git a/sys/miscfs/deadfs/dead_vnops.c b/sys/miscfs/deadfs/dead_vnops.c
new file mode 100644
index 0000000..cac8775
--- /dev/null
+++ b/sys/miscfs/deadfs/dead_vnops.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dead_vnops.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vnode.h>
+#include <sys/errno.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+
+/*
+ * Prototypes for dead operations on vnodes.
+ */
+int dead_badop(),
+ dead_ebadf();
+int dead_lookup __P((struct vop_lookup_args *));
+#define dead_create ((int (*) __P((struct vop_create_args *)))dead_badop)
+#define dead_mknod ((int (*) __P((struct vop_mknod_args *)))dead_badop)
+int dead_open __P((struct vop_open_args *));
+#define dead_close ((int (*) __P((struct vop_close_args *)))nullop)
+#define dead_access ((int (*) __P((struct vop_access_args *)))dead_ebadf)
+#define dead_getattr ((int (*) __P((struct vop_getattr_args *)))dead_ebadf)
+#define dead_setattr ((int (*) __P((struct vop_setattr_args *)))dead_ebadf)
+int dead_read __P((struct vop_read_args *));
+int dead_write __P((struct vop_write_args *));
+int dead_ioctl __P((struct vop_ioctl_args *));
+int dead_select __P((struct vop_select_args *));
+#define dead_mmap ((int (*) __P((struct vop_mmap_args *)))dead_badop)
+#define dead_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
+#define dead_seek ((int (*) __P((struct vop_seek_args *)))nullop)
+#define dead_remove ((int (*) __P((struct vop_remove_args *)))dead_badop)
+#define dead_link ((int (*) __P((struct vop_link_args *)))dead_badop)
+#define dead_rename ((int (*) __P((struct vop_rename_args *)))dead_badop)
+#define dead_mkdir ((int (*) __P((struct vop_mkdir_args *)))dead_badop)
+#define dead_rmdir ((int (*) __P((struct vop_rmdir_args *)))dead_badop)
+#define dead_symlink ((int (*) __P((struct vop_symlink_args *)))dead_badop)
+#define dead_readdir ((int (*) __P((struct vop_readdir_args *)))dead_ebadf)
+#define dead_readlink ((int (*) __P((struct vop_readlink_args *)))dead_ebadf)
+#define dead_abortop ((int (*) __P((struct vop_abortop_args *)))dead_badop)
+#define dead_inactive ((int (*) __P((struct vop_inactive_args *)))nullop)
+#define dead_reclaim ((int (*) __P((struct vop_reclaim_args *)))nullop)
+int dead_lock __P((struct vop_lock_args *));
+#define dead_unlock ((int (*) __P((struct vop_unlock_args *)))nullop)
+int dead_bmap __P((struct vop_bmap_args *));
+int dead_strategy __P((struct vop_strategy_args *));
+int dead_print __P((struct vop_print_args *));
+#define dead_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+#define dead_pathconf ((int (*) __P((struct vop_pathconf_args *)))dead_ebadf)
+#define dead_advlock ((int (*) __P((struct vop_advlock_args *)))dead_ebadf)
+#define dead_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))dead_badop)
+#define dead_valloc ((int (*) __P((struct vop_valloc_args *)))dead_badop)
+#define dead_vfree ((int (*) __P((struct vop_vfree_args *)))dead_badop)
+#define dead_truncate ((int (*) __P((struct vop_truncate_args *)))nullop)
+#define dead_update ((int (*) __P((struct vop_update_args *)))nullop)
+#define dead_bwrite ((int (*) __P((struct vop_bwrite_args *)))nullop)
+
+int (**dead_vnodeop_p)();
+struct vnodeopv_entry_desc dead_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, dead_lookup }, /* lookup */
+ { &vop_create_desc, dead_create }, /* create */
+ { &vop_mknod_desc, dead_mknod }, /* mknod */
+ { &vop_open_desc, dead_open }, /* open */
+ { &vop_close_desc, dead_close }, /* close */
+ { &vop_access_desc, dead_access }, /* access */
+ { &vop_getattr_desc, dead_getattr }, /* getattr */
+ { &vop_setattr_desc, dead_setattr }, /* setattr */
+ { &vop_read_desc, dead_read }, /* read */
+ { &vop_write_desc, dead_write }, /* write */
+ { &vop_ioctl_desc, dead_ioctl }, /* ioctl */
+ { &vop_select_desc, dead_select }, /* select */
+ { &vop_mmap_desc, dead_mmap }, /* mmap */
+ { &vop_fsync_desc, dead_fsync }, /* fsync */
+ { &vop_seek_desc, dead_seek }, /* seek */
+ { &vop_remove_desc, dead_remove }, /* remove */
+ { &vop_link_desc, dead_link }, /* link */
+ { &vop_rename_desc, dead_rename }, /* rename */
+ { &vop_mkdir_desc, dead_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, dead_rmdir }, /* rmdir */
+ { &vop_symlink_desc, dead_symlink }, /* symlink */
+ { &vop_readdir_desc, dead_readdir }, /* readdir */
+ { &vop_readlink_desc, dead_readlink }, /* readlink */
+ { &vop_abortop_desc, dead_abortop }, /* abortop */
+ { &vop_inactive_desc, dead_inactive }, /* inactive */
+ { &vop_reclaim_desc, dead_reclaim }, /* reclaim */
+ { &vop_lock_desc, dead_lock }, /* lock */
+ { &vop_unlock_desc, dead_unlock }, /* unlock */
+ { &vop_bmap_desc, dead_bmap }, /* bmap */
+ { &vop_strategy_desc, dead_strategy }, /* strategy */
+ { &vop_print_desc, dead_print }, /* print */
+ { &vop_islocked_desc, dead_islocked }, /* islocked */
+ { &vop_pathconf_desc, dead_pathconf }, /* pathconf */
+ { &vop_advlock_desc, dead_advlock }, /* advlock */
+ { &vop_blkatoff_desc, dead_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, dead_valloc }, /* valloc */
+ { &vop_vfree_desc, dead_vfree }, /* vfree */
+ { &vop_truncate_desc, dead_truncate }, /* truncate */
+ { &vop_update_desc, dead_update }, /* update */
+ { &vop_bwrite_desc, dead_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc dead_vnodeop_opv_desc =
+ { &dead_vnodeop_p, dead_vnodeop_entries };
+
+/*
+ * Trivial lookup routine that always fails.
+ */
+/* ARGSUSED */
+int
+dead_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode * a_dvp;
+ struct vnode ** a_vpp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+
+ *ap->a_vpp = NULL;
+ return (ENOTDIR);
+}
+
+/*
+ * Open always fails as if device did not exist.
+ */
+/* ARGSUSED */
+int
+dead_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (ENXIO);
+}
+
+/*
+ * Vnode op for read
+ */
+/* ARGSUSED */
+int
+dead_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+
+ if (chkvnlock(ap->a_vp))
+ panic("dead_read: lock");
+ /*
+ * Return EOF for character devices, EIO for others
+ */
+ if (ap->a_vp->v_type != VCHR)
+ return (EIO);
+ return (0);
+}
+
+/*
+ * Vnode op for write
+ */
+/* ARGSUSED */
+int
+dead_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+
+ if (chkvnlock(ap->a_vp))
+ panic("dead_write: lock");
+ return (EIO);
+}
+
+/*
+ * Device ioctl operation.
+ */
+/* ARGSUSED */
+int
+dead_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ if (!chkvnlock(ap->a_vp))
+ return (EBADF);
+ return (VCALL(ap->a_vp, VOFFSET(vop_ioctl), ap));
+}
+
+/* ARGSUSED */
+int
+dead_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ /*
+ * Let the user find out that the descriptor is gone.
+ */
+ return (1);
+}
+
+/*
+ * Just call the device strategy routine
+ */
+int
+dead_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+
+ if (ap->a_bp->b_vp == NULL || !chkvnlock(ap->a_bp->b_vp)) {
+ ap->a_bp->b_flags |= B_ERROR;
+ biodone(ap->a_bp);
+ return (EIO);
+ }
+ return (VOP_STRATEGY(ap->a_bp));
+}
+
+/*
+ * Wait until the vnode has finished changing state.
+ */
+int
+dead_lock(ap)
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ if (!chkvnlock(ap->a_vp))
+ return (0);
+ return (VCALL(ap->a_vp, VOFFSET(vop_lock), ap));
+}
+
+/*
+ * Wait until the vnode has finished changing state.
+ */
+int
+dead_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap;
+{
+
+ if (!chkvnlock(ap->a_vp))
+ return (EIO);
+ return (VOP_BMAP(ap->a_vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp));
+}
+
+/*
+ * Print out the contents of a dead vnode.
+ */
+/* ARGSUSED */
+int
+dead_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_NON, dead vnode\n");
+ return (0);
+}
+
+/*
+ * Empty vnode failed operation
+ */
+int
+dead_ebadf()
+{
+
+ return (EBADF);
+}
+
+/*
+ * Empty vnode bad operation
+ */
+int
+dead_badop()
+{
+
+ panic("dead_badop called");
+ /* NOTREACHED */
+}
+
+/*
+ * Empty vnode null operation
+ */
+int
+dead_nullop()
+{
+
+ return (0);
+}
+
+/*
+ * We have to wait during times when the vnode is
+ * in a state of change.
+ */
+int
+chkvnlock(vp)
+ register struct vnode *vp;
+{
+ int locked = 0;
+
+ while (vp->v_flag & VXLOCK) {
+ vp->v_flag |= VXWANT;
+ sleep((caddr_t)vp, PINOD);
+ locked = 1;
+ }
+ return (locked);
+}
diff --git a/sys/miscfs/fdesc/fdesc.h b/sys/miscfs/fdesc/fdesc.h
new file mode 100644
index 0000000..4c682e7
--- /dev/null
+++ b/sys/miscfs/fdesc/fdesc.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fdesc.h 8.5 (Berkeley) 1/21/94
+ *
+ * $Id: fdesc.h,v 1.8 1993/04/06 15:28:33 jsp Exp $
+ */
+
+#ifdef KERNEL
+struct fdescmount {
+ struct vnode *f_root; /* Root node */
+};
+
+#define FD_ROOT 2
+#define FD_DEVFD 3
+#define FD_STDIN 4
+#define FD_STDOUT 5
+#define FD_STDERR 6
+#define FD_CTTY 7
+#define FD_DESC 8
+#define FD_MAX 12
+
+typedef enum {
+ Froot,
+ Fdevfd,
+ Fdesc,
+ Flink,
+ Fctty
+} fdntype;
+
+struct fdescnode {
+ struct fdescnode *fd_forw; /* Hash chain */
+ struct fdescnode *fd_back;
+ struct vnode *fd_vnode; /* Back ptr to vnode */
+ fdntype fd_type; /* Type of this node */
+ unsigned fd_fd; /* Fd to be dup'ed */
+ char *fd_link; /* Link to fd/n */
+ int fd_ix; /* filesystem index */
+};
+
+#define VFSTOFDESC(mp) ((struct fdescmount *)((mp)->mnt_data))
+#define VTOFDESC(vp) ((struct fdescnode *)(vp)->v_data)
+
+extern dev_t devctty;
+extern int fdesc_init __P((void));
+extern int fdesc_root __P((struct mount *, struct vnode **));
+extern int fdesc_allocvp __P((fdntype, int, struct mount *, struct vnode **));
+extern int (**fdesc_vnodeop_p)();
+extern struct vfsops fdesc_vfsops;
+#endif /* KERNEL */
diff --git a/sys/miscfs/fdesc/fdesc_vfsops.c b/sys/miscfs/fdesc/fdesc_vfsops.c
new file mode 100644
index 0000000..80c543d
--- /dev/null
+++ b/sys/miscfs/fdesc/fdesc_vfsops.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fdesc_vfsops.c 8.4 (Berkeley) 1/21/94
+ *
+ * $Id: fdesc_vfsops.c,v 1.9 1993/04/06 15:28:33 jsp Exp $
+ */
+
+/*
+ * /dev/fd Filesystem
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/resourcevar.h>
+#include <sys/filedesc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <miscfs/fdesc/fdesc.h>
+
+/*
+ * Mount the per-process file descriptors (/dev/fd)
+ */
+int
+fdesc_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ int error = 0;
+ u_int size;
+ struct fdescmount *fmp;
+ struct vnode *rvp;
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE)
+ return (EOPNOTSUPP);
+
+ error = fdesc_allocvp(Froot, FD_ROOT, mp, &rvp);
+ if (error)
+ return (error);
+
+ MALLOC(fmp, struct fdescmount *, sizeof(struct fdescmount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+ rvp->v_type = VDIR;
+ rvp->v_flag |= VROOT;
+ fmp->f_root = rvp;
+ /* XXX -- don't mark as local to work around fts() problems */
+ /*mp->mnt_flag |= MNT_LOCAL;*/
+ mp->mnt_data = (qaddr_t) fmp;
+ getnewfsid(mp, MOUNT_FDESC);
+
+ (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+ bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
+ bcopy("fdesc", mp->mnt_stat.f_mntfromname, sizeof("fdesc"));
+ return (0);
+}
+
+int
+fdesc_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ return (0);
+}
+
+int
+fdesc_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ int error;
+ int flags = 0;
+ extern int doforce;
+ struct vnode *rootvp = VFSTOFDESC(mp)->f_root;
+
+ if (mntflags & MNT_FORCE) {
+ /* fdesc can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ /*
+ * Clear out buffer cache. I don't think we
+ * ever get anything cached at this level at the
+ * moment, but who knows...
+ */
+ if (rootvp->v_usecount > 1)
+ return (EBUSY);
+ if (error = vflush(mp, rootvp, flags))
+ return (error);
+
+ /*
+ * Release reference on underlying root vnode
+ */
+ vrele(rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(rootvp);
+ /*
+ * Finally, throw away the fdescmount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ mp->mnt_data = 0;
+
+ return (0);
+}
+
+int
+fdesc_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct vnode *vp;
+
+ /*
+ * Return locked reference to root.
+ */
+ vp = VFSTOFDESC(mp)->f_root;
+ VREF(vp);
+ VOP_LOCK(vp);
+ *vpp = vp;
+ return (0);
+}
+
+int
+fdesc_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+fdesc_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ struct filedesc *fdp;
+ int lim;
+ int i;
+ int last;
+ int freefd;
+
+ /*
+ * Compute number of free file descriptors.
+ * [ Strange results will ensue if the open file
+ * limit is ever reduced below the current number
+ * of open files... ]
+ */
+ lim = p->p_rlimit[RLIMIT_NOFILE].rlim_cur;
+ fdp = p->p_fd;
+ last = min(fdp->fd_nfiles, lim);
+ freefd = 0;
+ for (i = fdp->fd_freefile; i < last; i++)
+ if (fdp->fd_ofiles[i] == NULL)
+ freefd++;
+
+ /*
+ * Adjust for the fact that the fdesc array may not
+ * have been fully allocated yet.
+ */
+ if (fdp->fd_nfiles < lim)
+ freefd += (lim - fdp->fd_nfiles);
+
+ sbp->f_type = MOUNT_FDESC;
+ sbp->f_flags = 0;
+ sbp->f_bsize = DEV_BSIZE;
+ sbp->f_iosize = DEV_BSIZE;
+ sbp->f_blocks = 2; /* 1K to keep df happy */
+ sbp->f_bfree = 0;
+ sbp->f_bavail = 0;
+ sbp->f_files = lim + 1; /* Allow for "." */
+ sbp->f_ffree = freefd; /* See comments above */
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+ return (0);
+}
+
+int
+fdesc_sync(mp, waitfor)
+ struct mount *mp;
+ int waitfor;
+{
+
+ return (0);
+}
+
+/*
+ * Fdesc flat namespace lookup.
+ * Currently unsupported.
+ */
+int
+fdesc_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+fdesc_fhtovp(mp, fhp, setgen, vpp)
+ struct mount *mp;
+ struct fid *fhp;
+ int setgen;
+ struct vnode **vpp;
+{
+ return (EOPNOTSUPP);
+}
+
+int
+fdesc_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+struct vfsops fdesc_vfsops = {
+ fdesc_mount,
+ fdesc_start,
+ fdesc_unmount,
+ fdesc_root,
+ fdesc_quotactl,
+ fdesc_statfs,
+ fdesc_sync,
+ fdesc_vget,
+ fdesc_fhtovp,
+ fdesc_vptofh,
+ fdesc_init,
+};
diff --git a/sys/miscfs/fdesc/fdesc_vnops.c b/sys/miscfs/fdesc/fdesc_vnops.c
new file mode 100644
index 0000000..83e6657
--- /dev/null
+++ b/sys/miscfs/fdesc/fdesc_vnops.c
@@ -0,0 +1,977 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fdesc_vnops.c 8.9 (Berkeley) 1/21/94
+ *
+ * $Id: fdesc_vnops.c,v 1.12 1993/04/06 16:17:17 jsp Exp $
+ */
+
+/*
+ * /dev/fd Filesystem
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/kernel.h> /* boottime */
+#include <sys/resourcevar.h>
+#include <sys/filedesc.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+#include <sys/dirent.h>
+#include <miscfs/fdesc/fdesc.h>
+
+#define cttyvp(p) ((p)->p_flag & P_CONTROLT ? (p)->p_session->s_ttyvp : NULL)
+
+#define FDL_WANT 0x01
+#define FDL_LOCKED 0x02
+static int fdcache_lock;
+
+dev_t devctty;
+
+#if (FD_STDIN != FD_STDOUT-1) || (FD_STDOUT != FD_STDERR-1)
+FD_STDIN, FD_STDOUT, FD_STDERR must be a sequence n, n+1, n+2
+#endif
+
+#define NFDCACHE 3
+#define FD_NHASH(ix) ((ix) & NFDCACHE)
+
+/*
+ * Cache head
+ */
+struct fdcache {
+ struct fdescnode *fc_forw;
+ struct fdescnode *fc_back;
+};
+
+static struct fdcache fdcache[NFDCACHE];
+
+/*
+ * Initialise cache headers
+ */
+int
+fdesc_init()
+{
+ struct fdcache *fc;
+
+ devctty = makedev(nchrdev, 0);
+
+ for (fc = fdcache; fc < fdcache + NFDCACHE; fc++)
+ fc->fc_forw = fc->fc_back = (struct fdescnode *) fc;
+ return (0);
+}
+
+/*
+ * Compute hash list for given target vnode
+ */
+static struct fdcache *
+fdesc_hash(ix)
+ int ix;
+{
+
+ return (&fdcache[FD_NHASH(ix)]);
+}
+
+int
+fdesc_allocvp(ftype, ix, mp, vpp)
+ fdntype ftype;
+ int ix;
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct fdcache *fc;
+ struct fdescnode *fd;
+ int error = 0;
+
+loop:
+ fc = fdesc_hash(ix);
+ for (fd = fc->fc_forw; fd != (struct fdescnode *) fc; fd = fd->fd_forw) {
+ if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) {
+ if (vget(fd->fd_vnode, 0))
+ goto loop;
+ *vpp = fd->fd_vnode;
+ return (error);
+ }
+ }
+
+ /*
+ * otherwise lock the array while we call getnewvnode
+ * since that can block.
+ */
+ if (fdcache_lock & FDL_LOCKED) {
+ fdcache_lock |= FDL_WANT;
+ sleep((caddr_t) &fdcache_lock, PINOD);
+ goto loop;
+ }
+ fdcache_lock |= FDL_LOCKED;
+
+ error = getnewvnode(VT_FDESC, mp, fdesc_vnodeop_p, vpp);
+ if (error)
+ goto out;
+ MALLOC(fd, void *, sizeof(struct fdescnode), M_TEMP, M_WAITOK);
+ (*vpp)->v_data = fd;
+ fd->fd_vnode = *vpp;
+ fd->fd_type = ftype;
+ fd->fd_fd = -1;
+ fd->fd_link = 0;
+ fd->fd_ix = ix;
+ fc = fdesc_hash(ix);
+ insque(fd, fc);
+
+out:;
+ fdcache_lock &= ~FDL_LOCKED;
+
+ if (fdcache_lock & FDL_WANT) {
+ fdcache_lock &= ~FDL_WANT;
+ wakeup((caddr_t) &fdcache_lock);
+ }
+
+ return (error);
+}
+
+/*
+ * vp is the current namei directory
+ * ndp is the name to locate in that directory...
+ */
+int
+fdesc_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode * a_dvp;
+ struct vnode ** a_vpp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+ struct vnode **vpp = ap->a_vpp;
+ struct vnode *dvp = ap->a_dvp;
+ char *pname;
+ struct proc *p;
+ int nfiles;
+ unsigned fd = 0;
+ int error;
+ struct vnode *fvp;
+ char *ln;
+
+ pname = ap->a_cnp->cn_nameptr;
+ if (ap->a_cnp->cn_namelen == 1 && *pname == '.') {
+ *vpp = dvp;
+ VREF(dvp);
+ VOP_LOCK(dvp);
+ return (0);
+ }
+
+ p = ap->a_cnp->cn_proc;
+ nfiles = p->p_fd->fd_nfiles;
+
+ switch (VTOFDESC(dvp)->fd_type) {
+ default:
+ case Flink:
+ case Fdesc:
+ case Fctty:
+ error = ENOTDIR;
+ goto bad;
+
+ case Froot:
+ if (ap->a_cnp->cn_namelen == 2 && bcmp(pname, "fd", 2) == 0) {
+ error = fdesc_allocvp(Fdevfd, FD_DEVFD, dvp->v_mount, &fvp);
+ if (error)
+ goto bad;
+ *vpp = fvp;
+ fvp->v_type = VDIR;
+ VOP_LOCK(fvp);
+ return (0);
+ }
+
+ if (ap->a_cnp->cn_namelen == 3 && bcmp(pname, "tty", 3) == 0) {
+ struct vnode *ttyvp = cttyvp(p);
+ if (ttyvp == NULL) {
+ error = ENXIO;
+ goto bad;
+ }
+ error = fdesc_allocvp(Fctty, FD_CTTY, dvp->v_mount, &fvp);
+ if (error)
+ goto bad;
+ *vpp = fvp;
+ fvp->v_type = VFIFO;
+ VOP_LOCK(fvp);
+ return (0);
+ }
+
+ ln = 0;
+ switch (ap->a_cnp->cn_namelen) {
+ case 5:
+ if (bcmp(pname, "stdin", 5) == 0) {
+ ln = "fd/0";
+ fd = FD_STDIN;
+ }
+ break;
+ case 6:
+ if (bcmp(pname, "stdout", 6) == 0) {
+ ln = "fd/1";
+ fd = FD_STDOUT;
+ } else
+ if (bcmp(pname, "stderr", 6) == 0) {
+ ln = "fd/2";
+ fd = FD_STDERR;
+ }
+ break;
+ }
+
+ if (ln) {
+ error = fdesc_allocvp(Flink, fd, dvp->v_mount, &fvp);
+ if (error)
+ goto bad;
+ VTOFDESC(fvp)->fd_link = ln;
+ *vpp = fvp;
+ fvp->v_type = VLNK;
+ VOP_LOCK(fvp);
+ return (0);
+ } else {
+ error = ENOENT;
+ goto bad;
+ }
+
+ /* FALL THROUGH */
+
+ case Fdevfd:
+ if (ap->a_cnp->cn_namelen == 2 && bcmp(pname, "..", 2) == 0) {
+ error = fdesc_root(dvp->v_mount, vpp);
+ return (error);
+ }
+
+ fd = 0;
+ while (*pname >= '0' && *pname <= '9') {
+ fd = 10 * fd + *pname++ - '0';
+ if (fd >= nfiles)
+ break;
+ }
+
+ if (*pname != '\0') {
+ error = ENOENT;
+ goto bad;
+ }
+
+ if (fd >= nfiles || p->p_fd->fd_ofiles[fd] == NULL) {
+ error = EBADF;
+ goto bad;
+ }
+
+ error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp);
+ if (error)
+ goto bad;
+ VTOFDESC(fvp)->fd_fd = fd;
+ *vpp = fvp;
+ return (0);
+ }
+
+bad:;
+ *vpp = NULL;
+ return (error);
+}
+
+int
+fdesc_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ int error = 0;
+
+ switch (VTOFDESC(vp)->fd_type) {
+ case Fdesc:
+ /*
+ * XXX Kludge: set p->p_dupfd to contain the value of the
+ * the file descriptor being sought for duplication. The error
+ * return ensures that the vnode for this device will be
+ * released by vn_open. Open will detect this special error and
+ * take the actions in dupfdopen. Other callers of vn_open or
+ * VOP_OPEN will simply report the error.
+ */
+ ap->a_p->p_dupfd = VTOFDESC(vp)->fd_fd; /* XXX */
+ error = ENODEV;
+ break;
+
+ case Fctty:
+ error = cttyopen(devctty, ap->a_mode, 0, ap->a_p);
+ break;
+ }
+
+ return (error);
+}
+
+static int
+fdesc_attr(fd, vap, cred, p)
+ int fd;
+ struct vattr *vap;
+ struct ucred *cred;
+ struct proc *p;
+{
+ struct filedesc *fdp = p->p_fd;
+ struct file *fp;
+ struct stat stb;
+ int error;
+
+ if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL)
+ return (EBADF);
+
+ switch (fp->f_type) {
+ case DTYPE_VNODE:
+ error = VOP_GETATTR((struct vnode *) fp->f_data, vap, cred, p);
+ if (error == 0 && vap->va_type == VDIR) {
+ /*
+ * don't allow directories to show up because
+ * that causes loops in the namespace.
+ */
+ vap->va_type = VFIFO;
+ }
+ break;
+
+ case DTYPE_SOCKET:
+ error = soo_stat((struct socket *)fp->f_data, &stb);
+ if (error == 0) {
+ vattr_null(vap);
+ vap->va_type = VSOCK;
+ vap->va_mode = stb.st_mode;
+ vap->va_nlink = stb.st_nlink;
+ vap->va_uid = stb.st_uid;
+ vap->va_gid = stb.st_gid;
+ vap->va_fsid = stb.st_dev;
+ vap->va_fileid = stb.st_ino;
+ vap->va_size = stb.st_size;
+ vap->va_blocksize = stb.st_blksize;
+ vap->va_atime = stb.st_atimespec;
+ vap->va_mtime = stb.st_mtimespec;
+ vap->va_ctime = stb.st_ctimespec;
+ vap->va_gen = stb.st_gen;
+ vap->va_flags = stb.st_flags;
+ vap->va_rdev = stb.st_rdev;
+ vap->va_bytes = stb.st_blocks * stb.st_blksize;
+ }
+ break;
+
+ default:
+ panic("fdesc attr");
+ break;
+ }
+
+ return (error);
+}
+
+int
+fdesc_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct vattr *vap = ap->a_vap;
+ unsigned fd;
+ int error = 0;
+
+ switch (VTOFDESC(vp)->fd_type) {
+ case Froot:
+ case Fdevfd:
+ case Flink:
+ case Fctty:
+ bzero((caddr_t) vap, sizeof(*vap));
+ vattr_null(vap);
+ vap->va_fileid = VTOFDESC(vp)->fd_ix;
+
+ switch (VTOFDESC(vp)->fd_type) {
+ case Flink:
+ vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
+ vap->va_type = VLNK;
+ vap->va_nlink = 1;
+ vap->va_size = strlen(VTOFDESC(vp)->fd_link);
+ break;
+
+ case Fctty:
+ vap->va_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+ vap->va_type = VFIFO;
+ vap->va_nlink = 1;
+ vap->va_size = 0;
+ break;
+
+ default:
+ vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
+ vap->va_type = VDIR;
+ vap->va_nlink = 2;
+ vap->va_size = DEV_BSIZE;
+ break;
+ }
+ vap->va_uid = 0;
+ vap->va_gid = 0;
+ vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+ vap->va_blocksize = DEV_BSIZE;
+ vap->va_atime.ts_sec = boottime.tv_sec;
+ vap->va_atime.ts_nsec = 0;
+ vap->va_mtime = vap->va_atime;
+ vap->va_ctime = vap->va_mtime;
+ vap->va_gen = 0;
+ vap->va_flags = 0;
+ vap->va_rdev = 0;
+ vap->va_bytes = 0;
+ break;
+
+ case Fdesc:
+ fd = VTOFDESC(vp)->fd_fd;
+ error = fdesc_attr(fd, vap, ap->a_cred, ap->a_p);
+ break;
+
+ default:
+ panic("fdesc_getattr");
+ break;
+ }
+
+ if (error == 0)
+ vp->v_type = vap->va_type;
+
+ return (error);
+}
+
+int
+fdesc_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct filedesc *fdp = ap->a_p->p_fd;
+ struct file *fp;
+ unsigned fd;
+ int error;
+
+ /*
+ * Can't mess with the root vnode
+ */
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fdesc:
+ break;
+
+ case Fctty:
+ return (0);
+
+ default:
+ return (EACCES);
+ }
+
+ fd = VTOFDESC(ap->a_vp)->fd_fd;
+ if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) {
+ return (EBADF);
+ }
+
+ /*
+ * Can setattr the underlying vnode, but not sockets!
+ */
+ switch (fp->f_type) {
+ case DTYPE_VNODE:
+ error = VOP_SETATTR((struct vnode *) fp->f_data, ap->a_vap, ap->a_cred, ap->a_p);
+ break;
+
+ case DTYPE_SOCKET:
+ error = 0;
+ break;
+
+ default:
+ panic("fdesc setattr");
+ break;
+ }
+
+ return (error);
+}
+
+#define UIO_MX 16
+
+static struct dirtmp {
+ u_long d_fileno;
+ u_short d_reclen;
+ u_short d_namlen;
+ char d_name[8];
+} rootent[] = {
+ { FD_DEVFD, UIO_MX, 2, "fd" },
+ { FD_STDIN, UIO_MX, 5, "stdin" },
+ { FD_STDOUT, UIO_MX, 6, "stdout" },
+ { FD_STDERR, UIO_MX, 6, "stderr" },
+ { FD_CTTY, UIO_MX, 3, "tty" },
+ { 0 }
+};
+
+int
+fdesc_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct uio *uio = ap->a_uio;
+ struct filedesc *fdp;
+ int i;
+ int error;
+
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fctty:
+ return (0);
+
+ case Fdesc:
+ return (ENOTDIR);
+
+ default:
+ break;
+ }
+
+ fdp = uio->uio_procp->p_fd;
+
+ if (VTOFDESC(ap->a_vp)->fd_type == Froot) {
+ struct dirent d;
+ struct dirent *dp = &d;
+ struct dirtmp *dt;
+
+ i = uio->uio_offset / UIO_MX;
+ error = 0;
+
+ while (uio->uio_resid > 0) {
+ dt = &rootent[i];
+ if (dt->d_fileno == 0) {
+ /**eofflagp = 1;*/
+ break;
+ }
+ i++;
+
+ switch (dt->d_fileno) {
+ case FD_CTTY:
+ if (cttyvp(uio->uio_procp) == NULL)
+ continue;
+ break;
+
+ case FD_STDIN:
+ case FD_STDOUT:
+ case FD_STDERR:
+ if ((dt->d_fileno-FD_STDIN) >= fdp->fd_nfiles)
+ continue;
+ if (fdp->fd_ofiles[dt->d_fileno-FD_STDIN] == NULL)
+ continue;
+ break;
+ }
+ bzero((caddr_t) dp, UIO_MX);
+ dp->d_fileno = dt->d_fileno;
+ dp->d_namlen = dt->d_namlen;
+ dp->d_type = DT_UNKNOWN;
+ dp->d_reclen = dt->d_reclen;
+ bcopy(dt->d_name, dp->d_name, dp->d_namlen+1);
+ error = uiomove((caddr_t) dp, UIO_MX, uio);
+ if (error)
+ break;
+ }
+ uio->uio_offset = i * UIO_MX;
+ return (error);
+ }
+
+ i = uio->uio_offset / UIO_MX;
+ error = 0;
+ while (uio->uio_resid > 0) {
+ if (i >= fdp->fd_nfiles)
+ break;
+
+ if (fdp->fd_ofiles[i] != NULL) {
+ struct dirent d;
+ struct dirent *dp = &d;
+
+ bzero((caddr_t) dp, UIO_MX);
+
+ dp->d_namlen = sprintf(dp->d_name, "%d", i);
+ dp->d_reclen = UIO_MX;
+ dp->d_type = DT_UNKNOWN;
+ dp->d_fileno = i + FD_STDIN;
+ /*
+ * And ship to userland
+ */
+ error = uiomove((caddr_t) dp, UIO_MX, uio);
+ if (error)
+ break;
+ }
+ i++;
+ }
+
+ uio->uio_offset = i * UIO_MX;
+ return (error);
+}
+
+int
+fdesc_readlink(ap)
+ struct vop_readlink_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ int error;
+
+ if (vp->v_type != VLNK)
+ return (EPERM);
+
+ if (VTOFDESC(vp)->fd_type == Flink) {
+ char *ln = VTOFDESC(vp)->fd_link;
+ error = uiomove(ln, strlen(ln), ap->a_uio);
+ } else {
+ error = EOPNOTSUPP;
+ }
+
+ return (error);
+}
+
+int
+fdesc_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error = EOPNOTSUPP;
+
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fctty:
+ error = cttyread(devctty, ap->a_uio, ap->a_ioflag);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
+
+int
+fdesc_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error = EOPNOTSUPP;
+
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fctty:
+ error = cttywrite(devctty, ap->a_uio, ap->a_ioflag);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
+
+int
+fdesc_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error = EOPNOTSUPP;
+
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fctty:
+ error = cttyioctl(devctty, ap->a_command, ap->a_data,
+ ap->a_fflag, ap->a_p);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
+
+int
+fdesc_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error = EOPNOTSUPP;
+
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fctty:
+ error = cttyselect(devctty, ap->a_fflags, ap->a_p);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
+
+int
+fdesc_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+
+ /*
+ * Clear out the v_type field to avoid
+ * nasty things happening in vgone().
+ */
+ vp->v_type = VNON;
+ return (0);
+}
+
+int
+fdesc_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+
+ remque(VTOFDESC(vp));
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = 0;
+
+ return (0);
+}
+
+/*
+ * Return POSIX pathconf information applicable to special devices.
+ */
+int
+fdesc_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_MAX_CANON:
+ *ap->a_retval = MAX_CANON;
+ return (0);
+ case _PC_MAX_INPUT:
+ *ap->a_retval = MAX_INPUT;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ case _PC_VDISABLE:
+ *ap->a_retval = _POSIX_VDISABLE;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Print out the contents of a /dev/fd vnode.
+ */
+/* ARGSUSED */
+int
+fdesc_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_NON, fdesc vnode\n");
+ return (0);
+}
+
+/*void*/
+int
+fdesc_vfree(ap)
+ struct vop_vfree_args /* {
+ struct vnode *a_pvp;
+ ino_t a_ino;
+ int a_mode;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * /dev/fd vnode unsupported operation
+ */
+int
+fdesc_enotsupp()
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * /dev/fd "should never get here" operation
+ */
+int
+fdesc_badop()
+{
+
+ panic("fdesc: bad op");
+ /* NOTREACHED */
+}
+
+/*
+ * /dev/fd vnode null operation
+ */
+int
+fdesc_nullop()
+{
+
+ return (0);
+}
+
+#define fdesc_create ((int (*) __P((struct vop_create_args *)))fdesc_enotsupp)
+#define fdesc_mknod ((int (*) __P((struct vop_mknod_args *)))fdesc_enotsupp)
+#define fdesc_close ((int (*) __P((struct vop_close_args *)))nullop)
+#define fdesc_access ((int (*) __P((struct vop_access_args *)))nullop)
+#define fdesc_mmap ((int (*) __P((struct vop_mmap_args *)))fdesc_enotsupp)
+#define fdesc_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
+#define fdesc_seek ((int (*) __P((struct vop_seek_args *)))nullop)
+#define fdesc_remove ((int (*) __P((struct vop_remove_args *)))fdesc_enotsupp)
+#define fdesc_link ((int (*) __P((struct vop_link_args *)))fdesc_enotsupp)
+#define fdesc_rename ((int (*) __P((struct vop_rename_args *)))fdesc_enotsupp)
+#define fdesc_mkdir ((int (*) __P((struct vop_mkdir_args *)))fdesc_enotsupp)
+#define fdesc_rmdir ((int (*) __P((struct vop_rmdir_args *)))fdesc_enotsupp)
+#define fdesc_symlink ((int (*) __P((struct vop_symlink_args *)))fdesc_enotsupp)
+#define fdesc_abortop ((int (*) __P((struct vop_abortop_args *)))nullop)
+#define fdesc_lock ((int (*) __P((struct vop_lock_args *)))nullop)
+#define fdesc_unlock ((int (*) __P((struct vop_unlock_args *)))nullop)
+#define fdesc_bmap ((int (*) __P((struct vop_bmap_args *)))fdesc_badop)
+#define fdesc_strategy ((int (*) __P((struct vop_strategy_args *)))fdesc_badop)
+#define fdesc_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+#define fdesc_advlock ((int (*) __P((struct vop_advlock_args *)))fdesc_enotsupp)
+#define fdesc_blkatoff \
+ ((int (*) __P((struct vop_blkatoff_args *)))fdesc_enotsupp)
+#define fdesc_vget ((int (*) __P((struct vop_vget_args *)))fdesc_enotsupp)
+#define fdesc_valloc ((int(*) __P(( \
+ struct vnode *pvp, \
+ int mode, \
+ struct ucred *cred, \
+ struct vnode **vpp))) fdesc_enotsupp)
+#define fdesc_truncate \
+ ((int (*) __P((struct vop_truncate_args *)))fdesc_enotsupp)
+#define fdesc_update ((int (*) __P((struct vop_update_args *)))fdesc_enotsupp)
+#define fdesc_bwrite ((int (*) __P((struct vop_bwrite_args *)))fdesc_enotsupp)
+
+int (**fdesc_vnodeop_p)();
+struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, fdesc_lookup }, /* lookup */
+ { &vop_create_desc, fdesc_create }, /* create */
+ { &vop_mknod_desc, fdesc_mknod }, /* mknod */
+ { &vop_open_desc, fdesc_open }, /* open */
+ { &vop_close_desc, fdesc_close }, /* close */
+ { &vop_access_desc, fdesc_access }, /* access */
+ { &vop_getattr_desc, fdesc_getattr }, /* getattr */
+ { &vop_setattr_desc, fdesc_setattr }, /* setattr */
+ { &vop_read_desc, fdesc_read }, /* read */
+ { &vop_write_desc, fdesc_write }, /* write */
+ { &vop_ioctl_desc, fdesc_ioctl }, /* ioctl */
+ { &vop_select_desc, fdesc_select }, /* select */
+ { &vop_mmap_desc, fdesc_mmap }, /* mmap */
+ { &vop_fsync_desc, fdesc_fsync }, /* fsync */
+ { &vop_seek_desc, fdesc_seek }, /* seek */
+ { &vop_remove_desc, fdesc_remove }, /* remove */
+ { &vop_link_desc, fdesc_link }, /* link */
+ { &vop_rename_desc, fdesc_rename }, /* rename */
+ { &vop_mkdir_desc, fdesc_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, fdesc_rmdir }, /* rmdir */
+ { &vop_symlink_desc, fdesc_symlink }, /* symlink */
+ { &vop_readdir_desc, fdesc_readdir }, /* readdir */
+ { &vop_readlink_desc, fdesc_readlink }, /* readlink */
+ { &vop_abortop_desc, fdesc_abortop }, /* abortop */
+ { &vop_inactive_desc, fdesc_inactive }, /* inactive */
+ { &vop_reclaim_desc, fdesc_reclaim }, /* reclaim */
+ { &vop_lock_desc, fdesc_lock }, /* lock */
+ { &vop_unlock_desc, fdesc_unlock }, /* unlock */
+ { &vop_bmap_desc, fdesc_bmap }, /* bmap */
+ { &vop_strategy_desc, fdesc_strategy }, /* strategy */
+ { &vop_print_desc, fdesc_print }, /* print */
+ { &vop_islocked_desc, fdesc_islocked }, /* islocked */
+ { &vop_pathconf_desc, fdesc_pathconf }, /* pathconf */
+ { &vop_advlock_desc, fdesc_advlock }, /* advlock */
+ { &vop_blkatoff_desc, fdesc_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, fdesc_valloc }, /* valloc */
+ { &vop_vfree_desc, fdesc_vfree }, /* vfree */
+ { &vop_truncate_desc, fdesc_truncate }, /* truncate */
+ { &vop_update_desc, fdesc_update }, /* update */
+ { &vop_bwrite_desc, fdesc_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc fdesc_vnodeop_opv_desc =
+ { &fdesc_vnodeop_p, fdesc_vnodeop_entries };
diff --git a/sys/miscfs/fifofs/fifo.h b/sys/miscfs/fifofs/fifo.h
new file mode 100644
index 0000000..e89186d
--- /dev/null
+++ b/sys/miscfs/fifofs/fifo.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fifo.h 8.2 (Berkeley) 2/2/94
+ */
+
+#ifdef FIFO
+/*
+ * Prototypes for fifo operations on vnodes.
+ */
+int fifo_badop(),
+ fifo_ebadf();
+
+int fifo_lookup __P((struct vop_lookup_args *));
+#define fifo_create ((int (*) __P((struct vop_create_args *)))fifo_badop)
+#define fifo_mknod ((int (*) __P((struct vop_mknod_args *)))fifo_badop)
+int fifo_open __P((struct vop_open_args *));
+int fifo_close __P((struct vop_close_args *));
+#define fifo_access ((int (*) __P((struct vop_access_args *)))fifo_ebadf)
+#define fifo_getattr ((int (*) __P((struct vop_getattr_args *)))fifo_ebadf)
+#define fifo_setattr ((int (*) __P((struct vop_setattr_args *)))fifo_ebadf)
+int fifo_read __P((struct vop_read_args *));
+int fifo_write __P((struct vop_write_args *));
+int fifo_ioctl __P((struct vop_ioctl_args *));
+int fifo_select __P((struct vop_select_args *));
+#define fifo_mmap ((int (*) __P((struct vop_mmap_args *)))fifo_badop)
+#define fifo_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
+#define fifo_seek ((int (*) __P((struct vop_seek_args *)))fifo_badop)
+#define fifo_remove ((int (*) __P((struct vop_remove_args *)))fifo_badop)
+#define fifo_link ((int (*) __P((struct vop_link_args *)))fifo_badop)
+#define fifo_rename ((int (*) __P((struct vop_rename_args *)))fifo_badop)
+#define fifo_mkdir ((int (*) __P((struct vop_mkdir_args *)))fifo_badop)
+#define fifo_rmdir ((int (*) __P((struct vop_rmdir_args *)))fifo_badop)
+#define fifo_symlink ((int (*) __P((struct vop_symlink_args *)))fifo_badop)
+#define fifo_readdir ((int (*) __P((struct vop_readdir_args *)))fifo_badop)
+#define fifo_readlink ((int (*) __P((struct vop_readlink_args *)))fifo_badop)
+#define fifo_abortop ((int (*) __P((struct vop_abortop_args *)))fifo_badop)
+#define fifo_inactive ((int (*) __P((struct vop_inactive_args *)))nullop)
+#define fifo_reclaim ((int (*) __P((struct vop_reclaim_args *)))nullop)
+int fifo_lock __P((struct vop_lock_args *));
+int fifo_unlock __P((struct vop_unlock_args *));
+int fifo_bmap __P((struct vop_bmap_args *));
+#define fifo_strategy ((int (*) __P((struct vop_strategy_args *)))fifo_badop)
+int fifo_print __P((struct vop_print_args *));
+#define fifo_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+int fifo_pathconf __P((struct vop_pathconf_args *));
+int fifo_advlock __P((struct vop_advlock_args *));
+#define fifo_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))fifo_badop)
+#define fifo_valloc ((int (*) __P((struct vop_valloc_args *)))fifo_badop)
+#define fifo_reallocblks \
+ ((int (*) __P((struct vop_reallocblks_args *)))fifo_badop)
+#define fifo_vfree ((int (*) __P((struct vop_vfree_args *)))fifo_badop)
+#define fifo_truncate ((int (*) __P((struct vop_truncate_args *)))nullop)
+#define fifo_update ((int (*) __P((struct vop_update_args *)))nullop)
+#define fifo_bwrite ((int (*) __P((struct vop_bwrite_args *)))nullop)
+#endif /* FIFO */
diff --git a/sys/miscfs/fifofs/fifo_vnops.c b/sys/miscfs/fifofs/fifo_vnops.c
new file mode 100644
index 0000000..a1ba3f4
--- /dev/null
+++ b/sys/miscfs/fifofs/fifo_vnops.c
@@ -0,0 +1,512 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fifo_vnops.c 8.2 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/time.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/stat.h>
+#include <sys/systm.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <miscfs/fifofs/fifo.h>
+
+/*
+ * This structure is associated with the FIFO vnode and stores
+ * the state associated with the FIFO.
+ */
+struct fifoinfo {
+ struct socket *fi_readsock;
+ struct socket *fi_writesock;
+ long fi_readers;
+ long fi_writers;
+};
+
+int (**fifo_vnodeop_p)();
+struct vnodeopv_entry_desc fifo_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, fifo_lookup }, /* lookup */
+ { &vop_create_desc, fifo_create }, /* create */
+ { &vop_mknod_desc, fifo_mknod }, /* mknod */
+ { &vop_open_desc, fifo_open }, /* open */
+ { &vop_close_desc, fifo_close }, /* close */
+ { &vop_access_desc, fifo_access }, /* access */
+ { &vop_getattr_desc, fifo_getattr }, /* getattr */
+ { &vop_setattr_desc, fifo_setattr }, /* setattr */
+ { &vop_read_desc, fifo_read }, /* read */
+ { &vop_write_desc, fifo_write }, /* write */
+ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */
+ { &vop_select_desc, fifo_select }, /* select */
+ { &vop_mmap_desc, fifo_mmap }, /* mmap */
+ { &vop_fsync_desc, fifo_fsync }, /* fsync */
+ { &vop_seek_desc, fifo_seek }, /* seek */
+ { &vop_remove_desc, fifo_remove }, /* remove */
+ { &vop_link_desc, fifo_link }, /* link */
+ { &vop_rename_desc, fifo_rename }, /* rename */
+ { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */
+ { &vop_symlink_desc, fifo_symlink }, /* symlink */
+ { &vop_readdir_desc, fifo_readdir }, /* readdir */
+ { &vop_readlink_desc, fifo_readlink }, /* readlink */
+ { &vop_abortop_desc, fifo_abortop }, /* abortop */
+ { &vop_inactive_desc, fifo_inactive }, /* inactive */
+ { &vop_reclaim_desc, fifo_reclaim }, /* reclaim */
+ { &vop_lock_desc, fifo_lock }, /* lock */
+ { &vop_unlock_desc, fifo_unlock }, /* unlock */
+ { &vop_bmap_desc, fifo_bmap }, /* bmap */
+ { &vop_strategy_desc, fifo_strategy }, /* strategy */
+ { &vop_print_desc, fifo_print }, /* print */
+ { &vop_islocked_desc, fifo_islocked }, /* islocked */
+ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */
+ { &vop_advlock_desc, fifo_advlock }, /* advlock */
+ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, fifo_valloc }, /* valloc */
+ { &vop_vfree_desc, fifo_vfree }, /* vfree */
+ { &vop_truncate_desc, fifo_truncate }, /* truncate */
+ { &vop_update_desc, fifo_update }, /* update */
+ { &vop_bwrite_desc, fifo_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc fifo_vnodeop_opv_desc =
+ { &fifo_vnodeop_p, fifo_vnodeop_entries };
+
+/*
+ * Trivial lookup routine that always fails.
+ */
+/* ARGSUSED */
+int
+fifo_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode * a_dvp;
+ struct vnode ** a_vpp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+
+ *ap->a_vpp = NULL;
+ return (ENOTDIR);
+}
+
+/*
+ * Open called to set up a new instance of a fifo or
+ * to find an active instance of a fifo.
+ */
+/* ARGSUSED */
+int
+fifo_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct fifoinfo *fip;
+ struct socket *rso, *wso;
+ int error;
+ static char openstr[] = "fifo";
+
+ if ((ap->a_mode & (FREAD|FWRITE)) == (FREAD|FWRITE))
+ return (EINVAL);
+ if ((fip = vp->v_fifoinfo) == NULL) {
+ MALLOC(fip, struct fifoinfo *, sizeof(*fip), M_VNODE, M_WAITOK);
+ vp->v_fifoinfo = fip;
+ if (error = socreate(AF_UNIX, &rso, SOCK_STREAM, 0)) {
+ free(fip, M_VNODE);
+ vp->v_fifoinfo = NULL;
+ return (error);
+ }
+ fip->fi_readsock = rso;
+ if (error = socreate(AF_UNIX, &wso, SOCK_STREAM, 0)) {
+ (void)soclose(rso);
+ free(fip, M_VNODE);
+ vp->v_fifoinfo = NULL;
+ return (error);
+ }
+ fip->fi_writesock = wso;
+ if (error = unp_connect2(wso, rso)) {
+ (void)soclose(wso);
+ (void)soclose(rso);
+ free(fip, M_VNODE);
+ vp->v_fifoinfo = NULL;
+ return (error);
+ }
+ fip->fi_readers = fip->fi_writers = 0;
+ wso->so_state |= SS_CANTRCVMORE;
+ rso->so_state |= SS_CANTSENDMORE;
+ }
+ error = 0;
+ if (ap->a_mode & FREAD) {
+ fip->fi_readers++;
+ if (fip->fi_readers == 1) {
+ fip->fi_writesock->so_state &= ~SS_CANTSENDMORE;
+ if (fip->fi_writers > 0)
+ wakeup((caddr_t)&fip->fi_writers);
+ }
+ if (ap->a_mode & O_NONBLOCK)
+ return (0);
+ while (fip->fi_writers == 0) {
+ VOP_UNLOCK(vp);
+ error = tsleep((caddr_t)&fip->fi_readers,
+ PCATCH | PSOCK, openstr, 0);
+ VOP_LOCK(vp);
+ if (error)
+ break;
+ }
+ } else {
+ fip->fi_writers++;
+ if (fip->fi_readers == 0 && (ap->a_mode & O_NONBLOCK)) {
+ error = ENXIO;
+ } else {
+ if (fip->fi_writers == 1) {
+ fip->fi_readsock->so_state &= ~SS_CANTRCVMORE;
+ if (fip->fi_readers > 0)
+ wakeup((caddr_t)&fip->fi_readers);
+ }
+ while (fip->fi_readers == 0) {
+ VOP_UNLOCK(vp);
+ error = tsleep((caddr_t)&fip->fi_writers,
+ PCATCH | PSOCK, openstr, 0);
+ VOP_LOCK(vp);
+ if (error)
+ break;
+ }
+ }
+ }
+ if (error)
+ VOP_CLOSE(vp, ap->a_mode, ap->a_cred, ap->a_p);
+ return (error);
+}
+
+/*
+ * Vnode op for read
+ */
+/* ARGSUSED */
+int
+fifo_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct uio *uio = ap->a_uio;
+ register struct socket *rso = ap->a_vp->v_fifoinfo->fi_readsock;
+ int error, startresid;
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_READ)
+ panic("fifo_read mode");
+#endif
+ if (uio->uio_resid == 0)
+ return (0);
+ if (ap->a_ioflag & IO_NDELAY)
+ rso->so_state |= SS_NBIO;
+ startresid = uio->uio_resid;
+ VOP_UNLOCK(ap->a_vp);
+ error = soreceive(rso, (struct mbuf **)0, uio, (int *)0,
+ (struct mbuf **)0, (struct mbuf **)0);
+ VOP_LOCK(ap->a_vp);
+ /*
+ * Clear EOF indication after first such return.
+ */
+ if (uio->uio_resid == startresid)
+ rso->so_state &= ~SS_CANTRCVMORE;
+ if (ap->a_ioflag & IO_NDELAY)
+ rso->so_state &= ~SS_NBIO;
+ return (error);
+}
+
+/*
+ * Vnode op for write
+ */
+/* ARGSUSED */
+int
+fifo_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct socket *wso = ap->a_vp->v_fifoinfo->fi_writesock;
+ int error;
+
+#ifdef DIAGNOSTIC
+ if (ap->a_uio->uio_rw != UIO_WRITE)
+ panic("fifo_write mode");
+#endif
+ if (ap->a_ioflag & IO_NDELAY)
+ wso->so_state |= SS_NBIO;
+ VOP_UNLOCK(ap->a_vp);
+ error = sosend(wso, (struct mbuf *)0, ap->a_uio, 0, (struct mbuf *)0, 0);
+ VOP_LOCK(ap->a_vp);
+ if (ap->a_ioflag & IO_NDELAY)
+ wso->so_state &= ~SS_NBIO;
+ return (error);
+}
+
+/*
+ * Device ioctl operation.
+ */
+/* ARGSUSED */
+int
+fifo_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct file filetmp;
+
+ if (ap->a_command == FIONBIO)
+ return (0);
+ if (ap->a_fflag & FREAD)
+ filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock;
+ else
+ filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock;
+ return (soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p));
+}
+
+/* ARGSUSED */
+int
+fifo_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct file filetmp;
+
+ if (ap->a_fflags & FREAD)
+ filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock;
+ else
+ filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock;
+ return (soo_select(&filetmp, ap->a_which, ap->a_p));
+}
+
+/*
+ * This is a noop, simply returning what one has been given.
+ */
+int
+fifo_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ } */ *ap;
+{
+
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = ap->a_vp;
+ if (ap->a_bnp != NULL)
+ *ap->a_bnp = ap->a_bn;
+ return (0);
+}
+
+/*
+ * At the moment we do not do any locking.
+ */
+/* ARGSUSED */
+int
+fifo_lock(ap)
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/* ARGSUSED */
+int
+fifo_unlock(ap)
+ struct vop_unlock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * Device close routine
+ */
+/* ARGSUSED */
+int
+fifo_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct fifoinfo *fip = vp->v_fifoinfo;
+ int error1, error2;
+
+ if (ap->a_fflag & FWRITE) {
+ fip->fi_writers--;
+ if (fip->fi_writers == 0)
+ socantrcvmore(fip->fi_readsock);
+ } else {
+ fip->fi_readers--;
+ if (fip->fi_readers == 0)
+ socantsendmore(fip->fi_writesock);
+ }
+ if (vp->v_usecount > 1)
+ return (0);
+ error1 = soclose(fip->fi_readsock);
+ error2 = soclose(fip->fi_writesock);
+ FREE(fip, M_VNODE);
+ vp->v_fifoinfo = NULL;
+ if (error1)
+ return (error1);
+ return (error2);
+}
+
+/*
+ * Print out the contents of a fifo vnode.
+ */
+int
+fifo_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_NON");
+ fifo_printinfo(ap->a_vp);
+ printf("\n");
+ return (0);
+}
+
+/*
+ * Print out internal contents of a fifo vnode.
+ */
+int
+fifo_printinfo(vp)
+ struct vnode *vp;
+{
+ register struct fifoinfo *fip = vp->v_fifoinfo;
+
+ printf(", fifo with %d readers and %d writers",
+ fip->fi_readers, fip->fi_writers);
+ return (0);
+}
+
+/*
+ * Return POSIX pathconf information applicable to fifo's.
+ */
+int
+fifo_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Fifo failed operation
+ */
+int
+fifo_ebadf()
+{
+
+ return (EBADF);
+}
+
+/*
+ * Fifo advisory byte-level locks.
+ */
+/* ARGSUSED */
+int
+fifo_advlock(ap)
+ struct vop_advlock_args /* {
+ struct vnode *a_vp;
+ caddr_t a_id;
+ int a_op;
+ struct flock *a_fl;
+ int a_flags;
+ } */ *ap;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * Fifo bad operation
+ */
+int
+fifo_badop()
+{
+
+ panic("fifo_badop called");
+ /* NOTREACHED */
+}
diff --git a/sys/miscfs/kernfs/kernfs.h b/sys/miscfs/kernfs/kernfs.h
new file mode 100644
index 0000000..75ddecc
--- /dev/null
+++ b/sys/miscfs/kernfs/kernfs.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kernfs.h 8.4 (Berkeley) 1/21/94
+ */
+
+#define _PATH_KERNFS "/kern" /* Default mountpoint */
+
+#ifdef KERNEL
+struct kernfs_mount {
+ struct vnode *kf_root; /* Root node */
+};
+
+struct kernfs_node {
+ struct kern_target *kf_kt;
+};
+
+#define VFSTOKERNFS(mp) ((struct kernfs_mount *)((mp)->mnt_data))
+#define VTOKERN(vp) ((struct kernfs_node *)(vp)->v_data)
+
+extern int (**kernfs_vnodeop_p)();
+extern struct vfsops kernfs_vfsops;
+extern struct vnode *rrootvp;
+#endif /* KERNEL */
diff --git a/sys/miscfs/kernfs/kernfs_vfsops.c b/sys/miscfs/kernfs/kernfs_vfsops.c
new file mode 100644
index 0000000..a3fa803
--- /dev/null
+++ b/sys/miscfs/kernfs/kernfs_vfsops.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kernfs_vfsops.c 8.4 (Berkeley) 1/21/94
+ */
+
+/*
+ * Kernel params Filesystem
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+
+#include <miscfs/specfs/specdev.h>
+#include <miscfs/kernfs/kernfs.h>
+
+struct vnode *rrootvp;
+
+/*
+ * Create a vnode for a character device.
+ */
+int
+cdevvp(dev, vpp)
+ dev_t dev;
+ struct vnode **vpp;
+{
+ register struct vnode *vp;
+ struct vnode *nvp;
+ int error;
+
+ if (dev == NODEV)
+ return (0);
+ error = getnewvnode(VT_NON, (struct mount *)0, spec_vnodeop_p, &nvp);
+ if (error) {
+ *vpp = 0;
+ return (error);
+ }
+ vp = nvp;
+ vp->v_type = VCHR;
+ if (nvp = checkalias(vp, dev, (struct mount *)0)) {
+ vput(vp);
+ vp = nvp;
+ }
+ *vpp = vp;
+ return (0);
+}
+
+int
+kernfs_init()
+{
+ int cmaj;
+ int bmaj = major(rootdev);
+ int error = ENXIO;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_init\n"); /* printed during system boot */
+#endif
+
+ for (cmaj = 0; cmaj < nchrdev; cmaj++) {
+ if (cdevsw[cmaj].d_open == bdevsw[bmaj].d_open) {
+ dev_t cdev = makedev(cmaj, minor(rootdev));
+ error = cdevvp(cdev, &rrootvp);
+ if (error == 0)
+ break;
+ }
+ }
+
+ if (error) {
+ printf("kernfs: no raw boot device\n");
+ rrootvp = 0;
+ }
+ return (0);
+}
+
+/*
+ * Mount the Kernel params filesystem
+ */
+int
+kernfs_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ int error = 0;
+ u_int size;
+ struct kernfs_mount *fmp;
+ struct vnode *rvp;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_mount(mp = %x)\n", mp);
+#endif
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE)
+ return (EOPNOTSUPP);
+
+ error = getnewvnode(VT_KERNFS, mp, kernfs_vnodeop_p, &rvp); /* XXX */
+ if (error)
+ return (error);
+
+ MALLOC(fmp, struct kernfs_mount *, sizeof(struct kernfs_mount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+ rvp->v_type = VDIR;
+ rvp->v_flag |= VROOT;
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_mount: root vp = %x\n", rvp);
+#endif
+ fmp->kf_root = rvp;
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_data = (qaddr_t) fmp;
+ getnewfsid(mp, MOUNT_KERNFS);
+
+ (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+ bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
+ bcopy("kernfs", mp->mnt_stat.f_mntfromname, sizeof("kernfs"));
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_mount: at %s\n", mp->mnt_stat.f_mntonname);
+#endif
+ return (0);
+}
+
+int
+kernfs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ return (0);
+}
+
+int
+kernfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ int error;
+ int flags = 0;
+ extern int doforce;
+ struct vnode *rootvp = VFSTOKERNFS(mp)->kf_root;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_unmount(mp = %x)\n", mp);
+#endif
+
+ if (mntflags & MNT_FORCE) {
+ /* kernfs can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ /*
+ * Clear out buffer cache. I don't think we
+ * ever get anything cached at this level at the
+ * moment, but who knows...
+ */
+ if (rootvp->v_usecount > 1)
+ return (EBUSY);
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_unmount: calling vflush\n");
+#endif
+ if (error = vflush(mp, rootvp, flags))
+ return (error);
+
+#ifdef KERNFS_DIAGNOSTIC
+ vprint("kernfs root", rootvp);
+#endif
+ /*
+ * Release reference on underlying root vnode
+ */
+ vrele(rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(rootvp);
+ /*
+ * Finally, throw away the kernfs_mount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ mp->mnt_data = 0;
+ return 0;
+}
+
+int
+kernfs_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct vnode *vp;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_root(mp = %x)\n", mp);
+#endif
+
+ /*
+ * Return locked reference to root.
+ */
+ vp = VFSTOKERNFS(mp)->kf_root;
+ VREF(vp);
+ VOP_LOCK(vp);
+ *vpp = vp;
+ return (0);
+}
+
+int
+kernfs_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+ return (EOPNOTSUPP);
+}
+
+int
+kernfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_statfs(mp = %x)\n", mp);
+#endif
+
+ sbp->f_type = MOUNT_KERNFS;
+ sbp->f_flags = 0;
+ sbp->f_bsize = DEV_BSIZE;
+ sbp->f_iosize = DEV_BSIZE;
+ sbp->f_blocks = 2; /* 1K to keep df happy */
+ sbp->f_bfree = 0;
+ sbp->f_bavail = 0;
+ sbp->f_files = 0;
+ sbp->f_ffree = 0;
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+ return (0);
+}
+
+int
+kernfs_sync(mp, waitfor)
+ struct mount *mp;
+ int waitfor;
+{
+ return (0);
+}
+
+/*
+ * Kernfs flat namespace lookup.
+ * Currently unsupported.
+ */
+int
+kernfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+
+int
+kernfs_fhtovp(mp, fhp, setgen, vpp)
+ struct mount *mp;
+ struct fid *fhp;
+ int setgen;
+ struct vnode **vpp;
+{
+ return (EOPNOTSUPP);
+}
+
+int
+kernfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+ return (EOPNOTSUPP);
+}
+
+struct vfsops kernfs_vfsops = {
+ kernfs_mount,
+ kernfs_start,
+ kernfs_unmount,
+ kernfs_root,
+ kernfs_quotactl,
+ kernfs_statfs,
+ kernfs_sync,
+ kernfs_vget,
+ kernfs_fhtovp,
+ kernfs_vptofh,
+ kernfs_init,
+};
diff --git a/sys/miscfs/kernfs/kernfs_vnops.c b/sys/miscfs/kernfs/kernfs_vnops.c
new file mode 100644
index 0000000..db7377d
--- /dev/null
+++ b/sys/miscfs/kernfs/kernfs_vnops.c
@@ -0,0 +1,772 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kernfs_vnops.c 8.6 (Berkeley) 2/10/94
+ */
+
+/*
+ * Kernel parameter filesystem (/kern)
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/vmmeter.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+#include <sys/dirent.h>
+#include <miscfs/kernfs/kernfs.h>
+
+#define KSTRING 256 /* Largest I/O available via this filesystem */
+#define UIO_MX 32
+
+#define READ_MODE (S_IRUSR|S_IRGRP|S_IROTH)
+#define WRITE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)
+#define DIR_MODE (S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+
+struct kern_target {
+ char *kt_name;
+ void *kt_data;
+#define KTT_NULL 1
+#define KTT_TIME 5
+#define KTT_INT 17
+#define KTT_STRING 31
+#define KTT_HOSTNAME 47
+#define KTT_AVENRUN 53
+ int kt_tag;
+ int kt_rw;
+ int kt_vtype;
+} kern_targets[] = {
+/* NOTE: The name must be less than UIO_MX-16 chars in length */
+ /* name data tag ro/rw */
+ { ".", 0, KTT_NULL, VREAD, VDIR },
+ { "..", 0, KTT_NULL, VREAD, VDIR },
+ { "boottime", &boottime.tv_sec, KTT_INT, VREAD, VREG },
+ { "copyright", copyright, KTT_STRING, VREAD, VREG },
+ { "hostname", 0, KTT_HOSTNAME, VREAD|VWRITE, VREG },
+ { "hz", &hz, KTT_INT, VREAD, VREG },
+ { "loadavg", 0, KTT_AVENRUN, VREAD, VREG },
+ { "pagesize", &cnt.v_page_size, KTT_INT, VREAD, VREG },
+ { "physmem", &physmem, KTT_INT, VREAD, VREG },
+#if 0
+ { "root", 0, KTT_NULL, VREAD, VDIR },
+#endif
+ { "rootdev", 0, KTT_NULL, VREAD, VBLK },
+ { "rrootdev", 0, KTT_NULL, VREAD, VCHR },
+ { "time", 0, KTT_TIME, VREAD, VREG },
+ { "version", version, KTT_STRING, VREAD, VREG },
+};
+
+static int nkern_targets = sizeof(kern_targets) / sizeof(kern_targets[0]);
+
+static int
+kernfs_xread(kt, buf, len, lenp)
+ struct kern_target *kt;
+ char *buf;
+ int len;
+ int *lenp;
+{
+ switch (kt->kt_tag) {
+ case KTT_TIME: {
+ struct timeval tv;
+ microtime(&tv);
+ sprintf(buf, "%d %d\n", tv.tv_sec, tv.tv_usec);
+ break;
+ }
+
+ case KTT_INT: {
+ int *ip = kt->kt_data;
+ sprintf(buf, "%d\n", *ip);
+ break;
+ }
+
+ case KTT_STRING: {
+ char *cp = kt->kt_data;
+ int xlen = strlen(cp) + 1;
+
+ if (xlen >= len)
+ return (EINVAL);
+
+ bcopy(cp, buf, xlen);
+ break;
+ }
+
+ case KTT_HOSTNAME: {
+ char *cp = hostname;
+ int xlen = hostnamelen;
+
+ if (xlen >= (len-2))
+ return (EINVAL);
+
+ bcopy(cp, buf, xlen);
+ buf[xlen] = '\n';
+ buf[xlen+1] = '\0';
+ break;
+ }
+
+ case KTT_AVENRUN:
+ sprintf(buf, "%ld %ld %ld %ld\n",
+ averunnable.ldavg[0],
+ averunnable.ldavg[1],
+ averunnable.ldavg[2],
+ averunnable.fscale);
+ break;
+
+ default:
+ return (EINVAL);
+ }
+
+ *lenp = strlen(buf);
+ return (0);
+}
+
+static int
+kernfs_xwrite(kt, buf, len)
+ struct kern_target *kt;
+ char *buf;
+ int len;
+{
+ switch (kt->kt_tag) {
+ case KTT_HOSTNAME: {
+ if (buf[len-1] == '\n')
+ --len;
+ bcopy(buf, hostname, len);
+ hostname[len] = '\0';
+ hostnamelen = len;
+ return (0);
+ }
+
+ default:
+ return (EIO);
+ }
+}
+
+
+/*
+ * vp is the current namei directory
+ * ndp is the name to locate in that directory...
+ */
+int
+kernfs_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode * a_dvp;
+ struct vnode ** a_vpp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+ struct vnode **vpp = ap->a_vpp;
+ struct vnode *dvp = ap->a_dvp;
+ struct componentname *cnp = ap->a_cnp;
+ struct vnode *fvp;
+ int error, i;
+ char *pname;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_lookup(%x)\n", ap);
+ printf("kernfs_lookup(dp = %x, vpp = %x, cnp = %x)\n", dvp, vpp, ap->a_cnp);
+#endif
+ pname = cnp->cn_nameptr;
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_lookup(%s)\n", pname);
+#endif
+ if (cnp->cn_namelen == 1 && *pname == '.') {
+ *vpp = dvp;
+ VREF(dvp);
+ /*VOP_LOCK(dvp);*/
+ return (0);
+ }
+
+#if 0
+ if (cnp->cn_namelen == 4 && bcmp(pname, "root", 4) == 0) {
+ *vpp = rootdir;
+ VREF(rootdir);
+ VOP_LOCK(rootdir);
+ return (0);
+ }
+#endif
+
+ /*
+ * /kern/rootdev is the root device
+ */
+ if (cnp->cn_namelen == 7 && bcmp(pname, "rootdev", 7) == 0) {
+ *vpp = rootvp;
+ VREF(rootvp);
+ VOP_LOCK(rootvp);
+ return (0);
+ }
+
+ /*
+ * /kern/rrootdev is the raw root device
+ */
+ if (cnp->cn_namelen == 8 && bcmp(pname, "rrootdev", 8) == 0) {
+ if (rrootvp) {
+ *vpp = rrootvp;
+ VREF(rrootvp);
+ VOP_LOCK(rrootvp);
+ return (0);
+ }
+ error = ENXIO;
+ goto bad;
+ }
+
+ error = ENOENT;
+
+ for (i = 0; i < nkern_targets; i++) {
+ struct kern_target *kt = &kern_targets[i];
+ if (cnp->cn_namelen == strlen(kt->kt_name) &&
+ bcmp(kt->kt_name, pname, cnp->cn_namelen) == 0) {
+ error = 0;
+ break;
+ }
+ }
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_lookup: i = %d, error = %d\n", i, error);
+#endif
+
+ if (error)
+ goto bad;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_lookup: allocate new vnode\n");
+#endif
+ error = getnewvnode(VT_KERNFS, dvp->v_mount, kernfs_vnodeop_p, &fvp);
+ if (error)
+ goto bad;
+ MALLOC(fvp->v_data, void *, sizeof(struct kernfs_node), M_TEMP, M_WAITOK);
+ VTOKERN(fvp)->kf_kt = &kern_targets[i];
+ fvp->v_type = VTOKERN(fvp)->kf_kt->kt_vtype;
+ *vpp = fvp;
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_lookup: newvp = %x\n", fvp);
+#endif
+ return (0);
+
+bad:;
+ *vpp = NULL;
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_lookup: error = %d\n", error);
+#endif
+ return (error);
+}
+
+int
+kernfs_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+
+ /*
+ * Can always open the root (modulo perms)
+ */
+ if (vp->v_flag & VROOT)
+ return (0);
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_open, mode = %x, file = %s\n",
+ ap->a_mode, VTOKERN(vp)->kf_kt->kt_name);
+#endif
+
+ if ((ap->a_mode & FWRITE) && !(VTOKERN(vp)->kf_kt->kt_rw & VWRITE))
+ return (EOPNOTSUPP);
+
+ return (0);
+}
+
+static int
+kernfs_access(ap)
+ struct vop_access_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct ucred *cred = ap->a_cred;
+ mode_t mode = ap->a_mode;
+
+ if (mode & VEXEC) {
+ if (vp->v_flag & VROOT)
+ return (0);
+ return (EACCES);
+ }
+
+ if (cred->cr_uid == 0) {
+ if ((vp->v_flag & VROOT) == 0) {
+ struct kern_target *kt = VTOKERN(vp)->kf_kt;
+
+ if ((mode & VWRITE) && !(kt->kt_rw & VWRITE))
+ return (EROFS);
+ }
+ return (0);
+ }
+
+ if (mode & VWRITE)
+ return (EACCES);
+
+ return (0);
+}
+
+
+int
+kernfs_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct vattr *vap = ap->a_vap;
+ int error = 0;
+ char strbuf[KSTRING];
+
+ bzero((caddr_t) vap, sizeof(*vap));
+ vattr_null(vap);
+ vap->va_uid = 0;
+ vap->va_gid = 0;
+ vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+ /* vap->va_qsize = 0; */
+ vap->va_blocksize = DEV_BSIZE;
+ microtime(&vap->va_atime);
+ vap->va_mtime = vap->va_atime;
+ vap->va_ctime = vap->va_ctime;
+ vap->va_gen = 0;
+ vap->va_flags = 0;
+ vap->va_rdev = 0;
+ /* vap->va_qbytes = 0; */
+ vap->va_bytes = 0;
+
+ if (vp->v_flag & VROOT) {
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_getattr: stat rootdir\n");
+#endif
+ vap->va_type = VDIR;
+ vap->va_mode = DIR_MODE;
+ vap->va_nlink = 2;
+ vap->va_fileid = 2;
+ vap->va_size = DEV_BSIZE;
+ } else {
+ struct kern_target *kt = VTOKERN(vp)->kf_kt;
+ int nbytes;
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_getattr: stat target %s\n", kt->kt_name);
+#endif
+ vap->va_type = kt->kt_vtype;
+ vap->va_mode = (kt->kt_rw & VWRITE ? WRITE_MODE : READ_MODE);
+ vap->va_nlink = 1;
+ vap->va_fileid = 3 + (kt - kern_targets) / sizeof(*kt);
+ error = kernfs_xread(kt, strbuf, sizeof(strbuf), &nbytes);
+ vap->va_size = nbytes;
+ }
+
+ vp->v_type = vap->va_type;
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_getattr: return error %d\n", error);
+#endif
+ return (error);
+}
+
+int
+kernfs_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ /*
+ * Silently ignore attribute changes.
+ * This allows for open with truncate to have no
+ * effect until some data is written. I want to
+ * do it this way because all writes are atomic.
+ */
+ return (0);
+}
+
+static int
+kernfs_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct uio *uio = ap->a_uio;
+ struct kern_target *kt;
+ char strbuf[KSTRING];
+ int off = uio->uio_offset;
+ int error, len;
+ char *cp;
+
+ if (vp->v_flag & VROOT)
+ return (EOPNOTSUPP);
+
+ kt = VTOKERN(vp)->kf_kt;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kern_read %s\n", kt->kt_name);
+#endif
+
+ len = 0;
+ error = kernfs_xread(kt, strbuf, sizeof(strbuf), &len);
+ if (error)
+ return (error);
+ cp = strbuf + off;
+ len -= off;
+ return (uiomove(cp, len, uio));
+}
+
+static int
+kernfs_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct uio *uio = ap->a_uio;
+ struct kern_target *kt;
+ int error, xlen;
+ char strbuf[KSTRING];
+
+ if (vp->v_flag & VROOT)
+ return (0);
+
+ kt = VTOKERN(vp)->kf_kt;
+
+ if (uio->uio_offset != 0)
+ return (EINVAL);
+
+ xlen = min(uio->uio_resid, KSTRING-1);
+ error = uiomove(strbuf, xlen, uio);
+ if (error)
+ return (error);
+
+ if (uio->uio_resid != 0)
+ return (EIO);
+
+ strbuf[xlen] = '\0';
+ xlen = strlen(strbuf);
+ return (kernfs_xwrite(kt, strbuf, xlen));
+}
+
+
+int
+kernfs_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct uio *uio = ap->a_uio;
+ int i;
+ int error;
+
+ i = uio->uio_offset / UIO_MX;
+ error = 0;
+ while (uio->uio_resid > 0 && i < nkern_targets) {
+ struct dirent d;
+ struct dirent *dp = &d;
+ struct kern_target *kt = &kern_targets[i];
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_readdir: i = %d\n", i);
+#endif
+
+ bzero((caddr_t) dp, UIO_MX);
+
+ dp->d_namlen = strlen(kt->kt_name);
+ bcopy(kt->kt_name, dp->d_name, dp->d_namlen+1);
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_readdir: name = %s, len = %d\n",
+ dp->d_name, dp->d_namlen);
+#endif
+ /*
+ * Fill in the remaining fields
+ */
+ dp->d_reclen = UIO_MX;
+ dp->d_fileno = i + 3;
+ dp->d_type = DT_UNKNOWN; /* XXX */
+ /*
+ * And ship to userland
+ */
+ error = uiomove((caddr_t) dp, UIO_MX, uio);
+ if (error)
+ break;
+ i++;
+ }
+
+ uio->uio_offset = i * UIO_MX;
+
+ return (error);
+}
+
+int
+kernfs_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+
+ /*
+ * Clear out the v_type field to avoid
+ * nasty things happening in vgone().
+ */
+ vp->v_type = VNON;
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_inactive(%x)\n", vp);
+#endif
+ return (0);
+}
+
+int
+kernfs_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_reclaim(%x)\n", vp);
+#endif
+ if (vp->v_data) {
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = 0;
+ }
+ return (0);
+}
+
+/*
+ * Return POSIX pathconf information applicable to special devices.
+ */
+int
+kernfs_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_MAX_CANON:
+ *ap->a_retval = MAX_CANON;
+ return (0);
+ case _PC_MAX_INPUT:
+ *ap->a_retval = MAX_INPUT;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ case _PC_VDISABLE:
+ *ap->a_retval = _POSIX_VDISABLE;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Print out the contents of a /dev/fd vnode.
+ */
+/* ARGSUSED */
+int
+kernfs_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_KERNFS, kernfs vnode\n");
+ return (0);
+}
+
+/*void*/
+int
+kernfs_vfree(ap)
+ struct vop_vfree_args /* {
+ struct vnode *a_pvp;
+ ino_t a_ino;
+ int a_mode;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * /dev/fd vnode unsupported operation
+ */
+int
+kernfs_enotsupp()
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * /dev/fd "should never get here" operation
+ */
+int
+kernfs_badop()
+{
+
+ panic("kernfs: bad op");
+ /* NOTREACHED */
+}
+
+/*
+ * kernfs vnode null operation
+ */
+int
+kernfs_nullop()
+{
+
+ return (0);
+}
+
+#define kernfs_create ((int (*) __P((struct vop_create_args *)))kernfs_enotsupp)
+#define kernfs_mknod ((int (*) __P((struct vop_mknod_args *)))kernfs_enotsupp)
+#define kernfs_close ((int (*) __P((struct vop_close_args *)))nullop)
+#define kernfs_ioctl ((int (*) __P((struct vop_ioctl_args *)))kernfs_enotsupp)
+#define kernfs_select ((int (*) __P((struct vop_select_args *)))kernfs_enotsupp)
+#define kernfs_mmap ((int (*) __P((struct vop_mmap_args *)))kernfs_enotsupp)
+#define kernfs_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
+#define kernfs_seek ((int (*) __P((struct vop_seek_args *)))nullop)
+#define kernfs_remove ((int (*) __P((struct vop_remove_args *)))kernfs_enotsupp)
+#define kernfs_link ((int (*) __P((struct vop_link_args *)))kernfs_enotsupp)
+#define kernfs_rename ((int (*) __P((struct vop_rename_args *)))kernfs_enotsupp)
+#define kernfs_mkdir ((int (*) __P((struct vop_mkdir_args *)))kernfs_enotsupp)
+#define kernfs_rmdir ((int (*) __P((struct vop_rmdir_args *)))kernfs_enotsupp)
+#define kernfs_symlink ((int (*) __P((struct vop_symlink_args *)))kernfs_enotsupp)
+#define kernfs_readlink \
+ ((int (*) __P((struct vop_readlink_args *)))kernfs_enotsupp)
+#define kernfs_abortop ((int (*) __P((struct vop_abortop_args *)))nullop)
+#define kernfs_lock ((int (*) __P((struct vop_lock_args *)))nullop)
+#define kernfs_unlock ((int (*) __P((struct vop_unlock_args *)))nullop)
+#define kernfs_bmap ((int (*) __P((struct vop_bmap_args *)))kernfs_badop)
+#define kernfs_strategy ((int (*) __P((struct vop_strategy_args *)))kernfs_badop)
+#define kernfs_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+#define kernfs_advlock ((int (*) __P((struct vop_advlock_args *)))kernfs_enotsupp)
+#define kernfs_blkatoff \
+ ((int (*) __P((struct vop_blkatoff_args *)))kernfs_enotsupp)
+#define kernfs_valloc ((int(*) __P(( \
+ struct vnode *pvp, \
+ int mode, \
+ struct ucred *cred, \
+ struct vnode **vpp))) kernfs_enotsupp)
+#define kernfs_truncate \
+ ((int (*) __P((struct vop_truncate_args *)))kernfs_enotsupp)
+#define kernfs_update ((int (*) __P((struct vop_update_args *)))kernfs_enotsupp)
+#define kernfs_bwrite ((int (*) __P((struct vop_bwrite_args *)))kernfs_enotsupp)
+
+int (**kernfs_vnodeop_p)();
+struct vnodeopv_entry_desc kernfs_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, kernfs_lookup }, /* lookup */
+ { &vop_create_desc, kernfs_create }, /* create */
+ { &vop_mknod_desc, kernfs_mknod }, /* mknod */
+ { &vop_open_desc, kernfs_open }, /* open */
+ { &vop_close_desc, kernfs_close }, /* close */
+ { &vop_access_desc, kernfs_access }, /* access */
+ { &vop_getattr_desc, kernfs_getattr }, /* getattr */
+ { &vop_setattr_desc, kernfs_setattr }, /* setattr */
+ { &vop_read_desc, kernfs_read }, /* read */
+ { &vop_write_desc, kernfs_write }, /* write */
+ { &vop_ioctl_desc, kernfs_ioctl }, /* ioctl */
+ { &vop_select_desc, kernfs_select }, /* select */
+ { &vop_mmap_desc, kernfs_mmap }, /* mmap */
+ { &vop_fsync_desc, kernfs_fsync }, /* fsync */
+ { &vop_seek_desc, kernfs_seek }, /* seek */
+ { &vop_remove_desc, kernfs_remove }, /* remove */
+ { &vop_link_desc, kernfs_link }, /* link */
+ { &vop_rename_desc, kernfs_rename }, /* rename */
+ { &vop_mkdir_desc, kernfs_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, kernfs_rmdir }, /* rmdir */
+ { &vop_symlink_desc, kernfs_symlink }, /* symlink */
+ { &vop_readdir_desc, kernfs_readdir }, /* readdir */
+ { &vop_readlink_desc, kernfs_readlink },/* readlink */
+ { &vop_abortop_desc, kernfs_abortop }, /* abortop */
+ { &vop_inactive_desc, kernfs_inactive },/* inactive */
+ { &vop_reclaim_desc, kernfs_reclaim }, /* reclaim */
+ { &vop_lock_desc, kernfs_lock }, /* lock */
+ { &vop_unlock_desc, kernfs_unlock }, /* unlock */
+ { &vop_bmap_desc, kernfs_bmap }, /* bmap */
+ { &vop_strategy_desc, kernfs_strategy },/* strategy */
+ { &vop_print_desc, kernfs_print }, /* print */
+ { &vop_islocked_desc, kernfs_islocked },/* islocked */
+ { &vop_pathconf_desc, kernfs_pathconf },/* pathconf */
+ { &vop_advlock_desc, kernfs_advlock }, /* advlock */
+ { &vop_blkatoff_desc, kernfs_blkatoff },/* blkatoff */
+ { &vop_valloc_desc, kernfs_valloc }, /* valloc */
+ { &vop_vfree_desc, kernfs_vfree }, /* vfree */
+ { &vop_truncate_desc, kernfs_truncate },/* truncate */
+ { &vop_update_desc, kernfs_update }, /* update */
+ { &vop_bwrite_desc, kernfs_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc kernfs_vnodeop_opv_desc =
+ { &kernfs_vnodeop_p, kernfs_vnodeop_entries };
diff --git a/sys/miscfs/nullfs/null.h b/sys/miscfs/nullfs/null.h
new file mode 100644
index 0000000..14286ff
--- /dev/null
+++ b/sys/miscfs/nullfs/null.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)null.h 8.2 (Berkeley) 1/21/94
+ *
+ * $Id: lofs.h,v 1.8 1992/05/30 10:05:43 jsp Exp jsp $
+ */
+
+struct null_args {
+ char *target; /* Target of loopback */
+};
+
+struct null_mount {
+ struct mount *nullm_vfs;
+ struct vnode *nullm_rootvp; /* Reference to root null_node */
+};
+
+#ifdef KERNEL
+/*
+ * A cache of vnode references
+ */
+struct null_node {
+ struct null_node *null_forw; /* Hash chain */
+ struct null_node *null_back;
+ struct vnode *null_lowervp; /* VREFed once */
+ struct vnode *null_vnode; /* Back pointer */
+};
+
+extern int null_node_create __P((struct mount *mp, struct vnode *target, struct vnode **vpp));
+
+#define MOUNTTONULLMOUNT(mp) ((struct null_mount *)((mp)->mnt_data))
+#define VTONULL(vp) ((struct null_node *)(vp)->v_data)
+#define NULLTOV(xp) ((xp)->null_vnode)
+#ifdef NULLFS_DIAGNOSTIC
+extern struct vnode *null_checkvp __P((struct vnode *vp, char *fil, int lno));
+#define NULLVPTOLOWERVP(vp) null_checkvp((vp), __FILE__, __LINE__)
+#else
+#define NULLVPTOLOWERVP(vp) (VTONULL(vp)->null_lowervp)
+#endif
+
+extern int (**null_vnodeop_p)();
+extern struct vfsops null_vfsops;
+#endif /* KERNEL */
diff --git a/sys/miscfs/nullfs/null_subr.c b/sys/miscfs/nullfs/null_subr.c
new file mode 100644
index 0000000..5541fb3
--- /dev/null
+++ b/sys/miscfs/nullfs/null_subr.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)null_subr.c 8.4 (Berkeley) 1/21/94
+ *
+ * $Id: lofs_subr.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <miscfs/nullfs/null.h>
+
+#define LOG2_SIZEVNODE 7 /* log2(sizeof struct vnode) */
+#define NNULLNODECACHE 16
+#define NULL_NHASH(vp) ((((u_long)vp)>>LOG2_SIZEVNODE) & (NNULLNODECACHE-1))
+
+/*
+ * Null layer cache:
+ * Each cache entry holds a reference to the lower vnode
+ * along with a pointer to the alias vnode. When an
+ * entry is added the lower vnode is VREF'd. When the
+ * alias is removed the lower vnode is vrele'd.
+ */
+
+/*
+ * Cache head
+ */
+struct null_node_cache {
+ struct null_node *ac_forw;
+ struct null_node *ac_back;
+};
+
+static struct null_node_cache null_node_cache[NNULLNODECACHE];
+
+/*
+ * Initialise cache headers
+ */
+int
+nullfs_init()
+{
+ struct null_node_cache *ac;
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_init\n"); /* printed during system boot */
+#endif
+
+ for (ac = null_node_cache; ac < null_node_cache + NNULLNODECACHE; ac++)
+ ac->ac_forw = ac->ac_back = (struct null_node *) ac;
+ return (0);
+}
+
+/*
+ * Compute hash list for given lower vnode
+ */
+static struct null_node_cache *
+null_node_hash(lowervp)
+struct vnode *lowervp;
+{
+
+ return (&null_node_cache[NULL_NHASH(lowervp)]);
+}
+
+/*
+ * Return a VREF'ed alias for lower vnode if already exists, else 0.
+ */
+static struct vnode *
+null_node_find(mp, lowervp)
+ struct mount *mp;
+ struct vnode *lowervp;
+{
+ struct null_node_cache *hd;
+ struct null_node *a;
+ struct vnode *vp;
+
+ /*
+ * Find hash base, and then search the (two-way) linked
+ * list looking for a null_node structure which is referencing
+ * the lower vnode. If found, the increment the null_node
+ * reference count (but NOT the lower vnode's VREF counter).
+ */
+ hd = null_node_hash(lowervp);
+loop:
+ for (a = hd->ac_forw; a != (struct null_node *) hd; a = a->null_forw) {
+ if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) {
+ vp = NULLTOV(a);
+ /*
+ * We need vget for the VXLOCK
+ * stuff, but we don't want to lock
+ * the lower node.
+ */
+ if (vget(vp, 0)) {
+ printf ("null_node_find: vget failed.\n");
+ goto loop;
+ };
+ return (vp);
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Make a new null_node node.
+ * Vp is the alias vnode, lofsvp is the lower vnode.
+ * Maintain a reference to (lowervp).
+ */
+static int
+null_node_alloc(mp, lowervp, vpp)
+ struct mount *mp;
+ struct vnode *lowervp;
+ struct vnode **vpp;
+{
+ struct null_node_cache *hd;
+ struct null_node *xp;
+ struct vnode *othervp, *vp;
+ int error;
+
+ if (error = getnewvnode(VT_NULL, mp, null_vnodeop_p, vpp))
+ return (error);
+ vp = *vpp;
+
+ MALLOC(xp, struct null_node *, sizeof(struct null_node), M_TEMP, M_WAITOK);
+ vp->v_type = lowervp->v_type;
+ xp->null_vnode = vp;
+ vp->v_data = xp;
+ xp->null_lowervp = lowervp;
+ /*
+ * Before we insert our new node onto the hash chains,
+ * check to see if someone else has beaten us to it.
+ * (We could have slept in MALLOC.)
+ */
+ if (othervp = null_node_find(lowervp)) {
+ FREE(xp, M_TEMP);
+ vp->v_type = VBAD; /* node is discarded */
+ vp->v_usecount = 0; /* XXX */
+ *vpp = othervp;
+ return 0;
+ };
+ VREF(lowervp); /* Extra VREF will be vrele'd in null_node_create */
+ hd = null_node_hash(lowervp);
+ insque(xp, hd);
+ return 0;
+}
+
+
+/*
+ * Try to find an existing null_node vnode refering
+ * to it, otherwise make a new null_node vnode which
+ * contains a reference to the lower vnode.
+ */
+int
+null_node_create(mp, lowervp, newvpp)
+ struct mount *mp;
+ struct vnode *lowervp;
+ struct vnode **newvpp;
+{
+ struct vnode *aliasvp;
+
+ if (aliasvp = null_node_find(mp, lowervp)) {
+ /*
+ * null_node_find has taken another reference
+ * to the alias vnode.
+ */
+#ifdef NULLFS_DIAGNOSTIC
+ vprint("null_node_create: exists", NULLTOV(ap));
+#endif
+ /* VREF(aliasvp); --- done in null_node_find */
+ } else {
+ int error;
+
+ /*
+ * Get new vnode.
+ */
+#ifdef NULLFS_DIAGNOSTIC
+ printf("null_node_create: create new alias vnode\n");
+#endif
+
+ /*
+ * Make new vnode reference the null_node.
+ */
+ if (error = null_node_alloc(mp, lowervp, &aliasvp))
+ return error;
+
+ /*
+ * aliasvp is already VREF'd by getnewvnode()
+ */
+ }
+
+ vrele(lowervp);
+
+#ifdef DIAGNOSTIC
+ if (lowervp->v_usecount < 1) {
+ /* Should never happen... */
+ vprint ("null_node_create: alias ");
+ vprint ("null_node_create: lower ");
+ printf ("null_node_create: lower has 0 usecount.\n");
+ panic ("null_node_create: lower has 0 usecount.");
+ };
+#endif
+
+#ifdef NULLFS_DIAGNOSTIC
+ vprint("null_node_create: alias", aliasvp);
+ vprint("null_node_create: lower", lowervp);
+#endif
+
+ *newvpp = aliasvp;
+ return (0);
+}
+#ifdef NULLFS_DIAGNOSTIC
+struct vnode *
+null_checkvp(vp, fil, lno)
+ struct vnode *vp;
+ char *fil;
+ int lno;
+{
+ struct null_node *a = VTONULL(vp);
+#ifdef notyet
+ /*
+ * Can't do this check because vop_reclaim runs
+ * with a funny vop vector.
+ */
+ if (vp->v_op != null_vnodeop_p) {
+ printf ("null_checkvp: on non-null-node\n");
+ while (null_checkvp_barrier) /*WAIT*/ ;
+ panic("null_checkvp");
+ };
+#endif
+ if (a->null_lowervp == NULL) {
+ /* Should never happen */
+ int i; u_long *p;
+ printf("vp = %x, ZERO ptr\n", vp);
+ for (p = (u_long *) a, i = 0; i < 8; i++)
+ printf(" %x", p[i]);
+ printf("\n");
+ /* wait for debugger */
+ while (null_checkvp_barrier) /*WAIT*/ ;
+ panic("null_checkvp");
+ }
+ if (a->null_lowervp->v_usecount < 1) {
+ int i; u_long *p;
+ printf("vp = %x, unref'ed lowervp\n", vp);
+ for (p = (u_long *) a, i = 0; i < 8; i++)
+ printf(" %x", p[i]);
+ printf("\n");
+ /* wait for debugger */
+ while (null_checkvp_barrier) /*WAIT*/ ;
+ panic ("null with unref'ed lowervp");
+ };
+#ifdef notyet
+ printf("null %x/%d -> %x/%d [%s, %d]\n",
+ NULLTOV(a), NULLTOV(a)->v_usecount,
+ a->null_lowervp, a->null_lowervp->v_usecount,
+ fil, lno);
+#endif
+ return a->null_lowervp;
+}
+#endif
diff --git a/sys/miscfs/nullfs/null_vfsops.c b/sys/miscfs/nullfs/null_vfsops.c
new file mode 100644
index 0000000..b0d2df7
--- /dev/null
+++ b/sys/miscfs/nullfs/null_vfsops.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)null_vfsops.c 8.2 (Berkeley) 1/21/94
+ *
+ * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92
+ * $Id: lofs_vfsops.c,v 1.9 1992/05/30 10:26:24 jsp Exp jsp $
+ */
+
+/*
+ * Null Layer
+ * (See null_vnops.c for a description of what this does.)
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <miscfs/nullfs/null.h>
+
+/*
+ * Mount null layer
+ */
+int
+nullfs_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ int error = 0;
+ struct null_args args;
+ struct vnode *lowerrootvp, *vp;
+ struct vnode *nullm_rootvp;
+ struct null_mount *xmp;
+ u_int size;
+
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_mount(mp = %x)\n", mp);
+#endif
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ return (EOPNOTSUPP);
+ /* return VFS_MOUNT(MOUNTTONULLMOUNT(mp)->nullm_vfs, path, data, ndp, p);*/
+ }
+
+ /*
+ * Get argument
+ */
+ if (error = copyin(data, (caddr_t)&args, sizeof(struct null_args)))
+ return (error);
+
+ /*
+ * Find lower node
+ */
+ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF,
+ UIO_USERSPACE, args.target, p);
+ if (error = namei(ndp))
+ return (error);
+
+ /*
+ * Sanity check on lower vnode
+ */
+ lowerrootvp = ndp->ni_vp;
+
+ vrele(ndp->ni_dvp);
+ ndp->ni_dvp = NULL;
+
+ xmp = (struct null_mount *) malloc(sizeof(struct null_mount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+
+ /*
+ * Save reference to underlying FS
+ */
+ xmp->nullm_vfs = lowerrootvp->v_mount;
+
+ /*
+ * Save reference. Each mount also holds
+ * a reference on the root vnode.
+ */
+ error = null_node_create(mp, lowerrootvp, &vp);
+ /*
+ * Unlock the node (either the lower or the alias)
+ */
+ VOP_UNLOCK(vp);
+ /*
+ * Make sure the node alias worked
+ */
+ if (error) {
+ vrele(lowerrootvp);
+ free(xmp, M_UFSMNT); /* XXX */
+ return (error);
+ }
+
+ /*
+ * Keep a held reference to the root vnode.
+ * It is vrele'd in nullfs_unmount.
+ */
+ nullm_rootvp = vp;
+ nullm_rootvp->v_flag |= VROOT;
+ xmp->nullm_rootvp = nullm_rootvp;
+ if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL)
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_data = (qaddr_t) xmp;
+ getnewfsid(mp, MOUNT_LOFS);
+
+ (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+ (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_mount: lower %s, alias at %s\n",
+ mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
+#endif
+ return (0);
+}
+
+/*
+ * VFS start. Nothing needed here - the start routine
+ * on the underlying filesystem will have been called
+ * when that filesystem was mounted.
+ */
+int
+nullfs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ return (0);
+ /* return VFS_START(MOUNTTONULLMOUNT(mp)->nullm_vfs, flags, p); */
+}
+
+/*
+ * Free reference to null layer
+ */
+int
+nullfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ struct vnode *nullm_rootvp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
+ int error;
+ int flags = 0;
+ extern int doforce;
+
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_unmount(mp = %x)\n", mp);
+#endif
+
+ if (mntflags & MNT_FORCE) {
+ /* lofs can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ /*
+ * Clear out buffer cache. I don't think we
+ * ever get anything cached at this level at the
+ * moment, but who knows...
+ */
+#if 0
+ mntflushbuf(mp, 0);
+ if (mntinvalbuf(mp, 1))
+ return (EBUSY);
+#endif
+ if (nullm_rootvp->v_usecount > 1)
+ return (EBUSY);
+ if (error = vflush(mp, nullm_rootvp, flags))
+ return (error);
+
+#ifdef NULLFS_DIAGNOSTIC
+ vprint("alias root of lower", nullm_rootvp);
+#endif
+ /*
+ * Release reference on underlying root vnode
+ */
+ vrele(nullm_rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(nullm_rootvp);
+ /*
+ * Finally, throw away the null_mount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ mp->mnt_data = 0;
+ return 0;
+}
+
+int
+nullfs_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct vnode *vp;
+
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_root(mp = %x, vp = %x->%x)\n", mp,
+ MOUNTTONULLMOUNT(mp)->nullm_rootvp,
+ NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)
+ );
+#endif
+
+ /*
+ * Return locked reference to root.
+ */
+ vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
+ VREF(vp);
+ VOP_LOCK(vp);
+ *vpp = vp;
+ return 0;
+}
+
+int
+nullfs_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+ return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg, p);
+}
+
+int
+nullfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ int error;
+ struct statfs mstat;
+
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_statfs(mp = %x, vp = %x->%x)\n", mp,
+ MOUNTTONULLMOUNT(mp)->nullm_rootvp,
+ NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)
+ );
+#endif
+
+ bzero(&mstat, sizeof(mstat));
+
+ error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat, p);
+ if (error)
+ return (error);
+
+ /* now copy across the "interesting" information and fake the rest */
+ sbp->f_type = mstat.f_type;
+ sbp->f_flags = mstat.f_flags;
+ sbp->f_bsize = mstat.f_bsize;
+ sbp->f_iosize = mstat.f_iosize;
+ sbp->f_blocks = mstat.f_blocks;
+ sbp->f_bfree = mstat.f_bfree;
+ sbp->f_bavail = mstat.f_bavail;
+ sbp->f_files = mstat.f_files;
+ sbp->f_ffree = mstat.f_ffree;
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+ return (0);
+}
+
+int
+nullfs_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+ /*
+ * XXX - Assumes no data cached at null layer.
+ */
+ return (0);
+}
+
+int
+nullfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, vpp);
+}
+
+int
+nullfs_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
+ struct mount *mp;
+ struct fid *fidp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred**credanonp;
+{
+
+ return VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, nam, vpp, exflagsp,credanonp);
+}
+
+int
+nullfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+ return VFS_VPTOFH(NULLVPTOLOWERVP(vp), fhp);
+}
+
+int nullfs_init __P((void));
+
+struct vfsops null_vfsops = {
+ nullfs_mount,
+ nullfs_start,
+ nullfs_unmount,
+ nullfs_root,
+ nullfs_quotactl,
+ nullfs_statfs,
+ nullfs_sync,
+ nullfs_vget,
+ nullfs_fhtovp,
+ nullfs_vptofh,
+ nullfs_init,
+};
diff --git a/sys/miscfs/nullfs/null_vnops.c b/sys/miscfs/nullfs/null_vnops.c
new file mode 100644
index 0000000..115ff6f
--- /dev/null
+++ b/sys/miscfs/nullfs/null_vnops.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * John Heidemann of the UCLA Ficus project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)null_vnops.c 8.1 (Berkeley) 6/10/93
+ *
+ * Ancestors:
+ * @(#)lofs_vnops.c 1.2 (Berkeley) 6/18/92
+ * $Id: lofs_vnops.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
+ * ...and...
+ * @(#)null_vnodeops.c 1.20 92/07/07 UCLA Ficus project
+ */
+
+/*
+ * Null Layer
+ *
+ * (See mount_null(8) for more information.)
+ *
+ * The null layer duplicates a portion of the file system
+ * name space under a new name. In this respect, it is
+ * similar to the loopback file system. It differs from
+ * the loopback fs in two respects: it is implemented using
+ * a stackable layers techniques, and it's "null-node"s stack above
+ * all lower-layer vnodes, not just over directory vnodes.
+ *
+ * The null layer has two purposes. First, it serves as a demonstration
+ * of layering by proving a layer which does nothing. (It actually
+ * does everything the loopback file system does, which is slightly
+ * more than nothing.) Second, the null layer can serve as a prototype
+ * layer. Since it provides all necessary layer framework,
+ * new file system layers can be created very easily be starting
+ * with a null layer.
+ *
+ * The remainder of this man page examines the null layer as a basis
+ * for constructing new layers.
+ *
+ *
+ * INSTANTIATING NEW NULL LAYERS
+ *
+ * New null layers are created with mount_null(8).
+ * Mount_null(8) takes two arguments, the pathname
+ * of the lower vfs (target-pn) and the pathname where the null
+ * layer will appear in the namespace (alias-pn). After
+ * the null layer is put into place, the contents
+ * of target-pn subtree will be aliased under alias-pn.
+ *
+ *
+ * OPERATION OF A NULL LAYER
+ *
+ * The null layer is the minimum file system layer,
+ * simply bypassing all possible operations to the lower layer
+ * for processing there. The majority of its activity centers
+ * on the bypass routine, though which nearly all vnode operations
+ * pass.
+ *
+ * The bypass routine accepts arbitrary vnode operations for
+ * handling by the lower layer. It begins by examing vnode
+ * operation arguments and replacing any null-nodes by their
+ * lower-layer equivlants. It then invokes the operation
+ * on the lower layer. Finally, it replaces the null-nodes
+ * in the arguments and, if a vnode is return by the operation,
+ * stacks a null-node on top of the returned vnode.
+ *
+ * Although bypass handles most operations,
+ * vop_getattr, _inactive, _reclaim, and _print are not bypassed.
+ * Vop_getattr must change the fsid being returned.
+ * Vop_inactive and vop_reclaim are not bypassed so that
+ * they can handle freeing null-layer specific data.
+ * Vop_print is not bypassed to avoid excessive debugging
+ * information.
+ *
+ *
+ * INSTANTIATING VNODE STACKS
+ *
+ * Mounting associates the null layer with a lower layer,
+ * effect stacking two VFSes. Vnode stacks are instead
+ * created on demand as files are accessed.
+ *
+ * The initial mount creates a single vnode stack for the
+ * root of the new null layer. All other vnode stacks
+ * are created as a result of vnode operations on
+ * this or other null vnode stacks.
+ *
+ * New vnode stacks come into existance as a result of
+ * an operation which returns a vnode.
+ * The bypass routine stacks a null-node above the new
+ * vnode before returning it to the caller.
+ *
+ * For example, imagine mounting a null layer with
+ * "mount_null /usr/include /dev/layer/null".
+ * Changing directory to /dev/layer/null will assign
+ * the root null-node (which was created when the null layer was mounted).
+ * Now consider opening "sys". A vop_lookup would be
+ * done on the root null-node. This operation would bypass through
+ * to the lower layer which would return a vnode representing
+ * the UFS "sys". Null_bypass then builds a null-node
+ * aliasing the UFS "sys" and returns this to the caller.
+ * Later operations on the null-node "sys" will repeat this
+ * process when constructing other vnode stacks.
+ *
+ *
+ * CREATING OTHER FILE SYSTEM LAYERS
+ *
+ * One of the easiest ways to construct new file system layers is to make
+ * a copy of the null layer, rename all files and variables, and
+ * then begin modifing the copy. Sed can be used to easily rename
+ * all variables.
+ *
+ * The umap layer is an example of a layer descended from the
+ * null layer.
+ *
+ *
+ * INVOKING OPERATIONS ON LOWER LAYERS
+ *
+ * There are two techniques to invoke operations on a lower layer
+ * when the operation cannot be completely bypassed. Each method
+ * is appropriate in different situations. In both cases,
+ * it is the responsibility of the aliasing layer to make
+ * the operation arguments "correct" for the lower layer
+ * by mapping an vnode arguments to the lower layer.
+ *
+ * The first approach is to call the aliasing layer's bypass routine.
+ * This method is most suitable when you wish to invoke the operation
+ * currently being hanldled on the lower layer. It has the advantage
+ * that the bypass routine already must do argument mapping.
+ * An example of this is null_getattrs in the null layer.
+ *
+ * A second approach is to directly invoked vnode operations on
+ * the lower layer with the VOP_OPERATIONNAME interface.
+ * The advantage of this method is that it is easy to invoke
+ * arbitrary operations on the lower layer. The disadvantage
+ * is that vnodes arguments must be manualy mapped.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <miscfs/nullfs/null.h>
+
+
+int null_bug_bypass = 0; /* for debugging: enables bypass printf'ing */
+
+/*
+ * This is the 10-Apr-92 bypass routine.
+ * This version has been optimized for speed, throwing away some
+ * safety checks. It should still always work, but it's not as
+ * robust to programmer errors.
+ * Define SAFETY to include some error checking code.
+ *
+ * In general, we map all vnodes going down and unmap them on the way back.
+ * As an exception to this, vnodes can be marked "unmapped" by setting
+ * the Nth bit in operation's vdesc_flags.
+ *
+ * Also, some BSD vnode operations have the side effect of vrele'ing
+ * their arguments. With stacking, the reference counts are held
+ * by the upper node, not the lower one, so we must handle these
+ * side-effects here. This is not of concern in Sun-derived systems
+ * since there are no such side-effects.
+ *
+ * This makes the following assumptions:
+ * - only one returned vpp
+ * - no INOUT vpp's (Sun's vop_open has one of these)
+ * - the vnode operation vector of the first vnode should be used
+ * to determine what implementation of the op should be invoked
+ * - all mapped vnodes are of our vnode-type (NEEDSWORK:
+ * problems on rmdir'ing mount points and renaming?)
+ */
+int
+null_bypass(ap)
+ struct vop_generic_args /* {
+ struct vnodeop_desc *a_desc;
+ <other random data follows, presumably>
+ } */ *ap;
+{
+ extern int (**null_vnodeop_p)(); /* not extern, really "forward" */
+ register struct vnode **this_vp_p;
+ int error;
+ struct vnode *old_vps[VDESC_MAX_VPS];
+ struct vnode **vps_p[VDESC_MAX_VPS];
+ struct vnode ***vppp;
+ struct vnodeop_desc *descp = ap->a_desc;
+ int reles, i;
+
+ if (null_bug_bypass)
+ printf ("null_bypass: %s\n", descp->vdesc_name);
+
+#ifdef SAFETY
+ /*
+ * We require at least one vp.
+ */
+ if (descp->vdesc_vp_offsets == NULL ||
+ descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET)
+ panic ("null_bypass: no vp's in map.\n");
+#endif
+
+ /*
+ * Map the vnodes going in.
+ * Later, we'll invoke the operation based on
+ * the first mapped vnode's operation vector.
+ */
+ reles = descp->vdesc_flags;
+ for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
+ if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
+ break; /* bail out at end of list */
+ vps_p[i] = this_vp_p =
+ VOPARG_OFFSETTO(struct vnode**,descp->vdesc_vp_offsets[i],ap);
+ /*
+ * We're not guaranteed that any but the first vnode
+ * are of our type. Check for and don't map any
+ * that aren't. (We must always map first vp or vclean fails.)
+ */
+ if (i && (*this_vp_p)->v_op != null_vnodeop_p) {
+ old_vps[i] = NULL;
+ } else {
+ old_vps[i] = *this_vp_p;
+ *(vps_p[i]) = NULLVPTOLOWERVP(*this_vp_p);
+ /*
+ * XXX - Several operations have the side effect
+ * of vrele'ing their vp's. We must account for
+ * that. (This should go away in the future.)
+ */
+ if (reles & 1)
+ VREF(*this_vp_p);
+ }
+
+ }
+
+ /*
+ * Call the operation on the lower layer
+ * with the modified argument structure.
+ */
+ error = VCALL(*(vps_p[0]), descp->vdesc_offset, ap);
+
+ /*
+ * Maintain the illusion of call-by-value
+ * by restoring vnodes in the argument structure
+ * to their original value.
+ */
+ reles = descp->vdesc_flags;
+ for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
+ if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
+ break; /* bail out at end of list */
+ if (old_vps[i]) {
+ *(vps_p[i]) = old_vps[i];
+ if (reles & 1)
+ vrele(*(vps_p[i]));
+ }
+ }
+
+ /*
+ * Map the possible out-going vpp
+ * (Assumes that the lower layer always returns
+ * a VREF'ed vpp unless it gets an error.)
+ */
+ if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET &&
+ !(descp->vdesc_flags & VDESC_NOMAP_VPP) &&
+ !error) {
+ /*
+ * XXX - even though some ops have vpp returned vp's,
+ * several ops actually vrele this before returning.
+ * We must avoid these ops.
+ * (This should go away when these ops are regularized.)
+ */
+ if (descp->vdesc_flags & VDESC_VPP_WILLRELE)
+ goto out;
+ vppp = VOPARG_OFFSETTO(struct vnode***,
+ descp->vdesc_vpp_offset,ap);
+ error = null_node_create(old_vps[0]->v_mount, **vppp, *vppp);
+ }
+
+ out:
+ return (error);
+}
+
+
+/*
+ * We handle getattr only to change the fsid.
+ */
+int
+null_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error;
+ if (error = null_bypass(ap))
+ return (error);
+ /* Requires that arguments be restored. */
+ ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
+ return (0);
+}
+
+
+int
+null_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ /*
+ * Do nothing (and _don't_ bypass).
+ * Wait to vrele lowervp until reclaim,
+ * so that until then our null_node is in the
+ * cache and reusable.
+ *
+ * NEEDSWORK: Someday, consider inactive'ing
+ * the lowervp and then trying to reactivate it
+ * with capabilities (v_id)
+ * like they do in the name lookup cache code.
+ * That's too much work for now.
+ */
+ return (0);
+}
+
+int
+null_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct null_node *xp = VTONULL(vp);
+ struct vnode *lowervp = xp->null_lowervp;
+
+ /*
+ * Note: in vop_reclaim, vp->v_op == dead_vnodeop_p,
+ * so we can't call VOPs on ourself.
+ */
+ /* After this assignment, this node will not be re-used. */
+ xp->null_lowervp = NULL;
+ remque(xp);
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = NULL;
+ vrele (lowervp);
+ return (0);
+}
+
+
+int
+null_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ printf ("\ttag VT_NULLFS, vp=%x, lowervp=%x\n", vp, NULLVPTOLOWERVP(vp));
+ return (0);
+}
+
+
+/*
+ * XXX - vop_strategy must be hand coded because it has no
+ * vnode in its arguments.
+ * This goes away with a merged VM/buffer cache.
+ */
+int
+null_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ int error;
+ struct vnode *savedvp;
+
+ savedvp = bp->b_vp;
+ bp->b_vp = NULLVPTOLOWERVP(bp->b_vp);
+
+ error = VOP_STRATEGY(bp);
+
+ bp->b_vp = savedvp;
+
+ return (error);
+}
+
+
+/*
+ * XXX - like vop_strategy, vop_bwrite must be hand coded because it has no
+ * vnode in its arguments.
+ * This goes away with a merged VM/buffer cache.
+ */
+int
+null_bwrite(ap)
+ struct vop_bwrite_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ int error;
+ struct vnode *savedvp;
+
+ savedvp = bp->b_vp;
+ bp->b_vp = NULLVPTOLOWERVP(bp->b_vp);
+
+ error = VOP_BWRITE(bp);
+
+ bp->b_vp = savedvp;
+
+ return (error);
+}
+
+/*
+ * Global vfs data structures
+ */
+int (**null_vnodeop_p)();
+struct vnodeopv_entry_desc null_vnodeop_entries[] = {
+ { &vop_default_desc, null_bypass },
+
+ { &vop_getattr_desc, null_getattr },
+ { &vop_inactive_desc, null_inactive },
+ { &vop_reclaim_desc, null_reclaim },
+ { &vop_print_desc, null_print },
+
+ { &vop_strategy_desc, null_strategy },
+ { &vop_bwrite_desc, null_bwrite },
+
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc null_vnodeop_opv_desc =
+ { &null_vnodeop_p, null_vnodeop_entries };
diff --git a/sys/miscfs/portal/portal.h b/sys/miscfs/portal/portal.h
new file mode 100644
index 0000000..38d7ee0
--- /dev/null
+++ b/sys/miscfs/portal/portal.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)portal.h 8.4 (Berkeley) 1/21/94
+ *
+ * $Id: portal.h,v 1.3 1992/05/30 10:05:24 jsp Exp jsp $
+ */
+
+struct portal_args {
+ char *pa_config; /* Config file */
+ int pa_socket; /* Socket to server */
+};
+
+struct portal_cred {
+ int pcr_flag; /* File open mode */
+ uid_t pcr_uid; /* From ucred */
+ short pcr_ngroups; /* From ucred */
+ gid_t pcr_groups[NGROUPS]; /* From ucred */
+};
+
+#ifdef KERNEL
+struct portalmount {
+ struct vnode *pm_root; /* Root node */
+ struct file *pm_server; /* Held reference to server socket */
+};
+
+struct portalnode {
+ int pt_size; /* Length of Arg */
+ char *pt_arg; /* Arg to send to server */
+ int pt_fileid; /* cookie */
+};
+
+#define VFSTOPORTAL(mp) ((struct portalmount *)((mp)->mnt_data))
+#define VTOPORTAL(vp) ((struct portalnode *)(vp)->v_data)
+
+#define PORTAL_ROOTFILEID 2
+
+extern int (**portal_vnodeop_p)();
+extern struct vfsops portal_vfsops;
+#endif /* KERNEL */
diff --git a/sys/miscfs/portal/portal_vfsops.c b/sys/miscfs/portal/portal_vfsops.c
new file mode 100644
index 0000000..39e8563
--- /dev/null
+++ b/sys/miscfs/portal/portal_vfsops.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)portal_vfsops.c 8.6 (Berkeley) 1/21/94
+ *
+ * $Id: portal_vfsops.c,v 1.5 1992/05/30 10:25:27 jsp Exp jsp $
+ */
+
+/*
+ * Portal Filesystem
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/filedesc.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/un.h>
+#include <miscfs/portal/portal.h>
+
+int
+portal_init()
+{
+
+ return (0);
+}
+
+/*
+ * Mount the per-process file descriptors (/dev/fd)
+ */
+int
+portal_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ struct file *fp;
+ struct portal_args args;
+ struct portalmount *fmp;
+ struct socket *so;
+ struct vnode *rvp;
+ u_int size;
+ int error;
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE)
+ return (EOPNOTSUPP);
+
+ if (error = copyin(data, (caddr_t) &args, sizeof(struct portal_args)))
+ return (error);
+
+ if (error = getsock(p->p_fd, args.pa_socket, &fp))
+ return (error);
+ so = (struct socket *) fp->f_data;
+ if (so->so_proto->pr_domain->dom_family != AF_UNIX)
+ return (ESOCKTNOSUPPORT);
+
+ error = getnewvnode(VT_PORTAL, mp, portal_vnodeop_p, &rvp); /* XXX */
+ if (error)
+ return (error);
+ MALLOC(rvp->v_data, void *, sizeof(struct portalnode),
+ M_TEMP, M_WAITOK);
+
+ fmp = (struct portalmount *) malloc(sizeof(struct portalmount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+ rvp->v_type = VDIR;
+ rvp->v_flag |= VROOT;
+ VTOPORTAL(rvp)->pt_arg = 0;
+ VTOPORTAL(rvp)->pt_size = 0;
+ VTOPORTAL(rvp)->pt_fileid = PORTAL_ROOTFILEID;
+ fmp->pm_root = rvp;
+ fmp->pm_server = fp; fp->f_count++;
+
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_data = (qaddr_t) fmp;
+ getnewfsid(mp, MOUNT_PORTAL);
+
+ (void)copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+ (void)copyinstr(args.pa_config,
+ mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+
+#ifdef notdef
+ bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
+ bcopy("portal", mp->mnt_stat.f_mntfromname, sizeof("portal"));
+#endif
+
+ return (0);
+}
+
+int
+portal_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+
+ return (0);
+}
+
+int
+portal_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ extern int doforce;
+ struct vnode *rootvp = VFSTOPORTAL(mp)->pm_root;
+ int error, flags = 0;
+
+
+ if (mntflags & MNT_FORCE) {
+ /* portal can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ /*
+ * Clear out buffer cache. I don't think we
+ * ever get anything cached at this level at the
+ * moment, but who knows...
+ */
+#ifdef notyet
+ mntflushbuf(mp, 0);
+ if (mntinvalbuf(mp, 1))
+ return (EBUSY);
+#endif
+ if (rootvp->v_usecount > 1)
+ return (EBUSY);
+ if (error = vflush(mp, rootvp, flags))
+ return (error);
+
+ /*
+ * Release reference on underlying root vnode
+ */
+ vrele(rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(rootvp);
+ /*
+ * Shutdown the socket. This will cause the select in the
+ * daemon to wake up, and then the accept will get ECONNABORTED
+ * which it interprets as a request to go and bury itself.
+ */
+ soshutdown((struct socket *) VFSTOPORTAL(mp)->pm_server->f_data, 2);
+ /*
+ * Discard reference to underlying file. Must call closef because
+ * this may be the last reference.
+ */
+ closef(VFSTOPORTAL(mp)->pm_server, (struct proc *) 0);
+ /*
+ * Finally, throw away the portalmount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ mp->mnt_data = 0;
+ return (0);
+}
+
+int
+portal_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct vnode *vp;
+
+
+ /*
+ * Return locked reference to root.
+ */
+ vp = VFSTOPORTAL(mp)->pm_root;
+ VREF(vp);
+ VOP_LOCK(vp);
+ *vpp = vp;
+ return (0);
+}
+
+int
+portal_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+portal_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+
+ sbp->f_type = MOUNT_PORTAL;
+ sbp->f_flags = 0;
+ sbp->f_bsize = DEV_BSIZE;
+ sbp->f_iosize = DEV_BSIZE;
+ sbp->f_blocks = 2; /* 1K to keep df happy */
+ sbp->f_bfree = 0;
+ sbp->f_bavail = 0;
+ sbp->f_files = 1; /* Allow for "." */
+ sbp->f_ffree = 0; /* See comments above */
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+ return (0);
+}
+
+int
+portal_sync(mp, waitfor)
+ struct mount *mp;
+ int waitfor;
+{
+
+ return (0);
+}
+
+int
+portal_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+portal_fhtovp(mp, fhp, vpp)
+ struct mount *mp;
+ struct fid *fhp;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+portal_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+struct vfsops portal_vfsops = {
+ portal_mount,
+ portal_start,
+ portal_unmount,
+ portal_root,
+ portal_quotactl,
+ portal_statfs,
+ portal_sync,
+ portal_vget,
+ portal_fhtovp,
+ portal_vptofh,
+ portal_init,
+};
diff --git a/sys/miscfs/portal/portal_vnops.c b/sys/miscfs/portal/portal_vnops.c
new file mode 100644
index 0000000..c19e815
--- /dev/null
+++ b/sys/miscfs/portal/portal_vnops.c
@@ -0,0 +1,708 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)portal_vnops.c 8.8 (Berkeley) 1/21/94
+ *
+ * $Id: portal_vnops.c,v 1.4 1992/05/30 10:05:24 jsp Exp jsp $
+ */
+
+/*
+ * Portal Filesystem
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/filedesc.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/malloc.h>
+#include <sys/namei.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/un.h>
+#include <sys/unpcb.h>
+#include <miscfs/portal/portal.h>
+
+static int portal_fileid = PORTAL_ROOTFILEID+1;
+
+static void
+portal_closefd(p, fd)
+ struct proc *p;
+ int fd;
+{
+ int error;
+ struct {
+ int fd;
+ } ua;
+ int rc;
+
+ ua.fd = fd;
+ error = close(p, &ua, &rc);
+ /*
+ * We should never get an error, and there isn't anything
+ * we could do if we got one, so just print a message.
+ */
+ if (error)
+ printf("portal_closefd: error = %d\n", error);
+}
+
+/*
+ * vp is the current namei directory
+ * cnp is the name to locate in that directory...
+ */
+int
+portal_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode * a_dvp;
+ struct vnode ** a_vpp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+ char *pname = ap->a_cnp->cn_nameptr;
+ struct portalnode *pt;
+ int error;
+ struct vnode *fvp = 0;
+ char *path;
+ int size;
+
+ if (ap->a_cnp->cn_namelen == 1 && *pname == '.') {
+ *ap->a_vpp = ap->a_dvp;
+ VREF(ap->a_dvp);
+ /*VOP_LOCK(ap->a_dvp);*/
+ return (0);
+ }
+
+
+ error = getnewvnode(VT_PORTAL, ap->a_dvp->v_mount, portal_vnodeop_p, &fvp);
+ if (error)
+ goto bad;
+ fvp->v_type = VREG;
+ MALLOC(fvp->v_data, void *, sizeof(struct portalnode),
+ M_TEMP, M_WAITOK);
+
+ pt = VTOPORTAL(fvp);
+ /*
+ * Save all of the remaining pathname and
+ * advance the namei next pointer to the end
+ * of the string.
+ */
+ for (size = 0, path = pname; *path; path++)
+ size++;
+ ap->a_cnp->cn_consume = size - ap->a_cnp->cn_namelen;
+
+ pt->pt_arg = malloc(size+1, M_TEMP, M_WAITOK);
+ pt->pt_size = size+1;
+ bcopy(pname, pt->pt_arg, pt->pt_size);
+ pt->pt_fileid = portal_fileid++;
+
+ *ap->a_vpp = fvp;
+ /*VOP_LOCK(fvp);*/
+ return (0);
+
+bad:;
+ if (fvp) {
+ vrele(fvp);
+ }
+ *ap->a_vpp = NULL;
+ return (error);
+}
+
+static int
+portal_connect(so, so2)
+ struct socket *so;
+ struct socket *so2;
+{
+ /* from unp_connect, bypassing the namei stuff... */
+ struct socket *so3;
+ struct unpcb *unp2;
+ struct unpcb *unp3;
+
+ if (so2 == 0)
+ return (ECONNREFUSED);
+
+ if (so->so_type != so2->so_type)
+ return (EPROTOTYPE);
+
+ if ((so2->so_options & SO_ACCEPTCONN) == 0)
+ return (ECONNREFUSED);
+
+ if ((so3 = sonewconn(so2, 0)) == 0)
+ return (ECONNREFUSED);
+
+ unp2 = sotounpcb(so2);
+ unp3 = sotounpcb(so3);
+ if (unp2->unp_addr)
+ unp3->unp_addr = m_copy(unp2->unp_addr, 0, (int)M_COPYALL);
+
+ so2 = so3;
+
+
+ return (unp_connect2(so, so2));
+}
+
+int
+portal_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct socket *so = 0;
+ struct portalnode *pt;
+ struct proc *p = ap->a_p;
+ struct vnode *vp = ap->a_vp;
+ int s;
+ struct uio auio;
+ struct iovec aiov[2];
+ int res;
+ struct mbuf *cm = 0;
+ struct cmsghdr *cmsg;
+ int newfds;
+ int *ip;
+ int fd;
+ int error;
+ int len;
+ struct portalmount *fmp;
+ struct file *fp;
+ struct portal_cred pcred;
+
+ /*
+ * Nothing to do when opening the root node.
+ */
+ if (vp->v_flag & VROOT)
+ return (0);
+
+ /*
+ * Can't be opened unless the caller is set up
+ * to deal with the side effects. Check for this
+ * by testing whether the p_dupfd has been set.
+ */
+ if (p->p_dupfd >= 0)
+ return (ENODEV);
+
+ pt = VTOPORTAL(vp);
+ fmp = VFSTOPORTAL(vp->v_mount);
+
+ /*
+ * Create a new socket.
+ */
+ error = socreate(AF_UNIX, &so, SOCK_STREAM, 0);
+ if (error)
+ goto bad;
+
+ /*
+ * Reserve some buffer space
+ */
+ res = pt->pt_size + sizeof(pcred) + 512; /* XXX */
+ error = soreserve(so, res, res);
+ if (error)
+ goto bad;
+
+ /*
+ * Kick off connection
+ */
+ error = portal_connect(so, (struct socket *)fmp->pm_server->f_data);
+ if (error)
+ goto bad;
+
+ /*
+ * Wait for connection to complete
+ */
+ /*
+ * XXX: Since the mount point is holding a reference on the
+ * underlying server socket, it is not easy to find out whether
+ * the server process is still running. To handle this problem
+ * we loop waiting for the new socket to be connected (something
+ * which will only happen if the server is still running) or for
+ * the reference count on the server socket to drop to 1, which
+ * will happen if the server dies. Sleep for 5 second intervals
+ * and keep polling the reference count. XXX.
+ */
+ s = splnet();
+ while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
+ if (fmp->pm_server->f_count == 1) {
+ error = ECONNREFUSED;
+ splx(s);
+ goto bad;
+ }
+ (void) tsleep((caddr_t) &so->so_timeo, PSOCK, "portalcon", 5 * hz);
+ }
+ splx(s);
+
+ if (so->so_error) {
+ error = so->so_error;
+ goto bad;
+ }
+
+ /*
+ * Set miscellaneous flags
+ */
+ so->so_rcv.sb_timeo = 0;
+ so->so_snd.sb_timeo = 0;
+ so->so_rcv.sb_flags |= SB_NOINTR;
+ so->so_snd.sb_flags |= SB_NOINTR;
+
+
+ pcred.pcr_flag = ap->a_mode;
+ pcred.pcr_uid = ap->a_cred->cr_uid;
+ pcred.pcr_ngroups = ap->a_cred->cr_ngroups;
+ bcopy(ap->a_cred->cr_groups, pcred.pcr_groups, NGROUPS * sizeof(gid_t));
+ aiov[0].iov_base = (caddr_t) &pcred;
+ aiov[0].iov_len = sizeof(pcred);
+ aiov[1].iov_base = pt->pt_arg;
+ aiov[1].iov_len = pt->pt_size;
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = 2;
+ auio.uio_rw = UIO_WRITE;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_procp = p;
+ auio.uio_offset = 0;
+ auio.uio_resid = aiov[0].iov_len + aiov[1].iov_len;
+
+ error = sosend(so, (struct mbuf *) 0, &auio,
+ (struct mbuf *) 0, (struct mbuf *) 0, 0);
+ if (error)
+ goto bad;
+
+ len = auio.uio_resid = sizeof(int);
+ do {
+ struct mbuf *m = 0;
+ int flags = MSG_WAITALL;
+ error = soreceive(so, (struct mbuf **) 0, &auio,
+ &m, &cm, &flags);
+ if (error)
+ goto bad;
+
+ /*
+ * Grab an error code from the mbuf.
+ */
+ if (m) {
+ m = m_pullup(m, sizeof(int)); /* Needed? */
+ if (m) {
+ error = *(mtod(m, int *));
+ m_freem(m);
+ } else {
+ error = EINVAL;
+ }
+ } else {
+ if (cm == 0) {
+ error = ECONNRESET; /* XXX */
+#ifdef notdef
+ break;
+#endif
+ }
+ }
+ } while (cm == 0 && auio.uio_resid == len && !error);
+
+ if (cm == 0)
+ goto bad;
+
+ if (auio.uio_resid) {
+ error = 0;
+#ifdef notdef
+ error = EMSGSIZE;
+ goto bad;
+#endif
+ }
+
+ /*
+ * XXX: Break apart the control message, and retrieve the
+ * received file descriptor. Note that more than one descriptor
+ * may have been received, or that the rights chain may have more
+ * than a single mbuf in it. What to do?
+ */
+ cmsg = mtod(cm, struct cmsghdr *);
+ newfds = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof (int);
+ if (newfds == 0) {
+ error = ECONNREFUSED;
+ goto bad;
+ }
+ /*
+ * At this point the rights message consists of a control message
+ * header, followed by a data region containing a vector of
+ * integer file descriptors. The fds were allocated by the action
+ * of receiving the control message.
+ */
+ ip = (int *) (cmsg + 1);
+ fd = *ip++;
+ if (newfds > 1) {
+ /*
+ * Close extra fds.
+ */
+ int i;
+ printf("portal_open: %d extra fds\n", newfds - 1);
+ for (i = 1; i < newfds; i++) {
+ portal_closefd(p, *ip);
+ ip++;
+ }
+ }
+
+ /*
+ * Check that the mode the file is being opened for is a subset
+ * of the mode of the existing descriptor.
+ */
+ fp = p->p_fd->fd_ofiles[fd];
+ if (((ap->a_mode & (FREAD|FWRITE)) | fp->f_flag) != fp->f_flag) {
+ portal_closefd(p, fd);
+ error = EACCES;
+ goto bad;
+ }
+
+ /*
+ * Save the dup fd in the proc structure then return the
+ * special error code (ENXIO) which causes magic things to
+ * happen in vn_open. The whole concept is, well, hmmm.
+ */
+ p->p_dupfd = fd;
+ error = ENXIO;
+
+bad:;
+ /*
+ * And discard the control message.
+ */
+ if (cm) {
+ m_freem(cm);
+ }
+
+ if (so) {
+ soshutdown(so, 2);
+ soclose(so);
+ }
+ return (error);
+}
+
+int
+portal_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct vattr *vap = ap->a_vap;
+
+ bzero(vap, sizeof(*vap));
+ vattr_null(vap);
+ vap->va_uid = 0;
+ vap->va_gid = 0;
+ vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+ vap->va_size = DEV_BSIZE;
+ vap->va_blocksize = DEV_BSIZE;
+ microtime(&vap->va_atime);
+ vap->va_mtime = vap->va_atime;
+ vap->va_ctime = vap->va_ctime;
+ vap->va_gen = 0;
+ vap->va_flags = 0;
+ vap->va_rdev = 0;
+ /* vap->va_qbytes = 0; */
+ vap->va_bytes = 0;
+ /* vap->va_qsize = 0; */
+ if (vp->v_flag & VROOT) {
+ vap->va_type = VDIR;
+ vap->va_mode = S_IRUSR|S_IWUSR|S_IXUSR|
+ S_IRGRP|S_IWGRP|S_IXGRP|
+ S_IROTH|S_IWOTH|S_IXOTH;
+ vap->va_nlink = 2;
+ vap->va_fileid = 2;
+ } else {
+ vap->va_type = VREG;
+ vap->va_mode = S_IRUSR|S_IWUSR|
+ S_IRGRP|S_IWGRP|
+ S_IROTH|S_IWOTH;
+ vap->va_nlink = 1;
+ vap->va_fileid = VTOPORTAL(vp)->pt_fileid;
+ }
+ return (0);
+}
+
+int
+portal_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ /*
+ * Can't mess with the root vnode
+ */
+ if (ap->a_vp->v_flag & VROOT)
+ return (EACCES);
+
+ return (0);
+}
+
+/*
+ * Fake readdir, just return empty directory.
+ * It is hard to deal with '.' and '..' so don't bother.
+ */
+int
+portal_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+int
+portal_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+int
+portal_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct portalnode *pt = VTOPORTAL(ap->a_vp);
+
+ if (pt->pt_arg) {
+ free((caddr_t) pt->pt_arg, M_TEMP);
+ pt->pt_arg = 0;
+ }
+ FREE(ap->a_vp->v_data, M_TEMP);
+ ap->a_vp->v_data = 0;
+
+ return (0);
+}
+
+/*
+ * Return POSIX pathconf information applicable to special devices.
+ */
+int
+portal_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_MAX_CANON:
+ *ap->a_retval = MAX_CANON;
+ return (0);
+ case _PC_MAX_INPUT:
+ *ap->a_retval = MAX_INPUT;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ case _PC_VDISABLE:
+ *ap->a_retval = _POSIX_VDISABLE;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Print out the contents of a Portal vnode.
+ */
+/* ARGSUSED */
+int
+portal_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_PORTAL, portal vnode\n");
+ return (0);
+}
+
+/*void*/
+int
+portal_vfree(ap)
+ struct vop_vfree_args /* {
+ struct vnode *a_pvp;
+ ino_t a_ino;
+ int a_mode;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+
+/*
+ * Portal vnode unsupported operation
+ */
+int
+portal_enotsupp()
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * Portal "should never get here" operation
+ */
+int
+portal_badop()
+{
+
+ panic("portal: bad op");
+ /* NOTREACHED */
+}
+
+/*
+ * Portal vnode null operation
+ */
+int
+portal_nullop()
+{
+
+ return (0);
+}
+
+#define portal_create ((int (*) __P((struct vop_create_args *)))portal_enotsupp)
+#define portal_mknod ((int (*) __P((struct vop_mknod_args *)))portal_enotsupp)
+#define portal_close ((int (*) __P((struct vop_close_args *)))nullop)
+#define portal_access ((int (*) __P((struct vop_access_args *)))nullop)
+#define portal_read ((int (*) __P((struct vop_read_args *)))portal_enotsupp)
+#define portal_write ((int (*) __P((struct vop_write_args *)))portal_enotsupp)
+#define portal_ioctl ((int (*) __P((struct vop_ioctl_args *)))portal_enotsupp)
+#define portal_select ((int (*) __P((struct vop_select_args *)))portal_enotsupp)
+#define portal_mmap ((int (*) __P((struct vop_mmap_args *)))portal_enotsupp)
+#define portal_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
+#define portal_seek ((int (*) __P((struct vop_seek_args *)))nullop)
+#define portal_remove ((int (*) __P((struct vop_remove_args *)))portal_enotsupp)
+#define portal_link ((int (*) __P((struct vop_link_args *)))portal_enotsupp)
+#define portal_rename ((int (*) __P((struct vop_rename_args *)))portal_enotsupp)
+#define portal_mkdir ((int (*) __P((struct vop_mkdir_args *)))portal_enotsupp)
+#define portal_rmdir ((int (*) __P((struct vop_rmdir_args *)))portal_enotsupp)
+#define portal_symlink \
+ ((int (*) __P((struct vop_symlink_args *)))portal_enotsupp)
+#define portal_readlink \
+ ((int (*) __P((struct vop_readlink_args *)))portal_enotsupp)
+#define portal_abortop ((int (*) __P((struct vop_abortop_args *)))nullop)
+#define portal_lock ((int (*) __P((struct vop_lock_args *)))nullop)
+#define portal_unlock ((int (*) __P((struct vop_unlock_args *)))nullop)
+#define portal_bmap ((int (*) __P((struct vop_bmap_args *)))portal_badop)
+#define portal_strategy \
+ ((int (*) __P((struct vop_strategy_args *)))portal_badop)
+#define portal_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+#define portal_advlock \
+ ((int (*) __P((struct vop_advlock_args *)))portal_enotsupp)
+#define portal_blkatoff \
+ ((int (*) __P((struct vop_blkatoff_args *)))portal_enotsupp)
+#define portal_valloc ((int(*) __P(( \
+ struct vnode *pvp, \
+ int mode, \
+ struct ucred *cred, \
+ struct vnode **vpp))) portal_enotsupp)
+#define portal_truncate \
+ ((int (*) __P((struct vop_truncate_args *)))portal_enotsupp)
+#define portal_update ((int (*) __P((struct vop_update_args *)))portal_enotsupp)
+#define portal_bwrite ((int (*) __P((struct vop_bwrite_args *)))portal_enotsupp)
+
+int (**portal_vnodeop_p)();
+struct vnodeopv_entry_desc portal_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, portal_lookup }, /* lookup */
+ { &vop_create_desc, portal_create }, /* create */
+ { &vop_mknod_desc, portal_mknod }, /* mknod */
+ { &vop_open_desc, portal_open }, /* open */
+ { &vop_close_desc, portal_close }, /* close */
+ { &vop_access_desc, portal_access }, /* access */
+ { &vop_getattr_desc, portal_getattr }, /* getattr */
+ { &vop_setattr_desc, portal_setattr }, /* setattr */
+ { &vop_read_desc, portal_read }, /* read */
+ { &vop_write_desc, portal_write }, /* write */
+ { &vop_ioctl_desc, portal_ioctl }, /* ioctl */
+ { &vop_select_desc, portal_select }, /* select */
+ { &vop_mmap_desc, portal_mmap }, /* mmap */
+ { &vop_fsync_desc, portal_fsync }, /* fsync */
+ { &vop_seek_desc, portal_seek }, /* seek */
+ { &vop_remove_desc, portal_remove }, /* remove */
+ { &vop_link_desc, portal_link }, /* link */
+ { &vop_rename_desc, portal_rename }, /* rename */
+ { &vop_mkdir_desc, portal_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, portal_rmdir }, /* rmdir */
+ { &vop_symlink_desc, portal_symlink }, /* symlink */
+ { &vop_readdir_desc, portal_readdir }, /* readdir */
+ { &vop_readlink_desc, portal_readlink }, /* readlink */
+ { &vop_abortop_desc, portal_abortop }, /* abortop */
+ { &vop_inactive_desc, portal_inactive }, /* inactive */
+ { &vop_reclaim_desc, portal_reclaim }, /* reclaim */
+ { &vop_lock_desc, portal_lock }, /* lock */
+ { &vop_unlock_desc, portal_unlock }, /* unlock */
+ { &vop_bmap_desc, portal_bmap }, /* bmap */
+ { &vop_strategy_desc, portal_strategy }, /* strategy */
+ { &vop_print_desc, portal_print }, /* print */
+ { &vop_islocked_desc, portal_islocked }, /* islocked */
+ { &vop_pathconf_desc, portal_pathconf }, /* pathconf */
+ { &vop_advlock_desc, portal_advlock }, /* advlock */
+ { &vop_blkatoff_desc, portal_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, portal_valloc }, /* valloc */
+ { &vop_vfree_desc, portal_vfree }, /* vfree */
+ { &vop_truncate_desc, portal_truncate }, /* truncate */
+ { &vop_update_desc, portal_update }, /* update */
+ { &vop_bwrite_desc, portal_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc portal_vnodeop_opv_desc =
+ { &portal_vnodeop_p, portal_vnodeop_entries };
diff --git a/sys/miscfs/procfs/README b/sys/miscfs/procfs/README
new file mode 100644
index 0000000..38811b3
--- /dev/null
+++ b/sys/miscfs/procfs/README
@@ -0,0 +1,113 @@
+saute procfs lyonnais
+
+procfs supports two levels of directory. the filesystem root
+directory contains a representation of the system process table.
+this consists of an entry for each active and zombie process, and
+an additional entry "curproc" which always represents the process
+making the lookup request.
+
+each of the sub-directories contains several files. these files
+are used to control and interrogate processes. the files implemented
+are:
+
+ file - xxx. the exec'ed file.
+
+ status - r/o. returns process status.
+
+ ctl - w/o. sends a control message to the process.
+ for example:
+ echo hup > /proc/curproc/note
+ will send a SIGHUP to the shell.
+ whereas
+ echo attach > /proc/1293/ctl
+ would set up process 1293 for debugging.
+ see below for more details.
+
+ mem - r/w. virtual memory image of the process.
+ parts of the address space are readable
+ only if they exist in the target process.
+ a more reasonable alternative might be
+ to return zero pages instead of an error.
+ comments?
+
+ note - w/o. writing a string here sends the
+ equivalent note to the process.
+ [ not implemented. ]
+
+ notepg - w/o. the same as note, but sends to all
+ members of the process group.
+ [ not implemented. ]
+
+ regs - r/w. process register set. this can be read
+ or written any time even if the process
+ is not stopped. since the bsd kernel
+ is single-processor, this implementation
+ will get the "right" register values.
+ a multi-proc kernel would need to do some
+ synchronisation.
+
+this then looks like:
+
+% ls -li /proc
+total 0
+ 9 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 0
+ 17 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 1
+ 89 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 10
+ 25 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 2
+2065 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 257
+2481 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 309
+ 265 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 32
+3129 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 390
+3209 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 400
+3217 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 401
+3273 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 408
+ 393 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 48
+ 409 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 50
+ 465 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 57
+ 481 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 59
+ 537 dr-xr-xr-x 2 root kmem 0 Sep 21 15:06 66
+ 545 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 67
+ 657 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 81
+ 665 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 82
+ 673 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 83
+ 681 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 84
+3273 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 curproc
+% ls -li /proc/curproc
+total 408
+3341 --w------- 1 jsp staff 0 Sep 21 15:06 ctl
+1554 -r-xr-xr-x 1 bin bin 90112 Mar 29 04:52 file
+3339 -rw------- 1 jsp staff 118784 Sep 21 15:06 mem
+3343 --w------- 1 jsp staff 0 Sep 21 15:06 note
+3344 --w------- 1 jsp staff 0 Sep 21 15:06 notepg
+3340 -rw------- 1 jsp staff 0 Sep 21 15:06 regs
+3342 -r--r--r-- 1 jsp staff 0 Sep 21 15:06 status
+% df /proc/curproc /proc/curproc/file
+Filesystem 512-blocks Used Avail Capacity Mounted on
+proc 2 2 0 100% /proc
+/dev/wd0a 16186 13548 1018 93% /
+% cat /proc/curproc/status
+cat 446 439 400 81 12,0 ctty 748620684 270000 0 0 0 20000 nochan 11 20 20 20 0 21 117
+
+
+
+the basic sequence of commands written to "ctl" would be
+
+ attach - this stops the target process and
+ arranges for the sending process
+ to become the debug control process
+ wait - wait for the target process to come to
+ a steady state ready for debugging.
+ step - single step, with no signal delivery.
+ run - continue running, with no signal delivery,
+ until next trap or breakpoint.
+ <signame> - deliver signal <signame> and continue running.
+ detach - continue execution of the target process
+ and remove it from control by the debug process
+
+in a normal debugging environment, where the target is fork/exec'd by
+the debugger, the debugger should fork and the child should stop itself
+(with a self-inflicted SIGSTOP). the parent should do a "wait" then an
+"attach". as before, the child will hit a breakpoint on the first
+instruction in any newly exec'd image.
+
+$Id: README,v 3.1 1993/12/15 09:40:17 jsp Exp $
diff --git a/sys/miscfs/procfs/procfs.h b/sys/miscfs/procfs/procfs.h
new file mode 100644
index 0000000..f7b8fa3
--- /dev/null
+++ b/sys/miscfs/procfs/procfs.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs.h 8.6 (Berkeley) 2/3/94
+ *
+ * From:
+ * $Id: procfs.h,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+/*
+ * The different types of node in a procfs filesystem
+ */
+typedef enum {
+ Proot, /* the filesystem root */
+ Pproc, /* a process-specific sub-directory */
+ Pfile, /* the executable file */
+ Pmem, /* the process's memory image */
+ Pregs, /* the process's register set */
+ Pfpregs, /* the process's FP register set */
+ Pctl, /* process control */
+ Pstatus, /* process status */
+ Pnote, /* process notifier */
+ Pnotepg /* process group notifier */
+} pfstype;
+
+/*
+ * control data for the proc file system.
+ */
+struct pfsnode {
+ struct pfsnode *pfs_next; /* next on list */
+ struct vnode *pfs_vnode; /* vnode associated with this pfsnode */
+ pfstype pfs_type; /* type of procfs node */
+ pid_t pfs_pid; /* associated process */
+ u_short pfs_mode; /* mode bits for stat() */
+ u_long pfs_flags; /* open flags */
+ u_long pfs_fileno; /* unique file id */
+};
+
+#define PROCFS_NOTELEN 64 /* max length of a note (/proc/$pid/note) */
+#define PROCFS_CTLLEN 8 /* max length of a ctl msg (/proc/$pid/ctl */
+
+/*
+ * Kernel stuff follows
+ */
+#ifdef KERNEL
+#define CNEQ(cnp, s, len) \
+ ((cnp)->cn_namelen == (len) && \
+ (bcmp((s), (cnp)->cn_nameptr, (len)) == 0))
+
+/*
+ * Format of a directory entry in /proc, ...
+ * This must map onto struct dirent (see <dirent.h>)
+ */
+#define PROCFS_NAMELEN 8
+struct pfsdent {
+ u_long d_fileno;
+ u_short d_reclen;
+ u_char d_type;
+ u_char d_namlen;
+ char d_name[PROCFS_NAMELEN];
+};
+#define UIO_MX sizeof(struct pfsdent)
+#define PROCFS_FILENO(pid, type) \
+ (((type) == Proot) ? \
+ 2 : \
+ ((((pid)+1) << 3) + ((int) (type))))
+
+/*
+ * Convert between pfsnode vnode
+ */
+#define VTOPFS(vp) ((struct pfsnode *)(vp)->v_data)
+#define PFSTOV(pfs) ((pfs)->pfs_vnode)
+
+typedef struct vfs_namemap vfs_namemap_t;
+struct vfs_namemap {
+ const char *nm_name;
+ int nm_val;
+};
+
+extern int vfs_getuserstr __P((struct uio *, char *, int *));
+extern vfs_namemap_t *vfs_findname __P((vfs_namemap_t *, char *, int));
+
+/* <machine/reg.h> */
+struct reg;
+struct fpreg;
+
+#define PFIND(pid) ((pid) ? pfind(pid) : &proc0)
+extern int procfs_freevp __P((struct vnode *));
+extern int procfs_allocvp __P((struct mount *, struct vnode **, long, pfstype));
+extern struct vnode *procfs_findtextvp __P((struct proc *));
+extern int procfs_sstep __P((struct proc *));
+extern void procfs_fix_sstep __P((struct proc *));
+extern int procfs_read_regs __P((struct proc *, struct reg *));
+extern int procfs_write_regs __P((struct proc *, struct reg *));
+extern int procfs_read_fpregs __P((struct proc *, struct fpreg *));
+extern int procfs_write_fpregs __P((struct proc *, struct fpreg *));
+extern int procfs_donote __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+extern int procfs_doregs __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+extern int procfs_dofpregs __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+extern int procfs_domem __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+extern int procfs_doctl __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+extern int procfs_dostatus __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+
+#define PROCFS_LOCKED 0x01
+#define PROCFS_WANT 0x02
+
+extern int (**procfs_vnodeop_p)();
+extern struct vfsops procfs_vfsops;
+
+/*
+ * Prototypes for procfs vnode ops
+ */
+int procfs_badop(); /* varargs */
+int procfs_rw __P((struct vop_read_args *));
+int procfs_lookup __P((struct vop_lookup_args *));
+#define procfs_create ((int (*) __P((struct vop_create_args *))) procfs_badop)
+#define procfs_mknod ((int (*) __P((struct vop_mknod_args *))) procfs_badop)
+int procfs_open __P((struct vop_open_args *));
+int procfs_close __P((struct vop_close_args *));
+int procfs_access __P((struct vop_access_args *));
+int procfs_getattr __P((struct vop_getattr_args *));
+int procfs_setattr __P((struct vop_setattr_args *));
+#define procfs_read procfs_rw
+#define procfs_write procfs_rw
+int procfs_ioctl __P((struct vop_ioctl_args *));
+#define procfs_select ((int (*) __P((struct vop_select_args *))) procfs_badop)
+#define procfs_mmap ((int (*) __P((struct vop_mmap_args *))) procfs_badop)
+#define procfs_fsync ((int (*) __P((struct vop_fsync_args *))) procfs_badop)
+#define procfs_seek ((int (*) __P((struct vop_seek_args *))) procfs_badop)
+#define procfs_remove ((int (*) __P((struct vop_remove_args *))) procfs_badop)
+#define procfs_link ((int (*) __P((struct vop_link_args *))) procfs_badop)
+#define procfs_rename ((int (*) __P((struct vop_rename_args *))) procfs_badop)
+#define procfs_mkdir ((int (*) __P((struct vop_mkdir_args *))) procfs_badop)
+#define procfs_rmdir ((int (*) __P((struct vop_rmdir_args *))) procfs_badop)
+#define procfs_symlink ((int (*) __P((struct vop_symlink_args *))) procfs_badop)
+int procfs_readdir __P((struct vop_readdir_args *));
+#define procfs_readlink ((int (*) __P((struct vop_readlink_args *))) procfs_badop)
+int procfs_abortop __P((struct vop_abortop_args *));
+int procfs_inactive __P((struct vop_inactive_args *));
+int procfs_reclaim __P((struct vop_reclaim_args *));
+#define procfs_lock ((int (*) __P((struct vop_lock_args *))) nullop)
+#define procfs_unlock ((int (*) __P((struct vop_unlock_args *))) nullop)
+int procfs_bmap __P((struct vop_bmap_args *));
+#define procfs_strategy ((int (*) __P((struct vop_strategy_args *))) procfs_badop)
+int procfs_print __P((struct vop_print_args *));
+#define procfs_islocked ((int (*) __P((struct vop_islocked_args *))) nullop)
+#define procfs_advlock ((int (*) __P((struct vop_advlock_args *))) procfs_badop)
+#define procfs_blkatoff ((int (*) __P((struct vop_blkatoff_args *))) procfs_badop)
+#define procfs_valloc ((int (*) __P((struct vop_valloc_args *))) procfs_badop)
+#define procfs_vfree ((int (*) __P((struct vop_vfree_args *))) nullop)
+#define procfs_truncate ((int (*) __P((struct vop_truncate_args *))) procfs_badop)
+#define procfs_update ((int (*) __P((struct vop_update_args *))) nullop)
+#endif /* KERNEL */
diff --git a/sys/miscfs/procfs/procfs_ctl.c b/sys/miscfs/procfs/procfs_ctl.c
new file mode 100644
index 0000000..a42a03c
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_ctl.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_ctl.c 8.3 (Berkeley) 1/21/94
+ *
+ * From:
+ * $Id: procfs_ctl.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/resource.h>
+#include <sys/resourcevar.h>
+#include <miscfs/procfs/procfs.h>
+
+/*
+ * True iff process (p) is in trace wait state
+ * relative to process (curp)
+ */
+#define TRACE_WAIT_P(curp, p) \
+ ((p)->p_stat == SSTOP && \
+ (p)->p_pptr == (curp) && \
+ ((p)->p_flag & P_TRACED))
+
+#ifdef notdef
+#define FIX_SSTEP(p) { \
+ procfs_fix_sstep(p); \
+ } \
+}
+#else
+#define FIX_SSTEP(p)
+#endif
+
+#define PROCFS_CTL_ATTACH 1
+#define PROCFS_CTL_DETACH 2
+#define PROCFS_CTL_STEP 3
+#define PROCFS_CTL_RUN 4
+#define PROCFS_CTL_WAIT 5
+
+static vfs_namemap_t ctlnames[] = {
+ /* special /proc commands */
+ { "attach", PROCFS_CTL_ATTACH },
+ { "detach", PROCFS_CTL_DETACH },
+ { "step", PROCFS_CTL_STEP },
+ { "run", PROCFS_CTL_RUN },
+ { "wait", PROCFS_CTL_WAIT },
+ { 0 },
+};
+
+static vfs_namemap_t signames[] = {
+ /* regular signal names */
+ { "hup", SIGHUP }, { "int", SIGINT },
+ { "quit", SIGQUIT }, { "ill", SIGILL },
+ { "trap", SIGTRAP }, { "abrt", SIGABRT },
+ { "iot", SIGIOT }, { "emt", SIGEMT },
+ { "fpe", SIGFPE }, { "kill", SIGKILL },
+ { "bus", SIGBUS }, { "segv", SIGSEGV },
+ { "sys", SIGSYS }, { "pipe", SIGPIPE },
+ { "alrm", SIGALRM }, { "term", SIGTERM },
+ { "urg", SIGURG }, { "stop", SIGSTOP },
+ { "tstp", SIGTSTP }, { "cont", SIGCONT },
+ { "chld", SIGCHLD }, { "ttin", SIGTTIN },
+ { "ttou", SIGTTOU }, { "io", SIGIO },
+ { "xcpu", SIGXCPU }, { "xfsz", SIGXFSZ },
+ { "vtalrm", SIGVTALRM }, { "prof", SIGPROF },
+ { "winch", SIGWINCH }, { "info", SIGINFO },
+ { "usr1", SIGUSR1 }, { "usr2", SIGUSR2 },
+ { 0 },
+};
+
+static int
+procfs_control(curp, p, op)
+ struct proc *curp;
+ struct proc *p;
+ int op;
+{
+ int error;
+
+ /*
+ * Attach - attaches the target process for debugging
+ * by the calling process.
+ */
+ if (op == PROCFS_CTL_ATTACH) {
+ /* check whether already being traced */
+ if (p->p_flag & P_TRACED)
+ return (EBUSY);
+
+ /* can't trace yourself! */
+ if (p->p_pid == curp->p_pid)
+ return (EINVAL);
+
+ /*
+ * Go ahead and set the trace flag.
+ * Save the old parent (it's reset in
+ * _DETACH, and also in kern_exit.c:wait4()
+ * Reparent the process so that the tracing
+ * proc gets to see all the action.
+ * Stop the target.
+ */
+ p->p_flag |= P_TRACED;
+ p->p_xstat = 0; /* XXX ? */
+ if (p->p_pptr != curp) {
+ p->p_oppid = p->p_pptr->p_pid;
+ proc_reparent(p, curp);
+ }
+ psignal(p, SIGSTOP);
+ return (0);
+ }
+
+ /*
+ * Target process must be stopped, owned by (curp) and
+ * be set up for tracing (P_TRACED flag set).
+ * Allow DETACH to take place at any time for sanity.
+ * Allow WAIT any time, of course.
+ */
+ switch (op) {
+ case PROCFS_CTL_DETACH:
+ case PROCFS_CTL_WAIT:
+ break;
+
+ default:
+ if (!TRACE_WAIT_P(curp, p))
+ return (EBUSY);
+ }
+
+ /*
+ * do single-step fixup if needed
+ */
+ FIX_SSTEP(p);
+
+ /*
+ * Don't deliver any signal by default.
+ * To continue with a signal, just send
+ * the signal name to the ctl file
+ */
+ p->p_xstat = 0;
+
+ switch (op) {
+ /*
+ * Detach. Cleans up the target process, reparent it if possible
+ * and set it running once more.
+ */
+ case PROCFS_CTL_DETACH:
+ /* if not being traced, then this is a painless no-op */
+ if ((p->p_flag & P_TRACED) == 0)
+ return (0);
+
+ /* not being traced any more */
+ p->p_flag &= ~P_TRACED;
+
+ /* give process back to original parent */
+ if (p->p_oppid != p->p_pptr->p_pid) {
+ struct proc *pp;
+
+ pp = pfind(p->p_oppid);
+ if (pp)
+ proc_reparent(p, pp);
+ }
+
+ p->p_oppid = 0;
+ p->p_flag &= ~P_WAITED; /* XXX ? */
+ wakeup((caddr_t) curp); /* XXX for CTL_WAIT below ? */
+
+ break;
+
+ /*
+ * Step. Let the target process execute a single instruction.
+ */
+ case PROCFS_CTL_STEP:
+ procfs_sstep(p);
+ break;
+
+ /*
+ * Run. Let the target process continue running until a breakpoint
+ * or some other trap.
+ */
+ case PROCFS_CTL_RUN:
+ break;
+
+ /*
+ * Wait for the target process to stop.
+ * If the target is not being traced then just wait
+ * to enter
+ */
+ case PROCFS_CTL_WAIT:
+ error = 0;
+ if (p->p_flag & P_TRACED) {
+ while (error == 0 &&
+ (p->p_stat != SSTOP) &&
+ (p->p_flag & P_TRACED) &&
+ (p->p_pptr == curp)) {
+ error = tsleep((caddr_t) p,
+ PWAIT|PCATCH, "procfsx", 0);
+ }
+ if (error == 0 && !TRACE_WAIT_P(curp, p))
+ error = EBUSY;
+ } else {
+ while (error == 0 && p->p_stat != SSTOP) {
+ error = tsleep((caddr_t) p,
+ PWAIT|PCATCH, "procfs", 0);
+ }
+ }
+ return (error);
+
+ default:
+ panic("procfs_control");
+ }
+
+ if (p->p_stat == SSTOP)
+ setrunnable(p);
+ return (0);
+}
+
+int
+procfs_doctl(curp, p, pfs, uio)
+ struct proc *curp;
+ struct pfsnode *pfs;
+ struct uio *uio;
+ struct proc *p;
+{
+ int xlen;
+ int error;
+ char msg[PROCFS_CTLLEN+1];
+ vfs_namemap_t *nm;
+
+ if (uio->uio_rw != UIO_WRITE)
+ return (EOPNOTSUPP);
+
+ xlen = PROCFS_CTLLEN;
+ error = vfs_getuserstr(uio, msg, &xlen);
+ if (error)
+ return (error);
+
+ /*
+ * Map signal names into signal generation
+ * or debug control. Unknown commands and/or signals
+ * return EOPNOTSUPP.
+ *
+ * Sending a signal while the process is being debugged
+ * also has the side effect of letting the target continue
+ * to run. There is no way to single-step a signal delivery.
+ */
+ error = EOPNOTSUPP;
+
+ nm = vfs_findname(ctlnames, msg, xlen);
+ if (nm) {
+ error = procfs_control(curp, p, nm->nm_val);
+ } else {
+ nm = vfs_findname(signames, msg, xlen);
+ if (nm) {
+ if (TRACE_WAIT_P(curp, p)) {
+ p->p_xstat = nm->nm_val;
+ FIX_SSTEP(p);
+ setrunnable(p);
+ } else {
+ psignal(p, nm->nm_val);
+ }
+ error = 0;
+ }
+ }
+
+ return (error);
+}
diff --git a/sys/miscfs/procfs/procfs_fpregs.c b/sys/miscfs/procfs/procfs_fpregs.c
new file mode 100644
index 0000000..6d850a6
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_fpregs.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_fpregs.c 8.1 (Berkeley) 1/27/94
+ *
+ * From:
+ * $Id: procfs_regs.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <machine/reg.h>
+#include <miscfs/procfs/procfs.h>
+
+int
+procfs_dofpregs(curp, p, pfs, uio)
+ struct proc *curp;
+ struct proc *p;
+ struct pfsnode *pfs;
+ struct uio *uio;
+{
+ int error;
+ struct fpreg r;
+ char *kv;
+ int kl;
+
+ kl = sizeof(r);
+ kv = (char *) &r;
+
+ kv += uio->uio_offset;
+ kl -= uio->uio_offset;
+ if (kl > uio->uio_resid)
+ kl = uio->uio_resid;
+
+ if (kl < 0)
+ error = EINVAL;
+ else
+ error = procfs_read_fpregs(p, &r);
+ if (error == 0)
+ error = uiomove(kv, kl, uio);
+ if (error == 0 && uio->uio_rw == UIO_WRITE) {
+ if (p->p_stat != SSTOP)
+ error = EBUSY;
+ else
+ error = procfs_write_fpregs(p, &r);
+ }
+
+ uio->uio_offset = 0;
+ return (error);
+}
diff --git a/sys/miscfs/procfs/procfs_mem.c b/sys/miscfs/procfs/procfs_mem.c
new file mode 100644
index 0000000..039983d
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_mem.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993 Sean Eric Fagan
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry and Sean Eric Fagan.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_mem.c 8.4 (Berkeley) 1/21/94
+ *
+ * From:
+ * $Id: procfs_mem.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+/*
+ * This is a lightly hacked and merged version
+ * of sef's pread/pwrite functions
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <miscfs/procfs/procfs.h>
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+
+static int
+procfs_rwmem(p, uio)
+ struct proc *p;
+ struct uio *uio;
+{
+ int error;
+ int writing;
+
+ writing = uio->uio_rw == UIO_WRITE;
+
+ /*
+ * Only map in one page at a time. We don't have to, but it
+ * makes things easier. This way is trivial - right?
+ */
+ do {
+ vm_map_t map, tmap;
+ vm_object_t object;
+ vm_offset_t kva;
+ vm_offset_t uva;
+ int page_offset; /* offset into page */
+ vm_offset_t pageno; /* page number */
+ vm_map_entry_t out_entry;
+ vm_prot_t out_prot;
+ vm_page_t m;
+ boolean_t wired, single_use;
+ vm_offset_t off;
+ u_int len;
+ int fix_prot;
+
+ uva = (vm_offset_t) uio->uio_offset;
+ if (uva > VM_MAXUSER_ADDRESS) {
+ error = 0;
+ break;
+ }
+
+ /*
+ * Get the page number of this segment.
+ */
+ pageno = trunc_page(uva);
+ page_offset = uva - pageno;
+
+ /*
+ * How many bytes to copy
+ */
+ len = min(PAGE_SIZE - page_offset, uio->uio_resid);
+
+ /*
+ * The map we want...
+ */
+ map = &p->p_vmspace->vm_map;
+
+ /*
+ * Check the permissions for the area we're interested
+ * in.
+ */
+ fix_prot = 0;
+ if (writing)
+ fix_prot = !vm_map_check_protection(map, pageno,
+ pageno + PAGE_SIZE, VM_PROT_WRITE);
+
+ if (fix_prot) {
+ /*
+ * If the page is not writable, we make it so.
+ * XXX It is possible that a page may *not* be
+ * read/executable, if a process changes that!
+ * We will assume, for now, that a page is either
+ * VM_PROT_ALL, or VM_PROT_READ|VM_PROT_EXECUTE.
+ */
+ error = vm_map_protect(map, pageno,
+ pageno + PAGE_SIZE, VM_PROT_ALL, 0);
+ if (error)
+ break;
+ }
+
+ /*
+ * Now we need to get the page. out_entry, out_prot, wired,
+ * and single_use aren't used. One would think the vm code
+ * would be a *bit* nicer... We use tmap because
+ * vm_map_lookup() can change the map argument.
+ */
+ tmap = map;
+ error = vm_map_lookup(&tmap, pageno,
+ writing ? VM_PROT_WRITE : VM_PROT_READ,
+ &out_entry, &object, &off, &out_prot,
+ &wired, &single_use);
+ /*
+ * We're done with tmap now.
+ */
+ if (!error)
+ vm_map_lookup_done(tmap, out_entry);
+
+ /*
+ * Fault the page in...
+ */
+ if (!error && writing && object->shadow) {
+ m = vm_page_lookup(object, off);
+ if (m == 0 || (m->flags & PG_COPYONWRITE))
+ error = vm_fault(map, pageno,
+ VM_PROT_WRITE, FALSE);
+ }
+
+ /* Find space in kernel_map for the page we're interested in */
+ if (!error)
+ error = vm_map_find(kernel_map, object, off, &kva,
+ PAGE_SIZE, 1);
+
+ if (!error) {
+ /*
+ * Neither vm_map_lookup() nor vm_map_find() appear
+ * to add a reference count to the object, so we do
+ * that here and now.
+ */
+ vm_object_reference(object);
+
+ /*
+ * Mark the page we just found as pageable.
+ */
+ error = vm_map_pageable(kernel_map, kva,
+ kva + PAGE_SIZE, 0);
+
+ /*
+ * Now do the i/o move.
+ */
+ if (!error)
+ error = uiomove(kva + page_offset, len, uio);
+
+ vm_map_remove(kernel_map, kva, kva + PAGE_SIZE);
+ }
+ if (fix_prot)
+ vm_map_protect(map, pageno, pageno + PAGE_SIZE,
+ VM_PROT_READ|VM_PROT_EXECUTE, 0);
+ } while (error == 0 && uio->uio_resid > 0);
+
+ return (error);
+}
+
+/*
+ * Copy data in and out of the target process.
+ * We do this by mapping the process's page into
+ * the kernel and then doing a uiomove direct
+ * from the kernel address space.
+ */
+int
+procfs_domem(curp, p, pfs, uio)
+ struct proc *curp;
+ struct proc *p;
+ struct pfsnode *pfs;
+ struct uio *uio;
+{
+ int error;
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ error = procfs_rwmem(p, uio);
+
+ return (error);
+}
+
+/*
+ * Given process (p), find the vnode from which
+ * it's text segment is being executed.
+ *
+ * It would be nice to grab this information from
+ * the VM system, however, there is no sure-fire
+ * way of doing that. Instead, fork(), exec() and
+ * wait() all maintain the p_textvp field in the
+ * process proc structure which contains a held
+ * reference to the exec'ed vnode.
+ */
+struct vnode *
+procfs_findtextvp(p)
+ struct proc *p;
+{
+ return (p->p_textvp);
+}
+
+
+#ifdef probably_never
+/*
+ * Given process (p), find the vnode from which
+ * it's text segment is being mapped.
+ *
+ * (This is here, rather than in procfs_subr in order
+ * to keep all the VM related code in one place.)
+ */
+struct vnode *
+procfs_findtextvp(p)
+ struct proc *p;
+{
+ int error;
+ vm_object_t object;
+ vm_offset_t pageno; /* page number */
+
+ /* find a vnode pager for the user address space */
+
+ for (pageno = VM_MIN_ADDRESS;
+ pageno < VM_MAXUSER_ADDRESS;
+ pageno += PAGE_SIZE) {
+ vm_map_t map;
+ vm_map_entry_t out_entry;
+ vm_prot_t out_prot;
+ boolean_t wired, single_use;
+ vm_offset_t off;
+
+ map = &p->p_vmspace->vm_map;
+ error = vm_map_lookup(&map, pageno,
+ VM_PROT_READ,
+ &out_entry, &object, &off, &out_prot,
+ &wired, &single_use);
+
+ if (!error) {
+ vm_pager_t pager;
+
+ printf("procfs: found vm object\n");
+ vm_map_lookup_done(map, out_entry);
+ printf("procfs: vm object = %x\n", object);
+
+ /*
+ * At this point, assuming no errors, object
+ * is the VM object mapping UVA (pageno).
+ * Ensure it has a vnode pager, then grab
+ * the vnode from that pager's handle.
+ */
+
+ pager = object->pager;
+ printf("procfs: pager = %x\n", pager);
+ if (pager)
+ printf("procfs: found pager, type = %d\n", pager->pg_type);
+ if (pager && pager->pg_type == PG_VNODE) {
+ struct vnode *vp;
+
+ vp = (struct vnode *) pager->pg_handle;
+ printf("procfs: vp = 0x%x\n", vp);
+ return (vp);
+ }
+ }
+ }
+
+ printf("procfs: text object not found\n");
+ return (0);
+}
+#endif /* probably_never */
diff --git a/sys/miscfs/procfs/procfs_note.c b/sys/miscfs/procfs/procfs_note.c
new file mode 100644
index 0000000..bf2f160
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_note.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_note.c 8.2 (Berkeley) 1/21/94
+ *
+ * From:
+ * $Id: procfs_note.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/signal.h>
+#include <miscfs/procfs/procfs.h>
+
+int
+procfs_donote(curp, p, pfs, uio)
+ struct proc *curp;
+ struct proc *p;
+ struct pfsnode *pfs;
+ struct uio *uio;
+{
+ int xlen;
+ int error;
+ char note[PROCFS_NOTELEN+1];
+
+ if (uio->uio_rw != UIO_WRITE)
+ return (EINVAL);
+
+ xlen = PROCFS_NOTELEN;
+ error = vfs_getuserstr(uio, note, &xlen);
+ if (error)
+ return (error);
+
+ /* send to process's notify function */
+ return (EOPNOTSUPP);
+}
diff --git a/sys/miscfs/procfs/procfs_regs.c b/sys/miscfs/procfs/procfs_regs.c
new file mode 100644
index 0000000..fa95fef
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_regs.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_regs.c 8.3 (Berkeley) 1/27/94
+ *
+ * From:
+ * $Id: procfs_regs.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <machine/reg.h>
+#include <miscfs/procfs/procfs.h>
+
+int
+procfs_doregs(curp, p, pfs, uio)
+ struct proc *curp;
+ struct proc *p;
+ struct pfsnode *pfs;
+ struct uio *uio;
+{
+ int error;
+ struct reg r;
+ char *kv;
+ int kl;
+
+ kl = sizeof(r);
+ kv = (char *) &r;
+
+ kv += uio->uio_offset;
+ kl -= uio->uio_offset;
+ if (kl > uio->uio_resid)
+ kl = uio->uio_resid;
+
+ if (kl < 0)
+ error = EINVAL;
+ else
+ error = procfs_read_regs(p, &r);
+ if (error == 0)
+ error = uiomove(kv, kl, uio);
+ if (error == 0 && uio->uio_rw == UIO_WRITE) {
+ if (p->p_stat != SSTOP)
+ error = EBUSY;
+ else
+ error = procfs_write_regs(p, &r);
+ }
+
+ uio->uio_offset = 0;
+ return (error);
+}
diff --git a/sys/miscfs/procfs/procfs_status.c b/sys/miscfs/procfs/procfs_status.c
new file mode 100644
index 0000000..d88aaab
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_status.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_status.c 8.3 (Berkeley) 2/17/94
+ *
+ * From:
+ * $Id: procfs_status.c,v 3.1 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/resource.h>
+#include <sys/resourcevar.h>
+#include <miscfs/procfs/procfs.h>
+
+int
+procfs_dostatus(curp, p, pfs, uio)
+ struct proc *curp;
+ struct proc *p;
+ struct pfsnode *pfs;
+ struct uio *uio;
+{
+ struct session *sess;
+ struct tty *tp;
+ struct ucred *cr;
+ char *ps;
+ char *sep;
+ int pid, ppid, pgid, sid;
+ int i;
+ int xlen;
+ int error;
+ char psbuf[256]; /* XXX - conservative */
+
+ if (uio->uio_rw != UIO_READ)
+ return (EOPNOTSUPP);
+
+ pid = p->p_pid;
+ ppid = p->p_pptr ? p->p_pptr->p_pid : 0,
+ pgid = p->p_pgrp->pg_id;
+ sess = p->p_pgrp->pg_session;
+ sid = sess->s_leader ? sess->s_leader->p_pid : 0;
+
+/* comm pid ppid pgid sid maj,min ctty,sldr start ut st wmsg uid groups ... */
+
+ ps = psbuf;
+ bcopy(p->p_comm, ps, MAXCOMLEN);
+ ps[MAXCOMLEN] = '\0';
+ ps += strlen(ps);
+ ps += sprintf(ps, " %d %d %d %d ", pid, ppid, pgid, sid);
+
+ if ((p->p_flag&P_CONTROLT) && (tp = sess->s_ttyp))
+ ps += sprintf(ps, "%d,%d ", major(tp->t_dev), minor(tp->t_dev));
+ else
+ ps += sprintf(ps, "%d,%d ", -1, -1);
+
+ sep = "";
+ if (sess->s_ttyvp) {
+ ps += sprintf(ps, "%sctty", sep);
+ sep = ",";
+ }
+ if (SESS_LEADER(p)) {
+ ps += sprintf(ps, "%ssldr", sep);
+ sep = ",";
+ }
+ if (*sep != ',')
+ ps += sprintf(ps, "noflags");
+
+ if (p->p_flag & P_INMEM)
+ ps += sprintf(ps, " %d,%d",
+ p->p_stats->p_start.tv_sec,
+ p->p_stats->p_start.tv_usec);
+ else
+ ps += sprintf(ps, " -1,-1");
+
+ {
+ struct timeval ut, st;
+
+ calcru(p, &ut, &st, (void *) 0);
+ ps += sprintf(ps, " %d,%d %d,%d",
+ ut.tv_sec,
+ ut.tv_usec,
+ st.tv_sec,
+ st.tv_usec);
+ }
+
+ ps += sprintf(ps, " %s",
+ (p->p_wchan && p->p_wmesg) ? p->p_wmesg : "nochan");
+
+ cr = p->p_ucred;
+
+ ps += sprintf(ps, " %d", cr->cr_uid, cr->cr_gid);
+ for (i = 0; i < cr->cr_ngroups; i++)
+ ps += sprintf(ps, ",%d", cr->cr_groups[i]);
+ ps += sprintf(ps, "\n");
+
+ xlen = ps - psbuf;
+ xlen -= uio->uio_offset;
+ ps = psbuf + uio->uio_offset;
+ xlen = min(xlen, uio->uio_resid);
+ if (xlen <= 0)
+ error = 0;
+ else
+ error = uiomove(ps, xlen, uio);
+
+ return (error);
+}
diff --git a/sys/miscfs/procfs/procfs_subr.c b/sys/miscfs/procfs/procfs_subr.c
new file mode 100644
index 0000000..b371af1
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_subr.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_subr.c 8.4 (Berkeley) 1/27/94
+ *
+ * From:
+ * $Id: procfs_subr.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <miscfs/procfs/procfs.h>
+
+static struct pfsnode *pfshead;
+static int pfsvplock;
+
+/*
+ * allocate a pfsnode/vnode pair. the vnode is
+ * referenced, but not locked.
+ *
+ * the pid, pfs_type, and mount point uniquely
+ * identify a pfsnode. the mount point is needed
+ * because someone might mount this filesystem
+ * twice.
+ *
+ * all pfsnodes are maintained on a singly-linked
+ * list. new nodes are only allocated when they cannot
+ * be found on this list. entries on the list are
+ * removed when the vfs reclaim entry is called.
+ *
+ * a single lock is kept for the entire list. this is
+ * needed because the getnewvnode() function can block
+ * waiting for a vnode to become free, in which case there
+ * may be more than one process trying to get the same
+ * vnode. this lock is only taken if we are going to
+ * call getnewvnode, since the kernel itself is single-threaded.
+ *
+ * if an entry is found on the list, then call vget() to
+ * take a reference. this is done because there may be
+ * zero references to it and so it needs to removed from
+ * the vnode free list.
+ */
+int
+procfs_allocvp(mp, vpp, pid, pfs_type)
+ struct mount *mp;
+ struct vnode **vpp;
+ long pid;
+ pfstype pfs_type;
+{
+ int error;
+ struct pfsnode *pfs;
+ struct pfsnode **pp;
+
+loop:
+ for (pfs = pfshead; pfs != 0; pfs = pfs->pfs_next) {
+ if (pfs->pfs_pid == pid &&
+ pfs->pfs_type == pfs_type &&
+ PFSTOV(pfs)->v_mount == mp) {
+ if (vget(pfs->pfs_vnode, 0))
+ goto loop;
+ *vpp = pfs->pfs_vnode;
+ return (0);
+ }
+ }
+
+ /*
+ * otherwise lock the vp list while we call getnewvnode
+ * since that can block.
+ */
+ if (pfsvplock & PROCFS_LOCKED) {
+ pfsvplock |= PROCFS_WANT;
+ sleep((caddr_t) &pfsvplock, PINOD);
+ goto loop;
+ }
+ pfsvplock |= PROCFS_LOCKED;
+
+ error = getnewvnode(VT_PROCFS, mp, procfs_vnodeop_p, vpp);
+ if (error)
+ goto out;
+
+ MALLOC((*vpp)->v_data, void *, sizeof(struct pfsnode),
+ M_TEMP, M_WAITOK);
+
+ pfs = VTOPFS(*vpp);
+ pfs->pfs_next = 0;
+ pfs->pfs_pid = (pid_t) pid;
+ pfs->pfs_type = pfs_type;
+ pfs->pfs_vnode = *vpp;
+ pfs->pfs_flags = 0;
+ pfs->pfs_fileno = PROCFS_FILENO(pid, pfs_type);
+
+ switch (pfs_type) {
+ case Proot: /* /proc = dr-xr-xr-x */
+ pfs->pfs_mode = (VREAD|VEXEC) |
+ (VREAD|VEXEC) >> 3 |
+ (VREAD|VEXEC) >> 6;
+ break;
+
+ case Pproc:
+ pfs->pfs_mode = (VREAD|VEXEC) |
+ (VREAD|VEXEC) >> 3 |
+ (VREAD|VEXEC) >> 6;
+ break;
+
+ case Pfile:
+ pfs->pfs_mode = (VREAD|VWRITE);
+ break;
+
+ case Pmem:
+ pfs->pfs_mode = (VREAD|VWRITE);
+ break;
+
+ case Pregs:
+ pfs->pfs_mode = (VREAD|VWRITE);
+ break;
+
+ case Pfpregs:
+ pfs->pfs_mode = (VREAD|VWRITE);
+ break;
+
+ case Pctl:
+ pfs->pfs_mode = (VWRITE);
+ break;
+
+ case Pstatus:
+ pfs->pfs_mode = (VREAD) |
+ (VREAD >> 3) |
+ (VREAD >> 6);
+ break;
+
+ case Pnote:
+ pfs->pfs_mode = (VWRITE);
+ break;
+
+ case Pnotepg:
+ pfs->pfs_mode = (VWRITE);
+ break;
+
+ default:
+ panic("procfs_allocvp");
+ }
+
+ /* add to procfs vnode list */
+ for (pp = &pfshead; *pp; pp = &(*pp)->pfs_next)
+ continue;
+ *pp = pfs;
+
+out:
+ pfsvplock &= ~PROCFS_LOCKED;
+
+ if (pfsvplock & PROCFS_WANT) {
+ pfsvplock &= ~PROCFS_WANT;
+ wakeup((caddr_t) &pfsvplock);
+ }
+
+ return (error);
+}
+
+int
+procfs_freevp(vp)
+ struct vnode *vp;
+{
+ struct pfsnode **pfspp;
+ struct pfsnode *pfs = VTOPFS(vp);
+
+ for (pfspp = &pfshead; *pfspp != 0; pfspp = &(*pfspp)->pfs_next) {
+ if (*pfspp == pfs) {
+ *pfspp = pfs->pfs_next;
+ break;
+ }
+ }
+
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = 0;
+ return (0);
+}
+
+int
+procfs_rw(ap)
+ struct vop_read_args *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct uio *uio = ap->a_uio;
+ struct proc *curp = uio->uio_procp;
+ struct pfsnode *pfs = VTOPFS(vp);
+ struct proc *p;
+
+ p = PFIND(pfs->pfs_pid);
+ if (p == 0)
+ return (EINVAL);
+
+ switch (pfs->pfs_type) {
+ case Pnote:
+ case Pnotepg:
+ return (procfs_donote(curp, p, pfs, uio));
+
+ case Pregs:
+ return (procfs_doregs(curp, p, pfs, uio));
+
+ case Pfpregs:
+ return (procfs_dofpregs(curp, p, pfs, uio));
+
+ case Pctl:
+ return (procfs_doctl(curp, p, pfs, uio));
+
+ case Pstatus:
+ return (procfs_dostatus(curp, p, pfs, uio));
+
+ case Pmem:
+ return (procfs_domem(curp, p, pfs, uio));
+
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
+/*
+ * Get a string from userland into (buf). Strip a trailing
+ * nl character (to allow easy access from the shell).
+ * The buffer should be *buflenp + 1 chars long. vfs_getuserstr
+ * will automatically add a nul char at the end.
+ *
+ * Returns 0 on success or the following errors
+ *
+ * EINVAL: file offset is non-zero.
+ * EMSGSIZE: message is longer than kernel buffer
+ * EFAULT: user i/o buffer is not addressable
+ */
+int
+vfs_getuserstr(uio, buf, buflenp)
+ struct uio *uio;
+ char *buf;
+ int *buflenp;
+{
+ int xlen;
+ int error;
+
+ if (uio->uio_offset != 0)
+ return (EINVAL);
+
+ xlen = *buflenp;
+
+ /* must be able to read the whole string in one go */
+ if (xlen < uio->uio_resid)
+ return (EMSGSIZE);
+ xlen = uio->uio_resid;
+
+ error = uiomove(buf, xlen, uio);
+ if (error)
+ return (error);
+
+ /* allow multiple writes without seeks */
+ uio->uio_offset = 0;
+
+ /* cleanup string and remove trailing newline */
+ buf[xlen] = '\0';
+ xlen = strlen(buf);
+ if (xlen > 0 && buf[xlen-1] == '\n')
+ buf[--xlen] = '\0';
+ *buflenp = xlen;
+
+ return (0);
+}
+
+vfs_namemap_t *
+vfs_findname(nm, buf, buflen)
+ vfs_namemap_t *nm;
+ char *buf;
+ int buflen;
+{
+ for (; nm->nm_name; nm++)
+ if (bcmp(buf, (char *) nm->nm_name, buflen+1) == 0)
+ return (nm);
+
+ return (0);
+}
diff --git a/sys/miscfs/procfs/procfs_vfsops.c b/sys/miscfs/procfs/procfs_vfsops.c
new file mode 100644
index 0000000..67dc31f
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_vfsops.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_vfsops.c 8.4 (Berkeley) 1/21/94
+ *
+ * From:
+ * $Id: procfs_vfsops.c,v 3.1 1993/12/15 09:40:17 jsp Exp $
+ */
+
+/*
+ * procfs VFS interface
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/syslog.h>
+#include <sys/mount.h>
+#include <sys/signalvar.h>
+#include <sys/vnode.h>
+#include <miscfs/procfs/procfs.h>
+#include <vm/vm.h> /* for PAGE_SIZE */
+
+/*
+ * VFS Operations.
+ *
+ * mount system call
+ */
+/* ARGSUSED */
+int
+procfs_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ u_int size;
+
+ if (UIO_MX & (UIO_MX-1)) {
+ log(LOG_ERR, "procfs: invalid directory entry size");
+ return (EINVAL);
+ }
+
+ if (mp->mnt_flag & MNT_UPDATE)
+ return (EOPNOTSUPP);
+
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_data = 0;
+ getnewfsid(mp, MOUNT_PROCFS);
+
+ (void) copyinstr(path, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+
+ size = sizeof("procfs") - 1;
+ bcopy("procfs", mp->mnt_stat.f_mntfromname, size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+
+ return (0);
+}
+
+/*
+ * unmount system call
+ */
+int
+procfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ int error;
+ extern int doforce;
+ int flags = 0;
+
+ if (mntflags & MNT_FORCE) {
+ /* procfs can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ if (error = vflush(mp, 0, flags))
+ return (error);
+
+ return (0);
+}
+
+int
+procfs_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct pfsnode *pfs;
+ struct vnode *vp;
+ int error;
+
+ error = procfs_allocvp(mp, &vp, (pid_t) 0, Proot);
+ if (error)
+ return (error);
+
+ vp->v_type = VDIR;
+ vp->v_flag = VROOT;
+ pfs = VTOPFS(vp);
+
+ *vpp = vp;
+ return (0);
+}
+
+/*
+ */
+/* ARGSUSED */
+int
+procfs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+
+ return (0);
+}
+
+/*
+ * Get file system statistics.
+ */
+int
+procfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ sbp->f_type = MOUNT_PROCFS;
+ sbp->f_bsize = PAGE_SIZE;
+ sbp->f_iosize = PAGE_SIZE;
+ sbp->f_blocks = 1; /* avoid divide by zero in some df's */
+ sbp->f_bfree = 0;
+ sbp->f_bavail = 0;
+ sbp->f_files = maxproc; /* approx */
+ sbp->f_ffree = maxproc - nprocs; /* approx */
+
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+
+ return (0);
+}
+
+
+int
+procfs_quotactl(mp, cmds, uid, arg, p)
+ struct mount *mp;
+ int cmds;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+procfs_sync(mp, waitfor)
+ struct mount *mp;
+ int waitfor;
+{
+
+ return (0);
+}
+
+int
+procfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+procfs_fhtovp(mp, fhp, vpp)
+ struct mount *mp;
+ struct fid *fhp;
+ struct vnode **vpp;
+{
+
+ return (EINVAL);
+}
+
+int
+procfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return EINVAL;
+}
+
+int
+procfs_init()
+{
+
+ return (0);
+}
+
+struct vfsops procfs_vfsops = {
+ procfs_mount,
+ procfs_start,
+ procfs_unmount,
+ procfs_root,
+ procfs_quotactl,
+ procfs_statfs,
+ procfs_sync,
+ procfs_vget,
+ procfs_fhtovp,
+ procfs_vptofh,
+ procfs_init,
+};
diff --git a/sys/miscfs/procfs/procfs_vnops.c b/sys/miscfs/procfs/procfs_vnops.c
new file mode 100644
index 0000000..b6c6e6f
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_vnops.c
@@ -0,0 +1,830 @@
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)procfs_vnops.c 8.6 (Berkeley) 2/7/94
+ *
+ * From:
+ * $Id: procfs_vnops.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+/*
+ * procfs vnode interface
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/resourcevar.h>
+#include <miscfs/procfs/procfs.h>
+#include <vm/vm.h> /* for PAGE_SIZE */
+
+/*
+ * Vnode Operations.
+ *
+ */
+
+/*
+ * This is a list of the valid names in the
+ * process-specific sub-directories. It is
+ * used in procfs_lookup and procfs_readdir
+ */
+static struct pfsnames {
+ u_short d_namlen;
+ char d_name[PROCFS_NAMELEN];
+ pfstype d_pfstype;
+} procent[] = {
+#define N(s) sizeof(s)-1, s
+ /* namlen, nam, type */
+ { N("file"), Pfile },
+ { N("mem"), Pmem },
+ { N("regs"), Pregs },
+ { N("fpregs"), Pfpregs },
+ { N("ctl"), Pctl },
+ { N("status"), Pstatus },
+ { N("note"), Pnote },
+ { N("notepg"), Pnotepg },
+#undef N
+};
+#define Nprocent (sizeof(procent)/sizeof(procent[0]))
+
+static pid_t atopid __P((const char *, u_int));
+
+/*
+ * set things up for doing i/o on
+ * the pfsnode (vp). (vp) is locked
+ * on entry, and should be left locked
+ * on exit.
+ *
+ * for procfs we don't need to do anything
+ * in particular for i/o. all that is done
+ * is to support exclusive open on process
+ * memory images.
+ */
+int
+procfs_open(ap)
+ struct vop_open_args *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+
+ switch (pfs->pfs_type) {
+ case Pmem:
+ if (PFIND(pfs->pfs_pid) == 0)
+ return (ENOENT); /* was ESRCH, jsp */
+
+ if ((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL) ||
+ (pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE))
+ return (EBUSY);
+
+
+ if (ap->a_mode & FWRITE)
+ pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL);
+
+ return (0);
+
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * close the pfsnode (vp) after doing i/o.
+ * (vp) is not locked on entry or exit.
+ *
+ * nothing to do for procfs other than undo
+ * any exclusive open flag (see _open above).
+ */
+int
+procfs_close(ap)
+ struct vop_close_args *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+
+ switch (pfs->pfs_type) {
+ case Pmem:
+ if ((ap->a_fflag & FWRITE) && (pfs->pfs_flags & O_EXCL))
+ pfs->pfs_flags &= ~(FWRITE|O_EXCL);
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * do an ioctl operation on pfsnode (vp).
+ * (vp) is not locked on entry or exit.
+ */
+int
+procfs_ioctl(ap)
+ struct vop_ioctl_args *ap;
+{
+
+ return (ENOTTY);
+}
+
+/*
+ * do block mapping for pfsnode (vp).
+ * since we don't use the buffer cache
+ * for procfs this function should never
+ * be called. in any case, it's not clear
+ * what part of the kernel ever makes use
+ * of this function. for sanity, this is the
+ * usual no-op bmap, although returning
+ * (EIO) would be a reasonable alternative.
+ */
+int
+procfs_bmap(ap)
+ struct vop_bmap_args *ap;
+{
+
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = ap->a_vp;
+ if (ap->a_bnp != NULL)
+ *ap->a_bnp = ap->a_bn;
+ return (0);
+}
+
+/*
+ * _inactive is called when the pfsnode
+ * is vrele'd and the reference count goes
+ * to zero. (vp) will be on the vnode free
+ * list, so to get it back vget() must be
+ * used.
+ *
+ * for procfs, check if the process is still
+ * alive and if it isn't then just throw away
+ * the vnode by calling vgone(). this may
+ * be overkill and a waste of time since the
+ * chances are that the process will still be
+ * there and PFIND is not free.
+ *
+ * (vp) is not locked on entry or exit.
+ */
+int
+procfs_inactive(ap)
+ struct vop_inactive_args *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+
+ if (PFIND(pfs->pfs_pid) == 0)
+ vgone(ap->a_vp);
+
+ return (0);
+}
+
+/*
+ * _reclaim is called when getnewvnode()
+ * wants to make use of an entry on the vnode
+ * free list. at this time the filesystem needs
+ * to free any private data and remove the node
+ * from any private lists.
+ */
+int
+procfs_reclaim(ap)
+ struct vop_reclaim_args *ap;
+{
+ int error;
+
+ error = procfs_freevp(ap->a_vp);
+ return (error);
+}
+
+/*
+ * Return POSIX pathconf information applicable to special devices.
+ */
+int
+procfs_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_MAX_CANON:
+ *ap->a_retval = MAX_CANON;
+ return (0);
+ case _PC_MAX_INPUT:
+ *ap->a_retval = MAX_INPUT;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ case _PC_VDISABLE:
+ *ap->a_retval = _POSIX_VDISABLE;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * _print is used for debugging.
+ * just print a readable description
+ * of (vp).
+ */
+int
+procfs_print(ap)
+ struct vop_print_args *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+
+ printf("tag VT_PROCFS, pid %d, mode %x, flags %x\n",
+ pfs->pfs_pid,
+ pfs->pfs_mode, pfs->pfs_flags);
+ return (0);
+}
+
+/*
+ * _abortop is called when operations such as
+ * rename and create fail. this entry is responsible
+ * for undoing any side-effects caused by the lookup.
+ * this will always include freeing the pathname buffer.
+ */
+int
+procfs_abortop(ap)
+ struct vop_abortop_args *ap;
+{
+
+ if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
+ FREE(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ return (0);
+}
+
+/*
+ * generic entry point for unsupported operations
+ */
+int
+procfs_badop()
+{
+
+ return (EIO);
+}
+
+/*
+ * Invent attributes for pfsnode (vp) and store
+ * them in (vap).
+ * Directories lengths are returned as zero since
+ * any real length would require the genuine size
+ * to be computed, and nothing cares anyway.
+ *
+ * this is relatively minimal for procfs.
+ */
+int
+procfs_getattr(ap)
+ struct vop_getattr_args *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+ struct vattr *vap = ap->a_vap;
+ struct proc *procp;
+ int error;
+
+ /* first check the process still exists */
+ switch (pfs->pfs_type) {
+ case Proot:
+ procp = 0;
+ break;
+
+ default:
+ procp = PFIND(pfs->pfs_pid);
+ if (procp == 0)
+ return (ENOENT);
+ }
+
+ error = 0;
+
+ /* start by zeroing out the attributes */
+ VATTR_NULL(vap);
+
+ /* next do all the common fields */
+ vap->va_type = ap->a_vp->v_type;
+ vap->va_mode = pfs->pfs_mode;
+ vap->va_fileid = pfs->pfs_fileno;
+ vap->va_flags = 0;
+ vap->va_blocksize = PAGE_SIZE;
+ vap->va_bytes = vap->va_size = 0;
+
+ /*
+ * If the process has exercised some setuid or setgid
+ * privilege, then rip away read/write permission so
+ * that only root can gain access.
+ */
+ switch (pfs->pfs_type) {
+ case Pregs:
+ case Pfpregs:
+ case Pmem:
+ if (procp->p_flag & P_SUGID)
+ vap->va_mode &= ~((VREAD|VWRITE)|
+ ((VREAD|VWRITE)>>3)|
+ ((VREAD|VWRITE)>>6));
+ break;
+ }
+
+ /*
+ * Make all times be current TOD.
+ * It would be possible to get the process start
+ * time from the p_stat structure, but there's
+ * no "file creation" time stamp anyway, and the
+ * p_stat structure is not addressible if u. gets
+ * swapped out for that process.
+ */
+ microtime(&vap->va_ctime);
+ vap->va_atime = vap->va_mtime = vap->va_ctime;
+
+ /*
+ * now do the object specific fields
+ *
+ * The size could be set from struct reg, but it's hardly
+ * worth the trouble, and it puts some (potentially) machine
+ * dependent data into this machine-independent code. If it
+ * becomes important then this function should break out into
+ * a per-file stat function in the corresponding .c file.
+ */
+
+ switch (pfs->pfs_type) {
+ case Proot:
+ vap->va_nlink = 2;
+ vap->va_uid = 0;
+ vap->va_gid = 0;
+ break;
+
+ case Pproc:
+ vap->va_nlink = 2;
+ vap->va_uid = procp->p_ucred->cr_uid;
+ vap->va_gid = procp->p_ucred->cr_gid;
+ break;
+
+ case Pfile:
+ error = EOPNOTSUPP;
+ break;
+
+ case Pmem:
+ vap->va_nlink = 1;
+ vap->va_bytes = vap->va_size =
+ ctob(procp->p_vmspace->vm_tsize +
+ procp->p_vmspace->vm_dsize +
+ procp->p_vmspace->vm_ssize);
+ vap->va_uid = procp->p_ucred->cr_uid;
+ vap->va_gid = procp->p_ucred->cr_gid;
+ break;
+
+ case Pregs:
+ case Pfpregs:
+ case Pctl:
+ case Pstatus:
+ case Pnote:
+ case Pnotepg:
+ vap->va_nlink = 1;
+ vap->va_uid = procp->p_ucred->cr_uid;
+ vap->va_gid = procp->p_ucred->cr_gid;
+ break;
+
+ default:
+ panic("procfs_getattr");
+ }
+
+ return (error);
+}
+
+int
+procfs_setattr(ap)
+ struct vop_setattr_args *ap;
+{
+ /*
+ * just fake out attribute setting
+ * it's not good to generate an error
+ * return, otherwise things like creat()
+ * will fail when they try to set the
+ * file length to 0. worse, this means
+ * that echo $note > /proc/$pid/note will fail.
+ */
+
+ return (0);
+}
+
+/*
+ * implement access checking.
+ *
+ * something very similar to this code is duplicated
+ * throughout the 4bsd kernel and should be moved
+ * into kern/vfs_subr.c sometime.
+ *
+ * actually, the check for super-user is slightly
+ * broken since it will allow read access to write-only
+ * objects. this doesn't cause any particular trouble
+ * but does mean that the i/o entry points need to check
+ * that the operation really does make sense.
+ */
+int
+procfs_access(ap)
+ struct vop_access_args *ap;
+{
+ struct vattr *vap;
+ struct vattr vattr;
+ int error;
+
+ /*
+ * If you're the super-user,
+ * you always get access.
+ */
+ if (ap->a_cred->cr_uid == (uid_t) 0)
+ return (0);
+ vap = &vattr;
+ if (error = VOP_GETATTR(ap->a_vp, vap, ap->a_cred, ap->a_p))
+ return (error);
+
+ /*
+ * Access check is based on only one of owner, group, public.
+ * If not owner, then check group. If not a member of the
+ * group, then check public access.
+ */
+ if (ap->a_cred->cr_uid != vap->va_uid) {
+ gid_t *gp;
+ int i;
+
+ (ap->a_mode) >>= 3;
+ gp = ap->a_cred->cr_groups;
+ for (i = 0; i < ap->a_cred->cr_ngroups; i++, gp++)
+ if (vap->va_gid == *gp)
+ goto found;
+ ap->a_mode >>= 3;
+found:
+ ;
+ }
+
+ if ((vap->va_mode & ap->a_mode) == ap->a_mode)
+ return (0);
+
+ return (EACCES);
+}
+
+/*
+ * lookup. this is incredibly complicated in the
+ * general case, however for most pseudo-filesystems
+ * very little needs to be done.
+ *
+ * unless you want to get a migraine, just make sure your
+ * filesystem doesn't do any locking of its own. otherwise
+ * read and inwardly digest ufs_lookup().
+ */
+int
+procfs_lookup(ap)
+ struct vop_lookup_args *ap;
+{
+ struct componentname *cnp = ap->a_cnp;
+ struct vnode **vpp = ap->a_vpp;
+ struct vnode *dvp = ap->a_dvp;
+ char *pname = cnp->cn_nameptr;
+ int error = 0;
+ pid_t pid;
+ struct vnode *nvp;
+ struct pfsnode *pfs;
+ struct proc *procp;
+ pfstype pfs_type;
+ int i;
+
+ if (cnp->cn_namelen == 1 && *pname == '.') {
+ *vpp = dvp;
+ VREF(dvp);
+ /*VOP_LOCK(dvp);*/
+ return (0);
+ }
+
+ *vpp = NULL;
+
+ pfs = VTOPFS(dvp);
+ switch (pfs->pfs_type) {
+ case Proot:
+ if (cnp->cn_flags & ISDOTDOT)
+ return (EIO);
+
+ if (CNEQ(cnp, "curproc", 7))
+ pid = cnp->cn_proc->p_pid;
+ else
+ pid = atopid(pname, cnp->cn_namelen);
+ if (pid == NO_PID)
+ return (ENOENT);
+
+ procp = PFIND(pid);
+ if (procp == 0)
+ return (ENOENT);
+
+ error = procfs_allocvp(dvp->v_mount, &nvp, pid, Pproc);
+ if (error)
+ return (error);
+
+ nvp->v_type = VDIR;
+ pfs = VTOPFS(nvp);
+
+ *vpp = nvp;
+ return (0);
+
+ case Pproc:
+ if (cnp->cn_flags & ISDOTDOT) {
+ error = procfs_root(dvp->v_mount, vpp);
+ return (error);
+ }
+
+ procp = PFIND(pfs->pfs_pid);
+ if (procp == 0)
+ return (ENOENT);
+
+ for (i = 0; i < Nprocent; i++) {
+ struct pfsnames *dp = &procent[i];
+
+ if (cnp->cn_namelen == dp->d_namlen &&
+ bcmp(pname, dp->d_name, dp->d_namlen) == 0) {
+ pfs_type = dp->d_pfstype;
+ goto found;
+ }
+ }
+ return (ENOENT);
+
+ found:
+ if (pfs_type == Pfile) {
+ nvp = procfs_findtextvp(procp);
+ if (nvp) {
+ VREF(nvp);
+ VOP_LOCK(nvp);
+ } else {
+ error = ENXIO;
+ }
+ } else {
+ error = procfs_allocvp(dvp->v_mount, &nvp,
+ pfs->pfs_pid, pfs_type);
+ if (error)
+ return (error);
+
+ nvp->v_type = VREG;
+ pfs = VTOPFS(nvp);
+ }
+ *vpp = nvp;
+ return (error);
+
+ default:
+ return (ENOTDIR);
+ }
+}
+
+/*
+ * readdir returns directory entries from pfsnode (vp).
+ *
+ * the strategy here with procfs is to generate a single
+ * directory entry at a time (struct pfsdent) and then
+ * copy that out to userland using uiomove. a more efficent
+ * though more complex implementation, would try to minimize
+ * the number of calls to uiomove(). for procfs, this is
+ * hardly worth the added code complexity.
+ *
+ * this should just be done through read()
+ */
+int
+procfs_readdir(ap)
+ struct vop_readdir_args *ap;
+{
+ struct uio *uio = ap->a_uio;
+ struct pfsdent d;
+ struct pfsdent *dp = &d;
+ struct pfsnode *pfs;
+ int error;
+ int count;
+ int i;
+
+ pfs = VTOPFS(ap->a_vp);
+
+ if (uio->uio_resid < UIO_MX)
+ return (EINVAL);
+ if (uio->uio_offset & (UIO_MX-1))
+ return (EINVAL);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+
+ error = 0;
+ count = 0;
+ i = uio->uio_offset / UIO_MX;
+
+ switch (pfs->pfs_type) {
+ /*
+ * this is for the process-specific sub-directories.
+ * all that is needed to is copy out all the entries
+ * from the procent[] table (top of this file).
+ */
+ case Pproc: {
+ while (uio->uio_resid >= UIO_MX) {
+ struct pfsnames *dt;
+
+ if (i >= Nprocent)
+ break;
+
+ dt = &procent[i];
+
+ dp->d_reclen = UIO_MX;
+ dp->d_fileno = PROCFS_FILENO(pfs->pfs_pid, dt->d_pfstype);
+ dp->d_type = DT_REG;
+ dp->d_namlen = dt->d_namlen;
+ bcopy(dt->d_name, dp->d_name, sizeof(dt->d_name)-1);
+ error = uiomove((caddr_t) dp, UIO_MX, uio);
+ if (error)
+ break;
+ count += UIO_MX;
+ i++;
+ }
+
+ break;
+
+ }
+
+ /*
+ * this is for the root of the procfs filesystem
+ * what is needed is a special entry for "curproc"
+ * followed by an entry for each process on allproc
+#ifdef PROCFS_ZOMBIE
+ * and zombproc.
+#endif
+ */
+
+ case Proot: {
+ int pcnt;
+#ifdef PROCFS_ZOMBIE
+ int doingzomb = 0;
+#endif
+ volatile struct proc *p;
+
+ p = allproc;
+
+#define PROCFS_XFILES 1 /* number of other entries, like "curproc" */
+ pcnt = PROCFS_XFILES;
+
+ while (p && uio->uio_resid >= UIO_MX) {
+ bzero((char *) dp, UIO_MX);
+ dp->d_type = DT_DIR;
+ dp->d_reclen = UIO_MX;
+
+ switch (i) {
+ case 0:
+ /* ship out entry for "curproc" */
+ dp->d_fileno = PROCFS_FILENO(PID_MAX+1, Pproc);
+ dp->d_namlen = sprintf(dp->d_name, "curproc");
+ break;
+
+ default:
+ if (pcnt >= i) {
+ dp->d_fileno = PROCFS_FILENO(p->p_pid, Pproc);
+ dp->d_namlen = sprintf(dp->d_name, "%ld", (long) p->p_pid);
+ }
+
+ p = p->p_next;
+
+#ifdef PROCFS_ZOMBIE
+ if (p == 0 && doingzomb == 0) {
+ doingzomb = 1;
+ p = zombproc;
+ }
+#endif
+
+ if (pcnt++ < i)
+ continue;
+
+ break;
+ }
+ error = uiomove((caddr_t) dp, UIO_MX, uio);
+ if (error)
+ break;
+ count += UIO_MX;
+ i++;
+ }
+
+ break;
+
+ }
+
+ default:
+ error = ENOTDIR;
+ break;
+ }
+
+ uio->uio_offset = i * UIO_MX;
+
+ return (error);
+}
+
+/*
+ * convert decimal ascii to pid_t
+ */
+static pid_t
+atopid(b, len)
+ const char *b;
+ u_int len;
+{
+ pid_t p = 0;
+
+ while (len--) {
+ char c = *b++;
+ if (c < '0' || c > '9')
+ return (NO_PID);
+ p = 10 * p + (c - '0');
+ if (p > PID_MAX)
+ return (NO_PID);
+ }
+
+ return (p);
+}
+
+/*
+ * procfs vnode operations.
+ */
+int (**procfs_vnodeop_p)();
+struct vnodeopv_entry_desc procfs_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, procfs_lookup }, /* lookup */
+ { &vop_create_desc, procfs_create }, /* create */
+ { &vop_mknod_desc, procfs_mknod }, /* mknod */
+ { &vop_open_desc, procfs_open }, /* open */
+ { &vop_close_desc, procfs_close }, /* close */
+ { &vop_access_desc, procfs_access }, /* access */
+ { &vop_getattr_desc, procfs_getattr }, /* getattr */
+ { &vop_setattr_desc, procfs_setattr }, /* setattr */
+ { &vop_read_desc, procfs_read }, /* read */
+ { &vop_write_desc, procfs_write }, /* write */
+ { &vop_ioctl_desc, procfs_ioctl }, /* ioctl */
+ { &vop_select_desc, procfs_select }, /* select */
+ { &vop_mmap_desc, procfs_mmap }, /* mmap */
+ { &vop_fsync_desc, procfs_fsync }, /* fsync */
+ { &vop_seek_desc, procfs_seek }, /* seek */
+ { &vop_remove_desc, procfs_remove }, /* remove */
+ { &vop_link_desc, procfs_link }, /* link */
+ { &vop_rename_desc, procfs_rename }, /* rename */
+ { &vop_mkdir_desc, procfs_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, procfs_rmdir }, /* rmdir */
+ { &vop_symlink_desc, procfs_symlink }, /* symlink */
+ { &vop_readdir_desc, procfs_readdir }, /* readdir */
+ { &vop_readlink_desc, procfs_readlink }, /* readlink */
+ { &vop_abortop_desc, procfs_abortop }, /* abortop */
+ { &vop_inactive_desc, procfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, procfs_reclaim }, /* reclaim */
+ { &vop_lock_desc, procfs_lock }, /* lock */
+ { &vop_unlock_desc, procfs_unlock }, /* unlock */
+ { &vop_bmap_desc, procfs_bmap }, /* bmap */
+ { &vop_strategy_desc, procfs_strategy }, /* strategy */
+ { &vop_print_desc, procfs_print }, /* print */
+ { &vop_islocked_desc, procfs_islocked }, /* islocked */
+ { &vop_pathconf_desc, procfs_pathconf }, /* pathconf */
+ { &vop_advlock_desc, procfs_advlock }, /* advlock */
+ { &vop_blkatoff_desc, procfs_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, procfs_valloc }, /* valloc */
+ { &vop_vfree_desc, procfs_vfree }, /* vfree */
+ { &vop_truncate_desc, procfs_truncate }, /* truncate */
+ { &vop_update_desc, procfs_update }, /* update */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc procfs_vnodeop_opv_desc =
+ { &procfs_vnodeop_p, procfs_vnodeop_entries };
diff --git a/sys/miscfs/specfs/spec_vnops.c b/sys/miscfs/specfs/spec_vnops.c
new file mode 100644
index 0000000..55b5dd8
--- /dev/null
+++ b/sys/miscfs/specfs/spec_vnops.c
@@ -0,0 +1,705 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)spec_vnops.c 8.6 (Berkeley) 4/9/94
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/buf.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/disklabel.h>
+#include <miscfs/specfs/specdev.h>
+
+/* symbolic sleep message strings for devices */
+char devopn[] = "devopn";
+char devio[] = "devio";
+char devwait[] = "devwait";
+char devin[] = "devin";
+char devout[] = "devout";
+char devioc[] = "devioc";
+char devcls[] = "devcls";
+
+int (**spec_vnodeop_p)();
+struct vnodeopv_entry_desc spec_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, spec_lookup }, /* lookup */
+ { &vop_create_desc, spec_create }, /* create */
+ { &vop_mknod_desc, spec_mknod }, /* mknod */
+ { &vop_open_desc, spec_open }, /* open */
+ { &vop_close_desc, spec_close }, /* close */
+ { &vop_access_desc, spec_access }, /* access */
+ { &vop_getattr_desc, spec_getattr }, /* getattr */
+ { &vop_setattr_desc, spec_setattr }, /* setattr */
+ { &vop_read_desc, spec_read }, /* read */
+ { &vop_write_desc, spec_write }, /* write */
+ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */
+ { &vop_select_desc, spec_select }, /* select */
+ { &vop_mmap_desc, spec_mmap }, /* mmap */
+ { &vop_fsync_desc, spec_fsync }, /* fsync */
+ { &vop_seek_desc, spec_seek }, /* seek */
+ { &vop_remove_desc, spec_remove }, /* remove */
+ { &vop_link_desc, spec_link }, /* link */
+ { &vop_rename_desc, spec_rename }, /* rename */
+ { &vop_mkdir_desc, spec_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, spec_rmdir }, /* rmdir */
+ { &vop_symlink_desc, spec_symlink }, /* symlink */
+ { &vop_readdir_desc, spec_readdir }, /* readdir */
+ { &vop_readlink_desc, spec_readlink }, /* readlink */
+ { &vop_abortop_desc, spec_abortop }, /* abortop */
+ { &vop_inactive_desc, spec_inactive }, /* inactive */
+ { &vop_reclaim_desc, spec_reclaim }, /* reclaim */
+ { &vop_lock_desc, spec_lock }, /* lock */
+ { &vop_unlock_desc, spec_unlock }, /* unlock */
+ { &vop_bmap_desc, spec_bmap }, /* bmap */
+ { &vop_strategy_desc, spec_strategy }, /* strategy */
+ { &vop_print_desc, spec_print }, /* print */
+ { &vop_islocked_desc, spec_islocked }, /* islocked */
+ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */
+ { &vop_advlock_desc, spec_advlock }, /* advlock */
+ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, spec_valloc }, /* valloc */
+ { &vop_vfree_desc, spec_vfree }, /* vfree */
+ { &vop_truncate_desc, spec_truncate }, /* truncate */
+ { &vop_update_desc, spec_update }, /* update */
+ { &vop_bwrite_desc, spec_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc spec_vnodeop_opv_desc =
+ { &spec_vnodeop_p, spec_vnodeop_entries };
+
+/*
+ * Trivial lookup routine that always fails.
+ */
+int
+spec_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+
+ *ap->a_vpp = NULL;
+ return (ENOTDIR);
+}
+
+/*
+ * Open a special file.
+ */
+/* ARGSUSED */
+int
+spec_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *bvp, *vp = ap->a_vp;
+ dev_t bdev, dev = (dev_t)vp->v_rdev;
+ register int maj = major(dev);
+ int error;
+
+ /*
+ * Don't allow open if fs is mounted -nodev.
+ */
+ if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV))
+ return (ENXIO);
+
+ switch (vp->v_type) {
+
+ case VCHR:
+ if ((u_int)maj >= nchrdev)
+ return (ENXIO);
+ if (ap->a_cred != FSCRED && (ap->a_mode & FWRITE)) {
+ /*
+ * When running in very secure mode, do not allow
+ * opens for writing of any disk character devices.
+ */
+ if (securelevel >= 2 && isdisk(dev, VCHR))
+ return (EPERM);
+ /*
+ * When running in secure mode, do not allow opens
+ * for writing of /dev/mem, /dev/kmem, or character
+ * devices whose corresponding block devices are
+ * currently mounted.
+ */
+ if (securelevel >= 1) {
+ if ((bdev = chrtoblk(dev)) != NODEV &&
+ vfinddev(bdev, VBLK, &bvp) &&
+ bvp->v_usecount > 0 &&
+ (error = vfs_mountedon(bvp)))
+ return (error);
+ if (iskmemdev(dev))
+ return (EPERM);
+ }
+ }
+ VOP_UNLOCK(vp);
+ error = (*cdevsw[maj].d_open)(dev, ap->a_mode, S_IFCHR, ap->a_p);
+ VOP_LOCK(vp);
+ return (error);
+
+ case VBLK:
+ if ((u_int)maj >= nblkdev)
+ return (ENXIO);
+ /*
+ * When running in very secure mode, do not allow
+ * opens for writing of any disk block devices.
+ */
+ if (securelevel >= 2 && ap->a_cred != FSCRED &&
+ (ap->a_mode & FWRITE) && isdisk(dev, VBLK))
+ return (EPERM);
+ /*
+ * Do not allow opens of block devices that are
+ * currently mounted.
+ */
+ if (error = vfs_mountedon(vp))
+ return (error);
+ return ((*bdevsw[maj].d_open)(dev, ap->a_mode, S_IFBLK, ap->a_p));
+ }
+ return (0);
+}
+
+/*
+ * Vnode op for read
+ */
+/* ARGSUSED */
+int
+spec_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct uio *uio = ap->a_uio;
+ struct proc *p = uio->uio_procp;
+ struct buf *bp;
+ daddr_t bn, nextbn;
+ long bsize, bscale;
+ struct partinfo dpart;
+ int n, on, majordev, (*ioctl)();
+ int error = 0;
+ dev_t dev;
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_READ)
+ panic("spec_read mode");
+ if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
+ panic("spec_read proc");
+#endif
+ if (uio->uio_resid == 0)
+ return (0);
+
+ switch (vp->v_type) {
+
+ case VCHR:
+ VOP_UNLOCK(vp);
+ error = (*cdevsw[major(vp->v_rdev)].d_read)
+ (vp->v_rdev, uio, ap->a_ioflag);
+ VOP_LOCK(vp);
+ return (error);
+
+ case VBLK:
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+ bsize = BLKDEV_IOSIZE;
+ dev = vp->v_rdev;
+ if ((majordev = major(dev)) < nblkdev &&
+ (ioctl = bdevsw[majordev].d_ioctl) != NULL &&
+ (*ioctl)(dev, DIOCGPART, (caddr_t)&dpart, FREAD, p) == 0 &&
+ dpart.part->p_fstype == FS_BSDFFS &&
+ dpart.part->p_frag != 0 && dpart.part->p_fsize != 0)
+ bsize = dpart.part->p_frag * dpart.part->p_fsize;
+ bscale = bsize / DEV_BSIZE;
+ do {
+ bn = (uio->uio_offset / DEV_BSIZE) &~ (bscale - 1);
+ on = uio->uio_offset % bsize;
+ n = min((unsigned)(bsize - on), uio->uio_resid);
+ if (vp->v_lastr + bscale == bn) {
+ nextbn = bn + bscale;
+ error = breadn(vp, bn, (int)bsize, &nextbn,
+ (int *)&bsize, 1, NOCRED, &bp);
+ } else
+ error = bread(vp, bn, (int)bsize, NOCRED, &bp);
+ vp->v_lastr = bn;
+ n = min(n, bsize - bp->b_resid);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ error = uiomove((char *)bp->b_data + on, n, uio);
+ if (n + on == bsize)
+ bp->b_flags |= B_AGE;
+ brelse(bp);
+ } while (error == 0 && uio->uio_resid > 0 && n != 0);
+ return (error);
+
+ default:
+ panic("spec_read type");
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Vnode op for write
+ */
+/* ARGSUSED */
+int
+spec_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct uio *uio = ap->a_uio;
+ struct proc *p = uio->uio_procp;
+ struct buf *bp;
+ daddr_t bn;
+ int bsize, blkmask;
+ struct partinfo dpart;
+ register int n, on;
+ int error = 0;
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_WRITE)
+ panic("spec_write mode");
+ if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
+ panic("spec_write proc");
+#endif
+
+ switch (vp->v_type) {
+
+ case VCHR:
+ VOP_UNLOCK(vp);
+ error = (*cdevsw[major(vp->v_rdev)].d_write)
+ (vp->v_rdev, uio, ap->a_ioflag);
+ VOP_LOCK(vp);
+ return (error);
+
+ case VBLK:
+ if (uio->uio_resid == 0)
+ return (0);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+ bsize = BLKDEV_IOSIZE;
+ if ((*bdevsw[major(vp->v_rdev)].d_ioctl)(vp->v_rdev, DIOCGPART,
+ (caddr_t)&dpart, FREAD, p) == 0) {
+ if (dpart.part->p_fstype == FS_BSDFFS &&
+ dpart.part->p_frag != 0 && dpart.part->p_fsize != 0)
+ bsize = dpart.part->p_frag *
+ dpart.part->p_fsize;
+ }
+ blkmask = (bsize / DEV_BSIZE) - 1;
+ do {
+ bn = (uio->uio_offset / DEV_BSIZE) &~ blkmask;
+ on = uio->uio_offset % bsize;
+ n = min((unsigned)(bsize - on), uio->uio_resid);
+ if (n == bsize)
+ bp = getblk(vp, bn, bsize, 0, 0);
+ else
+ error = bread(vp, bn, bsize, NOCRED, &bp);
+ n = min(n, bsize - bp->b_resid);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ error = uiomove((char *)bp->b_data + on, n, uio);
+ if (n + on == bsize) {
+ bp->b_flags |= B_AGE;
+ bawrite(bp);
+ } else
+ bdwrite(bp);
+ } while (error == 0 && uio->uio_resid > 0 && n != 0);
+ return (error);
+
+ default:
+ panic("spec_write type");
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Device ioctl operation.
+ */
+/* ARGSUSED */
+int
+spec_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ dev_t dev = ap->a_vp->v_rdev;
+
+ switch (ap->a_vp->v_type) {
+
+ case VCHR:
+ return ((*cdevsw[major(dev)].d_ioctl)(dev, ap->a_command, ap->a_data,
+ ap->a_fflag, ap->a_p));
+
+ case VBLK:
+ if (ap->a_command == 0 && (int)ap->a_data == B_TAPE)
+ if (bdevsw[major(dev)].d_flags & B_TAPE)
+ return (0);
+ else
+ return (1);
+ return ((*bdevsw[major(dev)].d_ioctl)(dev, ap->a_command, ap->a_data,
+ ap->a_fflag, ap->a_p));
+
+ default:
+ panic("spec_ioctl");
+ /* NOTREACHED */
+ }
+}
+
+/* ARGSUSED */
+int
+spec_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register dev_t dev;
+
+ switch (ap->a_vp->v_type) {
+
+ default:
+ return (1); /* XXX */
+
+ case VCHR:
+ dev = ap->a_vp->v_rdev;
+ return (*cdevsw[major(dev)].d_select)(dev, ap->a_which, ap->a_p);
+ }
+}
+/*
+ * Synch buffers associated with a block device
+ */
+/* ARGSUSED */
+int
+spec_fsync(ap)
+ struct vop_fsync_args /* {
+ struct vnode *a_vp;
+ struct ucred *a_cred;
+ int a_waitfor;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct buf *bp;
+ struct buf *nbp;
+ int s;
+
+ if (vp->v_type == VCHR)
+ return (0);
+ /*
+ * Flush all dirty buffers associated with a block device.
+ */
+loop:
+ s = splbio();
+ for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
+ nbp = bp->b_vnbufs.le_next;
+ if ((bp->b_flags & B_BUSY))
+ continue;
+ if ((bp->b_flags & B_DELWRI) == 0)
+ panic("spec_fsync: not dirty");
+ bremfree(bp);
+ bp->b_flags |= B_BUSY;
+ splx(s);
+ bawrite(bp);
+ goto loop;
+ }
+ if (ap->a_waitfor == MNT_WAIT) {
+ while (vp->v_numoutput) {
+ vp->v_flag |= VBWAIT;
+ sleep((caddr_t)&vp->v_numoutput, PRIBIO + 1);
+ }
+#ifdef DIAGNOSTIC
+ if (vp->v_dirtyblkhd.lh_first) {
+ vprint("spec_fsync: dirty", vp);
+ goto loop;
+ }
+#endif
+ }
+ splx(s);
+ return (0);
+}
+
+/*
+ * Just call the device strategy routine
+ */
+int
+spec_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+
+ (*bdevsw[major(ap->a_bp->b_dev)].d_strategy)(ap->a_bp);
+ return (0);
+}
+
+/*
+ * This is a noop, simply returning what one has been given.
+ */
+int
+spec_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ } */ *ap;
+{
+
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = ap->a_vp;
+ if (ap->a_bnp != NULL)
+ *ap->a_bnp = ap->a_bn;
+ return (0);
+}
+
+/*
+ * At the moment we do not do any locking.
+ */
+/* ARGSUSED */
+int
+spec_lock(ap)
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/* ARGSUSED */
+int
+spec_unlock(ap)
+ struct vop_unlock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * Device close routine
+ */
+/* ARGSUSED */
+int
+spec_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ dev_t dev = vp->v_rdev;
+ int (*devclose) __P((dev_t, int, int, struct proc *));
+ int mode, error;
+
+ switch (vp->v_type) {
+
+ case VCHR:
+ /*
+ * Hack: a tty device that is a controlling terminal
+ * has a reference from the session structure.
+ * We cannot easily tell that a character device is
+ * a controlling terminal, unless it is the closing
+ * process' controlling terminal. In that case,
+ * if the reference count is 2 (this last descriptor
+ * plus the session), release the reference from the session.
+ */
+ if (vcount(vp) == 2 && ap->a_p &&
+ vp == ap->a_p->p_session->s_ttyvp) {
+ vrele(vp);
+ ap->a_p->p_session->s_ttyvp = NULL;
+ }
+ /*
+ * If the vnode is locked, then we are in the midst
+ * of forcably closing the device, otherwise we only
+ * close on last reference.
+ */
+ if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0)
+ return (0);
+ devclose = cdevsw[major(dev)].d_close;
+ mode = S_IFCHR;
+ break;
+
+ case VBLK:
+ /*
+ * On last close of a block device (that isn't mounted)
+ * we must invalidate any in core blocks, so that
+ * we can, for instance, change floppy disks.
+ */
+ if (error = vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 0, 0))
+ return (error);
+ /*
+ * We do not want to really close the device if it
+ * is still in use unless we are trying to close it
+ * forcibly. Since every use (buffer, vnode, swap, cmap)
+ * holds a reference to the vnode, and because we mark
+ * any other vnodes that alias this device, when the
+ * sum of the reference counts on all the aliased
+ * vnodes descends to one, we are on last close.
+ */
+ if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0)
+ return (0);
+ devclose = bdevsw[major(dev)].d_close;
+ mode = S_IFBLK;
+ break;
+
+ default:
+ panic("spec_close: not special");
+ }
+
+ return ((*devclose)(dev, ap->a_fflag, mode, ap->a_p));
+}
+
+/*
+ * Print out the contents of a special device vnode.
+ */
+int
+spec_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_NON, dev %d, %d\n", major(ap->a_vp->v_rdev),
+ minor(ap->a_vp->v_rdev));
+ return (0);
+}
+
+/*
+ * Return POSIX pathconf information applicable to special devices.
+ */
+int
+spec_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_MAX_CANON:
+ *ap->a_retval = MAX_CANON;
+ return (0);
+ case _PC_MAX_INPUT:
+ *ap->a_retval = MAX_INPUT;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ case _PC_VDISABLE:
+ *ap->a_retval = _POSIX_VDISABLE;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Special device advisory byte-level locks.
+ */
+/* ARGSUSED */
+int
+spec_advlock(ap)
+ struct vop_advlock_args /* {
+ struct vnode *a_vp;
+ caddr_t a_id;
+ int a_op;
+ struct flock *a_fl;
+ int a_flags;
+ } */ *ap;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * Special device failed operation
+ */
+int
+spec_ebadf()
+{
+
+ return (EBADF);
+}
+
+/*
+ * Special device bad operation
+ */
+int
+spec_badop()
+{
+
+ panic("spec_badop called");
+ /* NOTREACHED */
+}
diff --git a/sys/miscfs/specfs/specdev.h b/sys/miscfs/specfs/specdev.h
new file mode 100644
index 0000000..a13b66e
--- /dev/null
+++ b/sys/miscfs/specfs/specdev.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)specdev.h 8.2 (Berkeley) 2/2/94
+ */
+
+/*
+ * This structure defines the information maintained about
+ * special devices. It is allocated in checkalias and freed
+ * in vgone.
+ */
+struct specinfo {
+ struct vnode **si_hashchain;
+ struct vnode *si_specnext;
+ long si_flags;
+ dev_t si_rdev;
+};
+/*
+ * Exported shorthand
+ */
+#define v_rdev v_specinfo->si_rdev
+#define v_hashchain v_specinfo->si_hashchain
+#define v_specnext v_specinfo->si_specnext
+#define v_specflags v_specinfo->si_flags
+
+/*
+ * Flags for specinfo
+ */
+#define SI_MOUNTEDON 0x0001 /* block special device is mounted on */
+
+/*
+ * Special device management
+ */
+#define SPECHSZ 64
+#if ((SPECHSZ&(SPECHSZ-1)) == 0)
+#define SPECHASH(rdev) (((rdev>>5)+(rdev))&(SPECHSZ-1))
+#else
+#define SPECHASH(rdev) (((unsigned)((rdev>>5)+(rdev)))%SPECHSZ)
+#endif
+
+struct vnode *speclisth[SPECHSZ];
+
+/*
+ * Prototypes for special file operations on vnodes.
+ */
+extern int (**spec_vnodeop_p)();
+struct nameidata;
+struct componentname;
+struct ucred;
+struct flock;
+struct buf;
+struct uio;
+
+int spec_badop(),
+ spec_ebadf();
+
+int spec_lookup __P((struct vop_lookup_args *));
+#define spec_create ((int (*) __P((struct vop_create_args *)))spec_badop)
+#define spec_mknod ((int (*) __P((struct vop_mknod_args *)))spec_badop)
+int spec_open __P((struct vop_open_args *));
+int spec_close __P((struct vop_close_args *));
+#define spec_access ((int (*) __P((struct vop_access_args *)))spec_ebadf)
+#define spec_getattr ((int (*) __P((struct vop_getattr_args *)))spec_ebadf)
+#define spec_setattr ((int (*) __P((struct vop_setattr_args *)))spec_ebadf)
+int spec_read __P((struct vop_read_args *));
+int spec_write __P((struct vop_write_args *));
+int spec_ioctl __P((struct vop_ioctl_args *));
+int spec_select __P((struct vop_select_args *));
+#define spec_mmap ((int (*) __P((struct vop_mmap_args *)))spec_badop)
+int spec_fsync __P((struct vop_fsync_args *));
+#define spec_seek ((int (*) __P((struct vop_seek_args *)))spec_badop)
+#define spec_remove ((int (*) __P((struct vop_remove_args *)))spec_badop)
+#define spec_link ((int (*) __P((struct vop_link_args *)))spec_badop)
+#define spec_rename ((int (*) __P((struct vop_rename_args *)))spec_badop)
+#define spec_mkdir ((int (*) __P((struct vop_mkdir_args *)))spec_badop)
+#define spec_rmdir ((int (*) __P((struct vop_rmdir_args *)))spec_badop)
+#define spec_symlink ((int (*) __P((struct vop_symlink_args *)))spec_badop)
+#define spec_readdir ((int (*) __P((struct vop_readdir_args *)))spec_badop)
+#define spec_readlink ((int (*) __P((struct vop_readlink_args *)))spec_badop)
+#define spec_abortop ((int (*) __P((struct vop_abortop_args *)))spec_badop)
+#define spec_inactive ((int (*) __P((struct vop_inactive_args *)))nullop)
+#define spec_reclaim ((int (*) __P((struct vop_reclaim_args *)))nullop)
+int spec_lock __P((struct vop_lock_args *));
+int spec_unlock __P((struct vop_unlock_args *));
+int spec_bmap __P((struct vop_bmap_args *));
+int spec_strategy __P((struct vop_strategy_args *));
+int spec_print __P((struct vop_print_args *));
+#define spec_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+int spec_pathconf __P((struct vop_pathconf_args *));
+int spec_advlock __P((struct vop_advlock_args *));
+#define spec_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))spec_badop)
+#define spec_valloc ((int (*) __P((struct vop_valloc_args *)))spec_badop)
+#define spec_reallocblks \
+ ((int (*) __P((struct vop_reallocblks_args *)))spec_badop)
+#define spec_vfree ((int (*) __P((struct vop_vfree_args *)))spec_badop)
+#define spec_truncate ((int (*) __P((struct vop_truncate_args *)))nullop)
+#define spec_update ((int (*) __P((struct vop_update_args *)))nullop)
+#define spec_bwrite ((int (*) __P((struct vop_bwrite_args *)))nullop)
diff --git a/sys/miscfs/umapfs/umap.h b/sys/miscfs/umapfs/umap.h
new file mode 100644
index 0000000..9f4d1e7
--- /dev/null
+++ b/sys/miscfs/umapfs/umap.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * the UCLA Ficus project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)umap.h 8.3 (Berkeley) 1/21/94
+ *
+ * @(#)null_vnops.c 1.5 (Berkeley) 7/10/92
+ */
+
+#define MAPFILEENTRIES 64
+#define GMAPFILEENTRIES 16
+#define NOBODY 32767
+#define NULLGROUP 65534
+
+struct umap_args {
+ char *target; /* Target of loopback */
+ int nentries; /* # of entries in user map array */
+ int gnentries; /* # of entries in group map array */
+ u_long (*mapdata)[2]; /* pointer to array of user mappings */
+ u_long (*gmapdata)[2]; /* pointer to array of group mappings */
+};
+
+struct umap_mount {
+ struct mount *umapm_vfs;
+ struct vnode *umapm_rootvp; /* Reference to root umap_node */
+ int info_nentries; /* number of uid mappings */
+ int info_gnentries; /* number of gid mappings */
+ u_long info_mapdata[MAPFILEENTRIES][2]; /* mapping data for
+ user mapping in ficus */
+ u_long info_gmapdata[GMAPFILEENTRIES][2]; /*mapping data for
+ group mapping in ficus */
+};
+
+#ifdef KERNEL
+/*
+ * A cache of vnode references
+ */
+struct umap_node {
+ struct umap_node *umap_forw; /* Hash chain */
+ struct umap_node *umap_back;
+ struct vnode *umap_lowervp; /* Aliased vnode - VREFed once */
+ struct vnode *umap_vnode; /* Back pointer to vnode/umap_node */
+};
+
+extern int umap_node_create __P((struct mount *mp, struct vnode *target, struct vnode **vpp));
+extern u_long umap_reverse_findid __P((u_long id, u_long map[][2], int nentries));
+extern void umap_mapids __P((struct mount *v_mount, struct ucred *credp));
+
+#define MOUNTTOUMAPMOUNT(mp) ((struct umap_mount *)((mp)->mnt_data))
+#define VTOUMAP(vp) ((struct umap_node *)(vp)->v_data)
+#define UMAPTOV(xp) ((xp)->umap_vnode)
+#ifdef UMAPFS_DIAGNOSTIC
+extern struct vnode *umap_checkvp __P((struct vnode *vp, char *fil, int lno));
+#define UMAPVPTOLOWERVP(vp) umap_checkvp((vp), __FILE__, __LINE__)
+#else
+#define UMAPVPTOLOWERVP(vp) (VTOUMAP(vp)->umap_lowervp)
+#endif
+
+extern int (**umap_vnodeop_p)();
+extern struct vfsops umap_vfsops;
+#endif /* KERNEL */
diff --git a/sys/miscfs/umapfs/umap_subr.c b/sys/miscfs/umapfs/umap_subr.c
new file mode 100644
index 0000000..b640891
--- /dev/null
+++ b/sys/miscfs/umapfs/umap_subr.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)umap_subr.c 8.6 (Berkeley) 1/26/94
+ *
+ * $Id: lofs_subr.c, v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <miscfs/umapfs/umap.h>
+
+#define LOG2_SIZEVNODE 7 /* log2(sizeof struct vnode) */
+#define NUMAPNODECACHE 16
+#define UMAP_NHASH(vp) ((((u_long) vp)>>LOG2_SIZEVNODE) & (NUMAPNODECACHE-1))
+
+/*
+ * Null layer cache:
+ * Each cache entry holds a reference to the target vnode
+ * along with a pointer to the alias vnode. When an
+ * entry is added the target vnode is VREF'd. When the
+ * alias is removed the target vnode is vrele'd.
+ */
+
+/*
+ * Cache head
+ */
+struct umap_node_cache {
+ struct umap_node *ac_forw;
+ struct umap_node *ac_back;
+};
+
+static struct umap_node_cache umap_node_cache[NUMAPNODECACHE];
+
+/*
+ * Initialise cache headers
+ */
+int
+umapfs_init()
+{
+ struct umap_node_cache *ac;
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_init\n"); /* printed during system boot */
+#endif
+
+ for (ac = umap_node_cache; ac < umap_node_cache + NUMAPNODECACHE; ac++)
+ ac->ac_forw = ac->ac_back = (struct umap_node *) ac;
+ return (0);
+}
+
+/*
+ * Compute hash list for given target vnode
+ */
+static struct umap_node_cache *
+umap_node_hash(targetvp)
+ struct vnode *targetvp;
+{
+
+ return (&umap_node_cache[UMAP_NHASH(targetvp)]);
+}
+
+/*
+ * umap_findid is called by various routines in umap_vnodeops.c to
+ * find a user or group id in a map.
+ */
+static u_long
+umap_findid(id, map, nentries)
+ u_long id;
+ u_long map[][2];
+ int nentries;
+{
+ int i;
+
+ /* Find uid entry in map */
+ i = 0;
+ while ((i<nentries) && ((map[i][0]) != id))
+ i++;
+
+ if (i < nentries)
+ return (map[i][1]);
+ else
+ return (-1);
+
+}
+
+/*
+ * umap_reverse_findid is called by umap_getattr() in umap_vnodeops.c to
+ * find a user or group id in a map, in reverse.
+ */
+u_long
+umap_reverse_findid(id, map, nentries)
+ u_long id;
+ u_long map[][2];
+ int nentries;
+{
+ int i;
+
+ /* Find uid entry in map */
+ i = 0;
+ while ((i<nentries) && ((map[i][1]) != id))
+ i++;
+
+ if (i < nentries)
+ return (map[i][0]);
+ else
+ return (-1);
+
+}
+
+/*
+ * Return alias for target vnode if already exists, else 0.
+ */
+static struct vnode *
+umap_node_find(mp, targetvp)
+ struct mount *mp;
+ struct vnode *targetvp;
+{
+ struct umap_node_cache *hd;
+ struct umap_node *a;
+ struct vnode *vp;
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umap_node_find(mp = %x, target = %x)\n", mp, targetvp);
+#endif
+
+ /*
+ * Find hash base, and then search the (two-way) linked
+ * list looking for a umap_node structure which is referencing
+ * the target vnode. If found, the increment the umap_node
+ * reference count (but NOT the target vnode's VREF counter).
+ */
+ hd = umap_node_hash(targetvp);
+
+ loop:
+ for (a = hd->ac_forw; a != (struct umap_node *) hd; a = a->umap_forw) {
+ if (a->umap_lowervp == targetvp &&
+ a->umap_vnode->v_mount == mp) {
+ vp = UMAPTOV(a);
+ /*
+ * We need vget for the VXLOCK
+ * stuff, but we don't want to lock
+ * the lower node.
+ */
+ if (vget(vp, 0)) {
+#ifdef UMAPFS_DIAGNOSTIC
+ printf ("umap_node_find: vget failed.\n");
+#endif
+ goto loop;
+ }
+ return (vp);
+ }
+ }
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umap_node_find(%x, %x): NOT found\n", mp, targetvp);
+#endif
+
+ return (0);
+}
+
+/*
+ * Make a new umap_node node.
+ * Vp is the alias vnode, lofsvp is the target vnode.
+ * Maintain a reference to (targetvp).
+ */
+static int
+umap_node_alloc(mp, lowervp, vpp)
+ struct mount *mp;
+ struct vnode *lowervp;
+ struct vnode **vpp;
+{
+ struct umap_node_cache *hd;
+ struct umap_node *xp;
+ struct vnode *othervp, *vp;
+ int error;
+
+ if (error = getnewvnode(VT_UMAP, mp, umap_vnodeop_p, vpp))
+ return (error);
+ vp = *vpp;
+
+ MALLOC(xp, struct umap_node *, sizeof(struct umap_node),
+ M_TEMP, M_WAITOK);
+ vp->v_type = lowervp->v_type;
+ xp->umap_vnode = vp;
+ vp->v_data = xp;
+ xp->umap_lowervp = lowervp;
+ /*
+ * Before we insert our new node onto the hash chains,
+ * check to see if someone else has beaten us to it.
+ * (We could have slept in MALLOC.)
+ */
+ if (othervp = umap_node_find(lowervp)) {
+ FREE(xp, M_TEMP);
+ vp->v_type = VBAD; /* node is discarded */
+ vp->v_usecount = 0; /* XXX */
+ *vpp = othervp;
+ return (0);
+ }
+ VREF(lowervp); /* Extra VREF will be vrele'd in umap_node_create */
+ hd = umap_node_hash(lowervp);
+ insque(xp, hd);
+ return (0);
+}
+
+
+/*
+ * Try to find an existing umap_node vnode refering
+ * to it, otherwise make a new umap_node vnode which
+ * contains a reference to the target vnode.
+ */
+int
+umap_node_create(mp, targetvp, newvpp)
+ struct mount *mp;
+ struct vnode *targetvp;
+ struct vnode **newvpp;
+{
+ struct vnode *aliasvp;
+
+ if (aliasvp = umap_node_find(mp, targetvp)) {
+ /*
+ * Take another reference to the alias vnode
+ */
+#ifdef UMAPFS_DIAGNOSTIC
+ vprint("umap_node_create: exists", ap->umap_vnode);
+#endif
+ /* VREF(aliasvp); */
+ } else {
+ int error;
+
+ /*
+ * Get new vnode.
+ */
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umap_node_create: create new alias vnode\n");
+#endif
+ /*
+ * Make new vnode reference the umap_node.
+ */
+ if (error = umap_node_alloc(mp, targetvp, &aliasvp))
+ return (error);
+
+ /*
+ * aliasvp is already VREF'd by getnewvnode()
+ */
+ }
+
+ vrele(targetvp);
+
+#ifdef UMAPFS_DIAGNOSTIC
+ vprint("umap_node_create: alias", aliasvp);
+ vprint("umap_node_create: target", targetvp);
+#endif
+
+ *newvpp = aliasvp;
+ return (0);
+}
+
+#ifdef UMAPFS_DIAGNOSTIC
+int umap_checkvp_barrier = 1;
+struct vnode *
+umap_checkvp(vp, fil, lno)
+ struct vnode *vp;
+ char *fil;
+ int lno;
+{
+ struct umap_node *a = VTOUMAP(vp);
+#if 0
+ /*
+ * Can't do this check because vop_reclaim runs
+ * with funny vop vector.
+ */
+ if (vp->v_op != umap_vnodeop_p) {
+ printf ("umap_checkvp: on non-umap-node\n");
+ while (umap_checkvp_barrier) /*WAIT*/ ;
+ panic("umap_checkvp");
+ }
+#endif
+ if (a->umap_lowervp == NULL) {
+ /* Should never happen */
+ int i; u_long *p;
+ printf("vp = %x, ZERO ptr\n", vp);
+ for (p = (u_long *) a, i = 0; i < 8; i++)
+ printf(" %x", p[i]);
+ printf("\n");
+ /* wait for debugger */
+ while (umap_checkvp_barrier) /*WAIT*/ ;
+ panic("umap_checkvp");
+ }
+ if (a->umap_lowervp->v_usecount < 1) {
+ int i; u_long *p;
+ printf("vp = %x, unref'ed lowervp\n", vp);
+ for (p = (u_long *) a, i = 0; i < 8; i++)
+ printf(" %x", p[i]);
+ printf("\n");
+ /* wait for debugger */
+ while (umap_checkvp_barrier) /*WAIT*/ ;
+ panic ("umap with unref'ed lowervp");
+ }
+#if 0
+ printf("umap %x/%d -> %x/%d [%s, %d]\n",
+ a->umap_vnode, a->umap_vnode->v_usecount,
+ a->umap_lowervp, a->umap_lowervp->v_usecount,
+ fil, lno);
+#endif
+ return (a->umap_lowervp);
+}
+#endif
+
+/* umap_mapids maps all of the ids in a credential, both user and group. */
+
+void
+umap_mapids(v_mount, credp)
+ struct mount *v_mount;
+ struct ucred *credp;
+{
+ int i, unentries, gnentries;
+ u_long *groupmap, *usermap;
+ uid_t uid;
+ gid_t gid;
+
+ unentries = MOUNTTOUMAPMOUNT(v_mount)->info_nentries;
+ usermap = &(MOUNTTOUMAPMOUNT(v_mount)->info_mapdata[0][0]);
+ gnentries = MOUNTTOUMAPMOUNT(v_mount)->info_gnentries;
+ groupmap = &(MOUNTTOUMAPMOUNT(v_mount)->info_gmapdata[0][0]);
+
+ /* Find uid entry in map */
+
+ uid = (uid_t) umap_findid(credp->cr_uid, usermap, unentries);
+
+ if (uid != -1)
+ credp->cr_uid = uid;
+ else
+ credp->cr_uid = (uid_t) NOBODY;
+
+#ifdef notdef
+ /* cr_gid is the same as cr_groups[0] in 4BSD */
+
+ /* Find gid entry in map */
+
+ gid = (gid_t) umap_findid(credp->cr_gid, groupmap, gnentries);
+
+ if (gid != -1)
+ credp->cr_gid = gid;
+ else
+ credp->cr_gid = NULLGROUP;
+#endif
+
+ /* Now we must map each of the set of groups in the cr_groups
+ structure. */
+
+ i = 0;
+ while (credp->cr_groups[i] != 0) {
+ gid = (gid_t) umap_findid(credp->cr_groups[i],
+ groupmap, gnentries);
+
+ if (gid != -1)
+ credp->cr_groups[i++] = gid;
+ else
+ credp->cr_groups[i++] = NULLGROUP;
+ }
+}
diff --git a/sys/miscfs/umapfs/umap_vfsops.c b/sys/miscfs/umapfs/umap_vfsops.c
new file mode 100644
index 0000000..2480a85
--- /dev/null
+++ b/sys/miscfs/umapfs/umap_vfsops.c
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * the UCLA Ficus project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)umap_vfsops.c 8.3 (Berkeley) 1/21/94
+ *
+ * @(#)null_vfsops.c 1.5 (Berkeley) 7/10/92
+ */
+
+/*
+ * Umap Layer
+ * (See mount_umap(8) for a description of this layer.)
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <miscfs/umapfs/umap.h>
+
+/*
+ * Mount umap layer
+ */
+int
+umapfs_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ struct umap_args args;
+ struct vnode *lowerrootvp, *vp;
+ struct vnode *umapm_rootvp;
+ struct umap_mount *amp;
+ u_int size;
+ int error;
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_mount(mp = %x)\n", mp);
+#endif
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ return (EOPNOTSUPP);
+ /* return (VFS_MOUNT(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, path, data, ndp, p));*/
+ }
+
+ /*
+ * Get argument
+ */
+ if (error = copyin(data, (caddr_t)&args, sizeof(struct umap_args)))
+ return (error);
+
+ /*
+ * Find lower node
+ */
+ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF,
+ UIO_USERSPACE, args.target, p);
+ if (error = namei(ndp))
+ return (error);
+
+ /*
+ * Sanity check on lower vnode
+ */
+ lowerrootvp = ndp->ni_vp;
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("vp = %x, check for VDIR...\n", lowerrootvp);
+#endif
+ vrele(ndp->ni_dvp);
+ ndp->ni_dvp = 0;
+
+ if (lowerrootvp->v_type != VDIR) {
+ vput(lowerrootvp);
+ return (EINVAL);
+ }
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("mp = %x\n", mp);
+#endif
+
+ amp = (struct umap_mount *) malloc(sizeof(struct umap_mount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+
+ /*
+ * Save reference to underlying FS
+ */
+ amp->umapm_vfs = lowerrootvp->v_mount;
+
+ /*
+ * Now copy in the number of entries and maps for umap mapping.
+ */
+ amp->info_nentries = args.nentries;
+ amp->info_gnentries = args.gnentries;
+ error = copyin(args.mapdata, (caddr_t)amp->info_mapdata,
+ 2*sizeof(u_long)*args.nentries);
+ if (error)
+ return (error);
+
+#ifdef UMAP_DIAGNOSTIC
+ printf("umap_mount:nentries %d\n",args.nentries);
+ for (i = 0; i < args.nentries; i++)
+ printf(" %d maps to %d\n", amp->info_mapdata[i][0],
+ amp->info_mapdata[i][1]);
+#endif
+
+ error = copyin(args.gmapdata, (caddr_t)amp->info_gmapdata,
+ 2*sizeof(u_long)*args.nentries);
+ if (error)
+ return (error);
+
+#ifdef UMAP_DIAGNOSTIC
+ printf("umap_mount:gnentries %d\n",args.gnentries);
+ for (i = 0; i < args.gnentries; i++)
+ printf(" group %d maps to %d\n",
+ amp->info_gmapdata[i][0],
+ amp->info_gmapdata[i][1]);
+#endif
+
+
+ /*
+ * Save reference. Each mount also holds
+ * a reference on the root vnode.
+ */
+ error = umap_node_create(mp, lowerrootvp, &vp);
+ /*
+ * Unlock the node (either the lower or the alias)
+ */
+ VOP_UNLOCK(vp);
+ /*
+ * Make sure the node alias worked
+ */
+ if (error) {
+ vrele(lowerrootvp);
+ free(amp, M_UFSMNT); /* XXX */
+ return (error);
+ }
+
+ /*
+ * Keep a held reference to the root vnode.
+ * It is vrele'd in umapfs_unmount.
+ */
+ umapm_rootvp = vp;
+ umapm_rootvp->v_flag |= VROOT;
+ amp->umapm_rootvp = umapm_rootvp;
+ if (UMAPVPTOLOWERVP(umapm_rootvp)->v_mount->mnt_flag & MNT_LOCAL)
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_data = (qaddr_t) amp;
+ getnewfsid(mp, MOUNT_LOFS);
+
+ (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+ (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_mount: lower %s, alias at %s\n",
+ mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
+#endif
+ return (0);
+}
+
+/*
+ * VFS start. Nothing needed here - the start routine
+ * on the underlying filesystem will have been called
+ * when that filesystem was mounted.
+ */
+int
+umapfs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ return (0);
+ /* return (VFS_START(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, flags, p)); */
+}
+
+/*
+ * Free reference to umap layer
+ */
+int
+umapfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ struct vnode *umapm_rootvp = MOUNTTOUMAPMOUNT(mp)->umapm_rootvp;
+ int error;
+ int flags = 0;
+ extern int doforce;
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_unmount(mp = %x)\n", mp);
+#endif
+
+ if (mntflags & MNT_FORCE) {
+ /* lofs can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ /*
+ * Clear out buffer cache. I don't think we
+ * ever get anything cached at this level at the
+ * moment, but who knows...
+ */
+#ifdef notyet
+ mntflushbuf(mp, 0);
+ if (mntinvalbuf(mp, 1))
+ return (EBUSY);
+#endif
+ if (umapm_rootvp->v_usecount > 1)
+ return (EBUSY);
+ if (error = vflush(mp, umapm_rootvp, flags))
+ return (error);
+
+#ifdef UMAPFS_DIAGNOSTIC
+ vprint("alias root of lower", umapm_rootvp);
+#endif
+ /*
+ * Release reference on underlying root vnode
+ */
+ vrele(umapm_rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(umapm_rootvp);
+ /*
+ * Finally, throw away the umap_mount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ mp->mnt_data = 0;
+ return (0);
+}
+
+int
+umapfs_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct vnode *vp;
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_root(mp = %x, vp = %x->%x)\n", mp,
+ MOUNTTOUMAPMOUNT(mp)->umapm_rootvp,
+ UMAPVPTOLOWERVP(MOUNTTOUMAPMOUNT(mp)->umapm_rootvp)
+ );
+#endif
+
+ /*
+ * Return locked reference to root.
+ */
+ vp = MOUNTTOUMAPMOUNT(mp)->umapm_rootvp;
+ VREF(vp);
+ VOP_LOCK(vp);
+ *vpp = vp;
+ return (0);
+}
+
+int
+umapfs_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+ return (VFS_QUOTACTL(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, cmd, uid, arg, p));
+}
+
+int
+umapfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ int error;
+ struct statfs mstat;
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_statfs(mp = %x, vp = %x->%x)\n", mp,
+ MOUNTTOUMAPMOUNT(mp)->umapm_rootvp,
+ UMAPVPTOLOWERVP(MOUNTTOUMAPMOUNT(mp)->umapm_rootvp)
+ );
+#endif
+
+ bzero(&mstat, sizeof(mstat));
+
+ error = VFS_STATFS(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, &mstat, p);
+ if (error)
+ return (error);
+
+ /* now copy across the "interesting" information and fake the rest */
+ sbp->f_type = mstat.f_type;
+ sbp->f_flags = mstat.f_flags;
+ sbp->f_bsize = mstat.f_bsize;
+ sbp->f_iosize = mstat.f_iosize;
+ sbp->f_blocks = mstat.f_blocks;
+ sbp->f_bfree = mstat.f_bfree;
+ sbp->f_bavail = mstat.f_bavail;
+ sbp->f_files = mstat.f_files;
+ sbp->f_ffree = mstat.f_ffree;
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+ return (0);
+}
+
+int
+umapfs_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+ /*
+ * XXX - Assumes no data cached at umap layer.
+ */
+ return (0);
+}
+
+int
+umapfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (VFS_VGET(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, ino, vpp));
+}
+
+int
+umapfs_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
+ struct mount *mp;
+ struct fid *fidp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred**credanonp;
+{
+
+ return (VFS_FHTOVP(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, fidp, nam, vpp, exflagsp,credanonp));
+}
+
+int
+umapfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+ return (VFS_VPTOFH(UMAPVPTOLOWERVP(vp), fhp));
+}
+
+int umapfs_init __P((void));
+
+struct vfsops umap_vfsops = {
+ umapfs_mount,
+ umapfs_start,
+ umapfs_unmount,
+ umapfs_root,
+ umapfs_quotactl,
+ umapfs_statfs,
+ umapfs_sync,
+ umapfs_vget,
+ umapfs_fhtovp,
+ umapfs_vptofh,
+ umapfs_init,
+};
diff --git a/sys/miscfs/umapfs/umap_vnops.c b/sys/miscfs/umapfs/umap_vnops.c
new file mode 100644
index 0000000..0c1955f
--- /dev/null
+++ b/sys/miscfs/umapfs/umap_vnops.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * the UCLA Ficus project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)umap_vnops.c 8.3 (Berkeley) 1/5/94
+ */
+
+/*
+ * Umap Layer
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <miscfs/umapfs/umap.h>
+
+
+int umap_bug_bypass = 0; /* for debugging: enables bypass printf'ing */
+
+/*
+ * This is the 10-Apr-92 bypass routine.
+ * See null_vnops.c:null_bypass for more details.
+ */
+int
+umap_bypass(ap)
+ struct vop_generic_args /* {
+ struct vnodeop_desc *a_desc;
+ <other random data follows, presumably>
+ } */ *ap;
+{
+ extern int (**umap_vnodeop_p)(); /* not extern, really "forward" */
+ struct ucred **credpp = 0, *credp = 0;
+ struct ucred *savecredp = 0, *savecompcredp = 0;
+ struct ucred *compcredp = 0;
+ struct vnode **this_vp_p;
+ int error;
+ struct vnode *old_vps[VDESC_MAX_VPS];
+ struct vnode *vp1 = 0;
+ struct vnode **vps_p[VDESC_MAX_VPS];
+ struct vnode ***vppp;
+ struct vnodeop_desc *descp = ap->a_desc;
+ int reles, i;
+ struct componentname **compnamepp = 0;
+
+ if (umap_bug_bypass)
+ printf ("umap_bypass: %s\n", descp->vdesc_name);
+
+#ifdef SAFETY
+ /*
+ * We require at least one vp.
+ */
+ if (descp->vdesc_vp_offsets == NULL ||
+ descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET)
+ panic ("umap_bypass: no vp's in map.\n");
+#endif
+
+ /*
+ * Map the vnodes going in.
+ * Later, we'll invoke the operation based on
+ * the first mapped vnode's operation vector.
+ */
+ reles = descp->vdesc_flags;
+ for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
+ if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
+ break; /* bail out at end of list */
+ vps_p[i] = this_vp_p =
+ VOPARG_OFFSETTO(struct vnode**, descp->vdesc_vp_offsets[i], ap);
+
+ if (i == 0) {
+ vp1 = *vps_p[0];
+ }
+
+ /*
+ * We're not guaranteed that any but the first vnode
+ * are of our type. Check for and don't map any
+ * that aren't. (Must map first vp or vclean fails.)
+ */
+
+ if (i && (*this_vp_p)->v_op != umap_vnodeop_p) {
+ old_vps[i] = NULL;
+ } else {
+ old_vps[i] = *this_vp_p;
+ *(vps_p[i]) = UMAPVPTOLOWERVP(*this_vp_p);
+ if (reles & 1)
+ VREF(*this_vp_p);
+ }
+
+ }
+
+ /*
+ * Fix the credentials. (That's the purpose of this layer.)
+ */
+
+ if (descp->vdesc_cred_offset != VDESC_NO_OFFSET) {
+
+ credpp = VOPARG_OFFSETTO(struct ucred**,
+ descp->vdesc_cred_offset, ap);
+
+ /* Save old values */
+
+ savecredp = (*credpp);
+ (*credpp) = crdup(savecredp);
+ credp = *credpp;
+
+ if (umap_bug_bypass && credp->cr_uid != 0)
+ printf("umap_bypass: user was %d, group %d\n",
+ credp->cr_uid, credp->cr_gid);
+
+ /* Map all ids in the credential structure. */
+
+ umap_mapids(vp1->v_mount, credp);
+
+ if (umap_bug_bypass && credp->cr_uid != 0)
+ printf("umap_bypass: user now %d, group %d\n",
+ credp->cr_uid, credp->cr_gid);
+ }
+
+ /* BSD often keeps a credential in the componentname structure
+ * for speed. If there is one, it better get mapped, too.
+ */
+
+ if (descp->vdesc_componentname_offset != VDESC_NO_OFFSET) {
+
+ compnamepp = VOPARG_OFFSETTO(struct componentname**,
+ descp->vdesc_componentname_offset, ap);
+
+ compcredp = (*compnamepp)->cn_cred;
+ savecompcredp = compcredp;
+ compcredp = (*compnamepp)->cn_cred = crdup(savecompcredp);
+
+ if (umap_bug_bypass && compcredp->cr_uid != 0)
+ printf("umap_bypass: component credit user was %d, group %d\n",
+ compcredp->cr_uid, compcredp->cr_gid);
+
+ /* Map all ids in the credential structure. */
+
+ umap_mapids(vp1->v_mount, compcredp);
+
+ if (umap_bug_bypass && compcredp->cr_uid != 0)
+ printf("umap_bypass: component credit user now %d, group %d\n",
+ compcredp->cr_uid, compcredp->cr_gid);
+ }
+
+ /*
+ * Call the operation on the lower layer
+ * with the modified argument structure.
+ */
+ error = VCALL(*(vps_p[0]), descp->vdesc_offset, ap);
+
+ /*
+ * Maintain the illusion of call-by-value
+ * by restoring vnodes in the argument structure
+ * to their original value.
+ */
+ reles = descp->vdesc_flags;
+ for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
+ if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
+ break; /* bail out at end of list */
+ if (old_vps[i]) {
+ *(vps_p[i]) = old_vps[i];
+ if (reles & 1)
+ vrele(*(vps_p[i]));
+ };
+ };
+
+ /*
+ * Map the possible out-going vpp
+ * (Assumes that the lower layer always returns
+ * a VREF'ed vpp unless it gets an error.)
+ */
+ if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET &&
+ !(descp->vdesc_flags & VDESC_NOMAP_VPP) &&
+ !error) {
+ if (descp->vdesc_flags & VDESC_VPP_WILLRELE)
+ goto out;
+ vppp = VOPARG_OFFSETTO(struct vnode***,
+ descp->vdesc_vpp_offset, ap);
+ error = umap_node_create(old_vps[0]->v_mount, **vppp, *vppp);
+ };
+
+ out:
+ /*
+ * Free duplicate cred structure and restore old one.
+ */
+ if (descp->vdesc_cred_offset != VDESC_NO_OFFSET) {
+ if (umap_bug_bypass && credp && credp->cr_uid != 0)
+ printf("umap_bypass: returning-user was %d\n",
+ credp->cr_uid);
+
+ crfree(credp);
+ (*credpp) = savecredp;
+ if (umap_bug_bypass && credpp && (*credpp)->cr_uid != 0)
+ printf("umap_bypass: returning-user now %d\n\n",
+ (*credpp)->cr_uid);
+ }
+
+ if (descp->vdesc_componentname_offset != VDESC_NO_OFFSET) {
+ if (umap_bug_bypass && compcredp && compcredp->cr_uid != 0)
+ printf("umap_bypass: returning-component-user was %d\n",
+ compcredp->cr_uid);
+
+ crfree(compcredp);
+ (*compnamepp)->cn_cred = savecompcredp;
+ if (umap_bug_bypass && credpp && (*credpp)->cr_uid != 0)
+ printf("umap_bypass: returning-component-user now %d\n",
+ compcredp->cr_uid);
+ }
+
+ return (error);
+}
+
+
+/*
+ * We handle getattr to change the fsid.
+ */
+int
+umap_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ short uid, gid;
+ int error, tmpid, nentries, gnentries;
+ u_long (*mapdata)[2], (*gmapdata)[2];
+ struct vnode **vp1p;
+ struct vnodeop_desc *descp = ap->a_desc;
+
+ if (error = umap_bypass(ap))
+ return (error);
+ /* Requires that arguments be restored. */
+ ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
+
+ /*
+ * Umap needs to map the uid and gid returned by a stat
+ * into the proper values for this site. This involves
+ * finding the returned uid in the mapping information,
+ * translating it into the uid on the other end,
+ * and filling in the proper field in the vattr
+ * structure pointed to by ap->a_vap. The group
+ * is easier, since currently all groups will be
+ * translate to the NULLGROUP.
+ */
+
+ /* Find entry in map */
+
+ uid = ap->a_vap->va_uid;
+ gid = ap->a_vap->va_gid;
+ if (umap_bug_bypass)
+ printf("umap_getattr: mapped uid = %d, mapped gid = %d\n", uid,
+ gid);
+
+ vp1p = VOPARG_OFFSETTO(struct vnode**, descp->vdesc_vp_offsets[0], ap);
+ nentries = MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_nentries;
+ mapdata = (MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_mapdata);
+ gnentries = MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_gnentries;
+ gmapdata = (MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_gmapdata);
+
+ /* Reverse map the uid for the vnode. Since it's a reverse
+ map, we can't use umap_mapids() to do it. */
+
+ tmpid = umap_reverse_findid(uid, mapdata, nentries);
+
+ if (tmpid != -1) {
+
+ ap->a_vap->va_uid = (uid_t) tmpid;
+ if (umap_bug_bypass)
+ printf("umap_getattr: original uid = %d\n", uid);
+ } else
+ ap->a_vap->va_uid = (uid_t) NOBODY;
+
+ /* Reverse map the gid for the vnode. */
+
+ tmpid = umap_reverse_findid(gid, gmapdata, gnentries);
+
+ if (tmpid != -1) {
+
+ ap->a_vap->va_gid = (gid_t) tmpid;
+ if (umap_bug_bypass)
+ printf("umap_getattr: original gid = %d\n", gid);
+ } else
+ ap->a_vap->va_gid = (gid_t) NULLGROUP;
+
+ return (0);
+}
+
+int
+umap_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ /*
+ * Do nothing (and _don't_ bypass).
+ * Wait to vrele lowervp until reclaim,
+ * so that until then our umap_node is in the
+ * cache and reusable.
+ *
+ */
+ return (0);
+}
+
+int
+umap_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct umap_node *xp = VTOUMAP(vp);
+ struct vnode *lowervp = xp->umap_lowervp;
+
+ /* After this assignment, this node will not be re-used. */
+ xp->umap_lowervp = NULL;
+ remque(xp);
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = NULL;
+ vrele(lowervp);
+ return (0);
+}
+
+int
+umap_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ int error;
+ struct vnode *savedvp;
+
+ savedvp = bp->b_vp;
+ bp->b_vp = UMAPVPTOLOWERVP(bp->b_vp);
+
+ error = VOP_STRATEGY(ap->a_bp);
+
+ bp->b_vp = savedvp;
+
+ return (error);
+}
+
+int
+umap_bwrite(ap)
+ struct vop_bwrite_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ int error;
+ struct vnode *savedvp;
+
+ savedvp = bp->b_vp;
+ bp->b_vp = UMAPVPTOLOWERVP(bp->b_vp);
+
+ error = VOP_BWRITE(ap->a_bp);
+
+ bp->b_vp = savedvp;
+
+ return (error);
+}
+
+
+int
+umap_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ printf("\ttag VT_UMAPFS, vp=%x, lowervp=%x\n", vp, UMAPVPTOLOWERVP(vp));
+ return (0);
+}
+
+int
+umap_rename(ap)
+ struct vop_rename_args /* {
+ struct vnode *a_fdvp;
+ struct vnode *a_fvp;
+ struct componentname *a_fcnp;
+ struct vnode *a_tdvp;
+ struct vnode *a_tvp;
+ struct componentname *a_tcnp;
+ } */ *ap;
+{
+ int error;
+ struct componentname *compnamep;
+ struct ucred *compcredp, *savecompcredp;
+ struct vnode *vp;
+
+ /*
+ * Rename is irregular, having two componentname structures.
+ * We need to map the cre in the second structure,
+ * and then bypass takes care of the rest.
+ */
+
+ vp = ap->a_fdvp;
+ compnamep = ap->a_tcnp;
+ compcredp = compnamep->cn_cred;
+
+ savecompcredp = compcredp;
+ compcredp = compnamep->cn_cred = crdup(savecompcredp);
+
+ if (umap_bug_bypass && compcredp->cr_uid != 0)
+ printf("umap_rename: rename component credit user was %d, group %d\n",
+ compcredp->cr_uid, compcredp->cr_gid);
+
+ /* Map all ids in the credential structure. */
+
+ umap_mapids(vp->v_mount, compcredp);
+
+ if (umap_bug_bypass && compcredp->cr_uid != 0)
+ printf("umap_rename: rename component credit user now %d, group %d\n",
+ compcredp->cr_uid, compcredp->cr_gid);
+
+ error = umap_bypass(ap);
+
+ /* Restore the additional mapped componentname cred structure. */
+
+ crfree(compcredp);
+ compnamep->cn_cred = savecompcredp;
+
+ return error;
+}
+
+/*
+ * Global vfs data structures
+ */
+/*
+ * XXX - strategy, bwrite are hand coded currently. They should
+ * go away with a merged buffer/block cache.
+ *
+ */
+int (**umap_vnodeop_p)();
+struct vnodeopv_entry_desc umap_vnodeop_entries[] = {
+ { &vop_default_desc, umap_bypass },
+
+ { &vop_getattr_desc, umap_getattr },
+ { &vop_inactive_desc, umap_inactive },
+ { &vop_reclaim_desc, umap_reclaim },
+ { &vop_print_desc, umap_print },
+ { &vop_rename_desc, umap_rename },
+
+ { &vop_strategy_desc, umap_strategy },
+ { &vop_bwrite_desc, umap_bwrite },
+
+ { (struct vnodeop_desc*) NULL, (int(*)()) NULL }
+};
+struct vnodeopv_desc umap_vnodeop_opv_desc =
+ { &umap_vnodeop_p, umap_vnodeop_entries };
diff --git a/sys/miscfs/union/README b/sys/miscfs/union/README
new file mode 100644
index 0000000..14a4769
--- /dev/null
+++ b/sys/miscfs/union/README
@@ -0,0 +1,7 @@
+If you plan on using union mounts, then you should consider replacing
+"libc/gen/opendir.c" in the C library with the file "libc.opendir.c"
+in this directory. The replacement version of opendir() automatically
+removes duplicate names when a union stack is encountered. You will
+then need to rebuild the C library and all commands.
+
+@(#)README 8.1 (Berkeley) 2/15/94
diff --git a/sys/miscfs/union/libc.opendir.c b/sys/miscfs/union/libc.opendir.c
new file mode 100644
index 0000000..99ed58b
--- /dev/null
+++ b/sys/miscfs/union/libc.opendir.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 1983, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char orig_sccsid[] = "@(#)opendir.c 8.2 (Berkeley) 2/12/94";
+static char sccsid[] = "@(#)libc.opendir.c 8.1 (Berkeley) 2/15/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * open a directory.
+ */
+DIR *
+opendir(name)
+ const char *name;
+{
+ DIR *dirp;
+ int fd;
+ int incr;
+ struct statfs sfb;
+
+ if ((fd = open(name, 0)) == -1)
+ return (NULL);
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 ||
+ (dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
+ close(fd);
+ return (NULL);
+ }
+
+ /*
+ * If CLBYTES is an exact multiple of DIRBLKSIZ, use a CLBYTES
+ * buffer that it cluster boundary aligned.
+ * Hopefully this can be a big win someday by allowing page
+ * trades trade to user space to be done by getdirentries()
+ */
+ if ((CLBYTES % DIRBLKSIZ) == 0)
+ incr = CLBYTES;
+ else
+ incr = DIRBLKSIZ;
+
+#ifdef MOUNT_UNION
+ /*
+ * Determine whether this directory is the top of a union stack.
+ */
+ if (fstatfs(fd, &sfb) < 0) {
+ free(dirp);
+ close(fd);
+ return (NULL);
+ }
+
+ if (sfb.f_type == MOUNT_UNION) {
+ int len = 0;
+ int space = 0;
+ char *buf = 0;
+ char *ddptr = 0;
+ int n;
+ struct dirent **dpv;
+
+ /*
+ * The strategy here is to read all the directory
+ * entries into a buffer, sort the buffer, and
+ * remove duplicate entries by setting the inode
+ * number to zero.
+ */
+
+ /*
+ * Fixup dd_loc to be non-zero to fake out readdir
+ */
+ dirp->dd_loc = sizeof(void *);
+
+ do {
+ /*
+ * Always make at least DIRBLKSIZ bytes
+ * available to getdirentries
+ */
+ if (space < DIRBLKSIZ) {
+ space += incr;
+ len += incr;
+ buf = realloc(buf, len);
+ if (buf == NULL) {
+ free(dirp);
+ close(fd);
+ return (NULL);
+ }
+ ddptr = buf + (len - space) + dirp->dd_loc;
+ }
+
+ n = getdirentries(fd, ddptr, space, &dirp->dd_seek);
+ if (n > 0) {
+ ddptr += n;
+ space -= n;
+ }
+ } while (n > 0);
+
+ /*
+ * There is now a buffer full of (possibly) duplicate
+ * names.
+ */
+ dirp->dd_buf = buf;
+
+ /*
+ * Go round this loop twice...
+ *
+ * Scan through the buffer, counting entries.
+ * On the second pass, save pointers to each one.
+ * Then sort the pointers and remove duplicate names.
+ */
+ for (dpv = 0;;) {
+ n = 0;
+ ddptr = buf + dirp->dd_loc;
+ while (ddptr < buf + len) {
+ struct dirent *dp;
+
+ dp = (struct dirent *) ddptr;
+ if ((int)dp & 03)
+ break;
+ if ((dp->d_reclen <= 0) ||
+ (dp->d_reclen > (buf + len + 1 - ddptr)))
+ break;
+ ddptr += dp->d_reclen;
+ if (dp->d_fileno) {
+ if (dpv)
+ dpv[n] = dp;
+ n++;
+ }
+ }
+
+ if (dpv) {
+ struct dirent *xp;
+
+ /*
+ * If and when whiteouts happen,
+ * this sort would need to be stable.
+ */
+ heapsort(dpv, n, sizeof(*dpv), alphasort);
+
+ dpv[n] = NULL;
+ xp = NULL;
+
+ /*
+ * Scan through the buffer in sort order,
+ * zapping the inode number of any
+ * duplicate names.
+ */
+ for (n = 0; dpv[n]; n++) {
+ struct dirent *dp = dpv[n];
+
+ if ((xp == NULL) ||
+ strcmp(dp->d_name, xp->d_name))
+ xp = dp;
+ else
+ dp->d_fileno = 0;
+ }
+
+ free(dpv);
+ break;
+ } else {
+ dpv = malloc((n+1) * sizeof(struct dirent *));
+ if (dpv == NULL)
+ break;
+ }
+ }
+
+ dirp->dd_len = len;
+ dirp->dd_size = ddptr - dirp->dd_buf;
+ } else
+#endif /* MOUNT_UNION */
+ {
+ dirp->dd_len = incr;
+ dirp->dd_buf = malloc(dirp->dd_len);
+ if (dirp->dd_buf == NULL) {
+ free(dirp);
+ close (fd);
+ return (NULL);
+ }
+ dirp->dd_seek = 0;
+ dirp->dd_loc = 0;
+ }
+
+ dirp->dd_fd = fd;
+
+ /*
+ * Set up seek point for rewinddir.
+ */
+ dirp->dd_rewind = telldir(dirp);
+
+ return (dirp);
+}
diff --git a/sys/miscfs/union/union.h b/sys/miscfs/union/union.h
new file mode 100644
index 0000000..463218a
--- /dev/null
+++ b/sys/miscfs/union/union.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1994 The Regents of the University of California.
+ * Copyright (c) 1994 Jan-Simon Pendry.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)union.h 8.2 (Berkeley) 2/17/94
+ */
+
+struct union_args {
+ char *target; /* Target of loopback */
+ int mntflags; /* Options on the mount */
+};
+
+#define UNMNT_ABOVE 0x0001 /* Target appears below mount point */
+#define UNMNT_BELOW 0x0002 /* Target appears below mount point */
+#define UNMNT_REPLACE 0x0003 /* Target replaces mount point */
+#define UNMNT_OPMASK 0x0003
+
+struct union_mount {
+ struct vnode *um_uppervp;
+ struct vnode *um_lowervp;
+ struct ucred *um_cred; /* Credentials of user calling mount */
+ int um_cmode; /* cmask from mount process */
+ int um_op; /* Operation mode */
+};
+
+#ifdef KERNEL
+
+/*
+ * DEFDIRMODE is the mode bits used to create a shadow directory.
+ */
+#define VRWXMODE (VREAD|VWRITE|VEXEC)
+#define VRWMODE (VREAD|VWRITE)
+#define UN_DIRMODE ((VRWXMODE)|(VRWXMODE>>3)|(VRWXMODE>>6))
+#define UN_FILEMODE ((VRWMODE)|(VRWMODE>>3)|(VRWMODE>>6))
+
+/*
+ * A cache of vnode references
+ */
+struct union_node {
+ LIST_ENTRY(union_node) un_cache; /* Hash chain */
+ struct vnode *un_vnode; /* Back pointer */
+ struct vnode *un_uppervp; /* overlaying object */
+ struct vnode *un_lowervp; /* underlying object */
+ struct vnode *un_dirvp; /* Parent dir of uppervp */
+ char *un_path; /* saved component name */
+ int un_hash; /* saved un_path hash value */
+ int un_openl; /* # of opens on lowervp */
+ int un_flags;
+#ifdef DIAGNOSTIC
+ pid_t un_pid;
+#endif
+};
+
+#define UN_WANT 0x01
+#define UN_LOCKED 0x02
+#define UN_ULOCK 0x04 /* Upper node is locked */
+#define UN_KLOCK 0x08 /* Keep upper node locked on vput */
+
+extern int union_allocvp __P((struct vnode **, struct mount *,
+ struct vnode *, struct vnode *,
+ struct componentname *, struct vnode *,
+ struct vnode *));
+extern int union_copyfile __P((struct proc *, struct ucred *,
+ struct vnode *, struct vnode *));
+extern int union_mkshadow __P((struct union_mount *, struct vnode *,
+ struct componentname *, struct vnode **));
+extern int union_vn_create __P((struct vnode **, struct union_node *,
+ struct proc *));
+extern int union_cn_close __P((struct vnode *, int, struct ucred *,
+ struct proc *));
+extern void union_removed_upper __P((struct union_node *un));
+extern struct vnode *union_lowervp __P((struct vnode *));
+extern void union_newlower __P((struct union_node *, struct vnode *));
+extern void union_newupper __P((struct union_node *, struct vnode *));
+
+#define MOUNTTOUNIONMOUNT(mp) ((struct union_mount *)((mp)->mnt_data))
+#define VTOUNION(vp) ((struct union_node *)(vp)->v_data)
+#define UNIONTOV(un) ((un)->un_vnode)
+#define LOWERVP(vp) (VTOUNION(vp)->un_lowervp)
+#define UPPERVP(vp) (VTOUNION(vp)->un_uppervp)
+#define OTHERVP(vp) (UPPERVP(vp) ? UPPERVP(vp) : LOWERVP(vp))
+
+extern int (**union_vnodeop_p)();
+extern struct vfsops union_vfsops;
+#endif /* KERNEL */
diff --git a/sys/miscfs/union/union_subr.c b/sys/miscfs/union/union_subr.c
new file mode 100644
index 0000000..ea4f804
--- /dev/null
+++ b/sys/miscfs/union/union_subr.c
@@ -0,0 +1,743 @@
+/*
+ * Copyright (c) 1994 Jan-Simon Pendry
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)union_subr.c 8.4 (Berkeley) 2/17/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/queue.h>
+#include <miscfs/union/union.h>
+
+#include <sys/proc.h>
+
+/* must be power of two, otherwise change UNION_HASH() */
+#define NHASH 32
+
+/* unsigned int ... */
+#define UNION_HASH(u, l) \
+ (((((unsigned long) (u)) + ((unsigned long) l)) >> 8) & (NHASH-1))
+
+static LIST_HEAD(unhead, union_node) unhead[NHASH];
+static int unvplock[NHASH];
+
+int
+union_init()
+{
+ int i;
+
+ for (i = 0; i < NHASH; i++)
+ LIST_INIT(&unhead[i]);
+ bzero((caddr_t) unvplock, sizeof(unvplock));
+ return (0);
+}
+
+static int
+union_list_lock(ix)
+ int ix;
+{
+
+ if (unvplock[ix] & UN_LOCKED) {
+ unvplock[ix] |= UN_WANT;
+ sleep((caddr_t) &unvplock[ix], PINOD);
+ return (1);
+ }
+
+ unvplock[ix] |= UN_LOCKED;
+
+ return (0);
+}
+
+static void
+union_list_unlock(ix)
+ int ix;
+{
+
+ unvplock[ix] &= ~UN_LOCKED;
+
+ if (unvplock[ix] & UN_WANT) {
+ unvplock[ix] &= ~UN_WANT;
+ wakeup((caddr_t) &unvplock[ix]);
+ }
+}
+
+void
+union_updatevp(un, uppervp, lowervp)
+ struct union_node *un;
+ struct vnode *uppervp;
+ struct vnode *lowervp;
+{
+ int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp);
+ int nhash = UNION_HASH(uppervp, lowervp);
+
+ if (ohash != nhash) {
+ /*
+ * Ensure locking is ordered from lower to higher
+ * to avoid deadlocks.
+ */
+ if (nhash < ohash) {
+ int t = ohash;
+ ohash = nhash;
+ nhash = t;
+ }
+
+ while (union_list_lock(ohash))
+ continue;
+
+ while (union_list_lock(nhash))
+ continue;
+
+ LIST_REMOVE(un, un_cache);
+ union_list_unlock(ohash);
+ } else {
+ while (union_list_lock(nhash))
+ continue;
+ }
+
+ if (un->un_lowervp != lowervp) {
+ if (un->un_lowervp) {
+ vrele(un->un_lowervp);
+ if (un->un_path) {
+ free(un->un_path, M_TEMP);
+ un->un_path = 0;
+ }
+ if (un->un_dirvp) {
+ vrele(un->un_dirvp);
+ un->un_dirvp = NULLVP;
+ }
+ }
+ un->un_lowervp = lowervp;
+ }
+
+ if (un->un_uppervp != uppervp) {
+ if (un->un_uppervp)
+ vrele(un->un_uppervp);
+
+ un->un_uppervp = uppervp;
+ }
+
+ if (ohash != nhash)
+ LIST_INSERT_HEAD(&unhead[nhash], un, un_cache);
+
+ union_list_unlock(nhash);
+}
+
+void
+union_newlower(un, lowervp)
+ struct union_node *un;
+ struct vnode *lowervp;
+{
+
+ union_updatevp(un, un->un_uppervp, lowervp);
+}
+
+void
+union_newupper(un, uppervp)
+ struct union_node *un;
+ struct vnode *uppervp;
+{
+
+ union_updatevp(un, uppervp, un->un_lowervp);
+}
+
+/*
+ * allocate a union_node/vnode pair. the vnode is
+ * referenced and locked. the new vnode is returned
+ * via (vpp). (mp) is the mountpoint of the union filesystem,
+ * (dvp) is the parent directory where the upper layer object
+ * should exist (but doesn't) and (cnp) is the componentname
+ * information which is partially copied to allow the upper
+ * layer object to be created at a later time. (uppervp)
+ * and (lowervp) reference the upper and lower layer objects
+ * being mapped. either, but not both, can be nil.
+ * if supplied, (uppervp) is locked.
+ * the reference is either maintained in the new union_node
+ * object which is allocated, or they are vrele'd.
+ *
+ * all union_nodes are maintained on a singly-linked
+ * list. new nodes are only allocated when they cannot
+ * be found on this list. entries on the list are
+ * removed when the vfs reclaim entry is called.
+ *
+ * a single lock is kept for the entire list. this is
+ * needed because the getnewvnode() function can block
+ * waiting for a vnode to become free, in which case there
+ * may be more than one process trying to get the same
+ * vnode. this lock is only taken if we are going to
+ * call getnewvnode, since the kernel itself is single-threaded.
+ *
+ * if an entry is found on the list, then call vget() to
+ * take a reference. this is done because there may be
+ * zero references to it and so it needs to removed from
+ * the vnode free list.
+ */
+int
+union_allocvp(vpp, mp, undvp, dvp, cnp, uppervp, lowervp)
+ struct vnode **vpp;
+ struct mount *mp;
+ struct vnode *undvp;
+ struct vnode *dvp; /* may be null */
+ struct componentname *cnp; /* may be null */
+ struct vnode *uppervp; /* may be null */
+ struct vnode *lowervp; /* may be null */
+{
+ int error;
+ struct union_node *un = 0;
+ struct union_node **pp;
+ struct vnode *xlowervp = NULLVP;
+ int hash = 0;
+ int try;
+
+ if (uppervp == NULLVP && lowervp == NULLVP)
+ panic("union: unidentifiable allocation");
+
+ if (uppervp && lowervp && (uppervp->v_type != lowervp->v_type)) {
+ xlowervp = lowervp;
+ lowervp = NULLVP;
+ }
+
+loop:
+ for (try = 0; try < 3; try++) {
+ switch (try) {
+ case 0:
+ if (lowervp == NULLVP)
+ continue;
+ hash = UNION_HASH(uppervp, lowervp);
+ break;
+
+ case 1:
+ if (uppervp == NULLVP)
+ continue;
+ hash = UNION_HASH(uppervp, NULLVP);
+ break;
+
+ case 2:
+ if (lowervp == NULLVP)
+ continue;
+ hash = UNION_HASH(NULLVP, lowervp);
+ break;
+ }
+
+ while (union_list_lock(hash))
+ continue;
+
+ for (un = unhead[hash].lh_first; un != 0;
+ un = un->un_cache.le_next) {
+ if ((un->un_lowervp == lowervp ||
+ un->un_lowervp == NULLVP) &&
+ (un->un_uppervp == uppervp ||
+ un->un_uppervp == NULLVP) &&
+ (UNIONTOV(un)->v_mount == mp)) {
+ if (vget(UNIONTOV(un), 0)) {
+ union_list_unlock(hash);
+ goto loop;
+ }
+ break;
+ }
+ }
+
+ union_list_unlock(hash);
+
+ if (un)
+ break;
+ }
+
+ if (un) {
+ /*
+ * Obtain a lock on the union_node.
+ * uppervp is locked, though un->un_uppervp
+ * may not be. this doesn't break the locking
+ * hierarchy since in the case that un->un_uppervp
+ * is not yet locked it will be vrele'd and replaced
+ * with uppervp.
+ */
+
+ if ((dvp != NULLVP) && (uppervp == dvp)) {
+ /*
+ * Access ``.'', so (un) will already
+ * be locked. Since this process has
+ * the lock on (uppervp) no other
+ * process can hold the lock on (un).
+ */
+#ifdef DIAGNOSTIC
+ if ((un->un_flags & UN_LOCKED) == 0)
+ panic("union: . not locked");
+ else if (curproc && un->un_pid != curproc->p_pid &&
+ un->un_pid > -1 && curproc->p_pid > -1)
+ panic("union: allocvp not lock owner");
+#endif
+ } else {
+ if (un->un_flags & UN_LOCKED) {
+ vrele(UNIONTOV(un));
+ un->un_flags |= UN_WANT;
+ sleep((caddr_t) &un->un_flags, PINOD);
+ goto loop;
+ }
+ un->un_flags |= UN_LOCKED;
+
+#ifdef DIAGNOSTIC
+ if (curproc)
+ un->un_pid = curproc->p_pid;
+ else
+ un->un_pid = -1;
+#endif
+ }
+
+ /*
+ * At this point, the union_node is locked,
+ * un->un_uppervp may not be locked, and uppervp
+ * is locked or nil.
+ */
+
+ /*
+ * Save information about the upper layer.
+ */
+ if (uppervp != un->un_uppervp) {
+ union_newupper(un, uppervp);
+ } else if (uppervp) {
+ vrele(uppervp);
+ }
+
+ if (un->un_uppervp) {
+ un->un_flags |= UN_ULOCK;
+ un->un_flags &= ~UN_KLOCK;
+ }
+
+ /*
+ * Save information about the lower layer.
+ * This needs to keep track of pathname
+ * and directory information which union_vn_create
+ * might need.
+ */
+ if (lowervp != un->un_lowervp) {
+ union_newlower(un, lowervp);
+ if (cnp && (lowervp != NULLVP) &&
+ (lowervp->v_type == VREG)) {
+ un->un_hash = cnp->cn_hash;
+ un->un_path = malloc(cnp->cn_namelen+1,
+ M_TEMP, M_WAITOK);
+ bcopy(cnp->cn_nameptr, un->un_path,
+ cnp->cn_namelen);
+ un->un_path[cnp->cn_namelen] = '\0';
+ VREF(dvp);
+ un->un_dirvp = dvp;
+ }
+ } else if (lowervp) {
+ vrele(lowervp);
+ }
+ *vpp = UNIONTOV(un);
+ return (0);
+ }
+
+ /*
+ * otherwise lock the vp list while we call getnewvnode
+ * since that can block.
+ */
+ hash = UNION_HASH(uppervp, lowervp);
+
+ if (union_list_lock(hash))
+ goto loop;
+
+ error = getnewvnode(VT_UNION, mp, union_vnodeop_p, vpp);
+ if (error) {
+ if (uppervp) {
+ if (dvp == uppervp)
+ vrele(uppervp);
+ else
+ vput(uppervp);
+ }
+ if (lowervp)
+ vrele(lowervp);
+
+ goto out;
+ }
+
+ MALLOC((*vpp)->v_data, void *, sizeof(struct union_node),
+ M_TEMP, M_WAITOK);
+
+ if (uppervp)
+ (*vpp)->v_type = uppervp->v_type;
+ else
+ (*vpp)->v_type = lowervp->v_type;
+ un = VTOUNION(*vpp);
+ un->un_vnode = *vpp;
+ un->un_uppervp = uppervp;
+ un->un_lowervp = lowervp;
+ un->un_openl = 0;
+ un->un_flags = UN_LOCKED;
+ if (un->un_uppervp)
+ un->un_flags |= UN_ULOCK;
+#ifdef DIAGNOSTIC
+ if (curproc)
+ un->un_pid = curproc->p_pid;
+ else
+ un->un_pid = -1;
+#endif
+ if (cnp && (lowervp != NULLVP) && (lowervp->v_type == VREG)) {
+ un->un_hash = cnp->cn_hash;
+ un->un_path = malloc(cnp->cn_namelen+1, M_TEMP, M_WAITOK);
+ bcopy(cnp->cn_nameptr, un->un_path, cnp->cn_namelen);
+ un->un_path[cnp->cn_namelen] = '\0';
+ VREF(dvp);
+ un->un_dirvp = dvp;
+ } else {
+ un->un_hash = 0;
+ un->un_path = 0;
+ un->un_dirvp = 0;
+ }
+
+ LIST_INSERT_HEAD(&unhead[hash], un, un_cache);
+
+ if (xlowervp)
+ vrele(xlowervp);
+
+out:
+ union_list_unlock(hash);
+
+ return (error);
+}
+
+int
+union_freevp(vp)
+ struct vnode *vp;
+{
+ struct union_node *un = VTOUNION(vp);
+
+ LIST_REMOVE(un, un_cache);
+
+ if (un->un_uppervp)
+ vrele(un->un_uppervp);
+ if (un->un_lowervp)
+ vrele(un->un_lowervp);
+ if (un->un_dirvp)
+ vrele(un->un_dirvp);
+ if (un->un_path)
+ free(un->un_path, M_TEMP);
+
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = 0;
+
+ return (0);
+}
+
+/*
+ * copyfile. copy the vnode (fvp) to the vnode (tvp)
+ * using a sequence of reads and writes. both (fvp)
+ * and (tvp) are locked on entry and exit.
+ */
+int
+union_copyfile(p, cred, fvp, tvp)
+ struct proc *p;
+ struct ucred *cred;
+ struct vnode *fvp;
+ struct vnode *tvp;
+{
+ char *buf;
+ struct uio uio;
+ struct iovec iov;
+ int error = 0;
+
+ /*
+ * strategy:
+ * allocate a buffer of size MAXBSIZE.
+ * loop doing reads and writes, keeping track
+ * of the current uio offset.
+ * give up at the first sign of trouble.
+ */
+
+ uio.uio_procp = p;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_offset = 0;
+
+ VOP_UNLOCK(fvp); /* XXX */
+ LEASE_CHECK(fvp, p, cred, LEASE_READ);
+ VOP_LOCK(fvp); /* XXX */
+ VOP_UNLOCK(tvp); /* XXX */
+ LEASE_CHECK(tvp, p, cred, LEASE_WRITE);
+ VOP_LOCK(tvp); /* XXX */
+
+ buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
+
+ /* ugly loop follows... */
+ do {
+ off_t offset = uio.uio_offset;
+
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ iov.iov_base = buf;
+ iov.iov_len = MAXBSIZE;
+ uio.uio_resid = iov.iov_len;
+ uio.uio_rw = UIO_READ;
+ error = VOP_READ(fvp, &uio, 0, cred);
+
+ if (error == 0) {
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ iov.iov_base = buf;
+ iov.iov_len = MAXBSIZE - uio.uio_resid;
+ uio.uio_offset = offset;
+ uio.uio_rw = UIO_WRITE;
+ uio.uio_resid = iov.iov_len;
+
+ if (uio.uio_resid == 0)
+ break;
+
+ do {
+ error = VOP_WRITE(tvp, &uio, 0, cred);
+ } while ((uio.uio_resid > 0) && (error == 0));
+ }
+
+ } while (error == 0);
+
+ free(buf, M_TEMP);
+ return (error);
+}
+
+/*
+ * Create a shadow directory in the upper layer.
+ * The new vnode is returned locked.
+ *
+ * (um) points to the union mount structure for access to the
+ * the mounting process's credentials.
+ * (dvp) is the directory in which to create the shadow directory.
+ * it is unlocked on entry and exit.
+ * (cnp) is the componentname to be created.
+ * (vpp) is the returned newly created shadow directory, which
+ * is returned locked.
+ */
+int
+union_mkshadow(um, dvp, cnp, vpp)
+ struct union_mount *um;
+ struct vnode *dvp;
+ struct componentname *cnp;
+ struct vnode **vpp;
+{
+ int error;
+ struct vattr va;
+ struct proc *p = cnp->cn_proc;
+ struct componentname cn;
+
+ /*
+ * policy: when creating the shadow directory in the
+ * upper layer, create it owned by the user who did
+ * the mount, group from parent directory, and mode
+ * 777 modified by umask (ie mostly identical to the
+ * mkdir syscall). (jsp, kb)
+ */
+
+ /*
+ * A new componentname structure must be faked up because
+ * there is no way to know where the upper level cnp came
+ * from or what it is being used for. This must duplicate
+ * some of the work done by NDINIT, some of the work done
+ * by namei, some of the work done by lookup and some of
+ * the work done by VOP_LOOKUP when given a CREATE flag.
+ * Conclusion: Horrible.
+ *
+ * The pathname buffer will be FREEed by VOP_MKDIR.
+ */
+ cn.cn_pnbuf = malloc(cnp->cn_namelen+1, M_NAMEI, M_WAITOK);
+ bcopy(cnp->cn_nameptr, cn.cn_pnbuf, cnp->cn_namelen);
+ cn.cn_pnbuf[cnp->cn_namelen] = '\0';
+
+ cn.cn_nameiop = CREATE;
+ cn.cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN);
+ cn.cn_proc = cnp->cn_proc;
+ if (um->um_op == UNMNT_ABOVE)
+ cn.cn_cred = cnp->cn_cred;
+ else
+ cn.cn_cred = um->um_cred;
+ cn.cn_nameptr = cn.cn_pnbuf;
+ cn.cn_namelen = cnp->cn_namelen;
+ cn.cn_hash = cnp->cn_hash;
+ cn.cn_consume = cnp->cn_consume;
+
+ VREF(dvp);
+ if (error = relookup(dvp, vpp, &cn))
+ return (error);
+ vrele(dvp);
+
+ if (*vpp) {
+ VOP_ABORTOP(dvp, &cn);
+ VOP_UNLOCK(dvp);
+ vrele(*vpp);
+ *vpp = NULLVP;
+ return (EEXIST);
+ }
+
+ VATTR_NULL(&va);
+ va.va_type = VDIR;
+ va.va_mode = um->um_cmode;
+
+ /* LEASE_CHECK: dvp is locked */
+ LEASE_CHECK(dvp, p, p->p_ucred, LEASE_WRITE);
+
+ error = VOP_MKDIR(dvp, vpp, &cn, &va);
+ return (error);
+}
+
+/*
+ * union_vn_create: creates and opens a new shadow file
+ * on the upper union layer. this function is similar
+ * in spirit to calling vn_open but it avoids calling namei().
+ * the problem with calling namei is that a) it locks too many
+ * things, and b) it doesn't start at the "right" directory,
+ * whereas relookup is told where to start.
+ */
+int
+union_vn_create(vpp, un, p)
+ struct vnode **vpp;
+ struct union_node *un;
+ struct proc *p;
+{
+ struct vnode *vp;
+ struct ucred *cred = p->p_ucred;
+ struct vattr vat;
+ struct vattr *vap = &vat;
+ int fmode = FFLAGS(O_WRONLY|O_CREAT|O_TRUNC|O_EXCL);
+ int error;
+ int cmode = UN_FILEMODE & ~p->p_fd->fd_cmask;
+ char *cp;
+ struct componentname cn;
+
+ *vpp = NULLVP;
+
+ /*
+ * Build a new componentname structure (for the same
+ * reasons outlines in union_mkshadow).
+ * The difference here is that the file is owned by
+ * the current user, rather than by the person who
+ * did the mount, since the current user needs to be
+ * able to write the file (that's why it is being
+ * copied in the first place).
+ */
+ cn.cn_namelen = strlen(un->un_path);
+ cn.cn_pnbuf = (caddr_t) malloc(cn.cn_namelen, M_NAMEI, M_WAITOK);
+ bcopy(un->un_path, cn.cn_pnbuf, cn.cn_namelen+1);
+ cn.cn_nameiop = CREATE;
+ cn.cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN);
+ cn.cn_proc = p;
+ cn.cn_cred = p->p_ucred;
+ cn.cn_nameptr = cn.cn_pnbuf;
+ cn.cn_hash = un->un_hash;
+ cn.cn_consume = 0;
+
+ VREF(un->un_dirvp);
+ if (error = relookup(un->un_dirvp, &vp, &cn))
+ return (error);
+ vrele(un->un_dirvp);
+
+ if (vp) {
+ VOP_ABORTOP(un->un_dirvp, &cn);
+ if (un->un_dirvp == vp)
+ vrele(un->un_dirvp);
+ else
+ vput(un->un_dirvp);
+ vrele(vp);
+ return (EEXIST);
+ }
+
+ /*
+ * Good - there was no race to create the file
+ * so go ahead and create it. The permissions
+ * on the file will be 0666 modified by the
+ * current user's umask. Access to the file, while
+ * it is unioned, will require access to the top *and*
+ * bottom files. Access when not unioned will simply
+ * require access to the top-level file.
+ * TODO: confirm choice of access permissions.
+ */
+ VATTR_NULL(vap);
+ vap->va_type = VREG;
+ vap->va_mode = cmode;
+ LEASE_CHECK(un->un_dirvp, p, cred, LEASE_WRITE);
+ if (error = VOP_CREATE(un->un_dirvp, &vp, &cn, vap))
+ return (error);
+
+ if (error = VOP_OPEN(vp, fmode, cred, p)) {
+ vput(vp);
+ return (error);
+ }
+
+ vp->v_writecount++;
+ *vpp = vp;
+ return (0);
+}
+
+int
+union_vn_close(vp, fmode, cred, p)
+ struct vnode *vp;
+ int fmode;
+ struct ucred *cred;
+ struct proc *p;
+{
+ if (fmode & FWRITE)
+ --vp->v_writecount;
+ return (VOP_CLOSE(vp, fmode));
+}
+
+void
+union_removed_upper(un)
+ struct union_node *un;
+{
+ if (un->un_flags & UN_ULOCK) {
+ un->un_flags &= ~UN_ULOCK;
+ VOP_UNLOCK(un->un_uppervp);
+ }
+
+ union_newupper(un, NULLVP);
+}
+
+struct vnode *
+union_lowervp(vp)
+ struct vnode *vp;
+{
+ struct union_node *un = VTOUNION(vp);
+
+ if (un->un_lowervp && (vp->v_type == un->un_lowervp->v_type)) {
+ if (vget(un->un_lowervp, 0))
+ return (NULLVP);
+ }
+
+ return (un->un_lowervp);
+}
diff --git a/sys/miscfs/union/union_vfsops.c b/sys/miscfs/union/union_vfsops.c
new file mode 100644
index 0000000..42931d7
--- /dev/null
+++ b/sys/miscfs/union/union_vfsops.c
@@ -0,0 +1,550 @@
+/*
+ * Copyright (c) 1994 The Regents of the University of California.
+ * Copyright (c) 1994 Jan-Simon Pendry.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)union_vfsops.c 8.7 (Berkeley) 3/5/94
+ */
+
+/*
+ * Union Layer
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/filedesc.h>
+#include <sys/queue.h>
+#include <miscfs/union/union.h>
+
+/*
+ * Mount union filesystem
+ */
+int
+union_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ int error = 0;
+ struct union_args args;
+ struct vnode *lowerrootvp = NULLVP;
+ struct vnode *upperrootvp = NULLVP;
+ struct union_mount *um;
+ struct ucred *cred = 0;
+ struct ucred *scred;
+ struct vattr va;
+ char *cp = 0;
+ int len;
+ u_int size;
+
+#ifdef UNION_DIAGNOSTIC
+ printf("union_mount(mp = %x)\n", mp);
+#endif
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ /*
+ * Need to provide.
+ * 1. a way to convert between rdonly and rdwr mounts.
+ * 2. support for nfs exports.
+ */
+ error = EOPNOTSUPP;
+ goto bad;
+ }
+
+ /*
+ * Take a copy of the process's credentials. This isn't
+ * quite right since the euid will always be zero and we
+ * want to get the "real" users credentials. So fix up
+ * the uid field after taking the copy.
+ */
+ cred = crdup(p->p_ucred);
+ cred->cr_uid = p->p_cred->p_ruid;
+
+ /*
+ * Ensure the *real* user has write permission on the
+ * mounted-on directory. This allows the mount_union
+ * command to be made setuid root so allowing anyone
+ * to do union mounts onto any directory on which they
+ * have write permission and which they also own.
+ */
+ error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p);
+ if (error)
+ goto bad;
+ if ((va.va_uid != cred->cr_uid) &&
+ (cred->cr_uid != 0)) {
+ error = EACCES;
+ goto bad;
+ }
+ error = VOP_ACCESS(mp->mnt_vnodecovered, VWRITE, cred, p);
+ if (error)
+ goto bad;
+
+ /*
+ * Get argument
+ */
+ if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args)))
+ goto bad;
+
+ lowerrootvp = mp->mnt_vnodecovered;
+ VREF(lowerrootvp);
+
+ /*
+ * Find upper node. Use the real process credentials,
+ * not the effective ones since this will have come
+ * through a setuid process (mount_union). All this
+ * messing around with permissions is entirely bogus
+ * and should be removed by allowing any user straight
+ * past the mount system call.
+ */
+ scred = p->p_ucred;
+ p->p_ucred = cred;
+ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
+ UIO_USERSPACE, args.target, p);
+ p->p_ucred = scred;
+
+ if (error = namei(ndp))
+ goto bad;
+
+ upperrootvp = ndp->ni_vp;
+ vrele(ndp->ni_dvp);
+ ndp->ni_dvp = NULL;
+
+ if (upperrootvp->v_type != VDIR) {
+ error = EINVAL;
+ goto bad;
+ }
+
+ um = (struct union_mount *) malloc(sizeof(struct union_mount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+
+ /*
+ * Keep a held reference to the target vnodes.
+ * They are vrele'd in union_unmount.
+ *
+ * Depending on the _BELOW flag, the filesystems are
+ * viewed in a different order. In effect, this is the
+ * same as providing a mount under option to the mount syscall.
+ */
+
+ um->um_op = args.mntflags & UNMNT_OPMASK;
+ switch (um->um_op) {
+ case UNMNT_ABOVE:
+ um->um_lowervp = lowerrootvp;
+ um->um_uppervp = upperrootvp;
+ break;
+
+ case UNMNT_BELOW:
+ um->um_lowervp = upperrootvp;
+ um->um_uppervp = lowerrootvp;
+ break;
+
+ case UNMNT_REPLACE:
+ vrele(lowerrootvp);
+ lowerrootvp = NULLVP;
+ um->um_uppervp = upperrootvp;
+ um->um_lowervp = lowerrootvp;
+ break;
+
+ default:
+ error = EINVAL;
+ goto bad;
+ }
+
+ um->um_cred = cred;
+ um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
+
+ /*
+ * Depending on what you think the MNT_LOCAL flag might mean,
+ * you may want the && to be || on the conditional below.
+ * At the moment it has been defined that the filesystem is
+ * only local if it is all local, ie the MNT_LOCAL flag implies
+ * that the entire namespace is local. If you think the MNT_LOCAL
+ * flag implies that some of the files might be stored locally
+ * then you will want to change the conditional.
+ */
+ if (um->um_op == UNMNT_ABOVE) {
+ if (((um->um_lowervp == NULLVP) ||
+ (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
+ (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
+ mp->mnt_flag |= MNT_LOCAL;
+ }
+
+ /*
+ * Copy in the upper layer's RDONLY flag. This is for the benefit
+ * of lookup() which explicitly checks the flag, rather than asking
+ * the filesystem for it's own opinion. This means, that an update
+ * mount of the underlying filesystem to go from rdonly to rdwr
+ * will leave the unioned view as read-only.
+ */
+ mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
+
+ /*
+ * This is a user mount. Privilege check for unmount
+ * will be done in union_unmount.
+ */
+ mp->mnt_flag |= MNT_USER;
+
+ mp->mnt_data = (qaddr_t) um;
+ getnewfsid(mp, MOUNT_UNION);
+
+ (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+
+ switch (um->um_op) {
+ case UNMNT_ABOVE:
+ cp = "<above>";
+ break;
+ case UNMNT_BELOW:
+ cp = "<below>";
+ break;
+ case UNMNT_REPLACE:
+ cp = "";
+ break;
+ }
+ len = strlen(cp);
+ bcopy(cp, mp->mnt_stat.f_mntfromname, len);
+
+ cp = mp->mnt_stat.f_mntfromname + len;
+ len = MNAMELEN - len;
+
+ (void) copyinstr(args.target, cp, len - 1, &size);
+ bzero(cp + size, len - size);
+
+#ifdef UNION_DIAGNOSTIC
+ printf("union_mount: from %s, on %s\n",
+ mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
+#endif
+ return (0);
+
+bad:
+ if (cred)
+ crfree(cred);
+ if (upperrootvp)
+ vrele(upperrootvp);
+ if (lowerrootvp)
+ vrele(lowerrootvp);
+ return (error);
+}
+
+/*
+ * VFS start. Nothing needed here - the start routine
+ * on the underlying filesystem(s) will have been called
+ * when that filesystem was mounted.
+ */
+int
+union_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+
+ return (0);
+}
+
+/*
+ * Free reference to union layer
+ */
+int
+union_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
+ struct vnode *um_rootvp;
+ int error;
+ int flags = 0;
+ extern int doforce;
+
+#ifdef UNION_DIAGNOSTIC
+ printf("union_unmount(mp = %x)\n", mp);
+#endif
+
+ /* only the mounter, or superuser can unmount */
+ if ((p->p_cred->p_ruid != um->um_cred->cr_uid) &&
+ (error = suser(p->p_ucred, &p->p_acflag)))
+ return (error);
+
+ if (mntflags & MNT_FORCE) {
+ /* union can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ if (error = union_root(mp, &um_rootvp))
+ return (error);
+ if (um_rootvp->v_usecount > 1) {
+ vput(um_rootvp);
+ return (EBUSY);
+ }
+ if (error = vflush(mp, um_rootvp, flags)) {
+ vput(um_rootvp);
+ return (error);
+ }
+
+#ifdef UNION_DIAGNOSTIC
+ vprint("alias root of lower", um_rootvp);
+#endif
+ /*
+ * Discard references to upper and lower target vnodes.
+ */
+ if (um->um_lowervp)
+ vrele(um->um_lowervp);
+ vrele(um->um_uppervp);
+ crfree(um->um_cred);
+ /*
+ * Release reference on underlying root vnode
+ */
+ vput(um_rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(um_rootvp);
+ /*
+ * Finally, throw away the union_mount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ mp->mnt_data = 0;
+ return (0);
+}
+
+int
+union_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
+ int error;
+ int loselock;
+
+#ifdef UNION_DIAGNOSTIC
+ printf("union_root(mp = %x, lvp = %x, uvp = %x)\n", mp,
+ um->um_lowervp,
+ um->um_uppervp);
+#endif
+
+ /*
+ * Return locked reference to root.
+ */
+ VREF(um->um_uppervp);
+ if ((um->um_op == UNMNT_BELOW) &&
+ VOP_ISLOCKED(um->um_uppervp)) {
+ loselock = 1;
+ } else {
+ VOP_LOCK(um->um_uppervp);
+ loselock = 0;
+ }
+ if (um->um_lowervp)
+ VREF(um->um_lowervp);
+ error = union_allocvp(vpp, mp,
+ (struct vnode *) 0,
+ (struct vnode *) 0,
+ (struct componentname *) 0,
+ um->um_uppervp,
+ um->um_lowervp);
+
+ if (error) {
+ if (!loselock)
+ VOP_UNLOCK(um->um_uppervp);
+ vrele(um->um_uppervp);
+ if (um->um_lowervp)
+ vrele(um->um_lowervp);
+ } else {
+ (*vpp)->v_flag |= VROOT;
+ if (loselock)
+ VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
+ }
+
+ return (error);
+}
+
+int
+union_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+union_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ int error;
+ struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
+ struct statfs mstat;
+ int lbsize;
+
+#ifdef UNION_DIAGNOSTIC
+ printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp,
+ um->um_lowervp,
+ um->um_uppervp);
+#endif
+
+ bzero(&mstat, sizeof(mstat));
+
+ if (um->um_lowervp) {
+ error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
+ if (error)
+ return (error);
+ }
+
+ /* now copy across the "interesting" information and fake the rest */
+#if 0
+ sbp->f_type = mstat.f_type;
+ sbp->f_flags = mstat.f_flags;
+ sbp->f_bsize = mstat.f_bsize;
+ sbp->f_iosize = mstat.f_iosize;
+#endif
+ lbsize = mstat.f_bsize;
+ sbp->f_blocks = mstat.f_blocks;
+ sbp->f_bfree = mstat.f_bfree;
+ sbp->f_bavail = mstat.f_bavail;
+ sbp->f_files = mstat.f_files;
+ sbp->f_ffree = mstat.f_ffree;
+
+ error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p);
+ if (error)
+ return (error);
+
+ sbp->f_type = MOUNT_UNION;
+ sbp->f_flags = mstat.f_flags;
+ sbp->f_bsize = mstat.f_bsize;
+ sbp->f_iosize = mstat.f_iosize;
+
+ /*
+ * if the lower and upper blocksizes differ, then frig the
+ * block counts so that the sizes reported by df make some
+ * kind of sense. none of this makes sense though.
+ */
+
+ if (mstat.f_bsize != lbsize) {
+ sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize;
+ sbp->f_bfree = sbp->f_bfree * lbsize / mstat.f_bsize;
+ sbp->f_bavail = sbp->f_bavail * lbsize / mstat.f_bsize;
+ }
+ sbp->f_blocks += mstat.f_blocks;
+ sbp->f_bfree += mstat.f_bfree;
+ sbp->f_bavail += mstat.f_bavail;
+ sbp->f_files += mstat.f_files;
+ sbp->f_ffree += mstat.f_ffree;
+
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+ return (0);
+}
+
+int
+union_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+
+ /*
+ * XXX - Assumes no data cached at union layer.
+ */
+ return (0);
+}
+
+int
+union_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+union_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
+ struct mount *mp;
+ struct fid *fidp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred **credanonp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+union_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int union_init __P((void));
+
+struct vfsops union_vfsops = {
+ union_mount,
+ union_start,
+ union_unmount,
+ union_root,
+ union_quotactl,
+ union_statfs,
+ union_sync,
+ union_vget,
+ union_fhtovp,
+ union_vptofh,
+ union_init,
+};
diff --git a/sys/miscfs/union/union_vnops.c b/sys/miscfs/union/union_vnops.c
new file mode 100644
index 0000000..30f2233
--- /dev/null
+++ b/sys/miscfs/union/union_vnops.c
@@ -0,0 +1,1495 @@
+/*
+ * Copyright (c) 1992, 1993, 1994 The Regents of the University of California.
+ * Copyright (c) 1992, 1993, 1994 Jan-Simon Pendry.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)union_vnops.c 8.6 (Berkeley) 2/17/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/queue.h>
+#include <miscfs/union/union.h>
+
+#define FIXUP(un) { \
+ if (((un)->un_flags & UN_ULOCK) == 0) { \
+ union_fixup(un); \
+ } \
+}
+
+static void
+union_fixup(un)
+ struct union_node *un;
+{
+
+ VOP_LOCK(un->un_uppervp);
+ un->un_flags |= UN_ULOCK;
+}
+
+static int
+union_lookup1(udvp, dvp, vpp, cnp)
+ struct vnode *udvp;
+ struct vnode *dvp;
+ struct vnode **vpp;
+ struct componentname *cnp;
+{
+ int error;
+ struct vnode *tdvp;
+ struct mount *mp;
+
+ /*
+ * If stepping up the directory tree, check for going
+ * back across the mount point, in which case do what
+ * lookup would do by stepping back down the mount
+ * hierarchy.
+ */
+ if (cnp->cn_flags & ISDOTDOT) {
+ for (;;) {
+ /*
+ * Don't do the NOCROSSMOUNT check
+ * at this level. By definition,
+ * union fs deals with namespaces, not
+ * filesystems.
+ */
+ if ((dvp->v_flag & VROOT) == 0)
+ break;
+
+ tdvp = dvp;
+ dvp = dvp->v_mount->mnt_vnodecovered;
+ vput(tdvp);
+ VREF(dvp);
+ VOP_LOCK(dvp);
+ }
+ }
+
+ error = VOP_LOOKUP(dvp, &tdvp, cnp);
+ if (error)
+ return (error);
+
+ /*
+ * The parent directory will have been unlocked, unless lookup
+ * found the last component. In which case, re-lock the node
+ * here to allow it to be unlocked again (phew) in union_lookup.
+ */
+ if (dvp != tdvp && !(cnp->cn_flags & ISLASTCN))
+ VOP_LOCK(dvp);
+
+ dvp = tdvp;
+
+ /*
+ * Lastly check if the current node is a mount point in
+ * which case walk up the mount hierarchy making sure not to
+ * bump into the root of the mount tree (ie. dvp != udvp).
+ */
+ while (dvp != udvp && (dvp->v_type == VDIR) &&
+ (mp = dvp->v_mountedhere)) {
+
+ if (mp->mnt_flag & MNT_MLOCK) {
+ mp->mnt_flag |= MNT_MWAIT;
+ sleep((caddr_t) mp, PVFS);
+ continue;
+ }
+
+ if (error = VFS_ROOT(mp, &tdvp)) {
+ vput(dvp);
+ return (error);
+ }
+
+ vput(dvp);
+ dvp = tdvp;
+ }
+
+ *vpp = dvp;
+ return (0);
+}
+
+int
+union_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error;
+ int uerror, lerror;
+ struct vnode *uppervp, *lowervp;
+ struct vnode *upperdvp, *lowerdvp;
+ struct vnode *dvp = ap->a_dvp;
+ struct union_node *dun = VTOUNION(dvp);
+ struct componentname *cnp = ap->a_cnp;
+ int lockparent = cnp->cn_flags & LOCKPARENT;
+ int rdonly = cnp->cn_flags & RDONLY;
+ struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount);
+ struct ucred *saved_cred = 0;
+
+ cnp->cn_flags |= LOCKPARENT;
+
+ upperdvp = dun->un_uppervp;
+ lowerdvp = dun->un_lowervp;
+ uppervp = NULLVP;
+ lowervp = NULLVP;
+
+ /*
+ * do the lookup in the upper level.
+ * if that level comsumes additional pathnames,
+ * then assume that something special is going
+ * on and just return that vnode.
+ */
+ if (upperdvp) {
+ FIXUP(dun);
+ uerror = union_lookup1(um->um_uppervp, upperdvp,
+ &uppervp, cnp);
+ /*if (uppervp == upperdvp)
+ dun->un_flags |= UN_KLOCK;*/
+
+ if (cnp->cn_consume != 0) {
+ *ap->a_vpp = uppervp;
+ if (!lockparent)
+ cnp->cn_flags &= ~LOCKPARENT;
+ return (uerror);
+ }
+ } else {
+ uerror = ENOENT;
+ }
+
+ /*
+ * in a similar way to the upper layer, do the lookup
+ * in the lower layer. this time, if there is some
+ * component magic going on, then vput whatever we got
+ * back from the upper layer and return the lower vnode
+ * instead.
+ */
+ if (lowerdvp) {
+ int nameiop;
+
+ VOP_LOCK(lowerdvp);
+
+ /*
+ * Only do a LOOKUP on the bottom node, since
+ * we won't be making changes to it anyway.
+ */
+ nameiop = cnp->cn_nameiop;
+ cnp->cn_nameiop = LOOKUP;
+ if (um->um_op == UNMNT_BELOW) {
+ saved_cred = cnp->cn_cred;
+ cnp->cn_cred = um->um_cred;
+ }
+ lerror = union_lookup1(um->um_lowervp, lowerdvp,
+ &lowervp, cnp);
+ if (um->um_op == UNMNT_BELOW)
+ cnp->cn_cred = saved_cred;
+ cnp->cn_nameiop = nameiop;
+
+ if (lowervp != lowerdvp)
+ VOP_UNLOCK(lowerdvp);
+
+ if (cnp->cn_consume != 0) {
+ if (uppervp) {
+ if (uppervp == upperdvp)
+ vrele(uppervp);
+ else
+ vput(uppervp);
+ uppervp = NULLVP;
+ }
+ *ap->a_vpp = lowervp;
+ if (!lockparent)
+ cnp->cn_flags &= ~LOCKPARENT;
+ return (lerror);
+ }
+ } else {
+ lerror = ENOENT;
+ }
+
+ if (!lockparent)
+ cnp->cn_flags &= ~LOCKPARENT;
+
+ /*
+ * at this point, we have uerror and lerror indicating
+ * possible errors with the lookups in the upper and lower
+ * layers. additionally, uppervp and lowervp are (locked)
+ * references to existing vnodes in the upper and lower layers.
+ *
+ * there are now three cases to consider.
+ * 1. if both layers returned an error, then return whatever
+ * error the upper layer generated.
+ *
+ * 2. if the top layer failed and the bottom layer succeeded
+ * then two subcases occur.
+ * a. the bottom vnode is not a directory, in which
+ * case just return a new union vnode referencing
+ * an empty top layer and the existing bottom layer.
+ * b. the bottom vnode is a directory, in which case
+ * create a new directory in the top-level and
+ * continue as in case 3.
+ *
+ * 3. if the top layer succeeded then return a new union
+ * vnode referencing whatever the new top layer and
+ * whatever the bottom layer returned.
+ */
+
+ *ap->a_vpp = NULLVP;
+
+ /* case 1. */
+ if ((uerror != 0) && (lerror != 0)) {
+ return (uerror);
+ }
+
+ /* case 2. */
+ if (uerror != 0 /* && (lerror == 0) */ ) {
+ if (lowervp->v_type == VDIR) { /* case 2b. */
+ dun->un_flags &= ~UN_ULOCK;
+ VOP_UNLOCK(upperdvp);
+ uerror = union_mkshadow(um, upperdvp, cnp, &uppervp);
+ VOP_LOCK(upperdvp);
+ dun->un_flags |= UN_ULOCK;
+
+ if (uerror) {
+ if (lowervp) {
+ vput(lowervp);
+ lowervp = NULLVP;
+ }
+ return (uerror);
+ }
+ }
+ }
+
+ if (lowervp)
+ VOP_UNLOCK(lowervp);
+
+ error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
+ uppervp, lowervp);
+
+ if (error) {
+ if (uppervp)
+ vput(uppervp);
+ if (lowervp)
+ vrele(lowervp);
+ } else {
+ if (*ap->a_vpp != dvp)
+ if (!lockparent || !(cnp->cn_flags & ISLASTCN))
+ VOP_UNLOCK(dvp);
+ }
+
+ return (error);
+}
+
+int
+union_create(ap)
+ struct vop_create_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_dvp);
+ struct vnode *dvp = un->un_uppervp;
+
+ if (dvp) {
+ int error;
+ struct vnode *vp;
+
+ FIXUP(un);
+
+ VREF(dvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ error = VOP_CREATE(dvp, &vp, ap->a_cnp, ap->a_vap);
+ if (error)
+ return (error);
+
+ error = union_allocvp(
+ ap->a_vpp,
+ ap->a_dvp->v_mount,
+ ap->a_dvp,
+ NULLVP,
+ ap->a_cnp,
+ vp,
+ NULLVP);
+ if (error)
+ vput(vp);
+ return (error);
+ }
+
+ vput(ap->a_dvp);
+ return (EROFS);
+}
+
+int
+union_mknod(ap)
+ struct vop_mknod_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_dvp);
+ struct vnode *dvp = un->un_uppervp;
+
+ if (dvp) {
+ int error;
+ struct vnode *vp;
+
+ FIXUP(un);
+
+ VREF(dvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ error = VOP_MKNOD(dvp, &vp, ap->a_cnp, ap->a_vap);
+ if (error)
+ return (error);
+
+ if (vp) {
+ error = union_allocvp(
+ ap->a_vpp,
+ ap->a_dvp->v_mount,
+ ap->a_dvp,
+ NULLVP,
+ ap->a_cnp,
+ vp,
+ NULLVP);
+ if (error)
+ vput(vp);
+ }
+ return (error);
+ }
+
+ vput(ap->a_dvp);
+ return (EROFS);
+}
+
+int
+union_open(ap)
+ struct vop_open_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *tvp;
+ int mode = ap->a_mode;
+ struct ucred *cred = ap->a_cred;
+ struct proc *p = ap->a_p;
+ int error;
+
+ /*
+ * If there is an existing upper vp then simply open that.
+ */
+ tvp = un->un_uppervp;
+ if (tvp == NULLVP) {
+ /*
+ * If the lower vnode is being opened for writing, then
+ * copy the file contents to the upper vnode and open that,
+ * otherwise can simply open the lower vnode.
+ */
+ tvp = un->un_lowervp;
+ if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) {
+ struct vnode *vp;
+ int i;
+
+ /*
+ * Open the named file in the upper layer. Note that
+ * the file may have come into existence *since* the
+ * lookup was done, since the upper layer may really
+ * be a loopback mount of some other filesystem...
+ * so open the file with exclusive create and barf if
+ * it already exists.
+ * XXX - perhaps should re-lookup the node (once more
+ * with feeling) and simply open that. Who knows.
+ */
+ error = union_vn_create(&vp, un, p);
+ if (error)
+ return (error);
+
+ /* at this point, uppervp is locked */
+ union_newupper(un, vp);
+ un->un_flags |= UN_ULOCK;
+
+ /*
+ * Now, if the file is being opened with truncation,
+ * then the (new) upper vnode is ready to fly,
+ * otherwise the data from the lower vnode must be
+ * copied to the upper layer first. This only works
+ * for regular files (check is made above).
+ */
+ if ((mode & O_TRUNC) == 0) {
+ /*
+ * XXX - should not ignore errors
+ * from VOP_CLOSE
+ */
+ VOP_LOCK(tvp);
+ error = VOP_OPEN(tvp, FREAD, cred, p);
+ if (error == 0) {
+ error = union_copyfile(p, cred,
+ tvp, un->un_uppervp);
+ VOP_UNLOCK(tvp);
+ (void) VOP_CLOSE(tvp, FREAD);
+ } else {
+ VOP_UNLOCK(tvp);
+ }
+
+#ifdef UNION_DIAGNOSTIC
+ if (!error)
+ uprintf("union: copied up %s\n",
+ un->un_path);
+#endif
+ }
+
+ un->un_flags &= ~UN_ULOCK;
+ VOP_UNLOCK(un->un_uppervp);
+ union_vn_close(un->un_uppervp, FWRITE, cred, p);
+ VOP_LOCK(un->un_uppervp);
+ un->un_flags |= UN_ULOCK;
+
+ /*
+ * Subsequent IOs will go to the top layer, so
+ * call close on the lower vnode and open on the
+ * upper vnode to ensure that the filesystem keeps
+ * its references counts right. This doesn't do
+ * the right thing with (cred) and (FREAD) though.
+ * Ignoring error returns is not righ, either.
+ */
+ for (i = 0; i < un->un_openl; i++) {
+ (void) VOP_CLOSE(tvp, FREAD);
+ (void) VOP_OPEN(un->un_uppervp, FREAD, cred, p);
+ }
+ un->un_openl = 0;
+
+ if (error == 0)
+ error = VOP_OPEN(un->un_uppervp, mode, cred, p);
+ return (error);
+ }
+
+ /*
+ * Just open the lower vnode
+ */
+ un->un_openl++;
+ VOP_LOCK(tvp);
+ error = VOP_OPEN(tvp, mode, cred, p);
+ VOP_UNLOCK(tvp);
+
+ return (error);
+ }
+
+ FIXUP(un);
+
+ error = VOP_OPEN(tvp, mode, cred, p);
+
+ return (error);
+}
+
+int
+union_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *vp;
+
+ if (un->un_uppervp) {
+ vp = un->un_uppervp;
+ } else {
+#ifdef UNION_DIAGNOSTIC
+ if (un->un_openl <= 0)
+ panic("union: un_openl cnt");
+#endif
+ --un->un_openl;
+ vp = un->un_lowervp;
+ }
+
+ return (VOP_CLOSE(vp, ap->a_fflag, ap->a_cred, ap->a_p));
+}
+
+/*
+ * Check access permission on the union vnode.
+ * The access check being enforced is to check
+ * against both the underlying vnode, and any
+ * copied vnode. This ensures that no additional
+ * file permissions are given away simply because
+ * the user caused an implicit file copy.
+ */
+int
+union_access(ap)
+ struct vop_access_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_vp);
+ int error = EACCES;
+ struct vnode *vp;
+
+ if (vp = un->un_uppervp) {
+ FIXUP(un);
+ return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p));
+ }
+
+ if (vp = un->un_lowervp) {
+ VOP_LOCK(vp);
+ error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p);
+ if (error == 0) {
+ struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount);
+
+ if (um->um_op == UNMNT_BELOW)
+ error = VOP_ACCESS(vp, ap->a_mode,
+ um->um_cred, ap->a_p);
+ }
+ VOP_UNLOCK(vp);
+ if (error)
+ return (error);
+ }
+
+ return (error);
+}
+
+/*
+ * We handle getattr only to change the fsid.
+ */
+int
+union_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error;
+ struct union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *vp = un->un_uppervp;
+ struct vattr *vap;
+ struct vattr va;
+
+
+ /*
+ * Some programs walk the filesystem hierarchy by counting
+ * links to directories to avoid stat'ing all the time.
+ * This means the link count on directories needs to be "correct".
+ * The only way to do that is to call getattr on both layers
+ * and fix up the link count. The link count will not necessarily
+ * be accurate but will be large enough to defeat the tree walkers.
+ */
+
+ vap = ap->a_vap;
+
+ vp = un->un_uppervp;
+ if (vp != NULLVP) {
+ FIXUP(un);
+ error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
+ if (error)
+ return (error);
+ }
+
+ if (vp == NULLVP) {
+ vp = un->un_lowervp;
+ } else if (vp->v_type == VDIR) {
+ vp = un->un_lowervp;
+ vap = &va;
+ } else {
+ vp = NULLVP;
+ }
+
+ if (vp != NULLVP) {
+ VOP_LOCK(vp);
+ error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
+ VOP_UNLOCK(vp);
+ if (error)
+ return (error);
+ }
+
+ if ((vap != ap->a_vap) && (vap->va_type == VDIR))
+ ap->a_vap->va_nlink += vap->va_nlink;
+
+ vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
+ return (0);
+}
+
+int
+union_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_vp);
+ int error;
+
+ /*
+ * Handle case of truncating lower object to zero size,
+ * by creating a zero length upper object. This is to
+ * handle the case of open with O_TRUNC and O_CREAT.
+ */
+ if ((un->un_uppervp == NULLVP) &&
+ /* assert(un->un_lowervp != NULLVP) */
+ (un->un_lowervp->v_type == VREG) &&
+ (ap->a_vap->va_size == 0)) {
+ struct vnode *vp;
+
+ error = union_vn_create(&vp, un, ap->a_p);
+ if (error)
+ return (error);
+
+ /* at this point, uppervp is locked */
+ union_newupper(un, vp);
+
+ VOP_UNLOCK(vp);
+ union_vn_close(un->un_uppervp, FWRITE, ap->a_cred, ap->a_p);
+ VOP_LOCK(vp);
+ un->un_flags |= UN_ULOCK;
+ }
+
+ /*
+ * Try to set attributes in upper layer,
+ * otherwise return read-only filesystem error.
+ */
+ if (un->un_uppervp != NULLVP) {
+ FIXUP(un);
+ error = VOP_SETATTR(un->un_uppervp, ap->a_vap,
+ ap->a_cred, ap->a_p);
+ } else {
+ error = EROFS;
+ }
+
+ return (error);
+}
+
+int
+union_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
+ if (dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_write(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
+ if (dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (VOP_IOCTL(OTHERVP(ap->a_vp), ap->a_command, ap->a_data,
+ ap->a_fflag, ap->a_cred, ap->a_p));
+}
+
+int
+union_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (VOP_SELECT(OTHERVP(ap->a_vp), ap->a_which, ap->a_fflags,
+ ap->a_cred, ap->a_p));
+}
+
+int
+union_mmap(ap)
+ struct vop_mmap_args /* {
+ struct vnode *a_vp;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (VOP_MMAP(OTHERVP(ap->a_vp), ap->a_fflags,
+ ap->a_cred, ap->a_p));
+}
+
+int
+union_fsync(ap)
+ struct vop_fsync_args /* {
+ struct vnode *a_vp;
+ struct ucred *a_cred;
+ int a_waitfor;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error = 0;
+ struct vnode *targetvp = OTHERVP(ap->a_vp);
+
+ if (targetvp) {
+ int dolock = (targetvp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(targetvp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ error = VOP_FSYNC(targetvp, ap->a_cred,
+ ap->a_waitfor, ap->a_p);
+ if (dolock)
+ VOP_UNLOCK(targetvp);
+ }
+
+ return (error);
+}
+
+int
+union_seek(ap)
+ struct vop_seek_args /* {
+ struct vnode *a_vp;
+ off_t a_oldoff;
+ off_t a_newoff;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+
+ return (VOP_SEEK(OTHERVP(ap->a_vp), ap->a_oldoff, ap->a_newoff, ap->a_cred));
+}
+
+int
+union_remove(ap)
+ struct vop_remove_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error;
+ struct union_node *dun = VTOUNION(ap->a_dvp);
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+ if (dun->un_uppervp && un->un_uppervp) {
+ struct vnode *dvp = dun->un_uppervp;
+ struct vnode *vp = un->un_uppervp;
+
+ FIXUP(dun);
+ VREF(dvp);
+ dun->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ FIXUP(un);
+ VREF(vp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_vp);
+
+ error = VOP_REMOVE(dvp, vp, ap->a_cnp);
+ if (!error)
+ union_removed_upper(un);
+
+ /*
+ * XXX: should create a whiteout here
+ */
+ } else {
+ /*
+ * XXX: should create a whiteout here
+ */
+ vput(ap->a_dvp);
+ vput(ap->a_vp);
+ error = EROFS;
+ }
+
+ return (error);
+}
+
+int
+union_link(ap)
+ struct vop_link_args /* {
+ struct vnode *a_vp;
+ struct vnode *a_tdvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error;
+ struct union_node *dun = VTOUNION(ap->a_vp);
+ struct union_node *un = VTOUNION(ap->a_tdvp);
+
+ if (dun->un_uppervp && un->un_uppervp) {
+ struct vnode *dvp = dun->un_uppervp;
+ struct vnode *vp = un->un_uppervp;
+
+ FIXUP(dun);
+ VREF(dvp);
+ dun->un_flags |= UN_KLOCK;
+ vput(ap->a_vp);
+ FIXUP(un);
+ VREF(vp);
+ vrele(ap->a_tdvp);
+
+ error = VOP_LINK(dvp, vp, ap->a_cnp);
+ } else {
+ /*
+ * XXX: need to copy to upper layer
+ * and do the link there.
+ */
+ vput(ap->a_vp);
+ vrele(ap->a_tdvp);
+ error = EROFS;
+ }
+
+ return (error);
+}
+
+int
+union_rename(ap)
+ struct vop_rename_args /* {
+ struct vnode *a_fdvp;
+ struct vnode *a_fvp;
+ struct componentname *a_fcnp;
+ struct vnode *a_tdvp;
+ struct vnode *a_tvp;
+ struct componentname *a_tcnp;
+ } */ *ap;
+{
+ int error;
+
+ struct vnode *fdvp = ap->a_fdvp;
+ struct vnode *fvp = ap->a_fvp;
+ struct vnode *tdvp = ap->a_tdvp;
+ struct vnode *tvp = ap->a_tvp;
+
+ if (fdvp->v_op == union_vnodeop_p) { /* always true */
+ struct union_node *un = VTOUNION(fdvp);
+ if (un->un_uppervp == NULLVP) {
+ error = EROFS;
+ goto bad;
+ }
+
+ FIXUP(un);
+ fdvp = un->un_uppervp;
+ VREF(fdvp);
+ vrele(ap->a_fdvp);
+ }
+
+ if (fvp->v_op == union_vnodeop_p) { /* always true */
+ struct union_node *un = VTOUNION(fvp);
+ if (un->un_uppervp == NULLVP) {
+ error = EROFS;
+ goto bad;
+ }
+
+ FIXUP(un);
+ fvp = un->un_uppervp;
+ VREF(fvp);
+ vrele(ap->a_fvp);
+ }
+
+ if (tdvp->v_op == union_vnodeop_p) {
+ struct union_node *un = VTOUNION(tdvp);
+ if (un->un_uppervp == NULLVP) {
+ error = EROFS;
+ goto bad;
+ }
+
+ tdvp = un->un_uppervp;
+ VREF(tdvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_tdvp);
+ }
+
+ if (tvp && tvp->v_op == union_vnodeop_p) {
+ struct union_node *un = VTOUNION(tvp);
+ if (un->un_uppervp == NULLVP) {
+ error = EROFS;
+ goto bad;
+ }
+
+ tvp = un->un_uppervp;
+ VREF(tvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_tvp);
+ }
+
+ return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp));
+
+bad:
+ vrele(fdvp);
+ vrele(fvp);
+ vput(tdvp);
+ if (tvp)
+ vput(tvp);
+
+ return (error);
+}
+
+int
+union_mkdir(ap)
+ struct vop_mkdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_dvp);
+ struct vnode *dvp = un->un_uppervp;
+
+ if (dvp) {
+ int error;
+ struct vnode *vp;
+
+ FIXUP(un);
+ VREF(dvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ error = VOP_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap);
+ if (error)
+ return (error);
+
+ error = union_allocvp(
+ ap->a_vpp,
+ ap->a_dvp->v_mount,
+ ap->a_dvp,
+ NULLVP,
+ ap->a_cnp,
+ vp,
+ NULLVP);
+ if (error)
+ vput(vp);
+ return (error);
+ }
+
+ vput(ap->a_dvp);
+ return (EROFS);
+}
+
+int
+union_rmdir(ap)
+ struct vop_rmdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error;
+ struct union_node *dun = VTOUNION(ap->a_dvp);
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+ if (dun->un_uppervp && un->un_uppervp) {
+ struct vnode *dvp = dun->un_uppervp;
+ struct vnode *vp = un->un_uppervp;
+
+ FIXUP(dun);
+ VREF(dvp);
+ dun->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ FIXUP(un);
+ VREF(vp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_vp);
+
+ error = VOP_RMDIR(dvp, vp, ap->a_cnp);
+ if (!error)
+ union_removed_upper(un);
+
+ /*
+ * XXX: should create a whiteout here
+ */
+ } else {
+ /*
+ * XXX: should create a whiteout here
+ */
+ vput(ap->a_dvp);
+ vput(ap->a_vp);
+ error = EROFS;
+ }
+
+ return (error);
+}
+
+int
+union_symlink(ap)
+ struct vop_symlink_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ char *a_target;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_dvp);
+ struct vnode *dvp = un->un_uppervp;
+
+ if (dvp) {
+ int error;
+ struct vnode *vp;
+ struct mount *mp = ap->a_dvp->v_mount;
+
+ FIXUP(un);
+ VREF(dvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ error = VOP_SYMLINK(dvp, &vp, ap->a_cnp,
+ ap->a_vap, ap->a_target);
+ *ap->a_vpp = NULLVP;
+ return (error);
+ }
+
+ vput(ap->a_dvp);
+ return (EROFS);
+}
+
+/*
+ * union_readdir works in concert with getdirentries and
+ * readdir(3) to provide a list of entries in the unioned
+ * directories. getdirentries is responsible for walking
+ * down the union stack. readdir(3) is responsible for
+ * eliminating duplicate names from the returned data stream.
+ */
+int
+union_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error = 0;
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+ if (un->un_uppervp) {
+ FIXUP(un);
+ error = VOP_READDIR(un->un_uppervp, ap->a_uio, ap->a_cred);
+ }
+
+ return (error);
+}
+
+int
+union_readlink(ap)
+ struct vop_readlink_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
+ if (dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_abortop(ap)
+ struct vop_abortop_args /* {
+ struct vnode *a_dvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_dvp);
+ struct union_node *un = VTOUNION(ap->a_dvp);
+ int islocked = un->un_flags & UN_LOCKED;
+ int dolock = (vp == LOWERVP(ap->a_dvp));
+
+ if (islocked) {
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_dvp));
+ }
+ error = VOP_ABORTOP(vp, ap->a_cnp);
+ if (islocked && dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ /*
+ * Do nothing (and _don't_ bypass).
+ * Wait to vrele lowervp until reclaim,
+ * so that until then our union_node is in the
+ * cache and reusable.
+ *
+ * NEEDSWORK: Someday, consider inactive'ing
+ * the lowervp and then trying to reactivate it
+ * with capabilities (v_id)
+ * like they do in the name lookup cache code.
+ * That's too much work for now.
+ */
+
+#ifdef UNION_DIAGNOSTIC
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+ if (un->un_flags & UN_LOCKED)
+ panic("union: inactivating locked node");
+#endif
+
+ return (0);
+}
+
+int
+union_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ union_freevp(ap->a_vp);
+
+ return (0);
+}
+
+int
+union_lock(ap)
+ struct vop_lock_args *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct union_node *un;
+
+start:
+ while (vp->v_flag & VXLOCK) {
+ vp->v_flag |= VXWANT;
+ sleep((caddr_t)vp, PINOD);
+ }
+
+ un = VTOUNION(vp);
+
+ if (un->un_uppervp) {
+ if ((un->un_flags & UN_ULOCK) == 0) {
+ un->un_flags |= UN_ULOCK;
+ VOP_LOCK(un->un_uppervp);
+ }
+#ifdef DIAGNOSTIC
+ if (un->un_flags & UN_KLOCK)
+ panic("union: dangling upper lock");
+#endif
+ }
+
+ if (un->un_flags & UN_LOCKED) {
+#ifdef DIAGNOSTIC
+ if (curproc && un->un_pid == curproc->p_pid &&
+ un->un_pid > -1 && curproc->p_pid > -1)
+ panic("union: locking against myself");
+#endif
+ un->un_flags |= UN_WANT;
+ sleep((caddr_t) &un->un_flags, PINOD);
+ goto start;
+ }
+
+#ifdef DIAGNOSTIC
+ if (curproc)
+ un->un_pid = curproc->p_pid;
+ else
+ un->un_pid = -1;
+#endif
+
+ un->un_flags |= UN_LOCKED;
+ return (0);
+}
+
+int
+union_unlock(ap)
+ struct vop_lock_args *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+#ifdef DIAGNOSTIC
+ if ((un->un_flags & UN_LOCKED) == 0)
+ panic("union: unlock unlocked node");
+ if (curproc && un->un_pid != curproc->p_pid &&
+ curproc->p_pid > -1 && un->un_pid > -1)
+ panic("union: unlocking other process's union node");
+#endif
+
+ un->un_flags &= ~UN_LOCKED;
+
+ if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK)
+ VOP_UNLOCK(un->un_uppervp);
+
+ un->un_flags &= ~(UN_ULOCK|UN_KLOCK);
+
+ if (un->un_flags & UN_WANT) {
+ un->un_flags &= ~UN_WANT;
+ wakeup((caddr_t) &un->un_flags);
+ }
+
+#ifdef DIAGNOSTIC
+ un->un_pid = 0;
+#endif
+
+ return (0);
+}
+
+int
+union_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp);
+ if (dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+
+ printf("\ttag VT_UNION, vp=%x, uppervp=%x, lowervp=%x\n",
+ vp, UPPERVP(vp), LOWERVP(vp));
+ return (0);
+}
+
+int
+union_islocked(ap)
+ struct vop_islocked_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0);
+}
+
+int
+union_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval);
+ if (dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_advlock(ap)
+ struct vop_advlock_args /* {
+ struct vnode *a_vp;
+ caddr_t a_id;
+ int a_op;
+ struct flock *a_fl;
+ int a_flags;
+ } */ *ap;
+{
+
+ return (VOP_ADVLOCK(OTHERVP(ap->a_vp), ap->a_id, ap->a_op,
+ ap->a_fl, ap->a_flags));
+}
+
+
+/*
+ * XXX - vop_strategy must be hand coded because it has no
+ * vnode in its arguments.
+ * This goes away with a merged VM/buffer cache.
+ */
+int
+union_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ int error;
+ struct vnode *savedvp;
+
+ savedvp = bp->b_vp;
+ bp->b_vp = OTHERVP(bp->b_vp);
+
+#ifdef DIAGNOSTIC
+ if (bp->b_vp == NULLVP)
+ panic("union_strategy: nil vp");
+ if (((bp->b_flags & B_READ) == 0) &&
+ (bp->b_vp == LOWERVP(savedvp)))
+ panic("union_strategy: writing to lowervp");
+#endif
+
+ error = VOP_STRATEGY(bp);
+ bp->b_vp = savedvp;
+
+ return (error);
+}
+
+/*
+ * Global vfs data structures
+ */
+int (**union_vnodeop_p)();
+struct vnodeopv_entry_desc union_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, union_lookup }, /* lookup */
+ { &vop_create_desc, union_create }, /* create */
+ { &vop_mknod_desc, union_mknod }, /* mknod */
+ { &vop_open_desc, union_open }, /* open */
+ { &vop_close_desc, union_close }, /* close */
+ { &vop_access_desc, union_access }, /* access */
+ { &vop_getattr_desc, union_getattr }, /* getattr */
+ { &vop_setattr_desc, union_setattr }, /* setattr */
+ { &vop_read_desc, union_read }, /* read */
+ { &vop_write_desc, union_write }, /* write */
+ { &vop_ioctl_desc, union_ioctl }, /* ioctl */
+ { &vop_select_desc, union_select }, /* select */
+ { &vop_mmap_desc, union_mmap }, /* mmap */
+ { &vop_fsync_desc, union_fsync }, /* fsync */
+ { &vop_seek_desc, union_seek }, /* seek */
+ { &vop_remove_desc, union_remove }, /* remove */
+ { &vop_link_desc, union_link }, /* link */
+ { &vop_rename_desc, union_rename }, /* rename */
+ { &vop_mkdir_desc, union_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, union_rmdir }, /* rmdir */
+ { &vop_symlink_desc, union_symlink }, /* symlink */
+ { &vop_readdir_desc, union_readdir }, /* readdir */
+ { &vop_readlink_desc, union_readlink }, /* readlink */
+ { &vop_abortop_desc, union_abortop }, /* abortop */
+ { &vop_inactive_desc, union_inactive }, /* inactive */
+ { &vop_reclaim_desc, union_reclaim }, /* reclaim */
+ { &vop_lock_desc, union_lock }, /* lock */
+ { &vop_unlock_desc, union_unlock }, /* unlock */
+ { &vop_bmap_desc, union_bmap }, /* bmap */
+ { &vop_strategy_desc, union_strategy }, /* strategy */
+ { &vop_print_desc, union_print }, /* print */
+ { &vop_islocked_desc, union_islocked }, /* islocked */
+ { &vop_pathconf_desc, union_pathconf }, /* pathconf */
+ { &vop_advlock_desc, union_advlock }, /* advlock */
+#ifdef notdef
+ { &vop_blkatoff_desc, union_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, union_valloc }, /* valloc */
+ { &vop_vfree_desc, union_vfree }, /* vfree */
+ { &vop_truncate_desc, union_truncate }, /* truncate */
+ { &vop_update_desc, union_update }, /* update */
+ { &vop_bwrite_desc, union_bwrite }, /* bwrite */
+#endif
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc union_vnodeop_opv_desc =
+ { &union_vnodeop_p, union_vnodeop_entries };
diff --git a/sys/net/bpf.c b/sys/net/bpf.c
new file mode 100644
index 0000000..e40b769
--- /dev/null
+++ b/sys/net/bpf.c
@@ -0,0 +1,1316 @@
+/*
+ * Copyright (c) 1990, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from the Stanford/CMU enet packet filter,
+ * (net/enet.c) distributed as part of 4.3BSD, and code contributed
+ * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
+ * Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)bpf.c 8.2 (Berkeley) 3/28/94
+ *
+ * static char rcsid[] =
+ * "$Header: bpf.c,v 1.33 91/10/27 21:21:58 mccanne Exp $";
+ */
+
+#include "bpfilter.h"
+
+#if NBPFILTER > 0
+
+#ifndef __GNUC__
+#define inline
+#else
+#define inline __inline
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/buf.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/ioctl.h>
+#include <sys/map.h>
+
+#include <sys/file.h>
+#if defined(sparc) && BSD < 199103
+#include <sys/stream.h>
+#endif
+#include <sys/tty.h>
+#include <sys/uio.h>
+
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+
+#include <sys/errno.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <sys/kernel.h>
+
+/*
+ * Older BSDs don't have kernel malloc.
+ */
+#if BSD < 199103
+extern bcopy();
+static caddr_t bpf_alloc();
+#include <net/bpf_compat.h>
+#define BPF_BUFSIZE (MCLBYTES-8)
+#define UIOMOVE(cp, len, code, uio) uiomove(cp, len, code, uio)
+#else
+#define BPF_BUFSIZE 4096
+#define UIOMOVE(cp, len, code, uio) uiomove(cp, len, uio)
+#endif
+
+#define PRINET 26 /* interruptible */
+
+/*
+ * The default read buffer size is patchable.
+ */
+int bpf_bufsize = BPF_BUFSIZE;
+
+/*
+ * bpf_iflist is the list of interfaces; each corresponds to an ifnet
+ * bpf_dtab holds the descriptors, indexed by minor device #
+ */
+struct bpf_if *bpf_iflist;
+struct bpf_d bpf_dtab[NBPFILTER];
+
+#if BSD >= 199207
+/*
+ * bpfilterattach() is called at boot time in new systems. We do
+ * nothing here since old systems will not call this.
+ */
+/* ARGSUSED */
+void
+bpfilterattach(n)
+ int n;
+{
+}
+#endif
+
+static int bpf_allocbufs __P((struct bpf_d *));
+static int bpf_allocbufs __P((struct bpf_d *));
+static void bpf_freed __P((struct bpf_d *));
+static void bpf_freed __P((struct bpf_d *));
+static void bpf_ifname __P((struct ifnet *, struct ifreq *));
+static void bpf_ifname __P((struct ifnet *, struct ifreq *));
+static void bpf_mcopy __P((const void *, void *, u_int));
+static int bpf_movein __P((struct uio *, int,
+ struct mbuf **, struct sockaddr *, int *));
+static int bpf_setif __P((struct bpf_d *, struct ifreq *));
+static int bpf_setif __P((struct bpf_d *, struct ifreq *));
+static inline void
+ bpf_wakeup __P((struct bpf_d *));
+static void catchpacket __P((struct bpf_d *, u_char *, u_int,
+ u_int, void (*)(const void *, void *, u_int)));
+static void reset_d __P((struct bpf_d *));
+
+static int
+bpf_movein(uio, linktype, mp, sockp, datlen)
+ register struct uio *uio;
+ int linktype, *datlen;
+ register struct mbuf **mp;
+ register struct sockaddr *sockp;
+{
+ struct mbuf *m;
+ int error;
+ int len;
+ int hlen;
+
+ /*
+ * Build a sockaddr based on the data link layer type.
+ * We do this at this level because the ethernet header
+ * is copied directly into the data field of the sockaddr.
+ * In the case of SLIP, there is no header and the packet
+ * is forwarded as is.
+ * Also, we are careful to leave room at the front of the mbuf
+ * for the link level header.
+ */
+ switch (linktype) {
+
+ case DLT_SLIP:
+ sockp->sa_family = AF_INET;
+ hlen = 0;
+ break;
+
+ case DLT_EN10MB:
+ sockp->sa_family = AF_UNSPEC;
+ /* XXX Would MAXLINKHDR be better? */
+ hlen = sizeof(struct ether_header);
+ break;
+
+ case DLT_FDDI:
+ sockp->sa_family = AF_UNSPEC;
+ /* XXX 4(FORMAC)+6(dst)+6(src)+3(LLC)+5(SNAP) */
+ hlen = 24;
+ break;
+
+ case DLT_NULL:
+ sockp->sa_family = AF_UNSPEC;
+ hlen = 0;
+ break;
+
+ default:
+ return (EIO);
+ }
+
+ len = uio->uio_resid;
+ *datlen = len - hlen;
+ if ((unsigned)len > MCLBYTES)
+ return (EIO);
+
+ MGET(m, M_WAIT, MT_DATA);
+ if (m == 0)
+ return (ENOBUFS);
+ if (len > MLEN) {
+#if BSD >= 199103
+ MCLGET(m, M_WAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+#else
+ MCLGET(m);
+ if (m->m_len != MCLBYTES) {
+#endif
+ error = ENOBUFS;
+ goto bad;
+ }
+ }
+ m->m_len = len;
+ *mp = m;
+ /*
+ * Make room for link header.
+ */
+ if (hlen != 0) {
+ m->m_len -= hlen;
+#if BSD >= 199103
+ m->m_data += hlen; /* XXX */
+#else
+ m->m_off += hlen;
+#endif
+ error = UIOMOVE((caddr_t)sockp->sa_data, hlen, UIO_WRITE, uio);
+ if (error)
+ goto bad;
+ }
+ error = UIOMOVE(mtod(m, caddr_t), len - hlen, UIO_WRITE, uio);
+ if (!error)
+ return (0);
+ bad:
+ m_freem(m);
+ return (error);
+}
+
+/*
+ * Attach file to the bpf interface, i.e. make d listen on bp.
+ * Must be called at splimp.
+ */
+static void
+bpf_attachd(d, bp)
+ struct bpf_d *d;
+ struct bpf_if *bp;
+{
+ /*
+ * Point d at bp, and add d to the interface's list of listeners.
+ * Finally, point the driver's bpf cookie at the interface so
+ * it will divert packets to bpf.
+ */
+ d->bd_bif = bp;
+ d->bd_next = bp->bif_dlist;
+ bp->bif_dlist = d;
+
+ *bp->bif_driverp = bp;
+}
+
+/*
+ * Detach a file from its interface.
+ */
+static void
+bpf_detachd(d)
+ struct bpf_d *d;
+{
+ struct bpf_d **p;
+ struct bpf_if *bp;
+
+ bp = d->bd_bif;
+ /*
+ * Check if this descriptor had requested promiscuous mode.
+ * If so, turn it off.
+ */
+ if (d->bd_promisc) {
+ d->bd_promisc = 0;
+ if (ifpromisc(bp->bif_ifp, 0))
+ /*
+ * Something is really wrong if we were able to put
+ * the driver into promiscuous mode, but can't
+ * take it out.
+ */
+ panic("bpf: ifpromisc failed");
+ }
+ /* Remove d from the interface's descriptor list. */
+ p = &bp->bif_dlist;
+ while (*p != d) {
+ p = &(*p)->bd_next;
+ if (*p == 0)
+ panic("bpf_detachd: descriptor not in list");
+ }
+ *p = (*p)->bd_next;
+ if (bp->bif_dlist == 0)
+ /*
+ * Let the driver know that there are no more listeners.
+ */
+ *d->bd_bif->bif_driverp = 0;
+ d->bd_bif = 0;
+}
+
+
+/*
+ * Mark a descriptor free by making it point to itself.
+ * This is probably cheaper than marking with a constant since
+ * the address should be in a register anyway.
+ */
+#define D_ISFREE(d) ((d) == (d)->bd_next)
+#define D_MARKFREE(d) ((d)->bd_next = (d))
+#define D_MARKUSED(d) ((d)->bd_next = 0)
+
+/*
+ * Open ethernet device. Returns ENXIO for illegal minor device number,
+ * EBUSY if file is open by another process.
+ */
+/* ARGSUSED */
+int
+bpfopen(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ register struct bpf_d *d;
+
+ if (minor(dev) >= NBPFILTER)
+ return (ENXIO);
+ /*
+ * Each minor can be opened by only one process. If the requested
+ * minor is in use, return EBUSY.
+ */
+ d = &bpf_dtab[minor(dev)];
+ if (!D_ISFREE(d))
+ return (EBUSY);
+
+ /* Mark "free" and do most initialization. */
+ bzero((char *)d, sizeof(*d));
+ d->bd_bufsize = bpf_bufsize;
+
+ return (0);
+}
+
+/*
+ * Close the descriptor by detaching it from its interface,
+ * deallocating its buffers, and marking it free.
+ */
+/* ARGSUSED */
+int
+bpfclose(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ register struct bpf_d *d = &bpf_dtab[minor(dev)];
+ register int s;
+
+ s = splimp();
+ if (d->bd_bif)
+ bpf_detachd(d);
+ splx(s);
+ bpf_freed(d);
+
+ return (0);
+}
+
+/*
+ * Support for SunOS, which does not have tsleep.
+ */
+#if BSD < 199103
+static
+bpf_timeout(arg)
+ caddr_t arg;
+{
+ struct bpf_d *d = (struct bpf_d *)arg;
+ d->bd_timedout = 1;
+ wakeup(arg);
+}
+
+#define BPF_SLEEP(chan, pri, s, t) bpf_sleep((struct bpf_d *)chan)
+
+int
+bpf_sleep(d)
+ register struct bpf_d *d;
+{
+ register int rto = d->bd_rtout;
+ register int st;
+
+ if (rto != 0) {
+ d->bd_timedout = 0;
+ timeout(bpf_timeout, (caddr_t)d, rto);
+ }
+ st = sleep((caddr_t)d, PRINET|PCATCH);
+ if (rto != 0) {
+ if (d->bd_timedout == 0)
+ untimeout(bpf_timeout, (caddr_t)d);
+ else if (st == 0)
+ return EWOULDBLOCK;
+ }
+ return (st != 0) ? EINTR : 0;
+}
+#else
+#define BPF_SLEEP tsleep
+#endif
+
+/*
+ * Rotate the packet buffers in descriptor d. Move the store buffer
+ * into the hold slot, and the free buffer into the store slot.
+ * Zero the length of the new store buffer.
+ */
+#define ROTATE_BUFFERS(d) \
+ (d)->bd_hbuf = (d)->bd_sbuf; \
+ (d)->bd_hlen = (d)->bd_slen; \
+ (d)->bd_sbuf = (d)->bd_fbuf; \
+ (d)->bd_slen = 0; \
+ (d)->bd_fbuf = 0;
+/*
+ * bpfread - read next chunk of packets from buffers
+ */
+int
+bpfread(dev, uio)
+ dev_t dev;
+ register struct uio *uio;
+{
+ register struct bpf_d *d = &bpf_dtab[minor(dev)];
+ int error;
+ int s;
+
+ /*
+ * Restrict application to use a buffer the same size as
+ * as kernel buffers.
+ */
+ if (uio->uio_resid != d->bd_bufsize)
+ return (EINVAL);
+
+ s = splimp();
+ /*
+ * If the hold buffer is empty, then do a timed sleep, which
+ * ends when the timeout expires or when enough packets
+ * have arrived to fill the store buffer.
+ */
+ while (d->bd_hbuf == 0) {
+ if (d->bd_immediate && d->bd_slen != 0) {
+ /*
+ * A packet(s) either arrived since the previous
+ * read or arrived while we were asleep.
+ * Rotate the buffers and return what's here.
+ */
+ ROTATE_BUFFERS(d);
+ break;
+ }
+ error = BPF_SLEEP((caddr_t)d, PRINET|PCATCH, "bpf",
+ d->bd_rtout);
+ if (error == EINTR || error == ERESTART) {
+ splx(s);
+ return (error);
+ }
+ if (error == EWOULDBLOCK) {
+ /*
+ * On a timeout, return what's in the buffer,
+ * which may be nothing. If there is something
+ * in the store buffer, we can rotate the buffers.
+ */
+ if (d->bd_hbuf)
+ /*
+ * We filled up the buffer in between
+ * getting the timeout and arriving
+ * here, so we don't need to rotate.
+ */
+ break;
+
+ if (d->bd_slen == 0) {
+ splx(s);
+ return (0);
+ }
+ ROTATE_BUFFERS(d);
+ break;
+ }
+ }
+ /*
+ * At this point, we know we have something in the hold slot.
+ */
+ splx(s);
+
+ /*
+ * Move data from hold buffer into user space.
+ * We know the entire buffer is transferred since
+ * we checked above that the read buffer is bpf_bufsize bytes.
+ */
+ error = UIOMOVE(d->bd_hbuf, d->bd_hlen, UIO_READ, uio);
+
+ s = splimp();
+ d->bd_fbuf = d->bd_hbuf;
+ d->bd_hbuf = 0;
+ d->bd_hlen = 0;
+ splx(s);
+
+ return (error);
+}
+
+
+/*
+ * If there are processes sleeping on this descriptor, wake them up.
+ */
+static inline void
+bpf_wakeup(d)
+ register struct bpf_d *d;
+{
+ wakeup((caddr_t)d);
+#if BSD >= 199103
+ selwakeup(&d->bd_sel);
+ /* XXX */
+ d->bd_sel.si_pid = 0;
+#else
+ if (d->bd_selproc) {
+ selwakeup(d->bd_selproc, (int)d->bd_selcoll);
+ d->bd_selcoll = 0;
+ d->bd_selproc = 0;
+ }
+#endif
+}
+
+int
+bpfwrite(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ register struct bpf_d *d = &bpf_dtab[minor(dev)];
+ struct ifnet *ifp;
+ struct mbuf *m;
+ int error, s;
+ static struct sockaddr dst;
+ int datlen;
+
+ if (d->bd_bif == 0)
+ return (ENXIO);
+
+ ifp = d->bd_bif->bif_ifp;
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ error = bpf_movein(uio, (int)d->bd_bif->bif_dlt, &m, &dst, &datlen);
+ if (error)
+ return (error);
+
+ if (datlen > ifp->if_mtu)
+ return (EMSGSIZE);
+
+ s = splnet();
+#if BSD >= 199103
+ error = (*ifp->if_output)(ifp, m, &dst, (struct rtentry *)0);
+#else
+ error = (*ifp->if_output)(ifp, m, &dst);
+#endif
+ splx(s);
+ /*
+ * The driver frees the mbuf.
+ */
+ return (error);
+}
+
+/*
+ * Reset a descriptor by flushing its packet buffer and clearing the
+ * receive and drop counts. Should be called at splimp.
+ */
+static void
+reset_d(d)
+ struct bpf_d *d;
+{
+ if (d->bd_hbuf) {
+ /* Free the hold buffer. */
+ d->bd_fbuf = d->bd_hbuf;
+ d->bd_hbuf = 0;
+ }
+ d->bd_slen = 0;
+ d->bd_hlen = 0;
+ d->bd_rcount = 0;
+ d->bd_dcount = 0;
+}
+
+/*
+ * FIONREAD Check for read packet available.
+ * SIOCGIFADDR Get interface address - convenient hook to driver.
+ * BIOCGBLEN Get buffer len [for read()].
+ * BIOCSETF Set ethernet read filter.
+ * BIOCFLUSH Flush read packet buffer.
+ * BIOCPROMISC Put interface into promiscuous mode.
+ * BIOCGDLT Get link layer type.
+ * BIOCGETIF Get interface name.
+ * BIOCSETIF Set interface.
+ * BIOCSRTIMEOUT Set read timeout.
+ * BIOCGRTIMEOUT Get read timeout.
+ * BIOCGSTATS Get packet stats.
+ * BIOCIMMEDIATE Set immediate mode.
+ * BIOCVERSION Get filter language version.
+ */
+/* ARGSUSED */
+int
+bpfioctl(dev, cmd, addr, flag)
+ dev_t dev;
+ int cmd;
+ caddr_t addr;
+ int flag;
+{
+ register struct bpf_d *d = &bpf_dtab[minor(dev)];
+ int s, error = 0;
+
+ switch (cmd) {
+
+ default:
+ error = EINVAL;
+ break;
+
+ /*
+ * Check for read packet available.
+ */
+ case FIONREAD:
+ {
+ int n;
+
+ s = splimp();
+ n = d->bd_slen;
+ if (d->bd_hbuf)
+ n += d->bd_hlen;
+ splx(s);
+
+ *(int *)addr = n;
+ break;
+ }
+
+ case SIOCGIFADDR:
+ {
+ struct ifnet *ifp;
+
+ if (d->bd_bif == 0)
+ error = EINVAL;
+ else {
+ ifp = d->bd_bif->bif_ifp;
+ error = (*ifp->if_ioctl)(ifp, cmd, addr);
+ }
+ break;
+ }
+
+ /*
+ * Get buffer len [for read()].
+ */
+ case BIOCGBLEN:
+ *(u_int *)addr = d->bd_bufsize;
+ break;
+
+ /*
+ * Set buffer length.
+ */
+ case BIOCSBLEN:
+#if BSD < 199103
+ error = EINVAL;
+#else
+ if (d->bd_bif != 0)
+ error = EINVAL;
+ else {
+ register u_int size = *(u_int *)addr;
+
+ if (size > BPF_MAXBUFSIZE)
+ *(u_int *)addr = size = BPF_MAXBUFSIZE;
+ else if (size < BPF_MINBUFSIZE)
+ *(u_int *)addr = size = BPF_MINBUFSIZE;
+ d->bd_bufsize = size;
+ }
+#endif
+ break;
+
+ /*
+ * Set link layer read filter.
+ */
+ case BIOCSETF:
+ error = bpf_setf(d, (struct bpf_program *)addr);
+ break;
+
+ /*
+ * Flush read packet buffer.
+ */
+ case BIOCFLUSH:
+ s = splimp();
+ reset_d(d);
+ splx(s);
+ break;
+
+ /*
+ * Put interface into promiscuous mode.
+ */
+ case BIOCPROMISC:
+ if (d->bd_bif == 0) {
+ /*
+ * No interface attached yet.
+ */
+ error = EINVAL;
+ break;
+ }
+ s = splimp();
+ if (d->bd_promisc == 0) {
+ error = ifpromisc(d->bd_bif->bif_ifp, 1);
+ if (error == 0)
+ d->bd_promisc = 1;
+ }
+ splx(s);
+ break;
+
+ /*
+ * Get device parameters.
+ */
+ case BIOCGDLT:
+ if (d->bd_bif == 0)
+ error = EINVAL;
+ else
+ *(u_int *)addr = d->bd_bif->bif_dlt;
+ break;
+
+ /*
+ * Set interface name.
+ */
+ case BIOCGETIF:
+ if (d->bd_bif == 0)
+ error = EINVAL;
+ else
+ bpf_ifname(d->bd_bif->bif_ifp, (struct ifreq *)addr);
+ break;
+
+ /*
+ * Set interface.
+ */
+ case BIOCSETIF:
+ error = bpf_setif(d, (struct ifreq *)addr);
+ break;
+
+ /*
+ * Set read timeout.
+ */
+ case BIOCSRTIMEOUT:
+ {
+ struct timeval *tv = (struct timeval *)addr;
+ u_long msec;
+
+ /* Compute number of milliseconds. */
+ msec = tv->tv_sec * 1000 + tv->tv_usec / 1000;
+ /* Scale milliseconds to ticks. Assume hard
+ clock has millisecond or greater resolution
+ (i.e. tick >= 1000). For 10ms hardclock,
+ tick/1000 = 10, so rtout<-msec/10. */
+ d->bd_rtout = msec / (tick / 1000);
+ break;
+ }
+
+ /*
+ * Get read timeout.
+ */
+ case BIOCGRTIMEOUT:
+ {
+ struct timeval *tv = (struct timeval *)addr;
+ u_long msec = d->bd_rtout;
+
+ msec *= tick / 1000;
+ tv->tv_sec = msec / 1000;
+ tv->tv_usec = msec % 1000;
+ break;
+ }
+
+ /*
+ * Get packet stats.
+ */
+ case BIOCGSTATS:
+ {
+ struct bpf_stat *bs = (struct bpf_stat *)addr;
+
+ bs->bs_recv = d->bd_rcount;
+ bs->bs_drop = d->bd_dcount;
+ break;
+ }
+
+ /*
+ * Set immediate mode.
+ */
+ case BIOCIMMEDIATE:
+ d->bd_immediate = *(u_int *)addr;
+ break;
+
+ case BIOCVERSION:
+ {
+ struct bpf_version *bv = (struct bpf_version *)addr;
+
+ bv->bv_major = BPF_MAJOR_VERSION;
+ bv->bv_minor = BPF_MINOR_VERSION;
+ break;
+ }
+ }
+ return (error);
+}
+
+/*
+ * Set d's packet filter program to fp. If this file already has a filter,
+ * free it and replace it. Returns EINVAL for bogus requests.
+ */
+int
+bpf_setf(d, fp)
+ struct bpf_d *d;
+ struct bpf_program *fp;
+{
+ struct bpf_insn *fcode, *old;
+ u_int flen, size;
+ int s;
+
+ old = d->bd_filter;
+ if (fp->bf_insns == 0) {
+ if (fp->bf_len != 0)
+ return (EINVAL);
+ s = splimp();
+ d->bd_filter = 0;
+ reset_d(d);
+ splx(s);
+ if (old != 0)
+ free((caddr_t)old, M_DEVBUF);
+ return (0);
+ }
+ flen = fp->bf_len;
+ if (flen > BPF_MAXINSNS)
+ return (EINVAL);
+
+ size = flen * sizeof(*fp->bf_insns);
+ fcode = (struct bpf_insn *)malloc(size, M_DEVBUF, M_WAITOK);
+ if (copyin((caddr_t)fp->bf_insns, (caddr_t)fcode, size) == 0 &&
+ bpf_validate(fcode, (int)flen)) {
+ s = splimp();
+ d->bd_filter = fcode;
+ reset_d(d);
+ splx(s);
+ if (old != 0)
+ free((caddr_t)old, M_DEVBUF);
+
+ return (0);
+ }
+ free((caddr_t)fcode, M_DEVBUF);
+ return (EINVAL);
+}
+
+/*
+ * Detach a file from its current interface (if attached at all) and attach
+ * to the interface indicated by the name stored in ifr.
+ * Return an errno or 0.
+ */
+static int
+bpf_setif(d, ifr)
+ struct bpf_d *d;
+ struct ifreq *ifr;
+{
+ struct bpf_if *bp;
+ char *cp;
+ int unit, s, error;
+
+ /*
+ * Separate string into name part and unit number. Put a null
+ * byte at the end of the name part, and compute the number.
+ * If the a unit number is unspecified, the default is 0,
+ * as initialized above. XXX This should be common code.
+ */
+ unit = 0;
+ cp = ifr->ifr_name;
+ cp[sizeof(ifr->ifr_name) - 1] = '\0';
+ while (*cp++) {
+ if (*cp >= '0' && *cp <= '9') {
+ unit = *cp - '0';
+ *cp++ = '\0';
+ while (*cp)
+ unit = 10 * unit + *cp++ - '0';
+ break;
+ }
+ }
+ /*
+ * Look through attached interfaces for the named one.
+ */
+ for (bp = bpf_iflist; bp != 0; bp = bp->bif_next) {
+ struct ifnet *ifp = bp->bif_ifp;
+
+ if (ifp == 0 || unit != ifp->if_unit
+ || strcmp(ifp->if_name, ifr->ifr_name) != 0)
+ continue;
+ /*
+ * We found the requested interface.
+ * If it's not up, return an error.
+ * Allocate the packet buffers if we need to.
+ * If we're already attached to requested interface,
+ * just flush the buffer.
+ */
+ if ((ifp->if_flags & IFF_UP) == 0)
+ return (ENETDOWN);
+
+ if (d->bd_sbuf == 0) {
+ error = bpf_allocbufs(d);
+ if (error != 0)
+ return (error);
+ }
+ s = splimp();
+ if (bp != d->bd_bif) {
+ if (d->bd_bif)
+ /*
+ * Detach if attached to something else.
+ */
+ bpf_detachd(d);
+
+ bpf_attachd(d, bp);
+ }
+ reset_d(d);
+ splx(s);
+ return (0);
+ }
+ /* Not found. */
+ return (ENXIO);
+}
+
+/*
+ * Convert an interface name plus unit number of an ifp to a single
+ * name which is returned in the ifr.
+ */
+static void
+bpf_ifname(ifp, ifr)
+ struct ifnet *ifp;
+ struct ifreq *ifr;
+{
+ char *s = ifp->if_name;
+ char *d = ifr->ifr_name;
+
+ while (*d++ = *s++)
+ continue;
+ /* XXX Assume that unit number is less than 10. */
+ *d++ = ifp->if_unit + '0';
+ *d = '\0';
+}
+
+/*
+ * The new select interface passes down the proc pointer; the old select
+ * stubs had to grab it out of the user struct. This glue allows either case.
+ */
+#if BSD >= 199103
+#define bpf_select bpfselect
+#else
+int
+bpfselect(dev, rw)
+ register dev_t dev;
+ int rw;
+{
+ return (bpf_select(dev, rw, u.u_procp));
+}
+#endif
+
+/*
+ * Support for select() system call
+ *
+ * Return true iff the specific operation will not block indefinitely.
+ * Otherwise, return false but make a note that a selwakeup() must be done.
+ */
+int
+bpf_select(dev, rw, p)
+ register dev_t dev;
+ int rw;
+ struct proc *p;
+{
+ register struct bpf_d *d;
+ register int s;
+
+ if (rw != FREAD)
+ return (0);
+ /*
+ * An imitation of the FIONREAD ioctl code.
+ */
+ d = &bpf_dtab[minor(dev)];
+
+ s = splimp();
+ if (d->bd_hlen != 0 || (d->bd_immediate && d->bd_slen != 0)) {
+ /*
+ * There is data waiting.
+ */
+ splx(s);
+ return (1);
+ }
+#if BSD >= 199103
+ selrecord(p, &d->bd_sel);
+#else
+ /*
+ * No data ready. If there's already a select() waiting on this
+ * minor device then this is a collision. This shouldn't happen
+ * because minors really should not be shared, but if a process
+ * forks while one of these is open, it is possible that both
+ * processes could select on the same descriptor.
+ */
+ if (d->bd_selproc && d->bd_selproc->p_wchan == (caddr_t)&selwait)
+ d->bd_selcoll = 1;
+ else
+ d->bd_selproc = p;
+#endif
+ splx(s);
+ return (0);
+}
+
+/*
+ * Incoming linkage from device drivers. Process the packet pkt, of length
+ * pktlen, which is stored in a contiguous buffer. The packet is parsed
+ * by each process' filter, and if accepted, stashed into the corresponding
+ * buffer.
+ */
+void
+bpf_tap(arg, pkt, pktlen)
+ caddr_t arg;
+ register u_char *pkt;
+ register u_int pktlen;
+{
+ struct bpf_if *bp;
+ register struct bpf_d *d;
+ register u_int slen;
+ /*
+ * Note that the ipl does not have to be raised at this point.
+ * The only problem that could arise here is that if two different
+ * interfaces shared any data. This is not the case.
+ */
+ bp = (struct bpf_if *)arg;
+ for (d = bp->bif_dlist; d != 0; d = d->bd_next) {
+ ++d->bd_rcount;
+ slen = bpf_filter(d->bd_filter, pkt, pktlen, pktlen);
+ if (slen != 0)
+ catchpacket(d, pkt, pktlen, slen, bcopy);
+ }
+}
+
+/*
+ * Copy data from an mbuf chain into a buffer. This code is derived
+ * from m_copydata in sys/uipc_mbuf.c.
+ */
+static void
+bpf_mcopy(src_arg, dst_arg, len)
+ const void *src_arg;
+ void *dst_arg;
+ register u_int len;
+{
+ register const struct mbuf *m;
+ register u_int count;
+ u_char *dst;
+
+ m = src_arg;
+ dst = dst_arg;
+ while (len > 0) {
+ if (m == 0)
+ panic("bpf_mcopy");
+ count = min(m->m_len, len);
+ bcopy(mtod(m, caddr_t), (caddr_t)dst, count);
+ m = m->m_next;
+ dst += count;
+ len -= count;
+ }
+}
+
+/*
+ * Incoming linkage from device drivers, when packet is in an mbuf chain.
+ */
+void
+bpf_mtap(arg, m)
+ caddr_t arg;
+ struct mbuf *m;
+{
+ struct bpf_if *bp = (struct bpf_if *)arg;
+ struct bpf_d *d;
+ u_int pktlen, slen;
+ struct mbuf *m0;
+
+ pktlen = 0;
+ for (m0 = m; m0 != 0; m0 = m0->m_next)
+ pktlen += m0->m_len;
+
+ for (d = bp->bif_dlist; d != 0; d = d->bd_next) {
+ ++d->bd_rcount;
+ slen = bpf_filter(d->bd_filter, (u_char *)m, pktlen, 0);
+ if (slen != 0)
+ catchpacket(d, (u_char *)m, pktlen, slen, bpf_mcopy);
+ }
+}
+
+/*
+ * Move the packet data from interface memory (pkt) into the
+ * store buffer. Return 1 if it's time to wakeup a listener (buffer full),
+ * otherwise 0. "copy" is the routine called to do the actual data
+ * transfer. bcopy is passed in to copy contiguous chunks, while
+ * bpf_mcopy is passed in to copy mbuf chains. In the latter case,
+ * pkt is really an mbuf.
+ */
+static void
+catchpacket(d, pkt, pktlen, snaplen, cpfn)
+ register struct bpf_d *d;
+ register u_char *pkt;
+ register u_int pktlen, snaplen;
+ register void (*cpfn)(const void *, void *, u_int);
+{
+ register struct bpf_hdr *hp;
+ register int totlen, curlen;
+ register int hdrlen = d->bd_bif->bif_hdrlen;
+ /*
+ * Figure out how many bytes to move. If the packet is
+ * greater or equal to the snapshot length, transfer that
+ * much. Otherwise, transfer the whole packet (unless
+ * we hit the buffer size limit).
+ */
+ totlen = hdrlen + min(snaplen, pktlen);
+ if (totlen > d->bd_bufsize)
+ totlen = d->bd_bufsize;
+
+ /*
+ * Round up the end of the previous packet to the next longword.
+ */
+ curlen = BPF_WORDALIGN(d->bd_slen);
+ if (curlen + totlen > d->bd_bufsize) {
+ /*
+ * This packet will overflow the storage buffer.
+ * Rotate the buffers if we can, then wakeup any
+ * pending reads.
+ */
+ if (d->bd_fbuf == 0) {
+ /*
+ * We haven't completed the previous read yet,
+ * so drop the packet.
+ */
+ ++d->bd_dcount;
+ return;
+ }
+ ROTATE_BUFFERS(d);
+ bpf_wakeup(d);
+ curlen = 0;
+ }
+ else if (d->bd_immediate)
+ /*
+ * Immediate mode is set. A packet arrived so any
+ * reads should be woken up.
+ */
+ bpf_wakeup(d);
+
+ /*
+ * Append the bpf header.
+ */
+ hp = (struct bpf_hdr *)(d->bd_sbuf + curlen);
+#if BSD >= 199103
+ microtime(&hp->bh_tstamp);
+#elif defined(sun)
+ uniqtime(&hp->bh_tstamp);
+#else
+ hp->bh_tstamp = time;
+#endif
+ hp->bh_datalen = pktlen;
+ hp->bh_hdrlen = hdrlen;
+ /*
+ * Copy the packet data into the store buffer and update its length.
+ */
+ (*cpfn)(pkt, (u_char *)hp + hdrlen, (hp->bh_caplen = totlen - hdrlen));
+ d->bd_slen = curlen + totlen;
+}
+
+/*
+ * Initialize all nonzero fields of a descriptor.
+ */
+static int
+bpf_allocbufs(d)
+ register struct bpf_d *d;
+{
+ d->bd_fbuf = (caddr_t)malloc(d->bd_bufsize, M_DEVBUF, M_WAITOK);
+ if (d->bd_fbuf == 0)
+ return (ENOBUFS);
+
+ d->bd_sbuf = (caddr_t)malloc(d->bd_bufsize, M_DEVBUF, M_WAITOK);
+ if (d->bd_sbuf == 0) {
+ free(d->bd_fbuf, M_DEVBUF);
+ return (ENOBUFS);
+ }
+ d->bd_slen = 0;
+ d->bd_hlen = 0;
+ return (0);
+}
+
+/*
+ * Free buffers currently in use by a descriptor.
+ * Called on close.
+ */
+static void
+bpf_freed(d)
+ register struct bpf_d *d;
+{
+ /*
+ * We don't need to lock out interrupts since this descriptor has
+ * been detached from its interface and it yet hasn't been marked
+ * free.
+ */
+ if (d->bd_sbuf != 0) {
+ free(d->bd_sbuf, M_DEVBUF);
+ if (d->bd_hbuf != 0)
+ free(d->bd_hbuf, M_DEVBUF);
+ if (d->bd_fbuf != 0)
+ free(d->bd_fbuf, M_DEVBUF);
+ }
+ if (d->bd_filter)
+ free((caddr_t)d->bd_filter, M_DEVBUF);
+
+ D_MARKFREE(d);
+}
+
+/*
+ * Attach an interface to bpf. driverp is a pointer to a (struct bpf_if *)
+ * in the driver's softc; dlt is the link layer type; hdrlen is the fixed
+ * size of the link header (variable length headers not yet supported).
+ */
+void
+bpfattach(driverp, ifp, dlt, hdrlen)
+ caddr_t *driverp;
+ struct ifnet *ifp;
+ u_int dlt, hdrlen;
+{
+ struct bpf_if *bp;
+ int i;
+#if BSD < 199103
+ static struct bpf_if bpf_ifs[NBPFILTER];
+ static int bpfifno;
+
+ bp = (bpfifno < NBPFILTER) ? &bpf_ifs[bpfifno++] : 0;
+#else
+ bp = (struct bpf_if *)malloc(sizeof(*bp), M_DEVBUF, M_DONTWAIT);
+#endif
+ if (bp == 0)
+ panic("bpfattach");
+
+ bp->bif_dlist = 0;
+ bp->bif_driverp = (struct bpf_if **)driverp;
+ bp->bif_ifp = ifp;
+ bp->bif_dlt = dlt;
+
+ bp->bif_next = bpf_iflist;
+ bpf_iflist = bp;
+
+ *bp->bif_driverp = 0;
+
+ /*
+ * Compute the length of the bpf header. This is not necessarily
+ * equal to SIZEOF_BPF_HDR because we want to insert spacing such
+ * that the network layer header begins on a longword boundary (for
+ * performance reasons and to alleviate alignment restrictions).
+ */
+ bp->bif_hdrlen = BPF_WORDALIGN(hdrlen + SIZEOF_BPF_HDR) - hdrlen;
+
+ /*
+ * Mark all the descriptors free if this hasn't been done.
+ */
+ if (!D_ISFREE(&bpf_dtab[0]))
+ for (i = 0; i < NBPFILTER; ++i)
+ D_MARKFREE(&bpf_dtab[i]);
+
+ printf("bpf: %s%d attached\n", ifp->if_name, ifp->if_unit);
+}
+
+#if BSD >= 199103
+/* XXX This routine belongs in net/if.c. */
+/*
+ * Set/clear promiscuous mode on interface ifp based on the truth value
+ * of pswitch. The calls are reference counted so that only the first
+ * "on" request actually has an effect, as does the final "off" request.
+ * Results are undefined if the "off" and "on" requests are not matched.
+ */
+int
+ifpromisc(ifp, pswitch)
+ struct ifnet *ifp;
+ int pswitch;
+{
+ struct ifreq ifr;
+ /*
+ * If the device is not configured up, we cannot put it in
+ * promiscuous mode.
+ */
+ if ((ifp->if_flags & IFF_UP) == 0)
+ return (ENETDOWN);
+
+ if (pswitch) {
+ if (ifp->if_pcount++ != 0)
+ return (0);
+ ifp->if_flags |= IFF_PROMISC;
+ } else {
+ if (--ifp->if_pcount > 0)
+ return (0);
+ ifp->if_flags &= ~IFF_PROMISC;
+ }
+ ifr.ifr_flags = ifp->if_flags;
+ return ((*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr));
+}
+#endif
+
+#if BSD < 199103
+/*
+ * Allocate some memory for bpf. This is temporary SunOS support, and
+ * is admittedly a hack.
+ * If resources unavaiable, return 0.
+ */
+static caddr_t
+bpf_alloc(size, canwait)
+ register int size;
+ register int canwait;
+{
+ register struct mbuf *m;
+
+ if ((unsigned)size > (MCLBYTES-8))
+ return 0;
+
+ MGET(m, canwait, MT_DATA);
+ if (m == 0)
+ return 0;
+ if ((unsigned)size > (MLEN-8)) {
+ MCLGET(m);
+ if (m->m_len != MCLBYTES) {
+ m_freem(m);
+ return 0;
+ }
+ }
+ *mtod(m, struct mbuf **) = m;
+ return mtod(m, caddr_t) + 8;
+}
+#endif
+#endif
diff --git a/sys/net/bpf.h b/sys/net/bpf.h
new file mode 100644
index 0000000..2e093ac
--- /dev/null
+++ b/sys/net/bpf.h
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 1990, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from the Stanford/CMU enet packet filter,
+ * (net/enet.c) distributed as part of 4.3BSD, and code contributed
+ * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
+ * Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)bpf.h 8.1 (Berkeley) 6/10/93
+ *
+ * @(#) $Header: bpf.h,v 1.24 91/10/27 21:22:32 mccanne Exp $ (LBL)
+ */
+
+/*
+ * Alignment macros. BPF_WORDALIGN rounds up to the next
+ * even multiple of BPF_ALIGNMENT.
+ */
+#define BPF_ALIGNMENT sizeof(long)
+#define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1))
+
+#define BPF_MAXINSNS 512
+#define BPF_MAXBUFSIZE 0x8000
+#define BPF_MINBUFSIZE 32
+
+/*
+ * Structure for BIOCSETF.
+ */
+struct bpf_program {
+ u_int bf_len;
+ struct bpf_insn *bf_insns;
+};
+
+/*
+ * Struct returned by BIOCGSTATS.
+ */
+struct bpf_stat {
+ u_int bs_recv; /* number of packets received */
+ u_int bs_drop; /* number of packets dropped */
+};
+
+/*
+ * Struct return by BIOCVERSION. This represents the version number of
+ * the filter language described by the instruction encodings below.
+ * bpf understands a program iff kernel_major == filter_major &&
+ * kernel_minor >= filter_minor, that is, if the value returned by the
+ * running kernel has the same major number and a minor number equal
+ * equal to or less than the filter being downloaded. Otherwise, the
+ * results are undefined, meaning an error may be returned or packets
+ * may be accepted haphazardly.
+ * It has nothing to do with the source code version.
+ */
+struct bpf_version {
+ u_short bv_major;
+ u_short bv_minor;
+};
+/* Current version number. */
+#define BPF_MAJOR_VERSION 1
+#define BPF_MINOR_VERSION 1
+
+/*
+ * BPF ioctls
+ *
+ * The first set is for compatibility with Sun's pcc style
+ * header files. If your using gcc, we assume that you
+ * have run fixincludes so the latter set should work.
+ */
+#if (defined(sun) || defined(ibm032)) && !defined(__GNUC__)
+#define BIOCGBLEN _IOR(B,102, u_int)
+#define BIOCSBLEN _IOWR(B,102, u_int)
+#define BIOCSETF _IOW(B,103, struct bpf_program)
+#define BIOCFLUSH _IO(B,104)
+#define BIOCPROMISC _IO(B,105)
+#define BIOCGDLT _IOR(B,106, u_int)
+#define BIOCGETIF _IOR(B,107, struct ifreq)
+#define BIOCSETIF _IOW(B,108, struct ifreq)
+#define BIOCSRTIMEOUT _IOW(B,109, struct timeval)
+#define BIOCGRTIMEOUT _IOR(B,110, struct timeval)
+#define BIOCGSTATS _IOR(B,111, struct bpf_stat)
+#define BIOCIMMEDIATE _IOW(B,112, u_int)
+#define BIOCVERSION _IOR(B,113, struct bpf_version)
+#else
+#define BIOCGBLEN _IOR('B',102, u_int)
+#define BIOCSBLEN _IOWR('B',102, u_int)
+#define BIOCSETF _IOW('B',103, struct bpf_program)
+#define BIOCFLUSH _IO('B',104)
+#define BIOCPROMISC _IO('B',105)
+#define BIOCGDLT _IOR('B',106, u_int)
+#define BIOCGETIF _IOR('B',107, struct ifreq)
+#define BIOCSETIF _IOW('B',108, struct ifreq)
+#define BIOCSRTIMEOUT _IOW('B',109, struct timeval)
+#define BIOCGRTIMEOUT _IOR('B',110, struct timeval)
+#define BIOCGSTATS _IOR('B',111, struct bpf_stat)
+#define BIOCIMMEDIATE _IOW('B',112, u_int)
+#define BIOCVERSION _IOR('B',113, struct bpf_version)
+#endif
+
+/*
+ * Structure prepended to each packet.
+ */
+struct bpf_hdr {
+ struct timeval bh_tstamp; /* time stamp */
+ u_long bh_caplen; /* length of captured portion */
+ u_long bh_datalen; /* original length of packet */
+ u_short bh_hdrlen; /* length of bpf header (this struct
+ plus alignment padding) */
+};
+/*
+ * Because the structure above is not a multiple of 4 bytes, some compilers
+ * will insist on inserting padding; hence, sizeof(struct bpf_hdr) won't work.
+ * Only the kernel needs to know about it; applications use bh_hdrlen.
+ */
+#ifdef KERNEL
+#define SIZEOF_BPF_HDR 18
+#endif
+
+/*
+ * Data-link level type codes.
+ * Currently, only DLT_EN10MB and DLT_SLIP are supported.
+ */
+#define DLT_NULL 0 /* no link-layer encapsulation */
+#define DLT_EN10MB 1 /* Ethernet (10Mb) */
+#define DLT_EN3MB 2 /* Experimental Ethernet (3Mb) */
+#define DLT_AX25 3 /* Amateur Radio AX.25 */
+#define DLT_PRONET 4 /* Proteon ProNET Token Ring */
+#define DLT_CHAOS 5 /* Chaos */
+#define DLT_IEEE802 6 /* IEEE 802 Networks */
+#define DLT_ARCNET 7 /* ARCNET */
+#define DLT_SLIP 8 /* Serial Line IP */
+#define DLT_PPP 9 /* Point-to-point Protocol */
+#define DLT_FDDI 10 /* FDDI */
+
+/*
+ * The instruction encondings.
+ */
+/* instruction classes */
+#define BPF_CLASS(code) ((code) & 0x07)
+#define BPF_LD 0x00
+#define BPF_LDX 0x01
+#define BPF_ST 0x02
+#define BPF_STX 0x03
+#define BPF_ALU 0x04
+#define BPF_JMP 0x05
+#define BPF_RET 0x06
+#define BPF_MISC 0x07
+
+/* ld/ldx fields */
+#define BPF_SIZE(code) ((code) & 0x18)
+#define BPF_W 0x00
+#define BPF_H 0x08
+#define BPF_B 0x10
+#define BPF_MODE(code) ((code) & 0xe0)
+#define BPF_IMM 0x00
+#define BPF_ABS 0x20
+#define BPF_IND 0x40
+#define BPF_MEM 0x60
+#define BPF_LEN 0x80
+#define BPF_MSH 0xa0
+
+/* alu/jmp fields */
+#define BPF_OP(code) ((code) & 0xf0)
+#define BPF_ADD 0x00
+#define BPF_SUB 0x10
+#define BPF_MUL 0x20
+#define BPF_DIV 0x30
+#define BPF_OR 0x40
+#define BPF_AND 0x50
+#define BPF_LSH 0x60
+#define BPF_RSH 0x70
+#define BPF_NEG 0x80
+#define BPF_JA 0x00
+#define BPF_JEQ 0x10
+#define BPF_JGT 0x20
+#define BPF_JGE 0x30
+#define BPF_JSET 0x40
+#define BPF_SRC(code) ((code) & 0x08)
+#define BPF_K 0x00
+#define BPF_X 0x08
+
+/* ret - BPF_K and BPF_X also apply */
+#define BPF_RVAL(code) ((code) & 0x18)
+#define BPF_A 0x10
+
+/* misc */
+#define BPF_MISCOP(code) ((code) & 0xf8)
+#define BPF_TAX 0x00
+#define BPF_TXA 0x80
+
+/*
+ * The instruction data structure.
+ */
+struct bpf_insn {
+ u_short code;
+ u_char jt;
+ u_char jf;
+ long k;
+};
+
+/*
+ * Macros for insn array initializers.
+ */
+#define BPF_STMT(code, k) { (u_short)(code), 0, 0, k }
+#define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k }
+
+#ifdef KERNEL
+int bpf_validate __P((struct bpf_insn *, int));
+int bpfopen __P((dev_t, int));
+int bpfclose __P((dev_t, int));
+int bpfread __P((dev_t, struct uio *));
+int bpfwrite __P((dev_t, struct uio *));
+int bpfioctl __P((dev_t, int, caddr_t, int));
+int bpf_select __P((dev_t, int, struct proc *));
+void bpf_tap __P((caddr_t, u_char *, u_int));
+void bpf_mtap __P((caddr_t, struct mbuf *));
+void bpfattach __P((caddr_t *, struct ifnet *, u_int, u_int));
+void bpfilterattach __P((int));
+u_int bpf_filter __P((struct bpf_insn *, u_char *, u_int, u_int));
+#endif
+
+/*
+ * Number of scratch memory words (for BPF_LD|BPF_MEM and BPF_ST).
+ */
+#define BPF_MEMWORDS 16
+
diff --git a/sys/net/bpf_compat.h b/sys/net/bpf_compat.h
new file mode 100644
index 0000000..132a6df
--- /dev/null
+++ b/sys/net/bpf_compat.h
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)bpf_compat.h 8.1 (Berkeley) 6/10/93
+ */
+
+/* from: $Header: bpf_compat.h,v 1.1 92/05/22 15:33:20 mccanne Exp $ (LBL) */
+
+/*
+ * Some hacks for compatibility across SunOS and 4.4BSD. We emulate malloc
+ * and free with mbuf clusters. We store a pointer to the mbuf in the first
+ * word of the mbuf and return 8 bytes passed the start of data (for double
+ * word alignment). We cannot just use offsets because clusters are not at
+ * a fixed offset from the associated mbuf. Sorry for this kludge.
+ */
+#define malloc(size, type, canwait) bpf_alloc(size, canwait)
+#define free(cp, type) m_free(*(struct mbuf **)(cp - 8))
+#define M_WAITOK M_WAIT
+
+/* This mapping works for our purposes. */
+#define ERESTART EINTR
diff --git a/sys/net/bpf_filter.c b/sys/net/bpf_filter.c
new file mode 100644
index 0000000..cfae776
--- /dev/null
+++ b/sys/net/bpf_filter.c
@@ -0,0 +1,545 @@
+/*
+ * Copyright (c) 1990, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from the Stanford/CMU enet packet filter,
+ * (net/enet.c) distributed as part of 4.3BSD, and code contributed
+ * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
+ * Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)bpf_filter.c 8.1 (Berkeley) 6/10/93
+ *
+ * static char rcsid[] =
+ * "$Header: bpf_filter.c,v 1.16 91/10/27 21:22:35 mccanne Exp $";
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#ifdef sun
+#include <netinet/in.h>
+#endif
+
+#if defined(sparc) || defined(mips) || defined(ibm032)
+#define BPF_ALIGN
+#endif
+
+#ifndef BPF_ALIGN
+#define EXTRACT_SHORT(p) ((u_short)ntohs(*(u_short *)p))
+#define EXTRACT_LONG(p) (ntohl(*(u_long *)p))
+#else
+#define EXTRACT_SHORT(p)\
+ ((u_short)\
+ ((u_short)*((u_char *)p+0)<<8|\
+ (u_short)*((u_char *)p+1)<<0))
+#define EXTRACT_LONG(p)\
+ ((u_long)*((u_char *)p+0)<<24|\
+ (u_long)*((u_char *)p+1)<<16|\
+ (u_long)*((u_char *)p+2)<<8|\
+ (u_long)*((u_char *)p+3)<<0)
+#endif
+
+#ifdef KERNEL
+#include <sys/mbuf.h>
+#define MINDEX(m, k) \
+{ \
+ register int len = m->m_len; \
+ \
+ while (k >= len) { \
+ k -= len; \
+ m = m->m_next; \
+ if (m == 0) \
+ return 0; \
+ len = m->m_len; \
+ } \
+}
+
+static int
+m_xword(m, k, err)
+ register struct mbuf *m;
+ register int k, *err;
+{
+ register int len;
+ register u_char *cp, *np;
+ register struct mbuf *m0;
+
+ len = m->m_len;
+ while (k >= len) {
+ k -= len;
+ m = m->m_next;
+ if (m == 0)
+ goto bad;
+ len = m->m_len;
+ }
+ cp = mtod(m, u_char *) + k;
+ if (len - k >= 4) {
+ *err = 0;
+ return EXTRACT_LONG(cp);
+ }
+ m0 = m->m_next;
+ if (m0 == 0 || m0->m_len + len - k < 4)
+ goto bad;
+ *err = 0;
+ np = mtod(m0, u_char *);
+ switch (len - k) {
+
+ case 1:
+ return (cp[k] << 24) | (np[0] << 16) | (np[1] << 8) | np[2];
+
+ case 2:
+ return (cp[k] << 24) | (cp[k + 1] << 16) | (np[0] << 8) |
+ np[1];
+
+ default:
+ return (cp[k] << 24) | (cp[k + 1] << 16) | (cp[k + 2] << 8) |
+ np[0];
+ }
+ bad:
+ *err = 1;
+ return 0;
+}
+
+static int
+m_xhalf(m, k, err)
+ register struct mbuf *m;
+ register int k, *err;
+{
+ register int len;
+ register u_char *cp;
+ register struct mbuf *m0;
+
+ len = m->m_len;
+ while (k >= len) {
+ k -= len;
+ m = m->m_next;
+ if (m == 0)
+ goto bad;
+ len = m->m_len;
+ }
+ cp = mtod(m, u_char *) + k;
+ if (len - k >= 2) {
+ *err = 0;
+ return EXTRACT_SHORT(cp);
+ }
+ m0 = m->m_next;
+ if (m0 == 0)
+ goto bad;
+ *err = 0;
+ return (cp[k] << 8) | mtod(m0, u_char *)[0];
+ bad:
+ *err = 1;
+ return 0;
+}
+#endif
+
+#include <net/bpf.h>
+/*
+ * Execute the filter program starting at pc on the packet p
+ * wirelen is the length of the original packet
+ * buflen is the amount of data present
+ */
+u_int
+bpf_filter(pc, p, wirelen, buflen)
+ register struct bpf_insn *pc;
+ register u_char *p;
+ u_int wirelen;
+ register u_int buflen;
+{
+ register u_long A = 0, X = 0;
+ register int k;
+ long mem[BPF_MEMWORDS];
+
+ if (pc == 0)
+ /*
+ * No filter means accept all.
+ */
+ return (u_int)-1;
+
+ --pc;
+ while (1) {
+ ++pc;
+ switch (pc->code) {
+
+ default:
+#ifdef KERNEL
+ return 0;
+#else
+ abort();
+#endif
+ case BPF_RET|BPF_K:
+ return (u_int)pc->k;
+
+ case BPF_RET|BPF_A:
+ return (u_int)A;
+
+ case BPF_LD|BPF_W|BPF_ABS:
+ k = pc->k;
+ if (k + sizeof(long) > buflen) {
+#ifdef KERNEL
+ int merr;
+
+ if (buflen != 0)
+ return 0;
+ A = m_xword((struct mbuf *)p, k, &merr);
+ if (merr != 0)
+ return 0;
+ continue;
+#else
+ return 0;
+#endif
+ }
+#ifdef BPF_ALIGN
+ if (((int)(p + k) & 3) != 0)
+ A = EXTRACT_LONG(&p[k]);
+ else
+#endif
+ A = ntohl(*(long *)(p + k));
+ continue;
+
+ case BPF_LD|BPF_H|BPF_ABS:
+ k = pc->k;
+ if (k + sizeof(short) > buflen) {
+#ifdef KERNEL
+ int merr;
+
+ if (buflen != 0)
+ return 0;
+ A = m_xhalf((struct mbuf *)p, k, &merr);
+ continue;
+#else
+ return 0;
+#endif
+ }
+ A = EXTRACT_SHORT(&p[k]);
+ continue;
+
+ case BPF_LD|BPF_B|BPF_ABS:
+ k = pc->k;
+ if (k >= buflen) {
+#ifdef KERNEL
+ register struct mbuf *m;
+
+ if (buflen != 0)
+ return 0;
+ m = (struct mbuf *)p;
+ MINDEX(m, k);
+ A = mtod(m, u_char *)[k];
+ continue;
+#else
+ return 0;
+#endif
+ }
+ A = p[k];
+ continue;
+
+ case BPF_LD|BPF_W|BPF_LEN:
+ A = wirelen;
+ continue;
+
+ case BPF_LDX|BPF_W|BPF_LEN:
+ X = wirelen;
+ continue;
+
+ case BPF_LD|BPF_W|BPF_IND:
+ k = X + pc->k;
+ if (k + sizeof(long) > buflen) {
+#ifdef KERNEL
+ int merr;
+
+ if (buflen != 0)
+ return 0;
+ A = m_xword((struct mbuf *)p, k, &merr);
+ if (merr != 0)
+ return 0;
+ continue;
+#else
+ return 0;
+#endif
+ }
+#ifdef BPF_ALIGN
+ if (((int)(p + k) & 3) != 0)
+ A = EXTRACT_LONG(&p[k]);
+ else
+#endif
+ A = ntohl(*(long *)(p + k));
+ continue;
+
+ case BPF_LD|BPF_H|BPF_IND:
+ k = X + pc->k;
+ if (k + sizeof(short) > buflen) {
+#ifdef KERNEL
+ int merr;
+
+ if (buflen != 0)
+ return 0;
+ A = m_xhalf((struct mbuf *)p, k, &merr);
+ if (merr != 0)
+ return 0;
+ continue;
+#else
+ return 0;
+#endif
+ }
+ A = EXTRACT_SHORT(&p[k]);
+ continue;
+
+ case BPF_LD|BPF_B|BPF_IND:
+ k = X + pc->k;
+ if (k >= buflen) {
+#ifdef KERNEL
+ register struct mbuf *m;
+
+ if (buflen != 0)
+ return 0;
+ m = (struct mbuf *)p;
+ MINDEX(m, k);
+ A = mtod(m, char *)[k];
+ continue;
+#else
+ return 0;
+#endif
+ }
+ A = p[k];
+ continue;
+
+ case BPF_LDX|BPF_MSH|BPF_B:
+ k = pc->k;
+ if (k >= buflen) {
+#ifdef KERNEL
+ register struct mbuf *m;
+
+ if (buflen != 0)
+ return 0;
+ m = (struct mbuf *)p;
+ MINDEX(m, k);
+ X = (mtod(m, char *)[k] & 0xf) << 2;
+ continue;
+#else
+ return 0;
+#endif
+ }
+ X = (p[pc->k] & 0xf) << 2;
+ continue;
+
+ case BPF_LD|BPF_IMM:
+ A = pc->k;
+ continue;
+
+ case BPF_LDX|BPF_IMM:
+ X = pc->k;
+ continue;
+
+ case BPF_LD|BPF_MEM:
+ A = mem[pc->k];
+ continue;
+
+ case BPF_LDX|BPF_MEM:
+ X = mem[pc->k];
+ continue;
+
+ case BPF_ST:
+ mem[pc->k] = A;
+ continue;
+
+ case BPF_STX:
+ mem[pc->k] = X;
+ continue;
+
+ case BPF_JMP|BPF_JA:
+ pc += pc->k;
+ continue;
+
+ case BPF_JMP|BPF_JGT|BPF_K:
+ pc += (A > pc->k) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JGE|BPF_K:
+ pc += (A >= pc->k) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JEQ|BPF_K:
+ pc += (A == pc->k) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JSET|BPF_K:
+ pc += (A & pc->k) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JGT|BPF_X:
+ pc += (A > X) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JGE|BPF_X:
+ pc += (A >= X) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JEQ|BPF_X:
+ pc += (A == X) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JSET|BPF_X:
+ pc += (A & X) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_ALU|BPF_ADD|BPF_X:
+ A += X;
+ continue;
+
+ case BPF_ALU|BPF_SUB|BPF_X:
+ A -= X;
+ continue;
+
+ case BPF_ALU|BPF_MUL|BPF_X:
+ A *= X;
+ continue;
+
+ case BPF_ALU|BPF_DIV|BPF_X:
+ if (X == 0)
+ return 0;
+ A /= X;
+ continue;
+
+ case BPF_ALU|BPF_AND|BPF_X:
+ A &= X;
+ continue;
+
+ case BPF_ALU|BPF_OR|BPF_X:
+ A |= X;
+ continue;
+
+ case BPF_ALU|BPF_LSH|BPF_X:
+ A <<= X;
+ continue;
+
+ case BPF_ALU|BPF_RSH|BPF_X:
+ A >>= X;
+ continue;
+
+ case BPF_ALU|BPF_ADD|BPF_K:
+ A += pc->k;
+ continue;
+
+ case BPF_ALU|BPF_SUB|BPF_K:
+ A -= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_MUL|BPF_K:
+ A *= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_DIV|BPF_K:
+ A /= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_AND|BPF_K:
+ A &= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_OR|BPF_K:
+ A |= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_LSH|BPF_K:
+ A <<= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_RSH|BPF_K:
+ A >>= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_NEG:
+ A = -A;
+ continue;
+
+ case BPF_MISC|BPF_TAX:
+ X = A;
+ continue;
+
+ case BPF_MISC|BPF_TXA:
+ A = X;
+ continue;
+ }
+ }
+}
+
+#ifdef KERNEL
+/*
+ * Return true if the 'fcode' is a valid filter program.
+ * The constraints are that each jump be forward and to a valid
+ * code. The code must terminate with either an accept or reject.
+ * 'valid' is an array for use by the routine (it must be at least
+ * 'len' bytes long).
+ *
+ * The kernel needs to be able to verify an application's filter code.
+ * Otherwise, a bogus program could easily crash the system.
+ */
+int
+bpf_validate(f, len)
+ struct bpf_insn *f;
+ int len;
+{
+ register int i;
+ register struct bpf_insn *p;
+
+ for (i = 0; i < len; ++i) {
+ /*
+ * Check that that jumps are forward, and within
+ * the code block.
+ */
+ p = &f[i];
+ if (BPF_CLASS(p->code) == BPF_JMP) {
+ register int from = i + 1;
+
+ if (BPF_OP(p->code) == BPF_JA) {
+ if (from + p->k >= len)
+ return 0;
+ }
+ else if (from + p->jt >= len || from + p->jf >= len)
+ return 0;
+ }
+ /*
+ * Check that memory operations use valid addresses.
+ */
+ if ((BPF_CLASS(p->code) == BPF_ST ||
+ (BPF_CLASS(p->code) == BPF_LD &&
+ (p->code & 0xe0) == BPF_MEM)) &&
+ (p->k >= BPF_MEMWORDS || p->k < 0))
+ return 0;
+ /*
+ * Check for constant division by 0.
+ */
+ if (p->code == (BPF_ALU|BPF_DIV|BPF_K) && p->k == 0)
+ return 0;
+ }
+ return BPF_CLASS(f[len - 1].code) == BPF_RET;
+}
+#endif
diff --git a/sys/net/bpfdesc.h b/sys/net/bpfdesc.h
new file mode 100644
index 0000000..36e3d26
--- /dev/null
+++ b/sys/net/bpfdesc.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1990, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from the Stanford/CMU enet packet filter,
+ * (net/enet.c) distributed as part of 4.3BSD, and code contributed
+ * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
+ * Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)bpfdesc.h 8.1 (Berkeley) 6/10/93
+ *
+ * @(#) $Header: bpfdesc.h,v 1.9 91/10/27 21:22:38 mccanne Exp $ (LBL)
+ */
+
+#include <sys/select.h>
+
+/*
+ * Descriptor associated with each open bpf file.
+ */
+struct bpf_d {
+ struct bpf_d *bd_next; /* Linked list of descriptors */
+ /*
+ * Buffer slots: two mbuf clusters buffer the incoming packets.
+ * The model has three slots. Sbuf is always occupied.
+ * sbuf (store) - Receive interrupt puts packets here.
+ * hbuf (hold) - When sbuf is full, put cluster here and
+ * wakeup read (replace sbuf with fbuf).
+ * fbuf (free) - When read is done, put cluster here.
+ * On receiving, if sbuf is full and fbuf is 0, packet is dropped.
+ */
+ caddr_t bd_sbuf; /* store slot */
+ caddr_t bd_hbuf; /* hold slot */
+ caddr_t bd_fbuf; /* free slot */
+ int bd_slen; /* current length of store buffer */
+ int bd_hlen; /* current length of hold buffer */
+
+ int bd_bufsize; /* absolute length of buffers */
+
+ struct bpf_if * bd_bif; /* interface descriptor */
+ u_long bd_rtout; /* Read timeout in 'ticks' */
+ struct bpf_insn *bd_filter; /* filter code */
+ u_long bd_rcount; /* number of packets received */
+ u_long bd_dcount; /* number of packets dropped */
+
+ u_char bd_promisc; /* true if listening promiscuously */
+ u_char bd_state; /* idle, waiting, or timed out */
+ u_char bd_immediate; /* true to return on packet arrival */
+#if BSD < 199103
+ u_char bd_selcoll; /* true if selects collide */
+ int bd_timedout;
+ struct proc * bd_selproc; /* process that last selected us */
+#else
+ u_char bd_pad; /* explicit alignment */
+ struct selinfo bd_sel; /* bsd select info */
+#endif
+};
+
+/*
+ * Descriptor associated with each attached hardware interface.
+ */
+struct bpf_if {
+ struct bpf_if *bif_next; /* list of all interfaces */
+ struct bpf_d *bif_dlist; /* descriptor list */
+ struct bpf_if **bif_driverp; /* pointer into softc */
+ u_int bif_dlt; /* link layer type */
+ u_int bif_hdrlen; /* length of header (with padding) */
+ struct ifnet *bif_ifp; /* correspoding interface */
+};
+
+#ifdef KERNEL
+int bpf_setf __P((struct bpf_d *, struct bpf_program *));
+#endif
diff --git a/sys/net/if.c b/sys/net/if.c
new file mode 100644
index 0000000..3696388
--- /dev/null
+++ b/sys/net/if.c
@@ -0,0 +1,670 @@
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if.c 8.3 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/kernel.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+
+int ifqmaxlen = IFQ_MAXLEN;
+void if_slowtimo __P((void *arg));
+
+/*
+ * Network interface utility routines.
+ *
+ * Routines with ifa_ifwith* names take sockaddr *'s as
+ * parameters.
+ */
+void
+ifinit()
+{
+ register struct ifnet *ifp;
+
+ for (ifp = ifnet; ifp; ifp = ifp->if_next)
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+ if_slowtimo(0);
+}
+
+#ifdef vax
+/*
+ * Call each interface on a Unibus reset.
+ */
+void
+ifubareset(uban)
+ int uban;
+{
+ register struct ifnet *ifp;
+
+ for (ifp = ifnet; ifp; ifp = ifp->if_next)
+ if (ifp->if_reset)
+ (*ifp->if_reset)(ifp->if_unit, uban);
+}
+#endif
+
+int if_index = 0;
+struct ifaddr **ifnet_addrs;
+static char *sprint_d __P((u_int, char *, int));
+
+/*
+ * Attach an interface to the
+ * list of "active" interfaces.
+ */
+void
+if_attach(ifp)
+ struct ifnet *ifp;
+{
+ unsigned socksize, ifasize;
+ int namelen, unitlen, masklen, ether_output();
+ char workbuf[12], *unitname;
+ register struct ifnet **p = &ifnet;
+ register struct sockaddr_dl *sdl;
+ register struct ifaddr *ifa;
+ static int if_indexlim = 8;
+ extern void link_rtrequest();
+
+ while (*p)
+ p = &((*p)->if_next);
+ *p = ifp;
+ ifp->if_index = ++if_index;
+ if (ifnet_addrs == 0 || if_index >= if_indexlim) {
+ unsigned n = (if_indexlim <<= 1) * sizeof(ifa);
+ struct ifaddr **q = (struct ifaddr **)
+ malloc(n, M_IFADDR, M_WAITOK);
+ if (ifnet_addrs) {
+ bcopy((caddr_t)ifnet_addrs, (caddr_t)q, n/2);
+ free((caddr_t)ifnet_addrs, M_IFADDR);
+ }
+ ifnet_addrs = q;
+ }
+ /*
+ * create a Link Level name for this device
+ */
+ unitname = sprint_d((u_int)ifp->if_unit, workbuf, sizeof(workbuf));
+ namelen = strlen(ifp->if_name);
+ unitlen = strlen(unitname);
+#define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m))
+ masklen = _offsetof(struct sockaddr_dl, sdl_data[0]) +
+ unitlen + namelen;
+ socksize = masklen + ifp->if_addrlen;
+#define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1)))
+ socksize = ROUNDUP(socksize);
+ if (socksize < sizeof(*sdl))
+ socksize = sizeof(*sdl);
+ ifasize = sizeof(*ifa) + 2 * socksize;
+ if (ifa = (struct ifaddr *)malloc(ifasize, M_IFADDR, M_WAITOK)) {
+ bzero((caddr_t)ifa, ifasize);
+ sdl = (struct sockaddr_dl *)(ifa + 1);
+ sdl->sdl_len = socksize;
+ sdl->sdl_family = AF_LINK;
+ bcopy(ifp->if_name, sdl->sdl_data, namelen);
+ bcopy(unitname, namelen + (caddr_t)sdl->sdl_data, unitlen);
+ sdl->sdl_nlen = (namelen += unitlen);
+ sdl->sdl_index = ifp->if_index;
+ sdl->sdl_type = ifp->if_type;
+ ifnet_addrs[if_index - 1] = ifa;
+ ifa->ifa_ifp = ifp;
+ ifa->ifa_next = ifp->if_addrlist;
+ ifa->ifa_rtrequest = link_rtrequest;
+ ifp->if_addrlist = ifa;
+ ifa->ifa_addr = (struct sockaddr *)sdl;
+ sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl);
+ ifa->ifa_netmask = (struct sockaddr *)sdl;
+ sdl->sdl_len = masklen;
+ while (namelen != 0)
+ sdl->sdl_data[--namelen] = 0xff;
+ }
+ /* XXX -- Temporary fix before changing 10 ethernet drivers */
+ if (ifp->if_output == ether_output)
+ ether_ifattach(ifp);
+}
+/*
+ * Locate an interface based on a complete address.
+ */
+/*ARGSUSED*/
+struct ifaddr *
+ifa_ifwithaddr(addr)
+ register struct sockaddr *addr;
+{
+ register struct ifnet *ifp;
+ register struct ifaddr *ifa;
+
+#define equal(a1, a2) \
+ (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0)
+ for (ifp = ifnet; ifp; ifp = ifp->if_next)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family != addr->sa_family)
+ continue;
+ if (equal(addr, ifa->ifa_addr))
+ return (ifa);
+ if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr &&
+ equal(ifa->ifa_broadaddr, addr))
+ return (ifa);
+ }
+ return ((struct ifaddr *)0);
+}
+/*
+ * Locate the point to point interface with a given destination address.
+ */
+/*ARGSUSED*/
+struct ifaddr *
+ifa_ifwithdstaddr(addr)
+ register struct sockaddr *addr;
+{
+ register struct ifnet *ifp;
+ register struct ifaddr *ifa;
+
+ for (ifp = ifnet; ifp; ifp = ifp->if_next)
+ if (ifp->if_flags & IFF_POINTOPOINT)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family != addr->sa_family)
+ continue;
+ if (equal(addr, ifa->ifa_dstaddr))
+ return (ifa);
+ }
+ return ((struct ifaddr *)0);
+}
+
+/*
+ * Find an interface on a specific network. If many, choice
+ * is most specific found.
+ */
+struct ifaddr *
+ifa_ifwithnet(addr)
+ struct sockaddr *addr;
+{
+ register struct ifnet *ifp;
+ register struct ifaddr *ifa;
+ struct ifaddr *ifa_maybe = (struct ifaddr *) 0;
+ u_int af = addr->sa_family;
+ char *addr_data = addr->sa_data, *cplim;
+
+ if (af == AF_LINK) {
+ register struct sockaddr_dl *sdl = (struct sockaddr_dl *)addr;
+ if (sdl->sdl_index && sdl->sdl_index <= if_index)
+ return (ifnet_addrs[sdl->sdl_index - 1]);
+ }
+ for (ifp = ifnet; ifp; ifp = ifp->if_next)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
+ register char *cp, *cp2, *cp3;
+
+ if (ifa->ifa_addr->sa_family != af || ifa->ifa_netmask == 0)
+ next: continue;
+ cp = addr_data;
+ cp2 = ifa->ifa_addr->sa_data;
+ cp3 = ifa->ifa_netmask->sa_data;
+ cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask;
+ while (cp3 < cplim)
+ if ((*cp++ ^ *cp2++) & *cp3++)
+ goto next;
+ if (ifa_maybe == 0 ||
+ rn_refines((caddr_t)ifa->ifa_netmask,
+ (caddr_t)ifa_maybe->ifa_netmask))
+ ifa_maybe = ifa;
+ }
+ return (ifa_maybe);
+}
+
+/*
+ * Find an interface using a specific address family
+ */
+struct ifaddr *
+ifa_ifwithaf(af)
+ register int af;
+{
+ register struct ifnet *ifp;
+ register struct ifaddr *ifa;
+
+ for (ifp = ifnet; ifp; ifp = ifp->if_next)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+ if (ifa->ifa_addr->sa_family == af)
+ return (ifa);
+ return ((struct ifaddr *)0);
+}
+
+/*
+ * Find an interface address specific to an interface best matching
+ * a given address.
+ */
+struct ifaddr *
+ifaof_ifpforaddr(addr, ifp)
+ struct sockaddr *addr;
+ register struct ifnet *ifp;
+{
+ register struct ifaddr *ifa;
+ register char *cp, *cp2, *cp3;
+ register char *cplim;
+ struct ifaddr *ifa_maybe = 0;
+ u_int af = addr->sa_family;
+
+ if (af >= AF_MAX)
+ return (0);
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family != af)
+ continue;
+ ifa_maybe = ifa;
+ if (ifa->ifa_netmask == 0) {
+ if (equal(addr, ifa->ifa_addr) ||
+ (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr)))
+ return (ifa);
+ continue;
+ }
+ cp = addr->sa_data;
+ cp2 = ifa->ifa_addr->sa_data;
+ cp3 = ifa->ifa_netmask->sa_data;
+ cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask;
+ for (; cp3 < cplim; cp3++)
+ if ((*cp++ ^ *cp2++) & *cp3)
+ break;
+ if (cp3 == cplim)
+ return (ifa);
+ }
+ return (ifa_maybe);
+}
+
+#include <net/route.h>
+
+/*
+ * Default action when installing a route with a Link Level gateway.
+ * Lookup an appropriate real ifa to point to.
+ * This should be moved to /sys/net/link.c eventually.
+ */
+void
+link_rtrequest(cmd, rt, sa)
+ int cmd;
+ register struct rtentry *rt;
+ struct sockaddr *sa;
+{
+ register struct ifaddr *ifa;
+ struct sockaddr *dst;
+ struct ifnet *ifp;
+
+ if (cmd != RTM_ADD || ((ifa = rt->rt_ifa) == 0) ||
+ ((ifp = ifa->ifa_ifp) == 0) || ((dst = rt_key(rt)) == 0))
+ return;
+ if (ifa = ifaof_ifpforaddr(dst, ifp)) {
+ IFAFREE(rt->rt_ifa);
+ rt->rt_ifa = ifa;
+ ifa->ifa_refcnt++;
+ if (ifa->ifa_rtrequest && ifa->ifa_rtrequest != link_rtrequest)
+ ifa->ifa_rtrequest(cmd, rt, sa);
+ }
+}
+
+/*
+ * Mark an interface down and notify protocols of
+ * the transition.
+ * NOTE: must be called at splnet or eqivalent.
+ */
+void
+if_down(ifp)
+ register struct ifnet *ifp;
+{
+ register struct ifaddr *ifa;
+
+ ifp->if_flags &= ~IFF_UP;
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+ pfctlinput(PRC_IFDOWN, ifa->ifa_addr);
+ if_qflush(&ifp->if_snd);
+ rt_ifmsg(ifp);
+}
+
+/*
+ * Mark an interface up and notify protocols of
+ * the transition.
+ * NOTE: must be called at splnet or eqivalent.
+ */
+void
+if_up(ifp)
+ register struct ifnet *ifp;
+{
+ register struct ifaddr *ifa;
+
+ ifp->if_flags |= IFF_UP;
+#ifdef notyet
+ /* this has no effect on IP, and will kill all iso connections XXX */
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+ pfctlinput(PRC_IFUP, ifa->ifa_addr);
+#endif
+ rt_ifmsg(ifp);
+}
+
+/*
+ * Flush an interface queue.
+ */
+void
+if_qflush(ifq)
+ register struct ifqueue *ifq;
+{
+ register struct mbuf *m, *n;
+
+ n = ifq->ifq_head;
+ while (m = n) {
+ n = m->m_act;
+ m_freem(m);
+ }
+ ifq->ifq_head = 0;
+ ifq->ifq_tail = 0;
+ ifq->ifq_len = 0;
+}
+
+/*
+ * Handle interface watchdog timer routines. Called
+ * from softclock, we decrement timers (if set) and
+ * call the appropriate interface routine on expiration.
+ */
+void
+if_slowtimo(arg)
+ void *arg;
+{
+ register struct ifnet *ifp;
+ int s = splimp();
+
+ for (ifp = ifnet; ifp; ifp = ifp->if_next) {
+ if (ifp->if_timer == 0 || --ifp->if_timer)
+ continue;
+ if (ifp->if_watchdog)
+ (*ifp->if_watchdog)(ifp->if_unit);
+ }
+ splx(s);
+ timeout(if_slowtimo, (void *)0, hz / IFNET_SLOWHZ);
+}
+
+/*
+ * Map interface name to
+ * interface structure pointer.
+ */
+struct ifnet *
+ifunit(name)
+ register char *name;
+{
+ register char *cp;
+ register struct ifnet *ifp;
+ int unit;
+ unsigned len;
+ char *ep, c;
+
+ for (cp = name; cp < name + IFNAMSIZ && *cp; cp++)
+ if (*cp >= '0' && *cp <= '9')
+ break;
+ if (*cp == '\0' || cp == name + IFNAMSIZ)
+ return ((struct ifnet *)0);
+ /*
+ * Save first char of unit, and pointer to it,
+ * so we can put a null there to avoid matching
+ * initial substrings of interface names.
+ */
+ len = cp - name + 1;
+ c = *cp;
+ ep = cp;
+ for (unit = 0; *cp >= '0' && *cp <= '9'; )
+ unit = unit * 10 + *cp++ - '0';
+ *ep = 0;
+ for (ifp = ifnet; ifp; ifp = ifp->if_next) {
+ if (bcmp(ifp->if_name, name, len))
+ continue;
+ if (unit == ifp->if_unit)
+ break;
+ }
+ *ep = c;
+ return (ifp);
+}
+
+/*
+ * Interface ioctls.
+ */
+int
+ifioctl(so, cmd, data, p)
+ struct socket *so;
+ int cmd;
+ caddr_t data;
+ struct proc *p;
+{
+ register struct ifnet *ifp;
+ register struct ifreq *ifr;
+ int error;
+
+ switch (cmd) {
+
+ case SIOCGIFCONF:
+ case OSIOCGIFCONF:
+ return (ifconf(cmd, data));
+ }
+ ifr = (struct ifreq *)data;
+ ifp = ifunit(ifr->ifr_name);
+ if (ifp == 0)
+ return (ENXIO);
+ switch (cmd) {
+
+ case SIOCGIFFLAGS:
+ ifr->ifr_flags = ifp->if_flags;
+ break;
+
+ case SIOCGIFMETRIC:
+ ifr->ifr_metric = ifp->if_metric;
+ break;
+
+ case SIOCSIFFLAGS:
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ if (ifp->if_flags & IFF_UP && (ifr->ifr_flags & IFF_UP) == 0) {
+ int s = splimp();
+ if_down(ifp);
+ splx(s);
+ }
+ if (ifr->ifr_flags & IFF_UP && (ifp->if_flags & IFF_UP) == 0) {
+ int s = splimp();
+ if_up(ifp);
+ splx(s);
+ }
+ ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) |
+ (ifr->ifr_flags &~ IFF_CANTCHANGE);
+ if (ifp->if_ioctl)
+ (void) (*ifp->if_ioctl)(ifp, cmd, data);
+ break;
+
+ case SIOCSIFMETRIC:
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ ifp->if_metric = ifr->ifr_metric;
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ if (ifp->if_ioctl == NULL)
+ return (EOPNOTSUPP);
+ return ((*ifp->if_ioctl)(ifp, cmd, data));
+
+ default:
+ if (so->so_proto == 0)
+ return (EOPNOTSUPP);
+#ifndef COMPAT_43
+ return ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
+ cmd, data, ifp));
+#else
+ {
+ int ocmd = cmd;
+
+ switch (cmd) {
+
+ case SIOCSIFDSTADDR:
+ case SIOCSIFADDR:
+ case SIOCSIFBRDADDR:
+ case SIOCSIFNETMASK:
+#if BYTE_ORDER != BIG_ENDIAN
+ if (ifr->ifr_addr.sa_family == 0 &&
+ ifr->ifr_addr.sa_len < 16) {
+ ifr->ifr_addr.sa_family = ifr->ifr_addr.sa_len;
+ ifr->ifr_addr.sa_len = 16;
+ }
+#else
+ if (ifr->ifr_addr.sa_len == 0)
+ ifr->ifr_addr.sa_len = 16;
+#endif
+ break;
+
+ case OSIOCGIFADDR:
+ cmd = SIOCGIFADDR;
+ break;
+
+ case OSIOCGIFDSTADDR:
+ cmd = SIOCGIFDSTADDR;
+ break;
+
+ case OSIOCGIFBRDADDR:
+ cmd = SIOCGIFBRDADDR;
+ break;
+
+ case OSIOCGIFNETMASK:
+ cmd = SIOCGIFNETMASK;
+ }
+ error = ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
+ cmd, data, ifp));
+ switch (ocmd) {
+
+ case OSIOCGIFADDR:
+ case OSIOCGIFDSTADDR:
+ case OSIOCGIFBRDADDR:
+ case OSIOCGIFNETMASK:
+ *(u_short *)&ifr->ifr_addr = ifr->ifr_addr.sa_family;
+ }
+ return (error);
+
+ }
+#endif
+ }
+ return (0);
+}
+
+/*
+ * Return interface configuration
+ * of system. List may be used
+ * in later ioctl's (above) to get
+ * other information.
+ */
+/*ARGSUSED*/
+int
+ifconf(cmd, data)
+ int cmd;
+ caddr_t data;
+{
+ register struct ifconf *ifc = (struct ifconf *)data;
+ register struct ifnet *ifp = ifnet;
+ register struct ifaddr *ifa;
+ register char *cp, *ep;
+ struct ifreq ifr, *ifrp;
+ int space = ifc->ifc_len, error = 0;
+
+ ifrp = ifc->ifc_req;
+ ep = ifr.ifr_name + sizeof (ifr.ifr_name) - 2;
+ for (; space > sizeof (ifr) && ifp; ifp = ifp->if_next) {
+ strncpy(ifr.ifr_name, ifp->if_name, sizeof (ifr.ifr_name) - 2);
+ for (cp = ifr.ifr_name; cp < ep && *cp; cp++)
+ continue;
+ *cp++ = '0' + ifp->if_unit; *cp = '\0';
+ if ((ifa = ifp->if_addrlist) == 0) {
+ bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
+ error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
+ sizeof (ifr));
+ if (error)
+ break;
+ space -= sizeof (ifr), ifrp++;
+ } else
+ for ( ; space > sizeof (ifr) && ifa; ifa = ifa->ifa_next) {
+ register struct sockaddr *sa = ifa->ifa_addr;
+#ifdef COMPAT_43
+ if (cmd == OSIOCGIFCONF) {
+ struct osockaddr *osa =
+ (struct osockaddr *)&ifr.ifr_addr;
+ ifr.ifr_addr = *sa;
+ osa->sa_family = sa->sa_family;
+ error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
+ sizeof (ifr));
+ ifrp++;
+ } else
+#endif
+ if (sa->sa_len <= sizeof(*sa)) {
+ ifr.ifr_addr = *sa;
+ error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
+ sizeof (ifr));
+ ifrp++;
+ } else {
+ space -= sa->sa_len - sizeof(*sa);
+ if (space < sizeof (ifr))
+ break;
+ error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
+ sizeof (ifr.ifr_name));
+ if (error == 0)
+ error = copyout((caddr_t)sa,
+ (caddr_t)&ifrp->ifr_addr, sa->sa_len);
+ ifrp = (struct ifreq *)
+ (sa->sa_len + (caddr_t)&ifrp->ifr_addr);
+ }
+ if (error)
+ break;
+ space -= sizeof (ifr);
+ }
+ }
+ ifc->ifc_len -= space;
+ return (error);
+}
+
+static char *
+sprint_d(n, buf, buflen)
+ u_int n;
+ char *buf;
+ int buflen;
+{
+ register char *cp = buf + buflen - 1;
+
+ *cp = 0;
+ do {
+ cp--;
+ *cp = "0123456789"[n % 10];
+ n /= 10;
+ } while (n != 0);
+ return (cp);
+}
diff --git a/sys/net/if.h b/sys/net/if.h
new file mode 100644
index 0000000..e0bf795
--- /dev/null
+++ b/sys/net/if.h
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Structures defining a network interface, providing a packet
+ * transport mechanism (ala level 0 of the PUP protocols).
+ *
+ * Each interface accepts output datagrams of a specified maximum
+ * length, and provides higher level routines with input datagrams
+ * received from its medium.
+ *
+ * Output occurs when the routine if_output is called, with three parameters:
+ * (*ifp->if_output)(ifp, m, dst, rt)
+ * Here m is the mbuf chain to be sent and dst is the destination address.
+ * The output routine encapsulates the supplied datagram if necessary,
+ * and then transmits it on its medium.
+ *
+ * On input, each interface unwraps the data received by it, and either
+ * places it on the input queue of a internetwork datagram routine
+ * and posts the associated software interrupt, or passes the datagram to a raw
+ * packet input routine.
+ *
+ * Routines exist for locating interfaces by their addresses
+ * or for locating a interface on a certain network, as well as more general
+ * routing and gateway routines maintaining information used to locate
+ * interfaces. These routines live in the files if.c and route.c
+ */
+#ifndef _TIME_ /* XXX fast fix for SNMP, going away soon */
+#include <sys/time.h>
+#endif
+
+#ifdef __STDC__
+/*
+ * Forward structure declarations for function prototypes [sic].
+ */
+struct mbuf;
+struct proc;
+struct rtentry;
+struct socket;
+struct ether_header;
+#endif
+/*
+ * Structure describing information about an interface
+ * which may be of interest to management entities.
+ */
+/*
+ * Structure defining a queue for a network interface.
+ *
+ * (Would like to call this struct ``if'', but C isn't PL/1.)
+ */
+
+struct ifnet {
+ char *if_name; /* name, e.g. ``en'' or ``lo'' */
+ struct ifnet *if_next; /* all struct ifnets are chained */
+ struct ifaddr *if_addrlist; /* linked list of addresses per if */
+ int if_pcount; /* number of promiscuous listeners */
+ caddr_t if_bpf; /* packet filter structure */
+ u_short if_index; /* numeric abbreviation for this if */
+ short if_unit; /* sub-unit for lower level driver */
+ short if_timer; /* time 'til if_watchdog called */
+ short if_flags; /* up/down, broadcast, etc. */
+ struct if_data {
+/* generic interface information */
+ u_char ifi_type; /* ethernet, tokenring, etc */
+ u_char ifi_addrlen; /* media address length */
+ u_char ifi_hdrlen; /* media header length */
+ u_long ifi_mtu; /* maximum transmission unit */
+ u_long ifi_metric; /* routing metric (external only) */
+ u_long ifi_baudrate; /* linespeed */
+/* volatile statistics */
+ u_long ifi_ipackets; /* packets received on interface */
+ u_long ifi_ierrors; /* input errors on interface */
+ u_long ifi_opackets; /* packets sent on interface */
+ u_long ifi_oerrors; /* output errors on interface */
+ u_long ifi_collisions; /* collisions on csma interfaces */
+ u_long ifi_ibytes; /* total number of octets received */
+ u_long ifi_obytes; /* total number of octets sent */
+ u_long ifi_imcasts; /* packets received via multicast */
+ u_long ifi_omcasts; /* packets sent via multicast */
+ u_long ifi_iqdrops; /* dropped on input, this interface */
+ u_long ifi_noproto; /* destined for unsupported protocol */
+ struct timeval ifi_lastchange;/* last updated */
+ } if_data;
+/* procedure handles */
+ void (*if_init) /* init routine */
+ __P((int));
+ int (*if_output) /* output routine (enqueue) */
+ __P((struct ifnet *, struct mbuf *, struct sockaddr *,
+ struct rtentry *));
+ void (*if_start) /* initiate output routine */
+ __P((struct ifnet *));
+ int (*if_done) /* output complete routine */
+ __P((struct ifnet *)); /* (XXX not used; fake prototype) */
+ int (*if_ioctl) /* ioctl routine */
+ __P((struct ifnet *, int, caddr_t));
+ void (*if_reset)
+ __P((int)); /* new autoconfig will permit removal */
+ void (*if_watchdog) /* timer routine */
+ __P((int));
+ struct ifqueue {
+ struct mbuf *ifq_head;
+ struct mbuf *ifq_tail;
+ int ifq_len;
+ int ifq_maxlen;
+ int ifq_drops;
+ } if_snd; /* output queue */
+};
+#define if_mtu if_data.ifi_mtu
+#define if_type if_data.ifi_type
+#define if_addrlen if_data.ifi_addrlen
+#define if_hdrlen if_data.ifi_hdrlen
+#define if_metric if_data.ifi_metric
+#define if_baudrate if_data.ifi_baudrate
+#define if_ipackets if_data.ifi_ipackets
+#define if_ierrors if_data.ifi_ierrors
+#define if_opackets if_data.ifi_opackets
+#define if_oerrors if_data.ifi_oerrors
+#define if_collisions if_data.ifi_collisions
+#define if_ibytes if_data.ifi_ibytes
+#define if_obytes if_data.ifi_obytes
+#define if_imcasts if_data.ifi_imcasts
+#define if_omcasts if_data.ifi_omcasts
+#define if_iqdrops if_data.ifi_iqdrops
+#define if_noproto if_data.ifi_noproto
+#define if_lastchange if_data.ifi_lastchange
+
+#define IFF_UP 0x1 /* interface is up */
+#define IFF_BROADCAST 0x2 /* broadcast address valid */
+#define IFF_DEBUG 0x4 /* turn on debugging */
+#define IFF_LOOPBACK 0x8 /* is a loopback net */
+#define IFF_POINTOPOINT 0x10 /* interface is point-to-point link */
+#define IFF_NOTRAILERS 0x20 /* avoid use of trailers */
+#define IFF_RUNNING 0x40 /* resources allocated */
+#define IFF_NOARP 0x80 /* no address resolution protocol */
+#define IFF_PROMISC 0x100 /* receive all packets */
+#define IFF_ALLMULTI 0x200 /* receive all multicast packets */
+#define IFF_OACTIVE 0x400 /* transmission in progress */
+#define IFF_SIMPLEX 0x800 /* can't hear own transmissions */
+#define IFF_LINK0 0x1000 /* per link layer defined bit */
+#define IFF_LINK1 0x2000 /* per link layer defined bit */
+#define IFF_LINK2 0x4000 /* per link layer defined bit */
+#define IFF_MULTICAST 0x8000 /* supports multicast */
+
+/* flags set internally only: */
+#define IFF_CANTCHANGE \
+ (IFF_BROADCAST|IFF_POINTOPOINT|IFF_RUNNING|IFF_OACTIVE|\
+ IFF_SIMPLEX|IFF_MULTICAST|IFF_ALLMULTI)
+
+/*
+ * Output queues (ifp->if_snd) and internetwork datagram level (pup level 1)
+ * input routines have queues of messages stored on ifqueue structures
+ * (defined above). Entries are added to and deleted from these structures
+ * by these macros, which should be called with ipl raised to splimp().
+ */
+#define IF_QFULL(ifq) ((ifq)->ifq_len >= (ifq)->ifq_maxlen)
+#define IF_DROP(ifq) ((ifq)->ifq_drops++)
+#define IF_ENQUEUE(ifq, m) { \
+ (m)->m_nextpkt = 0; \
+ if ((ifq)->ifq_tail == 0) \
+ (ifq)->ifq_head = m; \
+ else \
+ (ifq)->ifq_tail->m_nextpkt = m; \
+ (ifq)->ifq_tail = m; \
+ (ifq)->ifq_len++; \
+}
+#define IF_PREPEND(ifq, m) { \
+ (m)->m_nextpkt = (ifq)->ifq_head; \
+ if ((ifq)->ifq_tail == 0) \
+ (ifq)->ifq_tail = (m); \
+ (ifq)->ifq_head = (m); \
+ (ifq)->ifq_len++; \
+}
+#define IF_DEQUEUE(ifq, m) { \
+ (m) = (ifq)->ifq_head; \
+ if (m) { \
+ if (((ifq)->ifq_head = (m)->m_nextpkt) == 0) \
+ (ifq)->ifq_tail = 0; \
+ (m)->m_nextpkt = 0; \
+ (ifq)->ifq_len--; \
+ } \
+}
+
+#define IFQ_MAXLEN 50
+#define IFNET_SLOWHZ 1 /* granularity is 1 second */
+
+/*
+ * The ifaddr structure contains information about one address
+ * of an interface. They are maintained by the different address families,
+ * are allocated and attached when an address is set, and are linked
+ * together so all addresses for an interface can be located.
+ */
+struct ifaddr {
+ struct sockaddr *ifa_addr; /* address of interface */
+ struct sockaddr *ifa_dstaddr; /* other end of p-to-p link */
+#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */
+ struct sockaddr *ifa_netmask; /* used to determine subnet */
+ struct ifnet *ifa_ifp; /* back-pointer to interface */
+ struct ifaddr *ifa_next; /* next address for interface */
+ void (*ifa_rtrequest)(); /* check or clean routes (+ or -)'d */
+ u_short ifa_flags; /* mostly rt_flags for cloning */
+ short ifa_refcnt; /* extra to malloc for link info */
+ int ifa_metric; /* cost of going out this interface */
+#ifdef notdef
+ struct rtentry *ifa_rt; /* XXXX for ROUTETOIF ????? */
+#endif
+};
+#define IFA_ROUTE RTF_UP /* route installed */
+
+/*
+ * Message format for use in obtaining information about interfaces
+ * from getkerninfo and the routing socket
+ */
+struct if_msghdr {
+ u_short ifm_msglen; /* to skip over non-understood messages */
+ u_char ifm_version; /* future binary compatability */
+ u_char ifm_type; /* message type */
+ int ifm_addrs; /* like rtm_addrs */
+ int ifm_flags; /* value of if_flags */
+ u_short ifm_index; /* index for associated ifp */
+ struct if_data ifm_data;/* statistics and other data about if */
+};
+
+/*
+ * Message format for use in obtaining information about interface addresses
+ * from getkerninfo and the routing socket
+ */
+struct ifa_msghdr {
+ u_short ifam_msglen; /* to skip over non-understood messages */
+ u_char ifam_version; /* future binary compatability */
+ u_char ifam_type; /* message type */
+ int ifam_addrs; /* like rtm_addrs */
+ int ifam_flags; /* value of ifa_flags */
+ u_short ifam_index; /* index for associated ifp */
+ int ifam_metric; /* value of ifa_metric */
+};
+
+/*
+ * Interface request structure used for socket
+ * ioctl's. All interface ioctl's must have parameter
+ * definitions which begin with ifr_name. The
+ * remainder may be interface specific.
+ */
+struct ifreq {
+#define IFNAMSIZ 16
+ char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ union {
+ struct sockaddr ifru_addr;
+ struct sockaddr ifru_dstaddr;
+ struct sockaddr ifru_broadaddr;
+ short ifru_flags;
+ int ifru_metric;
+ caddr_t ifru_data;
+ } ifr_ifru;
+#define ifr_addr ifr_ifru.ifru_addr /* address */
+#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-to-p link */
+#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
+#define ifr_flags ifr_ifru.ifru_flags /* flags */
+#define ifr_metric ifr_ifru.ifru_metric /* metric */
+#define ifr_data ifr_ifru.ifru_data /* for use by interface */
+};
+
+struct ifaliasreq {
+ char ifra_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ struct sockaddr ifra_addr;
+ struct sockaddr ifra_broadaddr;
+ struct sockaddr ifra_mask;
+};
+
+/*
+ * Structure used in SIOCGIFCONF request.
+ * Used to retrieve interface configuration
+ * for machine (useful for programs which
+ * must know all networks accessible).
+ */
+struct ifconf {
+ int ifc_len; /* size of associated buffer */
+ union {
+ caddr_t ifcu_buf;
+ struct ifreq *ifcu_req;
+ } ifc_ifcu;
+#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */
+#define ifc_req ifc_ifcu.ifcu_req /* array of structures returned */
+};
+
+#include <net/if_arp.h>
+
+#ifdef KERNEL
+#define IFAFREE(ifa) \
+ if ((ifa)->ifa_refcnt <= 0) \
+ ifafree(ifa); \
+ else \
+ (ifa)->ifa_refcnt--;
+
+struct ifnet *ifnet;
+
+void ether_ifattach __P((struct ifnet *));
+void ether_input __P((struct ifnet *, struct ether_header *, struct mbuf *));
+int ether_output __P((struct ifnet *,
+ struct mbuf *, struct sockaddr *, struct rtentry *));
+char *ether_sprintf __P((u_char *));
+
+void if_attach __P((struct ifnet *));
+void if_down __P((struct ifnet *));
+void if_qflush __P((struct ifqueue *));
+void if_slowtimo __P((void *));
+void if_up __P((struct ifnet *));
+#ifdef vax
+void ifubareset __P((int));
+#endif
+int ifconf __P((int, caddr_t));
+void ifinit __P((void));
+int ifioctl __P((struct socket *, int, caddr_t, struct proc *));
+int ifpromisc __P((struct ifnet *, int));
+struct ifnet *ifunit __P((char *));
+
+struct ifaddr *ifa_ifwithaddr __P((struct sockaddr *));
+struct ifaddr *ifa_ifwithaf __P((int));
+struct ifaddr *ifa_ifwithdstaddr __P((struct sockaddr *));
+struct ifaddr *ifa_ifwithnet __P((struct sockaddr *));
+struct ifaddr *ifa_ifwithroute __P((int, struct sockaddr *,
+ struct sockaddr *));
+struct ifaddr *ifaof_ifpforaddr __P((struct sockaddr *, struct ifnet *));
+void ifafree __P((struct ifaddr *));
+void link_rtrequest __P((int, struct rtentry *, struct sockaddr *));
+
+int loioctl __P((struct ifnet *, int, caddr_t));
+void loopattach __P((void));
+int looutput __P((struct ifnet *,
+ struct mbuf *, struct sockaddr *, struct rtentry *));
+void lortrequest __P((int, struct rtentry *, struct sockaddr *));
+#endif
diff --git a/sys/net/if_arp.h b/sys/net/if_arp.h
new file mode 100644
index 0000000..84581cb
--- /dev/null
+++ b/sys/net/if_arp.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_arp.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Address Resolution Protocol.
+ *
+ * See RFC 826 for protocol description. ARP packets are variable
+ * in size; the arphdr structure defines the fixed-length portion.
+ * Protocol type values are the same as those for 10 Mb/s Ethernet.
+ * It is followed by the variable-sized fields ar_sha, arp_spa,
+ * arp_tha and arp_tpa in that order, according to the lengths
+ * specified. Field names used correspond to RFC 826.
+ */
+struct arphdr {
+ u_short ar_hrd; /* format of hardware address */
+#define ARPHRD_ETHER 1 /* ethernet hardware format */
+#define ARPHRD_FRELAY 15 /* frame relay hardware format */
+ u_short ar_pro; /* format of protocol address */
+ u_char ar_hln; /* length of hardware address */
+ u_char ar_pln; /* length of protocol address */
+ u_short ar_op; /* one of: */
+#define ARPOP_REQUEST 1 /* request to resolve address */
+#define ARPOP_REPLY 2 /* response to previous request */
+#define ARPOP_REVREQUEST 3 /* request protocol address given hardware */
+#define ARPOP_REVREPLY 4 /* response giving protocol address */
+#define ARPOP_INVREQUEST 8 /* request to identify peer */
+#define ARPOP_INVREPLY 9 /* response identifying peer */
+/*
+ * The remaining fields are variable in size,
+ * according to the sizes above.
+ */
+#ifdef COMMENT_ONLY
+ u_char ar_sha[]; /* sender hardware address */
+ u_char ar_spa[]; /* sender protocol address */
+ u_char ar_tha[]; /* target hardware address */
+ u_char ar_tpa[]; /* target protocol address */
+#endif
+};
+
+/*
+ * ARP ioctl request
+ */
+struct arpreq {
+ struct sockaddr arp_pa; /* protocol address */
+ struct sockaddr arp_ha; /* hardware address */
+ int arp_flags; /* flags */
+};
+/* arp_flags and at_flags field values */
+#define ATF_INUSE 0x01 /* entry in use */
+#define ATF_COM 0x02 /* completed entry (enaddr valid) */
+#define ATF_PERM 0x04 /* permanent entry */
+#define ATF_PUBL 0x08 /* publish entry (respond for other host) */
+#define ATF_USETRAILERS 0x10 /* has requested trailers */
diff --git a/sys/net/if_dl.h b/sys/net/if_dl.h
new file mode 100644
index 0000000..3e53449
--- /dev/null
+++ b/sys/net/if_dl.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_dl.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * A Link-Level Sockaddr may specify the interface in one of two
+ * ways: either by means of a system-provided index number (computed
+ * anew and possibly differently on every reboot), or by a human-readable
+ * string such as "il0" (for managerial convenience).
+ *
+ * Census taking actions, such as something akin to SIOCGCONF would return
+ * both the index and the human name.
+ *
+ * High volume transactions (such as giving a link-level ``from'' address
+ * in a recvfrom or recvmsg call) may be likely only to provide the indexed
+ * form, (which requires fewer copy operations and less space).
+ *
+ * The form and interpretation of the link-level address is purely a matter
+ * of convention between the device driver and its consumers; however, it is
+ * expected that all drivers for an interface of a given if_type will agree.
+ */
+
+/*
+ * Structure of a Link-Level sockaddr:
+ */
+struct sockaddr_dl {
+ u_char sdl_len; /* Total length of sockaddr */
+ u_char sdl_family; /* AF_DLI */
+ u_short sdl_index; /* if != 0, system given index for interface */
+ u_char sdl_type; /* interface type */
+ u_char sdl_nlen; /* interface name length, no trailing 0 reqd. */
+ u_char sdl_alen; /* link level address length */
+ u_char sdl_slen; /* link layer selector length */
+ char sdl_data[12]; /* minimum work area, can be larger;
+ contains both if name and ll address */
+};
+
+#define LLADDR(s) ((caddr_t)((s)->sdl_data + (s)->sdl_nlen))
+
+#ifndef KERNEL
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+void link_addr __P((const char *, struct sockaddr_dl *));
+char *link_ntoa __P((const struct sockaddr_dl *));
+__END_DECLS
+
+#endif /* !KERNEL */
diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c
new file mode 100644
index 0000000..d4d6680
--- /dev/null
+++ b/sys/net/if_ethersubr.c
@@ -0,0 +1,675 @@
+/*
+ * Copyright (c) 1982, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_ethersubr.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+
+#include <machine/cpu.h>
+
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/route.h>
+#include <net/if_llc.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#endif
+#include <netinet/if_ether.h>
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#ifdef ISO
+#include <netiso/argo_debug.h>
+#include <netiso/iso.h>
+#include <netiso/iso_var.h>
+#include <netiso/iso_snpac.h>
+#endif
+
+#ifdef LLC
+#include <netccitt/dll.h>
+#include <netccitt/llc_var.h>
+#endif
+
+#if defined(LLC) && defined(CCITT)
+extern struct ifqueue pkintrq;
+#endif
+
+u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+extern struct ifnet loif;
+#define senderr(e) { error = (e); goto bad;}
+
+/*
+ * Ethernet output routine.
+ * Encapsulate a packet of type family for the local net.
+ * Use trailer local net encapsulation if enough data in first
+ * packet leaves a multiple of 512 bytes of data in remainder.
+ * Assumes that ifp is actually pointer to arpcom structure.
+ */
+int
+ether_output(ifp, m0, dst, rt0)
+ register struct ifnet *ifp;
+ struct mbuf *m0;
+ struct sockaddr *dst;
+ struct rtentry *rt0;
+{
+ short type;
+ int s, error = 0;
+ u_char edst[6];
+ register struct mbuf *m = m0;
+ register struct rtentry *rt;
+ struct mbuf *mcopy = (struct mbuf *)0;
+ register struct ether_header *eh;
+ int off, len = m->m_pkthdr.len;
+ struct arpcom *ac = (struct arpcom *)ifp;
+
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
+ senderr(ENETDOWN);
+ ifp->if_lastchange = time;
+ if (rt = rt0) {
+ if ((rt->rt_flags & RTF_UP) == 0) {
+ if (rt0 = rt = rtalloc1(dst, 1))
+ rt->rt_refcnt--;
+ else
+ senderr(EHOSTUNREACH);
+ }
+ if (rt->rt_flags & RTF_GATEWAY) {
+ if (rt->rt_gwroute == 0)
+ goto lookup;
+ if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
+ rtfree(rt); rt = rt0;
+ lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1);
+ if ((rt = rt->rt_gwroute) == 0)
+ senderr(EHOSTUNREACH);
+ }
+ }
+ if (rt->rt_flags & RTF_REJECT)
+ if (rt->rt_rmx.rmx_expire == 0 ||
+ time.tv_sec < rt->rt_rmx.rmx_expire)
+ senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
+ }
+ switch (dst->sa_family) {
+
+#ifdef INET
+ case AF_INET:
+ if (!arpresolve(ac, rt, m, dst, edst))
+ return (0); /* if not yet resolved */
+ /* If broadcasting on a simplex interface, loopback a copy */
+ if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
+ mcopy = m_copy(m, 0, (int)M_COPYALL);
+ off = m->m_pkthdr.len - m->m_len;
+ type = ETHERTYPE_IP;
+ break;
+#endif
+#ifdef NS
+ case AF_NS:
+ type = ETHERTYPE_NS;
+ bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
+ (caddr_t)edst, sizeof (edst));
+ if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, sizeof(edst)))
+ return (looutput(ifp, m, dst, rt));
+ /* If broadcasting on a simplex interface, loopback a copy */
+ if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
+ mcopy = m_copy(m, 0, (int)M_COPYALL);
+ break;
+#endif
+#ifdef ISO
+ case AF_ISO: {
+ int snpalen;
+ struct llc *l;
+ register struct sockaddr_dl *sdl;
+
+ if (rt && (sdl = (struct sockaddr_dl *)rt->rt_gateway) &&
+ sdl->sdl_family == AF_LINK && sdl->sdl_alen > 0) {
+ bcopy(LLADDR(sdl), (caddr_t)edst, sizeof(edst));
+ } else if (error =
+ iso_snparesolve(ifp, (struct sockaddr_iso *)dst,
+ (char *)edst, &snpalen))
+ goto bad; /* Not Resolved */
+ /* If broadcasting on a simplex interface, loopback a copy */
+ if (*edst & 1)
+ m->m_flags |= (M_BCAST|M_MCAST);
+ if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX) &&
+ (mcopy = m_copy(m, 0, (int)M_COPYALL))) {
+ M_PREPEND(mcopy, sizeof (*eh), M_DONTWAIT);
+ if (mcopy) {
+ eh = mtod(mcopy, struct ether_header *);
+ bcopy((caddr_t)edst,
+ (caddr_t)eh->ether_dhost, sizeof (edst));
+ bcopy((caddr_t)ac->ac_enaddr,
+ (caddr_t)eh->ether_shost, sizeof (edst));
+ }
+ }
+ M_PREPEND(m, 3, M_DONTWAIT);
+ if (m == NULL)
+ return (0);
+ type = m->m_pkthdr.len;
+ l = mtod(m, struct llc *);
+ l->llc_dsap = l->llc_ssap = LLC_ISO_LSAP;
+ l->llc_control = LLC_UI;
+ len += 3;
+ IFDEBUG(D_ETHER)
+ int i;
+ printf("unoutput: sending pkt to: ");
+ for (i=0; i<6; i++)
+ printf("%x ", edst[i] & 0xff);
+ printf("\n");
+ ENDDEBUG
+ } break;
+#endif /* ISO */
+#ifdef LLC
+/* case AF_NSAP: */
+ case AF_CCITT: {
+ register struct sockaddr_dl *sdl =
+ (struct sockaddr_dl *) rt -> rt_gateway;
+
+ if (sdl && sdl->sdl_family == AF_LINK
+ && sdl->sdl_alen > 0) {
+ bcopy(LLADDR(sdl), (char *)edst,
+ sizeof(edst));
+ } else goto bad; /* Not a link interface ? Funny ... */
+ if ((ifp->if_flags & IFF_SIMPLEX) && (*edst & 1) &&
+ (mcopy = m_copy(m, 0, (int)M_COPYALL))) {
+ M_PREPEND(mcopy, sizeof (*eh), M_DONTWAIT);
+ if (mcopy) {
+ eh = mtod(mcopy, struct ether_header *);
+ bcopy((caddr_t)edst,
+ (caddr_t)eh->ether_dhost, sizeof (edst));
+ bcopy((caddr_t)ac->ac_enaddr,
+ (caddr_t)eh->ether_shost, sizeof (edst));
+ }
+ }
+ type = m->m_pkthdr.len;
+#ifdef LLC_DEBUG
+ {
+ int i;
+ register struct llc *l = mtod(m, struct llc *);
+
+ printf("ether_output: sending LLC2 pkt to: ");
+ for (i=0; i<6; i++)
+ printf("%x ", edst[i] & 0xff);
+ printf(" len 0x%x dsap 0x%x ssap 0x%x control 0x%x\n",
+ type & 0xff, l->llc_dsap & 0xff, l->llc_ssap &0xff,
+ l->llc_control & 0xff);
+
+ }
+#endif /* LLC_DEBUG */
+ } break;
+#endif /* LLC */
+
+ case AF_UNSPEC:
+ eh = (struct ether_header *)dst->sa_data;
+ bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
+ type = eh->ether_type;
+ break;
+
+ default:
+ printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit,
+ dst->sa_family);
+ senderr(EAFNOSUPPORT);
+ }
+
+
+ if (mcopy)
+ (void) looutput(ifp, mcopy, dst, rt);
+ /*
+ * Add local net header. If no space in first mbuf,
+ * allocate another.
+ */
+ M_PREPEND(m, sizeof (struct ether_header), M_DONTWAIT);
+ if (m == 0)
+ senderr(ENOBUFS);
+ eh = mtod(m, struct ether_header *);
+ type = htons((u_short)type);
+ bcopy((caddr_t)&type,(caddr_t)&eh->ether_type,
+ sizeof(eh->ether_type));
+ bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst));
+ bcopy((caddr_t)ac->ac_enaddr, (caddr_t)eh->ether_shost,
+ sizeof(eh->ether_shost));
+ s = splimp();
+ /*
+ * Queue message on interface, and start output if interface
+ * not yet active.
+ */
+ if (IF_QFULL(&ifp->if_snd)) {
+ IF_DROP(&ifp->if_snd);
+ splx(s);
+ senderr(ENOBUFS);
+ }
+ IF_ENQUEUE(&ifp->if_snd, m);
+ if ((ifp->if_flags & IFF_OACTIVE) == 0)
+ (*ifp->if_start)(ifp);
+ splx(s);
+ ifp->if_obytes += len + sizeof (struct ether_header);
+ if (m->m_flags & M_MCAST)
+ ifp->if_omcasts++;
+ return (error);
+
+bad:
+ if (m)
+ m_freem(m);
+ return (error);
+}
+
+/*
+ * Process a received Ethernet packet;
+ * the packet is in the mbuf chain m without
+ * the ether header, which is provided separately.
+ */
+void
+ether_input(ifp, eh, m)
+ struct ifnet *ifp;
+ register struct ether_header *eh;
+ struct mbuf *m;
+{
+ register struct ifqueue *inq;
+ register struct llc *l;
+ struct arpcom *ac = (struct arpcom *)ifp;
+ int s;
+
+ if ((ifp->if_flags & IFF_UP) == 0) {
+ m_freem(m);
+ return;
+ }
+ ifp->if_lastchange = time;
+ ifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh);
+ if (bcmp((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost,
+ sizeof(etherbroadcastaddr)) == 0)
+ m->m_flags |= M_BCAST;
+ else if (eh->ether_dhost[0] & 1)
+ m->m_flags |= M_MCAST;
+ if (m->m_flags & (M_BCAST|M_MCAST))
+ ifp->if_imcasts++;
+
+ switch (eh->ether_type) {
+#ifdef INET
+ case ETHERTYPE_IP:
+ schednetisr(NETISR_IP);
+ inq = &ipintrq;
+ break;
+
+ case ETHERTYPE_ARP:
+ schednetisr(NETISR_ARP);
+ inq = &arpintrq;
+ break;
+#endif
+#ifdef NS
+ case ETHERTYPE_NS:
+ schednetisr(NETISR_NS);
+ inq = &nsintrq;
+ break;
+
+#endif
+ default:
+#if defined (ISO) || defined (LLC)
+ if (eh->ether_type > ETHERMTU)
+ goto dropanyway;
+ l = mtod(m, struct llc *);
+ switch (l->llc_dsap) {
+#ifdef ISO
+ case LLC_ISO_LSAP:
+ switch (l->llc_control) {
+ case LLC_UI:
+ /* LLC_UI_P forbidden in class 1 service */
+ if ((l->llc_dsap == LLC_ISO_LSAP) &&
+ (l->llc_ssap == LLC_ISO_LSAP)) {
+ /* LSAP for ISO */
+ if (m->m_pkthdr.len > eh->ether_type)
+ m_adj(m, eh->ether_type - m->m_pkthdr.len);
+ m->m_data += 3; /* XXX */
+ m->m_len -= 3; /* XXX */
+ m->m_pkthdr.len -= 3; /* XXX */
+ M_PREPEND(m, sizeof *eh, M_DONTWAIT);
+ if (m == 0)
+ return;
+ *mtod(m, struct ether_header *) = *eh;
+ IFDEBUG(D_ETHER)
+ printf("clnp packet");
+ ENDDEBUG
+ schednetisr(NETISR_ISO);
+ inq = &clnlintrq;
+ break;
+ }
+ goto dropanyway;
+
+ case LLC_XID:
+ case LLC_XID_P:
+ if(m->m_len < 6)
+ goto dropanyway;
+ l->llc_window = 0;
+ l->llc_fid = 9;
+ l->llc_class = 1;
+ l->llc_dsap = l->llc_ssap = 0;
+ /* Fall through to */
+ case LLC_TEST:
+ case LLC_TEST_P:
+ {
+ struct sockaddr sa;
+ register struct ether_header *eh2;
+ int i;
+ u_char c = l->llc_dsap;
+
+ l->llc_dsap = l->llc_ssap;
+ l->llc_ssap = c;
+ if (m->m_flags & (M_BCAST | M_MCAST))
+ bcopy((caddr_t)ac->ac_enaddr,
+ (caddr_t)eh->ether_dhost, 6);
+ sa.sa_family = AF_UNSPEC;
+ sa.sa_len = sizeof(sa);
+ eh2 = (struct ether_header *)sa.sa_data;
+ for (i = 0; i < 6; i++) {
+ eh2->ether_shost[i] = c = eh->ether_dhost[i];
+ eh2->ether_dhost[i] =
+ eh->ether_dhost[i] = eh->ether_shost[i];
+ eh->ether_shost[i] = c;
+ }
+ ifp->if_output(ifp, m, &sa, NULL);
+ return;
+ }
+ default:
+ m_freem(m);
+ return;
+ }
+ break;
+#endif /* ISO */
+#ifdef LLC
+ case LLC_X25_LSAP:
+ {
+ if (m->m_pkthdr.len > eh->ether_type)
+ m_adj(m, eh->ether_type - m->m_pkthdr.len);
+ M_PREPEND(m, sizeof(struct sdl_hdr) , M_DONTWAIT);
+ if (m == 0)
+ return;
+ if ( !sdl_sethdrif(ifp, eh->ether_shost, LLC_X25_LSAP,
+ eh->ether_dhost, LLC_X25_LSAP, 6,
+ mtod(m, struct sdl_hdr *)))
+ panic("ETHER cons addr failure");
+ mtod(m, struct sdl_hdr *)->sdlhdr_len = eh->ether_type;
+#ifdef LLC_DEBUG
+ printf("llc packet\n");
+#endif /* LLC_DEBUG */
+ schednetisr(NETISR_CCITT);
+ inq = &llcintrq;
+ break;
+ }
+#endif /* LLC */
+ dropanyway:
+ default:
+ m_freem(m);
+ return;
+ }
+#else /* ISO || LLC */
+ m_freem(m);
+ return;
+#endif /* ISO || LLC */
+ }
+
+ s = splimp();
+ if (IF_QFULL(inq)) {
+ IF_DROP(inq);
+ m_freem(m);
+ } else
+ IF_ENQUEUE(inq, m);
+ splx(s);
+}
+
+/*
+ * Convert Ethernet address to printable (loggable) representation.
+ */
+static char digits[] = "0123456789abcdef";
+char *
+ether_sprintf(ap)
+ register u_char *ap;
+{
+ register i;
+ static char etherbuf[18];
+ register char *cp = etherbuf;
+
+ for (i = 0; i < 6; i++) {
+ *cp++ = digits[*ap >> 4];
+ *cp++ = digits[*ap++ & 0xf];
+ *cp++ = ':';
+ }
+ *--cp = 0;
+ return (etherbuf);
+}
+
+/*
+ * Perform common duties while attaching to interface list
+ */
+void
+ether_ifattach(ifp)
+ register struct ifnet *ifp;
+{
+ register struct ifaddr *ifa;
+ register struct sockaddr_dl *sdl;
+
+ ifp->if_type = IFT_ETHER;
+ ifp->if_addrlen = 6;
+ ifp->if_hdrlen = 14;
+ ifp->if_mtu = ETHERMTU;
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+ if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) &&
+ sdl->sdl_family == AF_LINK) {
+ sdl->sdl_type = IFT_ETHER;
+ sdl->sdl_alen = ifp->if_addrlen;
+ bcopy((caddr_t)((struct arpcom *)ifp)->ac_enaddr,
+ LLADDR(sdl), ifp->if_addrlen);
+ break;
+ }
+}
+
+u_char ether_ipmulticast_min[6] = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 };
+u_char ether_ipmulticast_max[6] = { 0x01, 0x00, 0x5e, 0x7f, 0xff, 0xff };
+/*
+ * Add an Ethernet multicast address or range of addresses to the list for a
+ * given interface.
+ */
+int
+ether_addmulti(ifr, ac)
+ struct ifreq *ifr;
+ register struct arpcom *ac;
+{
+ register struct ether_multi *enm;
+ struct sockaddr_in *sin;
+ u_char addrlo[6];
+ u_char addrhi[6];
+ int s = splimp();
+
+ switch (ifr->ifr_addr.sa_family) {
+
+ case AF_UNSPEC:
+ bcopy(ifr->ifr_addr.sa_data, addrlo, 6);
+ bcopy(addrlo, addrhi, 6);
+ break;
+
+#ifdef INET
+ case AF_INET:
+ sin = (struct sockaddr_in *)&(ifr->ifr_addr);
+ if (sin->sin_addr.s_addr == INADDR_ANY) {
+ /*
+ * An IP address of INADDR_ANY means listen to all
+ * of the Ethernet multicast addresses used for IP.
+ * (This is for the sake of IP multicast routers.)
+ */
+ bcopy(ether_ipmulticast_min, addrlo, 6);
+ bcopy(ether_ipmulticast_max, addrhi, 6);
+ }
+ else {
+ ETHER_MAP_IP_MULTICAST(&sin->sin_addr, addrlo);
+ bcopy(addrlo, addrhi, 6);
+ }
+ break;
+#endif
+
+ default:
+ splx(s);
+ return (EAFNOSUPPORT);
+ }
+
+ /*
+ * Verify that we have valid Ethernet multicast addresses.
+ */
+ if ((addrlo[0] & 0x01) != 1 || (addrhi[0] & 0x01) != 1) {
+ splx(s);
+ return (EINVAL);
+ }
+ /*
+ * See if the address range is already in the list.
+ */
+ ETHER_LOOKUP_MULTI(addrlo, addrhi, ac, enm);
+ if (enm != NULL) {
+ /*
+ * Found it; just increment the reference count.
+ */
+ ++enm->enm_refcount;
+ splx(s);
+ return (0);
+ }
+ /*
+ * New address or range; malloc a new multicast record
+ * and link it into the interface's multicast list.
+ */
+ enm = (struct ether_multi *)malloc(sizeof(*enm), M_IFMADDR, M_NOWAIT);
+ if (enm == NULL) {
+ splx(s);
+ return (ENOBUFS);
+ }
+ bcopy(addrlo, enm->enm_addrlo, 6);
+ bcopy(addrhi, enm->enm_addrhi, 6);
+ enm->enm_ac = ac;
+ enm->enm_refcount = 1;
+ enm->enm_next = ac->ac_multiaddrs;
+ ac->ac_multiaddrs = enm;
+ ac->ac_multicnt++;
+ splx(s);
+ /*
+ * Return ENETRESET to inform the driver that the list has changed
+ * and its reception filter should be adjusted accordingly.
+ */
+ return (ENETRESET);
+}
+
+/*
+ * Delete a multicast address record.
+ */
+int
+ether_delmulti(ifr, ac)
+ struct ifreq *ifr;
+ register struct arpcom *ac;
+{
+ register struct ether_multi *enm;
+ register struct ether_multi **p;
+ struct sockaddr_in *sin;
+ u_char addrlo[6];
+ u_char addrhi[6];
+ int s = splimp();
+
+ switch (ifr->ifr_addr.sa_family) {
+
+ case AF_UNSPEC:
+ bcopy(ifr->ifr_addr.sa_data, addrlo, 6);
+ bcopy(addrlo, addrhi, 6);
+ break;
+
+#ifdef INET
+ case AF_INET:
+ sin = (struct sockaddr_in *)&(ifr->ifr_addr);
+ if (sin->sin_addr.s_addr == INADDR_ANY) {
+ /*
+ * An IP address of INADDR_ANY means stop listening
+ * to the range of Ethernet multicast addresses used
+ * for IP.
+ */
+ bcopy(ether_ipmulticast_min, addrlo, 6);
+ bcopy(ether_ipmulticast_max, addrhi, 6);
+ }
+ else {
+ ETHER_MAP_IP_MULTICAST(&sin->sin_addr, addrlo);
+ bcopy(addrlo, addrhi, 6);
+ }
+ break;
+#endif
+
+ default:
+ splx(s);
+ return (EAFNOSUPPORT);
+ }
+
+ /*
+ * Look up the address in our list.
+ */
+ ETHER_LOOKUP_MULTI(addrlo, addrhi, ac, enm);
+ if (enm == NULL) {
+ splx(s);
+ return (ENXIO);
+ }
+ if (--enm->enm_refcount != 0) {
+ /*
+ * Still some claims to this record.
+ */
+ splx(s);
+ return (0);
+ }
+ /*
+ * No remaining claims to this record; unlink and free it.
+ */
+ for (p = &enm->enm_ac->ac_multiaddrs;
+ *p != enm;
+ p = &(*p)->enm_next)
+ continue;
+ *p = (*p)->enm_next;
+ free(enm, M_IFMADDR);
+ ac->ac_multicnt--;
+ splx(s);
+ /*
+ * Return ENETRESET to inform the driver that the list has changed
+ * and its reception filter should be adjusted accordingly.
+ */
+ return (ENETRESET);
+}
diff --git a/sys/net/if_llc.h b/sys/net/if_llc.h
new file mode 100644
index 0000000..90dcb07
--- /dev/null
+++ b/sys/net/if_llc.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_llc.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * IEEE 802.2 Link Level Control headers, for use in conjunction with
+ * 802.{3,4,5} media access control methods.
+ *
+ * Headers here do not use bit fields due to shortcommings in many
+ * compilers.
+ */
+
+struct llc {
+ u_char llc_dsap;
+ u_char llc_ssap;
+ union {
+ struct {
+ u_char control;
+ u_char format_id;
+ u_char class;
+ u_char window_x2;
+ } type_u;
+ struct {
+ u_char num_snd_x2;
+ u_char num_rcv_x2;
+ } type_i;
+ struct {
+ u_char control;
+ u_char num_rcv_x2;
+ } type_s;
+ struct {
+ u_char control;
+ struct frmrinfo {
+ u_char rej_pdu_0;
+ u_char rej_pdu_1;
+ u_char frmr_control;
+ u_char frmr_control_ext;
+ u_char frmr_cause;
+ } frmrinfo;
+ } type_frmr;
+ struct {
+ u_char control;
+ u_char org_code[3];
+ u_short ether_type;
+ } type_snap;
+ struct {
+ u_char control;
+ u_char control_ext;
+ } type_raw;
+ } llc_un;
+};
+#define llc_control llc_un.type_u.control
+#define llc_control_ext llc_un.type_raw.control_ext
+#define llc_fid llc_un.type_u.format_id
+#define llc_class llc_un.type_u.class
+#define llc_window llc_un.type_u.window_x2
+#define llc_frmrinfo llc_un.type_frmr.frmrinfo
+#define llc_frmr_pdu0 llc_un.type_frmr.frmrinfo.rej_pdu0
+#define llc_frmr_pdu1 llc_un.type_frmr.frmrinfo.rej_pdu1
+#define llc_frmr_control llc_un.type_frmr.frmrinfo.frmr_control
+#define llc_frmr_control_ext llc_un.type_frmr.frmrinfo.frmr_control_ext
+#define llc_frmr_cause llc_un.type_frmr.frmrinfo.frmr_control_ext
+
+/*
+ * Don't use sizeof(struct llc_un) for LLC header sizes
+ */
+#define LLC_ISFRAMELEN 4
+#define LLC_UFRAMELEN 3
+#define LLC_FRMRLEN 7
+
+/*
+ * Unnumbered LLC format commands
+ */
+#define LLC_UI 0x3
+#define LLC_UI_P 0x13
+#define LLC_DISC 0x43
+#define LLC_DISC_P 0x53
+#define LLC_UA 0x63
+#define LLC_UA_P 0x73
+#define LLC_TEST 0xe3
+#define LLC_TEST_P 0xf3
+#define LLC_FRMR 0x87
+#define LLC_FRMR_P 0x97
+#define LLC_DM 0x0f
+#define LLC_DM_P 0x1f
+#define LLC_XID 0xaf
+#define LLC_XID_P 0xbf
+#define LLC_SABME 0x6f
+#define LLC_SABME_P 0x7f
+
+/*
+ * Supervisory LLC commands
+ */
+#define LLC_RR 0x01
+#define LLC_RNR 0x05
+#define LLC_REJ 0x09
+
+/*
+ * Info format - dummy only
+ */
+#define LLC_INFO 0x00
+
+/*
+ * ISO PDTR 10178 contains among others
+ */
+#define LLC_X25_LSAP 0x7e
+#define LLC_SNAP_LSAP 0xaa
+#define LLC_ISO_LSAP 0xfe
+
+
+
+
+
+
diff --git a/sys/net/if_loop.c b/sys/net/if_loop.c
new file mode 100644
index 0000000..9a5a957
--- /dev/null
+++ b/sys/net/if_loop.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_loop.c 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Loopback interface driver for protocol testing and timing.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <machine/cpu.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/netisr.h>
+#include <net/route.h>
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#ifdef ISO
+#include <netiso/iso.h>
+#include <netiso/iso_var.h>
+#endif
+
+#include "bpfilter.h"
+
+#ifdef TINY_LOMTU
+#define LOMTU (1024+512)
+#else
+#define LOMTU 65532
+#endif
+
+struct ifnet loif;
+
+/* ARGSUSED */
+void
+loopattach(void)
+{
+ register struct ifnet *ifp = &loif;
+
+ ifp->if_name = "lo";
+ ifp->if_mtu = LOMTU;
+ ifp->if_flags = IFF_LOOPBACK | IFF_MULTICAST;
+ ifp->if_ioctl = loioctl;
+ ifp->if_output = looutput;
+ ifp->if_type = IFT_LOOP;
+ ifp->if_hdrlen = 0;
+ ifp->if_addrlen = 0;
+ if_attach(ifp);
+#if NBPFILTER > 0
+ bpfattach(&ifp->if_bpf, ifp, DLT_NULL, sizeof(u_int));
+#endif
+}
+
+TEXT_SET(pseudo_set, loopattach);
+
+int
+looutput(ifp, m, dst, rt)
+ struct ifnet *ifp;
+ register struct mbuf *m;
+ struct sockaddr *dst;
+ register struct rtentry *rt;
+{
+ int s, isr;
+ register struct ifqueue *ifq = 0;
+
+ if ((m->m_flags & M_PKTHDR) == 0)
+ panic("looutput no HDR");
+ ifp->if_lastchange = time;
+#if NBPFILTER > 0
+ if (loif.if_bpf) {
+ /*
+ * We need to prepend the address family as
+ * a four byte field. Cons up a dummy header
+ * to pacify bpf. This is safe because bpf
+ * will only read from the mbuf (i.e., it won't
+ * try to free it or keep a pointer a to it).
+ */
+ struct mbuf m0;
+ u_int af = dst->sa_family;
+
+ m0.m_next = m;
+ m0.m_len = 4;
+ m0.m_data = (char *)&af;
+
+ bpf_mtap(loif.if_bpf, &m0);
+ }
+#endif
+ m->m_pkthdr.rcvif = ifp;
+
+ if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
+ m_freem(m);
+ return (rt->rt_flags & RTF_BLACKHOLE ? 0 :
+ rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
+ }
+ ifp->if_opackets++;
+ ifp->if_obytes += m->m_pkthdr.len;
+ switch (dst->sa_family) {
+
+#ifdef INET
+ case AF_INET:
+ ifq = &ipintrq;
+ isr = NETISR_IP;
+ break;
+#endif
+#ifdef NS
+ case AF_NS:
+ ifq = &nsintrq;
+ isr = NETISR_NS;
+ break;
+#endif
+#ifdef ISO
+ case AF_ISO:
+ ifq = &clnlintrq;
+ isr = NETISR_ISO;
+ break;
+#endif
+ default:
+ printf("lo%d: can't handle af%d\n", ifp->if_unit,
+ dst->sa_family);
+ m_freem(m);
+ return (EAFNOSUPPORT);
+ }
+ s = splimp();
+ if (IF_QFULL(ifq)) {
+ IF_DROP(ifq);
+ m_freem(m);
+ splx(s);
+ return (ENOBUFS);
+ }
+ IF_ENQUEUE(ifq, m);
+ schednetisr(isr);
+ ifp->if_ipackets++;
+ ifp->if_ibytes += m->m_pkthdr.len;
+ splx(s);
+ return (0);
+}
+
+/* ARGSUSED */
+void
+lortrequest(cmd, rt, sa)
+ int cmd;
+ struct rtentry *rt;
+ struct sockaddr *sa;
+{
+
+ if (rt)
+ rt->rt_rmx.rmx_mtu = LOMTU;
+}
+
+/*
+ * Process an ioctl request.
+ */
+/* ARGSUSED */
+int
+loioctl(ifp, cmd, data)
+ register struct ifnet *ifp;
+ int cmd;
+ caddr_t data;
+{
+ register struct ifaddr *ifa;
+ register struct ifreq *ifr;
+ register int error = 0;
+
+ switch (cmd) {
+
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ ifa = (struct ifaddr *)data;
+ if (ifa != 0 && ifa->ifa_addr->sa_family == AF_ISO)
+ ifa->ifa_rtrequest = lortrequest;
+ /*
+ * Everything else is done at a higher level.
+ */
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ ifr = (struct ifreq *)data;
+ if (ifr == 0) {
+ error = EAFNOSUPPORT; /* XXX */
+ break;
+ }
+ switch (ifr->ifr_addr.sa_family) {
+
+#ifdef INET
+ case AF_INET:
+ break;
+#endif
+
+ default:
+ error = EAFNOSUPPORT;
+ break;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
diff --git a/sys/net/if_sl.c b/sys/net/if_sl.c
new file mode 100644
index 0000000..46ae9ad
--- /dev/null
+++ b/sys/net/if_sl.c
@@ -0,0 +1,843 @@
+/*
+ * Copyright (c) 1987, 1989, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_sl.c 8.6 (Berkeley) 2/1/94
+ */
+
+/*
+ * Serial Line interface
+ *
+ * Rick Adams
+ * Center for Seismic Studies
+ * 1300 N 17th Street, Suite 1450
+ * Arlington, Virginia 22209
+ * (703)276-7900
+ * rick@seismo.ARPA
+ * seismo!rick
+ *
+ * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris).
+ * N.B.: this belongs in netinet, not net, the way it stands now.
+ * Should have a link-layer type designation, but wouldn't be
+ * backwards-compatible.
+ *
+ * Converted to 4.3BSD Beta by Chris Torek.
+ * Other changes made at Berkeley, based in part on code by Kirk Smith.
+ * W. Jolitz added slip abort.
+ *
+ * Hacked almost beyond recognition by Van Jacobson (van@helios.ee.lbl.gov).
+ * Added priority queuing for "interactive" traffic; hooks for TCP
+ * header compression; ICMP filtering (at 2400 baud, some cretin
+ * pinging you can use up all your bandwidth). Made low clist behavior
+ * more robust and slightly less likely to hang serial line.
+ * Sped up a bunch of things.
+ *
+ * Note that splimp() is used throughout to block both (tty) input
+ * interrupts and network activity; thus, splimp must be >= spltty.
+ */
+
+#include "sl.h"
+#if NSL > 0
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/buf.h>
+#include <sys/dkstat.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/tty.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+
+#include <machine/cpu.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/netisr.h>
+#include <net/route.h>
+
+#if INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#else
+Huh? Slip without inet?
+#endif
+
+#include <net/slcompress.h>
+#include <net/if_slvar.h>
+#include <net/slip.h>
+
+#if NBPFILTER > 0
+#include <sys/time.h>
+#include <net/bpf.h>
+#endif
+
+/*
+ * SLMAX is a hard limit on input packet size. To simplify the code
+ * and improve performance, we require that packets fit in an mbuf
+ * cluster, and if we get a compressed packet, there's enough extra
+ * room to expand the header into a max length tcp/ip header (128
+ * bytes). So, SLMAX can be at most
+ * MCLBYTES - 128
+ *
+ * SLMTU is a hard limit on output packet size. To insure good
+ * interactive response, SLMTU wants to be the smallest size that
+ * amortizes the header cost. (Remember that even with
+ * type-of-service queuing, we have to wait for any in-progress
+ * packet to finish. I.e., we wait, on the average, 1/2 * mtu /
+ * cps, where cps is the line speed in characters per second.
+ * E.g., 533ms wait for a 1024 byte MTU on a 9600 baud line. The
+ * average compressed header size is 6-8 bytes so any MTU > 90
+ * bytes will give us 90% of the line bandwidth. A 100ms wait is
+ * tolerable (500ms is not), so want an MTU around 296. (Since TCP
+ * will send 256 byte segments (to allow for 40 byte headers), the
+ * typical packet size on the wire will be around 260 bytes). In
+ * 4.3tahoe+ systems, we can set an MTU in a route so we do that &
+ * leave the interface MTU relatively high (so we don't IP fragment
+ * when acting as a gateway to someone using a stupid MTU).
+ *
+ * Similar considerations apply to SLIP_HIWAT: It's the amount of
+ * data that will be queued 'downstream' of us (i.e., in clists
+ * waiting to be picked up by the tty output interrupt). If we
+ * queue a lot of data downstream, it's immune to our t.o.s. queuing.
+ * E.g., if SLIP_HIWAT is 1024, the interactive traffic in mixed
+ * telnet/ftp will see a 1 sec wait, independent of the mtu (the
+ * wait is dependent on the ftp window size but that's typically
+ * 1k - 4k). So, we want SLIP_HIWAT just big enough to amortize
+ * the cost (in idle time on the wire) of the tty driver running
+ * off the end of its clists & having to call back slstart for a
+ * new packet. For a tty interface with any buffering at all, this
+ * cost will be zero. Even with a totally brain dead interface (like
+ * the one on a typical workstation), the cost will be <= 1 character
+ * time. So, setting SLIP_HIWAT to ~100 guarantees that we'll lose
+ * at most 1% while maintaining good interactive response.
+ */
+#if NBPFILTER > 0
+#define BUFOFFSET (128+sizeof(struct ifnet **)+SLIP_HDRLEN)
+#else
+#define BUFOFFSET (128+sizeof(struct ifnet **))
+#endif
+#define SLMAX (MCLBYTES - BUFOFFSET)
+#define SLBUFSIZE (SLMAX + BUFOFFSET)
+#define SLMTU 296
+#define SLIP_HIWAT roundup(50,CBSIZE)
+#define CLISTRESERVE 1024 /* Can't let clists get too low */
+
+/*
+ * SLIP ABORT ESCAPE MECHANISM:
+ * (inspired by HAYES modem escape arrangement)
+ * 1sec escape 1sec escape 1sec escape { 1sec escape 1sec escape }
+ * within window time signals a "soft" exit from slip mode by remote end
+ * if the IFF_DEBUG flag is on.
+ */
+#define ABT_ESC '\033' /* can't be t_intr - distant host must know it*/
+#define ABT_IDLE 1 /* in seconds - idle before an escape */
+#define ABT_COUNT 3 /* count of escapes for abort */
+#define ABT_WINDOW (ABT_COUNT*2+2) /* in seconds - time to count */
+
+struct sl_softc sl_softc[NSL];
+
+#define FRAME_END 0xc0 /* Frame End */
+#define FRAME_ESCAPE 0xdb /* Frame Esc */
+#define TRANS_FRAME_END 0xdc /* transposed frame end */
+#define TRANS_FRAME_ESCAPE 0xdd /* transposed frame esc */
+
+extern struct timeval time;
+
+static int slinit __P((struct sl_softc *));
+static struct mbuf *sl_btom __P((struct sl_softc *, int));
+
+/*
+ * Called from boot code to establish sl interfaces.
+ */
+void
+slattach()
+{
+ register struct sl_softc *sc;
+ register int i = 0;
+
+ for (sc = sl_softc; i < NSL; sc++) {
+ sc->sc_if.if_name = "sl";
+ sc->sc_if.if_next = NULL;
+ sc->sc_if.if_unit = i++;
+ sc->sc_if.if_mtu = SLMTU;
+ sc->sc_if.if_flags =
+ IFF_POINTOPOINT | SC_AUTOCOMP | IFF_MULTICAST;
+ sc->sc_if.if_type = IFT_SLIP;
+ sc->sc_if.if_ioctl = slioctl;
+ sc->sc_if.if_output = sloutput;
+ sc->sc_if.if_snd.ifq_maxlen = 50;
+ sc->sc_fastq.ifq_maxlen = 32;
+ if_attach(&sc->sc_if);
+#if NBPFILTER > 0
+ bpfattach(&sc->sc_bpf, &sc->sc_if, DLT_SLIP, SLIP_HDRLEN);
+#endif
+ }
+}
+
+TEXT_SET(pseudo_set, slattach);
+
+static int
+slinit(sc)
+ register struct sl_softc *sc;
+{
+ register caddr_t p;
+
+ if (sc->sc_ep == (u_char *) 0) {
+ MCLALLOC(p, M_WAIT);
+ if (p)
+ sc->sc_ep = (u_char *)p + SLBUFSIZE;
+ else {
+ printf("sl%d: can't allocate buffer\n", sc - sl_softc);
+ sc->sc_if.if_flags &= ~IFF_UP;
+ return (0);
+ }
+ }
+ sc->sc_buf = sc->sc_ep - SLMAX;
+ sc->sc_mp = sc->sc_buf;
+ sl_compress_init(&sc->sc_comp);
+ return (1);
+}
+
+/*
+ * Line specific open routine.
+ * Attach the given tty to the first available sl unit.
+ */
+/* ARGSUSED */
+int
+slopen(dev, tp)
+ dev_t dev;
+ register struct tty *tp;
+{
+ struct proc *p = curproc; /* XXX */
+ register struct sl_softc *sc;
+ register int nsl;
+ int error;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+
+ if (tp->t_line == SLIPDISC)
+ return (0);
+
+ for (nsl = NSL, sc = sl_softc; --nsl >= 0; sc++)
+ if (sc->sc_ttyp == NULL) {
+ if (slinit(sc) == 0)
+ return (ENOBUFS);
+ tp->t_sc = (caddr_t)sc;
+ sc->sc_ttyp = tp;
+ sc->sc_if.if_baudrate = tp->t_ospeed;
+ ttyflush(tp, FREAD | FWRITE);
+ return (0);
+ }
+ return (ENXIO);
+}
+
+/*
+ * Line specific close routine.
+ * Detach the tty from the sl unit.
+ */
+void
+slclose(tp)
+ struct tty *tp;
+{
+ register struct sl_softc *sc;
+ int s;
+
+ ttywflush(tp);
+ s = splimp(); /* actually, max(spltty, splnet) */
+ tp->t_line = 0;
+ sc = (struct sl_softc *)tp->t_sc;
+ if (sc != NULL) {
+ if_down(&sc->sc_if);
+ sc->sc_ttyp = NULL;
+ tp->t_sc = NULL;
+ MCLFREE((caddr_t)(sc->sc_ep - SLBUFSIZE));
+ sc->sc_ep = 0;
+ sc->sc_mp = 0;
+ sc->sc_buf = 0;
+ }
+ splx(s);
+}
+
+/*
+ * Line specific (tty) ioctl routine.
+ * Provide a way to get the sl unit number.
+ */
+/* ARGSUSED */
+int
+sltioctl(tp, cmd, data, flag)
+ struct tty *tp;
+ int cmd;
+ caddr_t data;
+ int flag;
+{
+ struct sl_softc *sc = (struct sl_softc *)tp->t_sc;
+
+ switch (cmd) {
+ case SLIOCGUNIT:
+ *(int *)data = sc->sc_if.if_unit;
+ break;
+
+ default:
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Queue a packet. Start transmission if not active.
+ * Compression happens in slstart; if we do it here, IP TOS
+ * will cause us to not compress "background" packets, because
+ * ordering gets trashed. It can be done for all packets in slstart.
+ */
+int
+sloutput(ifp, m, dst, rtp)
+ struct ifnet *ifp;
+ register struct mbuf *m;
+ struct sockaddr *dst;
+ struct rtentry *rtp;
+{
+ register struct sl_softc *sc = &sl_softc[ifp->if_unit];
+ register struct ip *ip;
+ register struct ifqueue *ifq;
+ int s;
+
+ /*
+ * `Cannot happen' (see slioctl). Someday we will extend
+ * the line protocol to support other address families.
+ */
+ if (dst->sa_family != AF_INET) {
+ printf("sl%d: af%d not supported\n", sc->sc_if.if_unit,
+ dst->sa_family);
+ m_freem(m);
+ sc->sc_if.if_noproto++;
+ return (EAFNOSUPPORT);
+ }
+
+ if (sc->sc_ttyp == NULL) {
+ m_freem(m);
+ return (ENETDOWN); /* sort of */
+ }
+ if ((sc->sc_ttyp->t_state & TS_CARR_ON) == 0 &&
+ (sc->sc_ttyp->t_cflag & CLOCAL) == 0) {
+ m_freem(m);
+ return (EHOSTUNREACH);
+ }
+ ifq = &sc->sc_if.if_snd;
+ ip = mtod(m, struct ip *);
+ if (sc->sc_if.if_flags & SC_NOICMP && ip->ip_p == IPPROTO_ICMP) {
+ m_freem(m);
+ return (ENETRESET); /* XXX ? */
+ }
+ if (ip->ip_tos & IPTOS_LOWDELAY)
+ ifq = &sc->sc_fastq;
+ s = splimp();
+ if (IF_QFULL(ifq)) {
+ IF_DROP(ifq);
+ m_freem(m);
+ splx(s);
+ sc->sc_if.if_oerrors++;
+ return (ENOBUFS);
+ }
+ IF_ENQUEUE(ifq, m);
+ sc->sc_if.if_lastchange = time;
+ if (sc->sc_ttyp->t_outq.c_cc == 0)
+ slstart(sc->sc_ttyp);
+ splx(s);
+ return (0);
+}
+
+/*
+ * Start output on interface. Get another datagram
+ * to send from the interface queue and map it to
+ * the interface before starting output.
+ */
+void
+slstart(tp)
+ register struct tty *tp;
+{
+ register struct sl_softc *sc = (struct sl_softc *)tp->t_sc;
+ register struct mbuf *m;
+ register u_char *cp;
+ register struct ip *ip;
+ int s;
+ struct mbuf *m2;
+#if NBPFILTER > 0
+ u_char bpfbuf[SLMTU + SLIP_HDRLEN];
+ register int len = 0;
+#endif
+ extern int cfreecount;
+
+ for (;;) {
+ /*
+ * If there is more in the output queue, just send it now.
+ * We are being called in lieu of ttstart and must do what
+ * it would.
+ */
+ if (tp->t_outq.c_cc != 0) {
+ (*tp->t_oproc)(tp);
+ if (tp->t_outq.c_cc > SLIP_HIWAT)
+ return;
+ }
+ /*
+ * This happens briefly when the line shuts down.
+ */
+ if (sc == NULL)
+ return;
+
+ /*
+ * Get a packet and send it to the interface.
+ */
+ s = splimp();
+ IF_DEQUEUE(&sc->sc_fastq, m);
+ if (m)
+ sc->sc_if.if_omcasts++; /* XXX */
+ else
+ IF_DEQUEUE(&sc->sc_if.if_snd, m);
+ splx(s);
+ if (m == NULL)
+ return;
+
+ /*
+ * We do the header compression here rather than in sloutput
+ * because the packets will be out of order if we are using TOS
+ * queueing, and the connection id compression will get
+ * munged when this happens.
+ */
+#if NBPFILTER > 0
+ if (sc->sc_bpf) {
+ /*
+ * We need to save the TCP/IP header before it's
+ * compressed. To avoid complicated code, we just
+ * copy the entire packet into a stack buffer (since
+ * this is a serial line, packets should be short
+ * and/or the copy should be negligible cost compared
+ * to the packet transmission time).
+ */
+ register struct mbuf *m1 = m;
+ register u_char *cp = bpfbuf + SLIP_HDRLEN;
+
+ len = 0;
+ do {
+ register int mlen = m1->m_len;
+
+ bcopy(mtod(m1, caddr_t), cp, mlen);
+ cp += mlen;
+ len += mlen;
+ } while (m1 = m1->m_next);
+ }
+#endif
+ if ((ip = mtod(m, struct ip *))->ip_p == IPPROTO_TCP) {
+ if (sc->sc_if.if_flags & SC_COMPRESS)
+ *mtod(m, u_char *) |= sl_compress_tcp(m, ip,
+ &sc->sc_comp, 1);
+ }
+#if NBPFILTER > 0
+ if (sc->sc_bpf) {
+ /*
+ * Put the SLIP pseudo-"link header" in place. The
+ * compressed header is now at the beginning of the
+ * mbuf.
+ */
+ bpfbuf[SLX_DIR] = SLIPDIR_OUT;
+ bcopy(mtod(m, caddr_t), &bpfbuf[SLX_CHDR], CHDR_LEN);
+ bpf_tap(sc->sc_bpf, bpfbuf, len + SLIP_HDRLEN);
+ }
+#endif
+ sc->sc_if.if_lastchange = time;
+
+#if 0
+ /*
+ * If system is getting low on clists, just flush our
+ * output queue (if the stuff was important, it'll get
+ * retransmitted).
+ */
+ if (cfreecount < CLISTRESERVE + SLMTU) {
+ m_freem(m);
+ sc->sc_if.if_collisions++;
+ continue;
+ }
+#endif
+ /*
+ * The extra FRAME_END will start up a new packet, and thus
+ * will flush any accumulated garbage. We do this whenever
+ * the line may have been idle for some time.
+ */
+ if (tp->t_outq.c_cc == 0) {
+ ++sc->sc_if.if_obytes;
+ (void) putc(FRAME_END, &tp->t_outq);
+ }
+
+ while (m) {
+ register u_char *ep;
+
+ cp = mtod(m, u_char *); ep = cp + m->m_len;
+ while (cp < ep) {
+ /*
+ * Find out how many bytes in the string we can
+ * handle without doing something special.
+ */
+ register u_char *bp = cp;
+
+ while (cp < ep) {
+ switch (*cp++) {
+ case FRAME_ESCAPE:
+ case FRAME_END:
+ --cp;
+ goto out;
+ }
+ }
+ out:
+ if (cp > bp) {
+ /*
+ * Put n characters at once
+ * into the tty output queue.
+ */
+ if (b_to_q((char *)bp, cp - bp,
+ &tp->t_outq))
+ break;
+ sc->sc_if.if_obytes += cp - bp;
+ }
+ /*
+ * If there are characters left in the mbuf,
+ * the first one must be special..
+ * Put it out in a different form.
+ */
+ if (cp < ep) {
+ if (putc(FRAME_ESCAPE, &tp->t_outq))
+ break;
+ if (putc(*cp++ == FRAME_ESCAPE ?
+ TRANS_FRAME_ESCAPE : TRANS_FRAME_END,
+ &tp->t_outq)) {
+ (void) unputc(&tp->t_outq);
+ break;
+ }
+ sc->sc_if.if_obytes += 2;
+ }
+ }
+ MFREE(m, m2);
+ m = m2;
+ }
+
+ if (putc(FRAME_END, &tp->t_outq)) {
+ /*
+ * Not enough room. Remove a char to make room
+ * and end the packet normally.
+ * If you get many collisions (more than one or two
+ * a day) you probably do not have enough clists
+ * and you should increase "nclist" in param.c.
+ */
+ (void) unputc(&tp->t_outq);
+ (void) putc(FRAME_END, &tp->t_outq);
+ sc->sc_if.if_collisions++;
+ } else {
+ ++sc->sc_if.if_obytes;
+ sc->sc_if.if_opackets++;
+ }
+ }
+}
+
+/*
+ * Copy data buffer to mbuf chain; add ifnet pointer.
+ */
+static struct mbuf *
+sl_btom(sc, len)
+ register struct sl_softc *sc;
+ register int len;
+{
+ register struct mbuf *m;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return (NULL);
+
+ /*
+ * If we have more than MHLEN bytes, it's cheaper to
+ * queue the cluster we just filled & allocate a new one
+ * for the input buffer. Otherwise, fill the mbuf we
+ * allocated above. Note that code in the input routine
+ * guarantees that packet will fit in a cluster.
+ */
+ if (len >= MHLEN) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ /*
+ * we couldn't get a cluster - if memory's this
+ * low, it's time to start dropping packets.
+ */
+ (void) m_free(m);
+ return (NULL);
+ }
+ sc->sc_ep = mtod(m, u_char *) + SLBUFSIZE;
+ m->m_data = (caddr_t)sc->sc_buf;
+ m->m_ext.ext_buf = (caddr_t)((int)sc->sc_buf &~ MCLOFSET);
+ } else
+ bcopy((caddr_t)sc->sc_buf, mtod(m, caddr_t), len);
+
+ m->m_len = len;
+ m->m_pkthdr.len = len;
+ m->m_pkthdr.rcvif = &sc->sc_if;
+ return (m);
+}
+
+/*
+ * tty interface receiver interrupt.
+ */
+void
+slinput(c, tp)
+ register int c;
+ register struct tty *tp;
+{
+ register struct sl_softc *sc;
+ register struct mbuf *m;
+ register int len;
+ int s;
+#if NBPFILTER > 0
+ u_char chdr[CHDR_LEN];
+#endif
+
+ tk_nin++;
+ sc = (struct sl_softc *)tp->t_sc;
+ if (sc == NULL)
+ return;
+ if (c & TTY_ERRORMASK || ((tp->t_state & TS_CARR_ON) == 0 &&
+ (tp->t_cflag & CLOCAL) == 0)) {
+ sc->sc_flags |= SC_ERROR;
+ return;
+ }
+ c &= TTY_CHARMASK;
+
+ ++sc->sc_if.if_ibytes;
+
+ if (sc->sc_if.if_flags & IFF_DEBUG) {
+ if (c == ABT_ESC) {
+ /*
+ * If we have a previous abort, see whether
+ * this one is within the time limit.
+ */
+ if (sc->sc_abortcount &&
+ time.tv_sec >= sc->sc_starttime + ABT_WINDOW)
+ sc->sc_abortcount = 0;
+ /*
+ * If we see an abort after "idle" time, count it;
+ * record when the first abort escape arrived.
+ */
+ if (time.tv_sec >= sc->sc_lasttime + ABT_IDLE) {
+ if (++sc->sc_abortcount == 1)
+ sc->sc_starttime = time.tv_sec;
+ if (sc->sc_abortcount >= ABT_COUNT) {
+ slclose(tp);
+ return;
+ }
+ }
+ } else
+ sc->sc_abortcount = 0;
+ sc->sc_lasttime = time.tv_sec;
+ }
+
+ switch (c) {
+
+ case TRANS_FRAME_ESCAPE:
+ if (sc->sc_escape)
+ c = FRAME_ESCAPE;
+ break;
+
+ case TRANS_FRAME_END:
+ if (sc->sc_escape)
+ c = FRAME_END;
+ break;
+
+ case FRAME_ESCAPE:
+ sc->sc_escape = 1;
+ return;
+
+ case FRAME_END:
+ if(sc->sc_flags & SC_ERROR) {
+ sc->sc_flags &= ~SC_ERROR;
+ goto newpack;
+ }
+ len = sc->sc_mp - sc->sc_buf;
+ if (len < 3)
+ /* less than min length packet - ignore */
+ goto newpack;
+
+#if NBPFILTER > 0
+ if (sc->sc_bpf) {
+ /*
+ * Save the compressed header, so we
+ * can tack it on later. Note that we
+ * will end up copying garbage in some
+ * cases but this is okay. We remember
+ * where the buffer started so we can
+ * compute the new header length.
+ */
+ bcopy(sc->sc_buf, chdr, CHDR_LEN);
+ }
+#endif
+
+ if ((c = (*sc->sc_buf & 0xf0)) != (IPVERSION << 4)) {
+ if (c & 0x80)
+ c = TYPE_COMPRESSED_TCP;
+ else if (c == TYPE_UNCOMPRESSED_TCP)
+ *sc->sc_buf &= 0x4f; /* XXX */
+ /*
+ * We've got something that's not an IP packet.
+ * If compression is enabled, try to decompress it.
+ * Otherwise, if `auto-enable' compression is on and
+ * it's a reasonable packet, decompress it and then
+ * enable compression. Otherwise, drop it.
+ */
+ if (sc->sc_if.if_flags & SC_COMPRESS) {
+ len = sl_uncompress_tcp(&sc->sc_buf, len,
+ (u_int)c, &sc->sc_comp);
+ if (len <= 0)
+ goto error;
+ } else if ((sc->sc_if.if_flags & SC_AUTOCOMP) &&
+ c == TYPE_UNCOMPRESSED_TCP && len >= 40) {
+ len = sl_uncompress_tcp(&sc->sc_buf, len,
+ (u_int)c, &sc->sc_comp);
+ if (len <= 0)
+ goto error;
+ sc->sc_if.if_flags |= SC_COMPRESS;
+ } else
+ goto error;
+ }
+#if NBPFILTER > 0
+ if (sc->sc_bpf) {
+ /*
+ * Put the SLIP pseudo-"link header" in place.
+ * We couldn't do this any earlier since
+ * decompression probably moved the buffer
+ * pointer. Then, invoke BPF.
+ */
+ register u_char *hp = sc->sc_buf - SLIP_HDRLEN;
+
+ hp[SLX_DIR] = SLIPDIR_IN;
+ bcopy(chdr, &hp[SLX_CHDR], CHDR_LEN);
+ bpf_tap(sc->sc_bpf, hp, len + SLIP_HDRLEN);
+ }
+#endif
+ m = sl_btom(sc, len);
+ if (m == NULL)
+ goto error;
+
+ sc->sc_if.if_ipackets++;
+ sc->sc_if.if_lastchange = time;
+ s = splimp();
+ if (IF_QFULL(&ipintrq)) {
+ IF_DROP(&ipintrq);
+ sc->sc_if.if_ierrors++;
+ sc->sc_if.if_iqdrops++;
+ m_freem(m);
+ } else {
+ IF_ENQUEUE(&ipintrq, m);
+ schednetisr(NETISR_IP);
+ }
+ splx(s);
+ goto newpack;
+ }
+ if (sc->sc_mp < sc->sc_ep) {
+ *sc->sc_mp++ = c;
+ sc->sc_escape = 0;
+ return;
+ }
+
+ /* can't put lower; would miss an extra frame */
+ sc->sc_flags |= SC_ERROR;
+
+error:
+ sc->sc_if.if_ierrors++;
+newpack:
+ sc->sc_mp = sc->sc_buf = sc->sc_ep - SLMAX;
+ sc->sc_escape = 0;
+}
+
+/*
+ * Process an ioctl request.
+ */
+int
+slioctl(ifp, cmd, data)
+ register struct ifnet *ifp;
+ int cmd;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *)data;
+ register struct ifreq *ifr;
+ register int s = splimp(), error = 0;
+
+ switch (cmd) {
+
+ case SIOCSIFADDR:
+ if (ifa->ifa_addr->sa_family == AF_INET)
+ ifp->if_flags |= IFF_UP;
+ else
+ error = EAFNOSUPPORT;
+ break;
+
+ case SIOCSIFDSTADDR:
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ error = EAFNOSUPPORT;
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ ifr = (struct ifreq *)data;
+ if (ifr == 0) {
+ error = EAFNOSUPPORT; /* XXX */
+ break;
+ }
+ switch (ifr->ifr_addr.sa_family) {
+
+#ifdef INET
+ case AF_INET:
+ break;
+#endif
+
+ default:
+ error = EAFNOSUPPORT;
+ break;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ splx(s);
+ return (error);
+}
+#endif
diff --git a/sys/net/if_slvar.h b/sys/net/if_slvar.h
new file mode 100644
index 0000000..e7b2764
--- /dev/null
+++ b/sys/net/if_slvar.h
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_slvar.h 8.3 (Berkeley) 2/1/94
+ *
+ * $Header: if_slvar.h,v 1.3 89/05/31 02:25:18 van Exp $
+ */
+
+/*
+ * Definitions for SLIP interface data structures
+ *
+ * (This exists so programs like slstats can get at the definition
+ * of sl_softc.)
+ */
+struct sl_softc {
+ struct ifnet sc_if; /* network-visible interface */
+ struct ifqueue sc_fastq; /* interactive output queue */
+ struct tty *sc_ttyp; /* pointer to tty structure */
+ u_char *sc_mp; /* pointer to next available buf char */
+ u_char *sc_ep; /* pointer to last available buf char */
+ u_char *sc_buf; /* input buffer */
+ u_int sc_flags; /* see below */
+ u_int sc_escape; /* =1 if last char input was FRAME_ESCAPE */
+ long sc_lasttime; /* last time a char arrived */
+ long sc_abortcount; /* number of abort esacpe chars */
+ long sc_starttime; /* time of first abort in window */
+#ifdef INET /* XXX */
+ struct slcompress sc_comp; /* tcp compression data */
+#endif
+ caddr_t sc_bpf; /* BPF data */
+};
+
+/* internal flags */
+#define SC_ERROR 0x0001 /* had an input error */
+
+/* visible flags */
+#define SC_COMPRESS IFF_LINK0 /* compress TCP traffic */
+#define SC_NOICMP IFF_LINK1 /* supress ICMP traffic */
+#define SC_AUTOCOMP IFF_LINK2 /* auto-enable TCP compression */
+
+#ifdef KERNEL
+void slattach __P((void));
+void slclose __P((struct tty *));
+void slinput __P((int, struct tty *));
+int slioctl __P((struct ifnet *, int, caddr_t));
+int slopen __P((dev_t, struct tty *));
+int sloutput __P((struct ifnet *,
+ struct mbuf *, struct sockaddr *, struct rtentry *));
+void slstart __P((struct tty *));
+int sltioctl __P((struct tty *, int, caddr_t, int));
+#endif /* KERNEL */
diff --git a/sys/net/if_types.h b/sys/net/if_types.h
new file mode 100644
index 0000000..030f234
--- /dev/null
+++ b/sys/net/if_types.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_types.h 8.2 (Berkeley) 4/20/94
+ */
+
+/*
+ * Interface types for benefit of parsing media address headers.
+ * This list is derived from the SNMP list of ifTypes, currently
+ * documented in RFC1573.
+ */
+
+#define IFT_OTHER 0x1 /* none of the following */
+#define IFT_1822 0x2 /* old-style arpanet imp */
+#define IFT_HDH1822 0x3 /* HDH arpanet imp */
+#define IFT_X25DDN 0x4 /* x25 to imp */
+#define IFT_X25 0x5 /* PDN X25 interface (RFC877) */
+#define IFT_ETHER 0x6 /* Ethernet CSMACD */
+#define IFT_ISO88023 0x7 /* CMSA CD */
+#define IFT_ISO88024 0x8 /* Token Bus */
+#define IFT_ISO88025 0x9 /* Token Ring */
+#define IFT_ISO88026 0xa /* MAN */
+#define IFT_STARLAN 0xb
+#define IFT_P10 0xc /* Proteon 10MBit ring */
+#define IFT_P80 0xd /* Proteon 10MBit ring */
+#define IFT_HY 0xe /* Hyperchannel */
+#define IFT_FDDI 0xf
+#define IFT_LAPB 0x10
+#define IFT_SDLC 0x11
+#define IFT_T1 0x12
+#define IFT_CEPT 0x13 /* E1 - european T1 */
+#define IFT_ISDNBASIC 0x14
+#define IFT_ISDNPRIMARY 0x15
+#define IFT_PTPSERIAL 0x16 /* Proprietary PTP serial */
+#define IFT_PPP 0x17 /* RFC 1331 */
+#define IFT_LOOP 0x18 /* loopback */
+#define IFT_EON 0x19 /* ISO over IP */
+#define IFT_XETHER 0x1a /* obsolete 3MB experimental ethernet */
+#define IFT_NSIP 0x1b /* XNS over IP */
+#define IFT_SLIP 0x1c /* IP over generic TTY */
+#define IFT_ULTRA 0x1d /* Ultra Technologies */
+#define IFT_DS3 0x1e /* Generic T3 */
+#define IFT_SIP 0x1f /* SMDS */
+#define IFT_FRELAY 0x20 /* Frame Relay DTE only */
+#define IFT_RS232 0x21
+#define IFT_PARA 0x22 /* parallel-port */
+#define IFT_ARCNET 0x23
+#define IFT_ARCNETPLUS 0x24
+#define IFT_ATM 0x25 /* ATM cells */
+#define IFT_MIOX25 0x26
+#define IFT_SONET 0x27 /* SONET or SDH */
+#define IFT_X25PLE 0x28
+#define IFT_ISO88022LLC 0x29
+#define IFT_LOCALTALK 0x2a
+#define IFT_SMDSDXI 0x2b
+#define IFT_FRELAYDCE 0x2c /* Frame Relay DCE */
+#define IFT_V35 0x2d
+#define IFT_HSSI 0x2e
+#define IFT_HIPPI 0x2f
+#define IFT_MODEM 0x30 /* Generic Modem */
+#define IFT_AAL5 0x31 /* AAL5 over ATM */
+#define IFT_SONETPATH 0x32
+#define IFT_SONETVT 0x33
+#define IFT_SMDSICIP 0x34 /* SMDS InterCarrier Interface */
+#define IFT_PROPVIRTUAL 0x35 /* Proprietary Virtual/internal */
+#define IFT_PROPMUX 0x36 /* Proprietary Multiplexing */
diff --git a/sys/net/netisr.h b/sys/net/netisr.h
new file mode 100644
index 0000000..03c5288
--- /dev/null
+++ b/sys/net/netisr.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 1980, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)netisr.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * The networking code runs off software interrupts.
+ *
+ * You can switch into the network by doing splnet() and return by splx().
+ * The software interrupt level for the network is higher than the software
+ * level for the clock (so you can enter the network in routines called
+ * at timeout time).
+ */
+#if defined(vax) || defined(tahoe)
+#define setsoftnet() mtpr(SIRR, 12)
+#endif
+
+/*
+ * Each ``pup-level-1'' input queue has a bit in a ``netisr'' status
+ * word which is used to de-multiplex a single software
+ * interrupt used for scheduling the network code to calls
+ * on the lowest level routine of each protocol.
+ */
+#define NETISR_RAW 0 /* same as AF_UNSPEC */
+#define NETISR_IP 2 /* same as AF_INET */
+#define NETISR_IMP 3 /* same as AF_IMPLINK */
+#define NETISR_NS 6 /* same as AF_NS */
+#define NETISR_ISO 7 /* same as AF_ISO */
+#define NETISR_CCITT 10 /* same as AF_CCITT */
+#define NETISR_ARP 18 /* same as AF_LINK */
+
+#define schednetisr(anisr) { netisr |= 1<<(anisr); setsoftnet(); }
+
+#ifndef LOCORE
+#ifdef KERNEL
+volatile unsigned int netisr; /* scheduling bits for network */
+#endif
+#endif
diff --git a/sys/net/radix.c b/sys/net/radix.c
new file mode 100644
index 0000000..fb34adb
--- /dev/null
+++ b/sys/net/radix.c
@@ -0,0 +1,757 @@
+/*
+ * Copyright (c) 1988, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)radix.c 8.2 (Berkeley) 1/4/94
+ */
+
+/*
+ * Routines to build and maintain radix trees for routing lookups.
+ */
+#ifndef RNF_NORMAL
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#define M_DONTWAIT M_NOWAIT
+#ifdef KERNEL
+#include <sys/domain.h>
+#endif
+#endif
+
+#include <net/radix.h>
+
+int max_keylen;
+struct radix_mask *rn_mkfreelist;
+struct radix_node_head *mask_rnhead;
+static int gotOddMasks;
+static char *maskedKey;
+static char *rn_zeros, *rn_ones;
+
+#define rn_masktop (mask_rnhead->rnh_treetop)
+#undef Bcmp
+#define Bcmp(a, b, l) (l == 0 ? 0 : bcmp((caddr_t)(a), (caddr_t)(b), (u_long)l))
+/*
+ * The data structure for the keys is a radix tree with one way
+ * branching removed. The index rn_b at an internal node n represents a bit
+ * position to be tested. The tree is arranged so that all descendants
+ * of a node n have keys whose bits all agree up to position rn_b - 1.
+ * (We say the index of n is rn_b.)
+ *
+ * There is at least one descendant which has a one bit at position rn_b,
+ * and at least one with a zero there.
+ *
+ * A route is determined by a pair of key and mask. We require that the
+ * bit-wise logical and of the key and mask to be the key.
+ * We define the index of a route to associated with the mask to be
+ * the first bit number in the mask where 0 occurs (with bit number 0
+ * representing the highest order bit).
+ *
+ * We say a mask is normal if every bit is 0, past the index of the mask.
+ * If a node n has a descendant (k, m) with index(m) == index(n) == rn_b,
+ * and m is a normal mask, then the route applies to every descendant of n.
+ * If the index(m) < rn_b, this implies the trailing last few bits of k
+ * before bit b are all 0, (and hence consequently true of every descendant
+ * of n), so the route applies to all descendants of the node as well.
+ *
+ * The present version of the code makes no use of normal routes,
+ * but similar logic shows that a non-normal mask m such that
+ * index(m) <= index(n) could potentially apply to many children of n.
+ * Thus, for each non-host route, we attach its mask to a list at an internal
+ * node as high in the tree as we can go.
+ */
+
+struct radix_node *
+rn_search(v_arg, head)
+ void *v_arg;
+ struct radix_node *head;
+{
+ register struct radix_node *x;
+ register caddr_t v;
+
+ for (x = head, v = v_arg; x->rn_b >= 0;) {
+ if (x->rn_bmask & v[x->rn_off])
+ x = x->rn_r;
+ else
+ x = x->rn_l;
+ }
+ return (x);
+};
+
+struct radix_node *
+rn_search_m(v_arg, head, m_arg)
+ struct radix_node *head;
+ void *v_arg, *m_arg;
+{
+ register struct radix_node *x;
+ register caddr_t v = v_arg, m = m_arg;
+
+ for (x = head; x->rn_b >= 0;) {
+ if ((x->rn_bmask & m[x->rn_off]) &&
+ (x->rn_bmask & v[x->rn_off]))
+ x = x->rn_r;
+ else
+ x = x->rn_l;
+ }
+ return x;
+};
+
+int
+rn_refines(m_arg, n_arg)
+ void *m_arg, *n_arg;
+{
+ register caddr_t m = m_arg, n = n_arg;
+ register caddr_t lim, lim2 = lim = n + *(u_char *)n;
+ int longer = (*(u_char *)n++) - (int)(*(u_char *)m++);
+ int masks_are_equal = 1;
+
+ if (longer > 0)
+ lim -= longer;
+ while (n < lim) {
+ if (*n & ~(*m))
+ return 0;
+ if (*n++ != *m++)
+ masks_are_equal = 0;
+
+ }
+ while (n < lim2)
+ if (*n++)
+ return 0;
+ if (masks_are_equal && (longer < 0))
+ for (lim2 = m - longer; m < lim2; )
+ if (*m++)
+ return 1;
+ return (!masks_are_equal);
+}
+
+
+struct radix_node *
+rn_match(v_arg, head)
+ void *v_arg;
+ struct radix_node_head *head;
+{
+ caddr_t v = v_arg;
+ register struct radix_node *t = head->rnh_treetop, *x;
+ register caddr_t cp = v, cp2, cp3;
+ caddr_t cplim, mstart;
+ struct radix_node *saved_t, *top = t;
+ int off = t->rn_off, vlen = *(u_char *)cp, matched_off;
+
+ /*
+ * Open code rn_search(v, top) to avoid overhead of extra
+ * subroutine call.
+ */
+ for (; t->rn_b >= 0; ) {
+ if (t->rn_bmask & cp[t->rn_off])
+ t = t->rn_r;
+ else
+ t = t->rn_l;
+ }
+ /*
+ * See if we match exactly as a host destination
+ */
+ cp += off; cp2 = t->rn_key + off; cplim = v + vlen;
+ for (; cp < cplim; cp++, cp2++)
+ if (*cp != *cp2)
+ goto on1;
+ /*
+ * This extra grot is in case we are explicitly asked
+ * to look up the default. Ugh!
+ */
+ if ((t->rn_flags & RNF_ROOT) && t->rn_dupedkey)
+ t = t->rn_dupedkey;
+ return t;
+on1:
+ matched_off = cp - v;
+ saved_t = t;
+ do {
+ if (t->rn_mask) {
+ /*
+ * Even if we don't match exactly as a hosts;
+ * we may match if the leaf we wound up at is
+ * a route to a net.
+ */
+ cp3 = matched_off + t->rn_mask;
+ cp2 = matched_off + t->rn_key;
+ for (; cp < cplim; cp++)
+ if ((*cp2++ ^ *cp) & *cp3++)
+ break;
+ if (cp == cplim)
+ return t;
+ cp = matched_off + v;
+ }
+ } while (t = t->rn_dupedkey);
+ t = saved_t;
+ /* start searching up the tree */
+ do {
+ register struct radix_mask *m;
+ t = t->rn_p;
+ if (m = t->rn_mklist) {
+ /*
+ * After doing measurements here, it may
+ * turn out to be faster to open code
+ * rn_search_m here instead of always
+ * copying and masking.
+ */
+ off = min(t->rn_off, matched_off);
+ mstart = maskedKey + off;
+ do {
+ cp2 = mstart;
+ cp3 = m->rm_mask + off;
+ for (cp = v + off; cp < cplim;)
+ *cp2++ = *cp++ & *cp3++;
+ x = rn_search(maskedKey, t);
+ while (x && x->rn_mask != m->rm_mask)
+ x = x->rn_dupedkey;
+ if (x &&
+ (Bcmp(mstart, x->rn_key + off,
+ vlen - off) == 0))
+ return x;
+ } while (m = m->rm_mklist);
+ }
+ } while (t != top);
+ return 0;
+};
+
+#ifdef RN_DEBUG
+int rn_nodenum;
+struct radix_node *rn_clist;
+int rn_saveinfo;
+int rn_debug = 1;
+#endif
+
+struct radix_node *
+rn_newpair(v, b, nodes)
+ void *v;
+ int b;
+ struct radix_node nodes[2];
+{
+ register struct radix_node *tt = nodes, *t = tt + 1;
+ t->rn_b = b; t->rn_bmask = 0x80 >> (b & 7);
+ t->rn_l = tt; t->rn_off = b >> 3;
+ tt->rn_b = -1; tt->rn_key = (caddr_t)v; tt->rn_p = t;
+ tt->rn_flags = t->rn_flags = RNF_ACTIVE;
+#ifdef RN_DEBUG
+ tt->rn_info = rn_nodenum++; t->rn_info = rn_nodenum++;
+ tt->rn_twin = t; tt->rn_ybro = rn_clist; rn_clist = tt;
+#endif
+ return t;
+}
+
+struct radix_node *
+rn_insert(v_arg, head, dupentry, nodes)
+ void *v_arg;
+ struct radix_node_head *head;
+ int *dupentry;
+ struct radix_node nodes[2];
+{
+ caddr_t v = v_arg;
+ struct radix_node *top = head->rnh_treetop;
+ int head_off = top->rn_off, vlen = (int)*((u_char *)v);
+ register struct radix_node *t = rn_search(v_arg, top);
+ register caddr_t cp = v + head_off;
+ register int b;
+ struct radix_node *tt;
+ /*
+ *find first bit at which v and t->rn_key differ
+ */
+ {
+ register caddr_t cp2 = t->rn_key + head_off;
+ register int cmp_res;
+ caddr_t cplim = v + vlen;
+
+ while (cp < cplim)
+ if (*cp2++ != *cp++)
+ goto on1;
+ *dupentry = 1;
+ return t;
+on1:
+ *dupentry = 0;
+ cmp_res = (cp[-1] ^ cp2[-1]) & 0xff;
+ for (b = (cp - v) << 3; cmp_res; b--)
+ cmp_res >>= 1;
+ }
+ {
+ register struct radix_node *p, *x = top;
+ cp = v;
+ do {
+ p = x;
+ if (cp[x->rn_off] & x->rn_bmask)
+ x = x->rn_r;
+ else x = x->rn_l;
+ } while (b > (unsigned) x->rn_b); /* x->rn_b < b && x->rn_b >= 0 */
+#ifdef RN_DEBUG
+ if (rn_debug)
+ printf("Going In:\n"), traverse(p);
+#endif
+ t = rn_newpair(v_arg, b, nodes); tt = t->rn_l;
+ if ((cp[p->rn_off] & p->rn_bmask) == 0)
+ p->rn_l = t;
+ else
+ p->rn_r = t;
+ x->rn_p = t; t->rn_p = p; /* frees x, p as temp vars below */
+ if ((cp[t->rn_off] & t->rn_bmask) == 0) {
+ t->rn_r = x;
+ } else {
+ t->rn_r = tt; t->rn_l = x;
+ }
+#ifdef RN_DEBUG
+ if (rn_debug)
+ printf("Coming out:\n"), traverse(p);
+#endif
+ }
+ return (tt);
+}
+
+struct radix_node *
+rn_addmask(n_arg, search, skip)
+ int search, skip;
+ void *n_arg;
+{
+ caddr_t netmask = (caddr_t)n_arg;
+ register struct radix_node *x;
+ register caddr_t cp, cplim;
+ register int b, mlen, j;
+ int maskduplicated;
+
+ mlen = *(u_char *)netmask;
+ if (search) {
+ x = rn_search(netmask, rn_masktop);
+ mlen = *(u_char *)netmask;
+ if (Bcmp(netmask, x->rn_key, mlen) == 0)
+ return (x);
+ }
+ R_Malloc(x, struct radix_node *, max_keylen + 2 * sizeof (*x));
+ if (x == 0)
+ return (0);
+ Bzero(x, max_keylen + 2 * sizeof (*x));
+ cp = (caddr_t)(x + 2);
+ Bcopy(netmask, cp, mlen);
+ netmask = cp;
+ x = rn_insert(netmask, mask_rnhead, &maskduplicated, x);
+ /*
+ * Calculate index of mask.
+ */
+ cplim = netmask + mlen;
+ for (cp = netmask + skip; cp < cplim; cp++)
+ if (*(u_char *)cp != 0xff)
+ break;
+ b = (cp - netmask) << 3;
+ if (cp != cplim) {
+ if (*cp != 0) {
+ gotOddMasks = 1;
+ for (j = 0x80; j; b++, j >>= 1)
+ if ((j & *cp) == 0)
+ break;
+ }
+ }
+ x->rn_b = -1 - b;
+ return (x);
+}
+
+struct radix_node *
+rn_addroute(v_arg, n_arg, head, treenodes)
+ void *v_arg, *n_arg;
+ struct radix_node_head *head;
+ struct radix_node treenodes[2];
+{
+ caddr_t v = (caddr_t)v_arg, netmask = (caddr_t)n_arg;
+ register struct radix_node *t, *x = 0, *tt;
+ struct radix_node *saved_tt, *top = head->rnh_treetop;
+ short b = 0, b_leaf;
+ int mlen, keyduplicated;
+ caddr_t cplim;
+ struct radix_mask *m, **mp;
+
+ /*
+ * In dealing with non-contiguous masks, there may be
+ * many different routes which have the same mask.
+ * We will find it useful to have a unique pointer to
+ * the mask to speed avoiding duplicate references at
+ * nodes and possibly save time in calculating indices.
+ */
+ if (netmask) {
+ x = rn_search(netmask, rn_masktop);
+ mlen = *(u_char *)netmask;
+ if (Bcmp(netmask, x->rn_key, mlen) != 0) {
+ x = rn_addmask(netmask, 0, top->rn_off);
+ if (x == 0)
+ return (0);
+ }
+ netmask = x->rn_key;
+ b = -1 - x->rn_b;
+ }
+ /*
+ * Deal with duplicated keys: attach node to previous instance
+ */
+ saved_tt = tt = rn_insert(v, head, &keyduplicated, treenodes);
+ if (keyduplicated) {
+ do {
+ if (tt->rn_mask == netmask)
+ return (0);
+ t = tt;
+ if (netmask == 0 ||
+ (tt->rn_mask && rn_refines(netmask, tt->rn_mask)))
+ break;
+ } while (tt = tt->rn_dupedkey);
+ /*
+ * If the mask is not duplicated, we wouldn't
+ * find it among possible duplicate key entries
+ * anyway, so the above test doesn't hurt.
+ *
+ * We sort the masks for a duplicated key the same way as
+ * in a masklist -- most specific to least specific.
+ * This may require the unfortunate nuisance of relocating
+ * the head of the list.
+ */
+ if (tt && t == saved_tt) {
+ struct radix_node *xx = x;
+ /* link in at head of list */
+ (tt = treenodes)->rn_dupedkey = t;
+ tt->rn_flags = t->rn_flags;
+ tt->rn_p = x = t->rn_p;
+ if (x->rn_l == t) x->rn_l = tt; else x->rn_r = tt;
+ saved_tt = tt; x = xx;
+ } else {
+ (tt = treenodes)->rn_dupedkey = t->rn_dupedkey;
+ t->rn_dupedkey = tt;
+ }
+#ifdef RN_DEBUG
+ t=tt+1; tt->rn_info = rn_nodenum++; t->rn_info = rn_nodenum++;
+ tt->rn_twin = t; tt->rn_ybro = rn_clist; rn_clist = tt;
+#endif
+ t = saved_tt;
+ tt->rn_key = (caddr_t) v;
+ tt->rn_b = -1;
+ tt->rn_flags = t->rn_flags & ~RNF_ROOT;
+ }
+ /*
+ * Put mask in tree.
+ */
+ if (netmask) {
+ tt->rn_mask = netmask;
+ tt->rn_b = x->rn_b;
+ }
+ t = saved_tt->rn_p;
+ b_leaf = -1 - t->rn_b;
+ if (t->rn_r == saved_tt) x = t->rn_l; else x = t->rn_r;
+ /* Promote general routes from below */
+ if (x->rn_b < 0) {
+ if (x->rn_mask && (x->rn_b >= b_leaf) && x->rn_mklist == 0) {
+ MKGet(m);
+ if (m) {
+ Bzero(m, sizeof *m);
+ m->rm_b = x->rn_b;
+ m->rm_mask = x->rn_mask;
+ x->rn_mklist = t->rn_mklist = m;
+ }
+ }
+ } else if (x->rn_mklist) {
+ /*
+ * Skip over masks whose index is > that of new node
+ */
+ for (mp = &x->rn_mklist; m = *mp; mp = &m->rm_mklist)
+ if (m->rm_b >= b_leaf)
+ break;
+ t->rn_mklist = m; *mp = 0;
+ }
+ /* Add new route to highest possible ancestor's list */
+ if ((netmask == 0) || (b > t->rn_b ))
+ return tt; /* can't lift at all */
+ b_leaf = tt->rn_b;
+ do {
+ x = t;
+ t = t->rn_p;
+ } while (b <= t->rn_b && x != top);
+ /*
+ * Search through routes associated with node to
+ * insert new route according to index.
+ * For nodes of equal index, place more specific
+ * masks first.
+ */
+ cplim = netmask + mlen;
+ for (mp = &x->rn_mklist; m = *mp; mp = &m->rm_mklist) {
+ if (m->rm_b < b_leaf)
+ continue;
+ if (m->rm_b > b_leaf)
+ break;
+ if (m->rm_mask == netmask) {
+ m->rm_refs++;
+ tt->rn_mklist = m;
+ return tt;
+ }
+ if (rn_refines(netmask, m->rm_mask))
+ break;
+ }
+ MKGet(m);
+ if (m == 0) {
+ printf("Mask for route not entered\n");
+ return (tt);
+ }
+ Bzero(m, sizeof *m);
+ m->rm_b = b_leaf;
+ m->rm_mask = netmask;
+ m->rm_mklist = *mp;
+ *mp = m;
+ tt->rn_mklist = m;
+ return tt;
+}
+
+struct radix_node *
+rn_delete(v_arg, netmask_arg, head)
+ void *v_arg, *netmask_arg;
+ struct radix_node_head *head;
+{
+ register struct radix_node *t, *p, *x, *tt;
+ struct radix_mask *m, *saved_m, **mp;
+ struct radix_node *dupedkey, *saved_tt, *top;
+ caddr_t v, netmask;
+ int b, head_off, vlen;
+
+ v = v_arg;
+ netmask = netmask_arg;
+ x = head->rnh_treetop;
+ tt = rn_search(v, x);
+ head_off = x->rn_off;
+ vlen = *(u_char *)v;
+ saved_tt = tt;
+ top = x;
+ if (tt == 0 ||
+ Bcmp(v + head_off, tt->rn_key + head_off, vlen - head_off))
+ return (0);
+ /*
+ * Delete our route from mask lists.
+ */
+ if (dupedkey = tt->rn_dupedkey) {
+ if (netmask)
+ netmask = rn_search(netmask, rn_masktop)->rn_key;
+ while (tt->rn_mask != netmask)
+ if ((tt = tt->rn_dupedkey) == 0)
+ return (0);
+ }
+ if (tt->rn_mask == 0 || (saved_m = m = tt->rn_mklist) == 0)
+ goto on1;
+ if (m->rm_mask != tt->rn_mask) {
+ printf("rn_delete: inconsistent annotation\n");
+ goto on1;
+ }
+ if (--m->rm_refs >= 0)
+ goto on1;
+ b = -1 - tt->rn_b;
+ t = saved_tt->rn_p;
+ if (b > t->rn_b)
+ goto on1; /* Wasn't lifted at all */
+ do {
+ x = t;
+ t = t->rn_p;
+ } while (b <= t->rn_b && x != top);
+ for (mp = &x->rn_mklist; m = *mp; mp = &m->rm_mklist)
+ if (m == saved_m) {
+ *mp = m->rm_mklist;
+ MKFree(m);
+ break;
+ }
+ if (m == 0)
+ printf("rn_delete: couldn't find our annotation\n");
+on1:
+ /*
+ * Eliminate us from tree
+ */
+ if (tt->rn_flags & RNF_ROOT)
+ return (0);
+#ifdef RN_DEBUG
+ /* Get us out of the creation list */
+ for (t = rn_clist; t && t->rn_ybro != tt; t = t->rn_ybro) {}
+ if (t) t->rn_ybro = tt->rn_ybro;
+#endif
+ t = tt->rn_p;
+ if (dupedkey) {
+ if (tt == saved_tt) {
+ x = dupedkey; x->rn_p = t;
+ if (t->rn_l == tt) t->rn_l = x; else t->rn_r = x;
+ } else {
+ for (x = p = saved_tt; p && p->rn_dupedkey != tt;)
+ p = p->rn_dupedkey;
+ if (p) p->rn_dupedkey = tt->rn_dupedkey;
+ else printf("rn_delete: couldn't find us\n");
+ }
+ t = tt + 1;
+ if (t->rn_flags & RNF_ACTIVE) {
+#ifndef RN_DEBUG
+ *++x = *t; p = t->rn_p;
+#else
+ b = t->rn_info; *++x = *t; t->rn_info = b; p = t->rn_p;
+#endif
+ if (p->rn_l == t) p->rn_l = x; else p->rn_r = x;
+ x->rn_l->rn_p = x; x->rn_r->rn_p = x;
+ }
+ goto out;
+ }
+ if (t->rn_l == tt) x = t->rn_r; else x = t->rn_l;
+ p = t->rn_p;
+ if (p->rn_r == t) p->rn_r = x; else p->rn_l = x;
+ x->rn_p = p;
+ /*
+ * Demote routes attached to us.
+ */
+ if (t->rn_mklist) {
+ if (x->rn_b >= 0) {
+ for (mp = &x->rn_mklist; m = *mp;)
+ mp = &m->rm_mklist;
+ *mp = t->rn_mklist;
+ } else {
+ for (m = t->rn_mklist; m;) {
+ struct radix_mask *mm = m->rm_mklist;
+ if (m == x->rn_mklist && (--(m->rm_refs) < 0)) {
+ x->rn_mklist = 0;
+ MKFree(m);
+ } else
+ printf("%s %x at %x\n",
+ "rn_delete: Orphaned Mask", m, x);
+ m = mm;
+ }
+ }
+ }
+ /*
+ * We may be holding an active internal node in the tree.
+ */
+ x = tt + 1;
+ if (t != x) {
+#ifndef RN_DEBUG
+ *t = *x;
+#else
+ b = t->rn_info; *t = *x; t->rn_info = b;
+#endif
+ t->rn_l->rn_p = t; t->rn_r->rn_p = t;
+ p = x->rn_p;
+ if (p->rn_l == x) p->rn_l = t; else p->rn_r = t;
+ }
+out:
+ tt->rn_flags &= ~RNF_ACTIVE;
+ tt[1].rn_flags &= ~RNF_ACTIVE;
+ return (tt);
+}
+
+int
+rn_walktree(h, f, w)
+ struct radix_node_head *h;
+ register int (*f)();
+ void *w;
+{
+ int error;
+ struct radix_node *base, *next;
+ register struct radix_node *rn = h->rnh_treetop;
+ /*
+ * This gets complicated because we may delete the node
+ * while applying the function f to it, so we need to calculate
+ * the successor node in advance.
+ */
+ /* First time through node, go left */
+ while (rn->rn_b >= 0)
+ rn = rn->rn_l;
+ for (;;) {
+ base = rn;
+ /* If at right child go back up, otherwise, go right */
+ while (rn->rn_p->rn_r == rn && (rn->rn_flags & RNF_ROOT) == 0)
+ rn = rn->rn_p;
+ /* Find the next *leaf* since next node might vanish, too */
+ for (rn = rn->rn_p->rn_r; rn->rn_b >= 0;)
+ rn = rn->rn_l;
+ next = rn;
+ /* Process leaves */
+ while (rn = base) {
+ base = rn->rn_dupedkey;
+ if (!(rn->rn_flags & RNF_ROOT) && (error = (*f)(rn, w)))
+ return (error);
+ }
+ rn = next;
+ if (rn->rn_flags & RNF_ROOT)
+ return (0);
+ }
+ /* NOTREACHED */
+}
+
+int
+rn_inithead(head, off)
+ void **head;
+ int off;
+{
+ register struct radix_node_head *rnh;
+ register struct radix_node *t, *tt, *ttt;
+ if (*head)
+ return (1);
+ R_Malloc(rnh, struct radix_node_head *, sizeof (*rnh));
+ if (rnh == 0)
+ return (0);
+ Bzero(rnh, sizeof (*rnh));
+ *head = rnh;
+ t = rn_newpair(rn_zeros, off, rnh->rnh_nodes);
+ ttt = rnh->rnh_nodes + 2;
+ t->rn_r = ttt;
+ t->rn_p = t;
+ tt = t->rn_l;
+ tt->rn_flags = t->rn_flags = RNF_ROOT | RNF_ACTIVE;
+ tt->rn_b = -1 - off;
+ *ttt = *tt;
+ ttt->rn_key = rn_ones;
+ rnh->rnh_addaddr = rn_addroute;
+ rnh->rnh_deladdr = rn_delete;
+ rnh->rnh_matchaddr = rn_match;
+ rnh->rnh_walktree = rn_walktree;
+ rnh->rnh_treetop = t;
+ return (1);
+}
+
+void
+rn_init()
+{
+ char *cp, *cplim;
+#ifdef KERNEL
+ struct domain *dom;
+
+ for (dom = domains; dom; dom = dom->dom_next)
+ if (dom->dom_maxrtkey > max_keylen)
+ max_keylen = dom->dom_maxrtkey;
+#endif
+ if (max_keylen == 0) {
+ printf("rn_init: radix functions require max_keylen be set\n");
+ return;
+ }
+ R_Malloc(rn_zeros, char *, 3 * max_keylen);
+ if (rn_zeros == NULL)
+ panic("rn_init");
+ Bzero(rn_zeros, 3 * max_keylen);
+ rn_ones = cp = rn_zeros + max_keylen;
+ maskedKey = cplim = rn_ones + max_keylen;
+ while (cp < cplim)
+ *cp++ = -1;
+ if (rn_inithead((void **)&mask_rnhead, 0) == 0)
+ panic("rn_init 2");
+}
diff --git a/sys/net/radix.h b/sys/net/radix.h
new file mode 100644
index 0000000..a11057f
--- /dev/null
+++ b/sys/net/radix.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 1988, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)radix.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef _RADIX_H_
+#define _RADIX_H_
+
+/*
+ * Radix search tree node layout.
+ */
+
+struct radix_node {
+ struct radix_mask *rn_mklist; /* list of masks contained in subtree */
+ struct radix_node *rn_p; /* parent */
+ short rn_b; /* bit offset; -1-index(netmask) */
+ char rn_bmask; /* node: mask for bit test*/
+ u_char rn_flags; /* enumerated next */
+#define RNF_NORMAL 1 /* leaf contains normal route */
+#define RNF_ROOT 2 /* leaf is root leaf for tree */
+#define RNF_ACTIVE 4 /* This node is alive (for rtfree) */
+ union {
+ struct { /* leaf only data: */
+ caddr_t rn_Key; /* object of search */
+ caddr_t rn_Mask; /* netmask, if present */
+ struct radix_node *rn_Dupedkey;
+ } rn_leaf;
+ struct { /* node only data: */
+ int rn_Off; /* where to start compare */
+ struct radix_node *rn_L;/* progeny */
+ struct radix_node *rn_R;/* progeny */
+ }rn_node;
+ } rn_u;
+#ifdef RN_DEBUG
+ int rn_info;
+ struct radix_node *rn_twin;
+ struct radix_node *rn_ybro;
+#endif
+};
+
+#define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey
+#define rn_key rn_u.rn_leaf.rn_Key
+#define rn_mask rn_u.rn_leaf.rn_Mask
+#define rn_off rn_u.rn_node.rn_Off
+#define rn_l rn_u.rn_node.rn_L
+#define rn_r rn_u.rn_node.rn_R
+
+/*
+ * Annotations to tree concerning potential routes applying to subtrees.
+ */
+
+extern struct radix_mask {
+ short rm_b; /* bit offset; -1-index(netmask) */
+ char rm_unused; /* cf. rn_bmask */
+ u_char rm_flags; /* cf. rn_flags */
+ struct radix_mask *rm_mklist; /* more masks to try */
+ caddr_t rm_mask; /* the mask */
+ int rm_refs; /* # of references to this struct */
+} *rn_mkfreelist;
+
+#define MKGet(m) {\
+ if (rn_mkfreelist) {\
+ m = rn_mkfreelist; \
+ rn_mkfreelist = (m)->rm_mklist; \
+ } else \
+ R_Malloc(m, struct radix_mask *, sizeof (*(m))); }\
+
+#define MKFree(m) { (m)->rm_mklist = rn_mkfreelist; rn_mkfreelist = (m);}
+
+struct radix_node_head {
+ struct radix_node *rnh_treetop;
+ int rnh_addrsize; /* permit, but not require fixed keys */
+ int rnh_pktsize; /* permit, but not require fixed keys */
+ struct radix_node *(*rnh_addaddr) /* add based on sockaddr */
+ __P((void *v, void *mask,
+ struct radix_node_head *head, struct radix_node nodes[]));
+ struct radix_node *(*rnh_addpkt) /* add based on packet hdr */
+ __P((void *v, void *mask,
+ struct radix_node_head *head, struct radix_node nodes[]));
+ struct radix_node *(*rnh_deladdr) /* remove based on sockaddr */
+ __P((void *v, void *mask, struct radix_node_head *head));
+ struct radix_node *(*rnh_delpkt) /* remove based on packet hdr */
+ __P((void *v, void *mask, struct radix_node_head *head));
+ struct radix_node *(*rnh_matchaddr) /* locate based on sockaddr */
+ __P((void *v, struct radix_node_head *head));
+ struct radix_node *(*rnh_matchpkt) /* locate based on packet hdr */
+ __P((void *v, struct radix_node_head *head));
+ int (*rnh_walktree) /* traverse tree */
+ __P((struct radix_node_head *head, int (*f)(), void *w));
+ struct radix_node rnh_nodes[3]; /* empty tree for common case */
+};
+
+
+#ifndef KERNEL
+#define Bcmp(a, b, n) bcmp(((char *)(a)), ((char *)(b)), (n))
+#define Bzero(p, n) bzero((char *)(p), (int)(n));
+#define R_Malloc(p, t, n) (p = (t) malloc((unsigned int)(n)))
+#define Free(p) free((char *)p);
+#else
+#define Bcmp(a, b, n) bcmp(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n))
+#define Bcopy(a, b, n) bcopy(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n))
+#define Bzero(p, n) bzero((caddr_t)(p), (unsigned)(n));
+#define R_Malloc(p, t, n) (p = (t) malloc((unsigned long)(n), M_RTABLE, M_DONTWAIT))
+#define Free(p) free((caddr_t)p, M_RTABLE);
+
+void rn_init __P((void));
+int rn_inithead __P((void **, int));
+int rn_refines __P((void *, void *));
+int rn_walktree __P((struct radix_node_head *, int (*)(), void *));
+struct radix_node
+ *rn_addmask __P((void *, int, int)),
+ *rn_addroute __P((void *, void *, struct radix_node_head *,
+ struct radix_node [2])),
+ *rn_delete __P((void *, void *, struct radix_node_head *)),
+ *rn_insert __P((void *, struct radix_node_head *, int *,
+ struct radix_node [2])),
+ *rn_match __P((void *, struct radix_node_head *)),
+ *rn_newpair __P((void *, int, struct radix_node[2])),
+ *rn_search __P((void *, struct radix_node *)),
+ *rn_search_m __P((void *, struct radix_node *, void *));
+
+#endif /*KERNEL*/
+#endif /* _RADIX_H_ */
diff --git a/sys/net/raw_cb.c b/sys/net/raw_cb.c
new file mode 100644
index 0000000..e44192d
--- /dev/null
+++ b/sys/net/raw_cb.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)raw_cb.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/raw_cb.h>
+#include <netinet/in.h>
+
+/*
+ * Routines to manage the raw protocol control blocks.
+ *
+ * TODO:
+ * hash lookups by protocol family/protocol + address family
+ * take care of unique address problems per AF?
+ * redo address binding to allow wildcards
+ */
+
+u_long raw_sendspace = RAWSNDQ;
+u_long raw_recvspace = RAWRCVQ;
+
+/*
+ * Allocate a control block and a nominal amount
+ * of buffer space for the socket.
+ */
+int
+raw_attach(so, proto)
+ register struct socket *so;
+ int proto;
+{
+ register struct rawcb *rp = sotorawcb(so);
+ int error;
+
+ /*
+ * It is assumed that raw_attach is called
+ * after space has been allocated for the
+ * rawcb.
+ */
+ if (rp == 0)
+ return (ENOBUFS);
+ if (error = soreserve(so, raw_sendspace, raw_recvspace))
+ return (error);
+ rp->rcb_socket = so;
+ rp->rcb_proto.sp_family = so->so_proto->pr_domain->dom_family;
+ rp->rcb_proto.sp_protocol = proto;
+ insque(rp, &rawcb);
+ return (0);
+}
+
+/*
+ * Detach the raw connection block and discard
+ * socket resources.
+ */
+void
+raw_detach(rp)
+ register struct rawcb *rp;
+{
+ struct socket *so = rp->rcb_socket;
+
+ so->so_pcb = 0;
+ sofree(so);
+ remque(rp);
+#ifdef notdef
+ if (rp->rcb_laddr)
+ m_freem(dtom(rp->rcb_laddr));
+ rp->rcb_laddr = 0;
+#endif
+ free((caddr_t)(rp), M_PCB);
+}
+
+/*
+ * Disconnect and possibly release resources.
+ */
+void
+raw_disconnect(rp)
+ struct rawcb *rp;
+{
+
+#ifdef notdef
+ if (rp->rcb_faddr)
+ m_freem(dtom(rp->rcb_faddr));
+ rp->rcb_faddr = 0;
+#endif
+ if (rp->rcb_socket->so_state & SS_NOFDREF)
+ raw_detach(rp);
+}
+
+#ifdef notdef
+int
+raw_bind(so, nam)
+ register struct socket *so;
+ struct mbuf *nam;
+{
+ struct sockaddr *addr = mtod(nam, struct sockaddr *);
+ register struct rawcb *rp;
+
+ if (ifnet == 0)
+ return (EADDRNOTAVAIL);
+ rp = sotorawcb(so);
+ nam = m_copym(nam, 0, M_COPYALL, M_WAITOK);
+ rp->rcb_laddr = mtod(nam, struct sockaddr *);
+ return (0);
+}
+#endif
diff --git a/sys/net/raw_cb.h b/sys/net/raw_cb.h
new file mode 100644
index 0000000..6003e18
--- /dev/null
+++ b/sys/net/raw_cb.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)raw_cb.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Raw protocol interface control block. Used
+ * to tie a socket to the generic raw interface.
+ */
+struct rawcb {
+ struct rawcb *rcb_next; /* doubly linked list */
+ struct rawcb *rcb_prev;
+ struct socket *rcb_socket; /* back pointer to socket */
+ struct sockaddr *rcb_faddr; /* destination address */
+ struct sockaddr *rcb_laddr; /* socket's address */
+ struct sockproto rcb_proto; /* protocol family, protocol */
+};
+
+#define sotorawcb(so) ((struct rawcb *)(so)->so_pcb)
+
+/*
+ * Nominal space allocated to a raw socket.
+ */
+#define RAWSNDQ 8192
+#define RAWRCVQ 8192
+
+#ifdef KERNEL
+struct rawcb rawcb; /* head of list */
+
+int raw_attach __P((struct socket *, int));
+void raw_ctlinput __P((int, struct sockaddr *));
+void raw_detach __P((struct rawcb *));
+void raw_disconnect __P((struct rawcb *));
+void raw_init __P((void));
+void raw_input __P((struct mbuf *,
+ struct sockproto *, struct sockaddr *, struct sockaddr *));
+int raw_usrreq __P((struct socket *,
+ int, struct mbuf *, struct mbuf *, struct mbuf *));
+#endif
diff --git a/sys/net/raw_usrreq.c b/sys/net/raw_usrreq.c
new file mode 100644
index 0000000..560106e
--- /dev/null
+++ b/sys/net/raw_usrreq.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)raw_usrreq.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/netisr.h>
+#include <net/raw_cb.h>
+
+/*
+ * Initialize raw connection block q.
+ */
+void
+raw_init()
+{
+
+ rawcb.rcb_next = rawcb.rcb_prev = &rawcb;
+}
+
+
+/*
+ * Raw protocol input routine. Find the socket
+ * associated with the packet(s) and move them over. If
+ * nothing exists for this packet, drop it.
+ */
+/*
+ * Raw protocol interface.
+ */
+void
+raw_input(m0, proto, src, dst)
+ struct mbuf *m0;
+ register struct sockproto *proto;
+ struct sockaddr *src, *dst;
+{
+ register struct rawcb *rp;
+ register struct mbuf *m = m0;
+ register int sockets = 0;
+ struct socket *last;
+
+ last = 0;
+ for (rp = rawcb.rcb_next; rp != &rawcb; rp = rp->rcb_next) {
+ if (rp->rcb_proto.sp_family != proto->sp_family)
+ continue;
+ if (rp->rcb_proto.sp_protocol &&
+ rp->rcb_proto.sp_protocol != proto->sp_protocol)
+ continue;
+ /*
+ * We assume the lower level routines have
+ * placed the address in a canonical format
+ * suitable for a structure comparison.
+ *
+ * Note that if the lengths are not the same
+ * the comparison will fail at the first byte.
+ */
+#define equal(a1, a2) \
+ (bcmp((caddr_t)(a1), (caddr_t)(a2), a1->sa_len) == 0)
+ if (rp->rcb_laddr && !equal(rp->rcb_laddr, dst))
+ continue;
+ if (rp->rcb_faddr && !equal(rp->rcb_faddr, src))
+ continue;
+ if (last) {
+ struct mbuf *n;
+ if (n = m_copy(m, 0, (int)M_COPYALL)) {
+ if (sbappendaddr(&last->so_rcv, src,
+ n, (struct mbuf *)0) == 0)
+ /* should notify about lost packet */
+ m_freem(n);
+ else {
+ sorwakeup(last);
+ sockets++;
+ }
+ }
+ }
+ last = rp->rcb_socket;
+ }
+ if (last) {
+ if (sbappendaddr(&last->so_rcv, src,
+ m, (struct mbuf *)0) == 0)
+ m_freem(m);
+ else {
+ sorwakeup(last);
+ sockets++;
+ }
+ } else
+ m_freem(m);
+}
+
+/*ARGSUSED*/
+void
+raw_ctlinput(cmd, arg)
+ int cmd;
+ struct sockaddr *arg;
+{
+
+ if (cmd < 0 || cmd > PRC_NCMDS)
+ return;
+ /* INCOMPLETE */
+}
+
+/*ARGSUSED*/
+int
+raw_usrreq(so, req, m, nam, control)
+ struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *control;
+{
+ register struct rawcb *rp = sotorawcb(so);
+ register int error = 0;
+ int len;
+
+ if (req == PRU_CONTROL)
+ return (EOPNOTSUPP);
+ if (control && control->m_len) {
+ error = EOPNOTSUPP;
+ goto release;
+ }
+ if (rp == 0) {
+ error = EINVAL;
+ goto release;
+ }
+ switch (req) {
+
+ /*
+ * Allocate a raw control block and fill in the
+ * necessary info to allow packets to be routed to
+ * the appropriate raw interface routine.
+ */
+ case PRU_ATTACH:
+ if ((so->so_state & SS_PRIV) == 0) {
+ error = EACCES;
+ break;
+ }
+ error = raw_attach(so, (int)nam);
+ break;
+
+ /*
+ * Destroy state just before socket deallocation.
+ * Flush data or not depending on the options.
+ */
+ case PRU_DETACH:
+ if (rp == 0) {
+ error = ENOTCONN;
+ break;
+ }
+ raw_detach(rp);
+ break;
+
+#ifdef notdef
+ /*
+ * If a socket isn't bound to a single address,
+ * the raw input routine will hand it anything
+ * within that protocol family (assuming there's
+ * nothing else around it should go to).
+ */
+ case PRU_CONNECT:
+ if (rp->rcb_faddr) {
+ error = EISCONN;
+ break;
+ }
+ nam = m_copym(nam, 0, M_COPYALL, M_WAIT);
+ rp->rcb_faddr = mtod(nam, struct sockaddr *);
+ soisconnected(so);
+ break;
+
+ case PRU_BIND:
+ if (rp->rcb_laddr) {
+ error = EINVAL; /* XXX */
+ break;
+ }
+ error = raw_bind(so, nam);
+ break;
+#endif
+
+ case PRU_CONNECT2:
+ error = EOPNOTSUPP;
+ goto release;
+
+ case PRU_DISCONNECT:
+ if (rp->rcb_faddr == 0) {
+ error = ENOTCONN;
+ break;
+ }
+ raw_disconnect(rp);
+ soisdisconnected(so);
+ break;
+
+ /*
+ * Mark the connection as being incapable of further input.
+ */
+ case PRU_SHUTDOWN:
+ socantsendmore(so);
+ break;
+
+ /*
+ * Ship a packet out. The appropriate raw output
+ * routine handles any massaging necessary.
+ */
+ case PRU_SEND:
+ if (nam) {
+ if (rp->rcb_faddr) {
+ error = EISCONN;
+ break;
+ }
+ rp->rcb_faddr = mtod(nam, struct sockaddr *);
+ } else if (rp->rcb_faddr == 0) {
+ error = ENOTCONN;
+ break;
+ }
+ error = (*so->so_proto->pr_output)(m, so);
+ m = NULL;
+ if (nam)
+ rp->rcb_faddr = 0;
+ break;
+
+ case PRU_ABORT:
+ raw_disconnect(rp);
+ sofree(so);
+ soisdisconnected(so);
+ break;
+
+ case PRU_SENSE:
+ /*
+ * stat: don't bother with a blocksize.
+ */
+ return (0);
+
+ /*
+ * Not supported.
+ */
+ case PRU_RCVOOB:
+ case PRU_RCVD:
+ return(EOPNOTSUPP);
+
+ case PRU_LISTEN:
+ case PRU_ACCEPT:
+ case PRU_SENDOOB:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_SOCKADDR:
+ if (rp->rcb_laddr == 0) {
+ error = EINVAL;
+ break;
+ }
+ len = rp->rcb_laddr->sa_len;
+ bcopy((caddr_t)rp->rcb_laddr, mtod(nam, caddr_t), (unsigned)len);
+ nam->m_len = len;
+ break;
+
+ case PRU_PEERADDR:
+ if (rp->rcb_faddr == 0) {
+ error = ENOTCONN;
+ break;
+ }
+ len = rp->rcb_faddr->sa_len;
+ bcopy((caddr_t)rp->rcb_faddr, mtod(nam, caddr_t), (unsigned)len);
+ nam->m_len = len;
+ break;
+
+ default:
+ panic("raw_usrreq");
+ }
+release:
+ if (m != NULL)
+ m_freem(m);
+ return (error);
+}
diff --git a/sys/net/route.c b/sys/net/route.c
new file mode 100644
index 0000000..f37e84c
--- /dev/null
+++ b/sys/net/route.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright (c) 1980, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)route.c 8.2 (Berkeley) 11/15/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/raw_cb.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+
+#ifdef NS
+#include <netns/ns.h>
+#endif
+
+#define SA(p) ((struct sockaddr *)(p))
+
+int rttrash; /* routes not in table but not freed */
+struct sockaddr wildcard; /* zero valued cookie for wildcard searches */
+
+void
+rtable_init(table)
+ void **table;
+{
+ struct domain *dom;
+ for (dom = domains; dom; dom = dom->dom_next)
+ if (dom->dom_rtattach)
+ dom->dom_rtattach(&table[dom->dom_family],
+ dom->dom_rtoffset);
+}
+
+void
+route_init()
+{
+ rn_init(); /* initialize all zeroes, all ones, mask table */
+ rtable_init((void **)rt_tables);
+}
+
+/*
+ * Packet routing routines.
+ */
+void
+rtalloc(ro)
+ register struct route *ro;
+{
+ if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP))
+ return; /* XXX */
+ ro->ro_rt = rtalloc1(&ro->ro_dst, 1);
+}
+
+struct rtentry *
+rtalloc1(dst, report)
+ register struct sockaddr *dst;
+ int report;
+{
+ register struct radix_node_head *rnh = rt_tables[dst->sa_family];
+ register struct rtentry *rt;
+ register struct radix_node *rn;
+ struct rtentry *newrt = 0;
+ struct rt_addrinfo info;
+ int s = splnet(), err = 0, msgtype = RTM_MISS;
+
+ if (rnh && (rn = rnh->rnh_matchaddr((caddr_t)dst, rnh)) &&
+ ((rn->rn_flags & RNF_ROOT) == 0)) {
+ newrt = rt = (struct rtentry *)rn;
+ if (report && (rt->rt_flags & RTF_CLONING)) {
+ err = rtrequest(RTM_RESOLVE, dst, SA(0),
+ SA(0), 0, &newrt);
+ if (err) {
+ newrt = rt;
+ rt->rt_refcnt++;
+ goto miss;
+ }
+ if ((rt = newrt) && (rt->rt_flags & RTF_XRESOLVE)) {
+ msgtype = RTM_RESOLVE;
+ goto miss;
+ }
+ } else
+ rt->rt_refcnt++;
+ } else {
+ rtstat.rts_unreach++;
+ miss: if (report) {
+ bzero((caddr_t)&info, sizeof(info));
+ info.rti_info[RTAX_DST] = dst;
+ rt_missmsg(msgtype, &info, 0, err);
+ }
+ }
+ splx(s);
+ return (newrt);
+}
+
+void
+rtfree(rt)
+ register struct rtentry *rt;
+{
+ register struct ifaddr *ifa;
+
+ if (rt == 0)
+ panic("rtfree");
+ rt->rt_refcnt--;
+ if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) {
+ if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT))
+ panic ("rtfree 2");
+ rttrash--;
+ if (rt->rt_refcnt < 0) {
+ printf("rtfree: %x not freed (neg refs)\n", rt);
+ return;
+ }
+ ifa = rt->rt_ifa;
+ IFAFREE(ifa);
+ Free(rt_key(rt));
+ Free(rt);
+ }
+}
+
+void
+ifafree(ifa)
+ register struct ifaddr *ifa;
+{
+ if (ifa == NULL)
+ panic("ifafree");
+ if (ifa->ifa_refcnt == 0)
+ free(ifa, M_IFADDR);
+ else
+ ifa->ifa_refcnt--;
+}
+
+/*
+ * Force a routing table entry to the specified
+ * destination to go through the given gateway.
+ * Normally called as a result of a routing redirect
+ * message from the network layer.
+ *
+ * N.B.: must be called at splnet
+ *
+ */
+void
+rtredirect(dst, gateway, netmask, flags, src, rtp)
+ struct sockaddr *dst, *gateway, *netmask, *src;
+ int flags;
+ struct rtentry **rtp;
+{
+ register struct rtentry *rt;
+ int error = 0;
+ short *stat = 0;
+ struct rt_addrinfo info;
+ struct ifaddr *ifa;
+
+ /* verify the gateway is directly reachable */
+ if ((ifa = ifa_ifwithnet(gateway)) == 0) {
+ error = ENETUNREACH;
+ goto out;
+ }
+ rt = rtalloc1(dst, 0);
+ /*
+ * If the redirect isn't from our current router for this dst,
+ * it's either old or wrong. If it redirects us to ourselves,
+ * we have a routing loop, perhaps as a result of an interface
+ * going down recently.
+ */
+#define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0)
+ if (!(flags & RTF_DONE) && rt &&
+ (!equal(src, rt->rt_gateway) || rt->rt_ifa != ifa))
+ error = EINVAL;
+ else if (ifa_ifwithaddr(gateway))
+ error = EHOSTUNREACH;
+ if (error)
+ goto done;
+ /*
+ * Create a new entry if we just got back a wildcard entry
+ * or the the lookup failed. This is necessary for hosts
+ * which use routing redirects generated by smart gateways
+ * to dynamically build the routing tables.
+ */
+ if ((rt == 0) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2))
+ goto create;
+ /*
+ * Don't listen to the redirect if it's
+ * for a route to an interface.
+ */
+ if (rt->rt_flags & RTF_GATEWAY) {
+ if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) {
+ /*
+ * Changing from route to net => route to host.
+ * Create new route, rather than smashing route to net.
+ */
+ create:
+ flags |= RTF_GATEWAY | RTF_DYNAMIC;
+ error = rtrequest((int)RTM_ADD, dst, gateway,
+ netmask, flags,
+ (struct rtentry **)0);
+ stat = &rtstat.rts_dynamic;
+ } else {
+ /*
+ * Smash the current notion of the gateway to
+ * this destination. Should check about netmask!!!
+ */
+ rt->rt_flags |= RTF_MODIFIED;
+ flags |= RTF_MODIFIED;
+ stat = &rtstat.rts_newgateway;
+ rt_setgate(rt, rt_key(rt), gateway);
+ }
+ } else
+ error = EHOSTUNREACH;
+done:
+ if (rt) {
+ if (rtp && !error)
+ *rtp = rt;
+ else
+ rtfree(rt);
+ }
+out:
+ if (error)
+ rtstat.rts_badredirect++;
+ else if (stat != NULL)
+ (*stat)++;
+ bzero((caddr_t)&info, sizeof(info));
+ info.rti_info[RTAX_DST] = dst;
+ info.rti_info[RTAX_GATEWAY] = gateway;
+ info.rti_info[RTAX_NETMASK] = netmask;
+ info.rti_info[RTAX_AUTHOR] = src;
+ rt_missmsg(RTM_REDIRECT, &info, flags, error);
+}
+
+/*
+* Routing table ioctl interface.
+*/
+int
+rtioctl(req, data, p)
+ int req;
+ caddr_t data;
+ struct proc *p;
+{
+ return (EOPNOTSUPP);
+}
+
+struct ifaddr *
+ifa_ifwithroute(flags, dst, gateway)
+ int flags;
+ struct sockaddr *dst, *gateway;
+{
+ register struct ifaddr *ifa;
+ if ((flags & RTF_GATEWAY) == 0) {
+ /*
+ * If we are adding a route to an interface,
+ * and the interface is a pt to pt link
+ * we should search for the destination
+ * as our clue to the interface. Otherwise
+ * we can use the local address.
+ */
+ ifa = 0;
+ if (flags & RTF_HOST)
+ ifa = ifa_ifwithdstaddr(dst);
+ if (ifa == 0)
+ ifa = ifa_ifwithaddr(gateway);
+ } else {
+ /*
+ * If we are adding a route to a remote net
+ * or host, the gateway may still be on the
+ * other end of a pt to pt link.
+ */
+ ifa = ifa_ifwithdstaddr(gateway);
+ }
+ if (ifa == 0)
+ ifa = ifa_ifwithnet(gateway);
+ if (ifa == 0) {
+ struct rtentry *rt = rtalloc1(dst, 0);
+ if (rt == 0)
+ return (0);
+ rt->rt_refcnt--;
+ if ((ifa = rt->rt_ifa) == 0)
+ return (0);
+ }
+ if (ifa->ifa_addr->sa_family != dst->sa_family) {
+ struct ifaddr *oifa = ifa;
+ ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp);
+ if (ifa == 0)
+ ifa = oifa;
+ }
+ return (ifa);
+}
+
+#define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+int
+rtrequest(req, dst, gateway, netmask, flags, ret_nrt)
+ int req, flags;
+ struct sockaddr *dst, *gateway, *netmask;
+ struct rtentry **ret_nrt;
+{
+ int s = splnet(); int error = 0;
+ register struct rtentry *rt;
+ register struct radix_node *rn;
+ register struct radix_node_head *rnh;
+ struct ifaddr *ifa;
+ struct sockaddr *ndst;
+#define senderr(x) { error = x ; goto bad; }
+
+ if ((rnh = rt_tables[dst->sa_family]) == 0)
+ senderr(ESRCH);
+ if (flags & RTF_HOST)
+ netmask = 0;
+ switch (req) {
+ case RTM_DELETE:
+ if ((rn = rnh->rnh_deladdr(dst, netmask, rnh)) == 0)
+ senderr(ESRCH);
+ if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT))
+ panic ("rtrequest delete");
+ rt = (struct rtentry *)rn;
+ rt->rt_flags &= ~RTF_UP;
+ if (rt->rt_gwroute) {
+ rt = rt->rt_gwroute; RTFREE(rt);
+ (rt = (struct rtentry *)rn)->rt_gwroute = 0;
+ }
+ if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest)
+ ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0));
+ rttrash++;
+ if (ret_nrt)
+ *ret_nrt = rt;
+ else if (rt->rt_refcnt <= 0) {
+ rt->rt_refcnt++;
+ rtfree(rt);
+ }
+ break;
+
+ case RTM_RESOLVE:
+ if (ret_nrt == 0 || (rt = *ret_nrt) == 0)
+ senderr(EINVAL);
+ ifa = rt->rt_ifa;
+ flags = rt->rt_flags & ~RTF_CLONING;
+ gateway = rt->rt_gateway;
+ if ((netmask = rt->rt_genmask) == 0)
+ flags |= RTF_HOST;
+ goto makeroute;
+
+ case RTM_ADD:
+ if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == 0)
+ senderr(ENETUNREACH);
+ makeroute:
+ R_Malloc(rt, struct rtentry *, sizeof(*rt));
+ if (rt == 0)
+ senderr(ENOBUFS);
+ Bzero(rt, sizeof(*rt));
+ rt->rt_flags = RTF_UP | flags;
+ if (rt_setgate(rt, dst, gateway)) {
+ Free(rt);
+ senderr(ENOBUFS);
+ }
+ ndst = rt_key(rt);
+ if (netmask) {
+ rt_maskedcopy(dst, ndst, netmask);
+ } else
+ Bcopy(dst, ndst, dst->sa_len);
+ rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)netmask,
+ rnh, rt->rt_nodes);
+ if (rn == 0) {
+ if (rt->rt_gwroute)
+ rtfree(rt->rt_gwroute);
+ Free(rt_key(rt));
+ Free(rt);
+ senderr(EEXIST);
+ }
+ ifa->ifa_refcnt++;
+ rt->rt_ifa = ifa;
+ rt->rt_ifp = ifa->ifa_ifp;
+ if (req == RTM_RESOLVE)
+ rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */
+ if (ifa->ifa_rtrequest)
+ ifa->ifa_rtrequest(req, rt, SA(ret_nrt ? *ret_nrt : 0));
+ if (ret_nrt) {
+ *ret_nrt = rt;
+ rt->rt_refcnt++;
+ }
+ break;
+ }
+bad:
+ splx(s);
+ return (error);
+}
+
+int
+rt_setgate(rt0, dst, gate)
+ struct rtentry *rt0;
+ struct sockaddr *dst, *gate;
+{
+ caddr_t new, old;
+ int dlen = ROUNDUP(dst->sa_len), glen = ROUNDUP(gate->sa_len);
+ register struct rtentry *rt = rt0;
+
+ if (rt->rt_gateway == 0 || glen > ROUNDUP(rt->rt_gateway->sa_len)) {
+ old = (caddr_t)rt_key(rt);
+ R_Malloc(new, caddr_t, dlen + glen);
+ if (new == 0)
+ return 1;
+ rt->rt_nodes->rn_key = new;
+ } else {
+ new = rt->rt_nodes->rn_key;
+ old = 0;
+ }
+ Bcopy(gate, (rt->rt_gateway = (struct sockaddr *)(new + dlen)), glen);
+ if (old) {
+ Bcopy(dst, new, dlen);
+ Free(old);
+ }
+ if (rt->rt_gwroute) {
+ rt = rt->rt_gwroute; RTFREE(rt);
+ rt = rt0; rt->rt_gwroute = 0;
+ }
+ if (rt->rt_flags & RTF_GATEWAY) {
+ rt->rt_gwroute = rtalloc1(gate, 1);
+ }
+ return 0;
+}
+
+void
+rt_maskedcopy(src, dst, netmask)
+ struct sockaddr *src, *dst, *netmask;
+{
+ register u_char *cp1 = (u_char *)src;
+ register u_char *cp2 = (u_char *)dst;
+ register u_char *cp3 = (u_char *)netmask;
+ u_char *cplim = cp2 + *cp3;
+ u_char *cplim2 = cp2 + *cp1;
+
+ *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */
+ cp3 += 2;
+ if (cplim > cplim2)
+ cplim = cplim2;
+ while (cp2 < cplim)
+ *cp2++ = *cp1++ & *cp3++;
+ if (cp2 < cplim2)
+ bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2));
+}
+
+/*
+ * Set up a routing table entry, normally
+ * for an interface.
+ */
+int
+rtinit(ifa, cmd, flags)
+ register struct ifaddr *ifa;
+ int cmd, flags;
+{
+ register struct rtentry *rt;
+ register struct sockaddr *dst;
+ register struct sockaddr *deldst;
+ struct mbuf *m = 0;
+ struct rtentry *nrt = 0;
+ int error;
+
+ dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr;
+ if (cmd == RTM_DELETE) {
+ if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) {
+ m = m_get(M_WAIT, MT_SONAME);
+ deldst = mtod(m, struct sockaddr *);
+ rt_maskedcopy(dst, deldst, ifa->ifa_netmask);
+ dst = deldst;
+ }
+ if (rt = rtalloc1(dst, 0)) {
+ rt->rt_refcnt--;
+ if (rt->rt_ifa != ifa) {
+ if (m)
+ (void) m_free(m);
+ return (flags & RTF_HOST ? EHOSTUNREACH
+ : ENETUNREACH);
+ }
+ }
+ }
+ error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask,
+ flags | ifa->ifa_flags, &nrt);
+ if (m)
+ (void) m_free(m);
+ if (cmd == RTM_DELETE && error == 0 && (rt = nrt)) {
+ rt_newaddrmsg(cmd, ifa, error, nrt);
+ if (rt->rt_refcnt <= 0) {
+ rt->rt_refcnt++;
+ rtfree(rt);
+ }
+ }
+ if (cmd == RTM_ADD && error == 0 && (rt = nrt)) {
+ rt->rt_refcnt--;
+ if (rt->rt_ifa != ifa) {
+ printf("rtinit: wrong ifa (%x) was (%x)\n", ifa,
+ rt->rt_ifa);
+ if (rt->rt_ifa->ifa_rtrequest)
+ rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0));
+ IFAFREE(rt->rt_ifa);
+ rt->rt_ifa = ifa;
+ rt->rt_ifp = ifa->ifa_ifp;
+ ifa->ifa_refcnt++;
+ if (ifa->ifa_rtrequest)
+ ifa->ifa_rtrequest(RTM_ADD, rt, SA(0));
+ }
+ rt_newaddrmsg(cmd, ifa, error, nrt);
+ }
+ return (error);
+}
diff --git a/sys/net/route.h b/sys/net/route.h
new file mode 100644
index 0000000..92f672c
--- /dev/null
+++ b/sys/net/route.h
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 1980, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)route.h 8.3 (Berkeley) 4/19/94
+ */
+
+/*
+ * Kernel resident routing tables.
+ *
+ * The routing tables are initialized when interface addresses
+ * are set by making entries for all directly connected interfaces.
+ */
+
+/*
+ * A route consists of a destination address and a reference
+ * to a routing entry. These are often held by protocols
+ * in their control blocks, e.g. inpcb.
+ */
+struct route {
+ struct rtentry *ro_rt;
+ struct sockaddr ro_dst;
+};
+
+/*
+ * These numbers are used by reliable protocols for determining
+ * retransmission behavior and are included in the routing structure.
+ */
+struct rt_metrics {
+ u_long rmx_locks; /* Kernel must leave these values alone */
+ u_long rmx_mtu; /* MTU for this path */
+ u_long rmx_hopcount; /* max hops expected */
+ u_long rmx_expire; /* lifetime for route, e.g. redirect */
+ u_long rmx_recvpipe; /* inbound delay-bandwith product */
+ u_long rmx_sendpipe; /* outbound delay-bandwith product */
+ u_long rmx_ssthresh; /* outbound gateway buffer limit */
+ u_long rmx_rtt; /* estimated round trip time */
+ u_long rmx_rttvar; /* estimated rtt variance */
+ u_long rmx_pksent; /* packets sent using this route */
+};
+
+/*
+ * rmx_rtt and rmx_rttvar are stored as microseconds;
+ * RTTTOPRHZ(rtt) converts to a value suitable for use
+ * by a protocol slowtimo counter.
+ */
+#define RTM_RTTUNIT 1000000 /* units for rtt, rttvar, as units per sec */
+#define RTTTOPRHZ(r) ((r) / (RTM_RTTUNIT / PR_SLOWHZ))
+
+/*
+ * We distinguish between routes to hosts and routes to networks,
+ * preferring the former if available. For each route we infer
+ * the interface to use from the gateway address supplied when
+ * the route was entered. Routes that forward packets through
+ * gateways are marked so that the output routines know to address the
+ * gateway rather than the ultimate destination.
+ */
+#ifndef RNF_NORMAL
+#include <net/radix.h>
+#endif
+struct rtentry {
+ struct radix_node rt_nodes[2]; /* tree glue, and other values */
+#define rt_key(r) ((struct sockaddr *)((r)->rt_nodes->rn_key))
+#define rt_mask(r) ((struct sockaddr *)((r)->rt_nodes->rn_mask))
+ struct sockaddr *rt_gateway; /* value */
+ short rt_flags; /* up/down?, host/net */
+ short rt_refcnt; /* # held references */
+ u_long rt_use; /* raw # packets forwarded */
+ struct ifnet *rt_ifp; /* the answer: interface to use */
+ struct ifaddr *rt_ifa; /* the answer: interface to use */
+ struct sockaddr *rt_genmask; /* for generation of cloned routes */
+ caddr_t rt_llinfo; /* pointer to link level info cache */
+ struct rt_metrics rt_rmx; /* metrics used by rx'ing protocols */
+ struct rtentry *rt_gwroute; /* implied entry for gatewayed routes */
+};
+
+/*
+ * Following structure necessary for 4.3 compatibility;
+ * We should eventually move it to a compat file.
+ */
+struct ortentry {
+ u_long rt_hash; /* to speed lookups */
+ struct sockaddr rt_dst; /* key */
+ struct sockaddr rt_gateway; /* value */
+ short rt_flags; /* up/down?, host/net */
+ short rt_refcnt; /* # held references */
+ u_long rt_use; /* raw # packets forwarded */
+ struct ifnet *rt_ifp; /* the answer: interface to use */
+};
+
+#define RTF_UP 0x1 /* route usable */
+#define RTF_GATEWAY 0x2 /* destination is a gateway */
+#define RTF_HOST 0x4 /* host entry (net otherwise) */
+#define RTF_REJECT 0x8 /* host or net unreachable */
+#define RTF_DYNAMIC 0x10 /* created dynamically (by redirect) */
+#define RTF_MODIFIED 0x20 /* modified dynamically (by redirect) */
+#define RTF_DONE 0x40 /* message confirmed */
+#define RTF_MASK 0x80 /* subnet mask present */
+#define RTF_CLONING 0x100 /* generate new routes on use */
+#define RTF_XRESOLVE 0x200 /* external daemon resolves name */
+#define RTF_LLINFO 0x400 /* generated by ARP or ESIS */
+#define RTF_STATIC 0x800 /* manually added */
+#define RTF_BLACKHOLE 0x1000 /* just discard pkts (during updates) */
+#define RTF_PROTO2 0x4000 /* protocol specific routing flag */
+#define RTF_PROTO1 0x8000 /* protocol specific routing flag */
+
+
+/*
+ * Routing statistics.
+ */
+struct rtstat {
+ short rts_badredirect; /* bogus redirect calls */
+ short rts_dynamic; /* routes created by redirects */
+ short rts_newgateway; /* routes modified by redirects */
+ short rts_unreach; /* lookups which failed */
+ short rts_wildcard; /* lookups satisfied by a wildcard */
+};
+/*
+ * Structures for routing messages.
+ */
+struct rt_msghdr {
+ u_short rtm_msglen; /* to skip over non-understood messages */
+ u_char rtm_version; /* future binary compatibility */
+ u_char rtm_type; /* message type */
+ u_short rtm_index; /* index for associated ifp */
+ int rtm_flags; /* flags, incl. kern & message, e.g. DONE */
+ int rtm_addrs; /* bitmask identifying sockaddrs in msg */
+ pid_t rtm_pid; /* identify sender */
+ int rtm_seq; /* for sender to identify action */
+ int rtm_errno; /* why failed */
+ int rtm_use; /* from rtentry */
+ u_long rtm_inits; /* which metrics we are initializing */
+ struct rt_metrics rtm_rmx; /* metrics themselves */
+};
+
+#define RTM_VERSION 3 /* Up the ante and ignore older versions */
+
+#define RTM_ADD 0x1 /* Add Route */
+#define RTM_DELETE 0x2 /* Delete Route */
+#define RTM_CHANGE 0x3 /* Change Metrics or flags */
+#define RTM_GET 0x4 /* Report Metrics */
+#define RTM_LOSING 0x5 /* Kernel Suspects Partitioning */
+#define RTM_REDIRECT 0x6 /* Told to use different route */
+#define RTM_MISS 0x7 /* Lookup failed on this address */
+#define RTM_LOCK 0x8 /* fix specified metrics */
+#define RTM_OLDADD 0x9 /* caused by SIOCADDRT */
+#define RTM_OLDDEL 0xa /* caused by SIOCDELRT */
+#define RTM_RESOLVE 0xb /* req to resolve dst to LL addr */
+#define RTM_NEWADDR 0xc /* address being added to iface */
+#define RTM_DELADDR 0xd /* address being removed from iface */
+#define RTM_IFINFO 0xe /* iface going up/down etc. */
+
+#define RTV_MTU 0x1 /* init or lock _mtu */
+#define RTV_HOPCOUNT 0x2 /* init or lock _hopcount */
+#define RTV_EXPIRE 0x4 /* init or lock _hopcount */
+#define RTV_RPIPE 0x8 /* init or lock _recvpipe */
+#define RTV_SPIPE 0x10 /* init or lock _sendpipe */
+#define RTV_SSTHRESH 0x20 /* init or lock _ssthresh */
+#define RTV_RTT 0x40 /* init or lock _rtt */
+#define RTV_RTTVAR 0x80 /* init or lock _rttvar */
+
+/*
+ * Bitmask values for rtm_addr.
+ */
+#define RTA_DST 0x1 /* destination sockaddr present */
+#define RTA_GATEWAY 0x2 /* gateway sockaddr present */
+#define RTA_NETMASK 0x4 /* netmask sockaddr present */
+#define RTA_GENMASK 0x8 /* cloning mask sockaddr present */
+#define RTA_IFP 0x10 /* interface name sockaddr present */
+#define RTA_IFA 0x20 /* interface addr sockaddr present */
+#define RTA_AUTHOR 0x40 /* sockaddr for author of redirect */
+#define RTA_BRD 0x80 /* for NEWADDR, broadcast or p-p dest addr */
+
+/*
+ * Index offsets for sockaddr array for alternate internal encoding.
+ */
+#define RTAX_DST 0 /* destination sockaddr present */
+#define RTAX_GATEWAY 1 /* gateway sockaddr present */
+#define RTAX_NETMASK 2 /* netmask sockaddr present */
+#define RTAX_GENMASK 3 /* cloning mask sockaddr present */
+#define RTAX_IFP 4 /* interface name sockaddr present */
+#define RTAX_IFA 5 /* interface addr sockaddr present */
+#define RTAX_AUTHOR 6 /* sockaddr for author of redirect */
+#define RTAX_BRD 7 /* for NEWADDR, broadcast or p-p dest addr */
+#define RTAX_MAX 8 /* size of array to allocate */
+
+struct rt_addrinfo {
+ int rti_addrs;
+ struct sockaddr *rti_info[RTAX_MAX];
+};
+
+struct route_cb {
+ int ip_count;
+ int ns_count;
+ int iso_count;
+ int any_count;
+};
+
+#ifdef KERNEL
+#define RTFREE(rt) \
+ if ((rt)->rt_refcnt <= 1) \
+ rtfree(rt); \
+ else \
+ (rt)->rt_refcnt--;
+
+struct route_cb route_cb;
+struct rtstat rtstat;
+struct radix_node_head *rt_tables[AF_MAX+1];
+
+void route_init __P((void));
+int route_output __P((struct mbuf *, struct socket *));
+int route_usrreq __P((struct socket *,
+ int, struct mbuf *, struct mbuf *, struct mbuf *));
+void rt_ifmsg __P((struct ifnet *));
+void rt_maskedcopy __P((struct sockaddr *,
+ struct sockaddr *, struct sockaddr *));
+void rt_missmsg __P((int, struct rt_addrinfo *, int, int));
+void rt_newaddrmsg __P((int, struct ifaddr *, int, struct rtentry *));
+int rt_setgate __P((struct rtentry *,
+ struct sockaddr *, struct sockaddr *));
+void rt_setmetrics __P((u_long, struct rt_metrics *, struct rt_metrics *));
+void rtable_init __P((void **));
+void rtalloc __P((struct route *));
+struct rtentry *
+ rtalloc1 __P((struct sockaddr *, int));
+void rtfree __P((struct rtentry *));
+int rtinit __P((struct ifaddr *, int, int));
+int rtioctl __P((int, caddr_t, struct proc *));
+void rtredirect __P((struct sockaddr *, struct sockaddr *,
+ struct sockaddr *, int, struct sockaddr *, struct rtentry **));
+int rtrequest __P((int, struct sockaddr *,
+ struct sockaddr *, struct sockaddr *, int, struct rtentry **));
+#endif
diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c
new file mode 100644
index 0000000..db0192f
--- /dev/null
+++ b/sys/net/rtsock.c
@@ -0,0 +1,833 @@
+/*
+ * Copyright (c) 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)rtsock.c 8.3 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/raw_cb.h>
+
+struct sockaddr route_dst = { 2, PF_ROUTE, };
+struct sockaddr route_src = { 2, PF_ROUTE, };
+struct sockproto route_proto = { PF_ROUTE, };
+
+struct walkarg {
+ int w_op, w_arg, w_given, w_needed, w_tmemsize;
+ caddr_t w_where, w_tmem;
+};
+
+static struct mbuf *
+ rt_msg1 __P((int, struct rt_addrinfo *));
+static int rt_msg2 __P((int,
+ struct rt_addrinfo *, caddr_t, struct walkarg *));
+static void rt_xaddrs __P((caddr_t, caddr_t, struct rt_addrinfo *));
+
+/* Sleazy use of local variables throughout file, warning!!!! */
+#define dst info.rti_info[RTAX_DST]
+#define gate info.rti_info[RTAX_GATEWAY]
+#define netmask info.rti_info[RTAX_NETMASK]
+#define genmask info.rti_info[RTAX_GENMASK]
+#define ifpaddr info.rti_info[RTAX_IFP]
+#define ifaaddr info.rti_info[RTAX_IFA]
+#define brdaddr info.rti_info[RTAX_BRD]
+
+/*ARGSUSED*/
+int
+route_usrreq(so, req, m, nam, control)
+ register struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *control;
+{
+ register int error = 0;
+ register struct rawcb *rp = sotorawcb(so);
+ int s;
+
+ if (req == PRU_ATTACH) {
+ MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK);
+ if (so->so_pcb = (caddr_t)rp)
+ bzero(so->so_pcb, sizeof(*rp));
+
+ }
+ if (req == PRU_DETACH && rp) {
+ int af = rp->rcb_proto.sp_protocol;
+ if (af == AF_INET)
+ route_cb.ip_count--;
+ else if (af == AF_NS)
+ route_cb.ns_count--;
+ else if (af == AF_ISO)
+ route_cb.iso_count--;
+ route_cb.any_count--;
+ }
+ s = splnet();
+ error = raw_usrreq(so, req, m, nam, control);
+ rp = sotorawcb(so);
+ if (req == PRU_ATTACH && rp) {
+ int af = rp->rcb_proto.sp_protocol;
+ if (error) {
+ free((caddr_t)rp, M_PCB);
+ splx(s);
+ return (error);
+ }
+ if (af == AF_INET)
+ route_cb.ip_count++;
+ else if (af == AF_NS)
+ route_cb.ns_count++;
+ else if (af == AF_ISO)
+ route_cb.iso_count++;
+ rp->rcb_faddr = &route_src;
+ route_cb.any_count++;
+ soisconnected(so);
+ so->so_options |= SO_USELOOPBACK;
+ }
+ splx(s);
+ return (error);
+}
+
+/*ARGSUSED*/
+int
+route_output(m, so)
+ register struct mbuf *m;
+ struct socket *so;
+{
+ register struct rt_msghdr *rtm = 0;
+ register struct rtentry *rt = 0;
+ struct rtentry *saved_nrt = 0;
+ struct rt_addrinfo info;
+ int len, error = 0;
+ struct ifnet *ifp = 0;
+ struct ifaddr *ifa = 0;
+
+#define senderr(e) { error = e; goto flush;}
+ if (m == 0 || ((m->m_len < sizeof(long)) &&
+ (m = m_pullup(m, sizeof(long))) == 0))
+ return (ENOBUFS);
+ if ((m->m_flags & M_PKTHDR) == 0)
+ panic("route_output");
+ len = m->m_pkthdr.len;
+ if (len < sizeof(*rtm) ||
+ len != mtod(m, struct rt_msghdr *)->rtm_msglen) {
+ dst = 0;
+ senderr(EINVAL);
+ }
+ R_Malloc(rtm, struct rt_msghdr *, len);
+ if (rtm == 0) {
+ dst = 0;
+ senderr(ENOBUFS);
+ }
+ m_copydata(m, 0, len, (caddr_t)rtm);
+ if (rtm->rtm_version != RTM_VERSION) {
+ dst = 0;
+ senderr(EPROTONOSUPPORT);
+ }
+ rtm->rtm_pid = curproc->p_pid;
+ info.rti_addrs = rtm->rtm_addrs;
+ rt_xaddrs((caddr_t)(rtm + 1), len + (caddr_t)rtm, &info);
+ if (dst == 0)
+ senderr(EINVAL);
+ if (genmask) {
+ struct radix_node *t;
+ t = rn_addmask((caddr_t)genmask, 1, 2);
+ if (t && Bcmp(genmask, t->rn_key, *(u_char *)genmask) == 0)
+ genmask = (struct sockaddr *)(t->rn_key);
+ else
+ senderr(ENOBUFS);
+ }
+ switch (rtm->rtm_type) {
+
+ case RTM_ADD:
+ if (gate == 0)
+ senderr(EINVAL);
+ error = rtrequest(RTM_ADD, dst, gate, netmask,
+ rtm->rtm_flags, &saved_nrt);
+ if (error == 0 && saved_nrt) {
+ rt_setmetrics(rtm->rtm_inits,
+ &rtm->rtm_rmx, &saved_nrt->rt_rmx);
+ saved_nrt->rt_refcnt--;
+ saved_nrt->rt_genmask = genmask;
+ }
+ break;
+
+ case RTM_DELETE:
+ error = rtrequest(RTM_DELETE, dst, gate, netmask,
+ rtm->rtm_flags, (struct rtentry **)0);
+ break;
+
+ case RTM_GET:
+ case RTM_CHANGE:
+ case RTM_LOCK:
+ rt = rtalloc1(dst, 0);
+ if (rt == 0)
+ senderr(ESRCH);
+ if (rtm->rtm_type != RTM_GET) {/* XXX: too grotty */
+ struct radix_node *rn;
+ extern struct radix_node_head *mask_rnhead;
+
+ if (Bcmp(dst, rt_key(rt), dst->sa_len) != 0)
+ senderr(ESRCH);
+ if (netmask && (rn = rn_search(netmask,
+ mask_rnhead->rnh_treetop)))
+ netmask = (struct sockaddr *)rn->rn_key;
+ for (rn = rt->rt_nodes; rn; rn = rn->rn_dupedkey)
+ if (netmask == (struct sockaddr *)rn->rn_mask)
+ break;
+ if (rn == 0)
+ senderr(ETOOMANYREFS);
+ rt = (struct rtentry *)rn;
+ }
+ switch(rtm->rtm_type) {
+
+ case RTM_GET:
+ dst = rt_key(rt);
+ gate = rt->rt_gateway;
+ netmask = rt_mask(rt);
+ genmask = rt->rt_genmask;
+ if (rtm->rtm_addrs & (RTA_IFP | RTA_IFA)) {
+ if (ifp = rt->rt_ifp) {
+ ifpaddr = ifp->if_addrlist->ifa_addr;
+ ifaaddr = rt->rt_ifa->ifa_addr;
+ rtm->rtm_index = ifp->if_index;
+ } else {
+ ifpaddr = 0;
+ ifaaddr = 0;
+ }
+ }
+ len = rt_msg2(RTM_GET, &info, (caddr_t)0,
+ (struct walkarg *)0);
+ if (len > rtm->rtm_msglen) {
+ struct rt_msghdr *new_rtm;
+ R_Malloc(new_rtm, struct rt_msghdr *, len);
+ if (new_rtm == 0)
+ senderr(ENOBUFS);
+ Bcopy(rtm, new_rtm, rtm->rtm_msglen);
+ Free(rtm); rtm = new_rtm;
+ }
+ (void)rt_msg2(RTM_GET, &info, (caddr_t)rtm,
+ (struct walkarg *)0);
+ rtm->rtm_flags = rt->rt_flags;
+ rtm->rtm_rmx = rt->rt_rmx;
+ rtm->rtm_addrs = info.rti_addrs;
+ break;
+
+ case RTM_CHANGE:
+ if (gate && rt_setgate(rt, rt_key(rt), gate))
+ senderr(EDQUOT);
+ /* new gateway could require new ifaddr, ifp;
+ flags may also be different; ifp may be specified
+ by ll sockaddr when protocol address is ambiguous */
+ if (ifpaddr && (ifa = ifa_ifwithnet(ifpaddr)) &&
+ (ifp = ifa->ifa_ifp))
+ ifa = ifaof_ifpforaddr(ifaaddr ? ifaaddr : gate,
+ ifp);
+ else if ((ifaaddr && (ifa = ifa_ifwithaddr(ifaaddr))) ||
+ (ifa = ifa_ifwithroute(rt->rt_flags,
+ rt_key(rt), gate)))
+ ifp = ifa->ifa_ifp;
+ if (ifa) {
+ register struct ifaddr *oifa = rt->rt_ifa;
+ if (oifa != ifa) {
+ if (oifa && oifa->ifa_rtrequest)
+ oifa->ifa_rtrequest(RTM_DELETE,
+ rt, gate);
+ IFAFREE(rt->rt_ifa);
+ rt->rt_ifa = ifa;
+ ifa->ifa_refcnt++;
+ rt->rt_ifp = ifp;
+ }
+ }
+ rt_setmetrics(rtm->rtm_inits, &rtm->rtm_rmx,
+ &rt->rt_rmx);
+ if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest)
+ rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, gate);
+ if (genmask)
+ rt->rt_genmask = genmask;
+ /*
+ * Fall into
+ */
+ case RTM_LOCK:
+ rt->rt_rmx.rmx_locks &= ~(rtm->rtm_inits);
+ rt->rt_rmx.rmx_locks |=
+ (rtm->rtm_inits & rtm->rtm_rmx.rmx_locks);
+ break;
+ }
+ break;
+
+ default:
+ senderr(EOPNOTSUPP);
+ }
+
+flush:
+ if (rtm) {
+ if (error)
+ rtm->rtm_errno = error;
+ else
+ rtm->rtm_flags |= RTF_DONE;
+ }
+ if (rt)
+ rtfree(rt);
+ {
+ register struct rawcb *rp = 0;
+ /*
+ * Check to see if we don't want our own messages.
+ */
+ if ((so->so_options & SO_USELOOPBACK) == 0) {
+ if (route_cb.any_count <= 1) {
+ if (rtm)
+ Free(rtm);
+ m_freem(m);
+ return (error);
+ }
+ /* There is another listener, so construct message */
+ rp = sotorawcb(so);
+ }
+ if (rtm) {
+ m_copyback(m, 0, rtm->rtm_msglen, (caddr_t)rtm);
+ Free(rtm);
+ }
+ if (rp)
+ rp->rcb_proto.sp_family = 0; /* Avoid us */
+ if (dst)
+ route_proto.sp_protocol = dst->sa_family;
+ raw_input(m, &route_proto, &route_src, &route_dst);
+ if (rp)
+ rp->rcb_proto.sp_family = PF_ROUTE;
+ }
+ return (error);
+}
+
+void
+rt_setmetrics(which, in, out)
+ u_long which;
+ register struct rt_metrics *in, *out;
+{
+#define metric(f, e) if (which & (f)) out->e = in->e;
+ metric(RTV_RPIPE, rmx_recvpipe);
+ metric(RTV_SPIPE, rmx_sendpipe);
+ metric(RTV_SSTHRESH, rmx_ssthresh);
+ metric(RTV_RTT, rmx_rtt);
+ metric(RTV_RTTVAR, rmx_rttvar);
+ metric(RTV_HOPCOUNT, rmx_hopcount);
+ metric(RTV_MTU, rmx_mtu);
+ metric(RTV_EXPIRE, rmx_expire);
+#undef metric
+}
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+static void
+rt_xaddrs(cp, cplim, rtinfo)
+ register caddr_t cp, cplim;
+ register struct rt_addrinfo *rtinfo;
+{
+ register struct sockaddr *sa;
+ register int i;
+
+ bzero(rtinfo->rti_info, sizeof(rtinfo->rti_info));
+ for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
+ if ((rtinfo->rti_addrs & (1 << i)) == 0)
+ continue;
+ rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
+ ADVANCE(cp, sa);
+ }
+}
+
+/*
+ * Copy data from a buffer back into the indicated mbuf chain,
+ * starting "off" bytes from the beginning, extending the mbuf
+ * chain if necessary.
+ */
+void
+m_copyback(m0, off, len, cp)
+ struct mbuf *m0;
+ register int off;
+ register int len;
+ caddr_t cp;
+{
+ register int mlen;
+ register struct mbuf *m = m0, *n;
+ int totlen = 0;
+
+ if (m0 == 0)
+ return;
+ while (off > (mlen = m->m_len)) {
+ off -= mlen;
+ totlen += mlen;
+ if (m->m_next == 0) {
+ n = m_getclr(M_DONTWAIT, m->m_type);
+ if (n == 0)
+ goto out;
+ n->m_len = min(MLEN, len + off);
+ m->m_next = n;
+ }
+ m = m->m_next;
+ }
+ while (len > 0) {
+ mlen = min (m->m_len - off, len);
+ bcopy(cp, off + mtod(m, caddr_t), (unsigned)mlen);
+ cp += mlen;
+ len -= mlen;
+ mlen += off;
+ off = 0;
+ totlen += mlen;
+ if (len == 0)
+ break;
+ if (m->m_next == 0) {
+ n = m_get(M_DONTWAIT, m->m_type);
+ if (n == 0)
+ break;
+ n->m_len = min(MLEN, len);
+ m->m_next = n;
+ }
+ m = m->m_next;
+ }
+out: if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen))
+ m->m_pkthdr.len = totlen;
+}
+
+static struct mbuf *
+rt_msg1(type, rtinfo)
+ int type;
+ register struct rt_addrinfo *rtinfo;
+{
+ register struct rt_msghdr *rtm;
+ register struct mbuf *m;
+ register int i;
+ register struct sockaddr *sa;
+ int len, dlen;
+
+ m = m_gethdr(M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ return (m);
+ switch (type) {
+
+ case RTM_DELADDR:
+ case RTM_NEWADDR:
+ len = sizeof(struct ifa_msghdr);
+ break;
+
+ case RTM_IFINFO:
+ len = sizeof(struct if_msghdr);
+ break;
+
+ default:
+ len = sizeof(struct rt_msghdr);
+ }
+ if (len > MHLEN)
+ panic("rt_msg1");
+ m->m_pkthdr.len = m->m_len = len;
+ m->m_pkthdr.rcvif = 0;
+ rtm = mtod(m, struct rt_msghdr *);
+ bzero((caddr_t)rtm, len);
+ for (i = 0; i < RTAX_MAX; i++) {
+ if ((sa = rtinfo->rti_info[i]) == NULL)
+ continue;
+ rtinfo->rti_addrs |= (1 << i);
+ dlen = ROUNDUP(sa->sa_len);
+ m_copyback(m, len, dlen, (caddr_t)sa);
+ len += dlen;
+ }
+ if (m->m_pkthdr.len != len) {
+ m_freem(m);
+ return (NULL);
+ }
+ rtm->rtm_msglen = len;
+ rtm->rtm_version = RTM_VERSION;
+ rtm->rtm_type = type;
+ return (m);
+}
+
+static int
+rt_msg2(type, rtinfo, cp, w)
+ int type;
+ register struct rt_addrinfo *rtinfo;
+ caddr_t cp;
+ struct walkarg *w;
+{
+ register int i;
+ int len, dlen, second_time = 0;
+ caddr_t cp0;
+
+ rtinfo->rti_addrs = 0;
+again:
+ switch (type) {
+
+ case RTM_DELADDR:
+ case RTM_NEWADDR:
+ len = sizeof(struct ifa_msghdr);
+ break;
+
+ case RTM_IFINFO:
+ len = sizeof(struct if_msghdr);
+ break;
+
+ default:
+ len = sizeof(struct rt_msghdr);
+ }
+ if (cp0 = cp)
+ cp += len;
+ for (i = 0; i < RTAX_MAX; i++) {
+ register struct sockaddr *sa;
+
+ if ((sa = rtinfo->rti_info[i]) == 0)
+ continue;
+ rtinfo->rti_addrs |= (1 << i);
+ dlen = ROUNDUP(sa->sa_len);
+ if (cp) {
+ bcopy((caddr_t)sa, cp, (unsigned)dlen);
+ cp += dlen;
+ }
+ len += dlen;
+ }
+ if (cp == 0 && w != NULL && !second_time) {
+ register struct walkarg *rw = w;
+
+ rw->w_needed += len;
+ if (rw->w_needed <= 0 && rw->w_where) {
+ if (rw->w_tmemsize < len) {
+ if (rw->w_tmem)
+ free(rw->w_tmem, M_RTABLE);
+ if (rw->w_tmem = (caddr_t)
+ malloc(len, M_RTABLE, M_NOWAIT))
+ rw->w_tmemsize = len;
+ }
+ if (rw->w_tmem) {
+ cp = rw->w_tmem;
+ second_time = 1;
+ goto again;
+ } else
+ rw->w_where = 0;
+ }
+ }
+ if (cp) {
+ register struct rt_msghdr *rtm = (struct rt_msghdr *)cp0;
+
+ rtm->rtm_version = RTM_VERSION;
+ rtm->rtm_type = type;
+ rtm->rtm_msglen = len;
+ }
+ return (len);
+}
+
+/*
+ * This routine is called to generate a message from the routing
+ * socket indicating that a redirect has occured, a routing lookup
+ * has failed, or that a protocol has detected timeouts to a particular
+ * destination.
+ */
+void
+rt_missmsg(type, rtinfo, flags, error)
+ int type, flags, error;
+ register struct rt_addrinfo *rtinfo;
+{
+ register struct rt_msghdr *rtm;
+ register struct mbuf *m;
+ struct sockaddr *sa = rtinfo->rti_info[RTAX_DST];
+
+ if (route_cb.any_count == 0)
+ return;
+ m = rt_msg1(type, rtinfo);
+ if (m == 0)
+ return;
+ rtm = mtod(m, struct rt_msghdr *);
+ rtm->rtm_flags = RTF_DONE | flags;
+ rtm->rtm_errno = error;
+ rtm->rtm_addrs = rtinfo->rti_addrs;
+ route_proto.sp_protocol = sa ? sa->sa_family : 0;
+ raw_input(m, &route_proto, &route_src, &route_dst);
+}
+
+/*
+ * This routine is called to generate a message from the routing
+ * socket indicating that the status of a network interface has changed.
+ */
+void
+rt_ifmsg(ifp)
+ register struct ifnet *ifp;
+{
+ register struct if_msghdr *ifm;
+ struct mbuf *m;
+ struct rt_addrinfo info;
+
+ if (route_cb.any_count == 0)
+ return;
+ bzero((caddr_t)&info, sizeof(info));
+ m = rt_msg1(RTM_IFINFO, &info);
+ if (m == 0)
+ return;
+ ifm = mtod(m, struct if_msghdr *);
+ ifm->ifm_index = ifp->if_index;
+ ifm->ifm_flags = ifp->if_flags;
+ ifm->ifm_data = ifp->if_data;
+ ifm->ifm_addrs = 0;
+ route_proto.sp_protocol = 0;
+ raw_input(m, &route_proto, &route_src, &route_dst);
+}
+
+/*
+ * This is called to generate messages from the routing socket
+ * indicating a network interface has had addresses associated with it.
+ * if we ever reverse the logic and replace messages TO the routing
+ * socket indicate a request to configure interfaces, then it will
+ * be unnecessary as the routing socket will automatically generate
+ * copies of it.
+ */
+void
+rt_newaddrmsg(cmd, ifa, error, rt)
+ int cmd, error;
+ register struct ifaddr *ifa;
+ register struct rtentry *rt;
+{
+ struct rt_addrinfo info;
+ struct sockaddr *sa = 0;
+ int pass;
+ struct mbuf *m = 0;
+ struct ifnet *ifp = ifa->ifa_ifp;
+
+ if (route_cb.any_count == 0)
+ return;
+ for (pass = 1; pass < 3; pass++) {
+ bzero((caddr_t)&info, sizeof(info));
+ if ((cmd == RTM_ADD && pass == 1) ||
+ (cmd == RTM_DELETE && pass == 2)) {
+ register struct ifa_msghdr *ifam;
+ int ncmd = cmd == RTM_ADD ? RTM_NEWADDR : RTM_DELADDR;
+
+ ifaaddr = sa = ifa->ifa_addr;
+ ifpaddr = ifp->if_addrlist->ifa_addr;
+ netmask = ifa->ifa_netmask;
+ brdaddr = ifa->ifa_dstaddr;
+ if ((m = rt_msg1(ncmd, &info)) == NULL)
+ continue;
+ ifam = mtod(m, struct ifa_msghdr *);
+ ifam->ifam_index = ifp->if_index;
+ ifam->ifam_metric = ifa->ifa_metric;
+ ifam->ifam_flags = ifa->ifa_flags;
+ ifam->ifam_addrs = info.rti_addrs;
+ }
+ if ((cmd == RTM_ADD && pass == 2) ||
+ (cmd == RTM_DELETE && pass == 1)) {
+ register struct rt_msghdr *rtm;
+
+ if (rt == 0)
+ continue;
+ netmask = rt_mask(rt);
+ dst = sa = rt_key(rt);
+ gate = rt->rt_gateway;
+ if ((m = rt_msg1(cmd, &info)) == NULL)
+ continue;
+ rtm = mtod(m, struct rt_msghdr *);
+ rtm->rtm_index = ifp->if_index;
+ rtm->rtm_flags |= rt->rt_flags;
+ rtm->rtm_errno = error;
+ rtm->rtm_addrs = info.rti_addrs;
+ }
+ route_proto.sp_protocol = sa ? sa->sa_family : 0;
+ raw_input(m, &route_proto, &route_src, &route_dst);
+ }
+}
+
+/*
+ * This is used in dumping the kernel table via sysctl().
+ */
+int
+sysctl_dumpentry(rn, w)
+ struct radix_node *rn;
+ register struct walkarg *w;
+{
+ register struct rtentry *rt = (struct rtentry *)rn;
+ int error = 0, size;
+ struct rt_addrinfo info;
+
+ if (w->w_op == NET_RT_FLAGS && !(rt->rt_flags & w->w_arg))
+ return 0;
+ bzero((caddr_t)&info, sizeof(info));
+ dst = rt_key(rt);
+ gate = rt->rt_gateway;
+ netmask = rt_mask(rt);
+ genmask = rt->rt_genmask;
+ size = rt_msg2(RTM_GET, &info, 0, w);
+ if (w->w_where && w->w_tmem) {
+ register struct rt_msghdr *rtm = (struct rt_msghdr *)w->w_tmem;
+
+ rtm->rtm_flags = rt->rt_flags;
+ rtm->rtm_use = rt->rt_use;
+ rtm->rtm_rmx = rt->rt_rmx;
+ rtm->rtm_index = rt->rt_ifp->if_index;
+ rtm->rtm_errno = rtm->rtm_pid = rtm->rtm_seq = 0;
+ rtm->rtm_addrs = info.rti_addrs;
+ if (error = copyout((caddr_t)rtm, w->w_where, size))
+ w->w_where = NULL;
+ else
+ w->w_where += size;
+ }
+ return (error);
+}
+
+int
+sysctl_iflist(af, w)
+ int af;
+ register struct walkarg *w;
+{
+ register struct ifnet *ifp;
+ register struct ifaddr *ifa;
+ struct rt_addrinfo info;
+ int len, error = 0;
+
+ bzero((caddr_t)&info, sizeof(info));
+ for (ifp = ifnet; ifp; ifp = ifp->if_next) {
+ if (w->w_arg && w->w_arg != ifp->if_index)
+ continue;
+ ifa = ifp->if_addrlist;
+ ifpaddr = ifa->ifa_addr;
+ len = rt_msg2(RTM_IFINFO, &info, (caddr_t)0, w);
+ ifpaddr = 0;
+ if (w->w_where && w->w_tmem) {
+ register struct if_msghdr *ifm;
+
+ ifm = (struct if_msghdr *)w->w_tmem;
+ ifm->ifm_index = ifp->if_index;
+ ifm->ifm_flags = ifp->if_flags;
+ ifm->ifm_data = ifp->if_data;
+ ifm->ifm_addrs = info.rti_addrs;
+ if (error = copyout((caddr_t)ifm, w->w_where, len))
+ return (error);
+ w->w_where += len;
+ }
+ while (ifa = ifa->ifa_next) {
+ if (af && af != ifa->ifa_addr->sa_family)
+ continue;
+ ifaaddr = ifa->ifa_addr;
+ netmask = ifa->ifa_netmask;
+ brdaddr = ifa->ifa_dstaddr;
+ len = rt_msg2(RTM_NEWADDR, &info, 0, w);
+ if (w->w_where && w->w_tmem) {
+ register struct ifa_msghdr *ifam;
+
+ ifam = (struct ifa_msghdr *)w->w_tmem;
+ ifam->ifam_index = ifa->ifa_ifp->if_index;
+ ifam->ifam_flags = ifa->ifa_flags;
+ ifam->ifam_metric = ifa->ifa_metric;
+ ifam->ifam_addrs = info.rti_addrs;
+ if (error = copyout(w->w_tmem, w->w_where, len))
+ return (error);
+ w->w_where += len;
+ }
+ }
+ ifaaddr = netmask = brdaddr = 0;
+ }
+ return (0);
+}
+
+int
+sysctl_rtable(name, namelen, where, given, new, newlen)
+ int *name;
+ int namelen;
+ caddr_t where;
+ size_t *given;
+ caddr_t *new;
+ size_t newlen;
+{
+ register struct radix_node_head *rnh;
+ int i, s, error = EINVAL;
+ u_char af;
+ struct walkarg w;
+
+ if (new)
+ return (EPERM);
+ if (namelen != 3)
+ return (EINVAL);
+ af = name[0];
+ Bzero(&w, sizeof(w));
+ w.w_where = where;
+ w.w_given = *given;
+ w.w_needed = 0 - w.w_given;
+ w.w_op = name[1];
+ w.w_arg = name[2];
+
+ s = splnet();
+ switch (w.w_op) {
+
+ case NET_RT_DUMP:
+ case NET_RT_FLAGS:
+ for (i = 1; i <= AF_MAX; i++)
+ if ((rnh = rt_tables[i]) && (af == 0 || af == i) &&
+ (error = rnh->rnh_walktree(rnh,
+ sysctl_dumpentry, &w)))
+ break;
+ break;
+
+ case NET_RT_IFLIST:
+ error = sysctl_iflist(af, &w);
+ }
+ splx(s);
+ if (w.w_tmem)
+ free(w.w_tmem, M_RTABLE);
+ w.w_needed += w.w_given;
+ if (where) {
+ *given = w.w_where - where;
+ if (*given < w.w_needed)
+ return (ENOMEM);
+ } else {
+ *given = (11 * w.w_needed) / 10;
+ }
+ return (error);
+}
+
+/*
+ * Definitions of protocols supported in the ROUTE domain.
+ */
+
+extern struct domain routedomain; /* or at least forward */
+
+struct protosw routesw[] = {
+{ SOCK_RAW, &routedomain, 0, PR_ATOMIC|PR_ADDR,
+ raw_input, route_output, raw_ctlinput, 0,
+ route_usrreq,
+ raw_init, 0, 0, 0,
+ sysctl_rtable,
+}
+};
+
+struct domain routedomain =
+ { PF_ROUTE, "route", route_init, 0, 0,
+ routesw, &routesw[sizeof(routesw)/sizeof(routesw[0])] };
diff --git a/sys/net/slcompress.c b/sys/net/slcompress.c
new file mode 100644
index 0000000..70af935
--- /dev/null
+++ b/sys/net/slcompress.c
@@ -0,0 +1,535 @@
+/*-
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)slcompress.c 8.2 (Berkeley) 4/16/94
+ */
+
+/*
+ * Routines to compress and uncompess tcp packets (for transmission
+ * over low speed serial lines.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ *
+ * static char rcsid[] =
+ * "$Header: slcompress.c,v 1.19 89/12/31 08:52:59 van Exp $";
+ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include <net/slcompress.h>
+
+#ifndef SL_NO_STATS
+#define INCR(counter) ++comp->counter;
+#else
+#define INCR(counter)
+#endif
+
+#define BCMP(p1, p2, n) bcmp((char *)(p1), (char *)(p2), (int)(n))
+#define BCOPY(p1, p2, n) bcopy((char *)(p1), (char *)(p2), (int)(n))
+#ifndef KERNEL
+#define ovbcopy bcopy
+#endif
+
+void
+sl_compress_init(comp)
+ struct slcompress *comp;
+{
+ register u_int i;
+ register struct cstate *tstate = comp->tstate;
+
+ bzero((char *)comp, sizeof(*comp));
+ for (i = MAX_STATES - 1; i > 0; --i) {
+ tstate[i].cs_id = i;
+ tstate[i].cs_next = &tstate[i - 1];
+ }
+ tstate[0].cs_next = &tstate[MAX_STATES - 1];
+ tstate[0].cs_id = 0;
+ comp->last_cs = &tstate[0];
+ comp->last_recv = 255;
+ comp->last_xmit = 255;
+ comp->flags = SLF_TOSS;
+}
+
+
+/* ENCODE encodes a number that is known to be non-zero. ENCODEZ
+ * checks for zero (since zero has to be encoded in the long, 3 byte
+ * form).
+ */
+#define ENCODE(n) { \
+ if ((u_short)(n) >= 256) { \
+ *cp++ = 0; \
+ cp[1] = (n); \
+ cp[0] = (n) >> 8; \
+ cp += 2; \
+ } else { \
+ *cp++ = (n); \
+ } \
+}
+#define ENCODEZ(n) { \
+ if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \
+ *cp++ = 0; \
+ cp[1] = (n); \
+ cp[0] = (n) >> 8; \
+ cp += 2; \
+ } else { \
+ *cp++ = (n); \
+ } \
+}
+
+#define DECODEL(f) { \
+ if (*cp == 0) {\
+ (f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \
+ cp += 3; \
+ } else { \
+ (f) = htonl(ntohl(f) + (u_long)*cp++); \
+ } \
+}
+
+#define DECODES(f) { \
+ if (*cp == 0) {\
+ (f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \
+ cp += 3; \
+ } else { \
+ (f) = htons(ntohs(f) + (u_long)*cp++); \
+ } \
+}
+
+#define DECODEU(f) { \
+ if (*cp == 0) {\
+ (f) = htons((cp[1] << 8) | cp[2]); \
+ cp += 3; \
+ } else { \
+ (f) = htons((u_long)*cp++); \
+ } \
+}
+
+u_int
+sl_compress_tcp(m, ip, comp, compress_cid)
+ struct mbuf *m;
+ register struct ip *ip;
+ struct slcompress *comp;
+ int compress_cid;
+{
+ register struct cstate *cs = comp->last_cs->cs_next;
+ register u_int hlen = ip->ip_hl;
+ register struct tcphdr *oth;
+ register struct tcphdr *th;
+ register u_int deltaS, deltaA;
+ register u_int changes = 0;
+ u_char new_seq[16];
+ register u_char *cp = new_seq;
+
+ /*
+ * Bail if this is an IP fragment or if the TCP packet isn't
+ * `compressible' (i.e., ACK isn't set or some other control bit is
+ * set). (We assume that the caller has already made sure the
+ * packet is IP proto TCP).
+ */
+ if ((ip->ip_off & htons(0x3fff)) || m->m_len < 40)
+ return (TYPE_IP);
+
+ th = (struct tcphdr *)&((int *)ip)[hlen];
+ if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK)
+ return (TYPE_IP);
+ /*
+ * Packet is compressible -- we're going to send either a
+ * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need
+ * to locate (or create) the connection state. Special case the
+ * most recently used connection since it's most likely to be used
+ * again & we don't have to do any reordering if it's used.
+ */
+ INCR(sls_packets)
+ if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr ||
+ ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr ||
+ *(int *)th != ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]) {
+ /*
+ * Wasn't the first -- search for it.
+ *
+ * States are kept in a circularly linked list with
+ * last_cs pointing to the end of the list. The
+ * list is kept in lru order by moving a state to the
+ * head of the list whenever it is referenced. Since
+ * the list is short and, empirically, the connection
+ * we want is almost always near the front, we locate
+ * states via linear search. If we don't find a state
+ * for the datagram, the oldest state is (re-)used.
+ */
+ register struct cstate *lcs;
+ register struct cstate *lastcs = comp->last_cs;
+
+ do {
+ lcs = cs; cs = cs->cs_next;
+ INCR(sls_searches)
+ if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr
+ && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr
+ && *(int *)th == ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl])
+ goto found;
+ } while (cs != lastcs);
+
+ /*
+ * Didn't find it -- re-use oldest cstate. Send an
+ * uncompressed packet that tells the other side what
+ * connection number we're using for this conversation.
+ * Note that since the state list is circular, the oldest
+ * state points to the newest and we only need to set
+ * last_cs to update the lru linkage.
+ */
+ INCR(sls_misses)
+ comp->last_cs = lcs;
+ hlen += th->th_off;
+ hlen <<= 2;
+ goto uncompressed;
+
+ found:
+ /*
+ * Found it -- move to the front on the connection list.
+ */
+ if (cs == lastcs)
+ comp->last_cs = lcs;
+ else {
+ lcs->cs_next = cs->cs_next;
+ cs->cs_next = lastcs->cs_next;
+ lastcs->cs_next = cs;
+ }
+ }
+
+ /*
+ * Make sure that only what we expect to change changed. The first
+ * line of the `if' checks the IP protocol version, header length &
+ * type of service. The 2nd line checks the "Don't fragment" bit.
+ * The 3rd line checks the time-to-live and protocol (the protocol
+ * check is unnecessary but costless). The 4th line checks the TCP
+ * header length. The 5th line checks IP options, if any. The 6th
+ * line checks TCP options, if any. If any of these things are
+ * different between the previous & current datagram, we send the
+ * current datagram `uncompressed'.
+ */
+ oth = (struct tcphdr *)&((int *)&cs->cs_ip)[hlen];
+ deltaS = hlen;
+ hlen += th->th_off;
+ hlen <<= 2;
+
+ if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] ||
+ ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] ||
+ ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] ||
+ th->th_off != oth->th_off ||
+ (deltaS > 5 &&
+ BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) ||
+ (th->th_off > 5 &&
+ BCMP(th + 1, oth + 1, (th->th_off - 5) << 2)))
+ goto uncompressed;
+
+ /*
+ * Figure out which of the changing fields changed. The
+ * receiver expects changes in the order: urgent, window,
+ * ack, seq (the order minimizes the number of temporaries
+ * needed in this section of code).
+ */
+ if (th->th_flags & TH_URG) {
+ deltaS = ntohs(th->th_urp);
+ ENCODEZ(deltaS);
+ changes |= NEW_U;
+ } else if (th->th_urp != oth->th_urp)
+ /* argh! URG not set but urp changed -- a sensible
+ * implementation should never do this but RFC793
+ * doesn't prohibit the change so we have to deal
+ * with it. */
+ goto uncompressed;
+
+ if (deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win))) {
+ ENCODE(deltaS);
+ changes |= NEW_W;
+ }
+
+ if (deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack)) {
+ if (deltaA > 0xffff)
+ goto uncompressed;
+ ENCODE(deltaA);
+ changes |= NEW_A;
+ }
+
+ if (deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq)) {
+ if (deltaS > 0xffff)
+ goto uncompressed;
+ ENCODE(deltaS);
+ changes |= NEW_S;
+ }
+
+ switch(changes) {
+
+ case 0:
+ /*
+ * Nothing changed. If this packet contains data and the
+ * last one didn't, this is probably a data packet following
+ * an ack (normal on an interactive connection) and we send
+ * it compressed. Otherwise it's probably a retransmit,
+ * retransmitted ack or window probe. Send it uncompressed
+ * in case the other side missed the compressed version.
+ */
+ if (ip->ip_len != cs->cs_ip.ip_len &&
+ ntohs(cs->cs_ip.ip_len) == hlen)
+ break;
+
+ /* (fall through) */
+
+ case SPECIAL_I:
+ case SPECIAL_D:
+ /*
+ * actual changes match one of our special case encodings --
+ * send packet uncompressed.
+ */
+ goto uncompressed;
+
+ case NEW_S|NEW_A:
+ if (deltaS == deltaA &&
+ deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
+ /* special case for echoed terminal traffic */
+ changes = SPECIAL_I;
+ cp = new_seq;
+ }
+ break;
+
+ case NEW_S:
+ if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
+ /* special case for data xfer */
+ changes = SPECIAL_D;
+ cp = new_seq;
+ }
+ break;
+ }
+
+ deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id);
+ if (deltaS != 1) {
+ ENCODEZ(deltaS);
+ changes |= NEW_I;
+ }
+ if (th->th_flags & TH_PUSH)
+ changes |= TCP_PUSH_BIT;
+ /*
+ * Grab the cksum before we overwrite it below. Then update our
+ * state with this packet's header.
+ */
+ deltaA = ntohs(th->th_sum);
+ BCOPY(ip, &cs->cs_ip, hlen);
+
+ /*
+ * We want to use the original packet as our compressed packet.
+ * (cp - new_seq) is the number of bytes we need for compressed
+ * sequence numbers. In addition we need one byte for the change
+ * mask, one for the connection id and two for the tcp checksum.
+ * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how
+ * many bytes of the original packet to toss so subtract the two to
+ * get the new packet size.
+ */
+ deltaS = cp - new_seq;
+ cp = (u_char *)ip;
+ if (compress_cid == 0 || comp->last_xmit != cs->cs_id) {
+ comp->last_xmit = cs->cs_id;
+ hlen -= deltaS + 4;
+ cp += hlen;
+ *cp++ = changes | NEW_C;
+ *cp++ = cs->cs_id;
+ } else {
+ hlen -= deltaS + 3;
+ cp += hlen;
+ *cp++ = changes;
+ }
+ m->m_len -= hlen;
+ m->m_data += hlen;
+ *cp++ = deltaA >> 8;
+ *cp++ = deltaA;
+ BCOPY(new_seq, cp, deltaS);
+ INCR(sls_compressed)
+ return (TYPE_COMPRESSED_TCP);
+
+ /*
+ * Update connection state cs & send uncompressed packet ('uncompressed'
+ * means a regular ip/tcp packet but with the 'conversation id' we hope
+ * to use on future compressed packets in the protocol field).
+ */
+uncompressed:
+ BCOPY(ip, &cs->cs_ip, hlen);
+ ip->ip_p = cs->cs_id;
+ comp->last_xmit = cs->cs_id;
+ return (TYPE_UNCOMPRESSED_TCP);
+}
+
+
+int
+sl_uncompress_tcp(bufp, len, type, comp)
+ u_char **bufp;
+ int len;
+ u_int type;
+ struct slcompress *comp;
+{
+ register u_char *cp;
+ register u_int hlen, changes;
+ register struct tcphdr *th;
+ register struct cstate *cs;
+ register struct ip *ip;
+
+ switch (type) {
+
+ case TYPE_UNCOMPRESSED_TCP:
+ ip = (struct ip *) *bufp;
+ if (ip->ip_p >= MAX_STATES)
+ goto bad;
+ cs = &comp->rstate[comp->last_recv = ip->ip_p];
+ comp->flags &=~ SLF_TOSS;
+ ip->ip_p = IPPROTO_TCP;
+ hlen = ip->ip_hl;
+ hlen += ((struct tcphdr *)&((int *)ip)[hlen])->th_off;
+ hlen <<= 2;
+ BCOPY(ip, &cs->cs_ip, hlen);
+ cs->cs_ip.ip_sum = 0;
+ cs->cs_hlen = hlen;
+ INCR(sls_uncompressedin)
+ return (len);
+
+ default:
+ goto bad;
+
+ case TYPE_COMPRESSED_TCP:
+ break;
+ }
+ /* We've got a compressed packet. */
+ INCR(sls_compressedin)
+ cp = *bufp;
+ changes = *cp++;
+ if (changes & NEW_C) {
+ /* Make sure the state index is in range, then grab the state.
+ * If we have a good state index, clear the 'discard' flag. */
+ if (*cp >= MAX_STATES)
+ goto bad;
+
+ comp->flags &=~ SLF_TOSS;
+ comp->last_recv = *cp++;
+ } else {
+ /* this packet has an implicit state index. If we've
+ * had a line error since the last time we got an
+ * explicit state index, we have to toss the packet. */
+ if (comp->flags & SLF_TOSS) {
+ INCR(sls_tossed)
+ return (0);
+ }
+ }
+ cs = &comp->rstate[comp->last_recv];
+ hlen = cs->cs_ip.ip_hl << 2;
+ th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen];
+ th->th_sum = htons((*cp << 8) | cp[1]);
+ cp += 2;
+ if (changes & TCP_PUSH_BIT)
+ th->th_flags |= TH_PUSH;
+ else
+ th->th_flags &=~ TH_PUSH;
+
+ switch (changes & SPECIALS_MASK) {
+ case SPECIAL_I:
+ {
+ register u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen;
+ th->th_ack = htonl(ntohl(th->th_ack) + i);
+ th->th_seq = htonl(ntohl(th->th_seq) + i);
+ }
+ break;
+
+ case SPECIAL_D:
+ th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len)
+ - cs->cs_hlen);
+ break;
+
+ default:
+ if (changes & NEW_U) {
+ th->th_flags |= TH_URG;
+ DECODEU(th->th_urp)
+ } else
+ th->th_flags &=~ TH_URG;
+ if (changes & NEW_W)
+ DECODES(th->th_win)
+ if (changes & NEW_A)
+ DECODEL(th->th_ack)
+ if (changes & NEW_S)
+ DECODEL(th->th_seq)
+ break;
+ }
+ if (changes & NEW_I) {
+ DECODES(cs->cs_ip.ip_id)
+ } else
+ cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1);
+
+ /*
+ * At this point, cp points to the first byte of data in the
+ * packet. If we're not aligned on a 4-byte boundary, copy the
+ * data down so the ip & tcp headers will be aligned. Then back up
+ * cp by the tcp/ip header length to make room for the reconstructed
+ * header (we assume the packet we were handed has enough space to
+ * prepend 128 bytes of header). Adjust the length to account for
+ * the new header & fill in the IP total length.
+ */
+ len -= (cp - *bufp);
+ if (len < 0)
+ /* we must have dropped some characters (crc should detect
+ * this but the old slip framing won't) */
+ goto bad;
+
+ if ((int)cp & 3) {
+ if (len > 0)
+ (void) ovbcopy(cp, (caddr_t)((int)cp &~ 3), len);
+ cp = (u_char *)((int)cp &~ 3);
+ }
+ cp -= cs->cs_hlen;
+ len += cs->cs_hlen;
+ cs->cs_ip.ip_len = htons(len);
+ BCOPY(&cs->cs_ip, cp, cs->cs_hlen);
+ *bufp = cp;
+
+ /* recompute the ip header checksum */
+ {
+ register u_short *bp = (u_short *)cp;
+ for (changes = 0; hlen > 0; hlen -= 2)
+ changes += *bp++;
+ changes = (changes & 0xffff) + (changes >> 16);
+ changes = (changes & 0xffff) + (changes >> 16);
+ ((struct ip *)cp)->ip_sum = ~ changes;
+ }
+ return (len);
+bad:
+ comp->flags |= SLF_TOSS;
+ INCR(sls_errorin)
+ return (0);
+}
diff --git a/sys/net/slcompress.h b/sys/net/slcompress.h
new file mode 100644
index 0000000..cefe940
--- /dev/null
+++ b/sys/net/slcompress.h
@@ -0,0 +1,157 @@
+/* slcompress.h 8.1 93/06/10 */
+/*
+ * Definitions for tcp compression routines.
+ *
+ * $Header: slcompress.h,v 1.10 89/12/31 08:53:02 van Exp $
+ *
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+#define MAX_STATES 16 /* must be > 2 and < 256 */
+#define MAX_HDR MLEN /* XXX 4bsd-ism: should really be 128 */
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits). The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet. The next two octets are the TCP checksum
+ * from the original datagram. The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ *
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowlegement, sequence number and IP ID. (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.) Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0. (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type. There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows. Top
+ * three bits are actual packet type. For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* packet types */
+#define TYPE_IP 0x40
+#define TYPE_UNCOMPRESSED_TCP 0x70
+#define TYPE_COMPRESSED_TCP 0x80
+#define TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C 0x40 /* flag bits for what changed in a packet */
+#define NEW_I 0x20
+#define NEW_S 0x08
+#define NEW_A 0x04
+#define NEW_W 0x02
+#define NEW_U 0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+
+/*
+ * "state" data for each active tcp conversation on the wire. This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+ struct cstate *cs_next; /* next most recently used cstate (xmit only) */
+ u_short cs_hlen; /* size of hdr (receive only) */
+ u_char cs_id; /* connection # associated with this state */
+ u_char cs_filler;
+ union {
+ char csu_hdr[MAX_HDR];
+ struct ip csu_ip; /* ip/tcp hdr from most recent packet */
+ } slcs_u;
+};
+#define cs_ip slcs_u.csu_ip
+#define cs_hdr slcs_u.csu_hdr
+
+/*
+ * all the state data for one serial line (we need one of these
+ * per line).
+ */
+struct slcompress {
+ struct cstate *last_cs; /* most recently used tstate */
+ u_char last_recv; /* last rcvd conn. id */
+ u_char last_xmit; /* last sent conn. id */
+ u_short flags;
+#ifndef SL_NO_STATS
+ int sls_packets; /* outbound packets */
+ int sls_compressed; /* outbound compressed packets */
+ int sls_searches; /* searches for connection state */
+ int sls_misses; /* times couldn't find conn. state */
+ int sls_uncompressedin; /* inbound uncompressed packets */
+ int sls_compressedin; /* inbound compressed packets */
+ int sls_errorin; /* inbound unknown type packets */
+ int sls_tossed; /* inbound packets tossed because of error */
+#endif
+ struct cstate tstate[MAX_STATES]; /* xmit connection states */
+ struct cstate rstate[MAX_STATES]; /* receive connection states */
+};
+/* flag values */
+#define SLF_TOSS 1 /* tossing rcvd frames because of input err */
+
+void sl_compress_init __P((struct slcompress *));
+u_int sl_compress_tcp __P((struct mbuf *,
+ struct ip *, struct slcompress *, int));
+int sl_uncompress_tcp __P((u_char **, int, u_int, struct slcompress *));
diff --git a/sys/net/slip.h b/sys/net/slip.h
new file mode 100644
index 0000000..4caeb46
--- /dev/null
+++ b/sys/net/slip.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)slip.h 8.1 (Berkeley) 2/12/94
+ */
+
+/* Ioctls operating on SLIP ttys. */
+#define SLIOCGUNIT _IOR('t', 88, int) /* get slip unit number */
+
+/*
+ * Definitions of the pseudo-link-level header attached to slip
+ * packets grabbed by the packet filter (bpf) traffic monitor.
+ */
+#define SLIP_HDRLEN 16 /* BPF SLIP header length */
+
+/* Offsets into BPF SLIP header. */
+#define SLX_DIR 0 /* direction; see below */
+#define SLX_CHDR 1 /* compressed header data */
+#define CHDR_LEN 15 /* length of compressed header data */
+
+#define SLIPDIR_IN 0 /* incoming */
+#define SLIPDIR_OUT 1 /* outgoing */
diff --git a/sys/netccitt/README.hdlc b/sys/netccitt/README.hdlc
new file mode 100644
index 0000000..24b5fef
--- /dev/null
+++ b/sys/netccitt/README.hdlc
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ *
+ * @(#)README.hdlc 8.1 (Berkeley) 6/10/93
+ *
+ * X.25 HDLC DATA LINK LEVEL:
+ *
+ *
+ * This module implements the Link Level of the Open Systems Interconnect
+ * Model. The implementation is based on the ISO High-Level Data Link
+ * Control (HDLC). These procedures subscribe to the principles of the
+ * ISO-Class of Procedures for point-to-point. These procedures implement
+ * two-way asynchronous balanced mode (LAPB) as recommented by the CCITT.
+ *
+ * The HDLC protocol layer interface consists of the following procedures:
+ * Hd_init (pr_init)
+ * Hd_ouput (pr_output)
+ * Hd_input (pr_input)
+ * Hd_timer (pr_slowtimo)
+ *
+ * Note: Supervisory commands RR, RNR and REJ are not transmitted by this
+ * station.
+ *
+ * This station never enters a busy (RNR) condition.
+ *
+ * The "Generate_rr" variable can be set to FALSE. This means that
+ * we NEVER send an RR. This works just fine if the network level
+ * is X.25 packet protocol -- which it is.
+ *
+ * Currently, this is only a DTE implementation.
+ *
+ * Think about:
+ * If the remote is busy, no iframes are sent. The remote sends a RR
+ * to clear this condition. However, this RR may be damaged, causing
+ * a possible deadlock. A solution is to poll with iframe (P(S)==P(R)
+ * of RNR) indefinitly.
+ *
+ *
+ * Date: February 1984
+ *
+ * Author: Gerald W. Neufeld
+ *
+ * Installation: Department of Computer Science
+ * University of British Columbia
+ * Vancouver, BC, CANADA.
+ *
+ * History:
+ *
+ *
+ */
diff --git a/sys/netccitt/README.packet b/sys/netccitt/README.packet
new file mode 100644
index 0000000..858d75c
--- /dev/null
+++ b/sys/netccitt/README.packet
@@ -0,0 +1,36 @@
+/*
+ * @(#)README.packet 8.1 (Berkeley) 6/10/93
+ *
+ * X.25 NETWORK PACKET LEVEL:
+ *
+ * This implementation is based on Recommentation X.25 as agreed at the
+ * March 1976 and the February 1980 meetings of CCITT Study Group VII.
+ * However, not all aspects are implemented. The following is a list of
+ * features which are not yet or may never be implemented:
+ *
+ * 1. D bit
+ * 2. PVC
+ * 3. fast select
+ *
+ *
+ * Note: This implementation is for DTEs only.
+ *
+ * Currently, only the 1976 verison is implemented.
+ *
+ *
+ * Date: February, 1984
+ *
+ * Author: Gerald W. Neufeld
+ *
+ * Installation: Department of Computer Science
+ * University of British Columbia
+ * Vancouver, BC, CANADA
+ *
+ * To Do: Find some reasonable heuristic for piggybacking packet
+ * level acks.
+ *
+ * Bugs: Clear might be sent before data is all out.
+ *
+ * History:
+ *
+ */
diff --git a/sys/netccitt/ccitt_proto.c b/sys/netccitt/ccitt_proto.c
new file mode 100644
index 0000000..d832fd3
--- /dev/null
+++ b/sys/netccitt/ccitt_proto.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Laboratory for Computation Vision and the Computer Science Department
+ * of the University of British Columbia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ccitt_proto.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+
+#include <netccitt/x25.h>
+
+#include <net/radix.h>
+
+/*
+ * Definitions of protocols supported in the CCITT domain.
+ */
+
+extern struct domain ccittdomain;
+#define DOMAIN &ccittdomain
+
+#ifdef LLC
+int llc_output();
+void llc_ctlinput(), llc_init(), llc_timer();
+#endif
+#ifdef HDLC
+int hd_output();
+void hd_ctlinput(), hd_init(), hd_timer();
+#endif
+int pk_usrreq(), pk_ctloutput();
+void pk_timer(), pk_init(), pk_input(), pk_ctlinput();
+
+struct protosw ccittsw[] = {
+#ifdef LLC
+ { 0, DOMAIN, IEEEPROTO_802LLC,0,
+ 0, llc_output, llc_ctlinput, 0,
+ 0,
+ llc_init, 0, llc_timer, 0,
+ },
+#endif
+#ifdef HDLC
+ { 0, DOMAIN, CCITTPROTO_HDLC,0,
+ 0, hd_output, hd_ctlinput, 0,
+ 0,
+ hd_init, 0, hd_timer, 0,
+ },
+#endif
+ { SOCK_STREAM, DOMAIN, CCITTPROTO_X25, PR_CONNREQUIRED|PR_ATOMIC|PR_WANTRCVD,
+ pk_input, 0, pk_ctlinput, pk_ctloutput,
+ pk_usrreq,
+ pk_init, 0, pk_timer, 0,
+ }
+};
+
+struct domain ccittdomain =
+ { AF_CCITT, "ccitt", 0, 0, 0, ccittsw,
+ &ccittsw[sizeof(ccittsw)/sizeof(ccittsw[0])], 0,
+ rn_inithead, 32, sizeof (struct sockaddr_x25) };
diff --git a/sys/netccitt/dll.h b/sys/netccitt/dll.h
new file mode 100644
index 0000000..46ded88
--- /dev/null
+++ b/sys/netccitt/dll.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) Dirk Husemann, Computer Science Department IV,
+ * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dll.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * We define the additional PRC_* codes in here
+ */
+#ifdef KERNEL
+#ifndef PRC_IFUP
+#define PRC_IFUP 3
+#endif
+#define PRC_CONNECT_INDICATION 8
+#define PRC_CONNECT_REQUEST 9
+#define PRC_DISCONNECT_REQUEST 10
+#define PRC_DISCONNECT_INDICATION 11
+#define PRC_RESET_REQUEST 12
+#endif
+
+/*
+ * Data link layer configuration --- basically a copy of the relevant parts
+ * of x25config, implemented to become a little bit more network
+ * layer independent. (Probably only used for casting et al.)
+ */
+struct dllconfig {
+ u_short dllcfg_unused0:4,
+ dllcfg_unused1:4,
+ dllcfg_trace:1, /* link level tracing flag */
+ dllcfg_window:7; /* link level window size */
+ u_short dllcfg_xchxid:1, /* exchange XID (not yet) */
+ dllcfg_unused2:7; /* here be dragons */
+};
+
+struct dll_ctlinfo {
+ union {
+ struct {
+ struct dllconfig *dctli_up_cfg;
+ u_char dctli_up_lsap;
+ } CTLI_UP;
+ struct {
+ caddr_t dctli_down_pcb;
+ struct rtentry *dctli_down_rt;
+ struct dllconfig *dctli_down_llconf;
+ } CTLI_DOWN;
+ } CTLIun;
+};
+#define dlcti_cfg CTLIun.CTLI_UP.dctli_up_cfg
+#define dlcti_lsap CTLIun.CTLI_UP.dctli_up_lsap
+#define dlcti_pcb CTLIun.CTLI_DOWN.dctli_down_pcb
+#define dlcti_rt CTLIun.CTLI_DOWN.dctli_down_rt
+#define dlcti_conf CTLIun.CTLI_DOWN.dctli_down_llconf
diff --git a/sys/netccitt/hd_debug.c b/sys/netccitt/hd_debug.c
new file mode 100644
index 0000000..b8a45a3
--- /dev/null
+++ b/sys/netccitt/hd_debug.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Laboratory for Computation Vision and the Computer Science Department
+ * of the University of British Columbia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)hd_debug.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+
+#include <netccitt/hdlc.h>
+#include <netccitt/hd_var.h>
+#include <netccitt/x25.h>
+
+#ifdef HDLCDEBUG
+#define NTRACE 32
+
+struct hdlctrace {
+ struct hdcb *ht_hdp;
+ short ht_dir;
+ struct mbuf *ht_frame;
+ struct timeval ht_time;
+} hdtrace[NTRACE];
+
+int lasttracelogged, freezetrace;
+#endif
+
+hd_trace (hdp, direction, frame)
+struct hdcb *hdp;
+register struct Hdlc_frame *frame;
+{
+ register char *s;
+ register int nr, pf, ns, i;
+ struct Hdlc_iframe *iframe = (struct Hdlc_iframe *) frame;
+
+#ifdef HDLCDEBUG
+ hd_savetrace (hdp, direction, frame);
+#endif
+ if (hdp -> hd_xcp -> xc_ltrace) {
+ if (direction == RX)
+ printf ("F-In: ");
+ else if (direction == 2)
+ printf ("F-Xmt: ");
+ else
+ printf ("F-Out: ");
+
+ nr = iframe -> nr;
+ pf = iframe -> pf;
+ ns = iframe -> ns;
+
+ switch (hd_decode (hdp, frame)) {
+ case SABM:
+ printf ("SABM : PF=%d\n", pf);
+ break;
+
+ case DISC:
+ printf ("DISC : PF=%d\n", pf);
+ break;
+
+ case DM:
+ printf ("DM : PF=%d\n", pf);
+ break;
+
+ case FRMR:
+ {
+ register struct Frmr_frame *f = (struct Frmr_frame *)frame;
+
+ printf ("FRMR : PF=%d, TEXT=", pf);
+ for (s = (char *) frame, i = 0; i < 5; ++i, ++s)
+ printf ("%x ", (int) * s & 0xff);
+ printf ("\n");
+ printf ("control=%x v(s)=%d v(r)=%d w%d x%d y%d z%d\n",
+ f->frmr_control, f->frmr_ns, f->frmr_nr,
+ f->frmr_w, f->frmr_x, f->frmr_y, f->frmr_z);
+ break;
+ }
+
+ case UA:
+ printf ("UA : PF=%d\n", pf);
+ break;
+
+ case RR:
+ printf ("RR : N(R)=%d, PF=%d\n", nr, pf);
+ break;
+
+ case RNR:
+ printf ("RNR : N(R)=%d, PF=%d\n", nr, pf);
+ break;
+
+ case REJ:
+ printf ("REJ : N(R)=%d, PF=%d\n", nr, pf);
+ break;
+
+ case IFRAME:
+ {
+ register struct mbuf *m;
+ register int len = 0;
+
+ for(m = dtom (frame); m; m = m -> m_next)
+ len += m -> m_len;
+ len -= HDHEADERLN;
+ printf ("IFRAME : N(R)=%d, PF=%d, N(S)=%d, DATA(%d)=",
+ nr, pf, ns, len);
+ for (s = (char *)iframe->i_field, i = 0; i < 3; ++i, ++s)
+ printf ("%x ", (int) *s & 0xff);
+ printf ("\n");
+ break;
+ }
+
+ default:
+ printf ("ILLEGAL: ");
+ for (s = (char *) frame, i = 0; i < 5; ++i, ++s)
+ printf ("%x ", (int) *s & 0xff);
+ printf ("\n");
+ }
+
+ }
+}
+
+#ifdef HDLCDEBUG
+static
+hd_savetrace (hdp, dir, frame)
+struct hdcb *hdp;
+struct Hdlc_frame *frame;
+{
+ register struct hdlctrace *htp;
+ register struct mbuf *m;
+
+ if (freezetrace)
+ return;
+ htp = &hdtrace[lasttracelogged];
+ lasttracelogged = (lasttracelogged + 1) % NTRACE;
+ if (m = htp->ht_frame)
+ m_freem (m);
+ m = dtom (frame);
+ htp->ht_frame = m_copy (m, 0, m->m_len);
+ htp->ht_hdp = hdp;
+ htp->ht_dir = dir;
+ htp->ht_time = time;
+}
+
+hd_dumptrace (hdp)
+struct hdcb *hdp;
+{
+ register int i, ltrace;
+ register struct hdlctrace *htp;
+
+ freezetrace = 1;
+ hd_status (hdp);
+ printf ("retransmit queue:");
+ for (i = 0; i < 8; i++)
+ printf (" %x", hdp -> hd_retxq[i]);
+ printf ("\n");
+ ltrace = hdp -> hd_xcp -> xc_ltrace;
+ hdp -> hd_xcp -> xc_ltrace = 1;
+ for (i = 0; i < NTRACE; i++) {
+ htp = &hdtrace[(lasttracelogged + i) % NTRACE];
+ if (htp->ht_hdp != hdp || htp->ht_frame == 0)
+ continue;
+ printf ("%d/%d ", htp->ht_time.tv_sec & 0xff,
+ htp->ht_time.tv_usec / 10000);
+ hd_trace (htp->ht_hdp, htp->ht_dir,
+ mtod (htp->ht_frame, struct Hdlc_frame *));
+ m_freem (htp->ht_frame);
+ htp->ht_frame = 0;
+ }
+ hdp -> hd_xcp -> xc_ltrace = ltrace;
+ freezetrace = 0;
+}
+#endif
diff --git a/sys/netccitt/hd_input.c b/sys/netccitt/hd_input.c
new file mode 100644
index 0000000..eb939d0
--- /dev/null
+++ b/sys/netccitt/hd_input.c
@@ -0,0 +1,669 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Laboratory for Computation Vision and the Computer Science Department
+ * of the University of British Columbia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)hd_input.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+
+#include <netccitt/hdlc.h>
+#include <netccitt/hd_var.h>
+#include <netccitt/x25.h>
+
+static frame_reject();
+static rej_routine();
+static free_iframes();
+/*
+ * HDLC INPUT INTERFACE
+ *
+ * This routine is called when the HDLC physical device has
+ * completed reading a frame.
+ */
+
+hdintr ()
+{
+ register struct mbuf *m;
+ register struct hdcb *hdp;
+ register struct ifnet *ifp;
+ register int s;
+ static struct ifnet *lastifp;
+ static struct hdcb *lasthdp;
+
+ for (;;) {
+ s = splimp ();
+ IF_DEQUEUE (&hdintrq, m);
+ splx (s);
+ if (m == 0)
+ break;
+ if (m->m_len < HDHEADERLN) {
+ printf ("hdintr: packet too short (len=%d)\n",
+ m->m_len);
+ m_freem (m);
+ continue;
+ }
+ if ((m->m_flags & M_PKTHDR) == 0)
+ panic("hdintr");
+ ifp = m->m_pkthdr.rcvif;
+
+ /*
+ * look up the appropriate hdlc control block
+ */
+
+ if (ifp == lastifp)
+ hdp = lasthdp;
+ else {
+ for (hdp = hdcbhead; hdp; hdp = hdp->hd_next)
+ if (hdp->hd_ifp == ifp)
+ break;
+ if (hdp == 0) {
+ printf ("hdintr: unknown interface %x\n", ifp);
+ m_freem (m);
+ continue;
+ }
+ lastifp = ifp;
+ lasthdp = hdp;
+ }
+
+ /* Process_rxframe returns FALSE if the frame was NOT queued
+ for the next higher layers. */
+ if (process_rxframe (hdp, m) == FALSE)
+ m_freem (m);
+ }
+}
+
+process_rxframe (hdp, fbuf)
+register struct hdcb *hdp;
+register struct mbuf *fbuf;
+{
+ register int queued = FALSE, frametype, pf;
+ register struct Hdlc_frame *frame;
+
+ frame = mtod (fbuf, struct Hdlc_frame *);
+ pf = ((struct Hdlc_iframe *) frame) -> pf;
+
+ hd_trace (hdp, RX, frame);
+ if (frame -> address != ADDRESS_A && frame -> address != ADDRESS_B)
+ return (queued);
+
+ switch ((frametype = hd_decode (hdp, frame)) + hdp->hd_state) {
+ case DM + DISC_SENT:
+ case UA + DISC_SENT:
+ /*
+ * Link now closed. Leave timer running
+ * so hd_timer() can periodically check the
+ * status of interface driver flag bit IFF_UP.
+ */
+ hdp->hd_state = DISCONNECTED;
+ break;
+
+ case DM + INIT:
+ case UA + INIT:
+ /*
+ * This is a non-standard state change needed for DCEs
+ * that do dynamic link selection. We can't go into the
+ * usual "SEND DM" state because a DM is a SARM in LAP.
+ */
+ hd_writeinternal (hdp, SABM, POLLOFF);
+ hdp->hd_state = SABM_SENT;
+ SET_TIMER (hdp);
+ break;
+
+ case SABM + DM_SENT:
+ case SABM + WAIT_SABM:
+ hd_writeinternal (hdp, UA, pf);
+ case UA + SABM_SENT:
+ case UA + WAIT_UA:
+ KILL_TIMER (hdp);
+ hd_initvars (hdp);
+ hdp->hd_state = ABM;
+ hd_message (hdp, "Link level operational");
+ /* Notify the packet level - to send RESTART. */
+ (void) pk_ctlinput (PRC_LINKUP, hdp->hd_pkp);
+ break;
+
+ case SABM + SABM_SENT:
+ /* Got a SABM collision. Acknowledge the remote's SABM
+ via UA but still wait for UA. */
+ hd_writeinternal (hdp, UA, pf);
+ break;
+
+ case SABM + ABM:
+ /* Request to reset the link from the remote. */
+ KILL_TIMER (hdp);
+ hd_message (hdp, "Link reset");
+#ifdef HDLCDEBUG
+ hd_dumptrace (hdp);
+#endif
+ hd_flush (hdp->hd_ifp);
+ hd_writeinternal (hdp, UA, pf);
+ hd_initvars (hdp);
+ (void) pk_ctlinput (PRC_LINKRESET, hdp->hd_pkp);
+ hdp->hd_resets++;
+ break;
+
+ case SABM + WAIT_UA:
+ hd_writeinternal (hdp, UA, pf);
+ break;
+
+ case DM + ABM:
+ hd_message (hdp, "DM received: link down");
+#ifdef HDLCDEBUG
+ hd_dumptrace (hdp);
+#endif
+ (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp);
+ hd_flush (hdp->hd_ifp);
+ case DM + DM_SENT:
+ case DM + WAIT_SABM:
+ case DM + WAIT_UA:
+ hd_writeinternal (hdp, SABM, pf);
+ hdp->hd_state = SABM_SENT;
+ SET_TIMER (hdp);
+ break;
+
+ case DISC + INIT:
+ case DISC + DM_SENT:
+ case DISC + SABM_SENT:
+ /* Note: This is a non-standard state change. */
+ hd_writeinternal (hdp, UA, pf);
+ hd_writeinternal (hdp, SABM, POLLOFF);
+ hdp->hd_state = SABM_SENT;
+ SET_TIMER (hdp);
+ break;
+
+ case DISC + WAIT_UA:
+ hd_writeinternal (hdp, DM, pf);
+ SET_TIMER (hdp);
+ hdp->hd_state = DM_SENT;
+ break;
+
+ case DISC + ABM:
+ hd_message (hdp, "DISC received: link down");
+ (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp);
+ case DISC + WAIT_SABM:
+ hd_writeinternal (hdp, UA, pf);
+ hdp->hd_state = DM_SENT;
+ SET_TIMER (hdp);
+ break;
+
+ case UA + ABM:
+ hd_message (hdp, "UA received: link down");
+ (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp);
+ case UA + WAIT_SABM:
+ hd_writeinternal (hdp, DM, pf);
+ hdp->hd_state = DM_SENT;
+ SET_TIMER (hdp);
+ break;
+
+ case FRMR + DM_SENT:
+ hd_writeinternal (hdp, SABM, pf);
+ hdp->hd_state = SABM_SENT;
+ SET_TIMER (hdp);
+ break;
+
+ case FRMR + WAIT_SABM:
+ hd_writeinternal (hdp, DM, pf);
+ hdp->hd_state = DM_SENT;
+ SET_TIMER (hdp);
+ break;
+
+ case FRMR + ABM:
+ hd_message (hdp, "FRMR received: link down");
+ (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp);
+#ifdef HDLCDEBUG
+ hd_dumptrace (hdp);
+#endif
+ hd_flush (hdp->hd_ifp);
+ hd_writeinternal (hdp, SABM, pf);
+ hdp->hd_state = WAIT_UA;
+ SET_TIMER (hdp);
+ break;
+
+ case RR + ABM:
+ case RNR + ABM:
+ case REJ + ABM:
+ process_sframe (hdp, (struct Hdlc_sframe *)frame, frametype);
+ break;
+
+ case IFRAME + ABM:
+ queued = process_iframe (hdp, fbuf, (struct Hdlc_iframe *)frame);
+ break;
+
+ case IFRAME + SABM_SENT:
+ case RR + SABM_SENT:
+ case RNR + SABM_SENT:
+ case REJ + SABM_SENT:
+ hd_writeinternal (hdp, DM, POLLON);
+ hdp->hd_state = DM_SENT;
+ SET_TIMER (hdp);
+ break;
+
+ case IFRAME + WAIT_SABM:
+ case RR + WAIT_SABM:
+ case RNR + WAIT_SABM:
+ case REJ + WAIT_SABM:
+ hd_writeinternal (hdp, FRMR, POLLOFF);
+ SET_TIMER (hdp);
+ break;
+
+ case ILLEGAL + SABM_SENT:
+ hdp->hd_unknown++;
+ hd_writeinternal (hdp, DM, POLLOFF);
+ hdp->hd_state = DM_SENT;
+ SET_TIMER (hdp);
+ break;
+
+ case ILLEGAL + ABM:
+ hd_message (hdp, "Unknown frame received: link down");
+ (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp);
+ case ILLEGAL + WAIT_SABM:
+ hdp->hd_unknown++;
+#ifdef HDLCDEBUG
+ hd_dumptrace (hdp);
+#endif
+ hd_writeinternal (hdp, FRMR, POLLOFF);
+ hdp->hd_state = WAIT_SABM;
+ SET_TIMER (hdp);
+ break;
+ }
+
+ return (queued);
+}
+
+process_iframe (hdp, fbuf, frame)
+register struct hdcb *hdp;
+struct mbuf *fbuf;
+register struct Hdlc_iframe *frame;
+{
+ register int nr = frame -> nr,
+ ns = frame -> ns,
+ pf = frame -> pf;
+ register int queued = FALSE;
+
+ /*
+ * Validate the iframe's N(R) value. It's N(R) value must be in
+ * sync with our V(S) value and our "last received nr".
+ */
+
+ if (valid_nr (hdp, nr, FALSE) == FALSE) {
+ frame_reject (hdp, Z, frame);
+ return (queued);
+ }
+
+
+ /*
+ * This section tests the IFRAME for proper sequence. That is, it's
+ * sequence number N(S) MUST be equal to V(S).
+ */
+
+ if (ns != hdp->hd_vr) {
+ hdp->hd_invalid_ns++;
+ if (pf || (hdp->hd_condition & REJ_CONDITION) == 0) {
+ hdp->hd_condition |= REJ_CONDITION;
+ /*
+ * Flush the transmit queue. This is ugly but we
+ * have no choice. A reject response must be
+ * immediately sent to the DCE. Failure to do so
+ * may result in another out of sequence iframe
+ * arriving (and thus sending another reject)
+ * before the first reject is transmitted. This
+ * will cause the DCE to receive two or more
+ * rejects back to back, which must never happen.
+ */
+ hd_flush (hdp->hd_ifp);
+ hd_writeinternal (hdp, REJ, pf);
+ }
+ return (queued);
+ }
+ hdp->hd_condition &= ~REJ_CONDITION;
+
+ /*
+ * This section finally tests the IFRAME's sequence number against
+ * the window size (K) and the sequence number of the last frame
+ * we have acknowledged. If the IFRAME is completely correct then
+ * it is queued for the packet level.
+ */
+
+ if (ns != (hdp -> hd_lasttxnr + hdp -> hd_xcp -> xc_lwsize) % MODULUS) {
+ hdp -> hd_vr = (hdp -> hd_vr + 1) % MODULUS;
+ if (pf == 1) {
+ /* Must generate a RR or RNR with final bit on. */
+ hd_writeinternal (hdp, RR, POLLON);
+ } else
+ /*
+ * Hopefully we can piggyback the RR, if not we will generate
+ * a RR when T3 timer expires.
+ */
+ if (hdp -> hd_rrtimer == 0)
+ hdp->hd_rrtimer = hd_t3;
+
+ /* Forward iframe to packet level of X.25. */
+ fbuf -> m_data += HDHEADERLN;
+ fbuf -> m_len -= HDHEADERLN;
+ fbuf -> m_pkthdr.len -= HDHEADERLN;
+ fbuf -> m_pkthdr.rcvif = (struct ifnet *)hdp -> hd_pkp;
+#ifdef BSD4_3
+ fbuf->m_act = 0; /* probably not necessary */
+#else
+ {
+ register struct mbuf *m;
+
+ for (m = fbuf; m -> m_next; m = m -> m_next)
+ m -> m_act = (struct mbuf *) 0;
+ m -> m_act = (struct mbuf *) 1;
+ }
+#endif
+ pk_input (fbuf);
+ queued = TRUE;
+ hd_start (hdp);
+ } else {
+ /*
+ * Here if the remote station has transmitted more iframes then
+ * the number which have been acknowledged plus K.
+ */
+ hdp->hd_invalid_ns++;
+ frame_reject (hdp, W, frame);
+ }
+ return (queued);
+}
+
+/*
+ * This routine is used to determine if a value (the middle parameter)
+ * is between two other values. The low value is the first parameter
+ * the high value is the last parameter. The routine checks the middle
+ * value to see if it is within the range of the first and last values.
+ * The reason we need this routine is the values are modulo some base
+ * hence a simple test for greater or less than is not sufficient.
+ */
+
+bool
+range_check (rear, value, front)
+int rear,
+ value,
+ front;
+{
+ register bool result = FALSE;
+
+ if (front > rear)
+ result = (rear <= value) && (value <= front);
+ else
+ result = (rear <= value) || (value <= front);
+
+ return (result);
+}
+
+/*
+ * This routine handles all the frame reject conditions which can
+ * arise as a result of secondary processing. The frame reject
+ * condition Y (frame length error) are handled elsewhere.
+ */
+
+static
+frame_reject (hdp, rejectcode, frame)
+struct hdcb *hdp;
+struct Hdlc_iframe *frame;
+{
+ register struct Frmr_frame *frmr = &hd_frmr;
+
+ frmr -> frmr_control = ((struct Hdlc_frame *) frame) -> control;
+
+ frmr -> frmr_ns = frame -> ns;
+ frmr -> frmr_f1_0 = 0;
+ frmr -> frmr_nr = frame -> nr;
+ frmr -> frmr_f2_0 = 0;
+
+ frmr -> frmr_0000 = 0;
+ frmr -> frmr_w = frmr -> frmr_x = frmr -> frmr_y =
+ frmr -> frmr_z = 0;
+ switch (rejectcode) {
+ case Z:
+ frmr -> frmr_z = 1;/* invalid N(R). */
+ break;
+
+ case Y:
+ frmr -> frmr_y = 1;/* iframe length error. */
+ break;
+
+ case X:
+ frmr -> frmr_x = 1;/* invalid information field. */
+ frmr -> frmr_w = 1;
+ break;
+
+ case W:
+ frmr -> frmr_w = 1;/* invalid N(S). */
+ }
+
+ hd_writeinternal (hdp, FRMR, POLLOFF);
+
+ hdp->hd_state = WAIT_SABM;
+ SET_TIMER (hdp);
+}
+
+/*
+ * This procedure is invoked when ever we receive a supervisor
+ * frame such as RR, RNR and REJ. All processing for these
+ * frames is done here.
+ */
+
+process_sframe (hdp, frame, frametype)
+register struct hdcb *hdp;
+register struct Hdlc_sframe *frame;
+int frametype;
+{
+ register int nr = frame -> nr, pf = frame -> pf, pollbit = 0;
+
+ if (valid_nr (hdp, nr, pf) == TRUE) {
+ switch (frametype) {
+ case RR:
+ hdp->hd_condition &= ~REMOTE_RNR_CONDITION;
+ break;
+
+ case RNR:
+ hdp->hd_condition |= REMOTE_RNR_CONDITION;
+ hdp->hd_retxcnt = 0;
+ break;
+
+ case REJ:
+ hdp->hd_condition &= ~REMOTE_RNR_CONDITION;
+ rej_routine (hdp, nr);
+ }
+
+ if (pf == 1) {
+ hdp->hd_retxcnt = 0;
+ hdp->hd_condition &= ~TIMER_RECOVERY_CONDITION;
+
+ if (frametype == RR && hdp->hd_lastrxnr == hdp->hd_vs
+ && hdp->hd_timer == 0 && hdp->hd_txq.head == 0)
+ hd_writeinternal(hdp, RR, pf);
+ else
+ /* If any iframes have been queued because of the
+ timer condition, transmit then now. */
+ if (hdp->hd_condition & REMOTE_RNR_CONDITION) {
+ /* Remote is busy or timer condition, so only
+ send one. */
+ if (hdp->hd_vs != hdp->hd_retxqi)
+ hd_send_iframe (hdp, hdp->hd_retxq[hdp->hd_vs], pollbit);
+ }
+ else /* Flush the retransmit list first. */
+ while (hdp->hd_vs != hdp->hd_retxqi)
+ hd_send_iframe (hdp, hdp->hd_retxq[hdp->hd_vs], POLLOFF);
+ }
+
+ hd_start (hdp);
+ } else
+ frame_reject (hdp, Z, (struct Hdlc_iframe *)frame); /* Invalid N(R). */
+}
+
+/*
+ * This routine tests the validity of the N(R) which we have received.
+ * If it is ok, then all the iframes which it acknowledges (if any)
+ * will be freed.
+ */
+
+bool
+valid_nr (hdp, nr, finalbit)
+register struct hdcb *hdp;
+register int finalbit;
+{
+ /* Make sure it really does acknowledge something. */
+ if (hdp->hd_lastrxnr == nr)
+ return (TRUE);
+
+ /*
+ * This section validates the frame's N(R) value. It's N(R) value
+ * must be in syncronization with our V(S) value and our "last
+ * received nr" variable. If it is correct then we are able to send
+ * more IFRAME's, else frame reject condition is entered.
+ */
+
+ if (range_check (hdp->hd_lastrxnr, nr, hdp->hd_vs) == FALSE) {
+ if ((hdp->hd_condition & TIMER_RECOVERY_CONDITION) &&
+ range_check (hdp->hd_vs, nr, hdp->hd_xx) == TRUE)
+ hdp->hd_vs = nr;
+
+ else {
+ hdp->hd_invalid_nr++;
+ return (FALSE);
+ }
+ }
+
+ /*
+ * If we get to here, we do have a valid frame but it might be out
+ * of sequence. However, we should still accept the receive state
+ * number N(R) since it has already passed our previous test and it
+ * does acknowledge frames which we are sending.
+ */
+
+ KILL_TIMER (hdp);
+ free_iframes (hdp, &nr, finalbit);/* Free all acknowledged iframes */
+ if (nr != hdp->hd_vs)
+ SET_TIMER (hdp);
+
+ return (TRUE);
+}
+
+/*
+ * This routine determines how many iframes need to be retransmitted.
+ * It then resets the Send State Variable V(S) to accomplish this.
+ */
+
+static
+rej_routine (hdp, rejnr)
+register struct hdcb *hdp;
+register int rejnr;
+{
+ register int anchor;
+
+ /*
+ * Flush the output queue. Any iframes queued for
+ * transmission will be out of sequence.
+ */
+
+ hd_flush (hdp->hd_ifp);
+
+ /*
+ * Determine how many frames should be re-transmitted. In the case
+ * of a normal REJ this should be 1 to K. In the case of a timer
+ * recovery REJ (ie. a REJ with the Final Bit on) this could be 0.
+ */
+
+ anchor = hdp->hd_vs;
+ if (hdp->hd_condition & TIMER_RECOVERY_CONDITION)
+ anchor = hdp->hd_xx;
+
+ anchor = (anchor - rejnr + 8) % MODULUS;
+
+ if (anchor > 0) {
+
+ /* There is at least one iframe to retransmit. */
+ KILL_TIMER (hdp);
+ hdp->hd_vs = rejnr;
+
+ while (hdp->hd_vs != hdp->hd_retxqi)
+ hd_send_iframe (hdp, hdp->hd_retxq[hdp->hd_vs], POLLOFF);
+
+ }
+ hd_start (hdp);
+}
+
+/*
+ * This routine frees iframes from the retransmit queue. It is called
+ * when a previously written iframe is acknowledged.
+ */
+
+static
+free_iframes (hdp, nr, finalbit)
+register struct hdcb *hdp;
+int *nr;
+register int finalbit;
+
+{
+ register int i, k;
+
+ /*
+ * We need to do the following because of a funny quirk in the
+ * protocol. This case occures when in Timer recovery condition
+ * we get a N(R) which acknowledges all the outstanding iframes
+ * but with the Final Bit off. In this case we need to save the last
+ * iframe for possible retransmission even though it has already been
+ * acknowledged!
+ */
+
+ if ((hdp->hd_condition & TIMER_RECOVERY_CONDITION) && *nr == hdp->hd_xx && finalbit == 0) {
+ *nr = (*nr - 1 + 8) % MODULUS;
+/* printf ("QUIRK\n"); */
+ }
+
+ k = (*nr - hdp->hd_lastrxnr + 8) % MODULUS;
+
+ /* Loop here freeing all acknowledged iframes. */
+ for (i = 0; i < k; ++i) {
+ m_freem (hdp->hd_retxq[hdp->hd_lastrxnr]);
+ hdp->hd_retxq[hdp->hd_lastrxnr] = 0;
+ hdp->hd_lastrxnr = (hdp->hd_lastrxnr + 1) % MODULUS;
+ }
+
+}
diff --git a/sys/netccitt/hd_output.c b/sys/netccitt/hd_output.c
new file mode 100644
index 0000000..05992e1
--- /dev/null
+++ b/sys/netccitt/hd_output.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Laboratory for Computation Vision and the Computer Science Department
+ * of the University of British Columbia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)hd_output.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+
+#include <netccitt/hdlc.h>
+#include <netccitt/hd_var.h>
+#include <netccitt/x25.h>
+
+/*
+ * HDLC OUTPUT INTERFACE
+ *
+ * This routine is called when the X.25 packet layer output routine
+ * has a information frame (iframe) to write. It is also called
+ * by the input and control routines of the HDLC layer.
+ */
+
+hd_output (hdp, m0)
+register struct hdcb *hdp;
+struct mbuf *m0;
+{
+ struct x25config *xcp;
+ register struct mbuf *m = m0;
+ int len;
+
+ if (m == NULL)
+ panic ("hd_output");
+ if ((m->m_flags & M_PKTHDR) == 0)
+ panic ("hd_output 2");
+
+ if (hdp->hd_state != ABM) {
+ m_freem (m);
+ return;
+ }
+
+ /*
+ * Make room for the hdlc header either by prepending
+ * another mbuf, or by adjusting the offset and length
+ * of the first mbuf in the mbuf chain.
+ */
+
+ M_PREPEND(m, HDHEADERLN, M_DONTWAIT);
+ if (m == NULL)
+ return;
+ for (len = 0; m; m = m->m_next)
+ len += m->m_len;
+ m = m0;
+ m->m_pkthdr.len = len;
+
+ hd_append (&hdp->hd_txq, m);
+ hd_start (hdp);
+}
+
+hd_start (hdp)
+register struct hdcb *hdp;
+{
+ register struct mbuf *m;
+
+ /*
+ * The iframe is only transmitted if all these conditions are FALSE.
+ * The iframe remains queued (hdp->hd_txq) however and will be
+ * transmitted as soon as these conditions are cleared.
+ */
+
+ while (!(hdp->hd_condition & (TIMER_RECOVERY_CONDITION | REMOTE_RNR_CONDITION | REJ_CONDITION))) {
+ if (hdp->hd_vs == (hdp->hd_lastrxnr + hdp->hd_xcp->xc_lwsize) % MODULUS) {
+
+ /* We have now exceeded the maximum number of
+ outstanding iframes. Therefore, we must wait
+ until at least one is acknowledged if this
+ condition is not turned off before we are
+ requested to write another iframe. */
+ hdp->hd_window_condition++;
+ break;
+ }
+
+ /* hd_remove top iframe from transmit queue. */
+ if ((m = hd_remove (&hdp->hd_txq)) == NULL)
+ break;
+
+ hd_send_iframe (hdp, m, POLLOFF);
+ }
+}
+
+/*
+ * This procedure is passed a buffer descriptor for an iframe. It builds
+ * the rest of the control part of the frame and then writes it out. It
+ * also starts the acknowledgement timer and keeps the iframe in the
+ * Retransmit queue (Retxq) just in case we have to do this again.
+ *
+ * Note: This routine is also called from hd_input.c when retransmission
+ * of old frames is required.
+ */
+
+hd_send_iframe (hdp, buf, poll_bit)
+register struct hdcb *hdp;
+register struct mbuf *buf;
+int poll_bit;
+{
+ register struct Hdlc_iframe *iframe;
+ struct mbuf *m;
+
+ KILL_TIMER (hdp);
+
+ if (buf == 0) {
+ printf ("hd_send_iframe: zero arg\n");
+#ifdef HDLCDEBUG
+ hd_status (hdp);
+ hd_dumptrace (hdp);
+#endif
+ hdp->hd_vs = (hdp->hd_vs + 7) % MODULUS;
+ return;
+ }
+ iframe = mtod (buf, struct Hdlc_iframe *);
+
+ iframe -> hdlc_0 = 0;
+ iframe -> nr = hdp->hd_vr;
+ iframe -> pf = poll_bit;
+ iframe -> ns = hdp->hd_vs;
+ iframe -> address = ADDRESS_B;
+ hdp->hd_lasttxnr = hdp->hd_vr;
+ hdp->hd_rrtimer = 0;
+
+ if (hdp->hd_vs == hdp->hd_retxqi) {
+ /* Check for retransmissions. */
+ /* Put iframe only once in the Retransmission queue. */
+ hdp->hd_retxq[hdp->hd_retxqi] = buf;
+ hdp->hd_retxqi = (hdp->hd_retxqi + 1) % MODULUS;
+ hdp->hd_iframes_out++;
+ }
+
+ hdp->hd_vs = (hdp->hd_vs + 1) % MODULUS;
+
+ hd_trace (hdp, TX, (struct Hdlc_frame *)iframe);
+
+ /* Write buffer on device. */
+ m = hdp->hd_dontcopy ? buf : m_copy(buf, 0, (int)M_COPYALL);
+ if (m == 0) {
+ printf("hdlc: out of mbufs\n");
+ return;
+ }
+ (*hdp->hd_output)(hdp, m);
+ SET_TIMER (hdp);
+}
+
+hd_ifoutput(hdp, m)
+register struct mbuf *m;
+register struct hdcb *hdp;
+{
+ /*
+ * Queue message on interface, and start output if interface
+ * not yet active.
+ */
+ register struct ifnet *ifp = hdp->hd_ifp;
+ int s = splimp();
+
+ if (IF_QFULL(&ifp->if_snd)) {
+ IF_DROP(&ifp->if_snd);
+ /* printf("%s%d: HDLC says OK to send but queue full, may hang\n",
+ ifp->if_name, ifp->if_unit);*/
+ m_freem(m);
+ } else {
+ IF_ENQUEUE(&ifp->if_snd, m);
+ if ((ifp->if_flags & IFF_OACTIVE) == 0)
+ (*ifp->if_start)(ifp);
+ }
+ splx(s);
+}
+
+
+/*
+ * This routine gets control when the timer expires because we have not
+ * received an acknowledgement for a iframe.
+ */
+
+hd_resend_iframe (hdp)
+register struct hdcb *hdp;
+{
+
+ if (hdp->hd_retxcnt++ < hd_n2) {
+ if (!(hdp->hd_condition & TIMER_RECOVERY_CONDITION)) {
+ hdp->hd_xx = hdp->hd_vs;
+ hdp->hd_condition |= TIMER_RECOVERY_CONDITION;
+ }
+
+ hdp->hd_vs = hdp->hd_lastrxnr;
+ hd_send_iframe (hdp, hdp->hd_retxq[hdp->hd_vs], POLLON);
+ } else {
+ /* At this point we have not received a RR even after N2
+ retries - attempt to reset link. */
+
+ hd_initvars (hdp);
+ hd_writeinternal (hdp, SABM, POLLOFF);
+ hdp->hd_state = WAIT_UA;
+ SET_TIMER (hdp);
+ hd_message (hdp, "Timer recovery failed: link down");
+ (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp);
+ }
+}
diff --git a/sys/netccitt/hd_subr.c b/sys/netccitt/hd_subr.c
new file mode 100644
index 0000000..c75ab07
--- /dev/null
+++ b/sys/netccitt/hd_subr.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Laboratory for Computation Vision and the Computer Science Department
+ * of the University of British Columbia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)hd_subr.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+
+#include <netccitt/hdlc.h>
+#include <netccitt/hd_var.h>
+#include <netccitt/x25.h>
+#include <netccitt/pk_var.h>
+
+hd_init ()
+{
+
+ hdintrq.ifq_maxlen = IFQ_MAXLEN;
+}
+
+hd_ctlinput (prc, addr)
+struct sockaddr *addr;
+{
+ register struct x25config *xcp = (struct x25config *)addr;
+ register struct hdcb *hdp;
+ register struct ifaddr *ifa;
+ struct ifnet *ifp;
+ caddr_t pk_newlink();
+
+ if (addr->sa_family != AF_CCITT)
+ return (EAFNOSUPPORT);
+ if (xcp->xc_lptype != HDLCPROTO_LAPB)
+ return (EPROTONOSUPPORT);
+ ifa = ifa_ifwithaddr(addr);
+ if (ifa == 0 || ifa->ifa_addr->sa_family != AF_CCITT ||
+ (ifp = ifa->ifa_ifp) == 0)
+ panic ("hd_ctlinput");
+ for (hdp = hdcbhead; hdp; hdp = hdp->hd_next)
+ if (hdp->hd_ifp == ifp)
+ break;
+
+ if (hdp == 0) { /* new interface */
+ int error, hd_ifoutput(), hd_output();
+
+ /* an hdcb is now too big to fit in an mbuf */
+ MALLOC(hdp, struct hdcb *, sizeof (*hdp), M_PCB, M_DONTWAIT);
+ if (hdp == 0)
+ return (ENOBUFS);
+ bzero((caddr_t)hdp, sizeof(*hdp));
+ hdp->hd_pkp =
+ (caddr_t) pk_newlink ((struct x25_ifaddr *) ifa,
+ (caddr_t) hdp);
+ ((struct x25_ifaddr *)ifa)->ia_pkcb =
+ (struct pkcb *) hdp->hd_pkp;
+ if (hdp -> hd_pkp == 0) {
+ free(hdp, M_PCB);
+ return (ENOBUFS);
+ }
+ hdp->hd_ifp = ifp;
+ hdp->hd_ifa = ifa;
+ hdp->hd_xcp = xcp;
+ hdp->hd_state = INIT;
+ hdp->hd_output = hd_ifoutput;
+ hdp->hd_next = hdcbhead;
+ hdcbhead = hdp;
+ } else if (hdp->hd_pkp == 0) { /* interface got reconfigured */
+ hdp->hd_pkp =
+ (caddr_t) pk_newlink ((struct x25_ifaddr *) ifa,
+ (caddr_t) hdp);
+ ((struct x25_ifaddr *)ifa)->ia_pkcb =
+ (struct pkcb *) hdp->hd_pkp;
+ if (hdp -> hd_pkp == 0) {
+ free(hdp, M_PCB);
+ return (ENOBUFS);
+ }
+ }
+
+ switch (prc) {
+ case PRC_IFUP:
+ if (xcp->xc_lwsize == 0 ||
+ xcp->xc_lwsize > MAX_WINDOW_SIZE)
+ xcp->xc_lwsize = MAX_WINDOW_SIZE;
+ if (hdp->hd_state == INIT)
+ SET_TIMER (hdp);
+ break;
+
+ case PRC_IFDOWN:
+ if (hdp->hd_state == ABM)
+ hd_message (hdp, "Operator shutdown: link closed");
+ (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp);
+
+ /* fall thru to ... */
+
+ case PRC_DISCONNECT_REQUEST:
+ /* drop reference to pkcb --- it's dead meat */
+ hdp->hd_pkp = (caddr_t) 0;
+ ((struct x25_ifaddr *)ifa)->ia_pkcb = (struct pkcb *) 0;
+
+ hd_writeinternal (hdp, DISC, POLLON);
+ hdp->hd_state = DISC_SENT;
+ SET_TIMER (hdp);
+ }
+ return (0);
+}
+
+hd_initvars (hdp)
+register struct hdcb *hdp;
+{
+ register struct mbuf *m;
+ register int i;
+
+ /* Clear Transmit queue. */
+ while ((m = hd_remove (&hdp->hd_txq)) != NULL)
+ m_freem (m);
+
+ /* Clear Retransmit queue. */
+ i = hdp->hd_lastrxnr;
+ while (i != hdp->hd_retxqi) {
+ m_freem (hdp->hd_retxq[i]);
+ i = (i + 1) % MODULUS;
+ }
+ hdp->hd_retxqi = 0;
+
+ hdp->hd_vs = hdp->hd_vr = 0;
+ hdp->hd_lasttxnr = hdp->hd_lastrxnr = 0;
+ hdp->hd_rrtimer = 0;
+ KILL_TIMER(hdp);
+ hdp->hd_retxcnt = 0;
+ hdp->hd_condition = 0;
+}
+
+hd_decode (hdp, frame)
+register struct hdcb *hdp;
+struct Hdlc_frame *frame;
+{
+ register int frametype = ILLEGAL;
+ register struct Hdlc_iframe *iframe = (struct Hdlc_iframe *) frame;
+ register struct Hdlc_sframe *sframe = (struct Hdlc_sframe *) frame;
+ register struct Hdlc_uframe *uframe = (struct Hdlc_uframe *) frame;
+
+ if (iframe -> hdlc_0 == 0) {
+ frametype = IFRAME;
+ hdp->hd_iframes_in++;
+ }
+
+ else if (sframe -> hdlc_01 == 1) {
+ /* Supervisory format. */
+ switch (sframe -> s2) {
+ case 0:
+ frametype = RR;
+ hdp->hd_rrs_in++;
+ break;
+
+ case 1:
+ frametype = RNR;
+ hdp->hd_rnrs_in++;
+ break;
+
+ case 2:
+ frametype = REJ;
+ hdp->hd_rejs_in++;
+ }
+ }
+ else if (uframe -> hdlc_11 == 3) {
+ /* Unnumbered format. */
+ switch (uframe -> m3) {
+ case 0:
+ frametype = DM;
+ break;
+
+ case 1:
+ frametype = SABM;
+ break;
+
+ case 2:
+ frametype = DISC;
+ break;
+
+ case 3:
+ frametype = UA;
+ break;
+
+ case 4:
+ frametype = FRMR;
+ hdp->hd_frmrs_in++;
+ }
+ }
+ return (frametype);
+}
+
+/*
+ * This routine is called when the HDLC layer internally generates a
+ * command or response for the remote machine ( eg. RR, UA etc. ).
+ * Only supervisory or unnumbered frames are processed.
+ */
+
+hd_writeinternal (hdp, frametype, pf)
+register struct hdcb *hdp;
+register int frametype, pf;
+{
+ register struct mbuf *buf;
+ struct Hdlc_frame *frame;
+ register struct Hdlc_sframe *sframe;
+ register struct Hdlc_uframe *uframe;
+
+ MGETHDR (buf, M_DONTWAIT, MT_HEADER);
+ if (buf == 0)
+ return;
+ frame = mtod (buf, struct Hdlc_frame *);
+ sframe = mtod (buf, struct Hdlc_sframe *);
+ uframe = mtod (buf, struct Hdlc_uframe *);
+
+ /* Assume a response - address structure for DTE */
+ frame -> address = ADDRESS_A;
+ buf -> m_len = 2;
+ buf -> m_act = buf -> m_next = NULL;
+
+ switch (frametype) {
+ case RR:
+ frame -> control = RR_CONTROL;
+ hdp->hd_rrs_out++;
+ break;
+
+ case RNR:
+ frame -> control = RNR_CONTROL;
+ hdp->hd_rnrs_out++;
+ break;
+
+ case REJ:
+ frame -> control = REJ_CONTROL;
+ hdp->hd_rejs_out++;
+ break;
+
+ case SABM:
+ frame -> control = SABM_CONTROL;
+ frame -> address = ADDRESS_B;
+ break;
+
+ case DISC:
+ if ((hdp->hd_ifp->if_flags & IFF_UP) == 0) {
+ hdp->hd_state = DISCONNECTED;
+ (void) m_freem (buf);
+ hd_flush (hdp->hd_ifp);
+ return;
+ }
+ frame -> control = DISC_CONTROL;
+ frame -> address = ADDRESS_B;
+ break;
+
+ case DM:
+ frame -> control = DM_CONTROL;
+ break;
+
+ case UA:
+ frame -> control = UA_CONTROL;
+ break;
+
+ case FRMR:
+ frame -> control = FRMR_CONTROL;
+ bcopy ((caddr_t)&hd_frmr, (caddr_t)frame -> info, 3);
+ buf -> m_len = 5;
+ hdp->hd_frmrs_out++;
+
+ }
+
+ if (sframe -> hdlc_01 == 1) {
+ /* Supervisory format - RR, REJ, or RNR. */
+ sframe -> nr = hdp->hd_vr;
+ sframe -> pf = pf;
+ hdp->hd_lasttxnr = hdp->hd_vr;
+ hdp->hd_rrtimer = 0;
+ }
+ else
+ uframe -> pf = pf;
+
+ hd_trace (hdp, TX, frame);
+ buf -> m_pkthdr.len = buf -> m_len;
+ (*hdp->hd_output) (hdp, buf);
+}
+
+struct mbuf *
+hd_remove (q)
+struct hdtxq *q;
+{
+ register struct mbuf *m;
+
+ m = q -> head;
+ if (m) {
+ if ((q -> head = m -> m_act) == NULL)
+ q -> tail = NULL;
+ m -> m_act = 0;
+ }
+ return (m);
+}
+
+hd_append (q, m)
+register struct hdtxq *q;
+register struct mbuf *m;
+{
+
+ m -> m_act = NULL;
+ if (q -> tail == NULL)
+ q -> head = m;
+ else
+ q -> tail -> m_act = m;
+ q -> tail = m;
+}
+
+hd_flush (ifp)
+struct ifnet *ifp;
+{
+ register struct mbuf *m;
+ register int s;
+
+ while (1) {
+ s = splimp ();
+ IF_DEQUEUE (&ifp->if_snd, m);
+ splx (s);
+ if (m == 0)
+ break;
+ m_freem (m);
+ }
+}
+
+hd_message (hdp, msg)
+struct hdcb *hdp;
+char *msg;
+{
+ char *format_ntn ();
+
+ if (hdcbhead -> hd_next)
+ printf ("HDLC(%s): %s\n", format_ntn (hdp->hd_xcp), msg);
+ else
+ printf ("HDLC: %s\n", msg);
+}
+
+#ifdef HDLCDEBUG
+hd_status (hdp)
+struct hdcb *hdp;
+{
+ printf ("HDLC STATUS:\n V(S)=%d, V(R)=%d, retxqi=%d,\n",
+ hdp->hd_vs, hdp->hd_vr, hdp->hd_retxqi);
+
+ printf ("Last_rx_nr=%d, Last_tx_nr=%d,\n Condition=%d, Xx=%d\n",
+ hdp->hd_lastrxnr, hdp->hd_lasttxnr, hdp->hd_condition, hdp->hd_xx);
+}
+#endif
diff --git a/sys/netccitt/hd_timer.c b/sys/netccitt/hd_timer.c
new file mode 100644
index 0000000..a3bf12a
--- /dev/null
+++ b/sys/netccitt/hd_timer.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Laboratory for Computation Vision and the Computer Science Department
+ * of the University of British Columbia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)hd_timer.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+
+#include <netccitt/hdlc.h>
+#include <netccitt/hd_var.h>
+#include <netccitt/x25.h>
+
+/*
+ * these can be patched with adb if the
+ * default values are inappropriate
+ */
+
+int hd_t1 = T1;
+int hd_t3 = T3;
+int hd_n2 = N2;
+
+/*
+ * HDLC TIMER
+ *
+ * This routine is called every 500ms by the kernel. Decrement timer by this
+ * amount - if expired then process the event.
+ */
+
+hd_timer ()
+{
+ register struct hdcb *hdp;
+ register int s = splimp ();
+
+ for (hdp = hdcbhead; hdp; hdp = hdp->hd_next) {
+ if (hdp->hd_rrtimer && (--hdp->hd_rrtimer == 0)) {
+ if (hdp->hd_lasttxnr != hdp->hd_vr)
+ hd_writeinternal (hdp, RR, POLLOFF);
+ }
+
+ if (!(hdp->hd_timer && --hdp->hd_timer == 0))
+ continue;
+
+ switch (hdp->hd_state) {
+ case INIT:
+ case DISC_SENT:
+ hd_writeinternal (hdp, DISC, POLLON);
+ break;
+
+ case ABM:
+ if (hdp->hd_lastrxnr != hdp->hd_vs) { /* XXX */
+ hdp->hd_timeouts++;
+ hd_resend_iframe (hdp);
+ }
+ break;
+
+ case WAIT_SABM:
+ hd_writeinternal (hdp, FRMR, POLLOFF);
+ if (++hdp->hd_retxcnt == hd_n2) {
+ hdp->hd_retxcnt = 0;
+ hd_writeinternal (hdp, SABM, POLLOFF);
+ hdp->hd_state = WAIT_UA;
+ }
+ break;
+
+ case DM_SENT:
+ if (++hdp->hd_retxcnt == hd_n2) {
+ /* Notify the packet level. */
+ (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp);
+ hdp->hd_retxcnt = 0;
+ hdp->hd_state = SABM_SENT;
+ hd_writeinternal (hdp, SABM, POLLOFF);
+ } else
+ hd_writeinternal (hdp, DM, POLLOFF);
+ break;
+
+ case WAIT_UA:
+ if (++hdp->hd_retxcnt == hd_n2) {
+ hdp->hd_retxcnt = 0;
+ hd_writeinternal (hdp, DM, POLLOFF);
+ hdp->hd_state = DM_SENT;
+ } else
+ hd_writeinternal (hdp, SABM, POLLOFF);
+ break;
+
+ case SABM_SENT:
+ /* Do this indefinitely. */
+ hd_writeinternal (hdp, SABM, POLLON);
+ break;
+
+ case DISCONNECTED:
+ /*
+ * Poll the interface driver flags waiting
+ * for the IFF_UP bit to come on.
+ */
+ if (hdp->hd_ifp->if_flags & IFF_UP)
+ hdp->hd_state = INIT;
+
+ }
+ SET_TIMER (hdp);
+ }
+
+ splx (s);
+}
diff --git a/sys/netccitt/hd_var.h b/sys/netccitt/hd_var.h
new file mode 100644
index 0000000..5fefe08
--- /dev/null
+++ b/sys/netccitt/hd_var.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Laboratory for Computation Vision and the Computer Science Department
+ * of the University of British Columbia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)hd_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ *
+ * hdlc control block
+ *
+ */
+
+struct hdtxq {
+ struct mbuf *head;
+ struct mbuf *tail;
+};
+
+struct hdcb {
+ struct hdcb *hd_next; /* pointer to next hdlc control block */
+ char hd_state; /* link state */
+ char hd_vs; /* send state variable */
+ char hd_vr; /* receive state variable */
+ char hd_lastrxnr; /* last received N(R) */
+ char hd_lasttxnr; /* last transmitted N(R) */
+ char hd_condition;
+#define TIMER_RECOVERY_CONDITION 0x01
+#define REJ_CONDITION 0x02
+#define REMOTE_RNR_CONDITION 0X04
+ char hd_retxcnt;
+ char hd_xx;
+ struct hdtxq hd_txq;
+ struct mbuf *hd_retxq[MODULUS];
+ char hd_retxqi;
+ char hd_rrtimer;
+ char hd_timer;
+#define SET_TIMER(hdp) hdp->hd_timer = hd_t1
+#define KILL_TIMER(hdp) hdp->hd_timer = 0
+ char hd_dontcopy; /* if-driver doesn't free I-frames */
+ struct ifnet *hd_ifp; /* device's network visible interface */
+ struct ifaddr *hd_ifa; /* device's X.25 network address */
+ struct x25config *hd_xcp;
+ caddr_t hd_pkp; /* Level III junk */
+ int (*hd_output)(); /* separate entry for HDLC direct output */
+
+ /* link statistics */
+
+ long hd_iframes_in;
+ long hd_iframes_out;
+ long hd_rrs_in;
+ long hd_rrs_out;
+ short hd_rejs_in;
+ short hd_rejs_out;
+ long hd_window_condition;
+ short hd_invalid_ns;
+ short hd_invalid_nr;
+ short hd_timeouts;
+ short hd_resets;
+ short hd_unknown;
+ short hd_frmrs_in;
+ short hd_frmrs_out;
+ short hd_rnrs_in;
+ short hd_rnrs_out;
+};
+
+#ifdef KERNEL
+struct hdcb *hdcbhead; /* head of linked list of hdcb's */
+struct Frmr_frame hd_frmr; /* rejected frame diagnostic info */
+struct ifqueue hdintrq; /* hdlc packet input queue */
+
+int hd_t1; /* timer T1 value */
+int hd_t3; /* RR send timer */
+int hd_n2; /* frame retransmission limit */
+#endif
diff --git a/sys/netccitt/hdlc.h b/sys/netccitt/hdlc.h
new file mode 100644
index 0000000..60cf7ad
--- /dev/null
+++ b/sys/netccitt/hdlc.h
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by the
+ * Laboratory for Computation Vision and the Computer Science Department
+ * of the University of British Columbia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)hdlc.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef ORDER4
+#define FALSE 0
+#define TRUE 1
+typedef u_char octet;
+typedef char bool;
+
+/*
+ * HDLC Packet format definitions
+ * This will eventually have to be rewritten without reference
+ * to bit fields, to be compliant with ANSI C and alignment safe.
+ */
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define ORDER4(a, b, c, d) a , b , c , d
+#define ORDER5(a, b, c, d, e) a , b , c , d , e
+#endif
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define ORDER4(a, b, c, d) d , c , b , a
+#define ORDER5(a, b, c, d, e) e , d , c , b , a
+#endif
+#endif
+
+#define MAX_INFO_LEN 4096+3+4
+#define ADDRESS_A 3 /* B'00000011' */
+#define ADDRESS_B 1 /* B'00000001' */
+
+struct Hdlc_iframe {
+ octet address;
+ octet ORDER4(nr:3, pf:1, ns:3, hdlc_0:1);
+ octet i_field[MAX_INFO_LEN];
+};
+
+struct Hdlc_sframe {
+ octet address;
+ octet ORDER4(nr:3, pf:1, s2:2, hdlc_01:2);
+};
+
+struct Hdlc_uframe {
+ octet address;
+ octet ORDER4(m3:3, pf:1, m2:2, hdlc_11:2);
+};
+
+struct Frmr_frame {
+ octet address;
+ octet control;
+ octet frmr_control;
+ octet ORDER4(frmr_nr:3, frmr_f1_0:1, frmr_ns:3, frmr_f2_0:1);
+ octet ORDER5(frmr_0000:4, frmr_z:1, frmr_y:1, frmr_x:1, frmr_w:1);
+};
+
+#define HDHEADERLN 2
+#define MINFRLN 2 /* Minimum frame length. */
+
+struct Hdlc_frame {
+ octet address;
+ octet control;
+ octet info[3]; /* min for FRMR */
+};
+
+#define SABM_CONTROL 057 /* B'00101111' */
+#define UA_CONTROL 0143 /* B'01100011' */
+#define DISC_CONTROL 0103 /* B'01000011' */
+#define DM_CONTROL 017 /* B'00001111' */
+#define FRMR_CONTROL 0207 /* B'10000111' */
+#define RR_CONTROL 01 /* B'00000001' */
+#define RNR_CONTROL 05 /* B'00000101' */
+#define REJ_CONTROL 011 /* B'00001001' */
+
+#define POLLOFF 0
+#define POLLON 1
+
+/* Define Link State constants. */
+
+#define INIT 0
+#define DM_SENT 1
+#define SABM_SENT 2
+#define ABM 3
+#define WAIT_SABM 4
+#define WAIT_UA 5
+#define DISC_SENT 6
+#define DISCONNECTED 7
+#define MAXSTATE 8
+
+/* The following constants are used in a switch statement to process
+ frames read from the communications line. */
+
+#define SABM 0 * MAXSTATE
+#define DM 1 * MAXSTATE
+#define DISC 2 * MAXSTATE
+#define UA 3 * MAXSTATE
+#define FRMR 4 * MAXSTATE
+#define RR 5 * MAXSTATE
+#define RNR 6 * MAXSTATE
+#define REJ 7 * MAXSTATE
+#define IFRAME 8 * MAXSTATE
+#define ILLEGAL 9 * MAXSTATE
+
+#define T1 (3 * PR_SLOWHZ) /* IFRAME TIMEOUT - 3 seconds */
+#define T3 (T1 / 2) /* RR generate timeout - 1.5 seconds */
+#define N2 10
+#define MODULUS 8
+#define MAX_WINDOW_SIZE 7
+
+#define Z 0
+#define Y 1
+#define X 2
+#define W 3
+#define A 4
+
+#define TX 0
+#define RX 1
+
+bool range_check ();
+bool valid_nr ();
+struct mbuf *hd_remove ();
diff --git a/sys/netccitt/if_x25subr.c b/sys/netccitt/if_x25subr.c
new file mode 100644
index 0000000..6f00496
--- /dev/null
+++ b/sys/netccitt/if_x25subr.c
@@ -0,0 +1,801 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_x25subr.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+
+#include <machine/mtpr.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/netisr.h>
+#include <net/route.h>
+
+#include <netccitt/x25.h>
+#include <netccitt/x25err.h>
+#include <netccitt/pk.h>
+#include <netccitt/pk_var.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#ifdef ISO
+int tp_incoming();
+#include <netiso/argo_debug.h>
+#include <netiso/iso.h>
+#include <netiso/iso_var.h>
+#endif
+
+extern struct ifnet loif;
+struct llinfo_x25 llinfo_x25 = {&llinfo_x25, &llinfo_x25};
+#ifndef _offsetof
+#define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m))
+#endif
+struct sockaddr *x25_dgram_sockmask;
+struct sockaddr_x25 x25_dgmask = {
+ _offsetof(struct sockaddr_x25, x25_udata[1]), /* _len */
+ 0, /* _family */
+ 0, /* _net */
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* _addr */
+ {0}, /* opts */
+ -1, /* _udlen */
+ {-1} /* _udata */
+};
+
+struct if_x25stats {
+ int ifx_wrongplen;
+ int ifx_nophdr;
+} if_x25stats;
+int x25_autoconnect = 0;
+
+#define senderr(x) {error = x; goto bad;}
+/*
+ * Ancillary routines
+ */
+static struct llinfo_x25 *
+x25_lxalloc(rt)
+register struct rtentry *rt;
+{
+ register struct llinfo_x25 *lx;
+ register struct sockaddr *dst = rt_key(rt);
+ register struct ifaddr *ifa;
+
+ MALLOC(lx, struct llinfo_x25 *, sizeof (*lx), M_PCB, M_NOWAIT);
+ if (lx == 0)
+ return lx;
+ Bzero(lx, sizeof(*lx));
+ lx->lx_rt = rt;
+ lx->lx_family = dst->sa_family;
+ rt->rt_refcnt++;
+ if (rt->rt_llinfo)
+ insque(lx, (struct llinfo_x25 *)rt->rt_llinfo);
+ else {
+ rt->rt_llinfo = (caddr_t)lx;
+ insque(lx, &llinfo_x25);
+ }
+ for (ifa = rt->rt_ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family == AF_CCITT)
+ lx->lx_ia = (struct x25_ifaddr *)ifa;
+ }
+ return lx;
+}
+x25_lxfree(lx)
+register struct llinfo_x25 *lx;
+{
+ register struct rtentry *rt = lx->lx_rt;
+ register struct pklcd *lcp = lx->lx_lcd;
+
+ if (lcp) {
+ lcp->lcd_upper = 0;
+ pk_disconnect(lcp);
+ }
+ if ((rt->rt_llinfo == (caddr_t)lx) && (lx->lx_next->lx_rt == rt))
+ rt->rt_llinfo = (caddr_t)lx->lx_next;
+ else
+ rt->rt_llinfo = 0;
+ RTFREE(rt);
+ remque(lx);
+ FREE(lx, M_PCB);
+}
+/*
+ * Process a x25 packet as datagram;
+ */
+x25_ifinput(lcp, m)
+struct pklcd *lcp;
+register struct mbuf *m;
+{
+ struct llinfo_x25 *lx = (struct llinfo_x25 *)lcp->lcd_upnext;
+ register struct ifnet *ifp;
+ struct ifqueue *inq;
+ extern struct timeval time;
+ int s, len, isr;
+
+ if (m == 0 || lcp->lcd_state != DATA_TRANSFER) {
+ x25_connect_callback(lcp, 0);
+ return;
+ }
+ pk_flowcontrol(lcp, 0, 1); /* Generate RR */
+ ifp = m->m_pkthdr.rcvif;
+ ifp->if_lastchange = time;
+ switch (m->m_type) {
+ default:
+ if (m)
+ m_freem(m);
+ return;
+
+ case MT_DATA:
+ /* FALLTHROUGH */;
+ }
+ switch (lx->lx_family) {
+#ifdef INET
+ case AF_INET:
+ isr = NETISR_IP;
+ inq = &ipintrq;
+ break;
+
+#endif
+#ifdef NS
+ case AF_NS:
+ isr = NETISR_NS;
+ inq = &nsintrq;
+ break;
+
+#endif
+#ifdef ISO
+ case AF_ISO:
+ isr = NETISR_ISO;
+ inq = &clnlintrq;
+ break;
+#endif
+ default:
+ m_freem(m);
+ ifp->if_noproto++;
+ return;
+ }
+ s = splimp();
+ schednetisr(isr);
+ if (IF_QFULL(inq)) {
+ IF_DROP(inq);
+ m_freem(m);
+ } else {
+ IF_ENQUEUE(inq, m);
+ ifp->if_ibytes += m->m_pkthdr.len;
+ }
+ splx(s);
+}
+x25_connect_callback(lcp, m)
+register struct pklcd *lcp;
+register struct mbuf *m;
+{
+ register struct llinfo_x25 *lx = (struct llinfo_x25 *)lcp->lcd_upnext;
+ int do_clear = 1;
+ if (m == 0)
+ goto refused;
+ if (m->m_type != MT_CONTROL) {
+ printf("x25_connect_callback: should panic\n");
+ goto refused;
+ }
+ switch (pk_decode(mtod(m, struct x25_packet *))) {
+ case CALL_ACCEPTED:
+ lcp->lcd_upper = x25_ifinput;
+ if (lcp->lcd_sb.sb_mb)
+ lcp->lcd_send(lcp); /* XXX start queued packets */
+ return;
+ default:
+ do_clear = 0;
+ refused:
+ lcp->lcd_upper = 0;
+ lx->lx_lcd = 0;
+ if (do_clear)
+ pk_disconnect(lcp);
+ return;
+ }
+}
+#define SA(p) ((struct sockaddr *)(p))
+#define RT(p) ((struct rtentry *)(p))
+
+x25_dgram_incoming(lcp, m0)
+register struct pklcd *lcp;
+struct mbuf *m0;
+{
+ register struct rtentry *rt, *nrt;
+ register struct mbuf *m = m0->m_next; /* m0 has calling sockaddr_x25 */
+ void x25_rtrequest();
+
+ rt = rtalloc1(SA(&lcp->lcd_faddr), 0);
+ if (rt == 0) {
+refuse: lcp->lcd_upper = 0;
+ pk_close(lcp);
+ return;
+ }
+ rt->rt_refcnt--;
+ if ((nrt = RT(rt->rt_llinfo)) == 0 || rt_mask(rt) != x25_dgram_sockmask)
+ goto refuse;
+ if ((nrt->rt_flags & RTF_UP) == 0) {
+ rt->rt_llinfo = (caddr_t)rtalloc1(rt->rt_gateway, 0);
+ rtfree(nrt);
+ if ((nrt = RT(rt->rt_llinfo)) == 0)
+ goto refuse;
+ nrt->rt_refcnt--;
+ }
+ if (nrt->rt_ifa == 0 || nrt->rt_ifa->ifa_rtrequest != x25_rtrequest)
+ goto refuse;
+ lcp->lcd_send(lcp); /* confirm call */
+ x25_rtattach(lcp, nrt);
+ m_freem(m);
+}
+
+/*
+ * X.25 output routine.
+ */
+x25_ifoutput(ifp, m0, dst, rt)
+struct ifnet *ifp;
+struct mbuf *m0;
+struct sockaddr *dst;
+register struct rtentry *rt;
+{
+ register struct mbuf *m = m0;
+ register struct llinfo_x25 *lx;
+ struct pklcd *lcp;
+ int s, error = 0;
+
+int plen;
+for (plen = 0; m; m = m->m_next)
+ plen += m->m_len;
+m = m0;
+
+ if ((ifp->if_flags & IFF_UP) == 0)
+ senderr(ENETDOWN);
+ while (rt == 0 || (rt->rt_flags & RTF_GATEWAY)) {
+ if (rt) {
+ if (rt->rt_llinfo) {
+ rt = (struct rtentry *)rt->rt_llinfo;
+ continue;
+ }
+ dst = rt->rt_gateway;
+ }
+ if ((rt = rtalloc1(dst, 1)) == 0)
+ senderr(EHOSTUNREACH);
+ rt->rt_refcnt--;
+ }
+ /*
+ * Sanity checks.
+ */
+ if ((rt->rt_ifp != ifp) ||
+ (rt->rt_flags & (RTF_CLONING | RTF_GATEWAY)) ||
+ ((lx = (struct llinfo_x25 *)rt->rt_llinfo) == 0)) {
+ senderr(ENETUNREACH);
+ }
+if ((m->m_flags & M_PKTHDR) == 0) {
+ if_x25stats.ifx_nophdr++;
+ m = m_gethdr(M_NOWAIT, MT_HEADER);
+ if (m == 0)
+ senderr(ENOBUFS);
+ m->m_pkthdr.len = plen;
+ m->m_next = m0;
+}
+if (plen != m->m_pkthdr.len) {
+ if_x25stats.ifx_wrongplen++;
+ m->m_pkthdr.len = plen;
+}
+next_circuit:
+ lcp = lx->lx_lcd;
+ if (lcp == 0) {
+ lx->lx_lcd = lcp = pk_attach((struct socket *)0);
+ if (lcp == 0)
+ senderr(ENOBUFS);
+ lcp->lcd_upper = x25_connect_callback;
+ lcp->lcd_upnext = (caddr_t)lx;
+ lcp->lcd_packetsize = lx->lx_ia->ia_xc.xc_psize;
+ lcp->lcd_flags = X25_MBS_HOLD;
+ }
+ switch (lcp->lcd_state) {
+ case READY:
+ if (dst->sa_family == AF_INET &&
+ ifp->if_type == IFT_X25DDN &&
+ rt->rt_gateway->sa_family != AF_CCITT)
+ x25_ddnip_to_ccitt(dst, rt);
+ if (rt->rt_gateway->sa_family != AF_CCITT) {
+ if ((rt->rt_flags & RTF_XRESOLVE) == 0)
+ senderr(EHOSTUNREACH);
+ } else if (x25_autoconnect)
+ error = pk_connect(lcp,
+ (struct sockaddr_x25 *)rt->rt_gateway);
+ if (error)
+ senderr(error);
+ /* FALLTHROUGH */
+ case SENT_CALL:
+ case DATA_TRANSFER:
+ if (sbspace(&lcp->lcd_sb) < 0) {
+ lx = lx->lx_next;
+ if (lx->lx_rt != rt)
+ senderr(ENOSPC);
+ goto next_circuit;
+ }
+ if (lx->lx_ia)
+ lcp->lcd_dg_timer =
+ lx->lx_ia->ia_xc.xc_dg_idletimo;
+ pk_send(lcp, m);
+ break;
+ default:
+ /*
+ * We count on the timer routine to close idle
+ * connections, if there are not enough circuits to go
+ * around.
+ *
+ * So throw away data for now.
+ * After we get it all working, we'll rewrite to handle
+ * actively closing connections (other than by timers),
+ * when circuits get tight.
+ *
+ * In the DDN case, the imp itself closes connections
+ * under heavy load.
+ */
+ error = ENOBUFS;
+ bad:
+ if (m)
+ m_freem(m);
+ }
+ return (error);
+}
+
+/*
+ * Simpleminded timer routine.
+ */
+x25_iftimeout(ifp)
+struct ifnet *ifp;
+{
+ register struct pkcb *pkcb = 0;
+ register struct pklcd **lcpp, *lcp;
+ int s = splimp();
+
+ FOR_ALL_PKCBS(pkcb)
+ if (pkcb->pk_ia->ia_ifp == ifp)
+ for (lcpp = pkcb->pk_chan + pkcb->pk_maxlcn;
+ --lcpp > pkcb->pk_chan;)
+ if ((lcp = *lcpp) &&
+ lcp->lcd_state == DATA_TRANSFER &&
+ (lcp->lcd_flags & X25_DG_CIRCUIT) &&
+ (lcp->lcd_dg_timer && --lcp->lcd_dg_timer == 0)) {
+ lcp->lcd_upper(lcp, 0);
+ }
+ splx(s);
+}
+/*
+ * This routine gets called when validating additions of new routes
+ * or deletions of old ones.
+ */
+x25_rtrequest(cmd, rt, dst)
+register struct rtentry *rt;
+struct sockaddr *dst;
+{
+ register struct llinfo_x25 *lx = (struct llinfo_x25 *)rt->rt_llinfo;
+ register struct sockaddr_x25 *sa =(struct sockaddr_x25 *)rt->rt_gateway;
+ register struct pklcd *lcp;
+
+ /* would put this pk_init, except routing table doesn't
+ exist yet. */
+ if (x25_dgram_sockmask == 0) {
+ struct radix_node *rn_addmask();
+ x25_dgram_sockmask =
+ SA(rn_addmask((caddr_t)&x25_dgmask, 0, 4)->rn_key);
+ }
+ if (rt->rt_flags & RTF_GATEWAY) {
+ if (rt->rt_llinfo)
+ RTFREE((struct rtentry *)rt->rt_llinfo);
+ rt->rt_llinfo = (cmd == RTM_ADD) ?
+ (caddr_t)rtalloc1(rt->rt_gateway, 1) : 0;
+ return;
+ }
+ if ((rt->rt_flags & RTF_HOST) == 0)
+ return;
+ if (cmd == RTM_DELETE) {
+ while (rt->rt_llinfo)
+ x25_lxfree((struct llinfo *)rt->rt_llinfo);
+ x25_rtinvert(RTM_DELETE, rt->rt_gateway, rt);
+ return;
+ }
+ if (lx == 0 && (lx = x25_lxalloc(rt)) == 0)
+ return;
+ if ((lcp = lx->lx_lcd) && lcp->lcd_state != READY) {
+ /*
+ * This can only happen on a RTM_CHANGE operation
+ * though cmd will be RTM_ADD.
+ */
+ if (lcp->lcd_ceaddr &&
+ Bcmp(rt->rt_gateway, lcp->lcd_ceaddr,
+ lcp->lcd_ceaddr->x25_len) != 0) {
+ x25_rtinvert(RTM_DELETE, lcp->lcd_ceaddr, rt);
+ lcp->lcd_upper = 0;
+ pk_disconnect(lcp);
+ }
+ lcp = 0;
+ }
+ x25_rtinvert(RTM_ADD, rt->rt_gateway, rt);
+}
+
+int x25_dont_rtinvert = 0;
+
+x25_rtinvert(cmd, sa, rt)
+register struct sockaddr *sa;
+register struct rtentry *rt;
+{
+ struct rtentry *rt2 = 0;
+ /*
+ * rt_gateway contains PID indicating which proto
+ * family on the other end, so will be different
+ * from general host route via X.25.
+ */
+ if (rt->rt_ifp->if_type == IFT_X25DDN || x25_dont_rtinvert)
+ return;
+ if (sa->sa_family != AF_CCITT)
+ return;
+ if (cmd != RTM_DELETE) {
+ rtrequest(RTM_ADD, sa, rt_key(rt), x25_dgram_sockmask,
+ RTF_PROTO2, &rt2);
+ if (rt2) {
+ rt2->rt_llinfo = (caddr_t) rt;
+ rt->rt_refcnt++;
+ }
+ return;
+ }
+ rt2 = rt;
+ if ((rt = rtalloc1(sa, 0)) == 0 ||
+ (rt->rt_flags & RTF_PROTO2) == 0 ||
+ rt->rt_llinfo != (caddr_t)rt2) {
+ printf("x25_rtchange: inverse route screwup\n");
+ return;
+ } else
+ rt2->rt_refcnt--;
+ rtrequest(RTM_DELETE, sa, rt_key(rt2), x25_dgram_sockmask,
+ 0, (struct rtentry **) 0);
+}
+
+static struct sockaddr_x25 blank_x25 = {sizeof blank_x25, AF_CCITT};
+/*
+ * IP to X25 address routine copyright ACC, used by permission.
+ */
+union imp_addr {
+ struct in_addr ip;
+ struct imp {
+ u_char s_net;
+ u_char s_host;
+ u_char s_lh;
+ u_char s_impno;
+ } imp;
+};
+
+/*
+ * The following is totally bogus and here only to preserve
+ * the IP to X.25 translation.
+ */
+x25_ddnip_to_ccitt(src, rt)
+struct sockaddr_in *src;
+register struct rtentry *rt;
+{
+ register struct sockaddr_x25 *dst = (struct sockaddr_x25 *)rt->rt_gateway;
+ union imp_addr imp_addr;
+ int imp_no, imp_port, temp;
+ char *x25addr = dst->x25_addr;
+
+
+ imp_addr.ip = src->sin_addr;
+ *dst = blank_x25;
+ if ((imp_addr.imp.s_net & 0x80) == 0x00) { /* class A */
+ imp_no = imp_addr.imp.s_impno;
+ imp_port = imp_addr.imp.s_host;
+ } else if ((imp_addr.imp.s_net & 0xc0) == 0x80) { /* class B */
+ imp_no = imp_addr.imp.s_impno;
+ imp_port = imp_addr.imp.s_lh;
+ } else { /* class C */
+ imp_no = imp_addr.imp.s_impno / 32;
+ imp_port = imp_addr.imp.s_impno % 32;
+ }
+
+ x25addr[0] = 12; /* length */
+ /* DNIC is cleared by struct copy above */
+
+ if (imp_port < 64) { /* Physical: 0000 0 IIIHH00 [SS] *//* s_impno
+ * -> III, s_host -> HH */
+ x25addr[5] = 0; /* set flag bit */
+ x25addr[6] = imp_no / 100;
+ x25addr[7] = (imp_no % 100) / 10;
+ x25addr[8] = imp_no % 10;
+ x25addr[9] = imp_port / 10;
+ x25addr[10] = imp_port % 10;
+ } else { /* Logical: 0000 1 RRRRR00 [SS] *//* s
+ * _host * 256 + s_impno -> RRRRR */
+ temp = (imp_port << 8) + imp_no;
+ x25addr[5] = 1;
+ x25addr[6] = temp / 10000;
+ x25addr[7] = (temp % 10000) / 1000;
+ x25addr[8] = (temp % 1000) / 100;
+ x25addr[9] = (temp % 100) / 10;
+ x25addr[10] = temp % 10;
+ }
+}
+
+/*
+ * This routine is a sketch and is not to be believed!!!!!
+ *
+ * This is a utility routine to be called by x25 devices when a
+ * call request is honored with the intent of starting datagram forwarding.
+ */
+x25_dg_rtinit(dst, ia, af)
+struct sockaddr_x25 *dst;
+register struct x25_ifaddr *ia;
+{
+ struct sockaddr *sa = 0;
+ struct rtentry *rt;
+ struct in_addr my_addr;
+ static struct sockaddr_in sin = {sizeof(sin), AF_INET};
+
+ if (ia->ia_ifp->if_type == IFT_X25DDN && af == AF_INET) {
+ /*
+ * Inverse X25 to IP mapping copyright and courtesy ACC.
+ */
+ int imp_no, imp_port, temp;
+ union imp_addr imp_addr;
+ {
+ /*
+ * First determine our IP addr for network
+ */
+ register struct in_ifaddr *ina;
+ extern struct in_ifaddr *in_ifaddr;
+
+ for (ina = in_ifaddr; ina; ina = ina->ia_next)
+ if (ina->ia_ifp == ia->ia_ifp) {
+ my_addr = ina->ia_addr.sin_addr;
+ break;
+ }
+ }
+ {
+
+ register char *x25addr = dst->x25_addr;
+
+ switch (x25addr[5] & 0x0f) {
+ case 0: /* Physical: 0000 0 IIIHH00 [SS] */
+ imp_no =
+ ((int) (x25addr[6] & 0x0f) * 100) +
+ ((int) (x25addr[7] & 0x0f) * 10) +
+ ((int) (x25addr[8] & 0x0f));
+
+
+ imp_port =
+ ((int) (x25addr[9] & 0x0f) * 10) +
+ ((int) (x25addr[10] & 0x0f));
+ break;
+ case 1: /* Logical: 0000 1 RRRRR00 [SS] */
+ temp = ((int) (x25addr[6] & 0x0f) * 10000)
+ + ((int) (x25addr[7] & 0x0f) * 1000)
+ + ((int) (x25addr[8] & 0x0f) * 100)
+ + ((int) (x25addr[9] & 0x0f) * 10)
+ + ((int) (x25addr[10] & 0x0f));
+
+ imp_port = temp >> 8;
+ imp_no = temp & 0xff;
+ break;
+ default:
+ return (0L);
+ }
+ imp_addr.ip = my_addr;
+ if ((imp_addr.imp.s_net & 0x80) == 0x00) {
+ /* class A */
+ imp_addr.imp.s_host = imp_port;
+ imp_addr.imp.s_impno = imp_no;
+ imp_addr.imp.s_lh = 0;
+ } else if ((imp_addr.imp.s_net & 0xc0) == 0x80) {
+ /* class B */
+ imp_addr.imp.s_lh = imp_port;
+ imp_addr.imp.s_impno = imp_no;
+ } else {
+ /* class C */
+ imp_addr.imp.s_impno = (imp_no << 5) + imp_port;
+ }
+ }
+ sin.sin_addr = imp_addr.ip;
+ sa = (struct sockaddr *)&sin;
+ } else {
+ /*
+ * This uses the X25 routing table to do inverse
+ * lookup of x25 address to sockaddr.
+ */
+ if (rt = rtalloc1(SA(dst), 0)) {
+ sa = rt->rt_gateway;
+ rt->rt_refcnt--;
+ }
+ }
+ /*
+ * Call to rtalloc1 will create rtentry for reverse path
+ * to callee by virtue of cloning magic and will allocate
+ * space for local control block.
+ */
+ if (sa && (rt = rtalloc1(sa, 1)))
+ rt->rt_refcnt--;
+}
+int x25_startproto = 1;
+
+pk_init()
+{
+ /*
+ * warning, sizeof (struct sockaddr_x25) > 32,
+ * but contains no data of interest beyond 32
+ */
+ if (x25_startproto) {
+ pk_protolisten(0xcc, 1, x25_dgram_incoming);
+ pk_protolisten(0x81, 1, x25_dgram_incoming);
+ }
+}
+
+struct x25_dgproto {
+ u_char spi;
+ u_char spilen;
+ int (*f)();
+} x25_dgprototab[] = {
+#if defined(ISO) && defined(TPCONS)
+{ 0x0, 0, tp_incoming},
+#endif
+{ 0xcc, 1, x25_dgram_incoming},
+{ 0xcd, 1, x25_dgram_incoming},
+{ 0x81, 1, x25_dgram_incoming},
+};
+
+pk_user_protolisten(info)
+register u_char *info;
+{
+ register struct x25_dgproto *dp = x25_dgprototab
+ + ((sizeof x25_dgprototab) / (sizeof *dp));
+ register struct pklcd *lcp;
+
+ while (dp > x25_dgprototab)
+ if ((--dp)->spi == info[0])
+ goto gotspi;
+ return ESRCH;
+
+gotspi: if (info[1])
+ return pk_protolisten(dp->spi, dp->spilen, dp->f);
+ for (lcp = pk_listenhead; lcp; lcp = lcp->lcd_listen)
+ if (lcp->lcd_laddr.x25_udlen == dp->spilen &&
+ Bcmp(&dp->spi, lcp->lcd_laddr.x25_udata, dp->spilen) == 0) {
+ pk_disconnect(lcp);
+ return 0;
+ }
+ return ESRCH;
+}
+
+/*
+ * This routine transfers an X.25 circuit to or from a routing entry.
+ * If the supplied circuit is * in DATA_TRANSFER state, it is added to the
+ * routing entry. If freshly allocated, it glues back the vc from
+ * the rtentry to the socket.
+ */
+pk_rtattach(so, m0)
+register struct socket *so;
+struct mbuf *m0;
+{
+ register struct pklcd *lcp = (struct pklcd *)so->so_pcb;
+ register struct mbuf *m = m0;
+ struct sockaddr *dst = mtod(m, struct sockaddr *);
+ register struct rtentry *rt = rtalloc1(dst, 0);
+ register struct llinfo_x25 *lx;
+ caddr_t cp;
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define transfer_sockbuf(s, f, l) \
+ while (m = (s)->sb_mb)\
+ {(s)->sb_mb = m->m_act; m->m_act = 0; sbfree((s), m); f(l, m);}
+
+ if (rt)
+ rt->rt_refcnt--;
+ cp = (dst->sa_len < m->m_len) ? ROUNDUP(dst->sa_len) + (caddr_t)dst : 0;
+ while (rt &&
+ ((cp == 0 && rt_mask(rt) != 0) ||
+ (cp != 0 && (rt_mask(rt) == 0 ||
+ Bcmp(cp, rt_mask(rt), rt_mask(rt)->sa_len)) != 0)))
+ rt = (struct rtentry *)rt->rt_nodes->rn_dupedkey;
+ if (rt == 0 || (rt->rt_flags & RTF_GATEWAY) ||
+ (lx = (struct llinfo_x25 *)rt->rt_llinfo) == 0)
+ return ESRCH;
+ if (lcp == 0)
+ return ENOTCONN;
+ switch (lcp->lcd_state) {
+ default:
+ return ENOTCONN;
+
+ case READY:
+ /* Detach VC from rtentry */
+ if (lx->lx_lcd == 0)
+ return ENOTCONN;
+ lcp->lcd_so = 0;
+ pk_close(lcp);
+ lcp = lx->lx_lcd;
+ if (lx->lx_next->lx_rt == rt)
+ x25_lxfree(lx);
+ lcp->lcd_so = so;
+ lcp->lcd_upper = 0;
+ lcp->lcd_upnext = 0;
+ transfer_sockbuf(&lcp->lcd_sb, sbappendrecord, &so->so_snd);
+ soisconnected(so);
+ return 0;
+
+ case DATA_TRANSFER:
+ /* Add VC to rtentry */
+ lcp->lcd_so = 0;
+ lcp->lcd_sb = so->so_snd; /* structure copy */
+ bzero((caddr_t)&so->so_snd, sizeof(so->so_snd)); /* XXXXXX */
+ so->so_pcb = 0;
+ x25_rtattach(lcp, rt);
+ transfer_sockbuf(&so->so_rcv, x25_ifinput, lcp);
+ soisdisconnected(so);
+ }
+ return 0;
+}
+x25_rtattach(lcp0, rt)
+register struct pklcd *lcp0;
+struct rtentry *rt;
+{
+ register struct llinfo_x25 *lx = (struct llinfo_x25 *)rt->rt_llinfo;
+ register struct pklcd *lcp;
+ register struct mbuf *m;
+ if (lcp = lx->lx_lcd) { /* adding an additional VC */
+ if (lcp->lcd_state == READY) {
+ transfer_sockbuf(&lcp->lcd_sb, pk_output, lcp0);
+ lcp->lcd_upper = 0;
+ pk_close(lcp);
+ } else {
+ lx = x25_lxalloc(rt);
+ if (lx == 0)
+ return ENOBUFS;
+ }
+ }
+ lx->lx_lcd = lcp = lcp0;
+ lcp->lcd_upper = x25_ifinput;
+ lcp->lcd_upnext = (caddr_t)lx;
+}
diff --git a/sys/netccitt/llc_input.c b/sys/netccitt/llc_input.c
new file mode 100644
index 0000000..7a01973
--- /dev/null
+++ b/sys/netccitt/llc_input.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) Dirk Husemann, Computer Science Department IV,
+ * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Dirk Husemann and the Computer Science Department (IV) of
+ * the University of Erlangen-Nuremberg, Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)llc_input.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_llc.h>
+#include <net/route.h>
+
+#include <netccitt/dll.h>
+#include <netccitt/llc_var.h>
+
+/*
+ * This module implements LLC as specified by ISO 8802-2.
+ */
+
+
+/*
+ * llcintr() handles all LLC frames (except ISO CLNS ones for the time being)
+ * and tries to pass them on to the appropriate network layer entity.
+ */
+void
+llcintr()
+{
+ register struct mbuf *m;
+ register int i;
+ register int frame_kind;
+ register u_char cmdrsp;
+ struct llc_linkcb *linkp;
+ struct rtentry *sirt;
+ struct npaidbentry *sapinfo;
+ struct sdl_hdr *sdlhdr;
+ struct llc *frame;
+ char *c;
+ long expected_len;
+
+ struct ifnet *ifp;
+ struct rtentry *llrt;
+ struct rtentry *nlrt;
+
+ for (;;) {
+ i = splimp();
+ IF_DEQUEUE(&llcintrq, m);
+ splx(i);
+ if (m == 0)
+ break;
+#ifdef DIAGNOSTIC
+ if ((m->m_flags & M_PKTHDR) == 0)
+ panic("llcintr no HDR");
+#endif
+ /*
+ * Get ifp this packet was received on
+ */
+ ifp = m->m_pkthdr.rcvif;
+
+ sdlhdr = mtod(m, struct sdl_hdr *);
+
+ /*
+ * [Copied from net/ip_input.c]
+ *
+ * Check that the amount of data in the buffers is
+ * at least as much as the LLC header tells us.
+ * Trim mbufs if longer than expected.
+ * Drop packets if shorter than we think they are.
+ *
+ * Layout of mbuf chain at this point:
+ *
+ * +-------------------------------+----+ -\
+ * | sockaddr_dl src - sdlhdr_src | 20 | \
+ * +-------------------------------+----+ |
+ * | sockaddr_dl dst - sdlhdr_dst | 20 | > sizeof(struct sdl_hdr) == 44
+ * +-------------------------------+----+ |
+ * | LLC frame len - sdlhdr_len | 04 | /
+ * +-------------------------------+----+ -/
+ * /
+ * | m_next
+ * \
+ * +----------------------------+----+ -\
+ * | llc DSAP | 01 | \
+ * +----------------------------+----+ |
+ * | llc SSAP | 01 | |
+ * +----------------------------+----+ > sdlhdr_len
+ * | llc control | 01 | |
+ * +----------------------------+----+ |
+ * | ... | | /
+ * -/
+ *
+ * Thus the we expect to have exactly
+ * (sdlhdr->sdlhdr_len+sizeof(struct sdl_hdr)) in the mbuf chain
+ */
+ expected_len = sdlhdr->sdlhdr_len + sizeof(struct sdl_hdr);
+
+ if (m->m_pkthdr.len < expected_len) {
+ m_freem(m);
+ continue;
+ }
+ if (m->m_pkthdr.len > expected_len) {
+ if (m->m_len == m->m_pkthdr.len) {
+ m->m_len = expected_len;
+ m->m_pkthdr.len = expected_len;
+ } else
+ m_adj(m, expected_len - m->m_pkthdr.len);
+ }
+
+ /*
+ * Get llc header
+ */
+ if (m->m_len > sizeof(struct sdl_hdr))
+ frame = mtod((struct mbuf *)((struct sdl_hdr*)(m+1)),
+ struct llc *);
+ else frame = mtod(m->m_next, struct llc *);
+ if (frame == (struct llc *) NULL)
+ panic("llcintr no llc header");
+
+ /*
+ * Now check for bogus I/S frame, i.e. those with a control
+ * field telling us they're an I/S frame yet their length
+ * is less than the established I/S frame length (DSAP + SSAP +
+ * control + N(R)&P/F = 4) --- we drop those suckers
+ */
+ if (((frame->llc_control & 0x03) != 0x03)
+ && ((expected_len - sizeof(struct sdl_hdr)) < LLC_ISFRAMELEN)) {
+ m_freem(m);
+ printf("llc: hurz error\n");
+ continue;
+ }
+
+ /*
+ * Get link control block for the addressed link connection.
+ * If there is none we take care of it later on.
+ */
+ cmdrsp = (frame->llc_ssap & 0x01);
+ frame->llc_ssap &= ~0x01;
+ if (llrt = rtalloc1((struct sockaddr *)&sdlhdr->sdlhdr_src, 0))
+ llrt->rt_refcnt--;
+#ifdef notyet
+ else llrt = npaidb_enter(&sdlhdr->sdlhdr_src, 0, 0, 0);
+#endif /* notyet */
+ else {
+ /*
+ * We cannot do anything currently here as we
+ * don't `know' this link --- drop it
+ */
+ m_freem(m);
+ continue;
+ }
+ linkp = ((struct npaidbentry *)(llrt->rt_llinfo))->np_link;
+ nlrt = ((struct npaidbentry *)(llrt->rt_llinfo))->np_rt;
+
+ /*
+ * If the link is not existing right now, we can try and look up
+ * the SAP info block.
+ */
+ if ((linkp == 0) && frame->llc_ssap)
+ sapinfo = llc_getsapinfo(frame->llc_dsap, ifp);
+
+ /*
+ * Handle XID and TEST frames
+ * XID: if DLSAP == 0, return type-of-services
+ * window-0
+ * DLSAP-0
+ * format-identifier-?
+ * if DLSAP != 0, locate sapcb and return
+ * type-of-services
+ * SAP-window
+ * format-identifier-?
+ * TEST: swap (snpah_dst, snpah_src) and return frame
+ *
+ * Also toggle the CMD/RESP bit
+ *
+ * Is this behaviour correct? Check ISO 8802-2 (90)!
+ */
+ frame_kind = llc_decode(frame, (struct llc_linkcb *)0);
+ switch(frame_kind) {
+ case LLCFT_XID:
+ if (linkp || sapinfo) {
+ if (linkp)
+ frame->llc_window = linkp->llcl_window;
+ else frame->llc_window = sapinfo->si_window;
+ frame->llc_fid = 9; /* XXX */
+ frame->llc_class = sapinfo->si_class;
+ frame->llc_ssap = frame->llc_dsap;
+ } else {
+ frame->llc_window = 0;
+ frame->llc_fid = 9;
+ frame->llc_class = 1;
+ frame->llc_dsap = frame->llc_ssap = 0;
+ }
+
+ /* fall thru to */
+ case LLCFT_TEST:
+ sdl_swapaddr(&(mtod(m, struct sdl_hdr *)->sdlhdr_dst),
+ &(mtod(m, struct sdl_hdr *)->sdlhdr_src));
+
+ /* Now set the CMD/RESP bit */
+ frame->llc_ssap |= (cmdrsp == 0x0 ? 0x1 : 0x0);
+
+ /* Ship it out again */
+ (*ifp->if_output)(ifp, m,
+ (struct sockaddr *) &(mtod(m, struct sdl_hdr *)->sdlhdr_dst),
+ (struct rtentry *) 0);
+ continue;
+ }
+
+ /*
+ * Create link control block in case it is not existing
+ */
+ if (linkp == 0 && sapinfo) {
+ if ((linkp = llc_newlink(&sdlhdr->sdlhdr_src, ifp, nlrt,
+ (nlrt == 0) ? 0 : nlrt->rt_llinfo,
+ llrt)) == 0) {
+ printf("llcintr: couldn't create new link\n");
+ m_freem(m);
+ continue;
+ }
+ ((struct npaidbentry *)llrt->rt_llinfo)->np_link = linkp;
+ } else if (linkp == 0) {
+ /* The link is not known to us, drop the frame and continue */
+ m_freem(m);
+ continue;
+ }
+
+ /*
+ * Drop SNPA header and get rid of empty mbuf at the
+ * front of the mbuf chain (I don't like 'em)
+ */
+ m_adj(m, sizeof(struct sdl_hdr));
+ /*
+ * LLC_UFRAMELEN is sufficient, m_pullup() will pull up
+ * the min(m->m_len, maxprotohdr_len [=40]) thus doing
+ * the trick ...
+ */
+ if ((m = m_pullup(m, LLC_UFRAMELEN)))
+ /*
+ * Pass it on thru the elements of procedure
+ */
+ llc_input(linkp, m, cmdrsp);
+ }
+ return;
+}
+
+/*
+ * llc_input() --- We deal with the various incoming frames here.
+ * Basically we (indirectly) call the appropriate
+ * state handler function that's pointed to by
+ * llcl_statehandler.
+ *
+ * The statehandler returns an action code ---
+ * further actions like
+ * o notify network layer
+ * o block further sending
+ * o deblock link
+ * o ...
+ * are then enacted accordingly.
+ */
+llc_input(struct llc_linkcb *linkp, struct mbuf *m, u_char cmdrsp)
+{
+ int frame_kind;
+ int pollfinal;
+ int action = 0;
+ struct llc *frame;
+ struct ifnet *ifp = linkp->llcl_if;
+
+ if ((frame = mtod(m, struct llc *)) == (struct llc *) 0) {
+ m_freem(m);
+ return 0;
+ }
+ pollfinal = ((frame->llc_control & 0x03) == 0x03) ?
+ LLCGBITS(frame->llc_control, u_pf) :
+ LLCGBITS(frame->llc_control_ext, s_pf);
+
+ /*
+ * first decode the frame
+ */
+ frame_kind = llc_decode(frame, linkp);
+
+ switch (action = llc_statehandler(linkp, frame, frame_kind, cmdrsp,
+ pollfinal)) {
+ case LLC_DATA_INDICATION:
+ m_adj(m, LLC_ISFRAMELEN);
+ if (m = m_pullup(m, NLHDRSIZEGUESS)) {
+ m->m_pkthdr.rcvif = (struct ifnet *)linkp->llcl_nlnext;
+ (*linkp->llcl_sapinfo->si_input)(m);
+ }
+ break;
+ }
+
+ /* release mbuf if not an info frame */
+ if (action != LLC_DATA_INDICATION && m)
+ m_freem(m);
+
+ /* try to get frames out ... */
+ llc_start(linkp);
+
+ return 0;
+}
+
+/*
+ * This routine is called by configuration setup. It sets up a station control
+ * block and notifies all registered upper level protocols.
+ */
+caddr_t
+llc_ctlinput(int prc, struct sockaddr *addr, caddr_t info)
+{
+ struct ifnet *ifp;
+ struct ifaddr *ifa;
+ struct dll_ctlinfo *ctlinfo = (struct dll_ctlinfo *)info;
+ u_char sap;
+ struct dllconfig *config;
+ caddr_t pcb;
+ struct rtentry *nlrt;
+ struct rtentry *llrt;
+ struct llc_linkcb *linkp;
+ register int i;
+
+ /* info must point to something valid at all times */
+ if (info == 0)
+ return 0;
+
+ if (prc == PRC_IFUP || prc == PRC_IFDOWN) {
+ /* we use either this set ... */
+ ifa = ifa_ifwithaddr(addr);
+ ifp = ifa ? ifa->ifa_ifp : 0;
+ if (ifp == 0)
+ return 0;
+
+ sap = ctlinfo->dlcti_lsap;
+ config = ctlinfo->dlcti_cfg;
+ pcb = (caddr_t) 0;
+ nlrt = (struct rtentry *) 0;
+ } else {
+ /* or this one */
+ sap = 0;
+ config = (struct dllconfig *) 0;
+ pcb = ctlinfo->dlcti_pcb;
+ nlrt = ctlinfo->dlcti_rt;
+
+ if ((llrt = rtalloc1(nlrt->rt_gateway, 0)))
+ llrt->rt_refcnt--;
+ else return 0;
+
+ linkp = ((struct npaidbentry *)llrt->rt_llinfo)->np_link;
+ }
+
+ switch (prc) {
+ case PRC_IFUP:
+ (void) llc_setsapinfo(ifp, addr->sa_family, sap, config);
+ return 0;
+
+ case PRC_IFDOWN: {
+ register struct llc_linkcb *linkp;
+ register struct llc_linkcb *nlinkp;
+ register int i;
+
+ /*
+ * All links are accessible over the doubly linked list llccb_q
+ */
+ if (!LQEMPTY) {
+ /*
+ * A for-loop is not that great an idea as the linkp
+ * will get deleted by llc_timer()
+ */
+ linkp = LQFIRST;
+ while (LQVALID(linkp)) {
+ nlinkp = LQNEXT(linkp);
+ if (linkp->llcl_if = ifp) {
+ i = splimp();
+ (void)llc_statehandler(linkp, (struct llc *)0,
+ NL_DISCONNECT_REQUEST,
+ 0, 1);
+ splx(i);
+ }
+ linkp = nlinkp;
+ }
+ }
+ }
+
+ case PRC_CONNECT_REQUEST:
+ if (linkp == 0) {
+ if ((linkp = llc_newlink((struct sockaddr_dl *) nlrt->rt_gateway,
+ nlrt->rt_ifp, nlrt,
+ pcb, llrt)) == 0)
+ return (0);
+ ((struct npaidbentry *)llrt->rt_llinfo)->np_link = linkp;
+ i = splimp();
+ (void)llc_statehandler(linkp, (struct llc *) 0,
+ NL_CONNECT_REQUEST, 0, 1);
+ splx(i);
+ }
+ return ((caddr_t)linkp);
+
+ case PRC_DISCONNECT_REQUEST:
+ if (linkp == 0)
+ panic("no link control block!");
+
+ i = splimp();
+ (void)llc_statehandler(linkp, (struct llc *) 0,
+ NL_DISCONNECT_REQUEST, 0, 1);
+ splx(i);
+
+ /*
+ * The actual removal of the link control block is done by the
+ * cleaning neutrum (i.e. llc_timer()).
+ */
+ break;
+
+ case PRC_RESET_REQUEST:
+ if (linkp == 0)
+ panic("no link control block!");
+
+ i = splimp();
+ (void)llc_statehandler(linkp, (struct llc *) 0,
+ NL_RESET_REQUEST, 0, 1);
+ splx(i);
+
+ break;
+
+ }
+
+ return 0;
+}
diff --git a/sys/netccitt/llc_output.c b/sys/netccitt/llc_output.c
new file mode 100644
index 0000000..98d0328
--- /dev/null
+++ b/sys/netccitt/llc_output.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) Dirk Husemann, Computer Science Department IV,
+ * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Dirk Husemann and the Computer Science Department (IV) of
+ * the University of Erlangen-Nuremberg, Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)llc_output.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_llc.h>
+#include <net/route.h>
+
+#include <netccitt/dll.h>
+#include <netccitt/llc_var.h>
+
+/*
+ * llc_output() --- called by an upper layer (network layer) entity whenever
+ * there is an INFO frame to be transmitted. We enqueue the
+ * info frame and call llc_start() to do the actual sending.
+ */
+
+llc_output(struct llc_linkcb *linkp, struct mbuf *m)
+{
+ register int i;
+
+ i = splimp();
+ LLC_ENQUEUE(linkp, m);
+ llc_start(linkp);
+ splx(i);
+
+}
+
+
+/*
+ * llc_start() --- We try to subsequently dequeue all the frames available and
+ * send them out.
+ */
+void
+llc_start(struct llc_linkcb *linkp)
+{
+ register int i;
+ register struct mbuf *m;
+ int action;
+
+ while ((LLC_STATEEQ(linkp, NORMAL) || LLC_STATEEQ(linkp, BUSY) ||
+ LLC_STATEEQ(linkp, REJECT)) &&
+ (linkp->llcl_slotsfree > 0) &&
+ (LLC_GETFLAG(linkp, REMOTE_BUSY) == 0)) {
+ LLC_DEQUEUE(linkp, m);
+ if (m == NULL)
+ break;
+ LLC_SETFRAME(linkp, m);
+ (void)llc_statehandler(linkp, (struct llc *) 0, NL_DATA_REQUEST,
+ 0, 0);
+ }
+}
+
+
+/*
+ * llc_send() --- Handles single frames. If dealing with INFO frames we need to
+ * prepend the LLC header, otherwise we just allocate an mbuf.
+ * In both cases the actual send is done by llc_rawsend().
+ */
+llc_send(struct llc_linkcb *linkp, int frame_kind, int cmdrsp, int pollfinal)
+{
+ register struct mbuf *m = (struct mbuf *)0;
+ register struct llc *frame;
+
+ if (frame_kind == LLCFT_INFO)
+ m = linkp->llcl_output_buffers[llc_seq2slot(linkp,
+ linkp->llcl_vs)];
+ LLC_GETHDR(frame, m);
+
+ /* pass it on to llc_rawsend() */
+ llc_rawsend(linkp, m, frame, frame_kind, linkp->llcl_vs, cmdrsp, pollfinal);
+
+ if (frame_kind == LLCFT_INFO)
+ LLC_INC(linkp->llcl_vs);
+
+ return 0;
+}
+
+/*
+ * llc_resend() --- llc_resend() retransmits all unacknowledged INFO frames.
+ */
+llc_resend(struct llc_linkcb *linkp, int cmdrsp, int pollfinal)
+{
+ register struct llc *frame;
+ register struct mbuf *m;
+ register int seq, slot;
+
+ if (linkp->llcl_slotsfree < linkp->llcl_window)
+ /* assert lock between nr_received & V(S) */
+ if (linkp->llcl_nr_received != linkp->llcl_vs)
+ panic("llc: V(S) != N(R) received\n");
+
+ for (slot = llc_seq2slot(linkp, linkp->llcl_vs);
+ slot != linkp->llcl_freeslot;
+ LLC_INC(linkp->llcl_vs),
+ slot = llc_seq2slot(linkp, linkp->llcl_vs)) {
+ m = linkp->llcl_output_buffers[slot];
+ LLC_GETHDR(frame, m);
+ llc_rawsend(linkp, m, frame, LLCFT_INFO, linkp->llcl_vs,
+ cmdrsp, pollfinal);
+ pollfinal = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * llc_rawsend() --- constructs an LLC frame and sends it out via the
+ * associated interface of the link control block.
+ *
+ * We need to make sure that outgoing frames have the correct length,
+ * in particular the 4 byte ones (RR, RNR, REJ) as LLC_GETHDR() will
+ * set the mbuf len to 3 as default len for non INFO frames ...
+ *
+ * Frame kind Length (w/o MAC header, {D,S}SAP incl.)
+ * --------------------------------------------------------------
+ * DISC, SABME, UA, DM 3 bytes ({D,S}SAP + CONTROL)
+ * RR, RNR, REJ 4 bytes ({D,S}SAP + CONTROL0 + CONTROL1)
+ * XID 6 bytes ({D,S}SAP + CONTROL0 + FI,CLASS,WINDOW)
+ * FRMR 7 bytes ({D,S}SAP + CONTROL0 + REJ CONTROL,V(S),V(R),CAUSE)
+ * INFO 4 -- MTU
+ * UI, TEST 3 -- MTU
+ *
+ */
+#define LLC_SETLEN(m, l) (m)->m_pkthdr.len = (m)->m_len = (l)
+
+llc_rawsend(struct llc_linkcb *linkp, struct mbuf *m, struct llc *frame,
+ int frame_kind, int vs, int cmdrsp, int pollfinal)
+{
+ register short adjust = LLC_UFRAMELEN;
+ struct ifnet *ifp;
+
+ switch (frame_kind) {
+ /* supervisory and information frames */
+ case LLCFT_INFO:
+ frame->llc_control = LLC_INFO;
+ LLCSBITS(frame->llc_control, i_ns, vs);
+ LLCSBITS(frame->llc_control_ext, i_nr, linkp->llcl_vr);
+ adjust = LLC_ISFRAMELEN;
+ break;
+ case LLCFT_RR:
+ frame->llc_control = LLC_RR;
+ LLC_SETLEN(m, LLC_ISFRAMELEN);
+ LLCSBITS(frame->llc_control_ext, s_nr, linkp->llcl_vr);
+ adjust = LLC_ISFRAMELEN;
+ break;
+ case LLCFT_RNR:
+ frame->llc_control = LLC_RNR;
+ LLC_SETLEN(m, LLC_ISFRAMELEN);
+ LLCSBITS(frame->llc_control_ext, s_nr, linkp->llcl_vr);
+ adjust = LLC_ISFRAMELEN;
+ break;
+ case LLCFT_REJ:
+ frame->llc_control = LLC_REJ;
+ LLC_SETLEN(m, LLC_ISFRAMELEN);
+ LLCSBITS(frame->llc_control_ext, s_nr, linkp->llcl_vr);
+ adjust = LLC_ISFRAMELEN;
+ break;
+ /* unnumbered frames */
+ case LLCFT_DM:
+ frame->llc_control = LLC_DM;
+ break;
+ case LLCFT_SABME:
+ frame->llc_control = LLC_SABME;
+ break;
+ case LLCFT_DISC:
+ frame->llc_control = LLC_DISC;
+ break;
+ case LLCFT_UA:
+ frame->llc_control = LLC_UA;
+ break;
+ case LLCFT_UI:
+ frame->llc_control = LLC_UI;
+ break;
+ case LLCFT_FRMR:
+ frame->llc_control = LLC_FRMR;
+ /* get more space --- FRMR frame are longer then usual */
+ LLC_SETLEN(m, LLC_FRMRLEN);
+ bcopy((caddr_t) &linkp->llcl_frmrinfo,
+ (caddr_t) &frame->llc_frmrinfo,
+ sizeof(struct frmrinfo));
+ break;
+ default:
+ /*
+ * We don't send {XID, TEST} frames
+ */
+ if (m)
+ m_freem(m);
+ return;
+ }
+
+ /*
+ * Fill in DSAP/SSAP
+ */
+ frame->llc_dsap = frame->llc_ssap = LLSAPADDR(&linkp->llcl_addr);
+ frame->llc_ssap |= cmdrsp;
+
+ /*
+ * Check for delayed action pending. ISO 8802-2, 7.9.2 (5)
+ * and ISO 8802-2, 7.9.2.3 (32), (34), (36) pertain to this
+ * piece of code --- hopefully we got it right here (i.e.
+ * in the spirit of (32), (34), and (36) ...
+ */
+ switch (frame_kind) {
+ case LLCFT_RR:
+ case LLCFT_RNR:
+ case LLCFT_REJ:
+ case LLCFT_INFO:
+ switch (LLC_GETFLAG(linkp, DACTION)) {
+ case LLC_DACKCMD:
+ case LLC_DACKRSP:
+ LLC_STOPTIMER(linkp, DACTION);
+ break;
+ case LLC_DACKCMDPOLL:
+ if (cmdrsp == LLC_CMD) {
+ pollfinal = 1;
+ LLC_STOPTIMER(linkp, DACTION);
+ }
+ break;
+ case LLC_DACKRSPFINAL:
+ if (cmdrsp == LLC_RSP) {
+ pollfinal = 1;
+ LLC_STOPTIMER(linkp, DACTION);
+ }
+ break;
+ }
+ break;
+ }
+
+ if (adjust == LLC_UFRAMELEN)
+ LLCSBITS(frame->llc_control, u_pf, pollfinal);
+ else LLCSBITS(frame->llc_control_ext, s_pf, pollfinal);
+
+ /*
+ * Get interface to send frame onto
+ */
+ ifp = linkp->llcl_if;
+ if (frame_kind == LLCFT_INFO) {
+ /*
+ * send out a copy of the frame, retain the
+ * original
+ */
+ (*ifp->if_output)(ifp, m_copy(m, 0, (int)M_COPYALL),
+ rt_key(linkp->llcl_nlrt),
+ linkp->llcl_nlrt);
+ /*
+ * Account for the LLC header and let it ``disappear''
+ * as the raw info frame payload is what we hold in
+ * the output_buffers of the link.
+ */
+ m_adj(m, LLC_ISFRAMELEN);
+ } else (*ifp->if_output)(ifp, m,
+ rt_key(linkp->llcl_nlrt),
+ linkp->llcl_nlrt);
+}
+
diff --git a/sys/netccitt/llc_subr.c b/sys/netccitt/llc_subr.c
new file mode 100644
index 0000000..46848fd
--- /dev/null
+++ b/sys/netccitt/llc_subr.c
@@ -0,0 +1,2356 @@
+/*
+ * Copyright (C) Dirk Husemann, Computer Science Department IV,
+ * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Dirk Husemann and the Computer Science Department (IV) of
+ * the University of Erlangen-Nuremberg, Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)llc_subr.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_llc.h>
+#include <net/route.h>
+
+#include <netccitt/dll.h>
+#include <netccitt/llc_var.h>
+
+/*
+ * Frame names for diagnostic messages
+ */
+char *frame_names[] = { "INFO", "RR", "RNR", "REJ", "DM", "SABME", "DISC",
+ "UA", "FRMR", "UI", "XID", "TEST", "ILLEGAL", "TIMER", "N2xT1"};
+
+
+/*
+ * Trace level
+ */
+int llc_tracelevel = LLCTR_URGENT;
+
+/*
+ * Values for accessing various bitfields
+ */
+struct bitslice llc_bitslice[] = {
+/* mask, shift value */
+ { 0x1, 0x0 },
+ { 0xfe, 0x1 },
+ { 0x3, 0x0 },
+ { 0xc, 0x2 },
+ { 0x10, 0x4 },
+ { 0xe0, 0x5 },
+ { 0x1f, 0x0 }
+};
+
+/*
+ * We keep the link control blocks on a doubly linked list -
+ * primarily for checking in llc_time()
+ */
+
+struct llccb_q llccb_q = { &llccb_q, &llccb_q };
+
+/*
+ * Flag for signalling wether route tree for AF_LINK has been
+ * initialized yet.
+ */
+
+int af_link_rts_init_done = 0;
+
+
+/*
+ * Functions dealing with struct sockaddr_dl */
+
+/* Compare sdl_a w/ sdl_b */
+
+sdl_cmp(struct sockaddr_dl *sdl_a, struct sockaddr_dl *sdl_b)
+{
+ if (LLADDRLEN(sdl_a) != LLADDRLEN(sdl_b))
+ return(1);
+ return(bcmp((caddr_t) sdl_a->sdl_data, (caddr_t) sdl_b->sdl_data,
+ LLADDRLEN(sdl_a)));
+}
+
+/* Copy sdl_f to sdl_t */
+
+sdl_copy(struct sockaddr_dl *sdl_f, struct sockaddr_dl *sdl_t)
+{
+ bcopy((caddr_t) sdl_f, (caddr_t) sdl_t, sdl_f->sdl_len);
+}
+
+/* Swap sdl_a w/ sdl_b */
+
+sdl_swapaddr(struct sockaddr_dl *sdl_a, struct sockaddr_dl *sdl_b)
+{
+ struct sockaddr_dl sdl_tmp;
+
+ sdl_copy(sdl_a, &sdl_tmp);
+ sdl_copy(sdl_b, sdl_a);
+ sdl_copy(&sdl_tmp, sdl_b);
+}
+
+/* Fetch the sdl of the associated if */
+
+struct sockaddr_dl *
+sdl_getaddrif(struct ifnet *ifp)
+{
+ register struct ifaddr *ifa;
+
+ for(ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+ if (ifa->ifa_addr->sa_family == AF_LINK )
+ return((struct sockaddr_dl *)(ifa->ifa_addr));
+
+ return((struct sockaddr_dl *)0);
+}
+
+/* Check addr of interface with the one given */
+
+sdl_checkaddrif(struct ifnet *ifp, struct sockaddr_dl *sdl_c)
+{
+ register struct ifaddr *ifa;
+
+ for(ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+ if ((ifa->ifa_addr->sa_family == AF_LINK ) &&
+ !sdl_cmp((struct sockaddr_dl *)(ifa->ifa_addr), sdl_c))
+ return(1);
+
+ return(0);
+}
+
+/* Build an sdl from MAC addr, DLSAP addr, and interface */
+
+sdl_setaddrif(struct ifnet *ifp, u_char *mac_addr, u_char dlsap_addr,
+ u_char mac_len, struct sockaddr_dl *sdl_to)
+{
+ register struct sockaddr_dl *sdl_tmp;
+
+ if ((sdl_tmp = sdl_getaddrif(ifp)) ) {
+ sdl_copy(sdl_tmp, sdl_to);
+ bcopy((caddr_t) mac_addr, (caddr_t) LLADDR(sdl_to), mac_len);
+ *(LLADDR(sdl_to)+mac_len) = dlsap_addr;
+ sdl_to->sdl_alen = mac_len+1;
+ return(1);
+ } else return(0);
+}
+
+/* Fill out the sdl header aggregate */
+
+sdl_sethdrif(struct ifnet *ifp, u_char *mac_src, u_char dlsap_src, u_char *mac_dst,
+ u_char dlsap_dst, u_char mac_len, struct sdl_hdr *sdlhdr_to)
+{
+ if ( !sdl_setaddrif(ifp, mac_src, dlsap_src, mac_len,
+ &sdlhdr_to->sdlhdr_src) ||
+ !sdl_setaddrif(ifp, mac_dst, dlsap_dst, mac_len,
+ &sdlhdr_to->sdlhdr_dst) )
+ return(0);
+ else return(1);
+}
+
+static struct sockaddr_dl sap_saddr;
+static struct sockaddr_dl sap_sgate = {
+ sizeof(struct sockaddr_dl), /* _len */
+ AF_LINK /* _af */
+};
+
+/*
+ * Set sapinfo for SAP address, llcconfig, af, and interface
+ */
+struct npaidbentry *
+llc_setsapinfo(struct ifnet *ifp, u_char af, u_char sap, struct dllconfig *llconf)
+{
+ struct protosw *pp;
+ struct sockaddr_dl *ifdl_addr;
+ struct rtentry *sirt = (struct rtentry *)0;
+ struct npaidbentry *sapinfo;
+ u_char saploc;
+ int size = sizeof(struct npaidbentry);
+
+ USES_AF_LINK_RTS;
+
+ /*
+ * We rely/assume that only STREAM protocols will make use of
+ * connection oriented LLC2. If this will one day not be the
+ * case this will obviously fail.
+ */
+ pp = pffindtype (af, SOCK_STREAM);
+ if (pp == 0 || pp->pr_input == 0 || pp->pr_ctlinput == 0) {
+ printf("network level protosw error");
+ return 0;
+ }
+
+ /*
+ * We need a way to jot down the LLC2 configuration for
+ * a certain LSAP address. To do this we enter
+ * a "route" for the SAP.
+ */
+ ifdl_addr = sdl_getaddrif(ifp);
+ sdl_copy(ifdl_addr, &sap_saddr);
+ sdl_copy(ifdl_addr, &sap_sgate);
+ saploc = LLSAPLOC(&sap_saddr, ifp);
+ sap_saddr.sdl_data[saploc] = sap;
+ sap_saddr.sdl_alen++;
+
+ /* now enter it */
+ rtrequest(RTM_ADD, (struct sockaddr *)&sap_saddr,
+ (struct sockaddr *)&sap_sgate, 0, 0, &sirt);
+ if (sirt == 0)
+ return 0;
+
+ /* Plug in config information in rt->rt_llinfo */
+
+ sirt->rt_llinfo = malloc(size , M_PCB, M_WAITOK);
+ sapinfo = (struct npaidbentry *) sirt->rt_llinfo;
+ if (sapinfo) {
+ bzero ((caddr_t)sapinfo, size);
+ /*
+ * For the time being we support LLC CLASS II here
+ * only
+ */
+ sapinfo->si_class = LLC_CLASS_II;
+ sapinfo->si_window = llconf->dllcfg_window;
+ sapinfo->si_trace = llconf->dllcfg_trace;
+ if (sapinfo->si_trace)
+ llc_tracelevel--;
+ else llc_tracelevel++;
+ sapinfo->si_input = pp->pr_input;
+ sapinfo->si_ctlinput = (caddr_t (*)())pp->pr_ctlinput;
+
+ return (sapinfo);
+ }
+
+ return 0;
+}
+
+/*
+ * Get sapinfo for SAP address and interface
+ */
+struct npaidbentry *
+llc_getsapinfo(u_char sap, struct ifnet *ifp)
+{
+ struct sockaddr_dl *ifdl_addr;
+ struct sockaddr_dl si_addr;
+ struct rtentry *sirt;
+ u_char saploc;
+
+ USES_AF_LINK_RTS;
+
+ ifdl_addr = sdl_getaddrif(ifp);
+ sdl_copy(ifdl_addr, &si_addr);
+ saploc = LLSAPLOC(&si_addr, ifp);
+ si_addr.sdl_data[saploc] = sap;
+ si_addr.sdl_alen++;
+
+ if ((sirt = rtalloc1((struct sockaddr *)&si_addr, 0)))
+ sirt->rt_refcnt--;
+ else return(0);
+
+ return((struct npaidbentry *)sirt->rt_llinfo);
+}
+
+/*
+ * llc_seq2slot() --- We only allocate enough memory to hold the window. This
+ * introduces the necessity to keep track of two ``pointers''
+ *
+ * o llcl_freeslot the next free slot to be used
+ * this one advances modulo llcl_window
+ * o llcl_projvs the V(S) associated with the next frame
+ * to be set via llcl_freeslot
+ * this one advances modulo LLC_MAX_SEQUENCE
+ *
+ * A new frame is inserted at llcl_output_buffers[llcl_freeslot], after
+ * which both llcl_freeslot and llcl_projvs are incremented.
+ *
+ * The slot sl(sn) for any given sequence number sn is given by
+ *
+ * sl(sn) = (llcl_freeslot + llcl_window - 1 - (llcl_projvs +
+ * LLC_MAX_SEQUENCE- sn) % LLC_MAX_SEQUENCE) %
+ * llcl_window
+ *
+ * i.e. we first calculate the number of frames we need to ``go back''
+ * from the current one (really the next one, but that doesn't matter as
+ * llcl_projvs is likewise of by plus one) and subtract that from the
+ * pointer to the most recently taken frame (llcl_freeslot - 1).
+ */
+
+short
+llc_seq2slot(struct llc_linkcb *linkp, short seqn)
+{
+ register sn = 0;
+
+ sn = (linkp->llcl_freeslot + linkp->llcl_window -
+ (linkp->llcl_projvs + LLC_MAX_SEQUENCE - seqn) %
+ LLC_MAX_SEQUENCE) % linkp->llcl_window;
+
+ return sn;
+}
+
+/*
+ * LLC2 link state handler
+ *
+ * There is in most cases one function per LLC2 state. The LLC2 standard
+ * ISO 8802-2 allows in some cases for ambiguities, i.e. we have the choice
+ * to do one thing or the other. Right now I have just chosen one but have also
+ * indicated the spot by "multiple possibilities". One could make the behavior
+ * in those cases configurable, allowing the superuser to enter a profile word
+ * (32/64 bits, whatever is needed) that would suit her needs [I quite like
+ * that idea, perhaps I'll get around to it].
+ *
+ * [Preceeding each state handler function is the description as taken from
+ * ISO 8802-2, section 7.9.2.1]
+ */
+
+/*
+ * ADM --- The connection component is in the asynchronous disconnected mode.
+ * It can accept an SABME PDU from a remote LLC SSAP or, at the request
+ * of the service access point user, can initiate an SABME PDU
+ * transmission to a remote LLC DSAP, to establish a data link
+ * connection. It also responds to a DISC command PDU and to any
+ * command PDU with the P bit set to ``1''.
+ */
+int
+llc_state_ADM(struct llc_linkcb *linkp, struct llc *frame, int frame_kind,
+ int cmdrsp, int pollfinal)
+{
+ int action = 0;
+
+ switch(frame_kind + cmdrsp) {
+ case NL_CONNECT_REQUEST:
+ llc_send(linkp, LLCFT_SABME, LLC_CMD, pollfinal);
+ LLC_SETFLAG(linkp, P, pollfinal);
+ LLC_SETFLAG(linkp, S, 0);
+ linkp->llcl_retry = 0;
+ LLC_NEWSTATE(linkp, SETUP);
+ break;
+ case LLCFT_SABME + LLC_CMD:
+ /*
+ * ISO 8802-2, table 7-1, ADM state says to set
+ * the P flag, yet this will cause an SABME [P] to be
+ * answered with an UA only, not an UA [F], all
+ * other `disconnected' states set the F flag, so ...
+ */
+ LLC_SETFLAG(linkp, F, pollfinal);
+ LLC_NEWSTATE(linkp, CONN);
+ action = LLC_CONNECT_INDICATION;
+ break;
+ case LLCFT_DISC + LLC_CMD:
+ llc_send(linkp, LLCFT_DM, LLC_RSP, pollfinal);
+ break;
+ default:
+ if (cmdrsp == LLC_CMD && pollfinal == 1)
+ llc_send(linkp, LLCFT_DM, LLC_RSP, 1);
+ /* remain in ADM state */
+ }
+
+ return action;
+}
+
+/*
+ * CONN --- The local connection component has received an SABME PDU from a
+ * remote LLC SSAP, and it is waiting for the local user to accept or
+ * refuse the connection.
+ */
+int
+llc_state_CONN(struct llc_linkcb *linkp, struct llc *frame, int frame_kind,
+ int cmdrsp, int pollfinal)
+{
+ int action = 0;
+
+ switch(frame_kind + cmdrsp) {
+ case NL_CONNECT_RESPONSE:
+ llc_send(linkp, LLCFT_UA, LLC_RSP, LLC_GETFLAG(linkp, F));
+ LLC_RESETCOUNTER(linkp);
+ LLC_SETFLAG(linkp, P, 0);
+ LLC_SETFLAG(linkp, REMOTE_BUSY, 0);
+ LLC_NEWSTATE(linkp, NORMAL);
+ break;
+ case NL_DISCONNECT_REQUEST:
+ llc_send(linkp, LLCFT_DM, LLC_RSP, LLC_GETFLAG(linkp, F));
+ LLC_NEWSTATE(linkp, ADM);
+ break;
+ case LLCFT_SABME + LLC_CMD:
+ LLC_SETFLAG(linkp, F, pollfinal);
+ break;
+ case LLCFT_DM + LLC_RSP:
+ LLC_NEWSTATE(linkp, ADM);
+ action = LLC_DISCONNECT_INDICATION;
+ break;
+ /* all other frames effect nothing here */
+ }
+
+ return action;
+}
+
+/*
+ * RESET_WAIT --- The local connection component is waiting for the local user
+ * to indicate a RESET_REQUEST or a DISCONNECT_REQUEST.
+ */
+int
+llc_state_RESET_WAIT(struct llc_linkcb *linkp, struct llc *frame, int frame_kind,
+ int cmdrsp, int pollfinal)
+{
+ int action = 0;
+
+ switch(frame_kind + cmdrsp) {
+ case NL_RESET_REQUEST:
+ if (LLC_GETFLAG(linkp, S) == 0) {
+ llc_send(linkp, LLCFT_SABME, LLC_CMD, pollfinal);
+ LLC_SETFLAG(linkp, P, pollfinal);
+ LLC_START_ACK_TIMER(linkp);
+ linkp->llcl_retry = 0;
+ LLC_NEWSTATE(linkp, RESET);
+ } else {
+ llc_send(linkp, LLCFT_UA, LLC_RSP,
+ LLC_GETFLAG(linkp, F));
+ LLC_RESETCOUNTER(linkp);
+ LLC_SETFLAG(linkp, P, 0);
+ LLC_SETFLAG(linkp, REMOTE_BUSY, 0);
+ LLC_NEWSTATE(linkp, NORMAL);
+ action = LLC_RESET_CONFIRM;
+ }
+ break;
+ case NL_DISCONNECT_REQUEST:
+ if (LLC_GETFLAG(linkp, S) == 0) {
+ llc_send(linkp, LLCFT_DISC, LLC_CMD, pollfinal);
+ LLC_SETFLAG(linkp, P, pollfinal);
+ LLC_START_ACK_TIMER(linkp);
+ linkp->llcl_retry = 0;
+ LLC_NEWSTATE(linkp, D_CONN);
+ } else {
+ llc_send(linkp, LLCFT_DM, LLC_RSP,
+ LLC_GETFLAG(linkp, F));
+ LLC_NEWSTATE(linkp, ADM);
+ }
+ break;
+ case LLCFT_DM + LLC_RSP:
+ LLC_NEWSTATE(linkp, ADM);
+ action = LLC_DISCONNECT_INDICATION;
+ break;
+ case LLCFT_SABME + LLC_CMD:
+ LLC_SETFLAG(linkp, S, 1);
+ LLC_SETFLAG(linkp, F, pollfinal);
+ break;
+ case LLCFT_DISC + LLC_CMD:
+ llc_send(linkp, LLCFT_DM, LLC_RSP, pollfinal);
+ LLC_NEWSTATE(linkp, ADM);
+ action = LLC_DISCONNECT_INDICATION;
+ break;
+ }
+
+ return action;
+}
+
+/*
+ * RESET_CHECK --- The local connection component is waiting for the local user
+ * to accept or refuse a remote reset request.
+ */
+int
+llc_state_RESET_CHECK(struct llc_linkcb *linkp, struct llc *frame, int frame_kind,
+ int cmdrsp, int pollfinal)
+{
+ int action = 0;
+
+ switch(frame_kind + cmdrsp) {
+ case NL_RESET_RESPONSE:
+ llc_send(linkp, LLCFT_UA, LLC_RSP, LLC_GETFLAG(linkp, F));
+ LLC_RESETCOUNTER(linkp);
+ LLC_SETFLAG(linkp, P, 0);
+ LLC_SETFLAG(linkp, REMOTE_BUSY, 0);
+ LLC_NEWSTATE(linkp, NORMAL);
+ break;
+ case NL_DISCONNECT_REQUEST:
+ llc_send(linkp, LLCFT_DM, LLC_RSP, LLC_GETFLAG(linkp, F));
+ LLC_NEWSTATE(linkp, ADM);
+ break;
+ case LLCFT_DM + LLC_RSP:
+ action = LLC_DISCONNECT_INDICATION;
+ break;
+ case LLCFT_SABME + LLC_CMD:
+ LLC_SETFLAG(linkp, F, pollfinal);
+ break;
+ case LLCFT_DISC + LLC_CMD:
+ llc_send(linkp, LLCFT_DM, LLC_RSP, pollfinal);
+ LLC_NEWSTATE(linkp, ADM);
+ action = LLC_DISCONNECT_INDICATION;
+ break;
+ }
+
+ return action;
+}
+
+/*
+ * SETUP --- The connection component has transmitted an SABME command PDU to a
+ * remote LLC DSAP and is waiting for a reply.
+ */
+int
+llc_state_SETUP(struct llc_linkcb *linkp, struct llc *frame, int frame_kind,
+ int cmdrsp, int pollfinal)
+{
+ int action = 0;
+
+ switch(frame_kind + cmdrsp) {
+ case LLCFT_SABME + LLC_CMD:
+ LLC_RESETCOUNTER(linkp);
+ llc_send(linkp, LLCFT_UA, LLC_RSP, pollfinal);
+ LLC_SETFLAG(linkp, S, 1);
+ break;
+ case LLCFT_UA + LLC_RSP:
+ if (LLC_GETFLAG(linkp, P) == pollfinal) {
+ LLC_STOP_ACK_TIMER(linkp);
+ LLC_RESETCOUNTER(linkp);
+ LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal);
+ LLC_SETFLAG(linkp, REMOTE_BUSY, 0);
+ LLC_NEWSTATE(linkp, NORMAL);
+ action = LLC_CONNECT_CONFIRM;
+ }
+ break;
+ case LLC_ACK_TIMER_EXPIRED:
+ if (LLC_GETFLAG(linkp, S) == 1) {
+ LLC_SETFLAG(linkp, P, 0);
+ LLC_SETFLAG(linkp, REMOTE_BUSY, 0),
+ LLC_NEWSTATE(linkp, NORMAL);
+ action = LLC_CONNECT_CONFIRM;
+ } else if (linkp->llcl_retry < llc_n2) {
+ llc_send(linkp, LLCFT_SABME, LLC_CMD, pollfinal);
+ LLC_SETFLAG(linkp, P, pollfinal);
+ LLC_START_ACK_TIMER(linkp);
+ linkp->llcl_retry++;
+ } else {
+ LLC_NEWSTATE(linkp, ADM);
+ action = LLC_DISCONNECT_INDICATION;
+ }
+ break;
+ case LLCFT_DISC + LLC_CMD:
+ llc_send(linkp, LLCFT_DM, LLC_RSP, pollfinal);
+ LLC_STOP_ACK_TIMER(linkp);
+ LLC_NEWSTATE(linkp, ADM);
+ action = LLC_DISCONNECT_INDICATION;
+ break;
+ case LLCFT_DM + LLC_RSP:
+ LLC_STOP_ACK_TIMER(linkp);
+ LLC_NEWSTATE(linkp, ADM);
+ action = LLC_DISCONNECT_INDICATION;
+ break;
+ }
+
+ return action;
+}
+
+/*
+ * RESET --- As a result of a service access point user request or the receipt
+ * of a FRMR response PDU, the local connection component has sent an
+ * SABME command PDU to the remote LLC DSAP to reset the data link
+ * connection and is waiting for a reply.
+ */
+int
+llc_state_RESET(struct llc_linkcb *linkp, struct llc *frame, int frame_kind,
+ int cmdrsp, int pollfinal)
+{
+ int action = 0;
+
+ switch(frame_kind + cmdrsp) {
+ case LLCFT_SABME + LLC_CMD:
+ LLC_RESETCOUNTER(linkp);
+ LLC_SETFLAG(linkp, S, 1);
+ llc_send(linkp, LLCFT_UA, LLC_RSP, pollfinal);
+ break;
+ case LLCFT_UA + LLC_RSP:
+ if (LLC_GETFLAG(linkp, P) == pollfinal) {
+ LLC_STOP_ACK_TIMER(linkp);
+ LLC_RESETCOUNTER(linkp);
+ LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal);
+ LLC_SETFLAG(linkp, REMOTE_BUSY, 0);
+ LLC_NEWSTATE(linkp, NORMAL);
+ action = LLC_RESET_CONFIRM;
+ }
+ break;
+ case LLC_ACK_TIMER_EXPIRED:
+ if (LLC_GETFLAG(linkp, S) == 1) {
+ LLC_SETFLAG(linkp, P, 0);
+ LLC_SETFLAG(linkp, REMOTE_BUSY, 0);
+ LLC_NEWSTATE(linkp, NORMAL);
+ action = LLC_RESET_CONFIRM;
+ } else if (linkp->llcl_retry < llc_n2) {
+ llc_send(linkp, LLCFT_SABME, LLC_CMD, pollfinal);
+ LLC_SETFLAG(linkp, P, pollfinal);
+ LLC_START_ACK_TIMER(linkp);
+ linkp->llcl_retry++;
+ } else {
+ LLC_NEWSTATE(linkp, ADM);
+ action = LLC_DISCONNECT_INDICATION;
+ }
+ break;
+ case LLCFT_DISC + LLC_CMD:
+ llc_send(linkp, LLCFT_DM, LLC_RSP, pollfinal);
+ LLC_STOP_ACK_TIMER(linkp);
+ LLC_NEWSTATE(linkp, ADM);
+ action = LLC_DISCONNECT_INDICATION;
+ break;
+ case LLCFT_DM + LLC_RSP:
+ LLC_STOP_ACK_TIMER(linkp);
+ LLC_NEWSTATE(linkp, ADM);
+ action = LLC_DISCONNECT_INDICATION;
+ break;
+ }
+
+ return action;
+}
+
+/*
+ * D_CONN --- At the request of the service access point user, the local LLC
+ * has sent a DISC command PDU to the remote LLC DSAP and is waiting
+ * for a reply.
+ */
+int
+llc_state_D_CONN(struct llc_linkcb *linkp, struct llc *frame, int frame_kind,
+ int cmdrsp, int pollfinal)
+{
+ int action = 0;
+
+ switch(frame_kind + cmdrsp) {
+ case LLCFT_SABME + LLC_CMD:
+ llc_send(linkp, LLCFT_DM, LLC_RSP, pollfinal);
+ LLC_STOP_ACK_TIMER(linkp);
+ LLC_NEWSTATE(linkp, ADM);
+ break;
+ case LLCFT_UA + LLC_RSP:
+ if (LLC_GETFLAG(linkp, P) == pollfinal) {
+ LLC_STOP_ACK_TIMER(linkp);
+ LLC_NEWSTATE(linkp, ADM);
+ }
+ break;
+ case LLCFT_DISC + LLC_CMD:
+ llc_send(linkp, LLCFT_UA, LLC_RSP, pollfinal);
+ break;
+ case LLCFT_DM + LLC_RSP:
+ LLC_STOP_ACK_TIMER(linkp);
+ LLC_NEWSTATE(linkp, ADM);
+ break;
+ case LLC_ACK_TIMER_EXPIRED:
+ if (linkp->llcl_retry < llc_n2) {
+ llc_send(linkp, LLCFT_DISC, LLC_CMD, pollfinal);
+ LLC_SETFLAG(linkp, P, pollfinal);
+ LLC_START_ACK_TIMER(linkp);
+ linkp->llcl_retry++;
+ } else LLC_NEWSTATE(linkp, ADM);
+ break;
+ }
+
+ return action;
+}
+
+/*
+ * ERROR --- The local connection component has detected an error in a received
+ * PDU and has sent a FRMR response PDU. It is waiting for a reply from
+ * the remote connection component.
+ */
+int
+llc_state_ERROR(struct llc_linkcb *linkp, struct llc *frame, int frame_kind,
+ int cmdrsp, int pollfinal)
+{
+ int action = 0;
+
+ switch(frame_kind + cmdrsp) {
+ case LLCFT_SABME + LLC_CMD:
+ LLC_STOP_ACK_TIMER(linkp);
+ LLC_NEWSTATE(linkp, RESET_CHECK);
+ action = LLC_RESET_INDICATION_REMOTE;
+ break;
+ case LLCFT_DISC + LLC_CMD:
+ llc_send(linkp, LLCFT_UA, LLC_RSP, pollfinal);
+ LLC_STOP_ACK_TIMER(linkp);
+ LLC_NEWSTATE(linkp, ADM);
+ action = LLC_DISCONNECT_INDICATION;
+ break;
+ case LLCFT_DM + LLC_RSP:
+ LLC_STOP_ACK_TIMER(linkp);
+ LLC_NEWSTATE(linkp, ADM);
+ action = LLC_DISCONNECT_INDICATION;
+ break;
+ case LLCFT_FRMR + LLC_RSP:
+ LLC_STOP_ACK_TIMER(linkp);
+ LLC_SETFLAG(linkp, S, 0);
+ LLC_NEWSTATE(linkp, RESET_WAIT);
+ action = LLC_FRMR_RECEIVED;
+ break;
+ case LLC_ACK_TIMER_EXPIRED:
+ if (linkp->llcl_retry < llc_n2) {
+ llc_send(linkp, LLCFT_FRMR, LLC_RSP, 0);
+ LLC_START_ACK_TIMER(linkp);
+ linkp->llcl_retry++;
+ } else {
+ LLC_SETFLAG(linkp, S, 0);
+ LLC_NEWSTATE(linkp, RESET_WAIT);
+ action = LLC_RESET_INDICATION_LOCAL;
+ }
+ break;
+ default:
+ if (cmdrsp == LLC_CMD){
+ llc_send(linkp, LLCFT_FRMR, LLC_RSP, pollfinal);
+ LLC_START_ACK_TIMER(linkp);
+ }
+ break;
+
+ }
+
+ return action;
+}
+
+/*
+ * NORMAL, BUSY, REJECT, AWAIT, AWAIT_BUSY, and AWAIT_REJECT all share
+ * a common core state handler.
+ */
+int
+llc_state_NBRAcore(struct llc_linkcb *linkp, struct llc *frame, int frame_kind,
+ int cmdrsp, int pollfinal)
+{
+ int action = 0;
+
+ switch(frame_kind + cmdrsp) {
+ case NL_DISCONNECT_REQUEST:
+ llc_send(linkp, LLCFT_DISC, LLC_CMD, pollfinal);
+ LLC_SETFLAG(linkp, P, pollfinal);
+ LLC_STOP_ALL_TIMERS(linkp);
+ LLC_START_ACK_TIMER(linkp);
+ linkp->llcl_retry = 0;
+ LLC_NEWSTATE(linkp, D_CONN);
+ break;
+ case NL_RESET_REQUEST:
+ llc_send(linkp, LLCFT_SABME, LLC_CMD, pollfinal);
+ LLC_SETFLAG(linkp, P, pollfinal);
+ LLC_STOP_ALL_TIMERS(linkp);
+ LLC_START_ACK_TIMER(linkp);
+ linkp->llcl_retry = 0;
+ LLC_SETFLAG(linkp, S, 0);
+ LLC_NEWSTATE(linkp, RESET);
+ break;
+ case LLCFT_SABME + LLC_CMD:
+ LLC_SETFLAG(linkp, F, pollfinal);
+ LLC_STOP_ALL_TIMERS(linkp);
+ LLC_NEWSTATE(linkp, RESET_CHECK);
+ action = LLC_RESET_INDICATION_REMOTE;
+ break;
+ case LLCFT_DISC + LLC_CMD:
+ llc_send(linkp, LLCFT_UA, LLC_RSP, pollfinal);
+ LLC_STOP_ALL_TIMERS(linkp);
+ LLC_NEWSTATE(linkp, ADM);
+ action = LLC_DISCONNECT_INDICATION;
+ break;
+ case LLCFT_FRMR + LLC_RSP:
+ LLC_STOP_ALL_TIMERS(linkp);
+ LLC_SETFLAG(linkp, S, 0);
+ LLC_NEWSTATE(linkp, RESET_WAIT);
+ action = LLC_FRMR_RECEIVED;
+ break;
+ case LLCFT_DM + LLC_RSP:
+ LLC_STOP_ALL_TIMERS(linkp);
+ LLC_NEWSTATE(linkp, ADM);
+ action = LLC_DISCONNECT_INDICATION;
+ break;
+ case LLC_INVALID_NR + LLC_CMD:
+ case LLC_INVALID_NS + LLC_CMD:
+ LLC_SETFRMR(linkp, frame, cmdrsp,
+ (frame_kind == LLC_INVALID_NR ? LLC_FRMR_Z :
+ (LLC_FRMR_V | LLC_FRMR_W)));
+ llc_send(linkp, LLCFT_FRMR, LLC_RSP, pollfinal);
+ LLC_STOP_ALL_TIMERS(linkp);
+ LLC_START_ACK_TIMER(linkp);
+ linkp->llcl_retry = 0;
+ LLC_NEWSTATE(linkp, ERROR);
+ action = LLC_FRMR_SENT;
+ break;
+ case LLC_INVALID_NR + LLC_RSP:
+ case LLC_INVALID_NS + LLC_RSP:
+ case LLCFT_UA + LLC_RSP:
+ case LLC_BAD_PDU: {
+ char frmrcause = 0;
+
+ switch (frame_kind) {
+ case LLC_INVALID_NR: frmrcause = LLC_FRMR_Z; break;
+ case LLC_INVALID_NS: frmrcause = LLC_FRMR_V | LLC_FRMR_W; break;
+ default: frmrcause = LLC_FRMR_W;
+ }
+ LLC_SETFRMR(linkp, frame, cmdrsp, frmrcause);
+ llc_send(linkp, LLCFT_FRMR, LLC_RSP, 0);
+ LLC_STOP_ALL_TIMERS(linkp);
+ LLC_START_ACK_TIMER(linkp);
+ linkp->llcl_retry = 0;
+ LLC_NEWSTATE(linkp, ERROR);
+ action = LLC_FRMR_SENT;
+ break;
+ }
+ default:
+ if (cmdrsp == LLC_RSP && pollfinal == 1 &&
+ LLC_GETFLAG(linkp, P) == 0) {
+ LLC_SETFRMR(linkp, frame, cmdrsp, LLC_FRMR_W);
+ LLC_STOP_ALL_TIMERS(linkp);
+ LLC_START_ACK_TIMER(linkp);
+ linkp->llcl_retry = 0;
+ LLC_NEWSTATE(linkp, ERROR);
+ action = LLC_FRMR_SENT;
+ }
+ break;
+ case LLC_P_TIMER_EXPIRED:
+ case LLC_ACK_TIMER_EXPIRED:
+ case LLC_REJ_TIMER_EXPIRED:
+ case LLC_BUSY_TIMER_EXPIRED:
+ if (linkp->llcl_retry >= llc_n2) {
+ LLC_STOP_ALL_TIMERS(linkp);
+ LLC_SETFLAG(linkp, S, 0);
+ LLC_NEWSTATE(linkp, RESET_WAIT);
+ action = LLC_RESET_INDICATION_LOCAL;
+ }
+ break;
+ }
+
+ return action;
+}
+
+/*
+ * NORMAL --- A data link connection exists between the local LLC service access
+ * point and the remote LLC service access point. Sending and
+ * reception of information and supervisory PDUs can be performed.
+ */
+int
+llc_state_NORMAL(struct llc_linkcb *linkp, struct llc *frame, int frame_kind,
+ int cmdrsp, int pollfinal)
+{
+ int action = LLC_PASSITON;
+
+ switch(frame_kind + cmdrsp) {
+ case NL_DATA_REQUEST:
+ if (LLC_GETFLAG(linkp, REMOTE_BUSY) == 0) {
+#ifdef not_now
+ if (LLC_GETFLAG(linkp, P) == 0) {
+ /* multiple possibilities */
+ llc_send(linkp, LLCFT_INFO, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ if (LLC_TIMERXPIRED(linkp, ACK) != LLC_TIMER_RUNNING)
+ LLC_START_ACK_TIMER(linkp);
+ } else {
+#endif
+ /* multiple possibilities */
+ llc_send(linkp, LLCFT_INFO, LLC_CMD, 0);
+ if (LLC_TIMERXPIRED(linkp, ACK) != LLC_TIMER_RUNNING)
+ LLC_START_ACK_TIMER(linkp);
+#ifdef not_now
+ }
+#endif
+ action = 0;
+ }
+ break;
+ case LLC_LOCAL_BUSY_DETECTED:
+ if (LLC_GETFLAG(linkp, P) == 0) {
+ /* multiple possibilities --- action-wise */
+ /* multiple possibilities --- CMD/RSP-wise */
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 0);
+ LLC_START_P_TIMER(linkp);
+ LLC_SETFLAG(linkp, DATA, 0);
+ LLC_NEWSTATE(linkp, BUSY);
+ action = 0;
+ } else {
+ /* multiple possibilities --- CMD/RSP-wise */
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 0);
+ LLC_SETFLAG(linkp, DATA, 0);
+ LLC_NEWSTATE(linkp, BUSY);
+ action = 0;
+ }
+ break;
+ case LLC_INVALID_NS + LLC_CMD:
+ case LLC_INVALID_NS + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ llc_send(linkp, LLCFT_REJ, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_START_REJ_TIMER(linkp);
+ LLC_NEWSTATE(linkp, REJECT);
+ action = 0;
+ } else if (pollfinal == 0 && p == 1) {
+ llc_send(linkp, LLCFT_REJ, LLC_CMD, 0);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_START_REJ_TIMER(linkp);
+ LLC_NEWSTATE(linkp, REJECT);
+ action = 0;
+ } else if ((pollfinal == 0 && p == 0) ||
+ (pollfinal == 1 && p == 1 && cmdrsp == LLC_RSP)) {
+ llc_send(linkp, LLCFT_REJ, LLC_CMD, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_START_P_TIMER(linkp);
+ LLC_START_REJ_TIMER(linkp);
+ if (cmdrsp == LLC_RSP && pollfinal == 1) {
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ } else action = 0;
+ LLC_NEWSTATE(linkp, REJECT);
+ }
+ break;
+ }
+ case LLCFT_INFO + LLC_CMD:
+ case LLCFT_INFO + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ LLC_INC(linkp->llcl_vr);
+ LLC_SENDACKNOWLEDGE(linkp, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ action = LLC_DATA_INDICATION;
+ } else if (pollfinal == 0 && p == 1) {
+ LLC_INC(linkp->llcl_vr);
+ LLC_SENDACKNOWLEDGE(linkp, LLC_CMD, 0);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ action = LLC_DATA_INDICATION;
+ } else if ((pollfinal == 0 && p == 0 && cmdrsp == LLC_CMD) ||
+ (pollfinal == p && cmdrsp == LLC_RSP)) {
+ LLC_INC(linkp->llcl_vr);
+ LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal);
+ LLC_SENDACKNOWLEDGE(linkp, LLC_CMD, 0);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ if (cmdrsp == LLC_RSP && pollfinal == 1)
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ action = LLC_DATA_INDICATION;
+ }
+ break;
+ }
+ case LLCFT_RR + LLC_CMD:
+ case LLCFT_RR + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ LLC_SENDACKNOWLEDGE(linkp, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ } else if ((pollfinal == 0) ||
+ (cmdrsp == LLC_RSP && pollfinal == 1 && p == 1)) {
+ LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ }
+ break;
+ }
+ case LLCFT_RNR + LLC_CMD:
+ case LLCFT_RNR + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ llc_send(linkp, LLCFT_RR, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_SET_REMOTE_BUSY(linkp, action);
+ } else if ((pollfinal == 0) ||
+ (cmdrsp == LLC_RSP && pollfinal == 1 && p == 1)) {
+ LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_SET_REMOTE_BUSY(linkp, action);
+ }
+ break;
+ }
+ case LLCFT_REJ + LLC_CMD:
+ case LLCFT_REJ + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ linkp->llcl_vs = nr;
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ llc_resend(linkp, LLC_RSP, 1);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ } else if (pollfinal == 0 && p == 1) {
+ linkp->llcl_vs = nr;
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ llc_resend(linkp, LLC_CMD, 0);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ } else if ((pollfinal == 0 && p == 0 && cmdrsp == LLC_CMD) ||
+ (pollfinal == p && cmdrsp == LLC_RSP)) {
+ linkp->llcl_vs = nr;
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_START_P_TIMER(linkp);
+ llc_resend(linkp, LLC_CMD, 1);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ }
+ break;
+ }
+ case NL_INITIATE_PF_CYCLE:
+ if (LLC_GETFLAG(linkp, P) == 0) {
+ llc_send(linkp, LLCFT_RR, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ action = 0;
+ }
+ break;
+ case LLC_P_TIMER_EXPIRED:
+ if (linkp->llcl_retry < llc_n2) {
+ llc_send(linkp, LLCFT_RR, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ linkp->llcl_retry++;
+ LLC_NEWSTATE(linkp, AWAIT);
+ action = 0;
+ }
+ break;
+ case LLC_ACK_TIMER_EXPIRED:
+ case LLC_BUSY_TIMER_EXPIRED:
+ if ((LLC_GETFLAG(linkp, P) == 0)
+ && (linkp->llcl_retry < llc_n2)) {
+ llc_send(linkp, LLCFT_RR, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ linkp->llcl_retry++;
+ LLC_NEWSTATE(linkp, AWAIT);
+ action = 0;
+ }
+ break;
+ }
+ if (action == LLC_PASSITON)
+ action = llc_state_NBRAcore(linkp, frame, frame_kind,
+ cmdrsp, pollfinal);
+
+ return action;
+}
+
+/*
+ * BUSY --- A data link connection exists between the local LLC service access
+ * point and the remote LLC service access point. I PDUs may be sent.
+ * Local conditions make it likely that the information feld of
+ * received I PDUs will be ignored. Supervisory PDUs may be both sent
+ * and received.
+ */
+int
+llc_state_BUSY(struct llc_linkcb *linkp, struct llc *frame, int frame_kind,
+ int cmdrsp, int pollfinal)
+{
+ int action = LLC_PASSITON;
+
+ switch(frame_kind + cmdrsp) {
+ case NL_DATA_REQUEST:
+ if (LLC_GETFLAG(linkp, REMOTE_BUSY) == 0)
+ if (LLC_GETFLAG(linkp, P) == 0) {
+ llc_send(linkp, LLCFT_INFO, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ if (LLC_TIMERXPIRED(linkp, ACK) != LLC_TIMER_RUNNING)
+ LLC_START_ACK_TIMER(linkp);
+ action = 0;
+ } else {
+ llc_send(linkp, LLCFT_INFO, LLC_CMD, 0);
+ if (LLC_TIMERXPIRED(linkp, ACK) != LLC_TIMER_RUNNING)
+ LLC_START_ACK_TIMER(linkp);
+ action = 0;
+ }
+ break;
+ case LLC_LOCAL_BUSY_CLEARED: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int df = LLC_GETFLAG(linkp, DATA);
+
+ switch (df) {
+ case 1:
+ if (p == 0) {
+ /* multiple possibilities */
+ llc_send(linkp, LLCFT_REJ, LLC_CMD, 1);
+ LLC_START_REJ_TIMER(linkp);
+ LLC_START_P_TIMER(linkp);
+ LLC_NEWSTATE(linkp, REJECT);
+ action = 0;
+ } else {
+ llc_send(linkp, LLCFT_REJ, LLC_CMD, 0);
+ LLC_START_REJ_TIMER(linkp);
+ LLC_NEWSTATE(linkp, REJECT);
+ action = 0;
+ }
+ break;
+ case 0:
+ if (p == 0) {
+ /* multiple possibilities */
+ llc_send(linkp, LLCFT_RR, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ LLC_NEWSTATE(linkp, NORMAL);
+ action = 0;
+ } else {
+ llc_send(linkp, LLCFT_RR, LLC_CMD, 0);
+ LLC_NEWSTATE(linkp, NORMAL);
+ action = 0;
+ }
+ break;
+ case 2:
+ if (p == 0) {
+ /* multiple possibilities */
+ llc_send(linkp, LLCFT_RR, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ LLC_NEWSTATE(linkp, REJECT);
+ action = 0;
+ } else {
+ llc_send(linkp, LLCFT_RR, LLC_CMD, 0);
+ LLC_NEWSTATE(linkp, REJECT);
+ action =0;
+ }
+ break;
+ }
+ break;
+ }
+ case LLC_INVALID_NS + LLC_CMD:
+ case LLC_INVALID_NS + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ llc_send(linkp, LLCFT_RNR, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ if (LLC_GETFLAG(linkp, DATA) == 0)
+ LLC_SETFLAG(linkp, DATA, 1);
+ action = 0;
+ } else if ((cmdrsp == LLC_CMD && pollfinal == 0 && p == 0) ||
+ (cmdrsp == LLC_RSP && pollfinal == p)) {
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 0);
+ LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ if (LLC_GETFLAG(linkp, DATA) == 0)
+ LLC_SETFLAG(linkp, DATA, 1);
+ if (cmdrsp == LLC_RSP && pollfinal == 1) {
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ } else action = 0;
+ } else if (pollfinal == 0 && p == 1) {
+ llc_send(linkp, LLCFT_RNR, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ if (LLC_GETFLAG(linkp, DATA) == 0)
+ LLC_SETFLAG(linkp, DATA, 1);
+ action = 0;
+ }
+ break;
+ }
+ case LLCFT_INFO + LLC_CMD:
+ case LLCFT_INFO + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ LLC_INC(linkp->llcl_vr);
+ llc_send(linkp, LLCFT_RNR, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ if (LLC_GETFLAG(linkp, DATA) == 2)
+ LLC_STOP_REJ_TIMER(linkp);
+ LLC_SETFLAG(linkp, DATA, 0);
+ action = LLC_DATA_INDICATION;
+ } else if ((cmdrsp == LLC_CMD && pollfinal == 0 && p == 0) ||
+ (cmdrsp == LLC_RSP && pollfinal == p)) {
+ LLC_INC(linkp->llcl_vr);
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ if (LLC_GETFLAG(linkp, DATA) == 2)
+ LLC_STOP_REJ_TIMER(linkp);
+ if (cmdrsp == LLC_RSP && pollfinal == 1)
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ action = LLC_DATA_INDICATION;
+ } else if (pollfinal == 0 && p == 1) {
+ LLC_INC(linkp->llcl_vr);
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 0);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ if (LLC_GETFLAG(linkp, DATA) == 2)
+ LLC_STOP_REJ_TIMER(linkp);
+ LLC_SETFLAG(linkp, DATA, 0);
+ action = LLC_DATA_INDICATION;
+ }
+ break;
+ }
+ case LLCFT_RR + LLC_CMD:
+ case LLCFT_RR + LLC_RSP:
+ case LLCFT_RNR + LLC_CMD:
+ case LLCFT_RNR + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ llc_send(linkp, LLCFT_RNR, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ if (frame_kind == LLCFT_RR) {
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ } else {
+ LLC_SET_REMOTE_BUSY(linkp, action);
+ }
+ } else if (pollfinal = 0 ||
+ (cmdrsp == LLC_RSP && pollfinal == 1)) {
+ LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ if (frame_kind == LLCFT_RR) {
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ } else {
+ LLC_SET_REMOTE_BUSY(linkp, action);
+ }
+ }
+ break;
+ }
+ case LLCFT_REJ + LLC_CMD:
+ case LLCFT_REJ + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ linkp->llcl_vs = nr;
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ llc_send(linkp, LLCFT_RNR, LLC_RSP, 1);
+ llc_resend(linkp, LLC_CMD, 0);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ } else if ((cmdrsp == LLC_CMD && pollfinal == 0 && p == 0) ||
+ (cmdrsp == LLC_RSP && pollfinal == p)) {
+ linkp->llcl_vs = nr;
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal);
+ llc_resend(linkp, LLC_CMD, 0);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ } else if (pollfinal == 0 && p == 1) {
+ linkp->llcl_vs = nr;
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ llc_resend(linkp, LLC_CMD, 0);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ }
+ break;
+ }
+ case NL_INITIATE_PF_CYCLE:
+ if (LLC_GETFLAG(linkp, P) == 0) {
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ action = 0;
+ }
+ break;
+ case LLC_P_TIMER_EXPIRED:
+ /* multiple possibilities */
+ if (linkp->llcl_retry < llc_n2) {
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ linkp->llcl_retry++;
+ LLC_NEWSTATE(linkp, AWAIT_BUSY);
+ action = 0;
+ }
+ break;
+ case LLC_ACK_TIMER_EXPIRED:
+ case LLC_BUSY_TIMER_EXPIRED:
+ if (LLC_GETFLAG(linkp, P) == 0 && linkp->llcl_retry < llc_n2) {
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ linkp->llcl_retry++;
+ LLC_NEWSTATE(linkp, AWAIT_BUSY);
+ action = 0;
+ }
+ break;
+ case LLC_REJ_TIMER_EXPIRED:
+ if (linkp->llcl_retry < llc_n2)
+ if (LLC_GETFLAG(linkp, P) == 0) {
+ /* multiple possibilities */
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ linkp->llcl_retry++;
+ LLC_SETFLAG(linkp, DATA, 1);
+ LLC_NEWSTATE(linkp, AWAIT_BUSY);
+ action = 0;
+ } else{
+ LLC_SETFLAG(linkp, DATA, 1);
+ LLC_NEWSTATE(linkp, BUSY);
+ action = 0;
+ }
+
+ break;
+ }
+ if (action == LLC_PASSITON)
+ action = llc_state_NBRAcore(linkp, frame, frame_kind,
+ cmdrsp, pollfinal);
+
+ return action;
+}
+
+/*
+ * REJECT --- A data link connection exists between the local LLC service
+ * access point and the remote LLC service access point. The local
+ * connection component has requested that the remote connection
+ * component resend a specific I PDU that the local connection
+ * componnent has detected as being out of sequence. Both I PDUs and
+ * supervisory PDUs may be sent and received.
+ */
+int
+llc_state_REJECT(struct llc_linkcb *linkp, struct llc *frame, int frame_kind,
+ int cmdrsp, int pollfinal)
+{
+ int action = LLC_PASSITON;
+
+ switch(frame_kind + cmdrsp) {
+ case NL_DATA_REQUEST:
+ if (LLC_GETFLAG(linkp, P) == 0) {
+ llc_send(linkp, LLCFT_INFO, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ if (LLC_TIMERXPIRED(linkp, ACK) != LLC_TIMER_RUNNING)
+ LLC_START_ACK_TIMER(linkp);
+ LLC_NEWSTATE(linkp, REJECT);
+ action = 0;
+ } else {
+ llc_send(linkp, LLCFT_INFO, LLC_CMD, 0);
+ if (LLC_TIMERXPIRED(linkp, ACK) != LLC_TIMER_RUNNING)
+ LLC_START_ACK_TIMER(linkp);
+ LLC_NEWSTATE(linkp, REJECT);
+ action = 0;
+ }
+ break;
+ case NL_LOCAL_BUSY_DETECTED:
+ if (LLC_GETFLAG(linkp, P) == 0) {
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ LLC_SETFLAG(linkp, DATA, 2);
+ LLC_NEWSTATE(linkp, BUSY);
+ action = 0;
+ } else {
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 0);
+ LLC_SETFLAG(linkp, DATA, 2);
+ LLC_NEWSTATE(linkp, BUSY);
+ action = 0;
+ }
+ break;
+ case LLC_INVALID_NS + LLC_CMD:
+ case LLC_INVALID_NS + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ llc_send(linkp, LLCFT_RR, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ action = 0;
+ } else if (pollfinal == 0 ||
+ (cmdrsp == LLC_RSP && pollfinal == 1 && p == 1)) {
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal);
+ if (cmdrsp == LLC_RSP && pollfinal == 1) {
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ } else action = 0;
+ }
+ break;
+ }
+ case LLCFT_INFO + LLC_CMD:
+ case LLCFT_INFO + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ LLC_INC(linkp->llcl_vr);
+ LLC_SENDACKNOWLEDGE(linkp, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_STOP_REJ_TIMER(linkp);
+ LLC_NEWSTATE(linkp, NORMAL);
+ action = LLC_DATA_INDICATION;
+ } else if ((cmdrsp = LLC_RSP && pollfinal == p) ||
+ (cmdrsp == LLC_CMD && pollfinal == 0 && p == 0)) {
+ LLC_INC(linkp->llcl_vr);
+ LLC_SENDACKNOWLEDGE(linkp, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ if (cmdrsp == LLC_RSP && pollfinal == 1)
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ LLC_STOP_REJ_TIMER(linkp);
+ LLC_NEWSTATE(linkp, NORMAL);
+ action = LLC_DATA_INDICATION;
+ } else if (pollfinal == 0 && p == 1) {
+ LLC_INC(linkp->llcl_vr);
+ LLC_SENDACKNOWLEDGE(linkp, LLC_CMD, 0);
+ LLC_STOP_REJ_TIMER(linkp);
+ LLC_NEWSTATE(linkp, NORMAL);
+ action = LLC_DATA_INDICATION;
+ }
+ break;
+ }
+ case LLCFT_RR + LLC_CMD:
+ case LLCFT_RR + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ LLC_SENDACKNOWLEDGE(linkp, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ } else if (pollfinal == 0 ||
+ (cmdrsp == LLC_RSP && pollfinal == 1 && p == 1)) {
+ LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ }
+ break;
+ }
+ case LLCFT_RNR + LLC_CMD:
+ case LLCFT_RNR + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ llc_send(linkp, LLCFT_RR, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_SET_REMOTE_BUSY(linkp, action);
+ } else if (pollfinal == 0 ||
+ (cmdrsp == LLC_RSP && pollfinal == 1 && p == 1)) {
+ LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ action = 0;
+ }
+ break;
+ }
+ case LLCFT_REJ + LLC_CMD:
+ case LLCFT_REJ + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ linkp->llcl_vs = nr;
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ llc_resend(linkp, LLC_RSP, 1);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ } else if ((cmdrsp == LLC_CMD && pollfinal == 0 && p == 0) ||
+ (cmdrsp == LLC_RSP && pollfinal == p)) {
+ linkp->llcl_vs = nr;
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal);
+ llc_resend(linkp, LLC_CMD, 0);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ } else if (pollfinal == 0 && p == 1) {
+ linkp->llcl_vs = nr;
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ llc_resend(linkp, LLC_CMD, 0);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ }
+ break;
+ }
+ case NL_INITIATE_PF_CYCLE:
+ if (LLC_GETFLAG(linkp, P) == 0) {
+ llc_send(linkp, LLCFT_RR, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ action = 0;
+ }
+ break;
+ case LLC_REJ_TIMER_EXPIRED:
+ if (LLC_GETFLAG(linkp, P) == 0 && linkp->llcl_retry < llc_n2) {
+ llc_send(linkp, LLCFT_REJ, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ LLC_START_REJ_TIMER(linkp);
+ linkp->llcl_retry++;
+ action = 0;
+ }
+ case LLC_P_TIMER_EXPIRED:
+ if (linkp->llcl_retry < llc_n2) {
+ llc_send(linkp, LLCFT_RR, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ LLC_START_REJ_TIMER(linkp);
+ linkp->llcl_retry++;
+ LLC_NEWSTATE(linkp, AWAIT_REJECT);
+ action = 0;
+ }
+ break;
+ case LLC_ACK_TIMER_EXPIRED:
+ case LLC_BUSY_TIMER_EXPIRED:
+ if (LLC_GETFLAG(linkp, P) == 0 && linkp->llcl_retry < llc_n2) {
+ llc_send(linkp, LLCFT_RR, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ LLC_START_REJ_TIMER(linkp);
+ linkp->llcl_retry++;
+ /*
+ * I cannot locate the description of RESET_V(S)
+ * in ISO 8802-2, table 7-1, state REJECT, last event,
+ * and assume they meant to set V(S) to 0 ...
+ */
+ linkp->llcl_vs = 0; /* XXX */
+ LLC_NEWSTATE(linkp, AWAIT_REJECT);
+ action = 0;
+ }
+
+ break;
+ }
+ if (action == LLC_PASSITON)
+ action = llc_state_NBRAcore(linkp, frame, frame_kind,
+ cmdrsp, pollfinal);
+
+ return action;
+}
+
+/*
+ * AWAIT --- A data link connection exists between the local LLC service access
+ * point and the remote LLC service access point. The local LLC is
+ * performing a timer recovery operation and has sent a command PDU
+ * with the P bit set to ``1'', and is awaiting an acknowledgement
+ * from the remote LLC. I PDUs may be received but not sent.
+ * Supervisory PDUs may be both sent and received.
+ */
+int
+llc_state_AWAIT(struct llc_linkcb *linkp, struct llc *frame, int frame_kind,
+ int cmdrsp, int pollfinal)
+{
+ int action = LLC_PASSITON;
+
+ switch(frame_kind + cmdrsp) {
+ case LLC_LOCAL_BUSY_DETECTED:
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 0);
+ LLC_SETFLAG(linkp, DATA, 0);
+ LLC_NEWSTATE(linkp, AWAIT_BUSY);
+ action = 0;
+ break;
+ case LLC_INVALID_NS + LLC_CMD:
+ case LLC_INVALID_NS + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ llc_send(linkp, LLCFT_REJ, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_START_REJ_TIMER(linkp);
+ LLC_NEWSTATE(linkp, AWAIT_REJECT);
+ action = 0;
+ } else if (cmdrsp == LLC_RSP && pollfinal == 1) {
+ llc_send(linkp, LLCFT_REJ, LLC_CMD, 0);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ linkp->llcl_vs = nr;
+ LLC_STOP_P_TIMER(linkp);
+ llc_resend(linkp, LLC_CMD, 0);
+ LLC_START_REJ_TIMER(linkp);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ LLC_NEWSTATE(linkp, REJECT);
+ } else if (pollfinal == 0) {
+ llc_send(linkp, LLCFT_REJ, LLC_CMD, 0);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_START_REJ_TIMER(linkp);
+ LLC_NEWSTATE(linkp, AWAIT_REJECT);
+ action = 0;
+ }
+ break;
+ }
+ case LLCFT_INFO + LLC_RSP:
+ case LLCFT_INFO + LLC_CMD: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ LLC_INC(linkp->llcl_vr);
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ llc_send(linkp, LLCFT_RR, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ action = LLC_DATA_INDICATION;
+ } else if (cmdrsp == LLC_RSP && pollfinal == 1) {
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ linkp->llcl_vs = nr;
+ llc_resend(linkp, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ LLC_NEWSTATE(linkp, NORMAL);
+ action = LLC_DATA_INDICATION;
+ } else if (pollfinal == 0) {
+ llc_send(linkp, LLCFT_RR, LLC_CMD, 0);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ action = LLC_DATA_INDICATION;
+ }
+ break;
+ }
+ case LLCFT_RR + LLC_CMD:
+ case LLCFT_RR + LLC_RSP:
+ case LLCFT_REJ + LLC_CMD:
+ case LLCFT_REJ + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ llc_send(linkp, LLCFT_RR, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ } else if (cmdrsp == LLC_RSP && pollfinal == 1) {
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ linkp->llcl_vs = nr;
+ LLC_STOP_P_TIMER(linkp);
+ llc_resend(linkp, LLC_CMD, 0);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ LLC_NEWSTATE(linkp, NORMAL);
+ } else if (pollfinal == 0) {
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ }
+ break;
+ }
+ case LLCFT_RNR + LLC_CMD:
+ case LLCFT_RNR + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (pollfinal == 1 && cmdrsp == LLC_CMD) {
+ llc_send(linkp, LLCFT_RR, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_SET_REMOTE_BUSY(linkp, action);
+ } else if (pollfinal == 1 && cmdrsp == LLC_RSP) {
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ linkp->llcl_vs = nr;
+ LLC_STOP_P_TIMER(linkp);
+ LLC_SET_REMOTE_BUSY(linkp, action);
+ LLC_NEWSTATE(linkp, NORMAL);
+ } else if (pollfinal == 0) {
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_SET_REMOTE_BUSY(linkp, action);
+ }
+ break;
+ }
+ case LLC_P_TIMER_EXPIRED:
+ if (linkp->llcl_retry < llc_n2) {
+ llc_send(linkp, LLCFT_RR, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ linkp->llcl_retry++;
+ action = 0;
+ }
+ break;
+ }
+ if (action == LLC_PASSITON)
+ action = llc_state_NBRAcore(linkp, frame, frame_kind,
+ cmdrsp, pollfinal);
+
+ return action;
+}
+
+/*
+ * AWAIT_BUSY --- A data link connection exists between the local LLC service
+ * access point and the remote LLC service access point. The
+ * local LLC is performing a timer recovery operation and has
+ * sent a command PDU with the P bit set to ``1'', and is
+ * awaiting an acknowledgement from the remote LLC. I PDUs may
+ * not be sent. Local conditions make it likely that the
+ * information feld of receoved I PDUs will be ignored.
+ * Supervisory PDUs may be both sent and received.
+ */
+int
+llc_state_AWAIT_BUSY(struct llc_linkcb *linkp, struct llc *frame, int frame_kind,
+ int cmdrsp, int pollfinal)
+{
+ int action = LLC_PASSITON;
+
+ switch(frame_kind + cmdrsp) {
+ case LLC_LOCAL_BUSY_CLEARED:
+ switch (LLC_GETFLAG(linkp, DATA)) {
+ case 1:
+ llc_send(linkp, LLCFT_REJ, LLC_CMD, 0);
+ LLC_START_REJ_TIMER(linkp);
+ LLC_NEWSTATE(linkp, AWAIT_REJECT);
+ action = 0;
+ break;
+ case 0:
+ llc_send(linkp, LLCFT_RR, LLC_CMD, 0);
+ LLC_NEWSTATE(linkp, AWAIT);
+ action = 0;
+ break;
+ case 2:
+ llc_send(linkp, LLCFT_RR, LLC_CMD, 0);
+ LLC_NEWSTATE(linkp, AWAIT_REJECT);
+ action = 0;
+ break;
+ }
+ break;
+ case LLC_INVALID_NS + LLC_CMD:
+ case LLC_INVALID_NS + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ llc_send(linkp, LLCFT_RNR, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_SETFLAG(linkp, DATA, 1);
+ action = 0;
+ } else if (cmdrsp == LLC_RSP && pollfinal == 1) {
+ /* optionally */
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 0);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ linkp->llcl_vs = nr;
+ LLC_STOP_P_TIMER(linkp);
+ LLC_SETFLAG(linkp, DATA, 1);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ llc_resend(linkp, LLC_CMD, 0);
+ LLC_NEWSTATE(linkp, BUSY);
+ } else if (pollfinal == 0) {
+ /* optionally */
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 0);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_SETFLAG(linkp, DATA, 1);
+ action = 0;
+ }
+ }
+ case LLCFT_INFO + LLC_CMD:
+ case LLCFT_INFO + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ llc_send(linkp, LLCFT_RNR, LLC_RSP, 1);
+ LLC_INC(linkp->llcl_vr);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_SETFLAG(linkp, DATA, 0);
+ action = LLC_DATA_INDICATION;
+ } else if (cmdrsp == LLC_RSP && pollfinal == 1) {
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 1);
+ LLC_INC(linkp->llcl_vr);
+ LLC_START_P_TIMER(linkp);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ linkp->llcl_vs = nr;
+ LLC_SETFLAG(linkp, DATA, 0);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ llc_resend(linkp, LLC_CMD, 0);
+ LLC_NEWSTATE(linkp, BUSY);
+ action = LLC_DATA_INDICATION;
+ } else if (pollfinal == 0) {
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 0);
+ LLC_INC(linkp->llcl_vr);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_SETFLAG(linkp, DATA, 0);
+ action = LLC_DATA_INDICATION;
+ }
+ break;
+ }
+ case LLCFT_RR + LLC_CMD:
+ case LLCFT_REJ + LLC_CMD:
+ case LLCFT_RR + LLC_RSP:
+ case LLCFT_REJ + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ llc_send(linkp, LLCFT_RNR, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ } else if (cmdrsp == LLC_RSP && pollfinal == 1) {
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ linkp->llcl_vs = nr;
+ LLC_STOP_P_TIMER(linkp);
+ llc_resend(linkp, LLC_CMD, 0);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ LLC_NEWSTATE(linkp, BUSY);
+ } else if (pollfinal == 0) {
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ linkp->llcl_vs = nr;
+ LLC_STOP_P_TIMER(linkp);
+ llc_resend(linkp, LLC_CMD, 0);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ }
+ break;
+ }
+ case LLCFT_RNR + LLC_CMD:
+ case LLCFT_RNR + LLC_RSP: {
+ register int p = LLC_GETFLAG(linkp, P);
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ llc_send(linkp, LLCFT_RNR, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_SET_REMOTE_BUSY(linkp, action);
+ } else if (cmdrsp == LLC_RSP && pollfinal == 1) {
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ linkp->llcl_vs = nr;
+ LLC_STOP_P_TIMER(linkp);
+ LLC_SET_REMOTE_BUSY(linkp, action);
+ LLC_NEWSTATE(linkp, BUSY);
+ } else if (pollfinal == 0) {
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_SET_REMOTE_BUSY(linkp, action);
+ }
+ break;
+ }
+ case LLC_P_TIMER_EXPIRED:
+ if (linkp->llcl_retry < llc_n2) {
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ linkp->llcl_retry++;
+ action = 0;
+ }
+ break;
+ }
+ if (action == LLC_PASSITON)
+ action = llc_state_NBRAcore(linkp, frame, frame_kind,
+ cmdrsp, pollfinal);
+
+ return action;
+}
+
+/*
+ * AWAIT_REJECT --- A data link connection exists between the local LLC service
+ * access point and the remote LLC service access point. The
+ * local connection component has requested that the remote
+ * connection component re-transmit a specific I PDU that the
+ * local connection component has detected as being out of
+ * sequence. Before the local LLC entered this state it was
+ * performing a timer recovery operation and had sent a
+ * command PDU with the P bit set to ``1'', and is still
+ * awaiting an acknowledgment from the remote LLC. I PDUs may
+ * be received but not transmitted. Supervisory PDUs may be
+ * both transmitted and received.
+ */
+int
+llc_state_AWAIT_REJECT(struct llc_linkcb *linkp, struct llc *frame, int frame_kind,
+ int cmdrsp, int pollfinal)
+{
+ int action = LLC_PASSITON;
+
+ switch(frame_kind + cmdrsp) {
+ case LLC_LOCAL_BUSY_DETECTED:
+ llc_send(linkp, LLCFT_RNR, LLC_CMD, 0);
+ LLC_SETFLAG(linkp, DATA, 2);
+ LLC_NEWSTATE(linkp, AWAIT_BUSY);
+ action = 0;
+ break;
+ case LLC_INVALID_NS + LLC_CMD:
+ case LLC_INVALID_NS + LLC_RSP: {
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ llc_send(linkp, LLCFT_RR, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ action = 0;
+ } else if (cmdrsp == LLC_RSP && pollfinal == 1) {
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ linkp->llcl_vs = nr;
+ llc_resend(linkp, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ LLC_NEWSTATE(linkp, REJECT);
+ } else if (pollfinal == 0) {
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ action = 0;
+ }
+ break;
+ }
+ case LLCFT_INFO + LLC_CMD:
+ case LLCFT_INFO + LLC_RSP: {
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ LLC_INC(linkp->llcl_vr);
+ llc_send(linkp, LLCFT_RR, LLC_RSP, 1);
+ LLC_STOP_REJ_TIMER(linkp);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_NEWSTATE(linkp, AWAIT);
+ action = LLC_DATA_INDICATION;
+ } else if (cmdrsp == LLC_RSP && pollfinal == 1) {
+ LLC_INC(linkp->llcl_vr);
+ LLC_STOP_P_TIMER(linkp);
+ LLC_STOP_REJ_TIMER(linkp);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ linkp->llcl_vs = nr;
+ llc_resend(linkp, LLC_CMD, 0);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ LLC_NEWSTATE(linkp, NORMAL);
+ action = LLC_DATA_INDICATION;
+ } else if (pollfinal == 0) {
+ LLC_INC(linkp->llcl_vr);
+ llc_send(linkp, LLCFT_RR, LLC_CMD, 0);
+ LLC_STOP_REJ_TIMER(linkp);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_NEWSTATE(linkp, AWAIT);
+ action = LLC_DATA_INDICATION;
+ }
+ break;
+ }
+ case LLCFT_RR + LLC_CMD:
+ case LLCFT_REJ + LLC_CMD:
+ case LLCFT_RR + LLC_RSP:
+ case LLCFT_REJ + LLC_RSP: {
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ llc_send(linkp, LLCFT_RR, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ } else if (cmdrsp == LLC_RSP && pollfinal == 1) {
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ linkp->llcl_vs = nr;
+ llc_resend(linkp, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ LLC_NEWSTATE(linkp, REJECT);
+ } else if (pollfinal == 0) {
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_CLEAR_REMOTE_BUSY(linkp, action);
+ }
+ break;
+ }
+ case LLCFT_RNR + LLC_CMD:
+ case LLCFT_RNR + LLC_RSP: {
+ register int nr = LLCGBITS(frame->llc_control_ext, s_nr);
+
+ if (cmdrsp == LLC_CMD && pollfinal == 1) {
+ llc_send(linkp, LLCFT_RR, LLC_RSP, 1);
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_SET_REMOTE_BUSY(linkp, action);
+ } else if (cmdrsp == LLC_RSP && pollfinal == 1) {
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ linkp->llcl_vs = nr;
+ LLC_STOP_P_TIMER(linkp);
+ LLC_SET_REMOTE_BUSY(linkp, action);
+ LLC_NEWSTATE(linkp, REJECT);
+ } else if (pollfinal == 0) {
+ LLC_UPDATE_NR_RECEIVED(linkp, nr);
+ LLC_SET_REMOTE_BUSY(linkp, action);
+ }
+ break;
+ }
+ case LLC_P_TIMER_EXPIRED:
+ if (linkp->llcl_retry < llc_n2) {
+ llc_send(linkp, LLCFT_REJ, LLC_CMD, 1);
+ LLC_START_P_TIMER(linkp);
+ linkp->llcl_retry++;
+ action = 0;
+ }
+ break;
+ }
+ if (action == LLC_PASSITON)
+ action = llc_state_NBRAcore(linkp, frame, frame_kind,
+ cmdrsp, pollfinal);
+
+ return action;
+}
+
+
+/*
+ * llc_statehandler() --- Wrapper for llc_state_*() functions.
+ * Deals with action codes and checks for
+ * ``stuck'' links.
+ */
+
+int
+llc_statehandler(struct llc_linkcb *linkp, struct llc *frame, int frame_kind,
+ int cmdrsp, int pollfinal)
+{
+ register int action = 0;
+
+ /*
+ * To check for ``zombie'' links each time llc_statehandler() gets called
+ * the AGE timer of linkp is reset. If it expires llc_timer() will
+ * take care of the link --- i.e. kill it 8=)
+ */
+ LLC_STARTTIMER(linkp, AGE);
+
+ /*
+ * Now call the current statehandler function.
+ */
+ action = (*linkp->llcl_statehandler)(linkp, frame, frame_kind,
+ cmdrsp, pollfinal);
+once_more_and_again:
+ switch (action) {
+ case LLC_CONNECT_INDICATION: {
+ int naction;
+
+ LLC_TRACE(linkp, LLCTR_INTERESTING, "CONNECT INDICATION");
+ linkp->llcl_nlnext =
+ (*linkp->llcl_sapinfo->si_ctlinput)
+ (PRC_CONNECT_INDICATION,
+ (struct sockaddr *) &linkp->llcl_addr, (caddr_t) linkp);
+ if (linkp->llcl_nlnext == 0)
+ naction = NL_DISCONNECT_REQUEST;
+ else naction = NL_CONNECT_RESPONSE;
+ action = (*linkp->llcl_statehandler)(linkp, frame, naction, 0, 0);
+ goto once_more_and_again;
+ }
+ case LLC_CONNECT_CONFIRM:
+ /* llc_resend(linkp, LLC_CMD, 0); */
+ llc_start(linkp);
+ break;
+ case LLC_DISCONNECT_INDICATION:
+ LLC_TRACE(linkp, LLCTR_INTERESTING, "DISCONNECT INDICATION");
+ (*linkp->llcl_sapinfo->si_ctlinput)
+ (PRC_DISCONNECT_INDICATION,
+ (struct sockaddr *) &linkp->llcl_addr, linkp->llcl_nlnext);
+ break;
+ /* internally visible only */
+ case LLC_RESET_CONFIRM:
+ case LLC_RESET_INDICATION_LOCAL:
+ /*
+ * not much we can do here, the state machine either makes it or
+ * brakes it ...
+ */
+ break;
+ case LLC_RESET_INDICATION_REMOTE:
+ LLC_TRACE(linkp, LLCTR_SHOULDKNOW, "RESET INDICATION (REMOTE)");
+ action = (*linkp->llcl_statehandler)(linkp, frame,
+ NL_RESET_RESPONSE, 0, 0);
+ goto once_more_and_again;
+ case LLC_FRMR_SENT:
+ LLC_TRACE(linkp, LLCTR_URGENT, "FRMR SENT");
+ break;
+ case LLC_FRMR_RECEIVED:
+ LLC_TRACE(linkp, LLCTR_URGEN, "FRMR RECEIVED");
+ action = (*linkp->llcl_statehandler)(linkp, frame,
+ NL_RESET_REQUEST, 0, 0);
+
+ goto once_more_and_again;
+ case LLC_REMOTE_BUSY:
+ LLC_TRACE(linkp, LLCTR_SHOULDKNOW, "REMOTE BUSY");
+ break;
+ case LLC_REMOTE_NOT_BUSY:
+ LLC_TRACE(linkp, LLCTR_SHOULDKNOW, "REMOTE BUSY CLEARED");
+ /*
+ * try to get queued frames out
+ */
+ llc_start(linkp);
+ break;
+ }
+
+ /*
+ * Only LLC_DATA_INDICATION is for the time being
+ * passed up to the network layer entity.
+ * The remaining action codes are for the time
+ * being visible internally only.
+ * However, this can/may be changed if necessary.
+ */
+
+ return action;
+}
+
+
+/*
+ * Core LLC2 routines
+ */
+
+/*
+ * The INIT call. This routine is called once after the system is booted.
+ */
+
+llc_init()
+{
+ llcintrq.ifq_maxlen = IFQ_MAXLEN;
+}
+
+
+/*
+ * In case of a link reset we need to shuffle the frames queued inside the
+ * LLC2 window.
+ */
+
+void
+llc_resetwindow(struct llc_linkcb *linkp)
+{
+ register struct mbuf *mptr = (struct mbuf *) 0;
+ register struct mbuf *anchor = (struct mbuf *)0;
+ register short i;
+
+ /* Pick up all queued frames and collect them in a linked mbuf list */
+ if (linkp->llcl_slotsfree != linkp->llcl_window) {
+ i = llc_seq2slot(linkp, linkp->llcl_nr_received);
+ anchor = mptr = linkp->llcl_output_buffers[i];
+ for (; i != linkp->llcl_freeslot;
+ i = llc_seq2slot(linkp, i+1)) {
+ if (linkp->llcl_output_buffers[i]) {
+ mptr->m_nextpkt = linkp->llcl_output_buffers[i];
+ mptr = mptr->m_nextpkt;
+ } else panic("LLC2 window broken");
+ }
+ }
+ /* clean closure */
+ if (mptr)
+ mptr->m_nextpkt = (struct mbuf *) 0;
+
+ /* Now --- plug 'em in again */
+ if (anchor != (struct mbuf *)0) {
+ for (i = 0, mptr = anchor; mptr != (struct mbuf *) 0; i++) {
+ linkp->llcl_output_buffers[i] = mptr;
+ mptr = mptr->m_nextpkt;
+ linkp->llcl_output_buffers[i]->m_nextpkt = (struct mbuf *)0;
+ }
+ linkp->llcl_freeslot = i;
+ } else linkp->llcl_freeslot = 0;
+
+ /* We're resetting the link, the next frame to be acknowledged is 0 */
+ linkp->llcl_nr_received = 0;
+
+ /* set distance between LLC2 sequence number and the top of window to 0 */
+ linkp->llcl_projvs = linkp->llcl_freeslot;
+
+ return;
+}
+
+/*
+ * llc_newlink() --- We allocate enough memory to contain a link control block
+ * and initialize it properly. We don't intiate the actual setup
+ * of the LLC2 link here.
+ */
+struct llc_linkcb *
+llc_newlink(struct sockaddr_dl *dst, struct ifnet *ifp, struct rtentry *nlrt,
+ caddr_t nlnext, struct rtentry *llrt)
+{
+ struct llc_linkcb *nlinkp;
+ u_char sap = LLSAPADDR(dst);
+ short llcwindow;
+
+
+ /* allocate memory for link control block */
+ MALLOC(nlinkp, struct llc_linkcb *, sizeof(struct llc_linkcb),
+ M_PCB, M_DONTWAIT);
+ if (nlinkp == 0)
+ return (NULL);
+ bzero((caddr_t)nlinkp, sizeof(struct llc_linkcb));
+
+ /* copy link address */
+ sdl_copy(dst, &nlinkp->llcl_addr);
+
+ /* hold on to the network layer route entry */
+ nlinkp->llcl_nlrt = nlrt;
+
+ /* likewise the network layer control block */
+ nlinkp->llcl_nlnext = nlnext;
+
+ /* jot down the link layer route entry */
+ nlinkp->llcl_llrt = llrt;
+
+ /* reset writeq */
+ nlinkp->llcl_writeqh = nlinkp->llcl_writeqt = NULL;
+
+ /* setup initial state handler function */
+ nlinkp->llcl_statehandler = llc_state_ADM;
+
+ /* hold on to interface pointer */
+ nlinkp->llcl_if = ifp;
+
+ /* get service access point information */
+ nlinkp->llcl_sapinfo = llc_getsapinfo(sap, ifp);
+
+ /* get window size from SAP info block */
+ if ((llcwindow = nlinkp->llcl_sapinfo->si_window) == 0)
+ llcwindow = LLC_MAX_WINDOW;
+
+ /* allocate memory for window buffer */
+ MALLOC(nlinkp->llcl_output_buffers, struct mbuf **,
+ llcwindow*sizeof(struct mbuf *), M_PCB, M_DONTWAIT);
+ if (nlinkp->llcl_output_buffers == 0) {
+ FREE(nlinkp, M_PCB);
+ return(NULL);
+ }
+ bzero((caddr_t)nlinkp->llcl_output_buffers,
+ llcwindow*sizeof(struct mbuf *));
+
+ /* set window size & slotsfree */
+ nlinkp->llcl_slotsfree = nlinkp->llcl_window = llcwindow;
+
+ /* enter into linked listed of link control blocks */
+ insque(nlinkp, &llccb_q);
+
+ return(nlinkp);
+}
+
+/*
+ * llc_dellink() --- farewell to link control block
+ */
+llc_dellink(struct llc_linkcb *linkp)
+{
+ register struct mbuf *m;
+ register struct mbuf *n;
+ register struct npaidbentry *sapinfo = linkp->llcl_sapinfo;
+ register i;
+
+ /* notify upper layer of imminent death */
+ if (linkp->llcl_nlnext && sapinfo->si_ctlinput)
+ (*sapinfo->si_ctlinput)
+ (PRC_DISCONNECT_INDICATION,
+ (struct sockaddr *)&linkp->llcl_addr, linkp->llcl_nlnext);
+
+ /* pull the plug */
+ if (linkp->llcl_llrt)
+ ((struct npaidbentry *)(linkp->llcl_llrt->rt_llinfo))->np_link
+ = (struct llc_linkcb *) 0;
+
+ /* leave link control block queue */
+ remque(linkp);
+
+ /* drop queued packets */
+ for (m = linkp->llcl_writeqh; m;) {
+ n = m->m_act;
+ m_freem(m);
+ m = n;
+ }
+
+ /* drop packets in the window */
+ for(i = 0; i < linkp->llcl_window; i++)
+ if (linkp->llcl_output_buffers[i])
+ m_freem(linkp->llcl_output_buffers[i]);
+
+ /* return the window space */
+ FREE((caddr_t)linkp->llcl_output_buffers, M_PCB);
+
+ /* return the control block space --- now it's gone ... */
+ FREE((caddr_t)linkp, M_PCB);
+}
+
+llc_decode(struct llc* frame, struct llc_linkcb * linkp)
+{
+ register int ft = LLC_BAD_PDU;
+
+ if ((frame->llc_control & 01) == 0) {
+ ft = LLCFT_INFO;
+ /* S or U frame ? */
+ } else switch (frame->llc_control) {
+
+ /* U frames */
+ case LLC_UI:
+ case LLC_UI_P: ft = LLC_UI; break;
+ case LLC_DM:
+ case LLC_DM_P: ft =LLCFT_DM; break;
+ case LLC_DISC:
+ case LLC_DISC_P: ft = LLCFT_DISC; break;
+ case LLC_UA:
+ case LLC_UA_P: ft = LLCFT_UA; break;
+ case LLC_SABME:
+ case LLC_SABME_P: ft = LLCFT_SABME; break;
+ case LLC_FRMR:
+ case LLC_FRMR_P: ft = LLCFT_FRMR; break;
+ case LLC_XID:
+ case LLC_XID_P: ft = LLCFT_XID; break;
+ case LLC_TEST:
+ case LLC_TEST_P: ft = LLCFT_TEST; break;
+
+ /* S frames */
+ case LLC_RR: ft = LLCFT_RR; break;
+ case LLC_RNR: ft = LLCFT_RNR; break;
+ case LLC_REJ: ft = LLCFT_REJ; break;
+ } /* switch */
+
+ if (linkp) {
+ switch (ft) {
+ case LLCFT_INFO:
+ if (LLCGBITS(frame->llc_control, i_ns) != linkp->llcl_vr) {
+ ft = LLC_INVALID_NS;
+ break;
+ }
+ /* fall thru --- yeeeeeee */
+ case LLCFT_RR:
+ case LLCFT_RNR:
+ case LLCFT_REJ:
+ /* splash! */
+ if (LLC_NR_VALID(linkp, LLCGBITS(frame->llc_control_ext,
+ s_nr)) == 0)
+ ft = LLC_INVALID_NR;
+ break;
+ }
+ }
+
+ return ft;
+}
+
+/*
+ * llc_anytimersup() --- Checks if at least one timer is still up and running.
+ */
+int
+llc_anytimersup(struct llc_linkcb * linkp)
+{
+ register int i;
+
+ FOR_ALL_LLC_TIMERS(i)
+ if (linkp->llcl_timers[i] > 0)
+ break;
+ if (i == LLC_AGE_SHIFT)
+ return 0;
+ else return 1;
+}
+
+/*
+ * llc_link_dump() - dump link info
+ */
+
+#define SAL(s) ((struct sockaddr_dl *)&(s)->llcl_addr)
+#define CHECK(l, s) if (LLC_STATEEQ(l, s)) return #s
+
+char *timer_names[] = {"ACK", "P", "BUSY", "REJ", "AGE"};
+
+char *
+llc_getstatename(struct llc_linkcb *linkp)
+{
+ CHECK(linkp, ADM);
+ CHECK(linkp, CONN);
+ CHECK(linkp, RESET_WAIT);
+ CHECK(linkp, RESET_CHECK);
+ CHECK(linkp, SETUP);
+ CHECK(linkp, RESET);
+ CHECK(linkp, D_CONN);
+ CHECK(linkp, ERROR);
+ CHECK(linkp, NORMAL);
+ CHECK(linkp, BUSY);
+ CHECK(linkp, REJECT);
+ CHECK(linkp, AWAIT);
+ CHECK(linkp, AWAIT_BUSY);
+ CHECK(linkp, AWAIT_REJECT);
+
+ return "UNKNOWN - eh?";
+}
+
+void
+llc_link_dump(struct llc_linkcb* linkp, const char *message)
+{
+ register int i;
+ register char *state;
+
+ /* print interface */
+ printf("if %s%d\n", linkp->llcl_if->if_name, linkp->llcl_if->if_unit);
+
+ /* print message */
+ printf(">> %s <<\n", message);
+
+ /* print MAC and LSAP */
+ printf("llc addr ");
+ for (i = 0; i < (SAL(linkp)->sdl_alen)-2; i++)
+ printf("%x:", (char)*(LLADDR(SAL(linkp))+i) & 0xff);
+ printf("%x,", (char)*(LLADDR(SAL(linkp))+i) & 0xff);
+ printf("%x\n", (char)*(LLADDR(SAL(linkp))+i+1) & 0xff);
+
+ /* print state we're in and timers */
+ printf("state %s, ", llc_getstatename(linkp));
+ for (i = LLC_ACK_SHIFT; i < LLC_AGE_SHIFT; i++)
+ printf("%s-%c %d/", timer_names[i],
+ (linkp->llcl_timerflags & (1<<i) ? 'R' : 'S'),
+ linkp->llcl_timers[i]);
+ printf("%s-%c %d\n", timer_names[i], (linkp->llcl_timerflags & (1<<i) ?
+ 'R' : 'S'), linkp->llcl_timers[i]);
+
+ /* print flag values */
+ printf("flags P %d/F %d/S %d/DATA %d/REMOTE_BUSY %d\n",
+ LLC_GETFLAG(linkp, P), LLC_GETFLAG(linkp, S),
+ LLC_GETFLAG(linkp, DATA), LLC_GETFLAG(linkp, REMOTE_BUSY));
+
+ /* print send and receive state variables, ack, and window */
+ printf("V(R) %d/V(S) %d/N(R) received %d/window %d/freeslot %d\n",
+ linkp->llcl_vs, linkp->llcl_vr, linkp->llcl_nr_received,
+ linkp->llcl_window, linkp->llcl_freeslot);
+
+ /* further expansions can follow here */
+
+}
+
+void
+llc_trace(struct llc_linkcb *linkp, int level, const char *message)
+{
+ if (linkp->llcl_sapinfo->si_trace && level > llc_tracelevel)
+ llc_link_dump(linkp, message);
+
+ return;
+}
diff --git a/sys/netccitt/llc_timer.c b/sys/netccitt/llc_timer.c
new file mode 100644
index 0000000..0aecd08
--- /dev/null
+++ b/sys/netccitt/llc_timer.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) Dirk Husemann, Computer Science Department IV,
+ * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Dirk Husemann and the Computer Science Department (IV) of
+ * the University of Erlangen-Nuremberg, Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)llc_timer.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_llc.h>
+
+#include <netccitt/dll.h>
+#include <netccitt/llc_var.h>
+
+
+/*
+ * Various timer values. They can be adjusted
+ * by patching the binary with adb if necessary.
+ */
+/* ISO 8802-2 timers */
+int llc_n2 = LLC_N2_VALUE;
+int llc_ACK_timer = LLC_ACK_TIMER;
+int llc_P_timer = LLC_P_TIMER;
+int llc_BUSY_timer = LLC_BUSY_TIMER;
+int llc_REJ_timer = LLC_REJ_TIMER;
+/* Implementation specific timers */
+int llc_AGE_timer = LLC_AGE_TIMER;
+int llc_DACTION_timer = LLC_DACTION_TIMER;
+
+/*
+ * The timer routine. We are called every 500ms by the kernel.
+ * Handle the various virtual timers.
+ */
+
+void
+llc_timer()
+{
+ register struct llc_linkcb *linkp;
+ register struct llc_linkcb *nlinkp;
+ register int timer;
+ register int action;
+ register int s = splimp();
+
+ /*
+ * All links are accessible over the doubly linked list llccb_q
+ */
+ if (!LQEMPTY) {
+ /*
+ * A for-loop is not that great an idea as the linkp
+ * might get deleted if the age timer has expired ...
+ */
+ linkp = LQFIRST;
+ while (LQVALID(linkp)) {
+ nlinkp = LQNEXT(linkp);
+ /*
+ * Check implementation specific timers first
+ */
+ /* The delayed action/acknowledge idle timer */
+ switch (LLC_TIMERXPIRED(linkp, DACTION)) {
+ case LLC_TIMER_RUNNING:
+ LLC_AGETIMER(linkp, DACTION);
+ break;
+ case LLC_TIMER_EXPIRED: {
+ register int cmdrsp;
+ register int pollfinal;
+
+ switch (LLC_GETFLAG(linkp, DACTION)) {
+ case LLC_DACKCMD:
+ cmdrsp = LLC_CMD, pollfinal = 0;
+ break;
+ case LLC_DACKCMDPOLL:
+ cmdrsp = LLC_CMD, pollfinal = 1;
+ break;
+ case LLC_DACKRSP:
+ cmdrsp = LLC_RSP, pollfinal = 0;
+ break;
+ case LLC_DACKRSPFINAL:
+ cmdrsp = LLC_RSP, pollfinal = 1;
+ break;
+ }
+ llc_send(linkp, LLCFT_RR, cmdrsp, pollfinal);
+ LLC_STOPTIMER(linkp, DACTION);
+ break;
+ }
+ }
+ /* The link idle timer */
+ switch (LLC_TIMERXPIRED(linkp, AGE)) {
+ case LLC_TIMER_RUNNING:
+ LLC_AGETIMER(linkp, AGE);
+ break;
+ case LLC_TIMER_EXPIRED:
+ /*
+ * Only crunch the link when really no
+ * timers are running any more.
+ */
+ if (llc_anytimersup(linkp) == 0) {
+ llc_dellink(linkp);
+ LLC_STOPTIMER(linkp, AGE);
+ goto gone;
+ } else {
+ LLC_STARTTIMER(linkp, AGE);
+ }
+ break;
+ }
+ /*
+ * Now, check all the ISO 8802-2 timers
+ */
+ FOR_ALL_LLC_TIMERS(timer) {
+ action = 0;
+ if ((linkp->llcl_timerflags & (1<<timer)) &&
+ (linkp->llcl_timers[timer] == 0)) {
+ switch (timer) {
+ case LLC_ACK_SHIFT:
+ action = LLC_ACK_TIMER_EXPIRED;
+ break;
+ case LLC_P_SHIFT:
+ action = LLC_P_TIMER_EXPIRED;
+ break;
+ case LLC_BUSY_SHIFT:
+ action = LLC_BUSY_TIMER_EXPIRED;
+ break;
+ case LLC_REJ_SHIFT:
+ action = LLC_REJ_TIMER_EXPIRED;
+ break;
+ }
+ linkp->llcl_timerflags &= ~(1<<timer);
+ (void)llc_statehandler(linkp, (struct llc *)0, action, 0, 1);
+ } else if (linkp->llcl_timers[timer] > 0)
+ linkp->llcl_timers[timer]--;
+ }
+
+gone: linkp = nlinkp;
+ }
+ }
+ splx (s);
+}
diff --git a/sys/netccitt/llc_var.h b/sys/netccitt/llc_var.h
new file mode 100644
index 0000000..a27db52
--- /dev/null
+++ b/sys/netccitt/llc_var.h
@@ -0,0 +1,659 @@
+/*
+ * Copyright (C) Dirk Husemann, Computer Science Department IV,
+ * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Dirk Husemann and the Computer Science Department (IV) of
+ * the University of Erlangen-Nuremberg, Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)llc_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifdef __STDC__
+/*
+ * Forward structure declarations for function prototypes [sic].
+ */
+struct llc;
+#endif
+
+#define NPAIDB_LINK 0
+
+struct npaidbentry {
+ union {
+ /* MAC,DLSAP -> CONS */
+ struct {
+ struct llc_linkcb *NE_link;
+ struct rtentry *NE_rt;
+ } NE;
+ /* SAP info for unconfigured incoming calls */
+ struct {
+ u_short SI_class;
+#define LLC_CLASS_I 0x1
+#define LLC_CLASS_II 0x3
+#define LLC_CLASS_III 0x4 /* Future */
+#define LLC_CLASS_IV 0x7 /* Future */
+ u_short SI_window;
+ u_short SI_trace;
+ u_short SI_xchxid;
+ void (*SI_input)
+ __P((struct mbuf *));
+ caddr_t (*SI_ctlinput)
+ __P((int, struct sockaddr *, caddr_t));
+ } SI;
+ } NESIun;
+};
+#define np_link NESIun.NE.NE_link
+#define np_rt NESIun.NE.NE_rt
+#define si_class NESIun.SI.SI_class
+#define si_window NESIun.SI.SI_window
+#define si_trace NESIun.SI.SI_trace
+#define si_xchxid NESIun.SI.SI_xchxid
+#define si_input NESIun.SI.SI_input
+#define si_ctlinput NESIun.SI.SI_ctlinput
+
+#define NPDL_SAPNETMASK 0x7e
+
+/*
+ * Definitions for accessing bitfields/bitslices inside
+ * LLC2 headers
+ */
+struct bitslice {
+ unsigned int bs_mask;
+ unsigned int bs_shift;
+};
+
+
+#define i_z 0
+#define i_ns 1
+#define i_pf 0
+#define i_nr 1
+#define s_oz 2
+#define s_selector 3
+#define s_pf 0
+#define s_nr 1
+#define u_bb 2
+#define u_select_other 3
+#define u_pf 4
+#define u_select 5
+#define f_vs 1
+#define f_cr 0
+#define f_vr 1
+#define f_wxyzv 6
+
+#define LLCGBITS(Arg, Index) (((Arg) & llc_bitslice[(Index)].bs_mask) >> llc_bitslice[(Index)].bs_shift)
+#define LLCSBITS(Arg, Index, Val) (Arg) |= (((Val) << llc_bitslice[(Index)].bs_shift) & llc_bitslice[(Index)].bs_mask)
+#define LLCCSBITS(Arg, Index, Val) (Arg) = (((Val) << llc_bitslice[(Index)].bs_shift) & llc_bitslice[(Index)].bs_mask)
+
+extern struct bitslice llc_bitslice[];
+
+#define LLC_CMD 0
+#define LLC_RSP 1
+#define LLC_MAXCMDRSP 2
+
+/*
+ * LLC events --- These events may either be frames received from the
+ * remote LLC DSAP, request from the network layer user,
+ * timer events from llc_timer(), or diagnostic events from
+ * llc_input().
+ */
+
+/* LLC frame types */
+#define LLCFT_INFO 0 * LLC_MAXCMDRSP
+#define LLCFT_RR 1 * LLC_MAXCMDRSP
+#define LLCFT_RNR 2 * LLC_MAXCMDRSP
+#define LLCFT_REJ 3 * LLC_MAXCMDRSP
+#define LLCFT_DM 4 * LLC_MAXCMDRSP
+#define LLCFT_SABME 5 * LLC_MAXCMDRSP
+#define LLCFT_DISC 6 * LLC_MAXCMDRSP
+#define LLCFT_UA 7 * LLC_MAXCMDRSP
+#define LLCFT_FRMR 8 * LLC_MAXCMDRSP
+#define LLCFT_UI 9 * LLC_MAXCMDRSP
+#define LLCFT_XID 10 * LLC_MAXCMDRSP
+#define LLCFT_TEST 11 * LLC_MAXCMDRSP
+
+/* LLC2 timer events */
+#define LLC_ACK_TIMER_EXPIRED 12 * LLC_MAXCMDRSP
+#define LLC_P_TIMER_EXPIRED 13 * LLC_MAXCMDRSP
+#define LLC_REJ_TIMER_EXPIRED 14 * LLC_MAXCMDRSP
+#define LLC_BUSY_TIMER_EXPIRED 15 * LLC_MAXCMDRSP
+
+/* LLC2 diagnostic events */
+#define LLC_INVALID_NR 16 * LLC_MAXCMDRSP
+#define LLC_INVALID_NS 17 * LLC_MAXCMDRSP
+#define LLC_BAD_PDU 18 * LLC_MAXCMDRSP
+#define LLC_LOCAL_BUSY_DETECTED 19 * LLC_MAXCMDRSP
+#define LLC_LOCAL_BUSY_CLEARED 20 * LLC_MAXCMDRSP
+
+/* Network layer user requests */
+/*
+ * NL_CONNECT_REQUEST --- The user has requested that a data link connection
+ * be established with a remote LLC DSAP.
+ */
+#define NL_CONNECT_REQUEST 21 * LLC_MAXCMDRSP
+/*
+ * NL_CONNECT_RESPONSE --- The user has accepted the data link connection.
+ */
+#define NL_CONNECT_RESPONSE 22 * LLC_MAXCMDRSP
+/*
+ * NL_RESET_REQUEST --- The user has requested that the data link with the
+ * remote LLC DSAP be reset.
+ */
+#define NL_RESET_REQUEST 23 * LLC_MAXCMDRSP
+/*
+ * NL_RESET_RESPONSE --- The user has accepted the reset of the data link
+ * connection.
+ */
+#define NL_RESET_RESPONSE 24 * LLC_MAXCMDRSP
+/*
+ * NL_DISCONNECT_REQUEST --- The user has requested that the data link
+ * connection with remote LLC DSAP be terminated.
+ */
+#define NL_DISCONNECT_REQUEST 25 * LLC_MAXCMDRSP
+/*
+ * NL_DATA_REQUEST --- The user has requested that a data unit be sent ot the
+ * remote LLC DSAP.
+ */
+#define NL_DATA_REQUEST 26 * LLC_MAXCMDRSP
+/*
+ * NL_INITIATE_PF_CYCLE --- The local LLC wants to initiate a P/F cycle.
+ */
+#define NL_INITIATE_PF_CYCLE 27 * LLC_MAXCMDRSP
+/*
+ * NL_LOCAL_BUSY_DETECTED --- The local entity has encountered a busy condition
+ */
+#define NL_LOCAL_BUSY_DETECTED 28 * LLC_MAXCMDRSP
+
+#define LLCFT_NONE 255
+
+/* return message from state handlers */
+
+/*
+ * LLC_CONNECT_INDICATION --- Inform the user that a connection has been
+ * requested by a remote LLC SSAP.
+ */
+#define LLC_CONNECT_INDICATION 1
+/*
+ * LLC_CONNECT_CONFIRM --- The connection service component indicates that the
+ * remote network entity has accepted the connection.
+ */
+#define LLC_CONNECT_CONFIRM 2
+/*
+ * LLC_DISCONNECT_INDICATION --- Inform the user that the remote network
+ * entity has intiated disconnection of the data
+ * link connection.
+ */
+#define LLC_DISCONNECT_INDICATION 3
+/*
+ * LLC_RESET_CONFIRM --- The connection service component indicates that the
+ * remote network entity has accepted the reset.
+ */
+#define LLC_RESET_CONFIRM 4
+/*
+ * LLC_RESET_INDICATION_REMOTE --- The remote network entity or remote peer
+ * has initiated a reset of the data link
+ * connection.
+ */
+#define LLC_RESET_INDICATION_REMOTE 5
+/*
+ * LLC_RESET_INDICATION_LOCAL --- The local LLC has determined that the data
+ * link connection is in need of
+ * reinitialization.
+ */
+#define LLC_RESET_INDICATION_LOCAL 6
+/*
+ * LLC_FRMR_RECEIVED --- The local connection service component has received a
+ * FRMR response PDU.
+ */
+#define LLC_FRMR_RECEIVED 7
+/*
+ * LLC_FRMR_SENT --- The local connection component has received an ivalid
+ * PDU, and has sent a FRMR response PDU.
+ */
+#define LLC_FRMR_SENT 8
+/*
+ * LLC_DATA_INDICATION --- The connection service component passes the data
+ * unit from the received I PDU to the user.
+ */
+#define LLC_DATA_INDICATION 9
+/*
+ * LLC_REMOTE_NOT_BUSY --- The remote LLC DSAP is no longer busy. The local
+ * connection service component will now accept a
+ * DATA_REQUEST.
+ */
+#define LLC_REMOTE_NOT_BUSY 10
+/*
+ * LLC_REMOTE_BUSY --- The remote LLC DSAP is busy. The local connection
+ * service component will not accept a DATA_REQUEST.
+ */
+#define LLC_REMOTE_BUSY 11
+
+/* Internal return code */
+#define LLC_PASSITON 255
+
+#define INFORMATION_CONTROL 0x00
+#define SUPERVISORY_CONTROL 0x02
+#define UNUMBERED_CONTROL 0x03
+
+/*
+ * Other necessary definitions
+ */
+
+#define LLC_MAX_SEQUENCE 128
+#define LLC_MAX_WINDOW 127
+#define LLC_WINDOW_SIZE 7
+
+/*
+ * Don't we love this one? CCITT likes to suck on bits 8=)
+ */
+#define NLHDRSIZEGUESS 3
+
+/*
+ * LLC control block
+ */
+
+struct llc_linkcb {
+ struct llccb_q {
+ struct llccb_q *q_forw; /* admin chain */
+ struct llccb_q *q_backw;
+ } llcl_q;
+ struct npaidbentry *llcl_sapinfo; /* SAP information */
+ struct sockaddr_dl llcl_addr; /* link snpa address */
+ struct rtentry *llcl_nlrt; /* layer 3 -> LLC */
+ struct rtentry *llcl_llrt; /* LLC -> layer 3 */
+ struct ifnet *llcl_if; /* our interface */
+ caddr_t llcl_nlnext; /* cb for network layer */
+ struct mbuf *llcl_writeqh; /* Write queue head */
+ struct mbuf *llcl_writeqt; /* Write queue tail */
+ struct mbuf **llcl_output_buffers;
+ short llcl_timers[6]; /* timer array */
+ long llcl_timerflags; /* flags signalling running timers */
+ int (*llcl_statehandler)
+ __P((struct llc_linkcb *, struct llc *, int, int, int));
+ int llcl_P_flag;
+ int llcl_F_flag;
+ int llcl_S_flag;
+ int llcl_DATA_flag;
+ int llcl_REMOTE_BUSY_flag;
+ int llcl_DACTION_flag; /* delayed action */
+ int llcl_retry;
+ /*
+ * The following components deal --- in one way or the other ---
+ * with the LLC2 window. Indicated by either [L] or [W] is the
+ * domain of the specific component:
+ *
+ * [L] The domain is 0--LLC_MAX_WINDOW
+ * [W] The domain is 0--llcl_window
+ */
+ short llcl_vr; /* next to receive [L] */
+ short llcl_vs; /* next to send [L] */
+ short llcl_nr_received; /* next frame to b ack'd [L] */
+ short llcl_freeslot; /* next free slot [W] */
+ short llcl_projvs; /* V(S) associated with freeslot */
+ short llcl_slotsfree; /* free slots [W] */
+ short llcl_window; /* window size */
+ /*
+ * In llcl_frmrinfo we jot down the last frmr info field, which we
+ * need to do as we need to be able to resend it in the ERROR state.
+ */
+ struct frmrinfo llcl_frmrinfo; /* last FRMR info field */
+};
+#define llcl_frmr_pdu0 llcl_frmrinfo.rej_pdu_0
+#define llcl_frmr_pdu1 llcl_frmrinfo.rej_pdu_1
+#define llcl_frmr_control llcl_frmrinfo.frmr_control
+#define llcl_frmr_control_ext llcl_frmrinfo.frmr_control_ext
+#define llcl_frmr_cause llcl_frmrinfo.frmr_cause
+
+#define LQNEXT(l) (struct llc_linkcb *)((l)->llcl_q.q_forw)
+#define LQEMPTY (llccb_q.q_forw == &llccb_q)
+#define LQFIRST (struct llc_linkcb *)(llccb_q.q_forw)
+#define LQVALID(l) (!((struct llccb_q *)(l) == &llccb_q))
+
+#define LLC_ENQUEUE(l, m) if ((l)->llcl_writeqh == NULL) { \
+ (l)->llcl_writeqh = (m); \
+ (l)->llcl_writeqt = (m); \
+ } else { \
+ (l)->llcl_writeqt->m_nextpkt = (m); \
+ (l)->llcl_writeqt = (m); \
+ }
+
+#define LLC_DEQUEUE(l, m) if ((l)->llcl_writeqh == NULL) \
+ (m) = NULL; \
+ else { \
+ (m) = (l)->llcl_writeqh; \
+ (l)->llcl_writeqh = (l)->llcl_writeqh->m_nextpkt; \
+ }
+
+#define LLC_SETFRAME(l, m) { \
+ if ((l)->llcl_slotsfree > 0) { \
+ (l)->llcl_slotsfree--; \
+ (l)->llcl_output_buffers[(l)->llcl_freeslot] = (m); \
+ (l)->llcl_freeslot = ((l)->llcl_freeslot+1) % (l)->llcl_window; \
+ LLC_INC((l)->llcl_projvs); \
+ } \
+ }
+
+/*
+ * handling of sockaddr_dl's
+ */
+
+#define LLADDRLEN(s) ((s)->sdl_alen + (s)->sdl_nlen)
+#define LLSAPADDR(s) ((s)->sdl_data[LLADDRLEN(s)-1] & 0xff)
+#define LLSAPLOC(s, if) ((s)->sdl_nlen + (if)->if_addrlen)
+
+struct sdl_hdr {
+ struct sockaddr_dl sdlhdr_dst;
+ struct sockaddr_dl sdlhdr_src;
+ long sdlhdr_len;
+};
+
+#define LLC_GETHDR(f,m) { \
+ struct mbuf *_m = (struct mbuf *) (m); \
+ if (_m) { \
+ M_PREPEND(_m, LLC_ISFRAMELEN, M_DONTWAIT); \
+ bzero(mtod(_m, caddr_t), LLC_ISFRAMELEN); \
+ } else { \
+ MGETHDR (_m, M_DONTWAIT, MT_HEADER); \
+ if (_m != NULL) { \
+ _m->m_pkthdr.len = _m->m_len = LLC_UFRAMELEN; \
+ _m->m_next = _m->m_act = NULL; \
+ bzero(mtod(_m, caddr_t), LLC_UFRAMELEN); \
+ } else return; \
+ } \
+ (m) = _m; \
+ (f) = mtod(m, struct llc *); \
+ }
+
+#define LLC_NEWSTATE(l, LLCstate) (l)->llcl_statehandler = llc_state_##LLCstate
+#define LLC_STATEEQ(l, LLCstate) ((l)->llcl_statehandler == llc_state_##LLCstate ? 1 : 0)
+
+#define LLC_ACK_SHIFT 0
+#define LLC_P_SHIFT 1
+#define LLC_BUSY_SHIFT 2
+#define LLC_REJ_SHIFT 3
+#define LLC_AGE_SHIFT 4
+#define LLC_DACTION_SHIFT 5
+
+#define LLC_TIMER_NOTRUNNING 0
+#define LLC_TIMER_RUNNING 1
+#define LLC_TIMER_EXPIRED 2
+
+#define LLC_STARTTIMER(l, LLCtimer) { \
+ (l)->llcl_timers[LLC_##LLCtimer##_SHIFT] = llc_##LLCtimer##_timer; \
+ (l)->llcl_timerflags |= (1<<LLC_##LLCtimer##_SHIFT); \
+ }
+#define LLC_STOPTIMER(l, LLCtimer) { \
+ (l)->llcl_timers[LLC_##LLCtimer##_SHIFT] = 0; \
+ (l)->llcl_timerflags &= ~(1<<LLC_##LLCtimer##_SHIFT); \
+ }
+#define LLC_AGETIMER(l, LLCtimer) if ((l)->llcl_timers[LLC_##LLCtimer##_SHIFT] > 0) \
+ (l)->llcl_timers[LLC_##LLCtimer##_SHIFT]--;
+
+#define LLC_TIMERXPIRED(l, LLCtimer) \
+ (((l)->llcl_timerflags & (1<<LLC_##LLCtimer##_SHIFT)) ? \
+ (((l)->llcl_timers[LLC_##LLCtimer##_SHIFT] == 0 ) ? \
+ LLC_TIMER_EXPIRED : LLC_TIMER_RUNNING) : LLC_TIMER_NOTRUNNING)
+
+#define FOR_ALL_LLC_TIMERS(t) for ((t) = LLC_ACK_SHIFT; (t) < LLC_AGE_SHIFT; (t)++)
+
+#define LLC_SETFLAG(l, LLCflag, v) (l)->llcl_##LLCflag##_flag = (v)
+#define LLC_GETFLAG(l, LLCflag) (l)->llcl_##LLCflag##_flag
+
+#define LLC_RESETCOUNTER(l) { \
+ (l)->llcl_vs = (l)->llcl_vr = (l)->llcl_retry = 0; \
+ llc_resetwindow((l)); \
+ }
+
+/*
+ * LLC2 macro definitions
+ */
+
+
+#define LLC_START_ACK_TIMER(l) LLC_STARTTIMER((l), ACK)
+#define LLC_STOP_ACK_TIMER(l) LLC_STOPTIMER((l), ACK)
+#define LLC_START_REJ_TIMER(l) LLC_STARTTIMER((l), REJ)
+#define LLC_STOP_REJ_TIMER(l) LLC_STOPTIMER((l), REJ)
+#define LLC_START_P_TIMER(l) { \
+ LLC_STARTTIMER((l), P); \
+ if (LLC_GETFLAG((l), P) == 0) \
+ (l)->llcl_retry = 0; \
+ LLC_SETFLAG((l), P, 1); \
+ }
+#define LLC_STOP_P_TIMER(l) { \
+ LLC_STOPTIMER((l), P); \
+ LLC_SETFLAG((l), P, 0); \
+ }
+#define LLC_STOP_ALL_TIMERS(l) { \
+ LLC_STOPTIMER((l), ACK); \
+ LLC_STOPTIMER((l), REJ); \
+ LLC_STOPTIMER((l), BUSY); \
+ LLC_STOPTIMER((l), P); \
+ }
+
+
+#define LLC_INC(i) (i) = ((i)+1) % LLC_MAX_SEQUENCE
+
+#define LLC_NR_VALID(l, nr) ((l)->llcl_vs < (l)->llcl_nr_received ? \
+ (((nr) >= (l)->llcl_nr_received) || \
+ ((nr) <= (l)->llcl_vs) ? 1 : 0) : \
+ (((nr) <= (l)->llcl_vs) && \
+ ((nr) >= (l)->llcl_nr_received) ? 1 : 0))
+
+#define LLC_UPDATE_P_FLAG(l, cr, pf) { \
+ if ((cr) == LLC_RSP && (pf) == 1) { \
+ LLC_SETFLAG((l), P, 0); \
+ LLC_STOPTIMER((l), P); \
+ } \
+ }
+
+#define LLC_UPDATE_NR_RECEIVED(l, nr) { \
+ while ((l)->llcl_nr_received != (nr)) { \
+ struct mbuf *_m; \
+ register short seq; \
+ if (_m = (l)->llcl_output_buffers[seq = llc_seq2slot((l), (l)->llcl_nr_received)]) \
+ m_freem(_m); \
+ (l)->llcl_output_buffers[seq] = NULL; \
+ LLC_INC((l)->llcl_nr_received); \
+ (l)->llcl_slotsfree++; \
+ } \
+ (l)->llcl_retry = 0; \
+ if ((l)->llcl_slotsfree < (l)->llcl_window) { \
+ LLC_START_ACK_TIMER(l); \
+ } else LLC_STOP_ACK_TIMER(l); \
+ LLC_STARTTIMER((l), DACTION); \
+ }
+
+#define LLC_SET_REMOTE_BUSY(l,a) { \
+ if (LLC_GETFLAG((l), REMOTE_BUSY) == 0) { \
+ LLC_SETFLAG((l), REMOTE_BUSY, 1); \
+ LLC_STARTTIMER((l), BUSY); \
+ (a) = LLC_REMOTE_BUSY; \
+ } else { \
+ (a) = 0; \
+ } \
+ }
+#define LLC_CLEAR_REMOTE_BUSY(l,a) { \
+ if (LLC_GETFLAG((l), REMOTE_BUSY) == 1) { \
+ LLC_SETFLAG((l), REMOTE_BUSY, 1); \
+ LLC_STOPTIMER((l), BUSY); \
+ if (LLC_STATEEQ((l), NORMAL) || \
+ LLC_STATEEQ((l), REJECT) || \
+ LLC_STATEEQ((l), BUSY)) \
+ llc_resend((l), LLC_CMD, 0); \
+ (a) = LLC_REMOTE_NOT_BUSY; \
+ } else { \
+ (a) = 0; \
+ } \
+ }
+
+#define LLC_DACKCMD 0x1
+#define LLC_DACKCMDPOLL 0x2
+#define LLC_DACKRSP 0x3
+#define LLC_DACKRSPFINAL 0x4
+
+#define LLC_SENDACKNOWLEDGE(l, cmd, pf) { \
+ if ((cmd) == LLC_CMD) { \
+ LLC_SETFLAG((l), DACTION, ((pf) == 0 ? LLC_DACKCMD : LLC_DACKCMDPOLL)); \
+ } else { \
+ LLC_SETFLAG((l), DACTION, ((pf) == 0 ? LLC_DACKRSP : LLC_DACKRSPFINAL)); \
+ } \
+ }
+
+#define LLC_FRMR_W (1<<0)
+#define LLC_FRMR_X (1<<1)
+#define LLC_FRMR_Y (1<<2)
+#define LLC_FRMR_Z (1<<3)
+#define LLC_FRMR_V (1<<4)
+
+#define LLC_SETFRMR(l, f, cr, c) { \
+ if ((f)->llc_control & 0x3) { \
+ (l)->llcl_frmr_pdu0 = (f)->llc_control; \
+ (l)->llcl_frmr_pdu1 = 0; \
+ } else { \
+ (l)->llcl_frmr_pdu0 = (f)->llc_control; \
+ (l)->llcl_frmr_pdu1 = (f)->llc_control_ext; \
+ } \
+ LLCCSBITS((l)->llcl_frmr_control, f_vs, (l)->llcl_vs); \
+ LLCCSBITS((l)->llcl_frmr_control_ext, f_cr, (cr)); \
+ LLCSBITS((l)->llcl_frmr_control_ext, f_vr, (l)->llcl_vr); \
+ LLCCSBITS((l)->llcl_frmr_cause, f_wxyzv, (c)); \
+ }
+
+/*
+ * LLC tracing levels:
+ * LLCTR_INTERESTING interesting event, we might care to know about
+ * it, but then again, we might not ...
+ * LLCTR_SHOULDKNOW we probably should know about this event
+ * LLCTR_URGENT something has gone utterly wrong ...
+ */
+#define LLCTR_INTERESTING 1
+#define LLCTR_SHOULDKNOW 2
+#define LLCTR_URGENT 3
+
+#ifdef LLCDEBUG
+#define LLC_TRACE(lp, l, msg) llc_trace((lp), (l), (msg))
+#else /* LLCDEBUG */
+#define LLC_TRACE(lp, l, msg) /* NOOP */
+#endif /* LLCDEBUG */
+
+#define LLC_N2_VALUE 15 /* up to 15 retries */
+#define LLC_ACK_TIMER 10 /* 5 secs */
+#define LLC_P_TIMER 4 /* 2 secs */
+#define LLC_BUSY_TIMER 12 /* 6 secs */
+#define LLC_REJ_TIMER 12 /* 6 secs */
+#define LLC_AGE_TIMER 40 /* 20 secs */
+#define LLC_DACTION_TIMER 2 /* 1 secs */
+
+#if defined (KERNEL) && defined(LLC)
+extern int llc_n2;
+extern int llc_ACK_timer;
+extern int llc_P_timer;
+extern int llc_REJ_timer;
+extern int llc_BUSY_timer;
+extern int llc_AGE_timer;
+extern int llc_DACTION_timer;
+
+extern int af_link_rts_init_done;
+
+#define USES_AF_LINK_RTS { \
+ if (!af_link_rts_init_done) { \
+ rn_inithead((void **)&rt_tables[AF_LINK], 32); \
+ af_link_rts_init_done++; \
+ } \
+ }
+
+struct ifqueue llcintrq;
+
+extern struct llccb_q llccb_q;
+extern char *frame_names[];
+
+/*
+ * Function prototypes
+ */
+int sdl_cmp __P((struct sockaddr_dl *, struct sockaddr_dl *));
+int sdl_copy __P((struct sockaddr_dl *, struct sockaddr_dl *));
+int sdl_swapaddr __P((struct sockaddr_dl *, struct sockaddr_dl *));
+int sdl_checkaddrif __P((struct ifnet *, struct sockaddr_dl *));
+int sdl_setaddrif __P((struct ifnet *, u_char *, u_char, u_char,
+ struct sockaddr_dl *));
+int sdl_sethdrif __P((struct ifnet *, u_char *, u_char, u_char *, u_char, u_char,
+ struct sdl_hdr *));
+struct npaidbentry *llc_setsapinfo __P((struct ifnet *, u_char, u_char,
+ struct dllconfig *));
+struct npaidbentry *llc_getsapinfo __P((u_char, struct ifnet *));
+struct rtentry *npaidb_enrich __P((short, caddr_t, struct sockaddr_dl *));
+int npaidb_destroy __P((struct rtentry *));
+short llc_seq2slot __P((struct llc_linkcb *, short));
+int llc_state_ADM __P((struct llc_linkcb *, struct llc *, int, int, int));
+int llc_state_CONN __P((struct llc_linkcb *, struct llc *, int, int, int));
+int llc_state_RESET_WAIT __P((struct llc_linkcb *, struct llc *,
+ int, int, int));
+int llc_state_RESET_CHECK __P((struct llc_linkcb *, struct llc *,
+ int, int, int));
+int llc_state_SETUP __P((struct llc_linkcb *, struct llc *, int, int, int));
+int llc_state_RESET __P((struct llc_linkcb *, struct llc *, int, int, int));
+int llc_state_D_CONN __P((struct llc_linkcb *, struct llc *, int, int, int));
+int llc_state_ERROR __P((struct llc_linkcb *, struct llc *, int, int, int));
+int llc_state_NBRAcore __P((struct llc_linkcb *, struct llc *, int, int, int));
+int llc_state_NORMAL __P((struct llc_linkcb *, struct llc *, int, int, int));
+int llc_state_BUSY __P((struct llc_linkcb *, struct llc *, int, int, int));
+int llc_state_REJECT __P((struct llc_linkcb *, struct llc *, int, int, int));
+int llc_state_AWAIT __P((struct llc_linkcb *, struct llc *, int, int, int));
+int llc_state_AWAIT_BUSY __P((struct llc_linkcb *, struct llc *, int, int, int));
+int llc_state_AWAIT_REJECT __P((struct llc_linkcb *, struct llc *, int, int, int));
+int llc_statehandler __P((struct llc_linkcb *, struct llc *, int, int, int));
+int llc_init __P((void));
+struct llc_linkcb *llc_newlink __P((struct sockaddr_dl *, struct ifnet *,
+ struct rtentry *, caddr_t, struct rtentry *));
+int llc_dellink __P((struct llc_linkcb *));
+int llc_anytimersup __P((struct llc_linkcb *));
+char * llc_getstatename __P((struct llc_linkcb *));
+void llc_link_dump __P((struct llc_linkcb *, const char *));
+void llc_trace __P((struct llc_linkcb *, int, const char *));
+void llc_resetwindow __P((struct llc_linkcb *));
+int llc_decode __P((struct llc *, struct llc_linkcb *));
+void llc_timer __P((void));
+void llcintr __P((void));
+int llc_input __P((struct llc_linkcb *, struct mbuf *, u_char));
+caddr_t llc_ctlinput __P((int, struct sockaddr *, caddr_t));
+int llc_output __P((struct llc_linkcb *, struct mbuf *));
+void llc_start __P((struct llc_linkcb *));
+int llc_send __P((struct llc_linkcb *, int, int, int));
+int llc_resend __P((struct llc_linkcb *, int, int));
+int llc_rawsend __P((struct llc_linkcb *, struct mbuf *, struct llc *, int, int,
+ int, int));
+int cons_rtrequest __P((int, struct rtentry *, struct sockaddr *));
+int x25_llcglue __P((int, struct sockaddr *));
+
+#endif
+
+
diff --git a/sys/netccitt/pk.h b/sys/netccitt/pk.h
new file mode 100644
index 0000000..528e0a6
--- /dev/null
+++ b/sys/netccitt/pk.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (c) 1990, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Laboratory for Computation Vision and the Computer Science Department
+ * of the University of British Columbia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pk.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ *
+ * X.25 Packet Level Definitions:
+ *
+ */
+
+/* Packet type identifier field defintions. */
+
+#define X25_CALL 11
+#define X25_CALL_ACCEPTED 15
+#define X25_CLEAR 19
+#define X25_CLEAR_CONFIRM 23
+#define X25_DATA 0
+#define X25_INTERRUPT 35
+#define X25_INTERRUPT_CONFIRM 39
+
+#define X25_RR 1
+#define X25_RNR 5
+#define X25_REJECT 9
+#define X25_RESET 27
+#define X25_RESET_CONFIRM 31
+#define X25_DIAGNOSTIC 241
+
+#define X25_RESTART 251
+#define X25_RESTART_CONFIRM 255
+
+/* Restart cause field definitions. */
+
+#define X25_RESTART_DTE_ORIGINATED 0
+#define X25_RESTART_LOCAL_PROCEDURE_ERROR 1
+#define X25_RESTART_NETWORK_CONGESTION 3
+#define X25_RESTART_NETWORK_OPERATIONAL 7
+#define X25_RESTART_DTE_ORIGINATED2 128
+
+
+/* Miscellaneous definitions. */
+
+#define DATA_PACKET_DESIGNATOR 0x01
+#define RR_OR_RNR_PACKET_DESIGNATOR 0x02
+#define RR_PACKET_DESIGNATOR 0x04
+
+#define DEFAULT_WINDOW_SIZE 2
+#define MODULUS 8
+
+#define ADDRLN 1
+#define MAXADDRLN 15
+#define FACILITIESLN 1
+#define MAXFACILITIESLN 10
+#define MAXUSERDATA 16
+#define MAXCALLINFOLN 1+15+1+10+16
+
+#define PACKET_OK 0
+#define IGNORE_PACKET 1
+#define ERROR_PACKET 2
+
+typedef char bool;
+#define FALSE 0
+#define TRUE 1
+
+/*
+ * X.25 Packet format definitions
+ * This will eventually have to be rewritten without reference
+ * to bit fields, to be ansi C compliant and allignment safe.
+ */
+
+typedef u_char octet;
+
+struct x25_calladdr {
+ octet addrlens;
+ octet address_field[MAXADDRLN];
+};
+
+struct x25_packet {
+ octet bits;
+ octet logical_channel_number;
+ octet packet_type;
+ octet packet_data;
+};
+#define packet_cause packet_data
+
+struct data_packet {
+ octet bits;
+};
+
+#define FACILITIES_REVERSE_CHARGE 0x1
+#define FACILITIES_THROUGHPUT 0x2
+#define FACILITIES_PACKETSIZE 0x42
+#define FACILITIES_WINDOWSIZE 0x43
+
+#define PKHEADERLN 3
+
+#define DP(xp) (((struct data_packet *)&(xp) -> packet_type) -> bits)
+#define PS(xp) X25GBITS(DP(xp), p_s)
+#define PR(xp) X25GBITS(DP(xp), p_r)
+#define MBIT(xp) X25GBITS(DP(xp), m_bit)
+#define SPR(xp, v) X25SBITS(DP(xp), p_r, (v))
+#define SPS(xp, v) X25SBITS(DP(xp), p_s, (v))
+#define SMBIT(xp, v) X25SBITS(DP(xp), m_bit, (v))
+
+#define LCN(xp) (xp -> logical_channel_number + \
+ (X25GBITS(xp -> bits, lc_group_number) ? (X25GBITS(xp -> bits, lc_group_number) << 8) : 0))
+#define SET_LCN(xp, lcn) ((xp -> logical_channel_number = lcn), \
+ (X25SBITS(xp -> bits, lc_group_number, lcn > 255 ? lcn >> 8 : 0)))
+
+struct mbuf *pk_template ();
+
+/* Define X.25 packet level states. */
+
+/* Call setup and clearing substates. */
+
+#define LISTEN 0
+#define READY 1
+#define RECEIVED_CALL 2
+#define SENT_CALL 3
+#define DATA_TRANSFER 4
+#define RECEIVED_CLEAR 5
+#define SENT_CLEAR 6
+
+/* DTE states. */
+
+#define DTE_WAITING 7
+#define DTE_RECEIVED_RESTART 8
+#define DTE_SENT_RESTART 9
+#define DTE_READY 0
+
+/* Cleaning out ... */
+
+#define LCN_ZOMBIE 10
+
+#define MAXSTATES 11
+
+/*
+ * The following definitions are used in a switch statement after
+ * determining the packet type. These values are returned by the
+ * pk_decode procedure.
+ */
+
+#define CALL 0 * MAXSTATES
+#define CALL_ACCEPTED 1 * MAXSTATES
+#define CLEAR 2 * MAXSTATES
+#define CLEAR_CONF 3 * MAXSTATES
+#define DATA 4 * MAXSTATES
+#define INTERRUPT 5 * MAXSTATES
+#define INTERRUPT_CONF 6 * MAXSTATES
+#define RR 7 * MAXSTATES
+#define RNR 8 * MAXSTATES
+#define RESET 9 * MAXSTATES
+#define RESET_CONF 10 * MAXSTATES
+#define RESTART 11 * MAXSTATES
+#define RESTART_CONF 12 * MAXSTATES
+#define REJECT 13 * MAXSTATES
+#define DIAG_TYPE 14 * MAXSTATES
+#define INVALID_PACKET 15 * MAXSTATES
+#define DELETE_PACKET INVALID_PACKET
+
+/*
+ * The following definitions are used by the restart procedures
+ * for noting wether the PLE is supposed to behave as DTE or DCE
+ * (essentially necessary for operation over LLC2)
+ */
+#define DTE_DXERESOLVING 0x0001
+#define DTE_PLAYDTE 0x0002
+#define DTE_PLAYDCE 0x0004
+#define DTE_CONNECTPENDING 0x0010
+#define DTE_PRETENDDTE 0x0020
+
+#define MAXRESTARTCOLLISIONS 10
diff --git a/sys/netccitt/pk_acct.c b/sys/netccitt/pk_acct.c
new file mode 100644
index 0000000..fccd875
--- /dev/null
+++ b/sys/netccitt/pk_acct.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Laboratory for Computation Vision and the Computer Science Department
+ * of the University of British Columbia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pk_acct.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <net/if.h>
+
+#include <netccitt/x25.h>
+#include <netccitt/pk.h>
+#include <netccitt/pk_var.h>
+#include <netccitt/x25acct.h>
+
+
+struct vnode *pkacctp;
+/*
+ * Turn on packet accounting
+ */
+
+pk_accton (path)
+ char *path;
+{
+ register struct vnode *vp = NULL;
+ struct nameidata nd;
+ struct vnode *oacctp = pkacctp;
+ struct proc *p = curproc;
+ int error;
+
+ if (path == 0)
+ goto close;
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, path, p);
+ if (error = vn_open (&nd, FWRITE, 0644))
+ return (error);
+ vp = nd.ni_vp;
+ VOP_UNLOCK(vp);
+ if (vp -> v_type != VREG) {
+ vrele (vp);
+ return (EACCES);
+ }
+ pkacctp = vp;
+ if (oacctp) {
+ close:
+ error = vn_close (oacctp, FWRITE, p -> p_ucred, p);
+ }
+ return (error);
+}
+
+/*
+ * Write a record on the accounting file.
+ */
+
+pk_acct (lcp)
+register struct pklcd *lcp;
+{
+ register struct vnode *vp;
+ register struct sockaddr_x25 *sa;
+ register char *src, *dst;
+ register int len;
+ register long etime;
+ static struct x25acct acbuf;
+
+ if ((vp = pkacctp) == 0)
+ return;
+ bzero ((caddr_t)&acbuf, sizeof (acbuf));
+ if (lcp -> lcd_ceaddr != 0)
+ sa = lcp -> lcd_ceaddr;
+ else if (lcp -> lcd_craddr != 0) {
+ sa = lcp -> lcd_craddr;
+ acbuf.x25acct_callin = 1;
+ } else
+ return;
+
+ if (sa -> x25_opts.op_flags & X25_REVERSE_CHARGE)
+ acbuf.x25acct_revcharge = 1;
+ acbuf.x25acct_stime = lcp -> lcd_stime;
+ acbuf.x25acct_etime = time.tv_sec - acbuf.x25acct_stime;
+ acbuf.x25acct_uid = curproc -> p_cred -> p_ruid;
+ acbuf.x25acct_psize = sa -> x25_opts.op_psize;
+ acbuf.x25acct_net = sa -> x25_net;
+ /*
+ * Convert address to bcd
+ */
+ src = sa -> x25_addr;
+ dst = acbuf.x25acct_addr;
+ for (len = 0; *src; len++)
+ if (len & 01)
+ *dst++ |= *src++ & 0xf;
+ else
+ *dst = *src++ << 4;
+ acbuf.x25acct_addrlen = len;
+
+ bcopy (sa -> x25_udata, acbuf.x25acct_udata,
+ sizeof (acbuf.x25acct_udata));
+ acbuf.x25acct_txcnt = lcp -> lcd_txcnt;
+ acbuf.x25acct_rxcnt = lcp -> lcd_rxcnt;
+
+ (void) vn_rdwr(UIO_WRITE, vp, (caddr_t)&acbuf, sizeof (acbuf),
+ (off_t)0, UIO_SYSSPACE, IO_UNIT|IO_APPEND,
+ curproc -> p_ucred, (int *)0,
+ (struct proc *)0);
+}
diff --git a/sys/netccitt/pk_debug.c b/sys/netccitt/pk_debug.c
new file mode 100644
index 0000000..b510355
--- /dev/null
+++ b/sys/netccitt/pk_debug.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Laboratory for Computation Vision and the Computer Science Department
+ * of the University of British Columbia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pk_debug.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+
+#include <netccitt/x25.h>
+#include <netccitt/pk.h>
+#include <netccitt/pk_var.h>
+
+char *pk_state[] = {
+ "Listen", "Ready", "Received-Call",
+ "Sent-Call", "Data-Transfer","Received-Clear",
+ "Sent-Clear",
+};
+
+char *pk_name[] = {
+ "Call", "Call-Conf", "Clear",
+ "Clear-Conf", "Data", "Intr", "Intr-Conf",
+ "Rr", "Rnr", "Reset", "Reset-Conf",
+ "Restart", "Restart-Conf", "Reject", "Diagnostic",
+ "Invalid"
+};
+
+pk_trace (xcp, m, dir)
+struct x25config *xcp;
+register struct mbuf *m;
+char *dir;
+{
+ register char *s;
+ struct x25_packet *xp = mtod(m, struct x25_packet *);
+ register int i, len = 0, cnt = 0;
+
+ if (xcp -> xc_ptrace == 0)
+ return;
+
+ i = pk_decode (xp) / MAXSTATES;
+ for (; m; m = m -> m_next) {
+ len = len + m -> m_len;
+ ++cnt;
+ }
+ printf ("LCN=%d %s: %s #=%d, len=%d ",
+ LCN(xp), dir, pk_name[i], cnt, len);
+ for (s = (char *) xp, i = 0; i < 5; ++i, ++s)
+ printf ("%x ", (int) * s & 0xff);
+ printf ("\n");
+}
+
+mbuf_cache(c, m)
+register struct mbuf_cache *c;
+struct mbuf *m;
+{
+ register struct mbuf **mp;
+
+ if (c->mbc_size != c->mbc_oldsize) {
+ unsigned zero_size, copy_size;
+ unsigned new_size = c->mbc_size * sizeof(m);
+ caddr_t cache = (caddr_t)c->mbc_cache;
+
+ if (new_size) {
+ c->mbc_cache = (struct mbuf **)
+ malloc(new_size, M_MBUF, M_NOWAIT);
+ if (c->mbc_cache == 0) {
+ c->mbc_cache = (struct mbuf **)cache;
+ return;
+ }
+ c->mbc_num %= c->mbc_size;
+ } else
+ c->mbc_cache = 0;
+ if (c->mbc_size < c->mbc_oldsize) {
+ register struct mbuf **mplim;
+ mp = c->mbc_size + (struct mbuf **)cache;
+ mplim = c->mbc_oldsize + (struct mbuf **)cache;
+ while (mp < mplim)
+ m_freem(*mp++);
+ zero_size = 0;
+ } else
+ zero_size = (c->mbc_size - c->mbc_oldsize) * sizeof(m);
+ copy_size = new_size - zero_size;
+ c->mbc_oldsize = c->mbc_size;
+ if (copy_size)
+ bcopy(cache, (caddr_t)c->mbc_cache, copy_size);
+ if (cache)
+ free(cache, M_MBUF);
+ if (zero_size)
+ bzero(copy_size + (caddr_t)c->mbc_cache, zero_size);
+ }
+ if (c->mbc_size == 0)
+ return;
+ mp = c->mbc_cache + c->mbc_num;
+ c->mbc_num = (1 + c->mbc_num) % c->mbc_size;
+ if (*mp)
+ m_freem(*mp);
+ if (*mp = m_copym(m, 0, M_COPYALL, M_DONTWAIT))
+ (*mp)->m_flags |= m->m_flags & 0x08;
+}
diff --git a/sys/netccitt/pk_input.c b/sys/netccitt/pk_input.c
new file mode 100644
index 0000000..1f8f0bc
--- /dev/null
+++ b/sys/netccitt/pk_input.c
@@ -0,0 +1,1119 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (C) Computer Science Department IV,
+ * University of Erlangen-Nuremberg, Germany, 1992
+ * Copyright (c) 1991, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by the
+ * Laboratory for Computation Vision and the Computer Science Department
+ * of the the University of British Columbia and the Computer Science
+ * Department (IV) of the University of Erlangen-Nuremberg, Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pk_input.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_llc.h>
+#include <net/route.h>
+
+#include <netccitt/dll.h>
+#include <netccitt/x25.h>
+#include <netccitt/pk.h>
+#include <netccitt/pk_var.h>
+#include <netccitt/llc_var.h>
+
+struct pkcb_q pkcb_q = {&pkcb_q, &pkcb_q};
+
+/*
+ * ccittintr() is the generic interrupt handler for HDLC, LLC2, and X.25. This
+ * allows to have kernel running X.25 but no HDLC or LLC2 or both (in case we
+ * employ boards that do all the stuff themselves, e.g. ADAX X.25 or TPS ISDN.)
+ */
+void
+ccittintr ()
+{
+ extern struct ifqueue pkintrq;
+ extern struct ifqueue hdintrq;
+ extern struct ifqueue llcintrq;
+
+#ifdef HDLC
+ if (hdintrq.ifq_len)
+ hdintr ();
+#endif
+#ifdef LLC
+ if (llcintrq.ifq_len)
+ llcintr ();
+#endif
+ if (pkintrq.ifq_len)
+ pkintr ();
+}
+
+struct pkcb *
+pk_newlink (ia, llnext)
+struct x25_ifaddr *ia;
+caddr_t llnext;
+{
+ register struct x25config *xcp = &ia -> ia_xc;
+ register struct pkcb *pkp;
+ register struct pklcd *lcp;
+ register struct protosw *pp;
+ unsigned size;
+
+ pp = pffindproto (AF_CCITT, (int) xcp -> xc_lproto, 0);
+ if (pp == 0 || pp -> pr_output == 0) {
+ pk_message (0, xcp, "link level protosw error");
+ return ((struct pkcb *)0);
+ }
+ /*
+ * Allocate a network control block structure
+ */
+ size = sizeof (struct pkcb);
+ pkp = (struct pkcb *) malloc (size, M_PCB, M_WAITOK);
+ if (pkp == 0)
+ return ((struct pkcb *)0);
+ bzero ((caddr_t) pkp, size);
+ pkp -> pk_lloutput = pp -> pr_output;
+ pkp -> pk_llctlinput = (caddr_t (*)()) pp -> pr_ctlinput;
+ pkp -> pk_xcp = xcp;
+ pkp -> pk_ia = ia;
+ pkp -> pk_state = DTE_WAITING;
+ pkp -> pk_llnext = llnext;
+ insque (pkp, &pkcb_q);
+
+ /*
+ * set defaults
+ */
+
+ if (xcp -> xc_pwsize == 0)
+ xcp -> xc_pwsize = DEFAULT_WINDOW_SIZE;
+ if (xcp -> xc_psize == 0)
+ xcp -> xc_psize = X25_PS128;
+ /*
+ * Allocate logical channel descriptor vector
+ */
+
+ (void) pk_resize (pkp);
+ return (pkp);
+}
+
+
+pk_dellink (pkp)
+register struct pkcb *pkp;
+{
+ register int i;
+ register struct protosw *pp;
+
+ /*
+ * Essentially we have the choice to
+ * (a) go ahead and let the route be deleted and
+ * leave the pkcb associated with that route
+ * as it is, i.e. the connections stay open
+ * (b) do a pk_disconnect() on all channels associated
+ * with the route via the pkcb and then proceed.
+ *
+ * For the time being we stick with (b)
+ */
+
+ for (i = 1; i < pkp -> pk_maxlcn; ++i)
+ if (pkp -> pk_chan[i])
+ pk_disconnect (pkp -> pk_chan[i]);
+
+ /*
+ * Free the pkcb
+ */
+
+ /*
+ * First find the protoswitch to get hold of the link level
+ * protocol to be notified that the packet level entity is
+ * dissolving ...
+ */
+ pp = pffindproto (AF_CCITT, (int) pkp -> pk_xcp -> xc_lproto, 0);
+ if (pp == 0 || pp -> pr_output == 0) {
+ pk_message (0, pkp -> pk_xcp, "link level protosw error");
+ return (EPROTONOSUPPORT);
+ }
+
+ pkp -> pk_refcount--;
+ if (!pkp -> pk_refcount) {
+ struct dll_ctlinfo ctlinfo;
+
+ remque (pkp);
+ if (pkp -> pk_rt -> rt_llinfo == (caddr_t) pkp)
+ pkp -> pk_rt -> rt_llinfo = (caddr_t) NULL;
+
+ /*
+ * Tell the link level that the pkcb is dissolving
+ */
+ if (pp -> pr_ctlinput && pkp -> pk_llnext) {
+ ctlinfo.dlcti_pcb = pkp -> pk_llnext;
+ ctlinfo.dlcti_rt = pkp -> pk_rt;
+ (pp -> pr_ctlinput)(PRC_DISCONNECT_REQUEST,
+ pkp -> pk_xcp, &ctlinfo);
+ }
+ free ((caddr_t) pkp -> pk_chan, M_IFADDR);
+ free ((caddr_t) pkp, M_PCB);
+ }
+
+ return (0);
+}
+
+
+pk_resize (pkp)
+register struct pkcb *pkp;
+{
+ struct pklcd *dev_lcp = 0;
+ struct x25config *xcp = pkp -> pk_xcp;
+ if (pkp -> pk_chan &&
+ (pkp -> pk_maxlcn != xcp -> xc_maxlcn)) {
+ pk_restart (pkp, X25_RESTART_NETWORK_CONGESTION);
+ dev_lcp = pkp -> pk_chan[0];
+ free ((caddr_t) pkp -> pk_chan, M_IFADDR);
+ pkp -> pk_chan = 0;
+ }
+ if (pkp -> pk_chan == 0) {
+ unsigned size;
+ pkp -> pk_maxlcn = xcp -> xc_maxlcn;
+ size = (pkp -> pk_maxlcn + 1) * sizeof (struct pklcd *);
+ pkp -> pk_chan =
+ (struct pklcd **) malloc (size, M_IFADDR, M_WAITOK);
+ if (pkp -> pk_chan) {
+ bzero ((caddr_t) pkp -> pk_chan, size);
+ /*
+ * Allocate a logical channel descriptor for lcn 0
+ */
+ if (dev_lcp == 0 &&
+ (dev_lcp = pk_attach ((struct socket *)0)) == 0)
+ return (ENOBUFS);
+ dev_lcp -> lcd_state = READY;
+ dev_lcp -> lcd_pkp = pkp;
+ pkp -> pk_chan[0] = dev_lcp;
+ } else {
+ if (dev_lcp)
+ pk_close (dev_lcp);
+ return (ENOBUFS);
+ }
+ }
+ return 0;
+}
+
+/*
+ * This procedure is called by the link level whenever the link
+ * becomes operational, is reset, or when the link goes down.
+ */
+/*VARARGS*/
+caddr_t
+pk_ctlinput (code, src, addr)
+ struct sockaddr *src;
+ caddr_t addr;
+{
+ register struct pkcb *pkp = (struct pkcb *) addr;
+
+ switch (code) {
+ case PRC_LINKUP:
+ if (pkp -> pk_state == DTE_WAITING)
+ pk_restart (pkp, X25_RESTART_NETWORK_CONGESTION);
+ break;
+
+ case PRC_LINKDOWN:
+ pk_restart (pkp, -1); /* Clear all active circuits */
+ pkp -> pk_state = DTE_WAITING;
+ break;
+
+ case PRC_LINKRESET:
+ pk_restart (pkp, X25_RESTART_NETWORK_CONGESTION);
+ break;
+
+ case PRC_CONNECT_INDICATION: {
+ struct rtentry *llrt;
+
+ if ((llrt = rtalloc1(src, 0)) == 0)
+ return 0;
+ else llrt -> rt_refcnt--;
+
+ pkp = (((struct npaidbentry *) llrt -> rt_llinfo) -> np_rt) ?
+ (struct pkcb *)(((struct npaidbentry *) llrt -> rt_llinfo) -> np_rt -> rt_llinfo) : (struct pkcb *) 0;
+ if (pkp == (struct pkcb *) 0)
+ return 0;
+ pkp -> pk_llnext = addr;
+
+ return ((caddr_t) pkp);
+ }
+ case PRC_DISCONNECT_INDICATION:
+ pk_restart (pkp, -1) ; /* Clear all active circuits */
+ pkp -> pk_state = DTE_WAITING;
+ pkp -> pk_llnext = (caddr_t) 0;
+ }
+ return (0);
+}
+struct ifqueue pkintrq;
+/*
+ * This routine is called if there are semi-smart devices that do HDLC
+ * in hardware and want to queue the packet and call level 3 directly
+ */
+pkintr ()
+{
+ register struct mbuf *m;
+ register struct ifaddr *ifa;
+ register struct ifnet *ifp;
+ register int s;
+
+ for (;;) {
+ s = splimp ();
+ IF_DEQUEUE (&pkintrq, m);
+ splx (s);
+ if (m == 0)
+ break;
+ if (m -> m_len < PKHEADERLN) {
+ printf ("pkintr: packet too short (len=%d)\n",
+ m -> m_len);
+ m_freem (m);
+ continue;
+ }
+ pk_input (m);
+ }
+}
+struct mbuf *pk_bad_packet;
+struct mbuf_cache pk_input_cache = {0 };
+/*
+ * X.25 PACKET INPUT
+ *
+ * This procedure is called by a link level procedure whenever
+ * an information frame is received. It decodes the packet and
+ * demultiplexes based on the logical channel number.
+ *
+ * We change the original conventions of the UBC code here --
+ * since there may be multiple pkcb's for a given interface
+ * of type 802.2 class 2, we retrieve which one it is from
+ * m_pkthdr.rcvif (which has been overwritten by lower layers);
+ * That field is then restored for the benefit of upper layers which
+ * may make use of it, such as CLNP.
+ *
+ */
+
+#define RESTART_DTE_ORIGINATED(xp) (((xp) -> packet_cause == X25_RESTART_DTE_ORIGINATED) || \
+ ((xp) -> packet_cause >= X25_RESTART_DTE_ORIGINATED2))
+
+pk_input (m)
+register struct mbuf *m;
+{
+ register struct x25_packet *xp;
+ register struct pklcd *lcp;
+ register struct socket *so = 0;
+ register struct pkcb *pkp;
+ int ptype, lcn, lcdstate = LISTEN;
+
+ if (pk_input_cache.mbc_size || pk_input_cache.mbc_oldsize)
+ mbuf_cache (&pk_input_cache, m);
+ if ((m -> m_flags & M_PKTHDR) == 0)
+ panic ("pkintr");
+
+ if ((pkp = (struct pkcb *) m -> m_pkthdr.rcvif) == 0)
+ return;
+ xp = mtod (m, struct x25_packet *);
+ ptype = pk_decode (xp);
+ lcn = LCN(xp);
+ lcp = pkp -> pk_chan[lcn];
+
+ /*
+ * If the DTE is in Restart state, then it will ignore data,
+ * interrupt, call setup and clearing, flow control and reset
+ * packets.
+ */
+ if (lcn < 0 || lcn > pkp -> pk_maxlcn) {
+ pk_message (lcn, pkp -> pk_xcp, "illegal lcn");
+ m_freem (m);
+ return;
+ }
+
+ pk_trace (pkp -> pk_xcp, m, "P-In");
+
+ if (pkp -> pk_state != DTE_READY && ptype != RESTART && ptype != RESTART_CONF) {
+ m_freem (m);
+ return;
+ }
+ if (lcp) {
+ so = lcp -> lcd_so;
+ lcdstate = lcp -> lcd_state;
+ } else {
+ if (ptype == CLEAR) { /* idle line probe (Datapac specific) */
+ /* send response on lcd 0's output queue */
+ lcp = pkp -> pk_chan[0];
+ lcp -> lcd_template = pk_template (lcn, X25_CLEAR_CONFIRM);
+ pk_output (lcp);
+ m_freem (m);
+ return;
+ }
+ if (ptype != CALL)
+ ptype = INVALID_PACKET;
+ }
+
+ if (lcn == 0 && ptype != RESTART && ptype != RESTART_CONF) {
+ pk_message (0, pkp -> pk_xcp, "illegal ptype (%d, %s) on lcn 0",
+ ptype, pk_name[ptype / MAXSTATES]);
+ if (pk_bad_packet)
+ m_freem (pk_bad_packet);
+ pk_bad_packet = m;
+ return;
+ }
+
+ m -> m_pkthdr.rcvif = pkp -> pk_ia -> ia_ifp;
+
+ switch (ptype + lcdstate) {
+ /*
+ * Incoming Call packet received.
+ */
+ case CALL + LISTEN:
+ pk_incoming_call (pkp, m);
+ break;
+
+ /*
+ * Call collision: Just throw this "incoming call" away since
+ * the DCE will ignore it anyway.
+ */
+ case CALL + SENT_CALL:
+ pk_message ((int) lcn, pkp -> pk_xcp,
+ "incoming call collision");
+ break;
+
+ /*
+ * Call confirmation packet received. This usually means our
+ * previous connect request is now complete.
+ */
+ case CALL_ACCEPTED + SENT_CALL:
+ MCHTYPE(m, MT_CONTROL);
+ pk_call_accepted (lcp, m);
+ break;
+
+ /*
+ * This condition can only happen if the previous state was
+ * SENT_CALL. Just ignore the packet, eventually a clear
+ * confirmation should arrive.
+ */
+ case CALL_ACCEPTED + SENT_CLEAR:
+ break;
+
+ /*
+ * Clear packet received. This requires a complete tear down
+ * of the virtual circuit. Free buffers and control blocks.
+ * and send a clear confirmation.
+ */
+ case CLEAR + READY:
+ case CLEAR + RECEIVED_CALL:
+ case CLEAR + SENT_CALL:
+ case CLEAR + DATA_TRANSFER:
+ lcp -> lcd_state = RECEIVED_CLEAR;
+ lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CLEAR_CONFIRM);
+ pk_output (lcp);
+ pk_clearcause (pkp, xp);
+ if (lcp -> lcd_upper) {
+ MCHTYPE(m, MT_CONTROL);
+ lcp -> lcd_upper (lcp, m);
+ }
+ pk_close (lcp);
+ lcp = 0;
+ break;
+
+ /*
+ * Clear collision: Treat this clear packet as a confirmation.
+ */
+ case CLEAR + SENT_CLEAR:
+ pk_close (lcp);
+ break;
+
+ /*
+ * Clear confirmation received. This usually means the virtual
+ * circuit is now completely removed.
+ */
+ case CLEAR_CONF + SENT_CLEAR:
+ pk_close (lcp);
+ break;
+
+ /*
+ * A clear confirmation on an unassigned logical channel - just
+ * ignore it. Note: All other packets on an unassigned channel
+ * results in a clear.
+ */
+ case CLEAR_CONF + READY:
+ case CLEAR_CONF + LISTEN:
+ break;
+
+ /*
+ * Data packet received. Pass on to next level. Move the Q and M
+ * bits into the data portion for the next level.
+ */
+ case DATA + DATA_TRANSFER:
+ if (lcp -> lcd_reset_condition) {
+ ptype = DELETE_PACKET;
+ break;
+ }
+
+ /*
+ * Process the P(S) flow control information in this Data packet.
+ * Check that the packets arrive in the correct sequence and that
+ * they are within the "lcd_input_window". Input window rotation is
+ * initiated by the receive interface.
+ */
+
+ if (PS(xp) != ((lcp -> lcd_rsn + 1) % MODULUS) ||
+ PS(xp) == ((lcp -> lcd_input_window + lcp -> lcd_windowsize) % MODULUS)) {
+ m_freem (m);
+ pk_procerror (RESET, lcp, "p(s) flow control error", 1);
+ break;
+ }
+ lcp -> lcd_rsn = PS(xp);
+
+ if (pk_ack (lcp, PR(xp)) != PACKET_OK) {
+ m_freem (m);
+ break;
+ }
+ m -> m_data += PKHEADERLN;
+ m -> m_len -= PKHEADERLN;
+ m -> m_pkthdr.len -= PKHEADERLN;
+
+ lcp -> lcd_rxcnt++;
+ if (lcp -> lcd_flags & X25_MBS_HOLD) {
+ register struct mbuf *n = lcp -> lcd_cps;
+ int mbit = MBIT(xp);
+ octet q_and_d_bits;
+
+ if (n) {
+ n -> m_pkthdr.len += m -> m_pkthdr.len;
+ while (n -> m_next)
+ n = n -> m_next;
+ n -> m_next = m;
+ m = lcp -> lcd_cps;
+
+ if (lcp -> lcd_cpsmax &&
+ n -> m_pkthdr.len > lcp -> lcd_cpsmax) {
+ pk_procerror (RESET, lcp,
+ "C.P.S. overflow", 128);
+ return;
+ }
+ q_and_d_bits = 0xc0 & *(octet *) xp;
+ xp = (struct x25_packet *)
+ (mtod (m, octet *) - PKHEADERLN);
+ *(octet *) xp |= q_and_d_bits;
+ }
+ if (mbit) {
+ lcp -> lcd_cps = m;
+ pk_flowcontrol (lcp, 0, 1);
+ return;
+ }
+ lcp -> lcd_cps = 0;
+ }
+ if (so == 0)
+ break;
+ if (lcp -> lcd_flags & X25_MQBIT) {
+ octet t = (X25GBITS(xp -> bits, q_bit)) ? t = 0x80 : 0;
+
+ if (MBIT(xp))
+ t |= 0x40;
+ m -> m_data -= 1;
+ m -> m_len += 1;
+ m -> m_pkthdr.len += 1;
+ *mtod (m, octet *) = t;
+ }
+
+ /*
+ * Discard Q-BIT packets if the application
+ * doesn't want to be informed of M and Q bit status
+ */
+ if (X25GBITS(xp -> bits, q_bit)
+ && (lcp -> lcd_flags & X25_MQBIT) == 0) {
+ m_freem (m);
+ /*
+ * NB. This is dangerous: sending a RR here can
+ * cause sequence number errors if a previous data
+ * packet has not yet been passed up to the application
+ * (RR's are normally generated via PRU_RCVD).
+ */
+ pk_flowcontrol (lcp, 0, 1);
+ } else {
+ sbappendrecord (&so -> so_rcv, m);
+ sorwakeup (so);
+ }
+ break;
+
+ /*
+ * Interrupt packet received.
+ */
+ case INTERRUPT + DATA_TRANSFER:
+ if (lcp -> lcd_reset_condition)
+ break;
+ lcp -> lcd_intrdata = xp -> packet_data;
+ lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_INTERRUPT_CONFIRM);
+ pk_output (lcp);
+ m -> m_data += PKHEADERLN;
+ m -> m_len -= PKHEADERLN;
+ m -> m_pkthdr.len -= PKHEADERLN;
+ MCHTYPE(m, MT_OOBDATA);
+ if (so) {
+ if (so -> so_options & SO_OOBINLINE)
+ sbinsertoob (&so -> so_rcv, m);
+ else
+ m_freem (m);
+ sohasoutofband (so);
+ }
+ break;
+
+ /*
+ * Interrupt confirmation packet received.
+ */
+ case INTERRUPT_CONF + DATA_TRANSFER:
+ if (lcp -> lcd_reset_condition)
+ break;
+ if (lcp -> lcd_intrconf_pending == TRUE)
+ lcp -> lcd_intrconf_pending = FALSE;
+ else
+ pk_procerror (RESET, lcp, "unexpected packet", 43);
+ break;
+
+ /*
+ * Receiver ready received. Rotate the output window and output
+ * any data packets waiting transmission.
+ */
+ case RR + DATA_TRANSFER:
+ if (lcp -> lcd_reset_condition ||
+ pk_ack (lcp, PR(xp)) != PACKET_OK) {
+ ptype = DELETE_PACKET;
+ break;
+ }
+ if (lcp -> lcd_rnr_condition == TRUE)
+ lcp -> lcd_rnr_condition = FALSE;
+ pk_output (lcp);
+ break;
+
+ /*
+ * Receiver Not Ready received. Packets up to the P(R) can be
+ * be sent. Condition is cleared with a RR.
+ */
+ case RNR + DATA_TRANSFER:
+ if (lcp -> lcd_reset_condition ||
+ pk_ack (lcp, PR(xp)) != PACKET_OK) {
+ ptype = DELETE_PACKET;
+ break;
+ }
+ lcp -> lcd_rnr_condition = TRUE;
+ break;
+
+ /*
+ * Reset packet received. Set state to FLOW_OPEN. The Input and
+ * Output window edges ar set to zero. Both the send and receive
+ * numbers are reset. A confirmation is returned.
+ */
+ case RESET + DATA_TRANSFER:
+ if (lcp -> lcd_reset_condition)
+ /* Reset collision. Just ignore packet. */
+ break;
+
+ pk_resetcause (pkp, xp);
+ lcp -> lcd_window_condition = lcp -> lcd_rnr_condition =
+ lcp -> lcd_intrconf_pending = FALSE;
+ lcp -> lcd_output_window = lcp -> lcd_input_window =
+ lcp -> lcd_last_transmitted_pr = 0;
+ lcp -> lcd_ssn = 0;
+ lcp -> lcd_rsn = MODULUS - 1;
+
+ lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RESET_CONFIRM);
+ pk_output (lcp);
+
+ pk_flush (lcp);
+ if (so == 0)
+ break;
+ wakeup ((caddr_t) & so -> so_timeo);
+ sorwakeup (so);
+ sowwakeup (so);
+ break;
+
+ /*
+ * Reset confirmation received.
+ */
+ case RESET_CONF + DATA_TRANSFER:
+ if (lcp -> lcd_reset_condition) {
+ lcp -> lcd_reset_condition = FALSE;
+ pk_output (lcp);
+ }
+ else
+ pk_procerror (RESET, lcp, "unexpected packet", 32);
+ break;
+
+ case DATA + SENT_CLEAR:
+ ptype = DELETE_PACKET;
+ case RR + SENT_CLEAR:
+ case RNR + SENT_CLEAR:
+ case INTERRUPT + SENT_CLEAR:
+ case INTERRUPT_CONF + SENT_CLEAR:
+ case RESET + SENT_CLEAR:
+ case RESET_CONF + SENT_CLEAR:
+ /* Just ignore p if we have sent a CLEAR already.
+ */
+ break;
+
+ /*
+ * Restart sets all the permanent virtual circuits to the "Data
+ * Transfer" stae and all the switched virtual circuits to the
+ * "Ready" state.
+ */
+ case RESTART + READY:
+ switch (pkp -> pk_state) {
+ case DTE_SENT_RESTART:
+ /*
+ * Restart collision.
+ * If case the restart cause is "DTE originated" we
+ * have a DTE-DTE situation and are trying to resolve
+ * who is going to play DTE/DCE [ISO 8208:4.2-4.5]
+ */
+ if (RESTART_DTE_ORIGINATED(xp)) {
+ pk_restart (pkp, X25_RESTART_DTE_ORIGINATED);
+ pk_message (0, pkp -> pk_xcp,
+ "RESTART collision");
+ if ((pkp -> pk_restartcolls++) > MAXRESTARTCOLLISIONS) {
+ pk_message (0, pkp -> pk_xcp,
+ "excessive RESTART collisions");
+ pkp -> pk_restartcolls = 0;
+ }
+ break;
+ }
+ pkp -> pk_state = DTE_READY;
+ pkp -> pk_dxerole |= DTE_PLAYDTE;
+ pkp -> pk_dxerole &= ~DTE_PLAYDCE;
+ pk_message (0, pkp -> pk_xcp,
+ "Packet level operational");
+ pk_message (0, pkp -> pk_xcp,
+ "Assuming DTE role");
+ if (pkp -> pk_dxerole & DTE_CONNECTPENDING)
+ pk_callcomplete (pkp);
+ break;
+
+ default:
+ pk_restart (pkp, -1);
+ pk_restartcause (pkp, xp);
+ pkp -> pk_chan[0] -> lcd_template = pk_template (0,
+ X25_RESTART_CONFIRM);
+ pk_output (pkp -> pk_chan[0]);
+ pkp -> pk_state = DTE_READY;
+ pkp -> pk_dxerole |= RESTART_DTE_ORIGINATED(xp) ? DTE_PLAYDCE :
+ DTE_PLAYDTE;
+ if (pkp -> pk_dxerole & DTE_PLAYDTE) {
+ pkp -> pk_dxerole &= ~DTE_PLAYDCE;
+ pk_message (0, pkp -> pk_xcp,
+ "Assuming DTE role");
+ } else {
+ pkp -> pk_dxerole &= ~DTE_PLAYDTE;
+ pk_message (0, pkp -> pk_xcp,
+ "Assuming DCE role");
+ }
+ if (pkp -> pk_dxerole & DTE_CONNECTPENDING)
+ pk_callcomplete (pkp);
+ }
+ break;
+
+ /*
+ * Restart confirmation received. All logical channels are set
+ * to READY.
+ */
+ case RESTART_CONF + READY:
+ switch (pkp -> pk_state) {
+ case DTE_SENT_RESTART:
+ pkp -> pk_state = DTE_READY;
+ pkp -> pk_dxerole |= DTE_PLAYDTE;
+ pkp -> pk_dxerole &= ~DTE_PLAYDCE;
+ pk_message (0, pkp -> pk_xcp,
+ "Packet level operational");
+ pk_message (0, pkp -> pk_xcp,
+ "Assuming DTE role");
+ if (pkp -> pk_dxerole & DTE_CONNECTPENDING)
+ pk_callcomplete (pkp);
+ break;
+
+ default:
+ /* Restart local procedure error. */
+ pk_restart (pkp, X25_RESTART_LOCAL_PROCEDURE_ERROR);
+ pkp -> pk_state = DTE_SENT_RESTART;
+ pkp -> pk_dxerole &= ~(DTE_PLAYDTE | DTE_PLAYDCE);
+ }
+ break;
+
+ default:
+ if (lcp) {
+ pk_procerror (CLEAR, lcp, "unknown packet error", 33);
+ pk_message (lcn, pkp -> pk_xcp,
+ "\"%s\" unexpected in \"%s\" state",
+ pk_name[ptype/MAXSTATES], pk_state[lcdstate]);
+ } else
+ pk_message (lcn, pkp -> pk_xcp,
+ "packet arrived on unassigned lcn");
+ break;
+ }
+ if (so == 0 && lcp && lcp -> lcd_upper && lcdstate == DATA_TRANSFER) {
+ if (ptype != DATA && ptype != INTERRUPT)
+ MCHTYPE(m, MT_CONTROL);
+ lcp -> lcd_upper (lcp, m);
+ } else if (ptype != DATA && ptype != INTERRUPT)
+ m_freem (m);
+}
+
+static
+prune_dnic (from, to, dnicname, xcp)
+char *from, *to, *dnicname;
+register struct x25config *xcp;
+{
+ register char *cp1 = from, *cp2 = from;
+ if (xcp -> xc_prepnd0 && *cp1 == '0') {
+ from = ++cp1;
+ goto copyrest;
+ }
+ if (xcp -> xc_nodnic) {
+ for (cp1 = dnicname; *cp2 = *cp1++;)
+ cp2++;
+ cp1 = from;
+ }
+copyrest:
+ for (cp1 = dnicname; *cp2 = *cp1++;)
+ cp2++;
+}
+/* static */
+pk_simple_bsd (from, to, lower, len)
+register octet *from, *to;
+register len, lower;
+{
+ register int c;
+ while (--len >= 0) {
+ c = *from;
+ if (lower & 0x01)
+ *from++;
+ else
+ c >>= 4;
+ c &= 0x0f; c |= 0x30; *to++ = c; lower++;
+ }
+ *to = 0;
+}
+
+/*static octet * */
+pk_from_bcd (a, iscalling, sa, xcp)
+register struct x25_calladdr *a;
+register struct sockaddr_x25 *sa;
+register struct x25config *xcp;
+{
+ octet buf[MAXADDRLN+1];
+ octet *cp;
+ unsigned count;
+
+ bzero ((caddr_t) sa, sizeof (*sa));
+ sa -> x25_len = sizeof (*sa);
+ sa -> x25_family = AF_CCITT;
+ if (iscalling) {
+ cp = a -> address_field + (X25GBITS(a -> addrlens, called_addrlen) / 2);
+ count = X25GBITS(a -> addrlens, calling_addrlen);
+ pk_simple_bsd (cp, buf, X25GBITS(a -> addrlens, called_addrlen), count);
+ } else {
+ count = X25GBITS(a -> addrlens, called_addrlen);
+ pk_simple_bsd (a -> address_field, buf, 0, count);
+ }
+ if (xcp -> xc_addr.x25_net && (xcp -> xc_nodnic || xcp -> xc_prepnd0)) {
+ octet dnicname[sizeof (long) * NBBY/3 + 2];
+
+ sprintf ((char *) dnicname, "%d", xcp -> xc_addr.x25_net);
+ prune_dnic ((char *) buf, sa -> x25_addr, dnicname, xcp);
+ } else
+ bcopy ((caddr_t) buf, (caddr_t) sa -> x25_addr, count + 1);
+}
+
+static
+save_extra (m0, fp, so)
+struct mbuf *m0;
+octet *fp;
+struct socket *so;
+{
+ register struct mbuf *m;
+ struct cmsghdr cmsghdr;
+ if (m = m_copy (m, 0, (int)M_COPYALL)) {
+ int off = fp - mtod (m0, octet *);
+ int len = m -> m_pkthdr.len - off + sizeof (cmsghdr);
+ cmsghdr.cmsg_len = len;
+ cmsghdr.cmsg_level = AF_CCITT;
+ cmsghdr.cmsg_type = PK_FACILITIES;
+ m_adj (m, off);
+ M_PREPEND (m, sizeof (cmsghdr), M_DONTWAIT);
+ if (m == 0)
+ return;
+ bcopy ((caddr_t)&cmsghdr, mtod (m, caddr_t), sizeof (cmsghdr));
+ MCHTYPE(m, MT_CONTROL);
+ sbappendrecord (&so -> so_rcv, m);
+ }
+}
+
+/*
+ * This routine handles incoming call packets. It matches the protocol
+ * field on the Call User Data field (usually the first four bytes) with
+ * sockets awaiting connections.
+ */
+
+pk_incoming_call (pkp, m0)
+struct mbuf *m0;
+struct pkcb *pkp;
+{
+ register struct pklcd *lcp = 0, *l;
+ register struct sockaddr_x25 *sa;
+ register struct x25_calladdr *a;
+ register struct socket *so = 0;
+ struct x25_packet *xp = mtod (m0, struct x25_packet *);
+ struct mbuf *m;
+ struct x25config *xcp = pkp -> pk_xcp;
+ int len = m0 -> m_pkthdr.len;
+ unsigned udlen;
+ char *errstr = "server unavailable";
+ octet *u, *facp;
+ int lcn = LCN(xp);
+
+ /* First, copy the data from the incoming call packet to a X25 address
+ descriptor. It is to be regretted that you have
+ to parse the facilities into a sockaddr to determine
+ if reverse charging is being requested */
+ if ((m = m_get (M_DONTWAIT, MT_SONAME)) == 0)
+ return;
+ sa = mtod (m, struct sockaddr_x25 *);
+ a = (struct x25_calladdr *) &xp -> packet_data;
+ facp = u = (octet *) (a -> address_field +
+ ((X25GBITS(a -> addrlens, called_addrlen) + X25GBITS(a -> addrlens, calling_addrlen) + 1) / 2));
+ u += *u + 1;
+ udlen = min (16, ((octet *) xp) + len - u);
+ if (udlen < 0)
+ udlen = 0;
+ pk_from_bcd (a, 1, sa, pkp -> pk_xcp); /* get calling address */
+ pk_parse_facilities (facp, sa);
+ bcopy ((caddr_t) u, sa -> x25_udata, udlen);
+ sa -> x25_udlen = udlen;
+
+ /*
+ * Now, loop through the listen sockets looking for a match on the
+ * PID. That is the first few octets of the user data field.
+ * This is the closest thing to a port number for X.25 packets.
+ * It does provide a way of multiplexing services at the user level.
+ */
+
+ for (l = pk_listenhead; l; l = l -> lcd_listen) {
+ struct sockaddr_x25 *sxp = l -> lcd_ceaddr;
+
+ if (bcmp (sxp -> x25_udata, u, sxp -> x25_udlen))
+ continue;
+ if (sxp -> x25_net &&
+ sxp -> x25_net != xcp -> xc_addr.x25_net)
+ continue;
+ /*
+ * don't accept incoming calls with the D-Bit on
+ * unless the server agrees
+ */
+ if (X25GBITS(xp -> bits, d_bit) && !(sxp -> x25_opts.op_flags & X25_DBIT)) {
+ errstr = "incoming D-Bit mismatch";
+ break;
+ }
+ /*
+ * don't accept incoming collect calls unless
+ * the server sets the reverse charging option.
+ */
+ if ((sxp -> x25_opts.op_flags & (X25_OLDSOCKADDR|X25_REVERSE_CHARGE)) == 0 &&
+ sa -> x25_opts.op_flags & X25_REVERSE_CHARGE) {
+ errstr = "incoming collect call refused";
+ break;
+ }
+ if (l -> lcd_so) {
+ if (so = sonewconn (l -> lcd_so, SS_ISCONNECTED))
+ lcp = (struct pklcd *) so -> so_pcb;
+ } else
+ lcp = pk_attach ((struct socket *) 0);
+ if (lcp == 0) {
+ /*
+ * Insufficient space or too many unaccepted
+ * connections. Just throw the call away.
+ */
+ errstr = "server malfunction";
+ break;
+ }
+ lcp -> lcd_upper = l -> lcd_upper;
+ lcp -> lcd_upnext = l -> lcd_upnext;
+ lcp -> lcd_lcn = lcn;
+ lcp -> lcd_state = RECEIVED_CALL;
+ sa -> x25_opts.op_flags |= (sxp -> x25_opts.op_flags &
+ ~X25_REVERSE_CHARGE) | l -> lcd_flags;
+ pk_assoc (pkp, lcp, sa);
+ lcp -> lcd_faddr = *sa;
+ lcp -> lcd_laddr.x25_udlen = sxp -> x25_udlen;
+ lcp -> lcd_craddr = &lcp -> lcd_faddr;
+ lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CALL_ACCEPTED);
+ if (lcp -> lcd_flags & X25_DBIT) {
+ if (X25GBITS(xp -> bits, d_bit))
+ X25SBITS(mtod (lcp -> lcd_template,
+ struct x25_packet *) -> bits, d_bit, 1);
+ else
+ lcp -> lcd_flags &= ~X25_DBIT;
+ }
+ if (so) {
+ pk_output (lcp);
+ soisconnected (so);
+ if (so -> so_options & SO_OOBINLINE)
+ save_extra (m0, facp, so);
+ } else if (lcp -> lcd_upper) {
+ (*lcp -> lcd_upper) (lcp, m0);
+ }
+ (void) m_free (m);
+ return;
+ }
+
+ /*
+ * If the call fails for whatever reason, we still need to build a
+ * skeleton LCD in order to be able to properly receive the CLEAR
+ * CONFIRMATION.
+ */
+#ifdef WATERLOO /* be explicit */
+ if (l == 0 && bcmp (sa -> x25_udata, "ean", 3) == 0)
+ pk_message (lcn, pkp -> pk_xcp, "host=%s ean%c: %s",
+ sa -> x25_addr, sa -> x25_udata[3] & 0xff, errstr);
+ else if (l == 0 && bcmp (sa -> x25_udata, "\1\0\0\0", 4) == 0)
+ pk_message (lcn, pkp -> pk_xcp, "host=%s x29d: %s",
+ sa -> x25_addr, errstr);
+ else
+#endif
+ pk_message (lcn, pkp -> pk_xcp, "host=%s pid=%x %x %x %x: %s",
+ sa -> x25_addr, sa -> x25_udata[0] & 0xff,
+ sa -> x25_udata[1] & 0xff, sa -> x25_udata[2] & 0xff,
+ sa -> x25_udata[3] & 0xff, errstr);
+ if ((lcp = pk_attach ((struct socket *)0)) == 0) {
+ (void) m_free (m);
+ return;
+ }
+ lcp -> lcd_lcn = lcn;
+ lcp -> lcd_state = RECEIVED_CALL;
+ pk_assoc (pkp, lcp, sa);
+ (void) m_free (m);
+ pk_clear (lcp, 0, 1);
+}
+
+pk_call_accepted (lcp, m)
+struct pklcd *lcp;
+struct mbuf *m;
+{
+ register struct x25_calladdr *ap;
+ register octet *fcp;
+ struct x25_packet *xp = mtod (m, struct x25_packet *);
+ int len = m -> m_len;
+
+ lcp -> lcd_state = DATA_TRANSFER;
+ if (lcp -> lcd_so)
+ soisconnected (lcp -> lcd_so);
+ if ((lcp -> lcd_flags & X25_DBIT) && (X25GBITS(xp -> bits, d_bit) == 0))
+ lcp -> lcd_flags &= ~X25_DBIT;
+ if (len > 3) {
+ ap = (struct x25_calladdr *) &xp -> packet_data;
+ fcp = (octet *) ap -> address_field + (X25GBITS(ap -> addrlens, calling_addrlen) +
+ X25GBITS(ap -> addrlens, called_addrlen) + 1) / 2;
+ if (fcp + *fcp <= ((octet *) xp) + len)
+ pk_parse_facilities (fcp, lcp -> lcd_ceaddr);
+ }
+ pk_assoc (lcp -> lcd_pkp, lcp, lcp -> lcd_ceaddr);
+ if (lcp -> lcd_so == 0 && lcp -> lcd_upper)
+ lcp -> lcd_upper (lcp, m);
+}
+
+pk_parse_facilities (fcp, sa)
+register octet *fcp;
+register struct sockaddr_x25 *sa;
+{
+ register octet *maxfcp;
+
+ maxfcp = fcp + *fcp;
+ fcp++;
+ while (fcp < maxfcp) {
+ /*
+ * Ignore national DCE or DTE facilities
+ */
+ if (*fcp == 0 || *fcp == 0xff)
+ break;
+ switch (*fcp) {
+ case FACILITIES_WINDOWSIZE:
+ sa -> x25_opts.op_wsize = fcp[1];
+ fcp += 3;
+ break;
+
+ case FACILITIES_PACKETSIZE:
+ sa -> x25_opts.op_psize = fcp[1];
+ fcp += 3;
+ break;
+
+ case FACILITIES_THROUGHPUT:
+ sa -> x25_opts.op_speed = fcp[1];
+ fcp += 2;
+ break;
+
+ case FACILITIES_REVERSE_CHARGE:
+ if (fcp[1] & 01)
+ sa -> x25_opts.op_flags |= X25_REVERSE_CHARGE;
+ /*
+ * Datapac specific: for a X.25(1976) DTE, bit 2
+ * indicates a "hi priority" (eg. international) call.
+ */
+ if (fcp[1] & 02 && sa -> x25_opts.op_psize == 0)
+ sa -> x25_opts.op_psize = X25_PS128;
+ fcp += 2;
+ break;
+
+ default:
+/*printf("unknown facility %x, class=%d\n", *fcp, (*fcp & 0xc0) >> 6);*/
+ switch ((*fcp & 0xc0) >> 6) {
+ case 0: /* class A */
+ fcp += 2;
+ break;
+
+ case 1:
+ fcp += 3;
+ break;
+
+ case 2:
+ fcp += 4;
+ break;
+
+ case 3:
+ fcp++;
+ fcp += *fcp;
+ }
+ }
+ }
+}
diff --git a/sys/netccitt/pk_llcsubr.c b/sys/netccitt/pk_llcsubr.c
new file mode 100644
index 0000000..d8cc501
--- /dev/null
+++ b/sys/netccitt/pk_llcsubr.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) Dirk Husemann, Computer Science Department IV,
+ * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Dirk Husemann and the Computer Science Department (IV) of
+ * the University of Erlangen-Nuremberg, Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pk_llcsubr.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_llc.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netccitt/dll.h>
+#include <netccitt/x25.h>
+#include <netccitt/pk.h>
+#include <netccitt/pk_var.h>
+#include <netccitt/llc_var.h>
+
+
+/*
+ * Routing support for X.25
+ *
+ * We distinguish between two cases:
+ * RTF_HOST:
+ * rt_key(rt) X.25 address of host
+ * rt_gateway SNPA (MAC+DLSAP) address of host
+ * rt_llinfo pkcb for rt_key(rt)
+ *
+ * RTF_GATEWAY
+ * rt_key(rt) X.25 address of host or suitably masked network
+ * rt_gateway X.25 address of next X.25 gateway (switch)
+ * rt_llinfo rtentry for rt_gateway address
+ * ought to be of type RTF_HOST
+ *
+ *
+ * Mapping of X.121 to pkcbs:
+ *
+ * HDLC uses the DTE-DCE model of X.25, therefore we need a many-to-one
+ * relationship, i.e.:
+ *
+ * {X.121_a, X.121_b, X.121_c, ..., X.121_i} -> pkcb_0
+ *
+ * LLC2 utilizes the DTE-DTE model of X.25, resulting effectively in a
+ * one-to-one relationship, i.e.:
+ *
+ * {X.121_j} -> pkcb_1a
+ * {X.121_k} -> pkcb_1b
+ * ...
+ * {X.121_q} -> pkcb_1q
+ *
+ * It might make sense to allow a many-to-one relation for LLC2 also,
+ *
+ * {X.121_r, X.121_s, X.121_t, X.121_u} -> pkcb_2a
+ *
+ * This would make addresses X.121_[r-u] essentially aliases of one
+ * address ({X.121_[r-u]} would constitute a representative set).
+ *
+ * Each one-to-one relation must obviously be entered individually with
+ * a route add command, whereas a many-to-one relationship can be
+ * either entered individually or generated by using a netmask.
+ *
+ * To facilitate dealings the many-to-one case for LLC2 can only be
+ * established via a netmask.
+ *
+ */
+
+#define XTRACTPKP(rt) ((rt)->rt_flags & RTF_GATEWAY ? \
+ ((rt)->rt_llinfo ? \
+ (struct pkcb *) ((struct rtentry *)((rt)->rt_llinfo))->rt_llinfo : \
+ (struct pkcb *) NULL) : \
+ (struct pkcb *)((rt)->rt_llinfo))
+
+#define equal(a1, a2) (bcmp((caddr_t)(a1), \
+ (caddr_t)(a2), \
+ (a1)->sa_len) == 0)
+#define XIFA(rt) ((struct x25_ifaddr *)((rt)->rt_ifa))
+#define SA(s) ((struct sockaddr *)s)
+
+int
+cons_rtrequest(int cmd, struct rtentry *rt, struct sockaddr *dst)
+{
+ register struct pkcb *pkp;
+ register int i;
+ register char one_to_one;
+ struct pkcb *pk_newlink();
+ struct rtentry *npaidb_enter();
+
+ pkp = XTRACTPKP(rt);
+
+ switch(cmd) {
+ case RTM_RESOLVE:
+ case RTM_ADD:
+ if (pkp)
+ return(EEXIST);
+
+ if (rt->rt_flags & RTF_GATEWAY) {
+ if (rt->rt_llinfo)
+ RTFREE((struct rtentry *)rt->rt_llinfo);
+ rt->rt_llinfo = (caddr_t) rtalloc1(rt->rt_gateway, 1);
+ return(0);
+ }
+ /*
+ * Assumptions: (1) ifnet structure is filled in
+ * (2) at least the pkcb created via
+ * x25config (ifconfig?) has been
+ * set up already.
+ * (3) HDLC interfaces have an if_type of
+ * IFT_X25{,DDN}, LLC2 interfaces
+ * anything else (any better way to
+ * do this?)
+ *
+ */
+ if (!rt->rt_ifa)
+ return (ENETDOWN);
+
+ /*
+ * We differentiate between dealing with a many-to-one
+ * (HDLC: DTE-DCE) and a one-to-one (LLC2: DTE-DTE)
+ * relationship (by looking at the if type).
+ *
+ * Only in case of the many-to-one relationship (HDLC)
+ * we set the ia->ia_pkcb pointer to the pkcb allocated
+ * via pk_newlink() as we will use just that one pkcb for
+ * future route additions (the rtentry->rt_llinfo pointer
+ * points to the pkcb allocated for that route).
+ *
+ * In case of the one-to-one relationship (LLC2) we
+ * create a new pkcb (via pk_newlink()) for each new rtentry.
+ *
+ * NOTE: Only in case of HDLC does ia->ia_pkcb point
+ * to a pkcb, in the LLC2 case it doesn't (as we don't
+ * need it here)!
+ */
+ one_to_one = ISISO8802(rt->rt_ifp);
+
+ if (!(pkp = XIFA(rt)->ia_pkcb) && !one_to_one)
+ XIFA(rt)->ia_pkcb = pkp =
+ pk_newlink(XIFA(rt), (caddr_t) 0);
+ else if (one_to_one &&
+ !equal(rt->rt_gateway, rt->rt_ifa->ifa_addr)) {
+ pkp = pk_newlink(XIFA(rt), (caddr_t) 0);
+ /*
+ * We also need another route entry for mapping
+ * MAC+LSAP->X.25 address
+ */
+ pkp->pk_llrt = npaidb_enter(rt->rt_gateway, rt_key(rt), rt, 0);
+ }
+ if (pkp) {
+ if (!pkp->pk_rt)
+ pkp->pk_rt = rt;
+ pkp->pk_refcount++;
+ }
+ rt->rt_llinfo = (caddr_t) pkp;
+
+ return(0);
+
+ case RTM_DELETE:
+ {
+ /*
+ * The pkp might be empty if we are dealing
+ * with an interface route entry for LLC2, in this
+ * case we don't need to do anything ...
+ */
+ if (pkp) {
+ if ( rt->rt_flags & RTF_GATEWAY ) {
+ if (rt->rt_llinfo)
+ RTFREE((struct rtentry *)rt->rt_llinfo);
+ return(0);
+ }
+
+ if (pkp->pk_llrt)
+ npaidb_destroy(pkp->pk_llrt);
+
+ pk_dellink (pkp);
+
+ return(0);
+ }
+ }
+ }
+}
+
+/*
+ * Network Protocol Addressing Information DataBase (npaidb)
+ *
+ * To speed up locating the entity dealing with an LLC packet use is made
+ * of a routing tree. This npaidb routing tree is handled
+ * by the normal rn_*() routines just like (almost) any other routing tree.
+ *
+ * The mapping being done by the npaidb_*() routines is as follows:
+ *
+ * Key: MAC,LSAP (enhancing struct sockaddr_dl)
+ * Gateway: sockaddr_x25 (i.e. X.25 address - X.121 or NSAP)
+ * Llinfo: npaidbentry {
+ * struct llc_linkcb *npaidb_linkp;
+ * struct rtentry *npaidb_rt;
+ * }
+ *
+ * Using the npaidbentry provided by llinfo we can then access
+ *
+ * o the pkcb by using (struct pkcb *) (npaidb_rt->rt_llinfo)
+ * o the linkcb via npaidb_linkp
+ *
+ * The following functions are provided
+ *
+ * o npaidb_enter(struct sockaddr_dl *sdl, struct sockaddr_x25 *sx25,
+ * struct struct llc_linkcb *link, struct rtentry *rt)
+ *
+ * o npaidb_enrich(short type, caddr_t info)
+ *
+ */
+
+struct sockaddr_dl npdl_netmask = {
+ sizeof(struct sockaddr_dl), /* _len */
+ 0, /* _family */
+ 0, /* _index */
+ 0, /* _type */
+ -1, /* _nlen */
+ -1, /* _alen */
+ -1, /* _slen */
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* _data */
+};
+struct sockaddr npdl_dummy;
+
+int npdl_datasize = sizeof(struct sockaddr_dl)-
+ ((int)((caddr_t)&((struct sockaddr_dl *)0)->sdl_data[0]));
+
+struct rtentry *
+npaidb_enter(struct sockaddr_dl *key, struct sockaddr *value,
+ struct rtentry *rt, struct llc_linkcb *link)
+{
+ struct rtentry *nprt; register int i;
+
+ USES_AF_LINK_RTS;
+
+ if ((nprt = rtalloc1(SA(key), 0)) == 0) {
+ register u_int size = sizeof(struct npaidbentry);
+ register u_char saploc = LLSAPLOC(key, rt->rt_ifp);
+
+ /*
+ * set up netmask: LLC2 packets have the lowest bit set in
+ * response packets (e.g. 0x7e for command packets, 0x7f for
+ * response packets), to facilitate the lookup we use a netmask
+ * of 11111110 for the SAP position. The remaining positions
+ * are zeroed out.
+ */
+ npdl_netmask.sdl_data[saploc] = NPDL_SAPNETMASK;
+ bzero((caddr_t)&npdl_netmask.sdl_data[saploc+1],
+ npdl_datasize-saploc-1);
+
+ if (value == 0)
+ value = &npdl_dummy;
+
+ /* now enter it */
+ rtrequest(RTM_ADD, SA(key), SA(value),
+ SA(&npdl_netmask), 0, &nprt);
+
+ /* and reset npdl_netmask */
+ for (i = saploc; i < npdl_datasize; i++)
+ npdl_netmask.sdl_data[i] = -1;
+
+ nprt->rt_llinfo = malloc(size , M_PCB, M_WAITOK);
+ if (nprt->rt_llinfo) {
+ bzero (nprt->rt_llinfo, size);
+ ((struct npaidbentry *) (nprt->rt_llinfo))->np_rt = rt;
+ }
+ } else nprt->rt_refcnt--;
+ return nprt;
+}
+
+struct rtentry *
+npaidb_enrich(short type, caddr_t info, struct sockaddr_dl *sdl)
+{
+ struct rtentry *rt;
+
+ USES_AF_LINK_RTS;
+
+ if (rt = rtalloc1((struct sockaddr *)sdl, 0)) {
+ rt->rt_refcnt--;
+ switch (type) {
+ case NPAIDB_LINK:
+ ((struct npaidbentry *)(rt->rt_llinfo))->np_link =
+ (struct llc_linkcb *) info;
+ break;
+ }
+ return rt;
+ }
+
+ return ((struct rtentry *) 0);
+
+}
+
+npaidb_destroy(struct rtentry *rt)
+{
+ USES_AF_LINK_RTS;
+
+ if (rt->rt_llinfo)
+ free((caddr_t) rt->rt_llinfo, M_PCB);
+ return(rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, rt_mask(rt),
+ 0, 0));
+}
+
+
+#ifdef LLC
+/*
+ * Glue between X.25 and LLC2
+ */
+int
+x25_llcglue(int prc, struct sockaddr *addr)
+{
+ register struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)addr;
+ register struct x25_ifaddr *x25ifa;
+ struct dll_ctlinfo ctlinfo;
+
+ if((x25ifa = (struct x25_ifaddr *)ifa_ifwithaddr(addr)) == 0)
+ return 0;
+
+ ctlinfo.dlcti_cfg =
+ (struct dllconfig *)(((struct sockaddr_x25 *)(&x25ifa->ia_xc))+1);
+ ctlinfo.dlcti_lsap = LLC_X25_LSAP;
+
+ return ((int)llc_ctlinput(prc, addr, (caddr_t)&ctlinfo));
+}
+#endif /* LLC */
diff --git a/sys/netccitt/pk_output.c b/sys/netccitt/pk_output.c
new file mode 100644
index 0000000..ccc02a4
--- /dev/null
+++ b/sys/netccitt/pk_output.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (C) Computer Science Department IV,
+ * University of Erlangen-Nuremberg, Germany, 1992
+ * Copyright (c) 1991, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by the
+ * Laboratory for Computation Vision and the Computer Science Department
+ * of the the University of British Columbia and the Computer Science
+ * Department (IV) of the University of Erlangen-Nuremberg, Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pk_output.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+
+#include <netccitt/x25.h>
+#include <netccitt/pk.h>
+#include <netccitt/pk_var.h>
+
+struct mbuf_cache pk_output_cache = {0 }, pk_input_cache;
+struct mbuf *nextpk ();
+
+pk_output (lcp)
+register struct pklcd *lcp;
+{
+ register struct x25_packet *xp;
+ register struct mbuf *m;
+ register struct pkcb *pkp = lcp -> lcd_pkp;
+
+ if (lcp == 0 || pkp == 0) {
+ printf ("pk_output: zero arg\n");
+ return;
+ }
+
+ while ((m = nextpk (lcp)) != NULL) {
+ xp = mtod (m, struct x25_packet *);
+
+ switch (pk_decode (xp) + lcp -> lcd_state) {
+ /*
+ * All the work is already done - just set the state and
+ * pass to peer.
+ */
+ case CALL + READY:
+ lcp -> lcd_state = SENT_CALL;
+ lcp -> lcd_timer = pk_t21;
+ break;
+
+ /*
+ * Just set the state to allow packet to flow and send the
+ * confirmation.
+ */
+ case CALL_ACCEPTED + RECEIVED_CALL:
+ lcp -> lcd_state = DATA_TRANSFER;
+ break;
+
+ /*
+ * Just set the state. Keep the LCD around till the clear
+ * confirmation is returned.
+ */
+ case CLEAR + RECEIVED_CALL:
+ case CLEAR + SENT_CALL:
+ case CLEAR + DATA_TRANSFER:
+ lcp -> lcd_state = SENT_CLEAR;
+ lcp -> lcd_retry = 0;
+ /* fall through */
+
+ case CLEAR + SENT_CLEAR:
+ lcp -> lcd_timer = pk_t23;
+ lcp -> lcd_retry++;
+ break;
+
+ case CLEAR_CONF + RECEIVED_CLEAR:
+ case CLEAR_CONF + SENT_CLEAR:
+ case CLEAR_CONF + READY:
+ lcp -> lcd_state = READY;
+ break;
+
+ case DATA + DATA_TRANSFER:
+ SPS(xp, lcp -> lcd_ssn);
+ lcp -> lcd_input_window =
+ (lcp -> lcd_rsn + 1) % MODULUS;
+ SPR(xp, lcp -> lcd_input_window);
+ lcp -> lcd_last_transmitted_pr = lcp -> lcd_input_window;
+ lcp -> lcd_ssn = (lcp -> lcd_ssn + 1) % MODULUS;
+ if (lcp -> lcd_ssn == ((lcp -> lcd_output_window + lcp -> lcd_windowsize) % MODULUS))
+ lcp -> lcd_window_condition = TRUE;
+ break;
+
+ case INTERRUPT + DATA_TRANSFER:
+#ifdef ancient_history
+ xp -> packet_data = 0;
+#endif
+ lcp -> lcd_intrconf_pending = TRUE;
+ break;
+
+ case INTERRUPT_CONF + DATA_TRANSFER:
+ break;
+
+ case RR + DATA_TRANSFER:
+ case RNR + DATA_TRANSFER:
+ lcp -> lcd_input_window =
+ (lcp -> lcd_rsn + 1) % MODULUS;
+ SPR(xp, lcp -> lcd_input_window);
+ lcp -> lcd_last_transmitted_pr = lcp -> lcd_input_window;
+ break;
+
+ case RESET + DATA_TRANSFER:
+ lcp -> lcd_reset_condition = TRUE;
+ break;
+
+ case RESET_CONF + DATA_TRANSFER:
+ lcp -> lcd_reset_condition = FALSE;
+ break;
+
+ /*
+ * A restart should be only generated internally. Therefore
+ * all logic for restart is in the pk_restart routine.
+ */
+ case RESTART + READY:
+ lcp -> lcd_timer = pk_t20;
+ break;
+
+ /*
+ * Restarts are all handled internally. Therefore all the
+ * logic for the incoming restart packet is handled in the
+ * pk_input routine.
+ */
+ case RESTART_CONF + READY:
+ break;
+
+ default:
+ m_freem (m);
+ return;
+ }
+
+ /* Trace the packet. */
+ pk_trace (pkp -> pk_xcp, m, "P-Out");
+
+ /* Pass the packet on down to the link layer */
+ if (pk_input_cache.mbc_size || pk_input_cache.mbc_oldsize) {
+ m->m_flags |= 0x08;
+ mbuf_cache(&pk_input_cache, m);
+ }
+ (*pkp -> pk_lloutput) (pkp -> pk_llnext, m, pkp -> pk_rt);
+ }
+}
+
+/*
+ * This procedure returns the next packet to send or null. A
+ * packet is composed of one or more mbufs.
+ */
+
+struct mbuf *
+nextpk (lcp)
+struct pklcd *lcp;
+{
+ register struct mbuf *m, *n;
+ struct socket *so = lcp -> lcd_so;
+ register struct sockbuf *sb = & (so ? so -> so_snd : lcp -> lcd_sb);
+
+ if (lcp -> lcd_template) {
+ m = lcp -> lcd_template;
+ lcp -> lcd_template = NULL;
+ } else {
+ if (lcp -> lcd_rnr_condition || lcp -> lcd_window_condition ||
+ lcp -> lcd_reset_condition)
+ return (NULL);
+
+ if ((m = sb -> sb_mb) == 0)
+ return (NULL);
+
+ sb -> sb_mb = m -> m_nextpkt;
+ m->m_act = 0;
+ for (n = m; n; n = n -> m_next)
+ sbfree (sb, n);
+ }
+ return (m);
+}
diff --git a/sys/netccitt/pk_subr.c b/sys/netccitt/pk_subr.c
new file mode 100644
index 0000000..44c43b6
--- /dev/null
+++ b/sys/netccitt/pk_subr.c
@@ -0,0 +1,1192 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (C) Computer Science Department IV,
+ * University of Erlangen-Nuremberg, Germany, 1992
+ * Copyright (c) 1991, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by the
+ * Laboratory for Computation Vision and the Computer Science Department
+ * of the the University of British Columbia and the Computer Science
+ * Department (IV) of the University of Erlangen-Nuremberg, Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pk_subr.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netccitt/dll.h>
+#include <netccitt/x25.h>
+#include <netccitt/x25err.h>
+#include <netccitt/pk.h>
+#include <netccitt/pk_var.h>
+
+int pk_sendspace = 1024 * 2 + 8;
+int pk_recvspace = 1024 * 2 + 8;
+
+struct pklcd_q pklcd_q = {&pklcd_q, &pklcd_q};
+
+struct x25bitslice x25_bitslice[] = {
+/* mask, shift value */
+ { 0xf0, 0x4 },
+ { 0xf, 0x0 },
+ { 0x80, 0x7 },
+ { 0x40, 0x6 },
+ { 0x30, 0x4 },
+ { 0xe0, 0x5 },
+ { 0x10, 0x4 },
+ { 0xe, 0x1 },
+ { 0x1, 0x0 }
+};
+
+
+/*
+ * Attach X.25 protocol to socket, allocate logical channel descripter
+ * and buffer space, and enter LISTEN state if we are to accept
+ * IN-COMMING CALL packets.
+ *
+ */
+
+struct pklcd *
+pk_attach (so)
+struct socket *so;
+{
+ register struct pklcd *lcp;
+ register int error = ENOBUFS;
+ int pk_output ();
+
+ MALLOC(lcp, struct pklcd *, sizeof (*lcp), M_PCB, M_NOWAIT);
+ if (lcp) {
+ bzero ((caddr_t)lcp, sizeof (*lcp));
+ insque (&lcp -> lcd_q, &pklcd_q);
+ lcp -> lcd_state = READY;
+ lcp -> lcd_send = pk_output;
+ if (so) {
+ error = soreserve (so, pk_sendspace, pk_recvspace);
+ lcp -> lcd_so = so;
+ if (so -> so_options & SO_ACCEPTCONN)
+ lcp -> lcd_state = LISTEN;
+ } else
+ sbreserve (&lcp -> lcd_sb, pk_sendspace);
+ }
+ if (so) {
+ so -> so_pcb = (caddr_t) lcp;
+ so -> so_error = error;
+ }
+ return (lcp);
+}
+
+/*
+ * Disconnect X.25 protocol from socket.
+ */
+
+pk_disconnect (lcp)
+register struct pklcd *lcp;
+{
+ register struct socket *so = lcp -> lcd_so;
+ register struct pklcd *l, *p;
+
+ switch (lcp -> lcd_state) {
+ case LISTEN:
+ for (p = 0, l = pk_listenhead; l && l != lcp; p = l, l = l -> lcd_listen);
+ if (p == 0) {
+ if (l != 0)
+ pk_listenhead = l -> lcd_listen;
+ }
+ else
+ if (l != 0)
+ p -> lcd_listen = l -> lcd_listen;
+ pk_close (lcp);
+ break;
+
+ case READY:
+ pk_acct (lcp);
+ pk_close (lcp);
+ break;
+
+ case SENT_CLEAR:
+ case RECEIVED_CLEAR:
+ break;
+
+ default:
+ pk_acct (lcp);
+ if (so) {
+ soisdisconnecting (so);
+ sbflush (&so -> so_rcv);
+ }
+ pk_clear (lcp, 241, 0); /* Normal Disconnect */
+
+ }
+}
+
+/*
+ * Close an X.25 Logical Channel. Discard all space held by the
+ * connection and internal descriptors. Wake up any sleepers.
+ */
+
+pk_close (lcp)
+struct pklcd *lcp;
+{
+ register struct socket *so = lcp -> lcd_so;
+
+ /*
+ * If the X.25 connection is torn down due to link
+ * level failure (e.g. LLC2 FRMR) and at the same the user
+ * level is still filling up the socket send buffer that
+ * send buffer is locked. An attempt to sbflush () that send
+ * buffer will lead us into - no, not temptation but - panic!
+ * So - we'll just check wether the send buffer is locked
+ * and if that's the case we'll mark the lcp as zombie and
+ * have the pk_timer () do the cleaning ...
+ */
+
+ if (so && so -> so_snd.sb_flags & SB_LOCK)
+ lcp -> lcd_state = LCN_ZOMBIE;
+ else
+ pk_freelcd (lcp);
+
+ if (so == NULL)
+ return;
+
+ so -> so_pcb = 0;
+ soisdisconnected (so);
+ /* sofree (so); /* gak!!! you can't do that here */
+}
+
+/*
+ * Create a template to be used to send X.25 packets on a logical
+ * channel. It allocates an mbuf and fills in a skeletal packet
+ * depending on its type. This packet is passed to pk_output where
+ * the remainer of the packet is filled in.
+*/
+
+struct mbuf *
+pk_template (lcn, type)
+int lcn, type;
+{
+ register struct mbuf *m;
+ register struct x25_packet *xp;
+
+ MGETHDR (m, M_DONTWAIT, MT_HEADER);
+ if (m == 0)
+ panic ("pk_template");
+ m -> m_act = 0;
+
+ /*
+ * Efficiency hack: leave a four byte gap at the beginning
+ * of the packet level header with the hope that this will
+ * be enough room for the link level to insert its header.
+ */
+ m -> m_data += max_linkhdr;
+ m -> m_pkthdr.len = m -> m_len = PKHEADERLN;
+
+ xp = mtod (m, struct x25_packet *);
+ *(long *)xp = 0; /* ugly, but fast */
+/* xp -> q_bit = 0;*/
+ X25SBITS(xp -> bits, fmt_identifier, 1);
+/* xp -> lc_group_number = 0;*/
+
+ SET_LCN(xp, lcn);
+ xp -> packet_type = type;
+
+ return (m);
+}
+
+/*
+ * This routine restarts all the virtual circuits. Actually,
+ * the virtual circuits are not "restarted" as such. Instead,
+ * any active switched circuit is simply returned to READY
+ * state.
+ */
+
+pk_restart (pkp, restart_cause)
+register struct pkcb *pkp;
+int restart_cause;
+{
+ register struct mbuf *m;
+ register struct pklcd *lcp;
+ register int i;
+
+ /* Restart all logical channels. */
+ if (pkp -> pk_chan == 0)
+ return;
+
+ /*
+ * Don't do this if we're doing a restart issued from
+ * inside pk_connect () --- which is only done if and
+ * only if the X.25 link is down, i.e. a RESTART needs
+ * to be done to get it up.
+ */
+ if (!(pkp -> pk_dxerole & DTE_CONNECTPENDING)) {
+ for (i = 1; i <= pkp -> pk_maxlcn; ++i)
+ if ((lcp = pkp -> pk_chan[i]) != NULL) {
+ if (lcp -> lcd_so) {
+ lcp -> lcd_so -> so_error = ENETRESET;
+ pk_close (lcp);
+ } else {
+ pk_flush (lcp);
+ lcp -> lcd_state = READY;
+ if (lcp -> lcd_upper)
+ lcp -> lcd_upper (lcp, 0);
+ }
+ }
+ }
+
+ if (restart_cause < 0)
+ return;
+
+ pkp -> pk_state = DTE_SENT_RESTART;
+ pkp -> pk_dxerole &= ~(DTE_PLAYDCE | DTE_PLAYDTE);
+ lcp = pkp -> pk_chan[0];
+ m = lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RESTART);
+ m -> m_pkthdr.len = m -> m_len += 2;
+ mtod (m, struct x25_packet *) -> packet_data = 0; /* DTE only */
+ mtod (m, octet *)[4] = restart_cause;
+ pk_output (lcp);
+}
+
+
+/*
+ * This procedure frees up the Logical Channel Descripter.
+ */
+
+pk_freelcd (lcp)
+register struct pklcd *lcp;
+{
+ if (lcp == NULL)
+ return;
+
+ if (lcp -> lcd_lcn > 0)
+ lcp -> lcd_pkp -> pk_chan[lcp -> lcd_lcn] = NULL;
+
+ pk_flush (lcp);
+ remque (&lcp -> lcd_q);
+ free ((caddr_t)lcp, M_PCB);
+}
+
+static struct x25_ifaddr *
+pk_ifwithaddr (sx)
+ struct sockaddr_x25 *sx;
+{
+ struct ifnet *ifp;
+ struct ifaddr *ifa;
+ register struct x25_ifaddr *ia;
+ char *addr = sx -> x25_addr;
+
+ for (ifp = ifnet; ifp; ifp = ifp -> if_next)
+ for (ifa = ifp -> if_addrlist; ifa; ifa = ifa -> ifa_next)
+ if (ifa -> ifa_addr -> sa_family == AF_CCITT) {
+ ia = (struct x25_ifaddr *)ifa;
+ if (bcmp (addr, ia -> ia_xc.xc_addr.x25_addr,
+ 16) == 0)
+ return (ia);
+
+ }
+ return ((struct x25_ifaddr *)0);
+}
+
+
+/*
+ * Bind a address and protocol value to a socket. The important
+ * part is the protocol value - the first four characters of the
+ * Call User Data field.
+ */
+
+#define XTRACTPKP(rt) ((rt) -> rt_flags & RTF_GATEWAY ? \
+ ((rt) -> rt_llinfo ? \
+ (struct pkcb *) ((struct rtentry *)((rt) -> rt_llinfo)) -> rt_llinfo : \
+ (struct pkcb *) NULL) : \
+ (struct pkcb *)((rt) -> rt_llinfo))
+
+pk_bind (lcp, nam)
+struct pklcd *lcp;
+struct mbuf *nam;
+{
+ register struct pklcd *pp;
+ register struct sockaddr_x25 *sa;
+
+ if (nam == NULL)
+ return (EADDRNOTAVAIL);
+ if (lcp -> lcd_ceaddr) /* XXX */
+ return (EADDRINUSE);
+ if (pk_checksockaddr (nam))
+ return (EINVAL);
+ sa = mtod (nam, struct sockaddr_x25 *);
+
+ /*
+ * If the user wishes to accept calls only from a particular
+ * net (net != 0), make sure the net is known
+ */
+
+ if (sa -> x25_addr[0]) {
+ if (!pk_ifwithaddr (sa))
+ return (ENETUNREACH);
+ } else if (sa -> x25_net) {
+ if (!ifa_ifwithnet ((struct sockaddr *)sa))
+ return (ENETUNREACH);
+ }
+
+ /*
+ * For ISO's sake permit default listeners, but only one such . . .
+ */
+ for (pp = pk_listenhead; pp; pp = pp -> lcd_listen) {
+ register struct sockaddr_x25 *sa2 = pp -> lcd_ceaddr;
+ if ((sa2 -> x25_udlen == sa -> x25_udlen) &&
+ (sa2 -> x25_udlen == 0 ||
+ (bcmp (sa2 -> x25_udata, sa -> x25_udata,
+ min (sa2 -> x25_udlen, sa -> x25_udlen)) == 0)))
+ return (EADDRINUSE);
+ }
+ lcp -> lcd_laddr = *sa;
+ lcp -> lcd_ceaddr = &lcp -> lcd_laddr;
+ return (0);
+}
+
+/*
+ * Include a bound control block in the list of listeners.
+ */
+pk_listen (lcp)
+register struct pklcd *lcp;
+{
+ register struct pklcd **pp;
+
+ if (lcp -> lcd_ceaddr == 0)
+ return (EDESTADDRREQ);
+
+ lcp -> lcd_state = LISTEN;
+ /*
+ * Add default listener at end, any others at start.
+ */
+ if (lcp -> lcd_ceaddr -> x25_udlen == 0) {
+ for (pp = &pk_listenhead; *pp; )
+ pp = &((*pp) -> lcd_listen);
+ *pp = lcp;
+ } else {
+ lcp -> lcd_listen = pk_listenhead;
+ pk_listenhead = lcp;
+ }
+ return (0);
+}
+/*
+ * Include a listening control block for the benefit of other protocols.
+ */
+pk_protolisten (spi, spilen, callee)
+int (*callee) ();
+{
+ register struct pklcd *lcp = pk_attach ((struct socket *)0);
+ register struct mbuf *nam;
+ register struct sockaddr_x25 *sa;
+ int error = ENOBUFS;
+
+ if (lcp) {
+ if (nam = m_getclr (MT_SONAME, M_DONTWAIT)) {
+ sa = mtod (nam, struct sockaddr_x25 *);
+ sa -> x25_family = AF_CCITT;
+ sa -> x25_len = nam -> m_len = sizeof (*sa);
+ sa -> x25_udlen = spilen;
+ sa -> x25_udata[0] = spi;
+ lcp -> lcd_upper = callee;
+ lcp -> lcd_flags = X25_MBS_HOLD;
+ if ((error = pk_bind (lcp, nam)) == 0)
+ error = pk_listen (lcp);
+ (void) m_free (nam);
+ }
+ if (error)
+ pk_freelcd (lcp);
+ }
+ return error; /* Hopefully Zero !*/
+}
+
+/*
+ * Associate a logical channel descriptor with a network.
+ * Fill in the default network specific parameters and then
+ * set any parameters explicitly specified by the user or
+ * by the remote DTE.
+ */
+
+pk_assoc (pkp, lcp, sa)
+register struct pkcb *pkp;
+register struct pklcd *lcp;
+register struct sockaddr_x25 *sa;
+{
+
+ lcp -> lcd_pkp = pkp;
+ lcp -> lcd_packetsize = pkp -> pk_xcp -> xc_psize;
+ lcp -> lcd_windowsize = pkp -> pk_xcp -> xc_pwsize;
+ lcp -> lcd_rsn = MODULUS - 1;
+ pkp -> pk_chan[lcp -> lcd_lcn] = lcp;
+
+ if (sa -> x25_opts.op_psize)
+ lcp -> lcd_packetsize = sa -> x25_opts.op_psize;
+ else
+ sa -> x25_opts.op_psize = lcp -> lcd_packetsize;
+ if (sa -> x25_opts.op_wsize)
+ lcp -> lcd_windowsize = sa -> x25_opts.op_wsize;
+ else
+ sa -> x25_opts.op_wsize = lcp -> lcd_windowsize;
+ sa -> x25_net = pkp -> pk_xcp -> xc_addr.x25_net;
+ lcp -> lcd_flags |= sa -> x25_opts.op_flags;
+ lcp -> lcd_stime = time.tv_sec;
+}
+
+pk_connect (lcp, sa)
+register struct pklcd *lcp;
+register struct sockaddr_x25 *sa;
+{
+ register struct pkcb *pkp;
+ register struct rtentry *rt;
+ register struct rtentry *nrt;
+
+ struct rtentry *npaidb_enter ();
+ struct pkcb *pk_newlink ();
+
+ if (sa -> x25_addr[0] == '\0')
+ return (EDESTADDRREQ);
+
+ /*
+ * Is the destination address known?
+ */
+ if (!(rt = rtalloc1 ((struct sockaddr *)sa, 1)))
+ return (ENETUNREACH);
+
+ if (!(pkp = XTRACTPKP(rt)))
+ pkp = pk_newlink ((struct x25_ifaddr *) (rt -> rt_ifa),
+ (caddr_t) 0);
+
+ /*
+ * Have we entered the LLC address?
+ */
+ if (nrt = npaidb_enter (rt -> rt_gateway, rt_key (rt), rt, 0))
+ pkp -> pk_llrt = nrt;
+
+ /*
+ * Have we allocated an LLC2 link yet?
+ */
+ if (pkp -> pk_llnext == (caddr_t)0 && pkp -> pk_llctlinput) {
+ struct dll_ctlinfo ctlinfo;
+
+ ctlinfo.dlcti_rt = rt;
+ ctlinfo.dlcti_pcb = (caddr_t) pkp;
+ ctlinfo.dlcti_conf =
+ (struct dllconfig *) (&((struct x25_ifaddr *)(rt -> rt_ifa)) -> ia_xc);
+ pkp -> pk_llnext =
+ (pkp -> pk_llctlinput) (PRC_CONNECT_REQUEST, 0, &ctlinfo);
+ }
+
+ if (pkp -> pk_state != DTE_READY && pkp -> pk_state != DTE_WAITING)
+ return (ENETDOWN);
+ if ((lcp -> lcd_lcn = pk_getlcn (pkp)) == 0)
+ return (EMFILE);
+
+ lcp -> lcd_faddr = *sa;
+ lcp -> lcd_ceaddr = & lcp -> lcd_faddr;
+ pk_assoc (pkp, lcp, lcp -> lcd_ceaddr);
+
+ /*
+ * If the link is not up yet, initiate an X.25 RESTART
+ */
+ if (pkp -> pk_state == DTE_WAITING) {
+ pkp -> pk_dxerole |= DTE_CONNECTPENDING;
+ pk_ctlinput (PRC_LINKUP, (struct sockaddr *)0, pkp);
+ if (lcp -> lcd_so)
+ soisconnecting (lcp -> lcd_so);
+ return 0;
+ }
+
+ if (lcp -> lcd_so)
+ soisconnecting (lcp -> lcd_so);
+ lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CALL);
+ pk_callrequest (lcp, lcp -> lcd_ceaddr, pkp -> pk_xcp);
+ return (*pkp -> pk_ia -> ia_start) (lcp);
+}
+
+/*
+ * Complete all pending X.25 call requests --- this gets called after
+ * the X.25 link has been restarted.
+ */
+#define RESHUFFLELCN(maxlcn, lcn) ((maxlcn) - (lcn) + 1)
+
+pk_callcomplete (pkp)
+ register struct pkcb *pkp;
+{
+ register struct pklcd *lcp;
+ register int i;
+ register int ni;
+
+
+ if (pkp -> pk_dxerole & DTE_CONNECTPENDING)
+ pkp -> pk_dxerole &= ~DTE_CONNECTPENDING;
+ else return;
+
+ if (pkp -> pk_chan == 0)
+ return;
+
+ /*
+ * We pretended to be a DTE for allocating lcns, if
+ * it turns out that we are in reality performing as a
+ * DCE we need to reshuffle the lcps.
+ *
+ * /+---------------+-------- -
+ * / | a (maxlcn-1) | \
+ * / +---------------+ \
+ * +--- * | b (maxlcn-2) | \
+ * | \ +---------------+ \
+ * r | \ | c (maxlcn-3) | \
+ * e | \+---------------+ |
+ * s | | . |
+ * h | | . | m
+ * u | | . | a
+ * f | | . | x
+ * f | | . | l
+ * l | /+---------------+ | c
+ * e | / | c' ( 3 ) | | n
+ * | / +---------------+ |
+ * +--> * | b' ( 2 ) | /
+ * \ +---------------+ /
+ * \ | a' ( 1 ) | /
+ * \+---------------+ /
+ * | 0 | /
+ * +---------------+-------- -
+ *
+ */
+ if (pkp -> pk_dxerole & DTE_PLAYDCE) {
+ /* Sigh, reshuffle it */
+ for (i = pkp -> pk_maxlcn; i > 0; --i)
+ if (pkp -> pk_chan[i]) {
+ ni = RESHUFFLELCN(pkp -> pk_maxlcn, i);
+ pkp -> pk_chan[ni] = pkp -> pk_chan[i];
+ pkp -> pk_chan[i] = NULL;
+ pkp -> pk_chan[ni] -> lcd_lcn = ni;
+ }
+ }
+
+ for (i = 1; i <= pkp -> pk_maxlcn; ++i)
+ if ((lcp = pkp -> pk_chan[i]) != NULL) {
+ /* if (lcp -> lcd_so)
+ soisconnecting (lcp -> lcd_so); */
+ lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CALL);
+ pk_callrequest (lcp, lcp -> lcd_ceaddr, pkp -> pk_xcp);
+ (*pkp -> pk_ia -> ia_start) (lcp);
+ }
+}
+
+struct bcdinfo {
+ octet *cp;
+ unsigned posn;
+};
+/*
+ * Build the rest of the CALL REQUEST packet. Fill in calling
+ * address, facilities fields and the user data field.
+ */
+
+pk_callrequest (lcp, sa, xcp)
+struct pklcd *lcp;
+register struct sockaddr_x25 *sa;
+register struct x25config *xcp;
+{
+ register struct x25_calladdr *a;
+ register struct mbuf *m = lcp -> lcd_template;
+ register struct x25_packet *xp = mtod (m, struct x25_packet *);
+ struct bcdinfo b;
+
+ if (lcp -> lcd_flags & X25_DBIT)
+ X25SBITS(xp -> bits, d_bit, 1);
+ a = (struct x25_calladdr *) &xp -> packet_data;
+ b.cp = (octet *) a -> address_field;
+ b.posn = 0;
+ X25SBITS(a -> addrlens, called_addrlen, to_bcd (&b, sa, xcp));
+ X25SBITS(a -> addrlens, calling_addrlen, to_bcd (&b, &xcp -> xc_addr, xcp));
+ if (b.posn & 0x01)
+ *b.cp++ &= 0xf0;
+ m -> m_pkthdr.len = m -> m_len += b.cp - (octet *) a;
+
+ if (lcp -> lcd_facilities) {
+ m -> m_pkthdr.len +=
+ (m -> m_next = lcp -> lcd_facilities) -> m_pkthdr.len;
+ lcp -> lcd_facilities = 0;
+ } else
+ pk_build_facilities (m, sa, (int)xcp -> xc_type);
+
+ m_copyback (m, m -> m_pkthdr.len, sa -> x25_udlen, sa -> x25_udata);
+}
+
+pk_build_facilities (m, sa, type)
+register struct mbuf *m;
+struct sockaddr_x25 *sa;
+{
+ register octet *cp;
+ register octet *fcp;
+ register int revcharge;
+
+ cp = mtod (m, octet *) + m -> m_len;
+ fcp = cp + 1;
+ revcharge = sa -> x25_opts.op_flags & X25_REVERSE_CHARGE ? 1 : 0;
+ /*
+ * This is specific to Datapac X.25(1976) DTEs. International
+ * calls must have the "hi priority" bit on.
+ */
+ if (type == X25_1976 && sa -> x25_opts.op_psize == X25_PS128)
+ revcharge |= 02;
+ if (revcharge) {
+ *fcp++ = FACILITIES_REVERSE_CHARGE;
+ *fcp++ = revcharge;
+ }
+ switch (type) {
+ case X25_1980:
+ case X25_1984:
+ *fcp++ = FACILITIES_PACKETSIZE;
+ *fcp++ = sa -> x25_opts.op_psize;
+ *fcp++ = sa -> x25_opts.op_psize;
+
+ *fcp++ = FACILITIES_WINDOWSIZE;
+ *fcp++ = sa -> x25_opts.op_wsize;
+ *fcp++ = sa -> x25_opts.op_wsize;
+ }
+ *cp = fcp - cp - 1;
+ m -> m_pkthdr.len = (m -> m_len += *cp + 1);
+}
+
+to_bcd (b, sa, xcp)
+register struct bcdinfo *b;
+struct sockaddr_x25 *sa;
+register struct x25config *xcp;
+{
+ register char *x = sa -> x25_addr;
+ unsigned start = b -> posn;
+ /*
+ * The nodnic and prepnd0 stuff looks tedious,
+ * but it does allow full X.121 addresses to be used,
+ * which is handy for routing info (& OSI type 37 addresses).
+ */
+ if (xcp -> xc_addr.x25_net && (xcp -> xc_nodnic || xcp -> xc_prepnd0)) {
+ char dnicname[sizeof (long) * NBBY/3 + 2];
+ register char *p = dnicname;
+
+ sprintf (p, "%d", xcp -> xc_addr.x25_net & 0x7fff);
+ for (; *p; p++) /* *p == 0 means dnic matched */
+ if ((*p ^ *x++) & 0x0f)
+ break;
+ if (*p || xcp -> xc_nodnic == 0)
+ x = sa -> x25_addr;
+ if (*p && xcp -> xc_prepnd0) {
+ if ((b -> posn)++ & 0x01)
+ *(b -> cp)++;
+ else
+ *(b -> cp) = 0;
+ }
+ }
+ while (*x)
+ if ((b -> posn)++ & 0x01)
+ *(b -> cp)++ |= *x++ & 0x0F;
+ else
+ *(b -> cp) = *x++ << 4;
+ return ((b -> posn) - start);
+}
+
+/*
+ * This routine gets the first available logical channel number. The
+ * search is
+ * - from the highest number to lowest number if playing DTE, and
+ * - from lowest to highest number if playing DCE.
+ */
+
+pk_getlcn (pkp)
+register struct pkcb *pkp;
+{
+ register int i;
+
+ if (pkp -> pk_chan == 0)
+ return (0);
+ if ( pkp -> pk_dxerole & DTE_PLAYDCE ) {
+ for (i = 1; i <= pkp -> pk_maxlcn; ++i)
+ if (pkp -> pk_chan[i] == NULL)
+ break;
+ } else {
+ for (i = pkp -> pk_maxlcn; i > 0; --i)
+ if (pkp -> pk_chan[i] == NULL)
+ break;
+ }
+ i = ( i > pkp -> pk_maxlcn ? 0 : i );
+ return (i);
+}
+
+/*
+ * This procedure sends a CLEAR request packet. The lc state is
+ * set to "SENT_CLEAR".
+ */
+
+pk_clear (lcp, diagnostic, abortive)
+register struct pklcd *lcp;
+{
+ register struct mbuf *m = pk_template (lcp -> lcd_lcn, X25_CLEAR);
+
+ m -> m_len += 2;
+ m -> m_pkthdr.len += 2;
+ mtod (m, struct x25_packet *) -> packet_data = 0;
+ mtod (m, octet *)[4] = diagnostic;
+ if (lcp -> lcd_facilities) {
+ m -> m_next = lcp -> lcd_facilities;
+ m -> m_pkthdr.len += m -> m_next -> m_len;
+ lcp -> lcd_facilities = 0;
+ }
+ if (abortive)
+ lcp -> lcd_template = m;
+ else {
+ struct socket *so = lcp -> lcd_so;
+ struct sockbuf *sb = so ? & so -> so_snd : & lcp -> lcd_sb;
+ sbappendrecord (sb, m);
+ }
+ pk_output (lcp);
+
+}
+
+/*
+ * This procedure generates RNR's or RR's to inhibit or enable
+ * inward data flow, if the current state changes (blocked ==> open or
+ * vice versa), or if forced to generate one. One forces RNR's to ack data.
+ */
+pk_flowcontrol (lcp, inhibit, forced)
+register struct pklcd *lcp;
+{
+ inhibit = (inhibit != 0);
+ if (lcp == 0 || lcp -> lcd_state != DATA_TRANSFER ||
+ (forced == 0 && lcp -> lcd_rxrnr_condition == inhibit))
+ return;
+ lcp -> lcd_rxrnr_condition = inhibit;
+ lcp -> lcd_template =
+ pk_template (lcp -> lcd_lcn, inhibit ? X25_RNR : X25_RR);
+ pk_output (lcp);
+}
+
+/*
+ * This procedure sends a RESET request packet. It re-intializes
+ * virtual circuit.
+ */
+
+static
+pk_reset (lcp, diagnostic)
+register struct pklcd *lcp;
+{
+ register struct mbuf *m;
+ register struct socket *so = lcp -> lcd_so;
+
+ if (lcp -> lcd_state != DATA_TRANSFER)
+ return;
+
+ if (so)
+ so -> so_error = ECONNRESET;
+ lcp -> lcd_reset_condition = TRUE;
+
+ /* Reset all the control variables for the channel. */
+ pk_flush (lcp);
+ lcp -> lcd_window_condition = lcp -> lcd_rnr_condition =
+ lcp -> lcd_intrconf_pending = FALSE;
+ lcp -> lcd_rsn = MODULUS - 1;
+ lcp -> lcd_ssn = 0;
+ lcp -> lcd_output_window = lcp -> lcd_input_window =
+ lcp -> lcd_last_transmitted_pr = 0;
+ m = lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RESET);
+ m -> m_pkthdr.len = m -> m_len += 2;
+ mtod (m, struct x25_packet *) -> packet_data = 0;
+ mtod (m, octet *)[4] = diagnostic;
+ pk_output (lcp);
+
+}
+
+/*
+ * This procedure frees all data queued for output or delivery on a
+ * virtual circuit.
+ */
+
+pk_flush (lcp)
+register struct pklcd *lcp;
+{
+ register struct socket *so;
+
+ if (lcp -> lcd_template)
+ m_freem (lcp -> lcd_template);
+
+ if (lcp -> lcd_cps) {
+ m_freem (lcp -> lcd_cps);
+ lcp -> lcd_cps = 0;
+ }
+ if (lcp -> lcd_facilities) {
+ m_freem (lcp -> lcd_facilities);
+ lcp -> lcd_facilities = 0;
+ }
+ if (so = lcp -> lcd_so)
+ sbflush (&so -> so_snd);
+ else
+ sbflush (&lcp -> lcd_sb);
+}
+
+/*
+ * This procedure handles all local protocol procedure errors.
+ */
+
+pk_procerror (error, lcp, errstr, diagnostic)
+register struct pklcd *lcp;
+char *errstr;
+{
+
+ pk_message (lcp -> lcd_lcn, lcp -> lcd_pkp -> pk_xcp, errstr);
+
+ switch (error) {
+ case CLEAR:
+ if (lcp -> lcd_so) {
+ lcp -> lcd_so -> so_error = ECONNABORTED;
+ soisdisconnecting (lcp -> lcd_so);
+ }
+ pk_clear (lcp, diagnostic, 1);
+ break;
+
+ case RESET:
+ pk_reset (lcp, diagnostic);
+ }
+}
+
+/*
+ * This procedure is called during the DATA TRANSFER state to check
+ * and process the P(R) values received in the DATA, RR OR RNR
+ * packets.
+ */
+
+pk_ack (lcp, pr)
+struct pklcd *lcp;
+unsigned pr;
+{
+ register struct socket *so = lcp -> lcd_so;
+
+ if (lcp -> lcd_output_window == pr)
+ return (PACKET_OK);
+ if (lcp -> lcd_output_window < lcp -> lcd_ssn) {
+ if (pr < lcp -> lcd_output_window || pr > lcp -> lcd_ssn) {
+ pk_procerror (RESET, lcp,
+ "p(r) flow control error", 2);
+ return (ERROR_PACKET);
+ }
+ }
+ else {
+ if (pr < lcp -> lcd_output_window && pr > lcp -> lcd_ssn) {
+ pk_procerror (RESET, lcp,
+ "p(r) flow control error #2", 2);
+ return (ERROR_PACKET);
+ }
+ }
+
+ lcp -> lcd_output_window = pr; /* Rotate window. */
+ if (lcp -> lcd_window_condition == TRUE)
+ lcp -> lcd_window_condition = FALSE;
+
+ if (so && ((so -> so_snd.sb_flags & SB_WAIT) ||
+ (so -> so_snd.sb_flags & SB_NOTIFY)))
+ sowwakeup (so);
+
+ return (PACKET_OK);
+}
+
+/*
+ * This procedure decodes the X.25 level 3 packet returning a
+ * code to be used in switchs or arrays.
+ */
+
+pk_decode (xp)
+register struct x25_packet *xp;
+{
+ register int type;
+
+ if (X25GBITS(xp -> bits, fmt_identifier) != 1)
+ return (INVALID_PACKET);
+#ifdef ancient_history
+ /*
+ * Make sure that the logical channel group number is 0.
+ * This restriction may be removed at some later date.
+ */
+ if (xp -> lc_group_number != 0)
+ return (INVALID_PACKET);
+#endif
+ /*
+ * Test for data packet first.
+ */
+ if (!(xp -> packet_type & DATA_PACKET_DESIGNATOR))
+ return (DATA);
+
+ /*
+ * Test if flow control packet (RR or RNR).
+ */
+ if (!(xp -> packet_type & RR_OR_RNR_PACKET_DESIGNATOR))
+ switch (xp -> packet_type & 0x1f) {
+ case X25_RR:
+ return (RR);
+ case X25_RNR:
+ return (RNR);
+ case X25_REJECT:
+ return (REJECT);
+ }
+
+ /*
+ * Determine the rest of the packet types.
+ */
+ switch (xp -> packet_type) {
+ case X25_CALL:
+ type = CALL;
+ break;
+
+ case X25_CALL_ACCEPTED:
+ type = CALL_ACCEPTED;
+ break;
+
+ case X25_CLEAR:
+ type = CLEAR;
+ break;
+
+ case X25_CLEAR_CONFIRM:
+ type = CLEAR_CONF;
+ break;
+
+ case X25_INTERRUPT:
+ type = INTERRUPT;
+ break;
+
+ case X25_INTERRUPT_CONFIRM:
+ type = INTERRUPT_CONF;
+ break;
+
+ case X25_RESET:
+ type = RESET;
+ break;
+
+ case X25_RESET_CONFIRM:
+ type = RESET_CONF;
+ break;
+
+ case X25_RESTART:
+ type = RESTART;
+ break;
+
+ case X25_RESTART_CONFIRM:
+ type = RESTART_CONF;
+ break;
+
+ case X25_DIAGNOSTIC:
+ type = DIAG_TYPE;
+ break;
+
+ default:
+ type = INVALID_PACKET;
+ }
+ return (type);
+}
+
+/*
+ * A restart packet has been received. Print out the reason
+ * for the restart.
+ */
+
+pk_restartcause (pkp, xp)
+struct pkcb *pkp;
+register struct x25_packet *xp;
+{
+ register struct x25config *xcp = pkp -> pk_xcp;
+ register int lcn = LCN(xp);
+
+ switch (xp -> packet_data) {
+ case X25_RESTART_LOCAL_PROCEDURE_ERROR:
+ pk_message (lcn, xcp, "restart: local procedure error");
+ break;
+
+ case X25_RESTART_NETWORK_CONGESTION:
+ pk_message (lcn, xcp, "restart: network congestion");
+ break;
+
+ case X25_RESTART_NETWORK_OPERATIONAL:
+ pk_message (lcn, xcp, "restart: network operational");
+ break;
+
+ default:
+ pk_message (lcn, xcp, "restart: unknown cause");
+ }
+}
+
+#define MAXRESETCAUSE 7
+
+int Reset_cause[] = {
+ EXRESET, EXROUT, 0, EXRRPE, 0, EXRLPE, 0, EXRNCG
+};
+
+/*
+ * A reset packet has arrived. Return the cause to the user.
+ */
+
+pk_resetcause (pkp, xp)
+struct pkcb *pkp;
+register struct x25_packet *xp;
+{
+ register struct pklcd *lcp =
+ pkp -> pk_chan[LCN(xp)];
+ register int code = xp -> packet_data;
+
+ if (code > MAXRESETCAUSE)
+ code = 7; /* EXRNCG */
+
+ pk_message (LCN(xp), lcp -> lcd_pkp, "reset code 0x%x, diagnostic 0x%x",
+ xp -> packet_data, 4[(u_char *)xp]);
+
+ if (lcp -> lcd_so)
+ lcp -> lcd_so -> so_error = Reset_cause[code];
+}
+
+#define MAXCLEARCAUSE 25
+
+int Clear_cause[] = {
+ EXCLEAR, EXCBUSY, 0, EXCINV, 0, EXCNCG, 0,
+ 0, 0, EXCOUT, 0, EXCAB, 0, EXCNOB, 0, 0, 0, EXCRPE,
+ 0, EXCLPE, 0, 0, 0, 0, 0, EXCRRC
+};
+
+/*
+ * A clear packet has arrived. Return the cause to the user.
+ */
+
+pk_clearcause (pkp, xp)
+struct pkcb *pkp;
+register struct x25_packet *xp;
+{
+ register struct pklcd *lcp =
+ pkp -> pk_chan[LCN(xp)];
+ register int code = xp -> packet_data;
+
+ if (code > MAXCLEARCAUSE)
+ code = 5; /* EXRNCG */
+ if (lcp -> lcd_so)
+ lcp -> lcd_so -> so_error = Clear_cause[code];
+}
+
+char *
+format_ntn (xcp)
+register struct x25config *xcp;
+{
+
+ return (xcp -> xc_addr.x25_addr);
+}
+
+/* VARARGS1 */
+pk_message (lcn, xcp, fmt, a1, a2, a3, a4, a5, a6)
+struct x25config *xcp;
+char *fmt;
+{
+
+ if (lcn)
+ if (!PQEMPTY)
+ printf ("X.25(%s): lcn %d: ", format_ntn (xcp), lcn);
+ else
+ printf ("X.25: lcn %d: ", lcn);
+ else
+ if (!PQEMPTY)
+ printf ("X.25(%s): ", format_ntn (xcp));
+ else
+ printf ("X.25: ");
+
+ printf (fmt, a1, a2, a3, a4, a5, a6);
+ printf ("\n");
+}
+
+pk_fragment (lcp, m0, qbit, mbit, wait)
+struct mbuf *m0;
+register struct pklcd *lcp;
+{
+ register struct mbuf *m = m0;
+ register struct x25_packet *xp;
+ register struct sockbuf *sb;
+ struct mbuf *head = 0, *next, **mp = &head, *m_split ();
+ int totlen, psize = 1 << (lcp -> lcd_packetsize);
+
+ if (m == 0)
+ return 0;
+ if (m -> m_flags & M_PKTHDR == 0)
+ panic ("pk_fragment");
+ totlen = m -> m_pkthdr.len;
+ m -> m_act = 0;
+ sb = lcp -> lcd_so ? &lcp -> lcd_so -> so_snd : & lcp -> lcd_sb;
+ do {
+ if (totlen > psize) {
+ if ((next = m_split (m, psize, wait)) == 0)
+ goto abort;
+ totlen -= psize;
+ } else
+ next = 0;
+ M_PREPEND(m, PKHEADERLN, wait);
+ if (m == 0)
+ goto abort;
+ *mp = m;
+ mp = & m -> m_act;
+ *mp = 0;
+ xp = mtod (m, struct x25_packet *);
+ 0[(char *)xp] = 0;
+ if (qbit)
+ X25SBITS(xp -> bits, q_bit, 1);
+ if (lcp -> lcd_flags & X25_DBIT)
+ X25SBITS(xp -> bits, d_bit, 1);
+ X25SBITS(xp -> bits, fmt_identifier, 1);
+ xp -> packet_type = X25_DATA;
+ SET_LCN(xp, lcp -> lcd_lcn);
+ if (next || (mbit && (totlen == psize ||
+ (lcp -> lcd_flags & X25_DBIT))))
+ SMBIT(xp, 1);
+ } while (m = next);
+ for (m = head; m; m = next) {
+ next = m -> m_act;
+ m -> m_act = 0;
+ sbappendrecord (sb, m);
+ }
+ return 0;
+abort:
+ if (wait)
+ panic ("pk_fragment null mbuf after wait");
+ if (next)
+ m_freem (next);
+ for (m = head; m; m = next) {
+ next = m -> m_act;
+ m_freem (m);
+ }
+ return ENOBUFS;
+}
diff --git a/sys/netccitt/pk_timer.c b/sys/netccitt/pk_timer.c
new file mode 100644
index 0000000..52c1860
--- /dev/null
+++ b/sys/netccitt/pk_timer.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) Computing Centre, University of British Columbia, 1984
+ * Copyright (C) Computer Science Department IV,
+ * University of Erlangen-Nuremberg, Germany, 1990, 1992
+ * Copyright (c) 1990, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by the
+ * Laboratory for Computation Vision and the Computer Science Department
+ * of the the University of British Columbia and the Computer Science
+ * Department (IV) of the University of Erlangen-Nuremberg, Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pk_timer.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+
+#include <netccitt/x25.h>
+#include <netccitt/pk.h>
+#include <netccitt/pk_var.h>
+
+/*
+ * Various timer values. They can be adjusted
+ * by patching the binary with adb if necessary.
+ */
+int pk_t20 = 18 * PR_SLOWHZ; /* restart timer */
+int pk_t21 = 20 * PR_SLOWHZ; /* call timer */
+/* XXX pk_t22 is never used */
+int pk_t22 = 18 * PR_SLOWHZ; /* reset timer */
+int pk_t23 = 18 * PR_SLOWHZ; /* clear timer */
+
+pk_timer ()
+{
+ register struct pkcb *pkp;
+ register struct pklcd *lcp, **pp;
+ register int lcns_jammed, cant_restart;
+
+ FOR_ALL_PKCBS(pkp) {
+ switch (pkp -> pk_state) {
+ case DTE_SENT_RESTART:
+ lcp = pkp -> pk_chan[0];
+ /*
+ * If restart failures are common, a link level
+ * reset should be initiated here.
+ */
+ if (lcp -> lcd_timer && --lcp -> lcd_timer == 0) {
+ pk_message (0, pkp -> pk_xcp,
+ "packet level restart failed");
+ pkp -> pk_state = DTE_WAITING;
+ }
+ break;
+
+ case DTE_READY:
+ lcns_jammed = cant_restart = 0;
+ for (pp = &pkp -> pk_chan[1]; pp <= &pkp -> pk_chan[pkp -> pk_maxlcn]; pp++) {
+ if ((lcp = *pp) == 0)
+ continue;
+ switch (lcp -> lcd_state) {
+ case SENT_CALL:
+ if (--lcp -> lcd_timer == 0) {
+ if (lcp -> lcd_so)
+ lcp -> lcd_so -> so_error = ETIMEDOUT;
+ pk_clear (lcp, 49, 1);
+ }
+ break;
+
+ case SENT_CLEAR:
+ if (lcp -> lcd_retry >= 3)
+ lcns_jammed++;
+ else
+ if (--lcp -> lcd_timer == 0)
+ pk_clear (lcp, 50, 1);
+ break;
+
+ case DATA_TRANSFER: /* lcn active */
+ cant_restart++;
+ break;
+
+ case LCN_ZOMBIE: /* zombie state */
+ pk_freelcd (lcp);
+ break;
+ }
+ }
+ if (lcns_jammed > pkp -> pk_maxlcn / 2 && cant_restart == 0) {
+ pk_message (0, pkp -> pk_xcp, "%d lcns jammed: attempting restart", lcns_jammed);
+ pk_restart (pkp, 0);
+ }
+ }
+ }
+}
diff --git a/sys/netccitt/pk_usrreq.c b/sys/netccitt/pk_usrreq.c
new file mode 100644
index 0000000..d0dc42c
--- /dev/null
+++ b/sys/netccitt/pk_usrreq.c
@@ -0,0 +1,604 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (C) Computer Science Department IV,
+ * University of Erlangen-Nuremberg, Germany, 1992
+ * Copyright (c) 1991, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by the
+ * Laboratory for Computation Vision and the Computer Science Department
+ * of the the University of British Columbia and the Computer Science
+ * Department (IV) of the University of Erlangen-Nuremberg, Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pk_usrreq.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netccitt/x25.h>
+#include <netccitt/pk.h>
+#include <netccitt/pk_var.h>
+
+static old_to_new();
+static new_to_old();
+/*
+ *
+ * X.25 Packet level protocol interface to socket abstraction.
+ *
+ * Process an X.25 user request on a logical channel. If this is a send
+ * request then m is the mbuf chain of the send data. If this is a timer
+ * expiration (called from the software clock routine) them timertype is
+ * the particular timer.
+ *
+ */
+
+pk_usrreq (so, req, m, nam, control)
+struct socket *so;
+int req;
+register struct mbuf *m, *nam;
+struct mbuf *control;
+{
+ register struct pklcd *lcp = (struct pklcd *) so -> so_pcb;
+ register int error = 0;
+
+ if (req == PRU_CONTROL)
+ return (pk_control (so, (int)m, (caddr_t)nam,
+ (struct ifnet *)control));
+ if (control && control -> m_len) {
+ error = EINVAL;
+ goto release;
+ }
+ if (lcp == NULL && req != PRU_ATTACH) {
+ error = EINVAL;
+ goto release;
+ }
+
+/*
+ pk_trace (pkcbhead, TR_USER, (struct pklcd *)0,
+ req, (struct x25_packet *)0);
+*/
+
+ switch (req) {
+ /*
+ * X.25 attaches to socket via PRU_ATTACH and allocates a logical
+ * channel descriptor. If the socket is to receive connections,
+ * then the LISTEN state is entered.
+ */
+ case PRU_ATTACH:
+ if (lcp) {
+ error = EISCONN;
+ /* Socket already connected. */
+ break;
+ }
+ lcp = pk_attach (so);
+ if (lcp == 0)
+ error = ENOBUFS;
+ break;
+
+ /*
+ * Detach a logical channel from the socket. If the state of the
+ * channel is embryonic, simply discard it. Otherwise we have to
+ * initiate a PRU_DISCONNECT which will finish later.
+ */
+ case PRU_DETACH:
+ pk_disconnect (lcp);
+ break;
+
+ /*
+ * Give the socket an address.
+ */
+ case PRU_BIND:
+ if (nam -> m_len == sizeof (struct x25_sockaddr))
+ old_to_new (nam);
+ error = pk_bind (lcp, nam);
+ break;
+
+ /*
+ * Prepare to accept connections.
+ */
+ case PRU_LISTEN:
+ error = pk_listen (lcp);
+ break;
+
+ /*
+ * Initiate a CALL REQUEST to peer entity. Enter state SENT_CALL
+ * and mark the socket as connecting. Set timer waiting for
+ * CALL ACCEPT or CLEAR.
+ */
+ case PRU_CONNECT:
+ if (nam -> m_len == sizeof (struct x25_sockaddr))
+ old_to_new (nam);
+ if (pk_checksockaddr (nam))
+ return (EINVAL);
+ error = pk_connect (lcp, mtod (nam, struct sockaddr_x25 *));
+ break;
+
+ /*
+ * Initiate a disconnect to peer entity via a CLEAR REQUEST packet.
+ * The socket will be disconnected when we receive a confirmation
+ * or a clear collision.
+ */
+ case PRU_DISCONNECT:
+ pk_disconnect (lcp);
+ break;
+
+ /*
+ * Accept an INCOMING CALL. Most of the work has already been done
+ * by pk_input. Just return the callers address to the user.
+ */
+ case PRU_ACCEPT:
+ if (lcp -> lcd_craddr == NULL)
+ break;
+ bcopy ((caddr_t)lcp -> lcd_craddr, mtod (nam, caddr_t),
+ sizeof (struct sockaddr_x25));
+ nam -> m_len = sizeof (struct sockaddr_x25);
+ if (lcp -> lcd_flags & X25_OLDSOCKADDR)
+ new_to_old (nam);
+ break;
+
+ /*
+ * After a receive, we should send a RR.
+ */
+ case PRU_RCVD:
+ pk_flowcontrol (lcp, /*sbspace (&so -> so_rcv) <= */ 0, 1);
+ break;
+
+ /*
+ * Send INTERRUPT packet.
+ */
+ case PRU_SENDOOB:
+ if (m == 0) {
+ MGETHDR(m, M_WAITOK, MT_OOBDATA);
+ m -> m_pkthdr.len = m -> m_len = 1;
+ *mtod (m, octet *) = 0;
+ }
+ if (m -> m_pkthdr.len > 32) {
+ m_freem (m);
+ error = EMSGSIZE;
+ break;
+ }
+ MCHTYPE(m, MT_OOBDATA);
+ /* FALLTHROUGH */
+
+ /*
+ * Do send by placing data on the socket output queue.
+ */
+ case PRU_SEND:
+ if (control) {
+ register struct cmsghdr *ch = mtod (m, struct cmsghdr *);
+ control -> m_len -= sizeof (*ch);
+ control -> m_data += sizeof (*ch);
+ error = pk_ctloutput (PRCO_SETOPT, so, ch -> cmsg_level,
+ ch -> cmsg_type, &control);
+ }
+ if (error == 0 && m)
+ error = pk_send (lcp, m);
+ break;
+
+ /*
+ * Abort a virtual circuit. For example all completed calls
+ * waiting acceptance.
+ */
+ case PRU_ABORT:
+ pk_disconnect (lcp);
+ break;
+
+ /* Begin unimplemented hooks. */
+
+ case PRU_SHUTDOWN:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_CONTROL:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_SENSE:
+#ifdef BSD4_3
+ ((struct stat *)m) -> st_blksize = so -> so_snd.sb_hiwat;
+#else
+ error = EOPNOTSUPP;
+#endif
+ break;
+
+ /* End unimplemented hooks. */
+
+ case PRU_SOCKADDR:
+ if (lcp -> lcd_ceaddr == 0)
+ return (EADDRNOTAVAIL);
+ nam -> m_len = sizeof (struct sockaddr_x25);
+ bcopy ((caddr_t)lcp -> lcd_ceaddr, mtod (nam, caddr_t),
+ sizeof (struct sockaddr_x25));
+ if (lcp -> lcd_flags & X25_OLDSOCKADDR)
+ new_to_old (nam);
+ break;
+
+ case PRU_PEERADDR:
+ if (lcp -> lcd_state != DATA_TRANSFER)
+ return (ENOTCONN);
+ nam -> m_len = sizeof (struct sockaddr_x25);
+ bcopy (lcp -> lcd_craddr ? (caddr_t)lcp -> lcd_craddr :
+ (caddr_t)lcp -> lcd_ceaddr,
+ mtod (nam, caddr_t), sizeof (struct sockaddr_x25));
+ if (lcp -> lcd_flags & X25_OLDSOCKADDR)
+ new_to_old (nam);
+ break;
+
+ /*
+ * Receive INTERRUPT packet.
+ */
+ case PRU_RCVOOB:
+ if (so -> so_options & SO_OOBINLINE) {
+ register struct mbuf *n = so -> so_rcv.sb_mb;
+ if (n && n -> m_type == MT_OOBDATA) {
+ unsigned len = n -> m_pkthdr.len;
+ so -> so_rcv.sb_mb = n -> m_nextpkt;
+ if (len != n -> m_len &&
+ (n = m_pullup (n, len)) == 0)
+ break;
+ m -> m_len = len;
+ bcopy (mtod (m, caddr_t), mtod (n, caddr_t), len);
+ m_freem (n);
+ }
+ break;
+ }
+ m -> m_len = 1;
+ *mtod (m, char *) = lcp -> lcd_intrdata;
+ break;
+
+ default:
+ panic ("pk_usrreq");
+ }
+release:
+ if (control != NULL)
+ m_freem (control);
+ return (error);
+}
+
+/*
+ * If you want to use UBC X.25 level 3 in conjunction with some
+ * other X.25 level 2 driver, have the ifp -> if_ioctl routine
+ * assign pk_start to ia -> ia_start when called with SIOCSIFCONF_X25.
+ */
+/* ARGSUSED */
+pk_start (lcp)
+register struct pklcd *lcp;
+{
+ pk_output (lcp);
+ return (0); /* XXX pk_output should return a value */
+}
+
+#ifndef _offsetof
+#define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m))
+#endif
+struct sockaddr_x25 pk_sockmask = {
+ _offsetof(struct sockaddr_x25, x25_addr[0]), /* x25_len */
+ 0, /* x25_family */
+ -1, /* x25_net id */
+};
+
+/*ARGSUSED*/
+pk_control (so, cmd, data, ifp)
+struct socket *so;
+int cmd;
+caddr_t data;
+register struct ifnet *ifp;
+{
+ register struct ifreq_x25 *ifr = (struct ifreq_x25 *)data;
+ register struct ifaddr *ifa = 0;
+ register struct x25_ifaddr *ia = 0;
+ struct pklcd *dev_lcp = 0;
+ int error, s, old_maxlcn;
+ unsigned n;
+
+ /*
+ * Find address for this interface, if it exists.
+ */
+ if (ifp)
+ for (ifa = ifp -> if_addrlist; ifa; ifa = ifa -> ifa_next)
+ if (ifa -> ifa_addr -> sa_family == AF_CCITT)
+ break;
+
+ ia = (struct x25_ifaddr *)ifa;
+ switch (cmd) {
+ case SIOCGIFCONF_X25:
+ if (ifa == 0)
+ return (EADDRNOTAVAIL);
+ ifr -> ifr_xc = ia -> ia_xc;
+ return (0);
+
+ case SIOCSIFCONF_X25:
+ if ((so->so_state & SS_PRIV) == 0)
+ return (EPERM);
+ if (ifp == 0)
+ panic ("pk_control");
+ if (ifa == (struct ifaddr *)0) {
+ register struct mbuf *m;
+
+ MALLOC(ia, struct x25_ifaddr *, sizeof (*ia),
+ M_IFADDR, M_WAITOK);
+ if (ia == 0)
+ return (ENOBUFS);
+ bzero ((caddr_t)ia, sizeof (*ia));
+ if (ifa = ifp -> if_addrlist) {
+ for ( ; ifa -> ifa_next; ifa = ifa -> ifa_next)
+ ;
+ ifa -> ifa_next = &ia -> ia_ifa;
+ } else
+ ifp -> if_addrlist = &ia -> ia_ifa;
+ ifa = &ia -> ia_ifa;
+ ifa -> ifa_netmask = (struct sockaddr *)&pk_sockmask;
+ ifa -> ifa_addr = (struct sockaddr *)&ia -> ia_xc.xc_addr;
+ ifa -> ifa_dstaddr = (struct sockaddr *)&ia -> ia_dstaddr; /* XXX */
+ ia -> ia_ifp = ifp;
+ ia -> ia_dstaddr.x25_family = AF_CCITT;
+ ia -> ia_dstaddr.x25_len = pk_sockmask.x25_len;
+ } else if (ISISO8802(ifp) == 0) {
+ rtinit (ifa, (int)RTM_DELETE, 0);
+ }
+ old_maxlcn = ia -> ia_maxlcn;
+ ia -> ia_xc = ifr -> ifr_xc;
+ ia -> ia_dstaddr.x25_net = ia -> ia_xc.xc_addr.x25_net;
+ if (ia -> ia_maxlcn != old_maxlcn && old_maxlcn != 0) {
+ /* VERY messy XXX */
+ register struct pkcb *pkp;
+ FOR_ALL_PKCBS(pkp)
+ if (pkp -> pk_ia == ia)
+ pk_resize (pkp);
+ }
+ /*
+ * Give the interface a chance to initialize if this
+p * is its first address, and to validate the address.
+ */
+ ia -> ia_start = pk_start;
+ s = splimp();
+ if (ifp -> if_ioctl)
+ error = (*ifp -> if_ioctl)(ifp, SIOCSIFCONF_X25,
+ (caddr_t) ifa);
+ if (error)
+ ifp -> if_flags &= ~IFF_UP;
+ else if (ISISO8802(ifp) == 0)
+ error = rtinit (ifa, (int)RTM_ADD, RTF_UP);
+ splx (s);
+ return (error);
+
+ default:
+ if (ifp == 0 || ifp -> if_ioctl == 0)
+ return (EOPNOTSUPP);
+ return ((*ifp -> if_ioctl)(ifp, cmd, data));
+ }
+}
+
+pk_ctloutput (cmd, so, level, optname, mp)
+struct socket *so;
+struct mbuf **mp;
+int cmd, level, optname;
+{
+ register struct mbuf *m = *mp;
+ register struct pklcd *lcp = (struct pklcd *) so -> so_pcb;
+ int error = EOPNOTSUPP;
+
+ if (m == 0)
+ return (EINVAL);
+ if (cmd == PRCO_SETOPT) switch (optname) {
+ case PK_FACILITIES:
+ if (m == 0)
+ return (EINVAL);
+ lcp -> lcd_facilities = m;
+ *mp = 0;
+ return (0);
+
+ case PK_ACCTFILE:
+ if ((so->so_state & SS_PRIV) == 0)
+ error = EPERM;
+ else if (m -> m_len)
+ error = pk_accton (mtod (m, char *));
+ else
+ error = pk_accton ((char *)0);
+ break;
+
+ case PK_RTATTACH:
+ error = pk_rtattach (so, m);
+ break;
+
+ case PK_PRLISTEN:
+ error = pk_user_protolisten (mtod (m, u_char *));
+ }
+ if (*mp) {
+ (void) m_freem (*mp);
+ *mp = 0;
+ }
+ return (error);
+
+}
+
+
+/*
+ * Do an in-place conversion of an "old style"
+ * socket address to the new style
+ */
+
+static
+old_to_new (m)
+register struct mbuf *m;
+{
+ register struct x25_sockaddr *oldp;
+ register struct sockaddr_x25 *newp;
+ register char *ocp, *ncp;
+ struct sockaddr_x25 new;
+
+ oldp = mtod (m, struct x25_sockaddr *);
+ newp = &new;
+ bzero ((caddr_t)newp, sizeof (*newp));
+
+ newp -> x25_family = AF_CCITT;
+ newp -> x25_len = sizeof(*newp);
+ newp -> x25_opts.op_flags = (oldp -> xaddr_facilities & X25_REVERSE_CHARGE)
+ | X25_MQBIT | X25_OLDSOCKADDR;
+ if (oldp -> xaddr_facilities & XS_HIPRIO) /* Datapac specific */
+ newp -> x25_opts.op_psize = X25_PS128;
+ bcopy ((caddr_t)oldp -> xaddr_addr, newp -> x25_addr,
+ (unsigned)min (oldp -> xaddr_len, sizeof (newp -> x25_addr) - 1));
+ if (bcmp ((caddr_t)oldp -> xaddr_proto, newp -> x25_udata, 4) != 0) {
+ bcopy ((caddr_t)oldp -> xaddr_proto, newp -> x25_udata, 4);
+ newp -> x25_udlen = 4;
+ }
+ ocp = (caddr_t)oldp -> xaddr_userdata;
+ ncp = newp -> x25_udata + 4;
+ while (*ocp && ocp < (caddr_t)oldp -> xaddr_userdata + 12) {
+ if (newp -> x25_udlen == 0)
+ newp -> x25_udlen = 4;
+ *ncp++ = *ocp++;
+ newp -> x25_udlen++;
+ }
+ bcopy ((caddr_t)newp, mtod (m, char *), sizeof (*newp));
+ m -> m_len = sizeof (*newp);
+}
+
+/*
+ * Do an in-place conversion of a new style
+ * socket address to the old style
+ */
+
+static
+new_to_old (m)
+register struct mbuf *m;
+{
+ register struct x25_sockaddr *oldp;
+ register struct sockaddr_x25 *newp;
+ register char *ocp, *ncp;
+ struct x25_sockaddr old;
+
+ oldp = &old;
+ newp = mtod (m, struct sockaddr_x25 *);
+ bzero ((caddr_t)oldp, sizeof (*oldp));
+
+ oldp -> xaddr_facilities = newp -> x25_opts.op_flags & X25_REVERSE_CHARGE;
+ if (newp -> x25_opts.op_psize == X25_PS128)
+ oldp -> xaddr_facilities |= XS_HIPRIO; /* Datapac specific */
+ ocp = (char *)oldp -> xaddr_addr;
+ ncp = newp -> x25_addr;
+ while (*ncp) {
+ *ocp++ = *ncp++;
+ oldp -> xaddr_len++;
+ }
+
+ bcopy (newp -> x25_udata, (caddr_t)oldp -> xaddr_proto, 4);
+ if (newp -> x25_udlen > 4)
+ bcopy (newp -> x25_udata + 4, (caddr_t)oldp -> xaddr_userdata,
+ (unsigned)(newp -> x25_udlen - 4));
+
+ bcopy ((caddr_t)oldp, mtod (m, char *), sizeof (*oldp));
+ m -> m_len = sizeof (*oldp);
+}
+
+
+pk_checksockaddr (m)
+struct mbuf *m;
+{
+ register struct sockaddr_x25 *sa = mtod (m, struct sockaddr_x25 *);
+ register char *cp;
+
+ if (m -> m_len != sizeof (struct sockaddr_x25))
+ return (1);
+ if (sa -> x25_family != AF_CCITT ||
+ sa -> x25_udlen > sizeof (sa -> x25_udata))
+ return (1);
+ for (cp = sa -> x25_addr; *cp; cp++) {
+ if (*cp < '0' || *cp > '9' ||
+ cp >= &sa -> x25_addr[sizeof (sa -> x25_addr) - 1])
+ return (1);
+ }
+ return (0);
+}
+
+pk_send (lcp, m)
+struct pklcd *lcp;
+register struct mbuf *m;
+{
+ int mqbit = 0, error = 0;
+ register struct x25_packet *xp;
+ register struct socket *so;
+
+ if (m -> m_type == MT_OOBDATA) {
+ if (lcp -> lcd_intrconf_pending)
+ error = ETOOMANYREFS;
+ if (m -> m_pkthdr.len > 32)
+ error = EMSGSIZE;
+ M_PREPEND(m, PKHEADERLN, M_WAITOK);
+ if (m == 0 || error)
+ goto bad;
+ *(mtod (m, octet *)) = 0;
+ xp = mtod (m, struct x25_packet *);
+ X25SBITS(xp -> bits, fmt_identifier, 1);
+ xp -> packet_type = X25_INTERRUPT;
+ SET_LCN(xp, lcp -> lcd_lcn);
+ sbinsertoob ( (so = lcp -> lcd_so) ?
+ &so -> so_snd : &lcp -> lcd_sb, m);
+ goto send;
+ }
+ /*
+ * Application has elected (at call setup time) to prepend
+ * a control byte to each packet written indicating m-bit
+ * and q-bit status. Examine and then discard this byte.
+ */
+ if (lcp -> lcd_flags & X25_MQBIT) {
+ if (m -> m_len < 1) {
+ m_freem (m);
+ return (EMSGSIZE);
+ }
+ mqbit = *(mtod (m, u_char *));
+ m -> m_len--;
+ m -> m_data++;
+ m -> m_pkthdr.len--;
+ }
+ error = pk_fragment (lcp, m, mqbit & 0x80, mqbit & 0x40, 1);
+send:
+ if (error == 0 && lcp -> lcd_state == DATA_TRANSFER)
+ lcp -> lcd_send (lcp); /* XXXXXXXXX fix pk_output!!! */
+ return (error);
+bad:
+ if (m)
+ m_freem (m);
+ return (error);
+}
diff --git a/sys/netccitt/pk_var.h b/sys/netccitt/pk_var.h
new file mode 100644
index 0000000..beda05d
--- /dev/null
+++ b/sys/netccitt/pk_var.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) Computing Centre, University of British Columbia, 1985
+ * Copyright (C) Computer Science Department IV,
+ * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by the
+ * Laboratory for Computation Vision and the Computer Science Department
+ * of the the University of British Columbia and the Computer Science
+ * Department (IV) of the University of Erlangen-Nuremberg, Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pk_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ *
+ * X.25 Logical Channel Descriptor
+ *
+ */
+
+struct pklcd {
+ struct pklcd_q {
+ struct pklcd_q *q_forw; /* debugging chain */
+ struct pklcd_q *q_back; /* debugging chain */
+ } lcd_q;
+ int (*lcd_upper)(); /* switch to socket vs datagram vs ...*/
+ caddr_t lcd_upnext; /* reference for lcd_upper() */
+ int (*lcd_send)(); /* if X.25 front end, direct connect */
+ caddr_t lcd_downnext; /* reference for lcd_send() */
+ short lcd_lcn; /* Logical channel number */
+ short lcd_state; /* Logical Channel state */
+ short lcd_timer; /* Various timer values */
+ short lcd_dg_timer; /* to reclaim idle datagram circuits */
+ bool lcd_intrconf_pending; /* Interrupt confirmation pending */
+ octet lcd_intrdata; /* Octet of incoming intr data */
+ char lcd_retry; /* Timer retry count */
+ char lcd_rsn; /* Seq no of last received packet */
+ char lcd_ssn; /* Seq no of next packet to send */
+ char lcd_output_window; /* Output flow control window */
+ char lcd_input_window; /* Input flow control window */
+ char lcd_last_transmitted_pr;/* Last Pr value transmitted */
+ bool lcd_rnr_condition; /* Remote in busy condition */
+ bool lcd_window_condition; /* Output window size exceeded */
+ bool lcd_reset_condition; /* True, if waiting reset confirm */
+ bool lcd_rxrnr_condition; /* True, if we have sent rnr */
+ char lcd_packetsize; /* Maximum packet size */
+ char lcd_windowsize; /* Window size - both directions */
+ octet lcd_closed_user_group; /* Closed user group specification */
+ char lcd_flags; /* copy of sockaddr_x25 op_flags */
+ struct mbuf *lcd_facilities; /* user supplied facilities for cr */
+ struct mbuf *lcd_template; /* Address of response packet */
+ struct socket *lcd_so; /* Socket addr for connection */
+ struct sockaddr_x25 *lcd_craddr;/* Calling address pointer */
+ struct sockaddr_x25 *lcd_ceaddr;/* Called address pointer */
+ time_t lcd_stime; /* time circuit established */
+ long lcd_txcnt; /* Data packet transmit count */
+ long lcd_rxcnt; /* Data packet receive count */
+ short lcd_intrcnt; /* Interrupt packet transmit count */
+ struct pklcd *lcd_listen; /* Next lcd on listen queue */
+ struct pkcb *lcd_pkp; /* Network this lcd is attached to */
+ struct mbuf *lcd_cps; /* Complete Packet Sequence reassembly*/
+ long lcd_cpsmax; /* Max length for CPS */
+ struct sockaddr_x25 lcd_faddr; /* Remote Address (Calling) */
+ struct sockaddr_x25 lcd_laddr; /* Local Address (Called) */
+ struct sockbuf lcd_sb; /* alternate for datagram service */
+};
+
+/*
+ * Per network information, allocated dynamically
+ * when a new network is configured.
+ */
+
+struct pkcb {
+ struct pkcb_q {
+ struct pkcb_q *q_forw;
+ struct pkcb_q *q_backw;
+ } pk_q;
+ short pk_state; /* packet level status */
+ short pk_maxlcn; /* local copy of xc_maxlcn */
+ int (*pk_lloutput) (); /* link level output procedure */
+ caddr_t (*pk_llctlinput) (); /* link level ctloutput procedure */
+ caddr_t pk_llnext; /* handle for next level down */
+ struct x25config *pk_xcp; /* network specific configuration */
+ struct x25_ifaddr *pk_ia; /* backpointer to ifaddr */
+ struct pklcd **pk_chan; /* actual size == xc_maxlcn+1 */
+ short pk_dxerole; /* DXE role of PLE over LLC2 */
+ short pk_restartcolls; /* counting RESTART collisions til resolved */
+ struct rtentry *pk_rt; /* back pointer to route */
+ struct rtentry *pk_llrt; /* pointer to reverse mapping */
+ u_short pk_refcount; /* ref count */
+};
+
+#define FOR_ALL_PKCBS(p) for((p) = (struct pkcb *)(pkcb_q.q_forw); \
+ (pkcb_q.q_forw != &pkcb_q) && ((struct pkcb_q *)(p) != &pkcb_q); \
+ (p) = (struct pkcb *)((p) -> pk_q.q_forw))
+
+#define PQEMPTY (pkcb_q.q_forw == &pkcb_q)
+
+/*
+ * Interface address, x25 version. Exactly one of these structures is
+ * allocated for each interface with an x25 address.
+ *
+ * The ifaddr structure conatins the protocol-independent part
+ * of the structure, and is assumed to be first.
+ */
+struct x25_ifaddr {
+ struct ifaddr ia_ifa; /* protocol-independent info */
+#define ia_ifp ia_ifa.ifa_ifp
+#define ia_flags ia_ifa.ifa_flags
+ struct x25config ia_xc; /* network specific configuration */
+ struct pkcb *ia_pkcb;
+#define ia_maxlcn ia_xc.xc_maxlcn
+ int (*ia_start) (); /* connect, confirm method */
+ struct sockaddr_x25 ia_dstaddr; /* reserve space for route dst */
+};
+
+/*
+ * ``Link-Level'' extension to Routing Entry for upper level
+ * packet switching via X.25 virtual circuits.
+ */
+struct llinfo_x25 {
+ struct llinfo_x25 *lx_next; /* chain together in linked list */
+ struct llinfo_x25 *lx_prev; /* chain together in linked list */
+ struct rtentry *lx_rt; /* back pointer to route */
+ struct pklcd *lx_lcd; /* local connection block */
+ struct x25_ifaddr *lx_ia; /* may not be same as rt_ifa */
+ int lx_state; /* can't trust lcd->lcd_state */
+ int lx_flags;
+ int lx_timer; /* for idle timeout */
+ int lx_family; /* for dispatch */
+};
+
+/* States for lx_state */
+#define LXS_NEWBORN 0
+#define LXS_RESOLVING 1
+#define LXS_FREE 2
+#define LXS_CONNECTING 3
+#define LXS_CONNECTED 4
+#define LXS_DISCONNECTING 5
+#define LXS_LISTENING 6
+
+/* flags */
+#define LXF_VALID 0x1 /* Circuit is live, etc. */
+#define LXF_RTHELD 0x2 /* this lcb references rtentry */
+#define LXF_LISTEN 0x4 /* accepting incoming calls */
+
+/*
+ * Definitions for accessing bitfields/bitslices inside X.25 structs
+ */
+
+
+struct x25bitslice {
+ unsigned int bs_mask;
+ unsigned int bs_shift;
+};
+
+#define calling_addrlen 0
+#define called_addrlen 1
+#define q_bit 2
+#define d_bit 3
+#define fmt_identifier 4
+#define lc_group_number 1
+#define p_r 5
+#define m_bit 6
+#define p_s 7
+#define zilch 8
+
+#define X25GBITS(Arg, Index) (((Arg) & x25_bitslice[(Index)].bs_mask) >> x25_bitslice[(Index)].bs_shift)
+#define X25SBITS(Arg, Index, Val) (Arg) |= (((Val) << x25_bitslice[(Index)].bs_shift) & x25_bitslice[(Index)].bs_mask)
+#define X25CSBITS(Arg, Index, Val) (Arg) = (((Val) << x25_bitslice[(Index)].bs_shift) & x25_bitslice[(Index)].bs_mask)
+
+extern struct x25bitslice x25_bitslice[];
+
+
+#define ISOFIFTTYPE(i,t) ((i)->if_type == (t))
+#define ISISO8802(i) ((ISOFIFTTYPE(i, IFT_ETHER) || \
+ ISOFIFTTYPE(i, IFT_ISO88023) || \
+ ISOFIFTTYPE(i, IFT_ISO88024) || \
+ ISOFIFTTYPE(i, IFT_ISO88025) || \
+ ISOFIFTTYPE(i, IFT_ISO88026) || \
+ ISOFIFTTYPE(i, IFT_P10) || \
+ ISOFIFTTYPE(i, IFT_P80) || \
+ ISOFIFTTYPE(i, IFT_FDDI)))
+
+/*
+ * miscellenous debugging info
+ */
+struct mbuf_cache {
+ int mbc_size;
+ int mbc_num;
+ int mbc_oldsize;
+ struct mbuf **mbc_cache;
+};
+
+#if defined(KERNEL) && defined(CCITT)
+extern struct pkcb_q pkcb_q;
+struct pklcd *pk_listenhead;
+struct pklcd *pk_attach();
+
+extern char *pk_name[], *pk_state[];
+int pk_t20, pk_t21, pk_t22, pk_t23;
+#endif
diff --git a/sys/netccitt/x25.h b/sys/netccitt/x25.h
new file mode 100644
index 0000000..e86af39
--- /dev/null
+++ b/sys/netccitt/x25.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (c) 1990, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * University of Erlangen-Nuremberg, Germany, 1992
+ *
+ * This code is derived from software contributed to Berkeley by the
+ * Laboratory for Computation Vision and the Computer Science Department
+ * of the the University of British Columbia and the Computer Science
+ * Department (IV) of the University of Erlangen-Nuremberg, Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)x25.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifdef KERNEL
+#define PRC_IFUP 3
+#define PRC_LINKUP 4
+#define PRC_LINKDOWN 5
+#define PRC_LINKRESET 6
+#define PRC_LINKDONTCOPY 7
+#ifndef PRC_DISCONNECT_REQUEST
+#define PRC_DISCONNECT_REQUEST 10
+#endif
+#endif
+
+#define CCITTPROTO_HDLC 1
+#define CCITTPROTO_X25 2 /* packet level protocol */
+#define IEEEPROTO_802LLC 3 /* doesn't belong here */
+
+#define HDLCPROTO_LAP 1
+#define HDLCPROTO_LAPB 2
+#define HDLCPROTO_UNSET 3
+#define HDLCPROTO_LAPD 4
+
+/* socket options */
+#define PK_ACCTFILE 1 /* use level = CCITTPROTO_X25 */
+#define PK_FACILITIES 2 /* use level = CCITTPROTO_X25 */
+#define PK_RTATTACH 3 /* use level = CCITTPROTO_X25 */
+#define PK_PRLISTEN 4 /* use level = CCITTPROTO_X25 */
+
+#define MAX_FACILITIES 109 /* maximum size for facilities */
+
+/*
+ * X.25 Socket address structure. It contains the X.121 or variation of
+ * X.121, facilities information, higher level protocol value (first four
+ * bytes of the User Data field), and the last 12 characters of the User
+ * Data field.
+ */
+
+struct x25_sockaddr { /* obsolete - use sockaddr_x25 */
+ short xaddr_len; /* Length of xaddr_addr. */
+ u_char xaddr_addr[15]; /* Network dependent or X.121 address. */
+ u_char xaddr_facilities; /* Facilities information. */
+#define XS_REVERSE_CHARGE 0x01
+#define XS_HIPRIO 0x02
+ u_char xaddr_proto[4]; /* Protocol ID (4 bytes of user data). */
+ u_char xaddr_userdata[12]; /* Remaining User data field. */
+};
+
+/*
+ * X.25 Socket address structure. It contains the network id, X.121
+ * address, facilities information, higher level protocol value (first four
+ * bytes of the User Data field), and up to 12 characters of User Data.
+ */
+
+struct sockaddr_x25 {
+ u_char x25_len;
+ u_char x25_family; /* must be AF_CCITT */
+ short x25_net; /* network id code (usually a dnic) */
+ char x25_addr[16]; /* X.121 address (null terminated) */
+ struct x25opts {
+ char op_flags; /* miscellaneous options */
+ /* pk_var.h defines other lcd_flags */
+#define X25_REVERSE_CHARGE 0x01 /* remote DTE pays for call */
+#define X25_DBIT 0x02 /* not yet supported */
+#define X25_MQBIT 0x04 /* prepend M&Q bit status byte to packet data */
+#define X25_OLDSOCKADDR 0x08 /* uses old sockaddr structure */
+#define X25_DG_CIRCUIT 0x10 /* lcd_flag: used for datagrams */
+#define X25_DG_ROUTING 0x20 /* lcd_flag: peer addr not yet known */
+#define X25_MBS_HOLD 0x40 /* lcd_flag: collect m-bit sequences */
+ char op_psize; /* requested packet size */
+#define X25_PS128 7
+#define X25_PS256 8
+#define X25_PS512 9
+ char op_wsize; /* window size (1 .. 7) */
+ char op_speed; /* throughput class */
+ } x25_opts;
+ short x25_udlen; /* user data field length */
+ char x25_udata[16]; /* user data field */
+};
+
+/*
+ * network configuration info
+ * this structure must be 16 bytes long
+ */
+
+struct x25config {
+ struct sockaddr_x25 xc_addr;
+ /* link level parameters */
+ u_short xc_lproto:4, /* link level protocol eg. CCITTPROTO_HDLC */
+ xc_lptype:4, /* protocol type eg. HDLCPROTO_LAPB */
+ xc_ltrace:1, /* link level tracing flag */
+ xc_lwsize:7; /* link level window size */
+ u_short xc_lxidxchg:1, /* link level XID exchange flag - NOT YET */
+ /* packet level parameters */
+ xc_rsvd1:2,
+ xc_pwsize:3, /* default window size */
+ xc_psize:4, /* default packet size 7=128, 8=256, ... */
+ xc_type:3, /* network type */
+#define X25_1976 0
+#define X25_1980 1
+#define X25_1984 2
+#define X25_DDN 3
+#define X25_BASIC 4
+ xc_ptrace:1, /* packet level tracing flag */
+ xc_nodnic:1, /* remove our dnic when calling on net */
+ xc_prepnd0:1; /* prepend 0 when making offnet calls */
+ u_short xc_maxlcn; /* max logical channels */
+ u_short xc_dg_idletimo; /* timeout for idle datagram circuits. */
+};
+
+#ifdef IFNAMSIZ
+struct ifreq_x25 {
+ char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ struct x25config ifr_xc;
+};
+#define SIOCSIFCONF_X25 _IOW('i', 12, struct ifreq_x25) /* set ifnet config */
+#define SIOCGIFCONF_X25 _IOWR('i',13, struct ifreq_x25) /* get ifnet config */
+#endif
diff --git a/sys/netccitt/x25acct.h b/sys/netccitt/x25acct.h
new file mode 100644
index 0000000..71f3fd8
--- /dev/null
+++ b/sys/netccitt/x25acct.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Laboratory for Computation Vision and the Computer Science Department
+ * of the University of British Columbia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)x25acct.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Format of X.25 accounting record written
+ * to X25ACCTF whenever a circuit is closed.
+ */
+
+#ifdef waterloo
+#define X25ACCTF "/usr/adm/logs/x25acct"
+#else
+#define X25ACCTF "/usr/adm/x25acct"
+#endif
+
+struct x25acct {
+ time_t x25acct_stime; /* start time */
+#ifdef waterloo
+ u_long x25acct_etime; /* elapsed time (seconds) */
+#else
+ u_short x25acct_etime; /* elapsed time (seconds) */
+#endif
+ short x25acct_uid; /* user id */
+ short x25acct_net; /* network id */
+ u_short x25acct_psize:4, /* packet size */
+ x25acct_addrlen:4, /* x25acct_addr length */
+ x25acct_revcharge:1, /* reverse charging */
+ x25acct_callin:1, /* incoming call */
+ x25acct_unused:6;
+ char x25acct_addr[8]; /* remote DTE address (in bcd) */
+ char x25acct_udata[4]; /* protocol id */
+ long x25acct_txcnt; /* packets transmitted */
+ long x25acct_rxcnt; /* packets received */
+};
diff --git a/sys/netccitt/x25err.h b/sys/netccitt/x25err.h
new file mode 100644
index 0000000..44d5490
--- /dev/null
+++ b/sys/netccitt/x25err.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Laboratory for Computation Vision and the Computer Science Department
+ * of the University of British Columbia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)x25err.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ *
+ * X.25 Reset and Clear errors and diagnostics. These values are
+ * returned in the u_error field of the u structure.
+ *
+ */
+
+#define EXRESET 100 /* Reset: call reset */
+#define EXROUT 101 /* Reset: out of order */
+#define EXRRPE 102 /* Reset: remote procedure error */
+#define EXRLPE 103 /* Reset: local procedure error */
+#define EXRNCG 104 /* Reset: network congestion */
+
+#define EXCLEAR 110 /* Clear: call cleared */
+#define EXCBUSY 111 /* Clear: number busy */
+#define EXCOUT 112 /* Clear: out of order */
+#define EXCRPE 113 /* Clear: remote procedure error */
+#define EXCRRC 114 /* Clear: collect call refused */
+#define EXCINV 115 /* Clear: invalid call */
+#define EXCAB 116 /* Clear: access barred */
+#define EXCLPE 117 /* Clear: local procedure error */
+#define EXCNCG 118 /* Clear: network congestion */
+#define EXCNOB 119 /* Clear: not obtainable */
+
diff --git a/sys/netinet/icmp_var.h b/sys/netinet/icmp_var.h
new file mode 100644
index 0000000..beef16e
--- /dev/null
+++ b/sys/netinet/icmp_var.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)icmp_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Variables related to this implementation
+ * of the internet control message protocol.
+ */
+struct icmpstat {
+/* statistics related to icmp packets generated */
+ u_long icps_error; /* # of calls to icmp_error */
+ u_long icps_oldshort; /* no error 'cuz old ip too short */
+ u_long icps_oldicmp; /* no error 'cuz old was icmp */
+ u_long icps_outhist[ICMP_MAXTYPE + 1];
+/* statistics related to input messages processed */
+ u_long icps_badcode; /* icmp_code out of range */
+ u_long icps_tooshort; /* packet < ICMP_MINLEN */
+ u_long icps_checksum; /* bad checksum */
+ u_long icps_badlen; /* calculated bound mismatch */
+ u_long icps_reflect; /* number of responses */
+ u_long icps_inhist[ICMP_MAXTYPE + 1];
+};
+
+/*
+ * Names for ICMP sysctl objects
+ */
+#define ICMPCTL_MASKREPL 1 /* allow replies to netmask requests */
+#define ICMPCTL_MAXID 2
+
+#define ICMPCTL_NAMES { \
+ { 0, 0 }, \
+ { "maskrepl", CTLTYPE_INT }, \
+}
+
+#ifdef KERNEL
+struct icmpstat icmpstat;
+#endif
diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c
new file mode 100644
index 0000000..41f07c0
--- /dev/null
+++ b/sys/netinet/if_ether.c
@@ -0,0 +1,554 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_ether.c 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Ethernet address resolution protocol.
+ * TODO:
+ * add "inuse/lock" bit (or ref. count) along with valid bit
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+
+#define SIN(s) ((struct sockaddr_in *)s)
+#define SDL(s) ((struct sockaddr_dl *)s)
+#define SRP(s) ((struct sockaddr_inarp *)s)
+
+/*
+ * ARP trailer negotiation. Trailer protocol is not IP specific,
+ * but ARP request/response use IP addresses.
+ */
+#define ETHERTYPE_IPTRAILERS ETHERTYPE_TRAIL
+
+
+/* timer values */
+int arpt_prune = (5*60*1); /* walk list every 5 minutes */
+int arpt_keep = (20*60); /* once resolved, good for 20 more minutes */
+int arpt_down = 20; /* once declared down, don't send for 20 secs */
+#define rt_expire rt_rmx.rmx_expire
+
+static void arprequest __P((struct arpcom *, u_long *, u_long *, u_char *));
+static void arptfree __P((struct llinfo_arp *));
+static void arptimer __P((void *));
+static struct llinfo_arp *arplookup __P((u_long, int, int));
+static void in_arpinput __P((struct mbuf *));
+
+extern struct ifnet loif;
+extern struct timeval time;
+struct llinfo_arp llinfo_arp = {&llinfo_arp, &llinfo_arp};
+struct ifqueue arpintrq = {0, 0, 0, 50};
+int arp_inuse, arp_allocated, arp_intimer;
+int arp_maxtries = 5;
+int useloopback = 1; /* use loopback interface for local traffic */
+int arpinit_done = 0;
+
+/*
+ * Timeout routine. Age arp_tab entries periodically.
+ */
+/* ARGSUSED */
+static void
+arptimer(ignored_arg)
+ void *ignored_arg;
+{
+ int s = splnet();
+ register struct llinfo_arp *la = llinfo_arp.la_next;
+
+ timeout(arptimer, (caddr_t)0, arpt_prune * hz);
+ while (la != &llinfo_arp) {
+ register struct rtentry *rt = la->la_rt;
+ la = la->la_next;
+ if (rt->rt_expire && rt->rt_expire <= time.tv_sec)
+ arptfree(la->la_prev); /* timer has expired, clear */
+ }
+ splx(s);
+}
+
+/*
+ * Parallel to llc_rtrequest.
+ */
+void
+arp_rtrequest(req, rt, sa)
+ int req;
+ register struct rtentry *rt;
+ struct sockaddr *sa;
+{
+ register struct sockaddr *gate = rt->rt_gateway;
+ register struct llinfo_arp *la = (struct llinfo_arp *)rt->rt_llinfo;
+ static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK};
+
+ if (!arpinit_done) {
+ arpinit_done = 1;
+ timeout(arptimer, (caddr_t)0, hz);
+ }
+ if (rt->rt_flags & RTF_GATEWAY)
+ return;
+ switch (req) {
+
+ case RTM_ADD:
+ /*
+ * XXX: If this is a manually added route to interface
+ * such as older version of routed or gated might provide,
+ * restore cloning bit.
+ */
+ if ((rt->rt_flags & RTF_HOST) == 0 &&
+ SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff)
+ rt->rt_flags |= RTF_CLONING;
+ if (rt->rt_flags & RTF_CLONING) {
+ /*
+ * Case 1: This route should come from a route to iface.
+ */
+ rt_setgate(rt, rt_key(rt),
+ (struct sockaddr *)&null_sdl);
+ gate = rt->rt_gateway;
+ SDL(gate)->sdl_type = rt->rt_ifp->if_type;
+ SDL(gate)->sdl_index = rt->rt_ifp->if_index;
+ rt->rt_expire = time.tv_sec;
+ break;
+ }
+ /* Announce a new entry if requested. */
+ if (rt->rt_flags & RTF_ANNOUNCE)
+ arprequest((struct arpcom *)rt->rt_ifp,
+ &SIN(rt_key(rt))->sin_addr.s_addr,
+ &SIN(rt_key(rt))->sin_addr.s_addr,
+ (u_char *)LLADDR(SDL(gate)));
+ /*FALLTHROUGH*/
+ case RTM_RESOLVE:
+ if (gate->sa_family != AF_LINK ||
+ gate->sa_len < sizeof(null_sdl)) {
+ log(LOG_DEBUG, "arp_rtrequest: bad gateway value");
+ break;
+ }
+ SDL(gate)->sdl_type = rt->rt_ifp->if_type;
+ SDL(gate)->sdl_index = rt->rt_ifp->if_index;
+ if (la != 0)
+ break; /* This happens on a route change */
+ /*
+ * Case 2: This route may come from cloning, or a manual route
+ * add with a LL address.
+ */
+ R_Malloc(la, struct llinfo_arp *, sizeof(*la));
+ rt->rt_llinfo = (caddr_t)la;
+ if (la == 0) {
+ log(LOG_DEBUG, "arp_rtrequest: malloc failed\n");
+ break;
+ }
+ arp_inuse++, arp_allocated++;
+ Bzero(la, sizeof(*la));
+ la->la_rt = rt;
+ rt->rt_flags |= RTF_LLINFO;
+ insque(la, &llinfo_arp);
+ if (SIN(rt_key(rt))->sin_addr.s_addr ==
+ (IA_SIN(rt->rt_ifa))->sin_addr.s_addr) {
+ /*
+ * This test used to be
+ * if (loif.if_flags & IFF_UP)
+ * It allowed local traffic to be forced
+ * through the hardware by configuring the loopback down.
+ * However, it causes problems during network configuration
+ * for boards that can't receive packets they send.
+ * It is now necessary to clear "useloopback" and remove
+ * the route to force traffic out to the hardware.
+ */
+ rt->rt_expire = 0;
+ Bcopy(((struct arpcom *)rt->rt_ifp)->ac_enaddr,
+ LLADDR(SDL(gate)), SDL(gate)->sdl_alen = 6);
+ if (useloopback)
+ rt->rt_ifp = &loif;
+
+ }
+ break;
+
+ case RTM_DELETE:
+ if (la == 0)
+ break;
+ arp_inuse--;
+ remque(la);
+ rt->rt_llinfo = 0;
+ rt->rt_flags &= ~RTF_LLINFO;
+ if (la->la_hold)
+ m_freem(la->la_hold);
+ Free((caddr_t)la);
+ }
+}
+
+/*
+ * Broadcast an ARP packet, asking who has addr on interface ac.
+ */
+void
+arpwhohas(ac, addr)
+ register struct arpcom *ac;
+ register struct in_addr *addr;
+{
+ arprequest(ac, &ac->ac_ipaddr.s_addr, &addr->s_addr, ac->ac_enaddr);
+}
+
+/*
+ * Broadcast an ARP request. Caller specifies:
+ * - arp header source ip address
+ * - arp header target ip address
+ * - arp header source ethernet address
+ */
+static void
+arprequest(ac, sip, tip, enaddr)
+ register struct arpcom *ac;
+ register u_long *sip, *tip;
+ register u_char *enaddr;
+{
+ register struct mbuf *m;
+ register struct ether_header *eh;
+ register struct ether_arp *ea;
+ struct sockaddr sa;
+
+ if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL)
+ return;
+ m->m_len = sizeof(*ea);
+ m->m_pkthdr.len = sizeof(*ea);
+ MH_ALIGN(m, sizeof(*ea));
+ ea = mtod(m, struct ether_arp *);
+ eh = (struct ether_header *)sa.sa_data;
+ bzero((caddr_t)ea, sizeof (*ea));
+ bcopy((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost,
+ sizeof(eh->ether_dhost));
+ eh->ether_type = ETHERTYPE_ARP; /* if_output will swap */
+ ea->arp_hrd = htons(ARPHRD_ETHER);
+ ea->arp_pro = htons(ETHERTYPE_IP);
+ ea->arp_hln = sizeof(ea->arp_sha); /* hardware address length */
+ ea->arp_pln = sizeof(ea->arp_spa); /* protocol address length */
+ ea->arp_op = htons(ARPOP_REQUEST);
+ bcopy((caddr_t)enaddr, (caddr_t)ea->arp_sha, sizeof(ea->arp_sha));
+ bcopy((caddr_t)sip, (caddr_t)ea->arp_spa, sizeof(ea->arp_spa));
+ bcopy((caddr_t)tip, (caddr_t)ea->arp_tpa, sizeof(ea->arp_tpa));
+ sa.sa_family = AF_UNSPEC;
+ sa.sa_len = sizeof(sa);
+ (*ac->ac_if.if_output)(&ac->ac_if, m, &sa, (struct rtentry *)0);
+}
+
+/*
+ * Resolve an IP address into an ethernet address. If success,
+ * desten is filled in. If there is no entry in arptab,
+ * set one up and broadcast a request for the IP address.
+ * Hold onto this mbuf and resend it once the address
+ * is finally resolved. A return value of 1 indicates
+ * that desten has been filled in and the packet should be sent
+ * normally; a 0 return indicates that the packet has been
+ * taken over here, either now or for later transmission.
+ */
+int
+arpresolve(ac, rt, m, dst, desten)
+ register struct arpcom *ac;
+ register struct rtentry *rt;
+ struct mbuf *m;
+ register struct sockaddr *dst;
+ register u_char *desten;
+{
+ register struct llinfo_arp *la;
+ struct sockaddr_dl *sdl;
+
+ if (m->m_flags & M_BCAST) { /* broadcast */
+ bcopy((caddr_t)etherbroadcastaddr, (caddr_t)desten,
+ sizeof(etherbroadcastaddr));
+ return (1);
+ }
+ if (m->m_flags & M_MCAST) { /* multicast */
+ ETHER_MAP_IP_MULTICAST(&SIN(dst)->sin_addr, desten);
+ return(1);
+ }
+ if (rt)
+ la = (struct llinfo_arp *)rt->rt_llinfo;
+ else {
+ if (la = arplookup(SIN(dst)->sin_addr.s_addr, 1, 0))
+ rt = la->la_rt;
+ }
+ if (la == 0 || rt == 0) {
+ log(LOG_DEBUG, "arpresolve: can't allocate llinfo");
+ m_freem(m);
+ return (0);
+ }
+ sdl = SDL(rt->rt_gateway);
+ /*
+ * Check the address family and length is valid, the address
+ * is resolved; otherwise, try to resolve.
+ */
+ if ((rt->rt_expire == 0 || rt->rt_expire > time.tv_sec) &&
+ sdl->sdl_family == AF_LINK && sdl->sdl_alen != 0) {
+ bcopy(LLADDR(sdl), desten, sdl->sdl_alen);
+ return 1;
+ }
+ /*
+ * There is an arptab entry, but no ethernet address
+ * response yet. Replace the held mbuf with this
+ * latest one.
+ */
+ if (la->la_hold)
+ m_freem(la->la_hold);
+ la->la_hold = m;
+ if (rt->rt_expire) {
+ rt->rt_flags &= ~RTF_REJECT;
+ if (la->la_asked == 0 || rt->rt_expire != time.tv_sec) {
+ rt->rt_expire = time.tv_sec;
+ if (la->la_asked++ < arp_maxtries)
+ arpwhohas(ac, &(SIN(dst)->sin_addr));
+ else {
+ rt->rt_flags |= RTF_REJECT;
+ rt->rt_expire += arpt_down;
+ la->la_asked = 0;
+ }
+
+ }
+ }
+ return (0);
+}
+
+/*
+ * Common length and type checks are done here,
+ * then the protocol-specific routine is called.
+ */
+void
+arpintr()
+{
+ register struct mbuf *m;
+ register struct arphdr *ar;
+ int s;
+
+ while (arpintrq.ifq_head) {
+ s = splimp();
+ IF_DEQUEUE(&arpintrq, m);
+ splx(s);
+ if (m == 0 || (m->m_flags & M_PKTHDR) == 0)
+ panic("arpintr");
+ if (m->m_len >= sizeof(struct arphdr) &&
+ (ar = mtod(m, struct arphdr *)) &&
+ ntohs(ar->ar_hrd) == ARPHRD_ETHER &&
+ m->m_len >=
+ sizeof(struct arphdr) + 2 * ar->ar_hln + 2 * ar->ar_pln)
+
+ switch (ntohs(ar->ar_pro)) {
+
+ case ETHERTYPE_IP:
+ case ETHERTYPE_IPTRAILERS:
+ in_arpinput(m);
+ continue;
+ }
+ m_freem(m);
+ }
+}
+
+/*
+ * ARP for Internet protocols on 10 Mb/s Ethernet.
+ * Algorithm is that given in RFC 826.
+ * In addition, a sanity check is performed on the sender
+ * protocol address, to catch impersonators.
+ * We no longer handle negotiations for use of trailer protocol:
+ * Formerly, ARP replied for protocol type ETHERTYPE_TRAIL sent
+ * along with IP replies if we wanted trailers sent to us,
+ * and also sent them in response to IP replies.
+ * This allowed either end to announce the desire to receive
+ * trailer packets.
+ * We no longer reply to requests for ETHERTYPE_TRAIL protocol either,
+ * but formerly didn't normally send requests.
+ */
+static void
+in_arpinput(m)
+ struct mbuf *m;
+{
+ register struct ether_arp *ea;
+ register struct arpcom *ac = (struct arpcom *)m->m_pkthdr.rcvif;
+ struct ether_header *eh;
+ register struct llinfo_arp *la = 0;
+ register struct rtentry *rt;
+ struct in_ifaddr *ia, *maybe_ia = 0;
+ struct sockaddr_dl *sdl;
+ struct sockaddr sa;
+ struct in_addr isaddr, itaddr, myaddr;
+ int op;
+
+ ea = mtod(m, struct ether_arp *);
+ op = ntohs(ea->arp_op);
+ bcopy((caddr_t)ea->arp_spa, (caddr_t)&isaddr, sizeof (isaddr));
+ bcopy((caddr_t)ea->arp_tpa, (caddr_t)&itaddr, sizeof (itaddr));
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == &ac->ac_if) {
+ maybe_ia = ia;
+ if ((itaddr.s_addr == ia->ia_addr.sin_addr.s_addr) ||
+ (isaddr.s_addr == ia->ia_addr.sin_addr.s_addr))
+ break;
+ }
+ if (maybe_ia == 0)
+ goto out;
+ myaddr = ia ? ia->ia_addr.sin_addr : maybe_ia->ia_addr.sin_addr;
+ if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)ac->ac_enaddr,
+ sizeof (ea->arp_sha)))
+ goto out; /* it's from me, ignore it. */
+ if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)etherbroadcastaddr,
+ sizeof (ea->arp_sha))) {
+ log(LOG_ERR,
+ "arp: ether address is broadcast for IP address %x!\n",
+ ntohl(isaddr.s_addr));
+ goto out;
+ }
+ if (isaddr.s_addr == myaddr.s_addr) {
+ log(LOG_ERR,
+ "duplicate IP address %x!! sent from ethernet address: %s\n",
+ ntohl(isaddr.s_addr), ether_sprintf(ea->arp_sha));
+ itaddr = myaddr;
+ goto reply;
+ }
+ la = arplookup(isaddr.s_addr, itaddr.s_addr == myaddr.s_addr, 0);
+ if (la && (rt = la->la_rt) && (sdl = SDL(rt->rt_gateway))) {
+ if (sdl->sdl_alen &&
+ bcmp((caddr_t)ea->arp_sha, LLADDR(sdl), sdl->sdl_alen))
+ log(LOG_INFO, "arp info overwritten for %x by %s\n",
+ isaddr.s_addr, ether_sprintf(ea->arp_sha));
+ bcopy((caddr_t)ea->arp_sha, LLADDR(sdl),
+ sdl->sdl_alen = sizeof(ea->arp_sha));
+ if (rt->rt_expire)
+ rt->rt_expire = time.tv_sec + arpt_keep;
+ rt->rt_flags &= ~RTF_REJECT;
+ la->la_asked = 0;
+ if (la->la_hold) {
+ (*ac->ac_if.if_output)(&ac->ac_if, la->la_hold,
+ rt_key(rt), rt);
+ la->la_hold = 0;
+ }
+ }
+reply:
+ if (op != ARPOP_REQUEST) {
+ out:
+ m_freem(m);
+ return;
+ }
+ if (itaddr.s_addr == myaddr.s_addr) {
+ /* I am the target */
+ bcopy((caddr_t)ea->arp_sha, (caddr_t)ea->arp_tha,
+ sizeof(ea->arp_sha));
+ bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha,
+ sizeof(ea->arp_sha));
+ } else {
+ la = arplookup(itaddr.s_addr, 0, SIN_PROXY);
+ if (la == NULL)
+ goto out;
+ rt = la->la_rt;
+ bcopy((caddr_t)ea->arp_sha, (caddr_t)ea->arp_tha,
+ sizeof(ea->arp_sha));
+ sdl = SDL(rt->rt_gateway);
+ bcopy(LLADDR(sdl), (caddr_t)ea->arp_sha, sizeof(ea->arp_sha));
+ }
+
+ bcopy((caddr_t)ea->arp_spa, (caddr_t)ea->arp_tpa, sizeof(ea->arp_spa));
+ bcopy((caddr_t)&itaddr, (caddr_t)ea->arp_spa, sizeof(ea->arp_spa));
+ ea->arp_op = htons(ARPOP_REPLY);
+ ea->arp_pro = htons(ETHERTYPE_IP); /* let's be sure! */
+ eh = (struct ether_header *)sa.sa_data;
+ bcopy((caddr_t)ea->arp_tha, (caddr_t)eh->ether_dhost,
+ sizeof(eh->ether_dhost));
+ eh->ether_type = ETHERTYPE_ARP;
+ sa.sa_family = AF_UNSPEC;
+ sa.sa_len = sizeof(sa);
+ (*ac->ac_if.if_output)(&ac->ac_if, m, &sa, (struct rtentry *)0);
+ return;
+}
+
+/*
+ * Free an arp entry.
+ */
+static void
+arptfree(la)
+ register struct llinfo_arp *la;
+{
+ register struct rtentry *rt = la->la_rt;
+ register struct sockaddr_dl *sdl;
+ if (rt == 0)
+ panic("arptfree");
+ if (rt->rt_refcnt > 0 && (sdl = SDL(rt->rt_gateway)) &&
+ sdl->sdl_family == AF_LINK) {
+ sdl->sdl_alen = 0;
+ la->la_asked = 0;
+ rt->rt_flags &= ~RTF_REJECT;
+ return;
+ }
+ rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt),
+ 0, (struct rtentry **)0);
+}
+/*
+ * Lookup or enter a new address in arptab.
+ */
+static struct llinfo_arp *
+arplookup(addr, create, proxy)
+ u_long addr;
+ int create, proxy;
+{
+ register struct rtentry *rt;
+ static struct sockaddr_inarp sin = {sizeof(sin), AF_INET };
+
+ sin.sin_addr.s_addr = addr;
+ sin.sin_other = proxy ? SIN_PROXY : 0;
+ rt = rtalloc1((struct sockaddr *)&sin, create);
+ if (rt == 0)
+ return (0);
+ rt->rt_refcnt--;
+ if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 ||
+ rt->rt_gateway->sa_family != AF_LINK) {
+ if (create)
+ log(LOG_DEBUG, "arptnew failed on %x\n", ntohl(addr));
+ return (0);
+ }
+ return ((struct llinfo_arp *)rt->rt_llinfo);
+}
+
+int
+arpioctl(cmd, data)
+ int cmd;
+ caddr_t data;
+{
+ return (EOPNOTSUPP);
+}
diff --git a/sys/netinet/if_ether.h b/sys/netinet/if_ether.h
new file mode 100644
index 0000000..6b4def0
--- /dev/null
+++ b/sys/netinet/if_ether.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_ether.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Structure of a 10Mb/s Ethernet header.
+ */
+struct ether_header {
+ u_char ether_dhost[6];
+ u_char ether_shost[6];
+ u_short ether_type;
+};
+
+#define ETHERTYPE_PUP 0x0200 /* PUP protocol */
+#define ETHERTYPE_IP 0x0800 /* IP protocol */
+#define ETHERTYPE_ARP 0x0806 /* Addr. resolution protocol */
+#define ETHERTYPE_REVARP 0x8035 /* reverse Addr. resolution protocol */
+
+/*
+ * The ETHERTYPE_NTRAILER packet types starting at ETHERTYPE_TRAIL have
+ * (type-ETHERTYPE_TRAIL)*512 bytes of data followed
+ * by an ETHER type (as given above) and then the (variable-length) header.
+ */
+#define ETHERTYPE_TRAIL 0x1000 /* Trailer packet */
+#define ETHERTYPE_NTRAILER 16
+
+#define ETHERMTU 1500
+#define ETHERMIN (60-14)
+
+#ifdef KERNEL
+/*
+ * Macro to map an IP multicast address to an Ethernet multicast address.
+ * The high-order 25 bits of the Ethernet address are statically assigned,
+ * and the low-order 23 bits are taken from the low end of the IP address.
+ */
+#define ETHER_MAP_IP_MULTICAST(ipaddr, enaddr) \
+ /* struct in_addr *ipaddr; */ \
+ /* u_char enaddr[6]; */ \
+{ \
+ (enaddr)[0] = 0x01; \
+ (enaddr)[1] = 0x00; \
+ (enaddr)[2] = 0x5e; \
+ (enaddr)[3] = ((u_char *)ipaddr)[1] & 0x7f; \
+ (enaddr)[4] = ((u_char *)ipaddr)[2]; \
+ (enaddr)[5] = ((u_char *)ipaddr)[3]; \
+}
+#endif
+
+/*
+ * Ethernet Address Resolution Protocol.
+ *
+ * See RFC 826 for protocol description. Structure below is adapted
+ * to resolving internet addresses. Field names used correspond to
+ * RFC 826.
+ */
+struct ether_arp {
+ struct arphdr ea_hdr; /* fixed-size header */
+ u_char arp_sha[6]; /* sender hardware address */
+ u_char arp_spa[4]; /* sender protocol address */
+ u_char arp_tha[6]; /* target hardware address */
+ u_char arp_tpa[4]; /* target protocol address */
+};
+#define arp_hrd ea_hdr.ar_hrd
+#define arp_pro ea_hdr.ar_pro
+#define arp_hln ea_hdr.ar_hln
+#define arp_pln ea_hdr.ar_pln
+#define arp_op ea_hdr.ar_op
+
+
+/*
+ * Structure shared between the ethernet driver modules and
+ * the address resolution code. For example, each ec_softc or il_softc
+ * begins with this structure.
+ */
+struct arpcom {
+ struct ifnet ac_if; /* network-visible interface */
+ u_char ac_enaddr[6]; /* ethernet hardware address */
+ struct in_addr ac_ipaddr; /* copy of ip address- XXX */
+ struct ether_multi *ac_multiaddrs; /* list of ether multicast addrs */
+ int ac_multicnt; /* length of ac_multiaddrs list */
+};
+
+struct llinfo_arp {
+ struct llinfo_arp *la_next;
+ struct llinfo_arp *la_prev;
+ struct rtentry *la_rt;
+ struct mbuf *la_hold; /* last packet until resolved/timeout */
+ long la_asked; /* last time we QUERIED for this addr */
+#define la_timer la_rt->rt_rmx.rmx_expire /* deletion time in seconds */
+};
+
+struct sockaddr_inarp {
+ u_char sin_len;
+ u_char sin_family;
+ u_short sin_port;
+ struct in_addr sin_addr;
+ struct in_addr sin_srcaddr;
+ u_short sin_tos;
+ u_short sin_other;
+#define SIN_PROXY 1
+};
+/*
+ * IP and ethernet specific routing flags
+ */
+#define RTF_USETRAILERS RTF_PROTO1 /* use trailers */
+#define RTF_ANNOUNCE RTF_PROTO2 /* announce new arp entry */
+
+#ifdef KERNEL
+u_char etherbroadcastaddr[6];
+u_char ether_ipmulticast_min[6];
+u_char ether_ipmulticast_max[6];
+struct ifqueue arpintrq;
+
+struct llinfo_arp *arptnew __P((struct in_addr *));
+struct llinfo_arp llinfo_arp; /* head of the llinfo queue */
+
+void arpwhohas __P((struct arpcom *, struct in_addr *));
+void arpintr __P((void));
+int arpresolve __P((struct arpcom *,
+ struct rtentry *, struct mbuf *, struct sockaddr *, u_char *));
+void arp_rtrequest __P((int, struct rtentry *, struct sockaddr *));
+void arpwhohas __P((struct arpcom *, struct in_addr *));
+
+int ether_addmulti __P((struct ifreq *, struct arpcom *));
+int ether_delmulti __P((struct ifreq *, struct arpcom *));
+
+/*
+ * Ethernet multicast address structure. There is one of these for each
+ * multicast address or range of multicast addresses that we are supposed
+ * to listen to on a particular interface. They are kept in a linked list,
+ * rooted in the interface's arpcom structure. (This really has nothing to
+ * do with ARP, or with the Internet address family, but this appears to be
+ * the minimally-disrupting place to put it.)
+ */
+struct ether_multi {
+ u_char enm_addrlo[6]; /* low or only address of range */
+ u_char enm_addrhi[6]; /* high or only address of range */
+ struct arpcom *enm_ac; /* back pointer to arpcom */
+ u_int enm_refcount; /* no. claims to this addr/range */
+ struct ether_multi *enm_next; /* ptr to next ether_multi */
+};
+
+/*
+ * Structure used by macros below to remember position when stepping through
+ * all of the ether_multi records.
+ */
+struct ether_multistep {
+ struct ether_multi *e_enm;
+};
+
+/*
+ * Macro for looking up the ether_multi record for a given range of Ethernet
+ * multicast addresses connected to a given arpcom structure. If no matching
+ * record is found, "enm" returns NULL.
+ */
+#define ETHER_LOOKUP_MULTI(addrlo, addrhi, ac, enm) \
+ /* u_char addrlo[6]; */ \
+ /* u_char addrhi[6]; */ \
+ /* struct arpcom *ac; */ \
+ /* struct ether_multi *enm; */ \
+{ \
+ for ((enm) = (ac)->ac_multiaddrs; \
+ (enm) != NULL && \
+ (bcmp((enm)->enm_addrlo, (addrlo), 6) != 0 || \
+ bcmp((enm)->enm_addrhi, (addrhi), 6) != 0); \
+ (enm) = (enm)->enm_next); \
+}
+
+/*
+ * Macro to step through all of the ether_multi records, one at a time.
+ * The current position is remembered in "step", which the caller must
+ * provide. ETHER_FIRST_MULTI(), below, must be called to initialize "step"
+ * and get the first record. Both macros return a NULL "enm" when there
+ * are no remaining records.
+ */
+#define ETHER_NEXT_MULTI(step, enm) \
+ /* struct ether_multistep step; */ \
+ /* struct ether_multi *enm; */ \
+{ \
+ if (((enm) = (step).e_enm) != NULL) \
+ (step).e_enm = (enm)->enm_next; \
+}
+
+#define ETHER_FIRST_MULTI(step, ac, enm) \
+ /* struct ether_multistep step; */ \
+ /* struct arpcom *ac; */ \
+ /* struct ether_multi *enm; */ \
+{ \
+ (step).e_enm = (ac)->ac_multiaddrs; \
+ ETHER_NEXT_MULTI((step), (enm)); \
+}
+
+#endif
diff --git a/sys/netinet/igmp.c b/sys/netinet/igmp.c
new file mode 100644
index 0000000..cc240eb
--- /dev/null
+++ b/sys/netinet/igmp.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 1988 Stephen Deering.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)igmp.c 8.1 (Berkeley) 7/19/93
+ */
+
+/* Internet Group Management Protocol (IGMP) routines. */
+
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/igmp.h>
+#include <netinet/igmp_var.h>
+
+extern struct ifnet loif;
+
+static int igmp_timers_are_running = 0;
+static u_long igmp_all_hosts_group;
+
+static void igmp_sendreport __P((struct in_multi *));
+
+void
+igmp_init()
+{
+ /*
+ * To avoid byte-swapping the same value over and over again.
+ */
+ igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP);
+}
+
+void
+igmp_input(m, iphlen)
+ register struct mbuf *m;
+ register int iphlen;
+{
+ register struct igmp *igmp;
+ register struct ip *ip;
+ register int igmplen;
+ register struct ifnet *ifp = m->m_pkthdr.rcvif;
+ register int minlen;
+ register struct in_multi *inm;
+ register struct in_ifaddr *ia;
+ struct in_multistep step;
+
+ ++igmpstat.igps_rcv_total;
+
+ ip = mtod(m, struct ip *);
+ igmplen = ip->ip_len;
+
+ /*
+ * Validate lengths
+ */
+ if (igmplen < IGMP_MINLEN) {
+ ++igmpstat.igps_rcv_tooshort;
+ m_freem(m);
+ return;
+ }
+ minlen = iphlen + IGMP_MINLEN;
+ if ((m->m_flags & M_EXT || m->m_len < minlen) &&
+ (m = m_pullup(m, minlen)) == 0) {
+ ++igmpstat.igps_rcv_tooshort;
+ return;
+ }
+
+ /*
+ * Validate checksum
+ */
+ m->m_data += iphlen;
+ m->m_len -= iphlen;
+ igmp = mtod(m, struct igmp *);
+ if (in_cksum(m, igmplen)) {
+ ++igmpstat.igps_rcv_badsum;
+ m_freem(m);
+ return;
+ }
+ m->m_data -= iphlen;
+ m->m_len += iphlen;
+ ip = mtod(m, struct ip *);
+
+ switch (igmp->igmp_type) {
+
+ case IGMP_HOST_MEMBERSHIP_QUERY:
+ ++igmpstat.igps_rcv_queries;
+
+ if (ifp == &loif)
+ break;
+
+ if (ip->ip_dst.s_addr != igmp_all_hosts_group) {
+ ++igmpstat.igps_rcv_badqueries;
+ m_freem(m);
+ return;
+ }
+
+ /*
+ * Start the timers in all of our membership records for
+ * the interface on which the query arrived, except those
+ * that are already running and those that belong to the
+ * "all-hosts" group.
+ */
+ IN_FIRST_MULTI(step, inm);
+ while (inm != NULL) {
+ if (inm->inm_ifp == ifp && inm->inm_timer == 0 &&
+ inm->inm_addr.s_addr != igmp_all_hosts_group) {
+ inm->inm_timer =
+ IGMP_RANDOM_DELAY(inm->inm_addr);
+ igmp_timers_are_running = 1;
+ }
+ IN_NEXT_MULTI(step, inm);
+ }
+
+ break;
+
+ case IGMP_HOST_MEMBERSHIP_REPORT:
+ ++igmpstat.igps_rcv_reports;
+
+ if (ifp == &loif)
+ break;
+
+ if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) ||
+ igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
+ ++igmpstat.igps_rcv_badreports;
+ m_freem(m);
+ return;
+ }
+
+ /*
+ * KLUDGE: if the IP source address of the report has an
+ * unspecified (i.e., zero) subnet number, as is allowed for
+ * a booting host, replace it with the correct subnet number
+ * so that a process-level multicast routing demon can
+ * determine which subnet it arrived from. This is necessary
+ * to compensate for the lack of any way for a process to
+ * determine the arrival interface of an incoming packet.
+ */
+ if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) {
+ IFP_TO_IA(ifp, ia);
+ if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet);
+ }
+
+ /*
+ * If we belong to the group being reported, stop
+ * our timer for that group.
+ */
+ IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
+ if (inm != NULL) {
+ inm->inm_timer = 0;
+ ++igmpstat.igps_rcv_ourreports;
+ }
+
+ break;
+ }
+
+ /*
+ * Pass all valid IGMP packets up to any process(es) listening
+ * on a raw IGMP socket.
+ */
+ rip_input(m);
+}
+
+void
+igmp_joingroup(inm)
+ struct in_multi *inm;
+{
+ register int s = splnet();
+
+ if (inm->inm_addr.s_addr == igmp_all_hosts_group ||
+ inm->inm_ifp == &loif)
+ inm->inm_timer = 0;
+ else {
+ igmp_sendreport(inm);
+ inm->inm_timer = IGMP_RANDOM_DELAY(inm->inm_addr);
+ igmp_timers_are_running = 1;
+ }
+ splx(s);
+}
+
+void
+igmp_leavegroup(inm)
+ struct in_multi *inm;
+{
+ /*
+ * No action required on leaving a group.
+ */
+}
+
+void
+igmp_fasttimo()
+{
+ register struct in_multi *inm;
+ register int s;
+ struct in_multistep step;
+
+ /*
+ * Quick check to see if any work needs to be done, in order
+ * to minimize the overhead of fasttimo processing.
+ */
+ if (!igmp_timers_are_running)
+ return;
+
+ s = splnet();
+ igmp_timers_are_running = 0;
+ IN_FIRST_MULTI(step, inm);
+ while (inm != NULL) {
+ if (inm->inm_timer == 0) {
+ /* do nothing */
+ } else if (--inm->inm_timer == 0) {
+ igmp_sendreport(inm);
+ } else {
+ igmp_timers_are_running = 1;
+ }
+ IN_NEXT_MULTI(step, inm);
+ }
+ splx(s);
+}
+
+static void
+igmp_sendreport(inm)
+ register struct in_multi *inm;
+{
+ register struct mbuf *m;
+ register struct igmp *igmp;
+ register struct ip *ip;
+ register struct ip_moptions *imo;
+ struct ip_moptions simo;
+
+ MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (m == NULL)
+ return;
+ /*
+ * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
+ * is smaller than mbuf size returned by MGETHDR.
+ */
+ m->m_data += max_linkhdr;
+ m->m_len = sizeof(struct ip) + IGMP_MINLEN;
+ m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
+
+ ip = mtod(m, struct ip *);
+ ip->ip_tos = 0;
+ ip->ip_len = sizeof(struct ip) + IGMP_MINLEN;
+ ip->ip_off = 0;
+ ip->ip_p = IPPROTO_IGMP;
+ ip->ip_src.s_addr = INADDR_ANY;
+ ip->ip_dst = inm->inm_addr;
+
+ igmp = (struct igmp *)(ip + 1);
+ igmp->igmp_type = IGMP_HOST_MEMBERSHIP_REPORT;
+ igmp->igmp_code = 0;
+ igmp->igmp_group = inm->inm_addr;
+ igmp->igmp_cksum = 0;
+ igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
+
+ imo = &simo;
+ bzero((caddr_t)imo, sizeof(*imo));
+ imo->imo_multicast_ifp = inm->inm_ifp;
+ imo->imo_multicast_ttl = 1;
+ /*
+ * Request loopback of the report if we are acting as a multicast
+ * router, so that the process-level routing demon can hear it.
+ */
+#ifdef MROUTING
+ {
+ extern struct socket *ip_mrouter;
+ imo->imo_multicast_loop = (ip_mrouter != NULL);
+ }
+#endif
+ ip_output(m, NULL, NULL, 0, imo);
+
+ ++igmpstat.igps_snd_reports;
+}
diff --git a/sys/netinet/igmp.h b/sys/netinet/igmp.h
new file mode 100644
index 0000000..29ce21d
--- /dev/null
+++ b/sys/netinet/igmp.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1988 Stephen Deering.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)igmp.h 8.1 (Berkeley) 6/10/93
+ */
+
+/* Internet Group Management Protocol (IGMP) definitions. */
+
+/*
+ * IGMP packet format.
+ */
+struct igmp {
+ u_char igmp_type; /* version & type of IGMP message */
+ u_char igmp_code; /* unused, should be zero */
+ u_short igmp_cksum; /* IP-style checksum */
+ struct in_addr igmp_group; /* group address being reported */
+}; /* (zero for queries) */
+
+#define IGMP_MINLEN 8
+
+#define IGMP_HOST_MEMBERSHIP_QUERY 0x11 /* message types, incl. version */
+#define IGMP_HOST_MEMBERSHIP_REPORT 0x12
+#define IGMP_DVMRP 0x13 /* for experimental multicast */
+ /* routing protocol */
+
+#define IGMP_MAX_HOST_REPORT_DELAY 10 /* max delay for response to */
diff --git a/sys/netinet/igmp_var.h b/sys/netinet/igmp_var.h
new file mode 100644
index 0000000..ff70f70
--- /dev/null
+++ b/sys/netinet/igmp_var.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 1988 Stephen Deering.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)igmp_var.h 8.1 (Berkeley) 7/19/93
+ */
+
+/*
+ * Internet Group Management Protocol (IGMP),
+ * implementation-specific definitions.
+ *
+ * Written by Steve Deering, Stanford, May 1988.
+ *
+ * MULTICAST 1.1
+ */
+
+struct igmpstat {
+ u_long igps_rcv_total; /* total IGMP messages received */
+ u_long igps_rcv_tooshort; /* received with too few bytes */
+ u_long igps_rcv_badsum; /* received with bad checksum */
+ u_long igps_rcv_queries; /* received membership queries */
+ u_long igps_rcv_badqueries; /* received invalid queries */
+ u_long igps_rcv_reports; /* received membership reports */
+ u_long igps_rcv_badreports; /* received invalid reports */
+ u_long igps_rcv_ourreports; /* received reports for our groups */
+ u_long igps_snd_reports; /* sent membership reports */
+};
+
+#ifdef KERNEL
+struct igmpstat igmpstat;
+
+/*
+ * Macro to compute a random timer value between 1 and (IGMP_MAX_REPORTING_
+ * DELAY * countdown frequency). We generate a "random" number by adding
+ * the total number of IP packets received, our primary IP address, and the
+ * multicast address being timed-out. The 4.3 random() routine really
+ * ought to be available in the kernel!
+ */
+#define IGMP_RANDOM_DELAY(multiaddr) \
+ /* struct in_addr multiaddr; */ \
+ ( (ipstat.ips_total + \
+ ntohl(IA_SIN(in_ifaddr)->sin_addr.s_addr) + \
+ ntohl((multiaddr).s_addr) \
+ ) \
+ % (IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ) + 1 \
+ )
+
+void igmp_init __P(());
+void igmp_input __P((struct mbuf *, int));
+void igmp_joingroup __P((struct in_multi *));
+void igmp_leavegroup __P((struct in_multi *));
+void igmp_fasttimo __P(());
+#endif
diff --git a/sys/netinet/in.c b/sys/netinet/in.c
new file mode 100644
index 0000000..bcf34e8
--- /dev/null
+++ b/sys/netinet/in.c
@@ -0,0 +1,628 @@
+/*
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in.c 8.2 (Berkeley) 11/15/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+
+#ifdef INET
+/*
+ * Return the network number from an internet address.
+ */
+u_long
+in_netof(in)
+ struct in_addr in;
+{
+ register u_long i = ntohl(in.s_addr);
+ register u_long net;
+ register struct in_ifaddr *ia;
+
+ if (IN_CLASSA(i))
+ net = i & IN_CLASSA_NET;
+ else if (IN_CLASSB(i))
+ net = i & IN_CLASSB_NET;
+ else if (IN_CLASSC(i))
+ net = i & IN_CLASSC_NET;
+ else if (IN_CLASSD(i))
+ net = i & IN_CLASSD_NET;
+ else
+ return (0);
+
+ /*
+ * Check whether network is a subnet;
+ * if so, return subnet number.
+ */
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (net == ia->ia_net)
+ return (i & ia->ia_subnetmask);
+ return (net);
+}
+
+#ifndef SUBNETSARELOCAL
+#define SUBNETSARELOCAL 1
+#endif
+int subnetsarelocal = SUBNETSARELOCAL;
+/*
+ * Return 1 if an internet address is for a ``local'' host
+ * (one to which we have a connection). If subnetsarelocal
+ * is true, this includes other subnets of the local net.
+ * Otherwise, it includes only the directly-connected (sub)nets.
+ */
+int
+in_localaddr(in)
+ struct in_addr in;
+{
+ register u_long i = ntohl(in.s_addr);
+ register struct in_ifaddr *ia;
+
+ if (subnetsarelocal) {
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if ((i & ia->ia_netmask) == ia->ia_net)
+ return (1);
+ } else {
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if ((i & ia->ia_subnetmask) == ia->ia_subnet)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Determine whether an IP address is in a reserved set of addresses
+ * that may not be forwarded, or whether datagrams to that destination
+ * may be forwarded.
+ */
+int
+in_canforward(in)
+ struct in_addr in;
+{
+ register u_long i = ntohl(in.s_addr);
+ register u_long net;
+
+ if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i))
+ return (0);
+ if (IN_CLASSA(i)) {
+ net = i & IN_CLASSA_NET;
+ if (net == 0 || net == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT))
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Trim a mask in a sockaddr
+ */
+void
+in_socktrim(ap)
+struct sockaddr_in *ap;
+{
+ register char *cplim = (char *) &ap->sin_addr;
+ register char *cp = (char *) (&ap->sin_addr + 1);
+
+ ap->sin_len = 0;
+ while (--cp > cplim)
+ if (*cp) {
+ (ap)->sin_len = cp - (char *) (ap) + 1;
+ break;
+ }
+}
+
+int in_interfaces; /* number of external internet interfaces */
+extern struct ifnet loif;
+
+/*
+ * Generic internet control operations (ioctl's).
+ * Ifp is 0 if not an interface-specific ioctl.
+ */
+/* ARGSUSED */
+int
+in_control(so, cmd, data, ifp)
+ struct socket *so;
+ int cmd;
+ caddr_t data;
+ register struct ifnet *ifp;
+{
+ register struct ifreq *ifr = (struct ifreq *)data;
+ register struct in_ifaddr *ia = 0;
+ register struct ifaddr *ifa;
+ struct in_ifaddr *oia;
+ struct in_aliasreq *ifra = (struct in_aliasreq *)data;
+ struct sockaddr_in oldaddr;
+ int error, hostIsNew, maskIsNew;
+ u_long i;
+
+ /*
+ * Find address for this interface, if it exists.
+ */
+ if (ifp)
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+
+ switch (cmd) {
+
+ case SIOCAIFADDR:
+ case SIOCDIFADDR:
+ if (ifra->ifra_addr.sin_family == AF_INET)
+ for (oia = ia; ia; ia = ia->ia_next) {
+ if (ia->ia_ifp == ifp &&
+ ia->ia_addr.sin_addr.s_addr ==
+ ifra->ifra_addr.sin_addr.s_addr)
+ break;
+ }
+ if (cmd == SIOCDIFADDR && ia == 0)
+ return (EADDRNOTAVAIL);
+ /* FALLTHROUGH */
+ case SIOCSIFADDR:
+ case SIOCSIFNETMASK:
+ case SIOCSIFDSTADDR:
+ if ((so->so_state & SS_PRIV) == 0)
+ return (EPERM);
+
+ if (ifp == 0)
+ panic("in_control");
+ if (ia == (struct in_ifaddr *)0) {
+ oia = (struct in_ifaddr *)
+ malloc(sizeof *oia, M_IFADDR, M_WAITOK);
+ if (oia == (struct in_ifaddr *)NULL)
+ return (ENOBUFS);
+ bzero((caddr_t)oia, sizeof *oia);
+ if (ia = in_ifaddr) {
+ for ( ; ia->ia_next; ia = ia->ia_next)
+ continue;
+ ia->ia_next = oia;
+ } else
+ in_ifaddr = oia;
+ ia = oia;
+ if (ifa = ifp->if_addrlist) {
+ for ( ; ifa->ifa_next; ifa = ifa->ifa_next)
+ continue;
+ ifa->ifa_next = (struct ifaddr *) ia;
+ } else
+ ifp->if_addrlist = (struct ifaddr *) ia;
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ ia->ia_ifa.ifa_dstaddr
+ = (struct sockaddr *)&ia->ia_dstaddr;
+ ia->ia_ifa.ifa_netmask
+ = (struct sockaddr *)&ia->ia_sockmask;
+ ia->ia_sockmask.sin_len = 8;
+ if (ifp->if_flags & IFF_BROADCAST) {
+ ia->ia_broadaddr.sin_len = sizeof(ia->ia_addr);
+ ia->ia_broadaddr.sin_family = AF_INET;
+ }
+ ia->ia_ifp = ifp;
+ if (ifp != &loif)
+ in_interfaces++;
+ }
+ break;
+
+ case SIOCSIFBRDADDR:
+ if ((so->so_state & SS_PRIV) == 0)
+ return (EPERM);
+ /* FALLTHROUGH */
+
+ case SIOCGIFADDR:
+ case SIOCGIFNETMASK:
+ case SIOCGIFDSTADDR:
+ case SIOCGIFBRDADDR:
+ if (ia == (struct in_ifaddr *)0)
+ return (EADDRNOTAVAIL);
+ break;
+ }
+ switch (cmd) {
+
+ case SIOCGIFADDR:
+ *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr;
+ break;
+
+ case SIOCGIFBRDADDR:
+ if ((ifp->if_flags & IFF_BROADCAST) == 0)
+ return (EINVAL);
+ *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr;
+ break;
+
+ case SIOCGIFDSTADDR:
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
+ return (EINVAL);
+ *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr;
+ break;
+
+ case SIOCGIFNETMASK:
+ *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask;
+ break;
+
+ case SIOCSIFDSTADDR:
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
+ return (EINVAL);
+ oldaddr = ia->ia_dstaddr;
+ ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr;
+ if (ifp->if_ioctl && (error = (*ifp->if_ioctl)
+ (ifp, SIOCSIFDSTADDR, (caddr_t)ia))) {
+ ia->ia_dstaddr = oldaddr;
+ return (error);
+ }
+ if (ia->ia_flags & IFA_ROUTE) {
+ ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr;
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
+ ia->ia_ifa.ifa_dstaddr =
+ (struct sockaddr *)&ia->ia_dstaddr;
+ rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP);
+ }
+ break;
+
+ case SIOCSIFBRDADDR:
+ if ((ifp->if_flags & IFF_BROADCAST) == 0)
+ return (EINVAL);
+ ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr;
+ break;
+
+ case SIOCSIFADDR:
+ return (in_ifinit(ifp, ia,
+ (struct sockaddr_in *) &ifr->ifr_addr, 1));
+
+ case SIOCSIFNETMASK:
+ i = ifra->ifra_addr.sin_addr.s_addr;
+ ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr = i);
+ break;
+
+ case SIOCAIFADDR:
+ maskIsNew = 0;
+ hostIsNew = 1;
+ error = 0;
+ if (ia->ia_addr.sin_family == AF_INET) {
+ if (ifra->ifra_addr.sin_len == 0) {
+ ifra->ifra_addr = ia->ia_addr;
+ hostIsNew = 0;
+ } else if (ifra->ifra_addr.sin_addr.s_addr ==
+ ia->ia_addr.sin_addr.s_addr)
+ hostIsNew = 0;
+ }
+ if (ifra->ifra_mask.sin_len) {
+ in_ifscrub(ifp, ia);
+ ia->ia_sockmask = ifra->ifra_mask;
+ ia->ia_subnetmask =
+ ntohl(ia->ia_sockmask.sin_addr.s_addr);
+ maskIsNew = 1;
+ }
+ if ((ifp->if_flags & IFF_POINTOPOINT) &&
+ (ifra->ifra_dstaddr.sin_family == AF_INET)) {
+ in_ifscrub(ifp, ia);
+ ia->ia_dstaddr = ifra->ifra_dstaddr;
+ maskIsNew = 1; /* We lie; but the effect's the same */
+ }
+ if (ifra->ifra_addr.sin_family == AF_INET &&
+ (hostIsNew || maskIsNew))
+ error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0);
+ if ((ifp->if_flags & IFF_BROADCAST) &&
+ (ifra->ifra_broadaddr.sin_family == AF_INET))
+ ia->ia_broadaddr = ifra->ifra_broadaddr;
+ return (error);
+
+ case SIOCDIFADDR:
+ in_ifscrub(ifp, ia);
+ if ((ifa = ifp->if_addrlist) == (struct ifaddr *)ia)
+ ifp->if_addrlist = ifa->ifa_next;
+ else {
+ while (ifa->ifa_next &&
+ (ifa->ifa_next != (struct ifaddr *)ia))
+ ifa = ifa->ifa_next;
+ if (ifa->ifa_next)
+ ifa->ifa_next = ((struct ifaddr *)ia)->ifa_next;
+ else
+ printf("Couldn't unlink inifaddr from ifp\n");
+ }
+ oia = ia;
+ if (oia == (ia = in_ifaddr))
+ in_ifaddr = ia->ia_next;
+ else {
+ while (ia->ia_next && (ia->ia_next != oia))
+ ia = ia->ia_next;
+ if (ia->ia_next)
+ ia->ia_next = oia->ia_next;
+ else
+ printf("Didn't unlink inifadr from list\n");
+ }
+ IFAFREE((&oia->ia_ifa));
+ break;
+
+ default:
+ if (ifp == 0 || ifp->if_ioctl == 0)
+ return (EOPNOTSUPP);
+ return ((*ifp->if_ioctl)(ifp, cmd, data));
+ }
+ return (0);
+}
+
+/*
+ * Delete any existing route for an interface.
+ */
+void
+in_ifscrub(ifp, ia)
+ register struct ifnet *ifp;
+ register struct in_ifaddr *ia;
+{
+
+ if ((ia->ia_flags & IFA_ROUTE) == 0)
+ return;
+ if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT))
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
+ else
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
+ ia->ia_flags &= ~IFA_ROUTE;
+}
+
+/*
+ * Initialize an interface's internet address
+ * and routing table entry.
+ */
+int
+in_ifinit(ifp, ia, sin, scrub)
+ register struct ifnet *ifp;
+ register struct in_ifaddr *ia;
+ struct sockaddr_in *sin;
+ int scrub;
+{
+ register u_long i = ntohl(sin->sin_addr.s_addr);
+ struct sockaddr_in oldaddr;
+ int s = splimp(), flags = RTF_UP, error, ether_output();
+
+ oldaddr = ia->ia_addr;
+ ia->ia_addr = *sin;
+ /*
+ * Give the interface a chance to initialize
+ * if this is its first address,
+ * and to validate the address if necessary.
+ */
+ if (ifp->if_ioctl &&
+ (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) {
+ splx(s);
+ ia->ia_addr = oldaddr;
+ return (error);
+ }
+ if (ifp->if_output == ether_output) { /* XXX: Another Kludge */
+ ia->ia_ifa.ifa_rtrequest = arp_rtrequest;
+ ia->ia_ifa.ifa_flags |= RTF_CLONING;
+ }
+ splx(s);
+ if (scrub) {
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr;
+ in_ifscrub(ifp, ia);
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ }
+ if (IN_CLASSA(i))
+ ia->ia_netmask = IN_CLASSA_NET;
+ else if (IN_CLASSB(i))
+ ia->ia_netmask = IN_CLASSB_NET;
+ else
+ ia->ia_netmask = IN_CLASSC_NET;
+ /*
+ * The subnet mask usually includes at least the standard network part,
+ * but may may be smaller in the case of supernetting.
+ * If it is set, we believe it.
+ */
+ if (ia->ia_subnetmask == 0) {
+ ia->ia_subnetmask = ia->ia_netmask;
+ ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask);
+ } else
+ ia->ia_netmask &= ia->ia_subnetmask;
+ ia->ia_net = i & ia->ia_netmask;
+ ia->ia_subnet = i & ia->ia_subnetmask;
+ in_socktrim(&ia->ia_sockmask);
+ /*
+ * Add route for the network.
+ */
+ ia->ia_ifa.ifa_metric = ifp->if_metric;
+ if (ifp->if_flags & IFF_BROADCAST) {
+ ia->ia_broadaddr.sin_addr.s_addr =
+ htonl(ia->ia_subnet | ~ia->ia_subnetmask);
+ ia->ia_netbroadcast.s_addr =
+ htonl(ia->ia_net | ~ ia->ia_netmask);
+ } else if (ifp->if_flags & IFF_LOOPBACK) {
+ ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr;
+ flags |= RTF_HOST;
+ } else if (ifp->if_flags & IFF_POINTOPOINT) {
+ if (ia->ia_dstaddr.sin_family != AF_INET)
+ return (0);
+ flags |= RTF_HOST;
+ }
+ if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0)
+ ia->ia_flags |= IFA_ROUTE;
+ /*
+ * If the interface supports multicast, join the "all hosts"
+ * multicast group on that interface.
+ */
+ if (ifp->if_flags & IFF_MULTICAST) {
+ struct in_addr addr;
+
+ addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+ in_addmulti(&addr, ifp);
+ }
+ return (error);
+}
+
+
+/*
+ * Return 1 if the address might be a local broadcast address.
+ */
+int
+in_broadcast(in, ifp)
+ struct in_addr in;
+ struct ifnet *ifp;
+{
+ register struct ifaddr *ifa;
+ u_long t;
+
+ if (in.s_addr == INADDR_BROADCAST ||
+ in.s_addr == INADDR_ANY)
+ return 1;
+ if ((ifp->if_flags & IFF_BROADCAST) == 0)
+ return 0;
+ t = ntohl(in.s_addr);
+ /*
+ * Look through the list of addresses for a match
+ * with a broadcast address.
+ */
+#define ia ((struct in_ifaddr *)ifa)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+ if (ifa->ifa_addr->sa_family == AF_INET &&
+ (in.s_addr == ia->ia_broadaddr.sin_addr.s_addr ||
+ in.s_addr == ia->ia_netbroadcast.s_addr ||
+ /*
+ * Check for old-style (host 0) broadcast.
+ */
+ t == ia->ia_subnet || t == ia->ia_net))
+ return 1;
+ return (0);
+#undef ia
+}
+/*
+ * Add an address to the list of IP multicast addresses for a given interface.
+ */
+struct in_multi *
+in_addmulti(ap, ifp)
+ register struct in_addr *ap;
+ register struct ifnet *ifp;
+{
+ register struct in_multi *inm;
+ struct ifreq ifr;
+ struct in_ifaddr *ia;
+ int s = splnet();
+
+ /*
+ * See if address already in list.
+ */
+ IN_LOOKUP_MULTI(*ap, ifp, inm);
+ if (inm != NULL) {
+ /*
+ * Found it; just increment the reference count.
+ */
+ ++inm->inm_refcount;
+ }
+ else {
+ /*
+ * New address; allocate a new multicast record
+ * and link it into the interface's multicast list.
+ */
+ inm = (struct in_multi *)malloc(sizeof(*inm),
+ M_IPMADDR, M_NOWAIT);
+ if (inm == NULL) {
+ splx(s);
+ return (NULL);
+ }
+ inm->inm_addr = *ap;
+ inm->inm_ifp = ifp;
+ inm->inm_refcount = 1;
+ IFP_TO_IA(ifp, ia);
+ if (ia == NULL) {
+ free(inm, M_IPMADDR);
+ splx(s);
+ return (NULL);
+ }
+ inm->inm_ia = ia;
+ inm->inm_next = ia->ia_multiaddrs;
+ ia->ia_multiaddrs = inm;
+ /*
+ * Ask the network driver to update its multicast reception
+ * filter appropriately for the new address.
+ */
+ ((struct sockaddr_in *)&ifr.ifr_addr)->sin_family = AF_INET;
+ ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr = *ap;
+ if ((ifp->if_ioctl == NULL) ||
+ (*ifp->if_ioctl)(ifp, SIOCADDMULTI,(caddr_t)&ifr) != 0) {
+ ia->ia_multiaddrs = inm->inm_next;
+ free(inm, M_IPMADDR);
+ splx(s);
+ return (NULL);
+ }
+ /*
+ * Let IGMP know that we have joined a new IP multicast group.
+ */
+ igmp_joingroup(inm);
+ }
+ splx(s);
+ return (inm);
+}
+
+/*
+ * Delete a multicast address record.
+ */
+void
+in_delmulti(inm)
+ register struct in_multi *inm;
+{
+ register struct in_multi **p;
+ struct ifreq ifr;
+ int s = splnet();
+
+ if (--inm->inm_refcount == 0) {
+ /*
+ * No remaining claims to this record; let IGMP know that
+ * we are leaving the multicast group.
+ */
+ igmp_leavegroup(inm);
+ /*
+ * Unlink from list.
+ */
+ for (p = &inm->inm_ia->ia_multiaddrs;
+ *p != inm;
+ p = &(*p)->inm_next)
+ continue;
+ *p = (*p)->inm_next;
+ /*
+ * Notify the network driver to update its multicast reception
+ * filter.
+ */
+ ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_family = AF_INET;
+ ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_addr =
+ inm->inm_addr;
+ (*inm->inm_ifp->if_ioctl)(inm->inm_ifp, SIOCDELMULTI,
+ (caddr_t)&ifr);
+ free(inm, M_IPMADDR);
+ }
+ splx(s);
+}
+#endif
diff --git a/sys/netinet/in.h b/sys/netinet/in.h
new file mode 100644
index 0000000..1ce9948
--- /dev/null
+++ b/sys/netinet/in.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 1982, 1986, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in.h 8.3 (Berkeley) 1/3/94
+ */
+
+/*
+ * Constants and structures defined by the internet system,
+ * Per RFC 790, September 1981, and numerous additions.
+ */
+
+/*
+ * Protocols
+ */
+#define IPPROTO_IP 0 /* dummy for IP */
+#define IPPROTO_ICMP 1 /* control message protocol */
+#define IPPROTO_IGMP 2 /* group mgmt protocol */
+#define IPPROTO_GGP 3 /* gateway^2 (deprecated) */
+#define IPPROTO_TCP 6 /* tcp */
+#define IPPROTO_EGP 8 /* exterior gateway protocol */
+#define IPPROTO_PUP 12 /* pup */
+#define IPPROTO_UDP 17 /* user datagram protocol */
+#define IPPROTO_IDP 22 /* xns idp */
+#define IPPROTO_TP 29 /* tp-4 w/ class negotiation */
+#define IPPROTO_EON 80 /* ISO cnlp */
+#define IPPROTO_ENCAP 98 /* encapsulation header */
+
+#define IPPROTO_RAW 255 /* raw IP packet */
+#define IPPROTO_MAX 256
+
+
+/*
+ * Local port number conventions:
+ * Ports < IPPORT_RESERVED are reserved for
+ * privileged processes (e.g. root).
+ * Ports > IPPORT_USERRESERVED are reserved
+ * for servers, not necessarily privileged.
+ */
+#define IPPORT_RESERVED 1024
+#define IPPORT_USERRESERVED 5000
+
+/*
+ * Internet address (a structure for historical reasons)
+ */
+struct in_addr {
+ u_long s_addr;
+};
+
+/*
+ * Definitions of bits in internet address integers.
+ * On subnets, the decomposition of addresses to host and net parts
+ * is done according to subnet mask, not the masks here.
+ */
+#define IN_CLASSA(i) (((long)(i) & 0x80000000) == 0)
+#define IN_CLASSA_NET 0xff000000
+#define IN_CLASSA_NSHIFT 24
+#define IN_CLASSA_HOST 0x00ffffff
+#define IN_CLASSA_MAX 128
+
+#define IN_CLASSB(i) (((long)(i) & 0xc0000000) == 0x80000000)
+#define IN_CLASSB_NET 0xffff0000
+#define IN_CLASSB_NSHIFT 16
+#define IN_CLASSB_HOST 0x0000ffff
+#define IN_CLASSB_MAX 65536
+
+#define IN_CLASSC(i) (((long)(i) & 0xe0000000) == 0xc0000000)
+#define IN_CLASSC_NET 0xffffff00
+#define IN_CLASSC_NSHIFT 8
+#define IN_CLASSC_HOST 0x000000ff
+
+#define IN_CLASSD(i) (((long)(i) & 0xf0000000) == 0xe0000000)
+#define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */
+#define IN_CLASSD_NSHIFT 28 /* net and host fields, but */
+#define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */
+#define IN_MULTICAST(i) IN_CLASSD(i)
+
+#define IN_EXPERIMENTAL(i) (((long)(i) & 0xf0000000) == 0xf0000000)
+#define IN_BADCLASS(i) (((long)(i) & 0xf0000000) == 0xf0000000)
+
+#define INADDR_ANY (u_long)0x00000000
+#define INADDR_BROADCAST (u_long)0xffffffff /* must be masked */
+#ifndef KERNEL
+#define INADDR_NONE 0xffffffff /* -1 return */
+#endif
+
+#define INADDR_UNSPEC_GROUP (u_long)0xe0000000 /* 224.0.0.0 */
+#define INADDR_ALLHOSTS_GROUP (u_long)0xe0000001 /* 224.0.0.1 */
+#define INADDR_MAX_LOCAL_GROUP (u_long)0xe00000ff /* 224.0.0.255 */
+
+#define IN_LOOPBACKNET 127 /* official! */
+
+/*
+ * Socket address, internet style.
+ */
+struct sockaddr_in {
+ u_char sin_len;
+ u_char sin_family;
+ u_short sin_port;
+ struct in_addr sin_addr;
+ char sin_zero[8];
+};
+
+/*
+ * Structure used to describe IP options.
+ * Used to store options internally, to pass them to a process,
+ * or to restore options retrieved earlier.
+ * The ip_dst is used for the first-hop gateway when using a source route
+ * (this gets put into the header proper).
+ */
+struct ip_opts {
+ struct in_addr ip_dst; /* first hop, 0 w/o src rt */
+ char ip_opts[40]; /* actually variable in size */
+};
+
+/*
+ * Options for use with [gs]etsockopt at the IP level.
+ * First word of comment is data type; bool is stored in int.
+ */
+#define IP_OPTIONS 1 /* buf/ip_opts; set/get IP options */
+#define IP_HDRINCL 2 /* int; header is included with data */
+#define IP_TOS 3 /* int; IP type of service and preced. */
+#define IP_TTL 4 /* int; IP time to live */
+#define IP_RECVOPTS 5 /* bool; receive all IP opts w/dgram */
+#define IP_RECVRETOPTS 6 /* bool; receive IP opts for response */
+#define IP_RECVDSTADDR 7 /* bool; receive IP dst addr w/dgram */
+#define IP_RETOPTS 8 /* ip_opts; set/get IP options */
+#define IP_MULTICAST_IF 9 /* u_char; set/get IP multicast i/f */
+#define IP_MULTICAST_TTL 10 /* u_char; set/get IP multicast ttl */
+#define IP_MULTICAST_LOOP 11 /* u_char; set/get IP multicast loopback */
+#define IP_ADD_MEMBERSHIP 12 /* ip_mreq; add an IP group membership */
+#define IP_DROP_MEMBERSHIP 13 /* ip_mreq; drop an IP group membership */
+
+/*
+ * Defaults and limits for options
+ */
+#define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */
+#define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */
+#define IP_MAX_MEMBERSHIPS 20 /* per socket; must fit in one mbuf */
+
+/*
+ * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP.
+ */
+struct ip_mreq {
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_interface; /* local IP address of interface */
+};
+
+/*
+ * Definitions for inet sysctl operations.
+ *
+ * Third level is protocol number.
+ * Fourth level is desired variable within that protocol.
+ */
+#define IPPROTO_MAXID (IPPROTO_IDP + 1) /* don't list to IPPROTO_MAX */
+
+#define CTL_IPPROTO_NAMES { \
+ { "ip", CTLTYPE_NODE }, \
+ { "icmp", CTLTYPE_NODE }, \
+ { "igmp", CTLTYPE_NODE }, \
+ { "ggp", CTLTYPE_NODE }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { "tcp", CTLTYPE_NODE }, \
+ { 0, 0 }, \
+ { "egp", CTLTYPE_NODE }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { "pup", CTLTYPE_NODE }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { "udp", CTLTYPE_NODE }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { "idp", CTLTYPE_NODE }, \
+}
+
+/*
+ * Names for IP sysctl objects
+ */
+#define IPCTL_FORWARDING 1 /* act as router */
+#define IPCTL_SENDREDIRECTS 2 /* may send redirects when forwarding */
+#define IPCTL_DEFTTL 3 /* default TTL */
+#ifdef notyet
+#define IPCTL_DEFMTU 4 /* default MTU */
+#endif
+#define IPCTL_MAXID 5
+
+#define IPCTL_NAMES { \
+ { 0, 0 }, \
+ { "forwarding", CTLTYPE_INT }, \
+ { "redirect", CTLTYPE_INT }, \
+ { "ttl", CTLTYPE_INT }, \
+ { "mtu", CTLTYPE_INT }, \
+}
+
+
+#ifdef KERNEL
+int in_broadcast __P((struct in_addr, struct ifnet *));
+int in_canforward __P((struct in_addr));
+int in_cksum __P((struct mbuf *, int));
+int in_localaddr __P((struct in_addr));
+u_long in_netof __P((struct in_addr));
+void in_socktrim __P((struct sockaddr_in *));
+#endif
diff --git a/sys/netinet/in_cksum.c b/sys/netinet/in_cksum.c
new file mode 100644
index 0000000..c19a920
--- /dev/null
+++ b/sys/netinet/in_cksum.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 1988, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+
+/*
+ * Checksum routine for Internet Protocol family headers (Portable Version).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ */
+
+#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
+#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
+
+int
+in_cksum(m, len)
+ register struct mbuf *m;
+ register int len;
+{
+ register u_short *w;
+ register int sum = 0;
+ register int mlen = 0;
+ int byte_swapped = 0;
+
+ union {
+ char c[2];
+ u_short s;
+ } s_util;
+ union {
+ u_short s[2];
+ long l;
+ } l_util;
+
+ for (;m && len; m = m->m_next) {
+ if (m->m_len == 0)
+ continue;
+ w = mtod(m, u_short *);
+ if (mlen == -1) {
+ /*
+ * The first byte of this mbuf is the continuation
+ * of a word spanning between this mbuf and the
+ * last mbuf.
+ *
+ * s_util.c[0] is already saved when scanning previous
+ * mbuf.
+ */
+ s_util.c[1] = *(char *)w;
+ sum += s_util.s;
+ w = (u_short *)((char *)w + 1);
+ mlen = m->m_len - 1;
+ len--;
+ } else
+ mlen = m->m_len;
+ if (len < mlen)
+ mlen = len;
+ len -= mlen;
+ /*
+ * Force to even boundary.
+ */
+ if ((1 & (int) w) && (mlen > 0)) {
+ REDUCE;
+ sum <<= 8;
+ s_util.c[0] = *(u_char *)w;
+ w = (u_short *)((char *)w + 1);
+ mlen--;
+ byte_swapped = 1;
+ }
+ /*
+ * Unroll the loop to make overhead from
+ * branches &c small.
+ */
+ while ((mlen -= 32) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
+ sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
+ sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
+ w += 16;
+ }
+ mlen += 32;
+ while ((mlen -= 8) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ w += 4;
+ }
+ mlen += 8;
+ if (mlen == 0 && byte_swapped == 0)
+ continue;
+ REDUCE;
+ while ((mlen -= 2) >= 0) {
+ sum += *w++;
+ }
+ if (byte_swapped) {
+ REDUCE;
+ sum <<= 8;
+ byte_swapped = 0;
+ if (mlen == -1) {
+ s_util.c[1] = *(char *)w;
+ sum += s_util.s;
+ mlen = 0;
+ } else
+ mlen = -1;
+ } else if (mlen == -1)
+ s_util.c[0] = *(char *)w;
+ }
+ if (len)
+ printf("cksum: out of data\n");
+ if (mlen == -1) {
+ /* The last mbuf has odd # of bytes. Follow the
+ standard (the odd byte may be shifted left by 8 bits
+ or not as determined by endian-ness of the machine) */
+ s_util.c[1] = 0;
+ sum += s_util.s;
+ }
+ REDUCE;
+ return (~sum & 0xffff);
+}
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
new file mode 100644
index 0000000..a8b1c6f
--- /dev/null
+++ b/sys/netinet/in_pcb.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_pcb.c 8.2 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+
+struct in_addr zeroin_addr;
+
+int
+in_pcballoc(so, head)
+ struct socket *so;
+ struct inpcb *head;
+{
+ register struct inpcb *inp;
+
+ MALLOC(inp, struct inpcb *, sizeof(*inp), M_PCB, M_WAITOK);
+ if (inp == NULL)
+ return (ENOBUFS);
+ bzero((caddr_t)inp, sizeof(*inp));
+ inp->inp_head = head;
+ inp->inp_socket = so;
+ insque(inp, head);
+ so->so_pcb = (caddr_t)inp;
+ return (0);
+}
+
+int
+in_pcbbind(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct socket *so = inp->inp_socket;
+ register struct inpcb *head = inp->inp_head;
+ register struct sockaddr_in *sin;
+ struct proc *p = curproc; /* XXX */
+ u_short lport = 0;
+ int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
+ int error;
+
+ if (in_ifaddr == 0)
+ return (EADDRNOTAVAIL);
+ if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
+ return (EINVAL);
+ if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 &&
+ ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
+ (so->so_options & SO_ACCEPTCONN) == 0))
+ wild = INPLOOKUP_WILDCARD;
+ if (nam) {
+ sin = mtod(nam, struct sockaddr_in *);
+ if (nam->m_len != sizeof (*sin))
+ return (EINVAL);
+#ifdef notdef
+ /*
+ * We should check the family, but old programs
+ * incorrectly fail to initialize it.
+ */
+ if (sin->sin_family != AF_INET)
+ return (EAFNOSUPPORT);
+#endif
+ lport = sin->sin_port;
+ if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
+ /*
+ * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
+ * allow complete duplication of binding if
+ * SO_REUSEPORT is set, or if SO_REUSEADDR is set
+ * and a multicast address is bound on both
+ * new and duplicated sockets.
+ */
+ if (so->so_options & SO_REUSEADDR)
+ reuseport = SO_REUSEADDR|SO_REUSEPORT;
+ } else if (sin->sin_addr.s_addr != INADDR_ANY) {
+ sin->sin_port = 0; /* yech... */
+ if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
+ return (EADDRNOTAVAIL);
+ }
+ if (lport) {
+ struct inpcb *t;
+
+ /* GROSS */
+ if (ntohs(lport) < IPPORT_RESERVED &&
+ (error = suser(p->p_ucred, &p->p_acflag)))
+ return (error);
+ t = in_pcblookup(head, zeroin_addr, 0,
+ sin->sin_addr, lport, wild);
+ if (t && (reuseport & t->inp_socket->so_options) == 0)
+ return (EADDRINUSE);
+ }
+ inp->inp_laddr = sin->sin_addr;
+ }
+ if (lport == 0)
+ do {
+ if (head->inp_lport++ < IPPORT_RESERVED ||
+ head->inp_lport > IPPORT_USERRESERVED)
+ head->inp_lport = IPPORT_RESERVED;
+ lport = htons(head->inp_lport);
+ } while (in_pcblookup(head,
+ zeroin_addr, 0, inp->inp_laddr, lport, wild));
+ inp->inp_lport = lport;
+ return (0);
+}
+
+/*
+ * Connect from a socket to a specified address.
+ * Both address and port must be specified in argument sin.
+ * If don't have a local address for this socket yet,
+ * then pick one.
+ */
+int
+in_pcbconnect(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ struct in_ifaddr *ia;
+ struct sockaddr_in *ifaddr = 0;
+ register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
+
+ if (nam->m_len != sizeof (*sin))
+ return (EINVAL);
+ if (sin->sin_family != AF_INET)
+ return (EAFNOSUPPORT);
+ if (sin->sin_port == 0)
+ return (EADDRNOTAVAIL);
+ if (in_ifaddr) {
+ /*
+ * If the destination address is INADDR_ANY,
+ * use the primary local address.
+ * If the supplied address is INADDR_BROADCAST,
+ * and the primary interface supports broadcast,
+ * choose the broadcast address for that interface.
+ */
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+#define sintosa(sin) ((struct sockaddr *)(sin))
+#define ifatoia(ifa) ((struct in_ifaddr *)(ifa))
+ if (sin->sin_addr.s_addr == INADDR_ANY)
+ sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
+ else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
+ (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST))
+ sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr;
+ }
+ if (inp->inp_laddr.s_addr == INADDR_ANY) {
+ register struct route *ro;
+
+ ia = (struct in_ifaddr *)0;
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the i/f, else punt.
+ */
+ ro = &inp->inp_route;
+ if (ro->ro_rt &&
+ (satosin(&ro->ro_dst)->sin_addr.s_addr !=
+ sin->sin_addr.s_addr ||
+ inp->inp_socket->so_options & SO_DONTROUTE)) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
+ (ro->ro_rt == (struct rtentry *)0 ||
+ ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
+ /* No route yet, so try to acquire one */
+ ro->ro_dst.sa_family = AF_INET;
+ ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
+ ((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
+ sin->sin_addr;
+ rtalloc(ro);
+ }
+ /*
+ * If we found a route, use the address
+ * corresponding to the outgoing interface
+ * unless it is the loopback (in case a route
+ * to our address on another net goes to loopback).
+ */
+ if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
+ ia = ifatoia(ro->ro_rt->rt_ifa);
+ if (ia == 0) {
+ u_short fport = sin->sin_port;
+
+ sin->sin_port = 0;
+ ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin)));
+ if (ia == 0)
+ ia = ifatoia(ifa_ifwithnet(sintosa(sin)));
+ sin->sin_port = fport;
+ if (ia == 0)
+ ia = in_ifaddr;
+ if (ia == 0)
+ return (EADDRNOTAVAIL);
+ }
+ /*
+ * If the destination address is multicast and an outgoing
+ * interface has been set as a multicast option, use the
+ * address of that interface as our source address.
+ */
+ if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
+ inp->inp_moptions != NULL) {
+ struct ip_moptions *imo;
+ struct ifnet *ifp;
+
+ imo = inp->inp_moptions;
+ if (imo->imo_multicast_ifp != NULL) {
+ ifp = imo->imo_multicast_ifp;
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+ if (ia == 0)
+ return (EADDRNOTAVAIL);
+ }
+ }
+ ifaddr = (struct sockaddr_in *)&ia->ia_addr;
+ }
+ if (in_pcblookup(inp->inp_head,
+ sin->sin_addr,
+ sin->sin_port,
+ inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
+ inp->inp_lport,
+ 0))
+ return (EADDRINUSE);
+ if (inp->inp_laddr.s_addr == INADDR_ANY) {
+ if (inp->inp_lport == 0)
+ (void)in_pcbbind(inp, (struct mbuf *)0);
+ inp->inp_laddr = ifaddr->sin_addr;
+ }
+ inp->inp_faddr = sin->sin_addr;
+ inp->inp_fport = sin->sin_port;
+ return (0);
+}
+
+void
+in_pcbdisconnect(inp)
+ struct inpcb *inp;
+{
+
+ inp->inp_faddr.s_addr = INADDR_ANY;
+ inp->inp_fport = 0;
+ if (inp->inp_socket->so_state & SS_NOFDREF)
+ in_pcbdetach(inp);
+}
+
+void
+in_pcbdetach(inp)
+ struct inpcb *inp;
+{
+ struct socket *so = inp->inp_socket;
+
+ so->so_pcb = 0;
+ sofree(so);
+ if (inp->inp_options)
+ (void)m_free(inp->inp_options);
+ if (inp->inp_route.ro_rt)
+ rtfree(inp->inp_route.ro_rt);
+ ip_freemoptions(inp->inp_moptions);
+ remque(inp);
+ FREE(inp, M_PCB);
+}
+
+void
+in_setsockaddr(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *sin;
+
+ nam->m_len = sizeof (*sin);
+ sin = mtod(nam, struct sockaddr_in *);
+ bzero((caddr_t)sin, sizeof (*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_port = inp->inp_lport;
+ sin->sin_addr = inp->inp_laddr;
+}
+
+void
+in_setpeeraddr(inp, nam)
+ struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *sin;
+
+ nam->m_len = sizeof (*sin);
+ sin = mtod(nam, struct sockaddr_in *);
+ bzero((caddr_t)sin, sizeof (*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_port = inp->inp_fport;
+ sin->sin_addr = inp->inp_faddr;
+}
+
+/*
+ * Pass some notification to all connections of a protocol
+ * associated with address dst. The local address and/or port numbers
+ * may be specified to limit the search. The "usual action" will be
+ * taken, depending on the ctlinput cmd. The caller must filter any
+ * cmds that are uninteresting (e.g., no error in the map).
+ * Call the protocol specific routine (if any) to report
+ * any errors for each matching socket.
+ *
+ * Must be called at splnet.
+ */
+void
+in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify)
+ struct inpcb *head;
+ struct sockaddr *dst;
+ u_int fport_arg, lport_arg;
+ struct in_addr laddr;
+ int cmd;
+ void (*notify) __P((struct inpcb *, int));
+{
+ extern u_char inetctlerrmap[];
+ register struct inpcb *inp, *oinp;
+ struct in_addr faddr;
+ u_short fport = fport_arg, lport = lport_arg;
+ int errno;
+
+ if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
+ return;
+ faddr = ((struct sockaddr_in *)dst)->sin_addr;
+ if (faddr.s_addr == INADDR_ANY)
+ return;
+
+ /*
+ * Redirects go to all references to the destination,
+ * and use in_rtchange to invalidate the route cache.
+ * Dead host indications: notify all references to the destination.
+ * Otherwise, if we have knowledge of the local port and address,
+ * deliver only to that socket.
+ */
+ if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
+ fport = 0;
+ lport = 0;
+ laddr.s_addr = 0;
+ if (cmd != PRC_HOSTDEAD)
+ notify = in_rtchange;
+ }
+ errno = inetctlerrmap[cmd];
+ for (inp = head->inp_next; inp != head;) {
+ if (inp->inp_faddr.s_addr != faddr.s_addr ||
+ inp->inp_socket == 0 ||
+ (lport && inp->inp_lport != lport) ||
+ (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) ||
+ (fport && inp->inp_fport != fport)) {
+ inp = inp->inp_next;
+ continue;
+ }
+ oinp = inp;
+ inp = inp->inp_next;
+ if (notify)
+ (*notify)(oinp, errno);
+ }
+}
+
+/*
+ * Check for alternatives when higher level complains
+ * about service problems. For now, invalidate cached
+ * routing information. If the route was created dynamically
+ * (by a redirect), time to try a default gateway again.
+ */
+void
+in_losing(inp)
+ struct inpcb *inp;
+{
+ register struct rtentry *rt;
+ struct rt_addrinfo info;
+
+ if ((rt = inp->inp_route.ro_rt)) {
+ inp->inp_route.ro_rt = 0;
+ bzero((caddr_t)&info, sizeof(info));
+ info.rti_info[RTAX_DST] =
+ (struct sockaddr *)&inp->inp_route.ro_dst;
+ info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
+ info.rti_info[RTAX_NETMASK] = rt_mask(rt);
+ rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
+ if (rt->rt_flags & RTF_DYNAMIC)
+ (void) rtrequest(RTM_DELETE, rt_key(rt),
+ rt->rt_gateway, rt_mask(rt), rt->rt_flags,
+ (struct rtentry **)0);
+ else
+ /*
+ * A new route can be allocated
+ * the next time output is attempted.
+ */
+ rtfree(rt);
+ }
+}
+
+/*
+ * After a routing change, flush old routing
+ * and allocate a (hopefully) better one.
+ */
+void
+in_rtchange(inp, errno)
+ register struct inpcb *inp;
+ int errno;
+{
+ if (inp->inp_route.ro_rt) {
+ rtfree(inp->inp_route.ro_rt);
+ inp->inp_route.ro_rt = 0;
+ /*
+ * A new route can be allocated the next time
+ * output is attempted.
+ */
+ }
+}
+
+struct inpcb *
+in_pcblookup(head, faddr, fport_arg, laddr, lport_arg, flags)
+ struct inpcb *head;
+ struct in_addr faddr, laddr;
+ u_int fport_arg, lport_arg;
+ int flags;
+{
+ register struct inpcb *inp, *match = 0;
+ int matchwild = 3, wildcard;
+ u_short fport = fport_arg, lport = lport_arg;
+
+ for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
+ if (inp->inp_lport != lport)
+ continue;
+ wildcard = 0;
+ if (inp->inp_laddr.s_addr != INADDR_ANY) {
+ if (laddr.s_addr == INADDR_ANY)
+ wildcard++;
+ else if (inp->inp_laddr.s_addr != laddr.s_addr)
+ continue;
+ } else {
+ if (laddr.s_addr != INADDR_ANY)
+ wildcard++;
+ }
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ if (faddr.s_addr == INADDR_ANY)
+ wildcard++;
+ else if (inp->inp_faddr.s_addr != faddr.s_addr ||
+ inp->inp_fport != fport)
+ continue;
+ } else {
+ if (faddr.s_addr != INADDR_ANY)
+ wildcard++;
+ }
+ if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
+ continue;
+ if (wildcard < matchwild) {
+ match = inp;
+ matchwild = wildcard;
+ if (matchwild == 0)
+ break;
+ }
+ }
+ return (match);
+}
diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h
new file mode 100644
index 0000000..baa8be3
--- /dev/null
+++ b/sys/netinet/in_pcb.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1982, 1986, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_pcb.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Common structure pcb for internet protocol implementation.
+ * Here are stored pointers to local and foreign host table
+ * entries, local and foreign socket numbers, and pointers
+ * up (to a socket structure) and down (to a protocol-specific)
+ * control block.
+ */
+struct inpcb {
+ struct inpcb *inp_next,*inp_prev;
+ /* pointers to other pcb's */
+ struct inpcb *inp_head; /* pointer back to chain of inpcb's
+ for this protocol */
+ struct in_addr inp_faddr; /* foreign host table entry */
+ u_short inp_fport; /* foreign port */
+ struct in_addr inp_laddr; /* local host table entry */
+ u_short inp_lport; /* local port */
+ struct socket *inp_socket; /* back pointer to socket */
+ caddr_t inp_ppcb; /* pointer to per-protocol pcb */
+ struct route inp_route; /* placeholder for routing entry */
+ int inp_flags; /* generic IP/datagram flags */
+ struct ip inp_ip; /* header prototype; should have more */
+ struct mbuf *inp_options; /* IP options */
+ struct ip_moptions *inp_moptions; /* IP multicast options */
+};
+
+/* flags in inp_flags: */
+#define INP_RECVOPTS 0x01 /* receive incoming IP options */
+#define INP_RECVRETOPTS 0x02 /* receive IP options for reply */
+#define INP_RECVDSTADDR 0x04 /* receive IP dst address */
+#define INP_CONTROLOPTS (INP_RECVOPTS|INP_RECVRETOPTS|INP_RECVDSTADDR)
+#define INP_HDRINCL 0x08 /* user supplies entire IP header */
+
+#define INPLOOKUP_WILDCARD 1
+#define INPLOOKUP_SETLOCAL 2
+
+#define sotoinpcb(so) ((struct inpcb *)(so)->so_pcb)
+
+#ifdef KERNEL
+void in_losing __P((struct inpcb *));
+int in_pcballoc __P((struct socket *, struct inpcb *));
+int in_pcbbind __P((struct inpcb *, struct mbuf *));
+int in_pcbconnect __P((struct inpcb *, struct mbuf *));
+void in_pcbdetach __P((struct inpcb *));
+void in_pcbdisconnect __P((struct inpcb *));
+struct inpcb *
+ in_pcblookup __P((struct inpcb *,
+ struct in_addr, u_int, struct in_addr, u_int, int));
+void in_pcbnotify __P((struct inpcb *, struct sockaddr *,
+ u_int, struct in_addr, u_int, int, void (*)(struct inpcb *, int)));
+void in_rtchange __P((struct inpcb *, int));
+void in_setpeeraddr __P((struct inpcb *, struct mbuf *));
+void in_setsockaddr __P((struct inpcb *, struct mbuf *));
+#endif
diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c
new file mode 100644
index 0000000..9927c5a
--- /dev/null
+++ b/sys/netinet/in_proto.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_proto.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/mbuf.h>
+
+#include <net/if.h>
+#include <net/radix.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/in_pcb.h>
+#include <netinet/igmp_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_debug.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+/*
+ * TCP/IP protocol family: IP, ICMP, UDP, TCP.
+ */
+
+#ifdef NSIP
+void idpip_input(), nsip_ctlinput();
+#endif
+
+#ifdef TPIP
+void tpip_input(), tpip_ctlinput(), tp_ctloutput();
+int tp_init(), tp_slowtimo(), tp_drain(), tp_usrreq();
+#endif
+
+#ifdef EON
+void eoninput(), eonctlinput(), eonprotoinit();
+#endif /* EON */
+
+extern struct domain inetdomain;
+
+struct protosw inetsw[] = {
+{ 0, &inetdomain, 0, 0,
+ 0, ip_output, 0, 0,
+ 0,
+ ip_init, 0, ip_slowtimo, ip_drain, ip_sysctl
+},
+{ SOCK_DGRAM, &inetdomain, IPPROTO_UDP, PR_ATOMIC|PR_ADDR,
+ udp_input, 0, udp_ctlinput, ip_ctloutput,
+ udp_usrreq,
+ udp_init, 0, 0, 0, udp_sysctl
+},
+{ SOCK_STREAM, &inetdomain, IPPROTO_TCP, PR_CONNREQUIRED|PR_WANTRCVD,
+ tcp_input, 0, tcp_ctlinput, tcp_ctloutput,
+ tcp_usrreq,
+ tcp_init, tcp_fasttimo, tcp_slowtimo, tcp_drain,
+},
+{ SOCK_RAW, &inetdomain, IPPROTO_RAW, PR_ATOMIC|PR_ADDR,
+ rip_input, rip_output, 0, rip_ctloutput,
+ rip_usrreq,
+ 0, 0, 0, 0,
+},
+{ SOCK_RAW, &inetdomain, IPPROTO_ICMP, PR_ATOMIC|PR_ADDR,
+ icmp_input, rip_output, 0, rip_ctloutput,
+ rip_usrreq,
+ 0, 0, 0, 0, icmp_sysctl
+},
+{ SOCK_RAW, &inetdomain, IPPROTO_IGMP, PR_ATOMIC|PR_ADDR,
+ igmp_input, rip_output, 0, rip_ctloutput,
+ rip_usrreq,
+ igmp_init, igmp_fasttimo, 0, 0,
+},
+#ifdef TPIP
+{ SOCK_SEQPACKET,&inetdomain, IPPROTO_TP, PR_CONNREQUIRED|PR_WANTRCVD,
+ tpip_input, 0, tpip_ctlinput, tp_ctloutput,
+ tp_usrreq,
+ tp_init, 0, tp_slowtimo, tp_drain,
+},
+#endif
+/* EON (ISO CLNL over IP) */
+#ifdef EON
+{ SOCK_RAW, &inetdomain, IPPROTO_EON, 0,
+ eoninput, 0, eonctlinput, 0,
+ 0,
+ eonprotoinit, 0, 0, 0,
+},
+#endif
+#ifdef NSIP
+{ SOCK_RAW, &inetdomain, IPPROTO_IDP, PR_ATOMIC|PR_ADDR,
+ idpip_input, rip_output, nsip_ctlinput, 0,
+ rip_usrreq,
+ 0, 0, 0, 0,
+},
+#endif
+ /* raw wildcard */
+{ SOCK_RAW, &inetdomain, 0, PR_ATOMIC|PR_ADDR,
+ rip_input, rip_output, 0, rip_ctloutput,
+ rip_usrreq,
+ rip_init, 0, 0, 0,
+},
+};
+
+struct domain inetdomain =
+ { AF_INET, "internet", 0, 0, 0,
+ inetsw, &inetsw[sizeof(inetsw)/sizeof(inetsw[0])], 0,
+ rn_inithead, 32, sizeof(struct sockaddr_in) };
+
+#include "imp.h"
+#if NIMP > 0
+extern struct domain impdomain;
+int rimp_output(), hostslowtimo();
+
+struct protosw impsw[] = {
+{ SOCK_RAW, &impdomain, 0, PR_ATOMIC|PR_ADDR,
+ 0, rimp_output, 0, 0,
+ rip_usrreq,
+ 0, 0, hostslowtimo, 0,
+},
+};
+
+struct domain impdomain =
+ { AF_IMPLINK, "imp", 0, 0, 0,
+ impsw, &impsw[sizeof (impsw)/sizeof(impsw[0])] };
+#endif
+
+#if 0
+#include "hy.h"
+#if NHY > 0
+/*
+ * HYPERchannel protocol family: raw interface.
+ */
+int rhy_output();
+extern struct domain hydomain;
+
+struct protosw hysw[] = {
+{ SOCK_RAW, &hydomain, 0, PR_ATOMIC|PR_ADDR,
+ 0, rhy_output, 0, 0,
+ rip_usrreq,
+ 0, 0, 0, 0,
+},
+};
+
+struct domain hydomain =
+ { AF_HYLINK, "hy", 0, 0, 0, hysw, &hysw[sizeof (hysw)/sizeof(hysw[0])] };
+#endif
+#endif
diff --git a/sys/netinet/in_systm.h b/sys/netinet/in_systm.h
new file mode 100644
index 0000000..cbd8e53
--- /dev/null
+++ b/sys/netinet/in_systm.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_systm.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Miscellaneous internetwork
+ * definitions for kernel.
+ */
+
+/*
+ * Network types.
+ *
+ * Internally the system keeps counters in the headers with the bytes
+ * swapped so that VAX instructions will work on them. It reverses
+ * the bytes before transmission at each protocol level. The n_ types
+ * represent the types with the bytes in ``high-ender'' order.
+ */
+typedef u_short n_short; /* short as received from the net */
+typedef u_long n_long; /* long as received from the net */
+
+typedef u_long n_time; /* ms since 00:00 GMT, byte rev */
+
+#ifdef KERNEL
+n_time iptime __P((void));
+#endif
diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h
new file mode 100644
index 0000000..d3d4c25
--- /dev/null
+++ b/sys/netinet/in_var.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 1985, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Interface address, Internet version. One of these structures
+ * is allocated for each interface with an Internet address.
+ * The ifaddr structure contains the protocol-independent part
+ * of the structure and is assumed to be first.
+ */
+struct in_ifaddr {
+ struct ifaddr ia_ifa; /* protocol-independent info */
+#define ia_ifp ia_ifa.ifa_ifp
+#define ia_flags ia_ifa.ifa_flags
+ /* ia_{,sub}net{,mask} in host order */
+ u_long ia_net; /* network number of interface */
+ u_long ia_netmask; /* mask of net part */
+ u_long ia_subnet; /* subnet number, including net */
+ u_long ia_subnetmask; /* mask of subnet part */
+ struct in_addr ia_netbroadcast; /* to recognize net broadcasts */
+ struct in_ifaddr *ia_next; /* next in list of internet addresses */
+ struct sockaddr_in ia_addr; /* reserve space for interface name */
+ struct sockaddr_in ia_dstaddr; /* reserve space for broadcast addr */
+#define ia_broadaddr ia_dstaddr
+ struct sockaddr_in ia_sockmask; /* reserve space for general netmask */
+ struct in_multi *ia_multiaddrs; /* list of multicast addresses */
+};
+
+struct in_aliasreq {
+ char ifra_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ struct sockaddr_in ifra_addr;
+ struct sockaddr_in ifra_broadaddr;
+#define ifra_dstaddr ifra_broadaddr
+ struct sockaddr_in ifra_mask;
+};
+/*
+ * Given a pointer to an in_ifaddr (ifaddr),
+ * return a pointer to the addr as a sockaddr_in.
+ */
+#define IA_SIN(ia) (&(((struct in_ifaddr *)(ia))->ia_addr))
+
+#define IN_LNAOF(in, ifa) \
+ ((ntohl((in).s_addr) & ~((struct in_ifaddr *)(ifa)->ia_subnetmask))
+
+
+#ifdef KERNEL
+extern struct in_ifaddr *in_ifaddr;
+extern struct ifqueue ipintrq; /* ip packet input queue */
+void in_socktrim __P((struct sockaddr_in *));
+
+
+/*
+ * Macro for finding the interface (ifnet structure) corresponding to one
+ * of our IP addresses.
+ */
+#define INADDR_TO_IFP(addr, ifp) \
+ /* struct in_addr addr; */ \
+ /* struct ifnet *ifp; */ \
+{ \
+ register struct in_ifaddr *ia; \
+\
+ for (ia = in_ifaddr; \
+ ia != NULL && IA_SIN(ia)->sin_addr.s_addr != (addr).s_addr; \
+ ia = ia->ia_next) \
+ continue; \
+ (ifp) = (ia == NULL) ? NULL : ia->ia_ifp; \
+}
+
+/*
+ * Macro for finding the internet address structure (in_ifaddr) corresponding
+ * to a given interface (ifnet structure).
+ */
+#define IFP_TO_IA(ifp, ia) \
+ /* struct ifnet *ifp; */ \
+ /* struct in_ifaddr *ia; */ \
+{ \
+ for ((ia) = in_ifaddr; \
+ (ia) != NULL && (ia)->ia_ifp != (ifp); \
+ (ia) = (ia)->ia_next) \
+ continue; \
+}
+#endif
+
+/*
+ * Internet multicast address structure. There is one of these for each IP
+ * multicast group to which this host belongs on a given network interface.
+ * They are kept in a linked list, rooted in the interface's in_ifaddr
+ * structure.
+ */
+struct in_multi {
+ struct in_addr inm_addr; /* IP multicast address */
+ struct ifnet *inm_ifp; /* back pointer to ifnet */
+ struct in_ifaddr *inm_ia; /* back pointer to in_ifaddr */
+ u_int inm_refcount; /* no. membership claims by sockets */
+ u_int inm_timer; /* IGMP membership report timer */
+ struct in_multi *inm_next; /* ptr to next multicast address */
+};
+
+#ifdef KERNEL
+/*
+ * Structure used by macros below to remember position when stepping through
+ * all of the in_multi records.
+ */
+struct in_multistep {
+ struct in_ifaddr *i_ia;
+ struct in_multi *i_inm;
+};
+
+/*
+ * Macro for looking up the in_multi record for a given IP multicast address
+ * on a given interface. If no matching record is found, "inm" returns NULL.
+ */
+#define IN_LOOKUP_MULTI(addr, ifp, inm) \
+ /* struct in_addr addr; */ \
+ /* struct ifnet *ifp; */ \
+ /* struct in_multi *inm; */ \
+{ \
+ register struct in_ifaddr *ia; \
+\
+ IFP_TO_IA((ifp), ia); \
+ if (ia == NULL) \
+ (inm) = NULL; \
+ else \
+ for ((inm) = ia->ia_multiaddrs; \
+ (inm) != NULL && (inm)->inm_addr.s_addr != (addr).s_addr; \
+ (inm) = inm->inm_next) \
+ continue; \
+}
+
+/*
+ * Macro to step through all of the in_multi records, one at a time.
+ * The current position is remembered in "step", which the caller must
+ * provide. IN_FIRST_MULTI(), below, must be called to initialize "step"
+ * and get the first record. Both macros return a NULL "inm" when there
+ * are no remaining records.
+ */
+#define IN_NEXT_MULTI(step, inm) \
+ /* struct in_multistep step; */ \
+ /* struct in_multi *inm; */ \
+{ \
+ if (((inm) = (step).i_inm) != NULL) \
+ (step).i_inm = (inm)->inm_next; \
+ else \
+ while ((step).i_ia != NULL) { \
+ (inm) = (step).i_ia->ia_multiaddrs; \
+ (step).i_ia = (step).i_ia->ia_next; \
+ if ((inm) != NULL) { \
+ (step).i_inm = (inm)->inm_next; \
+ break; \
+ } \
+ } \
+}
+
+#define IN_FIRST_MULTI(step, inm) \
+ /* struct in_multistep step; */ \
+ /* struct in_multi *inm; */ \
+{ \
+ (step).i_ia = in_ifaddr; \
+ (step).i_inm = NULL; \
+ IN_NEXT_MULTI((step), (inm)); \
+}
+
+int in_ifinit __P((struct ifnet *,
+ struct in_ifaddr *, struct sockaddr_in *, int));
+struct in_multi *in_addmulti __P((struct in_addr *, struct ifnet *));
+void in_delmulti __P((struct in_multi *));
+void in_ifscrub __P((struct ifnet *, struct in_ifaddr *));
+int in_control __P((struct socket *, int, caddr_t, struct ifnet *));
+#endif
diff --git a/sys/netinet/ip.h b/sys/netinet/ip.h
new file mode 100644
index 0000000..8a31dfa
--- /dev/null
+++ b/sys/netinet/ip.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Definitions for internet protocol version 4.
+ * Per RFC 791, September 1981.
+ */
+#define IPVERSION 4
+
+/*
+ * Structure of an internet header, naked of options.
+ *
+ * We declare ip_len and ip_off to be short, rather than u_short
+ * pragmatically since otherwise unsigned comparisons can result
+ * against negative integers quite easily, and fail in subtle ways.
+ */
+struct ip {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ u_char ip_hl:4, /* header length */
+ ip_v:4; /* version */
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ u_char ip_v:4, /* version */
+ ip_hl:4; /* header length */
+#endif
+ u_char ip_tos; /* type of service */
+ short ip_len; /* total length */
+ u_short ip_id; /* identification */
+ short ip_off; /* fragment offset field */
+#define IP_DF 0x4000 /* dont fragment flag */
+#define IP_MF 0x2000 /* more fragments flag */
+#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
+ u_char ip_ttl; /* time to live */
+ u_char ip_p; /* protocol */
+ u_short ip_sum; /* checksum */
+ struct in_addr ip_src,ip_dst; /* source and dest address */
+};
+
+#define IP_MAXPACKET 65535 /* maximum packet size */
+
+/*
+ * Definitions for IP type of service (ip_tos)
+ */
+#define IPTOS_LOWDELAY 0x10
+#define IPTOS_THROUGHPUT 0x08
+#define IPTOS_RELIABILITY 0x04
+
+/*
+ * Definitions for IP precedence (also in ip_tos) (hopefully unused)
+ */
+#define IPTOS_PREC_NETCONTROL 0xe0
+#define IPTOS_PREC_INTERNETCONTROL 0xc0
+#define IPTOS_PREC_CRITIC_ECP 0xa0
+#define IPTOS_PREC_FLASHOVERRIDE 0x80
+#define IPTOS_PREC_FLASH 0x60
+#define IPTOS_PREC_IMMEDIATE 0x40
+#define IPTOS_PREC_PRIORITY 0x20
+#define IPTOS_PREC_ROUTINE 0x10
+
+/*
+ * Definitions for options.
+ */
+#define IPOPT_COPIED(o) ((o)&0x80)
+#define IPOPT_CLASS(o) ((o)&0x60)
+#define IPOPT_NUMBER(o) ((o)&0x1f)
+
+#define IPOPT_CONTROL 0x00
+#define IPOPT_RESERVED1 0x20
+#define IPOPT_DEBMEAS 0x40
+#define IPOPT_RESERVED2 0x60
+
+#define IPOPT_EOL 0 /* end of option list */
+#define IPOPT_NOP 1 /* no operation */
+
+#define IPOPT_RR 7 /* record packet route */
+#define IPOPT_TS 68 /* timestamp */
+#define IPOPT_SECURITY 130 /* provide s,c,h,tcc */
+#define IPOPT_LSRR 131 /* loose source route */
+#define IPOPT_SATID 136 /* satnet id */
+#define IPOPT_SSRR 137 /* strict source route */
+
+/*
+ * Offsets to fields in options other than EOL and NOP.
+ */
+#define IPOPT_OPTVAL 0 /* option ID */
+#define IPOPT_OLEN 1 /* option length */
+#define IPOPT_OFFSET 2 /* offset within option */
+#define IPOPT_MINOFF 4 /* min value of above */
+
+/*
+ * Time stamp option structure.
+ */
+struct ip_timestamp {
+ u_char ipt_code; /* IPOPT_TS */
+ u_char ipt_len; /* size of structure (variable) */
+ u_char ipt_ptr; /* index of current entry */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ u_char ipt_flg:4, /* flags, see below */
+ ipt_oflw:4; /* overflow counter */
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ u_char ipt_oflw:4, /* overflow counter */
+ ipt_flg:4; /* flags, see below */
+#endif
+ union ipt_timestamp {
+ n_long ipt_time[1];
+ struct ipt_ta {
+ struct in_addr ipt_addr;
+ n_long ipt_time;
+ } ipt_ta[1];
+ } ipt_timestamp;
+};
+
+/* flag bits for ipt_flg */
+#define IPOPT_TS_TSONLY 0 /* timestamps only */
+#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */
+#define IPOPT_TS_PRESPEC 3 /* specified modules only */
+
+/* bits for security (not byte swapped) */
+#define IPOPT_SECUR_UNCLASS 0x0000
+#define IPOPT_SECUR_CONFID 0xf135
+#define IPOPT_SECUR_EFTO 0x789a
+#define IPOPT_SECUR_MMMM 0xbc4d
+#define IPOPT_SECUR_RESTR 0xaf13
+#define IPOPT_SECUR_SECRET 0xd788
+#define IPOPT_SECUR_TOPSECRET 0x6bc5
+
+/*
+ * Internet implementation parameters.
+ */
+#define MAXTTL 255 /* maximum time to live (seconds) */
+#define IPDEFTTL 64 /* default ttl, from RFC 1340 */
+#define IPFRAGTTL 60 /* time to live for frags, slowhz */
+#define IPTTLDEC 1 /* subtracted when forwarding */
+
+#define IP_MSS 576 /* default maximum segment size */
diff --git a/sys/netinet/ip_icmp.c b/sys/netinet/ip_icmp.c
new file mode 100644
index 0000000..c9b82bc
--- /dev/null
+++ b/sys/netinet/ip_icmp.c
@@ -0,0 +1,591 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp_var.h>
+
+/*
+ * ICMP routines: error generation, receive packet processing, and
+ * routines to turnaround packets back to the originator, and
+ * host table maintenance routines.
+ */
+
+int icmpmaskrepl = 0;
+#ifdef ICMPPRINTFS
+int icmpprintfs = 0;
+#endif
+
+extern struct protosw inetsw[];
+
+/*
+ * Generate an error packet of type error
+ * in response to bad packet ip.
+ */
+void
+icmp_error(n, type, code, dest, destifp)
+ struct mbuf *n;
+ int type, code;
+ n_long dest;
+ struct ifnet *destifp;
+{
+ register struct ip *oip = mtod(n, struct ip *), *nip;
+ register unsigned oiplen = oip->ip_hl << 2;
+ register struct icmp *icp;
+ register struct mbuf *m;
+ unsigned icmplen;
+
+#ifdef ICMPPRINTFS
+ if (icmpprintfs)
+ printf("icmp_error(%x, %d, %d)\n", oip, type, code);
+#endif
+ if (type != ICMP_REDIRECT)
+ icmpstat.icps_error++;
+ /*
+ * Don't send error if not the first fragment of message.
+ * Don't error if the old packet protocol was ICMP
+ * error message, only known informational types.
+ */
+ if (oip->ip_off &~ (IP_MF|IP_DF))
+ goto freeit;
+ if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT &&
+ n->m_len >= oiplen + ICMP_MINLEN &&
+ !ICMP_INFOTYPE(((struct icmp *)((caddr_t)oip + oiplen))->icmp_type)) {
+ icmpstat.icps_oldicmp++;
+ goto freeit;
+ }
+ /* Don't send error in response to a multicast or broadcast packet */
+ if (n->m_flags & (M_BCAST|M_MCAST))
+ goto freeit;
+ /*
+ * First, formulate icmp message
+ */
+ m = m_gethdr(M_DONTWAIT, MT_HEADER);
+ if (m == NULL)
+ goto freeit;
+ icmplen = oiplen + min(8, oip->ip_len);
+ m->m_len = icmplen + ICMP_MINLEN;
+ MH_ALIGN(m, m->m_len);
+ icp = mtod(m, struct icmp *);
+ if ((u_int)type > ICMP_MAXTYPE)
+ panic("icmp_error");
+ icmpstat.icps_outhist[type]++;
+ icp->icmp_type = type;
+ if (type == ICMP_REDIRECT)
+ icp->icmp_gwaddr.s_addr = dest;
+ else {
+ icp->icmp_void = 0;
+ /*
+ * The following assignments assume an overlay with the
+ * zeroed icmp_void field.
+ */
+ if (type == ICMP_PARAMPROB) {
+ icp->icmp_pptr = code;
+ code = 0;
+ } else if (type == ICMP_UNREACH &&
+ code == ICMP_UNREACH_NEEDFRAG && destifp) {
+ icp->icmp_nextmtu = htons(destifp->if_mtu);
+ }
+ }
+
+ icp->icmp_code = code;
+ bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen);
+ nip = &icp->icmp_ip;
+ nip->ip_len = htons((u_short)(nip->ip_len + oiplen));
+
+ /*
+ * Now, copy old ip header (without options)
+ * in front of icmp message.
+ */
+ if (m->m_data - sizeof(struct ip) < m->m_pktdat)
+ panic("icmp len");
+ m->m_data -= sizeof(struct ip);
+ m->m_len += sizeof(struct ip);
+ m->m_pkthdr.len = m->m_len;
+ m->m_pkthdr.rcvif = n->m_pkthdr.rcvif;
+ nip = mtod(m, struct ip *);
+ bcopy((caddr_t)oip, (caddr_t)nip, sizeof(struct ip));
+ nip->ip_len = m->m_len;
+ nip->ip_hl = sizeof(struct ip) >> 2;
+ nip->ip_p = IPPROTO_ICMP;
+ nip->ip_tos = 0;
+ icmp_reflect(m);
+
+freeit:
+ m_freem(n);
+}
+
+static struct sockaddr_in icmpsrc = { sizeof (struct sockaddr_in), AF_INET };
+static struct sockaddr_in icmpdst = { sizeof (struct sockaddr_in), AF_INET };
+static struct sockaddr_in icmpgw = { sizeof (struct sockaddr_in), AF_INET };
+struct sockaddr_in icmpmask = { 8, 0 };
+
+/*
+ * Process a received ICMP message.
+ */
+void
+icmp_input(m, hlen)
+ register struct mbuf *m;
+ int hlen;
+{
+ register struct icmp *icp;
+ register struct ip *ip = mtod(m, struct ip *);
+ int icmplen = ip->ip_len;
+ register int i;
+ struct in_ifaddr *ia;
+ void (*ctlfunc) __P((int, struct sockaddr *, struct ip *));
+ int code;
+ extern u_char ip_protox[];
+
+ /*
+ * Locate icmp structure in mbuf, and check
+ * that not corrupted and of at least minimum length.
+ */
+#ifdef ICMPPRINTFS
+ if (icmpprintfs)
+ printf("icmp_input from %x to %x, len %d\n",
+ ntohl(ip->ip_src.s_addr), ntohl(ip->ip_dst.s_addr),
+ icmplen);
+#endif
+ if (icmplen < ICMP_MINLEN) {
+ icmpstat.icps_tooshort++;
+ goto freeit;
+ }
+ i = hlen + min(icmplen, ICMP_ADVLENMIN);
+ if (m->m_len < i && (m = m_pullup(m, i)) == 0) {
+ icmpstat.icps_tooshort++;
+ return;
+ }
+ ip = mtod(m, struct ip *);
+ m->m_len -= hlen;
+ m->m_data += hlen;
+ icp = mtod(m, struct icmp *);
+ if (in_cksum(m, icmplen)) {
+ icmpstat.icps_checksum++;
+ goto freeit;
+ }
+ m->m_len += hlen;
+ m->m_data -= hlen;
+
+#ifdef ICMPPRINTFS
+ /*
+ * Message type specific processing.
+ */
+ if (icmpprintfs)
+ printf("icmp_input, type %d code %d\n", icp->icmp_type,
+ icp->icmp_code);
+#endif
+ if (icp->icmp_type > ICMP_MAXTYPE)
+ goto raw;
+ icmpstat.icps_inhist[icp->icmp_type]++;
+ code = icp->icmp_code;
+ switch (icp->icmp_type) {
+
+ case ICMP_UNREACH:
+ switch (code) {
+ case ICMP_UNREACH_NET:
+ case ICMP_UNREACH_HOST:
+ case ICMP_UNREACH_PROTOCOL:
+ case ICMP_UNREACH_PORT:
+ case ICMP_UNREACH_SRCFAIL:
+ code += PRC_UNREACH_NET;
+ break;
+
+ case ICMP_UNREACH_NEEDFRAG:
+ code = PRC_MSGSIZE;
+ break;
+
+ case ICMP_UNREACH_NET_UNKNOWN:
+ case ICMP_UNREACH_NET_PROHIB:
+ case ICMP_UNREACH_TOSNET:
+ code = PRC_UNREACH_NET;
+ break;
+
+ case ICMP_UNREACH_HOST_UNKNOWN:
+ case ICMP_UNREACH_ISOLATED:
+ case ICMP_UNREACH_HOST_PROHIB:
+ case ICMP_UNREACH_TOSHOST:
+ code = PRC_UNREACH_HOST;
+ break;
+
+ default:
+ goto badcode;
+ }
+ goto deliver;
+
+ case ICMP_TIMXCEED:
+ if (code > 1)
+ goto badcode;
+ code += PRC_TIMXCEED_INTRANS;
+ goto deliver;
+
+ case ICMP_PARAMPROB:
+ if (code > 1)
+ goto badcode;
+ code = PRC_PARAMPROB;
+ goto deliver;
+
+ case ICMP_SOURCEQUENCH:
+ if (code)
+ goto badcode;
+ code = PRC_QUENCH;
+ deliver:
+ /*
+ * Problem with datagram; advise higher level routines.
+ */
+ if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) ||
+ icp->icmp_ip.ip_hl < (sizeof(struct ip) >> 2)) {
+ icmpstat.icps_badlen++;
+ goto freeit;
+ }
+ NTOHS(icp->icmp_ip.ip_len);
+#ifdef ICMPPRINTFS
+ if (icmpprintfs)
+ printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
+#endif
+ icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
+ if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
+ (*ctlfunc)(code, (struct sockaddr *)&icmpsrc,
+ &icp->icmp_ip);
+ break;
+
+ badcode:
+ icmpstat.icps_badcode++;
+ break;
+
+ case ICMP_ECHO:
+ icp->icmp_type = ICMP_ECHOREPLY;
+ goto reflect;
+
+ case ICMP_TSTAMP:
+ if (icmplen < ICMP_TSLEN) {
+ icmpstat.icps_badlen++;
+ break;
+ }
+ icp->icmp_type = ICMP_TSTAMPREPLY;
+ icp->icmp_rtime = iptime();
+ icp->icmp_ttime = icp->icmp_rtime; /* bogus, do later! */
+ goto reflect;
+
+ case ICMP_MASKREQ:
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+ if (icmpmaskrepl == 0)
+ break;
+ /*
+ * We are not able to respond with all ones broadcast
+ * unless we receive it over a point-to-point interface.
+ */
+ if (icmplen < ICMP_MASKLEN)
+ break;
+ switch (ip->ip_dst.s_addr) {
+
+ case INADDR_BROADCAST:
+ case INADDR_ANY:
+ icmpdst.sin_addr = ip->ip_src;
+ break;
+
+ default:
+ icmpdst.sin_addr = ip->ip_dst;
+ }
+ ia = (struct in_ifaddr *)ifaof_ifpforaddr(
+ (struct sockaddr *)&icmpdst, m->m_pkthdr.rcvif);
+ if (ia == 0)
+ break;
+ icp->icmp_type = ICMP_MASKREPLY;
+ icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr;
+ if (ip->ip_src.s_addr == 0) {
+ if (ia->ia_ifp->if_flags & IFF_BROADCAST)
+ ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr;
+ else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT)
+ ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr;
+ }
+reflect:
+ ip->ip_len += hlen; /* since ip_input deducts this */
+ icmpstat.icps_reflect++;
+ icmpstat.icps_outhist[icp->icmp_type]++;
+ icmp_reflect(m);
+ return;
+
+ case ICMP_REDIRECT:
+ if (code > 3)
+ goto badcode;
+ if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) ||
+ icp->icmp_ip.ip_hl < (sizeof(struct ip) >> 2)) {
+ icmpstat.icps_badlen++;
+ break;
+ }
+ /*
+ * Short circuit routing redirects to force
+ * immediate change in the kernel's routing
+ * tables. The message is also handed to anyone
+ * listening on a raw socket (e.g. the routing
+ * daemon for use in updating its tables).
+ */
+ icmpgw.sin_addr = ip->ip_src;
+ icmpdst.sin_addr = icp->icmp_gwaddr;
+#ifdef ICMPPRINTFS
+ if (icmpprintfs)
+ printf("redirect dst %x to %x\n", icp->icmp_ip.ip_dst,
+ icp->icmp_gwaddr);
+#endif
+ icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
+ rtredirect((struct sockaddr *)&icmpsrc,
+ (struct sockaddr *)&icmpdst,
+ (struct sockaddr *)0, RTF_GATEWAY | RTF_HOST,
+ (struct sockaddr *)&icmpgw, (struct rtentry **)0);
+ pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&icmpsrc);
+ break;
+
+ /*
+ * No kernel processing for the following;
+ * just fall through to send to raw listener.
+ */
+ case ICMP_ECHOREPLY:
+ case ICMP_ROUTERADVERT:
+ case ICMP_ROUTERSOLICIT:
+ case ICMP_TSTAMPREPLY:
+ case ICMP_IREQREPLY:
+ case ICMP_MASKREPLY:
+ default:
+ break;
+ }
+
+raw:
+ rip_input(m);
+ return;
+
+freeit:
+ m_freem(m);
+}
+
+/*
+ * Reflect the ip packet back to the source
+ */
+void
+icmp_reflect(m)
+ struct mbuf *m;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register struct in_ifaddr *ia;
+ struct in_addr t;
+ struct mbuf *opts = 0, *ip_srcroute();
+ int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
+
+ if (!in_canforward(ip->ip_src) &&
+ ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) !=
+ (IN_LOOPBACKNET << IN_CLASSA_NSHIFT))) {
+ m_freem(m); /* Bad return address */
+ goto done; /* Ip_output() will check for broadcast */
+ }
+ t = ip->ip_dst;
+ ip->ip_dst = ip->ip_src;
+ /*
+ * If the incoming packet was addressed directly to us,
+ * use dst as the src for the reply. Otherwise (broadcast
+ * or anonymous), use the address which corresponds
+ * to the incoming interface.
+ */
+ for (ia = in_ifaddr; ia; ia = ia->ia_next) {
+ if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr)
+ break;
+ if ((ia->ia_ifp->if_flags & IFF_BROADCAST) &&
+ t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr)
+ break;
+ }
+ icmpdst.sin_addr = t;
+ if (ia == (struct in_ifaddr *)0)
+ ia = (struct in_ifaddr *)ifaof_ifpforaddr(
+ (struct sockaddr *)&icmpdst, m->m_pkthdr.rcvif);
+ /*
+ * The following happens if the packet was not addressed to us,
+ * and was received on an interface with no IP address.
+ */
+ if (ia == (struct in_ifaddr *)0)
+ ia = in_ifaddr;
+ t = IA_SIN(ia)->sin_addr;
+ ip->ip_src = t;
+ ip->ip_ttl = MAXTTL;
+
+ if (optlen > 0) {
+ register u_char *cp;
+ int opt, cnt;
+ u_int len;
+
+ /*
+ * Retrieve any source routing from the incoming packet;
+ * add on any record-route or timestamp options.
+ */
+ cp = (u_char *) (ip + 1);
+ if ((opts = ip_srcroute()) == 0 &&
+ (opts = m_gethdr(M_DONTWAIT, MT_HEADER))) {
+ opts->m_len = sizeof(struct in_addr);
+ mtod(opts, struct in_addr *)->s_addr = 0;
+ }
+ if (opts) {
+#ifdef ICMPPRINTFS
+ if (icmpprintfs)
+ printf("icmp_reflect optlen %d rt %d => ",
+ optlen, opts->m_len);
+#endif
+ for (cnt = optlen; cnt > 0; cnt -= len, cp += len) {
+ opt = cp[IPOPT_OPTVAL];
+ if (opt == IPOPT_EOL)
+ break;
+ if (opt == IPOPT_NOP)
+ len = 1;
+ else {
+ len = cp[IPOPT_OLEN];
+ if (len <= 0 || len > cnt)
+ break;
+ }
+ /*
+ * Should check for overflow, but it "can't happen"
+ */
+ if (opt == IPOPT_RR || opt == IPOPT_TS ||
+ opt == IPOPT_SECURITY) {
+ bcopy((caddr_t)cp,
+ mtod(opts, caddr_t) + opts->m_len, len);
+ opts->m_len += len;
+ }
+ }
+ /* Terminate & pad, if necessary */
+ if (cnt = opts->m_len % 4) {
+ for (; cnt < 4; cnt++) {
+ *(mtod(opts, caddr_t) + opts->m_len) =
+ IPOPT_EOL;
+ opts->m_len++;
+ }
+ }
+#ifdef ICMPPRINTFS
+ if (icmpprintfs)
+ printf("%d\n", opts->m_len);
+#endif
+ }
+ /*
+ * Now strip out original options by copying rest of first
+ * mbuf's data back, and adjust the IP length.
+ */
+ ip->ip_len -= optlen;
+ ip->ip_hl = sizeof(struct ip) >> 2;
+ m->m_len -= optlen;
+ if (m->m_flags & M_PKTHDR)
+ m->m_pkthdr.len -= optlen;
+ optlen += sizeof(struct ip);
+ bcopy((caddr_t)ip + optlen, (caddr_t)(ip + 1),
+ (unsigned)(m->m_len - sizeof(struct ip)));
+ }
+ m->m_flags &= ~(M_BCAST|M_MCAST);
+ icmp_send(m, opts);
+done:
+ if (opts)
+ (void)m_free(opts);
+}
+
+/*
+ * Send an icmp packet back to the ip level,
+ * after supplying a checksum.
+ */
+void
+icmp_send(m, opts)
+ register struct mbuf *m;
+ struct mbuf *opts;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register int hlen;
+ register struct icmp *icp;
+
+ hlen = ip->ip_hl << 2;
+ m->m_data += hlen;
+ m->m_len -= hlen;
+ icp = mtod(m, struct icmp *);
+ icp->icmp_cksum = 0;
+ icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
+ m->m_data -= hlen;
+ m->m_len += hlen;
+#ifdef ICMPPRINTFS
+ if (icmpprintfs)
+ printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src);
+#endif
+ (void) ip_output(m, opts, NULL, 0, NULL);
+}
+
+n_time
+iptime()
+{
+ struct timeval atv;
+ u_long t;
+
+ microtime(&atv);
+ t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000;
+ return (htonl(t));
+}
+
+int
+icmp_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+{
+
+ /* All sysctl names at this level are terminal. */
+ if (namelen != 1)
+ return (ENOTDIR);
+
+ switch (name[0]) {
+ case ICMPCTL_MASKREPL:
+ return (sysctl_int(oldp, oldlenp, newp, newlen, &icmpmaskrepl));
+ default:
+ return (ENOPROTOOPT);
+ }
+ /* NOTREACHED */
+}
diff --git a/sys/netinet/ip_icmp.h b/sys/netinet/ip_icmp.h
new file mode 100644
index 0000000..3c3462d
--- /dev/null
+++ b/sys/netinet/ip_icmp.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Interface Control Message Protocol Definitions.
+ * Per RFC 792, September 1981.
+ */
+
+/*
+ * Structure of an icmp header.
+ */
+struct icmp {
+ u_char icmp_type; /* type of message, see below */
+ u_char icmp_code; /* type sub code */
+ u_short icmp_cksum; /* ones complement cksum of struct */
+ union {
+ u_char ih_pptr; /* ICMP_PARAMPROB */
+ struct in_addr ih_gwaddr; /* ICMP_REDIRECT */
+ struct ih_idseq {
+ n_short icd_id;
+ n_short icd_seq;
+ } ih_idseq;
+ int ih_void;
+
+ /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
+ struct ih_pmtu {
+ n_short ipm_void;
+ n_short ipm_nextmtu;
+ } ih_pmtu;
+ } icmp_hun;
+#define icmp_pptr icmp_hun.ih_pptr
+#define icmp_gwaddr icmp_hun.ih_gwaddr
+#define icmp_id icmp_hun.ih_idseq.icd_id
+#define icmp_seq icmp_hun.ih_idseq.icd_seq
+#define icmp_void icmp_hun.ih_void
+#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
+#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu
+ union {
+ struct id_ts {
+ n_time its_otime;
+ n_time its_rtime;
+ n_time its_ttime;
+ } id_ts;
+ struct id_ip {
+ struct ip idi_ip;
+ /* options and then 64 bits of data */
+ } id_ip;
+ u_long id_mask;
+ char id_data[1];
+ } icmp_dun;
+#define icmp_otime icmp_dun.id_ts.its_otime
+#define icmp_rtime icmp_dun.id_ts.its_rtime
+#define icmp_ttime icmp_dun.id_ts.its_ttime
+#define icmp_ip icmp_dun.id_ip.idi_ip
+#define icmp_mask icmp_dun.id_mask
+#define icmp_data icmp_dun.id_data
+};
+
+/*
+ * Lower bounds on packet lengths for various types.
+ * For the error advice packets must first insure that the
+ * packet is large enought to contain the returned ip header.
+ * Only then can we do the check to see if 64 bits of packet
+ * data have been returned, since we need to check the returned
+ * ip header length.
+ */
+#define ICMP_MINLEN 8 /* abs minimum */
+#define ICMP_TSLEN (8 + 3 * sizeof (n_time)) /* timestamp */
+#define ICMP_MASKLEN 12 /* address mask */
+#define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */
+#define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8)
+ /* N.B.: must separately check that ip_hl >= 5 */
+
+/*
+ * Definition of type and code field values.
+ */
+#define ICMP_ECHOREPLY 0 /* echo reply */
+#define ICMP_UNREACH 3 /* dest unreachable, codes: */
+#define ICMP_UNREACH_NET 0 /* bad net */
+#define ICMP_UNREACH_HOST 1 /* bad host */
+#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */
+#define ICMP_UNREACH_PORT 3 /* bad port */
+#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */
+#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */
+#define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */
+#define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */
+#define ICMP_UNREACH_ISOLATED 8 /* src host isolated */
+#define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */
+#define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */
+#define ICMP_UNREACH_TOSNET 11 /* bad tos for net */
+#define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */
+#define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */
+#define ICMP_REDIRECT 5 /* shorter route, codes: */
+#define ICMP_REDIRECT_NET 0 /* for network */
+#define ICMP_REDIRECT_HOST 1 /* for host */
+#define ICMP_REDIRECT_TOSNET 2 /* for tos and net */
+#define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */
+#define ICMP_ECHO 8 /* echo service */
+#define ICMP_ROUTERADVERT 9 /* router advertisement */
+#define ICMP_ROUTERSOLICIT 10 /* router solicitation */
+#define ICMP_TIMXCEED 11 /* time exceeded, code: */
+#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */
+#define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */
+#define ICMP_PARAMPROB 12 /* ip header bad */
+#define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */
+#define ICMP_TSTAMP 13 /* timestamp request */
+#define ICMP_TSTAMPREPLY 14 /* timestamp reply */
+#define ICMP_IREQ 15 /* information request */
+#define ICMP_IREQREPLY 16 /* information reply */
+#define ICMP_MASKREQ 17 /* address mask request */
+#define ICMP_MASKREPLY 18 /* address mask reply */
+
+#define ICMP_MAXTYPE 18
+
+#define ICMP_INFOTYPE(type) \
+ ((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \
+ (type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \
+ (type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \
+ (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \
+ (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY)
+
+#ifdef KERNEL
+void icmp_error __P((struct mbuf *, int, int, n_long, struct ifnet *));
+void icmp_input __P((struct mbuf *, int));
+void icmp_reflect __P((struct mbuf *));
+void icmp_send __P((struct mbuf *, struct mbuf *));
+int icmp_sysctl __P((int *, u_int, void *, size_t *, void *, size_t));
+#endif
diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c
new file mode 100644
index 0000000..c720ff4
--- /dev/null
+++ b/sys/netinet/ip_input.c
@@ -0,0 +1,1166 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_input.c 8.2 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+
+#ifndef IPFORWARDING
+#ifdef GATEWAY
+#define IPFORWARDING 1 /* forward IP packets not for us */
+#else /* GATEWAY */
+#define IPFORWARDING 0 /* don't forward IP packets not for us */
+#endif /* GATEWAY */
+#endif /* IPFORWARDING */
+#ifndef IPSENDREDIRECTS
+#define IPSENDREDIRECTS 1
+#endif
+int ipforwarding = IPFORWARDING;
+int ipsendredirects = IPSENDREDIRECTS;
+int ip_defttl = IPDEFTTL;
+#ifdef DIAGNOSTIC
+int ipprintfs = 0;
+#endif
+
+extern struct domain inetdomain;
+extern struct protosw inetsw[];
+u_char ip_protox[IPPROTO_MAX];
+int ipqmaxlen = IFQ_MAXLEN;
+struct in_ifaddr *in_ifaddr; /* first inet address */
+struct ifqueue ipintrq;
+
+/*
+ * We need to save the IP options in case a protocol wants to respond
+ * to an incoming packet over the same route if the packet got here
+ * using IP source routing. This allows connection establishment and
+ * maintenance when the remote end is on a network that is not known
+ * to us.
+ */
+int ip_nhops = 0;
+static struct ip_srcrt {
+ struct in_addr dst; /* final destination */
+ char nop; /* one NOP to align */
+ char srcopt[IPOPT_OFFSET + 1]; /* OPTVAL, OLEN and OFFSET */
+ struct in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)];
+} ip_srcrt;
+
+#ifdef GATEWAY
+extern int if_index;
+u_long *ip_ifmatrix;
+#endif
+
+static void save_rte __P((u_char *, struct in_addr));
+/*
+ * IP initialization: fill in IP protocol switch table.
+ * All protocols not implemented in kernel go to raw IP protocol handler.
+ */
+void
+ip_init()
+{
+ register struct protosw *pr;
+ register int i;
+
+ pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW);
+ if (pr == 0)
+ panic("ip_init");
+ for (i = 0; i < IPPROTO_MAX; i++)
+ ip_protox[i] = pr - inetsw;
+ for (pr = inetdomain.dom_protosw;
+ pr < inetdomain.dom_protoswNPROTOSW; pr++)
+ if (pr->pr_domain->dom_family == PF_INET &&
+ pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
+ ip_protox[pr->pr_protocol] = pr - inetsw;
+ ipq.next = ipq.prev = &ipq;
+ ip_id = time.tv_sec & 0xffff;
+ ipintrq.ifq_maxlen = ipqmaxlen;
+#ifdef GATEWAY
+ i = (if_index + 1) * (if_index + 1) * sizeof (u_long);
+ ip_ifmatrix = (u_long *) malloc(i, M_RTABLE, M_WAITOK);
+ bzero((char *)ip_ifmatrix, i);
+#endif
+}
+
+struct sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
+struct route ipforward_rt;
+
+/*
+ * Ip input routine. Checksum and byte swap header. If fragmented
+ * try to reassemble. Process options. Pass to next level.
+ */
+void
+ipintr()
+{
+ register struct ip *ip;
+ register struct mbuf *m;
+ register struct ipq *fp;
+ register struct in_ifaddr *ia;
+ int hlen, s;
+
+next:
+ /*
+ * Get next datagram off input queue and get IP header
+ * in first mbuf.
+ */
+ s = splimp();
+ IF_DEQUEUE(&ipintrq, m);
+ splx(s);
+ if (m == 0)
+ return;
+#ifdef DIAGNOSTIC
+ if ((m->m_flags & M_PKTHDR) == 0)
+ panic("ipintr no HDR");
+#endif
+ /*
+ * If no IP addresses have been set yet but the interfaces
+ * are receiving, can't do anything with incoming packets yet.
+ */
+ if (in_ifaddr == NULL)
+ goto bad;
+ ipstat.ips_total++;
+ if (m->m_len < sizeof (struct ip) &&
+ (m = m_pullup(m, sizeof (struct ip))) == 0) {
+ ipstat.ips_toosmall++;
+ goto next;
+ }
+ ip = mtod(m, struct ip *);
+ if (ip->ip_v != IPVERSION) {
+ ipstat.ips_badvers++;
+ goto bad;
+ }
+ hlen = ip->ip_hl << 2;
+ if (hlen < sizeof(struct ip)) { /* minimum header length */
+ ipstat.ips_badhlen++;
+ goto bad;
+ }
+ if (hlen > m->m_len) {
+ if ((m = m_pullup(m, hlen)) == 0) {
+ ipstat.ips_badhlen++;
+ goto next;
+ }
+ ip = mtod(m, struct ip *);
+ }
+ if (ip->ip_sum = in_cksum(m, hlen)) {
+ ipstat.ips_badsum++;
+ goto bad;
+ }
+
+ /*
+ * Convert fields to host representation.
+ */
+ NTOHS(ip->ip_len);
+ if (ip->ip_len < hlen) {
+ ipstat.ips_badlen++;
+ goto bad;
+ }
+ NTOHS(ip->ip_id);
+ NTOHS(ip->ip_off);
+
+ /*
+ * Check that the amount of data in the buffers
+ * is as at least much as the IP header would have us expect.
+ * Trim mbufs if longer than we expect.
+ * Drop packet if shorter than we expect.
+ */
+ if (m->m_pkthdr.len < ip->ip_len) {
+ ipstat.ips_tooshort++;
+ goto bad;
+ }
+ if (m->m_pkthdr.len > ip->ip_len) {
+ if (m->m_len == m->m_pkthdr.len) {
+ m->m_len = ip->ip_len;
+ m->m_pkthdr.len = ip->ip_len;
+ } else
+ m_adj(m, ip->ip_len - m->m_pkthdr.len);
+ }
+
+ /*
+ * Process options and, if not destined for us,
+ * ship it on. ip_dooptions returns 1 when an
+ * error was detected (causing an icmp message
+ * to be sent and the original packet to be freed).
+ */
+ ip_nhops = 0; /* for source routed packets */
+ if (hlen > sizeof (struct ip) && ip_dooptions(m))
+ goto next;
+
+ /*
+ * Check our list of addresses, to see if the packet is for us.
+ */
+ for (ia = in_ifaddr; ia; ia = ia->ia_next) {
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+
+ if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr)
+ goto ours;
+ if (
+#ifdef DIRECTED_BROADCAST
+ ia->ia_ifp == m->m_pkthdr.rcvif &&
+#endif
+ (ia->ia_ifp->if_flags & IFF_BROADCAST)) {
+ u_long t;
+
+ if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
+ ip->ip_dst.s_addr)
+ goto ours;
+ if (ip->ip_dst.s_addr == ia->ia_netbroadcast.s_addr)
+ goto ours;
+ /*
+ * Look for all-0's host part (old broadcast addr),
+ * either for subnet or net.
+ */
+ t = ntohl(ip->ip_dst.s_addr);
+ if (t == ia->ia_subnet)
+ goto ours;
+ if (t == ia->ia_net)
+ goto ours;
+ }
+ }
+ if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
+ struct in_multi *inm;
+#ifdef MROUTING
+ extern struct socket *ip_mrouter;
+
+ if (ip_mrouter) {
+ /*
+ * If we are acting as a multicast router, all
+ * incoming multicast packets are passed to the
+ * kernel-level multicast forwarding function.
+ * The packet is returned (relatively) intact; if
+ * ip_mforward() returns a non-zero value, the packet
+ * must be discarded, else it may be accepted below.
+ *
+ * (The IP ident field is put in the same byte order
+ * as expected when ip_mforward() is called from
+ * ip_output().)
+ */
+ ip->ip_id = htons(ip->ip_id);
+ if (ip_mforward(m, m->m_pkthdr.rcvif) != 0) {
+ ipstat.ips_cantforward++;
+ m_freem(m);
+ goto next;
+ }
+ ip->ip_id = ntohs(ip->ip_id);
+
+ /*
+ * The process-level routing demon needs to receive
+ * all multicast IGMP packets, whether or not this
+ * host belongs to their destination groups.
+ */
+ if (ip->ip_p == IPPROTO_IGMP)
+ goto ours;
+ ipstat.ips_forward++;
+ }
+#endif
+ /*
+ * See if we belong to the destination multicast group on the
+ * arrival interface.
+ */
+ IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
+ if (inm == NULL) {
+ ipstat.ips_cantforward++;
+ m_freem(m);
+ goto next;
+ }
+ goto ours;
+ }
+ if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
+ goto ours;
+ if (ip->ip_dst.s_addr == INADDR_ANY)
+ goto ours;
+
+ /*
+ * Not for us; forward if possible and desirable.
+ */
+ if (ipforwarding == 0) {
+ ipstat.ips_cantforward++;
+ m_freem(m);
+ } else
+ ip_forward(m, 0);
+ goto next;
+
+ours:
+ /*
+ * If offset or IP_MF are set, must reassemble.
+ * Otherwise, nothing need be done.
+ * (We could look in the reassembly queue to see
+ * if the packet was previously fragmented,
+ * but it's not worth the time; just let them time out.)
+ */
+ if (ip->ip_off &~ IP_DF) {
+ if (m->m_flags & M_EXT) { /* XXX */
+ if ((m = m_pullup(m, sizeof (struct ip))) == 0) {
+ ipstat.ips_toosmall++;
+ goto next;
+ }
+ ip = mtod(m, struct ip *);
+ }
+ /*
+ * Look for queue of fragments
+ * of this datagram.
+ */
+ for (fp = ipq.next; fp != &ipq; fp = fp->next)
+ if (ip->ip_id == fp->ipq_id &&
+ ip->ip_src.s_addr == fp->ipq_src.s_addr &&
+ ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
+ ip->ip_p == fp->ipq_p)
+ goto found;
+ fp = 0;
+found:
+
+ /*
+ * Adjust ip_len to not reflect header,
+ * set ip_mff if more fragments are expected,
+ * convert offset of this to bytes.
+ */
+ ip->ip_len -= hlen;
+ ((struct ipasfrag *)ip)->ipf_mff &= ~1;
+ if (ip->ip_off & IP_MF)
+ ((struct ipasfrag *)ip)->ipf_mff |= 1;
+ ip->ip_off <<= 3;
+
+ /*
+ * If datagram marked as having more fragments
+ * or if this is not the first fragment,
+ * attempt reassembly; if it succeeds, proceed.
+ */
+ if (((struct ipasfrag *)ip)->ipf_mff & 1 || ip->ip_off) {
+ ipstat.ips_fragments++;
+ ip = ip_reass((struct ipasfrag *)ip, fp);
+ if (ip == 0)
+ goto next;
+ ipstat.ips_reassembled++;
+ m = dtom(ip);
+ } else
+ if (fp)
+ ip_freef(fp);
+ } else
+ ip->ip_len -= hlen;
+
+ /*
+ * Switch out to protocol's input routine.
+ */
+ ipstat.ips_delivered++;
+ (*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen);
+ goto next;
+bad:
+ m_freem(m);
+ goto next;
+}
+
+/*
+ * Take incoming datagram fragment and try to
+ * reassemble it into whole datagram. If a chain for
+ * reassembly of this datagram already exists, then it
+ * is given as fp; otherwise have to make a chain.
+ */
+struct ip *
+ip_reass(ip, fp)
+ register struct ipasfrag *ip;
+ register struct ipq *fp;
+{
+ register struct mbuf *m = dtom(ip);
+ register struct ipasfrag *q;
+ struct mbuf *t;
+ int hlen = ip->ip_hl << 2;
+ int i, next;
+
+ /*
+ * Presence of header sizes in mbufs
+ * would confuse code below.
+ */
+ m->m_data += hlen;
+ m->m_len -= hlen;
+
+ /*
+ * If first fragment to arrive, create a reassembly queue.
+ */
+ if (fp == 0) {
+ if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL)
+ goto dropfrag;
+ fp = mtod(t, struct ipq *);
+ insque(fp, &ipq);
+ fp->ipq_ttl = IPFRAGTTL;
+ fp->ipq_p = ip->ip_p;
+ fp->ipq_id = ip->ip_id;
+ fp->ipq_next = fp->ipq_prev = (struct ipasfrag *)fp;
+ fp->ipq_src = ((struct ip *)ip)->ip_src;
+ fp->ipq_dst = ((struct ip *)ip)->ip_dst;
+ q = (struct ipasfrag *)fp;
+ goto insert;
+ }
+
+ /*
+ * Find a segment which begins after this one does.
+ */
+ for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next)
+ if (q->ip_off > ip->ip_off)
+ break;
+
+ /*
+ * If there is a preceding segment, it may provide some of
+ * our data already. If so, drop the data from the incoming
+ * segment. If it provides all of our data, drop us.
+ */
+ if (q->ipf_prev != (struct ipasfrag *)fp) {
+ i = q->ipf_prev->ip_off + q->ipf_prev->ip_len - ip->ip_off;
+ if (i > 0) {
+ if (i >= ip->ip_len)
+ goto dropfrag;
+ m_adj(dtom(ip), i);
+ ip->ip_off += i;
+ ip->ip_len -= i;
+ }
+ }
+
+ /*
+ * While we overlap succeeding segments trim them or,
+ * if they are completely covered, dequeue them.
+ */
+ while (q != (struct ipasfrag *)fp && ip->ip_off + ip->ip_len > q->ip_off) {
+ i = (ip->ip_off + ip->ip_len) - q->ip_off;
+ if (i < q->ip_len) {
+ q->ip_len -= i;
+ q->ip_off += i;
+ m_adj(dtom(q), i);
+ break;
+ }
+ q = q->ipf_next;
+ m_freem(dtom(q->ipf_prev));
+ ip_deq(q->ipf_prev);
+ }
+
+insert:
+ /*
+ * Stick new segment in its place;
+ * check for complete reassembly.
+ */
+ ip_enq(ip, q->ipf_prev);
+ next = 0;
+ for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next) {
+ if (q->ip_off != next)
+ return (0);
+ next += q->ip_len;
+ }
+ if (q->ipf_prev->ipf_mff & 1)
+ return (0);
+
+ /*
+ * Reassembly is complete; concatenate fragments.
+ */
+ q = fp->ipq_next;
+ m = dtom(q);
+ t = m->m_next;
+ m->m_next = 0;
+ m_cat(m, t);
+ q = q->ipf_next;
+ while (q != (struct ipasfrag *)fp) {
+ t = dtom(q);
+ q = q->ipf_next;
+ m_cat(m, t);
+ }
+
+ /*
+ * Create header for new ip packet by
+ * modifying header of first packet;
+ * dequeue and discard fragment reassembly header.
+ * Make header visible.
+ */
+ ip = fp->ipq_next;
+ ip->ip_len = next;
+ ip->ipf_mff &= ~1;
+ ((struct ip *)ip)->ip_src = fp->ipq_src;
+ ((struct ip *)ip)->ip_dst = fp->ipq_dst;
+ remque(fp);
+ (void) m_free(dtom(fp));
+ m = dtom(ip);
+ m->m_len += (ip->ip_hl << 2);
+ m->m_data -= (ip->ip_hl << 2);
+ /* some debugging cruft by sklower, below, will go away soon */
+ if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */
+ register int plen = 0;
+ for (t = m; m; m = m->m_next)
+ plen += m->m_len;
+ t->m_pkthdr.len = plen;
+ }
+ return ((struct ip *)ip);
+
+dropfrag:
+ ipstat.ips_fragdropped++;
+ m_freem(m);
+ return (0);
+}
+
+/*
+ * Free a fragment reassembly header and all
+ * associated datagrams.
+ */
+void
+ip_freef(fp)
+ struct ipq *fp;
+{
+ register struct ipasfrag *q, *p;
+
+ for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = p) {
+ p = q->ipf_next;
+ ip_deq(q);
+ m_freem(dtom(q));
+ }
+ remque(fp);
+ (void) m_free(dtom(fp));
+}
+
+/*
+ * Put an ip fragment on a reassembly chain.
+ * Like insque, but pointers in middle of structure.
+ */
+void
+ip_enq(p, prev)
+ register struct ipasfrag *p, *prev;
+{
+
+ p->ipf_prev = prev;
+ p->ipf_next = prev->ipf_next;
+ prev->ipf_next->ipf_prev = p;
+ prev->ipf_next = p;
+}
+
+/*
+ * To ip_enq as remque is to insque.
+ */
+void
+ip_deq(p)
+ register struct ipasfrag *p;
+{
+
+ p->ipf_prev->ipf_next = p->ipf_next;
+ p->ipf_next->ipf_prev = p->ipf_prev;
+}
+
+/*
+ * IP timer processing;
+ * if a timer expires on a reassembly
+ * queue, discard it.
+ */
+void
+ip_slowtimo()
+{
+ register struct ipq *fp;
+ int s = splnet();
+
+ fp = ipq.next;
+ if (fp == 0) {
+ splx(s);
+ return;
+ }
+ while (fp != &ipq) {
+ --fp->ipq_ttl;
+ fp = fp->next;
+ if (fp->prev->ipq_ttl == 0) {
+ ipstat.ips_fragtimeout++;
+ ip_freef(fp->prev);
+ }
+ }
+ splx(s);
+}
+
+/*
+ * Drain off all datagram fragments.
+ */
+void
+ip_drain()
+{
+
+ while (ipq.next != &ipq) {
+ ipstat.ips_fragdropped++;
+ ip_freef(ipq.next);
+ }
+}
+
+/*
+ * Do option processing on a datagram,
+ * possibly discarding it if bad options are encountered,
+ * or forwarding it if source-routed.
+ * Returns 1 if packet has been forwarded/freed,
+ * 0 if the packet should be processed further.
+ */
+int
+ip_dooptions(m)
+ struct mbuf *m;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register u_char *cp;
+ register struct ip_timestamp *ipt;
+ register struct in_ifaddr *ia;
+ int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
+ struct in_addr *sin, dst;
+ n_time ntime;
+
+ dst = ip->ip_dst;
+ cp = (u_char *)(ip + 1);
+ cnt = (ip->ip_hl << 2) - sizeof (struct ip);
+ for (; cnt > 0; cnt -= optlen, cp += optlen) {
+ opt = cp[IPOPT_OPTVAL];
+ if (opt == IPOPT_EOL)
+ break;
+ if (opt == IPOPT_NOP)
+ optlen = 1;
+ else {
+ optlen = cp[IPOPT_OLEN];
+ if (optlen <= 0 || optlen > cnt) {
+ code = &cp[IPOPT_OLEN] - (u_char *)ip;
+ goto bad;
+ }
+ }
+ switch (opt) {
+
+ default:
+ break;
+
+ /*
+ * Source routing with record.
+ * Find interface with current destination address.
+ * If none on this machine then drop if strictly routed,
+ * or do nothing if loosely routed.
+ * Record interface address and bring up next address
+ * component. If strictly routed make sure next
+ * address is on directly accessible net.
+ */
+ case IPOPT_LSRR:
+ case IPOPT_SSRR:
+ if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
+ code = &cp[IPOPT_OFFSET] - (u_char *)ip;
+ goto bad;
+ }
+ ipaddr.sin_addr = ip->ip_dst;
+ ia = (struct in_ifaddr *)
+ ifa_ifwithaddr((struct sockaddr *)&ipaddr);
+ if (ia == 0) {
+ if (opt == IPOPT_SSRR) {
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_SRCFAIL;
+ goto bad;
+ }
+ /*
+ * Loose routing, and not at next destination
+ * yet; nothing to do except forward.
+ */
+ break;
+ }
+ off--; /* 0 origin */
+ if (off > optlen - sizeof(struct in_addr)) {
+ /*
+ * End of source route. Should be for us.
+ */
+ save_rte(cp, ip->ip_src);
+ break;
+ }
+ /*
+ * locate outgoing interface
+ */
+ bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr,
+ sizeof(ipaddr.sin_addr));
+ if (opt == IPOPT_SSRR) {
+#define INA struct in_ifaddr *
+#define SA struct sockaddr *
+ if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
+ ia = (INA)ifa_ifwithnet((SA)&ipaddr);
+ } else
+ ia = ip_rtaddr(ipaddr.sin_addr);
+ if (ia == 0) {
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_SRCFAIL;
+ goto bad;
+ }
+ ip->ip_dst = ipaddr.sin_addr;
+ bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
+ (caddr_t)(cp + off), sizeof(struct in_addr));
+ cp[IPOPT_OFFSET] += sizeof(struct in_addr);
+ /*
+ * Let ip_intr's mcast routing check handle mcast pkts
+ */
+ forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
+ break;
+
+ case IPOPT_RR:
+ if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
+ code = &cp[IPOPT_OFFSET] - (u_char *)ip;
+ goto bad;
+ }
+ /*
+ * If no space remains, ignore.
+ */
+ off--; /* 0 origin */
+ if (off > optlen - sizeof(struct in_addr))
+ break;
+ bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr,
+ sizeof(ipaddr.sin_addr));
+ /*
+ * locate outgoing interface; if we're the destination,
+ * use the incoming interface (should be same).
+ */
+ if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
+ (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_HOST;
+ goto bad;
+ }
+ bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
+ (caddr_t)(cp + off), sizeof(struct in_addr));
+ cp[IPOPT_OFFSET] += sizeof(struct in_addr);
+ break;
+
+ case IPOPT_TS:
+ code = cp - (u_char *)ip;
+ ipt = (struct ip_timestamp *)cp;
+ if (ipt->ipt_len < 5)
+ goto bad;
+ if (ipt->ipt_ptr > ipt->ipt_len - sizeof (long)) {
+ if (++ipt->ipt_oflw == 0)
+ goto bad;
+ break;
+ }
+ sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
+ switch (ipt->ipt_flg) {
+
+ case IPOPT_TS_TSONLY:
+ break;
+
+ case IPOPT_TS_TSANDADDR:
+ if (ipt->ipt_ptr + sizeof(n_time) +
+ sizeof(struct in_addr) > ipt->ipt_len)
+ goto bad;
+ ipaddr.sin_addr = dst;
+ ia = (INA)ifaof_ifpforaddr((SA)&ipaddr,
+ m->m_pkthdr.rcvif);
+ if (ia == 0)
+ continue;
+ bcopy((caddr_t)&IA_SIN(ia)->sin_addr,
+ (caddr_t)sin, sizeof(struct in_addr));
+ ipt->ipt_ptr += sizeof(struct in_addr);
+ break;
+
+ case IPOPT_TS_PRESPEC:
+ if (ipt->ipt_ptr + sizeof(n_time) +
+ sizeof(struct in_addr) > ipt->ipt_len)
+ goto bad;
+ bcopy((caddr_t)sin, (caddr_t)&ipaddr.sin_addr,
+ sizeof(struct in_addr));
+ if (ifa_ifwithaddr((SA)&ipaddr) == 0)
+ continue;
+ ipt->ipt_ptr += sizeof(struct in_addr);
+ break;
+
+ default:
+ goto bad;
+ }
+ ntime = iptime();
+ bcopy((caddr_t)&ntime, (caddr_t)cp + ipt->ipt_ptr - 1,
+ sizeof(n_time));
+ ipt->ipt_ptr += sizeof(n_time);
+ }
+ }
+ if (forward) {
+ ip_forward(m, 1);
+ return (1);
+ }
+ return (0);
+bad:
+ ip->ip_len -= ip->ip_hl << 2; /* XXX icmp_error adds in hdr length */
+ icmp_error(m, type, code, 0, 0);
+ ipstat.ips_badoptions++;
+ return (1);
+}
+
+/*
+ * Given address of next destination (final or next hop),
+ * return internet address info of interface to be used to get there.
+ */
+struct in_ifaddr *
+ip_rtaddr(dst)
+ struct in_addr dst;
+{
+ register struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *) &ipforward_rt.ro_dst;
+
+ if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) {
+ if (ipforward_rt.ro_rt) {
+ RTFREE(ipforward_rt.ro_rt);
+ ipforward_rt.ro_rt = 0;
+ }
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_addr = dst;
+
+ rtalloc(&ipforward_rt);
+ }
+ if (ipforward_rt.ro_rt == 0)
+ return ((struct in_ifaddr *)0);
+ return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa);
+}
+
+/*
+ * Save incoming source route for use in replies,
+ * to be picked up later by ip_srcroute if the receiver is interested.
+ */
+void
+save_rte(option, dst)
+ u_char *option;
+ struct in_addr dst;
+{
+ unsigned olen;
+
+ olen = option[IPOPT_OLEN];
+#ifdef DIAGNOSTIC
+ if (ipprintfs)
+ printf("save_rte: olen %d\n", olen);
+#endif
+ if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst)))
+ return;
+ bcopy((caddr_t)option, (caddr_t)ip_srcrt.srcopt, olen);
+ ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
+ ip_srcrt.dst = dst;
+}
+
+/*
+ * Retrieve incoming source route for use in replies,
+ * in the same form used by setsockopt.
+ * The first hop is placed before the options, will be removed later.
+ */
+struct mbuf *
+ip_srcroute()
+{
+ register struct in_addr *p, *q;
+ register struct mbuf *m;
+
+ if (ip_nhops == 0)
+ return ((struct mbuf *)0);
+ m = m_get(M_DONTWAIT, MT_SOOPTS);
+ if (m == 0)
+ return ((struct mbuf *)0);
+
+#define OPTSIZ (sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))
+
+ /* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
+ m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) +
+ OPTSIZ;
+#ifdef DIAGNOSTIC
+ if (ipprintfs)
+ printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len);
+#endif
+
+ /*
+ * First save first hop for return route
+ */
+ p = &ip_srcrt.route[ip_nhops - 1];
+ *(mtod(m, struct in_addr *)) = *p--;
+#ifdef DIAGNOSTIC
+ if (ipprintfs)
+ printf(" hops %lx", ntohl(mtod(m, struct in_addr *)->s_addr));
+#endif
+
+ /*
+ * Copy option fields and padding (nop) to mbuf.
+ */
+ ip_srcrt.nop = IPOPT_NOP;
+ ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF;
+ bcopy((caddr_t)&ip_srcrt.nop,
+ mtod(m, caddr_t) + sizeof(struct in_addr), OPTSIZ);
+ q = (struct in_addr *)(mtod(m, caddr_t) +
+ sizeof(struct in_addr) + OPTSIZ);
+#undef OPTSIZ
+ /*
+ * Record return path as an IP source route,
+ * reversing the path (pointers are now aligned).
+ */
+ while (p >= ip_srcrt.route) {
+#ifdef DIAGNOSTIC
+ if (ipprintfs)
+ printf(" %lx", ntohl(q->s_addr));
+#endif
+ *q++ = *p--;
+ }
+ /*
+ * Last hop goes to final destination.
+ */
+ *q = ip_srcrt.dst;
+#ifdef DIAGNOSTIC
+ if (ipprintfs)
+ printf(" %lx\n", ntohl(q->s_addr));
+#endif
+ return (m);
+}
+
+/*
+ * Strip out IP options, at higher
+ * level protocol in the kernel.
+ * Second argument is buffer to which options
+ * will be moved, and return value is their length.
+ * XXX should be deleted; last arg currently ignored.
+ */
+void
+ip_stripoptions(m, mopt)
+ register struct mbuf *m;
+ struct mbuf *mopt;
+{
+ register int i;
+ struct ip *ip = mtod(m, struct ip *);
+ register caddr_t opts;
+ int olen;
+
+ olen = (ip->ip_hl<<2) - sizeof (struct ip);
+ opts = (caddr_t)(ip + 1);
+ i = m->m_len - (sizeof (struct ip) + olen);
+ bcopy(opts + olen, opts, (unsigned)i);
+ m->m_len -= olen;
+ if (m->m_flags & M_PKTHDR)
+ m->m_pkthdr.len -= olen;
+ ip->ip_hl = sizeof(struct ip) >> 2;
+}
+
+u_char inetctlerrmap[PRC_NCMDS] = {
+ 0, 0, 0, 0,
+ 0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH,
+ EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED,
+ EMSGSIZE, EHOSTUNREACH, 0, 0,
+ 0, 0, 0, 0,
+ ENOPROTOOPT
+};
+
+/*
+ * Forward a packet. If some error occurs return the sender
+ * an icmp packet. Note we can't always generate a meaningful
+ * icmp message because icmp doesn't have a large enough repertoire
+ * of codes and types.
+ *
+ * If not forwarding, just drop the packet. This could be confusing
+ * if ipforwarding was zero but some routing protocol was advancing
+ * us as a gateway to somewhere. However, we must let the routing
+ * protocol deal with that.
+ *
+ * The srcrt parameter indicates whether the packet is being forwarded
+ * via a source route.
+ */
+void
+ip_forward(m, srcrt)
+ struct mbuf *m;
+ int srcrt;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register struct sockaddr_in *sin;
+ register struct rtentry *rt;
+ int error, type = 0, code = 0;
+ struct mbuf *mcopy;
+ n_long dest;
+ struct ifnet *destifp;
+
+ dest = 0;
+#ifdef DIAGNOSTIC
+ if (ipprintfs)
+ printf("forward: src %x dst %x ttl %x\n", ip->ip_src,
+ ip->ip_dst, ip->ip_ttl);
+#endif
+ if (m->m_flags & M_BCAST || in_canforward(ip->ip_dst) == 0) {
+ ipstat.ips_cantforward++;
+ m_freem(m);
+ return;
+ }
+ HTONS(ip->ip_id);
+ if (ip->ip_ttl <= IPTTLDEC) {
+ icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, dest, 0);
+ return;
+ }
+ ip->ip_ttl -= IPTTLDEC;
+
+ sin = (struct sockaddr_in *)&ipforward_rt.ro_dst;
+ if ((rt = ipforward_rt.ro_rt) == 0 ||
+ ip->ip_dst.s_addr != sin->sin_addr.s_addr) {
+ if (ipforward_rt.ro_rt) {
+ RTFREE(ipforward_rt.ro_rt);
+ ipforward_rt.ro_rt = 0;
+ }
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_addr = ip->ip_dst;
+
+ rtalloc(&ipforward_rt);
+ if (ipforward_rt.ro_rt == 0) {
+ icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0);
+ return;
+ }
+ rt = ipforward_rt.ro_rt;
+ }
+
+ /*
+ * Save at most 64 bytes of the packet in case
+ * we need to generate an ICMP message to the src.
+ */
+ mcopy = m_copy(m, 0, imin((int)ip->ip_len, 64));
+
+#ifdef GATEWAY
+ ip_ifmatrix[rt->rt_ifp->if_index +
+ if_index * m->m_pkthdr.rcvif->if_index]++;
+#endif
+ /*
+ * If forwarding packet using same interface that it came in on,
+ * perhaps should send a redirect to sender to shortcut a hop.
+ * Only send redirect if source is sending directly to us,
+ * and if packet was not source routed (or has any options).
+ * Also, don't send redirect if forwarding using a default route
+ * or a route modified by a redirect.
+ */
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+ if (rt->rt_ifp == m->m_pkthdr.rcvif &&
+ (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
+ satosin(rt_key(rt))->sin_addr.s_addr != 0 &&
+ ipsendredirects && !srcrt) {
+#define RTA(rt) ((struct in_ifaddr *)(rt->rt_ifa))
+ u_long src = ntohl(ip->ip_src.s_addr);
+
+ if (RTA(rt) &&
+ (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) {
+ if (rt->rt_flags & RTF_GATEWAY)
+ dest = satosin(rt->rt_gateway)->sin_addr.s_addr;
+ else
+ dest = ip->ip_dst.s_addr;
+ /* Router requirements says to only send host redirects */
+ type = ICMP_REDIRECT;
+ code = ICMP_REDIRECT_HOST;
+#ifdef DIAGNOSTIC
+ if (ipprintfs)
+ printf("redirect (%d) to %lx\n", code, (u_long)dest);
+#endif
+ }
+ }
+
+ error = ip_output(m, (struct mbuf *)0, &ipforward_rt, IP_FORWARDING
+#ifdef DIRECTED_BROADCAST
+ | IP_ALLOWBROADCAST
+#endif
+ , 0);
+ if (error)
+ ipstat.ips_cantforward++;
+ else {
+ ipstat.ips_forward++;
+ if (type)
+ ipstat.ips_redirectsent++;
+ else {
+ if (mcopy)
+ m_freem(mcopy);
+ return;
+ }
+ }
+ if (mcopy == NULL)
+ return;
+ destifp = NULL;
+
+ switch (error) {
+
+ case 0: /* forwarded, but need redirect */
+ /* type, code set above */
+ break;
+
+ case ENETUNREACH: /* shouldn't happen, checked above */
+ case EHOSTUNREACH:
+ case ENETDOWN:
+ case EHOSTDOWN:
+ default:
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_HOST;
+ break;
+
+ case EMSGSIZE:
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_NEEDFRAG;
+ if (ipforward_rt.ro_rt)
+ destifp = ipforward_rt.ro_rt->rt_ifp;
+ ipstat.ips_cantfrag++;
+ break;
+
+ case ENOBUFS:
+ type = ICMP_SOURCEQUENCH;
+ code = 0;
+ break;
+ }
+ icmp_error(mcopy, type, code, dest, destifp);
+}
+
+int
+ip_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+{
+ /* All sysctl names at this level are terminal. */
+ if (namelen != 1)
+ return (ENOTDIR);
+
+ switch (name[0]) {
+ case IPCTL_FORWARDING:
+ return (sysctl_int(oldp, oldlenp, newp, newlen, &ipforwarding));
+ case IPCTL_SENDREDIRECTS:
+ return (sysctl_int(oldp, oldlenp, newp, newlen,
+ &ipsendredirects));
+ case IPCTL_DEFTTL:
+ return (sysctl_int(oldp, oldlenp, newp, newlen, &ip_defttl));
+#ifdef notyet
+ case IPCTL_DEFMTU:
+ return (sysctl_int(oldp, oldlenp, newp, newlen, &ip_mtu));
+#endif
+ default:
+ return (EOPNOTSUPP);
+ }
+ /* NOTREACHED */
+}
diff --git a/sys/netinet/ip_mroute.c b/sys/netinet/ip_mroute.c
new file mode 100644
index 0000000..bb26c87
--- /dev/null
+++ b/sys/netinet/ip_mroute.c
@@ -0,0 +1,835 @@
+/*
+ * Copyright (c) 1989 Stephen Deering
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_mroute.c 8.2 (Berkeley) 11/15/93
+ */
+
+/*
+ * Procedures for the kernel part of DVMRP,
+ * a Distance-Vector Multicast Routing Protocol.
+ * (See RFC-1075.)
+ *
+ * Written by David Waitzman, BBN Labs, August 1988.
+ * Modified by Steve Deering, Stanford, February 1989.
+ *
+ * MROUTING 1.1
+ */
+
+#ifndef MROUTING
+int ip_mrtproto; /* for netstat only */
+#else
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/raw_cb.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+
+#include <netinet/igmp.h>
+#include <netinet/igmp_var.h>
+#include <netinet/ip_mroute.h>
+
+/* Static forwards */
+static int ip_mrouter_init __P((struct socket *));
+static int add_vif __P((struct vifctl *));
+static int del_vif __P((vifi_t *vifip));
+static int add_lgrp __P((struct lgrplctl *));
+static int del_lgrp __P((struct lgrplctl *));
+static int grplst_member __P((struct vif *, struct in_addr));
+static u_long nethash __P((struct in_addr in));
+static int add_mrt __P((struct mrtctl *));
+static int del_mrt __P((struct in_addr *));
+static struct mrt *mrtfind __P((struct in_addr));
+static void phyint_send __P((struct mbuf *, struct vif *));
+static void tunnel_send __P((struct mbuf *, struct vif *));
+
+#define INSIZ sizeof(struct in_addr)
+#define same(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), INSIZ) == 0)
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+
+/*
+ * Globals. All but ip_mrouter and ip_mrtproto could be static,
+ * except for netstat or debugging purposes.
+ */
+struct socket *ip_mrouter = NULL;
+int ip_mrtproto = IGMP_DVMRP; /* for netstat only */
+
+struct mrt *mrttable[MRTHASHSIZ];
+struct vif viftable[MAXVIFS];
+struct mrtstat mrtstat;
+
+/*
+ * Private variables.
+ */
+static vifi_t numvifs = 0;
+static struct mrt *cached_mrt = NULL;
+static u_long cached_origin;
+static u_long cached_originmask;
+
+/*
+ * Handle DVMRP setsockopt commands to modify the multicast routing tables.
+ */
+int
+ip_mrouter_cmd(cmd, so, m)
+ register int cmd;
+ register struct socket *so;
+ register struct mbuf *m;
+{
+ register int error = 0;
+
+ if (cmd != DVMRP_INIT && so != ip_mrouter)
+ error = EACCES;
+ else switch (cmd) {
+
+ case DVMRP_INIT:
+ error = ip_mrouter_init(so);
+ break;
+
+ case DVMRP_DONE:
+ error = ip_mrouter_done();
+ break;
+
+ case DVMRP_ADD_VIF:
+ if (m == NULL || m->m_len < sizeof(struct vifctl))
+ error = EINVAL;
+ else
+ error = add_vif(mtod(m, struct vifctl *));
+ break;
+
+ case DVMRP_DEL_VIF:
+ if (m == NULL || m->m_len < sizeof(short))
+ error = EINVAL;
+ else
+ error = del_vif(mtod(m, vifi_t *));
+ break;
+
+ case DVMRP_ADD_LGRP:
+ if (m == NULL || m->m_len < sizeof(struct lgrplctl))
+ error = EINVAL;
+ else
+ error = add_lgrp(mtod(m, struct lgrplctl *));
+ break;
+
+ case DVMRP_DEL_LGRP:
+ if (m == NULL || m->m_len < sizeof(struct lgrplctl))
+ error = EINVAL;
+ else
+ error = del_lgrp(mtod(m, struct lgrplctl *));
+ break;
+
+ case DVMRP_ADD_MRT:
+ if (m == NULL || m->m_len < sizeof(struct mrtctl))
+ error = EINVAL;
+ else
+ error = add_mrt(mtod(m, struct mrtctl *));
+ break;
+
+ case DVMRP_DEL_MRT:
+ if (m == NULL || m->m_len < sizeof(struct in_addr))
+ error = EINVAL;
+ else
+ error = del_mrt(mtod(m, struct in_addr *));
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+ return (error);
+}
+
+/*
+ * Enable multicast routing
+ */
+static int
+ip_mrouter_init(so)
+ register struct socket *so;
+{
+ if (so->so_type != SOCK_RAW ||
+ so->so_proto->pr_protocol != IPPROTO_IGMP)
+ return (EOPNOTSUPP);
+
+ if (ip_mrouter != NULL)
+ return (EADDRINUSE);
+
+ ip_mrouter = so;
+
+ return (0);
+}
+
+/*
+ * Disable multicast routing
+ */
+int
+ip_mrouter_done()
+{
+ register vifi_t vifi;
+ register int i;
+ register struct ifnet *ifp;
+ register int s;
+ struct ifreq ifr;
+
+ s = splnet();
+
+ /*
+ * For each phyint in use, free its local group list and
+ * disable promiscuous reception of all IP multicasts.
+ */
+ for (vifi = 0; vifi < numvifs; vifi++) {
+ if (viftable[vifi].v_lcl_addr.s_addr != 0 &&
+ !(viftable[vifi].v_flags & VIFF_TUNNEL)) {
+ if (viftable[vifi].v_lcl_grps)
+ free(viftable[vifi].v_lcl_grps, M_MRTABLE);
+ satosin(&ifr.ifr_addr)->sin_family = AF_INET;
+ satosin(&ifr.ifr_addr)->sin_addr.s_addr = INADDR_ANY;
+ ifp = viftable[vifi].v_ifp;
+ (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr);
+ }
+ }
+ bzero((caddr_t)viftable, sizeof(viftable));
+ numvifs = 0;
+
+ /*
+ * Free any multicast route entries.
+ */
+ for (i = 0; i < MRTHASHSIZ; i++)
+ if (mrttable[i])
+ free(mrttable[i], M_MRTABLE);
+ bzero((caddr_t)mrttable, sizeof(mrttable));
+ cached_mrt = NULL;
+
+ ip_mrouter = NULL;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Add a vif to the vif table
+ */
+static int
+add_vif(vifcp)
+ register struct vifctl *vifcp;
+{
+ register struct vif *vifp = viftable + vifcp->vifc_vifi;
+ register struct ifaddr *ifa;
+ register struct ifnet *ifp;
+ struct ifreq ifr;
+ register int error, s;
+ static struct sockaddr_in sin = { sizeof(sin), AF_INET };
+
+ if (vifcp->vifc_vifi >= MAXVIFS)
+ return (EINVAL);
+ if (vifp->v_lcl_addr.s_addr != 0)
+ return (EADDRINUSE);
+
+ /* Find the interface with an address in AF_INET family */
+ sin.sin_addr = vifcp->vifc_lcl_addr;
+ ifa = ifa_ifwithaddr((struct sockaddr *)&sin);
+ if (ifa == 0)
+ return (EADDRNOTAVAIL);
+
+ s = splnet();
+
+ if (vifcp->vifc_flags & VIFF_TUNNEL)
+ vifp->v_rmt_addr = vifcp->vifc_rmt_addr;
+ else {
+ /* Make sure the interface supports multicast */
+ ifp = ifa->ifa_ifp;
+ if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+ splx(s);
+ return (EOPNOTSUPP);
+ }
+ /*
+ * Enable promiscuous reception of all IP multicasts
+ * from the interface.
+ */
+ satosin(&ifr.ifr_addr)->sin_family = AF_INET;
+ satosin(&ifr.ifr_addr)->sin_addr.s_addr = INADDR_ANY;
+ error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr);
+ if (error) {
+ splx(s);
+ return (error);
+ }
+ }
+
+ vifp->v_flags = vifcp->vifc_flags;
+ vifp->v_threshold = vifcp->vifc_threshold;
+ vifp->v_lcl_addr = vifcp->vifc_lcl_addr;
+ vifp->v_ifp = ifa->ifa_ifp;
+
+ /* Adjust numvifs up if the vifi is higher than numvifs */
+ if (numvifs <= vifcp->vifc_vifi)
+ numvifs = vifcp->vifc_vifi + 1;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Delete a vif from the vif table
+ */
+static int
+del_vif(vifip)
+ register vifi_t *vifip;
+{
+ register struct vif *vifp = viftable + *vifip;
+ register struct ifnet *ifp;
+ register int i, s;
+ struct ifreq ifr;
+
+ if (*vifip >= numvifs)
+ return (EINVAL);
+ if (vifp->v_lcl_addr.s_addr == 0)
+ return (EADDRNOTAVAIL);
+
+ s = splnet();
+
+ if (!(vifp->v_flags & VIFF_TUNNEL)) {
+ if (vifp->v_lcl_grps)
+ free(vifp->v_lcl_grps, M_MRTABLE);
+ satosin(&ifr.ifr_addr)->sin_family = AF_INET;
+ satosin(&ifr.ifr_addr)->sin_addr.s_addr = INADDR_ANY;
+ ifp = vifp->v_ifp;
+ (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr);
+ }
+
+ bzero((caddr_t)vifp, sizeof (*vifp));
+
+ /* Adjust numvifs down */
+ for (i = numvifs - 1; i >= 0; i--)
+ if (viftable[i].v_lcl_addr.s_addr != 0)
+ break;
+ numvifs = i + 1;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Add the multicast group in the lgrpctl to the list of local multicast
+ * group memberships associated with the vif indexed by gcp->lgc_vifi.
+ */
+static int
+add_lgrp(gcp)
+ register struct lgrplctl *gcp;
+{
+ register struct vif *vifp;
+ register int s;
+
+ if (gcp->lgc_vifi >= numvifs)
+ return (EINVAL);
+
+ vifp = viftable + gcp->lgc_vifi;
+ if (vifp->v_lcl_addr.s_addr == 0 || (vifp->v_flags & VIFF_TUNNEL))
+ return (EADDRNOTAVAIL);
+
+ /* If not enough space in existing list, allocate a larger one */
+ s = splnet();
+ if (vifp->v_lcl_grps_n + 1 >= vifp->v_lcl_grps_max) {
+ register int num;
+ register struct in_addr *ip;
+
+ num = vifp->v_lcl_grps_max;
+ if (num <= 0)
+ num = 32; /* initial number */
+ else
+ num += num; /* double last number */
+ ip = (struct in_addr *)malloc(num * sizeof(*ip),
+ M_MRTABLE, M_NOWAIT);
+ if (ip == NULL) {
+ splx(s);
+ return (ENOBUFS);
+ }
+
+ bzero((caddr_t)ip, num * sizeof(*ip)); /* XXX paranoid */
+ bcopy((caddr_t)vifp->v_lcl_grps, (caddr_t)ip,
+ vifp->v_lcl_grps_n * sizeof(*ip));
+
+ vifp->v_lcl_grps_max = num;
+ if (vifp->v_lcl_grps)
+ free(vifp->v_lcl_grps, M_MRTABLE);
+ vifp->v_lcl_grps = ip;
+
+ splx(s);
+ }
+
+ vifp->v_lcl_grps[vifp->v_lcl_grps_n++] = gcp->lgc_gaddr;
+
+ if (gcp->lgc_gaddr.s_addr == vifp->v_cached_group)
+ vifp->v_cached_result = 1;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Delete the the local multicast group associated with the vif
+ * indexed by gcp->lgc_vifi.
+ */
+
+static int
+del_lgrp(gcp)
+ register struct lgrplctl *gcp;
+{
+ register struct vif *vifp;
+ register int i, error, s;
+
+ if (gcp->lgc_vifi >= numvifs)
+ return (EINVAL);
+ vifp = viftable + gcp->lgc_vifi;
+ if (vifp->v_lcl_addr.s_addr == 0 || (vifp->v_flags & VIFF_TUNNEL))
+ return (EADDRNOTAVAIL);
+
+ s = splnet();
+
+ if (gcp->lgc_gaddr.s_addr == vifp->v_cached_group)
+ vifp->v_cached_result = 0;
+
+ error = EADDRNOTAVAIL;
+ for (i = 0; i < vifp->v_lcl_grps_n; ++i)
+ if (same(&gcp->lgc_gaddr, &vifp->v_lcl_grps[i])) {
+ error = 0;
+ vifp->v_lcl_grps_n--;
+ bcopy((caddr_t)&vifp->v_lcl_grps[i + 1],
+ (caddr_t)&vifp->v_lcl_grps[i],
+ (vifp->v_lcl_grps_n - i) * sizeof(struct in_addr));
+ error = 0;
+ break;
+ }
+
+ splx(s);
+ return (error);
+}
+
+/*
+ * Return 1 if gaddr is a member of the local group list for vifp.
+ */
+static int
+grplst_member(vifp, gaddr)
+ register struct vif *vifp;
+ struct in_addr gaddr;
+{
+ register int i, s;
+ register u_long addr;
+
+ mrtstat.mrts_grp_lookups++;
+
+ addr = gaddr.s_addr;
+ if (addr == vifp->v_cached_group)
+ return (vifp->v_cached_result);
+
+ mrtstat.mrts_grp_misses++;
+
+ for (i = 0; i < vifp->v_lcl_grps_n; ++i)
+ if (addr == vifp->v_lcl_grps[i].s_addr) {
+ s = splnet();
+ vifp->v_cached_group = addr;
+ vifp->v_cached_result = 1;
+ splx(s);
+ return (1);
+ }
+ s = splnet();
+ vifp->v_cached_group = addr;
+ vifp->v_cached_result = 0;
+ splx(s);
+ return (0);
+}
+
+/*
+ * A simple hash function: returns MRTHASHMOD of the low-order octet of
+ * the argument's network or subnet number.
+ */
+static u_long
+nethash(in)
+ struct in_addr in;
+{
+ register u_long n;
+
+ n = in_netof(in);
+ while ((n & 0xff) == 0)
+ n >>= 8;
+ return (MRTHASHMOD(n));
+}
+
+/*
+ * Add an mrt entry
+ */
+static int
+add_mrt(mrtcp)
+ register struct mrtctl *mrtcp;
+{
+ struct mrt *rt;
+ u_long hash;
+ int s;
+
+ if (rt = mrtfind(mrtcp->mrtc_origin)) {
+ /* Just update the route */
+ s = splnet();
+ rt->mrt_parent = mrtcp->mrtc_parent;
+ VIFM_COPY(mrtcp->mrtc_children, rt->mrt_children);
+ VIFM_COPY(mrtcp->mrtc_leaves, rt->mrt_leaves);
+ splx(s);
+ return (0);
+ }
+
+ s = splnet();
+
+ rt = (struct mrt *)malloc(sizeof(*rt), M_MRTABLE, M_NOWAIT);
+ if (rt == NULL) {
+ splx(s);
+ return (ENOBUFS);
+ }
+
+ /*
+ * insert new entry at head of hash chain
+ */
+ rt->mrt_origin = mrtcp->mrtc_origin;
+ rt->mrt_originmask = mrtcp->mrtc_originmask;
+ rt->mrt_parent = mrtcp->mrtc_parent;
+ VIFM_COPY(mrtcp->mrtc_children, rt->mrt_children);
+ VIFM_COPY(mrtcp->mrtc_leaves, rt->mrt_leaves);
+ /* link into table */
+ hash = nethash(mrtcp->mrtc_origin);
+ rt->mrt_next = mrttable[hash];
+ mrttable[hash] = rt;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Delete an mrt entry
+ */
+static int
+del_mrt(origin)
+ register struct in_addr *origin;
+{
+ register struct mrt *rt, *prev_rt;
+ register u_long hash = nethash(*origin);
+ register int s;
+
+ for (prev_rt = rt = mrttable[hash]; rt; prev_rt = rt, rt = rt->mrt_next)
+ if (origin->s_addr == rt->mrt_origin.s_addr)
+ break;
+ if (!rt)
+ return (ESRCH);
+
+ s = splnet();
+
+ if (rt == cached_mrt)
+ cached_mrt = NULL;
+
+ if (prev_rt == rt)
+ mrttable[hash] = rt->mrt_next;
+ else
+ prev_rt->mrt_next = rt->mrt_next;
+ free(rt, M_MRTABLE);
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Find a route for a given origin IP address.
+ */
+static struct mrt *
+mrtfind(origin)
+ struct in_addr origin;
+{
+ register struct mrt *rt;
+ register u_int hash;
+ register int s;
+
+ mrtstat.mrts_mrt_lookups++;
+
+ if (cached_mrt != NULL &&
+ (origin.s_addr & cached_originmask) == cached_origin)
+ return (cached_mrt);
+
+ mrtstat.mrts_mrt_misses++;
+
+ hash = nethash(origin);
+ for (rt = mrttable[hash]; rt; rt = rt->mrt_next)
+ if ((origin.s_addr & rt->mrt_originmask.s_addr) ==
+ rt->mrt_origin.s_addr) {
+ s = splnet();
+ cached_mrt = rt;
+ cached_origin = rt->mrt_origin.s_addr;
+ cached_originmask = rt->mrt_originmask.s_addr;
+ splx(s);
+ return (rt);
+ }
+ return (NULL);
+}
+
+/*
+ * IP multicast forwarding function. This function assumes that the packet
+ * pointed to by "ip" has arrived on (or is about to be sent to) the interface
+ * pointed to by "ifp", and the packet is to be relayed to other networks
+ * that have members of the packet's destination IP multicast group.
+ *
+ * The packet is returned unscathed to the caller, unless it is tunneled
+ * or erroneous, in which case a non-zero return value tells the caller to
+ * discard it.
+ */
+
+#define IP_HDR_LEN 20 /* # bytes of fixed IP header (excluding options) */
+#define TUNNEL_LEN 12 /* # bytes of IP option for tunnel encapsulation */
+
+int
+ip_mforward(m, ifp)
+ register struct mbuf *m;
+ register struct ifnet *ifp;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register struct mrt *rt;
+ register struct vif *vifp;
+ register int vifi;
+ register u_char *ipoptions;
+ u_long tunnel_src;
+
+ if (ip->ip_hl < (IP_HDR_LEN + TUNNEL_LEN) >> 2 ||
+ (ipoptions = (u_char *)(ip + 1))[1] != IPOPT_LSRR ) {
+ /*
+ * Packet arrived via a physical interface.
+ */
+ tunnel_src = 0;
+ } else {
+ /*
+ * Packet arrived through a tunnel.
+ *
+ * A tunneled packet has a single NOP option and a
+ * two-element loose-source-and-record-route (LSRR)
+ * option immediately following the fixed-size part of
+ * the IP header. At this point in processing, the IP
+ * header should contain the following IP addresses:
+ *
+ * original source - in the source address field
+ * destination group - in the destination address field
+ * remote tunnel end-point - in the first element of LSRR
+ * one of this host's addrs - in the second element of LSRR
+ *
+ * NOTE: RFC-1075 would have the original source and
+ * remote tunnel end-point addresses swapped. However,
+ * that could cause delivery of ICMP error messages to
+ * innocent applications on intermediate routing
+ * hosts! Therefore, we hereby change the spec.
+ */
+
+ /*
+ * Verify that the tunnel options are well-formed.
+ */
+ if (ipoptions[0] != IPOPT_NOP ||
+ ipoptions[2] != 11 || /* LSRR option length */
+ ipoptions[3] != 12 || /* LSRR address pointer */
+ (tunnel_src = *(u_long *)(&ipoptions[4])) == 0) {
+ mrtstat.mrts_bad_tunnel++;
+ return (1);
+ }
+
+ /*
+ * Delete the tunnel options from the packet.
+ */
+ ovbcopy((caddr_t)(ipoptions + TUNNEL_LEN), (caddr_t)ipoptions,
+ (unsigned)(m->m_len - (IP_HDR_LEN + TUNNEL_LEN)));
+ m->m_len -= TUNNEL_LEN;
+ ip->ip_len -= TUNNEL_LEN;
+ ip->ip_hl -= TUNNEL_LEN >> 2;
+ }
+
+ /*
+ * Don't forward a packet with time-to-live of zero or one,
+ * or a packet destined to a local-only group.
+ */
+ if (ip->ip_ttl <= 1 ||
+ ntohl(ip->ip_dst.s_addr) <= INADDR_MAX_LOCAL_GROUP)
+ return ((int)tunnel_src);
+
+ /*
+ * Don't forward if we don't have a route for the packet's origin.
+ */
+ if (!(rt = mrtfind(ip->ip_src))) {
+ mrtstat.mrts_no_route++;
+ return ((int)tunnel_src);
+ }
+
+ /*
+ * Don't forward if it didn't arrive from the parent vif for its origin.
+ */
+ vifi = rt->mrt_parent;
+ if (tunnel_src == 0 ) {
+ if ((viftable[vifi].v_flags & VIFF_TUNNEL) ||
+ viftable[vifi].v_ifp != ifp )
+ return ((int)tunnel_src);
+ } else {
+ if (!(viftable[vifi].v_flags & VIFF_TUNNEL) ||
+ viftable[vifi].v_rmt_addr.s_addr != tunnel_src )
+ return ((int)tunnel_src);
+ }
+
+ /*
+ * For each vif, decide if a copy of the packet should be forwarded.
+ * Forward if:
+ * - the ttl exceeds the vif's threshold AND
+ * - the vif is a child in the origin's route AND
+ * - ( the vif is not a leaf in the origin's route OR
+ * the destination group has members on the vif )
+ *
+ * (This might be speeded up with some sort of cache -- someday.)
+ */
+ for (vifp = viftable, vifi = 0; vifi < numvifs; vifp++, vifi++) {
+ if (ip->ip_ttl > vifp->v_threshold &&
+ VIFM_ISSET(vifi, rt->mrt_children) &&
+ (!VIFM_ISSET(vifi, rt->mrt_leaves) ||
+ grplst_member(vifp, ip->ip_dst))) {
+ if (vifp->v_flags & VIFF_TUNNEL)
+ tunnel_send(m, vifp);
+ else
+ phyint_send(m, vifp);
+ }
+ }
+
+ return ((int)tunnel_src);
+}
+
+static void
+phyint_send(m, vifp)
+ register struct mbuf *m;
+ register struct vif *vifp;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register struct mbuf *mb_copy;
+ register struct ip_moptions *imo;
+ register int error;
+ struct ip_moptions simo;
+
+ mb_copy = m_copy(m, 0, M_COPYALL);
+ if (mb_copy == NULL)
+ return;
+
+ imo = &simo;
+ imo->imo_multicast_ifp = vifp->v_ifp;
+ imo->imo_multicast_ttl = ip->ip_ttl - 1;
+ imo->imo_multicast_loop = 1;
+
+ error = ip_output(mb_copy, NULL, NULL, IP_FORWARDING, imo);
+}
+
+static void
+tunnel_send(m, vifp)
+ register struct mbuf *m;
+ register struct vif *vifp;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register struct mbuf *mb_copy, *mb_opts;
+ register struct ip *ip_copy;
+ register int error;
+ register u_char *cp;
+
+ /*
+ * Make sure that adding the tunnel options won't exceed the
+ * maximum allowed number of option bytes.
+ */
+ if (ip->ip_hl > (60 - TUNNEL_LEN) >> 2) {
+ mrtstat.mrts_cant_tunnel++;
+ return;
+ }
+
+ /*
+ * Get a private copy of the IP header so that changes to some
+ * of the IP fields don't damage the original header, which is
+ * examined later in ip_input.c.
+ */
+ mb_copy = m_copy(m, IP_HDR_LEN, M_COPYALL);
+ if (mb_copy == NULL)
+ return;
+ MGETHDR(mb_opts, M_DONTWAIT, MT_HEADER);
+ if (mb_opts == NULL) {
+ m_freem(mb_copy);
+ return;
+ }
+ /*
+ * Make mb_opts be the new head of the packet chain.
+ * Any options of the packet were left in the old packet chain head
+ */
+ mb_opts->m_next = mb_copy;
+ mb_opts->m_len = IP_HDR_LEN + TUNNEL_LEN;
+ mb_opts->m_data += MSIZE - mb_opts->m_len;
+
+ ip_copy = mtod(mb_opts, struct ip *);
+ /*
+ * Copy the base ip header to the new head mbuf.
+ */
+ *ip_copy = *ip;
+ ip_copy->ip_ttl--;
+ ip_copy->ip_dst = vifp->v_rmt_addr; /* remote tunnel end-point */
+ /*
+ * Adjust the ip header length to account for the tunnel options.
+ */
+ ip_copy->ip_hl += TUNNEL_LEN >> 2;
+ ip_copy->ip_len += TUNNEL_LEN;
+ /*
+ * Add the NOP and LSRR after the base ip header
+ */
+ cp = (u_char *)(ip_copy + 1);
+ *cp++ = IPOPT_NOP;
+ *cp++ = IPOPT_LSRR;
+ *cp++ = 11; /* LSRR option length */
+ *cp++ = 8; /* LSSR pointer to second element */
+ *(u_long*)cp = vifp->v_lcl_addr.s_addr; /* local tunnel end-point */
+ cp += 4;
+ *(u_long*)cp = ip->ip_dst.s_addr; /* destination group */
+
+ error = ip_output(mb_opts, NULL, NULL, IP_FORWARDING, NULL);
+}
+#endif
diff --git a/sys/netinet/ip_mroute.h b/sys/netinet/ip_mroute.h
new file mode 100644
index 0000000..adb40be
--- /dev/null
+++ b/sys/netinet/ip_mroute.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 1989 Stephen Deering.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_mroute.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Definitions for the kernel part of DVMRP,
+ * a Distance-Vector Multicast Routing Protocol.
+ * (See RFC-1075.)
+ *
+ * Written by David Waitzman, BBN Labs, August 1988.
+ * Modified by Steve Deering, Stanford, February 1989.
+ *
+ * MROUTING 1.0
+ */
+
+
+/*
+ * DVMRP-specific setsockopt commands.
+ */
+#define DVMRP_INIT 100
+#define DVMRP_DONE 101
+#define DVMRP_ADD_VIF 102
+#define DVMRP_DEL_VIF 103
+#define DVMRP_ADD_LGRP 104
+#define DVMRP_DEL_LGRP 105
+#define DVMRP_ADD_MRT 106
+#define DVMRP_DEL_MRT 107
+
+
+/*
+ * Types and macros for handling bitmaps with one bit per virtual interface.
+ */
+#define MAXVIFS 32
+typedef u_long vifbitmap_t;
+typedef u_short vifi_t; /* type of a vif index */
+
+#define VIFM_SET(n, m) ((m) |= (1 << (n)))
+#define VIFM_CLR(n, m) ((m) &= ~(1 << (n)))
+#define VIFM_ISSET(n, m) ((m) & (1 << (n)))
+#define VIFM_CLRALL(m) ((m) = 0x00000000)
+#define VIFM_COPY(mfrom, mto) ((mto) = (mfrom))
+#define VIFM_SAME(m1, m2) ((m1) == (m2))
+
+
+/*
+ * Agument structure for DVMRP_ADD_VIF.
+ * (DVMRP_DEL_VIF takes a single vifi_t argument.)
+ */
+struct vifctl {
+ vifi_t vifc_vifi; /* the index of the vif to be added */
+ u_char vifc_flags; /* VIFF_ flags defined below */
+ u_char vifc_threshold; /* min ttl required to forward on vif */
+ struct in_addr vifc_lcl_addr; /* local interface address */
+ struct in_addr vifc_rmt_addr; /* remote address (tunnels only) */
+};
+
+#define VIFF_TUNNEL 0x1 /* vif represents a tunnel end-point */
+
+
+/*
+ * Argument structure for DVMRP_ADD_LGRP and DVMRP_DEL_LGRP.
+ */
+struct lgrplctl {
+ vifi_t lgc_vifi;
+ struct in_addr lgc_gaddr;
+};
+
+
+/*
+ * Argument structure for DVMRP_ADD_MRT.
+ * (DVMRP_DEL_MRT takes a single struct in_addr argument, containing origin.)
+ */
+struct mrtctl {
+ struct in_addr mrtc_origin; /* subnet origin of multicasts */
+ struct in_addr mrtc_originmask; /* subnet mask for origin */
+ vifi_t mrtc_parent; /* incoming vif */
+ vifbitmap_t mrtc_children; /* outgoing children vifs */
+ vifbitmap_t mrtc_leaves; /* subset of outgoing children vifs */
+};
+
+
+#ifdef KERNEL
+
+/*
+ * The kernel's virtual-interface structure.
+ */
+struct vif {
+ u_char v_flags; /* VIFF_ flags defined above */
+ u_char v_threshold; /* min ttl required to forward on vif */
+ struct in_addr v_lcl_addr; /* local interface address */
+ struct in_addr v_rmt_addr; /* remote address (tunnels only) */
+ struct ifnet *v_ifp; /* pointer to interface */
+ struct in_addr *v_lcl_grps; /* list of local grps (phyints only) */
+ int v_lcl_grps_max; /* malloc'ed number of v_lcl_grps */
+ int v_lcl_grps_n; /* used number of v_lcl_grps */
+ u_long v_cached_group; /* last grp looked-up (phyints only) */
+ int v_cached_result; /* last look-up result (phyints only) */
+};
+
+/*
+ * The kernel's multicast route structure.
+ */
+struct mrt {
+ struct in_addr mrt_origin; /* subnet origin of multicasts */
+ struct in_addr mrt_originmask; /* subnet mask for origin */
+ vifi_t mrt_parent; /* incoming vif */
+ vifbitmap_t mrt_children; /* outgoing children vifs */
+ vifbitmap_t mrt_leaves; /* subset of outgoing children vifs */
+ struct mrt *mrt_next; /* forward link */
+};
+
+
+#define MRTHASHSIZ 64
+#if (MRTHASHSIZ & (MRTHASHSIZ - 1)) == 0 /* from sys:route.h */
+#define MRTHASHMOD(h) ((h) & (MRTHASHSIZ - 1))
+#else
+#define MRTHASHMOD(h) ((h) % MRTHASHSIZ)
+#endif
+
+/*
+ * The kernel's multicast routing statistics.
+ */
+struct mrtstat {
+ u_long mrts_mrt_lookups; /* # multicast route lookups */
+ u_long mrts_mrt_misses; /* # multicast route cache misses */
+ u_long mrts_grp_lookups; /* # group address lookups */
+ u_long mrts_grp_misses; /* # group address cache misses */
+ u_long mrts_no_route; /* no route for packet's origin */
+ u_long mrts_bad_tunnel; /* malformed tunnel options */
+ u_long mrts_cant_tunnel; /* no room for tunnel options */
+};
+
+
+int ip_mrouter_cmd __P((int, struct socket *, struct mbuf *));
+int ip_mrouter_done __P((void));
+
+#endif /* KERNEL */
+
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
new file mode 100644
index 0000000..89c6de4
--- /dev/null
+++ b/sys/netinet/ip_output.c
@@ -0,0 +1,1075 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_output.c 8.3 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+
+#ifdef vax
+#include <machine/mtpr.h>
+#endif
+
+static struct mbuf *ip_insertoptions __P((struct mbuf *, struct mbuf *, int *));
+static void ip_mloopback
+ __P((struct ifnet *, struct mbuf *, struct sockaddr_in *));
+
+/*
+ * IP output. The packet in mbuf chain m contains a skeletal IP
+ * header (with len, off, ttl, proto, tos, src, dst).
+ * The mbuf chain containing the packet will be freed.
+ * The mbuf opt, if present, will not be freed.
+ */
+int
+ip_output(m0, opt, ro, flags, imo)
+ struct mbuf *m0;
+ struct mbuf *opt;
+ struct route *ro;
+ int flags;
+ struct ip_moptions *imo;
+{
+ register struct ip *ip, *mhip;
+ register struct ifnet *ifp;
+ register struct mbuf *m = m0;
+ register int hlen = sizeof (struct ip);
+ int len, off, error = 0;
+ struct route iproute;
+ struct sockaddr_in *dst;
+ struct in_ifaddr *ia;
+
+#ifdef DIAGNOSTIC
+ if ((m->m_flags & M_PKTHDR) == 0)
+ panic("ip_output no HDR");
+#endif
+ if (opt) {
+ m = ip_insertoptions(m, opt, &len);
+ hlen = len;
+ }
+ ip = mtod(m, struct ip *);
+ /*
+ * Fill in IP header.
+ */
+ if ((flags & (IP_FORWARDING|IP_RAWOUTPUT)) == 0) {
+ ip->ip_v = IPVERSION;
+ ip->ip_off &= IP_DF;
+ ip->ip_id = htons(ip_id++);
+ ip->ip_hl = hlen >> 2;
+ ipstat.ips_localout++;
+ } else {
+ hlen = ip->ip_hl << 2;
+ }
+ /*
+ * Route packet.
+ */
+ if (ro == 0) {
+ ro = &iproute;
+ bzero((caddr_t)ro, sizeof (*ro));
+ }
+ dst = (struct sockaddr_in *)&ro->ro_dst;
+ /*
+ * If there is a cached route,
+ * check that it is to the same destination
+ * and is still up. If not, free it and try again.
+ */
+ if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
+ dst->sin_addr.s_addr != ip->ip_dst.s_addr)) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ if (ro->ro_rt == 0) {
+ dst->sin_family = AF_INET;
+ dst->sin_len = sizeof(*dst);
+ dst->sin_addr = ip->ip_dst;
+ }
+ /*
+ * If routing to interface only,
+ * short circuit routing lookup.
+ */
+#define ifatoia(ifa) ((struct in_ifaddr *)(ifa))
+#define sintosa(sin) ((struct sockaddr *)(sin))
+ if (flags & IP_ROUTETOIF) {
+ if ((ia = ifatoia(ifa_ifwithdstaddr(sintosa(dst)))) == 0 &&
+ (ia = ifatoia(ifa_ifwithnet(sintosa(dst)))) == 0) {
+ ipstat.ips_noroute++;
+ error = ENETUNREACH;
+ goto bad;
+ }
+ ifp = ia->ia_ifp;
+ ip->ip_ttl = 1;
+ } else {
+ if (ro->ro_rt == 0)
+ rtalloc(ro);
+ if (ro->ro_rt == 0) {
+ ipstat.ips_noroute++;
+ error = EHOSTUNREACH;
+ goto bad;
+ }
+ ia = ifatoia(ro->ro_rt->rt_ifa);
+ ifp = ro->ro_rt->rt_ifp;
+ ro->ro_rt->rt_use++;
+ if (ro->ro_rt->rt_flags & RTF_GATEWAY)
+ dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway;
+ }
+ if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
+ struct in_multi *inm;
+ extern struct ifnet loif;
+
+ m->m_flags |= M_MCAST;
+ /*
+ * IP destination address is multicast. Make sure "dst"
+ * still points to the address in "ro". (It may have been
+ * changed to point to a gateway address, above.)
+ */
+ dst = (struct sockaddr_in *)&ro->ro_dst;
+ /*
+ * See if the caller provided any multicast options
+ */
+ if (imo != NULL) {
+ ip->ip_ttl = imo->imo_multicast_ttl;
+ if (imo->imo_multicast_ifp != NULL)
+ ifp = imo->imo_multicast_ifp;
+ } else
+ ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL;
+ /*
+ * Confirm that the outgoing interface supports multicast.
+ */
+ if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+ ipstat.ips_noroute++;
+ error = ENETUNREACH;
+ goto bad;
+ }
+ /*
+ * If source address not specified yet, use address
+ * of outgoing interface.
+ */
+ if (ip->ip_src.s_addr == INADDR_ANY) {
+ register struct in_ifaddr *ia;
+
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp) {
+ ip->ip_src = IA_SIN(ia)->sin_addr;
+ break;
+ }
+ }
+
+ IN_LOOKUP_MULTI(ip->ip_dst, ifp, inm);
+ if (inm != NULL &&
+ (imo == NULL || imo->imo_multicast_loop)) {
+ /*
+ * If we belong to the destination multicast group
+ * on the outgoing interface, and the caller did not
+ * forbid loopback, loop back a copy.
+ */
+ ip_mloopback(ifp, m, dst);
+ }
+#ifdef MROUTING
+ else {
+ /*
+ * If we are acting as a multicast router, perform
+ * multicast forwarding as if the packet had just
+ * arrived on the interface to which we are about
+ * to send. The multicast forwarding function
+ * recursively calls this function, using the
+ * IP_FORWARDING flag to prevent infinite recursion.
+ *
+ * Multicasts that are looped back by ip_mloopback(),
+ * above, will be forwarded by the ip_input() routine,
+ * if necessary.
+ */
+ extern struct socket *ip_mrouter;
+ if (ip_mrouter && (flags & IP_FORWARDING) == 0) {
+ if (ip_mforward(m, ifp) != 0) {
+ m_freem(m);
+ goto done;
+ }
+ }
+ }
+#endif
+ /*
+ * Multicasts with a time-to-live of zero may be looped-
+ * back, above, but must not be transmitted on a network.
+ * Also, multicasts addressed to the loopback interface
+ * are not sent -- the above call to ip_mloopback() will
+ * loop back a copy if this host actually belongs to the
+ * destination group on the loopback interface.
+ */
+ if (ip->ip_ttl == 0 || ifp == &loif) {
+ m_freem(m);
+ goto done;
+ }
+
+ goto sendit;
+ }
+#ifndef notdef
+ /*
+ * If source address not specified yet, use address
+ * of outgoing interface.
+ */
+ if (ip->ip_src.s_addr == INADDR_ANY)
+ ip->ip_src = IA_SIN(ia)->sin_addr;
+#endif
+ /*
+ * Verify that we have any chance at all of being able to queue
+ * the packet or packet fragments
+ */
+ if ((ifp->if_snd.ifq_len + ip->ip_len / ifp->if_mtu + 1) >=
+ ifp->if_snd.ifq_maxlen) {
+ error = ENOBUFS;
+ goto bad;
+ }
+
+ /*
+ * Look for broadcast address and
+ * and verify user is allowed to send
+ * such a packet.
+ */
+ if (in_broadcast(dst->sin_addr, ifp)) {
+ if ((ifp->if_flags & IFF_BROADCAST) == 0) {
+ error = EADDRNOTAVAIL;
+ goto bad;
+ }
+ if ((flags & IP_ALLOWBROADCAST) == 0) {
+ error = EACCES;
+ goto bad;
+ }
+ /* don't allow broadcast messages to be fragmented */
+ if ((u_short)ip->ip_len > ifp->if_mtu) {
+ error = EMSGSIZE;
+ goto bad;
+ }
+ m->m_flags |= M_BCAST;
+ } else
+ m->m_flags &= ~M_BCAST;
+
+sendit:
+ /*
+ * If small enough for interface, can just send directly.
+ */
+ if ((u_short)ip->ip_len <= ifp->if_mtu) {
+ ip->ip_len = htons((u_short)ip->ip_len);
+ ip->ip_off = htons((u_short)ip->ip_off);
+ ip->ip_sum = 0;
+ ip->ip_sum = in_cksum(m, hlen);
+ error = (*ifp->if_output)(ifp, m,
+ (struct sockaddr *)dst, ro->ro_rt);
+ goto done;
+ }
+ /*
+ * Too large for interface; fragment if possible.
+ * Must be able to put at least 8 bytes per fragment.
+ */
+ if (ip->ip_off & IP_DF) {
+ error = EMSGSIZE;
+ ipstat.ips_cantfrag++;
+ goto bad;
+ }
+ len = (ifp->if_mtu - hlen) &~ 7;
+ if (len < 8) {
+ error = EMSGSIZE;
+ goto bad;
+ }
+
+ {
+ int mhlen, firstlen = len;
+ struct mbuf **mnext = &m->m_nextpkt;
+
+ /*
+ * Loop through length of segment after first fragment,
+ * make new header and copy data of each part and link onto chain.
+ */
+ m0 = m;
+ mhlen = sizeof (struct ip);
+ for (off = hlen + len; off < (u_short)ip->ip_len; off += len) {
+ MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (m == 0) {
+ error = ENOBUFS;
+ ipstat.ips_odropped++;
+ goto sendorfree;
+ }
+ m->m_data += max_linkhdr;
+ mhip = mtod(m, struct ip *);
+ *mhip = *ip;
+ if (hlen > sizeof (struct ip)) {
+ mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip);
+ mhip->ip_hl = mhlen >> 2;
+ }
+ m->m_len = mhlen;
+ mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF);
+ if (ip->ip_off & IP_MF)
+ mhip->ip_off |= IP_MF;
+ if (off + len >= (u_short)ip->ip_len)
+ len = (u_short)ip->ip_len - off;
+ else
+ mhip->ip_off |= IP_MF;
+ mhip->ip_len = htons((u_short)(len + mhlen));
+ m->m_next = m_copy(m0, off, len);
+ if (m->m_next == 0) {
+ (void) m_free(m);
+ error = ENOBUFS; /* ??? */
+ ipstat.ips_odropped++;
+ goto sendorfree;
+ }
+ m->m_pkthdr.len = mhlen + len;
+ m->m_pkthdr.rcvif = (struct ifnet *)0;
+ mhip->ip_off = htons((u_short)mhip->ip_off);
+ mhip->ip_sum = 0;
+ mhip->ip_sum = in_cksum(m, mhlen);
+ *mnext = m;
+ mnext = &m->m_nextpkt;
+ ipstat.ips_ofragments++;
+ }
+ /*
+ * Update first fragment by trimming what's been copied out
+ * and updating header, then send each fragment (in order).
+ */
+ m = m0;
+ m_adj(m, hlen + firstlen - (u_short)ip->ip_len);
+ m->m_pkthdr.len = hlen + firstlen;
+ ip->ip_len = htons((u_short)m->m_pkthdr.len);
+ ip->ip_off = htons((u_short)(ip->ip_off | IP_MF));
+ ip->ip_sum = 0;
+ ip->ip_sum = in_cksum(m, hlen);
+sendorfree:
+ for (m = m0; m; m = m0) {
+ m0 = m->m_nextpkt;
+ m->m_nextpkt = 0;
+ if (error == 0)
+ error = (*ifp->if_output)(ifp, m,
+ (struct sockaddr *)dst, ro->ro_rt);
+ else
+ m_freem(m);
+ }
+
+ if (error == 0)
+ ipstat.ips_fragmented++;
+ }
+done:
+ if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt)
+ RTFREE(ro->ro_rt);
+ return (error);
+bad:
+ m_freem(m0);
+ goto done;
+}
+
+/*
+ * Insert IP options into preformed packet.
+ * Adjust IP destination as required for IP source routing,
+ * as indicated by a non-zero in_addr at the start of the options.
+ */
+static struct mbuf *
+ip_insertoptions(m, opt, phlen)
+ register struct mbuf *m;
+ struct mbuf *opt;
+ int *phlen;
+{
+ register struct ipoption *p = mtod(opt, struct ipoption *);
+ struct mbuf *n;
+ register struct ip *ip = mtod(m, struct ip *);
+ unsigned optlen;
+
+ optlen = opt->m_len - sizeof(p->ipopt_dst);
+ if (optlen + (u_short)ip->ip_len > IP_MAXPACKET)
+ return (m); /* XXX should fail */
+ if (p->ipopt_dst.s_addr)
+ ip->ip_dst = p->ipopt_dst;
+ if (m->m_flags & M_EXT || m->m_data - optlen < m->m_pktdat) {
+ MGETHDR(n, M_DONTWAIT, MT_HEADER);
+ if (n == 0)
+ return (m);
+ n->m_pkthdr.len = m->m_pkthdr.len + optlen;
+ m->m_len -= sizeof(struct ip);
+ m->m_data += sizeof(struct ip);
+ n->m_next = m;
+ m = n;
+ m->m_len = optlen + sizeof(struct ip);
+ m->m_data += max_linkhdr;
+ bcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip));
+ } else {
+ m->m_data -= optlen;
+ m->m_len += optlen;
+ m->m_pkthdr.len += optlen;
+ ovbcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip));
+ }
+ ip = mtod(m, struct ip *);
+ bcopy((caddr_t)p->ipopt_list, (caddr_t)(ip + 1), (unsigned)optlen);
+ *phlen = sizeof(struct ip) + optlen;
+ ip->ip_len += optlen;
+ return (m);
+}
+
+/*
+ * Copy options from ip to jp,
+ * omitting those not copied during fragmentation.
+ */
+int
+ip_optcopy(ip, jp)
+ struct ip *ip, *jp;
+{
+ register u_char *cp, *dp;
+ int opt, optlen, cnt;
+
+ cp = (u_char *)(ip + 1);
+ dp = (u_char *)(jp + 1);
+ cnt = (ip->ip_hl << 2) - sizeof (struct ip);
+ for (; cnt > 0; cnt -= optlen, cp += optlen) {
+ opt = cp[0];
+ if (opt == IPOPT_EOL)
+ break;
+ if (opt == IPOPT_NOP) {
+ /* Preserve for IP mcast tunnel's LSRR alignment. */
+ *dp++ = IPOPT_NOP;
+ optlen = 1;
+ continue;
+ } else
+ optlen = cp[IPOPT_OLEN];
+ /* bogus lengths should have been caught by ip_dooptions */
+ if (optlen > cnt)
+ optlen = cnt;
+ if (IPOPT_COPIED(opt)) {
+ bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen);
+ dp += optlen;
+ }
+ }
+ for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)
+ *dp++ = IPOPT_EOL;
+ return (optlen);
+}
+
+/*
+ * IP socket option processing.
+ */
+int
+ip_ctloutput(op, so, level, optname, mp)
+ int op;
+ struct socket *so;
+ int level, optname;
+ struct mbuf **mp;
+{
+ register struct inpcb *inp = sotoinpcb(so);
+ register struct mbuf *m = *mp;
+ register int optval = 0;
+ int error = 0;
+
+ if (level != IPPROTO_IP) {
+ error = EINVAL;
+ if (op == PRCO_SETOPT && *mp)
+ (void) m_free(*mp);
+ } else switch (op) {
+
+ case PRCO_SETOPT:
+ switch (optname) {
+ case IP_OPTIONS:
+#ifdef notyet
+ case IP_RETOPTS:
+ return (ip_pcbopts(optname, &inp->inp_options, m));
+#else
+ return (ip_pcbopts(&inp->inp_options, m));
+#endif
+
+ case IP_TOS:
+ case IP_TTL:
+ case IP_RECVOPTS:
+ case IP_RECVRETOPTS:
+ case IP_RECVDSTADDR:
+ if (m->m_len != sizeof(int))
+ error = EINVAL;
+ else {
+ optval = *mtod(m, int *);
+ switch (optname) {
+
+ case IP_TOS:
+ inp->inp_ip.ip_tos = optval;
+ break;
+
+ case IP_TTL:
+ inp->inp_ip.ip_ttl = optval;
+ break;
+#define OPTSET(bit) \
+ if (optval) \
+ inp->inp_flags |= bit; \
+ else \
+ inp->inp_flags &= ~bit;
+
+ case IP_RECVOPTS:
+ OPTSET(INP_RECVOPTS);
+ break;
+
+ case IP_RECVRETOPTS:
+ OPTSET(INP_RECVRETOPTS);
+ break;
+
+ case IP_RECVDSTADDR:
+ OPTSET(INP_RECVDSTADDR);
+ break;
+ }
+ }
+ break;
+#undef OPTSET
+
+ case IP_MULTICAST_IF:
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ error = ip_setmoptions(optname, &inp->inp_moptions, m);
+ break;
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ if (m)
+ (void)m_free(m);
+ break;
+
+ case PRCO_GETOPT:
+ switch (optname) {
+ case IP_OPTIONS:
+ case IP_RETOPTS:
+ *mp = m = m_get(M_WAIT, MT_SOOPTS);
+ if (inp->inp_options) {
+ m->m_len = inp->inp_options->m_len;
+ bcopy(mtod(inp->inp_options, caddr_t),
+ mtod(m, caddr_t), (unsigned)m->m_len);
+ } else
+ m->m_len = 0;
+ break;
+
+ case IP_TOS:
+ case IP_TTL:
+ case IP_RECVOPTS:
+ case IP_RECVRETOPTS:
+ case IP_RECVDSTADDR:
+ *mp = m = m_get(M_WAIT, MT_SOOPTS);
+ m->m_len = sizeof(int);
+ switch (optname) {
+
+ case IP_TOS:
+ optval = inp->inp_ip.ip_tos;
+ break;
+
+ case IP_TTL:
+ optval = inp->inp_ip.ip_ttl;
+ break;
+
+#define OPTBIT(bit) (inp->inp_flags & bit ? 1 : 0)
+
+ case IP_RECVOPTS:
+ optval = OPTBIT(INP_RECVOPTS);
+ break;
+
+ case IP_RECVRETOPTS:
+ optval = OPTBIT(INP_RECVRETOPTS);
+ break;
+
+ case IP_RECVDSTADDR:
+ optval = OPTBIT(INP_RECVDSTADDR);
+ break;
+ }
+ *mtod(m, int *) = optval;
+ break;
+
+ case IP_MULTICAST_IF:
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ error = ip_getmoptions(optname, inp->inp_moptions, mp);
+ break;
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ break;
+ }
+ return (error);
+}
+
+/*
+ * Set up IP options in pcb for insertion in output packets.
+ * Store in mbuf with pointer in pcbopt, adding pseudo-option
+ * with destination address if source routed.
+ */
+int
+#ifdef notyet
+ip_pcbopts(optname, pcbopt, m)
+ int optname;
+#else
+ip_pcbopts(pcbopt, m)
+#endif
+ struct mbuf **pcbopt;
+ register struct mbuf *m;
+{
+ register cnt, optlen;
+ register u_char *cp;
+ u_char opt;
+
+ /* turn off any old options */
+ if (*pcbopt)
+ (void)m_free(*pcbopt);
+ *pcbopt = 0;
+ if (m == (struct mbuf *)0 || m->m_len == 0) {
+ /*
+ * Only turning off any previous options.
+ */
+ if (m)
+ (void)m_free(m);
+ return (0);
+ }
+
+#ifndef vax
+ if (m->m_len % sizeof(long))
+ goto bad;
+#endif
+ /*
+ * IP first-hop destination address will be stored before
+ * actual options; move other options back
+ * and clear it when none present.
+ */
+ if (m->m_data + m->m_len + sizeof(struct in_addr) >= &m->m_dat[MLEN])
+ goto bad;
+ cnt = m->m_len;
+ m->m_len += sizeof(struct in_addr);
+ cp = mtod(m, u_char *) + sizeof(struct in_addr);
+ ovbcopy(mtod(m, caddr_t), (caddr_t)cp, (unsigned)cnt);
+ bzero(mtod(m, caddr_t), sizeof(struct in_addr));
+
+ for (; cnt > 0; cnt -= optlen, cp += optlen) {
+ opt = cp[IPOPT_OPTVAL];
+ if (opt == IPOPT_EOL)
+ break;
+ if (opt == IPOPT_NOP)
+ optlen = 1;
+ else {
+ optlen = cp[IPOPT_OLEN];
+ if (optlen <= IPOPT_OLEN || optlen > cnt)
+ goto bad;
+ }
+ switch (opt) {
+
+ default:
+ break;
+
+ case IPOPT_LSRR:
+ case IPOPT_SSRR:
+ /*
+ * user process specifies route as:
+ * ->A->B->C->D
+ * D must be our final destination (but we can't
+ * check that since we may not have connected yet).
+ * A is first hop destination, which doesn't appear in
+ * actual IP option, but is stored before the options.
+ */
+ if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr))
+ goto bad;
+ m->m_len -= sizeof(struct in_addr);
+ cnt -= sizeof(struct in_addr);
+ optlen -= sizeof(struct in_addr);
+ cp[IPOPT_OLEN] = optlen;
+ /*
+ * Move first hop before start of options.
+ */
+ bcopy((caddr_t)&cp[IPOPT_OFFSET+1], mtod(m, caddr_t),
+ sizeof(struct in_addr));
+ /*
+ * Then copy rest of options back
+ * to close up the deleted entry.
+ */
+ ovbcopy((caddr_t)(&cp[IPOPT_OFFSET+1] +
+ sizeof(struct in_addr)),
+ (caddr_t)&cp[IPOPT_OFFSET+1],
+ (unsigned)cnt + sizeof(struct in_addr));
+ break;
+ }
+ }
+ if (m->m_len > MAX_IPOPTLEN + sizeof(struct in_addr))
+ goto bad;
+ *pcbopt = m;
+ return (0);
+
+bad:
+ (void)m_free(m);
+ return (EINVAL);
+}
+
+/*
+ * Set the IP multicast options in response to user setsockopt().
+ */
+int
+ip_setmoptions(optname, imop, m)
+ int optname;
+ struct ip_moptions **imop;
+ struct mbuf *m;
+{
+ register int error = 0;
+ u_char loop;
+ register int i;
+ struct in_addr addr;
+ register struct ip_mreq *mreq;
+ register struct ifnet *ifp;
+ register struct ip_moptions *imo = *imop;
+ struct route ro;
+ register struct sockaddr_in *dst;
+
+ if (imo == NULL) {
+ /*
+ * No multicast option buffer attached to the pcb;
+ * allocate one and initialize to default values.
+ */
+ imo = (struct ip_moptions*)malloc(sizeof(*imo), M_IPMOPTS,
+ M_WAITOK);
+
+ if (imo == NULL)
+ return (ENOBUFS);
+ *imop = imo;
+ imo->imo_multicast_ifp = NULL;
+ imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
+ imo->imo_multicast_loop = IP_DEFAULT_MULTICAST_LOOP;
+ imo->imo_num_memberships = 0;
+ }
+
+ switch (optname) {
+
+ case IP_MULTICAST_IF:
+ /*
+ * Select the interface for outgoing multicast packets.
+ */
+ if (m == NULL || m->m_len != sizeof(struct in_addr)) {
+ error = EINVAL;
+ break;
+ }
+ addr = *(mtod(m, struct in_addr *));
+ /*
+ * INADDR_ANY is used to remove a previous selection.
+ * When no interface is selected, a default one is
+ * chosen every time a multicast packet is sent.
+ */
+ if (addr.s_addr == INADDR_ANY) {
+ imo->imo_multicast_ifp = NULL;
+ break;
+ }
+ /*
+ * The selected interface is identified by its local
+ * IP address. Find the interface and confirm that
+ * it supports multicasting.
+ */
+ INADDR_TO_IFP(addr, ifp);
+ if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ imo->imo_multicast_ifp = ifp;
+ break;
+
+ case IP_MULTICAST_TTL:
+ /*
+ * Set the IP time-to-live for outgoing multicast packets.
+ */
+ if (m == NULL || m->m_len != 1) {
+ error = EINVAL;
+ break;
+ }
+ imo->imo_multicast_ttl = *(mtod(m, u_char *));
+ break;
+
+ case IP_MULTICAST_LOOP:
+ /*
+ * Set the loopback flag for outgoing multicast packets.
+ * Must be zero or one.
+ */
+ if (m == NULL || m->m_len != 1 ||
+ (loop = *(mtod(m, u_char *))) > 1) {
+ error = EINVAL;
+ break;
+ }
+ imo->imo_multicast_loop = loop;
+ break;
+
+ case IP_ADD_MEMBERSHIP:
+ /*
+ * Add a multicast group membership.
+ * Group must be a valid IP multicast address.
+ */
+ if (m == NULL || m->m_len != sizeof(struct ip_mreq)) {
+ error = EINVAL;
+ break;
+ }
+ mreq = mtod(m, struct ip_mreq *);
+ if (!IN_MULTICAST(ntohl(mreq->imr_multiaddr.s_addr))) {
+ error = EINVAL;
+ break;
+ }
+ /*
+ * If no interface address was provided, use the interface of
+ * the route to the given multicast address.
+ */
+ if (mreq->imr_interface.s_addr == INADDR_ANY) {
+ ro.ro_rt = NULL;
+ dst = (struct sockaddr_in *)&ro.ro_dst;
+ dst->sin_len = sizeof(*dst);
+ dst->sin_family = AF_INET;
+ dst->sin_addr = mreq->imr_multiaddr;
+ rtalloc(&ro);
+ if (ro.ro_rt == NULL) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ ifp = ro.ro_rt->rt_ifp;
+ rtfree(ro.ro_rt);
+ }
+ else {
+ INADDR_TO_IFP(mreq->imr_interface, ifp);
+ }
+ /*
+ * See if we found an interface, and confirm that it
+ * supports multicast.
+ */
+ if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ /*
+ * See if the membership already exists or if all the
+ * membership slots are full.
+ */
+ for (i = 0; i < imo->imo_num_memberships; ++i) {
+ if (imo->imo_membership[i]->inm_ifp == ifp &&
+ imo->imo_membership[i]->inm_addr.s_addr
+ == mreq->imr_multiaddr.s_addr)
+ break;
+ }
+ if (i < imo->imo_num_memberships) {
+ error = EADDRINUSE;
+ break;
+ }
+ if (i == IP_MAX_MEMBERSHIPS) {
+ error = ETOOMANYREFS;
+ break;
+ }
+ /*
+ * Everything looks good; add a new record to the multicast
+ * address list for the given interface.
+ */
+ if ((imo->imo_membership[i] =
+ in_addmulti(&mreq->imr_multiaddr, ifp)) == NULL) {
+ error = ENOBUFS;
+ break;
+ }
+ ++imo->imo_num_memberships;
+ break;
+
+ case IP_DROP_MEMBERSHIP:
+ /*
+ * Drop a multicast group membership.
+ * Group must be a valid IP multicast address.
+ */
+ if (m == NULL || m->m_len != sizeof(struct ip_mreq)) {
+ error = EINVAL;
+ break;
+ }
+ mreq = mtod(m, struct ip_mreq *);
+ if (!IN_MULTICAST(ntohl(mreq->imr_multiaddr.s_addr))) {
+ error = EINVAL;
+ break;
+ }
+ /*
+ * If an interface address was specified, get a pointer
+ * to its ifnet structure.
+ */
+ if (mreq->imr_interface.s_addr == INADDR_ANY)
+ ifp = NULL;
+ else {
+ INADDR_TO_IFP(mreq->imr_interface, ifp);
+ if (ifp == NULL) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ }
+ /*
+ * Find the membership in the membership array.
+ */
+ for (i = 0; i < imo->imo_num_memberships; ++i) {
+ if ((ifp == NULL ||
+ imo->imo_membership[i]->inm_ifp == ifp) &&
+ imo->imo_membership[i]->inm_addr.s_addr ==
+ mreq->imr_multiaddr.s_addr)
+ break;
+ }
+ if (i == imo->imo_num_memberships) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ /*
+ * Give up the multicast address record to which the
+ * membership points.
+ */
+ in_delmulti(imo->imo_membership[i]);
+ /*
+ * Remove the gap in the membership array.
+ */
+ for (++i; i < imo->imo_num_memberships; ++i)
+ imo->imo_membership[i-1] = imo->imo_membership[i];
+ --imo->imo_num_memberships;
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ /*
+ * If all options have default values, no need to keep the mbuf.
+ */
+ if (imo->imo_multicast_ifp == NULL &&
+ imo->imo_multicast_ttl == IP_DEFAULT_MULTICAST_TTL &&
+ imo->imo_multicast_loop == IP_DEFAULT_MULTICAST_LOOP &&
+ imo->imo_num_memberships == 0) {
+ free(*imop, M_IPMOPTS);
+ *imop = NULL;
+ }
+
+ return (error);
+}
+
+/*
+ * Return the IP multicast options in response to user getsockopt().
+ */
+int
+ip_getmoptions(optname, imo, mp)
+ int optname;
+ register struct ip_moptions *imo;
+ register struct mbuf **mp;
+{
+ u_char *ttl;
+ u_char *loop;
+ struct in_addr *addr;
+ struct in_ifaddr *ia;
+
+ *mp = m_get(M_WAIT, MT_SOOPTS);
+
+ switch (optname) {
+
+ case IP_MULTICAST_IF:
+ addr = mtod(*mp, struct in_addr *);
+ (*mp)->m_len = sizeof(struct in_addr);
+ if (imo == NULL || imo->imo_multicast_ifp == NULL)
+ addr->s_addr = INADDR_ANY;
+ else {
+ IFP_TO_IA(imo->imo_multicast_ifp, ia);
+ addr->s_addr = (ia == NULL) ? INADDR_ANY
+ : IA_SIN(ia)->sin_addr.s_addr;
+ }
+ return (0);
+
+ case IP_MULTICAST_TTL:
+ ttl = mtod(*mp, u_char *);
+ (*mp)->m_len = 1;
+ *ttl = (imo == NULL) ? IP_DEFAULT_MULTICAST_TTL
+ : imo->imo_multicast_ttl;
+ return (0);
+
+ case IP_MULTICAST_LOOP:
+ loop = mtod(*mp, u_char *);
+ (*mp)->m_len = 1;
+ *loop = (imo == NULL) ? IP_DEFAULT_MULTICAST_LOOP
+ : imo->imo_multicast_loop;
+ return (0);
+
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
+/*
+ * Discard the IP multicast options.
+ */
+void
+ip_freemoptions(imo)
+ register struct ip_moptions *imo;
+{
+ register int i;
+
+ if (imo != NULL) {
+ for (i = 0; i < imo->imo_num_memberships; ++i)
+ in_delmulti(imo->imo_membership[i]);
+ free(imo, M_IPMOPTS);
+ }
+}
+
+/*
+ * Routine called from ip_output() to loop back a copy of an IP multicast
+ * packet to the input queue of a specified interface. Note that this
+ * calls the output routine of the loopback "driver", but with an interface
+ * pointer that might NOT be &loif -- easier than replicating that code here.
+ */
+static void
+ip_mloopback(ifp, m, dst)
+ struct ifnet *ifp;
+ register struct mbuf *m;
+ register struct sockaddr_in *dst;
+{
+ register struct ip *ip;
+ struct mbuf *copym;
+
+ copym = m_copy(m, 0, M_COPYALL);
+ if (copym != NULL) {
+ /*
+ * We don't bother to fragment if the IP length is greater
+ * than the interface's MTU. Can this possibly matter?
+ */
+ ip = mtod(copym, struct ip *);
+ ip->ip_len = htons((u_short)ip->ip_len);
+ ip->ip_off = htons((u_short)ip->ip_off);
+ ip->ip_sum = 0;
+ ip->ip_sum = in_cksum(copym, ip->ip_hl << 2);
+ (void) looutput(ifp, copym, (struct sockaddr *)dst, NULL);
+ }
+}
diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h
new file mode 100644
index 0000000..27eda5e
--- /dev/null
+++ b/sys/netinet/ip_var.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Overlay for ip header used by other protocols (tcp, udp).
+ */
+struct ipovly {
+ caddr_t ih_next, ih_prev; /* for protocol sequence q's */
+ u_char ih_x1; /* (unused) */
+ u_char ih_pr; /* protocol */
+ short ih_len; /* protocol length */
+ struct in_addr ih_src; /* source internet address */
+ struct in_addr ih_dst; /* destination internet address */
+};
+
+/*
+ * Ip reassembly queue structure. Each fragment
+ * being reassembled is attached to one of these structures.
+ * They are timed out after ipq_ttl drops to 0, and may also
+ * be reclaimed if memory becomes tight.
+ */
+struct ipq {
+ struct ipq *next,*prev; /* to other reass headers */
+ u_char ipq_ttl; /* time for reass q to live */
+ u_char ipq_p; /* protocol of this fragment */
+ u_short ipq_id; /* sequence id for reassembly */
+ struct ipasfrag *ipq_next,*ipq_prev;
+ /* to ip headers of fragments */
+ struct in_addr ipq_src,ipq_dst;
+};
+
+/*
+ * Ip header, when holding a fragment.
+ *
+ * Note: ipf_next must be at same offset as ipq_next above
+ */
+struct ipasfrag {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ u_char ip_hl:4,
+ ip_v:4;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ u_char ip_v:4,
+ ip_hl:4;
+#endif
+ u_char ipf_mff; /* XXX overlays ip_tos: use low bit
+ * to avoid destroying tos;
+ * copied from (ip_off&IP_MF) */
+ short ip_len;
+ u_short ip_id;
+ short ip_off;
+ u_char ip_ttl;
+ u_char ip_p;
+ u_short ip_sum;
+ struct ipasfrag *ipf_next; /* next fragment */
+ struct ipasfrag *ipf_prev; /* previous fragment */
+};
+
+/*
+ * Structure stored in mbuf in inpcb.ip_options
+ * and passed to ip_output when ip options are in use.
+ * The actual length of the options (including ipopt_dst)
+ * is in m_len.
+ */
+#define MAX_IPOPTLEN 40
+
+struct ipoption {
+ struct in_addr ipopt_dst; /* first-hop dst if source routed */
+ char ipopt_list[MAX_IPOPTLEN]; /* options proper */
+};
+
+/*
+ * Structure attached to inpcb.ip_moptions and
+ * passed to ip_output when IP multicast options are in use.
+ */
+struct ip_moptions {
+ struct ifnet *imo_multicast_ifp; /* ifp for outgoing multicasts */
+ u_char imo_multicast_ttl; /* TTL for outgoing multicasts */
+ u_char imo_multicast_loop; /* 1 => hear sends if a member */
+ u_short imo_num_memberships; /* no. memberships this socket */
+ struct in_multi *imo_membership[IP_MAX_MEMBERSHIPS];
+};
+
+struct ipstat {
+ u_long ips_total; /* total packets received */
+ u_long ips_badsum; /* checksum bad */
+ u_long ips_tooshort; /* packet too short */
+ u_long ips_toosmall; /* not enough data */
+ u_long ips_badhlen; /* ip header length < data size */
+ u_long ips_badlen; /* ip length < ip header length */
+ u_long ips_fragments; /* fragments received */
+ u_long ips_fragdropped; /* frags dropped (dups, out of space) */
+ u_long ips_fragtimeout; /* fragments timed out */
+ u_long ips_forward; /* packets forwarded */
+ u_long ips_cantforward; /* packets rcvd for unreachable dest */
+ u_long ips_redirectsent; /* packets forwarded on same net */
+ u_long ips_noproto; /* unknown or unsupported protocol */
+ u_long ips_delivered; /* datagrams delivered to upper level*/
+ u_long ips_localout; /* total ip packets generated here */
+ u_long ips_odropped; /* lost packets due to nobufs, etc. */
+ u_long ips_reassembled; /* total packets reassembled ok */
+ u_long ips_fragmented; /* datagrams sucessfully fragmented */
+ u_long ips_ofragments; /* output fragments created */
+ u_long ips_cantfrag; /* don't fragment flag was set, etc. */
+ u_long ips_badoptions; /* error in option processing */
+ u_long ips_noroute; /* packets discarded due to no route */
+ u_long ips_badvers; /* ip version != 4 */
+ u_long ips_rawout; /* total raw ip packets generated */
+};
+
+#ifdef KERNEL
+/* flags passed to ip_output as last parameter */
+#define IP_FORWARDING 0x1 /* most of ip header exists */
+#define IP_RAWOUTPUT 0x2 /* raw ip header exists */
+#define IP_ROUTETOIF SO_DONTROUTE /* bypass routing tables */
+#define IP_ALLOWBROADCAST SO_BROADCAST /* can send broadcast packets */
+
+struct ipstat ipstat;
+struct ipq ipq; /* ip reass. queue */
+u_short ip_id; /* ip packet ctr, for ids */
+int ip_defttl; /* default IP ttl */
+
+int in_control __P((struct socket *, int, caddr_t, struct ifnet *));
+int ip_ctloutput __P((int, struct socket *, int, int, struct mbuf **));
+void ip_deq __P((struct ipasfrag *));
+int ip_dooptions __P((struct mbuf *));
+void ip_drain __P((void));
+void ip_enq __P((struct ipasfrag *, struct ipasfrag *));
+void ip_forward __P((struct mbuf *, int));
+void ip_freef __P((struct ipq *));
+void ip_freemoptions __P((struct ip_moptions *));
+int ip_getmoptions __P((int, struct ip_moptions *, struct mbuf **));
+void ip_init __P((void));
+int ip_mforward __P((struct mbuf *, struct ifnet *));
+int ip_optcopy __P((struct ip *, struct ip *));
+int ip_output __P((struct mbuf *,
+ struct mbuf *, struct route *, int, struct ip_moptions *));
+int ip_pcbopts __P((struct mbuf **, struct mbuf *));
+struct ip *
+ ip_reass __P((struct ipasfrag *, struct ipq *));
+struct in_ifaddr *
+ ip_rtaddr __P((struct in_addr));
+int ip_setmoptions __P((int, struct ip_moptions **, struct mbuf *));
+void ip_slowtimo __P((void));
+struct mbuf *
+ ip_srcroute __P((void));
+void ip_stripoptions __P((struct mbuf *, struct mbuf *));
+int ip_sysctl __P((int *, u_int, void *, size_t *, void *, size_t));
+void ipintr __P((void));
+int rip_ctloutput __P((int, struct socket *, int, int, struct mbuf **));
+void rip_init __P((void));
+void rip_input __P((struct mbuf *));
+int rip_output __P((struct mbuf *, struct socket *, u_long));
+int rip_usrreq __P((struct socket *,
+ int, struct mbuf *, struct mbuf *, struct mbuf *));
+#endif
diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c
new file mode 100644
index 0000000..c8092ee
--- /dev/null
+++ b/sys/netinet/raw_ip.c
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)raw_ip.c 8.2 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_mroute.h>
+#include <netinet/in_pcb.h>
+
+struct inpcb rawinpcb;
+
+/*
+ * Nominal space allocated to a raw ip socket.
+ */
+#define RIPSNDQ 8192
+#define RIPRCVQ 8192
+
+/*
+ * Raw interface to IP protocol.
+ */
+
+/*
+ * Initialize raw connection block q.
+ */
+void
+rip_init()
+{
+
+ rawinpcb.inp_next = rawinpcb.inp_prev = &rawinpcb;
+}
+
+struct sockaddr_in ripsrc = { sizeof(ripsrc), AF_INET };
+/*
+ * Setup generic address and protocol structures
+ * for raw_input routine, then pass them along with
+ * mbuf chain.
+ */
+void
+rip_input(m)
+ struct mbuf *m;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register struct inpcb *inp;
+ struct socket *last = 0;
+
+ ripsrc.sin_addr = ip->ip_src;
+ for (inp = rawinpcb.inp_next; inp != &rawinpcb; inp = inp->inp_next) {
+ if (inp->inp_ip.ip_p && inp->inp_ip.ip_p != ip->ip_p)
+ continue;
+ if (inp->inp_laddr.s_addr &&
+ inp->inp_laddr.s_addr == ip->ip_dst.s_addr)
+ continue;
+ if (inp->inp_faddr.s_addr &&
+ inp->inp_faddr.s_addr == ip->ip_src.s_addr)
+ continue;
+ if (last) {
+ struct mbuf *n;
+ if (n = m_copy(m, 0, (int)M_COPYALL)) {
+ if (sbappendaddr(&last->so_rcv, &ripsrc,
+ n, (struct mbuf *)0) == 0)
+ /* should notify about lost packet */
+ m_freem(n);
+ else
+ sorwakeup(last);
+ }
+ }
+ last = inp->inp_socket;
+ }
+ if (last) {
+ if (sbappendaddr(&last->so_rcv, &ripsrc,
+ m, (struct mbuf *)0) == 0)
+ m_freem(m);
+ else
+ sorwakeup(last);
+ } else {
+ m_freem(m);
+ ipstat.ips_noproto++;
+ ipstat.ips_delivered--;
+ }
+}
+
+/*
+ * Generate IP header and pass packet to ip_output.
+ * Tack on options user may have setup with control call.
+ */
+int
+rip_output(m, so, dst)
+ register struct mbuf *m;
+ struct socket *so;
+ u_long dst;
+{
+ register struct ip *ip;
+ register struct inpcb *inp = sotoinpcb(so);
+ struct mbuf *opts;
+ int flags = (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST;
+
+ /*
+ * If the user handed us a complete IP packet, use it.
+ * Otherwise, allocate an mbuf for a header and fill it in.
+ */
+ if ((inp->inp_flags & INP_HDRINCL) == 0) {
+ M_PREPEND(m, sizeof(struct ip), M_WAIT);
+ ip = mtod(m, struct ip *);
+ ip->ip_tos = 0;
+ ip->ip_off = 0;
+ ip->ip_p = inp->inp_ip.ip_p;
+ ip->ip_len = m->m_pkthdr.len;
+ ip->ip_src = inp->inp_laddr;
+ ip->ip_dst.s_addr = dst;
+ ip->ip_ttl = MAXTTL;
+ opts = inp->inp_options;
+ } else {
+ ip = mtod(m, struct ip *);
+ if (ip->ip_id == 0)
+ ip->ip_id = htons(ip_id++);
+ opts = NULL;
+ /* XXX prevent ip_output from overwriting header fields */
+ flags |= IP_RAWOUTPUT;
+ ipstat.ips_rawout++;
+ }
+ return (ip_output(m, opts, &inp->inp_route, flags, inp->inp_moptions));
+}
+
+/*
+ * Raw IP socket option processing.
+ */
+int
+rip_ctloutput(op, so, level, optname, m)
+ int op;
+ struct socket *so;
+ int level, optname;
+ struct mbuf **m;
+{
+ register struct inpcb *inp = sotoinpcb(so);
+ register int error;
+
+ if (level != IPPROTO_IP)
+ return (EINVAL);
+
+ switch (optname) {
+
+ case IP_HDRINCL:
+ if (op == PRCO_SETOPT || op == PRCO_GETOPT) {
+ if (m == 0 || *m == 0 || (*m)->m_len < sizeof (int))
+ return (EINVAL);
+ if (op == PRCO_SETOPT) {
+ if (*mtod(*m, int *))
+ inp->inp_flags |= INP_HDRINCL;
+ else
+ inp->inp_flags &= ~INP_HDRINCL;
+ (void)m_free(*m);
+ } else {
+ (*m)->m_len = sizeof (int);
+ *mtod(*m, int *) = inp->inp_flags & INP_HDRINCL;
+ }
+ return (0);
+ }
+ break;
+
+ case DVMRP_INIT:
+ case DVMRP_DONE:
+ case DVMRP_ADD_VIF:
+ case DVMRP_DEL_VIF:
+ case DVMRP_ADD_LGRP:
+ case DVMRP_DEL_LGRP:
+ case DVMRP_ADD_MRT:
+ case DVMRP_DEL_MRT:
+#ifdef MROUTING
+ if (op == PRCO_SETOPT) {
+ error = ip_mrouter_cmd(optname, so, *m);
+ if (*m)
+ (void)m_free(*m);
+ } else
+ error = EINVAL;
+ return (error);
+#else
+ if (op == PRCO_SETOPT && *m)
+ (void)m_free(*m);
+ return (EOPNOTSUPP);
+#endif
+ }
+ return (ip_ctloutput(op, so, level, optname, m));
+}
+
+u_long rip_sendspace = RIPSNDQ;
+u_long rip_recvspace = RIPRCVQ;
+
+/*ARGSUSED*/
+int
+rip_usrreq(so, req, m, nam, control)
+ register struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *control;
+{
+ register int error = 0;
+ register struct inpcb *inp = sotoinpcb(so);
+#ifdef MROUTING
+ extern struct socket *ip_mrouter;
+#endif
+ switch (req) {
+
+ case PRU_ATTACH:
+ if (inp)
+ panic("rip_attach");
+ if ((so->so_state & SS_PRIV) == 0) {
+ error = EACCES;
+ break;
+ }
+ if ((error = soreserve(so, rip_sendspace, rip_recvspace)) ||
+ (error = in_pcballoc(so, &rawinpcb)))
+ break;
+ inp = (struct inpcb *)so->so_pcb;
+ inp->inp_ip.ip_p = (int)nam;
+ break;
+
+ case PRU_DISCONNECT:
+ if ((so->so_state & SS_ISCONNECTED) == 0) {
+ error = ENOTCONN;
+ break;
+ }
+ /* FALLTHROUGH */
+ case PRU_ABORT:
+ soisdisconnected(so);
+ /* FALLTHROUGH */
+ case PRU_DETACH:
+ if (inp == 0)
+ panic("rip_detach");
+#ifdef MROUTING
+ if (so == ip_mrouter)
+ ip_mrouter_done();
+#endif
+ in_pcbdetach(inp);
+ break;
+
+ case PRU_BIND:
+ {
+ struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *);
+
+ if (nam->m_len != sizeof(*addr)) {
+ error = EINVAL;
+ break;
+ }
+ if ((ifnet == 0) ||
+ ((addr->sin_family != AF_INET) &&
+ (addr->sin_family != AF_IMPLINK)) ||
+ (addr->sin_addr.s_addr &&
+ ifa_ifwithaddr((struct sockaddr *)addr) == 0)) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ inp->inp_laddr = addr->sin_addr;
+ break;
+ }
+ case PRU_CONNECT:
+ {
+ struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *);
+
+ if (nam->m_len != sizeof(*addr)) {
+ error = EINVAL;
+ break;
+ }
+ if (ifnet == 0) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ if ((addr->sin_family != AF_INET) &&
+ (addr->sin_family != AF_IMPLINK)) {
+ error = EAFNOSUPPORT;
+ break;
+ }
+ inp->inp_faddr = addr->sin_addr;
+ soisconnected(so);
+ break;
+ }
+
+ case PRU_CONNECT2:
+ error = EOPNOTSUPP;
+ break;
+
+ /*
+ * Mark the connection as being incapable of further input.
+ */
+ case PRU_SHUTDOWN:
+ socantsendmore(so);
+ break;
+
+ /*
+ * Ship a packet out. The appropriate raw output
+ * routine handles any massaging necessary.
+ */
+ case PRU_SEND:
+ {
+ register u_long dst;
+
+ if (so->so_state & SS_ISCONNECTED) {
+ if (nam) {
+ error = EISCONN;
+ break;
+ }
+ dst = inp->inp_faddr.s_addr;
+ } else {
+ if (nam == NULL) {
+ error = ENOTCONN;
+ break;
+ }
+ dst = mtod(nam, struct sockaddr_in *)->sin_addr.s_addr;
+ }
+ error = rip_output(m, so, dst);
+ m = NULL;
+ break;
+ }
+
+ case PRU_SENSE:
+ /*
+ * stat: don't bother with a blocksize.
+ */
+ return (0);
+
+ /*
+ * Not supported.
+ */
+ case PRU_RCVOOB:
+ case PRU_RCVD:
+ case PRU_LISTEN:
+ case PRU_ACCEPT:
+ case PRU_SENDOOB:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_SOCKADDR:
+ in_setsockaddr(inp, nam);
+ break;
+
+ case PRU_PEERADDR:
+ in_setpeeraddr(inp, nam);
+ break;
+
+ default:
+ panic("rip_usrreq");
+ }
+ if (m != NULL)
+ m_freem(m);
+ return (error);
+}
diff --git a/sys/netinet/tcp.h b/sys/netinet/tcp.h
new file mode 100644
index 0000000..6b77ff6
--- /dev/null
+++ b/sys/netinet/tcp.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp.h 8.1 (Berkeley) 6/10/93
+ */
+
+typedef u_long tcp_seq;
+/*
+ * TCP header.
+ * Per RFC 793, September, 1981.
+ */
+struct tcphdr {
+ u_short th_sport; /* source port */
+ u_short th_dport; /* destination port */
+ tcp_seq th_seq; /* sequence number */
+ tcp_seq th_ack; /* acknowledgement number */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ u_char th_x2:4, /* (unused) */
+ th_off:4; /* data offset */
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ u_char th_off:4, /* data offset */
+ th_x2:4; /* (unused) */
+#endif
+ u_char th_flags;
+#define TH_FIN 0x01
+#define TH_SYN 0x02
+#define TH_RST 0x04
+#define TH_PUSH 0x08
+#define TH_ACK 0x10
+#define TH_URG 0x20
+ u_short th_win; /* window */
+ u_short th_sum; /* checksum */
+ u_short th_urp; /* urgent pointer */
+};
+
+#define TCPOPT_EOL 0
+#define TCPOPT_NOP 1
+#define TCPOPT_MAXSEG 2
+#define TCPOLEN_MAXSEG 4
+#define TCPOPT_WINDOW 3
+#define TCPOLEN_WINDOW 3
+#define TCPOPT_SACK_PERMITTED 4 /* Experimental */
+#define TCPOLEN_SACK_PERMITTED 2
+#define TCPOPT_SACK 5 /* Experimental */
+#define TCPOPT_TIMESTAMP 8
+#define TCPOLEN_TIMESTAMP 10
+#define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */
+
+#define TCPOPT_TSTAMP_HDR \
+ (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP)
+
+/*
+ * Default maximum segment size for TCP.
+ * With an IP MSS of 576, this is 536,
+ * but 512 is probably more convenient.
+ * This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)).
+ */
+#define TCP_MSS 512
+
+#define TCP_MAXWIN 65535 /* largest value for (unscaled) window */
+
+#define TCP_MAX_WINSHIFT 14 /* maximum window shift */
+
+/*
+ * User-settable options (used with setsockopt).
+ */
+#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */
+#define TCP_MAXSEG 0x02 /* set maximum segment size */
diff --git a/sys/netinet/tcp_debug.c b/sys/netinet/tcp_debug.c
new file mode 100644
index 0000000..ddb3092
--- /dev/null
+++ b/sys/netinet/tcp_debug.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_debug.c 8.1 (Berkeley) 6/10/93
+ */
+
+#ifdef TCPDEBUG
+/* load symbolic names */
+#define PRUREQUESTS
+#define TCPSTATES
+#define TCPTIMERS
+#define TANAMES
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+
+#include <net/route.h>
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_debug.h>
+
+#ifdef TCPDEBUG
+int tcpconsdebug = 0;
+#endif
+/*
+ * Tcp debug routines
+ */
+void
+tcp_trace(act, ostate, tp, ti, req)
+ short act, ostate;
+ struct tcpcb *tp;
+ struct tcpiphdr *ti;
+ int req;
+{
+ tcp_seq seq, ack;
+ int len, flags;
+ struct tcp_debug *td = &tcp_debug[tcp_debx++];
+
+ if (tcp_debx == TCP_NDEBUG)
+ tcp_debx = 0;
+ td->td_time = iptime();
+ td->td_act = act;
+ td->td_ostate = ostate;
+ td->td_tcb = (caddr_t)tp;
+ if (tp)
+ td->td_cb = *tp;
+ else
+ bzero((caddr_t)&td->td_cb, sizeof (*tp));
+ if (ti)
+ td->td_ti = *ti;
+ else
+ bzero((caddr_t)&td->td_ti, sizeof (*ti));
+ td->td_req = req;
+#ifdef TCPDEBUG
+ if (tcpconsdebug == 0)
+ return;
+ if (tp)
+ printf("%x %s:", tp, tcpstates[ostate]);
+ else
+ printf("???????? ");
+ printf("%s ", tanames[act]);
+ switch (act) {
+
+ case TA_INPUT:
+ case TA_OUTPUT:
+ case TA_DROP:
+ if (ti == 0)
+ break;
+ seq = ti->ti_seq;
+ ack = ti->ti_ack;
+ len = ti->ti_len;
+ if (act == TA_OUTPUT) {
+ seq = ntohl(seq);
+ ack = ntohl(ack);
+ len = ntohs((u_short)len);
+ }
+ if (act == TA_OUTPUT)
+ len -= sizeof (struct tcphdr);
+ if (len)
+ printf("[%x..%x)", seq, seq+len);
+ else
+ printf("%x", seq);
+ printf("@%x, urp=%x", ack, ti->ti_urp);
+ flags = ti->ti_flags;
+ if (flags) {
+#ifndef lint
+ char *cp = "<";
+#define pf(f) { if (ti->ti_flags&TH_/**/f) { printf("%s%s", cp, "f"); cp = ","; } }
+ pf(SYN); pf(ACK); pf(FIN); pf(RST); pf(PUSH); pf(URG);
+#endif
+ printf(">");
+ }
+ break;
+
+ case TA_USER:
+ printf("%s", prurequests[req&0xff]);
+ if ((req & 0xff) == PRU_SLOWTIMO)
+ printf("<%s>", tcptimers[req>>8]);
+ break;
+ }
+ if (tp)
+ printf(" -> %s", tcpstates[tp->t_state]);
+ /* print out internal state of tp !?! */
+ printf("\n");
+ if (tp == 0)
+ return;
+ printf("\trcv_(nxt,wnd,up) (%x,%x,%x) snd_(una,nxt,max) (%x,%x,%x)\n",
+ tp->rcv_nxt, tp->rcv_wnd, tp->rcv_up, tp->snd_una, tp->snd_nxt,
+ tp->snd_max);
+ printf("\tsnd_(wl1,wl2,wnd) (%x,%x,%x)\n",
+ tp->snd_wl1, tp->snd_wl2, tp->snd_wnd);
+#endif /* TCPDEBUG */
+}
diff --git a/sys/netinet/tcp_debug.h b/sys/netinet/tcp_debug.h
new file mode 100644
index 0000000..c02c0cd
--- /dev/null
+++ b/sys/netinet/tcp_debug.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_debug.h 8.1 (Berkeley) 6/10/93
+ */
+
+struct tcp_debug {
+ n_time td_time;
+ short td_act;
+ short td_ostate;
+ caddr_t td_tcb;
+ struct tcpiphdr td_ti;
+ short td_req;
+ struct tcpcb td_cb;
+};
+
+#define TA_INPUT 0
+#define TA_OUTPUT 1
+#define TA_USER 2
+#define TA_RESPOND 3
+#define TA_DROP 4
+
+#ifdef TANAMES
+char *tanames[] =
+ { "input", "output", "user", "respond", "drop" };
+#endif
+
+#define TCP_NDEBUG 100
+struct tcp_debug tcp_debug[TCP_NDEBUG];
+int tcp_debx;
diff --git a/sys/netinet/tcp_fsm.h b/sys/netinet/tcp_fsm.h
new file mode 100644
index 0000000..c5da7fc
--- /dev/null
+++ b/sys/netinet/tcp_fsm.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_fsm.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * TCP FSM state definitions.
+ * Per RFC793, September, 1981.
+ */
+
+#define TCP_NSTATES 11
+
+#define TCPS_CLOSED 0 /* closed */
+#define TCPS_LISTEN 1 /* listening for connection */
+#define TCPS_SYN_SENT 2 /* active, have sent syn */
+#define TCPS_SYN_RECEIVED 3 /* have send and received syn */
+/* states < TCPS_ESTABLISHED are those where connections not established */
+#define TCPS_ESTABLISHED 4 /* established */
+#define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */
+/* states > TCPS_CLOSE_WAIT are those where user has closed */
+#define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */
+#define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */
+#define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */
+/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */
+#define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */
+#define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */
+
+#define TCPS_HAVERCVDSYN(s) ((s) >= TCPS_SYN_RECEIVED)
+#define TCPS_HAVERCVDFIN(s) ((s) >= TCPS_TIME_WAIT)
+
+#ifdef TCPOUTFLAGS
+/*
+ * Flags used when sending segments in tcp_output.
+ * Basic flags (TH_RST,TH_ACK,TH_SYN,TH_FIN) are totally
+ * determined by state, with the proviso that TH_FIN is sent only
+ * if all data queued for output is included in the segment.
+ */
+u_char tcp_outflags[TCP_NSTATES] = {
+ TH_RST|TH_ACK, 0, TH_SYN, TH_SYN|TH_ACK,
+ TH_ACK, TH_ACK,
+ TH_FIN|TH_ACK, TH_FIN|TH_ACK, TH_FIN|TH_ACK, TH_ACK, TH_ACK,
+};
+#endif
+
+#ifdef KPROF
+int tcp_acounts[TCP_NSTATES][PRU_NREQ];
+#endif
+
+#ifdef TCPSTATES
+char *tcpstates[] = {
+ "CLOSED", "LISTEN", "SYN_SENT", "SYN_RCVD",
+ "ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", "CLOSING",
+ "LAST_ACK", "FIN_WAIT_2", "TIME_WAIT",
+};
+#endif
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
new file mode 100644
index 0000000..121cb94
--- /dev/null
+++ b/sys/netinet/tcp_input.c
@@ -0,0 +1,1665 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_input.c 8.5 (Berkeley) 4/10/94
+ */
+
+#ifndef TUBA_INCLUDE
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_debug.h>
+
+int tcprexmtthresh = 3;
+struct tcpiphdr tcp_saveti;
+struct inpcb *tcp_last_inpcb = &tcb;
+
+extern u_long sb_max;
+
+#endif /* TUBA_INCLUDE */
+#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * PR_SLOWHZ)
+
+/* for modulo comparisons of timestamps */
+#define TSTMP_LT(a,b) ((int)((a)-(b)) < 0)
+#define TSTMP_GEQ(a,b) ((int)((a)-(b)) >= 0)
+
+
+/*
+ * Insert segment ti into reassembly queue of tcp with
+ * control block tp. Return TH_FIN if reassembly now includes
+ * a segment with FIN. The macro form does the common case inline
+ * (segment is the next to be received on an established connection,
+ * and the queue is empty), avoiding linkage into and removal
+ * from the queue and repetition of various conversions.
+ * Set DELACK for segments received in order, but ack immediately
+ * when segments are out of order (so fast retransmit can work).
+ */
+#define TCP_REASS(tp, ti, m, so, flags) { \
+ if ((ti)->ti_seq == (tp)->rcv_nxt && \
+ (tp)->seg_next == (struct tcpiphdr *)(tp) && \
+ (tp)->t_state == TCPS_ESTABLISHED) { \
+ tp->t_flags |= TF_DELACK; \
+ (tp)->rcv_nxt += (ti)->ti_len; \
+ flags = (ti)->ti_flags & TH_FIN; \
+ tcpstat.tcps_rcvpack++;\
+ tcpstat.tcps_rcvbyte += (ti)->ti_len;\
+ sbappend(&(so)->so_rcv, (m)); \
+ sorwakeup(so); \
+ } else { \
+ (flags) = tcp_reass((tp), (ti), (m)); \
+ tp->t_flags |= TF_ACKNOW; \
+ } \
+}
+#ifndef TUBA_INCLUDE
+
+int
+tcp_reass(tp, ti, m)
+ register struct tcpcb *tp;
+ register struct tcpiphdr *ti;
+ struct mbuf *m;
+{
+ register struct tcpiphdr *q;
+ struct socket *so = tp->t_inpcb->inp_socket;
+ int flags;
+
+ /*
+ * Call with ti==0 after become established to
+ * force pre-ESTABLISHED data up to user socket.
+ */
+ if (ti == 0)
+ goto present;
+
+ /*
+ * Find a segment which begins after this one does.
+ */
+ for (q = tp->seg_next; q != (struct tcpiphdr *)tp;
+ q = (struct tcpiphdr *)q->ti_next)
+ if (SEQ_GT(q->ti_seq, ti->ti_seq))
+ break;
+
+ /*
+ * If there is a preceding segment, it may provide some of
+ * our data already. If so, drop the data from the incoming
+ * segment. If it provides all of our data, drop us.
+ */
+ if ((struct tcpiphdr *)q->ti_prev != (struct tcpiphdr *)tp) {
+ register int i;
+ q = (struct tcpiphdr *)q->ti_prev;
+ /* conversion to int (in i) handles seq wraparound */
+ i = q->ti_seq + q->ti_len - ti->ti_seq;
+ if (i > 0) {
+ if (i >= ti->ti_len) {
+ tcpstat.tcps_rcvduppack++;
+ tcpstat.tcps_rcvdupbyte += ti->ti_len;
+ m_freem(m);
+ return (0);
+ }
+ m_adj(m, i);
+ ti->ti_len -= i;
+ ti->ti_seq += i;
+ }
+ q = (struct tcpiphdr *)(q->ti_next);
+ }
+ tcpstat.tcps_rcvoopack++;
+ tcpstat.tcps_rcvoobyte += ti->ti_len;
+ REASS_MBUF(ti) = m; /* XXX */
+
+ /*
+ * While we overlap succeeding segments trim them or,
+ * if they are completely covered, dequeue them.
+ */
+ while (q != (struct tcpiphdr *)tp) {
+ register int i = (ti->ti_seq + ti->ti_len) - q->ti_seq;
+ if (i <= 0)
+ break;
+ if (i < q->ti_len) {
+ q->ti_seq += i;
+ q->ti_len -= i;
+ m_adj(REASS_MBUF(q), i);
+ break;
+ }
+ q = (struct tcpiphdr *)q->ti_next;
+ m = REASS_MBUF((struct tcpiphdr *)q->ti_prev);
+ remque(q->ti_prev);
+ m_freem(m);
+ }
+
+ /*
+ * Stick new segment in its place.
+ */
+ insque(ti, q->ti_prev);
+
+present:
+ /*
+ * Present data to user, advancing rcv_nxt through
+ * completed sequence space.
+ */
+ if (TCPS_HAVERCVDSYN(tp->t_state) == 0)
+ return (0);
+ ti = tp->seg_next;
+ if (ti == (struct tcpiphdr *)tp || ti->ti_seq != tp->rcv_nxt)
+ return (0);
+ if (tp->t_state == TCPS_SYN_RECEIVED && ti->ti_len)
+ return (0);
+ do {
+ tp->rcv_nxt += ti->ti_len;
+ flags = ti->ti_flags & TH_FIN;
+ remque(ti);
+ m = REASS_MBUF(ti);
+ ti = (struct tcpiphdr *)ti->ti_next;
+ if (so->so_state & SS_CANTRCVMORE)
+ m_freem(m);
+ else
+ sbappend(&so->so_rcv, m);
+ } while (ti != (struct tcpiphdr *)tp && ti->ti_seq == tp->rcv_nxt);
+ sorwakeup(so);
+ return (flags);
+}
+
+/*
+ * TCP input routine, follows pages 65-76 of the
+ * protocol specification dated September, 1981 very closely.
+ */
+void
+tcp_input(m, iphlen)
+ register struct mbuf *m;
+ int iphlen;
+{
+ register struct tcpiphdr *ti;
+ register struct inpcb *inp;
+ caddr_t optp = NULL;
+ int optlen = 0;
+ int len, tlen, off;
+ register struct tcpcb *tp = 0;
+ register int tiflags;
+ struct socket *so = 0;
+ int todrop, acked, ourfinisacked, needoutput = 0;
+ short ostate = 0;
+ struct in_addr laddr;
+ int dropsocket = 0;
+ int iss = 0;
+ u_long tiwin, ts_val, ts_ecr;
+ int ts_present = 0;
+
+ tcpstat.tcps_rcvtotal++;
+ /*
+ * Get IP and TCP header together in first mbuf.
+ * Note: IP leaves IP header in first mbuf.
+ */
+ ti = mtod(m, struct tcpiphdr *);
+ if (iphlen > sizeof (struct ip))
+ ip_stripoptions(m, (struct mbuf *)0);
+ if (m->m_len < sizeof (struct tcpiphdr)) {
+ if ((m = m_pullup(m, sizeof (struct tcpiphdr))) == 0) {
+ tcpstat.tcps_rcvshort++;
+ return;
+ }
+ ti = mtod(m, struct tcpiphdr *);
+ }
+
+ /*
+ * Checksum extended TCP header and data.
+ */
+ tlen = ((struct ip *)ti)->ip_len;
+ len = sizeof (struct ip) + tlen;
+ ti->ti_next = ti->ti_prev = 0;
+ ti->ti_x1 = 0;
+ ti->ti_len = (u_short)tlen;
+ HTONS(ti->ti_len);
+ if (ti->ti_sum = in_cksum(m, len)) {
+ tcpstat.tcps_rcvbadsum++;
+ goto drop;
+ }
+#endif /* TUBA_INCLUDE */
+
+ /*
+ * Check that TCP offset makes sense,
+ * pull out TCP options and adjust length. XXX
+ */
+ off = ti->ti_off << 2;
+ if (off < sizeof (struct tcphdr) || off > tlen) {
+ tcpstat.tcps_rcvbadoff++;
+ goto drop;
+ }
+ tlen -= off;
+ ti->ti_len = tlen;
+ if (off > sizeof (struct tcphdr)) {
+ if (m->m_len < sizeof(struct ip) + off) {
+ if ((m = m_pullup(m, sizeof (struct ip) + off)) == 0) {
+ tcpstat.tcps_rcvshort++;
+ return;
+ }
+ ti = mtod(m, struct tcpiphdr *);
+ }
+ optlen = off - sizeof (struct tcphdr);
+ optp = mtod(m, caddr_t) + sizeof (struct tcpiphdr);
+ /*
+ * Do quick retrieval of timestamp options ("options
+ * prediction?"). If timestamp is the only option and it's
+ * formatted as recommended in RFC 1323 appendix A, we
+ * quickly get the values now and not bother calling
+ * tcp_dooptions(), etc.
+ */
+ if ((optlen == TCPOLEN_TSTAMP_APPA ||
+ (optlen > TCPOLEN_TSTAMP_APPA &&
+ optp[TCPOLEN_TSTAMP_APPA] == TCPOPT_EOL)) &&
+ *(u_long *)optp == htonl(TCPOPT_TSTAMP_HDR) &&
+ (ti->ti_flags & TH_SYN) == 0) {
+ ts_present = 1;
+ ts_val = ntohl(*(u_long *)(optp + 4));
+ ts_ecr = ntohl(*(u_long *)(optp + 8));
+ optp = NULL; /* we've parsed the options */
+ }
+ }
+ tiflags = ti->ti_flags;
+
+ /*
+ * Convert TCP protocol specific fields to host format.
+ */
+ NTOHL(ti->ti_seq);
+ NTOHL(ti->ti_ack);
+ NTOHS(ti->ti_win);
+ NTOHS(ti->ti_urp);
+
+ /*
+ * Locate pcb for segment.
+ */
+findpcb:
+ inp = tcp_last_inpcb;
+ if (inp->inp_lport != ti->ti_dport ||
+ inp->inp_fport != ti->ti_sport ||
+ inp->inp_faddr.s_addr != ti->ti_src.s_addr ||
+ inp->inp_laddr.s_addr != ti->ti_dst.s_addr) {
+ inp = in_pcblookup(&tcb, ti->ti_src, ti->ti_sport,
+ ti->ti_dst, ti->ti_dport, INPLOOKUP_WILDCARD);
+ if (inp)
+ tcp_last_inpcb = inp;
+ ++tcpstat.tcps_pcbcachemiss;
+ }
+
+ /*
+ * If the state is CLOSED (i.e., TCB does not exist) then
+ * all data in the incoming segment is discarded.
+ * If the TCB exists but is in CLOSED state, it is embryonic,
+ * but should either do a listen or a connect soon.
+ */
+ if (inp == 0)
+ goto dropwithreset;
+ tp = intotcpcb(inp);
+ if (tp == 0)
+ goto dropwithreset;
+ if (tp->t_state == TCPS_CLOSED)
+ goto drop;
+
+ /* Unscale the window into a 32-bit value. */
+ if ((tiflags & TH_SYN) == 0)
+ tiwin = ti->ti_win << tp->snd_scale;
+ else
+ tiwin = ti->ti_win;
+
+ so = inp->inp_socket;
+ if (so->so_options & (SO_DEBUG|SO_ACCEPTCONN)) {
+ if (so->so_options & SO_DEBUG) {
+ ostate = tp->t_state;
+ tcp_saveti = *ti;
+ }
+ if (so->so_options & SO_ACCEPTCONN) {
+ so = sonewconn(so, 0);
+ if (so == 0)
+ goto drop;
+ /*
+ * This is ugly, but ....
+ *
+ * Mark socket as temporary until we're
+ * committed to keeping it. The code at
+ * ``drop'' and ``dropwithreset'' check the
+ * flag dropsocket to see if the temporary
+ * socket created here should be discarded.
+ * We mark the socket as discardable until
+ * we're committed to it below in TCPS_LISTEN.
+ */
+ dropsocket++;
+ inp = (struct inpcb *)so->so_pcb;
+ inp->inp_laddr = ti->ti_dst;
+ inp->inp_lport = ti->ti_dport;
+#if BSD>=43
+ inp->inp_options = ip_srcroute();
+#endif
+ tp = intotcpcb(inp);
+ tp->t_state = TCPS_LISTEN;
+
+ /* Compute proper scaling value from buffer space
+ */
+ while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
+ TCP_MAXWIN << tp->request_r_scale < so->so_rcv.sb_hiwat)
+ tp->request_r_scale++;
+ }
+ }
+
+ /*
+ * Segment received on connection.
+ * Reset idle time and keep-alive timer.
+ */
+ tp->t_idle = 0;
+ tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+
+ /*
+ * Process options if not in LISTEN state,
+ * else do it below (after getting remote address).
+ */
+ if (optp && tp->t_state != TCPS_LISTEN)
+ tcp_dooptions(tp, optp, optlen, ti,
+ &ts_present, &ts_val, &ts_ecr);
+
+ /*
+ * Header prediction: check for the two common cases
+ * of a uni-directional data xfer. If the packet has
+ * no control flags, is in-sequence, the window didn't
+ * change and we're not retransmitting, it's a
+ * candidate. If the length is zero and the ack moved
+ * forward, we're the sender side of the xfer. Just
+ * free the data acked & wake any higher level process
+ * that was blocked waiting for space. If the length
+ * is non-zero and the ack didn't move, we're the
+ * receiver side. If we're getting packets in-order
+ * (the reassembly queue is empty), add the data to
+ * the socket buffer and note that we need a delayed ack.
+ */
+ if (tp->t_state == TCPS_ESTABLISHED &&
+ (tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK &&
+ (!ts_present || TSTMP_GEQ(ts_val, tp->ts_recent)) &&
+ ti->ti_seq == tp->rcv_nxt &&
+ tiwin && tiwin == tp->snd_wnd &&
+ tp->snd_nxt == tp->snd_max) {
+
+ /*
+ * If last ACK falls within this segment's sequence numbers,
+ * record the timestamp.
+ */
+ if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) &&
+ SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len)) {
+ tp->ts_recent_age = tcp_now;
+ tp->ts_recent = ts_val;
+ }
+
+ if (ti->ti_len == 0) {
+ if (SEQ_GT(ti->ti_ack, tp->snd_una) &&
+ SEQ_LEQ(ti->ti_ack, tp->snd_max) &&
+ tp->snd_cwnd >= tp->snd_wnd) {
+ /*
+ * this is a pure ack for outstanding data.
+ */
+ ++tcpstat.tcps_predack;
+ if (ts_present)
+ tcp_xmit_timer(tp, tcp_now-ts_ecr+1);
+ else if (tp->t_rtt &&
+ SEQ_GT(ti->ti_ack, tp->t_rtseq))
+ tcp_xmit_timer(tp, tp->t_rtt);
+ acked = ti->ti_ack - tp->snd_una;
+ tcpstat.tcps_rcvackpack++;
+ tcpstat.tcps_rcvackbyte += acked;
+ sbdrop(&so->so_snd, acked);
+ tp->snd_una = ti->ti_ack;
+ m_freem(m);
+
+ /*
+ * If all outstanding data are acked, stop
+ * retransmit timer, otherwise restart timer
+ * using current (possibly backed-off) value.
+ * If process is waiting for space,
+ * wakeup/selwakeup/signal. If data
+ * are ready to send, let tcp_output
+ * decide between more output or persist.
+ */
+ if (tp->snd_una == tp->snd_max)
+ tp->t_timer[TCPT_REXMT] = 0;
+ else if (tp->t_timer[TCPT_PERSIST] == 0)
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+
+ if (so->so_snd.sb_flags & SB_NOTIFY)
+ sowwakeup(so);
+ if (so->so_snd.sb_cc)
+ (void) tcp_output(tp);
+ return;
+ }
+ } else if (ti->ti_ack == tp->snd_una &&
+ tp->seg_next == (struct tcpiphdr *)tp &&
+ ti->ti_len <= sbspace(&so->so_rcv)) {
+ /*
+ * this is a pure, in-sequence data packet
+ * with nothing on the reassembly queue and
+ * we have enough buffer space to take it.
+ */
+ ++tcpstat.tcps_preddat;
+ tp->rcv_nxt += ti->ti_len;
+ tcpstat.tcps_rcvpack++;
+ tcpstat.tcps_rcvbyte += ti->ti_len;
+ /*
+ * Drop TCP, IP headers and TCP options then add data
+ * to socket buffer.
+ */
+ m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ sbappend(&so->so_rcv, m);
+ sorwakeup(so);
+ /*
+ * If this is a small packet, then ACK now - with Nagel
+ * congestion avoidance sender won't send more until
+ * he gets an ACK.
+ */
+ if ((unsigned)ti->ti_len < tp->t_maxseg) {
+ tp->t_flags |= TF_ACKNOW;
+ tcp_output(tp);
+ } else {
+ tp->t_flags |= TF_DELACK;
+ }
+ return;
+ }
+ }
+
+ /*
+ * Drop TCP, IP headers and TCP options.
+ */
+ m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+
+ /*
+ * Calculate amount of space in receive window,
+ * and then do TCP input processing.
+ * Receive window is amount of space in rcv queue,
+ * but not less than advertised window.
+ */
+ { int win;
+
+ win = sbspace(&so->so_rcv);
+ if (win < 0)
+ win = 0;
+ tp->rcv_wnd = max(win, (int)(tp->rcv_adv - tp->rcv_nxt));
+ }
+
+ switch (tp->t_state) {
+
+ /*
+ * If the state is LISTEN then ignore segment if it contains an RST.
+ * If the segment contains an ACK then it is bad and send a RST.
+ * If it does not contain a SYN then it is not interesting; drop it.
+ * Don't bother responding if the destination was a broadcast.
+ * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial
+ * tp->iss, and send a segment:
+ * <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK>
+ * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss.
+ * Fill in remote peer address fields if not previously specified.
+ * Enter SYN_RECEIVED state, and process any other fields of this
+ * segment in this state.
+ */
+ case TCPS_LISTEN: {
+ struct mbuf *am;
+ register struct sockaddr_in *sin;
+
+ if (tiflags & TH_RST)
+ goto drop;
+ if (tiflags & TH_ACK)
+ goto dropwithreset;
+ if ((tiflags & TH_SYN) == 0)
+ goto drop;
+ /*
+ * RFC1122 4.2.3.10, p. 104: discard bcast/mcast SYN
+ * in_broadcast() should never return true on a received
+ * packet with M_BCAST not set.
+ */
+ if (m->m_flags & (M_BCAST|M_MCAST) ||
+ IN_MULTICAST(ntohl(ti->ti_dst.s_addr)))
+ goto drop;
+ am = m_get(M_DONTWAIT, MT_SONAME); /* XXX */
+ if (am == NULL)
+ goto drop;
+ am->m_len = sizeof (struct sockaddr_in);
+ sin = mtod(am, struct sockaddr_in *);
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_addr = ti->ti_src;
+ sin->sin_port = ti->ti_sport;
+ bzero((caddr_t)sin->sin_zero, sizeof(sin->sin_zero));
+ laddr = inp->inp_laddr;
+ if (inp->inp_laddr.s_addr == INADDR_ANY)
+ inp->inp_laddr = ti->ti_dst;
+ if (in_pcbconnect(inp, am)) {
+ inp->inp_laddr = laddr;
+ (void) m_free(am);
+ goto drop;
+ }
+ (void) m_free(am);
+ tp->t_template = tcp_template(tp);
+ if (tp->t_template == 0) {
+ tp = tcp_drop(tp, ENOBUFS);
+ dropsocket = 0; /* socket is already gone */
+ goto drop;
+ }
+ if (optp)
+ tcp_dooptions(tp, optp, optlen, ti,
+ &ts_present, &ts_val, &ts_ecr);
+ if (iss)
+ tp->iss = iss;
+ else
+ tp->iss = tcp_iss;
+ tcp_iss += TCP_ISSINCR/2;
+ tp->irs = ti->ti_seq;
+ tcp_sendseqinit(tp);
+ tcp_rcvseqinit(tp);
+ tp->t_flags |= TF_ACKNOW;
+ tp->t_state = TCPS_SYN_RECEIVED;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ dropsocket = 0; /* committed to socket */
+ tcpstat.tcps_accepts++;
+ goto trimthenstep6;
+ }
+
+ /*
+ * If the state is SYN_SENT:
+ * if seg contains an ACK, but not for our SYN, drop the input.
+ * if seg contains a RST, then drop the connection.
+ * if seg does not contain SYN, then drop it.
+ * Otherwise this is an acceptable SYN segment
+ * initialize tp->rcv_nxt and tp->irs
+ * if seg contains ack then advance tp->snd_una
+ * if SYN has been acked change to ESTABLISHED else SYN_RCVD state
+ * arrange for segment to be acked (eventually)
+ * continue processing rest of data/controls, beginning with URG
+ */
+ case TCPS_SYN_SENT:
+ if ((tiflags & TH_ACK) &&
+ (SEQ_LEQ(ti->ti_ack, tp->iss) ||
+ SEQ_GT(ti->ti_ack, tp->snd_max)))
+ goto dropwithreset;
+ if (tiflags & TH_RST) {
+ if (tiflags & TH_ACK)
+ tp = tcp_drop(tp, ECONNREFUSED);
+ goto drop;
+ }
+ if ((tiflags & TH_SYN) == 0)
+ goto drop;
+ if (tiflags & TH_ACK) {
+ tp->snd_una = ti->ti_ack;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_una))
+ tp->snd_nxt = tp->snd_una;
+ }
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->irs = ti->ti_seq;
+ tcp_rcvseqinit(tp);
+ tp->t_flags |= TF_ACKNOW;
+ if (tiflags & TH_ACK && SEQ_GT(tp->snd_una, tp->iss)) {
+ tcpstat.tcps_connects++;
+ soisconnected(so);
+ tp->t_state = TCPS_ESTABLISHED;
+ /* Do window scaling on this connection? */
+ if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) ==
+ (TF_RCVD_SCALE|TF_REQ_SCALE)) {
+ tp->snd_scale = tp->requested_s_scale;
+ tp->rcv_scale = tp->request_r_scale;
+ }
+ (void) tcp_reass(tp, (struct tcpiphdr *)0,
+ (struct mbuf *)0);
+ /*
+ * if we didn't have to retransmit the SYN,
+ * use its rtt as our initial srtt & rtt var.
+ */
+ if (tp->t_rtt)
+ tcp_xmit_timer(tp, tp->t_rtt);
+ } else
+ tp->t_state = TCPS_SYN_RECEIVED;
+
+trimthenstep6:
+ /*
+ * Advance ti->ti_seq to correspond to first data byte.
+ * If data, trim to stay within window,
+ * dropping FIN if necessary.
+ */
+ ti->ti_seq++;
+ if (ti->ti_len > tp->rcv_wnd) {
+ todrop = ti->ti_len - tp->rcv_wnd;
+ m_adj(m, -todrop);
+ ti->ti_len = tp->rcv_wnd;
+ tiflags &= ~TH_FIN;
+ tcpstat.tcps_rcvpackafterwin++;
+ tcpstat.tcps_rcvbyteafterwin += todrop;
+ }
+ tp->snd_wl1 = ti->ti_seq - 1;
+ tp->rcv_up = ti->ti_seq;
+ goto step6;
+ }
+
+ /*
+ * States other than LISTEN or SYN_SENT.
+ * First check timestamp, if present.
+ * Then check that at least some bytes of segment are within
+ * receive window. If segment begins before rcv_nxt,
+ * drop leading data (and SYN); if nothing left, just ack.
+ *
+ * RFC 1323 PAWS: If we have a timestamp reply on this segment
+ * and it's less than ts_recent, drop it.
+ */
+ if (ts_present && (tiflags & TH_RST) == 0 && tp->ts_recent &&
+ TSTMP_LT(ts_val, tp->ts_recent)) {
+
+ /* Check to see if ts_recent is over 24 days old. */
+ if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) {
+ /*
+ * Invalidate ts_recent. If this segment updates
+ * ts_recent, the age will be reset later and ts_recent
+ * will get a valid value. If it does not, setting
+ * ts_recent to zero will at least satisfy the
+ * requirement that zero be placed in the timestamp
+ * echo reply when ts_recent isn't valid. The
+ * age isn't reset until we get a valid ts_recent
+ * because we don't want out-of-order segments to be
+ * dropped when ts_recent is old.
+ */
+ tp->ts_recent = 0;
+ } else {
+ tcpstat.tcps_rcvduppack++;
+ tcpstat.tcps_rcvdupbyte += ti->ti_len;
+ tcpstat.tcps_pawsdrop++;
+ goto dropafterack;
+ }
+ }
+
+ todrop = tp->rcv_nxt - ti->ti_seq;
+ if (todrop > 0) {
+ if (tiflags & TH_SYN) {
+ tiflags &= ~TH_SYN;
+ ti->ti_seq++;
+ if (ti->ti_urp > 1)
+ ti->ti_urp--;
+ else
+ tiflags &= ~TH_URG;
+ todrop--;
+ }
+ if (todrop >= ti->ti_len) {
+ tcpstat.tcps_rcvduppack++;
+ tcpstat.tcps_rcvdupbyte += ti->ti_len;
+ /*
+ * If segment is just one to the left of the window,
+ * check two special cases:
+ * 1. Don't toss RST in response to 4.2-style keepalive.
+ * 2. If the only thing to drop is a FIN, we can drop
+ * it, but check the ACK or we will get into FIN
+ * wars if our FINs crossed (both CLOSING).
+ * In either case, send ACK to resynchronize,
+ * but keep on processing for RST or ACK.
+ */
+ if ((tiflags & TH_FIN && todrop == ti->ti_len + 1)
+#ifdef TCP_COMPAT_42
+ || (tiflags & TH_RST && ti->ti_seq == tp->rcv_nxt - 1)
+#endif
+ ) {
+ todrop = ti->ti_len;
+ tiflags &= ~TH_FIN;
+ tp->t_flags |= TF_ACKNOW;
+ } else {
+ /*
+ * Handle the case when a bound socket connects
+ * to itself. Allow packets with a SYN and
+ * an ACK to continue with the processing.
+ */
+ if (todrop != 0 || (tiflags & TH_ACK) == 0)
+ goto dropafterack;
+ }
+ } else {
+ tcpstat.tcps_rcvpartduppack++;
+ tcpstat.tcps_rcvpartdupbyte += todrop;
+ }
+ m_adj(m, todrop);
+ ti->ti_seq += todrop;
+ ti->ti_len -= todrop;
+ if (ti->ti_urp > todrop)
+ ti->ti_urp -= todrop;
+ else {
+ tiflags &= ~TH_URG;
+ ti->ti_urp = 0;
+ }
+ }
+
+ /*
+ * If new data are received on a connection after the
+ * user processes are gone, then RST the other end.
+ */
+ if ((so->so_state & SS_NOFDREF) &&
+ tp->t_state > TCPS_CLOSE_WAIT && ti->ti_len) {
+ tp = tcp_close(tp);
+ tcpstat.tcps_rcvafterclose++;
+ goto dropwithreset;
+ }
+
+ /*
+ * If segment ends after window, drop trailing data
+ * (and PUSH and FIN); if nothing left, just ACK.
+ */
+ todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd);
+ if (todrop > 0) {
+ tcpstat.tcps_rcvpackafterwin++;
+ if (todrop >= ti->ti_len) {
+ tcpstat.tcps_rcvbyteafterwin += ti->ti_len;
+ /*
+ * If a new connection request is received
+ * while in TIME_WAIT, drop the old connection
+ * and start over if the sequence numbers
+ * are above the previous ones.
+ */
+ if (tiflags & TH_SYN &&
+ tp->t_state == TCPS_TIME_WAIT &&
+ SEQ_GT(ti->ti_seq, tp->rcv_nxt)) {
+ iss = tp->rcv_nxt + TCP_ISSINCR;
+ tp = tcp_close(tp);
+ goto findpcb;
+ }
+ /*
+ * If window is closed can only take segments at
+ * window edge, and have to drop data and PUSH from
+ * incoming segments. Continue processing, but
+ * remember to ack. Otherwise, drop segment
+ * and ack.
+ */
+ if (tp->rcv_wnd == 0 && ti->ti_seq == tp->rcv_nxt) {
+ tp->t_flags |= TF_ACKNOW;
+ tcpstat.tcps_rcvwinprobe++;
+ } else
+ goto dropafterack;
+ } else
+ tcpstat.tcps_rcvbyteafterwin += todrop;
+ m_adj(m, -todrop);
+ ti->ti_len -= todrop;
+ tiflags &= ~(TH_PUSH|TH_FIN);
+ }
+
+ /*
+ * If last ACK falls within this segment's sequence numbers,
+ * record its timestamp.
+ */
+ if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) &&
+ SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len +
+ ((tiflags & (TH_SYN|TH_FIN)) != 0))) {
+ tp->ts_recent_age = tcp_now;
+ tp->ts_recent = ts_val;
+ }
+
+ /*
+ * If the RST bit is set examine the state:
+ * SYN_RECEIVED STATE:
+ * If passive open, return to LISTEN state.
+ * If active open, inform user that connection was refused.
+ * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES:
+ * Inform user that connection was reset, and close tcb.
+ * CLOSING, LAST_ACK, TIME_WAIT STATES
+ * Close the tcb.
+ */
+ if (tiflags&TH_RST) switch (tp->t_state) {
+
+ case TCPS_SYN_RECEIVED:
+ so->so_error = ECONNREFUSED;
+ goto close;
+
+ case TCPS_ESTABLISHED:
+ case TCPS_FIN_WAIT_1:
+ case TCPS_FIN_WAIT_2:
+ case TCPS_CLOSE_WAIT:
+ so->so_error = ECONNRESET;
+ close:
+ tp->t_state = TCPS_CLOSED;
+ tcpstat.tcps_drops++;
+ tp = tcp_close(tp);
+ goto drop;
+
+ case TCPS_CLOSING:
+ case TCPS_LAST_ACK:
+ case TCPS_TIME_WAIT:
+ tp = tcp_close(tp);
+ goto drop;
+ }
+
+ /*
+ * If a SYN is in the window, then this is an
+ * error and we send an RST and drop the connection.
+ */
+ if (tiflags & TH_SYN) {
+ tp = tcp_drop(tp, ECONNRESET);
+ goto dropwithreset;
+ }
+
+ /*
+ * If the ACK bit is off we drop the segment and return.
+ */
+ if ((tiflags & TH_ACK) == 0)
+ goto drop;
+
+ /*
+ * Ack processing.
+ */
+ switch (tp->t_state) {
+
+ /*
+ * In SYN_RECEIVED state if the ack ACKs our SYN then enter
+ * ESTABLISHED state and continue processing, otherwise
+ * send an RST.
+ */
+ case TCPS_SYN_RECEIVED:
+ if (SEQ_GT(tp->snd_una, ti->ti_ack) ||
+ SEQ_GT(ti->ti_ack, tp->snd_max))
+ goto dropwithreset;
+ tcpstat.tcps_connects++;
+ soisconnected(so);
+ tp->t_state = TCPS_ESTABLISHED;
+ /* Do window scaling? */
+ if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) ==
+ (TF_RCVD_SCALE|TF_REQ_SCALE)) {
+ tp->snd_scale = tp->requested_s_scale;
+ tp->rcv_scale = tp->request_r_scale;
+ }
+ (void) tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0);
+ tp->snd_wl1 = ti->ti_seq - 1;
+ /* fall into ... */
+
+ /*
+ * In ESTABLISHED state: drop duplicate ACKs; ACK out of range
+ * ACKs. If the ack is in the range
+ * tp->snd_una < ti->ti_ack <= tp->snd_max
+ * then advance tp->snd_una to ti->ti_ack and drop
+ * data from the retransmission queue. If this ACK reflects
+ * more up to date window information we update our window information.
+ */
+ case TCPS_ESTABLISHED:
+ case TCPS_FIN_WAIT_1:
+ case TCPS_FIN_WAIT_2:
+ case TCPS_CLOSE_WAIT:
+ case TCPS_CLOSING:
+ case TCPS_LAST_ACK:
+ case TCPS_TIME_WAIT:
+
+ if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) {
+ if (ti->ti_len == 0 && tiwin == tp->snd_wnd) {
+ tcpstat.tcps_rcvdupack++;
+ /*
+ * If we have outstanding data (other than
+ * a window probe), this is a completely
+ * duplicate ack (ie, window info didn't
+ * change), the ack is the biggest we've
+ * seen and we've seen exactly our rexmt
+ * threshhold of them, assume a packet
+ * has been dropped and retransmit it.
+ * Kludge snd_nxt & the congestion
+ * window so we send only this one
+ * packet.
+ *
+ * We know we're losing at the current
+ * window size so do congestion avoidance
+ * (set ssthresh to half the current window
+ * and pull our congestion window back to
+ * the new ssthresh).
+ *
+ * Dup acks mean that packets have left the
+ * network (they're now cached at the receiver)
+ * so bump cwnd by the amount in the receiver
+ * to keep a constant cwnd packets in the
+ * network.
+ */
+ if (tp->t_timer[TCPT_REXMT] == 0 ||
+ ti->ti_ack != tp->snd_una)
+ tp->t_dupacks = 0;
+ else if (++tp->t_dupacks == tcprexmtthresh) {
+ tcp_seq onxt = tp->snd_nxt;
+ u_int win =
+ min(tp->snd_wnd, tp->snd_cwnd) / 2 /
+ tp->t_maxseg;
+
+ if (win < 2)
+ win = 2;
+ tp->snd_ssthresh = win * tp->t_maxseg;
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->t_rtt = 0;
+ tp->snd_nxt = ti->ti_ack;
+ tp->snd_cwnd = tp->t_maxseg;
+ (void) tcp_output(tp);
+ tp->snd_cwnd = tp->snd_ssthresh +
+ tp->t_maxseg * tp->t_dupacks;
+ if (SEQ_GT(onxt, tp->snd_nxt))
+ tp->snd_nxt = onxt;
+ goto drop;
+ } else if (tp->t_dupacks > tcprexmtthresh) {
+ tp->snd_cwnd += tp->t_maxseg;
+ (void) tcp_output(tp);
+ goto drop;
+ }
+ } else
+ tp->t_dupacks = 0;
+ break;
+ }
+ /*
+ * If the congestion window was inflated to account
+ * for the other side's cached packets, retract it.
+ */
+ if (tp->t_dupacks > tcprexmtthresh &&
+ tp->snd_cwnd > tp->snd_ssthresh)
+ tp->snd_cwnd = tp->snd_ssthresh;
+ tp->t_dupacks = 0;
+ if (SEQ_GT(ti->ti_ack, tp->snd_max)) {
+ tcpstat.tcps_rcvacktoomuch++;
+ goto dropafterack;
+ }
+ acked = ti->ti_ack - tp->snd_una;
+ tcpstat.tcps_rcvackpack++;
+ tcpstat.tcps_rcvackbyte += acked;
+
+ /*
+ * If we have a timestamp reply, update smoothed
+ * round trip time. If no timestamp is present but
+ * transmit timer is running and timed sequence
+ * number was acked, update smoothed round trip time.
+ * Since we now have an rtt measurement, cancel the
+ * timer backoff (cf., Phil Karn's retransmit alg.).
+ * Recompute the initial retransmit timer.
+ */
+ if (ts_present)
+ tcp_xmit_timer(tp, tcp_now-ts_ecr+1);
+ else if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq))
+ tcp_xmit_timer(tp,tp->t_rtt);
+
+ /*
+ * If all outstanding data is acked, stop retransmit
+ * timer and remember to restart (more output or persist).
+ * If there is more data to be acked, restart retransmit
+ * timer, using current (possibly backed-off) value.
+ */
+ if (ti->ti_ack == tp->snd_max) {
+ tp->t_timer[TCPT_REXMT] = 0;
+ needoutput = 1;
+ } else if (tp->t_timer[TCPT_PERSIST] == 0)
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ /*
+ * When new data is acked, open the congestion window.
+ * If the window gives us less than ssthresh packets
+ * in flight, open exponentially (maxseg per packet).
+ * Otherwise open linearly: maxseg per window
+ * (maxseg^2 / cwnd per packet), plus a constant
+ * fraction of a packet (maxseg/8) to help larger windows
+ * open quickly enough.
+ */
+ {
+ register u_int cw = tp->snd_cwnd;
+ register u_int incr = tp->t_maxseg;
+
+ if (cw > tp->snd_ssthresh)
+ incr = incr * incr / cw + incr / 8;
+ tp->snd_cwnd = min(cw + incr, TCP_MAXWIN<<tp->snd_scale);
+ }
+ if (acked > so->so_snd.sb_cc) {
+ tp->snd_wnd -= so->so_snd.sb_cc;
+ sbdrop(&so->so_snd, (int)so->so_snd.sb_cc);
+ ourfinisacked = 1;
+ } else {
+ sbdrop(&so->so_snd, acked);
+ tp->snd_wnd -= acked;
+ ourfinisacked = 0;
+ }
+ if (so->so_snd.sb_flags & SB_NOTIFY)
+ sowwakeup(so);
+ tp->snd_una = ti->ti_ack;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_una))
+ tp->snd_nxt = tp->snd_una;
+
+ switch (tp->t_state) {
+
+ /*
+ * In FIN_WAIT_1 STATE in addition to the processing
+ * for the ESTABLISHED state if our FIN is now acknowledged
+ * then enter FIN_WAIT_2.
+ */
+ case TCPS_FIN_WAIT_1:
+ if (ourfinisacked) {
+ /*
+ * If we can't receive any more
+ * data, then closing user can proceed.
+ * Starting the timer is contrary to the
+ * specification, but if we don't get a FIN
+ * we'll hang forever.
+ */
+ if (so->so_state & SS_CANTRCVMORE) {
+ soisdisconnected(so);
+ tp->t_timer[TCPT_2MSL] = tcp_maxidle;
+ }
+ tp->t_state = TCPS_FIN_WAIT_2;
+ }
+ break;
+
+ /*
+ * In CLOSING STATE in addition to the processing for
+ * the ESTABLISHED state if the ACK acknowledges our FIN
+ * then enter the TIME-WAIT state, otherwise ignore
+ * the segment.
+ */
+ case TCPS_CLOSING:
+ if (ourfinisacked) {
+ tp->t_state = TCPS_TIME_WAIT;
+ tcp_canceltimers(tp);
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ soisdisconnected(so);
+ }
+ break;
+
+ /*
+ * In LAST_ACK, we may still be waiting for data to drain
+ * and/or to be acked, as well as for the ack of our FIN.
+ * If our FIN is now acknowledged, delete the TCB,
+ * enter the closed state and return.
+ */
+ case TCPS_LAST_ACK:
+ if (ourfinisacked) {
+ tp = tcp_close(tp);
+ goto drop;
+ }
+ break;
+
+ /*
+ * In TIME_WAIT state the only thing that should arrive
+ * is a retransmission of the remote FIN. Acknowledge
+ * it and restart the finack timer.
+ */
+ case TCPS_TIME_WAIT:
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ goto dropafterack;
+ }
+ }
+
+step6:
+ /*
+ * Update window information.
+ * Don't look at window if no ACK: TAC's send garbage on first SYN.
+ */
+ if ((tiflags & TH_ACK) &&
+ (SEQ_LT(tp->snd_wl1, ti->ti_seq) || tp->snd_wl1 == ti->ti_seq &&
+ (SEQ_LT(tp->snd_wl2, ti->ti_ack) ||
+ tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd))) {
+ /* keep track of pure window updates */
+ if (ti->ti_len == 0 &&
+ tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd)
+ tcpstat.tcps_rcvwinupd++;
+ tp->snd_wnd = tiwin;
+ tp->snd_wl1 = ti->ti_seq;
+ tp->snd_wl2 = ti->ti_ack;
+ if (tp->snd_wnd > tp->max_sndwnd)
+ tp->max_sndwnd = tp->snd_wnd;
+ needoutput = 1;
+ }
+
+ /*
+ * Process segments with URG.
+ */
+ if ((tiflags & TH_URG) && ti->ti_urp &&
+ TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ /*
+ * This is a kludge, but if we receive and accept
+ * random urgent pointers, we'll crash in
+ * soreceive. It's hard to imagine someone
+ * actually wanting to send this much urgent data.
+ */
+ if (ti->ti_urp + so->so_rcv.sb_cc > sb_max) {
+ ti->ti_urp = 0; /* XXX */
+ tiflags &= ~TH_URG; /* XXX */
+ goto dodata; /* XXX */
+ }
+ /*
+ * If this segment advances the known urgent pointer,
+ * then mark the data stream. This should not happen
+ * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since
+ * a FIN has been received from the remote side.
+ * In these states we ignore the URG.
+ *
+ * According to RFC961 (Assigned Protocols),
+ * the urgent pointer points to the last octet
+ * of urgent data. We continue, however,
+ * to consider it to indicate the first octet
+ * of data past the urgent section as the original
+ * spec states (in one of two places).
+ */
+ if (SEQ_GT(ti->ti_seq+ti->ti_urp, tp->rcv_up)) {
+ tp->rcv_up = ti->ti_seq + ti->ti_urp;
+ so->so_oobmark = so->so_rcv.sb_cc +
+ (tp->rcv_up - tp->rcv_nxt) - 1;
+ if (so->so_oobmark == 0)
+ so->so_state |= SS_RCVATMARK;
+ sohasoutofband(so);
+ tp->t_oobflags &= ~(TCPOOB_HAVEDATA | TCPOOB_HADDATA);
+ }
+ /*
+ * Remove out of band data so doesn't get presented to user.
+ * This can happen independent of advancing the URG pointer,
+ * but if two URG's are pending at once, some out-of-band
+ * data may creep in... ick.
+ */
+ if (ti->ti_urp <= (u_long)ti->ti_len
+#ifdef SO_OOBINLINE
+ && (so->so_options & SO_OOBINLINE) == 0
+#endif
+ )
+ tcp_pulloutofband(so, ti, m);
+ } else
+ /*
+ * If no out of band data is expected,
+ * pull receive urgent pointer along
+ * with the receive window.
+ */
+ if (SEQ_GT(tp->rcv_nxt, tp->rcv_up))
+ tp->rcv_up = tp->rcv_nxt;
+dodata: /* XXX */
+
+ /*
+ * Process the segment text, merging it into the TCP sequencing queue,
+ * and arranging for acknowledgment of receipt if necessary.
+ * This process logically involves adjusting tp->rcv_wnd as data
+ * is presented to the user (this happens in tcp_usrreq.c,
+ * case PRU_RCVD). If a FIN has already been received on this
+ * connection then we just ignore the text.
+ */
+ if ((ti->ti_len || (tiflags&TH_FIN)) &&
+ TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ TCP_REASS(tp, ti, m, so, tiflags);
+ /*
+ * Note the amount of data that peer has sent into
+ * our window, in order to estimate the sender's
+ * buffer size.
+ */
+ len = so->so_rcv.sb_hiwat - (tp->rcv_adv - tp->rcv_nxt);
+ } else {
+ m_freem(m);
+ tiflags &= ~TH_FIN;
+ }
+
+ /*
+ * If FIN is received ACK the FIN and let the user know
+ * that the connection is closing.
+ */
+ if (tiflags & TH_FIN) {
+ if (TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ socantrcvmore(so);
+ tp->t_flags |= TF_ACKNOW;
+ tp->rcv_nxt++;
+ }
+ switch (tp->t_state) {
+
+ /*
+ * In SYN_RECEIVED and ESTABLISHED STATES
+ * enter the CLOSE_WAIT state.
+ */
+ case TCPS_SYN_RECEIVED:
+ case TCPS_ESTABLISHED:
+ tp->t_state = TCPS_CLOSE_WAIT;
+ break;
+
+ /*
+ * If still in FIN_WAIT_1 STATE FIN has not been acked so
+ * enter the CLOSING state.
+ */
+ case TCPS_FIN_WAIT_1:
+ tp->t_state = TCPS_CLOSING;
+ break;
+
+ /*
+ * In FIN_WAIT_2 state enter the TIME_WAIT state,
+ * starting the time-wait timer, turning off the other
+ * standard timers.
+ */
+ case TCPS_FIN_WAIT_2:
+ tp->t_state = TCPS_TIME_WAIT;
+ tcp_canceltimers(tp);
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ soisdisconnected(so);
+ break;
+
+ /*
+ * In TIME_WAIT state restart the 2 MSL time_wait timer.
+ */
+ case TCPS_TIME_WAIT:
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ break;
+ }
+ }
+ if (so->so_options & SO_DEBUG)
+ tcp_trace(TA_INPUT, ostate, tp, &tcp_saveti, 0);
+
+ /*
+ * If this is a small packet, then ACK now - with Nagel
+ * congestion avoidance sender won't send more until
+ * he gets an ACK.
+ */
+ if (ti->ti_len && ((unsigned)ti->ti_len < tp->t_maxseg))
+ tp->t_flags |= TF_ACKNOW;
+
+ /*
+ * Return any desired output.
+ */
+ if (needoutput || (tp->t_flags & TF_ACKNOW))
+ (void) tcp_output(tp);
+ return;
+
+dropafterack:
+ /*
+ * Generate an ACK dropping incoming segment if it occupies
+ * sequence space, where the ACK reflects our state.
+ */
+ if (tiflags & TH_RST)
+ goto drop;
+ m_freem(m);
+ tp->t_flags |= TF_ACKNOW;
+ (void) tcp_output(tp);
+ return;
+
+dropwithreset:
+ /*
+ * Generate a RST, dropping incoming segment.
+ * Make ACK acceptable to originator of segment.
+ * Don't bother to respond if destination was broadcast/multicast.
+ */
+ if ((tiflags & TH_RST) || m->m_flags & (M_BCAST|M_MCAST) ||
+ IN_MULTICAST(ntohl(ti->ti_dst.s_addr)))
+ goto drop;
+ if (tiflags & TH_ACK)
+ tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST);
+ else {
+ if (tiflags & TH_SYN)
+ ti->ti_len++;
+ tcp_respond(tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0,
+ TH_RST|TH_ACK);
+ }
+ /* destroy temporarily created socket */
+ if (dropsocket)
+ (void) soabort(so);
+ return;
+
+drop:
+ /*
+ * Drop space held by incoming segment and return.
+ */
+ if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))
+ tcp_trace(TA_DROP, ostate, tp, &tcp_saveti, 0);
+ m_freem(m);
+ /* destroy temporarily created socket */
+ if (dropsocket)
+ (void) soabort(so);
+ return;
+#ifndef TUBA_INCLUDE
+}
+
+void
+tcp_dooptions(tp, cp, cnt, ti, ts_present, ts_val, ts_ecr)
+ struct tcpcb *tp;
+ u_char *cp;
+ int cnt;
+ struct tcpiphdr *ti;
+ int *ts_present;
+ u_long *ts_val, *ts_ecr;
+{
+ u_short mss;
+ int opt, optlen;
+
+ for (; cnt > 0; cnt -= optlen, cp += optlen) {
+ opt = cp[0];
+ if (opt == TCPOPT_EOL)
+ break;
+ if (opt == TCPOPT_NOP)
+ optlen = 1;
+ else {
+ optlen = cp[1];
+ if (optlen <= 0)
+ break;
+ }
+ switch (opt) {
+
+ default:
+ continue;
+
+ case TCPOPT_MAXSEG:
+ if (optlen != TCPOLEN_MAXSEG)
+ continue;
+ if (!(ti->ti_flags & TH_SYN))
+ continue;
+ bcopy((char *) cp + 2, (char *) &mss, sizeof(mss));
+ NTOHS(mss);
+ (void) tcp_mss(tp, mss); /* sets t_maxseg */
+ break;
+
+ case TCPOPT_WINDOW:
+ if (optlen != TCPOLEN_WINDOW)
+ continue;
+ if (!(ti->ti_flags & TH_SYN))
+ continue;
+ tp->t_flags |= TF_RCVD_SCALE;
+ tp->requested_s_scale = min(cp[2], TCP_MAX_WINSHIFT);
+ break;
+
+ case TCPOPT_TIMESTAMP:
+ if (optlen != TCPOLEN_TIMESTAMP)
+ continue;
+ *ts_present = 1;
+ bcopy((char *)cp + 2, (char *) ts_val, sizeof(*ts_val));
+ NTOHL(*ts_val);
+ bcopy((char *)cp + 6, (char *) ts_ecr, sizeof(*ts_ecr));
+ NTOHL(*ts_ecr);
+
+ /*
+ * A timestamp received in a SYN makes
+ * it ok to send timestamp requests and replies.
+ */
+ if (ti->ti_flags & TH_SYN) {
+ tp->t_flags |= TF_RCVD_TSTMP;
+ tp->ts_recent = *ts_val;
+ tp->ts_recent_age = tcp_now;
+ }
+ break;
+ }
+ }
+}
+
+/*
+ * Pull out of band byte out of a segment so
+ * it doesn't appear in the user's data queue.
+ * It is still reflected in the segment length for
+ * sequencing purposes.
+ */
+void
+tcp_pulloutofband(so, ti, m)
+ struct socket *so;
+ struct tcpiphdr *ti;
+ register struct mbuf *m;
+{
+ int cnt = ti->ti_urp - 1;
+
+ while (cnt >= 0) {
+ if (m->m_len > cnt) {
+ char *cp = mtod(m, caddr_t) + cnt;
+ struct tcpcb *tp = sototcpcb(so);
+
+ tp->t_iobc = *cp;
+ tp->t_oobflags |= TCPOOB_HAVEDATA;
+ bcopy(cp+1, cp, (unsigned)(m->m_len - cnt - 1));
+ m->m_len--;
+ return;
+ }
+ cnt -= m->m_len;
+ m = m->m_next;
+ if (m == 0)
+ break;
+ }
+ panic("tcp_pulloutofband");
+}
+
+/*
+ * Collect new round-trip time estimate
+ * and update averages and current timeout.
+ */
+void
+tcp_xmit_timer(tp, rtt)
+ register struct tcpcb *tp;
+ short rtt;
+{
+ register short delta;
+
+ tcpstat.tcps_rttupdated++;
+ if (tp->t_srtt != 0) {
+ /*
+ * srtt is stored as fixed point with 3 bits after the
+ * binary point (i.e., scaled by 8). The following magic
+ * is equivalent to the smoothing algorithm in rfc793 with
+ * an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed
+ * point). Adjust rtt to origin 0.
+ */
+ delta = rtt - 1 - (tp->t_srtt >> TCP_RTT_SHIFT);
+ if ((tp->t_srtt += delta) <= 0)
+ tp->t_srtt = 1;
+ /*
+ * We accumulate a smoothed rtt variance (actually, a
+ * smoothed mean difference), then set the retransmit
+ * timer to smoothed rtt + 4 times the smoothed variance.
+ * rttvar is stored as fixed point with 2 bits after the
+ * binary point (scaled by 4). The following is
+ * equivalent to rfc793 smoothing with an alpha of .75
+ * (rttvar = rttvar*3/4 + |delta| / 4). This replaces
+ * rfc793's wired-in beta.
+ */
+ if (delta < 0)
+ delta = -delta;
+ delta -= (tp->t_rttvar >> TCP_RTTVAR_SHIFT);
+ if ((tp->t_rttvar += delta) <= 0)
+ tp->t_rttvar = 1;
+ } else {
+ /*
+ * No rtt measurement yet - use the unsmoothed rtt.
+ * Set the variance to half the rtt (so our first
+ * retransmit happens at 3*rtt).
+ */
+ tp->t_srtt = rtt << TCP_RTT_SHIFT;
+ tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1);
+ }
+ tp->t_rtt = 0;
+ tp->t_rxtshift = 0;
+
+ /*
+ * the retransmit should happen at rtt + 4 * rttvar.
+ * Because of the way we do the smoothing, srtt and rttvar
+ * will each average +1/2 tick of bias. When we compute
+ * the retransmit timer, we want 1/2 tick of rounding and
+ * 1 extra tick because of +-1/2 tick uncertainty in the
+ * firing of the timer. The bias will give us exactly the
+ * 1.5 tick we need. But, because the bias is
+ * statistical, we have to test that we don't drop below
+ * the minimum feasible timer (which is 2 ticks).
+ */
+ TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp),
+ tp->t_rttmin, TCPTV_REXMTMAX);
+
+ /*
+ * We received an ack for a packet that wasn't retransmitted;
+ * it is probably safe to discard any error indications we've
+ * received recently. This isn't quite right, but close enough
+ * for now (a route might have failed after we sent a segment,
+ * and the return path might not be symmetrical).
+ */
+ tp->t_softerror = 0;
+}
+
+/*
+ * Determine a reasonable value for maxseg size.
+ * If the route is known, check route for mtu.
+ * If none, use an mss that can be handled on the outgoing
+ * interface without forcing IP to fragment; if bigger than
+ * an mbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES
+ * to utilize large mbufs. If no route is found, route has no mtu,
+ * or the destination isn't local, use a default, hopefully conservative
+ * size (usually 512 or the default IP max size, but no more than the mtu
+ * of the interface), as we can't discover anything about intervening
+ * gateways or networks. We also initialize the congestion/slow start
+ * window to be a single segment if the destination isn't local.
+ * While looking at the routing entry, we also initialize other path-dependent
+ * parameters from pre-set or cached values in the routing entry.
+ */
+int
+tcp_mss(tp, offer)
+ register struct tcpcb *tp;
+ u_int offer;
+{
+ struct route *ro;
+ register struct rtentry *rt;
+ struct ifnet *ifp;
+ register int rtt, mss;
+ u_long bufsize;
+ struct inpcb *inp;
+ struct socket *so;
+ extern int tcp_mssdflt;
+
+ inp = tp->t_inpcb;
+ ro = &inp->inp_route;
+
+ if ((rt = ro->ro_rt) == (struct rtentry *)0) {
+ /* No route yet, so try to acquire one */
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ ro->ro_dst.sa_family = AF_INET;
+ ro->ro_dst.sa_len = sizeof(ro->ro_dst);
+ ((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
+ inp->inp_faddr;
+ rtalloc(ro);
+ }
+ if ((rt = ro->ro_rt) == (struct rtentry *)0)
+ return (tcp_mssdflt);
+ }
+ ifp = rt->rt_ifp;
+ so = inp->inp_socket;
+
+#ifdef RTV_MTU /* if route characteristics exist ... */
+ /*
+ * While we're here, check if there's an initial rtt
+ * or rttvar. Convert from the route-table units
+ * to scaled multiples of the slow timeout timer.
+ */
+ if (tp->t_srtt == 0 && (rtt = rt->rt_rmx.rmx_rtt)) {
+ /*
+ * XXX the lock bit for MTU indicates that the value
+ * is also a minimum value; this is subject to time.
+ */
+ if (rt->rt_rmx.rmx_locks & RTV_RTT)
+ tp->t_rttmin = rtt / (RTM_RTTUNIT / PR_SLOWHZ);
+ tp->t_srtt = rtt / (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTT_SCALE));
+ if (rt->rt_rmx.rmx_rttvar)
+ tp->t_rttvar = rt->rt_rmx.rmx_rttvar /
+ (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTTVAR_SCALE));
+ else
+ /* default variation is +- 1 rtt */
+ tp->t_rttvar =
+ tp->t_srtt * TCP_RTTVAR_SCALE / TCP_RTT_SCALE;
+ TCPT_RANGESET(tp->t_rxtcur,
+ ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1,
+ tp->t_rttmin, TCPTV_REXMTMAX);
+ }
+ /*
+ * if there's an mtu associated with the route, use it
+ */
+ if (rt->rt_rmx.rmx_mtu)
+ mss = rt->rt_rmx.rmx_mtu - sizeof(struct tcpiphdr);
+ else
+#endif /* RTV_MTU */
+ {
+ mss = ifp->if_mtu - sizeof(struct tcpiphdr);
+#if (MCLBYTES & (MCLBYTES - 1)) == 0
+ if (mss > MCLBYTES)
+ mss &= ~(MCLBYTES-1);
+#else
+ if (mss > MCLBYTES)
+ mss = mss / MCLBYTES * MCLBYTES;
+#endif
+ if (!in_localaddr(inp->inp_faddr))
+ mss = min(mss, tcp_mssdflt);
+ }
+ /*
+ * The current mss, t_maxseg, is initialized to the default value.
+ * If we compute a smaller value, reduce the current mss.
+ * If we compute a larger value, return it for use in sending
+ * a max seg size option, but don't store it for use
+ * unless we received an offer at least that large from peer.
+ * However, do not accept offers under 32 bytes.
+ */
+ if (offer)
+ mss = min(mss, offer);
+ mss = max(mss, 32); /* sanity */
+ if (mss < tp->t_maxseg || offer != 0) {
+ /*
+ * If there's a pipesize, change the socket buffer
+ * to that size. Make the socket buffers an integral
+ * number of mss units; if the mss is larger than
+ * the socket buffer, decrease the mss.
+ */
+#ifdef RTV_SPIPE
+ if ((bufsize = rt->rt_rmx.rmx_sendpipe) == 0)
+#endif
+ bufsize = so->so_snd.sb_hiwat;
+ if (bufsize < mss)
+ mss = bufsize;
+ else {
+ bufsize = roundup(bufsize, mss);
+ if (bufsize > sb_max)
+ bufsize = sb_max;
+ (void)sbreserve(&so->so_snd, bufsize);
+ }
+ tp->t_maxseg = mss;
+
+#ifdef RTV_RPIPE
+ if ((bufsize = rt->rt_rmx.rmx_recvpipe) == 0)
+#endif
+ bufsize = so->so_rcv.sb_hiwat;
+ if (bufsize > mss) {
+ bufsize = roundup(bufsize, mss);
+ if (bufsize > sb_max)
+ bufsize = sb_max;
+ (void)sbreserve(&so->so_rcv, bufsize);
+ }
+ }
+ tp->snd_cwnd = mss;
+
+#ifdef RTV_SSTHRESH
+ if (rt->rt_rmx.rmx_ssthresh) {
+ /*
+ * There's some sort of gateway or interface
+ * buffer limit on the path. Use this to set
+ * the slow start threshhold, but set the
+ * threshold to no less than 2*mss.
+ */
+ tp->snd_ssthresh = max(2 * mss, rt->rt_rmx.rmx_ssthresh);
+ }
+#endif /* RTV_MTU */
+ return (mss);
+}
+#endif /* TUBA_INCLUDE */
diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c
new file mode 100644
index 0000000..667579f
--- /dev/null
+++ b/sys/netinet/tcp_output.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_output.c 8.3 (Berkeley) 12/30/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#define TCPOUTFLAGS
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_debug.h>
+
+#ifdef notyet
+extern struct mbuf *m_copypack();
+#endif
+
+
+#define MAX_TCPOPTLEN 32 /* max # bytes that go in options */
+
+/*
+ * Tcp output routine: figure out what should be sent and send it.
+ */
+int
+tcp_output(tp)
+ register struct tcpcb *tp;
+{
+ register struct socket *so = tp->t_inpcb->inp_socket;
+ register long len, win;
+ int off, flags, error;
+ register struct mbuf *m;
+ register struct tcpiphdr *ti;
+ u_char opt[MAX_TCPOPTLEN];
+ unsigned optlen, hdrlen;
+ int idle, sendalot;
+
+ /*
+ * Determine length of data that should be transmitted,
+ * and flags that will be used.
+ * If there is some data or critical controls (SYN, RST)
+ * to send, then transmit; otherwise, investigate further.
+ */
+ idle = (tp->snd_max == tp->snd_una);
+ if (idle && tp->t_idle >= tp->t_rxtcur)
+ /*
+ * We have been idle for "a while" and no acks are
+ * expected to clock out any data we send --
+ * slow start to get ack "clock" running again.
+ */
+ tp->snd_cwnd = tp->t_maxseg;
+again:
+ sendalot = 0;
+ off = tp->snd_nxt - tp->snd_una;
+ win = min(tp->snd_wnd, tp->snd_cwnd);
+
+ flags = tcp_outflags[tp->t_state];
+ /*
+ * If in persist timeout with window of 0, send 1 byte.
+ * Otherwise, if window is small but nonzero
+ * and timer expired, we will send what we can
+ * and go to transmit state.
+ */
+ if (tp->t_force) {
+ if (win == 0) {
+ /*
+ * If we still have some data to send, then
+ * clear the FIN bit. Usually this would
+ * happen below when it realizes that we
+ * aren't sending all the data. However,
+ * if we have exactly 1 byte of unset data,
+ * then it won't clear the FIN bit below,
+ * and if we are in persist state, we wind
+ * up sending the packet without recording
+ * that we sent the FIN bit.
+ *
+ * We can't just blindly clear the FIN bit,
+ * because if we don't have any more data
+ * to send then the probe will be the FIN
+ * itself.
+ */
+ if (off < so->so_snd.sb_cc)
+ flags &= ~TH_FIN;
+ win = 1;
+ } else {
+ tp->t_timer[TCPT_PERSIST] = 0;
+ tp->t_rxtshift = 0;
+ }
+ }
+
+ len = min(so->so_snd.sb_cc, win) - off;
+
+ if (len < 0) {
+ /*
+ * If FIN has been sent but not acked,
+ * but we haven't been called to retransmit,
+ * len will be -1. Otherwise, window shrank
+ * after we sent into it. If window shrank to 0,
+ * cancel pending retransmit and pull snd_nxt
+ * back to (closed) window. We will enter persist
+ * state below. If the window didn't close completely,
+ * just wait for an ACK.
+ */
+ len = 0;
+ if (win == 0) {
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->snd_nxt = tp->snd_una;
+ }
+ }
+ if (len > tp->t_maxseg) {
+ len = tp->t_maxseg;
+ sendalot = 1;
+ }
+ if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc))
+ flags &= ~TH_FIN;
+
+ win = sbspace(&so->so_rcv);
+
+ /*
+ * Sender silly window avoidance. If connection is idle
+ * and can send all data, a maximum segment,
+ * at least a maximum default-size segment do it,
+ * or are forced, do it; otherwise don't bother.
+ * If peer's buffer is tiny, then send
+ * when window is at least half open.
+ * If retransmitting (possibly after persist timer forced us
+ * to send into a small window), then must resend.
+ */
+ if (len) {
+ if (len == tp->t_maxseg)
+ goto send;
+ if ((idle || tp->t_flags & TF_NODELAY) &&
+ len + off >= so->so_snd.sb_cc)
+ goto send;
+ if (tp->t_force)
+ goto send;
+ if (len >= tp->max_sndwnd / 2)
+ goto send;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_max))
+ goto send;
+ }
+
+ /*
+ * Compare available window to amount of window
+ * known to peer (as advertised window less
+ * next expected input). If the difference is at least two
+ * max size segments, or at least 50% of the maximum possible
+ * window, then want to send a window update to peer.
+ */
+ if (win > 0) {
+ /*
+ * "adv" is the amount we can increase the window,
+ * taking into account that we are limited by
+ * TCP_MAXWIN << tp->rcv_scale.
+ */
+ long adv = min(win, (long)TCP_MAXWIN << tp->rcv_scale) -
+ (tp->rcv_adv - tp->rcv_nxt);
+
+ if (adv >= (long) (2 * tp->t_maxseg))
+ goto send;
+ if (2 * adv >= (long) so->so_rcv.sb_hiwat)
+ goto send;
+ }
+
+ /*
+ * Send if we owe peer an ACK.
+ */
+ if (tp->t_flags & TF_ACKNOW)
+ goto send;
+ if (flags & (TH_SYN|TH_RST))
+ goto send;
+ if (SEQ_GT(tp->snd_up, tp->snd_una))
+ goto send;
+ /*
+ * If our state indicates that FIN should be sent
+ * and we have not yet done so, or we're retransmitting the FIN,
+ * then we need to send.
+ */
+ if (flags & TH_FIN &&
+ ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una))
+ goto send;
+
+ /*
+ * TCP window updates are not reliable, rather a polling protocol
+ * using ``persist'' packets is used to insure receipt of window
+ * updates. The three ``states'' for the output side are:
+ * idle not doing retransmits or persists
+ * persisting to move a small or zero window
+ * (re)transmitting and thereby not persisting
+ *
+ * tp->t_timer[TCPT_PERSIST]
+ * is set when we are in persist state.
+ * tp->t_force
+ * is set when we are called to send a persist packet.
+ * tp->t_timer[TCPT_REXMT]
+ * is set when we are retransmitting
+ * The output side is idle when both timers are zero.
+ *
+ * If send window is too small, there is data to transmit, and no
+ * retransmit or persist is pending, then go to persist state.
+ * If nothing happens soon, send when timer expires:
+ * if window is nonzero, transmit what we can,
+ * otherwise force out a byte.
+ */
+ if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 &&
+ tp->t_timer[TCPT_PERSIST] == 0) {
+ tp->t_rxtshift = 0;
+ tcp_setpersist(tp);
+ }
+
+ /*
+ * No reason to send a segment, just return.
+ */
+ return (0);
+
+send:
+ /*
+ * Before ESTABLISHED, force sending of initial options
+ * unless TCP set not to do any options.
+ * NOTE: we assume that the IP/TCP header plus TCP options
+ * always fit in a single mbuf, leaving room for a maximum
+ * link header, i.e.
+ * max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MHLEN
+ */
+ optlen = 0;
+ hdrlen = sizeof (struct tcpiphdr);
+ if (flags & TH_SYN) {
+ tp->snd_nxt = tp->iss;
+ if ((tp->t_flags & TF_NOOPT) == 0) {
+ u_short mss;
+
+ opt[0] = TCPOPT_MAXSEG;
+ opt[1] = 4;
+ mss = htons((u_short) tcp_mss(tp, 0));
+ bcopy((caddr_t)&mss, (caddr_t)(opt + 2), sizeof(mss));
+ optlen = 4;
+
+ if ((tp->t_flags & TF_REQ_SCALE) &&
+ ((flags & TH_ACK) == 0 ||
+ (tp->t_flags & TF_RCVD_SCALE))) {
+ *((u_long *) (opt + optlen)) = htonl(
+ TCPOPT_NOP << 24 |
+ TCPOPT_WINDOW << 16 |
+ TCPOLEN_WINDOW << 8 |
+ tp->request_r_scale);
+ optlen += 4;
+ }
+ }
+ }
+
+ /*
+ * Send a timestamp and echo-reply if this is a SYN and our side
+ * wants to use timestamps (TF_REQ_TSTMP is set) or both our side
+ * and our peer have sent timestamps in our SYN's.
+ */
+ if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP &&
+ (flags & TH_RST) == 0 &&
+ ((flags & (TH_SYN|TH_ACK)) == TH_SYN ||
+ (tp->t_flags & TF_RCVD_TSTMP))) {
+ u_long *lp = (u_long *)(opt + optlen);
+
+ /* Form timestamp option as shown in appendix A of RFC 1323. */
+ *lp++ = htonl(TCPOPT_TSTAMP_HDR);
+ *lp++ = htonl(tcp_now);
+ *lp = htonl(tp->ts_recent);
+ optlen += TCPOLEN_TSTAMP_APPA;
+ }
+
+ hdrlen += optlen;
+
+ /*
+ * Adjust data length if insertion of options will
+ * bump the packet length beyond the t_maxseg length.
+ */
+ if (len > tp->t_maxseg - optlen) {
+ len = tp->t_maxseg - optlen;
+ sendalot = 1;
+ }
+
+
+#ifdef DIAGNOSTIC
+ if (max_linkhdr + hdrlen > MHLEN)
+ panic("tcphdr too big");
+#endif
+
+ /*
+ * Grab a header mbuf, attaching a copy of data to
+ * be transmitted, and initialize the header from
+ * the template for sends on this connection.
+ */
+ if (len) {
+ if (tp->t_force && len == 1)
+ tcpstat.tcps_sndprobe++;
+ else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) {
+ tcpstat.tcps_sndrexmitpack++;
+ tcpstat.tcps_sndrexmitbyte += len;
+ } else {
+ tcpstat.tcps_sndpack++;
+ tcpstat.tcps_sndbyte += len;
+ }
+#ifdef notyet
+ if ((m = m_copypack(so->so_snd.sb_mb, off,
+ (int)len, max_linkhdr + hdrlen)) == 0) {
+ error = ENOBUFS;
+ goto out;
+ }
+ /*
+ * m_copypack left space for our hdr; use it.
+ */
+ m->m_len += hdrlen;
+ m->m_data -= hdrlen;
+#else
+ MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (m == NULL) {
+ error = ENOBUFS;
+ goto out;
+ }
+ m->m_data += max_linkhdr;
+ m->m_len = hdrlen;
+ if (len <= MHLEN - hdrlen - max_linkhdr) {
+ m_copydata(so->so_snd.sb_mb, off, (int) len,
+ mtod(m, caddr_t) + hdrlen);
+ m->m_len += len;
+ } else {
+ m->m_next = m_copy(so->so_snd.sb_mb, off, (int) len);
+ if (m->m_next == 0)
+ len = 0;
+ }
+#endif
+ /*
+ * If we're sending everything we've got, set PUSH.
+ * (This will keep happy those implementations which only
+ * give data to the user when a buffer fills or
+ * a PUSH comes in.)
+ */
+ if (off + len == so->so_snd.sb_cc)
+ flags |= TH_PUSH;
+ } else {
+ if (tp->t_flags & TF_ACKNOW)
+ tcpstat.tcps_sndacks++;
+ else if (flags & (TH_SYN|TH_FIN|TH_RST))
+ tcpstat.tcps_sndctrl++;
+ else if (SEQ_GT(tp->snd_up, tp->snd_una))
+ tcpstat.tcps_sndurg++;
+ else
+ tcpstat.tcps_sndwinup++;
+
+ MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (m == NULL) {
+ error = ENOBUFS;
+ goto out;
+ }
+ m->m_data += max_linkhdr;
+ m->m_len = hdrlen;
+ }
+ m->m_pkthdr.rcvif = (struct ifnet *)0;
+ ti = mtod(m, struct tcpiphdr *);
+ if (tp->t_template == 0)
+ panic("tcp_output");
+ bcopy((caddr_t)tp->t_template, (caddr_t)ti, sizeof (struct tcpiphdr));
+
+ /*
+ * Fill in fields, remembering maximum advertised
+ * window for use in delaying messages about window sizes.
+ * If resending a FIN, be sure not to use a new sequence number.
+ */
+ if (flags & TH_FIN && tp->t_flags & TF_SENTFIN &&
+ tp->snd_nxt == tp->snd_max)
+ tp->snd_nxt--;
+ /*
+ * If we are doing retransmissions, then snd_nxt will
+ * not reflect the first unsent octet. For ACK only
+ * packets, we do not want the sequence number of the
+ * retransmitted packet, we want the sequence number
+ * of the next unsent octet. So, if there is no data
+ * (and no SYN or FIN), use snd_max instead of snd_nxt
+ * when filling in ti_seq. But if we are in persist
+ * state, snd_max might reflect one byte beyond the
+ * right edge of the window, so use snd_nxt in that
+ * case, since we know we aren't doing a retransmission.
+ * (retransmit and persist are mutually exclusive...)
+ */
+ if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST])
+ ti->ti_seq = htonl(tp->snd_nxt);
+ else
+ ti->ti_seq = htonl(tp->snd_max);
+ ti->ti_ack = htonl(tp->rcv_nxt);
+ if (optlen) {
+ bcopy((caddr_t)opt, (caddr_t)(ti + 1), optlen);
+ ti->ti_off = (sizeof (struct tcphdr) + optlen) >> 2;
+ }
+ ti->ti_flags = flags;
+ /*
+ * Calculate receive window. Don't shrink window,
+ * but avoid silly window syndrome.
+ */
+ if (win < (long)(so->so_rcv.sb_hiwat / 4) && win < (long)tp->t_maxseg)
+ win = 0;
+ if (win > (long)TCP_MAXWIN << tp->rcv_scale)
+ win = (long)TCP_MAXWIN << tp->rcv_scale;
+ if (win < (long)(tp->rcv_adv - tp->rcv_nxt))
+ win = (long)(tp->rcv_adv - tp->rcv_nxt);
+ ti->ti_win = htons((u_short) (win>>tp->rcv_scale));
+ if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {
+ ti->ti_urp = htons((u_short)(tp->snd_up - tp->snd_nxt));
+ ti->ti_flags |= TH_URG;
+ } else
+ /*
+ * If no urgent pointer to send, then we pull
+ * the urgent pointer to the left edge of the send window
+ * so that it doesn't drift into the send window on sequence
+ * number wraparound.
+ */
+ tp->snd_up = tp->snd_una; /* drag it along */
+
+ /*
+ * Put TCP length in extended header, and then
+ * checksum extended header and data.
+ */
+ if (len + optlen)
+ ti->ti_len = htons((u_short)(sizeof (struct tcphdr) +
+ optlen + len));
+ ti->ti_sum = in_cksum(m, (int)(hdrlen + len));
+
+ /*
+ * In transmit state, time the transmission and arrange for
+ * the retransmit. In persist state, just set snd_max.
+ */
+ if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) {
+ tcp_seq startseq = tp->snd_nxt;
+
+ /*
+ * Advance snd_nxt over sequence space of this segment.
+ */
+ if (flags & (TH_SYN|TH_FIN)) {
+ if (flags & TH_SYN)
+ tp->snd_nxt++;
+ if (flags & TH_FIN) {
+ tp->snd_nxt++;
+ tp->t_flags |= TF_SENTFIN;
+ }
+ }
+ tp->snd_nxt += len;
+ if (SEQ_GT(tp->snd_nxt, tp->snd_max)) {
+ tp->snd_max = tp->snd_nxt;
+ /*
+ * Time this transmission if not a retransmission and
+ * not currently timing anything.
+ */
+ if (tp->t_rtt == 0) {
+ tp->t_rtt = 1;
+ tp->t_rtseq = startseq;
+ tcpstat.tcps_segstimed++;
+ }
+ }
+
+ /*
+ * Set retransmit timer if not currently set,
+ * and not doing an ack or a keep-alive probe.
+ * Initial value for retransmit timer is smoothed
+ * round-trip time + 2 * round-trip time variance.
+ * Initialize shift counter which is used for backoff
+ * of retransmit time.
+ */
+ if (tp->t_timer[TCPT_REXMT] == 0 &&
+ tp->snd_nxt != tp->snd_una) {
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ if (tp->t_timer[TCPT_PERSIST]) {
+ tp->t_timer[TCPT_PERSIST] = 0;
+ tp->t_rxtshift = 0;
+ }
+ }
+ } else
+ if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))
+ tp->snd_max = tp->snd_nxt + len;
+
+ /*
+ * Trace.
+ */
+ if (so->so_options & SO_DEBUG)
+ tcp_trace(TA_OUTPUT, tp->t_state, tp, ti, 0);
+
+ /*
+ * Fill in IP length and desired time to live and
+ * send to IP level. There should be a better way
+ * to handle ttl and tos; we could keep them in
+ * the template, but need a way to checksum without them.
+ */
+ m->m_pkthdr.len = hdrlen + len;
+#ifdef TUBA
+ if (tp->t_tuba_pcb)
+ error = tuba_output(m, tp);
+ else
+#endif
+ {
+ ((struct ip *)ti)->ip_len = m->m_pkthdr.len;
+ ((struct ip *)ti)->ip_ttl = tp->t_inpcb->inp_ip.ip_ttl; /* XXX */
+ ((struct ip *)ti)->ip_tos = tp->t_inpcb->inp_ip.ip_tos; /* XXX */
+#if BSD >= 43
+ error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
+ so->so_options & SO_DONTROUTE, 0);
+#else
+ error = ip_output(m, (struct mbuf *)0, &tp->t_inpcb->inp_route,
+ so->so_options & SO_DONTROUTE);
+#endif
+ }
+ if (error) {
+out:
+ if (error == ENOBUFS) {
+ tcp_quench(tp->t_inpcb, 0);
+ return (0);
+ }
+ if ((error == EHOSTUNREACH || error == ENETDOWN)
+ && TCPS_HAVERCVDSYN(tp->t_state)) {
+ tp->t_softerror = error;
+ return (0);
+ }
+ return (error);
+ }
+ tcpstat.tcps_sndtotal++;
+
+ /*
+ * Data sent (as far as we can tell).
+ * If this advertises a larger window than any other segment,
+ * then remember the size of the advertised window.
+ * Any pending ACK has now been sent.
+ */
+ if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
+ tp->rcv_adv = tp->rcv_nxt + win;
+ tp->last_ack_sent = tp->rcv_nxt;
+ tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
+ if (sendalot)
+ goto again;
+ return (0);
+}
+
+void
+tcp_setpersist(tp)
+ register struct tcpcb *tp;
+{
+ register t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
+
+ if (tp->t_timer[TCPT_REXMT])
+ panic("tcp_output REXMT");
+ /*
+ * Start/restart persistance timer.
+ */
+ TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
+ t * tcp_backoff[tp->t_rxtshift],
+ TCPTV_PERSMIN, TCPTV_PERSMAX);
+ if (tp->t_rxtshift < TCP_MAXRXTSHIFT)
+ tp->t_rxtshift++;
+}
diff --git a/sys/netinet/tcp_reass.c b/sys/netinet/tcp_reass.c
new file mode 100644
index 0000000..121cb94
--- /dev/null
+++ b/sys/netinet/tcp_reass.c
@@ -0,0 +1,1665 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_input.c 8.5 (Berkeley) 4/10/94
+ */
+
+#ifndef TUBA_INCLUDE
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_debug.h>
+
+int tcprexmtthresh = 3;
+struct tcpiphdr tcp_saveti;
+struct inpcb *tcp_last_inpcb = &tcb;
+
+extern u_long sb_max;
+
+#endif /* TUBA_INCLUDE */
+#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * PR_SLOWHZ)
+
+/* for modulo comparisons of timestamps */
+#define TSTMP_LT(a,b) ((int)((a)-(b)) < 0)
+#define TSTMP_GEQ(a,b) ((int)((a)-(b)) >= 0)
+
+
+/*
+ * Insert segment ti into reassembly queue of tcp with
+ * control block tp. Return TH_FIN if reassembly now includes
+ * a segment with FIN. The macro form does the common case inline
+ * (segment is the next to be received on an established connection,
+ * and the queue is empty), avoiding linkage into and removal
+ * from the queue and repetition of various conversions.
+ * Set DELACK for segments received in order, but ack immediately
+ * when segments are out of order (so fast retransmit can work).
+ */
+#define TCP_REASS(tp, ti, m, so, flags) { \
+ if ((ti)->ti_seq == (tp)->rcv_nxt && \
+ (tp)->seg_next == (struct tcpiphdr *)(tp) && \
+ (tp)->t_state == TCPS_ESTABLISHED) { \
+ tp->t_flags |= TF_DELACK; \
+ (tp)->rcv_nxt += (ti)->ti_len; \
+ flags = (ti)->ti_flags & TH_FIN; \
+ tcpstat.tcps_rcvpack++;\
+ tcpstat.tcps_rcvbyte += (ti)->ti_len;\
+ sbappend(&(so)->so_rcv, (m)); \
+ sorwakeup(so); \
+ } else { \
+ (flags) = tcp_reass((tp), (ti), (m)); \
+ tp->t_flags |= TF_ACKNOW; \
+ } \
+}
+#ifndef TUBA_INCLUDE
+
+int
+tcp_reass(tp, ti, m)
+ register struct tcpcb *tp;
+ register struct tcpiphdr *ti;
+ struct mbuf *m;
+{
+ register struct tcpiphdr *q;
+ struct socket *so = tp->t_inpcb->inp_socket;
+ int flags;
+
+ /*
+ * Call with ti==0 after become established to
+ * force pre-ESTABLISHED data up to user socket.
+ */
+ if (ti == 0)
+ goto present;
+
+ /*
+ * Find a segment which begins after this one does.
+ */
+ for (q = tp->seg_next; q != (struct tcpiphdr *)tp;
+ q = (struct tcpiphdr *)q->ti_next)
+ if (SEQ_GT(q->ti_seq, ti->ti_seq))
+ break;
+
+ /*
+ * If there is a preceding segment, it may provide some of
+ * our data already. If so, drop the data from the incoming
+ * segment. If it provides all of our data, drop us.
+ */
+ if ((struct tcpiphdr *)q->ti_prev != (struct tcpiphdr *)tp) {
+ register int i;
+ q = (struct tcpiphdr *)q->ti_prev;
+ /* conversion to int (in i) handles seq wraparound */
+ i = q->ti_seq + q->ti_len - ti->ti_seq;
+ if (i > 0) {
+ if (i >= ti->ti_len) {
+ tcpstat.tcps_rcvduppack++;
+ tcpstat.tcps_rcvdupbyte += ti->ti_len;
+ m_freem(m);
+ return (0);
+ }
+ m_adj(m, i);
+ ti->ti_len -= i;
+ ti->ti_seq += i;
+ }
+ q = (struct tcpiphdr *)(q->ti_next);
+ }
+ tcpstat.tcps_rcvoopack++;
+ tcpstat.tcps_rcvoobyte += ti->ti_len;
+ REASS_MBUF(ti) = m; /* XXX */
+
+ /*
+ * While we overlap succeeding segments trim them or,
+ * if they are completely covered, dequeue them.
+ */
+ while (q != (struct tcpiphdr *)tp) {
+ register int i = (ti->ti_seq + ti->ti_len) - q->ti_seq;
+ if (i <= 0)
+ break;
+ if (i < q->ti_len) {
+ q->ti_seq += i;
+ q->ti_len -= i;
+ m_adj(REASS_MBUF(q), i);
+ break;
+ }
+ q = (struct tcpiphdr *)q->ti_next;
+ m = REASS_MBUF((struct tcpiphdr *)q->ti_prev);
+ remque(q->ti_prev);
+ m_freem(m);
+ }
+
+ /*
+ * Stick new segment in its place.
+ */
+ insque(ti, q->ti_prev);
+
+present:
+ /*
+ * Present data to user, advancing rcv_nxt through
+ * completed sequence space.
+ */
+ if (TCPS_HAVERCVDSYN(tp->t_state) == 0)
+ return (0);
+ ti = tp->seg_next;
+ if (ti == (struct tcpiphdr *)tp || ti->ti_seq != tp->rcv_nxt)
+ return (0);
+ if (tp->t_state == TCPS_SYN_RECEIVED && ti->ti_len)
+ return (0);
+ do {
+ tp->rcv_nxt += ti->ti_len;
+ flags = ti->ti_flags & TH_FIN;
+ remque(ti);
+ m = REASS_MBUF(ti);
+ ti = (struct tcpiphdr *)ti->ti_next;
+ if (so->so_state & SS_CANTRCVMORE)
+ m_freem(m);
+ else
+ sbappend(&so->so_rcv, m);
+ } while (ti != (struct tcpiphdr *)tp && ti->ti_seq == tp->rcv_nxt);
+ sorwakeup(so);
+ return (flags);
+}
+
+/*
+ * TCP input routine, follows pages 65-76 of the
+ * protocol specification dated September, 1981 very closely.
+ */
+void
+tcp_input(m, iphlen)
+ register struct mbuf *m;
+ int iphlen;
+{
+ register struct tcpiphdr *ti;
+ register struct inpcb *inp;
+ caddr_t optp = NULL;
+ int optlen = 0;
+ int len, tlen, off;
+ register struct tcpcb *tp = 0;
+ register int tiflags;
+ struct socket *so = 0;
+ int todrop, acked, ourfinisacked, needoutput = 0;
+ short ostate = 0;
+ struct in_addr laddr;
+ int dropsocket = 0;
+ int iss = 0;
+ u_long tiwin, ts_val, ts_ecr;
+ int ts_present = 0;
+
+ tcpstat.tcps_rcvtotal++;
+ /*
+ * Get IP and TCP header together in first mbuf.
+ * Note: IP leaves IP header in first mbuf.
+ */
+ ti = mtod(m, struct tcpiphdr *);
+ if (iphlen > sizeof (struct ip))
+ ip_stripoptions(m, (struct mbuf *)0);
+ if (m->m_len < sizeof (struct tcpiphdr)) {
+ if ((m = m_pullup(m, sizeof (struct tcpiphdr))) == 0) {
+ tcpstat.tcps_rcvshort++;
+ return;
+ }
+ ti = mtod(m, struct tcpiphdr *);
+ }
+
+ /*
+ * Checksum extended TCP header and data.
+ */
+ tlen = ((struct ip *)ti)->ip_len;
+ len = sizeof (struct ip) + tlen;
+ ti->ti_next = ti->ti_prev = 0;
+ ti->ti_x1 = 0;
+ ti->ti_len = (u_short)tlen;
+ HTONS(ti->ti_len);
+ if (ti->ti_sum = in_cksum(m, len)) {
+ tcpstat.tcps_rcvbadsum++;
+ goto drop;
+ }
+#endif /* TUBA_INCLUDE */
+
+ /*
+ * Check that TCP offset makes sense,
+ * pull out TCP options and adjust length. XXX
+ */
+ off = ti->ti_off << 2;
+ if (off < sizeof (struct tcphdr) || off > tlen) {
+ tcpstat.tcps_rcvbadoff++;
+ goto drop;
+ }
+ tlen -= off;
+ ti->ti_len = tlen;
+ if (off > sizeof (struct tcphdr)) {
+ if (m->m_len < sizeof(struct ip) + off) {
+ if ((m = m_pullup(m, sizeof (struct ip) + off)) == 0) {
+ tcpstat.tcps_rcvshort++;
+ return;
+ }
+ ti = mtod(m, struct tcpiphdr *);
+ }
+ optlen = off - sizeof (struct tcphdr);
+ optp = mtod(m, caddr_t) + sizeof (struct tcpiphdr);
+ /*
+ * Do quick retrieval of timestamp options ("options
+ * prediction?"). If timestamp is the only option and it's
+ * formatted as recommended in RFC 1323 appendix A, we
+ * quickly get the values now and not bother calling
+ * tcp_dooptions(), etc.
+ */
+ if ((optlen == TCPOLEN_TSTAMP_APPA ||
+ (optlen > TCPOLEN_TSTAMP_APPA &&
+ optp[TCPOLEN_TSTAMP_APPA] == TCPOPT_EOL)) &&
+ *(u_long *)optp == htonl(TCPOPT_TSTAMP_HDR) &&
+ (ti->ti_flags & TH_SYN) == 0) {
+ ts_present = 1;
+ ts_val = ntohl(*(u_long *)(optp + 4));
+ ts_ecr = ntohl(*(u_long *)(optp + 8));
+ optp = NULL; /* we've parsed the options */
+ }
+ }
+ tiflags = ti->ti_flags;
+
+ /*
+ * Convert TCP protocol specific fields to host format.
+ */
+ NTOHL(ti->ti_seq);
+ NTOHL(ti->ti_ack);
+ NTOHS(ti->ti_win);
+ NTOHS(ti->ti_urp);
+
+ /*
+ * Locate pcb for segment.
+ */
+findpcb:
+ inp = tcp_last_inpcb;
+ if (inp->inp_lport != ti->ti_dport ||
+ inp->inp_fport != ti->ti_sport ||
+ inp->inp_faddr.s_addr != ti->ti_src.s_addr ||
+ inp->inp_laddr.s_addr != ti->ti_dst.s_addr) {
+ inp = in_pcblookup(&tcb, ti->ti_src, ti->ti_sport,
+ ti->ti_dst, ti->ti_dport, INPLOOKUP_WILDCARD);
+ if (inp)
+ tcp_last_inpcb = inp;
+ ++tcpstat.tcps_pcbcachemiss;
+ }
+
+ /*
+ * If the state is CLOSED (i.e., TCB does not exist) then
+ * all data in the incoming segment is discarded.
+ * If the TCB exists but is in CLOSED state, it is embryonic,
+ * but should either do a listen or a connect soon.
+ */
+ if (inp == 0)
+ goto dropwithreset;
+ tp = intotcpcb(inp);
+ if (tp == 0)
+ goto dropwithreset;
+ if (tp->t_state == TCPS_CLOSED)
+ goto drop;
+
+ /* Unscale the window into a 32-bit value. */
+ if ((tiflags & TH_SYN) == 0)
+ tiwin = ti->ti_win << tp->snd_scale;
+ else
+ tiwin = ti->ti_win;
+
+ so = inp->inp_socket;
+ if (so->so_options & (SO_DEBUG|SO_ACCEPTCONN)) {
+ if (so->so_options & SO_DEBUG) {
+ ostate = tp->t_state;
+ tcp_saveti = *ti;
+ }
+ if (so->so_options & SO_ACCEPTCONN) {
+ so = sonewconn(so, 0);
+ if (so == 0)
+ goto drop;
+ /*
+ * This is ugly, but ....
+ *
+ * Mark socket as temporary until we're
+ * committed to keeping it. The code at
+ * ``drop'' and ``dropwithreset'' check the
+ * flag dropsocket to see if the temporary
+ * socket created here should be discarded.
+ * We mark the socket as discardable until
+ * we're committed to it below in TCPS_LISTEN.
+ */
+ dropsocket++;
+ inp = (struct inpcb *)so->so_pcb;
+ inp->inp_laddr = ti->ti_dst;
+ inp->inp_lport = ti->ti_dport;
+#if BSD>=43
+ inp->inp_options = ip_srcroute();
+#endif
+ tp = intotcpcb(inp);
+ tp->t_state = TCPS_LISTEN;
+
+ /* Compute proper scaling value from buffer space
+ */
+ while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
+ TCP_MAXWIN << tp->request_r_scale < so->so_rcv.sb_hiwat)
+ tp->request_r_scale++;
+ }
+ }
+
+ /*
+ * Segment received on connection.
+ * Reset idle time and keep-alive timer.
+ */
+ tp->t_idle = 0;
+ tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+
+ /*
+ * Process options if not in LISTEN state,
+ * else do it below (after getting remote address).
+ */
+ if (optp && tp->t_state != TCPS_LISTEN)
+ tcp_dooptions(tp, optp, optlen, ti,
+ &ts_present, &ts_val, &ts_ecr);
+
+ /*
+ * Header prediction: check for the two common cases
+ * of a uni-directional data xfer. If the packet has
+ * no control flags, is in-sequence, the window didn't
+ * change and we're not retransmitting, it's a
+ * candidate. If the length is zero and the ack moved
+ * forward, we're the sender side of the xfer. Just
+ * free the data acked & wake any higher level process
+ * that was blocked waiting for space. If the length
+ * is non-zero and the ack didn't move, we're the
+ * receiver side. If we're getting packets in-order
+ * (the reassembly queue is empty), add the data to
+ * the socket buffer and note that we need a delayed ack.
+ */
+ if (tp->t_state == TCPS_ESTABLISHED &&
+ (tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK &&
+ (!ts_present || TSTMP_GEQ(ts_val, tp->ts_recent)) &&
+ ti->ti_seq == tp->rcv_nxt &&
+ tiwin && tiwin == tp->snd_wnd &&
+ tp->snd_nxt == tp->snd_max) {
+
+ /*
+ * If last ACK falls within this segment's sequence numbers,
+ * record the timestamp.
+ */
+ if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) &&
+ SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len)) {
+ tp->ts_recent_age = tcp_now;
+ tp->ts_recent = ts_val;
+ }
+
+ if (ti->ti_len == 0) {
+ if (SEQ_GT(ti->ti_ack, tp->snd_una) &&
+ SEQ_LEQ(ti->ti_ack, tp->snd_max) &&
+ tp->snd_cwnd >= tp->snd_wnd) {
+ /*
+ * this is a pure ack for outstanding data.
+ */
+ ++tcpstat.tcps_predack;
+ if (ts_present)
+ tcp_xmit_timer(tp, tcp_now-ts_ecr+1);
+ else if (tp->t_rtt &&
+ SEQ_GT(ti->ti_ack, tp->t_rtseq))
+ tcp_xmit_timer(tp, tp->t_rtt);
+ acked = ti->ti_ack - tp->snd_una;
+ tcpstat.tcps_rcvackpack++;
+ tcpstat.tcps_rcvackbyte += acked;
+ sbdrop(&so->so_snd, acked);
+ tp->snd_una = ti->ti_ack;
+ m_freem(m);
+
+ /*
+ * If all outstanding data are acked, stop
+ * retransmit timer, otherwise restart timer
+ * using current (possibly backed-off) value.
+ * If process is waiting for space,
+ * wakeup/selwakeup/signal. If data
+ * are ready to send, let tcp_output
+ * decide between more output or persist.
+ */
+ if (tp->snd_una == tp->snd_max)
+ tp->t_timer[TCPT_REXMT] = 0;
+ else if (tp->t_timer[TCPT_PERSIST] == 0)
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+
+ if (so->so_snd.sb_flags & SB_NOTIFY)
+ sowwakeup(so);
+ if (so->so_snd.sb_cc)
+ (void) tcp_output(tp);
+ return;
+ }
+ } else if (ti->ti_ack == tp->snd_una &&
+ tp->seg_next == (struct tcpiphdr *)tp &&
+ ti->ti_len <= sbspace(&so->so_rcv)) {
+ /*
+ * this is a pure, in-sequence data packet
+ * with nothing on the reassembly queue and
+ * we have enough buffer space to take it.
+ */
+ ++tcpstat.tcps_preddat;
+ tp->rcv_nxt += ti->ti_len;
+ tcpstat.tcps_rcvpack++;
+ tcpstat.tcps_rcvbyte += ti->ti_len;
+ /*
+ * Drop TCP, IP headers and TCP options then add data
+ * to socket buffer.
+ */
+ m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ sbappend(&so->so_rcv, m);
+ sorwakeup(so);
+ /*
+ * If this is a small packet, then ACK now - with Nagel
+ * congestion avoidance sender won't send more until
+ * he gets an ACK.
+ */
+ if ((unsigned)ti->ti_len < tp->t_maxseg) {
+ tp->t_flags |= TF_ACKNOW;
+ tcp_output(tp);
+ } else {
+ tp->t_flags |= TF_DELACK;
+ }
+ return;
+ }
+ }
+
+ /*
+ * Drop TCP, IP headers and TCP options.
+ */
+ m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+
+ /*
+ * Calculate amount of space in receive window,
+ * and then do TCP input processing.
+ * Receive window is amount of space in rcv queue,
+ * but not less than advertised window.
+ */
+ { int win;
+
+ win = sbspace(&so->so_rcv);
+ if (win < 0)
+ win = 0;
+ tp->rcv_wnd = max(win, (int)(tp->rcv_adv - tp->rcv_nxt));
+ }
+
+ switch (tp->t_state) {
+
+ /*
+ * If the state is LISTEN then ignore segment if it contains an RST.
+ * If the segment contains an ACK then it is bad and send a RST.
+ * If it does not contain a SYN then it is not interesting; drop it.
+ * Don't bother responding if the destination was a broadcast.
+ * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial
+ * tp->iss, and send a segment:
+ * <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK>
+ * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss.
+ * Fill in remote peer address fields if not previously specified.
+ * Enter SYN_RECEIVED state, and process any other fields of this
+ * segment in this state.
+ */
+ case TCPS_LISTEN: {
+ struct mbuf *am;
+ register struct sockaddr_in *sin;
+
+ if (tiflags & TH_RST)
+ goto drop;
+ if (tiflags & TH_ACK)
+ goto dropwithreset;
+ if ((tiflags & TH_SYN) == 0)
+ goto drop;
+ /*
+ * RFC1122 4.2.3.10, p. 104: discard bcast/mcast SYN
+ * in_broadcast() should never return true on a received
+ * packet with M_BCAST not set.
+ */
+ if (m->m_flags & (M_BCAST|M_MCAST) ||
+ IN_MULTICAST(ntohl(ti->ti_dst.s_addr)))
+ goto drop;
+ am = m_get(M_DONTWAIT, MT_SONAME); /* XXX */
+ if (am == NULL)
+ goto drop;
+ am->m_len = sizeof (struct sockaddr_in);
+ sin = mtod(am, struct sockaddr_in *);
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_addr = ti->ti_src;
+ sin->sin_port = ti->ti_sport;
+ bzero((caddr_t)sin->sin_zero, sizeof(sin->sin_zero));
+ laddr = inp->inp_laddr;
+ if (inp->inp_laddr.s_addr == INADDR_ANY)
+ inp->inp_laddr = ti->ti_dst;
+ if (in_pcbconnect(inp, am)) {
+ inp->inp_laddr = laddr;
+ (void) m_free(am);
+ goto drop;
+ }
+ (void) m_free(am);
+ tp->t_template = tcp_template(tp);
+ if (tp->t_template == 0) {
+ tp = tcp_drop(tp, ENOBUFS);
+ dropsocket = 0; /* socket is already gone */
+ goto drop;
+ }
+ if (optp)
+ tcp_dooptions(tp, optp, optlen, ti,
+ &ts_present, &ts_val, &ts_ecr);
+ if (iss)
+ tp->iss = iss;
+ else
+ tp->iss = tcp_iss;
+ tcp_iss += TCP_ISSINCR/2;
+ tp->irs = ti->ti_seq;
+ tcp_sendseqinit(tp);
+ tcp_rcvseqinit(tp);
+ tp->t_flags |= TF_ACKNOW;
+ tp->t_state = TCPS_SYN_RECEIVED;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ dropsocket = 0; /* committed to socket */
+ tcpstat.tcps_accepts++;
+ goto trimthenstep6;
+ }
+
+ /*
+ * If the state is SYN_SENT:
+ * if seg contains an ACK, but not for our SYN, drop the input.
+ * if seg contains a RST, then drop the connection.
+ * if seg does not contain SYN, then drop it.
+ * Otherwise this is an acceptable SYN segment
+ * initialize tp->rcv_nxt and tp->irs
+ * if seg contains ack then advance tp->snd_una
+ * if SYN has been acked change to ESTABLISHED else SYN_RCVD state
+ * arrange for segment to be acked (eventually)
+ * continue processing rest of data/controls, beginning with URG
+ */
+ case TCPS_SYN_SENT:
+ if ((tiflags & TH_ACK) &&
+ (SEQ_LEQ(ti->ti_ack, tp->iss) ||
+ SEQ_GT(ti->ti_ack, tp->snd_max)))
+ goto dropwithreset;
+ if (tiflags & TH_RST) {
+ if (tiflags & TH_ACK)
+ tp = tcp_drop(tp, ECONNREFUSED);
+ goto drop;
+ }
+ if ((tiflags & TH_SYN) == 0)
+ goto drop;
+ if (tiflags & TH_ACK) {
+ tp->snd_una = ti->ti_ack;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_una))
+ tp->snd_nxt = tp->snd_una;
+ }
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->irs = ti->ti_seq;
+ tcp_rcvseqinit(tp);
+ tp->t_flags |= TF_ACKNOW;
+ if (tiflags & TH_ACK && SEQ_GT(tp->snd_una, tp->iss)) {
+ tcpstat.tcps_connects++;
+ soisconnected(so);
+ tp->t_state = TCPS_ESTABLISHED;
+ /* Do window scaling on this connection? */
+ if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) ==
+ (TF_RCVD_SCALE|TF_REQ_SCALE)) {
+ tp->snd_scale = tp->requested_s_scale;
+ tp->rcv_scale = tp->request_r_scale;
+ }
+ (void) tcp_reass(tp, (struct tcpiphdr *)0,
+ (struct mbuf *)0);
+ /*
+ * if we didn't have to retransmit the SYN,
+ * use its rtt as our initial srtt & rtt var.
+ */
+ if (tp->t_rtt)
+ tcp_xmit_timer(tp, tp->t_rtt);
+ } else
+ tp->t_state = TCPS_SYN_RECEIVED;
+
+trimthenstep6:
+ /*
+ * Advance ti->ti_seq to correspond to first data byte.
+ * If data, trim to stay within window,
+ * dropping FIN if necessary.
+ */
+ ti->ti_seq++;
+ if (ti->ti_len > tp->rcv_wnd) {
+ todrop = ti->ti_len - tp->rcv_wnd;
+ m_adj(m, -todrop);
+ ti->ti_len = tp->rcv_wnd;
+ tiflags &= ~TH_FIN;
+ tcpstat.tcps_rcvpackafterwin++;
+ tcpstat.tcps_rcvbyteafterwin += todrop;
+ }
+ tp->snd_wl1 = ti->ti_seq - 1;
+ tp->rcv_up = ti->ti_seq;
+ goto step6;
+ }
+
+ /*
+ * States other than LISTEN or SYN_SENT.
+ * First check timestamp, if present.
+ * Then check that at least some bytes of segment are within
+ * receive window. If segment begins before rcv_nxt,
+ * drop leading data (and SYN); if nothing left, just ack.
+ *
+ * RFC 1323 PAWS: If we have a timestamp reply on this segment
+ * and it's less than ts_recent, drop it.
+ */
+ if (ts_present && (tiflags & TH_RST) == 0 && tp->ts_recent &&
+ TSTMP_LT(ts_val, tp->ts_recent)) {
+
+ /* Check to see if ts_recent is over 24 days old. */
+ if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) {
+ /*
+ * Invalidate ts_recent. If this segment updates
+ * ts_recent, the age will be reset later and ts_recent
+ * will get a valid value. If it does not, setting
+ * ts_recent to zero will at least satisfy the
+ * requirement that zero be placed in the timestamp
+ * echo reply when ts_recent isn't valid. The
+ * age isn't reset until we get a valid ts_recent
+ * because we don't want out-of-order segments to be
+ * dropped when ts_recent is old.
+ */
+ tp->ts_recent = 0;
+ } else {
+ tcpstat.tcps_rcvduppack++;
+ tcpstat.tcps_rcvdupbyte += ti->ti_len;
+ tcpstat.tcps_pawsdrop++;
+ goto dropafterack;
+ }
+ }
+
+ todrop = tp->rcv_nxt - ti->ti_seq;
+ if (todrop > 0) {
+ if (tiflags & TH_SYN) {
+ tiflags &= ~TH_SYN;
+ ti->ti_seq++;
+ if (ti->ti_urp > 1)
+ ti->ti_urp--;
+ else
+ tiflags &= ~TH_URG;
+ todrop--;
+ }
+ if (todrop >= ti->ti_len) {
+ tcpstat.tcps_rcvduppack++;
+ tcpstat.tcps_rcvdupbyte += ti->ti_len;
+ /*
+ * If segment is just one to the left of the window,
+ * check two special cases:
+ * 1. Don't toss RST in response to 4.2-style keepalive.
+ * 2. If the only thing to drop is a FIN, we can drop
+ * it, but check the ACK or we will get into FIN
+ * wars if our FINs crossed (both CLOSING).
+ * In either case, send ACK to resynchronize,
+ * but keep on processing for RST or ACK.
+ */
+ if ((tiflags & TH_FIN && todrop == ti->ti_len + 1)
+#ifdef TCP_COMPAT_42
+ || (tiflags & TH_RST && ti->ti_seq == tp->rcv_nxt - 1)
+#endif
+ ) {
+ todrop = ti->ti_len;
+ tiflags &= ~TH_FIN;
+ tp->t_flags |= TF_ACKNOW;
+ } else {
+ /*
+ * Handle the case when a bound socket connects
+ * to itself. Allow packets with a SYN and
+ * an ACK to continue with the processing.
+ */
+ if (todrop != 0 || (tiflags & TH_ACK) == 0)
+ goto dropafterack;
+ }
+ } else {
+ tcpstat.tcps_rcvpartduppack++;
+ tcpstat.tcps_rcvpartdupbyte += todrop;
+ }
+ m_adj(m, todrop);
+ ti->ti_seq += todrop;
+ ti->ti_len -= todrop;
+ if (ti->ti_urp > todrop)
+ ti->ti_urp -= todrop;
+ else {
+ tiflags &= ~TH_URG;
+ ti->ti_urp = 0;
+ }
+ }
+
+ /*
+ * If new data are received on a connection after the
+ * user processes are gone, then RST the other end.
+ */
+ if ((so->so_state & SS_NOFDREF) &&
+ tp->t_state > TCPS_CLOSE_WAIT && ti->ti_len) {
+ tp = tcp_close(tp);
+ tcpstat.tcps_rcvafterclose++;
+ goto dropwithreset;
+ }
+
+ /*
+ * If segment ends after window, drop trailing data
+ * (and PUSH and FIN); if nothing left, just ACK.
+ */
+ todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd);
+ if (todrop > 0) {
+ tcpstat.tcps_rcvpackafterwin++;
+ if (todrop >= ti->ti_len) {
+ tcpstat.tcps_rcvbyteafterwin += ti->ti_len;
+ /*
+ * If a new connection request is received
+ * while in TIME_WAIT, drop the old connection
+ * and start over if the sequence numbers
+ * are above the previous ones.
+ */
+ if (tiflags & TH_SYN &&
+ tp->t_state == TCPS_TIME_WAIT &&
+ SEQ_GT(ti->ti_seq, tp->rcv_nxt)) {
+ iss = tp->rcv_nxt + TCP_ISSINCR;
+ tp = tcp_close(tp);
+ goto findpcb;
+ }
+ /*
+ * If window is closed can only take segments at
+ * window edge, and have to drop data and PUSH from
+ * incoming segments. Continue processing, but
+ * remember to ack. Otherwise, drop segment
+ * and ack.
+ */
+ if (tp->rcv_wnd == 0 && ti->ti_seq == tp->rcv_nxt) {
+ tp->t_flags |= TF_ACKNOW;
+ tcpstat.tcps_rcvwinprobe++;
+ } else
+ goto dropafterack;
+ } else
+ tcpstat.tcps_rcvbyteafterwin += todrop;
+ m_adj(m, -todrop);
+ ti->ti_len -= todrop;
+ tiflags &= ~(TH_PUSH|TH_FIN);
+ }
+
+ /*
+ * If last ACK falls within this segment's sequence numbers,
+ * record its timestamp.
+ */
+ if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) &&
+ SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len +
+ ((tiflags & (TH_SYN|TH_FIN)) != 0))) {
+ tp->ts_recent_age = tcp_now;
+ tp->ts_recent = ts_val;
+ }
+
+ /*
+ * If the RST bit is set examine the state:
+ * SYN_RECEIVED STATE:
+ * If passive open, return to LISTEN state.
+ * If active open, inform user that connection was refused.
+ * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES:
+ * Inform user that connection was reset, and close tcb.
+ * CLOSING, LAST_ACK, TIME_WAIT STATES
+ * Close the tcb.
+ */
+ if (tiflags&TH_RST) switch (tp->t_state) {
+
+ case TCPS_SYN_RECEIVED:
+ so->so_error = ECONNREFUSED;
+ goto close;
+
+ case TCPS_ESTABLISHED:
+ case TCPS_FIN_WAIT_1:
+ case TCPS_FIN_WAIT_2:
+ case TCPS_CLOSE_WAIT:
+ so->so_error = ECONNRESET;
+ close:
+ tp->t_state = TCPS_CLOSED;
+ tcpstat.tcps_drops++;
+ tp = tcp_close(tp);
+ goto drop;
+
+ case TCPS_CLOSING:
+ case TCPS_LAST_ACK:
+ case TCPS_TIME_WAIT:
+ tp = tcp_close(tp);
+ goto drop;
+ }
+
+ /*
+ * If a SYN is in the window, then this is an
+ * error and we send an RST and drop the connection.
+ */
+ if (tiflags & TH_SYN) {
+ tp = tcp_drop(tp, ECONNRESET);
+ goto dropwithreset;
+ }
+
+ /*
+ * If the ACK bit is off we drop the segment and return.
+ */
+ if ((tiflags & TH_ACK) == 0)
+ goto drop;
+
+ /*
+ * Ack processing.
+ */
+ switch (tp->t_state) {
+
+ /*
+ * In SYN_RECEIVED state if the ack ACKs our SYN then enter
+ * ESTABLISHED state and continue processing, otherwise
+ * send an RST.
+ */
+ case TCPS_SYN_RECEIVED:
+ if (SEQ_GT(tp->snd_una, ti->ti_ack) ||
+ SEQ_GT(ti->ti_ack, tp->snd_max))
+ goto dropwithreset;
+ tcpstat.tcps_connects++;
+ soisconnected(so);
+ tp->t_state = TCPS_ESTABLISHED;
+ /* Do window scaling? */
+ if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) ==
+ (TF_RCVD_SCALE|TF_REQ_SCALE)) {
+ tp->snd_scale = tp->requested_s_scale;
+ tp->rcv_scale = tp->request_r_scale;
+ }
+ (void) tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0);
+ tp->snd_wl1 = ti->ti_seq - 1;
+ /* fall into ... */
+
+ /*
+ * In ESTABLISHED state: drop duplicate ACKs; ACK out of range
+ * ACKs. If the ack is in the range
+ * tp->snd_una < ti->ti_ack <= tp->snd_max
+ * then advance tp->snd_una to ti->ti_ack and drop
+ * data from the retransmission queue. If this ACK reflects
+ * more up to date window information we update our window information.
+ */
+ case TCPS_ESTABLISHED:
+ case TCPS_FIN_WAIT_1:
+ case TCPS_FIN_WAIT_2:
+ case TCPS_CLOSE_WAIT:
+ case TCPS_CLOSING:
+ case TCPS_LAST_ACK:
+ case TCPS_TIME_WAIT:
+
+ if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) {
+ if (ti->ti_len == 0 && tiwin == tp->snd_wnd) {
+ tcpstat.tcps_rcvdupack++;
+ /*
+ * If we have outstanding data (other than
+ * a window probe), this is a completely
+ * duplicate ack (ie, window info didn't
+ * change), the ack is the biggest we've
+ * seen and we've seen exactly our rexmt
+ * threshhold of them, assume a packet
+ * has been dropped and retransmit it.
+ * Kludge snd_nxt & the congestion
+ * window so we send only this one
+ * packet.
+ *
+ * We know we're losing at the current
+ * window size so do congestion avoidance
+ * (set ssthresh to half the current window
+ * and pull our congestion window back to
+ * the new ssthresh).
+ *
+ * Dup acks mean that packets have left the
+ * network (they're now cached at the receiver)
+ * so bump cwnd by the amount in the receiver
+ * to keep a constant cwnd packets in the
+ * network.
+ */
+ if (tp->t_timer[TCPT_REXMT] == 0 ||
+ ti->ti_ack != tp->snd_una)
+ tp->t_dupacks = 0;
+ else if (++tp->t_dupacks == tcprexmtthresh) {
+ tcp_seq onxt = tp->snd_nxt;
+ u_int win =
+ min(tp->snd_wnd, tp->snd_cwnd) / 2 /
+ tp->t_maxseg;
+
+ if (win < 2)
+ win = 2;
+ tp->snd_ssthresh = win * tp->t_maxseg;
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->t_rtt = 0;
+ tp->snd_nxt = ti->ti_ack;
+ tp->snd_cwnd = tp->t_maxseg;
+ (void) tcp_output(tp);
+ tp->snd_cwnd = tp->snd_ssthresh +
+ tp->t_maxseg * tp->t_dupacks;
+ if (SEQ_GT(onxt, tp->snd_nxt))
+ tp->snd_nxt = onxt;
+ goto drop;
+ } else if (tp->t_dupacks > tcprexmtthresh) {
+ tp->snd_cwnd += tp->t_maxseg;
+ (void) tcp_output(tp);
+ goto drop;
+ }
+ } else
+ tp->t_dupacks = 0;
+ break;
+ }
+ /*
+ * If the congestion window was inflated to account
+ * for the other side's cached packets, retract it.
+ */
+ if (tp->t_dupacks > tcprexmtthresh &&
+ tp->snd_cwnd > tp->snd_ssthresh)
+ tp->snd_cwnd = tp->snd_ssthresh;
+ tp->t_dupacks = 0;
+ if (SEQ_GT(ti->ti_ack, tp->snd_max)) {
+ tcpstat.tcps_rcvacktoomuch++;
+ goto dropafterack;
+ }
+ acked = ti->ti_ack - tp->snd_una;
+ tcpstat.tcps_rcvackpack++;
+ tcpstat.tcps_rcvackbyte += acked;
+
+ /*
+ * If we have a timestamp reply, update smoothed
+ * round trip time. If no timestamp is present but
+ * transmit timer is running and timed sequence
+ * number was acked, update smoothed round trip time.
+ * Since we now have an rtt measurement, cancel the
+ * timer backoff (cf., Phil Karn's retransmit alg.).
+ * Recompute the initial retransmit timer.
+ */
+ if (ts_present)
+ tcp_xmit_timer(tp, tcp_now-ts_ecr+1);
+ else if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq))
+ tcp_xmit_timer(tp,tp->t_rtt);
+
+ /*
+ * If all outstanding data is acked, stop retransmit
+ * timer and remember to restart (more output or persist).
+ * If there is more data to be acked, restart retransmit
+ * timer, using current (possibly backed-off) value.
+ */
+ if (ti->ti_ack == tp->snd_max) {
+ tp->t_timer[TCPT_REXMT] = 0;
+ needoutput = 1;
+ } else if (tp->t_timer[TCPT_PERSIST] == 0)
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ /*
+ * When new data is acked, open the congestion window.
+ * If the window gives us less than ssthresh packets
+ * in flight, open exponentially (maxseg per packet).
+ * Otherwise open linearly: maxseg per window
+ * (maxseg^2 / cwnd per packet), plus a constant
+ * fraction of a packet (maxseg/8) to help larger windows
+ * open quickly enough.
+ */
+ {
+ register u_int cw = tp->snd_cwnd;
+ register u_int incr = tp->t_maxseg;
+
+ if (cw > tp->snd_ssthresh)
+ incr = incr * incr / cw + incr / 8;
+ tp->snd_cwnd = min(cw + incr, TCP_MAXWIN<<tp->snd_scale);
+ }
+ if (acked > so->so_snd.sb_cc) {
+ tp->snd_wnd -= so->so_snd.sb_cc;
+ sbdrop(&so->so_snd, (int)so->so_snd.sb_cc);
+ ourfinisacked = 1;
+ } else {
+ sbdrop(&so->so_snd, acked);
+ tp->snd_wnd -= acked;
+ ourfinisacked = 0;
+ }
+ if (so->so_snd.sb_flags & SB_NOTIFY)
+ sowwakeup(so);
+ tp->snd_una = ti->ti_ack;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_una))
+ tp->snd_nxt = tp->snd_una;
+
+ switch (tp->t_state) {
+
+ /*
+ * In FIN_WAIT_1 STATE in addition to the processing
+ * for the ESTABLISHED state if our FIN is now acknowledged
+ * then enter FIN_WAIT_2.
+ */
+ case TCPS_FIN_WAIT_1:
+ if (ourfinisacked) {
+ /*
+ * If we can't receive any more
+ * data, then closing user can proceed.
+ * Starting the timer is contrary to the
+ * specification, but if we don't get a FIN
+ * we'll hang forever.
+ */
+ if (so->so_state & SS_CANTRCVMORE) {
+ soisdisconnected(so);
+ tp->t_timer[TCPT_2MSL] = tcp_maxidle;
+ }
+ tp->t_state = TCPS_FIN_WAIT_2;
+ }
+ break;
+
+ /*
+ * In CLOSING STATE in addition to the processing for
+ * the ESTABLISHED state if the ACK acknowledges our FIN
+ * then enter the TIME-WAIT state, otherwise ignore
+ * the segment.
+ */
+ case TCPS_CLOSING:
+ if (ourfinisacked) {
+ tp->t_state = TCPS_TIME_WAIT;
+ tcp_canceltimers(tp);
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ soisdisconnected(so);
+ }
+ break;
+
+ /*
+ * In LAST_ACK, we may still be waiting for data to drain
+ * and/or to be acked, as well as for the ack of our FIN.
+ * If our FIN is now acknowledged, delete the TCB,
+ * enter the closed state and return.
+ */
+ case TCPS_LAST_ACK:
+ if (ourfinisacked) {
+ tp = tcp_close(tp);
+ goto drop;
+ }
+ break;
+
+ /*
+ * In TIME_WAIT state the only thing that should arrive
+ * is a retransmission of the remote FIN. Acknowledge
+ * it and restart the finack timer.
+ */
+ case TCPS_TIME_WAIT:
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ goto dropafterack;
+ }
+ }
+
+step6:
+ /*
+ * Update window information.
+ * Don't look at window if no ACK: TAC's send garbage on first SYN.
+ */
+ if ((tiflags & TH_ACK) &&
+ (SEQ_LT(tp->snd_wl1, ti->ti_seq) || tp->snd_wl1 == ti->ti_seq &&
+ (SEQ_LT(tp->snd_wl2, ti->ti_ack) ||
+ tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd))) {
+ /* keep track of pure window updates */
+ if (ti->ti_len == 0 &&
+ tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd)
+ tcpstat.tcps_rcvwinupd++;
+ tp->snd_wnd = tiwin;
+ tp->snd_wl1 = ti->ti_seq;
+ tp->snd_wl2 = ti->ti_ack;
+ if (tp->snd_wnd > tp->max_sndwnd)
+ tp->max_sndwnd = tp->snd_wnd;
+ needoutput = 1;
+ }
+
+ /*
+ * Process segments with URG.
+ */
+ if ((tiflags & TH_URG) && ti->ti_urp &&
+ TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ /*
+ * This is a kludge, but if we receive and accept
+ * random urgent pointers, we'll crash in
+ * soreceive. It's hard to imagine someone
+ * actually wanting to send this much urgent data.
+ */
+ if (ti->ti_urp + so->so_rcv.sb_cc > sb_max) {
+ ti->ti_urp = 0; /* XXX */
+ tiflags &= ~TH_URG; /* XXX */
+ goto dodata; /* XXX */
+ }
+ /*
+ * If this segment advances the known urgent pointer,
+ * then mark the data stream. This should not happen
+ * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since
+ * a FIN has been received from the remote side.
+ * In these states we ignore the URG.
+ *
+ * According to RFC961 (Assigned Protocols),
+ * the urgent pointer points to the last octet
+ * of urgent data. We continue, however,
+ * to consider it to indicate the first octet
+ * of data past the urgent section as the original
+ * spec states (in one of two places).
+ */
+ if (SEQ_GT(ti->ti_seq+ti->ti_urp, tp->rcv_up)) {
+ tp->rcv_up = ti->ti_seq + ti->ti_urp;
+ so->so_oobmark = so->so_rcv.sb_cc +
+ (tp->rcv_up - tp->rcv_nxt) - 1;
+ if (so->so_oobmark == 0)
+ so->so_state |= SS_RCVATMARK;
+ sohasoutofband(so);
+ tp->t_oobflags &= ~(TCPOOB_HAVEDATA | TCPOOB_HADDATA);
+ }
+ /*
+ * Remove out of band data so doesn't get presented to user.
+ * This can happen independent of advancing the URG pointer,
+ * but if two URG's are pending at once, some out-of-band
+ * data may creep in... ick.
+ */
+ if (ti->ti_urp <= (u_long)ti->ti_len
+#ifdef SO_OOBINLINE
+ && (so->so_options & SO_OOBINLINE) == 0
+#endif
+ )
+ tcp_pulloutofband(so, ti, m);
+ } else
+ /*
+ * If no out of band data is expected,
+ * pull receive urgent pointer along
+ * with the receive window.
+ */
+ if (SEQ_GT(tp->rcv_nxt, tp->rcv_up))
+ tp->rcv_up = tp->rcv_nxt;
+dodata: /* XXX */
+
+ /*
+ * Process the segment text, merging it into the TCP sequencing queue,
+ * and arranging for acknowledgment of receipt if necessary.
+ * This process logically involves adjusting tp->rcv_wnd as data
+ * is presented to the user (this happens in tcp_usrreq.c,
+ * case PRU_RCVD). If a FIN has already been received on this
+ * connection then we just ignore the text.
+ */
+ if ((ti->ti_len || (tiflags&TH_FIN)) &&
+ TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ TCP_REASS(tp, ti, m, so, tiflags);
+ /*
+ * Note the amount of data that peer has sent into
+ * our window, in order to estimate the sender's
+ * buffer size.
+ */
+ len = so->so_rcv.sb_hiwat - (tp->rcv_adv - tp->rcv_nxt);
+ } else {
+ m_freem(m);
+ tiflags &= ~TH_FIN;
+ }
+
+ /*
+ * If FIN is received ACK the FIN and let the user know
+ * that the connection is closing.
+ */
+ if (tiflags & TH_FIN) {
+ if (TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ socantrcvmore(so);
+ tp->t_flags |= TF_ACKNOW;
+ tp->rcv_nxt++;
+ }
+ switch (tp->t_state) {
+
+ /*
+ * In SYN_RECEIVED and ESTABLISHED STATES
+ * enter the CLOSE_WAIT state.
+ */
+ case TCPS_SYN_RECEIVED:
+ case TCPS_ESTABLISHED:
+ tp->t_state = TCPS_CLOSE_WAIT;
+ break;
+
+ /*
+ * If still in FIN_WAIT_1 STATE FIN has not been acked so
+ * enter the CLOSING state.
+ */
+ case TCPS_FIN_WAIT_1:
+ tp->t_state = TCPS_CLOSING;
+ break;
+
+ /*
+ * In FIN_WAIT_2 state enter the TIME_WAIT state,
+ * starting the time-wait timer, turning off the other
+ * standard timers.
+ */
+ case TCPS_FIN_WAIT_2:
+ tp->t_state = TCPS_TIME_WAIT;
+ tcp_canceltimers(tp);
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ soisdisconnected(so);
+ break;
+
+ /*
+ * In TIME_WAIT state restart the 2 MSL time_wait timer.
+ */
+ case TCPS_TIME_WAIT:
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ break;
+ }
+ }
+ if (so->so_options & SO_DEBUG)
+ tcp_trace(TA_INPUT, ostate, tp, &tcp_saveti, 0);
+
+ /*
+ * If this is a small packet, then ACK now - with Nagel
+ * congestion avoidance sender won't send more until
+ * he gets an ACK.
+ */
+ if (ti->ti_len && ((unsigned)ti->ti_len < tp->t_maxseg))
+ tp->t_flags |= TF_ACKNOW;
+
+ /*
+ * Return any desired output.
+ */
+ if (needoutput || (tp->t_flags & TF_ACKNOW))
+ (void) tcp_output(tp);
+ return;
+
+dropafterack:
+ /*
+ * Generate an ACK dropping incoming segment if it occupies
+ * sequence space, where the ACK reflects our state.
+ */
+ if (tiflags & TH_RST)
+ goto drop;
+ m_freem(m);
+ tp->t_flags |= TF_ACKNOW;
+ (void) tcp_output(tp);
+ return;
+
+dropwithreset:
+ /*
+ * Generate a RST, dropping incoming segment.
+ * Make ACK acceptable to originator of segment.
+ * Don't bother to respond if destination was broadcast/multicast.
+ */
+ if ((tiflags & TH_RST) || m->m_flags & (M_BCAST|M_MCAST) ||
+ IN_MULTICAST(ntohl(ti->ti_dst.s_addr)))
+ goto drop;
+ if (tiflags & TH_ACK)
+ tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST);
+ else {
+ if (tiflags & TH_SYN)
+ ti->ti_len++;
+ tcp_respond(tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0,
+ TH_RST|TH_ACK);
+ }
+ /* destroy temporarily created socket */
+ if (dropsocket)
+ (void) soabort(so);
+ return;
+
+drop:
+ /*
+ * Drop space held by incoming segment and return.
+ */
+ if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))
+ tcp_trace(TA_DROP, ostate, tp, &tcp_saveti, 0);
+ m_freem(m);
+ /* destroy temporarily created socket */
+ if (dropsocket)
+ (void) soabort(so);
+ return;
+#ifndef TUBA_INCLUDE
+}
+
+void
+tcp_dooptions(tp, cp, cnt, ti, ts_present, ts_val, ts_ecr)
+ struct tcpcb *tp;
+ u_char *cp;
+ int cnt;
+ struct tcpiphdr *ti;
+ int *ts_present;
+ u_long *ts_val, *ts_ecr;
+{
+ u_short mss;
+ int opt, optlen;
+
+ for (; cnt > 0; cnt -= optlen, cp += optlen) {
+ opt = cp[0];
+ if (opt == TCPOPT_EOL)
+ break;
+ if (opt == TCPOPT_NOP)
+ optlen = 1;
+ else {
+ optlen = cp[1];
+ if (optlen <= 0)
+ break;
+ }
+ switch (opt) {
+
+ default:
+ continue;
+
+ case TCPOPT_MAXSEG:
+ if (optlen != TCPOLEN_MAXSEG)
+ continue;
+ if (!(ti->ti_flags & TH_SYN))
+ continue;
+ bcopy((char *) cp + 2, (char *) &mss, sizeof(mss));
+ NTOHS(mss);
+ (void) tcp_mss(tp, mss); /* sets t_maxseg */
+ break;
+
+ case TCPOPT_WINDOW:
+ if (optlen != TCPOLEN_WINDOW)
+ continue;
+ if (!(ti->ti_flags & TH_SYN))
+ continue;
+ tp->t_flags |= TF_RCVD_SCALE;
+ tp->requested_s_scale = min(cp[2], TCP_MAX_WINSHIFT);
+ break;
+
+ case TCPOPT_TIMESTAMP:
+ if (optlen != TCPOLEN_TIMESTAMP)
+ continue;
+ *ts_present = 1;
+ bcopy((char *)cp + 2, (char *) ts_val, sizeof(*ts_val));
+ NTOHL(*ts_val);
+ bcopy((char *)cp + 6, (char *) ts_ecr, sizeof(*ts_ecr));
+ NTOHL(*ts_ecr);
+
+ /*
+ * A timestamp received in a SYN makes
+ * it ok to send timestamp requests and replies.
+ */
+ if (ti->ti_flags & TH_SYN) {
+ tp->t_flags |= TF_RCVD_TSTMP;
+ tp->ts_recent = *ts_val;
+ tp->ts_recent_age = tcp_now;
+ }
+ break;
+ }
+ }
+}
+
+/*
+ * Pull out of band byte out of a segment so
+ * it doesn't appear in the user's data queue.
+ * It is still reflected in the segment length for
+ * sequencing purposes.
+ */
+void
+tcp_pulloutofband(so, ti, m)
+ struct socket *so;
+ struct tcpiphdr *ti;
+ register struct mbuf *m;
+{
+ int cnt = ti->ti_urp - 1;
+
+ while (cnt >= 0) {
+ if (m->m_len > cnt) {
+ char *cp = mtod(m, caddr_t) + cnt;
+ struct tcpcb *tp = sototcpcb(so);
+
+ tp->t_iobc = *cp;
+ tp->t_oobflags |= TCPOOB_HAVEDATA;
+ bcopy(cp+1, cp, (unsigned)(m->m_len - cnt - 1));
+ m->m_len--;
+ return;
+ }
+ cnt -= m->m_len;
+ m = m->m_next;
+ if (m == 0)
+ break;
+ }
+ panic("tcp_pulloutofband");
+}
+
+/*
+ * Collect new round-trip time estimate
+ * and update averages and current timeout.
+ */
+void
+tcp_xmit_timer(tp, rtt)
+ register struct tcpcb *tp;
+ short rtt;
+{
+ register short delta;
+
+ tcpstat.tcps_rttupdated++;
+ if (tp->t_srtt != 0) {
+ /*
+ * srtt is stored as fixed point with 3 bits after the
+ * binary point (i.e., scaled by 8). The following magic
+ * is equivalent to the smoothing algorithm in rfc793 with
+ * an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed
+ * point). Adjust rtt to origin 0.
+ */
+ delta = rtt - 1 - (tp->t_srtt >> TCP_RTT_SHIFT);
+ if ((tp->t_srtt += delta) <= 0)
+ tp->t_srtt = 1;
+ /*
+ * We accumulate a smoothed rtt variance (actually, a
+ * smoothed mean difference), then set the retransmit
+ * timer to smoothed rtt + 4 times the smoothed variance.
+ * rttvar is stored as fixed point with 2 bits after the
+ * binary point (scaled by 4). The following is
+ * equivalent to rfc793 smoothing with an alpha of .75
+ * (rttvar = rttvar*3/4 + |delta| / 4). This replaces
+ * rfc793's wired-in beta.
+ */
+ if (delta < 0)
+ delta = -delta;
+ delta -= (tp->t_rttvar >> TCP_RTTVAR_SHIFT);
+ if ((tp->t_rttvar += delta) <= 0)
+ tp->t_rttvar = 1;
+ } else {
+ /*
+ * No rtt measurement yet - use the unsmoothed rtt.
+ * Set the variance to half the rtt (so our first
+ * retransmit happens at 3*rtt).
+ */
+ tp->t_srtt = rtt << TCP_RTT_SHIFT;
+ tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1);
+ }
+ tp->t_rtt = 0;
+ tp->t_rxtshift = 0;
+
+ /*
+ * the retransmit should happen at rtt + 4 * rttvar.
+ * Because of the way we do the smoothing, srtt and rttvar
+ * will each average +1/2 tick of bias. When we compute
+ * the retransmit timer, we want 1/2 tick of rounding and
+ * 1 extra tick because of +-1/2 tick uncertainty in the
+ * firing of the timer. The bias will give us exactly the
+ * 1.5 tick we need. But, because the bias is
+ * statistical, we have to test that we don't drop below
+ * the minimum feasible timer (which is 2 ticks).
+ */
+ TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp),
+ tp->t_rttmin, TCPTV_REXMTMAX);
+
+ /*
+ * We received an ack for a packet that wasn't retransmitted;
+ * it is probably safe to discard any error indications we've
+ * received recently. This isn't quite right, but close enough
+ * for now (a route might have failed after we sent a segment,
+ * and the return path might not be symmetrical).
+ */
+ tp->t_softerror = 0;
+}
+
+/*
+ * Determine a reasonable value for maxseg size.
+ * If the route is known, check route for mtu.
+ * If none, use an mss that can be handled on the outgoing
+ * interface without forcing IP to fragment; if bigger than
+ * an mbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES
+ * to utilize large mbufs. If no route is found, route has no mtu,
+ * or the destination isn't local, use a default, hopefully conservative
+ * size (usually 512 or the default IP max size, but no more than the mtu
+ * of the interface), as we can't discover anything about intervening
+ * gateways or networks. We also initialize the congestion/slow start
+ * window to be a single segment if the destination isn't local.
+ * While looking at the routing entry, we also initialize other path-dependent
+ * parameters from pre-set or cached values in the routing entry.
+ */
+int
+tcp_mss(tp, offer)
+ register struct tcpcb *tp;
+ u_int offer;
+{
+ struct route *ro;
+ register struct rtentry *rt;
+ struct ifnet *ifp;
+ register int rtt, mss;
+ u_long bufsize;
+ struct inpcb *inp;
+ struct socket *so;
+ extern int tcp_mssdflt;
+
+ inp = tp->t_inpcb;
+ ro = &inp->inp_route;
+
+ if ((rt = ro->ro_rt) == (struct rtentry *)0) {
+ /* No route yet, so try to acquire one */
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ ro->ro_dst.sa_family = AF_INET;
+ ro->ro_dst.sa_len = sizeof(ro->ro_dst);
+ ((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
+ inp->inp_faddr;
+ rtalloc(ro);
+ }
+ if ((rt = ro->ro_rt) == (struct rtentry *)0)
+ return (tcp_mssdflt);
+ }
+ ifp = rt->rt_ifp;
+ so = inp->inp_socket;
+
+#ifdef RTV_MTU /* if route characteristics exist ... */
+ /*
+ * While we're here, check if there's an initial rtt
+ * or rttvar. Convert from the route-table units
+ * to scaled multiples of the slow timeout timer.
+ */
+ if (tp->t_srtt == 0 && (rtt = rt->rt_rmx.rmx_rtt)) {
+ /*
+ * XXX the lock bit for MTU indicates that the value
+ * is also a minimum value; this is subject to time.
+ */
+ if (rt->rt_rmx.rmx_locks & RTV_RTT)
+ tp->t_rttmin = rtt / (RTM_RTTUNIT / PR_SLOWHZ);
+ tp->t_srtt = rtt / (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTT_SCALE));
+ if (rt->rt_rmx.rmx_rttvar)
+ tp->t_rttvar = rt->rt_rmx.rmx_rttvar /
+ (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTTVAR_SCALE));
+ else
+ /* default variation is +- 1 rtt */
+ tp->t_rttvar =
+ tp->t_srtt * TCP_RTTVAR_SCALE / TCP_RTT_SCALE;
+ TCPT_RANGESET(tp->t_rxtcur,
+ ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1,
+ tp->t_rttmin, TCPTV_REXMTMAX);
+ }
+ /*
+ * if there's an mtu associated with the route, use it
+ */
+ if (rt->rt_rmx.rmx_mtu)
+ mss = rt->rt_rmx.rmx_mtu - sizeof(struct tcpiphdr);
+ else
+#endif /* RTV_MTU */
+ {
+ mss = ifp->if_mtu - sizeof(struct tcpiphdr);
+#if (MCLBYTES & (MCLBYTES - 1)) == 0
+ if (mss > MCLBYTES)
+ mss &= ~(MCLBYTES-1);
+#else
+ if (mss > MCLBYTES)
+ mss = mss / MCLBYTES * MCLBYTES;
+#endif
+ if (!in_localaddr(inp->inp_faddr))
+ mss = min(mss, tcp_mssdflt);
+ }
+ /*
+ * The current mss, t_maxseg, is initialized to the default value.
+ * If we compute a smaller value, reduce the current mss.
+ * If we compute a larger value, return it for use in sending
+ * a max seg size option, but don't store it for use
+ * unless we received an offer at least that large from peer.
+ * However, do not accept offers under 32 bytes.
+ */
+ if (offer)
+ mss = min(mss, offer);
+ mss = max(mss, 32); /* sanity */
+ if (mss < tp->t_maxseg || offer != 0) {
+ /*
+ * If there's a pipesize, change the socket buffer
+ * to that size. Make the socket buffers an integral
+ * number of mss units; if the mss is larger than
+ * the socket buffer, decrease the mss.
+ */
+#ifdef RTV_SPIPE
+ if ((bufsize = rt->rt_rmx.rmx_sendpipe) == 0)
+#endif
+ bufsize = so->so_snd.sb_hiwat;
+ if (bufsize < mss)
+ mss = bufsize;
+ else {
+ bufsize = roundup(bufsize, mss);
+ if (bufsize > sb_max)
+ bufsize = sb_max;
+ (void)sbreserve(&so->so_snd, bufsize);
+ }
+ tp->t_maxseg = mss;
+
+#ifdef RTV_RPIPE
+ if ((bufsize = rt->rt_rmx.rmx_recvpipe) == 0)
+#endif
+ bufsize = so->so_rcv.sb_hiwat;
+ if (bufsize > mss) {
+ bufsize = roundup(bufsize, mss);
+ if (bufsize > sb_max)
+ bufsize = sb_max;
+ (void)sbreserve(&so->so_rcv, bufsize);
+ }
+ }
+ tp->snd_cwnd = mss;
+
+#ifdef RTV_SSTHRESH
+ if (rt->rt_rmx.rmx_ssthresh) {
+ /*
+ * There's some sort of gateway or interface
+ * buffer limit on the path. Use this to set
+ * the slow start threshhold, but set the
+ * threshold to no less than 2*mss.
+ */
+ tp->snd_ssthresh = max(2 * mss, rt->rt_rmx.rmx_ssthresh);
+ }
+#endif /* RTV_MTU */
+ return (mss);
+}
+#endif /* TUBA_INCLUDE */
diff --git a/sys/netinet/tcp_seq.h b/sys/netinet/tcp_seq.h
new file mode 100644
index 0000000..8912299
--- /dev/null
+++ b/sys/netinet/tcp_seq.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_seq.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * TCP sequence numbers are 32 bit integers operated
+ * on with modular arithmetic. These macros can be
+ * used to compare such integers.
+ */
+#define SEQ_LT(a,b) ((int)((a)-(b)) < 0)
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define SEQ_GT(a,b) ((int)((a)-(b)) > 0)
+#define SEQ_GEQ(a,b) ((int)((a)-(b)) >= 0)
+
+/*
+ * Macros to initialize tcp sequence numbers for
+ * send and receive from initial send and receive
+ * sequence numbers.
+ */
+#define tcp_rcvseqinit(tp) \
+ (tp)->rcv_adv = (tp)->rcv_nxt = (tp)->irs + 1
+
+#define tcp_sendseqinit(tp) \
+ (tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = \
+ (tp)->iss
+
+#define TCP_ISSINCR (125*1024) /* increment for tcp_iss each second */
+
+#ifdef KERNEL
+tcp_seq tcp_iss; /* tcp initial send seq # */
+#endif
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
new file mode 100644
index 0000000..dd5940bb
--- /dev/null
+++ b/sys/netinet/tcp_subr.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_subr.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+
+#include <net/route.h>
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+
+/* patchable/settable parameters for tcp */
+int tcp_mssdflt = TCP_MSS;
+int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ;
+int tcp_do_rfc1323 = 1;
+
+extern struct inpcb *tcp_last_inpcb;
+
+/*
+ * Tcp initialization
+ */
+void
+tcp_init()
+{
+
+ tcp_iss = 1; /* wrong */
+ tcb.inp_next = tcb.inp_prev = &tcb;
+ if (max_protohdr < sizeof(struct tcpiphdr))
+ max_protohdr = sizeof(struct tcpiphdr);
+ if (max_linkhdr + sizeof(struct tcpiphdr) > MHLEN)
+ panic("tcp_init");
+}
+
+/*
+ * Create template to be used to send tcp packets on a connection.
+ * Call after host entry created, allocates an mbuf and fills
+ * in a skeletal tcp/ip header, minimizing the amount of work
+ * necessary when the connection is used.
+ */
+struct tcpiphdr *
+tcp_template(tp)
+ struct tcpcb *tp;
+{
+ register struct inpcb *inp = tp->t_inpcb;
+ register struct mbuf *m;
+ register struct tcpiphdr *n;
+
+ if ((n = tp->t_template) == 0) {
+ m = m_get(M_DONTWAIT, MT_HEADER);
+ if (m == NULL)
+ return (0);
+ m->m_len = sizeof (struct tcpiphdr);
+ n = mtod(m, struct tcpiphdr *);
+ }
+ n->ti_next = n->ti_prev = 0;
+ n->ti_x1 = 0;
+ n->ti_pr = IPPROTO_TCP;
+ n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip));
+ n->ti_src = inp->inp_laddr;
+ n->ti_dst = inp->inp_faddr;
+ n->ti_sport = inp->inp_lport;
+ n->ti_dport = inp->inp_fport;
+ n->ti_seq = 0;
+ n->ti_ack = 0;
+ n->ti_x2 = 0;
+ n->ti_off = 5;
+ n->ti_flags = 0;
+ n->ti_win = 0;
+ n->ti_sum = 0;
+ n->ti_urp = 0;
+ return (n);
+}
+
+/*
+ * Send a single message to the TCP at address specified by
+ * the given TCP/IP header. If m == 0, then we make a copy
+ * of the tcpiphdr at ti and send directly to the addressed host.
+ * This is used to force keep alive messages out using the TCP
+ * template for a connection tp->t_template. If flags are given
+ * then we send a message back to the TCP which originated the
+ * segment ti, and discard the mbuf containing it and any other
+ * attached mbufs.
+ *
+ * In any case the ack and sequence number of the transmitted
+ * segment are as specified by the parameters.
+ */
+void
+tcp_respond(tp, ti, m, ack, seq, flags)
+ struct tcpcb *tp;
+ register struct tcpiphdr *ti;
+ register struct mbuf *m;
+ tcp_seq ack, seq;
+ int flags;
+{
+ register int tlen;
+ int win = 0;
+ struct route *ro = 0;
+
+ if (tp) {
+ win = sbspace(&tp->t_inpcb->inp_socket->so_rcv);
+ ro = &tp->t_inpcb->inp_route;
+ }
+ if (m == 0) {
+ m = m_gethdr(M_DONTWAIT, MT_HEADER);
+ if (m == NULL)
+ return;
+#ifdef TCP_COMPAT_42
+ tlen = 1;
+#else
+ tlen = 0;
+#endif
+ m->m_data += max_linkhdr;
+ *mtod(m, struct tcpiphdr *) = *ti;
+ ti = mtod(m, struct tcpiphdr *);
+ flags = TH_ACK;
+ } else {
+ m_freem(m->m_next);
+ m->m_next = 0;
+ m->m_data = (caddr_t)ti;
+ m->m_len = sizeof (struct tcpiphdr);
+ tlen = 0;
+#define xchg(a,b,type) { type t; t=a; a=b; b=t; }
+ xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_long);
+ xchg(ti->ti_dport, ti->ti_sport, u_short);
+#undef xchg
+ }
+ ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen));
+ tlen += sizeof (struct tcpiphdr);
+ m->m_len = tlen;
+ m->m_pkthdr.len = tlen;
+ m->m_pkthdr.rcvif = (struct ifnet *) 0;
+ ti->ti_next = ti->ti_prev = 0;
+ ti->ti_x1 = 0;
+ ti->ti_seq = htonl(seq);
+ ti->ti_ack = htonl(ack);
+ ti->ti_x2 = 0;
+ ti->ti_off = sizeof (struct tcphdr) >> 2;
+ ti->ti_flags = flags;
+ if (tp)
+ ti->ti_win = htons((u_short) (win >> tp->rcv_scale));
+ else
+ ti->ti_win = htons((u_short)win);
+ ti->ti_urp = 0;
+ ti->ti_sum = 0;
+ ti->ti_sum = in_cksum(m, tlen);
+ ((struct ip *)ti)->ip_len = tlen;
+ ((struct ip *)ti)->ip_ttl = ip_defttl;
+ (void) ip_output(m, NULL, ro, 0, NULL);
+}
+
+/*
+ * Create a new TCP control block, making an
+ * empty reassembly queue and hooking it to the argument
+ * protocol control block.
+ */
+struct tcpcb *
+tcp_newtcpcb(inp)
+ struct inpcb *inp;
+{
+ register struct tcpcb *tp;
+
+ tp = malloc(sizeof(*tp), M_PCB, M_NOWAIT);
+ if (tp == NULL)
+ return ((struct tcpcb *)0);
+ bzero((char *) tp, sizeof(struct tcpcb));
+ tp->seg_next = tp->seg_prev = (struct tcpiphdr *)tp;
+ tp->t_maxseg = tcp_mssdflt;
+
+ tp->t_flags = tcp_do_rfc1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
+ tp->t_inpcb = inp;
+ /*
+ * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no
+ * rtt estimate. Set rttvar so that srtt + 2 * rttvar gives
+ * reasonable initial retransmit time.
+ */
+ tp->t_srtt = TCPTV_SRTTBASE;
+ tp->t_rttvar = tcp_rttdflt * PR_SLOWHZ << 2;
+ tp->t_rttmin = TCPTV_MIN;
+ TCPT_RANGESET(tp->t_rxtcur,
+ ((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1,
+ TCPTV_MIN, TCPTV_REXMTMAX);
+ tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+ tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+ inp->inp_ip.ip_ttl = ip_defttl;
+ inp->inp_ppcb = (caddr_t)tp;
+ return (tp);
+}
+
+/*
+ * Drop a TCP connection, reporting
+ * the specified error. If connection is synchronized,
+ * then send a RST to peer.
+ */
+struct tcpcb *
+tcp_drop(tp, errno)
+ register struct tcpcb *tp;
+ int errno;
+{
+ struct socket *so = tp->t_inpcb->inp_socket;
+
+ if (TCPS_HAVERCVDSYN(tp->t_state)) {
+ tp->t_state = TCPS_CLOSED;
+ (void) tcp_output(tp);
+ tcpstat.tcps_drops++;
+ } else
+ tcpstat.tcps_conndrops++;
+ if (errno == ETIMEDOUT && tp->t_softerror)
+ errno = tp->t_softerror;
+ so->so_error = errno;
+ return (tcp_close(tp));
+}
+
+/*
+ * Close a TCP control block:
+ * discard all space held by the tcp
+ * discard internet protocol block
+ * wake up any sleepers
+ */
+struct tcpcb *
+tcp_close(tp)
+ register struct tcpcb *tp;
+{
+ register struct tcpiphdr *t;
+ struct inpcb *inp = tp->t_inpcb;
+ struct socket *so = inp->inp_socket;
+ register struct mbuf *m;
+#ifdef RTV_RTT
+ register struct rtentry *rt;
+
+ /*
+ * If we sent enough data to get some meaningful characteristics,
+ * save them in the routing entry. 'Enough' is arbitrarily
+ * defined as the sendpipesize (default 4K) * 16. This would
+ * give us 16 rtt samples assuming we only get one sample per
+ * window (the usual case on a long haul net). 16 samples is
+ * enough for the srtt filter to converge to within 5% of the correct
+ * value; fewer samples and we could save a very bogus rtt.
+ *
+ * Don't update the default route's characteristics and don't
+ * update anything that the user "locked".
+ */
+ if (SEQ_LT(tp->iss + so->so_snd.sb_hiwat * 16, tp->snd_max) &&
+ (rt = inp->inp_route.ro_rt) &&
+ ((struct sockaddr_in *)rt_key(rt))->sin_addr.s_addr != INADDR_ANY) {
+ register u_long i = 0;
+
+ if ((rt->rt_rmx.rmx_locks & RTV_RTT) == 0) {
+ i = tp->t_srtt *
+ (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTT_SCALE));
+ if (rt->rt_rmx.rmx_rtt && i)
+ /*
+ * filter this update to half the old & half
+ * the new values, converting scale.
+ * See route.h and tcp_var.h for a
+ * description of the scaling constants.
+ */
+ rt->rt_rmx.rmx_rtt =
+ (rt->rt_rmx.rmx_rtt + i) / 2;
+ else
+ rt->rt_rmx.rmx_rtt = i;
+ }
+ if ((rt->rt_rmx.rmx_locks & RTV_RTTVAR) == 0) {
+ i = tp->t_rttvar *
+ (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTTVAR_SCALE));
+ if (rt->rt_rmx.rmx_rttvar && i)
+ rt->rt_rmx.rmx_rttvar =
+ (rt->rt_rmx.rmx_rttvar + i) / 2;
+ else
+ rt->rt_rmx.rmx_rttvar = i;
+ }
+ /*
+ * update the pipelimit (ssthresh) if it has been updated
+ * already or if a pipesize was specified & the threshhold
+ * got below half the pipesize. I.e., wait for bad news
+ * before we start updating, then update on both good
+ * and bad news.
+ */
+ if ((rt->rt_rmx.rmx_locks & RTV_SSTHRESH) == 0 &&
+ (i = tp->snd_ssthresh) && rt->rt_rmx.rmx_ssthresh ||
+ i < (rt->rt_rmx.rmx_sendpipe / 2)) {
+ /*
+ * convert the limit from user data bytes to
+ * packets then to packet data bytes.
+ */
+ i = (i + tp->t_maxseg / 2) / tp->t_maxseg;
+ if (i < 2)
+ i = 2;
+ i *= (u_long)(tp->t_maxseg + sizeof (struct tcpiphdr));
+ if (rt->rt_rmx.rmx_ssthresh)
+ rt->rt_rmx.rmx_ssthresh =
+ (rt->rt_rmx.rmx_ssthresh + i) / 2;
+ else
+ rt->rt_rmx.rmx_ssthresh = i;
+ }
+ }
+#endif /* RTV_RTT */
+ /* free the reassembly queue, if any */
+ t = tp->seg_next;
+ while (t != (struct tcpiphdr *)tp) {
+ t = (struct tcpiphdr *)t->ti_next;
+ m = REASS_MBUF((struct tcpiphdr *)t->ti_prev);
+ remque(t->ti_prev);
+ m_freem(m);
+ }
+ if (tp->t_template)
+ (void) m_free(dtom(tp->t_template));
+ free(tp, M_PCB);
+ inp->inp_ppcb = 0;
+ soisdisconnected(so);
+ /* clobber input pcb cache if we're closing the cached connection */
+ if (inp == tcp_last_inpcb)
+ tcp_last_inpcb = &tcb;
+ in_pcbdetach(inp);
+ tcpstat.tcps_closed++;
+ return ((struct tcpcb *)0);
+}
+
+void
+tcp_drain()
+{
+
+}
+
+/*
+ * Notify a tcp user of an asynchronous error;
+ * store error as soft error, but wake up user
+ * (for now, won't do anything until can select for soft error).
+ */
+void
+tcp_notify(inp, error)
+ struct inpcb *inp;
+ int error;
+{
+ register struct tcpcb *tp = (struct tcpcb *)inp->inp_ppcb;
+ register struct socket *so = inp->inp_socket;
+
+ /*
+ * Ignore some errors if we are hooked up.
+ * If connection hasn't completed, has retransmitted several times,
+ * and receives a second error, give up now. This is better
+ * than waiting a long time to establish a connection that
+ * can never complete.
+ */
+ if (tp->t_state == TCPS_ESTABLISHED &&
+ (error == EHOSTUNREACH || error == ENETUNREACH ||
+ error == EHOSTDOWN)) {
+ return;
+ } else if (tp->t_state < TCPS_ESTABLISHED && tp->t_rxtshift > 3 &&
+ tp->t_softerror)
+ so->so_error = error;
+ else
+ tp->t_softerror = error;
+ wakeup((caddr_t) &so->so_timeo);
+ sorwakeup(so);
+ sowwakeup(so);
+}
+
+void
+tcp_ctlinput(cmd, sa, ip)
+ int cmd;
+ struct sockaddr *sa;
+ register struct ip *ip;
+{
+ register struct tcphdr *th;
+ extern struct in_addr zeroin_addr;
+ extern u_char inetctlerrmap[];
+ void (*notify) __P((struct inpcb *, int)) = tcp_notify;
+
+ if (cmd == PRC_QUENCH)
+ notify = tcp_quench;
+ else if (!PRC_IS_REDIRECT(cmd) &&
+ ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0))
+ return;
+ if (ip) {
+ th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2));
+ in_pcbnotify(&tcb, sa, th->th_dport, ip->ip_src, th->th_sport,
+ cmd, notify);
+ } else
+ in_pcbnotify(&tcb, sa, 0, zeroin_addr, 0, cmd, notify);
+}
+
+/*
+ * When a source quench is received, close congestion window
+ * to one segment. We will gradually open it again as we proceed.
+ */
+void
+tcp_quench(inp, errno)
+ struct inpcb *inp;
+ int errno;
+{
+ struct tcpcb *tp = intotcpcb(inp);
+
+ if (tp)
+ tp->snd_cwnd = tp->t_maxseg;
+}
diff --git a/sys/netinet/tcp_timer.c b/sys/netinet/tcp_timer.c
new file mode 100644
index 0000000..0c0f0f8
--- /dev/null
+++ b/sys/netinet/tcp_timer.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_timer.c 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef TUBA_INCLUDE
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+
+int tcp_keepidle = TCPTV_KEEP_IDLE;
+int tcp_keepintvl = TCPTV_KEEPINTVL;
+int tcp_maxidle;
+#endif /* TUBA_INCLUDE */
+/*
+ * Fast timeout routine for processing delayed acks
+ */
+void
+tcp_fasttimo()
+{
+ register struct inpcb *inp;
+ register struct tcpcb *tp;
+ int s = splnet();
+
+ inp = tcb.inp_next;
+ if (inp)
+ for (; inp != &tcb; inp = inp->inp_next)
+ if ((tp = (struct tcpcb *)inp->inp_ppcb) &&
+ (tp->t_flags & TF_DELACK)) {
+ tp->t_flags &= ~TF_DELACK;
+ tp->t_flags |= TF_ACKNOW;
+ tcpstat.tcps_delack++;
+ (void) tcp_output(tp);
+ }
+ splx(s);
+}
+
+/*
+ * Tcp protocol timeout routine called every 500 ms.
+ * Updates the timers in all active tcb's and
+ * causes finite state machine actions if timers expire.
+ */
+void
+tcp_slowtimo()
+{
+ register struct inpcb *ip, *ipnxt;
+ register struct tcpcb *tp;
+ int s = splnet();
+ register int i;
+
+ tcp_maxidle = TCPTV_KEEPCNT * tcp_keepintvl;
+ /*
+ * Search through tcb's and update active timers.
+ */
+ ip = tcb.inp_next;
+ if (ip == 0) {
+ splx(s);
+ return;
+ }
+ for (; ip != &tcb; ip = ipnxt) {
+ ipnxt = ip->inp_next;
+ tp = intotcpcb(ip);
+ if (tp == 0)
+ continue;
+ for (i = 0; i < TCPT_NTIMERS; i++) {
+ if (tp->t_timer[i] && --tp->t_timer[i] == 0) {
+ (void) tcp_usrreq(tp->t_inpcb->inp_socket,
+ PRU_SLOWTIMO, (struct mbuf *)0,
+ (struct mbuf *)i, (struct mbuf *)0);
+ if (ipnxt->inp_prev != ip)
+ goto tpgone;
+ }
+ }
+ tp->t_idle++;
+ if (tp->t_rtt)
+ tp->t_rtt++;
+tpgone:
+ ;
+ }
+ tcp_iss += TCP_ISSINCR/PR_SLOWHZ; /* increment iss */
+#ifdef TCP_COMPAT_42
+ if ((int)tcp_iss < 0)
+ tcp_iss = 0; /* XXX */
+#endif
+ tcp_now++; /* for timestamps */
+ splx(s);
+}
+#ifndef TUBA_INCLUDE
+
+/*
+ * Cancel all timers for TCP tp.
+ */
+void
+tcp_canceltimers(tp)
+ struct tcpcb *tp;
+{
+ register int i;
+
+ for (i = 0; i < TCPT_NTIMERS; i++)
+ tp->t_timer[i] = 0;
+}
+
+int tcp_backoff[TCP_MAXRXTSHIFT + 1] =
+ { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
+
+/*
+ * TCP timer processing.
+ */
+struct tcpcb *
+tcp_timers(tp, timer)
+ register struct tcpcb *tp;
+ int timer;
+{
+ register int rexmt;
+
+ switch (timer) {
+
+ /*
+ * 2 MSL timeout in shutdown went off. If we're closed but
+ * still waiting for peer to close and connection has been idle
+ * too long, or if 2MSL time is up from TIME_WAIT, delete connection
+ * control block. Otherwise, check again in a bit.
+ */
+ case TCPT_2MSL:
+ if (tp->t_state != TCPS_TIME_WAIT &&
+ tp->t_idle <= tcp_maxidle)
+ tp->t_timer[TCPT_2MSL] = tcp_keepintvl;
+ else
+ tp = tcp_close(tp);
+ break;
+
+ /*
+ * Retransmission timer went off. Message has not
+ * been acked within retransmit interval. Back off
+ * to a longer retransmit interval and retransmit one segment.
+ */
+ case TCPT_REXMT:
+ if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
+ tp->t_rxtshift = TCP_MAXRXTSHIFT;
+ tcpstat.tcps_timeoutdrop++;
+ tp = tcp_drop(tp, tp->t_softerror ?
+ tp->t_softerror : ETIMEDOUT);
+ break;
+ }
+ tcpstat.tcps_rexmttimeo++;
+ rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
+ TCPT_RANGESET(tp->t_rxtcur, rexmt,
+ tp->t_rttmin, TCPTV_REXMTMAX);
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ /*
+ * If losing, let the lower level know and try for
+ * a better route. Also, if we backed off this far,
+ * our srtt estimate is probably bogus. Clobber it
+ * so we'll take the next rtt measurement as our srtt;
+ * move the current srtt into rttvar to keep the current
+ * retransmit times until then.
+ */
+ if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
+ in_losing(tp->t_inpcb);
+ tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
+ tp->t_srtt = 0;
+ }
+ tp->snd_nxt = tp->snd_una;
+ /*
+ * If timing a segment in this window, stop the timer.
+ */
+ tp->t_rtt = 0;
+ /*
+ * Close the congestion window down to one segment
+ * (we'll open it by one segment for each ack we get).
+ * Since we probably have a window's worth of unacked
+ * data accumulated, this "slow start" keeps us from
+ * dumping all that data as back-to-back packets (which
+ * might overwhelm an intermediate gateway).
+ *
+ * There are two phases to the opening: Initially we
+ * open by one mss on each ack. This makes the window
+ * size increase exponentially with time. If the
+ * window is larger than the path can handle, this
+ * exponential growth results in dropped packet(s)
+ * almost immediately. To get more time between
+ * drops but still "push" the network to take advantage
+ * of improving conditions, we switch from exponential
+ * to linear window opening at some threshhold size.
+ * For a threshhold, we use half the current window
+ * size, truncated to a multiple of the mss.
+ *
+ * (the minimum cwnd that will give us exponential
+ * growth is 2 mss. We don't allow the threshhold
+ * to go below this.)
+ */
+ {
+ u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg;
+ if (win < 2)
+ win = 2;
+ tp->snd_cwnd = tp->t_maxseg;
+ tp->snd_ssthresh = win * tp->t_maxseg;
+ tp->t_dupacks = 0;
+ }
+ (void) tcp_output(tp);
+ break;
+
+ /*
+ * Persistance timer into zero window.
+ * Force a byte to be output, if possible.
+ */
+ case TCPT_PERSIST:
+ tcpstat.tcps_persisttimeo++;
+ tcp_setpersist(tp);
+ tp->t_force = 1;
+ (void) tcp_output(tp);
+ tp->t_force = 0;
+ break;
+
+ /*
+ * Keep-alive timer went off; send something
+ * or drop connection if idle for too long.
+ */
+ case TCPT_KEEP:
+ tcpstat.tcps_keeptimeo++;
+ if (tp->t_state < TCPS_ESTABLISHED)
+ goto dropit;
+ if (tp->t_inpcb->inp_socket->so_options & SO_KEEPALIVE &&
+ tp->t_state <= TCPS_CLOSE_WAIT) {
+ if (tp->t_idle >= tcp_keepidle + tcp_maxidle)
+ goto dropit;
+ /*
+ * Send a packet designed to force a response
+ * if the peer is up and reachable:
+ * either an ACK if the connection is still alive,
+ * or an RST if the peer has closed the connection
+ * due to timeout or reboot.
+ * Using sequence number tp->snd_una-1
+ * causes the transmitted zero-length segment
+ * to lie outside the receive window;
+ * by the protocol spec, this requires the
+ * correspondent TCP to respond.
+ */
+ tcpstat.tcps_keepprobe++;
+#ifdef TCP_COMPAT_42
+ /*
+ * The keepalive packet must have nonzero length
+ * to get a 4.2 host to respond.
+ */
+ tcp_respond(tp, tp->t_template, (struct mbuf *)NULL,
+ tp->rcv_nxt - 1, tp->snd_una - 1, 0);
+#else
+ tcp_respond(tp, tp->t_template, (struct mbuf *)NULL,
+ tp->rcv_nxt, tp->snd_una - 1, 0);
+#endif
+ tp->t_timer[TCPT_KEEP] = tcp_keepintvl;
+ } else
+ tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+ break;
+ dropit:
+ tcpstat.tcps_keepdrops++;
+ tp = tcp_drop(tp, ETIMEDOUT);
+ break;
+ }
+ return (tp);
+}
+#endif /* TUBA_INCLUDE */
diff --git a/sys/netinet/tcp_timer.h b/sys/netinet/tcp_timer.h
new file mode 100644
index 0000000..7c31125
--- /dev/null
+++ b/sys/netinet/tcp_timer.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_timer.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Definitions of the TCP timers. These timers are counted
+ * down PR_SLOWHZ times a second.
+ */
+#define TCPT_NTIMERS 4
+
+#define TCPT_REXMT 0 /* retransmit */
+#define TCPT_PERSIST 1 /* retransmit persistance */
+#define TCPT_KEEP 2 /* keep alive */
+#define TCPT_2MSL 3 /* 2*msl quiet time timer */
+
+/*
+ * The TCPT_REXMT timer is used to force retransmissions.
+ * The TCP has the TCPT_REXMT timer set whenever segments
+ * have been sent for which ACKs are expected but not yet
+ * received. If an ACK is received which advances tp->snd_una,
+ * then the retransmit timer is cleared (if there are no more
+ * outstanding segments) or reset to the base value (if there
+ * are more ACKs expected). Whenever the retransmit timer goes off,
+ * we retransmit one unacknowledged segment, and do a backoff
+ * on the retransmit timer.
+ *
+ * The TCPT_PERSIST timer is used to keep window size information
+ * flowing even if the window goes shut. If all previous transmissions
+ * have been acknowledged (so that there are no retransmissions in progress),
+ * and the window is too small to bother sending anything, then we start
+ * the TCPT_PERSIST timer. When it expires, if the window is nonzero,
+ * we go to transmit state. Otherwise, at intervals send a single byte
+ * into the peer's window to force him to update our window information.
+ * We do this at most as often as TCPT_PERSMIN time intervals,
+ * but no more frequently than the current estimate of round-trip
+ * packet time. The TCPT_PERSIST timer is cleared whenever we receive
+ * a window update from the peer.
+ *
+ * The TCPT_KEEP timer is used to keep connections alive. If an
+ * connection is idle (no segments received) for TCPTV_KEEP_INIT amount of time,
+ * but not yet established, then we drop the connection. Once the connection
+ * is established, if the connection is idle for TCPTV_KEEP_IDLE time
+ * (and keepalives have been enabled on the socket), we begin to probe
+ * the connection. We force the peer to send us a segment by sending:
+ * <SEQ=SND.UNA-1><ACK=RCV.NXT><CTL=ACK>
+ * This segment is (deliberately) outside the window, and should elicit
+ * an ack segment in response from the peer. If, despite the TCPT_KEEP
+ * initiated segments we cannot elicit a response from a peer in TCPT_MAXIDLE
+ * amount of time probing, then we drop the connection.
+ */
+
+/*
+ * Time constants.
+ */
+#define TCPTV_MSL ( 30*PR_SLOWHZ) /* max seg lifetime (hah!) */
+#define TCPTV_SRTTBASE 0 /* base roundtrip time;
+ if 0, no idea yet */
+#define TCPTV_SRTTDFLT ( 3*PR_SLOWHZ) /* assumed RTT if no info */
+
+#define TCPTV_PERSMIN ( 5*PR_SLOWHZ) /* retransmit persistance */
+#define TCPTV_PERSMAX ( 60*PR_SLOWHZ) /* maximum persist interval */
+
+#define TCPTV_KEEP_INIT ( 75*PR_SLOWHZ) /* initial connect keep alive */
+#define TCPTV_KEEP_IDLE (120*60*PR_SLOWHZ) /* dflt time before probing */
+#define TCPTV_KEEPINTVL ( 75*PR_SLOWHZ) /* default probe interval */
+#define TCPTV_KEEPCNT 8 /* max probes before drop */
+
+#define TCPTV_MIN ( 1*PR_SLOWHZ) /* minimum allowable value */
+#define TCPTV_REXMTMAX ( 64*PR_SLOWHZ) /* max allowable REXMT value */
+
+#define TCP_LINGERTIME 120 /* linger at most 2 minutes */
+
+#define TCP_MAXRXTSHIFT 12 /* maximum retransmits */
+
+#ifdef TCPTIMERS
+char *tcptimers[] =
+ { "REXMT", "PERSIST", "KEEP", "2MSL" };
+#endif
+
+/*
+ * Force a time value to be in a certain range.
+ */
+#define TCPT_RANGESET(tv, value, tvmin, tvmax) { \
+ (tv) = (value); \
+ if ((u_long)(tv) < (u_long)(tvmin)) \
+ (tv) = (tvmin); \
+ else if ((u_long)(tv) > (u_long)(tvmax)) \
+ (tv) = (tvmax); \
+}
+
+#ifdef KERNEL
+extern int tcp_keepidle; /* time before keepalive probes begin */
+extern int tcp_keepintvl; /* time between keepalive probes */
+extern int tcp_maxidle; /* time to drop after starting probes */
+extern int tcp_ttl; /* time to live for TCP segs */
+extern int tcp_backoff[];
+#endif
diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c
new file mode 100644
index 0000000..dd5940bb
--- /dev/null
+++ b/sys/netinet/tcp_timewait.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_subr.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+
+#include <net/route.h>
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+
+/* patchable/settable parameters for tcp */
+int tcp_mssdflt = TCP_MSS;
+int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ;
+int tcp_do_rfc1323 = 1;
+
+extern struct inpcb *tcp_last_inpcb;
+
+/*
+ * Tcp initialization
+ */
+void
+tcp_init()
+{
+
+ tcp_iss = 1; /* wrong */
+ tcb.inp_next = tcb.inp_prev = &tcb;
+ if (max_protohdr < sizeof(struct tcpiphdr))
+ max_protohdr = sizeof(struct tcpiphdr);
+ if (max_linkhdr + sizeof(struct tcpiphdr) > MHLEN)
+ panic("tcp_init");
+}
+
+/*
+ * Create template to be used to send tcp packets on a connection.
+ * Call after host entry created, allocates an mbuf and fills
+ * in a skeletal tcp/ip header, minimizing the amount of work
+ * necessary when the connection is used.
+ */
+struct tcpiphdr *
+tcp_template(tp)
+ struct tcpcb *tp;
+{
+ register struct inpcb *inp = tp->t_inpcb;
+ register struct mbuf *m;
+ register struct tcpiphdr *n;
+
+ if ((n = tp->t_template) == 0) {
+ m = m_get(M_DONTWAIT, MT_HEADER);
+ if (m == NULL)
+ return (0);
+ m->m_len = sizeof (struct tcpiphdr);
+ n = mtod(m, struct tcpiphdr *);
+ }
+ n->ti_next = n->ti_prev = 0;
+ n->ti_x1 = 0;
+ n->ti_pr = IPPROTO_TCP;
+ n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip));
+ n->ti_src = inp->inp_laddr;
+ n->ti_dst = inp->inp_faddr;
+ n->ti_sport = inp->inp_lport;
+ n->ti_dport = inp->inp_fport;
+ n->ti_seq = 0;
+ n->ti_ack = 0;
+ n->ti_x2 = 0;
+ n->ti_off = 5;
+ n->ti_flags = 0;
+ n->ti_win = 0;
+ n->ti_sum = 0;
+ n->ti_urp = 0;
+ return (n);
+}
+
+/*
+ * Send a single message to the TCP at address specified by
+ * the given TCP/IP header. If m == 0, then we make a copy
+ * of the tcpiphdr at ti and send directly to the addressed host.
+ * This is used to force keep alive messages out using the TCP
+ * template for a connection tp->t_template. If flags are given
+ * then we send a message back to the TCP which originated the
+ * segment ti, and discard the mbuf containing it and any other
+ * attached mbufs.
+ *
+ * In any case the ack and sequence number of the transmitted
+ * segment are as specified by the parameters.
+ */
+void
+tcp_respond(tp, ti, m, ack, seq, flags)
+ struct tcpcb *tp;
+ register struct tcpiphdr *ti;
+ register struct mbuf *m;
+ tcp_seq ack, seq;
+ int flags;
+{
+ register int tlen;
+ int win = 0;
+ struct route *ro = 0;
+
+ if (tp) {
+ win = sbspace(&tp->t_inpcb->inp_socket->so_rcv);
+ ro = &tp->t_inpcb->inp_route;
+ }
+ if (m == 0) {
+ m = m_gethdr(M_DONTWAIT, MT_HEADER);
+ if (m == NULL)
+ return;
+#ifdef TCP_COMPAT_42
+ tlen = 1;
+#else
+ tlen = 0;
+#endif
+ m->m_data += max_linkhdr;
+ *mtod(m, struct tcpiphdr *) = *ti;
+ ti = mtod(m, struct tcpiphdr *);
+ flags = TH_ACK;
+ } else {
+ m_freem(m->m_next);
+ m->m_next = 0;
+ m->m_data = (caddr_t)ti;
+ m->m_len = sizeof (struct tcpiphdr);
+ tlen = 0;
+#define xchg(a,b,type) { type t; t=a; a=b; b=t; }
+ xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_long);
+ xchg(ti->ti_dport, ti->ti_sport, u_short);
+#undef xchg
+ }
+ ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen));
+ tlen += sizeof (struct tcpiphdr);
+ m->m_len = tlen;
+ m->m_pkthdr.len = tlen;
+ m->m_pkthdr.rcvif = (struct ifnet *) 0;
+ ti->ti_next = ti->ti_prev = 0;
+ ti->ti_x1 = 0;
+ ti->ti_seq = htonl(seq);
+ ti->ti_ack = htonl(ack);
+ ti->ti_x2 = 0;
+ ti->ti_off = sizeof (struct tcphdr) >> 2;
+ ti->ti_flags = flags;
+ if (tp)
+ ti->ti_win = htons((u_short) (win >> tp->rcv_scale));
+ else
+ ti->ti_win = htons((u_short)win);
+ ti->ti_urp = 0;
+ ti->ti_sum = 0;
+ ti->ti_sum = in_cksum(m, tlen);
+ ((struct ip *)ti)->ip_len = tlen;
+ ((struct ip *)ti)->ip_ttl = ip_defttl;
+ (void) ip_output(m, NULL, ro, 0, NULL);
+}
+
+/*
+ * Create a new TCP control block, making an
+ * empty reassembly queue and hooking it to the argument
+ * protocol control block.
+ */
+struct tcpcb *
+tcp_newtcpcb(inp)
+ struct inpcb *inp;
+{
+ register struct tcpcb *tp;
+
+ tp = malloc(sizeof(*tp), M_PCB, M_NOWAIT);
+ if (tp == NULL)
+ return ((struct tcpcb *)0);
+ bzero((char *) tp, sizeof(struct tcpcb));
+ tp->seg_next = tp->seg_prev = (struct tcpiphdr *)tp;
+ tp->t_maxseg = tcp_mssdflt;
+
+ tp->t_flags = tcp_do_rfc1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
+ tp->t_inpcb = inp;
+ /*
+ * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no
+ * rtt estimate. Set rttvar so that srtt + 2 * rttvar gives
+ * reasonable initial retransmit time.
+ */
+ tp->t_srtt = TCPTV_SRTTBASE;
+ tp->t_rttvar = tcp_rttdflt * PR_SLOWHZ << 2;
+ tp->t_rttmin = TCPTV_MIN;
+ TCPT_RANGESET(tp->t_rxtcur,
+ ((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1,
+ TCPTV_MIN, TCPTV_REXMTMAX);
+ tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+ tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+ inp->inp_ip.ip_ttl = ip_defttl;
+ inp->inp_ppcb = (caddr_t)tp;
+ return (tp);
+}
+
+/*
+ * Drop a TCP connection, reporting
+ * the specified error. If connection is synchronized,
+ * then send a RST to peer.
+ */
+struct tcpcb *
+tcp_drop(tp, errno)
+ register struct tcpcb *tp;
+ int errno;
+{
+ struct socket *so = tp->t_inpcb->inp_socket;
+
+ if (TCPS_HAVERCVDSYN(tp->t_state)) {
+ tp->t_state = TCPS_CLOSED;
+ (void) tcp_output(tp);
+ tcpstat.tcps_drops++;
+ } else
+ tcpstat.tcps_conndrops++;
+ if (errno == ETIMEDOUT && tp->t_softerror)
+ errno = tp->t_softerror;
+ so->so_error = errno;
+ return (tcp_close(tp));
+}
+
+/*
+ * Close a TCP control block:
+ * discard all space held by the tcp
+ * discard internet protocol block
+ * wake up any sleepers
+ */
+struct tcpcb *
+tcp_close(tp)
+ register struct tcpcb *tp;
+{
+ register struct tcpiphdr *t;
+ struct inpcb *inp = tp->t_inpcb;
+ struct socket *so = inp->inp_socket;
+ register struct mbuf *m;
+#ifdef RTV_RTT
+ register struct rtentry *rt;
+
+ /*
+ * If we sent enough data to get some meaningful characteristics,
+ * save them in the routing entry. 'Enough' is arbitrarily
+ * defined as the sendpipesize (default 4K) * 16. This would
+ * give us 16 rtt samples assuming we only get one sample per
+ * window (the usual case on a long haul net). 16 samples is
+ * enough for the srtt filter to converge to within 5% of the correct
+ * value; fewer samples and we could save a very bogus rtt.
+ *
+ * Don't update the default route's characteristics and don't
+ * update anything that the user "locked".
+ */
+ if (SEQ_LT(tp->iss + so->so_snd.sb_hiwat * 16, tp->snd_max) &&
+ (rt = inp->inp_route.ro_rt) &&
+ ((struct sockaddr_in *)rt_key(rt))->sin_addr.s_addr != INADDR_ANY) {
+ register u_long i = 0;
+
+ if ((rt->rt_rmx.rmx_locks & RTV_RTT) == 0) {
+ i = tp->t_srtt *
+ (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTT_SCALE));
+ if (rt->rt_rmx.rmx_rtt && i)
+ /*
+ * filter this update to half the old & half
+ * the new values, converting scale.
+ * See route.h and tcp_var.h for a
+ * description of the scaling constants.
+ */
+ rt->rt_rmx.rmx_rtt =
+ (rt->rt_rmx.rmx_rtt + i) / 2;
+ else
+ rt->rt_rmx.rmx_rtt = i;
+ }
+ if ((rt->rt_rmx.rmx_locks & RTV_RTTVAR) == 0) {
+ i = tp->t_rttvar *
+ (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTTVAR_SCALE));
+ if (rt->rt_rmx.rmx_rttvar && i)
+ rt->rt_rmx.rmx_rttvar =
+ (rt->rt_rmx.rmx_rttvar + i) / 2;
+ else
+ rt->rt_rmx.rmx_rttvar = i;
+ }
+ /*
+ * update the pipelimit (ssthresh) if it has been updated
+ * already or if a pipesize was specified & the threshhold
+ * got below half the pipesize. I.e., wait for bad news
+ * before we start updating, then update on both good
+ * and bad news.
+ */
+ if ((rt->rt_rmx.rmx_locks & RTV_SSTHRESH) == 0 &&
+ (i = tp->snd_ssthresh) && rt->rt_rmx.rmx_ssthresh ||
+ i < (rt->rt_rmx.rmx_sendpipe / 2)) {
+ /*
+ * convert the limit from user data bytes to
+ * packets then to packet data bytes.
+ */
+ i = (i + tp->t_maxseg / 2) / tp->t_maxseg;
+ if (i < 2)
+ i = 2;
+ i *= (u_long)(tp->t_maxseg + sizeof (struct tcpiphdr));
+ if (rt->rt_rmx.rmx_ssthresh)
+ rt->rt_rmx.rmx_ssthresh =
+ (rt->rt_rmx.rmx_ssthresh + i) / 2;
+ else
+ rt->rt_rmx.rmx_ssthresh = i;
+ }
+ }
+#endif /* RTV_RTT */
+ /* free the reassembly queue, if any */
+ t = tp->seg_next;
+ while (t != (struct tcpiphdr *)tp) {
+ t = (struct tcpiphdr *)t->ti_next;
+ m = REASS_MBUF((struct tcpiphdr *)t->ti_prev);
+ remque(t->ti_prev);
+ m_freem(m);
+ }
+ if (tp->t_template)
+ (void) m_free(dtom(tp->t_template));
+ free(tp, M_PCB);
+ inp->inp_ppcb = 0;
+ soisdisconnected(so);
+ /* clobber input pcb cache if we're closing the cached connection */
+ if (inp == tcp_last_inpcb)
+ tcp_last_inpcb = &tcb;
+ in_pcbdetach(inp);
+ tcpstat.tcps_closed++;
+ return ((struct tcpcb *)0);
+}
+
+void
+tcp_drain()
+{
+
+}
+
+/*
+ * Notify a tcp user of an asynchronous error;
+ * store error as soft error, but wake up user
+ * (for now, won't do anything until can select for soft error).
+ */
+void
+tcp_notify(inp, error)
+ struct inpcb *inp;
+ int error;
+{
+ register struct tcpcb *tp = (struct tcpcb *)inp->inp_ppcb;
+ register struct socket *so = inp->inp_socket;
+
+ /*
+ * Ignore some errors if we are hooked up.
+ * If connection hasn't completed, has retransmitted several times,
+ * and receives a second error, give up now. This is better
+ * than waiting a long time to establish a connection that
+ * can never complete.
+ */
+ if (tp->t_state == TCPS_ESTABLISHED &&
+ (error == EHOSTUNREACH || error == ENETUNREACH ||
+ error == EHOSTDOWN)) {
+ return;
+ } else if (tp->t_state < TCPS_ESTABLISHED && tp->t_rxtshift > 3 &&
+ tp->t_softerror)
+ so->so_error = error;
+ else
+ tp->t_softerror = error;
+ wakeup((caddr_t) &so->so_timeo);
+ sorwakeup(so);
+ sowwakeup(so);
+}
+
+void
+tcp_ctlinput(cmd, sa, ip)
+ int cmd;
+ struct sockaddr *sa;
+ register struct ip *ip;
+{
+ register struct tcphdr *th;
+ extern struct in_addr zeroin_addr;
+ extern u_char inetctlerrmap[];
+ void (*notify) __P((struct inpcb *, int)) = tcp_notify;
+
+ if (cmd == PRC_QUENCH)
+ notify = tcp_quench;
+ else if (!PRC_IS_REDIRECT(cmd) &&
+ ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0))
+ return;
+ if (ip) {
+ th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2));
+ in_pcbnotify(&tcb, sa, th->th_dport, ip->ip_src, th->th_sport,
+ cmd, notify);
+ } else
+ in_pcbnotify(&tcb, sa, 0, zeroin_addr, 0, cmd, notify);
+}
+
+/*
+ * When a source quench is received, close congestion window
+ * to one segment. We will gradually open it again as we proceed.
+ */
+void
+tcp_quench(inp, errno)
+ struct inpcb *inp;
+ int errno;
+{
+ struct tcpcb *tp = intotcpcb(inp);
+
+ if (tp)
+ tp->snd_cwnd = tp->t_maxseg;
+}
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
new file mode 100644
index 0000000..37dac23
--- /dev/null
+++ b/sys/netinet/tcp_usrreq.c
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_usrreq.c 8.2 (Berkeley) 1/3/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_debug.h>
+
+/*
+ * TCP protocol interface to socket abstraction.
+ */
+extern char *tcpstates[];
+
+/*
+ * Process a TCP user request for TCP tb. If this is a send request
+ * then m is the mbuf chain of send data. If this is a timer expiration
+ * (called from the software clock routine), then timertype tells which timer.
+ */
+/*ARGSUSED*/
+int
+tcp_usrreq(so, req, m, nam, control)
+ struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *control;
+{
+ register struct inpcb *inp;
+ register struct tcpcb *tp = 0;
+ int s;
+ int error = 0;
+ int ostate;
+
+ if (req == PRU_CONTROL)
+ return (in_control(so, (int)m, (caddr_t)nam,
+ (struct ifnet *)control));
+ if (control && control->m_len) {
+ m_freem(control);
+ if (m)
+ m_freem(m);
+ return (EINVAL);
+ }
+
+ s = splnet();
+ inp = sotoinpcb(so);
+ /*
+ * When a TCP is attached to a socket, then there will be
+ * a (struct inpcb) pointed at by the socket, and this
+ * structure will point at a subsidary (struct tcpcb).
+ */
+ if (inp == 0 && req != PRU_ATTACH) {
+ splx(s);
+ return (EINVAL); /* XXX */
+ }
+ if (inp) {
+ tp = intotcpcb(inp);
+ /* WHAT IF TP IS 0? */
+#ifdef KPROF
+ tcp_acounts[tp->t_state][req]++;
+#endif
+ ostate = tp->t_state;
+ } else
+ ostate = 0;
+ switch (req) {
+
+ /*
+ * TCP attaches to socket via PRU_ATTACH, reserving space,
+ * and an internet control block.
+ */
+ case PRU_ATTACH:
+ if (inp) {
+ error = EISCONN;
+ break;
+ }
+ error = tcp_attach(so);
+ if (error)
+ break;
+ if ((so->so_options & SO_LINGER) && so->so_linger == 0)
+ so->so_linger = TCP_LINGERTIME;
+ tp = sototcpcb(so);
+ break;
+
+ /*
+ * PRU_DETACH detaches the TCP protocol from the socket.
+ * If the protocol state is non-embryonic, then can't
+ * do this directly: have to initiate a PRU_DISCONNECT,
+ * which may finish later; embryonic TCB's can just
+ * be discarded here.
+ */
+ case PRU_DETACH:
+ if (tp->t_state > TCPS_LISTEN)
+ tp = tcp_disconnect(tp);
+ else
+ tp = tcp_close(tp);
+ break;
+
+ /*
+ * Give the socket an address.
+ */
+ case PRU_BIND:
+ error = in_pcbbind(inp, nam);
+ if (error)
+ break;
+ break;
+
+ /*
+ * Prepare to accept connections.
+ */
+ case PRU_LISTEN:
+ if (inp->inp_lport == 0)
+ error = in_pcbbind(inp, (struct mbuf *)0);
+ if (error == 0)
+ tp->t_state = TCPS_LISTEN;
+ break;
+
+ /*
+ * Initiate connection to peer.
+ * Create a template for use in transmissions on this connection.
+ * Enter SYN_SENT state, and mark socket as connecting.
+ * Start keep-alive timer, and seed output sequence space.
+ * Send initial segment on connection.
+ */
+ case PRU_CONNECT:
+ if (inp->inp_lport == 0) {
+ error = in_pcbbind(inp, (struct mbuf *)0);
+ if (error)
+ break;
+ }
+ error = in_pcbconnect(inp, nam);
+ if (error)
+ break;
+ tp->t_template = tcp_template(tp);
+ if (tp->t_template == 0) {
+ in_pcbdisconnect(inp);
+ error = ENOBUFS;
+ break;
+ }
+ /* Compute window scaling to request. */
+ while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
+ (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat)
+ tp->request_r_scale++;
+ soisconnecting(so);
+ tcpstat.tcps_connattempt++;
+ tp->t_state = TCPS_SYN_SENT;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2;
+ tcp_sendseqinit(tp);
+ error = tcp_output(tp);
+ break;
+
+ /*
+ * Create a TCP connection between two sockets.
+ */
+ case PRU_CONNECT2:
+ error = EOPNOTSUPP;
+ break;
+
+ /*
+ * Initiate disconnect from peer.
+ * If connection never passed embryonic stage, just drop;
+ * else if don't need to let data drain, then can just drop anyways,
+ * else have to begin TCP shutdown process: mark socket disconnecting,
+ * drain unread data, state switch to reflect user close, and
+ * send segment (e.g. FIN) to peer. Socket will be really disconnected
+ * when peer sends FIN and acks ours.
+ *
+ * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB.
+ */
+ case PRU_DISCONNECT:
+ tp = tcp_disconnect(tp);
+ break;
+
+ /*
+ * Accept a connection. Essentially all the work is
+ * done at higher levels; just return the address
+ * of the peer, storing through addr.
+ */
+ case PRU_ACCEPT:
+ in_setpeeraddr(inp, nam);
+ break;
+
+ /*
+ * Mark the connection as being incapable of further output.
+ */
+ case PRU_SHUTDOWN:
+ socantsendmore(so);
+ tp = tcp_usrclosed(tp);
+ if (tp)
+ error = tcp_output(tp);
+ break;
+
+ /*
+ * After a receive, possibly send window update to peer.
+ */
+ case PRU_RCVD:
+ (void) tcp_output(tp);
+ break;
+
+ /*
+ * Do a send by putting data in output queue and updating urgent
+ * marker if URG set. Possibly send more data.
+ */
+ case PRU_SEND:
+ sbappend(&so->so_snd, m);
+ error = tcp_output(tp);
+ break;
+
+ /*
+ * Abort the TCP.
+ */
+ case PRU_ABORT:
+ tp = tcp_drop(tp, ECONNABORTED);
+ break;
+
+ case PRU_SENSE:
+ ((struct stat *) m)->st_blksize = so->so_snd.sb_hiwat;
+ (void) splx(s);
+ return (0);
+
+ case PRU_RCVOOB:
+ if ((so->so_oobmark == 0 &&
+ (so->so_state & SS_RCVATMARK) == 0) ||
+ so->so_options & SO_OOBINLINE ||
+ tp->t_oobflags & TCPOOB_HADDATA) {
+ error = EINVAL;
+ break;
+ }
+ if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) {
+ error = EWOULDBLOCK;
+ break;
+ }
+ m->m_len = 1;
+ *mtod(m, caddr_t) = tp->t_iobc;
+ if (((int)nam & MSG_PEEK) == 0)
+ tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA);
+ break;
+
+ case PRU_SENDOOB:
+ if (sbspace(&so->so_snd) < -512) {
+ m_freem(m);
+ error = ENOBUFS;
+ break;
+ }
+ /*
+ * According to RFC961 (Assigned Protocols),
+ * the urgent pointer points to the last octet
+ * of urgent data. We continue, however,
+ * to consider it to indicate the first octet
+ * of data past the urgent section.
+ * Otherwise, snd_up should be one lower.
+ */
+ sbappend(&so->so_snd, m);
+ tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
+ tp->t_force = 1;
+ error = tcp_output(tp);
+ tp->t_force = 0;
+ break;
+
+ case PRU_SOCKADDR:
+ in_setsockaddr(inp, nam);
+ break;
+
+ case PRU_PEERADDR:
+ in_setpeeraddr(inp, nam);
+ break;
+
+ /*
+ * TCP slow timer went off; going through this
+ * routine for tracing's sake.
+ */
+ case PRU_SLOWTIMO:
+ tp = tcp_timers(tp, (int)nam);
+ req |= (int)nam << 8; /* for debug's sake */
+ break;
+
+ default:
+ panic("tcp_usrreq");
+ }
+ if (tp && (so->so_options & SO_DEBUG))
+ tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0, req);
+ splx(s);
+ return (error);
+}
+
+int
+tcp_ctloutput(op, so, level, optname, mp)
+ int op;
+ struct socket *so;
+ int level, optname;
+ struct mbuf **mp;
+{
+ int error = 0, s;
+ struct inpcb *inp;
+ register struct tcpcb *tp;
+ register struct mbuf *m;
+ register int i;
+
+ s = splnet();
+ inp = sotoinpcb(so);
+ if (inp == NULL) {
+ splx(s);
+ if (op == PRCO_SETOPT && *mp)
+ (void) m_free(*mp);
+ return (ECONNRESET);
+ }
+ if (level != IPPROTO_TCP) {
+ error = ip_ctloutput(op, so, level, optname, mp);
+ splx(s);
+ return (error);
+ }
+ tp = intotcpcb(inp);
+
+ switch (op) {
+
+ case PRCO_SETOPT:
+ m = *mp;
+ switch (optname) {
+
+ case TCP_NODELAY:
+ if (m == NULL || m->m_len < sizeof (int))
+ error = EINVAL;
+ else if (*mtod(m, int *))
+ tp->t_flags |= TF_NODELAY;
+ else
+ tp->t_flags &= ~TF_NODELAY;
+ break;
+
+ case TCP_MAXSEG:
+ if (m && (i = *mtod(m, int *)) > 0 && i <= tp->t_maxseg)
+ tp->t_maxseg = i;
+ else
+ error = EINVAL;
+ break;
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ if (m)
+ (void) m_free(m);
+ break;
+
+ case PRCO_GETOPT:
+ *mp = m = m_get(M_WAIT, MT_SOOPTS);
+ m->m_len = sizeof(int);
+
+ switch (optname) {
+ case TCP_NODELAY:
+ *mtod(m, int *) = tp->t_flags & TF_NODELAY;
+ break;
+ case TCP_MAXSEG:
+ *mtod(m, int *) = tp->t_maxseg;
+ break;
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ break;
+ }
+ splx(s);
+ return (error);
+}
+
+/*
+ * tcp_sendspace and tcp_recvspace are the default send and receive window
+ * sizes, respectively. These are obsolescent (this information should
+ * be set by the route).
+ */
+#ifdef TCP_SMALLSPACE
+u_long tcp_sendspace = 1024*4;
+u_long tcp_recvspace = 1024*4;
+#else
+u_long tcp_sendspace = 1024*16;
+u_long tcp_recvspace = 1024*16;
+#endif
+
+/*
+ * Attach TCP protocol to socket, allocating
+ * internet protocol control block, tcp control block,
+ * bufer space, and entering LISTEN state if to accept connections.
+ */
+int
+tcp_attach(so)
+ struct socket *so;
+{
+ register struct tcpcb *tp;
+ struct inpcb *inp;
+ int error;
+
+ if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
+ error = soreserve(so, tcp_sendspace, tcp_recvspace);
+ if (error)
+ return (error);
+ }
+ error = in_pcballoc(so, &tcb);
+ if (error)
+ return (error);
+ inp = sotoinpcb(so);
+ tp = tcp_newtcpcb(inp);
+ if (tp == 0) {
+ int nofd = so->so_state & SS_NOFDREF; /* XXX */
+
+ so->so_state &= ~SS_NOFDREF; /* don't free the socket yet */
+ in_pcbdetach(inp);
+ so->so_state |= nofd;
+ return (ENOBUFS);
+ }
+ tp->t_state = TCPS_CLOSED;
+ return (0);
+}
+
+/*
+ * Initiate (or continue) disconnect.
+ * If embryonic state, just send reset (once).
+ * If in ``let data drain'' option and linger null, just drop.
+ * Otherwise (hard), mark socket disconnecting and drop
+ * current input data; switch states based on user close, and
+ * send segment to peer (with FIN).
+ */
+struct tcpcb *
+tcp_disconnect(tp)
+ register struct tcpcb *tp;
+{
+ struct socket *so = tp->t_inpcb->inp_socket;
+
+ if (tp->t_state < TCPS_ESTABLISHED)
+ tp = tcp_close(tp);
+ else if ((so->so_options & SO_LINGER) && so->so_linger == 0)
+ tp = tcp_drop(tp, 0);
+ else {
+ soisdisconnecting(so);
+ sbflush(&so->so_rcv);
+ tp = tcp_usrclosed(tp);
+ if (tp)
+ (void) tcp_output(tp);
+ }
+ return (tp);
+}
+
+/*
+ * User issued close, and wish to trail through shutdown states:
+ * if never received SYN, just forget it. If got a SYN from peer,
+ * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
+ * If already got a FIN from peer, then almost done; go to LAST_ACK
+ * state. In all other cases, have already sent FIN to peer (e.g.
+ * after PRU_SHUTDOWN), and just have to play tedious game waiting
+ * for peer to send FIN or not respond to keep-alives, etc.
+ * We can let the user exit from the close as soon as the FIN is acked.
+ */
+struct tcpcb *
+tcp_usrclosed(tp)
+ register struct tcpcb *tp;
+{
+
+ switch (tp->t_state) {
+
+ case TCPS_CLOSED:
+ case TCPS_LISTEN:
+ case TCPS_SYN_SENT:
+ tp->t_state = TCPS_CLOSED;
+ tp = tcp_close(tp);
+ break;
+
+ case TCPS_SYN_RECEIVED:
+ case TCPS_ESTABLISHED:
+ tp->t_state = TCPS_FIN_WAIT_1;
+ break;
+
+ case TCPS_CLOSE_WAIT:
+ tp->t_state = TCPS_LAST_ACK;
+ break;
+ }
+ if (tp && tp->t_state >= TCPS_FIN_WAIT_2)
+ soisdisconnected(tp->t_inpcb->inp_socket);
+ return (tp);
+}
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
new file mode 100644
index 0000000..8a8e751
--- /dev/null
+++ b/sys/netinet/tcp_var.h
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 1982, 1986, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_var.h 8.3 (Berkeley) 4/10/94
+ */
+
+/*
+ * Kernel variables for tcp.
+ */
+
+/*
+ * Tcp control block, one per tcp; fields:
+ */
+struct tcpcb {
+ struct tcpiphdr *seg_next; /* sequencing queue */
+ struct tcpiphdr *seg_prev;
+ short t_state; /* state of this connection */
+ short t_timer[TCPT_NTIMERS]; /* tcp timers */
+ short t_rxtshift; /* log(2) of rexmt exp. backoff */
+ short t_rxtcur; /* current retransmit value */
+ short t_dupacks; /* consecutive dup acks recd */
+ u_short t_maxseg; /* maximum segment size */
+ char t_force; /* 1 if forcing out a byte */
+ u_short t_flags;
+#define TF_ACKNOW 0x0001 /* ack peer immediately */
+#define TF_DELACK 0x0002 /* ack, but try to delay it */
+#define TF_NODELAY 0x0004 /* don't delay packets to coalesce */
+#define TF_NOOPT 0x0008 /* don't use tcp options */
+#define TF_SENTFIN 0x0010 /* have sent FIN */
+#define TF_REQ_SCALE 0x0020 /* have/will request window scaling */
+#define TF_RCVD_SCALE 0x0040 /* other side has requested scaling */
+#define TF_REQ_TSTMP 0x0080 /* have/will request timestamps */
+#define TF_RCVD_TSTMP 0x0100 /* a timestamp was received in SYN */
+#define TF_SACK_PERMIT 0x0200 /* other side said I could SACK */
+
+ struct tcpiphdr *t_template; /* skeletal packet for transmit */
+ struct inpcb *t_inpcb; /* back pointer to internet pcb */
+/*
+ * The following fields are used as in the protocol specification.
+ * See RFC783, Dec. 1981, page 21.
+ */
+/* send sequence variables */
+ tcp_seq snd_una; /* send unacknowledged */
+ tcp_seq snd_nxt; /* send next */
+ tcp_seq snd_up; /* send urgent pointer */
+ tcp_seq snd_wl1; /* window update seg seq number */
+ tcp_seq snd_wl2; /* window update seg ack number */
+ tcp_seq iss; /* initial send sequence number */
+ u_long snd_wnd; /* send window */
+/* receive sequence variables */
+ u_long rcv_wnd; /* receive window */
+ tcp_seq rcv_nxt; /* receive next */
+ tcp_seq rcv_up; /* receive urgent pointer */
+ tcp_seq irs; /* initial receive sequence number */
+/*
+ * Additional variables for this implementation.
+ */
+/* receive variables */
+ tcp_seq rcv_adv; /* advertised window */
+/* retransmit variables */
+ tcp_seq snd_max; /* highest sequence number sent;
+ * used to recognize retransmits
+ */
+/* congestion control (for slow start, source quench, retransmit after loss) */
+ u_long snd_cwnd; /* congestion-controlled window */
+ u_long snd_ssthresh; /* snd_cwnd size threshhold for
+ * for slow start exponential to
+ * linear switch
+ */
+/*
+ * transmit timing stuff. See below for scale of srtt and rttvar.
+ * "Variance" is actually smoothed difference.
+ */
+ short t_idle; /* inactivity time */
+ short t_rtt; /* round trip time */
+ tcp_seq t_rtseq; /* sequence number being timed */
+ short t_srtt; /* smoothed round-trip time */
+ short t_rttvar; /* variance in round-trip time */
+ u_short t_rttmin; /* minimum rtt allowed */
+ u_long max_sndwnd; /* largest window peer has offered */
+
+/* out-of-band data */
+ char t_oobflags; /* have some */
+ char t_iobc; /* input character */
+#define TCPOOB_HAVEDATA 0x01
+#define TCPOOB_HADDATA 0x02
+ short t_softerror; /* possible error not yet reported */
+
+/* RFC 1323 variables */
+ u_char snd_scale; /* window scaling for send window */
+ u_char rcv_scale; /* window scaling for recv window */
+ u_char request_r_scale; /* pending window scaling */
+ u_char requested_s_scale;
+ u_long ts_recent; /* timestamp echo data */
+ u_long ts_recent_age; /* when last updated */
+ tcp_seq last_ack_sent;
+
+/* TUBA stuff */
+ caddr_t t_tuba_pcb; /* next level down pcb for TCP over z */
+};
+
+#define intotcpcb(ip) ((struct tcpcb *)(ip)->inp_ppcb)
+#define sototcpcb(so) (intotcpcb(sotoinpcb(so)))
+
+/*
+ * The smoothed round-trip time and estimated variance
+ * are stored as fixed point numbers scaled by the values below.
+ * For convenience, these scales are also used in smoothing the average
+ * (smoothed = (1/scale)sample + ((scale-1)/scale)smoothed).
+ * With these scales, srtt has 3 bits to the right of the binary point,
+ * and thus an "ALPHA" of 0.875. rttvar has 2 bits to the right of the
+ * binary point, and is smoothed with an ALPHA of 0.75.
+ */
+#define TCP_RTT_SCALE 8 /* multiplier for srtt; 3 bits frac. */
+#define TCP_RTT_SHIFT 3 /* shift for srtt; 3 bits frac. */
+#define TCP_RTTVAR_SCALE 4 /* multiplier for rttvar; 2 bits */
+#define TCP_RTTVAR_SHIFT 2 /* multiplier for rttvar; 2 bits */
+
+/*
+ * The initial retransmission should happen at rtt + 4 * rttvar.
+ * Because of the way we do the smoothing, srtt and rttvar
+ * will each average +1/2 tick of bias. When we compute
+ * the retransmit timer, we want 1/2 tick of rounding and
+ * 1 extra tick because of +-1/2 tick uncertainty in the
+ * firing of the timer. The bias will give us exactly the
+ * 1.5 tick we need. But, because the bias is
+ * statistical, we have to test that we don't drop below
+ * the minimum feasible timer (which is 2 ticks).
+ * This macro assumes that the value of TCP_RTTVAR_SCALE
+ * is the same as the multiplier for rttvar.
+ */
+#define TCP_REXMTVAL(tp) \
+ (((tp)->t_srtt >> TCP_RTT_SHIFT) + (tp)->t_rttvar)
+
+/* XXX
+ * We want to avoid doing m_pullup on incoming packets but that
+ * means avoiding dtom on the tcp reassembly code. That in turn means
+ * keeping an mbuf pointer in the reassembly queue (since we might
+ * have a cluster). As a quick hack, the source & destination
+ * port numbers (which are no longer needed once we've located the
+ * tcpcb) are overlayed with an mbuf pointer.
+ */
+#define REASS_MBUF(ti) (*(struct mbuf **)&((ti)->ti_t))
+
+/*
+ * TCP statistics.
+ * Many of these should be kept per connection,
+ * but that's inconvenient at the moment.
+ */
+struct tcpstat {
+ u_long tcps_connattempt; /* connections initiated */
+ u_long tcps_accepts; /* connections accepted */
+ u_long tcps_connects; /* connections established */
+ u_long tcps_drops; /* connections dropped */
+ u_long tcps_conndrops; /* embryonic connections dropped */
+ u_long tcps_closed; /* conn. closed (includes drops) */
+ u_long tcps_segstimed; /* segs where we tried to get rtt */
+ u_long tcps_rttupdated; /* times we succeeded */
+ u_long tcps_delack; /* delayed acks sent */
+ u_long tcps_timeoutdrop; /* conn. dropped in rxmt timeout */
+ u_long tcps_rexmttimeo; /* retransmit timeouts */
+ u_long tcps_persisttimeo; /* persist timeouts */
+ u_long tcps_keeptimeo; /* keepalive timeouts */
+ u_long tcps_keepprobe; /* keepalive probes sent */
+ u_long tcps_keepdrops; /* connections dropped in keepalive */
+
+ u_long tcps_sndtotal; /* total packets sent */
+ u_long tcps_sndpack; /* data packets sent */
+ u_long tcps_sndbyte; /* data bytes sent */
+ u_long tcps_sndrexmitpack; /* data packets retransmitted */
+ u_long tcps_sndrexmitbyte; /* data bytes retransmitted */
+ u_long tcps_sndacks; /* ack-only packets sent */
+ u_long tcps_sndprobe; /* window probes sent */
+ u_long tcps_sndurg; /* packets sent with URG only */
+ u_long tcps_sndwinup; /* window update-only packets sent */
+ u_long tcps_sndctrl; /* control (SYN|FIN|RST) packets sent */
+
+ u_long tcps_rcvtotal; /* total packets received */
+ u_long tcps_rcvpack; /* packets received in sequence */
+ u_long tcps_rcvbyte; /* bytes received in sequence */
+ u_long tcps_rcvbadsum; /* packets received with ccksum errs */
+ u_long tcps_rcvbadoff; /* packets received with bad offset */
+ u_long tcps_rcvshort; /* packets received too short */
+ u_long tcps_rcvduppack; /* duplicate-only packets received */
+ u_long tcps_rcvdupbyte; /* duplicate-only bytes received */
+ u_long tcps_rcvpartduppack; /* packets with some duplicate data */
+ u_long tcps_rcvpartdupbyte; /* dup. bytes in part-dup. packets */
+ u_long tcps_rcvoopack; /* out-of-order packets received */
+ u_long tcps_rcvoobyte; /* out-of-order bytes received */
+ u_long tcps_rcvpackafterwin; /* packets with data after window */
+ u_long tcps_rcvbyteafterwin; /* bytes rcvd after window */
+ u_long tcps_rcvafterclose; /* packets rcvd after "close" */
+ u_long tcps_rcvwinprobe; /* rcvd window probe packets */
+ u_long tcps_rcvdupack; /* rcvd duplicate acks */
+ u_long tcps_rcvacktoomuch; /* rcvd acks for unsent data */
+ u_long tcps_rcvackpack; /* rcvd ack packets */
+ u_long tcps_rcvackbyte; /* bytes acked by rcvd acks */
+ u_long tcps_rcvwinupd; /* rcvd window update packets */
+ u_long tcps_pawsdrop; /* segments dropped due to PAWS */
+ u_long tcps_predack; /* times hdr predict ok for acks */
+ u_long tcps_preddat; /* times hdr predict ok for data pkts */
+ u_long tcps_pcbcachemiss;
+};
+
+#ifdef KERNEL
+struct inpcb tcb; /* head of queue of active tcpcb's */
+struct tcpstat tcpstat; /* tcp statistics */
+u_long tcp_now; /* for RFC 1323 timestamps */
+
+int tcp_attach __P((struct socket *));
+void tcp_canceltimers __P((struct tcpcb *));
+struct tcpcb *
+ tcp_close __P((struct tcpcb *));
+void tcp_ctlinput __P((int, struct sockaddr *, struct ip *));
+int tcp_ctloutput __P((int, struct socket *, int, int, struct mbuf **));
+struct tcpcb *
+ tcp_disconnect __P((struct tcpcb *));
+struct tcpcb *
+ tcp_drop __P((struct tcpcb *, int));
+void tcp_dooptions __P((struct tcpcb *,
+ u_char *, int, struct tcpiphdr *, int *, u_long *, u_long *));
+void tcp_drain __P((void));
+void tcp_fasttimo __P((void));
+void tcp_init __P((void));
+void tcp_input __P((struct mbuf *, int));
+int tcp_mss __P((struct tcpcb *, u_int));
+struct tcpcb *
+ tcp_newtcpcb __P((struct inpcb *));
+void tcp_notify __P((struct inpcb *, int));
+int tcp_output __P((struct tcpcb *));
+void tcp_pulloutofband __P((struct socket *,
+ struct tcpiphdr *, struct mbuf *));
+void tcp_quench __P((struct inpcb *, int));
+int tcp_reass __P((struct tcpcb *, struct tcpiphdr *, struct mbuf *));
+void tcp_respond __P((struct tcpcb *,
+ struct tcpiphdr *, struct mbuf *, u_long, u_long, int));
+void tcp_setpersist __P((struct tcpcb *));
+void tcp_slowtimo __P((void));
+struct tcpiphdr *
+ tcp_template __P((struct tcpcb *));
+struct tcpcb *
+ tcp_timers __P((struct tcpcb *, int));
+void tcp_trace __P((int, int, struct tcpcb *, struct tcpiphdr *, int));
+struct tcpcb *
+ tcp_usrclosed __P((struct tcpcb *));
+int tcp_usrreq __P((struct socket *,
+ int, struct mbuf *, struct mbuf *, struct mbuf *));
+void tcp_xmit_timer __P((struct tcpcb *, int));
+#endif
diff --git a/sys/netinet/tcpip.h b/sys/netinet/tcpip.h
new file mode 100644
index 0000000..5000ae3
--- /dev/null
+++ b/sys/netinet/tcpip.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcpip.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Tcp+ip header, after ip options removed.
+ */
+struct tcpiphdr {
+ struct ipovly ti_i; /* overlaid ip structure */
+ struct tcphdr ti_t; /* tcp header */
+};
+#define ti_next ti_i.ih_next
+#define ti_prev ti_i.ih_prev
+#define ti_x1 ti_i.ih_x1
+#define ti_pr ti_i.ih_pr
+#define ti_len ti_i.ih_len
+#define ti_src ti_i.ih_src
+#define ti_dst ti_i.ih_dst
+#define ti_sport ti_t.th_sport
+#define ti_dport ti_t.th_dport
+#define ti_seq ti_t.th_seq
+#define ti_ack ti_t.th_ack
+#define ti_x2 ti_t.th_x2
+#define ti_off ti_t.th_off
+#define ti_flags ti_t.th_flags
+#define ti_win ti_t.th_win
+#define ti_sum ti_t.th_sum
+#define ti_urp ti_t.th_urp
diff --git a/sys/netinet/udp.h b/sys/netinet/udp.h
new file mode 100644
index 0000000..354a213
--- /dev/null
+++ b/sys/netinet/udp.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)udp.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Udp protocol header.
+ * Per RFC 768, September, 1981.
+ */
+struct udphdr {
+ u_short uh_sport; /* source port */
+ u_short uh_dport; /* destination port */
+ short uh_ulen; /* udp length */
+ u_short uh_sum; /* udp checksum */
+};
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
new file mode 100644
index 0000000..9ed53e8
--- /dev/null
+++ b/sys/netinet/udp_usrreq.c
@@ -0,0 +1,642 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)udp_usrreq.c 8.4 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+/*
+ * UDP protocol implementation.
+ * Per RFC 768, August, 1980.
+ */
+#ifndef COMPAT_42
+int udpcksum = 1;
+#else
+int udpcksum = 0; /* XXX */
+#endif
+
+struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET };
+struct inpcb *udp_last_inpcb = &udb;
+
+static void udp_detach __P((struct inpcb *));
+static void udp_notify __P((struct inpcb *, int));
+static struct mbuf *udp_saveopt __P((caddr_t, int, int));
+
+void
+udp_init()
+{
+ udb.inp_next = udb.inp_prev = &udb;
+}
+
+void
+udp_input(m, iphlen)
+ register struct mbuf *m;
+ int iphlen;
+{
+ register struct ip *ip;
+ register struct udphdr *uh;
+ register struct inpcb *inp;
+ struct mbuf *opts = 0;
+ int len;
+ struct ip save_ip;
+
+ udpstat.udps_ipackets++;
+
+ /*
+ * Strip IP options, if any; should skip this,
+ * make available to user, and use on returned packets,
+ * but we don't yet have a way to check the checksum
+ * with options still present.
+ */
+ if (iphlen > sizeof (struct ip)) {
+ ip_stripoptions(m, (struct mbuf *)0);
+ iphlen = sizeof(struct ip);
+ }
+
+ /*
+ * Get IP and UDP header together in first mbuf.
+ */
+ ip = mtod(m, struct ip *);
+ if (m->m_len < iphlen + sizeof(struct udphdr)) {
+ if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) {
+ udpstat.udps_hdrops++;
+ return;
+ }
+ ip = mtod(m, struct ip *);
+ }
+ uh = (struct udphdr *)((caddr_t)ip + iphlen);
+
+ /*
+ * Make mbuf data length reflect UDP length.
+ * If not enough data to reflect UDP length, drop.
+ */
+ len = ntohs((u_short)uh->uh_ulen);
+ if (ip->ip_len != len) {
+ if (len > ip->ip_len) {
+ udpstat.udps_badlen++;
+ goto bad;
+ }
+ m_adj(m, len - ip->ip_len);
+ /* ip->ip_len = len; */
+ }
+ /*
+ * Save a copy of the IP header in case we want restore it
+ * for sending an ICMP error message in response.
+ */
+ save_ip = *ip;
+
+ /*
+ * Checksum extended UDP header and data.
+ */
+ if (udpcksum && uh->uh_sum) {
+ ((struct ipovly *)ip)->ih_next = 0;
+ ((struct ipovly *)ip)->ih_prev = 0;
+ ((struct ipovly *)ip)->ih_x1 = 0;
+ ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
+ if (uh->uh_sum = in_cksum(m, len + sizeof (struct ip))) {
+ udpstat.udps_badsum++;
+ m_freem(m);
+ return;
+ }
+ }
+
+ if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
+ in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) {
+ struct socket *last;
+ /*
+ * Deliver a multicast or broadcast datagram to *all* sockets
+ * for which the local and remote addresses and ports match
+ * those of the incoming datagram. This allows more than
+ * one process to receive multi/broadcasts on the same port.
+ * (This really ought to be done for unicast datagrams as
+ * well, but that would cause problems with existing
+ * applications that open both address-specific sockets and
+ * a wildcard socket listening to the same port -- they would
+ * end up receiving duplicates of every unicast datagram.
+ * Those applications open the multiple sockets to overcome an
+ * inadequacy of the UDP socket interface, but for backwards
+ * compatibility we avoid the problem here rather than
+ * fixing the interface. Maybe 4.5BSD will remedy this?)
+ */
+
+ /*
+ * Construct sockaddr format source address.
+ */
+ udp_in.sin_port = uh->uh_sport;
+ udp_in.sin_addr = ip->ip_src;
+ m->m_len -= sizeof (struct udpiphdr);
+ m->m_data += sizeof (struct udpiphdr);
+ /*
+ * Locate pcb(s) for datagram.
+ * (Algorithm copied from raw_intr().)
+ */
+ last = NULL;
+ for (inp = udb.inp_next; inp != &udb; inp = inp->inp_next) {
+ if (inp->inp_lport != uh->uh_dport)
+ continue;
+ if (inp->inp_laddr.s_addr != INADDR_ANY) {
+ if (inp->inp_laddr.s_addr !=
+ ip->ip_dst.s_addr)
+ continue;
+ }
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ if (inp->inp_faddr.s_addr !=
+ ip->ip_src.s_addr ||
+ inp->inp_fport != uh->uh_sport)
+ continue;
+ }
+
+ if (last != NULL) {
+ struct mbuf *n;
+
+ if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
+ if (sbappendaddr(&last->so_rcv,
+ (struct sockaddr *)&udp_in,
+ n, (struct mbuf *)0) == 0) {
+ m_freem(n);
+ udpstat.udps_fullsock++;
+ } else
+ sorwakeup(last);
+ }
+ }
+ last = inp->inp_socket;
+ /*
+ * Don't look for additional matches if this one does
+ * not have either the SO_REUSEPORT or SO_REUSEADDR
+ * socket options set. This heuristic avoids searching
+ * through all pcbs in the common case of a non-shared
+ * port. It * assumes that an application will never
+ * clear these options after setting them.
+ */
+ if ((last->so_options&(SO_REUSEPORT|SO_REUSEADDR) == 0))
+ break;
+ }
+
+ if (last == NULL) {
+ /*
+ * No matching pcb found; discard datagram.
+ * (No need to send an ICMP Port Unreachable
+ * for a broadcast or multicast datgram.)
+ */
+ udpstat.udps_noportbcast++;
+ goto bad;
+ }
+ if (sbappendaddr(&last->so_rcv, (struct sockaddr *)&udp_in,
+ m, (struct mbuf *)0) == 0) {
+ udpstat.udps_fullsock++;
+ goto bad;
+ }
+ sorwakeup(last);
+ return;
+ }
+ /*
+ * Locate pcb for datagram.
+ */
+ inp = udp_last_inpcb;
+ if (inp->inp_lport != uh->uh_dport ||
+ inp->inp_fport != uh->uh_sport ||
+ inp->inp_faddr.s_addr != ip->ip_src.s_addr ||
+ inp->inp_laddr.s_addr != ip->ip_dst.s_addr) {
+ inp = in_pcblookup(&udb, ip->ip_src, uh->uh_sport,
+ ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD);
+ if (inp)
+ udp_last_inpcb = inp;
+ udpstat.udpps_pcbcachemiss++;
+ }
+ if (inp == 0) {
+ udpstat.udps_noport++;
+ if (m->m_flags & (M_BCAST | M_MCAST)) {
+ udpstat.udps_noportbcast++;
+ goto bad;
+ }
+ *ip = save_ip;
+ ip->ip_len += iphlen;
+ icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0);
+ return;
+ }
+
+ /*
+ * Construct sockaddr format source address.
+ * Stuff source address and datagram in user buffer.
+ */
+ udp_in.sin_port = uh->uh_sport;
+ udp_in.sin_addr = ip->ip_src;
+ if (inp->inp_flags & INP_CONTROLOPTS) {
+ struct mbuf **mp = &opts;
+
+ if (inp->inp_flags & INP_RECVDSTADDR) {
+ *mp = udp_saveopt((caddr_t) &ip->ip_dst,
+ sizeof(struct in_addr), IP_RECVDSTADDR);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+#ifdef notyet
+ /* options were tossed above */
+ if (inp->inp_flags & INP_RECVOPTS) {
+ *mp = udp_saveopt((caddr_t) opts_deleted_above,
+ sizeof(struct in_addr), IP_RECVOPTS);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+ /* ip_srcroute doesn't do what we want here, need to fix */
+ if (inp->inp_flags & INP_RECVRETOPTS) {
+ *mp = udp_saveopt((caddr_t) ip_srcroute(),
+ sizeof(struct in_addr), IP_RECVRETOPTS);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+#endif
+ }
+ iphlen += sizeof(struct udphdr);
+ m->m_len -= iphlen;
+ m->m_pkthdr.len -= iphlen;
+ m->m_data += iphlen;
+ if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
+ m, opts) == 0) {
+ udpstat.udps_fullsock++;
+ goto bad;
+ }
+ sorwakeup(inp->inp_socket);
+ return;
+bad:
+ m_freem(m);
+ if (opts)
+ m_freem(opts);
+}
+
+/*
+ * Create a "control" mbuf containing the specified data
+ * with the specified type for presentation with a datagram.
+ */
+struct mbuf *
+udp_saveopt(p, size, type)
+ caddr_t p;
+ register int size;
+ int type;
+{
+ register struct cmsghdr *cp;
+ struct mbuf *m;
+
+ if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL)
+ return ((struct mbuf *) NULL);
+ cp = (struct cmsghdr *) mtod(m, struct cmsghdr *);
+ bcopy(p, CMSG_DATA(cp), size);
+ size += sizeof(*cp);
+ m->m_len = size;
+ cp->cmsg_len = size;
+ cp->cmsg_level = IPPROTO_IP;
+ cp->cmsg_type = type;
+ return (m);
+}
+
+/*
+ * Notify a udp user of an asynchronous error;
+ * just wake up so that he can collect error status.
+ */
+static void
+udp_notify(inp, errno)
+ register struct inpcb *inp;
+ int errno;
+{
+ inp->inp_socket->so_error = errno;
+ sorwakeup(inp->inp_socket);
+ sowwakeup(inp->inp_socket);
+}
+
+void
+udp_ctlinput(cmd, sa, ip)
+ int cmd;
+ struct sockaddr *sa;
+ register struct ip *ip;
+{
+ register struct udphdr *uh;
+ extern struct in_addr zeroin_addr;
+ extern u_char inetctlerrmap[];
+
+ if (!PRC_IS_REDIRECT(cmd) &&
+ ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0))
+ return;
+ if (ip) {
+ uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2));
+ in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport,
+ cmd, udp_notify);
+ } else
+ in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify);
+}
+
+int
+udp_output(inp, m, addr, control)
+ register struct inpcb *inp;
+ register struct mbuf *m;
+ struct mbuf *addr, *control;
+{
+ register struct udpiphdr *ui;
+ register int len = m->m_pkthdr.len;
+ struct in_addr laddr;
+ int s = 0, error = 0;
+
+ if (control)
+ m_freem(control); /* XXX */
+
+ if (addr) {
+ laddr = inp->inp_laddr;
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ error = EISCONN;
+ goto release;
+ }
+ /*
+ * Must block input while temporarily connected.
+ */
+ s = splnet();
+ error = in_pcbconnect(inp, addr);
+ if (error) {
+ splx(s);
+ goto release;
+ }
+ } else {
+ if (inp->inp_faddr.s_addr == INADDR_ANY) {
+ error = ENOTCONN;
+ goto release;
+ }
+ }
+ /*
+ * Calculate data length and get a mbuf
+ * for UDP and IP headers.
+ */
+ M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);
+ if (m == 0) {
+ error = ENOBUFS;
+ goto release;
+ }
+
+ /*
+ * Fill in mbuf with extended UDP header
+ * and addresses and length put into network format.
+ */
+ ui = mtod(m, struct udpiphdr *);
+ ui->ui_next = ui->ui_prev = 0;
+ ui->ui_x1 = 0;
+ ui->ui_pr = IPPROTO_UDP;
+ ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
+ ui->ui_src = inp->inp_laddr;
+ ui->ui_dst = inp->inp_faddr;
+ ui->ui_sport = inp->inp_lport;
+ ui->ui_dport = inp->inp_fport;
+ ui->ui_ulen = ui->ui_len;
+
+ /*
+ * Stuff checksum and output datagram.
+ */
+ ui->ui_sum = 0;
+ if (udpcksum) {
+ if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
+ ui->ui_sum = 0xffff;
+ }
+ ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
+ ((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl; /* XXX */
+ ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */
+ udpstat.udps_opackets++;
+ error = ip_output(m, inp->inp_options, &inp->inp_route,
+ inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST),
+ inp->inp_moptions);
+
+ if (addr) {
+ in_pcbdisconnect(inp);
+ inp->inp_laddr = laddr;
+ splx(s);
+ }
+ return (error);
+
+release:
+ m_freem(m);
+ return (error);
+}
+
+u_long udp_sendspace = 9216; /* really max datagram size */
+u_long udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in));
+ /* 40 1K datagrams */
+
+/*ARGSUSED*/
+int
+udp_usrreq(so, req, m, addr, control)
+ struct socket *so;
+ int req;
+ struct mbuf *m, *addr, *control;
+{
+ struct inpcb *inp = sotoinpcb(so);
+ int error = 0;
+ int s;
+
+ if (req == PRU_CONTROL)
+ return (in_control(so, (int)m, (caddr_t)addr,
+ (struct ifnet *)control));
+ if (inp == NULL && req != PRU_ATTACH) {
+ error = EINVAL;
+ goto release;
+ }
+ /*
+ * Note: need to block udp_input while changing
+ * the udp pcb queue and/or pcb addresses.
+ */
+ switch (req) {
+
+ case PRU_ATTACH:
+ if (inp != NULL) {
+ error = EINVAL;
+ break;
+ }
+ s = splnet();
+ error = in_pcballoc(so, &udb);
+ splx(s);
+ if (error)
+ break;
+ error = soreserve(so, udp_sendspace, udp_recvspace);
+ if (error)
+ break;
+ ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = ip_defttl;
+ break;
+
+ case PRU_DETACH:
+ udp_detach(inp);
+ break;
+
+ case PRU_BIND:
+ s = splnet();
+ error = in_pcbbind(inp, addr);
+ splx(s);
+ break;
+
+ case PRU_LISTEN:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_CONNECT:
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ error = EISCONN;
+ break;
+ }
+ s = splnet();
+ error = in_pcbconnect(inp, addr);
+ splx(s);
+ if (error == 0)
+ soisconnected(so);
+ break;
+
+ case PRU_CONNECT2:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_ACCEPT:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_DISCONNECT:
+ if (inp->inp_faddr.s_addr == INADDR_ANY) {
+ error = ENOTCONN;
+ break;
+ }
+ s = splnet();
+ in_pcbdisconnect(inp);
+ inp->inp_laddr.s_addr = INADDR_ANY;
+ splx(s);
+ so->so_state &= ~SS_ISCONNECTED; /* XXX */
+ break;
+
+ case PRU_SHUTDOWN:
+ socantsendmore(so);
+ break;
+
+ case PRU_SEND:
+ return (udp_output(inp, m, addr, control));
+
+ case PRU_ABORT:
+ soisdisconnected(so);
+ udp_detach(inp);
+ break;
+
+ case PRU_SOCKADDR:
+ in_setsockaddr(inp, addr);
+ break;
+
+ case PRU_PEERADDR:
+ in_setpeeraddr(inp, addr);
+ break;
+
+ case PRU_SENSE:
+ /*
+ * stat: don't bother with a blocksize.
+ */
+ return (0);
+
+ case PRU_SENDOOB:
+ case PRU_FASTTIMO:
+ case PRU_SLOWTIMO:
+ case PRU_PROTORCV:
+ case PRU_PROTOSEND:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_RCVD:
+ case PRU_RCVOOB:
+ return (EOPNOTSUPP); /* do not free mbuf's */
+
+ default:
+ panic("udp_usrreq");
+ }
+
+release:
+ if (control) {
+ printf("udp control data unexpectedly retained\n");
+ m_freem(control);
+ }
+ if (m)
+ m_freem(m);
+ return (error);
+}
+
+static void
+udp_detach(inp)
+ struct inpcb *inp;
+{
+ int s = splnet();
+
+ if (inp == udp_last_inpcb)
+ udp_last_inpcb = &udb;
+ in_pcbdetach(inp);
+ splx(s);
+}
+
+/*
+ * Sysctl for udp variables.
+ */
+int
+udp_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+{
+ /* All sysctl names at this level are terminal. */
+ if (namelen != 1)
+ return (ENOTDIR);
+
+ switch (name[0]) {
+ case UDPCTL_CHECKSUM:
+ return (sysctl_int(oldp, oldlenp, newp, newlen, &udpcksum));
+ default:
+ return (ENOPROTOOPT);
+ }
+ /* NOTREACHED */
+}
diff --git a/sys/netinet/udp_var.h b/sys/netinet/udp_var.h
new file mode 100644
index 0000000..e8a21d2
--- /dev/null
+++ b/sys/netinet/udp_var.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)udp_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * UDP kernel structures and variables.
+ */
+struct udpiphdr {
+ struct ipovly ui_i; /* overlaid ip structure */
+ struct udphdr ui_u; /* udp header */
+};
+#define ui_next ui_i.ih_next
+#define ui_prev ui_i.ih_prev
+#define ui_x1 ui_i.ih_x1
+#define ui_pr ui_i.ih_pr
+#define ui_len ui_i.ih_len
+#define ui_src ui_i.ih_src
+#define ui_dst ui_i.ih_dst
+#define ui_sport ui_u.uh_sport
+#define ui_dport ui_u.uh_dport
+#define ui_ulen ui_u.uh_ulen
+#define ui_sum ui_u.uh_sum
+
+struct udpstat {
+ /* input statistics: */
+ u_long udps_ipackets; /* total input packets */
+ u_long udps_hdrops; /* packet shorter than header */
+ u_long udps_badsum; /* checksum error */
+ u_long udps_badlen; /* data length larger than packet */
+ u_long udps_noport; /* no socket on port */
+ u_long udps_noportbcast; /* of above, arrived as broadcast */
+ u_long udps_fullsock; /* not delivered, input socket full */
+ u_long udpps_pcbcachemiss; /* input packets missing pcb cache */
+ /* output statistics: */
+ u_long udps_opackets; /* total output packets */
+};
+
+/*
+ * Names for UDP sysctl objects
+ */
+#define UDPCTL_CHECKSUM 1 /* checksum UDP packets */
+#define UDPCTL_MAXID 2
+
+#define UDPCTL_NAMES { \
+ { 0, 0 }, \
+ { "checksum", CTLTYPE_INT }, \
+}
+
+#ifdef KERNEL
+struct inpcb udb;
+struct udpstat udpstat;
+
+void udp_ctlinput __P((int, struct sockaddr *, struct ip *));
+void udp_init __P((void));
+void udp_input __P((struct mbuf *, int));
+int udp_output __P((struct inpcb *,
+ struct mbuf *, struct mbuf *, struct mbuf *));
+int udp_sysctl __P((int *, u_int, void *, size_t *, void *, size_t));
+int udp_usrreq __P((struct socket *,
+ int, struct mbuf *, struct mbuf *, struct mbuf *));
+#endif
diff --git a/sys/netiso/argo_debug.h b/sys/netiso/argo_debug.h
new file mode 100644
index 0000000..653982f
--- /dev/null
+++ b/sys/netiso/argo_debug.h
@@ -0,0 +1,296 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)argo_debug.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*****************************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * $Header: argo_debug.h,v 4.6 88/07/19 15:53:40 hagens Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/argo_debug.h,v $
+ */
+
+#ifndef __ARGO_DEBUG__
+#define __ARGO_DEBUG__
+
+#define dump_buf(a, b) Dump_buf((caddr_t)(a), (int)(b))
+
+/***********************************************
+ * Lint stuff
+ **********************************************/
+#if defined(lint)
+/*
+ * lint can't handle the flaky vacuous definitions
+ * of IFDEBUG, ENDDEBUG, etc.
+ */
+#endif /* defined(lint) */
+
+/***********************************************
+ * DEBUG ON:
+ **********************************************/
+#ifndef ARGO_DEBUG
+#define ARGO_DEBUG
+#endif /* ARGO_DEBUG */
+
+
+#ifdef ARGO_DEBUG
+/*
+ #ifndef TPPT
+ #define TPPT
+ #endif TPPT
+
+ #ifndef TP_PERF_MEAS
+ #define TP_PERF_MEAS
+ #endif TP_PERF_MEAS
+*/
+
+unsigned char argo_debug[128];
+
+#define IFDEBUG(ascii) \
+ if(argo_debug[ascii]) {
+#define ENDDEBUG ; }
+
+#else /* ARGO_DEBUG */
+
+/***********************************************
+ * DEBUG OFF:
+ **********************************************/
+
+#ifndef STAR
+#define STAR *
+#endif /* STAR */
+#define IFDEBUG(ascii) //*beginning of comment*/STAR
+#define ENDDEBUG STAR/*end of comment*//
+
+#endif /* ARGO_DEBUG */
+
+/***********************************************
+ * ASSERT
+ **********************************************/
+#ifdef ARGO_DEBUG
+
+#ifndef lint
+#define ASSERT(phrase) \
+if( !(phrase) ) printf("ASSERTION NOT VALID at line %d file %s\n",__LINE__,__FILE__)
+#else /* lint */
+#define ASSERT(phrase) /* phrase */
+#endif /* lint */
+
+#else /* ARGO_DEBUG */
+
+#define ASSERT(phrase) /* phrase */
+
+#endif /* ARGO_DEBUG */
+
+
+/***********************************************
+ * CLNP DEBUG OPTIONS
+ **********************************************/
+#define D_INPUT '\1'
+/* clnp input */
+#define D_OUTPUT '\2'
+/* clnp output */
+#define D_ROUTE '\3'
+/* clnp routing */
+#define D_CTLINPUT '\4'
+/* clnp control input */
+#define D_CTLOUTPUT '\5'
+/* clnp control output */
+#define D_OPTIONS '\6'
+/* clnp options */
+#define D_IOCTL '\7'
+/* iso ioctls */
+#define D_ETHER '\10'
+/* clnp over ethernet */
+#define D_TOKEN '\11'
+/* clnp over token ring */
+#define D_ADCOM '\12'
+/* clnp over the adcom */
+#define D_ISO '\13'
+/* iso address family */
+#define D_FORWARD '\14'
+/* clnp forwarding */
+#define D_DUMPOUT '\15'
+/* dump clnp outgoing packets */
+#define D_DUMPIN '\16'
+/* dump clnp input packets */
+#define D_DISCARD '\17'
+/* debug clnp packet discard/er function */
+#define D_FRAG '\20'
+/* clnp fragmentation */
+#define D_REASS '\21'
+/* clnp reassembly */
+
+char *clnp_iso_addrp();
+
+/***********************************************
+ * ESIS DEBUG OPTIONS
+ **********************************************/
+#define D_ESISOUTPUT '\30'
+#define D_ESISINPUT '\31'
+#define D_SNPA '\32'
+
+/***********************************************
+ * ISIS DEBUG OPTIONS
+ **********************************************/
+#define D_ISISOUTPUT '\40'
+#define D_ISISINPUT '\41'
+
+/***********************************************
+ * EON DEBUG OPTION
+ **********************************************/
+#define D_EON '\57'
+
+/***********************************************
+ * CONS DEBUG OPTIONS
+ **********************************************/
+
+#define D_ECNWORK '\60'
+#define D_ECNOUT '\61'
+#define D_ECNFIN '\62'
+#define D_ECNDWN '\63'
+#define D_ECNUTIL '\64'
+
+#define D_INCOMING '\70'
+#define D_CDATA '\71'
+#define D_CFIND '\72'
+#define D_CDUMP_REQ '\73'
+#define D_CADDR '\74'
+#define D_CCONS '\75'
+#define D_CCONN '\76'
+
+
+/***********************************************
+ * TP DEBUG OPTIONS
+ **********************************************/
+
+#define D_SETPARAMS '\137'
+#define D_RTT '\140'
+
+#define D_ACKRECV '\141'
+#define D_ACKSEND '\142'
+#define D_CONN '\143'
+#define D_CREDIT '\144'
+#define D_DATA '\145'
+#define D_DRIVER '\146'
+
+#define D_EMIT '\147'
+#define D_ERROR_EMIT '\150'
+#define D_TPINPUT '\151'
+#define D_INDICATION '\152'
+#define D_CHKSUM '\153'
+
+#define D_RENEG '\154'
+#define D_PERF_MEAS '\155'
+#define D_MBUF_MEAS '\156'
+#define D_RTC '\157'
+#define D_SB '\160'
+
+#define D_DISASTER_CHECK '\161'
+#define D_REQUEST '\162'
+#define D_STASH '\163'
+#define D_NEWSOCK '\164'
+#define D_TIMER '\165'
+
+#define D_TPIOCTL '\166'
+#define D_SIZE_CHECK '\167'
+#define D_2ER '\170'
+#define D_DISASTER_CHECK_W '\171'
+
+#define D_XPD '\172'
+#define D_SYSCALL '\173'
+#define D_DROP '\174'
+#define D_ZDREF '\175'
+#define D_TPISO '\176'
+#define D_QUENCH '\177'
+
+void dump_mbuf();
+
+/***********************************************
+ * New mbuf types for debugging w/ netstat -m
+ * This messes up 4.4 malloc for now. need bigger
+ * mbtypes array for now.
+ **********************************************/
+#ifdef notdef
+
+#define TPMT_DATA 0x21
+#define TPMT_RCVRTC 0x42
+#define TPMT_SNDRTC 0x41
+#define TPMT_TPHDR 0x22
+#define TPMT_IPHDR 0x32
+#define TPMT_SONAME 0x28
+#define TPMT_EOT 0x40
+#define TPMT_XPD 0x44
+#define TPMT_PCB 0x23
+#define TPMT_PERF 0x45
+
+#else /* ARGO_DEBUG */
+
+#define TPMT_DATA MT_DATA
+#define TPMT_RCVRTC MT_DATA
+#define TPMT_SNDRTC MT_DATA
+#define TPMT_IPHDR MT_HEADER
+#define TPMT_TPHDR MT_HEADER
+#define TPMT_SONAME MT_SONAME
+/* MT_EOT and MT_XPD are defined in tp_param.h */
+#define TPMT_XPD MT_OOBDATA
+#define TPMT_PCB MT_PCB
+#define TPMT_PERF MT_PCB
+
+#endif /* ARGO_DEBUG */
+
+#endif /* __ARGO_DEBUG__ */
diff --git a/sys/netiso/clnl.h b/sys/netiso/clnl.h
new file mode 100644
index 0000000..87227dc
--- /dev/null
+++ b/sys/netiso/clnl.h
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clnl.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+struct clnl_protosw {
+ int (*clnl_input)(); /* input routine */
+};
diff --git a/sys/netiso/clnp.h b/sys/netiso/clnp.h
new file mode 100644
index 0000000..4c81ba3
--- /dev/null
+++ b/sys/netiso/clnp.h
@@ -0,0 +1,463 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clnp.h 8.2 (Berkeley) 4/16/94
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: /big/BSD4.4/isis-usr/src/sys/netiso/RCS/clnp.h,v 1.1 1992/02/07 18:14:59 hagens Exp hagens $ */
+/* $Source: /big/BSD4.4/isis-usr/src/sys/netiso/RCS/clnp.h,v $ */
+
+/* should be config option but cpp breaks with too many #defines */
+#define DECBIT
+
+/*
+ * Return true if the mbuf is a cluster mbuf
+ */
+#define IS_CLUSTER(m) ((m)->m_flags & M_EXT)
+
+/*
+ * Move the halfword into the two characters
+ */
+#define HTOC(msb, lsb, hword)\
+ (msb) = (u_char)((hword) >> 8);\
+ (lsb) = (u_char)((hword) & 0xff)
+/*
+ * Move the two charcters into the halfword
+ */
+#define CTOH(msb, lsb, hword)\
+ (hword) = ((msb) << 8) | (lsb)
+
+/*
+ * Return true if the checksum has been set - ie. the checksum is
+ * not zero
+ */
+#define CKSUM_REQUIRED(clnp)\
+ (((clnp)->cnf_cksum_msb != 0) || ((clnp)->cnf_cksum_lsb != 0))
+
+/*
+ * Fixed part of clnp header
+ */
+struct clnp_fixed {
+ u_char cnf_proto_id; /* network layer protocol identifier */
+ u_char cnf_hdr_len; /* length indicator (octets) */
+ u_char cnf_vers; /* version/protocol identifier extension */
+ u_char cnf_ttl; /* lifetime (500 milliseconds) */
+ u_char cnf_type; /* type code */
+ /* Includes err_ok, more_segs, and seg_ok */
+ u_char cnf_seglen_msb; /* pdu segment length (octets) high byte */
+ u_char cnf_seglen_lsb; /* pdu segment length (octets) low byte */
+ u_char cnf_cksum_msb; /* checksum high byte */
+ u_char cnf_cksum_lsb; /* checksum low byte */
+};
+#define CNF_TYPE 0x1f
+#define CNF_ERR_OK 0x20
+#define CNF_MORE_SEGS 0x40
+#define CNF_SEG_OK 0x80
+
+#define CLNP_CKSUM_OFF 0x07 /* offset of checksum */
+
+#define clnl_fixed clnp_fixed
+
+/*
+ * Segmentation part of clnp header
+ */
+struct clnp_segment {
+ u_short cng_id; /* data unit identifier */
+ u_short cng_off; /* segment offset */
+ u_short cng_tot_len; /* total length */
+};
+
+/*
+ * Clnp fragment reassembly structures:
+ *
+ * All packets undergoing reassembly are linked together in
+ * clnp_fragl structures. Each clnp_fragl structure contains a
+ * pointer to the original clnp packet header, as well as a
+ * list of packet fragments. Each packet fragment
+ * is headed by a clnp_frag structure. This structure contains the
+ * offset of the first and last byte of the fragment, as well as
+ * a pointer to the data (an mbuf chain) of the fragment.
+ */
+
+/*
+ * NOTE:
+ * The clnp_frag structure is stored in an mbuf immedately preceeding
+ * the fragment data. Since there are words in this struct,
+ * it must be word aligned.
+ *
+ * NOTE:
+ * All the fragment code assumes that the entire clnp header is
+ * contained in the first mbuf.
+ */
+struct clnp_frag {
+ u_int cfr_first; /* offset of first byte of this frag */
+ u_int cfr_last; /* offset of last byte of this frag */
+ u_int cfr_bytes; /* bytes to shave to get to data */
+ struct mbuf *cfr_data; /* ptr to data for this frag */
+ struct clnp_frag *cfr_next; /* next fragment in list */
+};
+
+struct clnp_fragl {
+ struct iso_addr cfl_src; /* source of the pkt */
+ struct iso_addr cfl_dst; /* destination of the pkt */
+ u_short cfl_id; /* id of the pkt */
+ u_char cfl_ttl; /* current ttl of pkt */
+ u_short cfl_last; /* offset of last byte of packet */
+ struct mbuf *cfl_orighdr; /* ptr to original header */
+ struct clnp_frag *cfl_frags; /* linked list of fragments for pkt */
+ struct clnp_fragl *cfl_next; /* next pkt being reassembled */
+};
+
+/*
+ * The following structure is used to index into an options section
+ * of a clnp datagram. These values can be used without worry that
+ * offset or length fields are invalid or too big, etc. That is,
+ * the consistancy of the options will be guaranteed before this
+ * structure is filled in. Any pointer (field ending in p) is
+ * actually the offset from the beginning of the mbuf the option
+ * is contained in. A value of NULL for any pointer
+ * means that the option is not present. The length any option
+ * does not include the option code or option length fields.
+ */
+struct clnp_optidx {
+ u_short cni_securep; /* ptr to beginning of security option */
+ char cni_secure_len; /* length of entire security option */
+
+ u_short cni_srcrt_s; /* offset of start of src rt option */
+ u_short cni_srcrt_len; /* length of entire src rt option */
+
+ u_short cni_recrtp; /* ptr to beginning of recrt option */
+ char cni_recrt_len; /* length of entire recrt option */
+
+ char cni_priorp; /* ptr to priority option */
+
+ u_short cni_qos_formatp; /* ptr to format of qos option */
+ char cni_qos_len; /* length of entire qos option */
+
+ u_char cni_er_reason; /* reason from ER pdu option */
+
+ /* ESIS options */
+
+ u_short cni_esct; /* value from ISH ESCT option */
+
+ u_short cni_netmaskp; /* ptr to beginning of netmask option */
+ char cni_netmask_len; /* length of entire netmask option */
+
+ u_short cni_snpamaskp; /* ptr to beginning of snpamask option */
+ char cni_snpamask_len; /* length of entire snpamask option */
+
+};
+
+#define ER_INVALREAS 0xff /* code for invalid ER pdu discard reason */
+
+/* given an mbuf and addr of option, return offset from data of mbuf */
+#define CLNP_OPTTOOFF(m, opt)\
+ ((u_short) (opt - mtod(m, caddr_t)))
+
+/* given an mbuf and offset of option, return address of option */
+#define CLNP_OFFTOOPT(m, off)\
+ ((caddr_t) (mtod(m, caddr_t) + off))
+
+/* return true iff src route is valid */
+#define CLNPSRCRT_VALID(oidx)\
+ ((oidx) && (oidx->cni_srcrt_s))
+
+/* return the offset field of the src rt */
+#define CLNPSRCRT_OFF(oidx, options)\
+ (*((u_char *)(CLNP_OFFTOOPT(options, oidx->cni_srcrt_s) + 1)))
+
+/* return the type field of the src rt */
+#define CLNPSRCRT_TYPE(oidx, options)\
+ ((u_char)(*(CLNP_OFFTOOPT(options, oidx->cni_srcrt_s))))
+
+/* return the length of the current address */
+#define CLNPSRCRT_CLEN(oidx, options)\
+ ((u_char)(*(CLNP_OFFTOOPT(options, oidx->cni_srcrt_s) + CLNPSRCRT_OFF(oidx, options) - 1)))
+
+/* return the address of the current address */
+#define CLNPSRCRT_CADDR(oidx, options)\
+ ((caddr_t)(CLNP_OFFTOOPT(options, oidx->cni_srcrt_s) + CLNPSRCRT_OFF(oidx, options)))
+
+/*
+ * return true if the src route has run out of routes
+ * this is true if the offset of next route is greater than the end of the rt
+ */
+#define CLNPSRCRT_TERM(oidx, options)\
+ (CLNPSRCRT_OFF(oidx, options) > oidx->cni_srcrt_len)
+
+/*
+ * Options a user can set/get
+ */
+#define CLNPOPT_FLAGS 0x01 /* flags: seg permitted, no er xmit, etc */
+#define CLNPOPT_OPTS 0x02 /* datagram options */
+
+/*
+ * Values for particular datagram options
+ */
+#define CLNPOVAL_PAD 0xcc /* padding */
+#define CLNPOVAL_SECURE 0xc5 /* security */
+#define CLNPOVAL_SRCRT 0xc8 /* source routing */
+#define CLNPOVAL_RECRT 0xcb /* record route */
+#define CLNPOVAL_QOS 0xc3 /* quality of service */
+#define CLNPOVAL_PRIOR 0xcd /* priority */
+#define CLNPOVAL_ERREAS 0xc1 /* ER PDU ONLY: reason for discard */
+
+#define CLNPOVAL_SRCSPEC 0x40 /* source address specific */
+#define CLNPOVAL_DSTSPEC 0x80 /* destination address specific */
+#define CLNPOVAL_GLOBAL 0xc0 /* globally unique */
+
+/* Globally Unique QOS */
+#define CLNPOVAL_SEQUENCING 0x10 /* sequencing preferred */
+#define CLNPOVAL_CONGESTED 0x08 /* congestion experienced */
+#define CLNPOVAL_LOWDELAY 0x04 /* low transit delay */
+
+#define CLNPOVAL_PARTRT 0x00 /* partial source routing */
+#define CLNPOVAL_COMPRT 0x01 /* complete source routing */
+
+/*
+ * Clnp flags used in a control block flags field.
+ * NOTE: these must be out of the range of bits defined in ../net/raw_cb.h
+ */
+#define CLNP_NO_SEG 0x010 /* segmentation not permitted */
+#define CLNP_NO_ER 0x020 /* do not generate ERs */
+#define CLNP_SEND_RAW 0x080 /* send pkt as RAW DT rather than TP DT */
+#define CLNP_NO_CKSUM 0x100 /* don't use clnp checksum */
+#define CLNP_ECHO 0x200 /* send echo request */
+#define CLNP_NOCACHE 0x400 /* don't store cache information */
+#define CLNP_ECHOR 0x800 /* send echo reply */
+
+/* valid clnp flags */
+#define CLNP_VFLAGS (CLNP_SEND_RAW|CLNP_NO_SEG|CLNP_NO_ER|CLNP_NO_CKSUM\
+ |CLNP_ECHO|CLNP_NOCACHE|CLNP_ECHOR)
+
+/*
+ * Constants used by clnp
+ */
+#define CLNP_HDR_MIN (sizeof (struct clnp_fixed))
+#define CLNP_HDR_MAX (254)
+#define CLNP_TTL_UNITS 2 /* 500 milliseconds */
+#define CLNP_TTL 15*CLNP_TTL_UNITS /* time to live (seconds) */
+#define ISO8473_V1 0x01
+
+/*
+ * Clnp packet types
+ * In order to test raw clnp and tp/clnp simultaneously, a third type of
+ * packet has been defined: CLNP_RAW. This is done so that the input
+ * routine can switch to the correct input routine (rclnp_input or
+ * tpclnp_input) based on the type field. If clnp had a higher level protocol
+ * field, this would not be necessary.
+ */
+#define CLNP_DT 0x1C /* normal data */
+#define CLNP_ER 0x01 /* error report */
+#define CLNP_RAW 0x1D /* debug only */
+#define CLNP_EC 0x1E /* echo packet */
+#define CLNP_ECR 0x1F /* echo reply */
+
+/*
+ * ER pdu error codes
+ */
+#define GEN_NOREAS 0x00 /* reason not specified */
+#define GEN_PROTOERR 0x01 /* protocol procedure error */
+#define GEN_BADCSUM 0x02 /* incorrect checksum */
+#define GEN_CONGEST 0x03 /* pdu discarded due to congestion */
+#define GEN_HDRSYNTAX 0x04 /* header syntax error */
+#define GEN_SEGNEEDED 0x05 /* segmentation needed, but not permitted */
+#define GEN_INCOMPLETE 0x06 /* incomplete pdu received */
+#define GEN_DUPOPT 0x07 /* duplicate option */
+
+/* address errors */
+#define ADDR_DESTUNREACH 0x80 /* destination address unreachable */
+#define ADDR_DESTUNKNOWN 0x81 /* destination address unknown */
+
+/* source routing */
+#define SRCRT_UNSPECERR 0x90 /* unspecified src rt error */
+#define SRCRT_SYNTAX 0x91 /* syntax error in src rt field */
+#define SRCRT_UNKNOWNADDR 0x92 /* unknown addr in src rt field */
+#define SRCRT_BADPATH 0x93 /* path not acceptable */
+
+/* lifetime */
+#define TTL_EXPTRANSIT 0xa0 /* lifetime expired during transit */
+#define TTL_EXPREASS 0xa1 /* lifetime expired during reassembly */
+
+/* pdu discarded */
+#define DISC_UNSUPPOPT 0xb0 /* unsupported option not specified? */
+#define DISC_UNSUPPVERS 0xb1 /* unsupported protocol version */
+#define DISC_UNSUPPSECURE 0xb2 /* unsupported security option */
+#define DISC_UNSUPPSRCRT 0xb3 /* unsupported src rt option */
+#define DISC_UNSUPPRECRT 0xb4 /* unsupported rec rt option */
+
+/* reassembly */
+#define REASS_INTERFERE 0xc0 /* reassembly interference */
+#define CLNP_ERRORS 22
+
+
+#ifdef KERNEL
+int clnp_er_index();
+#endif
+
+#ifdef CLNP_ER_CODES
+u_char clnp_er_codes[CLNP_ERRORS] = {
+GEN_NOREAS, GEN_PROTOERR, GEN_BADCSUM, GEN_CONGEST,
+GEN_HDRSYNTAX, GEN_SEGNEEDED, GEN_INCOMPLETE, GEN_DUPOPT,
+ADDR_DESTUNREACH, ADDR_DESTUNKNOWN,
+SRCRT_UNSPECERR, SRCRT_SYNTAX, SRCRT_UNKNOWNADDR, SRCRT_BADPATH,
+TTL_EXPTRANSIT, TTL_EXPREASS,
+DISC_UNSUPPOPT, DISC_UNSUPPVERS, DISC_UNSUPPSECURE,
+DISC_UNSUPPSRCRT, DISC_UNSUPPRECRT, REASS_INTERFERE };
+#endif
+
+#ifdef TROLL
+
+#define TR_DUPEND 0x01 /* duplicate end of fragment */
+#define TR_DUPPKT 0x02 /* duplicate entire packet */
+#define TR_DROPPKT 0x04 /* drop packet on output */
+#define TR_TRIM 0x08 /* trim bytes from packet */
+#define TR_CHANGE 0x10 /* change bytes in packet */
+#define TR_MTU 0x20 /* delta to change device mtu */
+#define TR_CHUCK 0x40 /* drop packet in rclnp_input */
+#define TR_BLAST 0x80 /* force rclnp_output to blast many packet */
+#define TR_RAWLOOP 0x100 /* make if_loop call clnpintr directly */
+struct troll {
+ int tr_ops; /* operations to perform */
+ float tr_dup_size; /* % to duplicate */
+ float tr_dup_freq; /* frequency to duplicate packets */
+ float tr_drop_freq; /* frequence to drop packets */
+ int tr_mtu_adj; /* delta to adjust if mtu */
+ int tr_blast_cnt; /* # of pkts to blast out */
+};
+
+#define SN_OUTPUT(clcp, m)\
+ troll_output(clcp->clc_ifp, m, clcp->clc_firsthop, clcp->clc_rt)
+
+#define SN_MTU(ifp, rt) (((rt && rt->rt_rmx.rmx_mtu) ?\
+ rt->rt_rmx.rmx_mtu : clnp_badmtu(ifp, rt, __LINE__, __FILE__))\
+ - trollctl.tr_mtu_adj)
+
+#ifdef KERNEL
+extern float troll_random;
+#endif
+
+#else /* NO TROLL */
+
+#define SN_OUTPUT(clcp, m)\
+ (*clcp->clc_ifp->if_output)(clcp->clc_ifp, m, clcp->clc_firsthop, clcp->clc_rt)
+
+#define SN_MTU(ifp, rt) (((rt && rt->rt_rmx.rmx_mtu) ?\
+ rt->rt_rmx.rmx_mtu : clnp_badmtu(ifp, rt, __LINE__, __FILE__)))
+
+#endif /* TROLL */
+
+/*
+ * Macro to remove an address from a clnp header
+ */
+#define CLNP_EXTRACT_ADDR(isoa, hoff, hend)\
+ {\
+ isoa.isoa_len = (u_char)*hoff;\
+ if ((((++hoff) + isoa.isoa_len) > hend) ||\
+ (isoa.isoa_len > 20) || (isoa.isoa_len == 0)) {\
+ hoff = (caddr_t)0;\
+ } else {\
+ (void) bcopy(hoff, (caddr_t)isoa.isoa_genaddr, isoa.isoa_len);\
+ hoff += isoa.isoa_len;\
+ }\
+ }
+
+/*
+ * Macro to insert an address into a clnp header
+ */
+#define CLNP_INSERT_ADDR(hoff, isoa)\
+ *hoff++ = (isoa).isoa_len;\
+ (void) bcopy((caddr_t)((isoa).isoa_genaddr), hoff, (isoa).isoa_len);\
+ hoff += (isoa).isoa_len;
+
+/*
+ * Clnp hdr cache. Whenever a clnp packet is sent, a copy of the
+ * header is made and kept in this cache. In addition to a copy of
+ * the cached clnp hdr, the cache contains
+ * information necessary to determine whether the new packet
+ * to send requires a new header to be built.
+ */
+struct clnp_cache {
+ /* these fields are used to check the validity of the cache */
+ struct iso_addr clc_dst; /* destination of packet */
+ struct mbuf *clc_options; /* ptr to options mbuf */
+ int clc_flags; /* flags passed to clnp_output */
+
+ /* these fields are state that clnp_output requires to finish the pkt */
+ int clc_segoff; /* offset of seg part of header */
+ struct rtentry *clc_rt; /* ptr to rtentry (points into
+ the route structure) */
+ struct sockaddr *clc_firsthop; /* first hop of packet */
+ struct ifnet *clc_ifp; /* ptr to interface structure */
+ struct iso_ifaddr *clc_ifa; /* ptr to interface address */
+ struct mbuf *clc_hdr; /* cached pkt hdr (finally)! */
+};
+
+#ifndef satosiso
+#define satosiso(sa)\
+ ((struct sockaddr_iso *)(sa))
+#endif
+
+#ifdef KERNEL
+caddr_t clnp_insert_addr();
+struct iso_addr *clnp_srcaddr();
+struct mbuf *clnp_reass();
+#ifdef TROLL
+struct troll trollctl;
+#endif /* TROLL */
+#endif /* KERNEL */
diff --git a/sys/netiso/clnp_debug.c b/sys/netiso/clnp_debug.c
new file mode 100644
index 0000000..964638e
--- /dev/null
+++ b/sys/netiso/clnp_debug.c
@@ -0,0 +1,260 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clnp_debug.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: clnp_debug.c,v 4.2 88/06/29 14:58:34 hagens Exp $ */
+/* $Source: /usr/argo/sys/netargo/RCS/clnp_debug.c,v $ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netiso/iso.h>
+#include <netiso/clnp.h>
+#include <netiso/clnp_stat.h>
+#include <netiso/argo_debug.h>
+
+#ifdef ARGO_DEBUG
+
+#ifdef TESTDEBUG
+#ifdef notdef
+struct addr_37 u_37 = {
+ {0x00, 0x02, 0x00, 0x10, 0x20, 0x30, 0x35},
+ {0x01, 0x02, 0x03, 0x04, 0x50, 0x60, 0x70, 0x80, 0x90}
+};
+struct addr_osinet u_osinet = {
+ {0x00, 0x04},
+ {0x00, 0x02, 0x00, 0x01, 0x23, 0x42, 0x78, 0x20, 0x01, 0x05, 0x00}
+};
+#endif /* notdef */
+struct addr_rfc986 u_rfc986 = {
+ {0x00, 0x06},
+ {0x01, 0xc0, 0x0c, 0x0c, 0xab, 0x11}
+};
+struct addr_rfc986 u_bad = {
+ {0x00, 0x01},
+ {0x01, 0xc0, 0x0c, 0x0c, 0xab, 0x11}
+};
+#include <stdio.h>
+main()
+{
+ struct iso_addr a;
+
+ a.isoa_afi = AFI_37;
+ a.isoa_u.addr_37 = u_37;
+ a.isoa_len = 17;
+ printf("type 37: %s\n", clnp_iso_addrp(&a));
+
+ a.isoa_afi = AFI_OSINET;
+ a.isoa_u.addr_osinet = u_osinet;
+ a.isoa_len = 14;
+ printf("type osinet: %s\n", clnp_iso_addrp(&a));
+
+ a.isoa_afi = AFI_RFC986;
+ a.isoa_u.addr_rfc986 = u_rfc986;
+ a.isoa_len = 9;
+ printf("type rfc986: %s\n", clnp_iso_addrp(&a));
+
+ a.isoa_afi = 12;
+ a.isoa_u.addr_rfc986 = u_rfc986;
+ a.isoa_len = 9;
+ printf("type bad afi: %s\n", clnp_iso_addrp(&a));
+
+ a.isoa_afi = AFI_RFC986;
+ a.isoa_u.addr_rfc986 = u_bad;
+ a.isoa_len = 9;
+ printf("type bad idi: %s\n", clnp_iso_addrp(&a));
+}
+#endif /* TESTDEBUG */
+
+unsigned int clnp_debug;
+static char letters[] = "0123456789abcdef";
+
+/*
+ * Print buffer in hex, return addr of where we left off.
+ * Do not null terminate.
+ */
+char *
+clnp_hexp(src, len, where)
+char *src; /* src of data to print */
+int len; /* lengthof src */
+char *where; /* where to put data */
+{
+ int i;
+
+ for (i=0; i<len; i++) {
+ register int j = ((u_char *)src)[i];
+ *where++ = letters[j >> 4];
+ *where++ = letters[j & 0x0f];
+ }
+ return where;
+}
+
+/*
+ * Return a ptr to a human readable form of an iso addr
+ */
+static char iso_addr_b[50];
+#define DELIM '.';
+
+char *
+clnp_iso_addrp(isoa)
+struct iso_addr *isoa;
+{
+ char *cp;
+
+ /* print length */
+ sprintf(iso_addr_b, "[%d] ", isoa->isoa_len);
+
+ /* set cp to end of what we have */
+ cp = iso_addr_b;
+ while (*cp)
+ cp++;
+
+ /* print afi */
+ cp = clnp_hexp(isoa->isoa_genaddr, (int)isoa->isoa_len, cp);
+#ifdef notdef
+ *cp++ = DELIM;
+
+ /* print type specific part */
+ switch(isoa->isoa_afi) {
+ case AFI_37:
+ cp = clnp_hexp(isoa->t37_idi, ADDR37_IDI_LEN, cp);
+ *cp++ = DELIM;
+ cp = clnp_hexp(isoa->t37_dsp, ADDR37_DSP_LEN, cp);
+ break;
+
+/* case AFI_OSINET:*/
+ case AFI_RFC986: {
+ u_short idi;
+
+ /* osinet and rfc986 have idi in the same place */
+ /* print idi */
+ cp = clnp_hexp(isoa->rfc986_idi, ADDROSINET_IDI_LEN, cp);
+ *cp++ = DELIM;
+ CTOH(isoa->rfc986_idi[0], isoa->rfc986_idi[1], idi);
+
+ if (idi == IDI_OSINET) {
+ struct ovl_osinet *oosi = (struct ovl_osinet *)isoa;
+ cp = clnp_hexp(oosi->oosi_orgid, OVLOSINET_ORGID_LEN, cp);
+ *cp++ = DELIM;
+ cp = clnp_hexp(oosi->oosi_snetid, OVLOSINET_SNETID_LEN, cp);
+ *cp++ = DELIM;
+ cp = clnp_hexp(oosi->oosi_snpa, OVLOSINET_SNPA_LEN, cp);
+ *cp++ = DELIM;
+ cp = clnp_hexp(oosi->oosi_nsap, OVLOSINET_NSAP_LEN, cp);
+ } else if (idi == IDI_RFC986) {
+ struct ovl_rfc986 *o986 = (struct ovl_rfc986 *)isoa;
+ cp = clnp_hexp(&o986->o986_vers, 1, cp);
+ *cp++ = DELIM;
+#ifdef vax
+ sprintf(cp, "%d.%d.%d.%d.%d",
+ o986->o986_inetaddr[0] & 0xff,
+ o986->o986_inetaddr[1] & 0xff,
+ o986->o986_inetaddr[2] & 0xff,
+ o986->o986_inetaddr[3] & 0xff,
+ o986->o986_upid & 0xff);
+ return(iso_addr_b);
+#else
+ cp = clnp_hexp(&o986->o986_inetaddr[0], 1, cp);
+ *cp++ = DELIM;
+ cp = clnp_hexp(&o986->o986_inetaddr[1], 1, cp);
+ *cp++ = DELIM;
+ cp = clnp_hexp(&o986->o986_inetaddr[2], 1, cp);
+ *cp++ = DELIM;
+ cp = clnp_hexp(&o986->o986_inetaddr[3], 1, cp);
+ *cp++ = DELIM;
+ cp = clnp_hexp(&o986->o986_upid, 1, cp);
+#endif /* vax */
+ }
+
+ } break;
+
+ default:
+ *cp++ = '?';
+ break;
+ }
+#endif /* notdef */
+ *cp = (char)0;
+
+ return(iso_addr_b);
+}
+
+char *
+clnp_saddr_isop(s)
+register struct sockaddr_iso *s;
+{
+ register char *cp = clnp_iso_addrp(&s->siso_addr);
+
+ while (*cp) cp++;
+ *cp++ = '(';
+ cp = clnp_hexp(TSEL(s), (int)s->siso_tlen, cp);
+ *cp++ = ')';
+ *cp++ = 0;
+ return (iso_addr_b);
+}
+
+#endif /* ARGO_DEBUG */
diff --git a/sys/netiso/clnp_er.c b/sys/netiso/clnp_er.c
new file mode 100644
index 0000000..8b7f45b
--- /dev/null
+++ b/sys/netiso/clnp_er.c
@@ -0,0 +1,375 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clnp_er.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: /var/src/sys/netiso/RCS/clnp_er.c,v 5.1 89/02/09 16:20:18 hagens Exp $ */
+/* $Source: /var/src/sys/netiso/RCS/clnp_er.c,v $ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netiso/iso.h>
+#include <netiso/iso_var.h>
+#include <netiso/iso_pcb.h>
+#define CLNP_ER_CODES
+#include <netiso/clnp.h>
+#include <netiso/clnp_stat.h>
+#include <netiso/argo_debug.h>
+
+static struct clnp_fixed er_template = {
+ ISO8473_CLNP, /* network identifier */
+ 0, /* length */
+ ISO8473_V1, /* version */
+ CLNP_TTL, /* ttl */
+ CLNP_ER, /* type */
+ 0, /* segment length */
+ 0 /* checksum */
+};
+
+/*
+ * FUNCTION: clnp_er_input
+ *
+ * PURPOSE: Process an ER pdu.
+ *
+ * RETURNS:
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+clnp_er_input(m, src, reason)
+struct mbuf *m; /* ptr to packet itself */
+struct iso_addr *src; /* ptr to src of er */
+u_char reason; /* reason code of er */
+{
+ int cmd = -1;
+ extern u_char clnp_protox[];
+
+ IFDEBUG(D_CTLINPUT)
+ printf("clnp_er_input: m x%x, src %s, reason x%x\n", m,
+ clnp_iso_addrp(src), reason);
+ ENDDEBUG
+
+ INCSTAT(cns_er_inhist[clnp_er_index(reason)]);
+ switch (reason) {
+ case GEN_NOREAS:
+ case GEN_PROTOERR:
+ break;
+ case GEN_BADCSUM:
+ cmd = PRC_PARAMPROB;
+ break;
+ case GEN_CONGEST:
+ cmd = PRC_QUENCH;
+ break;
+ case GEN_HDRSYNTAX:
+ cmd = PRC_PARAMPROB;
+ break;
+ case GEN_SEGNEEDED:
+ cmd = PRC_MSGSIZE;
+ break;
+ case GEN_INCOMPLETE:
+ cmd = PRC_PARAMPROB;
+ break;
+ case GEN_DUPOPT:
+ cmd = PRC_PARAMPROB;
+ break;
+ case ADDR_DESTUNREACH:
+ cmd = PRC_UNREACH_HOST;
+ break;
+ case ADDR_DESTUNKNOWN:
+ cmd = PRC_UNREACH_PROTOCOL;
+ break;
+ case SRCRT_UNSPECERR:
+ case SRCRT_SYNTAX:
+ case SRCRT_UNKNOWNADDR:
+ case SRCRT_BADPATH:
+ cmd = PRC_UNREACH_SRCFAIL;
+ break;
+ case TTL_EXPTRANSIT:
+ cmd = PRC_TIMXCEED_INTRANS;
+ break;
+ case TTL_EXPREASS:
+ cmd = PRC_TIMXCEED_REASS;
+ break;
+ case DISC_UNSUPPOPT:
+ case DISC_UNSUPPVERS:
+ case DISC_UNSUPPSECURE:
+ case DISC_UNSUPPSRCRT:
+ case DISC_UNSUPPRECRT:
+ cmd = PRC_PARAMPROB;
+ break;
+ case REASS_INTERFERE:
+ cmd = PRC_TIMXCEED_REASS;
+ break;
+ }
+
+ /*
+ * tpclnp_ctlinput1 is called directly so that we don't
+ * have to build an iso_sockaddr out of src.
+ */
+ if (cmd >= 0)
+ tpclnp_ctlinput1(cmd, src);
+
+ m_freem(m);
+}
+
+/*
+ * FUNCTION: clnp_discard
+ *
+ * PURPOSE: Discard a clnp datagram
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS: Will emit an ER pdu if possible
+ *
+ * NOTES: This code assumes that we have previously tried to pull
+ * up the header of the datagram into one mbuf.
+ */
+clnp_discard(m, reason)
+struct mbuf *m; /* header of packet to discard */
+char reason; /* reason for discard */
+{
+ IFDEBUG(D_DISCARD)
+ printf("clnp_discard: m x%x, reason x%x\n", m, reason);
+ ENDDEBUG
+
+ if (m != NULL) {
+ if (m->m_len >= sizeof(struct clnp_fixed)) {
+ register struct clnp_fixed *clnp = mtod(m, struct clnp_fixed *);
+
+ if (((clnp->cnf_type & CNF_TYPE) != CLNP_ER) &&
+ (clnp->cnf_type & CNF_ERR_OK)) {
+ clnp_emit_er(m, reason);
+ return;
+ }
+ }
+ m_freem(m);
+ }
+}
+
+/*
+ * FUNCTION: clnp_emit_er
+ *
+ * PURPOSE: Send an ER pdu.
+ * The src of the of the ER pdu is the host that is sending
+ * the ER (ie. us), *not* the original destination of the
+ * packet.
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: Takes responsibility for freeing mbuf passed
+ * This function may be called with a packet that
+ * was created by us; in this case, do not send
+ * an ER.
+ */
+clnp_emit_er(m, reason)
+struct mbuf *m; /* header of packet to discard */
+char reason; /* reason for discard */
+{
+ register struct clnp_fixed *clnp = mtod(m, struct clnp_fixed *);
+ register struct clnp_fixed *er;
+ struct route_iso route;
+ struct ifnet *ifp;
+ struct sockaddr *first_hop;
+ struct iso_addr src, dst, *our_addr;
+ caddr_t hoff, hend;
+ int total_len; /* total len of dg */
+ struct mbuf *m0; /* contains er pdu hdr */
+ struct iso_ifaddr *ia = 0;
+
+ IFDEBUG(D_DISCARD)
+ printf("clnp_emit_er: m x%x, hdr len %d\n", m, clnp->cnf_hdr_len);
+ ENDDEBUG
+
+ bzero((caddr_t)&route, sizeof(route));
+
+ /*
+ * If header length is incorrect, or entire header is not contained
+ * in this mbuf, we punt
+ */
+ if ((clnp->cnf_hdr_len < CLNP_HDR_MIN) ||
+ (clnp->cnf_hdr_len > CLNP_HDR_MAX) ||
+ (clnp->cnf_hdr_len > m->m_len))
+ goto bad;
+
+ /* extract src, dest address */
+ hend = (caddr_t)clnp + clnp->cnf_hdr_len;
+ hoff = (caddr_t)clnp + sizeof(struct clnp_fixed);
+ CLNP_EXTRACT_ADDR(dst, hoff, hend);
+ if (hoff == (caddr_t)0) {
+ goto bad;
+ }
+ CLNP_EXTRACT_ADDR(src, hoff, hend);
+ if (hoff == (caddr_t)0) {
+ goto bad;
+ }
+
+ /*
+ * Do not send ER if we generated the packet.
+ */
+ if (clnp_ours(&src))
+ goto bad;
+
+ /*
+ * Trim mbuf to hold only the header.
+ * This mbuf will be the 'data' of the er pdu
+ */
+ if (m->m_next != NULL) {
+ m_freem(m->m_next);
+ m->m_next = NULL;
+ }
+
+ if (m->m_len > clnp->cnf_hdr_len)
+ m_adj(m, (int)-(m->m_len - (int)clnp->cnf_hdr_len));
+
+ /* route er pdu: note we send pkt to src of original packet */
+ if (clnp_route(&src, &route, /* flags */0, &first_hop, &ia) != 0)
+ goto bad;
+
+ /* compute our address based upon firsthop/ifp */
+ if (ia)
+ our_addr = &ia->ia_addr.siso_addr;
+ else
+ goto bad;
+ ifp = ia->ia_ifp;
+
+ IFDEBUG(D_DISCARD)
+ printf("clnp_emit_er: to %s", clnp_iso_addrp(&src));
+ printf(" from %s\n", clnp_iso_addrp(our_addr));
+ ENDDEBUG
+
+ IFDEBUG(D_DISCARD)
+ printf("clnp_emit_er: packet routed to %s\n",
+ clnp_iso_addrp(&((struct sockaddr_iso *)first_hop)->siso_addr));
+ ENDDEBUG
+
+ /* allocate mbuf for er pdu header: punt on no space */
+ MGET(m0, M_DONTWAIT, MT_HEADER);
+ if (m0 == 0)
+ goto bad;
+
+ m0->m_next = m;
+ er = mtod(m0, struct clnp_fixed *);
+ *er = er_template;
+
+ /* setup src/dst on er pdu */
+ /* NOTE REVERSAL OF SRC/DST */
+ hoff = (caddr_t)er + sizeof(struct clnp_fixed);
+ CLNP_INSERT_ADDR(hoff, src);
+ CLNP_INSERT_ADDR(hoff, *our_addr);
+
+ /*
+ * TODO: if complete src rt was specified, then reverse path, and
+ * copy into er as option.
+ */
+
+ /* add er option */
+ *hoff++ = CLNPOVAL_ERREAS; /* code */
+ *hoff++ = 2; /* length */
+ *hoff++ = reason; /* discard reason */
+ *hoff++ = 0; /* error localization = not specified */
+
+ /* set length */
+ er->cnf_hdr_len = m0->m_len = (u_char)(hoff - (caddr_t)er);
+ total_len = m0->m_len + m->m_len;
+ HTOC(er->cnf_seglen_msb, er->cnf_seglen_lsb, total_len);
+
+ /* compute checksum (on header only) */
+ iso_gen_csum(m0, CLNP_CKSUM_OFF, (int)er->cnf_hdr_len);
+
+ /* trim packet if too large for interface */
+ if (total_len > ifp->if_mtu)
+ m_adj(m0, -(total_len - ifp->if_mtu));
+
+ /* send packet */
+ INCSTAT(cns_er_outhist[clnp_er_index(reason)]);
+ (void) (*ifp->if_output)(ifp, m0, first_hop, route.ro_rt);
+ goto done;
+
+bad:
+ m_freem(m);
+
+done:
+ /* free route if it is a temp */
+ if (route.ro_rt != NULL)
+ RTFREE(route.ro_rt);
+}
+
+clnp_er_index(p)
+u_char p;
+{
+ register u_char *cp = clnp_er_codes + CLNP_ERRORS;
+ while (cp > clnp_er_codes) {
+ cp--;
+ if (*cp == p)
+ return (cp - clnp_er_codes);
+ }
+ return (CLNP_ERRORS + 1);
+}
diff --git a/sys/netiso/clnp_frag.c b/sys/netiso/clnp_frag.c
new file mode 100644
index 0000000..546a592
--- /dev/null
+++ b/sys/netiso/clnp_frag.c
@@ -0,0 +1,859 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clnp_frag.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: /var/src/sys/netiso/RCS/clnp_frag.c,v 5.1 89/02/09 16:20:26 hagens Exp $ */
+/* $Source: /var/src/sys/netiso/RCS/clnp_frag.c,v $ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netiso/iso.h>
+#include <netiso/iso_var.h>
+#include <netiso/clnp.h>
+#include <netiso/clnp_stat.h>
+#include <netiso/argo_debug.h>
+
+/* all fragments are hung off this list */
+struct clnp_fragl *clnp_frags = NULL;
+
+struct mbuf *clnp_comp_pdu();
+
+
+/*
+ * FUNCTION: clnp_fragment
+ *
+ * PURPOSE: Fragment a datagram, and send the itty bitty pieces
+ * out over an interface.
+ *
+ * RETURNS: success - 0
+ * failure - unix error code
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: If there is an error sending the packet, clnp_discard
+ * is called to discard the packet and send an ER. If
+ * clnp_fragment was called from clnp_output, then
+ * we generated the packet, and should not send an
+ * ER -- clnp_emit_er will check for this. Otherwise,
+ * the packet was fragmented during forwarding. In this
+ * case, we ought to send an ER back.
+ */
+clnp_fragment(ifp, m, first_hop, total_len, segoff, flags, rt)
+struct ifnet *ifp; /* ptr to outgoing interface */
+struct mbuf *m; /* ptr to packet */
+struct sockaddr *first_hop; /* ptr to first hop */
+int total_len; /* length of datagram */
+int segoff; /* offset of segpart in hdr */
+int flags; /* flags passed to clnp_output */
+struct rtentry *rt; /* route if direct ether */
+{
+ struct clnp_fixed *clnp = mtod(m, struct clnp_fixed *);
+ int hdr_len = (int)clnp->cnf_hdr_len;
+ int frag_size = (SN_MTU(ifp, rt) - hdr_len) & ~7;
+
+ total_len -= hdr_len;
+ if ((clnp->cnf_type & CNF_SEG_OK) &&
+ (total_len >= 8) &&
+ (frag_size > 8 || (frag_size == 8 && !(total_len & 7)))) {
+
+ struct mbuf *hdr = NULL; /* save copy of clnp hdr */
+ struct mbuf *frag_hdr = NULL;
+ struct mbuf *frag_data = NULL;
+ struct clnp_segment seg_part; /* segmentation header */
+ int frag_base;
+ int error = 0;
+
+
+ INCSTAT(cns_fragmented);
+ (void) bcopy(segoff + mtod(m, caddr_t), (caddr_t)&seg_part,
+ sizeof(seg_part));
+ frag_base = ntohs(seg_part.cng_off);
+ /*
+ * Duplicate header, and remove from packet
+ */
+ if ((hdr = m_copy(m, 0, hdr_len)) == NULL) {
+ clnp_discard(m, GEN_CONGEST);
+ return(ENOBUFS);
+ }
+ m_adj(m, hdr_len);
+
+ while (total_len > 0) {
+ int remaining, last_frag;
+
+ IFDEBUG(D_FRAG)
+ struct mbuf *mdump = frag_hdr;
+ int tot_mlen = 0;
+ printf("clnp_fragment: total_len %d:\n", total_len);
+ while (mdump != NULL) {
+ printf("\tmbuf x%x, m_len %d\n",
+ mdump, mdump->m_len);
+ tot_mlen += mdump->m_len;
+ mdump = mdump->m_next;
+ }
+ printf("clnp_fragment: sum of mbuf chain %d:\n", tot_mlen);
+ ENDDEBUG
+
+ frag_size = min(total_len, frag_size);
+ if ((remaining = total_len - frag_size) == 0)
+ last_frag = 1;
+ else {
+ /*
+ * If this fragment will cause the last one to
+ * be less than 8 bytes, shorten this fragment a bit.
+ * The obscure test on frag_size above ensures that
+ * frag_size will be positive.
+ */
+ last_frag = 0;
+ if (remaining < 8)
+ frag_size -= 8;
+ }
+
+
+ IFDEBUG(D_FRAG)
+ printf("clnp_fragment: seg off %d, size %d, remaining %d\n",
+ ntohs(seg_part.cng_off), frag_size, total_len-frag_size);
+ if (last_frag)
+ printf("clnp_fragment: last fragment\n");
+ ENDDEBUG
+
+ if (last_frag) {
+ /*
+ * this is the last fragment; we don't need to get any other
+ * mbufs.
+ */
+ frag_hdr = hdr;
+ frag_data = m;
+ } else {
+ /* duplicate header and data mbufs */
+ if ((frag_hdr = m_copy(hdr, 0, (int)M_COPYALL)) == NULL) {
+ clnp_discard(hdr, GEN_CONGEST);
+ m_freem(m);
+ return(ENOBUFS);
+ }
+ if ((frag_data = m_copy(m, 0, frag_size)) == NULL) {
+ clnp_discard(hdr, GEN_CONGEST);
+ m_freem(m);
+ m_freem(frag_hdr);
+ return(ENOBUFS);
+ }
+ INCSTAT(cns_fragments);
+ }
+ clnp = mtod(frag_hdr, struct clnp_fixed *);
+
+ if (!last_frag)
+ clnp->cnf_type |= CNF_MORE_SEGS;
+
+ /* link together */
+ m_cat(frag_hdr, frag_data);
+
+ /* insert segmentation part; updated below */
+ bcopy((caddr_t)&seg_part, mtod(frag_hdr, caddr_t) + segoff,
+ sizeof(struct clnp_segment));
+
+ {
+ int derived_len = hdr_len + frag_size;
+ HTOC(clnp->cnf_seglen_msb, clnp->cnf_seglen_lsb, derived_len);
+ if ((frag_hdr->m_flags & M_PKTHDR) == 0)
+ panic("clnp_frag:lost header");
+ frag_hdr->m_pkthdr.len = derived_len;
+ }
+ /* compute clnp checksum (on header only) */
+ if (flags & CLNP_NO_CKSUM) {
+ HTOC(clnp->cnf_cksum_msb, clnp->cnf_cksum_lsb, 0);
+ } else {
+ iso_gen_csum(frag_hdr, CLNP_CKSUM_OFF, hdr_len);
+ }
+
+ IFDEBUG(D_DUMPOUT)
+ struct mbuf *mdump = frag_hdr;
+ printf("clnp_fragment: sending dg:\n");
+ while (mdump != NULL) {
+ printf("\tmbuf x%x, m_len %d\n", mdump, mdump->m_len);
+ mdump = mdump->m_next;
+ }
+ ENDDEBUG
+
+#ifdef TROLL
+ error = troll_output(ifp, frag_hdr, first_hop, rt);
+#else
+ error = (*ifp->if_output)(ifp, frag_hdr, first_hop, rt);
+#endif /* TROLL */
+
+ /*
+ * Tough situation: if the error occured on the last
+ * fragment, we can not send an ER, as the if_output
+ * routine consumed the packet. If the error occured
+ * on any intermediate packets, we can send an ER
+ * because we still have the original header in (m).
+ */
+ if (error) {
+ if (frag_hdr != hdr) {
+ /*
+ * The error was not on the last fragment. We must
+ * free hdr and m before returning
+ */
+ clnp_discard(hdr, GEN_NOREAS);
+ m_freem(m);
+ }
+ return(error);
+ }
+
+ /* bump segment offset, trim data mbuf, and decrement count left */
+#ifdef TROLL
+ /*
+ * Decrement frag_size by some fraction. This will cause the
+ * next fragment to start 'early', thus duplicating the end
+ * of the current fragment. troll.tr_dup_size controls
+ * the fraction. If positive, it specifies the fraction. If
+ * negative, a random fraction is used.
+ */
+ if ((trollctl.tr_ops & TR_DUPEND) && (!last_frag)) {
+ int num_bytes = frag_size;
+
+ if (trollctl.tr_dup_size > 0)
+ num_bytes *= trollctl.tr_dup_size;
+ else
+ num_bytes *= troll_random();
+ frag_size -= num_bytes;
+ }
+#endif /* TROLL */
+ total_len -= frag_size;
+ if (!last_frag) {
+ frag_base += frag_size;
+ seg_part.cng_off = htons(frag_base);
+ m_adj(m, frag_size);
+ }
+ }
+ return(0);
+ } else {
+ cantfrag:
+ INCSTAT(cns_cantfrag);
+ clnp_discard(m, GEN_SEGNEEDED);
+ return(EMSGSIZE);
+ }
+}
+
+/*
+ * FUNCTION: clnp_reass
+ *
+ * PURPOSE: Attempt to reassemble a clnp packet given the current
+ * fragment. If reassembly succeeds (all the fragments
+ * are present), then return a pointer to an mbuf chain
+ * containing the reassembled packet. This packet will
+ * appear in the mbufs as if it had just arrived in
+ * one piece.
+ *
+ * If reassembly fails, then save this fragment and
+ * return 0.
+ *
+ * RETURNS: Ptr to assembled packet, or 0
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ * clnp_slowtimo can not affect this code because clnpintr, and thus
+ * this code, is called at a higher priority than clnp_slowtimo.
+ */
+struct mbuf *
+clnp_reass(m, src, dst, seg)
+struct mbuf *m; /* new fragment */
+struct iso_addr *src; /* src of new fragment */
+struct iso_addr *dst; /* dst of new fragment */
+struct clnp_segment *seg; /* segment part of fragment header */
+{
+ register struct clnp_fragl *cfh;
+
+ /* look for other fragments of this datagram */
+ for (cfh = clnp_frags; cfh != NULL; cfh = cfh->cfl_next) {
+ if (seg->cng_id == cfh->cfl_id &&
+ iso_addrmatch1(src, &cfh->cfl_src) &&
+ iso_addrmatch1(dst, &cfh->cfl_dst)) {
+ IFDEBUG(D_REASS)
+ printf("clnp_reass: found packet\n");
+ ENDDEBUG
+ /*
+ * There are other fragments here already. Lets see if
+ * this fragment is of any help
+ */
+ clnp_insert_frag(cfh, m, seg);
+ if (m = clnp_comp_pdu(cfh)) {
+ register struct clnp_fixed *clnp = mtod(m, struct clnp_fixed *);
+ HTOC(clnp->cnf_seglen_msb, clnp->cnf_seglen_lsb,
+ seg->cng_tot_len);
+ }
+ return (m);
+ }
+ }
+
+ IFDEBUG(D_REASS)
+ printf("clnp_reass: new packet!\n");
+ ENDDEBUG
+
+ /*
+ * This is the first fragment. If src is not consuming too many
+ * resources, then create a new fragment list and add
+ * this fragment to the list.
+ */
+ /* TODO: don't let one src hog all the reassembly buffers */
+ if (!clnp_newpkt(m, src, dst, seg) /* || this src is a hog */) {
+ INCSTAT(cns_fragdropped);
+ clnp_discard(m, GEN_CONGEST);
+ }
+
+ return(NULL);
+}
+
+/*
+ * FUNCTION: clnp_newpkt
+ *
+ * PURPOSE: Create the necessary structures to handle a new
+ * fragmented clnp packet.
+ *
+ * RETURNS: non-zero if it succeeds, zero if fails.
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: Failure is only due to insufficient resources.
+ */
+clnp_newpkt(m, src, dst, seg)
+struct mbuf *m; /* new fragment */
+struct iso_addr *src; /* src of new fragment */
+struct iso_addr *dst; /* dst of new fragment */
+struct clnp_segment *seg; /* segment part of fragment header */
+{
+ register struct clnp_fragl *cfh;
+ register struct clnp_fixed *clnp;
+ struct mbuf *m0;
+
+ clnp = mtod(m, struct clnp_fixed *);
+
+ /*
+ * Allocate new clnp fragl structure to act as header of all fragments
+ * for this datagram.
+ */
+ MGET(m0, M_DONTWAIT, MT_FTABLE);
+ if (m0 == NULL) {
+ return (0);
+ }
+ cfh = mtod(m0, struct clnp_fragl *);
+
+ /*
+ * Duplicate the header of this fragment, and save in cfh.
+ * Free m0 and return if m_copy does not succeed.
+ */
+ if ((cfh->cfl_orighdr = m_copy(m, 0, (int)clnp->cnf_hdr_len)) == NULL) {
+ m_freem(m0);
+ return (0);
+ }
+
+ /* Fill in rest of fragl structure */
+ bcopy((caddr_t)src, (caddr_t)&cfh->cfl_src, sizeof(struct iso_addr));
+ bcopy((caddr_t)dst, (caddr_t)&cfh->cfl_dst, sizeof(struct iso_addr));
+ cfh->cfl_id = seg->cng_id;
+ cfh->cfl_ttl = clnp->cnf_ttl;
+ cfh->cfl_last = (seg->cng_tot_len - clnp->cnf_hdr_len) - 1;
+ cfh->cfl_frags = NULL;
+ cfh->cfl_next = NULL;
+
+ /* Insert into list of packets */
+ cfh->cfl_next = clnp_frags;
+ clnp_frags = cfh;
+
+ /* Insert this fragment into list headed by cfh */
+ clnp_insert_frag(cfh, m, seg);
+ return(1);
+}
+
+/*
+ * FUNCTION: clnp_insert_frag
+ *
+ * PURPOSE: Insert fragment into list headed by 'cf'.
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: This is the 'guts' of the reassembly algorithm.
+ * Each fragment in this list contains a clnp_frag
+ * structure followed by the data of the fragment.
+ * The clnp_frag structure actually lies on top of
+ * part of the old clnp header.
+ */
+clnp_insert_frag(cfh, m, seg)
+struct clnp_fragl *cfh; /* header of list of packet fragments */
+struct mbuf *m; /* new fragment */
+struct clnp_segment *seg; /* segment part of fragment header */
+{
+ register struct clnp_fixed *clnp; /* clnp hdr of fragment */
+ register struct clnp_frag *cf; /* generic fragment ptr */
+ register struct clnp_frag *cf_sub = NULL; /* frag subsequent to new one */
+ register struct clnp_frag *cf_prev = NULL; /* frag previous to new one */
+ u_short first; /* offset of first byte of initial pdu*/
+ u_short last; /* offset of last byte of initial pdu */
+ u_short fraglen;/* length of fragment */
+
+ clnp = mtod(m, struct clnp_fixed *);
+ first = seg->cng_off;
+ CTOH(clnp->cnf_seglen_msb, clnp->cnf_seglen_lsb, fraglen);
+ fraglen -= clnp->cnf_hdr_len;
+ last = (first + fraglen) - 1;
+
+ IFDEBUG(D_REASS)
+ printf("clnp_insert_frag: New fragment: [%d ... %d], len %d\n",
+ first, last, fraglen);
+ printf("clnp_insert_frag: current fragments:\n");
+ for (cf = cfh->cfl_frags; cf != NULL; cf = cf->cfr_next) {
+ printf("\tcf x%x: [%d ... %d]\n", cf, cf->cfr_first, cf->cfr_last);
+ }
+ ENDDEBUG
+
+ if (cfh->cfl_frags != NULL) {
+ /*
+ * Find fragment which begins after the new one
+ */
+ for (cf = cfh->cfl_frags; cf != NULL; cf_prev = cf, cf = cf->cfr_next) {
+ if (cf->cfr_first > first) {
+ cf_sub = cf;
+ break;
+ }
+ }
+
+ IFDEBUG(D_REASS)
+ printf("clnp_insert_frag: Previous frag is ");
+ if (cf_prev == NULL)
+ printf("NULL\n");
+ else
+ printf("[%d ... %d]\n", cf_prev->cfr_first, cf_prev->cfr_last);
+ printf("clnp_insert_frag: Subsequent frag is ");
+ if (cf_sub == NULL)
+ printf("NULL\n");
+ else
+ printf("[%d ... %d]\n", cf_sub->cfr_first, cf_sub->cfr_last);
+ ENDDEBUG
+
+ /*
+ * If there is a fragment before the new one, check if it
+ * overlaps the new one. If so, then trim the end of the
+ * previous one.
+ */
+ if (cf_prev != NULL) {
+ if (cf_prev->cfr_last > first) {
+ u_short overlap = cf_prev->cfr_last - first;
+
+ IFDEBUG(D_REASS)
+ printf("clnp_insert_frag: previous overlaps by %d\n",
+ overlap);
+ ENDDEBUG
+
+ if (overlap > fraglen) {
+ /*
+ * The new fragment is entirely contained in the
+ * preceeding one. We can punt on the new frag
+ * completely.
+ */
+ m_freem(m);
+ return;
+ } else {
+ /* Trim data off of end of previous fragment */
+ /* inc overlap to prevent duplication of last byte */
+ overlap++;
+ m_adj(cf_prev->cfr_data, -(int)overlap);
+ cf_prev->cfr_last -= overlap;
+ }
+ }
+ }
+
+ /*
+ * For all fragments past the new one, check if any data on
+ * the new one overlaps data on existing fragments. If so,
+ * then trim the extra data off the end of the new one.
+ */
+ for (cf = cf_sub; cf != NULL; cf = cf->cfr_next) {
+ if (cf->cfr_first < last) {
+ u_short overlap = last - cf->cfr_first;
+
+ IFDEBUG(D_REASS)
+ printf("clnp_insert_frag: subsequent overlaps by %d\n",
+ overlap);
+ ENDDEBUG
+
+ if (overlap > fraglen) {
+ /*
+ * The new fragment is entirely contained in the
+ * succeeding one. This should not happen, because
+ * early on in this code we scanned for the fragment
+ * which started after the new one!
+ */
+ m_freem(m);
+ printf("clnp_insert_frag: internal error!\n");
+ return;
+ } else {
+ /* Trim data off of end of new fragment */
+ /* inc overlap to prevent duplication of last byte */
+ overlap++;
+ m_adj(m, -(int)overlap);
+ last -= overlap;
+ }
+ }
+ }
+ }
+
+ /*
+ * Insert the new fragment beween cf_prev and cf_sub
+ *
+ * Note: the clnp hdr is still in the mbuf.
+ * If the data of the mbuf is not word aligned, shave off enough
+ * so that it is. Then, cast the clnp_frag structure on top
+ * of the clnp header.
+ * The clnp_hdr will not be used again (as we already have
+ * saved a copy of it).
+ *
+ * Save in cfr_bytes the number of bytes to shave off to get to
+ * the data of the packet. This is used when we coalesce fragments;
+ * the clnp_frag structure must be removed before joining mbufs.
+ */
+ {
+ int pad;
+ u_int bytes;
+
+ /* determine if header is not word aligned */
+ pad = (int)clnp % 4;
+ if (pad < 0)
+ pad = -pad;
+
+ /* bytes is number of bytes left in front of data */
+ bytes = clnp->cnf_hdr_len - pad;
+
+ IFDEBUG(D_REASS)
+ printf("clnp_insert_frag: clnp x%x requires %d alignment\n",
+ clnp, pad);
+ ENDDEBUG
+
+ /* make it word aligned if necessary */
+ if (pad)
+ m_adj(m, pad);
+
+ cf = mtod(m, struct clnp_frag *);
+ cf->cfr_bytes = bytes;
+
+ IFDEBUG(D_REASS)
+ printf("clnp_insert_frag: cf now x%x, cfr_bytes %d\n", cf,
+ cf->cfr_bytes);
+ ENDDEBUG
+ }
+ cf->cfr_first = first;
+ cf->cfr_last = last;
+
+
+ /*
+ * The data is the mbuf itself, although we must remember that the
+ * first few bytes are actually a clnp_frag structure
+ */
+ cf->cfr_data = m;
+
+ /* link into place */
+ cf->cfr_next = cf_sub;
+ if (cf_prev == NULL)
+ cfh->cfl_frags = cf;
+ else
+ cf_prev->cfr_next = cf;
+}
+
+/*
+ * FUNCTION: clnp_comp_pdu
+ *
+ * PURPOSE: Scan the list of fragments headed by cfh. Merge
+ * any contigious fragments into one. If, after
+ * traversing all the fragments, it is determined that
+ * the packet is complete, then return a pointer to
+ * the packet (with header prepended). Otherwise,
+ * return NULL.
+ *
+ * RETURNS: NULL, or a pointer to the assembled pdu in an mbuf chain.
+ *
+ * SIDE EFFECTS: Will colapse contigious fragments into one.
+ *
+ * NOTES: This code assumes that there are no overlaps of
+ * fragment pdus.
+ */
+struct mbuf *
+clnp_comp_pdu(cfh)
+struct clnp_fragl *cfh; /* fragment header */
+{
+ register struct clnp_frag *cf = cfh->cfl_frags;
+
+ while (cf->cfr_next != NULL) {
+ register struct clnp_frag *cf_next = cf->cfr_next;
+
+ IFDEBUG(D_REASS)
+ printf("clnp_comp_pdu: comparing: [%d ... %d] to [%d ... %d]\n",
+ cf->cfr_first, cf->cfr_last, cf_next->cfr_first,
+ cf_next->cfr_last);
+ ENDDEBUG
+
+ if (cf->cfr_last == (cf_next->cfr_first - 1)) {
+ /*
+ * Merge fragment cf and cf_next
+ *
+ * - update cf header
+ * - trim clnp_frag structure off of cf_next
+ * - append cf_next to cf
+ */
+ struct clnp_frag cf_next_hdr;
+ struct clnp_frag *next_frag;
+
+ cf_next_hdr = *cf_next;
+ next_frag = cf_next->cfr_next;
+
+ IFDEBUG(D_REASS)
+ struct mbuf *mdump;
+ int l;
+ printf("clnp_comp_pdu: merging fragments\n");
+ printf("clnp_comp_pdu: 1st: [%d ... %d] (bytes %d)\n",
+ cf->cfr_first, cf->cfr_last, cf->cfr_bytes);
+ mdump = cf->cfr_data;
+ l = 0;
+ while (mdump != NULL) {
+ printf("\tmbuf x%x, m_len %d\n", mdump, mdump->m_len);
+ l += mdump->m_len;
+ mdump = mdump->m_next;
+ }
+ printf("\ttotal len: %d\n", l);
+ printf("clnp_comp_pdu: 2nd: [%d ... %d] (bytes %d)\n",
+ cf_next->cfr_first, cf_next->cfr_last, cf_next->cfr_bytes);
+ mdump = cf_next->cfr_data;
+ l = 0;
+ while (mdump != NULL) {
+ printf("\tmbuf x%x, m_len %d\n", mdump, mdump->m_len);
+ l += mdump->m_len;
+ mdump = mdump->m_next;
+ }
+ printf("\ttotal len: %d\n", l);
+ ENDDEBUG
+
+ cf->cfr_last = cf_next->cfr_last;
+ /*
+ * After this m_adj, the cf_next ptr is useless because we
+ * have adjusted the clnp_frag structure away...
+ */
+ IFDEBUG(D_REASS)
+ printf("clnp_comp_pdu: shaving off %d bytes\n",
+ cf_next_hdr.cfr_bytes);
+ ENDDEBUG
+ m_adj(cf_next_hdr.cfr_data, (int)cf_next_hdr.cfr_bytes);
+ m_cat(cf->cfr_data, cf_next_hdr.cfr_data);
+ cf->cfr_next = next_frag;
+ } else {
+ cf = cf->cfr_next;
+ }
+ }
+
+ cf = cfh->cfl_frags;
+
+ IFDEBUG(D_REASS)
+ struct mbuf *mdump = cf->cfr_data;
+ printf("clnp_comp_pdu: first frag now: [%d ... %d]\n", cf->cfr_first,
+ cf->cfr_last);
+ printf("clnp_comp_pdu: data for frag:\n");
+ while (mdump != NULL) {
+ printf("mbuf x%x, m_len %d\n", mdump, mdump->m_len);
+/* dump_buf(mtod(mdump, caddr_t), mdump->m_len);*/
+ mdump = mdump->m_next;
+ }
+ ENDDEBUG
+
+ /* Check if datagram is complete */
+ if ((cf->cfr_first == 0) && (cf->cfr_last == cfh->cfl_last)) {
+ /*
+ * We have a complete pdu!
+ * - Remove the frag header from (only) remaining fragment
+ * (which is not really a fragment anymore, as the datagram is
+ * complete).
+ * - Prepend a clnp header
+ */
+ struct mbuf *data = cf->cfr_data;
+ struct mbuf *hdr = cfh->cfl_orighdr;
+ struct clnp_fragl *scan;
+
+ IFDEBUG(D_REASS)
+ printf("clnp_comp_pdu: complete pdu!\n");
+ ENDDEBUG
+
+ m_adj(data, (int)cf->cfr_bytes);
+ m_cat(hdr, data);
+
+ IFDEBUG(D_DUMPIN)
+ struct mbuf *mdump = hdr;
+ printf("clnp_comp_pdu: pdu is:\n");
+ while (mdump != NULL) {
+ printf("mbuf x%x, m_len %d\n", mdump, mdump->m_len);
+/* dump_buf(mtod(mdump, caddr_t), mdump->m_len);*/
+ mdump = mdump->m_next;
+ }
+ ENDDEBUG
+
+ /*
+ * Remove cfh from the list of fragmented pdus
+ */
+ if (clnp_frags == cfh) {
+ clnp_frags = cfh->cfl_next;
+ } else {
+ for (scan = clnp_frags; scan != NULL; scan = scan->cfl_next) {
+ if (scan->cfl_next == cfh) {
+ scan->cfl_next = cfh->cfl_next;
+ break;
+ }
+ }
+ }
+
+ /* free cfh */
+ m_freem(dtom(cfh));
+
+ return(hdr);
+ }
+
+ return(NULL);
+}
+#ifdef TROLL
+static int troll_cnt;
+#include <sys/time.h>
+/*
+ * FUNCTION: troll_random
+ *
+ * PURPOSE: generate a pseudo-random number between 0 and 1
+ *
+ * RETURNS: the random number
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: This is based on the clock.
+ */
+float troll_random()
+{
+ extern struct timeval time;
+ long t = time.tv_usec % 100;
+
+ return((float)t / (float) 100);
+}
+
+/*
+ * FUNCTION: troll_output
+ *
+ * PURPOSE: Do something sneaky with the datagram passed. Possible
+ * operations are:
+ * Duplicate the packet
+ * Drop the packet
+ * Trim some number of bytes from the packet
+ * Munge some byte in the packet
+ *
+ * RETURNS: 0, or unix error code
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: The operation of this procedure is regulated by the
+ * troll control structure (Troll).
+ */
+troll_output(ifp, m, dst, rt)
+struct ifnet *ifp;
+struct mbuf *m;
+struct sockaddr *dst;
+struct rtentry *rt;
+{
+ int err = 0;
+ troll_cnt++;
+
+ if (trollctl.tr_ops & TR_DUPPKT) {
+ /*
+ * Duplicate every Nth packet
+ * TODO: random?
+ */
+ float f_freq = troll_cnt * trollctl.tr_dup_freq;
+ int i_freq = troll_cnt * trollctl.tr_dup_freq;
+ if (i_freq == f_freq) {
+ struct mbuf *dup = m_copy(m, 0, (int)M_COPYALL);
+ if (dup != NULL)
+ err = (*ifp->if_output)(ifp, dup, dst, rt);
+ }
+ if (!err)
+ err = (*ifp->if_output)(ifp, m, dst, rt);
+ return(err);
+ } else if (trollctl.tr_ops & TR_DROPPKT) {
+ } else if (trollctl.tr_ops & TR_CHANGE) {
+ struct clnp_fixed *clnp = mtod(m, struct clnp_fixed *);
+ clnp->cnf_cksum_msb = 0;
+ err = (*ifp->if_output)(ifp, m, dst, rt);
+ return(err);
+ } else {
+ err = (*ifp->if_output)(ifp, m, dst, rt);
+ return(err);
+ }
+}
+
+#endif /* TROLL */
diff --git a/sys/netiso/clnp_input.c b/sys/netiso/clnp_input.c
new file mode 100644
index 0000000..c49de95
--- /dev/null
+++ b/sys/netiso/clnp_input.c
@@ -0,0 +1,551 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clnp_input.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: /var/src/sys/netiso/RCS/clnp_input.c,v 5.1 89/02/09 16:20:32 hagens Exp $ */
+/* $Source: /var/src/sys/netiso/RCS/clnp_input.c,v $ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netiso/iso.h>
+#include <netiso/iso_var.h>
+#include <netiso/iso_snpac.h>
+#include <netiso/clnp.h>
+#include <netiso/clnl.h>
+#include <netiso/esis.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <netiso/eonvar.h>
+#include <netiso/clnp_stat.h>
+#include <netiso/argo_debug.h>
+
+#ifdef ISO
+u_char clnp_protox[ISOPROTO_MAX];
+struct clnl_protosw clnl_protox[256];
+int clnpqmaxlen = IFQ_MAXLEN; /* RAH? why is this a variable */
+struct mbuf *clnp_data_ck();
+
+int clnp_input();
+
+int esis_input();
+
+#ifdef ISO_X25ESIS
+int x25esis_input();
+#endif /* ISO_X25ESIS */
+
+/*
+ * FUNCTION: clnp_init
+ *
+ * PURPOSE: clnp initialization. Fill in clnp switch tables.
+ *
+ * RETURNS: none
+ *
+ * SIDE EFFECTS: fills in clnp_protox table with correct offsets into
+ * the isosw table.
+ *
+ * NOTES:
+ */
+clnp_init()
+{
+ register struct protosw *pr;
+
+ /*
+ * CLNP protox initialization
+ */
+ if ((pr = pffindproto(PF_ISO, ISOPROTO_RAW, SOCK_RAW)) == 0)
+ printf("clnl_init: no raw CLNP\n");
+ else
+ clnp_protox[ISOPROTO_RAW] = pr - isosw;
+
+ if ((pr = pffindproto(PF_ISO, ISOPROTO_TP, SOCK_SEQPACKET)) == 0)
+ printf("clnl_init: no tp/clnp\n");
+ else
+ clnp_protox[ISOPROTO_TP] = pr - isosw;
+
+ /*
+ * CLNL protox initialization
+ */
+ clnl_protox[ISO8473_CLNP].clnl_input = clnp_input;
+
+ clnlintrq.ifq_maxlen = clnpqmaxlen;
+}
+
+/*
+ * FUNCTION: clnlintr
+ *
+ * PURPOSE: Process a packet on the clnl input queue
+ *
+ * RETURNS: nothing.
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+clnlintr()
+{
+ register struct mbuf *m; /* ptr to first mbuf of pkt */
+ register struct clnl_fixed *clnl; /* ptr to fixed part of clnl hdr */
+ int s; /* save and restore priority */
+ struct clnl_protosw *clnlsw;/* ptr to protocol switch */
+ struct snpa_hdr sh; /* subnetwork hdr */
+
+ /*
+ * Get next datagram off clnl input queue
+ */
+next:
+ s = splimp();
+ /* IF_DEQUEUESNPAHDR(&clnlintrq, m, sh);*/
+ IF_DEQUEUE(&clnlintrq, m);
+ splx(s);
+
+
+ if (m == 0) /* nothing to do */
+ return;
+ if ((m->m_flags & M_PKTHDR) == 0 || m->m_pkthdr.rcvif == 0) {
+ m_freem(m);
+ goto next;
+ } else {
+ register struct ifaddr *ifa;
+ for (ifa = m->m_pkthdr.rcvif->if_addrlist; ifa; ifa = ifa->ifa_next)
+ if (ifa->ifa_addr->sa_family == AF_ISO)
+ break;
+ if (ifa == 0) {
+ m_freem(m);
+ goto next;
+ }
+ }
+ bzero((caddr_t)&sh, sizeof(sh));
+ sh.snh_flags = m->m_flags & (M_MCAST|M_BCAST);
+ switch((sh.snh_ifp = m->m_pkthdr.rcvif)->if_type) {
+ extern int ether_output();
+ case IFT_EON:
+ bcopy(mtod(m, caddr_t), (caddr_t)sh.snh_dhost, sizeof(u_long));
+ bcopy(sizeof(u_long) + mtod(m, caddr_t),
+ (caddr_t)sh.snh_shost, sizeof(u_long));
+ sh.snh_dhost[4] = mtod(m, u_char *)[sizeof(struct ip) +
+ _offsetof(struct eon_hdr, eonh_class)];
+ m->m_data += EONIPLEN;
+ m->m_len -= EONIPLEN;
+ m->m_pkthdr.len -= EONIPLEN;
+ break;
+
+ default:
+ if (sh.snh_ifp->if_output == ether_output) {
+ bcopy((caddr_t)(mtod(m, struct ether_header *)->ether_dhost),
+ (caddr_t)sh.snh_dhost, 2*sizeof(sh.snh_dhost));
+ m->m_data += sizeof (struct ether_header);
+ m->m_len -= sizeof (struct ether_header);
+ m->m_pkthdr.len -= sizeof (struct ether_header);
+ }
+ }
+ IFDEBUG(D_INPUT)
+ int i;
+ printf("clnlintr: src:");
+ for (i=0; i<6; i++)
+ printf("%x%c", sh.snh_shost[i] & 0xff, (i<5) ? ':' : ' ');
+ printf(" dst:");
+ for (i=0; i<6; i++)
+ printf("%x%c", sh.snh_dhost[i] & 0xff, (i<5) ? ':' : ' ');
+ printf("\n");
+ ENDDEBUG
+
+ /*
+ * Get the fixed part of the clnl header into the first mbuf.
+ * Drop the packet if this fails.
+ * Do not call m_pullup if we have a cluster mbuf or the
+ * data is not there.
+ */
+ if ((IS_CLUSTER(m) || (m->m_len < sizeof(struct clnl_fixed))) &&
+ ((m = m_pullup(m, sizeof(struct clnl_fixed))) == 0)) {
+ INCSTAT(cns_toosmall); /* TODO: use clnl stats */
+ goto next; /* m_pullup discards mbuf */
+ }
+
+ clnl = mtod(m, struct clnl_fixed *);
+
+ /*
+ * Drop packet if the length of the header is not reasonable.
+ */
+ if ((clnl->cnf_hdr_len < CLNP_HDR_MIN) ||
+ (clnl->cnf_hdr_len > CLNP_HDR_MAX)) {
+ INCSTAT(cns_badhlen); /* TODO: use clnl stats */
+ m_freem(m);
+ goto next;
+ }
+
+ /*
+ * If the header is not contained in this mbuf, make it so.
+ * Drop packet if this fails.
+ * Note: m_pullup will allocate a cluster mbuf if necessary
+ */
+ if (clnl->cnf_hdr_len > m->m_len) {
+ if ((m = m_pullup(m, (int)clnl->cnf_hdr_len)) == 0) {
+ INCSTAT(cns_badhlen); /* TODO: use clnl stats */
+ goto next; /* m_pullup discards mbuf */
+ }
+ clnl = mtod(m, struct clnl_fixed *);
+ }
+
+ clnlsw = &clnl_protox[clnl->cnf_proto_id];
+
+
+ if (clnlsw->clnl_input)
+ (*clnlsw->clnl_input) (m, &sh);
+ else
+ m_freem(m);
+
+ goto next;
+}
+
+/*
+ * FUNCTION: clnp_input
+ *
+ * PURPOSE: process an incoming clnp packet
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS: increments fields of clnp_stat structure.
+ *
+ * NOTES:
+ * TODO: I would like to make seg_part a pointer into the mbuf, but
+ * will it be correctly aligned?
+ */
+clnp_input(m, shp)
+struct mbuf *m; /* ptr to first mbuf of pkt */
+struct snpa_hdr *shp; /* subnetwork header */
+{
+ register struct clnp_fixed *clnp; /* ptr to fixed part of header */
+ struct sockaddr_iso source; /* source address of pkt */
+ struct sockaddr_iso target; /* destination address of pkt */
+#define src source.siso_addr
+#define dst target.siso_addr
+ caddr_t hoff; /* current offset in packet */
+ caddr_t hend; /* address of end of header info */
+ struct clnp_segment seg_part; /* segment part of hdr */
+ int seg_off=0; /* offset of segment part of hdr */
+ int seg_len;/* length of packet data&hdr in bytes */
+ struct clnp_optidx oidx, *oidxp = NULL; /* option index */
+ extern int iso_systype; /* used by ESIS config resp */
+ extern struct sockaddr_iso blank_siso; /* used for initializing */
+ int need_afrin = 0;
+ /* true if congestion experienced */
+ /* which means you need afrin nose */
+ /* spray. How clever! */
+
+ IFDEBUG(D_INPUT)
+ printf(
+ "clnp_input: proccessing dg; First mbuf m_len %d, m_type x%x, %s\n",
+ m->m_len, m->m_type, IS_CLUSTER(m) ? "cluster" : "normal");
+ ENDDEBUG
+ need_afrin = 0;
+
+ /*
+ * If no iso addresses have been set, there is nothing
+ * to do with the packet.
+ */
+ if (iso_ifaddr == NULL) {
+ clnp_discard(m, ADDR_DESTUNREACH);
+ return;
+ }
+
+ INCSTAT(cns_total);
+ clnp = mtod(m, struct clnp_fixed *);
+
+ IFDEBUG(D_DUMPIN)
+ struct mbuf *mhead;
+ int total_len = 0;
+ printf("clnp_input: clnp header:\n");
+ dump_buf(mtod(m, caddr_t), clnp->cnf_hdr_len);
+ printf("clnp_input: mbuf chain:\n");
+ for (mhead = m; mhead != NULL; mhead=mhead->m_next) {
+ printf("m x%x, len %d\n", mhead, mhead->m_len);
+ total_len += mhead->m_len;
+ }
+ printf("clnp_input: total length of mbuf chain %d:\n", total_len);
+ ENDDEBUG
+
+ /*
+ * Compute checksum (if necessary) and drop packet if
+ * checksum does not match
+ */
+ if (CKSUM_REQUIRED(clnp) && iso_check_csum(m, (int)clnp->cnf_hdr_len)) {
+ INCSTAT(cns_badcsum);
+ clnp_discard(m, GEN_BADCSUM);
+ return;
+ }
+
+ if (clnp->cnf_vers != ISO8473_V1) {
+ INCSTAT(cns_badvers);
+ clnp_discard(m, DISC_UNSUPPVERS);
+ return;
+ }
+
+
+ /* check mbuf data length: clnp_data_ck will free mbuf upon error */
+ CTOH(clnp->cnf_seglen_msb, clnp->cnf_seglen_lsb, seg_len);
+ if ((m = clnp_data_ck(m, seg_len)) == 0)
+ return;
+
+ clnp = mtod(m, struct clnp_fixed *);
+ hend = (caddr_t)clnp + clnp->cnf_hdr_len;
+
+ /*
+ * extract the source and destination address
+ * drop packet on failure
+ */
+ source = target = blank_siso;
+
+ hoff = (caddr_t)clnp + sizeof(struct clnp_fixed);
+ CLNP_EXTRACT_ADDR(dst, hoff, hend);
+ if (hoff == (caddr_t)0) {
+ INCSTAT(cns_badaddr);
+ clnp_discard(m, GEN_INCOMPLETE);
+ return;
+ }
+ CLNP_EXTRACT_ADDR(src, hoff, hend);
+ if (hoff == (caddr_t)0) {
+ INCSTAT(cns_badaddr);
+ clnp_discard(m, GEN_INCOMPLETE);
+ return;
+ }
+
+ IFDEBUG(D_INPUT)
+ printf("clnp_input: from %s", clnp_iso_addrp(&src));
+ printf(" to %s\n", clnp_iso_addrp(&dst));
+ ENDDEBUG
+
+ /*
+ * extract the segmentation information, if it is present.
+ * drop packet on failure
+ */
+ if (((clnp->cnf_type & CNF_TYPE) != CLNP_ER) &&
+ (clnp->cnf_type & CNF_SEG_OK)) {
+ if (hoff + sizeof(struct clnp_segment) > hend) {
+ INCSTAT(cns_noseg);
+ clnp_discard(m, GEN_INCOMPLETE);
+ return;
+ } else {
+ (void) bcopy(hoff, (caddr_t)&seg_part, sizeof(struct clnp_segment));
+ /* make sure segmentation fields are in host order */
+ seg_part.cng_id = ntohs(seg_part.cng_id);
+ seg_part.cng_off = ntohs(seg_part.cng_off);
+ seg_part.cng_tot_len = ntohs(seg_part.cng_tot_len);
+ seg_off = hoff - (caddr_t)clnp;
+ hoff += sizeof(struct clnp_segment);
+ }
+ }
+
+ /*
+ * process options if present. If clnp_opt_sanity returns
+ * false (indicating an error was found in the options) or
+ * an unsupported option was found
+ * then drop packet and emit an ER.
+ */
+ if (hoff < hend) {
+ int errcode;
+
+ oidxp = &oidx;
+ errcode = clnp_opt_sanity(m, hoff, hend-hoff, oidxp);
+
+ /* we do not support security */
+ if ((errcode == 0) && (oidxp->cni_securep))
+ errcode = DISC_UNSUPPSECURE;
+
+ /* the er option is valid with ER pdus only */
+ if ((errcode == 0) && (oidxp->cni_er_reason != ER_INVALREAS) &&
+ ((clnp->cnf_type & CNF_TYPE) != CLNP_ER))
+ errcode = DISC_UNSUPPOPT;
+
+#ifdef DECBIT
+ /* check if the congestion experienced bit is set */
+ if (oidxp->cni_qos_formatp) {
+ caddr_t qosp = CLNP_OFFTOOPT(m, oidxp->cni_qos_formatp);
+ u_char qos = *qosp;
+
+ need_afrin = ((qos & (CLNPOVAL_GLOBAL|CLNPOVAL_CONGESTED)) ==
+ (CLNPOVAL_GLOBAL|CLNPOVAL_CONGESTED));
+ if (need_afrin)
+ INCSTAT(cns_congest_rcvd);
+ }
+#endif /* DECBIT */
+
+ if (errcode != 0) {
+ clnp_discard(m, (char)errcode);
+ IFDEBUG(D_INPUT)
+ printf("clnp_input: dropped (err x%x) due to bad options\n",
+ errcode);
+ ENDDEBUG
+ return;
+ }
+ }
+
+ /*
+ * check if this packet is for us. if not, then forward
+ */
+ if (clnp_ours(&dst) == 0) {
+ IFDEBUG(D_INPUT)
+ printf("clnp_input: forwarding packet not for us\n");
+ ENDDEBUG
+ clnp_forward(m, seg_len, &dst, oidxp, seg_off, shp);
+ return;
+ }
+
+ /*
+ * ESIS Configuration Response Function
+ *
+ * If the packet received was sent to the multicast address
+ * all end systems, then send an esh to the source
+ */
+ if ((shp->snh_flags & M_MCAST) && (iso_systype == SNPA_ES)) {
+ extern short esis_holding_time;
+
+ esis_shoutput(shp->snh_ifp, ESIS_ESH, esis_holding_time,
+ shp->snh_shost, 6, &dst);
+ }
+
+ /*
+ * If this is a fragment, then try to reassemble it. If clnp_reass
+ * returns non NULL, the packet has been reassembled, and should
+ * be give to TP. Otherwise the fragment has been delt with
+ * by the reassembly code (either stored or deleted). In either case
+ * we should have nothing more to do with it.
+ */
+ if (((clnp->cnf_type & CNF_TYPE) != CLNP_ER) &&
+ (clnp->cnf_type & CNF_SEG_OK) &&
+ (seg_len != seg_part.cng_tot_len)) {
+ struct mbuf *m0;
+
+ if ((m0 = clnp_reass(m, &src, &dst, &seg_part)) != NULL) {
+ m = m0;
+ clnp = mtod(m, struct clnp_fixed *);
+ INCSTAT(cns_reassembled);
+ } else {
+ return;
+ }
+ }
+
+ /*
+ * give the packet to the higher layer
+ *
+ * Note: the total length of packet
+ * is the total length field of the segmentation part,
+ * or, if absent, the segment length field of the
+ * header.
+ */
+ INCSTAT(cns_delivered);
+ switch (clnp->cnf_type & CNF_TYPE) {
+ case CLNP_ER:
+ /*
+ * This ER must have the er option.
+ * If the option is not present, discard datagram.
+ */
+ if (oidxp == NULL || oidxp->cni_er_reason == ER_INVALREAS) {
+ clnp_discard(m, GEN_HDRSYNTAX);
+ } else {
+ clnp_er_input(m, &src, oidxp->cni_er_reason);
+ }
+ break;
+
+ case CLNP_DT:
+ (*isosw[clnp_protox[ISOPROTO_TP]].pr_input)(m, &source, &target,
+ clnp->cnf_hdr_len, need_afrin);
+ break;
+
+ case CLNP_RAW:
+ case CLNP_ECR:
+ IFDEBUG(D_INPUT)
+ printf("clnp_input: raw input of %d bytes\n",
+ clnp->cnf_type & CNF_SEG_OK ? seg_part.cng_tot_len : seg_len);
+ ENDDEBUG
+ (*isosw[clnp_protox[ISOPROTO_RAW]].pr_input)(m, &source, &target,
+ clnp->cnf_hdr_len);
+ break;
+
+ case CLNP_EC:
+ IFDEBUG(D_INPUT)
+ printf("clnp_input: echoing packet\n");
+ ENDDEBUG
+ (void)clnp_echoreply(m,
+ (clnp->cnf_type & CNF_SEG_OK ? (int)seg_part.cng_tot_len : seg_len),
+ &source, &target, oidxp);
+ break;
+
+ default:
+ printf("clnp_input: unknown clnp pkt type %d\n",
+ clnp->cnf_type & CNF_TYPE);
+ clnp_stat.cns_delivered--;
+ clnp_stat.cns_noproto++;
+ clnp_discard(m, GEN_HDRSYNTAX);
+ break;
+ }
+}
+#endif /* ISO */
diff --git a/sys/netiso/clnp_options.c b/sys/netiso/clnp_options.c
new file mode 100644
index 0000000..250b438
--- /dev/null
+++ b/sys/netiso/clnp_options.c
@@ -0,0 +1,532 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clnp_options.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: /var/src/sys/netiso/RCS/clnp_options.c,v 5.1 89/02/09 16:20:37 hagens Exp $ */
+/* $Source: /var/src/sys/netiso/RCS/clnp_options.c,v $ */
+
+#ifdef ISO
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netiso/iso.h>
+#include <netiso/clnp.h>
+#include <netiso/clnp_stat.h>
+#include <netiso/argo_debug.h>
+
+/*
+ * FUNCTION: clnp_update_srcrt
+ *
+ * PURPOSE: Process src rt option accompanying a clnp datagram.
+ * - bump src route ptr if src routing and
+ * we appear current in src route list.
+ *
+ * RETURNS: none
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: If source routing has been terminated, do nothing.
+ */
+clnp_update_srcrt(options, oidx)
+struct mbuf *options; /* ptr to options mbuf */
+struct clnp_optidx *oidx; /* ptr to option index */
+{
+ u_char len; /* length of current address */
+ struct iso_addr isoa; /* copy current address into here */
+
+ if (CLNPSRCRT_TERM(oidx, options)) {
+ IFDEBUG(D_OPTIONS)
+ printf("clnp_update_srcrt: src rt terminated\n");
+ ENDDEBUG
+ return;
+ }
+
+ len = CLNPSRCRT_CLEN(oidx, options);
+ bcopy(CLNPSRCRT_CADDR(oidx, options), (caddr_t)&isoa, len);
+ isoa.isoa_len = len;
+
+ IFDEBUG(D_OPTIONS)
+ printf("clnp_update_srcrt: current src rt: %s\n",
+ clnp_iso_addrp(&isoa));
+ ENDDEBUG
+
+ if (clnp_ours(&isoa)) {
+ IFDEBUG(D_OPTIONS)
+ printf("clnp_update_srcrt: updating src rt\n");
+ ENDDEBUG
+
+ /* update pointer to next src route */
+ len++; /* count length byte too! */
+ CLNPSRCRT_OFF(oidx, options) += len;
+ }
+}
+
+/*
+ * FUNCTION: clnp_dooptions
+ *
+ * PURPOSE: Process options accompanying a clnp datagram.
+ * Processing includes
+ * - log our address if recording route
+ *
+ * RETURNS: none
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+clnp_dooptions(options, oidx, ifp, isoa)
+struct mbuf *options; /* ptr to options mbuf */
+struct clnp_optidx *oidx; /* ptr to option index */
+struct ifnet *ifp; /* ptr to interface pkt is leaving on */
+struct iso_addr *isoa; /* ptr to our address for this ifp */
+{
+ /*
+ * If record route is specified, move all
+ * existing records over, and insert the address of
+ * interface passed
+ */
+ if (oidx->cni_recrtp) {
+ char *opt; /* ptr to beginning of recrt option */
+ u_char off; /* offset from opt of first free byte */
+ char *rec_start; /* beginning of new rt recorded */
+
+ opt = CLNP_OFFTOOPT(options, oidx->cni_recrtp);
+ off = *(opt + 1);
+ rec_start = opt + off - 1;
+
+ IFDEBUG(D_OPTIONS)
+ printf("clnp_dooptions: record route: option x%x for %d bytes\n",
+ opt, oidx->cni_recrt_len);
+ printf("\tfree slot offset x%x\n", off);
+ printf("clnp_dooptions: recording %s\n", clnp_iso_addrp(isoa));
+ printf("clnp_dooptions: option dump:\n");
+ dump_buf(opt, oidx->cni_recrt_len);
+ ENDDEBUG
+
+ /* proceed only if recording has not been terminated */
+ if (off != 0xff) {
+ int new_addrlen = isoa->isoa_len + 1;
+ /*
+ * if there is insufficient room to store the next address,
+ * then terminate recording. Plus 1 on isoa_len is for the
+ * length byte itself
+ */
+ if (oidx->cni_recrt_len - (off - 1) < new_addrlen) {
+ *(opt + 1) = 0xff; /* terminate recording */
+ } else {
+ IFDEBUG(D_OPTIONS)
+ printf("clnp_dooptions: new addr at x%x for %d\n",
+ rec_start, new_addrlen);
+ ENDDEBUG
+
+ bcopy((caddr_t)isoa, rec_start, new_addrlen);
+
+ /* update offset field */
+ *(opt + 1) += new_addrlen;
+
+ IFDEBUG(D_OPTIONS)
+ printf("clnp_dooptions: new option dump:\n");
+ dump_buf(opt, oidx->cni_recrt_len);
+ ENDDEBUG
+ }
+ }
+ }
+}
+
+/*
+ * FUNCTION: clnp_set_opts
+ *
+ * PURPOSE: Check the data mbuf passed for option sanity. If it is
+ * ok, then set the options ptr to address the data mbuf.
+ * If an options mbuf exists, free it. This implies that
+ * any old options will be lost. If data is NULL, simply
+ * free any old options.
+ *
+ * RETURNS: unix error code
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+clnp_set_opts(options, data)
+struct mbuf **options; /* target for option information */
+struct mbuf **data; /* source of option information */
+{
+ int error = 0; /* error return value */
+ struct clnp_optidx dummy; /* dummy index - not used */
+
+ /*
+ * remove any existing options
+ */
+ if (*options != NULL) {
+ m_freem(*options);
+ *options = NULL;
+ }
+
+ if (*data != NULL) {
+ /*
+ * Insure that the options are reasonable.
+ *
+ * Also, we do not support security, priority,
+ * nor do we allow one to send an ER option
+ *
+ * The QOS parameter is checked for the DECBIT.
+ */
+ if ((clnp_opt_sanity(*data, mtod(*data, caddr_t), (*data)->m_len,
+ &dummy) != 0) ||
+ (dummy.cni_securep) ||
+ (dummy.cni_priorp) ||
+ (dummy.cni_er_reason != ER_INVALREAS)) {
+ error = EINVAL;
+ } else {
+ *options = *data;
+ *data = NULL; /* so caller won't free mbuf @ *data */
+ }
+ }
+ return error;
+}
+
+/*
+ * FUNCTION: clnp_opt_sanity
+ *
+ * PURPOSE: Check the options (beginning at opts for len bytes) for
+ * sanity. In addition, fill in the option index structure
+ * in with information about each option discovered.
+ *
+ * RETURNS: success (options check out) - 0
+ * failure - an ER pdu error code describing failure
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: Each pointer field of the option index is filled in with
+ * the offset from the beginning of the mbuf data, not the
+ * actual address.
+ */
+clnp_opt_sanity(m, opts, len, oidx)
+struct mbuf *m; /* mbuf options reside in */
+caddr_t opts; /* ptr to buffer containing options */
+int len; /* length of buffer */
+struct clnp_optidx *oidx; /* RETURN: filled in with option idx info */
+{
+ u_char opcode; /* code of particular option */
+ u_char oplen; /* length of a particular option */
+ caddr_t opts_end; /* ptr to end of options */
+ u_char pad = 0, secure = 0, srcrt = 0, recrt = 0, qos = 0, prior = 0;
+ /* flags for catching duplicate options */
+
+ IFDEBUG(D_OPTIONS)
+ printf("clnp_opt_sanity: checking %d bytes of data:\n", len);
+ dump_buf(opts, len);
+ ENDDEBUG
+
+ /* clear option index field if passed */
+ bzero((caddr_t)oidx, sizeof(struct clnp_optidx));
+
+ /*
+ * We need to indicate whether the ER option is present. This is done
+ * by overloading the er_reason field to also indicate presense of
+ * the option along with the option value. I would like ER_INVALREAS
+ * to have value 0, but alas, 0 is a valid er reason...
+ */
+ oidx->cni_er_reason = ER_INVALREAS;
+
+ opts_end = opts + len;
+ while (opts < opts_end) {
+ /* must have at least 2 bytes per option (opcode and len) */
+ if (opts + 2 > opts_end)
+ return(GEN_INCOMPLETE);
+
+ opcode = *opts++;
+ oplen = *opts++;
+ IFDEBUG(D_OPTIONS)
+ printf("clnp_opt_sanity: opcode is %x and oplen %d\n",
+ opcode, oplen);
+ printf("clnp_opt_sanity: clnpoval_SRCRT is %x\n", CLNPOVAL_SRCRT);
+
+ switch (opcode) {
+ case CLNPOVAL_PAD: {
+ printf("CLNPOVAL_PAD\n");
+ } break;
+ case CLNPOVAL_SECURE: {
+ printf("CLNPOVAL_SECURE\n");
+ } break;
+ case CLNPOVAL_SRCRT: {
+ printf("CLNPOVAL_SRCRT\n");
+ } break;
+ case CLNPOVAL_RECRT: {
+ printf("CLNPOVAL_RECRT\n");
+ } break;
+ case CLNPOVAL_QOS: {
+ printf("CLNPOVAL_QOS\n");
+ } break;
+ case CLNPOVAL_PRIOR: {
+ printf("CLNPOVAL_PRIOR\n");
+ } break;
+ case CLNPOVAL_ERREAS: {
+ printf("CLNPOVAL_ERREAS\n");
+ } break;
+ default:
+ printf("UKNOWN option %x\n", opcode);
+ }
+ ENDDEBUG
+
+ /* don't allow crazy length values */
+ if (opts + oplen > opts_end)
+ return(GEN_INCOMPLETE);
+
+ switch (opcode) {
+ case CLNPOVAL_PAD:
+ /*
+ * Padding: increment pointer by length of padding
+ */
+ if (pad++) /* duplicate ? */
+ return(GEN_DUPOPT);
+ opts += oplen;
+ break;
+
+ case CLNPOVAL_SECURE: {
+ u_char format = *opts;
+
+ if (secure++) /* duplicate ? */
+ return(GEN_DUPOPT);
+ /*
+ * Security: high 2 bits of first octet indicate format
+ * (00 in high bits is reserved).
+ * Remaining bits must be 0. Remaining octets indicate
+ * actual security
+ */
+ if (((format & 0x3f) > 0) || /* low 6 bits set ? */
+ ((format & 0xc0) == 0)) /* high 2 bits zero ? */
+ return(GEN_HDRSYNTAX);
+
+ oidx->cni_securep = CLNP_OPTTOOFF(m, opts);
+ oidx->cni_secure_len = oplen;
+ opts += oplen;
+ } break;
+
+ case CLNPOVAL_SRCRT: {
+ u_char type, offset; /* type of rt, offset of start */
+ caddr_t route_end; /* address of end of route option */
+
+ IFDEBUG(D_OPTIONS)
+ printf("clnp_opt_sanity: SRC RT\n");
+ ENDDEBUG
+
+ if (srcrt++) /* duplicate ? */
+ return(GEN_DUPOPT);
+ /*
+ * source route: There must be 2 bytes following the length
+ * field: type and offset. The type must be either
+ * partial route or complete route. The offset field must
+ * be within the option. A single exception is made, however.
+ * The offset may be 1 greater than the length. This case
+ * occurs when the last source route record is consumed.
+ * In this case, we ignore the source route option.
+ * RAH? You should be able to set offset to 'ff' like in record
+ * route!
+ * Following this is a series of address fields.
+ * Each address field is composed of a (length, address) pair.
+ * Insure that the offset and each address length is reasonable
+ */
+ route_end = opts + oplen;
+
+ if (opts + 2 > route_end)
+ return(SRCRT_SYNTAX);
+
+ type = *opts;
+ offset = *(opts+1);
+
+
+ /* type must be partial or complete */
+ if (!((type == CLNPOVAL_PARTRT) || (type == CLNPOVAL_COMPRT)))
+ return(SRCRT_SYNTAX);
+
+ oidx->cni_srcrt_s = CLNP_OPTTOOFF(m, opts);
+ oidx->cni_srcrt_len = oplen;
+
+ opts += offset-1; /*set opts to first addr in rt */
+
+ /*
+ * Offset must be reasonable:
+ * less than end of options, or equal to end of options
+ */
+ if (opts >= route_end) {
+ if (opts == route_end) {
+ IFDEBUG(D_OPTIONS)
+ printf("clnp_opt_sanity: end of src route info\n");
+ ENDDEBUG
+ break;
+ } else
+ return(SRCRT_SYNTAX);
+ }
+
+ while (opts < route_end) {
+ u_char addrlen = *opts++;
+ if (opts + addrlen > route_end)
+ return(SRCRT_SYNTAX);
+ opts += addrlen;
+ }
+ } break;
+ case CLNPOVAL_RECRT: {
+ u_char type, offset; /* type of rt, offset of start */
+ caddr_t record_end; /* address of end of record option */
+
+ if (recrt++) /* duplicate ? */
+ return(GEN_DUPOPT);
+ /*
+ * record route: after the length field, expect a
+ * type and offset. Type must be partial or complete.
+ * Offset indicates where to start recording. Insure it
+ * is within the option. All ones for offset means
+ * recording is terminated.
+ */
+ record_end = opts + oplen;
+
+ oidx->cni_recrtp = CLNP_OPTTOOFF(m, opts);
+ oidx->cni_recrt_len = oplen;
+
+ if (opts + 2 > record_end)
+ return(GEN_INCOMPLETE);
+
+ type = *opts;
+ offset = *(opts+1);
+
+ /* type must be partial or complete */
+ if (!((type == CLNPOVAL_PARTRT) || (type == CLNPOVAL_COMPRT)))
+ return(GEN_HDRSYNTAX);
+
+ /* offset must be reasonable */
+ if ((offset < 0xff) && (opts + offset > record_end))
+ return(GEN_HDRSYNTAX);
+ opts += oplen;
+ } break;
+ case CLNPOVAL_QOS: {
+ u_char format = *opts;
+
+ if (qos++) /* duplicate ? */
+ return(GEN_DUPOPT);
+ /*
+ * qos: high 2 bits of first octet indicate format
+ * (00 in high bits is reserved).
+ * Remaining bits must be 0 (unless format indicates
+ * globally unique qos, in which case remaining bits indicate
+ * qos (except bit 6 which is reserved)). Otherwise,
+ * remaining octets indicate actual qos.
+ */
+ if (((format & 0xc0) == 0) || /* high 2 bits zero ? */
+ (((format & 0xc0) != CLNPOVAL_GLOBAL) &&
+ ((format & 0x3f) > 0))) /* not global,low bits used ? */
+ return(GEN_HDRSYNTAX);
+
+ oidx->cni_qos_formatp = CLNP_OPTTOOFF(m, opts);
+ oidx->cni_qos_len = oplen;
+
+ opts += oplen;
+ } break;
+
+ case CLNPOVAL_PRIOR: {
+ if (prior++) /* duplicate ? */
+ return(GEN_DUPOPT);
+ /*
+ * priority: value must be one byte long
+ */
+ if (oplen != 1)
+ return(GEN_HDRSYNTAX);
+
+ oidx->cni_priorp = CLNP_OPTTOOFF(m, opts);
+
+ opts += oplen;
+ } break;
+
+ case CLNPOVAL_ERREAS: {
+ /*
+ * er reason: value must be two bytes long
+ */
+ if (oplen != 2)
+ return(GEN_HDRSYNTAX);
+
+ oidx->cni_er_reason = *opts;
+
+ opts += oplen;
+ } break;
+
+ default: {
+ IFDEBUG(D_OPTIONS)
+ printf("clnp_opt_sanity: UNKNOWN OPTION 0x%x\n", opcode);
+ ENDDEBUG
+ return(DISC_UNSUPPOPT);
+ }
+ }
+ }
+ IFDEBUG(D_OPTIONS)
+ printf("clnp_opt_sanity: return(0)\n", opcode);
+ ENDDEBUG
+ return(0);
+}
+#endif /* ISO */
diff --git a/sys/netiso/clnp_output.c b/sys/netiso/clnp_output.c
new file mode 100644
index 0000000..aba9f6e
--- /dev/null
+++ b/sys/netiso/clnp_output.c
@@ -0,0 +1,561 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clnp_output.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: /var/src/sys/netiso/RCS/clnp_output.c,v 5.0 89/02/08 12:00:15 hagens Exp $ */
+/* $Source: /var/src/sys/netiso/RCS/clnp_output.c,v $ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netiso/iso.h>
+#include <netiso/iso_var.h>
+#include <netiso/iso_pcb.h>
+#include <netiso/clnp.h>
+#include <netiso/clnp_stat.h>
+#include <netiso/argo_debug.h>
+
+static struct clnp_fixed dt_template = {
+ ISO8473_CLNP, /* network identifier */
+ 0, /* length */
+ ISO8473_V1, /* version */
+ CLNP_TTL, /* ttl */
+ CLNP_DT|CNF_SEG_OK|CNF_ERR_OK, /* type */
+ 0, /* segment length */
+ 0 /* checksum */
+};
+
+static struct clnp_fixed raw_template = {
+ ISO8473_CLNP, /* network identifier */
+ 0, /* length */
+ ISO8473_V1, /* version */
+ CLNP_TTL, /* ttl */
+ CLNP_RAW|CNF_SEG_OK|CNF_ERR_OK, /* type */
+ 0, /* segment length */
+ 0 /* checksum */
+};
+
+static struct clnp_fixed echo_template = {
+ ISO8473_CLNP, /* network identifier */
+ 0, /* length */
+ ISO8473_V1, /* version */
+ CLNP_TTL, /* ttl */
+ CLNP_EC|CNF_SEG_OK|CNF_ERR_OK, /* type */
+ 0, /* segment length */
+ 0 /* checksum */
+};
+
+static struct clnp_fixed echor_template = {
+ ISO8473_CLNP, /* network identifier */
+ 0, /* length */
+ ISO8473_V1, /* version */
+ CLNP_TTL, /* ttl */
+ CLNP_ECR|CNF_SEG_OK|CNF_ERR_OK, /* type */
+ 0, /* segment length */
+ 0 /* checksum */
+};
+
+#ifdef DECBIT
+u_char qos_option[] = {CLNPOVAL_QOS, 1,
+ CLNPOVAL_GLOBAL|CLNPOVAL_SEQUENCING|CLNPOVAL_LOWDELAY};
+#endif /* DECBIT */
+
+int clnp_id = 0; /* id for segmented dgrams */
+
+/*
+ * FUNCTION: clnp_output
+ *
+ * PURPOSE: output the data in the mbuf as a clnp datagram
+ *
+ * The data specified by m0 is sent as a clnp datagram.
+ * The mbuf chain m0 will be freed when this routine has
+ * returned.
+ *
+ * If options is non-null, it points to an mbuf which contains
+ * options to be sent with the datagram. The options must
+ * be formatted in the mbuf according to clnp rules. Options
+ * will not be freed.
+ *
+ * Datalen specifies the length of the data in m0.
+ *
+ * Src and dst are the addresses for the packet.
+ *
+ * If route is non-null, it is used as the route for
+ * the packet.
+ *
+ * By default, a DT is sent. However, if flags & CNLP_SEND_ER
+ * then an ER will be sent. If flags & CLNP_SEND_RAW, then
+ * the packet will be send as raw clnp.
+ *
+ * RETURNS: 0 success
+ * appropriate error code
+ *
+ * SIDE EFFECTS: none
+ *
+ * NOTES:
+ * Flags are interpretated as follows:
+ * CLNP_NO_SEG - do not allow this pkt to be segmented.
+ * CLNP_NO_ER - have pkt request ER suppression.
+ * CLNP_SEND_RAW - send pkt as RAW DT rather than TP DT
+ * CLNP_NO_CKSUM - don't compute clnp checksum
+ * CLNP_ECHO - send as ECHO packet
+ *
+ * When checking for a cached packet, clnp checks
+ * that the route taken is still up. It does not
+ * check that the route is still to the same destination.
+ * This means that any entity that alters an existing
+ * route for an isopcb (such as when a redirect arrives)
+ * must invalidate the clnp cache. It might be perferable
+ * to have clnp check that the route has the same dest, but
+ * by avoiding this check, we save a call to iso_addrmatch1.
+ */
+clnp_output(m0, isop, datalen, flags)
+struct mbuf *m0; /* data for the packet */
+struct isopcb *isop; /* iso pcb */
+int datalen; /* number of bytes of data in m0 */
+int flags; /* flags */
+{
+ int error = 0; /* return value of function */
+ register struct mbuf *m = m0; /* mbuf for clnp header chain */
+ register struct clnp_fixed *clnp; /* ptr to fixed part of hdr */
+ register caddr_t hoff; /* offset into header */
+ int total_len; /* total length of packet */
+ struct iso_addr *src; /* ptr to source address */
+ struct iso_addr *dst; /* ptr to destination address */
+ struct clnp_cache clc; /* storage for cache information */
+ struct clnp_cache *clcp = NULL; /* ptr to clc */
+ int hdrlen = 0;
+
+ dst = &isop->isop_faddr->siso_addr;
+ if (isop->isop_laddr == 0) {
+ struct iso_ifaddr *ia = 0;
+ clnp_route(dst, &isop->isop_route, flags, 0, &ia);
+ if (ia == 0 || ia->ia_ifa.ifa_addr->sa_family != AF_ISO)
+ return (ENETUNREACH);
+ src = &ia->ia_addr.siso_addr;
+ } else
+ src = &isop->isop_laddr->siso_addr;
+
+ IFDEBUG(D_OUTPUT)
+ printf("clnp_output: to %s", clnp_iso_addrp(dst));
+ printf(" from %s of %d bytes\n", clnp_iso_addrp(src), datalen);
+ printf("\toptions x%x, flags x%x, isop_clnpcache x%x\n",
+ isop->isop_options, flags, isop->isop_clnpcache);
+ ENDDEBUG
+
+ if (isop->isop_clnpcache != NULL) {
+ clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
+ }
+
+ /*
+ * Check if cache is valid ...
+ */
+ IFDEBUG(D_OUTPUT)
+ printf("clnp_output: ck cache: clcp %x\n", clcp);
+ if (clcp != NULL) {
+ printf("\tclc_dst %s\n", clnp_iso_addrp(&clcp->clc_dst));
+ printf("\tisop_opts x%x, clc_opts x%x\n", isop->isop_options,
+ clcp->clc_options);
+ if (isop->isop_route.ro_rt)
+ printf("\tro_rt x%x, rt_flags x%x\n",
+ isop->isop_route.ro_rt, isop->isop_route.ro_rt->rt_flags);
+ printf("\tflags x%x, clc_flags x%x\n", flags, clcp->clc_flags);
+ printf("\tclc_hdr x%x\n", clcp->clc_hdr);
+ }
+ ENDDEBUG
+ if ((clcp != NULL) && /* cache exists */
+ (isop->isop_options == clcp->clc_options) && /* same options */
+ (iso_addrmatch1(dst, &clcp->clc_dst)) && /* dst still same */
+ (isop->isop_route.ro_rt != NULL) && /* route exists */
+ (isop->isop_route.ro_rt == clcp->clc_rt) && /* and is cached */
+ (isop->isop_route.ro_rt->rt_flags & RTF_UP) && /* route still up */
+ (flags == clcp->clc_flags) && /* same flags */
+ (clcp->clc_hdr != NULL)) { /* hdr mbuf exists */
+ /*
+ * The cache is valid
+ */
+
+ IFDEBUG(D_OUTPUT)
+ printf("clnp_output: using cache\n");
+ ENDDEBUG
+
+ m = m_copy(clcp->clc_hdr, 0, (int)M_COPYALL);
+ if (m == NULL) {
+ /*
+ * No buffers left to copy cached packet header. Use
+ * the cached packet header this time, and
+ * mark the hdr as vacant
+ */
+ m = clcp->clc_hdr;
+ clcp->clc_hdr = NULL;
+ }
+ m->m_next = m0; /* ASSUMES pkt hdr is 1 mbuf long */
+ clnp = mtod(m, struct clnp_fixed *);
+ } else {
+ struct clnp_optidx *oidx = NULL; /* index to clnp options */
+
+ /*
+ * The cache is not valid. Allocate an mbuf (if necessary)
+ * to hold cached info. If one is not available, then
+ * don't bother with the cache
+ */
+ INCSTAT(cns_cachemiss);
+ if (flags & CLNP_NOCACHE) {
+ clcp = &clc;
+ } else {
+ if (isop->isop_clnpcache == NULL) {
+ /*
+ * There is no clnpcache. Allocate an mbuf to hold one
+ */
+ if ((isop->isop_clnpcache = m_get(M_DONTWAIT, MT_HEADER))
+ == NULL) {
+ /*
+ * No mbufs available. Pretend that we don't want
+ * caching this time.
+ */
+ IFDEBUG(D_OUTPUT)
+ printf("clnp_output: no mbufs to allocate to cache\n");
+ ENDDEBUG
+ flags |= CLNP_NOCACHE;
+ clcp = &clc;
+ } else {
+ clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
+ }
+ } else {
+ /*
+ * A clnpcache mbuf exists. If the clc_hdr is not null,
+ * we must free it, as a new one is about to be created.
+ */
+ clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
+ if (clcp->clc_hdr != NULL) {
+ /*
+ * The clc_hdr is not null but a clnpcache mbuf exists.
+ * This means that there was a cache, but the existing
+ * copy of the hdr is no longer valid. Free it now
+ * before we lose the pointer to it.
+ */
+ IFDEBUG(D_OUTPUT)
+ printf("clnp_output: freeing old clc_hdr 0x%x\n",
+ clcp->clc_hdr);
+ ENDDEBUG
+ m_free(clcp->clc_hdr);
+ IFDEBUG(D_OUTPUT)
+ printf("clnp_output: freed old clc_hdr (done)\n");
+ ENDDEBUG
+ }
+ }
+ }
+ IFDEBUG(D_OUTPUT)
+ printf("clnp_output: NEW clcp x%x\n",clcp);
+ ENDDEBUG
+ bzero((caddr_t)clcp, sizeof(struct clnp_cache));
+
+ if (isop->isop_optindex)
+ oidx = mtod(isop->isop_optindex, struct clnp_optidx *);
+
+ /*
+ * Don't allow packets with security, quality of service,
+ * priority, or error report options to be sent.
+ */
+ if ((isop->isop_options) && (oidx)) {
+ if ((oidx->cni_securep) ||
+ (oidx->cni_priorp) ||
+ (oidx->cni_qos_formatp) ||
+ (oidx->cni_er_reason != ER_INVALREAS)) {
+ IFDEBUG(D_OUTPUT)
+ printf("clnp_output: pkt dropped - option unsupported\n");
+ ENDDEBUG
+ m_freem(m0);
+ return(EINVAL);
+ }
+ }
+
+ /*
+ * Don't allow any invalid flags to be set
+ */
+ if ((flags & (CLNP_VFLAGS)) != flags) {
+ IFDEBUG(D_OUTPUT)
+ printf("clnp_output: packet dropped - flags unsupported\n");
+ ENDDEBUG
+ INCSTAT(cns_odropped);
+ m_freem(m0);
+ return(EINVAL);
+ }
+
+ /*
+ * Don't allow funny lengths on dst; src may be zero in which
+ * case we insert the source address based upon the interface
+ */
+ if ((src->isoa_len > sizeof(struct iso_addr)) ||
+ (dst->isoa_len == 0) ||
+ (dst->isoa_len > sizeof(struct iso_addr))) {
+ m_freem(m0);
+ INCSTAT(cns_odropped);
+ return(ENAMETOOLONG);
+ }
+
+ /*
+ * Grab mbuf to contain header
+ */
+ MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (m == 0) {
+ m_freem(m0);
+ INCSTAT(cns_odropped);
+ return(ENOBUFS);
+ }
+ INCSTAT(cns_sent);
+ m->m_next = m0;
+ clnp = mtod(m, struct clnp_fixed *);
+ clcp->clc_segoff = 0;
+
+ /*
+ * Fill in all of fixed hdr except lengths and checksum
+ */
+ if (flags & CLNP_SEND_RAW) {
+ *clnp = raw_template;
+ } else if (flags & CLNP_ECHO) {
+ *clnp = echo_template;
+ } else if (flags & CLNP_ECHOR) {
+ *clnp = echor_template;
+ } else {
+ *clnp = dt_template;
+ }
+ if (flags & CLNP_NO_SEG)
+ clnp->cnf_type &= ~CNF_SEG_OK;
+ if (flags & CLNP_NO_ER)
+ clnp->cnf_type &= ~CNF_ERR_OK;
+
+ /*
+ * Route packet; special case for source rt
+ */
+ if ((isop->isop_options) && CLNPSRCRT_VALID(oidx)) {
+ IFDEBUG(D_OUTPUT)
+ printf("clnp_output: calling clnp_srcroute\n");
+ ENDDEBUG
+ error = clnp_srcroute(isop->isop_options, oidx, &isop->isop_route,
+ &clcp->clc_firsthop, &clcp->clc_ifa, dst);
+ } else {
+ IFDEBUG(D_OUTPUT)
+ ENDDEBUG
+ error = clnp_route(dst, &isop->isop_route, flags,
+ &clcp->clc_firsthop, &clcp->clc_ifa);
+ }
+ if (error || (clcp->clc_ifa == 0)) {
+ IFDEBUG(D_OUTPUT)
+ printf("clnp_output: route failed, errno %d\n", error);
+ printf("@clcp:\n");
+ dump_buf(clcp, sizeof (struct clnp_cache));
+ ENDDEBUG
+ goto bad;
+ }
+ clcp->clc_rt = isop->isop_route.ro_rt; /* XXX */
+ clcp->clc_ifp = clcp->clc_ifa->ia_ifp; /* XXX */
+
+ IFDEBUG(D_OUTPUT)
+ printf("clnp_output: packet routed to %s\n",
+ clnp_iso_addrp(
+ &((struct sockaddr_iso *)clcp->clc_firsthop)->siso_addr));
+ ENDDEBUG
+
+ /*
+ * If src address is not yet specified, use address of
+ * interface. NOTE: this will now update the laddr field in
+ * the isopcb. Is this desirable? RAH?
+ */
+ if (src->isoa_len == 0) {
+ src = &(clcp->clc_ifa->ia_addr.siso_addr);
+ IFDEBUG(D_OUTPUT)
+ printf("clnp_output: new src %s\n", clnp_iso_addrp(src));
+ ENDDEBUG
+ }
+
+ /*
+ * Insert the source and destination address,
+ */
+ hoff = (caddr_t)clnp + sizeof(struct clnp_fixed);
+ CLNP_INSERT_ADDR(hoff, *dst);
+ CLNP_INSERT_ADDR(hoff, *src);
+
+ /*
+ * Leave room for the segment part, if segmenting is selected
+ */
+ if (clnp->cnf_type & CNF_SEG_OK) {
+ clcp->clc_segoff = hoff - (caddr_t)clnp;
+ hoff += sizeof(struct clnp_segment);
+ }
+
+ clnp->cnf_hdr_len = m->m_len = (u_char)(hoff - (caddr_t)clnp);
+ hdrlen = clnp->cnf_hdr_len;
+
+#ifdef DECBIT
+ /*
+ * Add the globally unique QOS (with room for congestion experienced
+ * bit). I can safely assume that this option is not in the options
+ * mbuf below because I checked that the option was not specified
+ * previously
+ */
+ if ((m->m_len + sizeof(qos_option)) < MLEN) {
+ bcopy((caddr_t)qos_option, hoff, sizeof(qos_option));
+ clnp->cnf_hdr_len += sizeof(qos_option);
+ hdrlen += sizeof(qos_option);
+ m->m_len += sizeof(qos_option);
+ }
+#endif /* DECBIT */
+
+ /*
+ * If an options mbuf is present, concatenate a copy to the hdr mbuf.
+ */
+ if (isop->isop_options) {
+ struct mbuf *opt_copy = m_copy(isop->isop_options, 0, (int)M_COPYALL);
+ if (opt_copy == NULL) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ /* Link in place */
+ opt_copy->m_next = m->m_next;
+ m->m_next = opt_copy;
+
+ /* update size of header */
+ clnp->cnf_hdr_len += opt_copy->m_len;
+ hdrlen += opt_copy->m_len;
+ }
+
+ if (hdrlen > CLNP_HDR_MAX) {
+ error = EMSGSIZE;
+ goto bad;
+ }
+
+ /*
+ * Now set up the cache entry in the pcb
+ */
+ if ((flags & CLNP_NOCACHE) == 0) {
+ if (clcp->clc_hdr = m_copy(m, 0, (int)clnp->cnf_hdr_len)) {
+ clcp->clc_dst = *dst;
+ clcp->clc_flags = flags;
+ clcp->clc_options = isop->isop_options;
+ }
+ }
+ }
+ /*
+ * If small enough for interface, send directly
+ * Fill in segmentation part of hdr if using the full protocol
+ */
+ total_len = clnp->cnf_hdr_len + datalen;
+ if (clnp->cnf_type & CNF_SEG_OK) {
+ struct clnp_segment seg_part; /* segment part of hdr */
+ seg_part.cng_id = htons(clnp_id++);
+ seg_part.cng_off = htons(0);
+ seg_part.cng_tot_len = htons(total_len);
+ (void) bcopy((caddr_t)&seg_part, (caddr_t) clnp + clcp->clc_segoff,
+ sizeof(seg_part));
+ }
+ if (total_len <= SN_MTU(clcp->clc_ifp, clcp->clc_rt)) {
+ HTOC(clnp->cnf_seglen_msb, clnp->cnf_seglen_lsb, total_len);
+ m->m_pkthdr.len = total_len;
+ /*
+ * Compute clnp checksum (on header only)
+ */
+ if (flags & CLNP_NO_CKSUM) {
+ HTOC(clnp->cnf_cksum_msb, clnp->cnf_cksum_lsb, 0);
+ } else {
+ iso_gen_csum(m, CLNP_CKSUM_OFF, (int)clnp->cnf_hdr_len);
+ }
+
+ IFDEBUG(D_DUMPOUT)
+ struct mbuf *mdump = m;
+ printf("clnp_output: sending dg:\n");
+ while (mdump != NULL) {
+ dump_buf(mtod(mdump, caddr_t), mdump->m_len);
+ mdump = mdump->m_next;
+ }
+ ENDDEBUG
+
+ error = SN_OUTPUT(clcp, m);
+ goto done;
+ } else {
+ /*
+ * Too large for interface; fragment if possible.
+ */
+ error = clnp_fragment(clcp->clc_ifp, m, clcp->clc_firsthop,
+ total_len, clcp->clc_segoff, flags, clcp->clc_rt);
+ goto done;
+ }
+bad:
+ m_freem(m);
+done:
+ if (error) {
+ clnp_stat.cns_sent--;
+ clnp_stat.cns_odropped++;
+ }
+ return (error);
+}
+
+int clnp_ctloutput()
+{
+}
diff --git a/sys/netiso/clnp_raw.c b/sys/netiso/clnp_raw.c
new file mode 100644
index 0000000..0bc3dba
--- /dev/null
+++ b/sys/netiso/clnp_raw.c
@@ -0,0 +1,352 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clnp_raw.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: clnp_raw.c,v 4.2 88/06/29 14:58:56 hagens Exp $ */
+/* $Source: /usr/argo/sys/netiso/RCS/clnp_raw.c,v $ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/raw_cb.h>
+
+#include <netiso/iso.h>
+#include <netiso/iso_pcb.h>
+#include <netiso/clnp.h>
+#include <netiso/clnp_stat.h>
+#include <netiso/argo_debug.h>
+
+#include <netiso/tp_user.h> /* XXX -- defines SOL_NETWORK */
+
+struct sockproto rclnp_proto = { PF_ISO, 0 };
+/*
+ * FUNCTION: rclnp_input
+ *
+ * PURPOSE: Setup generic address an protocol structures for
+ * raw input routine, then pass them along with the
+ * mbuf chain.
+ *
+ * RETURNS: none
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: The protocol field of rclnp_proto is set to zero indicating
+ * no protocol.
+ */
+rclnp_input(m, src, dst, hdrlen)
+struct mbuf *m; /* ptr to packet */
+struct sockaddr_iso *src; /* ptr to src address */
+struct sockaddr_iso *dst; /* ptr to dest address */
+int hdrlen; /* length (in bytes) of clnp header */
+{
+#ifdef TROLL
+ if (trollctl.tr_ops & TR_CHUCK) {
+ m_freem(m);
+ return;
+ }
+#endif /* TROLL */
+
+ raw_input(m, &rclnp_proto, (struct sockaddr *)src, (struct sockaddr *)dst);
+}
+
+/*
+ * FUNCTION: rclnp_output
+ *
+ * PURPOSE: Prepare to send a raw clnp packet. Setup src and dest
+ * addresses, count the number of bytes to send, and
+ * call clnp_output.
+ *
+ * RETURNS: success - 0
+ * failure - an appropriate error code
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+rclnp_output(m0, so)
+struct mbuf *m0; /* packet to send */
+struct socket *so; /* socket to send from */
+{
+ register struct mbuf *m; /* used to scan a chain */
+ int len = 0; /* store length of chain here */
+ struct rawisopcb *rp = sotorawisopcb(so); /* ptr to raw cb */
+ int error; /* return value of function */
+ int flags; /* flags for clnp_output */
+
+ if (0 == (m0->m_flags & M_PKTHDR))
+ return (EINVAL);
+ /*
+ * Set up src address. If user has bound socket to an address, use it.
+ * Otherwise, do not specify src (clnp_output will fill it in).
+ */
+ if (rp->risop_rcb.rcb_laddr) {
+ if (rp->risop_isop.isop_sladdr.siso_family != AF_ISO) {
+bad:
+ m_freem(m0);
+ return(EAFNOSUPPORT);
+ }
+ }
+ /* set up dest address */
+ if (rp->risop_rcb.rcb_faddr == 0)
+ goto bad;
+ rp->risop_isop.isop_sfaddr =
+ *(struct sockaddr_iso *)rp->risop_rcb.rcb_faddr;
+ rp->risop_isop.isop_faddr = &rp->risop_isop.isop_sfaddr;
+
+ /* get flags and ship it off */
+ flags = rp->risop_flags & CLNP_VFLAGS;
+
+ error = clnp_output(m0, &rp->risop_isop, m0->m_pkthdr.len,
+ flags|CLNP_NOCACHE);
+
+ return (error);
+}
+
+/*
+ * FUNCTION: rclnp_ctloutput
+ *
+ * PURPOSE: Raw clnp socket option processing
+ * All options are stored inside an mbuf.
+ *
+ * RETURNS: success - 0
+ * failure - unix error code
+ *
+ * SIDE EFFECTS: If the options mbuf does not exist, it the mbuf passed
+ * is used.
+ *
+ * NOTES:
+ */
+rclnp_ctloutput(op, so, level, optname, m)
+int op; /* type of operation */
+struct socket *so; /* ptr to socket */
+int level; /* level of option */
+int optname; /* name of option */
+struct mbuf **m; /* ptr to ptr to option data */
+{
+ int error = 0;
+ register struct rawisopcb *rp = sotorawisopcb(so);/* raw cb ptr */
+
+ IFDEBUG(D_CTLOUTPUT)
+ printf("rclnp_ctloutput: op = x%x, level = x%x, name = x%x\n",
+ op, level, optname);
+ if (*m != NULL) {
+ printf("rclnp_ctloutput: %d bytes of mbuf data\n", (*m)->m_len);
+ dump_buf(mtod((*m), caddr_t), (*m)->m_len);
+ }
+ ENDDEBUG
+
+#ifdef SOL_NETWORK
+ if (level != SOL_NETWORK)
+ error = EINVAL;
+ else switch (op) {
+#else
+ switch (op) {
+#endif /* SOL_NETWORK */
+ case PRCO_SETOPT:
+ switch (optname) {
+ case CLNPOPT_FLAGS: {
+ u_short usr_flags;
+ /*
+ * Insure that the data passed has exactly one short in it
+ */
+ if ((*m == NULL) || ((*m)->m_len != sizeof(short))) {
+ error = EINVAL;
+ break;
+ }
+
+ /*
+ * Don't allow invalid flags to be set
+ */
+ usr_flags = (*mtod((*m), short *));
+
+ if ((usr_flags & (CLNP_VFLAGS)) != usr_flags) {
+ error = EINVAL;
+ } else
+ rp->risop_flags |= usr_flags;
+
+ } break;
+
+ case CLNPOPT_OPTS:
+ if (error = clnp_set_opts(&rp->risop_isop.isop_options, m))
+ break;
+ rp->risop_isop.isop_optindex = m_get(M_WAIT, MT_SOOPTS);
+ (void) clnp_opt_sanity(rp->risop_isop.isop_options,
+ mtod(rp->risop_isop.isop_options, caddr_t),
+ rp->risop_isop.isop_options->m_len,
+ mtod(rp->risop_isop.isop_optindex,
+ struct clnp_optidx *));
+ break;
+ }
+ break;
+
+ case PRCO_GETOPT:
+#ifdef notdef
+ /* commented out to keep hi C quiet */
+ switch (optname) {
+ default:
+ error = EINVAL;
+ break;
+ }
+#endif /* notdef */
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ if (op == PRCO_SETOPT) {
+ /* note: m_freem does not barf is *m is NULL */
+ m_freem(*m);
+ *m = NULL;
+ }
+
+ return error;
+}
+
+/*ARGSUSED*/
+clnp_usrreq(so, req, m, nam, control)
+ register struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *control;
+{
+ register int error = 0;
+ register struct rawisopcb *rp = sotorawisopcb(so);
+
+ rp = sotorawisopcb(so);
+ switch (req) {
+
+ case PRU_ATTACH:
+ if (rp)
+ panic("rip_attach");
+ MALLOC(rp, struct rawisopcb *, sizeof *rp, M_PCB, M_WAITOK);
+ if (rp == 0)
+ return (ENOBUFS);
+ bzero((caddr_t)rp, sizeof *rp);
+ so->so_pcb = (caddr_t)rp;
+ break;
+
+ case PRU_DETACH:
+ if (rp == 0)
+ panic("rip_detach");
+ if (rp->risop_isop.isop_options)
+ m_freem(rp->risop_isop.isop_options);
+ if (rp->risop_isop.isop_route.ro_rt)
+ RTFREE(rp->risop_isop.isop_route.ro_rt);
+ if (rp->risop_rcb.rcb_laddr)
+ rp->risop_rcb.rcb_laddr = 0;
+ /* free clnp cached hdr if necessary */
+ if (rp->risop_isop.isop_clnpcache != NULL) {
+ struct clnp_cache *clcp =
+ mtod(rp->risop_isop.isop_clnpcache, struct clnp_cache *);
+ if (clcp->clc_hdr != NULL) {
+ m_free(clcp->clc_hdr);
+ }
+ m_free(rp->risop_isop.isop_clnpcache);
+ }
+ if (rp->risop_isop.isop_optindex != NULL)
+ m_free(rp->risop_isop.isop_optindex);
+
+ break;
+
+ case PRU_BIND:
+ {
+ struct sockaddr_iso *addr = mtod(nam, struct sockaddr_iso *);
+
+ if (nam->m_len != sizeof(*addr))
+ return (EINVAL);
+ if ((ifnet == 0) ||
+ (addr->siso_family != AF_ISO) ||
+ (addr->siso_addr.isoa_len &&
+ ifa_ifwithaddr((struct sockaddr *)addr) == 0))
+ return (EADDRNOTAVAIL);
+ rp->risop_isop.isop_sladdr = *addr;
+ rp->risop_rcb.rcb_laddr = (struct sockaddr *)
+ (rp->risop_isop.isop_laddr = &rp->risop_isop.isop_sladdr);
+ return (0);
+ }
+ case PRU_CONNECT:
+ {
+ struct sockaddr_iso *addr = mtod(nam, struct sockaddr_iso *);
+
+ if ((nam->m_len > sizeof(*addr)) || (addr->siso_len > sizeof(*addr)))
+ return (EINVAL);
+ if (ifnet == 0)
+ return (EADDRNOTAVAIL);
+ if (addr->siso_family != AF_ISO)
+ rp->risop_isop.isop_sfaddr = *addr;
+ rp->risop_rcb.rcb_faddr = (struct sockaddr *)
+ (rp->risop_isop.isop_faddr = &rp->risop_isop.isop_sfaddr);
+ soisconnected(so);
+ return (0);
+ }
+ }
+ error = raw_usrreq(so, req, m, nam, control);
+
+ if (error && req == PRU_ATTACH && so->so_pcb)
+ free((caddr_t)rp, M_PCB);
+ return (error);
+}
diff --git a/sys/netiso/clnp_stat.h b/sys/netiso/clnp_stat.h
new file mode 100644
index 0000000..07cd72c
--- /dev/null
+++ b/sys/netiso/clnp_stat.h
@@ -0,0 +1,103 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clnp_stat.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: /var/src/sys/netiso/RCS/clnp_stat.h,v 5.1 89/02/09 16:20:42 hagens Exp $ */
+/* $Source: /var/src/sys/netiso/RCS/clnp_stat.h,v $ */
+
+
+#ifndef __CLNP_STAT__
+#define __CLNP_STAT__
+
+struct clnp_stat {
+ int cns_total; /* total pkts received */
+ int cns_toosmall; /* fixed part of header too small */
+ int cns_badhlen; /* header length is not reasonable */
+ int cns_badcsum; /* checksum on packet failed */
+ int cns_badaddr; /* address fields were not reasonable */
+ int cns_badvers; /* incorrect version */
+ int cns_noseg; /* segment information forgotten */
+ int cns_noproto; /* incorrect protocol id */
+ int cns_delivered; /* packets consumed by protocol */
+ int cns_ttlexpired; /* ttl has expired */
+ int cns_forward; /* forwarded packets */
+ int cns_sent; /* total packets sent */
+ int cns_odropped; /* o.k. packets discarded, e.g. ENOBUFS */
+ int cns_cantforward; /* non-forwarded packets */
+ int cns_fragmented; /* packets fragmented */
+ int cns_fragments; /* fragments received */
+ int cns_fragdropped; /* fragments discarded */
+ int cns_fragtimeout; /* fragments timed out */
+ int cns_ofragments; /* fragments generated */
+ int cns_cantfrag; /* fragmentation prohibited */
+ int cns_reassembled; /* packets reconstructed */
+ int cns_cachemiss; /* cache misses */
+ int cns_congest_set; /* congestion experienced bit set */
+ int cns_congest_rcvd; /* congestion experienced bit received */
+ int cns_er_inhist[CLNP_ERRORS + 1];
+ int cns_er_outhist[CLNP_ERRORS + 1];
+} clnp_stat ;
+
+#ifdef INCSTAT
+#undef INCSTAT
+#endif /* INCSTAT */
+#define INCSTAT(x) clnp_stat./**/x/**/++
+
+#endif /* __CLNP_STAT__ */
diff --git a/sys/netiso/clnp_subr.c b/sys/netiso/clnp_subr.c
new file mode 100644
index 0000000..c877811
--- /dev/null
+++ b/sys/netiso/clnp_subr.c
@@ -0,0 +1,658 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clnp_subr.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: /var/src/sys/netiso/RCS/clnp_subr.c,v 5.1 89/02/09 16:20:46 hagens Exp $ */
+/* $Source: /var/src/sys/netiso/RCS/clnp_subr.c,v $ */
+
+#ifdef ISO
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+#include <netiso/iso.h>
+#include <netiso/iso_var.h>
+#include <netiso/iso_pcb.h>
+#include <netiso/iso_snpac.h>
+#include <netiso/clnp.h>
+#include <netiso/clnp_stat.h>
+#include <netiso/argo_debug.h>
+
+/*
+ * FUNCTION: clnp_data_ck
+ *
+ * PURPOSE: Check that the amount of data in the mbuf chain is
+ * at least as much as the clnp header would have us
+ * expect. Trim mbufs if longer than expected, drop
+ * packet if shorter than expected.
+ *
+ * RETURNS: success - ptr to mbuf chain
+ * failure - 0
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+struct mbuf *
+clnp_data_ck(m, length)
+register struct mbuf *m; /* ptr to mbuf chain containing hdr & data */
+int length; /* length (in bytes) of packet */
+ {
+ register int len; /* length of data */
+ register struct mbuf *mhead; /* ptr to head of chain */
+
+ len = -length;
+ mhead = m;
+ for (;;) {
+ len += m->m_len;
+ if (m->m_next == 0)
+ break;
+ m = m->m_next;
+ }
+ if (len != 0) {
+ if (len < 0) {
+ INCSTAT(cns_toosmall);
+ clnp_discard(mhead, GEN_INCOMPLETE);
+ return 0;
+ }
+ if (len <= m->m_len)
+ m->m_len -= len;
+ else
+ m_adj(mhead, -len);
+ }
+ return mhead;
+}
+
+#ifdef notdef
+/*
+ * FUNCTION: clnp_extract_addr
+ *
+ * PURPOSE: Extract the source and destination address from the
+ * supplied buffer. Place them in the supplied address buffers.
+ * If insufficient data is supplied, then fail.
+ *
+ * RETURNS: success - Address of first byte in the packet past
+ * the address part.
+ * failure - 0
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+caddr_t
+clnp_extract_addr(bufp, buflen, srcp, destp)
+caddr_t bufp; /* ptr to buffer containing addresses */
+int buflen; /* length of buffer */
+register struct iso_addr *srcp; /* ptr to source address buffer */
+register struct iso_addr *destp; /* ptr to destination address buffer */
+ {
+ int len; /* argument to bcopy */
+
+ /*
+ * check that we have enough data. Plus1 is for length octet
+ */
+ if ((u_char)*bufp + 1 > buflen) {
+ return((caddr_t)0);
+ }
+ len = destp->isoa_len = (u_char)*bufp++;
+ (void) bcopy(bufp, (caddr_t)destp, len);
+ buflen -= len;
+ bufp += len;
+
+ /*
+ * check that we have enough data. Plus1 is for length octet
+ */
+ if ((u_char)*bufp + 1 > buflen) {
+ return((caddr_t)0);
+ }
+ len = srcp->isoa_len = (u_char)* bufp++;
+ (void) bcopy(bufp, (caddr_t)srcp, len);
+ bufp += len;
+
+ /*
+ * Insure that the addresses make sense
+ */
+ if (iso_ck_addr(srcp) && iso_ck_addr(destp))
+ return bufp;
+ else
+ return (caddr_t) 0;
+}
+#endif /* notdef */
+
+/*
+ * FUNCTION: clnp_ours
+ *
+ * PURPOSE: Decide whether the supplied packet is destined for
+ * us, or that it should be forwarded on.
+ *
+ * RETURNS: packet is for us - 1
+ * packet is not for us - 0
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+clnp_ours(dst)
+register struct iso_addr *dst; /* ptr to destination address */
+{
+ register struct iso_ifaddr *ia; /* scan through interface addresses */
+
+ for (ia = iso_ifaddr; ia; ia = ia->ia_next) {
+ IFDEBUG(D_ROUTE)
+ printf("clnp_ours: ia_sis x%x, dst x%x\n", &ia->ia_addr,
+ dst);
+ ENDDEBUG
+ /*
+ * XXX Warning:
+ * We are overloading siso_tlen in the if's address, as an nsel length.
+ */
+ if (dst->isoa_len == ia->ia_addr.siso_nlen &&
+ bcmp((caddr_t)ia->ia_addr.siso_addr.isoa_genaddr,
+ (caddr_t)dst->isoa_genaddr,
+ ia->ia_addr.siso_nlen - ia->ia_addr.siso_tlen) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+/* Dec bit set if ifp qlen is greater than congest_threshold */
+int congest_threshold = 0;
+
+/*
+ * FUNCTION: clnp_forward
+ *
+ * PURPOSE: Forward the datagram passed
+ * clnpintr guarantees that the header will be
+ * contigious (a cluster mbuf will be used if necessary).
+ *
+ * If oidx is NULL, no options are present.
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+clnp_forward(m, len, dst, oidx, seg_off, inbound_shp)
+struct mbuf *m; /* pkt to forward */
+int len; /* length of pkt */
+struct iso_addr *dst; /* destination address */
+struct clnp_optidx *oidx; /* option index */
+int seg_off;/* offset of segmentation part */
+struct snpa_hdr *inbound_shp; /* subnetwork header of inbound packet */
+{
+ struct clnp_fixed *clnp; /* ptr to fixed part of header */
+ int error; /* return value of route function */
+ struct sockaddr *next_hop; /* next hop for dgram */
+ struct ifnet *ifp; /* ptr to outgoing interface */
+ struct iso_ifaddr *ia = 0;/* ptr to iso name for ifp */
+ struct route_iso route; /* filled in by clnp_route */
+ extern int iso_systype;
+
+ clnp = mtod(m, struct clnp_fixed *);
+ bzero((caddr_t)&route, sizeof(route)); /* MUST be done before "bad:" */
+
+ /*
+ * Don't forward multicast or broadcast packets
+ */
+ if ((inbound_shp) && (IS_MULTICAST(inbound_shp->snh_dhost))) {
+ IFDEBUG(D_FORWARD)
+ printf("clnp_forward: dropping multicast packet\n");
+ ENDDEBUG
+ clnp->cnf_type &= ~CNF_ERR_OK; /* so we don't generate an ER */
+ clnp_discard(m, 0);
+ INCSTAT(cns_cantforward);
+ goto done;
+ }
+
+ IFDEBUG(D_FORWARD)
+ printf("clnp_forward: %d bytes, to %s, options x%x\n", len,
+ clnp_iso_addrp(dst), oidx);
+ ENDDEBUG
+
+ /*
+ * Decrement ttl, and if zero drop datagram
+ * Can't compare ttl as less than zero 'cause its a unsigned
+ */
+ if ((clnp->cnf_ttl == 0) || (--clnp->cnf_ttl == 0)) {
+ IFDEBUG(D_FORWARD)
+ printf("clnp_forward: discarding datagram because ttl is zero\n");
+ ENDDEBUG
+ INCSTAT(cns_ttlexpired);
+ clnp_discard(m, TTL_EXPTRANSIT);
+ goto done;
+ }
+ /*
+ * Route packet; special case for source rt
+ */
+ if CLNPSRCRT_VALID(oidx) {
+ /*
+ * Update src route first
+ */
+ clnp_update_srcrt(m, oidx);
+ error = clnp_srcroute(m, oidx, &route, &next_hop, &ia, dst);
+ } else {
+ error = clnp_route(dst, &route, 0, &next_hop, &ia);
+ }
+ if (error || ia == 0) {
+ IFDEBUG(D_FORWARD)
+ printf("clnp_forward: can't route packet (errno %d)\n", error);
+ ENDDEBUG
+ clnp_discard(m, ADDR_DESTUNREACH);
+ INCSTAT(cns_cantforward);
+ goto done;
+ }
+ ifp = ia->ia_ifp;
+
+ IFDEBUG(D_FORWARD)
+ printf("clnp_forward: packet routed to %s\n",
+ clnp_iso_addrp(&((struct sockaddr_iso *)next_hop)->siso_addr));
+ ENDDEBUG
+
+ INCSTAT(cns_forward);
+
+ /*
+ * If we are an intermediate system and
+ * we are routing outbound on the same ifp that the packet
+ * arrived upon, and we know the next hop snpa,
+ * then generate a redirect request
+ */
+ if ((iso_systype & SNPA_IS) && (inbound_shp) &&
+ (ifp == inbound_shp->snh_ifp))
+ esis_rdoutput(inbound_shp, m, oidx, dst, route.ro_rt);
+ /*
+ * If options are present, update them
+ */
+ if (oidx) {
+ struct iso_addr *mysrc = &ia->ia_addr.siso_addr;
+ if (mysrc == NULL) {
+ clnp_discard(m, ADDR_DESTUNREACH);
+ INCSTAT(cns_cantforward);
+ clnp_stat.cns_forward--;
+ goto done;
+ } else {
+ (void) clnp_dooptions(m, oidx, ifp, mysrc);
+ }
+ }
+
+#ifdef DECBIT
+ if (ifp->if_snd.ifq_len > congest_threshold) {
+ /*
+ * Congestion! Set the Dec Bit and thank Dave Oran
+ */
+ IFDEBUG(D_FORWARD)
+ printf("clnp_forward: congestion experienced\n");
+ ENDDEBUG
+ if ((oidx) && (oidx->cni_qos_formatp)) {
+ caddr_t qosp = CLNP_OFFTOOPT(m, oidx->cni_qos_formatp);
+ u_char qos = *qosp;
+ IFDEBUG(D_FORWARD)
+ printf("clnp_forward: setting congestion bit (qos x%x)\n", qos);
+ ENDDEBUG
+ if ((qos & CLNPOVAL_GLOBAL) == CLNPOVAL_GLOBAL) {
+ qos |= CLNPOVAL_CONGESTED;
+ INCSTAT(cns_congest_set);
+ *qosp = qos;
+ }
+ }
+ }
+#endif /* DECBIT */
+
+ /*
+ * Dispatch the datagram if it is small enough, otherwise fragment
+ */
+ if (len <= SN_MTU(ifp, route.ro_rt)) {
+ iso_gen_csum(m, CLNP_CKSUM_OFF, (int)clnp->cnf_hdr_len);
+ (void) (*ifp->if_output)(ifp, m, next_hop, route.ro_rt);
+ } else {
+ (void) clnp_fragment(ifp, m, next_hop, len, seg_off, /* flags */0, route.ro_rt);
+ }
+
+done:
+ /*
+ * Free route
+ */
+ if (route.ro_rt != NULL) {
+ RTFREE(route.ro_rt);
+ }
+}
+
+#ifdef notdef
+/*
+ * FUNCTION: clnp_insert_addr
+ *
+ * PURPOSE: Insert the address part into a clnp datagram.
+ *
+ * RETURNS: Address of first byte after address part in datagram.
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: Assume that there is enough space for the address part.
+ */
+caddr_t
+clnp_insert_addr(bufp, srcp, dstp)
+caddr_t bufp; /* address of where addr part goes */
+register struct iso_addr *srcp; /* ptr to src addr */
+register struct iso_addr *dstp; /* ptr to dst addr */
+{
+ *bufp++ = dstp->isoa_len;
+ (void) bcopy((caddr_t)dstp, bufp, dstp->isoa_len);
+ bufp += dstp->isoa_len;
+
+ *bufp++ = srcp->isoa_len;
+ (void) bcopy((caddr_t)srcp, bufp, srcp->isoa_len);
+ bufp += srcp->isoa_len;
+
+ return bufp;
+}
+
+#endif /* notdef */
+
+/*
+ * FUNCTION: clnp_route
+ *
+ * PURPOSE: Route a clnp datagram to the first hop toward its
+ * destination. In many cases, the first hop will be
+ * the destination. The address of a route
+ * is specified. If a routing entry is present in
+ * that route, and it is still up to the same destination,
+ * then no further action is necessary. Otherwise, a
+ * new routing entry will be allocated.
+ *
+ * RETURNS: route found - 0
+ * unix error code
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: It is up to the caller to free the routing entry
+ * allocated in route.
+ */
+clnp_route(dst, ro, flags, first_hop, ifa)
+ struct iso_addr *dst; /* ptr to datagram destination */
+ register struct route_iso *ro; /* existing route structure */
+ int flags; /* flags for routing */
+ struct sockaddr **first_hop; /* result: fill in with ptr to firsthop */
+ struct iso_ifaddr **ifa; /* result: fill in with ptr to interface */
+{
+ if (flags & SO_DONTROUTE) {
+ struct iso_ifaddr *ia;
+
+ if (ro->ro_rt) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = 0;
+ }
+ bzero((caddr_t)&ro->ro_dst, sizeof(ro->ro_dst));
+ bcopy((caddr_t)dst, (caddr_t)&ro->ro_dst.siso_addr,
+ 1 + (unsigned)dst->isoa_len);
+ ro->ro_dst.siso_family = AF_ISO;
+ ro->ro_dst.siso_len = sizeof(ro->ro_dst);
+ ia = iso_localifa(&ro->ro_dst);
+ if (ia == 0)
+ return EADDRNOTAVAIL;
+ if (ifa)
+ *ifa = ia;
+ if (first_hop)
+ *first_hop = (struct sockaddr *)&ro->ro_dst;
+ return 0;
+ }
+ /*
+ * If there is a cached route, check that it is still up and to
+ * the same destination. If not, free it and try again.
+ */
+ if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
+ (Bcmp(ro->ro_dst.siso_data, dst->isoa_genaddr, dst->isoa_len)))) {
+ IFDEBUG(D_ROUTE)
+ printf("clnp_route: freeing old route: ro->ro_rt 0x%x\n",
+ ro->ro_rt);
+ printf("clnp_route: old route refcnt: 0x%x\n",
+ ro->ro_rt->rt_refcnt);
+ ENDDEBUG
+
+ /* free old route entry */
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ } else {
+ IFDEBUG(D_ROUTE)
+ printf("clnp_route: OK route exists\n");
+ ENDDEBUG
+ }
+
+ if (ro->ro_rt == 0) {
+ /* set up new route structure */
+ bzero((caddr_t)&ro->ro_dst, sizeof(ro->ro_dst));
+ ro->ro_dst.siso_len = sizeof(ro->ro_dst);
+ ro->ro_dst.siso_family = AF_ISO;
+ Bcopy(dst, &ro->ro_dst.siso_addr, 1 + dst->isoa_len);
+ /* allocate new route */
+ IFDEBUG(D_ROUTE)
+ printf("clnp_route: allocating new route to %s\n",
+ clnp_iso_addrp(dst));
+ ENDDEBUG
+ rtalloc((struct route *)ro);
+ }
+ if (ro->ro_rt == 0)
+ return(ENETUNREACH); /* rtalloc failed */
+ ro->ro_rt->rt_use++;
+ if (ifa)
+ if ((*ifa = (struct iso_ifaddr *)ro->ro_rt->rt_ifa) == 0)
+ panic("clnp_route");
+ if (first_hop) {
+ if (ro->ro_rt->rt_flags & RTF_GATEWAY)
+ *first_hop = ro->ro_rt->rt_gateway;
+ else
+ *first_hop = (struct sockaddr *)&ro->ro_dst;
+ }
+ return(0);
+}
+
+/*
+ * FUNCTION: clnp_srcroute
+ *
+ * PURPOSE: Source route the datagram. If complete source
+ * routing is specified but not possible, then
+ * return an error. If src routing is terminated, then
+ * try routing on destination.
+ * Usage of first_hop,
+ * ifp, and error return is identical to clnp_route.
+ *
+ * RETURNS: 0 or unix error code
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: Remember that option index pointers are really
+ * offsets from the beginning of the mbuf.
+ */
+clnp_srcroute(options, oidx, ro, first_hop, ifa, final_dst)
+struct mbuf *options; /* ptr to options */
+struct clnp_optidx *oidx; /* index to options */
+struct route_iso *ro; /* route structure */
+struct sockaddr **first_hop; /* RETURN: fill in with ptr to firsthop */
+struct iso_ifaddr **ifa; /* RETURN: fill in with ptr to interface */
+struct iso_addr *final_dst; /* final destination */
+{
+ struct iso_addr dst; /* first hop specified by src rt */
+ int error = 0; /* return code */
+
+ /*
+ * Check if we have run out of routes
+ * If so, then try to route on destination.
+ */
+ if CLNPSRCRT_TERM(oidx, options) {
+ dst.isoa_len = final_dst->isoa_len;
+ bcopy(final_dst->isoa_genaddr, dst.isoa_genaddr, dst.isoa_len);
+ } else {
+ /*
+ * setup dst based on src rt specified
+ */
+ dst.isoa_len = CLNPSRCRT_CLEN(oidx, options);
+ bcopy(CLNPSRCRT_CADDR(oidx, options), dst.isoa_genaddr, dst.isoa_len);
+ }
+
+ /*
+ * try to route it
+ */
+ error = clnp_route(&dst, ro, 0, first_hop, ifa);
+ if (error != 0)
+ return error;
+
+ /*
+ * If complete src rt, first hop must be equal to dst
+ */
+ if ((CLNPSRCRT_TYPE(oidx, options) == CLNPOVAL_COMPRT) &&
+ (!iso_addrmatch1(&(*(struct sockaddr_iso **)first_hop)->siso_addr,&dst))){
+ IFDEBUG(D_OPTIONS)
+ printf("clnp_srcroute: complete src route failed\n");
+ ENDDEBUG
+ return EHOSTUNREACH; /* RAH? would like ESRCRTFAILED */
+ }
+
+ return error;
+}
+
+/*
+ * FUNCTION: clnp_echoreply
+ *
+ * PURPOSE: generate an echo reply packet and transmit
+ *
+ * RETURNS: result of clnp_output
+ *
+ * SIDE EFFECTS:
+ */
+clnp_echoreply(ec_m, ec_len, ec_src, ec_dst, ec_oidxp)
+struct mbuf *ec_m; /* echo request */
+int ec_len; /* length of ec */
+struct sockaddr_iso *ec_src; /* src of ec */
+struct sockaddr_iso *ec_dst; /* destination of ec (i.e., us) */
+struct clnp_optidx *ec_oidxp; /* options index to ec packet */
+{
+ struct isopcb isopcb;
+ int flags = CLNP_NOCACHE|CLNP_ECHOR;
+ int ret;
+
+ /* fill in fake isopcb to pass to output function */
+ bzero(&isopcb, sizeof(isopcb));
+ isopcb.isop_laddr = ec_dst;
+ isopcb.isop_faddr = ec_src;
+
+ /* forget copying the options for now. If implemented, need only
+ * copy record route option, but it must be reset to zero length */
+
+ ret = clnp_output(ec_m, &isopcb, ec_len, flags);
+
+ IFDEBUG(D_OUTPUT)
+ printf("clnp_echoreply: output returns %d\n", ret);
+ ENDDEBUG
+ return ret;
+}
+
+/*
+ * FUNCTION: clnp_badmtu
+ *
+ * PURPOSE: print notice of route with mtu not initialized.
+ *
+ * RETURNS: mtu of ifp.
+ *
+ * SIDE EFFECTS: prints notice, slows down system.
+ */
+clnp_badmtu(ifp, rt, line, file)
+struct ifnet *ifp; /* outgoing interface */
+struct rtentry *rt; /* dst route */
+int line; /* where the dirty deed occured */
+char *file; /* where the dirty deed occured */
+{
+ printf("sending on route 0x%x with no mtu, line %d of file %s\n",
+ rt, line, file);
+#ifdef ARGO_DEBUG
+ printf("route dst is ");
+ dump_isoaddr(rt_key(rt));
+#endif
+ return ifp->if_mtu;
+}
+
+/*
+ * FUNCTION: clnp_ypocb - backwards bcopy
+ *
+ * PURPOSE: bcopy starting at end of src rather than beginning.
+ *
+ * RETURNS: none
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: No attempt has been made to make this efficient
+ */
+clnp_ypocb(from, to, len)
+caddr_t from; /* src buffer */
+caddr_t to; /* dst buffer */
+u_int len; /* number of bytes */
+{
+ while (len--)
+ *(to + len) = *(from + len);
+}
+#endif /* ISO */
diff --git a/sys/netiso/clnp_timer.c b/sys/netiso/clnp_timer.c
new file mode 100644
index 0000000..718d530
--- /dev/null
+++ b/sys/netiso/clnp_timer.c
@@ -0,0 +1,180 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clnp_timer.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: clnp_timer.c,v 4.2 88/06/29 14:59:05 hagens Exp $ */
+/* $Source: /usr/argo/sys/netiso/RCS/clnp_timer.c,v $ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netiso/iso.h>
+#include <netiso/clnp.h>
+#include <netiso/clnp_stat.h>
+#include <netiso/argo_debug.h>
+
+extern struct clnp_fragl *clnp_frags;
+
+/*
+ * FUNCTION: clnp_freefrags
+ *
+ * PURPOSE: Free the resources associated with a fragment
+ *
+ * RETURNS: pointer to next fragment in list of fragments
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ * TODO: send ER back to source
+ */
+struct clnp_fragl *
+clnp_freefrags(cfh)
+register struct clnp_fragl *cfh; /* fragment header to delete */
+{
+ struct clnp_fragl *next = cfh->cfl_next;
+ struct clnp_frag *cf;
+
+ /* free any frags hanging around */
+ cf = cfh->cfl_frags;
+ while (cf != NULL) {
+ struct clnp_frag *cf_next = cf->cfr_next;
+ INCSTAT(cns_fragdropped);
+ m_freem(cf->cfr_data);
+ cf = cf_next;
+ }
+
+ /* free the copy of the header */
+ INCSTAT(cns_fragdropped);
+ m_freem(cfh->cfl_orighdr);
+
+ if (clnp_frags == cfh) {
+ clnp_frags = cfh->cfl_next;
+ } else {
+ struct clnp_fragl *scan;
+
+ for (scan = clnp_frags; scan != NULL; scan = scan->cfl_next) {
+ if (scan->cfl_next == cfh) {
+ scan->cfl_next = cfh->cfl_next;
+ break;
+ }
+ }
+ }
+
+ /* free the fragment header */
+ m_freem(dtom(cfh));
+
+ return(next);
+}
+
+/*
+ * FUNCTION: clnp_slowtimo
+ *
+ * PURPOSE: clnp timer processing; if the ttl expires on a
+ * packet on the reassembly queue, discard it.
+ *
+ * RETURNS: none
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+clnp_slowtimo()
+{
+ register struct clnp_fragl *cfh = clnp_frags;
+ int s = splnet();
+
+ while (cfh != NULL) {
+ if (--cfh->cfl_ttl == 0) {
+ cfh = clnp_freefrags(cfh);
+ INCSTAT(cns_fragtimeout);
+ } else {
+ cfh = cfh->cfl_next;
+ }
+ }
+ splx(s);
+}
+
+/*
+ * FUNCTION: clnp_drain
+ *
+ * PURPOSE: drain off all datagram fragments
+ *
+ * RETURNS: none
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ * TODO: should send back ER
+ */
+clnp_drain()
+{
+ register struct clnp_fragl *cfh = clnp_frags;
+
+ while (cfh != NULL)
+ cfh = clnp_freefrags(cfh);
+}
diff --git a/sys/netiso/cltp_usrreq.c b/sys/netiso/cltp_usrreq.c
new file mode 100644
index 0000000..93f8d1c
--- /dev/null
+++ b/sys/netiso/cltp_usrreq.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cltp_usrreq.c 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef CLTPOVAL_SRC /* XXX -- till files gets changed */
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netiso/argo_debug.h>
+#include <netiso/iso.h>
+#include <netiso/iso_pcb.h>
+#include <netiso/iso_var.h>
+#include <netiso/clnp.h>
+#include <netiso/cltp_var.h>
+#endif
+
+/*
+ * CLTP protocol implementation.
+ * Per ISO 8602, December, 1987.
+ */
+cltp_init()
+{
+
+ cltb.isop_next = cltb.isop_prev = &cltb;
+}
+
+int cltp_cksum = 1;
+
+
+/* ARGUSED */
+cltp_input(m0, srcsa, dstsa, cons_channel, output)
+ struct mbuf *m0;
+ struct sockaddr *srcsa, *dstsa;
+ u_int cons_channel;
+ int (*output)();
+{
+ register struct isopcb *isop;
+ register struct mbuf *m = m0;
+ register u_char *up = mtod(m, u_char *);
+ register struct sockaddr_iso *src = (struct sockaddr_iso *)srcsa;
+ int len, hdrlen = *up + 1, dlen = 0;
+ u_char *uplim = up + hdrlen;
+ caddr_t dtsap;
+
+ for (len = 0; m; m = m->m_next)
+ len += m->m_len;
+ up += 2; /* skip header */
+ while (up < uplim) switch (*up) { /* process options */
+ case CLTPOVAL_SRC:
+ src->siso_tlen = up[1];
+ src->siso_len = up[1] + TSEL(src) - (caddr_t)src;
+ if (src->siso_len < sizeof(*src))
+ src->siso_len = sizeof(*src);
+ else if (src->siso_len > sizeof(*src)) {
+ MGET(m, M_DONTWAIT, MT_SONAME);
+ if (m == 0)
+ goto bad;
+ m->m_len = src->siso_len;
+ src = mtod(m, struct sockaddr_iso *);
+ bcopy((caddr_t)srcsa, (caddr_t)src, srcsa->sa_len);
+ }
+ bcopy((caddr_t)up + 2, TSEL(src), up[1]);
+ up += 2 + src->siso_tlen;
+ continue;
+
+ case CLTPOVAL_DST:
+ dtsap = 2 + (caddr_t)up;
+ dlen = up[1];
+ up += 2 + dlen;
+ continue;
+
+ case CLTPOVAL_CSM:
+ if (iso_check_csum(m0, len)) {
+ cltpstat.cltps_badsum++;
+ goto bad;
+ }
+ up += 4;
+ continue;
+
+ default:
+ printf("clts: unknown option (%x)\n", up[0]);
+ cltpstat.cltps_hdrops++;
+ goto bad;
+ }
+ if (dlen == 0 || src->siso_tlen == 0)
+ goto bad;
+ for (isop = cltb.isop_next;; isop = isop->isop_next) {
+ if (isop == &cltb) {
+ cltpstat.cltps_noport++;
+ goto bad;
+ }
+ if (isop->isop_laddr &&
+ bcmp(TSEL(isop->isop_laddr), dtsap, dlen) == 0)
+ break;
+ }
+ m = m0;
+ m->m_len -= hdrlen;
+ m->m_data += hdrlen;
+ if (sbappendaddr(&isop->isop_socket->so_rcv, (struct sockaddr *)src,
+ m, (struct mbuf *)0) == 0)
+ goto bad;
+ cltpstat.cltps_ipackets++;
+ sorwakeup(isop->isop_socket);
+ m0 = 0;
+bad:
+ if (src != (struct sockaddr_iso *)srcsa)
+ m_freem(dtom(src));
+ if (m0)
+ m_freem(m0);
+ return 0;
+}
+
+/*
+ * Notify a cltp user of an asynchronous error;
+ * just wake up so that he can collect error status.
+ */
+cltp_notify(isop)
+ register struct isopcb *isop;
+{
+
+ sorwakeup(isop->isop_socket);
+ sowwakeup(isop->isop_socket);
+}
+
+cltp_ctlinput(cmd, sa)
+ int cmd;
+ struct sockaddr *sa;
+{
+ extern u_char inetctlerrmap[];
+ struct sockaddr_iso *siso;
+ int iso_rtchange();
+
+ if ((unsigned)cmd > PRC_NCMDS)
+ return;
+ if (sa->sa_family != AF_ISO && sa->sa_family != AF_CCITT)
+ return;
+ siso = (struct sockaddr_iso *)sa;
+ if (siso == 0 || siso->siso_nlen == 0)
+ return;
+
+ switch (cmd) {
+ case PRC_ROUTEDEAD:
+ case PRC_REDIRECT_NET:
+ case PRC_REDIRECT_HOST:
+ case PRC_REDIRECT_TOSNET:
+ case PRC_REDIRECT_TOSHOST:
+ iso_pcbnotify(&cltb, siso,
+ (int)inetctlerrmap[cmd], iso_rtchange);
+ break;
+
+ default:
+ if (inetctlerrmap[cmd] == 0)
+ return; /* XXX */
+ iso_pcbnotify(&cltb, siso, (int)inetctlerrmap[cmd],
+ cltp_notify);
+ }
+}
+
+cltp_output(isop, m)
+ register struct isopcb *isop;
+ register struct mbuf *m;
+{
+ register int len;
+ register struct sockaddr_iso *siso;
+ int hdrlen, error = 0, docsum;
+ register u_char *up;
+
+ if (isop->isop_laddr == 0 || isop->isop_faddr == 0) {
+ error = ENOTCONN;
+ goto bad;
+ }
+ /*
+ * Calculate data length and get a mbuf for CLTP header.
+ */
+ hdrlen = 2 + 2 + isop->isop_laddr->siso_tlen
+ + 2 + isop->isop_faddr->siso_tlen;
+ if (docsum = /*isop->isop_flags & CLNP_NO_CKSUM*/ cltp_cksum)
+ hdrlen += 4;
+ M_PREPEND(m, hdrlen, M_WAIT);
+ len = m->m_pkthdr.len;
+ /*
+ * Fill in mbuf with extended CLTP header
+ */
+ up = mtod(m, u_char *);
+ up[0] = hdrlen - 1;
+ up[1] = UD_TPDU_type;
+ up[2] = CLTPOVAL_SRC;
+ up[3] = (siso = isop->isop_laddr)->siso_tlen;
+ up += 4;
+ bcopy(TSEL(siso), (caddr_t)up, siso->siso_tlen);
+ up += siso->siso_tlen;
+ up[0] = CLTPOVAL_DST;
+ up[1] = (siso = isop->isop_faddr)->siso_tlen;
+ up += 2;
+ bcopy(TSEL(siso), (caddr_t)up, siso->siso_tlen);
+ /*
+ * Stuff checksum and output datagram.
+ */
+ if (docsum) {
+ up += siso->siso_tlen;
+ up[0] = CLTPOVAL_CSM;
+ up[1] = 2;
+ iso_gen_csum(m, 2 + up - mtod(m, u_char *), len);
+ }
+ cltpstat.cltps_opackets++;
+ return (tpclnp_output(isop, m, len, !docsum));
+bad:
+ m_freem(m);
+ return (error);
+}
+
+u_long cltp_sendspace = 9216; /* really max datagram size */
+u_long cltp_recvspace = 40 * (1024 + sizeof(struct sockaddr_iso));
+ /* 40 1K datagrams */
+
+
+/*ARGSUSED*/
+cltp_usrreq(so, req, m, nam, control)
+ struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *control;
+{
+ register struct isopcb *isop = sotoisopcb(so);
+ int s, error = 0;
+
+ if (req == PRU_CONTROL)
+ return (iso_control(so, (int)m, (caddr_t)nam,
+ (struct ifnet *)control));
+ if ((isop == NULL && req != PRU_ATTACH) ||
+ (control && control->m_len)) {
+ error = EINVAL;
+ goto release;
+ }
+ switch (req) {
+
+ case PRU_ATTACH:
+ if (isop != NULL) {
+ error = EINVAL;
+ break;
+ }
+ error = iso_pcballoc(so, &cltb);
+ if (error)
+ break;
+ error = soreserve(so, cltp_sendspace, cltp_recvspace);
+ if (error)
+ break;
+ break;
+
+ case PRU_DETACH:
+ iso_pcbdetach(isop);
+ break;
+
+ case PRU_BIND:
+ error = iso_pcbbind(isop, nam);
+ break;
+
+ case PRU_LISTEN:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_CONNECT:
+ if (isop->isop_faddr) {
+ error = EISCONN;
+ break;
+ }
+ error = iso_pcbconnect(isop, nam);
+ if (error == 0)
+ soisconnected(so);
+ break;
+
+ case PRU_CONNECT2:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_ACCEPT:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_DISCONNECT:
+ if (isop->isop_faddr == 0) {
+ error = ENOTCONN;
+ break;
+ }
+ iso_pcbdisconnect(isop);
+ so->so_state &= ~SS_ISCONNECTED; /* XXX */
+ break;
+
+ case PRU_SHUTDOWN:
+ socantsendmore(so);
+ break;
+
+ case PRU_SEND:
+ if (nam) {
+ if (isop->isop_faddr) {
+ error = EISCONN;
+ break;
+ }
+ /*
+ * Must block input while temporarily connected.
+ */
+ s = splnet();
+ error = iso_pcbconnect(isop, nam);
+ if (error) {
+ splx(s);
+ break;
+ }
+ } else {
+ if (isop->isop_faddr == 0) {
+ error = ENOTCONN;
+ break;
+ }
+ }
+ error = cltp_output(isop, m);
+ m = 0;
+ if (nam) {
+ iso_pcbdisconnect(isop);
+ splx(s);
+ }
+ break;
+
+ case PRU_ABORT:
+ soisdisconnected(so);
+ iso_pcbdetach(isop);
+ break;
+
+ case PRU_SOCKADDR:
+ if (isop->isop_laddr)
+ bcopy((caddr_t)isop->isop_laddr, mtod(m, caddr_t),
+ nam->m_len = isop->isop_laddr->siso_len);
+ break;
+
+ case PRU_PEERADDR:
+ if (isop->isop_faddr)
+ bcopy((caddr_t)isop->isop_faddr, mtod(m, caddr_t),
+ nam->m_len = isop->isop_faddr->siso_len);
+ break;
+
+ case PRU_SENSE:
+ /*
+ * stat: don't bother with a blocksize.
+ */
+ return (0);
+
+ case PRU_SENDOOB:
+ case PRU_FASTTIMO:
+ case PRU_SLOWTIMO:
+ case PRU_PROTORCV:
+ case PRU_PROTOSEND:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_RCVD:
+ case PRU_RCVOOB:
+ return (EOPNOTSUPP); /* do not free mbuf's */
+
+ default:
+ panic("cltp_usrreq");
+ }
+release:
+ if (control != NULL)
+ m_freem(control);
+ if (m != NULL)
+ m_freem(m);
+ return (error);
+}
diff --git a/sys/netiso/cltp_var.h b/sys/netiso/cltp_var.h
new file mode 100644
index 0000000..b4e08f2
--- /dev/null
+++ b/sys/netiso/cltp_var.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cltp_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+#define UD_TPDU_type 0x40 /* packet type */
+
+#define CLTPOVAL_SRC 0xc1 /* Source TSAP -- required */
+#define CLTPOVAL_DST 0xc2 /* Destination TSAP -- required */
+#define CLTPOVAL_CSM 0xc3 /* Checksum parameter -- optional */
+
+struct cltpstat {
+ int cltps_hdrops;
+ int cltps_badsum;
+ int cltps_badlen;
+ int cltps_noport;
+ int cltps_ipackets;
+ int cltps_opackets;
+};
+
+#ifdef KERNEL
+struct isopcb cltb;
+struct cltpstat cltpstat;
+#endif
diff --git a/sys/netiso/cons.h b/sys/netiso/cons.h
new file mode 100644
index 0000000..b0739de
--- /dev/null
+++ b/sys/netiso/cons.h
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cons.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * $Header: cons.h,v 4.4 88/09/09 19:01:28 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/cons.h,v $
+ *
+ * interface between TP and CONS
+ */
+
+#define CONSOPT_X25CRUD 0x01 /* set x.25 call request user data */
+
+struct dte_addr {
+ u_char dtea_addr[7];
+ u_char dtea_niblen;
+};
+
+#ifdef KERNEL
+
+#define CONN_OPEN 0x33
+#define CONN_CONFIRM 0x30
+#define CONN_REFUSE 0x31
+#define CONN_CLOSE 0x32
+
+#define CONS_IS_DGM 0x1
+#define CONS_NOT_DGM 0x0
+
+#ifndef PRC_NCMDS
+#include <sys/protosw.h>
+#endif /* PRC_NCMDS */
+
+#define PRC_CONS_SEND_DONE 2 /* something unused in protosw.h */
+
+#endif /* KERNEL */
diff --git a/sys/netiso/cons_pcb.h b/sys/netiso/cons_pcb.h
new file mode 100644
index 0000000..b8adc37
--- /dev/null
+++ b/sys/netiso/cons_pcb.h
@@ -0,0 +1,193 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cons_pcb.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: cons_pcb.h,v 4.2 88/06/29 14:59:08 hagens Exp $ */
+/* $Source: /usr/argo/sys/netiso/RCS/cons_pcb.h,v $ */
+
+/*
+ * protocol control block for the connection oriented network service
+ */
+
+/*
+ * legit port #s for cons "transport" are 0..23 for su users only, and
+ * 1024..1099 for public users
+ */
+#define X25_SBSIZE 512
+#define X25_PORT_RESERVED 24
+#define X25_PORT_USERMAX 1099
+#define X25_FACIL_LEN_MAX 109
+#define X25_PARTIAL_PKT_LEN_MAX (MLEN - sizeof(struct cons_pcb))
+
+#ifndef ARGO_DEBUG
+#define X25_TTL 600 /* 5 min */
+#else /* ARGO_DEBUG */
+#define X25_TTL 120 /* 1 min */
+#endif /* ARGO_DEBUG */
+
+struct cons_pcb {
+ struct isopcb _co_isopcb;
+#define co_next _co_isopcb.isop_next
+/* prev used for netstat only */
+#define co_prev _co_isopcb.isop_prev
+#define co_head _co_isopcb.isop_head
+#define co_laddr _co_isopcb.isop_laddr
+#define co_faddr _co_isopcb.isop_faddr
+#define co_lport _co_isopcb.isop_laddr.siso_tsuffix
+#define co_fport _co_isopcb.isop_faddr.siso_tsuffix
+#define co_route _co_isopcb.isop_route
+#define co_socket _co_isopcb.isop_socket
+#define co_chanmask _co_isopcb.isop_chanmask
+#define co_negchanmask _co_isopcb.isop_negchanmask
+#define co_x25crud _co_isopcb.isop_x25crud
+#define co_x25crud_len _co_isopcb.isop_x25crud_len
+ u_short co_state;
+ u_char co_flags;
+ u_short co_ttl; /* time to live timer */
+ u_short co_init_ttl; /* initial value of ttl */
+ int co_channel; /* logical channel */
+ struct ifnet * co_ifp; /* interface */
+ struct protosw *co_proto;
+
+ struct ifqueue co_pending; /* queue data to send when connection
+ completes*/
+#define MAX_DTE_LEN 0x7 /* 17 bcd digits */
+ struct dte_addr co_peer_dte;
+ struct cons_pcb *co_myself; /* DEBUGGING AID */
+};
+
+/*
+ * X.25 Packet types
+ */
+#define XPKT_DATA 1
+#define XPKT_INTERRUPT 2
+#define XPKT_FLOWCONTROL 3 /* not delivered? */
+
+/*
+ * pcb xtates
+ */
+
+#define CLOSED 0x0
+#define LISTENING 0x1
+#define CLOSING 0x2
+/* USABLE STATES MUST BE LAST */
+#define CONNECTING 0x3
+#define ACKWAIT 0x4
+#define OPEN 0x5
+#define MIN_USABLE_STATE CONNECTING
+
+#define cons_NSTATES 0x6
+
+
+/* type */
+#define CONSF_OCRE 0x40 /* created on OUTPUT */
+#define CONSF_ICRE 0x20 /* created on INPUT */
+#define CONSF_unused 0x10 /* not used */
+#define CONSF_unused2 0x08 /* not used */
+#define CONSF_DGM 0x04 /* for dgm use only */
+#define CONSF_XTS 0x02 /* for cons-as-transport-service */
+#define CONSF_LOOPBACK 0x01 /* loopback was on when connection commenced */
+
+#define X_NOCHANNEL 0x80
+
+
+struct cons_stat {
+ u_int co_intr; /* input from eicon board */
+ u_int co_restart; /* ecn_restart() request issued to board */
+ u_int co_slowtimo; /* times slowtimo called */
+ u_int co_timedout; /* connections closed by slowtimo */
+ u_int co_ack; /* ECN_ACK indication came from eicon board */
+ u_int co_receive; /* ECN_RECEIVE indication came from eicon board */
+ u_int co_send; /* ECN_SEND request issued to board */
+ u_int co_reset_in; /* ECN_RESET indication came from eicon board */
+ u_int co_reset_out; /* ECN_RESET issued to the eicon board */
+ u_int co_clear_in; /* ECN_CLEAR indication came from eicon board */
+ u_int co_clear_out; /* ECN_CLEAR request issued to board */
+ u_int co_refuse; /* ECN_REFUSE indication came from eicon board */
+ u_int co_accept; /* ECN_ACCEPT indication came from eicon board */
+ u_int co_connect; /* ECN_CONNECT indication came from eicon board */
+ u_int co_call; /* ECN_CALL request issued to board */
+ u_int co_Rdrops; /* bad pkt came from ll */
+ u_int co_Xdrops; /* can't keep up */
+
+ u_int co_intrpt_pkts_in; /* interrupt packets in */
+ u_int co_avg_qlen;
+ u_int co_avg_qdrop;
+ u_int co_active;
+
+ u_int co_noresources;
+ u_int co_parse_facil_err;
+ u_int co_addr_proto_consist_err;
+ u_int co_no_copcb;
+} cons_stat;
+
+u_char x25_error_stats[CONL_ERROR_MAX + 1];
+
+struct ifqueue consintrq;
+
+/* reasons for clear are in a data mbuf chained to a clear ecn_request */
+struct e_clear_data {
+ u_char ecd_cause;
+ u_char ecd_diagnostic;
+};
+
+#ifdef KERNEL
+#define IncStat(XYZ) cons_stat.XYZ++
+#endif /* KERNEL */
diff --git a/sys/netiso/eonvar.h b/sys/netiso/eonvar.h
new file mode 100644
index 0000000..93f9917
--- /dev/null
+++ b/sys/netiso/eonvar.h
@@ -0,0 +1,170 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)eonvar.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+
+#define EON_986_VERSION 0x3
+#define EON_VERSION 0x1
+
+#define EON_CACHESIZE 30
+
+#define E_FREE 1
+#define E_LINK 2
+#define E_ES 3
+#define E_IS 4
+
+
+/*
+ * this overlays a sockaddr_iso
+ */
+
+struct sockaddr_eon {
+ u_char seon_len; /* Length */
+ u_char seon_family; /* AF_ISO */
+ u_char seon_status; /* overlays session suffixlen */
+#define EON_ESLINK_UP 0x1
+#define EON_ESLINK_DOWN 0x2
+#define EON_ISLINK_UP 0x10
+#define EON_ISLINK_DOWN 0x20
+/* no change is neither up or down */
+ u_char seon_pad1; /* 0, overlays tsfxlen */
+ u_char seon_adrlen;
+ u_char seon_afi; /* 47 */
+ u_char seon_idi[2]; /* 0006 */
+ u_char seon_vers; /* 03 */
+ u_char seon_glbnum[2]; /* see RFC 1069 */
+ u_char seon_RDN[2]; /* see RFC 1070 */
+ u_char seon_pad2[3]; /* see RFC 1070 */
+ u_char seon_LAREA[2]; /* see RFC 1070 */
+ u_char seon_pad3[2]; /* see RFC 1070 */
+ /* right now ip addr is aligned -- be careful --
+ * future revisions may have it u_char[4]
+ */
+ u_int seon_ipaddr; /* a.b.c.d */
+ u_char seon_protoid; /* NSEL */
+};
+
+#ifdef EON_TEMPLATE
+struct sockaddr_eon eon_template = {
+ sizeof (eon_template), AF_ISO, 0, 0, 0x14,
+ 0x47, 0x0, 0x6, 0x3, 0
+};
+#endif
+
+#define DOWNBITS ( EON_ESLINK_DOWN | EON_ISLINK_DOWN )
+#define UPBITS ( EON_ESLINK_UP | EON_ISLINK_UP )
+
+#define SIOCSEONCORE _IOWR('i',10, struct iso_ifreq) /* EON core member */
+#define SIOCGEONCORE _IOWR('i',11, struct iso_ifreq) /* EON core member */
+
+struct eon_hdr {
+ u_char eonh_vers; /* value 1 */
+ u_char eonh_class; /* address multicast class, below */
+#define EON_NORMAL_ADDR 0x0
+#define EON_MULTICAST_ES 0x1
+#define EON_MULTICAST_IS 0x2
+#define EON_BROADCAST 0x3
+ u_short eonh_csum; /* osi checksum (choke)*/
+};
+struct eon_iphdr {
+ struct ip ei_ip;
+ struct eon_hdr ei_eh;
+};
+#define EONIPLEN (sizeof(struct eon_hdr) + sizeof(struct ip))
+
+/* stole these 2 fields of the flags for I-am-ES and I-am-IS */
+#define IFF_ES 0x400
+#define IFF_IS 0x800
+
+struct eon_stat {
+ int es_in_multi_es;
+ int es_in_multi_is;
+ int es_in_broad;
+ int es_in_normal;
+ int es_out_multi_es;
+ int es_out_multi_is;
+ int es_out_broad;
+ int es_out_normal;
+ int es_ipout;
+
+ int es_icmp[PRC_NCMDS];
+ /* errors */
+ int es_badcsum;
+ int es_badhdr;
+} eonstat;
+
+#undef IncStat
+#define IncStat(xxx) eonstat.xxx++
+
+typedef struct qhdr {
+ struct qhdr *link, *rlink;
+} *queue_t;
+
+struct eon_llinfo {
+ struct qhdr el_qhdr; /* keep all in a list */
+ int el_flags; /* cache valid ? */
+ int el_snpaoffset; /* IP address contained in dst nsap */
+ struct rtentry *el_rt; /* back pointer to parent route */
+ struct eon_iphdr el_ei; /* precomputed portion of hdr */
+ struct route el_iproute; /* if direct route cache IP info */
+ /* if gateway, cache secondary route */
+};
+#define el_iphdr el_ei.ei_ip
+#define el_eonhdr el_ei.ei_eh
diff --git a/sys/netiso/esis.c b/sys/netiso/esis.c
new file mode 100644
index 0000000..f4ade0f
--- /dev/null
+++ b/sys/netiso/esis.c
@@ -0,0 +1,1063 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)esis.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+
+#ifdef ISO
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <net/raw_cb.h>
+
+#include <netiso/iso.h>
+#include <netiso/iso_pcb.h>
+#include <netiso/iso_var.h>
+#include <netiso/iso_snpac.h>
+#include <netiso/clnl.h>
+#include <netiso/clnp.h>
+#include <netiso/clnp_stat.h>
+#include <netiso/esis.h>
+#include <netiso/argo_debug.h>
+
+/*
+ * Global variables to esis implementation
+ *
+ * esis_holding_time - the holding time (sec) parameter for outgoing pdus
+ * esis_config_time - the frequency (sec) that hellos are generated
+ * esis_esconfig_time - suggested es configuration time placed in the
+ * ish.
+ *
+ */
+struct rawcb esis_pcb;
+void esis_config(), snpac_age();
+int esis_sendspace = 2048;
+int esis_recvspace = 2048;
+short esis_holding_time = ESIS_HT;
+short esis_config_time = ESIS_CONFIG;
+short esis_esconfig_time = ESIS_CONFIG;
+extern int iso_systype;
+struct sockaddr_dl esis_dl = { sizeof(esis_dl), AF_LINK };
+extern char all_es_snpa[], all_is_snpa[];
+
+#define EXTEND_PACKET(m, mhdr, cp)\
+ if (((m)->m_next = m_getclr(M_DONTWAIT, MT_HEADER)) == NULL) {\
+ esis_stat.es_nomem++;\
+ m_freem(mhdr);\
+ return;\
+ } else {\
+ (m) = (m)->m_next;\
+ (cp) = mtod((m), caddr_t);\
+ }
+/*
+ * FUNCTION: esis_init
+ *
+ * PURPOSE: Initialize the kernel portion of esis protocol
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+esis_init()
+{
+ extern struct clnl_protosw clnl_protox[256];
+ int esis_input(), isis_input();
+#ifdef ISO_X25ESIS
+ int x25esis_input();
+#endif /* ISO_X25ESIS */
+
+ esis_pcb.rcb_next = esis_pcb.rcb_prev = &esis_pcb;
+ llinfo_llc.lc_next = llinfo_llc.lc_prev = &llinfo_llc;
+
+ timeout(snpac_age, (caddr_t)0, hz);
+ timeout(esis_config, (caddr_t)0, hz);
+
+ clnl_protox[ISO9542_ESIS].clnl_input = esis_input;
+ clnl_protox[ISO10589_ISIS].clnl_input = isis_input;
+#ifdef ISO_X25ESIS
+ clnl_protox[ISO9542X25_ESIS].clnl_input = x25esis_input;
+#endif /* ISO_X25ESIS */
+}
+
+/*
+ * FUNCTION: esis_usrreq
+ *
+ * PURPOSE: Handle user level esis requests
+ *
+ * RETURNS: 0 or appropriate errno
+ *
+ * SIDE EFFECTS:
+ *
+ */
+/*ARGSUSED*/
+esis_usrreq(so, req, m, nam, control)
+struct socket *so; /* socket: used only to get to this code */
+int req; /* request */
+struct mbuf *m; /* data for request */
+struct mbuf *nam; /* optional name */
+struct mbuf *control; /* optional control */
+{
+ struct rawcb *rp = sotorawcb(so);
+ int error = 0;
+
+ if ((so->so_state & SS_PRIV) == 0) {
+ error = EACCES;
+ goto release;
+ }
+ if (rp == NULL && req != PRU_ATTACH) {
+ error = EINVAL;
+ goto release;
+ }
+
+ switch (req) {
+ case PRU_ATTACH:
+ if (rp != NULL) {
+ error = EINVAL;
+ break;
+ }
+ MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK);
+ if (so->so_pcb = (caddr_t)rp) {
+ bzero(so->so_pcb, sizeof(*rp));
+ insque(rp, &esis_pcb);
+ rp->rcb_socket = so;
+ error = soreserve(so, esis_sendspace, esis_recvspace);
+ } else
+ error = ENOBUFS;
+ break;
+
+ case PRU_SEND:
+ if (nam == NULL) {
+ error = EINVAL;
+ break;
+ }
+ /* error checking here */
+ error = isis_output(mtod(nam,struct sockaddr_dl *), m);
+ m = NULL;
+ break;
+
+ case PRU_DETACH:
+ raw_detach(rp);
+ break;
+
+ case PRU_SHUTDOWN:
+ socantsendmore(so);
+ break;
+
+ case PRU_ABORT:
+ soisdisconnected(so);
+ raw_detach(rp);
+ break;
+
+ case PRU_SENSE:
+ return (0);
+
+ default:
+ return (EOPNOTSUPP);
+ }
+release:
+ if (m != NULL)
+ m_freem(m);
+
+ return (error);
+}
+
+/*
+ * FUNCTION: esis_input
+ *
+ * PURPOSE: Process an incoming esis packet
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+esis_input(m0, shp)
+struct mbuf *m0; /* ptr to first mbuf of pkt */
+struct snpa_hdr *shp; /* subnetwork header */
+{
+ register struct esis_fixed *pdu = mtod(m0, struct esis_fixed *);
+ register int type;
+
+ /*
+ * check checksum if necessary
+ */
+ if (ESIS_CKSUM_REQUIRED(pdu) && iso_check_csum(m0, (int)pdu->esis_hdr_len)) {
+ esis_stat.es_badcsum++;
+ goto bad;
+ }
+
+ /* check version */
+ if (pdu->esis_vers != ESIS_VERSION) {
+ esis_stat.es_badvers++;
+ goto bad;
+ }
+ type = pdu->esis_type & 0x1f;
+ switch (type) {
+ case ESIS_ESH:
+ esis_eshinput(m0, shp);
+ break;
+
+ case ESIS_ISH:
+ esis_ishinput(m0, shp);
+ break;
+
+ case ESIS_RD:
+ esis_rdinput(m0, shp);
+ break;
+
+ default:
+ esis_stat.es_badtype++;
+ }
+
+bad:
+ if (esis_pcb.rcb_next != &esis_pcb)
+ isis_input(m0, shp);
+ else
+ m_freem(m0);
+}
+
+/*
+ * FUNCTION: esis_rdoutput
+ *
+ * PURPOSE: Transmit a redirect pdu
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: Assumes there is enough space for fixed part of header,
+ * DA, BSNPA and NET in first mbuf.
+ */
+esis_rdoutput(inbound_shp, inbound_m, inbound_oidx, rd_dstnsap, rt)
+struct snpa_hdr *inbound_shp; /* snpa hdr from incoming packet */
+struct mbuf *inbound_m; /* incoming pkt itself */
+struct clnp_optidx *inbound_oidx; /* clnp options assoc with incoming pkt */
+struct iso_addr *rd_dstnsap; /* ultimate destination of pkt */
+struct rtentry *rt; /* snpa cache info regarding next hop of
+ pkt */
+{
+ struct mbuf *m, *m0;
+ caddr_t cp;
+ struct esis_fixed *pdu;
+ int len, total_len = 0;
+ struct sockaddr_iso siso;
+ struct ifnet *ifp = inbound_shp->snh_ifp;
+ struct sockaddr_dl *sdl;
+ struct iso_addr *rd_gwnsap;
+
+ if (rt->rt_flags & RTF_GATEWAY) {
+ rd_gwnsap = &((struct sockaddr_iso *)rt->rt_gateway)->siso_addr;
+ rt = rtalloc1(rt->rt_gateway, 0);
+ } else
+ rd_gwnsap = &((struct sockaddr_iso *)rt_key(rt))->siso_addr;
+ if (rt == 0 || (sdl = (struct sockaddr_dl *)rt->rt_gateway) == 0 ||
+ sdl->sdl_family != AF_LINK) {
+ /* maybe we should have a function that you
+ could put in the iso_ifaddr structure
+ which could translate iso_addrs into snpa's
+ where there is a known mapping for that address type */
+ esis_stat.es_badtype++;
+ return;
+ }
+ esis_stat.es_rdsent++;
+ IFDEBUG(D_ESISOUTPUT)
+ printf("esis_rdoutput: ifp x%x (%s%d), ht %d, m x%x, oidx x%x\n",
+ ifp, ifp->if_name, ifp->if_unit, esis_holding_time, inbound_m,
+ inbound_oidx);
+ printf("\tdestination: %s\n", clnp_iso_addrp(rd_dstnsap));
+ printf("\tredirected toward:%s\n", clnp_iso_addrp(rd_gwnsap));
+ ENDDEBUG
+
+ if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
+ esis_stat.es_nomem++;
+ return;
+ }
+ bzero(mtod(m, caddr_t), MHLEN);
+
+ pdu = mtod(m, struct esis_fixed *);
+ cp = (caddr_t)(pdu + 1); /*pointer arith.; 1st byte after header */
+ len = sizeof(struct esis_fixed);
+
+ /*
+ * Build fixed part of header
+ */
+ pdu->esis_proto_id = ISO9542_ESIS;
+ pdu->esis_vers = ESIS_VERSION;
+ pdu->esis_type = ESIS_RD;
+ HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, esis_holding_time);
+
+ /* Insert destination address */
+ (void) esis_insert_addr(&cp, &len, rd_dstnsap, m, 0);
+
+ /* Insert the snpa of better next hop */
+ *cp++ = sdl->sdl_alen;
+ bcopy(LLADDR(sdl), cp, sdl->sdl_alen);
+ cp += sdl->sdl_alen;
+ len += (sdl->sdl_alen + 1);
+
+ /*
+ * If the next hop is not the destination, then it ought to be
+ * an IS and it should be inserted next. Else, set the
+ * NETL to 0
+ */
+ /* PHASE2 use mask from ifp of outgoing interface */
+ if (!iso_addrmatch1(rd_dstnsap, rd_gwnsap)) {
+ /* this should not happen:
+ if ((nhop_sc->sc_flags & SNPA_IS) == 0) {
+ printf("esis_rdoutput: next hop is not dst and not an IS\n");
+ m_freem(m0);
+ return;
+ } */
+ (void) esis_insert_addr(&cp, &len, rd_gwnsap, m, 0);
+ } else {
+ *cp++ = 0; /* NETL */
+ len++;
+ }
+ m->m_len = len;
+
+ /*
+ * PHASE2
+ * If redirect is to an IS, add an address mask. The mask to be
+ * used should be the mask present in the routing entry used to
+ * forward the original data packet.
+ */
+
+ /*
+ * Copy Qos, priority, or security options present in original npdu
+ */
+ if (inbound_oidx) {
+ /* THIS CODE IS CURRENTLY (mostly) UNTESTED */
+ int optlen = 0;
+ if (inbound_oidx->cni_qos_formatp)
+ optlen += (inbound_oidx->cni_qos_len + 2);
+ if (inbound_oidx->cni_priorp) /* priority option is 1 byte long */
+ optlen += 3;
+ if (inbound_oidx->cni_securep)
+ optlen += (inbound_oidx->cni_secure_len + 2);
+ if (M_TRAILINGSPACE(m) < optlen) {
+ EXTEND_PACKET(m, m0, cp);
+ m->m_len = 0;
+ /* assumes MLEN > optlen */
+ }
+ /* assume MLEN-len > optlen */
+ /*
+ * When copying options, copy from ptr - 2 in order to grab
+ * the option code and length
+ */
+ if (inbound_oidx->cni_qos_formatp) {
+ bcopy(mtod(inbound_m, caddr_t) + inbound_oidx->cni_qos_formatp - 2,
+ cp, (unsigned)(inbound_oidx->cni_qos_len + 2));
+ cp += inbound_oidx->cni_qos_len + 2;
+ }
+ if (inbound_oidx->cni_priorp) {
+ bcopy(mtod(inbound_m, caddr_t) + inbound_oidx->cni_priorp - 2,
+ cp, 3);
+ cp += 3;
+ }
+ if (inbound_oidx->cni_securep) {
+ bcopy(mtod(inbound_m, caddr_t) + inbound_oidx->cni_securep - 2, cp,
+ (unsigned)(inbound_oidx->cni_secure_len + 2));
+ cp += inbound_oidx->cni_secure_len + 2;
+ }
+ m->m_len += optlen;
+ len += optlen;
+ }
+
+ pdu->esis_hdr_len = m0->m_pkthdr.len = len;
+ iso_gen_csum(m0, ESIS_CKSUM_OFF, (int)pdu->esis_hdr_len);
+
+ bzero((caddr_t)&siso, sizeof(siso));
+ siso.siso_family = AF_ISO;
+ siso.siso_data[0] = AFI_SNA;
+ siso.siso_nlen = 6 + 1; /* should be taken from snpa_hdr */
+ /* +1 is for AFI */
+ bcopy(inbound_shp->snh_shost, siso.siso_data + 1, 6);
+ (ifp->if_output)(ifp, m0, (struct sockaddr *)&siso, 0);
+}
+
+/*
+ * FUNCTION: esis_insert_addr
+ *
+ * PURPOSE: Insert an iso_addr into a buffer
+ *
+ * RETURNS: true if buffer was big enough, else false
+ *
+ * SIDE EFFECTS: Increment buf & len according to size of iso_addr
+ *
+ * NOTES: Plus 1 here is for length byte
+ */
+esis_insert_addr(buf, len, isoa, m, nsellen)
+register caddr_t *buf; /* ptr to buffer to put address into */
+int *len; /* ptr to length of buffer so far */
+register struct iso_addr *isoa; /* ptr to address */
+register struct mbuf *m; /* determine if there remains space */
+int nsellen;
+{
+ register int newlen, result = 0;
+
+ isoa->isoa_len -= nsellen;
+ newlen = isoa->isoa_len + 1;
+ if (newlen <= M_TRAILINGSPACE(m)) {
+ bcopy((caddr_t)isoa, *buf, newlen);
+ *len += newlen;
+ *buf += newlen;
+ m->m_len += newlen;
+ result = 1;
+ }
+ isoa->isoa_len += nsellen;
+ return (result);
+}
+
+#define ESIS_EXTRACT_ADDR(d, b) { d = (struct iso_addr *)(b); b += (1 + *b); \
+ if (b > buflim) {esis_stat.es_toosmall++; goto bad;}}
+#define ESIS_NEXT_OPTION(b) { b += (2 + b[1]); \
+ if (b > buflim) {esis_stat.es_toosmall++; goto bad;}}
+int ESHonly = 0;
+/*
+
+/*
+ * FUNCTION: esis_eshinput
+ *
+ * PURPOSE: Process an incoming ESH pdu
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+esis_eshinput(m, shp)
+struct mbuf *m; /* esh pdu */
+struct snpa_hdr *shp; /* subnetwork header */
+{
+ struct esis_fixed *pdu = mtod(m, struct esis_fixed *);
+ u_short ht; /* holding time */
+ struct iso_addr *nsap;
+ int naddr;
+ u_char *buf = (u_char *)(pdu + 1);
+ u_char *buflim = pdu->esis_hdr_len + (u_char *)pdu;
+ int new_entry = 0;
+
+ esis_stat.es_eshrcvd++;
+
+ CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
+
+ naddr = *buf++;
+ if (buf >= buflim)
+ goto bad;
+ if (naddr == 1) {
+ ESIS_EXTRACT_ADDR(nsap, buf);
+ new_entry = snpac_add(shp->snh_ifp,
+ nsap, shp->snh_shost, SNPA_ES, ht, 0);
+ } else {
+ int nsellength = 0, nlen = 0;
+ {
+ /* See if we want to compress out multiple nsaps differing
+ only by nsel */
+ register struct ifaddr *ifa = shp->snh_ifp->if_addrlist;
+ for (; ifa; ifa = ifa->ifa_next)
+ if (ifa->ifa_addr->sa_family == AF_ISO) {
+ nsellength = ((struct iso_ifaddr *)ifa)->ia_addr.siso_tlen;
+ break;
+ }
+ }
+ IFDEBUG(D_ESISINPUT)
+ printf("esis_eshinput: esh: ht %d, naddr %d nsellength %d\n",
+ ht, naddr, nsellength);
+ ENDDEBUG
+ while (naddr-- > 0) {
+ struct iso_addr *nsap2; u_char *buf2;
+ ESIS_EXTRACT_ADDR(nsap, buf);
+ /* see if there is at least one more nsap in ESH differing
+ only by nsel */
+ if (nsellength != 0) for (buf2 = buf; buf2 < buflim;) {
+ ESIS_EXTRACT_ADDR(nsap2, buf2);
+ IFDEBUG(D_ESISINPUT)
+ printf("esis_eshinput: comparing %s ",
+ clnp_iso_addrp(nsap));
+ printf("and %s\n", clnp_iso_addrp(nsap2));
+ ENDDEBUG
+ if (Bcmp(nsap->isoa_genaddr, nsap2->isoa_genaddr,
+ nsap->isoa_len - nsellength) == 0) {
+ nlen = nsellength;
+ break;
+ }
+ }
+ new_entry |= snpac_add(shp->snh_ifp,
+ nsap, shp->snh_shost, SNPA_ES, ht, nlen);
+ nlen = 0;
+ }
+ }
+ IFDEBUG(D_ESISINPUT)
+ printf("esis_eshinput: nsap %s is %s\n",
+ clnp_iso_addrp(nsap), new_entry ? "new" : "old");
+ ENDDEBUG
+ if (new_entry && (iso_systype & SNPA_IS))
+ esis_shoutput(shp->snh_ifp, ESIS_ISH, esis_holding_time,
+ shp->snh_shost, 6, (struct iso_addr *)0);
+bad:
+ return;
+}
+
+/*
+ * FUNCTION: esis_ishinput
+ *
+ * PURPOSE: process an incoming ISH pdu
+ *
+ * RETURNS:
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+esis_ishinput(m, shp)
+struct mbuf *m; /* esh pdu */
+struct snpa_hdr *shp; /* subnetwork header */
+{
+ struct esis_fixed *pdu = mtod(m, struct esis_fixed *);
+ u_short ht, newct; /* holding time */
+ struct iso_addr *nsap; /* Network Entity Title */
+ register u_char *buf = (u_char *) (pdu + 1);
+ register u_char *buflim = pdu->esis_hdr_len + (u_char *)pdu;
+ int new_entry;
+
+ esis_stat.es_ishrcvd++;
+ CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
+
+ IFDEBUG(D_ESISINPUT)
+ printf("esis_ishinput: ish: ht %d\n", ht);
+ ENDDEBUG
+ if (ESHonly)
+ goto bad;
+
+ ESIS_EXTRACT_ADDR(nsap, buf);
+
+ while (buf < buflim) {
+ switch (*buf) {
+ case ESISOVAL_ESCT:
+ if (iso_systype & SNPA_IS)
+ break;
+ if (buf[1] != 2)
+ goto bad;
+ CTOH(buf[2], buf[3], newct);
+ if (esis_config_time != newct) {
+ untimeout(esis_config,0);
+ esis_config_time = newct;
+ esis_config();
+ }
+ break;
+
+ default:
+ printf("Unknown ISH option: %x\n", *buf);
+ }
+ ESIS_NEXT_OPTION(buf);
+ }
+ new_entry = snpac_add(shp->snh_ifp, nsap, shp->snh_shost, SNPA_IS, ht, 0);
+ IFDEBUG(D_ESISINPUT)
+ printf("esis_ishinput: nsap %s is %s\n",
+ clnp_iso_addrp(nsap), new_entry ? "new" : "old");
+ ENDDEBUG
+
+ if (new_entry)
+ esis_shoutput(shp->snh_ifp,
+ iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
+ esis_holding_time, shp->snh_shost, 6, (struct iso_addr *)0);
+bad:
+ return;
+}
+
+/*
+ * FUNCTION: esis_rdinput
+ *
+ * PURPOSE: Process an incoming RD pdu
+ *
+ * RETURNS:
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+esis_rdinput(m0, shp)
+struct mbuf *m0; /* esh pdu */
+struct snpa_hdr *shp; /* subnetwork header */
+{
+ struct esis_fixed *pdu = mtod(m0, struct esis_fixed *);
+ u_short ht; /* holding time */
+ struct iso_addr *da, *net = 0, *netmask = 0, *snpamask = 0;
+ register struct iso_addr *bsnpa;
+ register u_char *buf = (u_char *)(pdu + 1);
+ register u_char *buflim = pdu->esis_hdr_len + (u_char *)pdu;
+
+ esis_stat.es_rdrcvd++;
+
+ /* intermediate systems ignore redirects */
+ if (iso_systype & SNPA_IS)
+ return;
+ if (ESHonly)
+ return;
+
+ CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
+ if (buf >= buflim)
+ return;
+
+ /* Extract DA */
+ ESIS_EXTRACT_ADDR(da, buf);
+
+ /* Extract better snpa */
+ ESIS_EXTRACT_ADDR(bsnpa, buf);
+
+ /* Extract NET if present */
+ if (buf < buflim) {
+ if (*buf == 0)
+ buf++; /* no NET present, skip NETL anyway */
+ else
+ ESIS_EXTRACT_ADDR(net, buf);
+ }
+
+ /* process options */
+ while (buf < buflim) {
+ switch (*buf) {
+ case ESISOVAL_SNPAMASK:
+ if (snpamask) /* duplicate */
+ return;
+ snpamask = (struct iso_addr *)(buf + 1);
+ break;
+
+ case ESISOVAL_NETMASK:
+ if (netmask) /* duplicate */
+ return;
+ netmask = (struct iso_addr *)(buf + 1);
+ break;
+
+ default:
+ printf("Unknown option in ESIS RD (0x%x)\n", buf[-1]);
+ }
+ ESIS_NEXT_OPTION(buf);
+ }
+
+ IFDEBUG(D_ESISINPUT)
+ printf("esis_rdinput: rd: ht %d, da %s\n", ht, clnp_iso_addrp(da));
+ if (net)
+ printf("\t: net %s\n", clnp_iso_addrp(net));
+ ENDDEBUG
+ /*
+ * If netl is zero, then redirect is to an ES. We need to add an entry
+ * to the snpa cache for (destination, better snpa).
+ * If netl is not zero, then the redirect is to an IS. In this
+ * case, add an snpa cache entry for (net, better snpa).
+ *
+ * If the redirect is to an IS, add a route entry towards that
+ * IS.
+ */
+ if (net == 0 || net->isoa_len == 0 || snpamask) {
+ /* redirect to an ES */
+ snpac_add(shp->snh_ifp, da,
+ bsnpa->isoa_genaddr, SNPA_ES, ht, 0);
+ } else {
+ snpac_add(shp->snh_ifp, net,
+ bsnpa->isoa_genaddr, SNPA_IS, ht, 0);
+ snpac_addrt(shp->snh_ifp, da, net, netmask);
+ }
+bad: ; /* Needed by ESIS_NEXT_OPTION */
+}
+
+/*
+ * FUNCTION: esis_config
+ *
+ * PURPOSE: Report configuration
+ *
+ * RETURNS:
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: Called every esis_config_time seconds
+ */
+void
+esis_config()
+{
+ register struct ifnet *ifp;
+
+ timeout(esis_config, (caddr_t)0, hz * esis_config_time);
+
+ /*
+ * Report configuration for each interface that
+ * - is UP
+ * - has BROADCAST capability
+ * - has an ISO address
+ */
+ /* Todo: a better way would be to construct the esh or ish
+ * once and copy it out for all devices, possibly calling
+ * a method in the iso_ifaddr structure to encapsulate and
+ * transmit it. This could work to advantage for non-broadcast media
+ */
+
+ for (ifp = ifnet; ifp; ifp = ifp->if_next) {
+ if ((ifp->if_flags & IFF_UP) &&
+ (ifp->if_flags & IFF_BROADCAST)) {
+ /* search for an ISO address family */
+ struct ifaddr *ia;
+
+ for (ia = ifp->if_addrlist; ia; ia = ia->ifa_next) {
+ if (ia->ifa_addr->sa_family == AF_ISO) {
+ esis_shoutput(ifp,
+ iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
+ esis_holding_time,
+ (caddr_t)(iso_systype & SNPA_ES ? all_is_snpa :
+ all_es_snpa), 6, (struct iso_addr *)0);
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * FUNCTION: esis_shoutput
+ *
+ * PURPOSE: Transmit an esh or ish pdu
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+esis_shoutput(ifp, type, ht, sn_addr, sn_len, isoa)
+struct ifnet *ifp;
+int type;
+short ht;
+caddr_t sn_addr;
+int sn_len;
+struct iso_addr *isoa;
+{
+ struct mbuf *m, *m0;
+ caddr_t cp, naddrp;
+ int naddr = 0;
+ struct esis_fixed *pdu;
+ struct iso_ifaddr *ia;
+ int len;
+ struct sockaddr_iso siso;
+
+ if (type == ESIS_ESH)
+ esis_stat.es_eshsent++;
+ else if (type == ESIS_ISH)
+ esis_stat.es_ishsent++;
+ else {
+ printf("esis_shoutput: bad pdu type\n");
+ return;
+ }
+
+ IFDEBUG(D_ESISOUTPUT)
+ int i;
+ printf("esis_shoutput: ifp x%x (%s%d), %s, ht %d, to: [%d] ",
+ ifp, ifp->if_name, ifp->if_unit, type == ESIS_ESH ? "esh" : "ish",
+ ht, sn_len);
+ for (i=0; i<sn_len; i++)
+ printf("%x%c", *(sn_addr+i), i < (sn_len-1) ? ':' : ' ');
+ printf("\n");
+ ENDDEBUG
+
+ if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
+ esis_stat.es_nomem++;
+ return;
+ }
+ bzero(mtod(m, caddr_t), MHLEN);
+
+ pdu = mtod(m, struct esis_fixed *);
+ naddrp = cp = (caddr_t)(pdu + 1);
+ len = sizeof(struct esis_fixed);
+
+ /*
+ * Build fixed part of header
+ */
+ pdu->esis_proto_id = ISO9542_ESIS;
+ pdu->esis_vers = ESIS_VERSION;
+ pdu->esis_type = type;
+ HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
+
+ if (type == ESIS_ESH) {
+ cp++;
+ len++;
+ }
+
+ m->m_len = len;
+ if (isoa) {
+ /*
+ * Here we are responding to a clnp packet sent to an NSAP
+ * that is ours which was sent to the MAC addr all_es's.
+ * It is possible that we did not specifically advertise this
+ * NSAP, even though it is ours, so we will respond
+ * directly to the sender that we are here. If we do have
+ * multiple NSEL's we'll tack them on so he can compress them out.
+ */
+ (void) esis_insert_addr(&cp, &len, isoa, m, 0);
+ naddr = 1;
+ }
+ for (ia = iso_ifaddr; ia; ia = ia->ia_next) {
+ int nsellen = (type == ESIS_ISH ? ia->ia_addr.siso_tlen : 0);
+ int n = ia->ia_addr.siso_nlen;
+ register struct iso_ifaddr *ia2;
+
+ if (type == ESIS_ISH && naddr > 0)
+ break;
+ for (ia2 = iso_ifaddr; ia2 != ia; ia2 = ia2->ia_next)
+ if (Bcmp(ia->ia_addr.siso_data, ia2->ia_addr.siso_data, n) == 0)
+ break;
+ if (ia2 != ia)
+ continue; /* Means we have previously copied this nsap */
+ if (isoa && Bcmp(ia->ia_addr.siso_data, isoa->isoa_genaddr, n) == 0) {
+ isoa = 0;
+ continue; /* Ditto */
+ }
+ IFDEBUG(D_ESISOUTPUT)
+ printf("esis_shoutput: adding NSAP %s\n",
+ clnp_iso_addrp(&ia->ia_addr.siso_addr));
+ ENDDEBUG
+ if (!esis_insert_addr(&cp, &len,
+ &ia->ia_addr.siso_addr, m, nsellen)) {
+ EXTEND_PACKET(m, m0, cp);
+ (void) esis_insert_addr(&cp, &len, &ia->ia_addr.siso_addr, m,
+ nsellen);
+ }
+ naddr++;
+ }
+
+ if (type == ESIS_ESH)
+ *naddrp = naddr;
+ else {
+ /* add suggested es config timer option to ISH */
+ if (M_TRAILINGSPACE(m) < 4) {
+ printf("esis_shoutput: extending packet\n");
+ EXTEND_PACKET(m, m0, cp);
+ }
+ *cp++ = ESISOVAL_ESCT;
+ *cp++ = 2;
+ HTOC(*cp, *(cp+1), esis_esconfig_time);
+ len += 4;
+ m->m_len += 4;
+ IFDEBUG(D_ESISOUTPUT)
+ printf("m0 0x%x, m 0x%x, data 0x%x, len %d, cp 0x%x\n",
+ m0, m, m->m_data, m->m_len, cp);
+ ENDDEBUG
+ }
+
+ m0->m_pkthdr.len = len;
+ pdu->esis_hdr_len = len;
+ iso_gen_csum(m0, ESIS_CKSUM_OFF, (int)pdu->esis_hdr_len);
+
+ bzero((caddr_t)&siso, sizeof(siso));
+ siso.siso_family = AF_ISO;
+ siso.siso_data[0] = AFI_SNA;
+ siso.siso_nlen = sn_len + 1;
+ bcopy(sn_addr, siso.siso_data + 1, (unsigned)sn_len);
+ (ifp->if_output)(ifp, m0, (struct sockaddr *)&siso, 0);
+}
+
+/*
+ * FUNCTION: isis_input
+ *
+ * PURPOSE: Process an incoming isis packet
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+isis_input(m0, shp)
+struct mbuf *m0; /* ptr to first mbuf of pkt */
+struct snpa_hdr *shp; /* subnetwork header */
+{
+ register int type;
+ register struct rawcb *rp, *first_rp = 0;
+ struct ifnet *ifp = shp->snh_ifp;
+ char workbuf[16];
+ struct mbuf *mm;
+
+ IFDEBUG(D_ISISINPUT)
+ int i;
+
+ printf("isis_input: pkt on ifp x%x (%s%d): from:", ifp,
+ ifp->if_name, ifp->if_unit);
+ for (i=0; i<6; i++)
+ printf("%x%c", shp->snh_shost[i]&0xff, (i<5) ? ':' : ' ');
+ printf(" to:");
+ for (i=0; i<6; i++)
+ printf("%x%c", shp->snh_dhost[i]&0xff, (i<5) ? ':' : ' ');
+ printf("\n");
+ ENDDEBUG
+ esis_dl.sdl_alen = ifp->if_addrlen;
+ esis_dl.sdl_index = ifp->if_index;
+ bcopy(shp->snh_shost, (caddr_t)esis_dl.sdl_data, esis_dl.sdl_alen);
+ for (rp = esis_pcb.rcb_next; rp != &esis_pcb; rp = rp->rcb_next) {
+ if (first_rp == 0) {
+ first_rp = rp;
+ continue;
+ }
+ if (mm = m_copy(m0, 0, M_COPYALL)) { /*can't block at interrupt level */
+ if (sbappendaddr(&rp->rcb_socket->so_rcv,
+ &esis_dl, mm, (struct mbuf *)0) != 0) {
+ sorwakeup(rp->rcb_socket);
+ } else {
+ IFDEBUG(D_ISISINPUT)
+ printf("Error in sbappenaddr, mm = 0x%x\n", mm);
+ ENDDEBUG
+ m_freem(mm);
+ }
+ }
+ }
+ if (first_rp && sbappendaddr(&first_rp->rcb_socket->so_rcv,
+ &esis_dl, m0, (struct mbuf *)0) != 0) {
+ sorwakeup(first_rp->rcb_socket);
+ return;
+ }
+ m_freem(m0);
+}
+
+isis_output(sdl, m)
+register struct sockaddr_dl *sdl;
+struct mbuf *m;
+{
+ register struct ifnet *ifp;
+ struct ifaddr *ifa, *ifa_ifwithnet();
+ struct sockaddr_iso siso;
+ int error = 0;
+ unsigned sn_len;
+
+ ifa = ifa_ifwithnet((struct sockaddr *)sdl); /* get ifp from sdl */
+ if (ifa == 0) {
+ IFDEBUG(D_ISISOUTPUT)
+ printf("isis_output: interface not found\n");
+ ENDDEBUG
+ error = EINVAL;
+ goto release;
+ }
+ ifp = ifa->ifa_ifp;
+ sn_len = sdl->sdl_alen;
+ IFDEBUG(D_ISISOUTPUT)
+ u_char *cp = (u_char *)LLADDR(sdl), *cplim = cp + sn_len;
+ printf("isis_output: ifp 0x%x (%s%d), to: ",
+ ifp, ifp->if_name, ifp->if_unit);
+ while (cp < cplim) {
+ printf("%x", *cp++);
+ printf("%c", (cp < cplim) ? ':' : ' ');
+ }
+ printf("\n");
+ ENDDEBUG
+ bzero((caddr_t)&siso, sizeof(siso));
+ siso.siso_family = AF_ISO; /* This convention may be useful for X.25 */
+ siso.siso_data[0] = AFI_SNA;
+ siso.siso_nlen = sn_len + 1;
+ bcopy(LLADDR(sdl), siso.siso_data + 1, sn_len);
+ error = (ifp->if_output)(ifp, m, (struct sockaddr *)&siso, 0);
+ if (error) {
+ IFDEBUG(D_ISISOUTPUT)
+ printf("isis_output: error from ether_output is %d\n", error);
+ ENDDEBUG
+ }
+ return (error);
+
+release:
+ if (m != NULL)
+ m_freem(m);
+ return(error);
+}
+
+
+/*
+ * FUNCTION: esis_ctlinput
+ *
+ * PURPOSE: Handle the PRC_IFDOWN transition
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: Calls snpac_flush for interface specified.
+ * The loop through iso_ifaddr is stupid because
+ * back in if_down, we knew the ifp...
+ */
+esis_ctlinput(req, siso)
+int req; /* request: we handle only PRC_IFDOWN */
+struct sockaddr_iso *siso; /* address of ifp */
+{
+ register struct iso_ifaddr *ia; /* scan through interface addresses */
+
+ if (req == PRC_IFDOWN)
+ for (ia = iso_ifaddr; ia; ia = ia->ia_next) {
+ if (iso_addrmatch(IA_SIS(ia), siso))
+ snpac_flushifp(ia->ia_ifp);
+ }
+}
+
+#endif /* ISO */
diff --git a/sys/netiso/esis.h b/sys/netiso/esis.h
new file mode 100644
index 0000000..81dd74a
--- /dev/null
+++ b/sys/netiso/esis.h
@@ -0,0 +1,135 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)esis.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * $Header: esis.h,v 4.7 88/09/15 11:24:18 hagens Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/esis.h,v $
+ */
+
+#ifndef BYTE_ORDER
+/*
+ * Definitions for byte order,
+ * according to byte significance from low address to high.
+ */
+#define LITTLE_ENDIAN 1234 /* least-significant byte first (vax) */
+#define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */
+#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp) */
+
+#ifdef vax
+#define BYTE_ORDER LITTLE_ENDIAN
+#else
+#define BYTE_ORDER BIG_ENDIAN /* mc68000, tahoe, most others */
+#endif
+#endif /* BYTE_ORDER */
+
+#define SNPAC_AGE 60 /* seconds */
+#define ESIS_CONFIG 60 /* seconds */
+#define ESIS_HT (ESIS_CONFIG * 2)
+
+/*
+ * Fixed part of an ESIS header
+ */
+struct esis_fixed {
+ u_char esis_proto_id; /* network layer protocol identifier */
+ u_char esis_hdr_len; /* length indicator (octets) */
+ u_char esis_vers; /* version/protocol identifier extension */
+ u_char esis_res1; /* reserved */
+ u_char esis_type; /* type code */
+/* technically, type should be &='d 0x1f */
+#define ESIS_ESH 0x02 /* End System Hello */
+#define ESIS_ISH 0x04 /* Intermediate System Hello */
+#define ESIS_RD 0x06 /* Redirect */
+ u_char esis_ht_msb; /* holding time (seconds) high byte */
+ u_char esis_ht_lsb; /* holding time (seconds) low byte */
+ u_char esis_cksum_msb; /* checksum high byte */
+ u_char esis_cksum_lsb; /* checksum low byte */
+};
+/*
+ * Values for ESIS datagram options
+ */
+#define ESISOVAL_NETMASK 0xe1 /* address mask option, RD PDU only */
+#define ESISOVAL_SNPAMASK 0xe2 /* snpa mask option, RD PDU only */
+#define ESISOVAL_ESCT 0xc6 /* end system conf. timer, ISH PDU only */
+
+
+#define ESIS_CKSUM_OFF 0x07
+#define ESIS_CKSUM_REQUIRED(pdu)\
+ ((pdu->esis_cksum_msb != 0) || (pdu->esis_cksum_lsb != 0))
+
+#define ESIS_VERSION 1
+
+struct esis_stat {
+ u_short es_nomem; /* insufficient memory to send hello */
+ u_short es_badcsum; /* incorrect checksum */
+ u_short es_badvers; /* incorrect version number */
+ u_short es_badtype; /* unknown pdu type field */
+ u_short es_toosmall; /* packet too small */
+ u_short es_eshsent; /* ESH sent */
+ u_short es_eshrcvd; /* ESH rcvd */
+ u_short es_ishsent; /* ISH sent */
+ u_short es_ishrcvd; /* ISH rcvd */
+ u_short es_rdsent; /* RD sent */
+ u_short es_rdrcvd; /* RD rcvd */
+};
+
+#ifdef KERNEL
+struct esis_stat esis_stat;
+#endif /* KERNEL */
diff --git a/sys/netiso/idrp_usrreq.c b/sys/netiso/idrp_usrreq.c
new file mode 100644
index 0000000..3109936
--- /dev/null
+++ b/sys/netiso/idrp_usrreq.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)idrp_usrreq.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+
+#include <net/route.h>
+#include <net/if.h>
+
+#include <netiso/argo_debug.h>
+#include <netiso/iso.h>
+#include <netiso/clnp.h>
+#include <netiso/clnl.h>
+#include <netiso/iso_pcb.h>
+#include <netiso/iso_var.h>
+
+int idrp_input();
+struct isopcb idrp_isop;
+static struct sockaddr_iso idrp_addrs[2] =
+{ { sizeof(idrp_addrs), AF_ISO, }, { sizeof(idrp_addrs[1]), AF_ISO, } };
+/*
+ * IDRP initialization
+ */
+idrp_init()
+{
+ extern struct clnl_protosw clnl_protox[256];
+
+ idrp_isop.isop_next = idrp_isop.isop_prev = &idrp_isop;
+ idrp_isop.isop_faddr = &idrp_isop.isop_sfaddr;
+ idrp_isop.isop_laddr = &idrp_isop.isop_sladdr;
+ idrp_isop.isop_sladdr = idrp_addrs[1];
+ idrp_isop.isop_sfaddr = idrp_addrs[1];
+ clnl_protox[ISO10747_IDRP].clnl_input = idrp_input;
+}
+
+/*
+ * CALLED FROM:
+ * tpclnp_input().
+ * FUNCTION and ARGUMENTS:
+ * Take a packet (m) from clnp, strip off the clnp header
+ * and mke suitable for the idrp socket.
+ * No return value.
+ */
+idrp_input(m, src, dst)
+ register struct mbuf *m;
+ struct sockaddr_iso *src, *dst;
+{
+ if (idrp_isop.isop_socket == 0) {
+ bad: m_freem(m);
+ return 0;
+ }
+ bzero(idrp_addrs[0].siso_data, sizeof(idrp_addrs[0].siso_data));
+ bcopy((caddr_t)&(src->siso_addr), (caddr_t)&idrp_addrs[0].siso_addr,
+ 1 + src->siso_nlen);
+ bzero(idrp_addrs[1].siso_data, sizeof(idrp_addrs[1].siso_data));
+ bcopy((caddr_t)&(dst->siso_addr), (caddr_t)&idrp_addrs[1].siso_addr,
+ 1 + dst->siso_nlen);
+ if (sbappendaddr(&idrp_isop.isop_socket->so_rcv,
+ (struct sockaddr *)idrp_addrs, m, (struct mbuf *)0) == 0)
+ goto bad;
+ sorwakeup(idrp_isop.isop_socket);
+ return 0;
+}
+
+idrp_output(m, addr)
+ struct mbuf *m, *addr;
+{
+ register struct sockaddr_iso *siso = mtod(addr, struct sockaddr_iso *);
+ int s = splnet(), i;
+
+ bcopy((caddr_t)&(siso->siso_addr),
+ (caddr_t)&idrp_isop.isop_sfaddr.siso_addr, 1 + siso->siso_nlen);
+ siso++;
+ bcopy((caddr_t)&(siso->siso_addr),
+ (caddr_t)&idrp_isop.isop_sladdr.siso_addr, 1 + siso->siso_nlen);
+ i = clnp_output(m, idrp_isop, m->m_pkthdr.len, 0);
+ splx(s);
+ return (i);
+}
+
+u_long idrp_sendspace = 3072; /* really max datagram size */
+u_long idrp_recvspace = 40 * 1024; /* 40 1K datagrams */
+
+/*ARGSUSED*/
+idrp_usrreq(so, req, m, addr, control)
+ struct socket *so;
+ int req;
+ struct mbuf *m, *addr, *control;
+{
+ int error = 0;
+
+ /* Note: need to block idrp_input while changing
+ * the udp pcb queue and/or pcb addresses.
+ */
+ switch (req) {
+
+ case PRU_ATTACH:
+ if (idrp_isop.isop_socket != NULL) {
+ error = ENXIO;
+ break;
+ }
+ idrp_isop.isop_socket = so;
+ error = soreserve(so, idrp_sendspace, idrp_recvspace);
+ break;
+
+ case PRU_SHUTDOWN:
+ socantsendmore(so);
+ break;
+
+ case PRU_SEND:
+ return (idrp_output(m, addr));
+
+ case PRU_ABORT:
+ soisdisconnected(so);
+ case PRU_DETACH:
+ idrp_isop.isop_socket = 0;
+ break;
+
+
+ case PRU_SENSE:
+ /*
+ * stat: don't bother with a blocksize.
+ */
+ return (0);
+
+ default:
+ return (EOPNOTSUPP); /* do not free mbuf's */
+ }
+
+release:
+ if (control) {
+ printf("idrp control data unexpectedly retained\n");
+ m_freem(control);
+ }
+ if (m)
+ m_freem(m);
+ return (error);
+}
diff --git a/sys/netiso/if_cons.c b/sys/netiso/if_cons.c
new file mode 100644
index 0000000..7724b04
--- /dev/null
+++ b/sys/netiso/if_cons.c
@@ -0,0 +1,960 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_cons.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * $Header: if_cons.c,v 4.7 88/08/11 15:52:55 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/if_cons.c,v $
+ *
+ * cons.c - Connection Oriented Network Service:
+ * including support for a) user transport-level service,
+ * b) COSNS below CLNP, and c) CONS below TP.
+ */
+
+#ifdef TPCONS
+#ifdef KERNEL
+#ifdef ARGO_DEBUG
+#define Static
+unsigned LAST_CALL_PCB;
+#else /* ARGO_DEBUG */
+#define Static static
+#endif /* ARGO_DEBUG */
+
+#ifndef SOCK_STREAM
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/tsleep.h>
+
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/route.h>
+
+#include <netiso/iso_errno.h>
+#include <netiso/argo_debug.h>
+#include <netiso/tp_trace.h>
+#include <netiso/iso.h>
+#include <netiso/cons.h>
+#include <netiso/iso_pcb.h>
+
+#include <netccitt/x25.h>
+#include <netccitt/pk.h>
+#include <netccitt/pk_var.h>
+#endif
+
+#ifdef ARGO_DEBUG
+#define MT_XCONN 0x50
+#define MT_XCLOSE 0x51
+#define MT_XCONFIRM 0x52
+#define MT_XDATA 0x53
+#define MT_XHEADER 0x54
+#else
+#define MT_XCONN MT_DATA
+#define MT_XCLOSE MT_DATA
+#define MT_XCONFIRM MT_DATA
+#define MT_XDATA MT_DATA
+#define MT_XHEADER MT_HEADER
+#endif /* ARGO_DEBUG */
+
+#define DONTCLEAR -1
+
+/*********************************************************************
+ * cons.c - CONS interface to the x.25 layer
+ *
+ * TODO: figure out what resources we might run out of besides mbufs.
+ * If we run out of any of them (including mbufs) close and recycle
+ * lru x% of the connections, for some parameter x.
+ *
+ * There are 2 interfaces from above:
+ * 1) from TP0:
+ * cons CO network service
+ * TP associates a transport connection with a network connection.
+ * cons_output( isop, m, len, isdgm==0 )
+ * co_flags == 0
+ * 2) from TP4:
+ * It's a datagram service, like clnp is. - even though it calls
+ * cons_output( isop, m, len, isdgm==1 )
+ * it eventually goes through
+ * cosns_output(ifp, m, dst).
+ * TP4 permits multiplexing (reuse, possibly simultaneously) of the
+ * network connections.
+ * This means that many sockets (many tpcbs) may be associated with
+ * this pklcd, hence cannot have a back ptr from pklcd to a tpcb.
+ * co_flags & CONSF_DGM
+ * co_socket is null since there may be many sockets that use this pklcd.
+ *
+NOTE:
+ streams would really be nice. sigh.
+NOTE:
+ PVCs could be handled by config-ing a cons with an address and with the
+ IFF_POINTTOPOINT flag on. This code would then have to skip the
+ connection setup stuff for pt-to-pt links.
+
+
+ *********************************************************************/
+
+
+#define CONS_IFQMAXLEN 5
+
+
+/* protosw pointers for getting to higher layer */
+Static struct protosw *CLNP_proto;
+Static struct protosw *TP_proto;
+Static struct protosw *X25_proto;
+Static int issue_clear_req();
+
+#ifndef PHASEONE
+extern struct ifaddr *ifa_ifwithnet();
+#endif /* PHASEONE */
+
+extern struct ifaddr *ifa_ifwithaddr();
+
+extern struct isopcb tp_isopcb; /* chain of all TP pcbs */
+
+
+Static int parse_facil(), NSAPtoDTE(), make_partial_x25_packet();
+Static int FACILtoNSAP(), DTEtoNSAP();
+Static struct pklcd *cons_chan_to_pcb();
+
+#define HIGH_NIBBLE 1
+#define LOW_NIBBLE 0
+
+/*
+ * NAME: nibble_copy()
+ * FUNCTION and ARGUMENTS:
+ * copies (len) nibbles from (src_octet), high or low nibble
+ * to (dst_octet), high or low nibble,
+ * src_nibble & dst_nibble should be:
+ * HIGH_NIBBLE (1) if leftmost 4 bits/ most significant nibble
+ * LOW_NIBBLE (0) if rightmost 4 bits/ least significant nibble
+ * RETURNS: VOID
+ */
+void
+nibble_copy(src_octet, src_nibble, dst_octet, dst_nibble, len)
+ register char *src_octet;
+ register char *dst_octet;
+ register unsigned src_nibble;
+ register unsigned dst_nibble;
+ int len;
+{
+
+ register i;
+ register unsigned dshift, sshift;
+
+ IFDEBUG(D_CADDR)
+ printf("nibble_copy ( 0x%x, 0x%x, 0x%x, 0x%x 0x%x)\n",
+ src_octet, src_nibble, dst_octet, dst_nibble, len);
+ ENDDEBUG
+#define SHIFT 0x4
+
+ dshift = dst_nibble << 2;
+ sshift = src_nibble << 2;
+
+ for (i=0; i<len; i++) {
+ /* clear dst_nibble */
+ *dst_octet &= ~(0xf<< dshift);
+
+ /* set dst nibble */
+ *dst_octet |= ( 0xf & (*src_octet >> sshift))<< dshift;
+
+ dshift ^= SHIFT;
+ sshift ^= SHIFT;
+ src_nibble = 1-src_nibble;
+ dst_nibble = 1-dst_nibble;
+ src_octet += src_nibble;
+ dst_octet += dst_nibble;
+ }
+ IFDEBUG(D_CADDR)
+ printf("nibble_copy DONE\n");
+ ENDDEBUG
+}
+
+/*
+ * NAME: nibble_match()
+ * FUNCTION and ARGUMENTS:
+ * compares src_octet/src_nibble and dst_octet/dst_nibble for len nibbles.
+ * RETURNS: 0 if they differ, 1 if they are the same.
+ */
+int
+nibble_match( src_octet, src_nibble, dst_octet, dst_nibble, len)
+ register char *src_octet;
+ register char *dst_octet;
+ register unsigned src_nibble;
+ register unsigned dst_nibble;
+ int len;
+{
+
+ register i;
+ register unsigned dshift, sshift;
+ u_char nibble_a, nibble_b;
+
+ IFDEBUG(D_CADDR)
+ printf("nibble_match ( 0x%x, 0x%x, 0x%x, 0x%x 0x%x)\n",
+ src_octet, src_nibble, dst_octet, dst_nibble, len);
+ ENDDEBUG
+#define SHIFT 0x4
+
+ dshift = dst_nibble << 2;
+ sshift = src_nibble << 2;
+
+ for (i=0; i<len; i++) {
+ nibble_b = ((*dst_octet)>>dshift) & 0xf;
+ nibble_a = ( 0xf & (*src_octet >> sshift));
+ if (nibble_b != nibble_a)
+ return 0;
+
+ dshift ^= SHIFT;
+ sshift ^= SHIFT;
+ src_nibble = 1-src_nibble;
+ dst_nibble = 1-dst_nibble;
+ src_octet += src_nibble;
+ dst_octet += dst_nibble;
+ }
+ IFDEBUG(D_CADDR)
+ printf("nibble_match DONE\n");
+ ENDDEBUG
+ return 1;
+}
+
+/*
+ **************************** NET PROTOCOL cons ***************************
+ */
+/*
+ * NAME: cons_init()
+ * CALLED FROM:
+ * autoconf
+ * FUNCTION:
+ * initialize the protocol
+ */
+cons_init()
+{
+ int tp_incoming(), clnp_incoming();
+
+
+ CLNP_proto = pffindproto(AF_ISO, ISOPROTO_CLNP, SOCK_DGRAM);
+ X25_proto = pffindproto(AF_ISO, ISOPROTO_X25, SOCK_STREAM);
+ TP_proto = pffindproto(AF_ISO, ISOPROTO_TP0, SOCK_SEQPACKET);
+ IFDEBUG(D_CCONS)
+ printf("cons_init end : cnlp_proto 0x%x cons proto 0x%x tp proto 0x%x\n",
+ CLNP_proto, X25_proto, TP_proto);
+ ENDDEBUG
+#ifdef notdef
+ pk_protolisten(0x81, 0, clnp_incoming);
+ pk_protolisten(0x82, 0, esis_incoming);
+ pk_protolisten(0x84, 0, tp8878_A_incoming);
+ pk_protolisten(0, 0, tp_incoming);
+#endif
+}
+
+tp_incoming(lcp, m)
+struct pklcd *lcp;
+register struct mbuf *m;
+{
+ register struct isopcb *isop;
+ int cons_tpinput();
+
+ if (iso_pcballoc((struct socket *)0, &tp_isopcb)) {
+ pk_close(lcp);
+ return;
+ }
+ isop = tp_isopcb.isop_next;
+ lcp->lcd_upper = cons_tpinput;
+ lcp->lcd_upnext = (caddr_t)isop;
+ lcp->lcd_send(lcp); /* Confirms call */
+ isop->isop_chan = (caddr_t)lcp;
+ isop->isop_laddr = &isop->isop_sladdr;
+ isop->isop_faddr = &isop->isop_sfaddr;
+ DTEtoNSAP(isop->isop_laddr, &lcp->lcd_laddr);
+ DTEtoNSAP(isop->isop_faddr, &lcp->lcd_faddr);
+ parse_facil(lcp, isop, &(mtod(m, struct x25_packet *)->packet_data),
+ m->m_pkthdr.len - PKHEADERLN);
+}
+
+cons_tpinput(lcp, m0)
+struct mbuf *m0;
+struct pklcd *lcp;
+{
+ register struct isopcb *isop = (struct isopcb *)lcp->lcd_upnext;
+ register struct x25_packet *xp;
+ int cmd, ptype = CLEAR;
+
+ if (isop == 0)
+ return;
+ if (m0 == 0)
+ goto dead;
+ switch(m0->m_type) {
+ case MT_DATA:
+ case MT_OOBDATA:
+ tpcons_input(m0, isop->isop_faddr, isop->isop_laddr, (caddr_t)lcp);
+ return;
+
+ case MT_CONTROL:
+ switch (ptype = pk_decode(mtod(m0, struct x25_packet *))) {
+
+ case RR:
+ cmd = PRC_CONS_SEND_DONE;
+ break;
+
+ case CALL_ACCEPTED:
+ if (lcp->lcd_sb.sb_mb)
+ lcp->lcd_send(lcp); /* XXX - fix this */
+ /*FALLTHROUGH*/
+ default:
+ return;
+
+ dead:
+ case CLEAR:
+ case CLEAR_CONF:
+ lcp->lcd_upper = 0;
+ lcp->lcd_upnext = 0;
+ isop->isop_chan = 0;
+ case RESET:
+ cmd = PRC_ROUTEDEAD;
+ }
+ tpcons_ctlinput(cmd, isop->isop_faddr, isop);
+ if (cmd = PRC_ROUTEDEAD && isop->isop_refcnt == 0)
+ iso_pcbdetach(isop);
+ }
+}
+
+/*
+ * NAME: cons_connect()
+ * CALLED FROM:
+ * tpcons_pcbconnect() when opening a new connection.
+ * FUNCTION anD ARGUMENTS:
+ * Figures out which device to use, finding a route if one doesn't
+ * already exist.
+ * RETURN VALUE:
+ * returns E*
+ */
+cons_connect(isop)
+ register struct isopcb *isop;
+{
+ register struct pklcd *lcp = (struct pklcd *)isop->isop_chan;
+ register struct mbuf *m;
+ struct ifaddr *ifa;
+ int error;
+
+ IFDEBUG(D_CCONN)
+ printf("cons_connect(0x%x): ", isop);
+ dump_isoaddr(isop->isop_faddr);
+ printf("myaddr: ");
+ dump_isoaddr(isop->isop_laddr);
+ printf("\n" );
+ ENDDEBUG
+ NSAPtoDTE(isop->isop_faddr, &lcp->lcd_faddr);
+ lcp->lcd_upper = cons_tpinput;
+ lcp->lcd_upnext = (caddr_t)isop;
+ IFDEBUG(D_CCONN)
+ printf(
+ "calling make_partial_x25_packet( 0x%x, 0x%x, 0x%x)\n",
+ &lcp->lcd_faddr, &lcp->lcd_laddr,
+ isop->isop_socket->so_proto->pr_protocol);
+ ENDDEBUG
+ if ((error = make_partial_x25_packet(isop, lcp, m)) == 0)
+ error = pk_connect(lcp, &lcp->lcd_faddr);
+ return error;
+}
+
+/*
+ **************************** DEVICE cons ***************************
+ */
+
+
+/*
+ * NAME: cons_ctlinput()
+ * CALLED FROM:
+ * lower layer when ECN_CLEAR occurs : this routine is here
+ * for consistency - cons subnet service calls its higher layer
+ * through the protosw entry.
+ * FUNCTION & ARGUMENTS:
+ * cmd is a PRC_* command, list found in ../sys/protosw.h
+ * copcb is the obvious.
+ * This serves the higher-layer cons service.
+ * NOTE: this takes 3rd arg. because cons uses it to inform itself
+ * of things (timeouts, etc) but has a pcb instead of an address.
+ */
+cons_ctlinput(cmd, sa, copcb)
+ int cmd;
+ struct sockaddr *sa;
+ register struct pklcd *copcb;
+{
+}
+
+
+find_error_reason( xp )
+ register struct x25_packet *xp;
+{
+ extern u_char x25_error_stats[];
+ int error, cause;
+
+ if (xp) {
+ cause = 4[(char *)xp];
+ switch (cause) {
+ case 0x00:
+ case 0x80:
+ /* DTE originated; look at the diagnostic */
+ error = (CONL_ERROR_MASK | cause);
+ goto done;
+
+ case 0x01: /* number busy */
+ case 0x81:
+ case 0x09: /* Out of order */
+ case 0x89:
+ case 0x11: /* Remot Procedure Error */
+ case 0x91:
+ case 0x19: /* reverse charging accept not subscribed */
+ case 0x99:
+ case 0x21: /* Incampat destination */
+ case 0xa1:
+ case 0x29: /* fast select accept not subscribed */
+ case 0xa9:
+ case 0x39: /* ship absent */
+ case 0xb9:
+ case 0x03: /* invalid facil request */
+ case 0x83:
+ case 0x0b: /* access barred */
+ case 0x8b:
+ case 0x13: /* local procedure error */
+ case 0x93:
+ case 0x05: /* network congestion */
+ case 0x85:
+ case 0x8d: /* not obtainable */
+ case 0x0d:
+ case 0x95: /* RPOA out of order */
+ case 0x15:
+ /* take out bit 8
+ * so we don't have to have so many perror entries
+ */
+ error = (CONL_ERROR_MASK | 0x100 | (cause & ~0x80));
+ goto done;
+
+ case 0xc1: /* gateway-detected proc error */
+ case 0xc3: /* gateway congestion */
+
+ error = (CONL_ERROR_MASK | 0x100 | cause);
+ goto done;
+ }
+ }
+ /* otherwise, a *hopefully* valid perror exists in the e_reason field */
+ error = xp->packet_data;
+ if (error = 0) {
+ printf("Incoming PKT TYPE 0x%x with reason 0x%x\n",
+ pk_decode(xp),
+ cause);
+ error = E_CO_HLI_DISCA;
+ }
+
+done:
+ return error;
+}
+
+
+
+#endif /* KERNEL */
+
+/*
+ * NAME: make_partial_x25_packet()
+ *
+ * FUNCTION and ARGUMENTS:
+ * Makes part of an X.25 call packet, for use by x25.
+ * (src) and (dst) are the NSAP-addresses of source and destination.
+ * (buf) is a ptr to a buffer into which to write this partial header.
+ *
+ * 0 Facility length (in octets)
+ * 1 Facility field, which is a set of:
+ * m facil code
+ * m+1 facil param len (for >2-byte facilities) in octets
+ * m+2..p facil param field
+ * q user data (protocol identification octet)
+ *
+ *
+ * RETURNS:
+ * 0 if OK
+ * E* if failed.
+ *
+ * SIDE EFFECTS:
+ * Stores facilites mbuf in X.25 control block, where the connect
+ * routine knows where to look for it.
+ */
+
+#ifdef X25_1984
+int cons_use_facils = 1;
+#else /* X25_1984 */
+int cons_use_facils = 0;
+#endif /* X25_1984 */
+
+int cons_use_udata = 1; /* KLUDGE FOR DEBUGGING */
+
+Static int
+make_partial_x25_packet(isop, lcp)
+ struct isopcb *isop;
+ struct pklcd *lcp;
+{
+ u_int proto;
+ int flag;
+ caddr_t buf;
+ register caddr_t ptr;
+ register int len = 0;
+ int buflen =0;
+ caddr_t facil_len;
+ int oddness = 0;
+ struct mbuf *m;
+
+
+ IFDEBUG(D_CCONN)
+ printf("make_partial_x25_packet(0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n",
+ isop->isop_laddr, isop->isop_faddr, proto, m, flag);
+ ENDDEBUG
+ if (cons_use_udata) {
+ if (isop->isop_x25crud_len > 0) {
+ /*
+ * The user specified something. Stick it in
+ */
+ bcopy(isop->isop_x25crud, lcp->lcd_faddr.x25_udata,
+ isop->isop_x25crud_len);
+ lcp->lcd_faddr.x25_udlen = isop->isop_x25crud_len;
+ }
+ }
+
+ if (cons_use_facils == 0) {
+ lcp->lcd_facilities = 0;
+ return 0;
+ }
+ MGETHDR(m, MT_DATA, M_WAITOK);
+ if (m == 0)
+ return ENOBUFS;
+ buf = mtod(m, caddr_t);
+ ptr = buf;
+
+ /* ptr now points to facil length (len of whole facil field in OCTETS */
+ facil_len = ptr ++;
+ m->m_len = 0;
+ pk_build_facilities(m, &lcp->lcd_faddr, 0);
+
+ IFDEBUG(D_CADDR)
+ printf("make_partial calling: ptr 0x%x, len 0x%x\n", ptr,
+ isop->isop_laddr->siso_addr.isoa_len);
+ ENDDEBUG
+ if (cons_use_facils) {
+ *ptr++ = 0; /* Marker to separate X.25 facitilies from CCITT ones */
+ *ptr++ = 0x0f;
+ *ptr = 0xcb; /* calling facility code */
+ ptr ++;
+ ptr ++; /* leave room for facil param len (in OCTETS + 1) */
+ ptr ++; /* leave room for the facil param len (in nibbles),
+ * high two bits of which indicate full/partial NSAP
+ */
+ len = isop->isop_laddr->siso_addr.isoa_len;
+ bcopy( isop->isop_laddr->siso_data, ptr, len);
+ *(ptr-2) = len+1; /* facil param len in octets */
+ *(ptr-1) = len<<1; /* facil param len in nibbles */
+ ptr += len;
+
+ IFDEBUG(D_CADDR)
+ printf("make_partial called: ptr 0x%x, len 0x%x\n", ptr,
+ isop->isop_faddr->siso_addr.isoa_len);
+ ENDDEBUG
+ *ptr = 0xc9; /* called facility code */
+ ptr ++;
+ ptr ++; /* leave room for facil param len (in OCTETS + 1) */
+ ptr ++; /* leave room for the facil param len (in nibbles),
+ * high two bits of which indicate full/partial NSAP
+ */
+ len = isop->isop_faddr->siso_nlen;
+ bcopy(isop->isop_faddr->siso_data, ptr, len);
+ *(ptr-2) = len+1; /* facil param len = addr len + 1 for each of these
+ * two length fields, in octets */
+ *(ptr-1) = len<<1; /* facil param len in nibbles */
+ ptr += len;
+
+ }
+ *facil_len = ptr - facil_len - 1;
+ if (*facil_len > MAX_FACILITIES)
+ return E_CO_PNA_LONG;
+
+ buflen = (int)(ptr - buf);
+
+ IFDEBUG(D_CDUMP_REQ)
+ register int i;
+
+ printf("ECN_CONNECT DATA buf 0x%x len %d (0x%x)\n",
+ buf, buflen, buflen);
+ for( i=0; i < buflen; ) {
+ printf("+%d: %x %x %x %x %x %x %x %x\n",
+ i,
+ *(buf+i), *(buf+i+1), *(buf+i+2), *(buf+i+3),
+ *(buf+i+4), *(buf+i+5), *(buf+i+6), *(buf+i+7));
+ i+=8;
+ }
+ ENDDEBUG
+ IFDEBUG(D_CADDR)
+ printf("make_partial returns buf 0x%x size 0x%x bytes\n",
+ mtod(m, caddr_t), buflen);
+ ENDDEBUG
+
+ if (buflen > MHLEN)
+ return E_CO_PNA_LONG;
+
+ m->m_pkthdr.len = m->m_len = buflen;
+ lcp->lcd_facilities = m;
+ return 0;
+}
+
+/*
+ * NAME: NSAPtoDTE()
+ * CALLED FROM:
+ * make_partial_x25_packet()
+ * FUNCTION and ARGUMENTS:
+ * get a DTE address from an NSAP-address (struct sockaddr_iso)
+ * (dst_octet) is the octet into which to begin stashing the DTE addr
+ * (dst_nibble) takes 0 or 1. 1 means begin filling in the DTE addr
+ * in the high-order nibble of dst_octet. 0 means low-order nibble.
+ * (addr) is the NSAP-address
+ * (flag) is true if the transport suffix is to become the
+ * last two digits of the DTE address
+ * A DTE address is a series of ASCII digits
+ *
+ * A DTE address may have leading zeros. The are significant.
+ * 1 digit per nibble, may be an odd number of nibbles.
+ *
+ * An NSAP-address has the DTE address in the IDI. Leading zeros are
+ * significant. Trailing hex f indicates the end of the DTE address.
+ * The IDI is a series of BCD digits, one per nibble.
+ *
+ * RETURNS
+ * # significant digits in the DTE address, -1 if error.
+ */
+
+Static int
+NSAPtoDTE(siso, sx25)
+ register struct sockaddr_iso *siso;
+ register struct sockaddr_x25 *sx25;
+{
+ int dtelen = -1;
+
+ IFDEBUG(D_CADDR)
+ printf("NSAPtoDTE: nsap: %s\n", clnp_iso_addrp(&siso->siso_addr));
+ ENDDEBUG
+
+ if (siso->siso_data[0] == AFI_37) {
+ register char *out = sx25->x25_addr;
+ register char *in = siso->siso_data + 1;
+ register int nibble;
+ char *lim = siso->siso_data + siso->siso_nlen;
+ char *olim = out+15;
+ int lowNibble = 0;
+
+ while (in < lim) {
+ nibble = ((lowNibble ? *in++ : (*in >> 4)) & 0xf) | 0x30;
+ lowNibble ^= 1;
+ if (nibble != 0x3f && out < olim)
+ *out++ = nibble;
+ }
+ dtelen = out - sx25->x25_addr;
+ *out++ = 0;
+ } else {
+ /* error = iso_8208snparesolve(addr, x121string, &x121strlen);*/
+ register struct rtentry *rt;
+ extern struct sockaddr_iso blank_siso;
+ struct sockaddr_iso nsiso;
+
+ nsiso = blank_siso;
+ bcopy(nsiso.siso_data, siso->siso_data,
+ nsiso.siso_nlen = siso->siso_nlen);
+ if (rt = rtalloc1(&nsiso, 1)) {
+ register struct sockaddr_x25 *sxx =
+ (struct sockaddr_x25 *)rt->rt_gateway;
+ register char *in = sxx->x25_addr;
+
+ rt->rt_use--;
+ if (sxx && sxx->x25_family == AF_CCITT) {
+ bcopy(sx25->x25_addr, sxx->x25_addr, sizeof(sx25->x25_addr));
+ while (*in++) {}
+ dtelen = in - sxx->x25_addr;
+ }
+ }
+ }
+ return dtelen;
+}
+
+/*
+ * NAME: FACILtoNSAP()
+ * CALLED FROM:
+ * parse_facil()
+ * FUNCTION and ARGUMENTS:
+ * Creates and NSAP in the sockaddr_iso (addr) from the
+ * x.25 facility found at buf - 1.
+ * RETURNS:
+ * 0 if ok, -1 if error.
+ */
+
+Static int
+FACILtoNSAP(addr, buf)
+ register u_char *buf;
+ register struct sockaddr_iso *addr;
+{
+ int len_in_nibbles = *++buf & 0x3f;
+ u_char buf_len = (len_in_nibbles + 1) >> 1;; /* in bytes */
+
+ IFDEBUG(D_CADDR)
+ printf("FACILtoNSAP( 0x%x, 0x%x, 0x%x )\n",
+ buf, buf_len, addr );
+ ENDDEBUG
+
+ len_in_nibbles = *buf & 0x3f;
+ /* despite the fact that X.25 makes us put a length in nibbles
+ * here, the NSAP-addrs are always in full octets
+ */
+ switch (*buf++ & 0xc0) {
+ case 0:
+ /* Entire OSI NSAP address */
+ bcopy((caddr_t)buf, addr->siso_data, addr->siso_nlen = buf_len);
+ break;
+
+ case 40:
+ /* Partial OSI NSAP address, assume trailing */
+ if (buf_len + addr->siso_nlen > sizeof(addr->siso_addr))
+ return -1;
+ bcopy((caddr_t)buf, TSEL(addr), buf_len);
+ addr->siso_nlen += buf_len;
+ break;
+
+ default:
+ /* Rather than blow away the connection, just ignore and use
+ NSAP from DTE */;
+ }
+ return 0;
+}
+
+Static
+init_siso(siso)
+register struct sockaddr_iso *siso;
+{
+ siso->siso_len = sizeof (*siso);
+ siso->siso_family = AF_ISO;
+ siso->siso_data[0] = AFI_37;
+ siso->siso_nlen = 8;
+}
+
+/*
+ * NAME: DTEtoNSAP()
+ * CALLED FROM:
+ * parse_facil()
+ * FUNCTION and ARGUMENTS:
+ * Creates a type 37 NSAP in the sockaddr_iso (addr)
+ * from a DTE address found in a sockaddr_x25.
+ *
+ * RETURNS:
+ * 0 if ok; E* otherwise.
+ */
+
+Static int
+DTEtoNSAP(addr, sx)
+ struct sockaddr_iso *addr;
+ struct sockaddr_x25 *sx;
+{
+ register char *in, *out;
+ register int first;
+ int pad_tail = 0;
+ int src_len;
+
+
+ init_siso(addr);
+ in = sx->x25_addr;
+ src_len = strlen(in);
+ addr->siso_nlen = (src_len + 3) / 2;
+ out = addr->siso_data;
+ *out++ = 0x37;
+ if (src_len & 1) {
+ pad_tail = 0xf;
+ src_len++;
+ }
+ for (first = 0; src_len > 0; src_len--) {
+ first |= 0xf & *in++;
+ if (src_len & 1) {
+ *out++ = first;
+ first = 0;
+ }
+ else first <<= 4;
+ }
+ if (pad_tail)
+ out[-1] |= 0xf;
+ return 0; /* ok */
+}
+
+/*
+ * FUNCTION and ARGUMENTS:
+ * parses (buf_len) bytes beginning at (buf) and finds
+ * a called nsap, a calling nsap, and protocol identifier.
+ * RETURNS:
+ * 0 if ok, E* otherwise.
+ */
+
+Static int
+parse_facil(lcp, isop, buf, buf_len)
+ caddr_t buf;
+ u_char buf_len; /* in bytes */
+ struct isopcb *isop;
+ struct pklcd *lcp;
+{
+ register int i;
+ register u_char *ptr = (u_char *)buf;
+ u_char *ptr_lim, *facil_lim;
+ int facil_param_len, facil_len;
+
+ IFDEBUG(D_CADDR)
+ printf("parse_facil(0x%x, 0x%x, 0x%x, 0x%x)\n",
+ lcp, isop, buf, buf_len);
+ dump_buf(buf, buf_len);
+ ENDDEBUG
+
+ /* find the beginnings of the facility fields in buf
+ * by skipping over the called & calling DTE addresses
+ * i <- # nibbles in called + # nibbles in calling
+ * i += 1 so that an odd nibble gets rounded up to even
+ * before dividing by 2, then divide by two to get # octets
+ */
+ i = (int)(*ptr >> 4) + (int)(*ptr&0xf);
+ i++;
+ ptr += i >> 1;
+ ptr ++; /* plus one for the DTE lengths byte */
+
+ /* ptr now is at facil_length field */
+ facil_len = *ptr++;
+ facil_lim = ptr + facil_len;
+ IFDEBUG(D_CADDR)
+ printf("parse_facils: facil length is 0x%x\n", (int) facil_len);
+ ENDDEBUG
+
+ while (ptr < facil_lim) {
+ /* get NSAP addresses from facilities */
+ switch (*ptr++) {
+ case 0xcb:
+ /* calling NSAP */
+ facil_param_len = FACILtoNSAP(isop->isop_faddr, ptr);
+ break;
+ case 0xc9:
+ /* called NSAP */
+ facil_param_len = FACILtoNSAP(isop->isop_laddr, ptr);
+ break;
+
+ /* from here to default are legit cases that I ignore */
+ /* variable length */
+ case 0xca: /* end-to-end transit delay negot */
+ case 0xc6: /* network user id */
+ case 0xc5: /* charging info : indicating monetary unit */
+ case 0xc2: /* charging info : indicating segment count */
+ case 0xc1: /* charging info : indicating call duration */
+ case 0xc4: /* RPOA extended format */
+ case 0xc3: /* call redirection notification */
+ facil_param_len = 0;
+ break;
+
+ /* 1 octet */
+ case 0x0a: /* min. throughput class negot */
+ case 0x02: /* throughput class */
+ case 0x03: case 0x47: /* CUG shit */
+ case 0x0b: /* expedited data negot */
+ case 0x01: /* Fast select or reverse charging
+ (example of intelligent protocol design) */
+ case 0x04: /* charging info : requesting service */
+ case 0x08: /* called line addr modified notification */
+ case 0x00: /* marker to indicate beginning of CCITT facils */
+ facil_param_len = 1;
+ break;
+
+ /* any 2 octets */
+ case 0x42: /* pkt size */
+ case 0x43: /* win size */
+ case 0x44: /* RPOA basic format */
+ case 0x41: /* bilateral CUG shit */
+ case 0x49: /* transit delay selection and indication */
+ facil_param_len = 2;
+ break;
+
+ default:
+ printf(
+"BOGUS FACILITY CODE facil_lim 0x%x facil_len %d, ptr 0x%x *ptr 0x%x\n",
+ facil_lim, facil_len, ptr - 1, ptr[-1]);
+ /* facil that we don't handle
+ return E_CO_HLI_REJI; */
+ switch (ptr[-1] & 0xc0) {
+ case 0x00: facil_param_len = 1; break;
+ case 0x40: facil_param_len = 2; break;
+ case 0x80: facil_param_len = 3; break;
+ case 0xc0: facil_param_len = 0; break;
+ }
+ }
+ if (facil_param_len == -1)
+ return E_CO_REG_ICDA;
+ if (facil_param_len == 0) /* variable length */
+ facil_param_len = (int)*ptr++; /* 1 + the real facil param */
+ ptr += facil_param_len;
+ }
+ return 0;
+}
+
+#endif /* TPCONS */
diff --git a/sys/netiso/if_eon.c b/sys/netiso/if_eon.c
new file mode 100644
index 0000000..3c05133
--- /dev/null
+++ b/sys/netiso/if_eon.c
@@ -0,0 +1,609 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_eon.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * $Header: if_eon.c,v 1.4 88/07/19 15:53:59 hagens Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/if_eon.c,v $
+ *
+ * EON rfc
+ * Layer between IP and CLNL
+ *
+ * TODO:
+ * Put together a current rfc986 address format and get the right offset
+ * for the nsel
+ */
+
+#ifdef EON
+#define NEON 1
+
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/buf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/netisr.h>
+#include <net/route.h>
+#include <machine/mtpr.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/if_ether.h>
+
+#include <netiso/iso.h>
+#include <netiso/iso_var.h>
+#include <netiso/iso_snpac.h>
+#include <netiso/argo_debug.h>
+#include <netiso/iso_errno.h>
+#include <netiso/eonvar.h>
+
+extern struct timeval time;
+extern struct ifnet loif;
+
+#define EOK 0
+
+int eoninput();
+int eonoutput();
+int eonioctl();
+int eonattach();
+int eoninit();
+void eonrtrequest();
+struct ifnet eonif[1];
+
+eonprotoinit() {
+ (void) eonattach();
+}
+
+struct eon_llinfo eon_llinfo;
+#define PROBE_OK 0;
+
+
+/*
+ * FUNCTION: eonattach
+ *
+ * PURPOSE: autoconf attach routine
+ *
+ * RETURNS: void
+ */
+
+eonattach()
+{
+ register struct ifnet *ifp = eonif;
+
+ IFDEBUG(D_EON)
+ printf("eonattach()\n");
+ ENDDEBUG
+ ifp->if_unit = 0;
+ ifp->if_name = "eon";
+ ifp->if_mtu = ETHERMTU;
+ /* since everything will go out over ether or token ring */
+
+ ifp->if_init = eoninit;
+ ifp->if_ioctl = eonioctl;
+ ifp->if_output = eonoutput;
+ ifp->if_type = IFT_EON;
+ ifp->if_addrlen = 5;
+ ifp->if_hdrlen = EONIPLEN;
+ ifp->if_flags = IFF_BROADCAST;
+ if_attach(ifp);
+ eonioctl(ifp, SIOCSIFADDR, (caddr_t)ifp->if_addrlist);
+ eon_llinfo.el_qhdr.link =
+ eon_llinfo.el_qhdr.rlink = &(eon_llinfo.el_qhdr);
+
+ IFDEBUG(D_EON)
+ printf("eonattach()\n");
+ ENDDEBUG
+}
+
+
+/*
+ * FUNCTION: eonioctl
+ *
+ * PURPOSE: io controls - ifconfig
+ * need commands to
+ * link-UP (core addr) (flags: ES, IS)
+ * link-DOWN (core addr) (flags: ES, IS)
+ * must be callable from kernel or user
+ *
+ * RETURNS: nothing
+ */
+eonioctl(ifp, cmd, data)
+ register struct ifnet *ifp;
+ int cmd;
+ register caddr_t data;
+{
+ int s = splimp();
+ register int error = 0;
+
+ IFDEBUG(D_EON)
+ printf("eonioctl (cmd 0x%x) \n", cmd);
+ ENDDEBUG
+
+ switch (cmd) {
+ register struct ifaddr *ifa;
+
+ case SIOCSIFADDR:
+ if (ifa = (struct ifaddr *)data) {
+ ifp->if_flags |= IFF_UP;
+ if (ifa->ifa_addr->sa_family != AF_LINK)
+ ifa->ifa_rtrequest = eonrtrequest;
+ }
+ break;
+ }
+ splx(s);
+ return(error);
+}
+
+
+eoniphdr(hdr, loc, ro, class, zero)
+struct route *ro;
+register struct eon_iphdr *hdr;
+caddr_t loc;
+{
+ struct mbuf mhead;
+ register struct sockaddr_in *sin = (struct sockaddr_in *)&ro->ro_dst;
+ if (zero) {
+ bzero((caddr_t)hdr, sizeof (*hdr));
+ bzero((caddr_t)ro, sizeof (*ro));
+ }
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof (*sin);
+ bcopy(loc, (caddr_t)&sin->sin_addr, sizeof(struct in_addr));
+ /*
+ * If there is a cached route,
+ * check that it is to the same destination
+ * and is still up. If not, free it and try again.
+ */
+ if (ro->ro_rt) {
+ struct sockaddr_in *dst =
+ (struct sockaddr_in *)rt_key(ro->ro_rt);
+ if ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
+ sin->sin_addr.s_addr != dst->sin_addr.s_addr) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ }
+ rtalloc(ro);
+ if (ro->ro_rt)
+ ro->ro_rt->rt_use++;
+ hdr->ei_ip.ip_dst = sin->sin_addr;
+ hdr->ei_ip.ip_p = IPPROTO_EON;
+ hdr->ei_ip.ip_ttl = MAXTTL;
+ hdr->ei_eh.eonh_class = class;
+ hdr->ei_eh.eonh_vers = EON_VERSION;
+ hdr->ei_eh.eonh_csum = 0;
+ mhead.m_data = (caddr_t) &hdr->ei_eh;
+ mhead.m_len = sizeof(struct eon_hdr);
+ mhead.m_next = 0;
+ IFDEBUG(D_EON)
+ printf("eonoutput : gen csum (0x%x, offset %d, datalen %d)\n",
+ &mhead,
+ _offsetof(struct eon_hdr, eonh_csum), sizeof(struct eon_hdr));
+ ENDDEBUG
+ iso_gen_csum(&mhead,
+ _offsetof(struct eon_hdr, eonh_csum), sizeof(struct eon_hdr));
+}
+/*
+ * FUNCTION: eonrtrequest
+ *
+ * PURPOSE: maintains list of direct eon recipients.
+ * sets up IP route for rest.
+ *
+ * RETURNS: nothing
+ */
+void
+eonrtrequest(cmd, rt, gate)
+register struct rtentry *rt;
+register struct sockaddr *gate;
+{
+ unsigned long zerodst = 0;
+ caddr_t ipaddrloc = (caddr_t) &zerodst;
+ register struct eon_llinfo *el = (struct eon_llinfo *)rt->rt_llinfo;
+
+ /*
+ * Common Housekeeping
+ */
+ switch (cmd) {
+ case RTM_DELETE:
+ if (el) {
+ remque(&(el->el_qhdr));
+ if (el->el_iproute.ro_rt)
+ RTFREE(el->el_iproute.ro_rt);
+ Free(el);
+ rt->rt_llinfo = 0;
+ }
+ return;
+
+ case RTM_ADD:
+ case RTM_RESOLVE:
+ rt->rt_rmx.rmx_mtu = loif.if_mtu; /* unless better below */
+ R_Malloc(el, struct eon_llinfo *, sizeof(*el));
+ rt->rt_llinfo = (caddr_t)el;
+ if (el == 0)
+ return;
+ Bzero(el, sizeof(*el));
+ insque(&(el->el_qhdr), &eon_llinfo.el_qhdr);
+ el->el_rt = rt;
+ break;
+ }
+ if (gate || (gate = rt->rt_gateway)) switch (gate->sa_family) {
+ case AF_LINK:
+#define SDL(x) ((struct sockaddr_dl *)x)
+ if (SDL(gate)->sdl_alen == 1)
+ el->el_snpaoffset = *(u_char *)LLADDR(SDL(gate));
+ else
+ ipaddrloc = LLADDR(SDL(gate));
+ break;
+ case AF_INET:
+#define SIN(x) ((struct sockaddr_in *)x)
+ ipaddrloc = (caddr_t) &SIN(gate)->sin_addr;
+ break;
+ default:
+ return;
+ }
+ el->el_flags |= RTF_UP;
+ eoniphdr(&el->el_ei, ipaddrloc, &el->el_iproute, EON_NORMAL_ADDR, 0);
+ if (el->el_iproute.ro_rt)
+ rt->rt_rmx.rmx_mtu = el->el_iproute.ro_rt->rt_rmx.rmx_mtu
+ - sizeof(el->el_ei);
+}
+
+/*
+ * FUNCTION: eoninit
+ *
+ * PURPOSE: initialization
+ *
+ * RETURNS: nothing
+ */
+
+eoninit(unit)
+ int unit;
+{
+ printf("eon driver-init eon%d\n", unit);
+}
+
+
+/*
+ * FUNCTION: eonoutput
+ *
+ * PURPOSE: prepend an eon header and hand to IP
+ * ARGUMENTS: (ifp) is points to the ifnet structure for this unit/device
+ * (m) is an mbuf *, *m is a CLNL packet
+ * (dst) is a destination address - have to interp. as
+ * multicast or broadcast or real address.
+ *
+ * RETURNS: unix error code
+ *
+ * NOTES:
+ *
+ */
+eonoutput(ifp, m, dst, rt)
+ struct ifnet *ifp;
+ register struct mbuf *m; /* packet */
+ struct sockaddr_iso *dst; /* destination addr */
+ struct rtentry *rt;
+{
+ register struct eon_llinfo *el;
+ register struct eon_iphdr *ei;
+ struct route *ro;
+ int datalen;
+ struct mbuf *mh;
+ int error = 0, class = 0, alen = 0;
+ caddr_t ipaddrloc;
+ static struct eon_iphdr eon_iphdr;
+ static struct route route;
+
+ IFDEBUG(D_EON)
+ printf("eonoutput \n" );
+ ENDDEBUG
+
+ ifp->if_lastchange = time;
+ ifp->if_opackets++;
+ if (rt == 0 || (el = (struct eon_llinfo *)rt->rt_llinfo) == 0) {
+ if (dst->siso_family == AF_LINK) {
+ register struct sockaddr_dl *sdl = (struct sockaddr_dl *)dst;
+
+ ipaddrloc = LLADDR(sdl);
+ alen = sdl->sdl_alen;
+ } else if (dst->siso_family == AF_ISO && dst->siso_data[0] == AFI_SNA) {
+ alen = dst->siso_nlen - 1;
+ ipaddrloc = (caddr_t) dst->siso_data + 1;
+ }
+ switch (alen) {
+ case 5:
+ class = 4[(u_char *)ipaddrloc];
+ case 4:
+ ro = &route;
+ ei = &eon_iphdr;
+ eoniphdr(ei, ipaddrloc, ro, class, 1);
+ goto send;
+ }
+einval:
+ error = EINVAL;
+ goto flush;
+ }
+ if ((el->el_flags & RTF_UP) == 0) {
+ eonrtrequest(RTM_CHANGE, rt, (struct sockaddr *)0);
+ if ((el->el_flags & RTF_UP) == 0) {
+ error = EHOSTUNREACH;
+ goto flush;
+ }
+ }
+ if ((m->m_flags & M_PKTHDR) == 0) {
+ printf("eon: got non headered packet\n");
+ goto einval;
+ }
+ ei = &el->el_ei;
+ ro = &el->el_iproute;
+ if (el->el_snpaoffset) {
+ if (dst->siso_family == AF_ISO) {
+ bcopy((caddr_t) &dst->siso_data[el->el_snpaoffset],
+ (caddr_t) &ei->ei_ip.ip_dst, sizeof(ei->ei_ip.ip_dst));
+ } else
+ goto einval;
+ }
+send:
+ /* put an eon_hdr in the buffer, prepended by an ip header */
+ datalen = m->m_pkthdr.len + EONIPLEN;
+ MGETHDR(mh, M_DONTWAIT, MT_HEADER);
+ if(mh == (struct mbuf *)0)
+ goto flush;
+ mh->m_next = m;
+ m = mh;
+ MH_ALIGN(m, sizeof(struct eon_iphdr));
+ m->m_len = sizeof(struct eon_iphdr);
+ ifp->if_obytes +=
+ (ei->ei_ip.ip_len = (u_short)(m->m_pkthdr.len = datalen));
+ *mtod(m, struct eon_iphdr *) = *ei;
+
+ IFDEBUG(D_EON)
+ printf("eonoutput dst ip addr : %x\n", ei->ei_ip.ip_dst.s_addr);
+ printf("eonoutput ip_output : eonip header:\n");
+ dump_buf(ei, sizeof(struct eon_iphdr));
+ ENDDEBUG
+
+ error = ip_output(m, (struct mbuf *)0, ro, 0, NULL);
+ m = 0;
+ if (error) {
+ ifp->if_oerrors++;
+ ifp->if_opackets--;
+ ifp->if_obytes -= datalen;
+ }
+flush:
+ if (m)
+ m_freem(m);
+ return error;
+}
+
+eoninput(m, iphlen)
+ register struct mbuf *m;
+ int iphlen;
+{
+ register struct eon_hdr *eonhdr;
+ register struct ip *iphdr;
+ struct ifnet *eonifp;
+ int s;
+
+ eonifp = &eonif[0]; /* kludge - really want to give CLNP
+ * the ifp for eon, not for the real device
+ */
+
+ IFDEBUG(D_EON)
+ printf("eoninput() 0x%x m_data 0x%x m_len 0x%x dequeued\n",
+ m, m?m->m_data:0, m?m->m_len:0);
+ ENDDEBUG
+
+ if (m == 0)
+ return;
+ if (iphlen > sizeof (struct ip))
+ ip_stripoptions(m, (struct mbuf *)0);
+ if (m->m_len < EONIPLEN) {
+ if ((m = m_pullup(m, EONIPLEN)) == 0) {
+ IncStat(es_badhdr);
+drop:
+ IFDEBUG(D_EON)
+ printf("eoninput: DROP \n" );
+ ENDDEBUG
+ eonifp->if_ierrors ++;
+ m_freem(m);
+ return;
+ }
+ }
+ eonif->if_ibytes += m->m_pkthdr.len;
+ eonif->if_lastchange = time;
+ iphdr = mtod(m, struct ip *);
+ /* do a few checks for debugging */
+ if( iphdr->ip_p != IPPROTO_EON ) {
+ IncStat(es_badhdr);
+ goto drop;
+ }
+ /* temporarily drop ip header from the mbuf */
+ m->m_data += sizeof(struct ip);
+ eonhdr = mtod(m, struct eon_hdr *);
+ if( iso_check_csum( m, sizeof(struct eon_hdr) ) != EOK ) {
+ IncStat(es_badcsum);
+ goto drop;
+ }
+ m->m_data -= sizeof(struct ip);
+
+ IFDEBUG(D_EON)
+ printf("eoninput csum ok class 0x%x\n", eonhdr->eonh_class );
+ printf("eoninput: eon header:\n");
+ dump_buf(eonhdr, sizeof(struct eon_hdr));
+ ENDDEBUG
+
+ /* checks for debugging */
+ if( eonhdr->eonh_vers != EON_VERSION) {
+ IncStat(es_badhdr);
+ goto drop;
+ }
+ m->m_flags &= ~(M_BCAST|M_MCAST);
+ switch( eonhdr->eonh_class) {
+ case EON_BROADCAST:
+ IncStat(es_in_broad);
+ m->m_flags |= M_BCAST;
+ break;
+ case EON_NORMAL_ADDR:
+ IncStat(es_in_normal);
+ break;
+ case EON_MULTICAST_ES:
+ IncStat(es_in_multi_es);
+ m->m_flags |= M_MCAST;
+ break;
+ case EON_MULTICAST_IS:
+ IncStat(es_in_multi_is);
+ m->m_flags |= M_MCAST;
+ break;
+ }
+ eonifp->if_ipackets++;
+
+ {
+ /* put it on the CLNP queue and set soft interrupt */
+ struct ifqueue *ifq;
+ extern struct ifqueue clnlintrq;
+
+ m->m_pkthdr.rcvif = eonifp; /* KLUDGE */
+ IFDEBUG(D_EON)
+ printf("eoninput to clnl IFQ\n");
+ ENDDEBUG
+ ifq = &clnlintrq;
+ s = splimp();
+ if (IF_QFULL(ifq)) {
+ IF_DROP(ifq);
+ m_freem(m);
+ eonifp->if_iqdrops++;
+ eonifp->if_ipackets--;
+ splx(s);
+ return;
+ }
+ IF_ENQUEUE(ifq, m);
+ IFDEBUG(D_EON)
+ printf(
+ "0x%x enqueued on clnp Q: m_len 0x%x m_type 0x%x m_data 0x%x\n",
+ m, m->m_len, m->m_type, m->m_data);
+ dump_buf(mtod(m, caddr_t), m->m_len);
+ ENDDEBUG
+ schednetisr(NETISR_ISO);
+ splx(s);
+ }
+}
+
+int
+eonctlinput(cmd, sin)
+ int cmd;
+ struct sockaddr_in *sin;
+{
+ extern u_char inetctlerrmap[];
+
+ IFDEBUG(D_EON)
+ printf("eonctlinput: cmd 0x%x addr: ", cmd);
+ dump_isoaddr(sin);
+ printf("\n");
+ ENDDEBUG
+
+ if (cmd < 0 || cmd > PRC_NCMDS)
+ return 0;
+
+ IncStat(es_icmp[cmd]);
+ switch (cmd) {
+
+ case PRC_QUENCH:
+ case PRC_QUENCH2:
+ /* TODO: set the dec bit */
+ break;
+ case PRC_TIMXCEED_REASS:
+ case PRC_ROUTEDEAD:
+ case PRC_HOSTUNREACH:
+ case PRC_UNREACH_NET:
+ case PRC_IFDOWN:
+ case PRC_UNREACH_HOST:
+ case PRC_HOSTDEAD:
+ case PRC_TIMXCEED_INTRANS:
+ /* TODO: mark the link down */
+ break;
+
+ case PRC_UNREACH_PROTOCOL:
+ case PRC_UNREACH_PORT:
+ case PRC_UNREACH_SRCFAIL:
+ case PRC_REDIRECT_NET:
+ case PRC_REDIRECT_HOST:
+ case PRC_REDIRECT_TOSNET:
+ case PRC_REDIRECT_TOSHOST:
+ case PRC_MSGSIZE:
+ case PRC_PARAMPROB:
+ /* printf("eonctlinput: ICMP cmd 0x%x\n", cmd );*/
+ break;
+ }
+ return 0;
+}
+
+#endif
diff --git a/sys/netiso/iso.c b/sys/netiso/iso.c
new file mode 100644
index 0000000..cd64e68
--- /dev/null
+++ b/sys/netiso/iso.c
@@ -0,0 +1,919 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)iso.c 8.2 (Berkeley) 11/15/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * $Header: iso.c,v 4.11 88/09/19 14:58:35 root Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/iso.c,v $
+ *
+ * iso.c: miscellaneous routines to support the iso address family
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioctl.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netiso/iso.h>
+#include <netiso/iso_var.h>
+#include <netiso/iso_snpac.h>
+#include <netiso/iso_pcb.h>
+#include <netiso/clnp.h>
+#include <netiso/argo_debug.h>
+#ifdef TUBA
+#include <netiso/tuba_table.h>
+#endif
+
+#ifdef ISO
+
+int iso_interfaces = 0; /* number of external interfaces */
+extern struct ifnet loif; /* loopback interface */
+int ether_output();
+void llc_rtrequest();
+
+/*
+ * FUNCTION: iso_addrmatch1
+ *
+ * PURPOSE: decide if the two iso_addrs passed are equal
+ *
+ * RETURNS: true if the addrs match, false if they do not
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+iso_addrmatch1(isoaa, isoab)
+register struct iso_addr *isoaa, *isoab; /* addresses to check */
+{
+ u_int compare_len;
+
+ IFDEBUG(D_ROUTE)
+ printf("iso_addrmatch1: comparing lengths: %d to %d\n", isoaa->isoa_len,
+ isoab->isoa_len);
+ printf("a:\n");
+ dump_buf(isoaa->isoa_genaddr, isoaa->isoa_len);
+ printf("b:\n");
+ dump_buf(isoab->isoa_genaddr, isoab->isoa_len);
+ ENDDEBUG
+
+ if ((compare_len = isoaa->isoa_len) != isoab->isoa_len) {
+ IFDEBUG(D_ROUTE)
+ printf("iso_addrmatch1: returning false because of lengths\n");
+ ENDDEBUG
+ return 0;
+ }
+
+#ifdef notdef
+ /* TODO : generalize this to all afis with masks */
+ if( isoaa->isoa_afi == AFI_37 ) {
+ /* must not compare 2 least significant digits, or for
+ * that matter, the DSP
+ */
+ compare_len = ADDR37_IDI_LEN - 1;
+ }
+#endif
+
+ IFDEBUG(D_ROUTE)
+ int i;
+ char *a, *b;
+
+ a = isoaa->isoa_genaddr;
+ b = isoab->isoa_genaddr;
+
+ for (i=0; i<compare_len; i++) {
+ printf("<%x=%x>", a[i]&0xff, b[i]&0xff);
+ if (a[i] != b[i]) {
+ printf("\naddrs are not equal at byte %d\n", i);
+ return(0);
+ }
+ }
+ printf("\n");
+ printf("addrs are equal\n");
+ return (1);
+ ENDDEBUG
+ return (!bcmp(isoaa->isoa_genaddr, isoab->isoa_genaddr, compare_len));
+}
+
+/*
+ * FUNCTION: iso_addrmatch
+ *
+ * PURPOSE: decide if the two sockadrr_isos passed are equal
+ *
+ * RETURNS: true if the addrs match, false if they do not
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+iso_addrmatch(sisoa, sisob)
+struct sockaddr_iso *sisoa, *sisob; /* addresses to check */
+{
+ return(iso_addrmatch1(&sisoa->siso_addr, &sisob->siso_addr));
+}
+#ifdef notdef
+/*
+ * FUNCTION: iso_netmatch
+ *
+ * PURPOSE: similar to iso_addrmatch but takes sockaddr_iso
+ * as argument.
+ *
+ * RETURNS: true if same net, false if not
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+iso_netmatch(sisoa, sisob)
+struct sockaddr_iso *sisoa, *sisob;
+{
+ u_char bufa[sizeof(struct sockaddr_iso)];
+ u_char bufb[sizeof(struct sockaddr_iso)];
+ register int lena, lenb;
+
+ lena = iso_netof(&sisoa->siso_addr, bufa);
+ lenb = iso_netof(&sisob->siso_addr, bufb);
+
+ IFDEBUG(D_ROUTE)
+ printf("iso_netmatch: comparing lengths: %d to %d\n", lena, lenb);
+ printf("a:\n");
+ dump_buf(bufa, lena);
+ printf("b:\n");
+ dump_buf(bufb, lenb);
+ ENDDEBUG
+
+ return ((lena == lenb) && (!bcmp(bufa, bufb, lena)));
+}
+#endif /* notdef */
+
+/*
+ * FUNCTION: iso_hashchar
+ *
+ * PURPOSE: Hash all character in the buffer specified into
+ * a long. Return the long.
+ *
+ * RETURNS: The hash value.
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: The hash is achieved by exclusive ORing 4 byte
+ * quantities.
+ */
+u_long
+iso_hashchar(buf, len)
+register caddr_t buf; /* buffer to pack from */
+register int len; /* length of buffer */
+{
+ register u_long h = 0;
+ register int i;
+
+ for (i=0; i<len; i+=4) {
+ register u_long l = 0;
+
+ if ((len - i) < 4) {
+ /* buffer not multiple of 4 */
+ switch (len - i) {
+ case 3:
+ l |= buf[i+2] << 8;
+ case 2:
+ l |= buf[i+1] << 16;
+ case 1:
+ l |= buf[i] << 24;
+ break;
+ default:
+ printf("iso_hashchar: unexpected value x%x\n", len - i);
+ break;
+ }
+ } else {
+ l |= buf[i] << 24;
+ l |= buf[i+1] << 16;
+ l |= buf[i+2] << 8;
+ l |= buf[i+3];
+ }
+
+ h ^= l;
+ }
+
+ h ^= (u_long) (len % 4);
+
+ return(h);
+}
+#ifdef notdef
+/*
+ * FUNCTION: iso_hash
+ *
+ * PURPOSE: Fill in fields of afhash structure based upon addr passed.
+ *
+ * RETURNS: none
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+iso_hash(siso, hp)
+struct sockaddr_iso *siso; /* address to perform hash on */
+struct afhash *hp; /* RETURN: hash info here */
+{
+ u_long buf[sizeof(struct sockaddr_iso)+1/4];
+ register int bufsize;
+
+
+ bzero(buf, sizeof(buf));
+
+ bufsize = iso_netof(&siso->siso_addr, buf);
+ hp->afh_nethash = iso_hashchar((caddr_t)buf, bufsize);
+
+ IFDEBUG(D_ROUTE)
+ printf("iso_hash: iso_netof: bufsize = %d\n", bufsize);
+ ENDDEBUG
+
+ hp->afh_hosthash = iso_hashchar((caddr_t)&siso->siso_addr,
+ siso->siso_addr.isoa_len);
+
+ IFDEBUG(D_ROUTE)
+ printf("iso_hash: %s: nethash = x%x, hosthash = x%x\n",
+ clnp_iso_addrp(&siso->siso_addr), hp->afh_nethash,
+ hp->afh_hosthash);
+ ENDDEBUG
+}
+/*
+ * FUNCTION: iso_netof
+ *
+ * PURPOSE: Extract the network portion of the iso address.
+ * The network portion of the iso address varies depending
+ * on the type of address. The network portion of the
+ * address will include the IDP. The network portion is:
+ *
+ * TYPE DESC
+ * t37 The AFI and x.121 (IDI)
+ * osinet The AFI, orgid, snetid
+ * rfc986 The AFI, vers and network part of
+ * internet address.
+ *
+ * RETURNS: number of bytes placed into buf.
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: Buf is assumed to be big enough
+ */
+iso_netof(isoa, buf)
+struct iso_addr *isoa; /* address */
+caddr_t buf; /* RESULT: network portion of address here */
+{
+ u_int len = 1; /* length of afi */
+
+ switch (isoa->isoa_afi) {
+ case AFI_37:
+ /*
+ * Due to classic x.25 tunnel vision, there is no
+ * net portion of an x.121 address. For our purposes
+ * the AFI will do, so that all x.25 -type addresses
+ * map to the single x.25 SNPA. (Cannot have more than
+ * one, obviously).
+ */
+
+ break;
+
+/* case AFI_OSINET:*/
+ case AFI_RFC986: {
+ u_short idi; /* value of idi */
+
+ /* osinet and rfc986 have idi in the same place */
+ CTOH(isoa->rfc986_idi[0], isoa->rfc986_idi[1], idi);
+
+ if (idi == IDI_OSINET)
+/*
+ * Network portion of OSINET address can only be the IDI. Clearly,
+ * with one x25 interface, one could get to several orgids, and
+ * several snetids.
+ len += (ADDROSINET_IDI_LEN + OVLOSINET_ORGID_LEN +
+ OVLOSINET_SNETID_LEN);
+ */
+ len += ADDROSINET_IDI_LEN;
+ else if (idi == IDI_RFC986) {
+ u_long inetaddr;
+ struct ovl_rfc986 *o986 = (struct ovl_rfc986 *)isoa;
+
+ /* bump len to include idi and version (1 byte) */
+ len += ADDRRFC986_IDI_LEN + 1;
+
+ /* get inet addr long aligned */
+ bcopy(o986->o986_inetaddr, &inetaddr, sizeof(inetaddr));
+ inetaddr = ntohl(inetaddr); /* convert to host byte order */
+
+ IFDEBUG(D_ROUTE)
+ printf("iso_netof: isoa ");
+ dump_buf(isoa, sizeof(*isoa));
+ printf("iso_netof: inetaddr 0x%x ", inetaddr);
+ ENDDEBUG
+
+ /* bump len by size of network portion of inet address */
+ if (IN_CLASSA(inetaddr)) {
+ len += 4-IN_CLASSA_NSHIFT/8;
+ IFDEBUG(D_ROUTE)
+ printf("iso_netof: class A net len is now %d\n", len);
+ ENDDEBUG
+ } else if (IN_CLASSB(inetaddr)) {
+ len += 4-IN_CLASSB_NSHIFT/8;
+ IFDEBUG(D_ROUTE)
+ printf("iso_netof: class B net len is now %d\n", len);
+ ENDDEBUG
+ } else {
+ len += 4-IN_CLASSC_NSHIFT/8;
+ IFDEBUG(D_ROUTE)
+ printf("iso_netof: class C net len is now %d\n", len);
+ ENDDEBUG
+ }
+ } else
+ len = 0;
+ } break;
+
+ default:
+ len = 0;
+ }
+
+ bcopy((caddr_t)isoa, buf, len);
+ IFDEBUG(D_ROUTE)
+ printf("iso_netof: isoa ");
+ dump_buf(isoa, len);
+ printf("iso_netof: net ");
+ dump_buf(buf, len);
+ ENDDEBUG
+ return len;
+}
+#endif /* notdef */
+/*
+ * Generic iso control operations (ioctl's).
+ * Ifp is 0 if not an interface-specific ioctl.
+ */
+/* ARGSUSED */
+iso_control(so, cmd, data, ifp)
+ struct socket *so;
+ int cmd;
+ caddr_t data;
+ register struct ifnet *ifp;
+{
+ register struct iso_ifreq *ifr = (struct iso_ifreq *)data;
+ register struct iso_ifaddr *ia = 0;
+ register struct ifaddr *ifa;
+ struct iso_ifaddr *oia;
+ struct iso_aliasreq *ifra = (struct iso_aliasreq *)data;
+ int error, hostIsNew, maskIsNew;
+
+ /*
+ * Find address for this interface, if it exists.
+ */
+ if (ifp)
+ for (ia = iso_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+
+ switch (cmd) {
+
+ case SIOCAIFADDR_ISO:
+ case SIOCDIFADDR_ISO:
+ if (ifra->ifra_addr.siso_family == AF_ISO)
+ for (oia = ia; ia; ia = ia->ia_next) {
+ if (ia->ia_ifp == ifp &&
+ SAME_ISOADDR(&ia->ia_addr, &ifra->ifra_addr))
+ break;
+ }
+ if ((so->so_state & SS_PRIV) == 0)
+ return (EPERM);
+ if (ifp == 0)
+ panic("iso_control");
+ if (ia == (struct iso_ifaddr *)0) {
+ struct iso_ifaddr *nia;
+ if (cmd == SIOCDIFADDR_ISO)
+ return (EADDRNOTAVAIL);
+#ifdef TUBA
+ /* XXXXXX can't be done in the proto init routines */
+ if (tuba_tree == 0)
+ tuba_table_init();
+#endif
+ MALLOC(nia, struct iso_ifaddr *, sizeof(*nia),
+ M_IFADDR, M_WAITOK);
+ if (nia == (struct iso_ifaddr *)0)
+ return (ENOBUFS);
+ bzero((caddr_t)nia, sizeof(*nia));
+ if (ia = iso_ifaddr) {
+ for ( ; ia->ia_next; ia = ia->ia_next)
+ ;
+ ia->ia_next = nia;
+ } else
+ iso_ifaddr = nia;
+ ia = nia;
+ if (ifa = ifp->if_addrlist) {
+ for ( ; ifa->ifa_next; ifa = ifa->ifa_next)
+ ;
+ ifa->ifa_next = (struct ifaddr *) ia;
+ } else
+ ifp->if_addrlist = (struct ifaddr *) ia;
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ ia->ia_ifa.ifa_dstaddr
+ = (struct sockaddr *)&ia->ia_dstaddr;
+ ia->ia_ifa.ifa_netmask
+ = (struct sockaddr *)&ia->ia_sockmask;
+ ia->ia_ifp = ifp;
+ if (ifp != &loif)
+ iso_interfaces++;
+ }
+ break;
+
+#define cmdbyte(x) (((x) >> 8) & 0xff)
+ default:
+ if (cmdbyte(cmd) == 'a')
+ return (snpac_ioctl(so, cmd, data));
+ if (ia == (struct iso_ifaddr *)0)
+ return (EADDRNOTAVAIL);
+ break;
+ }
+ switch (cmd) {
+
+ case SIOCGIFADDR_ISO:
+ ifr->ifr_Addr = ia->ia_addr;
+ break;
+
+ case SIOCGIFDSTADDR_ISO:
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
+ return (EINVAL);
+ ifr->ifr_Addr = ia->ia_dstaddr;
+ break;
+
+ case SIOCGIFNETMASK_ISO:
+ ifr->ifr_Addr = ia->ia_sockmask;
+ break;
+
+ case SIOCAIFADDR_ISO:
+ maskIsNew = 0; hostIsNew = 1; error = 0;
+ if (ia->ia_addr.siso_family == AF_ISO) {
+ if (ifra->ifra_addr.siso_len == 0) {
+ ifra->ifra_addr = ia->ia_addr;
+ hostIsNew = 0;
+ } else if (SAME_ISOADDR(&ia->ia_addr, &ifra->ifra_addr))
+ hostIsNew = 0;
+ }
+ if (ifra->ifra_mask.siso_len) {
+ iso_ifscrub(ifp, ia);
+ ia->ia_sockmask = ifra->ifra_mask;
+ maskIsNew = 1;
+ }
+ if ((ifp->if_flags & IFF_POINTOPOINT) &&
+ (ifra->ifra_dstaddr.siso_family == AF_ISO)) {
+ iso_ifscrub(ifp, ia);
+ ia->ia_dstaddr = ifra->ifra_dstaddr;
+ maskIsNew = 1; /* We lie; but the effect's the same */
+ }
+ if (ifra->ifra_addr.siso_family == AF_ISO &&
+ (hostIsNew || maskIsNew)) {
+ error = iso_ifinit(ifp, ia, &ifra->ifra_addr, 0);
+ }
+ if (ifra->ifra_snpaoffset)
+ ia->ia_snpaoffset = ifra->ifra_snpaoffset;
+ return (error);
+
+ case SIOCDIFADDR_ISO:
+ iso_ifscrub(ifp, ia);
+ if ((ifa = ifp->if_addrlist) == (struct ifaddr *)ia)
+ ifp->if_addrlist = ifa->ifa_next;
+ else {
+ while (ifa->ifa_next &&
+ (ifa->ifa_next != (struct ifaddr *)ia))
+ ifa = ifa->ifa_next;
+ if (ifa->ifa_next)
+ ifa->ifa_next = ((struct ifaddr *)ia)->ifa_next;
+ else
+ printf("Couldn't unlink isoifaddr from ifp\n");
+ }
+ oia = ia;
+ if (oia == (ia = iso_ifaddr)) {
+ iso_ifaddr = ia->ia_next;
+ } else {
+ while (ia->ia_next && (ia->ia_next != oia)) {
+ ia = ia->ia_next;
+ }
+ if (ia->ia_next)
+ ia->ia_next = oia->ia_next;
+ else
+ printf("Didn't unlink isoifadr from list\n");
+ }
+ IFAFREE((&oia->ia_ifa));
+ break;
+
+ default:
+ if (ifp == 0 || ifp->if_ioctl == 0)
+ return (EOPNOTSUPP);
+ return ((*ifp->if_ioctl)(ifp, cmd, data));
+ }
+ return (0);
+}
+
+/*
+ * Delete any existing route for an interface.
+ */
+iso_ifscrub(ifp, ia)
+ register struct ifnet *ifp;
+ register struct iso_ifaddr *ia;
+{
+ int nsellength = ia->ia_addr.siso_tlen;
+ if ((ia->ia_flags & IFA_ROUTE) == 0)
+ return;
+ ia->ia_addr.siso_tlen = 0;
+ if (ifp->if_flags & IFF_LOOPBACK)
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
+ else if (ifp->if_flags & IFF_POINTOPOINT)
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
+ else {
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
+ }
+ ia->ia_addr.siso_tlen = nsellength;
+ ia->ia_flags &= ~IFA_ROUTE;
+}
+
+/*
+ * Initialize an interface's internet address
+ * and routing table entry.
+ */
+iso_ifinit(ifp, ia, siso, scrub)
+ register struct ifnet *ifp;
+ register struct iso_ifaddr *ia;
+ struct sockaddr_iso *siso;
+{
+ struct sockaddr_iso oldaddr;
+ int s = splimp(), error, nsellength;
+
+ oldaddr = ia->ia_addr;
+ ia->ia_addr = *siso;
+ /*
+ * Give the interface a chance to initialize
+ * if this is its first address,
+ * and to validate the address if necessary.
+ */
+ if (ifp->if_ioctl &&
+ (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) {
+ splx(s);
+ ia->ia_addr = oldaddr;
+ return (error);
+ }
+ if (scrub) {
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr;
+ iso_ifscrub(ifp, ia);
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ }
+ /* XXX -- The following is here temporarily out of laziness
+ in not changing every ethernet driver's if_ioctl routine */
+ if (ifp->if_output == ether_output) {
+ ia->ia_ifa.ifa_rtrequest = llc_rtrequest;
+ ia->ia_ifa.ifa_flags |= RTF_CLONING;
+ }
+ /*
+ * Add route for the network.
+ */
+ nsellength = ia->ia_addr.siso_tlen;
+ ia->ia_addr.siso_tlen = 0;
+ if (ifp->if_flags & IFF_LOOPBACK) {
+ ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr;
+ error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP);
+ } else if (ifp->if_flags & IFF_POINTOPOINT &&
+ ia->ia_dstaddr.siso_family == AF_ISO)
+ error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP);
+ else {
+ rt_maskedcopy(ia->ia_ifa.ifa_addr, ia->ia_ifa.ifa_dstaddr,
+ ia->ia_ifa.ifa_netmask);
+ ia->ia_dstaddr.siso_nlen =
+ min(ia->ia_addr.siso_nlen, (ia->ia_sockmask.siso_len - 6));
+ error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP);
+ }
+ ia->ia_addr.siso_tlen = nsellength;
+ ia->ia_flags |= IFA_ROUTE;
+ splx(s);
+ return (error);
+}
+#ifdef notdef
+
+struct ifaddr *
+iso_ifwithidi(addr)
+ register struct sockaddr *addr;
+{
+ register struct ifnet *ifp;
+ register struct ifaddr *ifa;
+ register u_int af = addr->sa_family;
+
+ if (af != AF_ISO)
+ return (0);
+ IFDEBUG(D_ROUTE)
+ printf(">>> iso_ifwithidi addr\n");
+ dump_isoaddr( (struct sockaddr_iso *)(addr));
+ printf("\n");
+ ENDDEBUG
+ for (ifp = ifnet; ifp; ifp = ifp->if_next) {
+ IFDEBUG(D_ROUTE)
+ printf("iso_ifwithidi ifnet %s\n", ifp->if_name);
+ ENDDEBUG
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
+ IFDEBUG(D_ROUTE)
+ printf("iso_ifwithidi address ");
+ dump_isoaddr( (struct sockaddr_iso *)(ifa->ifa_addr));
+ ENDDEBUG
+ if (ifa->ifa_addr->sa_family != addr->sa_family)
+ continue;
+
+#define IFA_SIS(ifa)\
+ ((struct sockaddr_iso *)((ifa)->ifa_addr))
+
+ IFDEBUG(D_ROUTE)
+ printf(" af same, args to iso_eqtype:\n");
+ printf("0x%x ", IFA_SIS(ifa)->siso_addr);
+ printf(" 0x%x\n",
+ &(((struct sockaddr_iso *)addr)->siso_addr));
+ ENDDEBUG
+
+ if (iso_eqtype(&(IFA_SIS(ifa)->siso_addr),
+ &(((struct sockaddr_iso *)addr)->siso_addr))) {
+ IFDEBUG(D_ROUTE)
+ printf("ifa_ifwithidi: ifa found\n");
+ ENDDEBUG
+ return (ifa);
+ }
+ IFDEBUG(D_ROUTE)
+ printf(" iso_eqtype failed\n");
+ ENDDEBUG
+ }
+ }
+ return ((struct ifaddr *)0);
+}
+
+#endif /* notdef */
+/*
+ * FUNCTION: iso_ck_addr
+ *
+ * PURPOSE: return true if the iso_addr passed is
+ * within the legal size limit for an iso address.
+ *
+ * RETURNS: true or false
+ *
+ * SIDE EFFECTS:
+ *
+ */
+iso_ck_addr(isoa)
+struct iso_addr *isoa; /* address to check */
+{
+ return (isoa->isoa_len <= 20);
+
+}
+
+#ifdef notdef
+/*
+ * FUNCTION: iso_eqtype
+ *
+ * PURPOSE: Determine if two iso addresses are of the same type.
+ * This is flaky. Really we should consider all type 47 addrs to be the
+ * same - but there do exist different structures for 47 addrs.
+ * Gosip adds a 3rd.
+ *
+ * RETURNS: true if the addresses are the same type
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: By type, I mean rfc986, t37, or osinet
+ *
+ * This will first compare afis. If they match, then
+ * if the addr is not t37, the idis must be compared.
+ */
+iso_eqtype(isoaa, isoab)
+struct iso_addr *isoaa; /* first addr to check */
+struct iso_addr *isoab; /* other addr to check */
+{
+ if (isoaa->isoa_afi == isoab->isoa_afi) {
+ if (isoaa->isoa_afi == AFI_37)
+ return(1);
+ else
+ return (!bcmp(&isoaa->isoa_u, &isoab->isoa_u, 2));
+ }
+ return(0);
+}
+#endif /* notdef */
+/*
+ * FUNCTION: iso_localifa()
+ *
+ * PURPOSE: Find an interface addresss having a given destination
+ * or at least matching the net.
+ *
+ * RETURNS: ptr to an interface address
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+struct iso_ifaddr *
+iso_localifa(siso)
+ register struct sockaddr_iso *siso;
+{
+ register struct iso_ifaddr *ia;
+ register char *cp1, *cp2, *cp3;
+ register struct ifnet *ifp;
+ struct iso_ifaddr *ia_maybe = 0;
+ /*
+ * We make one pass looking for both net matches and an exact
+ * dst addr.
+ */
+ for (ia = iso_ifaddr; ia; ia = ia->ia_next) {
+ if ((ifp = ia->ia_ifp) == 0 || ((ifp->if_flags & IFF_UP) == 0))
+ continue;
+ if (ifp->if_flags & IFF_POINTOPOINT) {
+ if ((ia->ia_dstaddr.siso_family == AF_ISO) &&
+ SAME_ISOADDR(&ia->ia_dstaddr, siso))
+ return (ia);
+ else
+ if (SAME_ISOADDR(&ia->ia_addr, siso))
+ ia_maybe = ia;
+ continue;
+ }
+ if (ia->ia_sockmask.siso_len) {
+ char *cplim = ia->ia_sockmask.siso_len + (char *)&ia->ia_sockmask;
+ cp1 = ia->ia_sockmask.siso_data;
+ cp2 = siso->siso_data;
+ cp3 = ia->ia_addr.siso_data;
+ while (cp1 < cplim)
+ if (*cp1++ & (*cp2++ ^ *cp3++))
+ goto next;
+ ia_maybe = ia;
+ }
+ if (SAME_ISOADDR(&ia->ia_addr, siso))
+ return ia;
+ next:;
+ }
+ return ia_maybe;
+}
+
+#ifdef TPCONS
+#include <netiso/cons.h>
+#endif /* TPCONS */
+/*
+ * FUNCTION: iso_nlctloutput
+ *
+ * PURPOSE: Set options at the network level
+ *
+ * RETURNS: E*
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: This could embody some of the functions of
+ * rclnp_ctloutput and cons_ctloutput.
+ */
+iso_nlctloutput(cmd, optname, pcb, m)
+int cmd; /* command:set or get */
+int optname; /* option of interest */
+caddr_t pcb; /* nl pcb */
+struct mbuf *m; /* data for set, buffer for get */
+{
+ struct isopcb *isop = (struct isopcb *)pcb;
+ int error = 0; /* return value */
+ caddr_t data; /* data for option */
+ int data_len; /* data's length */
+
+ IFDEBUG(D_ISO)
+ printf("iso_nlctloutput: cmd %x, opt %x, pcb %x, m %x\n",
+ cmd, optname, pcb, m);
+ ENDDEBUG
+
+ if ((cmd != PRCO_GETOPT) && (cmd != PRCO_SETOPT))
+ return(EOPNOTSUPP);
+
+ data = mtod(m, caddr_t);
+ data_len = (m)->m_len;
+
+ IFDEBUG(D_ISO)
+ printf("iso_nlctloutput: data is:\n");
+ dump_buf(data, data_len);
+ ENDDEBUG
+
+ switch (optname) {
+
+#ifdef TPCONS
+ case CONSOPT_X25CRUD:
+ if (cmd == PRCO_GETOPT) {
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ if (data_len > MAXX25CRUDLEN) {
+ error = EINVAL;
+ break;
+ }
+
+ IFDEBUG(D_ISO)
+ printf("iso_nlctloutput: setting x25 crud\n");
+ ENDDEBUG
+
+ bcopy(data, (caddr_t)isop->isop_x25crud, (unsigned)data_len);
+ isop->isop_x25crud_len = data_len;
+ break;
+#endif /* TPCONS */
+
+ default:
+ error = EOPNOTSUPP;
+ }
+ if (cmd == PRCO_SETOPT)
+ m_freem(m);
+ return error;
+}
+#endif /* ISO */
+
+#ifdef ARGO_DEBUG
+
+/*
+ * FUNCTION: dump_isoaddr
+ *
+ * PURPOSE: debugging
+ *
+ * RETURNS: nada
+ *
+ */
+dump_isoaddr(s)
+ struct sockaddr_iso *s;
+{
+ char *clnp_saddr_isop();
+ register int i;
+
+ if( s->siso_family == AF_ISO) {
+ printf("ISO address: suffixlen %d, %s\n",
+ s->siso_tlen, clnp_saddr_isop(s));
+ } else if( s->siso_family == AF_INET) {
+ /* hack */
+ struct sockaddr_in *sin = (struct sockaddr_in *)s;
+
+ printf("%d.%d.%d.%d: %d",
+ (sin->sin_addr.s_addr>>24)&0xff,
+ (sin->sin_addr.s_addr>>16)&0xff,
+ (sin->sin_addr.s_addr>>8)&0xff,
+ (sin->sin_addr.s_addr)&0xff,
+ sin->sin_port);
+ }
+}
+
+#endif /* ARGO_DEBUG */
diff --git a/sys/netiso/iso.h b/sys/netiso/iso.h
new file mode 100644
index 0000000..9237e6a
--- /dev/null
+++ b/sys/netiso/iso.h
@@ -0,0 +1,195 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)iso.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: iso.h,v 4.9 88/09/11 18:06:38 hagens Exp $ */
+/* $Source: /usr/argo/sys/netiso/RCS/iso.h,v $ */
+
+#ifndef __ISO__
+#define __ISO__
+
+/*
+ * Return true if this is a multicast address
+ * This assumes that the bit transmission is lsb first. This
+ * assumption is valid for 802.3 but not 802.5. There is a
+ * kludge to get around this for 802.5 -- see if_lan.c
+ * where subnetwork header is setup.
+ */
+#define IS_MULTICAST(snpa)\
+ ((snpa)[0] & 0x01)
+
+/*
+ * Protocols
+ */
+#define ISOPROTO_TCP 6 /* IETF experiment */
+#define ISOPROTO_UDP 17 /* IETF experiment */
+#define ISOPROTO_TP0 25 /* connection oriented transport protocol */
+#define ISOPROTO_TP1 26 /* not implemented */
+#define ISOPROTO_TP2 27 /* not implemented */
+#define ISOPROTO_TP3 28 /* not implemented */
+#define ISOPROTO_TP4 29 /* connection oriented transport protocol */
+#define ISOPROTO_TP ISOPROTO_TP4 /* tp-4 with negotiation */
+#define ISOPROTO_CLTP 30 /* connectionless transport (not yet impl.) */
+#define ISOPROTO_CLNP 31 /* connectionless internetworking protocol */
+#define ISOPROTO_X25 32 /* cons */
+#define ISOPROTO_INACT_NL 33 /* inactive network layer! */
+#define ISOPROTO_ESIS 34 /* ES-IS protocol */
+#define ISOPROTO_INTRAISIS 35 /* IS-IS protocol */
+#define ISOPROTO_IDRP 36 /* Interdomain Routing Protocol */
+
+#define ISOPROTO_RAW 255 /* raw clnp */
+#define ISOPROTO_MAX 256
+
+#define ISO_PORT_RESERVED 1024
+#define ISO_PORT_USERRESERVED 5000
+/*
+ * Port/socket numbers: standard network functions
+ * NOT PRESENTLY USED
+ */
+#define ISO_PORT_MAINT 501
+#define ISO_PORT_ECHO 507
+#define ISO_PORT_DISCARD 509
+#define ISO_PORT_SYSTAT 511
+#define ISO_PORT_NETSTAT 515
+/*
+ * Port/socket numbers: non-standard application functions
+ */
+#define ISO_PORT_LOGIN 513
+/*
+ * Port/socket numbers: public use
+ */
+#define ISO_PORT_PUBLIC 1024 /* high bit set --> public */
+
+/*
+ * Network layer protocol identifiers
+ */
+#define ISO8473_CLNP 0x81
+#define ISO9542_ESIS 0x82
+#define ISO9542X25_ESIS 0x8a
+#define ISO10589_ISIS 0x83
+#define ISO8878A_CONS 0x84
+#define ISO10747_IDRP 0x85
+
+
+#ifndef IN_CLASSA_NET
+#include <netinet/in.h>
+#endif /* IN_CLASSA_NET */
+
+
+
+/* The following looks like a sockaddr
+ * to facilitate using tree lookup routines */
+struct iso_addr {
+ u_char isoa_len; /* length (in bytes) */
+ char isoa_genaddr[20]; /* general opaque address */
+};
+
+struct sockaddr_iso {
+ u_char siso_len; /* length */
+ u_char siso_family; /* family */
+ u_char siso_plen; /* presentation selector length */
+ u_char siso_slen; /* session selector length */
+ u_char siso_tlen; /* transport selector length */
+ struct iso_addr siso_addr; /* network address */
+ u_char siso_pad[6]; /* space for gosip v2 sels */
+ /* makes struct 32 bytes long */
+};
+#define siso_nlen siso_addr.isoa_len
+#define siso_data siso_addr.isoa_genaddr
+
+#define TSEL(s) ((caddr_t)((s)->siso_data + (s)->siso_nlen))
+
+#define SAME_ISOADDR(a, b) \
+ (bcmp((a)->siso_data, (b)->siso_data, (unsigned)(a)->siso_nlen)==0)
+/*
+ * The following are specific values for siso->siso_data[0],
+ * otherwise known as the AFI:
+ */
+#define AFI_37 0x37 /* bcd of "37" */
+#define AFI_OSINET 0x47 /* bcd of "47" */
+#define AFI_RFC986 0x47 /* bcd of "47" */
+#define AFI_SNA 0x00 /* SubNetwork Address; invalid really...*/
+
+#ifdef KERNEL
+
+extern int iso_netmatch();
+extern int iso_hash();
+extern int iso_addrmatch();
+extern struct iso_ifaddr *iso_iaonnetof();
+extern struct domain isodomain;
+extern struct protosw isosw[];
+
+#else
+/* user utilities definitions from the iso library */
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+struct iso_addr *iso_addr __P((const char *));
+char *iso_ntoa __P((const struct iso_addr *));
+
+/* THESE DON'T EXIST YET */
+struct hostent *iso_gethostbyname(), *iso_gethostbyaddr();
+__END_DECLS
+
+#endif /* KERNEL */
+
+#define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m))
+#endif /* __ISO__ */
diff --git a/sys/netiso/iso_chksum.c b/sys/netiso/iso_chksum.c
new file mode 100644
index 0000000..5b1aae5
--- /dev/null
+++ b/sys/netiso/iso_chksum.c
@@ -0,0 +1,360 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)iso_chksum.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * $Header: iso_chksum.c,v 4.7 88/07/29 15:31:26 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/iso_chksum.c,v $
+ *
+ * ISO CHECKSUM
+ *
+ * The checksum generation and check routines are here.
+ * The checksum is 2 bytes such that the sum of all the bytes b(i) == 0
+ * and the sum of i * b(i) == 0.
+ * The whole thing is complicated by the fact that the data are in mbuf
+ * chains.
+ * Furthermore, there is the possibility of wraparound in the running
+ * sums after adding up 4102 octets. In order to avoid doing a mod
+ * operation after EACH add, we have restricted this implementation to
+ * negotiating a maximum of 4096-octets per TPDU (for the transport layer).
+ * The routine iso_check_csum doesn't need to know where the checksum
+ * octets are.
+ * The routine iso_gen_csum takes a pointer to an mbuf chain (logically
+ * a chunk of data), an offset into the chunk at which the 2 octets are to
+ * be stuffed, and the length of the chunk. The 2 octets have to be
+ * logically adjacent, but may be physically located in separate mbufs.
+ */
+
+#ifdef ISO
+#include <netiso/argo_debug.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#endif /* ISO */
+
+#ifndef MNULL
+#define MNULL (struct mbuf *)0
+#endif /* MNULL */
+
+/*
+ * FUNCTION: iso_check_csum
+ *
+ * PURPOSE: To check the checksum of the packet in the mbuf chain (m).
+ * The total length of the packet is (len).
+ * Called from tp_input() and clnp_intr()
+ *
+ * RETURNS: TRUE (something non-zero) if there is a checksum error,
+ * FALSE if there was NO checksum error.
+ *
+ * SIDE EFFECTS: none
+ *
+ * NOTES: It might be possible to gain something by optimizing
+ * this routine (unrolling loops, etc). But it is such
+ * a horrible thing to fiddle with anyway, it probably
+ * isn't worth it.
+ */
+int
+iso_check_csum(m, len)
+ struct mbuf *m;
+ int len;
+{
+ register u_char *p = mtod(m, u_char *);
+ register u_long c0=0, c1=0;
+ register int i=0;
+ int cum = 0; /* cumulative length */
+ int l;
+
+ l = len;
+ len = min(m->m_len, len);
+ i = 0;
+
+ IFDEBUG(D_CHKSUM)
+ printf("iso_check_csum: m x%x, l x%x, m->m_len x%x\n", m, l, m->m_len);
+ ENDDEBUG
+
+ while( i<l ) {
+ cum += len;
+ while (i<cum) {
+ c0 = c0 + *(p++);
+ c1 += c0;
+ i++;
+ }
+ if(i < l) {
+ m = m->m_next;
+ IFDEBUG(D_CHKSUM)
+ printf("iso_check_csum: new mbuf\n");
+ if(l-i < m->m_len)
+ printf(
+ "bad mbuf chain in check csum l 0x%x i 0x%x m_data 0x%x",
+ l,i,m->m_data);
+ ENDDEBUG
+ ASSERT( m != MNULL);
+ len = min( m->m_len, l-i);
+ p = mtod(m, u_char *);
+ }
+ }
+ if ( ((int)c0 % 255) || ((int)c1 % 255) ) {
+ IFDEBUG(D_CHKSUM)
+ printf("BAD iso_check_csum l 0x%x cum 0x%x len 0x%x, i 0x%x",
+ l, cum, len, i);
+ ENDDEBUG
+ return ((int)c0 % 255)<<8 | ((int)c1 % 255);
+ }
+ return 0;
+}
+
+/*
+ * FUNCTION: iso_gen_csum
+ *
+ * PURPOSE: To generate the checksum of the packet in the mbuf chain (m).
+ * The first of the 2 (logically) adjacent checksum bytes
+ * (x and y) go at offset (n).
+ * (n) is an offset relative to the beginning of the data,
+ * not the beginning of the mbuf.
+ * (l) is the length of the total mbuf chain's data.
+ * Called from tp_emit(), tp_error_emit()
+ * clnp_emit_er(), clnp_forward(), clnp_output().
+ *
+ * RETURNS: Rien
+ *
+ * SIDE EFFECTS: Puts the 2 checksum bytes into the packet.
+ *
+ * NOTES: Ditto the note for iso_check_csum().
+ */
+
+void
+iso_gen_csum(m,n,l)
+ struct mbuf *m;
+ int n; /* offset of 2 checksum bytes */
+ int l;
+{
+ register u_char *p = mtod(m, u_char *);
+ register int c0=0, c1=0;
+ register int i=0;
+ int loc = n++, len=0; /* n is position, loc is offset */
+ u_char *xloc;
+ u_char *yloc;
+ int cum=0; /* cum == cumulative length */
+
+ IFDEBUG(D_CHKSUM)
+ printf("enter gen csum m 0x%x n 0x%x l 0x%x\n",m, n-1 ,l );
+ ENDDEBUG
+
+ while(i < l) {
+ len = min(m->m_len, CLBYTES);
+ /* RAH: don't cksum more than l bytes */
+ len = min(len, l - i);
+
+ cum +=len;
+ p = mtod(m, u_char *);
+
+ if(loc>=0) {
+ if (loc < len) {
+ xloc = loc + mtod(m, u_char *);
+ IFDEBUG(D_CHKSUM)
+ printf("1: zeroing xloc 0x%x loc 0x%x\n",xloc, loc );
+ ENDDEBUG
+ *xloc = (u_char)0;
+ if (loc+1 < len) {
+ /* both xloc and yloc are in same mbuf */
+ yloc = 1 + xloc;
+ IFDEBUG(D_CHKSUM)
+ printf("2: zeroing yloc 0x%x loc 0x%x\n",yloc, loc );
+ ENDDEBUG
+ *yloc = (u_char)0;
+ } else {
+ /* crosses boundary of mbufs */
+ yloc = mtod(m->m_next, u_char *);
+ IFDEBUG(D_CHKSUM)
+ printf("3: zeroing yloc 0x%x \n",yloc );
+ ENDDEBUG
+ *yloc = (u_char)0;
+ }
+ }
+ loc -= len;
+ }
+
+ while(i < cum) {
+ c0 = (c0 + *p);
+ c1 += c0 ;
+ i++;
+ p++;
+ }
+ m = m->m_next;
+ }
+ IFDEBUG(D_CHKSUM)
+ printf("gen csum final xloc 0x%x yloc 0x%x\n",xloc, yloc );
+ ENDDEBUG
+
+ c1 = (((c0 * (l-n))-c1)%255) ;
+ *xloc = (u_char) ((c1 < 0)? c1+255 : c1);
+
+ c1 = (-(int)(c1+c0))%255;
+ *yloc = (u_char) (c1 < 0? c1 + 255 : c1);
+
+ IFDEBUG(D_CHKSUM)
+ printf("gen csum end \n");
+ ENDDEBUG
+}
+
+/*
+ * FUNCTION: m_datalen
+ *
+ * PURPOSE: returns length of the mbuf chain.
+ * used all over the iso code.
+ *
+ * RETURNS: integer
+ *
+ * SIDE EFFECTS: none
+ *
+ * NOTES:
+ */
+
+int
+m_datalen (m)
+ register struct mbuf *m;
+{
+ register int datalen;
+
+ for (datalen = 0; m; m = m->m_next)
+ datalen += m->m_len;
+ return datalen;
+}
+
+int
+m_compress(in, out)
+ register struct mbuf *in, **out;
+{
+ register int datalen = 0;
+ int s = splimp();
+
+ if( in->m_next == MNULL ) {
+ *out = in;
+ IFDEBUG(D_REQUEST)
+ printf("m_compress returning 0x%x: A\n", in->m_len);
+ ENDDEBUG
+ splx(s);
+ return in->m_len;
+ }
+ MGET((*out), M_DONTWAIT, MT_DATA);
+ if((*out) == MNULL) {
+ *out = in;
+ IFDEBUG(D_REQUEST)
+ printf("m_compress returning -1: B\n");
+ ENDDEBUG
+ splx(s);
+ return -1;
+ }
+ (*out)->m_len = 0;
+ (*out)->m_act = MNULL;
+
+ while (in) {
+ IFDEBUG(D_REQUEST)
+ printf("m_compress in 0x%x *out 0x%x\n", in, *out);
+ printf("m_compress in: len 0x%x, off 0x%x\n", in->m_len, in->m_data);
+ printf("m_compress *out: len 0x%x, off 0x%x\n", (*out)->m_len,
+ (*out)->m_data);
+ ENDDEBUG
+ if (in->m_flags & M_EXT) {
+ ASSERT(in->m_len == 0);
+ }
+ if ( in->m_len == 0) {
+ in = in->m_next;
+ continue;
+ }
+ if (((*out)->m_flags & M_EXT) == 0) {
+ int len;
+
+ len = M_TRAILINGSPACE(*out);
+ len = min(len, in->m_len);
+ datalen += len;
+
+ IFDEBUG(D_REQUEST)
+ printf("m_compress copying len %d\n", len);
+ ENDDEBUG
+ bcopy(mtod(in, caddr_t), mtod((*out), caddr_t) + (*out)->m_len,
+ (unsigned)len);
+
+ (*out)->m_len += len;
+ in->m_len -= len;
+ continue;
+ } else {
+ /* (*out) is full */
+ if(( (*out)->m_next = m_get(M_DONTWAIT, MT_DATA) ) == MNULL) {
+ m_freem(*out);
+ *out = in;
+ IFDEBUG(D_REQUEST)
+ printf("m_compress returning -1: B\n");
+ ENDDEBUG
+ splx(s);
+ return -1;
+ }
+ (*out)->m_len = 0;
+ (*out)->m_act = MNULL;
+ *out = (*out)->m_next;
+ }
+ }
+ m_freem(in);
+ IFDEBUG(D_REQUEST)
+ printf("m_compress returning 0x%x: A\n", datalen);
+ ENDDEBUG
+ splx(s);
+ return datalen;
+}
diff --git a/sys/netiso/iso_errno.h b/sys/netiso/iso_errno.h
new file mode 100644
index 0000000..0d75589c
--- /dev/null
+++ b/sys/netiso/iso_errno.h
@@ -0,0 +1,274 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)iso_errno.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+
+#ifndef __ISO_ERRNO__
+#define __ISO_ERRNO__
+
+#define ISO_ERROR_MASK 0x8000
+#define BSD_ERROR_MASK 0x0000
+#define TP_ERROR_MASK 0x8800 /* transport layer */
+#define CONL_ERROR_MASK 0x8400 /* co network layer */
+#define CLNL_ERROR_MASK 0x8200 /* cl network layer */
+#define TP_ERROR_SNDC 0x10000 /* kludge to force DC's on certain errors */
+
+#define E_CO_NOERROR (CONL_ERROR_MASK | 0x0) /* no add'l info */
+
+/******************************************************************************/
+/* */
+/* */
+/* Transport Layer */
+/* */
+/* */
+/******************************************************************************/
+
+#define E_TP_DR_NO_REAS (TP_ERROR_MASK | 0x0) /* dr reason not specified*/
+#define E_TP_CONGEST (TP_ERROR_MASK | 0x1) /* dr reason congestion */
+#define E_TP_NO_SESSION (TP_ERROR_MASK | 0x2) /* dr reason no sess ent */
+#define E_TP_ADDR_UNK (TP_ERROR_MASK | 0x3) /* dr reason addr unknown */
+
+#define E_TP_ER_NO_REAS (TP_ERROR_MASK | 0x40) /* er reas not specified */
+#define E_TP_INV_PCODE (TP_ERROR_MASK | 0x41) /* er reas invalid parm code */
+#define E_TP_INV_TPDU (TP_ERROR_MASK | 0x42) /* er reas invalid tpdu type */
+#define E_TP_INV_PVAL (TP_ERROR_MASK | 0x43) /* er reas invalid parm value*/
+
+#define E_TP_NORMAL_DISC (TP_ERROR_MASK | 0x80) /* dr reas normal disc */
+#define E_TP_CONGEST_2 (TP_ERROR_MASK | 0x81) /* dr reason congestion */
+#define E_TP_NEGOT_FAILED (TP_ERROR_MASK | 0x82) /* dr negotiation failed */
+#define E_TP_DUPL_SRCREF (TP_ERROR_MASK | 0x83) /* dr duplicate src ref */
+#define E_TP_MISM_REFS (TP_ERROR_MASK | 0x84) /* dr mismatched references*/
+#define E_TP_PROTO_ERR (TP_ERROR_MASK | 0x85) /* dr protocol error*/
+/* 0x86 not used */
+#define E_TP_REF_OVERFLOW (TP_ERROR_MASK | 0x87) /* dr reference overflow */
+#define E_TP_NO_CR_ON_NC (TP_ERROR_MASK | 0x88) /* dr cr refused on this nc */
+/* 0x89 not used */
+#define E_TP_LENGTH_INVAL (TP_ERROR_MASK | 0x8a) /* dr inval length in hdr*/
+
+/******************************************************************************/
+/* */
+/* */
+/* Connection Less Network Layer */
+/* */
+/* */
+/******************************************************************************/
+
+#define E_CLNL_??? (CLNL_ERROR_MASK | 0x1) /* explanation */
+
+/******************************************************************************/
+/* */
+/* */
+/* Connection Oriented Network Layer */
+/* */
+/* */
+/******************************************************************************/
+ /* see p. 149 of ISO 8208 */
+#define E_CO_NOERROR (CONL_ERROR_MASK | 0x0) /* no add'l info */
+#define E_CO_INV_PS (CONL_ERROR_MASK | 0x1) /* invalid p(s) */
+#define E_CO_INV_PR (CONL_ERROR_MASK | 0x2) /* invalid p(r) */
+ /* dot dot dot */
+#define E_CO_INV_PKT_TYPE (CONL_ERROR_MASK | 0x10) /* packet type invalid*/
+#define E_CO_INV_PKT_R1 (CONL_ERROR_MASK | 0x11) /* for state r1 */
+#define E_CO_INV_PKT_R2 (CONL_ERROR_MASK | 0x12) /* for state r2 */
+#define E_CO_INV_PKT_R3 (CONL_ERROR_MASK | 0x13) /* for state r3 */
+#define E_CO_INV_PKT_P1 (CONL_ERROR_MASK | 0x14) /* for state p1 */
+#define E_CO_INV_PKT_P2 (CONL_ERROR_MASK | 0x15) /* for state p2 */
+#define E_CO_INV_PKT_P3 (CONL_ERROR_MASK | 0x16) /* for state p3 */
+#define E_CO_INV_PKT_P4 (CONL_ERROR_MASK | 0x17) /* for state p4 */
+#define E_CO_INV_PKT_P5 (CONL_ERROR_MASK | 0x18) /* for state p5 */
+#define E_CO_INV_PKT_P6 (CONL_ERROR_MASK | 0x19) /* for state p6 */
+#define E_CO_INV_PKT_P7 (CONL_ERROR_MASK | 0x1a) /* for state p7 */
+#define E_CO_INV_PKT_D1 (CONL_ERROR_MASK | 0x1b) /* for state d1 */
+#define E_CO_INV_PKT_D2 (CONL_ERROR_MASK | 0x1c) /* for state d2 */
+#define E_CO_INV_PKT_D3 (CONL_ERROR_MASK | 0x1d) /* for state d3 */
+ /* dot dot dot */
+#define E_CO_PKT_NOT_ALWD (CONL_ERROR_MASK | 0x20) /* packet not allowed */
+#define E_CO_PNA_UNIDENT (CONL_ERROR_MASK | 0x21) /* unidentifiable pkt */
+#define E_CO_PNA_ONEWAY (CONL_ERROR_MASK | 0x22) /* call on 1-way lc */
+#define E_CO_PNA_PVC (CONL_ERROR_MASK | 0x23) /* inv pkt type on a pvc */
+#define E_CO_PNA_UNASSLC (CONL_ERROR_MASK | 0x24) /* pkt on unassigned lc */
+#define E_CO_PNA_REJECT (CONL_ERROR_MASK | 0x25) /* REJ not subscribed to*/
+#define E_CO_PNA_SHORT (CONL_ERROR_MASK | 0x26) /* pkt too short */
+#define E_CO_PNA_LONG (CONL_ERROR_MASK | 0x27) /* pkt too long */
+#define E_CO_PNA_INVGFI (CONL_ERROR_MASK | 0x28) /* inv gen format id */
+#define E_CO_PNA_NZLCI (CONL_ERROR_MASK | 0x29) \
+ /* restart or reg pkt with nonzero logical channel identifier */
+#define E_CO_PNA_FACIL (CONL_ERROR_MASK | 0x2a) \
+ /* pkt type not compat with facility */
+#define E_CO_PNA_UINTCON (CONL_ERROR_MASK | 0x2b) /* unauthor intrpt conf */
+#define E_CO_PNA_UINTRPT (CONL_ERROR_MASK | 0x2c) /* unauthorized intrpt */
+#define E_CO_PNA_UREJECT (CONL_ERROR_MASK | 0x2d) /* unauthorized reject */
+
+#define E_CO_TMR_EXP (CONL_ERROR_MASK | 0x30) /* timer expired */
+#define E_CO_TMR_CALR (CONL_ERROR_MASK | 0x31) /* inc. call or call req */
+#define E_CO_TMR_CLRI (CONL_ERROR_MASK | 0x32) /* clear indication */
+#define E_CO_TMR_RSTI (CONL_ERROR_MASK | 0x33) /* reset indication */
+#define E_CO_TMR_RRTI (CONL_ERROR_MASK | 0x34) /* restart indication */
+
+#define E_CO_REG_PROB (CONL_ERROR_MASK | 0x40)\
+ /* call setup, clear, or registration problem */
+#define E_CO_REG_CODE (CONL_ERROR_MASK | 0x41) /* code not allowed */
+#define E_CO_REG_PARM (CONL_ERROR_MASK | 0x42) /* parameter not allowed */
+#define E_CO_REG_ICDA (CONL_ERROR_MASK | 0x43) /* invalid called addr */
+#define E_CO_REG_ICGA (CONL_ERROR_MASK | 0x44) /* invalid calling addr */
+#define E_CO_REG_ILEN (CONL_ERROR_MASK | 0x45) /* invalid facil length */
+#define E_CO_REG_IBAR (CONL_ERROR_MASK | 0x46) /* incoming call barred */
+#define E_CO_REG_NOLC (CONL_ERROR_MASK | 0x47) /* no logical chan avail*/
+#define E_CO_REG_COLL (CONL_ERROR_MASK | 0x48) /* call collision */
+#define E_CO_REG_DUPF (CONL_ERROR_MASK | 0x49) /* dupl facil requested */
+#define E_CO_REG_NZAL (CONL_ERROR_MASK | 0x4a) /* non-zero addr length */
+#define E_CO_REG_NZFL (CONL_ERROR_MASK | 0x4b) /* non-zero facil length */
+#define E_CO_REG_EFNP (CONL_ERROR_MASK | 0x4c) \
+ /* expected facil not provided */
+#define E_CO_REG_ICCITT (CONL_ERROR_MASK | 0x4d) \
+ /* invalid CCITT-specified DTE facil */
+
+#define E_CO_MISC (CONL_ERROR_MASK | 0x50) /* miscellaneous */
+#define E_CO_MISC_CAUSE (CONL_ERROR_MASK | 0x51) /* improper cause code */
+#define E_CO_MISC_ALIGN (CONL_ERROR_MASK | 0x52) /* not octet-aligned */
+#define E_CO_MISC_IQBS (CONL_ERROR_MASK | 0x53) \
+ /* inconsistent Q bit settings */
+
+#define E_CO_INTL (CONL_ERROR_MASK | 0x70) /* international problem */
+#define E_CO_IREMNWK (CONL_ERROR_MASK | 0x71) /* remote network problem */
+#define E_CO_INPROTO (CONL_ERROR_MASK | 0x72) /* int'l protocol problem */
+#define E_CO_ILINKDWN (CONL_ERROR_MASK | 0x73) /* int'l link down */
+#define E_CO_ILINKBSY (CONL_ERROR_MASK | 0x74) /* int'l link busy */
+#define E_CO_IXNETFAC (CONL_ERROR_MASK | 0x75) /* transit netwk facil */
+#define E_CO_IRNETFAC (CONL_ERROR_MASK | 0x76) /* remote netwk facil */
+#define E_CO_IROUTING (CONL_ERROR_MASK | 0x77) /* int'l routing prob */
+#define E_CO_ITMPRTG (CONL_ERROR_MASK | 0x78) /* temporary routing prob */
+#define E_CO_IUNKDNIC (CONL_ERROR_MASK | 0x79) /* unknown called DNIC */
+#define E_CO_IMAINT (CONL_ERROR_MASK | 0x7a) /* maintenance action */
+
+#define E_CO_TIMO (CONL_ERROR_MASK | 0x90) \
+ /* timer expired or retransmission count surpassed */
+#define E_CO_TIM_INTRP (CONL_ERROR_MASK | 0x91) /* for interrupt */
+#define E_CO_TIM_DATA (CONL_ERROR_MASK | 0x92) /* for data */
+#define E_CO_TIM_REJ (CONL_ERROR_MASK | 0x93) /* for reject */
+
+#define E_CO_DTE_SPEC (CONL_ERROR_MASK | 0xa0) /* DTE-specific */
+#define E_CO_DTE_OK (CONL_ERROR_MASK | 0xa1) /* DTE operational */
+#define E_CO_DTE_NOK (CONL_ERROR_MASK | 0xa2) /* DTE not operational */
+#define E_CO_DTE_RSRC (CONL_ERROR_MASK | 0xa3) /* DTE resource constraint*/
+#define E_CO_DTE_FSLCT (CONL_ERROR_MASK | 0xa4) /* fast select not subsc */
+#define E_CO_DTE_PFPKT (CONL_ERROR_MASK | 0xa5) /* partially full pkt */
+#define E_CO_DTE_DBIT (CONL_ERROR_MASK | 0xa6) /* D-bit proc not supp */
+#define E_CO_DTE_RCCON (CONL_ERROR_MASK | 0xa7) /* reg/canell confirmed */
+
+#define E_CO_OSI_NSP (CONL_ERROR_MASK | 0xe0) /* OSI net svc problem */
+#define E_CO_OSI_DISCT (CONL_ERROR_MASK | 0xe1) /* disconnect transient */
+#define E_CO_OSI_DISCP (CONL_ERROR_MASK | 0xe2) /* disconnect permanent */
+#define E_CO_OSI_REJT (CONL_ERROR_MASK | 0xe3) /* reject transient */
+#define E_CO_OSI_REJP (CONL_ERROR_MASK | 0xe4) /* reject permanent */
+#define E_CO_OSI_QOST (CONL_ERROR_MASK | 0xe5) /* reject QOS transient */
+#define E_CO_OSI_QOSP (CONL_ERROR_MASK | 0xe6) /* reject QOS permanent */
+#define E_CO_OSI_NSAPT (CONL_ERROR_MASK | 0xe7) /* NSAP unreach transient */
+#define E_CO_OSI_NSAPP (CONL_ERROR_MASK | 0xe8) /* NSAP unreach permanent */
+#define E_CO_OSI_RESET (CONL_ERROR_MASK | 0xe9) /* reset no reason */
+#define E_CO_OSI_CONGEST (CONL_ERROR_MASK | 0xea) /* reset congestion */
+#define E_CO_OSI_UNSAP (CONL_ERROR_MASK | 0xeb) /* unknown NSAP permanent */
+
+#define E_CO_HLI_INIT (CONL_ERROR_MASK | 0xf0) /* higher level initiated*/
+#define E_CO_HLI_DISCN (CONL_ERROR_MASK | 0xf1) /* disconnect normal */
+#define E_CO_HLI_DISCA (CONL_ERROR_MASK | 0xf2) /* disconnect abnormal */
+#define E_CO_HLI_DISCI (CONL_ERROR_MASK | 0xf3) /* disconnect incompatible*/
+#define E_CO_HLI_REJT (CONL_ERROR_MASK | 0xf4) /* reject transient */
+#define E_CO_HLI_REJP (CONL_ERROR_MASK | 0xf5) /* reject permanent */
+#define E_CO_HLI_QOST (CONL_ERROR_MASK | 0xf6) /* reject QOS transient */
+#define E_CO_HLI_QOSP (CONL_ERROR_MASK | 0xf7) /* reject QOS permanent */
+#define E_CO_HLI_REJI (CONL_ERROR_MASK | 0xf8) /* reject incompatible */
+#define E_CO_HLI_PROTOID (CONL_ERROR_MASK | 0xf9) /* unrecog proto id */
+#define E_CO_HLI_RESYNC (CONL_ERROR_MASK | 0xfa) /* reset - user resync */
+
+/* Cause on 8208 CLEAR field */
+#define E_CO_NUMBERBUSY (CONL_ERROR_MASK | 0x101) /* Number busy */
+#define E_CO_INVFACREQ (CONL_ERROR_MASK | 0x103) /* invalid facil req */
+#define E_CO_NETCONGEST (CONL_ERROR_MASK | 0x105) /* Network congestion */
+#define E_CO_OUTOFORDER (CONL_ERROR_MASK | 0x109) /* Out of order */
+#define E_CO_ACCESSBAR (CONL_ERROR_MASK | 0x10b) /* access barred */
+#define E_CO_NOTOBTAIN (CONL_ERROR_MASK | 0x10d) /* not obtainable */
+#define E_CO_REMPROCERR (CONL_ERROR_MASK | 0x111) /* Remote procedure err */
+#define E_CO_LOCPROCERR (CONL_ERROR_MASK | 0x113) /* Local procedure err */
+#define E_CO_RPOAOOO (CONL_ERROR_MASK | 0x115) /* RPOA out of order */
+#define E_CO_NOREVCHG (CONL_ERROR_MASK | 0x119) /* Revs chg not accepted*/
+#define E_CO_INCOMPAT (CONL_ERROR_MASK | 0x121) /* Incompatible dest */
+#define E_CO_NOFASTSEL (CONL_ERROR_MASK | 0x129)
+ /* Fast select accpt not subscribed */
+#define E_CO_NOSHIP (CONL_ERROR_MASK | 0x139) /* ship absent */
+#define E_CO_GWPROCERR (CONL_ERROR_MASK | 0x1c1) /* Gateway-detected err*/
+#define E_CO_GWCONGEST (CONL_ERROR_MASK | 0x1c3) /* Gateway congestion*/
+
+/* ARGO only */
+#define E_CO_QFULL (CONL_ERROR_MASK | 0x100) /* dropped packet - queue full*/
+#define E_CO_AIWP (CONL_ERROR_MASK | 0x102) /* addr incompat w/proto */
+#define E_CO_CHAN (CONL_ERROR_MASK | 0x104) /* bad channel number */
+
+/* ARGO only; driver specific */
+#define E_CO_NORESOURCES (CONL_ERROR_MASK | 0x1b0) /* eicon clogged */
+#define E_CO_PDNDOWN (CONL_ERROR_MASK | 0x1b1) /* physical net down */
+#define E_CO_DRVRCLRESET (CONL_ERROR_MASK | 0x1b2) /* driver clear/reset */
+#define E_CO_PDNCLRESET (CONL_ERROR_MASK | 0x1b3) /* PDN clear/reset */
+#define E_CO_DTECLRESET (CONL_ERROR_MASK | 0x1b4) /* board clear/reset */
+#define E_CO_UNKCLRESET (CONL_ERROR_MASK | 0x1b5) /* unexpected clr/rst */
+
+#define CONL_ERROR_MAX 0x1c3
+
+#endif /* __ISO_ERRNO__ */
diff --git a/sys/netiso/iso_pcb.c b/sys/netiso/iso_pcb.c
new file mode 100644
index 0000000..0b50c60
--- /dev/null
+++ b/sys/netiso/iso_pcb.c
@@ -0,0 +1,617 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)iso_pcb.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * $Header: iso_pcb.c,v 4.5 88/06/29 14:59:56 hagens Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/iso_pcb.c,v $
+ *
+ * Iso address family net-layer(s) pcb stuff. NEH 1/29/87
+ */
+
+#ifdef ISO
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <netiso/argo_debug.h>
+#include <netiso/iso.h>
+#include <netiso/clnp.h>
+#include <netinet/in_systm.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netiso/iso_pcb.h>
+#include <netiso/iso_var.h>
+#include <sys/protosw.h>
+
+#ifdef TPCONS
+#include <netccitt/x25.h>
+#include <netccitt/pk.h>
+#include <netccitt/pk_var.h>
+#endif
+
+#define PCBNULL (struct isopcb *)0
+struct iso_addr zeroiso_addr = {
+ 0
+};
+
+
+/*
+ * FUNCTION: iso_pcballoc
+ *
+ * PURPOSE: creates an isopcb structure in an mbuf,
+ * with socket (so), and
+ * puts it in the queue with head (head)
+ *
+ * RETURNS: 0 if OK, ENOBUFS if can't alloc the necessary mbuf
+ */
+int
+iso_pcballoc(so, head)
+ struct socket *so;
+ struct isopcb *head;
+{
+ register struct isopcb *isop;
+
+ IFDEBUG(D_ISO)
+ printf("iso_pcballoc(so 0x%x)\n", so);
+ ENDDEBUG
+ MALLOC(isop, struct isopcb *, sizeof(*isop), M_PCB, M_NOWAIT);
+ if (isop == NULL)
+ return ENOBUFS;
+ bzero((caddr_t)isop, sizeof(*isop));
+ isop->isop_head = head;
+ isop->isop_socket = so;
+ insque(isop, head);
+ if (so)
+ so->so_pcb = (caddr_t)isop;
+ return 0;
+}
+
+/*
+ * FUNCTION: iso_pcbbind
+ *
+ * PURPOSE: binds the address given in *(nam) to the socket
+ * specified by the isopcb in *(isop)
+ * If the given address is zero, it makes sure the
+ * address isn't already in use and if it's got a network
+ * portion, we look for an interface with that network
+ * address. If the address given is zero, we allocate
+ * a port and stuff it in the (nam) structure.
+ *
+ * RETURNS: errno E* or 0 if ok.
+ *
+ * SIDE EFFECTS: increments head->isop_lport if it allocates a port #
+ *
+ * NOTES:
+ */
+#define satosiso(sa) ((struct sockaddr_iso *)(sa))
+int
+iso_pcbbind(isop, nam)
+ register struct isopcb *isop;
+ struct mbuf *nam;
+{
+ register struct isopcb *head = isop->isop_head;
+ register struct sockaddr_iso *siso;
+ struct iso_ifaddr *ia;
+ union {
+ char data[2];
+ u_short s;
+ } suf;
+
+ IFDEBUG(D_ISO)
+ printf("iso_pcbbind(isop 0x%x, nam 0x%x)\n", isop, nam);
+ ENDDEBUG
+ suf.s = 0;
+ if (iso_ifaddr == 0) /* any interfaces attached? */
+ return EADDRNOTAVAIL;
+ if (isop->isop_laddr) /* already bound */
+ return EADDRINUSE;
+ if(nam == (struct mbuf *)0) {
+ isop->isop_laddr = &isop->isop_sladdr;
+ isop->isop_sladdr.siso_len = sizeof(struct sockaddr_iso);
+ isop->isop_sladdr.siso_family = AF_ISO;
+ isop->isop_sladdr.siso_tlen = 2;
+ isop->isop_sladdr.siso_nlen = 0;
+ isop->isop_sladdr.siso_slen = 0;
+ isop->isop_sladdr.siso_plen = 0;
+ goto noname;
+ }
+ siso = mtod(nam, struct sockaddr_iso *);
+ IFDEBUG(D_ISO)
+ printf("iso_pcbbind(name len 0x%x)\n", nam->m_len);
+ printf("The address is %s\n", clnp_iso_addrp(&siso->siso_addr));
+ ENDDEBUG
+ /*
+ * We would like sort of length check but since some OSI addrs
+ * do not have fixed length, we can't really do much.
+ * The ONLY thing we can say is that an osi addr has to have
+ * at LEAST an afi and one more byte and had better fit into
+ * a struct iso_addr.
+ * However, in fact the size of the whole thing is a struct
+ * sockaddr_iso, so probably this is what we should check for.
+ */
+ if( (nam->m_len < 2) || (nam->m_len < siso->siso_len)) {
+ return ENAMETOOLONG;
+ }
+ if (siso->siso_nlen) {
+ /* non-zero net addr- better match one of our interfaces */
+ IFDEBUG(D_ISO)
+ printf("iso_pcbbind: bind to NOT zeroisoaddr\n");
+ ENDDEBUG
+ for (ia = iso_ifaddr; ia; ia = ia->ia_next)
+ if (SAME_ISOADDR(siso, &ia->ia_addr))
+ break;
+ if (ia == 0)
+ return EADDRNOTAVAIL;
+ }
+ if (siso->siso_len <= sizeof (isop->isop_sladdr)) {
+ isop->isop_laddr = &isop->isop_sladdr;
+ } else {
+ if ((nam = m_copy(nam, 0, (int)M_COPYALL)) == 0)
+ return ENOBUFS;
+ isop->isop_laddr = mtod(nam, struct sockaddr_iso *);
+ }
+ bcopy((caddr_t)siso, (caddr_t)isop->isop_laddr, siso->siso_len);
+ if (siso->siso_tlen == 0)
+ goto noname;
+ if ((isop->isop_socket->so_options & SO_REUSEADDR) == 0 &&
+ iso_pcblookup(head, 0, (caddr_t)0, isop->isop_laddr))
+ return EADDRINUSE;
+ if (siso->siso_tlen <= 2) {
+ bcopy(TSEL(siso), suf.data, sizeof(suf.data));
+ suf.s = ntohs(suf.s);
+ if((suf.s < ISO_PORT_RESERVED) &&
+ (isop->isop_socket->so_state && SS_PRIV) == 0)
+ return EACCES;
+ } else {
+ register char *cp;
+noname:
+ cp = TSEL(isop->isop_laddr);
+ IFDEBUG(D_ISO)
+ printf("iso_pcbbind noname\n");
+ ENDDEBUG
+ do {
+ if (head->isop_lport++ < ISO_PORT_RESERVED ||
+ head->isop_lport > ISO_PORT_USERRESERVED)
+ head->isop_lport = ISO_PORT_RESERVED;
+ suf.s = htons(head->isop_lport);
+ cp[0] = suf.data[0];
+ cp[1] = suf.data[1];
+ } while (iso_pcblookup(head, 0, (caddr_t)0, isop->isop_laddr));
+ }
+ IFDEBUG(D_ISO)
+ printf("iso_pcbbind returns 0, suf 0x%x\n", suf);
+ ENDDEBUG
+ return 0;
+}
+/*
+ * FUNCTION: iso_pcbconnect
+ *
+ * PURPOSE: Make the isopcb (isop) look like it's connected.
+ * In other words, give it the peer address given in
+ * the mbuf * (nam). Make sure such a combination
+ * of local, peer addresses doesn't already exist
+ * for this protocol. Internet mentality prevails here,
+ * wherein a src,dst pair uniquely identifies a connection.
+ * Both net address and port must be specified in argument
+ * (nam).
+ * If we don't have a local address for this socket yet,
+ * we pick one by calling iso_pcbbind().
+ *
+ * RETURNS: errno E* or 0 if ok.
+ *
+ * SIDE EFFECTS: Looks up a route, which may cause one to be left
+ * in the isopcb.
+ *
+ * NOTES:
+ */
+int
+iso_pcbconnect(isop, nam)
+ register struct isopcb *isop;
+ struct mbuf *nam;
+{
+ register struct sockaddr_iso *siso = mtod(nam, struct sockaddr_iso *);
+ int local_zero, error = 0;
+ struct iso_ifaddr *ia;
+
+ IFDEBUG(D_ISO)
+ printf("iso_pcbconnect(isop 0x%x sock 0x%x nam 0x%x",
+ isop, isop->isop_socket, nam);
+ printf("nam->m_len 0x%x), addr:\n", nam->m_len);
+ dump_isoaddr(siso);
+ ENDDEBUG
+ if (nam->m_len < siso->siso_len)
+ return EINVAL;
+ if (siso->siso_family != AF_ISO)
+ return EAFNOSUPPORT;
+ if (siso->siso_nlen == 0) {
+ if (ia = iso_ifaddr) {
+ int nlen = ia->ia_addr.siso_nlen;
+ ovbcopy(TSEL(siso), nlen + TSEL(siso),
+ siso->siso_plen + siso->siso_tlen + siso->siso_slen);
+ bcopy((caddr_t)&ia->ia_addr.siso_addr,
+ (caddr_t)&siso->siso_addr, nlen + 1);
+ /* includes siso->siso_nlen = nlen; */
+ } else
+ return EADDRNOTAVAIL;
+ }
+ /*
+ * Local zero means either not bound, or bound to a TSEL, but no
+ * particular local interface. So, if we want to send somebody
+ * we need to choose a return address.
+ */
+ local_zero =
+ ((isop->isop_laddr == 0) || (isop->isop_laddr->siso_nlen == 0));
+ if (local_zero) {
+ int flags;
+
+ IFDEBUG(D_ISO)
+ printf("iso_pcbconnect localzero 1\n");
+ ENDDEBUG
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the i/f, else punt.
+ */
+ flags = isop->isop_socket->so_options & SO_DONTROUTE;
+ if (error = clnp_route(&siso->siso_addr, &isop->isop_route, flags,
+ (struct sockaddr **)0, &ia))
+ return error;
+ IFDEBUG(D_ISO)
+ printf("iso_pcbconnect localzero 2, ro->ro_rt 0x%x",
+ isop->isop_route.ro_rt);
+ printf(" ia 0x%x\n", ia);
+ ENDDEBUG
+ }
+ IFDEBUG(D_ISO)
+ printf("in iso_pcbconnect before lookup isop 0x%x isop->sock 0x%x\n",
+ isop, isop->isop_socket);
+ ENDDEBUG
+ if (local_zero) {
+ int nlen, tlen, totlen; caddr_t oldtsel, newtsel;
+ siso = isop->isop_laddr;
+ if (siso == 0 || siso->siso_tlen == 0)
+ (void)iso_pcbbind(isop, (struct mbuf *)0);
+ /*
+ * Here we have problem of squezeing in a definite network address
+ * into an existing sockaddr_iso, which in fact may not have room
+ * for it. This gets messy.
+ */
+ siso = isop->isop_laddr;
+ oldtsel = TSEL(siso);
+ tlen = siso->siso_tlen;
+ nlen = ia->ia_addr.siso_nlen;
+ totlen = tlen + nlen + _offsetof(struct sockaddr_iso, siso_data[0]);
+ if ((siso == &isop->isop_sladdr) &&
+ (totlen > sizeof(isop->isop_sladdr))) {
+ struct mbuf *m = m_get(MT_SONAME, M_DONTWAIT);
+ if (m == 0)
+ return ENOBUFS;
+ m->m_len = totlen;
+ isop->isop_laddr = siso = mtod(m, struct sockaddr_iso *);
+ }
+ siso->siso_nlen = ia->ia_addr.siso_nlen;
+ newtsel = TSEL(siso);
+ ovbcopy(oldtsel, newtsel, tlen);
+ bcopy(ia->ia_addr.siso_data, siso->siso_data, nlen);
+ siso->siso_tlen = tlen;
+ siso->siso_family = AF_ISO;
+ siso->siso_len = totlen;
+ siso = mtod(nam, struct sockaddr_iso *);
+ }
+ IFDEBUG(D_ISO)
+ printf("in iso_pcbconnect before bcopy isop 0x%x isop->sock 0x%x\n",
+ isop, isop->isop_socket);
+ ENDDEBUG
+ /*
+ * If we had to allocate space to a previous big foreign address,
+ * and for some reason we didn't free it, we reuse it knowing
+ * that is going to be big enough, as sockaddrs are delivered in
+ * 128 byte mbufs.
+ * If the foreign address is small enough, we use default space;
+ * otherwise, we grab an mbuf to copy into.
+ */
+ if (isop->isop_faddr == 0 || isop->isop_faddr == &isop->isop_sfaddr) {
+ if (siso->siso_len <= sizeof(isop->isop_sfaddr))
+ isop->isop_faddr = &isop->isop_sfaddr;
+ else {
+ struct mbuf *m = m_get(MT_SONAME, M_DONTWAIT);
+ if (m == 0)
+ return ENOBUFS;
+ isop->isop_faddr = mtod(m, struct sockaddr_iso *);
+ }
+ }
+ bcopy((caddr_t)siso, (caddr_t)isop->isop_faddr, siso->siso_len);
+ IFDEBUG(D_ISO)
+ printf("in iso_pcbconnect after bcopy isop 0x%x isop->sock 0x%x\n",
+ isop, isop->isop_socket);
+ printf("iso_pcbconnect connected to addr:\n");
+ dump_isoaddr(isop->isop_faddr);
+ printf("iso_pcbconnect end: src addr:\n");
+ dump_isoaddr(isop->isop_laddr);
+ ENDDEBUG
+ return 0;
+}
+
+/*
+ * FUNCTION: iso_pcbdisconnect()
+ *
+ * PURPOSE: washes away the peer address info so the socket
+ * appears to be disconnected.
+ * If there's no file descriptor associated with the socket
+ * it detaches the pcb.
+ *
+ * RETURNS: Nada.
+ *
+ * SIDE EFFECTS: May detach the pcb.
+ *
+ * NOTES:
+ */
+void
+iso_pcbdisconnect(isop)
+ struct isopcb *isop;
+{
+ void iso_pcbdetach();
+ register struct sockaddr_iso *siso;
+
+ IFDEBUG(D_ISO)
+ printf("iso_pcbdisconnect(isop 0x%x)\n", isop);
+ ENDDEBUG
+ /*
+ * Preserver binding infnormation if already bound.
+ */
+ if ((siso = isop->isop_laddr) && siso->siso_nlen && siso->siso_tlen) {
+ caddr_t otsel = TSEL(siso);
+ siso->siso_nlen = 0;
+ ovbcopy(otsel, TSEL(siso), siso->siso_tlen);
+ }
+ if (isop->isop_faddr && isop->isop_faddr != &isop->isop_sfaddr)
+ m_freem(dtom(isop->isop_faddr));
+ isop->isop_faddr = 0;
+ if (isop->isop_socket->so_state & SS_NOFDREF)
+ iso_pcbdetach(isop);
+}
+
+/*
+ * FUNCTION: iso_pcbdetach
+ *
+ * PURPOSE: detach the pcb at *(isop) from it's socket and free
+ * the mbufs associated with the pcb..
+ * Dequeues (isop) from its head.
+ *
+ * RETURNS: Nada.
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+void
+iso_pcbdetach(isop)
+ struct isopcb *isop;
+{
+ struct socket *so = isop->isop_socket;
+
+ IFDEBUG(D_ISO)
+ printf("iso_pcbdetach(isop 0x%x socket 0x%x so 0x%x)\n",
+ isop, isop->isop_socket, so);
+ ENDDEBUG
+#ifdef TPCONS
+ if (isop->isop_chan) {
+ register struct pklcd *lcp = (struct pklcd *)isop->isop_chan;
+ if (--isop->isop_refcnt > 0)
+ return;
+ if (lcp && lcp->lcd_state == DATA_TRANSFER) {
+ lcp->lcd_upper = 0;
+ lcp->lcd_upnext = 0;
+ pk_disconnect(lcp);
+ }
+ isop->isop_chan = 0;
+ }
+#endif
+ if (so) { /* in the x.25 domain, we sometimes have no socket */
+ so->so_pcb = 0;
+ sofree(so);
+ }
+ IFDEBUG(D_ISO)
+ printf("iso_pcbdetach 2 \n");
+ ENDDEBUG
+ if (isop->isop_options)
+ (void)m_free(isop->isop_options);
+ IFDEBUG(D_ISO)
+ printf("iso_pcbdetach 3 \n");
+ ENDDEBUG
+ if (isop->isop_route.ro_rt)
+ rtfree(isop->isop_route.ro_rt);
+ IFDEBUG(D_ISO)
+ printf("iso_pcbdetach 3.1\n");
+ ENDDEBUG
+ if (isop->isop_clnpcache != NULL) {
+ struct clnp_cache *clcp =
+ mtod(isop->isop_clnpcache, struct clnp_cache *);
+ IFDEBUG(D_ISO)
+ printf("iso_pcbdetach 3.2: clcp 0x%x freeing clc_hdr x%x\n",
+ clcp, clcp->clc_hdr);
+ ENDDEBUG
+ if (clcp->clc_hdr != NULL)
+ m_free(clcp->clc_hdr);
+ IFDEBUG(D_ISO)
+ printf("iso_pcbdetach 3.3: freeing cache x%x\n",
+ isop->isop_clnpcache);
+ ENDDEBUG
+ m_free(isop->isop_clnpcache);
+ }
+ IFDEBUG(D_ISO)
+ printf("iso_pcbdetach 4 \n");
+ ENDDEBUG
+ remque(isop);
+ IFDEBUG(D_ISO)
+ printf("iso_pcbdetach 5 \n");
+ ENDDEBUG
+ if (isop->isop_laddr && (isop->isop_laddr != &isop->isop_sladdr))
+ m_freem(dtom(isop->isop_laddr));
+ free((caddr_t)isop, M_PCB);
+}
+
+
+/*
+ * FUNCTION: iso_pcbnotify
+ *
+ * PURPOSE: notify all connections in this protocol's queue (head)
+ * that have peer address (dst) of the problem (errno)
+ * by calling (notify) on the connections' isopcbs.
+ *
+ * RETURNS: Rien.
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: (notify) is called at splimp!
+ */
+void
+iso_pcbnotify(head, siso, errno, notify)
+ struct isopcb *head;
+ register struct sockaddr_iso *siso;
+ int errno, (*notify)();
+{
+ register struct isopcb *isop;
+ int s = splimp();
+
+ IFDEBUG(D_ISO)
+ printf("iso_pcbnotify(head 0x%x, notify 0x%x) dst:\n", head, notify);
+ ENDDEBUG
+ for (isop = head->isop_next; isop != head; isop = isop->isop_next) {
+ if (isop->isop_socket == 0 || isop->isop_faddr == 0 ||
+ !SAME_ISOADDR(siso, isop->isop_faddr)) {
+ IFDEBUG(D_ISO)
+ printf("iso_pcbnotify: CONTINUE isop 0x%x, sock 0x%x\n" ,
+ isop, isop->isop_socket);
+ printf("addrmatch cmp'd with (0x%x):\n", isop->isop_faddr);
+ dump_isoaddr(isop->isop_faddr);
+ ENDDEBUG
+ continue;
+ }
+ if (errno)
+ isop->isop_socket->so_error = errno;
+ if (notify)
+ (*notify)(isop);
+ }
+ splx(s);
+ IFDEBUG(D_ISO)
+ printf("END OF iso_pcbnotify\n" );
+ ENDDEBUG
+}
+
+
+/*
+ * FUNCTION: iso_pcblookup
+ *
+ * PURPOSE: looks for a given combination of (faddr), (fport),
+ * (lport), (laddr) in the queue named by (head).
+ * Argument (flags) is ignored.
+ *
+ * RETURNS: ptr to the isopcb if it finds a connection matching
+ * these arguments, o.w. returns zero.
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+struct isopcb *
+iso_pcblookup(head, fportlen, fport, laddr)
+ struct isopcb *head;
+ register struct sockaddr_iso *laddr;
+ caddr_t fport;
+ int fportlen;
+{
+ register struct isopcb *isop;
+ register caddr_t lp = TSEL(laddr);
+ unsigned int llen = laddr->siso_tlen;
+
+ IFDEBUG(D_ISO)
+ printf("iso_pcblookup(head 0x%x laddr 0x%x fport 0x%x)\n",
+ head, laddr, fport);
+ ENDDEBUG
+ for (isop = head->isop_next; isop != head; isop = isop->isop_next) {
+ if (isop->isop_laddr == 0 || isop->isop_laddr == laddr)
+ continue;
+ if (isop->isop_laddr->siso_tlen != llen)
+ continue;
+ if (bcmp(lp, TSEL(isop->isop_laddr), llen))
+ continue;
+ if (fportlen && isop->isop_faddr &&
+ bcmp(fport, TSEL(isop->isop_faddr), (unsigned)fportlen))
+ continue;
+ /* PHASE2
+ * addrmatch1 should be iso_addrmatch(a, b, mask)
+ * where mask is taken from isop->isop_laddrmask (new field)
+ * isop_lnetmask will also be available in isop
+ if (laddr != &zeroiso_addr &&
+ !iso_addrmatch1(laddr, &(isop->isop_laddr.siso_addr)))
+ continue;
+ */
+ if (laddr->siso_nlen && (!SAME_ISOADDR(laddr, isop->isop_laddr)))
+ continue;
+ return (isop);
+ }
+ return (struct isopcb *)0;
+}
+#endif /* ISO */
diff --git a/sys/netiso/iso_pcb.h b/sys/netiso/iso_pcb.h
new file mode 100644
index 0000000..aad76bc
--- /dev/null
+++ b/sys/netiso/iso_pcb.h
@@ -0,0 +1,113 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)iso_pcb.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: iso_pcb.h,v 4.3 88/06/29 15:00:01 hagens Exp $ */
+/* $Source: /usr/argo/sys/netiso/RCS/iso_pcb.h,v $ */
+
+#define MAXX25CRUDLEN 16 /* 16 bytes of call request user data */
+
+/*
+ * Common structure pcb for argo protocol implementation.
+ */
+struct isopcb {
+ struct isopcb *isop_next,*isop_prev; /* pointers to other pcb's */
+ struct isopcb *isop_head; /* pointer back to chain of pcbs for
+ this protocol */
+ struct socket *isop_socket; /* back pointer to socket */
+ struct sockaddr_iso *isop_laddr;
+ struct sockaddr_iso *isop_faddr;
+ struct route_iso {
+ struct rtentry *ro_rt;
+ struct sockaddr_iso ro_dst;
+ } isop_route; /* CLNP routing entry */
+ struct mbuf *isop_options; /* CLNP options */
+ struct mbuf *isop_optindex; /* CLNP options index */
+ struct mbuf *isop_clnpcache; /* CLNP cached hdr */
+ caddr_t isop_chan; /* actually struct pklcb * */
+ u_short isop_refcnt; /* mult TP4 tpcb's -> here */
+ u_short isop_lport; /* MISLEADLING work var */
+ u_short isop_tuba_cached; /* for tuba address ref cnts */
+ int isop_x25crud_len; /* x25 call request ud */
+ char isop_x25crud[MAXX25CRUDLEN];
+ struct ifaddr *isop_ifa; /* ESIS interface assoc w/sock */
+ struct sockaddr_iso isop_sladdr, /* preallocated laddr */
+ isop_sfaddr; /* preallocated faddr */
+};
+
+#ifdef sotorawcb
+/*
+ * Common structure pcb for raw clnp protocol access.
+ * Here are clnp specific extensions to the raw control block,
+ * and space is allocated to the necessary sockaddrs.
+ */
+struct rawisopcb {
+ struct rawcb risop_rcb; /* common control block prefix */
+ int risop_flags; /* flags, e.g. raw sockopts */
+ struct isopcb risop_isop; /* space for bound addresses, routes etc.*/
+};
+#endif
+
+#define sotoisopcb(so) ((struct isopcb *)(so)->so_pcb)
+#define sotorawisopcb(so) ((struct rawisopcb *)(so)->so_pcb)
+
+#ifdef KERNEL
+struct isopcb *iso_pcblookup();
+#endif
diff --git a/sys/netiso/iso_proto.c b/sys/netiso/iso_proto.c
new file mode 100644
index 0000000..59575c7
--- /dev/null
+++ b/sys/netiso/iso_proto.c
@@ -0,0 +1,197 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)iso_proto.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: iso_proto.c,v 4.4 88/09/08 08:38:42 hagens Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/iso_proto.c,v $
+ *
+ * iso_proto.c : protocol switch tables in the ISO domain
+ *
+ * ISO protocol family includes TP, CLTP, CLNP, 8208
+ * TP and CLNP are implemented here.
+ */
+
+#ifdef ISO
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/mbuf.h>
+
+#include <net/radix.h>
+
+#include <netiso/iso.h>
+
+int clnp_output(), clnp_init(),clnp_slowtimo(),clnp_drain();
+int rclnp_input(), rclnp_output(), rclnp_ctloutput(), raw_usrreq();
+int clnp_usrreq();
+
+int tp_ctloutput(), tpclnp_ctlinput(), tpclnp_input(), tp_usrreq();
+int tp_init(), tp_fasttimo(), tp_slowtimo(), tp_drain();
+int cons_init(), tpcons_input();
+
+int isis_input();
+int esis_input(), esis_ctlinput(), esis_init(), esis_usrreq();
+int idrp_input(), idrp_init(), idrp_usrreq();
+int cltp_input(), cltp_ctlinput(), cltp_init(), cltp_usrreq(), cltp_output();
+
+#ifdef TUBA
+int tuba_usrreq(), tuba_ctloutput(), tuba_init(), tuba_tcpinput();
+int tuba_slowtimo(), tuba_fasttimo();
+#endif
+
+struct protosw isosw[] = {
+/*
+ * We need a datagram entry through which net mgmt programs can get
+ * to the iso_control procedure (iso ioctls). Thus, a minimal
+ * SOCK_DGRAM interface is provided here.
+ * THIS ONE MUST BE FIRST: Kludge city : socket() says if(!proto) call
+ * pffindtype, which gets the first entry that matches the type.
+ * sigh.
+ */
+{ SOCK_DGRAM, &isodomain, ISOPROTO_CLTP, PR_ATOMIC|PR_ADDR,
+ 0, cltp_output, 0, 0,
+ cltp_usrreq,
+ cltp_init, 0, 0, 0
+},
+
+/*
+ * A datagram interface for clnp cannot co-exist with TP/CLNP
+ * because CLNP has no way to discriminate incoming TP packets from
+ * packets coming in for any other higher layer protocol.
+ * Old way: set it up so that pffindproto(... dgm, clnp) fails.
+ * New way: let pffindproto work (for x.25, thank you) but create
+ * a clnp_usrreq() that returns error on PRU_ATTACH.
+ */
+{SOCK_DGRAM, &isodomain, ISOPROTO_CLNP, 0,
+ 0, clnp_output, 0, 0,
+ clnp_usrreq,
+ clnp_init, 0, clnp_slowtimo, clnp_drain,
+},
+
+/* raw clnp */
+{ SOCK_RAW, &isodomain, ISOPROTO_RAW, PR_ATOMIC|PR_ADDR,
+ rclnp_input, rclnp_output, 0, rclnp_ctloutput,
+ clnp_usrreq,
+ 0, 0, 0, 0
+},
+
+/* ES-IS protocol */
+{ SOCK_DGRAM, &isodomain, ISOPROTO_ESIS, PR_ATOMIC|PR_ADDR,
+ esis_input, 0, esis_ctlinput, 0,
+ esis_usrreq,
+ esis_init, 0, 0, 0
+},
+
+/* ISOPROTO_INTRAISIS */
+{ SOCK_DGRAM, &isodomain, ISOPROTO_INTRAISIS, PR_ATOMIC|PR_ADDR,
+ isis_input, 0, 0, 0,
+ esis_usrreq,
+ 0, 0, 0, 0
+},
+
+/* ISOPROTO_IDRP */
+{ SOCK_DGRAM, &isodomain, ISOPROTO_IDRP, PR_ATOMIC|PR_ADDR,
+ idrp_input, 0, 0, 0,
+ idrp_usrreq,
+ idrp_init, 0, 0, 0
+},
+
+/* ISOPROTO_TP */
+{ SOCK_SEQPACKET, &isodomain, ISOPROTO_TP, PR_CONNREQUIRED|PR_WANTRCVD,
+ tpclnp_input, 0, tpclnp_ctlinput, tp_ctloutput,
+ tp_usrreq,
+ tp_init, tp_fasttimo, tp_slowtimo, tp_drain,
+},
+
+#ifdef TUBA
+{ SOCK_STREAM, &isodomain, ISOPROTO_TCP, PR_CONNREQUIRED|PR_WANTRCVD,
+ tuba_tcpinput, 0, 0, tuba_ctloutput,
+ tuba_usrreq,
+ tuba_init, tuba_fasttimo, tuba_fasttimo, 0
+},
+#endif
+
+#ifdef TPCONS
+/* ISOPROTO_TP */
+{ SOCK_SEQPACKET, &isodomain, ISOPROTO_TP0, PR_CONNREQUIRED|PR_WANTRCVD,
+ tpcons_input, 0, 0, tp_ctloutput,
+ tp_usrreq,
+ cons_init, 0, 0, 0,
+},
+#endif
+
+};
+
+
+struct domain isodomain = {
+ AF_ISO, /* family */
+ "iso-domain", /* name */
+ 0, /* initialize routine */
+ 0, /* externalize access rights */
+ 0, /* dispose of internalized rights */
+ isosw, /* protosw */
+ &isosw[sizeof(isosw)/sizeof(isosw[0])], /* NPROTOSW */
+ 0, /* next */
+ rn_inithead, /* rtattach */
+ 48, /* rtoffset */
+ sizeof(struct sockaddr_iso) /* maxkeylen */
+};
+#endif /* ISO */
diff --git a/sys/netiso/iso_snpac.c b/sys/netiso/iso_snpac.c
new file mode 100644
index 0000000..2473ae7
--- /dev/null
+++ b/sys/netiso/iso_snpac.c
@@ -0,0 +1,736 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)iso_snpac.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: iso_snpac.c,v 1.8 88/09/19 13:51:36 hagens Exp $ */
+/* $Source: /usr/argo/sys/netiso/RCS/iso_snpac.c,v $ */
+
+#ifdef ISO
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netiso/iso.h>
+#include <netiso/iso_var.h>
+#include <netiso/iso_snpac.h>
+#include <netiso/clnp.h>
+#include <netiso/clnp_stat.h>
+#include <netiso/esis.h>
+#include <netiso/argo_debug.h>
+
+int iso_systype = SNPA_ES; /* default to be an ES */
+extern short esis_holding_time, esis_config_time, esis_esconfig_time;
+extern struct timeval time;
+extern void esis_config();
+extern int hz;
+static void snpac_fixdstandmask();
+
+struct sockaddr_iso blank_siso = {sizeof(blank_siso), AF_ISO};
+extern u_long iso_hashchar();
+static struct sockaddr_iso
+ dst = {sizeof(dst), AF_ISO},
+ gte = {sizeof(dst), AF_ISO},
+ src = {sizeof(dst), AF_ISO},
+ msk = {sizeof(dst), AF_ISO},
+ zmk = {0};
+#define zsi blank_siso
+#define zero_isoa zsi.siso_addr
+#define zap_isoaddr(a, b) {Bzero(&a.siso_addr, sizeof(*r)); r = b; \
+ Bcopy(r, &a.siso_addr, 1 + (r)->isoa_len);}
+#define S(x) ((struct sockaddr *)&(x))
+
+static struct sockaddr_dl blank_dl = {sizeof(blank_dl), AF_LINK};
+static struct sockaddr_dl gte_dl;
+#define zap_linkaddr(a, b, c, i) \
+ (*a = blank_dl, bcopy(b, a->sdl_data, a->sdl_alen = c), a->sdl_index = i)
+
+/*
+ * We only keep track of a single IS at a time.
+ */
+struct rtentry *known_is;
+
+/*
+ * Addresses taken from NBS agreements, December 1987.
+ *
+ * These addresses assume on-the-wire transmission of least significant
+ * bit first. This is the method used by 802.3. When these
+ * addresses are passed to the token ring driver, (802.5), they
+ * must be bit-swaped because 802.5 transmission order is MSb first.
+ *
+ * Furthermore, according to IBM Austin, these addresses are not
+ * true token ring multicast addresses. More work is necessary
+ * to get multicast to work right on token ring.
+ *
+ * Currently, the token ring driver does not handle multicast, so
+ * these addresses are converted into the broadcast address in
+ * lan_output() That means that if these multicast addresses change
+ * the token ring driver must be altered.
+ */
+char all_es_snpa[] = { 0x09, 0x00, 0x2b, 0x00, 0x00, 0x04 };
+char all_is_snpa[] = { 0x09, 0x00, 0x2b, 0x00, 0x00, 0x05 };
+char all_l1is_snpa[] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x14};
+char all_l2is_snpa[] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x15};
+
+union sockunion {
+ struct sockaddr_iso siso;
+ struct sockaddr_dl sdl;
+ struct sockaddr sa;
+};
+
+/*
+ * FUNCTION: llc_rtrequest
+ *
+ * PURPOSE: Manage routing table entries specific to LLC for ISO.
+ *
+ * NOTES: This does a lot of obscure magic;
+ */
+llc_rtrequest(req, rt, sa)
+int req;
+register struct rtentry *rt;
+struct sockaddr *sa;
+{
+ register union sockunion *gate = (union sockunion *)rt->rt_gateway;
+ register struct llinfo_llc *lc = (struct llinfo_llc *)rt->rt_llinfo, *lc2;
+ struct rtentry *rt2;
+ struct ifnet *ifp = rt->rt_ifp;
+ int addrlen = ifp->if_addrlen;
+#define LLC_SIZE 3 /* XXXXXX do this right later */
+
+ IFDEBUG (D_SNPA)
+ printf("llc_rtrequest(%d, %x, %x)\n", req, rt, sa);
+ ENDDEBUG
+ if (rt->rt_flags & RTF_GATEWAY)
+ return;
+ else switch (req) {
+ case RTM_ADD:
+ /*
+ * Case 1: This route may come from a route to iface with mask
+ * or from a default route.
+ */
+ if (rt->rt_flags & RTF_CLONING) {
+ iso_setmcasts(ifp, req);
+ rt_setgate(rt, rt_key(rt), &blank_dl);
+ return;
+ }
+ if (lc != 0)
+ return; /* happens on a route change */
+ /* FALLTHROUGH */
+ case RTM_RESOLVE:
+ /*
+ * Case 2: This route may come from cloning, or a manual route
+ * add with a LL address.
+ */
+ if (gate->sdl.sdl_family != AF_LINK) {
+ log(LOG_DEBUG, "llc_rtrequest: got non-link non-gateway route\n");
+ break;
+ }
+ R_Malloc(lc, struct llinfo_llc *, sizeof (*lc));
+ rt->rt_llinfo = (caddr_t)lc;
+ if (lc == 0) {
+ log(LOG_DEBUG, "llc_rtrequest: malloc failed\n");
+ break;
+ }
+ Bzero(lc, sizeof(*lc));
+ lc->lc_rt = rt;
+ rt->rt_flags |= RTF_LLINFO;
+ insque(lc, &llinfo_llc);
+ if (gate->sdl.sdl_alen == sizeof(struct esis_req) + addrlen) {
+ gate->sdl.sdl_alen -= sizeof(struct esis_req);
+ bcopy(addrlen + LLADDR(&gate->sdl),
+ (caddr_t)&lc->lc_er, sizeof(lc->lc_er));
+ } else if (gate->sdl.sdl_alen == addrlen)
+ lc->lc_flags = (SNPA_ES | SNPA_VALID | SNPA_PERM);
+ break;
+ case RTM_DELETE:
+ if (rt->rt_flags & RTF_CLONING)
+ iso_setmcasts(ifp, req);
+ if (lc == 0)
+ return;
+ remque(lc);
+ Free(lc);
+ rt->rt_llinfo = 0;
+ rt->rt_flags &= ~RTF_LLINFO;
+ break;
+ }
+ if (rt->rt_rmx.rmx_mtu == 0) {
+ rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu - LLC_SIZE;
+ }
+}
+/*
+ * FUNCTION: iso_setmcasts
+ *
+ * PURPOSE: Enable/Disable ESIS/ISIS multicast reception on interfaces.
+ *
+ * NOTES: This also does a lot of obscure magic;
+ */
+iso_setmcasts(ifp, req)
+ struct ifnet *ifp;
+ int req;
+{
+ static char *addrlist[] =
+ { all_es_snpa, all_is_snpa, all_l1is_snpa, all_l2is_snpa, 0};
+ struct ifreq ifr;
+ register caddr_t *cpp;
+ int doreset = 0;
+
+ bzero((caddr_t)&ifr, sizeof(ifr));
+ for (cpp = (caddr_t *)addrlist; *cpp; cpp++) {
+ bcopy(*cpp, (caddr_t)ifr.ifr_addr.sa_data, 6);
+ if (req == RTM_ADD)
+ if (ether_addmulti(&ifr, (struct arpcom *)ifp) == ENETRESET)
+ doreset++;
+ else
+ if (ether_delmulti(&ifr, (struct arpcom *)ifp) == ENETRESET)
+ doreset++;
+ }
+ if (doreset) {
+ if (ifp->if_reset)
+ (*ifp->if_reset)(ifp->if_unit);
+ else
+ printf("iso_setmcasts: %s%d needs reseting to receive iso mcasts\n",
+ ifp->if_name, ifp->if_unit);
+ }
+}
+/*
+ * FUNCTION: iso_snparesolve
+ *
+ * PURPOSE: Resolve an iso address into snpa address
+ *
+ * RETURNS: 0 if addr is resolved
+ * errno if addr is unknown
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: Now that we have folded the snpa cache into the routing
+ * table, we know there is no snpa address known for this
+ * destination. If we know of a default IS, then the address
+ * of the IS is returned. If no IS is known, then return the
+ * multi-cast address for "all ES" for this interface.
+ *
+ * NB: the last case described above constitutes the
+ * query configuration function 9542, sec 6.5
+ * A mechanism is needed to prevent this function from
+ * being invoked if the system is an IS.
+ */
+iso_snparesolve(ifp, dest, snpa, snpa_len)
+struct ifnet *ifp; /* outgoing interface */
+struct sockaddr_iso *dest; /* destination */
+caddr_t snpa; /* RESULT: snpa to be used */
+int *snpa_len; /* RESULT: length of snpa */
+{
+ struct llinfo_llc *sc; /* ptr to snpa table entry */
+ caddr_t found_snpa;
+ int addrlen;
+
+ /*
+ * This hack allows us to send esis packets that have the destination snpa
+ * addresss embedded in the destination nsap address
+ */
+ if (dest->siso_data[0] == AFI_SNA) {
+ /*
+ * This is a subnetwork address. Return it immediately
+ */
+ IFDEBUG(D_SNPA)
+ printf("iso_snparesolve: return SN address\n");
+ ENDDEBUG
+ addrlen = dest->siso_nlen - 1; /* subtract size of AFI */
+ found_snpa = (caddr_t) dest->siso_data + 1;
+ /*
+ * If we are an IS, we can't do much with the packet;
+ * Check if we know about an IS.
+ */
+ } else if (iso_systype != SNPA_IS && known_is != 0 &&
+ (sc = (struct llinfo_llc *)known_is->rt_llinfo) &&
+ (sc->lc_flags & SNPA_VALID)) {
+ register struct sockaddr_dl *sdl =
+ (struct sockaddr_dl *)(known_is->rt_gateway);
+ found_snpa = LLADDR(sdl);
+ addrlen = sdl->sdl_alen;
+ } else if (ifp->if_flags & IFF_BROADCAST) {
+ /*
+ * no IS, no match. Return "all es" multicast address for this
+ * interface, as per Query Configuration Function (9542 sec 6.5)
+ *
+ * Note: there is a potential problem here. If the destination
+ * is on the subnet and it does not respond with a ESH, but
+ * does send back a TP CC, a connection could be established
+ * where we always transmit the CLNP packet to "all es"
+ */
+ addrlen = ifp->if_addrlen;
+ found_snpa = (caddr_t)all_es_snpa;
+ } else
+ return (ENETUNREACH);
+ bcopy(found_snpa, snpa, *snpa_len = addrlen);
+ return (0);
+}
+
+
+/*
+ * FUNCTION: snpac_free
+ *
+ * PURPOSE: free an entry in the iso address map table
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: If there is a route entry associated with cache
+ * entry, then delete that as well
+ */
+snpac_free(lc)
+register struct llinfo_llc *lc; /* entry to free */
+{
+ register struct rtentry *rt = lc->lc_rt;
+ register struct iso_addr *r;
+
+ if (known_is == rt)
+ known_is = 0;
+ if (rt && (rt->rt_flags & RTF_UP) &&
+ (rt->rt_flags & (RTF_DYNAMIC | RTF_MODIFIED))) {
+ RTFREE(rt);
+ rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, rt_mask(rt),
+ rt->rt_flags, (struct rtentry **)0);
+ RTFREE(rt);
+ }
+}
+
+/*
+ * FUNCTION: snpac_add
+ *
+ * PURPOSE: Add an entry to the snpa cache
+ *
+ * RETURNS:
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: If entry already exists, then update holding time.
+ */
+snpac_add(ifp, nsap, snpa, type, ht, nsellength)
+struct ifnet *ifp; /* interface info is related to */
+struct iso_addr *nsap; /* nsap to add */
+caddr_t snpa; /* translation */
+char type; /* SNPA_IS or SNPA_ES */
+u_short ht; /* holding time (in seconds) */
+int nsellength; /* nsaps may differ only in trailing bytes */
+{
+ register struct llinfo_llc *lc;
+ register struct rtentry *rt;
+ struct rtentry *mrt = 0;
+ register struct iso_addr *r; /* for zap_isoaddr macro */
+ int snpalen = min(ifp->if_addrlen, MAX_SNPALEN);
+ int new_entry = 0, index = ifp->if_index, iftype = ifp->if_type;
+
+ IFDEBUG(D_SNPA)
+ printf("snpac_add(%x, %x, %x, %x, %x, %x)\n",
+ ifp, nsap, snpa, type, ht, nsellength);
+ ENDDEBUG
+ zap_isoaddr(dst, nsap);
+ rt = rtalloc1(S(dst), 0);
+ IFDEBUG(D_SNPA)
+ printf("snpac_add: rtalloc1 returns %x\n", rt);
+ ENDDEBUG
+ if (rt == 0) {
+ struct sockaddr *netmask;
+ int flags;
+ add:
+ if (nsellength) {
+ netmask = S(msk); flags = RTF_UP;
+ snpac_fixdstandmask(nsellength);
+ } else {
+ netmask = 0; flags = RTF_UP | RTF_HOST;
+ }
+ new_entry = 1;
+ zap_linkaddr((&gte_dl), snpa, snpalen, index);
+ gte_dl.sdl_type = iftype;
+ if (rtrequest(RTM_ADD, S(dst), S(gte_dl), netmask, flags, &mrt) ||
+ mrt == 0)
+ return (0);
+ rt = mrt;
+ rt->rt_refcnt--;
+ } else {
+ register struct sockaddr_dl *sdl = (struct sockaddr_dl *)rt->rt_gateway;
+ rt->rt_refcnt--;
+ if ((rt->rt_flags & RTF_LLINFO) == 0)
+ goto add;
+ if (nsellength && (rt->rt_flags & RTF_HOST)) {
+ if (rt->rt_refcnt == 0) {
+ rtrequest(RTM_DELETE, S(dst), (struct sockaddr *)0,
+ (struct sockaddr *)0, 0, (struct rtentry *)0);
+ rt = 0;
+ goto add;
+ } else {
+ static struct iso_addr nsap2; register char *cp;
+ nsap2 = *nsap;
+ cp = nsap2.isoa_genaddr + nsap->isoa_len - nsellength;
+ while (cp < (char *)(1 + &nsap2))
+ *cp++ = 0;
+ (void) snpac_add(ifp, &nsap2, snpa, type, ht, nsellength);
+ }
+ }
+ if (sdl->sdl_family != AF_LINK || sdl->sdl_alen == 0) {
+ int old_sdl_len = sdl->sdl_len;
+ if (old_sdl_len < sizeof(*sdl)) {
+ log(LOG_DEBUG, "snpac_add: cant make room for lladdr\n");
+ return (0);
+ }
+ zap_linkaddr(sdl, snpa, snpalen, index);
+ sdl->sdl_len = old_sdl_len;
+ sdl->sdl_type = iftype;
+ new_entry = 1;
+ }
+ }
+ if ((lc = (struct llinfo_llc *)rt->rt_llinfo) == 0)
+ panic("snpac_rtrequest");
+ rt->rt_rmx.rmx_expire = ht + time.tv_sec;
+ lc->lc_flags = SNPA_VALID | type;
+ if ((type & SNPA_IS) && !(iso_systype & SNPA_IS))
+ snpac_logdefis(rt);
+ return (new_entry);
+}
+
+static void
+snpac_fixdstandmask(nsellength)
+{
+ register char *cp = msk.siso_data, *cplim;
+
+ cplim = cp + (dst.siso_nlen -= nsellength);
+ msk.siso_len = cplim - (char *)&msk;
+ msk.siso_nlen = 0;
+ while (cp < cplim)
+ *cp++ = -1;
+ while (cp < (char *)msk.siso_pad)
+ *cp++ = 0;
+ for (cp = dst.siso_data + dst.siso_nlen; cp < (char *)dst.siso_pad; )
+ *cp++ = 0;
+}
+
+/*
+ * FUNCTION: snpac_ioctl
+ *
+ * PURPOSE: Set/Get the system type and esis parameters
+ *
+ * RETURNS: 0 on success, or unix error code
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+snpac_ioctl (so, cmd, data)
+struct socket *so;
+int cmd; /* ioctl to process */
+caddr_t data; /* data for the cmd */
+{
+ register struct systype_req *rq = (struct systype_req *)data;
+
+ IFDEBUG(D_IOCTL)
+ if (cmd == SIOCSSTYPE)
+ printf("snpac_ioctl: cmd set, type x%x, ht %d, ct %d\n",
+ rq->sr_type, rq->sr_holdt, rq->sr_configt);
+ else
+ printf("snpac_ioctl: cmd get\n");
+ ENDDEBUG
+
+ if (cmd == SIOCSSTYPE) {
+ if ((so->so_state & SS_PRIV) == 0)
+ return (EPERM);
+ if ((rq->sr_type & (SNPA_ES|SNPA_IS)) == (SNPA_ES|SNPA_IS))
+ return(EINVAL);
+ if (rq->sr_type & SNPA_ES) {
+ iso_systype = SNPA_ES;
+ } else if (rq->sr_type & SNPA_IS) {
+ iso_systype = SNPA_IS;
+ } else {
+ return(EINVAL);
+ }
+ esis_holding_time = rq->sr_holdt;
+ esis_config_time = rq->sr_configt;
+ if (esis_esconfig_time != rq->sr_esconfigt) {
+ untimeout(esis_config, (caddr_t)0);
+ esis_esconfig_time = rq->sr_esconfigt;
+ esis_config();
+ }
+ } else if (cmd == SIOCGSTYPE) {
+ rq->sr_type = iso_systype;
+ rq->sr_holdt = esis_holding_time;
+ rq->sr_configt = esis_config_time;
+ rq->sr_esconfigt = esis_esconfig_time;
+ } else {
+ return (EINVAL);
+ }
+ return (0);
+}
+
+/*
+ * FUNCTION: snpac_logdefis
+ *
+ * PURPOSE: Mark the IS passed as the default IS
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+snpac_logdefis(sc)
+register struct rtentry *sc;
+{
+ register struct iso_addr *r;
+ register struct sockaddr_dl *sdl = (struct sockaddr_dl *)sc->rt_gateway;
+ register struct rtentry *rt;
+
+ if (known_is == sc || !(sc->rt_flags & RTF_HOST))
+ return;
+ if (known_is) {
+ RTFREE(known_is);
+ }
+ known_is = sc;
+ sc->rt_refcnt++;
+ rt = rtalloc1((struct sockaddr *)&zsi, 0);
+ if (rt == 0)
+ rtrequest(RTM_ADD, S(zsi), rt_key(sc), S(zmk),
+ RTF_DYNAMIC|RTF_GATEWAY, 0);
+ else {
+ if ((rt->rt_flags & RTF_DYNAMIC) &&
+ (rt->rt_flags & RTF_GATEWAY) && rt_mask(rt)->sa_len == 0)
+ rt_setgate(rt, rt_key(rt), rt_key(sc));
+ }
+}
+
+/*
+ * FUNCTION: snpac_age
+ *
+ * PURPOSE: Time out snpac entries
+ *
+ * RETURNS:
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: When encountering an entry for the first time, snpac_age
+ * may delete up to SNPAC_AGE too many seconds. Ie.
+ * if the entry is added a moment before snpac_age is
+ * called, the entry will immediately have SNPAC_AGE
+ * seconds taken off the holding time, even though
+ * it has only been held a brief moment.
+ *
+ * The proper way to do this is set an expiry timeval
+ * equal to current time + holding time. Then snpac_age
+ * would time out entries where expiry date is older
+ * than the current time.
+ */
+void
+snpac_age()
+{
+ register struct llinfo_llc *lc, *nlc;
+ register struct rtentry *rt;
+
+ timeout(snpac_age, (caddr_t)0, SNPAC_AGE * hz);
+
+ for (lc = llinfo_llc.lc_next; lc != & llinfo_llc; lc = nlc) {
+ nlc = lc->lc_next;
+ if (lc->lc_flags & SNPA_VALID) {
+ rt = lc->lc_rt;
+ if (rt->rt_rmx.rmx_expire && rt->rt_rmx.rmx_expire < time.tv_sec)
+ snpac_free(lc);
+ }
+ }
+}
+
+/*
+ * FUNCTION: snpac_ownmulti
+ *
+ * PURPOSE: Determine if the snpa address is a multicast address
+ * of the same type as the system.
+ *
+ * RETURNS: true or false
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: Used by interface drivers when not in eavesdrop mode
+ * as interm kludge until
+ * real multicast addresses can be configured
+ */
+snpac_ownmulti(snpa, len)
+caddr_t snpa;
+u_int len;
+{
+ return (((iso_systype & SNPA_ES) &&
+ (!bcmp(snpa, (caddr_t)all_es_snpa, len))) ||
+ ((iso_systype & SNPA_IS) &&
+ (!bcmp(snpa, (caddr_t)all_is_snpa, len))));
+}
+
+/*
+ * FUNCTION: snpac_flushifp
+ *
+ * PURPOSE: Flush entries associated with specific ifp
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+snpac_flushifp(ifp)
+struct ifnet *ifp;
+{
+ register struct llinfo_llc *lc;
+
+ for (lc = llinfo_llc.lc_next; lc != & llinfo_llc; lc = lc->lc_next) {
+ if (lc->lc_rt->rt_ifp == ifp && (lc->lc_flags & SNPA_VALID))
+ snpac_free(lc);
+ }
+}
+
+/*
+ * FUNCTION: snpac_rtrequest
+ *
+ * PURPOSE: Make a routing request
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: In the future, this should make a request of a user
+ * level routing daemon.
+ */
+snpac_rtrequest(req, host, gateway, netmask, flags, ret_nrt)
+int req;
+struct iso_addr *host;
+struct iso_addr *gateway;
+struct iso_addr *netmask;
+short flags;
+struct rtentry **ret_nrt;
+{
+ register struct iso_addr *r;
+
+ IFDEBUG(D_SNPA)
+ printf("snpac_rtrequest: ");
+ if (req == RTM_ADD)
+ printf("add");
+ else if (req == RTM_DELETE)
+ printf("delete");
+ else
+ printf("unknown command");
+ printf(" dst: %s\n", clnp_iso_addrp(host));
+ printf("\tgateway: %s\n", clnp_iso_addrp(gateway));
+ ENDDEBUG
+
+
+ zap_isoaddr(dst, host);
+ zap_isoaddr(gte, gateway);
+ if (netmask) {
+ zap_isoaddr(msk, netmask);
+ msk.siso_nlen = 0;
+ msk.siso_len = msk.siso_pad - (u_char *)&msk;
+ }
+
+ rtrequest(req, S(dst), S(gte), (netmask ? S(msk) : (struct sockaddr *)0),
+ flags, ret_nrt);
+}
+
+/*
+ * FUNCTION: snpac_addrt
+ *
+ * PURPOSE: Associate a routing entry with an snpac entry
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: If a cache entry exists for gateway, then
+ * make a routing entry (host, gateway) and associate
+ * with gateway.
+ *
+ * If a route already exists and is different, first delete
+ * it.
+ *
+ * This could be made more efficient by checking
+ * the existing route before adding a new one.
+ */
+snpac_addrt(ifp, host, gateway, netmask)
+struct ifnet *ifp;
+struct iso_addr *host, *gateway, *netmask;
+{
+ register struct iso_addr *r;
+
+ zap_isoaddr(dst, host);
+ zap_isoaddr(gte, gateway);
+ if (netmask) {
+ zap_isoaddr(msk, netmask);
+ msk.siso_nlen = 0;
+ msk.siso_len = msk.siso_pad - (u_char *)&msk;
+ rtredirect(S(dst), S(gte), S(msk), RTF_DONE, S(gte), 0);
+ } else
+ rtredirect(S(dst), S(gte), (struct sockaddr *)0,
+ RTF_DONE | RTF_HOST, S(gte), 0);
+}
+#endif /* ISO */
diff --git a/sys/netiso/iso_snpac.h b/sys/netiso/iso_snpac.h
new file mode 100644
index 0000000..105e8dd
--- /dev/null
+++ b/sys/netiso/iso_snpac.h
@@ -0,0 +1,112 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)iso_snpac.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+
+#define MAX_SNPALEN 8 /* curiously equal to sizeof x.121 (
+ plus 1 for nibble len) addr */
+struct snpa_req {
+ struct iso_addr sr_isoa; /* nsap address */
+ u_char sr_len; /* length of snpa */
+ u_char sr_snpa[MAX_SNPALEN]; /* snpa associated
+ with nsap address */
+ u_char sr_flags; /* true if entry is valid */
+ u_short sr_ht; /* holding time */
+};
+
+#define SNPA_VALID 0x01
+#define SNPA_ES 0x02
+#define SNPA_IS 0x04
+#define SNPA_PERM 0x10
+
+struct systype_req {
+ short sr_holdt; /* holding timer */
+ short sr_configt; /* configuration timer */
+ short sr_esconfigt; /* suggested ES configuration timer */
+ char sr_type; /* SNPA_ES or SNPA_IS */
+};
+
+struct esis_req {
+ short er_ht; /* holding time */
+ u_char er_flags; /* type and validity */
+};
+/*
+ * Space for this structure gets added onto the end of a route
+ * going to an ethernet or other 802.[45x] device.
+ */
+
+struct llinfo_llc {
+ struct llinfo_llc *lc_next; /* keep all llc routes linked */
+ struct llinfo_llc *lc_prev; /* keep all llc routes linked */
+ struct rtentry *lc_rt; /* backpointer to route */
+ struct esis_req lc_er; /* holding time, etc */
+#define lc_ht lc_er.er_ht
+#define lc_flags lc_er.er_flags
+};
+
+
+/* ISO arp IOCTL data structures */
+
+#define SIOCSSTYPE _IOW('a', 39, struct systype_req) /* set system type */
+#define SIOCGSTYPE _IOR('a', 40, struct systype_req) /* get system type */
+
+#ifdef KERNEL
+struct llinfo_llc llinfo_llc; /* head for linked lists */
+#endif /* KERNEL */
diff --git a/sys/netiso/iso_var.h b/sys/netiso/iso_var.h
new file mode 100644
index 0000000..946aeea
--- /dev/null
+++ b/sys/netiso/iso_var.h
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)iso_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: iso_var.h,v 4.2 88/06/29 15:00:08 hagens Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/iso_var.h,v $
+ */
+
+/*
+ * Interface address, iso version. One of these structures is
+ * allocated for each interface with an osi address. The ifaddr
+ * structure conatins the protocol-independent part
+ * of the structure, and is assumed to be first.
+ */
+struct iso_ifaddr {
+ struct ifaddr ia_ifa; /* protocol-independent info */
+#define ia_ifp ia_ifa.ifa_ifp
+#define ia_flags ia_ifa.ifa_flags
+ int ia_snpaoffset;
+ struct iso_ifaddr *ia_next; /* next in list of iso addresses */
+ struct sockaddr_iso ia_addr; /* reserve space for interface name */
+ struct sockaddr_iso ia_dstaddr; /* reserve space for broadcast addr */
+#define ia_broadaddr ia_dstaddr
+ struct sockaddr_iso ia_sockmask; /* reserve space for general netmask */
+};
+
+struct iso_aliasreq {
+ char ifra_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ struct sockaddr_iso ifra_addr;
+ struct sockaddr_iso ifra_dstaddr;
+ struct sockaddr_iso ifra_mask;
+ int ifra_snpaoffset;
+};
+
+struct iso_ifreq {
+ char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ struct sockaddr_iso ifr_Addr;
+};
+
+/*
+ * Given a pointer to an iso_ifaddr (ifaddr),
+ * return a pointer to the addr as a sockaddr_iso
+ */
+/*
+#define IA_SIS(ia) ((struct sockaddr_iso *)(ia.ia_ifa->ifa_addr))
+ * works if sockaddr_iso becomes variable sized.
+ */
+#define IA_SIS(ia) (&(((struct iso_ifaddr *)ia)->ia_addr))
+
+#define SIOCDIFADDR_ISO _IOW('i',25, struct iso_ifreq) /* delete IF addr */
+#define SIOCAIFADDR_ISO _IOW('i',26, struct iso_aliasreq)/* add/chg IFalias */
+#define SIOCGIFADDR_ISO _IOWR('i',33, struct iso_ifreq) /* get ifnet address */
+#define SIOCGIFDSTADDR_ISO _IOWR('i',34, struct iso_ifreq) /* get dst address */
+#define SIOCGIFNETMASK_ISO _IOWR('i',37, struct iso_ifreq) /* get dst address */
+
+/*
+ * This stuff should go in if.h or if_llc.h or someplace else,
+ * but for now . . .
+ */
+
+struct llc_etherhdr {
+ char dst[6];
+ char src[6];
+ char len[2];
+ char llc_dsap;
+ char llc_ssap;
+ char llc_ui_byte;
+};
+
+struct snpa_hdr {
+ struct ifnet *snh_ifp;
+ char snh_dhost[6];
+ char snh_shost[6];
+ short snh_flags;
+};
+#ifdef KERNEL
+struct iso_ifaddr *iso_ifaddr; /* linked list of iso address ifaces */
+struct iso_ifaddr *iso_localifa(); /* linked list of iso address ifaces */
+struct ifqueue clnlintrq; /* clnl packet input queue */
+#endif /* KERNEL */
diff --git a/sys/netiso/tp.trans b/sys/netiso/tp.trans
new file mode 100644
index 0000000..edefc76
--- /dev/null
+++ b/sys/netiso/tp.trans
@@ -0,0 +1,1342 @@
+/* NEW */
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp.trans 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* $Header: tp.trans,v 5.1 88/10/12 12:22:07 root Exp $
+ *
+ * Transition file for TP.
+ *
+ * DO NOT:
+ * - change the order of any of the events or states. to do so will
+ * make tppt, netstat, etc. cease working.
+ *
+ * NOTE:
+ * some hooks exist for data on (dis)connect, but it's ***NOT***SUPPORTED***
+ * (read: may not work!)
+ *
+ * I tried to put everything that causes a change of state in here, hence
+ * there are some seemingly trivial events like T_DETACH and T_LISTEN_req.
+ *
+ * Almost everything having to do w/ setting & cancelling timers is here
+ * but once it was debugged, I moved the setting of the
+ * keepalive (sendack) timer to tp_emit(), where an AK_TPDU is sent.
+ * This is so the code wouldn't be duplicated all over creation in here.
+ *
+ */
+*PROTOCOL tp
+
+*INCLUDE
+{
+/* @(#)tp.trans 8.1 (Berkeley) 6/10/93 */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/mbuf.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+
+#include <netiso/tp_param.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/tp_tpdu.h>
+#include <netiso/argo_debug.h>
+#include <netiso/tp_trace.h>
+#include <netiso/iso_errno.h>
+#include <netiso/tp_seq.h>
+#include <netiso/cons.h>
+
+#define DRIVERTRACE TPPTdriver
+#define sbwakeup(sb) sowakeup(p->tp_sock, sb);
+#define MCPY(d, w) (d ? m_copym(d, 0, (int)M_COPYALL, w): 0)
+
+static trick_hc = 1;
+
+int tp_emit(),
+ tp_goodack(), tp_goodXack(),
+ tp_stash()
+;
+void tp_indicate(), tp_getoptions(),
+ tp_soisdisconnecting(), tp_soisdisconnected(),
+ tp_recycle_tsuffix(),
+#ifdef TP_DEBUG_TIMERS
+ tp_etimeout(), tp_euntimeout(),
+ tp_ctimeout(), tp_cuntimeout(),
+ tp_ctimeout_MIN(),
+#endif
+ tp_freeref(), tp_detach(),
+ tp0_stash(), tp0_send(),
+ tp_netcmd(), tp_send()
+;
+
+typedef struct tp_pcb tpcb_struct;
+
+
+}
+
+*PCB tpcb_struct SYNONYM P
+
+*STATES
+
+TP_CLOSED
+TP_CRSENT
+TP_AKWAIT
+TP_OPEN
+TP_CLOSING
+TP_REFWAIT
+TP_LISTENING /* Local to this implementation */
+TP_CONFIRMING /* Local to this implementation */
+
+*EVENTS { struct timeval e_time; } SYNONYM E
+
+ /*
+ * C (typically cancelled) timers -
+ *
+ * let these be the first ones so for the sake of convenience
+ * their values are 0--> n-1
+ * DO NOT CHANGE THE ORDER OF THESE TIMER EVENTS!!
+ */
+ TM_inact
+ TM_retrans
+ /* TM_retrans is used for all
+ * simple retransmissions - CR,CC,XPD,DR
+ */
+
+ TM_sendack
+ /* TM_sendack does dual duty - keepalive AND closed-window
+ * Probes.
+ * It's set w/ keepalive-ticks every time an ack is sent.
+ * (this is done in (void) tp_emit() ).
+ * Whenever a DT arrives which doesn't require immediate acking,
+ * a separate fast-timeout flag is set ensuring 200ms response.
+ */
+ TM_notused
+
+ /*
+ * E (typically expired) timers - these may be in any order.
+ * These cause procedures to be executed directly; may not
+ * cause an 'event' as we know them here.
+ */
+ TM_reference { SeqNum e_low; SeqNum e_high; int e_retrans; }
+ TM_data_retrans { SeqNum e_low; SeqNum e_high; int e_retrans; }
+
+/* NOTE: in tp_input is a minor optimization that assumes that
+ * for all tpdu types that can take e_data and e_datalen, these
+ * fields fall in the same place in the event structure, that is,
+ * e_data is the first field and e_datalen is the 2nd field.
+ */
+
+ ER_TPDU {
+ u_char e_reason;
+ }
+ CR_TPDU { struct mbuf *e_data; /* first field */
+ int e_datalen; /* 2nd field */
+ u_int e_cdt;
+ }
+ DR_TPDU { struct mbuf *e_data; /* first field */
+ int e_datalen; /* 2nd field */
+ u_short e_sref;
+ u_char e_reason;
+ }
+ DC_TPDU
+ CC_TPDU { struct mbuf *e_data; /* first field */
+ int e_datalen; /* 2nd field */
+ u_short e_sref;
+ u_int e_cdt;
+ }
+ AK_TPDU { u_int e_cdt;
+ SeqNum e_seq;
+ SeqNum e_subseq;
+ u_char e_fcc_present;
+ }
+ DT_TPDU { struct mbuf *e_data; /* first field */
+ int e_datalen; /* 2nd field */
+ u_int e_eot;
+ SeqNum e_seq;
+ }
+ XPD_TPDU { struct mbuf *e_data; /* first field */
+ int e_datalen; /* 2nd field */
+ SeqNum e_seq;
+ }
+ XAK_TPDU { SeqNum e_seq; }
+
+ T_CONN_req
+ T_DISC_req { u_char e_reason; }
+ T_LISTEN_req
+ T_DATA_req
+ T_XPD_req
+ T_USR_rcvd
+ T_USR_Xrcvd
+ T_DETACH
+ T_NETRESET
+ T_ACPT_req
+
+
+*TRANSITIONS
+
+
+/* TP_AKWAIT doesn't exist in TP 0 */
+SAME <== TP_AKWAIT [ CC_TPDU, DC_TPDU, XAK_TPDU ]
+ DEFAULT
+ NULLACTION
+;
+
+
+/* applicable in TP4, TP0 */
+SAME <== TP_REFWAIT DR_TPDU
+ ( $$.e_sref != 0 )
+ {
+ (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
+ }
+;
+
+/* applicable in TP4, TP0 */
+SAME <== TP_REFWAIT [ CR_TPDU, CC_TPDU, DT_TPDU,
+ DR_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU, DC_TPDU, ER_TPDU ]
+ DEFAULT
+ {
+# ifdef TP_DEBUG
+ if( $E.ev_number != AK_TPDU )
+ printf("TPDU 0x%x in REFWAIT!!!!\n", $E.ev_number);
+# endif TP_DEBUG
+ }
+;
+
+/* applicable in TP4, TP0 */
+SAME <== TP_REFWAIT [ T_DETACH, T_DISC_req ]
+ DEFAULT
+ NULLACTION
+;
+
+/* applicable in TP4, TP0 */
+SAME <== TP_CRSENT AK_TPDU
+ ($P.tp_class == TP_CLASS_0)
+ {
+ /* oh, man is this grotesque or what? */
+ (void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq);
+ /* but it's necessary because this pseudo-ack may happen
+ * before the CC arrives, but we HAVE to adjust the
+ * snduna as a result of the ack, WHENEVER it arrives
+ */
+ }
+;
+
+/* applicable in TP4, TP0 */
+SAME <== TP_CRSENT
+ [ CR_TPDU, DC_TPDU, DT_TPDU, XPD_TPDU, XAK_TPDU ]
+ DEFAULT
+ NULLACTION
+;
+
+/* applicable in TP4, TP0 */
+SAME <== TP_CLOSED [ DT_TPDU, XPD_TPDU,
+ ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ]
+ DEFAULT
+ NULLACTION
+;
+
+/* TP_CLOSING doesn't exist in TP 0 */
+SAME <== TP_CLOSING
+ [ CC_TPDU, CR_TPDU, DT_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU ]
+ DEFAULT
+ NULLACTION
+;
+
+
+/* DC_TPDU doesn't exist in TP 0 */
+SAME <== TP_OPEN DC_TPDU
+ DEFAULT
+ NULLACTION
+;
+
+/* applicable in TP4, TP0 */
+SAME <== TP_LISTENING [DR_TPDU, CC_TPDU, DT_TPDU, XPD_TPDU,
+ ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ]
+ DEFAULT
+ NULLACTION
+;
+
+/* applicable in TP4, TP0 */
+TP_LISTENING <== TP_CLOSED T_LISTEN_req
+ DEFAULT
+ NULLACTION
+;
+
+/* applicable in TP4, TP0 */
+TP_CLOSED <== [ TP_LISTENING, TP_CLOSED ] T_DETACH
+ DEFAULT
+ {
+ tp_detach($P);
+ }
+;
+
+TP_CONFIRMING <== TP_LISTENING CR_TPDU
+ ( $P.tp_class == TP_CLASS_0)
+ {
+ $P.tp_refstate = REF_OPEN; /* has timers ??? */
+ }
+;
+
+TP_CONFIRMING <== TP_LISTENING CR_TPDU
+ DEFAULT
+ {
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc, "CR datalen data", $$.e_datalen, $$.e_data,0,0);
+ ENDTRACE
+ IFDEBUG(D_CONN)
+ printf("CR datalen 0x%x data 0x%x", $$.e_datalen, $$.e_data);
+ ENDDEBUG
+ $P.tp_refstate = REF_OPEN; /* has timers */
+ $P.tp_fcredit = $$.e_cdt;
+
+ if ($$.e_datalen > 0) {
+ /* n/a for class 0 */
+ ASSERT($P.tp_Xrcv.sb_cc == 0);
+ sbappendrecord(&$P.tp_Xrcv, $$.e_data);
+ $$.e_data = MNULL;
+ }
+ }
+;
+
+TP_OPEN <== TP_CONFIRMING T_ACPT_req
+ ( $P.tp_class == TP_CLASS_0 )
+ {
+ IncStat(ts_tp0_conn);
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
+ ENDTRACE
+ IFDEBUG(D_CONN)
+ printf("Confirming connection: $P" );
+ ENDDEBUG
+ soisconnected($P.tp_sock);
+ (void) tp_emit(CC_TPDU_type, $P, 0,0, MNULL) ;
+ $P.tp_fcredit = 1;
+ }
+;
+
+TP_AKWAIT <== TP_CONFIRMING T_ACPT_req
+ (tp_emit(CC_TPDU_type, $P, 0,0, MCPY($P.tp_ucddata, M_NOWAIT)) == 0)
+ {
+ IncStat(ts_tp4_conn); /* even though not quite open */
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
+ ENDTRACE
+ IFDEBUG(D_CONN)
+ printf("Confirming connection: $P" );
+ ENDDEBUG
+ tp_getoptions($P);
+ soisconnecting($P.tp_sock);
+ if (($P.tp_rx_strat & TPRX_FASTSTART) && ($P.tp_fcredit > 0))
+ $P.tp_cong_win = $P.tp_fcredit * $P.tp_l_tpdusize;
+ $P.tp_retrans = $P.tp_Nretrans;
+ tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
+ }
+;
+
+/* TP4 only */
+TP_CLOSED <== TP_CONFIRMING T_ACPT_req
+ DEFAULT /* emit failed */
+ {
+ IFDEBUG(D_CONN)
+ printf("event: CR_TPDU emit CC failed done " );
+ ENDDEBUG
+ soisdisconnected($P.tp_sock);
+ tp_recycle_tsuffix($P);
+ tp_freeref($P.tp_lref);
+ tp_detach($P);
+ }
+;
+
+/* applicable in TP4, TP0 */
+TP_CRSENT <== TP_CLOSED T_CONN_req
+ DEFAULT
+ {
+ int error;
+ struct mbuf *data = MNULL;
+
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)$P.tp_flags,
+ $P.tp_ucddata, 0, 0);
+ ENDTRACE
+ data = MCPY($P.tp_ucddata, M_WAIT);
+ if (data) {
+ IFDEBUG(D_CONN)
+ printf("T_CONN_req.trans m_copy cc 0x%x\n",
+ $P.tp_ucddata);
+ dump_mbuf(data, "sosnd @ T_CONN_req");
+ ENDDEBUG
+ }
+
+ if (error = tp_emit(CR_TPDU_type, $P, 0, 0, data) )
+ return error; /* driver WON'T change state; will return error */
+
+ $P.tp_refstate = REF_OPEN; /* has timers */
+ if($P.tp_class != TP_CLASS_0) {
+ $P.tp_retrans = $P.tp_Nretrans;
+ tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks);
+ }
+ }
+;
+
+/* applicable in TP4, TP0, but state TP_AKWAIT doesn't apply to TP0 */
+TP_REFWAIT <== [ TP_CRSENT, TP_AKWAIT, TP_OPEN ] DR_TPDU
+ DEFAULT
+ {
+ sbflush(&$P.tp_Xrcv); /* purge non-delivered data data */
+ if ($$.e_datalen > 0) {
+ sbappendrecord(&$P.tp_Xrcv, $$.e_data);
+ $$.e_data = MNULL;
+ }
+ if ($P.tp_state == TP_OPEN)
+ tp_indicate(T_DISCONNECT, $P, 0);
+ else {
+ int so_error = ECONNREFUSED;
+ if ($$.e_reason != (E_TP_NO_SESSION ^ TP_ERROR_MASK) &&
+ $$.e_reason != (E_TP_NO_CR_ON_NC ^ TP_ERROR_MASK) &&
+ $$.e_reason != (E_TP_REF_OVERFLOW ^ TP_ERROR_MASK))
+ so_error = ECONNABORTED;
+ tp_indicate(T_DISCONNECT, $P, so_error);
+ }
+ tp_soisdisconnected($P);
+ if ($P.tp_class != TP_CLASS_0) {
+ if ($P.tp_state == TP_OPEN ) {
+ tp_euntimeout($P, TM_data_retrans); /* all */
+ tp_cuntimeout($P, TM_retrans);
+ tp_cuntimeout($P, TM_inact);
+ tp_cuntimeout($P, TM_sendack);
+ $P.tp_flags &= ~TPF_DELACK;
+ }
+ tp_cuntimeout($P, TM_retrans);
+ if( $$.e_sref != 0 )
+ (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
+ }
+ }
+;
+
+SAME <== TP_CLOSED DR_TPDU
+ DEFAULT
+ {
+ if( $$.e_sref != 0 )
+ (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
+ /* reference timer already set - reset it to be safe (???) */
+ tp_euntimeout($P, TM_reference); /* all */
+ tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks);
+ }
+;
+
+/* NBS(34) */
+TP_REFWAIT <== TP_CRSENT ER_TPDU
+ DEFAULT
+ {
+ tp_cuntimeout($P, TM_retrans);
+ tp_indicate(ER_TPDU, $P, $$.e_reason);
+ tp_soisdisconnected($P);
+ }
+;
+
+/* NBS(27) */
+TP_REFWAIT <== TP_CLOSING DR_TPDU
+ DEFAULT
+ {
+ tp_cuntimeout($P, TM_retrans);
+ tp_soisdisconnected($P);
+ }
+;
+/* these two transitions are the same but can't be combined because xebec
+ * can't handle the use of $$.e_reason if they're combined
+ */
+/* NBS(27) */
+TP_REFWAIT <== TP_CLOSING ER_TPDU
+ DEFAULT
+ {
+ tp_indicate(ER_TPDU, $P, $$.e_reason);
+ tp_cuntimeout($P, TM_retrans);
+ tp_soisdisconnected($P);
+ }
+;
+/* NBS(27) */
+TP_REFWAIT <== TP_CLOSING DC_TPDU
+ DEFAULT
+ {
+ tp_cuntimeout($P, TM_retrans);
+ tp_soisdisconnected($P);
+ }
+;
+
+/* NBS(21) */
+SAME <== TP_CLOSED [ CC_TPDU, CR_TPDU ]
+ DEFAULT
+ { /* don't ask me why we have to do this - spec says so */
+ (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL);
+ /* don't bother with retransmissions of the DR */
+ }
+;
+
+/* NBS(34) */
+TP_REFWAIT <== TP_OPEN ER_TPDU
+ ($P.tp_class == TP_CLASS_0)
+ {
+ tp_soisdisconnecting($P.tp_sock);
+ tp_indicate(ER_TPDU, $P, $$.e_reason);
+ tp_soisdisconnected($P);
+ tp_netcmd( $P, CONN_CLOSE );
+ }
+;
+
+TP_CLOSING <== [ TP_AKWAIT, TP_OPEN ] ER_TPDU
+ DEFAULT
+ {
+ if ($P.tp_state == TP_OPEN) {
+ tp_euntimeout($P, TM_data_retrans); /* all */
+ tp_cuntimeout($P, TM_inact);
+ tp_cuntimeout($P, TM_sendack);
+ }
+ tp_soisdisconnecting($P.tp_sock);
+ tp_indicate(ER_TPDU, $P, $$.e_reason);
+ $P.tp_retrans = $P.tp_Nretrans;
+ tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
+ (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, MNULL);
+ }
+;
+/* NBS(6) */
+TP_OPEN <== TP_CRSENT CC_TPDU
+ ($P.tp_class == TP_CLASS_0)
+ {
+ tp_cuntimeout($P, TM_retrans);
+ IncStat(ts_tp0_conn);
+ $P.tp_fcredit = 1;
+ soisconnected($P.tp_sock);
+ }
+;
+
+TP_OPEN <== TP_CRSENT CC_TPDU
+ DEFAULT
+ {
+ IFDEBUG(D_CONN)
+ printf("trans: CC_TPDU in CRSENT state flags 0x%x\n",
+ (int)$P.tp_flags);
+ ENDDEBUG
+ IncStat(ts_tp4_conn);
+ $P.tp_fref = $$.e_sref;
+ $P.tp_fcredit = $$.e_cdt;
+ if (($P.tp_rx_strat & TPRX_FASTSTART) && ($$.e_cdt > 0))
+ $P.tp_cong_win = $$.e_cdt * $P.tp_l_tpdusize;
+ tp_getoptions($P);
+ tp_cuntimeout($P, TM_retrans);
+ if ($P.tp_ucddata) {
+ IFDEBUG(D_CONN)
+ printf("dropping user connect data cc 0x%x\n",
+ $P.tp_ucddata->m_len);
+ ENDDEBUG
+ m_freem($P.tp_ucddata);
+ $P.tp_ucddata = 0;
+ }
+ soisconnected($P.tp_sock);
+ if ($$.e_datalen > 0) {
+ ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */
+ sbappendrecord(&$P.tp_Xrcv, $$.e_data);
+ $$.e_data = MNULL;
+ }
+
+ (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
+ tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
+ }
+;
+
+/* TP4 only */
+SAME <== TP_CRSENT TM_retrans
+ ( $P.tp_retrans > 0 )
+ {
+ struct mbuf *data = MNULL;
+ int error;
+
+ IncStat(ts_retrans_cr);
+ $P.tp_cong_win = 1 * $P.tp_l_tpdusize;
+ data = MCPY($P.tp_ucddata, M_NOWAIT);
+ if($P.tp_ucddata) {
+ IFDEBUG(D_CONN)
+ printf("TM_retrans.trans m_copy cc 0x%x\n", data);
+ dump_mbuf($P.tp_ucddata, "sosnd @ TM_retrans");
+ ENDDEBUG
+ if( data == MNULL )
+ return ENOBUFS;
+ }
+
+ $P.tp_retrans --;
+ if( error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) {
+ $P.tp_sock->so_error = error;
+ }
+ tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks);
+ }
+;
+
+/* TP4 only */
+TP_REFWAIT <== TP_CRSENT TM_retrans
+ DEFAULT /* no more CR retransmissions */
+ {
+ IncStat(ts_conn_gaveup);
+ $P.tp_sock->so_error = ETIMEDOUT;
+ tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
+ tp_soisdisconnected($P);
+ }
+;
+
+/* TP4 only */
+SAME <== TP_AKWAIT CR_TPDU
+ DEFAULT
+ /* duplicate CR (which doesn't really exist in the context of
+ * a connectionless network layer)
+ * Doesn't occur in class 0.
+ */
+ {
+ int error;
+ struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
+
+ if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) {
+ $P.tp_sock->so_error = error;
+ }
+ $P.tp_retrans = $P.tp_Nretrans;
+ tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
+ }
+;
+
+/* TP4 only */
+TP_OPEN <== TP_AKWAIT DT_TPDU
+ ( IN_RWINDOW( $P, $$.e_seq,
+ $P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
+ {
+ int doack;
+
+ /*
+ * Get rid of any confirm or connect data, so that if we
+ * crash or close, it isn't thought of as disconnect data.
+ */
+ if ($P.tp_ucddata) {
+ m_freem($P.tp_ucddata);
+ $P.tp_ucddata = 0;
+ }
+ tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
+ tp_cuntimeout($P, TM_retrans);
+ soisconnected($P.tp_sock);
+ tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
+
+ /* see also next 2 transitions, if you make any changes */
+
+ doack = tp_stash($P, $E);
+ IFDEBUG(D_DATA)
+ printf("tp_stash returns %d\n",doack);
+ ENDDEBUG
+
+ if (doack) {
+ (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
+ tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
+ } else
+ tp_ctimeout( $P, TM_sendack, (int)$P.tp_sendack_ticks);
+
+ IFDEBUG(D_DATA)
+ printf("after stash calling sbwakeup\n");
+ ENDDEBUG
+ }
+;
+
+SAME <== TP_OPEN DT_TPDU
+ ( $P.tp_class == TP_CLASS_0 )
+ {
+ tp0_stash($P, $E);
+ sbwakeup( &$P.tp_sock->so_rcv );
+
+ IFDEBUG(D_DATA)
+ printf("after stash calling sbwakeup\n");
+ ENDDEBUG
+ }
+;
+
+/* TP4 only */
+SAME <== TP_OPEN DT_TPDU
+ ( IN_RWINDOW( $P, $$.e_seq,
+ $P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
+ {
+ int doack; /* tells if we must ack immediately */
+
+ tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
+ sbwakeup( &$P.tp_sock->so_rcv );
+
+ doack = tp_stash($P, $E);
+ IFDEBUG(D_DATA)
+ printf("tp_stash returns %d\n",doack);
+ ENDDEBUG
+
+ if(doack)
+ (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
+ else
+ tp_ctimeout_MIN( $P, TM_sendack, (int)$P.tp_sendack_ticks);
+
+ IFDEBUG(D_DATA)
+ printf("after stash calling sbwakeup\n");
+ ENDDEBUG
+ }
+;
+
+/* Not in window - we must ack under certain circumstances, namely
+ * a) if the seq number is below lwe but > lwe - (max credit ever given)
+ * (to handle lost acks) Can use max-possible-credit for this ^^^.
+ * and
+ * b) seq number is > uwe but < uwe + previously sent & withdrawn credit
+ *
+ * (see 12.2.3.8.1 of ISO spec, p. 73)
+ * We just always ack.
+ */
+/* TP4 only */
+SAME <== [ TP_OPEN, TP_AKWAIT ] DT_TPDU
+ DEFAULT /* Not in window */
+ {
+ IFTRACE(D_DATA)
+ tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ",
+ $$.e_seq, $P.tp_rcvnxt, $P.tp_lcredit, 0);
+ ENDTRACE
+ IncStat(ts_dt_niw);
+ m_freem($$.e_data);
+ tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
+ (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
+ }
+;
+
+/* TP4 only */
+TP_OPEN <== TP_AKWAIT AK_TPDU
+ DEFAULT
+ {
+ if ($P.tp_ucddata) {
+ m_freem($P.tp_ucddata);
+ $P.tp_ucddata = 0;
+ }
+ (void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq);
+ tp_cuntimeout($P, TM_retrans);
+
+ soisconnected($P.tp_sock);
+ IFTRACE(D_CONN)
+ struct socket *so = $P.tp_sock;
+ tptrace(TPPTmisc,
+ "called sosiconn: so so_state rcv.sb_sel rcv.sb_flags",
+ so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags);
+ tptrace(TPPTmisc,
+ "called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head",
+ so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head);
+ ENDTRACE
+
+ tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
+ tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
+ }
+;
+
+/* TP4 only */
+TP_OPEN <== [ TP_OPEN, TP_AKWAIT ] XPD_TPDU
+ ($P.tp_Xrcvnxt == $$.e_seq)
+ {
+ if( $P.tp_state == TP_AKWAIT ) {
+ if ($P.tp_ucddata) {
+ m_freem($P.tp_ucddata);
+ $P.tp_ucddata = 0;
+ }
+ tp_cuntimeout($P, TM_retrans);
+ soisconnected($P.tp_sock);
+ tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
+ tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
+ }
+ IFTRACE(D_XPD)
+ tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n",
+ $P.tp_Xrcvnxt,$$.e_seq, $$.e_datalen, $$.e_data->m_len);
+ ENDTRACE
+
+ $P.tp_sock->so_state |= SS_RCVATMARK;
+ $$.e_data->m_flags |= M_EOR;
+ sbinsertoob(&$P.tp_Xrcv, $$.e_data);
+ IFDEBUG(D_XPD)
+ dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv");
+ ENDDEBUG
+ tp_indicate(T_XDATA, $P, 0);
+ sbwakeup( &$P.tp_Xrcv );
+
+ (void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
+ SEQ_INC($P, $P.tp_Xrcvnxt);
+ }
+;
+
+/* TP4 only */
+SAME <== TP_OPEN T_USR_Xrcvd
+ DEFAULT
+ {
+ if( $P.tp_Xrcv.sb_cc == 0 ) {
+ /* kludge for select(): */
+ /* $P.tp_sock->so_state &= ~SS_OOBAVAIL; */
+ }
+ }
+ /* OLD WAY:
+ * Ack only after the user receives the XPD. This is better for
+ * users that use one XPD right after another.
+ * Acking right away (the NEW WAY, see the prev. transition) is
+ * better for occasional * XPD, when the receiving user doesn't
+ * want to read the XPD immediately (which is session's behavior).
+ *
+ int error = tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
+ SEQ_INC($P, $P.tp_Xrcvnxt);
+ return error;
+ */
+;
+
+/* NOTE: presently if the user doesn't read the connection data
+ * before and expedited data PDU comes in, the connection data will
+ * be dropped. This is a bug. To avoid it, we need somewhere else
+ * to put the connection data.
+ * On the other hand, we need not to have it sitting around forever.
+ * This is a problem with the idea of trying to accommodate
+ * data on connect w/ a passive-open user interface.
+ */
+/* TP4 only */
+
+SAME <== [ TP_AKWAIT, TP_OPEN ] XPD_TPDU
+ DEFAULT /* not in window or cdt==0 */
+ {
+ IFTRACE(D_XPD)
+ tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n",
+ $P.tp_Xrcvnxt, $$.e_seq, $P.tp_Xrcv.sb_cc , 0);
+ ENDTRACE
+ if( $P.tp_Xrcvnxt != $$.e_seq )
+ IncStat(ts_xpd_niw);
+ if( $P.tp_Xrcv.sb_cc ) {
+ /* might as well kick 'em again */
+ tp_indicate(T_XDATA, $P, 0);
+ IncStat(ts_xpd_dup);
+ }
+ m_freem($$.e_data);
+ tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
+ /* don't send an xack because the xak gives "last one received", not
+ * "next one i expect" (dumb)
+ */
+ }
+;
+
+/* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries
+ * to detach all its "children"
+ * Also (CRSENT) when user kills a job that's doing a connect()
+ */
+TP_REFWAIT <== TP_CRSENT T_DETACH
+ ($P.tp_class == TP_CLASS_0)
+ {
+ struct socket *so = $P.tp_sock;
+
+ /* detach from parent socket so it can finish closing */
+ if (so->so_head) {
+ if (!soqremque(so, 0) && !soqremque(so, 1))
+ panic("tp: T_DETACH");
+ so->so_head = 0;
+ }
+ tp_soisdisconnecting($P.tp_sock);
+ tp_netcmd( $P, CONN_CLOSE);
+ tp_soisdisconnected($P);
+ }
+;
+
+/* TP4 only */
+TP_CLOSING <== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ] T_DETACH
+ DEFAULT
+ {
+ struct socket *so = $P.tp_sock;
+ struct mbuf *data = MNULL;
+
+ /* detach from parent socket so it can finish closing */
+ if (so->so_head) {
+ if (!soqremque(so, 0) && !soqremque(so, 1))
+ panic("tp: T_DETACH");
+ so->so_head = 0;
+ }
+ if ($P.tp_state != TP_CLOSING) {
+ tp_soisdisconnecting($P.tp_sock);
+ data = MCPY($P.tp_ucddata, M_NOWAIT);
+ (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data);
+ $P.tp_retrans = $P.tp_Nretrans;
+ tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
+ }
+ }
+;
+
+TP_REFWAIT <== [ TP_OPEN, TP_CRSENT ] T_DISC_req
+ ( $P.tp_class == TP_CLASS_0 )
+ {
+ tp_soisdisconnecting($P.tp_sock);
+ tp_netcmd( $P, CONN_CLOSE);
+ tp_soisdisconnected($P);
+ }
+;
+
+/* TP4 only */
+TP_CLOSING <== [ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ] T_DISC_req
+ DEFAULT
+ {
+ struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
+
+ if($P.tp_state == TP_OPEN) {
+ tp_euntimeout($P, TM_data_retrans); /* all */
+ tp_cuntimeout($P, TM_inact);
+ tp_cuntimeout($P, TM_sendack);
+ $P.tp_flags &= ~TPF_DELACK;
+ }
+ if (data) {
+ IFDEBUG(D_CONN)
+ printf("T_DISC_req.trans tp_ucddata 0x%x\n",
+ $P.tp_ucddata);
+ dump_mbuf(data, "ucddata @ T_DISC_req");
+ ENDDEBUG
+ }
+ tp_soisdisconnecting($P.tp_sock);
+ $P.tp_retrans = $P.tp_Nretrans;
+ tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
+
+ if( trick_hc )
+ return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data);
+ }
+;
+
+/* TP4 only */
+SAME <== TP_AKWAIT TM_retrans
+ ( $P.tp_retrans > 0 )
+ {
+ int error;
+ struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
+
+ IncStat(ts_retrans_cc);
+ $P.tp_retrans --;
+ $P.tp_cong_win = 1 * $P.tp_l_tpdusize;
+
+ if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) )
+ $P.tp_sock->so_error = error;
+ tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
+ }
+;
+
+/* TP4 only */
+TP_CLOSING <== TP_AKWAIT TM_retrans
+ DEFAULT /* out of time */
+ {
+ IncStat(ts_conn_gaveup);
+ tp_soisdisconnecting($P.tp_sock);
+ $P.tp_sock->so_error = ETIMEDOUT;
+ tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
+ (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL);
+ $P.tp_retrans = $P.tp_Nretrans;
+ tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
+ }
+;
+
+/* the retrans timers had better go off BEFORE the inactivity timer does,
+ * if transmissions are going on.
+ * (i.e., TM_inact should be greater than timer for all retrans plus ack
+ * turnaround)
+ */
+/* TP4 only */
+TP_CLOSING <== TP_OPEN [ TM_inact, TM_retrans, TM_data_retrans ]
+ DEFAULT
+ {
+ tp_euntimeout($P, TM_data_retrans); /* all */
+ tp_cuntimeout($P, TM_inact);
+ tp_cuntimeout($P, TM_sendack);
+
+ IncStat(ts_conn_gaveup);
+ tp_soisdisconnecting($P.tp_sock);
+ $P.tp_sock->so_error = ETIMEDOUT;
+ tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
+ (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL);
+ $P.tp_retrans = $P.tp_Nretrans;
+ tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
+ }
+;
+
+/* TP4 only */
+SAME <== TP_OPEN TM_retrans
+ ( $P.tp_retrans > 0 )
+ {
+ $P.tp_cong_win = 1 * $P.tp_l_tpdusize;
+ /* resume XPD */
+ if ( $P.tp_Xsnd.sb_mb ) {
+ struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
+ int shift;
+
+ IFTRACE(D_XPD)
+ tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndnxt snduna",
+ $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt,
+ $P.tp_snduna);
+ ENDTRACE
+ IFDEBUG(D_XPD)
+ dump_mbuf(m, "XPD retrans emitting M");
+ ENDDEBUG
+ IncStat(ts_retrans_xpd);
+ $P.tp_retrans --;
+ shift = max($P.tp_Nretrans - $P.tp_retrans, 6);
+ (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
+ tp_ctimeout($P, TM_retrans, ((int)$P.tp_dt_ticks) << shift);
+ }
+ }
+;
+
+/* TP4 only */
+SAME <== TP_OPEN TM_data_retrans
+ ($P.tp_rxtshift < TP_NRETRANS)
+ {
+ $P.tp_rxtshift++;
+ (void) tp_data_retrans($P);
+ }
+;
+
+/* TP4 only */
+SAME <== TP_CLOSING TM_retrans
+ ( $P.tp_retrans > 0 )
+ {
+ $P.tp_retrans --;
+ (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL);
+ IncStat(ts_retrans_dr);
+ tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
+ }
+;
+
+/* TP4 only */
+TP_REFWAIT <== TP_CLOSING TM_retrans
+ DEFAULT /* no more retrans - gave up */
+ {
+ $P.tp_sock->so_error = ETIMEDOUT;
+ $P.tp_refstate = REF_FROZEN;
+ tp_recycle_tsuffix( $P );
+ tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks);
+ }
+;
+
+/*
+ * The resources are kept around until the ref timer goes off.
+ * The suffices are wiped out sooner so they can be reused right away.
+ */
+/* applicable in TP4, TP0 */
+TP_CLOSED <== TP_REFWAIT TM_reference
+ DEFAULT
+ {
+ tp_freeref($P.tp_lref);
+ tp_detach($P);
+ }
+;
+
+/* applicable in TP4, TP0 */
+/* A duplicate CR from connectionless network layer can't happen */
+SAME <== TP_OPEN [ CR_TPDU, CC_TPDU ]
+ DEFAULT
+ {
+ if( $P.tp_class != TP_CLASS_0) {
+ tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
+ if ( $E.ev_number == CC_TPDU )
+ (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
+ }
+ /* ignore it if class 0 - state tables are blank for this */
+ }
+;
+
+/* applicable in TP4, TP0 */
+SAME <== TP_OPEN T_DATA_req
+ DEFAULT
+ {
+ IFTRACE(D_DATA)
+ tptrace(TPPTmisc, "T_DATA_req sndnxt snduna fcredit, tpcb",
+ $P.tp_sndnxt, $P.tp_snduna, $P.tp_fcredit, $P);
+ ENDTRACE
+
+ tp_send($P);
+ }
+;
+
+/* TP4 only */
+SAME <== TP_OPEN T_XPD_req
+ DEFAULT
+ /* T_XPD_req was issued by sosend iff xpd socket buf was empty
+ * at time of sosend(),
+ * AND (which means) there were no unacknowledged XPD tpdus outstanding!
+ */
+ {
+ int error = 0;
+
+ /* resume XPD */
+ if ( $P.tp_Xsnd.sb_mb ) {
+ struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
+ /* m_copy doesn't preserve the m_xlink field, but at this pt.
+ * that doesn't matter
+ */
+
+ IFTRACE(D_XPD)
+ tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndnxt snduna",
+ $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt,
+ $P.tp_snduna);
+ ENDTRACE
+ IFDEBUG(D_XPD)
+ printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc);
+ dump_mbuf(m, "XPD req emitting M");
+ ENDDEBUG
+ error =
+ tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
+ $P.tp_retrans = $P.tp_Nretrans;
+
+ tp_ctimeout($P, TM_retrans, (int)$P.tp_rxtcur);
+ SEQ_INC($P, $P.tp_Xsndnxt);
+ }
+ if(trick_hc)
+ return error;
+ }
+;
+
+/* TP4, faked ack in TP0 when cons send completes */
+SAME <== TP_OPEN AK_TPDU
+ ( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq) )
+
+ /* tp_goodack == true means
+ * EITHER it actually acked something heretofore unacknowledged
+ * OR no news but the credit should be processed.
+ */
+ {
+ struct sockbuf *sb = &$P.tp_sock->so_snd;
+
+ IFDEBUG(D_ACKRECV)
+ printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt);
+ ENDDEBUG
+ if( $P.tp_class != TP_CLASS_0) {
+ tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
+ }
+ sbwakeup(sb);
+ IFDEBUG(D_ACKRECV)
+ printf("GOOD ACK new sndnxt 0x%x\n", $P.tp_sndnxt);
+ ENDDEBUG
+ }
+;
+
+/* TP4, and TP0 after sending a CC or possibly a CR */
+SAME <== TP_OPEN AK_TPDU
+ DEFAULT
+ {
+ IFTRACE(D_ACKRECV)
+ tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq",
+ $$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0);
+ ENDTRACE
+ if( $P.tp_class != TP_CLASS_0 ) {
+
+ if ( !$$.e_fcc_present ) {
+ /* send ACK with FCC */
+ IncStat( ts_ackreason[_ACK_FCC_] );
+ (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL);
+ }
+ tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
+ }
+ }
+;
+
+/* NBS(47) */
+ /* goes in at *** */
+ /* just so happens that this is never true now, because we allow
+ * only 1 packet in the queue at once (this could be changed)
+ if ( $P.tp_Xsnd.sb_mb ) {
+ struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??);
+
+ (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
+ $P.tp_retrans = $P.tp_Nretrans;
+ tp_ctimeout($P, TM_retrans, (int)$P.tp_xpd_ticks);
+ SEQ_INC($P, $P.tp_Xsndnxt);
+ }
+ */
+ /* end of the above hack */
+
+/* TP4 only */
+SAME <== TP_OPEN XAK_TPDU
+ ( tp_goodXack($P, $$.e_seq) )
+ /* tp_goodXack checks for good ack, removes the correct
+ * tpdu from the queue and returns 1 if ack was legit, 0 if not.
+ * also updates tp_Xuna
+ */
+ {
+ tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
+ tp_cuntimeout($P, TM_retrans);
+
+ sbwakeup( &$P.tp_sock->so_snd );
+
+ /* resume normal data */
+ tp_send($P);
+ }
+;
+
+/* TP4, and TP0 after sending a CC or possibly a CR */
+SAME <== TP_OPEN XAK_TPDU
+ DEFAULT
+ {
+ IFTRACE(D_ACKRECV)
+ tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0);
+ ENDTRACE
+ if( $P.tp_class != TP_CLASS_0 ) {
+ tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
+ }
+ }
+;
+
+/* TP4 only */
+SAME <== TP_OPEN TM_sendack
+ DEFAULT
+ {
+ int timo;
+ IFTRACE(D_TIMER)
+ tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe,
+ $P.tp_sent_lcdt, 0);
+ ENDTRACE
+ IncPStat($P, tps_n_TMsendack);
+ (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
+ if ($P.tp_fcredit == 0) {
+ if ($P.tp_rxtshift < TP_MAXRXTSHIFT)
+ $P.tp_rxtshift++;
+ timo = ($P.tp_dt_ticks) << $P.tp_rxtshift;
+ } else
+ timo = $P.tp_sendack_ticks;
+ tp_ctimeout($P, TM_sendack, timo);
+ }
+;
+
+/* TP0 only */
+SAME <== TP_OPEN T_USR_rcvd
+ ($P.tp_class == TP_CLASS_0)
+ {
+ if (sbspace(&$P.tp_sock->so_rcv) > 0)
+ tp0_openflow($P);
+ }
+;
+
+/* TP4 only */
+ /* If old credit was zero,
+ * we'd better inform other side that we now have space
+ * But this is not enough. Sender might not yet have
+ * seen an ack with cdt 0 but it might still think the
+ * window is closed, so it's going to wait.
+ * Best to send an ack each time.
+ * Strictly speaking, this ought to be a function of the
+ * general ack strategy.
+ */
+SAME <== TP_OPEN T_USR_rcvd
+ DEFAULT
+ {
+ if( trick_hc ) {
+ SeqNum ack_thresh;
+ /*
+ * If the upper window edge has advanced a reasonable
+ * amount beyond what was known, send an ACK.
+ * A reasonable amount is 2 packets, unless the max window
+ * is only 1 or 2 packets, in which case we
+ * should send an ack for any advance in the upper window edge.
+ */
+ LOCAL_CREDIT($P);
+ ack_thresh = SEQ_SUB($P, $P.tp_lcredit + $P.tp_rcvnxt,
+ ($P.tp_maxlcredit > 2 ? 2 : 1));
+ if (SEQ_GT($P, ack_thresh, $P.tp_sent_uwe)) {
+ IncStat(ts_ackreason[_ACK_USRRCV_]);
+ $P.tp_flags &= ~TPF_DELACK;
+ return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
+ }
+ }
+ }
+;
+
+/* applicable in TP4, TP0 */
+SAME <== TP_REFWAIT [ T_USR_rcvd, T_USR_Xrcvd ]
+ DEFAULT
+ /* This happens if other end sent a DR when the user was waiting
+ * on a receive.
+ * Processing the DR includes putting us in REFWAIT state.
+ */
+ {
+ if(trick_hc)
+ return ECONNABORTED;
+ }
+;
+
+/* TP0 only */
+TP_REFWAIT <== [ TP_OPEN, TP_CRSENT, TP_LISTENING ] T_NETRESET
+ ( $P.tp_class != TP_CLASS_4 )
+ /* 0 or (4 and 0) */
+ /* in OPEN class will be 0 or 4 but not both */
+ /* in CRSENT or LISTENING it could be in negotiation, hence both */
+ /* Actually, this shouldn't ever happen in LISTENING */
+ {
+ ASSERT( $P.tp_state != TP_LISTENING );
+ tp_indicate(T_DISCONNECT, $P, ECONNRESET);
+ tp_soisdisconnected($P);
+ }
+;
+
+/* TP4: ignore resets */
+SAME <== [ TP_OPEN, TP_CRSENT, TP_AKWAIT,
+ TP_CLOSING, TP_LISTENING ] T_NETRESET
+ DEFAULT
+ NULLACTION
+;
+
+/* applicable in TP4, TP0 */
+SAME <== [ TP_CLOSED, TP_REFWAIT ] T_NETRESET
+ DEFAULT
+ NULLACTION
+;
+
+/* C'EST TOUT */
diff --git a/sys/netiso/tp_astring.c b/sys/netiso/tp_astring.c
new file mode 100644
index 0000000..af08ceb
--- /dev/null
+++ b/sys/netiso/tp_astring.c
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_astring.c 8.1 (Berkeley) 6/10/93
+ */
+
+char *tp_sstring[] = {
+"ST_ERROR(0x0)",
+"TP_CLOSED(0x1)",
+"TP_CRSENT(0x2)",
+"TP_AKWAIT(0x3)",
+"TP_OPEN(0x4)",
+"TP_CLOSING(0x5)",
+"TP_REFWAIT(0x6)",
+"TP_LISTENING(0x7)",
+"TP_CONFIRMING(0x8)",
+};
+
+char *tp_estring[] = {
+"TM_inact(0x0)",
+"TM_retrans(0x1)",
+"TM_sendack(0x2)",
+"TM_notused(0x3)",
+"TM_reference(0x4)",
+"TM_data_retrans(0x5)",
+"ER_TPDU(0x6)",
+"CR_TPDU(0x7)",
+"DR_TPDU(0x8)",
+"DC_TPDU(0x9)",
+"CC_TPDU(0xa)",
+"AK_TPDU(0xb)",
+"DT_TPDU(0xc)",
+"XPD_TPDU(0xd)",
+"XAK_TPDU(0xe)",
+"T_CONN_req(0xf)",
+"T_DISC_req(0x10)",
+"T_LISTEN_req(0x11)",
+"T_DATA_req(0x12)",
+"T_XPD_req(0x13)",
+"T_USR_rcvd(0x14)",
+"T_USR_Xrcvd(0x15)",
+"T_DETACH(0x16)",
+"T_NETRESET(0x17)",
+"T_ACPT_req(0x18)",
+};
diff --git a/sys/netiso/tp_clnp.h b/sys/netiso/tp_clnp.h
new file mode 100644
index 0000000..81a7cff
--- /dev/null
+++ b/sys/netiso/tp_clnp.h
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_clnp.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_clnp.h,v 5.1 88/10/12 12:16:36 root Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_clnp.h,v $
+ *
+ * AF_ISO net-dependent structures and include files
+ *
+ */
+
+
+#ifndef __TP_CLNP__
+#define __TP_CLNP__
+
+#ifndef SOCK_STREAM
+#include <sys/socket.h>
+#endif /* SOCK_STREAM */
+
+#ifndef RTFREE
+#include <net/route.h>
+#endif
+#include <netiso/iso.h>
+#include <netiso/clnp.h>
+#include <netiso/iso_pcb.h>
+#ifndef IF_DEQUEUE
+#include <net/if.h>
+#endif
+#include <netiso/iso_var.h>
+
+struct isopcb tp_isopcb;
+ /* queue of active inpcbs for tp ; for tp with dod ip */
+
+#endif /* __TP_CLNP__ */
diff --git a/sys/netiso/tp_cons.c b/sys/netiso/tp_cons.c
new file mode 100644
index 0000000..797ee9e
--- /dev/null
+++ b/sys/netiso/tp_cons.c
@@ -0,0 +1,308 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_cons.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ * $Header: tp_cons.c,v 5.6 88/11/18 17:27:13 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_cons.c,v $
+ *
+ * Here is where you find the iso- and cons-dependent code. We've tried
+ * keep all net-level and (primarily) address-family-dependent stuff
+ * out of the tp source, and everthing here is reached indirectly
+ * through a switch table (struct nl_protosw *) tpcb->tp_nlproto
+ * (see tp_pcb.c).
+ * The routines here are:
+ * tpcons_input: pullup and call tp_input w/ correct arguments
+ * tpcons_output: package a pkt for cons given an isopcb & some data
+ * cons_chan_to_tpcb: find a tpcb based on the channel #
+ */
+
+#ifdef ISO
+#ifdef TPCONS
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/domain.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netiso/tp_param.h>
+#include <netiso/argo_debug.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/tp_trace.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_tpdu.h>
+#include <netiso/iso.h>
+#include <netiso/iso_errno.h>
+#include <netiso/iso_pcb.h>
+#include <netiso/cons.h>
+#include <netiso/tp_seq.h>
+
+#undef FALSE
+#undef TRUE
+#include <netccitt/x25.h>
+#include <netccitt/pk.h>
+#include <netccitt/pk_var.h>
+
+#include <netiso/if_cons.c>
+int tpcons_output();
+
+/*
+ * CALLED FROM:
+ * tp_route_to() for PRU_CONNECT
+ * FUNCTION, ARGUMENTS, SIDE EFFECTS and RETURN VALUE:
+ * version of the previous procedure for X.25
+ */
+
+tpcons_pcbconnect(isop, nam)
+struct isopcb *isop;
+register struct mbuf *nam;
+{
+ int error;
+ if (error = iso_pcbconnect(isop, nam))
+ return error;
+ if ((isop->isop_chan = (caddr_t) pk_attach((struct socket *)0)) == 0) {
+ IFDEBUG(D_CCONS)
+ printf("tpcons_pcbconnect: no pklcd; returns 0x%x\n", error);
+ ENDDEBUG
+ return ENOBUFS;
+ }
+ if (error = cons_connect(isop)) { /* if it doesn't work */
+ /* oh, dear, throw packet away */
+ pk_disconnect((struct pklcd *)isop->isop_chan);
+ isop->isop_chan = 0;
+ } else
+ isop->isop_refcnt = 1;
+ return error;
+}
+
+
+/*
+ * CALLED FROM:
+ * cons
+ * FUNCTION and ARGUMENTS:
+ * THIS MAYBE BELONGS IN SOME OTHER PLACE??? but i think not -
+ */
+ProtoHook
+tpcons_ctlinput(cmd, siso, isop)
+ int cmd;
+ struct sockaddr_iso *siso;
+ struct isopcb *isop;
+{
+ register struct tp_pcb *tpcb = 0;
+
+ if (isop->isop_socket)
+ tpcb = (struct tp_pcb *)isop->isop_socket->so_pcb;
+ switch (cmd) {
+
+ case PRC_CONS_SEND_DONE:
+ if (tpcb) {
+ struct tp_event E;
+ int error = 0;
+
+ if (tpcb->tp_class == TP_CLASS_0) {
+ /* only if class is exactly class zero, not
+ * still in class negotiation
+ */
+ /* fake an ack */
+ register SeqNum seq = SEQ_ADD(tpcb, tpcb->tp_snduna, 1);
+
+ IFTRACE(D_DATA)
+ tptrace(TPPTmisc, "FAKE ACK seq cdt 1",
+ seq, 0,0,0);
+ ENDTRACE
+ IFDEBUG(D_DATA)
+ printf("FAKE ACK seq 0x%x cdt 1\n", seq );
+ ENDDEBUG
+ E.ATTR(AK_TPDU).e_cdt = 1;
+ E.ATTR(AK_TPDU).e_seq = seq;
+ E.ATTR(AK_TPDU).e_subseq = 0;
+ E.ATTR(AK_TPDU).e_fcc_present = 0;
+ error = DoEvent(AK_TPDU);
+ if( error ) {
+ tpcb->tp_sock->so_error = error;
+ }
+ } /* else ignore it */
+ }
+ break;
+ case PRC_ROUTEDEAD:
+ if (tpcb && tpcb->tp_class == TP_CLASS_0) {
+ tpiso_reset(isop);
+ break;
+ } /* else drop through */
+ default:
+ (void) tpclnp_ctlinput(cmd, siso);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * CALLED FROM:
+ * cons's intr routine
+ * FUNCTION and ARGUMENTS:
+ * Take a packet (m) from cons, pullup m as required by tp,
+ * ignore the socket argument, and call tp_input.
+ * No return value.
+ */
+ProtoHook
+tpcons_input(m, faddr, laddr, channel)
+ struct mbuf *m;
+ struct sockaddr_iso *faddr, *laddr;
+ caddr_t channel;
+{
+ if( m == MNULL)
+ return 0;
+
+ m = (struct mbuf *)tp_inputprep(m);
+
+ IFDEBUG(D_TPINPUT)
+ printf("tpcons_input before tp_input(m 0x%x)\n", m);
+ dump_buf( m, 12+ m->m_len);
+ ENDDEBUG
+ tp_input(m, faddr, laddr, channel, tpcons_output, 0);
+ return 0;
+}
+
+
+/*
+ * CALLED FROM:
+ * tp_emit()
+ * FUNCTION and ARGUMENTS:
+ * Take a packet(m0) from tp and package it so that cons will accept it.
+ * This means filling in a few of the fields.
+ * inp is the isopcb structure; datalen is the length of the data in the
+ * mbuf string m0.
+ * RETURN VALUE:
+ * whatever (E*) is returned form the net layer output routine.
+ */
+
+int
+tpcons_output(isop, m0, datalen, nochksum)
+ struct isopcb *isop;
+ struct mbuf *m0;
+ int datalen;
+ int nochksum;
+{
+ register struct mbuf *m = m0;
+ int error;
+
+ IFDEBUG(D_EMIT)
+ printf(
+ "tpcons_output(isop 0x%x, m 0x%x, len 0x%x socket 0x%x\n",
+ isop, m0, datalen, isop->isop_socket);
+ ENDDEBUG
+ if (m == MNULL)
+ return 0;
+ if ((m->m_flags & M_PKTHDR) == 0) {
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ return ENOBUFS;
+ m->m_next = m0;
+ }
+ m->m_pkthdr.len = datalen;
+ if (isop->isop_chan == 0) {
+ /* got a restart maybe? */
+ if ((isop->isop_chan = (caddr_t) pk_attach((struct socket *)0)) == 0) {
+ IFDEBUG(D_CCONS)
+ printf("tpcons_output: no pklcd\n");
+ ENDDEBUG
+ error = ENOBUFS;
+ }
+ if (error = cons_connect(isop)) {
+ pk_disconnect((struct pklcd *)isop->isop_chan);
+ isop->isop_chan = 0;
+ IFDEBUG(D_CCONS)
+ printf("tpcons_output: can't reconnect\n");
+ ENDDEBUG
+ }
+ } else {
+ error = pk_send(isop->isop_chan, m);
+ IncStat(ts_tpdu_sent);
+ }
+ return error;
+}
+/*
+ * CALLED FROM:
+ * tp_error_emit()
+ * FUNCTION and ARGUMENTS:
+ * Take a packet(m0) from tp and package it so that cons will accept it.
+ * chan is the cons channel to use; datalen is the length of the data in the
+ * mbuf string m0.
+ * RETURN VALUE:
+ * whatever (E*) is returned form the net layer output routine.
+ */
+
+int
+tpcons_dg_output(chan, m0, datalen)
+ caddr_t chan;
+ struct mbuf *m0;
+ int datalen;
+{
+ return tpcons_output(((struct pklcd *)chan)->lcd_upnext, m0, datalen, 0);
+}
+#endif /* TPCONS */
+#endif /* ISO */
diff --git a/sys/netiso/tp_driver.c b/sys/netiso/tp_driver.c
new file mode 100644
index 0000000..586ef4e
--- /dev/null
+++ b/sys/netiso/tp_driver.c
@@ -0,0 +1,999 @@
+/* $Header$ */
+/* $Source$ */
+#ifndef lint
+static char *rcsid = "$Header/**/$";
+#endif lint
+#define _XEBEC_PG static
+
+#include "tp_states.h"
+
+static struct act_ent {
+ int a_newstate;
+ int a_action;
+} statetable[] = { {0,0},
+#include "tp_states.init"
+};
+
+/* @(#)tp.trans 8.1 (Berkeley) 6/10/93 */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/mbuf.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+
+#include <netiso/tp_param.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/tp_tpdu.h>
+#include <netiso/argo_debug.h>
+#include <netiso/tp_trace.h>
+#include <netiso/iso_errno.h>
+#include <netiso/tp_seq.h>
+#include <netiso/cons.h>
+
+#define DRIVERTRACE TPPTdriver
+#define sbwakeup(sb) sowakeup(p->tp_sock, sb);
+#define MCPY(d, w) (d ? m_copym(d, 0, (int)M_COPYALL, w): 0)
+
+static trick_hc = 1;
+
+int tp_emit(),
+ tp_goodack(), tp_goodXack(),
+ tp_stash()
+;
+void tp_indicate(), tp_getoptions(),
+ tp_soisdisconnecting(), tp_soisdisconnected(),
+ tp_recycle_tsuffix(),
+#ifdef TP_DEBUG_TIMERS
+ tp_etimeout(), tp_euntimeout(),
+ tp_ctimeout(), tp_cuntimeout(),
+ tp_ctimeout_MIN(),
+#endif
+ tp_freeref(), tp_detach(),
+ tp0_stash(), tp0_send(),
+ tp_netcmd(), tp_send()
+;
+
+typedef struct tp_pcb tpcb_struct;
+
+
+
+typedef tpcb_struct tp_PCB_;
+
+#include "tp_events.h"
+
+_XEBEC_PG int _Xebec_action(a,e,p)
+int a;
+struct tp_event *e;
+tp_PCB_ *p;
+{
+switch(a) {
+case -1: return tp_protocol_error(e,p);
+case 0x1:
+ {
+ (void) tp_emit(DC_TPDU_type, p, 0, 0, MNULL);
+ }
+ break;
+case 0x2:
+ {
+# ifdef TP_DEBUG
+ if( e->ev_number != AK_TPDU )
+ printf("TPDU 0x%x in REFWAIT!!!!\n", e->ev_number);
+# endif TP_DEBUG
+ }
+ break;
+case 0x3:
+ {
+ /* oh, man is this grotesque or what? */
+ (void) tp_goodack(p, e->ev_union.EV_AK_TPDU.e_cdt, e->ev_union.EV_AK_TPDU.e_seq, e->ev_union.EV_AK_TPDU.e_subseq);
+ /* but it's necessary because this pseudo-ack may happen
+ * before the CC arrives, but we HAVE to adjust the
+ * snduna as a result of the ack, WHENEVER it arrives
+ */
+ }
+ break;
+case 0x4:
+ {
+ tp_detach(p);
+ }
+ break;
+case 0x5:
+ {
+ p->tp_refstate = REF_OPEN; /* has timers ??? */
+ }
+ break;
+case 0x6:
+ {
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc, "CR datalen data", e->ev_union.EV_CR_TPDU.e_datalen, e->ev_union.EV_CR_TPDU.e_data,0,0);
+ ENDTRACE
+ IFDEBUG(D_CONN)
+ printf("CR datalen 0x%x data 0x%x", e->ev_union.EV_CR_TPDU.e_datalen, e->ev_union.EV_CR_TPDU.e_data);
+ ENDDEBUG
+ p->tp_refstate = REF_OPEN; /* has timers */
+ p->tp_fcredit = e->ev_union.EV_CR_TPDU.e_cdt;
+
+ if (e->ev_union.EV_CR_TPDU.e_datalen > 0) {
+ /* n/a for class 0 */
+ ASSERT(p->tp_Xrcv.sb_cc == 0);
+ sbappendrecord(&p->tp_Xrcv, e->ev_union.EV_CR_TPDU.e_data);
+ e->ev_union.EV_CR_TPDU.e_data = MNULL;
+ }
+ }
+ break;
+case 0x7:
+ {
+ IncStat(ts_tp0_conn);
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc, "Confiming", p, 0,0,0);
+ ENDTRACE
+ IFDEBUG(D_CONN)
+ printf("Confirming connection: p" );
+ ENDDEBUG
+ soisconnected(p->tp_sock);
+ (void) tp_emit(CC_TPDU_type, p, 0,0, MNULL) ;
+ p->tp_fcredit = 1;
+ }
+ break;
+case 0x8:
+ {
+ IncStat(ts_tp4_conn); /* even though not quite open */
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc, "Confiming", p, 0,0,0);
+ ENDTRACE
+ IFDEBUG(D_CONN)
+ printf("Confirming connection: p" );
+ ENDDEBUG
+ tp_getoptions(p);
+ soisconnecting(p->tp_sock);
+ if ((p->tp_rx_strat & TPRX_FASTSTART) && (p->tp_fcredit > 0))
+ p->tp_cong_win = p->tp_fcredit * p->tp_l_tpdusize;
+ p->tp_retrans = p->tp_Nretrans;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_cc_ticks);
+ }
+ break;
+case 0x9:
+ {
+ IFDEBUG(D_CONN)
+ printf("event: CR_TPDU emit CC failed done " );
+ ENDDEBUG
+ soisdisconnected(p->tp_sock);
+ tp_recycle_tsuffix(p);
+ tp_freeref(p->tp_lref);
+ tp_detach(p);
+ }
+ break;
+case 0xa:
+ {
+ int error;
+ struct mbuf *data = MNULL;
+
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)p->tp_flags,
+ p->tp_ucddata, 0, 0);
+ ENDTRACE
+ data = MCPY(p->tp_ucddata, M_WAIT);
+ if (data) {
+ IFDEBUG(D_CONN)
+ printf("T_CONN_req.trans m_copy cc 0x%x\n",
+ p->tp_ucddata);
+ dump_mbuf(data, "sosnd @ T_CONN_req");
+ ENDDEBUG
+ }
+
+ if (error = tp_emit(CR_TPDU_type, p, 0, 0, data) )
+ return error; /* driver WON'T change state; will return error */
+
+ p->tp_refstate = REF_OPEN; /* has timers */
+ if(p->tp_class != TP_CLASS_0) {
+ p->tp_retrans = p->tp_Nretrans;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_cr_ticks);
+ }
+ }
+ break;
+case 0xb:
+ {
+ sbflush(&p->tp_Xrcv); /* purge non-delivered data data */
+ if (e->ev_union.EV_DR_TPDU.e_datalen > 0) {
+ sbappendrecord(&p->tp_Xrcv, e->ev_union.EV_DR_TPDU.e_data);
+ e->ev_union.EV_DR_TPDU.e_data = MNULL;
+ }
+ if (p->tp_state == TP_OPEN)
+ tp_indicate(T_DISCONNECT, p, 0);
+ else {
+ int so_error = ECONNREFUSED;
+ if (e->ev_union.EV_DR_TPDU.e_reason != (E_TP_NO_SESSION ^ TP_ERROR_MASK) &&
+ e->ev_union.EV_DR_TPDU.e_reason != (E_TP_NO_CR_ON_NC ^ TP_ERROR_MASK) &&
+ e->ev_union.EV_DR_TPDU.e_reason != (E_TP_REF_OVERFLOW ^ TP_ERROR_MASK))
+ so_error = ECONNABORTED;
+ tp_indicate(T_DISCONNECT, p, so_error);
+ }
+ tp_soisdisconnected(p);
+ if (p->tp_class != TP_CLASS_0) {
+ if (p->tp_state == TP_OPEN ) {
+ tp_euntimeout(p, TM_data_retrans); /* all */
+ tp_cuntimeout(p, TM_retrans);
+ tp_cuntimeout(p, TM_inact);
+ tp_cuntimeout(p, TM_sendack);
+ p->tp_flags &= ~TPF_DELACK;
+ }
+ tp_cuntimeout(p, TM_retrans);
+ if( e->ev_union.EV_DR_TPDU.e_sref != 0 )
+ (void) tp_emit(DC_TPDU_type, p, 0, 0, MNULL);
+ }
+ }
+ break;
+case 0xc:
+ {
+ if( e->ev_union.EV_DR_TPDU.e_sref != 0 )
+ (void) tp_emit(DC_TPDU_type, p, 0, 0, MNULL);
+ /* reference timer already set - reset it to be safe (???) */
+ tp_euntimeout(p, TM_reference); /* all */
+ tp_etimeout(p, TM_reference, (int)p->tp_refer_ticks);
+ }
+ break;
+case 0xd:
+ {
+ tp_cuntimeout(p, TM_retrans);
+ tp_indicate(ER_TPDU, p, e->ev_union.EV_ER_TPDU.e_reason);
+ tp_soisdisconnected(p);
+ }
+ break;
+case 0xe:
+ {
+ tp_cuntimeout(p, TM_retrans);
+ tp_soisdisconnected(p);
+ }
+ break;
+case 0xf:
+ {
+ tp_indicate(ER_TPDU, p, e->ev_union.EV_ER_TPDU.e_reason);
+ tp_cuntimeout(p, TM_retrans);
+ tp_soisdisconnected(p);
+ }
+ break;
+case 0x10:
+ {
+ tp_cuntimeout(p, TM_retrans);
+ tp_soisdisconnected(p);
+ }
+ break;
+case 0x11:
+ { /* don't ask me why we have to do this - spec says so */
+ (void) tp_emit(DR_TPDU_type, p, 0, E_TP_NO_SESSION, MNULL);
+ /* don't bother with retransmissions of the DR */
+ }
+ break;
+case 0x12:
+ {
+ tp_soisdisconnecting(p->tp_sock);
+ tp_indicate(ER_TPDU, p, e->ev_union.EV_ER_TPDU.e_reason);
+ tp_soisdisconnected(p);
+ tp_netcmd( p, CONN_CLOSE );
+ }
+ break;
+case 0x13:
+ {
+ if (p->tp_state == TP_OPEN) {
+ tp_euntimeout(p, TM_data_retrans); /* all */
+ tp_cuntimeout(p, TM_inact);
+ tp_cuntimeout(p, TM_sendack);
+ }
+ tp_soisdisconnecting(p->tp_sock);
+ tp_indicate(ER_TPDU, p, e->ev_union.EV_ER_TPDU.e_reason);
+ p->tp_retrans = p->tp_Nretrans;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_dr_ticks);
+ (void) tp_emit(DR_TPDU_type, p, 0, E_TP_PROTO_ERR, MNULL);
+ }
+ break;
+case 0x14:
+ {
+ tp_cuntimeout(p, TM_retrans);
+ IncStat(ts_tp0_conn);
+ p->tp_fcredit = 1;
+ soisconnected(p->tp_sock);
+ }
+ break;
+case 0x15:
+ {
+ IFDEBUG(D_CONN)
+ printf("trans: CC_TPDU in CRSENT state flags 0x%x\n",
+ (int)p->tp_flags);
+ ENDDEBUG
+ IncStat(ts_tp4_conn);
+ p->tp_fref = e->ev_union.EV_CC_TPDU.e_sref;
+ p->tp_fcredit = e->ev_union.EV_CC_TPDU.e_cdt;
+ if ((p->tp_rx_strat & TPRX_FASTSTART) && (e->ev_union.EV_CC_TPDU.e_cdt > 0))
+ p->tp_cong_win = e->ev_union.EV_CC_TPDU.e_cdt * p->tp_l_tpdusize;
+ tp_getoptions(p);
+ tp_cuntimeout(p, TM_retrans);
+ if (p->tp_ucddata) {
+ IFDEBUG(D_CONN)
+ printf("dropping user connect data cc 0x%x\n",
+ p->tp_ucddata->m_len);
+ ENDDEBUG
+ m_freem(p->tp_ucddata);
+ p->tp_ucddata = 0;
+ }
+ soisconnected(p->tp_sock);
+ if (e->ev_union.EV_CC_TPDU.e_datalen > 0) {
+ ASSERT(p->tp_Xrcv.sb_cc == 0); /* should be empty */
+ sbappendrecord(&p->tp_Xrcv, e->ev_union.EV_CC_TPDU.e_data);
+ e->ev_union.EV_CC_TPDU.e_data = MNULL;
+ }
+
+ (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, MNULL);
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ }
+ break;
+case 0x16:
+ {
+ struct mbuf *data = MNULL;
+ int error;
+
+ IncStat(ts_retrans_cr);
+ p->tp_cong_win = 1 * p->tp_l_tpdusize;
+ data = MCPY(p->tp_ucddata, M_NOWAIT);
+ if(p->tp_ucddata) {
+ IFDEBUG(D_CONN)
+ printf("TM_retrans.trans m_copy cc 0x%x\n", data);
+ dump_mbuf(p->tp_ucddata, "sosnd @ TM_retrans");
+ ENDDEBUG
+ if( data == MNULL )
+ return ENOBUFS;
+ }
+
+ p->tp_retrans --;
+ if( error = tp_emit(CR_TPDU_type, p, 0, 0, data) ) {
+ p->tp_sock->so_error = error;
+ }
+ tp_ctimeout(p, TM_retrans, (int)p->tp_cr_ticks);
+ }
+ break;
+case 0x17:
+ {
+ IncStat(ts_conn_gaveup);
+ p->tp_sock->so_error = ETIMEDOUT;
+ tp_indicate(T_DISCONNECT, p, ETIMEDOUT);
+ tp_soisdisconnected(p);
+ }
+ break;
+case 0x18:
+ {
+ int error;
+ struct mbuf *data = MCPY(p->tp_ucddata, M_WAIT);
+
+ if( error = tp_emit(CC_TPDU_type, p, 0, 0, data) ) {
+ p->tp_sock->so_error = error;
+ }
+ p->tp_retrans = p->tp_Nretrans;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_cc_ticks);
+ }
+ break;
+case 0x19:
+ {
+ int doack;
+
+ /*
+ * Get rid of any confirm or connect data, so that if we
+ * crash or close, it isn't thought of as disconnect data.
+ */
+ if (p->tp_ucddata) {
+ m_freem(p->tp_ucddata);
+ p->tp_ucddata = 0;
+ }
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ tp_cuntimeout(p, TM_retrans);
+ soisconnected(p->tp_sock);
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+
+ /* see also next 2 transitions, if you make any changes */
+
+ doack = tp_stash(p, e);
+ IFDEBUG(D_DATA)
+ printf("tp_stash returns %d\n",doack);
+ ENDDEBUG
+
+ if (doack) {
+ (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, MNULL );
+ tp_ctimeout(p, TM_sendack, (int)p->tp_keepalive_ticks);
+ } else
+ tp_ctimeout( p, TM_sendack, (int)p->tp_sendack_ticks);
+
+ IFDEBUG(D_DATA)
+ printf("after stash calling sbwakeup\n");
+ ENDDEBUG
+ }
+ break;
+case 0x1a:
+ {
+ tp0_stash(p, e);
+ sbwakeup( &p->tp_sock->so_rcv );
+
+ IFDEBUG(D_DATA)
+ printf("after stash calling sbwakeup\n");
+ ENDDEBUG
+ }
+ break;
+case 0x1b:
+ {
+ int doack; /* tells if we must ack immediately */
+
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ sbwakeup( &p->tp_sock->so_rcv );
+
+ doack = tp_stash(p, e);
+ IFDEBUG(D_DATA)
+ printf("tp_stash returns %d\n",doack);
+ ENDDEBUG
+
+ if(doack)
+ (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, MNULL );
+ else
+ tp_ctimeout_MIN( p, TM_sendack, (int)p->tp_sendack_ticks);
+
+ IFDEBUG(D_DATA)
+ printf("after stash calling sbwakeup\n");
+ ENDDEBUG
+ }
+ break;
+case 0x1c:
+ {
+ IFTRACE(D_DATA)
+ tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ",
+ e->ev_union.EV_DT_TPDU.e_seq, p->tp_rcvnxt, p->tp_lcredit, 0);
+ ENDTRACE
+ IncStat(ts_dt_niw);
+ m_freem(e->ev_union.EV_DT_TPDU.e_data);
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, MNULL );
+ }
+ break;
+case 0x1d:
+ {
+ if (p->tp_ucddata) {
+ m_freem(p->tp_ucddata);
+ p->tp_ucddata = 0;
+ }
+ (void) tp_goodack(p, e->ev_union.EV_AK_TPDU.e_cdt, e->ev_union.EV_AK_TPDU.e_seq, e->ev_union.EV_AK_TPDU.e_subseq);
+ tp_cuntimeout(p, TM_retrans);
+
+ soisconnected(p->tp_sock);
+ IFTRACE(D_CONN)
+ struct socket *so = p->tp_sock;
+ tptrace(TPPTmisc,
+ "called sosiconn: so so_state rcv.sb_sel rcv.sb_flags",
+ so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags);
+ tptrace(TPPTmisc,
+ "called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head",
+ so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head);
+ ENDTRACE
+
+ tp_ctimeout(p, TM_sendack, (int)p->tp_keepalive_ticks);
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ }
+ break;
+case 0x1e:
+ {
+ if( p->tp_state == TP_AKWAIT ) {
+ if (p->tp_ucddata) {
+ m_freem(p->tp_ucddata);
+ p->tp_ucddata = 0;
+ }
+ tp_cuntimeout(p, TM_retrans);
+ soisconnected(p->tp_sock);
+ tp_ctimeout(p, TM_sendack, (int)p->tp_keepalive_ticks);
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ }
+ IFTRACE(D_XPD)
+ tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n",
+ p->tp_Xrcvnxt,e->ev_union.EV_XPD_TPDU.e_seq, e->ev_union.EV_XPD_TPDU.e_datalen, e->ev_union.EV_XPD_TPDU.e_data->m_len);
+ ENDTRACE
+
+ p->tp_sock->so_state |= SS_RCVATMARK;
+ e->ev_union.EV_XPD_TPDU.e_data->m_flags |= M_EOR;
+ sbinsertoob(&p->tp_Xrcv, e->ev_union.EV_XPD_TPDU.e_data);
+ IFDEBUG(D_XPD)
+ dump_mbuf(e->ev_union.EV_XPD_TPDU.e_data, "XPD TPDU: tp_Xrcv");
+ ENDDEBUG
+ tp_indicate(T_XDATA, p, 0);
+ sbwakeup( &p->tp_Xrcv );
+
+ (void) tp_emit(XAK_TPDU_type, p, p->tp_Xrcvnxt, 0, MNULL);
+ SEQ_INC(p, p->tp_Xrcvnxt);
+ }
+ break;
+case 0x1f:
+ {
+ if( p->tp_Xrcv.sb_cc == 0 ) {
+ /* kludge for select(): */
+ /* p->tp_sock->so_state &= ~SS_OOBAVAIL; */
+ }
+ }
+ break;
+case 0x20:
+ {
+ IFTRACE(D_XPD)
+ tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n",
+ p->tp_Xrcvnxt, e->ev_union.EV_XPD_TPDU.e_seq, p->tp_Xrcv.sb_cc , 0);
+ ENDTRACE
+ if( p->tp_Xrcvnxt != e->ev_union.EV_XPD_TPDU.e_seq )
+ IncStat(ts_xpd_niw);
+ if( p->tp_Xrcv.sb_cc ) {
+ /* might as well kick 'em again */
+ tp_indicate(T_XDATA, p, 0);
+ IncStat(ts_xpd_dup);
+ }
+ m_freem(e->ev_union.EV_XPD_TPDU.e_data);
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ /* don't send an xack because the xak gives "last one received", not
+ * "next one i expect" (dumb)
+ */
+ }
+ break;
+case 0x21:
+ {
+ struct socket *so = p->tp_sock;
+
+ /* detach from parent socket so it can finish closing */
+ if (so->so_head) {
+ if (!soqremque(so, 0) && !soqremque(so, 1))
+ panic("tp: T_DETACH");
+ so->so_head = 0;
+ }
+ tp_soisdisconnecting(p->tp_sock);
+ tp_netcmd( p, CONN_CLOSE);
+ tp_soisdisconnected(p);
+ }
+ break;
+case 0x22:
+ {
+ struct socket *so = p->tp_sock;
+ struct mbuf *data = MNULL;
+
+ /* detach from parent socket so it can finish closing */
+ if (so->so_head) {
+ if (!soqremque(so, 0) && !soqremque(so, 1))
+ panic("tp: T_DETACH");
+ so->so_head = 0;
+ }
+ if (p->tp_state != TP_CLOSING) {
+ tp_soisdisconnecting(p->tp_sock);
+ data = MCPY(p->tp_ucddata, M_NOWAIT);
+ (void) tp_emit(DR_TPDU_type, p, 0, E_TP_NORMAL_DISC, data);
+ p->tp_retrans = p->tp_Nretrans;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_dr_ticks);
+ }
+ }
+ break;
+case 0x23:
+ {
+ tp_soisdisconnecting(p->tp_sock);
+ tp_netcmd( p, CONN_CLOSE);
+ tp_soisdisconnected(p);
+ }
+ break;
+case 0x24:
+ {
+ struct mbuf *data = MCPY(p->tp_ucddata, M_WAIT);
+
+ if(p->tp_state == TP_OPEN) {
+ tp_euntimeout(p, TM_data_retrans); /* all */
+ tp_cuntimeout(p, TM_inact);
+ tp_cuntimeout(p, TM_sendack);
+ p->tp_flags &= ~TPF_DELACK;
+ }
+ if (data) {
+ IFDEBUG(D_CONN)
+ printf("T_DISC_req.trans tp_ucddata 0x%x\n",
+ p->tp_ucddata);
+ dump_mbuf(data, "ucddata @ T_DISC_req");
+ ENDDEBUG
+ }
+ tp_soisdisconnecting(p->tp_sock);
+ p->tp_retrans = p->tp_Nretrans;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_dr_ticks);
+
+ if( trick_hc )
+ return tp_emit(DR_TPDU_type, p, 0, e->ev_union.EV_T_DISC_req.e_reason, data);
+ }
+ break;
+case 0x25:
+ {
+ int error;
+ struct mbuf *data = MCPY(p->tp_ucddata, M_WAIT);
+
+ IncStat(ts_retrans_cc);
+ p->tp_retrans --;
+ p->tp_cong_win = 1 * p->tp_l_tpdusize;
+
+ if( error = tp_emit(CC_TPDU_type, p, 0, 0, data) )
+ p->tp_sock->so_error = error;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_cc_ticks);
+ }
+ break;
+case 0x26:
+ {
+ IncStat(ts_conn_gaveup);
+ tp_soisdisconnecting(p->tp_sock);
+ p->tp_sock->so_error = ETIMEDOUT;
+ tp_indicate(T_DISCONNECT, p, ETIMEDOUT);
+ (void) tp_emit(DR_TPDU_type, p, 0, E_TP_CONGEST, MNULL);
+ p->tp_retrans = p->tp_Nretrans;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_dr_ticks);
+ }
+ break;
+case 0x27:
+ {
+ tp_euntimeout(p, TM_data_retrans); /* all */
+ tp_cuntimeout(p, TM_inact);
+ tp_cuntimeout(p, TM_sendack);
+
+ IncStat(ts_conn_gaveup);
+ tp_soisdisconnecting(p->tp_sock);
+ p->tp_sock->so_error = ETIMEDOUT;
+ tp_indicate(T_DISCONNECT, p, ETIMEDOUT);
+ (void) tp_emit(DR_TPDU_type, p, 0, E_TP_CONGEST_2, MNULL);
+ p->tp_retrans = p->tp_Nretrans;
+ tp_ctimeout(p, TM_retrans, (int)p->tp_dr_ticks);
+ }
+ break;
+case 0x28:
+ {
+ p->tp_cong_win = 1 * p->tp_l_tpdusize;
+ /* resume XPD */
+ if ( p->tp_Xsnd.sb_mb ) {
+ struct mbuf *m = m_copy(p->tp_Xsnd.sb_mb, 0, (int)p->tp_Xsnd.sb_cc);
+ int shift;
+
+ IFTRACE(D_XPD)
+ tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndnxt snduna",
+ p->tp_Xuna, p->tp_Xsndnxt, p->tp_sndnxt,
+ p->tp_snduna);
+ ENDTRACE
+ IFDEBUG(D_XPD)
+ dump_mbuf(m, "XPD retrans emitting M");
+ ENDDEBUG
+ IncStat(ts_retrans_xpd);
+ p->tp_retrans --;
+ shift = max(p->tp_Nretrans - p->tp_retrans, 6);
+ (void) tp_emit(XPD_TPDU_type, p, p->tp_Xuna, 1, m);
+ tp_ctimeout(p, TM_retrans, ((int)p->tp_dt_ticks) << shift);
+ }
+ }
+ break;
+case 0x29:
+ {
+ p->tp_rxtshift++;
+ (void) tp_data_retrans(p);
+ }
+ break;
+case 0x2a:
+ {
+ p->tp_retrans --;
+ (void) tp_emit(DR_TPDU_type, p, 0, E_TP_DR_NO_REAS, MNULL);
+ IncStat(ts_retrans_dr);
+ tp_ctimeout(p, TM_retrans, (int)p->tp_dr_ticks);
+ }
+ break;
+case 0x2b:
+ {
+ p->tp_sock->so_error = ETIMEDOUT;
+ p->tp_refstate = REF_FROZEN;
+ tp_recycle_tsuffix( p );
+ tp_etimeout(p, TM_reference, (int)p->tp_refer_ticks);
+ }
+ break;
+case 0x2c:
+ {
+ tp_freeref(p->tp_lref);
+ tp_detach(p);
+ }
+ break;
+case 0x2d:
+ {
+ if( p->tp_class != TP_CLASS_0) {
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ if ( e->ev_number == CC_TPDU )
+ (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, MNULL);
+ }
+ /* ignore it if class 0 - state tables are blank for this */
+ }
+ break;
+case 0x2e:
+ {
+ IFTRACE(D_DATA)
+ tptrace(TPPTmisc, "T_DATA_req sndnxt snduna fcredit, tpcb",
+ p->tp_sndnxt, p->tp_snduna, p->tp_fcredit, p);
+ ENDTRACE
+
+ tp_send(p);
+ }
+ break;
+case 0x2f:
+ {
+ int error = 0;
+
+ /* resume XPD */
+ if ( p->tp_Xsnd.sb_mb ) {
+ struct mbuf *m = m_copy(p->tp_Xsnd.sb_mb, 0, (int)p->tp_Xsnd.sb_cc);
+ /* m_copy doesn't preserve the m_xlink field, but at this pt.
+ * that doesn't matter
+ */
+
+ IFTRACE(D_XPD)
+ tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndnxt snduna",
+ p->tp_Xuna, p->tp_Xsndnxt, p->tp_sndnxt,
+ p->tp_snduna);
+ ENDTRACE
+ IFDEBUG(D_XPD)
+ printf("T_XPD_req: sb_cc 0x%x\n", p->tp_Xsnd.sb_cc);
+ dump_mbuf(m, "XPD req emitting M");
+ ENDDEBUG
+ error =
+ tp_emit(XPD_TPDU_type, p, p->tp_Xuna, 1, m);
+ p->tp_retrans = p->tp_Nretrans;
+
+ tp_ctimeout(p, TM_retrans, (int)p->tp_rxtcur);
+ SEQ_INC(p, p->tp_Xsndnxt);
+ }
+ if(trick_hc)
+ return error;
+ }
+ break;
+case 0x30:
+ {
+ struct sockbuf *sb = &p->tp_sock->so_snd;
+
+ IFDEBUG(D_ACKRECV)
+ printf("GOOD ACK seq 0x%x cdt 0x%x\n", e->ev_union.EV_AK_TPDU.e_seq, e->ev_union.EV_AK_TPDU.e_cdt);
+ ENDDEBUG
+ if( p->tp_class != TP_CLASS_0) {
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ }
+ sbwakeup(sb);
+ IFDEBUG(D_ACKRECV)
+ printf("GOOD ACK new sndnxt 0x%x\n", p->tp_sndnxt);
+ ENDDEBUG
+ }
+ break;
+case 0x31:
+ {
+ IFTRACE(D_ACKRECV)
+ tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq",
+ e->ev_union.EV_AK_TPDU.e_fcc_present, p->tp_r_subseq, e->ev_union.EV_AK_TPDU.e_subseq, 0);
+ ENDTRACE
+ if( p->tp_class != TP_CLASS_0 ) {
+
+ if ( !e->ev_union.EV_AK_TPDU.e_fcc_present ) {
+ /* send ACK with FCC */
+ IncStat( ts_ackreason[_ACK_FCC_] );
+ (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 1, MNULL);
+ }
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ }
+ }
+ break;
+case 0x32:
+ {
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ tp_cuntimeout(p, TM_retrans);
+
+ sbwakeup( &p->tp_sock->so_snd );
+
+ /* resume normal data */
+ tp_send(p);
+ }
+ break;
+case 0x33:
+ {
+ IFTRACE(D_ACKRECV)
+ tptrace(TPPTmisc, "BOGUS XACK eventtype ", e->ev_number, 0, 0,0);
+ ENDTRACE
+ if( p->tp_class != TP_CLASS_0 ) {
+ tp_ctimeout(p, TM_inact, (int)p->tp_inact_ticks);
+ }
+ }
+ break;
+case 0x34:
+ {
+ int timo;
+ IFTRACE(D_TIMER)
+ tptrace(TPPTsendack, -1, p->tp_lcredit, p->tp_sent_uwe,
+ p->tp_sent_lcdt, 0);
+ ENDTRACE
+ IncPStat(p, tps_n_TMsendack);
+ (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, MNULL);
+ if (p->tp_fcredit == 0) {
+ if (p->tp_rxtshift < TP_MAXRXTSHIFT)
+ p->tp_rxtshift++;
+ timo = (p->tp_dt_ticks) << p->tp_rxtshift;
+ } else
+ timo = p->tp_sendack_ticks;
+ tp_ctimeout(p, TM_sendack, timo);
+ }
+ break;
+case 0x35:
+ {
+ if (sbspace(&p->tp_sock->so_rcv) > 0)
+ tp0_openflow(p);
+ }
+ break;
+case 0x36:
+ {
+ if( trick_hc ) {
+ SeqNum ack_thresh;
+ /*
+ * If the upper window edge has advanced a reasonable
+ * amount beyond what was known, send an ACK.
+ * A reasonable amount is 2 packets, unless the max window
+ * is only 1 or 2 packets, in which case we
+ * should send an ack for any advance in the upper window edge.
+ */
+ LOCAL_CREDIT(p);
+ ack_thresh = SEQ_SUB(p, p->tp_lcredit + p->tp_rcvnxt,
+ (p->tp_maxlcredit > 2 ? 2 : 1));
+ if (SEQ_GT(p, ack_thresh, p->tp_sent_uwe)) {
+ IncStat(ts_ackreason[_ACK_USRRCV_]);
+ p->tp_flags &= ~TPF_DELACK;
+ return tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, MNULL);
+ }
+ }
+ }
+ break;
+case 0x37:
+ {
+ if(trick_hc)
+ return ECONNABORTED;
+ }
+ break;
+case 0x38:
+ {
+ ASSERT( p->tp_state != TP_LISTENING );
+ tp_indicate(T_DISCONNECT, p, ECONNRESET);
+ tp_soisdisconnected(p);
+ }
+ break;
+ }
+return 0;
+}
+
+_XEBEC_PG int
+_Xebec_index( e,p )
+ struct tp_event *e;
+ tp_PCB_ *p;
+{
+switch( (e->ev_number<<4)+(p->tp_state) ) {
+case 0x12:
+ if ( p->tp_retrans > 0 ) return 0x1e;
+ else return 0x1f;
+case 0x13:
+ if ( p->tp_retrans > 0 ) return 0x2f;
+ else return 0x30;
+case 0x14:
+ if ( p->tp_retrans > 0 ) return 0x32;
+ else return 0x31;
+case 0x15:
+ if ( p->tp_retrans > 0 ) return 0x34;
+ else return 0x35;
+case 0x54:
+ if (p->tp_rxtshift < TP_NRETRANS) return 0x33;
+ else return 0x31;
+case 0x64:
+ if (p->tp_class == TP_CLASS_0) return 0x1a;
+ else return 0x1b;
+case 0x77:
+ if ( p->tp_class == TP_CLASS_0) return 0xd;
+ else return 0xe;
+case 0x86:
+ if ( e->ev_union.EV_DR_TPDU.e_sref != 0 ) return 0x2;
+ else return 0x3;
+case 0xa2:
+ if (p->tp_class == TP_CLASS_0) return 0x1c;
+ else return 0x1d;
+case 0xb2:
+ if (p->tp_class == TP_CLASS_0) return 0x5;
+ else return 0x0;
+case 0xb4:
+ if ( tp_goodack(p, e->ev_union.EV_AK_TPDU.e_cdt, e->ev_union.EV_AK_TPDU.e_seq, e->ev_union.EV_AK_TPDU.e_subseq) ) return 0x3a;
+ else return 0x3b;
+case 0xc3:
+ if ( IN_RWINDOW( p, e->ev_union.EV_DT_TPDU.e_seq,
+ p->tp_rcvnxt, SEQ(p, p->tp_rcvnxt + p->tp_lcredit)) ) return 0x21;
+ else return 0x24;
+case 0xc4:
+ if ( p->tp_class == TP_CLASS_0 ) return 0x22;
+ else if ( IN_RWINDOW( p, e->ev_union.EV_DT_TPDU.e_seq,
+ p->tp_rcvnxt, SEQ(p, p->tp_rcvnxt + p->tp_lcredit)) ) return 0x23;
+ else return 0x25;
+case 0xd3:
+ if (p->tp_Xrcvnxt == e->ev_union.EV_XPD_TPDU.e_seq) return 0x27;
+ else return 0x2a;
+case 0xd4:
+ if (p->tp_Xrcvnxt == e->ev_union.EV_XPD_TPDU.e_seq) return 0x27;
+ else return 0x29;
+case 0xe4:
+ if ( tp_goodXack(p, e->ev_union.EV_XAK_TPDU.e_seq) ) return 0x3c;
+ else return 0x3d;
+case 0x102:
+ if ( p->tp_class == TP_CLASS_0 ) return 0x2d;
+ else return 0x2e;
+case 0x104:
+ if ( p->tp_class == TP_CLASS_0 ) return 0x2d;
+ else return 0x2e;
+case 0x144:
+ if (p->tp_class == TP_CLASS_0) return 0x3f;
+ else return 0x40;
+case 0x162:
+ if (p->tp_class == TP_CLASS_0) return 0x2b;
+ else return 0x2c;
+case 0x172:
+ if ( p->tp_class != TP_CLASS_4 ) return 0x42;
+ else return 0x46;
+case 0x174:
+ if ( p->tp_class != TP_CLASS_4 ) return 0x42;
+ else return 0x47;
+case 0x177:
+ if ( p->tp_class != TP_CLASS_4 ) return 0x42;
+ else return 0x43;
+case 0x188:
+ if ( p->tp_class == TP_CLASS_0 ) return 0xf;
+ else if (tp_emit(CC_TPDU_type, p, 0,0, MCPY(p->tp_ucddata, M_NOWAIT)) == 0) return 0x10;
+ else return 0x11;
+default: return 0;
+} /* end switch */
+} /* _Xebec_index() */
+static int inx[26][9] = { {0,0,0,0,0,0,0,0,0,},
+ {0x0,0x0,0x0,0x0,0x31,0x0,0x0,0x0,0x0, },
+ {0x0,0x0,-1,-1,-1,-1,0x0,0x0,0x0, },
+ {0x0,0x0,0x0,0x0,0x3e,0x0,0x0,0x0,0x0, },
+ {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, },
+ {0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x0,0x0, },
+ {0x0,0x0,0x0,0x0,-1,0x0,0x0,0x0,0x0, },
+ {0x0,0x7,0x15,0x1b,-1,0x17,0x3,0xa,0x0, },
+ {0x0,0x19,0x6,0x20,0x37,0x8,0x3,-1,0x0, },
+ {0x0,0x14,0x13,0x13,0x13,0x16,-1,0xa,0x0, },
+ {0x0,0x7,0x6,0x1,0x9,0x18,0x3,0xa,0x0, },
+ {0x0,0x19,-1,0x1,0x37,0x8,0x3,0xa,0x0, },
+ {0x0,0x7,-1,0x26,-1,0x8,0x3,0xa,0x0, },
+ {0x0,0x7,0x6,-1,-1,0x8,0x3,0xa,0x0, },
+ {0x0,0x7,0x6,-1,-1,0x8,0x3,0xa,0x0, },
+ {0x0,0x7,0x6,0x1,-1,0x8,0x3,0xa,0x0, },
+ {0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0, },
+ {0x0,0x0,-1,0x2e,-1,0x0,0x4,0x0,0x2e, },
+ {0x0,0xb,0x0,0x0,0x0,0x0,0x0,0x0,0x0, },
+ {0x0,0x0,0x0,0x0,0x38,0x0,0x0,0x0,0x0, },
+ {0x0,0x0,0x0,0x0,0x39,0x0,0x0,0x0,0x0, },
+ {0x0,0x0,0x0,0x0,-1,0x0,0x41,0x0,0x0, },
+ {0x0,0x0,0x0,0x0,0x28,0x0,0x41,0x0,0x0, },
+ {0x0,0xc,-1,0x2c,0x0,0x2c,0x4,0xc,0x2c, },
+ {0x0,0x49,-1,0x45,-1,0x44,0x48,-1,0x0, },
+ {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,-1, },
+};
+tp_driver(p, e)
+register tp_PCB_ *p;
+register struct tp_event *e;
+{
+ register int index, error=0;
+ struct act_ent *a;
+ static struct act_ent erroraction = {0,-1};
+
+ index = inx[1 + e->ev_number][p->tp_state];
+ if(index<0) index=_Xebec_index(e, p);
+ if (index==0) {
+ a = &erroraction;
+ } else
+ a = &statetable[index];
+
+ if(a->a_action)
+ error = _Xebec_action( a->a_action, e, p );
+ IFTRACE(D_DRIVER)
+ tptrace(DRIVERTRACE, a->a_newstate, p->tp_state, e->ev_number, a->a_action, 0);
+ ENDTRACE
+ if(error==0)
+ p->tp_state = a->a_newstate;
+ return error;
+}
diff --git a/sys/netiso/tp_emit.c b/sys/netiso/tp_emit.c
new file mode 100644
index 0000000..16ed5bc
--- /dev/null
+++ b/sys/netiso/tp_emit.c
@@ -0,0 +1,996 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_emit.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_emit.c,v 5.5 88/11/18 17:27:20 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_emit.c,v $
+ *
+ * This file contains tp_emit() and tp_error_emit(), which
+ * form TPDUs and hand them to ip.
+ * They take data in the form of mbuf chain, allocate mbufs as
+ * necessary for headers, and set the fields as appropriate from
+ * information found in the tpcb and net-level pcb.
+ *
+ * The worst thing about this code is adding the variable-length
+ * options on a machine that requires alignment for any memory access
+ * that isn't of size 1. See the macro ADDOPTION() below.
+ *
+ * We don't do any concatenation. (There's a kludge to test the
+ * basic mechanism of separation under the 'w' tpdebug option, that's all.)
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <netiso/iso.h>
+#include <netiso/iso_pcb.h>
+#include <netiso/argo_debug.h>
+#include <netiso/tp_timer.h>
+#include <netiso/tp_param.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/tp_tpdu.h>
+#include <netiso/tp_trace.h>
+#include <netiso/tp_meas.h>
+#include <netiso/tp_seq.h>
+#include <netiso/iso_errno.h>
+
+#include <net/if.h>
+#ifdef TRUE
+#undef FALSE
+#undef TRUE
+#endif
+#include <netccitt/x25.h>
+#include <netccitt/pk.h>
+#include <netccitt/pk_var.h>
+
+void iso_gen_csum();
+
+
+/* Here is a mighty kludge. The token ring misorders packets if you
+ * fire them at it too fast, and TP sans checksum is "too fast", so
+ * we have introduced a delay when checksumming isn't used.
+ */
+char tp_delay = 0x00; /* delay to keep token ring from blowing it */
+
+/*
+ * NAME: tp_emit()
+ *
+ * CALLED FROM: tp.trans and from tp_sbsend()
+ *
+ * FUNCTION and ARGUMENTS:
+ * Emits one tpdu of the type (dutype), of the format appropriate
+ * to the connection described by the pcb (tpcb), with sequence
+ * number (seq) (where appropriate), end-of-tsdu bit (eot) where
+ * appropriate, and with the data in the mbuf chain (data).
+ * For DR and ER tpdus, the argument (eot) is
+ * the reason for issuing the tpdu rather than an end-of-tsdu indicator.
+ *
+ * RETURNS:
+ * 0 OK
+ * ENOBUFS
+ * E* returned from net layer output rtn
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ *
+ * WE ASSUME that the tp header + all options will fit in ONE mbuf.
+ * If mbufs are 256 this will most likely be true, but if they are 128 it's
+ * possible that they won't.
+ * If you used every option on the CR + max. user data you'd overrun
+ * 112 but unless you used > 115 bytes for the security
+ * parameter, it would fit in a 256-byte mbuf (240 bytes for the header)
+ * We don't support the security parameter, so this isn't a problem.
+ * If security is added, we ought to remove this assumption.
+ *
+ * We do not implement the flow control confirmation "element of procedure".
+ * A) it should not affect interoperability,
+ * B) it should not be necessary - the protocol will eventually
+ * straighten things out w/o FCC, as long as we don't have severely
+ * mismatched keepalive and inactivity timers, and
+ * C) it appears not to be REQUIRED, and
+ * D) it's incredibly grotesque, and no doubt will lengthen a few
+ * critical paths.
+ * HOWEVER, we're thinking about putting it in anyway, for
+ * completeness, just like we did with ack subsequencing.
+ */
+
+int
+tp_emit(dutype, tpcb, seq, eot, data)
+ int dutype;
+ struct tp_pcb *tpcb;
+ SeqNum seq;
+ u_int eot;
+ struct mbuf *data;
+{
+ register struct tpdu *hdr;
+ register struct mbuf *m;
+ int csum_offset=0;
+ int datalen = 0;
+ int error = 0;
+ SeqNum olduwe;
+ int acking_ooo;
+
+ /* NOTE:
+ * here we treat tpdu_li as if it DID include the li field, up until
+ * the end, at which time we subtract 1
+ * THis is because if we subtract 1 right away, we end up adding
+ * one every time we add an option.
+ */
+ IFDEBUG(D_EMIT)
+ printf(
+ "tp_emit dutype 0x%x, tpcb 0x%x, eot 0x%x, seq 0x%x, data 0x%x",
+ dutype, tpcb, eot, seq, data);
+ ENDDEBUG
+
+ if (dutype == CR_TPDU || dutype == CC_TPDU) {
+ m = (struct mbuf *) malloc((u_long)256, M_MBUF, M_DONTWAIT);
+ if (m) {
+ m->m_type = TPMT_TPHDR;
+ mbstat.m_mtypes[TPMT_TPHDR]++;
+ m->m_next = MNULL;
+ m->m_nextpkt = MNULL;
+ m->m_data = m->m_pktdat;
+ m->m_flags = M_PKTHDR;
+ }
+ } else {
+ MGETHDR(m, M_DONTWAIT, TPMT_TPHDR);
+ }
+ m->m_data += max_hdr;
+ if (m == NULL) {
+ if(data != (struct mbuf *)0)
+ m_freem(data);
+ error = ENOBUFS;
+ goto done;
+ }
+ m->m_len = sizeof(struct tpdu);
+ m->m_act = MNULL;
+
+ hdr = mtod(m, struct tpdu *);
+ bzero((caddr_t)hdr, sizeof(struct tpdu));
+
+ {
+ int tp_headersize();
+
+ hdr->tpdu_type = dutype;
+ hdr->tpdu_li = tp_headersize(dutype, tpcb);
+ /*
+ * class 0 doesn't use this for DT
+ * it'll just get overwritten below
+ */
+ hdr->tpdu_dref = htons(tpcb->tp_fref);
+ if( tpcb->tp_use_checksum ||
+ (dutype == CR_TPDU_type && (tpcb->tp_class & TP_CLASS_4) )) {
+ csum_offset = hdr->tpdu_li + 2; /* DOESN'T include csum */
+ ADDOPTION(TPP_checksum, hdr, 2, eot /* dummy arg */);
+ IFDEBUG(D_CHKSUM)
+ printf(
+ "tp_emit: csum_offset 0x%x, hdr->tpdu_li 0x%x\n",
+ csum_offset, hdr->tpdu_li);
+ ENDDEBUG
+ }
+ /*
+ * VARIABLE PARTS...
+ */
+ switch( dutype ) {
+
+ case CR_TPDU_type:
+ hdr->tpdu_CRdref_0 = 0; /* must be zero */
+ case CC_TPDU_type:
+ if (!tpcb->tp_cebit_off) {
+ tpcb->tp_win_recv = tp_start_win << 8;
+ LOCAL_CREDIT(tpcb);
+ CONG_INIT_SAMPLE(tpcb);
+ } else
+ LOCAL_CREDIT(tpcb);
+
+/* Case CC_TPDU_type used to be here */
+ {
+ u_char x;
+
+ hdr->tpdu_CCsref = htons(tpcb->tp_lref); /* same as CRsref */
+
+ if( tpcb->tp_class > TP_CLASS_1 ) {
+ tpcb->tp_sent_uwe = tpcb->tp_lcredit -1;
+ tpcb->tp_sent_rcvnxt = 1;
+ tpcb->tp_sent_lcdt = tpcb->tp_lcredit;
+ hdr->tpdu_cdt = tpcb->tp_lcredit;
+ } else {
+#ifdef TPCONS
+ if (tpcb->tp_netservice == ISO_CONS) {
+ struct isopcb *isop = (struct isopcb *)tpcb->tp_npcb;
+ struct pklcd *lcp = (struct pklcd *)(isop->isop_chan);
+ lcp->lcd_flags &= ~X25_DG_CIRCUIT;
+ }
+#endif
+ hdr->tpdu_cdt = 0;
+ }
+ hdr->tpdu_CCclass = tp_mask_to_num(tpcb->tp_class);
+ hdr->tpdu_CCoptions =
+ (tpcb->tp_xtd_format? TPO_XTD_FMT:0) |
+ (tpcb->tp_use_efc? TPO_USE_EFC:0);
+
+ IFPERF(tpcb)
+ u_char perf_meas = tpcb->tp_perf_on;
+ ADDOPTION(TPP_perf_meas, hdr, sizeof(perf_meas), perf_meas);
+ ENDPERF
+
+ if( dutype == CR_TPDU_type ) {
+ IncStat(ts_CR_sent);
+
+ ASSERT( tpcb->tp_lsuffixlen > 0 );
+ ASSERT( tpcb->tp_fsuffixlen > 0 );
+
+ ADDOPTION(TPP_calling_sufx, hdr,
+ tpcb->tp_lsuffixlen, tpcb->tp_lsuffix[0]);
+ ADDOPTION(TPP_called_sufx, hdr,
+ tpcb->tp_fsuffixlen, tpcb->tp_fsuffix[0]);
+ } else {
+ IncStat(ts_CC_sent);
+ }
+
+ ADDOPTION(TPP_tpdu_size, hdr,
+ sizeof(tpcb->tp_tpdusize), tpcb->tp_tpdusize);
+
+ if (tpcb->tp_class != TP_CLASS_0) {
+ short millisec = 500*(tpcb->tp_sendack_ticks);
+
+ millisec = htons(millisec);
+ ADDOPTION(TPP_acktime, hdr, sizeof(short), millisec);
+
+ x = (tpcb->tp_use_nxpd? TPAO_USE_NXPD: 0)
+ | (tpcb->tp_use_rcc? TPAO_USE_RCC : 0)
+ | (tpcb->tp_use_checksum?0: TPAO_NO_CSUM)
+ | (tpcb->tp_xpd_service? TPAO_USE_TXPD: 0);
+ ADDOPTION(TPP_addl_opt, hdr, 1, x);
+
+ if ((tpcb->tp_l_tpdusize ^ (1 << tpcb->tp_tpdusize)) != 0) {
+ u_short size_s = tpcb->tp_l_tpdusize >> 7;
+ u_char size_c = size_s;
+ ASSERT(tpcb->tp_l_tpdusize < 65536 * 128);
+ if (dutype == CR_TPDU_type)
+ tpcb->tp_ptpdusize = size_s;
+ if (size_s < 256) {
+ ADDOPTION(TPP_ptpdu_size, hdr, 1, size_c);
+ } else {
+ size_s = htons(size_s);
+ ADDOPTION(TPP_ptpdu_size, hdr, 2, size_s);
+ }
+ }
+ }
+
+ if( (dutype == CR_TPDU_type) && (tpcb->tp_class != TP_CLASS_0)){
+
+ ASSERT( 1 == sizeof(tpcb->tp_vers) );
+ ADDOPTION(TPP_vers, hdr, 1, tpcb->tp_vers);
+
+ /* for each alt protocol class x,
+ * x = x<<4;
+ * option = concat(option, x);
+ * Well, for now we only have TP0 for an
+ * alternative so... this is easy.
+ *
+ * HOWEVER... There should be NO alt protocol
+ * class over CLNS. Need to see if the route suggests
+ * CONS, and iff so add alt class.
+ */
+ x = 0;
+ ADDOPTION(TPP_alt_class, hdr, 1, x);
+ }
+
+ if( hdr->tpdu_li > MLEN)
+ panic("tp_emit CR/CC");
+ }
+ break;
+
+ case DR_TPDU_type:
+ if( hdr->tpdu_DRdref == 0 ) {
+ /* don't issue the DR */
+ goto done;
+ }
+ hdr->tpdu_cdt = 0;
+ hdr->tpdu_DRsref = htons(tpcb->tp_lref);
+ hdr->tpdu_DRreason = (u_char)eot; /* WHICH BYTE OF THIS??? */
+
+ /* forget the add'l information variable part */
+ IncStat(ts_DR_sent);
+ break;
+
+ case DC_TPDU_type: /* not used in class 0 */
+ ASSERT( tpcb->tp_class != TP_CLASS_0);
+ hdr->tpdu_DCsref = htons(tpcb->tp_lref);
+ hdr->tpdu_cdt = 0;
+ data = (struct mbuf *)0;
+ IncStat(ts_DC_sent);
+ break;
+
+ case XAK_TPDU_type: /* xak not used in class 0 */
+ ASSERT( tpcb->tp_class != TP_CLASS_0); /* fall through */
+ hdr->tpdu_cdt = 0;
+
+ IFTRACE(D_XPD)
+ tptraceTPCB(TPPTXack, seq, 0, 0, 0, 0);
+ ENDTRACE
+ data = (struct mbuf *)0;
+ if (tpcb->tp_xtd_format) {
+#ifdef BYTE_ORDER
+ union seq_type seqeotX;
+
+ seqeotX.s_seq = seq;
+ seqeotX.s_eot = 1;
+ hdr->tpdu_seqeotX = htonl(seqeotX.s_seqeot);
+#else
+ hdr->tpdu_XAKseqX = seq;
+#endif /* BYTE_ORDER */
+ } else {
+ hdr->tpdu_XAKseq = seq;
+ }
+ IncStat(ts_XAK_sent);
+ IncPStat(tpcb, tps_XAK_sent);
+ break;
+
+ case XPD_TPDU_type: /* xpd not used in class 0 */
+ ASSERT( tpcb->tp_class != TP_CLASS_0); /* fall through */
+ hdr->tpdu_cdt = 0;
+ if (tpcb->tp_xtd_format) {
+#ifdef BYTE_ORDER
+ union seq_type seqeotX;
+
+ seqeotX.s_seq = seq;
+ seqeotX.s_eot = 1;
+ hdr->tpdu_seqeotX = htonl(seqeotX.s_seqeot);
+#else
+ hdr->tpdu_XPDseqX = seq;
+ hdr->tpdu_XPDeotX = 1; /* always 1 for XPD tpdu */
+#endif /* BYTE_ORDER */
+ } else {
+ hdr->tpdu_XPDseq = seq;
+ hdr->tpdu_XPDeot = 1; /* always 1 for XPD tpdu */
+ }
+ IncStat(ts_XPD_sent);
+ IncPStat(tpcb, tps_XPD_sent);
+
+ /* kludge to test the input size checking */
+ IFDEBUG(D_SIZE_CHECK)
+ /*if(data->m_len <= 16 && data->m_off < (MLEN-18) ) {
+ printf("Sending too much data on XPD: 18 bytes\n");
+ data->m_len = 18;
+ }*/
+ ENDDEBUG
+ break;
+
+ case DT_TPDU_type:
+ hdr->tpdu_cdt = 0;
+ IFTRACE(D_DATA)
+ tptraceTPCB(TPPTmisc, "emit DT: eot seq tpdu_li", eot, seq,
+ hdr->tpdu_li, 0);
+ ENDTRACE
+ if (tpcb->tp_xtd_format) {
+#ifdef BYTE_ORDER
+ union seq_type seqeotX;
+
+ seqeotX.s_seq = seq;
+ seqeotX.s_eot = eot;
+ hdr->tpdu_seqeotX = htonl(seqeotX.s_seqeot);
+#else
+ hdr->tpdu_DTseqX = seq;
+ hdr->tpdu_DTeotX = eot;
+#endif /* BYTE_ORDER */
+ } else if (tpcb->tp_class == TP_CLASS_0) {
+ IFDEBUG(D_EMIT)
+ printf("DT tpdu: class 0 m 0x%x hdr 0x%x\n", m, hdr);
+ dump_buf( hdr, hdr->tpdu_li + 1 );
+ ENDDEBUG
+ ((struct tp0du *)hdr)->tp0du_eot = eot;
+ ((struct tp0du *)hdr)->tp0du_mbz = 0;
+ IFDEBUG(D_EMIT)
+ printf("DT 2 tpdu: class 0 m 0x%x hdr 0x%x\n", m, hdr);
+ dump_buf( hdr, hdr->tpdu_li + 1 );
+ ENDDEBUG
+ } else {
+ hdr->tpdu_DTseq = seq;
+ hdr->tpdu_DTeot = eot;
+ }
+ if(eot) {
+ IncStat(ts_EOT_sent);
+ }
+ IncStat(ts_DT_sent);
+ IncPStat(tpcb, tps_DT_sent);
+ break;
+
+ case AK_TPDU_type:/* ak not used in class 0 */
+ ASSERT( tpcb->tp_class != TP_CLASS_0);
+ data = (struct mbuf *)0;
+ olduwe = tpcb->tp_sent_uwe;
+
+ if (seq != tpcb->tp_sent_rcvnxt || tpcb->tp_rsycnt == 0) {
+ LOCAL_CREDIT( tpcb );
+ tpcb->tp_sent_uwe =
+ SEQ(tpcb,tpcb->tp_rcvnxt + tpcb->tp_lcredit -1);
+ tpcb->tp_sent_lcdt = tpcb->tp_lcredit;
+ acking_ooo = 0;
+ } else
+ acking_ooo = 1;
+
+ IFDEBUG(D_RENEG)
+ /* occasionally fake a reneging so
+ you can test subsequencing */
+ if( olduwe & 0x1 ) {
+ tpcb->tp_reneged = 1;
+ IncStat(ts_ldebug);
+ }
+ ENDDEBUG
+ /* Are we about to reneg on credit?
+ * When might we do so?
+ * a) when using optimistic credit (which we no longer do).
+ * b) when drain() gets implemented (not in the plans).
+ * c) when D_RENEG is on.
+ * d) when DEC BIT response is implemented.
+ * (not- when we do this, we'll need to implement flow control
+ * confirmation)
+ */
+ if( SEQ_LT(tpcb, tpcb->tp_sent_uwe, olduwe) ) {
+ tpcb->tp_reneged = 1;
+ IncStat(ts_lcdt_reduced);
+ IFTRACE(D_CREDIT)
+ tptraceTPCB(TPPTmisc,
+ "RENEG: olduwe newuwe lcredit rcvnxt",
+ olduwe,
+ tpcb->tp_sent_uwe, tpcb->tp_lcredit,
+ tpcb->tp_rcvnxt);
+ ENDTRACE
+ }
+ IFPERF(tpcb)
+ /* new lwe is less than old uwe means we're
+ * acking before we received a whole window full
+ */
+ if( SEQ_LT( tpcb, tpcb->tp_rcvnxt, olduwe) ) {
+ /* tmp1 = number of pkts fewer than the full window */
+ register int tmp1 =
+ (int) SEQ_SUB( tpcb, olduwe, tpcb->tp_rcvnxt);
+
+ if(tmp1 > TP_PM_MAX)
+ tmp1 = TP_PM_MAX;
+ IncPStat( tpcb, tps_ack_early[tmp1] );
+
+ /* tmp1 = amt of new cdt we're advertising */
+ tmp1 = SEQ_SUB( tpcb, seq, tpcb->tp_sent_rcvnxt);
+ if(tmp1 > TP_PM_MAX )
+ tmp1 = TP_PM_MAX;
+
+ IncPStat( tpcb,
+ tps_cdt_acked [ tmp1 ]
+ [ ((tpcb->tp_lcredit > TP_PM_MAX)?
+ TP_PM_MAX:tpcb->tp_lcredit) ] );
+
+ }
+ ENDPERF
+
+ IFTRACE(D_ACKSEND)
+ tptraceTPCB(TPPTack, seq, tpcb->tp_lcredit, tpcb->tp_sent_uwe,
+ tpcb->tp_r_subseq, 0);
+ ENDTRACE
+ if (tpcb->tp_xtd_format) {
+#ifdef BYTE_ORDER
+ union seq_type seqeotX;
+
+ seqeotX.s_seq = seq;
+ seqeotX.s_eot = 0;
+ hdr->tpdu_seqeotX = htonl(seqeotX.s_seqeot);
+ hdr->tpdu_AKcdtX = htons(tpcb->tp_lcredit);
+#else
+ hdr->tpdu_cdt = 0;
+ hdr->tpdu_AKseqX = seq;
+ hdr->tpdu_AKcdtX = tpcb->tp_lcredit;
+#endif /* BYTE_ORDER */
+ } else {
+ hdr->tpdu_AKseq = seq;
+ hdr->tpdu_AKcdt = tpcb->tp_lcredit;
+ }
+ if ((tpcb->tp_class == TP_CLASS_4) &&
+ (tpcb->tp_reneged || acking_ooo)) {
+ /*
+ * Ack subsequence parameter req'd if WE reneged on
+ * credit offered. (ISO 8073, 12.2.3.8.2, p. 74)
+ */
+ IFDEBUG(D_RENEG)
+ printf("Adding subseq 0x%x\n", tpcb->tp_s_subseq);
+ ENDDEBUG
+ tpcb->tp_s_subseq++;
+ /*
+ * add tmp subseq and do a htons on it.
+ */
+ ADDOPTION(TPP_subseq, hdr,
+ sizeof(tpcb->tp_s_subseq), tpcb->tp_s_subseq);
+ } else
+ tpcb->tp_s_subseq = 0;
+
+ if ( tpcb->tp_sendfcc || eot ) /* overloaded to mean SEND FCC */ {
+ /*
+ * Rules for sending FCC ("should" send when) :
+ * %a) received an ack from peer with NO NEWS whatsoever,
+ * and it did not contain an FCC
+ * b) received an ack from peer that opens its closed window.
+ * c) received an ack from peer after it reneged on its
+ * offered credit, AND this ack raises UWE but LWE is same
+ * and below UWE at time of reneging (reduction)
+ * Now, ISO 8073 12.2.3.8.3 says
+ * that a retransmitted AK shall not contain the FCC
+ * parameter. Now, how the hell you tell the difference
+ * between a retransmitted ack and an ack that's sent in
+ * response to a received ack, I don't know, because without
+ * any local activity, and w/o any received DTs, they
+ * will contain exactly the same credit/seq# information.
+ * Anyway, given that the "retransmission of acks"
+ * procedure (ISO 8073 12.2.3.8.3) is optional, and we
+ * don't do it (although the peer can't tell that), we
+ * ignore this last rule.
+ *
+ * We send FCC for reasons a) and b) only.
+ * To add reason c) would require a ridiculous amount of state.
+ *
+ */
+ u_short bogus[4]; /* lwe(32), subseq(16), cdt(16) */
+ SeqNum lwe;
+ u_short subseq, fcredit;
+
+ tpcb->tp_sendfcc = 0;
+
+ lwe = (SeqNum) htonl(tpcb->tp_snduna);
+ subseq = htons(tpcb->tp_r_subseq);
+ fcredit = htons(tpcb->tp_fcredit);
+
+ bcopy((caddr_t) &lwe, (caddr_t)&bogus[0], sizeof(SeqNum));
+ bcopy((caddr_t) &subseq, (caddr_t)&bogus[2], sizeof(u_short));
+ bcopy((caddr_t) &fcredit, (caddr_t)&bogus[3], sizeof(u_short));
+
+ IFTRACE(D_ACKSEND)
+ tptraceTPCB(TPPTmisc,
+ "emit w/FCC: snduna r_subseq fcredit",
+ tpcb->tp_snduna, tpcb->tp_r_subseq,
+ tpcb->tp_fcredit, 0);
+ ENDTRACE
+
+ IFDEBUG(D_ACKSEND)
+ printf("Calling ADDOPTION 0x%x, 0x%x, 0x%x,0x%x\n",
+ TPP_flow_cntl_conf,
+ hdr, sizeof(bogus), bogus[0]);
+ ENDDEBUG
+ ADDOPTION(TPP_flow_cntl_conf, hdr, sizeof(bogus), bogus[0]);
+ IFDEBUG(D_ACKSEND)
+ printf("after ADDOPTION hdr 0x%x hdr->tpdu_li 0x%x\n",
+ hdr, hdr->tpdu_li);
+ printf(
+ "after ADDOPTION csum_offset 0x%x, hdr->tpdu_li 0x%x\n",
+ csum_offset, hdr->tpdu_li);
+ ENDDEBUG
+
+ }
+ tpcb->tp_reneged = 0;
+ tpcb->tp_sent_rcvnxt = seq;
+ if (tpcb->tp_fcredit == 0) {
+ int timo = tpcb->tp_keepalive_ticks;
+ if (tpcb->tp_rxtshift < TP_MAXRXTSHIFT)
+ tpcb->tp_rxtshift++;
+ timo = min(timo, ((int)tpcb->tp_dt_ticks) << tpcb->tp_rxtshift);
+ tp_ctimeout(tpcb, TM_sendack, timo);
+ } else
+ tp_ctimeout(tpcb, TM_sendack, tpcb->tp_keepalive_ticks);
+ IncStat(ts_AK_sent);
+ IncPStat(tpcb, tps_AK_sent);
+ IFDEBUG(D_ACKSEND)
+ printf(
+ "2 after rADDOPTION csum_offset 0x%x, hdr->tpdu_li 0x%x\n",
+ csum_offset, hdr->tpdu_li);
+ ENDDEBUG
+ break;
+
+ case ER_TPDU_type:
+ hdr->tpdu_ERreason = eot;
+ hdr->tpdu_cdt = 0;
+ /* no user data */
+ data = (struct mbuf *)0;
+ IncStat(ts_ER_sent);
+ break;
+ }
+
+ }
+ ASSERT( ((int)hdr->tpdu_li > 0) && ((int)hdr->tpdu_li < MLEN) );
+
+ m->m_next = data;
+
+ ASSERT( hdr->tpdu_li < MLEN ); /* leave this in */
+ ASSERT( hdr->tpdu_li != 0 ); /* leave this in */
+
+ m->m_len = hdr->tpdu_li ;
+ hdr->tpdu_li --; /* doesn't include the li field */
+
+ datalen = m_datalen( m ); /* total len */
+
+ ASSERT( datalen <= tpcb->tp_l_tpdusize ); /* may become a problem
+ when CLNP is used; leave in here for the time being */
+ IFDEBUG(D_ACKSEND)
+ printf(
+ "4 after rADDOPTION csum_offset 0x%x, hdr->tpdu_li 0x%x\n",
+ csum_offset, hdr->tpdu_li);
+ ENDDEBUG
+ if( datalen > tpcb->tp_l_tpdusize ) {
+ printf("data len 0x%x tpcb->tp_l_tpdusize 0x%x\n",
+ datalen, tpcb->tp_l_tpdusize);
+ }
+ IFDEBUG(D_EMIT)
+ printf(
+ "tp_emit before gen_csum m_len 0x%x, csum_offset 0x%x, datalen 0x%x\n",
+ m->m_len, csum_offset, datalen);
+ ENDDEBUG
+ if( tpcb->tp_use_checksum ||
+ (dutype == CR_TPDU_type && (tpcb->tp_class & TP_CLASS_4)) ) {
+ iso_gen_csum(m, csum_offset, datalen);
+ }
+
+ IFDEBUG(D_EMIT)
+ printf("tp_emit before tpxxx_output tpcb 0x%x, dutype 0x%x, datalen 0x%x\n",
+ tpcb, dutype, datalen);
+ dump_buf(mtod(m, caddr_t), datalen);
+ ENDDEBUG
+
+ IFPERF(tpcb)
+ if( dutype == DT_TPDU_type ) {
+ PStat(tpcb, Nb_to_ll) += (datalen - m->m_len);
+ tpmeas( tpcb->tp_lref, TPtime_to_ll, (struct timeval *)0,
+ seq, PStat(tpcb, Nb_to_ll), (datalen - m->m_len));
+ }
+ ENDPERF
+
+ IFTRACE(D_EMIT)
+ tptraceTPCB(TPPTtpduout, dutype, hdr, hdr->tpdu_li+1, datalen, 0);
+ ENDTRACE
+ IFDEBUG(D_EMIT)
+ printf("OUTPUT: tpcb 0x%x, isop 0x%x, so 0x%x\n",
+ tpcb, tpcb->tp_npcb, tpcb->tp_sock);
+ ENDDEBUG
+
+ { extern char tp_delay;
+
+ if( tp_delay )
+ if( tpcb->tp_use_checksum == 0 ) {
+ register u_int i = tp_delay;
+ for (; i!= 0; i--)
+ (void) iso_check_csum(m, datalen);
+ }
+ }
+ ASSERT( m->m_len > 0 );
+ error = (tpcb->tp_nlproto->nlp_output)(tpcb->tp_npcb, m, datalen,
+ !tpcb->tp_use_checksum);
+ IFDEBUG(D_EMIT)
+ printf("OUTPUT: returned 0x%x\n", error);
+ ENDDEBUG
+ IFTRACE(D_EMIT)
+ tptraceTPCB(TPPTmisc,
+ "tp_emit nlproto->output netservice returns datalen",
+ tpcb->tp_nlproto->nlp_output, tpcb->tp_netservice, error, datalen);
+ ENDTRACE
+done:
+ if (error) {
+ if (dutype == AK_TPDU_type)
+ tp_ctimeout(tpcb, TM_sendack, 1);
+ if (error == E_CO_QFULL) {
+ tp_quench(tpcb, PRC_QUENCH);
+ return 0;
+ }
+ }
+ return error;
+}
+/*
+ * NAME: tp_error_emit()
+ * CALLED FROM: tp_input() when a DR or ER is to be issued in
+ * response to an input error.
+ * FUNCTION and ARGUMENTS:
+ * The error type is the first argument.
+ * The argument (sref) is the source reference on the bad incoming tpdu,
+ * and is used for a destination reference on the outgoing packet.
+ * (faddr) and (laddr) are the foreign and local addresses for this
+ * connection.
+ * (erdata) is a ptr to the errant incoming tpdu, and is copied into the
+ * outgoing ER, if an ER is to be issued.
+ * (erlen) is the number of octets of the errant tpdu that we should
+ * try to copy.
+ * (tpcb) is the pcb that describes the connection for which the bad tpdu
+ * arrived.
+ * RETURN VALUES:
+ * 0 OK
+ * ENOBUFS
+ * E* from net layer datagram output routine
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+
+int
+tp_error_emit(error, sref, faddr, laddr, erdata, erlen, tpcb, cons_channel,
+ dgout_routine)
+ int error;
+ u_long sref;
+ struct sockaddr_iso *faddr, *laddr;
+ struct mbuf *erdata;
+ int erlen;
+ struct tp_pcb *tpcb;
+ caddr_t cons_channel;
+ int (*dgout_routine)();
+{
+ int dutype;
+ int datalen = 0;
+ register struct tpdu *hdr;
+ register struct mbuf *m;
+ int csum_offset;
+
+ IFTRACE(D_ERROR_EMIT)
+ tptrace(TPPTmisc, "tp_error_emit error sref tpcb erlen",
+ error, sref, tpcb, erlen);
+ ENDTRACE
+ IFDEBUG(D_ERROR_EMIT)
+ printf(
+ "tp_error_emit error 0x%x sref 0x%x tpcb 0x%x erlen 0x%x chan 0x%x\n",
+ error, sref, tpcb, erlen, cons_channel);
+ ENDDEBUG
+
+ MGET(m, M_DONTWAIT, TPMT_TPHDR);
+ if (m == NULL) {
+ return ENOBUFS;
+ }
+ m->m_len = sizeof(struct tpdu);
+ m->m_act = MNULL;
+
+ hdr = mtod(m, struct tpdu *);
+
+ IFDEBUG(D_ERROR_EMIT)
+ printf("[error 0x%x] [error&0xff 0x%x] [(char)error 0x%x]\n",
+ error, error&0xff, (char)error);
+ ENDDEBUG
+
+
+ if (error & TP_ERROR_SNDC)
+ dutype = DC_TPDU_type;
+ else if (error & 0x40) {
+ error &= ~0x40;
+ dutype = ER_TPDU_type;
+ } else
+ dutype = DR_TPDU_type;
+ error &= 0xff;
+
+ hdr->tpdu_type = dutype;
+ hdr->tpdu_cdt = 0;
+
+ switch( dutype ) {
+
+ case DC_TPDU_type:
+ IncStat(ts_DC_sent);
+ hdr->tpdu_li = 6;
+ hdr->tpdu_DCdref = htons(sref);
+ hdr->tpdu_DCsref = tpcb ? htons(tpcb->tp_lref) : 0;
+ IFDEBUG(D_ERROR_EMIT)
+ printf("DC case:\n");
+ dump_buf( hdr, 6);
+ ENDDEBUG
+ /* forget the add'l information variable part */
+ break;
+
+ case DR_TPDU_type:
+ IncStat(ts_DR_sent);
+ hdr->tpdu_li = 7;
+ hdr->tpdu_DRdref = htons(sref);
+ hdr->tpdu_DRsref = 0;
+ hdr->tpdu_DRreason = (char)error;
+ IFDEBUG(D_ERROR_EMIT)
+ printf("DR case:\n");
+ dump_buf( hdr, 7);
+ ENDDEBUG
+ /* forget the add'l information variable part */
+ break;
+
+ case ER_TPDU_type:
+ IncStat(ts_ER_sent);
+ hdr->tpdu_li = 5;
+ hdr->tpdu_ERreason = (char)error;
+ hdr->tpdu_ERdref = htons(sref);
+ break;
+
+ default:
+ ASSERT(0);
+ printf("TP PANIC: bad dutype 0x%x\n", dutype);
+ }
+
+ if(tpcb)
+ if( tpcb->tp_use_checksum ) {
+ ADDOPTION(TPP_checksum, hdr, 2, csum_offset /* dummy argument */);
+ csum_offset = hdr->tpdu_li - 2;
+ }
+
+ ASSERT( hdr->tpdu_li < MLEN );
+
+ if (dutype == ER_TPDU_type) {
+ /* copy the errant tpdu into another 'variable part' */
+ register caddr_t P;
+
+ IFTRACE(D_ERROR_EMIT)
+ tptrace(TPPTmisc, "error_emit ER len tpduli", erlen, hdr->tpdu_li,
+ 0,0);
+ ENDTRACE
+ IFDEBUG(D_ERROR_EMIT)
+ printf("error_emit ER len 0x%x tpduli 0x%x\n", erlen, hdr->tpdu_li);
+ ENDDEBUG
+
+ /* copy at most as many octets for which you have room */
+ if (erlen + hdr->tpdu_li + 2 > TP_MAX_HEADER_LEN)
+ erlen = TP_MAX_HEADER_LEN - hdr->tpdu_li - 2;
+
+ /* add the "invalid tpdu" parameter : required in class 0 */
+ P = (caddr_t)hdr + (int)(hdr->tpdu_li);
+ vbptr(P)->tpv_code = TPP_invalid_tpdu; /* parameter code */
+ vbptr(P)->tpv_len = erlen; /* parameter length */
+ m->m_len = hdr->tpdu_li + 2; /* 1 for code, 1 for length */
+
+ /* tp_input very likely handed us an mbuf chain w/ nothing in
+ * the first mbuf and the data following the empty mbuf
+ */
+ if(erdata->m_len == 0) {
+ erdata = m_free(erdata); /* returns the next mbuf on the chain */
+ }
+ /*
+ * copy only up to the bad octet
+ * (or max that will fit in a header
+ */
+ m->m_next = m_copy(erdata, 0, erlen);
+ hdr->tpdu_li += erlen + 2;
+ m_freem(erdata);
+ } else {
+ IFDEBUG(D_ERROR_EMIT)
+ printf("error_emit DR error tpduli 0x%x\n", error, hdr->tpdu_li);
+ dump_buf( (char *)hdr, hdr->tpdu_li );
+ ENDDEBUG
+ m->m_len = hdr->tpdu_li ;
+ m_freem(erdata);
+ }
+
+ hdr->tpdu_li --;
+ IFTRACE(D_ERROR_EMIT)
+ tptrace(TPPTtpduout, 2, hdr, hdr->tpdu_li+1, 0, 0);
+ ENDTRACE
+
+ datalen = m_datalen( m);
+ if (tpcb) {
+ if( tpcb->tp_use_checksum ) {
+ IFTRACE(D_ERROR_EMIT)
+ tptrace(TPPTmisc, "before gen csum datalen", datalen,0,0,0);
+ ENDTRACE
+ IFDEBUG(D_ERROR_EMIT)
+ printf("before gen csum datalen 0x%x, csum_offset 0x%x\n",
+ datalen, csum_offset);
+ ENDDEBUG
+
+ iso_gen_csum(m, csum_offset, datalen);
+ }
+
+ IFDEBUG(D_ERROR_EMIT)
+ printf("OUTPUT: tpcb 0x%x, isop 0x%x, so 0x%x\n",
+ tpcb, tpcb->tp_npcb, tpcb->tp_sock);
+ ENDDEBUG
+ }
+ if (cons_channel) {
+#ifdef TPCONS
+ struct pklcd *lcp = (struct pklcd *)cons_channel;
+ struct isopcb *isop = (struct isopcb *)lcp->lcd_upnext;
+
+ tpcons_dg_output(cons_channel, m, datalen);
+ /* was if (tpcb == 0) iso_pcbdetach(isop); */
+ /* but other side may want to try again over same VC,
+ so, we'll depend on him closing it, but in case it gets forgotten
+ we'll mark it for garbage collection */
+ lcp->lcd_flags |= X25_DG_CIRCUIT;
+ IFDEBUG(D_ERROR_EMIT)
+ printf("OUTPUT: dutype 0x%x channel 0x%x\n",
+ dutype, cons_channel);
+ ENDDEBUG
+#else
+ printf("TP panic! cons channel 0x%x but not cons configured\n",
+ cons_channel);
+#endif
+ } else if (tpcb) {
+
+ IFDEBUG(D_ERROR_EMIT)
+ printf("tp_error_emit 1 sending DG: Laddr\n");
+ dump_addr((struct sockaddr *)laddr);
+ printf("Faddr\n");
+ dump_addr((struct sockaddr *)faddr);
+ ENDDEBUG
+ return (tpcb->tp_nlproto->nlp_dgoutput)(
+ &laddr->siso_addr,
+ &faddr->siso_addr,
+ m, datalen,
+ /* no route */ (caddr_t)0, !tpcb->tp_use_checksum);
+ } else if (dgout_routine) {
+ IFDEBUG(D_ERROR_EMIT)
+ printf("tp_error_emit sending DG: Laddr\n");
+ dump_addr((struct sockaddr *)laddr);
+ printf("Faddr\n");
+ dump_addr((struct sockaddr *)faddr);
+ ENDDEBUG
+ return (*dgout_routine)( &laddr->siso_addr, &faddr->siso_addr,
+ m, datalen, /* no route */
+ (caddr_t)0, /* nochecksum==false */0);
+ } else {
+ IFDEBUG(D_ERROR_EMIT)
+ printf("tp_error_emit DROPPING \n", m);
+ ENDDEBUG
+ IncStat(ts_send_drop);
+ m_freem(m);
+ return 0;
+ }
+}
diff --git a/sys/netiso/tp_events.h b/sys/netiso/tp_events.h
new file mode 100644
index 0000000..4822283
--- /dev/null
+++ b/sys/netiso/tp_events.h
@@ -0,0 +1,84 @@
+/* $Header$ */
+/* $Source$ */
+struct tp_event {
+ int ev_number;
+ struct timeval e_time;
+#define TM_inact 0x0
+#define TM_retrans 0x1
+#define TM_sendack 0x2
+#define TM_notused 0x3
+
+ union{
+struct { SeqNum e_low; SeqNum e_high; int e_retrans; } EV_TM_reference;
+
+#define TM_reference 0x4
+struct { SeqNum e_low; SeqNum e_high; int e_retrans; } EV_TM_data_retrans;
+
+#define TM_data_retrans 0x5
+struct {
+ u_char e_reason;
+ } EV_ER_TPDU;
+
+#define ER_TPDU 0x6
+struct { struct mbuf *e_data; /* first field */
+ int e_datalen; /* 2nd field */
+ u_int e_cdt;
+ } EV_CR_TPDU;
+
+#define CR_TPDU 0x7
+struct { struct mbuf *e_data; /* first field */
+ int e_datalen; /* 2nd field */
+ u_short e_sref;
+ u_char e_reason;
+ } EV_DR_TPDU;
+
+#define DR_TPDU 0x8
+#define DC_TPDU 0x9
+struct { struct mbuf *e_data; /* first field */
+ int e_datalen; /* 2nd field */
+ u_short e_sref;
+ u_int e_cdt;
+ } EV_CC_TPDU;
+
+#define CC_TPDU 0xa
+struct { u_int e_cdt;
+ SeqNum e_seq;
+ SeqNum e_subseq;
+ u_char e_fcc_present;
+ } EV_AK_TPDU;
+
+#define AK_TPDU 0xb
+struct { struct mbuf *e_data; /* first field */
+ int e_datalen; /* 2nd field */
+ u_int e_eot;
+ SeqNum e_seq;
+ } EV_DT_TPDU;
+
+#define DT_TPDU 0xc
+struct { struct mbuf *e_data; /* first field */
+ int e_datalen; /* 2nd field */
+ SeqNum e_seq;
+ } EV_XPD_TPDU;
+
+#define XPD_TPDU 0xd
+struct { SeqNum e_seq; } EV_XAK_TPDU;
+
+#define XAK_TPDU 0xe
+#define T_CONN_req 0xf
+struct { u_char e_reason; } EV_T_DISC_req;
+
+#define T_DISC_req 0x10
+#define T_LISTEN_req 0x11
+#define T_DATA_req 0x12
+#define T_XPD_req 0x13
+#define T_USR_rcvd 0x14
+#define T_USR_Xrcvd 0x15
+#define T_DETACH 0x16
+#define T_NETRESET 0x17
+#define T_ACPT_req 0x18
+ }ev_union;
+};/* end struct event */
+
+#define tp_NEVENTS 0x19
+
+#define ATTR(X)ev_union.EV_/**/X/**/
diff --git a/sys/netiso/tp_inet.c b/sys/netiso/tp_inet.c
new file mode 100644
index 0000000..fb01371
--- /dev/null
+++ b/sys/netiso/tp_inet.c
@@ -0,0 +1,688 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_inet.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ * $Header: tp_inet.c,v 5.3 88/11/18 17:27:29 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_inet.c,v $
+ *
+ * Here is where you find the inet-dependent code. We've tried
+ * keep all net-level and (primarily) address-family-dependent stuff
+ * out of the tp source, and everthing here is reached indirectly
+ * through a switch table (struct nl_protosw *) tpcb->tp_nlproto
+ * (see tp_pcb.c).
+ * The routines here are:
+ * in_getsufx: gets transport suffix out of an inpcb structure.
+ * in_putsufx: put transport suffix into an inpcb structure.
+ * in_putnetaddr: put a whole net addr into an inpcb.
+ * in_getnetaddr: get a whole net addr from an inpcb.
+ * in_cmpnetaddr: compare a whole net addr from an isopcb.
+ * in_recycle_suffix: clear suffix for reuse in inpcb
+ * tpip_mtu: figure out what size tpdu to use
+ * tpip_input: take a pkt from ip, strip off its ip header, give to tp
+ * tpip_output_dg: package a pkt for ip given 2 addresses & some data
+ * tpip_output: package a pkt for ip given an inpcb & some data
+ */
+
+#ifdef INET
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+
+#include <netiso/tp_param.h>
+#include <netiso/argo_debug.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_ip.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/tp_trace.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_tpdu.h>
+#include <netinet/in_var.h>
+
+#ifndef ISO
+#include <netiso/iso_chksum.c>
+#endif
+
+/*
+ * NAME: in_getsufx()
+
+ * CALLED FROM: pr_usrreq() on PRU_BIND,
+ * PRU_CONNECT, PRU_ACCEPT, and PRU_PEERADDR
+ *
+ * FUNCTION, ARGUMENTS, and RETURN VALUE:
+ * Get a transport suffix from an inpcb structure (inp).
+ * The argument (which) takes the value TP_LOCAL or TP_FOREIGN.
+ *
+ * RETURNS: internet port / transport suffix
+ * (CAST TO AN INT)
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+in_getsufx(inp, lenp, data_out, which)
+ struct inpcb *inp;
+ u_short *lenp;
+ caddr_t data_out;
+ int which;
+{
+ *lenp = sizeof(u_short);
+ switch (which) {
+ case TP_LOCAL:
+ *(u_short *)data_out = inp->inp_lport;
+ return;
+
+ case TP_FOREIGN:
+ *(u_short *)data_out = inp->inp_fport;
+ }
+
+}
+
+/*
+ * NAME: in_putsufx()
+ *
+ * CALLED FROM: tp_newsocket(); i.e., when a connection
+ * is being established by an incoming CR_TPDU.
+ *
+ * FUNCTION, ARGUMENTS:
+ * Put a transport suffix (found in name) into an inpcb structure (inp).
+ * The argument (which) takes the value TP_LOCAL or TP_FOREIGN.
+ *
+ * RETURNS: Nada
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+/*ARGSUSED*/
+void
+in_putsufx(inp, sufxloc, sufxlen, which)
+ struct inpcb *inp;
+ caddr_t sufxloc;
+ int which;
+{
+ if (which == TP_FOREIGN) {
+ bcopy(sufxloc, (caddr_t)&inp->inp_fport, sizeof(inp->inp_fport));
+ }
+}
+
+/*
+ * NAME: in_recycle_tsuffix()
+ *
+ * CALLED FROM: tp.trans whenever we go into REFWAIT state.
+ *
+ * FUNCTION and ARGUMENT:
+ * Called when a ref is frozen, to allow the suffix to be reused.
+ * (inp) is the net level pcb.
+ *
+ * RETURNS: Nada
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: This really shouldn't have to be done in a NET level pcb
+ * but... for the internet world that just the way it is done in BSD...
+ * The alternative is to have the port unusable until the reference
+ * timer goes off.
+ */
+void
+in_recycle_tsuffix(inp)
+ struct inpcb *inp;
+{
+ inp->inp_fport = inp->inp_lport = 0;
+}
+
+/*
+ * NAME: in_putnetaddr()
+ *
+ * CALLED FROM:
+ * tp_newsocket(); i.e., when a connection is being established by an
+ * incoming CR_TPDU.
+ *
+ * FUNCTION and ARGUMENTS:
+ * Copy a whole net addr from a struct sockaddr (name).
+ * into an inpcb (inp).
+ * The argument (which) takes values TP_LOCAL or TP_FOREIGN
+ *
+ * RETURNS: Nada
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+void
+in_putnetaddr(inp, name, which)
+ register struct inpcb *inp;
+ struct sockaddr_in *name;
+ int which;
+{
+ switch (which) {
+ case TP_LOCAL:
+ bcopy((caddr_t)&name->sin_addr,
+ (caddr_t)&inp->inp_laddr, sizeof(struct in_addr));
+ /* won't work if the dst address (name) is INADDR_ANY */
+
+ break;
+ case TP_FOREIGN:
+ if( name != (struct sockaddr_in *)0 ) {
+ bcopy((caddr_t)&name->sin_addr,
+ (caddr_t)&inp->inp_faddr, sizeof(struct in_addr));
+ }
+ }
+}
+
+/*
+ * NAME: in_putnetaddr()
+ *
+ * CALLED FROM:
+ * tp_input() when a connection is being established by an
+ * incoming CR_TPDU, and considered for interception.
+ *
+ * FUNCTION and ARGUMENTS:
+ * Compare a whole net addr from a struct sockaddr (name),
+ * with that implicitly stored in an inpcb (inp).
+ * The argument (which) takes values TP_LOCAL or TP_FOREIGN
+ *
+ * RETURNS: Nada
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+in_cmpnetaddr(inp, name, which)
+ register struct inpcb *inp;
+ register struct sockaddr_in *name;
+ int which;
+{
+ if (which == TP_LOCAL) {
+ if (name->sin_port && name->sin_port != inp->inp_lport)
+ return 0;
+ return (name->sin_addr.s_addr == inp->inp_laddr.s_addr);
+ }
+ if (name->sin_port && name->sin_port != inp->inp_fport)
+ return 0;
+ return (name->sin_addr.s_addr == inp->inp_faddr.s_addr);
+}
+
+/*
+ * NAME: in_getnetaddr()
+ *
+ * CALLED FROM:
+ * pr_usrreq() PRU_SOCKADDR, PRU_ACCEPT, PRU_PEERADDR
+ * FUNCTION and ARGUMENTS:
+ * Copy a whole net addr from an inpcb (inp) into
+ * an mbuf (name);
+ * The argument (which) takes values TP_LOCAL or TP_FOREIGN.
+ *
+ * RETURNS: Nada
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+
+void
+in_getnetaddr( inp, name, which)
+ register struct mbuf *name;
+ struct inpcb *inp;
+ int which;
+{
+ register struct sockaddr_in *sin = mtod(name, struct sockaddr_in *);
+ bzero((caddr_t)sin, sizeof(*sin));
+ switch (which) {
+ case TP_LOCAL:
+ sin->sin_addr = inp->inp_laddr;
+ sin->sin_port = inp->inp_lport;
+ break;
+ case TP_FOREIGN:
+ sin->sin_addr = inp->inp_faddr;
+ sin->sin_port = inp->inp_fport;
+ break;
+ default:
+ return;
+ }
+ name->m_len = sin->sin_len = sizeof (*sin);
+ sin->sin_family = AF_INET;
+}
+
+/*
+ * NAME: tpip_mtu()
+ *
+ * CALLED FROM:
+ * tp_route_to() on incoming CR, CC, and pr_usrreq() for PRU_CONNECT
+ *
+ * FUNCTION, ARGUMENTS, and RETURN VALUE:
+ *
+ * Perform subnetwork dependent part of determining MTU information.
+ * It appears that setting a double pointer to the rtentry associated with
+ * the destination, and returning the header size for the network protocol
+ * suffices.
+ *
+ * SIDE EFFECTS:
+ * Sets tp_routep pointer in pcb.
+ *
+ * NOTES:
+ */
+
+tpip_mtu(tpcb)
+register struct tp_pcb *tpcb;
+{
+ struct inpcb *inp = (struct inpcb *)tpcb->tp_npcb;
+
+ IFDEBUG(D_CONN)
+ printf("tpip_mtu(tpcb)\n", tpcb);
+ printf("tpip_mtu routing to addr 0x%x\n", inp->inp_faddr.s_addr);
+ ENDDEBUG
+ tpcb->tp_routep = &(inp->inp_route.ro_rt);
+ return (sizeof (struct ip));
+
+}
+
+/*
+ * NAME: tpip_output()
+ *
+ * CALLED FROM: tp_emit()
+ *
+ * FUNCTION and ARGUMENTS:
+ * Take a packet(m0) from tp and package it so that ip will accept it.
+ * This means prepending space for the ip header and filling in a few
+ * of the fields.
+ * inp is the inpcb structure; datalen is the length of the data in the
+ * mbuf string m0.
+ * RETURNS:
+ * whatever (E*) is returned form the net layer output routine.
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+
+int
+tpip_output(inp, m0, datalen, nochksum)
+ struct inpcb *inp;
+ struct mbuf *m0;
+ int datalen;
+ int nochksum;
+{
+ return tpip_output_dg( &inp->inp_laddr, &inp->inp_faddr, m0, datalen,
+ &inp->inp_route, nochksum);
+}
+
+/*
+ * NAME: tpip_output_dg()
+ *
+ * CALLED FROM: tp_error_emit()
+ *
+ * FUNCTION and ARGUMENTS:
+ * This is a copy of tpip_output that takes the addresses
+ * instead of a pcb. It's used by the tp_error_emit, when we
+ * don't have an in_pcb with which to call the normal output rtn.
+ *
+ * RETURNS: ENOBUFS or whatever (E*) is
+ * returned form the net layer output routine.
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+
+/*ARGSUSED*/
+int
+tpip_output_dg(laddr, faddr, m0, datalen, ro, nochksum)
+ struct in_addr *laddr, *faddr;
+ struct mbuf *m0;
+ int datalen;
+ struct route *ro;
+ int nochksum;
+{
+ register struct mbuf *m;
+ register struct ip *ip;
+ int error;
+
+ IFDEBUG(D_EMIT)
+ printf("tpip_output_dg datalen 0x%x m0 0x%x\n", datalen, m0);
+ ENDDEBUG
+
+
+ MGETHDR(m, M_DONTWAIT, TPMT_IPHDR);
+ if (m == 0) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ m->m_next = m0;
+ MH_ALIGN(m, sizeof(struct ip));
+ m->m_len = sizeof(struct ip);
+
+ ip = mtod(m, struct ip *);
+ bzero((caddr_t)ip, sizeof *ip);
+
+ ip->ip_p = IPPROTO_TP;
+ m->m_pkthdr.len = ip->ip_len = sizeof(struct ip) + datalen;
+ ip->ip_ttl = MAXTTL;
+ /* don't know why you need to set ttl;
+ * overlay doesn't even make this available
+ */
+
+ ip->ip_src = *laddr;
+ ip->ip_dst = *faddr;
+
+ IncStat(ts_tpdu_sent);
+ IFDEBUG(D_EMIT)
+ dump_mbuf(m, "tpip_output_dg before ip_output\n");
+ ENDDEBUG
+
+ error = ip_output(m, (struct mbuf *)0, ro, IP_ALLOWBROADCAST, NULL);
+
+ IFDEBUG(D_EMIT)
+ printf("tpip_output_dg after ip_output\n");
+ ENDDEBUG
+
+ return error;
+
+bad:
+ m_freem(m);
+ IncStat(ts_send_drop);
+ return error;
+}
+
+/*
+ * NAME: tpip_input()
+ *
+ * CALLED FROM:
+ * ip's input routine, indirectly through the protosw.
+ *
+ * FUNCTION and ARGUMENTS:
+ * Take a packet (m) from ip, strip off the ip header and give it to tp
+ *
+ * RETURNS: No return value.
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+ProtoHook
+tpip_input(m, iplen)
+ struct mbuf *m;
+ int iplen;
+{
+ struct sockaddr_in src, dst;
+ register struct ip *ip;
+ int s = splnet(), hdrlen;
+
+ IncStat(ts_pkt_rcvd);
+
+ /*
+ * IP layer has already pulled up the IP header,
+ * but the first byte after the IP header may not be there,
+ * e.g. if you came in via loopback, so you have to do an
+ * m_pullup to before you can even look to see how much you
+ * really need. The good news is that m_pullup will round
+ * up to almost the next mbuf's worth.
+ */
+
+
+ if((m = m_pullup(m, iplen + 1)) == MNULL)
+ goto discard;
+ CHANGE_MTYPE(m, TPMT_DATA);
+
+ /*
+ * Now pull up the whole tp header:
+ * Unfortunately, there may be IP options to skip past so we
+ * just fetch it as an unsigned char.
+ */
+ hdrlen = iplen + 1 + mtod(m, u_char *)[iplen];
+
+ if( m->m_len < hdrlen ) {
+ if((m = m_pullup(m, hdrlen)) == MNULL){
+ IFDEBUG(D_TPINPUT)
+ printf("tp_input, pullup 2!\n");
+ ENDDEBUG
+ goto discard;
+ }
+ }
+ /*
+ * cannot use tp_inputprep() here 'cause you don't
+ * have quite the same situation
+ */
+
+ IFDEBUG(D_TPINPUT)
+ dump_mbuf(m, "after tpip_input both pullups");
+ ENDDEBUG
+ /*
+ * m_pullup may have returned a different mbuf
+ */
+ ip = mtod(m, struct ip *);
+
+ /*
+ * drop the ip header from the front of the mbuf
+ * this is necessary for the tp checksum
+ */
+ m->m_len -= iplen;
+ m->m_data += iplen;
+
+ src.sin_addr = *(struct in_addr *)&(ip->ip_src);
+ src.sin_family = AF_INET;
+ src.sin_len = sizeof(src);
+ dst.sin_addr = *(struct in_addr *)&(ip->ip_dst);
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(dst);
+
+ (void) tp_input(m, (struct sockaddr *)&src, (struct sockaddr *)&dst,
+ 0, tpip_output_dg, 0);
+ return 0;
+
+discard:
+ IFDEBUG(D_TPINPUT)
+ printf("tpip_input DISCARD\n");
+ ENDDEBUG
+ IFTRACE(D_TPINPUT)
+ tptrace(TPPTmisc, "tpip_input DISCARD m", m,0,0,0);
+ ENDTRACE
+ m_freem(m);
+ IncStat(ts_recv_drop);
+ splx(s);
+ return 0;
+}
+
+
+#include <sys/protosw.h>
+#include <netinet/ip_icmp.h>
+
+extern void tp_quench();
+/*
+ * NAME: tpin_quench()
+ *
+ * CALLED FROM: tpip_ctlinput()
+ *
+ * FUNCTION and ARGUMENTS: find the tpcb pointer and pass it to tp_quench
+ *
+ * RETURNS: Nada
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+
+void
+tpin_quench(inp)
+ struct inpcb *inp;
+{
+ tp_quench((struct tp_pcb *)inp->inp_socket->so_pcb, PRC_QUENCH);
+}
+
+/*
+ * NAME: tpip_ctlinput()
+ *
+ * CALLED FROM:
+ * The network layer through the protosw table.
+ *
+ * FUNCTION and ARGUMENTS:
+ * When clnp gets an ICMP msg this gets called.
+ * It either returns an error status to the user or
+ * causes all connections on this address to be aborted
+ * by calling the appropriate xx_notify() routine.
+ * (cmd) is the type of ICMP error.
+ * (sa) the address of the sender
+ *
+ * RETURNS: Nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+ProtoHook
+tpip_ctlinput(cmd, sin)
+ int cmd;
+ struct sockaddr_in *sin;
+{
+ extern u_char inetctlerrmap[];
+ extern struct in_addr zeroin_addr;
+ void tp_quench __P((struct inpcb *,int));
+ void tpin_abort __P((struct inpcb *,int));
+
+ if (sin->sin_family != AF_INET && sin->sin_family != AF_IMPLINK)
+ return 0;
+ if (sin->sin_addr.s_addr == INADDR_ANY)
+ return 0;
+ if (cmd < 0 || cmd > PRC_NCMDS)
+ return 0;
+ switch (cmd) {
+
+ case PRC_QUENCH:
+ in_pcbnotify(&tp_inpcb, (struct sockaddr *)sin, 0,
+ zeroin_addr, 0, cmd, tp_quench);
+ break;
+
+ case PRC_ROUTEDEAD:
+ case PRC_HOSTUNREACH:
+ case PRC_UNREACH_NET:
+ case PRC_IFDOWN:
+ case PRC_HOSTDEAD:
+ in_pcbnotify(&tp_inpcb, (struct sockaddr *)sin, 0,
+ zeroin_addr, 0, cmd, in_rtchange);
+ break;
+
+ default:
+ /*
+ case PRC_MSGSIZE:
+ case PRC_UNREACH_HOST:
+ case PRC_UNREACH_PROTOCOL:
+ case PRC_UNREACH_PORT:
+ case PRC_UNREACH_NEEDFRAG:
+ case PRC_UNREACH_SRCFAIL:
+ case PRC_REDIRECT_NET:
+ case PRC_REDIRECT_HOST:
+ case PRC_REDIRECT_TOSNET:
+ case PRC_REDIRECT_TOSHOST:
+ case PRC_TIMXCEED_INTRANS:
+ case PRC_TIMXCEED_REASS:
+ case PRC_PARAMPROB:
+ */
+ in_pcbnotify(&tp_inpcb, (struct sockaddr *)sin, 0,
+ zeroin_addr, 0, cmd, tpin_abort);
+ }
+ return 0;
+}
+
+/*
+ * NAME: tpin_abort()
+ *
+ * CALLED FROM:
+ * xxx_notify() from tp_ctlinput() when
+ * net level gets some ICMP-equiv. type event.
+ *
+ * FUNCTION and ARGUMENTS:
+ * Cause the connection to be aborted with some sort of error
+ * reason indicating that the network layer caused the abort.
+ * Fakes an ER TPDU so we can go through the driver.
+ *
+ * RETURNS: Nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+
+ProtoHook
+tpin_abort(inp)
+ struct inpcb *inp;
+{
+ struct tp_event e;
+
+ e.ev_number = ER_TPDU;
+ e.ATTR(ER_TPDU).e_reason = ENETRESET;
+ (void) tp_driver((struct tp_pcb *)inp->inp_ppcb, &e);
+ return 0;
+}
+
+#ifdef ARGO_DEBUG
+dump_inaddr(addr)
+ register struct sockaddr_in *addr;
+{
+ printf("INET: port 0x%x; addr 0x%x\n", addr->sin_port, addr->sin_addr);
+}
+#endif /* ARGO_DEBUG */
+#endif /* INET */
diff --git a/sys/netiso/tp_input.c b/sys/netiso/tp_input.c
new file mode 100644
index 0000000..a071a5d
--- /dev/null
+++ b/sys/netiso/tp_input.c
@@ -0,0 +1,1624 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_input.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_input.c,v 5.6 88/11/18 17:27:38 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_input.c,v $
+ *
+ * tp_input() gets an mbuf chain from ip. Actually, not directly
+ * from ip, because ip calls a net-level routine that strips off
+ * the net header and then calls tp_input(), passing the proper type
+ * of addresses for the address family in use (how it figures out
+ * which AF is not yet determined.)
+ *
+ * Decomposing the tpdu is some of the most laughable code. The variable-length
+ * parameters and the problem of non-aligned memory references
+ * necessitates such abominations as the macros WHILE_OPTIONS (q.v. below)
+ * to loop through the header and decompose it.
+ *
+ * The routine tp_newsocket() is called when a CR comes in for a listening
+ * socket. tp_input calls sonewconn() and tp_newsocket() to set up the
+ * "child" socket. Most tpcb values are copied from the parent tpcb into
+ * the child.
+ *
+ * Also in here is tp_headersize() (grot) which tells the expected size
+ * of a tp header, to be used by other layers. It's in here because it
+ * uses the static structure tpdu_info.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <netiso/iso.h>
+#include <netiso/iso_errno.h>
+#include <netiso/iso_pcb.h>
+#include <netiso/tp_param.h>
+#include <netiso/tp_timer.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/argo_debug.h>
+#include <netiso/tp_trace.h>
+#include <netiso/tp_tpdu.h>
+
+#include <net/if.h>
+#ifdef TRUE
+#undef FALSE
+#undef TRUE
+#endif
+#include <netccitt/x25.h>
+#include <netccitt/pk.h>
+#include <netccitt/pk_var.h>
+
+int iso_check_csum(), tp_driver(), tp_headersize(), tp_error_emit();
+
+/*
+ #ifdef lint
+ #undef ATTR
+ #define ATTR(X)ev_number
+ #endif lint
+*/
+
+struct mbuf *
+tp_inputprep(m)
+ register struct mbuf *m;
+{
+ int hdrlen;
+
+ IFDEBUG(D_TPINPUT)
+ printf("tp_inputprep: m 0x%x\n", m) ;
+ ENDDEBUG
+
+ while( m->m_len < 1 ) {
+ /* The "m_free" logic
+ * if( (m = m_free(m)) == MNULL )
+ * return (struct mbuf *)0;
+ * would cause a system crash if ever executed.
+ * This logic will be executed if the first mbuf
+ * in the chain only contains a CLNP header. The m_free routine
+ * will release the mbuf containing the CLNP header from the
+ * chain and the new head of the chain will not have the
+ * M_PKTHDR bit set. This routine, tp_inputprep, will
+ * eventually call the "sbappendaddr" routine. "sbappendaddr"
+ * calls "panic" if M_PKTHDR is not set. m_pullup is a cheap
+ * way of keeping the head of the chain from being freed.
+ */
+ if((m = m_pullup(m, 1)) == MNULL)
+ return (MNULL);
+ }
+ if(((int)m->m_data) & 0x3) {
+ /* If we are not 4-byte aligned, we have to be
+ * above the beginning of the mbuf, and it is ok just
+ * to slide it back.
+ */
+ caddr_t ocp = m->m_data;
+
+ m->m_data = (caddr_t)(((int)m->m_data) & ~0x3);
+ bcopy(ocp, m->m_data, (unsigned)m->m_len);
+ }
+ CHANGE_MTYPE(m, TPMT_DATA);
+
+ /* we KNOW that there is at least 1 byte in this mbuf
+ and that it is hdr->tpdu_li XXXXXXX! */
+
+ hdrlen = 1 + *mtod( m, u_char *);
+
+ /*
+ * now pull up the whole tp header
+ */
+ if ( m->m_len < hdrlen) {
+ if ((m = m_pullup(m, hdrlen)) == MNULL ) {
+ IncStat(ts_recv_drop);
+ return (struct mbuf *)0;
+ }
+ }
+ IFDEBUG(D_INPUT)
+ printf(
+ " at end: m 0x%x hdr->tpdu_li 0x%x m_len 0x%x\n",m,
+ hdrlen, m->m_len);
+ ENDDEBUG
+ return m;
+}
+
+/* begin groan
+ * -- this array and the following macros allow you to step through the
+ * parameters of the variable part of a header
+ * note that if for any reason the values of the **_TPDU macros (in tp_events.h)
+ * should change, this array has to be rearranged
+ */
+
+#define TP_LEN_CLASS_0_INDEX 2
+#define TP_MAX_DATA_INDEX 3
+
+static u_char tpdu_info[][4] =
+{
+/* length max data len */
+/* reg fmt xtd fmt class 0 */
+ /* UNUSED 0x0 */ 0x0 , 0x0, 0x0, 0x0,
+ /* XPD_TPDU_type 0x1 */ 0x5, 0x8, 0x0, TP_MAX_XPD_DATA,
+ /* XAK_TPDU_type 0x2 */ 0x5 , 0x8, 0x0, 0x0,
+ /* GR_TPDU_type 0x3 */ 0x0 , 0x0, 0x0, 0x0,
+ /* UNUSED 0x4 */ 0x0 , 0x0, 0x0, 0x0,
+ /* UNUSED 0x5 */ 0x0 , 0x0, 0x0, 0x0,
+ /* AK_TPDU_type 0x6 */ 0x5, 0xa, 0x0, 0x0,
+ /* ER_TPDU_type 0x7 */ 0x5, 0x5, 0x0, 0x0,
+ /* DR_TPDU_type 0x8 */ 0x7, 0x7, 0x7, TP_MAX_DR_DATA,
+ /* UNUSED 0x9 */ 0x0 , 0x0, 0x0, 0x0,
+ /* UNUSED 0xa */ 0x0 , 0x0, 0x0, 0x0,
+ /* UNUSED 0xb */ 0x0 , 0x0, 0x0, 0x0,
+ /* DC_TPDU_type 0xc */ 0x6, 0x6, 0x0, 0x0,
+ /* CC_TPDU_type 0xd */ 0x7, 0x7, 0x7, TP_MAX_CC_DATA,
+ /* CR_TPDU_type 0xe */ 0x7, 0x7, 0x7, TP_MAX_CR_DATA,
+ /* DT_TPDU_type 0xf */ 0x5, 0x8, 0x3, 0x0,
+};
+
+#define CHECK(Phrase, Erval, Stat, Whattodo, Loc)\
+ if (Phrase) {error = (Erval); errlen = (int)(Loc); IncStat(Stat);\
+ goto Whattodo; }
+
+/*
+ * WHENEVER YOU USE THE FOLLOWING MACRO,
+ * BE SURE THE TPDUTYPE IS A LEGIT VALUE FIRST!
+ */
+
+#define WHILE_OPTIONS(P, hdr, format)\
+{ register caddr_t P = tpdu_info[(hdr)->tpdu_type][(format)] + (caddr_t)hdr;\
+ caddr_t PLIM = 1 + hdr->tpdu_li + (caddr_t)hdr;\
+ for (;; P += 2 + ((struct tp_vbp *)P)->tpv_len) {\
+ CHECK((P > PLIM), E_TP_LENGTH_INVAL, ts_inv_length,\
+ respond, P - (caddr_t)hdr);\
+ if (P == PLIM) break;
+
+#define END_WHILE_OPTIONS(P) } }
+
+/* end groan */
+
+/*
+ * NAME: tp_newsocket()
+ *
+ * CALLED FROM:
+ * tp_input() on incoming CR, when a socket w/ the called suffix
+ * is awaiting a connection request
+ *
+ * FUNCTION and ARGUMENTS:
+ * Create a new socket structure, attach to it a new transport pcb,
+ * using a copy of the net level pcb for the parent socket.
+ * (so) is the parent socket.
+ * (fname) is the foreign address (all that's used is the nsap portion)
+ *
+ * RETURN VALUE:
+ * a new socket structure, being this end of the newly formed connection.
+ *
+ * SIDE EFFECTS:
+ * Sets a few things in the tpcb and net level pcb
+ *
+ * NOTES:
+ */
+static struct socket *
+tp_newsocket(so, fname, cons_channel, class_to_use, netservice)
+ struct socket *so;
+ struct sockaddr *fname;
+ caddr_t cons_channel;
+ u_char class_to_use;
+ u_int netservice;
+{
+ register struct tp_pcb *tpcb = sototpcb(so); /* old tpcb, needed below */
+ register struct tp_pcb *newtpcb;
+
+ /*
+ * sonewconn() gets a new socket structure,
+ * a new lower layer pcb and a new tpcb,
+ * but the pcbs are unnamed (not bound)
+ */
+ IFTRACE(D_NEWSOCK)
+ tptraceTPCB(TPPTmisc, "newsock: listg_so, _tpcb, so_head",
+ so, tpcb, so->so_head, 0);
+ ENDTRACE
+
+ if ((so = sonewconn(so, SS_ISCONFIRMING)) == (struct socket *)0)
+ return so;
+ IFTRACE(D_NEWSOCK)
+ tptraceTPCB(TPPTmisc, "newsock: after newconn so, so_head",
+ so, so->so_head, 0, 0);
+ ENDTRACE
+
+ IFDEBUG(D_NEWSOCK)
+ printf("tp_newsocket(channel 0x%x) after sonewconn so 0x%x \n",
+ cons_channel, so);
+ dump_addr(fname);
+ {
+ struct socket *t, *head ;
+
+ head = so->so_head;
+ t = so;
+ printf("so 0x%x so_head 0x%x so_q0 0x%x, q0len %d\n",
+ t, t->so_head, t->so_q0, t->so_q0len);
+ while( (t=t->so_q0) && t!= so && t!= head)
+ printf("so 0x%x so_head 0x%x so_q0 0x%x, q0len %d\n",
+ t, t->so_head, t->so_q0, t->so_q0len);
+ }
+ ENDDEBUG
+
+ /*
+ * before we clobber the old tpcb ptr, get these items from the parent pcb
+ */
+ newtpcb = sototpcb(so);
+ newtpcb->_tp_param = tpcb->_tp_param;
+ newtpcb->tp_flags = tpcb->tp_flags;
+ newtpcb->tp_lcredit = tpcb->tp_lcredit;
+ newtpcb->tp_l_tpdusize = tpcb->tp_l_tpdusize;
+ newtpcb->tp_lsuffixlen = tpcb->tp_lsuffixlen;
+ bcopy( tpcb->tp_lsuffix, newtpcb->tp_lsuffix, newtpcb->tp_lsuffixlen);
+
+ if( /* old */ tpcb->tp_ucddata) {
+ /*
+ * These data are the connect- , confirm- or disconnect- data.
+ */
+ struct mbuf *conndata;
+
+ conndata = m_copy(tpcb->tp_ucddata, 0, (int)M_COPYALL);
+ IFDEBUG(D_CONN)
+ dump_mbuf(conndata, "conndata after mcopy");
+ ENDDEBUG
+ newtpcb->tp_ucddata = conndata;
+ }
+
+ tpcb = newtpcb;
+ tpcb->tp_state = TP_LISTENING;
+ tpcb->tp_class = class_to_use;
+ tpcb->tp_netservice = netservice;
+
+
+ ASSERT( fname != 0 ) ; /* just checking */
+ if ( fname ) {
+ /*
+ * tp_route_to takes its address argument in the form of an mbuf.
+ */
+ struct mbuf *m;
+ int err;
+
+ MGET(m, M_DONTWAIT, MT_SONAME); /* mbuf type used is confusing */
+ if (m) {
+ /*
+ * this seems a bit grotesque, but tp_route_to expects
+ * an mbuf * instead of simply a sockaddr; it calls the ll
+ * pcb_connect, which expects the name/addr in an mbuf as well.
+ * sigh.
+ */
+ bcopy((caddr_t)fname, mtod(m, caddr_t), fname->sa_len);
+ m->m_len = fname->sa_len;
+
+ /* grot : have to say the kernel can override params in
+ * the passive open case
+ */
+ tpcb->tp_dont_change_params = 0;
+ err = tp_route_to( m, tpcb, cons_channel);
+ m_free(m);
+
+ if (!err)
+ goto ok;
+ }
+ IFDEBUG(D_CONN)
+ printf("tp_route_to FAILED! detaching tpcb 0x%x, so 0x%x\n",
+ tpcb, so);
+ ENDDEBUG
+ (void) tp_detach(tpcb);
+ return 0;
+ }
+ok:
+ IFDEBUG(D_TPINPUT)
+ printf("tp_newsocket returning so 0x%x, sototpcb(so) 0x%x\n",
+ so, sototpcb(so));
+ ENDDEBUG
+ return so;
+}
+
+#ifndef TPCONS
+tpcons_output()
+{
+ return(0);
+}
+#endif /* !CONS */
+
+/*
+ * NAME: tp_input()
+ *
+ * CALLED FROM:
+ * net layer input routine
+ *
+ * FUNCTION and ARGUMENTS:
+ * Process an incoming TPDU (m), finding the associated tpcb if there
+ * is one. Create the appropriate type of event and call the driver.
+ * (faddr) and (laddr) are the foreign and local addresses.
+ *
+ * When tp_input() is called we KNOW that the ENTIRE TP HEADER
+ * has been m_pullup-ed.
+ *
+ * RETURN VALUE: Nada
+ *
+ * SIDE EFFECTS:
+ * When using COSNS it may affect the state of the net-level pcb
+ *
+ * NOTE:
+ * The initial value of acktime is 2 so that we will never
+ * have a 0 value for tp_peer_acktime. It gets used in the
+ * computation of the retransmission timer value, and so it
+ * mustn't be zero.
+ * 2 seems like a reasonable minimum.
+ */
+ProtoHook
+tp_input(m, faddr, laddr, cons_channel, dgout_routine, ce_bit)
+ register struct mbuf *m;
+ struct sockaddr *faddr, *laddr; /* NSAP addresses */
+ caddr_t cons_channel;
+ int (*dgout_routine)();
+ int ce_bit;
+
+{
+ register struct tp_pcb *tpcb;
+ register struct tpdu *hdr;
+ struct socket *so;
+ struct tp_event e;
+ int error;
+ unsigned dutype;
+ u_short dref, sref, acktime, subseq;
+ u_char preferred_class, class_to_use, pdusize;
+ u_char opt, dusize, addlopt, version;
+#ifdef TP_PERF_MEAS
+ u_char perf_meas;
+#endif /* TP_PERF_MEAS */
+ u_char fsufxlen, lsufxlen;
+ caddr_t fsufxloc, lsufxloc;
+ int tpdu_len;
+ u_int takes_data;
+ u_int fcc_present;
+ int errlen;
+ struct tp_conn_param tpp;
+ int tpcons_output();
+
+again:
+ hdr = mtod(m, struct tpdu *);
+ tpcb = 0;
+ error = errlen = tpdu_len = 0;
+ takes_data = fcc_present = FALSE;
+ acktime = 2; sref = subseq = 0;
+ fsufxloc = lsufxloc = NULL;
+ fsufxlen = lsufxlen =
+ preferred_class = class_to_use = pdusize = addlopt = 0;
+ dusize = TP_DFL_TPDUSIZE;
+#ifdef TP_PERF_MEAS
+ GET_CUR_TIME( &e.e_time ); perf_meas = 0;
+#endif /* TP_PERF_MEAS */
+
+ IFDEBUG(D_TPINPUT)
+ printf("tp_input(0x%x, ... 0x%x)\n", m, cons_channel);
+ ENDDEBUG
+
+
+ /*
+ * get the actual tpdu length - necessary for monitoring
+ * and for checksumming
+ *
+ * Also, maybe measure the mbuf chain lengths and sizes.
+ */
+
+ { register struct mbuf *n=m;
+# ifdef ARGO_DEBUG
+ int chain_length = 0;
+# endif ARGO_DEBUG
+
+ for(;;) {
+ tpdu_len += n->m_len;
+ IFDEBUG(D_MBUF_MEAS)
+ if( n->m_flags & M_EXT) {
+ IncStat(ts_mb_cluster);
+ } else {
+ IncStat(ts_mb_small);
+ }
+ chain_length ++;
+ ENDDEBUG
+ if (n->m_next == MNULL ) {
+ break;
+ }
+ n = n->m_next;
+ }
+ IFDEBUG(D_MBUF_MEAS)
+ if(chain_length > 16)
+ chain_length = 0; /* zero used for anything > 16 */
+ tp_stat.ts_mb_len_distr[chain_length] ++;
+ ENDDEBUG
+ }
+ IFTRACE(D_TPINPUT)
+ tptraceTPCB(TPPTtpduin, hdr->tpdu_type, hdr, hdr->tpdu_li+1, tpdu_len,
+ 0);
+ ENDTRACE
+
+ dref = ntohs((short)hdr->tpdu_dref);
+ sref = ntohs((short)hdr->tpdu_sref);
+ dutype = (int)hdr->tpdu_type;
+
+ IFDEBUG(D_TPINPUT)
+ printf("input: dutype 0x%x cons_channel 0x%x dref 0x%x\n", dutype,
+ cons_channel, dref);
+ printf("input: dref 0x%x sref 0x%x\n", dref, sref);
+ ENDDEBUG
+ IFTRACE(D_TPINPUT)
+ tptrace(TPPTmisc, "channel dutype dref ",
+ cons_channel, dutype, dref, 0);
+ ENDTRACE
+
+
+#ifdef ARGO_DEBUG
+ if( (dutype < TP_MIN_TPDUTYPE) || (dutype > TP_MAX_TPDUTYPE)) {
+ printf("BAD dutype! 0x%x, channel 0x%x dref 0x%x\n",
+ dutype, cons_channel, dref);
+ dump_buf (m, sizeof( struct mbuf ));
+
+ IncStat(ts_inv_dutype);
+ goto discard;
+ }
+#endif /* ARGO_DEBUG */
+
+ CHECK( (dutype < TP_MIN_TPDUTYPE || dutype > TP_MAX_TPDUTYPE),
+ E_TP_INV_TPDU, ts_inv_dutype, respond,
+ 2 );
+ /* unfortunately we can't take the address of the tpdu_type field,
+ * since it's a bit field - so we just use the constant offset 2
+ */
+
+ /* Now this isn't very neat but since you locate a pcb one way
+ * at the beginning of connection establishment, and by
+ * the dref for each tpdu after that, we have to treat CRs differently
+ */
+ if ( dutype == CR_TPDU_type ) {
+ u_char alt_classes = 0;
+
+ preferred_class = 1 << hdr->tpdu_CRclass;
+ opt = hdr->tpdu_CRoptions;
+
+ WHILE_OPTIONS(P, hdr, 1 ) /* { */
+
+ switch( vbptr(P)->tpv_code ) {
+
+ case TPP_tpdu_size:
+ vb_getval(P, u_char, dusize);
+ IFDEBUG(D_TPINPUT)
+ printf("CR dusize 0x%x\n", dusize);
+ ENDDEBUG
+ /* COS tests: NBS IA (Dec. 1987) Sec. 4.5.2.1 */
+ if (dusize < TP_MIN_TPDUSIZE || dusize > TP_MAX_TPDUSIZE)
+ dusize = TP_DFL_TPDUSIZE;
+ break;
+ case TPP_ptpdu_size:
+ switch (vbptr(P)->tpv_len) {
+ case 1: pdusize = vbval(P, u_char); break;
+ case 2: pdusize = ntohs(vbval(P, u_short)); break;
+ default: ;
+ IFDEBUG(D_TPINPUT)
+ printf("malformed prefered TPDU option\n");
+ ENDDEBUG
+ }
+ break;
+ case TPP_addl_opt:
+ vb_getval(P, u_char, addlopt);
+ break;
+ case TPP_calling_sufx:
+ /* could use vb_getval, but we want to save the loc & len
+ * for later use
+ */
+ fsufxloc = (caddr_t) &vbptr(P)->tpv_val;
+ fsufxlen = vbptr(P)->tpv_len;
+ IFDEBUG(D_TPINPUT)
+ printf("CR fsufx:");
+ { register int j;
+ for(j=0; j<fsufxlen; j++ ) {
+ printf(" 0x%x. ", *((caddr_t)(fsufxloc+j)) );
+ }
+ printf("\n");
+ }
+ ENDDEBUG
+ break;
+ case TPP_called_sufx:
+ /* could use vb_getval, but we want to save the loc & len
+ * for later use
+ */
+ lsufxloc = (caddr_t) &vbptr(P)->tpv_val;
+ lsufxlen = vbptr(P)->tpv_len;
+ IFDEBUG(D_TPINPUT)
+ printf("CR lsufx:");
+ { register int j;
+ for(j=0; j<lsufxlen; j++ ) {
+ printf(" 0x%x. ", *((u_char *)(lsufxloc+j)) );
+ }
+ printf("\n");
+ }
+ ENDDEBUG
+ break;
+
+#ifdef TP_PERF_MEAS
+ case TPP_perf_meas:
+ vb_getval(P, u_char, perf_meas);
+ break;
+#endif /* TP_PERF_MEAS */
+
+ case TPP_vers:
+ /* not in class 0; 1 octet; in CR_TPDU only */
+ /* COS tests says if version wrong, use default version!?XXX */
+ CHECK( (vbval(P, u_char) != TP_VERSION ),
+ E_TP_INV_PVAL, ts_inv_pval, setversion,
+ (1 + (caddr_t)&vbptr(P)->tpv_val - (caddr_t)hdr) );
+ setversion:
+ version = vbval(P, u_char);
+ break;
+ case TPP_acktime:
+ vb_getval(P, u_short, acktime);
+ acktime = ntohs(acktime);
+ acktime = acktime/500; /* convert to slowtimo ticks */
+ if((short)acktime <=0 )
+ acktime = 2; /* don't allow a bad peer to screw us up */
+ IFDEBUG(D_TPINPUT)
+ printf("CR acktime 0x%x\n", acktime);
+ ENDDEBUG
+ break;
+
+ case TPP_alt_class:
+ {
+ u_char *aclass = 0;
+ register int i;
+ static u_char bad_alt_classes[5] =
+ { ~0, ~3, ~5, ~0xf, ~0x1f};
+
+ aclass =
+ (u_char *) &(((struct tp_vbp *)P)->tpv_val);
+ for (i = ((struct tp_vbp *)P)->tpv_len; i>0; i--) {
+ alt_classes |= (1<<((*aclass++)>>4));
+ }
+ CHECK( (bad_alt_classes[hdr->tpdu_CRclass] & alt_classes),
+ E_TP_INV_PVAL, ts_inv_aclass, respond,
+ ((caddr_t)aclass) - (caddr_t)hdr);
+ IFDEBUG(D_TPINPUT)
+ printf("alt_classes 0x%x\n", alt_classes);
+ ENDDEBUG
+ }
+ break;
+
+ case TPP_security:
+ case TPP_residER:
+ case TPP_priority:
+ case TPP_transdelay:
+ case TPP_throughput:
+ case TPP_addl_info:
+ case TPP_subseq:
+ default:
+ IFDEBUG(D_TPINPUT)
+ printf("param ignored CR_TPDU code= 0x%x\n",
+ vbptr(P)->tpv_code);
+ ENDDEBUG
+ IncStat(ts_param_ignored);
+ break;
+
+ case TPP_checksum:
+ IFDEBUG(D_TPINPUT)
+ printf("CR before cksum\n");
+ ENDDEBUG
+
+ CHECK( iso_check_csum(m, tpdu_len),
+ E_TP_INV_PVAL, ts_bad_csum, discard, 0)
+
+ IFDEBUG(D_TPINPUT)
+ printf("CR before cksum\n");
+ ENDDEBUG
+ break;
+ }
+
+ /* } */ END_WHILE_OPTIONS(P)
+
+ if (lsufxlen == 0) {
+ /* can't look for a tpcb w/o any called sufx */
+ error = E_TP_LENGTH_INVAL;
+ IncStat(ts_inv_sufx);
+ goto respond;
+ } else {
+ register struct tp_pcb *t;
+ /*
+ * The intention here is to trap all CR requests
+ * to a given nsap, for constructing transport
+ * service bridges at user level; so these
+ * intercepts should precede the normal listens.
+ * Phrasing the logic in this way also allows for
+ * mop-up listeners, which we don't currently implement.
+ * We also wish to have a single socket be able to
+ * listen over any network service provider,
+ * (cons or clns or ip).
+ */
+ for (t = tp_listeners; t ; t = t->tp_nextlisten)
+ if ((t->tp_lsuffixlen == 0 ||
+ (lsufxlen == t->tp_lsuffixlen &&
+ bcmp(lsufxloc, t->tp_lsuffix, lsufxlen) == 0)) &&
+ ((t->tp_flags & TPF_GENERAL_ADDR) ||
+ (laddr->sa_family == t->tp_domain &&
+ (*t->tp_nlproto->nlp_cmpnetaddr)
+ (t->tp_npcb, laddr, TP_LOCAL))))
+ break;
+
+ CHECK(t == 0, E_TP_NO_SESSION, ts_inv_sufx, respond,
+ (1 + 2 + (caddr_t)&hdr->_tpduf - (caddr_t)hdr))
+ /* _tpduf is the fixed part; add 2 to get the dref bits of
+ * the fixed part (can't take the address of a bit field)
+ */
+ IFDEBUG(D_TPINPUT)
+ printf("checking if dup CR\n");
+ ENDDEBUG
+ tpcb = t;
+ for (t = tpcb->tp_next; t != tpcb; t = t->tp_next) {
+ if (sref != t->tp_fref)
+ continue;
+ if ((*tpcb->tp_nlproto->nlp_cmpnetaddr)(
+ t->tp_npcb, faddr, TP_FOREIGN)) {
+ IFDEBUG(D_TPINPUT)
+ printf("duplicate CR discarded\n");
+ ENDDEBUG
+ goto discard;
+ }
+ }
+ IFTRACE(D_TPINPUT)
+ tptrace(TPPTmisc, "tp_input: tpcb *lsufxloc tpstate",
+ tpcb, *lsufxloc, tpcb->tp_state, 0);
+ ENDTRACE
+ }
+
+ /*
+ * WE HAVE A TPCB
+ * already know that the classes in the CR match at least
+ * one class implemented, but we don't know yet if they
+ * include any classes permitted by this server.
+ */
+
+ IFDEBUG(D_TPINPUT)
+ printf("HAVE A TPCB 1: 0x%x\n", tpcb);
+ ENDDEBUG
+ IFDEBUG(D_CONN)
+ printf(
+"CR: bef CHKS: flags 0x%x class_to_use 0x%x alt 0x%x opt 0x%x tp_class 0x%x\n",
+ tpcb->tp_flags, class_to_use, alt_classes, opt, tpcb->tp_class);
+ ENDDEBUG
+ /* tpcb->tp_class doesn't include any classes not implemented */
+ class_to_use = (preferred_class & tpcb->tp_class);
+ if( (class_to_use = preferred_class & tpcb->tp_class) == 0 )
+ class_to_use = alt_classes & tpcb->tp_class;
+
+ class_to_use = 1 << tp_mask_to_num(class_to_use);
+
+ {
+ tpp = tpcb->_tp_param;
+ tpp.p_class = class_to_use;
+ tpp.p_tpdusize = dusize;
+ tpp.p_ptpdusize = pdusize;
+ tpp.p_xtd_format = (opt & TPO_XTD_FMT) == TPO_XTD_FMT;
+ tpp.p_xpd_service = (addlopt & TPAO_USE_TXPD) == TPAO_USE_TXPD;
+ tpp.p_use_checksum = (tpp.p_class == TP_CLASS_0)?0:
+ (addlopt & TPAO_NO_CSUM) == 0;
+ tpp.p_version = version;
+#ifdef notdef
+ tpp.p_use_efc = (opt & TPO_USE_EFC) == TPO_USE_EFC;
+ tpp.p_use_nxpd = (addlopt & TPAO_USE_NXPD) == TPAO_USE_NXPD;
+ tpp.p_use_rcc = (addlopt & TPAO_USE_RCC) == TPAO_USE_RCC;
+#endif /* notdef */
+
+ CHECK(
+ tp_consistency(tpcb, 0 /* not force or strict */, &tpp) != 0,
+ E_TP_NEGOT_FAILED, ts_negotfailed, clear_parent_tcb,
+ (1 + 2 + (caddr_t)&hdr->_tpdufr.CRCC - (caddr_t)hdr)
+ /* ^ more or less the location of class */
+ )
+ }
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc,
+ "after 1 consist class_to_use class, out, tpconsout",
+ class_to_use,
+ tpcb->tp_class, dgout_routine, tpcons_output
+ );
+ ENDTRACE
+ CHECK(
+ ((class_to_use == TP_CLASS_0)&&(dgout_routine != tpcons_output)),
+ E_TP_NEGOT_FAILED, ts_negotfailed, clear_parent_tcb,
+ (1 + 2 + (caddr_t)&hdr->_tpdufr.CRCC - (caddr_t)hdr)
+ /* ^ more or less the location of class */
+ )
+ IFDEBUG(D_CONN)
+ printf("CR: after CRCCCHECKS: tpcb 0x%x, flags 0x%x\n",
+ tpcb, tpcb->tp_flags);
+ ENDDEBUG
+ takes_data = TRUE;
+ e.ATTR(CR_TPDU).e_cdt = hdr->tpdu_CRcdt;
+ e.ev_number = CR_TPDU;
+
+ so = tpcb->tp_sock;
+ if (so->so_options & SO_ACCEPTCONN) {
+ struct tp_pcb *parent_tpcb = tpcb;
+ /*
+ * Create a socket, tpcb, ll pcb, etc.
+ * for this newborn connection, and fill in all the values.
+ */
+ IFDEBUG(D_CONN)
+ printf("abt to call tp_newsocket(0x%x, 0x%x, 0x%x, 0x%x)\n",
+ so, laddr, faddr, cons_channel);
+ ENDDEBUG
+ if( (so =
+ tp_newsocket(so, faddr, cons_channel,
+ class_to_use,
+ ((tpcb->tp_netservice == IN_CLNS) ? IN_CLNS :
+ (dgout_routine == tpcons_output)?ISO_CONS:ISO_CLNS))
+ ) == (struct socket *)0 ) {
+ /* note - even if netservice is IN_CLNS, as far as
+ * the tp entity is concerned, the only differences
+ * are CO vs CL
+ */
+ IFDEBUG(D_CONN)
+ printf("tp_newsocket returns 0\n");
+ ENDDEBUG
+ goto discard;
+ clear_parent_tcb:
+ tpcb = 0;
+ goto respond;
+ }
+ tpcb = sototpcb(so);
+ insque(tpcb, parent_tpcb);
+
+ /*
+ * Stash the addresses in the net level pcb
+ * kind of like a pcbconnect() but don't need
+ * or want all those checks.
+ */
+ (tpcb->tp_nlproto->nlp_putnetaddr)(tpcb->tp_npcb, faddr, TP_FOREIGN);
+ (tpcb->tp_nlproto->nlp_putnetaddr)(tpcb->tp_npcb, laddr, TP_LOCAL);
+
+ /* stash the f suffix in the new tpcb */
+ if (tpcb->tp_fsuffixlen = fsufxlen) {
+ bcopy(fsufxloc, tpcb->tp_fsuffix, fsufxlen);
+ (tpcb->tp_nlproto->nlp_putsufx)
+ (tpcb->tp_npcb, fsufxloc, fsufxlen, TP_FOREIGN);
+ }
+ /* stash the l suffix in the new tpcb */
+ tpcb->tp_lsuffixlen = lsufxlen;
+ bcopy(lsufxloc, tpcb->tp_lsuffix, lsufxlen);
+ (tpcb->tp_nlproto->nlp_putsufx)
+ (tpcb->tp_npcb, lsufxloc, lsufxlen, TP_LOCAL);
+#ifdef TP_PERF_MEAS
+ if( tpcb->tp_perf_on = perf_meas ) { /* assignment */
+ /* ok, let's create an mbuf for stashing the
+ * statistics if one doesn't already exist
+ */
+ (void) tp_setup_perf(tpcb);
+ }
+#endif /* TP_PERF_MEAS */
+ tpcb->tp_fref = sref;
+
+ /* We've already checked for consistency with the options
+ * set in tpp, but we couldn't set them earlier because
+ * we didn't want to change options in the LISTENING tpcb.
+ * Now we set the options in the new socket's tpcb.
+ */
+ (void) tp_consistency( tpcb, TP_FORCE, &tpp);
+
+ if(!tpcb->tp_use_checksum)
+ IncStat(ts_csum_off);
+ if(tpcb->tp_xpd_service)
+ IncStat(ts_use_txpd);
+ if(tpcb->tp_xtd_format)
+ IncStat(ts_xtd_fmt);
+
+ tpcb->tp_peer_acktime = acktime;
+
+ /*
+ * The following kludge is used to test retransmissions and
+ * timeout during connection establishment.
+ */
+ IFDEBUG(D_ZDREF)
+ IncStat(ts_zdebug);
+ /*tpcb->tp_fref = 0;*/
+ ENDDEBUG
+ }
+ LOCAL_CREDIT(tpcb);
+ IncStat(ts_CR_rcvd);
+ if (!tpcb->tp_cebit_off) {
+ tpcb->tp_win_recv = tp_start_win << 8;
+ tpcb->tp_cong_sample.cs_size = 0;
+ CONG_INIT_SAMPLE(tpcb);
+ CONG_UPDATE_SAMPLE(tpcb, ce_bit);
+ }
+ } else if ( dutype == ER_TPDU_type ) {
+ /*
+ * ER TPDUs have to be recognized separately
+ * because they don't necessarily have a tpcb
+ * with them and we don't want err out looking for such
+ * a beast.
+ * We could put a bunch of little kludges in the
+ * next section of code so it would avoid references to tpcb
+ * if dutype == ER_TPDU_type but we don't want code for ERs to
+ * mess up code for data transfer.
+ */
+ IncStat(ts_ER_rcvd);
+ e.ev_number = ER_TPDU;
+ e.ATTR(ER_TPDU).e_reason = (u_char)hdr->tpdu_ERreason;
+ CHECK (((int)dref <= 0 || dref >= tp_refinfo.tpr_size ||
+ (tpcb = tp_ref[dref].tpr_pcb ) == (struct tp_pcb *) 0 ||
+ tpcb->tp_refstate == REF_FREE ||
+ tpcb->tp_refstate == REF_FROZEN),
+ E_TP_MISM_REFS, ts_inv_dref, discard, 0)
+
+ } else {
+ /* tpdu type is CC, XPD, XAK, GR, AK, DR, DC, or DT */
+
+ /* In the next 4 checks,
+ * _tpduf is the fixed part; add 2 to get the dref bits of
+ * the fixed part (can't take the address of a bit field)
+ */
+#ifdef TPCONS
+ if (cons_channel && dutype == DT_TPDU_type) {
+ struct isopcb *isop = ((struct isopcb *)
+ ((struct pklcd *)cons_channel)->lcd_upnext);
+ if (isop && isop->isop_refcnt == 1 && isop->isop_socket &&
+ (tpcb = sototpcb(isop->isop_socket)) &&
+ (tpcb->tp_class == TP_CLASS_0/* || == CLASS_1 */)) {
+ IFDEBUG(D_TPINPUT)
+ printf("tpinput_dt: class 0 short circuit\n");
+ ENDDEBUG
+ dref = tpcb->tp_lref;
+ sref = tpcb->tp_fref;
+ CHECK( (tpcb->tp_refstate == REF_FREE),
+ E_TP_MISM_REFS,ts_inv_dref, nonx_dref,
+ (1 + 2 + (caddr_t)&hdr->_tpduf - (caddr_t)hdr))
+ goto tp0_data;
+ }
+
+ }
+#endif
+ {
+
+ CHECK( ((int)dref <= 0 || dref >= tp_refinfo.tpr_size) ,
+ E_TP_MISM_REFS,ts_inv_dref, nonx_dref,
+ (1 + 2 + (caddr_t)&hdr->_tpduf - (caddr_t)hdr))
+ CHECK( ((tpcb = tp_ref[dref].tpr_pcb ) == (struct tp_pcb *) 0 ),
+ E_TP_MISM_REFS,ts_inv_dref, nonx_dref,
+ (1 + 2 + (caddr_t)&hdr->_tpduf - (caddr_t)hdr))
+ CHECK( (tpcb->tp_refstate == REF_FREE),
+ E_TP_MISM_REFS,ts_inv_dref, nonx_dref,
+ (1 + 2 + (caddr_t)&hdr->_tpduf - (caddr_t)hdr))
+ }
+
+ IFDEBUG(D_TPINPUT)
+ printf("HAVE A TPCB 2: 0x%x\n", tpcb);
+ ENDDEBUG
+
+ /* causes a DR to be sent for CC; ER for all else */
+ CHECK( (tpcb->tp_refstate == REF_FROZEN),
+ (dutype == CC_TPDU_type?E_TP_NO_SESSION:E_TP_MISM_REFS),
+ ts_inv_dref, respond,
+ (1 + 2 + (caddr_t)&hdr->_tpduf - (caddr_t)hdr))
+
+ IFDEBUG(D_TPINPUT)
+ printf("state of dref %d ok, tpcb 0x%x\n", dref,tpcb);
+ ENDDEBUG
+ /*
+ * At this point the state of the dref could be
+ * FROZEN: tpr_pcb == NULL, has ( reference only) timers
+ * for example, DC may arrive after the close() has detached
+ * the tpcb (e.g., if user turned off SO_LISTEN option)
+ * OPENING : a tpcb exists but no timers yet
+ * OPEN : tpcb exists & timers are outstanding
+ */
+
+ if (!tpcb->tp_cebit_off)
+ CONG_UPDATE_SAMPLE(tpcb, ce_bit);
+
+ dusize = tpcb->tp_tpdusize;
+ pdusize = tpcb->tp_ptpdusize;
+
+ dutype = hdr->tpdu_type << 8; /* for the switch below */
+
+ WHILE_OPTIONS(P, hdr, tpcb->tp_xtd_format) /* { */
+
+#define caseof(x,y) case (((x)<<8)+(y))
+ switch( dutype | vbptr(P)->tpv_code ) {
+
+ caseof( CC_TPDU_type, TPP_addl_opt ):
+ /* not in class 0; 1 octet */
+ vb_getval(P, u_char, addlopt);
+ break;
+ caseof( CC_TPDU_type, TPP_tpdu_size ):
+ {
+ u_char odusize = dusize;
+ vb_getval(P, u_char, dusize);
+ CHECK( (dusize < TP_MIN_TPDUSIZE ||
+ dusize > TP_MAX_TPDUSIZE || dusize > odusize),
+ E_TP_INV_PVAL, ts_inv_pval, respond,
+ (1 + (caddr_t)&vbptr(P)->tpv_val - (caddr_t)hdr) )
+ IFDEBUG(D_TPINPUT)
+ printf("CC dusize 0x%x\n", dusize);
+ ENDDEBUG
+ }
+ break;
+ caseof( CC_TPDU_type, TPP_ptpdu_size ):
+ {
+ u_short opdusize = pdusize;
+ switch (vbptr(P)->tpv_len) {
+ case 1: pdusize = vbval(P, u_char); break;
+ case 2: pdusize = ntohs(vbval(P, u_short)); break;
+ default: ;
+ IFDEBUG(D_TPINPUT)
+ printf("malformed prefered TPDU option\n");
+ ENDDEBUG
+ }
+ CHECK( (pdusize == 0 ||
+ (opdusize && (pdusize > opdusize))),
+ E_TP_INV_PVAL, ts_inv_pval, respond,
+ (1 + (caddr_t)&vbptr(P)->tpv_val - (caddr_t)hdr) )
+ }
+ break;
+ caseof( CC_TPDU_type, TPP_calling_sufx):
+ IFDEBUG(D_TPINPUT)
+ printf("CC calling (local) sufxlen 0x%x\n", lsufxlen);
+ ENDDEBUG
+ lsufxloc = (caddr_t) &vbptr(P)->tpv_val;
+ lsufxlen = vbptr(P)->tpv_len;
+ break;
+ caseof( CC_TPDU_type, TPP_acktime ):
+ /* class 4 only, 2 octets */
+ vb_getval(P, u_short, acktime);
+ acktime = ntohs(acktime);
+ acktime = acktime/500; /* convert to slowtimo ticks */
+ if( (short)acktime <=0 )
+ acktime = 2;
+ break;
+ caseof( CC_TPDU_type, TPP_called_sufx):
+ fsufxloc = (caddr_t) &vbptr(P)->tpv_val;
+ fsufxlen = vbptr(P)->tpv_len;
+ IFDEBUG(D_TPINPUT)
+ printf("CC called (foreign) sufx len %d\n", fsufxlen);
+ ENDDEBUG
+ break;
+
+ caseof( CC_TPDU_type, TPP_checksum):
+ caseof( DR_TPDU_type, TPP_checksum):
+ caseof( DT_TPDU_type, TPP_checksum):
+ caseof( XPD_TPDU_type, TPP_checksum):
+ if( tpcb->tp_use_checksum ) {
+ CHECK( iso_check_csum(m, tpdu_len),
+ E_TP_INV_PVAL, ts_bad_csum, discard, 0)
+ }
+ break;
+
+ /* this is different from the above because in the context
+ * of concat/ sep tpdu_len might not be the same as hdr len
+ */
+ caseof( AK_TPDU_type, TPP_checksum):
+ caseof( XAK_TPDU_type, TPP_checksum):
+ caseof( DC_TPDU_type, TPP_checksum):
+ if( tpcb->tp_use_checksum ) {
+ CHECK( iso_check_csum(m, (int)hdr->tpdu_li + 1),
+ E_TP_INV_PVAL, ts_bad_csum, discard, 0)
+ }
+ break;
+#ifdef notdef
+ caseof( DR_TPDU_type, TPP_addl_info ):
+ /* ignore - its length and meaning are
+ * user defined and there's no way
+ * to pass this info to the user anyway
+ */
+ break;
+#endif /* notdef */
+
+ caseof( AK_TPDU_type, TPP_subseq ):
+ /* used after reduction of window */
+ vb_getval(P, u_short, subseq);
+ subseq = ntohs(subseq);
+ IFDEBUG(D_ACKRECV)
+ printf("AK dref 0x%x Subseq 0x%x\n", dref, subseq);
+ ENDDEBUG
+ break;
+
+ caseof( AK_TPDU_type, TPP_flow_cntl_conf ):
+ {
+ u_int ylwe;
+ u_short ysubseq, ycredit;
+
+ fcc_present = TRUE;
+ vb_getval(P, u_int, ylwe);
+ vb_getval(P, u_short, ysubseq);
+ vb_getval(P, u_short, ycredit);
+ ylwe = ntohl(ylwe);
+ ysubseq = ntohs(ysubseq);
+ ycredit = ntohs(ycredit);
+ IFDEBUG(D_ACKRECV)
+ printf("%s%x, subseq 0x%x, cdt 0x%x dref 0x%x\n",
+ "AK FCC lwe 0x", ylwe, ysubseq, ycredit, dref);
+ ENDDEBUG
+ }
+ break;
+
+ default:
+ IFDEBUG(D_TPINPUT)
+ printf("param ignored dutype 0x%x, code 0x%x\n",
+ dutype, vbptr(P)->tpv_code);
+ ENDDEBUG
+ IFTRACE(D_TPINPUT)
+ tptrace(TPPTmisc, "param ignored dutype code ",
+ dutype, vbptr(P)->tpv_code ,0,0);
+ ENDTRACE
+ IncStat(ts_param_ignored);
+ break;
+#undef caseof
+ }
+ /* } */ END_WHILE_OPTIONS(P)
+
+ /* NOTE: the variable dutype has been shifted left! */
+
+ switch( hdr->tpdu_type ) {
+ case CC_TPDU_type:
+ /* If CC comes back with an unacceptable class
+ * respond with a DR or ER
+ */
+
+ opt = hdr->tpdu_CCoptions; /* 1 byte */
+
+ {
+ tpp = tpcb->_tp_param;
+ tpp.p_class = (1<<hdr->tpdu_CCclass);
+ tpp.p_tpdusize = dusize;
+ tpp.p_ptpdusize = pdusize;
+ tpp.p_dont_change_params = 0;
+ tpp.p_xtd_format = (opt & TPO_XTD_FMT) == TPO_XTD_FMT;
+ tpp.p_xpd_service = (addlopt & TPAO_USE_TXPD) == TPAO_USE_TXPD;
+ tpp.p_use_checksum = (addlopt & TPAO_NO_CSUM) == 0;
+#ifdef notdef
+ tpp.p_use_efc = (opt & TPO_USE_EFC) == TPO_USE_EFC;
+ tpp.p_use_nxpd = (addlopt & TPAO_USE_NXPD) == TPAO_USE_NXPD;
+ tpp.p_use_rcc = (addlopt & TPAO_USE_RCC) == TPAO_USE_RCC;
+#endif /* notdef */
+
+ CHECK(
+ tp_consistency(tpcb, TP_FORCE, &tpp) != 0,
+ E_TP_NEGOT_FAILED, ts_negotfailed, respond,
+ (1 + 2 + (caddr_t)&hdr->_tpdufr.CRCC - (caddr_t)hdr)
+ /* ^ more or less the location of class */
+ )
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc,
+ "after 1 consist class, out, tpconsout",
+ tpcb->tp_class, dgout_routine, tpcons_output, 0
+ );
+ ENDTRACE
+ CHECK(
+ ((class_to_use == TP_CLASS_0)&&
+ (dgout_routine != tpcons_output)),
+ E_TP_NEGOT_FAILED, ts_negotfailed, respond,
+ (1 + 2 + (caddr_t)&hdr->_tpdufr.CRCC - (caddr_t)hdr)
+ /* ^ more or less the location of class */
+ )
+#ifdef TPCONS
+ if (tpcb->tp_netservice == ISO_CONS &&
+ class_to_use == TP_CLASS_0) {
+ struct isopcb *isop = (struct isopcb *)tpcb->tp_npcb;
+ struct pklcd *lcp = (struct pklcd *)isop->isop_chan;
+ lcp->lcd_flags &= ~X25_DG_CIRCUIT;
+ }
+#endif
+ }
+ if( ! tpcb->tp_use_checksum)
+ IncStat(ts_csum_off);
+ if(tpcb->tp_xpd_service)
+ IncStat(ts_use_txpd);
+ if(tpcb->tp_xtd_format)
+ IncStat(ts_xtd_fmt);
+
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc, "after CC class flags dusize CCclass",
+ tpcb->tp_class, tpcb->tp_flags, tpcb->tp_tpdusize,
+ hdr->tpdu_CCclass);
+ ENDTRACE
+
+ /* if called or calling suffices appeared on the CC,
+ * they'd better jive with what's in the pcb
+ */
+ if( fsufxlen ) {
+ CHECK( ((tpcb->tp_fsuffixlen != fsufxlen) ||
+ bcmp(fsufxloc, tpcb->tp_fsuffix, fsufxlen)),
+ E_TP_INV_PVAL,ts_inv_sufx, respond,
+ (1+fsufxloc - (caddr_t)hdr))
+ }
+ if( lsufxlen ) {
+ CHECK( ((tpcb->tp_lsuffixlen != lsufxlen) ||
+ bcmp(lsufxloc, tpcb->tp_lsuffix, lsufxlen)),
+ E_TP_INV_PVAL,ts_inv_sufx, respond,
+ (1+lsufxloc - (caddr_t)hdr))
+ }
+
+ e.ATTR(CC_TPDU).e_sref = sref;
+ e.ATTR(CC_TPDU).e_cdt = hdr->tpdu_CCcdt;
+ takes_data = TRUE;
+ e.ev_number = CC_TPDU;
+ IncStat(ts_CC_rcvd);
+ break;
+
+ case DC_TPDU_type:
+ if (sref != tpcb->tp_fref)
+ printf("INPUT: inv sufx DCsref 0x%x, tp_fref 0x%x\n",
+ sref, tpcb->tp_fref);
+
+ CHECK( (sref != tpcb->tp_fref),
+ E_TP_MISM_REFS, ts_inv_sufx, discard,
+ (1 + (caddr_t)&hdr->tpdu_DCsref - (caddr_t)hdr))
+
+ e.ev_number = DC_TPDU;
+ IncStat(ts_DC_rcvd);
+ break;
+
+ case DR_TPDU_type:
+ IFTRACE(D_TPINPUT)
+ tptrace(TPPTmisc, "DR recvd", hdr->tpdu_DRreason, 0, 0, 0);
+ ENDTRACE
+ if (sref != tpcb->tp_fref) {
+ printf("INPUT: inv sufx DRsref 0x%x tp_fref 0x%x\n",
+ sref, tpcb->tp_fref);
+ }
+
+ CHECK( (sref != 0 && sref != tpcb->tp_fref &&
+ tpcb->tp_state != TP_CRSENT),
+ (TP_ERROR_SNDC | E_TP_MISM_REFS),ts_inv_sufx, respond,
+ (1 + (caddr_t)&hdr->tpdu_DRsref - (caddr_t)hdr))
+
+ e.ATTR(DR_TPDU).e_reason = hdr->tpdu_DRreason;
+ e.ATTR(DR_TPDU).e_sref = (u_short)sref;
+ takes_data = TRUE;
+ e.ev_number = DR_TPDU;
+ IncStat(ts_DR_rcvd);
+ break;
+
+ case ER_TPDU_type:
+ IFTRACE(D_TPINPUT)
+ tptrace(TPPTmisc, "ER recvd", hdr->tpdu_ERreason,0,0,0);
+ ENDTRACE
+ e.ev_number = ER_TPDU;
+ e.ATTR(ER_TPDU).e_reason = hdr->tpdu_ERreason;
+ IncStat(ts_ER_rcvd);
+ break;
+
+ case AK_TPDU_type:
+
+ e.ATTR(AK_TPDU).e_subseq = subseq;
+ e.ATTR(AK_TPDU).e_fcc_present = fcc_present;
+
+ if (tpcb->tp_xtd_format) {
+#ifdef BYTE_ORDER
+ union seq_type seqeotX;
+
+ seqeotX.s_seqeot = ntohl(hdr->tpdu_seqeotX);
+ e.ATTR(AK_TPDU).e_seq = seqeotX.s_seq;
+ e.ATTR(AK_TPDU).e_cdt = ntohs(hdr->tpdu_AKcdtX);
+#else
+ e.ATTR(AK_TPDU).e_cdt = hdr->tpdu_AKcdtX;
+ e.ATTR(AK_TPDU).e_seq = hdr->tpdu_AKseqX;
+#endif /* BYTE_ORDER */
+ } else {
+ e.ATTR(AK_TPDU).e_cdt = hdr->tpdu_AKcdt;
+ e.ATTR(AK_TPDU).e_seq = hdr->tpdu_AKseq;
+ }
+ IFTRACE(D_TPINPUT)
+ tptrace(TPPTmisc, "AK recvd seq cdt subseq fcc_pres",
+ e.ATTR(AK_TPDU).e_seq, e.ATTR(AK_TPDU).e_cdt,
+ subseq, fcc_present);
+ ENDTRACE
+
+ e.ev_number = AK_TPDU;
+ IncStat(ts_AK_rcvd);
+ IncPStat(tpcb, tps_AK_rcvd);
+ break;
+
+ case XAK_TPDU_type:
+ if (tpcb->tp_xtd_format) {
+#ifdef BYTE_ORDER
+ union seq_type seqeotX;
+
+ seqeotX.s_seqeot = ntohl(hdr->tpdu_seqeotX);
+ e.ATTR(XAK_TPDU).e_seq = seqeotX.s_seq;
+#else
+ e.ATTR(XAK_TPDU).e_seq = hdr->tpdu_XAKseqX;
+#endif /* BYTE_ORDER */
+ } else {
+ e.ATTR(XAK_TPDU).e_seq = hdr->tpdu_XAKseq;
+ }
+ e.ev_number = XAK_TPDU;
+ IncStat(ts_XAK_rcvd);
+ IncPStat(tpcb, tps_XAK_rcvd);
+ break;
+
+ case XPD_TPDU_type:
+ if (tpcb->tp_xtd_format) {
+#ifdef BYTE_ORDER
+ union seq_type seqeotX;
+
+ seqeotX.s_seqeot = ntohl(hdr->tpdu_seqeotX);
+ e.ATTR(XPD_TPDU).e_seq = seqeotX.s_seq;
+#else
+ e.ATTR(XPD_TPDU).e_seq = hdr->tpdu_XPDseqX;
+#endif /* BYTE_ORDER */
+ } else {
+ e.ATTR(XPD_TPDU).e_seq = hdr->tpdu_XPDseq;
+ }
+ takes_data = TRUE;
+ e.ev_number = XPD_TPDU;
+ IncStat(ts_XPD_rcvd);
+ IncPStat(tpcb, tps_XPD_rcvd);
+ break;
+
+ case DT_TPDU_type:
+ { /* the y option will cause occasional packets to be dropped.
+ * A little crude but it works.
+ */
+
+ IFDEBUG(D_DROP)
+ if(time.tv_usec & 0x4 && hdr->tpdu_DTseq & 0x1) {
+ IncStat(ts_ydebug);
+ goto discard;
+ }
+ ENDDEBUG
+ }
+ if (tpcb->tp_class == TP_CLASS_0) {
+ tp0_data:
+ e.ATTR(DT_TPDU).e_seq = 0; /* actually don't care */
+ e.ATTR(DT_TPDU).e_eot = (((struct tp0du *)hdr)->tp0du_eot);
+ } else if (tpcb->tp_xtd_format) {
+#ifdef BYTE_ORDER
+ union seq_type seqeotX;
+
+ seqeotX.s_seqeot = ntohl(hdr->tpdu_seqeotX);
+ e.ATTR(DT_TPDU).e_seq = seqeotX.s_seq;
+ e.ATTR(DT_TPDU).e_eot = seqeotX.s_eot;
+#else
+ e.ATTR(DT_TPDU).e_seq = hdr->tpdu_DTseqX;
+ e.ATTR(DT_TPDU).e_eot = hdr->tpdu_DTeotX;
+#endif /* BYTE_ORDER */
+ } else {
+ e.ATTR(DT_TPDU).e_seq = hdr->tpdu_DTseq;
+ e.ATTR(DT_TPDU).e_eot = hdr->tpdu_DTeot;
+ }
+ if(e.ATTR(DT_TPDU).e_eot)
+ IncStat(ts_eot_input);
+ takes_data = TRUE;
+ e.ev_number = DT_TPDU;
+ IncStat(ts_DT_rcvd);
+ IncPStat(tpcb, tps_DT_rcvd);
+ break;
+
+ case GR_TPDU_type:
+ tp_indicate(T_DISCONNECT, tpcb, ECONNABORTED);
+ /* drop through */
+ default:
+ /* this should NEVER happen because there is a
+ * check for dutype well above here
+ */
+ error = E_TP_INV_TPDU; /* causes an ER */
+ IFDEBUG(D_TPINPUT)
+ printf("INVALID dutype 0x%x\n", hdr->tpdu_type);
+ ENDDEBUG
+ IncStat(ts_inv_dutype);
+ goto respond;
+ }
+ }
+ /* peel off the tp header;
+ * remember that the du_li doesn't count itself.
+ * This may leave us w/ an empty mbuf at the front of a chain.
+ * We can't just throw away the empty mbuf because hdr still points
+ * into the mbuf's data area and we're still using hdr (the tpdu header)
+ */
+ m->m_len -= ((int)hdr->tpdu_li + 1);
+ m->m_data += ((int)hdr->tpdu_li + 1);
+
+ if (takes_data) {
+ int max = tpdu_info[ hdr->tpdu_type ] [TP_MAX_DATA_INDEX];
+ int datalen = tpdu_len - hdr->tpdu_li - 1, mbtype = MT_DATA;
+ struct {
+ struct tp_disc_reason dr;
+ struct cmsghdr x_hdr;
+ } x;
+#define c_hdr x.x_hdr
+ register struct mbuf *n;
+
+ CHECK( (max && datalen > max), E_TP_LENGTH_INVAL,
+ ts_inv_length, respond, (max + hdr->tpdu_li + 1) );
+ switch( hdr->tpdu_type ) {
+
+ case CR_TPDU_type:
+ c_hdr.cmsg_type = TPOPT_CONN_DATA;
+ goto make_control_msg;
+
+ case CC_TPDU_type:
+ c_hdr.cmsg_type = TPOPT_CFRM_DATA;
+ goto make_control_msg;
+
+ case DR_TPDU_type:
+ x.dr.dr_hdr.cmsg_len = sizeof(x) - sizeof(c_hdr);
+ x.dr.dr_hdr.cmsg_type = TPOPT_DISC_REASON;
+ x.dr.dr_hdr.cmsg_level = SOL_TRANSPORT;
+ x.dr.dr_reason = hdr->tpdu_DRreason;
+ c_hdr.cmsg_type = TPOPT_DISC_DATA;
+ make_control_msg:
+ datalen += sizeof(c_hdr);
+ c_hdr.cmsg_len = datalen;
+ c_hdr.cmsg_level = SOL_TRANSPORT;
+ mbtype = MT_CONTROL;
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (n == 0)
+ {m_freem(m); m = 0; datalen = 0; goto invoke; }
+ if (hdr->tpdu_type == DR_TPDU_type) {
+ datalen += sizeof(x) - sizeof(c_hdr);
+ bcopy((caddr_t)&x, mtod(n, caddr_t), n->m_len = sizeof(x));
+ } else
+ bcopy((caddr_t)&c_hdr, mtod(n, caddr_t),
+ n->m_len = sizeof(c_hdr));
+ n->m_next = m;
+ m = n;
+ /* FALLTHROUGH */
+
+ case XPD_TPDU_type:
+ if (mbtype != MT_CONTROL)
+ mbtype = MT_OOBDATA;
+ m->m_flags |= M_EOR;
+ /* FALLTHROUGH */
+
+ case DT_TPDU_type:
+ for (n = m; n; n = n->m_next) {
+ MCHTYPE(n, mbtype);
+ }
+ invoke:
+ e.ATTR(DT_TPDU).e_datalen = datalen;
+ e.ATTR(DT_TPDU).e_data = m;
+ break;
+
+ default:
+ printf(
+ "ERROR in tp_input! hdr->tpdu_type 0x%x takes_data 0x%x m 0x%x\n",
+ hdr->tpdu_type, takes_data, m);
+ break;
+ }
+ /* prevent m_freem() after tp_driver() from throwing it all away */
+ m = MNULL;
+ }
+
+ IncStat(ts_tpdu_rcvd);
+
+ IFDEBUG(D_TPINPUT)
+ printf( "tp_input: before driver, state 0x%x event 0x%x m 0x%x",
+ tpcb->tp_state, e.ev_number, m );
+ printf(" e.e_data 0x%x\n", e.ATTR(DT_TPDU).e_data);
+ printf("takes_data 0x%x m_len 0x%x, tpdu_len 0x%x\n",
+ takes_data, (m==MNULL)?0:m->m_len, tpdu_len);
+ ENDDEBUG
+
+ error = tp_driver(tpcb, &e);
+
+ ASSERT(tpcb != (struct tp_pcb *)0);
+ ASSERT(tpcb->tp_sock != (struct socket *)0);
+ if( tpcb->tp_sock->so_error == 0 )
+ tpcb->tp_sock->so_error = error;
+
+ /* Kludge to keep the state tables under control (adding
+ * data on connect & disconnect & freeing the mbuf containing
+ * the data would have exploded the tables and made a big mess ).
+ */
+ switch(e.ev_number) {
+ case CC_TPDU:
+ case DR_TPDU:
+ case CR_TPDU:
+ m = e.ATTR(CC_TPDU).e_data; /* same field for all three dutypes */
+ IFDEBUG(D_TPINPUT)
+ printf("after driver, restoring m to 0x%x, takes_data 0x%x\n",
+ m, takes_data);
+ ENDDEBUG
+ break;
+ default:
+ break;
+ }
+ /* Concatenated sequences are terminated by any tpdu that
+ * carries data: CR, CC, DT, XPD, DR.
+ * All other tpdu types may be concatenated: AK, XAK, DC, ER.
+ */
+
+separate:
+ if ( takes_data == 0 ) {
+ ASSERT( m != MNULL );
+ /*
+ * we already peeled off the prev. tp header so
+ * we can just pull up some more and repeat
+ */
+
+ if( m = tp_inputprep(m) ) {
+ IFDEBUG(D_TPINPUT)
+ hdr = mtod(m, struct tpdu *);
+ printf("tp_input @ separate: hdr 0x%x size %d m 0x%x\n",
+ hdr, (int) hdr->tpdu_li + 1, m);
+ dump_mbuf(m, "tp_input after driver, at separate");
+ ENDDEBUG
+
+ IncStat(ts_concat_rcvd);
+ goto again;
+ }
+ }
+ if ( m != MNULL ) {
+ IFDEBUG(D_TPINPUT)
+ printf("tp_input : m_freem(0x%x)\n", m);
+ ENDDEBUG
+ m_freem(m);
+ IFDEBUG(D_TPINPUT)
+ printf("tp_input : after m_freem 0x%x\n", m);
+ ENDDEBUG
+ }
+ return (ProtoHook) tpcb;
+
+discard:
+ /* class 4: drop the tpdu */
+ /* class 2,0: Should drop the net connection, if you can figure out
+ * to which connection it applies
+ */
+ IFDEBUG(D_TPINPUT)
+ printf("tp_input DISCARD\n");
+ ENDDEBUG
+ IFTRACE(D_TPINPUT)
+ tptrace(TPPTmisc, "tp_input DISCARD m", m,0,0,0);
+ ENDTRACE
+ m_freem(m);
+ IncStat(ts_recv_drop);
+ return (ProtoHook)0;
+
+nonx_dref:
+ switch (dutype) {
+ default:
+ goto discard;
+ case CC_TPDU_type:
+ /* error = E_TP_MISM_REFS; */
+ break;
+ case DR_TPDU_type:
+ error |= TP_ERROR_SNDC;
+ }
+respond:
+ IFDEBUG(D_TPINPUT)
+ printf("RESPOND: error 0x%x, errlen 0x%x\n", error, errlen);
+ ENDDEBUG
+ IFTRACE(D_TPINPUT)
+ tptrace(TPPTmisc, "tp_input RESPOND m error sref", m, error, sref, 0);
+ ENDTRACE
+ if (sref == 0)
+ goto discard;
+ (void) tp_error_emit(error, (u_long)sref, (struct sockaddr_iso *)faddr,
+ (struct sockaddr_iso *)laddr, m, errlen, tpcb,
+ cons_channel, dgout_routine);
+ IFDEBUG(D_ERROR_EMIT)
+ printf("tp_input after error_emit\n");
+ ENDDEBUG
+
+#ifdef lint
+ printf("",sref,opt);
+#endif /* lint */
+ IncStat(ts_recv_drop);
+ return (ProtoHook)0;
+}
+
+
+/*
+ * NAME: tp_headersize()
+ *
+ * CALLED FROM:
+ * tp_emit() and tp_sbsend()
+ * TP needs to know the header size so it can figure out how
+ * much data to put in each tpdu.
+ *
+ * FUNCTION, ARGUMENTS, and RETURN VALUE:
+ * For a given connection, represented by (tpcb), and
+ * tpdu type (dutype), return the size of a tp header.
+ *
+ * RETURNS: the expected size of the heade in bytesr
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: It would be nice if it got the network header size as well.
+ */
+int
+tp_headersize(dutype, tpcb)
+ int dutype;
+ struct tp_pcb *tpcb;
+{
+ register int size = 0;
+
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc, "tp_headersize dutype class xtd_format",
+ dutype, tpcb->tp_class, tpcb->tp_xtd_format, 0);
+ ENDTRACE
+ if( !( (tpcb->tp_class == TP_CLASS_0) ||
+ (tpcb->tp_class == TP_CLASS_4) ||
+ (dutype == DR_TPDU_type) ||
+ (dutype == CR_TPDU_type) )) {
+ printf("tp_headersize:dutype 0x%x, class 0x%x",
+ dutype, tpcb->tp_class);
+ /* TODO: identify this and GET RID OF IT */
+ }
+ ASSERT( (tpcb->tp_class == TP_CLASS_0) ||
+ (tpcb->tp_class == TP_CLASS_4) ||
+ (dutype == DR_TPDU_type) ||
+ (dutype == CR_TPDU_type) );
+
+ if( tpcb->tp_class == TP_CLASS_0 ) {
+ size = tpdu_info[ dutype ] [TP_LEN_CLASS_0_INDEX];
+ } else {
+ size = tpdu_info[ dutype ] [tpcb->tp_xtd_format];
+ }
+ return size;
+ /* caller must get network level header size separately */
+}
diff --git a/sys/netiso/tp_ip.h b/sys/netiso/tp_ip.h
new file mode 100644
index 0000000..f277767
--- /dev/null
+++ b/sys/netiso/tp_ip.h
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_ip.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_ip.h,v 5.1 88/10/12 12:19:47 root Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_ip.h,v $
+ *
+ * internet IP-dependent structures and include files
+ *
+ */
+
+
+#ifndef __TP_IP__
+#define __TP_IP__
+
+#ifndef SOCK_STREAM
+#include <sys/socket.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <net/route.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+
+
+struct inpcb tp_inpcb;
+ /* queue of active inpcbs for tp ; for tp with dod ip */
+
+#endif /* __TP_IP__ */
diff --git a/sys/netiso/tp_iso.c b/sys/netiso/tp_iso.c
new file mode 100644
index 0000000..1cf67f8
--- /dev/null
+++ b/sys/netiso/tp_iso.c
@@ -0,0 +1,693 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_iso.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ * $Header: /var/src/sys/netiso/RCS/tp_iso.c,v 5.1 89/02/09 16:20:51 hagens Exp $
+ * $Source: /var/src/sys/netiso/RCS/tp_iso.c,v $
+ *
+ * Here is where you find the iso-dependent code. We've tried
+ * keep all net-level and (primarily) address-family-dependent stuff
+ * out of the tp source, and everthing here is reached indirectly
+ * through a switch table (struct nl_protosw *) tpcb->tp_nlproto
+ * (see tp_pcb.c).
+ * The routines here are:
+ * iso_getsufx: gets transport suffix out of an isopcb structure.
+ * iso_putsufx: put transport suffix into an isopcb structure.
+ * iso_putnetaddr: put a whole net addr into an isopcb.
+ * iso_getnetaddr: get a whole net addr from an isopcb.
+ * iso_cmpnetaddr: compare a whole net addr from an isopcb.
+ * iso_recycle_suffix: clear suffix for reuse in isopcb
+ * tpclnp_ctlinput: handle ER CNLPdu : icmp-like stuff
+ * tpclnp_mtu: figure out what size tpdu to use
+ * tpclnp_input: take a pkt from clnp, strip off its clnp header,
+ * give to tp
+ * tpclnp_output_dg: package a pkt for clnp given 2 addresses & some data
+ * tpclnp_output: package a pkt for clnp given an isopcb & some data
+ */
+
+#ifdef ISO
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/protosw.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netiso/argo_debug.h>
+#include <netiso/tp_param.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/tp_trace.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_tpdu.h>
+#include <netiso/tp_clnp.h>
+#include <netiso/cltp_var.h>
+
+/*
+ * CALLED FROM:
+ * pr_usrreq() on PRU_BIND, PRU_CONNECT, PRU_ACCEPT, and PRU_PEERADDR
+ * FUNCTION, ARGUMENTS:
+ * The argument (which) takes the value TP_LOCAL or TP_FOREIGN.
+ */
+
+iso_getsufx(isop, lenp, data_out, which)
+ struct isopcb *isop;
+ u_short *lenp;
+ caddr_t data_out;
+ int which;
+{
+ register struct sockaddr_iso *addr = 0;
+
+ switch (which) {
+ case TP_LOCAL:
+ addr = isop->isop_laddr;
+ break;
+
+ case TP_FOREIGN:
+ addr = isop->isop_faddr;
+ }
+ if (addr)
+ bcopy(TSEL(addr), data_out, (*lenp = addr->siso_tlen));
+}
+
+/* CALLED FROM:
+ * tp_newsocket(); i.e., when a connection is being established by an
+ * incoming CR_TPDU.
+ *
+ * FUNCTION, ARGUMENTS:
+ * Put a transport suffix (found in name) into an isopcb structure (isop).
+ * The argument (which) takes the value TP_LOCAL or TP_FOREIGN.
+ */
+void
+iso_putsufx(isop, sufxloc, sufxlen, which)
+ struct isopcb *isop;
+ caddr_t sufxloc;
+ int sufxlen, which;
+{
+ struct sockaddr_iso **dst, *backup;
+ register struct sockaddr_iso *addr;
+ struct mbuf *m;
+ int len;
+
+ switch (which) {
+ default:
+ return;
+
+ case TP_LOCAL:
+ dst = &isop->isop_laddr;
+ backup = &isop->isop_sladdr;
+ break;
+
+ case TP_FOREIGN:
+ dst = &isop->isop_faddr;
+ backup = &isop->isop_sfaddr;
+ }
+ if ((addr = *dst) == 0) {
+ addr = *dst = backup;
+ addr->siso_nlen = 0;
+ addr->siso_slen = 0;
+ addr->siso_plen = 0;
+ printf("iso_putsufx on un-initialized isopcb\n");
+ }
+ len = sufxlen + addr->siso_nlen +
+ (sizeof(*addr) - sizeof(addr->siso_data));
+ if (addr == backup) {
+ if (len > sizeof(*addr)) {
+ m = m_getclr(M_DONTWAIT, MT_SONAME);
+ if (m == 0)
+ return;
+ addr = *dst = mtod(m, struct sockaddr_iso *);
+ *addr = *backup;
+ m->m_len = len;
+ }
+ }
+ bcopy(sufxloc, TSEL(addr), sufxlen);
+ addr->siso_tlen = sufxlen;
+ addr->siso_len = len;
+}
+
+/*
+ * CALLED FROM:
+ * tp.trans whenever we go into REFWAIT state.
+ * FUNCTION and ARGUMENT:
+ * Called when a ref is frozen, to allow the suffix to be reused.
+ * (isop) is the net level pcb. This really shouldn't have to be
+ * done in a NET level pcb but... for the internet world that just
+ * the way it is done in BSD...
+ * The alternative is to have the port unusable until the reference
+ * timer goes off.
+ */
+void
+iso_recycle_tsuffix(isop)
+ struct isopcb *isop;
+{
+ isop->isop_laddr->siso_tlen = isop->isop_faddr->siso_tlen = 0;
+}
+
+/*
+ * CALLED FROM:
+ * tp_newsocket(); i.e., when a connection is being established by an
+ * incoming CR_TPDU.
+ *
+ * FUNCTION and ARGUMENTS:
+ * Copy a whole net addr from a struct sockaddr (name).
+ * into an isopcb (isop).
+ * The argument (which) takes values TP_LOCAL or TP_FOREIGN
+ */
+void
+iso_putnetaddr(isop, name, which)
+ register struct isopcb *isop;
+ struct sockaddr_iso *name;
+ int which;
+{
+ struct sockaddr_iso **sisop, *backup;
+ register struct sockaddr_iso *siso;
+
+ switch (which) {
+ default:
+ printf("iso_putnetaddr: should panic\n");
+ return;
+ case TP_LOCAL:
+ sisop = &isop->isop_laddr;
+ backup = &isop->isop_sladdr;
+ break;
+ case TP_FOREIGN:
+ sisop = &isop->isop_faddr;
+ backup = &isop->isop_sfaddr;
+ }
+ siso = ((*sisop == 0) ? (*sisop = backup) : *sisop);
+ IFDEBUG(D_TPISO)
+ printf("ISO_PUTNETADDR\n");
+ dump_isoaddr(isop->isop_faddr);
+ ENDDEBUG
+ siso->siso_addr = name->siso_addr;
+}
+
+/*
+ * CALLED FROM:
+ * tp_input() when a connection is being established by an
+ * incoming CR_TPDU, and considered for interception.
+ *
+ * FUNCTION and ARGUMENTS:
+ * compare a whole net addr from a struct sockaddr (name),
+ * with that implicitly stored in an isopcb (isop).
+ * The argument (which) takes values TP_LOCAL or TP_FOREIGN.
+ */
+iso_cmpnetaddr(isop, name, which)
+ register struct isopcb *isop;
+ register struct sockaddr_iso *name;
+ int which;
+{
+ struct sockaddr_iso **sisop, *backup;
+ register struct sockaddr_iso *siso;
+
+ switch (which) {
+ default:
+ printf("iso_cmpnetaddr: should panic\n");
+ return 0;
+ case TP_LOCAL:
+ sisop = &isop->isop_laddr;
+ backup = &isop->isop_sladdr;
+ break;
+ case TP_FOREIGN:
+ sisop = &isop->isop_faddr;
+ backup = &isop->isop_sfaddr;
+ }
+ siso = ((*sisop == 0) ? (*sisop = backup) : *sisop);
+ IFDEBUG(D_TPISO)
+ printf("ISO_CMPNETADDR\n");
+ dump_isoaddr(siso);
+ ENDDEBUG
+ if (name->siso_tlen && bcmp(TSEL(name), TSEL(siso), name->siso_tlen))
+ return (0);
+ return (bcmp((caddr_t)name->siso_data,
+ (caddr_t)siso->siso_data, name->siso_nlen) == 0);
+}
+
+/*
+ * CALLED FROM:
+ * pr_usrreq() PRU_SOCKADDR, PRU_ACCEPT, PRU_PEERADDR
+ * FUNCTION and ARGUMENTS:
+ * Copy a whole net addr from an isopcb (isop) into
+ * a struct sockaddr (name).
+ * The argument (which) takes values TP_LOCAL or TP_FOREIGN.
+ */
+
+void
+iso_getnetaddr( isop, name, which)
+ struct isopcb *isop;
+ struct mbuf *name;
+ int which;
+{
+ struct sockaddr_iso *siso =
+ (which == TP_LOCAL ? isop->isop_laddr : isop->isop_faddr);
+ if (siso)
+ bcopy((caddr_t)siso, mtod(name, caddr_t),
+ (unsigned)(name->m_len = siso->siso_len));
+ else
+ name->m_len = 0;
+}
+/*
+ * NAME: tpclnp_mtu()
+ *
+ * CALLED FROM:
+ * tp_route_to() on incoming CR, CC, and pr_usrreq() for PRU_CONNECT
+ *
+ * FUNCTION, ARGUMENTS, and RETURN VALUE:
+ *
+ * Perform subnetwork dependent part of determining MTU information.
+ * It appears that setting a double pointer to the rtentry associated with
+ * the destination, and returning the header size for the network protocol
+ * suffices.
+ *
+ * SIDE EFFECTS:
+ * Sets tp_routep pointer in pcb.
+ *
+ * NOTES:
+ */
+tpclnp_mtu(tpcb)
+register struct tp_pcb *tpcb;
+{
+ struct isopcb *isop = (struct isopcb *)tpcb->tp_npcb;
+
+ IFDEBUG(D_CONN)
+ printf("tpclnp_mtu(tpcb)\n", tpcb);
+ ENDDEBUG
+ tpcb->tp_routep = &(isop->isop_route.ro_rt);
+ if (tpcb->tp_netservice == ISO_CONS)
+ return 0;
+ else
+ return (sizeof(struct clnp_fixed) + sizeof(struct clnp_segment) +
+ 2 * sizeof(struct iso_addr));
+
+}
+
+/*
+ * CALLED FROM:
+ * tp_emit()
+ * FUNCTION and ARGUMENTS:
+ * Take a packet(m0) from tp and package it so that clnp will accept it.
+ * This means prepending space for the clnp header and filling in a few
+ * of the fields.
+ * isop is the isopcb structure; datalen is the length of the data in the
+ * mbuf string m0.
+ * RETURN VALUE:
+ * whatever (E*) is returned form the net layer output routine.
+ */
+
+int
+tpclnp_output(isop, m0, datalen, nochksum)
+ struct isopcb *isop;
+ struct mbuf *m0;
+ int datalen;
+ int nochksum;
+{
+ register struct mbuf *m = m0;
+ IncStat(ts_tpdu_sent);
+
+ IFDEBUG(D_TPISO)
+ struct tpdu *hdr = mtod(m0, struct tpdu *);
+
+ printf(
+"abt to call clnp_output: datalen 0x%x, hdr.li 0x%x, hdr.dutype 0x%x nocsum x%x dst addr:\n",
+ datalen,
+ (int)hdr->tpdu_li, (int)hdr->tpdu_type, nochksum);
+ dump_isoaddr(isop->isop_faddr);
+ printf("\nsrc addr:\n");
+ dump_isoaddr(isop->isop_laddr);
+ dump_mbuf(m0, "at tpclnp_output");
+ ENDDEBUG
+
+ return
+ clnp_output(m0, isop, datalen, /* flags */nochksum ? CLNP_NO_CKSUM : 0);
+}
+
+/*
+ * CALLED FROM:
+ * tp_error_emit()
+ * FUNCTION and ARGUMENTS:
+ * This is a copy of tpclnp_output that takes the addresses
+ * instead of a pcb. It's used by the tp_error_emit, when we
+ * don't have an iso_pcb with which to call the normal output rtn.
+ * RETURN VALUE:
+ * ENOBUFS or
+ * whatever (E*) is returned form the net layer output routine.
+ */
+
+int
+tpclnp_output_dg(laddr, faddr, m0, datalen, ro, nochksum)
+ struct iso_addr *laddr, *faddr;
+ struct mbuf *m0;
+ int datalen;
+ struct route *ro;
+ int nochksum;
+{
+ struct isopcb tmppcb;
+ int err;
+ int flags;
+ register struct mbuf *m = m0;
+
+ IFDEBUG(D_TPISO)
+ printf("tpclnp_output_dg datalen 0x%x m0 0x%x\n", datalen, m0);
+ ENDDEBUG
+
+ /*
+ * Fill in minimal portion of isopcb so that clnp can send the
+ * packet.
+ */
+ bzero((caddr_t)&tmppcb, sizeof(tmppcb));
+ tmppcb.isop_laddr = &tmppcb.isop_sladdr;
+ tmppcb.isop_laddr->siso_addr = *laddr;
+ tmppcb.isop_faddr = &tmppcb.isop_sfaddr;
+ tmppcb.isop_faddr->siso_addr = *faddr;
+
+ IFDEBUG(D_TPISO)
+ printf("tpclnp_output_dg faddr: \n");
+ dump_isoaddr(&tmppcb.isop_sfaddr);
+ printf("\ntpclnp_output_dg laddr: \n");
+ dump_isoaddr(&tmppcb.isop_sladdr);
+ printf("\n");
+ ENDDEBUG
+
+ /*
+ * Do not use packet cache since this is a one shot error packet
+ */
+ flags = (CLNP_NOCACHE|(nochksum?CLNP_NO_CKSUM:0));
+
+ IncStat(ts_tpdu_sent);
+
+ err = clnp_output(m0, &tmppcb, datalen, flags);
+
+ /*
+ * Free route allocated by clnp (if the route was indeed allocated)
+ */
+ if (tmppcb.isop_route.ro_rt)
+ RTFREE(tmppcb.isop_route.ro_rt);
+
+ return(err);
+}
+/*
+ * CALLED FROM:
+ * clnp's input routine, indirectly through the protosw.
+ * FUNCTION and ARGUMENTS:
+ * Take a packet (m) from clnp, strip off the clnp header and give it to tp
+ * No return value.
+ */
+ProtoHook
+tpclnp_input(m, src, dst, clnp_len, ce_bit)
+ register struct mbuf *m;
+ struct sockaddr_iso *src, *dst;
+ int clnp_len, ce_bit;
+{
+ struct mbuf *tp_inputprep();
+ int tp_input(), cltp_input(), (*input)() = tp_input;
+
+ IncStat(ts_pkt_rcvd);
+
+ IFDEBUG(D_TPINPUT)
+ printf("tpclnp_input: m 0x%x clnp_len 0x%x\n", m, clnp_len);
+ dump_mbuf(m, "at tpclnp_input");
+ ENDDEBUG
+ /*
+ * CLNP gives us an mbuf chain WITH the clnp header pulled up,
+ * and the length of the clnp header.
+ * First, strip off the Clnp header. leave the mbuf there for the
+ * pullup that follows.
+ */
+ m->m_len -= clnp_len;
+ m->m_data += clnp_len;
+ m->m_pkthdr.len -= clnp_len;
+ /* XXXX: should probably be in clnp_input */
+ switch (dst->siso_data[dst->siso_nlen - 1]) {
+#ifdef TUBA
+ case ISOPROTO_TCP:
+ return (tuba_tcpinput(m, src, dst));
+#endif
+ case 0:
+ if (m->m_len == 0 && (m = m_pullup(m, 1)) == 0)
+ return 0;
+ if (*(mtod(m, u_char *)) == ISO10747_IDRP)
+ return (idrp_input(m, src, dst));
+ }
+ m = tp_inputprep(m);
+ if (m == 0)
+ return 0;
+ if (mtod(m, u_char *)[1] == UD_TPDU_type)
+ input = cltp_input;
+
+ IFDEBUG(D_TPINPUT)
+ dump_mbuf(m, "after tpclnp_input both pullups");
+ ENDDEBUG
+
+ IFDEBUG(D_TPISO)
+ printf("calling %sinput : src 0x%x, dst 0x%x, src addr:\n",
+ (input == tp_input ? "tp_" : "clts_"), src, dst);
+ dump_isoaddr(src);
+ printf(" dst addr:\n");
+ dump_isoaddr(dst);
+ ENDDEBUG
+
+ (void) (*input)(m, (struct sockaddr *)src, (struct sockaddr *)dst,
+ 0, tpclnp_output_dg, ce_bit);
+
+ IFDEBUG(D_QUENCH)
+ {
+ if(time.tv_usec & 0x4 && time.tv_usec & 0x40) {
+ printf("tpclnp_input: FAKING %s\n",
+ tp_stat.ts_pkt_rcvd & 0x1?"QUENCH":"QUENCH2");
+ if(tp_stat.ts_pkt_rcvd & 0x1) {
+ tpclnp_ctlinput(PRC_QUENCH, &src);
+ } else {
+ tpclnp_ctlinput(PRC_QUENCH2, &src);
+ }
+ }
+ }
+ ENDDEBUG
+
+ return 0;
+}
+
+ProtoHook
+iso_rtchange()
+{
+ return 0;
+}
+
+/*
+ * CALLED FROM:
+ * tpclnp_ctlinput()
+ * FUNCTION and ARGUMENTS:
+ * find the tpcb pointer and pass it to tp_quench
+ */
+void
+tpiso_decbit(isop)
+ struct isopcb *isop;
+{
+ tp_quench((struct tp_pcb *)isop->isop_socket->so_pcb, PRC_QUENCH2);
+}
+/*
+ * CALLED FROM:
+ * tpclnp_ctlinput()
+ * FUNCTION and ARGUMENTS:
+ * find the tpcb pointer and pass it to tp_quench
+ */
+void
+tpiso_quench(isop)
+ struct isopcb *isop;
+{
+ tp_quench((struct tp_pcb *)isop->isop_socket->so_pcb, PRC_QUENCH);
+}
+
+/*
+ * CALLED FROM:
+ * The network layer through the protosw table.
+ * FUNCTION and ARGUMENTS:
+ * When clnp an ICMP-like msg this gets called.
+ * It either returns an error status to the user or
+ * it causes all connections on this address to be aborted
+ * by calling the appropriate xx_notify() routine.
+ * (cmd) is the type of ICMP error.
+ * (siso) is the address of the guy who sent the ER CLNPDU
+ */
+ProtoHook
+tpclnp_ctlinput(cmd, siso)
+ int cmd;
+ struct sockaddr_iso *siso;
+{
+ extern u_char inetctlerrmap[];
+ extern ProtoHook tpiso_abort();
+ extern ProtoHook iso_rtchange();
+ extern ProtoHook tpiso_reset();
+ void iso_pcbnotify();
+
+ IFDEBUG(D_TPINPUT)
+ printf("tpclnp_ctlinput1: cmd 0x%x addr: \n", cmd);
+ dump_isoaddr(siso);
+ ENDDEBUG
+
+ if (cmd < 0 || cmd > PRC_NCMDS)
+ return 0;
+ if (siso->siso_family != AF_ISO)
+ return 0;
+ switch (cmd) {
+
+ case PRC_QUENCH2:
+ iso_pcbnotify(&tp_isopcb, siso, 0, (int (*)())tpiso_decbit);
+ break;
+
+ case PRC_QUENCH:
+ iso_pcbnotify(&tp_isopcb, siso, 0, (int (*)())tpiso_quench);
+ break;
+
+ case PRC_TIMXCEED_REASS:
+ case PRC_ROUTEDEAD:
+ iso_pcbnotify(&tp_isopcb, siso, 0, tpiso_reset);
+ break;
+
+ case PRC_HOSTUNREACH:
+ case PRC_UNREACH_NET:
+ case PRC_IFDOWN:
+ case PRC_HOSTDEAD:
+ iso_pcbnotify(&tp_isopcb, siso,
+ (int)inetctlerrmap[cmd], iso_rtchange);
+ break;
+
+ default:
+ /*
+ case PRC_MSGSIZE:
+ case PRC_UNREACH_HOST:
+ case PRC_UNREACH_PROTOCOL:
+ case PRC_UNREACH_PORT:
+ case PRC_UNREACH_NEEDFRAG:
+ case PRC_UNREACH_SRCFAIL:
+ case PRC_REDIRECT_NET:
+ case PRC_REDIRECT_HOST:
+ case PRC_REDIRECT_TOSNET:
+ case PRC_REDIRECT_TOSHOST:
+ case PRC_TIMXCEED_INTRANS:
+ case PRC_PARAMPROB:
+ */
+ iso_pcbnotify(&tp_isopcb, siso, (int)inetctlerrmap[cmd], tpiso_abort);
+ break;
+ }
+ return 0;
+}
+/*
+ * XXX - Variant which is called by clnp_er.c with an isoaddr rather
+ * than a sockaddr_iso.
+ */
+
+static struct sockaddr_iso siso = {sizeof(siso), AF_ISO};
+tpclnp_ctlinput1(cmd, isoa)
+ int cmd;
+ struct iso_addr *isoa;
+{
+ bzero((caddr_t)&siso.siso_addr, sizeof(siso.siso_addr));
+ bcopy((caddr_t)isoa, (caddr_t)&siso.siso_addr, isoa->isoa_len);
+ tpclnp_ctlinput(cmd, &siso);
+}
+
+/*
+ * These next 2 routines are
+ * CALLED FROM:
+ * xxx_notify() from tp_ctlinput() when
+ * net level gets some ICMP-equiv. type event.
+ * FUNCTION and ARGUMENTS:
+ * Cause the connection to be aborted with some sort of error
+ * reason indicating that the network layer caused the abort.
+ * Fakes an ER TPDU so we can go through the driver.
+ * abort always aborts the TP connection.
+ * reset may or may not, depending on the TP class that's in use.
+ */
+ProtoHook
+tpiso_abort(isop)
+ struct isopcb *isop;
+{
+ struct tp_event e;
+
+ IFDEBUG(D_CONN)
+ printf("tpiso_abort 0x%x\n", isop);
+ ENDDEBUG
+ e.ev_number = ER_TPDU;
+ e.ATTR(ER_TPDU).e_reason = ECONNABORTED;
+ return tp_driver((struct tp_pcb *)isop->isop_socket->so_pcb, &e);
+}
+
+ProtoHook
+tpiso_reset(isop)
+ struct isopcb *isop;
+{
+ struct tp_event e;
+
+ e.ev_number = T_NETRESET;
+ return tp_driver((struct tp_pcb *)isop->isop_socket->so_pcb, &e);
+
+}
+
+#endif /* ISO */
diff --git a/sys/netiso/tp_meas.c b/sys/netiso/tp_meas.c
new file mode 100644
index 0000000..f8bbbe6
--- /dev/null
+++ b/sys/netiso/tp_meas.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_meas.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * $Header: tp_meas.c,v 5.2 88/11/18 17:28:04 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_meas.c,v $
+ *
+ * tp_meas.c : create a performance measurement event
+ * in the circular buffer tp_Meas[]
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <netiso/argo_debug.h>
+#include <netiso/tp_meas.h>
+
+extern struct timeval time;
+
+#ifdef TP_PERF_MEAS
+int tp_Measn = 0;
+struct tp_Meas tp_Meas[TPMEASN];
+
+/*
+ * NAME: tpmeas()
+ *
+ * CALLED FROM: tp_emit(), tp_soisdisconecting(), tp_soisdisconnected()
+ * tp0_stash(), tp_stash(), tp_send(), tp_goodack(), tp_usrreq()
+ *
+ * FUNCTION and ARGUMENTS:
+ * stashes a performance-measurement event for the given reference (ref)
+ * (kind) tells which kind of event, timev is the time to be stored
+ * with this event, (seq), (win), and (size) are integers that usually
+ * refer to the sequence number, window number (on send) and
+ * size of tpdu or window.
+ *
+ * RETURNS: Nada
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+void
+Tpmeas(ref, kind, timev, seq, win, size)
+ u_int ref;
+ u_int kind;
+ struct timeval *timev;
+ u_int seq, win, size;
+{
+ register struct tp_Meas *tpm;
+ static int mseq;
+
+ tpm = &tp_Meas[tp_Measn++];
+ tp_Measn %= TPMEASN;
+
+ tpm->tpm_kind = kind;
+ tpm->tpm_tseq = mseq++;
+ tpm->tpm_ref = ref;
+ if(kind == TPtime_from_ll)
+ bcopy((caddr_t)timev, (caddr_t)&tpm->tpm_time, sizeof(struct timeval));
+ else
+ bcopy( (caddr_t)&time,
+ (caddr_t)&tpm->tpm_time, sizeof(struct timeval) );
+ tpm->tpm_seq = seq;
+ tpm->tpm_window = win;
+ tpm->tpm_size = size;
+}
+
+#endif /* TP_PERF_MEAS */
diff --git a/sys/netiso/tp_meas.h b/sys/netiso/tp_meas.h
new file mode 100644
index 0000000..10ef93d
--- /dev/null
+++ b/sys/netiso/tp_meas.h
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_meas.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+#ifdef TP_PERF_MEAS
+#define tpmeas(a, b, t, c, d, e) \
+ Tpmeas((u_int)(a), (u_int)(b), t, (u_int)(c), (u_int)(d), (u_int)(e))
+
+struct tp_Meas {
+ int tpm_tseq;
+ u_char tpm_kind;
+ u_short tpm_ref;
+ u_short tpm_size;
+ u_short tpm_window;
+ u_int tpm_seq;
+ struct timeval tpm_time;
+};
+
+#define TPMEASN 4000
+extern int tp_Measn;
+extern struct tp_Meas tp_Meas[];
+
+/*
+ * the kinds of events for packet tracing are:
+ */
+#define TPtime_from_session 0x01
+#define TPtime_to_session 0x02
+#define TPtime_ack_rcvd 0x03
+#define TPtime_ack_sent 0x04
+#define TPtime_from_ll 0x05
+#define TPtime_to_ll 0x06
+#define TPsbsend 0x07
+#define TPtime_open 0x08
+#define TPtime_open_X 0x28 /* xtd format */
+#define TPtime_close 0x09
+
+#endif /* TP_PERF_MEAS */
diff --git a/sys/netiso/tp_output.c b/sys/netiso/tp_output.c
new file mode 100644
index 0000000..cdd7c4f
--- /dev/null
+++ b/sys/netiso/tp_output.c
@@ -0,0 +1,712 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_output.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_output.c,v 5.4 88/11/18 17:28:08 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_output.c,v $
+ *
+ * In here is tp_ctloutput(), the guy called by [sg]etsockopt(),
+ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <netiso/tp_param.h>
+#include <netiso/tp_user.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_ip.h>
+#include <netiso/tp_clnp.h>
+#include <netiso/tp_timer.h>
+#include <netiso/argo_debug.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/tp_trace.h>
+
+#define TPDUSIZESHIFT 24
+#define CLASSHIFT 16
+
+/*
+ * NAME: tp_consistency()
+ *
+ * CALLED FROM:
+ * tp_ctloutput(), tp_input()
+ *
+ * FUNCTION and ARGUMENTS:
+ * Checks the consistency of options and tpdusize with class,
+ * using the parameters passed in via (param).
+ * (cmd) may be TP_STRICT or TP_FORCE or both.
+ * Force means it will set all the values in (tpcb) to those in
+ * the input arguements iff no errors were encountered.
+ * Strict means that no inconsistency will be tolerated. If it's
+ * not used, checksum and tpdusize inconsistencies will be tolerated.
+ * The reason for this is that in some cases, when we're negotiating down
+ * from class 4, these options should be changed but should not
+ * cause negotiation to fail.
+ *
+ * RETURNS
+ * E* or EOK
+ * E* if the various parms aren't ok for a given class
+ * EOK if they are ok for a given class
+ */
+
+int
+tp_consistency( tpcb, cmd, param )
+ u_int cmd;
+ struct tp_conn_param *param;
+ struct tp_pcb *tpcb;
+{
+ register int error = EOK;
+ int class_to_use = tp_mask_to_num(param->p_class);
+
+ IFTRACE(D_SETPARAMS)
+ tptrace(TPPTmisc,
+ "tp_consist enter class_to_use dontchange param.class cmd",
+ class_to_use, param->p_dont_change_params, param->p_class, cmd);
+ ENDTRACE
+ IFDEBUG(D_SETPARAMS)
+ printf("tp_consistency %s %s\n",
+ cmd& TP_FORCE? "TP_FORCE": "",
+ cmd& TP_STRICT? "TP_STRICT":"");
+ ENDDEBUG
+ if ((cmd & TP_FORCE) && (param->p_dont_change_params)) {
+ cmd &= ~TP_FORCE;
+ }
+ /* can switch net services within a domain, but
+ * cannot switch domains
+ */
+ switch( param->p_netservice) {
+ case ISO_CONS:
+ case ISO_CLNS:
+ case ISO_COSNS:
+ /* param->p_netservice in ISO DOMAIN */
+ if(tpcb->tp_domain != AF_ISO ) {
+ error = EINVAL; goto done;
+ }
+ break;
+ case IN_CLNS:
+ /* param->p_netservice in INET DOMAIN */
+ if( tpcb->tp_domain != AF_INET ) {
+ error = EINVAL; goto done;
+ }
+ break;
+ /* no others not possible-> netservice is a 2-bit field! */
+ }
+
+ IFDEBUG(D_SETPARAMS)
+ printf("p_class 0x%x, class_to_use 0x%x\n", param->p_class,
+ class_to_use);
+ ENDDEBUG
+ if((param->p_netservice < 0) || (param->p_netservice > TP_MAX_NETSERVICES)){
+ error = EINVAL; goto done;
+ }
+ if( (param->p_class & TP_CLASSES_IMPLEMENTED) == 0 ) {
+ error = EINVAL; goto done;
+ }
+ IFDEBUG(D_SETPARAMS)
+ printf("Nretrans 0x%x\n", param->p_Nretrans );
+ ENDDEBUG
+ if( ( param->p_Nretrans < 1 ) ||
+ (param->p_cr_ticks < 1) || (param->p_cc_ticks < 1) ) {
+ /* bad for any class because negot has to be done a la class 4 */
+ error = EINVAL; goto done;
+ }
+ IFDEBUG(D_SETPARAMS)
+ printf("use_csum 0x%x\n", param->p_use_checksum );
+ printf("xtd_format 0x%x\n", param->p_xtd_format );
+ printf("xpd_service 0x%x\n", param->p_xpd_service );
+ printf("tpdusize 0x%x\n", param->p_tpdusize );
+ printf("tpcb->flags 0x%x\n", tpcb->tp_flags );
+ ENDDEBUG
+ switch( class_to_use ) {
+
+ case 0:
+ /* do not use checksums, xtd format, or XPD */
+
+ if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) {
+ if(cmd & TP_STRICT) {
+ error = EINVAL;
+ } else {
+ param->p_use_checksum = 0;
+ param->p_xtd_format = 0;
+ param->p_xpd_service = 0;
+ }
+ break;
+ }
+
+ if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
+ if(cmd & TP_STRICT) {
+ error = EINVAL;
+ } else {
+ param->p_tpdusize = TP_MIN_TPDUSIZE;
+ }
+ break;
+ }
+ if (param->p_tpdusize > TP0_TPDUSIZE) {
+ if (cmd & TP_STRICT) {
+ error = EINVAL;
+ } else {
+ param->p_tpdusize = TP0_TPDUSIZE;
+ }
+ break;
+ }
+
+ /* connect/disc data not allowed for class 0 */
+ if (tpcb->tp_ucddata) {
+ if(cmd & TP_STRICT) {
+ error = EINVAL;
+ } else if(cmd & TP_FORCE) {
+ m_freem(tpcb->tp_ucddata);
+ tpcb->tp_ucddata = 0;
+ }
+ }
+ break;
+
+ case 4:
+ IFDEBUG(D_SETPARAMS)
+ printf("dt_ticks 0x%x\n", param->p_dt_ticks );
+ printf("x_ticks 0x%x\n", param->p_x_ticks );
+ printf("dr_ticks 0x%x\n", param->p_dr_ticks );
+ printf("keepalive 0x%x\n", param->p_keepalive_ticks );
+ printf("sendack 0x%x\n", param->p_sendack_ticks );
+ printf("inact 0x%x\n", param->p_inact_ticks );
+ printf("ref 0x%x\n", param->p_ref_ticks );
+ ENDDEBUG
+ if( (param->p_class & TP_CLASS_4 ) && (
+ (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) ||
+ (param->p_x_ticks < 1) || (param->p_keepalive_ticks < 1) ||
+ (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) ||
+ (param->p_inact_ticks < 1) ) ) {
+ error = EINVAL;
+ break;
+ }
+ IFDEBUG(D_SETPARAMS)
+ printf("rx_strat 0x%x\n", param->p_rx_strat );
+ ENDDEBUG
+ if(param->p_rx_strat >
+ ( TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART) ) {
+ if(cmd & TP_STRICT) {
+ error = EINVAL;
+ } else {
+ param->p_rx_strat = TPRX_USE_CW;
+ }
+ break;
+ }
+ IFDEBUG(D_SETPARAMS)
+ printf("ack_strat 0x%x\n", param->p_ack_strat );
+ ENDDEBUG
+ if((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) {
+ if(cmd & TP_STRICT) {
+ error = EINVAL;
+ } else {
+ param->p_ack_strat = TPACK_WINDOW;
+ }
+ break;
+ }
+ if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
+ if(cmd & TP_STRICT) {
+ error = EINVAL;
+ } else {
+ param->p_tpdusize = TP_MIN_TPDUSIZE;
+ }
+ break;
+ }
+ if (param->p_tpdusize > TP_TPDUSIZE) {
+ if(cmd & TP_STRICT) {
+ error = EINVAL;
+ } else {
+ param->p_tpdusize = TP_TPDUSIZE;
+ }
+ break;
+ }
+ break;
+ }
+
+ if ((error==0) && (cmd & TP_FORCE)) {
+ long dusize = ((long)param->p_ptpdusize) << 7;
+ /* Enforce Negotation rules below */
+ tpcb->tp_class = param->p_class;
+ if (tpcb->tp_use_checksum || param->p_use_checksum)
+ tpcb->tp_use_checksum = 1;
+ if (!tpcb->tp_xpd_service || !param->p_xpd_service)
+ tpcb->tp_xpd_service = 0;
+ if (!tpcb->tp_xtd_format || !param->p_xtd_format)
+ tpcb->tp_xtd_format = 0;
+ if (dusize) {
+ if (tpcb->tp_l_tpdusize > dusize)
+ tpcb->tp_l_tpdusize = dusize;
+ if (tpcb->tp_ptpdusize == 0 ||
+ tpcb->tp_ptpdusize > param->p_ptpdusize)
+ tpcb->tp_ptpdusize = param->p_ptpdusize;
+ } else {
+ if (param->p_tpdusize != 0 &&
+ tpcb->tp_tpdusize > param->p_tpdusize)
+ tpcb->tp_tpdusize = param->p_tpdusize;
+ tpcb->tp_l_tpdusize = 1 << tpcb->tp_tpdusize;
+ }
+ }
+done:
+
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd",
+ error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
+ ENDTRACE
+ IFDEBUG(D_CONN)
+ printf(
+ "tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n",
+ error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
+ ENDDEBUG
+ return error;
+}
+
+/*
+ * NAME: tp_ctloutput()
+ *
+ * CALLED FROM:
+ * [sg]etsockopt(), via so[sg]etopt().
+ *
+ * FUNCTION and ARGUMENTS:
+ * Implements the socket options at transport level.
+ * (cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h).
+ * (so) is the socket.
+ * (level) is SOL_TRANSPORT (see ../sys/socket.h)
+ * (optname) is the particular command or option to be set.
+ * (**mp) is an mbuf structure.
+ *
+ * RETURN VALUE:
+ * ENOTSOCK if the socket hasn't got an associated tpcb
+ * EINVAL if
+ * trying to set window too big
+ * trying to set illegal max tpdu size
+ * trying to set illegal credit fraction
+ * trying to use unknown or unimplemented class of TP
+ * structure passed to set timer values is wrong size
+ * illegal combination of command/GET-SET option,
+ * e.g., GET w/ TPOPT_CDDATA_CLEAR:
+ * EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET
+ * or if the transport-specific command is not implemented
+ * EISCONN if trying a command that isn't allowed after a connection
+ * is established
+ * ENOTCONN if trying a command that is allowed only if a connection is
+ * established
+ * EMSGSIZE if trying to give too much data on connect/disconnect
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+ProtoHook
+tp_ctloutput(cmd, so, level, optname, mp)
+ int cmd, level, optname;
+ struct socket *so;
+ struct mbuf **mp;
+{
+ struct tp_pcb *tpcb = sototpcb(so);
+ int s = splnet();
+ caddr_t value;
+ unsigned val_len;
+ int error = 0;
+
+ IFTRACE(D_REQUEST)
+ tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp",
+ cmd, so, optname, mp);
+ ENDTRACE
+ IFDEBUG(D_REQUEST)
+ printf(
+ "tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n",
+ so, cmd, optname, mp, mp?*mp:0, tpcb);
+ ENDDEBUG
+ if( tpcb == (struct tp_pcb *)0 ) {
+ error = ENOTSOCK; goto done;
+ }
+ if(*mp == MNULL) {
+ register struct mbuf *m;
+
+ MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */
+ if (m == NULL) {
+ splx(s);
+ return ENOBUFS;
+ }
+ m->m_len = 0;
+ m->m_act = 0;
+ *mp = m;
+ }
+
+ /*
+ * Hook so one can set network options via a tp socket.
+ */
+ if ( level == SOL_NETWORK ) {
+ if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL))
+ error = ENOTSOCK;
+ else if (tpcb->tp_nlproto->nlp_ctloutput == NULL)
+ error = EOPNOTSUPP;
+ else
+ return ((tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname,
+ tpcb->tp_npcb, *mp));
+ goto done;
+ } else if ( level == SOL_SOCKET) {
+ if (optname == SO_RCVBUF && cmd == PRCO_SETOPT) {
+ u_long old_credit = tpcb->tp_maxlcredit;
+ tp_rsyset(tpcb);
+ if (tpcb->tp_rhiwat != so->so_rcv.sb_hiwat &&
+ tpcb->tp_state == TP_OPEN &&
+ (old_credit < tpcb->tp_maxlcredit))
+ tp_emit(AK_TPDU_type, tpcb,
+ tpcb->tp_rcvnxt, 0, MNULL);
+ tpcb->tp_rhiwat = so->so_rcv.sb_hiwat;
+ }
+ goto done;
+ } else if ( level != SOL_TRANSPORT ) {
+ error = EOPNOTSUPP; goto done;
+ }
+ if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
+ error = EOPNOTSUPP; goto done;
+ }
+ if ( so->so_error ) {
+ error = so->so_error; goto done;
+ }
+
+ /* The only options allowed after connection is established
+ * are GET (anything) and SET DISC DATA and SET PERF MEAS
+ */
+ if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED))
+ &&
+ (cmd == PRCO_SETOPT &&
+ optname != TPOPT_DISC_DATA &&
+ optname != TPOPT_CFRM_DATA &&
+ optname != TPOPT_PERF_MEAS &&
+ optname != TPOPT_CDDATA_CLEAR ) ) {
+ error = EISCONN; goto done;
+ }
+ /* The only options allowed after disconnection are GET DISC DATA,
+ * and TPOPT_PSTATISTICS
+ * and they're not allowed if the ref timer has gone off, because
+ * the tpcb is gone
+ */
+ if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) == 0) {
+ if ( so->so_pcb == (caddr_t)0 ) {
+ error = ENOTCONN; goto done;
+ }
+ if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) &&
+ (optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) {
+ error = ENOTCONN; goto done;
+ }
+ }
+
+ value = mtod(*mp, caddr_t); /* it's aligned, don't worry,
+ * but lint complains about it
+ */
+ val_len = (*mp)->m_len;
+
+ switch (optname) {
+
+ case TPOPT_INTERCEPT:
+#define INA(t) (((struct inpcb *)(t->tp_npcb))->inp_laddr.s_addr)
+#define ISOA(t) (((struct isopcb *)(t->tp_npcb))->isop_laddr->siso_addr)
+
+ if ((so->so_state & SS_PRIV) == 0) {
+ error = EPERM;
+ } else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_CLOSED ||
+ (tpcb->tp_flags & TPF_GENERAL_ADDR) ||
+ tpcb->tp_next == 0)
+ error = EINVAL;
+ else {
+ register struct tp_pcb *t;
+ error = EADDRINUSE;
+ for (t = tp_listeners; t; t = t->tp_nextlisten)
+ if ((t->tp_flags & TPF_GENERAL_ADDR) == 0 &&
+ t->tp_domain == tpcb->tp_domain)
+ switch (tpcb->tp_domain) {
+ default:
+ goto done;
+#ifdef INET
+ case AF_INET:
+ if (INA(t) == INA(tpcb))
+ goto done;
+ continue;
+#endif
+#ifdef ISO
+ case AF_ISO:
+ if (bcmp(ISOA(t).isoa_genaddr, ISOA(tpcb).isoa_genaddr,
+ ISOA(t).isoa_len) == 0)
+ goto done;
+ continue;
+#endif
+ }
+ tpcb->tp_lsuffixlen = 0;
+ tpcb->tp_state = TP_LISTENING;
+ error = 0;
+ remque(tpcb);
+ tpcb->tp_next = tpcb->tp_prev = tpcb;
+ tpcb->tp_nextlisten = tp_listeners;
+ tp_listeners = tpcb;
+ }
+ break;
+
+ case TPOPT_MY_TSEL:
+ if ( cmd == PRCO_GETOPT ) {
+ ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN );
+ bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen);
+ (*mp)->m_len = tpcb->tp_lsuffixlen;
+ } else /* cmd == PRCO_SETOPT */ {
+ if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
+ printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
+ error = EINVAL;
+ } else {
+ bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len);
+ tpcb->tp_lsuffixlen = val_len;
+ }
+ }
+ break;
+
+ case TPOPT_PEER_TSEL:
+ if ( cmd == PRCO_GETOPT ) {
+ ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN );
+ bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen);
+ (*mp)->m_len = tpcb->tp_fsuffixlen;
+ } else /* cmd == PRCO_SETOPT */ {
+ if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
+ printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
+ error = EINVAL;
+ } else {
+ bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len);
+ tpcb->tp_fsuffixlen = val_len;
+ }
+ }
+ break;
+
+ case TPOPT_FLAGS:
+ IFDEBUG(D_REQUEST)
+ printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n",
+ cmd==PRCO_GETOPT?"GET":"SET",
+ value,
+ *value,
+ tpcb->tp_flags);
+ ENDDEBUG
+
+ if ( cmd == PRCO_GETOPT ) {
+ *(int *)value = (int)tpcb->tp_flags;
+ (*mp)->m_len = sizeof(u_int);
+ } else /* cmd == PRCO_SETOPT */ {
+ error = EINVAL; goto done;
+ }
+ break;
+
+ case TPOPT_PARAMS:
+ /* This handles:
+ * timer values,
+ * class, use of transport expedited data,
+ * max tpdu size, checksum, xtd format and
+ * disconnect indications, and may get rid of connect/disc data
+ */
+ IFDEBUG(D_SETPARAMS)
+ printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
+ cmd==PRCO_GETOPT?"GET":"SET");
+ ENDDEBUG
+ IFDEBUG(D_REQUEST)
+ printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
+ cmd==PRCO_GETOPT?"GET":"SET");
+ ENDDEBUG
+
+ if ( cmd == PRCO_GETOPT ) {
+ *(struct tp_conn_param *)value = tpcb->_tp_param;
+ (*mp)->m_len = sizeof(tpcb->_tp_param);
+ } else /* cmd == PRCO_SETOPT */ {
+ if( (error =
+ tp_consistency(tpcb, TP_STRICT | TP_FORCE,
+ (struct tp_conn_param *)value))==0) {
+ /*
+ * tp_consistency doesn't copy the whole set of params
+ */
+ tpcb->_tp_param = *(struct tp_conn_param *)value;
+ (*mp)->m_len = sizeof(tpcb->_tp_param);
+ }
+ }
+ break;
+
+ case TPOPT_PSTATISTICS:
+#ifdef TP_PERF_MEAS
+ if (cmd == PRCO_SETOPT) {
+ error = EINVAL; goto done;
+ }
+ IFPERF(tpcb)
+ if (*mp) {
+ struct mbuf * n;
+ do {
+ MFREE(*mp, n);
+ *mp = n;
+ } while (n);
+ }
+ *mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK);
+ ENDPERF
+ else {
+ error = EINVAL; goto done;
+ }
+ break;
+#else
+ error = EOPNOTSUPP;
+ goto done;
+#endif /* TP_PERF_MEAS */
+
+ case TPOPT_CDDATA_CLEAR:
+ if (cmd == PRCO_GETOPT) {
+ error = EINVAL;
+ } else {
+ if (tpcb->tp_ucddata) {
+ m_freem(tpcb->tp_ucddata);
+ tpcb->tp_ucddata = 0;
+ }
+ }
+ break;
+
+ case TPOPT_CFRM_DATA:
+ case TPOPT_DISC_DATA:
+ case TPOPT_CONN_DATA:
+ if( tpcb->tp_class == TP_CLASS_0 ) {
+ error = EOPNOTSUPP;
+ break;
+ }
+ IFDEBUG(D_REQUEST)
+ printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data");
+ printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n",
+ (*mp)->m_len, val_len, so->so_snd.sb_cc);
+ dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
+ ENDDEBUG
+ if (cmd == PRCO_SETOPT) {
+ int len = tpcb->tp_ucddata ? tpcb->tp_ucddata->m_len : 0;
+ /* can append connect data in several calls */
+ if (len + val_len >
+ (optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) {
+ error = EMSGSIZE; goto done;
+ }
+ (*mp)->m_next = MNULL;
+ (*mp)->m_act = 0;
+ if (tpcb->tp_ucddata)
+ m_cat(tpcb->tp_ucddata, *mp);
+ else
+ tpcb->tp_ucddata = *mp;
+ IFDEBUG(D_REQUEST)
+ dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA");
+ ENDDEBUG
+ IFTRACE(D_REQUEST)
+ tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len",
+ tpcb->tp_flags, so->so_snd.sb_cc,val_len,0);
+ ENDTRACE
+ *mp = MNULL;
+ if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING))
+ (void) tp_confirm(tpcb);
+ }
+ break;
+
+ case TPOPT_PERF_MEAS:
+#ifdef TP_PERF_MEAS
+ if (cmd == PRCO_GETOPT) {
+ *value = (u_int)tpcb->tp_perf_on;
+ (*mp)->m_len = sizeof(u_int);
+ } else if (cmd == PRCO_SETOPT) {
+ (*mp)->m_len = 0;
+ if ((*value) != 0 && (*value) != 1 )
+ error = EINVAL;
+ else tpcb->tp_perf_on = (*value);
+ }
+ if( tpcb->tp_perf_on )
+ error = tp_setup_perf(tpcb);
+#else /* TP_PERF_MEAS */
+ error = EOPNOTSUPP;
+#endif /* TP_PERF_MEAS */
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ }
+
+done:
+ IFDEBUG(D_REQUEST)
+ dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
+ dump_mbuf(*mp, "tp_ctloutput *mp");
+ ENDDEBUG
+ /*
+ * sigh: getsockopt looks only at m_len : all output data must
+ * reside in the first mbuf
+ */
+ if (*mp) {
+ if (cmd == PRCO_SETOPT) {
+ m_freem(*mp);
+ *mp = MNULL;
+ } else {
+ ASSERT ( m_compress(*mp, mp) <= MLEN );
+ if (error)
+ (*mp)->m_len = 0;
+ IFDEBUG(D_REQUEST)
+ dump_mbuf(*mp, "tp_ctloutput *mp after compress");
+ ENDDEBUG
+ }
+ }
+ splx(s);
+ return error;
+}
diff --git a/sys/netiso/tp_param.h b/sys/netiso/tp_param.h
new file mode 100644
index 0000000..f1862a2
--- /dev/null
+++ b/sys/netiso/tp_param.h
@@ -0,0 +1,367 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_param.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_param.h,v 5.3 88/11/18 17:28:18 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_param.h,v $
+ *
+ */
+
+#ifndef __TP_PARAM__
+#define __TP_PARAM__
+
+
+/******************************************************
+ * compile time parameters that can be changed
+ *****************************************************/
+
+#define TP_CLASSES_IMPLEMENTED 0x11 /* zero and 4 */
+
+#define TP_DECBIT_CLEAR_COUNT 3
+
+/*#define N_TPREF 100 */
+#ifdef KERNEL
+extern int N_TPREF;
+#endif
+
+#define TP_SOCKBUFSIZE ((u_long)4096)
+#define TP0_SOCKBUFSIZE ((u_long)512)
+#define MAX_TSAP_SEL_LEN 64
+
+/* maximum tpdu size we'll accept: */
+#define TP_TPDUSIZE 0xc /* 4096 octets for classes 1-4*/
+#define TP0_TPDUSIZE 0xb /* 2048 octets for class 0 */
+#define TP_DFL_TPDUSIZE 0x7 /* 128 octets default */
+ /* NOTE: don't ever negotiate 8192 because could get
+ * wraparound in checksumming
+ * (No mtu is likely to be larger than 4K anyway...)
+ */
+#define TP_NRETRANS 12 /* TCP_MAXRXTSHIFT + 1 */
+#define TP_MAXRXTSHIFT 6 /* factor of 64 */
+#define TP_MAXPORT 0xefff
+
+/* ALPHA: to be used in the context: gain= 1/(2**alpha), or
+ * put another way, gaintimes(x) (x)>>alpha (forgetting the case alpha==0)
+ */
+#define TP_RTT_ALPHA 3
+#define TP_RTV_ALPHA 2
+#define TP_REXMTVAL(tpcb)\
+ ((tp_rttadd + (tpcb)->tp_rtt + ((tpcb)->tp_rtv) << 2) / tp_rttdiv)
+#define TP_RANGESET(tv, value, min, max) \
+ ((tv = value) > (max) ? (tv = max) : (tv < min ? tv = min : tv))
+
+/*
+ * not sure how to treat data on disconnect
+ */
+#define T_CONN_DATA 0x1
+#define T_DISCONNECT 0x2
+#define T_DISC_DATA 0x4
+#define T_XDATA 0x8
+
+#define ISO_CLNS 0
+#define IN_CLNS 1
+#define ISO_CONS 2
+#define ISO_COSNS 3
+#define TP_MAX_NETSERVICES 3
+
+/* Indices into tp stats ackreason[i] */
+#define _ACK_DONT_ 0
+#define _ACK_STRAT_EACH_ 0x1
+#define _ACK_STRAT_FULLWIN_ 0x2
+#define _ACK_DUP_ 0x3
+#define _ACK_EOT_ 0x4
+#define _ACK_REORDER_ 0x5
+#define _ACK_USRRCV_ 0x6
+#define _ACK_FCC_ 0x7
+#define _ACK_NUM_REASONS_ 0x8
+
+/* masks for use in tp_stash() */
+#define ACK_DONT 0
+#define ACK_STRAT_EACH (1<< _ACK_STRAT_EACH_)
+#define ACK_STRAT_FULLWIN (1<< _ACK_STRAT_FULLWIN_)
+#define ACK_DUP (1<< _ACK_DUP_)
+#define ACK_EOT (1<< _ACK_EOT_)
+#define ACK_REORDER (1<< _ACK_REORDER_)
+
+/******************************************************
+ * constants used in the protocol
+ *****************************************************/
+
+#define TP_VERSION 0x1
+
+#define TP_MAX_HEADER_LEN 256
+
+#define TP_MIN_TPDUSIZE 0x7 /* 128 octets */
+#define TP_MAX_TPDUSIZE 0xd /* 8192 octets */
+
+#define TP_MAX_XPD_DATA 0x10 /* 16 octets */
+#define TP_MAX_CC_DATA 0x20 /* 32 octets */
+#define TP_MAX_CR_DATA TP_MAX_CC_DATA
+#define TP_MAX_DR_DATA 0x40 /* 64 octets */
+
+#define TP_XTD_FMT_BIT 0x80000000
+#define TP_XTD_FMT_MASK 0x7fffffff
+#define TP_NML_FMT_BIT 0x80
+#define TP_NML_FMT_MASK 0x7f
+
+/*
+ * values for the tpdu_type field, 2nd byte in a tpdu
+ */
+
+#define TP_MIN_TPDUTYPE 0x1
+
+#define XPD_TPDU_type 0x1
+#define XAK_TPDU_type 0x2
+#define GR_TPDU_type 0x3
+#define AK_TPDU_type 0x6
+#define ER_TPDU_type 0x7
+#define DR_TPDU_type 0x8
+#define DC_TPDU_type 0xc
+#define CC_TPDU_type 0xd
+#define CR_TPDU_type 0xe
+#define DT_TPDU_type 0xf
+
+#define TP_MAX_TPDUTYPE 0xf
+
+/*
+ * identifiers for the variable-length options in tpdus
+ */
+
+#define TPP_acktime 0x85
+#define TPP_residER 0x86
+#define TPP_priority 0x87
+#define TPP_transdelay 0x88
+#define TPP_throughput 0x89
+#define TPP_subseq 0x8a
+#define TPP_flow_cntl_conf 0x8c /* not implemented */
+#define TPP_addl_info 0xe0
+#define TPP_tpdu_size 0xc0
+#define TPP_calling_sufx 0xc1
+#define TPP_invalid_tpdu 0xc1 /* the bozos used a value twice */
+#define TPP_called_sufx 0xc2
+#define TPP_checksum 0xc3
+#define TPP_vers 0xc4
+#define TPP_security 0xc5
+#define TPP_addl_opt 0xc6
+#define TPP_alt_class 0xc7
+#define TPP_perf_meas 0xc8 /* local item : perf meas on, svp */
+#define TPP_ptpdu_size 0xf0 /* preferred TPDU size */
+#define TPP_inact_time 0xf2 /* inactivity time exchanged */
+
+
+/******************************************************
+ * Some fundamental data types
+ *****************************************************/
+#ifndef TRUE
+#define TRUE 1
+#endif /* TRUE */
+
+#ifndef FALSE
+#define FALSE 0
+#endif /* FALSE */
+
+#define TP_LOCAL 22
+#define TP_FOREIGN 33
+
+#ifndef EOK
+#define EOK 0
+#endif /* EOK */
+
+#define TP_CLASS_0 (1<<0)
+#define TP_CLASS_1 (1<<1)
+#define TP_CLASS_2 (1<<2)
+#define TP_CLASS_3 (1<<3)
+#define TP_CLASS_4 (1<<4)
+
+#define TP_FORCE 0x1
+#define TP_STRICT 0x2
+
+#ifndef MNULL
+#define MNULL (struct mbuf *)0
+#endif /* MNULL */
+ /* if ../sys/mbuf.h gets MT_types up to 0x40, these will
+ * have to be changed:
+ */
+#define MT_XPD 0x44
+#define MT_EOT 0x40
+
+#define TP_ENOREF 0x80000000
+
+typedef unsigned int SeqNum;
+typedef unsigned short RefNum;
+typedef int ProtoHook;
+
+/******************************************************
+ * Macro used all over, for driver
+ *****************************************************/
+
+#define DoEvent(x) \
+ ((E.ev_number=(x)),(tp_driver(tpcb,&E)))
+
+/******************************************************
+ * Some macros used all over, for timestamping
+ *****************************************************/
+
+#define GET_CUR_TIME(tvalp) ((*tvalp) = time)
+
+#define GET_TIME_SINCE(oldtvalp, diffp) {\
+ (diffp)->tv_sec = time.tv_sec - (oldtvalp)->tv_sec;\
+ (diffp)->tv_usec = time.tv_usec - (oldtvalp)->tv_usec;\
+ if( (diffp)->tv_usec <0 ) {\
+ (diffp)->tv_sec --;\
+ (diffp)->tv_usec = 1000000 - (diffp)->tv_usec;\
+ }\
+}
+
+/******************************************************
+ * Some macros used for address families
+ *****************************************************/
+
+#define satosiso(ADDR) ((struct sockaddr_iso *)(ADDR))
+#define satosin(ADDR) ((struct sockaddr_in *)(ADDR))
+
+/******************************************************
+ * Macro used for changing types of mbufs
+ *****************************************************/
+
+#define CHANGE_MTYPE(m, TYPE)\
+ if((m)->m_type != TYPE) { \
+ mbstat.m_mtypes[(m)->m_type]--; mbstat.m_mtypes[TYPE]++; \
+ (m)->m_type = TYPE; \
+ }
+
+/******************************************************
+ * Macros used for adding options to a tpdu header and for
+ * parsing the headers.
+ * Options are variable-length and must be bcopy-d because on the
+ * RT your assignments must be N-word aligned for objects of length
+ * N. Such a drag.
+ *****************************************************/
+
+struct tp_vbp {
+ u_char tpv_code;
+ char tpv_len;
+ char tpv_val;
+};
+#define vbptr(x) ((struct tp_vbp *)(x))
+#define vbval(x,type) (*((type *)&(((struct tp_vbp *)(x))->tpv_val)))
+#define vbcode(x) (vbptr(x)->tpv_code)
+#define vblen(x) (vbptr(x)->tpv_len)
+
+#define vb_putval(dst,type,src)\
+ bcopy((caddr_t)&(src),(caddr_t)&(((struct tp_vbp *)(dst))->tpv_val),\
+ sizeof(type))
+
+#define vb_getval(src,type,dst)\
+bcopy((caddr_t)&(((struct tp_vbp *)(src))->tpv_val),(caddr_t)&(dst),sizeof(type))
+
+#define ADDOPTION(type, DU, len, src)\
+{ register caddr_t P;\
+ P = (caddr_t)(DU) + (int)((DU)->tpdu_li);\
+ vbptr(P)->tpv_code = type;\
+ vbptr(P)->tpv_len = len;\
+ bcopy((caddr_t)&src, (caddr_t)&(vbptr(P)->tpv_val), (unsigned)len);\
+ DU->tpdu_li += len+2;/* 1 for code, 1 for length */\
+}
+/******************************************************
+ * Macro for the local credit:
+ * uses max transmission unit for the ll
+ * (as modified by the max TPDU size negotiated)
+ *****************************************************/
+
+#if defined(ARGO_DEBUG)&&!defined(LOCAL_CREDIT_EXPAND)
+#define LOCAL_CREDIT(tpcb) tp_local_credit(tpcb)
+#else
+#define LOCAL_CREDIT(tpcb) { if (tpcb->tp_rsycnt == 0) {\
+ register struct sockbuf *xxsb = &((tpcb)->tp_sock->so_rcv);\
+ register int xxi = sbspace(xxsb);\
+ xxi = (xxi<0) ? 0 : ((xxi) / (tpcb)->tp_l_tpdusize);\
+ xxi = min(xxi, (tpcb)->tp_maxlcredit); \
+ if (!(tpcb->tp_cebit_off)) { \
+ (tpcb)->tp_lcredit = ROUND((tpcb)->tp_win_recv); \
+ if (xxi < (tpcb)->tp_lcredit) { \
+ (tpcb)->tp_lcredit = xxi; \
+ } \
+ } else \
+ (tpcb)->tp_lcredit = xxi; \
+} }
+#endif /* ARGO_DEBUG */
+
+#ifdef KERNEL
+extern int tp_rttadd, tp_rttdiv;
+#include <sys/syslog.h>
+#define printf logpri(LOG_DEBUG),addlog
+
+#ifndef tp_NSTATES
+
+#include <netiso/tp_states.h>
+#include <netiso/tp_events.h>
+#if defined(__STDC__) || defined(__cplusplus)
+#undef ATTR
+#define ATTR(X) ev_union.EV_ ## X
+#endif /* defined(__STDC__) || defined(__cplusplus) */
+
+#endif /* tp_NSTATES */
+#endif /* KERNEL */
+
+#endif /* __TP_PARAM__ */
diff --git a/sys/netiso/tp_pcb.c b/sys/netiso/tp_pcb.c
new file mode 100644
index 0000000..de345c1
--- /dev/null
+++ b/sys/netiso/tp_pcb.c
@@ -0,0 +1,999 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_pcb.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_pcb.c,v 5.4 88/11/18 17:28:24 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_pcb.c,v $
+ *
+ *
+ * This is the initialization and cleanup stuff -
+ * for the tp machine in general as well as for the individual pcbs.
+ * tp_init() is called at system startup. tp_attach() and tp_getref() are
+ * called when a socket is created. tp_detach() and tp_freeref()
+ * are called during the closing stage and/or when the reference timer
+ * goes off.
+ * tp_soisdisconnecting() and tp_soisdisconnected() are tp-specific
+ * versions of soisconnect*
+ * and are called (obviously) during the closing phase.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <netiso/argo_debug.h>
+#include <netiso/tp_param.h>
+#include <netiso/tp_timer.h>
+#include <netiso/tp_ip.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/tp_tpdu.h>
+#include <netiso/tp_trace.h>
+#include <netiso/tp_meas.h>
+#include <netiso/tp_seq.h>
+#include <netiso/tp_clnp.h>
+
+/* ticks are in units of:
+ * 500 nano-fortnights ;-) or
+ * 500 ms or
+ * 1/2 second
+ */
+
+struct tp_conn_param tp_conn_param[] = {
+ /* ISO_CLNS: TP4 CONNECTION LESS */
+ {
+ TP_NRETRANS, /* short p_Nretrans; */
+ 20, /* 10 sec */ /* short p_dr_ticks; */
+
+ 20, /* 10 sec */ /* short p_cc_ticks; */
+ 20, /* 10 sec */ /* short p_dt_ticks; */
+
+ 40, /* 20 sec */ /* short p_x_ticks; */
+ 80, /* 40 sec */ /* short p_cr_ticks;*/
+
+ 240, /* 2 min */ /* short p_keepalive_ticks;*/
+ 10, /* 5 sec */ /* short p_sendack_ticks; */
+
+ 600, /* 5 min */ /* short p_ref_ticks; */
+ 360, /* 3 min */ /* short p_inact_ticks; */
+
+ (short) 100, /* short p_lcdtfract */
+ (short) TP_SOCKBUFSIZE, /* short p_winsize */
+ TP_TPDUSIZE, /* u_char p_tpdusize */
+
+ TPACK_WINDOW, /* 4 bits p_ack_strat */
+ TPRX_USE_CW | TPRX_FASTSTART,
+ /* 4 bits p_rx_strat*/
+ TP_CLASS_4 | TP_CLASS_0,/* 5 bits p_class */
+ 1, /* 1 bit xtd format */
+ 1, /* 1 bit xpd service */
+ 1, /* 1 bit use_checksum */
+ 0, /* 1 bit use net xpd */
+ 0, /* 1 bit use rcc */
+ 0, /* 1 bit use efc */
+ 1, /* no disc indications */
+ 0, /* don't change params */
+ ISO_CLNS, /* p_netservice */
+ },
+ /* IN_CLNS: TP4 CONNECTION LESS */
+ {
+ TP_NRETRANS, /* short p_Nretrans; */
+ 20, /* 10 sec */ /* short p_dr_ticks; */
+
+ 20, /* 10 sec */ /* short p_cc_ticks; */
+ 20, /* 10 sec */ /* short p_dt_ticks; */
+
+ 40, /* 20 sec */ /* short p_x_ticks; */
+ 80, /* 40 sec */ /* short p_cr_ticks;*/
+
+ 240, /* 2 min */ /* short p_keepalive_ticks;*/
+ 10, /* 5 sec */ /* short p_sendack_ticks; */
+
+ 600, /* 5 min */ /* short p_ref_ticks; */
+ 360, /* 3 min */ /* short p_inact_ticks; */
+
+ (short) 100, /* short p_lcdtfract */
+ (short) TP_SOCKBUFSIZE, /* short p_winsize */
+ TP_TPDUSIZE, /* u_char p_tpdusize */
+
+ TPACK_WINDOW, /* 4 bits p_ack_strat */
+ TPRX_USE_CW | TPRX_FASTSTART,
+ /* 4 bits p_rx_strat*/
+ TP_CLASS_4, /* 5 bits p_class */
+ 1, /* 1 bit xtd format */
+ 1, /* 1 bit xpd service */
+ 1, /* 1 bit use_checksum */
+ 0, /* 1 bit use net xpd */
+ 0, /* 1 bit use rcc */
+ 0, /* 1 bit use efc */
+ 1, /* no disc indications */
+ 0, /* don't change params */
+ IN_CLNS, /* p_netservice */
+ },
+ /* ISO_CONS: TP0 CONNECTION MODE */
+ {
+ TP_NRETRANS, /* short p_Nretrans; */
+ 0, /* n/a */ /* short p_dr_ticks; */
+
+ 40, /* 20 sec */ /* short p_cc_ticks; */
+ 0, /* n/a */ /* short p_dt_ticks; */
+
+ 0, /* n/a */ /* short p_x_ticks; */
+ 360, /* 3 min */ /* short p_cr_ticks;*/
+
+ 0, /* n/a */ /* short p_keepalive_ticks;*/
+ 0, /* n/a */ /* short p_sendack_ticks; */
+
+ 600, /* for cr/cc to clear *//* short p_ref_ticks; */
+ 0, /* n/a */ /* short p_inact_ticks; */
+
+ /* Use tp4 defaults just in case the user changes ONLY
+ * the class
+ */
+ (short) 100, /* short p_lcdtfract */
+ (short) TP0_SOCKBUFSIZE, /* short p_winsize */
+ TP0_TPDUSIZE, /* 8 bits p_tpdusize */
+
+ 0, /* 4 bits p_ack_strat */
+ 0, /* 4 bits p_rx_strat*/
+ TP_CLASS_0, /* 5 bits p_class */
+ 0, /* 1 bit xtd format */
+ 0, /* 1 bit xpd service */
+ 0, /* 1 bit use_checksum */
+ 0, /* 1 bit use net xpd */
+ 0, /* 1 bit use rcc */
+ 0, /* 1 bit use efc */
+ 0, /* no disc indications */
+ 0, /* don't change params */
+ ISO_CONS, /* p_netservice */
+ },
+ /* ISO_COSNS: TP4 CONNECTION LESS SERVICE over CONSNS */
+ {
+ TP_NRETRANS, /* short p_Nretrans; */
+ 40, /* 20 sec */ /* short p_dr_ticks; */
+
+ 40, /* 20 sec */ /* short p_cc_ticks; */
+ 80, /* 40 sec */ /* short p_dt_ticks; */
+
+ 120, /* 1 min */ /* short p_x_ticks; */
+ 360, /* 3 min */ /* short p_cr_ticks;*/
+
+ 360, /* 3 min */ /* short p_keepalive_ticks;*/
+ 20, /* 10 sec */ /* short p_sendack_ticks; */
+
+ 600, /* 5 min */ /* short p_ref_ticks; */
+ 480, /* 4 min */ /* short p_inact_ticks; */
+
+ (short) 100, /* short p_lcdtfract */
+ (short) TP0_SOCKBUFSIZE, /* short p_winsize */
+ TP0_TPDUSIZE, /* u_char p_tpdusize */
+
+ TPACK_WINDOW, /* 4 bits p_ack_strat */
+ TPRX_USE_CW , /* No fast start */
+ /* 4 bits p_rx_strat*/
+ TP_CLASS_4 | TP_CLASS_0,/* 5 bits p_class */
+ 0, /* 1 bit xtd format */
+ 1, /* 1 bit xpd service */
+ 1, /* 1 bit use_checksum */
+ 0, /* 1 bit use net xpd */
+ 0, /* 1 bit use rcc */
+ 0, /* 1 bit use efc */
+ 0, /* no disc indications */
+ 0, /* don't change params */
+ ISO_COSNS, /* p_netservice */
+ },
+};
+
+#ifdef INET
+int in_putnetaddr();
+int in_getnetaddr();
+int in_cmpnetaddr();
+int in_putsufx();
+int in_getsufx();
+int in_recycle_tsuffix();
+int tpip_mtu();
+int in_pcbbind();
+int in_pcbconnect();
+int in_pcbdisconnect();
+int in_pcbdetach();
+int in_pcballoc();
+int tpip_output();
+int tpip_output_dg();
+struct inpcb tp_inpcb;
+#endif /* INET */
+#ifdef ISO
+int iso_putnetaddr();
+int iso_getnetaddr();
+int iso_cmpnetaddr();
+int iso_putsufx();
+int iso_getsufx();
+int iso_recycle_tsuffix();
+int tpclnp_mtu();
+int iso_pcbbind();
+int iso_pcbconnect();
+int iso_pcbdisconnect();
+int iso_pcbdetach();
+int iso_pcballoc();
+int tpclnp_output();
+int tpclnp_output_dg();
+int iso_nlctloutput();
+struct isopcb tp_isopcb;
+#endif /* ISO */
+#ifdef TPCONS
+int iso_putnetaddr();
+int iso_getnetaddr();
+int iso_cmpnetaddr();
+int iso_putsufx();
+int iso_getsufx();
+int iso_recycle_tsuffix();
+int iso_pcbbind();
+int tpcons_pcbconnect();
+int tpclnp_mtu();
+int iso_pcbdisconnect();
+int iso_pcbdetach();
+int iso_pcballoc();
+int tpcons_output();
+struct isopcb tp_isopcb;
+#endif /* TPCONS */
+
+
+struct nl_protosw nl_protosw[] = {
+ /* ISO_CLNS */
+#ifdef ISO
+ { AF_ISO, iso_putnetaddr, iso_getnetaddr, iso_cmpnetaddr,
+ iso_putsufx, iso_getsufx,
+ iso_recycle_tsuffix,
+ tpclnp_mtu, iso_pcbbind, iso_pcbconnect,
+ iso_pcbdisconnect, iso_pcbdetach,
+ iso_pcballoc,
+ tpclnp_output, tpclnp_output_dg, iso_nlctloutput,
+ (caddr_t) &tp_isopcb,
+ },
+#else
+ { 0 },
+#endif /* ISO */
+ /* IN_CLNS */
+#ifdef INET
+ { AF_INET, in_putnetaddr, in_getnetaddr, in_cmpnetaddr,
+ in_putsufx, in_getsufx,
+ in_recycle_tsuffix,
+ tpip_mtu, in_pcbbind, in_pcbconnect,
+ in_pcbdisconnect, in_pcbdetach,
+ in_pcballoc,
+ tpip_output, tpip_output_dg, /* nl_ctloutput */ NULL,
+ (caddr_t) &tp_inpcb,
+ },
+#else
+ { 0 },
+#endif /* INET */
+ /* ISO_CONS */
+#if defined(ISO) && defined(TPCONS)
+ { AF_ISO, iso_putnetaddr, iso_getnetaddr, iso_cmpnetaddr,
+ iso_putsufx, iso_getsufx,
+ iso_recycle_tsuffix,
+ tpclnp_mtu, iso_pcbbind, tpcons_pcbconnect,
+ iso_pcbdisconnect, iso_pcbdetach,
+ iso_pcballoc,
+ tpcons_output, tpcons_output, iso_nlctloutput,
+ (caddr_t) &tp_isopcb,
+ },
+#else
+ { 0 },
+#endif /* ISO_CONS */
+ /* End of protosw marker */
+ { 0 }
+};
+
+u_long tp_sendspace = 1024 * 4;
+u_long tp_recvspace = 1024 * 4;
+
+/*
+ * NAME: tp_init()
+ *
+ * CALLED FROM:
+ * autoconf through the protosw structure
+ *
+ * FUNCTION:
+ * initialize tp machine
+ *
+ * RETURNS: Nada
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+int
+tp_init()
+{
+ static int init_done=0;
+ void tp_timerinit();
+
+ if (init_done++)
+ return 0;
+
+
+ /* FOR INET */
+ tp_inpcb.inp_next = tp_inpcb.inp_prev = &tp_inpcb;
+ /* FOR ISO */
+ tp_isopcb.isop_next = tp_isopcb.isop_prev = &tp_isopcb;
+
+ tp_start_win = 2;
+
+ tp_timerinit();
+ bzero((caddr_t)&tp_stat, sizeof(struct tp_stat));
+ return 0;
+}
+
+/*
+ * NAME: tp_soisdisconnecting()
+ *
+ * CALLED FROM:
+ * tp.trans
+ *
+ * FUNCTION and ARGUMENTS:
+ * Set state of the socket (so) to reflect that fact that we're disconnectING
+ *
+ * RETURNS: Nada
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ * This differs from the regular soisdisconnecting() in that the latter
+ * also sets the SS_CANTRECVMORE and SS_CANTSENDMORE flags.
+ * We don't want to set those flags because those flags will cause
+ * a SIGPIPE to be delivered in sosend() and we don't like that.
+ * If anyone else is sleeping on this socket, wake 'em up.
+ */
+void
+tp_soisdisconnecting(so)
+ register struct socket *so;
+{
+ soisdisconnecting(so);
+ so->so_state &= ~SS_CANTSENDMORE;
+ IFPERF(sototpcb(so))
+ register struct tp_pcb *tpcb = sototpcb(so);
+ u_int fsufx, lsufx;
+
+ bcopy ((caddr_t)tpcb->tp_fsuffix, (caddr_t)&fsufx, sizeof(u_int) );
+ bcopy ((caddr_t)tpcb->tp_lsuffix, (caddr_t)&lsufx, sizeof(u_int) );
+
+ tpmeas(tpcb->tp_lref, TPtime_close, &time, fsufx, lsufx, tpcb->tp_fref);
+ tpcb->tp_perf_on = 0; /* turn perf off */
+ ENDPERF
+}
+
+
+/*
+ * NAME: tp_soisdisconnected()
+ *
+ * CALLED FROM:
+ * tp.trans
+ *
+ * FUNCTION and ARGUMENTS:
+ * Set state of the socket (so) to reflect that fact that we're disconnectED
+ * Set the state of the reference structure to closed, and
+ * recycle the suffix.
+ * Start a reference timer.
+ *
+ * RETURNS: Nada
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ * This differs from the regular soisdisconnected() in that the latter
+ * also sets the SS_CANTRECVMORE and SS_CANTSENDMORE flags.
+ * We don't want to set those flags because those flags will cause
+ * a SIGPIPE to be delivered in sosend() and we don't like that.
+ * If anyone else is sleeping on this socket, wake 'em up.
+ */
+void
+tp_soisdisconnected(tpcb)
+ register struct tp_pcb *tpcb;
+{
+ register struct socket *so = tpcb->tp_sock;
+
+ soisdisconnecting(so);
+ so->so_state &= ~SS_CANTSENDMORE;
+ IFPERF(tpcb)
+ register struct tp_pcb *ttpcb = sototpcb(so);
+ u_int fsufx, lsufx;
+
+ /* CHOKE */
+ bcopy ((caddr_t)ttpcb->tp_fsuffix, (caddr_t)&fsufx, sizeof(u_int) );
+ bcopy ((caddr_t)ttpcb->tp_lsuffix, (caddr_t)&lsufx, sizeof(u_int) );
+
+ tpmeas(ttpcb->tp_lref, TPtime_close,
+ &time, &lsufx, &fsufx, ttpcb->tp_fref);
+ tpcb->tp_perf_on = 0; /* turn perf off */
+ ENDPERF
+
+ tpcb->tp_refstate = REF_FROZEN;
+ tp_recycle_tsuffix(tpcb);
+ tp_etimeout(tpcb, TM_reference, (int)tpcb->tp_refer_ticks);
+}
+
+/*
+ * NAME: tp_freeref()
+ *
+ * CALLED FROM:
+ * tp.trans when the reference timer goes off, and
+ * from tp_attach() and tp_detach() when a tpcb is partially set up but not
+ * set up enough to have a ref timer set for it, and it's discarded
+ * due to some sort of error or an early close()
+ *
+ * FUNCTION and ARGUMENTS:
+ * Frees the reference represented by (r) for re-use.
+ *
+ * RETURNS: Nothing
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES: better be called at clock priority !!!!!
+ */
+void
+tp_freeref(n)
+RefNum n;
+{
+ register struct tp_ref *r = tp_ref + n;
+ register struct tp_pcb *tpcb;
+
+ tpcb = r->tpr_pcb;
+ IFDEBUG(D_TIMER)
+ printf("tp_freeref called for ref %d pcb %x maxrefopen %d\n",
+ n, tpcb, tp_refinfo.tpr_maxopen);
+ ENDDEBUG
+ IFTRACE(D_TIMER)
+ tptrace(TPPTmisc, "tp_freeref ref maxrefopen pcb",
+ n, tp_refinfo.tpr_maxopen, tpcb, 0);
+ ENDTRACE
+ if (tpcb == 0)
+ return;
+ IFDEBUG(D_CONN)
+ printf("tp_freeref: CLEARING tpr_pcb 0x%x\n", tpcb);
+ ENDDEBUG
+ r->tpr_pcb = (struct tp_pcb *)0;
+ tpcb->tp_refstate = REF_FREE;
+
+ for (r = tp_ref + tp_refinfo.tpr_maxopen; r > tp_ref; r--)
+ if (r->tpr_pcb)
+ break;
+ tp_refinfo.tpr_maxopen = r - tp_ref;
+ tp_refinfo.tpr_numopen--;
+
+ IFDEBUG(D_TIMER)
+ printf("tp_freeref ends w/ maxrefopen %d\n", tp_refinfo.tpr_maxopen);
+ ENDDEBUG
+}
+
+/*
+ * NAME: tp_getref()
+ *
+ * CALLED FROM:
+ * tp_attach()
+ *
+ * FUNCTION and ARGUMENTS:
+ * obtains the next free reference and allocates the appropriate
+ * ref structure, links that structure to (tpcb)
+ *
+ * RETURN VALUE:
+ * a reference number
+ * or TP_ENOREF
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+u_long
+tp_getref(tpcb)
+ register struct tp_pcb *tpcb;
+{
+ register struct tp_ref *r, *rlim;
+ register int i;
+ caddr_t obase;
+ unsigned size;
+
+ if (++tp_refinfo.tpr_numopen < tp_refinfo.tpr_size)
+ for (r = tp_refinfo.tpr_base, rlim = r + tp_refinfo.tpr_size;
+ ++r < rlim; ) /* tp_ref[0] is never used */
+ if (r->tpr_pcb == 0)
+ goto got_one;
+ /* else have to allocate more space */
+
+ obase = (caddr_t)tp_refinfo.tpr_base;
+ size = tp_refinfo.tpr_size * sizeof(struct tp_ref);
+ r = (struct tp_ref *) malloc(size + size, M_PCB, M_NOWAIT);
+ if (r == 0)
+ return (--tp_refinfo.tpr_numopen, TP_ENOREF);
+ tp_refinfo.tpr_base = tp_ref = r;
+ tp_refinfo.tpr_size *= 2;
+ bcopy(obase, (caddr_t)r, size);
+ free(obase, M_PCB);
+ r = (struct tp_ref *)(size + (caddr_t)r);
+ bzero((caddr_t)r, size);
+
+got_one:
+ r->tpr_pcb = tpcb;
+ tpcb->tp_refstate = REF_OPENING;
+ i = r - tp_refinfo.tpr_base;
+ if (tp_refinfo.tpr_maxopen < i)
+ tp_refinfo.tpr_maxopen = i;
+ return (u_long)i;
+}
+
+/*
+ * NAME: tp_set_npcb()
+ *
+ * CALLED FROM:
+ * tp_attach(), tp_route_to()
+ *
+ * FUNCTION and ARGUMENTS:
+ * given a tpcb, allocate an appropriate lower-lever npcb, freeing
+ * any old ones that might need re-assigning.
+ */
+tp_set_npcb(tpcb)
+register struct tp_pcb *tpcb;
+{
+ register struct socket *so = tpcb->tp_sock;
+ int error;
+
+ if (tpcb->tp_nlproto && tpcb->tp_npcb) {
+ short so_state = so->so_state;
+ so->so_state &= ~SS_NOFDREF;
+ tpcb->tp_nlproto->nlp_pcbdetach(tpcb->tp_npcb);
+ so->so_state = so_state;
+ }
+ tpcb->tp_nlproto = &nl_protosw[tpcb->tp_netservice];
+ /* xx_pcballoc sets so_pcb */
+ error = tpcb->tp_nlproto->nlp_pcballoc(so, tpcb->tp_nlproto->nlp_pcblist);
+ tpcb->tp_npcb = so->so_pcb;
+ so->so_pcb = (caddr_t)tpcb;
+ return (error);
+}
+/*
+ * NAME: tp_attach()
+ *
+ * CALLED FROM:
+ * tp_usrreq, PRU_ATTACH
+ *
+ * FUNCTION and ARGUMENTS:
+ * given a socket (so) and a protocol family (dom), allocate a tpcb
+ * and ref structure, initialize everything in the structures that
+ * needs to be initialized.
+ *
+ * RETURN VALUE:
+ * 0 ok
+ * EINVAL if DEBUG(X) in is on and a disaster has occurred
+ * ENOPROTOOPT if TP hasn't been configured or if the
+ * socket wasn't created with tp as its protocol
+ * EISCONN if this socket is already part of a connection
+ * ETOOMANYREFS if ran out of tp reference numbers.
+ * E* whatever error is returned from soreserve()
+ * for from the network-layer pcb allocation routine
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+tp_attach(so, protocol)
+ struct socket *so;
+ int protocol;
+{
+ register struct tp_pcb *tpcb;
+ int error = 0;
+ int dom = so->so_proto->pr_domain->dom_family;
+ u_long lref;
+ extern struct tp_conn_param tp_conn_param[];
+
+ IFDEBUG(D_CONN)
+ printf("tp_attach:dom 0x%x so 0x%x ", dom, so);
+ ENDDEBUG
+ IFTRACE(D_CONN)
+ tptrace(TPPTmisc, "tp_attach:dom so", dom, so, 0, 0);
+ ENDTRACE
+
+ if (so->so_pcb != NULL) {
+ return EISCONN; /* socket already part of a connection*/
+ }
+
+ if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0)
+ error = soreserve(so, tp_sendspace, tp_recvspace);
+ /* later an ioctl will allow reallocation IF still in closed state */
+
+ if (error)
+ goto bad2;
+
+ MALLOC(tpcb, struct tp_pcb *, sizeof(*tpcb), M_PCB, M_NOWAIT);
+ if (tpcb == NULL) {
+ error = ENOBUFS;
+ goto bad2;
+ }
+ bzero( (caddr_t)tpcb, sizeof (struct tp_pcb) );
+
+ if ( ((lref = tp_getref(tpcb)) & TP_ENOREF) != 0 ) {
+ error = ETOOMANYREFS;
+ goto bad3;
+ }
+ tpcb->tp_lref = lref;
+ tpcb->tp_sock = so;
+ tpcb->tp_domain = dom;
+ tpcb->tp_rhiwat = so->so_rcv.sb_hiwat;
+ /* tpcb->tp_proto = protocol; someday maybe? */
+ if (protocol && protocol<ISOPROTO_TP4) {
+ tpcb->tp_netservice = ISO_CONS;
+ tpcb->tp_snduna = (SeqNum) -1;/* kludge so the pseudo-ack from the CR/CC
+ * will generate correct fake-ack values
+ */
+ } else {
+ tpcb->tp_netservice = (dom== AF_INET)?IN_CLNS:ISO_CLNS;
+ /* the default */
+ }
+ tpcb->_tp_param = tp_conn_param[tpcb->tp_netservice];
+
+ tpcb->tp_state = TP_CLOSED;
+ tpcb->tp_vers = TP_VERSION;
+ tpcb->tp_notdetached = 1;
+
+ /* Spec says default is 128 octets,
+ * that is, if the tpdusize argument never appears, use 128.
+ * As the initiator, we will always "propose" the 2048
+ * size, that is, we will put this argument in the CR
+ * always, but accept what the other side sends on the CC.
+ * If the initiator sends us something larger on a CR,
+ * we'll respond w/ this.
+ * Our maximum is 4096. See tp_chksum.c comments.
+ */
+ tpcb->tp_cong_win =
+ tpcb->tp_l_tpdusize = 1 << tpcb->tp_tpdusize;
+
+ tpcb->tp_seqmask = TP_NML_FMT_MASK;
+ tpcb->tp_seqbit = TP_NML_FMT_BIT;
+ tpcb->tp_seqhalf = tpcb->tp_seqbit >> 1;
+
+ /* attach to a network-layer protoswitch */
+ if ( error = tp_set_npcb(tpcb))
+ goto bad4;
+ ASSERT( tpcb->tp_nlproto->nlp_afamily == tpcb->tp_domain);
+
+ /* nothing to do for iso case */
+ if( dom == AF_INET )
+ sotoinpcb(so)->inp_ppcb = (caddr_t) tpcb;
+
+ return 0;
+
+bad4:
+ IFDEBUG(D_CONN)
+ printf("BAD4 in tp_attach, so 0x%x\n", so);
+ ENDDEBUG
+ tp_freeref(tpcb->tp_lref);
+
+bad3:
+ IFDEBUG(D_CONN)
+ printf("BAD3 in tp_attach, so 0x%x\n", so);
+ ENDDEBUG
+
+ free((caddr_t)tpcb, M_PCB); /* never a cluster */
+
+bad2:
+ IFDEBUG(D_CONN)
+ printf("BAD2 in tp_attach, so 0x%x\n", so);
+ ENDDEBUG
+ so->so_pcb = 0;
+
+/*bad:*/
+ IFDEBUG(D_CONN)
+ printf("BAD in tp_attach, so 0x%x\n", so);
+ ENDDEBUG
+ return error;
+}
+
+/*
+ * NAME: tp_detach()
+ *
+ * CALLED FROM:
+ * tp.trans, on behalf of a user close request
+ * and when the reference timer goes off
+ * (if the disconnect was initiated by the protocol entity
+ * rather than by the user)
+ *
+ * FUNCTION and ARGUMENTS:
+ * remove the tpcb structure from the list of active or
+ * partially active connections, recycle all the mbufs
+ * associated with the pcb, ref structure, sockbufs, etc.
+ * Only free the ref structure if you know that a ref timer
+ * wasn't set for this tpcb.
+ *
+ * RETURNS: Nada
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ * tp_soisdisconnected() was already when this is called
+ */
+void
+tp_detach(tpcb)
+ register struct tp_pcb *tpcb;
+{
+ void tp_freeref(), tp_rsyflush();
+ register struct socket *so = tpcb->tp_sock;
+
+ IFDEBUG(D_CONN)
+ printf("tp_detach(tpcb 0x%x, so 0x%x)\n",
+ tpcb,so);
+ ENDDEBUG
+ IFTRACE(D_CONN)
+ tptraceTPCB(TPPTmisc, "tp_detach tpcb so lsufx",
+ tpcb, so, *(u_short *)(tpcb->tp_lsuffix), 0);
+ ENDTRACE
+
+ IFDEBUG(D_CONN)
+ printf("so_snd at 0x%x so_rcv at 0x%x\n", &so->so_snd, &so->so_rcv);
+ dump_mbuf(so->so_snd.sb_mb, "so_snd at detach ");
+ printf("about to call LL detach, nlproto 0x%x, nl_detach 0x%x\n",
+ tpcb->tp_nlproto, tpcb->tp_nlproto->nlp_pcbdetach);
+ ENDDEBUG
+
+ if (tpcb->tp_Xsnd.sb_mb) {
+ printf("Unsent Xdata on detach; would panic");
+ sbflush(&tpcb->tp_Xsnd);
+ }
+ if (tpcb->tp_ucddata)
+ m_freem(tpcb->tp_ucddata);
+
+ IFDEBUG(D_CONN)
+ printf("reassembly info cnt %d rsyq 0x%x\n",
+ tpcb->tp_rsycnt, tpcb->tp_rsyq);
+ ENDDEBUG
+ if (tpcb->tp_rsyq)
+ tp_rsyflush(tpcb);
+
+ if (tpcb->tp_next) {
+ remque(tpcb);
+ tpcb->tp_next = tpcb->tp_prev = 0;
+ }
+ tpcb->tp_notdetached = 0;
+
+ IFDEBUG(D_CONN)
+ printf("calling (...nlproto->...)(0x%x, so 0x%x)\n",
+ tpcb->tp_npcb, so);
+ printf("so 0x%x so_head 0x%x, qlen %d q0len %d qlimit %d\n",
+ so, so->so_head,
+ so->so_q0len, so->so_qlen, so->so_qlimit);
+ ENDDEBUG
+
+ (tpcb->tp_nlproto->nlp_pcbdetach)(tpcb->tp_npcb);
+ /* does an so->so_pcb = 0; sofree(so) */
+
+ IFDEBUG(D_CONN)
+ printf("after xxx_pcbdetach\n");
+ ENDDEBUG
+
+ if (tpcb->tp_state == TP_LISTENING) {
+ register struct tp_pcb **tt;
+ for (tt = &tp_listeners; *tt; tt = &((*tt)->tp_nextlisten))
+ if (*tt == tpcb)
+ break;
+ if (*tt)
+ *tt = tpcb->tp_nextlisten;
+ else
+ printf("tp_detach from listen: should panic\n");
+ }
+ if (tpcb->tp_refstate == REF_OPENING ) {
+ /* no connection existed here so no reference timer will be called */
+ IFDEBUG(D_CONN)
+ printf("SETTING ref %d to REF_FREE\n", tpcb->tp_lref);
+ ENDDEBUG
+
+ tp_freeref(tpcb->tp_lref);
+ }
+#ifdef TP_PERF_MEAS
+ /*
+ * Get rid of the cluster mbuf allocated for performance measurements, if
+ * there is one. Note that tpcb->tp_perf_on says nothing about whether or
+ * not a cluster mbuf was allocated, so you have to check for a pointer
+ * to one (that is, we need the TP_PERF_MEASs around the following section
+ * of code, not the IFPERFs)
+ */
+ if (tpcb->tp_p_mbuf) {
+ register struct mbuf *m = tpcb->tp_p_mbuf;
+ struct mbuf *n;
+ IFDEBUG(D_PERF_MEAS)
+ printf("freeing tp_p_meas 0x%x ", tpcb->tp_p_meas);
+ ENDDEBUG
+ do {
+ MFREE(m, n);
+ m = n;
+ } while (n);
+ tpcb->tp_p_meas = 0;
+ tpcb->tp_p_mbuf = 0;
+ }
+#endif /* TP_PERF_MEAS */
+
+ IFDEBUG(D_CONN)
+ printf( "end of detach, NOT single, tpcb 0x%x\n", tpcb);
+ ENDDEBUG
+ /* free((caddr_t)tpcb, M_PCB); WHere to put this ? */
+}
+
+struct que {
+ struct tp_pcb *next;
+ struct tp_pcb *prev;
+} tp_bound_pcbs =
+{(struct tp_pcb *)&tp_bound_pcbs, (struct tp_pcb *)&tp_bound_pcbs};
+
+u_short tp_unique;
+
+tp_tselinuse(tlen, tsel, siso, reuseaddr)
+caddr_t tsel;
+register struct sockaddr_iso *siso;
+{
+ struct tp_pcb *b = tp_bound_pcbs.next, *l = tp_listeners;
+ register struct tp_pcb *t;
+
+ for (;;) {
+ if (b != (struct tp_pcb *)&tp_bound_pcbs) {
+ t = b; b = t->tp_next;
+ } else if (l) {
+ t = l; l = t->tp_nextlisten;
+ } else
+ break;
+ if (tlen == t->tp_lsuffixlen && bcmp(tsel, t->tp_lsuffix, tlen) == 0) {
+ if (t->tp_flags & TPF_GENERAL_ADDR) {
+ if (siso == 0 || reuseaddr == 0)
+ return 1;
+ } else if (siso) {
+ if (siso->siso_family == t->tp_domain &&
+ t->tp_nlproto->nlp_cmpnetaddr(t->tp_npcb, siso, TP_LOCAL))
+ return 1;
+ } else if (reuseaddr == 0)
+ return 1;
+ }
+ }
+ return 0;
+
+}
+
+
+tp_pcbbind(tpcb, nam)
+register struct tp_pcb *tpcb;
+register struct mbuf *nam;
+{
+ register struct sockaddr_iso *siso = 0;
+ int tlen = 0, wrapped = 0;
+ caddr_t tsel;
+ u_short tutil;
+
+ if (tpcb->tp_state != TP_CLOSED)
+ return (EINVAL);
+ if (nam) {
+ siso = mtod(nam, struct sockaddr_iso *);
+ switch (siso->siso_family) {
+ default:
+ return (EAFNOSUPPORT);
+#ifdef ISO
+ case AF_ISO:
+ tlen = siso->siso_tlen;
+ tsel = TSEL(siso);
+ if (siso->siso_nlen == 0)
+ siso = 0;
+ break;
+#endif
+#ifdef INET
+ case AF_INET:
+ tsel = (caddr_t)&tutil;
+ if (tutil = ((struct sockaddr_in *)siso)->sin_port) {
+ tlen = 2;
+ }
+ if (((struct sockaddr_in *)siso)->sin_addr.s_addr == 0)
+ siso = 0;
+ }
+#endif
+ }
+ if (tpcb->tp_lsuffixlen == 0) {
+ if (tlen) {
+ if (tp_tselinuse(tlen, tsel, siso,
+ tpcb->tp_sock->so_options & SO_REUSEADDR))
+ return (EINVAL);
+ } else {
+ for (tsel = (caddr_t)&tutil, tlen = 2;;){
+ if (tp_unique++ < ISO_PORT_RESERVED ||
+ tp_unique > ISO_PORT_USERRESERVED) {
+ if (wrapped++)
+ return ESRCH;
+ tp_unique = ISO_PORT_RESERVED;
+ }
+ tutil = htons(tp_unique);
+ if (tp_tselinuse(tlen, tsel, siso, 0) == 0)
+ break;
+ }
+ if (siso) switch (siso->siso_family) {
+#ifdef ISO
+ case AF_ISO:
+ bcopy(tsel, TSEL(siso), tlen);
+ siso->siso_tlen = tlen;
+ break;
+#endif
+#ifdef INET
+ case AF_INET:
+ ((struct sockaddr_in *)siso)->sin_port = tutil;
+#endif
+ }
+ }
+ bcopy(tsel, tpcb->tp_lsuffix, (tpcb->tp_lsuffixlen = tlen));
+ insque(tpcb, &tp_bound_pcbs);
+ } else {
+ if (tlen || siso == 0)
+ return (EINVAL);
+ }
+ if (siso == 0) {
+ tpcb->tp_flags |= TPF_GENERAL_ADDR;
+ return (0);
+ }
+ return tpcb->tp_nlproto->nlp_pcbbind(tpcb->tp_npcb, nam);
+}
diff --git a/sys/netiso/tp_pcb.h b/sys/netiso/tp_pcb.h
new file mode 100644
index 0000000..0353cb4
--- /dev/null
+++ b/sys/netiso/tp_pcb.h
@@ -0,0 +1,356 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_pcb.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_pcb.h,v 5.2 88/11/18 17:09:32 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_pcb.h,v $
+ *
+ *
+ * This file defines the transport protocol control block (tpcb).
+ * and a bunch of #define values that are used in the tpcb.
+ */
+
+#ifndef __TP_PCB__
+#define __TP_PCB__
+
+#include <netiso/tp_param.h>
+#include <netiso/tp_timer.h>
+#include <netiso/tp_user.h>
+#ifndef sblock
+#include <sys/socketvar.h>
+#endif /* sblock */
+
+/* NOTE: the code depends on REF_CLOSED > REF_OPEN > the rest, and
+ * on REF_FREE being zero
+ *
+ * Possible improvement:
+ * think about merging the tp_ref w/ the tpcb and doing a search
+ * through the tpcb list, from tpb. This would slow down lookup
+ * during data transfer
+ * It would be a little nicer also to have something based on the
+ * clock (like top n bits of the reference is part of the clock, to
+ * minimize the likelihood of reuse after a crash)
+ * also, need to keep the timer servicing part to a minimum (although
+ * the cost of this is probably independent of whether the timers are
+ * in the pcb or in an array..
+ * Last, would have to make the number of timers a function of the amount of
+ * mbufs available, plus some for the frozen references.
+ *
+ * Possible improvement:
+ * Might not need the ref_state stuff either...
+ * REF_FREE could correspond to tp_state == CLOSED or nonexistend tpcb,
+ * REF_OPEN to tp_state anywhere from AK_WAIT or CR_SENT to CLOSING
+ * REF_OPENING could correspond to LISTENING, because that's the
+ * way it's used, not because the correspondence is exact.
+ * REF_CLOSED could correspond to REFWAIT
+ */
+#define REF_FROZEN 3 /* has ref timer only */
+#define REF_OPEN 2 /* has timers, possibly active */
+#define REF_OPENING 1 /* in use (has a pcb) but no timers */
+#define REF_FREE 0 /* free to reallocate */
+
+#define TM_NTIMERS 6
+
+struct tp_ref {
+ struct tp_pcb *tpr_pcb; /* back ptr to PCB */
+};
+
+/* PER system stuff (one static structure instead of a bunch of names) */
+struct tp_refinfo {
+ struct tp_ref *tpr_base;
+ int tpr_size;
+ int tpr_maxopen;
+ int tpr_numopen;
+};
+
+struct nl_protosw {
+ int nlp_afamily; /* address family */
+ int (*nlp_putnetaddr)(); /* puts addresses in nl pcb */
+ int (*nlp_getnetaddr)(); /* gets addresses from nl pcb */
+ int (*nlp_cmpnetaddr)(); /* compares address in pcb with sockaddr */
+ int (*nlp_putsufx)(); /* puts transport suffixes in nl pcb */
+ int (*nlp_getsufx)(); /* gets transport suffixes from nl pcb */
+ int (*nlp_recycle_suffix)();/* clears suffix from nl pcb */
+ int (*nlp_mtu)(); /* figures out mtu based on nl used */
+ int (*nlp_pcbbind)(); /* bind to pcb for net level */
+ int (*nlp_pcbconn)(); /* connect for net level */
+ int (*nlp_pcbdisc)(); /* disconnect net level */
+ int (*nlp_pcbdetach)(); /* detach net level pcb */
+ int (*nlp_pcballoc)(); /* allocate a net level pcb */
+ int (*nlp_output)(); /* prepare a packet to give to nl */
+ int (*nlp_dgoutput)(); /* prepare a packet to give to nl */
+ int (*nlp_ctloutput)(); /* hook for network set/get options */
+ caddr_t nlp_pcblist; /* list of xx_pcb's for connections */
+};
+
+
+struct tp_pcb {
+ struct tp_pcb *tp_next;
+ struct tp_pcb *tp_prev;
+ struct tp_pcb *tp_nextlisten; /* chain all listeners */
+ struct socket *tp_sock; /* back ptr */
+ u_short tp_state; /* state of fsm */
+ short tp_retrans; /* # times can still retrans */
+ caddr_t tp_npcb; /* to lower layer pcb */
+ struct nl_protosw *tp_nlproto; /* lower-layer dependent routines */
+ struct rtentry **tp_routep; /* obtain mtu; inside npcb */
+
+
+ RefNum tp_lref; /* local reference */
+ RefNum tp_fref; /* foreign reference */
+
+ u_int tp_seqmask; /* mask for seq space */
+ u_int tp_seqbit; /* bit for seq number wraparound */
+ u_int tp_seqhalf; /* half the seq space */
+
+ struct mbuf *tp_ucddata; /* user connect/disconnect data */
+
+ /* credit & sequencing info for SENDING */
+ u_short tp_fcredit; /* current remote credit in # packets */
+ u_short tp_maxfcredit; /* max remote credit in # packets */
+ u_short tp_dupacks; /* intuit packet loss before rxt timo */
+ u_long tp_cong_win; /* congestion window in bytes.
+ * see profuse comments in TCP code
+ */
+ u_long tp_ssthresh; /* cong_win threshold for slow start
+ * exponential to linear switch
+ */
+ SeqNum tp_snduna; /* seq # of lowest unacked DT */
+ SeqNum tp_sndnew; /* seq # of lowest unsent DT */
+ SeqNum tp_sndnum; /* next seq # to be assigned */
+ SeqNum tp_sndnxt; /* what to do next; poss. rxt */
+ struct mbuf *tp_sndnxt_m; /* packet corres. to sndnxt*/
+ int tp_Nwindow; /* for perf. measurement */
+
+ /* credit & sequencing info for RECEIVING */
+ SeqNum tp_rcvnxt; /* next DT seq # expect to recv */
+ SeqNum tp_sent_lcdt; /* cdt according to last ack sent */
+ SeqNum tp_sent_uwe; /* uwe according to last ack sent */
+ SeqNum tp_sent_rcvnxt; /* rcvnxt according to last ack sent
+ * needed for perf measurements only
+ */
+ u_short tp_lcredit; /* current local credit in # packets */
+ u_short tp_maxlcredit; /* needed for reassembly queue */
+ struct mbuf **tp_rsyq; /* unacked stuff recvd out of order */
+ int tp_rsycnt; /* number of packets "" "" "" "" */
+ u_long tp_rhiwat; /* remember original RCVBUF size */
+
+ /* receiver congestion state stuff ... */
+ u_int tp_win_recv;
+
+ /* receive window as a scaled int (8 bit fraction part) */
+
+ struct cong_sample {
+ ushort cs_size; /* current window size */
+ ushort cs_received; /* PDUs received in this sample */
+ ushort cs_ce_set; /* PDUs received in this sample with CE bit set */
+ } tp_cong_sample;
+
+
+ /* parameters per-connection controllable by user */
+ struct tp_conn_param _tp_param;
+
+#define tp_Nretrans _tp_param.p_Nretrans
+#define tp_dr_ticks _tp_param.p_dr_ticks
+#define tp_cc_ticks _tp_param.p_cc_ticks
+#define tp_dt_ticks _tp_param.p_dt_ticks
+#define tp_xpd_ticks _tp_param.p_x_ticks
+#define tp_cr_ticks _tp_param.p_cr_ticks
+#define tp_keepalive_ticks _tp_param.p_keepalive_ticks
+#define tp_sendack_ticks _tp_param.p_sendack_ticks
+#define tp_refer_ticks _tp_param.p_ref_ticks
+#define tp_inact_ticks _tp_param.p_inact_ticks
+#define tp_xtd_format _tp_param.p_xtd_format
+#define tp_xpd_service _tp_param.p_xpd_service
+#define tp_ack_strat _tp_param.p_ack_strat
+#define tp_rx_strat _tp_param.p_rx_strat
+#define tp_use_checksum _tp_param.p_use_checksum
+#define tp_use_efc _tp_param.p_use_efc
+#define tp_use_nxpd _tp_param.p_use_nxpd
+#define tp_use_rcc _tp_param.p_use_rcc
+#define tp_tpdusize _tp_param.p_tpdusize
+#define tp_class _tp_param.p_class
+#define tp_winsize _tp_param.p_winsize
+#define tp_no_disc_indications _tp_param.p_no_disc_indications
+#define tp_dont_change_params _tp_param.p_dont_change_params
+#define tp_netservice _tp_param.p_netservice
+#define tp_version _tp_param.p_version
+#define tp_ptpdusize _tp_param.p_ptpdusize
+
+ int tp_l_tpdusize;
+ /* whereas tp_tpdusize is log2(the negotiated max size)
+ * l_tpdusize is the size we'll use when sending, in # chars
+ */
+
+ int tp_rtv; /* max round-trip time variance */
+ int tp_rtt; /* smoothed round-trip time */
+ SeqNum tp_rttseq; /* packet being timed */
+ int tp_rttemit; /* when emitted, in ticks */
+ int tp_idle; /* last activity, in ticks */
+ short tp_rxtcur; /* current retransmit value */
+ short tp_rxtshift; /* log(2) of rexmt exp. backoff */
+ u_char tp_cebit_off; /* real DEC bit algorithms not in use */
+ u_char tp_oktonagle; /* Last unsent pckt may be append to */
+ u_char tp_flags; /* values: */
+#define TPF_NLQOS_PDN TPFLAG_NLQOS_PDN
+#define TPF_PEER_ON_SAMENET TPFLAG_PEER_ON_SAMENET
+#define TPF_GENERAL_ADDR TPFLAG_GENERAL_ADDR
+#define TPF_DELACK 0x8
+#define TPF_ACKNOW 0x10
+
+#define PEER_IS_LOCAL(t) (((t)->tp_flags & TPF_PEER_ON_SAME_NET) != 0)
+#define USES_PDN(t) (((t)->tp_flags & TPF_NLQOS_PDN) != 0)
+
+
+ unsigned
+ tp_sendfcc:1, /* shall next ack include FCC parameter? */
+ tp_trace:1, /* is this pcb being traced? (not used yet) */
+ tp_perf_on:1, /* 0/1 -> performance measuring on */
+ tp_reneged:1, /* have we reneged on cdt since last ack? */
+ tp_decbit:3, /* dec bit was set, we're in reneg mode */
+ tp_notdetached:1; /* Call tp_detach before freeing XXXXXXX */
+
+#ifdef TP_PERF_MEAS
+ /* performance stats - see tp_stat.h */
+ struct tp_pmeas *tp_p_meas;
+ struct mbuf *tp_p_mbuf;
+#endif /* TP_PERF_MEAS */
+
+ /* addressing */
+ u_short tp_domain; /* domain (INET, ISO) */
+ /* for compatibility with the *old* way and with INET, be sure that
+ * that lsuffix and fsuffix are aligned to a short addr.
+ * having them follow the u_short *suffixlen should suffice (choke)
+ */
+ u_short tp_fsuffixlen; /* foreign suffix */
+ char tp_fsuffix[MAX_TSAP_SEL_LEN];
+ u_short tp_lsuffixlen; /* local suffix */
+ char tp_lsuffix[MAX_TSAP_SEL_LEN];
+#define SHORT_LSUFXP(tpcb) ((short *)((tpcb)->tp_lsuffix))
+#define SHORT_FSUFXP(tpcb) ((short *)((tpcb)->tp_fsuffix))
+
+ /* Timer stuff */
+ u_char tp_vers; /* protocol version */
+ u_char tp_peer_acktime; /* used for DT retrans time */
+ u_char tp_refstate; /* values REF_FROZEN, etc. above */
+ struct tp_pcb *tp_fasttimeo; /* limit pcbs to examine */
+ u_int tp_timer[TM_NTIMERS]; /* C timers */
+
+ struct sockbuf tp_Xsnd; /* for expedited data */
+/* struct sockbuf tp_Xrcv; /* for expedited data */
+#define tp_Xrcv tp_sock->so_rcv
+ SeqNum tp_Xsndnxt; /* next XPD seq # to send */
+ SeqNum tp_Xuna; /* seq # of unacked XPD */
+ SeqNum tp_Xrcvnxt; /* next XPD seq # expect to recv */
+
+ /* AK subsequencing */
+ u_short tp_s_subseq; /* next subseq to send */
+ u_short tp_r_subseq; /* highest recv subseq */
+
+};
+
+u_int tp_start_win;
+
+#define ROUND(scaled_int) (((scaled_int) >> 8) + (((scaled_int) & 0x80) ? 1:0))
+
+/* to round off a scaled int with an 8 bit fraction part */
+
+#define CONG_INIT_SAMPLE(pcb) \
+ pcb->tp_cong_sample.cs_received = \
+ pcb->tp_cong_sample.cs_ce_set = 0; \
+ pcb->tp_cong_sample.cs_size = max(pcb->tp_lcredit, 1) << 1;
+
+#define CONG_UPDATE_SAMPLE(pcb, ce_bit) \
+ pcb->tp_cong_sample.cs_received++; \
+ if (ce_bit) { \
+ pcb->tp_cong_sample.cs_ce_set++; \
+ } \
+ if (pcb->tp_cong_sample.cs_size <= pcb->tp_cong_sample.cs_received) { \
+ if ((pcb->tp_cong_sample.cs_ce_set << 1) >= \
+ pcb->tp_cong_sample.cs_size ) { \
+ pcb->tp_win_recv -= pcb->tp_win_recv >> 3; /* multiply by .875 */ \
+ pcb->tp_win_recv = max(1 << 8, pcb->tp_win_recv); \
+ } \
+ else { \
+ pcb->tp_win_recv += (1 << 8); /* add one to the scaled int */ \
+ } \
+ pcb->tp_lcredit = ROUND(pcb->tp_win_recv); \
+ CONG_INIT_SAMPLE(pcb); \
+ }
+
+#ifdef KERNEL
+extern struct tp_refinfo tp_refinfo;
+extern struct timeval time;
+extern struct tp_ref *tp_ref;
+extern struct tp_param tp_param;
+extern struct nl_protosw nl_protosw[];
+extern struct tp_pcb *tp_listeners;
+extern struct tp_pcb *tp_ftimeolist;
+#endif
+
+#define sototpcb(so) ((struct tp_pcb *)(so->so_pcb))
+#define sototpref(so) ((sototpcb(so)->tp_ref))
+#define tpcbtoso(tp) ((struct socket *)((tp)->tp_sock))
+#define tpcbtoref(tp) ((struct tp_ref *)((tp)->tp_ref))
+
+#endif /* __TP_PCB__ */
diff --git a/sys/netiso/tp_seq.h b/sys/netiso/tp_seq.h
new file mode 100644
index 0000000..f14e5ae
--- /dev/null
+++ b/sys/netiso/tp_seq.h
@@ -0,0 +1,124 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_seq.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_seq.h,v 5.1 88/10/12 12:20:59 root Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_seq.h,v $
+ *
+ * These macros perform sequence number arithmetic modulo (2**7 or 2**31).
+ * The relevant fields in the tpcb are:
+ * tp_seqmask : the mask of bits that define the sequence space.
+ * tp_seqbit : 1 + tp_seqmask
+ * tp_seqhalf : tp_seqbit / 2 or half the sequence space (rounded up)
+ * Not exactly fast, but at least it's maintainable.
+ */
+
+#ifndef __TP_SEQ__
+#define __TP_SEQ__
+
+#define SEQ(tpcb,x) \
+ ((x) & (tpcb)->tp_seqmask)
+
+#define SEQ_GT(tpcb, seq, operand ) \
+( ((int)((seq)-(operand)) > 0)\
+? ((int)((seq)-(operand)) < (int)(tpcb)->tp_seqhalf)\
+: !(-((int)(seq)-(operand)) < (int)(tpcb)->tp_seqhalf))
+
+#define SEQ_GEQ(tpcb, seq, operand ) \
+( ((int)((seq)-(operand)) >= 0)\
+? ((int)((seq)-(operand)) < (int)(tpcb)->tp_seqhalf)\
+: !((-((int)(seq)-(operand))) < (int)(tpcb)->tp_seqhalf))
+
+#define SEQ_LEQ(tpcb, seq, operand ) \
+( ((int)((seq)-(operand)) <= 0)\
+? ((-(int)((seq)-(operand))) < (int)(tpcb)->tp_seqhalf)\
+: !(((int)(seq)-(operand)) < (int)(tpcb)->tp_seqhalf))
+
+#define SEQ_LT(tpcb, seq, operand ) \
+( ((int)((seq)-(operand)) < 0)\
+? ((-(int)((seq)-(operand))) < (int)(tpcb)->tp_seqhalf)\
+: !(((int)(seq)-(operand)) < (int)(tpcb)->tp_seqhalf))
+
+#define SEQ_MIN(tpcb, a, b) ( SEQ_GT(tpcb, a, b) ? b : a)
+
+#define SEQ_MAX(tpcb, a, b) ( SEQ_GT(tpcb, a, b) ? a : b)
+
+#define SEQ_INC(tpcb, Seq) ((++Seq), ((Seq) &= (tpcb)->tp_seqmask))
+
+#define SEQ_DEC(tpcb, Seq)\
+ ((Seq) = (((Seq)+(unsigned)((int)(tpcb)->tp_seqbit - 1))&(tpcb)->tp_seqmask))
+
+/* (amt) had better be less than the seq bit ! */
+
+#define SEQ_SUB(tpcb, Seq, amt)\
+ (((Seq) + (unsigned)((int)(tpcb)->tp_seqbit - amt)) & (tpcb)->tp_seqmask)
+#define SEQ_ADD(tpcb, Seq, amt) (((Seq) + (unsigned)amt) & (tpcb)->tp_seqmask)
+
+
+#define IN_RWINDOW(tpcb, seq, lwe, uwe)\
+ ( SEQ_GEQ(tpcb, seq, lwe) && SEQ_LT(tpcb, seq, uwe) )
+
+#define IN_SWINDOW(tpcb, seq, lwe, uwe)\
+ ( SEQ_GT(tpcb, seq, lwe) && SEQ_LEQ(tpcb, seq, uwe) )
+
+#endif /* __TP_SEQ__ */
diff --git a/sys/netiso/tp_stat.h b/sys/netiso/tp_stat.h
new file mode 100644
index 0000000..bf6e1a5
--- /dev/null
+++ b/sys/netiso/tp_stat.h
@@ -0,0 +1,283 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_stat.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_stat.h,v 5.4 88/11/18 17:28:38 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_stat.h,v $
+ *
+ * Here are the data structures in which the global
+ * statistics(counters) are gathered.
+ */
+
+#ifndef __TP_STAT__
+#define __TP_STAT__
+
+struct tp_stat {
+ u_long ts_param_ignored;
+ u_long ts_unused3;
+ u_long ts_bad_csum;
+
+ u_long ts_inv_length;
+ u_long ts_inv_pcode;
+ u_long ts_inv_dutype;
+ u_long ts_negotfailed;
+ u_long ts_inv_dref;
+ u_long ts_inv_pval;
+ u_long ts_inv_sufx;
+ u_long ts_inv_aclass;
+
+ u_long ts_xtd_fmt;
+ u_long ts_use_txpd;
+ u_long ts_csum_off;
+ u_long ts_send_drop;
+ u_long ts_recv_drop;
+
+ u_long ts_xpd_intheway;/* xpd mark caused data flow to stop */
+ u_long ts_xpdmark_del; /* xpd markers thrown away */
+ u_long ts_dt_ooo; /* dt tpdus received out of order */
+ u_long ts_dt_niw; /* dt tpdus received & not in window */
+ u_long ts_xpd_niw; /* xpd tpdus received & not in window */
+ u_long ts_xpd_dup;
+ u_long ts_dt_dup; /* dt tpdus received & are duplicates */
+
+ u_long ts_zfcdt; /* # times f credit went down to 0 */
+ u_long ts_lcdt_reduced; /*
+ # times local cdt reduced on an acknowledgement.
+ */
+
+ u_long ts_pkt_rcvd; /* from ip */
+ u_long ts_tpdu_rcvd; /* accepted as a TPDU in tp_input */
+ u_long ts_tpdu_sent;
+ u_long ts_unused2;
+
+ u_long ts_retrans_cr;
+ u_long ts_retrans_cc;
+ u_long ts_retrans_dr;
+ u_long ts_retrans_dt;
+ u_long ts_retrans_xpd;
+ u_long ts_conn_gaveup;
+
+ u_long ts_ER_sent;
+ u_long ts_DT_sent;
+ u_long ts_XPD_sent;
+ u_long ts_AK_sent;
+ u_long ts_XAK_sent;
+ u_long ts_DR_sent;
+ u_long ts_DC_sent;
+ u_long ts_CR_sent;
+ u_long ts_CC_sent;
+
+ u_long ts_ER_rcvd;
+ u_long ts_DT_rcvd;
+ u_long ts_XPD_rcvd;
+ u_long ts_AK_rcvd;
+ u_long ts_XAK_rcvd;
+ u_long ts_DR_rcvd;
+ u_long ts_DC_rcvd;
+ u_long ts_CR_rcvd;
+ u_long ts_CC_rcvd;
+
+ u_long ts_Eticks;
+ u_long ts_Eexpired;
+ u_long ts_Eset;
+ u_long ts_Ecan_act;
+ u_long ts_Cticks;
+ u_long ts_Cexpired;
+ u_long ts_Cset;
+ u_long ts_Ccan_act;
+ u_long ts_Ccan_inact;
+ u_long ts_Fdelack;
+ u_long ts_Fpruned;
+
+ u_long ts_concat_rcvd;
+
+ u_long ts_zdebug; /* zero dref to test timeout on conn estab tp_input.c */
+ u_long ts_ydebug; /* throw away pseudo-random pkts tp_input.c */
+ u_long ts_unused5;
+ u_long ts_unused; /* kludged concat to test separation tp_emit.c */
+ u_long ts_vdebug; /* kludge to test input size checking tp_emit.c */
+ u_long ts_unused4;
+ u_long ts_ldebug; /* faked a renegging of credit */
+
+ u_long ts_mb_small;
+ u_long ts_mb_cluster;
+ u_long ts_mb_len_distr[17];
+
+ u_long ts_eot_input;
+ u_long ts_eot_user;
+ u_long ts_EOT_sent;
+ u_long ts_tp0_conn;
+ u_long ts_tp4_conn;
+ u_long ts_quench;
+ u_long ts_rcvdecbit;
+
+#define NRTT_CATEGORIES 4
+ /* The 4 categories are:
+ * 0 --> tp_flags: ~TPF_PEER_ON_SAMENET | TPF_NL_PDN
+ * 1 --> tp_flags: ~TPF_PEER_ON_SAMENET | ~TPF_NL_PDN
+ * 2 --> tp_flags: TPF_PEER_ON_SAMENET | ~TPF_NL_PDN
+ * 3 --> tp_flags: TPF_PEER_ON_SAMENET | TPF_NL_PDN
+ */
+ int ts_rtt[NRTT_CATEGORIES];
+ int ts_rtv[NRTT_CATEGORIES];
+
+ u_long ts_ackreason[_ACK_NUM_REASONS_];
+ /* ACK_DONT 0 / ACK_STRAT_EACH 0x1 / ACK_STRAT_FULLWIN 0x4
+ * ACK_DUP 0x8 / ACK_EOT 0x10 / ACK_REORDER 0x20
+ * ACK_USRRCV **
+ * ACK_FCC **
+ */
+} tp_stat ;
+#define TP_PM_MAX 0xa /* 10 decimal */
+
+#define IncStat(x) tp_stat./**/x/**/++
+
+#ifdef TP_PERF_MEAS
+
+#define PStat(Tpcb, X) (Tpcb)->tp_p_meas->/**/X/**/
+#define IncPStat(Tpcb, X) if((Tpcb)->tp_perf_on) (Tpcb)->tp_p_meas->/**/X/**/++
+
+/* BEWARE OF MACROS like this ^^^ must be sure it's surrounded by {} if
+ * it's used in an if-else statement.
+ */
+
+
+/* for perf measurement stuff: maximum window size it can handle */
+
+struct tp_pmeas {
+ /* the first few are distributions as a fn of window size
+ * only keep enough space for normal format plus 1 slot for
+ * extended format, in case any windows larger than 15 are used
+ */
+
+ /*
+ * tps_npdusent: for each call to tp_sbsend, we inc the
+ * element representing the number of pdus sent in this call
+ */
+ int tps_win_lim_by_cdt[TP_PM_MAX+1];
+ int tps_win_lim_by_data[TP_PM_MAX+1];
+ /*
+ * tps_sendtime: Each call to tp_sbsend() is timed. For
+ * Each window size, we keep the running average of the time
+ * taken by tp_sbsend() for each window size.
+ */
+ int tps_sendtime[TP_PM_MAX+1];
+ /*
+ * n_TMsendack: # times ack sent because timer went off
+ * n_ack_cuz_eot: # times ack sent due to EOTSDU on incoming packet
+ * n_ack_cuz_dup: # times ack sent for receiving a duplicate pkt.
+ * n_ack_cuz_fullwin: # times ack sent for receiving the full window.
+ * n_ack_cuz_doack: # times ack sent for having just reordered data.
+ */
+ int tps_n_TMsendack;
+ int tps_n_ack_cuz_eot;
+ int tps_n_ack_cuz_fullwin;
+ int tps_n_ack_cuz_reorder;
+ int tps_n_ack_cuz_dup;
+ int tps_n_ack_cuz_strat;
+ /*
+ * when we send an ack: how much less than the "expected" window
+ * did we actually ack. For example: if we last sent a credit
+ * of 10, and we're acking now for whatever reason, and have
+ * only received 6 since our last credit advertisement, we'll
+ * keep the difference, 4, in this variable.
+ */
+ int tps_ack_early[TP_PM_MAX+1];
+ /*
+ * when we ack, for the # pkts we actually acked w/ this ack,
+ * how much cdt are we advertising?
+ * [ size of window acknowledged ] [ cdt we're giving ]
+ */
+ int tps_cdt_acked[TP_PM_MAX+1][TP_PM_MAX+1];
+
+ int tps_AK_sent;
+ int tps_XAK_sent;
+ int tps_DT_sent;
+ int tps_XPD_sent;
+ int tps_AK_rcvd;
+ int tps_XAK_rcvd;
+ int tps_DT_rcvd;
+ int tps_XPD_rcvd;
+
+ int Nb_from_sess;
+ int Nb_to_sess;
+ int Nb_to_ll;
+ int Nb_from_ll;
+};
+
+#define IFPERF(tpcb) if (tpcb->tp_perf_on && tpcb->tp_p_meas) {
+#define ENDPERF }
+
+#else
+
+int PStat_Junk;
+#define PStat(tpcb, x) PStat_Junk
+#define IncPStat(tpcb, x) /* no-op */
+#define tpmeas(a,b,c,d,e,f) 0
+
+#define IFPERF(x) if (0) {
+#define ENDPERF }
+
+#endif /* TP_PERF_MEAS */
+
+#endif /* __TP_STAT__ */
diff --git a/sys/netiso/tp_states.h b/sys/netiso/tp_states.h
new file mode 100644
index 0000000..ac6213a
--- /dev/null
+++ b/sys/netiso/tp_states.h
@@ -0,0 +1,13 @@
+/* $Header$ */
+/* $Source$ */
+#define ST_ERROR 0x0
+#define TP_CLOSED 0x1
+#define TP_CRSENT 0x2
+#define TP_AKWAIT 0x3
+#define TP_OPEN 0x4
+#define TP_CLOSING 0x5
+#define TP_REFWAIT 0x6
+#define TP_LISTENING 0x7
+#define TP_CONFIRMING 0x8
+
+#define tp_NSTATES 0x9
diff --git a/sys/netiso/tp_states.init b/sys/netiso/tp_states.init
new file mode 100644
index 0000000..89e5345
--- /dev/null
+++ b/sys/netiso/tp_states.init
@@ -0,0 +1,75 @@
+/* $Header$ */
+/* $Source$ */
+{0x3,0x0},
+{0x6,0x1},
+{0x6,0x2},
+{0x6,0x0},
+{0x2,0x3},
+{0x2,0x0},
+{0x1,0x0},
+{0x5,0x0},
+{0x4,0x0},
+{0x7,0x0},
+{0x7,0x0},
+{0x1,0x4},
+{0x8,0x5},
+{0x8,0x6},
+{0x4,0x7},
+{0x3,0x8},
+{0x1,0x9},
+{0x2,0xa},
+{0x6,0xb},
+{0x1,0xc},
+{0x6,0xd},
+{0x6,0xe},
+{0x6,0xf},
+{0x6,0x10},
+{0x1,0x11},
+{0x6,0x12},
+{0x5,0x13},
+{0x4,0x14},
+{0x4,0x15},
+{0x2,0x16},
+{0x6,0x17},
+{0x3,0x18},
+{0x4,0x19},
+{0x4,0x1a},
+{0x4,0x1b},
+{0x3,0x1c},
+{0x4,0x1c},
+{0x4,0x1d},
+{0x4,0x1e},
+{0x4,0x1f},
+{0x4,0x20},
+{0x3,0x20},
+{0x6,0x21},
+{0x5,0x22},
+{0x6,0x23},
+{0x5,0x24},
+{0x3,0x25},
+{0x5,0x26},
+{0x5,0x27},
+{0x4,0x28},
+{0x4,0x29},
+{0x5,0x2a},
+{0x6,0x2b},
+{0x1,0x2c},
+{0x4,0x2d},
+{0x4,0x2e},
+{0x4,0x2f},
+{0x4,0x30},
+{0x4,0x31},
+{0x4,0x32},
+{0x4,0x33},
+{0x4,0x34},
+{0x4,0x35},
+{0x4,0x36},
+{0x6,0x37},
+{0x6,0x38},
+{0x7,0x0},
+{0x5,0x0},
+{0x3,0x0},
+{0x2,0x0},
+{0x4,0x0},
+{0x6,0x0},
+{0x1,0x0},
diff --git a/sys/netiso/tp_subr.c b/sys/netiso/tp_subr.c
new file mode 100644
index 0000000..1259ee4
--- /dev/null
+++ b/sys/netiso/tp_subr.c
@@ -0,0 +1,947 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_subr.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_subr.c,v 5.3 88/11/18 17:28:43 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_subr.c,v $
+ *
+ * The main work of data transfer is done here.
+ * These routines are called from tp.trans.
+ * They include the routines that check the validity of acks and Xacks,
+ * (tp_goodack() and tp_goodXack() )
+ * take packets from socket buffers and send them (tp_send()),
+ * drop the data from the socket buffers (tp_sbdrop()),
+ * and put incoming packet data into socket buffers (tp_stash()).
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <netiso/tp_ip.h>
+#include <netiso/iso.h>
+#include <netiso/argo_debug.h>
+#include <netiso/tp_timer.h>
+#include <netiso/tp_param.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/tp_tpdu.h>
+#include <netiso/tp_trace.h>
+#include <netiso/tp_meas.h>
+#include <netiso/tp_seq.h>
+
+int tp_emit(), tp_sbdrop();
+int tprexmtthresh = 3;
+extern int ticks;
+void tp_send();
+
+/*
+ * CALLED FROM:
+ * tp.trans, when an XAK arrives
+ * FUNCTION and ARGUMENTS:
+ * Determines if the sequence number (seq) from the XAK
+ * acks anything new. If so, drop the appropriate tpdu
+ * from the XPD send queue.
+ * RETURN VALUE:
+ * Returns 1 if it did this, 0 if the ack caused no action.
+ */
+int
+tp_goodXack(tpcb, seq)
+ struct tp_pcb *tpcb;
+ SeqNum seq;
+{
+
+ IFTRACE(D_XPD)
+ tptraceTPCB(TPPTgotXack,
+ seq, tpcb->tp_Xuna, tpcb->tp_Xsndnxt, tpcb->tp_sndnew,
+ tpcb->tp_snduna);
+ ENDTRACE
+
+ if ( seq == tpcb->tp_Xuna ) {
+ tpcb->tp_Xuna = tpcb->tp_Xsndnxt;
+
+ /* DROP 1 packet from the Xsnd socket buf - just so happens
+ * that only one packet can be there at any time
+ * so drop the whole thing. If you allow > 1 packet
+ * the socket buffer, then you'll have to keep
+ * track of how many characters went w/ each XPD tpdu, so this
+ * will get messier
+ */
+ IFDEBUG(D_XPD)
+ dump_mbuf(tpcb->tp_Xsnd.sb_mb,
+ "tp_goodXack Xsnd before sbdrop");
+ ENDDEBUG
+
+ IFTRACE(D_XPD)
+ tptraceTPCB(TPPTmisc,
+ "goodXack: dropping cc ",
+ (int)(tpcb->tp_Xsnd.sb_cc),
+ 0,0,0);
+ ENDTRACE
+ sbdroprecord(&tpcb->tp_Xsnd);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * CALLED FROM:
+ * tp_good_ack()
+ * FUNCTION and ARGUMENTS:
+ * updates
+ * smoothed average round trip time (*rtt)
+ * roundtrip time variance (*rtv) - actually deviation, not variance
+ * given the new value (diff)
+ * RETURN VALUE:
+ * void
+ */
+
+void
+tp_rtt_rtv(tpcb)
+register struct tp_pcb *tpcb;
+{
+ int old = tpcb->tp_rtt;
+ int delta, elapsed = ticks - tpcb->tp_rttemit;
+
+ if (tpcb->tp_rtt != 0) {
+ /*
+ * rtt is the smoothed round trip time in machine clock ticks (hz).
+ * It is stored as a fixed point number, unscaled (unlike the tcp
+ * srtt). The rationale here is that it is only significant to the
+ * nearest unit of slowtimo, which is at least 8 machine clock ticks
+ * so there is no need to scale. The smoothing is done according
+ * to the same formula as TCP (rtt = rtt*7/8 + measured_rtt/8).
+ */
+ delta = elapsed - tpcb->tp_rtt;
+ if ((tpcb->tp_rtt += (delta >> TP_RTT_ALPHA)) <= 0)
+ tpcb->tp_rtt = 1;
+ /*
+ * rtv is a smoothed accumulated mean difference, unscaled
+ * for reasons expressed above.
+ * It is smoothed with an alpha of .75, and the round trip timer
+ * will be set to rtt + 4*rtv, also as TCP does.
+ */
+ if (delta < 0)
+ delta = -delta;
+ if ((tpcb->tp_rtv += ((delta - tpcb->tp_rtv) >> TP_RTV_ALPHA)) <= 0)
+ tpcb->tp_rtv = 1;
+ } else {
+ /*
+ * No rtt measurement yet - use the unsmoothed rtt.
+ * Set the variance to half the rtt (so our first
+ * retransmit happens at 3*rtt)
+ */
+ tpcb->tp_rtt = elapsed;
+ tpcb->tp_rtv = elapsed >> 1;
+ }
+ tpcb->tp_rttemit = 0;
+ tpcb->tp_rxtshift = 0;
+ /*
+ * Quoting TCP: "the retransmit should happen at rtt + 4 * rttvar.
+ * Because of the way we do the smoothing, srtt and rttvar
+ * will each average +1/2 tick of bias. When we compute
+ * the retransmit timer, we want 1/2 tick of rounding and
+ * 1 extra tick because of +-1/2 tick uncertainty in the
+ * firing of the timer. The bias will give us exactly the
+ * 1.5 tick we need. But, because the bias is
+ * statistical, we have to test that we don't drop below
+ * the minimum feasible timer (which is 2 ticks)."
+ */
+ TP_RANGESET(tpcb->tp_dt_ticks, TP_REXMTVAL(tpcb),
+ tpcb->tp_peer_acktime, 128 /* XXX */);
+ IFDEBUG(D_RTT)
+ printf("%s tpcb 0x%x, elapsed %d, delta %d, rtt %d, rtv %d, old %d\n",
+ "tp_rtt_rtv:",tpcb,elapsed,delta,tpcb->tp_rtt,tpcb->tp_rtv,old);
+ ENDDEBUG
+ tpcb->tp_rxtcur = tpcb->tp_dt_ticks;
+}
+
+/*
+ * CALLED FROM:
+ * tp.trans when an AK arrives
+ * FUNCTION and ARGUMENTS:
+ * Given (cdt), the credit from the AK tpdu, and
+ * (seq), the sequence number from the AK tpdu,
+ * tp_goodack() determines if the AK acknowledges something in the send
+ * window, and if so, drops the appropriate packets from the retransmission
+ * list, computes the round trip time, and updates the retransmission timer
+ * based on the new smoothed round trip time.
+ * RETURN VALUE:
+ * Returns 1 if
+ * EITHER it actually acked something heretofore unacknowledged
+ * OR no news but the credit should be processed.
+ * If something heretofore unacked was acked with this sequence number,
+ * the appropriate tpdus are dropped from the retransmission control list,
+ * by calling tp_sbdrop().
+ * No need to see the tpdu itself.
+ */
+int
+tp_goodack(tpcb, cdt, seq, subseq)
+ register struct tp_pcb *tpcb;
+ u_int cdt;
+ register SeqNum seq;
+ u_int subseq;
+{
+ int old_fcredit;
+ int bang = 0; /* bang --> ack for something heretofore unacked */
+ u_int bytes_acked;
+
+ IFDEBUG(D_ACKRECV)
+ printf("goodack tpcb 0x%x seq 0x%x cdt %d una 0x%x new 0x%x nxt 0x%x\n",
+ tpcb, seq, cdt, tpcb->tp_snduna, tpcb->tp_sndnew, tpcb->tp_sndnxt);
+ ENDDEBUG
+ IFTRACE(D_ACKRECV)
+ tptraceTPCB(TPPTgotack,
+ seq,cdt, tpcb->tp_snduna,tpcb->tp_sndnew,subseq);
+ ENDTRACE
+
+ IFPERF(tpcb)
+ tpmeas(tpcb->tp_lref, TPtime_ack_rcvd, (struct timeval *)0, seq, 0, 0);
+ ENDPERF
+
+ if (seq == tpcb->tp_snduna) {
+ if (subseq < tpcb->tp_r_subseq ||
+ (subseq == tpcb->tp_r_subseq && cdt <= tpcb->tp_fcredit)) {
+ discard_the_ack:
+ IFDEBUG(D_ACKRECV)
+ printf("goodack discard : tpcb 0x%x subseq %d r_subseq %d\n",
+ tpcb, subseq, tpcb->tp_r_subseq);
+ ENDDEBUG
+ goto done;
+ }
+ if (cdt == tpcb->tp_fcredit /*&& thus subseq > tpcb->tp_r_subseq */) {
+ tpcb->tp_r_subseq = subseq;
+ if (tpcb->tp_timer[TM_data_retrans] == 0)
+ tpcb->tp_dupacks = 0;
+ else if (++tpcb->tp_dupacks == tprexmtthresh) {
+ /* partner went out of his way to signal with different
+ subsequences that he has the same lack of an expected
+ packet. This may be an early indiciation of a loss */
+
+ SeqNum onxt = tpcb->tp_sndnxt;
+ struct mbuf *onxt_m = tpcb->tp_sndnxt_m;
+ u_int win = min(tpcb->tp_fcredit,
+ tpcb->tp_cong_win / tpcb->tp_l_tpdusize) / 2;
+ IFDEBUG(D_ACKRECV)
+ printf("%s tpcb 0x%x seq 0x%x rttseq 0x%x onxt 0x%x\n",
+ "goodack dupacks:", tpcb, seq, tpcb->tp_rttseq, onxt);
+ ENDDEBUG
+ if (win < 2)
+ win = 2;
+ tpcb->tp_ssthresh = win * tpcb->tp_l_tpdusize;
+ tpcb->tp_timer[TM_data_retrans] = 0;
+ tpcb->tp_rttemit = 0;
+ tpcb->tp_sndnxt = tpcb->tp_snduna;
+ tpcb->tp_sndnxt_m = 0;
+ tpcb->tp_cong_win = tpcb->tp_l_tpdusize;
+ tp_send(tpcb);
+ tpcb->tp_cong_win = tpcb->tp_ssthresh +
+ tpcb->tp_dupacks * tpcb->tp_l_tpdusize;
+ if (SEQ_GT(tpcb, onxt, tpcb->tp_sndnxt)) {
+ tpcb->tp_sndnxt = onxt;
+ tpcb->tp_sndnxt_m = onxt_m;
+ }
+
+ } else if (tpcb->tp_dupacks > tprexmtthresh) {
+ tpcb->tp_cong_win += tpcb->tp_l_tpdusize;
+ }
+ goto done;
+ }
+ } else if (SEQ_LT(tpcb, seq, tpcb->tp_snduna))
+ goto discard_the_ack;
+ /*
+ * If the congestion window was inflated to account
+ * for the other side's cached packets, retract it.
+ */
+ if (tpcb->tp_dupacks > tprexmtthresh &&
+ tpcb->tp_cong_win > tpcb->tp_ssthresh)
+ tpcb->tp_cong_win = tpcb->tp_ssthresh;
+ tpcb->tp_r_subseq = subseq;
+ old_fcredit = tpcb->tp_fcredit;
+ tpcb->tp_fcredit = cdt;
+ if (cdt > tpcb->tp_maxfcredit)
+ tpcb->tp_maxfcredit = cdt;
+ tpcb->tp_dupacks = 0;
+
+ if (IN_SWINDOW(tpcb, seq, tpcb->tp_snduna, tpcb->tp_sndnew)) {
+
+ tpsbcheck(tpcb, 0);
+ bytes_acked = tp_sbdrop(tpcb, seq);
+ tpsbcheck(tpcb, 1);
+ /*
+ * If transmit timer is running and timed sequence
+ * number was acked, update smoothed round trip time.
+ * Since we now have an rtt measurement, cancel the
+ * timer backoff (cf., Phil Karn's retransmit alg.).
+ * Recompute the initial retransmit timer.
+ */
+ if (tpcb->tp_rttemit && SEQ_GT(tpcb, seq, tpcb->tp_rttseq))
+ tp_rtt_rtv(tpcb);
+ /*
+ * If all outstanding data is acked, stop retransmit timer.
+ * If there is more data to be acked, restart retransmit
+ * timer, using current (possibly backed-off) value.
+ * OSI combines the keepalive and persistance functions.
+ * So, there is no persistance timer per se, to restart.
+ */
+ if (tpcb->tp_class != TP_CLASS_0)
+ tpcb->tp_timer[TM_data_retrans] =
+ (seq == tpcb->tp_sndnew) ? 0 : tpcb->tp_rxtcur;
+ /*
+ * When new data is acked, open the congestion window.
+ * If the window gives us less than ssthresh packets
+ * in flight, open exponentially (maxseg per packet).
+ * Otherwise open linearly: maxseg per window
+ * (maxseg^2 / cwnd per packet), plus a constant
+ * fraction of a packet (maxseg/8) to help larger windows
+ * open quickly enough.
+ */
+ {
+ u_int cw = tpcb->tp_cong_win, incr = tpcb->tp_l_tpdusize;
+
+ incr = min(incr, bytes_acked);
+ if (cw > tpcb->tp_ssthresh)
+ incr = incr * incr / cw + incr / 8;
+ tpcb->tp_cong_win =
+ min(cw + incr, tpcb->tp_sock->so_snd.sb_hiwat);
+ }
+ tpcb->tp_snduna = seq;
+ if (SEQ_LT(tpcb, tpcb->tp_sndnxt, seq)) {
+ tpcb->tp_sndnxt = seq;
+ tpcb->tp_sndnxt_m = 0;
+ }
+ bang++;
+ }
+
+ if( cdt != 0 && old_fcredit == 0 ) {
+ tpcb->tp_sendfcc = 1;
+ }
+ if (cdt == 0) {
+ if (old_fcredit != 0)
+ IncStat(ts_zfcdt);
+ /* The following might mean that the window shrunk */
+ if (tpcb->tp_timer[TM_data_retrans]) {
+ tpcb->tp_timer[TM_data_retrans] = 0;
+ tpcb->tp_timer[TM_sendack] = tpcb->tp_dt_ticks;
+ if (tpcb->tp_sndnxt != tpcb->tp_snduna) {
+ tpcb->tp_sndnxt = tpcb->tp_snduna;
+ tpcb->tp_sndnxt_m = 0;
+ }
+ }
+ }
+ tpcb->tp_fcredit = cdt;
+ bang |= (old_fcredit < cdt);
+
+done:
+ IFDEBUG(D_ACKRECV)
+ printf("goodack returns 0x%x, cdt 0x%x ocdt 0x%x cwin 0x%x\n",
+ bang, cdt, old_fcredit, tpcb->tp_cong_win);
+ ENDDEBUG
+ /* if (bang) XXXXX Very bad to remove this test, but somethings broken */
+ tp_send(tpcb);
+ return (bang);
+}
+
+/*
+ * CALLED FROM:
+ * tp_goodack()
+ * FUNCTION and ARGUMENTS:
+ * drops everything up TO but not INCLUDING seq # (seq)
+ * from the retransmission queue.
+ */
+tp_sbdrop(tpcb, seq)
+ register struct tp_pcb *tpcb;
+ SeqNum seq;
+{
+ struct sockbuf *sb = &tpcb->tp_sock->so_snd;
+ register int i = SEQ_SUB(tpcb, seq, tpcb->tp_snduna);
+ int oldcc = sb->sb_cc, oldi = i;
+
+ if (i >= tpcb->tp_seqhalf)
+ printf("tp_spdropping too much -- should panic");
+ while (i-- > 0)
+ sbdroprecord(sb);
+ IFDEBUG(D_ACKRECV)
+ printf("tp_sbdroping %d pkts %d bytes on %x at 0x%x\n",
+ oldi, oldcc - sb->sb_cc, tpcb, seq);
+ ENDDEBUG
+ if (sb->sb_flags & SB_NOTIFY)
+ sowwakeup(tpcb->tp_sock);
+ return (oldcc - sb->sb_cc);
+}
+
+/*
+ * CALLED FROM:
+ * tp.trans on user send request, arrival of AK and arrival of XAK
+ * FUNCTION and ARGUMENTS:
+ * Emits tpdus starting at sequence number (tpcb->tp_sndnxt).
+ * Emits until a) runs out of data, or b) runs into an XPD mark, or
+ * c) it hits seq number (highseq) limited by cong or credit.
+ *
+ * If you want XPD to buffer > 1 du per socket buffer, you can
+ * modifiy this to issue XPD tpdus also, but then it'll have
+ * to take some argument(s) to distinguish between the type of DU to
+ * hand tp_emit.
+ *
+ * When something is sent for the first time, its time-of-send
+ * is stashed (in system clock ticks rather than pf_slowtimo ticks).
+ * When the ack arrives, the smoothed round-trip time is figured
+ * using this value.
+ */
+void
+tp_send(tpcb)
+ register struct tp_pcb *tpcb;
+{
+ register int len;
+ register struct mbuf *m;
+ struct mbuf *mb = 0;
+ struct sockbuf *sb = &tpcb->tp_sock->so_snd;
+ unsigned int eotsdu = 0;
+ SeqNum highseq, checkseq;
+ int idle, idleticks, off, cong_win;
+#ifdef TP_PERF_MEAS
+ int send_start_time = ticks;
+ SeqNum oldnxt = tpcb->tp_sndnxt;
+#endif /* TP_PERF_MEAS */
+
+ idle = (tpcb->tp_snduna == tpcb->tp_sndnew);
+ if (idle) {
+ idleticks = tpcb->tp_inact_ticks - tpcb->tp_timer[TM_inact];
+ if (idleticks > tpcb->tp_dt_ticks)
+ /*
+ * We have been idle for "a while" and no acks are
+ * expected to clock out any data we send --
+ * slow start to get ack "clock" running again.
+ */
+ tpcb->tp_cong_win = tpcb->tp_l_tpdusize;
+ }
+
+ cong_win = tpcb->tp_cong_win;
+ highseq = SEQ(tpcb, tpcb->tp_fcredit + tpcb->tp_snduna);
+ if (tpcb->tp_Xsnd.sb_mb)
+ highseq = SEQ_MIN(tpcb, highseq, tpcb->tp_sndnew);
+
+ IFDEBUG(D_DATA)
+ printf("tp_send enter tpcb 0x%x nxt 0x%x win %d high 0x%x\n",
+ tpcb, tpcb->tp_sndnxt, cong_win, highseq);
+ ENDDEBUG
+ IFTRACE(D_DATA)
+ tptraceTPCB( TPPTmisc, "tp_send sndnew snduna",
+ tpcb->tp_sndnew, tpcb->tp_snduna, 0, 0);
+ tptraceTPCB( TPPTmisc, "tp_send tpcb->tp_sndnxt win fcredit congwin",
+ tpcb->tp_sndnxt, cong_win, tpcb->tp_fcredit, tpcb->tp_cong_win);
+ ENDTRACE
+ IFTRACE(D_DATA)
+ tptraceTPCB( TPPTmisc, "tp_send 2 nxt high fcredit congwin",
+ tpcb->tp_sndnxt, highseq, tpcb->tp_fcredit, cong_win);
+ ENDTRACE
+
+ if (tpcb->tp_sndnxt_m)
+ m = tpcb->tp_sndnxt_m;
+ else {
+ off = SEQ_SUB(tpcb, tpcb->tp_sndnxt, tpcb->tp_snduna);
+ for (m = sb->sb_mb; m && off > 0; m = m->m_next)
+ off--;
+ }
+send:
+ /*
+ * Avoid silly window syndrome here . . . figure out how!
+ */
+ checkseq = tpcb->tp_sndnum;
+ if (idle && SEQ_LT(tpcb, tpcb->tp_sndnum, highseq))
+ checkseq = highseq; /* i.e. DON'T retain highest assigned packet */
+
+ while ((SEQ_LT(tpcb, tpcb->tp_sndnxt, highseq)) && m && cong_win > 0) {
+
+ eotsdu = (m->m_flags & M_EOR) != 0;
+ len = m->m_pkthdr.len;
+ if (tpcb->tp_sndnxt == checkseq && eotsdu == 0 &&
+ len < (tpcb->tp_l_tpdusize / 2))
+ break; /* Nagle . . . . . */
+ cong_win -= len;
+ /* make a copy - mb goes into the retransmission list
+ * while m gets emitted. m_copy won't copy a zero-length mbuf.
+ */
+ mb = m;
+ m = m_copy(mb, 0, M_COPYALL);
+ if (m == MNULL)
+ break;
+ IFTRACE(D_STASH)
+ tptraceTPCB( TPPTmisc,
+ "tp_send mcopy nxt high eotsdu len",
+ tpcb->tp_sndnxt, highseq, eotsdu, len);
+ ENDTRACE
+
+ IFDEBUG(D_DATA)
+ printf("tp_sending tpcb 0x%x nxt 0x%x\n",
+ tpcb, tpcb->tp_sndnxt);
+ ENDDEBUG
+ /* when headers are precomputed, may need to fill
+ in checksum here */
+ if (tpcb->tp_sock->so_error =
+ tp_emit(DT_TPDU_type, tpcb, tpcb->tp_sndnxt, eotsdu, m)) {
+ /* error */
+ break;
+ }
+ m = mb->m_nextpkt;
+ tpcb->tp_sndnxt_m = m;
+ if (tpcb->tp_sndnxt == tpcb->tp_sndnew) {
+ SEQ_INC(tpcb, tpcb->tp_sndnew);
+ /*
+ * Time this transmission if not a retransmission and
+ * not currently timing anything.
+ */
+ if (tpcb->tp_rttemit == 0) {
+ tpcb->tp_rttemit = ticks;
+ tpcb->tp_rttseq = tpcb->tp_sndnxt;
+ }
+ tpcb->tp_sndnxt = tpcb->tp_sndnew;
+ } else
+ SEQ_INC(tpcb, tpcb->tp_sndnxt);
+ /*
+ * Set retransmit timer if not currently set.
+ * Initial value for retransmit timer is smoothed
+ * round-trip time + 2 * round-trip time variance.
+ * Initialize shift counter which is used for backoff
+ * of retransmit time.
+ */
+ if (tpcb->tp_timer[TM_data_retrans] == 0 &&
+ tpcb->tp_class != TP_CLASS_0) {
+ tpcb->tp_timer[TM_data_retrans] = tpcb->tp_dt_ticks;
+ tpcb->tp_timer[TM_sendack] = tpcb->tp_keepalive_ticks;
+ tpcb->tp_rxtshift = 0;
+ }
+ }
+ if (SEQ_GT(tpcb, tpcb->tp_sndnew, tpcb->tp_sndnum))
+ tpcb->tp_oktonagle = 0;
+#ifdef TP_PERF_MEAS
+ IFPERF(tpcb)
+ {
+ register int npkts;
+ int elapsed = ticks - send_start_time, *t;
+ struct timeval now;
+
+ npkts = SEQ_SUB(tpcb, tpcb->tp_sndnxt, oldnxt);
+
+ if (npkts > 0)
+ tpcb->tp_Nwindow++;
+
+ if (npkts > TP_PM_MAX)
+ npkts = TP_PM_MAX;
+
+ t = &(tpcb->tp_p_meas->tps_sendtime[npkts]);
+ *t += (t - elapsed) >> TP_RTT_ALPHA;
+
+ if (mb == 0) {
+ IncPStat(tpcb, tps_win_lim_by_data[npkts] );
+ } else {
+ IncPStat(tpcb, tps_win_lim_by_cdt[npkts] );
+ /* not true with congestion-window being used */
+ }
+ now.tv_sec = elapsed / hz;
+ now.tv_usec = (elapsed - (hz * now.tv_sec)) * 1000000 / hz;
+ tpmeas( tpcb->tp_lref,
+ TPsbsend, &elapsed, newseq, tpcb->tp_Nwindow, npkts);
+ }
+ ENDPERF
+#endif /* TP_PERF_MEAS */
+
+
+ IFTRACE(D_DATA)
+ tptraceTPCB( TPPTmisc,
+ "tp_send at end: new nxt eotsdu error",
+ tpcb->tp_sndnew, tpcb->tp_sndnxt, eotsdu, tpcb->tp_sock->so_error);
+
+ ENDTRACE
+}
+
+int TPNagleok;
+int TPNagled;
+
+tp_packetize(tpcb, m, eotsdu)
+register struct tp_pcb *tpcb;
+register struct mbuf *m;
+int eotsdu;
+{
+ register struct mbuf *n;
+ register struct sockbuf *sb = &tpcb->tp_sock->so_snd;
+ int maxsize = tpcb->tp_l_tpdusize
+ - tp_headersize(DT_TPDU_type, tpcb)
+ - (tpcb->tp_use_checksum?4:0) ;
+ int totlen = m->m_pkthdr.len;
+ struct mbuf *m_split();
+ /*
+ * Pre-packetize the data in the sockbuf
+ * according to negotiated mtu. Do it here
+ * where we can safely wait for mbufs.
+ *
+ * This presumes knowledge of sockbuf conventions.
+ * TODO: allocate space for header and fill it in (once!).
+ */
+ IFDEBUG(D_DATA)
+ printf("SEND BF: maxsize %d totlen %d eotsdu %d sndnum 0x%x\n",
+ maxsize, totlen, eotsdu, tpcb->tp_sndnum);
+ ENDTRACE
+ if (tpcb->tp_oktonagle) {
+ if ((n = sb->sb_mb) == 0)
+ panic("tp_packetize");
+ while (n->m_act)
+ n = n->m_act;
+ if (n->m_flags & M_EOR)
+ panic("tp_packetize 2");
+ SEQ_INC(tpcb, tpcb->tp_sndnum);
+ if (totlen + n->m_pkthdr.len < maxsize) {
+ /* There is an unsent packet with space, combine data */
+ struct mbuf *old_n = n;
+ tpsbcheck(tpcb,3);
+ n->m_pkthdr.len += totlen;
+ while (n->m_next)
+ n = n->m_next;
+ sbcompress(sb, m, n);
+ tpsbcheck(tpcb,4);
+ n = old_n;
+ TPNagled++;
+ goto out;
+ }
+ }
+ while (m) {
+ n = m;
+ if (totlen > maxsize) {
+ if ((m = m_split(n, maxsize, M_WAIT)) == 0)
+ panic("tp_packetize");
+ } else
+ m = 0;
+ totlen -= maxsize;
+ tpsbcheck(tpcb, 5);
+ sbappendrecord(sb, n);
+ tpsbcheck(tpcb, 6);
+ SEQ_INC(tpcb, tpcb->tp_sndnum);
+ }
+out:
+ if (eotsdu) {
+ n->m_flags |= M_EOR; /* XXX belongs at end */
+ tpcb->tp_oktonagle = 0;
+ } else {
+ SEQ_DEC(tpcb, tpcb->tp_sndnum);
+ tpcb->tp_oktonagle = 1;
+ TPNagleok++;
+ }
+ IFDEBUG(D_DATA)
+ printf("SEND out: oktonagle %d sndnum 0x%x\n",
+ tpcb->tp_oktonagle, tpcb->tp_sndnum);
+ ENDTRACE
+ return 0;
+}
+
+
+/*
+ * NAME: tp_stash()
+ * CALLED FROM:
+ * tp.trans on arrival of a DT tpdu
+ * FUNCTION, ARGUMENTS, and RETURN VALUE:
+ * Returns 1 if
+ * a) something new arrived and it's got eotsdu_reached bit on,
+ * b) this arrival was caused other out-of-sequence things to be
+ * accepted, or
+ * c) this arrival is the highest seq # for which we last gave credit
+ * (sender just sent a whole window)
+ * In other words, returns 1 if tp should send an ack immediately, 0 if
+ * the ack can wait a while.
+ *
+ * Note: this implementation no longer renegs on credit, (except
+ * when debugging option D_RENEG is on, for the purpose of testing
+ * ack subsequencing), so we don't need to check for incoming tpdus
+ * being in a reneged portion of the window.
+ */
+
+tp_stash(tpcb, e)
+ register struct tp_pcb *tpcb;
+ register struct tp_event *e;
+{
+ register int ack_reason= tpcb->tp_ack_strat & ACK_STRAT_EACH;
+ /* 0--> delay acks until full window */
+ /* 1--> ack each tpdu */
+#ifndef lint
+#define E e->ATTR(DT_TPDU)
+#else /* lint */
+#define E e->ev_union.EV_DT_TPDU
+#endif /* lint */
+
+ if ( E.e_eot ) {
+ register struct mbuf *n = E.e_data;
+ n->m_flags |= M_EOR;
+ n->m_act = 0;
+ }
+ IFDEBUG(D_STASH)
+ dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
+ "stash: so_rcv before appending");
+ dump_mbuf(E.e_data,
+ "stash: e_data before appending");
+ ENDDEBUG
+
+ IFPERF(tpcb)
+ PStat(tpcb, Nb_from_ll) += E.e_datalen;
+ tpmeas(tpcb->tp_lref, TPtime_from_ll, &e->e_time,
+ E.e_seq, (u_int)PStat(tpcb, Nb_from_ll), (u_int)E.e_datalen);
+ ENDPERF
+
+ if (E.e_seq == tpcb->tp_rcvnxt) {
+
+ IFDEBUG(D_STASH)
+ printf("stash EQ: seq 0x%x datalen 0x%x eot 0x%x\n",
+ E.e_seq, E.e_datalen, E.e_eot);
+ ENDDEBUG
+
+ IFTRACE(D_STASH)
+ tptraceTPCB(TPPTmisc, "stash EQ: seq len eot",
+ E.e_seq, E.e_datalen, E.e_eot, 0);
+ ENDTRACE
+
+ SET_DELACK(tpcb);
+
+ sbappend(&tpcb->tp_sock->so_rcv, E.e_data);
+
+ SEQ_INC( tpcb, tpcb->tp_rcvnxt );
+ /*
+ * move chains from the reassembly queue to the socket buffer
+ */
+ if (tpcb->tp_rsycnt) {
+ register struct mbuf **mp;
+ struct mbuf **mplim;
+
+ mp = tpcb->tp_rsyq + (tpcb->tp_rcvnxt % tpcb->tp_maxlcredit);
+ mplim = tpcb->tp_rsyq + tpcb->tp_maxlcredit;
+
+ while (tpcb->tp_rsycnt && *mp) {
+ sbappend(&tpcb->tp_sock->so_rcv, *mp);
+ tpcb->tp_rsycnt--;
+ *mp = 0;
+ SEQ_INC(tpcb, tpcb->tp_rcvnxt);
+ ack_reason |= ACK_REORDER;
+ if (++mp == mplim)
+ mp = tpcb->tp_rsyq;
+ }
+ }
+ IFDEBUG(D_STASH)
+ dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb,
+ "stash: so_rcv after appending");
+ ENDDEBUG
+
+ } else {
+ register struct mbuf **mp;
+ SeqNum uwe;
+
+ IFTRACE(D_STASH)
+ tptraceTPCB(TPPTmisc, "stash Reseq: seq rcvnxt lcdt",
+ E.e_seq, tpcb->tp_rcvnxt, tpcb->tp_lcredit, 0);
+ ENDTRACE
+
+ if (tpcb->tp_rsyq == 0)
+ tp_rsyset(tpcb);
+ uwe = SEQ(tpcb, tpcb->tp_rcvnxt + tpcb->tp_maxlcredit);
+ if (tpcb->tp_rsyq == 0 ||
+ !IN_RWINDOW(tpcb, E.e_seq, tpcb->tp_rcvnxt, uwe)) {
+ ack_reason = ACK_DONT;
+ m_freem(E.e_data);
+ } else if (*(mp = tpcb->tp_rsyq + (E.e_seq % tpcb->tp_maxlcredit))) {
+ IFDEBUG(D_STASH)
+ printf("tp_stash - drop & ack\n");
+ ENDDEBUG
+
+ /* retransmission - drop it and force an ack */
+ IncStat(ts_dt_dup);
+ IFPERF(tpcb)
+ IncPStat(tpcb, tps_n_ack_cuz_dup);
+ ENDPERF
+
+ m_freem(E.e_data);
+ ack_reason |= ACK_DUP;
+ } else {
+ *mp = E.e_data;
+ tpcb->tp_rsycnt++;
+ ack_reason = ACK_DONT;
+ }
+ }
+ /* there were some comments of historical interest here. */
+ {
+ LOCAL_CREDIT(tpcb);
+
+ if ( E.e_seq == tpcb->tp_sent_uwe )
+ ack_reason |= ACK_STRAT_FULLWIN;
+
+ IFTRACE(D_STASH)
+ tptraceTPCB(TPPTmisc,
+ "end of stash, eot, ack_reason, sent_uwe ",
+ E.e_eot, ack_reason, tpcb->tp_sent_uwe, 0);
+ ENDTRACE
+
+ if ( ack_reason == ACK_DONT ) {
+ IncStat( ts_ackreason[ACK_DONT] );
+ return 0;
+ } else {
+ IFPERF(tpcb)
+ if(ack_reason & ACK_STRAT_EACH) {
+ IncPStat(tpcb, tps_n_ack_cuz_strat);
+ } else if(ack_reason & ACK_STRAT_FULLWIN) {
+ IncPStat(tpcb, tps_n_ack_cuz_fullwin);
+ } else if(ack_reason & ACK_REORDER) {
+ IncPStat(tpcb, tps_n_ack_cuz_reorder);
+ }
+ tpmeas(tpcb->tp_lref, TPtime_ack_sent, 0,
+ SEQ_ADD(tpcb, E.e_seq, 1), 0, 0);
+ ENDPERF
+ {
+ register int i;
+
+ /* keep track of all reasons that apply */
+ for( i=1; i<_ACK_NUM_REASONS_ ;i++) {
+ if( ack_reason & (1<<i) )
+ IncStat( ts_ackreason[i] );
+ }
+ }
+ return 1;
+ }
+ }
+}
+
+/*
+ * tp_rsyflush - drop all the packets on the reassembly queue.
+ * Do this when closing the socket, or when somebody has changed
+ * the space avaible in the receive socket (XXX).
+ */
+tp_rsyflush(tpcb)
+register struct tp_pcb *tpcb;
+{
+ register struct mbuf *m, **mp;
+ if (tpcb->tp_rsycnt) {
+ for (mp == tpcb->tp_rsyq + tpcb->tp_maxlcredit;
+ --mp >= tpcb->tp_rsyq; )
+ if (*mp) {
+ tpcb->tp_rsycnt--;
+ m_freem(*mp);
+ }
+ if (tpcb->tp_rsycnt) {
+ printf("tp_rsyflush %x\n", tpcb);
+ tpcb->tp_rsycnt = 0;
+ }
+ }
+ free((caddr_t)tpcb->tp_rsyq, M_PCB);
+ tpcb->tp_rsyq = 0;
+}
+
+tp_rsyset(tpcb)
+register struct tp_pcb *tpcb;
+{
+ register struct socket *so = tpcb->tp_sock;
+ int maxcredit = tpcb->tp_xtd_format ? 0xffff : 0xf;
+ int old_credit = tpcb->tp_maxlcredit;
+ caddr_t rsyq;
+
+ tpcb->tp_maxlcredit = maxcredit = min(maxcredit,
+ (so->so_rcv.sb_hiwat + tpcb->tp_l_tpdusize)/ tpcb->tp_l_tpdusize);
+
+ if (old_credit == tpcb->tp_maxlcredit && tpcb->tp_rsyq != 0)
+ return;
+ maxcredit *= sizeof(struct mbuf *);
+ if (tpcb->tp_rsyq)
+ tp_rsyflush(tpcb);
+ if (rsyq = (caddr_t)malloc(maxcredit, M_PCB, M_NOWAIT))
+ bzero(rsyq, maxcredit);
+ tpcb->tp_rsyq = (struct mbuf **)rsyq;
+}
+
+tpsbcheck(tpcb, i)
+struct tp_pcb *tpcb;
+{
+ register struct mbuf *n, *m;
+ register int len = 0, mbcnt = 0, pktlen;
+ struct sockbuf *sb = &tpcb->tp_sock->so_snd;
+
+ for (n = sb->sb_mb; n; n = n->m_nextpkt) {
+ if ((n->m_flags & M_PKTHDR) == 0)
+ panic("tpsbcheck nohdr");
+ pktlen = len + n->m_pkthdr.len;
+ for (m = n; m; m = m->m_next) {
+ len += m->m_len;
+ mbcnt += MSIZE;
+ if (m->m_flags & M_EXT)
+ mbcnt += m->m_ext.ext_size;
+ }
+ if (len != pktlen) {
+ printf("test %d; len %d != pktlen %d on mbuf 0x%x\n",
+ i, len, pktlen, n);
+ panic("tpsbcheck short");
+ }
+ }
+ if (len != sb->sb_cc || mbcnt != sb->sb_mbcnt) {
+ printf("test %d: cc %d != %d || mbcnt %d != %d\n", i, len, sb->sb_cc,
+ mbcnt, sb->sb_mbcnt);
+ panic("tpsbcheck");
+ }
+}
diff --git a/sys/netiso/tp_subr2.c b/sys/netiso/tp_subr2.c
new file mode 100644
index 0000000..60c7ce2
--- /dev/null
+++ b/sys/netiso/tp_subr2.c
@@ -0,0 +1,880 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_subr2.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_subr2.c,v 5.5 88/11/18 17:28:55 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_subr2.c,v $
+ *
+ * Some auxiliary routines:
+ * tp_protocol_error: required by xebec- called when a combo of state,
+ * event, predicate isn't covered for by the transition file.
+ * tp_indicate: gives indications(signals) to the user process
+ * tp_getoptions: initializes variables that are affected by the options
+ * chosen.
+ */
+
+/* this def'n is to cause the expansion of this macro in the
+ * routine tp_local_credit :
+ */
+#define LOCAL_CREDIT_EXPAND
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#undef MNULL
+#include <netiso/argo_debug.h>
+#include <netiso/tp_param.h>
+#include <netiso/tp_ip.h>
+#include <netiso/iso.h>
+#include <netiso/iso_errno.h>
+#include <netiso/iso_pcb.h>
+#include <netiso/tp_timer.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_tpdu.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/tp_seq.h>
+#include <netiso/tp_trace.h>
+#include <netiso/tp_user.h>
+#include <netiso/cons.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#ifdef TRUE
+#undef FALSE
+#undef TRUE
+#endif
+#include <netccitt/x25.h>
+#include <netccitt/pk.h>
+#include <netccitt/pk_var.h>
+
+void tp_rsyset();
+
+/*
+ * NAME: tp_local_credit()
+ *
+ * CALLED FROM:
+ * tp_emit(), tp_usrreq()
+ *
+ * FUNCTION and ARGUMENTS:
+ * Computes the local credit and stashes it in tpcb->tp_lcredit.
+ * It's a macro in the production system rather than a procdure.
+ *
+ * RETURNS:
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ * This doesn't actually get called in a production system -
+ * the macro gets expanded instead in place of calls to this proc.
+ * But for debugging, we call this and that allows us to add
+ * debugging messages easily here.
+ */
+void
+tp_local_credit(tpcb)
+ struct tp_pcb *tpcb;
+{
+ LOCAL_CREDIT(tpcb);
+ IFDEBUG(D_CREDIT)
+ printf("ref 0x%x lcdt 0x%x l_tpdusize 0x%x decbit 0x%x\n",
+ tpcb->tp_lref,
+ tpcb->tp_lcredit,
+ tpcb->tp_l_tpdusize,
+ tpcb->tp_decbit,
+ tpcb->tp_cong_win
+ );
+ ENDDEBUG
+ IFTRACE(D_CREDIT)
+ tptraceTPCB(TPPTmisc,
+ "lcdt tpdusz \n",
+ tpcb->tp_lcredit, tpcb->tp_l_tpdusize, 0, 0);
+ ENDTRACE
+}
+
+/*
+ * NAME: tp_protocol_error()
+ *
+ * CALLED FROM:
+ * tp_driver(), when it doesn't know what to do with
+ * a combo of event, state, predicate
+ *
+ * FUNCTION and ARGUMENTS:
+ * print error mesg
+ *
+ * RETURN VALUE:
+ * EIO - always
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+int
+tp_protocol_error(e,tpcb)
+ struct tp_event *e;
+ struct tp_pcb *tpcb;
+{
+ printf("TP PROTOCOL ERROR! tpcb 0x%x event 0x%x, state 0x%x\n",
+ tpcb, e->ev_number, tpcb->tp_state);
+ IFTRACE(D_DRIVER)
+ tptraceTPCB(TPPTmisc, "PROTOCOL ERROR tpcb event state",
+ tpcb, e->ev_number, tpcb->tp_state, 0 );
+ ENDTRACE
+ return EIO; /* for lack of anything better */
+}
+
+
+/* Not used at the moment */
+ProtoHook
+tp_drain()
+{
+ return 0;
+}
+
+
+/*
+ * NAME: tp_indicate()
+ *
+ * CALLED FROM:
+ * tp.trans when XPD arrive, when a connection is being disconnected by
+ * the arrival of a DR or ER, and when a connection times out.
+ *
+ * FUNCTION and ARGUMENTS:
+ * (ind) is the type of indication : T_DISCONNECT, T_XPD
+ * (error) is an E* value that will be put in the socket structure
+ * to be passed along to the user later.
+ * Gives a SIGURG to the user process or group indicated by the socket
+ * attached to the tpcb.
+ *
+ * RETURNS: Rien
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+void
+tp_indicate(ind, tpcb, error)
+ int ind;
+ u_short error;
+ register struct tp_pcb *tpcb;
+{
+ register struct socket *so = tpcb->tp_sock;
+ IFTRACE(D_INDICATION)
+ tptraceTPCB(TPPTindicate, ind, *(u_short *)(tpcb->tp_lsuffix),
+ *(u_short *)(tpcb->tp_fsuffix), error,so->so_pgid);
+ ENDTRACE
+ IFDEBUG(D_INDICATION)
+ char *ls, *fs;
+ ls = tpcb->tp_lsuffix,
+ fs = tpcb->tp_fsuffix,
+
+ printf(
+"indicate 0x%x lsuf 0x%02x%02x fsuf 0x%02x%02x err 0x%x noind 0x%x ref 0x%x\n",
+ ind,
+ *ls, *(ls+1), *fs, *(fs+1),
+ error, /*so->so_pgrp,*/
+ tpcb->tp_no_disc_indications,
+ tpcb->tp_lref);
+ ENDDEBUG
+
+ if (ind == ER_TPDU) {
+ register struct mbuf *m;
+ struct tp_disc_reason x;
+
+ if ((so->so_state & SS_CANTRCVMORE) == 0 &&
+ (m = m_get(M_DONTWAIT, MT_OOBDATA)) != 0) {
+
+ x.dr_hdr.cmsg_len = m->m_len = sizeof(x);
+ x.dr_hdr.cmsg_level = SOL_TRANSPORT;
+ x.dr_hdr.cmsg_type= TPOPT_DISC_REASON;
+ x.dr_reason = error;
+ *mtod(m, struct tp_disc_reason *) = x;
+ sbappendrecord(&tpcb->tp_Xrcv, m);
+ error = 0;
+ } else
+ error = ECONNRESET;
+ }
+ so->so_error = error;
+
+ if (ind == T_DISCONNECT) {
+ if (error == 0)
+ so->so_error = ENOTCONN;
+ if ( tpcb->tp_no_disc_indications )
+ return;
+ }
+ IFTRACE(D_INDICATION)
+ tptraceTPCB(TPPTmisc, "doing sohasoutofband(so)", so,0,0,0);
+ ENDTRACE
+ sohasoutofband(so);
+}
+
+/*
+ * NAME : tp_getoptions()
+ *
+ * CALLED FROM:
+ * tp.trans whenever we go into OPEN state
+ *
+ * FUNCTION and ARGUMENTS:
+ * sets the proper flags and values in the tpcb, to control
+ * the appropriate actions for the given class, options,
+ * sequence space, etc, etc.
+ *
+ * RETURNS: Nada
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+void
+tp_getoptions(tpcb)
+struct tp_pcb *tpcb;
+{
+ tpcb->tp_seqmask =
+ tpcb->tp_xtd_format ? TP_XTD_FMT_MASK : TP_NML_FMT_MASK ;
+ tpcb->tp_seqbit =
+ tpcb->tp_xtd_format ? TP_XTD_FMT_BIT : TP_NML_FMT_BIT ;
+ tpcb->tp_seqhalf = tpcb->tp_seqbit >> 1;
+ tpcb->tp_dt_ticks =
+ max(tpcb->tp_dt_ticks, (tpcb->tp_peer_acktime + 2));
+ tp_rsyset(tpcb);
+
+}
+
+/*
+ * NAME: tp_recycle_tsuffix()
+ *
+ * CALLED FROM:
+ * Called when a ref is frozen.
+ *
+ * FUNCTION and ARGUMENTS:
+ * allows the suffix to be reused.
+ *
+ * RETURNS: zilch
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+void
+tp_recycle_tsuffix(tpcb)
+ struct tp_pcb *tpcb;
+{
+ bzero((caddr_t)tpcb->tp_lsuffix, sizeof( tpcb->tp_lsuffix));
+ bzero((caddr_t)tpcb->tp_fsuffix, sizeof( tpcb->tp_fsuffix));
+ tpcb->tp_fsuffixlen = tpcb->tp_lsuffixlen = 0;
+
+ (tpcb->tp_nlproto->nlp_recycle_suffix)(tpcb->tp_npcb);
+}
+
+/*
+ * NAME: tp_quench()
+ *
+ * CALLED FROM:
+ * tp{af}_quench() when ICMP source quench or similar thing arrives.
+ *
+ * FUNCTION and ARGUMENTS:
+ * Drop the congestion window back to 1.
+ * Congestion window scheme:
+ * Initial value is 1. ("slow start" as Nagle, et. al. call it)
+ * For each good ack that arrives, the congestion window is increased
+ * by 1 (up to max size of logical infinity, which is to say,
+ * it doesn't wrap around).
+ * Source quench causes it to drop back to 1.
+ * tp_send() uses the smaller of (regular window, congestion window).
+ * One retransmission strategy option is to have any retransmission
+ * cause reset the congestion window back to 1.
+ *
+ * (cmd) is either PRC_QUENCH: source quench, or
+ * PRC_QUENCH2: dest. quench (dec bit)
+ *
+ * RETURNS:
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+void
+tp_quench( tpcb, cmd )
+ struct tp_pcb *tpcb;
+ int cmd;
+{
+ IFDEBUG(D_QUENCH)
+ printf("tp_quench tpcb 0x%x ref 0x%x sufx 0x%x\n",
+ tpcb, tpcb->tp_lref, *(u_short *)(tpcb->tp_lsuffix));
+ printf("cong_win 0x%x decbit 0x%x \n",
+ tpcb->tp_cong_win, tpcb->tp_decbit);
+ ENDDEBUG
+ switch(cmd) {
+ case PRC_QUENCH:
+ tpcb->tp_cong_win = tpcb->tp_l_tpdusize;
+ IncStat(ts_quench);
+ break;
+ case PRC_QUENCH2:
+ tpcb->tp_cong_win = tpcb->tp_l_tpdusize; /* might as well quench source also */
+ tpcb->tp_decbit = TP_DECBIT_CLEAR_COUNT;
+ IncStat(ts_rcvdecbit);
+ break;
+ }
+}
+
+
+/*
+ * NAME: tp_netcmd()
+ *
+ * CALLED FROM:
+ *
+ * FUNCTION and ARGUMENTS:
+ *
+ * RETURNS:
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+tp_netcmd( tpcb, cmd )
+ struct tp_pcb *tpcb;
+ int cmd;
+{
+#ifdef TPCONS
+ struct isopcb *isop;
+ struct pklcd *lcp;
+
+ if (tpcb->tp_netservice != ISO_CONS)
+ return;
+ isop = (struct isopcb *)tpcb->tp_npcb;
+ lcp = (struct pklcd *)isop->isop_chan;
+ switch (cmd) {
+
+ case CONN_CLOSE:
+ case CONN_REFUSE:
+ if (isop->isop_refcnt == 1) {
+ /* This is really superfluous, since it would happen
+ anyway in iso_pcbdetach, although it is a courtesy
+ to free up the x.25 channel before the refwait timer
+ expires. */
+ lcp->lcd_upper = 0;
+ lcp->lcd_upnext = 0;
+ pk_disconnect(lcp);
+ isop->isop_chan = 0;
+ isop->isop_refcnt = 0;
+ }
+ break;
+
+ default:
+ printf("tp_netcmd(0x%x, 0x%x) NOT IMPLEMENTED\n", tpcb, cmd);
+ break;
+ }
+#else /* TPCONS */
+ printf("tp_netcmd(): X25 NOT CONFIGURED!!\n");
+#endif
+}
+/*
+ * CALLED FROM:
+ * tp_ctloutput() and tp_emit()
+ * FUNCTION and ARGUMENTS:
+ * Convert a class mask to the highest numeric value it represents.
+ */
+
+int
+tp_mask_to_num(x)
+ u_char x;
+{
+ register int j;
+
+ for(j = 4; j>=0 ;j--) {
+ if(x & (1<<j))
+ break;
+ }
+ ASSERT( (j == 4) || (j == 0) ); /* for now */
+ if( (j != 4) && (j != 0) ) {
+ printf("ASSERTION ERROR: tp_mask_to_num: x 0x%x j %d\n",
+ x, j);
+ }
+ IFTRACE(D_TPINPUT)
+ tptrace(TPPTmisc, "tp_mask_to_num(x) returns j", x, j, 0, 0);
+ ENDTRACE
+ IFDEBUG(D_TPINPUT)
+ printf("tp_mask_to_num(0x%x) returns 0x%x\n", x, j);
+ ENDDEBUG
+ return j;
+}
+
+static
+copyQOSparms(src, dst)
+ struct tp_conn_param *src, *dst;
+{
+ /* copy all but the bits stuff at the end */
+#define COPYSIZE (12 * sizeof(short))
+
+ bcopy((caddr_t)src, (caddr_t)dst, COPYSIZE);
+ dst->p_tpdusize = src->p_tpdusize;
+ dst->p_ack_strat = src->p_ack_strat;
+ dst->p_rx_strat = src->p_rx_strat;
+#undef COPYSIZE
+}
+/*
+ * Determine a reasonable value for maxseg size.
+ * If the route is known, check route for mtu.
+ * We also initialize the congestion/slow start
+ * window to be a single segment if the destination isn't local.
+ * While looking at the routing entry, we also initialize other path-dependent
+ * parameters from pre-set or cached values in the routing entry.
+ */
+void
+tp_mss(tpcb, nhdr_size)
+ register struct tp_pcb *tpcb;
+ int nhdr_size;
+{
+ register struct rtentry *rt;
+ struct ifnet *ifp;
+ register int rtt, mss;
+ u_long bufsize;
+ int i, ssthresh = 0, rt_mss;
+ struct socket *so;
+
+ if (tpcb->tp_ptpdusize)
+ mss = tpcb->tp_ptpdusize << 7;
+ else
+ mss = 1 << tpcb->tp_tpdusize;
+ so = tpcb->tp_sock;
+ if ((rt = *(tpcb->tp_routep)) == 0) {
+ bufsize = so->so_rcv.sb_hiwat;
+ goto punt_route;
+ }
+ ifp = rt->rt_ifp;
+
+#ifdef RTV_MTU /* if route characteristics exist ... */
+ /*
+ * While we're here, check if there's an initial rtt
+ * or rttvar. Convert from the route-table units
+ * to hz ticks for the smoothed timers and slow-timeout units
+ * for other inital variables.
+ */
+ if (tpcb->tp_rtt == 0 && (rtt = rt->rt_rmx.rmx_rtt)) {
+ tpcb->tp_rtt = rtt * hz / RTM_RTTUNIT;
+ if (rt->rt_rmx.rmx_rttvar)
+ tpcb->tp_rtv = rt->rt_rmx.rmx_rttvar
+ * hz / RTM_RTTUNIT;
+ else
+ tpcb->tp_rtv = tpcb->tp_rtt;
+ }
+ /*
+ * if there's an mtu associated with the route, use it
+ */
+ if (rt->rt_rmx.rmx_mtu)
+ rt_mss = rt->rt_rmx.rmx_mtu - nhdr_size;
+ else
+#endif /* RTV_MTU */
+ rt_mss = (ifp->if_mtu - nhdr_size);
+ if (tpcb->tp_ptpdusize == 0 || /* assume application doesn't care */
+ mss > rt_mss /* network won't support what was asked for */)
+ mss = rt_mss;
+ /* can propose mtu which are multiples of 128 */
+ mss &= ~0x7f;
+ /*
+ * If there's a pipesize, change the socket buffer
+ * to that size.
+ */
+#ifdef RTV_SPIPE
+ if ((bufsize = rt->rt_rmx.rmx_sendpipe) > 0) {
+#endif
+ bufsize = min(bufsize, so->so_snd.sb_hiwat);
+ (void) sbreserve(&so->so_snd, bufsize);
+ }
+#ifdef RTV_SPIPE
+ if ((bufsize = rt->rt_rmx.rmx_recvpipe) > 0) {
+#endif
+ bufsize = min(bufsize, so->so_rcv.sb_hiwat);
+ (void) sbreserve(&so->so_rcv, bufsize);
+ } else
+ bufsize = so->so_rcv.sb_hiwat;
+#ifdef RTV_SSTHRESH
+ /*
+ * There's some sort of gateway or interface
+ * buffer limit on the path. Use this to set
+ * the slow start threshhold, but set the
+ * threshold to no less than 2*mss.
+ */
+ ssthresh = rt->rt_rmx.rmx_ssthresh;
+punt_route:
+ /*
+ * The current mss is initialized to the default value.
+ * If we compute a smaller value, reduce the current mss.
+ * If we compute a larger value, return it for use in sending
+ * a max seg size option.
+ * If we received an offer, don't exceed it.
+ * However, do not accept offers under 128 bytes.
+ */
+ if (tpcb->tp_l_tpdusize)
+ mss = min(mss, tpcb->tp_l_tpdusize);
+ /*
+ * We want a minimum recv window of 4 packets to
+ * signal packet loss by duplicate acks.
+ */
+ mss = min(mss, bufsize >> 2) & ~0x7f;
+ mss = max(mss, 128); /* sanity */
+ tpcb->tp_cong_win =
+ (rt == 0 || (rt->rt_flags & RTF_GATEWAY)) ? mss : bufsize;
+ tpcb->tp_l_tpdusize = mss;
+ tp_rsyset(tpcb);
+ tpcb->tp_ssthresh = max(2 * mss, ssthresh);
+ /* Calculate log2 of mss */
+ for (i = TP_MIN_TPDUSIZE + 1; i <= TP_MAX_TPDUSIZE; i++)
+ if ((1 << i) > mss)
+ break;
+ i--;
+ tpcb->tp_tpdusize = i;
+#endif /* RTV_MTU */
+}
+
+/*
+ * CALLED FROM:
+ * tp_usrreq on PRU_CONNECT and tp_input on receipt of CR
+ *
+ * FUNCTION and ARGUMENTS:
+ * -- An mbuf containing the peer's network address.
+ * -- Our control block, which will be modified
+ * -- In the case of cons, a control block for that layer.
+ *
+ *
+ * RETURNS:
+ * errno value :
+ * EAFNOSUPPORT if can't find an nl_protosw for x.25 (really could panic)
+ * ECONNREFUSED if trying to run TP0 with non-type 37 address
+ * possibly other E* returned from cons_netcmd()
+ *
+ * SIDE EFFECTS:
+ * Determines recommended tpdusize, buffering and intial delays
+ * based on information cached on the route.
+ */
+int
+tp_route_to( m, tpcb, channel)
+ struct mbuf *m;
+ register struct tp_pcb *tpcb;
+ caddr_t channel;
+{
+ register struct sockaddr_iso *siso; /* NOTE: this may be a sockaddr_in */
+ extern struct tp_conn_param tp_conn_param[];
+ int error = 0, save_netservice = tpcb->tp_netservice;
+ register struct rtentry *rt = 0;
+ int nhdr_size, mtu, bufsize;
+
+ siso = mtod(m, struct sockaddr_iso *);
+ IFTRACE(D_CONN)
+ tptraceTPCB(TPPTmisc,
+ "route_to: so afi netservice class",
+ tpcb->tp_sock, siso->siso_addr.isoa_genaddr[0], tpcb->tp_netservice,
+ tpcb->tp_class);
+ ENDTRACE
+ IFDEBUG(D_CONN)
+ printf("tp_route_to( m x%x, channel 0x%x, tpcb 0x%x netserv 0x%x)\n",
+ m, channel, tpcb, tpcb->tp_netservice);
+ printf("m->mlen x%x, m->m_data:\n", m->m_len);
+ dump_buf(mtod(m, caddr_t), m->m_len);
+ ENDDEBUG
+ if (channel) {
+#ifdef TPCONS
+ struct pklcd *lcp = (struct pklcd *)channel;
+ struct isopcb *isop = (struct isopcb *)lcp->lcd_upnext,
+ *isop_new = (struct isopcb *)tpcb->tp_npcb;
+ /* The next 2 lines believe that you haven't
+ set any network level options or done a pcbconnect
+ and XXXXXXX'edly apply to both inpcb's and isopcb's */
+ remque(isop_new);
+ free(isop_new, M_PCB);
+ tpcb->tp_npcb = (caddr_t)isop;
+ tpcb->tp_netservice = ISO_CONS;
+ tpcb->tp_nlproto = nl_protosw + ISO_CONS;
+ if (isop->isop_refcnt++ == 0) {
+ iso_putsufx(isop, tpcb->tp_lsuffix, tpcb->tp_lsuffixlen, TP_LOCAL);
+ isop->isop_socket = tpcb->tp_sock;
+ } else
+ /* there are already connections sharing this */;
+#endif
+ } else {
+ switch (siso->siso_family) {
+ default:
+ error = EAFNOSUPPORT;
+ goto done;
+#ifdef ISO
+ case AF_ISO:
+ {
+ struct isopcb *isop = (struct isopcb *)tpcb->tp_npcb;
+ int flags = tpcb->tp_sock->so_options & SO_DONTROUTE;
+ tpcb->tp_netservice = ISO_CLNS;
+ if (clnp_route(&siso->siso_addr, &isop->isop_route,
+ flags, (void **)0, (void **)0) == 0) {
+ rt = isop->isop_route.ro_rt;
+ if (rt && rt->rt_flags & RTF_PROTO1)
+ tpcb->tp_netservice = ISO_CONS;
+ }
+ } break;
+#endif
+#ifdef INET
+ case AF_INET:
+ tpcb->tp_netservice = IN_CLNS;
+#endif
+ }
+ if (tpcb->tp_nlproto->nlp_afamily != siso->siso_family) {
+ IFDEBUG(D_CONN)
+ printf("tp_route_to( CHANGING nlproto old 0x%x new 0x%x)\n",
+ save_netservice, tpcb->tp_netservice);
+ ENDDEBUG
+ if (error = tp_set_npcb(tpcb))
+ goto done;
+ }
+ IFDEBUG(D_CONN)
+ printf("tp_route_to calling nlp_pcbconn, netserv %d\n",
+ tpcb->tp_netservice);
+ ENDDEBUG
+ tpcb->tp_nlproto = nl_protosw + tpcb->tp_netservice;
+ error = (tpcb->tp_nlproto->nlp_pcbconn)(tpcb->tp_npcb, m);
+ }
+ if (error)
+ goto done;
+ nhdr_size = tpcb->tp_nlproto->nlp_mtu(tpcb); /* only gets common info */
+ tp_mss(tpcb, nhdr_size);
+done:
+ IFDEBUG(D_CONN)
+ printf("tp_route_to returns 0x%x\n", error);
+ ENDDEBUG
+ IFTRACE(D_CONN)
+ tptraceTPCB(TPPTmisc, "route_to: returns: error netserv class", error,
+ tpcb->tp_netservice, tpcb->tp_class, 0);
+ ENDTRACE
+ return error;
+}
+
+
+/* class zero version */
+void
+tp0_stash( tpcb, e )
+ register struct tp_pcb *tpcb;
+ register struct tp_event *e;
+{
+#ifndef lint
+#define E e->ATTR(DT_TPDU)
+#else /* lint */
+#define E e->ev_union.EV_DT_TPDU
+#endif /* lint */
+
+ register struct sockbuf *sb = &tpcb->tp_sock->so_rcv;
+ register struct isopcb *isop = (struct isopcb *)tpcb->tp_npcb;
+
+ IFPERF(tpcb)
+ PStat(tpcb, Nb_from_ll) += E.e_datalen;
+ tpmeas(tpcb->tp_lref, TPtime_from_ll, &e->e_time,
+ E.e_seq, PStat(tpcb, Nb_from_ll), E.e_datalen);
+ ENDPERF
+
+ IFDEBUG(D_STASH)
+ printf("stash EQ: seq 0x%x datalen 0x%x eot 0x%x",
+ E.e_seq, E.e_datalen, E.e_eot);
+ ENDDEBUG
+
+ IFTRACE(D_STASH)
+ tptraceTPCB(TPPTmisc, "stash EQ: seq len eot",
+ E.e_seq, E.e_datalen, E.e_eot, 0);
+ ENDTRACE
+
+ if ( E.e_eot ) {
+ register struct mbuf *n = E.e_data;
+ n->m_flags |= M_EOR;
+ n->m_act = MNULL; /* set on tp_input */
+ }
+ sbappend(sb, E.e_data);
+ IFDEBUG(D_STASH)
+ dump_mbuf(sb->sb_mb, "stash 0: so_rcv after appending");
+ ENDDEBUG
+ if (tpcb->tp_netservice != ISO_CONS)
+ printf("tp0_stash: tp running over something wierd\n");
+ else {
+ register struct pklcd *lcp = (struct pklcd *)isop->isop_chan;
+ pk_flowcontrol(lcp, sbspace(sb) <= 0, 1);
+ }
+}
+
+void
+tp0_openflow(tpcb)
+register struct tp_pcb *tpcb;
+{
+ register struct isopcb *isop = (struct isopcb *)tpcb->tp_npcb;
+ if (tpcb->tp_netservice != ISO_CONS)
+ printf("tp0_openflow: tp running over something wierd\n");
+ else {
+ register struct pklcd *lcp = (struct pklcd *)isop->isop_chan;
+ if (lcp->lcd_rxrnr_condition)
+ pk_flowcontrol(lcp, 0, 0);
+ }
+}
+#ifndef TPCONS
+static
+pk_flowcontrol() {}
+#endif
+
+#ifdef TP_PERF_MEAS
+/*
+ * CALLED FROM:
+ * tp_ctloutput() when the user sets TPOPT_PERF_MEAS on
+ * and tp_newsocket() when a new connection is made from
+ * a listening socket with tp_perf_on == true.
+ * FUNCTION and ARGUMENTS:
+ * (tpcb) is the usual; this procedure gets a clear cluster mbuf for
+ * a tp_pmeas structure, and makes tpcb->tp_p_meas point to it.
+ * RETURN VALUE:
+ * ENOBUFS if it cannot get a cluster mbuf.
+ */
+
+int
+tp_setup_perf(tpcb)
+ register struct tp_pcb *tpcb;
+{
+ register struct mbuf *q;
+
+ if( tpcb->tp_p_meas == 0 ) {
+ MGET(q, M_WAITOK, MT_PCB);
+ if (q == 0)
+ return ENOBUFS;
+ MCLGET(q, M_WAITOK);
+ if ((q->m_flags & M_EXT) == 0) {
+ (void) m_free(q);
+ return ENOBUFS;
+ }
+ q->m_len = sizeof (struct tp_pmeas);
+ tpcb->tp_p_mbuf = q;
+ tpcb->tp_p_meas = mtod(q, struct tp_pmeas *);
+ bzero( (caddr_t)tpcb->tp_p_meas, sizeof (struct tp_pmeas) );
+ IFDEBUG(D_PERF_MEAS)
+ printf(
+ "tpcb 0x%x so 0x%x ref 0x%x tp_p_meas 0x%x tp_perf_on 0x%x\n",
+ tpcb, tpcb->tp_sock, tpcb->tp_lref,
+ tpcb->tp_p_meas, tpcb->tp_perf_on);
+ ENDDEBUG
+ tpcb->tp_perf_on = 1;
+ }
+ return 0;
+}
+#endif /* TP_PERF_MEAS */
+
+#ifdef ARGO_DEBUG
+dump_addr (addr)
+ register struct sockaddr *addr;
+{
+ switch( addr->sa_family ) {
+ case AF_INET:
+ dump_inaddr((struct sockaddr_in *)addr);
+ break;
+#ifdef ISO
+ case AF_ISO:
+ dump_isoaddr((struct sockaddr_iso *)addr);
+ break;
+#endif /* ISO */
+ default:
+ printf("BAD AF: 0x%x\n", addr->sa_family);
+ break;
+ }
+}
+
+#define MAX_COLUMNS 8
+/*
+ * Dump the buffer to the screen in a readable format. Format is:
+ *
+ * hex/dec where hex is the hex format, dec is the decimal format.
+ * columns of hex/dec numbers will be printed, followed by the
+ * character representations (if printable).
+ */
+Dump_buf(buf, len)
+caddr_t buf;
+int len;
+{
+ int i,j;
+#define Buf ((u_char *)buf)
+ printf("Dump buf 0x%x len 0x%x\n", buf, len);
+ for (i = 0; i < len; i += MAX_COLUMNS) {
+ printf("+%d:\t", i);
+ for (j = 0; j < MAX_COLUMNS; j++) {
+ if (i + j < len) {
+ printf("%x/%d\t", Buf[i+j], Buf[i+j]);
+ } else {
+ printf(" ");
+ }
+ }
+
+ for (j = 0; j < MAX_COLUMNS; j++) {
+ if (i + j < len) {
+ if (((Buf[i+j]) > 31) && ((Buf[i+j]) < 128))
+ printf("%c", Buf[i+j]);
+ else
+ printf(".");
+ }
+ }
+ printf("\n");
+ }
+}
+#endif /* ARGO_DEBUG */
diff --git a/sys/netiso/tp_timer.c b/sys/netiso/tp_timer.c
new file mode 100644
index 0000000..b3a0be3
--- /dev/null
+++ b/sys/netiso/tp_timer.c
@@ -0,0 +1,377 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_timer.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_timer.c,v 5.2 88/11/18 17:29:07 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_timer.c,v $
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/malloc.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/kernel.h>
+
+#include <netiso/argo_debug.h>
+#include <netiso/tp_param.h>
+#include <netiso/tp_timer.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/tp_tpdu.h>
+#include <netiso/tp_trace.h>
+#include <netiso/tp_seq.h>
+
+struct tp_ref *tp_ref;
+int tp_rttdiv, tp_rttadd, N_TPREF = 127;
+struct tp_refinfo tp_refinfo;
+struct tp_pcb *tp_ftimeolist = (struct tp_pcb *)&tp_ftimeolist;
+
+/*
+ * CALLED FROM:
+ * at autoconfig time from tp_init()
+ * a combo of event, state, predicate
+ * FUNCTION and ARGUMENTS:
+ * initialize data structures for the timers
+ */
+void
+tp_timerinit()
+{
+ register int s;
+ /*
+ * Initialize storage
+ */
+ if (tp_refinfo.tpr_base)
+ return;
+ tp_refinfo.tpr_size = N_TPREF + 1; /* Need to start somewhere */
+ s = sizeof(*tp_ref) * tp_refinfo.tpr_size;
+ if ((tp_ref = (struct tp_ref *) malloc(s, M_PCB, M_NOWAIT)) == 0)
+ panic("tp_timerinit");
+ bzero((caddr_t)tp_ref, (unsigned) s);
+ tp_refinfo.tpr_base = tp_ref;
+ tp_rttdiv = hz / PR_SLOWHZ;
+ tp_rttadd = (2 * tp_rttdiv) - 1;
+}
+#ifdef TP_DEBUG_TIMERS
+/********************** e timers *************************/
+
+/*
+ * CALLED FROM:
+ * tp.trans all over
+ * FUNCTION and ARGUMENTS:
+ * Set an E type timer.
+ */
+void
+tp_etimeout(tpcb, fun, ticks)
+ register struct tp_pcb *tpcb;
+ int fun; /* function to be called */
+ int ticks;
+{
+
+ register u_int *callp;
+ IFDEBUG(D_TIMER)
+ printf("etimeout pcb 0x%x state 0x%x\n", tpcb, tpcb->tp_state);
+ ENDDEBUG
+ IFTRACE(D_TIMER)
+ tptrace(TPPTmisc, "tp_etimeout ref refstate tks Etick", tpcb->tp_lref,
+ tpcb->tp_state, ticks, tp_stat.ts_Eticks);
+ ENDTRACE
+ if (tpcb == 0)
+ return;
+ IncStat(ts_Eset);
+ if (ticks == 0)
+ ticks = 1;
+ callp = tpcb->tp_timer + fun;
+ if (*callp == 0 || *callp > ticks)
+ *callp = ticks;
+}
+
+/*
+ * CALLED FROM:
+ * tp.trans all over
+ * FUNCTION and ARGUMENTS:
+ * Cancel all occurrences of E-timer function (fun) for reference (refp)
+ */
+void
+tp_euntimeout(tpcb, fun)
+ register struct tp_pcb *tpcb;
+ int fun;
+{
+ IFTRACE(D_TIMER)
+ tptrace(TPPTmisc, "tp_euntimeout ref", tpcb->tp_lref, 0, 0, 0);
+ ENDTRACE
+
+ if (tpcb)
+ tpcb->tp_timer[fun] = 0;
+}
+
+/**************** c timers **********************
+ *
+ * These are not chained together; they sit
+ * in the tp_ref structure. they are the kind that
+ * are typically cancelled so it's faster not to
+ * mess with the chains
+ */
+#endif
+/*
+ * CALLED FROM:
+ * the clock, every 500 ms
+ * FUNCTION and ARGUMENTS:
+ * Look for open references with active timers.
+ * If they exist, call the appropriate timer routines to update
+ * the timers and possibly generate events.
+ */
+ProtoHook
+tp_slowtimo()
+{
+ register u_int *cp;
+ register struct tp_ref *rp;
+ struct tp_pcb *tpcb;
+ struct tp_event E;
+ int s = splnet(), t;
+
+ /* check only open reference structures */
+ IncStat(ts_Cticks);
+ /* tp_ref[0] is never used */
+ for (rp = tp_ref + tp_refinfo.tpr_maxopen; rp > tp_ref; rp--) {
+ if ((tpcb = rp->tpr_pcb) == 0 || tpcb->tp_refstate < REF_OPEN)
+ continue;
+ /* check the timers */
+ for (t = 0; t < TM_NTIMERS; t++) {
+ cp = tpcb->tp_timer + t;
+ if (*cp && --(*cp) <= 0 ) {
+ *cp = 0;
+ E.ev_number = t;
+ IFDEBUG(D_TIMER)
+ printf("tp_slowtimo: pcb 0x%x t %d\n",
+ tpcb, t);
+ ENDDEBUG
+ IncStat(ts_Cexpired);
+ tp_driver(tpcb, &E);
+ if (t == TM_reference && tpcb->tp_state == TP_CLOSED) {
+ if (tpcb->tp_notdetached) {
+ IFDEBUG(D_CONN)
+ printf("PRU_DETACH: not detached\n");
+ ENDDEBUG
+ tp_detach(tpcb);
+ }
+ /* XXX wart; where else to do it? */
+ free((caddr_t)tpcb, M_PCB);
+ }
+ }
+ }
+ }
+ splx(s);
+ return 0;
+}
+
+/*
+ * Called From: tp.trans from tp_slowtimo() -- retransmission timer went off.
+ */
+tp_data_retrans(tpcb)
+register struct tp_pcb *tpcb;
+{
+ int rexmt, win;
+ tpcb->tp_rttemit = 0; /* cancel current round trip time */
+ tpcb->tp_dupacks = 0;
+ tpcb->tp_sndnxt = tpcb->tp_snduna;
+ if (tpcb->tp_fcredit == 0) {
+ /*
+ * We transmitted new data, started timing it and the window
+ * got shrunk under us. This can only happen if all data
+ * that they wanted us to send got acked, so don't
+ * bother shrinking the congestion windows, et. al.
+ * The retransmission timer should have been reset in goodack()
+ */
+ IFDEBUG(D_ACKRECV)
+ printf("tp_data_retrans: 0 window tpcb 0x%x una 0x%x\n",
+ tpcb, tpcb->tp_snduna);
+ ENDDEBUG
+ tpcb->tp_rxtshift = 0;
+ tpcb->tp_timer[TM_data_retrans] = 0;
+ tpcb->tp_timer[TM_sendack] = tpcb->tp_dt_ticks;
+ return;
+ }
+ rexmt = tpcb->tp_dt_ticks << min(tpcb->tp_rxtshift, TP_MAXRXTSHIFT);
+ win = min(tpcb->tp_fcredit, (tpcb->tp_cong_win / tpcb->tp_l_tpdusize / 2));
+ win = max(win, 2);
+ tpcb->tp_cong_win = tpcb->tp_l_tpdusize; /* slow start again. */
+ tpcb->tp_ssthresh = win * tpcb->tp_l_tpdusize;
+ /* We're losing; our srtt estimate is probably bogus.
+ * Clobber it so we'll take the next rtt measurement as our srtt;
+ * Maintain current rxt times until then.
+ */
+ if (++tpcb->tp_rxtshift > TP_NRETRANS / 4) {
+ /* tpcb->tp_nlprotosw->nlp_losing(tpcb->tp_npcb) someday */
+ tpcb->tp_rtt = 0;
+ }
+ TP_RANGESET(tpcb->tp_rxtcur, rexmt, tpcb->tp_peer_acktime, 128);
+ tpcb->tp_timer[TM_data_retrans] = tpcb->tp_rxtcur;
+ tp_send(tpcb);
+}
+
+int
+tp_fasttimo()
+{
+ register struct tp_pcb *t;
+ int s = splnet();
+ struct tp_event E;
+
+ E.ev_number = TM_sendack;
+ while ((t = tp_ftimeolist) != (struct tp_pcb *)&tp_ftimeolist) {
+ if (t == 0) {
+ printf("tp_fasttimeo: should panic");
+ tp_ftimeolist = (struct tp_pcb *)&tp_ftimeolist;
+ } else {
+ if (t->tp_flags & TPF_DELACK) {
+ IncStat(ts_Fdelack);
+ tp_driver(t, &E);
+ t->tp_flags &= ~TPF_DELACK;
+ } else
+ IncStat(ts_Fpruned);
+ tp_ftimeolist = t->tp_fasttimeo;
+ t->tp_fasttimeo = 0;
+ }
+ }
+ splx(s);
+}
+
+#ifdef TP_DEBUG_TIMERS
+/*
+ * CALLED FROM:
+ * tp.trans, tp_emit()
+ * FUNCTION and ARGUMENTS:
+ * Set a C type timer of type (which) to go off after (ticks) time.
+ */
+void
+tp_ctimeout(tpcb, which, ticks)
+ register struct tp_pcb *tpcb;
+ int which, ticks;
+{
+
+ IFTRACE(D_TIMER)
+ tptrace(TPPTmisc, "tp_ctimeout ref which tpcb active",
+ tpcb->tp_lref, which, tpcb, tpcb->tp_timer[which]);
+ ENDTRACE
+ if(tpcb->tp_timer[which])
+ IncStat(ts_Ccan_act);
+ IncStat(ts_Cset);
+ if (ticks <= 0)
+ ticks = 1;
+ tpcb->tp_timer[which] = ticks;
+}
+
+/*
+ * CALLED FROM:
+ * tp.trans
+ * FUNCTION and ARGUMENTS:
+ * Version of tp_ctimeout that resets the C-type time if the
+ * parameter (ticks) is > the current value of the timer.
+ */
+void
+tp_ctimeout_MIN(tpcb, which, ticks)
+ register struct tp_pcb *tpcb;
+ int which, ticks;
+{
+ IFTRACE(D_TIMER)
+ tptrace(TPPTmisc, "tp_ctimeout_MIN ref which tpcb active",
+ tpcb->tp_lref, which, tpcb, tpcb->tp_timer[which]);
+ ENDTRACE
+ IncStat(ts_Cset);
+ if (tpcb->tp_timer[which]) {
+ tpcb->tp_timer[which] = min(ticks, tpcb->tp_timer[which]);
+ IncStat(ts_Ccan_act);
+ } else
+ tpcb->tp_timer[which] = ticks;
+}
+
+/*
+ * CALLED FROM:
+ * tp.trans
+ * FUNCTION and ARGUMENTS:
+ * Cancel the (which) timer in the ref structure indicated by (refp).
+ */
+void
+tp_cuntimeout(tpcb, which)
+ register struct tp_pcb *tpcb;
+ int which;
+{
+ IFDEBUG(D_TIMER)
+ printf("tp_cuntimeout(0x%x, %d) active %d\n",
+ tpcb, which, tpcb->tp_timer[which]);
+ ENDDEBUG
+
+ IFTRACE(D_TIMER)
+ tptrace(TPPTmisc, "tp_cuntimeout ref which, active", refp-tp_ref,
+ which, tpcb->tp_timer[which], 0);
+ ENDTRACE
+
+ if (tpcb->tp_timer[which])
+ IncStat(ts_Ccan_act);
+ else
+ IncStat(ts_Ccan_inact);
+ tpcb->tp_timer[which] = 0;
+}
+#endif
diff --git a/sys/netiso/tp_timer.h b/sys/netiso/tp_timer.h
new file mode 100644
index 0000000..a6f7735
--- /dev/null
+++ b/sys/netiso/tp_timer.h
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_timer.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_timer.h,v 5.1 88/10/12 12:21:41 root Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_timer.h,v $
+ *
+ * ARGO TP
+ * The callout structures used by the tp timers.
+ */
+
+#ifndef __TP_TIMER__
+#define __TP_TIMER__
+
+#define SET_DELACK(t) {\
+ (t)->tp_flags |= TPF_DELACK; \
+ if ((t)->tp_fasttimeo == 0)\
+ { (t)->tp_fasttimeo = tp_ftimeolist; tp_ftimeolist = (t); } }
+
+#ifdef ARGO_DEBUG
+#define TP_DEBUG_TIMERS
+#endif
+
+#ifndef TP_DEBUG_TIMERS
+#define tp_ctimeout(tpcb, which, timo) ((tpcb)->tp_timer[which] = (timo))
+#define tp_cuntimeout(tpcb, which) ((tpcb)->tp_timer[which] = 0)
+#define tp_etimeout tp_ctimeout
+#define tp_euntimeout tp_cuntimeout
+#define tp_ctimeout_MIN(p, w, t) \
+ { if((p)->tp_timer[w] > (t)) (p)->tp_timer[w] = (t);}
+#endif /* TP_DEBUG_TIMERS */
+
+#endif /* __TP_TIMER__ */
diff --git a/sys/netiso/tp_tpdu.h b/sys/netiso/tp_tpdu.h
new file mode 100644
index 0000000..15f130d
--- /dev/null
+++ b/sys/netiso/tp_tpdu.h
@@ -0,0 +1,296 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_tpdu.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_tpdu.h,v 4.4 88/07/26 16:45:40 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_tpdu.h,v $
+ *
+ * This ghastly set of macros makes it possible to
+ * refer to tpdu structures without going mad.
+ */
+
+#ifndef __TP_TPDU__
+#define __TP_TPDU__
+
+#ifndef BYTE_ORDER
+/*
+ * Definitions for byte order,
+ * according to byte significance from low address to high.
+ */
+#define LITTLE_ENDIAN 1234 /* least-significant byte first (vax) */
+#define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */
+#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp) */
+
+#ifdef vax
+#define BYTE_ORDER LITTLE_ENDIAN
+#else
+#define BYTE_ORDER BIG_ENDIAN /* mc68000, tahoe, most others */
+#endif
+#endif /* BYTE_ORDER */
+
+/* This much of a tpdu is the same for all types of tpdus (except
+ * DT tpdus in class 0; their exceptions are handled by the data
+ * structure below
+ */
+struct tpdu_fixed {
+ u_char _tpduf_li:8, /* length indicator */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ _tpduf_cdt: 4, /* credit */
+ _tpduf_type: 4; /* type of tpdu (DT, CR, etc.) */
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ _tpduf_type: 4, /* type of tpdu (DT, CR, etc.) */
+ _tpduf_cdt: 4; /* credit */
+#endif
+ u_short _tpduf_dref; /* destination ref; not in DT in class 0 */
+};
+
+#define tpdu_li _tpduf._tpduf_li
+#define tpdu_type _tpduf._tpduf_type
+#define tpdu_cdt _tpduf._tpduf_cdt
+#define tpdu_dref _tpduf._tpduf_dref
+
+struct tp0du {
+ u_char _tp0_li,
+ _tp0_cdt_type, /* same as in tpdu_fixed */
+#if BYTE_ORDER == BIG_ENDIAN
+ _tp0_eot: 1, /* eot */
+ _tp0_mbz: 7, /* must be zero */
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN
+ _tp0_mbz: 7, /* must be zero */
+ _tp0_eot: 1, /* eot */
+#endif
+ _tp0_notused: 8; /* data begins on this octet */
+};
+
+#define tp0du_eot _tp0_eot
+#define tp0du_mbz _tp0_mbz
+
+/*
+ * This is used when the extended format seqence numbers are
+ * being sent and received.
+ */
+ /*
+ * the seqeot field is an int that overlays the seq
+ * and eot fields, this allows the htonl operation
+ * to be applied to the entire 32 bit quantity, and
+ * simplifies the structure definitions.
+ */
+union seq_type {
+ struct {
+#if BYTE_ORDER == BIG_ENDIAN
+ unsigned int st_eot:1, /* end-of-tsdu */
+ st_seq:31; /* 31 bit sequence number */
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned int st_seq:31, /* 31 bit sequence number */
+ st_eot:1; /* end-of-tsdu */
+#endif
+ } st;
+ unsigned int s_seqeot;
+#define s_eot st.st_eot
+#define s_seq st.st_seq
+};
+
+/* Then most tpdu types have a portion that is always present but
+ * differs among the tpdu types :
+ */
+union tpdu_fixed_rest {
+
+ struct {
+ u_short _tpdufr_sref, /* source reference */
+#if BYTE_ORDER == BIG_ENDIAN
+ _tpdufr_class: 4, /* class [ ISO 8073 13.3.3.e ] */
+ _tpdufr_opt: 4, /* options [ ISO 8073 13.3.3.e ] */
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN
+ _tpdufr_opt: 4, /* options [ ISO 8073 13.3.3.e ] */
+ _tpdufr_class: 4, /* class [ ISO 8073 13.3.3.e ] */
+#endif
+ _tpdufr_xx: 8; /* unused */
+ } CRCC;
+
+#define tpdu_CRli _tpduf._tpduf_li
+#define tpdu_CRtype _tpduf._tpduf_type
+#define tpdu_CRcdt _tpduf._tpduf_cdt
+#define tpdu_CRdref_0 _tpduf._tpduf_dref
+#define tpdu_CRsref _tpdufr.CRCC._tpdufr_sref
+#define tpdu_sref _tpdufr.CRCC._tpdufr_sref
+#define tpdu_CRclass _tpdufr.CRCC._tpdufr_class
+#define tpdu_CRoptions _tpdufr.CRCC._tpdufr_opt
+
+#define tpdu_CCli _tpduf._tpduf_li
+#define tpdu_CCtype _tpduf._tpduf_type
+#define tpdu_CCcdt _tpduf._tpduf_cdt
+#define tpdu_CCdref _tpduf._tpduf_dref
+#define tpdu_CCsref _tpdufr.CRCC._tpdufr_sref
+#define tpdu_CCclass _tpdufr.CRCC._tpdufr_class
+#define tpdu_CCoptions _tpdufr.CRCC._tpdufr_opt
+
+/* OPTIONS and ADDL OPTIONS bits */
+#define TPO_USE_EFC 0x1
+#define TPO_XTD_FMT 0x2
+#define TPAO_USE_TXPD 0x1
+#define TPAO_NO_CSUM 0x2
+#define TPAO_USE_RCC 0x4
+#define TPAO_USE_NXPD 0x8
+
+ struct {
+ unsigned short _tpdufr_sref; /* source reference */
+ unsigned char _tpdufr_reason; /* [ ISO 8073 13.5.3.d ] */
+ } DR;
+#define tpdu_DRli _tpduf._tpduf_li
+#define tpdu_DRtype _tpduf._tpduf_type
+#define tpdu_DRdref _tpduf._tpduf_dref
+#define tpdu_DRsref _tpdufr.DR._tpdufr_sref
+#define tpdu_DRreason _tpdufr.DR._tpdufr_reason
+
+ unsigned short _tpdufr_sref; /* source reference */
+
+#define tpdu_DCli _tpduf._tpduf_li
+#define tpdu_DCtype _tpduf._tpduf_type
+#define tpdu_DCdref _tpduf._tpduf_dref
+#define tpdu_DCsref _tpdufr._tpdufr_sref
+
+ struct {
+#if BYTE_ORDER == BIG_ENDIAN
+ unsigned char _tpdufr_eot:1, /* end-of-tsdu */
+ _tpdufr_seq:7; /* 7 bit sequence number */
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned char _tpdufr_seq:7, /* 7 bit sequence number */
+ _tpdufr_eot:1; /* end-of-tsdu */
+#endif
+ }SEQEOT;
+ struct {
+#if BYTE_ORDER == BIG_ENDIAN
+ unsigned int _tpdufr_Xeot:1, /* end-of-tsdu */
+ _tpdufr_Xseq:31; /* 31 bit sequence number */
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned int _tpdufr_Xseq:31, /* 31 bit sequence number */
+ _tpdufr_Xeot:1; /* end-of-tsdu */
+#endif
+ }SEQEOT31;
+ unsigned int _tpdufr_Xseqeot;
+#define tpdu_seqeotX _tpdufr._tpdufr_Xseqeot
+
+#define tpdu_DTli _tpduf._tpduf_li
+#define tpdu_DTtype _tpduf._tpduf_type
+#define tpdu_DTdref _tpduf._tpduf_dref
+#define tpdu_DTseq _tpdufr.SEQEOT._tpdufr_seq
+#define tpdu_DTeot _tpdufr.SEQEOT._tpdufr_eot
+#define tpdu_DTseqX _tpdufr.SEQEOT31._tpdufr_Xseq
+#define tpdu_DTeotX _tpdufr.SEQEOT31._tpdufr_Xeot
+
+#define tpdu_XPDli _tpduf._tpduf_li
+#define tpdu_XPDtype _tpduf._tpduf_type
+#define tpdu_XPDdref _tpduf._tpduf_dref
+#define tpdu_XPDseq _tpdufr.SEQEOT._tpdufr_seq
+#define tpdu_XPDeot _tpdufr.SEQEOT._tpdufr_eot
+#define tpdu_XPDseqX _tpdufr.SEQEOT31._tpdufr_Xseq
+#define tpdu_XPDeotX _tpdufr.SEQEOT31._tpdufr_Xeot
+
+ struct {
+#if BYTE_ORDER == BIG_ENDIAN
+ unsigned _tpdufr_yrseq0:1, /* always zero */
+ _tpdufr_yrseq:31; /* [ ISO 8073 13.9.3.d ] */
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned _tpdufr_yrseq:31, /* [ ISO 8073 13.9.3.d ] */
+ _tpdufr_yrseq0:1; /* always zero */
+#endif
+ unsigned short _tpdufr_cdt; /* [ ISO 8073 13.9.3.b ] */
+ } AK31;
+
+#define tpdu_AKli _tpduf._tpduf_li
+#define tpdu_AKtype _tpduf._tpduf_type
+#define tpdu_AKdref _tpduf._tpduf_dref
+#define tpdu_AKseq _tpdufr.SEQEOT._tpdufr_seq
+#define tpdu_AKseqX _tpdufr.AK31._tpdufr_yrseq
+/* location of cdt depends on size of seq. numbers */
+#define tpdu_AKcdt _tpduf._tpduf_cdt
+#define tpdu_AKcdtX _tpdufr.AK31._tpdufr_cdt
+
+#define tpdu_XAKli _tpduf._tpduf_li
+#define tpdu_XAKtype _tpduf._tpduf_type
+#define tpdu_XAKdref _tpduf._tpduf_dref
+#define tpdu_XAKseq _tpdufr.SEQEOT._tpdufr_seq
+#define tpdu_XAKseqX _tpdufr.SEQEOT31._tpdufr_Xseq
+
+ unsigned char _tpdu_ERreason; /* [ ISO 8073 13.12.3.c ] */
+
+#define tpdu_ERli _tpduf._tpduf_li
+#define tpdu_ERtype _tpduf._tpduf_type
+#define tpdu_ERdref _tpduf._tpduf_dref
+#define tpdu_ERreason _tpdufr._tpdu_ERreason
+
+};
+
+struct tpdu {
+ struct tpdu_fixed _tpduf;
+ union tpdu_fixed_rest _tpdufr;
+};
+
+#endif /* __TP_TPDU__ */
diff --git a/sys/netiso/tp_trace.c b/sys/netiso/tp_trace.c
new file mode 100644
index 0000000..115597b
--- /dev/null
+++ b/sys/netiso/tp_trace.c
@@ -0,0 +1,175 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_trace.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_trace.c,v 5.3 88/11/18 17:29:14 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_trace.c,v $
+ *
+ * The whole protocol trace module.
+ * We keep a circular buffer of trace structures, which are big
+ * unions of different structures we might want to see.
+ * Unfortunately this gets too big pretty easily. Pcbs were removed
+ * from the tracing when the kernel got too big to boot.
+ */
+
+#define TP_TRACEFILE
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netiso/tp_param.h>
+#include <netiso/tp_timer.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_param.h>
+#include <netiso/tp_ip.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/tp_tpdu.h>
+#include <netiso/argo_debug.h>
+#include <netiso/tp_trace.h>
+
+#ifdef TPPT
+static tp_seq = 0;
+u_char tp_traceflags[128];
+
+/*
+ * The argument tpcb is the obvious.
+ * event here is just the type of trace event - TPPTmisc, etc.
+ * The rest of the arguments have different uses depending
+ * on the type of trace event.
+ */
+/*ARGSUSED*/
+/*VARARGS*/
+
+void
+tpTrace(tpcb, event, arg, src, len, arg4, arg5)
+ struct tp_pcb *tpcb;
+ u_int event, arg;
+ u_int src;
+ u_int len;
+ u_int arg4;
+ u_int arg5;
+{
+ register struct tp_Trace *tp;
+
+ tp = &tp_Trace[tp_Tracen++];
+ tp_Tracen %= TPTRACEN;
+
+ tp->tpt_event = event;
+ tp->tpt_tseq = tp_seq++;
+ tp->tpt_arg = arg;
+ if(tpcb)
+ tp->tpt_arg2 = tpcb->tp_lref;
+ bcopy( (caddr_t)&time, (caddr_t)&tp->tpt_time, sizeof(struct timeval) );
+
+ switch(event) {
+
+ case TPPTertpdu:
+ bcopy((caddr_t)src, (caddr_t)&tp->tpt_ertpdu,
+ (unsigned)MIN((int)len, sizeof(struct tp_Trace)));
+ break;
+
+ case TPPTusrreq:
+ case TPPTmisc:
+
+ /* arg is a string */
+ bcopy((caddr_t)arg, (caddr_t)tp->tpt_str,
+ (unsigned)MIN(1+strlen((caddr_t) arg), TPTRACE_STRLEN));
+ tp->tpt_m2 = src;
+ tp->tpt_m3 = len;
+ tp->tpt_m4 = arg4;
+ tp->tpt_m1 = arg5;
+ break;
+
+ case TPPTgotXack:
+ case TPPTXack:
+ case TPPTsendack:
+ case TPPTgotack:
+ case TPPTack:
+ case TPPTindicate:
+ default:
+ case TPPTdriver:
+ tp->tpt_m2 = arg;
+ tp->tpt_m3 = src;
+ tp->tpt_m4 = len;
+ tp->tpt_m5 = arg4;
+ tp->tpt_m1 = arg5;
+ break;
+ case TPPTparam:
+ bcopy((caddr_t)src, (caddr_t)&tp->tpt_param, sizeof(struct tp_param));
+ break;
+ case TPPTref:
+ bcopy((caddr_t)src, (caddr_t)&tp->tpt_ref, sizeof(struct tp_ref));
+ break;
+
+ case TPPTtpduin:
+ case TPPTtpduout:
+ tp->tpt_arg2 = arg4;
+ bcopy((caddr_t)src, (caddr_t)&tp->tpt_tpdu,
+ (unsigned)MIN((int)len, sizeof(struct tp_Trace)));
+ break;
+ }
+}
+#endif /* TPPT */
diff --git a/sys/netiso/tp_trace.h b/sys/netiso/tp_trace.h
new file mode 100644
index 0000000..8857305
--- /dev/null
+++ b/sys/netiso/tp_trace.h
@@ -0,0 +1,198 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_trace.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_trace.h,v 5.1 88/10/12 12:21:51 root Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_trace.h,v $
+ *
+ *
+ * Definitions needed for the protocol trace mechanism.
+ */
+
+#ifndef __TP_TRACE__
+#define __TP_TRACE__
+
+
+#define TPPTsendack 1
+#define TPPTgotack 2
+#define TPPTXack 3
+#define TPPTgotXack 4
+#define TPPTack 5
+#define TPPTindicate 6
+#define TPPTusrreq 7
+#define TPPTmisc 8
+#define TPPTpcb 9
+#define TPPTref 10
+#define TPPTtpduin 11
+#define TPPTparam 12
+#define TPPTertpdu 13
+#define TPPTdriver 14
+#define TPPTtpduout 15
+
+#include <netiso/tp_pcb.h>
+
+/* this #if is to avoid lint */
+
+#if defined(TP_TRACEFILE)||!defined(KERNEL)
+
+#include <netiso/tp_tpdu.h>
+
+#define TPTRACE_STRLEN 50
+
+
+/* for packet tracing */
+struct tp_timeval {
+ SeqNum tptv_seq;
+ u_int tptv_kind;
+ u_int tptv_window;
+ u_int tptv_size;
+};
+
+struct tp_Trace {
+ u_int tpt_event;
+ u_int tpt_arg;
+ u_int tpt_arg2;
+ int tpt_tseq;
+ struct timeval tpt_time;
+ union {
+ struct inpcb tpt_Inpcb; /* protocol control block */
+ struct tp_ref tpt_Ref; /* ref part of pcb */
+ struct tpdu tpt_Tpdu; /* header*/
+ struct tp_refinfo tpt_Param; /* ?? bytes, make sure < 128??*/
+ struct tp_timeval tpt_Time;
+ struct {
+ u_int tptm_2;
+ u_int tptm_3;
+ u_int tptm_4;
+ u_int tptm_5;
+ char tpt_Str[TPTRACE_STRLEN];
+ u_int tptm_1;
+ } tptmisc;
+ u_char tpt_Ertpdu; /* use rest of structure */
+ } tpt_stuff;
+};
+#define tpt_inpcb tpt_stuff.tpt_Inpcb
+#define tpt_pcb tpt_stuff.tpt_Pcb
+#define tpt_ref tpt_stuff.tpt_Ref
+#define tpt_tpdu tpt_stuff.tpt_Tpdu
+#define tpt_param tpt_stuff.tpt_Param
+#define tpt_ertpdu tpt_stuff.tpt_Ertpdu
+#define tpt_str tpt_stuff.tptmisc.tpt_Str
+#define tpt_m1 tpt_stuff.tptmisc.tptm_1
+#define tpt_m2 tpt_stuff.tptmisc.tptm_2
+#define tpt_m3 tpt_stuff.tptmisc.tptm_3
+#define tpt_m4 tpt_stuff.tptmisc.tptm_4
+#define tpt_m5 tpt_stuff.tptmisc.tptm_5
+
+#define tpt_seq tpt_stuff.tpt_Time.tptv_seq
+#define tpt_kind tpt_stuff.tpt_Time.tptv_kind
+#define tpt_window tpt_stuff.tpt_Time.tptv_window
+#define tpt_size tpt_stuff.tpt_Time.tptv_size
+
+#endif /* defined(TP_TRACEFILE)||!defined(KERNEL) */
+
+
+#ifdef TPPT
+
+#define TPTRACEN 300
+
+#define tptrace(A,B,C,D,E,F) \
+ tpTrace((struct tp_pcb *)0,\
+ (u_int)(A),(u_int)(B),(u_int)(C),(u_int)(D),(u_int)(E),(u_int)(F))
+
+#define tptraceTPCB(A,B,C,D,E,F) \
+ tpTrace(tpcb,\
+ (u_int)(A),(u_int)(B),(u_int)(C),(u_int)(D),(u_int)(E),(u_int)(F))
+
+extern void tpTrace();
+extern struct tp_Trace tp_Trace[];
+extern u_char tp_traceflags[];
+int tp_Tracen = 0;
+
+#define IFTRACE(ascii)\
+ if(tp_traceflags[ascii]) {
+/*
+ * for some reason lint complains about tp_param being undefined no
+ * matter where or how many times I define it.
+ */
+#define ENDTRACE }
+
+
+#else /* TPPT */
+
+/***********************************************
+ * NO TPPT TRACE STUFF
+ **********************************************/
+#define TPTRACEN 1
+
+#define tptrace(A,B,C,D,E,F) 0
+#define tptraceTPCB(A,B,C,D,E,F) 0
+
+#define IFTRACE(ascii) if (0) {
+#define ENDTRACE }
+
+#endif /* TPPT */
+
+
+
+#endif /* __TP_TRACE__ */
diff --git a/sys/netiso/tp_user.h b/sys/netiso/tp_user.h
new file mode 100644
index 0000000..b81491b
--- /dev/null
+++ b/sys/netiso/tp_user.h
@@ -0,0 +1,162 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_user.h 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_user.h,v 5.2 88/11/04 15:44:44 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_user.h,v $
+ *
+ * These are the values a real-live user ;-) needs.
+ */
+
+#ifndef _TYPES_
+#include <sys/types.h>
+#endif
+
+#ifndef __TP_USER__
+#define __TP_USER__
+
+struct tp_conn_param {
+ /* PER CONNECTION parameters */
+ short p_Nretrans;
+ short p_dr_ticks;
+
+ short p_cc_ticks;
+ short p_dt_ticks;
+
+ short p_x_ticks;
+ short p_cr_ticks;
+
+ short p_keepalive_ticks;
+ short p_sendack_ticks;
+
+ short p_ref_ticks;
+ short p_inact_ticks;
+
+ short p_ptpdusize; /* preferred tpdusize/128 */
+ short p_winsize;
+
+ u_char p_tpdusize; /* log 2 of size */
+
+ u_char p_ack_strat; /* see comments in tp_pcb.h */
+ u_char p_rx_strat; /* see comments in tp_pcb.h */
+ u_char p_class; /* class bitmask */
+ u_char p_xtd_format;
+ u_char p_xpd_service;
+ u_char p_use_checksum;
+ u_char p_use_nxpd; /* netwk expedited data: not implemented */
+ u_char p_use_rcc; /* receipt confirmation: not implemented */
+ u_char p_use_efc; /* explicit flow control: not implemented */
+ u_char p_no_disc_indications; /* don't deliver indic on disc */
+ u_char p_dont_change_params; /* use these params as they are */
+ u_char p_netservice;
+ u_char p_version; /* only here for checking */
+};
+
+/*
+ * These sockopt level definitions should be considered for socket.h
+ */
+#define SOL_TRANSPORT 0xfffe
+#define SOL_NETWORK 0xfffd
+
+/* get/set socket opt commands */
+#define TPACK_WINDOW 0x0 /* ack only on full window */
+#define TPACK_EACH 0x1 /* ack every packet */
+
+#define TPRX_USE_CW 0x8 /* use congestion window transmit */
+#define TPRX_EACH 0x4 /* retrans each packet of a set */
+#define TPRX_FASTSTART 0x1 /* don't use slow start */
+
+#define TPOPT_INTERCEPT 0x200
+#define TPOPT_FLAGS 0x300
+#define TPOPT_CONN_DATA 0x400
+#define TPOPT_DISC_DATA 0x500
+#define TPOPT_CFRM_DATA 0x600
+#define TPOPT_CDDATA_CLEAR 0x700
+#define TPOPT_MY_TSEL 0x800
+#define TPOPT_PEER_TSEL 0x900
+#define TPOPT_PERF_MEAS 0xa00
+#define TPOPT_PSTATISTICS 0xb00
+#define TPOPT_PARAMS 0xc00 /* to replace a bunch of the others */
+#define TPOPT_DISC_REASON 0xe00
+
+struct tp_disc_reason {
+ struct cmsghdr dr_hdr;
+ u_int dr_reason;
+};
+
+/*
+ ***********************flags**********************************
+ */
+
+/* read only flags */
+#define TPFLAG_NLQOS_PDN (u_char)0x01
+#define TPFLAG_PEER_ON_SAMENET (u_char)0x02
+#define TPFLAG_GENERAL_ADDR (u_char)0x04 /* bound to wildcard addr */
+
+
+/*
+ ***********************end flags******************************
+ */
+
+
+#endif /* __TP_USER__ */
diff --git a/sys/netiso/tp_usrreq.c b/sys/netiso/tp_usrreq.c
new file mode 100644
index 0000000..8060c94
--- /dev/null
+++ b/sys/netiso/tp_usrreq.c
@@ -0,0 +1,756 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_usrreq.c 8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/*
+ * ARGO TP
+ *
+ * $Header: tp_usrreq.c,v 5.4 88/11/18 17:29:18 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_usrreq.c,v $
+ *
+ * tp_usrreq(), the fellow that gets called from most of the socket code.
+ * Pretty straighforward.
+ * THe only really awful stuff here is the OOB processing, which is done
+ * wholly here.
+ * tp_rcvoob() and tp_sendoob() are contained here and called by tp_usrreq().
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <netiso/tp_param.h>
+#include <netiso/tp_timer.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_seq.h>
+#include <netiso/tp_ip.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/argo_debug.h>
+#include <netiso/tp_trace.h>
+#include <netiso/tp_meas.h>
+#include <netiso/iso.h>
+#include <netiso/iso_errno.h>
+
+int tp_attach(), tp_driver(), tp_pcbbind();
+int TNew;
+int TPNagle1, TPNagle2;
+struct tp_pcb *tp_listeners, *tp_intercepts;
+
+#ifdef ARGO_DEBUG
+/*
+ * CALLED FROM:
+ * anywhere you want to debug...
+ * FUNCTION and ARGUMENTS:
+ * print (str) followed by the control info in the mbufs of an mbuf chain (n)
+ */
+void
+dump_mbuf(n, str)
+ struct mbuf *n;
+ char *str;
+{
+ struct mbuf *nextrecord;
+
+ printf("dump %s\n", str);
+
+ if (n == MNULL) {
+ printf("EMPTY:\n");
+ return;
+ }
+
+ while (n) {
+ nextrecord = n->m_act;
+ printf("RECORD:\n");
+ while (n) {
+ printf("%x : Len %x Data %x A %x Nx %x Tp %x\n",
+ n, n->m_len, n->m_data, n->m_act, n->m_next, n->m_type);
+#ifdef notdef
+ {
+ register char *p = mtod(n, char *);
+ register int i;
+
+ printf("data: ");
+ for (i = 0; i < n->m_len; i++) {
+ if (i%8 == 0)
+ printf("\n");
+ printf("0x%x ", *(p+i));
+ }
+ printf("\n");
+ }
+#endif /* notdef */
+ if (n->m_next == n) {
+ printf("LOOP!\n");
+ return;
+ }
+ n = n->m_next;
+ }
+ n = nextrecord;
+ }
+ printf("\n");
+}
+
+#endif /* ARGO_DEBUG */
+
+/*
+ * CALLED FROM:
+ * tp_usrreq(), PRU_RCVOOB
+ * FUNCTION and ARGUMENTS:
+ * Copy data from the expedited data socket buffer into
+ * the pre-allocated mbuf m.
+ * There is an isomorphism between XPD TPDUs and expedited data TSDUs.
+ * XPD tpdus are limited to 16 bytes of data so they fit in one mbuf.
+ * RETURN VALUE:
+ * EINVAL if debugging is on and a disaster has occurred
+ * ENOTCONN if the socket isn't connected
+ * EWOULDBLOCK if the socket is in non-blocking mode and there's no
+ * xpd data in the buffer
+ * E* whatever is returned from the fsm.
+ */
+tp_rcvoob(tpcb, so, m, outflags, inflags)
+ struct tp_pcb *tpcb;
+ register struct socket *so;
+ register struct mbuf *m;
+ int *outflags;
+ int inflags;
+{
+ register struct mbuf *n;
+ register struct sockbuf *sb = &so->so_rcv;
+ struct tp_event E;
+ int error = 0;
+ register struct mbuf **nn;
+
+ IFDEBUG(D_XPD)
+ printf("PRU_RCVOOB, sostate 0x%x\n", so->so_state);
+ ENDDEBUG
+
+ /* if you use soreceive */
+ if (m == MNULL)
+ return ENOBUFS;
+
+restart:
+ if ((((so->so_state & SS_ISCONNECTED) == 0)
+ || (so->so_state & SS_ISDISCONNECTING) != 0) &&
+ (so->so_proto->pr_flags & PR_CONNREQUIRED)) {
+ return ENOTCONN;
+ }
+
+ /* Take the first mbuf off the chain.
+ * Each XPD TPDU gives you a complete TSDU so the chains don't get
+ * coalesced, but one TSDU may span several mbufs.
+ * Nevertheless, since n should have a most 16 bytes, it
+ * will fit into m. (size was checked in tp_input() )
+ */
+
+ /*
+ * Code for excision of OOB data should be added to
+ * uipc_socket2.c (like sbappend).
+ */
+
+ sblock(sb, M_WAITOK);
+ for (nn = &sb->sb_mb; n = *nn; nn = &n->m_act)
+ if (n->m_type == MT_OOBDATA)
+ break;
+
+ if (n == 0) {
+ IFDEBUG(D_XPD)
+ printf("RCVOOB: empty queue!\n");
+ ENDDEBUG
+ sbunlock(sb);
+ if (so->so_state & SS_NBIO) {
+ return EWOULDBLOCK;
+ }
+ sbwait(sb);
+ goto restart;
+ }
+ m->m_len = 0;
+
+ /* Assuming at most one xpd tpdu is in the buffer at once */
+ while (n != MNULL) {
+ m->m_len += n->m_len;
+ bcopy(mtod(n, caddr_t), mtod(m, caddr_t), (unsigned)n->m_len);
+ m->m_data += n->m_len; /* so mtod() in bcopy() above gives right addr */
+ n = n->m_next;
+ }
+ m->m_data = m->m_dat;
+ m->m_flags |= M_EOR;
+
+ IFDEBUG(D_XPD)
+ printf("tp_rcvoob: xpdlen 0x%x\n", m->m_len);
+ dump_mbuf(so->so_rcv.sb_mb, "RCVOOB: Rcv socketbuf");
+ dump_mbuf(sb->sb_mb, "RCVOOB: Xrcv socketbuf");
+ ENDDEBUG
+
+ if ((inflags & MSG_PEEK) == 0) {
+ n = *nn;
+ *nn = n->m_act;
+ for (; n; n = m_free(n))
+ sbfree(sb, n);
+ }
+
+release:
+ sbunlock(sb);
+
+ IFTRACE(D_XPD)
+ tptraceTPCB(TPPTmisc, "PRU_RCVOOB @ release sb_cc m_len",
+ tpcb->tp_Xrcv.sb_cc, m->m_len, 0, 0);
+ ENDTRACE
+ if (error == 0)
+ error = DoEvent(T_USR_Xrcvd);
+ return error;
+}
+
+/*
+ * CALLED FROM:
+ * tp_usrreq(), PRU_SENDOOB
+ * FUNCTION and ARGUMENTS:
+ * Send what's in the mbuf chain (m) as an XPD TPDU.
+ * The mbuf may not contain more then 16 bytes of data.
+ * XPD TSDUs aren't segmented, so they translate into
+ * exactly one XPD TPDU, with EOT bit set.
+ * RETURN VALUE:
+ * EWOULDBLOCK if socket is in non-blocking mode and the previous
+ * xpd data haven't been acked yet.
+ * EMSGSIZE if trying to send > max-xpd bytes (16)
+ * ENOBUFS if ran out of mbufs
+ */
+tp_sendoob(tpcb, so, xdata, outflags)
+ struct tp_pcb *tpcb;
+ register struct socket *so;
+ register struct mbuf *xdata;
+ int *outflags; /* not used */
+{
+ /*
+ * Each mbuf chain represents a sequence # in the XPD seq space.
+ * The first one in the queue has sequence # tp_Xuna.
+ * When we add to the XPD queue, we stuff a zero-length
+ * mbuf (mark) into the DATA queue, with its sequence number in m_next
+ * to be assigned to this XPD tpdu, so data xfer can stop
+ * when it reaches the zero-length mbuf if this XPD TPDU hasn't
+ * yet been acknowledged.
+ */
+ register struct sockbuf *sb = &(tpcb->tp_Xsnd);
+ register struct mbuf *xmark;
+ register int len=0;
+ struct tp_event E;
+
+ IFDEBUG(D_XPD)
+ printf("tp_sendoob:");
+ if (xdata)
+ printf("xdata len 0x%x\n", xdata->m_len);
+ ENDDEBUG
+ /* DO NOT LOCK the Xsnd buffer!!!! You can have at MOST one
+ * socket buf locked at any time!!! (otherwise you might
+ * sleep() in sblock() w/ a signal pending and cause the
+ * system call to be aborted w/ a locked socketbuf, which
+ * is a problem. So the so_snd buffer lock
+ * (done in sosend()) serves as the lock for Xpd.
+ */
+ if (sb->sb_mb) { /* Anything already in eXpedited data sockbuf? */
+ if (so->so_state & SS_NBIO) {
+ return EWOULDBLOCK;
+ }
+ while (sb->sb_mb) {
+ sbunlock(&so->so_snd); /* already locked by sosend */
+ sbwait(&so->so_snd);
+ sblock(&so->so_snd, M_WAITOK); /* sosend will unlock on return */
+ }
+ }
+
+ if (xdata == (struct mbuf *)0) {
+ /* empty xpd packet */
+ MGETHDR(xdata, M_WAIT, MT_OOBDATA);
+ if (xdata == NULL) {
+ return ENOBUFS;
+ }
+ xdata->m_len = 0;
+ xdata->m_pkthdr.len = 0;
+ }
+ IFDEBUG(D_XPD)
+ printf("tp_sendoob 1:");
+ if (xdata)
+ printf("xdata len 0x%x\n", xdata->m_len);
+ ENDDEBUG
+ xmark = xdata; /* temporary use of variable xmark */
+ while (xmark) {
+ len += xmark->m_len;
+ xmark = xmark->m_next;
+ }
+ if (len > TP_MAX_XPD_DATA) {
+ return EMSGSIZE;
+ }
+ IFDEBUG(D_XPD)
+ printf("tp_sendoob 2:");
+ if (xdata)
+ printf("xdata len 0x%x\n", len);
+ ENDDEBUG
+
+
+ IFTRACE(D_XPD)
+ tptraceTPCB(TPPTmisc, "XPD mark m_next ", xdata->m_next, 0, 0, 0);
+ ENDTRACE
+
+ sbappendrecord(sb, xdata);
+
+ IFDEBUG(D_XPD)
+ printf("tp_sendoob len 0x%x\n", len);
+ dump_mbuf(so->so_snd.sb_mb, "XPD request Regular sndbuf:");
+ dump_mbuf(tpcb->tp_Xsnd.sb_mb, "XPD request Xsndbuf:");
+ ENDDEBUG
+ return DoEvent(T_XPD_req);
+}
+
+/*
+ * CALLED FROM:
+ * the socket routines
+ * FUNCTION and ARGUMENTS:
+ * Handles all "user requests" except the [gs]ockopts() requests.
+ * The argument (req) is the request type (PRU*),
+ * (m) is an mbuf chain, generally used for send and
+ * receive type requests only.
+ * (nam) is used for addresses usually, in particular for the bind request.
+ *
+ */
+/*ARGSUSED*/
+ProtoHook
+tp_usrreq(so, req, m, nam, controlp)
+ struct socket *so;
+ u_int req;
+ struct mbuf *m, *nam, *controlp;
+{
+ register struct tp_pcb *tpcb = sototpcb(so);
+ int s = splnet();
+ int error = 0;
+ int flags, *outflags = &flags;
+ u_long eotsdu = 0;
+ struct tp_event E;
+
+ IFDEBUG(D_REQUEST)
+ printf("usrreq(0x%x,%d,0x%x,0x%x,0x%x)\n",so,req,m,nam,outflags);
+ if (so->so_error)
+ printf("WARNING!!! so->so_error is 0x%x\n", so->so_error);
+ ENDDEBUG
+ IFTRACE(D_REQUEST)
+ tptraceTPCB(TPPTusrreq, "req so m state [", req, so, m,
+ tpcb?tpcb->tp_state:0);
+ ENDTRACE
+
+ if ((u_int)tpcb == 0 && req != PRU_ATTACH) {
+ IFTRACE(D_REQUEST)
+ tptraceTPCB(TPPTusrreq, "req failed NO TPCB[", 0, 0, 0, 0);
+ ENDTRACE
+ splx(s);
+ return ENOTCONN;
+ }
+
+ switch (req) {
+
+ case PRU_ATTACH:
+ if (tpcb) {
+ error = EISCONN;
+ } else if ((error = tp_attach(so, (int)nam)) == 0)
+ tpcb = sototpcb(so);
+ break;
+
+ case PRU_ABORT: /* called from close() */
+ /* called for each incoming connect queued on the
+ * parent (accepting) socket
+ */
+ if (tpcb->tp_state == TP_OPEN || tpcb->tp_state == TP_CONFIRMING) {
+ E.ATTR(T_DISC_req).e_reason = E_TP_NO_SESSION;
+ error = DoEvent(T_DISC_req); /* pretend it was a close() */
+ break;
+ } /* else DROP THROUGH */
+
+ case PRU_DETACH: /* called from close() */
+ /* called only after disconnect was called */
+ error = DoEvent(T_DETACH);
+ if (tpcb->tp_state == TP_CLOSED) {
+ if (tpcb->tp_notdetached) {
+ IFDEBUG(D_CONN)
+ printf("PRU_DETACH: not detached\n");
+ ENDDEBUG
+ tp_detach(tpcb);
+ }
+ free((caddr_t)tpcb, M_PCB);
+ tpcb = 0;
+ }
+ break;
+
+ case PRU_SHUTDOWN:
+ /* recv end may have been released; local credit might be zero */
+ case PRU_DISCONNECT:
+ E.ATTR(T_DISC_req).e_reason = E_TP_NORMAL_DISC;
+ error = DoEvent(T_DISC_req);
+ break;
+
+ case PRU_BIND:
+ error = tp_pcbbind(tpcb, nam);
+ break;
+
+ case PRU_LISTEN:
+ if (tpcb->tp_state != TP_CLOSED || tpcb->tp_lsuffixlen == 0 ||
+ tpcb->tp_next == 0)
+ error = EINVAL;
+ else {
+ register struct tp_pcb **tt;
+ remque(tpcb);
+ tpcb->tp_next = tpcb->tp_prev = tpcb;
+ for (tt = &tp_listeners; *tt; tt = &((*tt)->tp_nextlisten))
+ if ((*tt)->tp_lsuffixlen)
+ break;
+ tpcb->tp_nextlisten = *tt;
+ *tt = tpcb;
+ error = DoEvent(T_LISTEN_req);
+ }
+ break;
+
+ case PRU_CONNECT2:
+ error = EOPNOTSUPP; /* for unix domain sockets */
+ break;
+
+ case PRU_CONNECT:
+ IFTRACE(D_CONN)
+ tptraceTPCB(TPPTmisc,
+ "PRU_CONNECT: so 0x%x *SHORT_LSUFXP(tpcb) 0x%x lsuflen 0x%x, class 0x%x",
+ tpcb->tp_sock, *SHORT_LSUFXP(tpcb), tpcb->tp_lsuffixlen,
+ tpcb->tp_class);
+ ENDTRACE
+ IFDEBUG(D_CONN)
+ printf("PRU_CONNECT: so *SHORT_LSUFXP(tpcb) 0x%x lsuflen 0x%x, class 0x%x",
+ tpcb->tp_sock, *SHORT_LSUFXP(tpcb), tpcb->tp_lsuffixlen,
+ tpcb->tp_class);
+ ENDDEBUG
+ if (tpcb->tp_lsuffixlen == 0) {
+ if (error = tp_pcbbind(tpcb, MNULL)) {
+ IFDEBUG(D_CONN)
+ printf("pcbbind returns error 0x%x\n", error);
+ ENDDEBUG
+ break;
+ }
+ }
+ IFDEBUG(D_CONN)
+ printf("isop 0x%x isop->isop_socket offset 12 :\n", tpcb->tp_npcb);
+ dump_buf(tpcb->tp_npcb, 16);
+ ENDDEBUG
+ if (error = tp_route_to(nam, tpcb, /* channel */0))
+ break;
+ IFDEBUG(D_CONN)
+ printf(
+ "PRU_CONNECT after tpcb 0x%x so 0x%x npcb 0x%x flags 0x%x\n",
+ tpcb, so, tpcb->tp_npcb, tpcb->tp_flags);
+ printf("isop 0x%x isop->isop_socket offset 12 :\n", tpcb->tp_npcb);
+ dump_buf(tpcb->tp_npcb, 16);
+ ENDDEBUG
+ if (tpcb->tp_fsuffixlen == 0) {
+ /* didn't set peer extended suffix */
+ (tpcb->tp_nlproto->nlp_getsufx)(tpcb->tp_npcb, &tpcb->tp_fsuffixlen,
+ tpcb->tp_fsuffix, TP_FOREIGN);
+ }
+ if (tpcb->tp_state == TP_CLOSED) {
+ soisconnecting(so);
+ error = DoEvent(T_CONN_req);
+ } else {
+ (tpcb->tp_nlproto->nlp_pcbdisc)(tpcb->tp_npcb);
+ error = EISCONN;
+ }
+ IFPERF(tpcb)
+ u_int lsufx, fsufx;
+ lsufx = *(u_short *)(tpcb->tp_lsuffix);
+ fsufx = *(u_short *)(tpcb->tp_fsuffix);
+
+ tpmeas(tpcb->tp_lref,
+ TPtime_open | (tpcb->tp_xtd_format << 4),
+ &time, lsufx, fsufx, tpcb->tp_fref);
+ ENDPERF
+ break;
+
+ case PRU_ACCEPT:
+ (tpcb->tp_nlproto->nlp_getnetaddr)(tpcb->tp_npcb, nam, TP_FOREIGN);
+ IFDEBUG(D_REQUEST)
+ printf("ACCEPT PEERADDDR:");
+ dump_buf(mtod(nam, char *), nam->m_len);
+ ENDDEBUG
+ IFPERF(tpcb)
+ u_int lsufx, fsufx;
+ lsufx = *(u_short *)(tpcb->tp_lsuffix);
+ fsufx = *(u_short *)(tpcb->tp_fsuffix);
+
+ tpmeas(tpcb->tp_lref, TPtime_open,
+ &time, lsufx, fsufx, tpcb->tp_fref);
+ ENDPERF
+ break;
+
+ case PRU_RCVD:
+ if (so->so_state & SS_ISCONFIRMING) {
+ if (tpcb->tp_state == TP_CONFIRMING)
+ error = tp_confirm(tpcb);
+ break;
+ }
+ IFTRACE(D_DATA)
+ tptraceTPCB(TPPTmisc,
+ "RCVD BF: lcredit sent_lcdt cc hiwat \n",
+ tpcb->tp_lcredit, tpcb->tp_sent_lcdt,
+ so->so_rcv.sb_cc, so->so_rcv.sb_hiwat);
+ LOCAL_CREDIT(tpcb);
+ tptraceTPCB(TPPTmisc,
+ "PRU_RCVD AF sbspace lcredit hiwat cc",
+ sbspace(&so->so_rcv), tpcb->tp_lcredit,
+ so->so_rcv.sb_cc, so->so_rcv.sb_hiwat);
+ ENDTRACE
+ IFDEBUG(D_REQUEST)
+ printf("RCVD: cc %d space %d hiwat %d\n",
+ so->so_rcv.sb_cc, sbspace(&so->so_rcv),
+ so->so_rcv.sb_hiwat);
+ ENDDEBUG
+ if (((int)nam) & MSG_OOB)
+ error = DoEvent(T_USR_Xrcvd);
+ else
+ error = DoEvent(T_USR_rcvd);
+ break;
+
+ case PRU_RCVOOB:
+ if ((so->so_state & SS_ISCONNECTED) == 0) {
+ error = ENOTCONN;
+ break;
+ }
+ if (! tpcb->tp_xpd_service) {
+ error = EOPNOTSUPP;
+ break;
+ }
+ /* kludge - nam is really flags here */
+ error = tp_rcvoob(tpcb, so, m, outflags, (int)nam);
+ break;
+
+ case PRU_SEND:
+ case PRU_SENDOOB:
+ if (controlp) {
+ error = tp_snd_control(controlp, so, &m);
+ controlp = NULL;
+ if (error)
+ break;
+ }
+ if ((so->so_state & SS_ISCONFIRMING) &&
+ (tpcb->tp_state == TP_CONFIRMING) &&
+ (error = tp_confirm(tpcb)))
+ break;
+ if (req == PRU_SENDOOB) {
+ error = (tpcb->tp_xpd_service == 0) ?
+ EOPNOTSUPP : tp_sendoob(tpcb, so, m, outflags);
+ break;
+ }
+ if (m == 0)
+ break;
+ if (m->m_flags & M_EOR) {
+ eotsdu = 1;
+ m->m_flags &= ~M_EOR;
+ }
+ if (eotsdu == 0 && m->m_pkthdr.len == 0)
+ break;
+ if (tpcb->tp_state != TP_AKWAIT && tpcb->tp_state != TP_OPEN) {
+ error = ENOTCONN;
+ break;
+ }
+ /*
+ * The protocol machine copies mbuf chains,
+ * prepends headers, assigns seq numbers, and
+ * puts the packets on the device.
+ * When they are acked they are removed from the socket buf.
+ *
+ * sosend calls this up until sbspace goes negative.
+ * Sbspace may be made negative by appending this mbuf chain,
+ * possibly by a whole cluster.
+ */
+ {
+ /*
+ * Could have eotsdu and no data.(presently MUST have
+ * an mbuf though, even if its length == 0)
+ */
+ int totlen = m->m_pkthdr.len;
+ struct sockbuf *sb = &so->so_snd;
+ IFPERF(tpcb)
+ PStat(tpcb, Nb_from_sess) += totlen;
+ tpmeas(tpcb->tp_lref, TPtime_from_session, 0, 0,
+ PStat(tpcb, Nb_from_sess), totlen);
+ ENDPERF
+ IFDEBUG(D_SYSCALL)
+ printf(
+ "PRU_SEND: eot %d before sbappend 0x%x len 0x%x to sb @ 0x%x\n",
+ eotsdu, m, totlen, sb);
+ dump_mbuf(sb->sb_mb, "so_snd.sb_mb");
+ dump_mbuf(m, "m : to be added");
+ ENDDEBUG
+ tp_packetize(tpcb, m, eotsdu);
+ IFDEBUG(D_SYSCALL)
+ printf("PRU_SEND: eot %d after sbappend 0x%x\n", eotsdu, m);
+ dump_mbuf(sb->sb_mb, "so_snd.sb_mb");
+ ENDDEBUG
+ if (tpcb->tp_state == TP_OPEN)
+ error = DoEvent(T_DATA_req);
+ IFDEBUG(D_SYSCALL)
+ printf("PRU_SEND: after driver error 0x%x \n",error);
+ printf("so_snd 0x%x cc 0t%d mbcnt 0t%d\n",
+ sb, sb->sb_cc, sb->sb_mbcnt);
+ dump_mbuf(sb->sb_mb, "so_snd.sb_mb after driver");
+ ENDDEBUG
+ }
+ break;
+
+ case PRU_SOCKADDR:
+ (tpcb->tp_nlproto->nlp_getnetaddr)(tpcb->tp_npcb, nam, TP_LOCAL);
+ break;
+
+ case PRU_PEERADDR:
+ (tpcb->tp_nlproto->nlp_getnetaddr)(tpcb->tp_npcb, nam, TP_FOREIGN);
+ break;
+
+ case PRU_CONTROL:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_PROTOSEND:
+ case PRU_PROTORCV:
+ case PRU_SENSE:
+ case PRU_SLOWTIMO:
+ case PRU_FASTTIMO:
+ error = EOPNOTSUPP;
+ break;
+
+ default:
+#ifdef ARGO_DEBUG
+ printf("tp_usrreq UNKNOWN PRU %d\n", req);
+#endif /* ARGO_DEBUG */
+ error = EOPNOTSUPP;
+ }
+
+ IFDEBUG(D_REQUEST)
+ printf("%s, so 0x%x, tpcb 0x%x, error %d, state %d\n",
+ "returning from tp_usrreq", so, tpcb, error,
+ tpcb ? tpcb->tp_state : 0);
+ ENDDEBUG
+ IFTRACE(D_REQUEST)
+ tptraceTPCB(TPPTusrreq, "END req so m state [", req, so, m,
+ tpcb ? tpcb->tp_state : 0);
+ ENDTRACE
+ if (controlp) {
+ m_freem(controlp);
+ printf("control data unexpectedly retained in tp_usrreq()");
+ }
+ splx(s);
+ return error;
+}
+tp_ltrace(so, uio)
+struct socket *so;
+struct uio *uio;
+{
+ IFTRACE(D_DATA)
+ register struct tp_pcb *tpcb = sototpcb(so);
+ if (tpcb) {
+ tptraceTPCB(TPPTmisc, "sosend so resid iovcnt", so,
+ uio->uio_resid, uio->uio_iovcnt, 0);
+ }
+ ENDTRACE
+}
+
+tp_confirm(tpcb)
+register struct tp_pcb *tpcb;
+{
+ struct tp_event E;
+ if (tpcb->tp_state == TP_CONFIRMING)
+ return DoEvent(T_ACPT_req);
+ printf("Tp confirm called when not confirming; tpcb 0x%x, state 0x%x\n",
+ tpcb, tpcb->tp_state);
+ return 0;
+}
+
+/*
+ * Process control data sent with sendmsg()
+ */
+tp_snd_control(m, so, data)
+ struct mbuf *m;
+ struct socket *so;
+ register struct mbuf **data;
+{
+ register struct cmsghdr *ch;
+ int error = 0;
+
+ if (m && m->m_len) {
+ ch = mtod(m, struct cmsghdr *);
+ m->m_len -= sizeof (*ch);
+ m->m_data += sizeof (*ch);
+ error = tp_ctloutput(PRCO_SETOPT,
+ so, ch->cmsg_level, ch->cmsg_type, &m);
+ if (ch->cmsg_type == TPOPT_DISC_DATA) {
+ if (data && *data) {
+ m_freem(*data);
+ *data = 0;
+ }
+ error = tp_usrreq(so, PRU_DISCONNECT, (struct mbuf *)0,
+ (caddr_t)0, (struct mbuf *)0);
+ }
+ }
+ if (m)
+ m_freem(m);
+ return error;
+}
diff --git a/sys/netiso/tuba_subr.c b/sys/netiso/tuba_subr.c
new file mode 100644
index 0000000..d346927
--- /dev/null
+++ b/sys/netiso/tuba_subr.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tuba_subr.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+
+#include <net/route.h>
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_debug.h>
+
+#include <netiso/argo_debug.h>
+#include <netiso/iso.h>
+#include <netiso/clnp.h>
+#include <netiso/iso_pcb.h>
+#include <netiso/iso_var.h>
+#include <netiso/tuba_table.h>
+
+static struct sockaddr_iso null_siso = { sizeof(null_siso), AF_ISO, };
+extern int tuba_table_size, tcp_keepidle, tcp_keepintvl, tcp_maxidle;
+extern int tcppcbcachemiss, tcppredack, tcppreddat, tcprexmtthresh;
+extern struct tcpiphdr tcp_saveti;
+struct inpcb tuba_inpcb;
+struct inpcb *tuba_last_inpcb = &tuba_inpcb;
+struct isopcb tuba_isopcb;
+/*
+ * Tuba initialization
+ */
+tuba_init()
+{
+#define TUBAHDRSIZE (3 /*LLC*/ + 9 /*CLNP Fixed*/ + 42 /*Addresses*/ \
+ + 6 /*CLNP Segment*/ + 20 /*TCP*/)
+
+ tuba_inpcb.inp_next = tuba_inpcb.inp_prev = &tuba_inpcb;
+ tuba_isopcb.isop_next = tuba_isopcb.isop_prev = &tuba_isopcb;
+ tuba_isopcb.isop_faddr = &tuba_isopcb.isop_sfaddr;
+ tuba_isopcb.isop_laddr = &tuba_isopcb.isop_sladdr;
+ if (max_protohdr < TUBAHDRSIZE)
+ max_protohdr = TUBAHDRSIZE;
+ if (max_linkhdr + TUBAHDRSIZE > MHLEN)
+ panic("tuba_init");
+}
+
+struct addr_arg {
+ int error;
+ int offset;
+ u_long sum;
+};
+
+/*
+ * Calculate contribution to fudge factor for TCP checksum,
+ * and coincidentally set pointer for convenience of clnp_output
+ * if we are are responding when there is no isopcb around.
+ */
+static void
+tuba_getaddr(arg, siso, index)
+ register struct addr_arg *arg;
+ struct sockaddr_iso **siso;
+ u_long index;
+{
+ register struct tuba_cache *tc;
+ if (index <= tuba_table_size && (tc = tuba_table[index])) {
+ if (siso)
+ *siso = &tc->tc_siso;
+ arg->sum += (arg->offset & 1 ? tc->tc_ssum : tc->tc_sum)
+ + (0xffff ^ index);
+ arg->offset += tc->tc_siso.siso_nlen + 1;
+ } else
+ arg->error = 1;
+}
+
+tuba_output(m, tp)
+ register struct mbuf *m;
+ struct tcpcb *tp;
+{
+ register struct tcpiphdr *n;
+ struct isopcb *isop;
+ struct addr_arg arg;
+
+ if (tp == 0 || (n = tp->t_template) == 0 ||
+ (isop = (struct isopcb *)tp->t_tuba_pcb) == 0) {
+ isop = &tuba_isopcb;
+ n = mtod(m, struct tcpiphdr *);
+ arg.error = arg.sum = arg.offset = 0;
+ tuba_getaddr(&arg, &tuba_isopcb.isop_faddr, n->ti_dst.s_addr);
+ tuba_getaddr(&arg, &tuba_isopcb.isop_laddr, n->ti_src.s_addr);
+ REDUCE(arg.sum, arg.sum);
+ goto adjust;
+ }
+ if (n->ti_sum == 0) {
+ arg.error = arg.sum = arg.offset = 0;
+ tuba_getaddr(&arg, (struct sockaddr_iso **)0, n->ti_dst.s_addr);
+ tuba_getaddr(&arg, (struct sockaddr_iso **)0, n->ti_src.s_addr);
+ REDUCE(arg.sum, arg.sum);
+ n->ti_sum = arg.sum;
+ n = mtod(m, struct tcpiphdr *);
+ adjust:
+ if (arg.error) {
+ m_freem(m);
+ return (EADDRNOTAVAIL);
+ }
+ REDUCE(n->ti_sum, n->ti_sum + (0xffff ^ arg.sum));
+ }
+ m->m_len -= sizeof (struct ip);
+ m->m_pkthdr.len -= sizeof (struct ip);
+ m->m_data += sizeof (struct ip);
+ return (clnp_output(m, isop, m->m_pkthdr.len, 0));
+}
+
+tuba_refcnt(isop, delta)
+ struct isopcb *isop;
+{
+ register struct tuba_cache *tc;
+ unsigned index, sum;
+
+ if (delta != 1)
+ delta = -1;
+ if (isop == 0 || isop->isop_faddr == 0 || isop->isop_laddr == 0 ||
+ (delta == -1 && isop->isop_tuba_cached == 0) ||
+ (delta == 1 && isop->isop_tuba_cached != 0))
+ return;
+ isop->isop_tuba_cached = (delta == 1);
+ if ((index = tuba_lookup(isop->isop_faddr, M_DONTWAIT)) != 0 &&
+ (tc = tuba_table[index]) != 0 && (delta == 1 || tc->tc_refcnt > 0))
+ tc->tc_refcnt += delta;
+ if ((index = tuba_lookup(isop->isop_laddr, M_DONTWAIT)) != 0 &&
+ (tc = tuba_table[index]) != 0 && (delta == 1 || tc->tc_refcnt > 0))
+ tc->tc_refcnt += delta;
+}
+
+tuba_pcbdetach(isop)
+ struct isopcb *isop;
+{
+ if (isop == 0)
+ return;
+ tuba_refcnt(isop, -1);
+ isop->isop_socket = 0;
+ iso_pcbdetach(isop);
+}
+
+/*
+ * Avoid in_pcbconnect in faked out tcp_input()
+ */
+tuba_pcbconnect(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_iso *siso;
+ struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
+ struct tcpcb *tp = intotcpcb(inp);
+ struct isopcb *isop = (struct isopcb *)tp->t_tuba_pcb;
+ int error;
+
+ /* hardwire iso_pcbbind() here */
+ siso = isop->isop_laddr = &isop->isop_sladdr;
+ *siso = tuba_table[inp->inp_laddr.s_addr]->tc_siso;
+ siso->siso_tlen = sizeof(inp->inp_lport);
+ bcopy((caddr_t)&inp->inp_lport, TSEL(siso), sizeof(inp->inp_lport));
+
+ /* hardwire in_pcbconnect() here without assigning route */
+ inp->inp_fport = sin->sin_port;
+ inp->inp_faddr = sin->sin_addr;
+
+ /* reuse nam argument to call iso_pcbconnect() */
+ nam->m_len = sizeof(*siso);
+ siso = mtod(nam, struct sockaddr_iso *);
+ *siso = tuba_table[inp->inp_faddr.s_addr]->tc_siso;
+ siso->siso_tlen = sizeof(inp->inp_fport);
+ bcopy((caddr_t)&inp->inp_fport, TSEL(siso), sizeof(inp->inp_fport));
+
+ if ((error = iso_pcbconnect(isop, nam)) == 0)
+ tuba_refcnt(isop, 1);
+ return (error);
+}
+
+/*
+ * CALLED FROM:
+ * clnp's input routine, indirectly through the protosw.
+ * FUNCTION and ARGUMENTS:
+ * Take a packet (m) from clnp, strip off the clnp header
+ * and do tcp input processing.
+ * No return value.
+ */
+tuba_tcpinput(m, src, dst)
+ register struct mbuf *m;
+ struct sockaddr_iso *src, *dst;
+{
+ unsigned long sum, lindex, findex;
+ register struct tcpiphdr *ti;
+ register struct inpcb *inp;
+ caddr_t optp = NULL;
+ int optlen;
+ int len, tlen, off;
+ register struct tcpcb *tp = 0;
+ int tiflags;
+ struct socket *so;
+ int todrop, acked, ourfinisacked, needoutput = 0;
+ short ostate;
+ struct in_addr laddr;
+ int dropsocket = 0, iss = 0;
+ u_long tiwin, ts_val, ts_ecr;
+ int ts_present = 0;
+
+ if ((m->m_flags & M_PKTHDR) == 0)
+ panic("tuba_tcpinput");
+ /*
+ * Do some housekeeping looking up CLNP addresses.
+ * If we are out of space might as well drop the packet now.
+ */
+ tcpstat.tcps_rcvtotal++;
+ lindex = tuba_lookup(dst, M_DONTWAIT);
+ findex = tuba_lookup(src, M_DONTWAIT);
+ if (lindex == 0 || findex == 0)
+ goto drop;
+ /*
+ * CLNP gave us an mbuf chain WITH the clnp header pulled up,
+ * but the data pointer pushed past it.
+ */
+ len = m->m_len;
+ tlen = m->m_pkthdr.len;
+ m->m_data -= sizeof(struct ip);
+ m->m_len += sizeof(struct ip);
+ m->m_pkthdr.len += sizeof(struct ip);
+ m->m_flags &= ~(M_MCAST|M_BCAST); /* XXX should do this in clnp_input */
+ /*
+ * The reassembly code assumes it will be overwriting a useless
+ * part of the packet, which is why we need to have it point
+ * into the packet itself.
+ *
+ * Check to see if the data is properly alligned
+ * so that we can save copying the tcp header.
+ * This code knows way too much about the structure of mbufs!
+ */
+ off = ((sizeof (long) - 1) & ((m->m_flags & M_EXT) ?
+ (m->m_data - m->m_ext.ext_buf) : (m->m_data - m->m_pktdat)));
+ if (off || len < sizeof(struct tcphdr)) {
+ struct mbuf *m0 = m;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == 0) {
+ m = m0;
+ goto drop;
+ }
+ m->m_next = m0;
+ m->m_data += max_linkhdr;
+ m->m_pkthdr = m0->m_pkthdr;
+ m->m_flags = m0->m_flags & M_COPYFLAGS;
+ if (len < sizeof(struct tcphdr)) {
+ m->m_len = 0;
+ if ((m = m_pullup(m, sizeof(struct tcpiphdr))) == 0) {
+ tcpstat.tcps_rcvshort++;
+ return;
+ }
+ } else {
+ bcopy(mtod(m0, caddr_t) + sizeof(struct ip),
+ mtod(m, caddr_t) + sizeof(struct ip),
+ sizeof(struct tcphdr));
+ m0->m_len -= sizeof(struct tcpiphdr);
+ m0->m_data += sizeof(struct tcpiphdr);
+ m->m_len = sizeof(struct tcpiphdr);
+ }
+ }
+ /*
+ * Calculate checksum of extended TCP header and data,
+ * replacing what would have been IP addresses by
+ * the IP checksum of the CLNP addresses.
+ */
+ ti = mtod(m, struct tcpiphdr *);
+ ti->ti_dst.s_addr = tuba_table[lindex]->tc_sum;
+ if (dst->siso_nlen & 1)
+ ti->ti_src.s_addr = tuba_table[findex]->tc_sum;
+ else
+ ti->ti_src.s_addr = tuba_table[findex]->tc_ssum;
+ ti->ti_prev = ti->ti_next = 0;
+ ti->ti_x1 = 0; ti->ti_pr = ISOPROTO_TCP;
+ ti->ti_len = htons((u_short)tlen);
+ if (ti->ti_sum = in_cksum(m, m->m_pkthdr.len)) {
+ tcpstat.tcps_rcvbadsum++;
+ goto drop;
+ }
+ ti->ti_src.s_addr = findex;
+ ti->ti_dst.s_addr = lindex;
+ /*
+ * Now include the rest of TCP input
+ */
+#define TUBA_INCLUDE
+#define in_pcbconnect tuba_pcbconnect
+#define tcb tuba_inpcb
+#define tcp_last_inpcb tuba_last_inpcb
+
+#include <netinet/tcp_input.c>
+}
+
+#define tcp_slowtimo tuba_slowtimo
+#define tcp_fasttimo tuba_fasttimo
+
+#include <netinet/tcp_timer.c>
diff --git a/sys/netiso/tuba_table.c b/sys/netiso/tuba_table.c
new file mode 100644
index 0000000..a1bf5f9
--- /dev/null
+++ b/sys/netiso/tuba_table.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tuba_table.c 8.2 (Berkeley) 11/15/93
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/radix.h>
+
+#include <netiso/iso.h>
+#include <netiso/tuba_table.h>
+
+int tuba_table_size;
+struct tuba_cache **tuba_table;
+struct radix_node_head *tuba_tree;
+extern int arpt_keep, arpt_prune; /* use same values as arp cache */
+
+void
+tuba_timer()
+{
+ int s = splnet();
+ int i;
+ register struct tuba_cache *tc;
+ long timelimit = time.tv_sec - arpt_keep;
+
+ timeout(tuba_timer, (caddr_t)0, arpt_prune * hz);
+ for (i = tuba_table_size; i > 0; i--)
+ if ((tc = tuba_table[i]) && (tc->tc_refcnt == 0) &&
+ (tc->tc_time < timelimit)) {
+ tuba_table[i] = 0;
+ rn_delete(&tc->tc_siso.siso_addr, NULL, tuba_tree);
+ free((caddr_t)tc, M_RTABLE);
+ }
+ splx(s);
+}
+
+tuba_table_init()
+{
+ rn_inithead((void **)&tuba_tree, 40);
+ timeout(tuba_timer, (caddr_t)0, arpt_prune * hz);
+}
+
+int
+tuba_lookup(siso, wait)
+ register struct sockaddr_iso *siso;
+{
+ struct radix_node *rn, *rn_match();
+ register struct tuba_cache *tc;
+ struct tuba_cache **new;
+ int dupentry = 0, sum_a = 0, sum_b = 0, old_size, i;
+
+ if ((rn = rn_match((caddr_t)&siso->siso_addr, tuba_tree->rnh_treetop))
+ && ((rn->rn_flags & RNF_ROOT) == 0)) {
+ tc = (struct tuba_cache *)rn;
+ tc->tc_time = time.tv_sec;
+ return (tc->tc_index);
+ }
+ if ((tc = (struct tuba_cache *)malloc(sizeof(*tc), M_RTABLE, wait))
+ == NULL)
+ return (0);
+ bzero((caddr_t)tc, sizeof (*tc));
+ bcopy(siso->siso_data, tc->tc_siso.siso_data,
+ tc->tc_siso.siso_nlen = siso->siso_nlen);
+ rn_insert(&tc->tc_siso.siso_addr, tuba_tree, &dupentry, tc->tc_nodes);
+ if (dupentry)
+ panic("tuba_lookup 1");
+ tc->tc_siso.siso_family = AF_ISO;
+ tc->tc_siso.siso_len = sizeof(tc->tc_siso);
+ tc->tc_time = time.tv_sec;
+ for (i = sum_a = tc->tc_siso.siso_nlen; --i >= 0; )
+ (i & 1 ? sum_a : sum_b) += (u_char)tc->tc_siso.siso_data[i];
+ REDUCE(tc->tc_sum, (sum_a << 8) + sum_b);
+ HTONS(tc->tc_sum);
+ SWAB(tc->tc_ssum, tc->tc_sum);
+ for (i = tuba_table_size; i > 0; i--)
+ if (tuba_table[i] == 0)
+ goto fixup;
+ old_size = tuba_table_size;
+ if (tuba_table_size == 0)
+ tuba_table_size = 15;
+ if (tuba_table_size > 0x7fff)
+ return (0);
+ tuba_table_size = 1 + 2 * tuba_table_size;
+ i = (tuba_table_size + 1) * sizeof(tc);
+ new = (struct tuba_cache **)malloc((unsigned)i, M_RTABLE, wait);
+ if (new == 0) {
+ tuba_table_size = old_size;
+ rn_delete(&tc->tc_siso.siso_addr, NULL, tuba_tree);
+ free((caddr_t)tc, M_RTABLE);
+ return (0);
+ }
+ bzero((caddr_t)new, (unsigned)i);
+ if (tuba_table) {
+ bcopy((caddr_t)tuba_table, (caddr_t)new, i >> 1);
+ free((caddr_t)tuba_table, M_RTABLE);
+ }
+ tuba_table = new;
+ i = tuba_table_size;
+fixup:
+ tuba_table[i] = tc;
+ tc->tc_index = i;
+ return (tc->tc_index);
+}
diff --git a/sys/netiso/tuba_table.h b/sys/netiso/tuba_table.h
new file mode 100644
index 0000000..6be8afa
--- /dev/null
+++ b/sys/netiso/tuba_table.h
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tuba_table.h 8.1 (Berkeley) 6/10/93
+ */
+
+struct tuba_cache {
+ struct radix_node tc_nodes[2]; /* convenient lookup */
+ int tc_refcnt;
+ int tc_time; /* last looked up */
+ int tc_flags;
+#define TCF_PERM 1
+ int tc_index;
+ u_short tc_sum; /* cksum of nsap inc. length */
+ u_short tc_ssum; /* swab(tc_sum) */
+ struct sockaddr_iso tc_siso; /* for responding */
+};
+
+#define ADDCARRY(x) (x >= 65535 ? x -= 65535 : x)
+#define REDUCE(a, b) { union { u_short s[2]; long l;} l_util; long x; \
+ l_util.l = (b); x = l_util.s[0] + l_util.s[1]; ADDCARRY(x); \
+ if (x == 0) x = 0xffff; a = x;}
+#define SWAB(a, b) { union { u_char c[2]; u_short s;} s; u_char t; \
+ s.s = (b); t = s.c[0]; s.c[0] = s.c[1]; s.c[1] = t; a = s.s;}
+
+#ifdef KERNEL
+extern int tuba_table_size;
+extern struct tuba_cache **tuba_table;
+extern struct radix_node_head *tuba_tree;
+#endif
diff --git a/sys/netiso/tuba_usrreq.c b/sys/netiso/tuba_usrreq.c
new file mode 100644
index 0000000..2d92117
--- /dev/null
+++ b/sys/netiso/tuba_usrreq.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tuba_usrreq.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_debug.h>
+
+#include <netiso/argo_debug.h>
+#include <netiso/iso.h>
+#include <netiso/clnp.h>
+#include <netiso/iso_pcb.h>
+#include <netiso/iso_var.h>
+#include <netiso/tuba_table.h>
+/*
+ * TCP protocol interface to socket abstraction.
+ */
+extern char *tcpstates[];
+extern struct inpcb tuba_inpcb;
+extern struct isopcb tuba_isopcb;
+
+/*
+ * Process a TCP user request for TCP tb. If this is a send request
+ * then m is the mbuf chain of send data. If this is a timer expiration
+ * (called from the software clock routine), then timertype tells which timer.
+ */
+/*ARGSUSED*/
+tuba_usrreq(so, req, m, nam, control)
+ struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *control;
+{
+ register struct inpcb *inp;
+ register struct isopcb *isop;
+ register struct tcpcb *tp;
+ int s;
+ int error = 0;
+ int ostate;
+ struct sockaddr_iso *siso;
+
+ if (req == PRU_CONTROL)
+ return (iso_control(so, (int)m, (caddr_t)nam,
+ (struct ifnet *)control));
+
+ s = splnet();
+ inp = sotoinpcb(so);
+ /*
+ * When a TCP is attached to a socket, then there will be
+ * a (struct inpcb) pointed at by the socket, and this
+ * structure will point at a subsidary (struct tcpcb).
+ */
+ if (inp == 0 && req != PRU_ATTACH) {
+ splx(s);
+ return (EINVAL); /* XXX */
+ }
+ if (inp) {
+ tp = intotcpcb(inp);
+ if (tp == 0)
+ panic("tuba_usrreq");
+ ostate = tp->t_state;
+ isop = (struct isopcb *)tp->t_tuba_pcb;
+ if (isop == 0)
+ panic("tuba_usrreq 2");
+ } else
+ ostate = 0;
+ switch (req) {
+
+ /*
+ * TCP attaches to socket via PRU_ATTACH, reserving space,
+ * and an internet control block. We also need to
+ * allocate an isopcb and separate the control block from
+ * tcp/ip ones.
+ */
+ case PRU_ATTACH:
+ if (error = iso_pcballoc(so, &tuba_isopcb))
+ break;
+ isop = (struct isopcb *)so->so_pcb;
+ so->so_pcb = 0;
+ if (error = tcp_usrreq(so, req, m, nam, control)) {
+ isop->isop_socket = 0;
+ iso_pcbdetach(isop);
+ } else {
+ inp = sotoinpcb(so);
+ remque(inp);
+ insque(inp, &tuba_inpcb);
+ inp->inp_head = &tuba_inpcb;
+ tp = intotcpcb(inp);
+ if (tp == 0)
+ panic("tuba_usrreq 3");
+ tp->t_tuba_pcb = (caddr_t) isop;
+ }
+ goto notrace;
+
+ /*
+ * PRU_DETACH detaches the TCP protocol from the socket.
+ * If the protocol state is non-embryonic, then can't
+ * do this directly: have to initiate a PRU_DISCONNECT,
+ * which may finish later; embryonic TCB's can just
+ * be discarded here.
+ */
+ case PRU_DETACH:
+ if (tp->t_state > TCPS_LISTEN)
+ tp = tcp_disconnect(tp);
+ else
+ tp = tcp_close(tp);
+ if (tp == 0)
+ tuba_pcbdetach(isop);
+ break;
+
+ /*
+ * Give the socket an address.
+ */
+ case PRU_BIND:
+ siso = mtod(nam, struct sockaddr_iso *);
+ if (siso->siso_tlen && siso->siso_tlen != 2) {
+ error = EINVAL;
+ break;
+ }
+ if ((error = iso_pcbbind(isop, nam)) ||
+ (siso = isop->isop_laddr) == 0)
+ break;
+ bcopy(TSEL(siso), &inp->inp_lport, 2);
+ if (siso->siso_nlen &&
+ !(inp->inp_laddr.s_addr = tuba_lookup(siso, M_WAITOK)))
+ error = ENOBUFS;
+ break;
+
+ /*
+ * Prepare to accept connections.
+ */
+ case PRU_CONNECT:
+ case PRU_LISTEN:
+ if (inp->inp_lport == 0 &&
+ (error = iso_pcbbind(isop, (struct mbuf *)0)))
+ break;
+ bcopy(TSEL(isop->isop_laddr), &inp->inp_lport, 2);
+ if (req == PRU_LISTEN) {
+ tp->t_state = TCPS_LISTEN;
+ break;
+ }
+ /*FALLTHROUGH*/
+ /*
+ * Initiate connection to peer.
+ * Create a template for use in transmissions on this connection.
+ * Enter SYN_SENT state, and mark socket as connecting.
+ * Start keep-alive timer, and seed output sequence space.
+ * Send initial segment on connection.
+ */
+ /* case PRU_CONNECT: */
+ if (error = iso_pcbconnect(isop, nam))
+ break;
+ if ((siso = isop->isop_laddr) && siso->siso_nlen > 1)
+ siso->siso_data[siso->siso_nlen - 1] = ISOPROTO_TCP;
+ else
+ panic("tuba_usrreq: connect");
+ siso = mtod(nam, struct sockaddr_iso *);
+ if (!(inp->inp_faddr.s_addr = tuba_lookup(siso, M_WAITOK))) {
+ unconnect:
+ iso_pcbdisconnect(isop);
+ error = ENOBUFS;
+ break;
+ }
+ bcopy(TSEL(isop->isop_faddr), &inp->inp_fport, 2);
+ if (inp->inp_laddr.s_addr == 0 &&
+ (inp->inp_laddr.s_addr =
+ tuba_lookup(isop->isop_laddr, M_WAITOK)) == 0)
+ goto unconnect;
+ if ((tp->t_template = tcp_template(tp)) == 0)
+ goto unconnect;
+ soisconnecting(so);
+ tcpstat.tcps_connattempt++;
+ tp->t_state = TCPS_SYN_SENT;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2;
+ tcp_sendseqinit(tp);
+ error = tcp_output(tp);
+ tuba_refcnt(isop, 1);
+ break;
+
+ /*
+ * Initiate disconnect from peer.
+ * If connection never passed embryonic stage, just drop;
+ * else if don't need to let data drain, then can just drop anyways,
+ * else have to begin TCP shutdown process: mark socket disconnecting,
+ * drain unread data, state switch to reflect user close, and
+ * send segment (e.g. FIN) to peer. Socket will be really disconnected
+ * when peer sends FIN and acks ours.
+ *
+ * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB.
+ */
+ case PRU_DISCONNECT:
+ if ((tp = tcp_disconnect(tp)) == 0)
+ tuba_pcbdetach(isop);
+ break;
+
+ /*
+ * Accept a connection. Essentially all the work is
+ * done at higher levels; just return the address
+ * of the peer, storing through addr.
+ */
+ case PRU_ACCEPT:
+ bcopy((caddr_t)isop->isop_faddr, mtod(nam, caddr_t),
+ nam->m_len = isop->isop_faddr->siso_len);
+ break;
+
+ /*
+ * Mark the connection as being incapable of further output.
+ */
+ case PRU_SHUTDOWN:
+ socantsendmore(so);
+ tp = tcp_usrclosed(tp);
+ if (tp)
+ error = tcp_output(tp);
+ else
+ tuba_pcbdetach(isop);
+ break;
+ /*
+ * Abort the TCP.
+ */
+ case PRU_ABORT:
+ if ((tp = tcp_drop(tp, ECONNABORTED)) == 0)
+ tuba_pcbdetach(isop);
+ break;
+
+
+ case PRU_SOCKADDR:
+ if (isop->isop_laddr)
+ bcopy((caddr_t)isop->isop_laddr, mtod(nam, caddr_t),
+ nam->m_len = isop->isop_laddr->siso_len);
+ break;
+
+ case PRU_PEERADDR:
+ if (isop->isop_faddr)
+ bcopy((caddr_t)isop->isop_faddr, mtod(nam, caddr_t),
+ nam->m_len = isop->isop_faddr->siso_len);
+ break;
+
+ default:
+ error = tcp_usrreq(so, req, m, nam, control);
+ goto notrace;
+ }
+ if (tp && (so->so_options & SO_DEBUG))
+ tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0, req);
+notrace:
+ splx(s);
+ return(error);
+}
+
+tuba_ctloutput(op, so, level, optname, mp)
+ int op;
+ struct socket *so;
+ int level, optname;
+ struct mbuf **mp;
+{
+ int clnp_ctloutput(), tcp_ctloutput();
+
+ return ((level != IPPROTO_TCP ? clnp_ctloutput : tcp_ctloutput)
+ (op, so, level, optname, mp));
+}
diff --git a/sys/netiso/xebec/Makefile b/sys/netiso/xebec/Makefile
new file mode 100644
index 0000000..fa05f9c
--- /dev/null
+++ b/sys/netiso/xebec/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 5.16 (Berkeley) 4/26/91
+
+PROG= xebec
+SRCS= llparse.c llscan.c main.c malloc.c procs.c putdriver.c sets.c xebec.c
+CFLAGS+= -DDEBUG -traditional
+NOMAN = noman
+
+.include <bsd.prog.mk>
diff --git a/sys/netiso/xebec/debug.h b/sys/netiso/xebec/debug.h
new file mode 100644
index 0000000..2e3f167
--- /dev/null
+++ b/sys/netiso/xebec/debug.h
@@ -0,0 +1,22 @@
+/* $Header: debug.h,v 2.1 88/09/19 12:56:16 nhall Exp $ */
+/* $Source: /var/home/tadl/src/argo/xebec/RCS/debug.h,v $ */
+
+#define OUT stdout
+
+extern int debug[128];
+
+#ifdef DEBUG
+extern int column;
+
+#define IFDEBUG(letter) \
+ if(debug['letter']) {
+#define ENDDEBUG ; (void) fflush(stdout);}
+
+#else
+
+#define STAR *
+#define IFDEBUG(letter) //*beginning of comment*/STAR
+#define ENDDEBUG STAR/*end of comment*//
+
+#endif DEBUG
+
diff --git a/sys/netiso/xebec/llparse.c b/sys/netiso/xebec/llparse.c
new file mode 100644
index 0000000..fee7a9f
--- /dev/null
+++ b/sys/netiso/xebec/llparse.c
@@ -0,0 +1,366 @@
+/* $Header: llparse.c,v 2.2 88/09/19 12:54:59 nhall Exp $ */
+/* $Source: /var/home/tadl/src/argo/xebec/RCS/llparse.c,v $ */
+/*
+ * ************************* NOTICE *******************************
+ * This code is in the public domain. It cannot be copyrighted.
+ * This ll parser was originally written by Keith Thompson for the
+ * University of Wisconsin Crystal project.
+ * It was based on an FMQ lr parser written by Jon Mauney at the
+ * University of Wisconsin.
+ * It was subsequently modified very slightly by Nancy Hall at the
+ * University of Wisconsin for the Crystal project.
+ * ****************************************************************
+ */
+#include "xebec.h"
+#include "llparse.h"
+#include "main.h"
+#include <stdio.h>
+
+#include "debug.h"
+
+#define LLMINACTION -LLINF
+
+short llparsestack[STACKSIZE];
+short llstackptr = 0;
+LLtoken lltoken;
+
+llparse()
+{
+ register havetoken = FALSE;
+ register sym;
+ register LLtoken *t = &lltoken;
+ register parseaction;
+ register accepted = FALSE;
+
+ llpushprod(llnprods-1); /* $$$ ::= <start symbol> */
+
+ do {
+ sym = llparsestack[llstackptr];
+ IFDEBUG(L)
+ printf("llparse() top of loop, llstackptr=%d, sym=%d\n",
+ llstackptr, sym);
+ ENDDEBUG
+
+ if(sym < 0) {
+ /* action symbol */
+ if(sym <= LLMINACTION) {
+ for(;sym<=LLMINACTION;sym++) {
+ llaction(1, t); /* calls llfinprod */
+ }
+ llstackptr--;
+ continue;
+ } else { llaction(-sym, t);
+ llstackptr--;
+ continue;
+ }
+ }
+
+ if(sym < llnterms) {
+
+ /* it's a terminal symbol */
+
+ if(!havetoken) {
+ llgettoken(t);
+ havetoken = TRUE;
+ }
+
+ if(sym == t->llterm) {
+ llpushattr(t->llattrib);
+ llaccept(t);
+ llstackptr--; /* pop terminal */
+ if(t->llterm == llnterms-1) { /* end symbol $$$ */
+ accepted = TRUE;
+ } else {
+ havetoken = FALSE;
+ }
+ } else {
+ llparsererror(t); /* wrong terminal on input */
+ havetoken = FALSE;
+ }
+ continue;
+ }
+
+ /* non terminal */
+
+ if(!havetoken) {
+ llgettoken(t);
+ havetoken = TRUE;
+ }
+
+ /* consult parse table for new production */
+ parseaction = llfindaction(sym, t->llterm);
+
+ if(parseaction == 0) {
+ /* error entry */
+ llparsererror(t);
+ havetoken = FALSE;
+ continue;
+ }
+
+ if(llepsilon[parseaction]) {
+ /* epsilon production */
+ if(llepsilonok(t->llterm)) {
+ llstackptr--; /* pop nonterminal */
+ llpushprod(parseaction); /* push rhs of production */
+ } else {
+ llparsererror(t);
+ havetoken = FALSE;
+ }
+ } else {
+ llstackptr--; /* pop nonterminal */
+ llpushprod(parseaction); /* push rhs of production */
+ }
+ } while(!accepted);
+
+ return(0);
+}
+
+llpushprod(prod) /* recognize production prod - push rhs on stack */
+short prod;
+{
+ register start;
+ register length;
+ register count;
+
+ start = llprodindex[prod].llprodstart;
+ length = llprodindex[prod].llprodlength;
+
+ IFDEBUG(L)
+ printf("llpushprod(%d) llstackptr=0x%x(%d), length = 0x%x(%d)\n",
+ prod, llstackptr, llstackptr, length , length);
+ /*
+ dump_parse_stack();
+ */
+ ENDDEBUG
+ if(llstackptr+length >= STACKSIZE) {
+ fprintf(stderr,"Parse stack overflow. llstackptr=0x%x, length=0x%x\n",
+ llstackptr, length);
+ Exit(-1);
+ }
+
+
+ llsetattr(llprodindex[prod].llprodtlen);
+
+ /* put a marker on the stack to mark beginning of production */
+ if(llparsestack[llstackptr] <= LLMINACTION) {
+ (llparsestack[llstackptr]) --; /* if there's already one there, don't
+ put another on; just let it represent all of
+ the adjacent markers */
+ }
+ else {
+ llstackptr++;
+ llparsestack[llstackptr] = LLMINACTION;
+ }
+
+ for(count=0; count<length; count++) {
+ llstackptr++;
+ llparsestack[llstackptr] = llproductions[start++];
+ }
+ if(llstackptr > STACKSIZE) {
+ fprintf(stderr, "PARSE STACK OVERFLOW! \n"); Exit(-1);
+ Exit(-1);
+ }
+}
+
+
+llepsilonok(term)
+{
+ register ptr;
+ register sym;
+ register pact;
+ register nomore;
+ register rval;
+
+ IFDEBUG(L)
+ printf("llepsilonok() enter\n");
+ ENDDEBUG
+ rval = TRUE;
+
+ ptr = llstackptr;
+
+ do {
+ sym = llparsestack[ptr];
+
+ if(sym < 0) {
+ ptr--;
+ nomore = ptr == 0;
+ continue;
+ }
+
+ if(sym < llnterms) {
+ nomore = TRUE;
+ rval = sym == term;
+ continue;
+ }
+
+ pact = llfindaction(sym, term);
+
+ if(pact == 0) {
+ nomore = TRUE;
+ rval = FALSE;
+ continue;
+ }
+
+ if(llepsilon[pact] == TRUE) {
+ ptr--;
+ nomore = ptr == 0;
+ }
+ else {
+ nomore = TRUE;
+ }
+
+ } while(!nomore);
+
+ return(rval);
+}
+
+
+short llfindaction(sym, term)
+{
+ register index;
+
+ IFDEBUG(L)
+ printf("llfindaction(sym=%d, term=%d) enter \n", sym, term);
+ ENDDEBUG
+ index = llparseindex[sym];
+
+ while(llparsetable[index].llterm != 0) {
+ if(llparsetable[index].llterm == term) {
+ return(llparsetable[index].llprod);
+ }
+ index++;
+ }
+ return(0);
+}
+
+
+llparsererror(token)
+LLtoken *token;
+{
+ IFDEBUG(L)
+ fprintf(stderr,"llparsererror() enter\n");
+ prt_token(token);
+ ENDDEBUG
+
+ fprintf(stderr, "Syntax error: ");
+ prt_token(token);
+ dump_buffer();
+ Exit(-1);
+}
+
+
+llgettoken(token)
+LLtoken *token;
+{
+ llscan(token);
+ token->llstate = NORMAL;
+ IFDEBUG(L)
+ printf("llgettoken(): ");
+ prt_token(token);
+ ENDDEBUG
+}
+
+
+/******************************************************************************
+
+ Attribute support routines
+
+******************************************************************************/
+/*
+** attribute stack
+**
+** AttrStack = stack of record
+** values : array of values;
+** ptr : index;
+** end;
+**
+*/
+
+LLattrib llattributes[LLMAXATTR];
+int llattrtop = 0;
+
+struct llattr llattrdesc[LLMAXDESC];
+
+int lldescindex = 1;
+
+
+llsetattr(n)
+{
+ register struct llattr *ptr;
+
+ IFDEBUG(L)
+ printf("llsetattr(%d) enter\n",n);
+ ENDDEBUG
+ if(lldescindex >= LLMAXDESC) {
+ fprintf(stdout, "llattribute stack overflow: desc\n");
+ fprintf(stdout,
+ "lldescindex=0x%x, llattrtop=0x%x\n",lldescindex, llattrtop);
+ Exit(-1);
+ }
+ ptr = &llattrdesc[lldescindex];
+ ptr->llabase = &llattributes[llattrtop];
+ ptr->lloldtop = ++llattrtop;
+ ptr->llaindex = 1;
+ ptr->llacnt = n+1; /* the lhs ALWAYS uses an attr; it remains on the
+ stack when the production is recognized */
+ lldescindex++;
+}
+
+llpushattr(attr)
+LLattrib attr;
+{
+ struct llattr *a;
+
+ IFDEBUG(L)
+ printf("llpushattr() enter\n");
+ ENDDEBUG
+ if(llattrtop + 1 > LLMAXATTR) {
+ fprintf(stderr, "ATTRIBUTE STACK OVERFLOW!\n");
+ Exit(-1);
+ }
+ a = &llattrdesc[lldescindex-1];
+ llattributes[llattrtop++] = attr;
+ a->llaindex++; /* inc count of attrs on the stack for this prod */
+}
+
+llfinprod()
+{
+ IFDEBUG(L)
+ printf("llfinprod() enter\n");
+ ENDDEBUG
+ lldescindex--;
+ llattrtop = llattrdesc[lldescindex].lloldtop;
+ llattrdesc[lldescindex-1].llaindex++; /* lhs-of-prod.attr stays on
+ the stack; it is now one of the rhs attrs of the now-top production
+ on the stack */
+}
+
+#ifndef LINT
+#ifdef DEBUG
+dump_parse_stack()
+{
+ int ind;
+
+ printf("PARSE STACK:\n");
+ for(ind=llstackptr; ind>=0; ind--) {
+ printf("%d\t%d\t%s\n",
+ ind, llparsestack[ind],
+ llparsestack[ind]<0? "Action symbol" : llstrings[llparsestack[ind]]);
+ }
+}
+
+#endif DEBUG
+#endif LINT
+
+prt_token(t)
+LLtoken *t;
+{
+ fprintf(stdout, "t at 0x%x\n", t);
+ fprintf(stdout, "t->llterm=0x%x\n", t->llterm); (void) fflush(stdout);
+ fprintf(stdout, "TOK: %s\n", llstrings[t->llterm]);
+ (void) fflush(stdout);
+#ifdef LINT
+ /* to make lint shut up */
+ fprintf(stdout, "", llnterms, llnsyms, llnprods, llinfinite);
+#endif LINT
+}
diff --git a/sys/netiso/xebec/llparse.h b/sys/netiso/xebec/llparse.h
new file mode 100644
index 0000000..1b6133b
--- /dev/null
+++ b/sys/netiso/xebec/llparse.h
@@ -0,0 +1,145 @@
+/* $Header: llparse.h,v 2.1 88/09/19 12:56:20 nhall Exp $ */
+/* $Source: /var/home/tadl/src/argo/xebec/RCS/llparse.h,v $ */
+
+ /************************************************************
+ attributes stack garbage
+ ************************************************************/
+
+#define LLMAXATTR 512
+#define LLMAXDESC 256
+#define LLATTR /* build an attribute stack */
+
+ /*
+ ** attribute stack
+ **
+ ** AttrStack = stack of record
+ ** values : array of values;
+ ** ptr : index;
+ ** end;
+ **
+ */
+
+ typedef union llattrib LLattrib;
+
+ extern LLattrib llattributes[LLMAXATTR];
+ extern int llattrtop;
+
+ extern struct llattr {
+ LLattrib *llabase; /* ptr into the attr stack (llattributes) */
+ int llaindex;/* # attrs on the stack so far for this prod */
+ int llacnt;/* total # ever to go on for this prod */
+
+ int lloldtop;/* when popping this prod, restore stack to here ;
+ one attr will remain on the stack (for the lhs) */
+ } llattrdesc[LLMAXDESC];
+
+ extern int lldescindex;
+
+ /************************************************************
+ attributes stack garbage
+ ************************************************************/
+
+ extern struct lltoken {
+ short llterm; /* token number */
+ short llstate; /* inserted deleted normal */
+ LLattrib llattrib;
+ } lltoken;
+ typedef struct lltoken LLtoken;
+
+/************************************************************
+ constants used in llparse.c
+************************************************************/
+
+#define STACKSIZE 500
+#define MAXCORR 16
+
+#define NORMAL 0
+#define DELETE 1
+#define INSERT 2
+
+/************************************************************
+ datatypes used to communicate with the parser
+************************************************************/
+
+struct llinsert {
+ short llinscost;
+ short llinslength;
+ short llinsert[MAXCORR];
+};
+typedef struct llinsert LLinsert;
+
+extern short llparsestack[];
+extern short llstackptr;
+extern short llinfinite;
+
+/************************************************************
+ variables used to pass information
+ specific to each grammer
+************************************************************/
+
+extern short llnterms;
+extern short llnsyms;
+extern short llnprods;
+
+extern char *llefile;
+
+extern struct llparsetable {
+ short llterm;
+ short llprod;
+} llparsetable[];
+
+extern short llparseindex[];
+
+extern short llepsilon[];
+
+extern short llproductions[];
+
+extern struct llprodindex {
+ short llprodstart;
+ short llprodlength;
+ short llprodtlen;
+} llprodindex[];
+
+extern struct llcosts {
+ short llinsert;
+ short lldelete;
+} llcosts[];
+
+extern struct llstable {
+ short llsstart;
+ short llslength;
+} llstable[];
+
+extern short llsspace[];
+
+extern struct lletable {
+ short llecost;
+ short llelength;
+ short llestart;
+} lletable[];
+
+extern long lleindex[];
+
+extern short llespace[];
+
+extern char *llstrings[];
+
+/************************************************************
+ routines defined in llparse.c
+************************************************************/
+
+extern llparse();
+extern llcopye();
+extern llcopys();
+extern llcorrector();
+extern llepsilonok();
+extern llexpand();
+extern short llfindaction();
+extern llgetprefix();
+extern llgettoken();
+extern llinsert();
+extern llinsertsym();
+extern llinserttokens();
+extern llparsererror();
+extern llpushprod();
+extern llreadetab();
diff --git a/sys/netiso/xebec/llscan.c b/sys/netiso/xebec/llscan.c
new file mode 100644
index 0000000..ffdb9a9
--- /dev/null
+++ b/sys/netiso/xebec/llscan.c
@@ -0,0 +1,430 @@
+/* $Header: llscan.c,v 2.2 88/09/19 12:55:06 nhall Exp $ */
+/* $Source: /var/home/tadl/src/argo/xebec/RCS/llscan.c,v $ */
+/*
+ * ************************* NOTICE *******************************
+ * This code is in the public domain. It cannot be copyrighted.
+ * This scanner was originally written by Keith Thompson for the
+ * University of Wisconsin Crystal project.
+ * It was subsequently modified significantly by Nancy Hall at the
+ * University of Wisconsin for the ARGO project.
+ * ****************************************************************
+ */
+#include "xebec.h"
+#include "llparse.h"
+
+#include "main.h"
+#include <stdio.h>
+#include "procs.h"
+#include "debug.h"
+
+#define EOFILE 0x01
+#define UNUSED 0x02
+#define IGNORE 0x04
+#define OPCHAR 0x8
+#define DIGITS 0x10
+#define LETTER 0x20
+
+int chtype[128] = {
+/* null, soh ^a, stx ^b etx ^c eot ^d enq ^e ack ^f bel ^g */
+ EOFILE, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+/* bs ^h ht ^i lf ^j vt ^k ff ^l cr ^m so ^n si ^o */
+ UNUSED, IGNORE, IGNORE, UNUSED, IGNORE, IGNORE, UNUSED, UNUSED,
+/* dle ^p dc1 ^q dc2 ^r dc3 ^s dc4 ^t nak ^u syn ^v etb ^w */
+ UNUSED, UNUSED, UNUSED, UNUSED, EOFILE, UNUSED, UNUSED, UNUSED,
+/* can ^x em ^y sub ^z esc ^] fs ^\ gs ^} rs ^` us ^/ */
+ UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+
+/* ! " # $ % & ' */
+ IGNORE, UNUSED, OPCHAR, UNUSED, OPCHAR, UNUSED, OPCHAR, OPCHAR,
+/* ( ) * + , - . / */
+ OPCHAR, OPCHAR, OPCHAR, OPCHAR, OPCHAR, OPCHAR, OPCHAR, OPCHAR,
+/* 0 1 2 3 4 5 6 7 */
+ DIGITS, DIGITS, DIGITS, DIGITS, DIGITS, DIGITS, DIGITS, DIGITS,
+/* 8 9 : ; < = > ? */
+ DIGITS, DIGITS, OPCHAR, OPCHAR, OPCHAR, OPCHAR, OPCHAR, OPCHAR,
+
+/* @ A B C D E F G */
+ UNUSED, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+/* H I J K L M N O */
+ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+/* P Q R S T U V W */
+ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+/* X Y Z [ \ ] ^ _ */
+ LETTER, LETTER, LETTER, OPCHAR, UNUSED, OPCHAR, OPCHAR, LETTER,
+
+/* ` a b c d e f g */
+ UNUSED, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+/* h i j k l m n o */
+ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+/* p q r s t u v w */
+ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+/* x y z { | } ~ del */
+ LETTER, LETTER, LETTER, OPCHAR, UNUSED, OPCHAR, UNUSED, UNUSED
+};
+
+
+extern FILE *astringfile;
+static char *buffptr;
+static char buffer[2][LINELEN];
+static int currentbuf = 1;
+
+#define addbuf(x) *buffptr++ = x
+
+static int ch = ' ';
+
+skip()
+{
+ while((chtype[ch] == IGNORE) ) {
+ ch = getch();
+ }
+}
+
+llaccept(t)
+LLtoken *t;
+{
+ switch(t->llstate) {
+ case NORMAL:
+ break;
+ case INSERT:
+ fprintf(stderr,"Insert %s\n", llstrings[t->llterm]);
+ break;
+ case DELETE:
+ fprintf(stderr,"Delete %s\n", llstrings[t->llterm]);
+ break;
+ }
+}
+
+#define TVAL (t->llattrib)
+
+
+dump_buffer()
+{
+ register int i;
+ for(i=0; i<20; i++)
+ (void) fputc(buffer[currentbuf][i], stderr);
+ (void) fputc('\n', stderr);
+ (void) fflush(stderr);
+}
+
+int iskey(c, buf)
+char *c;
+char **buf;
+{
+ register int i;
+ static struct { char *key_word; int term_type; } keys[] = {
+ { "SAME", T_SAME },
+ { "DEFAULT", T_DEFAULT },
+ { "NULLACTION", T_NULLACTION },
+ { "STRUCT", T_STRUCT },
+ { "SYNONYM", T_SYNONYM },
+ { "TRANSITIONS", T_TRANSITIONS },
+ { "STATES", T_STATES },
+ { "EVENTS", T_EVENTS },
+ { "PCB", T_PCB },
+ { "INCLUDE", T_INCLUDE },
+ { "PROTOCOL", T_PROTOCOL },
+ { 0, 0},
+ };
+
+ for (i = 0; keys[i].key_word ; i++) {
+ if( !strcmp(c, (*buf = keys[i].key_word) ) ) {
+ return ( keys[i].term_type );
+ }
+ }
+ *buf = (char *)0;
+ return(0);
+}
+
+getstr(o,c)
+ /* c is the string delimiter
+ * allow the delimiter to be escaped
+ * the messy part: translate $ID to
+ * e->ev_union.ID
+ * where ID is an event with a non-zero obj_struc
+ * need we check for the field???
+ */
+char o,c;
+{
+ register int nested = 1;
+ register int allow_nesting = (o==c)?-1:1;
+
+ IFDEBUG(S)
+ fprintf(stdout,"getstr: ch=%c, delimiters %c %c\n",
+ ch,o, c);
+ fprintf(stdout,"getstr: buffptr 0x%x, currentbuf 0x%x\n",
+ buffptr, currentbuf);
+ ENDDEBUG
+
+ if( ch == c ) nested--;
+ while(nested) {
+ if(ch == '\0') {
+ fprintf(stderr,
+ "Eof inside of a string, delims= %c,%c, nesting %d",c,o, nested);
+ Exit(-1);
+ /* notreached */
+ } else if(ch == '$') {
+ /* might be an attribute */
+ IFDEBUG(S)
+ fprintf(stdout,"getstr: atttribute?\n");
+ ENDDEBUG
+
+ /* assume it's an event */
+ /* addbuf is a macro so this isn't as bad as
+ * it looks
+ * add "e->ev_union."
+ */
+ if( (ch = getch()) == '$' ) {
+ addbuf('e'); addbuf('-'); addbuf('>');
+ addbuf('e'); addbuf('v'); addbuf('_');
+ addbuf('u'); addbuf('n'); addbuf('i');
+ addbuf('o'); addbuf('n');
+ addbuf('.');
+ AddCurrentEventName(& buffptr);
+ } else {
+ char *obufp = buffptr;
+
+ do {
+ addbuf(ch);
+ ch = getch();
+ } while(chtype[ch] & LETTER);
+ addbuf('\0');
+ if( !strncmp(obufp, synonyms[PCB_SYN],
+ strlen(synonyms[PCB_SYN]) )) {
+ buffptr = obufp;
+ addbuf('p');
+ } else if( !strncmp(obufp, synonyms[EVENT_SYN],
+ strlen(synonyms[EVENT_SYN]))) {
+ buffptr = obufp;
+ addbuf('e');
+ } else {
+ fprintf(stderr, "Unknown synonym %s\n", obufp);
+ Exit(-1);
+ }
+ if(ch == '.') {
+ addbuf('-'); addbuf('>');
+ } else {
+ /* needs to be checked for nesting */
+ goto check;
+ }
+ }
+ /* end of attribute handling */
+ goto skip;
+ } else if(ch == '\\') {
+ /* possible escape - this is kludgy beyond belief:
+ * \ is used to escape open and closing delimiters
+ * and '$'
+ * otherwise it's passed through to be compiled by C
+ */
+ ch = getch();
+ if( (ch != o ) && (ch != c) && (ch != '$') ) {
+ /* may need to handle case where \ is last char in file... */
+ /* don't treat is as escape; not open or close so
+ * don't have to worry about nesting either
+ */
+ addbuf('\\');
+ }
+ }
+ addbuf(ch);
+ skip:
+ ch = getch();
+ check:
+ if( ch == o ) nested += allow_nesting;
+ else if( ch == c ) nested--;
+ if ( (buffptr - buffer[currentbuf]) > LINELEN) {
+ fprintf(stderr,
+ "%s too long.\n", (o=='{')?"Action":"Predicate"); /*}*/
+ fprintf(stderr,
+ "buffptr, currentbuf 0x%x, 0x%x\n",buffptr,currentbuf );
+ Exit(-1);
+ }
+ IFDEBUG(S)
+ fprintf(stdout,"loop in getstr: ch 0x%x,%c o=%c,c=%c nested=%d\n",
+ ch,ch,o,c,nested);
+ ENDDEBUG
+ }
+ addbuf(ch);
+ addbuf('\0');
+
+ IFDEBUG(S)
+ fprintf(stdout,"exit getstr: got %s\n", buffer[currentbuf]);
+ fprintf(stdout,"exit getstr: buffptr 0x%x, currentbuf 0x%x\n",
+ buffptr, currentbuf);
+ ENDDEBUG
+}
+
+getch()
+{
+ char c;
+ extern FILE *infile;
+ extern int lineno;
+
+ c = fgetc(infile) ;
+ if (c == '\n') lineno++;
+ if ((int)c == EOF) c = (char)0;
+ if (feof(infile)) c = (char) 0;
+ IFDEBUG(e)
+ fprintf(stdout, "getch: 0x%x\n", c);
+ (void) fputc( c, stdout);
+ fflush(stdout);
+ ENDDEBUG
+
+ return c;
+}
+
+llscan(t)
+LLtoken *t;
+{
+ char c;
+
+ t->llstate = NORMAL;
+
+ ++currentbuf;
+ currentbuf&=1;
+again:
+ buffptr = &buffer[currentbuf][0];
+
+ skip();
+
+ switch(chtype[ch]) {
+
+ case EOFILE:
+ t->llterm = T_ENDMARKER;
+ break;
+
+ case UNUSED:
+ fprintf(stderr, "Illegal character in input - 0x%x ignored.", ch);
+ ch = getch();
+ goto again;
+
+ case OPCHAR:
+
+ switch(ch) {
+
+ case '/':
+ /* possible comment : elide ; kludge */
+ IFDEBUG(S)
+ fprintf(stdout, "Comment ch=%c\n", ch);
+ ENDDEBUG
+ c = getch();
+ if (c != '*') {
+ fprintf(stderr,"Syntax error : character(0x%x) ignored", ch);
+ ch = c;
+ goto again;
+ } else {
+ register int state = 2, whatchar=0;
+ static int dfa[3][3] = {
+ /* done seen-star middle */
+ /* star */ { 0, 1, 1 },
+ /* / */ { 0, 0, 2 },
+ /* other */ { 0, 2, 2 }
+ };
+
+ while( state ) {
+ if( (c = getch()) == (char)0)
+ break;
+ whatchar = (c=='*')?0:(c=='/'?1:2);
+ IFDEBUG(S)
+ fprintf(stdout,
+ "comment: whatchar = %d, c = 0x%x,%c, oldstate=%d",
+ whatchar, c,c, state);
+ ENDDEBUG
+ state = dfa[whatchar][state];
+ IFDEBUG(S)
+ fprintf(stdout, ", newstate=%d\n", state);
+ ENDDEBUG
+ }
+ if(state) {
+ fprintf(stderr,
+ "Syntax error: end of file inside a comment");
+ Exit(-1);
+ } else ch = getch();
+ }
+ IFDEBUG(S)
+ fprintf(stdout, "end of comment at 0x%x,%c\n",ch,ch);
+ ENDDEBUG
+ goto again;
+
+
+ case '*':
+ t->llterm = T_STAR;
+ break;
+
+ case ',':
+ t->llterm = T_COMMA;
+ break;
+
+ case ';':
+ t->llterm = T_SEMI;
+ break;
+
+ case '<':
+ t->llterm = T_LANGLE;
+ break;
+
+ case '=':
+ t->llterm = T_EQUAL;
+ break;
+
+ case '[':
+ t->llterm = T_LBRACK;
+ break;
+
+ case ']':
+ t->llterm = T_RBRACK;
+ break;
+
+#ifdef T_FSTRING
+ case '"':
+ t->llterm = T_FSTRING;
+ addbuf(ch);
+ ch = getch();
+ getstr('"', '"');
+ TVAL.FSTRING.address = stash(buffer[currentbuf]);
+ break;
+#endif T_FSTRING
+
+ case '(':
+ t->llterm = T_PREDICATE;
+ getstr(ch, ')' );
+ TVAL.PREDICATE.address = buffer[currentbuf];
+ break;
+
+ case '{':
+ t->llterm = T_ACTION;
+ getstr(ch, '}');
+ TVAL.ACTION.address = buffer[currentbuf];
+ break;
+
+ default:
+ fprintf(stderr,"Syntax error : character(0x%x) ignored", ch);
+ ch = getch();
+ goto again;
+
+ }
+ ch = getch();
+ break;
+
+ case LETTER:
+ do {
+ addbuf(ch);
+ ch = getch();
+ } while(chtype[ch] & (LETTER | DIGITS));
+
+ addbuf('\0');
+
+ t->llterm = iskey(buffer[currentbuf], &TVAL.ID.address);
+ if(!t->llterm) {
+ t->llterm = T_ID;
+ TVAL.ID.address = buffer[currentbuf];
+ }
+ IFDEBUG(S)
+ fprintf(stdout, "llscan: id or keyword 0x%x, %s\n",
+ TVAL.ID.address, TVAL.ID.address);
+ ENDDEBUG
+ break;
+
+ default:
+ fprintf(stderr, "Snark in llscan: chtype=0x%x, ch=0x%x\n",
+ chtype[ch], ch);
+ }
+}
diff --git a/sys/netiso/xebec/main.c b/sys/netiso/xebec/main.c
new file mode 100644
index 0000000..a0b4842
--- /dev/null
+++ b/sys/netiso/xebec/main.c
@@ -0,0 +1,410 @@
+/* $Header: main.c,v 2.4 88/09/19 12:55:13 nhall Exp $ */
+/* $Source: /var/home/tadl/src/argo/xebec/RCS/main.c,v $ */
+/*
+ * TODO:
+ * rewrite the command line stuff altogether - it's kludged beyond
+ * belief (as is the rest of the code...)
+ *
+ * DISCLAIMER DISCLAIMER DISCLAIMER
+ * This code is such a kludge that I don't want to put my name on it.
+ * It was a ridiculously fast hack and needs rewriting.
+ * However it does work...
+ */
+
+#include <stdio.h>
+#include <strings.h>
+#include "malloc.h"
+#include "debug.h"
+#include "main.h"
+
+int debug[128];
+
+int lineno = 1;
+
+FILE *statefile, *actfile, *eventfile_h, *statevalfile;
+FILE *infile, *astringfile;
+char *Transfilename;
+char *astringfile_name = DEBUGFILE;
+char *actfile_name = ACTFILE;
+char *statefile_name = STATEFILE;
+char *statevalfile_name = STATEVALFILE;
+char *eventfile_h_name = EVENTFILE_H;
+int print_trans = 0;
+int print_protoerrs = 0;
+int pgoption = 0;
+char kerneldirname[50] = "\0";
+
+char protocol[50];
+
+char *synonyms[] = {
+ "EVENT",
+ "PCB",
+ 0
+};
+
+usage(a)
+char *a;
+{
+ fprintf(stderr,
+ "usage: %s <transition file> {-D<debug options>} <other options>\n",
+ a);
+ fprintf(stderr, "\t<other options> is any combination of:\n");
+ fprintf(stderr, "\t\t-A<action file name>\n");
+ fprintf(stderr, "\t\t-E<event file name>\n");
+ fprintf(stderr, "\t\t-S<state file name>\n");
+ fprintf(stderr, "\t\t-I<initial values file name>\n");
+ fprintf(stderr, "\t\t-X<debugging file name>\n");
+ fprintf(stderr, "\t\t-K<directory name>\n");
+ fprintf(stderr,
+ "\tThese names do NOT include the suffices (.c, .h)\n");
+ fprintf(stderr,
+ "\t\t-D<options> to turn on debug options for xebec itself\n");
+ fprintf(stderr, "\t-<nn> for levels of debugging output\n");
+ fprintf(stderr, "\t\t<nn> ranges from 1 to 3, 1 is default(everything)\n");
+ fprintf(stderr, "\t\t-T to print transitions\n");
+ fprintf(stderr, "\t\t-e to print list of combinations of\n");
+ fprintf(stderr, "\t\t\t [event,old_state] that produce protocol errors\n");
+ fprintf(stderr, "\t\t-g include profiling code in driver\n");
+ Exit(-1);
+}
+
+openfiles(proto)
+register char *proto;
+{
+ register char *junk;
+ register int lenp = strlen(proto);
+
+ IFDEBUG(b)
+ fprintf(OUT, "openfiles %s\n",proto);
+ ENDDEBUG
+
+#define HEADER Header
+#define SOURCE Source
+#define DOIT(X)\
+ /* GAG */\
+ junk = Malloc( 2 + lenp + strlen(X/**/_name) );\
+ (void) sprintf(junk, "%s_", proto);\
+ X/**/_name = strcat(junk, X/**/_name);\
+ X = fopen(X/**/_name, "w");\
+ if((X)==(FILE *)0)\
+ { fprintf(stderr,"Open failed: %s\n", "X"); Exit(-1); }\
+ fprintf(X, "/* %cHeader%c */\n",'$', '$' );\
+ fprintf(X, "/* %cSource%c */\n",'$', '$' );
+
+ DOIT(eventfile_h);
+
+ IFDEBUG(X)
+#ifdef DEBUG
+ DOIT(astringfile);
+#endif DEBUG
+ fprintf(astringfile,
+ "#ifndef _NFILE\n#include <stdio.h>\n#endif _NFILE\n" );
+ ENDDEBUG
+
+ DOIT(statevalfile);
+ DOIT(statefile);
+ DOIT(actfile);
+ fprintf(actfile,
+ "#ifndef lint\nstatic char *rcsid = \"$Header/**/$\";\n#endif lint\n");
+
+ if(pgoption)
+ putdriver(actfile, 15);
+ else
+ putdriver(actfile, 14);
+
+ FakeFilename(actfile, Transfilename, lineno);
+ putdriver(actfile, 1);
+ FakeFilename(actfile, Transfilename, lineno);
+ putdriver(actfile, 12);
+ fprintf(actfile, "#include \"%s%s\"\n", kerneldirname, statevalfile_name);
+ FakeFilename(actfile, Transfilename, lineno);
+ putdriver(actfile, 2);
+
+ initsets(eventfile_h, statefile);
+}
+
+includecode(file, f)
+FILE *file;
+register char *f;
+{
+ register int count=1;
+ static char o='{';
+ static char c='}';
+ register char *g;
+
+ IFDEBUG(a)
+ fprintf(stdout, "including: %s, f=0x%x", f,f);
+ ENDDEBUG
+ g = ++f;
+ while(count>0) {
+ if(*g == o) count++;
+ if(*g == c) count--;
+ g++;
+ }
+ *(--g) = '\0';
+ IFDEBUG(a)
+ fprintf(stdout, "derived: %s", f);
+ ENDDEBUG
+ fprintf(file, "%s", f);
+ FakeFilename(file, Transfilename, lineno);
+}
+
+putincludes()
+{
+ FakeFilename(actfile, Transfilename, lineno);
+ fprintf(actfile, "\n#include \"%s%s\"\n", kerneldirname, eventfile_h_name);
+ IFDEBUG(X)
+ if( !debug['K'] )
+ fprintf(actfile, "\n#include \"%s\"\n", astringfile_name);
+ /* not in kernel mode */
+ ENDDEBUG
+ FakeFilename(actfile, Transfilename, lineno);
+}
+
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ register int i = 2;
+ extern char *strcpy();
+ int start, finish;
+ extern int FirstEventAttribute;
+ extern int Nevents, Nstates;
+
+ start = time(0);
+ if(argc < 2) {
+ usage(argv[0]);
+ }
+ IFDEBUG(a)
+ fprintf(stdout, "infile = %s\n",argv[1]);
+ ENDDEBUG
+ Transfilename = argv[1];
+ infile = fopen(argv[1], "r");
+
+ if(argc > 2) while(i < argc) {
+ register int j=0;
+ char c;
+ char *name;
+
+ if(argv[i][j] == '-') j++;
+ switch(c = argv[i][j]) {
+
+ /* GROT */
+ case 'A':
+ name = &argv[i][++j];
+ actfile_name = Malloc( strlen(name)+4);
+ actfile_name = (char *)strcpy(actfile_name,name);
+#ifdef LINT
+ name =
+#endif LINT
+ strcat(actfile_name, ".c");
+ fprintf(stdout, "debugging file is %s\n",actfile_name);
+ break;
+ case 'K':
+ debug[c]=1;
+ fprintf(OUT, "option %c file %s\n",c, &argv[i][j+1]);
+ (void) strcpy(kerneldirname,&argv[i][++j]);
+ break;
+ case 'X':
+ debug[c]=1;
+ name = &argv[i][++j];
+ astringfile_name = Malloc( strlen(name)+4);
+ astringfile_name = (char *)strcpy(astringfile_name,name);
+#ifdef LINT
+ name =
+#endif LINT
+ strcat(astringfile_name, ".c");
+ fprintf(OUT, "option %c, astringfile name %s\n",c, name);
+ break;
+ case 'E':
+ name = &argv[i][++j];
+ eventfile_h_name = Malloc( strlen(name)+4);
+ eventfile_h_name = (char *)strcpy(eventfile_h_name,name);
+#ifdef LINT
+ name =
+#endif LINT
+ strcat(eventfile_h_name, ".h");
+ fprintf(stdout, "event files is %s\n",eventfile_h_name);
+ break;
+ case 'I':
+ name = &argv[i][++j];
+ statevalfile_name = Malloc( strlen(name)+4 );
+ statevalfile_name = (char *)strcpy(statevalfile_name,name);
+#ifdef LINT
+ name =
+#endif LINT
+ strcat(statevalfile_name, ".init");
+ fprintf(stdout, "state table initial values file is %s\n",statevalfile_name);
+ break;
+ case 'S':
+ name = &argv[i][++j];
+ statefile_name = Malloc( strlen(name)+4);
+ statefile_name = (char *)strcpy(statefile_name,name);
+#ifdef LINT
+ name =
+#endif LINT
+ strcat(statefile_name, ".h");
+ fprintf(stdout, "state file is %s\n",statefile_name);
+ break;
+ /* END GROT */
+ case '1':
+ case '2':
+ case '3':
+ debug['X']= (int)argv[i][j] - (int) '0';
+ fprintf(OUT, "value of debug['X'] is 0x%x,%d\n", debug['X'],
+ debug['X']);
+ break;
+ case 'D':
+ while( c = argv[i][++j] ) {
+ if(c == 'X') {
+ fprintf(OUT, "debugging on");
+ if(debug['X']) fprintf(OUT,
+ " - overrides any -%d flags used\n", debug['X']);
+ }
+ debug[c]=1;
+ fprintf(OUT, "debug %c\n",c);
+ }
+ break;
+ case 'g':
+ pgoption = 1;
+ fprintf(stdout, "Profiling\n");
+ break;
+ case 'e':
+ print_protoerrs = 1;
+ fprintf(stdout, "Protocol error table:\n");
+ break;
+
+ case 'T':
+ print_trans = 1;
+ fprintf(stdout, "Transitions:\n");
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ i++;
+ }
+ if(kerneldirname[0]) {
+ char *c;
+#ifdef notdef
+ if(debug['X']) {
+ fprintf(OUT, "Option K overrides option X\n");
+ debug['X'] = 0;
+ }
+#endif notdef
+ if(strlen(kerneldirname)<1) {
+ fprintf(OUT, "K option: dir name too short!\n");
+ exit(-1);
+ }
+ /* add ../name/ */
+ c = (char *) Malloc(strlen(kerneldirname)+6) ;
+ if(c <= (char *)0) {
+ fprintf(OUT, "Cannot allocate %d bytes for kerneldirname\n",
+ strlen(kerneldirname + 6) );
+ fprintf(OUT, "kerneldirname is %s\n", kerneldirname );
+ exit(-1);
+ }
+ *c = '.';
+ *(c+1) = '.';
+ *(c+2) = '/';
+ (void) strcat(c, kerneldirname);
+ (void) strcat(c, "/\0");
+ strcpy(kerneldirname, c);
+ }
+
+ init_alloc();
+
+ (void) llparse();
+
+ /* {{ */
+ if( !FirstEventAttribute )
+ fprintf(eventfile_h, "\t}ev_union;\n");
+ fprintf(eventfile_h, "};/* end struct event */\n");
+ fprintf(eventfile_h, "\n#define %s_NEVENTS 0x%x\n", protocol, Nevents);
+ fprintf(eventfile_h,
+ "\n#define ATTR(X)ev_union.%s/**/X/**/\n",EV_PREFIX);
+ (void) fclose(eventfile_h);
+
+ /* {{ */ fprintf(actfile, "\t}\nreturn 0;\n}\n"); /* end switch; end action() */
+ dump_predtable(actfile);
+
+ putdriver(actfile, 3);
+ IFDEBUG(X)
+ if(!debug['K'])
+ putdriver(actfile, 4);
+ ENDDEBUG
+ putdriver(actfile, 6);
+ IFDEBUG(X)
+ /*
+ putdriver(actfile, 10);
+ */
+ if(debug['K']) {
+ putdriver(actfile, 11);
+ } else {
+ switch(debug['X']) {
+ case 1:
+ default:
+ putdriver(actfile, 7);
+ break;
+ case 2:
+ putdriver(actfile, 13);
+ break;
+ case 3:
+ break;
+ }
+ }
+ ENDDEBUG
+ putdriver(actfile, 8);
+ (void) fclose(actfile);
+ IFDEBUG(X)
+ /* { */
+ fprintf(astringfile, "};\n");
+ (void) fclose(astringfile);
+ ENDDEBUG
+
+ (void) fclose(statevalfile);
+
+ fprintf(statefile, "\n#define %s_NSTATES 0x%x\n", protocol, Nstates);
+ (void) fclose(statefile);
+
+ finish = time(0);
+ fprintf(stdout, "%d seconds\n", finish - start);
+ if( print_protoerrs )
+ printprotoerrs();
+}
+
+int transno = 0;
+
+Exit(n)
+{
+ fprintf(stderr, "Error at line %d\n",lineno);
+ if(transno) fprintf(stderr, "Transition number %d\n",transno);
+ (void) fflush(stdout);
+ (void) fflush(statefile);
+ (void) fflush(eventfile_h);
+ (void) fflush(actfile);
+ exit(n);
+}
+
+syntax()
+{
+ static char *synt[] = {
+ "*PROTOCOL <string>\n",
+ "*PCB <string> <optional: SYNONYM synonymstring>\n",
+ "<optional: *INCLUDE {\n<C source>\n} >\n",
+ "*STATES <string>\n",
+ "*EVENTS <string>\n",
+ "*TRANSITIONS <string>\n",
+ };
+}
+
+FakeFilename(outfile, name, l)
+FILE *outfile;
+char *name;
+int l;
+{
+ /*
+ doesn't work
+ fprintf(outfile, "\n\n\n\n# line %d \"%s\"\n", l, name);
+ */
+}
diff --git a/sys/netiso/xebec/main.h b/sys/netiso/xebec/main.h
new file mode 100644
index 0000000..cb5bd74
--- /dev/null
+++ b/sys/netiso/xebec/main.h
@@ -0,0 +1,32 @@
+/* $Header: main.h,v 2.1 88/09/19 12:56:24 nhall Exp $ */
+/* $Source: /var/home/tadl/src/argo/xebec/RCS/main.h,v $ */
+
+#define TRUE 1
+#define FALSE 0
+#define LINELEN 2350
+ /* approx limit on token size for C compiler
+ * which matters for the purpose of debugging (astring.c...)
+ */
+
+#define MSIZE 4000
+#define DEBUGFILE "astring.c"
+#define ACTFILE "driver.c"
+#define EVENTFILE_H "events.h"
+#define STATEFILE "states.h"
+#define STATEVALFILE "states.init"
+
+#define EV_PREFIX "EV_"
+#define ST_PREFIX "ST_"
+
+#define PCBNAME "_PCB_"
+
+extern char kerneldirname[];
+extern char protocol[];
+extern char *synonyms[];
+#define EVENT_SYN 0
+#define PCB_SYN 1
+
+extern int transno;
+extern int print_trans;
+extern char *stash();
+
diff --git a/sys/netiso/xebec/malloc.c b/sys/netiso/xebec/malloc.c
new file mode 100644
index 0000000..5cdfc14
--- /dev/null
+++ b/sys/netiso/xebec/malloc.c
@@ -0,0 +1,136 @@
+/* $Header: malloc.c,v 2.2 88/09/19 12:55:18 nhall Exp $ */
+/* $Source: /var/home/tadl/src/argo/xebec/RCS/malloc.c,v $ */
+/*
+ * This code is such a kludge that I don't want to put my name on it.
+ * It was a ridiculously fast hack and needs rewriting.
+ * However it does work...
+ */
+
+/*
+ * a simple malloc
+ * it might be brain-damaged but for the purposes of xebec
+ * it's a whole lot faster than the c library malloc
+ */
+
+#include <stdio.h>
+#include "malloc.h"
+#include "debug.h"
+#define CHUNKSIZE 4096*2
+
+static char *hiwat, *highend;
+int bytesmalloced=0;
+int byteswasted = 0;
+
+
+init_alloc()
+{
+#ifdef LINT
+ hiwat = 0;
+ highend = 0;
+#else LINT
+ extern char *sbrk();
+
+ hiwat = (char *) sbrk(0);
+ hiwat = (char *)((unsigned)(hiwat + 3) & ~0x3);
+ highend = hiwat;
+#endif LINT
+}
+
+HIWAT(s)
+char *s;
+{
+ IFDEBUG(M)
+ fprintf(stdout, "HIWAT 0x%x %s\n", hiwat,s);
+ fflush(stdout);
+ ENDDEBUG
+}
+
+#define MIN(x,y) ((x<y)?x:y)
+
+char *Malloc(x)
+int x;
+{
+ char *c;
+ extern char *sbrk();
+ static int firsttime=1;
+ int total = x;
+ int first_iter = 1;
+ char *returnvalue;
+
+ IFDEBUG(N)
+ fprintf(stdout, "Malloc 0x%x, %d, bytesmalloced %d\n",
+ total,total, bytesmalloced);
+ fflush(stdout);
+ ENDDEBUG
+ IFDEBUG(M)
+ fprintf(stdout, "Malloc 0x%x, %d, hiwat 0x%x\n",
+ total,total, hiwat);
+ fflush(stdout);
+ ENDDEBUG
+ if(firsttime) {
+ hiwat = sbrk(0);
+ if(((unsigned)(hiwat) & 0x3)) {
+ bytesmalloced = 4 - (int) ((unsigned)(hiwat) & 0x3);
+ hiwat = sbrk( bytesmalloced );
+ } else
+ bytesmalloced = 0;
+ firsttime = 0;
+ highend = hiwat;
+ }
+ while( total ) {
+ x = MIN(CHUNKSIZE, total);
+ if(total != x) {
+ IFDEBUG(N)
+ fprintf(stdout, "BIG Malloc tot %d, x %d, left %d net %d\n",
+ total,x, total-x, bytesmalloced);
+ fflush(stdout);
+ ENDDEBUG
+ }
+ if ( (hiwat + x) > highend) {
+ c = sbrk(CHUNKSIZE);
+ IFDEBUG(M)
+ fprintf(stdout, "hiwat 0x%x, x 0x%x, highend 0x%x, c 0x%x\n",
+ hiwat, x, highend, c);
+ fflush(stdout);
+ ENDDEBUG
+ if( c == (char *) -1 ) {
+ fprintf(stderr, "Ran out of memory!\n");
+ Exit(-1);
+ }
+ if(first_iter) {
+ returnvalue = c;
+ first_iter = 0;
+ }
+ bytesmalloced += CHUNKSIZE;
+ IFDEBUG(m)
+ if (highend != c) {
+ fprintf(OUT, "warning: %d wasted bytes!\n", highend - hiwat);
+ fprintf(OUT, " chunksize 0x%x, x 0x%x \n", CHUNKSIZE, x);
+ }
+ ENDDEBUG
+ highend = c + CHUNKSIZE;
+ hiwat = c;
+ }
+ c = hiwat;
+ if(first_iter) {
+ returnvalue = c;
+ first_iter = 0;
+ }
+ hiwat += x;
+ total -= x;
+ }
+ if((unsigned)hiwat & 0x3) {
+ byteswasted += (int)((unsigned)(hiwat) & 0x3);
+ hiwat = (char *)((unsigned)(hiwat + 3) & ~0x3);
+ }
+ IFDEBUG(M)
+ fprintf(stdout, "Malloc = 0x%x, bytesm 0x%x, wasted 0x%x, hiwat 0x%x\n",
+ returnvalue, bytesmalloced, byteswasted, hiwat);
+ ENDDEBUG
+ IFDEBUG(N)
+ fprintf(stdout, "Malloc returns 0x%x, sbrk(0) 0x%x\n", returnvalue, sbrk(0));
+ fflush(stdout);
+ ENDDEBUG
+ return(returnvalue);
+}
+
diff --git a/sys/netiso/xebec/malloc.h b/sys/netiso/xebec/malloc.h
new file mode 100644
index 0000000..53d865b
--- /dev/null
+++ b/sys/netiso/xebec/malloc.h
@@ -0,0 +1,4 @@
+/* $Header: malloc.h,v 2.1 88/09/19 12:56:27 nhall Exp $ */
+/* $Source: /var/home/tadl/src/argo/xebec/RCS/malloc.h,v $ */
+
+char *Malloc();
diff --git a/sys/netiso/xebec/procs.c b/sys/netiso/xebec/procs.c
new file mode 100644
index 0000000..49d862a
--- /dev/null
+++ b/sys/netiso/xebec/procs.c
@@ -0,0 +1,437 @@
+/* $Header: procs.c,v 2.3 88/09/19 12:55:22 nhall Exp $ */
+/* $Source: /var/home/tadl/src/argo/xebec/RCS/procs.c,v $ */
+/*
+ * This code is such a kludge that I don't want to put my name on it.
+ * It was a ridiculously fast hack and needs rewriting.
+ * However it does work...
+ */
+
+#include <stdio.h>
+#include <strings.h>
+#include "malloc.h"
+#include "main.h"
+#include "debug.h"
+#include "sets.h"
+#include "procs.h"
+
+struct Predicate {
+ int p_index;
+ int p_transno;
+ char *p_str;
+ struct Predicate *p_next;
+};
+
+struct Stateent {
+ int s_index;
+ int s_newstate;
+ int s_action;
+ struct Stateent *s_next;
+};
+
+struct Object *SameState = (struct Object *)-1;
+int Index = 0;
+int Nstates = 0;
+int Nevents = 0;
+struct Predicate **Predlist;
+struct Stateent **Statelist;
+extern FILE *astringfile;
+
+end_events() {
+ int size, part;
+ char *addr;
+
+ IFDEBUG(X)
+ /* finish estring[], start astring[] */
+ if(debug['X'] < 2 )
+ fprintf(astringfile, "};\n\nchar *%s_astring[] = {\n\"NULLACTION\",\n",
+ protocol);
+ ENDDEBUG
+ /* NOSTRICT */
+ Statelist =
+ (struct Stateent **) Malloc((Nstates+1) * sizeof(struct Statent *));
+ /* NOSTRICT */
+ Predlist =
+ (struct Predicate **)
+ Malloc ( (((Nevents)<<Eventshift)+Nstates)*sizeof(struct Predicate *) );
+
+ size = (((Nevents)<<Eventshift)+Nstates)*sizeof(struct Predicate *) ;
+ addr = (char *)Predlist;
+ IFDEBUG(N)
+ fprintf(OUT, "Predlist at 0x%x, sbrk 0x%x bzero size %d at addr 0x%x\n",
+ Predlist, sbrk(0), size, addr);
+ ENDDEBUG
+#define BZSIZE 8192
+ while(size) {
+ part = size>BZSIZE?BZSIZE:size;
+ IFDEBUG(N)
+ fprintf(OUT, "bzero addr 0x%x part %d size %d\n",addr, part, size);
+ ENDDEBUG
+ bzero(addr, part);
+ IFDEBUG(N)
+ fprintf(OUT, "after bzero addr 0x%x part %d size %d\n",addr, part, size);
+ ENDDEBUG
+ addr += part;
+ size -= part;
+
+ }
+ IFDEBUG(N)
+ fprintf(OUT, "endevents..done \n");
+ ENDDEBUG
+}
+
+int acttable(f,actstring)
+char *actstring;
+FILE *f;
+{
+ static Actindex = 0;
+ extern FILE *astringfile;
+ extern int pgoption;
+
+ IFDEBUG(a)
+ fprintf(OUT,"acttable()\n");
+ ENDDEBUG
+ fprintf(f, "case 0x%x: \n", ++Actindex);
+
+ if(pgoption) {
+ fprintf(f, "asm(\" # dummy statement\");\n");
+ fprintf(f, "asm(\"_Xebec_action_%x: \");\n", Actindex );
+ fprintf(f, "asm(\".data\");\n");
+ fprintf(f, "asm(\".globl _Xebec_action_%x# X profiling\");\n",
+ Actindex );
+ fprintf(f, "asm(\".long 0 # X profiling\");\n");
+ fprintf(f, "asm(\".text # X profiling\");\n");
+ fprintf(f, "asm(\"cas r0,r15,r0 # X profiling\");\n");
+ fprintf(f, "asm(\"bali r15,mcount # X profiling\");\n");
+ }
+
+ fprintf(f, "\t\t%s\n\t\t break;\n", actstring);
+ IFDEBUG(X)
+ if(debug['X']<2) {
+ register int len = 0;
+ fputc('"',astringfile);
+ while(*actstring) {
+ if( *actstring == '\n' ) {
+ fputc('\\', astringfile);
+ len++;
+ fputc('n', astringfile);
+ } else if (*actstring == '\\') {
+ fputc('\\', astringfile);
+ len ++;
+ fputc('\\', astringfile);
+ } else if (*actstring == '\"') {
+ fputc('\\', astringfile);
+ len ++;
+ fputc('\"', astringfile);
+ } else fputc(*actstring, astringfile);
+ actstring++;
+ len++;
+ }
+ fprintf(astringfile,"\",\n");
+ if (len > LINELEN) {
+ fprintf(stderr, "Action too long: %d\n",len); Exit(-1);
+ }
+ }
+ ENDDEBUG
+
+ return(Actindex);
+}
+
+static int Npred=0, Ndefpred=0, Ntrans=0, Ndefevent=0, Nnulla=0;
+
+statetable(string, oldstate, newstate, action, event)
+char *string;
+int action;
+struct Object *oldstate, *newstate, *event;
+{
+ register int different;
+
+ IFDEBUG(a)
+ fprintf(OUT,"statetable(0x%x, 0x%x,0x%x, 0x%x)\n",
+ string, oldstate, newstate, action);
+ fprintf(OUT,"statetable(%s, %s,%s, 0x%x)\n",
+ string, oldstate->obj_name, newstate->obj_name, action);
+ ENDDEBUG
+
+ if( !action) Nnulla++;
+ if( newstate->obj_kind == OBJ_SET) {
+ fprintf(stderr, "Newstate cannot be a set\n");
+ Exit(-1);
+ }
+ different = (newstate != SameState);
+
+ (void) predtable( oldstate, event, string,
+ action, (newstate->obj_number) * different );
+ IFDEBUG(a)
+ fprintf(OUT,"EXIT statetable\n");
+ ENDDEBUG
+}
+
+stateentry(index, oldstate, newstate, action)
+int index, action;
+int oldstate, newstate;
+{
+ extern FILE *statevalfile;
+
+ IFDEBUG(a)
+ fprintf(OUT,"stateentry(0x%x,0x%x,0x%x,0x%x) Statelist@0x%x, val 0x%x\n",
+ index, oldstate, newstate,action, &Statelist, Statelist);
+ ENDDEBUG
+
+
+ fprintf(statevalfile, "{0x%x,0x%x},\n", newstate, action);
+}
+
+int predtable(os, oe, str, action, newstate)
+struct Object *os, *oe;
+char *str;
+int action, newstate;
+{
+ register struct Predicate *p, **q;
+ register int event, state;
+ register struct Object *e, *s;
+ struct Object *firste;
+
+ if (oe == (struct Object *)0 ) {
+ Ndefevent ++;
+ fprintf(stderr, "DEFAULT EVENTS aren't implemented; trans ignored\n");
+ return;
+ }
+ Ntrans++;
+ IFDEBUG(g)
+ fprintf(stdout,
+ "PREDTAB: s %5s; e %5s\n", os->obj_kind==OBJ_SET?"SET":"item",
+ oe->obj_kind==OBJ_SET?"SET":"item");
+ ENDDEBUG
+ if (os->obj_kind == OBJ_SET) s = os->obj_members;
+ else s = os;
+ if (oe->obj_kind == OBJ_SET) firste = oe->obj_members;
+ else firste = oe;
+ if(newstate) {
+ fprintf(statevalfile, "{0x%x,0x%x},\n",newstate, action);
+ Index++;
+ }
+ while (s) {
+ if( !newstate ) { /* !newstate --> SAME */
+ /* i.e., use old obj_number */
+ fprintf(statevalfile, "{0x%x,0x%x},\n",s->obj_number, action);
+ Index++;
+ }
+ e = firste;
+ while (e) {
+ event = e->obj_number; state = s->obj_number;
+ IFDEBUG(g)
+ fprintf(stdout,"pred table event=0x%x, state 0x%x\n",
+ event, state);
+ fflush(stdout);
+ ENDDEBUG
+ if( !str /* DEFAULT PREDICATE */) {
+ Ndefpred++;
+ IFDEBUG(g)
+ fprintf(stdout,
+ "DEFAULT pred state 0x%x, event 0x%x, Index 0x%x\n",
+ state, event, Index);
+ fflush(stdout);
+ ENDDEBUG
+ } else
+ Npred++;
+ /* put at END of list */
+#ifndef LINT
+ IFDEBUG(g)
+ fprintf(stdout,
+ "predicate for event 0x%x, state 0x%x is 0x%x, %s\n",
+ event, state, Index, str);
+ fflush(stdout);
+ ENDDEBUG
+#endif LINT
+ for( ((q = &Predlist[(event<<Eventshift)+state]),
+ (p = Predlist[(event<<Eventshift)+state]));
+ p ; p = p->p_next ) {
+ q = &p->p_next;
+ }
+
+ p = (struct Predicate *)Malloc(sizeof(struct Predicate));
+ p->p_next = (struct Predicate *)0;
+ p->p_str = str;
+ p->p_index = Index;
+ p->p_transno = transno;
+ *q = p;
+
+ IFDEBUG(g)
+ fprintf(stdout,
+ "predtable index 0x%x, transno %d, E 0x%x, S 0x%x\n",
+ Index, transno, e, s);
+ ENDDEBUG
+
+ e = e->obj_members;
+ }
+ s = s->obj_members;
+ }
+ return Index ;
+}
+
+printprotoerrs()
+{
+ register int e,s;
+
+ fprintf(stderr, "[ Event, State ] without any transitions :\n");
+ for(e = 0; e < Nevents; e++) {
+ fprintf(stderr, "Event 0x%x: states ", e);
+ for(s = 0; s < Nstates; s++) {
+ if( Predlist[(e<<Eventshift)+s] == 0 )
+ fprintf(stderr, "0x%x ", s);
+ }
+ fprintf(stderr, "\n");
+ }
+}
+
+#ifndef LINT
+dump_predtable(f)
+FILE *f;
+{
+ struct Predicate *p;
+ register int e,s, hadapred;
+ int defaultindex;
+ int defaultItrans;
+ extern int bytesmalloced;
+ extern int byteswasted;
+
+#ifdef notdef
+ fprintf(stdout,
+ " Xebec used %8d bytes of storage, wasted %8d bytes\n",
+ bytesmalloced, byteswasted);
+#endif notdef
+ fprintf(stdout,
+ " %8d states\n %8d events\n %8d transitions\n",
+ Nstates, Nevents, Ntrans);
+ fprintf(stdout,
+ " %8d predicates\n %8d default predicates used\n",
+ Npred, Ndefpred);
+ fprintf(stdout,
+ " %8d null actions\n",
+ Nnulla);
+
+ putdriver(f, 5);
+ for(e = 0; e < Nevents; e++) { for(s = 0; s < Nstates; s++) {
+ p = Predlist[(e<<Eventshift)+s];
+ hadapred=0;
+ defaultindex=0;
+ defaultItrans=0;
+ if(p) {
+ IFDEBUG(d)
+ fflush(f);
+ ENDDEBUG
+ while(p) {
+ if(p->p_str) {
+ if(!hadapred)
+ fprintf(f, "case 0x%x:\n\t", (e<<Eventshift) + s);
+ hadapred = 1;
+ fprintf(f, "if %s return 0x%x;\n\t else ",
+ p->p_str, p->p_index);
+ } else {
+ if(defaultindex) {
+ fprintf(stderr,
+"\nConflict between transitions %d and %d: duplicate default \n",
+ p->p_transno, defaultItrans);
+ Exit(-1);
+ }
+ defaultindex = p->p_index;
+ defaultItrans = p->p_transno;
+ }
+ p = p->p_next;
+ }
+ if( hadapred) {
+ fprintf(f, "return 0x%x;\n", defaultindex);
+ }
+ IFDEBUG(d)
+ fflush(f);
+ ENDDEBUG
+ }
+ IFDEBUG(g)
+ fprintf(stdout,
+ "loop: e 0x%x s 0x%x hadapred 0x%x dindex 0x%x for trans 0x%x\n",
+ e, s, hadapred, defaultindex, defaultItrans);
+ ENDDEBUG
+ if ( hadapred ) {
+ /* put a -1 in the array - Predlist is temporary storage */
+ Predlist[(e<<Eventshift)+s] = (struct Predicate *)(-1);
+ } else {
+ /* put defaultindex in the array */
+ /* if defaultindex is zero, then the driver will
+ * cause an erroraction (same as if no default
+ * were given and none of the predicates were true;
+ * also same as if no preds or defaults were given
+ * for this combo)
+ */
+ Predlist[(e<<Eventshift)+s] = (struct Predicate *)(defaultindex);
+ }
+ } }
+ fprintf(f, "default: return 0;\n} /* end switch */\n");
+#ifdef notdef
+ fprintf(f, "/*NOTREACHED*/return 0;\n} /* _Xebec_index() */\n");
+#else notdef
+ fprintf(f, "} /* _Xebec_index() */\n");
+#endif notdef
+ fprintf(f, "static int inx[%d][%d] = { {", Nevents+1,Nstates);
+ for(s = 0; s< Nstates; s++) fprintf(f, "0,"); /* event 0 */
+ fprintf(f, "},\n");
+
+ for(e = 0; e < Nevents; e++) {
+ fprintf(f, " {");
+ for(s = 0; s < Nstates; s++) {
+ register struct Predicate *xyz = Predlist[(e<<Eventshift)+s];
+ /* this kludge is to avoid a lint msg. concerning
+ * loss of bits
+ */
+ if (xyz == (struct Predicate *)(-1))
+ fprintf(f, "-1,");
+ else
+ fprintf(f, "0x%x,", Predlist[(e<<Eventshift)+s]);
+ }
+ fprintf(f, " },\n");
+ }
+ fprintf(f, "};");
+}
+#endif LINT
+
+char *
+stash(buf)
+char *buf;
+{
+ register int len;
+ register char *c;
+
+ /* grot */
+ len = strlen(buf);
+ c = Malloc(len+1);
+#ifdef LINT
+ c =
+#endif LINT
+ strcpy(c, buf);
+
+ IFDEBUG(z)
+ fprintf(stdout,"stash %s at 0x%x\n", c,c);
+ ENDDEBUG
+ return(c);
+}
+
+#ifdef notdef
+dump_pentry(event,state)
+int event,state;
+{
+ register struct Predicate *p, **q;
+
+ for(
+ ((q = &Predlist[(event<<Eventshift) +state]),
+ (p = Predlist[(event<<Eventshift) + state]));
+ p!= (struct Predicate *)0 ; p = p->p_next ) {
+#ifndef LINT
+ IFDEBUG(a)
+ fprintf(OUT,
+ "dump_pentry for event 0x%x, state 0x%x is 0x%x\n",
+ event, state, p);
+ ENDDEBUG
+#endif LINT
+ q = &p->p_next;
+ }
+}
+#endif notdef
diff --git a/sys/netiso/xebec/procs.h b/sys/netiso/xebec/procs.h
new file mode 100644
index 0000000..e41ae75
--- /dev/null
+++ b/sys/netiso/xebec/procs.h
@@ -0,0 +1,5 @@
+/* $Header: procs.h,v 2.1 88/09/19 12:56:30 nhall Exp $ */
+/* $Source: /var/home/tadl/src/argo/xebec/RCS/procs.h,v $ */
+
+extern char *stash();
+extern struct Object *SameState;
diff --git a/sys/netiso/xebec/putdriver.c b/sys/netiso/xebec/putdriver.c
new file mode 100644
index 0000000..996ac64
--- /dev/null
+++ b/sys/netiso/xebec/putdriver.c
@@ -0,0 +1,244 @@
+/* $Header: putdriver.c,v 2.2 88/09/19 12:55:27 nhall Exp $ */
+/* $Source: /var/home/tadl/src/argo/xebec/RCS/putdriver.c,v $ */
+
+/*
+ * This code is such a kludge that I don't want to put my name on it.
+ * It was a ridiculously fast hack and needs rewriting.
+ * However it does work...
+ */
+
+/* The original idea was to put all the driver code
+ * in one place so it would be easy to modify
+ * but as hacks got thrown in it got worse and worse...
+ * It's to the point where a user would be better off
+ * writing his own driver and xebec should JUST produce
+ * the tables.
+ */
+
+#include <stdio.h>
+#include "main.h"
+#include "debug.h"
+
+extern char protocol[];
+char Eventshiftstring[10];
+static char statename[] = {'_', 's', 't', 'a', 't', 'e', 0 };
+
+static char *strings[] = {
+
+#define PART1 { 0,3 }
+
+ "\n#include \"",
+ kerneldirname,
+ protocol,
+ "_states.h\"",
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+
+#define PART12 { 10,12 }
+ "\n\nstatic struct act_ent {\n",
+ "\tint a_newstate;\n\tint a_action;\n",
+ "} statetable[] = { {0,0},\n",
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+
+#define PART2 { 20,20 }
+ "};\n",
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+
+#define PART3 { 30,41 }
+ "\n",
+ protocol,
+ "_driver(p, e)\nregister ",
+ protocol,
+ PCBNAME,
+ " *p;\nregister struct ",
+ protocol,
+ "_event *e;\n",
+ "{\n",
+ "\tregister int index, error=0;\n",
+ "\tstruct act_ent *a;\n",
+ "\tstatic struct act_ent erroraction = {0,-1};\n",
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+
+#define PART4 { 50,54 }
+
+ "\textern int ",
+ protocol,
+ "_debug;\n\textern FILE *",
+ protocol,
+ "_astringfile;\n",
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+
+#define PART6 { 60, 65 }
+ "\n\tindex = inx[1 + e->ev_number][p->",
+ protocol,
+ statename,
+ "];\n\tif(index<0) index=_Xebec_index(e, p);\n",
+ "\tif (index==0) {\n\t\ta = &erroraction;\n",
+ "\t} else\n\t\ta = &statetable[index];\n\n",
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+
+#define PART7 {70, 77 }
+ "\tif(",
+ protocol,
+ "_debug) fprintf(",
+ protocol,
+ "_astringfile, \"%15s <-- %15s [%15s] \\n\\t%s\\n\",\n",
+ "\t\tsstring[a->a_newstate], sstring[p->",
+ protocol,
+ "_state], estring[e->ev_number], astring[a->a_action]);\n\n",
+ (char *)0,
+ (char *)0,
+
+#define PART8 { 80, 84 }
+ "\tif(a->a_action)\n",
+ "\t\terror = _Xebec_action( a->a_action, e, p );\n",
+ "\tif(error==0)\n\tp->",
+ protocol,
+ "_state = a->a_newstate;\n\treturn error;\n}\n",
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+
+#define PART9 { 90, 99 }
+ "\n_XEBEC_PG int _Xebec_action(a,e,p)\nint a;\nstruct ",
+ protocol,
+ "_event *e;\n",
+ protocol,
+ PCBNAME,
+ " *p;\n{\n",
+ "switch(a) {\n",
+ "case -1: return ",
+ protocol,
+ "_protocol_error(e,p);\n",
+ (char *)0,
+
+#define PART10 { 101, 105 }
+ "\tif(",
+ protocol,
+ "_debug) fprintf(",
+ protocol,
+ "_astringfile, \"index 0x%5x\\n\", index);\n",
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+
+#define PART5 { 110, 121 }
+ "\n_XEBEC_PG int\n_Xebec_index( e,p )\n",
+ "\tstruct ",
+ protocol,
+ "_event *e;\n\t",
+ protocol,
+ PCBNAME,
+ " *p;\n{\nswitch( (e->ev_number<<",
+ Eventshiftstring,
+ ")+(p->",
+ protocol,
+ statename,
+ ") ) {\n",
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+ (char *)0,
+
+#define PART11 {130, 137 }
+ "\tIFTRACE(D_DRIVER)\n",
+ "\t",
+ protocol,
+ "trace(DRIVERTRACE,",
+ "\t\ta->a_newstate, p->",
+ protocol,
+ "_state, e->ev_number, a->a_action, 0);\n\n",
+ "\tENDTRACE\n",
+ (char *)0,
+ (char *)0,
+
+#define PART13 {140, 147 }
+ "\tif(",
+ protocol,
+ "_debug) fprintf(",
+ protocol,
+ "_astringfile, \"%15s <-- %15s [%15s] \\n\",\n",
+ "\t\tsstring[a->a_newstate], sstring[p->",
+ protocol,
+ "_state], estring[e->ev_number]);\n\n",
+ (char *)0,
+ (char *)0,
+
+#define PART14 { 150,150 }
+ "#define _XEBEC_PG static\n",
+
+#define PART15 { 151,151 }
+ "#define _XEBEC_PG \n",
+
+};
+
+static struct { int start; int finish; } parts[] = {
+ { 0,0 },
+ PART1,
+ PART2,
+ PART3,
+ PART4,
+ PART5,
+ PART6,
+ PART7,
+ PART8,
+ PART9,
+ PART10,
+ PART11,
+ PART12,
+ PART13,
+ PART14,
+ PART15,
+};
+
+putdriver(f, x)
+FILE *f;
+int x;
+{
+ register int i;
+
+ for( i = parts[x].start; i<= parts[x].finish; i++)
+ fprintf(f, "%s", strings[i]);
+ IFDEBUG(d)
+ fflush(f);
+ ENDDEBUG
+}
diff --git a/sys/netiso/xebec/sets.c b/sys/netiso/xebec/sets.c
new file mode 100644
index 0000000..3bb74ed
--- /dev/null
+++ b/sys/netiso/xebec/sets.c
@@ -0,0 +1,472 @@
+/* $Header: sets.c,v 2.3 88/09/19 12:55:30 nhall Exp $ */
+/* $Source: /var/home/tadl/src/argo/xebec/RCS/sets.c,v $ */
+/*
+ * This code is such a kludge that I don't want to put my name on it.
+ * It was a ridiculously fast hack and needs rewriting.
+ * However it does work...
+ */
+#include "main.h"
+#include "malloc.h"
+#include "sets.h"
+#include "debug.h"
+#include <stdio.h>
+
+struct Object *CurrentEvent = (struct Object *)0;
+struct Object *Objtree;
+struct Object dummy;
+/*
+ * define a set w/ type and name
+ * return a set number
+ */
+#undef NULL
+#define NULL (struct Object *)0
+
+static FILE *Sfile, *Efile;
+extern FILE *astringfile;
+char *Noname = "Unnamed set\0";
+
+initsets(f,s)
+FILE *f, *s;
+{
+ static char errorstring[20];
+ extern struct Object *SameState;
+ Efile = f;
+ Sfile = s;
+
+ IFDEBUG(X)
+ fprintf(astringfile, "char *%s_sstring[] = {\n", protocol);
+ ENDDEBUG
+ sprintf(errorstring, "%sERROR\0", ST_PREFIX);
+ defineitem(STATESET, errorstring, (char *)0); /* state 0 */
+ SameState = (struct Object *) Malloc( sizeof (struct Object) );
+ SameState->obj_kind = OBJ_ITEM;
+ SameState->obj_type = STATESET;
+ SameState->obj_name = "SAME";
+ SameState->obj_struc = (char *)0;
+ SameState->obj_number = 0;
+ SameState->obj_members = (struct Object *)0;
+ SameState->obj_left = (struct Object *)0;
+ SameState->obj_right = (struct Object *)0;
+ SameState->obj_parent = (struct Object *)0;
+}
+
+/*
+ * get a set based on its type and name
+ * returns address of an Object, may be set or item
+ */
+
+struct Object *lookup(type, name)
+unsigned char type;
+char *name;
+{
+ register struct Object *p = Objtree;
+ int val = 1 ;
+
+ IFDEBUG(o)
+ fprintf(stdout,"lookup 0x%x,%s \n",
+ type, name);
+ ENDDEBUG
+
+ while( p && val ) {
+ IFDEBUG(o)
+ fprintf(OUT, "lookup strcmp 0x%x,%s, 0x%x,%s\n",
+ name, name, OBJ_NAME(p), OBJ_NAME(p));
+ ENDDEBUG
+ if( p->obj_name == (char *)0 ) {
+ fprintf(stderr, "Unnamed set in table!\n");
+ Exit(-1);
+ }
+ val = (int) strcmp(name, OBJ_NAME(p));
+ if(val < 0) {
+ /* left */
+ p = p->obj_left;
+ } else if (val > 0) {
+ /* right */
+ p = p->obj_right;
+ }
+ }
+ if( p && ( p->obj_type != type)) {
+ fprintf(stdout, "lookup(0x%x,%s) found wrong obj type 0x%x\n",
+ type,name, p->obj_type);
+ p = NULL;
+ }
+ IFDEBUG(o)
+ fprintf(stdout,"lookup 0x%x,%s returning 0x%x\n",type, name, p);
+ ENDDEBUG
+ return(p);
+}
+
+static int states_done = 0;
+
+end_states(f)
+FILE *f;
+{
+ register unsigned n = Nstates;
+ register int i;
+ extern char Eventshiftstring[];
+
+ states_done = 1;
+
+ for( i = 0; ;i++) {
+ if( (n >>= 1) <= 0 ) break;
+ }
+ Eventshift = i+1;
+ IFDEBUG(d)
+ fprintf(OUT, "Eventshift=%d\n", Eventshift);
+ ENDDEBUG
+ sprintf(Eventshiftstring, "%d\0",Eventshift);
+ fprintf(f, "struct %s_event {\n\tint ev_number;\n", &protocol[0]);
+ IFDEBUG(X)
+ /* finish sstring[] & start estring[] */
+ fprintf(astringfile,
+ "};\n\nchar *%s_estring[] = {\n", protocol);
+ ENDDEBUG
+}
+
+int FirstEventAttribute = 1;
+
+static
+insert(o)
+struct Object *o;
+{
+ struct Object *p = Objtree;
+ struct Object **q = &Objtree;
+ int val=1;
+
+
+ if (o->obj_name == (char *)0) {
+ fprintf(stderr, "Internal Error: inserting unnamed object\n");
+ Exit(-1);
+ }
+ if( o->obj_type == STATESET) {
+ if( states_done ) {
+ fprintf(stderr, "No states may be defined after *TRANSITIONS\n");
+ Exit(-1);
+ }
+ o->obj_number = Nstates++ ;
+ if(Nstates > MAXSTATES) {
+ fprintf(stderr, "Too many states\n");
+ Exit(-1);
+ }
+ fprintf(Sfile, "#define %s 0x%x\n", o->obj_name, o->obj_number);
+ IFDEBUG(X)
+ fprintf(astringfile, "\"%s(0x%x)\",\n", o->obj_name, o->obj_number);
+ ENDDEBUG
+ } else {
+ /* EVENTSET */
+ if( ! states_done ) {
+ fprintf(stderr, "states must precede events\n");
+ Exit(-1);
+ }
+ o->obj_number = Nevents++ ;
+ if(Nevents > MAXEVENTS) {
+ fprintf(stderr, "Too many events\n");
+ Exit(-1);
+ }
+ if(o->obj_struc) {
+ if( FirstEventAttribute ) {
+ fprintf(Efile, "\n\tunion{\n"); /*} */
+ FirstEventAttribute = 0;
+ }
+ fprintf(Efile,
+ "struct %s %s%s;\n\n", o->obj_struc, EV_PREFIX, o->obj_name);
+ }
+ fprintf(Efile, "#define %s 0x%x\n", o->obj_name, o->obj_number);
+ IFDEBUG(X)
+ fprintf(astringfile, "\"%s(0x%x)\",\n", o->obj_name, o->obj_number);
+ ENDDEBUG
+ }
+ IFDEBUG(o)
+ fprintf(OUT, "insert(%s)\n", OBJ_NAME(o) );
+ if(o->obj_right != NULL) {
+ fprintf(OUT, "insert: unclean Object right\n");
+ exit(-1);
+ }
+ if(o->obj_left != NULL) {
+ fprintf(OUT, "insert: unclean Object left\n");
+ exit(-1);
+ }
+ fflush(OUT);
+ ENDDEBUG
+
+ while( val ) {
+ if(p == NULL) {
+ *q = o;
+ o->obj_parent = (struct Object *)q;
+ break;
+ }
+ if(!(val = strcmp(o->obj_name, p->obj_name)) ) {
+ /* equal */
+ fprintf(stderr, "re-inserting %s\n",o->obj_name);
+ exit(-1);
+ }
+ if(val < 0) {
+ /* left */
+ q = &p->obj_left;
+ p = p->obj_left;
+ } else {
+ /* right */
+ q = &p->obj_right;
+ p = p->obj_right;
+ }
+ }
+ IFDEBUG(a)
+ dumptree(Objtree,0);
+ ENDDEBUG
+}
+
+delete(o)
+struct Object *o;
+{
+ register struct Object *p = o->obj_right;
+ register struct Object *q;
+ register struct Object *newparent;
+ register struct Object **np_childlink;
+
+ IFDEBUG(T)
+ fprintf(stdout, "delete(0x%x)\n", o);
+ dumptree(Objtree,0);
+ ENDDEBUG
+
+ /* q <== lowest valued node of the right subtree */
+ while( p ) {
+ q = p;
+ p = p->obj_left;
+ }
+
+ if (o->obj_parent == (struct Object *)&Objtree) {
+ newparent = (struct Object *)&Objtree;
+ np_childlink = (struct Object **)&Objtree;
+ } else if(o->obj_parent->obj_left == o) {
+ newparent = o->obj_parent;
+ np_childlink = &(o->obj_parent->obj_left);
+ } else {
+ newparent = o->obj_parent;
+ np_childlink = &(o->obj_parent->obj_right);
+ }
+ IFDEBUG(T)
+ fprintf(OUT, "newparent=0x%x\n");
+ ENDDEBUG
+
+ if (q) { /* q gets the left, parent gets the right */
+ IFDEBUG(T)
+ fprintf(OUT, "delete: q null\n");
+ ENDDEBUG
+ q->obj_left = p;
+ if(p) p->obj_parent = q;
+ p = o->obj_right;
+ } else { /* parent(instead of q) gets the left ; there is no right */
+ IFDEBUG(T)
+ fprintf(OUT, "delete: q not null\n");
+ ENDDEBUG
+ p = o->obj_left;
+ }
+ *np_childlink = p;
+ if(p)
+ p->obj_parent = newparent;
+
+ IFDEBUG(T)
+ fprintf(OUT, "After deleting 0x%x\n",o);
+ dumptree(Objtree,0);
+ ENDDEBUG
+}
+
+struct Object *
+defineset(type, adr, keep)
+unsigned char type;
+char *adr;
+int keep;
+{
+ struct Object *onew;
+ IFDEBUG(o)
+ printf("defineset(0x%x,%s, %s)\n", type , adr, keep?"KEEP":"NO_KEEP");
+ ENDDEBUG
+
+ onew = (struct Object *)Malloc(sizeof (struct Object));
+ bzero(onew, sizeof(struct Object));
+ onew->obj_name = adr;
+ onew->obj_kind = OBJ_SET;
+ onew->obj_type = type;
+ if(keep)
+ insert( onew );
+ /* address already stashed before calling defineset */
+ IFDEBUG(o)
+ printf("defineset(0x%x,%s) returning 0x%x\n", type , adr, onew);
+ dumptree(Objtree,0);
+ ENDDEBUG
+ return(onew);
+}
+
+dumpit(o, s)
+char *o;
+char *s;
+{
+ register int i;
+
+IFDEBUG(o)
+ fprintf(OUT, "object 0x%x, %s\n",o, s);
+ for(i=0; i< sizeof(struct Object); i+=4) {
+ fprintf(OUT, "0x%x: 0x%x 0x%x 0x%x 0x%x\n",
+ *((int *)o), *o, *(o+1), *(o+2), *(o+3) );
+ }
+ENDDEBUG
+}
+
+defineitem(type, adr, struc)
+unsigned char type;
+char *adr;
+char *struc;
+{
+ struct Object *onew;
+ IFDEBUG(o)
+ printf("defineitem(0x%x, %s at 0x%x, %s)\n", type, adr, adr, struc);
+ ENDDEBUG
+
+ if( onew = lookup( type, adr ) ) {
+ fprintf(stderr,
+ "Internal error at defineitem: trying to redefine obj type 0x%x, adr %s\n",
+ type, adr);
+ exit(-1);
+ } else {
+ onew = (struct Object *)Malloc(sizeof (struct Object));
+ bzero(onew, sizeof(struct Object));
+ onew->obj_name = stash(adr);
+ onew->obj_kind = OBJ_ITEM;
+ onew->obj_type = type;
+ onew->obj_struc = struc?stash(struc):struc;
+ insert( onew );
+ }
+ IFDEBUG(o)
+ fprintf(OUT, "defineitem(0x%x, %s) returning 0x%x\n", type, adr, onew);
+ ENDDEBUG
+}
+
+member(o, adr)
+struct Object *o;
+char *adr;
+{
+ struct Object *onew, *oold;
+ IFDEBUG(o)
+ printf("member(0x%x, %s)\n", o, adr);
+ ENDDEBUG
+
+ oold = lookup( o->obj_type, adr );
+
+ onew = (struct Object *)Malloc(sizeof (struct Object));
+ if( oold == NULL ) {
+ extern int lineno;
+
+ fprintf(stderr,
+ "Warning at line %d: set definition of %s causes definition of\n",
+ lineno, OBJ_NAME(o));
+ fprintf(stderr, "\t (previously undefined) member %s\n", adr);
+ bzero(onew, sizeof(struct Object));
+ onew->obj_name = stash(adr);
+ onew->obj_kind = OBJ_ITEM;
+ onew->obj_type = o->obj_type;
+ onew->obj_members = NULL;
+ insert( onew );
+ } else {
+ if(oold->obj_kind != OBJ_ITEM) {
+ fprintf(stderr, "Sets cannot be members of sets; %s\n", adr);
+ exit(-1);
+ }
+ bcopy(oold, onew, sizeof(struct Object));
+ onew->obj_members = onew->obj_left = onew->obj_right = NULL;
+ }
+ onew->obj_members = o->obj_members;
+ o->obj_members = onew;
+}
+
+struct Object *Lookup(type, name)
+unsigned char type;
+char *name;
+{
+ register struct Object *o = lookup(type,name);
+
+ if(o == NULL) {
+ fprintf(stderr, "Trying to use undefined %s: %s\n",
+ type==STATESET?"state":"event", name);
+ Exit(-1);
+ }
+ return(o);
+}
+
+AddCurrentEventName(x)
+register char **x;
+{
+ register char *n = EV_PREFIX; ;
+
+ if( CurrentEvent == (struct Object *)0 ) {
+ fprintf(stderr, "No event named! BARF!\n"); Exit(-1);
+ }
+
+ if( ! CurrentEvent->obj_struc ) {
+ fprintf(stderr, "No attributes for current event!\n"); Exit(-1);
+ }
+
+ /* add prefix first */
+ while(*n) {
+ *(*x)++ = *n++;
+ }
+
+ n = CurrentEvent->obj_name;
+
+ while(*n) {
+ *(*x)++ = *n++;
+ }
+}
+
+dumptree(o,i)
+ register struct Object *o;
+ int i;
+{
+ register int j;
+
+ if(o == NULL) {
+ for(j=0; j<i; j++)
+ fputc(' ', stdout);
+ fprintf(stdout, "%3d NULL\n", i);
+ } else {
+ dumptree(o->obj_left, i+1);
+ for(j=0; j<i; j++)
+ fputc(' ', stdout);
+ fprintf(stdout, "%3d 0x%x: %s\n", i,o, OBJ_NAME(o));
+ dumptree(o->obj_right, i+1);
+ }
+}
+
+dump(c,a)
+{
+ register int x = 8;
+ int zero = 0;
+#include <sys/signal.h>
+
+ fprintf(stderr, "dump: c 0x%x, a 0x%x\n",c,a);
+
+ x = x/zero;
+ kill(0, SIGQUIT);
+}
+
+dump_trans( pred, oldstate, newstate, action, event )
+struct Object *oldstate, *newstate, *event;
+char *pred, *action;
+{
+ extern int transno;
+ struct Object *o;
+
+ fprintf(stdout, "\n%d: ", transno);
+#define dumpit(x)\
+ if((x)->obj_kind == OBJ_SET) {\
+ o = (x)->obj_members; fprintf( stdout, "[ " );\
+ while(o) { fprintf(stdout, "%s ", o->obj_name); o = o->obj_members; }\
+ fprintf( stdout, " ] ");\
+ } else { fprintf(stdout, "%s ", (x)->obj_name); }
+
+ dumpit(newstate);
+ fprintf(stdout, " <== ");
+ dumpit(oldstate);
+ dumpit(event);
+ fprintf(stdout, "\n\t\t%s\n\t\t%s\n", pred?pred:"DEFAULT",
+ action);
+}
diff --git a/sys/netiso/xebec/sets.h b/sys/netiso/xebec/sets.h
new file mode 100644
index 0000000..96eb791
--- /dev/null
+++ b/sys/netiso/xebec/sets.h
@@ -0,0 +1,36 @@
+/* $Header: sets.h,v 2.1 88/09/19 12:56:33 nhall Exp $ */
+/* $Source: /var/home/tadl/src/argo/xebec/RCS/sets.h,v $ */
+
+#define MAXEVENTS 200
+#define MAXSTATES 200
+
+#define STATESET 10
+#define EVENTSET 5
+
+#define OBJ_ITEM 2
+#define OBJ_SET 3
+
+struct Object {
+ unsigned char obj_kind;
+ unsigned char obj_type; /* state or event */
+ char *obj_name;
+ char *obj_struc;
+ int obj_number;
+ struct Object *obj_members; /* must be null for kind==item */
+ /* for the tree */
+ struct Object *obj_left;
+ struct Object *obj_right;
+ struct Object *obj_parent;
+} ;
+
+extern char *Noname;
+
+#define OBJ_NAME(o) (((o)->obj_name)?(o)->obj_name:Noname)
+
+extern int Nevents, Nstates;
+int Eventshift;
+extern struct Object *CurrentEvent;
+
+extern struct Object *Lookup();
+extern struct Object *defineset();
+
diff --git a/sys/netiso/xebec/test.trans b/sys/netiso/xebec/test.trans
new file mode 100644
index 0000000..49db361
--- /dev/null
+++ b/sys/netiso/xebec/test.trans
@@ -0,0 +1,64 @@
+/* $Header: test.trans,v 0.2 88/09/19 12:58:29 nhall Exp $
+ */
+*PROTOCOL test
+
+*INCLUDE
+
+{
+#include "test_def.h"
+}
+
+*PCB test_pcbstruct SYNONYM P
+
+*STATES
+
+STATE_A
+STATE_B
+STATE_C
+ALL_STATES = [STATE_A, STATE_B, STATE_C]
+
+*EVENTS { int ev_all; } SYNONYM E
+
+EV_1 { char *ev1_char; }
+EV_2 { int ev2_int; char ev2_char; }
+EV_3
+EV_4 { struct blah *ev4_blahptr;
+ unsigned int ev4_uint;
+ int ev4_int;
+ }
+
+*TRANSITIONS
+
+SAME <== [ STATE_A, STATE_B ] [ EV_1, EV_2, EV_3 ]
+ ( $E.ev_all > 0 )
+ {
+ if( $P.test_state == STATE_A )
+ printf("state is STATE_A\n");
+ else
+ printf("state is STATE_B\n");
+ printf("action first transition\n");
+ }
+
+;
+STATE_C <== [ STATE_A, STATE_B ] [ EV_1, EV_2, EV_3 ]
+ DEFAULT
+ {
+ printf("default - transition 2\n");
+ MACRO1( $P.test_pcbfield );
+ }
+;
+
+STATE_C <== [ STATE_A, STATE_B ] EV_4
+ ( $$.ev4_blahptr->blahfield & 0x1 )
+ NULLACTION
+;
+
+STATE_C <== ALL_STATES EV_4
+ DEFAULT
+ {
+ printf("default - transition 4\n");
+ printf("pcb is 0x%x, event is 0x%x \n", $P, $E);
+ printf("ev4 values are : blahptr 0x%x uint 0x%x int 0x%x\n",
+ $$.ev4_blahptr, $$.ev4_uint, $$.ev4_int);
+ }
+;
diff --git a/sys/netiso/xebec/test_def.h b/sys/netiso/xebec/test_def.h
new file mode 100644
index 0000000..6faa2df
--- /dev/null
+++ b/sys/netiso/xebec/test_def.h
@@ -0,0 +1,13 @@
+
+struct blah {
+ unsigned int blahfield;
+ int dummyi;
+ char dummyc;
+};
+
+struct test_pcbstruct {
+ int test_pcbfield;
+ int test_state;
+};
+
+#define MACRO1(arg) if(arg != 0) { printf("macro1\n"); }
diff --git a/sys/netiso/xebec/xebec.bnf b/sys/netiso/xebec/xebec.bnf
new file mode 100644
index 0000000..d7406d9
--- /dev/null
+++ b/sys/netiso/xebec/xebec.bnf
@@ -0,0 +1,315 @@
+{
+#include "main.h"
+#include "sets.h"
+#include <stdio.h>
+
+extern FILE *eventfile_h, *actfile;
+}
+
+*fmq
+
+ novocab
+ nobnf
+ nofirst
+ nofollow
+ noparsetable
+ noerrortables
+ nos
+ noe
+
+*terminals
+
+ID 0 0 { char *address; }
+STRUCT 0 0
+SYNONYM 0 0
+PREDICATE 0 0 { char *address; }
+ACTION 0 0 { char *address; }
+/*
+FSTRING 0 0 { char *address; }
+*/
+PROTOCOL 0 0
+LBRACK 0 0
+RBRACK 0 0
+LANGLE 0 0
+EQUAL 0 0
+COMMA 0 0
+STAR 0 0
+EVENTS 0 0
+TRANSITIONS 0 0
+INCLUDE 0 0
+STATES 0 0
+SEMI 0 0
+PCB 0 0 { char *address; }
+DEFAULT 0 0
+NULLACTION 0 0
+SAME 0 0
+
+*nonterminals
+
+pcb { char *address; int isevent; }
+syn { int type; }
+setlist { struct Object *setnum; }
+setlisttail { struct Object *setnum; }
+part { unsigned char type; }
+parttail { unsigned char type; }
+partrest { unsigned char type; char *address; }
+setstruct { struct Object *object; }
+setdef { unsigned char type,keep; char *address; struct Object *object; }
+translist
+transition
+event { struct Object *object; }
+oldstate { struct Object *object; }
+newstate { struct Object *object; }
+predicatepart { char *string; }
+actionpart { char *string; struct Object *oldstate; struct Object *newstate; }
+
+*productions
+
+program ::=
+ STAR PROTOCOL ID
+ {
+ if(strlen($ID.address) > 50 ) {
+ fprintf(stderr,
+ "Protocol name may not exceed 50 chars in length.\n");
+ Exit(-1);
+ }
+ strcpy(protocol, $ID.address);
+ openfiles(protocol);
+ }
+ STAR includelist
+ PCB
+ {
+ $$pcb.isevent = 0;
+ }
+ pcb
+ {
+ fprintf(actfile, "\ntypedef %s %s%s;\n",
+ $pcb[7].address,protocol, PCBNAME);
+ $$syn.type = PCB_SYN;
+ }
+ syn
+ STAR STATES { $$part.type = (unsigned char) STATESET; } part
+ STAR { end_states(eventfile_h); } EVENTS
+ { $$pcb.isevent = 1; }
+ pcb
+ {
+ fprintf(eventfile_h, "\t"); /* fmq gags on single chars */
+ includecode(eventfile_h, $pcb[14].address);
+ fprintf(eventfile_h, "\n"); /* fmq gags on single chars */
+ $$syn.type = EVENT_SYN;
+ }
+ syn
+ {
+ $$part.type = (unsigned char)EVENTSET;
+ }
+ part
+ STAR { end_events(); }
+ TRANSITIONS
+ {
+ putincludes();
+ putdriver(actfile, 9);
+ }
+ translist
+;
+pcb ::= STRUCT
+ { if($pcb.isevent) {
+ fprintf(stderr,
+ "Event is a list of objects enclosed by \"{}\"\n");
+ Exit(-1);
+ }
+ fprintf(eventfile_h, "struct ");
+ }
+ ACTION { $pcb.address = $ACTION.address; }
+ optsemi
+ ::= ACTION
+ { if( ! $pcb.isevent) {
+ fprintf(stderr,
+ "Pcb requires a type or structure definition.\"{}\"\n");
+ Exit(-1);
+ }
+ $pcb.address = $ACTION.address;
+ }
+ optsemi
+ ::= ID { $pcb.address = $ID.address; } optsemi
+;
+
+syn ::= SYNONYM ID { synonyms[$syn.type] = stash( $ID.address ); }
+ ::=
+;
+
+optsemi ::= SEMI
+ ::=
+;
+includelist ::= INCLUDE ACTION { includecode(actfile, $ACTION.address);} STAR
+ ::=
+;
+part ::= ID
+ {
+ $$partrest.address = $ID.address;
+ $$partrest.type = $part.type;
+ }
+ partrest
+ { $$parttail.type = $part.type; }
+ parttail
+;
+parttail ::= { $$part.type = $parttail.type; } part
+ ::=
+;
+partrest ::= EQUAL
+ {
+ if( lookup( $partrest.type, $partrest.address ) ) {
+ fprintf(stderr, "bnf:trying to redefine obj type 0x%x, adr %s\n",
+ $partrest.type, $partrest.address);
+ Exit(-1);
+ }
+ $$setdef.type = $partrest.type;
+ $$setdef.address = stash( $partrest.address );
+ $$setdef.keep = 1;
+ } setdef { $$setstruct.object = $setdef.object; } setstruct
+
+ ::= ACTION
+ {
+ defineitem($partrest.type,
+ $partrest.address, $ACTION.address);
+ }
+
+ ::= {
+ defineitem($partrest.type, $partrest.address, (char *)0);
+ }
+;
+
+setstruct ::= ACTION
+ {
+ if($setstruct.object) {
+ /* WHEN COULD THIS BE FALSE??
+ * isn't it supposed to be setstruct.object???
+ * (it used to be $ACTION.address)
+ */
+
+ $setstruct.object->obj_struc = $ACTION.address;
+ fprintf(eventfile_h,
+ "struct %s %s%s;\n\n", $ACTION.address,
+ EV_PREFIX, $setstruct.object->obj_name);
+ }
+ }
+ ::=
+;
+
+setdef ::= LBRACK
+ {
+ $$setlist.setnum =
+ defineset($setdef.type, $setdef.address, $setdef.keep);
+ } setlist RBRACK { $setdef.object = $setlist.setnum; }
+;
+
+setlist ::= ID
+ {
+ member($setlist.setnum, $ID.address);
+ $$setlisttail.setnum = $setlist.setnum;
+ } setlisttail
+;
+
+setlisttail ::= COMMA { $$setlist.setnum = $setlisttail.setnum; } setlist
+ ::=
+;
+translist ::= transition translisttail
+;
+translisttail ::= translist
+ ::=
+;
+transition ::= newstate { transno ++; } LANGLE EQUAL EQUAL oldstate
+ event
+ {
+ CurrentEvent /* GAG! */ = $event.object;
+ }
+ predicatepart
+ {
+ $$actionpart.string = $predicatepart.string;
+ $$actionpart.newstate = $newstate.object;
+ $$actionpart.oldstate = $oldstate.object;
+ }
+ actionpart
+ SEMI
+;
+
+predicatepart ::= PREDICATE
+ {
+ $predicatepart.string = stash ( $PREDICATE.address );
+ }
+ ::= DEFAULT
+ {
+ $predicatepart.string = (char *)0;
+ }
+;
+
+actionpart ::=
+ ACTION
+ {
+ statetable( $actionpart.string, $actionpart.oldstate,
+ $actionpart.newstate,
+ acttable(actfile, $ACTION.address ),
+ CurrentEvent );
+ if( print_trans ) {
+ dump_trans( $actionpart.string, $actionpart.oldstate,
+ $actionpart.newstate,
+ $ACTION.address, CurrentEvent );
+ }
+ }
+ ::= NULLACTION
+ {
+ statetable($actionpart.string, $actionpart.oldstate, $actionpart.newstate,
+ 0, CurrentEvent ); /* KLUDGE - remove this */
+ if( print_trans ) {
+ dump_trans( $actionpart.string, $actionpart.oldstate,
+ $actionpart.newstate,
+ "NULLACTION", CurrentEvent );
+ }
+ }
+;
+
+oldstate ::= ID
+ {
+ $oldstate.object = Lookup(STATESET, $ID.address);
+ }
+ ::= {
+ $$setdef.address = (char *)0;
+ $$setdef.type = (unsigned char)STATESET;
+ $$setdef.keep = 0;
+ }
+ setdef
+ {
+ $oldstate.object = $setdef.object;
+ }
+;
+
+newstate ::= ID
+ {
+ $newstate.object = Lookup(STATESET, $ID.address);
+ }
+;
+
+newstate ::= SAME
+ {
+ extern struct Object *SameState;
+
+ $newstate.object = SameState;
+ }
+;
+
+event ::= ID
+ {
+ $event.object = Lookup(EVENTSET, $ID.address);
+ }
+ ::=
+ {
+ $$setdef.address = (char *)0;
+ $$setdef.type = (unsigned char)EVENTSET;
+ $$setdef.keep = 0;
+ }
+ setdef
+ {
+ $event.object = $setdef.object;
+ }
+;
+
+*end
diff --git a/sys/netiso/xebec/xebec.c b/sys/netiso/xebec/xebec.c
new file mode 100644
index 0000000..132bcb8
--- /dev/null
+++ b/sys/netiso/xebec/xebec.c
@@ -0,0 +1,451 @@
+/* $Header: xebec.c,v 2.2 88/09/19 12:55:37 nhall Exp $ */
+/* $Source: /var/home/tadl/src/argo/xebec/RCS/xebec.c,v $ */
+
+#include "xebec.h"
+#include "llparse.h"
+#ifndef E_TABLE
+#define E_TABLE "xebec.e"
+#endif E_TABLE
+
+#include "main.h"
+#include "sets.h"
+#include <stdio.h>
+
+extern FILE *eventfile_h, *actfile;
+
+llaction(lln,token)
+LLtoken *token;
+{
+ struct llattr *llattr;
+ llattr = &llattrdesc[lldescindex-1];
+switch(lln) {
+case 1:
+ llfinprod();
+ break;
+
+case 10: {
+
+ if(strlen(llattr->llabase[3].ID.address) > 50 ) {
+ fprintf(stderr,
+ "Protocol name may not exceed 50 chars in length.\n");
+ Exit(-1);
+ }
+ strcpy(protocol, llattr->llabase[3].ID.address);
+ openfiles(protocol);
+
+} break;
+
+case 11: {
+
+ llattr->llabase[7].pcb.isevent = 0;
+
+} break;
+
+case 12: {
+
+ fprintf(actfile, "\ntypedef %s %s%s;\n",
+ llattr->llabase[7].pcb.address,protocol, PCBNAME);
+ llattr->llabase[8].syn.type = PCB_SYN;
+
+} break;
+
+case 13: {
+ llattr->llabase[11].part.type = (unsigned char) STATESET;
+} break;
+
+case 14: {
+ end_states(eventfile_h);
+} break;
+
+case 15: {
+ llattr->llabase[14].pcb.isevent = 1;
+} break;
+
+case 16: {
+
+ fprintf(eventfile_h, "\t"); /* fmq gags on single chars */
+ includecode(eventfile_h, llattr->llabase[14].pcb.address);
+ fprintf(eventfile_h, "\n"); /* fmq gags on single chars */
+ llattr->llabase[15].syn.type = EVENT_SYN;
+
+} break;
+
+case 17: {
+
+ llattr->llabase[16].part.type = (unsigned char)EVENTSET;
+
+} break;
+
+case 18: {
+ end_events();
+} break;
+
+case 19: {
+
+ putincludes();
+ putdriver(actfile, 9);
+
+} break;
+
+case 20: {
+ if(llattr->llabase[0].pcb.isevent) {
+ fprintf(stderr,
+ "Event is a list of objects enclosed by \"{}\"\n");
+ Exit(-1);
+ }
+ fprintf(eventfile_h, "struct ");
+
+} break;
+
+case 21: {
+ llattr->llabase[0].pcb.address = llattr->llabase[2].ACTION.address;
+} break;
+
+case 22: {
+ if( ! llattr->llabase[0].pcb.isevent) {
+ fprintf(stderr,
+ "Pcb requires a type or structure definition.\"{}\"\n");
+ Exit(-1);
+ }
+ llattr->llabase[0].pcb.address = llattr->llabase[1].ACTION.address;
+
+} break;
+
+case 23: {
+ llattr->llabase[0].pcb.address = llattr->llabase[1].ID.address;
+} break;
+
+case 24: {
+ synonyms[llattr->llabase[0].syn.type] = stash( llattr->llabase[2].ID.address );
+} break;
+
+case 25: {
+ includecode(actfile, llattr->llabase[2].ACTION.address);
+} break;
+
+case 26: {
+
+ llattr->llabase[2].partrest.address = llattr->llabase[1].ID.address;
+ llattr->llabase[2].partrest.type = llattr->llabase[0].part.type;
+
+} break;
+
+case 27: {
+ llattr->llabase[3].parttail.type = llattr->llabase[0].part.type;
+} break;
+
+case 28: {
+ llattr->llabase[1].part.type = llattr->llabase[0].parttail.type;
+} break;
+
+case 29: {
+
+ if( lookup( llattr->llabase[0].partrest.type, llattr->llabase[0].partrest.address ) ) {
+ fprintf(stderr, "bnf:trying to redefine obj type 0x%x, adr %s\n",
+ llattr->llabase[0].partrest.type, llattr->llabase[0].partrest.address);
+ Exit(-1);
+ }
+ llattr->llabase[2].setdef.type = llattr->llabase[0].partrest.type;
+ llattr->llabase[2].setdef.address = stash( llattr->llabase[0].partrest.address );
+ llattr->llabase[2].setdef.keep = 1;
+
+} break;
+
+case 30: {
+ llattr->llabase[3].setstruct.object = llattr->llabase[2].setdef.object;
+} break;
+
+case 31: {
+
+ defineitem(llattr->llabase[0].partrest.type,
+ llattr->llabase[0].partrest.address, llattr->llabase[1].ACTION.address);
+
+} break;
+
+case 32: {
+
+ defineitem(llattr->llabase[0].partrest.type, llattr->llabase[0].partrest.address, (char *)0);
+
+} break;
+
+case 33: {
+
+ if(llattr->llabase[0].setstruct.object) {
+ /* WHEN COULD THIS BE FALSE??
+ * isn't it supposed to be setstruct.object???
+ * (it used to be $ACTION.address)
+ */
+
+ llattr->llabase[0].setstruct.object->obj_struc = llattr->llabase[1].ACTION.address;
+ fprintf(eventfile_h,
+ "struct %s %s%s;\n\n", llattr->llabase[1].ACTION.address,
+ EV_PREFIX, llattr->llabase[0].setstruct.object->obj_name);
+ }
+
+} break;
+
+case 34: {
+
+ llattr->llabase[2].setlist.setnum =
+ defineset(llattr->llabase[0].setdef.type, llattr->llabase[0].setdef.address, llattr->llabase[0].setdef.keep);
+
+} break;
+
+case 35: {
+ llattr->llabase[0].setdef.object = llattr->llabase[2].setlist.setnum;
+} break;
+
+case 36: {
+
+ member(llattr->llabase[0].setlist.setnum, llattr->llabase[1].ID.address);
+ llattr->llabase[2].setlisttail.setnum = llattr->llabase[0].setlist.setnum;
+
+} break;
+
+case 37: {
+ llattr->llabase[2].setlist.setnum = llattr->llabase[0].setlisttail.setnum;
+} break;
+
+case 38: {
+ transno ++;
+} break;
+
+case 39: {
+
+ CurrentEvent /* GAG! */ = llattr->llabase[6].event.object;
+
+} break;
+
+case 40: {
+
+ llattr->llabase[8].actionpart.string = llattr->llabase[7].predicatepart.string;
+ llattr->llabase[8].actionpart.newstate = llattr->llabase[1].newstate.object;
+ llattr->llabase[8].actionpart.oldstate = llattr->llabase[5].oldstate.object;
+
+} break;
+
+case 41: {
+
+ llattr->llabase[0].predicatepart.string = stash ( llattr->llabase[1].PREDICATE.address );
+
+} break;
+
+case 42: {
+
+ llattr->llabase[0].predicatepart.string = (char *)0;
+
+} break;
+
+case 43: {
+
+ statetable( llattr->llabase[0].actionpart.string, llattr->llabase[0].actionpart.oldstate,
+ llattr->llabase[0].actionpart.newstate,
+ acttable(actfile, llattr->llabase[1].ACTION.address ),
+ CurrentEvent );
+ if( print_trans ) {
+ dump_trans( llattr->llabase[0].actionpart.string, llattr->llabase[0].actionpart.oldstate,
+ llattr->llabase[0].actionpart.newstate,
+ llattr->llabase[1].ACTION.address, CurrentEvent );
+ }
+
+} break;
+
+case 44: {
+
+ statetable(llattr->llabase[0].actionpart.string, llattr->llabase[0].actionpart.oldstate, llattr->llabase[0].actionpart.newstate,
+ 0, CurrentEvent ); /* KLUDGE - remove this */
+ if( print_trans ) {
+ dump_trans( llattr->llabase[0].actionpart.string, llattr->llabase[0].actionpart.oldstate,
+ llattr->llabase[0].actionpart.newstate,
+ "NULLACTION", CurrentEvent );
+ }
+
+} break;
+
+case 45: {
+
+ llattr->llabase[0].oldstate.object = Lookup(STATESET, llattr->llabase[1].ID.address);
+
+} break;
+
+case 46: {
+
+ llattr->llabase[1].setdef.address = (char *)0;
+ llattr->llabase[1].setdef.type = (unsigned char)STATESET;
+ llattr->llabase[1].setdef.keep = 0;
+
+} break;
+
+case 47: {
+
+ llattr->llabase[0].oldstate.object = llattr->llabase[1].setdef.object;
+
+} break;
+
+case 48: {
+
+ llattr->llabase[0].newstate.object = Lookup(STATESET, llattr->llabase[1].ID.address);
+
+} break;
+
+case 49: {
+
+ extern struct Object *SameState;
+
+ llattr->llabase[0].newstate.object = SameState;
+
+} break;
+
+case 50: {
+
+ llattr->llabase[0].event.object = Lookup(EVENTSET, llattr->llabase[1].ID.address);
+
+} break;
+
+case 51: {
+
+ llattr->llabase[1].setdef.address = (char *)0;
+ llattr->llabase[1].setdef.type = (unsigned char)EVENTSET;
+ llattr->llabase[1].setdef.keep = 0;
+
+} break;
+
+case 52: {
+
+ llattr->llabase[0].event.object = llattr->llabase[1].setdef.object;
+
+} break;
+}
+}
+char *llstrings[] = {
+ "<null>",
+ "ID",
+ "STRUCT",
+ "SYNONYM",
+ "PREDICATE",
+ "ACTION",
+ "PROTOCOL",
+ "LBRACK",
+ "RBRACK",
+ "LANGLE",
+ "EQUAL",
+ "COMMA",
+ "STAR",
+ "EVENTS",
+ "TRANSITIONS",
+ "INCLUDE",
+ "STATES",
+ "SEMI",
+ "PCB",
+ "DEFAULT",
+ "NULLACTION",
+ "SAME",
+ "ENDMARKER",
+ "pcb",
+ "syn",
+ "setlist",
+ "setlisttail",
+ "part",
+ "parttail",
+ "partrest",
+ "setstruct",
+ "setdef",
+ "translist",
+ "transition",
+ "event",
+ "oldstate",
+ "newstate",
+ "predicatepart",
+ "actionpart",
+ "program",
+ "includelist",
+ "optsemi",
+ "translisttail",
+ "$goal$",
+ (char *) 0
+};
+short llnterms = 23;
+short llnsyms = 44;
+short llnprods = 38;
+short llinfinite = 10000;
+short llproductions[] = {
+41, -21, 5, -20, 2,
+41, -22, 5,
+41, -23, 1,
+-24, 1, 3,
+
+26, -36, 1,
+25, -37, 11,
+
+28, -27, 29, -26, 1,
+27, -28,
+
+30, -30, 31, -29, 10,
+-31, 5,
+-32,
+-33, 5,
+
+-35, 8, 25, -34, 7,
+42, 33,
+17, 38, -40, 37, -39, 34, 35, 10, 10, 9, -38, 36,
+-50, 1,
+-52, 31, -51,
+-45, 1,
+-47, 31, -46,
+-48, 1,
+-49, 21,
+-41, 4,
+-42, 19,
+-43, 5,
+-44, 20,
+32, -19, 14, -18, 12, 27, -17, 24, -16, 23, -15, 13, -14, 12, 27, -13, 16, 12, 24, -12, 23, -11, 18, 40, 12, -10, 1, 6, 12,
+12, -25, 5, 15,
+
+17,
+
+32,
+
+22, 39,
+0
+};
+struct llprodindex llprodindex[] = {
+{ 0, 0, 0 }, { 0, 5, 19 }, { 5, 3, 3 }, { 8, 3, 2 },
+{ 11, 3, 2 }, { 14, 0, 2 }, { 14, 3, 0 }, { 17, 3, 1 },
+{ 20, 0, 0 }, { 20, 5, 3 }, { 25, 2, 0 }, { 27, 0, 3 },
+{ 27, 5, 1 }, { 32, 2, 0 }, { 34, 1, 3 }, { 35, 2, 1 },
+{ 37, 0, 0 }, { 37, 5, 1 }, { 42, 2, 0 }, { 44, 12, 3 },
+{ 56, 2, 2 }, { 58, 3, 2 }, { 61, 2, 0 }, { 63, 3, 2 },
+{ 66, 2, 1 }, { 68, 2, 0 }, { 70, 2, 9 }, { 72, 2, 1 },
+{ 74, 2, 1 }, { 76, 2, 1 }, { 78, 29, 1 }, { 107, 4, 1 },
+{ 111, 0, 1 }, { 111, 1, 1 }, { 112, 0, 1 }, { 112, 1, 1 },
+{ 113, 0, 1 }, { 113, 2, 2 }, { 0, 0, 0 }
+};
+short llepsilon[] = {
+ 0, 0, 0, 0, 0, 1, 0, 0, 1, 0,
+ 0, 1, 0, 0, 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 1, 0, 1, 0, 0
+};
+struct llparsetable llparsetable[] = {
+{ 1, 3 }, { 2, 1 }, { 5, 2 }, { 0, 23 }, { 1, 5 },
+{ 3, 4 }, { 12, 5 }, { 0, 24 }, { 1, 6 }, { 0, 25 },
+{ 8, 8 }, { 11, 7 }, { 0, 26 }, { 1, 9 }, { 0, 27 },
+{ 1, 10 }, { 12, 11 }, { 0, 28 }, { 1, 14 }, { 5, 13 },
+{ 10, 12 }, { 12, 14 }, { 0, 29 }, { 1, 16 }, { 5, 15 },
+{ 12, 16 }, { 0, 30 }, { 7, 17 }, { 0, 31 }, { 1, 18 },
+{ 21, 18 }, { 0, 32 }, { 1, 19 }, { 21, 19 }, { 0, 33 },
+{ 1, 20 }, { 7, 21 }, { 0, 34 }, { 1, 22 }, { 7, 23 },
+{ 0, 35 }, { 1, 24 }, { 21, 25 }, { 0, 36 }, { 4, 26 },
+{ 19, 27 }, { 0, 37 }, { 5, 28 }, { 20, 29 }, { 0, 38 },
+{ 12, 30 }, { 0, 39 }, { 15, 31 }, { 18, 32 }, { 0, 40 },
+{ 1, 34 }, { 3, 34 }, { 12, 34 }, { 17, 33 }, { 0, 41 },
+{ 1, 35 }, { 21, 35 }, { 22, 36 }, { 0, 42 }, { 12, 37 },
+{ 0, 43 }, { 0, 0 }
+};
+short llparseindex[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 8, 10, 13, 15, 18,
+ 23, 27, 29, 32, 35, 38, 41, 44, 47, 50,
+ 52, 55, 60, 64, 0
+};
diff --git a/sys/netiso/xebec/xebec.h b/sys/netiso/xebec/xebec.h
new file mode 100644
index 0000000..168bb77
--- /dev/null
+++ b/sys/netiso/xebec/xebec.h
@@ -0,0 +1,88 @@
+/* $Header: xebec.h,v 2.1 88/09/19 12:56:35 nhall Exp $ */
+/* $Source: /var/home/tadl/src/argo/xebec/RCS/xebec.h,v $ */
+
+union llattrib {
+ struct {
+ char *address; } ID;
+ int STRUCT;
+ int SYNONYM;
+ struct {
+ char *address; } PREDICATE;
+ struct {
+ char *address; } ACTION;
+ int PROTOCOL;
+ int LBRACK;
+ int RBRACK;
+ int LANGLE;
+ int EQUAL;
+ int COMMA;
+ int STAR;
+ int EVENTS;
+ int TRANSITIONS;
+ int INCLUDE;
+ int STATES;
+ int SEMI;
+ struct {
+ char *address; } PCB;
+ int DEFAULT;
+ int NULLACTION;
+ int SAME;
+ struct {
+ char *address; int isevent; } pcb;
+ struct {
+ int type; } syn;
+ struct {
+ struct Object *setnum; } setlist;
+ struct {
+ struct Object *setnum; } setlisttail;
+ struct {
+ unsigned char type; } part;
+ struct {
+ unsigned char type; } parttail;
+ struct {
+ unsigned char type; char *address; } partrest;
+ struct {
+ struct Object *object; } setstruct;
+ struct {
+ unsigned char type,keep; char *address; struct Object *object; } setdef;
+ int translist;
+ int transition;
+ struct {
+ struct Object *object; } event;
+ struct {
+ struct Object *object; } oldstate;
+ struct {
+ struct Object *object; } newstate;
+ struct {
+ char *string; } predicatepart;
+ struct {
+ char *string; struct Object *oldstate; struct Object *newstate; } actionpart;
+};
+#define LLTERM 23
+#define LLSYM 44
+#define LLPROD 38
+
+#define LLINF 10000
+
+#define T_ID 1
+#define T_STRUCT 2
+#define T_SYNONYM 3
+#define T_PREDICATE 4
+#define T_ACTION 5
+#define T_PROTOCOL 6
+#define T_LBRACK 7
+#define T_RBRACK 8
+#define T_LANGLE 9
+#define T_EQUAL 10
+#define T_COMMA 11
+#define T_STAR 12
+#define T_EVENTS 13
+#define T_TRANSITIONS 14
+#define T_INCLUDE 15
+#define T_STATES 16
+#define T_SEMI 17
+#define T_PCB 18
+#define T_DEFAULT 19
+#define T_NULLACTION 20
+#define T_SAME 21
+#define T_ENDMARKER 22
diff --git a/sys/netns/idp.h b/sys/netns/idp.h
new file mode 100644
index 0000000..254208d
--- /dev/null
+++ b/sys/netns/idp.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)idp.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Definitions for NS(tm) Internet Datagram Protocol
+ */
+struct idp {
+ u_short idp_sum; /* Checksum */
+ u_short idp_len; /* Length, in bytes, including header */
+ u_char idp_tc; /* Transport Crontrol (i.e. hop count) */
+ u_char idp_pt; /* Packet Type (i.e. level 2 protocol) */
+ struct ns_addr idp_dna; /* Destination Network Address */
+ struct ns_addr idp_sna; /* Source Network Address */
+};
diff --git a/sys/netns/idp_usrreq.c b/sys/netns/idp_usrreq.c
new file mode 100644
index 0000000..b548a12
--- /dev/null
+++ b/sys/netns/idp_usrreq.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)idp_usrreq.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netns/ns.h>
+#include <netns/ns_pcb.h>
+#include <netns/ns_if.h>
+#include <netns/idp.h>
+#include <netns/idp_var.h>
+#include <netns/ns_error.h>
+
+/*
+ * IDP protocol implementation.
+ */
+
+struct sockaddr_ns idp_ns = { sizeof(idp_ns), AF_NS };
+
+/*
+ * This may also be called for raw listeners.
+ */
+idp_input(m, nsp)
+ struct mbuf *m;
+ register struct nspcb *nsp;
+{
+ register struct idp *idp = mtod(m, struct idp *);
+ struct ifnet *ifp = m->m_pkthdr.rcvif;
+
+ if (nsp==0)
+ panic("No nspcb");
+ /*
+ * Construct sockaddr format source address.
+ * Stuff source address and datagram in user buffer.
+ */
+ idp_ns.sns_addr = idp->idp_sna;
+ if (ns_neteqnn(idp->idp_sna.x_net, ns_zeronet) && ifp) {
+ register struct ifaddr *ifa;
+
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family == AF_NS) {
+ idp_ns.sns_addr.x_net =
+ IA_SNS(ifa)->sns_addr.x_net;
+ break;
+ }
+ }
+ }
+ nsp->nsp_rpt = idp->idp_pt;
+ if ( ! (nsp->nsp_flags & NSP_RAWIN) ) {
+ m->m_len -= sizeof (struct idp);
+ m->m_pkthdr.len -= sizeof (struct idp);
+ m->m_data += sizeof (struct idp);
+ }
+ if (sbappendaddr(&nsp->nsp_socket->so_rcv, (struct sockaddr *)&idp_ns,
+ m, (struct mbuf *)0) == 0)
+ goto bad;
+ sorwakeup(nsp->nsp_socket);
+ return;
+bad:
+ m_freem(m);
+}
+
+idp_abort(nsp)
+ struct nspcb *nsp;
+{
+ struct socket *so = nsp->nsp_socket;
+
+ ns_pcbdisconnect(nsp);
+ soisdisconnected(so);
+}
+/*
+ * Drop connection, reporting
+ * the specified error.
+ */
+struct nspcb *
+idp_drop(nsp, errno)
+ register struct nspcb *nsp;
+ int errno;
+{
+ struct socket *so = nsp->nsp_socket;
+
+ /*
+ * someday, in the xerox world
+ * we will generate error protocol packets
+ * announcing that the socket has gone away.
+ */
+ /*if (TCPS_HAVERCVDSYN(tp->t_state)) {
+ tp->t_state = TCPS_CLOSED;
+ (void) tcp_output(tp);
+ }*/
+ so->so_error = errno;
+ ns_pcbdisconnect(nsp);
+ soisdisconnected(so);
+}
+
+int noIdpRoute;
+idp_output(nsp, m0)
+ struct nspcb *nsp;
+ struct mbuf *m0;
+{
+ register struct mbuf *m;
+ register struct idp *idp;
+ register struct socket *so;
+ register int len = 0;
+ register struct route *ro;
+ struct mbuf *mprev;
+ extern int idpcksum;
+
+ /*
+ * Calculate data length.
+ */
+ for (m = m0; m; m = m->m_next) {
+ mprev = m;
+ len += m->m_len;
+ }
+ /*
+ * Make sure packet is actually of even length.
+ */
+
+ if (len & 1) {
+ m = mprev;
+ if ((m->m_flags & M_EXT) == 0 &&
+ (m->m_len + m->m_data < &m->m_dat[MLEN])) {
+ m->m_len++;
+ } else {
+ struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA);
+
+ if (m1 == 0) {
+ m_freem(m0);
+ return (ENOBUFS);
+ }
+ m1->m_len = 1;
+ * mtod(m1, char *) = 0;
+ m->m_next = m1;
+ }
+ m0->m_pkthdr.len++;
+ }
+
+ /*
+ * Fill in mbuf with extended IDP header
+ * and addresses and length put into network format.
+ */
+ m = m0;
+ if (nsp->nsp_flags & NSP_RAWOUT) {
+ idp = mtod(m, struct idp *);
+ } else {
+ M_PREPEND(m, sizeof (struct idp), M_DONTWAIT);
+ if (m == 0)
+ return (ENOBUFS);
+ idp = mtod(m, struct idp *);
+ idp->idp_tc = 0;
+ idp->idp_pt = nsp->nsp_dpt;
+ idp->idp_sna = nsp->nsp_laddr;
+ idp->idp_dna = nsp->nsp_faddr;
+ len += sizeof (struct idp);
+ }
+
+ idp->idp_len = htons((u_short)len);
+
+ if (idpcksum) {
+ idp->idp_sum = 0;
+ len = ((len - 1) | 1) + 1;
+ idp->idp_sum = ns_cksum(m, len);
+ } else
+ idp->idp_sum = 0xffff;
+
+ /*
+ * Output datagram.
+ */
+ so = nsp->nsp_socket;
+ if (so->so_options & SO_DONTROUTE)
+ return (ns_output(m, (struct route *)0,
+ (so->so_options & SO_BROADCAST) | NS_ROUTETOIF));
+ /*
+ * Use cached route for previous datagram if
+ * possible. If the previous net was the same
+ * and the interface was a broadcast medium, or
+ * if the previous destination was identical,
+ * then we are ok.
+ *
+ * NB: We don't handle broadcasts because that
+ * would require 3 subroutine calls.
+ */
+ ro = &nsp->nsp_route;
+#ifdef ancient_history
+ /*
+ * I think that this will all be handled in ns_pcbconnect!
+ */
+ if (ro->ro_rt) {
+ if(ns_neteq(nsp->nsp_lastdst, idp->idp_dna)) {
+ /*
+ * This assumes we have no GH type routes
+ */
+ if (ro->ro_rt->rt_flags & RTF_HOST) {
+ if (!ns_hosteq(nsp->nsp_lastdst, idp->idp_dna))
+ goto re_route;
+
+ }
+ if ((ro->ro_rt->rt_flags & RTF_GATEWAY) == 0) {
+ register struct ns_addr *dst =
+ &satons_addr(ro->ro_dst);
+ dst->x_host = idp->idp_dna.x_host;
+ }
+ /*
+ * Otherwise, we go through the same gateway
+ * and dst is already set up.
+ */
+ } else {
+ re_route:
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ }
+ nsp->nsp_lastdst = idp->idp_dna;
+#endif /* ancient_history */
+ if (noIdpRoute) ro = 0;
+ return (ns_output(m, ro, so->so_options & SO_BROADCAST));
+}
+/* ARGSUSED */
+idp_ctloutput(req, so, level, name, value)
+ int req, level;
+ struct socket *so;
+ int name;
+ struct mbuf **value;
+{
+ register struct mbuf *m;
+ struct nspcb *nsp = sotonspcb(so);
+ int mask, error = 0;
+ extern long ns_pexseq;
+
+ if (nsp == NULL)
+ return (EINVAL);
+
+ switch (req) {
+
+ case PRCO_GETOPT:
+ if (value==NULL)
+ return (EINVAL);
+ m = m_get(M_DONTWAIT, MT_DATA);
+ if (m==NULL)
+ return (ENOBUFS);
+ switch (name) {
+
+ case SO_ALL_PACKETS:
+ mask = NSP_ALL_PACKETS;
+ goto get_flags;
+
+ case SO_HEADERS_ON_INPUT:
+ mask = NSP_RAWIN;
+ goto get_flags;
+
+ case SO_HEADERS_ON_OUTPUT:
+ mask = NSP_RAWOUT;
+ get_flags:
+ m->m_len = sizeof(short);
+ *mtod(m, short *) = nsp->nsp_flags & mask;
+ break;
+
+ case SO_DEFAULT_HEADERS:
+ m->m_len = sizeof(struct idp);
+ {
+ register struct idp *idp = mtod(m, struct idp *);
+ idp->idp_len = 0;
+ idp->idp_sum = 0;
+ idp->idp_tc = 0;
+ idp->idp_pt = nsp->nsp_dpt;
+ idp->idp_dna = nsp->nsp_faddr;
+ idp->idp_sna = nsp->nsp_laddr;
+ }
+ break;
+
+ case SO_SEQNO:
+ m->m_len = sizeof(long);
+ *mtod(m, long *) = ns_pexseq++;
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ *value = m;
+ break;
+
+ case PRCO_SETOPT:
+ switch (name) {
+ int *ok;
+
+ case SO_ALL_PACKETS:
+ mask = NSP_ALL_PACKETS;
+ goto set_head;
+
+ case SO_HEADERS_ON_INPUT:
+ mask = NSP_RAWIN;
+ goto set_head;
+
+ case SO_HEADERS_ON_OUTPUT:
+ mask = NSP_RAWOUT;
+ set_head:
+ if (value && *value) {
+ ok = mtod(*value, int *);
+ if (*ok)
+ nsp->nsp_flags |= mask;
+ else
+ nsp->nsp_flags &= ~mask;
+ } else error = EINVAL;
+ break;
+
+ case SO_DEFAULT_HEADERS:
+ {
+ register struct idp *idp
+ = mtod(*value, struct idp *);
+ nsp->nsp_dpt = idp->idp_pt;
+ }
+ break;
+#ifdef NSIP
+
+ case SO_NSIP_ROUTE:
+ error = nsip_route(*value);
+ break;
+#endif /* NSIP */
+ default:
+ error = EINVAL;
+ }
+ if (value && *value)
+ m_freem(*value);
+ break;
+ }
+ return (error);
+}
+
+/*ARGSUSED*/
+idp_usrreq(so, req, m, nam, control)
+ struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *control;
+{
+ struct nspcb *nsp = sotonspcb(so);
+ int error = 0;
+
+ if (req == PRU_CONTROL)
+ return (ns_control(so, (int)m, (caddr_t)nam,
+ (struct ifnet *)control));
+ if (control && control->m_len) {
+ error = EINVAL;
+ goto release;
+ }
+ if (nsp == NULL && req != PRU_ATTACH) {
+ error = EINVAL;
+ goto release;
+ }
+ switch (req) {
+
+ case PRU_ATTACH:
+ if (nsp != NULL) {
+ error = EINVAL;
+ break;
+ }
+ error = ns_pcballoc(so, &nspcb);
+ if (error)
+ break;
+ error = soreserve(so, (u_long) 2048, (u_long) 2048);
+ if (error)
+ break;
+ break;
+
+ case PRU_DETACH:
+ if (nsp == NULL) {
+ error = ENOTCONN;
+ break;
+ }
+ ns_pcbdetach(nsp);
+ break;
+
+ case PRU_BIND:
+ error = ns_pcbbind(nsp, nam);
+ break;
+
+ case PRU_LISTEN:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_CONNECT:
+ if (!ns_nullhost(nsp->nsp_faddr)) {
+ error = EISCONN;
+ break;
+ }
+ error = ns_pcbconnect(nsp, nam);
+ if (error == 0)
+ soisconnected(so);
+ break;
+
+ case PRU_CONNECT2:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_ACCEPT:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_DISCONNECT:
+ if (ns_nullhost(nsp->nsp_faddr)) {
+ error = ENOTCONN;
+ break;
+ }
+ ns_pcbdisconnect(nsp);
+ soisdisconnected(so);
+ break;
+
+ case PRU_SHUTDOWN:
+ socantsendmore(so);
+ break;
+
+ case PRU_SEND:
+ {
+ struct ns_addr laddr;
+ int s;
+
+ if (nam) {
+ laddr = nsp->nsp_laddr;
+ if (!ns_nullhost(nsp->nsp_faddr)) {
+ error = EISCONN;
+ break;
+ }
+ /*
+ * Must block input while temporarily connected.
+ */
+ s = splnet();
+ error = ns_pcbconnect(nsp, nam);
+ if (error) {
+ splx(s);
+ break;
+ }
+ } else {
+ if (ns_nullhost(nsp->nsp_faddr)) {
+ error = ENOTCONN;
+ break;
+ }
+ }
+ error = idp_output(nsp, m);
+ m = NULL;
+ if (nam) {
+ ns_pcbdisconnect(nsp);
+ splx(s);
+ nsp->nsp_laddr.x_host = laddr.x_host;
+ nsp->nsp_laddr.x_port = laddr.x_port;
+ }
+ }
+ break;
+
+ case PRU_ABORT:
+ ns_pcbdetach(nsp);
+ sofree(so);
+ soisdisconnected(so);
+ break;
+
+ case PRU_SOCKADDR:
+ ns_setsockaddr(nsp, nam);
+ break;
+
+ case PRU_PEERADDR:
+ ns_setpeeraddr(nsp, nam);
+ break;
+
+ case PRU_SENSE:
+ /*
+ * stat: don't bother with a blocksize.
+ */
+ return (0);
+
+ case PRU_SENDOOB:
+ case PRU_FASTTIMO:
+ case PRU_SLOWTIMO:
+ case PRU_PROTORCV:
+ case PRU_PROTOSEND:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_CONTROL:
+ case PRU_RCVD:
+ case PRU_RCVOOB:
+ return (EOPNOTSUPP); /* do not free mbuf's */
+
+ default:
+ panic("idp_usrreq");
+ }
+release:
+ if (control != NULL)
+ m_freem(control);
+ if (m != NULL)
+ m_freem(m);
+ return (error);
+}
+/*ARGSUSED*/
+idp_raw_usrreq(so, req, m, nam, control)
+ struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *control;
+{
+ int error = 0;
+ struct nspcb *nsp = sotonspcb(so);
+ extern struct nspcb nsrawpcb;
+
+ switch (req) {
+
+ case PRU_ATTACH:
+
+ if (!(so->so_state & SS_PRIV) || (nsp != NULL)) {
+ error = EINVAL;
+ break;
+ }
+ error = ns_pcballoc(so, &nsrawpcb);
+ if (error)
+ break;
+ error = soreserve(so, (u_long) 2048, (u_long) 2048);
+ if (error)
+ break;
+ nsp = sotonspcb(so);
+ nsp->nsp_faddr.x_host = ns_broadhost;
+ nsp->nsp_flags = NSP_RAWIN | NSP_RAWOUT;
+ break;
+ default:
+ error = idp_usrreq(so, req, m, nam, control);
+ }
+ return (error);
+}
+
diff --git a/sys/netns/idp_var.h b/sys/netns/idp_var.h
new file mode 100644
index 0000000..fc9a4f4
--- /dev/null
+++ b/sys/netns/idp_var.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)idp_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * IDP Kernel Structures and Variables
+ */
+struct idpstat {
+ int idps_badsum; /* checksum bad */
+ int idps_tooshort; /* packet too short */
+ int idps_toosmall; /* not enough data */
+ int idps_badhlen; /* ip header length < data size */
+ int idps_badlen; /* ip length < ip header length */
+};
+
+#ifdef KERNEL
+struct idpstat idpstat;
+#endif
diff --git a/sys/netns/ns.c b/sys/netns/ns.c
new file mode 100644
index 0000000..8b76543
--- /dev/null
+++ b/sys/netns/ns.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ns.c 8.2 (Berkeley) 11/15/93
+ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/ioctl.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+
+#ifdef NS
+
+struct ns_ifaddr *ns_ifaddr;
+int ns_interfaces;
+extern struct sockaddr_ns ns_netmask, ns_hostmask;
+
+/*
+ * Generic internet control operations (ioctl's).
+ */
+/* ARGSUSED */
+ns_control(so, cmd, data, ifp)
+ struct socket *so;
+ int cmd;
+ caddr_t data;
+ register struct ifnet *ifp;
+{
+ register struct ifreq *ifr = (struct ifreq *)data;
+ register struct ns_aliasreq *ifra = (struct ns_aliasreq *)data;
+ register struct ns_ifaddr *ia;
+ struct ifaddr *ifa;
+ struct ns_ifaddr *oia;
+ int error, dstIsNew, hostIsNew;
+
+ /*
+ * Find address for this interface, if it exists.
+ */
+ if (ifp == 0)
+ return (EADDRNOTAVAIL);
+ for (ia = ns_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+
+ switch (cmd) {
+
+ case SIOCGIFADDR:
+ if (ia == (struct ns_ifaddr *)0)
+ return (EADDRNOTAVAIL);
+ *(struct sockaddr_ns *)&ifr->ifr_addr = ia->ia_addr;
+ return (0);
+
+
+ case SIOCGIFBRDADDR:
+ if (ia == (struct ns_ifaddr *)0)
+ return (EADDRNOTAVAIL);
+ if ((ifp->if_flags & IFF_BROADCAST) == 0)
+ return (EINVAL);
+ *(struct sockaddr_ns *)&ifr->ifr_dstaddr = ia->ia_broadaddr;
+ return (0);
+
+ case SIOCGIFDSTADDR:
+ if (ia == (struct ns_ifaddr *)0)
+ return (EADDRNOTAVAIL);
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
+ return (EINVAL);
+ *(struct sockaddr_ns *)&ifr->ifr_dstaddr = ia->ia_dstaddr;
+ return (0);
+ }
+
+ if ((so->so_state & SS_PRIV) == 0)
+ return (EPERM);
+
+ switch (cmd) {
+ case SIOCAIFADDR:
+ case SIOCDIFADDR:
+ if (ifra->ifra_addr.sns_family == AF_NS)
+ for (oia = ia; ia; ia = ia->ia_next) {
+ if (ia->ia_ifp == ifp &&
+ ns_neteq(ia->ia_addr.sns_addr,
+ ifra->ifra_addr.sns_addr))
+ break;
+ }
+ if (cmd == SIOCDIFADDR && ia == 0)
+ return (EADDRNOTAVAIL);
+ /* FALLTHROUGH */
+
+ case SIOCSIFADDR:
+ case SIOCSIFDSTADDR:
+ if (ia == (struct ns_ifaddr *)0) {
+ oia = (struct ns_ifaddr *)
+ malloc(sizeof *ia, M_IFADDR, M_WAITOK);
+ if (oia == (struct ns_ifaddr *)NULL)
+ return (ENOBUFS);
+ bzero((caddr_t)oia, sizeof(*oia));
+ if (ia = ns_ifaddr) {
+ for ( ; ia->ia_next; ia = ia->ia_next)
+ ;
+ ia->ia_next = oia;
+ } else
+ ns_ifaddr = oia;
+ ia = oia;
+ if (ifa = ifp->if_addrlist) {
+ for ( ; ifa->ifa_next; ifa = ifa->ifa_next)
+ ;
+ ifa->ifa_next = (struct ifaddr *) ia;
+ } else
+ ifp->if_addrlist = (struct ifaddr *) ia;
+ ia->ia_ifp = ifp;
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+
+ ia->ia_ifa.ifa_netmask =
+ (struct sockaddr *)&ns_netmask;
+
+ ia->ia_ifa.ifa_dstaddr =
+ (struct sockaddr *)&ia->ia_dstaddr;
+ if (ifp->if_flags & IFF_BROADCAST) {
+ ia->ia_broadaddr.sns_family = AF_NS;
+ ia->ia_broadaddr.sns_len = sizeof(ia->ia_addr);
+ ia->ia_broadaddr.sns_addr.x_host = ns_broadhost;
+ }
+ ns_interfaces++;
+ }
+ }
+
+ switch (cmd) {
+ int error;
+
+ case SIOCSIFDSTADDR:
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
+ return (EINVAL);
+ if (ia->ia_flags & IFA_ROUTE) {
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
+ ia->ia_flags &= ~IFA_ROUTE;
+ }
+ if (ifp->if_ioctl) {
+ error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR, ia);
+ if (error)
+ return (error);
+ }
+ *(struct sockaddr *)&ia->ia_dstaddr = ifr->ifr_dstaddr;
+ return (0);
+
+ case SIOCSIFADDR:
+ return (ns_ifinit(ifp, ia,
+ (struct sockaddr_ns *)&ifr->ifr_addr, 1));
+
+ case SIOCDIFADDR:
+ ns_ifscrub(ifp, ia);
+ if ((ifa = ifp->if_addrlist) == (struct ifaddr *)ia)
+ ifp->if_addrlist = ifa->ifa_next;
+ else {
+ while (ifa->ifa_next &&
+ (ifa->ifa_next != (struct ifaddr *)ia))
+ ifa = ifa->ifa_next;
+ if (ifa->ifa_next)
+ ifa->ifa_next = ((struct ifaddr *)ia)->ifa_next;
+ else
+ printf("Couldn't unlink nsifaddr from ifp\n");
+ }
+ oia = ia;
+ if (oia == (ia = ns_ifaddr)) {
+ ns_ifaddr = ia->ia_next;
+ } else {
+ while (ia->ia_next && (ia->ia_next != oia)) {
+ ia = ia->ia_next;
+ }
+ if (ia->ia_next)
+ ia->ia_next = oia->ia_next;
+ else
+ printf("Didn't unlink nsifadr from list\n");
+ }
+ IFAFREE((&oia->ia_ifa));
+ if (0 == --ns_interfaces) {
+ /*
+ * We reset to virginity and start all over again
+ */
+ ns_thishost = ns_zerohost;
+ }
+ return (0);
+
+ case SIOCAIFADDR:
+ dstIsNew = 0; hostIsNew = 1;
+ if (ia->ia_addr.sns_family == AF_NS) {
+ if (ifra->ifra_addr.sns_len == 0) {
+ ifra->ifra_addr = ia->ia_addr;
+ hostIsNew = 0;
+ } else if (ns_neteq(ifra->ifra_addr.sns_addr,
+ ia->ia_addr.sns_addr))
+ hostIsNew = 0;
+ }
+ if ((ifp->if_flags & IFF_POINTOPOINT) &&
+ (ifra->ifra_dstaddr.sns_family == AF_NS)) {
+ if (hostIsNew == 0)
+ ns_ifscrub(ifp, ia);
+ ia->ia_dstaddr = ifra->ifra_dstaddr;
+ dstIsNew = 1;
+ }
+ if (ifra->ifra_addr.sns_family == AF_NS &&
+ (hostIsNew || dstIsNew))
+ error = ns_ifinit(ifp, ia, &ifra->ifra_addr, 0);
+ return (error);
+
+ default:
+ if (ifp->if_ioctl == 0)
+ return (EOPNOTSUPP);
+ return ((*ifp->if_ioctl)(ifp, cmd, data));
+ }
+}
+
+/*
+* Delete any previous route for an old address.
+*/
+ns_ifscrub(ifp, ia)
+ register struct ifnet *ifp;
+ register struct ns_ifaddr *ia;
+{
+ if (ia->ia_flags & IFA_ROUTE) {
+ if (ifp->if_flags & IFF_POINTOPOINT) {
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
+ } else
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
+ ia->ia_flags &= ~IFA_ROUTE;
+ }
+}
+/*
+ * Initialize an interface's internet address
+ * and routing table entry.
+ */
+ns_ifinit(ifp, ia, sns, scrub)
+ register struct ifnet *ifp;
+ register struct ns_ifaddr *ia;
+ register struct sockaddr_ns *sns;
+{
+ struct sockaddr_ns oldaddr;
+ register union ns_host *h = &ia->ia_addr.sns_addr.x_host;
+ int s = splimp(), error;
+
+ /*
+ * Set up new addresses.
+ */
+ oldaddr = ia->ia_addr;
+ ia->ia_addr = *sns;
+ /*
+ * The convention we shall adopt for naming is that
+ * a supplied address of zero means that "we don't care".
+ * if there is a single interface, use the address of that
+ * interface as our 6 byte host address.
+ * if there are multiple interfaces, use any address already
+ * used.
+ *
+ * Give the interface a chance to initialize
+ * if this is its first address,
+ * and to validate the address if necessary.
+ */
+ if (ns_hosteqnh(ns_thishost, ns_zerohost)) {
+ if (ifp->if_ioctl &&
+ (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, ia))) {
+ ia->ia_addr = oldaddr;
+ splx(s);
+ return (error);
+ }
+ ns_thishost = *h;
+ } else if (ns_hosteqnh(sns->sns_addr.x_host, ns_zerohost)
+ || ns_hosteqnh(sns->sns_addr.x_host, ns_thishost)) {
+ *h = ns_thishost;
+ if (ifp->if_ioctl &&
+ (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, ia))) {
+ ia->ia_addr = oldaddr;
+ splx(s);
+ return (error);
+ }
+ if (!ns_hosteqnh(ns_thishost,*h)) {
+ ia->ia_addr = oldaddr;
+ splx(s);
+ return (EINVAL);
+ }
+ } else {
+ ia->ia_addr = oldaddr;
+ splx(s);
+ return (EINVAL);
+ }
+ ia->ia_ifa.ifa_metric = ifp->if_metric;
+ /*
+ * Add route for the network.
+ */
+ if (scrub) {
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr;
+ ns_ifscrub(ifp, ia);
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ }
+ if (ifp->if_flags & IFF_POINTOPOINT)
+ rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP);
+ else {
+ ia->ia_broadaddr.sns_addr.x_net = ia->ia_addr.sns_addr.x_net;
+ rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP);
+ }
+ ia->ia_flags |= IFA_ROUTE;
+ return (0);
+}
+
+/*
+ * Return address info for specified internet network.
+ */
+struct ns_ifaddr *
+ns_iaonnetof(dst)
+ register struct ns_addr *dst;
+{
+ register struct ns_ifaddr *ia;
+ register struct ns_addr *compare;
+ register struct ifnet *ifp;
+ struct ns_ifaddr *ia_maybe = 0;
+ union ns_net net = dst->x_net;
+
+ for (ia = ns_ifaddr; ia; ia = ia->ia_next) {
+ if (ifp = ia->ia_ifp) {
+ if (ifp->if_flags & IFF_POINTOPOINT) {
+ compare = &satons_addr(ia->ia_dstaddr);
+ if (ns_hosteq(*dst, *compare))
+ return (ia);
+ if (ns_neteqnn(net, ia->ia_addr.sns_addr.x_net))
+ ia_maybe = ia;
+ } else {
+ if (ns_neteqnn(net, ia->ia_addr.sns_addr.x_net))
+ return (ia);
+ }
+ }
+ }
+ return (ia_maybe);
+}
+#endif
diff --git a/sys/netns/ns.h b/sys/netns/ns.h
new file mode 100644
index 0000000..cf51f00
--- /dev/null
+++ b/sys/netns/ns.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ns.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Constants and Structures defined by the Xerox Network Software
+ * per "Internet Transport Protocols", XSIS 028112, December 1981
+ */
+
+/*
+ * Protocols
+ */
+#define NSPROTO_RI 1 /* Routing Information */
+#define NSPROTO_ECHO 2 /* Echo Protocol */
+#define NSPROTO_ERROR 3 /* Error Protocol */
+#define NSPROTO_PE 4 /* Packet Exchange */
+#define NSPROTO_SPP 5 /* Sequenced Packet */
+#define NSPROTO_RAW 255 /* Placemarker*/
+#define NSPROTO_MAX 256 /* Placemarker*/
+
+
+/*
+ * Port/Socket numbers: network standard functions
+ */
+
+#define NSPORT_RI 1 /* Routing Information */
+#define NSPORT_ECHO 2 /* Echo */
+#define NSPORT_RE 3 /* Router Error */
+
+/*
+ * Ports < NSPORT_RESERVED are reserved for priveleged
+ * processes (e.g. root).
+ */
+#define NSPORT_RESERVED 3000
+
+/* flags passed to ns_output as last parameter */
+
+#define NS_FORWARDING 0x1 /* most of idp header exists */
+#define NS_ROUTETOIF 0x10 /* same as SO_DONTROUTE */
+#define NS_ALLOWBROADCAST SO_BROADCAST /* can send broadcast packets */
+
+#define NS_MAXHOPS 15
+
+/* flags passed to get/set socket option */
+#define SO_HEADERS_ON_INPUT 1
+#define SO_HEADERS_ON_OUTPUT 2
+#define SO_DEFAULT_HEADERS 3
+#define SO_LAST_HEADER 4
+#define SO_NSIP_ROUTE 5
+#define SO_SEQNO 6
+#define SO_ALL_PACKETS 7
+#define SO_MTU 8
+
+
+/*
+ * NS addressing
+ */
+union ns_host {
+ u_char c_host[6];
+ u_short s_host[3];
+};
+
+union ns_net {
+ u_char c_net[4];
+ u_short s_net[2];
+};
+
+union ns_net_u {
+ union ns_net net_e;
+ u_long long_e;
+};
+
+struct ns_addr {
+ union ns_net x_net;
+ union ns_host x_host;
+ u_short x_port;
+};
+
+/*
+ * Socket address, Xerox style
+ */
+struct sockaddr_ns {
+ u_char sns_len;
+ u_char sns_family;
+ struct ns_addr sns_addr;
+ char sns_zero[2];
+};
+#define sns_port sns_addr.x_port
+
+#ifdef vax
+#define ns_netof(a) (*(long *) & ((a).x_net)) /* XXX - not needed */
+#endif
+#define ns_neteqnn(a,b) (((a).s_net[0]==(b).s_net[0]) && \
+ ((a).s_net[1]==(b).s_net[1]))
+#define ns_neteq(a,b) ns_neteqnn((a).x_net, (b).x_net)
+#define satons_addr(sa) (((struct sockaddr_ns *)&(sa))->sns_addr)
+#define ns_hosteqnh(s,t) ((s).s_host[0] == (t).s_host[0] && \
+ (s).s_host[1] == (t).s_host[1] && (s).s_host[2] == (t).s_host[2])
+#define ns_hosteq(s,t) (ns_hosteqnh((s).x_host,(t).x_host))
+#define ns_nullhost(x) (((x).x_host.s_host[0]==0) && \
+ ((x).x_host.s_host[1]==0) && ((x).x_host.s_host[2]==0))
+
+#ifdef KERNEL
+extern struct domain nsdomain;
+union ns_host ns_thishost;
+union ns_host ns_zerohost;
+union ns_host ns_broadhost;
+union ns_net ns_zeronet;
+union ns_net ns_broadnet;
+u_short ns_cksum();
+#else
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+extern struct ns_addr ns_addr __P((const char *));
+extern char *ns_ntoa __P((struct ns_addr));
+__END_DECLS
+
+#endif
diff --git a/sys/netns/ns_cksum.c b/sys/netns/ns_cksum.c
new file mode 100644
index 0000000..52eba8bc
--- /dev/null
+++ b/sys/netns/ns_cksum.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 1982, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ns_cksum.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+
+/*
+ * Checksum routine for Network Systems Protocol Packets (Big-Endian).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ */
+
+#define ADDCARRY(x) { if ((x) > 65535) (x) -= 65535; }
+#define FOLD(x) {l_util.l = (x); (x) = l_util.s[0] + l_util.s[1]; ADDCARRY(x);}
+
+u_short
+ns_cksum(m, len)
+ register struct mbuf *m;
+ register int len;
+{
+ register u_short *w;
+ register int sum = 0;
+ register int mlen = 0;
+ register int sum2;
+
+ union {
+ u_short s[2];
+ long l;
+ } l_util;
+
+ for (;m && len; m = m->m_next) {
+ if (m->m_len == 0)
+ continue;
+ /*
+ * Each trip around loop adds in
+ * word from one mbuf segment.
+ */
+ w = mtod(m, u_short *);
+ if (mlen == -1) {
+ /*
+ * There is a byte left from the last segment;
+ * ones-complement add it into the checksum.
+ */
+#if BYTE_ORDER == BIG_ENDIAN
+ sum += *(u_char *)w;
+#else
+ sum += *(u_char *)w << 8;
+#endif
+ sum += sum;
+ w = (u_short *)(1 + (char *)w);
+ mlen = m->m_len - 1;
+ len--;
+ FOLD(sum);
+ } else
+ mlen = m->m_len;
+ if (len < mlen)
+ mlen = len;
+ len -= mlen;
+ /*
+ * We can do a 16 bit ones complement sum using
+ * 32 bit arithmetic registers for adding,
+ * with carries from the low added
+ * into the high (by normal carry-chaining)
+ * so long as we fold back before 16 carries have occured.
+ */
+ if (1 & (int) w)
+ goto uuuuglyy;
+#ifndef TINY
+/* -DTINY reduces the size from 1250 to 550, but slows it down by 22% */
+ while ((mlen -= 32) >= 0) {
+ sum += w[0]; sum += sum; sum += w[1]; sum += sum;
+ sum += w[2]; sum += sum; sum += w[3]; sum += sum;
+ sum += w[4]; sum += sum; sum += w[5]; sum += sum;
+ sum += w[6]; sum += sum; sum += w[7]; sum += sum;
+ FOLD(sum);
+ sum += w[8]; sum += sum; sum += w[9]; sum += sum;
+ sum += w[10]; sum += sum; sum += w[11]; sum += sum;
+ sum += w[12]; sum += sum; sum += w[13]; sum += sum;
+ sum += w[14]; sum += sum; sum += w[15]; sum += sum;
+ FOLD(sum);
+ w += 16;
+ }
+ mlen += 32;
+#endif
+ while ((mlen -= 8) >= 0) {
+ sum += w[0]; sum += sum; sum += w[1]; sum += sum;
+ sum += w[2]; sum += sum; sum += w[3]; sum += sum;
+ FOLD(sum);
+ w += 4;
+ }
+ mlen += 8;
+ while ((mlen -= 2) >= 0) {
+ sum += *w++; sum += sum;
+ }
+ goto commoncase;
+uuuuglyy:
+#if BYTE_ORDER == BIG_ENDIAN
+#define ww(n) (((u_char *)w)[n + n + 1])
+#define vv(n) (((u_char *)w)[n + n])
+#else
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define vv(n) (((u_char *)w)[n + n + 1])
+#define ww(n) (((u_char *)w)[n + n])
+#endif
+#endif
+ sum2 = 0;
+#ifndef TINY
+ while ((mlen -= 32) >= 0) {
+ sum += ww(0); sum += sum; sum += ww(1); sum += sum;
+ sum += ww(2); sum += sum; sum += ww(3); sum += sum;
+ sum += ww(4); sum += sum; sum += ww(5); sum += sum;
+ sum += ww(6); sum += sum; sum += ww(7); sum += sum;
+ FOLD(sum);
+ sum += ww(8); sum += sum; sum += ww(9); sum += sum;
+ sum += ww(10); sum += sum; sum += ww(11); sum += sum;
+ sum += ww(12); sum += sum; sum += ww(13); sum += sum;
+ sum += ww(14); sum += sum; sum += ww(15); sum += sum;
+ FOLD(sum);
+ sum2 += vv(0); sum2 += sum2; sum2 += vv(1); sum2 += sum2;
+ sum2 += vv(2); sum2 += sum2; sum2 += vv(3); sum2 += sum2;
+ sum2 += vv(4); sum2 += sum2; sum2 += vv(5); sum2 += sum2;
+ sum2 += vv(6); sum2 += sum2; sum2 += vv(7); sum2 += sum2;
+ FOLD(sum2);
+ sum2 += vv(8); sum2 += sum2; sum2 += vv(9); sum2 += sum2;
+ sum2 += vv(10); sum2 += sum2; sum2 += vv(11); sum2 += sum2;
+ sum2 += vv(12); sum2 += sum2; sum2 += vv(13); sum2 += sum2;
+ sum2 += vv(14); sum2 += sum2; sum2 += vv(15); sum2 += sum2;
+ FOLD(sum2);
+ w += 16;
+ }
+ mlen += 32;
+#endif
+ while ((mlen -= 8) >= 0) {
+ sum += ww(0); sum += sum; sum += ww(1); sum += sum;
+ sum += ww(2); sum += sum; sum += ww(3); sum += sum;
+ FOLD(sum);
+ sum2 += vv(0); sum2 += sum2; sum2 += vv(1); sum2 += sum2;
+ sum2 += vv(2); sum2 += sum2; sum2 += vv(3); sum2 += sum2;
+ FOLD(sum2);
+ w += 4;
+ }
+ mlen += 8;
+ while ((mlen -= 2) >= 0) {
+ sum += ww(0); sum += sum;
+ sum2 += vv(0); sum2 += sum2;
+ w++;
+ }
+ sum += (sum2 << 8);
+commoncase:
+ if (mlen == -1) {
+#if BYTE_ORDER == BIG_ENDIAN
+ sum += *(u_char *)w << 8;
+#else
+ sum += *(u_char *)w;
+#endif
+ }
+ FOLD(sum);
+ }
+ if (mlen == -1) {
+ /* We had an odd number of bytes to sum; assume a garbage
+ byte of zero and clean up */
+ sum += sum;
+ FOLD(sum);
+ }
+ /*
+ * sum has already been kept to low sixteen bits.
+ * just examine result and exit.
+ */
+ if(sum==0xffff) sum = 0;
+ return (sum);
+}
diff --git a/sys/netns/ns_error.c b/sys/netns/ns_error.c
new file mode 100644
index 0000000..03473a2
--- /dev/null
+++ b/sys/netns/ns_error.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 1984, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ns_error.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/route.h>
+
+#include <netns/ns.h>
+#include <netns/ns_pcb.h>
+#include <netns/idp.h>
+#include <netns/ns_error.h>
+
+#ifdef lint
+#define NS_ERRPRINTFS 1
+#endif
+
+#ifdef NS_ERRPRINTFS
+/*
+ * NS_ERR routines: error generation, receive packet processing, and
+ * routines to turnaround packets back to the originator.
+ */
+int ns_errprintfs = 0;
+#endif
+
+ns_err_x(c)
+{
+ register u_short *w, *lim, *base = ns_errstat.ns_es_codes;
+ u_short x = c;
+
+ /*
+ * zero is a legit error code, handle specially
+ */
+ if (x == 0)
+ return (0);
+ lim = base + NS_ERR_MAX - 1;
+ for (w = base + 1; w < lim; w++) {
+ if (*w == 0)
+ *w = x;
+ if (*w == x)
+ break;
+ }
+ return (w - base);
+}
+
+/*
+ * Generate an error packet of type error
+ * in response to bad packet.
+ */
+
+ns_error(om, type, param)
+ struct mbuf *om;
+ int type;
+{
+ register struct ns_epidp *ep;
+ struct mbuf *m;
+ struct idp *nip;
+ register struct idp *oip = mtod(om, struct idp *);
+ extern int idpcksum;
+
+ /*
+ * If this packet was sent to the echo port,
+ * and nobody was there, just echo it.
+ * (Yes, this is a wart!)
+ */
+ if (type == NS_ERR_NOSOCK &&
+ oip->idp_dna.x_port == htons(2) &&
+ (type = ns_echo(om))==0)
+ return;
+
+#ifdef NS_ERRPRINTFS
+ if (ns_errprintfs)
+ printf("ns_err_error(%x, %d, %d)\n", oip, type, param);
+#endif
+ /*
+ * Don't Generate error packets in response to multicasts.
+ */
+ if (oip->idp_dna.x_host.c_host[0] & 1)
+ goto freeit;
+
+ ns_errstat.ns_es_error++;
+ /*
+ * Make sure that the old IDP packet had 30 bytes of data to return;
+ * if not, don't bother. Also don't EVER error if the old
+ * packet protocol was NS_ERR.
+ */
+ if (oip->idp_len < sizeof(struct idp)) {
+ ns_errstat.ns_es_oldshort++;
+ goto freeit;
+ }
+ if (oip->idp_pt == NSPROTO_ERROR) {
+ ns_errstat.ns_es_oldns_err++;
+ goto freeit;
+ }
+
+ /*
+ * First, formulate ns_err message
+ */
+ m = m_gethdr(M_DONTWAIT, MT_HEADER);
+ if (m == NULL)
+ goto freeit;
+ m->m_len = sizeof(*ep);
+ MH_ALIGN(m, m->m_len);
+ ep = mtod(m, struct ns_epidp *);
+ if ((u_int)type > NS_ERR_TOO_BIG)
+ panic("ns_err_error");
+ ns_errstat.ns_es_outhist[ns_err_x(type)]++;
+ ep->ns_ep_errp.ns_err_num = htons((u_short)type);
+ ep->ns_ep_errp.ns_err_param = htons((u_short)param);
+ bcopy((caddr_t)oip, (caddr_t)&ep->ns_ep_errp.ns_err_idp, 42);
+ nip = &ep->ns_ep_idp;
+ nip->idp_len = sizeof(*ep);
+ nip->idp_len = htons((u_short)nip->idp_len);
+ nip->idp_pt = NSPROTO_ERROR;
+ nip->idp_tc = 0;
+ nip->idp_dna = oip->idp_sna;
+ nip->idp_sna = oip->idp_dna;
+ if (idpcksum) {
+ nip->idp_sum = 0;
+ nip->idp_sum = ns_cksum(m, sizeof(*ep));
+ } else
+ nip->idp_sum = 0xffff;
+ (void) ns_output(m, (struct route *)0, 0);
+
+freeit:
+ m_freem(om);
+}
+
+ns_printhost(p)
+register struct ns_addr *p;
+{
+
+ printf("<net:%x%x,host:%x%x%x,port:%x>",
+ p->x_net.s_net[0],
+ p->x_net.s_net[1],
+ p->x_host.s_host[0],
+ p->x_host.s_host[1],
+ p->x_host.s_host[2],
+ p->x_port);
+
+}
+
+/*
+ * Process a received NS_ERR message.
+ */
+ns_err_input(m)
+ struct mbuf *m;
+{
+ register struct ns_errp *ep;
+ register struct ns_epidp *epidp = mtod(m, struct ns_epidp *);
+ register int i;
+ int type, code, param;
+
+ /*
+ * Locate ns_err structure in mbuf, and check
+ * that not corrupted and of at least minimum length.
+ */
+#ifdef NS_ERRPRINTFS
+ if (ns_errprintfs) {
+ printf("ns_err_input from ");
+ ns_printhost(&epidp->ns_ep_idp.idp_sna);
+ printf("len %d\n", ntohs(epidp->ns_ep_idp.idp_len));
+ }
+#endif
+ i = sizeof (struct ns_epidp);
+ if (((m->m_flags & M_EXT) || m->m_len < i) &&
+ (m = m_pullup(m, i)) == 0) {
+ ns_errstat.ns_es_tooshort++;
+ return;
+ }
+ ep = &(mtod(m, struct ns_epidp *)->ns_ep_errp);
+ type = ntohs(ep->ns_err_num);
+ param = ntohs(ep->ns_err_param);
+ ns_errstat.ns_es_inhist[ns_err_x(type)]++;
+
+#ifdef NS_ERRPRINTFS
+ /*
+ * Message type specific processing.
+ */
+ if (ns_errprintfs)
+ printf("ns_err_input, type %d param %d\n", type, param);
+#endif
+ if (type >= NS_ERR_TOO_BIG) {
+ goto badcode;
+ }
+ ns_errstat.ns_es_outhist[ns_err_x(type)]++;
+ switch (type) {
+
+ case NS_ERR_UNREACH_HOST:
+ code = PRC_UNREACH_NET;
+ goto deliver;
+
+ case NS_ERR_TOO_OLD:
+ code = PRC_TIMXCEED_INTRANS;
+ goto deliver;
+
+ case NS_ERR_TOO_BIG:
+ code = PRC_MSGSIZE;
+ goto deliver;
+
+ case NS_ERR_FULLUP:
+ code = PRC_QUENCH;
+ goto deliver;
+
+ case NS_ERR_NOSOCK:
+ code = PRC_UNREACH_PORT;
+ goto deliver;
+
+ case NS_ERR_UNSPEC_T:
+ case NS_ERR_BADSUM_T:
+ case NS_ERR_BADSUM:
+ case NS_ERR_UNSPEC:
+ code = PRC_PARAMPROB;
+ goto deliver;
+
+ deliver:
+ /*
+ * Problem with datagram; advise higher level routines.
+ */
+#ifdef NS_ERRPRINTFS
+ if (ns_errprintfs)
+ printf("deliver to protocol %d\n",
+ ep->ns_err_idp.idp_pt);
+#endif
+ switch(ep->ns_err_idp.idp_pt) {
+ case NSPROTO_SPP:
+ spp_ctlinput(code, (caddr_t)ep);
+ break;
+
+ default:
+ idp_ctlinput(code, (caddr_t)ep);
+ }
+
+ goto freeit;
+
+ default:
+ badcode:
+ ns_errstat.ns_es_badcode++;
+ goto freeit;
+
+ }
+freeit:
+ m_freem(m);
+}
+
+#ifdef notdef
+u_long
+nstime()
+{
+ int s = splclock();
+ u_long t;
+
+ t = (time.tv_sec % (24*60*60)) * 1000 + time.tv_usec / 1000;
+ splx(s);
+ return (htonl(t));
+}
+#endif
+
+ns_echo(m)
+struct mbuf *m;
+{
+ register struct idp *idp = mtod(m, struct idp *);
+ register struct echo {
+ struct idp ec_idp;
+ u_short ec_op; /* Operation, 1 = request, 2 = reply */
+ } *ec = (struct echo *)idp;
+ struct ns_addr temp;
+
+ if (idp->idp_pt!=NSPROTO_ECHO) return(NS_ERR_NOSOCK);
+ if (ec->ec_op!=htons(1)) return(NS_ERR_UNSPEC);
+
+ ec->ec_op = htons(2);
+
+ temp = idp->idp_dna;
+ idp->idp_dna = idp->idp_sna;
+ idp->idp_sna = temp;
+
+ if (idp->idp_sum != 0xffff) {
+ idp->idp_sum = 0;
+ idp->idp_sum = ns_cksum(m,
+ (int)(((ntohs(idp->idp_len) - 1)|1)+1));
+ }
+ (void) ns_output(m, (struct route *)0, NS_FORWARDING);
+ return(0);
+}
diff --git a/sys/netns/ns_error.h b/sys/netns/ns_error.h
new file mode 100644
index 0000000..992911f
--- /dev/null
+++ b/sys/netns/ns_error.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 1984, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ns_error.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Xerox NS error messages
+ */
+
+struct ns_errp {
+ u_short ns_err_num; /* Error Number */
+ u_short ns_err_param; /* Error Parameter */
+ struct idp ns_err_idp; /* Initial segment of offending
+ packet */
+ u_char ns_err_lev2[12]; /* at least this much higher
+ level protocol */
+};
+struct ns_epidp {
+ struct idp ns_ep_idp;
+ struct ns_errp ns_ep_errp;
+};
+
+#define NS_ERR_UNSPEC 0 /* Unspecified Error detected at dest. */
+#define NS_ERR_BADSUM 1 /* Bad Checksum detected at dest */
+#define NS_ERR_NOSOCK 2 /* Specified socket does not exist at dest*/
+#define NS_ERR_FULLUP 3 /* Dest. refuses packet due to resource lim.*/
+#define NS_ERR_UNSPEC_T 0x200 /* Unspec. Error occured before reaching dest*/
+#define NS_ERR_BADSUM_T 0x201 /* Bad Checksum detected in transit */
+#define NS_ERR_UNREACH_HOST 0x202 /* Dest cannot be reached from here*/
+#define NS_ERR_TOO_OLD 0x203 /* Packet x'd 15 routers without delivery*/
+#define NS_ERR_TOO_BIG 0x204 /* Packet too large to be forwarded through
+ some intermediate gateway. The error
+ parameter field contains the max packet
+ size that can be accommodated */
+#define NS_ERR_MAX 20
+
+/*
+ * Variables related to this implementation
+ * of the network systems error message protocol.
+ */
+struct ns_errstat {
+/* statistics related to ns_err packets generated */
+ int ns_es_error; /* # of calls to ns_error */
+ int ns_es_oldshort; /* no error 'cuz old ip too short */
+ int ns_es_oldns_err; /* no error 'cuz old was ns_err */
+ int ns_es_outhist[NS_ERR_MAX];
+/* statistics related to input messages processed */
+ int ns_es_badcode; /* ns_err_code out of range */
+ int ns_es_tooshort; /* packet < IDP_MINLEN */
+ int ns_es_checksum; /* bad checksum */
+ int ns_es_badlen; /* calculated bound mismatch */
+ int ns_es_reflect; /* number of responses */
+ int ns_es_inhist[NS_ERR_MAX];
+ u_short ns_es_codes[NS_ERR_MAX];/* which error code for outhist
+ since we might not know all */
+};
+
+#ifdef KERNEL
+struct ns_errstat ns_errstat;
+#endif
diff --git a/sys/netns/ns_if.h b/sys/netns/ns_if.h
new file mode 100644
index 0000000..3abb284
--- /dev/null
+++ b/sys/netns/ns_if.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ns_if.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Interface address, xerox version. One of these structures
+ * is allocated for each interface with an internet address.
+ * The ifaddr structure contains the protocol-independent part
+ * of the structure and is assumed to be first.
+ */
+
+struct ns_ifaddr {
+ struct ifaddr ia_ifa; /* protocol-independent info */
+#define ia_ifp ia_ifa.ifa_ifp
+#define ia_flags ia_ifa.ifa_flags
+ struct ns_ifaddr *ia_next; /* next in list of xerox addresses */
+ struct sockaddr_ns ia_addr; /* reserve space for my address */
+ struct sockaddr_ns ia_dstaddr; /* space for my broadcast address */
+#define ia_broadaddr ia_dstaddr
+ struct sockaddr_ns ia_netmask; /* space for my network mask */
+};
+
+struct ns_aliasreq {
+ char ifra_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ struct sockaddr_ns ifra_addr;
+ struct sockaddr_ns ifra_broadaddr;
+#define ifra_dstaddr ifra_broadaddr
+};
+/*
+ * Given a pointer to an ns_ifaddr (ifaddr),
+ * return a pointer to the addr as a sockadd_ns.
+ */
+
+#define IA_SNS(ia) (&(((struct ns_ifaddr *)(ia))->ia_addr))
+
+/* This is not the right place for this but where is? */
+#define ETHERTYPE_NS 0x0600
+
+#ifdef NSIP
+struct nsip_req {
+ struct sockaddr rq_ns; /* must be ns format destination */
+ struct sockaddr rq_ip; /* must be ip format gateway */
+ short rq_flags;
+};
+#endif
+
+#ifdef KERNEL
+struct ns_ifaddr *ns_ifaddr;
+struct ns_ifaddr *ns_iaonnetof();
+struct ifqueue nsintrq; /* XNS input packet queue */
+#endif
diff --git a/sys/netns/ns_input.c b/sys/netns/ns_input.c
new file mode 100644
index 0000000..7a6e1bab
--- /dev/null
+++ b/sys/netns/ns_input.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ns_input.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/raw_cb.h>
+
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#include <netns/ns_pcb.h>
+#include <netns/idp.h>
+#include <netns/idp_var.h>
+#include <netns/ns_error.h>
+
+/*
+ * NS initialization.
+ */
+union ns_host ns_thishost;
+union ns_host ns_zerohost;
+union ns_host ns_broadhost;
+union ns_net ns_zeronet;
+union ns_net ns_broadnet;
+struct sockaddr_ns ns_netmask, ns_hostmask;
+
+static u_short allones[] = {-1, -1, -1};
+
+struct nspcb nspcb;
+struct nspcb nsrawpcb;
+
+struct ifqueue nsintrq;
+int nsqmaxlen = IFQ_MAXLEN;
+
+int idpcksum = 1;
+long ns_pexseq;
+
+ns_init()
+{
+ extern struct timeval time;
+
+ ns_broadhost = * (union ns_host *) allones;
+ ns_broadnet = * (union ns_net *) allones;
+ nspcb.nsp_next = nspcb.nsp_prev = &nspcb;
+ nsrawpcb.nsp_next = nsrawpcb.nsp_prev = &nsrawpcb;
+ nsintrq.ifq_maxlen = nsqmaxlen;
+ ns_pexseq = time.tv_usec;
+ ns_netmask.sns_len = 6;
+ ns_netmask.sns_addr.x_net = ns_broadnet;
+ ns_hostmask.sns_len = 12;
+ ns_hostmask.sns_addr.x_net = ns_broadnet;
+ ns_hostmask.sns_addr.x_host = ns_broadhost;
+}
+
+/*
+ * Idp input routine. Pass to next level.
+ */
+int nsintr_getpck = 0;
+int nsintr_swtch = 0;
+nsintr()
+{
+ register struct idp *idp;
+ register struct mbuf *m;
+ register struct nspcb *nsp;
+ register int i;
+ int len, s, error;
+ char oddpacketp;
+
+next:
+ /*
+ * Get next datagram off input queue and get IDP header
+ * in first mbuf.
+ */
+ s = splimp();
+ IF_DEQUEUE(&nsintrq, m);
+ splx(s);
+ nsintr_getpck++;
+ if (m == 0)
+ return;
+ if ((m->m_flags & M_EXT || m->m_len < sizeof (struct idp)) &&
+ (m = m_pullup(m, sizeof (struct idp))) == 0) {
+ idpstat.idps_toosmall++;
+ goto next;
+ }
+
+ /*
+ * Give any raw listeners a crack at the packet
+ */
+ for (nsp = nsrawpcb.nsp_next; nsp != &nsrawpcb; nsp = nsp->nsp_next) {
+ struct mbuf *m1 = m_copy(m, 0, (int)M_COPYALL);
+ if (m1) idp_input(m1, nsp);
+ }
+
+ idp = mtod(m, struct idp *);
+ len = ntohs(idp->idp_len);
+ if (oddpacketp = len & 1) {
+ len++; /* If this packet is of odd length,
+ preserve garbage byte for checksum */
+ }
+
+ /*
+ * Check that the amount of data in the buffers
+ * is as at least much as the IDP header would have us expect.
+ * Trim mbufs if longer than we expect.
+ * Drop packet if shorter than we expect.
+ */
+ if (m->m_pkthdr.len < len) {
+ idpstat.idps_tooshort++;
+ goto bad;
+ }
+ if (m->m_pkthdr.len > len) {
+ if (m->m_len == m->m_pkthdr.len) {
+ m->m_len = len;
+ m->m_pkthdr.len = len;
+ } else
+ m_adj(m, len - m->m_pkthdr.len);
+ }
+ if (idpcksum && ((i = idp->idp_sum)!=0xffff)) {
+ idp->idp_sum = 0;
+ if (i != (idp->idp_sum = ns_cksum(m, len))) {
+ idpstat.idps_badsum++;
+ idp->idp_sum = i;
+ if (ns_hosteqnh(ns_thishost, idp->idp_dna.x_host))
+ error = NS_ERR_BADSUM;
+ else
+ error = NS_ERR_BADSUM_T;
+ ns_error(m, error, 0);
+ goto next;
+ }
+ }
+ /*
+ * Is this a directed broadcast?
+ */
+ if (ns_hosteqnh(ns_broadhost,idp->idp_dna.x_host)) {
+ if ((!ns_neteq(idp->idp_dna, idp->idp_sna)) &&
+ (!ns_neteqnn(idp->idp_dna.x_net, ns_broadnet)) &&
+ (!ns_neteqnn(idp->idp_sna.x_net, ns_zeronet)) &&
+ (!ns_neteqnn(idp->idp_dna.x_net, ns_zeronet)) ) {
+ /*
+ * Look to see if I need to eat this packet.
+ * Algorithm is to forward all young packets
+ * and prematurely age any packets which will
+ * by physically broadcasted.
+ * Any very old packets eaten without forwarding
+ * would die anyway.
+ *
+ * Suggestion of Bill Nesheim, Cornell U.
+ */
+ if (idp->idp_tc < NS_MAXHOPS) {
+ idp_forward(m);
+ goto next;
+ }
+ }
+ /*
+ * Is this our packet? If not, forward.
+ */
+ } else if (!ns_hosteqnh(ns_thishost,idp->idp_dna.x_host)) {
+ idp_forward(m);
+ goto next;
+ }
+ /*
+ * Locate pcb for datagram.
+ */
+ nsp = ns_pcblookup(&idp->idp_sna, idp->idp_dna.x_port, NS_WILDCARD);
+ /*
+ * Switch out to protocol's input routine.
+ */
+ nsintr_swtch++;
+ if (nsp) {
+ if (oddpacketp) {
+ m_adj(m, -1);
+ }
+ if ((nsp->nsp_flags & NSP_ALL_PACKETS)==0)
+ switch (idp->idp_pt) {
+
+ case NSPROTO_SPP:
+ spp_input(m, nsp);
+ goto next;
+
+ case NSPROTO_ERROR:
+ ns_err_input(m);
+ goto next;
+ }
+ idp_input(m, nsp);
+ } else {
+ ns_error(m, NS_ERR_NOSOCK, 0);
+ }
+ goto next;
+
+bad:
+ m_freem(m);
+ goto next;
+}
+
+u_char nsctlerrmap[PRC_NCMDS] = {
+ ECONNABORTED, ECONNABORTED, 0, 0,
+ 0, 0, EHOSTDOWN, EHOSTUNREACH,
+ ENETUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED,
+ EMSGSIZE, 0, 0, 0,
+ 0, 0, 0, 0
+};
+
+int idp_donosocks = 1;
+
+idp_ctlinput(cmd, arg)
+ int cmd;
+ caddr_t arg;
+{
+ struct ns_addr *ns;
+ struct nspcb *nsp;
+ struct ns_errp *errp;
+ int idp_abort();
+ extern struct nspcb *idp_drop();
+ int type;
+
+ if (cmd < 0 || cmd > PRC_NCMDS)
+ return;
+ if (nsctlerrmap[cmd] == 0)
+ return; /* XXX */
+ type = NS_ERR_UNREACH_HOST;
+ switch (cmd) {
+ struct sockaddr_ns *sns;
+
+ case PRC_IFDOWN:
+ case PRC_HOSTDEAD:
+ case PRC_HOSTUNREACH:
+ sns = (struct sockaddr_ns *)arg;
+ if (sns->sns_family != AF_NS)
+ return;
+ ns = &sns->sns_addr;
+ break;
+
+ default:
+ errp = (struct ns_errp *)arg;
+ ns = &errp->ns_err_idp.idp_dna;
+ type = errp->ns_err_num;
+ type = ntohs((u_short)type);
+ }
+ switch (type) {
+
+ case NS_ERR_UNREACH_HOST:
+ ns_pcbnotify(ns, (int)nsctlerrmap[cmd], idp_abort, (long)0);
+ break;
+
+ case NS_ERR_NOSOCK:
+ nsp = ns_pcblookup(ns, errp->ns_err_idp.idp_sna.x_port,
+ NS_WILDCARD);
+ if(nsp && idp_donosocks && ! ns_nullhost(nsp->nsp_faddr))
+ (void) idp_drop(nsp, (int)nsctlerrmap[cmd]);
+ }
+}
+
+int idpprintfs = 0;
+int idpforwarding = 1;
+/*
+ * Forward a packet. If some error occurs return the sender
+ * an error packet. Note we can't always generate a meaningful
+ * error message because the NS errors don't have a large enough repetoire
+ * of codes and types.
+ */
+struct route idp_droute;
+struct route idp_sroute;
+
+idp_forward(m)
+struct mbuf *m;
+{
+ register struct idp *idp = mtod(m, struct idp *);
+ register int error, type, code;
+ struct mbuf *mcopy = NULL;
+ int agedelta = 1;
+ int flags = NS_FORWARDING;
+ int ok_there = 0;
+ int ok_back = 0;
+
+ if (idpprintfs) {
+ printf("forward: src ");
+ ns_printhost(&idp->idp_sna);
+ printf(", dst ");
+ ns_printhost(&idp->idp_dna);
+ printf("hop count %d\n", idp->idp_tc);
+ }
+ if (idpforwarding == 0) {
+ /* can't tell difference between net and host */
+ type = NS_ERR_UNREACH_HOST, code = 0;
+ goto senderror;
+ }
+ idp->idp_tc++;
+ if (idp->idp_tc > NS_MAXHOPS) {
+ type = NS_ERR_TOO_OLD, code = 0;
+ goto senderror;
+ }
+ /*
+ * Save at most 42 bytes of the packet in case
+ * we need to generate an NS error message to the src.
+ */
+ mcopy = m_copy(m, 0, imin((int)ntohs(idp->idp_len), 42));
+
+ if ((ok_there = idp_do_route(&idp->idp_dna,&idp_droute))==0) {
+ type = NS_ERR_UNREACH_HOST, code = 0;
+ goto senderror;
+ }
+ /*
+ * Here we think about forwarding broadcast packets,
+ * so we try to insure that it doesn't go back out
+ * on the interface it came in on. Also, if we
+ * are going to physically broadcast this, let us
+ * age the packet so we can eat it safely the second time around.
+ */
+ if (idp->idp_dna.x_host.c_host[0] & 0x1) {
+ struct ns_ifaddr *ia = ns_iaonnetof(&idp->idp_dna);
+ struct ifnet *ifp;
+ if (ia) {
+ /* I'm gonna hafta eat this packet */
+ agedelta += NS_MAXHOPS - idp->idp_tc;
+ idp->idp_tc = NS_MAXHOPS;
+ }
+ if ((ok_back = idp_do_route(&idp->idp_sna,&idp_sroute))==0) {
+ /* error = ENETUNREACH; He'll never get it! */
+ m_freem(m);
+ goto cleanup;
+ }
+ if (idp_droute.ro_rt &&
+ (ifp=idp_droute.ro_rt->rt_ifp) &&
+ idp_sroute.ro_rt &&
+ (ifp!=idp_sroute.ro_rt->rt_ifp)) {
+ flags |= NS_ALLOWBROADCAST;
+ } else {
+ type = NS_ERR_UNREACH_HOST, code = 0;
+ goto senderror;
+ }
+ }
+ /* need to adjust checksum */
+ if (idp->idp_sum!=0xffff) {
+ union bytes {
+ u_char c[4];
+ u_short s[2];
+ long l;
+ } x;
+ register int shift;
+ x.l = 0; x.c[0] = agedelta;
+ shift = (((((int)ntohs(idp->idp_len))+1)>>1)-2) & 0xf;
+ x.l = idp->idp_sum + (x.s[0] << shift);
+ x.l = x.s[0] + x.s[1];
+ x.l = x.s[0] + x.s[1];
+ if (x.l==0xffff) idp->idp_sum = 0; else idp->idp_sum = x.l;
+ }
+ if ((error = ns_output(m, &idp_droute, flags)) &&
+ (mcopy!=NULL)) {
+ idp = mtod(mcopy, struct idp *);
+ type = NS_ERR_UNSPEC_T, code = 0;
+ switch (error) {
+
+ case ENETUNREACH:
+ case EHOSTDOWN:
+ case EHOSTUNREACH:
+ case ENETDOWN:
+ case EPERM:
+ type = NS_ERR_UNREACH_HOST;
+ break;
+
+ case EMSGSIZE:
+ type = NS_ERR_TOO_BIG;
+ code = 576; /* too hard to figure out mtu here */
+ break;
+
+ case ENOBUFS:
+ type = NS_ERR_UNSPEC_T;
+ break;
+ }
+ mcopy = NULL;
+ senderror:
+ ns_error(m, type, code);
+ }
+cleanup:
+ if (ok_there)
+ idp_undo_route(&idp_droute);
+ if (ok_back)
+ idp_undo_route(&idp_sroute);
+ if (mcopy != NULL)
+ m_freem(mcopy);
+}
+
+idp_do_route(src, ro)
+struct ns_addr *src;
+struct route *ro;
+{
+
+ struct sockaddr_ns *dst;
+
+ bzero((caddr_t)ro, sizeof (*ro));
+ dst = (struct sockaddr_ns *)&ro->ro_dst;
+
+ dst->sns_len = sizeof(*dst);
+ dst->sns_family = AF_NS;
+ dst->sns_addr = *src;
+ dst->sns_addr.x_port = 0;
+ rtalloc(ro);
+ if (ro->ro_rt == 0 || ro->ro_rt->rt_ifp == 0) {
+ return (0);
+ }
+ ro->ro_rt->rt_use++;
+ return (1);
+}
+
+idp_undo_route(ro)
+register struct route *ro;
+{
+ if (ro->ro_rt) {RTFREE(ro->ro_rt);}
+}
+
+ns_watch_output(m, ifp)
+struct mbuf *m;
+struct ifnet *ifp;
+{
+ register struct nspcb *nsp;
+ register struct ifaddr *ifa;
+ /*
+ * Give any raw listeners a crack at the packet
+ */
+ for (nsp = nsrawpcb.nsp_next; nsp != &nsrawpcb; nsp = nsp->nsp_next) {
+ struct mbuf *m0 = m_copy(m, 0, (int)M_COPYALL);
+ if (m0) {
+ register struct idp *idp;
+
+ M_PREPEND(m0, sizeof (*idp), M_DONTWAIT);
+ if (m0 == NULL)
+ continue;
+ idp = mtod(m0, struct idp *);
+ idp->idp_sna.x_net = ns_zeronet;
+ idp->idp_sna.x_host = ns_thishost;
+ if (ifp && (ifp->if_flags & IFF_POINTOPOINT))
+ for(ifa = ifp->if_addrlist; ifa;
+ ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family==AF_NS) {
+ idp->idp_sna = IA_SNS(ifa)->sns_addr;
+ break;
+ }
+ }
+ idp->idp_len = ntohl(m0->m_pkthdr.len);
+ idp_input(m0, nsp);
+ }
+ }
+}
diff --git a/sys/netns/ns_ip.c b/sys/netns/ns_ip.c
new file mode 100644
index 0000000..09deb8f
--- /dev/null
+++ b/sys/netns/ns_ip.c
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ns_ip.c 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Software interface driver for encapsulating ns in ip.
+ */
+
+#ifdef NSIP
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/protosw.h>
+
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+
+#include <machine/mtpr.h>
+
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#include <netns/idp.h>
+
+struct ifnet_en {
+ struct ifnet ifen_ifnet;
+ struct route ifen_route;
+ struct in_addr ifen_src;
+ struct in_addr ifen_dst;
+ struct ifnet_en *ifen_next;
+};
+
+int nsipoutput(), nsipioctl(), nsipstart();
+#define LOMTU (1024+512);
+
+struct ifnet nsipif;
+struct ifnet_en *nsip_list; /* list of all hosts and gateways or
+ broadcast addrs */
+
+struct ifnet_en *
+nsipattach()
+{
+ register struct ifnet_en *m;
+ register struct ifnet *ifp;
+
+ if (nsipif.if_mtu == 0) {
+ ifp = &nsipif;
+ ifp->if_name = "nsip";
+ ifp->if_mtu = LOMTU;
+ ifp->if_ioctl = nsipioctl;
+ ifp->if_output = nsipoutput;
+ ifp->if_start = nsipstart;
+ ifp->if_flags = IFF_POINTOPOINT;
+ }
+
+ MALLOC((m), struct ifnet_en *, sizeof(*m), M_PCB, M_NOWAIT);
+ if (m == NULL) return (NULL);
+ m->ifen_next = nsip_list;
+ nsip_list = m;
+ ifp = &m->ifen_ifnet;
+
+ ifp->if_name = "nsip";
+ ifp->if_mtu = LOMTU;
+ ifp->if_ioctl = nsipioctl;
+ ifp->if_output = nsipoutput;
+ ifp->if_start = nsipstart;
+ ifp->if_flags = IFF_POINTOPOINT;
+ ifp->if_unit = nsipif.if_unit++;
+ if_attach(ifp);
+
+ return (m);
+}
+
+
+/*
+ * Process an ioctl request.
+ */
+/* ARGSUSED */
+nsipioctl(ifp, cmd, data)
+ register struct ifnet *ifp;
+ int cmd;
+ caddr_t data;
+{
+ int error = 0;
+ struct ifreq *ifr;
+
+ switch (cmd) {
+
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ /* fall into: */
+
+ case SIOCSIFDSTADDR:
+ /*
+ * Everything else is done at a higher level.
+ */
+ break;
+
+ case SIOCSIFFLAGS:
+ ifr = (struct ifreq *)data;
+ if ((ifr->ifr_flags & IFF_UP) == 0)
+ error = nsip_free(ifp);
+
+
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+
+struct mbuf *nsip_badlen;
+struct mbuf *nsip_lastin;
+int nsip_hold_input;
+
+idpip_input(m, ifp)
+ register struct mbuf *m;
+ struct ifnet *ifp;
+{
+ register struct ip *ip;
+ register struct idp *idp;
+ register struct ifqueue *ifq = &nsintrq;
+ int len, s;
+
+ if (nsip_hold_input) {
+ if (nsip_lastin) {
+ m_freem(nsip_lastin);
+ }
+ nsip_lastin = m_copym(m, 0, (int)M_COPYALL, M_DONTWAIT);
+ }
+ /*
+ * Get IP and IDP header together in first mbuf.
+ */
+ nsipif.if_ipackets++;
+ s = sizeof (struct ip) + sizeof (struct idp);
+ if (((m->m_flags & M_EXT) || m->m_len < s) &&
+ (m = m_pullup(m, s)) == 0) {
+ nsipif.if_ierrors++;
+ return;
+ }
+ ip = mtod(m, struct ip *);
+ if (ip->ip_hl > (sizeof (struct ip) >> 2)) {
+ ip_stripoptions(m, (struct mbuf *)0);
+ if (m->m_len < s) {
+ if ((m = m_pullup(m, s)) == 0) {
+ nsipif.if_ierrors++;
+ return;
+ }
+ ip = mtod(m, struct ip *);
+ }
+ }
+
+ /*
+ * Make mbuf data length reflect IDP length.
+ * If not enough data to reflect IDP length, drop.
+ */
+ m->m_data += sizeof (struct ip);
+ m->m_len -= sizeof (struct ip);
+ m->m_pkthdr.len -= sizeof (struct ip);
+ idp = mtod(m, struct idp *);
+ len = ntohs(idp->idp_len);
+ if (len & 1) len++; /* Preserve Garbage Byte */
+ if (ip->ip_len != len) {
+ if (len > ip->ip_len) {
+ nsipif.if_ierrors++;
+ if (nsip_badlen) m_freem(nsip_badlen);
+ nsip_badlen = m;
+ return;
+ }
+ /* Any extra will be trimmed off by the NS routines */
+ }
+
+ /*
+ * Place interface pointer before the data
+ * for the receiving protocol.
+ */
+ m->m_pkthdr.rcvif = ifp;
+ /*
+ * Deliver to NS
+ */
+ s = splimp();
+ if (IF_QFULL(ifq)) {
+ IF_DROP(ifq);
+bad:
+ m_freem(m);
+ splx(s);
+ return;
+ }
+ IF_ENQUEUE(ifq, m);
+ schednetisr(NETISR_NS);
+ splx(s);
+ return;
+}
+
+/* ARGSUSED */
+nsipoutput(ifn, m, dst)
+ struct ifnet_en *ifn;
+ register struct mbuf *m;
+ struct sockaddr *dst;
+{
+
+ register struct ip *ip;
+ register struct route *ro = &(ifn->ifen_route);
+ register int len = 0;
+ register struct idp *idp = mtod(m, struct idp *);
+ int error;
+
+ ifn->ifen_ifnet.if_opackets++;
+ nsipif.if_opackets++;
+
+
+ /*
+ * Calculate data length and make space
+ * for IP header.
+ */
+ len = ntohs(idp->idp_len);
+ if (len & 1) len++; /* Preserve Garbage Byte */
+ /* following clause not necessary on vax */
+ if (3 & (int)m->m_data) {
+ /* force longword alignment of ip hdr */
+ struct mbuf *m0 = m_gethdr(MT_HEADER, M_DONTWAIT);
+ if (m0 == 0) {
+ m_freem(m);
+ return (ENOBUFS);
+ }
+ MH_ALIGN(m0, sizeof (struct ip));
+ m0->m_flags = m->m_flags & M_COPYFLAGS;
+ m0->m_next = m;
+ m0->m_len = sizeof (struct ip);
+ m0->m_pkthdr.len = m0->m_len + m->m_len;
+ m->m_flags &= ~M_PKTHDR;
+ } else {
+ M_PREPEND(m, sizeof (struct ip), M_DONTWAIT);
+ if (m == 0)
+ return (ENOBUFS);
+ }
+ /*
+ * Fill in IP header.
+ */
+ ip = mtod(m, struct ip *);
+ *(long *)ip = 0;
+ ip->ip_p = IPPROTO_IDP;
+ ip->ip_src = ifn->ifen_src;
+ ip->ip_dst = ifn->ifen_dst;
+ ip->ip_len = (u_short)len + sizeof (struct ip);
+ ip->ip_ttl = MAXTTL;
+
+ /*
+ * Output final datagram.
+ */
+ error = (ip_output(m, (struct mbuf *)0, ro, SO_BROADCAST, NULL));
+ if (error) {
+ ifn->ifen_ifnet.if_oerrors++;
+ ifn->ifen_ifnet.if_ierrors = error;
+ }
+ return (error);
+bad:
+ m_freem(m);
+ return (ENETUNREACH);
+}
+
+nsipstart(ifp)
+struct ifnet *ifp;
+{
+ panic("nsip_start called\n");
+}
+
+struct ifreq ifr = {"nsip0"};
+
+nsip_route(m)
+ register struct mbuf *m;
+{
+ register struct nsip_req *rq = mtod(m, struct nsip_req *);
+ struct sockaddr_ns *ns_dst = (struct sockaddr_ns *)&rq->rq_ns;
+ struct sockaddr_in *ip_dst = (struct sockaddr_in *)&rq->rq_ip;
+ struct route ro;
+ struct ifnet_en *ifn;
+ struct sockaddr_in *src;
+
+ /*
+ * First, make sure we already have an ns address:
+ */
+ if (ns_hosteqnh(ns_thishost, ns_zerohost))
+ return (EADDRNOTAVAIL);
+ /*
+ * Now, determine if we can get to the destination
+ */
+ bzero((caddr_t)&ro, sizeof (ro));
+ ro.ro_dst = *(struct sockaddr *)ip_dst;
+ rtalloc(&ro);
+ if (ro.ro_rt == 0 || ro.ro_rt->rt_ifp == 0) {
+ return (ENETUNREACH);
+ }
+
+ /*
+ * And see how he's going to get back to us:
+ * i.e., what return ip address do we use?
+ */
+ {
+ register struct in_ifaddr *ia;
+ struct ifnet *ifp = ro.ro_rt->rt_ifp;
+
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+ if (ia == 0)
+ ia = in_ifaddr;
+ if (ia == 0) {
+ RTFREE(ro.ro_rt);
+ return (EADDRNOTAVAIL);
+ }
+ src = (struct sockaddr_in *)&ia->ia_addr;
+ }
+
+ /*
+ * Is there a free (pseudo-)interface or space?
+ */
+ for (ifn = nsip_list; ifn; ifn = ifn->ifen_next) {
+ if ((ifn->ifen_ifnet.if_flags & IFF_UP) == 0)
+ break;
+ }
+ if (ifn == NULL)
+ ifn = nsipattach();
+ if (ifn == NULL) {
+ RTFREE(ro.ro_rt);
+ return (ENOBUFS);
+ }
+ ifn->ifen_route = ro;
+ ifn->ifen_dst = ip_dst->sin_addr;
+ ifn->ifen_src = src->sin_addr;
+
+ /*
+ * now configure this as a point to point link
+ */
+ ifr.ifr_name[4] = '0' + nsipif.if_unit - 1;
+ ifr.ifr_dstaddr = * (struct sockaddr *) ns_dst;
+ (void)ns_control((struct socket *)0, (int)SIOCSIFDSTADDR, (caddr_t)&ifr,
+ (struct ifnet *)ifn);
+ satons_addr(ifr.ifr_addr).x_host = ns_thishost;
+ return (ns_control((struct socket *)0, (int)SIOCSIFADDR, (caddr_t)&ifr,
+ (struct ifnet *)ifn));
+}
+
+nsip_free(ifp)
+struct ifnet *ifp;
+{
+ register struct ifnet_en *ifn = (struct ifnet_en *)ifp;
+ struct route *ro = & ifn->ifen_route;
+
+ if (ro->ro_rt) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = 0;
+ }
+ ifp->if_flags &= ~IFF_UP;
+ return (0);
+}
+
+nsip_ctlinput(cmd, sa)
+ int cmd;
+ struct sockaddr *sa;
+{
+ extern u_char inetctlerrmap[];
+ struct sockaddr_in *sin;
+ int in_rtchange();
+
+ if ((unsigned)cmd >= PRC_NCMDS)
+ return;
+ if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK)
+ return;
+ sin = (struct sockaddr_in *)sa;
+ if (sin->sin_addr.s_addr == INADDR_ANY)
+ return;
+
+ switch (cmd) {
+
+ case PRC_ROUTEDEAD:
+ case PRC_REDIRECT_NET:
+ case PRC_REDIRECT_HOST:
+ case PRC_REDIRECT_TOSNET:
+ case PRC_REDIRECT_TOSHOST:
+ nsip_rtchange(&sin->sin_addr);
+ break;
+ }
+}
+
+nsip_rtchange(dst)
+ register struct in_addr *dst;
+{
+ register struct ifnet_en *ifn;
+
+ for (ifn = nsip_list; ifn; ifn = ifn->ifen_next) {
+ if (ifn->ifen_dst.s_addr == dst->s_addr &&
+ ifn->ifen_route.ro_rt) {
+ RTFREE(ifn->ifen_route.ro_rt);
+ ifn->ifen_route.ro_rt = 0;
+ }
+ }
+}
+#endif
diff --git a/sys/netns/ns_output.c b/sys/netns/ns_output.c
new file mode 100644
index 0000000..4c9f364
--- /dev/null
+++ b/sys/netns/ns_output.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ns_output.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#include <netns/idp.h>
+#include <netns/idp_var.h>
+
+#ifdef vax
+#include <machine/mtpr.h>
+#endif
+int ns_hold_output = 0;
+int ns_copy_output = 0;
+int ns_output_cnt = 0;
+struct mbuf *ns_lastout;
+
+ns_output(m0, ro, flags)
+ struct mbuf *m0;
+ struct route *ro;
+ int flags;
+{
+ register struct idp *idp = mtod(m0, struct idp *);
+ register struct ifnet *ifp = 0;
+ int error = 0;
+ struct route idproute;
+ struct sockaddr_ns *dst;
+ extern int idpcksum;
+
+ if (ns_hold_output) {
+ if (ns_lastout) {
+ (void)m_free(ns_lastout);
+ }
+ ns_lastout = m_copy(m0, 0, (int)M_COPYALL);
+ }
+ /*
+ * Route packet.
+ */
+ if (ro == 0) {
+ ro = &idproute;
+ bzero((caddr_t)ro, sizeof (*ro));
+ }
+ dst = (struct sockaddr_ns *)&ro->ro_dst;
+ if (ro->ro_rt == 0) {
+ dst->sns_family = AF_NS;
+ dst->sns_len = sizeof (*dst);
+ dst->sns_addr = idp->idp_dna;
+ dst->sns_addr.x_port = 0;
+ /*
+ * If routing to interface only,
+ * short circuit routing lookup.
+ */
+ if (flags & NS_ROUTETOIF) {
+ struct ns_ifaddr *ia = ns_iaonnetof(&idp->idp_dna);
+
+ if (ia == 0) {
+ error = ENETUNREACH;
+ goto bad;
+ }
+ ifp = ia->ia_ifp;
+ goto gotif;
+ }
+ rtalloc(ro);
+ } else if ((ro->ro_rt->rt_flags & RTF_UP) == 0) {
+ /*
+ * The old route has gone away; try for a new one.
+ */
+ rtfree(ro->ro_rt);
+ ro->ro_rt = NULL;
+ rtalloc(ro);
+ }
+ if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0) {
+ error = ENETUNREACH;
+ goto bad;
+ }
+ ro->ro_rt->rt_use++;
+ if (ro->ro_rt->rt_flags & (RTF_GATEWAY|RTF_HOST))
+ dst = (struct sockaddr_ns *)ro->ro_rt->rt_gateway;
+gotif:
+
+ /*
+ * Look for multicast addresses and
+ * and verify user is allowed to send
+ * such a packet.
+ */
+ if (dst->sns_addr.x_host.c_host[0]&1) {
+ if ((ifp->if_flags & IFF_BROADCAST) == 0) {
+ error = EADDRNOTAVAIL;
+ goto bad;
+ }
+ if ((flags & NS_ALLOWBROADCAST) == 0) {
+ error = EACCES;
+ goto bad;
+ }
+ }
+
+ if (htons(idp->idp_len) <= ifp->if_mtu) {
+ ns_output_cnt++;
+ if (ns_copy_output) {
+ ns_watch_output(m0, ifp);
+ }
+ error = (*ifp->if_output)(ifp, m0,
+ (struct sockaddr *)dst, ro->ro_rt);
+ goto done;
+ } else error = EMSGSIZE;
+
+
+bad:
+ if (ns_copy_output) {
+ ns_watch_output(m0, ifp);
+ }
+ m_freem(m0);
+done:
+ if (ro == &idproute && (flags & NS_ROUTETOIF) == 0 && ro->ro_rt) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = 0;
+ }
+ return (error);
+}
diff --git a/sys/netns/ns_pcb.c b/sys/netns/ns_pcb.c
new file mode 100644
index 0000000..ca88472
--- /dev/null
+++ b/sys/netns/ns_pcb.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ns_pcb.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#include <netns/ns_pcb.h>
+
+struct ns_addr zerons_addr;
+
+ns_pcballoc(so, head)
+ struct socket *so;
+ struct nspcb *head;
+{
+ struct mbuf *m;
+ register struct nspcb *nsp;
+
+ m = m_getclr(M_DONTWAIT, MT_PCB);
+ if (m == NULL)
+ return (ENOBUFS);
+ nsp = mtod(m, struct nspcb *);
+ nsp->nsp_socket = so;
+ insque(nsp, head);
+ so->so_pcb = (caddr_t)nsp;
+ return (0);
+}
+
+ns_pcbbind(nsp, nam)
+ register struct nspcb *nsp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_ns *sns;
+ u_short lport = 0;
+
+ if (nsp->nsp_lport || !ns_nullhost(nsp->nsp_laddr))
+ return (EINVAL);
+ if (nam == 0)
+ goto noname;
+ sns = mtod(nam, struct sockaddr_ns *);
+ if (nam->m_len != sizeof (*sns))
+ return (EINVAL);
+ if (!ns_nullhost(sns->sns_addr)) {
+ int tport = sns->sns_port;
+
+ sns->sns_port = 0; /* yech... */
+ if (ifa_ifwithaddr((struct sockaddr *)sns) == 0)
+ return (EADDRNOTAVAIL);
+ sns->sns_port = tport;
+ }
+ lport = sns->sns_port;
+ if (lport) {
+ u_short aport = ntohs(lport);
+
+ if (aport < NSPORT_RESERVED &&
+ (nsp->nsp_socket->so_state & SS_PRIV) == 0)
+ return (EACCES);
+ if (ns_pcblookup(&zerons_addr, lport, 0))
+ return (EADDRINUSE);
+ }
+ nsp->nsp_laddr = sns->sns_addr;
+noname:
+ if (lport == 0)
+ do {
+ if (nspcb.nsp_lport++ < NSPORT_RESERVED)
+ nspcb.nsp_lport = NSPORT_RESERVED;
+ lport = htons(nspcb.nsp_lport);
+ } while (ns_pcblookup(&zerons_addr, lport, 0));
+ nsp->nsp_lport = lport;
+ return (0);
+}
+
+/*
+ * Connect from a socket to a specified address.
+ * Both address and port must be specified in argument sns.
+ * If don't have a local address for this socket yet,
+ * then pick one.
+ */
+ns_pcbconnect(nsp, nam)
+ struct nspcb *nsp;
+ struct mbuf *nam;
+{
+ struct ns_ifaddr *ia;
+ register struct sockaddr_ns *sns = mtod(nam, struct sockaddr_ns *);
+ register struct ns_addr *dst;
+ register struct route *ro;
+ struct ifnet *ifp;
+
+ if (nam->m_len != sizeof (*sns))
+ return (EINVAL);
+ if (sns->sns_family != AF_NS)
+ return (EAFNOSUPPORT);
+ if (sns->sns_port==0 || ns_nullhost(sns->sns_addr))
+ return (EADDRNOTAVAIL);
+ /*
+ * If we haven't bound which network number to use as ours,
+ * we will use the number of the outgoing interface.
+ * This depends on having done a routing lookup, which
+ * we will probably have to do anyway, so we might
+ * as well do it now. On the other hand if we are
+ * sending to multiple destinations we may have already
+ * done the lookup, so see if we can use the route
+ * from before. In any case, we only
+ * chose a port number once, even if sending to multiple
+ * destinations.
+ */
+ ro = &nsp->nsp_route;
+ dst = &satons_addr(ro->ro_dst);
+ if (nsp->nsp_socket->so_options & SO_DONTROUTE)
+ goto flush;
+ if (!ns_neteq(nsp->nsp_lastdst, sns->sns_addr))
+ goto flush;
+ if (!ns_hosteq(nsp->nsp_lastdst, sns->sns_addr)) {
+ if (ro->ro_rt && ! (ro->ro_rt->rt_flags & RTF_HOST)) {
+ /* can patch route to avoid rtalloc */
+ *dst = sns->sns_addr;
+ } else {
+ flush:
+ if (ro->ro_rt)
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ nsp->nsp_laddr.x_net = ns_zeronet;
+ }
+ }/* else cached route is ok; do nothing */
+ nsp->nsp_lastdst = sns->sns_addr;
+ if ((nsp->nsp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
+ (ro->ro_rt == (struct rtentry *)0 ||
+ ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
+ /* No route yet, so try to acquire one */
+ ro->ro_dst.sa_family = AF_NS;
+ ro->ro_dst.sa_len = sizeof(ro->ro_dst);
+ *dst = sns->sns_addr;
+ dst->x_port = 0;
+ rtalloc(ro);
+ }
+ if (ns_neteqnn(nsp->nsp_laddr.x_net, ns_zeronet)) {
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the i/f, else punt.
+ */
+
+ ia = (struct ns_ifaddr *)0;
+ /*
+ * If we found a route, use the address
+ * corresponding to the outgoing interface
+ */
+ if (ro->ro_rt && (ifp = ro->ro_rt->rt_ifp))
+ for (ia = ns_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+ if (ia == 0) {
+ u_short fport = sns->sns_addr.x_port;
+ sns->sns_addr.x_port = 0;
+ ia = (struct ns_ifaddr *)
+ ifa_ifwithdstaddr((struct sockaddr *)sns);
+ sns->sns_addr.x_port = fport;
+ if (ia == 0)
+ ia = ns_iaonnetof(&sns->sns_addr);
+ if (ia == 0)
+ ia = ns_ifaddr;
+ if (ia == 0)
+ return (EADDRNOTAVAIL);
+ }
+ nsp->nsp_laddr.x_net = satons_addr(ia->ia_addr).x_net;
+ }
+ if (ns_pcblookup(&sns->sns_addr, nsp->nsp_lport, 0))
+ return (EADDRINUSE);
+ if (ns_nullhost(nsp->nsp_laddr)) {
+ if (nsp->nsp_lport == 0)
+ (void) ns_pcbbind(nsp, (struct mbuf *)0);
+ nsp->nsp_laddr.x_host = ns_thishost;
+ }
+ nsp->nsp_faddr = sns->sns_addr;
+ /* Includes nsp->nsp_fport = sns->sns_port; */
+ return (0);
+}
+
+ns_pcbdisconnect(nsp)
+ struct nspcb *nsp;
+{
+
+ nsp->nsp_faddr = zerons_addr;
+ if (nsp->nsp_socket->so_state & SS_NOFDREF)
+ ns_pcbdetach(nsp);
+}
+
+ns_pcbdetach(nsp)
+ struct nspcb *nsp;
+{
+ struct socket *so = nsp->nsp_socket;
+
+ so->so_pcb = 0;
+ sofree(so);
+ if (nsp->nsp_route.ro_rt)
+ rtfree(nsp->nsp_route.ro_rt);
+ remque(nsp);
+ (void) m_free(dtom(nsp));
+}
+
+ns_setsockaddr(nsp, nam)
+ register struct nspcb *nsp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_ns *sns = mtod(nam, struct sockaddr_ns *);
+
+ nam->m_len = sizeof (*sns);
+ sns = mtod(nam, struct sockaddr_ns *);
+ bzero((caddr_t)sns, sizeof (*sns));
+ sns->sns_len = sizeof(*sns);
+ sns->sns_family = AF_NS;
+ sns->sns_addr = nsp->nsp_laddr;
+}
+
+ns_setpeeraddr(nsp, nam)
+ register struct nspcb *nsp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_ns *sns = mtod(nam, struct sockaddr_ns *);
+
+ nam->m_len = sizeof (*sns);
+ sns = mtod(nam, struct sockaddr_ns *);
+ bzero((caddr_t)sns, sizeof (*sns));
+ sns->sns_len = sizeof(*sns);
+ sns->sns_family = AF_NS;
+ sns->sns_addr = nsp->nsp_faddr;
+}
+
+/*
+ * Pass some notification to all connections of a protocol
+ * associated with address dst. Call the
+ * protocol specific routine to handle each connection.
+ * Also pass an extra paramter via the nspcb. (which may in fact
+ * be a parameter list!)
+ */
+ns_pcbnotify(dst, errno, notify, param)
+ register struct ns_addr *dst;
+ long param;
+ int errno, (*notify)();
+{
+ register struct nspcb *nsp, *oinp;
+ int s = splimp();
+
+ for (nsp = (&nspcb)->nsp_next; nsp != (&nspcb);) {
+ if (!ns_hosteq(*dst,nsp->nsp_faddr)) {
+ next:
+ nsp = nsp->nsp_next;
+ continue;
+ }
+ if (nsp->nsp_socket == 0)
+ goto next;
+ if (errno)
+ nsp->nsp_socket->so_error = errno;
+ oinp = nsp;
+ nsp = nsp->nsp_next;
+ oinp->nsp_notify_param = param;
+ (*notify)(oinp);
+ }
+ splx(s);
+}
+
+#ifdef notdef
+/*
+ * After a routing change, flush old routing
+ * and allocate a (hopefully) better one.
+ */
+ns_rtchange(nsp)
+ struct nspcb *nsp;
+{
+ if (nsp->nsp_route.ro_rt) {
+ rtfree(nsp->nsp_route.ro_rt);
+ nsp->nsp_route.ro_rt = 0;
+ /*
+ * A new route can be allocated the next time
+ * output is attempted.
+ */
+ }
+ /* SHOULD NOTIFY HIGHER-LEVEL PROTOCOLS */
+}
+#endif
+
+struct nspcb *
+ns_pcblookup(faddr, lport, wildp)
+ struct ns_addr *faddr;
+ u_short lport;
+{
+ register struct nspcb *nsp, *match = 0;
+ int matchwild = 3, wildcard;
+ u_short fport;
+
+ fport = faddr->x_port;
+ for (nsp = (&nspcb)->nsp_next; nsp != (&nspcb); nsp = nsp->nsp_next) {
+ if (nsp->nsp_lport != lport)
+ continue;
+ wildcard = 0;
+ if (ns_nullhost(nsp->nsp_faddr)) {
+ if (!ns_nullhost(*faddr))
+ wildcard++;
+ } else {
+ if (ns_nullhost(*faddr))
+ wildcard++;
+ else {
+ if (!ns_hosteq(nsp->nsp_faddr, *faddr))
+ continue;
+ if (nsp->nsp_fport != fport) {
+ if (nsp->nsp_fport != 0)
+ continue;
+ else
+ wildcard++;
+ }
+ }
+ }
+ if (wildcard && wildp==0)
+ continue;
+ if (wildcard < matchwild) {
+ match = nsp;
+ matchwild = wildcard;
+ if (wildcard == 0)
+ break;
+ }
+ }
+ return (match);
+}
diff --git a/sys/netns/ns_pcb.h b/sys/netns/ns_pcb.h
new file mode 100644
index 0000000..68cf744
--- /dev/null
+++ b/sys/netns/ns_pcb.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ns_pcb.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Ns protocol interface control block.
+ */
+struct nspcb {
+ struct nspcb *nsp_next; /* doubly linked list */
+ struct nspcb *nsp_prev;
+ struct nspcb *nsp_head;
+ struct socket *nsp_socket; /* back pointer to socket */
+ struct ns_addr nsp_faddr; /* destination address */
+ struct ns_addr nsp_laddr; /* socket's address */
+ caddr_t nsp_pcb; /* protocol specific stuff */
+ struct route nsp_route; /* routing information */
+ struct ns_addr nsp_lastdst; /* validate cached route for dg socks*/
+ long nsp_notify_param; /* extra info passed via ns_pcbnotify*/
+ short nsp_flags;
+ u_char nsp_dpt; /* default packet type for idp_output*/
+ u_char nsp_rpt; /* last received packet type by
+ idp_input() */
+};
+
+/* possible flags */
+
+#define NSP_IN_ABORT 0x1 /* calling abort through socket */
+#define NSP_RAWIN 0x2 /* show headers on input */
+#define NSP_RAWOUT 0x4 /* show header on output */
+#define NSP_ALL_PACKETS 0x8 /* Turn off higher proto processing */
+
+#define NS_WILDCARD 1
+
+#define nsp_lport nsp_laddr.x_port
+#define nsp_fport nsp_faddr.x_port
+
+#define sotonspcb(so) ((struct nspcb *)((so)->so_pcb))
+
+/*
+ * Nominal space allocated to a ns socket.
+ */
+#define NSSNDQ 2048
+#define NSRCVQ 2048
+
+
+#ifdef KERNEL
+struct nspcb nspcb; /* head of list */
+struct nspcb *ns_pcblookup();
+#endif
diff --git a/sys/netns/ns_proto.c b/sys/netns/ns_proto.c
new file mode 100644
index 0000000..fc9f823
--- /dev/null
+++ b/sys/netns/ns_proto.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ns_proto.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/mbuf.h>
+
+#include <net/radix.h>
+
+#include <netns/ns.h>
+
+/*
+ * NS protocol family: IDP, ERR, PE, SPP, ROUTE.
+ */
+int ns_init();
+int idp_input(), idp_output(), idp_ctlinput(), idp_usrreq();
+int idp_raw_usrreq(), idp_ctloutput();
+int spp_input(), spp_ctlinput();
+int spp_usrreq(), spp_usrreq_sp(), spp_ctloutput();
+int spp_init(), spp_fasttimo(), spp_slowtimo();
+extern int raw_usrreq();
+
+extern struct domain nsdomain;
+
+struct protosw nssw[] = {
+{ 0, &nsdomain, 0, 0,
+ 0, idp_output, 0, 0,
+ 0,
+ ns_init, 0, 0, 0,
+},
+{ SOCK_DGRAM, &nsdomain, 0, PR_ATOMIC|PR_ADDR,
+ 0, 0, idp_ctlinput, idp_ctloutput,
+ idp_usrreq,
+ 0, 0, 0, 0,
+},
+{ SOCK_STREAM, &nsdomain, NSPROTO_SPP, PR_CONNREQUIRED|PR_WANTRCVD,
+ spp_input, 0, spp_ctlinput, spp_ctloutput,
+ spp_usrreq,
+ spp_init, spp_fasttimo, spp_slowtimo, 0,
+},
+{ SOCK_SEQPACKET,&nsdomain, NSPROTO_SPP, PR_CONNREQUIRED|PR_WANTRCVD|PR_ATOMIC,
+ spp_input, 0, spp_ctlinput, spp_ctloutput,
+ spp_usrreq_sp,
+ 0, 0, 0, 0,
+},
+{ SOCK_RAW, &nsdomain, NSPROTO_RAW, PR_ATOMIC|PR_ADDR,
+ idp_input, idp_output, 0, idp_ctloutput,
+ idp_raw_usrreq,
+ 0, 0, 0, 0,
+},
+{ SOCK_RAW, &nsdomain, NSPROTO_ERROR, PR_ATOMIC|PR_ADDR,
+ idp_ctlinput, idp_output, 0, idp_ctloutput,
+ idp_raw_usrreq,
+ 0, 0, 0, 0,
+},
+};
+
+struct domain nsdomain =
+ { AF_NS, "network systems", 0, 0, 0,
+ nssw, &nssw[sizeof(nssw)/sizeof(nssw[0])], 0,
+ rn_inithead, 16, sizeof(struct sockaddr_ns)};
+
diff --git a/sys/netns/sp.h b/sys/netns/sp.h
new file mode 100644
index 0000000..b55dac2
--- /dev/null
+++ b/sys/netns/sp.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)sp.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Definitions for Xerox NS style sequenced packet protocol
+ */
+
+struct sphdr {
+ u_char sp_cc; /* connection control */
+ u_char sp_dt; /* datastream type */
+#define SP_SP 0x80 /* system packet */
+#define SP_SA 0x40 /* send acknowledgement */
+#define SP_OB 0x20 /* attention (out of band data) */
+#define SP_EM 0x10 /* end of message */
+ u_short sp_sid; /* source connection identifier */
+ u_short sp_did; /* destination connection identifier */
+ u_short sp_seq; /* sequence number */
+ u_short sp_ack; /* acknowledge number */
+ u_short sp_alo; /* allocation number */
+};
diff --git a/sys/netns/spidp.h b/sys/netns/spidp.h
new file mode 100644
index 0000000..332df5b
--- /dev/null
+++ b/sys/netns/spidp.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)spidp.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Definitions for NS(tm) Internet Datagram Protocol
+ * containing a Sequenced Packet Protocol packet.
+ */
+struct spidp {
+ struct idp si_i;
+ struct sphdr si_s;
+};
+struct spidp_q {
+ struct spidp_q *si_next;
+ struct spidp_q *si_prev;
+};
+#define SI(x) ((struct spidp *)x)
+#define si_sum si_i.idp_sum
+#define si_len si_i.idp_len
+#define si_tc si_i.idp_tc
+#define si_pt si_i.idp_pt
+#define si_dna si_i.idp_dna
+#define si_sna si_i.idp_sna
+#define si_sport si_i.idp_sna.x_port
+#define si_cc si_s.sp_cc
+#define si_dt si_s.sp_dt
+#define si_sid si_s.sp_sid
+#define si_did si_s.sp_did
+#define si_seq si_s.sp_seq
+#define si_ack si_s.sp_ack
+#define si_alo si_s.sp_alo
diff --git a/sys/netns/spp_debug.c b/sys/netns/spp_debug.c
new file mode 100644
index 0000000..eaa1d02
--- /dev/null
+++ b/sys/netns/spp_debug.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)spp_debug.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+
+#include <net/route.h>
+#include <net/if.h>
+#include <netinet/tcp_fsm.h>
+
+#include <netns/ns.h>
+#include <netns/ns_pcb.h>
+#include <netns/idp.h>
+#include <netns/idp_var.h>
+#include <netns/sp.h>
+#include <netns/spidp.h>
+#define SPPTIMERS
+#include <netns/spp_timer.h>
+#include <netns/spp_var.h>
+#define SANAMES
+#include <netns/spp_debug.h>
+
+int sppconsdebug = 0;
+/*
+ * spp debug routines
+ */
+spp_trace(act, ostate, sp, si, req)
+ short act;
+ u_char ostate;
+ struct sppcb *sp;
+ struct spidp *si;
+ int req;
+{
+#ifdef INET
+#ifdef TCPDEBUG
+ u_short seq, ack, len, alo;
+ unsigned long iptime();
+ int flags;
+ struct spp_debug *sd = &spp_debug[spp_debx++];
+ extern char *prurequests[];
+ extern char *sanames[];
+ extern char *tcpstates[];
+ extern char *spptimers[];
+
+ if (spp_debx == SPP_NDEBUG)
+ spp_debx = 0;
+ sd->sd_time = iptime();
+ sd->sd_act = act;
+ sd->sd_ostate = ostate;
+ sd->sd_cb = (caddr_t)sp;
+ if (sp)
+ sd->sd_sp = *sp;
+ else
+ bzero((caddr_t)&sd->sd_sp, sizeof (*sp));
+ if (si)
+ sd->sd_si = *si;
+ else
+ bzero((caddr_t)&sd->sd_si, sizeof (*si));
+ sd->sd_req = req;
+ if (sppconsdebug == 0)
+ return;
+ if (ostate >= TCP_NSTATES) ostate = 0;
+ if (act >= SA_DROP) act = SA_DROP;
+ if (sp)
+ printf("%x %s:", sp, tcpstates[ostate]);
+ else
+ printf("???????? ");
+ printf("%s ", sanames[act]);
+ switch (act) {
+
+ case SA_RESPOND:
+ case SA_INPUT:
+ case SA_OUTPUT:
+ case SA_DROP:
+ if (si == 0)
+ break;
+ seq = si->si_seq;
+ ack = si->si_ack;
+ alo = si->si_alo;
+ len = si->si_len;
+ if (act == SA_OUTPUT) {
+ seq = ntohs(seq);
+ ack = ntohs(ack);
+ alo = ntohs(alo);
+ len = ntohs(len);
+ }
+#ifndef lint
+#define p1(f) { printf("%s = %x, ", "f", f); }
+ p1(seq); p1(ack); p1(alo); p1(len);
+#endif
+ flags = si->si_cc;
+ if (flags) {
+ char *cp = "<";
+#ifndef lint
+#define pf(f) { if (flags&SP_/**/f) { printf("%s%s", cp, "f"); cp = ","; } }
+ pf(SP); pf(SA); pf(OB); pf(EM);
+#else
+ cp = cp;
+#endif
+ printf(">");
+ }
+#ifndef lint
+#define p2(f) { printf("%s = %x, ", "f", si->si_/**/f); }
+ p2(sid);p2(did);p2(dt);p2(pt);
+#endif
+ ns_printhost(&si->si_sna);
+ ns_printhost(&si->si_dna);
+
+ if (act==SA_RESPOND) {
+ printf("idp_len = %x, ",
+ ((struct idp *)si)->idp_len);
+ }
+ break;
+
+ case SA_USER:
+ printf("%s", prurequests[req&0xff]);
+ if ((req & 0xff) == PRU_SLOWTIMO)
+ printf("<%s>", spptimers[req>>8]);
+ break;
+ }
+ if (sp)
+ printf(" -> %s", tcpstates[sp->s_state]);
+ /* print out internal state of sp !?! */
+ printf("\n");
+ if (sp == 0)
+ return;
+#ifndef lint
+#define p3(f) { printf("%s = %x, ", "f", sp->s_/**/f); }
+ printf("\t"); p3(rack);p3(ralo);p3(smax);p3(flags); printf("\n");
+#endif
+#endif
+#endif
+}
diff --git a/sys/netns/spp_debug.h b/sys/netns/spp_debug.h
new file mode 100644
index 0000000..8dfe242
--- /dev/null
+++ b/sys/netns/spp_debug.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)spp_debug.h 8.1 (Berkeley) 6/10/93
+ */
+
+struct spp_debug {
+ u_long sd_time;
+ short sd_act;
+ short sd_ostate;
+ caddr_t sd_cb;
+ short sd_req;
+ struct spidp sd_si;
+ struct sppcb sd_sp;
+};
+
+#define SA_INPUT 0
+#define SA_OUTPUT 1
+#define SA_USER 2
+#define SA_RESPOND 3
+#define SA_DROP 4
+
+#ifdef SANAMES
+char *sanames[] =
+ { "input", "output", "user", "respond", "drop" };
+#endif
+
+#define SPP_NDEBUG 100
+struct spp_debug spp_debug[SPP_NDEBUG];
+int spp_debx;
diff --git a/sys/netns/spp_timer.h b/sys/netns/spp_timer.h
new file mode 100644
index 0000000..f84e328
--- /dev/null
+++ b/sys/netns/spp_timer.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)spp_timer.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Definitions of the SPP timers. These timers are counted
+ * down PR_SLOWHZ times a second.
+ */
+#define SPPT_NTIMERS 4
+
+#define SPPT_REXMT 0 /* retransmit */
+#define SPPT_PERSIST 1 /* retransmit persistance */
+#define SPPT_KEEP 2 /* keep alive */
+#define SPPT_2MSL 3 /* 2*msl quiet time timer */
+
+/*
+ * The SPPT_REXMT timer is used to force retransmissions.
+ * The SPP has the SPPT_REXMT timer set whenever segments
+ * have been sent for which ACKs are expected but not yet
+ * received. If an ACK is received which advances tp->snd_una,
+ * then the retransmit timer is cleared (if there are no more
+ * outstanding segments) or reset to the base value (if there
+ * are more ACKs expected). Whenever the retransmit timer goes off,
+ * we retransmit one unacknowledged segment, and do a backoff
+ * on the retransmit timer.
+ *
+ * The SPPT_PERSIST timer is used to keep window size information
+ * flowing even if the window goes shut. If all previous transmissions
+ * have been acknowledged (so that there are no retransmissions in progress),
+ * and the window is too small to bother sending anything, then we start
+ * the SPPT_PERSIST timer. When it expires, if the window is nonzero,
+ * we go to transmit state. Otherwise, at intervals send a single byte
+ * into the peer's window to force him to update our window information.
+ * We do this at most as often as SPPT_PERSMIN time intervals,
+ * but no more frequently than the current estimate of round-trip
+ * packet time. The SPPT_PERSIST timer is cleared whenever we receive
+ * a window update from the peer.
+ *
+ * The SPPT_KEEP timer is used to keep connections alive. If an
+ * connection is idle (no segments received) for SPPTV_KEEP amount of time,
+ * but not yet established, then we drop the connection. If the connection
+ * is established, then we force the peer to send us a segment by sending:
+ * <SEQ=SND.UNA-1><ACK=RCV.NXT><CTL=ACK>
+ * This segment is (deliberately) outside the window, and should elicit
+ * an ack segment in response from the peer. If, despite the SPPT_KEEP
+ * initiated segments we cannot elicit a response from a peer in SPPT_MAXIDLE
+ * amount of time, then we drop the connection.
+ */
+
+#define SPP_TTL 30 /* default time to live for SPP segs */
+/*
+ * Time constants.
+ */
+#define SPPTV_MSL ( 15*PR_SLOWHZ) /* max seg lifetime */
+#define SPPTV_SRTTBASE 0 /* base roundtrip time;
+ if 0, no idea yet */
+#define SPPTV_SRTTDFLT ( 3*PR_SLOWHZ) /* assumed RTT if no info */
+
+#define SPPTV_PERSMIN ( 5*PR_SLOWHZ) /* retransmit persistance */
+#define SPPTV_PERSMAX ( 60*PR_SLOWHZ) /* maximum persist interval */
+
+#define SPPTV_KEEP ( 75*PR_SLOWHZ) /* keep alive - 75 secs */
+#define SPPTV_MAXIDLE ( 8*SPPTV_KEEP) /* maximum allowable idle
+ time before drop conn */
+
+#define SPPTV_MIN ( 1*PR_SLOWHZ) /* minimum allowable value */
+#define SPPTV_REXMTMAX ( 64*PR_SLOWHZ) /* max allowable REXMT value */
+
+#define SPP_LINGERTIME 120 /* linger at most 2 minutes */
+
+#define SPP_MAXRXTSHIFT 12 /* maximum retransmits */
+
+#ifdef SPPTIMERS
+char *spptimers[] =
+ { "REXMT", "PERSIST", "KEEP", "2MSL" };
+#endif
+
+/*
+ * Force a time value to be in a certain range.
+ */
+#define SPPT_RANGESET(tv, value, tvmin, tvmax) { \
+ (tv) = (value); \
+ if ((tv) < (tvmin)) \
+ (tv) = (tvmin); \
+ else if ((tv) > (tvmax)) \
+ (tv) = (tvmax); \
+}
+
+#ifdef KERNEL
+extern int spp_backoff[];
+#endif
diff --git a/sys/netns/spp_usrreq.c b/sys/netns/spp_usrreq.c
new file mode 100644
index 0000000..062bbec
--- /dev/null
+++ b/sys/netns/spp_usrreq.c
@@ -0,0 +1,1804 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)spp_usrreq.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/tcp_fsm.h>
+
+#include <netns/ns.h>
+#include <netns/ns_pcb.h>
+#include <netns/idp.h>
+#include <netns/idp_var.h>
+#include <netns/ns_error.h>
+#include <netns/sp.h>
+#include <netns/spidp.h>
+#include <netns/spp_timer.h>
+#include <netns/spp_var.h>
+#include <netns/spp_debug.h>
+
+/*
+ * SP protocol implementation.
+ */
+spp_init()
+{
+
+ spp_iss = 1; /* WRONG !! should fish it out of TODR */
+}
+struct spidp spp_savesi;
+int traceallspps = 0;
+extern int sppconsdebug;
+int spp_hardnosed;
+int spp_use_delack = 0;
+u_short spp_newchecks[50];
+
+/*ARGSUSED*/
+spp_input(m, nsp)
+ register struct mbuf *m;
+ register struct nspcb *nsp;
+{
+ register struct sppcb *cb;
+ register struct spidp *si = mtod(m, struct spidp *);
+ register struct socket *so;
+ short ostate;
+ int dropsocket = 0;
+
+
+ sppstat.spps_rcvtotal++;
+ if (nsp == 0) {
+ panic("No nspcb in spp_input\n");
+ return;
+ }
+
+ cb = nstosppcb(nsp);
+ if (cb == 0) goto bad;
+
+ if (m->m_len < sizeof(*si)) {
+ if ((m = m_pullup(m, sizeof(*si))) == 0) {
+ sppstat.spps_rcvshort++;
+ return;
+ }
+ si = mtod(m, struct spidp *);
+ }
+ si->si_seq = ntohs(si->si_seq);
+ si->si_ack = ntohs(si->si_ack);
+ si->si_alo = ntohs(si->si_alo);
+
+ so = nsp->nsp_socket;
+ if (so->so_options & SO_DEBUG || traceallspps) {
+ ostate = cb->s_state;
+ spp_savesi = *si;
+ }
+ if (so->so_options & SO_ACCEPTCONN) {
+ struct sppcb *ocb = cb;
+
+ so = sonewconn(so, 0);
+ if (so == 0) {
+ goto drop;
+ }
+ /*
+ * This is ugly, but ....
+ *
+ * Mark socket as temporary until we're
+ * committed to keeping it. The code at
+ * ``drop'' and ``dropwithreset'' check the
+ * flag dropsocket to see if the temporary
+ * socket created here should be discarded.
+ * We mark the socket as discardable until
+ * we're committed to it below in TCPS_LISTEN.
+ */
+ dropsocket++;
+ nsp = (struct nspcb *)so->so_pcb;
+ nsp->nsp_laddr = si->si_dna;
+ cb = nstosppcb(nsp);
+ cb->s_mtu = ocb->s_mtu; /* preserve sockopts */
+ cb->s_flags = ocb->s_flags; /* preserve sockopts */
+ cb->s_flags2 = ocb->s_flags2; /* preserve sockopts */
+ cb->s_state = TCPS_LISTEN;
+ }
+
+ /*
+ * Packet received on connection.
+ * reset idle time and keep-alive timer;
+ */
+ cb->s_idle = 0;
+ cb->s_timer[SPPT_KEEP] = SPPTV_KEEP;
+
+ switch (cb->s_state) {
+
+ case TCPS_LISTEN:{
+ struct mbuf *am;
+ register struct sockaddr_ns *sns;
+ struct ns_addr laddr;
+
+ /*
+ * If somebody here was carying on a conversation
+ * and went away, and his pen pal thinks he can
+ * still talk, we get the misdirected packet.
+ */
+ if (spp_hardnosed && (si->si_did != 0 || si->si_seq != 0)) {
+ spp_istat.gonawy++;
+ goto dropwithreset;
+ }
+ am = m_get(M_DONTWAIT, MT_SONAME);
+ if (am == NULL)
+ goto drop;
+ am->m_len = sizeof (struct sockaddr_ns);
+ sns = mtod(am, struct sockaddr_ns *);
+ sns->sns_len = sizeof(*sns);
+ sns->sns_family = AF_NS;
+ sns->sns_addr = si->si_sna;
+ laddr = nsp->nsp_laddr;
+ if (ns_nullhost(laddr))
+ nsp->nsp_laddr = si->si_dna;
+ if (ns_pcbconnect(nsp, am)) {
+ nsp->nsp_laddr = laddr;
+ (void) m_free(am);
+ spp_istat.noconn++;
+ goto drop;
+ }
+ (void) m_free(am);
+ spp_template(cb);
+ dropsocket = 0; /* committed to socket */
+ cb->s_did = si->si_sid;
+ cb->s_rack = si->si_ack;
+ cb->s_ralo = si->si_alo;
+#define THREEWAYSHAKE
+#ifdef THREEWAYSHAKE
+ cb->s_state = TCPS_SYN_RECEIVED;
+ cb->s_force = 1 + SPPT_KEEP;
+ sppstat.spps_accepts++;
+ cb->s_timer[SPPT_KEEP] = SPPTV_KEEP;
+ }
+ break;
+ /*
+ * This state means that we have heard a response
+ * to our acceptance of their connection
+ * It is probably logically unnecessary in this
+ * implementation.
+ */
+ case TCPS_SYN_RECEIVED: {
+ if (si->si_did!=cb->s_sid) {
+ spp_istat.wrncon++;
+ goto drop;
+ }
+#endif
+ nsp->nsp_fport = si->si_sport;
+ cb->s_timer[SPPT_REXMT] = 0;
+ cb->s_timer[SPPT_KEEP] = SPPTV_KEEP;
+ soisconnected(so);
+ cb->s_state = TCPS_ESTABLISHED;
+ sppstat.spps_accepts++;
+ }
+ break;
+
+ /*
+ * This state means that we have gotten a response
+ * to our attempt to establish a connection.
+ * We fill in the data from the other side,
+ * telling us which port to respond to, instead of the well-
+ * known one we might have sent to in the first place.
+ * We also require that this is a response to our
+ * connection id.
+ */
+ case TCPS_SYN_SENT:
+ if (si->si_did!=cb->s_sid) {
+ spp_istat.notme++;
+ goto drop;
+ }
+ sppstat.spps_connects++;
+ cb->s_did = si->si_sid;
+ cb->s_rack = si->si_ack;
+ cb->s_ralo = si->si_alo;
+ cb->s_dport = nsp->nsp_fport = si->si_sport;
+ cb->s_timer[SPPT_REXMT] = 0;
+ cb->s_flags |= SF_ACKNOW;
+ soisconnected(so);
+ cb->s_state = TCPS_ESTABLISHED;
+ /* Use roundtrip time of connection request for initial rtt */
+ if (cb->s_rtt) {
+ cb->s_srtt = cb->s_rtt << 3;
+ cb->s_rttvar = cb->s_rtt << 1;
+ SPPT_RANGESET(cb->s_rxtcur,
+ ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1,
+ SPPTV_MIN, SPPTV_REXMTMAX);
+ cb->s_rtt = 0;
+ }
+ }
+ if (so->so_options & SO_DEBUG || traceallspps)
+ spp_trace(SA_INPUT, (u_char)ostate, cb, &spp_savesi, 0);
+
+ m->m_len -= sizeof (struct idp);
+ m->m_pkthdr.len -= sizeof (struct idp);
+ m->m_data += sizeof (struct idp);
+
+ if (spp_reass(cb, si)) {
+ (void) m_freem(m);
+ }
+ if (cb->s_force || (cb->s_flags & (SF_ACKNOW|SF_WIN|SF_RXT)))
+ (void) spp_output(cb, (struct mbuf *)0);
+ cb->s_flags &= ~(SF_WIN|SF_RXT);
+ return;
+
+dropwithreset:
+ if (dropsocket)
+ (void) soabort(so);
+ si->si_seq = ntohs(si->si_seq);
+ si->si_ack = ntohs(si->si_ack);
+ si->si_alo = ntohs(si->si_alo);
+ ns_error(dtom(si), NS_ERR_NOSOCK, 0);
+ if (cb->s_nspcb->nsp_socket->so_options & SO_DEBUG || traceallspps)
+ spp_trace(SA_DROP, (u_char)ostate, cb, &spp_savesi, 0);
+ return;
+
+drop:
+bad:
+ if (cb == 0 || cb->s_nspcb->nsp_socket->so_options & SO_DEBUG ||
+ traceallspps)
+ spp_trace(SA_DROP, (u_char)ostate, cb, &spp_savesi, 0);
+ m_freem(m);
+}
+
+int spprexmtthresh = 3;
+
+/*
+ * This is structurally similar to the tcp reassembly routine
+ * but its function is somewhat different: It merely queues
+ * packets up, and suppresses duplicates.
+ */
+spp_reass(cb, si)
+register struct sppcb *cb;
+register struct spidp *si;
+{
+ register struct spidp_q *q;
+ register struct mbuf *m;
+ register struct socket *so = cb->s_nspcb->nsp_socket;
+ char packetp = cb->s_flags & SF_HI;
+ int incr;
+ char wakeup = 0;
+
+ if (si == SI(0))
+ goto present;
+ /*
+ * Update our news from them.
+ */
+ if (si->si_cc & SP_SA)
+ cb->s_flags |= (spp_use_delack ? SF_DELACK : SF_ACKNOW);
+ if (SSEQ_GT(si->si_alo, cb->s_ralo))
+ cb->s_flags |= SF_WIN;
+ if (SSEQ_LEQ(si->si_ack, cb->s_rack)) {
+ if ((si->si_cc & SP_SP) && cb->s_rack != (cb->s_smax + 1)) {
+ sppstat.spps_rcvdupack++;
+ /*
+ * If this is a completely duplicate ack
+ * and other conditions hold, we assume
+ * a packet has been dropped and retransmit
+ * it exactly as in tcp_input().
+ */
+ if (si->si_ack != cb->s_rack ||
+ si->si_alo != cb->s_ralo)
+ cb->s_dupacks = 0;
+ else if (++cb->s_dupacks == spprexmtthresh) {
+ u_short onxt = cb->s_snxt;
+ int cwnd = cb->s_cwnd;
+
+ cb->s_snxt = si->si_ack;
+ cb->s_cwnd = CUNIT;
+ cb->s_force = 1 + SPPT_REXMT;
+ (void) spp_output(cb, (struct mbuf *)0);
+ cb->s_timer[SPPT_REXMT] = cb->s_rxtcur;
+ cb->s_rtt = 0;
+ if (cwnd >= 4 * CUNIT)
+ cb->s_cwnd = cwnd / 2;
+ if (SSEQ_GT(onxt, cb->s_snxt))
+ cb->s_snxt = onxt;
+ return (1);
+ }
+ } else
+ cb->s_dupacks = 0;
+ goto update_window;
+ }
+ cb->s_dupacks = 0;
+ /*
+ * If our correspondent acknowledges data we haven't sent
+ * TCP would drop the packet after acking. We'll be a little
+ * more permissive
+ */
+ if (SSEQ_GT(si->si_ack, (cb->s_smax + 1))) {
+ sppstat.spps_rcvacktoomuch++;
+ si->si_ack = cb->s_smax + 1;
+ }
+ sppstat.spps_rcvackpack++;
+ /*
+ * If transmit timer is running and timed sequence
+ * number was acked, update smoothed round trip time.
+ * See discussion of algorithm in tcp_input.c
+ */
+ if (cb->s_rtt && SSEQ_GT(si->si_ack, cb->s_rtseq)) {
+ sppstat.spps_rttupdated++;
+ if (cb->s_srtt != 0) {
+ register short delta;
+ delta = cb->s_rtt - (cb->s_srtt >> 3);
+ if ((cb->s_srtt += delta) <= 0)
+ cb->s_srtt = 1;
+ if (delta < 0)
+ delta = -delta;
+ delta -= (cb->s_rttvar >> 2);
+ if ((cb->s_rttvar += delta) <= 0)
+ cb->s_rttvar = 1;
+ } else {
+ /*
+ * No rtt measurement yet
+ */
+ cb->s_srtt = cb->s_rtt << 3;
+ cb->s_rttvar = cb->s_rtt << 1;
+ }
+ cb->s_rtt = 0;
+ cb->s_rxtshift = 0;
+ SPPT_RANGESET(cb->s_rxtcur,
+ ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1,
+ SPPTV_MIN, SPPTV_REXMTMAX);
+ }
+ /*
+ * If all outstanding data is acked, stop retransmit
+ * timer and remember to restart (more output or persist).
+ * If there is more data to be acked, restart retransmit
+ * timer, using current (possibly backed-off) value;
+ */
+ if (si->si_ack == cb->s_smax + 1) {
+ cb->s_timer[SPPT_REXMT] = 0;
+ cb->s_flags |= SF_RXT;
+ } else if (cb->s_timer[SPPT_PERSIST] == 0)
+ cb->s_timer[SPPT_REXMT] = cb->s_rxtcur;
+ /*
+ * When new data is acked, open the congestion window.
+ * If the window gives us less than ssthresh packets
+ * in flight, open exponentially (maxseg at a time).
+ * Otherwise open linearly (maxseg^2 / cwnd at a time).
+ */
+ incr = CUNIT;
+ if (cb->s_cwnd > cb->s_ssthresh)
+ incr = max(incr * incr / cb->s_cwnd, 1);
+ cb->s_cwnd = min(cb->s_cwnd + incr, cb->s_cwmx);
+ /*
+ * Trim Acked data from output queue.
+ */
+ while ((m = so->so_snd.sb_mb) != NULL) {
+ if (SSEQ_LT((mtod(m, struct spidp *))->si_seq, si->si_ack))
+ sbdroprecord(&so->so_snd);
+ else
+ break;
+ }
+ sowwakeup(so);
+ cb->s_rack = si->si_ack;
+update_window:
+ if (SSEQ_LT(cb->s_snxt, cb->s_rack))
+ cb->s_snxt = cb->s_rack;
+ if (SSEQ_LT(cb->s_swl1, si->si_seq) || cb->s_swl1 == si->si_seq &&
+ (SSEQ_LT(cb->s_swl2, si->si_ack) ||
+ cb->s_swl2 == si->si_ack && SSEQ_LT(cb->s_ralo, si->si_alo))) {
+ /* keep track of pure window updates */
+ if ((si->si_cc & SP_SP) && cb->s_swl2 == si->si_ack
+ && SSEQ_LT(cb->s_ralo, si->si_alo)) {
+ sppstat.spps_rcvwinupd++;
+ sppstat.spps_rcvdupack--;
+ }
+ cb->s_ralo = si->si_alo;
+ cb->s_swl1 = si->si_seq;
+ cb->s_swl2 = si->si_ack;
+ cb->s_swnd = (1 + si->si_alo - si->si_ack);
+ if (cb->s_swnd > cb->s_smxw)
+ cb->s_smxw = cb->s_swnd;
+ cb->s_flags |= SF_WIN;
+ }
+ /*
+ * If this packet number is higher than that which
+ * we have allocated refuse it, unless urgent
+ */
+ if (SSEQ_GT(si->si_seq, cb->s_alo)) {
+ if (si->si_cc & SP_SP) {
+ sppstat.spps_rcvwinprobe++;
+ return (1);
+ } else
+ sppstat.spps_rcvpackafterwin++;
+ if (si->si_cc & SP_OB) {
+ if (SSEQ_GT(si->si_seq, cb->s_alo + 60)) {
+ ns_error(dtom(si), NS_ERR_FULLUP, 0);
+ return (0);
+ } /* else queue this packet; */
+ } else {
+ /*register struct socket *so = cb->s_nspcb->nsp_socket;
+ if (so->so_state && SS_NOFDREF) {
+ ns_error(dtom(si), NS_ERR_NOSOCK, 0);
+ (void)spp_close(cb);
+ } else
+ would crash system*/
+ spp_istat.notyet++;
+ ns_error(dtom(si), NS_ERR_FULLUP, 0);
+ return (0);
+ }
+ }
+ /*
+ * If this is a system packet, we don't need to
+ * queue it up, and won't update acknowledge #
+ */
+ if (si->si_cc & SP_SP) {
+ return (1);
+ }
+ /*
+ * We have already seen this packet, so drop.
+ */
+ if (SSEQ_LT(si->si_seq, cb->s_ack)) {
+ spp_istat.bdreas++;
+ sppstat.spps_rcvduppack++;
+ if (si->si_seq == cb->s_ack - 1)
+ spp_istat.lstdup++;
+ return (1);
+ }
+ /*
+ * Loop through all packets queued up to insert in
+ * appropriate sequence.
+ */
+ for (q = cb->s_q.si_next; q!=&cb->s_q; q = q->si_next) {
+ if (si->si_seq == SI(q)->si_seq) {
+ sppstat.spps_rcvduppack++;
+ return (1);
+ }
+ if (SSEQ_LT(si->si_seq, SI(q)->si_seq)) {
+ sppstat.spps_rcvoopack++;
+ break;
+ }
+ }
+ insque(si, q->si_prev);
+ /*
+ * If this packet is urgent, inform process
+ */
+ if (si->si_cc & SP_OB) {
+ cb->s_iobc = ((char *)si)[1 + sizeof(*si)];
+ sohasoutofband(so);
+ cb->s_oobflags |= SF_IOOB;
+ }
+present:
+#define SPINC sizeof(struct sphdr)
+ /*
+ * Loop through all packets queued up to update acknowledge
+ * number, and present all acknowledged data to user;
+ * If in packet interface mode, show packet headers.
+ */
+ for (q = cb->s_q.si_next; q!=&cb->s_q; q = q->si_next) {
+ if (SI(q)->si_seq == cb->s_ack) {
+ cb->s_ack++;
+ m = dtom(q);
+ if (SI(q)->si_cc & SP_OB) {
+ cb->s_oobflags &= ~SF_IOOB;
+ if (so->so_rcv.sb_cc)
+ so->so_oobmark = so->so_rcv.sb_cc;
+ else
+ so->so_state |= SS_RCVATMARK;
+ }
+ q = q->si_prev;
+ remque(q->si_next);
+ wakeup = 1;
+ sppstat.spps_rcvpack++;
+#ifdef SF_NEWCALL
+ if (cb->s_flags2 & SF_NEWCALL) {
+ struct sphdr *sp = mtod(m, struct sphdr *);
+ u_char dt = sp->sp_dt;
+ spp_newchecks[4]++;
+ if (dt != cb->s_rhdr.sp_dt) {
+ struct mbuf *mm =
+ m_getclr(M_DONTWAIT, MT_CONTROL);
+ spp_newchecks[0]++;
+ if (mm != NULL) {
+ u_short *s =
+ mtod(mm, u_short *);
+ cb->s_rhdr.sp_dt = dt;
+ mm->m_len = 5; /*XXX*/
+ s[0] = 5;
+ s[1] = 1;
+ *(u_char *)(&s[2]) = dt;
+ sbappend(&so->so_rcv, mm);
+ }
+ }
+ if (sp->sp_cc & SP_OB) {
+ MCHTYPE(m, MT_OOBDATA);
+ spp_newchecks[1]++;
+ so->so_oobmark = 0;
+ so->so_state &= ~SS_RCVATMARK;
+ }
+ if (packetp == 0) {
+ m->m_data += SPINC;
+ m->m_len -= SPINC;
+ m->m_pkthdr.len -= SPINC;
+ }
+ if ((sp->sp_cc & SP_EM) || packetp) {
+ sbappendrecord(&so->so_rcv, m);
+ spp_newchecks[9]++;
+ } else
+ sbappend(&so->so_rcv, m);
+ } else
+#endif
+ if (packetp) {
+ sbappendrecord(&so->so_rcv, m);
+ } else {
+ cb->s_rhdr = *mtod(m, struct sphdr *);
+ m->m_data += SPINC;
+ m->m_len -= SPINC;
+ m->m_pkthdr.len -= SPINC;
+ sbappend(&so->so_rcv, m);
+ }
+ } else
+ break;
+ }
+ if (wakeup) sorwakeup(so);
+ return (0);
+}
+
+spp_ctlinput(cmd, arg)
+ int cmd;
+ caddr_t arg;
+{
+ struct ns_addr *na;
+ extern u_char nsctlerrmap[];
+ extern spp_abort(), spp_quench();
+ extern struct nspcb *idp_drop();
+ struct ns_errp *errp;
+ struct nspcb *nsp;
+ struct sockaddr_ns *sns;
+ int type;
+
+ if (cmd < 0 || cmd > PRC_NCMDS)
+ return;
+ type = NS_ERR_UNREACH_HOST;
+
+ switch (cmd) {
+
+ case PRC_ROUTEDEAD:
+ return;
+
+ case PRC_IFDOWN:
+ case PRC_HOSTDEAD:
+ case PRC_HOSTUNREACH:
+ sns = (struct sockaddr_ns *)arg;
+ if (sns->sns_family != AF_NS)
+ return;
+ na = &sns->sns_addr;
+ break;
+
+ default:
+ errp = (struct ns_errp *)arg;
+ na = &errp->ns_err_idp.idp_dna;
+ type = errp->ns_err_num;
+ type = ntohs((u_short)type);
+ }
+ switch (type) {
+
+ case NS_ERR_UNREACH_HOST:
+ ns_pcbnotify(na, (int)nsctlerrmap[cmd], spp_abort, (long) 0);
+ break;
+
+ case NS_ERR_TOO_BIG:
+ case NS_ERR_NOSOCK:
+ nsp = ns_pcblookup(na, errp->ns_err_idp.idp_sna.x_port,
+ NS_WILDCARD);
+ if (nsp) {
+ if(nsp->nsp_pcb)
+ (void) spp_drop((struct sppcb *)nsp->nsp_pcb,
+ (int)nsctlerrmap[cmd]);
+ else
+ (void) idp_drop(nsp, (int)nsctlerrmap[cmd]);
+ }
+ break;
+
+ case NS_ERR_FULLUP:
+ ns_pcbnotify(na, 0, spp_quench, (long) 0);
+ }
+}
+/*
+ * When a source quench is received, close congestion window
+ * to one packet. We will gradually open it again as we proceed.
+ */
+spp_quench(nsp)
+ struct nspcb *nsp;
+{
+ struct sppcb *cb = nstosppcb(nsp);
+
+ if (cb)
+ cb->s_cwnd = CUNIT;
+}
+
+#ifdef notdef
+int
+spp_fixmtu(nsp)
+register struct nspcb *nsp;
+{
+ register struct sppcb *cb = (struct sppcb *)(nsp->nsp_pcb);
+ register struct mbuf *m;
+ register struct spidp *si;
+ struct ns_errp *ep;
+ struct sockbuf *sb;
+ int badseq, len;
+ struct mbuf *firstbad, *m0;
+
+ if (cb) {
+ /*
+ * The notification that we have sent
+ * too much is bad news -- we will
+ * have to go through queued up so far
+ * splitting ones which are too big and
+ * reassigning sequence numbers and checksums.
+ * we should then retransmit all packets from
+ * one above the offending packet to the last one
+ * we had sent (or our allocation)
+ * then the offending one so that the any queued
+ * data at our destination will be discarded.
+ */
+ ep = (struct ns_errp *)nsp->nsp_notify_param;
+ sb = &nsp->nsp_socket->so_snd;
+ cb->s_mtu = ep->ns_err_param;
+ badseq = SI(&ep->ns_err_idp)->si_seq;
+ for (m = sb->sb_mb; m; m = m->m_act) {
+ si = mtod(m, struct spidp *);
+ if (si->si_seq == badseq)
+ break;
+ }
+ if (m == 0) return;
+ firstbad = m;
+ /*for (;;) {*/
+ /* calculate length */
+ for (m0 = m, len = 0; m ; m = m->m_next)
+ len += m->m_len;
+ if (len > cb->s_mtu) {
+ }
+ /* FINISH THIS
+ } */
+ }
+}
+#endif
+
+spp_output(cb, m0)
+ register struct sppcb *cb;
+ struct mbuf *m0;
+{
+ struct socket *so = cb->s_nspcb->nsp_socket;
+ register struct mbuf *m;
+ register struct spidp *si = (struct spidp *) 0;
+ register struct sockbuf *sb = &so->so_snd;
+ int len = 0, win, rcv_win;
+ short span, off, recordp = 0;
+ u_short alo;
+ int error = 0, sendalot;
+#ifdef notdef
+ int idle;
+#endif
+ struct mbuf *mprev;
+ extern int idpcksum;
+
+ if (m0) {
+ int mtu = cb->s_mtu;
+ int datalen;
+ /*
+ * Make sure that packet isn't too big.
+ */
+ for (m = m0; m ; m = m->m_next) {
+ mprev = m;
+ len += m->m_len;
+ if (m->m_flags & M_EOR)
+ recordp = 1;
+ }
+ datalen = (cb->s_flags & SF_HO) ?
+ len - sizeof (struct sphdr) : len;
+ if (datalen > mtu) {
+ if (cb->s_flags & SF_PI) {
+ m_freem(m0);
+ return (EMSGSIZE);
+ } else {
+ int oldEM = cb->s_cc & SP_EM;
+
+ cb->s_cc &= ~SP_EM;
+ while (len > mtu) {
+ /*
+ * Here we are only being called
+ * from usrreq(), so it is OK to
+ * block.
+ */
+ m = m_copym(m0, 0, mtu, M_WAIT);
+ if (cb->s_flags & SF_NEWCALL) {
+ struct mbuf *mm = m;
+ spp_newchecks[7]++;
+ while (mm) {
+ mm->m_flags &= ~M_EOR;
+ mm = mm->m_next;
+ }
+ }
+ error = spp_output(cb, m);
+ if (error) {
+ cb->s_cc |= oldEM;
+ m_freem(m0);
+ return(error);
+ }
+ m_adj(m0, mtu);
+ len -= mtu;
+ }
+ cb->s_cc |= oldEM;
+ }
+ }
+ /*
+ * Force length even, by adding a "garbage byte" if
+ * necessary.
+ */
+ if (len & 1) {
+ m = mprev;
+ if (M_TRAILINGSPACE(m) >= 1)
+ m->m_len++;
+ else {
+ struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA);
+
+ if (m1 == 0) {
+ m_freem(m0);
+ return (ENOBUFS);
+ }
+ m1->m_len = 1;
+ *(mtod(m1, u_char *)) = 0;
+ m->m_next = m1;
+ }
+ }
+ m = m_gethdr(M_DONTWAIT, MT_HEADER);
+ if (m == 0) {
+ m_freem(m0);
+ return (ENOBUFS);
+ }
+ /*
+ * Fill in mbuf with extended SP header
+ * and addresses and length put into network format.
+ */
+ MH_ALIGN(m, sizeof (struct spidp));
+ m->m_len = sizeof (struct spidp);
+ m->m_next = m0;
+ si = mtod(m, struct spidp *);
+ si->si_i = *cb->s_idp;
+ si->si_s = cb->s_shdr;
+ if ((cb->s_flags & SF_PI) && (cb->s_flags & SF_HO)) {
+ register struct sphdr *sh;
+ if (m0->m_len < sizeof (*sh)) {
+ if((m0 = m_pullup(m0, sizeof(*sh))) == NULL) {
+ (void) m_free(m);
+ m_freem(m0);
+ return (EINVAL);
+ }
+ m->m_next = m0;
+ }
+ sh = mtod(m0, struct sphdr *);
+ si->si_dt = sh->sp_dt;
+ si->si_cc |= sh->sp_cc & SP_EM;
+ m0->m_len -= sizeof (*sh);
+ m0->m_data += sizeof (*sh);
+ len -= sizeof (*sh);
+ }
+ len += sizeof(*si);
+ if ((cb->s_flags2 & SF_NEWCALL) && recordp) {
+ si->si_cc |= SP_EM;
+ spp_newchecks[8]++;
+ }
+ if (cb->s_oobflags & SF_SOOB) {
+ /*
+ * Per jqj@cornell:
+ * make sure OB packets convey exactly 1 byte.
+ * If the packet is 1 byte or larger, we
+ * have already guaranted there to be at least
+ * one garbage byte for the checksum, and
+ * extra bytes shouldn't hurt!
+ */
+ if (len > sizeof(*si)) {
+ si->si_cc |= SP_OB;
+ len = (1 + sizeof(*si));
+ }
+ }
+ si->si_len = htons((u_short)len);
+ m->m_pkthdr.len = ((len - 1) | 1) + 1;
+ /*
+ * queue stuff up for output
+ */
+ sbappendrecord(sb, m);
+ cb->s_seq++;
+ }
+#ifdef notdef
+ idle = (cb->s_smax == (cb->s_rack - 1));
+#endif
+again:
+ sendalot = 0;
+ off = cb->s_snxt - cb->s_rack;
+ win = min(cb->s_swnd, (cb->s_cwnd/CUNIT));
+
+ /*
+ * If in persist timeout with window of 0, send a probe.
+ * Otherwise, if window is small but nonzero
+ * and timer expired, send what we can and go into
+ * transmit state.
+ */
+ if (cb->s_force == 1 + SPPT_PERSIST) {
+ if (win != 0) {
+ cb->s_timer[SPPT_PERSIST] = 0;
+ cb->s_rxtshift = 0;
+ }
+ }
+ span = cb->s_seq - cb->s_rack;
+ len = min(span, win) - off;
+
+ if (len < 0) {
+ /*
+ * Window shrank after we went into it.
+ * If window shrank to 0, cancel pending
+ * restransmission and pull s_snxt back
+ * to (closed) window. We will enter persist
+ * state below. If the widndow didn't close completely,
+ * just wait for an ACK.
+ */
+ len = 0;
+ if (win == 0) {
+ cb->s_timer[SPPT_REXMT] = 0;
+ cb->s_snxt = cb->s_rack;
+ }
+ }
+ if (len > 1)
+ sendalot = 1;
+ rcv_win = sbspace(&so->so_rcv);
+
+ /*
+ * Send if we owe peer an ACK.
+ */
+ if (cb->s_oobflags & SF_SOOB) {
+ /*
+ * must transmit this out of band packet
+ */
+ cb->s_oobflags &= ~ SF_SOOB;
+ sendalot = 1;
+ sppstat.spps_sndurg++;
+ goto found;
+ }
+ if (cb->s_flags & SF_ACKNOW)
+ goto send;
+ if (cb->s_state < TCPS_ESTABLISHED)
+ goto send;
+ /*
+ * Silly window can't happen in spp.
+ * Code from tcp deleted.
+ */
+ if (len)
+ goto send;
+ /*
+ * Compare available window to amount of window
+ * known to peer (as advertised window less
+ * next expected input.) If the difference is at least two
+ * packets or at least 35% of the mximum possible window,
+ * then want to send a window update to peer.
+ */
+ if (rcv_win > 0) {
+ u_short delta = 1 + cb->s_alo - cb->s_ack;
+ int adv = rcv_win - (delta * cb->s_mtu);
+
+ if ((so->so_rcv.sb_cc == 0 && adv >= (2 * cb->s_mtu)) ||
+ (100 * adv / so->so_rcv.sb_hiwat >= 35)) {
+ sppstat.spps_sndwinup++;
+ cb->s_flags |= SF_ACKNOW;
+ goto send;
+ }
+
+ }
+ /*
+ * Many comments from tcp_output.c are appropriate here
+ * including . . .
+ * If send window is too small, there is data to transmit, and no
+ * retransmit or persist is pending, then go to persist state.
+ * If nothing happens soon, send when timer expires:
+ * if window is nonzero, transmit what we can,
+ * otherwise send a probe.
+ */
+ if (so->so_snd.sb_cc && cb->s_timer[SPPT_REXMT] == 0 &&
+ cb->s_timer[SPPT_PERSIST] == 0) {
+ cb->s_rxtshift = 0;
+ spp_setpersist(cb);
+ }
+ /*
+ * No reason to send a packet, just return.
+ */
+ cb->s_outx = 1;
+ return (0);
+
+send:
+ /*
+ * Find requested packet.
+ */
+ si = 0;
+ if (len > 0) {
+ cb->s_want = cb->s_snxt;
+ for (m = sb->sb_mb; m; m = m->m_act) {
+ si = mtod(m, struct spidp *);
+ if (SSEQ_LEQ(cb->s_snxt, si->si_seq))
+ break;
+ }
+ found:
+ if (si) {
+ if (si->si_seq == cb->s_snxt)
+ cb->s_snxt++;
+ else
+ sppstat.spps_sndvoid++, si = 0;
+ }
+ }
+ /*
+ * update window
+ */
+ if (rcv_win < 0)
+ rcv_win = 0;
+ alo = cb->s_ack - 1 + (rcv_win / ((short)cb->s_mtu));
+ if (SSEQ_LT(alo, cb->s_alo))
+ alo = cb->s_alo;
+
+ if (si) {
+ /*
+ * must make a copy of this packet for
+ * idp_output to monkey with
+ */
+ m = m_copy(dtom(si), 0, (int)M_COPYALL);
+ if (m == NULL) {
+ return (ENOBUFS);
+ }
+ si = mtod(m, struct spidp *);
+ if (SSEQ_LT(si->si_seq, cb->s_smax))
+ sppstat.spps_sndrexmitpack++;
+ else
+ sppstat.spps_sndpack++;
+ } else if (cb->s_force || cb->s_flags & SF_ACKNOW) {
+ /*
+ * Must send an acknowledgement or a probe
+ */
+ if (cb->s_force)
+ sppstat.spps_sndprobe++;
+ if (cb->s_flags & SF_ACKNOW)
+ sppstat.spps_sndacks++;
+ m = m_gethdr(M_DONTWAIT, MT_HEADER);
+ if (m == 0)
+ return (ENOBUFS);
+ /*
+ * Fill in mbuf with extended SP header
+ * and addresses and length put into network format.
+ */
+ MH_ALIGN(m, sizeof (struct spidp));
+ m->m_len = sizeof (*si);
+ m->m_pkthdr.len = sizeof (*si);
+ si = mtod(m, struct spidp *);
+ si->si_i = *cb->s_idp;
+ si->si_s = cb->s_shdr;
+ si->si_seq = cb->s_smax + 1;
+ si->si_len = htons(sizeof (*si));
+ si->si_cc |= SP_SP;
+ } else {
+ cb->s_outx = 3;
+ if (so->so_options & SO_DEBUG || traceallspps)
+ spp_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
+ return (0);
+ }
+ /*
+ * Stuff checksum and output datagram.
+ */
+ if ((si->si_cc & SP_SP) == 0) {
+ if (cb->s_force != (1 + SPPT_PERSIST) ||
+ cb->s_timer[SPPT_PERSIST] == 0) {
+ /*
+ * If this is a new packet and we are not currently
+ * timing anything, time this one.
+ */
+ if (SSEQ_LT(cb->s_smax, si->si_seq)) {
+ cb->s_smax = si->si_seq;
+ if (cb->s_rtt == 0) {
+ sppstat.spps_segstimed++;
+ cb->s_rtseq = si->si_seq;
+ cb->s_rtt = 1;
+ }
+ }
+ /*
+ * Set rexmt timer if not currently set,
+ * Initial value for retransmit timer is smoothed
+ * round-trip time + 2 * round-trip time variance.
+ * Initialize shift counter which is used for backoff
+ * of retransmit time.
+ */
+ if (cb->s_timer[SPPT_REXMT] == 0 &&
+ cb->s_snxt != cb->s_rack) {
+ cb->s_timer[SPPT_REXMT] = cb->s_rxtcur;
+ if (cb->s_timer[SPPT_PERSIST]) {
+ cb->s_timer[SPPT_PERSIST] = 0;
+ cb->s_rxtshift = 0;
+ }
+ }
+ } else if (SSEQ_LT(cb->s_smax, si->si_seq)) {
+ cb->s_smax = si->si_seq;
+ }
+ } else if (cb->s_state < TCPS_ESTABLISHED) {
+ if (cb->s_rtt == 0)
+ cb->s_rtt = 1; /* Time initial handshake */
+ if (cb->s_timer[SPPT_REXMT] == 0)
+ cb->s_timer[SPPT_REXMT] = cb->s_rxtcur;
+ }
+ {
+ /*
+ * Do not request acks when we ack their data packets or
+ * when we do a gratuitous window update.
+ */
+ if (((si->si_cc & SP_SP) == 0) || cb->s_force)
+ si->si_cc |= SP_SA;
+ si->si_seq = htons(si->si_seq);
+ si->si_alo = htons(alo);
+ si->si_ack = htons(cb->s_ack);
+
+ if (idpcksum) {
+ si->si_sum = 0;
+ len = ntohs(si->si_len);
+ if (len & 1)
+ len++;
+ si->si_sum = ns_cksum(m, len);
+ } else
+ si->si_sum = 0xffff;
+
+ cb->s_outx = 4;
+ if (so->so_options & SO_DEBUG || traceallspps)
+ spp_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
+
+ if (so->so_options & SO_DONTROUTE)
+ error = ns_output(m, (struct route *)0, NS_ROUTETOIF);
+ else
+ error = ns_output(m, &cb->s_nspcb->nsp_route, 0);
+ }
+ if (error) {
+ return (error);
+ }
+ sppstat.spps_sndtotal++;
+ /*
+ * Data sent (as far as we can tell).
+ * If this advertises a larger window than any other segment,
+ * then remember the size of the advertized window.
+ * Any pending ACK has now been sent.
+ */
+ cb->s_force = 0;
+ cb->s_flags &= ~(SF_ACKNOW|SF_DELACK);
+ if (SSEQ_GT(alo, cb->s_alo))
+ cb->s_alo = alo;
+ if (sendalot)
+ goto again;
+ cb->s_outx = 5;
+ return (0);
+}
+
+int spp_do_persist_panics = 0;
+
+spp_setpersist(cb)
+ register struct sppcb *cb;
+{
+ register t = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
+ extern int spp_backoff[];
+
+ if (cb->s_timer[SPPT_REXMT] && spp_do_persist_panics)
+ panic("spp_output REXMT");
+ /*
+ * Start/restart persistance timer.
+ */
+ SPPT_RANGESET(cb->s_timer[SPPT_PERSIST],
+ t*spp_backoff[cb->s_rxtshift],
+ SPPTV_PERSMIN, SPPTV_PERSMAX);
+ if (cb->s_rxtshift < SPP_MAXRXTSHIFT)
+ cb->s_rxtshift++;
+}
+/*ARGSUSED*/
+spp_ctloutput(req, so, level, name, value)
+ int req;
+ struct socket *so;
+ int name;
+ struct mbuf **value;
+{
+ register struct mbuf *m;
+ struct nspcb *nsp = sotonspcb(so);
+ register struct sppcb *cb;
+ int mask, error = 0;
+
+ if (level != NSPROTO_SPP) {
+ /* This will have to be changed when we do more general
+ stacking of protocols */
+ return (idp_ctloutput(req, so, level, name, value));
+ }
+ if (nsp == NULL) {
+ error = EINVAL;
+ goto release;
+ } else
+ cb = nstosppcb(nsp);
+
+ switch (req) {
+
+ case PRCO_GETOPT:
+ if (value == NULL)
+ return (EINVAL);
+ m = m_get(M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return (ENOBUFS);
+ switch (name) {
+
+ case SO_HEADERS_ON_INPUT:
+ mask = SF_HI;
+ goto get_flags;
+
+ case SO_HEADERS_ON_OUTPUT:
+ mask = SF_HO;
+ get_flags:
+ m->m_len = sizeof(short);
+ *mtod(m, short *) = cb->s_flags & mask;
+ break;
+
+ case SO_MTU:
+ m->m_len = sizeof(u_short);
+ *mtod(m, short *) = cb->s_mtu;
+ break;
+
+ case SO_LAST_HEADER:
+ m->m_len = sizeof(struct sphdr);
+ *mtod(m, struct sphdr *) = cb->s_rhdr;
+ break;
+
+ case SO_DEFAULT_HEADERS:
+ m->m_len = sizeof(struct spidp);
+ *mtod(m, struct sphdr *) = cb->s_shdr;
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ *value = m;
+ break;
+
+ case PRCO_SETOPT:
+ if (value == 0 || *value == 0) {
+ error = EINVAL;
+ break;
+ }
+ switch (name) {
+ int *ok;
+
+ case SO_HEADERS_ON_INPUT:
+ mask = SF_HI;
+ goto set_head;
+
+ case SO_HEADERS_ON_OUTPUT:
+ mask = SF_HO;
+ set_head:
+ if (cb->s_flags & SF_PI) {
+ ok = mtod(*value, int *);
+ if (*ok)
+ cb->s_flags |= mask;
+ else
+ cb->s_flags &= ~mask;
+ } else error = EINVAL;
+ break;
+
+ case SO_MTU:
+ cb->s_mtu = *(mtod(*value, u_short *));
+ break;
+
+#ifdef SF_NEWCALL
+ case SO_NEWCALL:
+ ok = mtod(*value, int *);
+ if (*ok) {
+ cb->s_flags2 |= SF_NEWCALL;
+ spp_newchecks[5]++;
+ } else {
+ cb->s_flags2 &= ~SF_NEWCALL;
+ spp_newchecks[6]++;
+ }
+ break;
+#endif
+
+ case SO_DEFAULT_HEADERS:
+ {
+ register struct sphdr *sp
+ = mtod(*value, struct sphdr *);
+ cb->s_dt = sp->sp_dt;
+ cb->s_cc = sp->sp_cc & SP_EM;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ m_freem(*value);
+ break;
+ }
+ release:
+ return (error);
+}
+
+/*ARGSUSED*/
+spp_usrreq(so, req, m, nam, controlp)
+ struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *controlp;
+{
+ struct nspcb *nsp = sotonspcb(so);
+ register struct sppcb *cb;
+ int s = splnet();
+ int error = 0, ostate;
+ struct mbuf *mm;
+ register struct sockbuf *sb;
+
+ if (req == PRU_CONTROL)
+ return (ns_control(so, (int)m, (caddr_t)nam,
+ (struct ifnet *)controlp));
+ if (nsp == NULL) {
+ if (req != PRU_ATTACH) {
+ error = EINVAL;
+ goto release;
+ }
+ } else
+ cb = nstosppcb(nsp);
+
+ ostate = cb ? cb->s_state : 0;
+
+ switch (req) {
+
+ case PRU_ATTACH:
+ if (nsp != NULL) {
+ error = EISCONN;
+ break;
+ }
+ error = ns_pcballoc(so, &nspcb);
+ if (error)
+ break;
+ if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
+ error = soreserve(so, (u_long) 3072, (u_long) 3072);
+ if (error)
+ break;
+ }
+ nsp = sotonspcb(so);
+
+ mm = m_getclr(M_DONTWAIT, MT_PCB);
+ sb = &so->so_snd;
+
+ if (mm == NULL) {
+ error = ENOBUFS;
+ break;
+ }
+ cb = mtod(mm, struct sppcb *);
+ mm = m_getclr(M_DONTWAIT, MT_HEADER);
+ if (mm == NULL) {
+ (void) m_free(dtom(m));
+ error = ENOBUFS;
+ break;
+ }
+ cb->s_idp = mtod(mm, struct idp *);
+ cb->s_state = TCPS_LISTEN;
+ cb->s_smax = -1;
+ cb->s_swl1 = -1;
+ cb->s_q.si_next = cb->s_q.si_prev = &cb->s_q;
+ cb->s_nspcb = nsp;
+ cb->s_mtu = 576 - sizeof (struct spidp);
+ cb->s_cwnd = sbspace(sb) * CUNIT / cb->s_mtu;
+ cb->s_ssthresh = cb->s_cwnd;
+ cb->s_cwmx = sbspace(sb) * CUNIT /
+ (2 * sizeof (struct spidp));
+ /* Above is recomputed when connecting to account
+ for changed buffering or mtu's */
+ cb->s_rtt = SPPTV_SRTTBASE;
+ cb->s_rttvar = SPPTV_SRTTDFLT << 2;
+ SPPT_RANGESET(cb->s_rxtcur,
+ ((SPPTV_SRTTBASE >> 2) + (SPPTV_SRTTDFLT << 2)) >> 1,
+ SPPTV_MIN, SPPTV_REXMTMAX);
+ nsp->nsp_pcb = (caddr_t) cb;
+ break;
+
+ case PRU_DETACH:
+ if (nsp == NULL) {
+ error = ENOTCONN;
+ break;
+ }
+ if (cb->s_state > TCPS_LISTEN)
+ cb = spp_disconnect(cb);
+ else
+ cb = spp_close(cb);
+ break;
+
+ case PRU_BIND:
+ error = ns_pcbbind(nsp, nam);
+ break;
+
+ case PRU_LISTEN:
+ if (nsp->nsp_lport == 0)
+ error = ns_pcbbind(nsp, (struct mbuf *)0);
+ if (error == 0)
+ cb->s_state = TCPS_LISTEN;
+ break;
+
+ /*
+ * Initiate connection to peer.
+ * Enter SYN_SENT state, and mark socket as connecting.
+ * Start keep-alive timer, setup prototype header,
+ * Send initial system packet requesting connection.
+ */
+ case PRU_CONNECT:
+ if (nsp->nsp_lport == 0) {
+ error = ns_pcbbind(nsp, (struct mbuf *)0);
+ if (error)
+ break;
+ }
+ error = ns_pcbconnect(nsp, nam);
+ if (error)
+ break;
+ soisconnecting(so);
+ sppstat.spps_connattempt++;
+ cb->s_state = TCPS_SYN_SENT;
+ cb->s_did = 0;
+ spp_template(cb);
+ cb->s_timer[SPPT_KEEP] = SPPTV_KEEP;
+ cb->s_force = 1 + SPPTV_KEEP;
+ /*
+ * Other party is required to respond to
+ * the port I send from, but he is not
+ * required to answer from where I am sending to,
+ * so allow wildcarding.
+ * original port I am sending to is still saved in
+ * cb->s_dport.
+ */
+ nsp->nsp_fport = 0;
+ error = spp_output(cb, (struct mbuf *) 0);
+ break;
+
+ case PRU_CONNECT2:
+ error = EOPNOTSUPP;
+ break;
+
+ /*
+ * We may decide later to implement connection closing
+ * handshaking at the spp level optionally.
+ * here is the hook to do it:
+ */
+ case PRU_DISCONNECT:
+ cb = spp_disconnect(cb);
+ break;
+
+ /*
+ * Accept a connection. Essentially all the work is
+ * done at higher levels; just return the address
+ * of the peer, storing through addr.
+ */
+ case PRU_ACCEPT: {
+ struct sockaddr_ns *sns = mtod(nam, struct sockaddr_ns *);
+
+ nam->m_len = sizeof (struct sockaddr_ns);
+ sns->sns_family = AF_NS;
+ sns->sns_addr = nsp->nsp_faddr;
+ break;
+ }
+
+ case PRU_SHUTDOWN:
+ socantsendmore(so);
+ cb = spp_usrclosed(cb);
+ if (cb)
+ error = spp_output(cb, (struct mbuf *) 0);
+ break;
+
+ /*
+ * After a receive, possibly send acknowledgment
+ * updating allocation.
+ */
+ case PRU_RCVD:
+ cb->s_flags |= SF_RVD;
+ (void) spp_output(cb, (struct mbuf *) 0);
+ cb->s_flags &= ~SF_RVD;
+ break;
+
+ case PRU_ABORT:
+ (void) spp_drop(cb, ECONNABORTED);
+ break;
+
+ case PRU_SENSE:
+ case PRU_CONTROL:
+ m = NULL;
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_RCVOOB:
+ if ((cb->s_oobflags & SF_IOOB) || so->so_oobmark ||
+ (so->so_state & SS_RCVATMARK)) {
+ m->m_len = 1;
+ *mtod(m, caddr_t) = cb->s_iobc;
+ break;
+ }
+ error = EINVAL;
+ break;
+
+ case PRU_SENDOOB:
+ if (sbspace(&so->so_snd) < -512) {
+ error = ENOBUFS;
+ break;
+ }
+ cb->s_oobflags |= SF_SOOB;
+ /* fall into */
+ case PRU_SEND:
+ if (controlp) {
+ u_short *p = mtod(controlp, u_short *);
+ spp_newchecks[2]++;
+ if ((p[0] == 5) && p[1] == 1) { /* XXXX, for testing */
+ cb->s_shdr.sp_dt = *(u_char *)(&p[2]);
+ spp_newchecks[3]++;
+ }
+ m_freem(controlp);
+ }
+ controlp = NULL;
+ error = spp_output(cb, m);
+ m = NULL;
+ break;
+
+ case PRU_SOCKADDR:
+ ns_setsockaddr(nsp, nam);
+ break;
+
+ case PRU_PEERADDR:
+ ns_setpeeraddr(nsp, nam);
+ break;
+
+ case PRU_SLOWTIMO:
+ cb = spp_timers(cb, (int)nam);
+ req |= ((int)nam) << 8;
+ break;
+
+ case PRU_FASTTIMO:
+ case PRU_PROTORCV:
+ case PRU_PROTOSEND:
+ error = EOPNOTSUPP;
+ break;
+
+ default:
+ panic("sp_usrreq");
+ }
+ if (cb && (so->so_options & SO_DEBUG || traceallspps))
+ spp_trace(SA_USER, (u_char)ostate, cb, (struct spidp *)0, req);
+release:
+ if (controlp != NULL)
+ m_freem(controlp);
+ if (m != NULL)
+ m_freem(m);
+ splx(s);
+ return (error);
+}
+
+spp_usrreq_sp(so, req, m, nam, controlp)
+ struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *controlp;
+{
+ int error = spp_usrreq(so, req, m, nam, controlp);
+
+ if (req == PRU_ATTACH && error == 0) {
+ struct nspcb *nsp = sotonspcb(so);
+ ((struct sppcb *)nsp->nsp_pcb)->s_flags |=
+ (SF_HI | SF_HO | SF_PI);
+ }
+ return (error);
+}
+
+/*
+ * Create template to be used to send spp packets on a connection.
+ * Called after host entry created, fills
+ * in a skeletal spp header (choosing connection id),
+ * minimizing the amount of work necessary when the connection is used.
+ */
+spp_template(cb)
+ register struct sppcb *cb;
+{
+ register struct nspcb *nsp = cb->s_nspcb;
+ register struct idp *idp = cb->s_idp;
+ register struct sockbuf *sb = &(nsp->nsp_socket->so_snd);
+
+ idp->idp_pt = NSPROTO_SPP;
+ idp->idp_sna = nsp->nsp_laddr;
+ idp->idp_dna = nsp->nsp_faddr;
+ cb->s_sid = htons(spp_iss);
+ spp_iss += SPP_ISSINCR/2;
+ cb->s_alo = 1;
+ cb->s_cwnd = (sbspace(sb) * CUNIT) / cb->s_mtu;
+ cb->s_ssthresh = cb->s_cwnd; /* Try to expand fast to full complement
+ of large packets */
+ cb->s_cwmx = (sbspace(sb) * CUNIT) / (2 * sizeof(struct spidp));
+ cb->s_cwmx = max(cb->s_cwmx, cb->s_cwnd);
+ /* But allow for lots of little packets as well */
+}
+
+/*
+ * Close a SPIP control block:
+ * discard spp control block itself
+ * discard ns protocol control block
+ * wake up any sleepers
+ */
+struct sppcb *
+spp_close(cb)
+ register struct sppcb *cb;
+{
+ register struct spidp_q *s;
+ struct nspcb *nsp = cb->s_nspcb;
+ struct socket *so = nsp->nsp_socket;
+ register struct mbuf *m;
+
+ s = cb->s_q.si_next;
+ while (s != &(cb->s_q)) {
+ s = s->si_next;
+ m = dtom(s->si_prev);
+ remque(s->si_prev);
+ m_freem(m);
+ }
+ (void) m_free(dtom(cb->s_idp));
+ (void) m_free(dtom(cb));
+ nsp->nsp_pcb = 0;
+ soisdisconnected(so);
+ ns_pcbdetach(nsp);
+ sppstat.spps_closed++;
+ return ((struct sppcb *)0);
+}
+/*
+ * Someday we may do level 3 handshaking
+ * to close a connection or send a xerox style error.
+ * For now, just close.
+ */
+struct sppcb *
+spp_usrclosed(cb)
+ register struct sppcb *cb;
+{
+ return (spp_close(cb));
+}
+struct sppcb *
+spp_disconnect(cb)
+ register struct sppcb *cb;
+{
+ return (spp_close(cb));
+}
+/*
+ * Drop connection, reporting
+ * the specified error.
+ */
+struct sppcb *
+spp_drop(cb, errno)
+ register struct sppcb *cb;
+ int errno;
+{
+ struct socket *so = cb->s_nspcb->nsp_socket;
+
+ /*
+ * someday, in the xerox world
+ * we will generate error protocol packets
+ * announcing that the socket has gone away.
+ */
+ if (TCPS_HAVERCVDSYN(cb->s_state)) {
+ sppstat.spps_drops++;
+ cb->s_state = TCPS_CLOSED;
+ /*(void) tcp_output(cb);*/
+ } else
+ sppstat.spps_conndrops++;
+ so->so_error = errno;
+ return (spp_close(cb));
+}
+
+spp_abort(nsp)
+ struct nspcb *nsp;
+{
+
+ (void) spp_close((struct sppcb *)nsp->nsp_pcb);
+}
+
+int spp_backoff[SPP_MAXRXTSHIFT+1] =
+ { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
+/*
+ * Fast timeout routine for processing delayed acks
+ */
+spp_fasttimo()
+{
+ register struct nspcb *nsp;
+ register struct sppcb *cb;
+ int s = splnet();
+
+ nsp = nspcb.nsp_next;
+ if (nsp)
+ for (; nsp != &nspcb; nsp = nsp->nsp_next)
+ if ((cb = (struct sppcb *)nsp->nsp_pcb) &&
+ (cb->s_flags & SF_DELACK)) {
+ cb->s_flags &= ~SF_DELACK;
+ cb->s_flags |= SF_ACKNOW;
+ sppstat.spps_delack++;
+ (void) spp_output(cb, (struct mbuf *) 0);
+ }
+ splx(s);
+}
+
+/*
+ * spp protocol timeout routine called every 500 ms.
+ * Updates the timers in all active pcb's and
+ * causes finite state machine actions if timers expire.
+ */
+spp_slowtimo()
+{
+ register struct nspcb *ip, *ipnxt;
+ register struct sppcb *cb;
+ int s = splnet();
+ register int i;
+
+ /*
+ * Search through tcb's and update active timers.
+ */
+ ip = nspcb.nsp_next;
+ if (ip == 0) {
+ splx(s);
+ return;
+ }
+ while (ip != &nspcb) {
+ cb = nstosppcb(ip);
+ ipnxt = ip->nsp_next;
+ if (cb == 0)
+ goto tpgone;
+ for (i = 0; i < SPPT_NTIMERS; i++) {
+ if (cb->s_timer[i] && --cb->s_timer[i] == 0) {
+ (void) spp_usrreq(cb->s_nspcb->nsp_socket,
+ PRU_SLOWTIMO, (struct mbuf *)0,
+ (struct mbuf *)i, (struct mbuf *)0,
+ (struct mbuf *)0);
+ if (ipnxt->nsp_prev != ip)
+ goto tpgone;
+ }
+ }
+ cb->s_idle++;
+ if (cb->s_rtt)
+ cb->s_rtt++;
+tpgone:
+ ip = ipnxt;
+ }
+ spp_iss += SPP_ISSINCR/PR_SLOWHZ; /* increment iss */
+ splx(s);
+}
+/*
+ * SPP timer processing.
+ */
+struct sppcb *
+spp_timers(cb, timer)
+ register struct sppcb *cb;
+ int timer;
+{
+ long rexmt;
+ int win;
+
+ cb->s_force = 1 + timer;
+ switch (timer) {
+
+ /*
+ * 2 MSL timeout in shutdown went off. TCP deletes connection
+ * control block.
+ */
+ case SPPT_2MSL:
+ printf("spp: SPPT_2MSL went off for no reason\n");
+ cb->s_timer[timer] = 0;
+ break;
+
+ /*
+ * Retransmission timer went off. Message has not
+ * been acked within retransmit interval. Back off
+ * to a longer retransmit interval and retransmit one packet.
+ */
+ case SPPT_REXMT:
+ if (++cb->s_rxtshift > SPP_MAXRXTSHIFT) {
+ cb->s_rxtshift = SPP_MAXRXTSHIFT;
+ sppstat.spps_timeoutdrop++;
+ cb = spp_drop(cb, ETIMEDOUT);
+ break;
+ }
+ sppstat.spps_rexmttimeo++;
+ rexmt = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
+ rexmt *= spp_backoff[cb->s_rxtshift];
+ SPPT_RANGESET(cb->s_rxtcur, rexmt, SPPTV_MIN, SPPTV_REXMTMAX);
+ cb->s_timer[SPPT_REXMT] = cb->s_rxtcur;
+ /*
+ * If we have backed off fairly far, our srtt
+ * estimate is probably bogus. Clobber it
+ * so we'll take the next rtt measurement as our srtt;
+ * move the current srtt into rttvar to keep the current
+ * retransmit times until then.
+ */
+ if (cb->s_rxtshift > SPP_MAXRXTSHIFT / 4 ) {
+ cb->s_rttvar += (cb->s_srtt >> 2);
+ cb->s_srtt = 0;
+ }
+ cb->s_snxt = cb->s_rack;
+ /*
+ * If timing a packet, stop the timer.
+ */
+ cb->s_rtt = 0;
+ /*
+ * See very long discussion in tcp_timer.c about congestion
+ * window and sstrhesh
+ */
+ win = min(cb->s_swnd, (cb->s_cwnd/CUNIT)) / 2;
+ if (win < 2)
+ win = 2;
+ cb->s_cwnd = CUNIT;
+ cb->s_ssthresh = win * CUNIT;
+ (void) spp_output(cb, (struct mbuf *) 0);
+ break;
+
+ /*
+ * Persistance timer into zero window.
+ * Force a probe to be sent.
+ */
+ case SPPT_PERSIST:
+ sppstat.spps_persisttimeo++;
+ spp_setpersist(cb);
+ (void) spp_output(cb, (struct mbuf *) 0);
+ break;
+
+ /*
+ * Keep-alive timer went off; send something
+ * or drop connection if idle for too long.
+ */
+ case SPPT_KEEP:
+ sppstat.spps_keeptimeo++;
+ if (cb->s_state < TCPS_ESTABLISHED)
+ goto dropit;
+ if (cb->s_nspcb->nsp_socket->so_options & SO_KEEPALIVE) {
+ if (cb->s_idle >= SPPTV_MAXIDLE)
+ goto dropit;
+ sppstat.spps_keepprobe++;
+ (void) spp_output(cb, (struct mbuf *) 0);
+ } else
+ cb->s_idle = 0;
+ cb->s_timer[SPPT_KEEP] = SPPTV_KEEP;
+ break;
+ dropit:
+ sppstat.spps_keepdrops++;
+ cb = spp_drop(cb, ETIMEDOUT);
+ break;
+ }
+ return (cb);
+}
+#ifndef lint
+int SppcbSize = sizeof (struct sppcb);
+int NspcbSize = sizeof (struct nspcb);
+#endif /* lint */
diff --git a/sys/netns/spp_var.h b/sys/netns/spp_var.h
new file mode 100644
index 0000000..0d44f63
--- /dev/null
+++ b/sys/netns/spp_var.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 1984, 1985, 1986, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)spp_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Sp control block, one per connection
+ */
+struct sppcb {
+ struct spidp_q s_q; /* queue for out-of-order receipt */
+ struct nspcb *s_nspcb; /* backpointer to internet pcb */
+ u_char s_state;
+ u_char s_flags;
+#define SF_ACKNOW 0x01 /* Ack peer immediately */
+#define SF_DELACK 0x02 /* Ack, but try to delay it */
+#define SF_HI 0x04 /* Show headers on input */
+#define SF_HO 0x08 /* Show headers on output */
+#define SF_PI 0x10 /* Packet (datagram) interface */
+#define SF_WIN 0x20 /* Window info changed */
+#define SF_RXT 0x40 /* Rxt info changed */
+#define SF_RVD 0x80 /* Calling from read usrreq routine */
+ u_short s_mtu; /* Max packet size for this stream */
+/* use sequence fields in headers to store sequence numbers for this
+ connection */
+ struct idp *s_idp;
+ struct sphdr s_shdr; /* prototype header to transmit */
+#define s_cc s_shdr.sp_cc /* connection control (for EM bit) */
+#define s_dt s_shdr.sp_dt /* datastream type */
+#define s_sid s_shdr.sp_sid /* source connection identifier */
+#define s_did s_shdr.sp_did /* destination connection identifier */
+#define s_seq s_shdr.sp_seq /* sequence number */
+#define s_ack s_shdr.sp_ack /* acknowledge number */
+#define s_alo s_shdr.sp_alo /* allocation number */
+#define s_dport s_idp->idp_dna.x_port /* where we are sending */
+ struct sphdr s_rhdr; /* last received header (in effect!)*/
+ u_short s_rack; /* their acknowledge number */
+ u_short s_ralo; /* their allocation number */
+ u_short s_smax; /* highest packet # we have sent */
+ u_short s_snxt; /* which packet to send next */
+
+/* congestion control */
+#define CUNIT 1024 /* scaling for ... */
+ int s_cwnd; /* Congestion-controlled window */
+ /* in packets * CUNIT */
+ short s_swnd; /* == tcp snd_wnd, in packets */
+ short s_smxw; /* == tcp max_sndwnd */
+ /* difference of two spp_seq's can be
+ no bigger than a short */
+ u_short s_swl1; /* == tcp snd_wl1 */
+ u_short s_swl2; /* == tcp snd_wl2 */
+ int s_cwmx; /* max allowable cwnd */
+ int s_ssthresh; /* s_cwnd size threshhold for
+ * slow start exponential-to-
+ * linear switch */
+/* transmit timing stuff
+ * srtt and rttvar are stored as fixed point, for convenience in smoothing.
+ * srtt has 3 bits to the right of the binary point, rttvar has 2.
+ */
+ short s_idle; /* time idle */
+ short s_timer[SPPT_NTIMERS]; /* timers */
+ short s_rxtshift; /* log(2) of rexmt exp. backoff */
+ short s_rxtcur; /* current retransmit value */
+ u_short s_rtseq; /* packet being timed */
+ short s_rtt; /* timer for round trips */
+ short s_srtt; /* averaged timer */
+ short s_rttvar; /* variance in round trip time */
+ char s_force; /* which timer expired */
+ char s_dupacks; /* counter to intuit xmt loss */
+
+/* out of band data */
+ char s_oobflags;
+#define SF_SOOB 0x08 /* sending out of band data */
+#define SF_IOOB 0x10 /* receiving out of band data */
+ char s_iobc; /* input characters */
+/* debug stuff */
+ u_short s_want; /* Last candidate for sending */
+ char s_outx; /* exit taken from spp_output */
+ char s_inx; /* exit taken from spp_input */
+ u_short s_flags2; /* more flags for testing */
+#define SF_NEWCALL 0x100 /* for new_recvmsg */
+#define SO_NEWCALL 10 /* for new_recvmsg */
+};
+
+#define nstosppcb(np) ((struct sppcb *)(np)->nsp_pcb)
+#define sotosppcb(so) (nstosppcb(sotonspcb(so)))
+
+struct sppstat {
+ long spps_connattempt; /* connections initiated */
+ long spps_accepts; /* connections accepted */
+ long spps_connects; /* connections established */
+ long spps_drops; /* connections dropped */
+ long spps_conndrops; /* embryonic connections dropped */
+ long spps_closed; /* conn. closed (includes drops) */
+ long spps_segstimed; /* segs where we tried to get rtt */
+ long spps_rttupdated; /* times we succeeded */
+ long spps_delack; /* delayed acks sent */
+ long spps_timeoutdrop; /* conn. dropped in rxmt timeout */
+ long spps_rexmttimeo; /* retransmit timeouts */
+ long spps_persisttimeo; /* persist timeouts */
+ long spps_keeptimeo; /* keepalive timeouts */
+ long spps_keepprobe; /* keepalive probes sent */
+ long spps_keepdrops; /* connections dropped in keepalive */
+
+ long spps_sndtotal; /* total packets sent */
+ long spps_sndpack; /* data packets sent */
+ long spps_sndbyte; /* data bytes sent */
+ long spps_sndrexmitpack; /* data packets retransmitted */
+ long spps_sndrexmitbyte; /* data bytes retransmitted */
+ long spps_sndacks; /* ack-only packets sent */
+ long spps_sndprobe; /* window probes sent */
+ long spps_sndurg; /* packets sent with URG only */
+ long spps_sndwinup; /* window update-only packets sent */
+ long spps_sndctrl; /* control (SYN|FIN|RST) packets sent */
+ long spps_sndvoid; /* couldn't find requested packet*/
+
+ long spps_rcvtotal; /* total packets received */
+ long spps_rcvpack; /* packets received in sequence */
+ long spps_rcvbyte; /* bytes received in sequence */
+ long spps_rcvbadsum; /* packets received with ccksum errs */
+ long spps_rcvbadoff; /* packets received with bad offset */
+ long spps_rcvshort; /* packets received too short */
+ long spps_rcvduppack; /* duplicate-only packets received */
+ long spps_rcvdupbyte; /* duplicate-only bytes received */
+ long spps_rcvpartduppack; /* packets with some duplicate data */
+ long spps_rcvpartdupbyte; /* dup. bytes in part-dup. packets */
+ long spps_rcvoopack; /* out-of-order packets received */
+ long spps_rcvoobyte; /* out-of-order bytes received */
+ long spps_rcvpackafterwin; /* packets with data after window */
+ long spps_rcvbyteafterwin; /* bytes rcvd after window */
+ long spps_rcvafterclose; /* packets rcvd after "close" */
+ long spps_rcvwinprobe; /* rcvd window probe packets */
+ long spps_rcvdupack; /* rcvd duplicate acks */
+ long spps_rcvacktoomuch; /* rcvd acks for unsent data */
+ long spps_rcvackpack; /* rcvd ack packets */
+ long spps_rcvackbyte; /* bytes acked by rcvd acks */
+ long spps_rcvwinupd; /* rcvd window update packets */
+};
+struct spp_istat {
+ short hdrops;
+ short badsum;
+ short badlen;
+ short slotim;
+ short fastim;
+ short nonucn;
+ short noconn;
+ short notme;
+ short wrncon;
+ short bdreas;
+ short gonawy;
+ short notyet;
+ short lstdup;
+ struct sppstat newstats;
+};
+
+#ifdef KERNEL
+struct spp_istat spp_istat;
+
+/* Following was struct sppstat sppstat; */
+#ifndef sppstat
+#define sppstat spp_istat.newstats
+#endif
+
+u_short spp_iss;
+extern struct sppcb *spp_close(), *spp_disconnect(),
+ *spp_usrclosed(), *spp_timers(), *spp_drop();
+#endif
+
+#define SPP_ISSINCR 128
+/*
+ * SPP sequence numbers are 16 bit integers operated
+ * on with modular arithmetic. These macros can be
+ * used to compare such integers.
+ */
+#ifdef sun
+short xnsCbug;
+#define SSEQ_LT(a,b) ((xnsCbug = (short)((a)-(b))) < 0)
+#define SSEQ_LEQ(a,b) ((xnsCbug = (short)((a)-(b))) <= 0)
+#define SSEQ_GT(a,b) ((xnsCbug = (short)((a)-(b))) > 0)
+#define SSEQ_GEQ(a,b) ((xnsCbug = (short)((a)-(b))) >= 0)
+#else
+#define SSEQ_LT(a,b) (((short)((a)-(b))) < 0)
+#define SSEQ_LEQ(a,b) (((short)((a)-(b))) <= 0)
+#define SSEQ_GT(a,b) (((short)((a)-(b))) > 0)
+#define SSEQ_GEQ(a,b) (((short)((a)-(b))) >= 0)
+#endif
diff --git a/sys/nfs/nfs.h b/sys/nfs/nfs.h
new file mode 100644
index 0000000..261fd42
--- /dev/null
+++ b/sys/nfs/nfs.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Tunable constants for nfs
+ */
+
+#define NFS_MAXIOVEC 34
+#define NFS_HZ 25 /* Ticks per second for NFS timeouts */
+#define NFS_TIMEO (1*NFS_HZ) /* Default timeout = 1 second */
+#define NFS_MINTIMEO (1*NFS_HZ) /* Min timeout to use */
+#define NFS_MAXTIMEO (60*NFS_HZ) /* Max timeout to backoff to */
+#define NFS_MINIDEMTIMEO (5*NFS_HZ) /* Min timeout for non-idempotent ops*/
+#define NFS_MAXREXMIT 100 /* Stop counting after this many */
+#define NFS_MAXWINDOW 1024 /* Max number of outstanding requests */
+#define NFS_RETRANS 10 /* Num of retrans for soft mounts */
+#define NFS_MAXGRPS 16 /* Max. size of groups list */
+#define NFS_MINATTRTIMO 5 /* Attribute cache timeout in sec */
+#define NFS_MAXATTRTIMO 60
+#define NFS_WSIZE 8192 /* Def. write data size <= 8192 */
+#define NFS_RSIZE 8192 /* Def. read data size <= 8192 */
+#define NFS_DEFRAHEAD 1 /* Def. read ahead # blocks */
+#define NFS_MAXRAHEAD 4 /* Max. read ahead # blocks */
+#define NFS_MAXREADDIR NFS_MAXDATA /* Max. size of directory read */
+#define NFS_MAXUIDHASH 64 /* Max. # of hashed uid entries/mp */
+#define NFS_MAXASYNCDAEMON 20 /* Max. number async_daemons runable */
+#define NFS_DIRBLKSIZ 1024 /* Size of an NFS directory block */
+#define NMOD(a) ((a) % nfs_asyncdaemons)
+
+/*
+ * Set the attribute timeout based on how recently the file has been modified.
+ */
+#define NFS_ATTRTIMEO(np) \
+ ((((np)->n_flag & NMODIFIED) || \
+ (time.tv_sec - (np)->n_mtime) / 10 < NFS_MINATTRTIMO) ? NFS_MINATTRTIMO : \
+ ((time.tv_sec - (np)->n_mtime) / 10 > NFS_MAXATTRTIMO ? NFS_MAXATTRTIMO : \
+ (time.tv_sec - (np)->n_mtime) / 10))
+
+/*
+ * Structures for the nfssvc(2) syscall. Not that anyone but nfsd and mount_nfs
+ * should ever try and use it.
+ */
+struct nfsd_args {
+ int sock; /* Socket to serve */
+ caddr_t name; /* Client address for connection based sockets */
+ int namelen; /* Length of name */
+};
+
+struct nfsd_srvargs {
+ struct nfsd *nsd_nfsd; /* Pointer to in kernel nfsd struct */
+ uid_t nsd_uid; /* Effective uid mapped to cred */
+ u_long nsd_haddr; /* Ip address of client */
+ struct ucred nsd_cr; /* Cred. uid maps to */
+ int nsd_authlen; /* Length of auth string (ret) */
+ char *nsd_authstr; /* Auth string (ret) */
+};
+
+struct nfsd_cargs {
+ char *ncd_dirp; /* Mount dir path */
+ uid_t ncd_authuid; /* Effective uid */
+ int ncd_authtype; /* Type of authenticator */
+ int ncd_authlen; /* Length of authenticator string */
+ char *ncd_authstr; /* Authenticator string */
+};
+
+/*
+ * Stats structure
+ */
+struct nfsstats {
+ int attrcache_hits;
+ int attrcache_misses;
+ int lookupcache_hits;
+ int lookupcache_misses;
+ int direofcache_hits;
+ int direofcache_misses;
+ int biocache_reads;
+ int read_bios;
+ int read_physios;
+ int biocache_writes;
+ int write_bios;
+ int write_physios;
+ int biocache_readlinks;
+ int readlink_bios;
+ int biocache_readdirs;
+ int readdir_bios;
+ int rpccnt[NFS_NPROCS];
+ int rpcretries;
+ int srvrpccnt[NFS_NPROCS];
+ int srvrpc_errs;
+ int srv_errs;
+ int rpcrequests;
+ int rpctimeouts;
+ int rpcunexpected;
+ int rpcinvalid;
+ int srvcache_inproghits;
+ int srvcache_idemdonehits;
+ int srvcache_nonidemdonehits;
+ int srvcache_misses;
+ int srvnqnfs_leases;
+ int srvnqnfs_maxleases;
+ int srvnqnfs_getleases;
+};
+
+/*
+ * Flags for nfssvc() system call.
+ */
+#define NFSSVC_BIOD 0x002
+#define NFSSVC_NFSD 0x004
+#define NFSSVC_ADDSOCK 0x008
+#define NFSSVC_AUTHIN 0x010
+#define NFSSVC_GOTAUTH 0x040
+#define NFSSVC_AUTHINFAIL 0x080
+#define NFSSVC_MNTD 0x100
+
+/*
+ * The set of signals the interrupt an I/O in progress for NFSMNT_INT mounts.
+ * What should be in this set is open to debate, but I believe that since
+ * I/O system calls on ufs are never interrupted by signals the set should
+ * be minimal. My reasoning is that many current programs that use signals
+ * such as SIGALRM will not expect file I/O system calls to be interrupted
+ * by them and break.
+ */
+#ifdef KERNEL
+#define NFSINT_SIGMASK (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \
+ sigmask(SIGHUP)|sigmask(SIGQUIT))
+
+/*
+ * Socket errors ignored for connectionless sockets??
+ * For now, ignore them all
+ */
+#define NFSIGNORE_SOERROR(s, e) \
+ ((e) != EINTR && (e) != ERESTART && (e) != EWOULDBLOCK && \
+ ((s) & PR_CONNREQUIRED) == 0)
+
+/*
+ * Nfs outstanding request list element
+ */
+struct nfsreq {
+ struct nfsreq *r_next;
+ struct nfsreq *r_prev;
+ struct mbuf *r_mreq;
+ struct mbuf *r_mrep;
+ struct mbuf *r_md;
+ caddr_t r_dpos;
+ struct nfsmount *r_nmp;
+ struct vnode *r_vp;
+ u_long r_xid;
+ int r_flags; /* flags on request, see below */
+ int r_retry; /* max retransmission count */
+ int r_rexmit; /* current retrans count */
+ int r_timer; /* tick counter on reply */
+ int r_procnum; /* NFS procedure number */
+ int r_rtt; /* RTT for rpc */
+ struct proc *r_procp; /* Proc that did I/O system call */
+};
+
+/* Flag values for r_flags */
+#define R_TIMING 0x01 /* timing request (in mntp) */
+#define R_SENT 0x02 /* request has been sent */
+#define R_SOFTTERM 0x04 /* soft mnt, too many retries */
+#define R_INTR 0x08 /* intr mnt, signal pending */
+#define R_SOCKERR 0x10 /* Fatal error on socket */
+#define R_TPRINTFMSG 0x20 /* Did a tprintf msg. */
+#define R_MUSTRESEND 0x40 /* Must resend request */
+#define R_GETONEREP 0x80 /* Probe for one reply only */
+
+struct nfsstats nfsstats;
+
+/*
+ * A list of nfssvc_sock structures is maintained with all the sockets
+ * that require service by the nfsd.
+ * The nfsuid structs hang off of the nfssvc_sock structs in both lru
+ * and uid hash lists.
+ */
+#define NUIDHASHSIZ 32
+#define NUIDHASH(uid) ((uid) & (NUIDHASHSIZ - 1))
+
+/*
+ * Network address hash list element
+ */
+union nethostaddr {
+ u_long had_inetaddr;
+ struct mbuf *had_nam;
+};
+
+struct nfsuid {
+ struct nfsuid *nu_lrunext; /* MUST be first */
+ struct nfsuid *nu_lruprev;
+ struct nfsuid *nu_hnext;
+ struct nfsuid *nu_hprev;
+ int nu_flag; /* Flags */
+ uid_t nu_uid; /* Uid mapped by this entry */
+ union nethostaddr nu_haddr; /* Host addr. for dgram sockets */
+ struct ucred nu_cr; /* Cred uid mapped to */
+};
+
+#define nu_inetaddr nu_haddr.had_inetaddr
+#define nu_nam nu_haddr.had_nam
+/* Bits for nu_flag */
+#define NU_INETADDR 0x1
+
+struct nfssvc_sock {
+ struct nfsuid *ns_lrunext; /* MUST be first */
+ struct nfsuid *ns_lruprev;
+ struct nfssvc_sock *ns_next;
+ struct nfssvc_sock *ns_prev;
+ int ns_flag;
+ u_long ns_sref;
+ struct file *ns_fp;
+ struct socket *ns_so;
+ int ns_solock;
+ struct mbuf *ns_nam;
+ int ns_cc;
+ struct mbuf *ns_raw;
+ struct mbuf *ns_rawend;
+ int ns_reclen;
+ struct mbuf *ns_rec;
+ struct mbuf *ns_recend;
+ int ns_numuids;
+ struct nfsuid *ns_uidh[NUIDHASHSIZ];
+};
+
+/* Bits for "ns_flag" */
+#define SLP_VALID 0x01
+#define SLP_DOREC 0x02
+#define SLP_NEEDQ 0x04
+#define SLP_DISCONN 0x08
+#define SLP_GETSTREAM 0x10
+#define SLP_INIT 0x20
+#define SLP_WANTINIT 0x40
+
+#define SLP_ALLFLAGS 0xff
+
+/*
+ * One of these structures is allocated for each nfsd.
+ */
+struct nfsd {
+ struct nfsd *nd_next; /* Must be first */
+ struct nfsd *nd_prev;
+ int nd_flag; /* NFSD_ flags */
+ struct nfssvc_sock *nd_slp; /* Current socket */
+ struct mbuf *nd_nam; /* Client addr for datagram req. */
+ struct mbuf *nd_mrep; /* Req. mbuf list */
+ struct mbuf *nd_md;
+ caddr_t nd_dpos; /* Position in list */
+ int nd_procnum; /* RPC procedure number */
+ u_long nd_retxid; /* RPC xid */
+ int nd_repstat; /* Reply status value */
+ struct ucred nd_cr; /* Credentials for req. */
+ int nd_nqlflag; /* Leasing flag */
+ int nd_duration; /* Lease duration */
+ int nd_authlen; /* Authenticator len */
+ u_char nd_authstr[RPCAUTH_MAXSIZ]; /* Authenticator data */
+ struct proc *nd_procp; /* Proc ptr */
+};
+
+#define NFSD_WAITING 0x01
+#define NFSD_CHECKSLP 0x02
+#define NFSD_REQINPROG 0x04
+#define NFSD_NEEDAUTH 0x08
+#define NFSD_AUTHFAIL 0x10
+#endif /* KERNEL */
diff --git a/sys/nfs/nfs_bio.c b/sys/nfs/nfs_bio.c
new file mode 100644
index 0000000..9ef81fe
--- /dev/null
+++ b/sys/nfs/nfs_bio.c
@@ -0,0 +1,803 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_bio.c 8.5 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/resourcevar.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/trace.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+
+#include <vm/vm.h>
+
+#include <nfs/nfsnode.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nqnfs.h>
+
+struct buf *incore(), *nfs_getcacheblk();
+extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
+extern int nfs_numasync;
+
+/*
+ * Vnode op for read using bio
+ * Any similarity to readip() is purely coincidental
+ */
+int
+nfs_bioread(vp, uio, ioflag, cred)
+ register struct vnode *vp;
+ register struct uio *uio;
+ int ioflag;
+ struct ucred *cred;
+{
+ register struct nfsnode *np = VTONFS(vp);
+ register int biosize, diff;
+ struct buf *bp = 0, *rabp;
+ struct vattr vattr;
+ struct proc *p;
+ struct nfsmount *nmp;
+ daddr_t lbn, bn, rabn;
+ caddr_t baddr;
+ int got_buf = 0, nra, error = 0, n = 0, on = 0, not_readin;
+
+#ifdef lint
+ ioflag = ioflag;
+#endif /* lint */
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_READ)
+ panic("nfs_read mode");
+#endif
+ if (uio->uio_resid == 0)
+ return (0);
+ if (uio->uio_offset < 0 && vp->v_type != VDIR)
+ return (EINVAL);
+ nmp = VFSTONFS(vp->v_mount);
+ biosize = nmp->nm_rsize;
+ p = uio->uio_procp;
+ /*
+ * For nfs, cache consistency can only be maintained approximately.
+ * Although RFC1094 does not specify the criteria, the following is
+ * believed to be compatible with the reference port.
+ * For nqnfs, full cache consistency is maintained within the loop.
+ * For nfs:
+ * If the file's modify time on the server has changed since the
+ * last read rpc or you have written to the file,
+ * you may have lost data cache consistency with the
+ * server, so flush all of the file's data out of the cache.
+ * Then force a getattr rpc to ensure that you have up to date
+ * attributes.
+ * The mount flag NFSMNT_MYWRITE says "Assume that my writes are
+ * the ones changing the modify time.
+ * NB: This implies that cache data can be read when up to
+ * NFS_ATTRTIMEO seconds out of date. If you find that you need current
+ * attributes this could be forced by setting n_attrstamp to 0 before
+ * the VOP_GETATTR() call.
+ */
+ if ((nmp->nm_flag & NFSMNT_NQNFS) == 0 && vp->v_type != VLNK) {
+ if (np->n_flag & NMODIFIED) {
+ if ((nmp->nm_flag & NFSMNT_MYWRITE) == 0 ||
+ vp->v_type != VREG) {
+ if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+ return (error);
+ }
+ np->n_attrstamp = 0;
+ np->n_direofoffset = 0;
+ if (error = VOP_GETATTR(vp, &vattr, cred, p))
+ return (error);
+ np->n_mtime = vattr.va_mtime.ts_sec;
+ } else {
+ if (error = VOP_GETATTR(vp, &vattr, cred, p))
+ return (error);
+ if (np->n_mtime != vattr.va_mtime.ts_sec) {
+ np->n_direofoffset = 0;
+ if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+ return (error);
+ np->n_mtime = vattr.va_mtime.ts_sec;
+ }
+ }
+ }
+ do {
+
+ /*
+ * Get a valid lease. If cached data is stale, flush it.
+ */
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ if (NQNFS_CKINVALID(vp, np, NQL_READ)) {
+ do {
+ error = nqnfs_getlease(vp, NQL_READ, cred, p);
+ } while (error == NQNFS_EXPIRED);
+ if (error)
+ return (error);
+ if (np->n_lrev != np->n_brev ||
+ (np->n_flag & NQNFSNONCACHE) ||
+ ((np->n_flag & NMODIFIED) && vp->v_type == VDIR)) {
+ if (vp->v_type == VDIR) {
+ np->n_direofoffset = 0;
+ cache_purge(vp);
+ }
+ if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+ return (error);
+ np->n_brev = np->n_lrev;
+ }
+ } else if (vp->v_type == VDIR && (np->n_flag & NMODIFIED)) {
+ np->n_direofoffset = 0;
+ cache_purge(vp);
+ if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+ return (error);
+ }
+ }
+ if (np->n_flag & NQNFSNONCACHE) {
+ switch (vp->v_type) {
+ case VREG:
+ error = nfs_readrpc(vp, uio, cred);
+ break;
+ case VLNK:
+ error = nfs_readlinkrpc(vp, uio, cred);
+ break;
+ case VDIR:
+ error = nfs_readdirrpc(vp, uio, cred);
+ break;
+ };
+ return (error);
+ }
+ baddr = (caddr_t)0;
+ switch (vp->v_type) {
+ case VREG:
+ nfsstats.biocache_reads++;
+ lbn = uio->uio_offset / biosize;
+ on = uio->uio_offset & (biosize-1);
+ bn = lbn * (biosize / DEV_BSIZE);
+ not_readin = 1;
+
+ /*
+ * Start the read ahead(s), as required.
+ */
+ if (nfs_numasync > 0 && nmp->nm_readahead > 0 &&
+ lbn == vp->v_lastr + 1) {
+ for (nra = 0; nra < nmp->nm_readahead &&
+ (lbn + 1 + nra) * biosize < np->n_size; nra++) {
+ rabn = (lbn + 1 + nra) * (biosize / DEV_BSIZE);
+ if (!incore(vp, rabn)) {
+ rabp = nfs_getcacheblk(vp, rabn, biosize, p);
+ if (!rabp)
+ return (EINTR);
+ if ((rabp->b_flags & (B_DELWRI | B_DONE)) == 0) {
+ rabp->b_flags |= (B_READ | B_ASYNC);
+ if (nfs_asyncio(rabp, cred)) {
+ rabp->b_flags |= B_INVAL;
+ brelse(rabp);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * If the block is in the cache and has the required data
+ * in a valid region, just copy it out.
+ * Otherwise, get the block and write back/read in,
+ * as required.
+ */
+ if ((bp = incore(vp, bn)) &&
+ (bp->b_flags & (B_BUSY | B_WRITEINPROG)) ==
+ (B_BUSY | B_WRITEINPROG))
+ got_buf = 0;
+ else {
+again:
+ bp = nfs_getcacheblk(vp, bn, biosize, p);
+ if (!bp)
+ return (EINTR);
+ got_buf = 1;
+ if ((bp->b_flags & (B_DONE | B_DELWRI)) == 0) {
+ bp->b_flags |= B_READ;
+ not_readin = 0;
+ if (error = nfs_doio(bp, cred, p)) {
+ brelse(bp);
+ return (error);
+ }
+ }
+ }
+ n = min((unsigned)(biosize - on), uio->uio_resid);
+ diff = np->n_size - uio->uio_offset;
+ if (diff < n)
+ n = diff;
+ if (not_readin && n > 0) {
+ if (on < bp->b_validoff || (on + n) > bp->b_validend) {
+ if (!got_buf) {
+ bp = nfs_getcacheblk(vp, bn, biosize, p);
+ if (!bp)
+ return (EINTR);
+ got_buf = 1;
+ }
+ bp->b_flags |= B_INVAL;
+ if (bp->b_dirtyend > 0) {
+ if ((bp->b_flags & B_DELWRI) == 0)
+ panic("nfsbioread");
+ if (VOP_BWRITE(bp) == EINTR)
+ return (EINTR);
+ } else
+ brelse(bp);
+ goto again;
+ }
+ }
+ vp->v_lastr = lbn;
+ diff = (on >= bp->b_validend) ? 0 : (bp->b_validend - on);
+ if (diff < n)
+ n = diff;
+ break;
+ case VLNK:
+ nfsstats.biocache_readlinks++;
+ bp = nfs_getcacheblk(vp, (daddr_t)0, NFS_MAXPATHLEN, p);
+ if (!bp)
+ return (EINTR);
+ if ((bp->b_flags & B_DONE) == 0) {
+ bp->b_flags |= B_READ;
+ if (error = nfs_doio(bp, cred, p)) {
+ brelse(bp);
+ return (error);
+ }
+ }
+ n = min(uio->uio_resid, NFS_MAXPATHLEN - bp->b_resid);
+ got_buf = 1;
+ on = 0;
+ break;
+ case VDIR:
+ nfsstats.biocache_readdirs++;
+ bn = (daddr_t)uio->uio_offset;
+ bp = nfs_getcacheblk(vp, bn, NFS_DIRBLKSIZ, p);
+ if (!bp)
+ return (EINTR);
+ if ((bp->b_flags & B_DONE) == 0) {
+ bp->b_flags |= B_READ;
+ if (error = nfs_doio(bp, cred, p)) {
+ brelse(bp);
+ return (error);
+ }
+ }
+
+ /*
+ * If not eof and read aheads are enabled, start one.
+ * (You need the current block first, so that you have the
+ * directory offset cookie of the next block.
+ */
+ rabn = bp->b_blkno;
+ if (nfs_numasync > 0 && nmp->nm_readahead > 0 &&
+ rabn != 0 && rabn != np->n_direofoffset &&
+ !incore(vp, rabn)) {
+ rabp = nfs_getcacheblk(vp, rabn, NFS_DIRBLKSIZ, p);
+ if (rabp) {
+ if ((rabp->b_flags & (B_DONE | B_DELWRI)) == 0) {
+ rabp->b_flags |= (B_READ | B_ASYNC);
+ if (nfs_asyncio(rabp, cred)) {
+ rabp->b_flags |= B_INVAL;
+ brelse(rabp);
+ }
+ }
+ }
+ }
+ on = 0;
+ n = min(uio->uio_resid, NFS_DIRBLKSIZ - bp->b_resid);
+ got_buf = 1;
+ break;
+ };
+
+ if (n > 0) {
+ if (!baddr)
+ baddr = bp->b_data;
+ error = uiomove(baddr + on, (int)n, uio);
+ }
+ switch (vp->v_type) {
+ case VREG:
+ if (n + on == biosize || uio->uio_offset == np->n_size)
+ bp->b_flags |= B_AGE;
+ break;
+ case VLNK:
+ n = 0;
+ break;
+ case VDIR:
+ uio->uio_offset = bp->b_blkno;
+ break;
+ };
+ if (got_buf)
+ brelse(bp);
+ } while (error == 0 && uio->uio_resid > 0 && n > 0);
+ return (error);
+}
+
+/*
+ * Vnode op for write using bio
+ */
+int
+nfs_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register int biosize;
+ register struct uio *uio = ap->a_uio;
+ struct proc *p = uio->uio_procp;
+ register struct vnode *vp = ap->a_vp;
+ struct nfsnode *np = VTONFS(vp);
+ register struct ucred *cred = ap->a_cred;
+ int ioflag = ap->a_ioflag;
+ struct buf *bp;
+ struct vattr vattr;
+ struct nfsmount *nmp;
+ daddr_t lbn, bn;
+ int n, on, error = 0;
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_WRITE)
+ panic("nfs_write mode");
+ if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
+ panic("nfs_write proc");
+#endif
+ if (vp->v_type != VREG)
+ return (EIO);
+ if (np->n_flag & NWRITEERR) {
+ np->n_flag &= ~NWRITEERR;
+ return (np->n_error);
+ }
+ if (ioflag & (IO_APPEND | IO_SYNC)) {
+ if (np->n_flag & NMODIFIED) {
+ np->n_attrstamp = 0;
+ if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+ return (error);
+ }
+ if (ioflag & IO_APPEND) {
+ np->n_attrstamp = 0;
+ if (error = VOP_GETATTR(vp, &vattr, cred, p))
+ return (error);
+ uio->uio_offset = np->n_size;
+ }
+ }
+ nmp = VFSTONFS(vp->v_mount);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+ if (uio->uio_resid == 0)
+ return (0);
+ /*
+ * Maybe this should be above the vnode op call, but so long as
+ * file servers have no limits, i don't think it matters
+ */
+ if (p && uio->uio_offset + uio->uio_resid >
+ p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
+ psignal(p, SIGXFSZ);
+ return (EFBIG);
+ }
+ /*
+ * I use nm_rsize, not nm_wsize so that all buffer cache blocks
+ * will be the same size within a filesystem. nfs_writerpc will
+ * still use nm_wsize when sizing the rpc's.
+ */
+ biosize = nmp->nm_rsize;
+ do {
+
+ /*
+ * Check for a valid write lease.
+ * If non-cachable, just do the rpc
+ */
+ if ((nmp->nm_flag & NFSMNT_NQNFS) &&
+ NQNFS_CKINVALID(vp, np, NQL_WRITE)) {
+ do {
+ error = nqnfs_getlease(vp, NQL_WRITE, cred, p);
+ } while (error == NQNFS_EXPIRED);
+ if (error)
+ return (error);
+ if (np->n_lrev != np->n_brev ||
+ (np->n_flag & NQNFSNONCACHE)) {
+ if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+ return (error);
+ np->n_brev = np->n_lrev;
+ }
+ }
+ if (np->n_flag & NQNFSNONCACHE)
+ return (nfs_writerpc(vp, uio, cred, ioflag));
+ nfsstats.biocache_writes++;
+ lbn = uio->uio_offset / biosize;
+ on = uio->uio_offset & (biosize-1);
+ n = min((unsigned)(biosize - on), uio->uio_resid);
+ bn = lbn * (biosize / DEV_BSIZE);
+again:
+ bp = nfs_getcacheblk(vp, bn, biosize, p);
+ if (!bp)
+ return (EINTR);
+ if (bp->b_wcred == NOCRED) {
+ crhold(cred);
+ bp->b_wcred = cred;
+ }
+ np->n_flag |= NMODIFIED;
+ if (uio->uio_offset + n > np->n_size) {
+ np->n_size = uio->uio_offset + n;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ }
+
+ /*
+ * If the new write will leave a contiguous dirty
+ * area, just update the b_dirtyoff and b_dirtyend,
+ * otherwise force a write rpc of the old dirty area.
+ */
+ if (bp->b_dirtyend > 0 &&
+ (on > bp->b_dirtyend || (on + n) < bp->b_dirtyoff)) {
+ bp->b_proc = p;
+ if (VOP_BWRITE(bp) == EINTR)
+ return (EINTR);
+ goto again;
+ }
+
+ /*
+ * Check for valid write lease and get one as required.
+ * In case getblk() and/or bwrite() delayed us.
+ */
+ if ((nmp->nm_flag & NFSMNT_NQNFS) &&
+ NQNFS_CKINVALID(vp, np, NQL_WRITE)) {
+ do {
+ error = nqnfs_getlease(vp, NQL_WRITE, cred, p);
+ } while (error == NQNFS_EXPIRED);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ if (np->n_lrev != np->n_brev ||
+ (np->n_flag & NQNFSNONCACHE)) {
+ brelse(bp);
+ if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+ return (error);
+ np->n_brev = np->n_lrev;
+ goto again;
+ }
+ }
+ if (error = uiomove((char *)bp->b_data + on, n, uio)) {
+ bp->b_flags |= B_ERROR;
+ brelse(bp);
+ return (error);
+ }
+ if (bp->b_dirtyend > 0) {
+ bp->b_dirtyoff = min(on, bp->b_dirtyoff);
+ bp->b_dirtyend = max((on + n), bp->b_dirtyend);
+ } else {
+ bp->b_dirtyoff = on;
+ bp->b_dirtyend = on + n;
+ }
+#ifndef notdef
+ if (bp->b_validend == 0 || bp->b_validend < bp->b_dirtyoff ||
+ bp->b_validoff > bp->b_dirtyend) {
+ bp->b_validoff = bp->b_dirtyoff;
+ bp->b_validend = bp->b_dirtyend;
+ } else {
+ bp->b_validoff = min(bp->b_validoff, bp->b_dirtyoff);
+ bp->b_validend = max(bp->b_validend, bp->b_dirtyend);
+ }
+#else
+ bp->b_validoff = bp->b_dirtyoff;
+ bp->b_validend = bp->b_dirtyend;
+#endif
+ if (ioflag & IO_APPEND)
+ bp->b_flags |= B_APPENDWRITE;
+
+ /*
+ * If the lease is non-cachable or IO_SYNC do bwrite().
+ */
+ if ((np->n_flag & NQNFSNONCACHE) || (ioflag & IO_SYNC)) {
+ bp->b_proc = p;
+ if (error = VOP_BWRITE(bp))
+ return (error);
+ } else if ((n + on) == biosize &&
+ (nmp->nm_flag & NFSMNT_NQNFS) == 0) {
+ bp->b_proc = (struct proc *)0;
+ bawrite(bp);
+ } else
+ bdwrite(bp);
+ } while (uio->uio_resid > 0 && n > 0);
+ return (0);
+}
+
+/*
+ * Get an nfs cache block.
+ * Allocate a new one if the block isn't currently in the cache
+ * and return the block marked busy. If the calling process is
+ * interrupted by a signal for an interruptible mount point, return
+ * NULL.
+ */
+struct buf *
+nfs_getcacheblk(vp, bn, size, p)
+ struct vnode *vp;
+ daddr_t bn;
+ int size;
+ struct proc *p;
+{
+ register struct buf *bp;
+ struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+
+ if (nmp->nm_flag & NFSMNT_INT) {
+ bp = getblk(vp, bn, size, PCATCH, 0);
+ while (bp == (struct buf *)0) {
+ if (nfs_sigintr(nmp, (struct nfsreq *)0, p))
+ return ((struct buf *)0);
+ bp = getblk(vp, bn, size, 0, 2 * hz);
+ }
+ } else
+ bp = getblk(vp, bn, size, 0, 0);
+ return (bp);
+}
+
+/*
+ * Flush and invalidate all dirty buffers. If another process is already
+ * doing the flush, just wait for completion.
+ */
+int
+nfs_vinvalbuf(vp, flags, cred, p, intrflg)
+ struct vnode *vp;
+ int flags;
+ struct ucred *cred;
+ struct proc *p;
+ int intrflg;
+{
+ register struct nfsnode *np = VTONFS(vp);
+ struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+ int error = 0, slpflag, slptimeo;
+
+ if ((nmp->nm_flag & NFSMNT_INT) == 0)
+ intrflg = 0;
+ if (intrflg) {
+ slpflag = PCATCH;
+ slptimeo = 2 * hz;
+ } else {
+ slpflag = 0;
+ slptimeo = 0;
+ }
+ /*
+ * First wait for any other process doing a flush to complete.
+ */
+ while (np->n_flag & NFLUSHINPROG) {
+ np->n_flag |= NFLUSHWANT;
+ error = tsleep((caddr_t)&np->n_flag, PRIBIO + 2, "nfsvinval",
+ slptimeo);
+ if (error && intrflg && nfs_sigintr(nmp, (struct nfsreq *)0, p))
+ return (EINTR);
+ }
+
+ /*
+ * Now, flush as required.
+ */
+ np->n_flag |= NFLUSHINPROG;
+ error = vinvalbuf(vp, flags, cred, p, slpflag, 0);
+ while (error) {
+ if (intrflg && nfs_sigintr(nmp, (struct nfsreq *)0, p)) {
+ np->n_flag &= ~NFLUSHINPROG;
+ if (np->n_flag & NFLUSHWANT) {
+ np->n_flag &= ~NFLUSHWANT;
+ wakeup((caddr_t)&np->n_flag);
+ }
+ return (EINTR);
+ }
+ error = vinvalbuf(vp, flags, cred, p, 0, slptimeo);
+ }
+ np->n_flag &= ~(NMODIFIED | NFLUSHINPROG);
+ if (np->n_flag & NFLUSHWANT) {
+ np->n_flag &= ~NFLUSHWANT;
+ wakeup((caddr_t)&np->n_flag);
+ }
+ return (0);
+}
+
+/*
+ * Initiate asynchronous I/O. Return an error if no nfsiods are available.
+ * This is mainly to avoid queueing async I/O requests when the nfsiods
+ * are all hung on a dead server.
+ */
+int
+nfs_asyncio(bp, cred)
+ register struct buf *bp;
+ struct ucred *cred;
+{
+ register int i;
+
+ if (nfs_numasync == 0)
+ return (EIO);
+ for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
+ if (nfs_iodwant[i]) {
+ if (bp->b_flags & B_READ) {
+ if (bp->b_rcred == NOCRED && cred != NOCRED) {
+ crhold(cred);
+ bp->b_rcred = cred;
+ }
+ } else {
+ if (bp->b_wcred == NOCRED && cred != NOCRED) {
+ crhold(cred);
+ bp->b_wcred = cred;
+ }
+ }
+
+ TAILQ_INSERT_TAIL(&nfs_bufq, bp, b_freelist);
+ nfs_iodwant[i] = (struct proc *)0;
+ wakeup((caddr_t)&nfs_iodwant[i]);
+ return (0);
+ }
+ return (EIO);
+}
+
+/*
+ * Do an I/O operation to/from a cache block. This may be called
+ * synchronously or from an nfsiod.
+ */
+int
+nfs_doio(bp, cr, p)
+ register struct buf *bp;
+ struct cred *cr;
+ struct proc *p;
+{
+ register struct uio *uiop;
+ register struct vnode *vp;
+ struct nfsnode *np;
+ struct nfsmount *nmp;
+ int error = 0, diff, len;
+ struct uio uio;
+ struct iovec io;
+
+ vp = bp->b_vp;
+ np = VTONFS(vp);
+ nmp = VFSTONFS(vp->v_mount);
+ uiop = &uio;
+ uiop->uio_iov = &io;
+ uiop->uio_iovcnt = 1;
+ uiop->uio_segflg = UIO_SYSSPACE;
+ uiop->uio_procp = p;
+
+ /*
+ * Historically, paging was done with physio, but no more.
+ */
+ if (bp->b_flags & B_PHYS)
+ panic("doio phys");
+ if (bp->b_flags & B_READ) {
+ io.iov_len = uiop->uio_resid = bp->b_bcount;
+ io.iov_base = bp->b_data;
+ uiop->uio_rw = UIO_READ;
+ switch (vp->v_type) {
+ case VREG:
+ uiop->uio_offset = bp->b_blkno * DEV_BSIZE;
+ nfsstats.read_bios++;
+ error = nfs_readrpc(vp, uiop, cr);
+ if (!error) {
+ bp->b_validoff = 0;
+ if (uiop->uio_resid) {
+ /*
+ * If len > 0, there is a hole in the file and
+ * no writes after the hole have been pushed to
+ * the server yet.
+ * Just zero fill the rest of the valid area.
+ */
+ diff = bp->b_bcount - uiop->uio_resid;
+ len = np->n_size - (bp->b_blkno * DEV_BSIZE
+ + diff);
+ if (len > 0) {
+ len = min(len, uiop->uio_resid);
+ bzero((char *)bp->b_data + diff, len);
+ bp->b_validend = diff + len;
+ } else
+ bp->b_validend = diff;
+ } else
+ bp->b_validend = bp->b_bcount;
+ }
+ if (p && (vp->v_flag & VTEXT) &&
+ (((nmp->nm_flag & NFSMNT_NQNFS) &&
+ np->n_lrev != np->n_brev) ||
+ (!(nmp->nm_flag & NFSMNT_NQNFS) &&
+ np->n_mtime != np->n_vattr.va_mtime.ts_sec))) {
+ uprintf("Process killed due to text file modification\n");
+ psignal(p, SIGKILL);
+ p->p_flag |= P_NOSWAP;
+ }
+ break;
+ case VLNK:
+ uiop->uio_offset = 0;
+ nfsstats.readlink_bios++;
+ error = nfs_readlinkrpc(vp, uiop, cr);
+ break;
+ case VDIR:
+ uiop->uio_offset = bp->b_lblkno;
+ nfsstats.readdir_bios++;
+ if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS)
+ error = nfs_readdirlookrpc(vp, uiop, cr);
+ else
+ error = nfs_readdirrpc(vp, uiop, cr);
+ /*
+ * Save offset cookie in b_blkno.
+ */
+ bp->b_blkno = uiop->uio_offset;
+ break;
+ };
+ if (error) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = error;
+ }
+ } else {
+ io.iov_len = uiop->uio_resid = bp->b_dirtyend
+ - bp->b_dirtyoff;
+ uiop->uio_offset = (bp->b_blkno * DEV_BSIZE)
+ + bp->b_dirtyoff;
+ io.iov_base = (char *)bp->b_data + bp->b_dirtyoff;
+ uiop->uio_rw = UIO_WRITE;
+ nfsstats.write_bios++;
+ if (bp->b_flags & B_APPENDWRITE)
+ error = nfs_writerpc(vp, uiop, cr, IO_APPEND);
+ else
+ error = nfs_writerpc(vp, uiop, cr, 0);
+ bp->b_flags &= ~(B_WRITEINPROG | B_APPENDWRITE);
+
+ /*
+ * For an interrupted write, the buffer is still valid and the
+ * write hasn't been pushed to the server yet, so we can't set
+ * B_ERROR and report the interruption by setting B_EINTR. For
+ * the B_ASYNC case, B_EINTR is not relevant, so the rpc attempt
+ * is essentially a noop.
+ */
+ if (error == EINTR) {
+ bp->b_flags &= ~B_INVAL;
+ bp->b_flags |= B_DELWRI;
+
+ /*
+ * Since for the B_ASYNC case, nfs_bwrite() has reassigned the
+ * buffer to the clean list, we have to reassign it back to the
+ * dirty one. Ugh.
+ */
+ if (bp->b_flags & B_ASYNC)
+ reassignbuf(bp, vp);
+ else
+ bp->b_flags |= B_EINTR;
+ } else {
+ if (error) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = np->n_error = error;
+ np->n_flag |= NWRITEERR;
+ }
+ bp->b_dirtyoff = bp->b_dirtyend = 0;
+ }
+ }
+ bp->b_resid = uiop->uio_resid;
+ biodone(bp);
+ return (error);
+}
diff --git a/sys/nfs/nfs_common.c b/sys/nfs/nfs_common.c
new file mode 100644
index 0000000..d17cde7
--- /dev/null
+++ b/sys/nfs/nfs_common.c
@@ -0,0 +1,1143 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_subs.c 8.3 (Berkeley) 1/4/94
+ */
+
+/*
+ * These functions support the macros and help fiddle mbuf chains for
+ * the nfs op functions. They do things like create the rpc header and
+ * copy data between mbuf chains and uio lists.
+ */
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfs.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nqnfs.h>
+#include <nfs/nfsrtt.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <netinet/in.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Data items converted to xdr at startup, since they are constant
+ * This is kinda hokey, but may save a little time doing byte swaps
+ */
+u_long nfs_procids[NFS_NPROCS];
+u_long nfs_xdrneg1;
+u_long rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr,
+ rpc_mismatch, rpc_auth_unix, rpc_msgaccepted, rpc_rejectedcred,
+ rpc_auth_kerb;
+u_long nfs_vers, nfs_prog, nfs_true, nfs_false;
+
+/* And other global data */
+static u_long nfs_xid = 0;
+enum vtype ntov_type[7] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON };
+extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
+extern struct nfsreq nfsreqh;
+extern int nqnfs_piggy[NFS_NPROCS];
+extern struct nfsrtt nfsrtt;
+extern time_t nqnfsstarttime;
+extern u_long nqnfs_prog, nqnfs_vers;
+extern int nqsrv_clockskew;
+extern int nqsrv_writeslack;
+extern int nqsrv_maxlease;
+
+/*
+ * Create the header for an rpc request packet
+ * The hsiz is the size of the rest of the nfs request header.
+ * (just used to decide if a cluster is a good idea)
+ */
+struct mbuf *
+nfsm_reqh(vp, procid, hsiz, bposp)
+ struct vnode *vp;
+ u_long procid;
+ int hsiz;
+ caddr_t *bposp;
+{
+ register struct mbuf *mb;
+ register u_long *tl;
+ register caddr_t bpos;
+ struct mbuf *mb2;
+ struct nfsmount *nmp;
+ int nqflag;
+
+ MGET(mb, M_WAIT, MT_DATA);
+ if (hsiz >= MINCLSIZE)
+ MCLGET(mb, M_WAIT);
+ mb->m_len = 0;
+ bpos = mtod(mb, caddr_t);
+
+ /*
+ * For NQNFS, add lease request.
+ */
+ if (vp) {
+ nmp = VFSTONFS(vp->v_mount);
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ nqflag = NQNFS_NEEDLEASE(vp, procid);
+ if (nqflag) {
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(nqflag);
+ *tl = txdr_unsigned(nmp->nm_leaseterm);
+ } else {
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = 0;
+ }
+ }
+ }
+ /* Finally, return values */
+ *bposp = bpos;
+ return (mb);
+}
+
+/*
+ * Build the RPC header and fill in the authorization info.
+ * The authorization string argument is only used when the credentials
+ * come from outside of the kernel.
+ * Returns the head of the mbuf list.
+ */
+struct mbuf *
+nfsm_rpchead(cr, nqnfs, procid, auth_type, auth_len, auth_str, mrest,
+ mrest_len, mbp, xidp)
+ register struct ucred *cr;
+ int nqnfs;
+ int procid;
+ int auth_type;
+ int auth_len;
+ char *auth_str;
+ struct mbuf *mrest;
+ int mrest_len;
+ struct mbuf **mbp;
+ u_long *xidp;
+{
+ register struct mbuf *mb;
+ register u_long *tl;
+ register caddr_t bpos;
+ register int i;
+ struct mbuf *mreq, *mb2;
+ int siz, grpsiz, authsiz;
+
+ authsiz = nfsm_rndup(auth_len);
+ if (auth_type == RPCAUTH_NQNFS)
+ authsiz += 2 * NFSX_UNSIGNED;
+ MGETHDR(mb, M_WAIT, MT_DATA);
+ if ((authsiz + 10*NFSX_UNSIGNED) >= MINCLSIZE) {
+ MCLGET(mb, M_WAIT);
+ } else if ((authsiz + 10*NFSX_UNSIGNED) < MHLEN) {
+ MH_ALIGN(mb, authsiz + 10*NFSX_UNSIGNED);
+ } else {
+ MH_ALIGN(mb, 8*NFSX_UNSIGNED);
+ }
+ mb->m_len = 0;
+ mreq = mb;
+ bpos = mtod(mb, caddr_t);
+
+ /*
+ * First the RPC header.
+ */
+ nfsm_build(tl, u_long *, 8*NFSX_UNSIGNED);
+ if (++nfs_xid == 0)
+ nfs_xid++;
+ *tl++ = *xidp = txdr_unsigned(nfs_xid);
+ *tl++ = rpc_call;
+ *tl++ = rpc_vers;
+ if (nqnfs) {
+ *tl++ = txdr_unsigned(NQNFS_PROG);
+ *tl++ = txdr_unsigned(NQNFS_VER1);
+ } else {
+ *tl++ = txdr_unsigned(NFS_PROG);
+ *tl++ = txdr_unsigned(NFS_VER2);
+ }
+ *tl++ = txdr_unsigned(procid);
+
+ /*
+ * And then the authorization cred.
+ */
+ *tl++ = txdr_unsigned(auth_type);
+ *tl = txdr_unsigned(authsiz);
+ switch (auth_type) {
+ case RPCAUTH_UNIX:
+ nfsm_build(tl, u_long *, auth_len);
+ *tl++ = 0; /* stamp ?? */
+ *tl++ = 0; /* NULL hostname */
+ *tl++ = txdr_unsigned(cr->cr_uid);
+ *tl++ = txdr_unsigned(cr->cr_groups[0]);
+ grpsiz = (auth_len >> 2) - 5;
+ *tl++ = txdr_unsigned(grpsiz);
+ for (i = 1; i <= grpsiz; i++)
+ *tl++ = txdr_unsigned(cr->cr_groups[i]);
+ break;
+ case RPCAUTH_NQNFS:
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(cr->cr_uid);
+ *tl = txdr_unsigned(auth_len);
+ siz = auth_len;
+ while (siz > 0) {
+ if (M_TRAILINGSPACE(mb) == 0) {
+ MGET(mb2, M_WAIT, MT_DATA);
+ if (siz >= MINCLSIZE)
+ MCLGET(mb2, M_WAIT);
+ mb->m_next = mb2;
+ mb = mb2;
+ mb->m_len = 0;
+ bpos = mtod(mb, caddr_t);
+ }
+ i = min(siz, M_TRAILINGSPACE(mb));
+ bcopy(auth_str, bpos, i);
+ mb->m_len += i;
+ auth_str += i;
+ bpos += i;
+ siz -= i;
+ }
+ if ((siz = (nfsm_rndup(auth_len) - auth_len)) > 0) {
+ for (i = 0; i < siz; i++)
+ *bpos++ = '\0';
+ mb->m_len += siz;
+ }
+ break;
+ };
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(RPCAUTH_NULL);
+ *tl = 0;
+ mb->m_next = mrest;
+ mreq->m_pkthdr.len = authsiz + 10*NFSX_UNSIGNED + mrest_len;
+ mreq->m_pkthdr.rcvif = (struct ifnet *)0;
+ *mbp = mb;
+ return (mreq);
+}
+
+/*
+ * copies mbuf chain to the uio scatter/gather list
+ */
+int
+nfsm_mbuftouio(mrep, uiop, siz, dpos)
+ struct mbuf **mrep;
+ register struct uio *uiop;
+ int siz;
+ caddr_t *dpos;
+{
+ register char *mbufcp, *uiocp;
+ register int xfer, left, len;
+ register struct mbuf *mp;
+ long uiosiz, rem;
+ int error = 0;
+
+ mp = *mrep;
+ mbufcp = *dpos;
+ len = mtod(mp, caddr_t)+mp->m_len-mbufcp;
+ rem = nfsm_rndup(siz)-siz;
+ while (siz > 0) {
+ if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
+ return (EFBIG);
+ left = uiop->uio_iov->iov_len;
+ uiocp = uiop->uio_iov->iov_base;
+ if (left > siz)
+ left = siz;
+ uiosiz = left;
+ while (left > 0) {
+ while (len == 0) {
+ mp = mp->m_next;
+ if (mp == NULL)
+ return (EBADRPC);
+ mbufcp = mtod(mp, caddr_t);
+ len = mp->m_len;
+ }
+ xfer = (left > len) ? len : left;
+#ifdef notdef
+ /* Not Yet.. */
+ if (uiop->uio_iov->iov_op != NULL)
+ (*(uiop->uio_iov->iov_op))
+ (mbufcp, uiocp, xfer);
+ else
+#endif
+ if (uiop->uio_segflg == UIO_SYSSPACE)
+ bcopy(mbufcp, uiocp, xfer);
+ else
+ copyout(mbufcp, uiocp, xfer);
+ left -= xfer;
+ len -= xfer;
+ mbufcp += xfer;
+ uiocp += xfer;
+ uiop->uio_offset += xfer;
+ uiop->uio_resid -= xfer;
+ }
+ if (uiop->uio_iov->iov_len <= siz) {
+ uiop->uio_iovcnt--;
+ uiop->uio_iov++;
+ } else {
+ uiop->uio_iov->iov_base += uiosiz;
+ uiop->uio_iov->iov_len -= uiosiz;
+ }
+ siz -= uiosiz;
+ }
+ *dpos = mbufcp;
+ *mrep = mp;
+ if (rem > 0) {
+ if (len < rem)
+ error = nfs_adv(mrep, dpos, rem, len);
+ else
+ *dpos += rem;
+ }
+ return (error);
+}
+
+/*
+ * copies a uio scatter/gather list to an mbuf chain...
+ */
+int
+nfsm_uiotombuf(uiop, mq, siz, bpos)
+ register struct uio *uiop;
+ struct mbuf **mq;
+ int siz;
+ caddr_t *bpos;
+{
+ register char *uiocp;
+ register struct mbuf *mp, *mp2;
+ register int xfer, left, mlen;
+ int uiosiz, clflg, rem;
+ char *cp;
+
+ if (siz > MLEN) /* or should it >= MCLBYTES ?? */
+ clflg = 1;
+ else
+ clflg = 0;
+ rem = nfsm_rndup(siz)-siz;
+ mp = mp2 = *mq;
+ while (siz > 0) {
+ if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
+ return (EINVAL);
+ left = uiop->uio_iov->iov_len;
+ uiocp = uiop->uio_iov->iov_base;
+ if (left > siz)
+ left = siz;
+ uiosiz = left;
+ while (left > 0) {
+ mlen = M_TRAILINGSPACE(mp);
+ if (mlen == 0) {
+ MGET(mp, M_WAIT, MT_DATA);
+ if (clflg)
+ MCLGET(mp, M_WAIT);
+ mp->m_len = 0;
+ mp2->m_next = mp;
+ mp2 = mp;
+ mlen = M_TRAILINGSPACE(mp);
+ }
+ xfer = (left > mlen) ? mlen : left;
+#ifdef notdef
+ /* Not Yet.. */
+ if (uiop->uio_iov->iov_op != NULL)
+ (*(uiop->uio_iov->iov_op))
+ (uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
+ else
+#endif
+ if (uiop->uio_segflg == UIO_SYSSPACE)
+ bcopy(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
+ else
+ copyin(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
+ mp->m_len += xfer;
+ left -= xfer;
+ uiocp += xfer;
+ uiop->uio_offset += xfer;
+ uiop->uio_resid -= xfer;
+ }
+ if (uiop->uio_iov->iov_len <= siz) {
+ uiop->uio_iovcnt--;
+ uiop->uio_iov++;
+ } else {
+ uiop->uio_iov->iov_base += uiosiz;
+ uiop->uio_iov->iov_len -= uiosiz;
+ }
+ siz -= uiosiz;
+ }
+ if (rem > 0) {
+ if (rem > M_TRAILINGSPACE(mp)) {
+ MGET(mp, M_WAIT, MT_DATA);
+ mp->m_len = 0;
+ mp2->m_next = mp;
+ }
+ cp = mtod(mp, caddr_t)+mp->m_len;
+ for (left = 0; left < rem; left++)
+ *cp++ = '\0';
+ mp->m_len += rem;
+ *bpos = cp;
+ } else
+ *bpos = mtod(mp, caddr_t)+mp->m_len;
+ *mq = mp;
+ return (0);
+}
+
+/*
+ * Help break down an mbuf chain by setting the first siz bytes contiguous
+ * pointed to by returned val.
+ * This is used by the macros nfsm_dissect and nfsm_dissecton for tough
+ * cases. (The macros use the vars. dpos and dpos2)
+ */
+int
+nfsm_disct(mdp, dposp, siz, left, cp2)
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ int siz;
+ int left;
+ caddr_t *cp2;
+{
+ register struct mbuf *mp, *mp2;
+ register int siz2, xfer;
+ register caddr_t p;
+
+ mp = *mdp;
+ while (left == 0) {
+ *mdp = mp = mp->m_next;
+ if (mp == NULL)
+ return (EBADRPC);
+ left = mp->m_len;
+ *dposp = mtod(mp, caddr_t);
+ }
+ if (left >= siz) {
+ *cp2 = *dposp;
+ *dposp += siz;
+ } else if (mp->m_next == NULL) {
+ return (EBADRPC);
+ } else if (siz > MHLEN) {
+ panic("nfs S too big");
+ } else {
+ MGET(mp2, M_WAIT, MT_DATA);
+ mp2->m_next = mp->m_next;
+ mp->m_next = mp2;
+ mp->m_len -= left;
+ mp = mp2;
+ *cp2 = p = mtod(mp, caddr_t);
+ bcopy(*dposp, p, left); /* Copy what was left */
+ siz2 = siz-left;
+ p += left;
+ mp2 = mp->m_next;
+ /* Loop around copying up the siz2 bytes */
+ while (siz2 > 0) {
+ if (mp2 == NULL)
+ return (EBADRPC);
+ xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
+ if (xfer > 0) {
+ bcopy(mtod(mp2, caddr_t), p, xfer);
+ NFSMADV(mp2, xfer);
+ mp2->m_len -= xfer;
+ p += xfer;
+ siz2 -= xfer;
+ }
+ if (siz2 > 0)
+ mp2 = mp2->m_next;
+ }
+ mp->m_len = siz;
+ *mdp = mp2;
+ *dposp = mtod(mp2, caddr_t);
+ }
+ return (0);
+}
+
+/*
+ * Advance the position in the mbuf chain.
+ */
+int
+nfs_adv(mdp, dposp, offs, left)
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ int offs;
+ int left;
+{
+ register struct mbuf *m;
+ register int s;
+
+ m = *mdp;
+ s = left;
+ while (s < offs) {
+ offs -= s;
+ m = m->m_next;
+ if (m == NULL)
+ return (EBADRPC);
+ s = m->m_len;
+ }
+ *mdp = m;
+ *dposp = mtod(m, caddr_t)+offs;
+ return (0);
+}
+
+/*
+ * Copy a string into mbufs for the hard cases...
+ */
+int
+nfsm_strtmbuf(mb, bpos, cp, siz)
+ struct mbuf **mb;
+ char **bpos;
+ char *cp;
+ long siz;
+{
+ register struct mbuf *m1 = 0, *m2;
+ long left, xfer, len, tlen;
+ u_long *tl;
+ int putsize;
+
+ putsize = 1;
+ m2 = *mb;
+ left = M_TRAILINGSPACE(m2);
+ if (left > 0) {
+ tl = ((u_long *)(*bpos));
+ *tl++ = txdr_unsigned(siz);
+ putsize = 0;
+ left -= NFSX_UNSIGNED;
+ m2->m_len += NFSX_UNSIGNED;
+ if (left > 0) {
+ bcopy(cp, (caddr_t) tl, left);
+ siz -= left;
+ cp += left;
+ m2->m_len += left;
+ left = 0;
+ }
+ }
+ /* Loop around adding mbufs */
+ while (siz > 0) {
+ MGET(m1, M_WAIT, MT_DATA);
+ if (siz > MLEN)
+ MCLGET(m1, M_WAIT);
+ m1->m_len = NFSMSIZ(m1);
+ m2->m_next = m1;
+ m2 = m1;
+ tl = mtod(m1, u_long *);
+ tlen = 0;
+ if (putsize) {
+ *tl++ = txdr_unsigned(siz);
+ m1->m_len -= NFSX_UNSIGNED;
+ tlen = NFSX_UNSIGNED;
+ putsize = 0;
+ }
+ if (siz < m1->m_len) {
+ len = nfsm_rndup(siz);
+ xfer = siz;
+ if (xfer < len)
+ *(tl+(xfer>>2)) = 0;
+ } else {
+ xfer = len = m1->m_len;
+ }
+ bcopy(cp, (caddr_t) tl, xfer);
+ m1->m_len = len+tlen;
+ siz -= xfer;
+ cp += xfer;
+ }
+ *mb = m1;
+ *bpos = mtod(m1, caddr_t)+m1->m_len;
+ return (0);
+}
+
+/*
+ * Called once to initialize data structures...
+ */
+int
+nfs_init()
+{
+ register int i;
+
+ nfsrtt.pos = 0;
+ rpc_vers = txdr_unsigned(RPC_VER2);
+ rpc_call = txdr_unsigned(RPC_CALL);
+ rpc_reply = txdr_unsigned(RPC_REPLY);
+ rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
+ rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
+ rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
+ rpc_autherr = txdr_unsigned(RPC_AUTHERR);
+ rpc_rejectedcred = txdr_unsigned(AUTH_REJECTCRED);
+ rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
+ rpc_auth_kerb = txdr_unsigned(RPCAUTH_NQNFS);
+ nfs_vers = txdr_unsigned(NFS_VER2);
+ nfs_prog = txdr_unsigned(NFS_PROG);
+ nfs_true = txdr_unsigned(TRUE);
+ nfs_false = txdr_unsigned(FALSE);
+ /* Loop thru nfs procids */
+ for (i = 0; i < NFS_NPROCS; i++)
+ nfs_procids[i] = txdr_unsigned(i);
+ /* Ensure async daemons disabled */
+ for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
+ nfs_iodwant[i] = (struct proc *)0;
+ TAILQ_INIT(&nfs_bufq);
+ nfs_xdrneg1 = txdr_unsigned(-1);
+ nfs_nhinit(); /* Init the nfsnode table */
+ nfsrv_init(0); /* Init server data structures */
+ nfsrv_initcache(); /* Init the server request cache */
+
+ /*
+ * Initialize the nqnfs server stuff.
+ */
+ if (nqnfsstarttime == 0) {
+ nqnfsstarttime = boottime.tv_sec + nqsrv_maxlease
+ + nqsrv_clockskew + nqsrv_writeslack;
+ NQLOADNOVRAM(nqnfsstarttime);
+ nqnfs_prog = txdr_unsigned(NQNFS_PROG);
+ nqnfs_vers = txdr_unsigned(NQNFS_VER1);
+ nqthead.th_head[0] = &nqthead;
+ nqthead.th_head[1] = &nqthead;
+ nqfhead = hashinit(NQLCHSZ, M_NQLEASE, &nqfheadhash);
+ }
+
+ /*
+ * Initialize reply list and start timer
+ */
+ nfsreqh.r_prev = nfsreqh.r_next = &nfsreqh;
+ nfs_timer();
+
+ return (0);
+}
+
+/*
+ * Attribute cache routines.
+ * nfs_loadattrcache() - loads or updates the cache contents from attributes
+ * that are on the mbuf list
+ * nfs_getattrcache() - returns valid attributes if found in cache, returns
+ * error otherwise
+ */
+
+/*
+ * Load the attribute cache (that lives in the nfsnode entry) with
+ * the values on the mbuf list and
+ * Iff vap not NULL
+ * copy the attributes to *vaper
+ */
+int
+nfs_loadattrcache(vpp, mdp, dposp, vaper)
+ struct vnode **vpp;
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ struct vattr *vaper;
+{
+ register struct vnode *vp = *vpp;
+ register struct vattr *vap;
+ register struct nfsv2_fattr *fp;
+ extern int (**spec_nfsv2nodeop_p)();
+ register struct nfsnode *np, *nq, **nhpp;
+ register long t1;
+ caddr_t dpos, cp2;
+ int error = 0, isnq;
+ struct mbuf *md;
+ enum vtype vtyp;
+ u_short vmode;
+ long rdev;
+ struct timespec mtime;
+ struct vnode *nvp;
+
+ md = *mdp;
+ dpos = *dposp;
+ t1 = (mtod(md, caddr_t) + md->m_len) - dpos;
+ isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ if (error = nfsm_disct(&md, &dpos, NFSX_FATTR(isnq), t1, &cp2))
+ return (error);
+ fp = (struct nfsv2_fattr *)cp2;
+ vtyp = nfstov_type(fp->fa_type);
+ vmode = fxdr_unsigned(u_short, fp->fa_mode);
+ if (vtyp == VNON || vtyp == VREG)
+ vtyp = IFTOVT(vmode);
+ if (isnq) {
+ rdev = fxdr_unsigned(long, fp->fa_nqrdev);
+ fxdr_nqtime(&fp->fa_nqmtime, &mtime);
+ } else {
+ rdev = fxdr_unsigned(long, fp->fa_nfsrdev);
+ fxdr_nfstime(&fp->fa_nfsmtime, &mtime);
+ }
+ /*
+ * If v_type == VNON it is a new node, so fill in the v_type,
+ * n_mtime fields. Check to see if it represents a special
+ * device, and if so, check for a possible alias. Once the
+ * correct vnode has been obtained, fill in the rest of the
+ * information.
+ */
+ np = VTONFS(vp);
+ if (vp->v_type == VNON) {
+ if (vtyp == VCHR && rdev == 0xffffffff)
+ vp->v_type = vtyp = VFIFO;
+ else
+ vp->v_type = vtyp;
+ if (vp->v_type == VFIFO) {
+#ifdef FIFO
+ extern int (**fifo_nfsv2nodeop_p)();
+ vp->v_op = fifo_nfsv2nodeop_p;
+#else
+ return (EOPNOTSUPP);
+#endif /* FIFO */
+ }
+ if (vp->v_type == VCHR || vp->v_type == VBLK) {
+ vp->v_op = spec_nfsv2nodeop_p;
+ if (nvp = checkalias(vp, (dev_t)rdev, vp->v_mount)) {
+ /*
+ * Discard unneeded vnode, but save its nfsnode.
+ */
+ if (nq = np->n_forw)
+ nq->n_back = np->n_back;
+ *np->n_back = nq;
+ nvp->v_data = vp->v_data;
+ vp->v_data = NULL;
+ vp->v_op = spec_vnodeop_p;
+ vrele(vp);
+ vgone(vp);
+ /*
+ * Reinitialize aliased node.
+ */
+ np->n_vnode = nvp;
+ nhpp = (struct nfsnode **)nfs_hash(&np->n_fh);
+ if (nq = *nhpp)
+ nq->n_back = &np->n_forw;
+ np->n_forw = nq;
+ np->n_back = nhpp;
+ *nhpp = np;
+ *vpp = vp = nvp;
+ }
+ }
+ np->n_mtime = mtime.ts_sec;
+ }
+ vap = &np->n_vattr;
+ vap->va_type = vtyp;
+ vap->va_mode = (vmode & 07777);
+ vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
+ vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
+ vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
+ vap->va_rdev = (dev_t)rdev;
+ vap->va_mtime = mtime;
+ vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+ if (isnq) {
+ fxdr_hyper(&fp->fa_nqsize, &vap->va_size);
+ vap->va_blocksize = fxdr_unsigned(long, fp->fa_nqblocksize);
+ fxdr_hyper(&fp->fa_nqbytes, &vap->va_bytes);
+ vap->va_fileid = fxdr_unsigned(long, fp->fa_nqfileid);
+ fxdr_nqtime(&fp->fa_nqatime, &vap->va_atime);
+ vap->va_flags = fxdr_unsigned(u_long, fp->fa_nqflags);
+ fxdr_nqtime(&fp->fa_nqctime, &vap->va_ctime);
+ vap->va_gen = fxdr_unsigned(u_long, fp->fa_nqgen);
+ fxdr_hyper(&fp->fa_nqfilerev, &vap->va_filerev);
+ } else {
+ vap->va_size = fxdr_unsigned(u_long, fp->fa_nfssize);
+ vap->va_blocksize = fxdr_unsigned(long, fp->fa_nfsblocksize);
+ vap->va_bytes = fxdr_unsigned(long, fp->fa_nfsblocks) * NFS_FABLKSIZE;
+ vap->va_fileid = fxdr_unsigned(long, fp->fa_nfsfileid);
+ fxdr_nfstime(&fp->fa_nfsatime, &vap->va_atime);
+ vap->va_flags = 0;
+ vap->va_ctime.ts_sec = fxdr_unsigned(long, fp->fa_nfsctime.nfs_sec);
+ vap->va_ctime.ts_nsec = 0;
+ vap->va_gen = fxdr_unsigned(u_long, fp->fa_nfsctime.nfs_usec);
+ vap->va_filerev = 0;
+ }
+ if (vap->va_size != np->n_size) {
+ if (vap->va_type == VREG) {
+ if (np->n_flag & NMODIFIED) {
+ if (vap->va_size < np->n_size)
+ vap->va_size = np->n_size;
+ else
+ np->n_size = vap->va_size;
+ } else
+ np->n_size = vap->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else
+ np->n_size = vap->va_size;
+ }
+ np->n_attrstamp = time.tv_sec;
+ *dposp = dpos;
+ *mdp = md;
+ if (vaper != NULL) {
+ bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
+#ifdef notdef
+ if ((np->n_flag & NMODIFIED) && np->n_size > vap->va_size)
+ if (np->n_size > vap->va_size)
+ vaper->va_size = np->n_size;
+#endif
+ if (np->n_flag & NCHG) {
+ if (np->n_flag & NACC) {
+ vaper->va_atime.ts_sec = np->n_atim.tv_sec;
+ vaper->va_atime.ts_nsec =
+ np->n_atim.tv_usec * 1000;
+ }
+ if (np->n_flag & NUPD) {
+ vaper->va_mtime.ts_sec = np->n_mtim.tv_sec;
+ vaper->va_mtime.ts_nsec =
+ np->n_mtim.tv_usec * 1000;
+ }
+ }
+ }
+ return (0);
+}
+
+/*
+ * Check the time stamp
+ * If the cache is valid, copy contents to *vap and return 0
+ * otherwise return an error
+ */
+int
+nfs_getattrcache(vp, vaper)
+ register struct vnode *vp;
+ struct vattr *vaper;
+{
+ register struct nfsnode *np = VTONFS(vp);
+ register struct vattr *vap;
+
+ if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQLOOKLEASE) {
+ if (!NQNFS_CKCACHABLE(vp, NQL_READ) || np->n_attrstamp == 0) {
+ nfsstats.attrcache_misses++;
+ return (ENOENT);
+ }
+ } else if ((time.tv_sec - np->n_attrstamp) >= NFS_ATTRTIMEO(np)) {
+ nfsstats.attrcache_misses++;
+ return (ENOENT);
+ }
+ nfsstats.attrcache_hits++;
+ vap = &np->n_vattr;
+ if (vap->va_size != np->n_size) {
+ if (vap->va_type == VREG) {
+ if (np->n_flag & NMODIFIED) {
+ if (vap->va_size < np->n_size)
+ vap->va_size = np->n_size;
+ else
+ np->n_size = vap->va_size;
+ } else
+ np->n_size = vap->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else
+ np->n_size = vap->va_size;
+ }
+ bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(struct vattr));
+#ifdef notdef
+ if ((np->n_flag & NMODIFIED) == 0) {
+ np->n_size = vaper->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else if (np->n_size > vaper->va_size)
+ if (np->n_size > vaper->va_size)
+ vaper->va_size = np->n_size;
+#endif
+ if (np->n_flag & NCHG) {
+ if (np->n_flag & NACC) {
+ vaper->va_atime.ts_sec = np->n_atim.tv_sec;
+ vaper->va_atime.ts_nsec = np->n_atim.tv_usec * 1000;
+ }
+ if (np->n_flag & NUPD) {
+ vaper->va_mtime.ts_sec = np->n_mtim.tv_sec;
+ vaper->va_mtime.ts_nsec = np->n_mtim.tv_usec * 1000;
+ }
+ }
+ return (0);
+}
+
+/*
+ * Set up nameidata for a lookup() call and do it
+ */
+int
+nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p)
+ register struct nameidata *ndp;
+ fhandle_t *fhp;
+ int len;
+ struct nfssvc_sock *slp;
+ struct mbuf *nam;
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ struct proc *p;
+{
+ register int i, rem;
+ register struct mbuf *md;
+ register char *fromcp, *tocp;
+ struct vnode *dp;
+ int error, rdonly;
+ struct componentname *cnp = &ndp->ni_cnd;
+
+ MALLOC(cnp->cn_pnbuf, char *, len + 1, M_NAMEI, M_WAITOK);
+ /*
+ * Copy the name from the mbuf list to ndp->ni_pnbuf
+ * and set the various ndp fields appropriately.
+ */
+ fromcp = *dposp;
+ tocp = cnp->cn_pnbuf;
+ md = *mdp;
+ rem = mtod(md, caddr_t) + md->m_len - fromcp;
+ cnp->cn_hash = 0;
+ for (i = 0; i < len; i++) {
+ while (rem == 0) {
+ md = md->m_next;
+ if (md == NULL) {
+ error = EBADRPC;
+ goto out;
+ }
+ fromcp = mtod(md, caddr_t);
+ rem = md->m_len;
+ }
+ if (*fromcp == '\0' || *fromcp == '/') {
+ error = EINVAL;
+ goto out;
+ }
+ cnp->cn_hash += (unsigned char)*fromcp;
+ *tocp++ = *fromcp++;
+ rem--;
+ }
+ *tocp = '\0';
+ *mdp = md;
+ *dposp = fromcp;
+ len = nfsm_rndup(len)-len;
+ if (len > 0) {
+ if (rem >= len)
+ *dposp += len;
+ else if (error = nfs_adv(mdp, dposp, len, rem))
+ goto out;
+ }
+ ndp->ni_pathlen = tocp - cnp->cn_pnbuf;
+ cnp->cn_nameptr = cnp->cn_pnbuf;
+ /*
+ * Extract and set starting directory.
+ */
+ if (error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
+ nam, &rdonly))
+ goto out;
+ if (dp->v_type != VDIR) {
+ vrele(dp);
+ error = ENOTDIR;
+ goto out;
+ }
+ ndp->ni_startdir = dp;
+ if (rdonly)
+ cnp->cn_flags |= (NOCROSSMOUNT | RDONLY);
+ else
+ cnp->cn_flags |= NOCROSSMOUNT;
+ /*
+ * And call lookup() to do the real work
+ */
+ cnp->cn_proc = p;
+ if (error = lookup(ndp))
+ goto out;
+ /*
+ * Check for encountering a symbolic link
+ */
+ if (cnp->cn_flags & ISSYMLINK) {
+ if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
+ vput(ndp->ni_dvp);
+ else
+ vrele(ndp->ni_dvp);
+ vput(ndp->ni_vp);
+ ndp->ni_vp = NULL;
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * Check for saved name request
+ */
+ if (cnp->cn_flags & (SAVENAME | SAVESTART)) {
+ cnp->cn_flags |= HASBUF;
+ return (0);
+ }
+out:
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ return (error);
+}
+
+/*
+ * A fiddled version of m_adj() that ensures null fill to a long
+ * boundary and only trims off the back end
+ */
+void
+nfsm_adj(mp, len, nul)
+ struct mbuf *mp;
+ register int len;
+ int nul;
+{
+ register struct mbuf *m;
+ register int count, i;
+ register char *cp;
+
+ /*
+ * Trim from tail. Scan the mbuf chain,
+ * calculating its length and finding the last mbuf.
+ * If the adjustment only affects this mbuf, then just
+ * adjust and return. Otherwise, rescan and truncate
+ * after the remaining size.
+ */
+ count = 0;
+ m = mp;
+ for (;;) {
+ count += m->m_len;
+ if (m->m_next == (struct mbuf *)0)
+ break;
+ m = m->m_next;
+ }
+ if (m->m_len > len) {
+ m->m_len -= len;
+ if (nul > 0) {
+ cp = mtod(m, caddr_t)+m->m_len-nul;
+ for (i = 0; i < nul; i++)
+ *cp++ = '\0';
+ }
+ return;
+ }
+ count -= len;
+ if (count < 0)
+ count = 0;
+ /*
+ * Correct length for chain is "count".
+ * Find the mbuf with last data, adjust its length,
+ * and toss data from remaining mbufs on chain.
+ */
+ for (m = mp; m; m = m->m_next) {
+ if (m->m_len >= count) {
+ m->m_len = count;
+ if (nul > 0) {
+ cp = mtod(m, caddr_t)+m->m_len-nul;
+ for (i = 0; i < nul; i++)
+ *cp++ = '\0';
+ }
+ break;
+ }
+ count -= m->m_len;
+ }
+ while (m = m->m_next)
+ m->m_len = 0;
+}
+
+/*
+ * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
+ * - look up fsid in mount list (if not found ret error)
+ * - get vp and export rights by calling VFS_FHTOVP()
+ * - if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
+ * - if not lockflag unlock it with VOP_UNLOCK()
+ */
+int
+nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp)
+ fhandle_t *fhp;
+ int lockflag;
+ struct vnode **vpp;
+ struct ucred *cred;
+ struct nfssvc_sock *slp;
+ struct mbuf *nam;
+ int *rdonlyp;
+{
+ register struct mount *mp;
+ register struct nfsuid *uidp;
+ register int i;
+ struct ucred *credanon;
+ int error, exflags;
+
+ *vpp = (struct vnode *)0;
+ if ((mp = getvfs(&fhp->fh_fsid)) == NULL)
+ return (ESTALE);
+ if (error = VFS_FHTOVP(mp, &fhp->fh_fid, nam, vpp, &exflags, &credanon))
+ return (error);
+ /*
+ * Check/setup credentials.
+ */
+ if (exflags & MNT_EXKERB) {
+ uidp = slp->ns_uidh[NUIDHASH(cred->cr_uid)];
+ while (uidp) {
+ if (uidp->nu_uid == cred->cr_uid)
+ break;
+ uidp = uidp->nu_hnext;
+ }
+ if (uidp) {
+ cred->cr_uid = uidp->nu_cr.cr_uid;
+ for (i = 0; i < uidp->nu_cr.cr_ngroups; i++)
+ cred->cr_groups[i] = uidp->nu_cr.cr_groups[i];
+ } else {
+ vput(*vpp);
+ return (NQNFS_AUTHERR);
+ }
+ } else if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
+ cred->cr_uid = credanon->cr_uid;
+ for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
+ cred->cr_groups[i] = credanon->cr_groups[i];
+ }
+ if (exflags & MNT_EXRDONLY)
+ *rdonlyp = 1;
+ else
+ *rdonlyp = 0;
+ if (!lockflag)
+ VOP_UNLOCK(*vpp);
+ return (0);
+}
+
+/*
+ * This function compares two net addresses by family and returns TRUE
+ * if they are the same host.
+ * If there is any doubt, return FALSE.
+ * The AF_INET family is handled as a special case so that address mbufs
+ * don't need to be saved to store "struct in_addr", which is only 4 bytes.
+ */
+int
+netaddr_match(family, haddr, nam)
+ int family;
+ union nethostaddr *haddr;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *inetaddr;
+
+ switch (family) {
+ case AF_INET:
+ inetaddr = mtod(nam, struct sockaddr_in *);
+ if (inetaddr->sin_family == AF_INET &&
+ inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
+ return (1);
+ break;
+#ifdef ISO
+ case AF_ISO:
+ {
+ register struct sockaddr_iso *isoaddr1, *isoaddr2;
+
+ isoaddr1 = mtod(nam, struct sockaddr_iso *);
+ isoaddr2 = mtod(haddr->had_nam, struct sockaddr_iso *);
+ if (isoaddr1->siso_family == AF_ISO &&
+ isoaddr1->siso_nlen > 0 &&
+ isoaddr1->siso_nlen == isoaddr2->siso_nlen &&
+ SAME_ISOADDR(isoaddr1, isoaddr2))
+ return (1);
+ break;
+ }
+#endif /* ISO */
+ default:
+ break;
+ };
+ return (0);
+}
diff --git a/sys/nfs/nfs_common.h b/sys/nfs/nfs_common.h
new file mode 100644
index 0000000..879db36
--- /dev/null
+++ b/sys/nfs/nfs_common.h
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsm_subs.h 8.1 (Berkeley) 6/16/93
+ */
+
+/*
+ * These macros do strange and peculiar things to mbuf chains for
+ * the assistance of the nfs code. To attempt to use them for any
+ * other purpose will be dangerous. (they make weird assumptions)
+ */
+
+/*
+ * First define what the actual subs. return
+ */
+extern struct mbuf *nfsm_reqh();
+
+#define M_HASCL(m) ((m)->m_flags & M_EXT)
+#define NFSMINOFF(m) \
+ if (M_HASCL(m)) \
+ (m)->m_data = (m)->m_ext.ext_buf; \
+ else if ((m)->m_flags & M_PKTHDR) \
+ (m)->m_data = (m)->m_pktdat; \
+ else \
+ (m)->m_data = (m)->m_dat
+#define NFSMADV(m, s) (m)->m_data += (s)
+#define NFSMSIZ(m) ((M_HASCL(m))?MCLBYTES: \
+ (((m)->m_flags & M_PKTHDR)?MHLEN:MLEN))
+
+/*
+ * Now for the macros that do the simple stuff and call the functions
+ * for the hard stuff.
+ * These macros use several vars. declared in nfsm_reqhead and these
+ * vars. must not be used elsewhere unless you are careful not to corrupt
+ * them. The vars. starting with pN and tN (N=1,2,3,..) are temporaries
+ * that may be used so long as the value is not expected to retained
+ * after a macro.
+ * I know, this is kind of dorkey, but it makes the actual op functions
+ * fairly clean and deals with the mess caused by the xdr discriminating
+ * unions.
+ */
+
+#define nfsm_build(a,c,s) \
+ { if ((s) > M_TRAILINGSPACE(mb)) { \
+ MGET(mb2, M_WAIT, MT_DATA); \
+ if ((s) > MLEN) \
+ panic("build > MLEN"); \
+ mb->m_next = mb2; \
+ mb = mb2; \
+ mb->m_len = 0; \
+ bpos = mtod(mb, caddr_t); \
+ } \
+ (a) = (c)(bpos); \
+ mb->m_len += (s); \
+ bpos += (s); }
+
+#define nfsm_dissect(a,c,s) \
+ { t1 = mtod(md, caddr_t)+md->m_len-dpos; \
+ if (t1 >= (s)) { \
+ (a) = (c)(dpos); \
+ dpos += (s); \
+ } else if (error = nfsm_disct(&md, &dpos, (s), t1, &cp2)) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ } else { \
+ (a) = (c)cp2; \
+ } }
+
+#define nfsm_fhtom(v) \
+ nfsm_build(cp,caddr_t,NFSX_FH); \
+ bcopy((caddr_t)&(VTONFS(v)->n_fh), cp, NFSX_FH)
+
+#define nfsm_srvfhtom(f) \
+ nfsm_build(cp,caddr_t,NFSX_FH); \
+ bcopy((caddr_t)(f), cp, NFSX_FH)
+
+#define nfsm_mtofh(d,v) \
+ { struct nfsnode *np; nfsv2fh_t *fhp; \
+ nfsm_dissect(fhp,nfsv2fh_t *,NFSX_FH); \
+ if (error = nfs_nget((d)->v_mount, fhp, &np)) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ } \
+ (v) = NFSTOV(np); \
+ nfsm_loadattr(v, (struct vattr *)0); \
+ }
+
+#define nfsm_loadattr(v,a) \
+ { struct vnode *tvp = (v); \
+ if (error = nfs_loadattrcache(&tvp, &md, &dpos, (a))) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ } \
+ (v) = tvp; }
+
+#define nfsm_strsiz(s,m) \
+ { nfsm_dissect(tl,u_long *,NFSX_UNSIGNED); \
+ if (((s) = fxdr_unsigned(long,*tl)) > (m)) { \
+ m_freem(mrep); \
+ error = EBADRPC; \
+ goto nfsmout; \
+ } }
+
+#define nfsm_srvstrsiz(s,m) \
+ { nfsm_dissect(tl,u_long *,NFSX_UNSIGNED); \
+ if (((s) = fxdr_unsigned(long,*tl)) > (m) || (s) <= 0) { \
+ error = EBADRPC; \
+ nfsm_reply(0); \
+ } }
+
+#define nfsm_mtouio(p,s) \
+ if ((s) > 0 && \
+ (error = nfsm_mbuftouio(&md,(p),(s),&dpos))) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ }
+
+#define nfsm_uiotom(p,s) \
+ if (error = nfsm_uiotombuf((p),&mb,(s),&bpos)) { \
+ m_freem(mreq); \
+ goto nfsmout; \
+ }
+
+#define nfsm_reqhead(v,a,s) \
+ mb = mreq = nfsm_reqh((v),(a),(s),&bpos)
+
+#define nfsm_reqdone m_freem(mrep); \
+ nfsmout:
+
+#define nfsm_rndup(a) (((a)+3)&(~0x3))
+
+#define nfsm_request(v, t, p, c) \
+ if (error = nfs_request((v), mreq, (t), (p), \
+ (c), &mrep, &md, &dpos)) \
+ goto nfsmout
+
+#define nfsm_strtom(a,s,m) \
+ if ((s) > (m)) { \
+ m_freem(mreq); \
+ error = ENAMETOOLONG; \
+ goto nfsmout; \
+ } \
+ t2 = nfsm_rndup(s)+NFSX_UNSIGNED; \
+ if (t2 <= M_TRAILINGSPACE(mb)) { \
+ nfsm_build(tl,u_long *,t2); \
+ *tl++ = txdr_unsigned(s); \
+ *(tl+((t2>>2)-2)) = 0; \
+ bcopy((caddr_t)(a), (caddr_t)tl, (s)); \
+ } else if (error = nfsm_strtmbuf(&mb, &bpos, (a), (s))) { \
+ m_freem(mreq); \
+ goto nfsmout; \
+ }
+
+#define nfsm_srvdone \
+ nfsmout: \
+ return(error)
+
+#define nfsm_reply(s) \
+ { \
+ nfsd->nd_repstat = error; \
+ if (error) \
+ (void) nfs_rephead(0, nfsd, error, cache, &frev, \
+ mrq, &mb, &bpos); \
+ else \
+ (void) nfs_rephead((s), nfsd, error, cache, &frev, \
+ mrq, &mb, &bpos); \
+ m_freem(mrep); \
+ mreq = *mrq; \
+ if (error) \
+ return(0); \
+ }
+
+#define nfsm_adv(s) \
+ t1 = mtod(md, caddr_t)+md->m_len-dpos; \
+ if (t1 >= (s)) { \
+ dpos += (s); \
+ } else if (error = nfs_adv(&md, &dpos, (s), t1)) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ }
+
+#define nfsm_srvmtofh(f) \
+ nfsm_dissect(tl, u_long *, NFSX_FH); \
+ bcopy((caddr_t)tl, (caddr_t)f, NFSX_FH)
+
+#define nfsm_clget \
+ if (bp >= be) { \
+ if (mp == mb) \
+ mp->m_len += bp-bpos; \
+ MGET(mp, M_WAIT, MT_DATA); \
+ MCLGET(mp, M_WAIT); \
+ mp->m_len = NFSMSIZ(mp); \
+ mp2->m_next = mp; \
+ mp2 = mp; \
+ bp = mtod(mp, caddr_t); \
+ be = bp+mp->m_len; \
+ } \
+ tl = (u_long *)bp
+
+#define nfsm_srvfillattr \
+ fp->fa_type = vtonfs_type(vap->va_type); \
+ fp->fa_mode = vtonfs_mode(vap->va_type, vap->va_mode); \
+ fp->fa_nlink = txdr_unsigned(vap->va_nlink); \
+ fp->fa_uid = txdr_unsigned(vap->va_uid); \
+ fp->fa_gid = txdr_unsigned(vap->va_gid); \
+ if (nfsd->nd_nqlflag == NQL_NOVAL) { \
+ fp->fa_nfsblocksize = txdr_unsigned(vap->va_blocksize); \
+ if (vap->va_type == VFIFO) \
+ fp->fa_nfsrdev = 0xffffffff; \
+ else \
+ fp->fa_nfsrdev = txdr_unsigned(vap->va_rdev); \
+ fp->fa_nfsfsid = txdr_unsigned(vap->va_fsid); \
+ fp->fa_nfsfileid = txdr_unsigned(vap->va_fileid); \
+ fp->fa_nfssize = txdr_unsigned(vap->va_size); \
+ fp->fa_nfsblocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE); \
+ txdr_nfstime(&vap->va_atime, &fp->fa_nfsatime); \
+ txdr_nfstime(&vap->va_mtime, &fp->fa_nfsmtime); \
+ fp->fa_nfsctime.nfs_sec = txdr_unsigned(vap->va_ctime.ts_sec); \
+ fp->fa_nfsctime.nfs_usec = txdr_unsigned(vap->va_gen); \
+ } else { \
+ fp->fa_nqblocksize = txdr_unsigned(vap->va_blocksize); \
+ if (vap->va_type == VFIFO) \
+ fp->fa_nqrdev = 0xffffffff; \
+ else \
+ fp->fa_nqrdev = txdr_unsigned(vap->va_rdev); \
+ fp->fa_nqfsid = txdr_unsigned(vap->va_fsid); \
+ fp->fa_nqfileid = txdr_unsigned(vap->va_fileid); \
+ txdr_hyper(&vap->va_size, &fp->fa_nqsize); \
+ txdr_hyper(&vap->va_bytes, &fp->fa_nqbytes); \
+ txdr_nqtime(&vap->va_atime, &fp->fa_nqatime); \
+ txdr_nqtime(&vap->va_mtime, &fp->fa_nqmtime); \
+ txdr_nqtime(&vap->va_ctime, &fp->fa_nqctime); \
+ fp->fa_nqflags = txdr_unsigned(vap->va_flags); \
+ fp->fa_nqgen = txdr_unsigned(vap->va_gen); \
+ txdr_hyper(&vap->va_filerev, &fp->fa_nqfilerev); \
+ }
+
diff --git a/sys/nfs/nfs_node.c b/sys/nfs/nfs_node.c
new file mode 100644
index 0000000..9c70c53
--- /dev/null
+++ b/sys/nfs/nfs_node.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_node.c 8.2 (Berkeley) 12/30/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nqnfs.h>
+
+struct nfsnode **nheadhashtbl;
+u_long nheadhash;
+#define NFSNOHASH(fhsum) ((fhsum)&nheadhash)
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Initialize hash links for nfsnodes
+ * and build nfsnode free list.
+ */
+void
+nfs_nhinit()
+{
+
+#ifndef lint
+ if ((sizeof(struct nfsnode) - 1) & sizeof(struct nfsnode))
+ printf("nfs_nhinit: bad size %d\n", sizeof(struct nfsnode));
+#endif /* not lint */
+ nheadhashtbl = hashinit(desiredvnodes, M_NFSNODE, &nheadhash);
+}
+
+/*
+ * Compute an entry in the NFS hash table structure
+ */
+struct nfsnode **
+nfs_hash(fhp)
+ register nfsv2fh_t *fhp;
+{
+ register u_char *fhpp;
+ register u_long fhsum;
+ int i;
+
+ fhpp = &fhp->fh_bytes[0];
+ fhsum = 0;
+ for (i = 0; i < NFSX_FH; i++)
+ fhsum += *fhpp++;
+ return (&nheadhashtbl[NFSNOHASH(fhsum)]);
+}
+
+/*
+ * Look up a vnode/nfsnode by file handle.
+ * Callers must check for mount points!!
+ * In all cases, a pointer to a
+ * nfsnode structure is returned.
+ */
+int
+nfs_nget(mntp, fhp, npp)
+ struct mount *mntp;
+ register nfsv2fh_t *fhp;
+ struct nfsnode **npp;
+{
+ register struct nfsnode *np, *nq, **nhpp;
+ register struct vnode *vp;
+ extern int (**nfsv2_vnodeop_p)();
+ struct vnode *nvp;
+ int error;
+
+ nhpp = nfs_hash(fhp);
+loop:
+ for (np = *nhpp; np; np = np->n_forw) {
+ if (mntp != NFSTOV(np)->v_mount ||
+ bcmp((caddr_t)fhp, (caddr_t)&np->n_fh, NFSX_FH))
+ continue;
+ vp = NFSTOV(np);
+ if (vget(vp, 1))
+ goto loop;
+ *npp = np;
+ return(0);
+ }
+ if (error = getnewvnode(VT_NFS, mntp, nfsv2_vnodeop_p, &nvp)) {
+ *npp = 0;
+ return (error);
+ }
+ vp = nvp;
+ MALLOC(np, struct nfsnode *, sizeof *np, M_NFSNODE, M_WAITOK);
+ vp->v_data = np;
+ np->n_vnode = vp;
+ /*
+ * Insert the nfsnode in the hash queue for its new file handle
+ */
+ np->n_flag = 0;
+ if (nq = *nhpp)
+ nq->n_back = &np->n_forw;
+ np->n_forw = nq;
+ np->n_back = nhpp;
+ *nhpp = np;
+ bcopy((caddr_t)fhp, (caddr_t)&np->n_fh, NFSX_FH);
+ np->n_attrstamp = 0;
+ np->n_direofoffset = 0;
+ np->n_sillyrename = (struct sillyrename *)0;
+ np->n_size = 0;
+ np->n_mtime = 0;
+ if (VFSTONFS(mntp)->nm_flag & NFSMNT_NQNFS) {
+ np->n_brev = 0;
+ np->n_lrev = 0;
+ np->n_expiry = (time_t)0;
+ np->n_tnext = (struct nfsnode *)0;
+ }
+ *npp = np;
+ return (0);
+}
+
+int
+nfs_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct nfsnode *np;
+ register struct sillyrename *sp;
+ struct proc *p = curproc; /* XXX */
+ extern int prtactive;
+
+ np = VTONFS(ap->a_vp);
+ if (prtactive && ap->a_vp->v_usecount != 0)
+ vprint("nfs_inactive: pushing active", ap->a_vp);
+ sp = np->n_sillyrename;
+ np->n_sillyrename = (struct sillyrename *)0;
+ if (sp) {
+ /*
+ * Remove the silly file that was rename'd earlier
+ */
+ (void) nfs_vinvalbuf(ap->a_vp, 0, sp->s_cred, p, 1);
+ nfs_removeit(sp);
+ crfree(sp->s_cred);
+ vrele(sp->s_dvp);
+#ifdef SILLYSEPARATE
+ free((caddr_t)sp, M_NFSREQ);
+#endif
+ }
+ np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NQNFSEVICTED |
+ NQNFSNONCACHE | NQNFSWRITE);
+ return (0);
+}
+
+/*
+ * Reclaim an nfsnode so that it can be used for other purposes.
+ */
+int
+nfs_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+ register struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+ register struct nfsnode *nq;
+ extern int prtactive;
+
+ if (prtactive && vp->v_usecount != 0)
+ vprint("nfs_reclaim: pushing active", vp);
+ /*
+ * Remove the nfsnode from its hash chain.
+ */
+ if (nq = np->n_forw)
+ nq->n_back = np->n_back;
+ *np->n_back = nq;
+
+ /*
+ * For nqnfs, take it off the timer queue as required.
+ */
+ if ((nmp->nm_flag & NFSMNT_NQNFS) && np->n_tnext) {
+ if (np->n_tnext == (struct nfsnode *)nmp)
+ nmp->nm_tprev = np->n_tprev;
+ else
+ np->n_tnext->n_tprev = np->n_tprev;
+ if (np->n_tprev == (struct nfsnode *)nmp)
+ nmp->nm_tnext = np->n_tnext;
+ else
+ np->n_tprev->n_tnext = np->n_tnext;
+ }
+ cache_purge(vp);
+ FREE(vp->v_data, M_NFSNODE);
+ vp->v_data = (void *)0;
+ return (0);
+}
+
+/*
+ * Lock an nfsnode
+ */
+int
+nfs_lock(ap)
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+
+ /*
+ * Ugh, another place where interruptible mounts will get hung.
+ * If you make this sleep interruptible, then you have to fix all
+ * the VOP_LOCK() calls to expect interruptibility.
+ */
+ while (vp->v_flag & VXLOCK) {
+ vp->v_flag |= VXWANT;
+ sleep((caddr_t)vp, PINOD);
+ }
+ if (vp->v_tag == VT_NON)
+ return (ENOENT);
+ return (0);
+}
+
+/*
+ * Unlock an nfsnode
+ */
+int
+nfs_unlock(ap)
+ struct vop_unlock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * Check for a locked nfsnode
+ */
+int
+nfs_islocked(ap)
+ struct vop_islocked_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * Nfs abort op, called after namei() when a CREATE/DELETE isn't actually
+ * done. Currently nothing to do.
+ */
+/* ARGSUSED */
+int
+nfs_abortop(ap)
+ struct vop_abortop_args /* {
+ struct vnode *a_dvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+
+ if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
+ FREE(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ return (0);
+}
diff --git a/sys/nfs/nfs_nqlease.c b/sys/nfs/nfs_nqlease.c
new file mode 100644
index 0000000..7ba69e5
--- /dev/null
+++ b/sys/nfs/nfs_nqlease.c
@@ -0,0 +1,1236 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_nqlease.c 8.3 (Berkeley) 1/4/94
+ */
+
+/*
+ * References:
+ * Cary G. Gray and David R. Cheriton, "Leases: An Efficient Fault-Tolerant
+ * Mechanism for Distributed File Cache Consistency",
+ * In Proc. of the Twelfth ACM Symposium on Operating Systems
+ * Principals, pg. 202-210, Litchfield Park, AZ, Dec. 1989.
+ * Michael N. Nelson, Brent B. Welch and John K. Ousterhout, "Caching
+ * in the Sprite Network File System", ACM TOCS 6(1),
+ * pages 134-154, February 1988.
+ * V. Srinivasan and Jeffrey C. Mogul, "Spritely NFS: Implementation and
+ * Performance of Cache-Consistency Protocols", Digital
+ * Equipment Corporation WRL Research Report 89/5, May 1989.
+ */
+#include <sys/param.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/file.h>
+#include <sys/buf.h>
+#include <sys/stat.h>
+#include <sys/protosw.h>
+
+#include <netinet/in.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nqnfs.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfsmount.h>
+
+/*
+ * List head for the lease queue and other global data.
+ * At any time a lease is linked into a list ordered by increasing expiry time.
+ */
+#define NQFHHASH(f) ((*((u_long *)(f)))&nqfheadhash)
+
+union nqsrvthead nqthead;
+struct nqlease **nqfhead;
+u_long nqfheadhash;
+time_t nqnfsstarttime = (time_t)0;
+u_long nqnfs_prog, nqnfs_vers;
+int nqsrv_clockskew = NQ_CLOCKSKEW;
+int nqsrv_writeslack = NQ_WRITESLACK;
+int nqsrv_maxlease = NQ_MAXLEASE;
+int nqsrv_maxnumlease = NQ_MAXNUMLEASE;
+void nqsrv_instimeq(), nqsrv_send_eviction(), nfs_sndunlock();
+void nqsrv_unlocklease(), nqsrv_waitfor_expiry(), nfsrv_slpderef();
+void nqsrv_addhost(), nqsrv_locklease(), nqnfs_serverd();
+void nqnfs_clientlease();
+struct mbuf *nfsm_rpchead();
+
+/*
+ * Signifies which rpcs can have piggybacked lease requests
+ */
+int nqnfs_piggy[NFS_NPROCS] = {
+ 0,
+ NQL_READ,
+ NQL_WRITE,
+ 0,
+ NQL_READ,
+ NQL_READ,
+ NQL_READ,
+ 0,
+ NQL_WRITE,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ NQL_READ,
+ 0,
+ NQL_READ,
+ 0,
+ 0,
+ 0,
+ 0,
+};
+
+int nnnnnn = sizeof (struct nqlease);
+int oooooo = sizeof (struct nfsnode);
+extern nfstype nfs_type[9];
+extern struct nfssvc_sock *nfs_udpsock, *nfs_cltpsock;
+extern struct nfsd nfsd_head;
+extern int nfsd_waiting;
+extern struct nfsreq nfsreqh;
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Get or check for a lease for "vp", based on NQL_CHECK flag.
+ * The rules are as follows:
+ * - if a current non-caching lease, reply non-caching
+ * - if a current lease for same host only, extend lease
+ * - if a read cachable lease and a read lease request
+ * add host to list any reply cachable
+ * - else { set non-cachable for read-write sharing }
+ * send eviction notice messages to all other hosts that have lease
+ * wait for lease termination { either by receiving vacated messages
+ * from all the other hosts or expiry
+ * via. timeout }
+ * modify lease to non-cachable
+ * - else if no current lease, issue new one
+ * - reply
+ * - return boolean TRUE iff nam should be m_freem()'d
+ * NB: Since nqnfs_serverd() is called from a timer, any potential tsleep()
+ * in here must be framed by nqsrv_locklease() and nqsrv_unlocklease().
+ * nqsrv_locklease() is coded such that at least one of LC_LOCKED and
+ * LC_WANTED is set whenever a process is tsleeping in it. The exception
+ * is when a new lease is being allocated, since it is not in the timer
+ * queue yet. (Ditto for the splsoftclock() and splx(s) calls)
+ */
+int
+nqsrv_getlease(vp, duration, flags, nd, nam, cachablep, frev, cred)
+ struct vnode *vp;
+ u_long *duration;
+ int flags;
+ struct nfsd *nd;
+ struct mbuf *nam;
+ int *cachablep;
+ u_quad_t *frev;
+ struct ucred *cred;
+{
+ register struct nqlease *lp, *lq, **lpp = 0;
+ register struct nqhost *lph = 0;
+ struct nqlease *tlp;
+ struct nqm **lphp;
+ struct vattr vattr;
+ fhandle_t fh;
+ int i, ok, error, s;
+
+ if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK)
+ return (0);
+ if (*duration > nqsrv_maxlease)
+ *duration = nqsrv_maxlease;
+ if (error = VOP_GETATTR(vp, &vattr, cred, nd->nd_procp))
+ return (error);
+ *frev = vattr.va_filerev;
+ s = splsoftclock();
+ tlp = vp->v_lease;
+ if ((flags & NQL_CHECK) == 0)
+ nfsstats.srvnqnfs_getleases++;
+ if (tlp == (struct nqlease *)0) {
+
+ /*
+ * Find the lease by searching the hash list.
+ */
+ fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+ if (error = VFS_VPTOFH(vp, &fh.fh_fid)) {
+ splx(s);
+ return (error);
+ }
+ lpp = &nqfhead[NQFHHASH(fh.fh_fid.fid_data)];
+ for (lp = *lpp; lp; lp = lp->lc_fhnext)
+ if (fh.fh_fsid.val[0] == lp->lc_fsid.val[0] &&
+ fh.fh_fsid.val[1] == lp->lc_fsid.val[1] &&
+ !bcmp(fh.fh_fid.fid_data, lp->lc_fiddata,
+ fh.fh_fid.fid_len - sizeof (long))) {
+ /* Found it */
+ lp->lc_vp = vp;
+ vp->v_lease = lp;
+ tlp = lp;
+ break;
+ }
+ }
+ lp = tlp;
+ if (lp) {
+ if ((lp->lc_flag & LC_NONCACHABLE) ||
+ (lp->lc_morehosts == (struct nqm *)0 &&
+ nqsrv_cmpnam(nd->nd_slp, nam, &lp->lc_host)))
+ goto doreply;
+ if ((flags & NQL_READ) && (lp->lc_flag & LC_WRITE)==0) {
+ if (flags & NQL_CHECK)
+ goto doreply;
+ if (nqsrv_cmpnam(nd->nd_slp, nam, &lp->lc_host))
+ goto doreply;
+ i = 0;
+ if (lp->lc_morehosts) {
+ lph = lp->lc_morehosts->lpm_hosts;
+ lphp = &lp->lc_morehosts->lpm_next;
+ ok = 1;
+ } else {
+ lphp = &lp->lc_morehosts;
+ ok = 0;
+ }
+ while (ok && (lph->lph_flag & LC_VALID)) {
+ if (nqsrv_cmpnam(nd->nd_slp, nam, lph))
+ goto doreply;
+ if (++i == LC_MOREHOSTSIZ) {
+ i = 0;
+ if (*lphp) {
+ lph = (*lphp)->lpm_hosts;
+ lphp = &((*lphp)->lpm_next);
+ } else
+ ok = 0;
+ } else
+ lph++;
+ }
+ nqsrv_locklease(lp);
+ if (!ok) {
+ *lphp = (struct nqm *)
+ malloc(sizeof (struct nqm),
+ M_NQMHOST, M_WAITOK);
+ bzero((caddr_t)*lphp, sizeof (struct nqm));
+ lph = (*lphp)->lpm_hosts;
+ }
+ nqsrv_addhost(lph, nd->nd_slp, nam);
+ nqsrv_unlocklease(lp);
+ } else {
+ lp->lc_flag |= LC_NONCACHABLE;
+ nqsrv_locklease(lp);
+ nqsrv_send_eviction(vp, lp, nd->nd_slp, nam, cred);
+ nqsrv_waitfor_expiry(lp);
+ nqsrv_unlocklease(lp);
+ }
+doreply:
+ /*
+ * Update the lease and return
+ */
+ if ((flags & NQL_CHECK) == 0)
+ nqsrv_instimeq(lp, *duration);
+ if (lp->lc_flag & LC_NONCACHABLE)
+ *cachablep = 0;
+ else {
+ *cachablep = 1;
+ if (flags & NQL_WRITE)
+ lp->lc_flag |= LC_WRITTEN;
+ }
+ splx(s);
+ return (0);
+ }
+ splx(s);
+ if (flags & NQL_CHECK)
+ return (0);
+
+ /*
+ * Allocate new lease
+ * The value of nqsrv_maxnumlease should be set generously, so that
+ * the following "printf" happens infrequently.
+ */
+ if (nfsstats.srvnqnfs_leases > nqsrv_maxnumlease) {
+ printf("Nqnfs server, too many leases\n");
+ do {
+ (void) tsleep((caddr_t)&lbolt, PSOCK,
+ "nqsrvnuml", 0);
+ } while (nfsstats.srvnqnfs_leases > nqsrv_maxnumlease);
+ }
+ MALLOC(lp, struct nqlease *, sizeof (struct nqlease), M_NQLEASE, M_WAITOK);
+ bzero((caddr_t)lp, sizeof (struct nqlease));
+ if (flags & NQL_WRITE)
+ lp->lc_flag |= (LC_WRITE | LC_WRITTEN);
+ nqsrv_addhost(&lp->lc_host, nd->nd_slp, nam);
+ lp->lc_vp = vp;
+ lp->lc_fsid = fh.fh_fsid;
+ bcopy(fh.fh_fid.fid_data, lp->lc_fiddata, fh.fh_fid.fid_len - sizeof (long));
+ if (lq = *lpp)
+ lq->lc_fhprev = &lp->lc_fhnext;
+ lp->lc_fhnext = lq;
+ lp->lc_fhprev = lpp;
+ *lpp = lp;
+ vp->v_lease = lp;
+ s = splsoftclock();
+ nqsrv_instimeq(lp, *duration);
+ splx(s);
+ *cachablep = 1;
+ if (++nfsstats.srvnqnfs_leases > nfsstats.srvnqnfs_maxleases)
+ nfsstats.srvnqnfs_maxleases = nfsstats.srvnqnfs_leases;
+ return (0);
+}
+
+/*
+ * Local lease check for server syscalls.
+ * Just set up args and let nqsrv_getlease() do the rest.
+ */
+void
+lease_check(vp, p, cred, flag)
+ struct vnode *vp;
+ struct proc *p;
+ struct ucred *cred;
+ int flag;
+{
+ int duration = 0, cache;
+ struct nfsd nfsd;
+ u_quad_t frev;
+
+ nfsd.nd_slp = NQLOCALSLP;
+ nfsd.nd_procp = p;
+ (void) nqsrv_getlease(vp, &duration, NQL_CHECK | flag, &nfsd,
+ (struct mbuf *)0, &cache, &frev, cred);
+}
+
+/*
+ * Add a host to an nqhost structure for a lease.
+ */
+void
+nqsrv_addhost(lph, slp, nam)
+ register struct nqhost *lph;
+ struct nfssvc_sock *slp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *saddr;
+
+ if (slp == NQLOCALSLP)
+ lph->lph_flag |= (LC_VALID | LC_LOCAL);
+ else if (slp == nfs_udpsock) {
+ saddr = mtod(nam, struct sockaddr_in *);
+ lph->lph_flag |= (LC_VALID | LC_UDP);
+ lph->lph_inetaddr = saddr->sin_addr.s_addr;
+ lph->lph_port = saddr->sin_port;
+ } else if (slp == nfs_cltpsock) {
+ lph->lph_nam = m_copym(nam, 0, M_COPYALL, M_WAIT);
+ lph->lph_flag |= (LC_VALID | LC_CLTP);
+ } else {
+ lph->lph_flag |= (LC_VALID | LC_SREF);
+ lph->lph_slp = slp;
+ slp->ns_sref++;
+ }
+}
+
+/*
+ * Update the lease expiry time and position it in the timer queue correctly.
+ */
+void
+nqsrv_instimeq(lp, duration)
+ register struct nqlease *lp;
+ u_long duration;
+{
+ register struct nqlease *tlp;
+ time_t newexpiry;
+
+ newexpiry = time.tv_sec + duration + nqsrv_clockskew;
+ if (lp->lc_expiry == newexpiry)
+ return;
+ if (lp->lc_chain1[0])
+ remque(lp);
+ lp->lc_expiry = newexpiry;
+
+ /*
+ * Find where in the queue it should be.
+ */
+ tlp = nqthead.th_chain[1];
+ while (tlp->lc_expiry > newexpiry && tlp != (struct nqlease *)&nqthead)
+ tlp = tlp->lc_chain1[1];
+ if (tlp == nqthead.th_chain[1])
+ NQSTORENOVRAM(newexpiry);
+ insque(lp, tlp);
+}
+
+/*
+ * Compare the requesting host address with the lph entry in the lease.
+ * Return true iff it is the same.
+ * This is somewhat messy due to the union in the nqhost structure.
+ * The local host is indicated by the special value of NQLOCALSLP for slp.
+ */
+int
+nqsrv_cmpnam(slp, nam, lph)
+ register struct nfssvc_sock *slp;
+ struct mbuf *nam;
+ register struct nqhost *lph;
+{
+ register struct sockaddr_in *saddr;
+ struct mbuf *addr;
+ union nethostaddr lhaddr;
+ int ret;
+
+ if (slp == NQLOCALSLP) {
+ if (lph->lph_flag & LC_LOCAL)
+ return (1);
+ else
+ return (0);
+ }
+ if (slp == nfs_udpsock || slp == nfs_cltpsock)
+ addr = nam;
+ else
+ addr = slp->ns_nam;
+ if (lph->lph_flag & LC_UDP)
+ ret = netaddr_match(AF_INET, &lph->lph_haddr, addr);
+ else if (lph->lph_flag & LC_CLTP)
+ ret = netaddr_match(AF_ISO, &lph->lph_claddr, addr);
+ else {
+ if ((lph->lph_slp->ns_flag & SLP_VALID) == 0)
+ return (0);
+ saddr = mtod(lph->lph_slp->ns_nam, struct sockaddr_in *);
+ if (saddr->sin_family == AF_INET)
+ lhaddr.had_inetaddr = saddr->sin_addr.s_addr;
+ else
+ lhaddr.had_nam = lph->lph_slp->ns_nam;
+ ret = netaddr_match(saddr->sin_family, &lhaddr, addr);
+ }
+ return (ret);
+}
+
+/*
+ * Send out eviction notice messages to all other hosts for the lease.
+ */
+void
+nqsrv_send_eviction(vp, lp, slp, nam, cred)
+ struct vnode *vp;
+ register struct nqlease *lp;
+ struct nfssvc_sock *slp;
+ struct mbuf *nam;
+ struct ucred *cred;
+{
+ register struct nqhost *lph = &lp->lc_host;
+ register struct mbuf *m;
+ register int siz;
+ struct nqm *lphnext = lp->lc_morehosts;
+ struct mbuf *mreq, *mb, *mb2, *nam2, *mheadend;
+ struct socket *so;
+ struct sockaddr_in *saddr;
+ fhandle_t *fhp;
+ caddr_t bpos, cp;
+ u_long xid;
+ int len = 1, ok = 1, i = 0;
+ int sotype, *solockp;
+
+ while (ok && (lph->lph_flag & LC_VALID)) {
+ if (nqsrv_cmpnam(slp, nam, lph))
+ lph->lph_flag |= LC_VACATED;
+ else if ((lph->lph_flag & (LC_LOCAL | LC_VACATED)) == 0) {
+ if (lph->lph_flag & LC_UDP) {
+ MGET(nam2, M_WAIT, MT_SONAME);
+ saddr = mtod(nam2, struct sockaddr_in *);
+ nam2->m_len = saddr->sin_len =
+ sizeof (struct sockaddr_in);
+ saddr->sin_family = AF_INET;
+ saddr->sin_addr.s_addr = lph->lph_inetaddr;
+ saddr->sin_port = lph->lph_port;
+ so = nfs_udpsock->ns_so;
+ } else if (lph->lph_flag & LC_CLTP) {
+ nam2 = lph->lph_nam;
+ so = nfs_cltpsock->ns_so;
+ } else if (lph->lph_slp->ns_flag & SLP_VALID) {
+ nam2 = (struct mbuf *)0;
+ so = lph->lph_slp->ns_so;
+ } else
+ goto nextone;
+ sotype = so->so_type;
+ if (so->so_proto->pr_flags & PR_CONNREQUIRED)
+ solockp = &lph->lph_slp->ns_solock;
+ else
+ solockp = (int *)0;
+ nfsm_reqhead((struct vnode *)0, NQNFSPROC_EVICTED,
+ NFSX_FH);
+ nfsm_build(cp, caddr_t, NFSX_FH);
+ bzero(cp, NFSX_FH);
+ fhp = (fhandle_t *)cp;
+ fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+ VFS_VPTOFH(vp, &fhp->fh_fid);
+ m = mreq;
+ siz = 0;
+ while (m) {
+ siz += m->m_len;
+ m = m->m_next;
+ }
+ if (siz <= 0 || siz > NFS_MAXPACKET) {
+ printf("mbuf siz=%d\n",siz);
+ panic("Bad nfs svc reply");
+ }
+ m = nfsm_rpchead(cred, TRUE, NQNFSPROC_EVICTED,
+ RPCAUTH_UNIX, 5*NFSX_UNSIGNED, (char *)0,
+ mreq, siz, &mheadend, &xid);
+ /*
+ * For stream protocols, prepend a Sun RPC
+ * Record Mark.
+ */
+ if (sotype == SOCK_STREAM) {
+ M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
+ *mtod(m, u_long *) = htonl(0x80000000 |
+ (m->m_pkthdr.len - NFSX_UNSIGNED));
+ }
+ if (((lph->lph_flag & (LC_UDP | LC_CLTP)) == 0 &&
+ (lph->lph_slp->ns_flag & SLP_VALID) == 0) ||
+ (solockp && (*solockp & NFSMNT_SNDLOCK)))
+ m_freem(m);
+ else {
+ if (solockp)
+ *solockp |= NFSMNT_SNDLOCK;
+ (void) nfs_send(so, nam2, m,
+ (struct nfsreq *)0);
+ if (solockp)
+ nfs_sndunlock(solockp);
+ }
+ if (lph->lph_flag & LC_UDP)
+ MFREE(nam2, m);
+ }
+nextone:
+ if (++i == len) {
+ if (lphnext) {
+ i = 0;
+ len = LC_MOREHOSTSIZ;
+ lph = lphnext->lpm_hosts;
+ lphnext = lphnext->lpm_next;
+ } else
+ ok = 0;
+ } else
+ lph++;
+ }
+}
+
+/*
+ * Wait for the lease to expire.
+ * This will occur when all clients have sent "vacated" messages to
+ * this server OR when it expires do to timeout.
+ */
+void
+nqsrv_waitfor_expiry(lp)
+ register struct nqlease *lp;
+{
+ register struct nqhost *lph;
+ register int i;
+ struct nqm *lphnext;
+ int len, ok;
+
+tryagain:
+ if (time.tv_sec > lp->lc_expiry)
+ return;
+ lph = &lp->lc_host;
+ lphnext = lp->lc_morehosts;
+ len = 1;
+ i = 0;
+ ok = 1;
+ while (ok && (lph->lph_flag & LC_VALID)) {
+ if ((lph->lph_flag & (LC_LOCAL | LC_VACATED)) == 0) {
+ lp->lc_flag |= LC_EXPIREDWANTED;
+ (void) tsleep((caddr_t)&lp->lc_flag, PSOCK,
+ "nqexp", 0);
+ goto tryagain;
+ }
+ if (++i == len) {
+ if (lphnext) {
+ i = 0;
+ len = LC_MOREHOSTSIZ;
+ lph = lphnext->lpm_hosts;
+ lphnext = lphnext->lpm_next;
+ } else
+ ok = 0;
+ } else
+ lph++;
+ }
+}
+
+/*
+ * Nqnfs server timer that maintains the server lease queue.
+ * Scan the lease queue for expired entries:
+ * - when one is found, wakeup anyone waiting for it
+ * else dequeue and free
+ */
+void
+nqnfs_serverd()
+{
+ register struct nqlease *lp, *lq;
+ register struct nqhost *lph;
+ struct nqlease *nextlp;
+ struct nqm *lphnext, *olphnext;
+ struct mbuf *n;
+ int i, len, ok;
+
+ lp = nqthead.th_chain[0];
+ while (lp != (struct nqlease *)&nqthead) {
+ if (lp->lc_expiry >= time.tv_sec)
+ break;
+ nextlp = lp->lc_chain1[0];
+ if (lp->lc_flag & LC_EXPIREDWANTED) {
+ lp->lc_flag &= ~LC_EXPIREDWANTED;
+ wakeup((caddr_t)&lp->lc_flag);
+ } else if ((lp->lc_flag & (LC_LOCKED | LC_WANTED)) == 0) {
+ /*
+ * Make a best effort at keeping a write caching lease long
+ * enough by not deleting it until it has been explicitly
+ * vacated or there have been no writes in the previous
+ * write_slack seconds since expiry and the nfsds are not
+ * all busy. The assumption is that if the nfsds are not
+ * all busy now (no queue of nfs requests), then the client
+ * would have been able to do at least one write to the
+ * file during the last write_slack seconds if it was still
+ * trying to push writes to the server.
+ */
+ if ((lp->lc_flag & (LC_WRITE | LC_VACATED)) == LC_WRITE &&
+ ((lp->lc_flag & LC_WRITTEN) || nfsd_waiting == 0)) {
+ lp->lc_flag &= ~LC_WRITTEN;
+ nqsrv_instimeq(lp, nqsrv_writeslack);
+ } else {
+ remque(lp);
+ if (lq = lp->lc_fhnext)
+ lq->lc_fhprev = lp->lc_fhprev;
+ *lp->lc_fhprev = lq;
+ /*
+ * This soft reference may no longer be valid, but
+ * no harm done. The worst case is if the vnode was
+ * recycled and has another valid lease reference,
+ * which is dereferenced prematurely.
+ */
+ lp->lc_vp->v_lease = (struct nqlease *)0;
+ lph = &lp->lc_host;
+ lphnext = lp->lc_morehosts;
+ olphnext = (struct nqm *)0;
+ len = 1;
+ i = 0;
+ ok = 1;
+ while (ok && (lph->lph_flag & LC_VALID)) {
+ if (lph->lph_flag & LC_CLTP)
+ MFREE(lph->lph_nam, n);
+ if (lph->lph_flag & LC_SREF)
+ nfsrv_slpderef(lph->lph_slp);
+ if (++i == len) {
+ if (olphnext) {
+ free((caddr_t)olphnext, M_NQMHOST);
+ olphnext = (struct nqm *)0;
+ }
+ if (lphnext) {
+ olphnext = lphnext;
+ i = 0;
+ len = LC_MOREHOSTSIZ;
+ lph = lphnext->lpm_hosts;
+ lphnext = lphnext->lpm_next;
+ } else
+ ok = 0;
+ } else
+ lph++;
+ }
+ FREE((caddr_t)lp, M_NQLEASE);
+ if (olphnext)
+ free((caddr_t)olphnext, M_NQMHOST);
+ nfsstats.srvnqnfs_leases--;
+ }
+ }
+ lp = nextlp;
+ }
+}
+
+/*
+ * Called from nfssvc_nfsd() for a getlease rpc request.
+ * Do the from/to xdr translation and call nqsrv_getlease() to
+ * do the real work.
+ */
+int
+nqnfsrv_getlease(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct nfsv2_fattr *fp;
+ struct vattr va;
+ register struct vattr *vap = &va;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ register u_long *tl;
+ register long t1;
+ u_quad_t frev;
+ caddr_t bpos;
+ int error = 0;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ int flags, rdonly, cache;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
+ flags = fxdr_unsigned(int, *tl++);
+ nfsd->nd_duration = fxdr_unsigned(int, *tl);
+ if (error = nfsrv_fhtovp(fhp,
+ TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ if (rdonly && flags == NQL_WRITE) {
+ error = EROFS;
+ nfsm_reply(0);
+ }
+ (void) nqsrv_getlease(vp, &nfsd->nd_duration, flags, nfsd,
+ nam, &cache, &frev, cred);
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_NQFATTR + 4*NFSX_UNSIGNED);
+ nfsm_build(tl, u_long *, 4*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(cache);
+ *tl++ = txdr_unsigned(nfsd->nd_duration);
+ txdr_hyper(&frev, tl);
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_NQFATTR);
+ nfsm_srvfillattr;
+ nfsm_srvdone;
+}
+
+/*
+ * Called from nfssvc_nfsd() when a "vacated" message is received from a
+ * client. Find the entry and expire it.
+ */
+int
+nqnfsrv_vacated(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct nqlease *lp;
+ register struct nqhost *lph;
+ struct nqlease *tlp = (struct nqlease *)0;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ register u_long *tl;
+ register long t1;
+ struct nqm *lphnext;
+ int error = 0, i, len, ok, gotit = 0;
+ char *cp2;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ m_freem(mrep);
+ /*
+ * Find the lease by searching the hash list.
+ */
+ for (lp = nqfhead[NQFHHASH(fhp->fh_fid.fid_data)]; lp;
+ lp = lp->lc_fhnext)
+ if (fhp->fh_fsid.val[0] == lp->lc_fsid.val[0] &&
+ fhp->fh_fsid.val[1] == lp->lc_fsid.val[1] &&
+ !bcmp(fhp->fh_fid.fid_data, lp->lc_fiddata,
+ MAXFIDSZ)) {
+ /* Found it */
+ tlp = lp;
+ break;
+ }
+ if (tlp) {
+ lp = tlp;
+ len = 1;
+ i = 0;
+ lph = &lp->lc_host;
+ lphnext = lp->lc_morehosts;
+ ok = 1;
+ while (ok && (lph->lph_flag & LC_VALID)) {
+ if (nqsrv_cmpnam(nfsd->nd_slp, nam, lph)) {
+ lph->lph_flag |= LC_VACATED;
+ gotit++;
+ break;
+ }
+ if (++i == len) {
+ if (lphnext) {
+ len = LC_MOREHOSTSIZ;
+ i = 0;
+ lph = lphnext->lpm_hosts;
+ lphnext = lphnext->lpm_next;
+ } else
+ ok = 0;
+ } else
+ lph++;
+ }
+ if ((lp->lc_flag & LC_EXPIREDWANTED) && gotit) {
+ lp->lc_flag &= ~LC_EXPIREDWANTED;
+ wakeup((caddr_t)&lp->lc_flag);
+ }
+nfsmout:
+ return (EPERM);
+ }
+ return (EPERM);
+}
+
+/*
+ * Client get lease rpc function.
+ */
+int
+nqnfs_getlease(vp, rwflag, cred, p)
+ register struct vnode *vp;
+ int rwflag;
+ struct ucred *cred;
+ struct proc *p;
+{
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1;
+ register struct nfsnode *np;
+ struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+ caddr_t bpos, dpos, cp2;
+ time_t reqtime;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ int cachable;
+ u_quad_t frev;
+
+ nfsstats.rpccnt[NQNFSPROC_GETLEASE]++;
+ mb = mreq = nfsm_reqh(vp, NQNFSPROC_GETLEASE, NFSX_FH+2*NFSX_UNSIGNED,
+ &bpos);
+ nfsm_fhtom(vp);
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(rwflag);
+ *tl = txdr_unsigned(nmp->nm_leaseterm);
+ reqtime = time.tv_sec;
+ nfsm_request(vp, NQNFSPROC_GETLEASE, p, cred);
+ np = VTONFS(vp);
+ nfsm_dissect(tl, u_long *, 4*NFSX_UNSIGNED);
+ cachable = fxdr_unsigned(int, *tl++);
+ reqtime += fxdr_unsigned(int, *tl++);
+ if (reqtime > time.tv_sec) {
+ fxdr_hyper(tl, &frev);
+ nqnfs_clientlease(nmp, np, rwflag, cachable, reqtime, frev);
+ nfsm_loadattr(vp, (struct vattr *)0);
+ } else
+ error = NQNFS_EXPIRED;
+ nfsm_reqdone;
+ return (error);
+}
+
+/*
+ * Client vacated message function.
+ */
+int
+nqnfs_vacated(vp, cred)
+ register struct vnode *vp;
+ struct ucred *cred;
+{
+ register caddr_t cp;
+ register struct mbuf *m;
+ register int i;
+ caddr_t bpos;
+ u_long xid;
+ int error = 0;
+ struct mbuf *mreq, *mb, *mb2, *mheadend;
+ struct nfsmount *nmp;
+ struct nfsreq myrep;
+
+ nmp = VFSTONFS(vp->v_mount);
+ nfsstats.rpccnt[NQNFSPROC_VACATED]++;
+ nfsm_reqhead(vp, NQNFSPROC_VACATED, NFSX_FH);
+ nfsm_fhtom(vp);
+ m = mreq;
+ i = 0;
+ while (m) {
+ i += m->m_len;
+ m = m->m_next;
+ }
+ m = nfsm_rpchead(cred, TRUE, NQNFSPROC_VACATED,
+ RPCAUTH_UNIX, 5*NFSX_UNSIGNED, (char *)0,
+ mreq, i, &mheadend, &xid);
+ if (nmp->nm_sotype == SOCK_STREAM) {
+ M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
+ *mtod(m, u_long *) = htonl(0x80000000 | (m->m_pkthdr.len -
+ NFSX_UNSIGNED));
+ }
+ myrep.r_flags = 0;
+ myrep.r_nmp = nmp;
+ if (nmp->nm_soflags & PR_CONNREQUIRED)
+ (void) nfs_sndlock(&nmp->nm_flag, (struct nfsreq *)0);
+ (void) nfs_send(nmp->nm_so, nmp->nm_nam, m, &myrep);
+ if (nmp->nm_soflags & PR_CONNREQUIRED)
+ nfs_sndunlock(&nmp->nm_flag);
+ return (error);
+}
+
+/*
+ * Called for client side callbacks
+ */
+int
+nqnfs_callback(nmp, mrep, md, dpos)
+ struct nfsmount *nmp;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+{
+ register struct vnode *vp;
+ register u_long *tl;
+ register long t1;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct nfsnode *np;
+ struct nfsd nd;
+ int error;
+ char *cp2;
+
+ nd.nd_mrep = mrep;
+ nd.nd_md = md;
+ nd.nd_dpos = dpos;
+ if (error = nfs_getreq(&nd, FALSE))
+ return (error);
+ md = nd.nd_md;
+ dpos = nd.nd_dpos;
+ if (nd.nd_procnum != NQNFSPROC_EVICTED) {
+ m_freem(mrep);
+ return (EPERM);
+ }
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ m_freem(mrep);
+ if (error = nfs_nget(nmp->nm_mountp, fhp, &np))
+ return (error);
+ vp = NFSTOV(np);
+ if (np->n_tnext) {
+ np->n_expiry = 0;
+ np->n_flag |= NQNFSEVICTED;
+ if (np->n_tprev != (struct nfsnode *)nmp) {
+ if (np->n_tnext == (struct nfsnode *)nmp)
+ nmp->nm_tprev = np->n_tprev;
+ else
+ np->n_tnext->n_tprev = np->n_tprev;
+ np->n_tprev->n_tnext = np->n_tnext;
+ np->n_tnext = nmp->nm_tnext;
+ nmp->nm_tnext = np;
+ np->n_tprev = (struct nfsnode *)nmp;
+ if (np->n_tnext == (struct nfsnode *)nmp)
+ nmp->nm_tprev = np;
+ else
+ np->n_tnext->n_tprev = np;
+ }
+ }
+ vrele(vp);
+ nfsm_srvdone;
+}
+
+/*
+ * Nqnfs client helper daemon. Runs once a second to expire leases.
+ * It also get authorization strings for "kerb" mounts.
+ * It must start at the beginning of the list again after any potential
+ * "sleep" since nfs_reclaim() called from vclean() can pull a node off
+ * the list asynchronously.
+ */
+int
+nqnfs_clientd(nmp, cred, ncd, flag, argp, p)
+ register struct nfsmount *nmp;
+ struct ucred *cred;
+ struct nfsd_cargs *ncd;
+ int flag;
+ caddr_t argp;
+ struct proc *p;
+{
+ register struct nfsnode *np;
+ struct vnode *vp;
+ struct nfsreq myrep;
+ int error = 0, vpid;
+
+ /*
+ * First initialize some variables
+ */
+ nqnfs_prog = txdr_unsigned(NQNFS_PROG);
+ nqnfs_vers = txdr_unsigned(NQNFS_VER1);
+
+ /*
+ * If an authorization string is being passed in, get it.
+ */
+ if ((flag & NFSSVC_GOTAUTH) &&
+ (nmp->nm_flag & (NFSMNT_WAITAUTH | NFSMNT_DISMNT)) == 0) {
+ if (nmp->nm_flag & NFSMNT_HASAUTH)
+ panic("cld kerb");
+ if ((flag & NFSSVC_AUTHINFAIL) == 0) {
+ if (ncd->ncd_authlen <= RPCAUTH_MAXSIZ &&
+ copyin(ncd->ncd_authstr, nmp->nm_authstr,
+ ncd->ncd_authlen) == 0) {
+ nmp->nm_authtype = ncd->ncd_authtype;
+ nmp->nm_authlen = ncd->ncd_authlen;
+ } else
+ nmp->nm_flag |= NFSMNT_AUTHERR;
+ } else
+ nmp->nm_flag |= NFSMNT_AUTHERR;
+ nmp->nm_flag |= NFSMNT_HASAUTH;
+ wakeup((caddr_t)&nmp->nm_authlen);
+ } else
+ nmp->nm_flag |= NFSMNT_WAITAUTH;
+
+ /*
+ * Loop every second updating queue until there is a termination sig.
+ */
+ while ((nmp->nm_flag & NFSMNT_DISMNT) == 0) {
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ /*
+ * If there are no outstanding requests (and therefore no
+ * processes in nfs_reply) and there is data in the receive
+ * queue, poke for callbacks.
+ */
+ if (nfsreqh.r_next == &nfsreqh && nmp->nm_so &&
+ nmp->nm_so->so_rcv.sb_cc > 0) {
+ myrep.r_flags = R_GETONEREP;
+ myrep.r_nmp = nmp;
+ myrep.r_mrep = (struct mbuf *)0;
+ myrep.r_procp = (struct proc *)0;
+ (void) nfs_reply(&myrep);
+ }
+
+ /*
+ * Loop through the leases, updating as required.
+ */
+ np = nmp->nm_tnext;
+ while (np != (struct nfsnode *)nmp &&
+ (nmp->nm_flag & NFSMNT_DISMINPROG) == 0) {
+ vp = NFSTOV(np);
+if (vp->v_mount->mnt_stat.f_fsid.val[1] != MOUNT_NFS) panic("trash2");
+ vpid = vp->v_id;
+ if (np->n_expiry < time.tv_sec) {
+ if (vget(vp, 1) == 0) {
+ nmp->nm_inprog = vp;
+ if (vpid == vp->v_id) {
+if (vp->v_mount->mnt_stat.f_fsid.val[1] != MOUNT_NFS) panic("trash3");
+ if (np->n_tnext == (struct nfsnode *)nmp)
+ nmp->nm_tprev = np->n_tprev;
+ else
+ np->n_tnext->n_tprev = np->n_tprev;
+ if (np->n_tprev == (struct nfsnode *)nmp)
+ nmp->nm_tnext = np->n_tnext;
+ else
+ np->n_tprev->n_tnext = np->n_tnext;
+ np->n_tnext = (struct nfsnode *)0;
+ if ((np->n_flag & (NMODIFIED | NQNFSEVICTED))
+ && vp->v_type == VREG) {
+ if (np->n_flag & NQNFSEVICTED) {
+ (void) nfs_vinvalbuf(vp,
+ V_SAVE, cred, p, 0);
+ np->n_flag &= ~NQNFSEVICTED;
+ (void) nqnfs_vacated(vp, cred);
+ } else {
+ (void) VOP_FSYNC(vp, cred,
+ MNT_WAIT, p);
+ np->n_flag &= ~NMODIFIED;
+ }
+ }
+ }
+ vrele(vp);
+ nmp->nm_inprog = NULLVP;
+ }
+ if (np != nmp->nm_tnext)
+ np = nmp->nm_tnext;
+ else
+ break;
+ } else if ((np->n_expiry - NQ_RENEWAL) < time.tv_sec) {
+ if ((np->n_flag & (NQNFSWRITE | NQNFSNONCACHE))
+ == NQNFSWRITE && vp->v_dirtyblkhd.lh_first &&
+ vget(vp, 1) == 0) {
+ nmp->nm_inprog = vp;
+if (vp->v_mount->mnt_stat.f_fsid.val[1] != MOUNT_NFS) panic("trash4");
+ if (vpid == vp->v_id &&
+ nqnfs_getlease(vp, NQL_WRITE, cred, p)==0)
+ np->n_brev = np->n_lrev;
+ vrele(vp);
+ nmp->nm_inprog = NULLVP;
+ }
+ if (np != nmp->nm_tnext)
+ np = nmp->nm_tnext;
+ else
+ break;
+ } else
+ break;
+ }
+ }
+
+ /*
+ * Get an authorization string, if required.
+ */
+ if ((nmp->nm_flag & (NFSMNT_WAITAUTH | NFSMNT_DISMNT | NFSMNT_HASAUTH)) == 0) {
+ ncd->ncd_authuid = nmp->nm_authuid;
+ if (copyout((caddr_t)ncd, argp, sizeof (struct nfsd_cargs)))
+ nmp->nm_flag |= NFSMNT_WAITAUTH;
+ else
+ return (ENEEDAUTH);
+ }
+
+ /*
+ * Wait a bit (no pun) and do it again.
+ */
+ if ((nmp->nm_flag & NFSMNT_DISMNT) == 0 &&
+ (nmp->nm_flag & (NFSMNT_WAITAUTH | NFSMNT_HASAUTH))) {
+ error = tsleep((caddr_t)&nmp->nm_authstr, PSOCK | PCATCH,
+ "nqnfstimr", hz / 3);
+ if (error == EINTR || error == ERESTART)
+ (void) dounmount(nmp->nm_mountp, 0, p);
+ }
+ }
+ free((caddr_t)nmp, M_NFSMNT);
+ if (error == EWOULDBLOCK)
+ error = 0;
+ return (error);
+}
+
+/*
+ * Adjust all timer queue expiry times when the time of day clock is changed.
+ * Called from the settimeofday() syscall.
+ */
+void
+lease_updatetime(deltat)
+ register int deltat;
+{
+ register struct nqlease *lp;
+ register struct nfsnode *np;
+ struct mount *mp;
+ struct nfsmount *nmp;
+ int s;
+
+ if (nqnfsstarttime != 0)
+ nqnfsstarttime += deltat;
+ s = splsoftclock();
+ lp = nqthead.th_chain[0];
+ while (lp != (struct nqlease *)&nqthead) {
+ lp->lc_expiry += deltat;
+ lp = lp->lc_chain1[0];
+ }
+ splx(s);
+
+ /*
+ * Search the mount list for all nqnfs mounts and do their timer
+ * queues.
+ */
+ for (mp = mountlist.tqh_first; mp != NULL; mp = mp->mnt_list.tqe_next) {
+ if (mp->mnt_stat.f_fsid.val[1] == MOUNT_NFS) {
+ nmp = VFSTONFS(mp);
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ np = nmp->nm_tnext;
+ while (np != (struct nfsnode *)nmp) {
+ np->n_expiry += deltat;
+ np = np->n_tnext;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Lock a server lease.
+ */
+void
+nqsrv_locklease(lp)
+ struct nqlease *lp;
+{
+
+ while (lp->lc_flag & LC_LOCKED) {
+ lp->lc_flag |= LC_WANTED;
+ (void) tsleep((caddr_t)lp, PSOCK, "nqlc", 0);
+ }
+ lp->lc_flag |= LC_LOCKED;
+ lp->lc_flag &= ~LC_WANTED;
+}
+
+/*
+ * Unlock a server lease.
+ */
+void
+nqsrv_unlocklease(lp)
+ struct nqlease *lp;
+{
+
+ lp->lc_flag &= ~LC_LOCKED;
+ if (lp->lc_flag & LC_WANTED)
+ wakeup((caddr_t)lp);
+}
+
+/*
+ * Update a client lease.
+ */
+void
+nqnfs_clientlease(nmp, np, rwflag, cachable, expiry, frev)
+ register struct nfsmount *nmp;
+ register struct nfsnode *np;
+ int rwflag, cachable;
+ time_t expiry;
+ u_quad_t frev;
+{
+ register struct nfsnode *tp;
+
+ if (np->n_tnext) {
+ if (np->n_tnext == (struct nfsnode *)nmp)
+ nmp->nm_tprev = np->n_tprev;
+ else
+ np->n_tnext->n_tprev = np->n_tprev;
+ if (np->n_tprev == (struct nfsnode *)nmp)
+ nmp->nm_tnext = np->n_tnext;
+ else
+ np->n_tprev->n_tnext = np->n_tnext;
+ if (rwflag == NQL_WRITE)
+ np->n_flag |= NQNFSWRITE;
+ } else if (rwflag == NQL_READ)
+ np->n_flag &= ~NQNFSWRITE;
+ else
+ np->n_flag |= NQNFSWRITE;
+ if (cachable)
+ np->n_flag &= ~NQNFSNONCACHE;
+ else
+ np->n_flag |= NQNFSNONCACHE;
+ np->n_expiry = expiry;
+ np->n_lrev = frev;
+ tp = nmp->nm_tprev;
+ while (tp != (struct nfsnode *)nmp && tp->n_expiry > np->n_expiry)
+ tp = tp->n_tprev;
+ if (tp == (struct nfsnode *)nmp) {
+ np->n_tnext = nmp->nm_tnext;
+ nmp->nm_tnext = np;
+ } else {
+ np->n_tnext = tp->n_tnext;
+ tp->n_tnext = np;
+ }
+ np->n_tprev = tp;
+ if (np->n_tnext == (struct nfsnode *)nmp)
+ nmp->nm_tprev = np;
+ else
+ np->n_tnext->n_tprev = np;
+}
diff --git a/sys/nfs/nfs_serv.c b/sys/nfs/nfs_serv.c
new file mode 100644
index 0000000..32b0da2
--- /dev/null
+++ b/sys/nfs/nfs_serv.c
@@ -0,0 +1,1929 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_serv.c 8.3 (Berkeley) 1/12/94
+ */
+
+/*
+ * nfs version 2 server calls to vnode ops
+ * - these routines generally have 3 phases
+ * 1 - break down and validate rpc request in mbuf list
+ * 2 - do the vnode ops for the request
+ * (surprisingly ?? many are very similar to syscalls in vfs_syscalls.c)
+ * 3 - build the rpc reply in an mbuf list
+ * nb:
+ * - do not mix the phases, since the nfsm_?? macros can return failures
+ * on a bad rpc or similar and do not do any vrele() or vput()'s
+ *
+ * - the nfsm_reply() macro generates an nfs rpc reply with the nfs
+ * error number iff error != 0 whereas
+ * returning an error from the server function implies a fatal error
+ * such as a badly constructed rpc request that should be dropped without
+ * a reply.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/mbuf.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+
+#include <vm/vm.h>
+
+#include <nfs/nfsv2.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfs.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nqnfs.h>
+
+/* Defs */
+#define TRUE 1
+#define FALSE 0
+
+/* Global vars */
+extern u_long nfs_procids[NFS_NPROCS];
+extern u_long nfs_xdrneg1;
+extern u_long nfs_false, nfs_true;
+nfstype nfs_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFNON,
+ NFCHR, NFNON };
+
+/*
+ * nqnfs access service
+ */
+int
+nqnfsrv_access(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache = 0, mode = 0;
+ char *cp2;
+ struct mbuf *mb, *mreq;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ if (*tl++ == nfs_true)
+ mode |= VREAD;
+ if (*tl++ == nfs_true)
+ mode |= VWRITE;
+ if (*tl == nfs_true)
+ mode |= VEXEC;
+ error = nfsrv_access(vp, mode, cred, rdonly, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(0);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs getattr service
+ */
+int
+nfsrv_getattr(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct nfsv2_fattr *fp;
+ struct vattr va;
+ register struct vattr *vap = &va;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ nqsrv_getl(vp, NQL_READ);
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ nfsm_srvdone;
+}
+
+/*
+ * nfs setattr service
+ */
+int
+nfsrv_setattr(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct vattr va;
+ register struct vattr *vap = &va;
+ register struct nfsv2_sattr *sp;
+ register struct nfsv2_fattr *fp;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ u_quad_t frev, frev2;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_SATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ nqsrv_getl(vp, NQL_WRITE);
+ VATTR_NULL(vap);
+ /*
+ * Nah nah nah nah na nah
+ * There is a bug in the Sun client that puts 0xffff in the mode
+ * field of sattr when it should put in 0xffffffff. The u_short
+ * doesn't sign extend.
+ * --> check the low order 2 bytes for 0xffff
+ */
+ if ((fxdr_unsigned(int, sp->sa_mode) & 0xffff) != 0xffff)
+ vap->va_mode = nfstov_mode(sp->sa_mode);
+ if (sp->sa_uid != nfs_xdrneg1)
+ vap->va_uid = fxdr_unsigned(uid_t, sp->sa_uid);
+ if (sp->sa_gid != nfs_xdrneg1)
+ vap->va_gid = fxdr_unsigned(gid_t, sp->sa_gid);
+ if (nfsd->nd_nqlflag == NQL_NOVAL) {
+ if (sp->sa_nfssize != nfs_xdrneg1)
+ vap->va_size = fxdr_unsigned(u_quad_t, sp->sa_nfssize);
+ if (sp->sa_nfsatime.nfs_sec != nfs_xdrneg1) {
+#ifdef notyet
+ fxdr_nfstime(&sp->sa_nfsatime, &vap->va_atime);
+#else
+ vap->va_atime.ts_sec =
+ fxdr_unsigned(long, sp->sa_nfsatime.nfs_sec);
+ vap->va_atime.ts_nsec = 0;
+#endif
+ }
+ if (sp->sa_nfsmtime.nfs_sec != nfs_xdrneg1)
+ fxdr_nfstime(&sp->sa_nfsmtime, &vap->va_mtime);
+ } else {
+ fxdr_hyper(&sp->sa_nqsize, &vap->va_size);
+ fxdr_nqtime(&sp->sa_nqatime, &vap->va_atime);
+ fxdr_nqtime(&sp->sa_nqmtime, &vap->va_mtime);
+ vap->va_flags = fxdr_unsigned(u_long, sp->sa_nqflags);
+ }
+
+ /*
+ * If the size is being changed write acces is required, otherwise
+ * just check for a read only file system.
+ */
+ if (vap->va_size == ((u_quad_t)((quad_t) -1))) {
+ if (rdonly || (vp->v_mount->mnt_flag & MNT_RDONLY)) {
+ error = EROFS;
+ goto out;
+ }
+ } else {
+ if (vp->v_type == VDIR) {
+ error = EISDIR;
+ goto out;
+ } else if (error = nfsrv_access(vp, VWRITE, cred, rdonly,
+ nfsd->nd_procp))
+ goto out;
+ }
+ if (error = VOP_SETATTR(vp, vap, cred, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+out:
+ vput(vp);
+ nfsm_reply(NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL) + 2*NFSX_UNSIGNED);
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ if (nfsd->nd_nqlflag != NQL_NOVAL) {
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ txdr_hyper(&frev2, tl);
+ }
+ nfsm_srvdone;
+}
+
+/*
+ * nfs lookup rpc
+ */
+int
+nfsrv_lookup(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct nfsv2_fattr *fp;
+ struct nameidata nd;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ register caddr_t cp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, cache, duration2, cache2, len;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct vattr va, *vap = &va;
+ u_quad_t frev, frev2;
+
+ fhp = &nfh.fh_generic;
+ duration2 = 0;
+ if (nfsd->nd_nqlflag != NQL_NOVAL) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ duration2 = fxdr_unsigned(int, *tl);
+ }
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = LOOKUP;
+ nd.ni_cnd.cn_flags = LOCKLEAF | SAVESTART;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ nfsm_reply(0);
+ nqsrv_getl(nd.ni_startdir, NQL_READ);
+ vrele(nd.ni_startdir);
+ FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ vp = nd.ni_vp;
+ bzero((caddr_t)fhp, sizeof(nfh));
+ fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+ if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ if (duration2)
+ (void) nqsrv_getlease(vp, &duration2, NQL_READ, nfsd,
+ nam, &cache2, &frev2, cred);
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_FH + NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL) + 5*NFSX_UNSIGNED);
+ if (nfsd->nd_nqlflag != NQL_NOVAL) {
+ if (duration2) {
+ nfsm_build(tl, u_long *, 5*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(NQL_READ);
+ *tl++ = txdr_unsigned(cache2);
+ *tl++ = txdr_unsigned(duration2);
+ txdr_hyper(&frev2, tl);
+ } else {
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = 0;
+ }
+ }
+ nfsm_srvfhtom(fhp);
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ nfsm_srvdone;
+}
+
+/*
+ * nfs readlink service
+ */
+int
+nfsrv_readlink(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct iovec iv[(NFS_MAXPATHLEN+MLEN-1)/MLEN];
+ register struct iovec *ivp = iv;
+ register struct mbuf *mp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache, i, tlen, len;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mp2 = 0, *mp3 = 0, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct uio io, *uiop = &io;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ len = 0;
+ i = 0;
+ while (len < NFS_MAXPATHLEN) {
+ MGET(mp, M_WAIT, MT_DATA);
+ MCLGET(mp, M_WAIT);
+ mp->m_len = NFSMSIZ(mp);
+ if (len == 0)
+ mp3 = mp2 = mp;
+ else {
+ mp2->m_next = mp;
+ mp2 = mp;
+ }
+ if ((len+mp->m_len) > NFS_MAXPATHLEN) {
+ mp->m_len = NFS_MAXPATHLEN-len;
+ len = NFS_MAXPATHLEN;
+ } else
+ len += mp->m_len;
+ ivp->iov_base = mtod(mp, caddr_t);
+ ivp->iov_len = mp->m_len;
+ i++;
+ ivp++;
+ }
+ uiop->uio_iov = iv;
+ uiop->uio_iovcnt = i;
+ uiop->uio_offset = 0;
+ uiop->uio_resid = len;
+ uiop->uio_rw = UIO_READ;
+ uiop->uio_segflg = UIO_SYSSPACE;
+ uiop->uio_procp = (struct proc *)0;
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly)) {
+ m_freem(mp3);
+ nfsm_reply(0);
+ }
+ if (vp->v_type != VLNK) {
+ error = EINVAL;
+ goto out;
+ }
+ nqsrv_getl(vp, NQL_READ);
+ error = VOP_READLINK(vp, uiop, cred);
+out:
+ vput(vp);
+ if (error)
+ m_freem(mp3);
+ nfsm_reply(NFSX_UNSIGNED);
+ if (uiop->uio_resid > 0) {
+ len -= uiop->uio_resid;
+ tlen = nfsm_rndup(len);
+ nfsm_adj(mp3, NFS_MAXPATHLEN-tlen, tlen-len);
+ }
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(len);
+ mb->m_next = mp3;
+ nfsm_srvdone;
+}
+
+/*
+ * nfs read service
+ */
+int
+nfsrv_read(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct iovec *iv;
+ struct iovec *iv2;
+ register struct mbuf *m;
+ register struct nfsv2_fattr *fp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache, i, cnt, len, left, siz, tlen;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct mbuf *m2;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct uio io, *uiop = &io;
+ struct vattr va, *vap = &va;
+ off_t off;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ if (nfsd->nd_nqlflag == NQL_NOVAL) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ off = (off_t)fxdr_unsigned(u_long, *tl);
+ } else {
+ nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+ fxdr_hyper(tl, &off);
+ }
+ nfsm_srvstrsiz(cnt, NFS_MAXDATA);
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ if (vp->v_type != VREG) {
+ error = (vp->v_type == VDIR) ? EISDIR : EACCES;
+ vput(vp);
+ nfsm_reply(0);
+ }
+ nqsrv_getl(vp, NQL_READ);
+ if ((error = nfsrv_access(vp, VREAD, cred, rdonly, nfsd->nd_procp)) &&
+ (error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp))) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ if (error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ if (off >= vap->va_size)
+ cnt = 0;
+ else if ((off + cnt) > vap->va_size)
+ cnt = nfsm_rndup(vap->va_size - off);
+ nfsm_reply(NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL)+NFSX_UNSIGNED+nfsm_rndup(cnt));
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ len = left = cnt;
+ if (cnt > 0) {
+ /*
+ * Generate the mbuf list with the uio_iov ref. to it.
+ */
+ i = 0;
+ m = m2 = mb;
+ MALLOC(iv, struct iovec *,
+ ((NFS_MAXDATA+MLEN-1)/MLEN) * sizeof (struct iovec),
+ M_TEMP, M_WAITOK);
+ iv2 = iv;
+ while (left > 0) {
+ siz = min(M_TRAILINGSPACE(m), left);
+ if (siz > 0) {
+ m->m_len += siz;
+ iv->iov_base = bpos;
+ iv->iov_len = siz;
+ iv++;
+ i++;
+ left -= siz;
+ }
+ if (left > 0) {
+ MGET(m, M_WAIT, MT_DATA);
+ MCLGET(m, M_WAIT);
+ m->m_len = 0;
+ m2->m_next = m;
+ m2 = m;
+ bpos = mtod(m, caddr_t);
+ }
+ }
+ uiop->uio_iov = iv2;
+ uiop->uio_iovcnt = i;
+ uiop->uio_offset = off;
+ uiop->uio_resid = cnt;
+ uiop->uio_rw = UIO_READ;
+ uiop->uio_segflg = UIO_SYSSPACE;
+ error = VOP_READ(vp, uiop, IO_NODELOCKED, cred);
+ off = uiop->uio_offset;
+ FREE((caddr_t)iv2, M_TEMP);
+ if (error || (error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp))) {
+ m_freem(mreq);
+ vput(vp);
+ nfsm_reply(0);
+ }
+ } else
+ uiop->uio_resid = 0;
+ vput(vp);
+ nfsm_srvfillattr;
+ len -= uiop->uio_resid;
+ tlen = nfsm_rndup(len);
+ if (cnt != tlen || tlen != len)
+ nfsm_adj(mb, cnt-tlen, tlen-len);
+ *tl = txdr_unsigned(len);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs write service
+ */
+int
+nfsrv_write(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct iovec *ivp;
+ register struct mbuf *mp;
+ register struct nfsv2_fattr *fp;
+ struct iovec iv[NFS_MAXIOVEC];
+ struct vattr va;
+ register struct vattr *vap = &va;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache, siz, len, xfer;
+ int ioflags = IO_SYNC | IO_NODELOCKED;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct uio io, *uiop = &io;
+ off_t off;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED);
+ if (nfsd->nd_nqlflag == NQL_NOVAL) {
+ off = (off_t)fxdr_unsigned(u_long, *++tl);
+ tl += 2;
+ } else {
+ fxdr_hyper(tl, &off);
+ tl += 2;
+ if (fxdr_unsigned(u_long, *tl++))
+ ioflags |= IO_APPEND;
+ }
+ len = fxdr_unsigned(long, *tl);
+ if (len > NFS_MAXDATA || len <= 0) {
+ error = EBADRPC;
+ nfsm_reply(0);
+ }
+ if (dpos == (mtod(md, caddr_t)+md->m_len)) {
+ mp = md->m_next;
+ if (mp == NULL) {
+ error = EBADRPC;
+ nfsm_reply(0);
+ }
+ } else {
+ mp = md;
+ siz = dpos-mtod(mp, caddr_t);
+ mp->m_len -= siz;
+ NFSMADV(mp, siz);
+ }
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ if (vp->v_type != VREG) {
+ error = (vp->v_type == VDIR) ? EISDIR : EACCES;
+ vput(vp);
+ nfsm_reply(0);
+ }
+ nqsrv_getl(vp, NQL_WRITE);
+ if (error = nfsrv_access(vp, VWRITE, cred, rdonly, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ uiop->uio_resid = 0;
+ uiop->uio_rw = UIO_WRITE;
+ uiop->uio_segflg = UIO_SYSSPACE;
+ uiop->uio_procp = (struct proc *)0;
+ /*
+ * Do up to NFS_MAXIOVEC mbufs of write each iteration of the
+ * loop until done.
+ */
+ while (len > 0 && uiop->uio_resid == 0) {
+ ivp = iv;
+ siz = 0;
+ uiop->uio_iov = ivp;
+ uiop->uio_iovcnt = 0;
+ uiop->uio_offset = off;
+ while (len > 0 && uiop->uio_iovcnt < NFS_MAXIOVEC && mp != NULL) {
+ ivp->iov_base = mtod(mp, caddr_t);
+ if (len < mp->m_len)
+ ivp->iov_len = xfer = len;
+ else
+ ivp->iov_len = xfer = mp->m_len;
+#ifdef notdef
+ /* Not Yet .. */
+ if (M_HASCL(mp) && (((u_long)ivp->iov_base) & CLOFSET) == 0)
+ ivp->iov_op = NULL; /* what should it be ?? */
+ else
+ ivp->iov_op = NULL;
+#endif
+ uiop->uio_iovcnt++;
+ ivp++;
+ len -= xfer;
+ siz += xfer;
+ mp = mp->m_next;
+ }
+ if (len > 0 && mp == NULL) {
+ error = EBADRPC;
+ vput(vp);
+ nfsm_reply(0);
+ }
+ uiop->uio_resid = siz;
+ if (error = VOP_WRITE(vp, uiop, ioflags, cred)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ off = uiop->uio_offset;
+ }
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ if (nfsd->nd_nqlflag != NQL_NOVAL) {
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ txdr_hyper(&vap->va_filerev, tl);
+ }
+ nfsm_srvdone;
+}
+
+/*
+ * nfs create service
+ * now does a truncate to 0 length via. setattr if it already exists
+ */
+int
+nfsrv_create(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct nfsv2_fattr *fp;
+ struct vattr va;
+ register struct vattr *vap = &va;
+ register struct nfsv2_sattr *sp;
+ register u_long *tl;
+ struct nameidata nd;
+ register caddr_t cp;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdev, cache, len, tsize;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ u_quad_t frev;
+
+ nd.ni_cnd.cn_nameiop = 0;
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = CREATE;
+ nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | SAVESTART;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ nfsm_reply(0);
+ VATTR_NULL(vap);
+ nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_SATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ /*
+ * Iff doesn't exist, create it
+ * otherwise just truncate to 0 length
+ * should I set the mode too ??
+ */
+ if (nd.ni_vp == NULL) {
+ vap->va_type = IFTOVT(fxdr_unsigned(u_long, sp->sa_mode));
+ if (vap->va_type == VNON)
+ vap->va_type = VREG;
+ vap->va_mode = nfstov_mode(sp->sa_mode);
+ if (nfsd->nd_nqlflag == NQL_NOVAL)
+ rdev = fxdr_unsigned(long, sp->sa_nfssize);
+ else
+ rdev = fxdr_unsigned(long, sp->sa_nqrdev);
+ if (vap->va_type == VREG || vap->va_type == VSOCK) {
+ vrele(nd.ni_startdir);
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ if (error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap))
+ nfsm_reply(0);
+ FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ } else if (vap->va_type == VCHR || vap->va_type == VBLK ||
+ vap->va_type == VFIFO) {
+ if (vap->va_type == VCHR && rdev == 0xffffffff)
+ vap->va_type = VFIFO;
+ if (vap->va_type == VFIFO) {
+#ifndef FIFO
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ vput(nd.ni_dvp);
+ error = ENXIO;
+ goto out;
+#endif /* FIFO */
+ } else if (error = suser(cred, (u_short *)0)) {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ vput(nd.ni_dvp);
+ goto out;
+ } else
+ vap->va_rdev = (dev_t)rdev;
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ if (error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap)) {
+ vrele(nd.ni_startdir);
+ nfsm_reply(0);
+ }
+ nd.ni_cnd.cn_nameiop = LOOKUP;
+ nd.ni_cnd.cn_flags &= ~(LOCKPARENT | SAVESTART);
+ nd.ni_cnd.cn_proc = nfsd->nd_procp;
+ nd.ni_cnd.cn_cred = nfsd->nd_procp->p_ucred;
+ if (error = lookup(&nd)) {
+ free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ nfsm_reply(0);
+ }
+ FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ if (nd.ni_cnd.cn_flags & ISSYMLINK) {
+ vrele(nd.ni_dvp);
+ vput(nd.ni_vp);
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ error = EINVAL;
+ nfsm_reply(0);
+ }
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ vput(nd.ni_dvp);
+ error = ENXIO;
+ goto out;
+ }
+ vp = nd.ni_vp;
+ } else {
+ vrele(nd.ni_startdir);
+ free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ vp = nd.ni_vp;
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nfsd->nd_nqlflag == NQL_NOVAL) {
+ tsize = fxdr_unsigned(long, sp->sa_nfssize);
+ if (tsize != -1)
+ vap->va_size = (u_quad_t)tsize;
+ else
+ vap->va_size = -1;
+ } else
+ fxdr_hyper(&sp->sa_nqsize, &vap->va_size);
+ if (vap->va_size != -1) {
+ if (error = nfsrv_access(vp, VWRITE, cred,
+ (nd.ni_cnd.cn_flags & RDONLY), nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ nqsrv_getl(vp, NQL_WRITE);
+ if (error = VOP_SETATTR(vp, vap, cred, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ }
+ }
+ bzero((caddr_t)fhp, sizeof(nfh));
+ fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+ if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_FH+NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfhtom(fhp);
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ return (error);
+nfsmout:
+ if (nd.ni_cnd.cn_nameiop || nd.ni_cnd.cn_flags)
+ vrele(nd.ni_startdir);
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ if (nd.ni_vp)
+ vput(nd.ni_vp);
+ return (error);
+
+out:
+ vrele(nd.ni_startdir);
+ free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ nfsm_reply(0);
+ return (0);
+}
+
+/*
+ * nfs remove service
+ */
+int
+nfsrv_remove(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct nameidata nd;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, cache, len;
+ char *cp2;
+ struct mbuf *mb, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = DELETE;
+ nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ nfsm_reply(0);
+ vp = nd.ni_vp;
+ if (vp->v_type == VDIR &&
+ (error = suser(cred, (u_short *)0)))
+ goto out;
+ /*
+ * The root of a mounted filesystem cannot be deleted.
+ */
+ if (vp->v_flag & VROOT) {
+ error = EBUSY;
+ goto out;
+ }
+ if (vp->v_flag & VTEXT)
+ (void) vnode_pager_uncache(vp);
+out:
+ if (!error) {
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ nqsrv_getl(vp, NQL_WRITE);
+ error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vput(vp);
+ }
+ nfsm_reply(0);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs rename service
+ */
+int
+nfsrv_rename(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, cache, len, len2;
+ char *cp2;
+ struct mbuf *mb, *mreq;
+ struct nameidata fromnd, tond;
+ struct vnode *fvp = 0, *tvp, *tdvp;
+ nfsv2fh_t fnfh, tnfh;
+ fhandle_t *ffhp, *tfhp;
+ u_quad_t frev;
+ uid_t saved_uid;
+
+ ffhp = &fnfh.fh_generic;
+ tfhp = &tnfh.fh_generic;
+ fromnd.ni_cnd.cn_nameiop = 0;
+ tond.ni_cnd.cn_nameiop = 0;
+ nfsm_srvmtofh(ffhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ /*
+ * Remember our original uid so that we can reset cr_uid before
+ * the second nfs_namei() call, in case it is remapped.
+ */
+ saved_uid = cred->cr_uid;
+ fromnd.ni_cnd.cn_cred = cred;
+ fromnd.ni_cnd.cn_nameiop = DELETE;
+ fromnd.ni_cnd.cn_flags = WANTPARENT | SAVESTART;
+ if (error = nfs_namei(&fromnd, ffhp, len, nfsd->nd_slp, nam, &md,
+ &dpos, nfsd->nd_procp))
+ nfsm_reply(0);
+ fvp = fromnd.ni_vp;
+ nfsm_srvmtofh(tfhp);
+ nfsm_strsiz(len2, NFS_MAXNAMLEN);
+ cred->cr_uid = saved_uid;
+ tond.ni_cnd.cn_cred = cred;
+ tond.ni_cnd.cn_nameiop = RENAME;
+ tond.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART;
+ if (error = nfs_namei(&tond, tfhp, len2, nfsd->nd_slp, nam, &md,
+ &dpos, nfsd->nd_procp)) {
+ VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
+ vrele(fromnd.ni_dvp);
+ vrele(fvp);
+ goto out1;
+ }
+ tdvp = tond.ni_dvp;
+ tvp = tond.ni_vp;
+ if (tvp != NULL) {
+ if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
+ error = EISDIR;
+ goto out;
+ } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) {
+ error = ENOTDIR;
+ goto out;
+ }
+ if (tvp->v_type == VDIR && tvp->v_mountedhere) {
+ error = EXDEV;
+ goto out;
+ }
+ }
+ if (fvp->v_type == VDIR && fvp->v_mountedhere) {
+ error = EBUSY;
+ goto out;
+ }
+ if (fvp->v_mount != tdvp->v_mount) {
+ error = EXDEV;
+ goto out;
+ }
+ if (fvp == tdvp)
+ error = EINVAL;
+ /*
+ * If source is the same as the destination (that is the
+ * same vnode with the same name in the same directory),
+ * then there is nothing to do.
+ */
+ if (fvp == tvp && fromnd.ni_dvp == tdvp &&
+ fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen &&
+ !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr,
+ fromnd.ni_cnd.cn_namelen))
+ error = -1;
+out:
+ if (!error) {
+ nqsrv_getl(fromnd.ni_dvp, NQL_WRITE);
+ nqsrv_getl(tdvp, NQL_WRITE);
+ if (tvp)
+ nqsrv_getl(tvp, NQL_WRITE);
+ error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd,
+ tond.ni_dvp, tond.ni_vp, &tond.ni_cnd);
+ } else {
+ VOP_ABORTOP(tond.ni_dvp, &tond.ni_cnd);
+ if (tdvp == tvp)
+ vrele(tdvp);
+ else
+ vput(tdvp);
+ if (tvp)
+ vput(tvp);
+ VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
+ vrele(fromnd.ni_dvp);
+ vrele(fvp);
+ }
+ vrele(tond.ni_startdir);
+ FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI);
+out1:
+ vrele(fromnd.ni_startdir);
+ FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI);
+ nfsm_reply(0);
+ return (error);
+
+nfsmout:
+ if (tond.ni_cnd.cn_nameiop || tond.ni_cnd.cn_flags) {
+ vrele(tond.ni_startdir);
+ FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI);
+ }
+ if (fromnd.ni_cnd.cn_nameiop || fromnd.ni_cnd.cn_flags) {
+ vrele(fromnd.ni_startdir);
+ FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI);
+ VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
+ vrele(fromnd.ni_dvp);
+ vrele(fvp);
+ }
+ return (error);
+}
+
+/*
+ * nfs link service
+ */
+int
+nfsrv_link(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct nameidata nd;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache, len;
+ char *cp2;
+ struct mbuf *mb, *mreq;
+ struct vnode *vp, *xp;
+ nfsv2fh_t nfh, dnfh;
+ fhandle_t *fhp, *dfhp;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ dfhp = &dnfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvmtofh(dfhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ if (error = nfsrv_fhtovp(fhp, FALSE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ if (vp->v_type == VDIR && (error = suser(cred, (u_short *)0)))
+ goto out1;
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = CREATE;
+ nd.ni_cnd.cn_flags = LOCKPARENT;
+ if (error = nfs_namei(&nd, dfhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ goto out1;
+ xp = nd.ni_vp;
+ if (xp != NULL) {
+ error = EEXIST;
+ goto out;
+ }
+ xp = nd.ni_dvp;
+ if (vp->v_mount != xp->v_mount)
+ error = EXDEV;
+out:
+ if (!error) {
+ nqsrv_getl(vp, NQL_WRITE);
+ nqsrv_getl(xp, NQL_WRITE);
+ error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ if (nd.ni_vp)
+ vrele(nd.ni_vp);
+ }
+out1:
+ vrele(vp);
+ nfsm_reply(0);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs symbolic link service
+ */
+int
+nfsrv_symlink(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct vattr va;
+ struct nameidata nd;
+ register struct vattr *vap = &va;
+ register u_long *tl;
+ register long t1;
+ struct nfsv2_sattr *sp;
+ caddr_t bpos;
+ struct uio io;
+ struct iovec iv;
+ int error = 0, cache, len, len2;
+ char *pathcp, *cp2;
+ struct mbuf *mb, *mreq;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ u_quad_t frev;
+
+ pathcp = (char *)0;
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = CREATE;
+ nd.ni_cnd.cn_flags = LOCKPARENT;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ goto out;
+ nfsm_strsiz(len2, NFS_MAXPATHLEN);
+ MALLOC(pathcp, caddr_t, len2 + 1, M_TEMP, M_WAITOK);
+ iv.iov_base = pathcp;
+ iv.iov_len = len2;
+ io.uio_resid = len2;
+ io.uio_offset = 0;
+ io.uio_iov = &iv;
+ io.uio_iovcnt = 1;
+ io.uio_segflg = UIO_SYSSPACE;
+ io.uio_rw = UIO_READ;
+ io.uio_procp = (struct proc *)0;
+ nfsm_mtouio(&io, len2);
+ nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_SATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ *(pathcp + len2) = '\0';
+ if (nd.ni_vp) {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vrele(nd.ni_vp);
+ error = EEXIST;
+ goto out;
+ }
+ VATTR_NULL(vap);
+ vap->va_mode = fxdr_unsigned(u_short, sp->sa_mode);
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap, pathcp);
+out:
+ if (pathcp)
+ FREE(pathcp, M_TEMP);
+ nfsm_reply(0);
+ return (error);
+nfsmout:
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ if (nd.ni_vp)
+ vrele(nd.ni_vp);
+ if (pathcp)
+ FREE(pathcp, M_TEMP);
+ return (error);
+}
+
+/*
+ * nfs mkdir service
+ */
+int
+nfsrv_mkdir(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct vattr va;
+ register struct vattr *vap = &va;
+ register struct nfsv2_fattr *fp;
+ struct nameidata nd;
+ register caddr_t cp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, cache, len;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = CREATE;
+ nd.ni_cnd.cn_flags = LOCKPARENT;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ nfsm_reply(0);
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ VATTR_NULL(vap);
+ vap->va_type = VDIR;
+ vap->va_mode = nfstov_mode(*tl++);
+ vp = nd.ni_vp;
+ if (vp != NULL) {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vrele(vp);
+ error = EEXIST;
+ nfsm_reply(0);
+ }
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ if (error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap))
+ nfsm_reply(0);
+ vp = nd.ni_vp;
+ bzero((caddr_t)fhp, sizeof(nfh));
+ fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+ if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_FH+NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfhtom(fhp);
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ return (error);
+nfsmout:
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ if (nd.ni_vp)
+ vrele(nd.ni_vp);
+ return (error);
+}
+
+/*
+ * nfs rmdir service
+ */
+int
+nfsrv_rmdir(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, cache, len;
+ char *cp2;
+ struct mbuf *mb, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct nameidata nd;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = DELETE;
+ nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ nfsm_reply(0);
+ vp = nd.ni_vp;
+ if (vp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto out;
+ }
+ /*
+ * No rmdir "." please.
+ */
+ if (nd.ni_dvp == vp) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * The root of a mounted filesystem cannot be deleted.
+ */
+ if (vp->v_flag & VROOT)
+ error = EBUSY;
+out:
+ if (!error) {
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ nqsrv_getl(vp, NQL_WRITE);
+ error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vput(vp);
+ }
+ nfsm_reply(0);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs readdir service
+ * - mallocs what it thinks is enough to read
+ * count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR
+ * - calls VOP_READDIR()
+ * - loops around building the reply
+ * if the output generated exceeds count break out of loop
+ * The nfsm_clget macro is used here so that the reply will be packed
+ * tightly in mbuf clusters.
+ * - it only knows that it has encountered eof when the VOP_READDIR()
+ * reads nothing
+ * - as such one readdir rpc will return eof false although you are there
+ * and then the next will return eof
+ * - it trims out records with d_fileno == 0
+ * this doesn't matter for Unix clients, but they might confuse clients
+ * for other os'.
+ * NB: It is tempting to set eof to true if the VOP_READDIR() reads less
+ * than requested, but this may not apply to all filesystems. For
+ * example, client NFS does not { although it is never remote mounted
+ * anyhow }
+ * The alternate call nqnfsrv_readdirlook() does lookups as well.
+ * PS: The NFS protocol spec. does not clarify what the "count" byte
+ * argument is a count of.. just name strings and file id's or the
+ * entire reply rpc or ...
+ * I tried just file name and id sizes and it confused the Sun client,
+ * so I am using the full rpc size now. The "paranoia.." comment refers
+ * to including the status longwords that are not a part of the dir.
+ * "entry" structures, but are in the rpc.
+ */
+struct flrep {
+ u_long fl_cachable;
+ u_long fl_duration;
+ u_long fl_frev[2];
+ nfsv2fh_t fl_nfh;
+ u_long fl_fattr[NFSX_NQFATTR / sizeof (u_long)];
+};
+
+int
+nfsrv_readdir(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register char *bp, *be;
+ register struct mbuf *mp;
+ register struct dirent *dp;
+ register caddr_t cp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ struct mbuf *mb, *mb2, *mreq, *mp2;
+ char *cpos, *cend, *cp2, *rbuf;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct uio io;
+ struct iovec iv;
+ int len, nlen, rem, xfer, tsiz, i, error = 0;
+ int siz, cnt, fullsiz, eofflag, rdonly, cache;
+ u_quad_t frev;
+ u_long on, off, toff;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
+ toff = fxdr_unsigned(u_long, *tl++);
+ off = (toff & ~(NFS_DIRBLKSIZ-1));
+ on = (toff & (NFS_DIRBLKSIZ-1));
+ cnt = fxdr_unsigned(int, *tl);
+ siz = ((cnt+NFS_DIRBLKSIZ-1) & ~(NFS_DIRBLKSIZ-1));
+ if (cnt > NFS_MAXREADDIR)
+ siz = NFS_MAXREADDIR;
+ fullsiz = siz;
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ nqsrv_getl(vp, NQL_READ);
+ if (error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ VOP_UNLOCK(vp);
+ MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
+again:
+ iv.iov_base = rbuf;
+ iv.iov_len = fullsiz;
+ io.uio_iov = &iv;
+ io.uio_iovcnt = 1;
+ io.uio_offset = (off_t)off;
+ io.uio_resid = fullsiz;
+ io.uio_segflg = UIO_SYSSPACE;
+ io.uio_rw = UIO_READ;
+ io.uio_procp = (struct proc *)0;
+ error = VOP_READDIR(vp, &io, cred);
+ off = (off_t)io.uio_offset;
+ if (error) {
+ vrele(vp);
+ free((caddr_t)rbuf, M_TEMP);
+ nfsm_reply(0);
+ }
+ if (io.uio_resid < fullsiz)
+ eofflag = 0;
+ else
+ eofflag = 1;
+ if (io.uio_resid) {
+ siz -= io.uio_resid;
+
+ /*
+ * If nothing read, return eof
+ * rpc reply
+ */
+ if (siz == 0) {
+ vrele(vp);
+ nfsm_reply(2*NFSX_UNSIGNED);
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = nfs_false;
+ *tl = nfs_true;
+ FREE((caddr_t)rbuf, M_TEMP);
+ return (0);
+ }
+ }
+
+ /*
+ * Check for degenerate cases of nothing useful read.
+ * If so go try again
+ */
+ cpos = rbuf + on;
+ cend = rbuf + siz;
+ dp = (struct dirent *)cpos;
+ while (cpos < cend && dp->d_fileno == 0) {
+ cpos += dp->d_reclen;
+ dp = (struct dirent *)cpos;
+ }
+ if (cpos >= cend) {
+ toff = off;
+ siz = fullsiz;
+ on = 0;
+ goto again;
+ }
+
+ cpos = rbuf + on;
+ cend = rbuf + siz;
+ dp = (struct dirent *)cpos;
+ len = 3*NFSX_UNSIGNED; /* paranoia, probably can be 0 */
+ nfsm_reply(siz);
+ mp = mp2 = mb;
+ bp = bpos;
+ be = bp + M_TRAILINGSPACE(mp);
+
+ /* Loop through the records and build reply */
+ while (cpos < cend) {
+ if (dp->d_fileno != 0) {
+ nlen = dp->d_namlen;
+ rem = nfsm_rndup(nlen)-nlen;
+ len += (4*NFSX_UNSIGNED + nlen + rem);
+ if (len > cnt) {
+ eofflag = 0;
+ break;
+ }
+ /*
+ * Build the directory record xdr from
+ * the dirent entry.
+ */
+ nfsm_clget;
+ *tl = nfs_true;
+ bp += NFSX_UNSIGNED;
+ nfsm_clget;
+ *tl = txdr_unsigned(dp->d_fileno);
+ bp += NFSX_UNSIGNED;
+ nfsm_clget;
+ *tl = txdr_unsigned(nlen);
+ bp += NFSX_UNSIGNED;
+
+ /* And loop around copying the name */
+ xfer = nlen;
+ cp = dp->d_name;
+ while (xfer > 0) {
+ nfsm_clget;
+ if ((bp+xfer) > be)
+ tsiz = be-bp;
+ else
+ tsiz = xfer;
+ bcopy(cp, bp, tsiz);
+ bp += tsiz;
+ xfer -= tsiz;
+ if (xfer > 0)
+ cp += tsiz;
+ }
+ /* And null pad to a long boundary */
+ for (i = 0; i < rem; i++)
+ *bp++ = '\0';
+ nfsm_clget;
+
+ /* Finish off the record */
+ toff += dp->d_reclen;
+ *tl = txdr_unsigned(toff);
+ bp += NFSX_UNSIGNED;
+ } else
+ toff += dp->d_reclen;
+ cpos += dp->d_reclen;
+ dp = (struct dirent *)cpos;
+ }
+ vrele(vp);
+ nfsm_clget;
+ *tl = nfs_false;
+ bp += NFSX_UNSIGNED;
+ nfsm_clget;
+ if (eofflag)
+ *tl = nfs_true;
+ else
+ *tl = nfs_false;
+ bp += NFSX_UNSIGNED;
+ if (mp != mb) {
+ if (bp < be)
+ mp->m_len = bp - mtod(mp, caddr_t);
+ } else
+ mp->m_len += bp - bpos;
+ FREE(rbuf, M_TEMP);
+ nfsm_srvdone;
+}
+
+int
+nqnfsrv_readdirlook(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register char *bp, *be;
+ register struct mbuf *mp;
+ register struct dirent *dp;
+ register caddr_t cp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ struct mbuf *mb, *mb2, *mreq, *mp2;
+ char *cpos, *cend, *cp2, *rbuf;
+ struct vnode *vp, *nvp;
+ struct flrep fl;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct uio io;
+ struct iovec iv;
+ struct vattr va, *vap = &va;
+ struct nfsv2_fattr *fp;
+ int len, nlen, rem, xfer, tsiz, i, error = 0, duration2, cache2;
+ int siz, cnt, fullsiz, eofflag, rdonly, cache;
+ u_quad_t frev, frev2;
+ u_long on, off, toff;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED);
+ toff = fxdr_unsigned(u_long, *tl++);
+ off = (toff & ~(NFS_DIRBLKSIZ-1));
+ on = (toff & (NFS_DIRBLKSIZ-1));
+ cnt = fxdr_unsigned(int, *tl++);
+ duration2 = fxdr_unsigned(int, *tl);
+ siz = ((cnt+NFS_DIRBLKSIZ-1) & ~(NFS_DIRBLKSIZ-1));
+ if (cnt > NFS_MAXREADDIR)
+ siz = NFS_MAXREADDIR;
+ fullsiz = siz;
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ nqsrv_getl(vp, NQL_READ);
+ if (error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ VOP_UNLOCK(vp);
+ MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
+again:
+ iv.iov_base = rbuf;
+ iv.iov_len = fullsiz;
+ io.uio_iov = &iv;
+ io.uio_iovcnt = 1;
+ io.uio_offset = (off_t)off;
+ io.uio_resid = fullsiz;
+ io.uio_segflg = UIO_SYSSPACE;
+ io.uio_rw = UIO_READ;
+ io.uio_procp = (struct proc *)0;
+ error = VOP_READDIR(vp, &io, cred);
+ off = (u_long)io.uio_offset;
+ if (error) {
+ vrele(vp);
+ free((caddr_t)rbuf, M_TEMP);
+ nfsm_reply(0);
+ }
+ if (io.uio_resid < fullsiz)
+ eofflag = 0;
+ else
+ eofflag = 1;
+ if (io.uio_resid) {
+ siz -= io.uio_resid;
+
+ /*
+ * If nothing read, return eof
+ * rpc reply
+ */
+ if (siz == 0) {
+ vrele(vp);
+ nfsm_reply(2 * NFSX_UNSIGNED);
+ nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
+ *tl++ = nfs_false;
+ *tl = nfs_true;
+ FREE((caddr_t)rbuf, M_TEMP);
+ return (0);
+ }
+ }
+
+ /*
+ * Check for degenerate cases of nothing useful read.
+ * If so go try again
+ */
+ cpos = rbuf + on;
+ cend = rbuf + siz;
+ dp = (struct dirent *)cpos;
+ while (cpos < cend && dp->d_fileno == 0) {
+ cpos += dp->d_reclen;
+ dp = (struct dirent *)cpos;
+ }
+ if (cpos >= cend) {
+ toff = off;
+ siz = fullsiz;
+ on = 0;
+ goto again;
+ }
+
+ cpos = rbuf + on;
+ cend = rbuf + siz;
+ dp = (struct dirent *)cpos;
+ len = 3 * NFSX_UNSIGNED; /* paranoia, probably can be 0 */
+ nfsm_reply(siz);
+ mp = mp2 = mb;
+ bp = bpos;
+ be = bp + M_TRAILINGSPACE(mp);
+
+ /* Loop through the records and build reply */
+ while (cpos < cend) {
+ if (dp->d_fileno != 0) {
+ nlen = dp->d_namlen;
+ rem = nfsm_rndup(nlen)-nlen;
+
+ /*
+ * For readdir_and_lookup get the vnode using
+ * the file number.
+ */
+ if (VFS_VGET(vp->v_mount, dp->d_fileno, &nvp))
+ goto invalid;
+ bzero((caddr_t)&fl.fl_nfh, sizeof (nfsv2fh_t));
+ fl.fl_nfh.fh_generic.fh_fsid =
+ nvp->v_mount->mnt_stat.f_fsid;
+ if (VFS_VPTOFH(nvp, &fl.fl_nfh.fh_generic.fh_fid)) {
+ vput(nvp);
+ goto invalid;
+ }
+ if (duration2) {
+ (void) nqsrv_getlease(nvp, &duration2, NQL_READ,
+ nfsd, nam, &cache2, &frev2, cred);
+ fl.fl_duration = txdr_unsigned(duration2);
+ fl.fl_cachable = txdr_unsigned(cache2);
+ txdr_hyper(&frev2, fl.fl_frev);
+ } else
+ fl.fl_duration = 0;
+ if (VOP_GETATTR(nvp, vap, cred, nfsd->nd_procp)) {
+ vput(nvp);
+ goto invalid;
+ }
+ vput(nvp);
+ fp = (struct nfsv2_fattr *)&fl.fl_fattr;
+ nfsm_srvfillattr;
+ len += (4*NFSX_UNSIGNED + nlen + rem + NFSX_FH
+ + NFSX_NQFATTR);
+ if (len > cnt) {
+ eofflag = 0;
+ break;
+ }
+ /*
+ * Build the directory record xdr from
+ * the dirent entry.
+ */
+ nfsm_clget;
+ *tl = nfs_true;
+ bp += NFSX_UNSIGNED;
+
+ /*
+ * For readdir_and_lookup copy the stuff out.
+ */
+ xfer = sizeof (struct flrep);
+ cp = (caddr_t)&fl;
+ while (xfer > 0) {
+ nfsm_clget;
+ if ((bp+xfer) > be)
+ tsiz = be-bp;
+ else
+ tsiz = xfer;
+ bcopy(cp, bp, tsiz);
+ bp += tsiz;
+ xfer -= tsiz;
+ if (xfer > 0)
+ cp += tsiz;
+ }
+ nfsm_clget;
+ *tl = txdr_unsigned(dp->d_fileno);
+ bp += NFSX_UNSIGNED;
+ nfsm_clget;
+ *tl = txdr_unsigned(nlen);
+ bp += NFSX_UNSIGNED;
+
+ /* And loop around copying the name */
+ xfer = nlen;
+ cp = dp->d_name;
+ while (xfer > 0) {
+ nfsm_clget;
+ if ((bp+xfer) > be)
+ tsiz = be-bp;
+ else
+ tsiz = xfer;
+ bcopy(cp, bp, tsiz);
+ bp += tsiz;
+ xfer -= tsiz;
+ if (xfer > 0)
+ cp += tsiz;
+ }
+ /* And null pad to a long boundary */
+ for (i = 0; i < rem; i++)
+ *bp++ = '\0';
+ nfsm_clget;
+
+ /* Finish off the record */
+ toff += dp->d_reclen;
+ *tl = txdr_unsigned(toff);
+ bp += NFSX_UNSIGNED;
+ } else
+invalid:
+ toff += dp->d_reclen;
+ cpos += dp->d_reclen;
+ dp = (struct dirent *)cpos;
+ }
+ vrele(vp);
+ nfsm_clget;
+ *tl = nfs_false;
+ bp += NFSX_UNSIGNED;
+ nfsm_clget;
+ if (eofflag)
+ *tl = nfs_true;
+ else
+ *tl = nfs_false;
+ bp += NFSX_UNSIGNED;
+ if (mp != mb) {
+ if (bp < be)
+ mp->m_len = bp - mtod(mp, caddr_t);
+ } else
+ mp->m_len += bp - bpos;
+ FREE(rbuf, M_TEMP);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs statfs service
+ */
+int
+nfsrv_statfs(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct statfs *sf;
+ register struct nfsv2_statfs *sfp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache = 0, isnq;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct statfs statfs;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ isnq = (nfsd->nd_nqlflag != NQL_NOVAL);
+ nfsm_srvmtofh(fhp);
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ sf = &statfs;
+ error = VFS_STATFS(vp->v_mount, sf, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_STATFS(isnq));
+ nfsm_build(sfp, struct nfsv2_statfs *, NFSX_STATFS(isnq));
+ sfp->sf_tsize = txdr_unsigned(NFS_MAXDGRAMDATA);
+ sfp->sf_bsize = txdr_unsigned(sf->f_bsize);
+ sfp->sf_blocks = txdr_unsigned(sf->f_blocks);
+ sfp->sf_bfree = txdr_unsigned(sf->f_bfree);
+ sfp->sf_bavail = txdr_unsigned(sf->f_bavail);
+ if (isnq) {
+ sfp->sf_files = txdr_unsigned(sf->f_files);
+ sfp->sf_ffree = txdr_unsigned(sf->f_ffree);
+ }
+ nfsm_srvdone;
+}
+
+/*
+ * Null operation, used by clients to ping server
+ */
+/* ARGSUSED */
+int
+nfsrv_null(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ caddr_t bpos;
+ int error = VNOVAL, cache = 0;
+ struct mbuf *mb, *mreq;
+ u_quad_t frev;
+
+ nfsm_reply(0);
+ return (error);
+}
+
+/*
+ * No operation, used for obsolete procedures
+ */
+/* ARGSUSED */
+int
+nfsrv_noop(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ caddr_t bpos;
+ int error, cache = 0;
+ struct mbuf *mb, *mreq;
+ u_quad_t frev;
+
+ if (nfsd->nd_repstat)
+ error = nfsd->nd_repstat;
+ else
+ error = EPROCUNAVAIL;
+ nfsm_reply(0);
+ return (error);
+}
+
+/*
+ * Perform access checking for vnodes obtained from file handles that would
+ * refer to files already opened by a Unix client. You cannot just use
+ * vn_writechk() and VOP_ACCESS() for two reasons.
+ * 1 - You must check for exported rdonly as well as MNT_RDONLY for the write case
+ * 2 - The owner is to be given access irrespective of mode bits so that
+ * processes that chmod after opening a file don't break. I don't like
+ * this because it opens a security hole, but since the nfs server opens
+ * a security hole the size of a barn door anyhow, what the heck.
+ */
+int
+nfsrv_access(vp, flags, cred, rdonly, p)
+ register struct vnode *vp;
+ int flags;
+ register struct ucred *cred;
+ int rdonly;
+ struct proc *p;
+{
+ struct vattr vattr;
+ int error;
+ if (flags & VWRITE) {
+ /* Just vn_writechk() changed to check rdonly */
+ /*
+ * Disallow write attempts on read-only file systems;
+ * unless the file is a socket or a block or character
+ * device resident on the file system.
+ */
+ if (rdonly || (vp->v_mount->mnt_flag & MNT_RDONLY)) {
+ switch (vp->v_type) {
+ case VREG: case VDIR: case VLNK:
+ return (EROFS);
+ }
+ }
+ /*
+ * If there's shared text associated with
+ * the inode, try to free it up once. If
+ * we fail, we can't allow writing.
+ */
+ if ((vp->v_flag & VTEXT) && !vnode_pager_uncache(vp))
+ return (ETXTBSY);
+ }
+ if (error = VOP_GETATTR(vp, &vattr, cred, p))
+ return (error);
+ if ((error = VOP_ACCESS(vp, flags, cred, p)) &&
+ cred->cr_uid != vattr.va_uid)
+ return (error);
+ return (0);
+}
diff --git a/sys/nfs/nfs_socket.c b/sys/nfs/nfs_socket.c
new file mode 100644
index 0000000..c00f7d0
--- /dev/null
+++ b/sys/nfs/nfs_socket.c
@@ -0,0 +1,2005 @@
+/*
+ * Copyright (c) 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_socket.c 8.3 (Berkeley) 1/12/94
+ */
+
+/*
+ * Socket operations for use by nfs
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/vnode.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/syslog.h>
+#include <sys/tprintf.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfsrtt.h>
+#include <nfs/nqnfs.h>
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Estimate rto for an nfs rpc sent via. an unreliable datagram.
+ * Use the mean and mean deviation of rtt for the appropriate type of rpc
+ * for the frequent rpcs and a default for the others.
+ * The justification for doing "other" this way is that these rpcs
+ * happen so infrequently that timer est. would probably be stale.
+ * Also, since many of these rpcs are
+ * non-idempotent, a conservative timeout is desired.
+ * getattr, lookup - A+2D
+ * read, write - A+4D
+ * other - nm_timeo
+ */
+#define NFS_RTO(n, t) \
+ ((t) == 0 ? (n)->nm_timeo : \
+ ((t) < 3 ? \
+ (((((n)->nm_srtt[t-1] + 3) >> 2) + (n)->nm_sdrtt[t-1] + 1) >> 1) : \
+ ((((n)->nm_srtt[t-1] + 7) >> 3) + (n)->nm_sdrtt[t-1] + 1)))
+#define NFS_SRTT(r) (r)->r_nmp->nm_srtt[proct[(r)->r_procnum] - 1]
+#define NFS_SDRTT(r) (r)->r_nmp->nm_sdrtt[proct[(r)->r_procnum] - 1]
+/*
+ * External data, mostly RPC constants in XDR form
+ */
+extern u_long rpc_reply, rpc_msgdenied, rpc_mismatch, rpc_vers, rpc_auth_unix,
+ rpc_msgaccepted, rpc_call, rpc_autherr, rpc_rejectedcred,
+ rpc_auth_kerb;
+extern u_long nfs_prog, nfs_vers, nqnfs_prog, nqnfs_vers;
+extern time_t nqnfsstarttime;
+extern int nonidempotent[NFS_NPROCS];
+
+/*
+ * Maps errno values to nfs error numbers.
+ * Use NFSERR_IO as the catch all for ones not specifically defined in
+ * RFC 1094.
+ */
+static int nfsrv_errmap[ELAST] = {
+ NFSERR_PERM, NFSERR_NOENT, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_NXIO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_ACCES, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_EXIST, NFSERR_IO, NFSERR_NODEV, NFSERR_NOTDIR,
+ NFSERR_ISDIR, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_FBIG, NFSERR_NOSPC, NFSERR_IO, NFSERR_ROFS,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_NAMETOL, NFSERR_IO, NFSERR_IO,
+ NFSERR_NOTEMPTY, NFSERR_IO, NFSERR_IO, NFSERR_DQUOT, NFSERR_STALE,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO,
+};
+
+/*
+ * Defines which timer to use for the procnum.
+ * 0 - default
+ * 1 - getattr
+ * 2 - lookup
+ * 3 - read
+ * 4 - write
+ */
+static int proct[NFS_NPROCS] = {
+ 0, 1, 0, 0, 2, 3, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0,
+};
+
+/*
+ * There is a congestion window for outstanding rpcs maintained per mount
+ * point. The cwnd size is adjusted in roughly the way that:
+ * Van Jacobson, Congestion avoidance and Control, In "Proceedings of
+ * SIGCOMM '88". ACM, August 1988.
+ * describes for TCP. The cwnd size is chopped in half on a retransmit timeout
+ * and incremented by 1/cwnd when each rpc reply is received and a full cwnd
+ * of rpcs is in progress.
+ * (The sent count and cwnd are scaled for integer arith.)
+ * Variants of "slow start" were tried and were found to be too much of a
+ * performance hit (ave. rtt 3 times larger),
+ * I suspect due to the large rtt that nfs rpcs have.
+ */
+#define NFS_CWNDSCALE 256
+#define NFS_MAXCWND (NFS_CWNDSCALE * 32)
+static int nfs_backoff[8] = { 2, 4, 8, 16, 32, 64, 128, 256, };
+int nfs_sbwait();
+void nfs_disconnect(), nfs_realign(), nfsrv_wakenfsd(), nfs_sndunlock();
+void nfs_rcvunlock(), nqnfs_serverd(), nqnfs_clientlease();
+struct mbuf *nfsm_rpchead();
+int nfsrtton = 0;
+struct nfsrtt nfsrtt;
+struct nfsd nfsd_head;
+
+int nfsrv_null(),
+ nfsrv_getattr(),
+ nfsrv_setattr(),
+ nfsrv_lookup(),
+ nfsrv_readlink(),
+ nfsrv_read(),
+ nfsrv_write(),
+ nfsrv_create(),
+ nfsrv_remove(),
+ nfsrv_rename(),
+ nfsrv_link(),
+ nfsrv_symlink(),
+ nfsrv_mkdir(),
+ nfsrv_rmdir(),
+ nfsrv_readdir(),
+ nfsrv_statfs(),
+ nfsrv_noop(),
+ nqnfsrv_readdirlook(),
+ nqnfsrv_getlease(),
+ nqnfsrv_vacated(),
+ nqnfsrv_access();
+
+int (*nfsrv_procs[NFS_NPROCS])() = {
+ nfsrv_null,
+ nfsrv_getattr,
+ nfsrv_setattr,
+ nfsrv_noop,
+ nfsrv_lookup,
+ nfsrv_readlink,
+ nfsrv_read,
+ nfsrv_noop,
+ nfsrv_write,
+ nfsrv_create,
+ nfsrv_remove,
+ nfsrv_rename,
+ nfsrv_link,
+ nfsrv_symlink,
+ nfsrv_mkdir,
+ nfsrv_rmdir,
+ nfsrv_readdir,
+ nfsrv_statfs,
+ nqnfsrv_readdirlook,
+ nqnfsrv_getlease,
+ nqnfsrv_vacated,
+ nfsrv_noop,
+ nqnfsrv_access,
+};
+
+struct nfsreq nfsreqh;
+
+/*
+ * Initialize sockets and congestion for a new NFS connection.
+ * We do not free the sockaddr if error.
+ */
+int
+nfs_connect(nmp, rep)
+ register struct nfsmount *nmp;
+ struct nfsreq *rep;
+{
+ register struct socket *so;
+ int s, error, rcvreserve, sndreserve;
+ struct sockaddr *saddr;
+ struct sockaddr_in *sin;
+ struct mbuf *m;
+ u_short tport;
+
+ nmp->nm_so = (struct socket *)0;
+ saddr = mtod(nmp->nm_nam, struct sockaddr *);
+ if (error = socreate(saddr->sa_family,
+ &nmp->nm_so, nmp->nm_sotype, nmp->nm_soproto))
+ goto bad;
+ so = nmp->nm_so;
+ nmp->nm_soflags = so->so_proto->pr_flags;
+
+ /*
+ * Some servers require that the client port be a reserved port number.
+ */
+ if (saddr->sa_family == AF_INET && (nmp->nm_flag & NFSMNT_RESVPORT)) {
+ MGET(m, M_WAIT, MT_SONAME);
+ sin = mtod(m, struct sockaddr_in *);
+ sin->sin_len = m->m_len = sizeof (struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = INADDR_ANY;
+ tport = IPPORT_RESERVED - 1;
+ sin->sin_port = htons(tport);
+ while ((error = sobind(so, m)) == EADDRINUSE &&
+ --tport > IPPORT_RESERVED / 2)
+ sin->sin_port = htons(tport);
+ m_freem(m);
+ if (error)
+ goto bad;
+ }
+
+ /*
+ * Protocols that do not require connections may be optionally left
+ * unconnected for servers that reply from a port other than NFS_PORT.
+ */
+ if (nmp->nm_flag & NFSMNT_NOCONN) {
+ if (nmp->nm_soflags & PR_CONNREQUIRED) {
+ error = ENOTCONN;
+ goto bad;
+ }
+ } else {
+ if (error = soconnect(so, nmp->nm_nam))
+ goto bad;
+
+ /*
+ * Wait for the connection to complete. Cribbed from the
+ * connect system call but with the wait timing out so
+ * that interruptible mounts don't hang here for a long time.
+ */
+ s = splnet();
+ while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
+ (void) tsleep((caddr_t)&so->so_timeo, PSOCK,
+ "nfscon", 2 * hz);
+ if ((so->so_state & SS_ISCONNECTING) &&
+ so->so_error == 0 && rep &&
+ (error = nfs_sigintr(nmp, rep, rep->r_procp))) {
+ so->so_state &= ~SS_ISCONNECTING;
+ splx(s);
+ goto bad;
+ }
+ }
+ if (so->so_error) {
+ error = so->so_error;
+ so->so_error = 0;
+ splx(s);
+ goto bad;
+ }
+ splx(s);
+ }
+ if (nmp->nm_flag & (NFSMNT_SOFT | NFSMNT_INT)) {
+ so->so_rcv.sb_timeo = (5 * hz);
+ so->so_snd.sb_timeo = (5 * hz);
+ } else {
+ so->so_rcv.sb_timeo = 0;
+ so->so_snd.sb_timeo = 0;
+ }
+ if (nmp->nm_sotype == SOCK_DGRAM) {
+ sndreserve = nmp->nm_wsize + NFS_MAXPKTHDR;
+ rcvreserve = nmp->nm_rsize + NFS_MAXPKTHDR;
+ } else if (nmp->nm_sotype == SOCK_SEQPACKET) {
+ sndreserve = (nmp->nm_wsize + NFS_MAXPKTHDR) * 2;
+ rcvreserve = (nmp->nm_rsize + NFS_MAXPKTHDR) * 2;
+ } else {
+ if (nmp->nm_sotype != SOCK_STREAM)
+ panic("nfscon sotype");
+ if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
+ MGET(m, M_WAIT, MT_SOOPTS);
+ *mtod(m, int *) = 1;
+ m->m_len = sizeof(int);
+ sosetopt(so, SOL_SOCKET, SO_KEEPALIVE, m);
+ }
+ if (so->so_proto->pr_protocol == IPPROTO_TCP) {
+ MGET(m, M_WAIT, MT_SOOPTS);
+ *mtod(m, int *) = 1;
+ m->m_len = sizeof(int);
+ sosetopt(so, IPPROTO_TCP, TCP_NODELAY, m);
+ }
+ sndreserve = (nmp->nm_wsize + NFS_MAXPKTHDR + sizeof (u_long))
+ * 2;
+ rcvreserve = (nmp->nm_rsize + NFS_MAXPKTHDR + sizeof (u_long))
+ * 2;
+ }
+ if (error = soreserve(so, sndreserve, rcvreserve))
+ goto bad;
+ so->so_rcv.sb_flags |= SB_NOINTR;
+ so->so_snd.sb_flags |= SB_NOINTR;
+
+ /* Initialize other non-zero congestion variables */
+ nmp->nm_srtt[0] = nmp->nm_srtt[1] = nmp->nm_srtt[2] = nmp->nm_srtt[3] =
+ nmp->nm_srtt[4] = (NFS_TIMEO << 3);
+ nmp->nm_sdrtt[0] = nmp->nm_sdrtt[1] = nmp->nm_sdrtt[2] =
+ nmp->nm_sdrtt[3] = nmp->nm_sdrtt[4] = 0;
+ nmp->nm_cwnd = NFS_MAXCWND / 2; /* Initial send window */
+ nmp->nm_sent = 0;
+ nmp->nm_timeouts = 0;
+ return (0);
+
+bad:
+ nfs_disconnect(nmp);
+ return (error);
+}
+
+/*
+ * Reconnect routine:
+ * Called when a connection is broken on a reliable protocol.
+ * - clean up the old socket
+ * - nfs_connect() again
+ * - set R_MUSTRESEND for all outstanding requests on mount point
+ * If this fails the mount point is DEAD!
+ * nb: Must be called with the nfs_sndlock() set on the mount point.
+ */
+int
+nfs_reconnect(rep)
+ register struct nfsreq *rep;
+{
+ register struct nfsreq *rp;
+ register struct nfsmount *nmp = rep->r_nmp;
+ int error;
+
+ nfs_disconnect(nmp);
+ while (error = nfs_connect(nmp, rep)) {
+ if (error == EINTR || error == ERESTART)
+ return (EINTR);
+ (void) tsleep((caddr_t)&lbolt, PSOCK, "nfscon", 0);
+ }
+
+ /*
+ * Loop through outstanding request list and fix up all requests
+ * on old socket.
+ */
+ rp = nfsreqh.r_next;
+ while (rp != &nfsreqh) {
+ if (rp->r_nmp == nmp)
+ rp->r_flags |= R_MUSTRESEND;
+ rp = rp->r_next;
+ }
+ return (0);
+}
+
+/*
+ * NFS disconnect. Clean up and unlink.
+ */
+void
+nfs_disconnect(nmp)
+ register struct nfsmount *nmp;
+{
+ register struct socket *so;
+
+ if (nmp->nm_so) {
+ so = nmp->nm_so;
+ nmp->nm_so = (struct socket *)0;
+ soshutdown(so, 2);
+ soclose(so);
+ }
+}
+
+/*
+ * This is the nfs send routine. For connection based socket types, it
+ * must be called with an nfs_sndlock() on the socket.
+ * "rep == NULL" indicates that it has been called from a server.
+ * For the client side:
+ * - return EINTR if the RPC is terminated, 0 otherwise
+ * - set R_MUSTRESEND if the send fails for any reason
+ * - do any cleanup required by recoverable socket errors (???)
+ * For the server side:
+ * - return EINTR or ERESTART if interrupted by a signal
+ * - return EPIPE if a connection is lost for connection based sockets (TCP...)
+ * - do any cleanup required by recoverable socket errors (???)
+ */
+int
+nfs_send(so, nam, top, rep)
+ register struct socket *so;
+ struct mbuf *nam;
+ register struct mbuf *top;
+ struct nfsreq *rep;
+{
+ struct mbuf *sendnam;
+ int error, soflags, flags;
+
+ if (rep) {
+ if (rep->r_flags & R_SOFTTERM) {
+ m_freem(top);
+ return (EINTR);
+ }
+ if ((so = rep->r_nmp->nm_so) == NULL) {
+ rep->r_flags |= R_MUSTRESEND;
+ m_freem(top);
+ return (0);
+ }
+ rep->r_flags &= ~R_MUSTRESEND;
+ soflags = rep->r_nmp->nm_soflags;
+ } else
+ soflags = so->so_proto->pr_flags;
+ if ((soflags & PR_CONNREQUIRED) || (so->so_state & SS_ISCONNECTED))
+ sendnam = (struct mbuf *)0;
+ else
+ sendnam = nam;
+ if (so->so_type == SOCK_SEQPACKET)
+ flags = MSG_EOR;
+ else
+ flags = 0;
+
+ error = sosend(so, sendnam, (struct uio *)0, top,
+ (struct mbuf *)0, flags);
+ if (error) {
+ if (rep) {
+ log(LOG_INFO, "nfs send error %d for server %s\n",error,
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ /*
+ * Deal with errors for the client side.
+ */
+ if (rep->r_flags & R_SOFTTERM)
+ error = EINTR;
+ else
+ rep->r_flags |= R_MUSTRESEND;
+ } else
+ log(LOG_INFO, "nfsd send error %d\n", error);
+
+ /*
+ * Handle any recoverable (soft) socket errors here. (???)
+ */
+ if (error != EINTR && error != ERESTART &&
+ error != EWOULDBLOCK && error != EPIPE)
+ error = 0;
+ }
+ return (error);
+}
+
+/*
+ * Receive a Sun RPC Request/Reply. For SOCK_DGRAM, the work is all
+ * done by soreceive(), but for SOCK_STREAM we must deal with the Record
+ * Mark and consolidate the data into a new mbuf list.
+ * nb: Sometimes TCP passes the data up to soreceive() in long lists of
+ * small mbufs.
+ * For SOCK_STREAM we must be very careful to read an entire record once
+ * we have read any of it, even if the system call has been interrupted.
+ */
+int
+nfs_receive(rep, aname, mp)
+ register struct nfsreq *rep;
+ struct mbuf **aname;
+ struct mbuf **mp;
+{
+ register struct socket *so;
+ struct uio auio;
+ struct iovec aio;
+ register struct mbuf *m;
+ struct mbuf *control;
+ u_long len;
+ struct mbuf **getnam;
+ int error, sotype, rcvflg;
+ struct proc *p = curproc; /* XXX */
+
+ /*
+ * Set up arguments for soreceive()
+ */
+ *mp = (struct mbuf *)0;
+ *aname = (struct mbuf *)0;
+ sotype = rep->r_nmp->nm_sotype;
+
+ /*
+ * For reliable protocols, lock against other senders/receivers
+ * in case a reconnect is necessary.
+ * For SOCK_STREAM, first get the Record Mark to find out how much
+ * more there is to get.
+ * We must lock the socket against other receivers
+ * until we have an entire rpc request/reply.
+ */
+ if (sotype != SOCK_DGRAM) {
+ if (error = nfs_sndlock(&rep->r_nmp->nm_flag, rep))
+ return (error);
+tryagain:
+ /*
+ * Check for fatal errors and resending request.
+ */
+ /*
+ * Ugh: If a reconnect attempt just happened, nm_so
+ * would have changed. NULL indicates a failed
+ * attempt that has essentially shut down this
+ * mount point.
+ */
+ if (rep->r_mrep || (rep->r_flags & R_SOFTTERM)) {
+ nfs_sndunlock(&rep->r_nmp->nm_flag);
+ return (EINTR);
+ }
+ if ((so = rep->r_nmp->nm_so) == NULL) {
+ if (error = nfs_reconnect(rep)) {
+ nfs_sndunlock(&rep->r_nmp->nm_flag);
+ return (error);
+ }
+ goto tryagain;
+ }
+ while (rep->r_flags & R_MUSTRESEND) {
+ m = m_copym(rep->r_mreq, 0, M_COPYALL, M_WAIT);
+ nfsstats.rpcretries++;
+ if (error = nfs_send(so, rep->r_nmp->nm_nam, m, rep)) {
+ if (error == EINTR || error == ERESTART ||
+ (error = nfs_reconnect(rep))) {
+ nfs_sndunlock(&rep->r_nmp->nm_flag);
+ return (error);
+ }
+ goto tryagain;
+ }
+ }
+ nfs_sndunlock(&rep->r_nmp->nm_flag);
+ if (sotype == SOCK_STREAM) {
+ aio.iov_base = (caddr_t) &len;
+ aio.iov_len = sizeof(u_long);
+ auio.uio_iov = &aio;
+ auio.uio_iovcnt = 1;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_READ;
+ auio.uio_offset = 0;
+ auio.uio_resid = sizeof(u_long);
+ auio.uio_procp = p;
+ do {
+ rcvflg = MSG_WAITALL;
+ error = soreceive(so, (struct mbuf **)0, &auio,
+ (struct mbuf **)0, (struct mbuf **)0, &rcvflg);
+ if (error == EWOULDBLOCK && rep) {
+ if (rep->r_flags & R_SOFTTERM)
+ return (EINTR);
+ }
+ } while (error == EWOULDBLOCK);
+ if (!error && auio.uio_resid > 0) {
+ log(LOG_INFO,
+ "short receive (%d/%d) from nfs server %s\n",
+ sizeof(u_long) - auio.uio_resid,
+ sizeof(u_long),
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ error = EPIPE;
+ }
+ if (error)
+ goto errout;
+ len = ntohl(len) & ~0x80000000;
+ /*
+ * This is SERIOUS! We are out of sync with the sender
+ * and forcing a disconnect/reconnect is all I can do.
+ */
+ if (len > NFS_MAXPACKET) {
+ log(LOG_ERR, "%s (%d) from nfs server %s\n",
+ "impossible packet length",
+ len,
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ error = EFBIG;
+ goto errout;
+ }
+ auio.uio_resid = len;
+ do {
+ rcvflg = MSG_WAITALL;
+ error = soreceive(so, (struct mbuf **)0,
+ &auio, mp, (struct mbuf **)0, &rcvflg);
+ } while (error == EWOULDBLOCK || error == EINTR ||
+ error == ERESTART);
+ if (!error && auio.uio_resid > 0) {
+ log(LOG_INFO,
+ "short receive (%d/%d) from nfs server %s\n",
+ len - auio.uio_resid, len,
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ error = EPIPE;
+ }
+ } else {
+ /*
+ * NB: Since uio_resid is big, MSG_WAITALL is ignored
+ * and soreceive() will return when it has either a
+ * control msg or a data msg.
+ * We have no use for control msg., but must grab them
+ * and then throw them away so we know what is going
+ * on.
+ */
+ auio.uio_resid = len = 100000000; /* Anything Big */
+ auio.uio_procp = p;
+ do {
+ rcvflg = 0;
+ error = soreceive(so, (struct mbuf **)0,
+ &auio, mp, &control, &rcvflg);
+ if (control)
+ m_freem(control);
+ if (error == EWOULDBLOCK && rep) {
+ if (rep->r_flags & R_SOFTTERM)
+ return (EINTR);
+ }
+ } while (error == EWOULDBLOCK ||
+ (!error && *mp == NULL && control));
+ if ((rcvflg & MSG_EOR) == 0)
+ printf("Egad!!\n");
+ if (!error && *mp == NULL)
+ error = EPIPE;
+ len -= auio.uio_resid;
+ }
+errout:
+ if (error && error != EINTR && error != ERESTART) {
+ m_freem(*mp);
+ *mp = (struct mbuf *)0;
+ if (error != EPIPE)
+ log(LOG_INFO,
+ "receive error %d from nfs server %s\n",
+ error,
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ error = nfs_sndlock(&rep->r_nmp->nm_flag, rep);
+ if (!error)
+ error = nfs_reconnect(rep);
+ if (!error)
+ goto tryagain;
+ }
+ } else {
+ if ((so = rep->r_nmp->nm_so) == NULL)
+ return (EACCES);
+ if (so->so_state & SS_ISCONNECTED)
+ getnam = (struct mbuf **)0;
+ else
+ getnam = aname;
+ auio.uio_resid = len = 1000000;
+ auio.uio_procp = p;
+ do {
+ rcvflg = 0;
+ error = soreceive(so, getnam, &auio, mp,
+ (struct mbuf **)0, &rcvflg);
+ if (error == EWOULDBLOCK &&
+ (rep->r_flags & R_SOFTTERM))
+ return (EINTR);
+ } while (error == EWOULDBLOCK);
+ len -= auio.uio_resid;
+ }
+ if (error) {
+ m_freem(*mp);
+ *mp = (struct mbuf *)0;
+ }
+ /*
+ * Search for any mbufs that are not a multiple of 4 bytes long
+ * or with m_data not longword aligned.
+ * These could cause pointer alignment problems, so copy them to
+ * well aligned mbufs.
+ */
+ nfs_realign(*mp, 5 * NFSX_UNSIGNED);
+ return (error);
+}
+
+/*
+ * Implement receipt of reply on a socket.
+ * We must search through the list of received datagrams matching them
+ * with outstanding requests using the xid, until ours is found.
+ */
+/* ARGSUSED */
+int
+nfs_reply(myrep)
+ struct nfsreq *myrep;
+{
+ register struct nfsreq *rep;
+ register struct nfsmount *nmp = myrep->r_nmp;
+ register long t1;
+ struct mbuf *mrep, *nam, *md;
+ u_long rxid, *tl;
+ caddr_t dpos, cp2;
+ int error;
+
+ /*
+ * Loop around until we get our own reply
+ */
+ for (;;) {
+ /*
+ * Lock against other receivers so that I don't get stuck in
+ * sbwait() after someone else has received my reply for me.
+ * Also necessary for connection based protocols to avoid
+ * race conditions during a reconnect.
+ */
+ if (error = nfs_rcvlock(myrep))
+ return (error);
+ /* Already received, bye bye */
+ if (myrep->r_mrep != NULL) {
+ nfs_rcvunlock(&nmp->nm_flag);
+ return (0);
+ }
+ /*
+ * Get the next Rpc reply off the socket
+ */
+ error = nfs_receive(myrep, &nam, &mrep);
+ nfs_rcvunlock(&nmp->nm_flag);
+ if (error) {
+
+ /*
+ * Ignore routing errors on connectionless protocols??
+ */
+ if (NFSIGNORE_SOERROR(nmp->nm_soflags, error)) {
+ nmp->nm_so->so_error = 0;
+ if (myrep->r_flags & R_GETONEREP)
+ return (0);
+ continue;
+ }
+ return (error);
+ }
+ if (nam)
+ m_freem(nam);
+
+ /*
+ * Get the xid and check that it is an rpc reply
+ */
+ md = mrep;
+ dpos = mtod(md, caddr_t);
+ nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
+ rxid = *tl++;
+ if (*tl != rpc_reply) {
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ if (nqnfs_callback(nmp, mrep, md, dpos))
+ nfsstats.rpcinvalid++;
+ } else {
+ nfsstats.rpcinvalid++;
+ m_freem(mrep);
+ }
+nfsmout:
+ if (myrep->r_flags & R_GETONEREP)
+ return (0);
+ continue;
+ }
+
+ /*
+ * Loop through the request list to match up the reply
+ * Iff no match, just drop the datagram
+ */
+ rep = nfsreqh.r_next;
+ while (rep != &nfsreqh) {
+ if (rep->r_mrep == NULL && rxid == rep->r_xid) {
+ /* Found it.. */
+ rep->r_mrep = mrep;
+ rep->r_md = md;
+ rep->r_dpos = dpos;
+ if (nfsrtton) {
+ struct rttl *rt;
+
+ rt = &nfsrtt.rttl[nfsrtt.pos];
+ rt->proc = rep->r_procnum;
+ rt->rto = NFS_RTO(nmp, proct[rep->r_procnum]);
+ rt->sent = nmp->nm_sent;
+ rt->cwnd = nmp->nm_cwnd;
+ rt->srtt = nmp->nm_srtt[proct[rep->r_procnum] - 1];
+ rt->sdrtt = nmp->nm_sdrtt[proct[rep->r_procnum] - 1];
+ rt->fsid = nmp->nm_mountp->mnt_stat.f_fsid;
+ rt->tstamp = time;
+ if (rep->r_flags & R_TIMING)
+ rt->rtt = rep->r_rtt;
+ else
+ rt->rtt = 1000000;
+ nfsrtt.pos = (nfsrtt.pos + 1) % NFSRTTLOGSIZ;
+ }
+ /*
+ * Update congestion window.
+ * Do the additive increase of
+ * one rpc/rtt.
+ */
+ if (nmp->nm_cwnd <= nmp->nm_sent) {
+ nmp->nm_cwnd +=
+ (NFS_CWNDSCALE * NFS_CWNDSCALE +
+ (nmp->nm_cwnd >> 1)) / nmp->nm_cwnd;
+ if (nmp->nm_cwnd > NFS_MAXCWND)
+ nmp->nm_cwnd = NFS_MAXCWND;
+ }
+ rep->r_flags &= ~R_SENT;
+ nmp->nm_sent -= NFS_CWNDSCALE;
+ /*
+ * Update rtt using a gain of 0.125 on the mean
+ * and a gain of 0.25 on the deviation.
+ */
+ if (rep->r_flags & R_TIMING) {
+ /*
+ * Since the timer resolution of
+ * NFS_HZ is so course, it can often
+ * result in r_rtt == 0. Since
+ * r_rtt == N means that the actual
+ * rtt is between N+dt and N+2-dt ticks,
+ * add 1.
+ */
+ t1 = rep->r_rtt + 1;
+ t1 -= (NFS_SRTT(rep) >> 3);
+ NFS_SRTT(rep) += t1;
+ if (t1 < 0)
+ t1 = -t1;
+ t1 -= (NFS_SDRTT(rep) >> 2);
+ NFS_SDRTT(rep) += t1;
+ }
+ nmp->nm_timeouts = 0;
+ break;
+ }
+ rep = rep->r_next;
+ }
+ /*
+ * If not matched to a request, drop it.
+ * If it's mine, get out.
+ */
+ if (rep == &nfsreqh) {
+ nfsstats.rpcunexpected++;
+ m_freem(mrep);
+ } else if (rep == myrep) {
+ if (rep->r_mrep == NULL)
+ panic("nfsreply nil");
+ return (0);
+ }
+ if (myrep->r_flags & R_GETONEREP)
+ return (0);
+ }
+}
+
+/*
+ * nfs_request - goes something like this
+ * - fill in request struct
+ * - links it into list
+ * - calls nfs_send() for first transmit
+ * - calls nfs_receive() to get reply
+ * - break down rpc header and return with nfs reply pointed to
+ * by mrep or error
+ * nb: always frees up mreq mbuf list
+ */
+int
+nfs_request(vp, mrest, procnum, procp, cred, mrp, mdp, dposp)
+ struct vnode *vp;
+ struct mbuf *mrest;
+ int procnum;
+ struct proc *procp;
+ struct ucred *cred;
+ struct mbuf **mrp;
+ struct mbuf **mdp;
+ caddr_t *dposp;
+{
+ register struct mbuf *m, *mrep;
+ register struct nfsreq *rep;
+ register u_long *tl;
+ register int i;
+ struct nfsmount *nmp;
+ struct mbuf *md, *mheadend;
+ struct nfsreq *reph;
+ struct nfsnode *np;
+ time_t reqtime, waituntil;
+ caddr_t dpos, cp2;
+ int t1, nqlflag, cachable, s, error = 0, mrest_len, auth_len, auth_type;
+ int trylater_delay = NQ_TRYLATERDEL, trylater_cnt = 0, failed_auth = 0;
+ u_long xid;
+ u_quad_t frev;
+ char *auth_str;
+
+ nmp = VFSTONFS(vp->v_mount);
+ MALLOC(rep, struct nfsreq *, sizeof(struct nfsreq), M_NFSREQ, M_WAITOK);
+ rep->r_nmp = nmp;
+ rep->r_vp = vp;
+ rep->r_procp = procp;
+ rep->r_procnum = procnum;
+ i = 0;
+ m = mrest;
+ while (m) {
+ i += m->m_len;
+ m = m->m_next;
+ }
+ mrest_len = i;
+
+ /*
+ * Get the RPC header with authorization.
+ */
+kerbauth:
+ auth_str = (char *)0;
+ if (nmp->nm_flag & NFSMNT_KERB) {
+ if (failed_auth) {
+ error = nfs_getauth(nmp, rep, cred, &auth_type,
+ &auth_str, &auth_len);
+ if (error) {
+ free((caddr_t)rep, M_NFSREQ);
+ m_freem(mrest);
+ return (error);
+ }
+ } else {
+ auth_type = RPCAUTH_UNIX;
+ auth_len = 5 * NFSX_UNSIGNED;
+ }
+ } else {
+ auth_type = RPCAUTH_UNIX;
+ if (cred->cr_ngroups < 1)
+ panic("nfsreq nogrps");
+ auth_len = ((((cred->cr_ngroups - 1) > nmp->nm_numgrps) ?
+ nmp->nm_numgrps : (cred->cr_ngroups - 1)) << 2) +
+ 5 * NFSX_UNSIGNED;
+ }
+ m = nfsm_rpchead(cred, (nmp->nm_flag & NFSMNT_NQNFS), procnum,
+ auth_type, auth_len, auth_str, mrest, mrest_len, &mheadend, &xid);
+ if (auth_str)
+ free(auth_str, M_TEMP);
+
+ /*
+ * For stream protocols, insert a Sun RPC Record Mark.
+ */
+ if (nmp->nm_sotype == SOCK_STREAM) {
+ M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
+ *mtod(m, u_long *) = htonl(0x80000000 |
+ (m->m_pkthdr.len - NFSX_UNSIGNED));
+ }
+ rep->r_mreq = m;
+ rep->r_xid = xid;
+tryagain:
+ if (nmp->nm_flag & NFSMNT_SOFT)
+ rep->r_retry = nmp->nm_retry;
+ else
+ rep->r_retry = NFS_MAXREXMIT + 1; /* past clip limit */
+ rep->r_rtt = rep->r_rexmit = 0;
+ if (proct[procnum] > 0)
+ rep->r_flags = R_TIMING;
+ else
+ rep->r_flags = 0;
+ rep->r_mrep = NULL;
+
+ /*
+ * Do the client side RPC.
+ */
+ nfsstats.rpcrequests++;
+ /*
+ * Chain request into list of outstanding requests. Be sure
+ * to put it LAST so timer finds oldest requests first.
+ */
+ s = splsoftclock();
+ reph = &nfsreqh;
+ reph->r_prev->r_next = rep;
+ rep->r_prev = reph->r_prev;
+ reph->r_prev = rep;
+ rep->r_next = reph;
+
+ /* Get send time for nqnfs */
+ reqtime = time.tv_sec;
+
+ /*
+ * If backing off another request or avoiding congestion, don't
+ * send this one now but let timer do it. If not timing a request,
+ * do it now.
+ */
+ if (nmp->nm_so && (nmp->nm_sotype != SOCK_DGRAM ||
+ (nmp->nm_flag & NFSMNT_DUMBTIMR) ||
+ nmp->nm_sent < nmp->nm_cwnd)) {
+ splx(s);
+ if (nmp->nm_soflags & PR_CONNREQUIRED)
+ error = nfs_sndlock(&nmp->nm_flag, rep);
+ if (!error) {
+ m = m_copym(m, 0, M_COPYALL, M_WAIT);
+ error = nfs_send(nmp->nm_so, nmp->nm_nam, m, rep);
+ if (nmp->nm_soflags & PR_CONNREQUIRED)
+ nfs_sndunlock(&nmp->nm_flag);
+ }
+ if (!error && (rep->r_flags & R_MUSTRESEND) == 0) {
+ nmp->nm_sent += NFS_CWNDSCALE;
+ rep->r_flags |= R_SENT;
+ }
+ } else {
+ splx(s);
+ rep->r_rtt = -1;
+ }
+
+ /*
+ * Wait for the reply from our send or the timer's.
+ */
+ if (!error || error == EPIPE)
+ error = nfs_reply(rep);
+
+ /*
+ * RPC done, unlink the request.
+ */
+ s = splsoftclock();
+ rep->r_prev->r_next = rep->r_next;
+ rep->r_next->r_prev = rep->r_prev;
+ splx(s);
+
+ /*
+ * Decrement the outstanding request count.
+ */
+ if (rep->r_flags & R_SENT) {
+ rep->r_flags &= ~R_SENT; /* paranoia */
+ nmp->nm_sent -= NFS_CWNDSCALE;
+ }
+
+ /*
+ * If there was a successful reply and a tprintf msg.
+ * tprintf a response.
+ */
+ if (!error && (rep->r_flags & R_TPRINTFMSG))
+ nfs_msg(rep->r_procp, nmp->nm_mountp->mnt_stat.f_mntfromname,
+ "is alive again");
+ mrep = rep->r_mrep;
+ md = rep->r_md;
+ dpos = rep->r_dpos;
+ if (error) {
+ m_freem(rep->r_mreq);
+ free((caddr_t)rep, M_NFSREQ);
+ return (error);
+ }
+
+ /*
+ * break down the rpc header and check if ok
+ */
+ nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED);
+ if (*tl++ == rpc_msgdenied) {
+ if (*tl == rpc_mismatch)
+ error = EOPNOTSUPP;
+ else if ((nmp->nm_flag & NFSMNT_KERB) && *tl++ == rpc_autherr) {
+ if (*tl == rpc_rejectedcred && failed_auth == 0) {
+ failed_auth++;
+ mheadend->m_next = (struct mbuf *)0;
+ m_freem(mrep);
+ m_freem(rep->r_mreq);
+ goto kerbauth;
+ } else
+ error = EAUTH;
+ } else
+ error = EACCES;
+ m_freem(mrep);
+ m_freem(rep->r_mreq);
+ free((caddr_t)rep, M_NFSREQ);
+ return (error);
+ }
+
+ /*
+ * skip over the auth_verf, someday we may want to cache auth_short's
+ * for nfs_reqhead(), but for now just dump it
+ */
+ if (*++tl != 0) {
+ i = nfsm_rndup(fxdr_unsigned(long, *tl));
+ nfsm_adv(i);
+ }
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ /* 0 == ok */
+ if (*tl == 0) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ if (*tl != 0) {
+ error = fxdr_unsigned(int, *tl);
+ m_freem(mrep);
+ if ((nmp->nm_flag & NFSMNT_NQNFS) &&
+ error == NQNFS_TRYLATER) {
+ error = 0;
+ waituntil = time.tv_sec + trylater_delay;
+ while (time.tv_sec < waituntil)
+ (void) tsleep((caddr_t)&lbolt,
+ PSOCK, "nqnfstry", 0);
+ trylater_delay *= nfs_backoff[trylater_cnt];
+ if (trylater_cnt < 7)
+ trylater_cnt++;
+ goto tryagain;
+ }
+
+ /*
+ * If the File Handle was stale, invalidate the
+ * lookup cache, just in case.
+ */
+ if (error == ESTALE)
+ cache_purge(vp);
+ m_freem(rep->r_mreq);
+ free((caddr_t)rep, M_NFSREQ);
+ return (error);
+ }
+
+ /*
+ * For nqnfs, get any lease in reply
+ */
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ if (*tl) {
+ np = VTONFS(vp);
+ nqlflag = fxdr_unsigned(int, *tl);
+ nfsm_dissect(tl, u_long *, 4*NFSX_UNSIGNED);
+ cachable = fxdr_unsigned(int, *tl++);
+ reqtime += fxdr_unsigned(int, *tl++);
+ if (reqtime > time.tv_sec) {
+ fxdr_hyper(tl, &frev);
+ nqnfs_clientlease(nmp, np, nqlflag,
+ cachable, reqtime, frev);
+ }
+ }
+ }
+ *mrp = mrep;
+ *mdp = md;
+ *dposp = dpos;
+ m_freem(rep->r_mreq);
+ FREE((caddr_t)rep, M_NFSREQ);
+ return (0);
+ }
+ m_freem(mrep);
+ m_freem(rep->r_mreq);
+ free((caddr_t)rep, M_NFSREQ);
+ error = EPROTONOSUPPORT;
+nfsmout:
+ return (error);
+}
+
+/*
+ * Generate the rpc reply header
+ * siz arg. is used to decide if adding a cluster is worthwhile
+ */
+int
+nfs_rephead(siz, nd, err, cache, frev, mrq, mbp, bposp)
+ int siz;
+ struct nfsd *nd;
+ int err;
+ int cache;
+ u_quad_t *frev;
+ struct mbuf **mrq;
+ struct mbuf **mbp;
+ caddr_t *bposp;
+{
+ register u_long *tl;
+ register struct mbuf *mreq;
+ caddr_t bpos;
+ struct mbuf *mb, *mb2;
+
+ MGETHDR(mreq, M_WAIT, MT_DATA);
+ mb = mreq;
+ /*
+ * If this is a big reply, use a cluster else
+ * try and leave leading space for the lower level headers.
+ */
+ siz += RPC_REPLYSIZ;
+ if (siz >= MINCLSIZE) {
+ MCLGET(mreq, M_WAIT);
+ } else
+ mreq->m_data += max_hdr;
+ tl = mtod(mreq, u_long *);
+ mreq->m_len = 6*NFSX_UNSIGNED;
+ bpos = ((caddr_t)tl)+mreq->m_len;
+ *tl++ = nd->nd_retxid;
+ *tl++ = rpc_reply;
+ if (err == ERPCMISMATCH || err == NQNFS_AUTHERR) {
+ *tl++ = rpc_msgdenied;
+ if (err == NQNFS_AUTHERR) {
+ *tl++ = rpc_autherr;
+ *tl = rpc_rejectedcred;
+ mreq->m_len -= NFSX_UNSIGNED;
+ bpos -= NFSX_UNSIGNED;
+ } else {
+ *tl++ = rpc_mismatch;
+ *tl++ = txdr_unsigned(2);
+ *tl = txdr_unsigned(2);
+ }
+ } else {
+ *tl++ = rpc_msgaccepted;
+ *tl++ = 0;
+ *tl++ = 0;
+ switch (err) {
+ case EPROGUNAVAIL:
+ *tl = txdr_unsigned(RPC_PROGUNAVAIL);
+ break;
+ case EPROGMISMATCH:
+ *tl = txdr_unsigned(RPC_PROGMISMATCH);
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(2);
+ *tl = txdr_unsigned(2); /* someday 3 */
+ break;
+ case EPROCUNAVAIL:
+ *tl = txdr_unsigned(RPC_PROCUNAVAIL);
+ break;
+ default:
+ *tl = 0;
+ if (err != VNOVAL) {
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ if (err)
+ *tl = txdr_unsigned(nfsrv_errmap[err - 1]);
+ else
+ *tl = 0;
+ }
+ break;
+ };
+ }
+
+ /*
+ * For nqnfs, piggyback lease as requested.
+ */
+ if (nd->nd_nqlflag != NQL_NOVAL && err == 0) {
+ if (nd->nd_nqlflag) {
+ nfsm_build(tl, u_long *, 5*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(nd->nd_nqlflag);
+ *tl++ = txdr_unsigned(cache);
+ *tl++ = txdr_unsigned(nd->nd_duration);
+ txdr_hyper(frev, tl);
+ } else {
+ if (nd->nd_nqlflag != 0)
+ panic("nqreph");
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = 0;
+ }
+ }
+ *mrq = mreq;
+ *mbp = mb;
+ *bposp = bpos;
+ if (err != 0 && err != VNOVAL)
+ nfsstats.srvrpc_errs++;
+ return (0);
+}
+
+/*
+ * Nfs timer routine
+ * Scan the nfsreq list and retranmit any requests that have timed out
+ * To avoid retransmission attempts on STREAM sockets (in the future) make
+ * sure to set the r_retry field to 0 (implies nm_retry == 0).
+ */
+void
+nfs_timer(arg)
+ void *arg;
+{
+ register struct nfsreq *rep;
+ register struct mbuf *m;
+ register struct socket *so;
+ register struct nfsmount *nmp;
+ register int timeo;
+ static long lasttime = 0;
+ int s, error;
+
+ s = splnet();
+ for (rep = nfsreqh.r_next; rep != &nfsreqh; rep = rep->r_next) {
+ nmp = rep->r_nmp;
+ if (rep->r_mrep || (rep->r_flags & R_SOFTTERM))
+ continue;
+ if (nfs_sigintr(nmp, rep, rep->r_procp)) {
+ rep->r_flags |= R_SOFTTERM;
+ continue;
+ }
+ if (rep->r_rtt >= 0) {
+ rep->r_rtt++;
+ if (nmp->nm_flag & NFSMNT_DUMBTIMR)
+ timeo = nmp->nm_timeo;
+ else
+ timeo = NFS_RTO(nmp, proct[rep->r_procnum]);
+ if (nmp->nm_timeouts > 0)
+ timeo *= nfs_backoff[nmp->nm_timeouts - 1];
+ if (rep->r_rtt <= timeo)
+ continue;
+ if (nmp->nm_timeouts < 8)
+ nmp->nm_timeouts++;
+ }
+ /*
+ * Check for server not responding
+ */
+ if ((rep->r_flags & R_TPRINTFMSG) == 0 &&
+ rep->r_rexmit > nmp->nm_deadthresh) {
+ nfs_msg(rep->r_procp,
+ nmp->nm_mountp->mnt_stat.f_mntfromname,
+ "not responding");
+ rep->r_flags |= R_TPRINTFMSG;
+ }
+ if (rep->r_rexmit >= rep->r_retry) { /* too many */
+ nfsstats.rpctimeouts++;
+ rep->r_flags |= R_SOFTTERM;
+ continue;
+ }
+ if (nmp->nm_sotype != SOCK_DGRAM) {
+ if (++rep->r_rexmit > NFS_MAXREXMIT)
+ rep->r_rexmit = NFS_MAXREXMIT;
+ continue;
+ }
+ if ((so = nmp->nm_so) == NULL)
+ continue;
+
+ /*
+ * If there is enough space and the window allows..
+ * Resend it
+ * Set r_rtt to -1 in case we fail to send it now.
+ */
+ rep->r_rtt = -1;
+ if (sbspace(&so->so_snd) >= rep->r_mreq->m_pkthdr.len &&
+ ((nmp->nm_flag & NFSMNT_DUMBTIMR) ||
+ (rep->r_flags & R_SENT) ||
+ nmp->nm_sent < nmp->nm_cwnd) &&
+ (m = m_copym(rep->r_mreq, 0, M_COPYALL, M_DONTWAIT))){
+ if ((nmp->nm_flag & NFSMNT_NOCONN) == 0)
+ error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, m,
+ (struct mbuf *)0, (struct mbuf *)0);
+ else
+ error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, m,
+ nmp->nm_nam, (struct mbuf *)0);
+ if (error) {
+ if (NFSIGNORE_SOERROR(nmp->nm_soflags, error))
+ so->so_error = 0;
+ } else {
+ /*
+ * Iff first send, start timing
+ * else turn timing off, backoff timer
+ * and divide congestion window by 2.
+ */
+ if (rep->r_flags & R_SENT) {
+ rep->r_flags &= ~R_TIMING;
+ if (++rep->r_rexmit > NFS_MAXREXMIT)
+ rep->r_rexmit = NFS_MAXREXMIT;
+ nmp->nm_cwnd >>= 1;
+ if (nmp->nm_cwnd < NFS_CWNDSCALE)
+ nmp->nm_cwnd = NFS_CWNDSCALE;
+ nfsstats.rpcretries++;
+ } else {
+ rep->r_flags |= R_SENT;
+ nmp->nm_sent += NFS_CWNDSCALE;
+ }
+ rep->r_rtt = 0;
+ }
+ }
+ }
+
+ /*
+ * Call the nqnfs server timer once a second to handle leases.
+ */
+ if (lasttime != time.tv_sec) {
+ lasttime = time.tv_sec;
+ nqnfs_serverd();
+ }
+ splx(s);
+ timeout(nfs_timer, (void *)0, hz / NFS_HZ);
+}
+
+/*
+ * Test for a termination condition pending on the process.
+ * This is used for NFSMNT_INT mounts.
+ */
+int
+nfs_sigintr(nmp, rep, p)
+ struct nfsmount *nmp;
+ struct nfsreq *rep;
+ register struct proc *p;
+{
+
+ if (rep && (rep->r_flags & R_SOFTTERM))
+ return (EINTR);
+ if (!(nmp->nm_flag & NFSMNT_INT))
+ return (0);
+ if (p && p->p_siglist &&
+ (((p->p_siglist & ~p->p_sigmask) & ~p->p_sigignore) &
+ NFSINT_SIGMASK))
+ return (EINTR);
+ return (0);
+}
+
+/*
+ * Lock a socket against others.
+ * Necessary for STREAM sockets to ensure you get an entire rpc request/reply
+ * and also to avoid race conditions between the processes with nfs requests
+ * in progress when a reconnect is necessary.
+ */
+int
+nfs_sndlock(flagp, rep)
+ register int *flagp;
+ struct nfsreq *rep;
+{
+ struct proc *p;
+ int slpflag = 0, slptimeo = 0;
+
+ if (rep) {
+ p = rep->r_procp;
+ if (rep->r_nmp->nm_flag & NFSMNT_INT)
+ slpflag = PCATCH;
+ } else
+ p = (struct proc *)0;
+ while (*flagp & NFSMNT_SNDLOCK) {
+ if (nfs_sigintr(rep->r_nmp, rep, p))
+ return (EINTR);
+ *flagp |= NFSMNT_WANTSND;
+ (void) tsleep((caddr_t)flagp, slpflag | (PZERO - 1), "nfsndlck",
+ slptimeo);
+ if (slpflag == PCATCH) {
+ slpflag = 0;
+ slptimeo = 2 * hz;
+ }
+ }
+ *flagp |= NFSMNT_SNDLOCK;
+ return (0);
+}
+
+/*
+ * Unlock the stream socket for others.
+ */
+void
+nfs_sndunlock(flagp)
+ register int *flagp;
+{
+
+ if ((*flagp & NFSMNT_SNDLOCK) == 0)
+ panic("nfs sndunlock");
+ *flagp &= ~NFSMNT_SNDLOCK;
+ if (*flagp & NFSMNT_WANTSND) {
+ *flagp &= ~NFSMNT_WANTSND;
+ wakeup((caddr_t)flagp);
+ }
+}
+
+int
+nfs_rcvlock(rep)
+ register struct nfsreq *rep;
+{
+ register int *flagp = &rep->r_nmp->nm_flag;
+ int slpflag, slptimeo = 0;
+
+ if (*flagp & NFSMNT_INT)
+ slpflag = PCATCH;
+ else
+ slpflag = 0;
+ while (*flagp & NFSMNT_RCVLOCK) {
+ if (nfs_sigintr(rep->r_nmp, rep, rep->r_procp))
+ return (EINTR);
+ *flagp |= NFSMNT_WANTRCV;
+ (void) tsleep((caddr_t)flagp, slpflag | (PZERO - 1), "nfsrcvlk",
+ slptimeo);
+ if (slpflag == PCATCH) {
+ slpflag = 0;
+ slptimeo = 2 * hz;
+ }
+ }
+ *flagp |= NFSMNT_RCVLOCK;
+ return (0);
+}
+
+/*
+ * Unlock the stream socket for others.
+ */
+void
+nfs_rcvunlock(flagp)
+ register int *flagp;
+{
+
+ if ((*flagp & NFSMNT_RCVLOCK) == 0)
+ panic("nfs rcvunlock");
+ *flagp &= ~NFSMNT_RCVLOCK;
+ if (*flagp & NFSMNT_WANTRCV) {
+ *flagp &= ~NFSMNT_WANTRCV;
+ wakeup((caddr_t)flagp);
+ }
+}
+
+/*
+ * Check for badly aligned mbuf data areas and
+ * realign data in an mbuf list by copying the data areas up, as required.
+ */
+void
+nfs_realign(m, hsiz)
+ register struct mbuf *m;
+ int hsiz;
+{
+ register struct mbuf *m2;
+ register int siz, mlen, olen;
+ register caddr_t tcp, fcp;
+ struct mbuf *mnew;
+
+ while (m) {
+ /*
+ * This never happens for UDP, rarely happens for TCP
+ * but frequently happens for iso transport.
+ */
+ if ((m->m_len & 0x3) || (mtod(m, int) & 0x3)) {
+ olen = m->m_len;
+ fcp = mtod(m, caddr_t);
+ if ((int)fcp & 0x3) {
+ m->m_flags &= ~M_PKTHDR;
+ if (m->m_flags & M_EXT)
+ m->m_data = m->m_ext.ext_buf +
+ ((m->m_ext.ext_size - olen) & ~0x3);
+ else
+ m->m_data = m->m_dat;
+ }
+ m->m_len = 0;
+ tcp = mtod(m, caddr_t);
+ mnew = m;
+ m2 = m->m_next;
+
+ /*
+ * If possible, only put the first invariant part
+ * of the RPC header in the first mbuf.
+ */
+ mlen = M_TRAILINGSPACE(m);
+ if (olen <= hsiz && mlen > hsiz)
+ mlen = hsiz;
+
+ /*
+ * Loop through the mbuf list consolidating data.
+ */
+ while (m) {
+ while (olen > 0) {
+ if (mlen == 0) {
+ m2->m_flags &= ~M_PKTHDR;
+ if (m2->m_flags & M_EXT)
+ m2->m_data = m2->m_ext.ext_buf;
+ else
+ m2->m_data = m2->m_dat;
+ m2->m_len = 0;
+ mlen = M_TRAILINGSPACE(m2);
+ tcp = mtod(m2, caddr_t);
+ mnew = m2;
+ m2 = m2->m_next;
+ }
+ siz = min(mlen, olen);
+ if (tcp != fcp)
+ bcopy(fcp, tcp, siz);
+ mnew->m_len += siz;
+ mlen -= siz;
+ olen -= siz;
+ tcp += siz;
+ fcp += siz;
+ }
+ m = m->m_next;
+ if (m) {
+ olen = m->m_len;
+ fcp = mtod(m, caddr_t);
+ }
+ }
+
+ /*
+ * Finally, set m_len == 0 for any trailing mbufs that have
+ * been copied out of.
+ */
+ while (m2) {
+ m2->m_len = 0;
+ m2 = m2->m_next;
+ }
+ return;
+ }
+ m = m->m_next;
+ }
+}
+
+/*
+ * Socket upcall routine for the nfsd sockets.
+ * The caddr_t arg is a pointer to the "struct nfssvc_sock".
+ * Essentially do as much as possible non-blocking, else punt and it will
+ * be called with M_WAIT from an nfsd.
+ */
+void
+nfsrv_rcv(so, arg, waitflag)
+ struct socket *so;
+ caddr_t arg;
+ int waitflag;
+{
+ register struct nfssvc_sock *slp = (struct nfssvc_sock *)arg;
+ register struct mbuf *m;
+ struct mbuf *mp, *nam;
+ struct uio auio;
+ int flags, error;
+
+ if ((slp->ns_flag & SLP_VALID) == 0)
+ return;
+#ifdef notdef
+ /*
+ * Define this to test for nfsds handling this under heavy load.
+ */
+ if (waitflag == M_DONTWAIT) {
+ slp->ns_flag |= SLP_NEEDQ; goto dorecs;
+ }
+#endif
+ auio.uio_procp = NULL;
+ if (so->so_type == SOCK_STREAM) {
+ /*
+ * If there are already records on the queue, defer soreceive()
+ * to an nfsd so that there is feedback to the TCP layer that
+ * the nfs servers are heavily loaded.
+ */
+ if (slp->ns_rec && waitflag == M_DONTWAIT) {
+ slp->ns_flag |= SLP_NEEDQ;
+ goto dorecs;
+ }
+
+ /*
+ * Do soreceive().
+ */
+ auio.uio_resid = 1000000000;
+ flags = MSG_DONTWAIT;
+ error = soreceive(so, &nam, &auio, &mp, (struct mbuf **)0, &flags);
+ if (error || mp == (struct mbuf *)0) {
+ if (error == EWOULDBLOCK)
+ slp->ns_flag |= SLP_NEEDQ;
+ else
+ slp->ns_flag |= SLP_DISCONN;
+ goto dorecs;
+ }
+ m = mp;
+ if (slp->ns_rawend) {
+ slp->ns_rawend->m_next = m;
+ slp->ns_cc += 1000000000 - auio.uio_resid;
+ } else {
+ slp->ns_raw = m;
+ slp->ns_cc = 1000000000 - auio.uio_resid;
+ }
+ while (m->m_next)
+ m = m->m_next;
+ slp->ns_rawend = m;
+
+ /*
+ * Now try and parse record(s) out of the raw stream data.
+ */
+ if (error = nfsrv_getstream(slp, waitflag)) {
+ if (error == EPERM)
+ slp->ns_flag |= SLP_DISCONN;
+ else
+ slp->ns_flag |= SLP_NEEDQ;
+ }
+ } else {
+ do {
+ auio.uio_resid = 1000000000;
+ flags = MSG_DONTWAIT;
+ error = soreceive(so, &nam, &auio, &mp,
+ (struct mbuf **)0, &flags);
+ if (mp) {
+ nfs_realign(mp, 10 * NFSX_UNSIGNED);
+ if (nam) {
+ m = nam;
+ m->m_next = mp;
+ } else
+ m = mp;
+ if (slp->ns_recend)
+ slp->ns_recend->m_nextpkt = m;
+ else
+ slp->ns_rec = m;
+ slp->ns_recend = m;
+ m->m_nextpkt = (struct mbuf *)0;
+ }
+ if (error) {
+ if ((so->so_proto->pr_flags & PR_CONNREQUIRED)
+ && error != EWOULDBLOCK) {
+ slp->ns_flag |= SLP_DISCONN;
+ goto dorecs;
+ }
+ }
+ } while (mp);
+ }
+
+ /*
+ * Now try and process the request records, non-blocking.
+ */
+dorecs:
+ if (waitflag == M_DONTWAIT &&
+ (slp->ns_rec || (slp->ns_flag & (SLP_NEEDQ | SLP_DISCONN))))
+ nfsrv_wakenfsd(slp);
+}
+
+/*
+ * Try and extract an RPC request from the mbuf data list received on a
+ * stream socket. The "waitflag" argument indicates whether or not it
+ * can sleep.
+ */
+int
+nfsrv_getstream(slp, waitflag)
+ register struct nfssvc_sock *slp;
+ int waitflag;
+{
+ register struct mbuf *m;
+ register char *cp1, *cp2;
+ register int len;
+ struct mbuf *om, *m2, *recm = 0;
+ u_long recmark;
+
+ if (slp->ns_flag & SLP_GETSTREAM)
+ panic("nfs getstream");
+ slp->ns_flag |= SLP_GETSTREAM;
+ for (;;) {
+ if (slp->ns_reclen == 0) {
+ if (slp->ns_cc < NFSX_UNSIGNED) {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (0);
+ }
+ m = slp->ns_raw;
+ if (m->m_len >= NFSX_UNSIGNED) {
+ bcopy(mtod(m, caddr_t), (caddr_t)&recmark, NFSX_UNSIGNED);
+ m->m_data += NFSX_UNSIGNED;
+ m->m_len -= NFSX_UNSIGNED;
+ } else {
+ cp1 = (caddr_t)&recmark;
+ cp2 = mtod(m, caddr_t);
+ while (cp1 < ((caddr_t)&recmark) + NFSX_UNSIGNED) {
+ while (m->m_len == 0) {
+ m = m->m_next;
+ cp2 = mtod(m, caddr_t);
+ }
+ *cp1++ = *cp2++;
+ m->m_data++;
+ m->m_len--;
+ }
+ }
+ slp->ns_cc -= NFSX_UNSIGNED;
+ slp->ns_reclen = ntohl(recmark) & ~0x80000000;
+ if (slp->ns_reclen < NFS_MINPACKET || slp->ns_reclen > NFS_MAXPACKET) {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (EPERM);
+ }
+ }
+
+ /*
+ * Now get the record part.
+ */
+ if (slp->ns_cc == slp->ns_reclen) {
+ recm = slp->ns_raw;
+ slp->ns_raw = slp->ns_rawend = (struct mbuf *)0;
+ slp->ns_cc = slp->ns_reclen = 0;
+ } else if (slp->ns_cc > slp->ns_reclen) {
+ len = 0;
+ m = slp->ns_raw;
+ om = (struct mbuf *)0;
+ while (len < slp->ns_reclen) {
+ if ((len + m->m_len) > slp->ns_reclen) {
+ m2 = m_copym(m, 0, slp->ns_reclen - len,
+ waitflag);
+ if (m2) {
+ if (om) {
+ om->m_next = m2;
+ recm = slp->ns_raw;
+ } else
+ recm = m2;
+ m->m_data += slp->ns_reclen - len;
+ m->m_len -= slp->ns_reclen - len;
+ len = slp->ns_reclen;
+ } else {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (EWOULDBLOCK);
+ }
+ } else if ((len + m->m_len) == slp->ns_reclen) {
+ om = m;
+ len += m->m_len;
+ m = m->m_next;
+ recm = slp->ns_raw;
+ om->m_next = (struct mbuf *)0;
+ } else {
+ om = m;
+ len += m->m_len;
+ m = m->m_next;
+ }
+ }
+ slp->ns_raw = m;
+ slp->ns_cc -= len;
+ slp->ns_reclen = 0;
+ } else {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (0);
+ }
+ nfs_realign(recm, 10 * NFSX_UNSIGNED);
+ if (slp->ns_recend)
+ slp->ns_recend->m_nextpkt = recm;
+ else
+ slp->ns_rec = recm;
+ slp->ns_recend = recm;
+ }
+}
+
+/*
+ * Parse an RPC header.
+ */
+int
+nfsrv_dorec(slp, nd)
+ register struct nfssvc_sock *slp;
+ register struct nfsd *nd;
+{
+ register struct mbuf *m;
+ int error;
+
+ if ((slp->ns_flag & SLP_VALID) == 0 ||
+ (m = slp->ns_rec) == (struct mbuf *)0)
+ return (ENOBUFS);
+ if (slp->ns_rec = m->m_nextpkt)
+ m->m_nextpkt = (struct mbuf *)0;
+ else
+ slp->ns_recend = (struct mbuf *)0;
+ if (m->m_type == MT_SONAME) {
+ nd->nd_nam = m;
+ nd->nd_md = nd->nd_mrep = m->m_next;
+ m->m_next = (struct mbuf *)0;
+ } else {
+ nd->nd_nam = (struct mbuf *)0;
+ nd->nd_md = nd->nd_mrep = m;
+ }
+ nd->nd_dpos = mtod(nd->nd_md, caddr_t);
+ if (error = nfs_getreq(nd, TRUE)) {
+ m_freem(nd->nd_nam);
+ return (error);
+ }
+ return (0);
+}
+
+/*
+ * Parse an RPC request
+ * - verify it
+ * - fill in the cred struct.
+ */
+int
+nfs_getreq(nd, has_header)
+ register struct nfsd *nd;
+ int has_header;
+{
+ register int len, i;
+ register u_long *tl;
+ register long t1;
+ struct uio uio;
+ struct iovec iov;
+ caddr_t dpos, cp2;
+ u_long nfsvers, auth_type;
+ int error = 0, nqnfs = 0;
+ struct mbuf *mrep, *md;
+
+ mrep = nd->nd_mrep;
+ md = nd->nd_md;
+ dpos = nd->nd_dpos;
+ if (has_header) {
+ nfsm_dissect(tl, u_long *, 10*NFSX_UNSIGNED);
+ nd->nd_retxid = *tl++;
+ if (*tl++ != rpc_call) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ } else {
+ nfsm_dissect(tl, u_long *, 8*NFSX_UNSIGNED);
+ }
+ nd->nd_repstat = 0;
+ if (*tl++ != rpc_vers) {
+ nd->nd_repstat = ERPCMISMATCH;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ nfsvers = nfs_vers;
+ if (*tl != nfs_prog) {
+ if (*tl == nqnfs_prog) {
+ nqnfs++;
+ nfsvers = nqnfs_vers;
+ } else {
+ nd->nd_repstat = EPROGUNAVAIL;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ }
+ tl++;
+ if (*tl++ != nfsvers) {
+ nd->nd_repstat = EPROGMISMATCH;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ nd->nd_procnum = fxdr_unsigned(u_long, *tl++);
+ if (nd->nd_procnum == NFSPROC_NULL)
+ return (0);
+ if (nd->nd_procnum >= NFS_NPROCS ||
+ (!nqnfs && nd->nd_procnum > NFSPROC_STATFS) ||
+ (*tl != rpc_auth_unix && *tl != rpc_auth_kerb)) {
+ nd->nd_repstat = EPROCUNAVAIL;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ auth_type = *tl++;
+ len = fxdr_unsigned(int, *tl++);
+ if (len < 0 || len > RPCAUTH_MAXSIZ) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+
+ /*
+ * Handle auth_unix or auth_kerb.
+ */
+ if (auth_type == rpc_auth_unix) {
+ len = fxdr_unsigned(int, *++tl);
+ if (len < 0 || len > NFS_MAXNAMLEN) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ nfsm_adv(nfsm_rndup(len));
+ nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED);
+ nd->nd_cr.cr_uid = fxdr_unsigned(uid_t, *tl++);
+ nd->nd_cr.cr_gid = fxdr_unsigned(gid_t, *tl++);
+ len = fxdr_unsigned(int, *tl);
+ if (len < 0 || len > RPCAUTH_UNIXGIDS) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ nfsm_dissect(tl, u_long *, (len + 2)*NFSX_UNSIGNED);
+ for (i = 1; i <= len; i++)
+ if (i < NGROUPS)
+ nd->nd_cr.cr_groups[i] = fxdr_unsigned(gid_t, *tl++);
+ else
+ tl++;
+ nd->nd_cr.cr_ngroups = (len >= NGROUPS) ? NGROUPS : (len + 1);
+ } else if (auth_type == rpc_auth_kerb) {
+ nd->nd_cr.cr_uid = fxdr_unsigned(uid_t, *tl++);
+ nd->nd_authlen = fxdr_unsigned(int, *tl);
+ uio.uio_resid = nfsm_rndup(nd->nd_authlen);
+ if (uio.uio_resid > (len - 2 * NFSX_UNSIGNED)) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ uio.uio_offset = 0;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_segflg = UIO_SYSSPACE;
+ iov.iov_base = (caddr_t)nd->nd_authstr;
+ iov.iov_len = RPCAUTH_MAXSIZ;
+ nfsm_mtouio(&uio, uio.uio_resid);
+ nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+ nd->nd_flag |= NFSD_NEEDAUTH;
+ }
+
+ /*
+ * Do we have any use for the verifier.
+ * According to the "Remote Procedure Call Protocol Spec." it
+ * should be AUTH_NULL, but some clients make it AUTH_UNIX?
+ * For now, just skip over it
+ */
+ len = fxdr_unsigned(int, *++tl);
+ if (len < 0 || len > RPCAUTH_MAXSIZ) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ if (len > 0) {
+ nfsm_adv(nfsm_rndup(len));
+ }
+
+ /*
+ * For nqnfs, get piggybacked lease request.
+ */
+ if (nqnfs && nd->nd_procnum != NQNFSPROC_EVICTED) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ nd->nd_nqlflag = fxdr_unsigned(int, *tl);
+ if (nd->nd_nqlflag) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ nd->nd_duration = fxdr_unsigned(int, *tl);
+ } else
+ nd->nd_duration = NQ_MINLEASE;
+ } else {
+ nd->nd_nqlflag = NQL_NOVAL;
+ nd->nd_duration = NQ_MINLEASE;
+ }
+ nd->nd_md = md;
+ nd->nd_dpos = dpos;
+ return (0);
+nfsmout:
+ return (error);
+}
+
+/*
+ * Search for a sleeping nfsd and wake it up.
+ * SIDE EFFECT: If none found, set NFSD_CHECKSLP flag, so that one of the
+ * running nfsds will go look for the work in the nfssvc_sock list.
+ */
+void
+nfsrv_wakenfsd(slp)
+ struct nfssvc_sock *slp;
+{
+ register struct nfsd *nd = nfsd_head.nd_next;
+
+ if ((slp->ns_flag & SLP_VALID) == 0)
+ return;
+ while (nd != (struct nfsd *)&nfsd_head) {
+ if (nd->nd_flag & NFSD_WAITING) {
+ nd->nd_flag &= ~NFSD_WAITING;
+ if (nd->nd_slp)
+ panic("nfsd wakeup");
+ slp->ns_sref++;
+ nd->nd_slp = slp;
+ wakeup((caddr_t)nd);
+ return;
+ }
+ nd = nd->nd_next;
+ }
+ slp->ns_flag |= SLP_DOREC;
+ nfsd_head.nd_flag |= NFSD_CHECKSLP;
+}
+
+int
+nfs_msg(p, server, msg)
+ struct proc *p;
+ char *server, *msg;
+{
+ tpr_t tpr;
+
+ if (p)
+ tpr = tprintf_open(p);
+ else
+ tpr = NULL;
+ tprintf(tpr, "nfs server %s: %s\n", server, msg);
+ tprintf_close(tpr);
+ return (0);
+}
diff --git a/sys/nfs/nfs_srvcache.c b/sys/nfs/nfs_srvcache.c
new file mode 100644
index 0000000..45bfe1b
--- /dev/null
+++ b/sys/nfs/nfs_srvcache.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_srvcache.c 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Reference: Chet Juszczak, "Improving the Performance and Correctness
+ * of an NFS Server", in Proc. Winter 1989 USENIX Conference,
+ * pages 53-63. San Diego, February 1989.
+ */
+#include <sys/param.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <netinet/in.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+#include <nfs/nfsm_subs.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsrvcache.h>
+#include <nfs/nqnfs.h>
+
+long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
+
+#define NFSRCHASH(xid) (((xid) + ((xid) >> 24)) & rheadhash)
+static struct nfsrvcache *nfsrvlruhead, **nfsrvlrutail = &nfsrvlruhead;
+static struct nfsrvcache **rheadhtbl;
+static u_long rheadhash;
+
+#define TRUE 1
+#define FALSE 0
+
+#define NETFAMILY(rp) \
+ (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
+
+/*
+ * Static array that defines which nfs rpc's are nonidempotent
+ */
+int nonidempotent[NFS_NPROCS] = {
+ FALSE,
+ FALSE,
+ TRUE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+};
+
+/* True iff the rpc reply is an nfs status ONLY! */
+static int repliesstatus[NFS_NPROCS] = {
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ FALSE,
+ TRUE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ TRUE,
+};
+
+/*
+ * Initialize the server request cache list
+ */
+void
+nfsrv_initcache()
+{
+
+ rheadhtbl = hashinit(desirednfsrvcache, M_NFSD, &rheadhash);
+}
+
+/*
+ * Look for the request in the cache
+ * If found then
+ * return action and optionally reply
+ * else
+ * insert it in the cache
+ *
+ * The rules are as follows:
+ * - if in progress, return DROP request
+ * - if completed within DELAY of the current time, return DROP it
+ * - if completed a longer time ago return REPLY if the reply was cached or
+ * return DOIT
+ * Update/add new request at end of lru list
+ */
+int
+nfsrv_getcache(nam, nd, repp)
+ struct mbuf *nam;
+ register struct nfsd *nd;
+ struct mbuf **repp;
+{
+ register struct nfsrvcache *rp, *rq, **rpp;
+ struct mbuf *mb;
+ struct sockaddr_in *saddr;
+ caddr_t bpos;
+ int ret;
+
+ if (nd->nd_nqlflag != NQL_NOVAL)
+ return (RC_DOIT);
+ rpp = &rheadhtbl[NFSRCHASH(nd->nd_retxid)];
+loop:
+ for (rp = *rpp; rp; rp = rp->rc_forw) {
+ if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
+ netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
+ if ((rp->rc_flag & RC_LOCKED) != 0) {
+ rp->rc_flag |= RC_WANTED;
+ (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
+ goto loop;
+ }
+ rp->rc_flag |= RC_LOCKED;
+ /* If not at end of LRU chain, move it there */
+ if (rp->rc_next) {
+ /* remove from LRU chain */
+ *rp->rc_prev = rp->rc_next;
+ rp->rc_next->rc_prev = rp->rc_prev;
+ /* and replace at end of it */
+ rp->rc_next = NULL;
+ rp->rc_prev = nfsrvlrutail;
+ *nfsrvlrutail = rp;
+ nfsrvlrutail = &rp->rc_next;
+ }
+ if (rp->rc_state == RC_UNUSED)
+ panic("nfsrv cache");
+ if (rp->rc_state == RC_INPROG) {
+ nfsstats.srvcache_inproghits++;
+ ret = RC_DROPIT;
+ } else if (rp->rc_flag & RC_REPSTATUS) {
+ nfsstats.srvcache_nonidemdonehits++;
+ nfs_rephead(0, nd, rp->rc_status,
+ 0, (u_quad_t *)0, repp, &mb, &bpos);
+ ret = RC_REPLY;
+ } else if (rp->rc_flag & RC_REPMBUF) {
+ nfsstats.srvcache_nonidemdonehits++;
+ *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
+ M_WAIT);
+ ret = RC_REPLY;
+ } else {
+ nfsstats.srvcache_idemdonehits++;
+ rp->rc_state = RC_INPROG;
+ ret = RC_DOIT;
+ }
+ rp->rc_flag &= ~RC_LOCKED;
+ if (rp->rc_flag & RC_WANTED) {
+ rp->rc_flag &= ~RC_WANTED;
+ wakeup((caddr_t)rp);
+ }
+ return (ret);
+ }
+ }
+ nfsstats.srvcache_misses++;
+ if (numnfsrvcache < desirednfsrvcache) {
+ rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
+ M_NFSD, M_WAITOK);
+ bzero((char *)rp, sizeof *rp);
+ numnfsrvcache++;
+ rp->rc_flag = RC_LOCKED;
+ } else {
+ rp = nfsrvlruhead;
+ while ((rp->rc_flag & RC_LOCKED) != 0) {
+ rp->rc_flag |= RC_WANTED;
+ (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
+ rp = nfsrvlruhead;
+ }
+ rp->rc_flag |= RC_LOCKED;
+ /* remove from hash chain */
+ if (rq = rp->rc_forw)
+ rq->rc_back = rp->rc_back;
+ *rp->rc_back = rq;
+ /* remove from LRU chain */
+ *rp->rc_prev = rp->rc_next;
+ rp->rc_next->rc_prev = rp->rc_prev;
+ if (rp->rc_flag & RC_REPMBUF)
+ m_freem(rp->rc_reply);
+ if (rp->rc_flag & RC_NAM)
+ MFREE(rp->rc_nam, mb);
+ rp->rc_flag &= (RC_LOCKED | RC_WANTED);
+ }
+ /* place at end of LRU list */
+ rp->rc_next = NULL;
+ rp->rc_prev = nfsrvlrutail;
+ *nfsrvlrutail = rp;
+ nfsrvlrutail = &rp->rc_next;
+ rp->rc_state = RC_INPROG;
+ rp->rc_xid = nd->nd_retxid;
+ saddr = mtod(nam, struct sockaddr_in *);
+ switch (saddr->sin_family) {
+ case AF_INET:
+ rp->rc_flag |= RC_INETADDR;
+ rp->rc_inetaddr = saddr->sin_addr.s_addr;
+ break;
+ case AF_ISO:
+ default:
+ rp->rc_flag |= RC_NAM;
+ rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT);
+ break;
+ };
+ rp->rc_proc = nd->nd_procnum;
+ /* insert into hash chain */
+ if (rq = *rpp)
+ rq->rc_back = &rp->rc_forw;
+ rp->rc_forw = rq;
+ rp->rc_back = rpp;
+ *rpp = rp;
+ rp->rc_flag &= ~RC_LOCKED;
+ if (rp->rc_flag & RC_WANTED) {
+ rp->rc_flag &= ~RC_WANTED;
+ wakeup((caddr_t)rp);
+ }
+ return (RC_DOIT);
+}
+
+/*
+ * Update a request cache entry after the rpc has been done
+ */
+void
+nfsrv_updatecache(nam, nd, repvalid, repmbuf)
+ struct mbuf *nam;
+ register struct nfsd *nd;
+ int repvalid;
+ struct mbuf *repmbuf;
+{
+ register struct nfsrvcache *rp;
+
+ if (nd->nd_nqlflag != NQL_NOVAL)
+ return;
+loop:
+ for (rp = rheadhtbl[NFSRCHASH(nd->nd_retxid)]; rp; rp = rp->rc_forw) {
+ if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
+ netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
+ if ((rp->rc_flag & RC_LOCKED) != 0) {
+ rp->rc_flag |= RC_WANTED;
+ (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
+ goto loop;
+ }
+ rp->rc_flag |= RC_LOCKED;
+ rp->rc_state = RC_DONE;
+ /*
+ * If we have a valid reply update status and save
+ * the reply for non-idempotent rpc's.
+ */
+ if (repvalid && nonidempotent[nd->nd_procnum]) {
+ if (repliesstatus[nd->nd_procnum]) {
+ rp->rc_status = nd->nd_repstat;
+ rp->rc_flag |= RC_REPSTATUS;
+ } else {
+ rp->rc_reply = m_copym(repmbuf,
+ 0, M_COPYALL, M_WAIT);
+ rp->rc_flag |= RC_REPMBUF;
+ }
+ }
+ rp->rc_flag &= ~RC_LOCKED;
+ if (rp->rc_flag & RC_WANTED) {
+ rp->rc_flag &= ~RC_WANTED;
+ wakeup((caddr_t)rp);
+ }
+ return;
+ }
+ }
+}
+
+/*
+ * Clean out the cache. Called when the last nfsd terminates.
+ */
+void
+nfsrv_cleancache()
+{
+ register struct nfsrvcache *rp, *nextrp;
+
+ for (rp = nfsrvlruhead; rp; rp = nextrp) {
+ nextrp = rp->rc_next;
+ free(rp, M_NFSD);
+ }
+ bzero((char *)rheadhtbl, (rheadhash + 1) * sizeof(void *));
+ nfsrvlruhead = NULL;
+ nfsrvlrutail = &nfsrvlruhead;
+ numnfsrvcache = 0;
+}
diff --git a/sys/nfs/nfs_subs.c b/sys/nfs/nfs_subs.c
new file mode 100644
index 0000000..d17cde7
--- /dev/null
+++ b/sys/nfs/nfs_subs.c
@@ -0,0 +1,1143 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_subs.c 8.3 (Berkeley) 1/4/94
+ */
+
+/*
+ * These functions support the macros and help fiddle mbuf chains for
+ * the nfs op functions. They do things like create the rpc header and
+ * copy data between mbuf chains and uio lists.
+ */
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfs.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nqnfs.h>
+#include <nfs/nfsrtt.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <netinet/in.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Data items converted to xdr at startup, since they are constant
+ * This is kinda hokey, but may save a little time doing byte swaps
+ */
+u_long nfs_procids[NFS_NPROCS];
+u_long nfs_xdrneg1;
+u_long rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr,
+ rpc_mismatch, rpc_auth_unix, rpc_msgaccepted, rpc_rejectedcred,
+ rpc_auth_kerb;
+u_long nfs_vers, nfs_prog, nfs_true, nfs_false;
+
+/* And other global data */
+static u_long nfs_xid = 0;
+enum vtype ntov_type[7] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON };
+extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
+extern struct nfsreq nfsreqh;
+extern int nqnfs_piggy[NFS_NPROCS];
+extern struct nfsrtt nfsrtt;
+extern time_t nqnfsstarttime;
+extern u_long nqnfs_prog, nqnfs_vers;
+extern int nqsrv_clockskew;
+extern int nqsrv_writeslack;
+extern int nqsrv_maxlease;
+
+/*
+ * Create the header for an rpc request packet
+ * The hsiz is the size of the rest of the nfs request header.
+ * (just used to decide if a cluster is a good idea)
+ */
+struct mbuf *
+nfsm_reqh(vp, procid, hsiz, bposp)
+ struct vnode *vp;
+ u_long procid;
+ int hsiz;
+ caddr_t *bposp;
+{
+ register struct mbuf *mb;
+ register u_long *tl;
+ register caddr_t bpos;
+ struct mbuf *mb2;
+ struct nfsmount *nmp;
+ int nqflag;
+
+ MGET(mb, M_WAIT, MT_DATA);
+ if (hsiz >= MINCLSIZE)
+ MCLGET(mb, M_WAIT);
+ mb->m_len = 0;
+ bpos = mtod(mb, caddr_t);
+
+ /*
+ * For NQNFS, add lease request.
+ */
+ if (vp) {
+ nmp = VFSTONFS(vp->v_mount);
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ nqflag = NQNFS_NEEDLEASE(vp, procid);
+ if (nqflag) {
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(nqflag);
+ *tl = txdr_unsigned(nmp->nm_leaseterm);
+ } else {
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = 0;
+ }
+ }
+ }
+ /* Finally, return values */
+ *bposp = bpos;
+ return (mb);
+}
+
+/*
+ * Build the RPC header and fill in the authorization info.
+ * The authorization string argument is only used when the credentials
+ * come from outside of the kernel.
+ * Returns the head of the mbuf list.
+ */
+struct mbuf *
+nfsm_rpchead(cr, nqnfs, procid, auth_type, auth_len, auth_str, mrest,
+ mrest_len, mbp, xidp)
+ register struct ucred *cr;
+ int nqnfs;
+ int procid;
+ int auth_type;
+ int auth_len;
+ char *auth_str;
+ struct mbuf *mrest;
+ int mrest_len;
+ struct mbuf **mbp;
+ u_long *xidp;
+{
+ register struct mbuf *mb;
+ register u_long *tl;
+ register caddr_t bpos;
+ register int i;
+ struct mbuf *mreq, *mb2;
+ int siz, grpsiz, authsiz;
+
+ authsiz = nfsm_rndup(auth_len);
+ if (auth_type == RPCAUTH_NQNFS)
+ authsiz += 2 * NFSX_UNSIGNED;
+ MGETHDR(mb, M_WAIT, MT_DATA);
+ if ((authsiz + 10*NFSX_UNSIGNED) >= MINCLSIZE) {
+ MCLGET(mb, M_WAIT);
+ } else if ((authsiz + 10*NFSX_UNSIGNED) < MHLEN) {
+ MH_ALIGN(mb, authsiz + 10*NFSX_UNSIGNED);
+ } else {
+ MH_ALIGN(mb, 8*NFSX_UNSIGNED);
+ }
+ mb->m_len = 0;
+ mreq = mb;
+ bpos = mtod(mb, caddr_t);
+
+ /*
+ * First the RPC header.
+ */
+ nfsm_build(tl, u_long *, 8*NFSX_UNSIGNED);
+ if (++nfs_xid == 0)
+ nfs_xid++;
+ *tl++ = *xidp = txdr_unsigned(nfs_xid);
+ *tl++ = rpc_call;
+ *tl++ = rpc_vers;
+ if (nqnfs) {
+ *tl++ = txdr_unsigned(NQNFS_PROG);
+ *tl++ = txdr_unsigned(NQNFS_VER1);
+ } else {
+ *tl++ = txdr_unsigned(NFS_PROG);
+ *tl++ = txdr_unsigned(NFS_VER2);
+ }
+ *tl++ = txdr_unsigned(procid);
+
+ /*
+ * And then the authorization cred.
+ */
+ *tl++ = txdr_unsigned(auth_type);
+ *tl = txdr_unsigned(authsiz);
+ switch (auth_type) {
+ case RPCAUTH_UNIX:
+ nfsm_build(tl, u_long *, auth_len);
+ *tl++ = 0; /* stamp ?? */
+ *tl++ = 0; /* NULL hostname */
+ *tl++ = txdr_unsigned(cr->cr_uid);
+ *tl++ = txdr_unsigned(cr->cr_groups[0]);
+ grpsiz = (auth_len >> 2) - 5;
+ *tl++ = txdr_unsigned(grpsiz);
+ for (i = 1; i <= grpsiz; i++)
+ *tl++ = txdr_unsigned(cr->cr_groups[i]);
+ break;
+ case RPCAUTH_NQNFS:
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(cr->cr_uid);
+ *tl = txdr_unsigned(auth_len);
+ siz = auth_len;
+ while (siz > 0) {
+ if (M_TRAILINGSPACE(mb) == 0) {
+ MGET(mb2, M_WAIT, MT_DATA);
+ if (siz >= MINCLSIZE)
+ MCLGET(mb2, M_WAIT);
+ mb->m_next = mb2;
+ mb = mb2;
+ mb->m_len = 0;
+ bpos = mtod(mb, caddr_t);
+ }
+ i = min(siz, M_TRAILINGSPACE(mb));
+ bcopy(auth_str, bpos, i);
+ mb->m_len += i;
+ auth_str += i;
+ bpos += i;
+ siz -= i;
+ }
+ if ((siz = (nfsm_rndup(auth_len) - auth_len)) > 0) {
+ for (i = 0; i < siz; i++)
+ *bpos++ = '\0';
+ mb->m_len += siz;
+ }
+ break;
+ };
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(RPCAUTH_NULL);
+ *tl = 0;
+ mb->m_next = mrest;
+ mreq->m_pkthdr.len = authsiz + 10*NFSX_UNSIGNED + mrest_len;
+ mreq->m_pkthdr.rcvif = (struct ifnet *)0;
+ *mbp = mb;
+ return (mreq);
+}
+
+/*
+ * copies mbuf chain to the uio scatter/gather list
+ */
+int
+nfsm_mbuftouio(mrep, uiop, siz, dpos)
+ struct mbuf **mrep;
+ register struct uio *uiop;
+ int siz;
+ caddr_t *dpos;
+{
+ register char *mbufcp, *uiocp;
+ register int xfer, left, len;
+ register struct mbuf *mp;
+ long uiosiz, rem;
+ int error = 0;
+
+ mp = *mrep;
+ mbufcp = *dpos;
+ len = mtod(mp, caddr_t)+mp->m_len-mbufcp;
+ rem = nfsm_rndup(siz)-siz;
+ while (siz > 0) {
+ if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
+ return (EFBIG);
+ left = uiop->uio_iov->iov_len;
+ uiocp = uiop->uio_iov->iov_base;
+ if (left > siz)
+ left = siz;
+ uiosiz = left;
+ while (left > 0) {
+ while (len == 0) {
+ mp = mp->m_next;
+ if (mp == NULL)
+ return (EBADRPC);
+ mbufcp = mtod(mp, caddr_t);
+ len = mp->m_len;
+ }
+ xfer = (left > len) ? len : left;
+#ifdef notdef
+ /* Not Yet.. */
+ if (uiop->uio_iov->iov_op != NULL)
+ (*(uiop->uio_iov->iov_op))
+ (mbufcp, uiocp, xfer);
+ else
+#endif
+ if (uiop->uio_segflg == UIO_SYSSPACE)
+ bcopy(mbufcp, uiocp, xfer);
+ else
+ copyout(mbufcp, uiocp, xfer);
+ left -= xfer;
+ len -= xfer;
+ mbufcp += xfer;
+ uiocp += xfer;
+ uiop->uio_offset += xfer;
+ uiop->uio_resid -= xfer;
+ }
+ if (uiop->uio_iov->iov_len <= siz) {
+ uiop->uio_iovcnt--;
+ uiop->uio_iov++;
+ } else {
+ uiop->uio_iov->iov_base += uiosiz;
+ uiop->uio_iov->iov_len -= uiosiz;
+ }
+ siz -= uiosiz;
+ }
+ *dpos = mbufcp;
+ *mrep = mp;
+ if (rem > 0) {
+ if (len < rem)
+ error = nfs_adv(mrep, dpos, rem, len);
+ else
+ *dpos += rem;
+ }
+ return (error);
+}
+
+/*
+ * copies a uio scatter/gather list to an mbuf chain...
+ */
+int
+nfsm_uiotombuf(uiop, mq, siz, bpos)
+ register struct uio *uiop;
+ struct mbuf **mq;
+ int siz;
+ caddr_t *bpos;
+{
+ register char *uiocp;
+ register struct mbuf *mp, *mp2;
+ register int xfer, left, mlen;
+ int uiosiz, clflg, rem;
+ char *cp;
+
+ if (siz > MLEN) /* or should it >= MCLBYTES ?? */
+ clflg = 1;
+ else
+ clflg = 0;
+ rem = nfsm_rndup(siz)-siz;
+ mp = mp2 = *mq;
+ while (siz > 0) {
+ if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
+ return (EINVAL);
+ left = uiop->uio_iov->iov_len;
+ uiocp = uiop->uio_iov->iov_base;
+ if (left > siz)
+ left = siz;
+ uiosiz = left;
+ while (left > 0) {
+ mlen = M_TRAILINGSPACE(mp);
+ if (mlen == 0) {
+ MGET(mp, M_WAIT, MT_DATA);
+ if (clflg)
+ MCLGET(mp, M_WAIT);
+ mp->m_len = 0;
+ mp2->m_next = mp;
+ mp2 = mp;
+ mlen = M_TRAILINGSPACE(mp);
+ }
+ xfer = (left > mlen) ? mlen : left;
+#ifdef notdef
+ /* Not Yet.. */
+ if (uiop->uio_iov->iov_op != NULL)
+ (*(uiop->uio_iov->iov_op))
+ (uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
+ else
+#endif
+ if (uiop->uio_segflg == UIO_SYSSPACE)
+ bcopy(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
+ else
+ copyin(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
+ mp->m_len += xfer;
+ left -= xfer;
+ uiocp += xfer;
+ uiop->uio_offset += xfer;
+ uiop->uio_resid -= xfer;
+ }
+ if (uiop->uio_iov->iov_len <= siz) {
+ uiop->uio_iovcnt--;
+ uiop->uio_iov++;
+ } else {
+ uiop->uio_iov->iov_base += uiosiz;
+ uiop->uio_iov->iov_len -= uiosiz;
+ }
+ siz -= uiosiz;
+ }
+ if (rem > 0) {
+ if (rem > M_TRAILINGSPACE(mp)) {
+ MGET(mp, M_WAIT, MT_DATA);
+ mp->m_len = 0;
+ mp2->m_next = mp;
+ }
+ cp = mtod(mp, caddr_t)+mp->m_len;
+ for (left = 0; left < rem; left++)
+ *cp++ = '\0';
+ mp->m_len += rem;
+ *bpos = cp;
+ } else
+ *bpos = mtod(mp, caddr_t)+mp->m_len;
+ *mq = mp;
+ return (0);
+}
+
+/*
+ * Help break down an mbuf chain by setting the first siz bytes contiguous
+ * pointed to by returned val.
+ * This is used by the macros nfsm_dissect and nfsm_dissecton for tough
+ * cases. (The macros use the vars. dpos and dpos2)
+ */
+int
+nfsm_disct(mdp, dposp, siz, left, cp2)
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ int siz;
+ int left;
+ caddr_t *cp2;
+{
+ register struct mbuf *mp, *mp2;
+ register int siz2, xfer;
+ register caddr_t p;
+
+ mp = *mdp;
+ while (left == 0) {
+ *mdp = mp = mp->m_next;
+ if (mp == NULL)
+ return (EBADRPC);
+ left = mp->m_len;
+ *dposp = mtod(mp, caddr_t);
+ }
+ if (left >= siz) {
+ *cp2 = *dposp;
+ *dposp += siz;
+ } else if (mp->m_next == NULL) {
+ return (EBADRPC);
+ } else if (siz > MHLEN) {
+ panic("nfs S too big");
+ } else {
+ MGET(mp2, M_WAIT, MT_DATA);
+ mp2->m_next = mp->m_next;
+ mp->m_next = mp2;
+ mp->m_len -= left;
+ mp = mp2;
+ *cp2 = p = mtod(mp, caddr_t);
+ bcopy(*dposp, p, left); /* Copy what was left */
+ siz2 = siz-left;
+ p += left;
+ mp2 = mp->m_next;
+ /* Loop around copying up the siz2 bytes */
+ while (siz2 > 0) {
+ if (mp2 == NULL)
+ return (EBADRPC);
+ xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
+ if (xfer > 0) {
+ bcopy(mtod(mp2, caddr_t), p, xfer);
+ NFSMADV(mp2, xfer);
+ mp2->m_len -= xfer;
+ p += xfer;
+ siz2 -= xfer;
+ }
+ if (siz2 > 0)
+ mp2 = mp2->m_next;
+ }
+ mp->m_len = siz;
+ *mdp = mp2;
+ *dposp = mtod(mp2, caddr_t);
+ }
+ return (0);
+}
+
+/*
+ * Advance the position in the mbuf chain.
+ */
+int
+nfs_adv(mdp, dposp, offs, left)
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ int offs;
+ int left;
+{
+ register struct mbuf *m;
+ register int s;
+
+ m = *mdp;
+ s = left;
+ while (s < offs) {
+ offs -= s;
+ m = m->m_next;
+ if (m == NULL)
+ return (EBADRPC);
+ s = m->m_len;
+ }
+ *mdp = m;
+ *dposp = mtod(m, caddr_t)+offs;
+ return (0);
+}
+
+/*
+ * Copy a string into mbufs for the hard cases...
+ */
+int
+nfsm_strtmbuf(mb, bpos, cp, siz)
+ struct mbuf **mb;
+ char **bpos;
+ char *cp;
+ long siz;
+{
+ register struct mbuf *m1 = 0, *m2;
+ long left, xfer, len, tlen;
+ u_long *tl;
+ int putsize;
+
+ putsize = 1;
+ m2 = *mb;
+ left = M_TRAILINGSPACE(m2);
+ if (left > 0) {
+ tl = ((u_long *)(*bpos));
+ *tl++ = txdr_unsigned(siz);
+ putsize = 0;
+ left -= NFSX_UNSIGNED;
+ m2->m_len += NFSX_UNSIGNED;
+ if (left > 0) {
+ bcopy(cp, (caddr_t) tl, left);
+ siz -= left;
+ cp += left;
+ m2->m_len += left;
+ left = 0;
+ }
+ }
+ /* Loop around adding mbufs */
+ while (siz > 0) {
+ MGET(m1, M_WAIT, MT_DATA);
+ if (siz > MLEN)
+ MCLGET(m1, M_WAIT);
+ m1->m_len = NFSMSIZ(m1);
+ m2->m_next = m1;
+ m2 = m1;
+ tl = mtod(m1, u_long *);
+ tlen = 0;
+ if (putsize) {
+ *tl++ = txdr_unsigned(siz);
+ m1->m_len -= NFSX_UNSIGNED;
+ tlen = NFSX_UNSIGNED;
+ putsize = 0;
+ }
+ if (siz < m1->m_len) {
+ len = nfsm_rndup(siz);
+ xfer = siz;
+ if (xfer < len)
+ *(tl+(xfer>>2)) = 0;
+ } else {
+ xfer = len = m1->m_len;
+ }
+ bcopy(cp, (caddr_t) tl, xfer);
+ m1->m_len = len+tlen;
+ siz -= xfer;
+ cp += xfer;
+ }
+ *mb = m1;
+ *bpos = mtod(m1, caddr_t)+m1->m_len;
+ return (0);
+}
+
+/*
+ * Called once to initialize data structures...
+ */
+int
+nfs_init()
+{
+ register int i;
+
+ nfsrtt.pos = 0;
+ rpc_vers = txdr_unsigned(RPC_VER2);
+ rpc_call = txdr_unsigned(RPC_CALL);
+ rpc_reply = txdr_unsigned(RPC_REPLY);
+ rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
+ rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
+ rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
+ rpc_autherr = txdr_unsigned(RPC_AUTHERR);
+ rpc_rejectedcred = txdr_unsigned(AUTH_REJECTCRED);
+ rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
+ rpc_auth_kerb = txdr_unsigned(RPCAUTH_NQNFS);
+ nfs_vers = txdr_unsigned(NFS_VER2);
+ nfs_prog = txdr_unsigned(NFS_PROG);
+ nfs_true = txdr_unsigned(TRUE);
+ nfs_false = txdr_unsigned(FALSE);
+ /* Loop thru nfs procids */
+ for (i = 0; i < NFS_NPROCS; i++)
+ nfs_procids[i] = txdr_unsigned(i);
+ /* Ensure async daemons disabled */
+ for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
+ nfs_iodwant[i] = (struct proc *)0;
+ TAILQ_INIT(&nfs_bufq);
+ nfs_xdrneg1 = txdr_unsigned(-1);
+ nfs_nhinit(); /* Init the nfsnode table */
+ nfsrv_init(0); /* Init server data structures */
+ nfsrv_initcache(); /* Init the server request cache */
+
+ /*
+ * Initialize the nqnfs server stuff.
+ */
+ if (nqnfsstarttime == 0) {
+ nqnfsstarttime = boottime.tv_sec + nqsrv_maxlease
+ + nqsrv_clockskew + nqsrv_writeslack;
+ NQLOADNOVRAM(nqnfsstarttime);
+ nqnfs_prog = txdr_unsigned(NQNFS_PROG);
+ nqnfs_vers = txdr_unsigned(NQNFS_VER1);
+ nqthead.th_head[0] = &nqthead;
+ nqthead.th_head[1] = &nqthead;
+ nqfhead = hashinit(NQLCHSZ, M_NQLEASE, &nqfheadhash);
+ }
+
+ /*
+ * Initialize reply list and start timer
+ */
+ nfsreqh.r_prev = nfsreqh.r_next = &nfsreqh;
+ nfs_timer();
+
+ return (0);
+}
+
+/*
+ * Attribute cache routines.
+ * nfs_loadattrcache() - loads or updates the cache contents from attributes
+ * that are on the mbuf list
+ * nfs_getattrcache() - returns valid attributes if found in cache, returns
+ * error otherwise
+ */
+
+/*
+ * Load the attribute cache (that lives in the nfsnode entry) with
+ * the values on the mbuf list and
+ * Iff vap not NULL
+ * copy the attributes to *vaper
+ */
+int
+nfs_loadattrcache(vpp, mdp, dposp, vaper)
+ struct vnode **vpp;
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ struct vattr *vaper;
+{
+ register struct vnode *vp = *vpp;
+ register struct vattr *vap;
+ register struct nfsv2_fattr *fp;
+ extern int (**spec_nfsv2nodeop_p)();
+ register struct nfsnode *np, *nq, **nhpp;
+ register long t1;
+ caddr_t dpos, cp2;
+ int error = 0, isnq;
+ struct mbuf *md;
+ enum vtype vtyp;
+ u_short vmode;
+ long rdev;
+ struct timespec mtime;
+ struct vnode *nvp;
+
+ md = *mdp;
+ dpos = *dposp;
+ t1 = (mtod(md, caddr_t) + md->m_len) - dpos;
+ isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ if (error = nfsm_disct(&md, &dpos, NFSX_FATTR(isnq), t1, &cp2))
+ return (error);
+ fp = (struct nfsv2_fattr *)cp2;
+ vtyp = nfstov_type(fp->fa_type);
+ vmode = fxdr_unsigned(u_short, fp->fa_mode);
+ if (vtyp == VNON || vtyp == VREG)
+ vtyp = IFTOVT(vmode);
+ if (isnq) {
+ rdev = fxdr_unsigned(long, fp->fa_nqrdev);
+ fxdr_nqtime(&fp->fa_nqmtime, &mtime);
+ } else {
+ rdev = fxdr_unsigned(long, fp->fa_nfsrdev);
+ fxdr_nfstime(&fp->fa_nfsmtime, &mtime);
+ }
+ /*
+ * If v_type == VNON it is a new node, so fill in the v_type,
+ * n_mtime fields. Check to see if it represents a special
+ * device, and if so, check for a possible alias. Once the
+ * correct vnode has been obtained, fill in the rest of the
+ * information.
+ */
+ np = VTONFS(vp);
+ if (vp->v_type == VNON) {
+ if (vtyp == VCHR && rdev == 0xffffffff)
+ vp->v_type = vtyp = VFIFO;
+ else
+ vp->v_type = vtyp;
+ if (vp->v_type == VFIFO) {
+#ifdef FIFO
+ extern int (**fifo_nfsv2nodeop_p)();
+ vp->v_op = fifo_nfsv2nodeop_p;
+#else
+ return (EOPNOTSUPP);
+#endif /* FIFO */
+ }
+ if (vp->v_type == VCHR || vp->v_type == VBLK) {
+ vp->v_op = spec_nfsv2nodeop_p;
+ if (nvp = checkalias(vp, (dev_t)rdev, vp->v_mount)) {
+ /*
+ * Discard unneeded vnode, but save its nfsnode.
+ */
+ if (nq = np->n_forw)
+ nq->n_back = np->n_back;
+ *np->n_back = nq;
+ nvp->v_data = vp->v_data;
+ vp->v_data = NULL;
+ vp->v_op = spec_vnodeop_p;
+ vrele(vp);
+ vgone(vp);
+ /*
+ * Reinitialize aliased node.
+ */
+ np->n_vnode = nvp;
+ nhpp = (struct nfsnode **)nfs_hash(&np->n_fh);
+ if (nq = *nhpp)
+ nq->n_back = &np->n_forw;
+ np->n_forw = nq;
+ np->n_back = nhpp;
+ *nhpp = np;
+ *vpp = vp = nvp;
+ }
+ }
+ np->n_mtime = mtime.ts_sec;
+ }
+ vap = &np->n_vattr;
+ vap->va_type = vtyp;
+ vap->va_mode = (vmode & 07777);
+ vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
+ vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
+ vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
+ vap->va_rdev = (dev_t)rdev;
+ vap->va_mtime = mtime;
+ vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+ if (isnq) {
+ fxdr_hyper(&fp->fa_nqsize, &vap->va_size);
+ vap->va_blocksize = fxdr_unsigned(long, fp->fa_nqblocksize);
+ fxdr_hyper(&fp->fa_nqbytes, &vap->va_bytes);
+ vap->va_fileid = fxdr_unsigned(long, fp->fa_nqfileid);
+ fxdr_nqtime(&fp->fa_nqatime, &vap->va_atime);
+ vap->va_flags = fxdr_unsigned(u_long, fp->fa_nqflags);
+ fxdr_nqtime(&fp->fa_nqctime, &vap->va_ctime);
+ vap->va_gen = fxdr_unsigned(u_long, fp->fa_nqgen);
+ fxdr_hyper(&fp->fa_nqfilerev, &vap->va_filerev);
+ } else {
+ vap->va_size = fxdr_unsigned(u_long, fp->fa_nfssize);
+ vap->va_blocksize = fxdr_unsigned(long, fp->fa_nfsblocksize);
+ vap->va_bytes = fxdr_unsigned(long, fp->fa_nfsblocks) * NFS_FABLKSIZE;
+ vap->va_fileid = fxdr_unsigned(long, fp->fa_nfsfileid);
+ fxdr_nfstime(&fp->fa_nfsatime, &vap->va_atime);
+ vap->va_flags = 0;
+ vap->va_ctime.ts_sec = fxdr_unsigned(long, fp->fa_nfsctime.nfs_sec);
+ vap->va_ctime.ts_nsec = 0;
+ vap->va_gen = fxdr_unsigned(u_long, fp->fa_nfsctime.nfs_usec);
+ vap->va_filerev = 0;
+ }
+ if (vap->va_size != np->n_size) {
+ if (vap->va_type == VREG) {
+ if (np->n_flag & NMODIFIED) {
+ if (vap->va_size < np->n_size)
+ vap->va_size = np->n_size;
+ else
+ np->n_size = vap->va_size;
+ } else
+ np->n_size = vap->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else
+ np->n_size = vap->va_size;
+ }
+ np->n_attrstamp = time.tv_sec;
+ *dposp = dpos;
+ *mdp = md;
+ if (vaper != NULL) {
+ bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
+#ifdef notdef
+ if ((np->n_flag & NMODIFIED) && np->n_size > vap->va_size)
+ if (np->n_size > vap->va_size)
+ vaper->va_size = np->n_size;
+#endif
+ if (np->n_flag & NCHG) {
+ if (np->n_flag & NACC) {
+ vaper->va_atime.ts_sec = np->n_atim.tv_sec;
+ vaper->va_atime.ts_nsec =
+ np->n_atim.tv_usec * 1000;
+ }
+ if (np->n_flag & NUPD) {
+ vaper->va_mtime.ts_sec = np->n_mtim.tv_sec;
+ vaper->va_mtime.ts_nsec =
+ np->n_mtim.tv_usec * 1000;
+ }
+ }
+ }
+ return (0);
+}
+
+/*
+ * Check the time stamp
+ * If the cache is valid, copy contents to *vap and return 0
+ * otherwise return an error
+ */
+int
+nfs_getattrcache(vp, vaper)
+ register struct vnode *vp;
+ struct vattr *vaper;
+{
+ register struct nfsnode *np = VTONFS(vp);
+ register struct vattr *vap;
+
+ if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQLOOKLEASE) {
+ if (!NQNFS_CKCACHABLE(vp, NQL_READ) || np->n_attrstamp == 0) {
+ nfsstats.attrcache_misses++;
+ return (ENOENT);
+ }
+ } else if ((time.tv_sec - np->n_attrstamp) >= NFS_ATTRTIMEO(np)) {
+ nfsstats.attrcache_misses++;
+ return (ENOENT);
+ }
+ nfsstats.attrcache_hits++;
+ vap = &np->n_vattr;
+ if (vap->va_size != np->n_size) {
+ if (vap->va_type == VREG) {
+ if (np->n_flag & NMODIFIED) {
+ if (vap->va_size < np->n_size)
+ vap->va_size = np->n_size;
+ else
+ np->n_size = vap->va_size;
+ } else
+ np->n_size = vap->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else
+ np->n_size = vap->va_size;
+ }
+ bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(struct vattr));
+#ifdef notdef
+ if ((np->n_flag & NMODIFIED) == 0) {
+ np->n_size = vaper->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else if (np->n_size > vaper->va_size)
+ if (np->n_size > vaper->va_size)
+ vaper->va_size = np->n_size;
+#endif
+ if (np->n_flag & NCHG) {
+ if (np->n_flag & NACC) {
+ vaper->va_atime.ts_sec = np->n_atim.tv_sec;
+ vaper->va_atime.ts_nsec = np->n_atim.tv_usec * 1000;
+ }
+ if (np->n_flag & NUPD) {
+ vaper->va_mtime.ts_sec = np->n_mtim.tv_sec;
+ vaper->va_mtime.ts_nsec = np->n_mtim.tv_usec * 1000;
+ }
+ }
+ return (0);
+}
+
+/*
+ * Set up nameidata for a lookup() call and do it
+ */
+int
+nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p)
+ register struct nameidata *ndp;
+ fhandle_t *fhp;
+ int len;
+ struct nfssvc_sock *slp;
+ struct mbuf *nam;
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ struct proc *p;
+{
+ register int i, rem;
+ register struct mbuf *md;
+ register char *fromcp, *tocp;
+ struct vnode *dp;
+ int error, rdonly;
+ struct componentname *cnp = &ndp->ni_cnd;
+
+ MALLOC(cnp->cn_pnbuf, char *, len + 1, M_NAMEI, M_WAITOK);
+ /*
+ * Copy the name from the mbuf list to ndp->ni_pnbuf
+ * and set the various ndp fields appropriately.
+ */
+ fromcp = *dposp;
+ tocp = cnp->cn_pnbuf;
+ md = *mdp;
+ rem = mtod(md, caddr_t) + md->m_len - fromcp;
+ cnp->cn_hash = 0;
+ for (i = 0; i < len; i++) {
+ while (rem == 0) {
+ md = md->m_next;
+ if (md == NULL) {
+ error = EBADRPC;
+ goto out;
+ }
+ fromcp = mtod(md, caddr_t);
+ rem = md->m_len;
+ }
+ if (*fromcp == '\0' || *fromcp == '/') {
+ error = EINVAL;
+ goto out;
+ }
+ cnp->cn_hash += (unsigned char)*fromcp;
+ *tocp++ = *fromcp++;
+ rem--;
+ }
+ *tocp = '\0';
+ *mdp = md;
+ *dposp = fromcp;
+ len = nfsm_rndup(len)-len;
+ if (len > 0) {
+ if (rem >= len)
+ *dposp += len;
+ else if (error = nfs_adv(mdp, dposp, len, rem))
+ goto out;
+ }
+ ndp->ni_pathlen = tocp - cnp->cn_pnbuf;
+ cnp->cn_nameptr = cnp->cn_pnbuf;
+ /*
+ * Extract and set starting directory.
+ */
+ if (error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
+ nam, &rdonly))
+ goto out;
+ if (dp->v_type != VDIR) {
+ vrele(dp);
+ error = ENOTDIR;
+ goto out;
+ }
+ ndp->ni_startdir = dp;
+ if (rdonly)
+ cnp->cn_flags |= (NOCROSSMOUNT | RDONLY);
+ else
+ cnp->cn_flags |= NOCROSSMOUNT;
+ /*
+ * And call lookup() to do the real work
+ */
+ cnp->cn_proc = p;
+ if (error = lookup(ndp))
+ goto out;
+ /*
+ * Check for encountering a symbolic link
+ */
+ if (cnp->cn_flags & ISSYMLINK) {
+ if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
+ vput(ndp->ni_dvp);
+ else
+ vrele(ndp->ni_dvp);
+ vput(ndp->ni_vp);
+ ndp->ni_vp = NULL;
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * Check for saved name request
+ */
+ if (cnp->cn_flags & (SAVENAME | SAVESTART)) {
+ cnp->cn_flags |= HASBUF;
+ return (0);
+ }
+out:
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ return (error);
+}
+
+/*
+ * A fiddled version of m_adj() that ensures null fill to a long
+ * boundary and only trims off the back end
+ */
+void
+nfsm_adj(mp, len, nul)
+ struct mbuf *mp;
+ register int len;
+ int nul;
+{
+ register struct mbuf *m;
+ register int count, i;
+ register char *cp;
+
+ /*
+ * Trim from tail. Scan the mbuf chain,
+ * calculating its length and finding the last mbuf.
+ * If the adjustment only affects this mbuf, then just
+ * adjust and return. Otherwise, rescan and truncate
+ * after the remaining size.
+ */
+ count = 0;
+ m = mp;
+ for (;;) {
+ count += m->m_len;
+ if (m->m_next == (struct mbuf *)0)
+ break;
+ m = m->m_next;
+ }
+ if (m->m_len > len) {
+ m->m_len -= len;
+ if (nul > 0) {
+ cp = mtod(m, caddr_t)+m->m_len-nul;
+ for (i = 0; i < nul; i++)
+ *cp++ = '\0';
+ }
+ return;
+ }
+ count -= len;
+ if (count < 0)
+ count = 0;
+ /*
+ * Correct length for chain is "count".
+ * Find the mbuf with last data, adjust its length,
+ * and toss data from remaining mbufs on chain.
+ */
+ for (m = mp; m; m = m->m_next) {
+ if (m->m_len >= count) {
+ m->m_len = count;
+ if (nul > 0) {
+ cp = mtod(m, caddr_t)+m->m_len-nul;
+ for (i = 0; i < nul; i++)
+ *cp++ = '\0';
+ }
+ break;
+ }
+ count -= m->m_len;
+ }
+ while (m = m->m_next)
+ m->m_len = 0;
+}
+
+/*
+ * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
+ * - look up fsid in mount list (if not found ret error)
+ * - get vp and export rights by calling VFS_FHTOVP()
+ * - if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
+ * - if not lockflag unlock it with VOP_UNLOCK()
+ */
+int
+nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp)
+ fhandle_t *fhp;
+ int lockflag;
+ struct vnode **vpp;
+ struct ucred *cred;
+ struct nfssvc_sock *slp;
+ struct mbuf *nam;
+ int *rdonlyp;
+{
+ register struct mount *mp;
+ register struct nfsuid *uidp;
+ register int i;
+ struct ucred *credanon;
+ int error, exflags;
+
+ *vpp = (struct vnode *)0;
+ if ((mp = getvfs(&fhp->fh_fsid)) == NULL)
+ return (ESTALE);
+ if (error = VFS_FHTOVP(mp, &fhp->fh_fid, nam, vpp, &exflags, &credanon))
+ return (error);
+ /*
+ * Check/setup credentials.
+ */
+ if (exflags & MNT_EXKERB) {
+ uidp = slp->ns_uidh[NUIDHASH(cred->cr_uid)];
+ while (uidp) {
+ if (uidp->nu_uid == cred->cr_uid)
+ break;
+ uidp = uidp->nu_hnext;
+ }
+ if (uidp) {
+ cred->cr_uid = uidp->nu_cr.cr_uid;
+ for (i = 0; i < uidp->nu_cr.cr_ngroups; i++)
+ cred->cr_groups[i] = uidp->nu_cr.cr_groups[i];
+ } else {
+ vput(*vpp);
+ return (NQNFS_AUTHERR);
+ }
+ } else if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
+ cred->cr_uid = credanon->cr_uid;
+ for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
+ cred->cr_groups[i] = credanon->cr_groups[i];
+ }
+ if (exflags & MNT_EXRDONLY)
+ *rdonlyp = 1;
+ else
+ *rdonlyp = 0;
+ if (!lockflag)
+ VOP_UNLOCK(*vpp);
+ return (0);
+}
+
+/*
+ * This function compares two net addresses by family and returns TRUE
+ * if they are the same host.
+ * If there is any doubt, return FALSE.
+ * The AF_INET family is handled as a special case so that address mbufs
+ * don't need to be saved to store "struct in_addr", which is only 4 bytes.
+ */
+int
+netaddr_match(family, haddr, nam)
+ int family;
+ union nethostaddr *haddr;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *inetaddr;
+
+ switch (family) {
+ case AF_INET:
+ inetaddr = mtod(nam, struct sockaddr_in *);
+ if (inetaddr->sin_family == AF_INET &&
+ inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
+ return (1);
+ break;
+#ifdef ISO
+ case AF_ISO:
+ {
+ register struct sockaddr_iso *isoaddr1, *isoaddr2;
+
+ isoaddr1 = mtod(nam, struct sockaddr_iso *);
+ isoaddr2 = mtod(haddr->had_nam, struct sockaddr_iso *);
+ if (isoaddr1->siso_family == AF_ISO &&
+ isoaddr1->siso_nlen > 0 &&
+ isoaddr1->siso_nlen == isoaddr2->siso_nlen &&
+ SAME_ISOADDR(isoaddr1, isoaddr2))
+ return (1);
+ break;
+ }
+#endif /* ISO */
+ default:
+ break;
+ };
+ return (0);
+}
diff --git a/sys/nfs/nfs_syscalls.c b/sys/nfs/nfs_syscalls.c
new file mode 100644
index 0000000..b00a225
--- /dev/null
+++ b/sys/nfs/nfs_syscalls.c
@@ -0,0 +1,883 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_syscalls.c 8.3 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/namei.h>
+#include <sys/syslog.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsrvcache.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nqnfs.h>
+#include <nfs/nfsrtt.h>
+
+void nfsrv_zapsock __P((struct nfssvc_sock *));
+
+/* Global defs. */
+extern u_long nfs_prog, nfs_vers;
+extern int (*nfsrv_procs[NFS_NPROCS])();
+extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
+extern int nfs_numasync;
+extern time_t nqnfsstarttime;
+extern struct nfsrv_req nsrvq_head;
+extern struct nfsd nfsd_head;
+extern int nqsrv_writeslack;
+extern int nfsrtton;
+struct nfssvc_sock *nfs_udpsock, *nfs_cltpsock;
+int nuidhash_max = NFS_MAXUIDHASH;
+static int nfs_numnfsd = 0;
+int nfsd_waiting = 0;
+static int notstarted = 1;
+static int modify_flag = 0;
+static struct nfsdrt nfsdrt;
+void nfsrv_cleancache(), nfsrv_rcv(), nfsrv_wakenfsd(), nfs_sndunlock();
+static void nfsd_rt();
+void nfsrv_slpderef(), nfsrv_init();
+
+#define TRUE 1
+#define FALSE 0
+
+static int nfs_asyncdaemon[NFS_MAXASYNCDAEMON];
+/*
+ * NFS server system calls
+ * getfh() lives here too, but maybe should move to kern/vfs_syscalls.c
+ */
+
+/*
+ * Get file handle system call
+ */
+struct getfh_args {
+ char *fname;
+ fhandle_t *fhp;
+};
+int
+getfh(p, uap, retval)
+ struct proc *p;
+ register struct getfh_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ fhandle_t fh;
+ int error;
+ struct nameidata nd;
+
+ /*
+ * Must be super user
+ */
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ bzero((caddr_t)&fh, sizeof(fh));
+ fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+ error = VFS_VPTOFH(vp, &fh.fh_fid);
+ vput(vp);
+ if (error)
+ return (error);
+ error = copyout((caddr_t)&fh, (caddr_t)uap->fhp, sizeof (fh));
+ return (error);
+}
+
+static struct nfssvc_sock nfssvc_sockhead;
+
+/*
+ * Nfs server psuedo system call for the nfsd's
+ * Based on the flag value it either:
+ * - adds a socket to the selection list
+ * - remains in the kernel as an nfsd
+ * - remains in the kernel as an nfsiod
+ */
+struct nfssvc_args {
+ int flag;
+ caddr_t argp;
+};
+int
+nfssvc(p, uap, retval)
+ struct proc *p;
+ register struct nfssvc_args *uap;
+ int *retval;
+{
+ struct nameidata nd;
+ struct file *fp;
+ struct mbuf *nam;
+ struct nfsd_args nfsdarg;
+ struct nfsd_srvargs nfsd_srvargs, *nsd = &nfsd_srvargs;
+ struct nfsd_cargs ncd;
+ struct nfsd *nfsd;
+ struct nfssvc_sock *slp;
+ struct nfsuid *nuidp, **nuh;
+ struct nfsmount *nmp;
+ int error;
+
+ /*
+ * Must be super user
+ */
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ while (nfssvc_sockhead.ns_flag & SLP_INIT) {
+ nfssvc_sockhead.ns_flag |= SLP_WANTINIT;
+ (void) tsleep((caddr_t)&nfssvc_sockhead, PSOCK, "nfsd init", 0);
+ }
+ if (uap->flag & NFSSVC_BIOD)
+ error = nfssvc_iod(p);
+ else if (uap->flag & NFSSVC_MNTD) {
+ if (error = copyin(uap->argp, (caddr_t)&ncd, sizeof (ncd)))
+ return (error);
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
+ ncd.ncd_dirp, p);
+ if (error = namei(&nd))
+ return (error);
+ if ((nd.ni_vp->v_flag & VROOT) == 0)
+ error = EINVAL;
+ nmp = VFSTONFS(nd.ni_vp->v_mount);
+ vput(nd.ni_vp);
+ if (error)
+ return (error);
+ if ((nmp->nm_flag & NFSMNT_MNTD) &&
+ (uap->flag & NFSSVC_GOTAUTH) == 0)
+ return (0);
+ nmp->nm_flag |= NFSMNT_MNTD;
+ error = nqnfs_clientd(nmp, p->p_ucred, &ncd, uap->flag,
+ uap->argp, p);
+ } else if (uap->flag & NFSSVC_ADDSOCK) {
+ if (error = copyin(uap->argp, (caddr_t)&nfsdarg,
+ sizeof(nfsdarg)))
+ return (error);
+ if (error = getsock(p->p_fd, nfsdarg.sock, &fp))
+ return (error);
+ /*
+ * Get the client address for connected sockets.
+ */
+ if (nfsdarg.name == NULL || nfsdarg.namelen == 0)
+ nam = (struct mbuf *)0;
+ else if (error = sockargs(&nam, nfsdarg.name, nfsdarg.namelen,
+ MT_SONAME))
+ return (error);
+ error = nfssvc_addsock(fp, nam);
+ } else {
+ if (error = copyin(uap->argp, (caddr_t)nsd, sizeof (*nsd)))
+ return (error);
+ if ((uap->flag & NFSSVC_AUTHIN) && (nfsd = nsd->nsd_nfsd) &&
+ (nfsd->nd_slp->ns_flag & SLP_VALID)) {
+ slp = nfsd->nd_slp;
+
+ /*
+ * First check to see if another nfsd has already
+ * added this credential.
+ */
+ nuidp = slp->ns_uidh[NUIDHASH(nsd->nsd_uid)];
+ while (nuidp) {
+ if (nuidp->nu_uid == nsd->nsd_uid)
+ break;
+ nuidp = nuidp->nu_hnext;
+ }
+ if (!nuidp) {
+ /*
+ * Nope, so we will.
+ */
+ if (slp->ns_numuids < nuidhash_max) {
+ slp->ns_numuids++;
+ nuidp = (struct nfsuid *)
+ malloc(sizeof (struct nfsuid), M_NFSUID,
+ M_WAITOK);
+ } else
+ nuidp = (struct nfsuid *)0;
+ if ((slp->ns_flag & SLP_VALID) == 0) {
+ if (nuidp)
+ free((caddr_t)nuidp, M_NFSUID);
+ } else {
+ if (nuidp == (struct nfsuid *)0) {
+ nuidp = slp->ns_lruprev;
+ remque(nuidp);
+ if (nuidp->nu_hprev)
+ nuidp->nu_hprev->nu_hnext =
+ nuidp->nu_hnext;
+ if (nuidp->nu_hnext)
+ nuidp->nu_hnext->nu_hprev =
+ nuidp->nu_hprev;
+ }
+ nuidp->nu_cr = nsd->nsd_cr;
+ if (nuidp->nu_cr.cr_ngroups > NGROUPS)
+ nuidp->nu_cr.cr_ngroups = NGROUPS;
+ nuidp->nu_cr.cr_ref = 1;
+ nuidp->nu_uid = nsd->nsd_uid;
+ insque(nuidp, (struct nfsuid *)slp);
+ nuh = &slp->ns_uidh[NUIDHASH(nsd->nsd_uid)];
+ if (nuidp->nu_hnext = *nuh)
+ nuidp->nu_hnext->nu_hprev = nuidp;
+ nuidp->nu_hprev = (struct nfsuid *)0;
+ *nuh = nuidp;
+ }
+ }
+ }
+ if ((uap->flag & NFSSVC_AUTHINFAIL) && (nfsd = nsd->nsd_nfsd))
+ nfsd->nd_flag |= NFSD_AUTHFAIL;
+ error = nfssvc_nfsd(nsd, uap->argp, p);
+ }
+ if (error == EINTR || error == ERESTART)
+ error = 0;
+ return (error);
+}
+
+/*
+ * Adds a socket to the list for servicing by nfsds.
+ */
+int
+nfssvc_addsock(fp, mynam)
+ struct file *fp;
+ struct mbuf *mynam;
+{
+ register struct mbuf *m;
+ register int siz;
+ register struct nfssvc_sock *slp;
+ register struct socket *so;
+ struct nfssvc_sock *tslp;
+ int error, s;
+
+ so = (struct socket *)fp->f_data;
+ tslp = (struct nfssvc_sock *)0;
+ /*
+ * Add it to the list, as required.
+ */
+ if (so->so_proto->pr_protocol == IPPROTO_UDP) {
+ tslp = nfs_udpsock;
+ if (tslp->ns_flag & SLP_VALID) {
+ m_freem(mynam);
+ return (EPERM);
+ }
+#ifdef ISO
+ } else if (so->so_proto->pr_protocol == ISOPROTO_CLTP) {
+ tslp = nfs_cltpsock;
+ if (tslp->ns_flag & SLP_VALID) {
+ m_freem(mynam);
+ return (EPERM);
+ }
+#endif /* ISO */
+ }
+ if (so->so_type == SOCK_STREAM)
+ siz = NFS_MAXPACKET + sizeof (u_long);
+ else
+ siz = NFS_MAXPACKET;
+ if (error = soreserve(so, siz, siz)) {
+ m_freem(mynam);
+ return (error);
+ }
+
+ /*
+ * Set protocol specific options { for now TCP only } and
+ * reserve some space. For datagram sockets, this can get called
+ * repeatedly for the same socket, but that isn't harmful.
+ */
+ if (so->so_type == SOCK_STREAM) {
+ MGET(m, M_WAIT, MT_SOOPTS);
+ *mtod(m, int *) = 1;
+ m->m_len = sizeof(int);
+ sosetopt(so, SOL_SOCKET, SO_KEEPALIVE, m);
+ }
+ if (so->so_proto->pr_domain->dom_family == AF_INET &&
+ so->so_proto->pr_protocol == IPPROTO_TCP) {
+ MGET(m, M_WAIT, MT_SOOPTS);
+ *mtod(m, int *) = 1;
+ m->m_len = sizeof(int);
+ sosetopt(so, IPPROTO_TCP, TCP_NODELAY, m);
+ }
+ so->so_rcv.sb_flags &= ~SB_NOINTR;
+ so->so_rcv.sb_timeo = 0;
+ so->so_snd.sb_flags &= ~SB_NOINTR;
+ so->so_snd.sb_timeo = 0;
+ if (tslp)
+ slp = tslp;
+ else {
+ slp = (struct nfssvc_sock *)
+ malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
+ bzero((caddr_t)slp, sizeof (struct nfssvc_sock));
+ slp->ns_prev = nfssvc_sockhead.ns_prev;
+ slp->ns_prev->ns_next = slp;
+ slp->ns_next = &nfssvc_sockhead;
+ nfssvc_sockhead.ns_prev = slp;
+ slp->ns_lrunext = slp->ns_lruprev = (struct nfsuid *)slp;
+ }
+ slp->ns_so = so;
+ slp->ns_nam = mynam;
+ fp->f_count++;
+ slp->ns_fp = fp;
+ s = splnet();
+ so->so_upcallarg = (caddr_t)slp;
+ so->so_upcall = nfsrv_rcv;
+ slp->ns_flag = (SLP_VALID | SLP_NEEDQ);
+ nfsrv_wakenfsd(slp);
+ splx(s);
+ return (0);
+}
+
+/*
+ * Called by nfssvc() for nfsds. Just loops around servicing rpc requests
+ * until it is killed by a signal.
+ */
+int
+nfssvc_nfsd(nsd, argp, p)
+ struct nfsd_srvargs *nsd;
+ caddr_t argp;
+ struct proc *p;
+{
+ register struct mbuf *m, *nam2;
+ register int siz;
+ register struct nfssvc_sock *slp;
+ register struct socket *so;
+ register int *solockp;
+ struct nfsd *nd = nsd->nsd_nfsd;
+ struct mbuf *mreq, *nam;
+ struct timeval starttime;
+ struct nfsuid *uidp;
+ int error = 0, cacherep, s;
+ int sotype;
+
+ s = splnet();
+ if (nd == (struct nfsd *)0) {
+ nsd->nsd_nfsd = nd = (struct nfsd *)
+ malloc(sizeof (struct nfsd), M_NFSD, M_WAITOK);
+ bzero((caddr_t)nd, sizeof (struct nfsd));
+ nd->nd_procp = p;
+ nd->nd_cr.cr_ref = 1;
+ insque(nd, &nfsd_head);
+ nd->nd_nqlflag = NQL_NOVAL;
+ nfs_numnfsd++;
+ }
+ /*
+ * Loop getting rpc requests until SIGKILL.
+ */
+ for (;;) {
+ if ((nd->nd_flag & NFSD_REQINPROG) == 0) {
+ while (nd->nd_slp == (struct nfssvc_sock *)0 &&
+ (nfsd_head.nd_flag & NFSD_CHECKSLP) == 0) {
+ nd->nd_flag |= NFSD_WAITING;
+ nfsd_waiting++;
+ error = tsleep((caddr_t)nd, PSOCK | PCATCH, "nfsd", 0);
+ nfsd_waiting--;
+ if (error)
+ goto done;
+ }
+ if (nd->nd_slp == (struct nfssvc_sock *)0 &&
+ (nfsd_head.nd_flag & NFSD_CHECKSLP)) {
+ slp = nfssvc_sockhead.ns_next;
+ while (slp != &nfssvc_sockhead) {
+ if ((slp->ns_flag & (SLP_VALID | SLP_DOREC))
+ == (SLP_VALID | SLP_DOREC)) {
+ slp->ns_flag &= ~SLP_DOREC;
+ slp->ns_sref++;
+ nd->nd_slp = slp;
+ break;
+ }
+ slp = slp->ns_next;
+ }
+ if (slp == &nfssvc_sockhead)
+ nfsd_head.nd_flag &= ~NFSD_CHECKSLP;
+ }
+ if ((slp = nd->nd_slp) == (struct nfssvc_sock *)0)
+ continue;
+ if (slp->ns_flag & SLP_VALID) {
+ if (slp->ns_flag & SLP_DISCONN)
+ nfsrv_zapsock(slp);
+ else if (slp->ns_flag & SLP_NEEDQ) {
+ slp->ns_flag &= ~SLP_NEEDQ;
+ (void) nfs_sndlock(&slp->ns_solock,
+ (struct nfsreq *)0);
+ nfsrv_rcv(slp->ns_so, (caddr_t)slp,
+ M_WAIT);
+ nfs_sndunlock(&slp->ns_solock);
+ }
+ error = nfsrv_dorec(slp, nd);
+ nd->nd_flag |= NFSD_REQINPROG;
+ }
+ } else {
+ error = 0;
+ slp = nd->nd_slp;
+ }
+ if (error || (slp->ns_flag & SLP_VALID) == 0) {
+ nd->nd_slp = (struct nfssvc_sock *)0;
+ nd->nd_flag &= ~NFSD_REQINPROG;
+ nfsrv_slpderef(slp);
+ continue;
+ }
+ splx(s);
+ so = slp->ns_so;
+ sotype = so->so_type;
+ starttime = time;
+ if (so->so_proto->pr_flags & PR_CONNREQUIRED)
+ solockp = &slp->ns_solock;
+ else
+ solockp = (int *)0;
+ /*
+ * nam == nam2 for connectionless protocols such as UDP
+ * nam2 == NULL for connection based protocols to disable
+ * recent request caching.
+ */
+ if (nam2 = nd->nd_nam) {
+ nam = nam2;
+ cacherep = RC_CHECKIT;
+ } else {
+ nam = slp->ns_nam;
+ cacherep = RC_DOIT;
+ }
+
+ /*
+ * Check to see if authorization is needed.
+ */
+ if (nd->nd_flag & NFSD_NEEDAUTH) {
+ static int logauth = 0;
+
+ nd->nd_flag &= ~NFSD_NEEDAUTH;
+ /*
+ * Check for a mapping already installed.
+ */
+ uidp = slp->ns_uidh[NUIDHASH(nd->nd_cr.cr_uid)];
+ while (uidp) {
+ if (uidp->nu_uid == nd->nd_cr.cr_uid)
+ break;
+ uidp = uidp->nu_hnext;
+ }
+ if (!uidp) {
+ nsd->nsd_uid = nd->nd_cr.cr_uid;
+ if (nam2 && logauth++ == 0)
+ log(LOG_WARNING, "Kerberized NFS using UDP\n");
+ nsd->nsd_haddr =
+ mtod(nam, struct sockaddr_in *)->sin_addr.s_addr;
+ nsd->nsd_authlen = nd->nd_authlen;
+ if (copyout(nd->nd_authstr, nsd->nsd_authstr,
+ nd->nd_authlen) == 0 &&
+ copyout((caddr_t)nsd, argp, sizeof (*nsd)) == 0)
+ return (ENEEDAUTH);
+ cacherep = RC_DROPIT;
+ }
+ }
+ if (cacherep == RC_CHECKIT)
+ cacherep = nfsrv_getcache(nam2, nd, &mreq);
+
+ /*
+ * Check for just starting up for NQNFS and send
+ * fake "try again later" replies to the NQNFS clients.
+ */
+ if (notstarted && nqnfsstarttime <= time.tv_sec) {
+ if (modify_flag) {
+ nqnfsstarttime = time.tv_sec + nqsrv_writeslack;
+ modify_flag = 0;
+ } else
+ notstarted = 0;
+ }
+ if (notstarted) {
+ if (nd->nd_nqlflag == NQL_NOVAL)
+ cacherep = RC_DROPIT;
+ else if (nd->nd_procnum != NFSPROC_WRITE) {
+ nd->nd_procnum = NFSPROC_NOOP;
+ nd->nd_repstat = NQNFS_TRYLATER;
+ cacherep = RC_DOIT;
+ } else
+ modify_flag = 1;
+ } else if (nd->nd_flag & NFSD_AUTHFAIL) {
+ nd->nd_flag &= ~NFSD_AUTHFAIL;
+ nd->nd_procnum = NFSPROC_NOOP;
+ nd->nd_repstat = NQNFS_AUTHERR;
+ cacherep = RC_DOIT;
+ }
+
+ switch (cacherep) {
+ case RC_DOIT:
+ error = (*(nfsrv_procs[nd->nd_procnum]))(nd,
+ nd->nd_mrep, nd->nd_md, nd->nd_dpos, &nd->nd_cr,
+ nam, &mreq);
+ if (nd->nd_cr.cr_ref != 1) {
+ printf("nfssvc cref=%d\n", nd->nd_cr.cr_ref);
+ panic("nfssvc cref");
+ }
+ if (error) {
+ if (nd->nd_procnum != NQNFSPROC_VACATED)
+ nfsstats.srv_errs++;
+ if (nam2) {
+ nfsrv_updatecache(nam2, nd, FALSE, mreq);
+ m_freem(nam2);
+ }
+ break;
+ }
+ nfsstats.srvrpccnt[nd->nd_procnum]++;
+ if (nam2)
+ nfsrv_updatecache(nam2, nd, TRUE, mreq);
+ nd->nd_mrep = (struct mbuf *)0;
+ case RC_REPLY:
+ m = mreq;
+ siz = 0;
+ while (m) {
+ siz += m->m_len;
+ m = m->m_next;
+ }
+ if (siz <= 0 || siz > NFS_MAXPACKET) {
+ printf("mbuf siz=%d\n",siz);
+ panic("Bad nfs svc reply");
+ }
+ m = mreq;
+ m->m_pkthdr.len = siz;
+ m->m_pkthdr.rcvif = (struct ifnet *)0;
+ /*
+ * For stream protocols, prepend a Sun RPC
+ * Record Mark.
+ */
+ if (sotype == SOCK_STREAM) {
+ M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
+ *mtod(m, u_long *) = htonl(0x80000000 | siz);
+ }
+ if (solockp)
+ (void) nfs_sndlock(solockp, (struct nfsreq *)0);
+ if (slp->ns_flag & SLP_VALID)
+ error = nfs_send(so, nam2, m, (struct nfsreq *)0);
+ else {
+ error = EPIPE;
+ m_freem(m);
+ }
+ if (nfsrtton)
+ nfsd_rt(&starttime, sotype, nd, nam, cacherep);
+ if (nam2)
+ MFREE(nam2, m);
+ if (nd->nd_mrep)
+ m_freem(nd->nd_mrep);
+ if (error == EPIPE)
+ nfsrv_zapsock(slp);
+ if (solockp)
+ nfs_sndunlock(solockp);
+ if (error == EINTR || error == ERESTART) {
+ nfsrv_slpderef(slp);
+ s = splnet();
+ goto done;
+ }
+ break;
+ case RC_DROPIT:
+ if (nfsrtton)
+ nfsd_rt(&starttime, sotype, nd, nam, cacherep);
+ m_freem(nd->nd_mrep);
+ m_freem(nam2);
+ break;
+ };
+ s = splnet();
+ if (nfsrv_dorec(slp, nd)) {
+ nd->nd_flag &= ~NFSD_REQINPROG;
+ nd->nd_slp = (struct nfssvc_sock *)0;
+ nfsrv_slpderef(slp);
+ }
+ }
+done:
+ remque(nd);
+ splx(s);
+ free((caddr_t)nd, M_NFSD);
+ nsd->nsd_nfsd = (struct nfsd *)0;
+ if (--nfs_numnfsd == 0)
+ nfsrv_init(TRUE); /* Reinitialize everything */
+ return (error);
+}
+
+/*
+ * Asynchronous I/O daemons for client nfs.
+ * They do read-ahead and write-behind operations on the block I/O cache.
+ * Never returns unless it fails or gets killed.
+ */
+int
+nfssvc_iod(p)
+ struct proc *p;
+{
+ register struct buf *bp;
+ register int i, myiod;
+ int error = 0;
+
+ /*
+ * Assign my position or return error if too many already running
+ */
+ myiod = -1;
+ for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
+ if (nfs_asyncdaemon[i] == 0) {
+ nfs_asyncdaemon[i]++;
+ myiod = i;
+ break;
+ }
+ if (myiod == -1)
+ return (EBUSY);
+ nfs_numasync++;
+ /*
+ * Just loop around doin our stuff until SIGKILL
+ */
+ for (;;) {
+ while (nfs_bufq.tqh_first == NULL && error == 0) {
+ nfs_iodwant[myiod] = p;
+ error = tsleep((caddr_t)&nfs_iodwant[myiod],
+ PWAIT | PCATCH, "nfsidl", 0);
+ }
+ while ((bp = nfs_bufq.tqh_first) != NULL) {
+ /* Take one off the front of the list */
+ TAILQ_REMOVE(&nfs_bufq, bp, b_freelist);
+ if (bp->b_flags & B_READ)
+ (void) nfs_doio(bp, bp->b_rcred, (struct proc *)0);
+ else
+ (void) nfs_doio(bp, bp->b_wcred, (struct proc *)0);
+ }
+ if (error) {
+ nfs_asyncdaemon[myiod] = 0;
+ nfs_numasync--;
+ return (error);
+ }
+ }
+}
+
+/*
+ * Shut down a socket associated with an nfssvc_sock structure.
+ * Should be called with the send lock set, if required.
+ * The trick here is to increment the sref at the start, so that the nfsds
+ * will stop using it and clear ns_flag at the end so that it will not be
+ * reassigned during cleanup.
+ */
+void
+nfsrv_zapsock(slp)
+ register struct nfssvc_sock *slp;
+{
+ register struct nfsuid *nuidp, *onuidp;
+ register int i;
+ struct socket *so;
+ struct file *fp;
+ struct mbuf *m;
+
+ slp->ns_flag &= ~SLP_ALLFLAGS;
+ if (fp = slp->ns_fp) {
+ slp->ns_fp = (struct file *)0;
+ so = slp->ns_so;
+ so->so_upcall = NULL;
+ soshutdown(so, 2);
+ closef(fp, (struct proc *)0);
+ if (slp->ns_nam)
+ MFREE(slp->ns_nam, m);
+ m_freem(slp->ns_raw);
+ m_freem(slp->ns_rec);
+ nuidp = slp->ns_lrunext;
+ while (nuidp != (struct nfsuid *)slp) {
+ onuidp = nuidp;
+ nuidp = nuidp->nu_lrunext;
+ free((caddr_t)onuidp, M_NFSUID);
+ }
+ slp->ns_lrunext = slp->ns_lruprev = (struct nfsuid *)slp;
+ for (i = 0; i < NUIDHASHSIZ; i++)
+ slp->ns_uidh[i] = (struct nfsuid *)0;
+ }
+}
+
+/*
+ * Get an authorization string for the uid by having the mount_nfs sitting
+ * on this mount point porpous out of the kernel and do it.
+ */
+int
+nfs_getauth(nmp, rep, cred, auth_type, auth_str, auth_len)
+ register struct nfsmount *nmp;
+ struct nfsreq *rep;
+ struct ucred *cred;
+ int *auth_type;
+ char **auth_str;
+ int *auth_len;
+{
+ int error = 0;
+
+ while ((nmp->nm_flag & NFSMNT_WAITAUTH) == 0) {
+ nmp->nm_flag |= NFSMNT_WANTAUTH;
+ (void) tsleep((caddr_t)&nmp->nm_authtype, PSOCK,
+ "nfsauth1", 2 * hz);
+ if (error = nfs_sigintr(nmp, rep, rep->r_procp)) {
+ nmp->nm_flag &= ~NFSMNT_WANTAUTH;
+ return (error);
+ }
+ }
+ nmp->nm_flag &= ~(NFSMNT_WAITAUTH | NFSMNT_WANTAUTH);
+ nmp->nm_authstr = *auth_str = (char *)malloc(RPCAUTH_MAXSIZ, M_TEMP, M_WAITOK);
+ nmp->nm_authuid = cred->cr_uid;
+ wakeup((caddr_t)&nmp->nm_authstr);
+
+ /*
+ * And wait for mount_nfs to do its stuff.
+ */
+ while ((nmp->nm_flag & NFSMNT_HASAUTH) == 0 && error == 0) {
+ (void) tsleep((caddr_t)&nmp->nm_authlen, PSOCK,
+ "nfsauth2", 2 * hz);
+ error = nfs_sigintr(nmp, rep, rep->r_procp);
+ }
+ if (nmp->nm_flag & NFSMNT_AUTHERR) {
+ nmp->nm_flag &= ~NFSMNT_AUTHERR;
+ error = EAUTH;
+ }
+ if (error)
+ free((caddr_t)*auth_str, M_TEMP);
+ else {
+ *auth_type = nmp->nm_authtype;
+ *auth_len = nmp->nm_authlen;
+ }
+ nmp->nm_flag &= ~NFSMNT_HASAUTH;
+ nmp->nm_flag |= NFSMNT_WAITAUTH;
+ if (nmp->nm_flag & NFSMNT_WANTAUTH) {
+ nmp->nm_flag &= ~NFSMNT_WANTAUTH;
+ wakeup((caddr_t)&nmp->nm_authtype);
+ }
+ return (error);
+}
+
+/*
+ * Derefence a server socket structure. If it has no more references and
+ * is no longer valid, you can throw it away.
+ */
+void
+nfsrv_slpderef(slp)
+ register struct nfssvc_sock *slp;
+{
+ if (--(slp->ns_sref) == 0 && (slp->ns_flag & SLP_VALID) == 0) {
+ slp->ns_prev->ns_next = slp->ns_next;
+ slp->ns_next->ns_prev = slp->ns_prev;
+ free((caddr_t)slp, M_NFSSVC);
+ }
+}
+
+/*
+ * Initialize the data structures for the server.
+ * Handshake with any new nfsds starting up to avoid any chance of
+ * corruption.
+ */
+void
+nfsrv_init(terminating)
+ int terminating;
+{
+ register struct nfssvc_sock *slp;
+ struct nfssvc_sock *oslp;
+
+ if (nfssvc_sockhead.ns_flag & SLP_INIT)
+ panic("nfsd init");
+ nfssvc_sockhead.ns_flag |= SLP_INIT;
+ if (terminating) {
+ slp = nfssvc_sockhead.ns_next;
+ while (slp != &nfssvc_sockhead) {
+ if (slp->ns_flag & SLP_VALID)
+ nfsrv_zapsock(slp);
+ slp->ns_next->ns_prev = slp->ns_prev;
+ slp->ns_prev->ns_next = slp->ns_next;
+ oslp = slp;
+ slp = slp->ns_next;
+ free((caddr_t)oslp, M_NFSSVC);
+ }
+ nfsrv_cleancache(); /* And clear out server cache */
+ }
+ nfs_udpsock = (struct nfssvc_sock *)
+ malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
+ bzero((caddr_t)nfs_udpsock, sizeof (struct nfssvc_sock));
+ nfs_cltpsock = (struct nfssvc_sock *)
+ malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
+ bzero((caddr_t)nfs_cltpsock, sizeof (struct nfssvc_sock));
+ nfssvc_sockhead.ns_next = nfs_udpsock;
+ nfs_udpsock->ns_next = nfs_cltpsock;
+ nfs_cltpsock->ns_next = &nfssvc_sockhead;
+ nfssvc_sockhead.ns_prev = nfs_cltpsock;
+ nfs_cltpsock->ns_prev = nfs_udpsock;
+ nfs_udpsock->ns_prev = &nfssvc_sockhead;
+ nfs_udpsock->ns_lrunext = nfs_udpsock->ns_lruprev =
+ (struct nfsuid *)nfs_udpsock;
+ nfs_cltpsock->ns_lrunext = nfs_cltpsock->ns_lruprev =
+ (struct nfsuid *)nfs_cltpsock;
+ nfsd_head.nd_next = nfsd_head.nd_prev = &nfsd_head;
+ nfsd_head.nd_flag = 0;
+ nfssvc_sockhead.ns_flag &= ~SLP_INIT;
+ if (nfssvc_sockhead.ns_flag & SLP_WANTINIT) {
+ nfssvc_sockhead.ns_flag &= ~SLP_WANTINIT;
+ wakeup((caddr_t)&nfssvc_sockhead);
+ }
+}
+
+/*
+ * Add entries to the server monitor log.
+ */
+static void
+nfsd_rt(startp, sotype, nd, nam, cacherep)
+ struct timeval *startp;
+ int sotype;
+ register struct nfsd *nd;
+ struct mbuf *nam;
+ int cacherep;
+{
+ register struct drt *rt;
+
+ rt = &nfsdrt.drt[nfsdrt.pos];
+ if (cacherep == RC_DOIT)
+ rt->flag = 0;
+ else if (cacherep == RC_REPLY)
+ rt->flag = DRT_CACHEREPLY;
+ else
+ rt->flag = DRT_CACHEDROP;
+ if (sotype == SOCK_STREAM)
+ rt->flag |= DRT_TCP;
+ if (nd->nd_nqlflag != NQL_NOVAL)
+ rt->flag |= DRT_NQNFS;
+ rt->proc = nd->nd_procnum;
+ if (mtod(nam, struct sockaddr *)->sa_family == AF_INET)
+ rt->ipadr = mtod(nam, struct sockaddr_in *)->sin_addr.s_addr;
+ else
+ rt->ipadr = INADDR_ANY;
+ rt->resptime = ((time.tv_sec - startp->tv_sec) * 1000000) +
+ (time.tv_usec - startp->tv_usec);
+ rt->tstamp = time;
+ nfsdrt.pos = (nfsdrt.pos + 1) % NFSRTTLOGSIZ;
+}
diff --git a/sys/nfs/nfs_vfsops.c b/sys/nfs/nfs_vfsops.c
new file mode 100644
index 0000000..1f18676
--- /dev/null
+++ b/sys/nfs/nfs_vfsops.c
@@ -0,0 +1,740 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_vfsops.c 8.3 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/ioctl.h>
+#include <sys/signal.h>
+#include <sys/proc.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/kernel.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nfs.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nfsdiskless.h>
+#include <nfs/nqnfs.h>
+
+/*
+ * nfs vfs operations.
+ */
+struct vfsops nfs_vfsops = {
+ nfs_mount,
+ nfs_start,
+ nfs_unmount,
+ nfs_root,
+ nfs_quotactl,
+ nfs_statfs,
+ nfs_sync,
+ nfs_vget,
+ nfs_fhtovp,
+ nfs_vptofh,
+ nfs_init,
+};
+
+/*
+ * This structure must be filled in by a primary bootstrap or bootstrap
+ * server for a diskless/dataless machine. It is initialized below just
+ * to ensure that it is allocated to initialized data (.data not .bss).
+ */
+struct nfs_diskless nfs_diskless = { 0 };
+
+extern u_long nfs_procids[NFS_NPROCS];
+extern u_long nfs_prog, nfs_vers;
+void nfs_disconnect __P((struct nfsmount *));
+void nfsargs_ntoh __P((struct nfs_args *));
+static struct mount *nfs_mountdiskless __P((char *, char *, int,
+ struct sockaddr_in *, struct nfs_args *, register struct vnode **));
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * nfs statfs call
+ */
+int
+nfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ register struct statfs *sbp;
+ struct proc *p;
+{
+ register struct vnode *vp;
+ register struct nfsv2_statfs *sfp;
+ register caddr_t cp;
+ register long t1;
+ caddr_t bpos, dpos, cp2;
+ int error = 0, isnq;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ struct nfsmount *nmp;
+ struct ucred *cred;
+ struct nfsnode *np;
+
+ nmp = VFSTONFS(mp);
+ isnq = (nmp->nm_flag & NFSMNT_NQNFS);
+ if (error = nfs_nget(mp, &nmp->nm_fh, &np))
+ return (error);
+ vp = NFSTOV(np);
+ nfsstats.rpccnt[NFSPROC_STATFS]++;
+ cred = crget();
+ cred->cr_ngroups = 1;
+ nfsm_reqhead(vp, NFSPROC_STATFS, NFSX_FH);
+ nfsm_fhtom(vp);
+ nfsm_request(vp, NFSPROC_STATFS, p, cred);
+ nfsm_dissect(sfp, struct nfsv2_statfs *, NFSX_STATFS(isnq));
+ sbp->f_type = MOUNT_NFS;
+ sbp->f_flags = nmp->nm_flag;
+ sbp->f_iosize = NFS_MAXDGRAMDATA;
+ sbp->f_bsize = fxdr_unsigned(long, sfp->sf_bsize);
+ sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks);
+ sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree);
+ sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail);
+ if (isnq) {
+ sbp->f_files = fxdr_unsigned(long, sfp->sf_files);
+ sbp->f_ffree = fxdr_unsigned(long, sfp->sf_ffree);
+ } else {
+ sbp->f_files = 0;
+ sbp->f_ffree = 0;
+ }
+ if (sbp != &mp->mnt_stat) {
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+ nfsm_reqdone;
+ vrele(vp);
+ crfree(cred);
+ return (error);
+}
+
+/*
+ * Mount a remote root fs via. nfs. This depends on the info in the
+ * nfs_diskless structure that has been filled in properly by some primary
+ * bootstrap.
+ * It goes something like this:
+ * - do enough of "ifconfig" by calling ifioctl() so that the system
+ * can talk to the server
+ * - If nfs_diskless.mygateway is filled in, use that address as
+ * a default gateway.
+ * - hand craft the swap nfs vnode hanging off a fake mount point
+ * if swdevt[0].sw_dev == NODEV
+ * - build the rootfs mount point and call mountnfs() to do the rest.
+ */
+int
+nfs_mountroot()
+{
+ register struct mount *mp;
+ register struct nfs_diskless *nd = &nfs_diskless;
+ struct socket *so;
+ struct vnode *vp;
+ struct proc *p = curproc; /* XXX */
+ int error, i;
+
+ /*
+ * XXX time must be non-zero when we init the interface or else
+ * the arp code will wedge...
+ */
+ if (time.tv_sec == 0)
+ time.tv_sec = 1;
+
+#ifdef notyet
+ /* Set up swap credentials. */
+ proc0.p_ucred->cr_uid = ntohl(nd->swap_ucred.cr_uid);
+ proc0.p_ucred->cr_gid = ntohl(nd->swap_ucred.cr_gid);
+ if ((proc0.p_ucred->cr_ngroups = ntohs(nd->swap_ucred.cr_ngroups)) >
+ NGROUPS)
+ proc0.p_ucred->cr_ngroups = NGROUPS;
+ for (i = 0; i < proc0.p_ucred->cr_ngroups; i++)
+ proc0.p_ucred->cr_groups[i] = ntohl(nd->swap_ucred.cr_groups[i]);
+#endif
+
+ /*
+ * Do enough of ifconfig(8) so that the critical net interface can
+ * talk to the server.
+ */
+ if (error = socreate(nd->myif.ifra_addr.sa_family, &so, SOCK_DGRAM, 0))
+ panic("nfs_mountroot: socreate: %d", error);
+ if (error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, p))
+ panic("nfs_mountroot: SIOCAIFADDR: %d", error);
+ soclose(so);
+
+ /*
+ * If the gateway field is filled in, set it as the default route.
+ */
+ if (nd->mygateway.sin_len != 0) {
+ struct sockaddr_in mask, sin;
+
+ bzero((caddr_t)&mask, sizeof(mask));
+ sin = mask;
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ if (error = rtrequest(RTM_ADD, (struct sockaddr *)&sin,
+ (struct sockaddr *)&nd->mygateway,
+ (struct sockaddr *)&mask,
+ RTF_UP | RTF_GATEWAY, (struct rtentry **)0))
+ panic("nfs_mountroot: RTM_ADD: %d", error);
+ }
+
+ /*
+ * If swapping to an nfs node (indicated by swdevt[0].sw_dev == NODEV):
+ * Create a fake mount point just for the swap vnode so that the
+ * swap file can be on a different server from the rootfs.
+ */
+ if (swdevt[0].sw_dev == NODEV) {
+ nd->swap_args.fh = (nfsv2fh_t *)nd->swap_fh;
+ (void) nfs_mountdiskless(nd->swap_hostnam, "/swap", 0,
+ &nd->swap_saddr, &nd->swap_args, &vp);
+
+ /*
+ * Since the swap file is not the root dir of a file system,
+ * hack it to a regular file.
+ */
+ vp->v_type = VREG;
+ vp->v_flag = 0;
+ swapdev_vp = vp;
+ VREF(vp);
+ swdevt[0].sw_vp = vp;
+ swdevt[0].sw_nblks = ntohl(nd->swap_nblks);
+ } else if (bdevvp(swapdev, &swapdev_vp))
+ panic("nfs_mountroot: can't setup swapdev_vp");
+
+ /*
+ * Create the rootfs mount point.
+ */
+ nd->root_args.fh = (nfsv2fh_t *)nd->root_fh;
+ mp = nfs_mountdiskless(nd->root_hostnam, "/", MNT_RDONLY,
+ &nd->root_saddr, &nd->root_args, &vp);
+
+ if (vfs_lock(mp))
+ panic("nfs_mountroot: vfs_lock");
+ TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
+ mp->mnt_flag |= MNT_ROOTFS;
+ mp->mnt_vnodecovered = NULLVP;
+ vfs_unlock(mp);
+ rootvp = vp;
+
+ /*
+ * This is not really an nfs issue, but it is much easier to
+ * set hostname here and then let the "/etc/rc.xxx" files
+ * mount the right /var based upon its preset value.
+ */
+ bcopy(nd->my_hostnam, hostname, MAXHOSTNAMELEN);
+ hostname[MAXHOSTNAMELEN - 1] = '\0';
+ for (i = 0; i < MAXHOSTNAMELEN; i++)
+ if (hostname[i] == '\0')
+ break;
+ hostnamelen = i;
+ inittodr(ntohl(nd->root_time));
+ return (0);
+}
+
+/*
+ * Internal version of mount system call for diskless setup.
+ */
+static struct mount *
+nfs_mountdiskless(path, which, mountflag, sin, args, vpp)
+ char *path;
+ char *which;
+ int mountflag;
+ struct sockaddr_in *sin;
+ struct nfs_args *args;
+ register struct vnode **vpp;
+{
+ register struct mount *mp;
+ register struct mbuf *m;
+ register int error;
+
+ mp = (struct mount *)malloc((u_long)sizeof(struct mount),
+ M_MOUNT, M_NOWAIT);
+ if (mp == NULL)
+ panic("nfs_mountroot: %s mount malloc", which);
+ bzero((char *)mp, (u_long)sizeof(struct mount));
+ mp->mnt_op = &nfs_vfsops;
+ mp->mnt_flag = mountflag;
+
+ MGET(m, MT_SONAME, M_DONTWAIT);
+ if (m == NULL)
+ panic("nfs_mountroot: %s mount mbuf", which);
+ bcopy((caddr_t)sin, mtod(m, caddr_t), sin->sin_len);
+ m->m_len = sin->sin_len;
+ nfsargs_ntoh(args);
+ if (error = mountnfs(args, mp, m, which, path, vpp))
+ panic("nfs_mountroot: mount %s on %s: %d", path, which, error);
+
+ return (mp);
+}
+
+/*
+ * Convert the integer fields of the nfs_args structure from net byte order
+ * to host byte order. Called by nfs_mountroot() above.
+ */
+void
+nfsargs_ntoh(nfsp)
+ register struct nfs_args *nfsp;
+{
+
+ NTOHL(nfsp->sotype);
+ NTOHL(nfsp->proto);
+ NTOHL(nfsp->flags);
+ NTOHL(nfsp->wsize);
+ NTOHL(nfsp->rsize);
+ NTOHL(nfsp->timeo);
+ NTOHL(nfsp->retrans);
+ NTOHL(nfsp->maxgrouplist);
+ NTOHL(nfsp->readahead);
+ NTOHL(nfsp->leaseterm);
+ NTOHL(nfsp->deadthresh);
+}
+
+/*
+ * VFS Operations.
+ *
+ * mount system call
+ * It seems a bit dumb to copyinstr() the host and path here and then
+ * bcopy() them in mountnfs(), but I wanted to detect errors before
+ * doing the sockargs() call because sockargs() allocates an mbuf and
+ * an error after that means that I have to release the mbuf.
+ */
+/* ARGSUSED */
+int
+nfs_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ int error;
+ struct nfs_args args;
+ struct mbuf *nam;
+ struct vnode *vp;
+ char pth[MNAMELEN], hst[MNAMELEN];
+ u_int len;
+ nfsv2fh_t nfh;
+
+ if (error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args)))
+ return (error);
+ if (error = copyin((caddr_t)args.fh, (caddr_t)&nfh, sizeof (nfsv2fh_t)))
+ return (error);
+ if (error = copyinstr(path, pth, MNAMELEN-1, &len))
+ return (error);
+ bzero(&pth[len], MNAMELEN - len);
+ if (error = copyinstr(args.hostname, hst, MNAMELEN-1, &len))
+ return (error);
+ bzero(&hst[len], MNAMELEN - len);
+ /* sockargs() call must be after above copyin() calls */
+ if (error = sockargs(&nam, (caddr_t)args.addr,
+ args.addrlen, MT_SONAME))
+ return (error);
+ args.fh = &nfh;
+ error = mountnfs(&args, mp, nam, pth, hst, &vp);
+ return (error);
+}
+
+/*
+ * Common code for mount and mountroot
+ */
+int
+mountnfs(argp, mp, nam, pth, hst, vpp)
+ register struct nfs_args *argp;
+ register struct mount *mp;
+ struct mbuf *nam;
+ char *pth, *hst;
+ struct vnode **vpp;
+{
+ register struct nfsmount *nmp;
+ struct nfsnode *np;
+ int error;
+
+ if (mp->mnt_flag & MNT_UPDATE) {
+ nmp = VFSTONFS(mp);
+ /* update paths, file handles, etc, here XXX */
+ m_freem(nam);
+ return (0);
+ } else {
+ MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount),
+ M_NFSMNT, M_WAITOK);
+ bzero((caddr_t)nmp, sizeof (struct nfsmount));
+ mp->mnt_data = (qaddr_t)nmp;
+ }
+ getnewfsid(mp, MOUNT_NFS);
+ nmp->nm_mountp = mp;
+ nmp->nm_flag = argp->flags;
+ if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_MYWRITE)) ==
+ (NFSMNT_NQNFS | NFSMNT_MYWRITE)) {
+ error = EPERM;
+ goto bad;
+ }
+ if (nmp->nm_flag & NFSMNT_NQNFS)
+ /*
+ * We have to set mnt_maxsymlink to a non-zero value so
+ * that COMPAT_43 routines will know that we are setting
+ * the d_type field in directories (and can zero it for
+ * unsuspecting binaries).
+ */
+ mp->mnt_maxsymlinklen = 1;
+ nmp->nm_timeo = NFS_TIMEO;
+ nmp->nm_retry = NFS_RETRANS;
+ nmp->nm_wsize = NFS_WSIZE;
+ nmp->nm_rsize = NFS_RSIZE;
+ nmp->nm_numgrps = NFS_MAXGRPS;
+ nmp->nm_readahead = NFS_DEFRAHEAD;
+ nmp->nm_leaseterm = NQ_DEFLEASE;
+ nmp->nm_deadthresh = NQ_DEADTHRESH;
+ nmp->nm_tnext = (struct nfsnode *)nmp;
+ nmp->nm_tprev = (struct nfsnode *)nmp;
+ nmp->nm_inprog = NULLVP;
+ bcopy((caddr_t)argp->fh, (caddr_t)&nmp->nm_fh, sizeof(nfsv2fh_t));
+ mp->mnt_stat.f_type = MOUNT_NFS;
+ bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
+ bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN);
+ nmp->nm_nam = nam;
+
+ if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
+ nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
+ if (nmp->nm_timeo < NFS_MINTIMEO)
+ nmp->nm_timeo = NFS_MINTIMEO;
+ else if (nmp->nm_timeo > NFS_MAXTIMEO)
+ nmp->nm_timeo = NFS_MAXTIMEO;
+ }
+
+ if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
+ nmp->nm_retry = argp->retrans;
+ if (nmp->nm_retry > NFS_MAXREXMIT)
+ nmp->nm_retry = NFS_MAXREXMIT;
+ }
+
+ if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
+ nmp->nm_wsize = argp->wsize;
+ /* Round down to multiple of blocksize */
+ nmp->nm_wsize &= ~0x1ff;
+ if (nmp->nm_wsize <= 0)
+ nmp->nm_wsize = 512;
+ else if (nmp->nm_wsize > NFS_MAXDATA)
+ nmp->nm_wsize = NFS_MAXDATA;
+ }
+ if (nmp->nm_wsize > MAXBSIZE)
+ nmp->nm_wsize = MAXBSIZE;
+
+ if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
+ nmp->nm_rsize = argp->rsize;
+ /* Round down to multiple of blocksize */
+ nmp->nm_rsize &= ~0x1ff;
+ if (nmp->nm_rsize <= 0)
+ nmp->nm_rsize = 512;
+ else if (nmp->nm_rsize > NFS_MAXDATA)
+ nmp->nm_rsize = NFS_MAXDATA;
+ }
+ if (nmp->nm_rsize > MAXBSIZE)
+ nmp->nm_rsize = MAXBSIZE;
+ if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 &&
+ argp->maxgrouplist <= NFS_MAXGRPS)
+ nmp->nm_numgrps = argp->maxgrouplist;
+ if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0 &&
+ argp->readahead <= NFS_MAXRAHEAD)
+ nmp->nm_readahead = argp->readahead;
+ if ((argp->flags & NFSMNT_LEASETERM) && argp->leaseterm >= 2 &&
+ argp->leaseterm <= NQ_MAXLEASE)
+ nmp->nm_leaseterm = argp->leaseterm;
+ if ((argp->flags & NFSMNT_DEADTHRESH) && argp->deadthresh >= 1 &&
+ argp->deadthresh <= NQ_NEVERDEAD)
+ nmp->nm_deadthresh = argp->deadthresh;
+ /* Set up the sockets and per-host congestion */
+ nmp->nm_sotype = argp->sotype;
+ nmp->nm_soproto = argp->proto;
+
+ /*
+ * For Connection based sockets (TCP,...) defer the connect until
+ * the first request, in case the server is not responding.
+ */
+ if (nmp->nm_sotype == SOCK_DGRAM &&
+ (error = nfs_connect(nmp, (struct nfsreq *)0)))
+ goto bad;
+
+ /*
+ * This is silly, but it has to be set so that vinifod() works.
+ * We do not want to do an nfs_statfs() here since we can get
+ * stuck on a dead server and we are holding a lock on the mount
+ * point.
+ */
+ mp->mnt_stat.f_iosize = NFS_MAXDGRAMDATA;
+ /*
+ * A reference count is needed on the nfsnode representing the
+ * remote root. If this object is not persistent, then backward
+ * traversals of the mount point (i.e. "..") will not work if
+ * the nfsnode gets flushed out of the cache. Ufs does not have
+ * this problem, because one can identify root inodes by their
+ * number == ROOTINO (2).
+ */
+ if (error = nfs_nget(mp, &nmp->nm_fh, &np))
+ goto bad;
+ *vpp = NFSTOV(np);
+
+ return (0);
+bad:
+ nfs_disconnect(nmp);
+ free((caddr_t)nmp, M_NFSMNT);
+ m_freem(nam);
+ return (error);
+}
+
+/*
+ * unmount system call
+ */
+int
+nfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ register struct nfsmount *nmp;
+ struct nfsnode *np;
+ struct vnode *vp;
+ int error, flags = 0;
+ extern int doforce;
+
+ if (mntflags & MNT_FORCE) {
+ if (!doforce || (mp->mnt_flag & MNT_ROOTFS))
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+ nmp = VFSTONFS(mp);
+ /*
+ * Goes something like this..
+ * - Check for activity on the root vnode (other than ourselves).
+ * - Call vflush() to clear out vnodes for this file system,
+ * except for the root vnode.
+ * - Decrement reference on the vnode representing remote root.
+ * - Close the socket
+ * - Free up the data structures
+ */
+ /*
+ * We need to decrement the ref. count on the nfsnode representing
+ * the remote root. See comment in mountnfs(). The VFS unmount()
+ * has done vput on this vnode, otherwise we would get deadlock!
+ */
+ if (error = nfs_nget(mp, &nmp->nm_fh, &np))
+ return(error);
+ vp = NFSTOV(np);
+ if (vp->v_usecount > 2) {
+ vput(vp);
+ return (EBUSY);
+ }
+
+ /*
+ * Must handshake with nqnfs_clientd() if it is active.
+ */
+ nmp->nm_flag |= NFSMNT_DISMINPROG;
+ while (nmp->nm_inprog != NULLVP)
+ (void) tsleep((caddr_t)&lbolt, PSOCK, "nfsdism", 0);
+ if (error = vflush(mp, vp, flags)) {
+ vput(vp);
+ nmp->nm_flag &= ~NFSMNT_DISMINPROG;
+ return (error);
+ }
+
+ /*
+ * We are now committed to the unmount.
+ * For NQNFS, let the server daemon free the nfsmount structure.
+ */
+ if (nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB))
+ nmp->nm_flag |= NFSMNT_DISMNT;
+
+ /*
+ * There are two reference counts to get rid of here.
+ */
+ vrele(vp);
+ vrele(vp);
+ vgone(vp);
+ nfs_disconnect(nmp);
+ m_freem(nmp->nm_nam);
+
+ if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB)) == 0)
+ free((caddr_t)nmp, M_NFSMNT);
+ return (0);
+}
+
+/*
+ * Return root of a filesystem
+ */
+int
+nfs_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ register struct vnode *vp;
+ struct nfsmount *nmp;
+ struct nfsnode *np;
+ int error;
+
+ nmp = VFSTONFS(mp);
+ if (error = nfs_nget(mp, &nmp->nm_fh, &np))
+ return (error);
+ vp = NFSTOV(np);
+ vp->v_type = VDIR;
+ vp->v_flag = VROOT;
+ *vpp = vp;
+ return (0);
+}
+
+extern int syncprt;
+
+/*
+ * Flush out the buffer cache
+ */
+/* ARGSUSED */
+int
+nfs_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+ register struct vnode *vp;
+ int error, allerror = 0;
+
+ /*
+ * Force stale buffer cache information to be flushed.
+ */
+loop:
+ for (vp = mp->mnt_vnodelist.lh_first;
+ vp != NULL;
+ vp = vp->v_mntvnodes.le_next) {
+ /*
+ * If the vnode that we are about to sync is no longer
+ * associated with this mount point, start over.
+ */
+ if (vp->v_mount != mp)
+ goto loop;
+ if (VOP_ISLOCKED(vp) || vp->v_dirtyblkhd.lh_first == NULL)
+ continue;
+ if (vget(vp, 1))
+ goto loop;
+ if (error = VOP_FSYNC(vp, cred, waitfor, p))
+ allerror = error;
+ vput(vp);
+ }
+ return (allerror);
+}
+
+/*
+ * NFS flat namespace lookup.
+ * Currently unsupported.
+ */
+/* ARGSUSED */
+int
+nfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * At this point, this should never happen
+ */
+/* ARGSUSED */
+int
+nfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
+ register struct mount *mp;
+ struct fid *fhp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred **credanonp;
+{
+
+ return (EINVAL);
+}
+
+/*
+ * Vnode pointer to File handle, should never happen either
+ */
+/* ARGSUSED */
+int
+nfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return (EINVAL);
+}
+
+/*
+ * Vfs start routine, a no-op.
+ */
+/* ARGSUSED */
+int
+nfs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+
+ return (0);
+}
+
+/*
+ * Do operations associated with quotas, not supported
+ */
+/* ARGSUSED */
+int
+nfs_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
diff --git a/sys/nfs/nfs_vnops.c b/sys/nfs/nfs_vnops.c
new file mode 100644
index 0000000..9e76ded
--- /dev/null
+++ b/sys/nfs/nfs_vnops.c
@@ -0,0 +1,2541 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_vnops.c 8.5 (Berkeley) 2/13/94
+ */
+
+/*
+ * vnode op calls for sun nfs version 2
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/conf.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/map.h>
+#include <sys/dirent.h>
+
+#include <vm/vm.h>
+
+#include <miscfs/specfs/specdev.h>
+#include <miscfs/fifofs/fifo.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfsmount.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nqnfs.h>
+
+/* Defs */
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Global vfs data structures for nfs
+ */
+int (**nfsv2_vnodeop_p)();
+struct vnodeopv_entry_desc nfsv2_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, nfs_lookup }, /* lookup */
+ { &vop_create_desc, nfs_create }, /* create */
+ { &vop_mknod_desc, nfs_mknod }, /* mknod */
+ { &vop_open_desc, nfs_open }, /* open */
+ { &vop_close_desc, nfs_close }, /* close */
+ { &vop_access_desc, nfs_access }, /* access */
+ { &vop_getattr_desc, nfs_getattr }, /* getattr */
+ { &vop_setattr_desc, nfs_setattr }, /* setattr */
+ { &vop_read_desc, nfs_read }, /* read */
+ { &vop_write_desc, nfs_write }, /* write */
+ { &vop_ioctl_desc, nfs_ioctl }, /* ioctl */
+ { &vop_select_desc, nfs_select }, /* select */
+ { &vop_mmap_desc, nfs_mmap }, /* mmap */
+ { &vop_fsync_desc, nfs_fsync }, /* fsync */
+ { &vop_seek_desc, nfs_seek }, /* seek */
+ { &vop_remove_desc, nfs_remove }, /* remove */
+ { &vop_link_desc, nfs_link }, /* link */
+ { &vop_rename_desc, nfs_rename }, /* rename */
+ { &vop_mkdir_desc, nfs_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, nfs_rmdir }, /* rmdir */
+ { &vop_symlink_desc, nfs_symlink }, /* symlink */
+ { &vop_readdir_desc, nfs_readdir }, /* readdir */
+ { &vop_readlink_desc, nfs_readlink }, /* readlink */
+ { &vop_abortop_desc, nfs_abortop }, /* abortop */
+ { &vop_inactive_desc, nfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, nfs_reclaim }, /* reclaim */
+ { &vop_lock_desc, nfs_lock }, /* lock */
+ { &vop_unlock_desc, nfs_unlock }, /* unlock */
+ { &vop_bmap_desc, nfs_bmap }, /* bmap */
+ { &vop_strategy_desc, nfs_strategy }, /* strategy */
+ { &vop_print_desc, nfs_print }, /* print */
+ { &vop_islocked_desc, nfs_islocked }, /* islocked */
+ { &vop_pathconf_desc, nfs_pathconf }, /* pathconf */
+ { &vop_advlock_desc, nfs_advlock }, /* advlock */
+ { &vop_blkatoff_desc, nfs_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, nfs_valloc }, /* valloc */
+ { &vop_reallocblks_desc, nfs_reallocblks }, /* reallocblks */
+ { &vop_vfree_desc, nfs_vfree }, /* vfree */
+ { &vop_truncate_desc, nfs_truncate }, /* truncate */
+ { &vop_update_desc, nfs_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc nfsv2_vnodeop_opv_desc =
+ { &nfsv2_vnodeop_p, nfsv2_vnodeop_entries };
+
+/*
+ * Special device vnode ops
+ */
+int (**spec_nfsv2nodeop_p)();
+struct vnodeopv_entry_desc spec_nfsv2nodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, spec_lookup }, /* lookup */
+ { &vop_create_desc, spec_create }, /* create */
+ { &vop_mknod_desc, spec_mknod }, /* mknod */
+ { &vop_open_desc, spec_open }, /* open */
+ { &vop_close_desc, nfsspec_close }, /* close */
+ { &vop_access_desc, nfsspec_access }, /* access */
+ { &vop_getattr_desc, nfs_getattr }, /* getattr */
+ { &vop_setattr_desc, nfs_setattr }, /* setattr */
+ { &vop_read_desc, nfsspec_read }, /* read */
+ { &vop_write_desc, nfsspec_write }, /* write */
+ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */
+ { &vop_select_desc, spec_select }, /* select */
+ { &vop_mmap_desc, spec_mmap }, /* mmap */
+ { &vop_fsync_desc, nfs_fsync }, /* fsync */
+ { &vop_seek_desc, spec_seek }, /* seek */
+ { &vop_remove_desc, spec_remove }, /* remove */
+ { &vop_link_desc, spec_link }, /* link */
+ { &vop_rename_desc, spec_rename }, /* rename */
+ { &vop_mkdir_desc, spec_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, spec_rmdir }, /* rmdir */
+ { &vop_symlink_desc, spec_symlink }, /* symlink */
+ { &vop_readdir_desc, spec_readdir }, /* readdir */
+ { &vop_readlink_desc, spec_readlink }, /* readlink */
+ { &vop_abortop_desc, spec_abortop }, /* abortop */
+ { &vop_inactive_desc, nfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, nfs_reclaim }, /* reclaim */
+ { &vop_lock_desc, nfs_lock }, /* lock */
+ { &vop_unlock_desc, nfs_unlock }, /* unlock */
+ { &vop_bmap_desc, spec_bmap }, /* bmap */
+ { &vop_strategy_desc, spec_strategy }, /* strategy */
+ { &vop_print_desc, nfs_print }, /* print */
+ { &vop_islocked_desc, nfs_islocked }, /* islocked */
+ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */
+ { &vop_advlock_desc, spec_advlock }, /* advlock */
+ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, spec_valloc }, /* valloc */
+ { &vop_reallocblks_desc, spec_reallocblks }, /* reallocblks */
+ { &vop_vfree_desc, spec_vfree }, /* vfree */
+ { &vop_truncate_desc, spec_truncate }, /* truncate */
+ { &vop_update_desc, nfs_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc spec_nfsv2nodeop_opv_desc =
+ { &spec_nfsv2nodeop_p, spec_nfsv2nodeop_entries };
+
+#ifdef FIFO
+int (**fifo_nfsv2nodeop_p)();
+struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, fifo_lookup }, /* lookup */
+ { &vop_create_desc, fifo_create }, /* create */
+ { &vop_mknod_desc, fifo_mknod }, /* mknod */
+ { &vop_open_desc, fifo_open }, /* open */
+ { &vop_close_desc, nfsfifo_close }, /* close */
+ { &vop_access_desc, nfsspec_access }, /* access */
+ { &vop_getattr_desc, nfs_getattr }, /* getattr */
+ { &vop_setattr_desc, nfs_setattr }, /* setattr */
+ { &vop_read_desc, nfsfifo_read }, /* read */
+ { &vop_write_desc, nfsfifo_write }, /* write */
+ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */
+ { &vop_select_desc, fifo_select }, /* select */
+ { &vop_mmap_desc, fifo_mmap }, /* mmap */
+ { &vop_fsync_desc, nfs_fsync }, /* fsync */
+ { &vop_seek_desc, fifo_seek }, /* seek */
+ { &vop_remove_desc, fifo_remove }, /* remove */
+ { &vop_link_desc, fifo_link }, /* link */
+ { &vop_rename_desc, fifo_rename }, /* rename */
+ { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */
+ { &vop_symlink_desc, fifo_symlink }, /* symlink */
+ { &vop_readdir_desc, fifo_readdir }, /* readdir */
+ { &vop_readlink_desc, fifo_readlink }, /* readlink */
+ { &vop_abortop_desc, fifo_abortop }, /* abortop */
+ { &vop_inactive_desc, nfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, nfs_reclaim }, /* reclaim */
+ { &vop_lock_desc, nfs_lock }, /* lock */
+ { &vop_unlock_desc, nfs_unlock }, /* unlock */
+ { &vop_bmap_desc, fifo_bmap }, /* bmap */
+ { &vop_strategy_desc, fifo_badop }, /* strategy */
+ { &vop_print_desc, nfs_print }, /* print */
+ { &vop_islocked_desc, nfs_islocked }, /* islocked */
+ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */
+ { &vop_advlock_desc, fifo_advlock }, /* advlock */
+ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, fifo_valloc }, /* valloc */
+ { &vop_reallocblks_desc, fifo_reallocblks }, /* reallocblks */
+ { &vop_vfree_desc, fifo_vfree }, /* vfree */
+ { &vop_truncate_desc, fifo_truncate }, /* truncate */
+ { &vop_update_desc, nfs_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc =
+ { &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries };
+#endif /* FIFO */
+
+void nqnfs_clientlease();
+
+/*
+ * Global variables
+ */
+extern u_long nfs_procids[NFS_NPROCS];
+extern u_long nfs_prog, nfs_vers, nfs_true, nfs_false;
+extern char nfsiobuf[MAXPHYS+NBPG];
+struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
+int nfs_numasync = 0;
+#define DIRHDSIZ (sizeof (struct dirent) - (MAXNAMLEN + 1))
+
+/*
+ * nfs null call from vfs.
+ */
+int
+nfs_null(vp, cred, procp)
+ struct vnode *vp;
+ struct ucred *cred;
+ struct proc *procp;
+{
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb;
+
+ nfsm_reqhead(vp, NFSPROC_NULL, 0);
+ nfsm_request(vp, NFSPROC_NULL, procp, cred);
+ nfsm_reqdone;
+ return (error);
+}
+
+/*
+ * nfs access vnode op.
+ * For nfs, just return ok. File accesses may fail later.
+ * For nqnfs, use the access rpc to check accessibility. If file modes are
+ * changed on the server, accesses might still fail later.
+ */
+int
+nfs_access(ap)
+ struct vop_access_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register u_long *tl;
+ register caddr_t cp;
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ /*
+ * For nqnfs, do an access rpc, otherwise you are stuck emulating
+ * ufs_access() locally using the vattr. This may not be correct,
+ * since the server may apply other access criteria such as
+ * client uid-->server uid mapping that we do not know about, but
+ * this is better than just returning anything that is lying about
+ * in the cache.
+ */
+ if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) {
+ nfsstats.rpccnt[NQNFSPROC_ACCESS]++;
+ nfsm_reqhead(vp, NQNFSPROC_ACCESS, NFSX_FH + 3 * NFSX_UNSIGNED);
+ nfsm_fhtom(vp);
+ nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
+ if (ap->a_mode & VREAD)
+ *tl++ = nfs_true;
+ else
+ *tl++ = nfs_false;
+ if (ap->a_mode & VWRITE)
+ *tl++ = nfs_true;
+ else
+ *tl++ = nfs_false;
+ if (ap->a_mode & VEXEC)
+ *tl = nfs_true;
+ else
+ *tl = nfs_false;
+ nfsm_request(vp, NQNFSPROC_ACCESS, ap->a_p, ap->a_cred);
+ nfsm_reqdone;
+ return (error);
+ } else
+ return (nfsspec_access(ap));
+}
+
+/*
+ * nfs open vnode op
+ * Check to see if the type is ok
+ * and that deletion is not in progress.
+ * For paged in text files, you will need to flush the page cache
+ * if consistency is lost.
+ */
+/* ARGSUSED */
+int
+nfs_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ struct nfsnode *np = VTONFS(vp);
+ struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+ struct vattr vattr;
+ int error;
+
+ if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK)
+ return (EACCES);
+ if (vp->v_flag & VTEXT) {
+ /*
+ * Get a valid lease. If cached data is stale, flush it.
+ */
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ if (NQNFS_CKINVALID(vp, np, NQL_READ)) {
+ do {
+ error = nqnfs_getlease(vp, NQL_READ, ap->a_cred, ap->a_p);
+ } while (error == NQNFS_EXPIRED);
+ if (error)
+ return (error);
+ if (np->n_lrev != np->n_brev ||
+ (np->n_flag & NQNFSNONCACHE)) {
+ if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
+ ap->a_p, 1)) == EINTR)
+ return (error);
+ (void) vnode_pager_uncache(vp);
+ np->n_brev = np->n_lrev;
+ }
+ }
+ } else {
+ if (np->n_flag & NMODIFIED) {
+ if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
+ ap->a_p, 1)) == EINTR)
+ return (error);
+ (void) vnode_pager_uncache(vp);
+ np->n_attrstamp = 0;
+ np->n_direofoffset = 0;
+ if (error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p))
+ return (error);
+ np->n_mtime = vattr.va_mtime.ts_sec;
+ } else {
+ if (error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p))
+ return (error);
+ if (np->n_mtime != vattr.va_mtime.ts_sec) {
+ np->n_direofoffset = 0;
+ if ((error = nfs_vinvalbuf(vp, V_SAVE,
+ ap->a_cred, ap->a_p, 1)) == EINTR)
+ return (error);
+ (void) vnode_pager_uncache(vp);
+ np->n_mtime = vattr.va_mtime.ts_sec;
+ }
+ }
+ }
+ } else if ((nmp->nm_flag & NFSMNT_NQNFS) == 0)
+ np->n_attrstamp = 0; /* For Open/Close consistency */
+ return (0);
+}
+
+/*
+ * nfs close vnode op
+ * For reg files, invalidate any buffer cache entries.
+ */
+/* ARGSUSED */
+int
+nfs_close(ap)
+ struct vop_close_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+ int error = 0;
+
+ if (vp->v_type == VREG) {
+ if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) == 0 &&
+ (np->n_flag & NMODIFIED)) {
+ error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1);
+ np->n_attrstamp = 0;
+ }
+ if (np->n_flag & NWRITEERR) {
+ np->n_flag &= ~NWRITEERR;
+ error = np->n_error;
+ }
+ }
+ return (error);
+}
+
+/*
+ * nfs getattr call from vfs.
+ */
+int
+nfs_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+ register caddr_t cp;
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ /*
+ * Update local times for special files.
+ */
+ if (np->n_flag & (NACC | NUPD))
+ np->n_flag |= NCHG;
+ /*
+ * First look in the cache.
+ */
+ if (nfs_getattrcache(vp, ap->a_vap) == 0)
+ return (0);
+ nfsstats.rpccnt[NFSPROC_GETATTR]++;
+ nfsm_reqhead(vp, NFSPROC_GETATTR, NFSX_FH);
+ nfsm_fhtom(vp);
+ nfsm_request(vp, NFSPROC_GETATTR, ap->a_p, ap->a_cred);
+ nfsm_loadattr(vp, ap->a_vap);
+ nfsm_reqdone;
+ return (error);
+}
+
+/*
+ * nfs setattr call.
+ */
+int
+nfs_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct nfsv2_sattr *sp;
+ register caddr_t cp;
+ register long t1;
+ caddr_t bpos, dpos, cp2;
+ u_long *tl;
+ int error = 0, isnq;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+ register struct vattr *vap = ap->a_vap;
+ u_quad_t frev, tsize = 0;
+
+ if (vap->va_size != VNOVAL || vap->va_mtime.ts_sec != VNOVAL ||
+ vap->va_atime.ts_sec != VNOVAL) {
+ if (vap->va_size != VNOVAL) {
+ if (np->n_flag & NMODIFIED) {
+ if (vap->va_size == 0)
+ error = nfs_vinvalbuf(vp, 0, ap->a_cred,
+ ap->a_p, 1);
+ else
+ error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
+ ap->a_p, 1);
+ if (error)
+ return (error);
+ }
+ tsize = np->n_size;
+ np->n_size = np->n_vattr.va_size = vap->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else if ((np->n_flag & NMODIFIED) &&
+ (error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
+ ap->a_p, 1)) == EINTR)
+ return (error);
+ }
+ nfsstats.rpccnt[NFSPROC_SETATTR]++;
+ isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ nfsm_reqhead(vp, NFSPROC_SETATTR, NFSX_FH+NFSX_SATTR(isnq));
+ nfsm_fhtom(vp);
+ nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq));
+ if (vap->va_mode == (u_short)-1)
+ sp->sa_mode = VNOVAL;
+ else
+ sp->sa_mode = vtonfs_mode(vp->v_type, vap->va_mode);
+ if (vap->va_uid == (uid_t)-1)
+ sp->sa_uid = VNOVAL;
+ else
+ sp->sa_uid = txdr_unsigned(vap->va_uid);
+ if (vap->va_gid == (gid_t)-1)
+ sp->sa_gid = VNOVAL;
+ else
+ sp->sa_gid = txdr_unsigned(vap->va_gid);
+ if (isnq) {
+ txdr_hyper(&vap->va_size, &sp->sa_nqsize);
+ txdr_nqtime(&vap->va_atime, &sp->sa_nqatime);
+ txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime);
+ sp->sa_nqflags = txdr_unsigned(vap->va_flags);
+ sp->sa_nqrdev = VNOVAL;
+ } else {
+ sp->sa_nfssize = txdr_unsigned(vap->va_size);
+ txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
+ txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+ }
+ nfsm_request(vp, NFSPROC_SETATTR, ap->a_p, ap->a_cred);
+ nfsm_loadattr(vp, (struct vattr *)0);
+ if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) &&
+ NQNFS_CKCACHABLE(vp, NQL_WRITE)) {
+ nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
+ fxdr_hyper(tl, &frev);
+ if (frev > np->n_brev)
+ np->n_brev = frev;
+ }
+ nfsm_reqdone;
+ if (error) {
+ np->n_size = np->n_vattr.va_size = tsize;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ }
+ return (error);
+}
+
+/*
+ * nfs lookup call, one step at a time...
+ * First look in cache
+ * If not found, unlock the directory nfsnode and do the rpc
+ */
+int
+nfs_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ register struct componentname *cnp = ap->a_cnp;
+ register struct vnode *dvp = ap->a_dvp;
+ register struct vnode **vpp = ap->a_vpp;
+ register int flags = cnp->cn_flags;
+ register struct vnode *vdp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1, t2;
+ struct nfsmount *nmp;
+ caddr_t bpos, dpos, cp2;
+ time_t reqtime = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ struct vnode *newvp;
+ long len;
+ nfsv2fh_t *fhp;
+ struct nfsnode *np;
+ int lockparent, wantparent, error = 0;
+ int nqlflag = 0, cachable = 0;
+ u_quad_t frev;
+
+ *vpp = NULL;
+ if (dvp->v_type != VDIR)
+ return (ENOTDIR);
+ lockparent = flags & LOCKPARENT;
+ wantparent = flags & (LOCKPARENT|WANTPARENT);
+ nmp = VFSTONFS(dvp->v_mount);
+ np = VTONFS(dvp);
+ if ((error = cache_lookup(dvp, vpp, cnp)) && error != ENOENT) {
+ struct vattr vattr;
+ int vpid;
+
+ vdp = *vpp;
+ vpid = vdp->v_id;
+ /*
+ * See the comment starting `Step through' in ufs/ufs_lookup.c
+ * for an explanation of the locking protocol
+ */
+ if (dvp == vdp) {
+ VREF(vdp);
+ error = 0;
+ } else
+ error = vget(vdp, 1);
+ if (!error) {
+ if (vpid == vdp->v_id) {
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) == 0) {
+ nfsstats.lookupcache_hits++;
+ if (cnp->cn_nameiop != LOOKUP &&
+ (flags & ISLASTCN))
+ cnp->cn_flags |= SAVENAME;
+ return (0);
+ } else if (NQNFS_CKCACHABLE(dvp, NQL_READ)) {
+ if (np->n_lrev != np->n_brev ||
+ (np->n_flag & NMODIFIED)) {
+ np->n_direofoffset = 0;
+ cache_purge(dvp);
+ error = nfs_vinvalbuf(dvp, 0,
+ cnp->cn_cred, cnp->cn_proc,
+ 1);
+ if (error == EINTR)
+ return (error);
+ np->n_brev = np->n_lrev;
+ } else {
+ nfsstats.lookupcache_hits++;
+ if (cnp->cn_nameiop != LOOKUP &&
+ (flags & ISLASTCN))
+ cnp->cn_flags |= SAVENAME;
+ return (0);
+ }
+ }
+ } else if (!VOP_GETATTR(vdp, &vattr, cnp->cn_cred, cnp->cn_proc) &&
+ vattr.va_ctime.ts_sec == VTONFS(vdp)->n_ctime) {
+ nfsstats.lookupcache_hits++;
+ if (cnp->cn_nameiop != LOOKUP &&
+ (flags & ISLASTCN))
+ cnp->cn_flags |= SAVENAME;
+ return (0);
+ }
+ cache_purge(vdp);
+ }
+ vrele(vdp);
+ }
+ *vpp = NULLVP;
+ }
+ error = 0;
+ nfsstats.lookupcache_misses++;
+ nfsstats.rpccnt[NFSPROC_LOOKUP]++;
+ len = cnp->cn_namelen;
+ nfsm_reqhead(dvp, NFSPROC_LOOKUP, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len));
+
+ /*
+ * For nqnfs optionally piggyback a getlease request for the name
+ * being looked up.
+ */
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) &&
+ ((cnp->cn_flags & MAKEENTRY) &&
+ (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))))
+ *tl = txdr_unsigned(nmp->nm_leaseterm);
+ else
+ *tl = 0;
+ }
+ nfsm_fhtom(dvp);
+ nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
+ reqtime = time.tv_sec;
+ nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred);
+nfsmout:
+ if (error) {
+ if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
+ (flags & ISLASTCN) && error == ENOENT)
+ error = EJUSTRETURN;
+ if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
+ cnp->cn_flags |= SAVENAME;
+ return (error);
+ }
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ if (*tl) {
+ nqlflag = fxdr_unsigned(int, *tl);
+ nfsm_dissect(tl, u_long *, 4*NFSX_UNSIGNED);
+ cachable = fxdr_unsigned(int, *tl++);
+ reqtime += fxdr_unsigned(int, *tl++);
+ fxdr_hyper(tl, &frev);
+ } else
+ nqlflag = 0;
+ }
+ nfsm_dissect(fhp, nfsv2fh_t *, NFSX_FH);
+
+ /*
+ * Handle RENAME case...
+ */
+ if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
+ if (!bcmp(np->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) {
+ m_freem(mrep);
+ return (EISDIR);
+ }
+ if (error = nfs_nget(dvp->v_mount, fhp, &np)) {
+ m_freem(mrep);
+ return (error);
+ }
+ newvp = NFSTOV(np);
+ if (error =
+ nfs_loadattrcache(&newvp, &md, &dpos, (struct vattr *)0)) {
+ vrele(newvp);
+ m_freem(mrep);
+ return (error);
+ }
+ *vpp = newvp;
+ m_freem(mrep);
+ cnp->cn_flags |= SAVENAME;
+ return (0);
+ }
+
+ if (!bcmp(np->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) {
+ VREF(dvp);
+ newvp = dvp;
+ } else {
+ if (error = nfs_nget(dvp->v_mount, fhp, &np)) {
+ m_freem(mrep);
+ return (error);
+ }
+ newvp = NFSTOV(np);
+ }
+ if (error = nfs_loadattrcache(&newvp, &md, &dpos, (struct vattr *)0)) {
+ vrele(newvp);
+ m_freem(mrep);
+ return (error);
+ }
+ m_freem(mrep);
+ *vpp = newvp;
+ if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
+ cnp->cn_flags |= SAVENAME;
+ if ((cnp->cn_flags & MAKEENTRY) &&
+ (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))) {
+ if ((nmp->nm_flag & NFSMNT_NQNFS) == 0)
+ np->n_ctime = np->n_vattr.va_ctime.ts_sec;
+ else if (nqlflag && reqtime > time.tv_sec)
+ nqnfs_clientlease(nmp, np, nqlflag, cachable, reqtime,
+ frev);
+ cache_enter(dvp, *vpp, cnp);
+ }
+ return (0);
+}
+
+/*
+ * nfs read call.
+ * Just call nfs_bioread() to do the work.
+ */
+int
+nfs_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+
+ if (vp->v_type != VREG)
+ return (EPERM);
+ return (nfs_bioread(vp, ap->a_uio, ap->a_ioflag, ap->a_cred));
+}
+
+/*
+ * nfs readlink call
+ */
+int
+nfs_readlink(ap)
+ struct vop_readlink_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+
+ if (vp->v_type != VLNK)
+ return (EPERM);
+ return (nfs_bioread(vp, ap->a_uio, 0, ap->a_cred));
+}
+
+/*
+ * Do a readlink rpc.
+ * Called by nfs_doio() from below the buffer cache.
+ */
+int
+nfs_readlinkrpc(vp, uiop, cred)
+ register struct vnode *vp;
+ struct uio *uiop;
+ struct ucred *cred;
+{
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1;
+ caddr_t bpos, dpos, cp2;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ long len;
+
+ nfsstats.rpccnt[NFSPROC_READLINK]++;
+ nfsm_reqhead(vp, NFSPROC_READLINK, NFSX_FH);
+ nfsm_fhtom(vp);
+ nfsm_request(vp, NFSPROC_READLINK, uiop->uio_procp, cred);
+ nfsm_strsiz(len, NFS_MAXPATHLEN);
+ nfsm_mtouio(uiop, len);
+ nfsm_reqdone;
+ return (error);
+}
+
+/*
+ * nfs read rpc call
+ * Ditto above
+ */
+int
+nfs_readrpc(vp, uiop, cred)
+ register struct vnode *vp;
+ struct uio *uiop;
+ struct ucred *cred;
+{
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1;
+ caddr_t bpos, dpos, cp2;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ struct nfsmount *nmp;
+ long len, retlen, tsiz;
+
+ nmp = VFSTONFS(vp->v_mount);
+ tsiz = uiop->uio_resid;
+ if (uiop->uio_offset + tsiz > 0xffffffff &&
+ (nmp->nm_flag & NFSMNT_NQNFS) == 0)
+ return (EFBIG);
+ while (tsiz > 0) {
+ nfsstats.rpccnt[NFSPROC_READ]++;
+ len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz;
+ nfsm_reqhead(vp, NFSPROC_READ, NFSX_FH+NFSX_UNSIGNED*3);
+ nfsm_fhtom(vp);
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED*3);
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ txdr_hyper(&uiop->uio_offset, tl);
+ *(tl + 2) = txdr_unsigned(len);
+ } else {
+ *tl++ = txdr_unsigned(uiop->uio_offset);
+ *tl++ = txdr_unsigned(len);
+ *tl = 0;
+ }
+ nfsm_request(vp, NFSPROC_READ, uiop->uio_procp, cred);
+ nfsm_loadattr(vp, (struct vattr *)0);
+ nfsm_strsiz(retlen, nmp->nm_rsize);
+ nfsm_mtouio(uiop, retlen);
+ m_freem(mrep);
+ if (retlen < len)
+ tsiz = 0;
+ else
+ tsiz -= len;
+ }
+nfsmout:
+ return (error);
+}
+
+/*
+ * nfs write call
+ */
+int
+nfs_writerpc(vp, uiop, cred, ioflags)
+ register struct vnode *vp;
+ struct uio *uiop;
+ struct ucred *cred;
+ int ioflags;
+{
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1;
+ caddr_t bpos, dpos, cp2;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ struct nfsmount *nmp;
+ struct nfsnode *np = VTONFS(vp);
+ u_quad_t frev;
+ long len, tsiz;
+
+ nmp = VFSTONFS(vp->v_mount);
+ tsiz = uiop->uio_resid;
+ if (uiop->uio_offset + tsiz > 0xffffffff &&
+ (nmp->nm_flag & NFSMNT_NQNFS) == 0)
+ return (EFBIG);
+ while (tsiz > 0) {
+ nfsstats.rpccnt[NFSPROC_WRITE]++;
+ len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz;
+ nfsm_reqhead(vp, NFSPROC_WRITE,
+ NFSX_FH+NFSX_UNSIGNED*4+nfsm_rndup(len));
+ nfsm_fhtom(vp);
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED * 4);
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ txdr_hyper(&uiop->uio_offset, tl);
+ tl += 2;
+ if (ioflags & IO_APPEND)
+ *tl++ = txdr_unsigned(1);
+ else
+ *tl++ = 0;
+ } else {
+ *++tl = txdr_unsigned(uiop->uio_offset);
+ tl += 2;
+ }
+ *tl = txdr_unsigned(len);
+ nfsm_uiotom(uiop, len);
+ nfsm_request(vp, NFSPROC_WRITE, uiop->uio_procp, cred);
+ nfsm_loadattr(vp, (struct vattr *)0);
+ if (nmp->nm_flag & NFSMNT_MYWRITE)
+ VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.va_mtime.ts_sec;
+ else if ((nmp->nm_flag & NFSMNT_NQNFS) &&
+ NQNFS_CKCACHABLE(vp, NQL_WRITE)) {
+ nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
+ fxdr_hyper(tl, &frev);
+ if (frev > np->n_brev)
+ np->n_brev = frev;
+ }
+ m_freem(mrep);
+ tsiz -= len;
+ }
+nfsmout:
+ if (error)
+ uiop->uio_resid = tsiz;
+ return (error);
+}
+
+/*
+ * nfs mknod call
+ * This is a kludge. Use a create rpc but with the IFMT bits of the mode
+ * set to specify the file type and the size field for rdev.
+ */
+/* ARGSUSED */
+int
+nfs_mknod(ap)
+ struct vop_mknod_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ register struct vnode *dvp = ap->a_dvp;
+ register struct vattr *vap = ap->a_vap;
+ register struct componentname *cnp = ap->a_cnp;
+ register struct nfsv2_sattr *sp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1, t2;
+ struct vnode *newvp = 0;
+ struct vattr vattr;
+ char *cp2;
+ caddr_t bpos, dpos;
+ int error = 0, isnq;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ u_long rdev;
+
+ isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ if (vap->va_type == VCHR || vap->va_type == VBLK)
+ rdev = txdr_unsigned(vap->va_rdev);
+#ifdef FIFO
+ else if (vap->va_type == VFIFO)
+ rdev = 0xffffffff;
+#endif /* FIFO */
+ else {
+ VOP_ABORTOP(dvp, cnp);
+ vput(dvp);
+ return (EOPNOTSUPP);
+ }
+ if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
+ VOP_ABORTOP(dvp, cnp);
+ vput(dvp);
+ return (error);
+ }
+ nfsstats.rpccnt[NFSPROC_CREATE]++;
+ nfsm_reqhead(dvp, NFSPROC_CREATE,
+ NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)+NFSX_SATTR(isnq));
+ nfsm_fhtom(dvp);
+ nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq));
+ sp->sa_mode = vtonfs_mode(vap->va_type, vap->va_mode);
+ sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
+ sp->sa_gid = txdr_unsigned(vattr.va_gid);
+ if (isnq) {
+ sp->sa_nqrdev = rdev;
+ sp->sa_nqflags = 0;
+ txdr_nqtime(&vap->va_atime, &sp->sa_nqatime);
+ txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime);
+ } else {
+ sp->sa_nfssize = rdev;
+ txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
+ txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+ }
+ nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred);
+ nfsm_mtofh(dvp, newvp);
+ nfsm_reqdone;
+ if (!error && (cnp->cn_flags & MAKEENTRY))
+ cache_enter(dvp, newvp, cnp);
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ VTONFS(dvp)->n_flag |= NMODIFIED;
+ VTONFS(dvp)->n_attrstamp = 0;
+ vrele(dvp);
+ return (error);
+}
+
+/*
+ * nfs file create call
+ */
+int
+nfs_create(ap)
+ struct vop_create_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ register struct vnode *dvp = ap->a_dvp;
+ register struct vattr *vap = ap->a_vap;
+ register struct componentname *cnp = ap->a_cnp;
+ register struct nfsv2_sattr *sp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1, t2;
+ caddr_t bpos, dpos, cp2;
+ int error = 0, isnq;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ struct vattr vattr;
+
+ if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
+ VOP_ABORTOP(dvp, cnp);
+ vput(dvp);
+ return (error);
+ }
+ nfsstats.rpccnt[NFSPROC_CREATE]++;
+ isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ nfsm_reqhead(dvp, NFSPROC_CREATE,
+ NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)+NFSX_SATTR(isnq));
+ nfsm_fhtom(dvp);
+ nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq));
+ sp->sa_mode = vtonfs_mode(vap->va_type, vap->va_mode);
+ sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
+ sp->sa_gid = txdr_unsigned(vattr.va_gid);
+ if (isnq) {
+ u_quad_t qval = 0;
+
+ txdr_hyper(&qval, &sp->sa_nqsize);
+ sp->sa_nqflags = 0;
+ sp->sa_nqrdev = -1;
+ txdr_nqtime(&vap->va_atime, &sp->sa_nqatime);
+ txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime);
+ } else {
+ sp->sa_nfssize = 0;
+ txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
+ txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+ }
+ nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred);
+ nfsm_mtofh(dvp, *ap->a_vpp);
+ nfsm_reqdone;
+ if (!error && (cnp->cn_flags & MAKEENTRY))
+ cache_enter(dvp, *ap->a_vpp, cnp);
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ VTONFS(dvp)->n_flag |= NMODIFIED;
+ VTONFS(dvp)->n_attrstamp = 0;
+ vrele(dvp);
+ return (error);
+}
+
+/*
+ * nfs file remove call
+ * To try and make nfs semantics closer to ufs semantics, a file that has
+ * other processes using the vnode is renamed instead of removed and then
+ * removed later on the last close.
+ * - If v_usecount > 1
+ * If a rename is not already in the works
+ * call nfs_sillyrename() to set it up
+ * else
+ * do the remove rpc
+ */
+int
+nfs_remove(ap)
+ struct vop_remove_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode * a_dvp;
+ struct vnode * a_vp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct vnode *dvp = ap->a_dvp;
+ register struct componentname *cnp = ap->a_cnp;
+ register struct nfsnode *np = VTONFS(vp);
+ register u_long *tl;
+ register caddr_t cp;
+ register long t2;
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ if (vp->v_usecount > 1) {
+ if (!np->n_sillyrename)
+ error = nfs_sillyrename(dvp, vp, cnp);
+ } else {
+ /*
+ * Purge the name cache so that the chance of a lookup for
+ * the name succeeding while the remove is in progress is
+ * minimized. Without node locking it can still happen, such
+ * that an I/O op returns ESTALE, but since you get this if
+ * another host removes the file..
+ */
+ cache_purge(vp);
+ /*
+ * Throw away biocache buffers. Mainly to avoid
+ * unnecessary delayed writes.
+ */
+ error = nfs_vinvalbuf(vp, 0, cnp->cn_cred, cnp->cn_proc, 1);
+ if (error == EINTR)
+ return (error);
+ /* Do the rpc */
+ nfsstats.rpccnt[NFSPROC_REMOVE]++;
+ nfsm_reqhead(dvp, NFSPROC_REMOVE,
+ NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen));
+ nfsm_fhtom(dvp);
+ nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_request(dvp, NFSPROC_REMOVE, cnp->cn_proc, cnp->cn_cred);
+ nfsm_reqdone;
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ VTONFS(dvp)->n_flag |= NMODIFIED;
+ VTONFS(dvp)->n_attrstamp = 0;
+ /*
+ * Kludge City: If the first reply to the remove rpc is lost..
+ * the reply to the retransmitted request will be ENOENT
+ * since the file was in fact removed
+ * Therefore, we cheat and return success.
+ */
+ if (error == ENOENT)
+ error = 0;
+ }
+ np->n_attrstamp = 0;
+ vrele(dvp);
+ vrele(vp);
+ return (error);
+}
+
+/*
+ * nfs file remove rpc called from nfs_inactive
+ */
+int
+nfs_removeit(sp)
+ register struct sillyrename *sp;
+{
+ register u_long *tl;
+ register caddr_t cp;
+ register long t2;
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ nfsstats.rpccnt[NFSPROC_REMOVE]++;
+ nfsm_reqhead(sp->s_dvp, NFSPROC_REMOVE,
+ NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(sp->s_namlen));
+ nfsm_fhtom(sp->s_dvp);
+ nfsm_strtom(sp->s_name, sp->s_namlen, NFS_MAXNAMLEN);
+ nfsm_request(sp->s_dvp, NFSPROC_REMOVE, NULL, sp->s_cred);
+ nfsm_reqdone;
+ VTONFS(sp->s_dvp)->n_flag |= NMODIFIED;
+ VTONFS(sp->s_dvp)->n_attrstamp = 0;
+ return (error);
+}
+
+/*
+ * nfs file rename call
+ */
+int
+nfs_rename(ap)
+ struct vop_rename_args /* {
+ struct vnode *a_fdvp;
+ struct vnode *a_fvp;
+ struct componentname *a_fcnp;
+ struct vnode *a_tdvp;
+ struct vnode *a_tvp;
+ struct componentname *a_tcnp;
+ } */ *ap;
+{
+ register struct vnode *fvp = ap->a_fvp;
+ register struct vnode *tvp = ap->a_tvp;
+ register struct vnode *fdvp = ap->a_fdvp;
+ register struct vnode *tdvp = ap->a_tdvp;
+ register struct componentname *tcnp = ap->a_tcnp;
+ register struct componentname *fcnp = ap->a_fcnp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t2;
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ /* Check for cross-device rename */
+ if ((fvp->v_mount != tdvp->v_mount) ||
+ (tvp && (fvp->v_mount != tvp->v_mount))) {
+ error = EXDEV;
+ goto out;
+ }
+
+
+ nfsstats.rpccnt[NFSPROC_RENAME]++;
+ nfsm_reqhead(fdvp, NFSPROC_RENAME,
+ (NFSX_FH+NFSX_UNSIGNED)*2+nfsm_rndup(fcnp->cn_namelen)+
+ nfsm_rndup(fcnp->cn_namelen)); /* or fcnp->cn_cred?*/
+ nfsm_fhtom(fdvp);
+ nfsm_strtom(fcnp->cn_nameptr, fcnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_fhtom(tdvp);
+ nfsm_strtom(tcnp->cn_nameptr, tcnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_request(fdvp, NFSPROC_RENAME, tcnp->cn_proc, tcnp->cn_cred);
+ nfsm_reqdone;
+ VTONFS(fdvp)->n_flag |= NMODIFIED;
+ VTONFS(fdvp)->n_attrstamp = 0;
+ VTONFS(tdvp)->n_flag |= NMODIFIED;
+ VTONFS(tdvp)->n_attrstamp = 0;
+ if (fvp->v_type == VDIR) {
+ if (tvp != NULL && tvp->v_type == VDIR)
+ cache_purge(tdvp);
+ cache_purge(fdvp);
+ }
+out:
+ if (tdvp == tvp)
+ vrele(tdvp);
+ else
+ vput(tdvp);
+ if (tvp)
+ vput(tvp);
+ vrele(fdvp);
+ vrele(fvp);
+ /*
+ * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
+ */
+ if (error == ENOENT)
+ error = 0;
+ return (error);
+}
+
+/*
+ * nfs file rename rpc called from nfs_remove() above
+ */
+int
+nfs_renameit(sdvp, scnp, sp)
+ struct vnode *sdvp;
+ struct componentname *scnp;
+ register struct sillyrename *sp;
+{
+ register u_long *tl;
+ register caddr_t cp;
+ register long t2;
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ nfsstats.rpccnt[NFSPROC_RENAME]++;
+ nfsm_reqhead(sdvp, NFSPROC_RENAME,
+ (NFSX_FH+NFSX_UNSIGNED)*2+nfsm_rndup(scnp->cn_namelen)+
+ nfsm_rndup(sp->s_namlen));
+ nfsm_fhtom(sdvp);
+ nfsm_strtom(scnp->cn_nameptr, scnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_fhtom(sdvp);
+ nfsm_strtom(sp->s_name, sp->s_namlen, NFS_MAXNAMLEN);
+ nfsm_request(sdvp, NFSPROC_RENAME, scnp->cn_proc, scnp->cn_cred);
+ nfsm_reqdone;
+ FREE(scnp->cn_pnbuf, M_NAMEI);
+ VTONFS(sdvp)->n_flag |= NMODIFIED;
+ VTONFS(sdvp)->n_attrstamp = 0;
+ return (error);
+}
+
+/*
+ * nfs hard link create call
+ */
+int
+nfs_link(ap)
+ struct vop_link_args /* {
+ struct vnode *a_vp;
+ struct vnode *a_tdvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct vnode *tdvp = ap->a_tdvp;
+ register struct componentname *cnp = ap->a_cnp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t2;
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ if (vp->v_mount != tdvp->v_mount) {
+ /*VOP_ABORTOP(vp, cnp);*/
+ if (tdvp == vp)
+ vrele(vp);
+ else
+ vput(vp);
+ return (EXDEV);
+ }
+
+ nfsstats.rpccnt[NFSPROC_LINK]++;
+ nfsm_reqhead(tdvp, NFSPROC_LINK,
+ NFSX_FH*2+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen));
+ nfsm_fhtom(tdvp);
+ nfsm_fhtom(vp);
+ nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_request(tdvp, NFSPROC_LINK, cnp->cn_proc, cnp->cn_cred);
+ nfsm_reqdone;
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ VTONFS(tdvp)->n_attrstamp = 0;
+ VTONFS(tdvp)->n_flag |= NMODIFIED;
+ VTONFS(vp)->n_attrstamp = 0;
+ vrele(vp);
+ /*
+ * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
+ */
+ if (error == EEXIST)
+ error = 0;
+ return (error);
+}
+
+/*
+ * nfs symbolic link create call
+ */
+/* start here */
+int
+nfs_symlink(ap)
+ struct vop_symlink_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ char *a_target;
+ } */ *ap;
+{
+ register struct vnode *dvp = ap->a_dvp;
+ register struct vattr *vap = ap->a_vap;
+ register struct componentname *cnp = ap->a_cnp;
+ register struct nfsv2_sattr *sp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t2;
+ caddr_t bpos, dpos;
+ int slen, error = 0, isnq;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ nfsstats.rpccnt[NFSPROC_SYMLINK]++;
+ slen = strlen(ap->a_target);
+ isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ nfsm_reqhead(dvp, NFSPROC_SYMLINK, NFSX_FH+2*NFSX_UNSIGNED+
+ nfsm_rndup(cnp->cn_namelen)+nfsm_rndup(slen)+NFSX_SATTR(isnq));
+ nfsm_fhtom(dvp);
+ nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN);
+ nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq));
+ sp->sa_mode = vtonfs_mode(VLNK, vap->va_mode);
+ sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
+ sp->sa_gid = txdr_unsigned(cnp->cn_cred->cr_gid);
+ if (isnq) {
+ quad_t qval = -1;
+
+ txdr_hyper(&qval, &sp->sa_nqsize);
+ sp->sa_nqflags = 0;
+ txdr_nqtime(&vap->va_atime, &sp->sa_nqatime);
+ txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime);
+ } else {
+ sp->sa_nfssize = -1;
+ txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
+ txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+ }
+ nfsm_request(dvp, NFSPROC_SYMLINK, cnp->cn_proc, cnp->cn_cred);
+ nfsm_reqdone;
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ VTONFS(dvp)->n_flag |= NMODIFIED;
+ VTONFS(dvp)->n_attrstamp = 0;
+ vrele(dvp);
+ /*
+ * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
+ */
+ if (error == EEXIST)
+ error = 0;
+ return (error);
+}
+
+/*
+ * nfs make dir call
+ */
+int
+nfs_mkdir(ap)
+ struct vop_mkdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ register struct vnode *dvp = ap->a_dvp;
+ register struct vattr *vap = ap->a_vap;
+ register struct componentname *cnp = ap->a_cnp;
+ register struct vnode **vpp = ap->a_vpp;
+ register struct nfsv2_sattr *sp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1, t2;
+ register int len;
+ caddr_t bpos, dpos, cp2;
+ int error = 0, firsttry = 1, isnq;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ struct vattr vattr;
+
+ if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
+ VOP_ABORTOP(dvp, cnp);
+ vput(dvp);
+ return (error);
+ }
+ len = cnp->cn_namelen;
+ isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ nfsstats.rpccnt[NFSPROC_MKDIR]++;
+ nfsm_reqhead(dvp, NFSPROC_MKDIR,
+ NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len)+NFSX_SATTR(isnq));
+ nfsm_fhtom(dvp);
+ nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
+ nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq));
+ sp->sa_mode = vtonfs_mode(VDIR, vap->va_mode);
+ sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
+ sp->sa_gid = txdr_unsigned(vattr.va_gid);
+ if (isnq) {
+ quad_t qval = -1;
+
+ txdr_hyper(&qval, &sp->sa_nqsize);
+ sp->sa_nqflags = 0;
+ txdr_nqtime(&vap->va_atime, &sp->sa_nqatime);
+ txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime);
+ } else {
+ sp->sa_nfssize = -1;
+ txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
+ txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+ }
+ nfsm_request(dvp, NFSPROC_MKDIR, cnp->cn_proc, cnp->cn_cred);
+ nfsm_mtofh(dvp, *vpp);
+ nfsm_reqdone;
+ VTONFS(dvp)->n_flag |= NMODIFIED;
+ VTONFS(dvp)->n_attrstamp = 0;
+ /*
+ * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
+ * if we can succeed in looking up the directory.
+ * "firsttry" is necessary since the macros may "goto nfsmout" which
+ * is above the if on errors. (Ugh)
+ */
+ if (error == EEXIST && firsttry) {
+ firsttry = 0;
+ error = 0;
+ nfsstats.rpccnt[NFSPROC_LOOKUP]++;
+ *vpp = NULL;
+ nfsm_reqhead(dvp, NFSPROC_LOOKUP,
+ NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len));
+ nfsm_fhtom(dvp);
+ nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
+ nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred);
+ nfsm_mtofh(dvp, *vpp);
+ if ((*vpp)->v_type != VDIR) {
+ vput(*vpp);
+ error = EEXIST;
+ }
+ m_freem(mrep);
+ }
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ vrele(dvp);
+ return (error);
+}
+
+/*
+ * nfs remove directory call
+ */
+int
+nfs_rmdir(ap)
+ struct vop_rmdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct vnode *dvp = ap->a_dvp;
+ register struct componentname *cnp = ap->a_cnp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t2;
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ if (dvp == vp) {
+ vrele(dvp);
+ vrele(dvp);
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ return (EINVAL);
+ }
+ nfsstats.rpccnt[NFSPROC_RMDIR]++;
+ nfsm_reqhead(dvp, NFSPROC_RMDIR,
+ NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen));
+ nfsm_fhtom(dvp);
+ nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_request(dvp, NFSPROC_RMDIR, cnp->cn_proc, cnp->cn_cred);
+ nfsm_reqdone;
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ VTONFS(dvp)->n_flag |= NMODIFIED;
+ VTONFS(dvp)->n_attrstamp = 0;
+ cache_purge(dvp);
+ cache_purge(vp);
+ vrele(vp);
+ vrele(dvp);
+ /*
+ * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
+ */
+ if (error == ENOENT)
+ error = 0;
+ return (error);
+}
+
+/*
+ * nfs readdir call
+ * Although cookie is defined as opaque, I translate it to/from net byte
+ * order so that it looks more sensible. This appears consistent with the
+ * Ultrix implementation of NFS.
+ */
+int
+nfs_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+ register struct uio *uio = ap->a_uio;
+ int tresid, error;
+ struct vattr vattr;
+
+ if (vp->v_type != VDIR)
+ return (EPERM);
+ /*
+ * First, check for hit on the EOF offset cache
+ */
+ if (uio->uio_offset != 0 && uio->uio_offset == np->n_direofoffset &&
+ (np->n_flag & NMODIFIED) == 0) {
+ if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) {
+ if (NQNFS_CKCACHABLE(vp, NQL_READ)) {
+ nfsstats.direofcache_hits++;
+ return (0);
+ }
+ } else if (VOP_GETATTR(vp, &vattr, ap->a_cred, uio->uio_procp) == 0 &&
+ np->n_mtime == vattr.va_mtime.ts_sec) {
+ nfsstats.direofcache_hits++;
+ return (0);
+ }
+ }
+
+ /*
+ * Call nfs_bioread() to do the real work.
+ */
+ tresid = uio->uio_resid;
+ error = nfs_bioread(vp, uio, 0, ap->a_cred);
+
+ if (!error && uio->uio_resid == tresid)
+ nfsstats.direofcache_misses++;
+ return (error);
+}
+
+/*
+ * Readdir rpc call.
+ * Called from below the buffer cache by nfs_doio().
+ */
+int
+nfs_readdirrpc(vp, uiop, cred)
+ register struct vnode *vp;
+ struct uio *uiop;
+ struct ucred *cred;
+{
+ register long len;
+ register struct dirent *dp = 0;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1;
+ long tlen, lastlen = 0;
+ caddr_t bpos, dpos, cp2;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ struct mbuf *md2;
+ caddr_t dpos2;
+ int siz;
+ int more_dirs = 1;
+ u_long off, savoff = 0;
+ struct dirent *savdp = 0;
+ struct nfsmount *nmp;
+ struct nfsnode *np = VTONFS(vp);
+ long tresid;
+
+ nmp = VFSTONFS(vp->v_mount);
+ tresid = uiop->uio_resid;
+ /*
+ * Loop around doing readdir rpc's of size uio_resid or nm_rsize,
+ * whichever is smaller, truncated to a multiple of NFS_DIRBLKSIZ.
+ * The stopping criteria is EOF or buffer full.
+ */
+ while (more_dirs && uiop->uio_resid >= NFS_DIRBLKSIZ) {
+ nfsstats.rpccnt[NFSPROC_READDIR]++;
+ nfsm_reqhead(vp, NFSPROC_READDIR,
+ NFSX_FH + 2 * NFSX_UNSIGNED);
+ nfsm_fhtom(vp);
+ nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
+ off = (u_long)uiop->uio_offset;
+ *tl++ = txdr_unsigned(off);
+ *tl = txdr_unsigned(((uiop->uio_resid > nmp->nm_rsize) ?
+ nmp->nm_rsize : uiop->uio_resid) & ~(NFS_DIRBLKSIZ-1));
+ nfsm_request(vp, NFSPROC_READDIR, uiop->uio_procp, cred);
+ siz = 0;
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ more_dirs = fxdr_unsigned(int, *tl);
+
+ /* Save the position so that we can do nfsm_mtouio() later */
+ dpos2 = dpos;
+ md2 = md;
+
+ /* loop thru the dir entries, doctoring them to 4bsd form */
+#ifdef lint
+ dp = (struct dirent *)0;
+#endif /* lint */
+ while (more_dirs && siz < uiop->uio_resid) {
+ savoff = off; /* Hold onto offset and dp */
+ savdp = dp;
+ nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+ dp = (struct dirent *)tl;
+ dp->d_fileno = fxdr_unsigned(u_long, *tl++);
+ len = fxdr_unsigned(int, *tl);
+ if (len <= 0 || len > NFS_MAXNAMLEN) {
+ error = EBADRPC;
+ m_freem(mrep);
+ goto nfsmout;
+ }
+ dp->d_namlen = (u_char)len;
+ dp->d_type = DT_UNKNOWN;
+ nfsm_adv(len); /* Point past name */
+ tlen = nfsm_rndup(len);
+ /*
+ * This should not be necessary, but some servers have
+ * broken XDR such that these bytes are not null filled.
+ */
+ if (tlen != len) {
+ *dpos = '\0'; /* Null-terminate */
+ nfsm_adv(tlen - len);
+ len = tlen;
+ }
+ nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+ off = fxdr_unsigned(u_long, *tl);
+ *tl++ = 0; /* Ensures null termination of name */
+ more_dirs = fxdr_unsigned(int, *tl);
+ dp->d_reclen = len + 4 * NFSX_UNSIGNED;
+ siz += dp->d_reclen;
+ }
+ /*
+ * If at end of rpc data, get the eof boolean
+ */
+ if (!more_dirs) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ more_dirs = (fxdr_unsigned(int, *tl) == 0);
+
+ /*
+ * If at EOF, cache directory offset
+ */
+ if (!more_dirs)
+ np->n_direofoffset = off;
+ }
+ /*
+ * If there is too much to fit in the data buffer, use savoff and
+ * savdp to trim off the last record.
+ * --> we are not at eof
+ */
+ if (siz > uiop->uio_resid) {
+ off = savoff;
+ siz -= dp->d_reclen;
+ dp = savdp;
+ more_dirs = 0; /* Paranoia */
+ }
+ if (siz > 0) {
+ lastlen = dp->d_reclen;
+ md = md2;
+ dpos = dpos2;
+ nfsm_mtouio(uiop, siz);
+ uiop->uio_offset = (off_t)off;
+ } else
+ more_dirs = 0; /* Ugh, never happens, but in case.. */
+ m_freem(mrep);
+ }
+ /*
+ * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ
+ * by increasing d_reclen for the last record.
+ */
+ if (uiop->uio_resid < tresid) {
+ len = uiop->uio_resid & (NFS_DIRBLKSIZ - 1);
+ if (len > 0) {
+ dp = (struct dirent *)
+ (uiop->uio_iov->iov_base - lastlen);
+ dp->d_reclen += len;
+ uiop->uio_iov->iov_base += len;
+ uiop->uio_iov->iov_len -= len;
+ uiop->uio_resid -= len;
+ }
+ }
+nfsmout:
+ return (error);
+}
+
+/*
+ * Nqnfs readdir_and_lookup RPC. Used in place of nfs_readdirrpc().
+ */
+int
+nfs_readdirlookrpc(vp, uiop, cred)
+ struct vnode *vp;
+ register struct uio *uiop;
+ struct ucred *cred;
+{
+ register int len;
+ register struct dirent *dp = 0;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1;
+ caddr_t bpos, dpos, cp2;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ struct nameidata nami, *ndp = &nami;
+ struct componentname *cnp = &ndp->ni_cnd;
+ u_long off, endoff = 0, fileno;
+ time_t reqtime, ltime = 0;
+ struct nfsmount *nmp;
+ struct nfsnode *np;
+ struct vnode *newvp;
+ nfsv2fh_t *fhp;
+ u_quad_t frev;
+ int error = 0, tlen, more_dirs = 1, tresid, doit, bigenough, i;
+ int cachable = 0;
+
+ if (uiop->uio_iovcnt != 1)
+ panic("nfs rdirlook");
+ nmp = VFSTONFS(vp->v_mount);
+ tresid = uiop->uio_resid;
+ ndp->ni_dvp = vp;
+ newvp = NULLVP;
+ /*
+ * Loop around doing readdir rpc's of size uio_resid or nm_rsize,
+ * whichever is smaller, truncated to a multiple of NFS_DIRBLKSIZ.
+ * The stopping criteria is EOF or buffer full.
+ */
+ while (more_dirs && uiop->uio_resid >= NFS_DIRBLKSIZ) {
+ nfsstats.rpccnt[NQNFSPROC_READDIRLOOK]++;
+ nfsm_reqhead(vp, NQNFSPROC_READDIRLOOK,
+ NFSX_FH + 3 * NFSX_UNSIGNED);
+ nfsm_fhtom(vp);
+ nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
+ off = (u_long)uiop->uio_offset;
+ *tl++ = txdr_unsigned(off);
+ *tl++ = txdr_unsigned(((uiop->uio_resid > nmp->nm_rsize) ?
+ nmp->nm_rsize : uiop->uio_resid) & ~(NFS_DIRBLKSIZ-1));
+ if (nmp->nm_flag & NFSMNT_NQLOOKLEASE)
+ *tl = txdr_unsigned(nmp->nm_leaseterm);
+ else
+ *tl = 0;
+ reqtime = time.tv_sec;
+ nfsm_request(vp, NQNFSPROC_READDIRLOOK, uiop->uio_procp, cred);
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ more_dirs = fxdr_unsigned(int, *tl);
+
+ /* loop thru the dir entries, doctoring them to 4bsd form */
+ bigenough = 1;
+ while (more_dirs && bigenough) {
+ doit = 1;
+ nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED);
+ if (nmp->nm_flag & NFSMNT_NQLOOKLEASE) {
+ cachable = fxdr_unsigned(int, *tl++);
+ ltime = reqtime + fxdr_unsigned(int, *tl++);
+ fxdr_hyper(tl, &frev);
+ }
+ nfsm_dissect(fhp, nfsv2fh_t *, NFSX_FH);
+ if (!bcmp(VTONFS(vp)->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) {
+ VREF(vp);
+ newvp = vp;
+ np = VTONFS(vp);
+ } else {
+ if (error = nfs_nget(vp->v_mount, fhp, &np))
+ doit = 0;
+ newvp = NFSTOV(np);
+ }
+ if (error = nfs_loadattrcache(&newvp, &md, &dpos,
+ (struct vattr *)0))
+ doit = 0;
+ nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+ fileno = fxdr_unsigned(u_long, *tl++);
+ len = fxdr_unsigned(int, *tl);
+ if (len <= 0 || len > NFS_MAXNAMLEN) {
+ error = EBADRPC;
+ m_freem(mrep);
+ goto nfsmout;
+ }
+ tlen = (len + 4) & ~0x3;
+ if ((tlen + DIRHDSIZ) > uiop->uio_resid)
+ bigenough = 0;
+ if (bigenough && doit) {
+ dp = (struct dirent *)uiop->uio_iov->iov_base;
+ dp->d_fileno = fileno;
+ dp->d_namlen = len;
+ dp->d_reclen = tlen + DIRHDSIZ;
+ dp->d_type =
+ IFTODT(VTTOIF(np->n_vattr.va_type));
+ uiop->uio_resid -= DIRHDSIZ;
+ uiop->uio_iov->iov_base += DIRHDSIZ;
+ uiop->uio_iov->iov_len -= DIRHDSIZ;
+ cnp->cn_nameptr = uiop->uio_iov->iov_base;
+ cnp->cn_namelen = len;
+ ndp->ni_vp = newvp;
+ nfsm_mtouio(uiop, len);
+ cp = uiop->uio_iov->iov_base;
+ tlen -= len;
+ for (i = 0; i < tlen; i++)
+ *cp++ = '\0';
+ uiop->uio_iov->iov_base += tlen;
+ uiop->uio_iov->iov_len -= tlen;
+ uiop->uio_resid -= tlen;
+ cnp->cn_hash = 0;
+ for (cp = cnp->cn_nameptr, i = 1; i <= len; i++, cp++)
+ cnp->cn_hash += (unsigned char)*cp * i;
+ if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) &&
+ ltime > time.tv_sec)
+ nqnfs_clientlease(nmp, np, NQL_READ,
+ cachable, ltime, frev);
+ if (cnp->cn_namelen <= NCHNAMLEN)
+ cache_enter(ndp->ni_dvp, ndp->ni_vp, cnp);
+ } else {
+ nfsm_adv(nfsm_rndup(len));
+ }
+ if (newvp != NULLVP) {
+ vrele(newvp);
+ newvp = NULLVP;
+ }
+ nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+ if (bigenough)
+ endoff = off = fxdr_unsigned(u_long, *tl++);
+ else
+ endoff = fxdr_unsigned(u_long, *tl++);
+ more_dirs = fxdr_unsigned(int, *tl);
+ }
+ /*
+ * If at end of rpc data, get the eof boolean
+ */
+ if (!more_dirs) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ more_dirs = (fxdr_unsigned(int, *tl) == 0);
+
+ /*
+ * If at EOF, cache directory offset
+ */
+ if (!more_dirs)
+ VTONFS(vp)->n_direofoffset = endoff;
+ }
+ if (uiop->uio_resid < tresid)
+ uiop->uio_offset = (off_t)off;
+ else
+ more_dirs = 0;
+ m_freem(mrep);
+ }
+ /*
+ * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ
+ * by increasing d_reclen for the last record.
+ */
+ if (uiop->uio_resid < tresid) {
+ len = uiop->uio_resid & (NFS_DIRBLKSIZ - 1);
+ if (len > 0) {
+ dp->d_reclen += len;
+ uiop->uio_iov->iov_base += len;
+ uiop->uio_iov->iov_len -= len;
+ uiop->uio_resid -= len;
+ }
+ }
+nfsmout:
+ if (newvp != NULLVP)
+ vrele(newvp);
+ return (error);
+}
+static char hextoasc[] = "0123456789abcdef";
+
+/*
+ * Silly rename. To make the NFS filesystem that is stateless look a little
+ * more like the "ufs" a remove of an active vnode is translated to a rename
+ * to a funny looking filename that is removed by nfs_inactive on the
+ * nfsnode. There is the potential for another process on a different client
+ * to create the same funny name between the nfs_lookitup() fails and the
+ * nfs_rename() completes, but...
+ */
+int
+nfs_sillyrename(dvp, vp, cnp)
+ struct vnode *dvp, *vp;
+ struct componentname *cnp;
+{
+ register struct nfsnode *np;
+ register struct sillyrename *sp;
+ int error;
+ short pid;
+
+ cache_purge(dvp);
+ np = VTONFS(vp);
+#ifdef SILLYSEPARATE
+ MALLOC(sp, struct sillyrename *, sizeof (struct sillyrename),
+ M_NFSREQ, M_WAITOK);
+#else
+ sp = &np->n_silly;
+#endif
+ sp->s_cred = crdup(cnp->cn_cred);
+ sp->s_dvp = dvp;
+ VREF(dvp);
+
+ /* Fudge together a funny name */
+ pid = cnp->cn_proc->p_pid;
+ bcopy(".nfsAxxxx4.4", sp->s_name, 13);
+ sp->s_namlen = 12;
+ sp->s_name[8] = hextoasc[pid & 0xf];
+ sp->s_name[7] = hextoasc[(pid >> 4) & 0xf];
+ sp->s_name[6] = hextoasc[(pid >> 8) & 0xf];
+ sp->s_name[5] = hextoasc[(pid >> 12) & 0xf];
+
+ /* Try lookitups until we get one that isn't there */
+ while (nfs_lookitup(sp, (nfsv2fh_t *)0, cnp->cn_proc) == 0) {
+ sp->s_name[4]++;
+ if (sp->s_name[4] > 'z') {
+ error = EINVAL;
+ goto bad;
+ }
+ }
+ if (error = nfs_renameit(dvp, cnp, sp))
+ goto bad;
+ nfs_lookitup(sp, &np->n_fh, cnp->cn_proc);
+ np->n_sillyrename = sp;
+ return (0);
+bad:
+ vrele(sp->s_dvp);
+ crfree(sp->s_cred);
+#ifdef SILLYSEPARATE
+ free((caddr_t)sp, M_NFSREQ);
+#endif
+ return (error);
+}
+
+/*
+ * Look up a file name for silly rename stuff.
+ * Just like nfs_lookup() except that it doesn't load returned values
+ * into the nfsnode table.
+ * If fhp != NULL it copies the returned file handle out
+ */
+int
+nfs_lookitup(sp, fhp, procp)
+ register struct sillyrename *sp;
+ nfsv2fh_t *fhp;
+ struct proc *procp;
+{
+ register struct vnode *vp = sp->s_dvp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1, t2;
+ caddr_t bpos, dpos, cp2;
+ int error = 0, isnq;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ long len;
+
+ isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ nfsstats.rpccnt[NFSPROC_LOOKUP]++;
+ len = sp->s_namlen;
+ nfsm_reqhead(vp, NFSPROC_LOOKUP, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len));
+ if (isnq) {
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = 0;
+ }
+ nfsm_fhtom(vp);
+ nfsm_strtom(sp->s_name, len, NFS_MAXNAMLEN);
+ nfsm_request(vp, NFSPROC_LOOKUP, procp, sp->s_cred);
+ if (fhp != NULL) {
+ if (isnq)
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ nfsm_dissect(cp, caddr_t, NFSX_FH);
+ bcopy(cp, (caddr_t)fhp, NFSX_FH);
+ }
+ nfsm_reqdone;
+ return (error);
+}
+
+/*
+ * Kludge City..
+ * - make nfs_bmap() essentially a no-op that does no translation
+ * - do nfs_strategy() by faking physical I/O with nfs_readrpc/nfs_writerpc
+ * after mapping the physical addresses into Kernel Virtual space in the
+ * nfsiobuf area.
+ * (Maybe I could use the process's page mapping, but I was concerned that
+ * Kernel Write might not be enabled and also figured copyout() would do
+ * a lot more work than bcopy() and also it currently happens in the
+ * context of the swapper process (2).
+ */
+int
+nfs_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = vp;
+ if (ap->a_bnp != NULL)
+ *ap->a_bnp = ap->a_bn * btodb(vp->v_mount->mnt_stat.f_iosize);
+ return (0);
+}
+
+/*
+ * Strategy routine.
+ * For async requests when nfsiod(s) are running, queue the request by
+ * calling nfs_asyncio(), otherwise just all nfs_doio() to do the
+ * request.
+ */
+int
+nfs_strategy(ap)
+ struct vop_strategy_args *ap;
+{
+ register struct buf *bp = ap->a_bp;
+ struct ucred *cr;
+ struct proc *p;
+ int error = 0;
+
+ if (bp->b_flags & B_PHYS)
+ panic("nfs physio");
+ if (bp->b_flags & B_ASYNC)
+ p = (struct proc *)0;
+ else
+ p = curproc; /* XXX */
+ if (bp->b_flags & B_READ)
+ cr = bp->b_rcred;
+ else
+ cr = bp->b_wcred;
+ /*
+ * If the op is asynchronous and an i/o daemon is waiting
+ * queue the request, wake it up and wait for completion
+ * otherwise just do it ourselves.
+ */
+ if ((bp->b_flags & B_ASYNC) == 0 ||
+ nfs_asyncio(bp, NOCRED))
+ error = nfs_doio(bp, cr, p);
+ return (error);
+}
+
+/*
+ * Mmap a file
+ *
+ * NB Currently unsupported.
+ */
+/* ARGSUSED */
+int
+nfs_mmap(ap)
+ struct vop_mmap_args /* {
+ struct vnode *a_vp;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (EINVAL);
+}
+
+/*
+ * Flush all the blocks associated with a vnode.
+ * Walk through the buffer pool and push any dirty pages
+ * associated with the vnode.
+ */
+/* ARGSUSED */
+int
+nfs_fsync(ap)
+ struct vop_fsync_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode * a_vp;
+ struct ucred * a_cred;
+ int a_waitfor;
+ struct proc * a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+ register struct buf *bp;
+ struct buf *nbp;
+ struct nfsmount *nmp;
+ int s, error = 0, slptimeo = 0, slpflag = 0;
+
+ nmp = VFSTONFS(vp->v_mount);
+ if (nmp->nm_flag & NFSMNT_INT)
+ slpflag = PCATCH;
+loop:
+ s = splbio();
+ for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
+ nbp = bp->b_vnbufs.le_next;
+ if (bp->b_flags & B_BUSY) {
+ if (ap->a_waitfor != MNT_WAIT)
+ continue;
+ bp->b_flags |= B_WANTED;
+ error = tsleep((caddr_t)bp, slpflag | (PRIBIO + 1),
+ "nfsfsync", slptimeo);
+ splx(s);
+ if (error) {
+ if (nfs_sigintr(nmp, (struct nfsreq *)0, ap->a_p))
+ return (EINTR);
+ if (slpflag == PCATCH) {
+ slpflag = 0;
+ slptimeo = 2 * hz;
+ }
+ }
+ goto loop;
+ }
+ if ((bp->b_flags & B_DELWRI) == 0)
+ panic("nfs_fsync: not dirty");
+ bremfree(bp);
+ bp->b_flags |= B_BUSY;
+ splx(s);
+ bp->b_flags |= B_ASYNC;
+ VOP_BWRITE(bp);
+ goto loop;
+ }
+ splx(s);
+ if (ap->a_waitfor == MNT_WAIT) {
+ while (vp->v_numoutput) {
+ vp->v_flag |= VBWAIT;
+ error = tsleep((caddr_t)&vp->v_numoutput,
+ slpflag | (PRIBIO + 1), "nfsfsync", slptimeo);
+ if (error) {
+ if (nfs_sigintr(nmp, (struct nfsreq *)0, ap->a_p))
+ return (EINTR);
+ if (slpflag == PCATCH) {
+ slpflag = 0;
+ slptimeo = 2 * hz;
+ }
+ }
+ }
+ if (vp->v_dirtyblkhd.lh_first) {
+#ifdef DIAGNOSTIC
+ vprint("nfs_fsync: dirty", vp);
+#endif
+ goto loop;
+ }
+ }
+ if (np->n_flag & NWRITEERR) {
+ error = np->n_error;
+ np->n_flag &= ~NWRITEERR;
+ }
+ return (error);
+}
+
+/*
+ * Return POSIX pathconf information applicable to nfs.
+ *
+ * Currently the NFS protocol does not support getting such
+ * information from the remote server.
+ */
+/* ARGSUSED */
+int
+nfs_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+
+ return (EINVAL);
+}
+
+/*
+ * NFS advisory byte-level locks.
+ * Currently unsupported.
+ */
+int
+nfs_advlock(ap)
+ struct vop_advlock_args /* {
+ struct vnode *a_vp;
+ caddr_t a_id;
+ int a_op;
+ struct flock *a_fl;
+ int a_flags;
+ } */ *ap;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * Print out the contents of an nfsnode.
+ */
+int
+nfs_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+
+ printf("tag VT_NFS, fileid %d fsid 0x%x",
+ np->n_vattr.va_fileid, np->n_vattr.va_fsid);
+#ifdef FIFO
+ if (vp->v_type == VFIFO)
+ fifo_printinfo(vp);
+#endif /* FIFO */
+ printf("\n");
+ return (0);
+}
+
+/*
+ * NFS directory offset lookup.
+ * Currently unsupported.
+ */
+int
+nfs_blkatoff(ap)
+ struct vop_blkatoff_args /* {
+ struct vnode *a_vp;
+ off_t a_offset;
+ char **a_res;
+ struct buf **a_bpp;
+ } */ *ap;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * NFS flat namespace allocation.
+ * Currently unsupported.
+ */
+int
+nfs_valloc(ap)
+ struct vop_valloc_args /* {
+ struct vnode *a_pvp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct vnode **a_vpp;
+ } */ *ap;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * NFS flat namespace free.
+ * Currently unsupported.
+ */
+int
+nfs_vfree(ap)
+ struct vop_vfree_args /* {
+ struct vnode *a_pvp;
+ ino_t a_ino;
+ int a_mode;
+ } */ *ap;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * NFS file truncation.
+ */
+int
+nfs_truncate(ap)
+ struct vop_truncate_args /* {
+ struct vnode *a_vp;
+ off_t a_length;
+ int a_flags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ /* Use nfs_setattr */
+ printf("nfs_truncate: need to implement!!");
+ return (EOPNOTSUPP);
+}
+
+/*
+ * NFS update.
+ */
+int
+nfs_update(ap)
+ struct vop_update_args /* {
+ struct vnode *a_vp;
+ struct timeval *a_ta;
+ struct timeval *a_tm;
+ int a_waitfor;
+ } */ *ap;
+{
+
+ /* Use nfs_setattr */
+ printf("nfs_update: need to implement!!");
+ return (EOPNOTSUPP);
+}
+
+/*
+ * nfs special file access vnode op.
+ * Essentially just get vattr and then imitate iaccess() since the device is
+ * local to the client.
+ */
+int
+nfsspec_access(ap)
+ struct vop_access_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vattr *vap;
+ register gid_t *gp;
+ register struct ucred *cred = ap->a_cred;
+ mode_t mode = ap->a_mode;
+ struct vattr vattr;
+ register int i;
+ int error;
+
+ /*
+ * If you're the super-user,
+ * you always get access.
+ */
+ if (cred->cr_uid == 0)
+ return (0);
+ vap = &vattr;
+ if (error = VOP_GETATTR(ap->a_vp, vap, cred, ap->a_p))
+ return (error);
+ /*
+ * Access check is based on only one of owner, group, public.
+ * If not owner, then check group. If not a member of the
+ * group, then check public access.
+ */
+ if (cred->cr_uid != vap->va_uid) {
+ mode >>= 3;
+ gp = cred->cr_groups;
+ for (i = 0; i < cred->cr_ngroups; i++, gp++)
+ if (vap->va_gid == *gp)
+ goto found;
+ mode >>= 3;
+found:
+ ;
+ }
+ return ((vap->va_mode & mode) == mode ? 0 : EACCES);
+}
+
+/*
+ * Read wrapper for special devices.
+ */
+int
+nfsspec_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct nfsnode *np = VTONFS(ap->a_vp);
+
+ /*
+ * Set access flag.
+ */
+ np->n_flag |= NACC;
+ np->n_atim = time;
+ return (VOCALL(spec_vnodeop_p, VOFFSET(vop_read), ap));
+}
+
+/*
+ * Write wrapper for special devices.
+ */
+int
+nfsspec_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct nfsnode *np = VTONFS(ap->a_vp);
+
+ /*
+ * Set update flag.
+ */
+ np->n_flag |= NUPD;
+ np->n_mtim = time;
+ return (VOCALL(spec_vnodeop_p, VOFFSET(vop_write), ap));
+}
+
+/*
+ * Close wrapper for special devices.
+ *
+ * Update the times on the nfsnode then do device close.
+ */
+int
+nfsspec_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+ struct vattr vattr;
+
+ if (np->n_flag & (NACC | NUPD)) {
+ np->n_flag |= NCHG;
+ if (vp->v_usecount == 1 &&
+ (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
+ VATTR_NULL(&vattr);
+ if (np->n_flag & NACC) {
+ vattr.va_atime.ts_sec = np->n_atim.tv_sec;
+ vattr.va_atime.ts_nsec =
+ np->n_atim.tv_usec * 1000;
+ }
+ if (np->n_flag & NUPD) {
+ vattr.va_mtime.ts_sec = np->n_mtim.tv_sec;
+ vattr.va_mtime.ts_nsec =
+ np->n_mtim.tv_usec * 1000;
+ }
+ (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p);
+ }
+ }
+ return (VOCALL(spec_vnodeop_p, VOFFSET(vop_close), ap));
+}
+
+#ifdef FIFO
+/*
+ * Read wrapper for fifos.
+ */
+int
+nfsfifo_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ extern int (**fifo_vnodeop_p)();
+ register struct nfsnode *np = VTONFS(ap->a_vp);
+
+ /*
+ * Set access flag.
+ */
+ np->n_flag |= NACC;
+ np->n_atim = time;
+ return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap));
+}
+
+/*
+ * Write wrapper for fifos.
+ */
+int
+nfsfifo_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ extern int (**fifo_vnodeop_p)();
+ register struct nfsnode *np = VTONFS(ap->a_vp);
+
+ /*
+ * Set update flag.
+ */
+ np->n_flag |= NUPD;
+ np->n_mtim = time;
+ return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap));
+}
+
+/*
+ * Close wrapper for fifos.
+ *
+ * Update the times on the nfsnode then do fifo close.
+ */
+int
+nfsfifo_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+ struct vattr vattr;
+ extern int (**fifo_vnodeop_p)();
+
+ if (np->n_flag & (NACC | NUPD)) {
+ if (np->n_flag & NACC)
+ np->n_atim = time;
+ if (np->n_flag & NUPD)
+ np->n_mtim = time;
+ np->n_flag |= NCHG;
+ if (vp->v_usecount == 1 &&
+ (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
+ VATTR_NULL(&vattr);
+ if (np->n_flag & NACC) {
+ vattr.va_atime.ts_sec = np->n_atim.tv_sec;
+ vattr.va_atime.ts_nsec =
+ np->n_atim.tv_usec * 1000;
+ }
+ if (np->n_flag & NUPD) {
+ vattr.va_mtime.ts_sec = np->n_mtim.tv_sec;
+ vattr.va_mtime.ts_nsec =
+ np->n_mtim.tv_usec * 1000;
+ }
+ (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p);
+ }
+ }
+ return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_close), ap));
+}
+#endif /* FIFO */
diff --git a/sys/nfs/nfsdiskless.h b/sys/nfs/nfsdiskless.h
new file mode 100644
index 0000000..74e6b7b
--- /dev/null
+++ b/sys/nfs/nfsdiskless.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsdiskless.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Structure that must be initialized for a diskless nfs client.
+ * This structure is used by nfs_mountroot() to set up the root and swap
+ * vnodes plus do a partial ifconfig(8) and route(8) so that the critical net
+ * interface can communicate with the server.
+ * The primary bootstrap is expected to fill in the appropriate fields before
+ * starting vmunix. Whether or not the swap area is nfs mounted is determined
+ * by the value in swdevt[0]. (equal to NODEV --> swap over nfs)
+ * Currently only works for AF_INET protocols.
+ * NB: All fields are stored in net byte order to avoid hassles with
+ * client/server byte ordering differences.
+ */
+struct nfs_diskless {
+ struct ifaliasreq myif; /* Default interface */
+ struct sockaddr_in mygateway; /* Default gateway */
+ struct nfs_args swap_args; /* Mount args for swap file */
+ u_char swap_fh[NFS_FHSIZE]; /* Swap file's file handle */
+ struct sockaddr_in swap_saddr; /* Address of swap server */
+ char swap_hostnam[MNAMELEN]; /* Host name for mount pt */
+ int swap_nblks; /* Size of server swap file */
+ struct ucred swap_ucred; /* Swap credentials */
+ struct nfs_args root_args; /* Mount args for root fs */
+ u_char root_fh[NFS_FHSIZE]; /* File handle of root dir */
+ struct sockaddr_in root_saddr; /* Address of root server */
+ char root_hostnam[MNAMELEN]; /* Host name for mount pt */
+ long root_time; /* Timestamp of root fs */
+ char my_hostnam[MAXHOSTNAMELEN]; /* Client host name */
+};
diff --git a/sys/nfs/nfsm_subs.h b/sys/nfs/nfsm_subs.h
new file mode 100644
index 0000000..879db36
--- /dev/null
+++ b/sys/nfs/nfsm_subs.h
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsm_subs.h 8.1 (Berkeley) 6/16/93
+ */
+
+/*
+ * These macros do strange and peculiar things to mbuf chains for
+ * the assistance of the nfs code. To attempt to use them for any
+ * other purpose will be dangerous. (they make weird assumptions)
+ */
+
+/*
+ * First define what the actual subs. return
+ */
+extern struct mbuf *nfsm_reqh();
+
+#define M_HASCL(m) ((m)->m_flags & M_EXT)
+#define NFSMINOFF(m) \
+ if (M_HASCL(m)) \
+ (m)->m_data = (m)->m_ext.ext_buf; \
+ else if ((m)->m_flags & M_PKTHDR) \
+ (m)->m_data = (m)->m_pktdat; \
+ else \
+ (m)->m_data = (m)->m_dat
+#define NFSMADV(m, s) (m)->m_data += (s)
+#define NFSMSIZ(m) ((M_HASCL(m))?MCLBYTES: \
+ (((m)->m_flags & M_PKTHDR)?MHLEN:MLEN))
+
+/*
+ * Now for the macros that do the simple stuff and call the functions
+ * for the hard stuff.
+ * These macros use several vars. declared in nfsm_reqhead and these
+ * vars. must not be used elsewhere unless you are careful not to corrupt
+ * them. The vars. starting with pN and tN (N=1,2,3,..) are temporaries
+ * that may be used so long as the value is not expected to retained
+ * after a macro.
+ * I know, this is kind of dorkey, but it makes the actual op functions
+ * fairly clean and deals with the mess caused by the xdr discriminating
+ * unions.
+ */
+
+#define nfsm_build(a,c,s) \
+ { if ((s) > M_TRAILINGSPACE(mb)) { \
+ MGET(mb2, M_WAIT, MT_DATA); \
+ if ((s) > MLEN) \
+ panic("build > MLEN"); \
+ mb->m_next = mb2; \
+ mb = mb2; \
+ mb->m_len = 0; \
+ bpos = mtod(mb, caddr_t); \
+ } \
+ (a) = (c)(bpos); \
+ mb->m_len += (s); \
+ bpos += (s); }
+
+#define nfsm_dissect(a,c,s) \
+ { t1 = mtod(md, caddr_t)+md->m_len-dpos; \
+ if (t1 >= (s)) { \
+ (a) = (c)(dpos); \
+ dpos += (s); \
+ } else if (error = nfsm_disct(&md, &dpos, (s), t1, &cp2)) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ } else { \
+ (a) = (c)cp2; \
+ } }
+
+#define nfsm_fhtom(v) \
+ nfsm_build(cp,caddr_t,NFSX_FH); \
+ bcopy((caddr_t)&(VTONFS(v)->n_fh), cp, NFSX_FH)
+
+#define nfsm_srvfhtom(f) \
+ nfsm_build(cp,caddr_t,NFSX_FH); \
+ bcopy((caddr_t)(f), cp, NFSX_FH)
+
+#define nfsm_mtofh(d,v) \
+ { struct nfsnode *np; nfsv2fh_t *fhp; \
+ nfsm_dissect(fhp,nfsv2fh_t *,NFSX_FH); \
+ if (error = nfs_nget((d)->v_mount, fhp, &np)) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ } \
+ (v) = NFSTOV(np); \
+ nfsm_loadattr(v, (struct vattr *)0); \
+ }
+
+#define nfsm_loadattr(v,a) \
+ { struct vnode *tvp = (v); \
+ if (error = nfs_loadattrcache(&tvp, &md, &dpos, (a))) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ } \
+ (v) = tvp; }
+
+#define nfsm_strsiz(s,m) \
+ { nfsm_dissect(tl,u_long *,NFSX_UNSIGNED); \
+ if (((s) = fxdr_unsigned(long,*tl)) > (m)) { \
+ m_freem(mrep); \
+ error = EBADRPC; \
+ goto nfsmout; \
+ } }
+
+#define nfsm_srvstrsiz(s,m) \
+ { nfsm_dissect(tl,u_long *,NFSX_UNSIGNED); \
+ if (((s) = fxdr_unsigned(long,*tl)) > (m) || (s) <= 0) { \
+ error = EBADRPC; \
+ nfsm_reply(0); \
+ } }
+
+#define nfsm_mtouio(p,s) \
+ if ((s) > 0 && \
+ (error = nfsm_mbuftouio(&md,(p),(s),&dpos))) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ }
+
+#define nfsm_uiotom(p,s) \
+ if (error = nfsm_uiotombuf((p),&mb,(s),&bpos)) { \
+ m_freem(mreq); \
+ goto nfsmout; \
+ }
+
+#define nfsm_reqhead(v,a,s) \
+ mb = mreq = nfsm_reqh((v),(a),(s),&bpos)
+
+#define nfsm_reqdone m_freem(mrep); \
+ nfsmout:
+
+#define nfsm_rndup(a) (((a)+3)&(~0x3))
+
+#define nfsm_request(v, t, p, c) \
+ if (error = nfs_request((v), mreq, (t), (p), \
+ (c), &mrep, &md, &dpos)) \
+ goto nfsmout
+
+#define nfsm_strtom(a,s,m) \
+ if ((s) > (m)) { \
+ m_freem(mreq); \
+ error = ENAMETOOLONG; \
+ goto nfsmout; \
+ } \
+ t2 = nfsm_rndup(s)+NFSX_UNSIGNED; \
+ if (t2 <= M_TRAILINGSPACE(mb)) { \
+ nfsm_build(tl,u_long *,t2); \
+ *tl++ = txdr_unsigned(s); \
+ *(tl+((t2>>2)-2)) = 0; \
+ bcopy((caddr_t)(a), (caddr_t)tl, (s)); \
+ } else if (error = nfsm_strtmbuf(&mb, &bpos, (a), (s))) { \
+ m_freem(mreq); \
+ goto nfsmout; \
+ }
+
+#define nfsm_srvdone \
+ nfsmout: \
+ return(error)
+
+#define nfsm_reply(s) \
+ { \
+ nfsd->nd_repstat = error; \
+ if (error) \
+ (void) nfs_rephead(0, nfsd, error, cache, &frev, \
+ mrq, &mb, &bpos); \
+ else \
+ (void) nfs_rephead((s), nfsd, error, cache, &frev, \
+ mrq, &mb, &bpos); \
+ m_freem(mrep); \
+ mreq = *mrq; \
+ if (error) \
+ return(0); \
+ }
+
+#define nfsm_adv(s) \
+ t1 = mtod(md, caddr_t)+md->m_len-dpos; \
+ if (t1 >= (s)) { \
+ dpos += (s); \
+ } else if (error = nfs_adv(&md, &dpos, (s), t1)) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ }
+
+#define nfsm_srvmtofh(f) \
+ nfsm_dissect(tl, u_long *, NFSX_FH); \
+ bcopy((caddr_t)tl, (caddr_t)f, NFSX_FH)
+
+#define nfsm_clget \
+ if (bp >= be) { \
+ if (mp == mb) \
+ mp->m_len += bp-bpos; \
+ MGET(mp, M_WAIT, MT_DATA); \
+ MCLGET(mp, M_WAIT); \
+ mp->m_len = NFSMSIZ(mp); \
+ mp2->m_next = mp; \
+ mp2 = mp; \
+ bp = mtod(mp, caddr_t); \
+ be = bp+mp->m_len; \
+ } \
+ tl = (u_long *)bp
+
+#define nfsm_srvfillattr \
+ fp->fa_type = vtonfs_type(vap->va_type); \
+ fp->fa_mode = vtonfs_mode(vap->va_type, vap->va_mode); \
+ fp->fa_nlink = txdr_unsigned(vap->va_nlink); \
+ fp->fa_uid = txdr_unsigned(vap->va_uid); \
+ fp->fa_gid = txdr_unsigned(vap->va_gid); \
+ if (nfsd->nd_nqlflag == NQL_NOVAL) { \
+ fp->fa_nfsblocksize = txdr_unsigned(vap->va_blocksize); \
+ if (vap->va_type == VFIFO) \
+ fp->fa_nfsrdev = 0xffffffff; \
+ else \
+ fp->fa_nfsrdev = txdr_unsigned(vap->va_rdev); \
+ fp->fa_nfsfsid = txdr_unsigned(vap->va_fsid); \
+ fp->fa_nfsfileid = txdr_unsigned(vap->va_fileid); \
+ fp->fa_nfssize = txdr_unsigned(vap->va_size); \
+ fp->fa_nfsblocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE); \
+ txdr_nfstime(&vap->va_atime, &fp->fa_nfsatime); \
+ txdr_nfstime(&vap->va_mtime, &fp->fa_nfsmtime); \
+ fp->fa_nfsctime.nfs_sec = txdr_unsigned(vap->va_ctime.ts_sec); \
+ fp->fa_nfsctime.nfs_usec = txdr_unsigned(vap->va_gen); \
+ } else { \
+ fp->fa_nqblocksize = txdr_unsigned(vap->va_blocksize); \
+ if (vap->va_type == VFIFO) \
+ fp->fa_nqrdev = 0xffffffff; \
+ else \
+ fp->fa_nqrdev = txdr_unsigned(vap->va_rdev); \
+ fp->fa_nqfsid = txdr_unsigned(vap->va_fsid); \
+ fp->fa_nqfileid = txdr_unsigned(vap->va_fileid); \
+ txdr_hyper(&vap->va_size, &fp->fa_nqsize); \
+ txdr_hyper(&vap->va_bytes, &fp->fa_nqbytes); \
+ txdr_nqtime(&vap->va_atime, &fp->fa_nqatime); \
+ txdr_nqtime(&vap->va_mtime, &fp->fa_nqmtime); \
+ txdr_nqtime(&vap->va_ctime, &fp->fa_nqctime); \
+ fp->fa_nqflags = txdr_unsigned(vap->va_flags); \
+ fp->fa_nqgen = txdr_unsigned(vap->va_gen); \
+ txdr_hyper(&vap->va_filerev, &fp->fa_nqfilerev); \
+ }
+
diff --git a/sys/nfs/nfsmount.h b/sys/nfs/nfsmount.h
new file mode 100644
index 0000000..4d74acb
--- /dev/null
+++ b/sys/nfs/nfsmount.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsmount.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Mount structure.
+ * One allocated on every NFS mount.
+ * Holds NFS specific information for mount.
+ */
+struct nfsmount {
+ int nm_flag; /* Flags for soft/hard... */
+ struct mount *nm_mountp; /* Vfs structure for this filesystem */
+ int nm_numgrps; /* Max. size of groupslist */
+ nfsv2fh_t nm_fh; /* File handle of root dir */
+ struct socket *nm_so; /* Rpc socket */
+ int nm_sotype; /* Type of socket */
+ int nm_soproto; /* and protocol */
+ int nm_soflags; /* pr_flags for socket protocol */
+ struct mbuf *nm_nam; /* Addr of server */
+ int nm_timeo; /* Init timer for NFSMNT_DUMBTIMR */
+ int nm_retry; /* Max retries */
+ int nm_srtt[4]; /* Timers for rpcs */
+ int nm_sdrtt[4];
+ int nm_sent; /* Request send count */
+ int nm_cwnd; /* Request send window */
+ int nm_timeouts; /* Request timeouts */
+ int nm_deadthresh; /* Threshold of timeouts-->dead server*/
+ int nm_rsize; /* Max size of read rpc */
+ int nm_wsize; /* Max size of write rpc */
+ int nm_readahead; /* Num. of blocks to readahead */
+ int nm_leaseterm; /* Term (sec) for NQNFS lease */
+ struct nfsnode *nm_tnext; /* Head of lease timer queue */
+ struct nfsnode *nm_tprev;
+ struct vnode *nm_inprog; /* Vnode in prog by nqnfs_clientd() */
+ uid_t nm_authuid; /* Uid for authenticator */
+ int nm_authtype; /* Authenticator type */
+ int nm_authlen; /* and length */
+ char *nm_authstr; /* Authenticator string */
+};
+
+#ifdef KERNEL
+/*
+ * Convert mount ptr to nfsmount ptr.
+ */
+#define VFSTONFS(mp) ((struct nfsmount *)((mp)->mnt_data))
+#endif /* KERNEL */
+
+/*
+ * Prototypes for NFS mount operations
+ */
+int nfs_mount __P((
+ struct mount *mp,
+ char *path,
+ caddr_t data,
+ struct nameidata *ndp,
+ struct proc *p));
+int nfs_start __P((
+ struct mount *mp,
+ int flags,
+ struct proc *p));
+int nfs_unmount __P((
+ struct mount *mp,
+ int mntflags,
+ struct proc *p));
+int nfs_root __P((
+ struct mount *mp,
+ struct vnode **vpp));
+int nfs_quotactl __P((
+ struct mount *mp,
+ int cmds,
+ uid_t uid,
+ caddr_t arg,
+ struct proc *p));
+int nfs_statfs __P((
+ struct mount *mp,
+ struct statfs *sbp,
+ struct proc *p));
+int nfs_sync __P((
+ struct mount *mp,
+ int waitfor,
+ struct ucred *cred,
+ struct proc *p));
+int nfs_fhtovp __P((
+ struct mount *mp,
+ struct fid *fhp,
+ struct mbuf *nam,
+ struct vnode **vpp,
+ int *exflagsp,
+ struct ucred **credanonp));
+int nfs_vptofh __P((
+ struct vnode *vp,
+ struct fid *fhp));
+int nfs_init __P(());
diff --git a/sys/nfs/nfsnode.h b/sys/nfs/nfsnode.h
new file mode 100644
index 0000000..f5fee5b
--- /dev/null
+++ b/sys/nfs/nfsnode.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsnode.h 8.4 (Berkeley) 2/13/94
+ */
+
+/*
+ * Silly rename structure that hangs off the nfsnode until the name
+ * can be removed by nfs_inactive()
+ */
+struct sillyrename {
+ struct ucred *s_cred;
+ struct vnode *s_dvp;
+ long s_namlen;
+ char s_name[20];
+};
+
+/*
+ * The nfsnode is the nfs equivalent to ufs's inode. Any similarity
+ * is purely coincidental.
+ * There is a unique nfsnode allocated for each active file,
+ * each current directory, each mounted-on file, text file, and the root.
+ * An nfsnode is 'named' by its file handle. (nget/nfs_node.c)
+ */
+
+struct nfsnode {
+ struct nfsnode *n_forw; /* hash, forward */
+ struct nfsnode **n_back; /* hash, backward */
+ nfsv2fh_t n_fh; /* NFS File Handle */
+ long n_flag; /* Flag for locking.. */
+ struct vnode *n_vnode; /* vnode associated with this node */
+ struct vattr n_vattr; /* Vnode attribute cache */
+ time_t n_attrstamp; /* Time stamp for cached attributes */
+ struct sillyrename *n_sillyrename; /* Ptr to silly rename struct */
+ u_quad_t n_size; /* Current size of file */
+ int n_error; /* Save write error value */
+ u_long n_direofoffset; /* Dir. EOF offset cache */
+ time_t n_mtime; /* Prev modify time. */
+ time_t n_ctime; /* Prev create time. */
+ u_quad_t n_brev; /* Modify rev when cached */
+ u_quad_t n_lrev; /* Modify rev for lease */
+ time_t n_expiry; /* Lease expiry time */
+ struct nfsnode *n_tnext; /* Nqnfs timer chain */
+ struct nfsnode *n_tprev;
+ long spare1; /* To 8 byte boundary */
+ struct sillyrename n_silly; /* Silly rename struct */
+ struct timeval n_atim; /* Special file times */
+ struct timeval n_mtim;
+};
+
+/*
+ * Flags for n_flag
+ */
+#define NFLUSHWANT 0x0001 /* Want wakeup from a flush in prog. */
+#define NFLUSHINPROG 0x0002 /* Avoid multiple calls to vinvalbuf() */
+#define NMODIFIED 0x0004 /* Might have a modified buffer in bio */
+#define NWRITEERR 0x0008 /* Flag write errors so close will know */
+#define NQNFSNONCACHE 0x0020 /* Non-cachable lease */
+#define NQNFSWRITE 0x0040 /* Write lease */
+#define NQNFSEVICTED 0x0080 /* Has been evicted */
+#define NACC 0x0100 /* Special file accessed */
+#define NUPD 0x0200 /* Special file updated */
+#define NCHG 0x0400 /* Special file times changed */
+
+/*
+ * Convert between nfsnode pointers and vnode pointers
+ */
+#define VTONFS(vp) ((struct nfsnode *)(vp)->v_data)
+#define NFSTOV(np) ((struct vnode *)(np)->n_vnode)
+
+/*
+ * Queue head for nfsiod's
+ */
+TAILQ_HEAD(nfsbufs, buf) nfs_bufq;
+
+#ifdef KERNEL
+/*
+ * Prototypes for NFS vnode operations
+ */
+int nfs_lookup __P((struct vop_lookup_args *));
+int nfs_create __P((struct vop_create_args *));
+int nfs_mknod __P((struct vop_mknod_args *));
+int nfs_open __P((struct vop_open_args *));
+int nfs_close __P((struct vop_close_args *));
+int nfsspec_close __P((struct vop_close_args *));
+#ifdef FIFO
+int nfsfifo_close __P((struct vop_close_args *));
+#endif
+int nfs_access __P((struct vop_access_args *));
+int nfsspec_access __P((struct vop_access_args *));
+int nfs_getattr __P((struct vop_getattr_args *));
+int nfs_setattr __P((struct vop_setattr_args *));
+int nfs_read __P((struct vop_read_args *));
+int nfs_write __P((struct vop_write_args *));
+int nfsspec_read __P((struct vop_read_args *));
+int nfsspec_write __P((struct vop_write_args *));
+#ifdef FIFO
+int nfsfifo_read __P((struct vop_read_args *));
+int nfsfifo_write __P((struct vop_write_args *));
+#endif
+#define nfs_ioctl ((int (*) __P((struct vop_ioctl_args *)))enoioctl)
+#define nfs_select ((int (*) __P((struct vop_select_args *)))seltrue)
+int nfs_mmap __P((struct vop_mmap_args *));
+int nfs_fsync __P((struct vop_fsync_args *));
+#define nfs_seek ((int (*) __P((struct vop_seek_args *)))nullop)
+int nfs_remove __P((struct vop_remove_args *));
+int nfs_link __P((struct vop_link_args *));
+int nfs_rename __P((struct vop_rename_args *));
+int nfs_mkdir __P((struct vop_mkdir_args *));
+int nfs_rmdir __P((struct vop_rmdir_args *));
+int nfs_symlink __P((struct vop_symlink_args *));
+int nfs_readdir __P((struct vop_readdir_args *));
+int nfs_readlink __P((struct vop_readlink_args *));
+int nfs_abortop __P((struct vop_abortop_args *));
+int nfs_inactive __P((struct vop_inactive_args *));
+int nfs_reclaim __P((struct vop_reclaim_args *));
+int nfs_lock __P((struct vop_lock_args *));
+int nfs_unlock __P((struct vop_unlock_args *));
+int nfs_bmap __P((struct vop_bmap_args *));
+int nfs_strategy __P((struct vop_strategy_args *));
+int nfs_print __P((struct vop_print_args *));
+int nfs_islocked __P((struct vop_islocked_args *));
+int nfs_pathconf __P((struct vop_pathconf_args *));
+int nfs_advlock __P((struct vop_advlock_args *));
+int nfs_blkatoff __P((struct vop_blkatoff_args *));
+int nfs_vget __P((struct mount *, ino_t, struct vnode **));
+int nfs_valloc __P((struct vop_valloc_args *));
+#define nfs_reallocblks \
+ ((int (*) __P((struct vop_reallocblks_args *)))eopnotsupp)
+int nfs_vfree __P((struct vop_vfree_args *));
+int nfs_truncate __P((struct vop_truncate_args *));
+int nfs_update __P((struct vop_update_args *));
+int nfs_bwrite __P((struct vop_bwrite_args *));
+#endif /* KERNEL */
diff --git a/sys/nfs/nfsrtt.h b/sys/nfs/nfsrtt.h
new file mode 100644
index 0000000..0d23880
--- /dev/null
+++ b/sys/nfs/nfsrtt.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsrtt.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Definitions for performance monitor.
+ * The client and server logging are turned on by setting the global
+ * constant "nfsrtton" to 1.
+ */
+#define NFSRTTLOGSIZ 128
+
+/*
+ * Circular log of client side rpc activity. Each log entry is for one
+ * rpc filled in upon completion. (ie. in order of completion)
+ * The "pos" is the table index for the "next" entry, therefore the
+ * list goes from nfsrtt.rttl[pos] --> nfsrtt.rttl[pos - 1] in
+ * chronological order of completion.
+ */
+struct nfsrtt {
+ int pos; /* Position in array for next entry */
+ struct rttl {
+ int proc; /* NFS procedure number */
+ int rtt; /* Measured round trip time */
+ int rto; /* Round Trip Timeout */
+ int sent; /* # rpcs in progress */
+ int cwnd; /* Send window */
+ int srtt; /* Ave Round Trip Time */
+ int sdrtt; /* Ave mean deviation of RTT */
+ fsid_t fsid; /* Fsid for mount point */
+ struct timeval tstamp; /* Timestamp of log entry */
+ } rttl[NFSRTTLOGSIZ];
+};
+
+/*
+ * And definitions for server side performance monitor.
+ * The log organization is the same as above except it is filled in at the
+ * time the server sends the rpc reply.
+ */
+
+/*
+ * Bits for the flags field.
+ */
+#define DRT_NQNFS 0x01 /* Rpc used Nqnfs protocol */
+#define DRT_TCP 0x02 /* Client used TCP transport */
+#define DRT_CACHEREPLY 0x04 /* Reply was from recent request cache */
+#define DRT_CACHEDROP 0x08 /* Rpc request dropped, due to recent reply */
+
+/*
+ * Server log structure
+ * NB: ipadr == INADDR_ANY indicates a client using a non IP protocol.
+ * (ISO perhaps?)
+ */
+struct nfsdrt {
+ int pos; /* Position of next log entry */
+ struct drt {
+ int flag; /* Bits as defined above */
+ int proc; /* NFS procedure number */
+ u_long ipadr; /* IP address of client */
+ int resptime; /* Response time (usec) */
+ struct timeval tstamp; /* Timestamp of log entry */
+ } drt[NFSRTTLOGSIZ];
+};
diff --git a/sys/nfs/nfsrvcache.h b/sys/nfs/nfsrvcache.h
new file mode 100644
index 0000000..26da2c2
--- /dev/null
+++ b/sys/nfs/nfsrvcache.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsrvcache.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Definitions for the server recent request cache
+ */
+
+#define NFSRVCACHESIZ 256
+
+struct nfsrvcache {
+ struct nfsrvcache *rc_forw; /* Hash chain links */
+ struct nfsrvcache **rc_back; /* Hash chain links */
+ struct nfsrvcache *rc_next; /* Lru list */
+ struct nfsrvcache **rc_prev; /* Lru list */
+ u_long rc_xid; /* rpc id number */
+ union {
+ struct mbuf *ru_repmb; /* Reply mbuf list OR */
+ int ru_repstat; /* Reply status */
+ } rc_un;
+ union nethostaddr rc_haddr; /* Host address */
+ short rc_proc; /* rpc proc number */
+ u_char rc_state; /* Current state of request */
+ u_char rc_flag; /* Flag bits */
+};
+
+#define rc_reply rc_un.ru_repmb
+#define rc_status rc_un.ru_repstat
+#define rc_inetaddr rc_haddr.had_inetaddr
+#define rc_nam rc_haddr.had_nam
+
+/* Cache entry states */
+#define RC_UNUSED 0
+#define RC_INPROG 1
+#define RC_DONE 2
+
+/* Return values */
+#define RC_DROPIT 0
+#define RC_REPLY 1
+#define RC_DOIT 2
+#define RC_CHECKIT 3
+
+/* Flag bits */
+#define RC_LOCKED 0x01
+#define RC_WANTED 0x02
+#define RC_REPSTATUS 0x04
+#define RC_REPMBUF 0x08
+#define RC_NQNFS 0x10
+#define RC_INETADDR 0x20
+#define RC_NAM 0x40
diff --git a/sys/nfs/nfsv2.h b/sys/nfs/nfsv2.h
new file mode 100644
index 0000000..e9d2985
--- /dev/null
+++ b/sys/nfs/nfsv2.h
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsv2.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * nfs definitions as per the version 2 specs
+ */
+
+/*
+ * Constants as defined in the Sun NFS Version 2 spec.
+ * "NFS: Network File System Protocol Specification" RFC1094
+ */
+
+#define NFS_PORT 2049
+#define NFS_PROG 100003
+#define NFS_VER2 2
+#define NFS_MAXDGRAMDATA 8192
+#define NFS_MAXDATA 32768
+#define NFS_MAXPATHLEN 1024
+#define NFS_MAXNAMLEN 255
+#define NFS_FHSIZE 32
+#define NFS_MAXPKTHDR 404
+#define NFS_MAXPACKET (NFS_MAXPKTHDR+NFS_MAXDATA)
+#define NFS_MINPACKET 20
+#define NFS_FABLKSIZE 512 /* Size in bytes of a block wrt fa_blocks */
+
+/* Stat numbers for rpc returns */
+#define NFS_OK 0
+#define NFSERR_PERM 1
+#define NFSERR_NOENT 2
+#define NFSERR_IO 5
+#define NFSERR_NXIO 6
+#define NFSERR_ACCES 13
+#define NFSERR_EXIST 17
+#define NFSERR_NODEV 19
+#define NFSERR_NOTDIR 20
+#define NFSERR_ISDIR 21
+#define NFSERR_FBIG 27
+#define NFSERR_NOSPC 28
+#define NFSERR_ROFS 30
+#define NFSERR_NAMETOL 63
+#define NFSERR_NOTEMPTY 66
+#define NFSERR_DQUOT 69
+#define NFSERR_STALE 70
+#define NFSERR_WFLUSH 99
+
+/* Sizes in bytes of various nfs rpc components */
+#define NFSX_FH 32
+#define NFSX_UNSIGNED 4
+#define NFSX_NFSFATTR 68
+#define NFSX_NQFATTR 92
+#define NFSX_NFSSATTR 32
+#define NFSX_NQSATTR 44
+#define NFSX_COOKIE 4
+#define NFSX_NFSSTATFS 20
+#define NFSX_NQSTATFS 28
+#define NFSX_FATTR(isnq) ((isnq) ? NFSX_NQFATTR : NFSX_NFSFATTR)
+#define NFSX_SATTR(isnq) ((isnq) ? NFSX_NQSATTR : NFSX_NFSSATTR)
+#define NFSX_STATFS(isnq) ((isnq) ? NFSX_NQSTATFS : NFSX_NFSSTATFS)
+
+/* nfs rpc procedure numbers */
+#define NFSPROC_NULL 0
+#define NFSPROC_GETATTR 1
+#define NFSPROC_SETATTR 2
+#define NFSPROC_NOOP 3
+#define NFSPROC_ROOT NFSPROC_NOOP /* Obsolete */
+#define NFSPROC_LOOKUP 4
+#define NFSPROC_READLINK 5
+#define NFSPROC_READ 6
+#define NFSPROC_WRITECACHE NFSPROC_NOOP /* Obsolete */
+#define NFSPROC_WRITE 8
+#define NFSPROC_CREATE 9
+#define NFSPROC_REMOVE 10
+#define NFSPROC_RENAME 11
+#define NFSPROC_LINK 12
+#define NFSPROC_SYMLINK 13
+#define NFSPROC_MKDIR 14
+#define NFSPROC_RMDIR 15
+#define NFSPROC_READDIR 16
+#define NFSPROC_STATFS 17
+
+/* NQ nfs numbers */
+#define NQNFSPROC_READDIRLOOK 18
+#define NQNFSPROC_GETLEASE 19
+#define NQNFSPROC_VACATED 20
+#define NQNFSPROC_EVICTED 21
+#define NQNFSPROC_ACCESS 22
+
+#define NFS_NPROCS 23
+/* Conversion macros */
+extern int vttoif_tab[];
+#define vtonfs_mode(t,m) \
+ txdr_unsigned(((t) == VFIFO) ? MAKEIMODE(VCHR, (m)) : \
+ MAKEIMODE((t), (m)))
+#define nfstov_mode(a) (fxdr_unsigned(u_short, (a))&07777)
+#define vtonfs_type(a) txdr_unsigned(nfs_type[((long)(a))])
+#define nfstov_type(a) ntov_type[fxdr_unsigned(u_long,(a))&0x7]
+
+/* File types */
+typedef enum { NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 } nfstype;
+
+/* Structs for common parts of the rpc's */
+struct nfsv2_time {
+ u_long nfs_sec;
+ u_long nfs_usec;
+};
+
+struct nqnfs_time {
+ u_long nq_sec;
+ u_long nq_nsec;
+};
+
+/*
+ * File attributes and setable attributes. These structures cover both
+ * NFS version 2 and the NQNFS protocol. Note that the union is only
+ * used to that one pointer can refer to both variants. These structures
+ * go out on the wire and must be densely packed, so no quad data types
+ * are used. (all fields are longs or u_longs or structures of same)
+ * NB: You can't do sizeof(struct nfsv2_fattr), you must use the
+ * NFSX_FATTR(isnq) macro.
+ */
+struct nfsv2_fattr {
+ u_long fa_type;
+ u_long fa_mode;
+ u_long fa_nlink;
+ u_long fa_uid;
+ u_long fa_gid;
+ union {
+ struct {
+ u_long nfsfa_size;
+ u_long nfsfa_blocksize;
+ u_long nfsfa_rdev;
+ u_long nfsfa_blocks;
+ u_long nfsfa_fsid;
+ u_long nfsfa_fileid;
+ struct nfsv2_time nfsfa_atime;
+ struct nfsv2_time nfsfa_mtime;
+ struct nfsv2_time nfsfa_ctime;
+ } fa_nfsv2;
+ struct {
+ struct {
+ u_long nqfa_qsize[2];
+ } nqfa_size;
+ u_long nqfa_blocksize;
+ u_long nqfa_rdev;
+ struct {
+ u_long nqfa_qbytes[2];
+ } nqfa_bytes;
+ u_long nqfa_fsid;
+ u_long nqfa_fileid;
+ struct nqnfs_time nqfa_atime;
+ struct nqnfs_time nqfa_mtime;
+ struct nqnfs_time nqfa_ctime;
+ u_long nqfa_flags;
+ u_long nqfa_gen;
+ struct {
+ u_long nqfa_qfilerev[2];
+ } nqfa_filerev;
+ } fa_nqnfs;
+ } fa_un;
+};
+
+/* and some ugly defines for accessing union components */
+#define fa_nfssize fa_un.fa_nfsv2.nfsfa_size
+#define fa_nfsblocksize fa_un.fa_nfsv2.nfsfa_blocksize
+#define fa_nfsrdev fa_un.fa_nfsv2.nfsfa_rdev
+#define fa_nfsblocks fa_un.fa_nfsv2.nfsfa_blocks
+#define fa_nfsfsid fa_un.fa_nfsv2.nfsfa_fsid
+#define fa_nfsfileid fa_un.fa_nfsv2.nfsfa_fileid
+#define fa_nfsatime fa_un.fa_nfsv2.nfsfa_atime
+#define fa_nfsmtime fa_un.fa_nfsv2.nfsfa_mtime
+#define fa_nfsctime fa_un.fa_nfsv2.nfsfa_ctime
+#define fa_nqsize fa_un.fa_nqnfs.nqfa_size
+#define fa_nqblocksize fa_un.fa_nqnfs.nqfa_blocksize
+#define fa_nqrdev fa_un.fa_nqnfs.nqfa_rdev
+#define fa_nqbytes fa_un.fa_nqnfs.nqfa_bytes
+#define fa_nqfsid fa_un.fa_nqnfs.nqfa_fsid
+#define fa_nqfileid fa_un.fa_nqnfs.nqfa_fileid
+#define fa_nqatime fa_un.fa_nqnfs.nqfa_atime
+#define fa_nqmtime fa_un.fa_nqnfs.nqfa_mtime
+#define fa_nqctime fa_un.fa_nqnfs.nqfa_ctime
+#define fa_nqflags fa_un.fa_nqnfs.nqfa_flags
+#define fa_nqgen fa_un.fa_nqnfs.nqfa_gen
+#define fa_nqfilerev fa_un.fa_nqnfs.nqfa_filerev
+
+struct nfsv2_sattr {
+ u_long sa_mode;
+ u_long sa_uid;
+ u_long sa_gid;
+ union {
+ struct {
+ u_long nfssa_size;
+ struct nfsv2_time nfssa_atime;
+ struct nfsv2_time nfssa_mtime;
+ } sa_nfsv2;
+ struct {
+ struct {
+ u_long nqsa_qsize[2];
+ } nqsa_size;
+ struct nqnfs_time nqsa_atime;
+ struct nqnfs_time nqsa_mtime;
+ u_long nqsa_flags;
+ u_long nqsa_rdev;
+ } sa_nqnfs;
+ } sa_un;
+};
+
+/* and some ugly defines for accessing the unions */
+#define sa_nfssize sa_un.sa_nfsv2.nfssa_size
+#define sa_nfsatime sa_un.sa_nfsv2.nfssa_atime
+#define sa_nfsmtime sa_un.sa_nfsv2.nfssa_mtime
+#define sa_nqsize sa_un.sa_nqnfs.nqsa_size
+#define sa_nqatime sa_un.sa_nqnfs.nqsa_atime
+#define sa_nqmtime sa_un.sa_nqnfs.nqsa_mtime
+#define sa_nqflags sa_un.sa_nqnfs.nqsa_flags
+#define sa_nqrdev sa_un.sa_nqnfs.nqsa_rdev
+
+struct nfsv2_statfs {
+ u_long sf_tsize;
+ u_long sf_bsize;
+ u_long sf_blocks;
+ u_long sf_bfree;
+ u_long sf_bavail;
+ u_long sf_files; /* Nqnfs only */
+ u_long sf_ffree; /* ditto */
+};
diff --git a/sys/nfs/nqnfs.h b/sys/nfs/nqnfs.h
new file mode 100644
index 0000000..730741a
--- /dev/null
+++ b/sys/nfs/nqnfs.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nqnfs.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Definitions for NQNFS (Not Quite NFS) cache consistency protocol.
+ */
+
+/* Tunable constants */
+#define NQ_CLOCKSKEW 3 /* Clock skew factor (sec) */
+#define NQ_WRITESLACK 5 /* Delay for write cache flushing */
+#define NQ_MAXLEASE 60 /* Max lease duration (sec) */
+#define NQ_MINLEASE 5 /* Min lease duration (sec) */
+#define NQ_DEFLEASE 30 /* Default lease duration (sec) */
+#define NQ_RENEWAL 3 /* Time before expiry (sec) to renew */
+#define NQ_TRYLATERDEL 15 /* Initial try later delay (sec) */
+#define NQ_MAXNUMLEASE 2048 /* Upper bound on number of server leases */
+#define NQ_DEADTHRESH NQ_NEVERDEAD /* Default nm_deadthresh */
+#define NQ_NEVERDEAD 9 /* Greater than max. nm_timeouts */
+#define NQLCHSZ 256 /* Server hash table size */
+
+#define NQNFS_PROG 300105 /* As assigned by Sun */
+#define NQNFS_VER1 1
+#define NQNFS_EVICTSIZ 156 /* Size of eviction request in bytes */
+
+/*
+ * Definitions used for saving the "last lease expires" time in Non-volatile
+ * RAM on the server. The default definitions below assume that NOVRAM is not
+ * available.
+ */
+#define NQSTORENOVRAM(t)
+#define NQLOADNOVRAM(t)
+
+/*
+ * Defn and structs used on the server to maintain state for current leases.
+ * The list of host(s) that hold the lease are kept as nqhost structures.
+ * The first one lives in nqlease and any others are held in a linked
+ * list of nqm structures hanging off of nqlease.
+ *
+ * Each nqlease structure is chained into two lists. The first is a list
+ * ordered by increasing expiry time for nqsrv_timer() and the second is a chain
+ * hashed on lc_fh.
+ */
+#define LC_MOREHOSTSIZ 10
+
+struct nqhost {
+ union {
+ struct {
+ u_short udp_flag;
+ u_short udp_port;
+ union nethostaddr udp_haddr;
+ } un_udp;
+ struct {
+ u_short connless_flag;
+ u_short connless_spare;
+ union nethostaddr connless_haddr;
+ } un_connless;
+ struct {
+ u_short conn_flag;
+ u_short conn_spare;
+ struct nfssvc_sock *conn_slp;
+ } un_conn;
+ } lph_un;
+};
+#define lph_flag lph_un.un_udp.udp_flag
+#define lph_port lph_un.un_udp.udp_port
+#define lph_haddr lph_un.un_udp.udp_haddr
+#define lph_inetaddr lph_un.un_udp.udp_haddr.had_inetaddr
+#define lph_claddr lph_un.un_connless.connless_haddr
+#define lph_nam lph_un.un_connless.connless_haddr.had_nam
+#define lph_slp lph_un.un_conn.conn_slp
+
+struct nqlease {
+ struct nqlease *lc_chain1[2]; /* Timer queue list (must be first) */
+ struct nqlease *lc_fhnext; /* Fhandle hash list */
+ struct nqlease **lc_fhprev;
+ time_t lc_expiry; /* Expiry time (sec) */
+ struct nqhost lc_host; /* Host that got lease */
+ struct nqm *lc_morehosts; /* Other hosts that share read lease */
+ fsid_t lc_fsid; /* Fhandle */
+ char lc_fiddata[MAXFIDSZ];
+ struct vnode *lc_vp; /* Soft reference to associated vnode */
+};
+#define lc_flag lc_host.lph_un.un_udp.udp_flag
+
+/* lc_flag bits */
+#define LC_VALID 0x0001 /* Host address valid */
+#define LC_WRITE 0x0002 /* Write cache */
+#define LC_NONCACHABLE 0x0004 /* Non-cachable lease */
+#define LC_LOCKED 0x0008 /* Locked */
+#define LC_WANTED 0x0010 /* Lock wanted */
+#define LC_EXPIREDWANTED 0x0020 /* Want lease when expired */
+#define LC_UDP 0x0040 /* Host address for udp socket */
+#define LC_CLTP 0x0080 /* Host address for other connectionless */
+#define LC_LOCAL 0x0100 /* Host is server */
+#define LC_VACATED 0x0200 /* Host has vacated lease */
+#define LC_WRITTEN 0x0400 /* Recently wrote to the leased file */
+#define LC_SREF 0x0800 /* Holds a nfssvc_sock reference */
+
+struct nqm {
+ struct nqm *lpm_next;
+ struct nqhost lpm_hosts[LC_MOREHOSTSIZ];
+};
+
+/*
+ * Flag bits for flags argument to nqsrv_getlease.
+ */
+#define NQL_READ LEASE_READ /* Read Request */
+#define NQL_WRITE LEASE_WRITE /* Write Request */
+#define NQL_CHECK 0x4 /* Check for lease */
+#define NQL_NOVAL 0xffffffff /* Invalid */
+
+/*
+ * Special value for slp for local server calls.
+ */
+#define NQLOCALSLP ((struct nfssvc_sock *) -1)
+
+/*
+ * Server side macros.
+ */
+#define nqsrv_getl(v, l) \
+ (void) nqsrv_getlease((v), &nfsd->nd_duration, \
+ ((nfsd->nd_nqlflag != 0 && nfsd->nd_nqlflag != NQL_NOVAL) ? nfsd->nd_nqlflag : \
+ ((l) | NQL_CHECK)), \
+ nfsd, nam, &cache, &frev, cred)
+
+/*
+ * Client side macros that check for a valid lease.
+ */
+#define NQNFS_CKINVALID(v, n, f) \
+ ((time.tv_sec > (n)->n_expiry && \
+ VFSTONFS((v)->v_mount)->nm_timeouts < VFSTONFS((v)->v_mount)->nm_deadthresh) \
+ || ((f) == NQL_WRITE && ((n)->n_flag & NQNFSWRITE) == 0))
+
+#define NQNFS_CKCACHABLE(v, f) \
+ ((time.tv_sec <= VTONFS(v)->n_expiry || \
+ VFSTONFS((v)->v_mount)->nm_timeouts >= VFSTONFS((v)->v_mount)->nm_deadthresh) \
+ && (VTONFS(v)->n_flag & NQNFSNONCACHE) == 0 && \
+ ((f) == NQL_READ || (VTONFS(v)->n_flag & NQNFSWRITE)))
+
+#define NQNFS_NEEDLEASE(v, p) \
+ (time.tv_sec > VTONFS(v)->n_expiry ? \
+ ((VTONFS(v)->n_flag & NQNFSEVICTED) ? 0 : nqnfs_piggy[p]) : \
+ (((time.tv_sec + NQ_RENEWAL) > VTONFS(v)->n_expiry && \
+ nqnfs_piggy[p]) ? \
+ ((VTONFS(v)->n_flag & NQNFSWRITE) ? \
+ NQL_WRITE : nqnfs_piggy[p]) : 0))
+
+/*
+ * List head for timer queue.
+ */
+extern union nqsrvthead {
+ union nqsrvthead *th_head[2];
+ struct nqlease *th_chain[2];
+} nqthead;
+extern struct nqlease **nqfhead;
+extern u_long nqfheadhash;
+
+/*
+ * Nqnfs return status numbers.
+ */
+#define NQNFS_EXPIRED 500
+#define NQNFS_TRYLATER 501
+#define NQNFS_AUTHERR 502
diff --git a/sys/nfs/rpcv2.h b/sys/nfs/rpcv2.h
new file mode 100644
index 0000000..9c793a7
--- /dev/null
+++ b/sys/nfs/rpcv2.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)rpcv2.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Definitions for Sun RPC Version 2, from
+ * "RPC: Remote Procedure Call Protocol Specification" RFC1057
+ */
+
+/* Version # */
+#define RPC_VER2 2
+
+/* Authentication */
+#define RPCAUTH_NULL 0
+#define RPCAUTH_UNIX 1
+#define RPCAUTH_SHORT 2
+#define RPCAUTH_NQNFS 300000
+#define RPCAUTH_MAXSIZ 400
+#define RPCAUTH_UNIXGIDS 16
+
+/* Rpc Constants */
+#define RPC_CALL 0
+#define RPC_REPLY 1
+#define RPC_MSGACCEPTED 0
+#define RPC_MSGDENIED 1
+#define RPC_PROGUNAVAIL 1
+#define RPC_PROGMISMATCH 2
+#define RPC_PROCUNAVAIL 3
+#define RPC_GARBAGE 4 /* I like this one */
+#define RPC_MISMATCH 0
+#define RPC_AUTHERR 1
+
+/* Authentication failures */
+#define AUTH_BADCRED 1
+#define AUTH_REJECTCRED 2
+#define AUTH_BADVERF 3
+#define AUTH_REJECTVERF 4
+#define AUTH_TOOWEAK 5 /* Give em wheaties */
+
+/* Sizes of rpc header parts */
+#define RPC_SIZ 24
+#define RPC_REPLYSIZ 28
+
+/* RPC Prog definitions */
+#define RPCPROG_MNT 100005
+#define RPCMNT_VER1 1
+#define RPCMNT_MOUNT 1
+#define RPCMNT_DUMP 2
+#define RPCMNT_UMOUNT 3
+#define RPCMNT_UMNTALL 4
+#define RPCMNT_EXPORT 5
+#define RPCMNT_NAMELEN 255
+#define RPCMNT_PATHLEN 1024
+#define RPCPROG_NFS 100003
diff --git a/sys/nfs/xdr_subs.h b/sys/nfs/xdr_subs.h
new file mode 100644
index 0000000..c2aa4f3
--- /dev/null
+++ b/sys/nfs/xdr_subs.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)xdr_subs.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Macros used for conversion to/from xdr representation by nfs...
+ * These use the MACHINE DEPENDENT routines ntohl, htonl
+ * As defined by "XDR: External Data Representation Standard" RFC1014
+ *
+ * To simplify the implementation, we use ntohl/htonl even on big-endian
+ * machines, and count on them being `#define'd away. Some of these
+ * might be slightly more efficient as quad_t copies on a big-endian,
+ * but we cannot count on their alignment anyway.
+ */
+
+#define fxdr_unsigned(t, v) ((t)ntohl((long)(v)))
+#define txdr_unsigned(v) (htonl((long)(v)))
+
+#define fxdr_nfstime(f, t) { \
+ (t)->ts_sec = ntohl(((struct nfsv2_time *)(f))->nfs_sec); \
+ (t)->ts_nsec = 1000 * ntohl(((struct nfsv2_time *)(f))->nfs_usec); \
+}
+#define txdr_nfstime(f, t) { \
+ ((struct nfsv2_time *)(t))->nfs_sec = htonl((f)->ts_sec); \
+ ((struct nfsv2_time *)(t))->nfs_usec = htonl((f)->ts_nsec) / 1000; \
+}
+
+#define fxdr_nqtime(f, t) { \
+ (t)->ts_sec = ntohl(((struct nqnfs_time *)(f))->nq_sec); \
+ (t)->ts_nsec = ntohl(((struct nqnfs_time *)(f))->nq_nsec); \
+}
+#define txdr_nqtime(f, t) { \
+ ((struct nqnfs_time *)(t))->nq_sec = htonl((f)->ts_sec); \
+ ((struct nqnfs_time *)(t))->nq_nsec = htonl((f)->ts_nsec); \
+}
+
+#define fxdr_hyper(f, t) { \
+ ((long *)(t))[_QUAD_HIGHWORD] = ntohl(((long *)(f))[0]); \
+ ((long *)(t))[_QUAD_LOWWORD] = ntohl(((long *)(f))[1]); \
+}
+#define txdr_hyper(f, t) { \
+ ((long *)(t))[0] = htonl(((long *)(f))[_QUAD_HIGHWORD]); \
+ ((long *)(t))[1] = htonl(((long *)(f))[_QUAD_LOWWORD]); \
+}
diff --git a/sys/nfsclient/nfs.h b/sys/nfsclient/nfs.h
new file mode 100644
index 0000000..261fd42
--- /dev/null
+++ b/sys/nfsclient/nfs.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Tunable constants for nfs
+ */
+
+#define NFS_MAXIOVEC 34
+#define NFS_HZ 25 /* Ticks per second for NFS timeouts */
+#define NFS_TIMEO (1*NFS_HZ) /* Default timeout = 1 second */
+#define NFS_MINTIMEO (1*NFS_HZ) /* Min timeout to use */
+#define NFS_MAXTIMEO (60*NFS_HZ) /* Max timeout to backoff to */
+#define NFS_MINIDEMTIMEO (5*NFS_HZ) /* Min timeout for non-idempotent ops*/
+#define NFS_MAXREXMIT 100 /* Stop counting after this many */
+#define NFS_MAXWINDOW 1024 /* Max number of outstanding requests */
+#define NFS_RETRANS 10 /* Num of retrans for soft mounts */
+#define NFS_MAXGRPS 16 /* Max. size of groups list */
+#define NFS_MINATTRTIMO 5 /* Attribute cache timeout in sec */
+#define NFS_MAXATTRTIMO 60
+#define NFS_WSIZE 8192 /* Def. write data size <= 8192 */
+#define NFS_RSIZE 8192 /* Def. read data size <= 8192 */
+#define NFS_DEFRAHEAD 1 /* Def. read ahead # blocks */
+#define NFS_MAXRAHEAD 4 /* Max. read ahead # blocks */
+#define NFS_MAXREADDIR NFS_MAXDATA /* Max. size of directory read */
+#define NFS_MAXUIDHASH 64 /* Max. # of hashed uid entries/mp */
+#define NFS_MAXASYNCDAEMON 20 /* Max. number async_daemons runable */
+#define NFS_DIRBLKSIZ 1024 /* Size of an NFS directory block */
+#define NMOD(a) ((a) % nfs_asyncdaemons)
+
+/*
+ * Set the attribute timeout based on how recently the file has been modified.
+ */
+#define NFS_ATTRTIMEO(np) \
+ ((((np)->n_flag & NMODIFIED) || \
+ (time.tv_sec - (np)->n_mtime) / 10 < NFS_MINATTRTIMO) ? NFS_MINATTRTIMO : \
+ ((time.tv_sec - (np)->n_mtime) / 10 > NFS_MAXATTRTIMO ? NFS_MAXATTRTIMO : \
+ (time.tv_sec - (np)->n_mtime) / 10))
+
+/*
+ * Structures for the nfssvc(2) syscall. Not that anyone but nfsd and mount_nfs
+ * should ever try and use it.
+ */
+struct nfsd_args {
+ int sock; /* Socket to serve */
+ caddr_t name; /* Client address for connection based sockets */
+ int namelen; /* Length of name */
+};
+
+struct nfsd_srvargs {
+ struct nfsd *nsd_nfsd; /* Pointer to in kernel nfsd struct */
+ uid_t nsd_uid; /* Effective uid mapped to cred */
+ u_long nsd_haddr; /* Ip address of client */
+ struct ucred nsd_cr; /* Cred. uid maps to */
+ int nsd_authlen; /* Length of auth string (ret) */
+ char *nsd_authstr; /* Auth string (ret) */
+};
+
+struct nfsd_cargs {
+ char *ncd_dirp; /* Mount dir path */
+ uid_t ncd_authuid; /* Effective uid */
+ int ncd_authtype; /* Type of authenticator */
+ int ncd_authlen; /* Length of authenticator string */
+ char *ncd_authstr; /* Authenticator string */
+};
+
+/*
+ * Stats structure
+ */
+struct nfsstats {
+ int attrcache_hits;
+ int attrcache_misses;
+ int lookupcache_hits;
+ int lookupcache_misses;
+ int direofcache_hits;
+ int direofcache_misses;
+ int biocache_reads;
+ int read_bios;
+ int read_physios;
+ int biocache_writes;
+ int write_bios;
+ int write_physios;
+ int biocache_readlinks;
+ int readlink_bios;
+ int biocache_readdirs;
+ int readdir_bios;
+ int rpccnt[NFS_NPROCS];
+ int rpcretries;
+ int srvrpccnt[NFS_NPROCS];
+ int srvrpc_errs;
+ int srv_errs;
+ int rpcrequests;
+ int rpctimeouts;
+ int rpcunexpected;
+ int rpcinvalid;
+ int srvcache_inproghits;
+ int srvcache_idemdonehits;
+ int srvcache_nonidemdonehits;
+ int srvcache_misses;
+ int srvnqnfs_leases;
+ int srvnqnfs_maxleases;
+ int srvnqnfs_getleases;
+};
+
+/*
+ * Flags for nfssvc() system call.
+ */
+#define NFSSVC_BIOD 0x002
+#define NFSSVC_NFSD 0x004
+#define NFSSVC_ADDSOCK 0x008
+#define NFSSVC_AUTHIN 0x010
+#define NFSSVC_GOTAUTH 0x040
+#define NFSSVC_AUTHINFAIL 0x080
+#define NFSSVC_MNTD 0x100
+
+/*
+ * The set of signals the interrupt an I/O in progress for NFSMNT_INT mounts.
+ * What should be in this set is open to debate, but I believe that since
+ * I/O system calls on ufs are never interrupted by signals the set should
+ * be minimal. My reasoning is that many current programs that use signals
+ * such as SIGALRM will not expect file I/O system calls to be interrupted
+ * by them and break.
+ */
+#ifdef KERNEL
+#define NFSINT_SIGMASK (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \
+ sigmask(SIGHUP)|sigmask(SIGQUIT))
+
+/*
+ * Socket errors ignored for connectionless sockets??
+ * For now, ignore them all
+ */
+#define NFSIGNORE_SOERROR(s, e) \
+ ((e) != EINTR && (e) != ERESTART && (e) != EWOULDBLOCK && \
+ ((s) & PR_CONNREQUIRED) == 0)
+
+/*
+ * Nfs outstanding request list element
+ */
+struct nfsreq {
+ struct nfsreq *r_next;
+ struct nfsreq *r_prev;
+ struct mbuf *r_mreq;
+ struct mbuf *r_mrep;
+ struct mbuf *r_md;
+ caddr_t r_dpos;
+ struct nfsmount *r_nmp;
+ struct vnode *r_vp;
+ u_long r_xid;
+ int r_flags; /* flags on request, see below */
+ int r_retry; /* max retransmission count */
+ int r_rexmit; /* current retrans count */
+ int r_timer; /* tick counter on reply */
+ int r_procnum; /* NFS procedure number */
+ int r_rtt; /* RTT for rpc */
+ struct proc *r_procp; /* Proc that did I/O system call */
+};
+
+/* Flag values for r_flags */
+#define R_TIMING 0x01 /* timing request (in mntp) */
+#define R_SENT 0x02 /* request has been sent */
+#define R_SOFTTERM 0x04 /* soft mnt, too many retries */
+#define R_INTR 0x08 /* intr mnt, signal pending */
+#define R_SOCKERR 0x10 /* Fatal error on socket */
+#define R_TPRINTFMSG 0x20 /* Did a tprintf msg. */
+#define R_MUSTRESEND 0x40 /* Must resend request */
+#define R_GETONEREP 0x80 /* Probe for one reply only */
+
+struct nfsstats nfsstats;
+
+/*
+ * A list of nfssvc_sock structures is maintained with all the sockets
+ * that require service by the nfsd.
+ * The nfsuid structs hang off of the nfssvc_sock structs in both lru
+ * and uid hash lists.
+ */
+#define NUIDHASHSIZ 32
+#define NUIDHASH(uid) ((uid) & (NUIDHASHSIZ - 1))
+
+/*
+ * Network address hash list element
+ */
+union nethostaddr {
+ u_long had_inetaddr;
+ struct mbuf *had_nam;
+};
+
+struct nfsuid {
+ struct nfsuid *nu_lrunext; /* MUST be first */
+ struct nfsuid *nu_lruprev;
+ struct nfsuid *nu_hnext;
+ struct nfsuid *nu_hprev;
+ int nu_flag; /* Flags */
+ uid_t nu_uid; /* Uid mapped by this entry */
+ union nethostaddr nu_haddr; /* Host addr. for dgram sockets */
+ struct ucred nu_cr; /* Cred uid mapped to */
+};
+
+#define nu_inetaddr nu_haddr.had_inetaddr
+#define nu_nam nu_haddr.had_nam
+/* Bits for nu_flag */
+#define NU_INETADDR 0x1
+
+struct nfssvc_sock {
+ struct nfsuid *ns_lrunext; /* MUST be first */
+ struct nfsuid *ns_lruprev;
+ struct nfssvc_sock *ns_next;
+ struct nfssvc_sock *ns_prev;
+ int ns_flag;
+ u_long ns_sref;
+ struct file *ns_fp;
+ struct socket *ns_so;
+ int ns_solock;
+ struct mbuf *ns_nam;
+ int ns_cc;
+ struct mbuf *ns_raw;
+ struct mbuf *ns_rawend;
+ int ns_reclen;
+ struct mbuf *ns_rec;
+ struct mbuf *ns_recend;
+ int ns_numuids;
+ struct nfsuid *ns_uidh[NUIDHASHSIZ];
+};
+
+/* Bits for "ns_flag" */
+#define SLP_VALID 0x01
+#define SLP_DOREC 0x02
+#define SLP_NEEDQ 0x04
+#define SLP_DISCONN 0x08
+#define SLP_GETSTREAM 0x10
+#define SLP_INIT 0x20
+#define SLP_WANTINIT 0x40
+
+#define SLP_ALLFLAGS 0xff
+
+/*
+ * One of these structures is allocated for each nfsd.
+ */
+struct nfsd {
+ struct nfsd *nd_next; /* Must be first */
+ struct nfsd *nd_prev;
+ int nd_flag; /* NFSD_ flags */
+ struct nfssvc_sock *nd_slp; /* Current socket */
+ struct mbuf *nd_nam; /* Client addr for datagram req. */
+ struct mbuf *nd_mrep; /* Req. mbuf list */
+ struct mbuf *nd_md;
+ caddr_t nd_dpos; /* Position in list */
+ int nd_procnum; /* RPC procedure number */
+ u_long nd_retxid; /* RPC xid */
+ int nd_repstat; /* Reply status value */
+ struct ucred nd_cr; /* Credentials for req. */
+ int nd_nqlflag; /* Leasing flag */
+ int nd_duration; /* Lease duration */
+ int nd_authlen; /* Authenticator len */
+ u_char nd_authstr[RPCAUTH_MAXSIZ]; /* Authenticator data */
+ struct proc *nd_procp; /* Proc ptr */
+};
+
+#define NFSD_WAITING 0x01
+#define NFSD_CHECKSLP 0x02
+#define NFSD_REQINPROG 0x04
+#define NFSD_NEEDAUTH 0x08
+#define NFSD_AUTHFAIL 0x10
+#endif /* KERNEL */
diff --git a/sys/nfsclient/nfs_bio.c b/sys/nfsclient/nfs_bio.c
new file mode 100644
index 0000000..9ef81fe
--- /dev/null
+++ b/sys/nfsclient/nfs_bio.c
@@ -0,0 +1,803 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_bio.c 8.5 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/resourcevar.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/trace.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+
+#include <vm/vm.h>
+
+#include <nfs/nfsnode.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nqnfs.h>
+
+struct buf *incore(), *nfs_getcacheblk();
+extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
+extern int nfs_numasync;
+
+/*
+ * Vnode op for read using bio
+ * Any similarity to readip() is purely coincidental
+ */
+int
+nfs_bioread(vp, uio, ioflag, cred)
+ register struct vnode *vp;
+ register struct uio *uio;
+ int ioflag;
+ struct ucred *cred;
+{
+ register struct nfsnode *np = VTONFS(vp);
+ register int biosize, diff;
+ struct buf *bp = 0, *rabp;
+ struct vattr vattr;
+ struct proc *p;
+ struct nfsmount *nmp;
+ daddr_t lbn, bn, rabn;
+ caddr_t baddr;
+ int got_buf = 0, nra, error = 0, n = 0, on = 0, not_readin;
+
+#ifdef lint
+ ioflag = ioflag;
+#endif /* lint */
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_READ)
+ panic("nfs_read mode");
+#endif
+ if (uio->uio_resid == 0)
+ return (0);
+ if (uio->uio_offset < 0 && vp->v_type != VDIR)
+ return (EINVAL);
+ nmp = VFSTONFS(vp->v_mount);
+ biosize = nmp->nm_rsize;
+ p = uio->uio_procp;
+ /*
+ * For nfs, cache consistency can only be maintained approximately.
+ * Although RFC1094 does not specify the criteria, the following is
+ * believed to be compatible with the reference port.
+ * For nqnfs, full cache consistency is maintained within the loop.
+ * For nfs:
+ * If the file's modify time on the server has changed since the
+ * last read rpc or you have written to the file,
+ * you may have lost data cache consistency with the
+ * server, so flush all of the file's data out of the cache.
+ * Then force a getattr rpc to ensure that you have up to date
+ * attributes.
+ * The mount flag NFSMNT_MYWRITE says "Assume that my writes are
+ * the ones changing the modify time.
+ * NB: This implies that cache data can be read when up to
+ * NFS_ATTRTIMEO seconds out of date. If you find that you need current
+ * attributes this could be forced by setting n_attrstamp to 0 before
+ * the VOP_GETATTR() call.
+ */
+ if ((nmp->nm_flag & NFSMNT_NQNFS) == 0 && vp->v_type != VLNK) {
+ if (np->n_flag & NMODIFIED) {
+ if ((nmp->nm_flag & NFSMNT_MYWRITE) == 0 ||
+ vp->v_type != VREG) {
+ if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+ return (error);
+ }
+ np->n_attrstamp = 0;
+ np->n_direofoffset = 0;
+ if (error = VOP_GETATTR(vp, &vattr, cred, p))
+ return (error);
+ np->n_mtime = vattr.va_mtime.ts_sec;
+ } else {
+ if (error = VOP_GETATTR(vp, &vattr, cred, p))
+ return (error);
+ if (np->n_mtime != vattr.va_mtime.ts_sec) {
+ np->n_direofoffset = 0;
+ if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+ return (error);
+ np->n_mtime = vattr.va_mtime.ts_sec;
+ }
+ }
+ }
+ do {
+
+ /*
+ * Get a valid lease. If cached data is stale, flush it.
+ */
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ if (NQNFS_CKINVALID(vp, np, NQL_READ)) {
+ do {
+ error = nqnfs_getlease(vp, NQL_READ, cred, p);
+ } while (error == NQNFS_EXPIRED);
+ if (error)
+ return (error);
+ if (np->n_lrev != np->n_brev ||
+ (np->n_flag & NQNFSNONCACHE) ||
+ ((np->n_flag & NMODIFIED) && vp->v_type == VDIR)) {
+ if (vp->v_type == VDIR) {
+ np->n_direofoffset = 0;
+ cache_purge(vp);
+ }
+ if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+ return (error);
+ np->n_brev = np->n_lrev;
+ }
+ } else if (vp->v_type == VDIR && (np->n_flag & NMODIFIED)) {
+ np->n_direofoffset = 0;
+ cache_purge(vp);
+ if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+ return (error);
+ }
+ }
+ if (np->n_flag & NQNFSNONCACHE) {
+ switch (vp->v_type) {
+ case VREG:
+ error = nfs_readrpc(vp, uio, cred);
+ break;
+ case VLNK:
+ error = nfs_readlinkrpc(vp, uio, cred);
+ break;
+ case VDIR:
+ error = nfs_readdirrpc(vp, uio, cred);
+ break;
+ };
+ return (error);
+ }
+ baddr = (caddr_t)0;
+ switch (vp->v_type) {
+ case VREG:
+ nfsstats.biocache_reads++;
+ lbn = uio->uio_offset / biosize;
+ on = uio->uio_offset & (biosize-1);
+ bn = lbn * (biosize / DEV_BSIZE);
+ not_readin = 1;
+
+ /*
+ * Start the read ahead(s), as required.
+ */
+ if (nfs_numasync > 0 && nmp->nm_readahead > 0 &&
+ lbn == vp->v_lastr + 1) {
+ for (nra = 0; nra < nmp->nm_readahead &&
+ (lbn + 1 + nra) * biosize < np->n_size; nra++) {
+ rabn = (lbn + 1 + nra) * (biosize / DEV_BSIZE);
+ if (!incore(vp, rabn)) {
+ rabp = nfs_getcacheblk(vp, rabn, biosize, p);
+ if (!rabp)
+ return (EINTR);
+ if ((rabp->b_flags & (B_DELWRI | B_DONE)) == 0) {
+ rabp->b_flags |= (B_READ | B_ASYNC);
+ if (nfs_asyncio(rabp, cred)) {
+ rabp->b_flags |= B_INVAL;
+ brelse(rabp);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * If the block is in the cache and has the required data
+ * in a valid region, just copy it out.
+ * Otherwise, get the block and write back/read in,
+ * as required.
+ */
+ if ((bp = incore(vp, bn)) &&
+ (bp->b_flags & (B_BUSY | B_WRITEINPROG)) ==
+ (B_BUSY | B_WRITEINPROG))
+ got_buf = 0;
+ else {
+again:
+ bp = nfs_getcacheblk(vp, bn, biosize, p);
+ if (!bp)
+ return (EINTR);
+ got_buf = 1;
+ if ((bp->b_flags & (B_DONE | B_DELWRI)) == 0) {
+ bp->b_flags |= B_READ;
+ not_readin = 0;
+ if (error = nfs_doio(bp, cred, p)) {
+ brelse(bp);
+ return (error);
+ }
+ }
+ }
+ n = min((unsigned)(biosize - on), uio->uio_resid);
+ diff = np->n_size - uio->uio_offset;
+ if (diff < n)
+ n = diff;
+ if (not_readin && n > 0) {
+ if (on < bp->b_validoff || (on + n) > bp->b_validend) {
+ if (!got_buf) {
+ bp = nfs_getcacheblk(vp, bn, biosize, p);
+ if (!bp)
+ return (EINTR);
+ got_buf = 1;
+ }
+ bp->b_flags |= B_INVAL;
+ if (bp->b_dirtyend > 0) {
+ if ((bp->b_flags & B_DELWRI) == 0)
+ panic("nfsbioread");
+ if (VOP_BWRITE(bp) == EINTR)
+ return (EINTR);
+ } else
+ brelse(bp);
+ goto again;
+ }
+ }
+ vp->v_lastr = lbn;
+ diff = (on >= bp->b_validend) ? 0 : (bp->b_validend - on);
+ if (diff < n)
+ n = diff;
+ break;
+ case VLNK:
+ nfsstats.biocache_readlinks++;
+ bp = nfs_getcacheblk(vp, (daddr_t)0, NFS_MAXPATHLEN, p);
+ if (!bp)
+ return (EINTR);
+ if ((bp->b_flags & B_DONE) == 0) {
+ bp->b_flags |= B_READ;
+ if (error = nfs_doio(bp, cred, p)) {
+ brelse(bp);
+ return (error);
+ }
+ }
+ n = min(uio->uio_resid, NFS_MAXPATHLEN - bp->b_resid);
+ got_buf = 1;
+ on = 0;
+ break;
+ case VDIR:
+ nfsstats.biocache_readdirs++;
+ bn = (daddr_t)uio->uio_offset;
+ bp = nfs_getcacheblk(vp, bn, NFS_DIRBLKSIZ, p);
+ if (!bp)
+ return (EINTR);
+ if ((bp->b_flags & B_DONE) == 0) {
+ bp->b_flags |= B_READ;
+ if (error = nfs_doio(bp, cred, p)) {
+ brelse(bp);
+ return (error);
+ }
+ }
+
+ /*
+ * If not eof and read aheads are enabled, start one.
+ * (You need the current block first, so that you have the
+ * directory offset cookie of the next block.
+ */
+ rabn = bp->b_blkno;
+ if (nfs_numasync > 0 && nmp->nm_readahead > 0 &&
+ rabn != 0 && rabn != np->n_direofoffset &&
+ !incore(vp, rabn)) {
+ rabp = nfs_getcacheblk(vp, rabn, NFS_DIRBLKSIZ, p);
+ if (rabp) {
+ if ((rabp->b_flags & (B_DONE | B_DELWRI)) == 0) {
+ rabp->b_flags |= (B_READ | B_ASYNC);
+ if (nfs_asyncio(rabp, cred)) {
+ rabp->b_flags |= B_INVAL;
+ brelse(rabp);
+ }
+ }
+ }
+ }
+ on = 0;
+ n = min(uio->uio_resid, NFS_DIRBLKSIZ - bp->b_resid);
+ got_buf = 1;
+ break;
+ };
+
+ if (n > 0) {
+ if (!baddr)
+ baddr = bp->b_data;
+ error = uiomove(baddr + on, (int)n, uio);
+ }
+ switch (vp->v_type) {
+ case VREG:
+ if (n + on == biosize || uio->uio_offset == np->n_size)
+ bp->b_flags |= B_AGE;
+ break;
+ case VLNK:
+ n = 0;
+ break;
+ case VDIR:
+ uio->uio_offset = bp->b_blkno;
+ break;
+ };
+ if (got_buf)
+ brelse(bp);
+ } while (error == 0 && uio->uio_resid > 0 && n > 0);
+ return (error);
+}
+
+/*
+ * Vnode op for write using bio
+ */
+int
+nfs_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register int biosize;
+ register struct uio *uio = ap->a_uio;
+ struct proc *p = uio->uio_procp;
+ register struct vnode *vp = ap->a_vp;
+ struct nfsnode *np = VTONFS(vp);
+ register struct ucred *cred = ap->a_cred;
+ int ioflag = ap->a_ioflag;
+ struct buf *bp;
+ struct vattr vattr;
+ struct nfsmount *nmp;
+ daddr_t lbn, bn;
+ int n, on, error = 0;
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_WRITE)
+ panic("nfs_write mode");
+ if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
+ panic("nfs_write proc");
+#endif
+ if (vp->v_type != VREG)
+ return (EIO);
+ if (np->n_flag & NWRITEERR) {
+ np->n_flag &= ~NWRITEERR;
+ return (np->n_error);
+ }
+ if (ioflag & (IO_APPEND | IO_SYNC)) {
+ if (np->n_flag & NMODIFIED) {
+ np->n_attrstamp = 0;
+ if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+ return (error);
+ }
+ if (ioflag & IO_APPEND) {
+ np->n_attrstamp = 0;
+ if (error = VOP_GETATTR(vp, &vattr, cred, p))
+ return (error);
+ uio->uio_offset = np->n_size;
+ }
+ }
+ nmp = VFSTONFS(vp->v_mount);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+ if (uio->uio_resid == 0)
+ return (0);
+ /*
+ * Maybe this should be above the vnode op call, but so long as
+ * file servers have no limits, i don't think it matters
+ */
+ if (p && uio->uio_offset + uio->uio_resid >
+ p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
+ psignal(p, SIGXFSZ);
+ return (EFBIG);
+ }
+ /*
+ * I use nm_rsize, not nm_wsize so that all buffer cache blocks
+ * will be the same size within a filesystem. nfs_writerpc will
+ * still use nm_wsize when sizing the rpc's.
+ */
+ biosize = nmp->nm_rsize;
+ do {
+
+ /*
+ * Check for a valid write lease.
+ * If non-cachable, just do the rpc
+ */
+ if ((nmp->nm_flag & NFSMNT_NQNFS) &&
+ NQNFS_CKINVALID(vp, np, NQL_WRITE)) {
+ do {
+ error = nqnfs_getlease(vp, NQL_WRITE, cred, p);
+ } while (error == NQNFS_EXPIRED);
+ if (error)
+ return (error);
+ if (np->n_lrev != np->n_brev ||
+ (np->n_flag & NQNFSNONCACHE)) {
+ if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+ return (error);
+ np->n_brev = np->n_lrev;
+ }
+ }
+ if (np->n_flag & NQNFSNONCACHE)
+ return (nfs_writerpc(vp, uio, cred, ioflag));
+ nfsstats.biocache_writes++;
+ lbn = uio->uio_offset / biosize;
+ on = uio->uio_offset & (biosize-1);
+ n = min((unsigned)(biosize - on), uio->uio_resid);
+ bn = lbn * (biosize / DEV_BSIZE);
+again:
+ bp = nfs_getcacheblk(vp, bn, biosize, p);
+ if (!bp)
+ return (EINTR);
+ if (bp->b_wcred == NOCRED) {
+ crhold(cred);
+ bp->b_wcred = cred;
+ }
+ np->n_flag |= NMODIFIED;
+ if (uio->uio_offset + n > np->n_size) {
+ np->n_size = uio->uio_offset + n;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ }
+
+ /*
+ * If the new write will leave a contiguous dirty
+ * area, just update the b_dirtyoff and b_dirtyend,
+ * otherwise force a write rpc of the old dirty area.
+ */
+ if (bp->b_dirtyend > 0 &&
+ (on > bp->b_dirtyend || (on + n) < bp->b_dirtyoff)) {
+ bp->b_proc = p;
+ if (VOP_BWRITE(bp) == EINTR)
+ return (EINTR);
+ goto again;
+ }
+
+ /*
+ * Check for valid write lease and get one as required.
+ * In case getblk() and/or bwrite() delayed us.
+ */
+ if ((nmp->nm_flag & NFSMNT_NQNFS) &&
+ NQNFS_CKINVALID(vp, np, NQL_WRITE)) {
+ do {
+ error = nqnfs_getlease(vp, NQL_WRITE, cred, p);
+ } while (error == NQNFS_EXPIRED);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ if (np->n_lrev != np->n_brev ||
+ (np->n_flag & NQNFSNONCACHE)) {
+ brelse(bp);
+ if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+ return (error);
+ np->n_brev = np->n_lrev;
+ goto again;
+ }
+ }
+ if (error = uiomove((char *)bp->b_data + on, n, uio)) {
+ bp->b_flags |= B_ERROR;
+ brelse(bp);
+ return (error);
+ }
+ if (bp->b_dirtyend > 0) {
+ bp->b_dirtyoff = min(on, bp->b_dirtyoff);
+ bp->b_dirtyend = max((on + n), bp->b_dirtyend);
+ } else {
+ bp->b_dirtyoff = on;
+ bp->b_dirtyend = on + n;
+ }
+#ifndef notdef
+ if (bp->b_validend == 0 || bp->b_validend < bp->b_dirtyoff ||
+ bp->b_validoff > bp->b_dirtyend) {
+ bp->b_validoff = bp->b_dirtyoff;
+ bp->b_validend = bp->b_dirtyend;
+ } else {
+ bp->b_validoff = min(bp->b_validoff, bp->b_dirtyoff);
+ bp->b_validend = max(bp->b_validend, bp->b_dirtyend);
+ }
+#else
+ bp->b_validoff = bp->b_dirtyoff;
+ bp->b_validend = bp->b_dirtyend;
+#endif
+ if (ioflag & IO_APPEND)
+ bp->b_flags |= B_APPENDWRITE;
+
+ /*
+ * If the lease is non-cachable or IO_SYNC do bwrite().
+ */
+ if ((np->n_flag & NQNFSNONCACHE) || (ioflag & IO_SYNC)) {
+ bp->b_proc = p;
+ if (error = VOP_BWRITE(bp))
+ return (error);
+ } else if ((n + on) == biosize &&
+ (nmp->nm_flag & NFSMNT_NQNFS) == 0) {
+ bp->b_proc = (struct proc *)0;
+ bawrite(bp);
+ } else
+ bdwrite(bp);
+ } while (uio->uio_resid > 0 && n > 0);
+ return (0);
+}
+
+/*
+ * Get an nfs cache block.
+ * Allocate a new one if the block isn't currently in the cache
+ * and return the block marked busy. If the calling process is
+ * interrupted by a signal for an interruptible mount point, return
+ * NULL.
+ */
+struct buf *
+nfs_getcacheblk(vp, bn, size, p)
+ struct vnode *vp;
+ daddr_t bn;
+ int size;
+ struct proc *p;
+{
+ register struct buf *bp;
+ struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+
+ if (nmp->nm_flag & NFSMNT_INT) {
+ bp = getblk(vp, bn, size, PCATCH, 0);
+ while (bp == (struct buf *)0) {
+ if (nfs_sigintr(nmp, (struct nfsreq *)0, p))
+ return ((struct buf *)0);
+ bp = getblk(vp, bn, size, 0, 2 * hz);
+ }
+ } else
+ bp = getblk(vp, bn, size, 0, 0);
+ return (bp);
+}
+
+/*
+ * Flush and invalidate all dirty buffers. If another process is already
+ * doing the flush, just wait for completion.
+ */
+int
+nfs_vinvalbuf(vp, flags, cred, p, intrflg)
+ struct vnode *vp;
+ int flags;
+ struct ucred *cred;
+ struct proc *p;
+ int intrflg;
+{
+ register struct nfsnode *np = VTONFS(vp);
+ struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+ int error = 0, slpflag, slptimeo;
+
+ if ((nmp->nm_flag & NFSMNT_INT) == 0)
+ intrflg = 0;
+ if (intrflg) {
+ slpflag = PCATCH;
+ slptimeo = 2 * hz;
+ } else {
+ slpflag = 0;
+ slptimeo = 0;
+ }
+ /*
+ * First wait for any other process doing a flush to complete.
+ */
+ while (np->n_flag & NFLUSHINPROG) {
+ np->n_flag |= NFLUSHWANT;
+ error = tsleep((caddr_t)&np->n_flag, PRIBIO + 2, "nfsvinval",
+ slptimeo);
+ if (error && intrflg && nfs_sigintr(nmp, (struct nfsreq *)0, p))
+ return (EINTR);
+ }
+
+ /*
+ * Now, flush as required.
+ */
+ np->n_flag |= NFLUSHINPROG;
+ error = vinvalbuf(vp, flags, cred, p, slpflag, 0);
+ while (error) {
+ if (intrflg && nfs_sigintr(nmp, (struct nfsreq *)0, p)) {
+ np->n_flag &= ~NFLUSHINPROG;
+ if (np->n_flag & NFLUSHWANT) {
+ np->n_flag &= ~NFLUSHWANT;
+ wakeup((caddr_t)&np->n_flag);
+ }
+ return (EINTR);
+ }
+ error = vinvalbuf(vp, flags, cred, p, 0, slptimeo);
+ }
+ np->n_flag &= ~(NMODIFIED | NFLUSHINPROG);
+ if (np->n_flag & NFLUSHWANT) {
+ np->n_flag &= ~NFLUSHWANT;
+ wakeup((caddr_t)&np->n_flag);
+ }
+ return (0);
+}
+
+/*
+ * Initiate asynchronous I/O. Return an error if no nfsiods are available.
+ * This is mainly to avoid queueing async I/O requests when the nfsiods
+ * are all hung on a dead server.
+ */
+int
+nfs_asyncio(bp, cred)
+ register struct buf *bp;
+ struct ucred *cred;
+{
+ register int i;
+
+ if (nfs_numasync == 0)
+ return (EIO);
+ for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
+ if (nfs_iodwant[i]) {
+ if (bp->b_flags & B_READ) {
+ if (bp->b_rcred == NOCRED && cred != NOCRED) {
+ crhold(cred);
+ bp->b_rcred = cred;
+ }
+ } else {
+ if (bp->b_wcred == NOCRED && cred != NOCRED) {
+ crhold(cred);
+ bp->b_wcred = cred;
+ }
+ }
+
+ TAILQ_INSERT_TAIL(&nfs_bufq, bp, b_freelist);
+ nfs_iodwant[i] = (struct proc *)0;
+ wakeup((caddr_t)&nfs_iodwant[i]);
+ return (0);
+ }
+ return (EIO);
+}
+
+/*
+ * Do an I/O operation to/from a cache block. This may be called
+ * synchronously or from an nfsiod.
+ */
+int
+nfs_doio(bp, cr, p)
+ register struct buf *bp;
+ struct cred *cr;
+ struct proc *p;
+{
+ register struct uio *uiop;
+ register struct vnode *vp;
+ struct nfsnode *np;
+ struct nfsmount *nmp;
+ int error = 0, diff, len;
+ struct uio uio;
+ struct iovec io;
+
+ vp = bp->b_vp;
+ np = VTONFS(vp);
+ nmp = VFSTONFS(vp->v_mount);
+ uiop = &uio;
+ uiop->uio_iov = &io;
+ uiop->uio_iovcnt = 1;
+ uiop->uio_segflg = UIO_SYSSPACE;
+ uiop->uio_procp = p;
+
+ /*
+ * Historically, paging was done with physio, but no more.
+ */
+ if (bp->b_flags & B_PHYS)
+ panic("doio phys");
+ if (bp->b_flags & B_READ) {
+ io.iov_len = uiop->uio_resid = bp->b_bcount;
+ io.iov_base = bp->b_data;
+ uiop->uio_rw = UIO_READ;
+ switch (vp->v_type) {
+ case VREG:
+ uiop->uio_offset = bp->b_blkno * DEV_BSIZE;
+ nfsstats.read_bios++;
+ error = nfs_readrpc(vp, uiop, cr);
+ if (!error) {
+ bp->b_validoff = 0;
+ if (uiop->uio_resid) {
+ /*
+ * If len > 0, there is a hole in the file and
+ * no writes after the hole have been pushed to
+ * the server yet.
+ * Just zero fill the rest of the valid area.
+ */
+ diff = bp->b_bcount - uiop->uio_resid;
+ len = np->n_size - (bp->b_blkno * DEV_BSIZE
+ + diff);
+ if (len > 0) {
+ len = min(len, uiop->uio_resid);
+ bzero((char *)bp->b_data + diff, len);
+ bp->b_validend = diff + len;
+ } else
+ bp->b_validend = diff;
+ } else
+ bp->b_validend = bp->b_bcount;
+ }
+ if (p && (vp->v_flag & VTEXT) &&
+ (((nmp->nm_flag & NFSMNT_NQNFS) &&
+ np->n_lrev != np->n_brev) ||
+ (!(nmp->nm_flag & NFSMNT_NQNFS) &&
+ np->n_mtime != np->n_vattr.va_mtime.ts_sec))) {
+ uprintf("Process killed due to text file modification\n");
+ psignal(p, SIGKILL);
+ p->p_flag |= P_NOSWAP;
+ }
+ break;
+ case VLNK:
+ uiop->uio_offset = 0;
+ nfsstats.readlink_bios++;
+ error = nfs_readlinkrpc(vp, uiop, cr);
+ break;
+ case VDIR:
+ uiop->uio_offset = bp->b_lblkno;
+ nfsstats.readdir_bios++;
+ if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS)
+ error = nfs_readdirlookrpc(vp, uiop, cr);
+ else
+ error = nfs_readdirrpc(vp, uiop, cr);
+ /*
+ * Save offset cookie in b_blkno.
+ */
+ bp->b_blkno = uiop->uio_offset;
+ break;
+ };
+ if (error) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = error;
+ }
+ } else {
+ io.iov_len = uiop->uio_resid = bp->b_dirtyend
+ - bp->b_dirtyoff;
+ uiop->uio_offset = (bp->b_blkno * DEV_BSIZE)
+ + bp->b_dirtyoff;
+ io.iov_base = (char *)bp->b_data + bp->b_dirtyoff;
+ uiop->uio_rw = UIO_WRITE;
+ nfsstats.write_bios++;
+ if (bp->b_flags & B_APPENDWRITE)
+ error = nfs_writerpc(vp, uiop, cr, IO_APPEND);
+ else
+ error = nfs_writerpc(vp, uiop, cr, 0);
+ bp->b_flags &= ~(B_WRITEINPROG | B_APPENDWRITE);
+
+ /*
+ * For an interrupted write, the buffer is still valid and the
+ * write hasn't been pushed to the server yet, so we can't set
+ * B_ERROR and report the interruption by setting B_EINTR. For
+ * the B_ASYNC case, B_EINTR is not relevant, so the rpc attempt
+ * is essentially a noop.
+ */
+ if (error == EINTR) {
+ bp->b_flags &= ~B_INVAL;
+ bp->b_flags |= B_DELWRI;
+
+ /*
+ * Since for the B_ASYNC case, nfs_bwrite() has reassigned the
+ * buffer to the clean list, we have to reassign it back to the
+ * dirty one. Ugh.
+ */
+ if (bp->b_flags & B_ASYNC)
+ reassignbuf(bp, vp);
+ else
+ bp->b_flags |= B_EINTR;
+ } else {
+ if (error) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = np->n_error = error;
+ np->n_flag |= NWRITEERR;
+ }
+ bp->b_dirtyoff = bp->b_dirtyend = 0;
+ }
+ }
+ bp->b_resid = uiop->uio_resid;
+ biodone(bp);
+ return (error);
+}
diff --git a/sys/nfsclient/nfs_nfsiod.c b/sys/nfsclient/nfs_nfsiod.c
new file mode 100644
index 0000000..b00a225
--- /dev/null
+++ b/sys/nfsclient/nfs_nfsiod.c
@@ -0,0 +1,883 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_syscalls.c 8.3 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/namei.h>
+#include <sys/syslog.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsrvcache.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nqnfs.h>
+#include <nfs/nfsrtt.h>
+
+void nfsrv_zapsock __P((struct nfssvc_sock *));
+
+/* Global defs. */
+extern u_long nfs_prog, nfs_vers;
+extern int (*nfsrv_procs[NFS_NPROCS])();
+extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
+extern int nfs_numasync;
+extern time_t nqnfsstarttime;
+extern struct nfsrv_req nsrvq_head;
+extern struct nfsd nfsd_head;
+extern int nqsrv_writeslack;
+extern int nfsrtton;
+struct nfssvc_sock *nfs_udpsock, *nfs_cltpsock;
+int nuidhash_max = NFS_MAXUIDHASH;
+static int nfs_numnfsd = 0;
+int nfsd_waiting = 0;
+static int notstarted = 1;
+static int modify_flag = 0;
+static struct nfsdrt nfsdrt;
+void nfsrv_cleancache(), nfsrv_rcv(), nfsrv_wakenfsd(), nfs_sndunlock();
+static void nfsd_rt();
+void nfsrv_slpderef(), nfsrv_init();
+
+#define TRUE 1
+#define FALSE 0
+
+static int nfs_asyncdaemon[NFS_MAXASYNCDAEMON];
+/*
+ * NFS server system calls
+ * getfh() lives here too, but maybe should move to kern/vfs_syscalls.c
+ */
+
+/*
+ * Get file handle system call
+ */
+struct getfh_args {
+ char *fname;
+ fhandle_t *fhp;
+};
+int
+getfh(p, uap, retval)
+ struct proc *p;
+ register struct getfh_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ fhandle_t fh;
+ int error;
+ struct nameidata nd;
+
+ /*
+ * Must be super user
+ */
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ bzero((caddr_t)&fh, sizeof(fh));
+ fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+ error = VFS_VPTOFH(vp, &fh.fh_fid);
+ vput(vp);
+ if (error)
+ return (error);
+ error = copyout((caddr_t)&fh, (caddr_t)uap->fhp, sizeof (fh));
+ return (error);
+}
+
+static struct nfssvc_sock nfssvc_sockhead;
+
+/*
+ * Nfs server psuedo system call for the nfsd's
+ * Based on the flag value it either:
+ * - adds a socket to the selection list
+ * - remains in the kernel as an nfsd
+ * - remains in the kernel as an nfsiod
+ */
+struct nfssvc_args {
+ int flag;
+ caddr_t argp;
+};
+int
+nfssvc(p, uap, retval)
+ struct proc *p;
+ register struct nfssvc_args *uap;
+ int *retval;
+{
+ struct nameidata nd;
+ struct file *fp;
+ struct mbuf *nam;
+ struct nfsd_args nfsdarg;
+ struct nfsd_srvargs nfsd_srvargs, *nsd = &nfsd_srvargs;
+ struct nfsd_cargs ncd;
+ struct nfsd *nfsd;
+ struct nfssvc_sock *slp;
+ struct nfsuid *nuidp, **nuh;
+ struct nfsmount *nmp;
+ int error;
+
+ /*
+ * Must be super user
+ */
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ while (nfssvc_sockhead.ns_flag & SLP_INIT) {
+ nfssvc_sockhead.ns_flag |= SLP_WANTINIT;
+ (void) tsleep((caddr_t)&nfssvc_sockhead, PSOCK, "nfsd init", 0);
+ }
+ if (uap->flag & NFSSVC_BIOD)
+ error = nfssvc_iod(p);
+ else if (uap->flag & NFSSVC_MNTD) {
+ if (error = copyin(uap->argp, (caddr_t)&ncd, sizeof (ncd)))
+ return (error);
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
+ ncd.ncd_dirp, p);
+ if (error = namei(&nd))
+ return (error);
+ if ((nd.ni_vp->v_flag & VROOT) == 0)
+ error = EINVAL;
+ nmp = VFSTONFS(nd.ni_vp->v_mount);
+ vput(nd.ni_vp);
+ if (error)
+ return (error);
+ if ((nmp->nm_flag & NFSMNT_MNTD) &&
+ (uap->flag & NFSSVC_GOTAUTH) == 0)
+ return (0);
+ nmp->nm_flag |= NFSMNT_MNTD;
+ error = nqnfs_clientd(nmp, p->p_ucred, &ncd, uap->flag,
+ uap->argp, p);
+ } else if (uap->flag & NFSSVC_ADDSOCK) {
+ if (error = copyin(uap->argp, (caddr_t)&nfsdarg,
+ sizeof(nfsdarg)))
+ return (error);
+ if (error = getsock(p->p_fd, nfsdarg.sock, &fp))
+ return (error);
+ /*
+ * Get the client address for connected sockets.
+ */
+ if (nfsdarg.name == NULL || nfsdarg.namelen == 0)
+ nam = (struct mbuf *)0;
+ else if (error = sockargs(&nam, nfsdarg.name, nfsdarg.namelen,
+ MT_SONAME))
+ return (error);
+ error = nfssvc_addsock(fp, nam);
+ } else {
+ if (error = copyin(uap->argp, (caddr_t)nsd, sizeof (*nsd)))
+ return (error);
+ if ((uap->flag & NFSSVC_AUTHIN) && (nfsd = nsd->nsd_nfsd) &&
+ (nfsd->nd_slp->ns_flag & SLP_VALID)) {
+ slp = nfsd->nd_slp;
+
+ /*
+ * First check to see if another nfsd has already
+ * added this credential.
+ */
+ nuidp = slp->ns_uidh[NUIDHASH(nsd->nsd_uid)];
+ while (nuidp) {
+ if (nuidp->nu_uid == nsd->nsd_uid)
+ break;
+ nuidp = nuidp->nu_hnext;
+ }
+ if (!nuidp) {
+ /*
+ * Nope, so we will.
+ */
+ if (slp->ns_numuids < nuidhash_max) {
+ slp->ns_numuids++;
+ nuidp = (struct nfsuid *)
+ malloc(sizeof (struct nfsuid), M_NFSUID,
+ M_WAITOK);
+ } else
+ nuidp = (struct nfsuid *)0;
+ if ((slp->ns_flag & SLP_VALID) == 0) {
+ if (nuidp)
+ free((caddr_t)nuidp, M_NFSUID);
+ } else {
+ if (nuidp == (struct nfsuid *)0) {
+ nuidp = slp->ns_lruprev;
+ remque(nuidp);
+ if (nuidp->nu_hprev)
+ nuidp->nu_hprev->nu_hnext =
+ nuidp->nu_hnext;
+ if (nuidp->nu_hnext)
+ nuidp->nu_hnext->nu_hprev =
+ nuidp->nu_hprev;
+ }
+ nuidp->nu_cr = nsd->nsd_cr;
+ if (nuidp->nu_cr.cr_ngroups > NGROUPS)
+ nuidp->nu_cr.cr_ngroups = NGROUPS;
+ nuidp->nu_cr.cr_ref = 1;
+ nuidp->nu_uid = nsd->nsd_uid;
+ insque(nuidp, (struct nfsuid *)slp);
+ nuh = &slp->ns_uidh[NUIDHASH(nsd->nsd_uid)];
+ if (nuidp->nu_hnext = *nuh)
+ nuidp->nu_hnext->nu_hprev = nuidp;
+ nuidp->nu_hprev = (struct nfsuid *)0;
+ *nuh = nuidp;
+ }
+ }
+ }
+ if ((uap->flag & NFSSVC_AUTHINFAIL) && (nfsd = nsd->nsd_nfsd))
+ nfsd->nd_flag |= NFSD_AUTHFAIL;
+ error = nfssvc_nfsd(nsd, uap->argp, p);
+ }
+ if (error == EINTR || error == ERESTART)
+ error = 0;
+ return (error);
+}
+
+/*
+ * Adds a socket to the list for servicing by nfsds.
+ */
+int
+nfssvc_addsock(fp, mynam)
+ struct file *fp;
+ struct mbuf *mynam;
+{
+ register struct mbuf *m;
+ register int siz;
+ register struct nfssvc_sock *slp;
+ register struct socket *so;
+ struct nfssvc_sock *tslp;
+ int error, s;
+
+ so = (struct socket *)fp->f_data;
+ tslp = (struct nfssvc_sock *)0;
+ /*
+ * Add it to the list, as required.
+ */
+ if (so->so_proto->pr_protocol == IPPROTO_UDP) {
+ tslp = nfs_udpsock;
+ if (tslp->ns_flag & SLP_VALID) {
+ m_freem(mynam);
+ return (EPERM);
+ }
+#ifdef ISO
+ } else if (so->so_proto->pr_protocol == ISOPROTO_CLTP) {
+ tslp = nfs_cltpsock;
+ if (tslp->ns_flag & SLP_VALID) {
+ m_freem(mynam);
+ return (EPERM);
+ }
+#endif /* ISO */
+ }
+ if (so->so_type == SOCK_STREAM)
+ siz = NFS_MAXPACKET + sizeof (u_long);
+ else
+ siz = NFS_MAXPACKET;
+ if (error = soreserve(so, siz, siz)) {
+ m_freem(mynam);
+ return (error);
+ }
+
+ /*
+ * Set protocol specific options { for now TCP only } and
+ * reserve some space. For datagram sockets, this can get called
+ * repeatedly for the same socket, but that isn't harmful.
+ */
+ if (so->so_type == SOCK_STREAM) {
+ MGET(m, M_WAIT, MT_SOOPTS);
+ *mtod(m, int *) = 1;
+ m->m_len = sizeof(int);
+ sosetopt(so, SOL_SOCKET, SO_KEEPALIVE, m);
+ }
+ if (so->so_proto->pr_domain->dom_family == AF_INET &&
+ so->so_proto->pr_protocol == IPPROTO_TCP) {
+ MGET(m, M_WAIT, MT_SOOPTS);
+ *mtod(m, int *) = 1;
+ m->m_len = sizeof(int);
+ sosetopt(so, IPPROTO_TCP, TCP_NODELAY, m);
+ }
+ so->so_rcv.sb_flags &= ~SB_NOINTR;
+ so->so_rcv.sb_timeo = 0;
+ so->so_snd.sb_flags &= ~SB_NOINTR;
+ so->so_snd.sb_timeo = 0;
+ if (tslp)
+ slp = tslp;
+ else {
+ slp = (struct nfssvc_sock *)
+ malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
+ bzero((caddr_t)slp, sizeof (struct nfssvc_sock));
+ slp->ns_prev = nfssvc_sockhead.ns_prev;
+ slp->ns_prev->ns_next = slp;
+ slp->ns_next = &nfssvc_sockhead;
+ nfssvc_sockhead.ns_prev = slp;
+ slp->ns_lrunext = slp->ns_lruprev = (struct nfsuid *)slp;
+ }
+ slp->ns_so = so;
+ slp->ns_nam = mynam;
+ fp->f_count++;
+ slp->ns_fp = fp;
+ s = splnet();
+ so->so_upcallarg = (caddr_t)slp;
+ so->so_upcall = nfsrv_rcv;
+ slp->ns_flag = (SLP_VALID | SLP_NEEDQ);
+ nfsrv_wakenfsd(slp);
+ splx(s);
+ return (0);
+}
+
+/*
+ * Called by nfssvc() for nfsds. Just loops around servicing rpc requests
+ * until it is killed by a signal.
+ */
+int
+nfssvc_nfsd(nsd, argp, p)
+ struct nfsd_srvargs *nsd;
+ caddr_t argp;
+ struct proc *p;
+{
+ register struct mbuf *m, *nam2;
+ register int siz;
+ register struct nfssvc_sock *slp;
+ register struct socket *so;
+ register int *solockp;
+ struct nfsd *nd = nsd->nsd_nfsd;
+ struct mbuf *mreq, *nam;
+ struct timeval starttime;
+ struct nfsuid *uidp;
+ int error = 0, cacherep, s;
+ int sotype;
+
+ s = splnet();
+ if (nd == (struct nfsd *)0) {
+ nsd->nsd_nfsd = nd = (struct nfsd *)
+ malloc(sizeof (struct nfsd), M_NFSD, M_WAITOK);
+ bzero((caddr_t)nd, sizeof (struct nfsd));
+ nd->nd_procp = p;
+ nd->nd_cr.cr_ref = 1;
+ insque(nd, &nfsd_head);
+ nd->nd_nqlflag = NQL_NOVAL;
+ nfs_numnfsd++;
+ }
+ /*
+ * Loop getting rpc requests until SIGKILL.
+ */
+ for (;;) {
+ if ((nd->nd_flag & NFSD_REQINPROG) == 0) {
+ while (nd->nd_slp == (struct nfssvc_sock *)0 &&
+ (nfsd_head.nd_flag & NFSD_CHECKSLP) == 0) {
+ nd->nd_flag |= NFSD_WAITING;
+ nfsd_waiting++;
+ error = tsleep((caddr_t)nd, PSOCK | PCATCH, "nfsd", 0);
+ nfsd_waiting--;
+ if (error)
+ goto done;
+ }
+ if (nd->nd_slp == (struct nfssvc_sock *)0 &&
+ (nfsd_head.nd_flag & NFSD_CHECKSLP)) {
+ slp = nfssvc_sockhead.ns_next;
+ while (slp != &nfssvc_sockhead) {
+ if ((slp->ns_flag & (SLP_VALID | SLP_DOREC))
+ == (SLP_VALID | SLP_DOREC)) {
+ slp->ns_flag &= ~SLP_DOREC;
+ slp->ns_sref++;
+ nd->nd_slp = slp;
+ break;
+ }
+ slp = slp->ns_next;
+ }
+ if (slp == &nfssvc_sockhead)
+ nfsd_head.nd_flag &= ~NFSD_CHECKSLP;
+ }
+ if ((slp = nd->nd_slp) == (struct nfssvc_sock *)0)
+ continue;
+ if (slp->ns_flag & SLP_VALID) {
+ if (slp->ns_flag & SLP_DISCONN)
+ nfsrv_zapsock(slp);
+ else if (slp->ns_flag & SLP_NEEDQ) {
+ slp->ns_flag &= ~SLP_NEEDQ;
+ (void) nfs_sndlock(&slp->ns_solock,
+ (struct nfsreq *)0);
+ nfsrv_rcv(slp->ns_so, (caddr_t)slp,
+ M_WAIT);
+ nfs_sndunlock(&slp->ns_solock);
+ }
+ error = nfsrv_dorec(slp, nd);
+ nd->nd_flag |= NFSD_REQINPROG;
+ }
+ } else {
+ error = 0;
+ slp = nd->nd_slp;
+ }
+ if (error || (slp->ns_flag & SLP_VALID) == 0) {
+ nd->nd_slp = (struct nfssvc_sock *)0;
+ nd->nd_flag &= ~NFSD_REQINPROG;
+ nfsrv_slpderef(slp);
+ continue;
+ }
+ splx(s);
+ so = slp->ns_so;
+ sotype = so->so_type;
+ starttime = time;
+ if (so->so_proto->pr_flags & PR_CONNREQUIRED)
+ solockp = &slp->ns_solock;
+ else
+ solockp = (int *)0;
+ /*
+ * nam == nam2 for connectionless protocols such as UDP
+ * nam2 == NULL for connection based protocols to disable
+ * recent request caching.
+ */
+ if (nam2 = nd->nd_nam) {
+ nam = nam2;
+ cacherep = RC_CHECKIT;
+ } else {
+ nam = slp->ns_nam;
+ cacherep = RC_DOIT;
+ }
+
+ /*
+ * Check to see if authorization is needed.
+ */
+ if (nd->nd_flag & NFSD_NEEDAUTH) {
+ static int logauth = 0;
+
+ nd->nd_flag &= ~NFSD_NEEDAUTH;
+ /*
+ * Check for a mapping already installed.
+ */
+ uidp = slp->ns_uidh[NUIDHASH(nd->nd_cr.cr_uid)];
+ while (uidp) {
+ if (uidp->nu_uid == nd->nd_cr.cr_uid)
+ break;
+ uidp = uidp->nu_hnext;
+ }
+ if (!uidp) {
+ nsd->nsd_uid = nd->nd_cr.cr_uid;
+ if (nam2 && logauth++ == 0)
+ log(LOG_WARNING, "Kerberized NFS using UDP\n");
+ nsd->nsd_haddr =
+ mtod(nam, struct sockaddr_in *)->sin_addr.s_addr;
+ nsd->nsd_authlen = nd->nd_authlen;
+ if (copyout(nd->nd_authstr, nsd->nsd_authstr,
+ nd->nd_authlen) == 0 &&
+ copyout((caddr_t)nsd, argp, sizeof (*nsd)) == 0)
+ return (ENEEDAUTH);
+ cacherep = RC_DROPIT;
+ }
+ }
+ if (cacherep == RC_CHECKIT)
+ cacherep = nfsrv_getcache(nam2, nd, &mreq);
+
+ /*
+ * Check for just starting up for NQNFS and send
+ * fake "try again later" replies to the NQNFS clients.
+ */
+ if (notstarted && nqnfsstarttime <= time.tv_sec) {
+ if (modify_flag) {
+ nqnfsstarttime = time.tv_sec + nqsrv_writeslack;
+ modify_flag = 0;
+ } else
+ notstarted = 0;
+ }
+ if (notstarted) {
+ if (nd->nd_nqlflag == NQL_NOVAL)
+ cacherep = RC_DROPIT;
+ else if (nd->nd_procnum != NFSPROC_WRITE) {
+ nd->nd_procnum = NFSPROC_NOOP;
+ nd->nd_repstat = NQNFS_TRYLATER;
+ cacherep = RC_DOIT;
+ } else
+ modify_flag = 1;
+ } else if (nd->nd_flag & NFSD_AUTHFAIL) {
+ nd->nd_flag &= ~NFSD_AUTHFAIL;
+ nd->nd_procnum = NFSPROC_NOOP;
+ nd->nd_repstat = NQNFS_AUTHERR;
+ cacherep = RC_DOIT;
+ }
+
+ switch (cacherep) {
+ case RC_DOIT:
+ error = (*(nfsrv_procs[nd->nd_procnum]))(nd,
+ nd->nd_mrep, nd->nd_md, nd->nd_dpos, &nd->nd_cr,
+ nam, &mreq);
+ if (nd->nd_cr.cr_ref != 1) {
+ printf("nfssvc cref=%d\n", nd->nd_cr.cr_ref);
+ panic("nfssvc cref");
+ }
+ if (error) {
+ if (nd->nd_procnum != NQNFSPROC_VACATED)
+ nfsstats.srv_errs++;
+ if (nam2) {
+ nfsrv_updatecache(nam2, nd, FALSE, mreq);
+ m_freem(nam2);
+ }
+ break;
+ }
+ nfsstats.srvrpccnt[nd->nd_procnum]++;
+ if (nam2)
+ nfsrv_updatecache(nam2, nd, TRUE, mreq);
+ nd->nd_mrep = (struct mbuf *)0;
+ case RC_REPLY:
+ m = mreq;
+ siz = 0;
+ while (m) {
+ siz += m->m_len;
+ m = m->m_next;
+ }
+ if (siz <= 0 || siz > NFS_MAXPACKET) {
+ printf("mbuf siz=%d\n",siz);
+ panic("Bad nfs svc reply");
+ }
+ m = mreq;
+ m->m_pkthdr.len = siz;
+ m->m_pkthdr.rcvif = (struct ifnet *)0;
+ /*
+ * For stream protocols, prepend a Sun RPC
+ * Record Mark.
+ */
+ if (sotype == SOCK_STREAM) {
+ M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
+ *mtod(m, u_long *) = htonl(0x80000000 | siz);
+ }
+ if (solockp)
+ (void) nfs_sndlock(solockp, (struct nfsreq *)0);
+ if (slp->ns_flag & SLP_VALID)
+ error = nfs_send(so, nam2, m, (struct nfsreq *)0);
+ else {
+ error = EPIPE;
+ m_freem(m);
+ }
+ if (nfsrtton)
+ nfsd_rt(&starttime, sotype, nd, nam, cacherep);
+ if (nam2)
+ MFREE(nam2, m);
+ if (nd->nd_mrep)
+ m_freem(nd->nd_mrep);
+ if (error == EPIPE)
+ nfsrv_zapsock(slp);
+ if (solockp)
+ nfs_sndunlock(solockp);
+ if (error == EINTR || error == ERESTART) {
+ nfsrv_slpderef(slp);
+ s = splnet();
+ goto done;
+ }
+ break;
+ case RC_DROPIT:
+ if (nfsrtton)
+ nfsd_rt(&starttime, sotype, nd, nam, cacherep);
+ m_freem(nd->nd_mrep);
+ m_freem(nam2);
+ break;
+ };
+ s = splnet();
+ if (nfsrv_dorec(slp, nd)) {
+ nd->nd_flag &= ~NFSD_REQINPROG;
+ nd->nd_slp = (struct nfssvc_sock *)0;
+ nfsrv_slpderef(slp);
+ }
+ }
+done:
+ remque(nd);
+ splx(s);
+ free((caddr_t)nd, M_NFSD);
+ nsd->nsd_nfsd = (struct nfsd *)0;
+ if (--nfs_numnfsd == 0)
+ nfsrv_init(TRUE); /* Reinitialize everything */
+ return (error);
+}
+
+/*
+ * Asynchronous I/O daemons for client nfs.
+ * They do read-ahead and write-behind operations on the block I/O cache.
+ * Never returns unless it fails or gets killed.
+ */
+int
+nfssvc_iod(p)
+ struct proc *p;
+{
+ register struct buf *bp;
+ register int i, myiod;
+ int error = 0;
+
+ /*
+ * Assign my position or return error if too many already running
+ */
+ myiod = -1;
+ for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
+ if (nfs_asyncdaemon[i] == 0) {
+ nfs_asyncdaemon[i]++;
+ myiod = i;
+ break;
+ }
+ if (myiod == -1)
+ return (EBUSY);
+ nfs_numasync++;
+ /*
+ * Just loop around doin our stuff until SIGKILL
+ */
+ for (;;) {
+ while (nfs_bufq.tqh_first == NULL && error == 0) {
+ nfs_iodwant[myiod] = p;
+ error = tsleep((caddr_t)&nfs_iodwant[myiod],
+ PWAIT | PCATCH, "nfsidl", 0);
+ }
+ while ((bp = nfs_bufq.tqh_first) != NULL) {
+ /* Take one off the front of the list */
+ TAILQ_REMOVE(&nfs_bufq, bp, b_freelist);
+ if (bp->b_flags & B_READ)
+ (void) nfs_doio(bp, bp->b_rcred, (struct proc *)0);
+ else
+ (void) nfs_doio(bp, bp->b_wcred, (struct proc *)0);
+ }
+ if (error) {
+ nfs_asyncdaemon[myiod] = 0;
+ nfs_numasync--;
+ return (error);
+ }
+ }
+}
+
+/*
+ * Shut down a socket associated with an nfssvc_sock structure.
+ * Should be called with the send lock set, if required.
+ * The trick here is to increment the sref at the start, so that the nfsds
+ * will stop using it and clear ns_flag at the end so that it will not be
+ * reassigned during cleanup.
+ */
+void
+nfsrv_zapsock(slp)
+ register struct nfssvc_sock *slp;
+{
+ register struct nfsuid *nuidp, *onuidp;
+ register int i;
+ struct socket *so;
+ struct file *fp;
+ struct mbuf *m;
+
+ slp->ns_flag &= ~SLP_ALLFLAGS;
+ if (fp = slp->ns_fp) {
+ slp->ns_fp = (struct file *)0;
+ so = slp->ns_so;
+ so->so_upcall = NULL;
+ soshutdown(so, 2);
+ closef(fp, (struct proc *)0);
+ if (slp->ns_nam)
+ MFREE(slp->ns_nam, m);
+ m_freem(slp->ns_raw);
+ m_freem(slp->ns_rec);
+ nuidp = slp->ns_lrunext;
+ while (nuidp != (struct nfsuid *)slp) {
+ onuidp = nuidp;
+ nuidp = nuidp->nu_lrunext;
+ free((caddr_t)onuidp, M_NFSUID);
+ }
+ slp->ns_lrunext = slp->ns_lruprev = (struct nfsuid *)slp;
+ for (i = 0; i < NUIDHASHSIZ; i++)
+ slp->ns_uidh[i] = (struct nfsuid *)0;
+ }
+}
+
+/*
+ * Get an authorization string for the uid by having the mount_nfs sitting
+ * on this mount point porpous out of the kernel and do it.
+ */
+int
+nfs_getauth(nmp, rep, cred, auth_type, auth_str, auth_len)
+ register struct nfsmount *nmp;
+ struct nfsreq *rep;
+ struct ucred *cred;
+ int *auth_type;
+ char **auth_str;
+ int *auth_len;
+{
+ int error = 0;
+
+ while ((nmp->nm_flag & NFSMNT_WAITAUTH) == 0) {
+ nmp->nm_flag |= NFSMNT_WANTAUTH;
+ (void) tsleep((caddr_t)&nmp->nm_authtype, PSOCK,
+ "nfsauth1", 2 * hz);
+ if (error = nfs_sigintr(nmp, rep, rep->r_procp)) {
+ nmp->nm_flag &= ~NFSMNT_WANTAUTH;
+ return (error);
+ }
+ }
+ nmp->nm_flag &= ~(NFSMNT_WAITAUTH | NFSMNT_WANTAUTH);
+ nmp->nm_authstr = *auth_str = (char *)malloc(RPCAUTH_MAXSIZ, M_TEMP, M_WAITOK);
+ nmp->nm_authuid = cred->cr_uid;
+ wakeup((caddr_t)&nmp->nm_authstr);
+
+ /*
+ * And wait for mount_nfs to do its stuff.
+ */
+ while ((nmp->nm_flag & NFSMNT_HASAUTH) == 0 && error == 0) {
+ (void) tsleep((caddr_t)&nmp->nm_authlen, PSOCK,
+ "nfsauth2", 2 * hz);
+ error = nfs_sigintr(nmp, rep, rep->r_procp);
+ }
+ if (nmp->nm_flag & NFSMNT_AUTHERR) {
+ nmp->nm_flag &= ~NFSMNT_AUTHERR;
+ error = EAUTH;
+ }
+ if (error)
+ free((caddr_t)*auth_str, M_TEMP);
+ else {
+ *auth_type = nmp->nm_authtype;
+ *auth_len = nmp->nm_authlen;
+ }
+ nmp->nm_flag &= ~NFSMNT_HASAUTH;
+ nmp->nm_flag |= NFSMNT_WAITAUTH;
+ if (nmp->nm_flag & NFSMNT_WANTAUTH) {
+ nmp->nm_flag &= ~NFSMNT_WANTAUTH;
+ wakeup((caddr_t)&nmp->nm_authtype);
+ }
+ return (error);
+}
+
+/*
+ * Derefence a server socket structure. If it has no more references and
+ * is no longer valid, you can throw it away.
+ */
+void
+nfsrv_slpderef(slp)
+ register struct nfssvc_sock *slp;
+{
+ if (--(slp->ns_sref) == 0 && (slp->ns_flag & SLP_VALID) == 0) {
+ slp->ns_prev->ns_next = slp->ns_next;
+ slp->ns_next->ns_prev = slp->ns_prev;
+ free((caddr_t)slp, M_NFSSVC);
+ }
+}
+
+/*
+ * Initialize the data structures for the server.
+ * Handshake with any new nfsds starting up to avoid any chance of
+ * corruption.
+ */
+void
+nfsrv_init(terminating)
+ int terminating;
+{
+ register struct nfssvc_sock *slp;
+ struct nfssvc_sock *oslp;
+
+ if (nfssvc_sockhead.ns_flag & SLP_INIT)
+ panic("nfsd init");
+ nfssvc_sockhead.ns_flag |= SLP_INIT;
+ if (terminating) {
+ slp = nfssvc_sockhead.ns_next;
+ while (slp != &nfssvc_sockhead) {
+ if (slp->ns_flag & SLP_VALID)
+ nfsrv_zapsock(slp);
+ slp->ns_next->ns_prev = slp->ns_prev;
+ slp->ns_prev->ns_next = slp->ns_next;
+ oslp = slp;
+ slp = slp->ns_next;
+ free((caddr_t)oslp, M_NFSSVC);
+ }
+ nfsrv_cleancache(); /* And clear out server cache */
+ }
+ nfs_udpsock = (struct nfssvc_sock *)
+ malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
+ bzero((caddr_t)nfs_udpsock, sizeof (struct nfssvc_sock));
+ nfs_cltpsock = (struct nfssvc_sock *)
+ malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
+ bzero((caddr_t)nfs_cltpsock, sizeof (struct nfssvc_sock));
+ nfssvc_sockhead.ns_next = nfs_udpsock;
+ nfs_udpsock->ns_next = nfs_cltpsock;
+ nfs_cltpsock->ns_next = &nfssvc_sockhead;
+ nfssvc_sockhead.ns_prev = nfs_cltpsock;
+ nfs_cltpsock->ns_prev = nfs_udpsock;
+ nfs_udpsock->ns_prev = &nfssvc_sockhead;
+ nfs_udpsock->ns_lrunext = nfs_udpsock->ns_lruprev =
+ (struct nfsuid *)nfs_udpsock;
+ nfs_cltpsock->ns_lrunext = nfs_cltpsock->ns_lruprev =
+ (struct nfsuid *)nfs_cltpsock;
+ nfsd_head.nd_next = nfsd_head.nd_prev = &nfsd_head;
+ nfsd_head.nd_flag = 0;
+ nfssvc_sockhead.ns_flag &= ~SLP_INIT;
+ if (nfssvc_sockhead.ns_flag & SLP_WANTINIT) {
+ nfssvc_sockhead.ns_flag &= ~SLP_WANTINIT;
+ wakeup((caddr_t)&nfssvc_sockhead);
+ }
+}
+
+/*
+ * Add entries to the server monitor log.
+ */
+static void
+nfsd_rt(startp, sotype, nd, nam, cacherep)
+ struct timeval *startp;
+ int sotype;
+ register struct nfsd *nd;
+ struct mbuf *nam;
+ int cacherep;
+{
+ register struct drt *rt;
+
+ rt = &nfsdrt.drt[nfsdrt.pos];
+ if (cacherep == RC_DOIT)
+ rt->flag = 0;
+ else if (cacherep == RC_REPLY)
+ rt->flag = DRT_CACHEREPLY;
+ else
+ rt->flag = DRT_CACHEDROP;
+ if (sotype == SOCK_STREAM)
+ rt->flag |= DRT_TCP;
+ if (nd->nd_nqlflag != NQL_NOVAL)
+ rt->flag |= DRT_NQNFS;
+ rt->proc = nd->nd_procnum;
+ if (mtod(nam, struct sockaddr *)->sa_family == AF_INET)
+ rt->ipadr = mtod(nam, struct sockaddr_in *)->sin_addr.s_addr;
+ else
+ rt->ipadr = INADDR_ANY;
+ rt->resptime = ((time.tv_sec - startp->tv_sec) * 1000000) +
+ (time.tv_usec - startp->tv_usec);
+ rt->tstamp = time;
+ nfsdrt.pos = (nfsdrt.pos + 1) % NFSRTTLOGSIZ;
+}
diff --git a/sys/nfsclient/nfs_node.c b/sys/nfsclient/nfs_node.c
new file mode 100644
index 0000000..9c70c53
--- /dev/null
+++ b/sys/nfsclient/nfs_node.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_node.c 8.2 (Berkeley) 12/30/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nqnfs.h>
+
+struct nfsnode **nheadhashtbl;
+u_long nheadhash;
+#define NFSNOHASH(fhsum) ((fhsum)&nheadhash)
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Initialize hash links for nfsnodes
+ * and build nfsnode free list.
+ */
+void
+nfs_nhinit()
+{
+
+#ifndef lint
+ if ((sizeof(struct nfsnode) - 1) & sizeof(struct nfsnode))
+ printf("nfs_nhinit: bad size %d\n", sizeof(struct nfsnode));
+#endif /* not lint */
+ nheadhashtbl = hashinit(desiredvnodes, M_NFSNODE, &nheadhash);
+}
+
+/*
+ * Compute an entry in the NFS hash table structure
+ */
+struct nfsnode **
+nfs_hash(fhp)
+ register nfsv2fh_t *fhp;
+{
+ register u_char *fhpp;
+ register u_long fhsum;
+ int i;
+
+ fhpp = &fhp->fh_bytes[0];
+ fhsum = 0;
+ for (i = 0; i < NFSX_FH; i++)
+ fhsum += *fhpp++;
+ return (&nheadhashtbl[NFSNOHASH(fhsum)]);
+}
+
+/*
+ * Look up a vnode/nfsnode by file handle.
+ * Callers must check for mount points!!
+ * In all cases, a pointer to a
+ * nfsnode structure is returned.
+ */
+int
+nfs_nget(mntp, fhp, npp)
+ struct mount *mntp;
+ register nfsv2fh_t *fhp;
+ struct nfsnode **npp;
+{
+ register struct nfsnode *np, *nq, **nhpp;
+ register struct vnode *vp;
+ extern int (**nfsv2_vnodeop_p)();
+ struct vnode *nvp;
+ int error;
+
+ nhpp = nfs_hash(fhp);
+loop:
+ for (np = *nhpp; np; np = np->n_forw) {
+ if (mntp != NFSTOV(np)->v_mount ||
+ bcmp((caddr_t)fhp, (caddr_t)&np->n_fh, NFSX_FH))
+ continue;
+ vp = NFSTOV(np);
+ if (vget(vp, 1))
+ goto loop;
+ *npp = np;
+ return(0);
+ }
+ if (error = getnewvnode(VT_NFS, mntp, nfsv2_vnodeop_p, &nvp)) {
+ *npp = 0;
+ return (error);
+ }
+ vp = nvp;
+ MALLOC(np, struct nfsnode *, sizeof *np, M_NFSNODE, M_WAITOK);
+ vp->v_data = np;
+ np->n_vnode = vp;
+ /*
+ * Insert the nfsnode in the hash queue for its new file handle
+ */
+ np->n_flag = 0;
+ if (nq = *nhpp)
+ nq->n_back = &np->n_forw;
+ np->n_forw = nq;
+ np->n_back = nhpp;
+ *nhpp = np;
+ bcopy((caddr_t)fhp, (caddr_t)&np->n_fh, NFSX_FH);
+ np->n_attrstamp = 0;
+ np->n_direofoffset = 0;
+ np->n_sillyrename = (struct sillyrename *)0;
+ np->n_size = 0;
+ np->n_mtime = 0;
+ if (VFSTONFS(mntp)->nm_flag & NFSMNT_NQNFS) {
+ np->n_brev = 0;
+ np->n_lrev = 0;
+ np->n_expiry = (time_t)0;
+ np->n_tnext = (struct nfsnode *)0;
+ }
+ *npp = np;
+ return (0);
+}
+
+int
+nfs_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct nfsnode *np;
+ register struct sillyrename *sp;
+ struct proc *p = curproc; /* XXX */
+ extern int prtactive;
+
+ np = VTONFS(ap->a_vp);
+ if (prtactive && ap->a_vp->v_usecount != 0)
+ vprint("nfs_inactive: pushing active", ap->a_vp);
+ sp = np->n_sillyrename;
+ np->n_sillyrename = (struct sillyrename *)0;
+ if (sp) {
+ /*
+ * Remove the silly file that was rename'd earlier
+ */
+ (void) nfs_vinvalbuf(ap->a_vp, 0, sp->s_cred, p, 1);
+ nfs_removeit(sp);
+ crfree(sp->s_cred);
+ vrele(sp->s_dvp);
+#ifdef SILLYSEPARATE
+ free((caddr_t)sp, M_NFSREQ);
+#endif
+ }
+ np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NQNFSEVICTED |
+ NQNFSNONCACHE | NQNFSWRITE);
+ return (0);
+}
+
+/*
+ * Reclaim an nfsnode so that it can be used for other purposes.
+ */
+int
+nfs_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+ register struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+ register struct nfsnode *nq;
+ extern int prtactive;
+
+ if (prtactive && vp->v_usecount != 0)
+ vprint("nfs_reclaim: pushing active", vp);
+ /*
+ * Remove the nfsnode from its hash chain.
+ */
+ if (nq = np->n_forw)
+ nq->n_back = np->n_back;
+ *np->n_back = nq;
+
+ /*
+ * For nqnfs, take it off the timer queue as required.
+ */
+ if ((nmp->nm_flag & NFSMNT_NQNFS) && np->n_tnext) {
+ if (np->n_tnext == (struct nfsnode *)nmp)
+ nmp->nm_tprev = np->n_tprev;
+ else
+ np->n_tnext->n_tprev = np->n_tprev;
+ if (np->n_tprev == (struct nfsnode *)nmp)
+ nmp->nm_tnext = np->n_tnext;
+ else
+ np->n_tprev->n_tnext = np->n_tnext;
+ }
+ cache_purge(vp);
+ FREE(vp->v_data, M_NFSNODE);
+ vp->v_data = (void *)0;
+ return (0);
+}
+
+/*
+ * Lock an nfsnode
+ */
+int
+nfs_lock(ap)
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+
+ /*
+ * Ugh, another place where interruptible mounts will get hung.
+ * If you make this sleep interruptible, then you have to fix all
+ * the VOP_LOCK() calls to expect interruptibility.
+ */
+ while (vp->v_flag & VXLOCK) {
+ vp->v_flag |= VXWANT;
+ sleep((caddr_t)vp, PINOD);
+ }
+ if (vp->v_tag == VT_NON)
+ return (ENOENT);
+ return (0);
+}
+
+/*
+ * Unlock an nfsnode
+ */
+int
+nfs_unlock(ap)
+ struct vop_unlock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * Check for a locked nfsnode
+ */
+int
+nfs_islocked(ap)
+ struct vop_islocked_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * Nfs abort op, called after namei() when a CREATE/DELETE isn't actually
+ * done. Currently nothing to do.
+ */
+/* ARGSUSED */
+int
+nfs_abortop(ap)
+ struct vop_abortop_args /* {
+ struct vnode *a_dvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+
+ if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
+ FREE(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ return (0);
+}
diff --git a/sys/nfsclient/nfs_socket.c b/sys/nfsclient/nfs_socket.c
new file mode 100644
index 0000000..c00f7d0
--- /dev/null
+++ b/sys/nfsclient/nfs_socket.c
@@ -0,0 +1,2005 @@
+/*
+ * Copyright (c) 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_socket.c 8.3 (Berkeley) 1/12/94
+ */
+
+/*
+ * Socket operations for use by nfs
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/vnode.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/syslog.h>
+#include <sys/tprintf.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfsrtt.h>
+#include <nfs/nqnfs.h>
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Estimate rto for an nfs rpc sent via. an unreliable datagram.
+ * Use the mean and mean deviation of rtt for the appropriate type of rpc
+ * for the frequent rpcs and a default for the others.
+ * The justification for doing "other" this way is that these rpcs
+ * happen so infrequently that timer est. would probably be stale.
+ * Also, since many of these rpcs are
+ * non-idempotent, a conservative timeout is desired.
+ * getattr, lookup - A+2D
+ * read, write - A+4D
+ * other - nm_timeo
+ */
+#define NFS_RTO(n, t) \
+ ((t) == 0 ? (n)->nm_timeo : \
+ ((t) < 3 ? \
+ (((((n)->nm_srtt[t-1] + 3) >> 2) + (n)->nm_sdrtt[t-1] + 1) >> 1) : \
+ ((((n)->nm_srtt[t-1] + 7) >> 3) + (n)->nm_sdrtt[t-1] + 1)))
+#define NFS_SRTT(r) (r)->r_nmp->nm_srtt[proct[(r)->r_procnum] - 1]
+#define NFS_SDRTT(r) (r)->r_nmp->nm_sdrtt[proct[(r)->r_procnum] - 1]
+/*
+ * External data, mostly RPC constants in XDR form
+ */
+extern u_long rpc_reply, rpc_msgdenied, rpc_mismatch, rpc_vers, rpc_auth_unix,
+ rpc_msgaccepted, rpc_call, rpc_autherr, rpc_rejectedcred,
+ rpc_auth_kerb;
+extern u_long nfs_prog, nfs_vers, nqnfs_prog, nqnfs_vers;
+extern time_t nqnfsstarttime;
+extern int nonidempotent[NFS_NPROCS];
+
+/*
+ * Maps errno values to nfs error numbers.
+ * Use NFSERR_IO as the catch all for ones not specifically defined in
+ * RFC 1094.
+ */
+static int nfsrv_errmap[ELAST] = {
+ NFSERR_PERM, NFSERR_NOENT, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_NXIO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_ACCES, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_EXIST, NFSERR_IO, NFSERR_NODEV, NFSERR_NOTDIR,
+ NFSERR_ISDIR, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_FBIG, NFSERR_NOSPC, NFSERR_IO, NFSERR_ROFS,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_NAMETOL, NFSERR_IO, NFSERR_IO,
+ NFSERR_NOTEMPTY, NFSERR_IO, NFSERR_IO, NFSERR_DQUOT, NFSERR_STALE,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO,
+};
+
+/*
+ * Defines which timer to use for the procnum.
+ * 0 - default
+ * 1 - getattr
+ * 2 - lookup
+ * 3 - read
+ * 4 - write
+ */
+static int proct[NFS_NPROCS] = {
+ 0, 1, 0, 0, 2, 3, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0,
+};
+
+/*
+ * There is a congestion window for outstanding rpcs maintained per mount
+ * point. The cwnd size is adjusted in roughly the way that:
+ * Van Jacobson, Congestion avoidance and Control, In "Proceedings of
+ * SIGCOMM '88". ACM, August 1988.
+ * describes for TCP. The cwnd size is chopped in half on a retransmit timeout
+ * and incremented by 1/cwnd when each rpc reply is received and a full cwnd
+ * of rpcs is in progress.
+ * (The sent count and cwnd are scaled for integer arith.)
+ * Variants of "slow start" were tried and were found to be too much of a
+ * performance hit (ave. rtt 3 times larger),
+ * I suspect due to the large rtt that nfs rpcs have.
+ */
+#define NFS_CWNDSCALE 256
+#define NFS_MAXCWND (NFS_CWNDSCALE * 32)
+static int nfs_backoff[8] = { 2, 4, 8, 16, 32, 64, 128, 256, };
+int nfs_sbwait();
+void nfs_disconnect(), nfs_realign(), nfsrv_wakenfsd(), nfs_sndunlock();
+void nfs_rcvunlock(), nqnfs_serverd(), nqnfs_clientlease();
+struct mbuf *nfsm_rpchead();
+int nfsrtton = 0;
+struct nfsrtt nfsrtt;
+struct nfsd nfsd_head;
+
+int nfsrv_null(),
+ nfsrv_getattr(),
+ nfsrv_setattr(),
+ nfsrv_lookup(),
+ nfsrv_readlink(),
+ nfsrv_read(),
+ nfsrv_write(),
+ nfsrv_create(),
+ nfsrv_remove(),
+ nfsrv_rename(),
+ nfsrv_link(),
+ nfsrv_symlink(),
+ nfsrv_mkdir(),
+ nfsrv_rmdir(),
+ nfsrv_readdir(),
+ nfsrv_statfs(),
+ nfsrv_noop(),
+ nqnfsrv_readdirlook(),
+ nqnfsrv_getlease(),
+ nqnfsrv_vacated(),
+ nqnfsrv_access();
+
+int (*nfsrv_procs[NFS_NPROCS])() = {
+ nfsrv_null,
+ nfsrv_getattr,
+ nfsrv_setattr,
+ nfsrv_noop,
+ nfsrv_lookup,
+ nfsrv_readlink,
+ nfsrv_read,
+ nfsrv_noop,
+ nfsrv_write,
+ nfsrv_create,
+ nfsrv_remove,
+ nfsrv_rename,
+ nfsrv_link,
+ nfsrv_symlink,
+ nfsrv_mkdir,
+ nfsrv_rmdir,
+ nfsrv_readdir,
+ nfsrv_statfs,
+ nqnfsrv_readdirlook,
+ nqnfsrv_getlease,
+ nqnfsrv_vacated,
+ nfsrv_noop,
+ nqnfsrv_access,
+};
+
+struct nfsreq nfsreqh;
+
+/*
+ * Initialize sockets and congestion for a new NFS connection.
+ * We do not free the sockaddr if error.
+ */
+int
+nfs_connect(nmp, rep)
+ register struct nfsmount *nmp;
+ struct nfsreq *rep;
+{
+ register struct socket *so;
+ int s, error, rcvreserve, sndreserve;
+ struct sockaddr *saddr;
+ struct sockaddr_in *sin;
+ struct mbuf *m;
+ u_short tport;
+
+ nmp->nm_so = (struct socket *)0;
+ saddr = mtod(nmp->nm_nam, struct sockaddr *);
+ if (error = socreate(saddr->sa_family,
+ &nmp->nm_so, nmp->nm_sotype, nmp->nm_soproto))
+ goto bad;
+ so = nmp->nm_so;
+ nmp->nm_soflags = so->so_proto->pr_flags;
+
+ /*
+ * Some servers require that the client port be a reserved port number.
+ */
+ if (saddr->sa_family == AF_INET && (nmp->nm_flag & NFSMNT_RESVPORT)) {
+ MGET(m, M_WAIT, MT_SONAME);
+ sin = mtod(m, struct sockaddr_in *);
+ sin->sin_len = m->m_len = sizeof (struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = INADDR_ANY;
+ tport = IPPORT_RESERVED - 1;
+ sin->sin_port = htons(tport);
+ while ((error = sobind(so, m)) == EADDRINUSE &&
+ --tport > IPPORT_RESERVED / 2)
+ sin->sin_port = htons(tport);
+ m_freem(m);
+ if (error)
+ goto bad;
+ }
+
+ /*
+ * Protocols that do not require connections may be optionally left
+ * unconnected for servers that reply from a port other than NFS_PORT.
+ */
+ if (nmp->nm_flag & NFSMNT_NOCONN) {
+ if (nmp->nm_soflags & PR_CONNREQUIRED) {
+ error = ENOTCONN;
+ goto bad;
+ }
+ } else {
+ if (error = soconnect(so, nmp->nm_nam))
+ goto bad;
+
+ /*
+ * Wait for the connection to complete. Cribbed from the
+ * connect system call but with the wait timing out so
+ * that interruptible mounts don't hang here for a long time.
+ */
+ s = splnet();
+ while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
+ (void) tsleep((caddr_t)&so->so_timeo, PSOCK,
+ "nfscon", 2 * hz);
+ if ((so->so_state & SS_ISCONNECTING) &&
+ so->so_error == 0 && rep &&
+ (error = nfs_sigintr(nmp, rep, rep->r_procp))) {
+ so->so_state &= ~SS_ISCONNECTING;
+ splx(s);
+ goto bad;
+ }
+ }
+ if (so->so_error) {
+ error = so->so_error;
+ so->so_error = 0;
+ splx(s);
+ goto bad;
+ }
+ splx(s);
+ }
+ if (nmp->nm_flag & (NFSMNT_SOFT | NFSMNT_INT)) {
+ so->so_rcv.sb_timeo = (5 * hz);
+ so->so_snd.sb_timeo = (5 * hz);
+ } else {
+ so->so_rcv.sb_timeo = 0;
+ so->so_snd.sb_timeo = 0;
+ }
+ if (nmp->nm_sotype == SOCK_DGRAM) {
+ sndreserve = nmp->nm_wsize + NFS_MAXPKTHDR;
+ rcvreserve = nmp->nm_rsize + NFS_MAXPKTHDR;
+ } else if (nmp->nm_sotype == SOCK_SEQPACKET) {
+ sndreserve = (nmp->nm_wsize + NFS_MAXPKTHDR) * 2;
+ rcvreserve = (nmp->nm_rsize + NFS_MAXPKTHDR) * 2;
+ } else {
+ if (nmp->nm_sotype != SOCK_STREAM)
+ panic("nfscon sotype");
+ if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
+ MGET(m, M_WAIT, MT_SOOPTS);
+ *mtod(m, int *) = 1;
+ m->m_len = sizeof(int);
+ sosetopt(so, SOL_SOCKET, SO_KEEPALIVE, m);
+ }
+ if (so->so_proto->pr_protocol == IPPROTO_TCP) {
+ MGET(m, M_WAIT, MT_SOOPTS);
+ *mtod(m, int *) = 1;
+ m->m_len = sizeof(int);
+ sosetopt(so, IPPROTO_TCP, TCP_NODELAY, m);
+ }
+ sndreserve = (nmp->nm_wsize + NFS_MAXPKTHDR + sizeof (u_long))
+ * 2;
+ rcvreserve = (nmp->nm_rsize + NFS_MAXPKTHDR + sizeof (u_long))
+ * 2;
+ }
+ if (error = soreserve(so, sndreserve, rcvreserve))
+ goto bad;
+ so->so_rcv.sb_flags |= SB_NOINTR;
+ so->so_snd.sb_flags |= SB_NOINTR;
+
+ /* Initialize other non-zero congestion variables */
+ nmp->nm_srtt[0] = nmp->nm_srtt[1] = nmp->nm_srtt[2] = nmp->nm_srtt[3] =
+ nmp->nm_srtt[4] = (NFS_TIMEO << 3);
+ nmp->nm_sdrtt[0] = nmp->nm_sdrtt[1] = nmp->nm_sdrtt[2] =
+ nmp->nm_sdrtt[3] = nmp->nm_sdrtt[4] = 0;
+ nmp->nm_cwnd = NFS_MAXCWND / 2; /* Initial send window */
+ nmp->nm_sent = 0;
+ nmp->nm_timeouts = 0;
+ return (0);
+
+bad:
+ nfs_disconnect(nmp);
+ return (error);
+}
+
+/*
+ * Reconnect routine:
+ * Called when a connection is broken on a reliable protocol.
+ * - clean up the old socket
+ * - nfs_connect() again
+ * - set R_MUSTRESEND for all outstanding requests on mount point
+ * If this fails the mount point is DEAD!
+ * nb: Must be called with the nfs_sndlock() set on the mount point.
+ */
+int
+nfs_reconnect(rep)
+ register struct nfsreq *rep;
+{
+ register struct nfsreq *rp;
+ register struct nfsmount *nmp = rep->r_nmp;
+ int error;
+
+ nfs_disconnect(nmp);
+ while (error = nfs_connect(nmp, rep)) {
+ if (error == EINTR || error == ERESTART)
+ return (EINTR);
+ (void) tsleep((caddr_t)&lbolt, PSOCK, "nfscon", 0);
+ }
+
+ /*
+ * Loop through outstanding request list and fix up all requests
+ * on old socket.
+ */
+ rp = nfsreqh.r_next;
+ while (rp != &nfsreqh) {
+ if (rp->r_nmp == nmp)
+ rp->r_flags |= R_MUSTRESEND;
+ rp = rp->r_next;
+ }
+ return (0);
+}
+
+/*
+ * NFS disconnect. Clean up and unlink.
+ */
+void
+nfs_disconnect(nmp)
+ register struct nfsmount *nmp;
+{
+ register struct socket *so;
+
+ if (nmp->nm_so) {
+ so = nmp->nm_so;
+ nmp->nm_so = (struct socket *)0;
+ soshutdown(so, 2);
+ soclose(so);
+ }
+}
+
+/*
+ * This is the nfs send routine. For connection based socket types, it
+ * must be called with an nfs_sndlock() on the socket.
+ * "rep == NULL" indicates that it has been called from a server.
+ * For the client side:
+ * - return EINTR if the RPC is terminated, 0 otherwise
+ * - set R_MUSTRESEND if the send fails for any reason
+ * - do any cleanup required by recoverable socket errors (???)
+ * For the server side:
+ * - return EINTR or ERESTART if interrupted by a signal
+ * - return EPIPE if a connection is lost for connection based sockets (TCP...)
+ * - do any cleanup required by recoverable socket errors (???)
+ */
+int
+nfs_send(so, nam, top, rep)
+ register struct socket *so;
+ struct mbuf *nam;
+ register struct mbuf *top;
+ struct nfsreq *rep;
+{
+ struct mbuf *sendnam;
+ int error, soflags, flags;
+
+ if (rep) {
+ if (rep->r_flags & R_SOFTTERM) {
+ m_freem(top);
+ return (EINTR);
+ }
+ if ((so = rep->r_nmp->nm_so) == NULL) {
+ rep->r_flags |= R_MUSTRESEND;
+ m_freem(top);
+ return (0);
+ }
+ rep->r_flags &= ~R_MUSTRESEND;
+ soflags = rep->r_nmp->nm_soflags;
+ } else
+ soflags = so->so_proto->pr_flags;
+ if ((soflags & PR_CONNREQUIRED) || (so->so_state & SS_ISCONNECTED))
+ sendnam = (struct mbuf *)0;
+ else
+ sendnam = nam;
+ if (so->so_type == SOCK_SEQPACKET)
+ flags = MSG_EOR;
+ else
+ flags = 0;
+
+ error = sosend(so, sendnam, (struct uio *)0, top,
+ (struct mbuf *)0, flags);
+ if (error) {
+ if (rep) {
+ log(LOG_INFO, "nfs send error %d for server %s\n",error,
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ /*
+ * Deal with errors for the client side.
+ */
+ if (rep->r_flags & R_SOFTTERM)
+ error = EINTR;
+ else
+ rep->r_flags |= R_MUSTRESEND;
+ } else
+ log(LOG_INFO, "nfsd send error %d\n", error);
+
+ /*
+ * Handle any recoverable (soft) socket errors here. (???)
+ */
+ if (error != EINTR && error != ERESTART &&
+ error != EWOULDBLOCK && error != EPIPE)
+ error = 0;
+ }
+ return (error);
+}
+
+/*
+ * Receive a Sun RPC Request/Reply. For SOCK_DGRAM, the work is all
+ * done by soreceive(), but for SOCK_STREAM we must deal with the Record
+ * Mark and consolidate the data into a new mbuf list.
+ * nb: Sometimes TCP passes the data up to soreceive() in long lists of
+ * small mbufs.
+ * For SOCK_STREAM we must be very careful to read an entire record once
+ * we have read any of it, even if the system call has been interrupted.
+ */
+int
+nfs_receive(rep, aname, mp)
+ register struct nfsreq *rep;
+ struct mbuf **aname;
+ struct mbuf **mp;
+{
+ register struct socket *so;
+ struct uio auio;
+ struct iovec aio;
+ register struct mbuf *m;
+ struct mbuf *control;
+ u_long len;
+ struct mbuf **getnam;
+ int error, sotype, rcvflg;
+ struct proc *p = curproc; /* XXX */
+
+ /*
+ * Set up arguments for soreceive()
+ */
+ *mp = (struct mbuf *)0;
+ *aname = (struct mbuf *)0;
+ sotype = rep->r_nmp->nm_sotype;
+
+ /*
+ * For reliable protocols, lock against other senders/receivers
+ * in case a reconnect is necessary.
+ * For SOCK_STREAM, first get the Record Mark to find out how much
+ * more there is to get.
+ * We must lock the socket against other receivers
+ * until we have an entire rpc request/reply.
+ */
+ if (sotype != SOCK_DGRAM) {
+ if (error = nfs_sndlock(&rep->r_nmp->nm_flag, rep))
+ return (error);
+tryagain:
+ /*
+ * Check for fatal errors and resending request.
+ */
+ /*
+ * Ugh: If a reconnect attempt just happened, nm_so
+ * would have changed. NULL indicates a failed
+ * attempt that has essentially shut down this
+ * mount point.
+ */
+ if (rep->r_mrep || (rep->r_flags & R_SOFTTERM)) {
+ nfs_sndunlock(&rep->r_nmp->nm_flag);
+ return (EINTR);
+ }
+ if ((so = rep->r_nmp->nm_so) == NULL) {
+ if (error = nfs_reconnect(rep)) {
+ nfs_sndunlock(&rep->r_nmp->nm_flag);
+ return (error);
+ }
+ goto tryagain;
+ }
+ while (rep->r_flags & R_MUSTRESEND) {
+ m = m_copym(rep->r_mreq, 0, M_COPYALL, M_WAIT);
+ nfsstats.rpcretries++;
+ if (error = nfs_send(so, rep->r_nmp->nm_nam, m, rep)) {
+ if (error == EINTR || error == ERESTART ||
+ (error = nfs_reconnect(rep))) {
+ nfs_sndunlock(&rep->r_nmp->nm_flag);
+ return (error);
+ }
+ goto tryagain;
+ }
+ }
+ nfs_sndunlock(&rep->r_nmp->nm_flag);
+ if (sotype == SOCK_STREAM) {
+ aio.iov_base = (caddr_t) &len;
+ aio.iov_len = sizeof(u_long);
+ auio.uio_iov = &aio;
+ auio.uio_iovcnt = 1;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_READ;
+ auio.uio_offset = 0;
+ auio.uio_resid = sizeof(u_long);
+ auio.uio_procp = p;
+ do {
+ rcvflg = MSG_WAITALL;
+ error = soreceive(so, (struct mbuf **)0, &auio,
+ (struct mbuf **)0, (struct mbuf **)0, &rcvflg);
+ if (error == EWOULDBLOCK && rep) {
+ if (rep->r_flags & R_SOFTTERM)
+ return (EINTR);
+ }
+ } while (error == EWOULDBLOCK);
+ if (!error && auio.uio_resid > 0) {
+ log(LOG_INFO,
+ "short receive (%d/%d) from nfs server %s\n",
+ sizeof(u_long) - auio.uio_resid,
+ sizeof(u_long),
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ error = EPIPE;
+ }
+ if (error)
+ goto errout;
+ len = ntohl(len) & ~0x80000000;
+ /*
+ * This is SERIOUS! We are out of sync with the sender
+ * and forcing a disconnect/reconnect is all I can do.
+ */
+ if (len > NFS_MAXPACKET) {
+ log(LOG_ERR, "%s (%d) from nfs server %s\n",
+ "impossible packet length",
+ len,
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ error = EFBIG;
+ goto errout;
+ }
+ auio.uio_resid = len;
+ do {
+ rcvflg = MSG_WAITALL;
+ error = soreceive(so, (struct mbuf **)0,
+ &auio, mp, (struct mbuf **)0, &rcvflg);
+ } while (error == EWOULDBLOCK || error == EINTR ||
+ error == ERESTART);
+ if (!error && auio.uio_resid > 0) {
+ log(LOG_INFO,
+ "short receive (%d/%d) from nfs server %s\n",
+ len - auio.uio_resid, len,
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ error = EPIPE;
+ }
+ } else {
+ /*
+ * NB: Since uio_resid is big, MSG_WAITALL is ignored
+ * and soreceive() will return when it has either a
+ * control msg or a data msg.
+ * We have no use for control msg., but must grab them
+ * and then throw them away so we know what is going
+ * on.
+ */
+ auio.uio_resid = len = 100000000; /* Anything Big */
+ auio.uio_procp = p;
+ do {
+ rcvflg = 0;
+ error = soreceive(so, (struct mbuf **)0,
+ &auio, mp, &control, &rcvflg);
+ if (control)
+ m_freem(control);
+ if (error == EWOULDBLOCK && rep) {
+ if (rep->r_flags & R_SOFTTERM)
+ return (EINTR);
+ }
+ } while (error == EWOULDBLOCK ||
+ (!error && *mp == NULL && control));
+ if ((rcvflg & MSG_EOR) == 0)
+ printf("Egad!!\n");
+ if (!error && *mp == NULL)
+ error = EPIPE;
+ len -= auio.uio_resid;
+ }
+errout:
+ if (error && error != EINTR && error != ERESTART) {
+ m_freem(*mp);
+ *mp = (struct mbuf *)0;
+ if (error != EPIPE)
+ log(LOG_INFO,
+ "receive error %d from nfs server %s\n",
+ error,
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ error = nfs_sndlock(&rep->r_nmp->nm_flag, rep);
+ if (!error)
+ error = nfs_reconnect(rep);
+ if (!error)
+ goto tryagain;
+ }
+ } else {
+ if ((so = rep->r_nmp->nm_so) == NULL)
+ return (EACCES);
+ if (so->so_state & SS_ISCONNECTED)
+ getnam = (struct mbuf **)0;
+ else
+ getnam = aname;
+ auio.uio_resid = len = 1000000;
+ auio.uio_procp = p;
+ do {
+ rcvflg = 0;
+ error = soreceive(so, getnam, &auio, mp,
+ (struct mbuf **)0, &rcvflg);
+ if (error == EWOULDBLOCK &&
+ (rep->r_flags & R_SOFTTERM))
+ return (EINTR);
+ } while (error == EWOULDBLOCK);
+ len -= auio.uio_resid;
+ }
+ if (error) {
+ m_freem(*mp);
+ *mp = (struct mbuf *)0;
+ }
+ /*
+ * Search for any mbufs that are not a multiple of 4 bytes long
+ * or with m_data not longword aligned.
+ * These could cause pointer alignment problems, so copy them to
+ * well aligned mbufs.
+ */
+ nfs_realign(*mp, 5 * NFSX_UNSIGNED);
+ return (error);
+}
+
+/*
+ * Implement receipt of reply on a socket.
+ * We must search through the list of received datagrams matching them
+ * with outstanding requests using the xid, until ours is found.
+ */
+/* ARGSUSED */
+int
+nfs_reply(myrep)
+ struct nfsreq *myrep;
+{
+ register struct nfsreq *rep;
+ register struct nfsmount *nmp = myrep->r_nmp;
+ register long t1;
+ struct mbuf *mrep, *nam, *md;
+ u_long rxid, *tl;
+ caddr_t dpos, cp2;
+ int error;
+
+ /*
+ * Loop around until we get our own reply
+ */
+ for (;;) {
+ /*
+ * Lock against other receivers so that I don't get stuck in
+ * sbwait() after someone else has received my reply for me.
+ * Also necessary for connection based protocols to avoid
+ * race conditions during a reconnect.
+ */
+ if (error = nfs_rcvlock(myrep))
+ return (error);
+ /* Already received, bye bye */
+ if (myrep->r_mrep != NULL) {
+ nfs_rcvunlock(&nmp->nm_flag);
+ return (0);
+ }
+ /*
+ * Get the next Rpc reply off the socket
+ */
+ error = nfs_receive(myrep, &nam, &mrep);
+ nfs_rcvunlock(&nmp->nm_flag);
+ if (error) {
+
+ /*
+ * Ignore routing errors on connectionless protocols??
+ */
+ if (NFSIGNORE_SOERROR(nmp->nm_soflags, error)) {
+ nmp->nm_so->so_error = 0;
+ if (myrep->r_flags & R_GETONEREP)
+ return (0);
+ continue;
+ }
+ return (error);
+ }
+ if (nam)
+ m_freem(nam);
+
+ /*
+ * Get the xid and check that it is an rpc reply
+ */
+ md = mrep;
+ dpos = mtod(md, caddr_t);
+ nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
+ rxid = *tl++;
+ if (*tl != rpc_reply) {
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ if (nqnfs_callback(nmp, mrep, md, dpos))
+ nfsstats.rpcinvalid++;
+ } else {
+ nfsstats.rpcinvalid++;
+ m_freem(mrep);
+ }
+nfsmout:
+ if (myrep->r_flags & R_GETONEREP)
+ return (0);
+ continue;
+ }
+
+ /*
+ * Loop through the request list to match up the reply
+ * Iff no match, just drop the datagram
+ */
+ rep = nfsreqh.r_next;
+ while (rep != &nfsreqh) {
+ if (rep->r_mrep == NULL && rxid == rep->r_xid) {
+ /* Found it.. */
+ rep->r_mrep = mrep;
+ rep->r_md = md;
+ rep->r_dpos = dpos;
+ if (nfsrtton) {
+ struct rttl *rt;
+
+ rt = &nfsrtt.rttl[nfsrtt.pos];
+ rt->proc = rep->r_procnum;
+ rt->rto = NFS_RTO(nmp, proct[rep->r_procnum]);
+ rt->sent = nmp->nm_sent;
+ rt->cwnd = nmp->nm_cwnd;
+ rt->srtt = nmp->nm_srtt[proct[rep->r_procnum] - 1];
+ rt->sdrtt = nmp->nm_sdrtt[proct[rep->r_procnum] - 1];
+ rt->fsid = nmp->nm_mountp->mnt_stat.f_fsid;
+ rt->tstamp = time;
+ if (rep->r_flags & R_TIMING)
+ rt->rtt = rep->r_rtt;
+ else
+ rt->rtt = 1000000;
+ nfsrtt.pos = (nfsrtt.pos + 1) % NFSRTTLOGSIZ;
+ }
+ /*
+ * Update congestion window.
+ * Do the additive increase of
+ * one rpc/rtt.
+ */
+ if (nmp->nm_cwnd <= nmp->nm_sent) {
+ nmp->nm_cwnd +=
+ (NFS_CWNDSCALE * NFS_CWNDSCALE +
+ (nmp->nm_cwnd >> 1)) / nmp->nm_cwnd;
+ if (nmp->nm_cwnd > NFS_MAXCWND)
+ nmp->nm_cwnd = NFS_MAXCWND;
+ }
+ rep->r_flags &= ~R_SENT;
+ nmp->nm_sent -= NFS_CWNDSCALE;
+ /*
+ * Update rtt using a gain of 0.125 on the mean
+ * and a gain of 0.25 on the deviation.
+ */
+ if (rep->r_flags & R_TIMING) {
+ /*
+ * Since the timer resolution of
+ * NFS_HZ is so course, it can often
+ * result in r_rtt == 0. Since
+ * r_rtt == N means that the actual
+ * rtt is between N+dt and N+2-dt ticks,
+ * add 1.
+ */
+ t1 = rep->r_rtt + 1;
+ t1 -= (NFS_SRTT(rep) >> 3);
+ NFS_SRTT(rep) += t1;
+ if (t1 < 0)
+ t1 = -t1;
+ t1 -= (NFS_SDRTT(rep) >> 2);
+ NFS_SDRTT(rep) += t1;
+ }
+ nmp->nm_timeouts = 0;
+ break;
+ }
+ rep = rep->r_next;
+ }
+ /*
+ * If not matched to a request, drop it.
+ * If it's mine, get out.
+ */
+ if (rep == &nfsreqh) {
+ nfsstats.rpcunexpected++;
+ m_freem(mrep);
+ } else if (rep == myrep) {
+ if (rep->r_mrep == NULL)
+ panic("nfsreply nil");
+ return (0);
+ }
+ if (myrep->r_flags & R_GETONEREP)
+ return (0);
+ }
+}
+
+/*
+ * nfs_request - goes something like this
+ * - fill in request struct
+ * - links it into list
+ * - calls nfs_send() for first transmit
+ * - calls nfs_receive() to get reply
+ * - break down rpc header and return with nfs reply pointed to
+ * by mrep or error
+ * nb: always frees up mreq mbuf list
+ */
+int
+nfs_request(vp, mrest, procnum, procp, cred, mrp, mdp, dposp)
+ struct vnode *vp;
+ struct mbuf *mrest;
+ int procnum;
+ struct proc *procp;
+ struct ucred *cred;
+ struct mbuf **mrp;
+ struct mbuf **mdp;
+ caddr_t *dposp;
+{
+ register struct mbuf *m, *mrep;
+ register struct nfsreq *rep;
+ register u_long *tl;
+ register int i;
+ struct nfsmount *nmp;
+ struct mbuf *md, *mheadend;
+ struct nfsreq *reph;
+ struct nfsnode *np;
+ time_t reqtime, waituntil;
+ caddr_t dpos, cp2;
+ int t1, nqlflag, cachable, s, error = 0, mrest_len, auth_len, auth_type;
+ int trylater_delay = NQ_TRYLATERDEL, trylater_cnt = 0, failed_auth = 0;
+ u_long xid;
+ u_quad_t frev;
+ char *auth_str;
+
+ nmp = VFSTONFS(vp->v_mount);
+ MALLOC(rep, struct nfsreq *, sizeof(struct nfsreq), M_NFSREQ, M_WAITOK);
+ rep->r_nmp = nmp;
+ rep->r_vp = vp;
+ rep->r_procp = procp;
+ rep->r_procnum = procnum;
+ i = 0;
+ m = mrest;
+ while (m) {
+ i += m->m_len;
+ m = m->m_next;
+ }
+ mrest_len = i;
+
+ /*
+ * Get the RPC header with authorization.
+ */
+kerbauth:
+ auth_str = (char *)0;
+ if (nmp->nm_flag & NFSMNT_KERB) {
+ if (failed_auth) {
+ error = nfs_getauth(nmp, rep, cred, &auth_type,
+ &auth_str, &auth_len);
+ if (error) {
+ free((caddr_t)rep, M_NFSREQ);
+ m_freem(mrest);
+ return (error);
+ }
+ } else {
+ auth_type = RPCAUTH_UNIX;
+ auth_len = 5 * NFSX_UNSIGNED;
+ }
+ } else {
+ auth_type = RPCAUTH_UNIX;
+ if (cred->cr_ngroups < 1)
+ panic("nfsreq nogrps");
+ auth_len = ((((cred->cr_ngroups - 1) > nmp->nm_numgrps) ?
+ nmp->nm_numgrps : (cred->cr_ngroups - 1)) << 2) +
+ 5 * NFSX_UNSIGNED;
+ }
+ m = nfsm_rpchead(cred, (nmp->nm_flag & NFSMNT_NQNFS), procnum,
+ auth_type, auth_len, auth_str, mrest, mrest_len, &mheadend, &xid);
+ if (auth_str)
+ free(auth_str, M_TEMP);
+
+ /*
+ * For stream protocols, insert a Sun RPC Record Mark.
+ */
+ if (nmp->nm_sotype == SOCK_STREAM) {
+ M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
+ *mtod(m, u_long *) = htonl(0x80000000 |
+ (m->m_pkthdr.len - NFSX_UNSIGNED));
+ }
+ rep->r_mreq = m;
+ rep->r_xid = xid;
+tryagain:
+ if (nmp->nm_flag & NFSMNT_SOFT)
+ rep->r_retry = nmp->nm_retry;
+ else
+ rep->r_retry = NFS_MAXREXMIT + 1; /* past clip limit */
+ rep->r_rtt = rep->r_rexmit = 0;
+ if (proct[procnum] > 0)
+ rep->r_flags = R_TIMING;
+ else
+ rep->r_flags = 0;
+ rep->r_mrep = NULL;
+
+ /*
+ * Do the client side RPC.
+ */
+ nfsstats.rpcrequests++;
+ /*
+ * Chain request into list of outstanding requests. Be sure
+ * to put it LAST so timer finds oldest requests first.
+ */
+ s = splsoftclock();
+ reph = &nfsreqh;
+ reph->r_prev->r_next = rep;
+ rep->r_prev = reph->r_prev;
+ reph->r_prev = rep;
+ rep->r_next = reph;
+
+ /* Get send time for nqnfs */
+ reqtime = time.tv_sec;
+
+ /*
+ * If backing off another request or avoiding congestion, don't
+ * send this one now but let timer do it. If not timing a request,
+ * do it now.
+ */
+ if (nmp->nm_so && (nmp->nm_sotype != SOCK_DGRAM ||
+ (nmp->nm_flag & NFSMNT_DUMBTIMR) ||
+ nmp->nm_sent < nmp->nm_cwnd)) {
+ splx(s);
+ if (nmp->nm_soflags & PR_CONNREQUIRED)
+ error = nfs_sndlock(&nmp->nm_flag, rep);
+ if (!error) {
+ m = m_copym(m, 0, M_COPYALL, M_WAIT);
+ error = nfs_send(nmp->nm_so, nmp->nm_nam, m, rep);
+ if (nmp->nm_soflags & PR_CONNREQUIRED)
+ nfs_sndunlock(&nmp->nm_flag);
+ }
+ if (!error && (rep->r_flags & R_MUSTRESEND) == 0) {
+ nmp->nm_sent += NFS_CWNDSCALE;
+ rep->r_flags |= R_SENT;
+ }
+ } else {
+ splx(s);
+ rep->r_rtt = -1;
+ }
+
+ /*
+ * Wait for the reply from our send or the timer's.
+ */
+ if (!error || error == EPIPE)
+ error = nfs_reply(rep);
+
+ /*
+ * RPC done, unlink the request.
+ */
+ s = splsoftclock();
+ rep->r_prev->r_next = rep->r_next;
+ rep->r_next->r_prev = rep->r_prev;
+ splx(s);
+
+ /*
+ * Decrement the outstanding request count.
+ */
+ if (rep->r_flags & R_SENT) {
+ rep->r_flags &= ~R_SENT; /* paranoia */
+ nmp->nm_sent -= NFS_CWNDSCALE;
+ }
+
+ /*
+ * If there was a successful reply and a tprintf msg.
+ * tprintf a response.
+ */
+ if (!error && (rep->r_flags & R_TPRINTFMSG))
+ nfs_msg(rep->r_procp, nmp->nm_mountp->mnt_stat.f_mntfromname,
+ "is alive again");
+ mrep = rep->r_mrep;
+ md = rep->r_md;
+ dpos = rep->r_dpos;
+ if (error) {
+ m_freem(rep->r_mreq);
+ free((caddr_t)rep, M_NFSREQ);
+ return (error);
+ }
+
+ /*
+ * break down the rpc header and check if ok
+ */
+ nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED);
+ if (*tl++ == rpc_msgdenied) {
+ if (*tl == rpc_mismatch)
+ error = EOPNOTSUPP;
+ else if ((nmp->nm_flag & NFSMNT_KERB) && *tl++ == rpc_autherr) {
+ if (*tl == rpc_rejectedcred && failed_auth == 0) {
+ failed_auth++;
+ mheadend->m_next = (struct mbuf *)0;
+ m_freem(mrep);
+ m_freem(rep->r_mreq);
+ goto kerbauth;
+ } else
+ error = EAUTH;
+ } else
+ error = EACCES;
+ m_freem(mrep);
+ m_freem(rep->r_mreq);
+ free((caddr_t)rep, M_NFSREQ);
+ return (error);
+ }
+
+ /*
+ * skip over the auth_verf, someday we may want to cache auth_short's
+ * for nfs_reqhead(), but for now just dump it
+ */
+ if (*++tl != 0) {
+ i = nfsm_rndup(fxdr_unsigned(long, *tl));
+ nfsm_adv(i);
+ }
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ /* 0 == ok */
+ if (*tl == 0) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ if (*tl != 0) {
+ error = fxdr_unsigned(int, *tl);
+ m_freem(mrep);
+ if ((nmp->nm_flag & NFSMNT_NQNFS) &&
+ error == NQNFS_TRYLATER) {
+ error = 0;
+ waituntil = time.tv_sec + trylater_delay;
+ while (time.tv_sec < waituntil)
+ (void) tsleep((caddr_t)&lbolt,
+ PSOCK, "nqnfstry", 0);
+ trylater_delay *= nfs_backoff[trylater_cnt];
+ if (trylater_cnt < 7)
+ trylater_cnt++;
+ goto tryagain;
+ }
+
+ /*
+ * If the File Handle was stale, invalidate the
+ * lookup cache, just in case.
+ */
+ if (error == ESTALE)
+ cache_purge(vp);
+ m_freem(rep->r_mreq);
+ free((caddr_t)rep, M_NFSREQ);
+ return (error);
+ }
+
+ /*
+ * For nqnfs, get any lease in reply
+ */
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ if (*tl) {
+ np = VTONFS(vp);
+ nqlflag = fxdr_unsigned(int, *tl);
+ nfsm_dissect(tl, u_long *, 4*NFSX_UNSIGNED);
+ cachable = fxdr_unsigned(int, *tl++);
+ reqtime += fxdr_unsigned(int, *tl++);
+ if (reqtime > time.tv_sec) {
+ fxdr_hyper(tl, &frev);
+ nqnfs_clientlease(nmp, np, nqlflag,
+ cachable, reqtime, frev);
+ }
+ }
+ }
+ *mrp = mrep;
+ *mdp = md;
+ *dposp = dpos;
+ m_freem(rep->r_mreq);
+ FREE((caddr_t)rep, M_NFSREQ);
+ return (0);
+ }
+ m_freem(mrep);
+ m_freem(rep->r_mreq);
+ free((caddr_t)rep, M_NFSREQ);
+ error = EPROTONOSUPPORT;
+nfsmout:
+ return (error);
+}
+
+/*
+ * Generate the rpc reply header
+ * siz arg. is used to decide if adding a cluster is worthwhile
+ */
+int
+nfs_rephead(siz, nd, err, cache, frev, mrq, mbp, bposp)
+ int siz;
+ struct nfsd *nd;
+ int err;
+ int cache;
+ u_quad_t *frev;
+ struct mbuf **mrq;
+ struct mbuf **mbp;
+ caddr_t *bposp;
+{
+ register u_long *tl;
+ register struct mbuf *mreq;
+ caddr_t bpos;
+ struct mbuf *mb, *mb2;
+
+ MGETHDR(mreq, M_WAIT, MT_DATA);
+ mb = mreq;
+ /*
+ * If this is a big reply, use a cluster else
+ * try and leave leading space for the lower level headers.
+ */
+ siz += RPC_REPLYSIZ;
+ if (siz >= MINCLSIZE) {
+ MCLGET(mreq, M_WAIT);
+ } else
+ mreq->m_data += max_hdr;
+ tl = mtod(mreq, u_long *);
+ mreq->m_len = 6*NFSX_UNSIGNED;
+ bpos = ((caddr_t)tl)+mreq->m_len;
+ *tl++ = nd->nd_retxid;
+ *tl++ = rpc_reply;
+ if (err == ERPCMISMATCH || err == NQNFS_AUTHERR) {
+ *tl++ = rpc_msgdenied;
+ if (err == NQNFS_AUTHERR) {
+ *tl++ = rpc_autherr;
+ *tl = rpc_rejectedcred;
+ mreq->m_len -= NFSX_UNSIGNED;
+ bpos -= NFSX_UNSIGNED;
+ } else {
+ *tl++ = rpc_mismatch;
+ *tl++ = txdr_unsigned(2);
+ *tl = txdr_unsigned(2);
+ }
+ } else {
+ *tl++ = rpc_msgaccepted;
+ *tl++ = 0;
+ *tl++ = 0;
+ switch (err) {
+ case EPROGUNAVAIL:
+ *tl = txdr_unsigned(RPC_PROGUNAVAIL);
+ break;
+ case EPROGMISMATCH:
+ *tl = txdr_unsigned(RPC_PROGMISMATCH);
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(2);
+ *tl = txdr_unsigned(2); /* someday 3 */
+ break;
+ case EPROCUNAVAIL:
+ *tl = txdr_unsigned(RPC_PROCUNAVAIL);
+ break;
+ default:
+ *tl = 0;
+ if (err != VNOVAL) {
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ if (err)
+ *tl = txdr_unsigned(nfsrv_errmap[err - 1]);
+ else
+ *tl = 0;
+ }
+ break;
+ };
+ }
+
+ /*
+ * For nqnfs, piggyback lease as requested.
+ */
+ if (nd->nd_nqlflag != NQL_NOVAL && err == 0) {
+ if (nd->nd_nqlflag) {
+ nfsm_build(tl, u_long *, 5*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(nd->nd_nqlflag);
+ *tl++ = txdr_unsigned(cache);
+ *tl++ = txdr_unsigned(nd->nd_duration);
+ txdr_hyper(frev, tl);
+ } else {
+ if (nd->nd_nqlflag != 0)
+ panic("nqreph");
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = 0;
+ }
+ }
+ *mrq = mreq;
+ *mbp = mb;
+ *bposp = bpos;
+ if (err != 0 && err != VNOVAL)
+ nfsstats.srvrpc_errs++;
+ return (0);
+}
+
+/*
+ * Nfs timer routine
+ * Scan the nfsreq list and retranmit any requests that have timed out
+ * To avoid retransmission attempts on STREAM sockets (in the future) make
+ * sure to set the r_retry field to 0 (implies nm_retry == 0).
+ */
+void
+nfs_timer(arg)
+ void *arg;
+{
+ register struct nfsreq *rep;
+ register struct mbuf *m;
+ register struct socket *so;
+ register struct nfsmount *nmp;
+ register int timeo;
+ static long lasttime = 0;
+ int s, error;
+
+ s = splnet();
+ for (rep = nfsreqh.r_next; rep != &nfsreqh; rep = rep->r_next) {
+ nmp = rep->r_nmp;
+ if (rep->r_mrep || (rep->r_flags & R_SOFTTERM))
+ continue;
+ if (nfs_sigintr(nmp, rep, rep->r_procp)) {
+ rep->r_flags |= R_SOFTTERM;
+ continue;
+ }
+ if (rep->r_rtt >= 0) {
+ rep->r_rtt++;
+ if (nmp->nm_flag & NFSMNT_DUMBTIMR)
+ timeo = nmp->nm_timeo;
+ else
+ timeo = NFS_RTO(nmp, proct[rep->r_procnum]);
+ if (nmp->nm_timeouts > 0)
+ timeo *= nfs_backoff[nmp->nm_timeouts - 1];
+ if (rep->r_rtt <= timeo)
+ continue;
+ if (nmp->nm_timeouts < 8)
+ nmp->nm_timeouts++;
+ }
+ /*
+ * Check for server not responding
+ */
+ if ((rep->r_flags & R_TPRINTFMSG) == 0 &&
+ rep->r_rexmit > nmp->nm_deadthresh) {
+ nfs_msg(rep->r_procp,
+ nmp->nm_mountp->mnt_stat.f_mntfromname,
+ "not responding");
+ rep->r_flags |= R_TPRINTFMSG;
+ }
+ if (rep->r_rexmit >= rep->r_retry) { /* too many */
+ nfsstats.rpctimeouts++;
+ rep->r_flags |= R_SOFTTERM;
+ continue;
+ }
+ if (nmp->nm_sotype != SOCK_DGRAM) {
+ if (++rep->r_rexmit > NFS_MAXREXMIT)
+ rep->r_rexmit = NFS_MAXREXMIT;
+ continue;
+ }
+ if ((so = nmp->nm_so) == NULL)
+ continue;
+
+ /*
+ * If there is enough space and the window allows..
+ * Resend it
+ * Set r_rtt to -1 in case we fail to send it now.
+ */
+ rep->r_rtt = -1;
+ if (sbspace(&so->so_snd) >= rep->r_mreq->m_pkthdr.len &&
+ ((nmp->nm_flag & NFSMNT_DUMBTIMR) ||
+ (rep->r_flags & R_SENT) ||
+ nmp->nm_sent < nmp->nm_cwnd) &&
+ (m = m_copym(rep->r_mreq, 0, M_COPYALL, M_DONTWAIT))){
+ if ((nmp->nm_flag & NFSMNT_NOCONN) == 0)
+ error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, m,
+ (struct mbuf *)0, (struct mbuf *)0);
+ else
+ error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, m,
+ nmp->nm_nam, (struct mbuf *)0);
+ if (error) {
+ if (NFSIGNORE_SOERROR(nmp->nm_soflags, error))
+ so->so_error = 0;
+ } else {
+ /*
+ * Iff first send, start timing
+ * else turn timing off, backoff timer
+ * and divide congestion window by 2.
+ */
+ if (rep->r_flags & R_SENT) {
+ rep->r_flags &= ~R_TIMING;
+ if (++rep->r_rexmit > NFS_MAXREXMIT)
+ rep->r_rexmit = NFS_MAXREXMIT;
+ nmp->nm_cwnd >>= 1;
+ if (nmp->nm_cwnd < NFS_CWNDSCALE)
+ nmp->nm_cwnd = NFS_CWNDSCALE;
+ nfsstats.rpcretries++;
+ } else {
+ rep->r_flags |= R_SENT;
+ nmp->nm_sent += NFS_CWNDSCALE;
+ }
+ rep->r_rtt = 0;
+ }
+ }
+ }
+
+ /*
+ * Call the nqnfs server timer once a second to handle leases.
+ */
+ if (lasttime != time.tv_sec) {
+ lasttime = time.tv_sec;
+ nqnfs_serverd();
+ }
+ splx(s);
+ timeout(nfs_timer, (void *)0, hz / NFS_HZ);
+}
+
+/*
+ * Test for a termination condition pending on the process.
+ * This is used for NFSMNT_INT mounts.
+ */
+int
+nfs_sigintr(nmp, rep, p)
+ struct nfsmount *nmp;
+ struct nfsreq *rep;
+ register struct proc *p;
+{
+
+ if (rep && (rep->r_flags & R_SOFTTERM))
+ return (EINTR);
+ if (!(nmp->nm_flag & NFSMNT_INT))
+ return (0);
+ if (p && p->p_siglist &&
+ (((p->p_siglist & ~p->p_sigmask) & ~p->p_sigignore) &
+ NFSINT_SIGMASK))
+ return (EINTR);
+ return (0);
+}
+
+/*
+ * Lock a socket against others.
+ * Necessary for STREAM sockets to ensure you get an entire rpc request/reply
+ * and also to avoid race conditions between the processes with nfs requests
+ * in progress when a reconnect is necessary.
+ */
+int
+nfs_sndlock(flagp, rep)
+ register int *flagp;
+ struct nfsreq *rep;
+{
+ struct proc *p;
+ int slpflag = 0, slptimeo = 0;
+
+ if (rep) {
+ p = rep->r_procp;
+ if (rep->r_nmp->nm_flag & NFSMNT_INT)
+ slpflag = PCATCH;
+ } else
+ p = (struct proc *)0;
+ while (*flagp & NFSMNT_SNDLOCK) {
+ if (nfs_sigintr(rep->r_nmp, rep, p))
+ return (EINTR);
+ *flagp |= NFSMNT_WANTSND;
+ (void) tsleep((caddr_t)flagp, slpflag | (PZERO - 1), "nfsndlck",
+ slptimeo);
+ if (slpflag == PCATCH) {
+ slpflag = 0;
+ slptimeo = 2 * hz;
+ }
+ }
+ *flagp |= NFSMNT_SNDLOCK;
+ return (0);
+}
+
+/*
+ * Unlock the stream socket for others.
+ */
+void
+nfs_sndunlock(flagp)
+ register int *flagp;
+{
+
+ if ((*flagp & NFSMNT_SNDLOCK) == 0)
+ panic("nfs sndunlock");
+ *flagp &= ~NFSMNT_SNDLOCK;
+ if (*flagp & NFSMNT_WANTSND) {
+ *flagp &= ~NFSMNT_WANTSND;
+ wakeup((caddr_t)flagp);
+ }
+}
+
+int
+nfs_rcvlock(rep)
+ register struct nfsreq *rep;
+{
+ register int *flagp = &rep->r_nmp->nm_flag;
+ int slpflag, slptimeo = 0;
+
+ if (*flagp & NFSMNT_INT)
+ slpflag = PCATCH;
+ else
+ slpflag = 0;
+ while (*flagp & NFSMNT_RCVLOCK) {
+ if (nfs_sigintr(rep->r_nmp, rep, rep->r_procp))
+ return (EINTR);
+ *flagp |= NFSMNT_WANTRCV;
+ (void) tsleep((caddr_t)flagp, slpflag | (PZERO - 1), "nfsrcvlk",
+ slptimeo);
+ if (slpflag == PCATCH) {
+ slpflag = 0;
+ slptimeo = 2 * hz;
+ }
+ }
+ *flagp |= NFSMNT_RCVLOCK;
+ return (0);
+}
+
+/*
+ * Unlock the stream socket for others.
+ */
+void
+nfs_rcvunlock(flagp)
+ register int *flagp;
+{
+
+ if ((*flagp & NFSMNT_RCVLOCK) == 0)
+ panic("nfs rcvunlock");
+ *flagp &= ~NFSMNT_RCVLOCK;
+ if (*flagp & NFSMNT_WANTRCV) {
+ *flagp &= ~NFSMNT_WANTRCV;
+ wakeup((caddr_t)flagp);
+ }
+}
+
+/*
+ * Check for badly aligned mbuf data areas and
+ * realign data in an mbuf list by copying the data areas up, as required.
+ */
+void
+nfs_realign(m, hsiz)
+ register struct mbuf *m;
+ int hsiz;
+{
+ register struct mbuf *m2;
+ register int siz, mlen, olen;
+ register caddr_t tcp, fcp;
+ struct mbuf *mnew;
+
+ while (m) {
+ /*
+ * This never happens for UDP, rarely happens for TCP
+ * but frequently happens for iso transport.
+ */
+ if ((m->m_len & 0x3) || (mtod(m, int) & 0x3)) {
+ olen = m->m_len;
+ fcp = mtod(m, caddr_t);
+ if ((int)fcp & 0x3) {
+ m->m_flags &= ~M_PKTHDR;
+ if (m->m_flags & M_EXT)
+ m->m_data = m->m_ext.ext_buf +
+ ((m->m_ext.ext_size - olen) & ~0x3);
+ else
+ m->m_data = m->m_dat;
+ }
+ m->m_len = 0;
+ tcp = mtod(m, caddr_t);
+ mnew = m;
+ m2 = m->m_next;
+
+ /*
+ * If possible, only put the first invariant part
+ * of the RPC header in the first mbuf.
+ */
+ mlen = M_TRAILINGSPACE(m);
+ if (olen <= hsiz && mlen > hsiz)
+ mlen = hsiz;
+
+ /*
+ * Loop through the mbuf list consolidating data.
+ */
+ while (m) {
+ while (olen > 0) {
+ if (mlen == 0) {
+ m2->m_flags &= ~M_PKTHDR;
+ if (m2->m_flags & M_EXT)
+ m2->m_data = m2->m_ext.ext_buf;
+ else
+ m2->m_data = m2->m_dat;
+ m2->m_len = 0;
+ mlen = M_TRAILINGSPACE(m2);
+ tcp = mtod(m2, caddr_t);
+ mnew = m2;
+ m2 = m2->m_next;
+ }
+ siz = min(mlen, olen);
+ if (tcp != fcp)
+ bcopy(fcp, tcp, siz);
+ mnew->m_len += siz;
+ mlen -= siz;
+ olen -= siz;
+ tcp += siz;
+ fcp += siz;
+ }
+ m = m->m_next;
+ if (m) {
+ olen = m->m_len;
+ fcp = mtod(m, caddr_t);
+ }
+ }
+
+ /*
+ * Finally, set m_len == 0 for any trailing mbufs that have
+ * been copied out of.
+ */
+ while (m2) {
+ m2->m_len = 0;
+ m2 = m2->m_next;
+ }
+ return;
+ }
+ m = m->m_next;
+ }
+}
+
+/*
+ * Socket upcall routine for the nfsd sockets.
+ * The caddr_t arg is a pointer to the "struct nfssvc_sock".
+ * Essentially do as much as possible non-blocking, else punt and it will
+ * be called with M_WAIT from an nfsd.
+ */
+void
+nfsrv_rcv(so, arg, waitflag)
+ struct socket *so;
+ caddr_t arg;
+ int waitflag;
+{
+ register struct nfssvc_sock *slp = (struct nfssvc_sock *)arg;
+ register struct mbuf *m;
+ struct mbuf *mp, *nam;
+ struct uio auio;
+ int flags, error;
+
+ if ((slp->ns_flag & SLP_VALID) == 0)
+ return;
+#ifdef notdef
+ /*
+ * Define this to test for nfsds handling this under heavy load.
+ */
+ if (waitflag == M_DONTWAIT) {
+ slp->ns_flag |= SLP_NEEDQ; goto dorecs;
+ }
+#endif
+ auio.uio_procp = NULL;
+ if (so->so_type == SOCK_STREAM) {
+ /*
+ * If there are already records on the queue, defer soreceive()
+ * to an nfsd so that there is feedback to the TCP layer that
+ * the nfs servers are heavily loaded.
+ */
+ if (slp->ns_rec && waitflag == M_DONTWAIT) {
+ slp->ns_flag |= SLP_NEEDQ;
+ goto dorecs;
+ }
+
+ /*
+ * Do soreceive().
+ */
+ auio.uio_resid = 1000000000;
+ flags = MSG_DONTWAIT;
+ error = soreceive(so, &nam, &auio, &mp, (struct mbuf **)0, &flags);
+ if (error || mp == (struct mbuf *)0) {
+ if (error == EWOULDBLOCK)
+ slp->ns_flag |= SLP_NEEDQ;
+ else
+ slp->ns_flag |= SLP_DISCONN;
+ goto dorecs;
+ }
+ m = mp;
+ if (slp->ns_rawend) {
+ slp->ns_rawend->m_next = m;
+ slp->ns_cc += 1000000000 - auio.uio_resid;
+ } else {
+ slp->ns_raw = m;
+ slp->ns_cc = 1000000000 - auio.uio_resid;
+ }
+ while (m->m_next)
+ m = m->m_next;
+ slp->ns_rawend = m;
+
+ /*
+ * Now try and parse record(s) out of the raw stream data.
+ */
+ if (error = nfsrv_getstream(slp, waitflag)) {
+ if (error == EPERM)
+ slp->ns_flag |= SLP_DISCONN;
+ else
+ slp->ns_flag |= SLP_NEEDQ;
+ }
+ } else {
+ do {
+ auio.uio_resid = 1000000000;
+ flags = MSG_DONTWAIT;
+ error = soreceive(so, &nam, &auio, &mp,
+ (struct mbuf **)0, &flags);
+ if (mp) {
+ nfs_realign(mp, 10 * NFSX_UNSIGNED);
+ if (nam) {
+ m = nam;
+ m->m_next = mp;
+ } else
+ m = mp;
+ if (slp->ns_recend)
+ slp->ns_recend->m_nextpkt = m;
+ else
+ slp->ns_rec = m;
+ slp->ns_recend = m;
+ m->m_nextpkt = (struct mbuf *)0;
+ }
+ if (error) {
+ if ((so->so_proto->pr_flags & PR_CONNREQUIRED)
+ && error != EWOULDBLOCK) {
+ slp->ns_flag |= SLP_DISCONN;
+ goto dorecs;
+ }
+ }
+ } while (mp);
+ }
+
+ /*
+ * Now try and process the request records, non-blocking.
+ */
+dorecs:
+ if (waitflag == M_DONTWAIT &&
+ (slp->ns_rec || (slp->ns_flag & (SLP_NEEDQ | SLP_DISCONN))))
+ nfsrv_wakenfsd(slp);
+}
+
+/*
+ * Try and extract an RPC request from the mbuf data list received on a
+ * stream socket. The "waitflag" argument indicates whether or not it
+ * can sleep.
+ */
+int
+nfsrv_getstream(slp, waitflag)
+ register struct nfssvc_sock *slp;
+ int waitflag;
+{
+ register struct mbuf *m;
+ register char *cp1, *cp2;
+ register int len;
+ struct mbuf *om, *m2, *recm = 0;
+ u_long recmark;
+
+ if (slp->ns_flag & SLP_GETSTREAM)
+ panic("nfs getstream");
+ slp->ns_flag |= SLP_GETSTREAM;
+ for (;;) {
+ if (slp->ns_reclen == 0) {
+ if (slp->ns_cc < NFSX_UNSIGNED) {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (0);
+ }
+ m = slp->ns_raw;
+ if (m->m_len >= NFSX_UNSIGNED) {
+ bcopy(mtod(m, caddr_t), (caddr_t)&recmark, NFSX_UNSIGNED);
+ m->m_data += NFSX_UNSIGNED;
+ m->m_len -= NFSX_UNSIGNED;
+ } else {
+ cp1 = (caddr_t)&recmark;
+ cp2 = mtod(m, caddr_t);
+ while (cp1 < ((caddr_t)&recmark) + NFSX_UNSIGNED) {
+ while (m->m_len == 0) {
+ m = m->m_next;
+ cp2 = mtod(m, caddr_t);
+ }
+ *cp1++ = *cp2++;
+ m->m_data++;
+ m->m_len--;
+ }
+ }
+ slp->ns_cc -= NFSX_UNSIGNED;
+ slp->ns_reclen = ntohl(recmark) & ~0x80000000;
+ if (slp->ns_reclen < NFS_MINPACKET || slp->ns_reclen > NFS_MAXPACKET) {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (EPERM);
+ }
+ }
+
+ /*
+ * Now get the record part.
+ */
+ if (slp->ns_cc == slp->ns_reclen) {
+ recm = slp->ns_raw;
+ slp->ns_raw = slp->ns_rawend = (struct mbuf *)0;
+ slp->ns_cc = slp->ns_reclen = 0;
+ } else if (slp->ns_cc > slp->ns_reclen) {
+ len = 0;
+ m = slp->ns_raw;
+ om = (struct mbuf *)0;
+ while (len < slp->ns_reclen) {
+ if ((len + m->m_len) > slp->ns_reclen) {
+ m2 = m_copym(m, 0, slp->ns_reclen - len,
+ waitflag);
+ if (m2) {
+ if (om) {
+ om->m_next = m2;
+ recm = slp->ns_raw;
+ } else
+ recm = m2;
+ m->m_data += slp->ns_reclen - len;
+ m->m_len -= slp->ns_reclen - len;
+ len = slp->ns_reclen;
+ } else {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (EWOULDBLOCK);
+ }
+ } else if ((len + m->m_len) == slp->ns_reclen) {
+ om = m;
+ len += m->m_len;
+ m = m->m_next;
+ recm = slp->ns_raw;
+ om->m_next = (struct mbuf *)0;
+ } else {
+ om = m;
+ len += m->m_len;
+ m = m->m_next;
+ }
+ }
+ slp->ns_raw = m;
+ slp->ns_cc -= len;
+ slp->ns_reclen = 0;
+ } else {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (0);
+ }
+ nfs_realign(recm, 10 * NFSX_UNSIGNED);
+ if (slp->ns_recend)
+ slp->ns_recend->m_nextpkt = recm;
+ else
+ slp->ns_rec = recm;
+ slp->ns_recend = recm;
+ }
+}
+
+/*
+ * Parse an RPC header.
+ */
+int
+nfsrv_dorec(slp, nd)
+ register struct nfssvc_sock *slp;
+ register struct nfsd *nd;
+{
+ register struct mbuf *m;
+ int error;
+
+ if ((slp->ns_flag & SLP_VALID) == 0 ||
+ (m = slp->ns_rec) == (struct mbuf *)0)
+ return (ENOBUFS);
+ if (slp->ns_rec = m->m_nextpkt)
+ m->m_nextpkt = (struct mbuf *)0;
+ else
+ slp->ns_recend = (struct mbuf *)0;
+ if (m->m_type == MT_SONAME) {
+ nd->nd_nam = m;
+ nd->nd_md = nd->nd_mrep = m->m_next;
+ m->m_next = (struct mbuf *)0;
+ } else {
+ nd->nd_nam = (struct mbuf *)0;
+ nd->nd_md = nd->nd_mrep = m;
+ }
+ nd->nd_dpos = mtod(nd->nd_md, caddr_t);
+ if (error = nfs_getreq(nd, TRUE)) {
+ m_freem(nd->nd_nam);
+ return (error);
+ }
+ return (0);
+}
+
+/*
+ * Parse an RPC request
+ * - verify it
+ * - fill in the cred struct.
+ */
+int
+nfs_getreq(nd, has_header)
+ register struct nfsd *nd;
+ int has_header;
+{
+ register int len, i;
+ register u_long *tl;
+ register long t1;
+ struct uio uio;
+ struct iovec iov;
+ caddr_t dpos, cp2;
+ u_long nfsvers, auth_type;
+ int error = 0, nqnfs = 0;
+ struct mbuf *mrep, *md;
+
+ mrep = nd->nd_mrep;
+ md = nd->nd_md;
+ dpos = nd->nd_dpos;
+ if (has_header) {
+ nfsm_dissect(tl, u_long *, 10*NFSX_UNSIGNED);
+ nd->nd_retxid = *tl++;
+ if (*tl++ != rpc_call) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ } else {
+ nfsm_dissect(tl, u_long *, 8*NFSX_UNSIGNED);
+ }
+ nd->nd_repstat = 0;
+ if (*tl++ != rpc_vers) {
+ nd->nd_repstat = ERPCMISMATCH;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ nfsvers = nfs_vers;
+ if (*tl != nfs_prog) {
+ if (*tl == nqnfs_prog) {
+ nqnfs++;
+ nfsvers = nqnfs_vers;
+ } else {
+ nd->nd_repstat = EPROGUNAVAIL;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ }
+ tl++;
+ if (*tl++ != nfsvers) {
+ nd->nd_repstat = EPROGMISMATCH;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ nd->nd_procnum = fxdr_unsigned(u_long, *tl++);
+ if (nd->nd_procnum == NFSPROC_NULL)
+ return (0);
+ if (nd->nd_procnum >= NFS_NPROCS ||
+ (!nqnfs && nd->nd_procnum > NFSPROC_STATFS) ||
+ (*tl != rpc_auth_unix && *tl != rpc_auth_kerb)) {
+ nd->nd_repstat = EPROCUNAVAIL;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ auth_type = *tl++;
+ len = fxdr_unsigned(int, *tl++);
+ if (len < 0 || len > RPCAUTH_MAXSIZ) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+
+ /*
+ * Handle auth_unix or auth_kerb.
+ */
+ if (auth_type == rpc_auth_unix) {
+ len = fxdr_unsigned(int, *++tl);
+ if (len < 0 || len > NFS_MAXNAMLEN) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ nfsm_adv(nfsm_rndup(len));
+ nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED);
+ nd->nd_cr.cr_uid = fxdr_unsigned(uid_t, *tl++);
+ nd->nd_cr.cr_gid = fxdr_unsigned(gid_t, *tl++);
+ len = fxdr_unsigned(int, *tl);
+ if (len < 0 || len > RPCAUTH_UNIXGIDS) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ nfsm_dissect(tl, u_long *, (len + 2)*NFSX_UNSIGNED);
+ for (i = 1; i <= len; i++)
+ if (i < NGROUPS)
+ nd->nd_cr.cr_groups[i] = fxdr_unsigned(gid_t, *tl++);
+ else
+ tl++;
+ nd->nd_cr.cr_ngroups = (len >= NGROUPS) ? NGROUPS : (len + 1);
+ } else if (auth_type == rpc_auth_kerb) {
+ nd->nd_cr.cr_uid = fxdr_unsigned(uid_t, *tl++);
+ nd->nd_authlen = fxdr_unsigned(int, *tl);
+ uio.uio_resid = nfsm_rndup(nd->nd_authlen);
+ if (uio.uio_resid > (len - 2 * NFSX_UNSIGNED)) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ uio.uio_offset = 0;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_segflg = UIO_SYSSPACE;
+ iov.iov_base = (caddr_t)nd->nd_authstr;
+ iov.iov_len = RPCAUTH_MAXSIZ;
+ nfsm_mtouio(&uio, uio.uio_resid);
+ nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+ nd->nd_flag |= NFSD_NEEDAUTH;
+ }
+
+ /*
+ * Do we have any use for the verifier.
+ * According to the "Remote Procedure Call Protocol Spec." it
+ * should be AUTH_NULL, but some clients make it AUTH_UNIX?
+ * For now, just skip over it
+ */
+ len = fxdr_unsigned(int, *++tl);
+ if (len < 0 || len > RPCAUTH_MAXSIZ) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ if (len > 0) {
+ nfsm_adv(nfsm_rndup(len));
+ }
+
+ /*
+ * For nqnfs, get piggybacked lease request.
+ */
+ if (nqnfs && nd->nd_procnum != NQNFSPROC_EVICTED) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ nd->nd_nqlflag = fxdr_unsigned(int, *tl);
+ if (nd->nd_nqlflag) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ nd->nd_duration = fxdr_unsigned(int, *tl);
+ } else
+ nd->nd_duration = NQ_MINLEASE;
+ } else {
+ nd->nd_nqlflag = NQL_NOVAL;
+ nd->nd_duration = NQ_MINLEASE;
+ }
+ nd->nd_md = md;
+ nd->nd_dpos = dpos;
+ return (0);
+nfsmout:
+ return (error);
+}
+
+/*
+ * Search for a sleeping nfsd and wake it up.
+ * SIDE EFFECT: If none found, set NFSD_CHECKSLP flag, so that one of the
+ * running nfsds will go look for the work in the nfssvc_sock list.
+ */
+void
+nfsrv_wakenfsd(slp)
+ struct nfssvc_sock *slp;
+{
+ register struct nfsd *nd = nfsd_head.nd_next;
+
+ if ((slp->ns_flag & SLP_VALID) == 0)
+ return;
+ while (nd != (struct nfsd *)&nfsd_head) {
+ if (nd->nd_flag & NFSD_WAITING) {
+ nd->nd_flag &= ~NFSD_WAITING;
+ if (nd->nd_slp)
+ panic("nfsd wakeup");
+ slp->ns_sref++;
+ nd->nd_slp = slp;
+ wakeup((caddr_t)nd);
+ return;
+ }
+ nd = nd->nd_next;
+ }
+ slp->ns_flag |= SLP_DOREC;
+ nfsd_head.nd_flag |= NFSD_CHECKSLP;
+}
+
+int
+nfs_msg(p, server, msg)
+ struct proc *p;
+ char *server, *msg;
+{
+ tpr_t tpr;
+
+ if (p)
+ tpr = tprintf_open(p);
+ else
+ tpr = NULL;
+ tprintf(tpr, "nfs server %s: %s\n", server, msg);
+ tprintf_close(tpr);
+ return (0);
+}
diff --git a/sys/nfsclient/nfs_subs.c b/sys/nfsclient/nfs_subs.c
new file mode 100644
index 0000000..d17cde7
--- /dev/null
+++ b/sys/nfsclient/nfs_subs.c
@@ -0,0 +1,1143 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_subs.c 8.3 (Berkeley) 1/4/94
+ */
+
+/*
+ * These functions support the macros and help fiddle mbuf chains for
+ * the nfs op functions. They do things like create the rpc header and
+ * copy data between mbuf chains and uio lists.
+ */
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfs.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nqnfs.h>
+#include <nfs/nfsrtt.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <netinet/in.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Data items converted to xdr at startup, since they are constant
+ * This is kinda hokey, but may save a little time doing byte swaps
+ */
+u_long nfs_procids[NFS_NPROCS];
+u_long nfs_xdrneg1;
+u_long rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr,
+ rpc_mismatch, rpc_auth_unix, rpc_msgaccepted, rpc_rejectedcred,
+ rpc_auth_kerb;
+u_long nfs_vers, nfs_prog, nfs_true, nfs_false;
+
+/* And other global data */
+static u_long nfs_xid = 0;
+enum vtype ntov_type[7] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON };
+extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
+extern struct nfsreq nfsreqh;
+extern int nqnfs_piggy[NFS_NPROCS];
+extern struct nfsrtt nfsrtt;
+extern time_t nqnfsstarttime;
+extern u_long nqnfs_prog, nqnfs_vers;
+extern int nqsrv_clockskew;
+extern int nqsrv_writeslack;
+extern int nqsrv_maxlease;
+
+/*
+ * Create the header for an rpc request packet
+ * The hsiz is the size of the rest of the nfs request header.
+ * (just used to decide if a cluster is a good idea)
+ */
+struct mbuf *
+nfsm_reqh(vp, procid, hsiz, bposp)
+ struct vnode *vp;
+ u_long procid;
+ int hsiz;
+ caddr_t *bposp;
+{
+ register struct mbuf *mb;
+ register u_long *tl;
+ register caddr_t bpos;
+ struct mbuf *mb2;
+ struct nfsmount *nmp;
+ int nqflag;
+
+ MGET(mb, M_WAIT, MT_DATA);
+ if (hsiz >= MINCLSIZE)
+ MCLGET(mb, M_WAIT);
+ mb->m_len = 0;
+ bpos = mtod(mb, caddr_t);
+
+ /*
+ * For NQNFS, add lease request.
+ */
+ if (vp) {
+ nmp = VFSTONFS(vp->v_mount);
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ nqflag = NQNFS_NEEDLEASE(vp, procid);
+ if (nqflag) {
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(nqflag);
+ *tl = txdr_unsigned(nmp->nm_leaseterm);
+ } else {
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = 0;
+ }
+ }
+ }
+ /* Finally, return values */
+ *bposp = bpos;
+ return (mb);
+}
+
+/*
+ * Build the RPC header and fill in the authorization info.
+ * The authorization string argument is only used when the credentials
+ * come from outside of the kernel.
+ * Returns the head of the mbuf list.
+ */
+struct mbuf *
+nfsm_rpchead(cr, nqnfs, procid, auth_type, auth_len, auth_str, mrest,
+ mrest_len, mbp, xidp)
+ register struct ucred *cr;
+ int nqnfs;
+ int procid;
+ int auth_type;
+ int auth_len;
+ char *auth_str;
+ struct mbuf *mrest;
+ int mrest_len;
+ struct mbuf **mbp;
+ u_long *xidp;
+{
+ register struct mbuf *mb;
+ register u_long *tl;
+ register caddr_t bpos;
+ register int i;
+ struct mbuf *mreq, *mb2;
+ int siz, grpsiz, authsiz;
+
+ authsiz = nfsm_rndup(auth_len);
+ if (auth_type == RPCAUTH_NQNFS)
+ authsiz += 2 * NFSX_UNSIGNED;
+ MGETHDR(mb, M_WAIT, MT_DATA);
+ if ((authsiz + 10*NFSX_UNSIGNED) >= MINCLSIZE) {
+ MCLGET(mb, M_WAIT);
+ } else if ((authsiz + 10*NFSX_UNSIGNED) < MHLEN) {
+ MH_ALIGN(mb, authsiz + 10*NFSX_UNSIGNED);
+ } else {
+ MH_ALIGN(mb, 8*NFSX_UNSIGNED);
+ }
+ mb->m_len = 0;
+ mreq = mb;
+ bpos = mtod(mb, caddr_t);
+
+ /*
+ * First the RPC header.
+ */
+ nfsm_build(tl, u_long *, 8*NFSX_UNSIGNED);
+ if (++nfs_xid == 0)
+ nfs_xid++;
+ *tl++ = *xidp = txdr_unsigned(nfs_xid);
+ *tl++ = rpc_call;
+ *tl++ = rpc_vers;
+ if (nqnfs) {
+ *tl++ = txdr_unsigned(NQNFS_PROG);
+ *tl++ = txdr_unsigned(NQNFS_VER1);
+ } else {
+ *tl++ = txdr_unsigned(NFS_PROG);
+ *tl++ = txdr_unsigned(NFS_VER2);
+ }
+ *tl++ = txdr_unsigned(procid);
+
+ /*
+ * And then the authorization cred.
+ */
+ *tl++ = txdr_unsigned(auth_type);
+ *tl = txdr_unsigned(authsiz);
+ switch (auth_type) {
+ case RPCAUTH_UNIX:
+ nfsm_build(tl, u_long *, auth_len);
+ *tl++ = 0; /* stamp ?? */
+ *tl++ = 0; /* NULL hostname */
+ *tl++ = txdr_unsigned(cr->cr_uid);
+ *tl++ = txdr_unsigned(cr->cr_groups[0]);
+ grpsiz = (auth_len >> 2) - 5;
+ *tl++ = txdr_unsigned(grpsiz);
+ for (i = 1; i <= grpsiz; i++)
+ *tl++ = txdr_unsigned(cr->cr_groups[i]);
+ break;
+ case RPCAUTH_NQNFS:
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(cr->cr_uid);
+ *tl = txdr_unsigned(auth_len);
+ siz = auth_len;
+ while (siz > 0) {
+ if (M_TRAILINGSPACE(mb) == 0) {
+ MGET(mb2, M_WAIT, MT_DATA);
+ if (siz >= MINCLSIZE)
+ MCLGET(mb2, M_WAIT);
+ mb->m_next = mb2;
+ mb = mb2;
+ mb->m_len = 0;
+ bpos = mtod(mb, caddr_t);
+ }
+ i = min(siz, M_TRAILINGSPACE(mb));
+ bcopy(auth_str, bpos, i);
+ mb->m_len += i;
+ auth_str += i;
+ bpos += i;
+ siz -= i;
+ }
+ if ((siz = (nfsm_rndup(auth_len) - auth_len)) > 0) {
+ for (i = 0; i < siz; i++)
+ *bpos++ = '\0';
+ mb->m_len += siz;
+ }
+ break;
+ };
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(RPCAUTH_NULL);
+ *tl = 0;
+ mb->m_next = mrest;
+ mreq->m_pkthdr.len = authsiz + 10*NFSX_UNSIGNED + mrest_len;
+ mreq->m_pkthdr.rcvif = (struct ifnet *)0;
+ *mbp = mb;
+ return (mreq);
+}
+
+/*
+ * copies mbuf chain to the uio scatter/gather list
+ */
+int
+nfsm_mbuftouio(mrep, uiop, siz, dpos)
+ struct mbuf **mrep;
+ register struct uio *uiop;
+ int siz;
+ caddr_t *dpos;
+{
+ register char *mbufcp, *uiocp;
+ register int xfer, left, len;
+ register struct mbuf *mp;
+ long uiosiz, rem;
+ int error = 0;
+
+ mp = *mrep;
+ mbufcp = *dpos;
+ len = mtod(mp, caddr_t)+mp->m_len-mbufcp;
+ rem = nfsm_rndup(siz)-siz;
+ while (siz > 0) {
+ if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
+ return (EFBIG);
+ left = uiop->uio_iov->iov_len;
+ uiocp = uiop->uio_iov->iov_base;
+ if (left > siz)
+ left = siz;
+ uiosiz = left;
+ while (left > 0) {
+ while (len == 0) {
+ mp = mp->m_next;
+ if (mp == NULL)
+ return (EBADRPC);
+ mbufcp = mtod(mp, caddr_t);
+ len = mp->m_len;
+ }
+ xfer = (left > len) ? len : left;
+#ifdef notdef
+ /* Not Yet.. */
+ if (uiop->uio_iov->iov_op != NULL)
+ (*(uiop->uio_iov->iov_op))
+ (mbufcp, uiocp, xfer);
+ else
+#endif
+ if (uiop->uio_segflg == UIO_SYSSPACE)
+ bcopy(mbufcp, uiocp, xfer);
+ else
+ copyout(mbufcp, uiocp, xfer);
+ left -= xfer;
+ len -= xfer;
+ mbufcp += xfer;
+ uiocp += xfer;
+ uiop->uio_offset += xfer;
+ uiop->uio_resid -= xfer;
+ }
+ if (uiop->uio_iov->iov_len <= siz) {
+ uiop->uio_iovcnt--;
+ uiop->uio_iov++;
+ } else {
+ uiop->uio_iov->iov_base += uiosiz;
+ uiop->uio_iov->iov_len -= uiosiz;
+ }
+ siz -= uiosiz;
+ }
+ *dpos = mbufcp;
+ *mrep = mp;
+ if (rem > 0) {
+ if (len < rem)
+ error = nfs_adv(mrep, dpos, rem, len);
+ else
+ *dpos += rem;
+ }
+ return (error);
+}
+
+/*
+ * copies a uio scatter/gather list to an mbuf chain...
+ */
+int
+nfsm_uiotombuf(uiop, mq, siz, bpos)
+ register struct uio *uiop;
+ struct mbuf **mq;
+ int siz;
+ caddr_t *bpos;
+{
+ register char *uiocp;
+ register struct mbuf *mp, *mp2;
+ register int xfer, left, mlen;
+ int uiosiz, clflg, rem;
+ char *cp;
+
+ if (siz > MLEN) /* or should it >= MCLBYTES ?? */
+ clflg = 1;
+ else
+ clflg = 0;
+ rem = nfsm_rndup(siz)-siz;
+ mp = mp2 = *mq;
+ while (siz > 0) {
+ if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
+ return (EINVAL);
+ left = uiop->uio_iov->iov_len;
+ uiocp = uiop->uio_iov->iov_base;
+ if (left > siz)
+ left = siz;
+ uiosiz = left;
+ while (left > 0) {
+ mlen = M_TRAILINGSPACE(mp);
+ if (mlen == 0) {
+ MGET(mp, M_WAIT, MT_DATA);
+ if (clflg)
+ MCLGET(mp, M_WAIT);
+ mp->m_len = 0;
+ mp2->m_next = mp;
+ mp2 = mp;
+ mlen = M_TRAILINGSPACE(mp);
+ }
+ xfer = (left > mlen) ? mlen : left;
+#ifdef notdef
+ /* Not Yet.. */
+ if (uiop->uio_iov->iov_op != NULL)
+ (*(uiop->uio_iov->iov_op))
+ (uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
+ else
+#endif
+ if (uiop->uio_segflg == UIO_SYSSPACE)
+ bcopy(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
+ else
+ copyin(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
+ mp->m_len += xfer;
+ left -= xfer;
+ uiocp += xfer;
+ uiop->uio_offset += xfer;
+ uiop->uio_resid -= xfer;
+ }
+ if (uiop->uio_iov->iov_len <= siz) {
+ uiop->uio_iovcnt--;
+ uiop->uio_iov++;
+ } else {
+ uiop->uio_iov->iov_base += uiosiz;
+ uiop->uio_iov->iov_len -= uiosiz;
+ }
+ siz -= uiosiz;
+ }
+ if (rem > 0) {
+ if (rem > M_TRAILINGSPACE(mp)) {
+ MGET(mp, M_WAIT, MT_DATA);
+ mp->m_len = 0;
+ mp2->m_next = mp;
+ }
+ cp = mtod(mp, caddr_t)+mp->m_len;
+ for (left = 0; left < rem; left++)
+ *cp++ = '\0';
+ mp->m_len += rem;
+ *bpos = cp;
+ } else
+ *bpos = mtod(mp, caddr_t)+mp->m_len;
+ *mq = mp;
+ return (0);
+}
+
+/*
+ * Help break down an mbuf chain by setting the first siz bytes contiguous
+ * pointed to by returned val.
+ * This is used by the macros nfsm_dissect and nfsm_dissecton for tough
+ * cases. (The macros use the vars. dpos and dpos2)
+ */
+int
+nfsm_disct(mdp, dposp, siz, left, cp2)
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ int siz;
+ int left;
+ caddr_t *cp2;
+{
+ register struct mbuf *mp, *mp2;
+ register int siz2, xfer;
+ register caddr_t p;
+
+ mp = *mdp;
+ while (left == 0) {
+ *mdp = mp = mp->m_next;
+ if (mp == NULL)
+ return (EBADRPC);
+ left = mp->m_len;
+ *dposp = mtod(mp, caddr_t);
+ }
+ if (left >= siz) {
+ *cp2 = *dposp;
+ *dposp += siz;
+ } else if (mp->m_next == NULL) {
+ return (EBADRPC);
+ } else if (siz > MHLEN) {
+ panic("nfs S too big");
+ } else {
+ MGET(mp2, M_WAIT, MT_DATA);
+ mp2->m_next = mp->m_next;
+ mp->m_next = mp2;
+ mp->m_len -= left;
+ mp = mp2;
+ *cp2 = p = mtod(mp, caddr_t);
+ bcopy(*dposp, p, left); /* Copy what was left */
+ siz2 = siz-left;
+ p += left;
+ mp2 = mp->m_next;
+ /* Loop around copying up the siz2 bytes */
+ while (siz2 > 0) {
+ if (mp2 == NULL)
+ return (EBADRPC);
+ xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
+ if (xfer > 0) {
+ bcopy(mtod(mp2, caddr_t), p, xfer);
+ NFSMADV(mp2, xfer);
+ mp2->m_len -= xfer;
+ p += xfer;
+ siz2 -= xfer;
+ }
+ if (siz2 > 0)
+ mp2 = mp2->m_next;
+ }
+ mp->m_len = siz;
+ *mdp = mp2;
+ *dposp = mtod(mp2, caddr_t);
+ }
+ return (0);
+}
+
+/*
+ * Advance the position in the mbuf chain.
+ */
+int
+nfs_adv(mdp, dposp, offs, left)
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ int offs;
+ int left;
+{
+ register struct mbuf *m;
+ register int s;
+
+ m = *mdp;
+ s = left;
+ while (s < offs) {
+ offs -= s;
+ m = m->m_next;
+ if (m == NULL)
+ return (EBADRPC);
+ s = m->m_len;
+ }
+ *mdp = m;
+ *dposp = mtod(m, caddr_t)+offs;
+ return (0);
+}
+
+/*
+ * Copy a string into mbufs for the hard cases...
+ */
+int
+nfsm_strtmbuf(mb, bpos, cp, siz)
+ struct mbuf **mb;
+ char **bpos;
+ char *cp;
+ long siz;
+{
+ register struct mbuf *m1 = 0, *m2;
+ long left, xfer, len, tlen;
+ u_long *tl;
+ int putsize;
+
+ putsize = 1;
+ m2 = *mb;
+ left = M_TRAILINGSPACE(m2);
+ if (left > 0) {
+ tl = ((u_long *)(*bpos));
+ *tl++ = txdr_unsigned(siz);
+ putsize = 0;
+ left -= NFSX_UNSIGNED;
+ m2->m_len += NFSX_UNSIGNED;
+ if (left > 0) {
+ bcopy(cp, (caddr_t) tl, left);
+ siz -= left;
+ cp += left;
+ m2->m_len += left;
+ left = 0;
+ }
+ }
+ /* Loop around adding mbufs */
+ while (siz > 0) {
+ MGET(m1, M_WAIT, MT_DATA);
+ if (siz > MLEN)
+ MCLGET(m1, M_WAIT);
+ m1->m_len = NFSMSIZ(m1);
+ m2->m_next = m1;
+ m2 = m1;
+ tl = mtod(m1, u_long *);
+ tlen = 0;
+ if (putsize) {
+ *tl++ = txdr_unsigned(siz);
+ m1->m_len -= NFSX_UNSIGNED;
+ tlen = NFSX_UNSIGNED;
+ putsize = 0;
+ }
+ if (siz < m1->m_len) {
+ len = nfsm_rndup(siz);
+ xfer = siz;
+ if (xfer < len)
+ *(tl+(xfer>>2)) = 0;
+ } else {
+ xfer = len = m1->m_len;
+ }
+ bcopy(cp, (caddr_t) tl, xfer);
+ m1->m_len = len+tlen;
+ siz -= xfer;
+ cp += xfer;
+ }
+ *mb = m1;
+ *bpos = mtod(m1, caddr_t)+m1->m_len;
+ return (0);
+}
+
+/*
+ * Called once to initialize data structures...
+ */
+int
+nfs_init()
+{
+ register int i;
+
+ nfsrtt.pos = 0;
+ rpc_vers = txdr_unsigned(RPC_VER2);
+ rpc_call = txdr_unsigned(RPC_CALL);
+ rpc_reply = txdr_unsigned(RPC_REPLY);
+ rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
+ rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
+ rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
+ rpc_autherr = txdr_unsigned(RPC_AUTHERR);
+ rpc_rejectedcred = txdr_unsigned(AUTH_REJECTCRED);
+ rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
+ rpc_auth_kerb = txdr_unsigned(RPCAUTH_NQNFS);
+ nfs_vers = txdr_unsigned(NFS_VER2);
+ nfs_prog = txdr_unsigned(NFS_PROG);
+ nfs_true = txdr_unsigned(TRUE);
+ nfs_false = txdr_unsigned(FALSE);
+ /* Loop thru nfs procids */
+ for (i = 0; i < NFS_NPROCS; i++)
+ nfs_procids[i] = txdr_unsigned(i);
+ /* Ensure async daemons disabled */
+ for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
+ nfs_iodwant[i] = (struct proc *)0;
+ TAILQ_INIT(&nfs_bufq);
+ nfs_xdrneg1 = txdr_unsigned(-1);
+ nfs_nhinit(); /* Init the nfsnode table */
+ nfsrv_init(0); /* Init server data structures */
+ nfsrv_initcache(); /* Init the server request cache */
+
+ /*
+ * Initialize the nqnfs server stuff.
+ */
+ if (nqnfsstarttime == 0) {
+ nqnfsstarttime = boottime.tv_sec + nqsrv_maxlease
+ + nqsrv_clockskew + nqsrv_writeslack;
+ NQLOADNOVRAM(nqnfsstarttime);
+ nqnfs_prog = txdr_unsigned(NQNFS_PROG);
+ nqnfs_vers = txdr_unsigned(NQNFS_VER1);
+ nqthead.th_head[0] = &nqthead;
+ nqthead.th_head[1] = &nqthead;
+ nqfhead = hashinit(NQLCHSZ, M_NQLEASE, &nqfheadhash);
+ }
+
+ /*
+ * Initialize reply list and start timer
+ */
+ nfsreqh.r_prev = nfsreqh.r_next = &nfsreqh;
+ nfs_timer();
+
+ return (0);
+}
+
+/*
+ * Attribute cache routines.
+ * nfs_loadattrcache() - loads or updates the cache contents from attributes
+ * that are on the mbuf list
+ * nfs_getattrcache() - returns valid attributes if found in cache, returns
+ * error otherwise
+ */
+
+/*
+ * Load the attribute cache (that lives in the nfsnode entry) with
+ * the values on the mbuf list and
+ * Iff vap not NULL
+ * copy the attributes to *vaper
+ */
+int
+nfs_loadattrcache(vpp, mdp, dposp, vaper)
+ struct vnode **vpp;
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ struct vattr *vaper;
+{
+ register struct vnode *vp = *vpp;
+ register struct vattr *vap;
+ register struct nfsv2_fattr *fp;
+ extern int (**spec_nfsv2nodeop_p)();
+ register struct nfsnode *np, *nq, **nhpp;
+ register long t1;
+ caddr_t dpos, cp2;
+ int error = 0, isnq;
+ struct mbuf *md;
+ enum vtype vtyp;
+ u_short vmode;
+ long rdev;
+ struct timespec mtime;
+ struct vnode *nvp;
+
+ md = *mdp;
+ dpos = *dposp;
+ t1 = (mtod(md, caddr_t) + md->m_len) - dpos;
+ isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ if (error = nfsm_disct(&md, &dpos, NFSX_FATTR(isnq), t1, &cp2))
+ return (error);
+ fp = (struct nfsv2_fattr *)cp2;
+ vtyp = nfstov_type(fp->fa_type);
+ vmode = fxdr_unsigned(u_short, fp->fa_mode);
+ if (vtyp == VNON || vtyp == VREG)
+ vtyp = IFTOVT(vmode);
+ if (isnq) {
+ rdev = fxdr_unsigned(long, fp->fa_nqrdev);
+ fxdr_nqtime(&fp->fa_nqmtime, &mtime);
+ } else {
+ rdev = fxdr_unsigned(long, fp->fa_nfsrdev);
+ fxdr_nfstime(&fp->fa_nfsmtime, &mtime);
+ }
+ /*
+ * If v_type == VNON it is a new node, so fill in the v_type,
+ * n_mtime fields. Check to see if it represents a special
+ * device, and if so, check for a possible alias. Once the
+ * correct vnode has been obtained, fill in the rest of the
+ * information.
+ */
+ np = VTONFS(vp);
+ if (vp->v_type == VNON) {
+ if (vtyp == VCHR && rdev == 0xffffffff)
+ vp->v_type = vtyp = VFIFO;
+ else
+ vp->v_type = vtyp;
+ if (vp->v_type == VFIFO) {
+#ifdef FIFO
+ extern int (**fifo_nfsv2nodeop_p)();
+ vp->v_op = fifo_nfsv2nodeop_p;
+#else
+ return (EOPNOTSUPP);
+#endif /* FIFO */
+ }
+ if (vp->v_type == VCHR || vp->v_type == VBLK) {
+ vp->v_op = spec_nfsv2nodeop_p;
+ if (nvp = checkalias(vp, (dev_t)rdev, vp->v_mount)) {
+ /*
+ * Discard unneeded vnode, but save its nfsnode.
+ */
+ if (nq = np->n_forw)
+ nq->n_back = np->n_back;
+ *np->n_back = nq;
+ nvp->v_data = vp->v_data;
+ vp->v_data = NULL;
+ vp->v_op = spec_vnodeop_p;
+ vrele(vp);
+ vgone(vp);
+ /*
+ * Reinitialize aliased node.
+ */
+ np->n_vnode = nvp;
+ nhpp = (struct nfsnode **)nfs_hash(&np->n_fh);
+ if (nq = *nhpp)
+ nq->n_back = &np->n_forw;
+ np->n_forw = nq;
+ np->n_back = nhpp;
+ *nhpp = np;
+ *vpp = vp = nvp;
+ }
+ }
+ np->n_mtime = mtime.ts_sec;
+ }
+ vap = &np->n_vattr;
+ vap->va_type = vtyp;
+ vap->va_mode = (vmode & 07777);
+ vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
+ vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
+ vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
+ vap->va_rdev = (dev_t)rdev;
+ vap->va_mtime = mtime;
+ vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+ if (isnq) {
+ fxdr_hyper(&fp->fa_nqsize, &vap->va_size);
+ vap->va_blocksize = fxdr_unsigned(long, fp->fa_nqblocksize);
+ fxdr_hyper(&fp->fa_nqbytes, &vap->va_bytes);
+ vap->va_fileid = fxdr_unsigned(long, fp->fa_nqfileid);
+ fxdr_nqtime(&fp->fa_nqatime, &vap->va_atime);
+ vap->va_flags = fxdr_unsigned(u_long, fp->fa_nqflags);
+ fxdr_nqtime(&fp->fa_nqctime, &vap->va_ctime);
+ vap->va_gen = fxdr_unsigned(u_long, fp->fa_nqgen);
+ fxdr_hyper(&fp->fa_nqfilerev, &vap->va_filerev);
+ } else {
+ vap->va_size = fxdr_unsigned(u_long, fp->fa_nfssize);
+ vap->va_blocksize = fxdr_unsigned(long, fp->fa_nfsblocksize);
+ vap->va_bytes = fxdr_unsigned(long, fp->fa_nfsblocks) * NFS_FABLKSIZE;
+ vap->va_fileid = fxdr_unsigned(long, fp->fa_nfsfileid);
+ fxdr_nfstime(&fp->fa_nfsatime, &vap->va_atime);
+ vap->va_flags = 0;
+ vap->va_ctime.ts_sec = fxdr_unsigned(long, fp->fa_nfsctime.nfs_sec);
+ vap->va_ctime.ts_nsec = 0;
+ vap->va_gen = fxdr_unsigned(u_long, fp->fa_nfsctime.nfs_usec);
+ vap->va_filerev = 0;
+ }
+ if (vap->va_size != np->n_size) {
+ if (vap->va_type == VREG) {
+ if (np->n_flag & NMODIFIED) {
+ if (vap->va_size < np->n_size)
+ vap->va_size = np->n_size;
+ else
+ np->n_size = vap->va_size;
+ } else
+ np->n_size = vap->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else
+ np->n_size = vap->va_size;
+ }
+ np->n_attrstamp = time.tv_sec;
+ *dposp = dpos;
+ *mdp = md;
+ if (vaper != NULL) {
+ bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
+#ifdef notdef
+ if ((np->n_flag & NMODIFIED) && np->n_size > vap->va_size)
+ if (np->n_size > vap->va_size)
+ vaper->va_size = np->n_size;
+#endif
+ if (np->n_flag & NCHG) {
+ if (np->n_flag & NACC) {
+ vaper->va_atime.ts_sec = np->n_atim.tv_sec;
+ vaper->va_atime.ts_nsec =
+ np->n_atim.tv_usec * 1000;
+ }
+ if (np->n_flag & NUPD) {
+ vaper->va_mtime.ts_sec = np->n_mtim.tv_sec;
+ vaper->va_mtime.ts_nsec =
+ np->n_mtim.tv_usec * 1000;
+ }
+ }
+ }
+ return (0);
+}
+
+/*
+ * Check the time stamp
+ * If the cache is valid, copy contents to *vap and return 0
+ * otherwise return an error
+ */
+int
+nfs_getattrcache(vp, vaper)
+ register struct vnode *vp;
+ struct vattr *vaper;
+{
+ register struct nfsnode *np = VTONFS(vp);
+ register struct vattr *vap;
+
+ if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQLOOKLEASE) {
+ if (!NQNFS_CKCACHABLE(vp, NQL_READ) || np->n_attrstamp == 0) {
+ nfsstats.attrcache_misses++;
+ return (ENOENT);
+ }
+ } else if ((time.tv_sec - np->n_attrstamp) >= NFS_ATTRTIMEO(np)) {
+ nfsstats.attrcache_misses++;
+ return (ENOENT);
+ }
+ nfsstats.attrcache_hits++;
+ vap = &np->n_vattr;
+ if (vap->va_size != np->n_size) {
+ if (vap->va_type == VREG) {
+ if (np->n_flag & NMODIFIED) {
+ if (vap->va_size < np->n_size)
+ vap->va_size = np->n_size;
+ else
+ np->n_size = vap->va_size;
+ } else
+ np->n_size = vap->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else
+ np->n_size = vap->va_size;
+ }
+ bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(struct vattr));
+#ifdef notdef
+ if ((np->n_flag & NMODIFIED) == 0) {
+ np->n_size = vaper->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else if (np->n_size > vaper->va_size)
+ if (np->n_size > vaper->va_size)
+ vaper->va_size = np->n_size;
+#endif
+ if (np->n_flag & NCHG) {
+ if (np->n_flag & NACC) {
+ vaper->va_atime.ts_sec = np->n_atim.tv_sec;
+ vaper->va_atime.ts_nsec = np->n_atim.tv_usec * 1000;
+ }
+ if (np->n_flag & NUPD) {
+ vaper->va_mtime.ts_sec = np->n_mtim.tv_sec;
+ vaper->va_mtime.ts_nsec = np->n_mtim.tv_usec * 1000;
+ }
+ }
+ return (0);
+}
+
+/*
+ * Set up nameidata for a lookup() call and do it
+ */
+int
+nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p)
+ register struct nameidata *ndp;
+ fhandle_t *fhp;
+ int len;
+ struct nfssvc_sock *slp;
+ struct mbuf *nam;
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ struct proc *p;
+{
+ register int i, rem;
+ register struct mbuf *md;
+ register char *fromcp, *tocp;
+ struct vnode *dp;
+ int error, rdonly;
+ struct componentname *cnp = &ndp->ni_cnd;
+
+ MALLOC(cnp->cn_pnbuf, char *, len + 1, M_NAMEI, M_WAITOK);
+ /*
+ * Copy the name from the mbuf list to ndp->ni_pnbuf
+ * and set the various ndp fields appropriately.
+ */
+ fromcp = *dposp;
+ tocp = cnp->cn_pnbuf;
+ md = *mdp;
+ rem = mtod(md, caddr_t) + md->m_len - fromcp;
+ cnp->cn_hash = 0;
+ for (i = 0; i < len; i++) {
+ while (rem == 0) {
+ md = md->m_next;
+ if (md == NULL) {
+ error = EBADRPC;
+ goto out;
+ }
+ fromcp = mtod(md, caddr_t);
+ rem = md->m_len;
+ }
+ if (*fromcp == '\0' || *fromcp == '/') {
+ error = EINVAL;
+ goto out;
+ }
+ cnp->cn_hash += (unsigned char)*fromcp;
+ *tocp++ = *fromcp++;
+ rem--;
+ }
+ *tocp = '\0';
+ *mdp = md;
+ *dposp = fromcp;
+ len = nfsm_rndup(len)-len;
+ if (len > 0) {
+ if (rem >= len)
+ *dposp += len;
+ else if (error = nfs_adv(mdp, dposp, len, rem))
+ goto out;
+ }
+ ndp->ni_pathlen = tocp - cnp->cn_pnbuf;
+ cnp->cn_nameptr = cnp->cn_pnbuf;
+ /*
+ * Extract and set starting directory.
+ */
+ if (error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
+ nam, &rdonly))
+ goto out;
+ if (dp->v_type != VDIR) {
+ vrele(dp);
+ error = ENOTDIR;
+ goto out;
+ }
+ ndp->ni_startdir = dp;
+ if (rdonly)
+ cnp->cn_flags |= (NOCROSSMOUNT | RDONLY);
+ else
+ cnp->cn_flags |= NOCROSSMOUNT;
+ /*
+ * And call lookup() to do the real work
+ */
+ cnp->cn_proc = p;
+ if (error = lookup(ndp))
+ goto out;
+ /*
+ * Check for encountering a symbolic link
+ */
+ if (cnp->cn_flags & ISSYMLINK) {
+ if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
+ vput(ndp->ni_dvp);
+ else
+ vrele(ndp->ni_dvp);
+ vput(ndp->ni_vp);
+ ndp->ni_vp = NULL;
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * Check for saved name request
+ */
+ if (cnp->cn_flags & (SAVENAME | SAVESTART)) {
+ cnp->cn_flags |= HASBUF;
+ return (0);
+ }
+out:
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ return (error);
+}
+
+/*
+ * A fiddled version of m_adj() that ensures null fill to a long
+ * boundary and only trims off the back end
+ */
+void
+nfsm_adj(mp, len, nul)
+ struct mbuf *mp;
+ register int len;
+ int nul;
+{
+ register struct mbuf *m;
+ register int count, i;
+ register char *cp;
+
+ /*
+ * Trim from tail. Scan the mbuf chain,
+ * calculating its length and finding the last mbuf.
+ * If the adjustment only affects this mbuf, then just
+ * adjust and return. Otherwise, rescan and truncate
+ * after the remaining size.
+ */
+ count = 0;
+ m = mp;
+ for (;;) {
+ count += m->m_len;
+ if (m->m_next == (struct mbuf *)0)
+ break;
+ m = m->m_next;
+ }
+ if (m->m_len > len) {
+ m->m_len -= len;
+ if (nul > 0) {
+ cp = mtod(m, caddr_t)+m->m_len-nul;
+ for (i = 0; i < nul; i++)
+ *cp++ = '\0';
+ }
+ return;
+ }
+ count -= len;
+ if (count < 0)
+ count = 0;
+ /*
+ * Correct length for chain is "count".
+ * Find the mbuf with last data, adjust its length,
+ * and toss data from remaining mbufs on chain.
+ */
+ for (m = mp; m; m = m->m_next) {
+ if (m->m_len >= count) {
+ m->m_len = count;
+ if (nul > 0) {
+ cp = mtod(m, caddr_t)+m->m_len-nul;
+ for (i = 0; i < nul; i++)
+ *cp++ = '\0';
+ }
+ break;
+ }
+ count -= m->m_len;
+ }
+ while (m = m->m_next)
+ m->m_len = 0;
+}
+
+/*
+ * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
+ * - look up fsid in mount list (if not found ret error)
+ * - get vp and export rights by calling VFS_FHTOVP()
+ * - if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
+ * - if not lockflag unlock it with VOP_UNLOCK()
+ */
+int
+nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp)
+ fhandle_t *fhp;
+ int lockflag;
+ struct vnode **vpp;
+ struct ucred *cred;
+ struct nfssvc_sock *slp;
+ struct mbuf *nam;
+ int *rdonlyp;
+{
+ register struct mount *mp;
+ register struct nfsuid *uidp;
+ register int i;
+ struct ucred *credanon;
+ int error, exflags;
+
+ *vpp = (struct vnode *)0;
+ if ((mp = getvfs(&fhp->fh_fsid)) == NULL)
+ return (ESTALE);
+ if (error = VFS_FHTOVP(mp, &fhp->fh_fid, nam, vpp, &exflags, &credanon))
+ return (error);
+ /*
+ * Check/setup credentials.
+ */
+ if (exflags & MNT_EXKERB) {
+ uidp = slp->ns_uidh[NUIDHASH(cred->cr_uid)];
+ while (uidp) {
+ if (uidp->nu_uid == cred->cr_uid)
+ break;
+ uidp = uidp->nu_hnext;
+ }
+ if (uidp) {
+ cred->cr_uid = uidp->nu_cr.cr_uid;
+ for (i = 0; i < uidp->nu_cr.cr_ngroups; i++)
+ cred->cr_groups[i] = uidp->nu_cr.cr_groups[i];
+ } else {
+ vput(*vpp);
+ return (NQNFS_AUTHERR);
+ }
+ } else if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
+ cred->cr_uid = credanon->cr_uid;
+ for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
+ cred->cr_groups[i] = credanon->cr_groups[i];
+ }
+ if (exflags & MNT_EXRDONLY)
+ *rdonlyp = 1;
+ else
+ *rdonlyp = 0;
+ if (!lockflag)
+ VOP_UNLOCK(*vpp);
+ return (0);
+}
+
+/*
+ * This function compares two net addresses by family and returns TRUE
+ * if they are the same host.
+ * If there is any doubt, return FALSE.
+ * The AF_INET family is handled as a special case so that address mbufs
+ * don't need to be saved to store "struct in_addr", which is only 4 bytes.
+ */
+int
+netaddr_match(family, haddr, nam)
+ int family;
+ union nethostaddr *haddr;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *inetaddr;
+
+ switch (family) {
+ case AF_INET:
+ inetaddr = mtod(nam, struct sockaddr_in *);
+ if (inetaddr->sin_family == AF_INET &&
+ inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
+ return (1);
+ break;
+#ifdef ISO
+ case AF_ISO:
+ {
+ register struct sockaddr_iso *isoaddr1, *isoaddr2;
+
+ isoaddr1 = mtod(nam, struct sockaddr_iso *);
+ isoaddr2 = mtod(haddr->had_nam, struct sockaddr_iso *);
+ if (isoaddr1->siso_family == AF_ISO &&
+ isoaddr1->siso_nlen > 0 &&
+ isoaddr1->siso_nlen == isoaddr2->siso_nlen &&
+ SAME_ISOADDR(isoaddr1, isoaddr2))
+ return (1);
+ break;
+ }
+#endif /* ISO */
+ default:
+ break;
+ };
+ return (0);
+}
diff --git a/sys/nfsclient/nfs_vfsops.c b/sys/nfsclient/nfs_vfsops.c
new file mode 100644
index 0000000..1f18676
--- /dev/null
+++ b/sys/nfsclient/nfs_vfsops.c
@@ -0,0 +1,740 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_vfsops.c 8.3 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/ioctl.h>
+#include <sys/signal.h>
+#include <sys/proc.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/kernel.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nfs.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nfsdiskless.h>
+#include <nfs/nqnfs.h>
+
+/*
+ * nfs vfs operations.
+ */
+struct vfsops nfs_vfsops = {
+ nfs_mount,
+ nfs_start,
+ nfs_unmount,
+ nfs_root,
+ nfs_quotactl,
+ nfs_statfs,
+ nfs_sync,
+ nfs_vget,
+ nfs_fhtovp,
+ nfs_vptofh,
+ nfs_init,
+};
+
+/*
+ * This structure must be filled in by a primary bootstrap or bootstrap
+ * server for a diskless/dataless machine. It is initialized below just
+ * to ensure that it is allocated to initialized data (.data not .bss).
+ */
+struct nfs_diskless nfs_diskless = { 0 };
+
+extern u_long nfs_procids[NFS_NPROCS];
+extern u_long nfs_prog, nfs_vers;
+void nfs_disconnect __P((struct nfsmount *));
+void nfsargs_ntoh __P((struct nfs_args *));
+static struct mount *nfs_mountdiskless __P((char *, char *, int,
+ struct sockaddr_in *, struct nfs_args *, register struct vnode **));
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * nfs statfs call
+ */
+int
+nfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ register struct statfs *sbp;
+ struct proc *p;
+{
+ register struct vnode *vp;
+ register struct nfsv2_statfs *sfp;
+ register caddr_t cp;
+ register long t1;
+ caddr_t bpos, dpos, cp2;
+ int error = 0, isnq;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ struct nfsmount *nmp;
+ struct ucred *cred;
+ struct nfsnode *np;
+
+ nmp = VFSTONFS(mp);
+ isnq = (nmp->nm_flag & NFSMNT_NQNFS);
+ if (error = nfs_nget(mp, &nmp->nm_fh, &np))
+ return (error);
+ vp = NFSTOV(np);
+ nfsstats.rpccnt[NFSPROC_STATFS]++;
+ cred = crget();
+ cred->cr_ngroups = 1;
+ nfsm_reqhead(vp, NFSPROC_STATFS, NFSX_FH);
+ nfsm_fhtom(vp);
+ nfsm_request(vp, NFSPROC_STATFS, p, cred);
+ nfsm_dissect(sfp, struct nfsv2_statfs *, NFSX_STATFS(isnq));
+ sbp->f_type = MOUNT_NFS;
+ sbp->f_flags = nmp->nm_flag;
+ sbp->f_iosize = NFS_MAXDGRAMDATA;
+ sbp->f_bsize = fxdr_unsigned(long, sfp->sf_bsize);
+ sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks);
+ sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree);
+ sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail);
+ if (isnq) {
+ sbp->f_files = fxdr_unsigned(long, sfp->sf_files);
+ sbp->f_ffree = fxdr_unsigned(long, sfp->sf_ffree);
+ } else {
+ sbp->f_files = 0;
+ sbp->f_ffree = 0;
+ }
+ if (sbp != &mp->mnt_stat) {
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+ nfsm_reqdone;
+ vrele(vp);
+ crfree(cred);
+ return (error);
+}
+
+/*
+ * Mount a remote root fs via. nfs. This depends on the info in the
+ * nfs_diskless structure that has been filled in properly by some primary
+ * bootstrap.
+ * It goes something like this:
+ * - do enough of "ifconfig" by calling ifioctl() so that the system
+ * can talk to the server
+ * - If nfs_diskless.mygateway is filled in, use that address as
+ * a default gateway.
+ * - hand craft the swap nfs vnode hanging off a fake mount point
+ * if swdevt[0].sw_dev == NODEV
+ * - build the rootfs mount point and call mountnfs() to do the rest.
+ */
+int
+nfs_mountroot()
+{
+ register struct mount *mp;
+ register struct nfs_diskless *nd = &nfs_diskless;
+ struct socket *so;
+ struct vnode *vp;
+ struct proc *p = curproc; /* XXX */
+ int error, i;
+
+ /*
+ * XXX time must be non-zero when we init the interface or else
+ * the arp code will wedge...
+ */
+ if (time.tv_sec == 0)
+ time.tv_sec = 1;
+
+#ifdef notyet
+ /* Set up swap credentials. */
+ proc0.p_ucred->cr_uid = ntohl(nd->swap_ucred.cr_uid);
+ proc0.p_ucred->cr_gid = ntohl(nd->swap_ucred.cr_gid);
+ if ((proc0.p_ucred->cr_ngroups = ntohs(nd->swap_ucred.cr_ngroups)) >
+ NGROUPS)
+ proc0.p_ucred->cr_ngroups = NGROUPS;
+ for (i = 0; i < proc0.p_ucred->cr_ngroups; i++)
+ proc0.p_ucred->cr_groups[i] = ntohl(nd->swap_ucred.cr_groups[i]);
+#endif
+
+ /*
+ * Do enough of ifconfig(8) so that the critical net interface can
+ * talk to the server.
+ */
+ if (error = socreate(nd->myif.ifra_addr.sa_family, &so, SOCK_DGRAM, 0))
+ panic("nfs_mountroot: socreate: %d", error);
+ if (error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, p))
+ panic("nfs_mountroot: SIOCAIFADDR: %d", error);
+ soclose(so);
+
+ /*
+ * If the gateway field is filled in, set it as the default route.
+ */
+ if (nd->mygateway.sin_len != 0) {
+ struct sockaddr_in mask, sin;
+
+ bzero((caddr_t)&mask, sizeof(mask));
+ sin = mask;
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ if (error = rtrequest(RTM_ADD, (struct sockaddr *)&sin,
+ (struct sockaddr *)&nd->mygateway,
+ (struct sockaddr *)&mask,
+ RTF_UP | RTF_GATEWAY, (struct rtentry **)0))
+ panic("nfs_mountroot: RTM_ADD: %d", error);
+ }
+
+ /*
+ * If swapping to an nfs node (indicated by swdevt[0].sw_dev == NODEV):
+ * Create a fake mount point just for the swap vnode so that the
+ * swap file can be on a different server from the rootfs.
+ */
+ if (swdevt[0].sw_dev == NODEV) {
+ nd->swap_args.fh = (nfsv2fh_t *)nd->swap_fh;
+ (void) nfs_mountdiskless(nd->swap_hostnam, "/swap", 0,
+ &nd->swap_saddr, &nd->swap_args, &vp);
+
+ /*
+ * Since the swap file is not the root dir of a file system,
+ * hack it to a regular file.
+ */
+ vp->v_type = VREG;
+ vp->v_flag = 0;
+ swapdev_vp = vp;
+ VREF(vp);
+ swdevt[0].sw_vp = vp;
+ swdevt[0].sw_nblks = ntohl(nd->swap_nblks);
+ } else if (bdevvp(swapdev, &swapdev_vp))
+ panic("nfs_mountroot: can't setup swapdev_vp");
+
+ /*
+ * Create the rootfs mount point.
+ */
+ nd->root_args.fh = (nfsv2fh_t *)nd->root_fh;
+ mp = nfs_mountdiskless(nd->root_hostnam, "/", MNT_RDONLY,
+ &nd->root_saddr, &nd->root_args, &vp);
+
+ if (vfs_lock(mp))
+ panic("nfs_mountroot: vfs_lock");
+ TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
+ mp->mnt_flag |= MNT_ROOTFS;
+ mp->mnt_vnodecovered = NULLVP;
+ vfs_unlock(mp);
+ rootvp = vp;
+
+ /*
+ * This is not really an nfs issue, but it is much easier to
+ * set hostname here and then let the "/etc/rc.xxx" files
+ * mount the right /var based upon its preset value.
+ */
+ bcopy(nd->my_hostnam, hostname, MAXHOSTNAMELEN);
+ hostname[MAXHOSTNAMELEN - 1] = '\0';
+ for (i = 0; i < MAXHOSTNAMELEN; i++)
+ if (hostname[i] == '\0')
+ break;
+ hostnamelen = i;
+ inittodr(ntohl(nd->root_time));
+ return (0);
+}
+
+/*
+ * Internal version of mount system call for diskless setup.
+ */
+static struct mount *
+nfs_mountdiskless(path, which, mountflag, sin, args, vpp)
+ char *path;
+ char *which;
+ int mountflag;
+ struct sockaddr_in *sin;
+ struct nfs_args *args;
+ register struct vnode **vpp;
+{
+ register struct mount *mp;
+ register struct mbuf *m;
+ register int error;
+
+ mp = (struct mount *)malloc((u_long)sizeof(struct mount),
+ M_MOUNT, M_NOWAIT);
+ if (mp == NULL)
+ panic("nfs_mountroot: %s mount malloc", which);
+ bzero((char *)mp, (u_long)sizeof(struct mount));
+ mp->mnt_op = &nfs_vfsops;
+ mp->mnt_flag = mountflag;
+
+ MGET(m, MT_SONAME, M_DONTWAIT);
+ if (m == NULL)
+ panic("nfs_mountroot: %s mount mbuf", which);
+ bcopy((caddr_t)sin, mtod(m, caddr_t), sin->sin_len);
+ m->m_len = sin->sin_len;
+ nfsargs_ntoh(args);
+ if (error = mountnfs(args, mp, m, which, path, vpp))
+ panic("nfs_mountroot: mount %s on %s: %d", path, which, error);
+
+ return (mp);
+}
+
+/*
+ * Convert the integer fields of the nfs_args structure from net byte order
+ * to host byte order. Called by nfs_mountroot() above.
+ */
+void
+nfsargs_ntoh(nfsp)
+ register struct nfs_args *nfsp;
+{
+
+ NTOHL(nfsp->sotype);
+ NTOHL(nfsp->proto);
+ NTOHL(nfsp->flags);
+ NTOHL(nfsp->wsize);
+ NTOHL(nfsp->rsize);
+ NTOHL(nfsp->timeo);
+ NTOHL(nfsp->retrans);
+ NTOHL(nfsp->maxgrouplist);
+ NTOHL(nfsp->readahead);
+ NTOHL(nfsp->leaseterm);
+ NTOHL(nfsp->deadthresh);
+}
+
+/*
+ * VFS Operations.
+ *
+ * mount system call
+ * It seems a bit dumb to copyinstr() the host and path here and then
+ * bcopy() them in mountnfs(), but I wanted to detect errors before
+ * doing the sockargs() call because sockargs() allocates an mbuf and
+ * an error after that means that I have to release the mbuf.
+ */
+/* ARGSUSED */
+int
+nfs_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ int error;
+ struct nfs_args args;
+ struct mbuf *nam;
+ struct vnode *vp;
+ char pth[MNAMELEN], hst[MNAMELEN];
+ u_int len;
+ nfsv2fh_t nfh;
+
+ if (error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args)))
+ return (error);
+ if (error = copyin((caddr_t)args.fh, (caddr_t)&nfh, sizeof (nfsv2fh_t)))
+ return (error);
+ if (error = copyinstr(path, pth, MNAMELEN-1, &len))
+ return (error);
+ bzero(&pth[len], MNAMELEN - len);
+ if (error = copyinstr(args.hostname, hst, MNAMELEN-1, &len))
+ return (error);
+ bzero(&hst[len], MNAMELEN - len);
+ /* sockargs() call must be after above copyin() calls */
+ if (error = sockargs(&nam, (caddr_t)args.addr,
+ args.addrlen, MT_SONAME))
+ return (error);
+ args.fh = &nfh;
+ error = mountnfs(&args, mp, nam, pth, hst, &vp);
+ return (error);
+}
+
+/*
+ * Common code for mount and mountroot
+ */
+int
+mountnfs(argp, mp, nam, pth, hst, vpp)
+ register struct nfs_args *argp;
+ register struct mount *mp;
+ struct mbuf *nam;
+ char *pth, *hst;
+ struct vnode **vpp;
+{
+ register struct nfsmount *nmp;
+ struct nfsnode *np;
+ int error;
+
+ if (mp->mnt_flag & MNT_UPDATE) {
+ nmp = VFSTONFS(mp);
+ /* update paths, file handles, etc, here XXX */
+ m_freem(nam);
+ return (0);
+ } else {
+ MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount),
+ M_NFSMNT, M_WAITOK);
+ bzero((caddr_t)nmp, sizeof (struct nfsmount));
+ mp->mnt_data = (qaddr_t)nmp;
+ }
+ getnewfsid(mp, MOUNT_NFS);
+ nmp->nm_mountp = mp;
+ nmp->nm_flag = argp->flags;
+ if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_MYWRITE)) ==
+ (NFSMNT_NQNFS | NFSMNT_MYWRITE)) {
+ error = EPERM;
+ goto bad;
+ }
+ if (nmp->nm_flag & NFSMNT_NQNFS)
+ /*
+ * We have to set mnt_maxsymlink to a non-zero value so
+ * that COMPAT_43 routines will know that we are setting
+ * the d_type field in directories (and can zero it for
+ * unsuspecting binaries).
+ */
+ mp->mnt_maxsymlinklen = 1;
+ nmp->nm_timeo = NFS_TIMEO;
+ nmp->nm_retry = NFS_RETRANS;
+ nmp->nm_wsize = NFS_WSIZE;
+ nmp->nm_rsize = NFS_RSIZE;
+ nmp->nm_numgrps = NFS_MAXGRPS;
+ nmp->nm_readahead = NFS_DEFRAHEAD;
+ nmp->nm_leaseterm = NQ_DEFLEASE;
+ nmp->nm_deadthresh = NQ_DEADTHRESH;
+ nmp->nm_tnext = (struct nfsnode *)nmp;
+ nmp->nm_tprev = (struct nfsnode *)nmp;
+ nmp->nm_inprog = NULLVP;
+ bcopy((caddr_t)argp->fh, (caddr_t)&nmp->nm_fh, sizeof(nfsv2fh_t));
+ mp->mnt_stat.f_type = MOUNT_NFS;
+ bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
+ bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN);
+ nmp->nm_nam = nam;
+
+ if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
+ nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
+ if (nmp->nm_timeo < NFS_MINTIMEO)
+ nmp->nm_timeo = NFS_MINTIMEO;
+ else if (nmp->nm_timeo > NFS_MAXTIMEO)
+ nmp->nm_timeo = NFS_MAXTIMEO;
+ }
+
+ if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
+ nmp->nm_retry = argp->retrans;
+ if (nmp->nm_retry > NFS_MAXREXMIT)
+ nmp->nm_retry = NFS_MAXREXMIT;
+ }
+
+ if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
+ nmp->nm_wsize = argp->wsize;
+ /* Round down to multiple of blocksize */
+ nmp->nm_wsize &= ~0x1ff;
+ if (nmp->nm_wsize <= 0)
+ nmp->nm_wsize = 512;
+ else if (nmp->nm_wsize > NFS_MAXDATA)
+ nmp->nm_wsize = NFS_MAXDATA;
+ }
+ if (nmp->nm_wsize > MAXBSIZE)
+ nmp->nm_wsize = MAXBSIZE;
+
+ if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
+ nmp->nm_rsize = argp->rsize;
+ /* Round down to multiple of blocksize */
+ nmp->nm_rsize &= ~0x1ff;
+ if (nmp->nm_rsize <= 0)
+ nmp->nm_rsize = 512;
+ else if (nmp->nm_rsize > NFS_MAXDATA)
+ nmp->nm_rsize = NFS_MAXDATA;
+ }
+ if (nmp->nm_rsize > MAXBSIZE)
+ nmp->nm_rsize = MAXBSIZE;
+ if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 &&
+ argp->maxgrouplist <= NFS_MAXGRPS)
+ nmp->nm_numgrps = argp->maxgrouplist;
+ if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0 &&
+ argp->readahead <= NFS_MAXRAHEAD)
+ nmp->nm_readahead = argp->readahead;
+ if ((argp->flags & NFSMNT_LEASETERM) && argp->leaseterm >= 2 &&
+ argp->leaseterm <= NQ_MAXLEASE)
+ nmp->nm_leaseterm = argp->leaseterm;
+ if ((argp->flags & NFSMNT_DEADTHRESH) && argp->deadthresh >= 1 &&
+ argp->deadthresh <= NQ_NEVERDEAD)
+ nmp->nm_deadthresh = argp->deadthresh;
+ /* Set up the sockets and per-host congestion */
+ nmp->nm_sotype = argp->sotype;
+ nmp->nm_soproto = argp->proto;
+
+ /*
+ * For Connection based sockets (TCP,...) defer the connect until
+ * the first request, in case the server is not responding.
+ */
+ if (nmp->nm_sotype == SOCK_DGRAM &&
+ (error = nfs_connect(nmp, (struct nfsreq *)0)))
+ goto bad;
+
+ /*
+ * This is silly, but it has to be set so that vinifod() works.
+ * We do not want to do an nfs_statfs() here since we can get
+ * stuck on a dead server and we are holding a lock on the mount
+ * point.
+ */
+ mp->mnt_stat.f_iosize = NFS_MAXDGRAMDATA;
+ /*
+ * A reference count is needed on the nfsnode representing the
+ * remote root. If this object is not persistent, then backward
+ * traversals of the mount point (i.e. "..") will not work if
+ * the nfsnode gets flushed out of the cache. Ufs does not have
+ * this problem, because one can identify root inodes by their
+ * number == ROOTINO (2).
+ */
+ if (error = nfs_nget(mp, &nmp->nm_fh, &np))
+ goto bad;
+ *vpp = NFSTOV(np);
+
+ return (0);
+bad:
+ nfs_disconnect(nmp);
+ free((caddr_t)nmp, M_NFSMNT);
+ m_freem(nam);
+ return (error);
+}
+
+/*
+ * unmount system call
+ */
+int
+nfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ register struct nfsmount *nmp;
+ struct nfsnode *np;
+ struct vnode *vp;
+ int error, flags = 0;
+ extern int doforce;
+
+ if (mntflags & MNT_FORCE) {
+ if (!doforce || (mp->mnt_flag & MNT_ROOTFS))
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+ nmp = VFSTONFS(mp);
+ /*
+ * Goes something like this..
+ * - Check for activity on the root vnode (other than ourselves).
+ * - Call vflush() to clear out vnodes for this file system,
+ * except for the root vnode.
+ * - Decrement reference on the vnode representing remote root.
+ * - Close the socket
+ * - Free up the data structures
+ */
+ /*
+ * We need to decrement the ref. count on the nfsnode representing
+ * the remote root. See comment in mountnfs(). The VFS unmount()
+ * has done vput on this vnode, otherwise we would get deadlock!
+ */
+ if (error = nfs_nget(mp, &nmp->nm_fh, &np))
+ return(error);
+ vp = NFSTOV(np);
+ if (vp->v_usecount > 2) {
+ vput(vp);
+ return (EBUSY);
+ }
+
+ /*
+ * Must handshake with nqnfs_clientd() if it is active.
+ */
+ nmp->nm_flag |= NFSMNT_DISMINPROG;
+ while (nmp->nm_inprog != NULLVP)
+ (void) tsleep((caddr_t)&lbolt, PSOCK, "nfsdism", 0);
+ if (error = vflush(mp, vp, flags)) {
+ vput(vp);
+ nmp->nm_flag &= ~NFSMNT_DISMINPROG;
+ return (error);
+ }
+
+ /*
+ * We are now committed to the unmount.
+ * For NQNFS, let the server daemon free the nfsmount structure.
+ */
+ if (nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB))
+ nmp->nm_flag |= NFSMNT_DISMNT;
+
+ /*
+ * There are two reference counts to get rid of here.
+ */
+ vrele(vp);
+ vrele(vp);
+ vgone(vp);
+ nfs_disconnect(nmp);
+ m_freem(nmp->nm_nam);
+
+ if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB)) == 0)
+ free((caddr_t)nmp, M_NFSMNT);
+ return (0);
+}
+
+/*
+ * Return root of a filesystem
+ */
+int
+nfs_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ register struct vnode *vp;
+ struct nfsmount *nmp;
+ struct nfsnode *np;
+ int error;
+
+ nmp = VFSTONFS(mp);
+ if (error = nfs_nget(mp, &nmp->nm_fh, &np))
+ return (error);
+ vp = NFSTOV(np);
+ vp->v_type = VDIR;
+ vp->v_flag = VROOT;
+ *vpp = vp;
+ return (0);
+}
+
+extern int syncprt;
+
+/*
+ * Flush out the buffer cache
+ */
+/* ARGSUSED */
+int
+nfs_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+ register struct vnode *vp;
+ int error, allerror = 0;
+
+ /*
+ * Force stale buffer cache information to be flushed.
+ */
+loop:
+ for (vp = mp->mnt_vnodelist.lh_first;
+ vp != NULL;
+ vp = vp->v_mntvnodes.le_next) {
+ /*
+ * If the vnode that we are about to sync is no longer
+ * associated with this mount point, start over.
+ */
+ if (vp->v_mount != mp)
+ goto loop;
+ if (VOP_ISLOCKED(vp) || vp->v_dirtyblkhd.lh_first == NULL)
+ continue;
+ if (vget(vp, 1))
+ goto loop;
+ if (error = VOP_FSYNC(vp, cred, waitfor, p))
+ allerror = error;
+ vput(vp);
+ }
+ return (allerror);
+}
+
+/*
+ * NFS flat namespace lookup.
+ * Currently unsupported.
+ */
+/* ARGSUSED */
+int
+nfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * At this point, this should never happen
+ */
+/* ARGSUSED */
+int
+nfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
+ register struct mount *mp;
+ struct fid *fhp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred **credanonp;
+{
+
+ return (EINVAL);
+}
+
+/*
+ * Vnode pointer to File handle, should never happen either
+ */
+/* ARGSUSED */
+int
+nfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return (EINVAL);
+}
+
+/*
+ * Vfs start routine, a no-op.
+ */
+/* ARGSUSED */
+int
+nfs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+
+ return (0);
+}
+
+/*
+ * Do operations associated with quotas, not supported
+ */
+/* ARGSUSED */
+int
+nfs_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
diff --git a/sys/nfsclient/nfs_vnops.c b/sys/nfsclient/nfs_vnops.c
new file mode 100644
index 0000000..9e76ded
--- /dev/null
+++ b/sys/nfsclient/nfs_vnops.c
@@ -0,0 +1,2541 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_vnops.c 8.5 (Berkeley) 2/13/94
+ */
+
+/*
+ * vnode op calls for sun nfs version 2
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/conf.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/map.h>
+#include <sys/dirent.h>
+
+#include <vm/vm.h>
+
+#include <miscfs/specfs/specdev.h>
+#include <miscfs/fifofs/fifo.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfsmount.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nqnfs.h>
+
+/* Defs */
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Global vfs data structures for nfs
+ */
+int (**nfsv2_vnodeop_p)();
+struct vnodeopv_entry_desc nfsv2_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, nfs_lookup }, /* lookup */
+ { &vop_create_desc, nfs_create }, /* create */
+ { &vop_mknod_desc, nfs_mknod }, /* mknod */
+ { &vop_open_desc, nfs_open }, /* open */
+ { &vop_close_desc, nfs_close }, /* close */
+ { &vop_access_desc, nfs_access }, /* access */
+ { &vop_getattr_desc, nfs_getattr }, /* getattr */
+ { &vop_setattr_desc, nfs_setattr }, /* setattr */
+ { &vop_read_desc, nfs_read }, /* read */
+ { &vop_write_desc, nfs_write }, /* write */
+ { &vop_ioctl_desc, nfs_ioctl }, /* ioctl */
+ { &vop_select_desc, nfs_select }, /* select */
+ { &vop_mmap_desc, nfs_mmap }, /* mmap */
+ { &vop_fsync_desc, nfs_fsync }, /* fsync */
+ { &vop_seek_desc, nfs_seek }, /* seek */
+ { &vop_remove_desc, nfs_remove }, /* remove */
+ { &vop_link_desc, nfs_link }, /* link */
+ { &vop_rename_desc, nfs_rename }, /* rename */
+ { &vop_mkdir_desc, nfs_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, nfs_rmdir }, /* rmdir */
+ { &vop_symlink_desc, nfs_symlink }, /* symlink */
+ { &vop_readdir_desc, nfs_readdir }, /* readdir */
+ { &vop_readlink_desc, nfs_readlink }, /* readlink */
+ { &vop_abortop_desc, nfs_abortop }, /* abortop */
+ { &vop_inactive_desc, nfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, nfs_reclaim }, /* reclaim */
+ { &vop_lock_desc, nfs_lock }, /* lock */
+ { &vop_unlock_desc, nfs_unlock }, /* unlock */
+ { &vop_bmap_desc, nfs_bmap }, /* bmap */
+ { &vop_strategy_desc, nfs_strategy }, /* strategy */
+ { &vop_print_desc, nfs_print }, /* print */
+ { &vop_islocked_desc, nfs_islocked }, /* islocked */
+ { &vop_pathconf_desc, nfs_pathconf }, /* pathconf */
+ { &vop_advlock_desc, nfs_advlock }, /* advlock */
+ { &vop_blkatoff_desc, nfs_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, nfs_valloc }, /* valloc */
+ { &vop_reallocblks_desc, nfs_reallocblks }, /* reallocblks */
+ { &vop_vfree_desc, nfs_vfree }, /* vfree */
+ { &vop_truncate_desc, nfs_truncate }, /* truncate */
+ { &vop_update_desc, nfs_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc nfsv2_vnodeop_opv_desc =
+ { &nfsv2_vnodeop_p, nfsv2_vnodeop_entries };
+
+/*
+ * Special device vnode ops
+ */
+int (**spec_nfsv2nodeop_p)();
+struct vnodeopv_entry_desc spec_nfsv2nodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, spec_lookup }, /* lookup */
+ { &vop_create_desc, spec_create }, /* create */
+ { &vop_mknod_desc, spec_mknod }, /* mknod */
+ { &vop_open_desc, spec_open }, /* open */
+ { &vop_close_desc, nfsspec_close }, /* close */
+ { &vop_access_desc, nfsspec_access }, /* access */
+ { &vop_getattr_desc, nfs_getattr }, /* getattr */
+ { &vop_setattr_desc, nfs_setattr }, /* setattr */
+ { &vop_read_desc, nfsspec_read }, /* read */
+ { &vop_write_desc, nfsspec_write }, /* write */
+ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */
+ { &vop_select_desc, spec_select }, /* select */
+ { &vop_mmap_desc, spec_mmap }, /* mmap */
+ { &vop_fsync_desc, nfs_fsync }, /* fsync */
+ { &vop_seek_desc, spec_seek }, /* seek */
+ { &vop_remove_desc, spec_remove }, /* remove */
+ { &vop_link_desc, spec_link }, /* link */
+ { &vop_rename_desc, spec_rename }, /* rename */
+ { &vop_mkdir_desc, spec_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, spec_rmdir }, /* rmdir */
+ { &vop_symlink_desc, spec_symlink }, /* symlink */
+ { &vop_readdir_desc, spec_readdir }, /* readdir */
+ { &vop_readlink_desc, spec_readlink }, /* readlink */
+ { &vop_abortop_desc, spec_abortop }, /* abortop */
+ { &vop_inactive_desc, nfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, nfs_reclaim }, /* reclaim */
+ { &vop_lock_desc, nfs_lock }, /* lock */
+ { &vop_unlock_desc, nfs_unlock }, /* unlock */
+ { &vop_bmap_desc, spec_bmap }, /* bmap */
+ { &vop_strategy_desc, spec_strategy }, /* strategy */
+ { &vop_print_desc, nfs_print }, /* print */
+ { &vop_islocked_desc, nfs_islocked }, /* islocked */
+ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */
+ { &vop_advlock_desc, spec_advlock }, /* advlock */
+ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, spec_valloc }, /* valloc */
+ { &vop_reallocblks_desc, spec_reallocblks }, /* reallocblks */
+ { &vop_vfree_desc, spec_vfree }, /* vfree */
+ { &vop_truncate_desc, spec_truncate }, /* truncate */
+ { &vop_update_desc, nfs_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc spec_nfsv2nodeop_opv_desc =
+ { &spec_nfsv2nodeop_p, spec_nfsv2nodeop_entries };
+
+#ifdef FIFO
+int (**fifo_nfsv2nodeop_p)();
+struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, fifo_lookup }, /* lookup */
+ { &vop_create_desc, fifo_create }, /* create */
+ { &vop_mknod_desc, fifo_mknod }, /* mknod */
+ { &vop_open_desc, fifo_open }, /* open */
+ { &vop_close_desc, nfsfifo_close }, /* close */
+ { &vop_access_desc, nfsspec_access }, /* access */
+ { &vop_getattr_desc, nfs_getattr }, /* getattr */
+ { &vop_setattr_desc, nfs_setattr }, /* setattr */
+ { &vop_read_desc, nfsfifo_read }, /* read */
+ { &vop_write_desc, nfsfifo_write }, /* write */
+ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */
+ { &vop_select_desc, fifo_select }, /* select */
+ { &vop_mmap_desc, fifo_mmap }, /* mmap */
+ { &vop_fsync_desc, nfs_fsync }, /* fsync */
+ { &vop_seek_desc, fifo_seek }, /* seek */
+ { &vop_remove_desc, fifo_remove }, /* remove */
+ { &vop_link_desc, fifo_link }, /* link */
+ { &vop_rename_desc, fifo_rename }, /* rename */
+ { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */
+ { &vop_symlink_desc, fifo_symlink }, /* symlink */
+ { &vop_readdir_desc, fifo_readdir }, /* readdir */
+ { &vop_readlink_desc, fifo_readlink }, /* readlink */
+ { &vop_abortop_desc, fifo_abortop }, /* abortop */
+ { &vop_inactive_desc, nfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, nfs_reclaim }, /* reclaim */
+ { &vop_lock_desc, nfs_lock }, /* lock */
+ { &vop_unlock_desc, nfs_unlock }, /* unlock */
+ { &vop_bmap_desc, fifo_bmap }, /* bmap */
+ { &vop_strategy_desc, fifo_badop }, /* strategy */
+ { &vop_print_desc, nfs_print }, /* print */
+ { &vop_islocked_desc, nfs_islocked }, /* islocked */
+ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */
+ { &vop_advlock_desc, fifo_advlock }, /* advlock */
+ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, fifo_valloc }, /* valloc */
+ { &vop_reallocblks_desc, fifo_reallocblks }, /* reallocblks */
+ { &vop_vfree_desc, fifo_vfree }, /* vfree */
+ { &vop_truncate_desc, fifo_truncate }, /* truncate */
+ { &vop_update_desc, nfs_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc =
+ { &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries };
+#endif /* FIFO */
+
+void nqnfs_clientlease();
+
+/*
+ * Global variables
+ */
+extern u_long nfs_procids[NFS_NPROCS];
+extern u_long nfs_prog, nfs_vers, nfs_true, nfs_false;
+extern char nfsiobuf[MAXPHYS+NBPG];
+struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
+int nfs_numasync = 0;
+#define DIRHDSIZ (sizeof (struct dirent) - (MAXNAMLEN + 1))
+
+/*
+ * nfs null call from vfs.
+ */
+int
+nfs_null(vp, cred, procp)
+ struct vnode *vp;
+ struct ucred *cred;
+ struct proc *procp;
+{
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb;
+
+ nfsm_reqhead(vp, NFSPROC_NULL, 0);
+ nfsm_request(vp, NFSPROC_NULL, procp, cred);
+ nfsm_reqdone;
+ return (error);
+}
+
+/*
+ * nfs access vnode op.
+ * For nfs, just return ok. File accesses may fail later.
+ * For nqnfs, use the access rpc to check accessibility. If file modes are
+ * changed on the server, accesses might still fail later.
+ */
+int
+nfs_access(ap)
+ struct vop_access_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register u_long *tl;
+ register caddr_t cp;
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ /*
+ * For nqnfs, do an access rpc, otherwise you are stuck emulating
+ * ufs_access() locally using the vattr. This may not be correct,
+ * since the server may apply other access criteria such as
+ * client uid-->server uid mapping that we do not know about, but
+ * this is better than just returning anything that is lying about
+ * in the cache.
+ */
+ if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) {
+ nfsstats.rpccnt[NQNFSPROC_ACCESS]++;
+ nfsm_reqhead(vp, NQNFSPROC_ACCESS, NFSX_FH + 3 * NFSX_UNSIGNED);
+ nfsm_fhtom(vp);
+ nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
+ if (ap->a_mode & VREAD)
+ *tl++ = nfs_true;
+ else
+ *tl++ = nfs_false;
+ if (ap->a_mode & VWRITE)
+ *tl++ = nfs_true;
+ else
+ *tl++ = nfs_false;
+ if (ap->a_mode & VEXEC)
+ *tl = nfs_true;
+ else
+ *tl = nfs_false;
+ nfsm_request(vp, NQNFSPROC_ACCESS, ap->a_p, ap->a_cred);
+ nfsm_reqdone;
+ return (error);
+ } else
+ return (nfsspec_access(ap));
+}
+
+/*
+ * nfs open vnode op
+ * Check to see if the type is ok
+ * and that deletion is not in progress.
+ * For paged in text files, you will need to flush the page cache
+ * if consistency is lost.
+ */
+/* ARGSUSED */
+int
+nfs_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ struct nfsnode *np = VTONFS(vp);
+ struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+ struct vattr vattr;
+ int error;
+
+ if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK)
+ return (EACCES);
+ if (vp->v_flag & VTEXT) {
+ /*
+ * Get a valid lease. If cached data is stale, flush it.
+ */
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ if (NQNFS_CKINVALID(vp, np, NQL_READ)) {
+ do {
+ error = nqnfs_getlease(vp, NQL_READ, ap->a_cred, ap->a_p);
+ } while (error == NQNFS_EXPIRED);
+ if (error)
+ return (error);
+ if (np->n_lrev != np->n_brev ||
+ (np->n_flag & NQNFSNONCACHE)) {
+ if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
+ ap->a_p, 1)) == EINTR)
+ return (error);
+ (void) vnode_pager_uncache(vp);
+ np->n_brev = np->n_lrev;
+ }
+ }
+ } else {
+ if (np->n_flag & NMODIFIED) {
+ if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
+ ap->a_p, 1)) == EINTR)
+ return (error);
+ (void) vnode_pager_uncache(vp);
+ np->n_attrstamp = 0;
+ np->n_direofoffset = 0;
+ if (error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p))
+ return (error);
+ np->n_mtime = vattr.va_mtime.ts_sec;
+ } else {
+ if (error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p))
+ return (error);
+ if (np->n_mtime != vattr.va_mtime.ts_sec) {
+ np->n_direofoffset = 0;
+ if ((error = nfs_vinvalbuf(vp, V_SAVE,
+ ap->a_cred, ap->a_p, 1)) == EINTR)
+ return (error);
+ (void) vnode_pager_uncache(vp);
+ np->n_mtime = vattr.va_mtime.ts_sec;
+ }
+ }
+ }
+ } else if ((nmp->nm_flag & NFSMNT_NQNFS) == 0)
+ np->n_attrstamp = 0; /* For Open/Close consistency */
+ return (0);
+}
+
+/*
+ * nfs close vnode op
+ * For reg files, invalidate any buffer cache entries.
+ */
+/* ARGSUSED */
+int
+nfs_close(ap)
+ struct vop_close_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+ int error = 0;
+
+ if (vp->v_type == VREG) {
+ if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) == 0 &&
+ (np->n_flag & NMODIFIED)) {
+ error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1);
+ np->n_attrstamp = 0;
+ }
+ if (np->n_flag & NWRITEERR) {
+ np->n_flag &= ~NWRITEERR;
+ error = np->n_error;
+ }
+ }
+ return (error);
+}
+
+/*
+ * nfs getattr call from vfs.
+ */
+int
+nfs_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+ register caddr_t cp;
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ /*
+ * Update local times for special files.
+ */
+ if (np->n_flag & (NACC | NUPD))
+ np->n_flag |= NCHG;
+ /*
+ * First look in the cache.
+ */
+ if (nfs_getattrcache(vp, ap->a_vap) == 0)
+ return (0);
+ nfsstats.rpccnt[NFSPROC_GETATTR]++;
+ nfsm_reqhead(vp, NFSPROC_GETATTR, NFSX_FH);
+ nfsm_fhtom(vp);
+ nfsm_request(vp, NFSPROC_GETATTR, ap->a_p, ap->a_cred);
+ nfsm_loadattr(vp, ap->a_vap);
+ nfsm_reqdone;
+ return (error);
+}
+
+/*
+ * nfs setattr call.
+ */
+int
+nfs_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct nfsv2_sattr *sp;
+ register caddr_t cp;
+ register long t1;
+ caddr_t bpos, dpos, cp2;
+ u_long *tl;
+ int error = 0, isnq;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+ register struct vattr *vap = ap->a_vap;
+ u_quad_t frev, tsize = 0;
+
+ if (vap->va_size != VNOVAL || vap->va_mtime.ts_sec != VNOVAL ||
+ vap->va_atime.ts_sec != VNOVAL) {
+ if (vap->va_size != VNOVAL) {
+ if (np->n_flag & NMODIFIED) {
+ if (vap->va_size == 0)
+ error = nfs_vinvalbuf(vp, 0, ap->a_cred,
+ ap->a_p, 1);
+ else
+ error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
+ ap->a_p, 1);
+ if (error)
+ return (error);
+ }
+ tsize = np->n_size;
+ np->n_size = np->n_vattr.va_size = vap->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else if ((np->n_flag & NMODIFIED) &&
+ (error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
+ ap->a_p, 1)) == EINTR)
+ return (error);
+ }
+ nfsstats.rpccnt[NFSPROC_SETATTR]++;
+ isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ nfsm_reqhead(vp, NFSPROC_SETATTR, NFSX_FH+NFSX_SATTR(isnq));
+ nfsm_fhtom(vp);
+ nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq));
+ if (vap->va_mode == (u_short)-1)
+ sp->sa_mode = VNOVAL;
+ else
+ sp->sa_mode = vtonfs_mode(vp->v_type, vap->va_mode);
+ if (vap->va_uid == (uid_t)-1)
+ sp->sa_uid = VNOVAL;
+ else
+ sp->sa_uid = txdr_unsigned(vap->va_uid);
+ if (vap->va_gid == (gid_t)-1)
+ sp->sa_gid = VNOVAL;
+ else
+ sp->sa_gid = txdr_unsigned(vap->va_gid);
+ if (isnq) {
+ txdr_hyper(&vap->va_size, &sp->sa_nqsize);
+ txdr_nqtime(&vap->va_atime, &sp->sa_nqatime);
+ txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime);
+ sp->sa_nqflags = txdr_unsigned(vap->va_flags);
+ sp->sa_nqrdev = VNOVAL;
+ } else {
+ sp->sa_nfssize = txdr_unsigned(vap->va_size);
+ txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
+ txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+ }
+ nfsm_request(vp, NFSPROC_SETATTR, ap->a_p, ap->a_cred);
+ nfsm_loadattr(vp, (struct vattr *)0);
+ if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) &&
+ NQNFS_CKCACHABLE(vp, NQL_WRITE)) {
+ nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
+ fxdr_hyper(tl, &frev);
+ if (frev > np->n_brev)
+ np->n_brev = frev;
+ }
+ nfsm_reqdone;
+ if (error) {
+ np->n_size = np->n_vattr.va_size = tsize;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ }
+ return (error);
+}
+
+/*
+ * nfs lookup call, one step at a time...
+ * First look in cache
+ * If not found, unlock the directory nfsnode and do the rpc
+ */
+int
+nfs_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ register struct componentname *cnp = ap->a_cnp;
+ register struct vnode *dvp = ap->a_dvp;
+ register struct vnode **vpp = ap->a_vpp;
+ register int flags = cnp->cn_flags;
+ register struct vnode *vdp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1, t2;
+ struct nfsmount *nmp;
+ caddr_t bpos, dpos, cp2;
+ time_t reqtime = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ struct vnode *newvp;
+ long len;
+ nfsv2fh_t *fhp;
+ struct nfsnode *np;
+ int lockparent, wantparent, error = 0;
+ int nqlflag = 0, cachable = 0;
+ u_quad_t frev;
+
+ *vpp = NULL;
+ if (dvp->v_type != VDIR)
+ return (ENOTDIR);
+ lockparent = flags & LOCKPARENT;
+ wantparent = flags & (LOCKPARENT|WANTPARENT);
+ nmp = VFSTONFS(dvp->v_mount);
+ np = VTONFS(dvp);
+ if ((error = cache_lookup(dvp, vpp, cnp)) && error != ENOENT) {
+ struct vattr vattr;
+ int vpid;
+
+ vdp = *vpp;
+ vpid = vdp->v_id;
+ /*
+ * See the comment starting `Step through' in ufs/ufs_lookup.c
+ * for an explanation of the locking protocol
+ */
+ if (dvp == vdp) {
+ VREF(vdp);
+ error = 0;
+ } else
+ error = vget(vdp, 1);
+ if (!error) {
+ if (vpid == vdp->v_id) {
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) == 0) {
+ nfsstats.lookupcache_hits++;
+ if (cnp->cn_nameiop != LOOKUP &&
+ (flags & ISLASTCN))
+ cnp->cn_flags |= SAVENAME;
+ return (0);
+ } else if (NQNFS_CKCACHABLE(dvp, NQL_READ)) {
+ if (np->n_lrev != np->n_brev ||
+ (np->n_flag & NMODIFIED)) {
+ np->n_direofoffset = 0;
+ cache_purge(dvp);
+ error = nfs_vinvalbuf(dvp, 0,
+ cnp->cn_cred, cnp->cn_proc,
+ 1);
+ if (error == EINTR)
+ return (error);
+ np->n_brev = np->n_lrev;
+ } else {
+ nfsstats.lookupcache_hits++;
+ if (cnp->cn_nameiop != LOOKUP &&
+ (flags & ISLASTCN))
+ cnp->cn_flags |= SAVENAME;
+ return (0);
+ }
+ }
+ } else if (!VOP_GETATTR(vdp, &vattr, cnp->cn_cred, cnp->cn_proc) &&
+ vattr.va_ctime.ts_sec == VTONFS(vdp)->n_ctime) {
+ nfsstats.lookupcache_hits++;
+ if (cnp->cn_nameiop != LOOKUP &&
+ (flags & ISLASTCN))
+ cnp->cn_flags |= SAVENAME;
+ return (0);
+ }
+ cache_purge(vdp);
+ }
+ vrele(vdp);
+ }
+ *vpp = NULLVP;
+ }
+ error = 0;
+ nfsstats.lookupcache_misses++;
+ nfsstats.rpccnt[NFSPROC_LOOKUP]++;
+ len = cnp->cn_namelen;
+ nfsm_reqhead(dvp, NFSPROC_LOOKUP, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len));
+
+ /*
+ * For nqnfs optionally piggyback a getlease request for the name
+ * being looked up.
+ */
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) &&
+ ((cnp->cn_flags & MAKEENTRY) &&
+ (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))))
+ *tl = txdr_unsigned(nmp->nm_leaseterm);
+ else
+ *tl = 0;
+ }
+ nfsm_fhtom(dvp);
+ nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
+ reqtime = time.tv_sec;
+ nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred);
+nfsmout:
+ if (error) {
+ if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
+ (flags & ISLASTCN) && error == ENOENT)
+ error = EJUSTRETURN;
+ if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
+ cnp->cn_flags |= SAVENAME;
+ return (error);
+ }
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ if (*tl) {
+ nqlflag = fxdr_unsigned(int, *tl);
+ nfsm_dissect(tl, u_long *, 4*NFSX_UNSIGNED);
+ cachable = fxdr_unsigned(int, *tl++);
+ reqtime += fxdr_unsigned(int, *tl++);
+ fxdr_hyper(tl, &frev);
+ } else
+ nqlflag = 0;
+ }
+ nfsm_dissect(fhp, nfsv2fh_t *, NFSX_FH);
+
+ /*
+ * Handle RENAME case...
+ */
+ if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
+ if (!bcmp(np->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) {
+ m_freem(mrep);
+ return (EISDIR);
+ }
+ if (error = nfs_nget(dvp->v_mount, fhp, &np)) {
+ m_freem(mrep);
+ return (error);
+ }
+ newvp = NFSTOV(np);
+ if (error =
+ nfs_loadattrcache(&newvp, &md, &dpos, (struct vattr *)0)) {
+ vrele(newvp);
+ m_freem(mrep);
+ return (error);
+ }
+ *vpp = newvp;
+ m_freem(mrep);
+ cnp->cn_flags |= SAVENAME;
+ return (0);
+ }
+
+ if (!bcmp(np->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) {
+ VREF(dvp);
+ newvp = dvp;
+ } else {
+ if (error = nfs_nget(dvp->v_mount, fhp, &np)) {
+ m_freem(mrep);
+ return (error);
+ }
+ newvp = NFSTOV(np);
+ }
+ if (error = nfs_loadattrcache(&newvp, &md, &dpos, (struct vattr *)0)) {
+ vrele(newvp);
+ m_freem(mrep);
+ return (error);
+ }
+ m_freem(mrep);
+ *vpp = newvp;
+ if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
+ cnp->cn_flags |= SAVENAME;
+ if ((cnp->cn_flags & MAKEENTRY) &&
+ (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))) {
+ if ((nmp->nm_flag & NFSMNT_NQNFS) == 0)
+ np->n_ctime = np->n_vattr.va_ctime.ts_sec;
+ else if (nqlflag && reqtime > time.tv_sec)
+ nqnfs_clientlease(nmp, np, nqlflag, cachable, reqtime,
+ frev);
+ cache_enter(dvp, *vpp, cnp);
+ }
+ return (0);
+}
+
+/*
+ * nfs read call.
+ * Just call nfs_bioread() to do the work.
+ */
+int
+nfs_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+
+ if (vp->v_type != VREG)
+ return (EPERM);
+ return (nfs_bioread(vp, ap->a_uio, ap->a_ioflag, ap->a_cred));
+}
+
+/*
+ * nfs readlink call
+ */
+int
+nfs_readlink(ap)
+ struct vop_readlink_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+
+ if (vp->v_type != VLNK)
+ return (EPERM);
+ return (nfs_bioread(vp, ap->a_uio, 0, ap->a_cred));
+}
+
+/*
+ * Do a readlink rpc.
+ * Called by nfs_doio() from below the buffer cache.
+ */
+int
+nfs_readlinkrpc(vp, uiop, cred)
+ register struct vnode *vp;
+ struct uio *uiop;
+ struct ucred *cred;
+{
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1;
+ caddr_t bpos, dpos, cp2;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ long len;
+
+ nfsstats.rpccnt[NFSPROC_READLINK]++;
+ nfsm_reqhead(vp, NFSPROC_READLINK, NFSX_FH);
+ nfsm_fhtom(vp);
+ nfsm_request(vp, NFSPROC_READLINK, uiop->uio_procp, cred);
+ nfsm_strsiz(len, NFS_MAXPATHLEN);
+ nfsm_mtouio(uiop, len);
+ nfsm_reqdone;
+ return (error);
+}
+
+/*
+ * nfs read rpc call
+ * Ditto above
+ */
+int
+nfs_readrpc(vp, uiop, cred)
+ register struct vnode *vp;
+ struct uio *uiop;
+ struct ucred *cred;
+{
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1;
+ caddr_t bpos, dpos, cp2;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ struct nfsmount *nmp;
+ long len, retlen, tsiz;
+
+ nmp = VFSTONFS(vp->v_mount);
+ tsiz = uiop->uio_resid;
+ if (uiop->uio_offset + tsiz > 0xffffffff &&
+ (nmp->nm_flag & NFSMNT_NQNFS) == 0)
+ return (EFBIG);
+ while (tsiz > 0) {
+ nfsstats.rpccnt[NFSPROC_READ]++;
+ len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz;
+ nfsm_reqhead(vp, NFSPROC_READ, NFSX_FH+NFSX_UNSIGNED*3);
+ nfsm_fhtom(vp);
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED*3);
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ txdr_hyper(&uiop->uio_offset, tl);
+ *(tl + 2) = txdr_unsigned(len);
+ } else {
+ *tl++ = txdr_unsigned(uiop->uio_offset);
+ *tl++ = txdr_unsigned(len);
+ *tl = 0;
+ }
+ nfsm_request(vp, NFSPROC_READ, uiop->uio_procp, cred);
+ nfsm_loadattr(vp, (struct vattr *)0);
+ nfsm_strsiz(retlen, nmp->nm_rsize);
+ nfsm_mtouio(uiop, retlen);
+ m_freem(mrep);
+ if (retlen < len)
+ tsiz = 0;
+ else
+ tsiz -= len;
+ }
+nfsmout:
+ return (error);
+}
+
+/*
+ * nfs write call
+ */
+int
+nfs_writerpc(vp, uiop, cred, ioflags)
+ register struct vnode *vp;
+ struct uio *uiop;
+ struct ucred *cred;
+ int ioflags;
+{
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1;
+ caddr_t bpos, dpos, cp2;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ struct nfsmount *nmp;
+ struct nfsnode *np = VTONFS(vp);
+ u_quad_t frev;
+ long len, tsiz;
+
+ nmp = VFSTONFS(vp->v_mount);
+ tsiz = uiop->uio_resid;
+ if (uiop->uio_offset + tsiz > 0xffffffff &&
+ (nmp->nm_flag & NFSMNT_NQNFS) == 0)
+ return (EFBIG);
+ while (tsiz > 0) {
+ nfsstats.rpccnt[NFSPROC_WRITE]++;
+ len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz;
+ nfsm_reqhead(vp, NFSPROC_WRITE,
+ NFSX_FH+NFSX_UNSIGNED*4+nfsm_rndup(len));
+ nfsm_fhtom(vp);
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED * 4);
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ txdr_hyper(&uiop->uio_offset, tl);
+ tl += 2;
+ if (ioflags & IO_APPEND)
+ *tl++ = txdr_unsigned(1);
+ else
+ *tl++ = 0;
+ } else {
+ *++tl = txdr_unsigned(uiop->uio_offset);
+ tl += 2;
+ }
+ *tl = txdr_unsigned(len);
+ nfsm_uiotom(uiop, len);
+ nfsm_request(vp, NFSPROC_WRITE, uiop->uio_procp, cred);
+ nfsm_loadattr(vp, (struct vattr *)0);
+ if (nmp->nm_flag & NFSMNT_MYWRITE)
+ VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.va_mtime.ts_sec;
+ else if ((nmp->nm_flag & NFSMNT_NQNFS) &&
+ NQNFS_CKCACHABLE(vp, NQL_WRITE)) {
+ nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
+ fxdr_hyper(tl, &frev);
+ if (frev > np->n_brev)
+ np->n_brev = frev;
+ }
+ m_freem(mrep);
+ tsiz -= len;
+ }
+nfsmout:
+ if (error)
+ uiop->uio_resid = tsiz;
+ return (error);
+}
+
+/*
+ * nfs mknod call
+ * This is a kludge. Use a create rpc but with the IFMT bits of the mode
+ * set to specify the file type and the size field for rdev.
+ */
+/* ARGSUSED */
+int
+nfs_mknod(ap)
+ struct vop_mknod_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ register struct vnode *dvp = ap->a_dvp;
+ register struct vattr *vap = ap->a_vap;
+ register struct componentname *cnp = ap->a_cnp;
+ register struct nfsv2_sattr *sp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1, t2;
+ struct vnode *newvp = 0;
+ struct vattr vattr;
+ char *cp2;
+ caddr_t bpos, dpos;
+ int error = 0, isnq;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ u_long rdev;
+
+ isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ if (vap->va_type == VCHR || vap->va_type == VBLK)
+ rdev = txdr_unsigned(vap->va_rdev);
+#ifdef FIFO
+ else if (vap->va_type == VFIFO)
+ rdev = 0xffffffff;
+#endif /* FIFO */
+ else {
+ VOP_ABORTOP(dvp, cnp);
+ vput(dvp);
+ return (EOPNOTSUPP);
+ }
+ if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
+ VOP_ABORTOP(dvp, cnp);
+ vput(dvp);
+ return (error);
+ }
+ nfsstats.rpccnt[NFSPROC_CREATE]++;
+ nfsm_reqhead(dvp, NFSPROC_CREATE,
+ NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)+NFSX_SATTR(isnq));
+ nfsm_fhtom(dvp);
+ nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq));
+ sp->sa_mode = vtonfs_mode(vap->va_type, vap->va_mode);
+ sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
+ sp->sa_gid = txdr_unsigned(vattr.va_gid);
+ if (isnq) {
+ sp->sa_nqrdev = rdev;
+ sp->sa_nqflags = 0;
+ txdr_nqtime(&vap->va_atime, &sp->sa_nqatime);
+ txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime);
+ } else {
+ sp->sa_nfssize = rdev;
+ txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
+ txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+ }
+ nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred);
+ nfsm_mtofh(dvp, newvp);
+ nfsm_reqdone;
+ if (!error && (cnp->cn_flags & MAKEENTRY))
+ cache_enter(dvp, newvp, cnp);
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ VTONFS(dvp)->n_flag |= NMODIFIED;
+ VTONFS(dvp)->n_attrstamp = 0;
+ vrele(dvp);
+ return (error);
+}
+
+/*
+ * nfs file create call
+ */
+int
+nfs_create(ap)
+ struct vop_create_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ register struct vnode *dvp = ap->a_dvp;
+ register struct vattr *vap = ap->a_vap;
+ register struct componentname *cnp = ap->a_cnp;
+ register struct nfsv2_sattr *sp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1, t2;
+ caddr_t bpos, dpos, cp2;
+ int error = 0, isnq;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ struct vattr vattr;
+
+ if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
+ VOP_ABORTOP(dvp, cnp);
+ vput(dvp);
+ return (error);
+ }
+ nfsstats.rpccnt[NFSPROC_CREATE]++;
+ isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ nfsm_reqhead(dvp, NFSPROC_CREATE,
+ NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)+NFSX_SATTR(isnq));
+ nfsm_fhtom(dvp);
+ nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq));
+ sp->sa_mode = vtonfs_mode(vap->va_type, vap->va_mode);
+ sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
+ sp->sa_gid = txdr_unsigned(vattr.va_gid);
+ if (isnq) {
+ u_quad_t qval = 0;
+
+ txdr_hyper(&qval, &sp->sa_nqsize);
+ sp->sa_nqflags = 0;
+ sp->sa_nqrdev = -1;
+ txdr_nqtime(&vap->va_atime, &sp->sa_nqatime);
+ txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime);
+ } else {
+ sp->sa_nfssize = 0;
+ txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
+ txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+ }
+ nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred);
+ nfsm_mtofh(dvp, *ap->a_vpp);
+ nfsm_reqdone;
+ if (!error && (cnp->cn_flags & MAKEENTRY))
+ cache_enter(dvp, *ap->a_vpp, cnp);
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ VTONFS(dvp)->n_flag |= NMODIFIED;
+ VTONFS(dvp)->n_attrstamp = 0;
+ vrele(dvp);
+ return (error);
+}
+
+/*
+ * nfs file remove call
+ * To try and make nfs semantics closer to ufs semantics, a file that has
+ * other processes using the vnode is renamed instead of removed and then
+ * removed later on the last close.
+ * - If v_usecount > 1
+ * If a rename is not already in the works
+ * call nfs_sillyrename() to set it up
+ * else
+ * do the remove rpc
+ */
+int
+nfs_remove(ap)
+ struct vop_remove_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode * a_dvp;
+ struct vnode * a_vp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct vnode *dvp = ap->a_dvp;
+ register struct componentname *cnp = ap->a_cnp;
+ register struct nfsnode *np = VTONFS(vp);
+ register u_long *tl;
+ register caddr_t cp;
+ register long t2;
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ if (vp->v_usecount > 1) {
+ if (!np->n_sillyrename)
+ error = nfs_sillyrename(dvp, vp, cnp);
+ } else {
+ /*
+ * Purge the name cache so that the chance of a lookup for
+ * the name succeeding while the remove is in progress is
+ * minimized. Without node locking it can still happen, such
+ * that an I/O op returns ESTALE, but since you get this if
+ * another host removes the file..
+ */
+ cache_purge(vp);
+ /*
+ * Throw away biocache buffers. Mainly to avoid
+ * unnecessary delayed writes.
+ */
+ error = nfs_vinvalbuf(vp, 0, cnp->cn_cred, cnp->cn_proc, 1);
+ if (error == EINTR)
+ return (error);
+ /* Do the rpc */
+ nfsstats.rpccnt[NFSPROC_REMOVE]++;
+ nfsm_reqhead(dvp, NFSPROC_REMOVE,
+ NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen));
+ nfsm_fhtom(dvp);
+ nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_request(dvp, NFSPROC_REMOVE, cnp->cn_proc, cnp->cn_cred);
+ nfsm_reqdone;
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ VTONFS(dvp)->n_flag |= NMODIFIED;
+ VTONFS(dvp)->n_attrstamp = 0;
+ /*
+ * Kludge City: If the first reply to the remove rpc is lost..
+ * the reply to the retransmitted request will be ENOENT
+ * since the file was in fact removed
+ * Therefore, we cheat and return success.
+ */
+ if (error == ENOENT)
+ error = 0;
+ }
+ np->n_attrstamp = 0;
+ vrele(dvp);
+ vrele(vp);
+ return (error);
+}
+
+/*
+ * nfs file remove rpc called from nfs_inactive
+ */
+int
+nfs_removeit(sp)
+ register struct sillyrename *sp;
+{
+ register u_long *tl;
+ register caddr_t cp;
+ register long t2;
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ nfsstats.rpccnt[NFSPROC_REMOVE]++;
+ nfsm_reqhead(sp->s_dvp, NFSPROC_REMOVE,
+ NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(sp->s_namlen));
+ nfsm_fhtom(sp->s_dvp);
+ nfsm_strtom(sp->s_name, sp->s_namlen, NFS_MAXNAMLEN);
+ nfsm_request(sp->s_dvp, NFSPROC_REMOVE, NULL, sp->s_cred);
+ nfsm_reqdone;
+ VTONFS(sp->s_dvp)->n_flag |= NMODIFIED;
+ VTONFS(sp->s_dvp)->n_attrstamp = 0;
+ return (error);
+}
+
+/*
+ * nfs file rename call
+ */
+int
+nfs_rename(ap)
+ struct vop_rename_args /* {
+ struct vnode *a_fdvp;
+ struct vnode *a_fvp;
+ struct componentname *a_fcnp;
+ struct vnode *a_tdvp;
+ struct vnode *a_tvp;
+ struct componentname *a_tcnp;
+ } */ *ap;
+{
+ register struct vnode *fvp = ap->a_fvp;
+ register struct vnode *tvp = ap->a_tvp;
+ register struct vnode *fdvp = ap->a_fdvp;
+ register struct vnode *tdvp = ap->a_tdvp;
+ register struct componentname *tcnp = ap->a_tcnp;
+ register struct componentname *fcnp = ap->a_fcnp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t2;
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ /* Check for cross-device rename */
+ if ((fvp->v_mount != tdvp->v_mount) ||
+ (tvp && (fvp->v_mount != tvp->v_mount))) {
+ error = EXDEV;
+ goto out;
+ }
+
+
+ nfsstats.rpccnt[NFSPROC_RENAME]++;
+ nfsm_reqhead(fdvp, NFSPROC_RENAME,
+ (NFSX_FH+NFSX_UNSIGNED)*2+nfsm_rndup(fcnp->cn_namelen)+
+ nfsm_rndup(fcnp->cn_namelen)); /* or fcnp->cn_cred?*/
+ nfsm_fhtom(fdvp);
+ nfsm_strtom(fcnp->cn_nameptr, fcnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_fhtom(tdvp);
+ nfsm_strtom(tcnp->cn_nameptr, tcnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_request(fdvp, NFSPROC_RENAME, tcnp->cn_proc, tcnp->cn_cred);
+ nfsm_reqdone;
+ VTONFS(fdvp)->n_flag |= NMODIFIED;
+ VTONFS(fdvp)->n_attrstamp = 0;
+ VTONFS(tdvp)->n_flag |= NMODIFIED;
+ VTONFS(tdvp)->n_attrstamp = 0;
+ if (fvp->v_type == VDIR) {
+ if (tvp != NULL && tvp->v_type == VDIR)
+ cache_purge(tdvp);
+ cache_purge(fdvp);
+ }
+out:
+ if (tdvp == tvp)
+ vrele(tdvp);
+ else
+ vput(tdvp);
+ if (tvp)
+ vput(tvp);
+ vrele(fdvp);
+ vrele(fvp);
+ /*
+ * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
+ */
+ if (error == ENOENT)
+ error = 0;
+ return (error);
+}
+
+/*
+ * nfs file rename rpc called from nfs_remove() above
+ */
+int
+nfs_renameit(sdvp, scnp, sp)
+ struct vnode *sdvp;
+ struct componentname *scnp;
+ register struct sillyrename *sp;
+{
+ register u_long *tl;
+ register caddr_t cp;
+ register long t2;
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ nfsstats.rpccnt[NFSPROC_RENAME]++;
+ nfsm_reqhead(sdvp, NFSPROC_RENAME,
+ (NFSX_FH+NFSX_UNSIGNED)*2+nfsm_rndup(scnp->cn_namelen)+
+ nfsm_rndup(sp->s_namlen));
+ nfsm_fhtom(sdvp);
+ nfsm_strtom(scnp->cn_nameptr, scnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_fhtom(sdvp);
+ nfsm_strtom(sp->s_name, sp->s_namlen, NFS_MAXNAMLEN);
+ nfsm_request(sdvp, NFSPROC_RENAME, scnp->cn_proc, scnp->cn_cred);
+ nfsm_reqdone;
+ FREE(scnp->cn_pnbuf, M_NAMEI);
+ VTONFS(sdvp)->n_flag |= NMODIFIED;
+ VTONFS(sdvp)->n_attrstamp = 0;
+ return (error);
+}
+
+/*
+ * nfs hard link create call
+ */
+int
+nfs_link(ap)
+ struct vop_link_args /* {
+ struct vnode *a_vp;
+ struct vnode *a_tdvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct vnode *tdvp = ap->a_tdvp;
+ register struct componentname *cnp = ap->a_cnp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t2;
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ if (vp->v_mount != tdvp->v_mount) {
+ /*VOP_ABORTOP(vp, cnp);*/
+ if (tdvp == vp)
+ vrele(vp);
+ else
+ vput(vp);
+ return (EXDEV);
+ }
+
+ nfsstats.rpccnt[NFSPROC_LINK]++;
+ nfsm_reqhead(tdvp, NFSPROC_LINK,
+ NFSX_FH*2+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen));
+ nfsm_fhtom(tdvp);
+ nfsm_fhtom(vp);
+ nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_request(tdvp, NFSPROC_LINK, cnp->cn_proc, cnp->cn_cred);
+ nfsm_reqdone;
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ VTONFS(tdvp)->n_attrstamp = 0;
+ VTONFS(tdvp)->n_flag |= NMODIFIED;
+ VTONFS(vp)->n_attrstamp = 0;
+ vrele(vp);
+ /*
+ * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
+ */
+ if (error == EEXIST)
+ error = 0;
+ return (error);
+}
+
+/*
+ * nfs symbolic link create call
+ */
+/* start here */
+int
+nfs_symlink(ap)
+ struct vop_symlink_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ char *a_target;
+ } */ *ap;
+{
+ register struct vnode *dvp = ap->a_dvp;
+ register struct vattr *vap = ap->a_vap;
+ register struct componentname *cnp = ap->a_cnp;
+ register struct nfsv2_sattr *sp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t2;
+ caddr_t bpos, dpos;
+ int slen, error = 0, isnq;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ nfsstats.rpccnt[NFSPROC_SYMLINK]++;
+ slen = strlen(ap->a_target);
+ isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ nfsm_reqhead(dvp, NFSPROC_SYMLINK, NFSX_FH+2*NFSX_UNSIGNED+
+ nfsm_rndup(cnp->cn_namelen)+nfsm_rndup(slen)+NFSX_SATTR(isnq));
+ nfsm_fhtom(dvp);
+ nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN);
+ nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq));
+ sp->sa_mode = vtonfs_mode(VLNK, vap->va_mode);
+ sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
+ sp->sa_gid = txdr_unsigned(cnp->cn_cred->cr_gid);
+ if (isnq) {
+ quad_t qval = -1;
+
+ txdr_hyper(&qval, &sp->sa_nqsize);
+ sp->sa_nqflags = 0;
+ txdr_nqtime(&vap->va_atime, &sp->sa_nqatime);
+ txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime);
+ } else {
+ sp->sa_nfssize = -1;
+ txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
+ txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+ }
+ nfsm_request(dvp, NFSPROC_SYMLINK, cnp->cn_proc, cnp->cn_cred);
+ nfsm_reqdone;
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ VTONFS(dvp)->n_flag |= NMODIFIED;
+ VTONFS(dvp)->n_attrstamp = 0;
+ vrele(dvp);
+ /*
+ * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
+ */
+ if (error == EEXIST)
+ error = 0;
+ return (error);
+}
+
+/*
+ * nfs make dir call
+ */
+int
+nfs_mkdir(ap)
+ struct vop_mkdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ register struct vnode *dvp = ap->a_dvp;
+ register struct vattr *vap = ap->a_vap;
+ register struct componentname *cnp = ap->a_cnp;
+ register struct vnode **vpp = ap->a_vpp;
+ register struct nfsv2_sattr *sp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1, t2;
+ register int len;
+ caddr_t bpos, dpos, cp2;
+ int error = 0, firsttry = 1, isnq;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ struct vattr vattr;
+
+ if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
+ VOP_ABORTOP(dvp, cnp);
+ vput(dvp);
+ return (error);
+ }
+ len = cnp->cn_namelen;
+ isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ nfsstats.rpccnt[NFSPROC_MKDIR]++;
+ nfsm_reqhead(dvp, NFSPROC_MKDIR,
+ NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len)+NFSX_SATTR(isnq));
+ nfsm_fhtom(dvp);
+ nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
+ nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq));
+ sp->sa_mode = vtonfs_mode(VDIR, vap->va_mode);
+ sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
+ sp->sa_gid = txdr_unsigned(vattr.va_gid);
+ if (isnq) {
+ quad_t qval = -1;
+
+ txdr_hyper(&qval, &sp->sa_nqsize);
+ sp->sa_nqflags = 0;
+ txdr_nqtime(&vap->va_atime, &sp->sa_nqatime);
+ txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime);
+ } else {
+ sp->sa_nfssize = -1;
+ txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
+ txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+ }
+ nfsm_request(dvp, NFSPROC_MKDIR, cnp->cn_proc, cnp->cn_cred);
+ nfsm_mtofh(dvp, *vpp);
+ nfsm_reqdone;
+ VTONFS(dvp)->n_flag |= NMODIFIED;
+ VTONFS(dvp)->n_attrstamp = 0;
+ /*
+ * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
+ * if we can succeed in looking up the directory.
+ * "firsttry" is necessary since the macros may "goto nfsmout" which
+ * is above the if on errors. (Ugh)
+ */
+ if (error == EEXIST && firsttry) {
+ firsttry = 0;
+ error = 0;
+ nfsstats.rpccnt[NFSPROC_LOOKUP]++;
+ *vpp = NULL;
+ nfsm_reqhead(dvp, NFSPROC_LOOKUP,
+ NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len));
+ nfsm_fhtom(dvp);
+ nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
+ nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred);
+ nfsm_mtofh(dvp, *vpp);
+ if ((*vpp)->v_type != VDIR) {
+ vput(*vpp);
+ error = EEXIST;
+ }
+ m_freem(mrep);
+ }
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ vrele(dvp);
+ return (error);
+}
+
+/*
+ * nfs remove directory call
+ */
+int
+nfs_rmdir(ap)
+ struct vop_rmdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct vnode *dvp = ap->a_dvp;
+ register struct componentname *cnp = ap->a_cnp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t2;
+ caddr_t bpos, dpos;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+ if (dvp == vp) {
+ vrele(dvp);
+ vrele(dvp);
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ return (EINVAL);
+ }
+ nfsstats.rpccnt[NFSPROC_RMDIR]++;
+ nfsm_reqhead(dvp, NFSPROC_RMDIR,
+ NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen));
+ nfsm_fhtom(dvp);
+ nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
+ nfsm_request(dvp, NFSPROC_RMDIR, cnp->cn_proc, cnp->cn_cred);
+ nfsm_reqdone;
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ VTONFS(dvp)->n_flag |= NMODIFIED;
+ VTONFS(dvp)->n_attrstamp = 0;
+ cache_purge(dvp);
+ cache_purge(vp);
+ vrele(vp);
+ vrele(dvp);
+ /*
+ * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
+ */
+ if (error == ENOENT)
+ error = 0;
+ return (error);
+}
+
+/*
+ * nfs readdir call
+ * Although cookie is defined as opaque, I translate it to/from net byte
+ * order so that it looks more sensible. This appears consistent with the
+ * Ultrix implementation of NFS.
+ */
+int
+nfs_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+ register struct uio *uio = ap->a_uio;
+ int tresid, error;
+ struct vattr vattr;
+
+ if (vp->v_type != VDIR)
+ return (EPERM);
+ /*
+ * First, check for hit on the EOF offset cache
+ */
+ if (uio->uio_offset != 0 && uio->uio_offset == np->n_direofoffset &&
+ (np->n_flag & NMODIFIED) == 0) {
+ if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) {
+ if (NQNFS_CKCACHABLE(vp, NQL_READ)) {
+ nfsstats.direofcache_hits++;
+ return (0);
+ }
+ } else if (VOP_GETATTR(vp, &vattr, ap->a_cred, uio->uio_procp) == 0 &&
+ np->n_mtime == vattr.va_mtime.ts_sec) {
+ nfsstats.direofcache_hits++;
+ return (0);
+ }
+ }
+
+ /*
+ * Call nfs_bioread() to do the real work.
+ */
+ tresid = uio->uio_resid;
+ error = nfs_bioread(vp, uio, 0, ap->a_cred);
+
+ if (!error && uio->uio_resid == tresid)
+ nfsstats.direofcache_misses++;
+ return (error);
+}
+
+/*
+ * Readdir rpc call.
+ * Called from below the buffer cache by nfs_doio().
+ */
+int
+nfs_readdirrpc(vp, uiop, cred)
+ register struct vnode *vp;
+ struct uio *uiop;
+ struct ucred *cred;
+{
+ register long len;
+ register struct dirent *dp = 0;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1;
+ long tlen, lastlen = 0;
+ caddr_t bpos, dpos, cp2;
+ int error = 0;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ struct mbuf *md2;
+ caddr_t dpos2;
+ int siz;
+ int more_dirs = 1;
+ u_long off, savoff = 0;
+ struct dirent *savdp = 0;
+ struct nfsmount *nmp;
+ struct nfsnode *np = VTONFS(vp);
+ long tresid;
+
+ nmp = VFSTONFS(vp->v_mount);
+ tresid = uiop->uio_resid;
+ /*
+ * Loop around doing readdir rpc's of size uio_resid or nm_rsize,
+ * whichever is smaller, truncated to a multiple of NFS_DIRBLKSIZ.
+ * The stopping criteria is EOF or buffer full.
+ */
+ while (more_dirs && uiop->uio_resid >= NFS_DIRBLKSIZ) {
+ nfsstats.rpccnt[NFSPROC_READDIR]++;
+ nfsm_reqhead(vp, NFSPROC_READDIR,
+ NFSX_FH + 2 * NFSX_UNSIGNED);
+ nfsm_fhtom(vp);
+ nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
+ off = (u_long)uiop->uio_offset;
+ *tl++ = txdr_unsigned(off);
+ *tl = txdr_unsigned(((uiop->uio_resid > nmp->nm_rsize) ?
+ nmp->nm_rsize : uiop->uio_resid) & ~(NFS_DIRBLKSIZ-1));
+ nfsm_request(vp, NFSPROC_READDIR, uiop->uio_procp, cred);
+ siz = 0;
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ more_dirs = fxdr_unsigned(int, *tl);
+
+ /* Save the position so that we can do nfsm_mtouio() later */
+ dpos2 = dpos;
+ md2 = md;
+
+ /* loop thru the dir entries, doctoring them to 4bsd form */
+#ifdef lint
+ dp = (struct dirent *)0;
+#endif /* lint */
+ while (more_dirs && siz < uiop->uio_resid) {
+ savoff = off; /* Hold onto offset and dp */
+ savdp = dp;
+ nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+ dp = (struct dirent *)tl;
+ dp->d_fileno = fxdr_unsigned(u_long, *tl++);
+ len = fxdr_unsigned(int, *tl);
+ if (len <= 0 || len > NFS_MAXNAMLEN) {
+ error = EBADRPC;
+ m_freem(mrep);
+ goto nfsmout;
+ }
+ dp->d_namlen = (u_char)len;
+ dp->d_type = DT_UNKNOWN;
+ nfsm_adv(len); /* Point past name */
+ tlen = nfsm_rndup(len);
+ /*
+ * This should not be necessary, but some servers have
+ * broken XDR such that these bytes are not null filled.
+ */
+ if (tlen != len) {
+ *dpos = '\0'; /* Null-terminate */
+ nfsm_adv(tlen - len);
+ len = tlen;
+ }
+ nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+ off = fxdr_unsigned(u_long, *tl);
+ *tl++ = 0; /* Ensures null termination of name */
+ more_dirs = fxdr_unsigned(int, *tl);
+ dp->d_reclen = len + 4 * NFSX_UNSIGNED;
+ siz += dp->d_reclen;
+ }
+ /*
+ * If at end of rpc data, get the eof boolean
+ */
+ if (!more_dirs) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ more_dirs = (fxdr_unsigned(int, *tl) == 0);
+
+ /*
+ * If at EOF, cache directory offset
+ */
+ if (!more_dirs)
+ np->n_direofoffset = off;
+ }
+ /*
+ * If there is too much to fit in the data buffer, use savoff and
+ * savdp to trim off the last record.
+ * --> we are not at eof
+ */
+ if (siz > uiop->uio_resid) {
+ off = savoff;
+ siz -= dp->d_reclen;
+ dp = savdp;
+ more_dirs = 0; /* Paranoia */
+ }
+ if (siz > 0) {
+ lastlen = dp->d_reclen;
+ md = md2;
+ dpos = dpos2;
+ nfsm_mtouio(uiop, siz);
+ uiop->uio_offset = (off_t)off;
+ } else
+ more_dirs = 0; /* Ugh, never happens, but in case.. */
+ m_freem(mrep);
+ }
+ /*
+ * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ
+ * by increasing d_reclen for the last record.
+ */
+ if (uiop->uio_resid < tresid) {
+ len = uiop->uio_resid & (NFS_DIRBLKSIZ - 1);
+ if (len > 0) {
+ dp = (struct dirent *)
+ (uiop->uio_iov->iov_base - lastlen);
+ dp->d_reclen += len;
+ uiop->uio_iov->iov_base += len;
+ uiop->uio_iov->iov_len -= len;
+ uiop->uio_resid -= len;
+ }
+ }
+nfsmout:
+ return (error);
+}
+
+/*
+ * Nqnfs readdir_and_lookup RPC. Used in place of nfs_readdirrpc().
+ */
+int
+nfs_readdirlookrpc(vp, uiop, cred)
+ struct vnode *vp;
+ register struct uio *uiop;
+ struct ucred *cred;
+{
+ register int len;
+ register struct dirent *dp = 0;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1;
+ caddr_t bpos, dpos, cp2;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ struct nameidata nami, *ndp = &nami;
+ struct componentname *cnp = &ndp->ni_cnd;
+ u_long off, endoff = 0, fileno;
+ time_t reqtime, ltime = 0;
+ struct nfsmount *nmp;
+ struct nfsnode *np;
+ struct vnode *newvp;
+ nfsv2fh_t *fhp;
+ u_quad_t frev;
+ int error = 0, tlen, more_dirs = 1, tresid, doit, bigenough, i;
+ int cachable = 0;
+
+ if (uiop->uio_iovcnt != 1)
+ panic("nfs rdirlook");
+ nmp = VFSTONFS(vp->v_mount);
+ tresid = uiop->uio_resid;
+ ndp->ni_dvp = vp;
+ newvp = NULLVP;
+ /*
+ * Loop around doing readdir rpc's of size uio_resid or nm_rsize,
+ * whichever is smaller, truncated to a multiple of NFS_DIRBLKSIZ.
+ * The stopping criteria is EOF or buffer full.
+ */
+ while (more_dirs && uiop->uio_resid >= NFS_DIRBLKSIZ) {
+ nfsstats.rpccnt[NQNFSPROC_READDIRLOOK]++;
+ nfsm_reqhead(vp, NQNFSPROC_READDIRLOOK,
+ NFSX_FH + 3 * NFSX_UNSIGNED);
+ nfsm_fhtom(vp);
+ nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
+ off = (u_long)uiop->uio_offset;
+ *tl++ = txdr_unsigned(off);
+ *tl++ = txdr_unsigned(((uiop->uio_resid > nmp->nm_rsize) ?
+ nmp->nm_rsize : uiop->uio_resid) & ~(NFS_DIRBLKSIZ-1));
+ if (nmp->nm_flag & NFSMNT_NQLOOKLEASE)
+ *tl = txdr_unsigned(nmp->nm_leaseterm);
+ else
+ *tl = 0;
+ reqtime = time.tv_sec;
+ nfsm_request(vp, NQNFSPROC_READDIRLOOK, uiop->uio_procp, cred);
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ more_dirs = fxdr_unsigned(int, *tl);
+
+ /* loop thru the dir entries, doctoring them to 4bsd form */
+ bigenough = 1;
+ while (more_dirs && bigenough) {
+ doit = 1;
+ nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED);
+ if (nmp->nm_flag & NFSMNT_NQLOOKLEASE) {
+ cachable = fxdr_unsigned(int, *tl++);
+ ltime = reqtime + fxdr_unsigned(int, *tl++);
+ fxdr_hyper(tl, &frev);
+ }
+ nfsm_dissect(fhp, nfsv2fh_t *, NFSX_FH);
+ if (!bcmp(VTONFS(vp)->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) {
+ VREF(vp);
+ newvp = vp;
+ np = VTONFS(vp);
+ } else {
+ if (error = nfs_nget(vp->v_mount, fhp, &np))
+ doit = 0;
+ newvp = NFSTOV(np);
+ }
+ if (error = nfs_loadattrcache(&newvp, &md, &dpos,
+ (struct vattr *)0))
+ doit = 0;
+ nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+ fileno = fxdr_unsigned(u_long, *tl++);
+ len = fxdr_unsigned(int, *tl);
+ if (len <= 0 || len > NFS_MAXNAMLEN) {
+ error = EBADRPC;
+ m_freem(mrep);
+ goto nfsmout;
+ }
+ tlen = (len + 4) & ~0x3;
+ if ((tlen + DIRHDSIZ) > uiop->uio_resid)
+ bigenough = 0;
+ if (bigenough && doit) {
+ dp = (struct dirent *)uiop->uio_iov->iov_base;
+ dp->d_fileno = fileno;
+ dp->d_namlen = len;
+ dp->d_reclen = tlen + DIRHDSIZ;
+ dp->d_type =
+ IFTODT(VTTOIF(np->n_vattr.va_type));
+ uiop->uio_resid -= DIRHDSIZ;
+ uiop->uio_iov->iov_base += DIRHDSIZ;
+ uiop->uio_iov->iov_len -= DIRHDSIZ;
+ cnp->cn_nameptr = uiop->uio_iov->iov_base;
+ cnp->cn_namelen = len;
+ ndp->ni_vp = newvp;
+ nfsm_mtouio(uiop, len);
+ cp = uiop->uio_iov->iov_base;
+ tlen -= len;
+ for (i = 0; i < tlen; i++)
+ *cp++ = '\0';
+ uiop->uio_iov->iov_base += tlen;
+ uiop->uio_iov->iov_len -= tlen;
+ uiop->uio_resid -= tlen;
+ cnp->cn_hash = 0;
+ for (cp = cnp->cn_nameptr, i = 1; i <= len; i++, cp++)
+ cnp->cn_hash += (unsigned char)*cp * i;
+ if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) &&
+ ltime > time.tv_sec)
+ nqnfs_clientlease(nmp, np, NQL_READ,
+ cachable, ltime, frev);
+ if (cnp->cn_namelen <= NCHNAMLEN)
+ cache_enter(ndp->ni_dvp, ndp->ni_vp, cnp);
+ } else {
+ nfsm_adv(nfsm_rndup(len));
+ }
+ if (newvp != NULLVP) {
+ vrele(newvp);
+ newvp = NULLVP;
+ }
+ nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+ if (bigenough)
+ endoff = off = fxdr_unsigned(u_long, *tl++);
+ else
+ endoff = fxdr_unsigned(u_long, *tl++);
+ more_dirs = fxdr_unsigned(int, *tl);
+ }
+ /*
+ * If at end of rpc data, get the eof boolean
+ */
+ if (!more_dirs) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ more_dirs = (fxdr_unsigned(int, *tl) == 0);
+
+ /*
+ * If at EOF, cache directory offset
+ */
+ if (!more_dirs)
+ VTONFS(vp)->n_direofoffset = endoff;
+ }
+ if (uiop->uio_resid < tresid)
+ uiop->uio_offset = (off_t)off;
+ else
+ more_dirs = 0;
+ m_freem(mrep);
+ }
+ /*
+ * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ
+ * by increasing d_reclen for the last record.
+ */
+ if (uiop->uio_resid < tresid) {
+ len = uiop->uio_resid & (NFS_DIRBLKSIZ - 1);
+ if (len > 0) {
+ dp->d_reclen += len;
+ uiop->uio_iov->iov_base += len;
+ uiop->uio_iov->iov_len -= len;
+ uiop->uio_resid -= len;
+ }
+ }
+nfsmout:
+ if (newvp != NULLVP)
+ vrele(newvp);
+ return (error);
+}
+static char hextoasc[] = "0123456789abcdef";
+
+/*
+ * Silly rename. To make the NFS filesystem that is stateless look a little
+ * more like the "ufs" a remove of an active vnode is translated to a rename
+ * to a funny looking filename that is removed by nfs_inactive on the
+ * nfsnode. There is the potential for another process on a different client
+ * to create the same funny name between the nfs_lookitup() fails and the
+ * nfs_rename() completes, but...
+ */
+int
+nfs_sillyrename(dvp, vp, cnp)
+ struct vnode *dvp, *vp;
+ struct componentname *cnp;
+{
+ register struct nfsnode *np;
+ register struct sillyrename *sp;
+ int error;
+ short pid;
+
+ cache_purge(dvp);
+ np = VTONFS(vp);
+#ifdef SILLYSEPARATE
+ MALLOC(sp, struct sillyrename *, sizeof (struct sillyrename),
+ M_NFSREQ, M_WAITOK);
+#else
+ sp = &np->n_silly;
+#endif
+ sp->s_cred = crdup(cnp->cn_cred);
+ sp->s_dvp = dvp;
+ VREF(dvp);
+
+ /* Fudge together a funny name */
+ pid = cnp->cn_proc->p_pid;
+ bcopy(".nfsAxxxx4.4", sp->s_name, 13);
+ sp->s_namlen = 12;
+ sp->s_name[8] = hextoasc[pid & 0xf];
+ sp->s_name[7] = hextoasc[(pid >> 4) & 0xf];
+ sp->s_name[6] = hextoasc[(pid >> 8) & 0xf];
+ sp->s_name[5] = hextoasc[(pid >> 12) & 0xf];
+
+ /* Try lookitups until we get one that isn't there */
+ while (nfs_lookitup(sp, (nfsv2fh_t *)0, cnp->cn_proc) == 0) {
+ sp->s_name[4]++;
+ if (sp->s_name[4] > 'z') {
+ error = EINVAL;
+ goto bad;
+ }
+ }
+ if (error = nfs_renameit(dvp, cnp, sp))
+ goto bad;
+ nfs_lookitup(sp, &np->n_fh, cnp->cn_proc);
+ np->n_sillyrename = sp;
+ return (0);
+bad:
+ vrele(sp->s_dvp);
+ crfree(sp->s_cred);
+#ifdef SILLYSEPARATE
+ free((caddr_t)sp, M_NFSREQ);
+#endif
+ return (error);
+}
+
+/*
+ * Look up a file name for silly rename stuff.
+ * Just like nfs_lookup() except that it doesn't load returned values
+ * into the nfsnode table.
+ * If fhp != NULL it copies the returned file handle out
+ */
+int
+nfs_lookitup(sp, fhp, procp)
+ register struct sillyrename *sp;
+ nfsv2fh_t *fhp;
+ struct proc *procp;
+{
+ register struct vnode *vp = sp->s_dvp;
+ register u_long *tl;
+ register caddr_t cp;
+ register long t1, t2;
+ caddr_t bpos, dpos, cp2;
+ int error = 0, isnq;
+ struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+ long len;
+
+ isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ nfsstats.rpccnt[NFSPROC_LOOKUP]++;
+ len = sp->s_namlen;
+ nfsm_reqhead(vp, NFSPROC_LOOKUP, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len));
+ if (isnq) {
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = 0;
+ }
+ nfsm_fhtom(vp);
+ nfsm_strtom(sp->s_name, len, NFS_MAXNAMLEN);
+ nfsm_request(vp, NFSPROC_LOOKUP, procp, sp->s_cred);
+ if (fhp != NULL) {
+ if (isnq)
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ nfsm_dissect(cp, caddr_t, NFSX_FH);
+ bcopy(cp, (caddr_t)fhp, NFSX_FH);
+ }
+ nfsm_reqdone;
+ return (error);
+}
+
+/*
+ * Kludge City..
+ * - make nfs_bmap() essentially a no-op that does no translation
+ * - do nfs_strategy() by faking physical I/O with nfs_readrpc/nfs_writerpc
+ * after mapping the physical addresses into Kernel Virtual space in the
+ * nfsiobuf area.
+ * (Maybe I could use the process's page mapping, but I was concerned that
+ * Kernel Write might not be enabled and also figured copyout() would do
+ * a lot more work than bcopy() and also it currently happens in the
+ * context of the swapper process (2).
+ */
+int
+nfs_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = vp;
+ if (ap->a_bnp != NULL)
+ *ap->a_bnp = ap->a_bn * btodb(vp->v_mount->mnt_stat.f_iosize);
+ return (0);
+}
+
+/*
+ * Strategy routine.
+ * For async requests when nfsiod(s) are running, queue the request by
+ * calling nfs_asyncio(), otherwise just all nfs_doio() to do the
+ * request.
+ */
+int
+nfs_strategy(ap)
+ struct vop_strategy_args *ap;
+{
+ register struct buf *bp = ap->a_bp;
+ struct ucred *cr;
+ struct proc *p;
+ int error = 0;
+
+ if (bp->b_flags & B_PHYS)
+ panic("nfs physio");
+ if (bp->b_flags & B_ASYNC)
+ p = (struct proc *)0;
+ else
+ p = curproc; /* XXX */
+ if (bp->b_flags & B_READ)
+ cr = bp->b_rcred;
+ else
+ cr = bp->b_wcred;
+ /*
+ * If the op is asynchronous and an i/o daemon is waiting
+ * queue the request, wake it up and wait for completion
+ * otherwise just do it ourselves.
+ */
+ if ((bp->b_flags & B_ASYNC) == 0 ||
+ nfs_asyncio(bp, NOCRED))
+ error = nfs_doio(bp, cr, p);
+ return (error);
+}
+
+/*
+ * Mmap a file
+ *
+ * NB Currently unsupported.
+ */
+/* ARGSUSED */
+int
+nfs_mmap(ap)
+ struct vop_mmap_args /* {
+ struct vnode *a_vp;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (EINVAL);
+}
+
+/*
+ * Flush all the blocks associated with a vnode.
+ * Walk through the buffer pool and push any dirty pages
+ * associated with the vnode.
+ */
+/* ARGSUSED */
+int
+nfs_fsync(ap)
+ struct vop_fsync_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode * a_vp;
+ struct ucred * a_cred;
+ int a_waitfor;
+ struct proc * a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+ register struct buf *bp;
+ struct buf *nbp;
+ struct nfsmount *nmp;
+ int s, error = 0, slptimeo = 0, slpflag = 0;
+
+ nmp = VFSTONFS(vp->v_mount);
+ if (nmp->nm_flag & NFSMNT_INT)
+ slpflag = PCATCH;
+loop:
+ s = splbio();
+ for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
+ nbp = bp->b_vnbufs.le_next;
+ if (bp->b_flags & B_BUSY) {
+ if (ap->a_waitfor != MNT_WAIT)
+ continue;
+ bp->b_flags |= B_WANTED;
+ error = tsleep((caddr_t)bp, slpflag | (PRIBIO + 1),
+ "nfsfsync", slptimeo);
+ splx(s);
+ if (error) {
+ if (nfs_sigintr(nmp, (struct nfsreq *)0, ap->a_p))
+ return (EINTR);
+ if (slpflag == PCATCH) {
+ slpflag = 0;
+ slptimeo = 2 * hz;
+ }
+ }
+ goto loop;
+ }
+ if ((bp->b_flags & B_DELWRI) == 0)
+ panic("nfs_fsync: not dirty");
+ bremfree(bp);
+ bp->b_flags |= B_BUSY;
+ splx(s);
+ bp->b_flags |= B_ASYNC;
+ VOP_BWRITE(bp);
+ goto loop;
+ }
+ splx(s);
+ if (ap->a_waitfor == MNT_WAIT) {
+ while (vp->v_numoutput) {
+ vp->v_flag |= VBWAIT;
+ error = tsleep((caddr_t)&vp->v_numoutput,
+ slpflag | (PRIBIO + 1), "nfsfsync", slptimeo);
+ if (error) {
+ if (nfs_sigintr(nmp, (struct nfsreq *)0, ap->a_p))
+ return (EINTR);
+ if (slpflag == PCATCH) {
+ slpflag = 0;
+ slptimeo = 2 * hz;
+ }
+ }
+ }
+ if (vp->v_dirtyblkhd.lh_first) {
+#ifdef DIAGNOSTIC
+ vprint("nfs_fsync: dirty", vp);
+#endif
+ goto loop;
+ }
+ }
+ if (np->n_flag & NWRITEERR) {
+ error = np->n_error;
+ np->n_flag &= ~NWRITEERR;
+ }
+ return (error);
+}
+
+/*
+ * Return POSIX pathconf information applicable to nfs.
+ *
+ * Currently the NFS protocol does not support getting such
+ * information from the remote server.
+ */
+/* ARGSUSED */
+int
+nfs_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+
+ return (EINVAL);
+}
+
+/*
+ * NFS advisory byte-level locks.
+ * Currently unsupported.
+ */
+int
+nfs_advlock(ap)
+ struct vop_advlock_args /* {
+ struct vnode *a_vp;
+ caddr_t a_id;
+ int a_op;
+ struct flock *a_fl;
+ int a_flags;
+ } */ *ap;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * Print out the contents of an nfsnode.
+ */
+int
+nfs_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+
+ printf("tag VT_NFS, fileid %d fsid 0x%x",
+ np->n_vattr.va_fileid, np->n_vattr.va_fsid);
+#ifdef FIFO
+ if (vp->v_type == VFIFO)
+ fifo_printinfo(vp);
+#endif /* FIFO */
+ printf("\n");
+ return (0);
+}
+
+/*
+ * NFS directory offset lookup.
+ * Currently unsupported.
+ */
+int
+nfs_blkatoff(ap)
+ struct vop_blkatoff_args /* {
+ struct vnode *a_vp;
+ off_t a_offset;
+ char **a_res;
+ struct buf **a_bpp;
+ } */ *ap;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * NFS flat namespace allocation.
+ * Currently unsupported.
+ */
+int
+nfs_valloc(ap)
+ struct vop_valloc_args /* {
+ struct vnode *a_pvp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct vnode **a_vpp;
+ } */ *ap;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * NFS flat namespace free.
+ * Currently unsupported.
+ */
+int
+nfs_vfree(ap)
+ struct vop_vfree_args /* {
+ struct vnode *a_pvp;
+ ino_t a_ino;
+ int a_mode;
+ } */ *ap;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * NFS file truncation.
+ */
+int
+nfs_truncate(ap)
+ struct vop_truncate_args /* {
+ struct vnode *a_vp;
+ off_t a_length;
+ int a_flags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ /* Use nfs_setattr */
+ printf("nfs_truncate: need to implement!!");
+ return (EOPNOTSUPP);
+}
+
+/*
+ * NFS update.
+ */
+int
+nfs_update(ap)
+ struct vop_update_args /* {
+ struct vnode *a_vp;
+ struct timeval *a_ta;
+ struct timeval *a_tm;
+ int a_waitfor;
+ } */ *ap;
+{
+
+ /* Use nfs_setattr */
+ printf("nfs_update: need to implement!!");
+ return (EOPNOTSUPP);
+}
+
+/*
+ * nfs special file access vnode op.
+ * Essentially just get vattr and then imitate iaccess() since the device is
+ * local to the client.
+ */
+int
+nfsspec_access(ap)
+ struct vop_access_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vattr *vap;
+ register gid_t *gp;
+ register struct ucred *cred = ap->a_cred;
+ mode_t mode = ap->a_mode;
+ struct vattr vattr;
+ register int i;
+ int error;
+
+ /*
+ * If you're the super-user,
+ * you always get access.
+ */
+ if (cred->cr_uid == 0)
+ return (0);
+ vap = &vattr;
+ if (error = VOP_GETATTR(ap->a_vp, vap, cred, ap->a_p))
+ return (error);
+ /*
+ * Access check is based on only one of owner, group, public.
+ * If not owner, then check group. If not a member of the
+ * group, then check public access.
+ */
+ if (cred->cr_uid != vap->va_uid) {
+ mode >>= 3;
+ gp = cred->cr_groups;
+ for (i = 0; i < cred->cr_ngroups; i++, gp++)
+ if (vap->va_gid == *gp)
+ goto found;
+ mode >>= 3;
+found:
+ ;
+ }
+ return ((vap->va_mode & mode) == mode ? 0 : EACCES);
+}
+
+/*
+ * Read wrapper for special devices.
+ */
+int
+nfsspec_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct nfsnode *np = VTONFS(ap->a_vp);
+
+ /*
+ * Set access flag.
+ */
+ np->n_flag |= NACC;
+ np->n_atim = time;
+ return (VOCALL(spec_vnodeop_p, VOFFSET(vop_read), ap));
+}
+
+/*
+ * Write wrapper for special devices.
+ */
+int
+nfsspec_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct nfsnode *np = VTONFS(ap->a_vp);
+
+ /*
+ * Set update flag.
+ */
+ np->n_flag |= NUPD;
+ np->n_mtim = time;
+ return (VOCALL(spec_vnodeop_p, VOFFSET(vop_write), ap));
+}
+
+/*
+ * Close wrapper for special devices.
+ *
+ * Update the times on the nfsnode then do device close.
+ */
+int
+nfsspec_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+ struct vattr vattr;
+
+ if (np->n_flag & (NACC | NUPD)) {
+ np->n_flag |= NCHG;
+ if (vp->v_usecount == 1 &&
+ (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
+ VATTR_NULL(&vattr);
+ if (np->n_flag & NACC) {
+ vattr.va_atime.ts_sec = np->n_atim.tv_sec;
+ vattr.va_atime.ts_nsec =
+ np->n_atim.tv_usec * 1000;
+ }
+ if (np->n_flag & NUPD) {
+ vattr.va_mtime.ts_sec = np->n_mtim.tv_sec;
+ vattr.va_mtime.ts_nsec =
+ np->n_mtim.tv_usec * 1000;
+ }
+ (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p);
+ }
+ }
+ return (VOCALL(spec_vnodeop_p, VOFFSET(vop_close), ap));
+}
+
+#ifdef FIFO
+/*
+ * Read wrapper for fifos.
+ */
+int
+nfsfifo_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ extern int (**fifo_vnodeop_p)();
+ register struct nfsnode *np = VTONFS(ap->a_vp);
+
+ /*
+ * Set access flag.
+ */
+ np->n_flag |= NACC;
+ np->n_atim = time;
+ return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap));
+}
+
+/*
+ * Write wrapper for fifos.
+ */
+int
+nfsfifo_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ extern int (**fifo_vnodeop_p)();
+ register struct nfsnode *np = VTONFS(ap->a_vp);
+
+ /*
+ * Set update flag.
+ */
+ np->n_flag |= NUPD;
+ np->n_mtim = time;
+ return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap));
+}
+
+/*
+ * Close wrapper for fifos.
+ *
+ * Update the times on the nfsnode then do fifo close.
+ */
+int
+nfsfifo_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nfsnode *np = VTONFS(vp);
+ struct vattr vattr;
+ extern int (**fifo_vnodeop_p)();
+
+ if (np->n_flag & (NACC | NUPD)) {
+ if (np->n_flag & NACC)
+ np->n_atim = time;
+ if (np->n_flag & NUPD)
+ np->n_mtim = time;
+ np->n_flag |= NCHG;
+ if (vp->v_usecount == 1 &&
+ (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
+ VATTR_NULL(&vattr);
+ if (np->n_flag & NACC) {
+ vattr.va_atime.ts_sec = np->n_atim.tv_sec;
+ vattr.va_atime.ts_nsec =
+ np->n_atim.tv_usec * 1000;
+ }
+ if (np->n_flag & NUPD) {
+ vattr.va_mtime.ts_sec = np->n_mtim.tv_sec;
+ vattr.va_mtime.ts_nsec =
+ np->n_mtim.tv_usec * 1000;
+ }
+ (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p);
+ }
+ }
+ return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_close), ap));
+}
+#endif /* FIFO */
diff --git a/sys/nfsclient/nfsargs.h b/sys/nfsclient/nfsargs.h
new file mode 100644
index 0000000..261fd42
--- /dev/null
+++ b/sys/nfsclient/nfsargs.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Tunable constants for nfs
+ */
+
+#define NFS_MAXIOVEC 34
+#define NFS_HZ 25 /* Ticks per second for NFS timeouts */
+#define NFS_TIMEO (1*NFS_HZ) /* Default timeout = 1 second */
+#define NFS_MINTIMEO (1*NFS_HZ) /* Min timeout to use */
+#define NFS_MAXTIMEO (60*NFS_HZ) /* Max timeout to backoff to */
+#define NFS_MINIDEMTIMEO (5*NFS_HZ) /* Min timeout for non-idempotent ops*/
+#define NFS_MAXREXMIT 100 /* Stop counting after this many */
+#define NFS_MAXWINDOW 1024 /* Max number of outstanding requests */
+#define NFS_RETRANS 10 /* Num of retrans for soft mounts */
+#define NFS_MAXGRPS 16 /* Max. size of groups list */
+#define NFS_MINATTRTIMO 5 /* Attribute cache timeout in sec */
+#define NFS_MAXATTRTIMO 60
+#define NFS_WSIZE 8192 /* Def. write data size <= 8192 */
+#define NFS_RSIZE 8192 /* Def. read data size <= 8192 */
+#define NFS_DEFRAHEAD 1 /* Def. read ahead # blocks */
+#define NFS_MAXRAHEAD 4 /* Max. read ahead # blocks */
+#define NFS_MAXREADDIR NFS_MAXDATA /* Max. size of directory read */
+#define NFS_MAXUIDHASH 64 /* Max. # of hashed uid entries/mp */
+#define NFS_MAXASYNCDAEMON 20 /* Max. number async_daemons runable */
+#define NFS_DIRBLKSIZ 1024 /* Size of an NFS directory block */
+#define NMOD(a) ((a) % nfs_asyncdaemons)
+
+/*
+ * Set the attribute timeout based on how recently the file has been modified.
+ */
+#define NFS_ATTRTIMEO(np) \
+ ((((np)->n_flag & NMODIFIED) || \
+ (time.tv_sec - (np)->n_mtime) / 10 < NFS_MINATTRTIMO) ? NFS_MINATTRTIMO : \
+ ((time.tv_sec - (np)->n_mtime) / 10 > NFS_MAXATTRTIMO ? NFS_MAXATTRTIMO : \
+ (time.tv_sec - (np)->n_mtime) / 10))
+
+/*
+ * Structures for the nfssvc(2) syscall. Not that anyone but nfsd and mount_nfs
+ * should ever try and use it.
+ */
+struct nfsd_args {
+ int sock; /* Socket to serve */
+ caddr_t name; /* Client address for connection based sockets */
+ int namelen; /* Length of name */
+};
+
+struct nfsd_srvargs {
+ struct nfsd *nsd_nfsd; /* Pointer to in kernel nfsd struct */
+ uid_t nsd_uid; /* Effective uid mapped to cred */
+ u_long nsd_haddr; /* Ip address of client */
+ struct ucred nsd_cr; /* Cred. uid maps to */
+ int nsd_authlen; /* Length of auth string (ret) */
+ char *nsd_authstr; /* Auth string (ret) */
+};
+
+struct nfsd_cargs {
+ char *ncd_dirp; /* Mount dir path */
+ uid_t ncd_authuid; /* Effective uid */
+ int ncd_authtype; /* Type of authenticator */
+ int ncd_authlen; /* Length of authenticator string */
+ char *ncd_authstr; /* Authenticator string */
+};
+
+/*
+ * Stats structure
+ */
+struct nfsstats {
+ int attrcache_hits;
+ int attrcache_misses;
+ int lookupcache_hits;
+ int lookupcache_misses;
+ int direofcache_hits;
+ int direofcache_misses;
+ int biocache_reads;
+ int read_bios;
+ int read_physios;
+ int biocache_writes;
+ int write_bios;
+ int write_physios;
+ int biocache_readlinks;
+ int readlink_bios;
+ int biocache_readdirs;
+ int readdir_bios;
+ int rpccnt[NFS_NPROCS];
+ int rpcretries;
+ int srvrpccnt[NFS_NPROCS];
+ int srvrpc_errs;
+ int srv_errs;
+ int rpcrequests;
+ int rpctimeouts;
+ int rpcunexpected;
+ int rpcinvalid;
+ int srvcache_inproghits;
+ int srvcache_idemdonehits;
+ int srvcache_nonidemdonehits;
+ int srvcache_misses;
+ int srvnqnfs_leases;
+ int srvnqnfs_maxleases;
+ int srvnqnfs_getleases;
+};
+
+/*
+ * Flags for nfssvc() system call.
+ */
+#define NFSSVC_BIOD 0x002
+#define NFSSVC_NFSD 0x004
+#define NFSSVC_ADDSOCK 0x008
+#define NFSSVC_AUTHIN 0x010
+#define NFSSVC_GOTAUTH 0x040
+#define NFSSVC_AUTHINFAIL 0x080
+#define NFSSVC_MNTD 0x100
+
+/*
+ * The set of signals the interrupt an I/O in progress for NFSMNT_INT mounts.
+ * What should be in this set is open to debate, but I believe that since
+ * I/O system calls on ufs are never interrupted by signals the set should
+ * be minimal. My reasoning is that many current programs that use signals
+ * such as SIGALRM will not expect file I/O system calls to be interrupted
+ * by them and break.
+ */
+#ifdef KERNEL
+#define NFSINT_SIGMASK (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \
+ sigmask(SIGHUP)|sigmask(SIGQUIT))
+
+/*
+ * Socket errors ignored for connectionless sockets??
+ * For now, ignore them all
+ */
+#define NFSIGNORE_SOERROR(s, e) \
+ ((e) != EINTR && (e) != ERESTART && (e) != EWOULDBLOCK && \
+ ((s) & PR_CONNREQUIRED) == 0)
+
+/*
+ * Nfs outstanding request list element
+ */
+struct nfsreq {
+ struct nfsreq *r_next;
+ struct nfsreq *r_prev;
+ struct mbuf *r_mreq;
+ struct mbuf *r_mrep;
+ struct mbuf *r_md;
+ caddr_t r_dpos;
+ struct nfsmount *r_nmp;
+ struct vnode *r_vp;
+ u_long r_xid;
+ int r_flags; /* flags on request, see below */
+ int r_retry; /* max retransmission count */
+ int r_rexmit; /* current retrans count */
+ int r_timer; /* tick counter on reply */
+ int r_procnum; /* NFS procedure number */
+ int r_rtt; /* RTT for rpc */
+ struct proc *r_procp; /* Proc that did I/O system call */
+};
+
+/* Flag values for r_flags */
+#define R_TIMING 0x01 /* timing request (in mntp) */
+#define R_SENT 0x02 /* request has been sent */
+#define R_SOFTTERM 0x04 /* soft mnt, too many retries */
+#define R_INTR 0x08 /* intr mnt, signal pending */
+#define R_SOCKERR 0x10 /* Fatal error on socket */
+#define R_TPRINTFMSG 0x20 /* Did a tprintf msg. */
+#define R_MUSTRESEND 0x40 /* Must resend request */
+#define R_GETONEREP 0x80 /* Probe for one reply only */
+
+struct nfsstats nfsstats;
+
+/*
+ * A list of nfssvc_sock structures is maintained with all the sockets
+ * that require service by the nfsd.
+ * The nfsuid structs hang off of the nfssvc_sock structs in both lru
+ * and uid hash lists.
+ */
+#define NUIDHASHSIZ 32
+#define NUIDHASH(uid) ((uid) & (NUIDHASHSIZ - 1))
+
+/*
+ * Network address hash list element
+ */
+union nethostaddr {
+ u_long had_inetaddr;
+ struct mbuf *had_nam;
+};
+
+struct nfsuid {
+ struct nfsuid *nu_lrunext; /* MUST be first */
+ struct nfsuid *nu_lruprev;
+ struct nfsuid *nu_hnext;
+ struct nfsuid *nu_hprev;
+ int nu_flag; /* Flags */
+ uid_t nu_uid; /* Uid mapped by this entry */
+ union nethostaddr nu_haddr; /* Host addr. for dgram sockets */
+ struct ucred nu_cr; /* Cred uid mapped to */
+};
+
+#define nu_inetaddr nu_haddr.had_inetaddr
+#define nu_nam nu_haddr.had_nam
+/* Bits for nu_flag */
+#define NU_INETADDR 0x1
+
+struct nfssvc_sock {
+ struct nfsuid *ns_lrunext; /* MUST be first */
+ struct nfsuid *ns_lruprev;
+ struct nfssvc_sock *ns_next;
+ struct nfssvc_sock *ns_prev;
+ int ns_flag;
+ u_long ns_sref;
+ struct file *ns_fp;
+ struct socket *ns_so;
+ int ns_solock;
+ struct mbuf *ns_nam;
+ int ns_cc;
+ struct mbuf *ns_raw;
+ struct mbuf *ns_rawend;
+ int ns_reclen;
+ struct mbuf *ns_rec;
+ struct mbuf *ns_recend;
+ int ns_numuids;
+ struct nfsuid *ns_uidh[NUIDHASHSIZ];
+};
+
+/* Bits for "ns_flag" */
+#define SLP_VALID 0x01
+#define SLP_DOREC 0x02
+#define SLP_NEEDQ 0x04
+#define SLP_DISCONN 0x08
+#define SLP_GETSTREAM 0x10
+#define SLP_INIT 0x20
+#define SLP_WANTINIT 0x40
+
+#define SLP_ALLFLAGS 0xff
+
+/*
+ * One of these structures is allocated for each nfsd.
+ */
+struct nfsd {
+ struct nfsd *nd_next; /* Must be first */
+ struct nfsd *nd_prev;
+ int nd_flag; /* NFSD_ flags */
+ struct nfssvc_sock *nd_slp; /* Current socket */
+ struct mbuf *nd_nam; /* Client addr for datagram req. */
+ struct mbuf *nd_mrep; /* Req. mbuf list */
+ struct mbuf *nd_md;
+ caddr_t nd_dpos; /* Position in list */
+ int nd_procnum; /* RPC procedure number */
+ u_long nd_retxid; /* RPC xid */
+ int nd_repstat; /* Reply status value */
+ struct ucred nd_cr; /* Credentials for req. */
+ int nd_nqlflag; /* Leasing flag */
+ int nd_duration; /* Lease duration */
+ int nd_authlen; /* Authenticator len */
+ u_char nd_authstr[RPCAUTH_MAXSIZ]; /* Authenticator data */
+ struct proc *nd_procp; /* Proc ptr */
+};
+
+#define NFSD_WAITING 0x01
+#define NFSD_CHECKSLP 0x02
+#define NFSD_REQINPROG 0x04
+#define NFSD_NEEDAUTH 0x08
+#define NFSD_AUTHFAIL 0x10
+#endif /* KERNEL */
diff --git a/sys/nfsclient/nfsdiskless.h b/sys/nfsclient/nfsdiskless.h
new file mode 100644
index 0000000..74e6b7b
--- /dev/null
+++ b/sys/nfsclient/nfsdiskless.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsdiskless.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Structure that must be initialized for a diskless nfs client.
+ * This structure is used by nfs_mountroot() to set up the root and swap
+ * vnodes plus do a partial ifconfig(8) and route(8) so that the critical net
+ * interface can communicate with the server.
+ * The primary bootstrap is expected to fill in the appropriate fields before
+ * starting vmunix. Whether or not the swap area is nfs mounted is determined
+ * by the value in swdevt[0]. (equal to NODEV --> swap over nfs)
+ * Currently only works for AF_INET protocols.
+ * NB: All fields are stored in net byte order to avoid hassles with
+ * client/server byte ordering differences.
+ */
+struct nfs_diskless {
+ struct ifaliasreq myif; /* Default interface */
+ struct sockaddr_in mygateway; /* Default gateway */
+ struct nfs_args swap_args; /* Mount args for swap file */
+ u_char swap_fh[NFS_FHSIZE]; /* Swap file's file handle */
+ struct sockaddr_in swap_saddr; /* Address of swap server */
+ char swap_hostnam[MNAMELEN]; /* Host name for mount pt */
+ int swap_nblks; /* Size of server swap file */
+ struct ucred swap_ucred; /* Swap credentials */
+ struct nfs_args root_args; /* Mount args for root fs */
+ u_char root_fh[NFS_FHSIZE]; /* File handle of root dir */
+ struct sockaddr_in root_saddr; /* Address of root server */
+ char root_hostnam[MNAMELEN]; /* Host name for mount pt */
+ long root_time; /* Timestamp of root fs */
+ char my_hostnam[MAXHOSTNAMELEN]; /* Client host name */
+};
diff --git a/sys/nfsclient/nfsm_subs.h b/sys/nfsclient/nfsm_subs.h
new file mode 100644
index 0000000..879db36
--- /dev/null
+++ b/sys/nfsclient/nfsm_subs.h
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsm_subs.h 8.1 (Berkeley) 6/16/93
+ */
+
+/*
+ * These macros do strange and peculiar things to mbuf chains for
+ * the assistance of the nfs code. To attempt to use them for any
+ * other purpose will be dangerous. (they make weird assumptions)
+ */
+
+/*
+ * First define what the actual subs. return
+ */
+extern struct mbuf *nfsm_reqh();
+
+#define M_HASCL(m) ((m)->m_flags & M_EXT)
+#define NFSMINOFF(m) \
+ if (M_HASCL(m)) \
+ (m)->m_data = (m)->m_ext.ext_buf; \
+ else if ((m)->m_flags & M_PKTHDR) \
+ (m)->m_data = (m)->m_pktdat; \
+ else \
+ (m)->m_data = (m)->m_dat
+#define NFSMADV(m, s) (m)->m_data += (s)
+#define NFSMSIZ(m) ((M_HASCL(m))?MCLBYTES: \
+ (((m)->m_flags & M_PKTHDR)?MHLEN:MLEN))
+
+/*
+ * Now for the macros that do the simple stuff and call the functions
+ * for the hard stuff.
+ * These macros use several vars. declared in nfsm_reqhead and these
+ * vars. must not be used elsewhere unless you are careful not to corrupt
+ * them. The vars. starting with pN and tN (N=1,2,3,..) are temporaries
+ * that may be used so long as the value is not expected to retained
+ * after a macro.
+ * I know, this is kind of dorkey, but it makes the actual op functions
+ * fairly clean and deals with the mess caused by the xdr discriminating
+ * unions.
+ */
+
+#define nfsm_build(a,c,s) \
+ { if ((s) > M_TRAILINGSPACE(mb)) { \
+ MGET(mb2, M_WAIT, MT_DATA); \
+ if ((s) > MLEN) \
+ panic("build > MLEN"); \
+ mb->m_next = mb2; \
+ mb = mb2; \
+ mb->m_len = 0; \
+ bpos = mtod(mb, caddr_t); \
+ } \
+ (a) = (c)(bpos); \
+ mb->m_len += (s); \
+ bpos += (s); }
+
+#define nfsm_dissect(a,c,s) \
+ { t1 = mtod(md, caddr_t)+md->m_len-dpos; \
+ if (t1 >= (s)) { \
+ (a) = (c)(dpos); \
+ dpos += (s); \
+ } else if (error = nfsm_disct(&md, &dpos, (s), t1, &cp2)) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ } else { \
+ (a) = (c)cp2; \
+ } }
+
+#define nfsm_fhtom(v) \
+ nfsm_build(cp,caddr_t,NFSX_FH); \
+ bcopy((caddr_t)&(VTONFS(v)->n_fh), cp, NFSX_FH)
+
+#define nfsm_srvfhtom(f) \
+ nfsm_build(cp,caddr_t,NFSX_FH); \
+ bcopy((caddr_t)(f), cp, NFSX_FH)
+
+#define nfsm_mtofh(d,v) \
+ { struct nfsnode *np; nfsv2fh_t *fhp; \
+ nfsm_dissect(fhp,nfsv2fh_t *,NFSX_FH); \
+ if (error = nfs_nget((d)->v_mount, fhp, &np)) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ } \
+ (v) = NFSTOV(np); \
+ nfsm_loadattr(v, (struct vattr *)0); \
+ }
+
+#define nfsm_loadattr(v,a) \
+ { struct vnode *tvp = (v); \
+ if (error = nfs_loadattrcache(&tvp, &md, &dpos, (a))) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ } \
+ (v) = tvp; }
+
+#define nfsm_strsiz(s,m) \
+ { nfsm_dissect(tl,u_long *,NFSX_UNSIGNED); \
+ if (((s) = fxdr_unsigned(long,*tl)) > (m)) { \
+ m_freem(mrep); \
+ error = EBADRPC; \
+ goto nfsmout; \
+ } }
+
+#define nfsm_srvstrsiz(s,m) \
+ { nfsm_dissect(tl,u_long *,NFSX_UNSIGNED); \
+ if (((s) = fxdr_unsigned(long,*tl)) > (m) || (s) <= 0) { \
+ error = EBADRPC; \
+ nfsm_reply(0); \
+ } }
+
+#define nfsm_mtouio(p,s) \
+ if ((s) > 0 && \
+ (error = nfsm_mbuftouio(&md,(p),(s),&dpos))) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ }
+
+#define nfsm_uiotom(p,s) \
+ if (error = nfsm_uiotombuf((p),&mb,(s),&bpos)) { \
+ m_freem(mreq); \
+ goto nfsmout; \
+ }
+
+#define nfsm_reqhead(v,a,s) \
+ mb = mreq = nfsm_reqh((v),(a),(s),&bpos)
+
+#define nfsm_reqdone m_freem(mrep); \
+ nfsmout:
+
+#define nfsm_rndup(a) (((a)+3)&(~0x3))
+
+#define nfsm_request(v, t, p, c) \
+ if (error = nfs_request((v), mreq, (t), (p), \
+ (c), &mrep, &md, &dpos)) \
+ goto nfsmout
+
+#define nfsm_strtom(a,s,m) \
+ if ((s) > (m)) { \
+ m_freem(mreq); \
+ error = ENAMETOOLONG; \
+ goto nfsmout; \
+ } \
+ t2 = nfsm_rndup(s)+NFSX_UNSIGNED; \
+ if (t2 <= M_TRAILINGSPACE(mb)) { \
+ nfsm_build(tl,u_long *,t2); \
+ *tl++ = txdr_unsigned(s); \
+ *(tl+((t2>>2)-2)) = 0; \
+ bcopy((caddr_t)(a), (caddr_t)tl, (s)); \
+ } else if (error = nfsm_strtmbuf(&mb, &bpos, (a), (s))) { \
+ m_freem(mreq); \
+ goto nfsmout; \
+ }
+
+#define nfsm_srvdone \
+ nfsmout: \
+ return(error)
+
+#define nfsm_reply(s) \
+ { \
+ nfsd->nd_repstat = error; \
+ if (error) \
+ (void) nfs_rephead(0, nfsd, error, cache, &frev, \
+ mrq, &mb, &bpos); \
+ else \
+ (void) nfs_rephead((s), nfsd, error, cache, &frev, \
+ mrq, &mb, &bpos); \
+ m_freem(mrep); \
+ mreq = *mrq; \
+ if (error) \
+ return(0); \
+ }
+
+#define nfsm_adv(s) \
+ t1 = mtod(md, caddr_t)+md->m_len-dpos; \
+ if (t1 >= (s)) { \
+ dpos += (s); \
+ } else if (error = nfs_adv(&md, &dpos, (s), t1)) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ }
+
+#define nfsm_srvmtofh(f) \
+ nfsm_dissect(tl, u_long *, NFSX_FH); \
+ bcopy((caddr_t)tl, (caddr_t)f, NFSX_FH)
+
+#define nfsm_clget \
+ if (bp >= be) { \
+ if (mp == mb) \
+ mp->m_len += bp-bpos; \
+ MGET(mp, M_WAIT, MT_DATA); \
+ MCLGET(mp, M_WAIT); \
+ mp->m_len = NFSMSIZ(mp); \
+ mp2->m_next = mp; \
+ mp2 = mp; \
+ bp = mtod(mp, caddr_t); \
+ be = bp+mp->m_len; \
+ } \
+ tl = (u_long *)bp
+
+#define nfsm_srvfillattr \
+ fp->fa_type = vtonfs_type(vap->va_type); \
+ fp->fa_mode = vtonfs_mode(vap->va_type, vap->va_mode); \
+ fp->fa_nlink = txdr_unsigned(vap->va_nlink); \
+ fp->fa_uid = txdr_unsigned(vap->va_uid); \
+ fp->fa_gid = txdr_unsigned(vap->va_gid); \
+ if (nfsd->nd_nqlflag == NQL_NOVAL) { \
+ fp->fa_nfsblocksize = txdr_unsigned(vap->va_blocksize); \
+ if (vap->va_type == VFIFO) \
+ fp->fa_nfsrdev = 0xffffffff; \
+ else \
+ fp->fa_nfsrdev = txdr_unsigned(vap->va_rdev); \
+ fp->fa_nfsfsid = txdr_unsigned(vap->va_fsid); \
+ fp->fa_nfsfileid = txdr_unsigned(vap->va_fileid); \
+ fp->fa_nfssize = txdr_unsigned(vap->va_size); \
+ fp->fa_nfsblocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE); \
+ txdr_nfstime(&vap->va_atime, &fp->fa_nfsatime); \
+ txdr_nfstime(&vap->va_mtime, &fp->fa_nfsmtime); \
+ fp->fa_nfsctime.nfs_sec = txdr_unsigned(vap->va_ctime.ts_sec); \
+ fp->fa_nfsctime.nfs_usec = txdr_unsigned(vap->va_gen); \
+ } else { \
+ fp->fa_nqblocksize = txdr_unsigned(vap->va_blocksize); \
+ if (vap->va_type == VFIFO) \
+ fp->fa_nqrdev = 0xffffffff; \
+ else \
+ fp->fa_nqrdev = txdr_unsigned(vap->va_rdev); \
+ fp->fa_nqfsid = txdr_unsigned(vap->va_fsid); \
+ fp->fa_nqfileid = txdr_unsigned(vap->va_fileid); \
+ txdr_hyper(&vap->va_size, &fp->fa_nqsize); \
+ txdr_hyper(&vap->va_bytes, &fp->fa_nqbytes); \
+ txdr_nqtime(&vap->va_atime, &fp->fa_nqatime); \
+ txdr_nqtime(&vap->va_mtime, &fp->fa_nqmtime); \
+ txdr_nqtime(&vap->va_ctime, &fp->fa_nqctime); \
+ fp->fa_nqflags = txdr_unsigned(vap->va_flags); \
+ fp->fa_nqgen = txdr_unsigned(vap->va_gen); \
+ txdr_hyper(&vap->va_filerev, &fp->fa_nqfilerev); \
+ }
+
diff --git a/sys/nfsclient/nfsmount.h b/sys/nfsclient/nfsmount.h
new file mode 100644
index 0000000..4d74acb
--- /dev/null
+++ b/sys/nfsclient/nfsmount.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsmount.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Mount structure.
+ * One allocated on every NFS mount.
+ * Holds NFS specific information for mount.
+ */
+struct nfsmount {
+ int nm_flag; /* Flags for soft/hard... */
+ struct mount *nm_mountp; /* Vfs structure for this filesystem */
+ int nm_numgrps; /* Max. size of groupslist */
+ nfsv2fh_t nm_fh; /* File handle of root dir */
+ struct socket *nm_so; /* Rpc socket */
+ int nm_sotype; /* Type of socket */
+ int nm_soproto; /* and protocol */
+ int nm_soflags; /* pr_flags for socket protocol */
+ struct mbuf *nm_nam; /* Addr of server */
+ int nm_timeo; /* Init timer for NFSMNT_DUMBTIMR */
+ int nm_retry; /* Max retries */
+ int nm_srtt[4]; /* Timers for rpcs */
+ int nm_sdrtt[4];
+ int nm_sent; /* Request send count */
+ int nm_cwnd; /* Request send window */
+ int nm_timeouts; /* Request timeouts */
+ int nm_deadthresh; /* Threshold of timeouts-->dead server*/
+ int nm_rsize; /* Max size of read rpc */
+ int nm_wsize; /* Max size of write rpc */
+ int nm_readahead; /* Num. of blocks to readahead */
+ int nm_leaseterm; /* Term (sec) for NQNFS lease */
+ struct nfsnode *nm_tnext; /* Head of lease timer queue */
+ struct nfsnode *nm_tprev;
+ struct vnode *nm_inprog; /* Vnode in prog by nqnfs_clientd() */
+ uid_t nm_authuid; /* Uid for authenticator */
+ int nm_authtype; /* Authenticator type */
+ int nm_authlen; /* and length */
+ char *nm_authstr; /* Authenticator string */
+};
+
+#ifdef KERNEL
+/*
+ * Convert mount ptr to nfsmount ptr.
+ */
+#define VFSTONFS(mp) ((struct nfsmount *)((mp)->mnt_data))
+#endif /* KERNEL */
+
+/*
+ * Prototypes for NFS mount operations
+ */
+int nfs_mount __P((
+ struct mount *mp,
+ char *path,
+ caddr_t data,
+ struct nameidata *ndp,
+ struct proc *p));
+int nfs_start __P((
+ struct mount *mp,
+ int flags,
+ struct proc *p));
+int nfs_unmount __P((
+ struct mount *mp,
+ int mntflags,
+ struct proc *p));
+int nfs_root __P((
+ struct mount *mp,
+ struct vnode **vpp));
+int nfs_quotactl __P((
+ struct mount *mp,
+ int cmds,
+ uid_t uid,
+ caddr_t arg,
+ struct proc *p));
+int nfs_statfs __P((
+ struct mount *mp,
+ struct statfs *sbp,
+ struct proc *p));
+int nfs_sync __P((
+ struct mount *mp,
+ int waitfor,
+ struct ucred *cred,
+ struct proc *p));
+int nfs_fhtovp __P((
+ struct mount *mp,
+ struct fid *fhp,
+ struct mbuf *nam,
+ struct vnode **vpp,
+ int *exflagsp,
+ struct ucred **credanonp));
+int nfs_vptofh __P((
+ struct vnode *vp,
+ struct fid *fhp));
+int nfs_init __P(());
diff --git a/sys/nfsclient/nfsnode.h b/sys/nfsclient/nfsnode.h
new file mode 100644
index 0000000..f5fee5b
--- /dev/null
+++ b/sys/nfsclient/nfsnode.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsnode.h 8.4 (Berkeley) 2/13/94
+ */
+
+/*
+ * Silly rename structure that hangs off the nfsnode until the name
+ * can be removed by nfs_inactive()
+ */
+struct sillyrename {
+ struct ucred *s_cred;
+ struct vnode *s_dvp;
+ long s_namlen;
+ char s_name[20];
+};
+
+/*
+ * The nfsnode is the nfs equivalent to ufs's inode. Any similarity
+ * is purely coincidental.
+ * There is a unique nfsnode allocated for each active file,
+ * each current directory, each mounted-on file, text file, and the root.
+ * An nfsnode is 'named' by its file handle. (nget/nfs_node.c)
+ */
+
+struct nfsnode {
+ struct nfsnode *n_forw; /* hash, forward */
+ struct nfsnode **n_back; /* hash, backward */
+ nfsv2fh_t n_fh; /* NFS File Handle */
+ long n_flag; /* Flag for locking.. */
+ struct vnode *n_vnode; /* vnode associated with this node */
+ struct vattr n_vattr; /* Vnode attribute cache */
+ time_t n_attrstamp; /* Time stamp for cached attributes */
+ struct sillyrename *n_sillyrename; /* Ptr to silly rename struct */
+ u_quad_t n_size; /* Current size of file */
+ int n_error; /* Save write error value */
+ u_long n_direofoffset; /* Dir. EOF offset cache */
+ time_t n_mtime; /* Prev modify time. */
+ time_t n_ctime; /* Prev create time. */
+ u_quad_t n_brev; /* Modify rev when cached */
+ u_quad_t n_lrev; /* Modify rev for lease */
+ time_t n_expiry; /* Lease expiry time */
+ struct nfsnode *n_tnext; /* Nqnfs timer chain */
+ struct nfsnode *n_tprev;
+ long spare1; /* To 8 byte boundary */
+ struct sillyrename n_silly; /* Silly rename struct */
+ struct timeval n_atim; /* Special file times */
+ struct timeval n_mtim;
+};
+
+/*
+ * Flags for n_flag
+ */
+#define NFLUSHWANT 0x0001 /* Want wakeup from a flush in prog. */
+#define NFLUSHINPROG 0x0002 /* Avoid multiple calls to vinvalbuf() */
+#define NMODIFIED 0x0004 /* Might have a modified buffer in bio */
+#define NWRITEERR 0x0008 /* Flag write errors so close will know */
+#define NQNFSNONCACHE 0x0020 /* Non-cachable lease */
+#define NQNFSWRITE 0x0040 /* Write lease */
+#define NQNFSEVICTED 0x0080 /* Has been evicted */
+#define NACC 0x0100 /* Special file accessed */
+#define NUPD 0x0200 /* Special file updated */
+#define NCHG 0x0400 /* Special file times changed */
+
+/*
+ * Convert between nfsnode pointers and vnode pointers
+ */
+#define VTONFS(vp) ((struct nfsnode *)(vp)->v_data)
+#define NFSTOV(np) ((struct vnode *)(np)->n_vnode)
+
+/*
+ * Queue head for nfsiod's
+ */
+TAILQ_HEAD(nfsbufs, buf) nfs_bufq;
+
+#ifdef KERNEL
+/*
+ * Prototypes for NFS vnode operations
+ */
+int nfs_lookup __P((struct vop_lookup_args *));
+int nfs_create __P((struct vop_create_args *));
+int nfs_mknod __P((struct vop_mknod_args *));
+int nfs_open __P((struct vop_open_args *));
+int nfs_close __P((struct vop_close_args *));
+int nfsspec_close __P((struct vop_close_args *));
+#ifdef FIFO
+int nfsfifo_close __P((struct vop_close_args *));
+#endif
+int nfs_access __P((struct vop_access_args *));
+int nfsspec_access __P((struct vop_access_args *));
+int nfs_getattr __P((struct vop_getattr_args *));
+int nfs_setattr __P((struct vop_setattr_args *));
+int nfs_read __P((struct vop_read_args *));
+int nfs_write __P((struct vop_write_args *));
+int nfsspec_read __P((struct vop_read_args *));
+int nfsspec_write __P((struct vop_write_args *));
+#ifdef FIFO
+int nfsfifo_read __P((struct vop_read_args *));
+int nfsfifo_write __P((struct vop_write_args *));
+#endif
+#define nfs_ioctl ((int (*) __P((struct vop_ioctl_args *)))enoioctl)
+#define nfs_select ((int (*) __P((struct vop_select_args *)))seltrue)
+int nfs_mmap __P((struct vop_mmap_args *));
+int nfs_fsync __P((struct vop_fsync_args *));
+#define nfs_seek ((int (*) __P((struct vop_seek_args *)))nullop)
+int nfs_remove __P((struct vop_remove_args *));
+int nfs_link __P((struct vop_link_args *));
+int nfs_rename __P((struct vop_rename_args *));
+int nfs_mkdir __P((struct vop_mkdir_args *));
+int nfs_rmdir __P((struct vop_rmdir_args *));
+int nfs_symlink __P((struct vop_symlink_args *));
+int nfs_readdir __P((struct vop_readdir_args *));
+int nfs_readlink __P((struct vop_readlink_args *));
+int nfs_abortop __P((struct vop_abortop_args *));
+int nfs_inactive __P((struct vop_inactive_args *));
+int nfs_reclaim __P((struct vop_reclaim_args *));
+int nfs_lock __P((struct vop_lock_args *));
+int nfs_unlock __P((struct vop_unlock_args *));
+int nfs_bmap __P((struct vop_bmap_args *));
+int nfs_strategy __P((struct vop_strategy_args *));
+int nfs_print __P((struct vop_print_args *));
+int nfs_islocked __P((struct vop_islocked_args *));
+int nfs_pathconf __P((struct vop_pathconf_args *));
+int nfs_advlock __P((struct vop_advlock_args *));
+int nfs_blkatoff __P((struct vop_blkatoff_args *));
+int nfs_vget __P((struct mount *, ino_t, struct vnode **));
+int nfs_valloc __P((struct vop_valloc_args *));
+#define nfs_reallocblks \
+ ((int (*) __P((struct vop_reallocblks_args *)))eopnotsupp)
+int nfs_vfree __P((struct vop_vfree_args *));
+int nfs_truncate __P((struct vop_truncate_args *));
+int nfs_update __P((struct vop_update_args *));
+int nfs_bwrite __P((struct vop_bwrite_args *));
+#endif /* KERNEL */
diff --git a/sys/nfsclient/nfsstats.h b/sys/nfsclient/nfsstats.h
new file mode 100644
index 0000000..261fd42
--- /dev/null
+++ b/sys/nfsclient/nfsstats.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Tunable constants for nfs
+ */
+
+#define NFS_MAXIOVEC 34
+#define NFS_HZ 25 /* Ticks per second for NFS timeouts */
+#define NFS_TIMEO (1*NFS_HZ) /* Default timeout = 1 second */
+#define NFS_MINTIMEO (1*NFS_HZ) /* Min timeout to use */
+#define NFS_MAXTIMEO (60*NFS_HZ) /* Max timeout to backoff to */
+#define NFS_MINIDEMTIMEO (5*NFS_HZ) /* Min timeout for non-idempotent ops*/
+#define NFS_MAXREXMIT 100 /* Stop counting after this many */
+#define NFS_MAXWINDOW 1024 /* Max number of outstanding requests */
+#define NFS_RETRANS 10 /* Num of retrans for soft mounts */
+#define NFS_MAXGRPS 16 /* Max. size of groups list */
+#define NFS_MINATTRTIMO 5 /* Attribute cache timeout in sec */
+#define NFS_MAXATTRTIMO 60
+#define NFS_WSIZE 8192 /* Def. write data size <= 8192 */
+#define NFS_RSIZE 8192 /* Def. read data size <= 8192 */
+#define NFS_DEFRAHEAD 1 /* Def. read ahead # blocks */
+#define NFS_MAXRAHEAD 4 /* Max. read ahead # blocks */
+#define NFS_MAXREADDIR NFS_MAXDATA /* Max. size of directory read */
+#define NFS_MAXUIDHASH 64 /* Max. # of hashed uid entries/mp */
+#define NFS_MAXASYNCDAEMON 20 /* Max. number async_daemons runable */
+#define NFS_DIRBLKSIZ 1024 /* Size of an NFS directory block */
+#define NMOD(a) ((a) % nfs_asyncdaemons)
+
+/*
+ * Set the attribute timeout based on how recently the file has been modified.
+ */
+#define NFS_ATTRTIMEO(np) \
+ ((((np)->n_flag & NMODIFIED) || \
+ (time.tv_sec - (np)->n_mtime) / 10 < NFS_MINATTRTIMO) ? NFS_MINATTRTIMO : \
+ ((time.tv_sec - (np)->n_mtime) / 10 > NFS_MAXATTRTIMO ? NFS_MAXATTRTIMO : \
+ (time.tv_sec - (np)->n_mtime) / 10))
+
+/*
+ * Structures for the nfssvc(2) syscall. Not that anyone but nfsd and mount_nfs
+ * should ever try and use it.
+ */
+struct nfsd_args {
+ int sock; /* Socket to serve */
+ caddr_t name; /* Client address for connection based sockets */
+ int namelen; /* Length of name */
+};
+
+struct nfsd_srvargs {
+ struct nfsd *nsd_nfsd; /* Pointer to in kernel nfsd struct */
+ uid_t nsd_uid; /* Effective uid mapped to cred */
+ u_long nsd_haddr; /* Ip address of client */
+ struct ucred nsd_cr; /* Cred. uid maps to */
+ int nsd_authlen; /* Length of auth string (ret) */
+ char *nsd_authstr; /* Auth string (ret) */
+};
+
+struct nfsd_cargs {
+ char *ncd_dirp; /* Mount dir path */
+ uid_t ncd_authuid; /* Effective uid */
+ int ncd_authtype; /* Type of authenticator */
+ int ncd_authlen; /* Length of authenticator string */
+ char *ncd_authstr; /* Authenticator string */
+};
+
+/*
+ * Stats structure
+ */
+struct nfsstats {
+ int attrcache_hits;
+ int attrcache_misses;
+ int lookupcache_hits;
+ int lookupcache_misses;
+ int direofcache_hits;
+ int direofcache_misses;
+ int biocache_reads;
+ int read_bios;
+ int read_physios;
+ int biocache_writes;
+ int write_bios;
+ int write_physios;
+ int biocache_readlinks;
+ int readlink_bios;
+ int biocache_readdirs;
+ int readdir_bios;
+ int rpccnt[NFS_NPROCS];
+ int rpcretries;
+ int srvrpccnt[NFS_NPROCS];
+ int srvrpc_errs;
+ int srv_errs;
+ int rpcrequests;
+ int rpctimeouts;
+ int rpcunexpected;
+ int rpcinvalid;
+ int srvcache_inproghits;
+ int srvcache_idemdonehits;
+ int srvcache_nonidemdonehits;
+ int srvcache_misses;
+ int srvnqnfs_leases;
+ int srvnqnfs_maxleases;
+ int srvnqnfs_getleases;
+};
+
+/*
+ * Flags for nfssvc() system call.
+ */
+#define NFSSVC_BIOD 0x002
+#define NFSSVC_NFSD 0x004
+#define NFSSVC_ADDSOCK 0x008
+#define NFSSVC_AUTHIN 0x010
+#define NFSSVC_GOTAUTH 0x040
+#define NFSSVC_AUTHINFAIL 0x080
+#define NFSSVC_MNTD 0x100
+
+/*
+ * The set of signals the interrupt an I/O in progress for NFSMNT_INT mounts.
+ * What should be in this set is open to debate, but I believe that since
+ * I/O system calls on ufs are never interrupted by signals the set should
+ * be minimal. My reasoning is that many current programs that use signals
+ * such as SIGALRM will not expect file I/O system calls to be interrupted
+ * by them and break.
+ */
+#ifdef KERNEL
+#define NFSINT_SIGMASK (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \
+ sigmask(SIGHUP)|sigmask(SIGQUIT))
+
+/*
+ * Socket errors ignored for connectionless sockets??
+ * For now, ignore them all
+ */
+#define NFSIGNORE_SOERROR(s, e) \
+ ((e) != EINTR && (e) != ERESTART && (e) != EWOULDBLOCK && \
+ ((s) & PR_CONNREQUIRED) == 0)
+
+/*
+ * Nfs outstanding request list element
+ */
+struct nfsreq {
+ struct nfsreq *r_next;
+ struct nfsreq *r_prev;
+ struct mbuf *r_mreq;
+ struct mbuf *r_mrep;
+ struct mbuf *r_md;
+ caddr_t r_dpos;
+ struct nfsmount *r_nmp;
+ struct vnode *r_vp;
+ u_long r_xid;
+ int r_flags; /* flags on request, see below */
+ int r_retry; /* max retransmission count */
+ int r_rexmit; /* current retrans count */
+ int r_timer; /* tick counter on reply */
+ int r_procnum; /* NFS procedure number */
+ int r_rtt; /* RTT for rpc */
+ struct proc *r_procp; /* Proc that did I/O system call */
+};
+
+/* Flag values for r_flags */
+#define R_TIMING 0x01 /* timing request (in mntp) */
+#define R_SENT 0x02 /* request has been sent */
+#define R_SOFTTERM 0x04 /* soft mnt, too many retries */
+#define R_INTR 0x08 /* intr mnt, signal pending */
+#define R_SOCKERR 0x10 /* Fatal error on socket */
+#define R_TPRINTFMSG 0x20 /* Did a tprintf msg. */
+#define R_MUSTRESEND 0x40 /* Must resend request */
+#define R_GETONEREP 0x80 /* Probe for one reply only */
+
+struct nfsstats nfsstats;
+
+/*
+ * A list of nfssvc_sock structures is maintained with all the sockets
+ * that require service by the nfsd.
+ * The nfsuid structs hang off of the nfssvc_sock structs in both lru
+ * and uid hash lists.
+ */
+#define NUIDHASHSIZ 32
+#define NUIDHASH(uid) ((uid) & (NUIDHASHSIZ - 1))
+
+/*
+ * Network address hash list element
+ */
+union nethostaddr {
+ u_long had_inetaddr;
+ struct mbuf *had_nam;
+};
+
+struct nfsuid {
+ struct nfsuid *nu_lrunext; /* MUST be first */
+ struct nfsuid *nu_lruprev;
+ struct nfsuid *nu_hnext;
+ struct nfsuid *nu_hprev;
+ int nu_flag; /* Flags */
+ uid_t nu_uid; /* Uid mapped by this entry */
+ union nethostaddr nu_haddr; /* Host addr. for dgram sockets */
+ struct ucred nu_cr; /* Cred uid mapped to */
+};
+
+#define nu_inetaddr nu_haddr.had_inetaddr
+#define nu_nam nu_haddr.had_nam
+/* Bits for nu_flag */
+#define NU_INETADDR 0x1
+
+struct nfssvc_sock {
+ struct nfsuid *ns_lrunext; /* MUST be first */
+ struct nfsuid *ns_lruprev;
+ struct nfssvc_sock *ns_next;
+ struct nfssvc_sock *ns_prev;
+ int ns_flag;
+ u_long ns_sref;
+ struct file *ns_fp;
+ struct socket *ns_so;
+ int ns_solock;
+ struct mbuf *ns_nam;
+ int ns_cc;
+ struct mbuf *ns_raw;
+ struct mbuf *ns_rawend;
+ int ns_reclen;
+ struct mbuf *ns_rec;
+ struct mbuf *ns_recend;
+ int ns_numuids;
+ struct nfsuid *ns_uidh[NUIDHASHSIZ];
+};
+
+/* Bits for "ns_flag" */
+#define SLP_VALID 0x01
+#define SLP_DOREC 0x02
+#define SLP_NEEDQ 0x04
+#define SLP_DISCONN 0x08
+#define SLP_GETSTREAM 0x10
+#define SLP_INIT 0x20
+#define SLP_WANTINIT 0x40
+
+#define SLP_ALLFLAGS 0xff
+
+/*
+ * One of these structures is allocated for each nfsd.
+ */
+struct nfsd {
+ struct nfsd *nd_next; /* Must be first */
+ struct nfsd *nd_prev;
+ int nd_flag; /* NFSD_ flags */
+ struct nfssvc_sock *nd_slp; /* Current socket */
+ struct mbuf *nd_nam; /* Client addr for datagram req. */
+ struct mbuf *nd_mrep; /* Req. mbuf list */
+ struct mbuf *nd_md;
+ caddr_t nd_dpos; /* Position in list */
+ int nd_procnum; /* RPC procedure number */
+ u_long nd_retxid; /* RPC xid */
+ int nd_repstat; /* Reply status value */
+ struct ucred nd_cr; /* Credentials for req. */
+ int nd_nqlflag; /* Leasing flag */
+ int nd_duration; /* Lease duration */
+ int nd_authlen; /* Authenticator len */
+ u_char nd_authstr[RPCAUTH_MAXSIZ]; /* Authenticator data */
+ struct proc *nd_procp; /* Proc ptr */
+};
+
+#define NFSD_WAITING 0x01
+#define NFSD_CHECKSLP 0x02
+#define NFSD_REQINPROG 0x04
+#define NFSD_NEEDAUTH 0x08
+#define NFSD_AUTHFAIL 0x10
+#endif /* KERNEL */
diff --git a/sys/nfsserver/nfs.h b/sys/nfsserver/nfs.h
new file mode 100644
index 0000000..261fd42
--- /dev/null
+++ b/sys/nfsserver/nfs.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Tunable constants for nfs
+ */
+
+#define NFS_MAXIOVEC 34
+#define NFS_HZ 25 /* Ticks per second for NFS timeouts */
+#define NFS_TIMEO (1*NFS_HZ) /* Default timeout = 1 second */
+#define NFS_MINTIMEO (1*NFS_HZ) /* Min timeout to use */
+#define NFS_MAXTIMEO (60*NFS_HZ) /* Max timeout to backoff to */
+#define NFS_MINIDEMTIMEO (5*NFS_HZ) /* Min timeout for non-idempotent ops*/
+#define NFS_MAXREXMIT 100 /* Stop counting after this many */
+#define NFS_MAXWINDOW 1024 /* Max number of outstanding requests */
+#define NFS_RETRANS 10 /* Num of retrans for soft mounts */
+#define NFS_MAXGRPS 16 /* Max. size of groups list */
+#define NFS_MINATTRTIMO 5 /* Attribute cache timeout in sec */
+#define NFS_MAXATTRTIMO 60
+#define NFS_WSIZE 8192 /* Def. write data size <= 8192 */
+#define NFS_RSIZE 8192 /* Def. read data size <= 8192 */
+#define NFS_DEFRAHEAD 1 /* Def. read ahead # blocks */
+#define NFS_MAXRAHEAD 4 /* Max. read ahead # blocks */
+#define NFS_MAXREADDIR NFS_MAXDATA /* Max. size of directory read */
+#define NFS_MAXUIDHASH 64 /* Max. # of hashed uid entries/mp */
+#define NFS_MAXASYNCDAEMON 20 /* Max. number async_daemons runable */
+#define NFS_DIRBLKSIZ 1024 /* Size of an NFS directory block */
+#define NMOD(a) ((a) % nfs_asyncdaemons)
+
+/*
+ * Set the attribute timeout based on how recently the file has been modified.
+ */
+#define NFS_ATTRTIMEO(np) \
+ ((((np)->n_flag & NMODIFIED) || \
+ (time.tv_sec - (np)->n_mtime) / 10 < NFS_MINATTRTIMO) ? NFS_MINATTRTIMO : \
+ ((time.tv_sec - (np)->n_mtime) / 10 > NFS_MAXATTRTIMO ? NFS_MAXATTRTIMO : \
+ (time.tv_sec - (np)->n_mtime) / 10))
+
+/*
+ * Structures for the nfssvc(2) syscall. Not that anyone but nfsd and mount_nfs
+ * should ever try and use it.
+ */
+struct nfsd_args {
+ int sock; /* Socket to serve */
+ caddr_t name; /* Client address for connection based sockets */
+ int namelen; /* Length of name */
+};
+
+struct nfsd_srvargs {
+ struct nfsd *nsd_nfsd; /* Pointer to in kernel nfsd struct */
+ uid_t nsd_uid; /* Effective uid mapped to cred */
+ u_long nsd_haddr; /* Ip address of client */
+ struct ucred nsd_cr; /* Cred. uid maps to */
+ int nsd_authlen; /* Length of auth string (ret) */
+ char *nsd_authstr; /* Auth string (ret) */
+};
+
+struct nfsd_cargs {
+ char *ncd_dirp; /* Mount dir path */
+ uid_t ncd_authuid; /* Effective uid */
+ int ncd_authtype; /* Type of authenticator */
+ int ncd_authlen; /* Length of authenticator string */
+ char *ncd_authstr; /* Authenticator string */
+};
+
+/*
+ * Stats structure
+ */
+struct nfsstats {
+ int attrcache_hits;
+ int attrcache_misses;
+ int lookupcache_hits;
+ int lookupcache_misses;
+ int direofcache_hits;
+ int direofcache_misses;
+ int biocache_reads;
+ int read_bios;
+ int read_physios;
+ int biocache_writes;
+ int write_bios;
+ int write_physios;
+ int biocache_readlinks;
+ int readlink_bios;
+ int biocache_readdirs;
+ int readdir_bios;
+ int rpccnt[NFS_NPROCS];
+ int rpcretries;
+ int srvrpccnt[NFS_NPROCS];
+ int srvrpc_errs;
+ int srv_errs;
+ int rpcrequests;
+ int rpctimeouts;
+ int rpcunexpected;
+ int rpcinvalid;
+ int srvcache_inproghits;
+ int srvcache_idemdonehits;
+ int srvcache_nonidemdonehits;
+ int srvcache_misses;
+ int srvnqnfs_leases;
+ int srvnqnfs_maxleases;
+ int srvnqnfs_getleases;
+};
+
+/*
+ * Flags for nfssvc() system call.
+ */
+#define NFSSVC_BIOD 0x002
+#define NFSSVC_NFSD 0x004
+#define NFSSVC_ADDSOCK 0x008
+#define NFSSVC_AUTHIN 0x010
+#define NFSSVC_GOTAUTH 0x040
+#define NFSSVC_AUTHINFAIL 0x080
+#define NFSSVC_MNTD 0x100
+
+/*
+ * The set of signals the interrupt an I/O in progress for NFSMNT_INT mounts.
+ * What should be in this set is open to debate, but I believe that since
+ * I/O system calls on ufs are never interrupted by signals the set should
+ * be minimal. My reasoning is that many current programs that use signals
+ * such as SIGALRM will not expect file I/O system calls to be interrupted
+ * by them and break.
+ */
+#ifdef KERNEL
+#define NFSINT_SIGMASK (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \
+ sigmask(SIGHUP)|sigmask(SIGQUIT))
+
+/*
+ * Socket errors ignored for connectionless sockets??
+ * For now, ignore them all
+ */
+#define NFSIGNORE_SOERROR(s, e) \
+ ((e) != EINTR && (e) != ERESTART && (e) != EWOULDBLOCK && \
+ ((s) & PR_CONNREQUIRED) == 0)
+
+/*
+ * Nfs outstanding request list element
+ */
+struct nfsreq {
+ struct nfsreq *r_next;
+ struct nfsreq *r_prev;
+ struct mbuf *r_mreq;
+ struct mbuf *r_mrep;
+ struct mbuf *r_md;
+ caddr_t r_dpos;
+ struct nfsmount *r_nmp;
+ struct vnode *r_vp;
+ u_long r_xid;
+ int r_flags; /* flags on request, see below */
+ int r_retry; /* max retransmission count */
+ int r_rexmit; /* current retrans count */
+ int r_timer; /* tick counter on reply */
+ int r_procnum; /* NFS procedure number */
+ int r_rtt; /* RTT for rpc */
+ struct proc *r_procp; /* Proc that did I/O system call */
+};
+
+/* Flag values for r_flags */
+#define R_TIMING 0x01 /* timing request (in mntp) */
+#define R_SENT 0x02 /* request has been sent */
+#define R_SOFTTERM 0x04 /* soft mnt, too many retries */
+#define R_INTR 0x08 /* intr mnt, signal pending */
+#define R_SOCKERR 0x10 /* Fatal error on socket */
+#define R_TPRINTFMSG 0x20 /* Did a tprintf msg. */
+#define R_MUSTRESEND 0x40 /* Must resend request */
+#define R_GETONEREP 0x80 /* Probe for one reply only */
+
+struct nfsstats nfsstats;
+
+/*
+ * A list of nfssvc_sock structures is maintained with all the sockets
+ * that require service by the nfsd.
+ * The nfsuid structs hang off of the nfssvc_sock structs in both lru
+ * and uid hash lists.
+ */
+#define NUIDHASHSIZ 32
+#define NUIDHASH(uid) ((uid) & (NUIDHASHSIZ - 1))
+
+/*
+ * Network address hash list element
+ */
+union nethostaddr {
+ u_long had_inetaddr;
+ struct mbuf *had_nam;
+};
+
+struct nfsuid {
+ struct nfsuid *nu_lrunext; /* MUST be first */
+ struct nfsuid *nu_lruprev;
+ struct nfsuid *nu_hnext;
+ struct nfsuid *nu_hprev;
+ int nu_flag; /* Flags */
+ uid_t nu_uid; /* Uid mapped by this entry */
+ union nethostaddr nu_haddr; /* Host addr. for dgram sockets */
+ struct ucred nu_cr; /* Cred uid mapped to */
+};
+
+#define nu_inetaddr nu_haddr.had_inetaddr
+#define nu_nam nu_haddr.had_nam
+/* Bits for nu_flag */
+#define NU_INETADDR 0x1
+
+struct nfssvc_sock {
+ struct nfsuid *ns_lrunext; /* MUST be first */
+ struct nfsuid *ns_lruprev;
+ struct nfssvc_sock *ns_next;
+ struct nfssvc_sock *ns_prev;
+ int ns_flag;
+ u_long ns_sref;
+ struct file *ns_fp;
+ struct socket *ns_so;
+ int ns_solock;
+ struct mbuf *ns_nam;
+ int ns_cc;
+ struct mbuf *ns_raw;
+ struct mbuf *ns_rawend;
+ int ns_reclen;
+ struct mbuf *ns_rec;
+ struct mbuf *ns_recend;
+ int ns_numuids;
+ struct nfsuid *ns_uidh[NUIDHASHSIZ];
+};
+
+/* Bits for "ns_flag" */
+#define SLP_VALID 0x01
+#define SLP_DOREC 0x02
+#define SLP_NEEDQ 0x04
+#define SLP_DISCONN 0x08
+#define SLP_GETSTREAM 0x10
+#define SLP_INIT 0x20
+#define SLP_WANTINIT 0x40
+
+#define SLP_ALLFLAGS 0xff
+
+/*
+ * One of these structures is allocated for each nfsd.
+ */
+struct nfsd {
+ struct nfsd *nd_next; /* Must be first */
+ struct nfsd *nd_prev;
+ int nd_flag; /* NFSD_ flags */
+ struct nfssvc_sock *nd_slp; /* Current socket */
+ struct mbuf *nd_nam; /* Client addr for datagram req. */
+ struct mbuf *nd_mrep; /* Req. mbuf list */
+ struct mbuf *nd_md;
+ caddr_t nd_dpos; /* Position in list */
+ int nd_procnum; /* RPC procedure number */
+ u_long nd_retxid; /* RPC xid */
+ int nd_repstat; /* Reply status value */
+ struct ucred nd_cr; /* Credentials for req. */
+ int nd_nqlflag; /* Leasing flag */
+ int nd_duration; /* Lease duration */
+ int nd_authlen; /* Authenticator len */
+ u_char nd_authstr[RPCAUTH_MAXSIZ]; /* Authenticator data */
+ struct proc *nd_procp; /* Proc ptr */
+};
+
+#define NFSD_WAITING 0x01
+#define NFSD_CHECKSLP 0x02
+#define NFSD_REQINPROG 0x04
+#define NFSD_NEEDAUTH 0x08
+#define NFSD_AUTHFAIL 0x10
+#endif /* KERNEL */
diff --git a/sys/nfsserver/nfs_serv.c b/sys/nfsserver/nfs_serv.c
new file mode 100644
index 0000000..32b0da2
--- /dev/null
+++ b/sys/nfsserver/nfs_serv.c
@@ -0,0 +1,1929 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_serv.c 8.3 (Berkeley) 1/12/94
+ */
+
+/*
+ * nfs version 2 server calls to vnode ops
+ * - these routines generally have 3 phases
+ * 1 - break down and validate rpc request in mbuf list
+ * 2 - do the vnode ops for the request
+ * (surprisingly ?? many are very similar to syscalls in vfs_syscalls.c)
+ * 3 - build the rpc reply in an mbuf list
+ * nb:
+ * - do not mix the phases, since the nfsm_?? macros can return failures
+ * on a bad rpc or similar and do not do any vrele() or vput()'s
+ *
+ * - the nfsm_reply() macro generates an nfs rpc reply with the nfs
+ * error number iff error != 0 whereas
+ * returning an error from the server function implies a fatal error
+ * such as a badly constructed rpc request that should be dropped without
+ * a reply.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/mbuf.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+
+#include <vm/vm.h>
+
+#include <nfs/nfsv2.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfs.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nqnfs.h>
+
+/* Defs */
+#define TRUE 1
+#define FALSE 0
+
+/* Global vars */
+extern u_long nfs_procids[NFS_NPROCS];
+extern u_long nfs_xdrneg1;
+extern u_long nfs_false, nfs_true;
+nfstype nfs_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFNON,
+ NFCHR, NFNON };
+
+/*
+ * nqnfs access service
+ */
+int
+nqnfsrv_access(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache = 0, mode = 0;
+ char *cp2;
+ struct mbuf *mb, *mreq;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ if (*tl++ == nfs_true)
+ mode |= VREAD;
+ if (*tl++ == nfs_true)
+ mode |= VWRITE;
+ if (*tl == nfs_true)
+ mode |= VEXEC;
+ error = nfsrv_access(vp, mode, cred, rdonly, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(0);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs getattr service
+ */
+int
+nfsrv_getattr(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct nfsv2_fattr *fp;
+ struct vattr va;
+ register struct vattr *vap = &va;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ nqsrv_getl(vp, NQL_READ);
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ nfsm_srvdone;
+}
+
+/*
+ * nfs setattr service
+ */
+int
+nfsrv_setattr(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct vattr va;
+ register struct vattr *vap = &va;
+ register struct nfsv2_sattr *sp;
+ register struct nfsv2_fattr *fp;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ u_quad_t frev, frev2;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_SATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ nqsrv_getl(vp, NQL_WRITE);
+ VATTR_NULL(vap);
+ /*
+ * Nah nah nah nah na nah
+ * There is a bug in the Sun client that puts 0xffff in the mode
+ * field of sattr when it should put in 0xffffffff. The u_short
+ * doesn't sign extend.
+ * --> check the low order 2 bytes for 0xffff
+ */
+ if ((fxdr_unsigned(int, sp->sa_mode) & 0xffff) != 0xffff)
+ vap->va_mode = nfstov_mode(sp->sa_mode);
+ if (sp->sa_uid != nfs_xdrneg1)
+ vap->va_uid = fxdr_unsigned(uid_t, sp->sa_uid);
+ if (sp->sa_gid != nfs_xdrneg1)
+ vap->va_gid = fxdr_unsigned(gid_t, sp->sa_gid);
+ if (nfsd->nd_nqlflag == NQL_NOVAL) {
+ if (sp->sa_nfssize != nfs_xdrneg1)
+ vap->va_size = fxdr_unsigned(u_quad_t, sp->sa_nfssize);
+ if (sp->sa_nfsatime.nfs_sec != nfs_xdrneg1) {
+#ifdef notyet
+ fxdr_nfstime(&sp->sa_nfsatime, &vap->va_atime);
+#else
+ vap->va_atime.ts_sec =
+ fxdr_unsigned(long, sp->sa_nfsatime.nfs_sec);
+ vap->va_atime.ts_nsec = 0;
+#endif
+ }
+ if (sp->sa_nfsmtime.nfs_sec != nfs_xdrneg1)
+ fxdr_nfstime(&sp->sa_nfsmtime, &vap->va_mtime);
+ } else {
+ fxdr_hyper(&sp->sa_nqsize, &vap->va_size);
+ fxdr_nqtime(&sp->sa_nqatime, &vap->va_atime);
+ fxdr_nqtime(&sp->sa_nqmtime, &vap->va_mtime);
+ vap->va_flags = fxdr_unsigned(u_long, sp->sa_nqflags);
+ }
+
+ /*
+ * If the size is being changed write acces is required, otherwise
+ * just check for a read only file system.
+ */
+ if (vap->va_size == ((u_quad_t)((quad_t) -1))) {
+ if (rdonly || (vp->v_mount->mnt_flag & MNT_RDONLY)) {
+ error = EROFS;
+ goto out;
+ }
+ } else {
+ if (vp->v_type == VDIR) {
+ error = EISDIR;
+ goto out;
+ } else if (error = nfsrv_access(vp, VWRITE, cred, rdonly,
+ nfsd->nd_procp))
+ goto out;
+ }
+ if (error = VOP_SETATTR(vp, vap, cred, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+out:
+ vput(vp);
+ nfsm_reply(NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL) + 2*NFSX_UNSIGNED);
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ if (nfsd->nd_nqlflag != NQL_NOVAL) {
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ txdr_hyper(&frev2, tl);
+ }
+ nfsm_srvdone;
+}
+
+/*
+ * nfs lookup rpc
+ */
+int
+nfsrv_lookup(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct nfsv2_fattr *fp;
+ struct nameidata nd;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ register caddr_t cp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, cache, duration2, cache2, len;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct vattr va, *vap = &va;
+ u_quad_t frev, frev2;
+
+ fhp = &nfh.fh_generic;
+ duration2 = 0;
+ if (nfsd->nd_nqlflag != NQL_NOVAL) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ duration2 = fxdr_unsigned(int, *tl);
+ }
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = LOOKUP;
+ nd.ni_cnd.cn_flags = LOCKLEAF | SAVESTART;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ nfsm_reply(0);
+ nqsrv_getl(nd.ni_startdir, NQL_READ);
+ vrele(nd.ni_startdir);
+ FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ vp = nd.ni_vp;
+ bzero((caddr_t)fhp, sizeof(nfh));
+ fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+ if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ if (duration2)
+ (void) nqsrv_getlease(vp, &duration2, NQL_READ, nfsd,
+ nam, &cache2, &frev2, cred);
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_FH + NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL) + 5*NFSX_UNSIGNED);
+ if (nfsd->nd_nqlflag != NQL_NOVAL) {
+ if (duration2) {
+ nfsm_build(tl, u_long *, 5*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(NQL_READ);
+ *tl++ = txdr_unsigned(cache2);
+ *tl++ = txdr_unsigned(duration2);
+ txdr_hyper(&frev2, tl);
+ } else {
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = 0;
+ }
+ }
+ nfsm_srvfhtom(fhp);
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ nfsm_srvdone;
+}
+
+/*
+ * nfs readlink service
+ */
+int
+nfsrv_readlink(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct iovec iv[(NFS_MAXPATHLEN+MLEN-1)/MLEN];
+ register struct iovec *ivp = iv;
+ register struct mbuf *mp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache, i, tlen, len;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mp2 = 0, *mp3 = 0, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct uio io, *uiop = &io;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ len = 0;
+ i = 0;
+ while (len < NFS_MAXPATHLEN) {
+ MGET(mp, M_WAIT, MT_DATA);
+ MCLGET(mp, M_WAIT);
+ mp->m_len = NFSMSIZ(mp);
+ if (len == 0)
+ mp3 = mp2 = mp;
+ else {
+ mp2->m_next = mp;
+ mp2 = mp;
+ }
+ if ((len+mp->m_len) > NFS_MAXPATHLEN) {
+ mp->m_len = NFS_MAXPATHLEN-len;
+ len = NFS_MAXPATHLEN;
+ } else
+ len += mp->m_len;
+ ivp->iov_base = mtod(mp, caddr_t);
+ ivp->iov_len = mp->m_len;
+ i++;
+ ivp++;
+ }
+ uiop->uio_iov = iv;
+ uiop->uio_iovcnt = i;
+ uiop->uio_offset = 0;
+ uiop->uio_resid = len;
+ uiop->uio_rw = UIO_READ;
+ uiop->uio_segflg = UIO_SYSSPACE;
+ uiop->uio_procp = (struct proc *)0;
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly)) {
+ m_freem(mp3);
+ nfsm_reply(0);
+ }
+ if (vp->v_type != VLNK) {
+ error = EINVAL;
+ goto out;
+ }
+ nqsrv_getl(vp, NQL_READ);
+ error = VOP_READLINK(vp, uiop, cred);
+out:
+ vput(vp);
+ if (error)
+ m_freem(mp3);
+ nfsm_reply(NFSX_UNSIGNED);
+ if (uiop->uio_resid > 0) {
+ len -= uiop->uio_resid;
+ tlen = nfsm_rndup(len);
+ nfsm_adj(mp3, NFS_MAXPATHLEN-tlen, tlen-len);
+ }
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(len);
+ mb->m_next = mp3;
+ nfsm_srvdone;
+}
+
+/*
+ * nfs read service
+ */
+int
+nfsrv_read(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct iovec *iv;
+ struct iovec *iv2;
+ register struct mbuf *m;
+ register struct nfsv2_fattr *fp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache, i, cnt, len, left, siz, tlen;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct mbuf *m2;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct uio io, *uiop = &io;
+ struct vattr va, *vap = &va;
+ off_t off;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ if (nfsd->nd_nqlflag == NQL_NOVAL) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ off = (off_t)fxdr_unsigned(u_long, *tl);
+ } else {
+ nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+ fxdr_hyper(tl, &off);
+ }
+ nfsm_srvstrsiz(cnt, NFS_MAXDATA);
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ if (vp->v_type != VREG) {
+ error = (vp->v_type == VDIR) ? EISDIR : EACCES;
+ vput(vp);
+ nfsm_reply(0);
+ }
+ nqsrv_getl(vp, NQL_READ);
+ if ((error = nfsrv_access(vp, VREAD, cred, rdonly, nfsd->nd_procp)) &&
+ (error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp))) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ if (error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ if (off >= vap->va_size)
+ cnt = 0;
+ else if ((off + cnt) > vap->va_size)
+ cnt = nfsm_rndup(vap->va_size - off);
+ nfsm_reply(NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL)+NFSX_UNSIGNED+nfsm_rndup(cnt));
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ len = left = cnt;
+ if (cnt > 0) {
+ /*
+ * Generate the mbuf list with the uio_iov ref. to it.
+ */
+ i = 0;
+ m = m2 = mb;
+ MALLOC(iv, struct iovec *,
+ ((NFS_MAXDATA+MLEN-1)/MLEN) * sizeof (struct iovec),
+ M_TEMP, M_WAITOK);
+ iv2 = iv;
+ while (left > 0) {
+ siz = min(M_TRAILINGSPACE(m), left);
+ if (siz > 0) {
+ m->m_len += siz;
+ iv->iov_base = bpos;
+ iv->iov_len = siz;
+ iv++;
+ i++;
+ left -= siz;
+ }
+ if (left > 0) {
+ MGET(m, M_WAIT, MT_DATA);
+ MCLGET(m, M_WAIT);
+ m->m_len = 0;
+ m2->m_next = m;
+ m2 = m;
+ bpos = mtod(m, caddr_t);
+ }
+ }
+ uiop->uio_iov = iv2;
+ uiop->uio_iovcnt = i;
+ uiop->uio_offset = off;
+ uiop->uio_resid = cnt;
+ uiop->uio_rw = UIO_READ;
+ uiop->uio_segflg = UIO_SYSSPACE;
+ error = VOP_READ(vp, uiop, IO_NODELOCKED, cred);
+ off = uiop->uio_offset;
+ FREE((caddr_t)iv2, M_TEMP);
+ if (error || (error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp))) {
+ m_freem(mreq);
+ vput(vp);
+ nfsm_reply(0);
+ }
+ } else
+ uiop->uio_resid = 0;
+ vput(vp);
+ nfsm_srvfillattr;
+ len -= uiop->uio_resid;
+ tlen = nfsm_rndup(len);
+ if (cnt != tlen || tlen != len)
+ nfsm_adj(mb, cnt-tlen, tlen-len);
+ *tl = txdr_unsigned(len);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs write service
+ */
+int
+nfsrv_write(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct iovec *ivp;
+ register struct mbuf *mp;
+ register struct nfsv2_fattr *fp;
+ struct iovec iv[NFS_MAXIOVEC];
+ struct vattr va;
+ register struct vattr *vap = &va;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache, siz, len, xfer;
+ int ioflags = IO_SYNC | IO_NODELOCKED;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct uio io, *uiop = &io;
+ off_t off;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED);
+ if (nfsd->nd_nqlflag == NQL_NOVAL) {
+ off = (off_t)fxdr_unsigned(u_long, *++tl);
+ tl += 2;
+ } else {
+ fxdr_hyper(tl, &off);
+ tl += 2;
+ if (fxdr_unsigned(u_long, *tl++))
+ ioflags |= IO_APPEND;
+ }
+ len = fxdr_unsigned(long, *tl);
+ if (len > NFS_MAXDATA || len <= 0) {
+ error = EBADRPC;
+ nfsm_reply(0);
+ }
+ if (dpos == (mtod(md, caddr_t)+md->m_len)) {
+ mp = md->m_next;
+ if (mp == NULL) {
+ error = EBADRPC;
+ nfsm_reply(0);
+ }
+ } else {
+ mp = md;
+ siz = dpos-mtod(mp, caddr_t);
+ mp->m_len -= siz;
+ NFSMADV(mp, siz);
+ }
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ if (vp->v_type != VREG) {
+ error = (vp->v_type == VDIR) ? EISDIR : EACCES;
+ vput(vp);
+ nfsm_reply(0);
+ }
+ nqsrv_getl(vp, NQL_WRITE);
+ if (error = nfsrv_access(vp, VWRITE, cred, rdonly, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ uiop->uio_resid = 0;
+ uiop->uio_rw = UIO_WRITE;
+ uiop->uio_segflg = UIO_SYSSPACE;
+ uiop->uio_procp = (struct proc *)0;
+ /*
+ * Do up to NFS_MAXIOVEC mbufs of write each iteration of the
+ * loop until done.
+ */
+ while (len > 0 && uiop->uio_resid == 0) {
+ ivp = iv;
+ siz = 0;
+ uiop->uio_iov = ivp;
+ uiop->uio_iovcnt = 0;
+ uiop->uio_offset = off;
+ while (len > 0 && uiop->uio_iovcnt < NFS_MAXIOVEC && mp != NULL) {
+ ivp->iov_base = mtod(mp, caddr_t);
+ if (len < mp->m_len)
+ ivp->iov_len = xfer = len;
+ else
+ ivp->iov_len = xfer = mp->m_len;
+#ifdef notdef
+ /* Not Yet .. */
+ if (M_HASCL(mp) && (((u_long)ivp->iov_base) & CLOFSET) == 0)
+ ivp->iov_op = NULL; /* what should it be ?? */
+ else
+ ivp->iov_op = NULL;
+#endif
+ uiop->uio_iovcnt++;
+ ivp++;
+ len -= xfer;
+ siz += xfer;
+ mp = mp->m_next;
+ }
+ if (len > 0 && mp == NULL) {
+ error = EBADRPC;
+ vput(vp);
+ nfsm_reply(0);
+ }
+ uiop->uio_resid = siz;
+ if (error = VOP_WRITE(vp, uiop, ioflags, cred)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ off = uiop->uio_offset;
+ }
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ if (nfsd->nd_nqlflag != NQL_NOVAL) {
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ txdr_hyper(&vap->va_filerev, tl);
+ }
+ nfsm_srvdone;
+}
+
+/*
+ * nfs create service
+ * now does a truncate to 0 length via. setattr if it already exists
+ */
+int
+nfsrv_create(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct nfsv2_fattr *fp;
+ struct vattr va;
+ register struct vattr *vap = &va;
+ register struct nfsv2_sattr *sp;
+ register u_long *tl;
+ struct nameidata nd;
+ register caddr_t cp;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdev, cache, len, tsize;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ u_quad_t frev;
+
+ nd.ni_cnd.cn_nameiop = 0;
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = CREATE;
+ nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | SAVESTART;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ nfsm_reply(0);
+ VATTR_NULL(vap);
+ nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_SATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ /*
+ * Iff doesn't exist, create it
+ * otherwise just truncate to 0 length
+ * should I set the mode too ??
+ */
+ if (nd.ni_vp == NULL) {
+ vap->va_type = IFTOVT(fxdr_unsigned(u_long, sp->sa_mode));
+ if (vap->va_type == VNON)
+ vap->va_type = VREG;
+ vap->va_mode = nfstov_mode(sp->sa_mode);
+ if (nfsd->nd_nqlflag == NQL_NOVAL)
+ rdev = fxdr_unsigned(long, sp->sa_nfssize);
+ else
+ rdev = fxdr_unsigned(long, sp->sa_nqrdev);
+ if (vap->va_type == VREG || vap->va_type == VSOCK) {
+ vrele(nd.ni_startdir);
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ if (error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap))
+ nfsm_reply(0);
+ FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ } else if (vap->va_type == VCHR || vap->va_type == VBLK ||
+ vap->va_type == VFIFO) {
+ if (vap->va_type == VCHR && rdev == 0xffffffff)
+ vap->va_type = VFIFO;
+ if (vap->va_type == VFIFO) {
+#ifndef FIFO
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ vput(nd.ni_dvp);
+ error = ENXIO;
+ goto out;
+#endif /* FIFO */
+ } else if (error = suser(cred, (u_short *)0)) {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ vput(nd.ni_dvp);
+ goto out;
+ } else
+ vap->va_rdev = (dev_t)rdev;
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ if (error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap)) {
+ vrele(nd.ni_startdir);
+ nfsm_reply(0);
+ }
+ nd.ni_cnd.cn_nameiop = LOOKUP;
+ nd.ni_cnd.cn_flags &= ~(LOCKPARENT | SAVESTART);
+ nd.ni_cnd.cn_proc = nfsd->nd_procp;
+ nd.ni_cnd.cn_cred = nfsd->nd_procp->p_ucred;
+ if (error = lookup(&nd)) {
+ free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ nfsm_reply(0);
+ }
+ FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ if (nd.ni_cnd.cn_flags & ISSYMLINK) {
+ vrele(nd.ni_dvp);
+ vput(nd.ni_vp);
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ error = EINVAL;
+ nfsm_reply(0);
+ }
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ vput(nd.ni_dvp);
+ error = ENXIO;
+ goto out;
+ }
+ vp = nd.ni_vp;
+ } else {
+ vrele(nd.ni_startdir);
+ free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ vp = nd.ni_vp;
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nfsd->nd_nqlflag == NQL_NOVAL) {
+ tsize = fxdr_unsigned(long, sp->sa_nfssize);
+ if (tsize != -1)
+ vap->va_size = (u_quad_t)tsize;
+ else
+ vap->va_size = -1;
+ } else
+ fxdr_hyper(&sp->sa_nqsize, &vap->va_size);
+ if (vap->va_size != -1) {
+ if (error = nfsrv_access(vp, VWRITE, cred,
+ (nd.ni_cnd.cn_flags & RDONLY), nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ nqsrv_getl(vp, NQL_WRITE);
+ if (error = VOP_SETATTR(vp, vap, cred, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ }
+ }
+ bzero((caddr_t)fhp, sizeof(nfh));
+ fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+ if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_FH+NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfhtom(fhp);
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ return (error);
+nfsmout:
+ if (nd.ni_cnd.cn_nameiop || nd.ni_cnd.cn_flags)
+ vrele(nd.ni_startdir);
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ if (nd.ni_vp)
+ vput(nd.ni_vp);
+ return (error);
+
+out:
+ vrele(nd.ni_startdir);
+ free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ nfsm_reply(0);
+ return (0);
+}
+
+/*
+ * nfs remove service
+ */
+int
+nfsrv_remove(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct nameidata nd;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, cache, len;
+ char *cp2;
+ struct mbuf *mb, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = DELETE;
+ nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ nfsm_reply(0);
+ vp = nd.ni_vp;
+ if (vp->v_type == VDIR &&
+ (error = suser(cred, (u_short *)0)))
+ goto out;
+ /*
+ * The root of a mounted filesystem cannot be deleted.
+ */
+ if (vp->v_flag & VROOT) {
+ error = EBUSY;
+ goto out;
+ }
+ if (vp->v_flag & VTEXT)
+ (void) vnode_pager_uncache(vp);
+out:
+ if (!error) {
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ nqsrv_getl(vp, NQL_WRITE);
+ error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vput(vp);
+ }
+ nfsm_reply(0);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs rename service
+ */
+int
+nfsrv_rename(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, cache, len, len2;
+ char *cp2;
+ struct mbuf *mb, *mreq;
+ struct nameidata fromnd, tond;
+ struct vnode *fvp = 0, *tvp, *tdvp;
+ nfsv2fh_t fnfh, tnfh;
+ fhandle_t *ffhp, *tfhp;
+ u_quad_t frev;
+ uid_t saved_uid;
+
+ ffhp = &fnfh.fh_generic;
+ tfhp = &tnfh.fh_generic;
+ fromnd.ni_cnd.cn_nameiop = 0;
+ tond.ni_cnd.cn_nameiop = 0;
+ nfsm_srvmtofh(ffhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ /*
+ * Remember our original uid so that we can reset cr_uid before
+ * the second nfs_namei() call, in case it is remapped.
+ */
+ saved_uid = cred->cr_uid;
+ fromnd.ni_cnd.cn_cred = cred;
+ fromnd.ni_cnd.cn_nameiop = DELETE;
+ fromnd.ni_cnd.cn_flags = WANTPARENT | SAVESTART;
+ if (error = nfs_namei(&fromnd, ffhp, len, nfsd->nd_slp, nam, &md,
+ &dpos, nfsd->nd_procp))
+ nfsm_reply(0);
+ fvp = fromnd.ni_vp;
+ nfsm_srvmtofh(tfhp);
+ nfsm_strsiz(len2, NFS_MAXNAMLEN);
+ cred->cr_uid = saved_uid;
+ tond.ni_cnd.cn_cred = cred;
+ tond.ni_cnd.cn_nameiop = RENAME;
+ tond.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART;
+ if (error = nfs_namei(&tond, tfhp, len2, nfsd->nd_slp, nam, &md,
+ &dpos, nfsd->nd_procp)) {
+ VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
+ vrele(fromnd.ni_dvp);
+ vrele(fvp);
+ goto out1;
+ }
+ tdvp = tond.ni_dvp;
+ tvp = tond.ni_vp;
+ if (tvp != NULL) {
+ if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
+ error = EISDIR;
+ goto out;
+ } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) {
+ error = ENOTDIR;
+ goto out;
+ }
+ if (tvp->v_type == VDIR && tvp->v_mountedhere) {
+ error = EXDEV;
+ goto out;
+ }
+ }
+ if (fvp->v_type == VDIR && fvp->v_mountedhere) {
+ error = EBUSY;
+ goto out;
+ }
+ if (fvp->v_mount != tdvp->v_mount) {
+ error = EXDEV;
+ goto out;
+ }
+ if (fvp == tdvp)
+ error = EINVAL;
+ /*
+ * If source is the same as the destination (that is the
+ * same vnode with the same name in the same directory),
+ * then there is nothing to do.
+ */
+ if (fvp == tvp && fromnd.ni_dvp == tdvp &&
+ fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen &&
+ !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr,
+ fromnd.ni_cnd.cn_namelen))
+ error = -1;
+out:
+ if (!error) {
+ nqsrv_getl(fromnd.ni_dvp, NQL_WRITE);
+ nqsrv_getl(tdvp, NQL_WRITE);
+ if (tvp)
+ nqsrv_getl(tvp, NQL_WRITE);
+ error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd,
+ tond.ni_dvp, tond.ni_vp, &tond.ni_cnd);
+ } else {
+ VOP_ABORTOP(tond.ni_dvp, &tond.ni_cnd);
+ if (tdvp == tvp)
+ vrele(tdvp);
+ else
+ vput(tdvp);
+ if (tvp)
+ vput(tvp);
+ VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
+ vrele(fromnd.ni_dvp);
+ vrele(fvp);
+ }
+ vrele(tond.ni_startdir);
+ FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI);
+out1:
+ vrele(fromnd.ni_startdir);
+ FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI);
+ nfsm_reply(0);
+ return (error);
+
+nfsmout:
+ if (tond.ni_cnd.cn_nameiop || tond.ni_cnd.cn_flags) {
+ vrele(tond.ni_startdir);
+ FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI);
+ }
+ if (fromnd.ni_cnd.cn_nameiop || fromnd.ni_cnd.cn_flags) {
+ vrele(fromnd.ni_startdir);
+ FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI);
+ VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
+ vrele(fromnd.ni_dvp);
+ vrele(fvp);
+ }
+ return (error);
+}
+
+/*
+ * nfs link service
+ */
+int
+nfsrv_link(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct nameidata nd;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache, len;
+ char *cp2;
+ struct mbuf *mb, *mreq;
+ struct vnode *vp, *xp;
+ nfsv2fh_t nfh, dnfh;
+ fhandle_t *fhp, *dfhp;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ dfhp = &dnfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvmtofh(dfhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ if (error = nfsrv_fhtovp(fhp, FALSE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ if (vp->v_type == VDIR && (error = suser(cred, (u_short *)0)))
+ goto out1;
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = CREATE;
+ nd.ni_cnd.cn_flags = LOCKPARENT;
+ if (error = nfs_namei(&nd, dfhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ goto out1;
+ xp = nd.ni_vp;
+ if (xp != NULL) {
+ error = EEXIST;
+ goto out;
+ }
+ xp = nd.ni_dvp;
+ if (vp->v_mount != xp->v_mount)
+ error = EXDEV;
+out:
+ if (!error) {
+ nqsrv_getl(vp, NQL_WRITE);
+ nqsrv_getl(xp, NQL_WRITE);
+ error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ if (nd.ni_vp)
+ vrele(nd.ni_vp);
+ }
+out1:
+ vrele(vp);
+ nfsm_reply(0);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs symbolic link service
+ */
+int
+nfsrv_symlink(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct vattr va;
+ struct nameidata nd;
+ register struct vattr *vap = &va;
+ register u_long *tl;
+ register long t1;
+ struct nfsv2_sattr *sp;
+ caddr_t bpos;
+ struct uio io;
+ struct iovec iv;
+ int error = 0, cache, len, len2;
+ char *pathcp, *cp2;
+ struct mbuf *mb, *mreq;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ u_quad_t frev;
+
+ pathcp = (char *)0;
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = CREATE;
+ nd.ni_cnd.cn_flags = LOCKPARENT;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ goto out;
+ nfsm_strsiz(len2, NFS_MAXPATHLEN);
+ MALLOC(pathcp, caddr_t, len2 + 1, M_TEMP, M_WAITOK);
+ iv.iov_base = pathcp;
+ iv.iov_len = len2;
+ io.uio_resid = len2;
+ io.uio_offset = 0;
+ io.uio_iov = &iv;
+ io.uio_iovcnt = 1;
+ io.uio_segflg = UIO_SYSSPACE;
+ io.uio_rw = UIO_READ;
+ io.uio_procp = (struct proc *)0;
+ nfsm_mtouio(&io, len2);
+ nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_SATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ *(pathcp + len2) = '\0';
+ if (nd.ni_vp) {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vrele(nd.ni_vp);
+ error = EEXIST;
+ goto out;
+ }
+ VATTR_NULL(vap);
+ vap->va_mode = fxdr_unsigned(u_short, sp->sa_mode);
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap, pathcp);
+out:
+ if (pathcp)
+ FREE(pathcp, M_TEMP);
+ nfsm_reply(0);
+ return (error);
+nfsmout:
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ if (nd.ni_vp)
+ vrele(nd.ni_vp);
+ if (pathcp)
+ FREE(pathcp, M_TEMP);
+ return (error);
+}
+
+/*
+ * nfs mkdir service
+ */
+int
+nfsrv_mkdir(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct vattr va;
+ register struct vattr *vap = &va;
+ register struct nfsv2_fattr *fp;
+ struct nameidata nd;
+ register caddr_t cp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, cache, len;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = CREATE;
+ nd.ni_cnd.cn_flags = LOCKPARENT;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ nfsm_reply(0);
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ VATTR_NULL(vap);
+ vap->va_type = VDIR;
+ vap->va_mode = nfstov_mode(*tl++);
+ vp = nd.ni_vp;
+ if (vp != NULL) {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vrele(vp);
+ error = EEXIST;
+ nfsm_reply(0);
+ }
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ if (error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap))
+ nfsm_reply(0);
+ vp = nd.ni_vp;
+ bzero((caddr_t)fhp, sizeof(nfh));
+ fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+ if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_FH+NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfhtom(fhp);
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ return (error);
+nfsmout:
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ if (nd.ni_vp)
+ vrele(nd.ni_vp);
+ return (error);
+}
+
+/*
+ * nfs rmdir service
+ */
+int
+nfsrv_rmdir(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, cache, len;
+ char *cp2;
+ struct mbuf *mb, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct nameidata nd;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = DELETE;
+ nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ nfsm_reply(0);
+ vp = nd.ni_vp;
+ if (vp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto out;
+ }
+ /*
+ * No rmdir "." please.
+ */
+ if (nd.ni_dvp == vp) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * The root of a mounted filesystem cannot be deleted.
+ */
+ if (vp->v_flag & VROOT)
+ error = EBUSY;
+out:
+ if (!error) {
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ nqsrv_getl(vp, NQL_WRITE);
+ error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vput(vp);
+ }
+ nfsm_reply(0);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs readdir service
+ * - mallocs what it thinks is enough to read
+ * count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR
+ * - calls VOP_READDIR()
+ * - loops around building the reply
+ * if the output generated exceeds count break out of loop
+ * The nfsm_clget macro is used here so that the reply will be packed
+ * tightly in mbuf clusters.
+ * - it only knows that it has encountered eof when the VOP_READDIR()
+ * reads nothing
+ * - as such one readdir rpc will return eof false although you are there
+ * and then the next will return eof
+ * - it trims out records with d_fileno == 0
+ * this doesn't matter for Unix clients, but they might confuse clients
+ * for other os'.
+ * NB: It is tempting to set eof to true if the VOP_READDIR() reads less
+ * than requested, but this may not apply to all filesystems. For
+ * example, client NFS does not { although it is never remote mounted
+ * anyhow }
+ * The alternate call nqnfsrv_readdirlook() does lookups as well.
+ * PS: The NFS protocol spec. does not clarify what the "count" byte
+ * argument is a count of.. just name strings and file id's or the
+ * entire reply rpc or ...
+ * I tried just file name and id sizes and it confused the Sun client,
+ * so I am using the full rpc size now. The "paranoia.." comment refers
+ * to including the status longwords that are not a part of the dir.
+ * "entry" structures, but are in the rpc.
+ */
+struct flrep {
+ u_long fl_cachable;
+ u_long fl_duration;
+ u_long fl_frev[2];
+ nfsv2fh_t fl_nfh;
+ u_long fl_fattr[NFSX_NQFATTR / sizeof (u_long)];
+};
+
+int
+nfsrv_readdir(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register char *bp, *be;
+ register struct mbuf *mp;
+ register struct dirent *dp;
+ register caddr_t cp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ struct mbuf *mb, *mb2, *mreq, *mp2;
+ char *cpos, *cend, *cp2, *rbuf;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct uio io;
+ struct iovec iv;
+ int len, nlen, rem, xfer, tsiz, i, error = 0;
+ int siz, cnt, fullsiz, eofflag, rdonly, cache;
+ u_quad_t frev;
+ u_long on, off, toff;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
+ toff = fxdr_unsigned(u_long, *tl++);
+ off = (toff & ~(NFS_DIRBLKSIZ-1));
+ on = (toff & (NFS_DIRBLKSIZ-1));
+ cnt = fxdr_unsigned(int, *tl);
+ siz = ((cnt+NFS_DIRBLKSIZ-1) & ~(NFS_DIRBLKSIZ-1));
+ if (cnt > NFS_MAXREADDIR)
+ siz = NFS_MAXREADDIR;
+ fullsiz = siz;
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ nqsrv_getl(vp, NQL_READ);
+ if (error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ VOP_UNLOCK(vp);
+ MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
+again:
+ iv.iov_base = rbuf;
+ iv.iov_len = fullsiz;
+ io.uio_iov = &iv;
+ io.uio_iovcnt = 1;
+ io.uio_offset = (off_t)off;
+ io.uio_resid = fullsiz;
+ io.uio_segflg = UIO_SYSSPACE;
+ io.uio_rw = UIO_READ;
+ io.uio_procp = (struct proc *)0;
+ error = VOP_READDIR(vp, &io, cred);
+ off = (off_t)io.uio_offset;
+ if (error) {
+ vrele(vp);
+ free((caddr_t)rbuf, M_TEMP);
+ nfsm_reply(0);
+ }
+ if (io.uio_resid < fullsiz)
+ eofflag = 0;
+ else
+ eofflag = 1;
+ if (io.uio_resid) {
+ siz -= io.uio_resid;
+
+ /*
+ * If nothing read, return eof
+ * rpc reply
+ */
+ if (siz == 0) {
+ vrele(vp);
+ nfsm_reply(2*NFSX_UNSIGNED);
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = nfs_false;
+ *tl = nfs_true;
+ FREE((caddr_t)rbuf, M_TEMP);
+ return (0);
+ }
+ }
+
+ /*
+ * Check for degenerate cases of nothing useful read.
+ * If so go try again
+ */
+ cpos = rbuf + on;
+ cend = rbuf + siz;
+ dp = (struct dirent *)cpos;
+ while (cpos < cend && dp->d_fileno == 0) {
+ cpos += dp->d_reclen;
+ dp = (struct dirent *)cpos;
+ }
+ if (cpos >= cend) {
+ toff = off;
+ siz = fullsiz;
+ on = 0;
+ goto again;
+ }
+
+ cpos = rbuf + on;
+ cend = rbuf + siz;
+ dp = (struct dirent *)cpos;
+ len = 3*NFSX_UNSIGNED; /* paranoia, probably can be 0 */
+ nfsm_reply(siz);
+ mp = mp2 = mb;
+ bp = bpos;
+ be = bp + M_TRAILINGSPACE(mp);
+
+ /* Loop through the records and build reply */
+ while (cpos < cend) {
+ if (dp->d_fileno != 0) {
+ nlen = dp->d_namlen;
+ rem = nfsm_rndup(nlen)-nlen;
+ len += (4*NFSX_UNSIGNED + nlen + rem);
+ if (len > cnt) {
+ eofflag = 0;
+ break;
+ }
+ /*
+ * Build the directory record xdr from
+ * the dirent entry.
+ */
+ nfsm_clget;
+ *tl = nfs_true;
+ bp += NFSX_UNSIGNED;
+ nfsm_clget;
+ *tl = txdr_unsigned(dp->d_fileno);
+ bp += NFSX_UNSIGNED;
+ nfsm_clget;
+ *tl = txdr_unsigned(nlen);
+ bp += NFSX_UNSIGNED;
+
+ /* And loop around copying the name */
+ xfer = nlen;
+ cp = dp->d_name;
+ while (xfer > 0) {
+ nfsm_clget;
+ if ((bp+xfer) > be)
+ tsiz = be-bp;
+ else
+ tsiz = xfer;
+ bcopy(cp, bp, tsiz);
+ bp += tsiz;
+ xfer -= tsiz;
+ if (xfer > 0)
+ cp += tsiz;
+ }
+ /* And null pad to a long boundary */
+ for (i = 0; i < rem; i++)
+ *bp++ = '\0';
+ nfsm_clget;
+
+ /* Finish off the record */
+ toff += dp->d_reclen;
+ *tl = txdr_unsigned(toff);
+ bp += NFSX_UNSIGNED;
+ } else
+ toff += dp->d_reclen;
+ cpos += dp->d_reclen;
+ dp = (struct dirent *)cpos;
+ }
+ vrele(vp);
+ nfsm_clget;
+ *tl = nfs_false;
+ bp += NFSX_UNSIGNED;
+ nfsm_clget;
+ if (eofflag)
+ *tl = nfs_true;
+ else
+ *tl = nfs_false;
+ bp += NFSX_UNSIGNED;
+ if (mp != mb) {
+ if (bp < be)
+ mp->m_len = bp - mtod(mp, caddr_t);
+ } else
+ mp->m_len += bp - bpos;
+ FREE(rbuf, M_TEMP);
+ nfsm_srvdone;
+}
+
+int
+nqnfsrv_readdirlook(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register char *bp, *be;
+ register struct mbuf *mp;
+ register struct dirent *dp;
+ register caddr_t cp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ struct mbuf *mb, *mb2, *mreq, *mp2;
+ char *cpos, *cend, *cp2, *rbuf;
+ struct vnode *vp, *nvp;
+ struct flrep fl;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct uio io;
+ struct iovec iv;
+ struct vattr va, *vap = &va;
+ struct nfsv2_fattr *fp;
+ int len, nlen, rem, xfer, tsiz, i, error = 0, duration2, cache2;
+ int siz, cnt, fullsiz, eofflag, rdonly, cache;
+ u_quad_t frev, frev2;
+ u_long on, off, toff;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED);
+ toff = fxdr_unsigned(u_long, *tl++);
+ off = (toff & ~(NFS_DIRBLKSIZ-1));
+ on = (toff & (NFS_DIRBLKSIZ-1));
+ cnt = fxdr_unsigned(int, *tl++);
+ duration2 = fxdr_unsigned(int, *tl);
+ siz = ((cnt+NFS_DIRBLKSIZ-1) & ~(NFS_DIRBLKSIZ-1));
+ if (cnt > NFS_MAXREADDIR)
+ siz = NFS_MAXREADDIR;
+ fullsiz = siz;
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ nqsrv_getl(vp, NQL_READ);
+ if (error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ VOP_UNLOCK(vp);
+ MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
+again:
+ iv.iov_base = rbuf;
+ iv.iov_len = fullsiz;
+ io.uio_iov = &iv;
+ io.uio_iovcnt = 1;
+ io.uio_offset = (off_t)off;
+ io.uio_resid = fullsiz;
+ io.uio_segflg = UIO_SYSSPACE;
+ io.uio_rw = UIO_READ;
+ io.uio_procp = (struct proc *)0;
+ error = VOP_READDIR(vp, &io, cred);
+ off = (u_long)io.uio_offset;
+ if (error) {
+ vrele(vp);
+ free((caddr_t)rbuf, M_TEMP);
+ nfsm_reply(0);
+ }
+ if (io.uio_resid < fullsiz)
+ eofflag = 0;
+ else
+ eofflag = 1;
+ if (io.uio_resid) {
+ siz -= io.uio_resid;
+
+ /*
+ * If nothing read, return eof
+ * rpc reply
+ */
+ if (siz == 0) {
+ vrele(vp);
+ nfsm_reply(2 * NFSX_UNSIGNED);
+ nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
+ *tl++ = nfs_false;
+ *tl = nfs_true;
+ FREE((caddr_t)rbuf, M_TEMP);
+ return (0);
+ }
+ }
+
+ /*
+ * Check for degenerate cases of nothing useful read.
+ * If so go try again
+ */
+ cpos = rbuf + on;
+ cend = rbuf + siz;
+ dp = (struct dirent *)cpos;
+ while (cpos < cend && dp->d_fileno == 0) {
+ cpos += dp->d_reclen;
+ dp = (struct dirent *)cpos;
+ }
+ if (cpos >= cend) {
+ toff = off;
+ siz = fullsiz;
+ on = 0;
+ goto again;
+ }
+
+ cpos = rbuf + on;
+ cend = rbuf + siz;
+ dp = (struct dirent *)cpos;
+ len = 3 * NFSX_UNSIGNED; /* paranoia, probably can be 0 */
+ nfsm_reply(siz);
+ mp = mp2 = mb;
+ bp = bpos;
+ be = bp + M_TRAILINGSPACE(mp);
+
+ /* Loop through the records and build reply */
+ while (cpos < cend) {
+ if (dp->d_fileno != 0) {
+ nlen = dp->d_namlen;
+ rem = nfsm_rndup(nlen)-nlen;
+
+ /*
+ * For readdir_and_lookup get the vnode using
+ * the file number.
+ */
+ if (VFS_VGET(vp->v_mount, dp->d_fileno, &nvp))
+ goto invalid;
+ bzero((caddr_t)&fl.fl_nfh, sizeof (nfsv2fh_t));
+ fl.fl_nfh.fh_generic.fh_fsid =
+ nvp->v_mount->mnt_stat.f_fsid;
+ if (VFS_VPTOFH(nvp, &fl.fl_nfh.fh_generic.fh_fid)) {
+ vput(nvp);
+ goto invalid;
+ }
+ if (duration2) {
+ (void) nqsrv_getlease(nvp, &duration2, NQL_READ,
+ nfsd, nam, &cache2, &frev2, cred);
+ fl.fl_duration = txdr_unsigned(duration2);
+ fl.fl_cachable = txdr_unsigned(cache2);
+ txdr_hyper(&frev2, fl.fl_frev);
+ } else
+ fl.fl_duration = 0;
+ if (VOP_GETATTR(nvp, vap, cred, nfsd->nd_procp)) {
+ vput(nvp);
+ goto invalid;
+ }
+ vput(nvp);
+ fp = (struct nfsv2_fattr *)&fl.fl_fattr;
+ nfsm_srvfillattr;
+ len += (4*NFSX_UNSIGNED + nlen + rem + NFSX_FH
+ + NFSX_NQFATTR);
+ if (len > cnt) {
+ eofflag = 0;
+ break;
+ }
+ /*
+ * Build the directory record xdr from
+ * the dirent entry.
+ */
+ nfsm_clget;
+ *tl = nfs_true;
+ bp += NFSX_UNSIGNED;
+
+ /*
+ * For readdir_and_lookup copy the stuff out.
+ */
+ xfer = sizeof (struct flrep);
+ cp = (caddr_t)&fl;
+ while (xfer > 0) {
+ nfsm_clget;
+ if ((bp+xfer) > be)
+ tsiz = be-bp;
+ else
+ tsiz = xfer;
+ bcopy(cp, bp, tsiz);
+ bp += tsiz;
+ xfer -= tsiz;
+ if (xfer > 0)
+ cp += tsiz;
+ }
+ nfsm_clget;
+ *tl = txdr_unsigned(dp->d_fileno);
+ bp += NFSX_UNSIGNED;
+ nfsm_clget;
+ *tl = txdr_unsigned(nlen);
+ bp += NFSX_UNSIGNED;
+
+ /* And loop around copying the name */
+ xfer = nlen;
+ cp = dp->d_name;
+ while (xfer > 0) {
+ nfsm_clget;
+ if ((bp+xfer) > be)
+ tsiz = be-bp;
+ else
+ tsiz = xfer;
+ bcopy(cp, bp, tsiz);
+ bp += tsiz;
+ xfer -= tsiz;
+ if (xfer > 0)
+ cp += tsiz;
+ }
+ /* And null pad to a long boundary */
+ for (i = 0; i < rem; i++)
+ *bp++ = '\0';
+ nfsm_clget;
+
+ /* Finish off the record */
+ toff += dp->d_reclen;
+ *tl = txdr_unsigned(toff);
+ bp += NFSX_UNSIGNED;
+ } else
+invalid:
+ toff += dp->d_reclen;
+ cpos += dp->d_reclen;
+ dp = (struct dirent *)cpos;
+ }
+ vrele(vp);
+ nfsm_clget;
+ *tl = nfs_false;
+ bp += NFSX_UNSIGNED;
+ nfsm_clget;
+ if (eofflag)
+ *tl = nfs_true;
+ else
+ *tl = nfs_false;
+ bp += NFSX_UNSIGNED;
+ if (mp != mb) {
+ if (bp < be)
+ mp->m_len = bp - mtod(mp, caddr_t);
+ } else
+ mp->m_len += bp - bpos;
+ FREE(rbuf, M_TEMP);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs statfs service
+ */
+int
+nfsrv_statfs(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct statfs *sf;
+ register struct nfsv2_statfs *sfp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache = 0, isnq;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct statfs statfs;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ isnq = (nfsd->nd_nqlflag != NQL_NOVAL);
+ nfsm_srvmtofh(fhp);
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ sf = &statfs;
+ error = VFS_STATFS(vp->v_mount, sf, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_STATFS(isnq));
+ nfsm_build(sfp, struct nfsv2_statfs *, NFSX_STATFS(isnq));
+ sfp->sf_tsize = txdr_unsigned(NFS_MAXDGRAMDATA);
+ sfp->sf_bsize = txdr_unsigned(sf->f_bsize);
+ sfp->sf_blocks = txdr_unsigned(sf->f_blocks);
+ sfp->sf_bfree = txdr_unsigned(sf->f_bfree);
+ sfp->sf_bavail = txdr_unsigned(sf->f_bavail);
+ if (isnq) {
+ sfp->sf_files = txdr_unsigned(sf->f_files);
+ sfp->sf_ffree = txdr_unsigned(sf->f_ffree);
+ }
+ nfsm_srvdone;
+}
+
+/*
+ * Null operation, used by clients to ping server
+ */
+/* ARGSUSED */
+int
+nfsrv_null(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ caddr_t bpos;
+ int error = VNOVAL, cache = 0;
+ struct mbuf *mb, *mreq;
+ u_quad_t frev;
+
+ nfsm_reply(0);
+ return (error);
+}
+
+/*
+ * No operation, used for obsolete procedures
+ */
+/* ARGSUSED */
+int
+nfsrv_noop(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ caddr_t bpos;
+ int error, cache = 0;
+ struct mbuf *mb, *mreq;
+ u_quad_t frev;
+
+ if (nfsd->nd_repstat)
+ error = nfsd->nd_repstat;
+ else
+ error = EPROCUNAVAIL;
+ nfsm_reply(0);
+ return (error);
+}
+
+/*
+ * Perform access checking for vnodes obtained from file handles that would
+ * refer to files already opened by a Unix client. You cannot just use
+ * vn_writechk() and VOP_ACCESS() for two reasons.
+ * 1 - You must check for exported rdonly as well as MNT_RDONLY for the write case
+ * 2 - The owner is to be given access irrespective of mode bits so that
+ * processes that chmod after opening a file don't break. I don't like
+ * this because it opens a security hole, but since the nfs server opens
+ * a security hole the size of a barn door anyhow, what the heck.
+ */
+int
+nfsrv_access(vp, flags, cred, rdonly, p)
+ register struct vnode *vp;
+ int flags;
+ register struct ucred *cred;
+ int rdonly;
+ struct proc *p;
+{
+ struct vattr vattr;
+ int error;
+ if (flags & VWRITE) {
+ /* Just vn_writechk() changed to check rdonly */
+ /*
+ * Disallow write attempts on read-only file systems;
+ * unless the file is a socket or a block or character
+ * device resident on the file system.
+ */
+ if (rdonly || (vp->v_mount->mnt_flag & MNT_RDONLY)) {
+ switch (vp->v_type) {
+ case VREG: case VDIR: case VLNK:
+ return (EROFS);
+ }
+ }
+ /*
+ * If there's shared text associated with
+ * the inode, try to free it up once. If
+ * we fail, we can't allow writing.
+ */
+ if ((vp->v_flag & VTEXT) && !vnode_pager_uncache(vp))
+ return (ETXTBSY);
+ }
+ if (error = VOP_GETATTR(vp, &vattr, cred, p))
+ return (error);
+ if ((error = VOP_ACCESS(vp, flags, cred, p)) &&
+ cred->cr_uid != vattr.va_uid)
+ return (error);
+ return (0);
+}
diff --git a/sys/nfsserver/nfs_srvcache.c b/sys/nfsserver/nfs_srvcache.c
new file mode 100644
index 0000000..45bfe1b
--- /dev/null
+++ b/sys/nfsserver/nfs_srvcache.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_srvcache.c 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Reference: Chet Juszczak, "Improving the Performance and Correctness
+ * of an NFS Server", in Proc. Winter 1989 USENIX Conference,
+ * pages 53-63. San Diego, February 1989.
+ */
+#include <sys/param.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <netinet/in.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+#include <nfs/nfsm_subs.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsrvcache.h>
+#include <nfs/nqnfs.h>
+
+long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
+
+#define NFSRCHASH(xid) (((xid) + ((xid) >> 24)) & rheadhash)
+static struct nfsrvcache *nfsrvlruhead, **nfsrvlrutail = &nfsrvlruhead;
+static struct nfsrvcache **rheadhtbl;
+static u_long rheadhash;
+
+#define TRUE 1
+#define FALSE 0
+
+#define NETFAMILY(rp) \
+ (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
+
+/*
+ * Static array that defines which nfs rpc's are nonidempotent
+ */
+int nonidempotent[NFS_NPROCS] = {
+ FALSE,
+ FALSE,
+ TRUE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+};
+
+/* True iff the rpc reply is an nfs status ONLY! */
+static int repliesstatus[NFS_NPROCS] = {
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ FALSE,
+ TRUE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ TRUE,
+};
+
+/*
+ * Initialize the server request cache list
+ */
+void
+nfsrv_initcache()
+{
+
+ rheadhtbl = hashinit(desirednfsrvcache, M_NFSD, &rheadhash);
+}
+
+/*
+ * Look for the request in the cache
+ * If found then
+ * return action and optionally reply
+ * else
+ * insert it in the cache
+ *
+ * The rules are as follows:
+ * - if in progress, return DROP request
+ * - if completed within DELAY of the current time, return DROP it
+ * - if completed a longer time ago return REPLY if the reply was cached or
+ * return DOIT
+ * Update/add new request at end of lru list
+ */
+int
+nfsrv_getcache(nam, nd, repp)
+ struct mbuf *nam;
+ register struct nfsd *nd;
+ struct mbuf **repp;
+{
+ register struct nfsrvcache *rp, *rq, **rpp;
+ struct mbuf *mb;
+ struct sockaddr_in *saddr;
+ caddr_t bpos;
+ int ret;
+
+ if (nd->nd_nqlflag != NQL_NOVAL)
+ return (RC_DOIT);
+ rpp = &rheadhtbl[NFSRCHASH(nd->nd_retxid)];
+loop:
+ for (rp = *rpp; rp; rp = rp->rc_forw) {
+ if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
+ netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
+ if ((rp->rc_flag & RC_LOCKED) != 0) {
+ rp->rc_flag |= RC_WANTED;
+ (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
+ goto loop;
+ }
+ rp->rc_flag |= RC_LOCKED;
+ /* If not at end of LRU chain, move it there */
+ if (rp->rc_next) {
+ /* remove from LRU chain */
+ *rp->rc_prev = rp->rc_next;
+ rp->rc_next->rc_prev = rp->rc_prev;
+ /* and replace at end of it */
+ rp->rc_next = NULL;
+ rp->rc_prev = nfsrvlrutail;
+ *nfsrvlrutail = rp;
+ nfsrvlrutail = &rp->rc_next;
+ }
+ if (rp->rc_state == RC_UNUSED)
+ panic("nfsrv cache");
+ if (rp->rc_state == RC_INPROG) {
+ nfsstats.srvcache_inproghits++;
+ ret = RC_DROPIT;
+ } else if (rp->rc_flag & RC_REPSTATUS) {
+ nfsstats.srvcache_nonidemdonehits++;
+ nfs_rephead(0, nd, rp->rc_status,
+ 0, (u_quad_t *)0, repp, &mb, &bpos);
+ ret = RC_REPLY;
+ } else if (rp->rc_flag & RC_REPMBUF) {
+ nfsstats.srvcache_nonidemdonehits++;
+ *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
+ M_WAIT);
+ ret = RC_REPLY;
+ } else {
+ nfsstats.srvcache_idemdonehits++;
+ rp->rc_state = RC_INPROG;
+ ret = RC_DOIT;
+ }
+ rp->rc_flag &= ~RC_LOCKED;
+ if (rp->rc_flag & RC_WANTED) {
+ rp->rc_flag &= ~RC_WANTED;
+ wakeup((caddr_t)rp);
+ }
+ return (ret);
+ }
+ }
+ nfsstats.srvcache_misses++;
+ if (numnfsrvcache < desirednfsrvcache) {
+ rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
+ M_NFSD, M_WAITOK);
+ bzero((char *)rp, sizeof *rp);
+ numnfsrvcache++;
+ rp->rc_flag = RC_LOCKED;
+ } else {
+ rp = nfsrvlruhead;
+ while ((rp->rc_flag & RC_LOCKED) != 0) {
+ rp->rc_flag |= RC_WANTED;
+ (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
+ rp = nfsrvlruhead;
+ }
+ rp->rc_flag |= RC_LOCKED;
+ /* remove from hash chain */
+ if (rq = rp->rc_forw)
+ rq->rc_back = rp->rc_back;
+ *rp->rc_back = rq;
+ /* remove from LRU chain */
+ *rp->rc_prev = rp->rc_next;
+ rp->rc_next->rc_prev = rp->rc_prev;
+ if (rp->rc_flag & RC_REPMBUF)
+ m_freem(rp->rc_reply);
+ if (rp->rc_flag & RC_NAM)
+ MFREE(rp->rc_nam, mb);
+ rp->rc_flag &= (RC_LOCKED | RC_WANTED);
+ }
+ /* place at end of LRU list */
+ rp->rc_next = NULL;
+ rp->rc_prev = nfsrvlrutail;
+ *nfsrvlrutail = rp;
+ nfsrvlrutail = &rp->rc_next;
+ rp->rc_state = RC_INPROG;
+ rp->rc_xid = nd->nd_retxid;
+ saddr = mtod(nam, struct sockaddr_in *);
+ switch (saddr->sin_family) {
+ case AF_INET:
+ rp->rc_flag |= RC_INETADDR;
+ rp->rc_inetaddr = saddr->sin_addr.s_addr;
+ break;
+ case AF_ISO:
+ default:
+ rp->rc_flag |= RC_NAM;
+ rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT);
+ break;
+ };
+ rp->rc_proc = nd->nd_procnum;
+ /* insert into hash chain */
+ if (rq = *rpp)
+ rq->rc_back = &rp->rc_forw;
+ rp->rc_forw = rq;
+ rp->rc_back = rpp;
+ *rpp = rp;
+ rp->rc_flag &= ~RC_LOCKED;
+ if (rp->rc_flag & RC_WANTED) {
+ rp->rc_flag &= ~RC_WANTED;
+ wakeup((caddr_t)rp);
+ }
+ return (RC_DOIT);
+}
+
+/*
+ * Update a request cache entry after the rpc has been done
+ */
+void
+nfsrv_updatecache(nam, nd, repvalid, repmbuf)
+ struct mbuf *nam;
+ register struct nfsd *nd;
+ int repvalid;
+ struct mbuf *repmbuf;
+{
+ register struct nfsrvcache *rp;
+
+ if (nd->nd_nqlflag != NQL_NOVAL)
+ return;
+loop:
+ for (rp = rheadhtbl[NFSRCHASH(nd->nd_retxid)]; rp; rp = rp->rc_forw) {
+ if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
+ netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
+ if ((rp->rc_flag & RC_LOCKED) != 0) {
+ rp->rc_flag |= RC_WANTED;
+ (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
+ goto loop;
+ }
+ rp->rc_flag |= RC_LOCKED;
+ rp->rc_state = RC_DONE;
+ /*
+ * If we have a valid reply update status and save
+ * the reply for non-idempotent rpc's.
+ */
+ if (repvalid && nonidempotent[nd->nd_procnum]) {
+ if (repliesstatus[nd->nd_procnum]) {
+ rp->rc_status = nd->nd_repstat;
+ rp->rc_flag |= RC_REPSTATUS;
+ } else {
+ rp->rc_reply = m_copym(repmbuf,
+ 0, M_COPYALL, M_WAIT);
+ rp->rc_flag |= RC_REPMBUF;
+ }
+ }
+ rp->rc_flag &= ~RC_LOCKED;
+ if (rp->rc_flag & RC_WANTED) {
+ rp->rc_flag &= ~RC_WANTED;
+ wakeup((caddr_t)rp);
+ }
+ return;
+ }
+ }
+}
+
+/*
+ * Clean out the cache. Called when the last nfsd terminates.
+ */
+void
+nfsrv_cleancache()
+{
+ register struct nfsrvcache *rp, *nextrp;
+
+ for (rp = nfsrvlruhead; rp; rp = nextrp) {
+ nextrp = rp->rc_next;
+ free(rp, M_NFSD);
+ }
+ bzero((char *)rheadhtbl, (rheadhash + 1) * sizeof(void *));
+ nfsrvlruhead = NULL;
+ nfsrvlrutail = &nfsrvlruhead;
+ numnfsrvcache = 0;
+}
diff --git a/sys/nfsserver/nfs_srvsock.c b/sys/nfsserver/nfs_srvsock.c
new file mode 100644
index 0000000..c00f7d0
--- /dev/null
+++ b/sys/nfsserver/nfs_srvsock.c
@@ -0,0 +1,2005 @@
+/*
+ * Copyright (c) 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_socket.c 8.3 (Berkeley) 1/12/94
+ */
+
+/*
+ * Socket operations for use by nfs
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/vnode.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/syslog.h>
+#include <sys/tprintf.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfsrtt.h>
+#include <nfs/nqnfs.h>
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Estimate rto for an nfs rpc sent via. an unreliable datagram.
+ * Use the mean and mean deviation of rtt for the appropriate type of rpc
+ * for the frequent rpcs and a default for the others.
+ * The justification for doing "other" this way is that these rpcs
+ * happen so infrequently that timer est. would probably be stale.
+ * Also, since many of these rpcs are
+ * non-idempotent, a conservative timeout is desired.
+ * getattr, lookup - A+2D
+ * read, write - A+4D
+ * other - nm_timeo
+ */
+#define NFS_RTO(n, t) \
+ ((t) == 0 ? (n)->nm_timeo : \
+ ((t) < 3 ? \
+ (((((n)->nm_srtt[t-1] + 3) >> 2) + (n)->nm_sdrtt[t-1] + 1) >> 1) : \
+ ((((n)->nm_srtt[t-1] + 7) >> 3) + (n)->nm_sdrtt[t-1] + 1)))
+#define NFS_SRTT(r) (r)->r_nmp->nm_srtt[proct[(r)->r_procnum] - 1]
+#define NFS_SDRTT(r) (r)->r_nmp->nm_sdrtt[proct[(r)->r_procnum] - 1]
+/*
+ * External data, mostly RPC constants in XDR form
+ */
+extern u_long rpc_reply, rpc_msgdenied, rpc_mismatch, rpc_vers, rpc_auth_unix,
+ rpc_msgaccepted, rpc_call, rpc_autherr, rpc_rejectedcred,
+ rpc_auth_kerb;
+extern u_long nfs_prog, nfs_vers, nqnfs_prog, nqnfs_vers;
+extern time_t nqnfsstarttime;
+extern int nonidempotent[NFS_NPROCS];
+
+/*
+ * Maps errno values to nfs error numbers.
+ * Use NFSERR_IO as the catch all for ones not specifically defined in
+ * RFC 1094.
+ */
+static int nfsrv_errmap[ELAST] = {
+ NFSERR_PERM, NFSERR_NOENT, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_NXIO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_ACCES, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_EXIST, NFSERR_IO, NFSERR_NODEV, NFSERR_NOTDIR,
+ NFSERR_ISDIR, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_FBIG, NFSERR_NOSPC, NFSERR_IO, NFSERR_ROFS,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_NAMETOL, NFSERR_IO, NFSERR_IO,
+ NFSERR_NOTEMPTY, NFSERR_IO, NFSERR_IO, NFSERR_DQUOT, NFSERR_STALE,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO,
+};
+
+/*
+ * Defines which timer to use for the procnum.
+ * 0 - default
+ * 1 - getattr
+ * 2 - lookup
+ * 3 - read
+ * 4 - write
+ */
+static int proct[NFS_NPROCS] = {
+ 0, 1, 0, 0, 2, 3, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0,
+};
+
+/*
+ * There is a congestion window for outstanding rpcs maintained per mount
+ * point. The cwnd size is adjusted in roughly the way that:
+ * Van Jacobson, Congestion avoidance and Control, In "Proceedings of
+ * SIGCOMM '88". ACM, August 1988.
+ * describes for TCP. The cwnd size is chopped in half on a retransmit timeout
+ * and incremented by 1/cwnd when each rpc reply is received and a full cwnd
+ * of rpcs is in progress.
+ * (The sent count and cwnd are scaled for integer arith.)
+ * Variants of "slow start" were tried and were found to be too much of a
+ * performance hit (ave. rtt 3 times larger),
+ * I suspect due to the large rtt that nfs rpcs have.
+ */
+#define NFS_CWNDSCALE 256
+#define NFS_MAXCWND (NFS_CWNDSCALE * 32)
+static int nfs_backoff[8] = { 2, 4, 8, 16, 32, 64, 128, 256, };
+int nfs_sbwait();
+void nfs_disconnect(), nfs_realign(), nfsrv_wakenfsd(), nfs_sndunlock();
+void nfs_rcvunlock(), nqnfs_serverd(), nqnfs_clientlease();
+struct mbuf *nfsm_rpchead();
+int nfsrtton = 0;
+struct nfsrtt nfsrtt;
+struct nfsd nfsd_head;
+
+int nfsrv_null(),
+ nfsrv_getattr(),
+ nfsrv_setattr(),
+ nfsrv_lookup(),
+ nfsrv_readlink(),
+ nfsrv_read(),
+ nfsrv_write(),
+ nfsrv_create(),
+ nfsrv_remove(),
+ nfsrv_rename(),
+ nfsrv_link(),
+ nfsrv_symlink(),
+ nfsrv_mkdir(),
+ nfsrv_rmdir(),
+ nfsrv_readdir(),
+ nfsrv_statfs(),
+ nfsrv_noop(),
+ nqnfsrv_readdirlook(),
+ nqnfsrv_getlease(),
+ nqnfsrv_vacated(),
+ nqnfsrv_access();
+
+int (*nfsrv_procs[NFS_NPROCS])() = {
+ nfsrv_null,
+ nfsrv_getattr,
+ nfsrv_setattr,
+ nfsrv_noop,
+ nfsrv_lookup,
+ nfsrv_readlink,
+ nfsrv_read,
+ nfsrv_noop,
+ nfsrv_write,
+ nfsrv_create,
+ nfsrv_remove,
+ nfsrv_rename,
+ nfsrv_link,
+ nfsrv_symlink,
+ nfsrv_mkdir,
+ nfsrv_rmdir,
+ nfsrv_readdir,
+ nfsrv_statfs,
+ nqnfsrv_readdirlook,
+ nqnfsrv_getlease,
+ nqnfsrv_vacated,
+ nfsrv_noop,
+ nqnfsrv_access,
+};
+
+struct nfsreq nfsreqh;
+
+/*
+ * Initialize sockets and congestion for a new NFS connection.
+ * We do not free the sockaddr if error.
+ */
+int
+nfs_connect(nmp, rep)
+ register struct nfsmount *nmp;
+ struct nfsreq *rep;
+{
+ register struct socket *so;
+ int s, error, rcvreserve, sndreserve;
+ struct sockaddr *saddr;
+ struct sockaddr_in *sin;
+ struct mbuf *m;
+ u_short tport;
+
+ nmp->nm_so = (struct socket *)0;
+ saddr = mtod(nmp->nm_nam, struct sockaddr *);
+ if (error = socreate(saddr->sa_family,
+ &nmp->nm_so, nmp->nm_sotype, nmp->nm_soproto))
+ goto bad;
+ so = nmp->nm_so;
+ nmp->nm_soflags = so->so_proto->pr_flags;
+
+ /*
+ * Some servers require that the client port be a reserved port number.
+ */
+ if (saddr->sa_family == AF_INET && (nmp->nm_flag & NFSMNT_RESVPORT)) {
+ MGET(m, M_WAIT, MT_SONAME);
+ sin = mtod(m, struct sockaddr_in *);
+ sin->sin_len = m->m_len = sizeof (struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = INADDR_ANY;
+ tport = IPPORT_RESERVED - 1;
+ sin->sin_port = htons(tport);
+ while ((error = sobind(so, m)) == EADDRINUSE &&
+ --tport > IPPORT_RESERVED / 2)
+ sin->sin_port = htons(tport);
+ m_freem(m);
+ if (error)
+ goto bad;
+ }
+
+ /*
+ * Protocols that do not require connections may be optionally left
+ * unconnected for servers that reply from a port other than NFS_PORT.
+ */
+ if (nmp->nm_flag & NFSMNT_NOCONN) {
+ if (nmp->nm_soflags & PR_CONNREQUIRED) {
+ error = ENOTCONN;
+ goto bad;
+ }
+ } else {
+ if (error = soconnect(so, nmp->nm_nam))
+ goto bad;
+
+ /*
+ * Wait for the connection to complete. Cribbed from the
+ * connect system call but with the wait timing out so
+ * that interruptible mounts don't hang here for a long time.
+ */
+ s = splnet();
+ while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
+ (void) tsleep((caddr_t)&so->so_timeo, PSOCK,
+ "nfscon", 2 * hz);
+ if ((so->so_state & SS_ISCONNECTING) &&
+ so->so_error == 0 && rep &&
+ (error = nfs_sigintr(nmp, rep, rep->r_procp))) {
+ so->so_state &= ~SS_ISCONNECTING;
+ splx(s);
+ goto bad;
+ }
+ }
+ if (so->so_error) {
+ error = so->so_error;
+ so->so_error = 0;
+ splx(s);
+ goto bad;
+ }
+ splx(s);
+ }
+ if (nmp->nm_flag & (NFSMNT_SOFT | NFSMNT_INT)) {
+ so->so_rcv.sb_timeo = (5 * hz);
+ so->so_snd.sb_timeo = (5 * hz);
+ } else {
+ so->so_rcv.sb_timeo = 0;
+ so->so_snd.sb_timeo = 0;
+ }
+ if (nmp->nm_sotype == SOCK_DGRAM) {
+ sndreserve = nmp->nm_wsize + NFS_MAXPKTHDR;
+ rcvreserve = nmp->nm_rsize + NFS_MAXPKTHDR;
+ } else if (nmp->nm_sotype == SOCK_SEQPACKET) {
+ sndreserve = (nmp->nm_wsize + NFS_MAXPKTHDR) * 2;
+ rcvreserve = (nmp->nm_rsize + NFS_MAXPKTHDR) * 2;
+ } else {
+ if (nmp->nm_sotype != SOCK_STREAM)
+ panic("nfscon sotype");
+ if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
+ MGET(m, M_WAIT, MT_SOOPTS);
+ *mtod(m, int *) = 1;
+ m->m_len = sizeof(int);
+ sosetopt(so, SOL_SOCKET, SO_KEEPALIVE, m);
+ }
+ if (so->so_proto->pr_protocol == IPPROTO_TCP) {
+ MGET(m, M_WAIT, MT_SOOPTS);
+ *mtod(m, int *) = 1;
+ m->m_len = sizeof(int);
+ sosetopt(so, IPPROTO_TCP, TCP_NODELAY, m);
+ }
+ sndreserve = (nmp->nm_wsize + NFS_MAXPKTHDR + sizeof (u_long))
+ * 2;
+ rcvreserve = (nmp->nm_rsize + NFS_MAXPKTHDR + sizeof (u_long))
+ * 2;
+ }
+ if (error = soreserve(so, sndreserve, rcvreserve))
+ goto bad;
+ so->so_rcv.sb_flags |= SB_NOINTR;
+ so->so_snd.sb_flags |= SB_NOINTR;
+
+ /* Initialize other non-zero congestion variables */
+ nmp->nm_srtt[0] = nmp->nm_srtt[1] = nmp->nm_srtt[2] = nmp->nm_srtt[3] =
+ nmp->nm_srtt[4] = (NFS_TIMEO << 3);
+ nmp->nm_sdrtt[0] = nmp->nm_sdrtt[1] = nmp->nm_sdrtt[2] =
+ nmp->nm_sdrtt[3] = nmp->nm_sdrtt[4] = 0;
+ nmp->nm_cwnd = NFS_MAXCWND / 2; /* Initial send window */
+ nmp->nm_sent = 0;
+ nmp->nm_timeouts = 0;
+ return (0);
+
+bad:
+ nfs_disconnect(nmp);
+ return (error);
+}
+
+/*
+ * Reconnect routine:
+ * Called when a connection is broken on a reliable protocol.
+ * - clean up the old socket
+ * - nfs_connect() again
+ * - set R_MUSTRESEND for all outstanding requests on mount point
+ * If this fails the mount point is DEAD!
+ * nb: Must be called with the nfs_sndlock() set on the mount point.
+ */
+int
+nfs_reconnect(rep)
+ register struct nfsreq *rep;
+{
+ register struct nfsreq *rp;
+ register struct nfsmount *nmp = rep->r_nmp;
+ int error;
+
+ nfs_disconnect(nmp);
+ while (error = nfs_connect(nmp, rep)) {
+ if (error == EINTR || error == ERESTART)
+ return (EINTR);
+ (void) tsleep((caddr_t)&lbolt, PSOCK, "nfscon", 0);
+ }
+
+ /*
+ * Loop through outstanding request list and fix up all requests
+ * on old socket.
+ */
+ rp = nfsreqh.r_next;
+ while (rp != &nfsreqh) {
+ if (rp->r_nmp == nmp)
+ rp->r_flags |= R_MUSTRESEND;
+ rp = rp->r_next;
+ }
+ return (0);
+}
+
+/*
+ * NFS disconnect. Clean up and unlink.
+ */
+void
+nfs_disconnect(nmp)
+ register struct nfsmount *nmp;
+{
+ register struct socket *so;
+
+ if (nmp->nm_so) {
+ so = nmp->nm_so;
+ nmp->nm_so = (struct socket *)0;
+ soshutdown(so, 2);
+ soclose(so);
+ }
+}
+
+/*
+ * This is the nfs send routine. For connection based socket types, it
+ * must be called with an nfs_sndlock() on the socket.
+ * "rep == NULL" indicates that it has been called from a server.
+ * For the client side:
+ * - return EINTR if the RPC is terminated, 0 otherwise
+ * - set R_MUSTRESEND if the send fails for any reason
+ * - do any cleanup required by recoverable socket errors (???)
+ * For the server side:
+ * - return EINTR or ERESTART if interrupted by a signal
+ * - return EPIPE if a connection is lost for connection based sockets (TCP...)
+ * - do any cleanup required by recoverable socket errors (???)
+ */
+int
+nfs_send(so, nam, top, rep)
+ register struct socket *so;
+ struct mbuf *nam;
+ register struct mbuf *top;
+ struct nfsreq *rep;
+{
+ struct mbuf *sendnam;
+ int error, soflags, flags;
+
+ if (rep) {
+ if (rep->r_flags & R_SOFTTERM) {
+ m_freem(top);
+ return (EINTR);
+ }
+ if ((so = rep->r_nmp->nm_so) == NULL) {
+ rep->r_flags |= R_MUSTRESEND;
+ m_freem(top);
+ return (0);
+ }
+ rep->r_flags &= ~R_MUSTRESEND;
+ soflags = rep->r_nmp->nm_soflags;
+ } else
+ soflags = so->so_proto->pr_flags;
+ if ((soflags & PR_CONNREQUIRED) || (so->so_state & SS_ISCONNECTED))
+ sendnam = (struct mbuf *)0;
+ else
+ sendnam = nam;
+ if (so->so_type == SOCK_SEQPACKET)
+ flags = MSG_EOR;
+ else
+ flags = 0;
+
+ error = sosend(so, sendnam, (struct uio *)0, top,
+ (struct mbuf *)0, flags);
+ if (error) {
+ if (rep) {
+ log(LOG_INFO, "nfs send error %d for server %s\n",error,
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ /*
+ * Deal with errors for the client side.
+ */
+ if (rep->r_flags & R_SOFTTERM)
+ error = EINTR;
+ else
+ rep->r_flags |= R_MUSTRESEND;
+ } else
+ log(LOG_INFO, "nfsd send error %d\n", error);
+
+ /*
+ * Handle any recoverable (soft) socket errors here. (???)
+ */
+ if (error != EINTR && error != ERESTART &&
+ error != EWOULDBLOCK && error != EPIPE)
+ error = 0;
+ }
+ return (error);
+}
+
+/*
+ * Receive a Sun RPC Request/Reply. For SOCK_DGRAM, the work is all
+ * done by soreceive(), but for SOCK_STREAM we must deal with the Record
+ * Mark and consolidate the data into a new mbuf list.
+ * nb: Sometimes TCP passes the data up to soreceive() in long lists of
+ * small mbufs.
+ * For SOCK_STREAM we must be very careful to read an entire record once
+ * we have read any of it, even if the system call has been interrupted.
+ */
+int
+nfs_receive(rep, aname, mp)
+ register struct nfsreq *rep;
+ struct mbuf **aname;
+ struct mbuf **mp;
+{
+ register struct socket *so;
+ struct uio auio;
+ struct iovec aio;
+ register struct mbuf *m;
+ struct mbuf *control;
+ u_long len;
+ struct mbuf **getnam;
+ int error, sotype, rcvflg;
+ struct proc *p = curproc; /* XXX */
+
+ /*
+ * Set up arguments for soreceive()
+ */
+ *mp = (struct mbuf *)0;
+ *aname = (struct mbuf *)0;
+ sotype = rep->r_nmp->nm_sotype;
+
+ /*
+ * For reliable protocols, lock against other senders/receivers
+ * in case a reconnect is necessary.
+ * For SOCK_STREAM, first get the Record Mark to find out how much
+ * more there is to get.
+ * We must lock the socket against other receivers
+ * until we have an entire rpc request/reply.
+ */
+ if (sotype != SOCK_DGRAM) {
+ if (error = nfs_sndlock(&rep->r_nmp->nm_flag, rep))
+ return (error);
+tryagain:
+ /*
+ * Check for fatal errors and resending request.
+ */
+ /*
+ * Ugh: If a reconnect attempt just happened, nm_so
+ * would have changed. NULL indicates a failed
+ * attempt that has essentially shut down this
+ * mount point.
+ */
+ if (rep->r_mrep || (rep->r_flags & R_SOFTTERM)) {
+ nfs_sndunlock(&rep->r_nmp->nm_flag);
+ return (EINTR);
+ }
+ if ((so = rep->r_nmp->nm_so) == NULL) {
+ if (error = nfs_reconnect(rep)) {
+ nfs_sndunlock(&rep->r_nmp->nm_flag);
+ return (error);
+ }
+ goto tryagain;
+ }
+ while (rep->r_flags & R_MUSTRESEND) {
+ m = m_copym(rep->r_mreq, 0, M_COPYALL, M_WAIT);
+ nfsstats.rpcretries++;
+ if (error = nfs_send(so, rep->r_nmp->nm_nam, m, rep)) {
+ if (error == EINTR || error == ERESTART ||
+ (error = nfs_reconnect(rep))) {
+ nfs_sndunlock(&rep->r_nmp->nm_flag);
+ return (error);
+ }
+ goto tryagain;
+ }
+ }
+ nfs_sndunlock(&rep->r_nmp->nm_flag);
+ if (sotype == SOCK_STREAM) {
+ aio.iov_base = (caddr_t) &len;
+ aio.iov_len = sizeof(u_long);
+ auio.uio_iov = &aio;
+ auio.uio_iovcnt = 1;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_READ;
+ auio.uio_offset = 0;
+ auio.uio_resid = sizeof(u_long);
+ auio.uio_procp = p;
+ do {
+ rcvflg = MSG_WAITALL;
+ error = soreceive(so, (struct mbuf **)0, &auio,
+ (struct mbuf **)0, (struct mbuf **)0, &rcvflg);
+ if (error == EWOULDBLOCK && rep) {
+ if (rep->r_flags & R_SOFTTERM)
+ return (EINTR);
+ }
+ } while (error == EWOULDBLOCK);
+ if (!error && auio.uio_resid > 0) {
+ log(LOG_INFO,
+ "short receive (%d/%d) from nfs server %s\n",
+ sizeof(u_long) - auio.uio_resid,
+ sizeof(u_long),
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ error = EPIPE;
+ }
+ if (error)
+ goto errout;
+ len = ntohl(len) & ~0x80000000;
+ /*
+ * This is SERIOUS! We are out of sync with the sender
+ * and forcing a disconnect/reconnect is all I can do.
+ */
+ if (len > NFS_MAXPACKET) {
+ log(LOG_ERR, "%s (%d) from nfs server %s\n",
+ "impossible packet length",
+ len,
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ error = EFBIG;
+ goto errout;
+ }
+ auio.uio_resid = len;
+ do {
+ rcvflg = MSG_WAITALL;
+ error = soreceive(so, (struct mbuf **)0,
+ &auio, mp, (struct mbuf **)0, &rcvflg);
+ } while (error == EWOULDBLOCK || error == EINTR ||
+ error == ERESTART);
+ if (!error && auio.uio_resid > 0) {
+ log(LOG_INFO,
+ "short receive (%d/%d) from nfs server %s\n",
+ len - auio.uio_resid, len,
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ error = EPIPE;
+ }
+ } else {
+ /*
+ * NB: Since uio_resid is big, MSG_WAITALL is ignored
+ * and soreceive() will return when it has either a
+ * control msg or a data msg.
+ * We have no use for control msg., but must grab them
+ * and then throw them away so we know what is going
+ * on.
+ */
+ auio.uio_resid = len = 100000000; /* Anything Big */
+ auio.uio_procp = p;
+ do {
+ rcvflg = 0;
+ error = soreceive(so, (struct mbuf **)0,
+ &auio, mp, &control, &rcvflg);
+ if (control)
+ m_freem(control);
+ if (error == EWOULDBLOCK && rep) {
+ if (rep->r_flags & R_SOFTTERM)
+ return (EINTR);
+ }
+ } while (error == EWOULDBLOCK ||
+ (!error && *mp == NULL && control));
+ if ((rcvflg & MSG_EOR) == 0)
+ printf("Egad!!\n");
+ if (!error && *mp == NULL)
+ error = EPIPE;
+ len -= auio.uio_resid;
+ }
+errout:
+ if (error && error != EINTR && error != ERESTART) {
+ m_freem(*mp);
+ *mp = (struct mbuf *)0;
+ if (error != EPIPE)
+ log(LOG_INFO,
+ "receive error %d from nfs server %s\n",
+ error,
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ error = nfs_sndlock(&rep->r_nmp->nm_flag, rep);
+ if (!error)
+ error = nfs_reconnect(rep);
+ if (!error)
+ goto tryagain;
+ }
+ } else {
+ if ((so = rep->r_nmp->nm_so) == NULL)
+ return (EACCES);
+ if (so->so_state & SS_ISCONNECTED)
+ getnam = (struct mbuf **)0;
+ else
+ getnam = aname;
+ auio.uio_resid = len = 1000000;
+ auio.uio_procp = p;
+ do {
+ rcvflg = 0;
+ error = soreceive(so, getnam, &auio, mp,
+ (struct mbuf **)0, &rcvflg);
+ if (error == EWOULDBLOCK &&
+ (rep->r_flags & R_SOFTTERM))
+ return (EINTR);
+ } while (error == EWOULDBLOCK);
+ len -= auio.uio_resid;
+ }
+ if (error) {
+ m_freem(*mp);
+ *mp = (struct mbuf *)0;
+ }
+ /*
+ * Search for any mbufs that are not a multiple of 4 bytes long
+ * or with m_data not longword aligned.
+ * These could cause pointer alignment problems, so copy them to
+ * well aligned mbufs.
+ */
+ nfs_realign(*mp, 5 * NFSX_UNSIGNED);
+ return (error);
+}
+
+/*
+ * Implement receipt of reply on a socket.
+ * We must search through the list of received datagrams matching them
+ * with outstanding requests using the xid, until ours is found.
+ */
+/* ARGSUSED */
+int
+nfs_reply(myrep)
+ struct nfsreq *myrep;
+{
+ register struct nfsreq *rep;
+ register struct nfsmount *nmp = myrep->r_nmp;
+ register long t1;
+ struct mbuf *mrep, *nam, *md;
+ u_long rxid, *tl;
+ caddr_t dpos, cp2;
+ int error;
+
+ /*
+ * Loop around until we get our own reply
+ */
+ for (;;) {
+ /*
+ * Lock against other receivers so that I don't get stuck in
+ * sbwait() after someone else has received my reply for me.
+ * Also necessary for connection based protocols to avoid
+ * race conditions during a reconnect.
+ */
+ if (error = nfs_rcvlock(myrep))
+ return (error);
+ /* Already received, bye bye */
+ if (myrep->r_mrep != NULL) {
+ nfs_rcvunlock(&nmp->nm_flag);
+ return (0);
+ }
+ /*
+ * Get the next Rpc reply off the socket
+ */
+ error = nfs_receive(myrep, &nam, &mrep);
+ nfs_rcvunlock(&nmp->nm_flag);
+ if (error) {
+
+ /*
+ * Ignore routing errors on connectionless protocols??
+ */
+ if (NFSIGNORE_SOERROR(nmp->nm_soflags, error)) {
+ nmp->nm_so->so_error = 0;
+ if (myrep->r_flags & R_GETONEREP)
+ return (0);
+ continue;
+ }
+ return (error);
+ }
+ if (nam)
+ m_freem(nam);
+
+ /*
+ * Get the xid and check that it is an rpc reply
+ */
+ md = mrep;
+ dpos = mtod(md, caddr_t);
+ nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
+ rxid = *tl++;
+ if (*tl != rpc_reply) {
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ if (nqnfs_callback(nmp, mrep, md, dpos))
+ nfsstats.rpcinvalid++;
+ } else {
+ nfsstats.rpcinvalid++;
+ m_freem(mrep);
+ }
+nfsmout:
+ if (myrep->r_flags & R_GETONEREP)
+ return (0);
+ continue;
+ }
+
+ /*
+ * Loop through the request list to match up the reply
+ * Iff no match, just drop the datagram
+ */
+ rep = nfsreqh.r_next;
+ while (rep != &nfsreqh) {
+ if (rep->r_mrep == NULL && rxid == rep->r_xid) {
+ /* Found it.. */
+ rep->r_mrep = mrep;
+ rep->r_md = md;
+ rep->r_dpos = dpos;
+ if (nfsrtton) {
+ struct rttl *rt;
+
+ rt = &nfsrtt.rttl[nfsrtt.pos];
+ rt->proc = rep->r_procnum;
+ rt->rto = NFS_RTO(nmp, proct[rep->r_procnum]);
+ rt->sent = nmp->nm_sent;
+ rt->cwnd = nmp->nm_cwnd;
+ rt->srtt = nmp->nm_srtt[proct[rep->r_procnum] - 1];
+ rt->sdrtt = nmp->nm_sdrtt[proct[rep->r_procnum] - 1];
+ rt->fsid = nmp->nm_mountp->mnt_stat.f_fsid;
+ rt->tstamp = time;
+ if (rep->r_flags & R_TIMING)
+ rt->rtt = rep->r_rtt;
+ else
+ rt->rtt = 1000000;
+ nfsrtt.pos = (nfsrtt.pos + 1) % NFSRTTLOGSIZ;
+ }
+ /*
+ * Update congestion window.
+ * Do the additive increase of
+ * one rpc/rtt.
+ */
+ if (nmp->nm_cwnd <= nmp->nm_sent) {
+ nmp->nm_cwnd +=
+ (NFS_CWNDSCALE * NFS_CWNDSCALE +
+ (nmp->nm_cwnd >> 1)) / nmp->nm_cwnd;
+ if (nmp->nm_cwnd > NFS_MAXCWND)
+ nmp->nm_cwnd = NFS_MAXCWND;
+ }
+ rep->r_flags &= ~R_SENT;
+ nmp->nm_sent -= NFS_CWNDSCALE;
+ /*
+ * Update rtt using a gain of 0.125 on the mean
+ * and a gain of 0.25 on the deviation.
+ */
+ if (rep->r_flags & R_TIMING) {
+ /*
+ * Since the timer resolution of
+ * NFS_HZ is so course, it can often
+ * result in r_rtt == 0. Since
+ * r_rtt == N means that the actual
+ * rtt is between N+dt and N+2-dt ticks,
+ * add 1.
+ */
+ t1 = rep->r_rtt + 1;
+ t1 -= (NFS_SRTT(rep) >> 3);
+ NFS_SRTT(rep) += t1;
+ if (t1 < 0)
+ t1 = -t1;
+ t1 -= (NFS_SDRTT(rep) >> 2);
+ NFS_SDRTT(rep) += t1;
+ }
+ nmp->nm_timeouts = 0;
+ break;
+ }
+ rep = rep->r_next;
+ }
+ /*
+ * If not matched to a request, drop it.
+ * If it's mine, get out.
+ */
+ if (rep == &nfsreqh) {
+ nfsstats.rpcunexpected++;
+ m_freem(mrep);
+ } else if (rep == myrep) {
+ if (rep->r_mrep == NULL)
+ panic("nfsreply nil");
+ return (0);
+ }
+ if (myrep->r_flags & R_GETONEREP)
+ return (0);
+ }
+}
+
+/*
+ * nfs_request - goes something like this
+ * - fill in request struct
+ * - links it into list
+ * - calls nfs_send() for first transmit
+ * - calls nfs_receive() to get reply
+ * - break down rpc header and return with nfs reply pointed to
+ * by mrep or error
+ * nb: always frees up mreq mbuf list
+ */
+int
+nfs_request(vp, mrest, procnum, procp, cred, mrp, mdp, dposp)
+ struct vnode *vp;
+ struct mbuf *mrest;
+ int procnum;
+ struct proc *procp;
+ struct ucred *cred;
+ struct mbuf **mrp;
+ struct mbuf **mdp;
+ caddr_t *dposp;
+{
+ register struct mbuf *m, *mrep;
+ register struct nfsreq *rep;
+ register u_long *tl;
+ register int i;
+ struct nfsmount *nmp;
+ struct mbuf *md, *mheadend;
+ struct nfsreq *reph;
+ struct nfsnode *np;
+ time_t reqtime, waituntil;
+ caddr_t dpos, cp2;
+ int t1, nqlflag, cachable, s, error = 0, mrest_len, auth_len, auth_type;
+ int trylater_delay = NQ_TRYLATERDEL, trylater_cnt = 0, failed_auth = 0;
+ u_long xid;
+ u_quad_t frev;
+ char *auth_str;
+
+ nmp = VFSTONFS(vp->v_mount);
+ MALLOC(rep, struct nfsreq *, sizeof(struct nfsreq), M_NFSREQ, M_WAITOK);
+ rep->r_nmp = nmp;
+ rep->r_vp = vp;
+ rep->r_procp = procp;
+ rep->r_procnum = procnum;
+ i = 0;
+ m = mrest;
+ while (m) {
+ i += m->m_len;
+ m = m->m_next;
+ }
+ mrest_len = i;
+
+ /*
+ * Get the RPC header with authorization.
+ */
+kerbauth:
+ auth_str = (char *)0;
+ if (nmp->nm_flag & NFSMNT_KERB) {
+ if (failed_auth) {
+ error = nfs_getauth(nmp, rep, cred, &auth_type,
+ &auth_str, &auth_len);
+ if (error) {
+ free((caddr_t)rep, M_NFSREQ);
+ m_freem(mrest);
+ return (error);
+ }
+ } else {
+ auth_type = RPCAUTH_UNIX;
+ auth_len = 5 * NFSX_UNSIGNED;
+ }
+ } else {
+ auth_type = RPCAUTH_UNIX;
+ if (cred->cr_ngroups < 1)
+ panic("nfsreq nogrps");
+ auth_len = ((((cred->cr_ngroups - 1) > nmp->nm_numgrps) ?
+ nmp->nm_numgrps : (cred->cr_ngroups - 1)) << 2) +
+ 5 * NFSX_UNSIGNED;
+ }
+ m = nfsm_rpchead(cred, (nmp->nm_flag & NFSMNT_NQNFS), procnum,
+ auth_type, auth_len, auth_str, mrest, mrest_len, &mheadend, &xid);
+ if (auth_str)
+ free(auth_str, M_TEMP);
+
+ /*
+ * For stream protocols, insert a Sun RPC Record Mark.
+ */
+ if (nmp->nm_sotype == SOCK_STREAM) {
+ M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
+ *mtod(m, u_long *) = htonl(0x80000000 |
+ (m->m_pkthdr.len - NFSX_UNSIGNED));
+ }
+ rep->r_mreq = m;
+ rep->r_xid = xid;
+tryagain:
+ if (nmp->nm_flag & NFSMNT_SOFT)
+ rep->r_retry = nmp->nm_retry;
+ else
+ rep->r_retry = NFS_MAXREXMIT + 1; /* past clip limit */
+ rep->r_rtt = rep->r_rexmit = 0;
+ if (proct[procnum] > 0)
+ rep->r_flags = R_TIMING;
+ else
+ rep->r_flags = 0;
+ rep->r_mrep = NULL;
+
+ /*
+ * Do the client side RPC.
+ */
+ nfsstats.rpcrequests++;
+ /*
+ * Chain request into list of outstanding requests. Be sure
+ * to put it LAST so timer finds oldest requests first.
+ */
+ s = splsoftclock();
+ reph = &nfsreqh;
+ reph->r_prev->r_next = rep;
+ rep->r_prev = reph->r_prev;
+ reph->r_prev = rep;
+ rep->r_next = reph;
+
+ /* Get send time for nqnfs */
+ reqtime = time.tv_sec;
+
+ /*
+ * If backing off another request or avoiding congestion, don't
+ * send this one now but let timer do it. If not timing a request,
+ * do it now.
+ */
+ if (nmp->nm_so && (nmp->nm_sotype != SOCK_DGRAM ||
+ (nmp->nm_flag & NFSMNT_DUMBTIMR) ||
+ nmp->nm_sent < nmp->nm_cwnd)) {
+ splx(s);
+ if (nmp->nm_soflags & PR_CONNREQUIRED)
+ error = nfs_sndlock(&nmp->nm_flag, rep);
+ if (!error) {
+ m = m_copym(m, 0, M_COPYALL, M_WAIT);
+ error = nfs_send(nmp->nm_so, nmp->nm_nam, m, rep);
+ if (nmp->nm_soflags & PR_CONNREQUIRED)
+ nfs_sndunlock(&nmp->nm_flag);
+ }
+ if (!error && (rep->r_flags & R_MUSTRESEND) == 0) {
+ nmp->nm_sent += NFS_CWNDSCALE;
+ rep->r_flags |= R_SENT;
+ }
+ } else {
+ splx(s);
+ rep->r_rtt = -1;
+ }
+
+ /*
+ * Wait for the reply from our send or the timer's.
+ */
+ if (!error || error == EPIPE)
+ error = nfs_reply(rep);
+
+ /*
+ * RPC done, unlink the request.
+ */
+ s = splsoftclock();
+ rep->r_prev->r_next = rep->r_next;
+ rep->r_next->r_prev = rep->r_prev;
+ splx(s);
+
+ /*
+ * Decrement the outstanding request count.
+ */
+ if (rep->r_flags & R_SENT) {
+ rep->r_flags &= ~R_SENT; /* paranoia */
+ nmp->nm_sent -= NFS_CWNDSCALE;
+ }
+
+ /*
+ * If there was a successful reply and a tprintf msg.
+ * tprintf a response.
+ */
+ if (!error && (rep->r_flags & R_TPRINTFMSG))
+ nfs_msg(rep->r_procp, nmp->nm_mountp->mnt_stat.f_mntfromname,
+ "is alive again");
+ mrep = rep->r_mrep;
+ md = rep->r_md;
+ dpos = rep->r_dpos;
+ if (error) {
+ m_freem(rep->r_mreq);
+ free((caddr_t)rep, M_NFSREQ);
+ return (error);
+ }
+
+ /*
+ * break down the rpc header and check if ok
+ */
+ nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED);
+ if (*tl++ == rpc_msgdenied) {
+ if (*tl == rpc_mismatch)
+ error = EOPNOTSUPP;
+ else if ((nmp->nm_flag & NFSMNT_KERB) && *tl++ == rpc_autherr) {
+ if (*tl == rpc_rejectedcred && failed_auth == 0) {
+ failed_auth++;
+ mheadend->m_next = (struct mbuf *)0;
+ m_freem(mrep);
+ m_freem(rep->r_mreq);
+ goto kerbauth;
+ } else
+ error = EAUTH;
+ } else
+ error = EACCES;
+ m_freem(mrep);
+ m_freem(rep->r_mreq);
+ free((caddr_t)rep, M_NFSREQ);
+ return (error);
+ }
+
+ /*
+ * skip over the auth_verf, someday we may want to cache auth_short's
+ * for nfs_reqhead(), but for now just dump it
+ */
+ if (*++tl != 0) {
+ i = nfsm_rndup(fxdr_unsigned(long, *tl));
+ nfsm_adv(i);
+ }
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ /* 0 == ok */
+ if (*tl == 0) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ if (*tl != 0) {
+ error = fxdr_unsigned(int, *tl);
+ m_freem(mrep);
+ if ((nmp->nm_flag & NFSMNT_NQNFS) &&
+ error == NQNFS_TRYLATER) {
+ error = 0;
+ waituntil = time.tv_sec + trylater_delay;
+ while (time.tv_sec < waituntil)
+ (void) tsleep((caddr_t)&lbolt,
+ PSOCK, "nqnfstry", 0);
+ trylater_delay *= nfs_backoff[trylater_cnt];
+ if (trylater_cnt < 7)
+ trylater_cnt++;
+ goto tryagain;
+ }
+
+ /*
+ * If the File Handle was stale, invalidate the
+ * lookup cache, just in case.
+ */
+ if (error == ESTALE)
+ cache_purge(vp);
+ m_freem(rep->r_mreq);
+ free((caddr_t)rep, M_NFSREQ);
+ return (error);
+ }
+
+ /*
+ * For nqnfs, get any lease in reply
+ */
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ if (*tl) {
+ np = VTONFS(vp);
+ nqlflag = fxdr_unsigned(int, *tl);
+ nfsm_dissect(tl, u_long *, 4*NFSX_UNSIGNED);
+ cachable = fxdr_unsigned(int, *tl++);
+ reqtime += fxdr_unsigned(int, *tl++);
+ if (reqtime > time.tv_sec) {
+ fxdr_hyper(tl, &frev);
+ nqnfs_clientlease(nmp, np, nqlflag,
+ cachable, reqtime, frev);
+ }
+ }
+ }
+ *mrp = mrep;
+ *mdp = md;
+ *dposp = dpos;
+ m_freem(rep->r_mreq);
+ FREE((caddr_t)rep, M_NFSREQ);
+ return (0);
+ }
+ m_freem(mrep);
+ m_freem(rep->r_mreq);
+ free((caddr_t)rep, M_NFSREQ);
+ error = EPROTONOSUPPORT;
+nfsmout:
+ return (error);
+}
+
+/*
+ * Generate the rpc reply header
+ * siz arg. is used to decide if adding a cluster is worthwhile
+ */
+int
+nfs_rephead(siz, nd, err, cache, frev, mrq, mbp, bposp)
+ int siz;
+ struct nfsd *nd;
+ int err;
+ int cache;
+ u_quad_t *frev;
+ struct mbuf **mrq;
+ struct mbuf **mbp;
+ caddr_t *bposp;
+{
+ register u_long *tl;
+ register struct mbuf *mreq;
+ caddr_t bpos;
+ struct mbuf *mb, *mb2;
+
+ MGETHDR(mreq, M_WAIT, MT_DATA);
+ mb = mreq;
+ /*
+ * If this is a big reply, use a cluster else
+ * try and leave leading space for the lower level headers.
+ */
+ siz += RPC_REPLYSIZ;
+ if (siz >= MINCLSIZE) {
+ MCLGET(mreq, M_WAIT);
+ } else
+ mreq->m_data += max_hdr;
+ tl = mtod(mreq, u_long *);
+ mreq->m_len = 6*NFSX_UNSIGNED;
+ bpos = ((caddr_t)tl)+mreq->m_len;
+ *tl++ = nd->nd_retxid;
+ *tl++ = rpc_reply;
+ if (err == ERPCMISMATCH || err == NQNFS_AUTHERR) {
+ *tl++ = rpc_msgdenied;
+ if (err == NQNFS_AUTHERR) {
+ *tl++ = rpc_autherr;
+ *tl = rpc_rejectedcred;
+ mreq->m_len -= NFSX_UNSIGNED;
+ bpos -= NFSX_UNSIGNED;
+ } else {
+ *tl++ = rpc_mismatch;
+ *tl++ = txdr_unsigned(2);
+ *tl = txdr_unsigned(2);
+ }
+ } else {
+ *tl++ = rpc_msgaccepted;
+ *tl++ = 0;
+ *tl++ = 0;
+ switch (err) {
+ case EPROGUNAVAIL:
+ *tl = txdr_unsigned(RPC_PROGUNAVAIL);
+ break;
+ case EPROGMISMATCH:
+ *tl = txdr_unsigned(RPC_PROGMISMATCH);
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(2);
+ *tl = txdr_unsigned(2); /* someday 3 */
+ break;
+ case EPROCUNAVAIL:
+ *tl = txdr_unsigned(RPC_PROCUNAVAIL);
+ break;
+ default:
+ *tl = 0;
+ if (err != VNOVAL) {
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ if (err)
+ *tl = txdr_unsigned(nfsrv_errmap[err - 1]);
+ else
+ *tl = 0;
+ }
+ break;
+ };
+ }
+
+ /*
+ * For nqnfs, piggyback lease as requested.
+ */
+ if (nd->nd_nqlflag != NQL_NOVAL && err == 0) {
+ if (nd->nd_nqlflag) {
+ nfsm_build(tl, u_long *, 5*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(nd->nd_nqlflag);
+ *tl++ = txdr_unsigned(cache);
+ *tl++ = txdr_unsigned(nd->nd_duration);
+ txdr_hyper(frev, tl);
+ } else {
+ if (nd->nd_nqlflag != 0)
+ panic("nqreph");
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = 0;
+ }
+ }
+ *mrq = mreq;
+ *mbp = mb;
+ *bposp = bpos;
+ if (err != 0 && err != VNOVAL)
+ nfsstats.srvrpc_errs++;
+ return (0);
+}
+
+/*
+ * Nfs timer routine
+ * Scan the nfsreq list and retranmit any requests that have timed out
+ * To avoid retransmission attempts on STREAM sockets (in the future) make
+ * sure to set the r_retry field to 0 (implies nm_retry == 0).
+ */
+void
+nfs_timer(arg)
+ void *arg;
+{
+ register struct nfsreq *rep;
+ register struct mbuf *m;
+ register struct socket *so;
+ register struct nfsmount *nmp;
+ register int timeo;
+ static long lasttime = 0;
+ int s, error;
+
+ s = splnet();
+ for (rep = nfsreqh.r_next; rep != &nfsreqh; rep = rep->r_next) {
+ nmp = rep->r_nmp;
+ if (rep->r_mrep || (rep->r_flags & R_SOFTTERM))
+ continue;
+ if (nfs_sigintr(nmp, rep, rep->r_procp)) {
+ rep->r_flags |= R_SOFTTERM;
+ continue;
+ }
+ if (rep->r_rtt >= 0) {
+ rep->r_rtt++;
+ if (nmp->nm_flag & NFSMNT_DUMBTIMR)
+ timeo = nmp->nm_timeo;
+ else
+ timeo = NFS_RTO(nmp, proct[rep->r_procnum]);
+ if (nmp->nm_timeouts > 0)
+ timeo *= nfs_backoff[nmp->nm_timeouts - 1];
+ if (rep->r_rtt <= timeo)
+ continue;
+ if (nmp->nm_timeouts < 8)
+ nmp->nm_timeouts++;
+ }
+ /*
+ * Check for server not responding
+ */
+ if ((rep->r_flags & R_TPRINTFMSG) == 0 &&
+ rep->r_rexmit > nmp->nm_deadthresh) {
+ nfs_msg(rep->r_procp,
+ nmp->nm_mountp->mnt_stat.f_mntfromname,
+ "not responding");
+ rep->r_flags |= R_TPRINTFMSG;
+ }
+ if (rep->r_rexmit >= rep->r_retry) { /* too many */
+ nfsstats.rpctimeouts++;
+ rep->r_flags |= R_SOFTTERM;
+ continue;
+ }
+ if (nmp->nm_sotype != SOCK_DGRAM) {
+ if (++rep->r_rexmit > NFS_MAXREXMIT)
+ rep->r_rexmit = NFS_MAXREXMIT;
+ continue;
+ }
+ if ((so = nmp->nm_so) == NULL)
+ continue;
+
+ /*
+ * If there is enough space and the window allows..
+ * Resend it
+ * Set r_rtt to -1 in case we fail to send it now.
+ */
+ rep->r_rtt = -1;
+ if (sbspace(&so->so_snd) >= rep->r_mreq->m_pkthdr.len &&
+ ((nmp->nm_flag & NFSMNT_DUMBTIMR) ||
+ (rep->r_flags & R_SENT) ||
+ nmp->nm_sent < nmp->nm_cwnd) &&
+ (m = m_copym(rep->r_mreq, 0, M_COPYALL, M_DONTWAIT))){
+ if ((nmp->nm_flag & NFSMNT_NOCONN) == 0)
+ error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, m,
+ (struct mbuf *)0, (struct mbuf *)0);
+ else
+ error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, m,
+ nmp->nm_nam, (struct mbuf *)0);
+ if (error) {
+ if (NFSIGNORE_SOERROR(nmp->nm_soflags, error))
+ so->so_error = 0;
+ } else {
+ /*
+ * Iff first send, start timing
+ * else turn timing off, backoff timer
+ * and divide congestion window by 2.
+ */
+ if (rep->r_flags & R_SENT) {
+ rep->r_flags &= ~R_TIMING;
+ if (++rep->r_rexmit > NFS_MAXREXMIT)
+ rep->r_rexmit = NFS_MAXREXMIT;
+ nmp->nm_cwnd >>= 1;
+ if (nmp->nm_cwnd < NFS_CWNDSCALE)
+ nmp->nm_cwnd = NFS_CWNDSCALE;
+ nfsstats.rpcretries++;
+ } else {
+ rep->r_flags |= R_SENT;
+ nmp->nm_sent += NFS_CWNDSCALE;
+ }
+ rep->r_rtt = 0;
+ }
+ }
+ }
+
+ /*
+ * Call the nqnfs server timer once a second to handle leases.
+ */
+ if (lasttime != time.tv_sec) {
+ lasttime = time.tv_sec;
+ nqnfs_serverd();
+ }
+ splx(s);
+ timeout(nfs_timer, (void *)0, hz / NFS_HZ);
+}
+
+/*
+ * Test for a termination condition pending on the process.
+ * This is used for NFSMNT_INT mounts.
+ */
+int
+nfs_sigintr(nmp, rep, p)
+ struct nfsmount *nmp;
+ struct nfsreq *rep;
+ register struct proc *p;
+{
+
+ if (rep && (rep->r_flags & R_SOFTTERM))
+ return (EINTR);
+ if (!(nmp->nm_flag & NFSMNT_INT))
+ return (0);
+ if (p && p->p_siglist &&
+ (((p->p_siglist & ~p->p_sigmask) & ~p->p_sigignore) &
+ NFSINT_SIGMASK))
+ return (EINTR);
+ return (0);
+}
+
+/*
+ * Lock a socket against others.
+ * Necessary for STREAM sockets to ensure you get an entire rpc request/reply
+ * and also to avoid race conditions between the processes with nfs requests
+ * in progress when a reconnect is necessary.
+ */
+int
+nfs_sndlock(flagp, rep)
+ register int *flagp;
+ struct nfsreq *rep;
+{
+ struct proc *p;
+ int slpflag = 0, slptimeo = 0;
+
+ if (rep) {
+ p = rep->r_procp;
+ if (rep->r_nmp->nm_flag & NFSMNT_INT)
+ slpflag = PCATCH;
+ } else
+ p = (struct proc *)0;
+ while (*flagp & NFSMNT_SNDLOCK) {
+ if (nfs_sigintr(rep->r_nmp, rep, p))
+ return (EINTR);
+ *flagp |= NFSMNT_WANTSND;
+ (void) tsleep((caddr_t)flagp, slpflag | (PZERO - 1), "nfsndlck",
+ slptimeo);
+ if (slpflag == PCATCH) {
+ slpflag = 0;
+ slptimeo = 2 * hz;
+ }
+ }
+ *flagp |= NFSMNT_SNDLOCK;
+ return (0);
+}
+
+/*
+ * Unlock the stream socket for others.
+ */
+void
+nfs_sndunlock(flagp)
+ register int *flagp;
+{
+
+ if ((*flagp & NFSMNT_SNDLOCK) == 0)
+ panic("nfs sndunlock");
+ *flagp &= ~NFSMNT_SNDLOCK;
+ if (*flagp & NFSMNT_WANTSND) {
+ *flagp &= ~NFSMNT_WANTSND;
+ wakeup((caddr_t)flagp);
+ }
+}
+
+int
+nfs_rcvlock(rep)
+ register struct nfsreq *rep;
+{
+ register int *flagp = &rep->r_nmp->nm_flag;
+ int slpflag, slptimeo = 0;
+
+ if (*flagp & NFSMNT_INT)
+ slpflag = PCATCH;
+ else
+ slpflag = 0;
+ while (*flagp & NFSMNT_RCVLOCK) {
+ if (nfs_sigintr(rep->r_nmp, rep, rep->r_procp))
+ return (EINTR);
+ *flagp |= NFSMNT_WANTRCV;
+ (void) tsleep((caddr_t)flagp, slpflag | (PZERO - 1), "nfsrcvlk",
+ slptimeo);
+ if (slpflag == PCATCH) {
+ slpflag = 0;
+ slptimeo = 2 * hz;
+ }
+ }
+ *flagp |= NFSMNT_RCVLOCK;
+ return (0);
+}
+
+/*
+ * Unlock the stream socket for others.
+ */
+void
+nfs_rcvunlock(flagp)
+ register int *flagp;
+{
+
+ if ((*flagp & NFSMNT_RCVLOCK) == 0)
+ panic("nfs rcvunlock");
+ *flagp &= ~NFSMNT_RCVLOCK;
+ if (*flagp & NFSMNT_WANTRCV) {
+ *flagp &= ~NFSMNT_WANTRCV;
+ wakeup((caddr_t)flagp);
+ }
+}
+
+/*
+ * Check for badly aligned mbuf data areas and
+ * realign data in an mbuf list by copying the data areas up, as required.
+ */
+void
+nfs_realign(m, hsiz)
+ register struct mbuf *m;
+ int hsiz;
+{
+ register struct mbuf *m2;
+ register int siz, mlen, olen;
+ register caddr_t tcp, fcp;
+ struct mbuf *mnew;
+
+ while (m) {
+ /*
+ * This never happens for UDP, rarely happens for TCP
+ * but frequently happens for iso transport.
+ */
+ if ((m->m_len & 0x3) || (mtod(m, int) & 0x3)) {
+ olen = m->m_len;
+ fcp = mtod(m, caddr_t);
+ if ((int)fcp & 0x3) {
+ m->m_flags &= ~M_PKTHDR;
+ if (m->m_flags & M_EXT)
+ m->m_data = m->m_ext.ext_buf +
+ ((m->m_ext.ext_size - olen) & ~0x3);
+ else
+ m->m_data = m->m_dat;
+ }
+ m->m_len = 0;
+ tcp = mtod(m, caddr_t);
+ mnew = m;
+ m2 = m->m_next;
+
+ /*
+ * If possible, only put the first invariant part
+ * of the RPC header in the first mbuf.
+ */
+ mlen = M_TRAILINGSPACE(m);
+ if (olen <= hsiz && mlen > hsiz)
+ mlen = hsiz;
+
+ /*
+ * Loop through the mbuf list consolidating data.
+ */
+ while (m) {
+ while (olen > 0) {
+ if (mlen == 0) {
+ m2->m_flags &= ~M_PKTHDR;
+ if (m2->m_flags & M_EXT)
+ m2->m_data = m2->m_ext.ext_buf;
+ else
+ m2->m_data = m2->m_dat;
+ m2->m_len = 0;
+ mlen = M_TRAILINGSPACE(m2);
+ tcp = mtod(m2, caddr_t);
+ mnew = m2;
+ m2 = m2->m_next;
+ }
+ siz = min(mlen, olen);
+ if (tcp != fcp)
+ bcopy(fcp, tcp, siz);
+ mnew->m_len += siz;
+ mlen -= siz;
+ olen -= siz;
+ tcp += siz;
+ fcp += siz;
+ }
+ m = m->m_next;
+ if (m) {
+ olen = m->m_len;
+ fcp = mtod(m, caddr_t);
+ }
+ }
+
+ /*
+ * Finally, set m_len == 0 for any trailing mbufs that have
+ * been copied out of.
+ */
+ while (m2) {
+ m2->m_len = 0;
+ m2 = m2->m_next;
+ }
+ return;
+ }
+ m = m->m_next;
+ }
+}
+
+/*
+ * Socket upcall routine for the nfsd sockets.
+ * The caddr_t arg is a pointer to the "struct nfssvc_sock".
+ * Essentially do as much as possible non-blocking, else punt and it will
+ * be called with M_WAIT from an nfsd.
+ */
+void
+nfsrv_rcv(so, arg, waitflag)
+ struct socket *so;
+ caddr_t arg;
+ int waitflag;
+{
+ register struct nfssvc_sock *slp = (struct nfssvc_sock *)arg;
+ register struct mbuf *m;
+ struct mbuf *mp, *nam;
+ struct uio auio;
+ int flags, error;
+
+ if ((slp->ns_flag & SLP_VALID) == 0)
+ return;
+#ifdef notdef
+ /*
+ * Define this to test for nfsds handling this under heavy load.
+ */
+ if (waitflag == M_DONTWAIT) {
+ slp->ns_flag |= SLP_NEEDQ; goto dorecs;
+ }
+#endif
+ auio.uio_procp = NULL;
+ if (so->so_type == SOCK_STREAM) {
+ /*
+ * If there are already records on the queue, defer soreceive()
+ * to an nfsd so that there is feedback to the TCP layer that
+ * the nfs servers are heavily loaded.
+ */
+ if (slp->ns_rec && waitflag == M_DONTWAIT) {
+ slp->ns_flag |= SLP_NEEDQ;
+ goto dorecs;
+ }
+
+ /*
+ * Do soreceive().
+ */
+ auio.uio_resid = 1000000000;
+ flags = MSG_DONTWAIT;
+ error = soreceive(so, &nam, &auio, &mp, (struct mbuf **)0, &flags);
+ if (error || mp == (struct mbuf *)0) {
+ if (error == EWOULDBLOCK)
+ slp->ns_flag |= SLP_NEEDQ;
+ else
+ slp->ns_flag |= SLP_DISCONN;
+ goto dorecs;
+ }
+ m = mp;
+ if (slp->ns_rawend) {
+ slp->ns_rawend->m_next = m;
+ slp->ns_cc += 1000000000 - auio.uio_resid;
+ } else {
+ slp->ns_raw = m;
+ slp->ns_cc = 1000000000 - auio.uio_resid;
+ }
+ while (m->m_next)
+ m = m->m_next;
+ slp->ns_rawend = m;
+
+ /*
+ * Now try and parse record(s) out of the raw stream data.
+ */
+ if (error = nfsrv_getstream(slp, waitflag)) {
+ if (error == EPERM)
+ slp->ns_flag |= SLP_DISCONN;
+ else
+ slp->ns_flag |= SLP_NEEDQ;
+ }
+ } else {
+ do {
+ auio.uio_resid = 1000000000;
+ flags = MSG_DONTWAIT;
+ error = soreceive(so, &nam, &auio, &mp,
+ (struct mbuf **)0, &flags);
+ if (mp) {
+ nfs_realign(mp, 10 * NFSX_UNSIGNED);
+ if (nam) {
+ m = nam;
+ m->m_next = mp;
+ } else
+ m = mp;
+ if (slp->ns_recend)
+ slp->ns_recend->m_nextpkt = m;
+ else
+ slp->ns_rec = m;
+ slp->ns_recend = m;
+ m->m_nextpkt = (struct mbuf *)0;
+ }
+ if (error) {
+ if ((so->so_proto->pr_flags & PR_CONNREQUIRED)
+ && error != EWOULDBLOCK) {
+ slp->ns_flag |= SLP_DISCONN;
+ goto dorecs;
+ }
+ }
+ } while (mp);
+ }
+
+ /*
+ * Now try and process the request records, non-blocking.
+ */
+dorecs:
+ if (waitflag == M_DONTWAIT &&
+ (slp->ns_rec || (slp->ns_flag & (SLP_NEEDQ | SLP_DISCONN))))
+ nfsrv_wakenfsd(slp);
+}
+
+/*
+ * Try and extract an RPC request from the mbuf data list received on a
+ * stream socket. The "waitflag" argument indicates whether or not it
+ * can sleep.
+ */
+int
+nfsrv_getstream(slp, waitflag)
+ register struct nfssvc_sock *slp;
+ int waitflag;
+{
+ register struct mbuf *m;
+ register char *cp1, *cp2;
+ register int len;
+ struct mbuf *om, *m2, *recm = 0;
+ u_long recmark;
+
+ if (slp->ns_flag & SLP_GETSTREAM)
+ panic("nfs getstream");
+ slp->ns_flag |= SLP_GETSTREAM;
+ for (;;) {
+ if (slp->ns_reclen == 0) {
+ if (slp->ns_cc < NFSX_UNSIGNED) {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (0);
+ }
+ m = slp->ns_raw;
+ if (m->m_len >= NFSX_UNSIGNED) {
+ bcopy(mtod(m, caddr_t), (caddr_t)&recmark, NFSX_UNSIGNED);
+ m->m_data += NFSX_UNSIGNED;
+ m->m_len -= NFSX_UNSIGNED;
+ } else {
+ cp1 = (caddr_t)&recmark;
+ cp2 = mtod(m, caddr_t);
+ while (cp1 < ((caddr_t)&recmark) + NFSX_UNSIGNED) {
+ while (m->m_len == 0) {
+ m = m->m_next;
+ cp2 = mtod(m, caddr_t);
+ }
+ *cp1++ = *cp2++;
+ m->m_data++;
+ m->m_len--;
+ }
+ }
+ slp->ns_cc -= NFSX_UNSIGNED;
+ slp->ns_reclen = ntohl(recmark) & ~0x80000000;
+ if (slp->ns_reclen < NFS_MINPACKET || slp->ns_reclen > NFS_MAXPACKET) {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (EPERM);
+ }
+ }
+
+ /*
+ * Now get the record part.
+ */
+ if (slp->ns_cc == slp->ns_reclen) {
+ recm = slp->ns_raw;
+ slp->ns_raw = slp->ns_rawend = (struct mbuf *)0;
+ slp->ns_cc = slp->ns_reclen = 0;
+ } else if (slp->ns_cc > slp->ns_reclen) {
+ len = 0;
+ m = slp->ns_raw;
+ om = (struct mbuf *)0;
+ while (len < slp->ns_reclen) {
+ if ((len + m->m_len) > slp->ns_reclen) {
+ m2 = m_copym(m, 0, slp->ns_reclen - len,
+ waitflag);
+ if (m2) {
+ if (om) {
+ om->m_next = m2;
+ recm = slp->ns_raw;
+ } else
+ recm = m2;
+ m->m_data += slp->ns_reclen - len;
+ m->m_len -= slp->ns_reclen - len;
+ len = slp->ns_reclen;
+ } else {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (EWOULDBLOCK);
+ }
+ } else if ((len + m->m_len) == slp->ns_reclen) {
+ om = m;
+ len += m->m_len;
+ m = m->m_next;
+ recm = slp->ns_raw;
+ om->m_next = (struct mbuf *)0;
+ } else {
+ om = m;
+ len += m->m_len;
+ m = m->m_next;
+ }
+ }
+ slp->ns_raw = m;
+ slp->ns_cc -= len;
+ slp->ns_reclen = 0;
+ } else {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (0);
+ }
+ nfs_realign(recm, 10 * NFSX_UNSIGNED);
+ if (slp->ns_recend)
+ slp->ns_recend->m_nextpkt = recm;
+ else
+ slp->ns_rec = recm;
+ slp->ns_recend = recm;
+ }
+}
+
+/*
+ * Parse an RPC header.
+ */
+int
+nfsrv_dorec(slp, nd)
+ register struct nfssvc_sock *slp;
+ register struct nfsd *nd;
+{
+ register struct mbuf *m;
+ int error;
+
+ if ((slp->ns_flag & SLP_VALID) == 0 ||
+ (m = slp->ns_rec) == (struct mbuf *)0)
+ return (ENOBUFS);
+ if (slp->ns_rec = m->m_nextpkt)
+ m->m_nextpkt = (struct mbuf *)0;
+ else
+ slp->ns_recend = (struct mbuf *)0;
+ if (m->m_type == MT_SONAME) {
+ nd->nd_nam = m;
+ nd->nd_md = nd->nd_mrep = m->m_next;
+ m->m_next = (struct mbuf *)0;
+ } else {
+ nd->nd_nam = (struct mbuf *)0;
+ nd->nd_md = nd->nd_mrep = m;
+ }
+ nd->nd_dpos = mtod(nd->nd_md, caddr_t);
+ if (error = nfs_getreq(nd, TRUE)) {
+ m_freem(nd->nd_nam);
+ return (error);
+ }
+ return (0);
+}
+
+/*
+ * Parse an RPC request
+ * - verify it
+ * - fill in the cred struct.
+ */
+int
+nfs_getreq(nd, has_header)
+ register struct nfsd *nd;
+ int has_header;
+{
+ register int len, i;
+ register u_long *tl;
+ register long t1;
+ struct uio uio;
+ struct iovec iov;
+ caddr_t dpos, cp2;
+ u_long nfsvers, auth_type;
+ int error = 0, nqnfs = 0;
+ struct mbuf *mrep, *md;
+
+ mrep = nd->nd_mrep;
+ md = nd->nd_md;
+ dpos = nd->nd_dpos;
+ if (has_header) {
+ nfsm_dissect(tl, u_long *, 10*NFSX_UNSIGNED);
+ nd->nd_retxid = *tl++;
+ if (*tl++ != rpc_call) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ } else {
+ nfsm_dissect(tl, u_long *, 8*NFSX_UNSIGNED);
+ }
+ nd->nd_repstat = 0;
+ if (*tl++ != rpc_vers) {
+ nd->nd_repstat = ERPCMISMATCH;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ nfsvers = nfs_vers;
+ if (*tl != nfs_prog) {
+ if (*tl == nqnfs_prog) {
+ nqnfs++;
+ nfsvers = nqnfs_vers;
+ } else {
+ nd->nd_repstat = EPROGUNAVAIL;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ }
+ tl++;
+ if (*tl++ != nfsvers) {
+ nd->nd_repstat = EPROGMISMATCH;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ nd->nd_procnum = fxdr_unsigned(u_long, *tl++);
+ if (nd->nd_procnum == NFSPROC_NULL)
+ return (0);
+ if (nd->nd_procnum >= NFS_NPROCS ||
+ (!nqnfs && nd->nd_procnum > NFSPROC_STATFS) ||
+ (*tl != rpc_auth_unix && *tl != rpc_auth_kerb)) {
+ nd->nd_repstat = EPROCUNAVAIL;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ auth_type = *tl++;
+ len = fxdr_unsigned(int, *tl++);
+ if (len < 0 || len > RPCAUTH_MAXSIZ) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+
+ /*
+ * Handle auth_unix or auth_kerb.
+ */
+ if (auth_type == rpc_auth_unix) {
+ len = fxdr_unsigned(int, *++tl);
+ if (len < 0 || len > NFS_MAXNAMLEN) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ nfsm_adv(nfsm_rndup(len));
+ nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED);
+ nd->nd_cr.cr_uid = fxdr_unsigned(uid_t, *tl++);
+ nd->nd_cr.cr_gid = fxdr_unsigned(gid_t, *tl++);
+ len = fxdr_unsigned(int, *tl);
+ if (len < 0 || len > RPCAUTH_UNIXGIDS) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ nfsm_dissect(tl, u_long *, (len + 2)*NFSX_UNSIGNED);
+ for (i = 1; i <= len; i++)
+ if (i < NGROUPS)
+ nd->nd_cr.cr_groups[i] = fxdr_unsigned(gid_t, *tl++);
+ else
+ tl++;
+ nd->nd_cr.cr_ngroups = (len >= NGROUPS) ? NGROUPS : (len + 1);
+ } else if (auth_type == rpc_auth_kerb) {
+ nd->nd_cr.cr_uid = fxdr_unsigned(uid_t, *tl++);
+ nd->nd_authlen = fxdr_unsigned(int, *tl);
+ uio.uio_resid = nfsm_rndup(nd->nd_authlen);
+ if (uio.uio_resid > (len - 2 * NFSX_UNSIGNED)) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ uio.uio_offset = 0;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_segflg = UIO_SYSSPACE;
+ iov.iov_base = (caddr_t)nd->nd_authstr;
+ iov.iov_len = RPCAUTH_MAXSIZ;
+ nfsm_mtouio(&uio, uio.uio_resid);
+ nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+ nd->nd_flag |= NFSD_NEEDAUTH;
+ }
+
+ /*
+ * Do we have any use for the verifier.
+ * According to the "Remote Procedure Call Protocol Spec." it
+ * should be AUTH_NULL, but some clients make it AUTH_UNIX?
+ * For now, just skip over it
+ */
+ len = fxdr_unsigned(int, *++tl);
+ if (len < 0 || len > RPCAUTH_MAXSIZ) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ if (len > 0) {
+ nfsm_adv(nfsm_rndup(len));
+ }
+
+ /*
+ * For nqnfs, get piggybacked lease request.
+ */
+ if (nqnfs && nd->nd_procnum != NQNFSPROC_EVICTED) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ nd->nd_nqlflag = fxdr_unsigned(int, *tl);
+ if (nd->nd_nqlflag) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ nd->nd_duration = fxdr_unsigned(int, *tl);
+ } else
+ nd->nd_duration = NQ_MINLEASE;
+ } else {
+ nd->nd_nqlflag = NQL_NOVAL;
+ nd->nd_duration = NQ_MINLEASE;
+ }
+ nd->nd_md = md;
+ nd->nd_dpos = dpos;
+ return (0);
+nfsmout:
+ return (error);
+}
+
+/*
+ * Search for a sleeping nfsd and wake it up.
+ * SIDE EFFECT: If none found, set NFSD_CHECKSLP flag, so that one of the
+ * running nfsds will go look for the work in the nfssvc_sock list.
+ */
+void
+nfsrv_wakenfsd(slp)
+ struct nfssvc_sock *slp;
+{
+ register struct nfsd *nd = nfsd_head.nd_next;
+
+ if ((slp->ns_flag & SLP_VALID) == 0)
+ return;
+ while (nd != (struct nfsd *)&nfsd_head) {
+ if (nd->nd_flag & NFSD_WAITING) {
+ nd->nd_flag &= ~NFSD_WAITING;
+ if (nd->nd_slp)
+ panic("nfsd wakeup");
+ slp->ns_sref++;
+ nd->nd_slp = slp;
+ wakeup((caddr_t)nd);
+ return;
+ }
+ nd = nd->nd_next;
+ }
+ slp->ns_flag |= SLP_DOREC;
+ nfsd_head.nd_flag |= NFSD_CHECKSLP;
+}
+
+int
+nfs_msg(p, server, msg)
+ struct proc *p;
+ char *server, *msg;
+{
+ tpr_t tpr;
+
+ if (p)
+ tpr = tprintf_open(p);
+ else
+ tpr = NULL;
+ tprintf(tpr, "nfs server %s: %s\n", server, msg);
+ tprintf_close(tpr);
+ return (0);
+}
diff --git a/sys/nfsserver/nfs_srvsubs.c b/sys/nfsserver/nfs_srvsubs.c
new file mode 100644
index 0000000..d17cde7
--- /dev/null
+++ b/sys/nfsserver/nfs_srvsubs.c
@@ -0,0 +1,1143 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_subs.c 8.3 (Berkeley) 1/4/94
+ */
+
+/*
+ * These functions support the macros and help fiddle mbuf chains for
+ * the nfs op functions. They do things like create the rpc header and
+ * copy data between mbuf chains and uio lists.
+ */
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfs.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nqnfs.h>
+#include <nfs/nfsrtt.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <netinet/in.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Data items converted to xdr at startup, since they are constant
+ * This is kinda hokey, but may save a little time doing byte swaps
+ */
+u_long nfs_procids[NFS_NPROCS];
+u_long nfs_xdrneg1;
+u_long rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr,
+ rpc_mismatch, rpc_auth_unix, rpc_msgaccepted, rpc_rejectedcred,
+ rpc_auth_kerb;
+u_long nfs_vers, nfs_prog, nfs_true, nfs_false;
+
+/* And other global data */
+static u_long nfs_xid = 0;
+enum vtype ntov_type[7] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON };
+extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
+extern struct nfsreq nfsreqh;
+extern int nqnfs_piggy[NFS_NPROCS];
+extern struct nfsrtt nfsrtt;
+extern time_t nqnfsstarttime;
+extern u_long nqnfs_prog, nqnfs_vers;
+extern int nqsrv_clockskew;
+extern int nqsrv_writeslack;
+extern int nqsrv_maxlease;
+
+/*
+ * Create the header for an rpc request packet
+ * The hsiz is the size of the rest of the nfs request header.
+ * (just used to decide if a cluster is a good idea)
+ */
+struct mbuf *
+nfsm_reqh(vp, procid, hsiz, bposp)
+ struct vnode *vp;
+ u_long procid;
+ int hsiz;
+ caddr_t *bposp;
+{
+ register struct mbuf *mb;
+ register u_long *tl;
+ register caddr_t bpos;
+ struct mbuf *mb2;
+ struct nfsmount *nmp;
+ int nqflag;
+
+ MGET(mb, M_WAIT, MT_DATA);
+ if (hsiz >= MINCLSIZE)
+ MCLGET(mb, M_WAIT);
+ mb->m_len = 0;
+ bpos = mtod(mb, caddr_t);
+
+ /*
+ * For NQNFS, add lease request.
+ */
+ if (vp) {
+ nmp = VFSTONFS(vp->v_mount);
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ nqflag = NQNFS_NEEDLEASE(vp, procid);
+ if (nqflag) {
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(nqflag);
+ *tl = txdr_unsigned(nmp->nm_leaseterm);
+ } else {
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = 0;
+ }
+ }
+ }
+ /* Finally, return values */
+ *bposp = bpos;
+ return (mb);
+}
+
+/*
+ * Build the RPC header and fill in the authorization info.
+ * The authorization string argument is only used when the credentials
+ * come from outside of the kernel.
+ * Returns the head of the mbuf list.
+ */
+struct mbuf *
+nfsm_rpchead(cr, nqnfs, procid, auth_type, auth_len, auth_str, mrest,
+ mrest_len, mbp, xidp)
+ register struct ucred *cr;
+ int nqnfs;
+ int procid;
+ int auth_type;
+ int auth_len;
+ char *auth_str;
+ struct mbuf *mrest;
+ int mrest_len;
+ struct mbuf **mbp;
+ u_long *xidp;
+{
+ register struct mbuf *mb;
+ register u_long *tl;
+ register caddr_t bpos;
+ register int i;
+ struct mbuf *mreq, *mb2;
+ int siz, grpsiz, authsiz;
+
+ authsiz = nfsm_rndup(auth_len);
+ if (auth_type == RPCAUTH_NQNFS)
+ authsiz += 2 * NFSX_UNSIGNED;
+ MGETHDR(mb, M_WAIT, MT_DATA);
+ if ((authsiz + 10*NFSX_UNSIGNED) >= MINCLSIZE) {
+ MCLGET(mb, M_WAIT);
+ } else if ((authsiz + 10*NFSX_UNSIGNED) < MHLEN) {
+ MH_ALIGN(mb, authsiz + 10*NFSX_UNSIGNED);
+ } else {
+ MH_ALIGN(mb, 8*NFSX_UNSIGNED);
+ }
+ mb->m_len = 0;
+ mreq = mb;
+ bpos = mtod(mb, caddr_t);
+
+ /*
+ * First the RPC header.
+ */
+ nfsm_build(tl, u_long *, 8*NFSX_UNSIGNED);
+ if (++nfs_xid == 0)
+ nfs_xid++;
+ *tl++ = *xidp = txdr_unsigned(nfs_xid);
+ *tl++ = rpc_call;
+ *tl++ = rpc_vers;
+ if (nqnfs) {
+ *tl++ = txdr_unsigned(NQNFS_PROG);
+ *tl++ = txdr_unsigned(NQNFS_VER1);
+ } else {
+ *tl++ = txdr_unsigned(NFS_PROG);
+ *tl++ = txdr_unsigned(NFS_VER2);
+ }
+ *tl++ = txdr_unsigned(procid);
+
+ /*
+ * And then the authorization cred.
+ */
+ *tl++ = txdr_unsigned(auth_type);
+ *tl = txdr_unsigned(authsiz);
+ switch (auth_type) {
+ case RPCAUTH_UNIX:
+ nfsm_build(tl, u_long *, auth_len);
+ *tl++ = 0; /* stamp ?? */
+ *tl++ = 0; /* NULL hostname */
+ *tl++ = txdr_unsigned(cr->cr_uid);
+ *tl++ = txdr_unsigned(cr->cr_groups[0]);
+ grpsiz = (auth_len >> 2) - 5;
+ *tl++ = txdr_unsigned(grpsiz);
+ for (i = 1; i <= grpsiz; i++)
+ *tl++ = txdr_unsigned(cr->cr_groups[i]);
+ break;
+ case RPCAUTH_NQNFS:
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(cr->cr_uid);
+ *tl = txdr_unsigned(auth_len);
+ siz = auth_len;
+ while (siz > 0) {
+ if (M_TRAILINGSPACE(mb) == 0) {
+ MGET(mb2, M_WAIT, MT_DATA);
+ if (siz >= MINCLSIZE)
+ MCLGET(mb2, M_WAIT);
+ mb->m_next = mb2;
+ mb = mb2;
+ mb->m_len = 0;
+ bpos = mtod(mb, caddr_t);
+ }
+ i = min(siz, M_TRAILINGSPACE(mb));
+ bcopy(auth_str, bpos, i);
+ mb->m_len += i;
+ auth_str += i;
+ bpos += i;
+ siz -= i;
+ }
+ if ((siz = (nfsm_rndup(auth_len) - auth_len)) > 0) {
+ for (i = 0; i < siz; i++)
+ *bpos++ = '\0';
+ mb->m_len += siz;
+ }
+ break;
+ };
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(RPCAUTH_NULL);
+ *tl = 0;
+ mb->m_next = mrest;
+ mreq->m_pkthdr.len = authsiz + 10*NFSX_UNSIGNED + mrest_len;
+ mreq->m_pkthdr.rcvif = (struct ifnet *)0;
+ *mbp = mb;
+ return (mreq);
+}
+
+/*
+ * copies mbuf chain to the uio scatter/gather list
+ */
+int
+nfsm_mbuftouio(mrep, uiop, siz, dpos)
+ struct mbuf **mrep;
+ register struct uio *uiop;
+ int siz;
+ caddr_t *dpos;
+{
+ register char *mbufcp, *uiocp;
+ register int xfer, left, len;
+ register struct mbuf *mp;
+ long uiosiz, rem;
+ int error = 0;
+
+ mp = *mrep;
+ mbufcp = *dpos;
+ len = mtod(mp, caddr_t)+mp->m_len-mbufcp;
+ rem = nfsm_rndup(siz)-siz;
+ while (siz > 0) {
+ if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
+ return (EFBIG);
+ left = uiop->uio_iov->iov_len;
+ uiocp = uiop->uio_iov->iov_base;
+ if (left > siz)
+ left = siz;
+ uiosiz = left;
+ while (left > 0) {
+ while (len == 0) {
+ mp = mp->m_next;
+ if (mp == NULL)
+ return (EBADRPC);
+ mbufcp = mtod(mp, caddr_t);
+ len = mp->m_len;
+ }
+ xfer = (left > len) ? len : left;
+#ifdef notdef
+ /* Not Yet.. */
+ if (uiop->uio_iov->iov_op != NULL)
+ (*(uiop->uio_iov->iov_op))
+ (mbufcp, uiocp, xfer);
+ else
+#endif
+ if (uiop->uio_segflg == UIO_SYSSPACE)
+ bcopy(mbufcp, uiocp, xfer);
+ else
+ copyout(mbufcp, uiocp, xfer);
+ left -= xfer;
+ len -= xfer;
+ mbufcp += xfer;
+ uiocp += xfer;
+ uiop->uio_offset += xfer;
+ uiop->uio_resid -= xfer;
+ }
+ if (uiop->uio_iov->iov_len <= siz) {
+ uiop->uio_iovcnt--;
+ uiop->uio_iov++;
+ } else {
+ uiop->uio_iov->iov_base += uiosiz;
+ uiop->uio_iov->iov_len -= uiosiz;
+ }
+ siz -= uiosiz;
+ }
+ *dpos = mbufcp;
+ *mrep = mp;
+ if (rem > 0) {
+ if (len < rem)
+ error = nfs_adv(mrep, dpos, rem, len);
+ else
+ *dpos += rem;
+ }
+ return (error);
+}
+
+/*
+ * copies a uio scatter/gather list to an mbuf chain...
+ */
+int
+nfsm_uiotombuf(uiop, mq, siz, bpos)
+ register struct uio *uiop;
+ struct mbuf **mq;
+ int siz;
+ caddr_t *bpos;
+{
+ register char *uiocp;
+ register struct mbuf *mp, *mp2;
+ register int xfer, left, mlen;
+ int uiosiz, clflg, rem;
+ char *cp;
+
+ if (siz > MLEN) /* or should it >= MCLBYTES ?? */
+ clflg = 1;
+ else
+ clflg = 0;
+ rem = nfsm_rndup(siz)-siz;
+ mp = mp2 = *mq;
+ while (siz > 0) {
+ if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
+ return (EINVAL);
+ left = uiop->uio_iov->iov_len;
+ uiocp = uiop->uio_iov->iov_base;
+ if (left > siz)
+ left = siz;
+ uiosiz = left;
+ while (left > 0) {
+ mlen = M_TRAILINGSPACE(mp);
+ if (mlen == 0) {
+ MGET(mp, M_WAIT, MT_DATA);
+ if (clflg)
+ MCLGET(mp, M_WAIT);
+ mp->m_len = 0;
+ mp2->m_next = mp;
+ mp2 = mp;
+ mlen = M_TRAILINGSPACE(mp);
+ }
+ xfer = (left > mlen) ? mlen : left;
+#ifdef notdef
+ /* Not Yet.. */
+ if (uiop->uio_iov->iov_op != NULL)
+ (*(uiop->uio_iov->iov_op))
+ (uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
+ else
+#endif
+ if (uiop->uio_segflg == UIO_SYSSPACE)
+ bcopy(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
+ else
+ copyin(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
+ mp->m_len += xfer;
+ left -= xfer;
+ uiocp += xfer;
+ uiop->uio_offset += xfer;
+ uiop->uio_resid -= xfer;
+ }
+ if (uiop->uio_iov->iov_len <= siz) {
+ uiop->uio_iovcnt--;
+ uiop->uio_iov++;
+ } else {
+ uiop->uio_iov->iov_base += uiosiz;
+ uiop->uio_iov->iov_len -= uiosiz;
+ }
+ siz -= uiosiz;
+ }
+ if (rem > 0) {
+ if (rem > M_TRAILINGSPACE(mp)) {
+ MGET(mp, M_WAIT, MT_DATA);
+ mp->m_len = 0;
+ mp2->m_next = mp;
+ }
+ cp = mtod(mp, caddr_t)+mp->m_len;
+ for (left = 0; left < rem; left++)
+ *cp++ = '\0';
+ mp->m_len += rem;
+ *bpos = cp;
+ } else
+ *bpos = mtod(mp, caddr_t)+mp->m_len;
+ *mq = mp;
+ return (0);
+}
+
+/*
+ * Help break down an mbuf chain by setting the first siz bytes contiguous
+ * pointed to by returned val.
+ * This is used by the macros nfsm_dissect and nfsm_dissecton for tough
+ * cases. (The macros use the vars. dpos and dpos2)
+ */
+int
+nfsm_disct(mdp, dposp, siz, left, cp2)
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ int siz;
+ int left;
+ caddr_t *cp2;
+{
+ register struct mbuf *mp, *mp2;
+ register int siz2, xfer;
+ register caddr_t p;
+
+ mp = *mdp;
+ while (left == 0) {
+ *mdp = mp = mp->m_next;
+ if (mp == NULL)
+ return (EBADRPC);
+ left = mp->m_len;
+ *dposp = mtod(mp, caddr_t);
+ }
+ if (left >= siz) {
+ *cp2 = *dposp;
+ *dposp += siz;
+ } else if (mp->m_next == NULL) {
+ return (EBADRPC);
+ } else if (siz > MHLEN) {
+ panic("nfs S too big");
+ } else {
+ MGET(mp2, M_WAIT, MT_DATA);
+ mp2->m_next = mp->m_next;
+ mp->m_next = mp2;
+ mp->m_len -= left;
+ mp = mp2;
+ *cp2 = p = mtod(mp, caddr_t);
+ bcopy(*dposp, p, left); /* Copy what was left */
+ siz2 = siz-left;
+ p += left;
+ mp2 = mp->m_next;
+ /* Loop around copying up the siz2 bytes */
+ while (siz2 > 0) {
+ if (mp2 == NULL)
+ return (EBADRPC);
+ xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
+ if (xfer > 0) {
+ bcopy(mtod(mp2, caddr_t), p, xfer);
+ NFSMADV(mp2, xfer);
+ mp2->m_len -= xfer;
+ p += xfer;
+ siz2 -= xfer;
+ }
+ if (siz2 > 0)
+ mp2 = mp2->m_next;
+ }
+ mp->m_len = siz;
+ *mdp = mp2;
+ *dposp = mtod(mp2, caddr_t);
+ }
+ return (0);
+}
+
+/*
+ * Advance the position in the mbuf chain.
+ */
+int
+nfs_adv(mdp, dposp, offs, left)
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ int offs;
+ int left;
+{
+ register struct mbuf *m;
+ register int s;
+
+ m = *mdp;
+ s = left;
+ while (s < offs) {
+ offs -= s;
+ m = m->m_next;
+ if (m == NULL)
+ return (EBADRPC);
+ s = m->m_len;
+ }
+ *mdp = m;
+ *dposp = mtod(m, caddr_t)+offs;
+ return (0);
+}
+
+/*
+ * Copy a string into mbufs for the hard cases...
+ */
+int
+nfsm_strtmbuf(mb, bpos, cp, siz)
+ struct mbuf **mb;
+ char **bpos;
+ char *cp;
+ long siz;
+{
+ register struct mbuf *m1 = 0, *m2;
+ long left, xfer, len, tlen;
+ u_long *tl;
+ int putsize;
+
+ putsize = 1;
+ m2 = *mb;
+ left = M_TRAILINGSPACE(m2);
+ if (left > 0) {
+ tl = ((u_long *)(*bpos));
+ *tl++ = txdr_unsigned(siz);
+ putsize = 0;
+ left -= NFSX_UNSIGNED;
+ m2->m_len += NFSX_UNSIGNED;
+ if (left > 0) {
+ bcopy(cp, (caddr_t) tl, left);
+ siz -= left;
+ cp += left;
+ m2->m_len += left;
+ left = 0;
+ }
+ }
+ /* Loop around adding mbufs */
+ while (siz > 0) {
+ MGET(m1, M_WAIT, MT_DATA);
+ if (siz > MLEN)
+ MCLGET(m1, M_WAIT);
+ m1->m_len = NFSMSIZ(m1);
+ m2->m_next = m1;
+ m2 = m1;
+ tl = mtod(m1, u_long *);
+ tlen = 0;
+ if (putsize) {
+ *tl++ = txdr_unsigned(siz);
+ m1->m_len -= NFSX_UNSIGNED;
+ tlen = NFSX_UNSIGNED;
+ putsize = 0;
+ }
+ if (siz < m1->m_len) {
+ len = nfsm_rndup(siz);
+ xfer = siz;
+ if (xfer < len)
+ *(tl+(xfer>>2)) = 0;
+ } else {
+ xfer = len = m1->m_len;
+ }
+ bcopy(cp, (caddr_t) tl, xfer);
+ m1->m_len = len+tlen;
+ siz -= xfer;
+ cp += xfer;
+ }
+ *mb = m1;
+ *bpos = mtod(m1, caddr_t)+m1->m_len;
+ return (0);
+}
+
+/*
+ * Called once to initialize data structures...
+ */
+int
+nfs_init()
+{
+ register int i;
+
+ nfsrtt.pos = 0;
+ rpc_vers = txdr_unsigned(RPC_VER2);
+ rpc_call = txdr_unsigned(RPC_CALL);
+ rpc_reply = txdr_unsigned(RPC_REPLY);
+ rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
+ rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
+ rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
+ rpc_autherr = txdr_unsigned(RPC_AUTHERR);
+ rpc_rejectedcred = txdr_unsigned(AUTH_REJECTCRED);
+ rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
+ rpc_auth_kerb = txdr_unsigned(RPCAUTH_NQNFS);
+ nfs_vers = txdr_unsigned(NFS_VER2);
+ nfs_prog = txdr_unsigned(NFS_PROG);
+ nfs_true = txdr_unsigned(TRUE);
+ nfs_false = txdr_unsigned(FALSE);
+ /* Loop thru nfs procids */
+ for (i = 0; i < NFS_NPROCS; i++)
+ nfs_procids[i] = txdr_unsigned(i);
+ /* Ensure async daemons disabled */
+ for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
+ nfs_iodwant[i] = (struct proc *)0;
+ TAILQ_INIT(&nfs_bufq);
+ nfs_xdrneg1 = txdr_unsigned(-1);
+ nfs_nhinit(); /* Init the nfsnode table */
+ nfsrv_init(0); /* Init server data structures */
+ nfsrv_initcache(); /* Init the server request cache */
+
+ /*
+ * Initialize the nqnfs server stuff.
+ */
+ if (nqnfsstarttime == 0) {
+ nqnfsstarttime = boottime.tv_sec + nqsrv_maxlease
+ + nqsrv_clockskew + nqsrv_writeslack;
+ NQLOADNOVRAM(nqnfsstarttime);
+ nqnfs_prog = txdr_unsigned(NQNFS_PROG);
+ nqnfs_vers = txdr_unsigned(NQNFS_VER1);
+ nqthead.th_head[0] = &nqthead;
+ nqthead.th_head[1] = &nqthead;
+ nqfhead = hashinit(NQLCHSZ, M_NQLEASE, &nqfheadhash);
+ }
+
+ /*
+ * Initialize reply list and start timer
+ */
+ nfsreqh.r_prev = nfsreqh.r_next = &nfsreqh;
+ nfs_timer();
+
+ return (0);
+}
+
+/*
+ * Attribute cache routines.
+ * nfs_loadattrcache() - loads or updates the cache contents from attributes
+ * that are on the mbuf list
+ * nfs_getattrcache() - returns valid attributes if found in cache, returns
+ * error otherwise
+ */
+
+/*
+ * Load the attribute cache (that lives in the nfsnode entry) with
+ * the values on the mbuf list and
+ * Iff vap not NULL
+ * copy the attributes to *vaper
+ */
+int
+nfs_loadattrcache(vpp, mdp, dposp, vaper)
+ struct vnode **vpp;
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ struct vattr *vaper;
+{
+ register struct vnode *vp = *vpp;
+ register struct vattr *vap;
+ register struct nfsv2_fattr *fp;
+ extern int (**spec_nfsv2nodeop_p)();
+ register struct nfsnode *np, *nq, **nhpp;
+ register long t1;
+ caddr_t dpos, cp2;
+ int error = 0, isnq;
+ struct mbuf *md;
+ enum vtype vtyp;
+ u_short vmode;
+ long rdev;
+ struct timespec mtime;
+ struct vnode *nvp;
+
+ md = *mdp;
+ dpos = *dposp;
+ t1 = (mtod(md, caddr_t) + md->m_len) - dpos;
+ isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ if (error = nfsm_disct(&md, &dpos, NFSX_FATTR(isnq), t1, &cp2))
+ return (error);
+ fp = (struct nfsv2_fattr *)cp2;
+ vtyp = nfstov_type(fp->fa_type);
+ vmode = fxdr_unsigned(u_short, fp->fa_mode);
+ if (vtyp == VNON || vtyp == VREG)
+ vtyp = IFTOVT(vmode);
+ if (isnq) {
+ rdev = fxdr_unsigned(long, fp->fa_nqrdev);
+ fxdr_nqtime(&fp->fa_nqmtime, &mtime);
+ } else {
+ rdev = fxdr_unsigned(long, fp->fa_nfsrdev);
+ fxdr_nfstime(&fp->fa_nfsmtime, &mtime);
+ }
+ /*
+ * If v_type == VNON it is a new node, so fill in the v_type,
+ * n_mtime fields. Check to see if it represents a special
+ * device, and if so, check for a possible alias. Once the
+ * correct vnode has been obtained, fill in the rest of the
+ * information.
+ */
+ np = VTONFS(vp);
+ if (vp->v_type == VNON) {
+ if (vtyp == VCHR && rdev == 0xffffffff)
+ vp->v_type = vtyp = VFIFO;
+ else
+ vp->v_type = vtyp;
+ if (vp->v_type == VFIFO) {
+#ifdef FIFO
+ extern int (**fifo_nfsv2nodeop_p)();
+ vp->v_op = fifo_nfsv2nodeop_p;
+#else
+ return (EOPNOTSUPP);
+#endif /* FIFO */
+ }
+ if (vp->v_type == VCHR || vp->v_type == VBLK) {
+ vp->v_op = spec_nfsv2nodeop_p;
+ if (nvp = checkalias(vp, (dev_t)rdev, vp->v_mount)) {
+ /*
+ * Discard unneeded vnode, but save its nfsnode.
+ */
+ if (nq = np->n_forw)
+ nq->n_back = np->n_back;
+ *np->n_back = nq;
+ nvp->v_data = vp->v_data;
+ vp->v_data = NULL;
+ vp->v_op = spec_vnodeop_p;
+ vrele(vp);
+ vgone(vp);
+ /*
+ * Reinitialize aliased node.
+ */
+ np->n_vnode = nvp;
+ nhpp = (struct nfsnode **)nfs_hash(&np->n_fh);
+ if (nq = *nhpp)
+ nq->n_back = &np->n_forw;
+ np->n_forw = nq;
+ np->n_back = nhpp;
+ *nhpp = np;
+ *vpp = vp = nvp;
+ }
+ }
+ np->n_mtime = mtime.ts_sec;
+ }
+ vap = &np->n_vattr;
+ vap->va_type = vtyp;
+ vap->va_mode = (vmode & 07777);
+ vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
+ vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
+ vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
+ vap->va_rdev = (dev_t)rdev;
+ vap->va_mtime = mtime;
+ vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+ if (isnq) {
+ fxdr_hyper(&fp->fa_nqsize, &vap->va_size);
+ vap->va_blocksize = fxdr_unsigned(long, fp->fa_nqblocksize);
+ fxdr_hyper(&fp->fa_nqbytes, &vap->va_bytes);
+ vap->va_fileid = fxdr_unsigned(long, fp->fa_nqfileid);
+ fxdr_nqtime(&fp->fa_nqatime, &vap->va_atime);
+ vap->va_flags = fxdr_unsigned(u_long, fp->fa_nqflags);
+ fxdr_nqtime(&fp->fa_nqctime, &vap->va_ctime);
+ vap->va_gen = fxdr_unsigned(u_long, fp->fa_nqgen);
+ fxdr_hyper(&fp->fa_nqfilerev, &vap->va_filerev);
+ } else {
+ vap->va_size = fxdr_unsigned(u_long, fp->fa_nfssize);
+ vap->va_blocksize = fxdr_unsigned(long, fp->fa_nfsblocksize);
+ vap->va_bytes = fxdr_unsigned(long, fp->fa_nfsblocks) * NFS_FABLKSIZE;
+ vap->va_fileid = fxdr_unsigned(long, fp->fa_nfsfileid);
+ fxdr_nfstime(&fp->fa_nfsatime, &vap->va_atime);
+ vap->va_flags = 0;
+ vap->va_ctime.ts_sec = fxdr_unsigned(long, fp->fa_nfsctime.nfs_sec);
+ vap->va_ctime.ts_nsec = 0;
+ vap->va_gen = fxdr_unsigned(u_long, fp->fa_nfsctime.nfs_usec);
+ vap->va_filerev = 0;
+ }
+ if (vap->va_size != np->n_size) {
+ if (vap->va_type == VREG) {
+ if (np->n_flag & NMODIFIED) {
+ if (vap->va_size < np->n_size)
+ vap->va_size = np->n_size;
+ else
+ np->n_size = vap->va_size;
+ } else
+ np->n_size = vap->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else
+ np->n_size = vap->va_size;
+ }
+ np->n_attrstamp = time.tv_sec;
+ *dposp = dpos;
+ *mdp = md;
+ if (vaper != NULL) {
+ bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
+#ifdef notdef
+ if ((np->n_flag & NMODIFIED) && np->n_size > vap->va_size)
+ if (np->n_size > vap->va_size)
+ vaper->va_size = np->n_size;
+#endif
+ if (np->n_flag & NCHG) {
+ if (np->n_flag & NACC) {
+ vaper->va_atime.ts_sec = np->n_atim.tv_sec;
+ vaper->va_atime.ts_nsec =
+ np->n_atim.tv_usec * 1000;
+ }
+ if (np->n_flag & NUPD) {
+ vaper->va_mtime.ts_sec = np->n_mtim.tv_sec;
+ vaper->va_mtime.ts_nsec =
+ np->n_mtim.tv_usec * 1000;
+ }
+ }
+ }
+ return (0);
+}
+
+/*
+ * Check the time stamp
+ * If the cache is valid, copy contents to *vap and return 0
+ * otherwise return an error
+ */
+int
+nfs_getattrcache(vp, vaper)
+ register struct vnode *vp;
+ struct vattr *vaper;
+{
+ register struct nfsnode *np = VTONFS(vp);
+ register struct vattr *vap;
+
+ if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQLOOKLEASE) {
+ if (!NQNFS_CKCACHABLE(vp, NQL_READ) || np->n_attrstamp == 0) {
+ nfsstats.attrcache_misses++;
+ return (ENOENT);
+ }
+ } else if ((time.tv_sec - np->n_attrstamp) >= NFS_ATTRTIMEO(np)) {
+ nfsstats.attrcache_misses++;
+ return (ENOENT);
+ }
+ nfsstats.attrcache_hits++;
+ vap = &np->n_vattr;
+ if (vap->va_size != np->n_size) {
+ if (vap->va_type == VREG) {
+ if (np->n_flag & NMODIFIED) {
+ if (vap->va_size < np->n_size)
+ vap->va_size = np->n_size;
+ else
+ np->n_size = vap->va_size;
+ } else
+ np->n_size = vap->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else
+ np->n_size = vap->va_size;
+ }
+ bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(struct vattr));
+#ifdef notdef
+ if ((np->n_flag & NMODIFIED) == 0) {
+ np->n_size = vaper->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else if (np->n_size > vaper->va_size)
+ if (np->n_size > vaper->va_size)
+ vaper->va_size = np->n_size;
+#endif
+ if (np->n_flag & NCHG) {
+ if (np->n_flag & NACC) {
+ vaper->va_atime.ts_sec = np->n_atim.tv_sec;
+ vaper->va_atime.ts_nsec = np->n_atim.tv_usec * 1000;
+ }
+ if (np->n_flag & NUPD) {
+ vaper->va_mtime.ts_sec = np->n_mtim.tv_sec;
+ vaper->va_mtime.ts_nsec = np->n_mtim.tv_usec * 1000;
+ }
+ }
+ return (0);
+}
+
+/*
+ * Set up nameidata for a lookup() call and do it
+ */
+int
+nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p)
+ register struct nameidata *ndp;
+ fhandle_t *fhp;
+ int len;
+ struct nfssvc_sock *slp;
+ struct mbuf *nam;
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ struct proc *p;
+{
+ register int i, rem;
+ register struct mbuf *md;
+ register char *fromcp, *tocp;
+ struct vnode *dp;
+ int error, rdonly;
+ struct componentname *cnp = &ndp->ni_cnd;
+
+ MALLOC(cnp->cn_pnbuf, char *, len + 1, M_NAMEI, M_WAITOK);
+ /*
+ * Copy the name from the mbuf list to ndp->ni_pnbuf
+ * and set the various ndp fields appropriately.
+ */
+ fromcp = *dposp;
+ tocp = cnp->cn_pnbuf;
+ md = *mdp;
+ rem = mtod(md, caddr_t) + md->m_len - fromcp;
+ cnp->cn_hash = 0;
+ for (i = 0; i < len; i++) {
+ while (rem == 0) {
+ md = md->m_next;
+ if (md == NULL) {
+ error = EBADRPC;
+ goto out;
+ }
+ fromcp = mtod(md, caddr_t);
+ rem = md->m_len;
+ }
+ if (*fromcp == '\0' || *fromcp == '/') {
+ error = EINVAL;
+ goto out;
+ }
+ cnp->cn_hash += (unsigned char)*fromcp;
+ *tocp++ = *fromcp++;
+ rem--;
+ }
+ *tocp = '\0';
+ *mdp = md;
+ *dposp = fromcp;
+ len = nfsm_rndup(len)-len;
+ if (len > 0) {
+ if (rem >= len)
+ *dposp += len;
+ else if (error = nfs_adv(mdp, dposp, len, rem))
+ goto out;
+ }
+ ndp->ni_pathlen = tocp - cnp->cn_pnbuf;
+ cnp->cn_nameptr = cnp->cn_pnbuf;
+ /*
+ * Extract and set starting directory.
+ */
+ if (error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
+ nam, &rdonly))
+ goto out;
+ if (dp->v_type != VDIR) {
+ vrele(dp);
+ error = ENOTDIR;
+ goto out;
+ }
+ ndp->ni_startdir = dp;
+ if (rdonly)
+ cnp->cn_flags |= (NOCROSSMOUNT | RDONLY);
+ else
+ cnp->cn_flags |= NOCROSSMOUNT;
+ /*
+ * And call lookup() to do the real work
+ */
+ cnp->cn_proc = p;
+ if (error = lookup(ndp))
+ goto out;
+ /*
+ * Check for encountering a symbolic link
+ */
+ if (cnp->cn_flags & ISSYMLINK) {
+ if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
+ vput(ndp->ni_dvp);
+ else
+ vrele(ndp->ni_dvp);
+ vput(ndp->ni_vp);
+ ndp->ni_vp = NULL;
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * Check for saved name request
+ */
+ if (cnp->cn_flags & (SAVENAME | SAVESTART)) {
+ cnp->cn_flags |= HASBUF;
+ return (0);
+ }
+out:
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ return (error);
+}
+
+/*
+ * A fiddled version of m_adj() that ensures null fill to a long
+ * boundary and only trims off the back end
+ */
+void
+nfsm_adj(mp, len, nul)
+ struct mbuf *mp;
+ register int len;
+ int nul;
+{
+ register struct mbuf *m;
+ register int count, i;
+ register char *cp;
+
+ /*
+ * Trim from tail. Scan the mbuf chain,
+ * calculating its length and finding the last mbuf.
+ * If the adjustment only affects this mbuf, then just
+ * adjust and return. Otherwise, rescan and truncate
+ * after the remaining size.
+ */
+ count = 0;
+ m = mp;
+ for (;;) {
+ count += m->m_len;
+ if (m->m_next == (struct mbuf *)0)
+ break;
+ m = m->m_next;
+ }
+ if (m->m_len > len) {
+ m->m_len -= len;
+ if (nul > 0) {
+ cp = mtod(m, caddr_t)+m->m_len-nul;
+ for (i = 0; i < nul; i++)
+ *cp++ = '\0';
+ }
+ return;
+ }
+ count -= len;
+ if (count < 0)
+ count = 0;
+ /*
+ * Correct length for chain is "count".
+ * Find the mbuf with last data, adjust its length,
+ * and toss data from remaining mbufs on chain.
+ */
+ for (m = mp; m; m = m->m_next) {
+ if (m->m_len >= count) {
+ m->m_len = count;
+ if (nul > 0) {
+ cp = mtod(m, caddr_t)+m->m_len-nul;
+ for (i = 0; i < nul; i++)
+ *cp++ = '\0';
+ }
+ break;
+ }
+ count -= m->m_len;
+ }
+ while (m = m->m_next)
+ m->m_len = 0;
+}
+
+/*
+ * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
+ * - look up fsid in mount list (if not found ret error)
+ * - get vp and export rights by calling VFS_FHTOVP()
+ * - if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
+ * - if not lockflag unlock it with VOP_UNLOCK()
+ */
+int
+nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp)
+ fhandle_t *fhp;
+ int lockflag;
+ struct vnode **vpp;
+ struct ucred *cred;
+ struct nfssvc_sock *slp;
+ struct mbuf *nam;
+ int *rdonlyp;
+{
+ register struct mount *mp;
+ register struct nfsuid *uidp;
+ register int i;
+ struct ucred *credanon;
+ int error, exflags;
+
+ *vpp = (struct vnode *)0;
+ if ((mp = getvfs(&fhp->fh_fsid)) == NULL)
+ return (ESTALE);
+ if (error = VFS_FHTOVP(mp, &fhp->fh_fid, nam, vpp, &exflags, &credanon))
+ return (error);
+ /*
+ * Check/setup credentials.
+ */
+ if (exflags & MNT_EXKERB) {
+ uidp = slp->ns_uidh[NUIDHASH(cred->cr_uid)];
+ while (uidp) {
+ if (uidp->nu_uid == cred->cr_uid)
+ break;
+ uidp = uidp->nu_hnext;
+ }
+ if (uidp) {
+ cred->cr_uid = uidp->nu_cr.cr_uid;
+ for (i = 0; i < uidp->nu_cr.cr_ngroups; i++)
+ cred->cr_groups[i] = uidp->nu_cr.cr_groups[i];
+ } else {
+ vput(*vpp);
+ return (NQNFS_AUTHERR);
+ }
+ } else if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
+ cred->cr_uid = credanon->cr_uid;
+ for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
+ cred->cr_groups[i] = credanon->cr_groups[i];
+ }
+ if (exflags & MNT_EXRDONLY)
+ *rdonlyp = 1;
+ else
+ *rdonlyp = 0;
+ if (!lockflag)
+ VOP_UNLOCK(*vpp);
+ return (0);
+}
+
+/*
+ * This function compares two net addresses by family and returns TRUE
+ * if they are the same host.
+ * If there is any doubt, return FALSE.
+ * The AF_INET family is handled as a special case so that address mbufs
+ * don't need to be saved to store "struct in_addr", which is only 4 bytes.
+ */
+int
+netaddr_match(family, haddr, nam)
+ int family;
+ union nethostaddr *haddr;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *inetaddr;
+
+ switch (family) {
+ case AF_INET:
+ inetaddr = mtod(nam, struct sockaddr_in *);
+ if (inetaddr->sin_family == AF_INET &&
+ inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
+ return (1);
+ break;
+#ifdef ISO
+ case AF_ISO:
+ {
+ register struct sockaddr_iso *isoaddr1, *isoaddr2;
+
+ isoaddr1 = mtod(nam, struct sockaddr_iso *);
+ isoaddr2 = mtod(haddr->had_nam, struct sockaddr_iso *);
+ if (isoaddr1->siso_family == AF_ISO &&
+ isoaddr1->siso_nlen > 0 &&
+ isoaddr1->siso_nlen == isoaddr2->siso_nlen &&
+ SAME_ISOADDR(isoaddr1, isoaddr2))
+ return (1);
+ break;
+ }
+#endif /* ISO */
+ default:
+ break;
+ };
+ return (0);
+}
diff --git a/sys/nfsserver/nfs_syscalls.c b/sys/nfsserver/nfs_syscalls.c
new file mode 100644
index 0000000..b00a225
--- /dev/null
+++ b/sys/nfsserver/nfs_syscalls.c
@@ -0,0 +1,883 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_syscalls.c 8.3 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/namei.h>
+#include <sys/syslog.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsrvcache.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nqnfs.h>
+#include <nfs/nfsrtt.h>
+
+void nfsrv_zapsock __P((struct nfssvc_sock *));
+
+/* Global defs. */
+extern u_long nfs_prog, nfs_vers;
+extern int (*nfsrv_procs[NFS_NPROCS])();
+extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
+extern int nfs_numasync;
+extern time_t nqnfsstarttime;
+extern struct nfsrv_req nsrvq_head;
+extern struct nfsd nfsd_head;
+extern int nqsrv_writeslack;
+extern int nfsrtton;
+struct nfssvc_sock *nfs_udpsock, *nfs_cltpsock;
+int nuidhash_max = NFS_MAXUIDHASH;
+static int nfs_numnfsd = 0;
+int nfsd_waiting = 0;
+static int notstarted = 1;
+static int modify_flag = 0;
+static struct nfsdrt nfsdrt;
+void nfsrv_cleancache(), nfsrv_rcv(), nfsrv_wakenfsd(), nfs_sndunlock();
+static void nfsd_rt();
+void nfsrv_slpderef(), nfsrv_init();
+
+#define TRUE 1
+#define FALSE 0
+
+static int nfs_asyncdaemon[NFS_MAXASYNCDAEMON];
+/*
+ * NFS server system calls
+ * getfh() lives here too, but maybe should move to kern/vfs_syscalls.c
+ */
+
+/*
+ * Get file handle system call
+ */
+struct getfh_args {
+ char *fname;
+ fhandle_t *fhp;
+};
+int
+getfh(p, uap, retval)
+ struct proc *p;
+ register struct getfh_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ fhandle_t fh;
+ int error;
+ struct nameidata nd;
+
+ /*
+ * Must be super user
+ */
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ bzero((caddr_t)&fh, sizeof(fh));
+ fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+ error = VFS_VPTOFH(vp, &fh.fh_fid);
+ vput(vp);
+ if (error)
+ return (error);
+ error = copyout((caddr_t)&fh, (caddr_t)uap->fhp, sizeof (fh));
+ return (error);
+}
+
+static struct nfssvc_sock nfssvc_sockhead;
+
+/*
+ * Nfs server psuedo system call for the nfsd's
+ * Based on the flag value it either:
+ * - adds a socket to the selection list
+ * - remains in the kernel as an nfsd
+ * - remains in the kernel as an nfsiod
+ */
+struct nfssvc_args {
+ int flag;
+ caddr_t argp;
+};
+int
+nfssvc(p, uap, retval)
+ struct proc *p;
+ register struct nfssvc_args *uap;
+ int *retval;
+{
+ struct nameidata nd;
+ struct file *fp;
+ struct mbuf *nam;
+ struct nfsd_args nfsdarg;
+ struct nfsd_srvargs nfsd_srvargs, *nsd = &nfsd_srvargs;
+ struct nfsd_cargs ncd;
+ struct nfsd *nfsd;
+ struct nfssvc_sock *slp;
+ struct nfsuid *nuidp, **nuh;
+ struct nfsmount *nmp;
+ int error;
+
+ /*
+ * Must be super user
+ */
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ while (nfssvc_sockhead.ns_flag & SLP_INIT) {
+ nfssvc_sockhead.ns_flag |= SLP_WANTINIT;
+ (void) tsleep((caddr_t)&nfssvc_sockhead, PSOCK, "nfsd init", 0);
+ }
+ if (uap->flag & NFSSVC_BIOD)
+ error = nfssvc_iod(p);
+ else if (uap->flag & NFSSVC_MNTD) {
+ if (error = copyin(uap->argp, (caddr_t)&ncd, sizeof (ncd)))
+ return (error);
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
+ ncd.ncd_dirp, p);
+ if (error = namei(&nd))
+ return (error);
+ if ((nd.ni_vp->v_flag & VROOT) == 0)
+ error = EINVAL;
+ nmp = VFSTONFS(nd.ni_vp->v_mount);
+ vput(nd.ni_vp);
+ if (error)
+ return (error);
+ if ((nmp->nm_flag & NFSMNT_MNTD) &&
+ (uap->flag & NFSSVC_GOTAUTH) == 0)
+ return (0);
+ nmp->nm_flag |= NFSMNT_MNTD;
+ error = nqnfs_clientd(nmp, p->p_ucred, &ncd, uap->flag,
+ uap->argp, p);
+ } else if (uap->flag & NFSSVC_ADDSOCK) {
+ if (error = copyin(uap->argp, (caddr_t)&nfsdarg,
+ sizeof(nfsdarg)))
+ return (error);
+ if (error = getsock(p->p_fd, nfsdarg.sock, &fp))
+ return (error);
+ /*
+ * Get the client address for connected sockets.
+ */
+ if (nfsdarg.name == NULL || nfsdarg.namelen == 0)
+ nam = (struct mbuf *)0;
+ else if (error = sockargs(&nam, nfsdarg.name, nfsdarg.namelen,
+ MT_SONAME))
+ return (error);
+ error = nfssvc_addsock(fp, nam);
+ } else {
+ if (error = copyin(uap->argp, (caddr_t)nsd, sizeof (*nsd)))
+ return (error);
+ if ((uap->flag & NFSSVC_AUTHIN) && (nfsd = nsd->nsd_nfsd) &&
+ (nfsd->nd_slp->ns_flag & SLP_VALID)) {
+ slp = nfsd->nd_slp;
+
+ /*
+ * First check to see if another nfsd has already
+ * added this credential.
+ */
+ nuidp = slp->ns_uidh[NUIDHASH(nsd->nsd_uid)];
+ while (nuidp) {
+ if (nuidp->nu_uid == nsd->nsd_uid)
+ break;
+ nuidp = nuidp->nu_hnext;
+ }
+ if (!nuidp) {
+ /*
+ * Nope, so we will.
+ */
+ if (slp->ns_numuids < nuidhash_max) {
+ slp->ns_numuids++;
+ nuidp = (struct nfsuid *)
+ malloc(sizeof (struct nfsuid), M_NFSUID,
+ M_WAITOK);
+ } else
+ nuidp = (struct nfsuid *)0;
+ if ((slp->ns_flag & SLP_VALID) == 0) {
+ if (nuidp)
+ free((caddr_t)nuidp, M_NFSUID);
+ } else {
+ if (nuidp == (struct nfsuid *)0) {
+ nuidp = slp->ns_lruprev;
+ remque(nuidp);
+ if (nuidp->nu_hprev)
+ nuidp->nu_hprev->nu_hnext =
+ nuidp->nu_hnext;
+ if (nuidp->nu_hnext)
+ nuidp->nu_hnext->nu_hprev =
+ nuidp->nu_hprev;
+ }
+ nuidp->nu_cr = nsd->nsd_cr;
+ if (nuidp->nu_cr.cr_ngroups > NGROUPS)
+ nuidp->nu_cr.cr_ngroups = NGROUPS;
+ nuidp->nu_cr.cr_ref = 1;
+ nuidp->nu_uid = nsd->nsd_uid;
+ insque(nuidp, (struct nfsuid *)slp);
+ nuh = &slp->ns_uidh[NUIDHASH(nsd->nsd_uid)];
+ if (nuidp->nu_hnext = *nuh)
+ nuidp->nu_hnext->nu_hprev = nuidp;
+ nuidp->nu_hprev = (struct nfsuid *)0;
+ *nuh = nuidp;
+ }
+ }
+ }
+ if ((uap->flag & NFSSVC_AUTHINFAIL) && (nfsd = nsd->nsd_nfsd))
+ nfsd->nd_flag |= NFSD_AUTHFAIL;
+ error = nfssvc_nfsd(nsd, uap->argp, p);
+ }
+ if (error == EINTR || error == ERESTART)
+ error = 0;
+ return (error);
+}
+
+/*
+ * Adds a socket to the list for servicing by nfsds.
+ */
+int
+nfssvc_addsock(fp, mynam)
+ struct file *fp;
+ struct mbuf *mynam;
+{
+ register struct mbuf *m;
+ register int siz;
+ register struct nfssvc_sock *slp;
+ register struct socket *so;
+ struct nfssvc_sock *tslp;
+ int error, s;
+
+ so = (struct socket *)fp->f_data;
+ tslp = (struct nfssvc_sock *)0;
+ /*
+ * Add it to the list, as required.
+ */
+ if (so->so_proto->pr_protocol == IPPROTO_UDP) {
+ tslp = nfs_udpsock;
+ if (tslp->ns_flag & SLP_VALID) {
+ m_freem(mynam);
+ return (EPERM);
+ }
+#ifdef ISO
+ } else if (so->so_proto->pr_protocol == ISOPROTO_CLTP) {
+ tslp = nfs_cltpsock;
+ if (tslp->ns_flag & SLP_VALID) {
+ m_freem(mynam);
+ return (EPERM);
+ }
+#endif /* ISO */
+ }
+ if (so->so_type == SOCK_STREAM)
+ siz = NFS_MAXPACKET + sizeof (u_long);
+ else
+ siz = NFS_MAXPACKET;
+ if (error = soreserve(so, siz, siz)) {
+ m_freem(mynam);
+ return (error);
+ }
+
+ /*
+ * Set protocol specific options { for now TCP only } and
+ * reserve some space. For datagram sockets, this can get called
+ * repeatedly for the same socket, but that isn't harmful.
+ */
+ if (so->so_type == SOCK_STREAM) {
+ MGET(m, M_WAIT, MT_SOOPTS);
+ *mtod(m, int *) = 1;
+ m->m_len = sizeof(int);
+ sosetopt(so, SOL_SOCKET, SO_KEEPALIVE, m);
+ }
+ if (so->so_proto->pr_domain->dom_family == AF_INET &&
+ so->so_proto->pr_protocol == IPPROTO_TCP) {
+ MGET(m, M_WAIT, MT_SOOPTS);
+ *mtod(m, int *) = 1;
+ m->m_len = sizeof(int);
+ sosetopt(so, IPPROTO_TCP, TCP_NODELAY, m);
+ }
+ so->so_rcv.sb_flags &= ~SB_NOINTR;
+ so->so_rcv.sb_timeo = 0;
+ so->so_snd.sb_flags &= ~SB_NOINTR;
+ so->so_snd.sb_timeo = 0;
+ if (tslp)
+ slp = tslp;
+ else {
+ slp = (struct nfssvc_sock *)
+ malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
+ bzero((caddr_t)slp, sizeof (struct nfssvc_sock));
+ slp->ns_prev = nfssvc_sockhead.ns_prev;
+ slp->ns_prev->ns_next = slp;
+ slp->ns_next = &nfssvc_sockhead;
+ nfssvc_sockhead.ns_prev = slp;
+ slp->ns_lrunext = slp->ns_lruprev = (struct nfsuid *)slp;
+ }
+ slp->ns_so = so;
+ slp->ns_nam = mynam;
+ fp->f_count++;
+ slp->ns_fp = fp;
+ s = splnet();
+ so->so_upcallarg = (caddr_t)slp;
+ so->so_upcall = nfsrv_rcv;
+ slp->ns_flag = (SLP_VALID | SLP_NEEDQ);
+ nfsrv_wakenfsd(slp);
+ splx(s);
+ return (0);
+}
+
+/*
+ * Called by nfssvc() for nfsds. Just loops around servicing rpc requests
+ * until it is killed by a signal.
+ */
+int
+nfssvc_nfsd(nsd, argp, p)
+ struct nfsd_srvargs *nsd;
+ caddr_t argp;
+ struct proc *p;
+{
+ register struct mbuf *m, *nam2;
+ register int siz;
+ register struct nfssvc_sock *slp;
+ register struct socket *so;
+ register int *solockp;
+ struct nfsd *nd = nsd->nsd_nfsd;
+ struct mbuf *mreq, *nam;
+ struct timeval starttime;
+ struct nfsuid *uidp;
+ int error = 0, cacherep, s;
+ int sotype;
+
+ s = splnet();
+ if (nd == (struct nfsd *)0) {
+ nsd->nsd_nfsd = nd = (struct nfsd *)
+ malloc(sizeof (struct nfsd), M_NFSD, M_WAITOK);
+ bzero((caddr_t)nd, sizeof (struct nfsd));
+ nd->nd_procp = p;
+ nd->nd_cr.cr_ref = 1;
+ insque(nd, &nfsd_head);
+ nd->nd_nqlflag = NQL_NOVAL;
+ nfs_numnfsd++;
+ }
+ /*
+ * Loop getting rpc requests until SIGKILL.
+ */
+ for (;;) {
+ if ((nd->nd_flag & NFSD_REQINPROG) == 0) {
+ while (nd->nd_slp == (struct nfssvc_sock *)0 &&
+ (nfsd_head.nd_flag & NFSD_CHECKSLP) == 0) {
+ nd->nd_flag |= NFSD_WAITING;
+ nfsd_waiting++;
+ error = tsleep((caddr_t)nd, PSOCK | PCATCH, "nfsd", 0);
+ nfsd_waiting--;
+ if (error)
+ goto done;
+ }
+ if (nd->nd_slp == (struct nfssvc_sock *)0 &&
+ (nfsd_head.nd_flag & NFSD_CHECKSLP)) {
+ slp = nfssvc_sockhead.ns_next;
+ while (slp != &nfssvc_sockhead) {
+ if ((slp->ns_flag & (SLP_VALID | SLP_DOREC))
+ == (SLP_VALID | SLP_DOREC)) {
+ slp->ns_flag &= ~SLP_DOREC;
+ slp->ns_sref++;
+ nd->nd_slp = slp;
+ break;
+ }
+ slp = slp->ns_next;
+ }
+ if (slp == &nfssvc_sockhead)
+ nfsd_head.nd_flag &= ~NFSD_CHECKSLP;
+ }
+ if ((slp = nd->nd_slp) == (struct nfssvc_sock *)0)
+ continue;
+ if (slp->ns_flag & SLP_VALID) {
+ if (slp->ns_flag & SLP_DISCONN)
+ nfsrv_zapsock(slp);
+ else if (slp->ns_flag & SLP_NEEDQ) {
+ slp->ns_flag &= ~SLP_NEEDQ;
+ (void) nfs_sndlock(&slp->ns_solock,
+ (struct nfsreq *)0);
+ nfsrv_rcv(slp->ns_so, (caddr_t)slp,
+ M_WAIT);
+ nfs_sndunlock(&slp->ns_solock);
+ }
+ error = nfsrv_dorec(slp, nd);
+ nd->nd_flag |= NFSD_REQINPROG;
+ }
+ } else {
+ error = 0;
+ slp = nd->nd_slp;
+ }
+ if (error || (slp->ns_flag & SLP_VALID) == 0) {
+ nd->nd_slp = (struct nfssvc_sock *)0;
+ nd->nd_flag &= ~NFSD_REQINPROG;
+ nfsrv_slpderef(slp);
+ continue;
+ }
+ splx(s);
+ so = slp->ns_so;
+ sotype = so->so_type;
+ starttime = time;
+ if (so->so_proto->pr_flags & PR_CONNREQUIRED)
+ solockp = &slp->ns_solock;
+ else
+ solockp = (int *)0;
+ /*
+ * nam == nam2 for connectionless protocols such as UDP
+ * nam2 == NULL for connection based protocols to disable
+ * recent request caching.
+ */
+ if (nam2 = nd->nd_nam) {
+ nam = nam2;
+ cacherep = RC_CHECKIT;
+ } else {
+ nam = slp->ns_nam;
+ cacherep = RC_DOIT;
+ }
+
+ /*
+ * Check to see if authorization is needed.
+ */
+ if (nd->nd_flag & NFSD_NEEDAUTH) {
+ static int logauth = 0;
+
+ nd->nd_flag &= ~NFSD_NEEDAUTH;
+ /*
+ * Check for a mapping already installed.
+ */
+ uidp = slp->ns_uidh[NUIDHASH(nd->nd_cr.cr_uid)];
+ while (uidp) {
+ if (uidp->nu_uid == nd->nd_cr.cr_uid)
+ break;
+ uidp = uidp->nu_hnext;
+ }
+ if (!uidp) {
+ nsd->nsd_uid = nd->nd_cr.cr_uid;
+ if (nam2 && logauth++ == 0)
+ log(LOG_WARNING, "Kerberized NFS using UDP\n");
+ nsd->nsd_haddr =
+ mtod(nam, struct sockaddr_in *)->sin_addr.s_addr;
+ nsd->nsd_authlen = nd->nd_authlen;
+ if (copyout(nd->nd_authstr, nsd->nsd_authstr,
+ nd->nd_authlen) == 0 &&
+ copyout((caddr_t)nsd, argp, sizeof (*nsd)) == 0)
+ return (ENEEDAUTH);
+ cacherep = RC_DROPIT;
+ }
+ }
+ if (cacherep == RC_CHECKIT)
+ cacherep = nfsrv_getcache(nam2, nd, &mreq);
+
+ /*
+ * Check for just starting up for NQNFS and send
+ * fake "try again later" replies to the NQNFS clients.
+ */
+ if (notstarted && nqnfsstarttime <= time.tv_sec) {
+ if (modify_flag) {
+ nqnfsstarttime = time.tv_sec + nqsrv_writeslack;
+ modify_flag = 0;
+ } else
+ notstarted = 0;
+ }
+ if (notstarted) {
+ if (nd->nd_nqlflag == NQL_NOVAL)
+ cacherep = RC_DROPIT;
+ else if (nd->nd_procnum != NFSPROC_WRITE) {
+ nd->nd_procnum = NFSPROC_NOOP;
+ nd->nd_repstat = NQNFS_TRYLATER;
+ cacherep = RC_DOIT;
+ } else
+ modify_flag = 1;
+ } else if (nd->nd_flag & NFSD_AUTHFAIL) {
+ nd->nd_flag &= ~NFSD_AUTHFAIL;
+ nd->nd_procnum = NFSPROC_NOOP;
+ nd->nd_repstat = NQNFS_AUTHERR;
+ cacherep = RC_DOIT;
+ }
+
+ switch (cacherep) {
+ case RC_DOIT:
+ error = (*(nfsrv_procs[nd->nd_procnum]))(nd,
+ nd->nd_mrep, nd->nd_md, nd->nd_dpos, &nd->nd_cr,
+ nam, &mreq);
+ if (nd->nd_cr.cr_ref != 1) {
+ printf("nfssvc cref=%d\n", nd->nd_cr.cr_ref);
+ panic("nfssvc cref");
+ }
+ if (error) {
+ if (nd->nd_procnum != NQNFSPROC_VACATED)
+ nfsstats.srv_errs++;
+ if (nam2) {
+ nfsrv_updatecache(nam2, nd, FALSE, mreq);
+ m_freem(nam2);
+ }
+ break;
+ }
+ nfsstats.srvrpccnt[nd->nd_procnum]++;
+ if (nam2)
+ nfsrv_updatecache(nam2, nd, TRUE, mreq);
+ nd->nd_mrep = (struct mbuf *)0;
+ case RC_REPLY:
+ m = mreq;
+ siz = 0;
+ while (m) {
+ siz += m->m_len;
+ m = m->m_next;
+ }
+ if (siz <= 0 || siz > NFS_MAXPACKET) {
+ printf("mbuf siz=%d\n",siz);
+ panic("Bad nfs svc reply");
+ }
+ m = mreq;
+ m->m_pkthdr.len = siz;
+ m->m_pkthdr.rcvif = (struct ifnet *)0;
+ /*
+ * For stream protocols, prepend a Sun RPC
+ * Record Mark.
+ */
+ if (sotype == SOCK_STREAM) {
+ M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
+ *mtod(m, u_long *) = htonl(0x80000000 | siz);
+ }
+ if (solockp)
+ (void) nfs_sndlock(solockp, (struct nfsreq *)0);
+ if (slp->ns_flag & SLP_VALID)
+ error = nfs_send(so, nam2, m, (struct nfsreq *)0);
+ else {
+ error = EPIPE;
+ m_freem(m);
+ }
+ if (nfsrtton)
+ nfsd_rt(&starttime, sotype, nd, nam, cacherep);
+ if (nam2)
+ MFREE(nam2, m);
+ if (nd->nd_mrep)
+ m_freem(nd->nd_mrep);
+ if (error == EPIPE)
+ nfsrv_zapsock(slp);
+ if (solockp)
+ nfs_sndunlock(solockp);
+ if (error == EINTR || error == ERESTART) {
+ nfsrv_slpderef(slp);
+ s = splnet();
+ goto done;
+ }
+ break;
+ case RC_DROPIT:
+ if (nfsrtton)
+ nfsd_rt(&starttime, sotype, nd, nam, cacherep);
+ m_freem(nd->nd_mrep);
+ m_freem(nam2);
+ break;
+ };
+ s = splnet();
+ if (nfsrv_dorec(slp, nd)) {
+ nd->nd_flag &= ~NFSD_REQINPROG;
+ nd->nd_slp = (struct nfssvc_sock *)0;
+ nfsrv_slpderef(slp);
+ }
+ }
+done:
+ remque(nd);
+ splx(s);
+ free((caddr_t)nd, M_NFSD);
+ nsd->nsd_nfsd = (struct nfsd *)0;
+ if (--nfs_numnfsd == 0)
+ nfsrv_init(TRUE); /* Reinitialize everything */
+ return (error);
+}
+
+/*
+ * Asynchronous I/O daemons for client nfs.
+ * They do read-ahead and write-behind operations on the block I/O cache.
+ * Never returns unless it fails or gets killed.
+ */
+int
+nfssvc_iod(p)
+ struct proc *p;
+{
+ register struct buf *bp;
+ register int i, myiod;
+ int error = 0;
+
+ /*
+ * Assign my position or return error if too many already running
+ */
+ myiod = -1;
+ for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
+ if (nfs_asyncdaemon[i] == 0) {
+ nfs_asyncdaemon[i]++;
+ myiod = i;
+ break;
+ }
+ if (myiod == -1)
+ return (EBUSY);
+ nfs_numasync++;
+ /*
+ * Just loop around doin our stuff until SIGKILL
+ */
+ for (;;) {
+ while (nfs_bufq.tqh_first == NULL && error == 0) {
+ nfs_iodwant[myiod] = p;
+ error = tsleep((caddr_t)&nfs_iodwant[myiod],
+ PWAIT | PCATCH, "nfsidl", 0);
+ }
+ while ((bp = nfs_bufq.tqh_first) != NULL) {
+ /* Take one off the front of the list */
+ TAILQ_REMOVE(&nfs_bufq, bp, b_freelist);
+ if (bp->b_flags & B_READ)
+ (void) nfs_doio(bp, bp->b_rcred, (struct proc *)0);
+ else
+ (void) nfs_doio(bp, bp->b_wcred, (struct proc *)0);
+ }
+ if (error) {
+ nfs_asyncdaemon[myiod] = 0;
+ nfs_numasync--;
+ return (error);
+ }
+ }
+}
+
+/*
+ * Shut down a socket associated with an nfssvc_sock structure.
+ * Should be called with the send lock set, if required.
+ * The trick here is to increment the sref at the start, so that the nfsds
+ * will stop using it and clear ns_flag at the end so that it will not be
+ * reassigned during cleanup.
+ */
+void
+nfsrv_zapsock(slp)
+ register struct nfssvc_sock *slp;
+{
+ register struct nfsuid *nuidp, *onuidp;
+ register int i;
+ struct socket *so;
+ struct file *fp;
+ struct mbuf *m;
+
+ slp->ns_flag &= ~SLP_ALLFLAGS;
+ if (fp = slp->ns_fp) {
+ slp->ns_fp = (struct file *)0;
+ so = slp->ns_so;
+ so->so_upcall = NULL;
+ soshutdown(so, 2);
+ closef(fp, (struct proc *)0);
+ if (slp->ns_nam)
+ MFREE(slp->ns_nam, m);
+ m_freem(slp->ns_raw);
+ m_freem(slp->ns_rec);
+ nuidp = slp->ns_lrunext;
+ while (nuidp != (struct nfsuid *)slp) {
+ onuidp = nuidp;
+ nuidp = nuidp->nu_lrunext;
+ free((caddr_t)onuidp, M_NFSUID);
+ }
+ slp->ns_lrunext = slp->ns_lruprev = (struct nfsuid *)slp;
+ for (i = 0; i < NUIDHASHSIZ; i++)
+ slp->ns_uidh[i] = (struct nfsuid *)0;
+ }
+}
+
+/*
+ * Get an authorization string for the uid by having the mount_nfs sitting
+ * on this mount point porpous out of the kernel and do it.
+ */
+int
+nfs_getauth(nmp, rep, cred, auth_type, auth_str, auth_len)
+ register struct nfsmount *nmp;
+ struct nfsreq *rep;
+ struct ucred *cred;
+ int *auth_type;
+ char **auth_str;
+ int *auth_len;
+{
+ int error = 0;
+
+ while ((nmp->nm_flag & NFSMNT_WAITAUTH) == 0) {
+ nmp->nm_flag |= NFSMNT_WANTAUTH;
+ (void) tsleep((caddr_t)&nmp->nm_authtype, PSOCK,
+ "nfsauth1", 2 * hz);
+ if (error = nfs_sigintr(nmp, rep, rep->r_procp)) {
+ nmp->nm_flag &= ~NFSMNT_WANTAUTH;
+ return (error);
+ }
+ }
+ nmp->nm_flag &= ~(NFSMNT_WAITAUTH | NFSMNT_WANTAUTH);
+ nmp->nm_authstr = *auth_str = (char *)malloc(RPCAUTH_MAXSIZ, M_TEMP, M_WAITOK);
+ nmp->nm_authuid = cred->cr_uid;
+ wakeup((caddr_t)&nmp->nm_authstr);
+
+ /*
+ * And wait for mount_nfs to do its stuff.
+ */
+ while ((nmp->nm_flag & NFSMNT_HASAUTH) == 0 && error == 0) {
+ (void) tsleep((caddr_t)&nmp->nm_authlen, PSOCK,
+ "nfsauth2", 2 * hz);
+ error = nfs_sigintr(nmp, rep, rep->r_procp);
+ }
+ if (nmp->nm_flag & NFSMNT_AUTHERR) {
+ nmp->nm_flag &= ~NFSMNT_AUTHERR;
+ error = EAUTH;
+ }
+ if (error)
+ free((caddr_t)*auth_str, M_TEMP);
+ else {
+ *auth_type = nmp->nm_authtype;
+ *auth_len = nmp->nm_authlen;
+ }
+ nmp->nm_flag &= ~NFSMNT_HASAUTH;
+ nmp->nm_flag |= NFSMNT_WAITAUTH;
+ if (nmp->nm_flag & NFSMNT_WANTAUTH) {
+ nmp->nm_flag &= ~NFSMNT_WANTAUTH;
+ wakeup((caddr_t)&nmp->nm_authtype);
+ }
+ return (error);
+}
+
+/*
+ * Derefence a server socket structure. If it has no more references and
+ * is no longer valid, you can throw it away.
+ */
+void
+nfsrv_slpderef(slp)
+ register struct nfssvc_sock *slp;
+{
+ if (--(slp->ns_sref) == 0 && (slp->ns_flag & SLP_VALID) == 0) {
+ slp->ns_prev->ns_next = slp->ns_next;
+ slp->ns_next->ns_prev = slp->ns_prev;
+ free((caddr_t)slp, M_NFSSVC);
+ }
+}
+
+/*
+ * Initialize the data structures for the server.
+ * Handshake with any new nfsds starting up to avoid any chance of
+ * corruption.
+ */
+void
+nfsrv_init(terminating)
+ int terminating;
+{
+ register struct nfssvc_sock *slp;
+ struct nfssvc_sock *oslp;
+
+ if (nfssvc_sockhead.ns_flag & SLP_INIT)
+ panic("nfsd init");
+ nfssvc_sockhead.ns_flag |= SLP_INIT;
+ if (terminating) {
+ slp = nfssvc_sockhead.ns_next;
+ while (slp != &nfssvc_sockhead) {
+ if (slp->ns_flag & SLP_VALID)
+ nfsrv_zapsock(slp);
+ slp->ns_next->ns_prev = slp->ns_prev;
+ slp->ns_prev->ns_next = slp->ns_next;
+ oslp = slp;
+ slp = slp->ns_next;
+ free((caddr_t)oslp, M_NFSSVC);
+ }
+ nfsrv_cleancache(); /* And clear out server cache */
+ }
+ nfs_udpsock = (struct nfssvc_sock *)
+ malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
+ bzero((caddr_t)nfs_udpsock, sizeof (struct nfssvc_sock));
+ nfs_cltpsock = (struct nfssvc_sock *)
+ malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
+ bzero((caddr_t)nfs_cltpsock, sizeof (struct nfssvc_sock));
+ nfssvc_sockhead.ns_next = nfs_udpsock;
+ nfs_udpsock->ns_next = nfs_cltpsock;
+ nfs_cltpsock->ns_next = &nfssvc_sockhead;
+ nfssvc_sockhead.ns_prev = nfs_cltpsock;
+ nfs_cltpsock->ns_prev = nfs_udpsock;
+ nfs_udpsock->ns_prev = &nfssvc_sockhead;
+ nfs_udpsock->ns_lrunext = nfs_udpsock->ns_lruprev =
+ (struct nfsuid *)nfs_udpsock;
+ nfs_cltpsock->ns_lrunext = nfs_cltpsock->ns_lruprev =
+ (struct nfsuid *)nfs_cltpsock;
+ nfsd_head.nd_next = nfsd_head.nd_prev = &nfsd_head;
+ nfsd_head.nd_flag = 0;
+ nfssvc_sockhead.ns_flag &= ~SLP_INIT;
+ if (nfssvc_sockhead.ns_flag & SLP_WANTINIT) {
+ nfssvc_sockhead.ns_flag &= ~SLP_WANTINIT;
+ wakeup((caddr_t)&nfssvc_sockhead);
+ }
+}
+
+/*
+ * Add entries to the server monitor log.
+ */
+static void
+nfsd_rt(startp, sotype, nd, nam, cacherep)
+ struct timeval *startp;
+ int sotype;
+ register struct nfsd *nd;
+ struct mbuf *nam;
+ int cacherep;
+{
+ register struct drt *rt;
+
+ rt = &nfsdrt.drt[nfsdrt.pos];
+ if (cacherep == RC_DOIT)
+ rt->flag = 0;
+ else if (cacherep == RC_REPLY)
+ rt->flag = DRT_CACHEREPLY;
+ else
+ rt->flag = DRT_CACHEDROP;
+ if (sotype == SOCK_STREAM)
+ rt->flag |= DRT_TCP;
+ if (nd->nd_nqlflag != NQL_NOVAL)
+ rt->flag |= DRT_NQNFS;
+ rt->proc = nd->nd_procnum;
+ if (mtod(nam, struct sockaddr *)->sa_family == AF_INET)
+ rt->ipadr = mtod(nam, struct sockaddr_in *)->sin_addr.s_addr;
+ else
+ rt->ipadr = INADDR_ANY;
+ rt->resptime = ((time.tv_sec - startp->tv_sec) * 1000000) +
+ (time.tv_usec - startp->tv_usec);
+ rt->tstamp = time;
+ nfsdrt.pos = (nfsdrt.pos + 1) % NFSRTTLOGSIZ;
+}
diff --git a/sys/nfsserver/nfsm_subs.h b/sys/nfsserver/nfsm_subs.h
new file mode 100644
index 0000000..879db36
--- /dev/null
+++ b/sys/nfsserver/nfsm_subs.h
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsm_subs.h 8.1 (Berkeley) 6/16/93
+ */
+
+/*
+ * These macros do strange and peculiar things to mbuf chains for
+ * the assistance of the nfs code. To attempt to use them for any
+ * other purpose will be dangerous. (they make weird assumptions)
+ */
+
+/*
+ * First define what the actual subs. return
+ */
+extern struct mbuf *nfsm_reqh();
+
+#define M_HASCL(m) ((m)->m_flags & M_EXT)
+#define NFSMINOFF(m) \
+ if (M_HASCL(m)) \
+ (m)->m_data = (m)->m_ext.ext_buf; \
+ else if ((m)->m_flags & M_PKTHDR) \
+ (m)->m_data = (m)->m_pktdat; \
+ else \
+ (m)->m_data = (m)->m_dat
+#define NFSMADV(m, s) (m)->m_data += (s)
+#define NFSMSIZ(m) ((M_HASCL(m))?MCLBYTES: \
+ (((m)->m_flags & M_PKTHDR)?MHLEN:MLEN))
+
+/*
+ * Now for the macros that do the simple stuff and call the functions
+ * for the hard stuff.
+ * These macros use several vars. declared in nfsm_reqhead and these
+ * vars. must not be used elsewhere unless you are careful not to corrupt
+ * them. The vars. starting with pN and tN (N=1,2,3,..) are temporaries
+ * that may be used so long as the value is not expected to retained
+ * after a macro.
+ * I know, this is kind of dorkey, but it makes the actual op functions
+ * fairly clean and deals with the mess caused by the xdr discriminating
+ * unions.
+ */
+
+#define nfsm_build(a,c,s) \
+ { if ((s) > M_TRAILINGSPACE(mb)) { \
+ MGET(mb2, M_WAIT, MT_DATA); \
+ if ((s) > MLEN) \
+ panic("build > MLEN"); \
+ mb->m_next = mb2; \
+ mb = mb2; \
+ mb->m_len = 0; \
+ bpos = mtod(mb, caddr_t); \
+ } \
+ (a) = (c)(bpos); \
+ mb->m_len += (s); \
+ bpos += (s); }
+
+#define nfsm_dissect(a,c,s) \
+ { t1 = mtod(md, caddr_t)+md->m_len-dpos; \
+ if (t1 >= (s)) { \
+ (a) = (c)(dpos); \
+ dpos += (s); \
+ } else if (error = nfsm_disct(&md, &dpos, (s), t1, &cp2)) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ } else { \
+ (a) = (c)cp2; \
+ } }
+
+#define nfsm_fhtom(v) \
+ nfsm_build(cp,caddr_t,NFSX_FH); \
+ bcopy((caddr_t)&(VTONFS(v)->n_fh), cp, NFSX_FH)
+
+#define nfsm_srvfhtom(f) \
+ nfsm_build(cp,caddr_t,NFSX_FH); \
+ bcopy((caddr_t)(f), cp, NFSX_FH)
+
+#define nfsm_mtofh(d,v) \
+ { struct nfsnode *np; nfsv2fh_t *fhp; \
+ nfsm_dissect(fhp,nfsv2fh_t *,NFSX_FH); \
+ if (error = nfs_nget((d)->v_mount, fhp, &np)) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ } \
+ (v) = NFSTOV(np); \
+ nfsm_loadattr(v, (struct vattr *)0); \
+ }
+
+#define nfsm_loadattr(v,a) \
+ { struct vnode *tvp = (v); \
+ if (error = nfs_loadattrcache(&tvp, &md, &dpos, (a))) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ } \
+ (v) = tvp; }
+
+#define nfsm_strsiz(s,m) \
+ { nfsm_dissect(tl,u_long *,NFSX_UNSIGNED); \
+ if (((s) = fxdr_unsigned(long,*tl)) > (m)) { \
+ m_freem(mrep); \
+ error = EBADRPC; \
+ goto nfsmout; \
+ } }
+
+#define nfsm_srvstrsiz(s,m) \
+ { nfsm_dissect(tl,u_long *,NFSX_UNSIGNED); \
+ if (((s) = fxdr_unsigned(long,*tl)) > (m) || (s) <= 0) { \
+ error = EBADRPC; \
+ nfsm_reply(0); \
+ } }
+
+#define nfsm_mtouio(p,s) \
+ if ((s) > 0 && \
+ (error = nfsm_mbuftouio(&md,(p),(s),&dpos))) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ }
+
+#define nfsm_uiotom(p,s) \
+ if (error = nfsm_uiotombuf((p),&mb,(s),&bpos)) { \
+ m_freem(mreq); \
+ goto nfsmout; \
+ }
+
+#define nfsm_reqhead(v,a,s) \
+ mb = mreq = nfsm_reqh((v),(a),(s),&bpos)
+
+#define nfsm_reqdone m_freem(mrep); \
+ nfsmout:
+
+#define nfsm_rndup(a) (((a)+3)&(~0x3))
+
+#define nfsm_request(v, t, p, c) \
+ if (error = nfs_request((v), mreq, (t), (p), \
+ (c), &mrep, &md, &dpos)) \
+ goto nfsmout
+
+#define nfsm_strtom(a,s,m) \
+ if ((s) > (m)) { \
+ m_freem(mreq); \
+ error = ENAMETOOLONG; \
+ goto nfsmout; \
+ } \
+ t2 = nfsm_rndup(s)+NFSX_UNSIGNED; \
+ if (t2 <= M_TRAILINGSPACE(mb)) { \
+ nfsm_build(tl,u_long *,t2); \
+ *tl++ = txdr_unsigned(s); \
+ *(tl+((t2>>2)-2)) = 0; \
+ bcopy((caddr_t)(a), (caddr_t)tl, (s)); \
+ } else if (error = nfsm_strtmbuf(&mb, &bpos, (a), (s))) { \
+ m_freem(mreq); \
+ goto nfsmout; \
+ }
+
+#define nfsm_srvdone \
+ nfsmout: \
+ return(error)
+
+#define nfsm_reply(s) \
+ { \
+ nfsd->nd_repstat = error; \
+ if (error) \
+ (void) nfs_rephead(0, nfsd, error, cache, &frev, \
+ mrq, &mb, &bpos); \
+ else \
+ (void) nfs_rephead((s), nfsd, error, cache, &frev, \
+ mrq, &mb, &bpos); \
+ m_freem(mrep); \
+ mreq = *mrq; \
+ if (error) \
+ return(0); \
+ }
+
+#define nfsm_adv(s) \
+ t1 = mtod(md, caddr_t)+md->m_len-dpos; \
+ if (t1 >= (s)) { \
+ dpos += (s); \
+ } else if (error = nfs_adv(&md, &dpos, (s), t1)) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ }
+
+#define nfsm_srvmtofh(f) \
+ nfsm_dissect(tl, u_long *, NFSX_FH); \
+ bcopy((caddr_t)tl, (caddr_t)f, NFSX_FH)
+
+#define nfsm_clget \
+ if (bp >= be) { \
+ if (mp == mb) \
+ mp->m_len += bp-bpos; \
+ MGET(mp, M_WAIT, MT_DATA); \
+ MCLGET(mp, M_WAIT); \
+ mp->m_len = NFSMSIZ(mp); \
+ mp2->m_next = mp; \
+ mp2 = mp; \
+ bp = mtod(mp, caddr_t); \
+ be = bp+mp->m_len; \
+ } \
+ tl = (u_long *)bp
+
+#define nfsm_srvfillattr \
+ fp->fa_type = vtonfs_type(vap->va_type); \
+ fp->fa_mode = vtonfs_mode(vap->va_type, vap->va_mode); \
+ fp->fa_nlink = txdr_unsigned(vap->va_nlink); \
+ fp->fa_uid = txdr_unsigned(vap->va_uid); \
+ fp->fa_gid = txdr_unsigned(vap->va_gid); \
+ if (nfsd->nd_nqlflag == NQL_NOVAL) { \
+ fp->fa_nfsblocksize = txdr_unsigned(vap->va_blocksize); \
+ if (vap->va_type == VFIFO) \
+ fp->fa_nfsrdev = 0xffffffff; \
+ else \
+ fp->fa_nfsrdev = txdr_unsigned(vap->va_rdev); \
+ fp->fa_nfsfsid = txdr_unsigned(vap->va_fsid); \
+ fp->fa_nfsfileid = txdr_unsigned(vap->va_fileid); \
+ fp->fa_nfssize = txdr_unsigned(vap->va_size); \
+ fp->fa_nfsblocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE); \
+ txdr_nfstime(&vap->va_atime, &fp->fa_nfsatime); \
+ txdr_nfstime(&vap->va_mtime, &fp->fa_nfsmtime); \
+ fp->fa_nfsctime.nfs_sec = txdr_unsigned(vap->va_ctime.ts_sec); \
+ fp->fa_nfsctime.nfs_usec = txdr_unsigned(vap->va_gen); \
+ } else { \
+ fp->fa_nqblocksize = txdr_unsigned(vap->va_blocksize); \
+ if (vap->va_type == VFIFO) \
+ fp->fa_nqrdev = 0xffffffff; \
+ else \
+ fp->fa_nqrdev = txdr_unsigned(vap->va_rdev); \
+ fp->fa_nqfsid = txdr_unsigned(vap->va_fsid); \
+ fp->fa_nqfileid = txdr_unsigned(vap->va_fileid); \
+ txdr_hyper(&vap->va_size, &fp->fa_nqsize); \
+ txdr_hyper(&vap->va_bytes, &fp->fa_nqbytes); \
+ txdr_nqtime(&vap->va_atime, &fp->fa_nqatime); \
+ txdr_nqtime(&vap->va_mtime, &fp->fa_nqmtime); \
+ txdr_nqtime(&vap->va_ctime, &fp->fa_nqctime); \
+ fp->fa_nqflags = txdr_unsigned(vap->va_flags); \
+ fp->fa_nqgen = txdr_unsigned(vap->va_gen); \
+ txdr_hyper(&vap->va_filerev, &fp->fa_nqfilerev); \
+ }
+
diff --git a/sys/nfsserver/nfsrvcache.h b/sys/nfsserver/nfsrvcache.h
new file mode 100644
index 0000000..26da2c2
--- /dev/null
+++ b/sys/nfsserver/nfsrvcache.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsrvcache.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Definitions for the server recent request cache
+ */
+
+#define NFSRVCACHESIZ 256
+
+struct nfsrvcache {
+ struct nfsrvcache *rc_forw; /* Hash chain links */
+ struct nfsrvcache **rc_back; /* Hash chain links */
+ struct nfsrvcache *rc_next; /* Lru list */
+ struct nfsrvcache **rc_prev; /* Lru list */
+ u_long rc_xid; /* rpc id number */
+ union {
+ struct mbuf *ru_repmb; /* Reply mbuf list OR */
+ int ru_repstat; /* Reply status */
+ } rc_un;
+ union nethostaddr rc_haddr; /* Host address */
+ short rc_proc; /* rpc proc number */
+ u_char rc_state; /* Current state of request */
+ u_char rc_flag; /* Flag bits */
+};
+
+#define rc_reply rc_un.ru_repmb
+#define rc_status rc_un.ru_repstat
+#define rc_inetaddr rc_haddr.had_inetaddr
+#define rc_nam rc_haddr.had_nam
+
+/* Cache entry states */
+#define RC_UNUSED 0
+#define RC_INPROG 1
+#define RC_DONE 2
+
+/* Return values */
+#define RC_DROPIT 0
+#define RC_REPLY 1
+#define RC_DOIT 2
+#define RC_CHECKIT 3
+
+/* Flag bits */
+#define RC_LOCKED 0x01
+#define RC_WANTED 0x02
+#define RC_REPSTATUS 0x04
+#define RC_REPMBUF 0x08
+#define RC_NQNFS 0x10
+#define RC_INETADDR 0x20
+#define RC_NAM 0x40
diff --git a/sys/nfsserver/nfsrvstats.h b/sys/nfsserver/nfsrvstats.h
new file mode 100644
index 0000000..261fd42
--- /dev/null
+++ b/sys/nfsserver/nfsrvstats.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Tunable constants for nfs
+ */
+
+#define NFS_MAXIOVEC 34
+#define NFS_HZ 25 /* Ticks per second for NFS timeouts */
+#define NFS_TIMEO (1*NFS_HZ) /* Default timeout = 1 second */
+#define NFS_MINTIMEO (1*NFS_HZ) /* Min timeout to use */
+#define NFS_MAXTIMEO (60*NFS_HZ) /* Max timeout to backoff to */
+#define NFS_MINIDEMTIMEO (5*NFS_HZ) /* Min timeout for non-idempotent ops*/
+#define NFS_MAXREXMIT 100 /* Stop counting after this many */
+#define NFS_MAXWINDOW 1024 /* Max number of outstanding requests */
+#define NFS_RETRANS 10 /* Num of retrans for soft mounts */
+#define NFS_MAXGRPS 16 /* Max. size of groups list */
+#define NFS_MINATTRTIMO 5 /* Attribute cache timeout in sec */
+#define NFS_MAXATTRTIMO 60
+#define NFS_WSIZE 8192 /* Def. write data size <= 8192 */
+#define NFS_RSIZE 8192 /* Def. read data size <= 8192 */
+#define NFS_DEFRAHEAD 1 /* Def. read ahead # blocks */
+#define NFS_MAXRAHEAD 4 /* Max. read ahead # blocks */
+#define NFS_MAXREADDIR NFS_MAXDATA /* Max. size of directory read */
+#define NFS_MAXUIDHASH 64 /* Max. # of hashed uid entries/mp */
+#define NFS_MAXASYNCDAEMON 20 /* Max. number async_daemons runable */
+#define NFS_DIRBLKSIZ 1024 /* Size of an NFS directory block */
+#define NMOD(a) ((a) % nfs_asyncdaemons)
+
+/*
+ * Set the attribute timeout based on how recently the file has been modified.
+ */
+#define NFS_ATTRTIMEO(np) \
+ ((((np)->n_flag & NMODIFIED) || \
+ (time.tv_sec - (np)->n_mtime) / 10 < NFS_MINATTRTIMO) ? NFS_MINATTRTIMO : \
+ ((time.tv_sec - (np)->n_mtime) / 10 > NFS_MAXATTRTIMO ? NFS_MAXATTRTIMO : \
+ (time.tv_sec - (np)->n_mtime) / 10))
+
+/*
+ * Structures for the nfssvc(2) syscall. Not that anyone but nfsd and mount_nfs
+ * should ever try and use it.
+ */
+struct nfsd_args {
+ int sock; /* Socket to serve */
+ caddr_t name; /* Client address for connection based sockets */
+ int namelen; /* Length of name */
+};
+
+struct nfsd_srvargs {
+ struct nfsd *nsd_nfsd; /* Pointer to in kernel nfsd struct */
+ uid_t nsd_uid; /* Effective uid mapped to cred */
+ u_long nsd_haddr; /* Ip address of client */
+ struct ucred nsd_cr; /* Cred. uid maps to */
+ int nsd_authlen; /* Length of auth string (ret) */
+ char *nsd_authstr; /* Auth string (ret) */
+};
+
+struct nfsd_cargs {
+ char *ncd_dirp; /* Mount dir path */
+ uid_t ncd_authuid; /* Effective uid */
+ int ncd_authtype; /* Type of authenticator */
+ int ncd_authlen; /* Length of authenticator string */
+ char *ncd_authstr; /* Authenticator string */
+};
+
+/*
+ * Stats structure
+ */
+struct nfsstats {
+ int attrcache_hits;
+ int attrcache_misses;
+ int lookupcache_hits;
+ int lookupcache_misses;
+ int direofcache_hits;
+ int direofcache_misses;
+ int biocache_reads;
+ int read_bios;
+ int read_physios;
+ int biocache_writes;
+ int write_bios;
+ int write_physios;
+ int biocache_readlinks;
+ int readlink_bios;
+ int biocache_readdirs;
+ int readdir_bios;
+ int rpccnt[NFS_NPROCS];
+ int rpcretries;
+ int srvrpccnt[NFS_NPROCS];
+ int srvrpc_errs;
+ int srv_errs;
+ int rpcrequests;
+ int rpctimeouts;
+ int rpcunexpected;
+ int rpcinvalid;
+ int srvcache_inproghits;
+ int srvcache_idemdonehits;
+ int srvcache_nonidemdonehits;
+ int srvcache_misses;
+ int srvnqnfs_leases;
+ int srvnqnfs_maxleases;
+ int srvnqnfs_getleases;
+};
+
+/*
+ * Flags for nfssvc() system call.
+ */
+#define NFSSVC_BIOD 0x002
+#define NFSSVC_NFSD 0x004
+#define NFSSVC_ADDSOCK 0x008
+#define NFSSVC_AUTHIN 0x010
+#define NFSSVC_GOTAUTH 0x040
+#define NFSSVC_AUTHINFAIL 0x080
+#define NFSSVC_MNTD 0x100
+
+/*
+ * The set of signals the interrupt an I/O in progress for NFSMNT_INT mounts.
+ * What should be in this set is open to debate, but I believe that since
+ * I/O system calls on ufs are never interrupted by signals the set should
+ * be minimal. My reasoning is that many current programs that use signals
+ * such as SIGALRM will not expect file I/O system calls to be interrupted
+ * by them and break.
+ */
+#ifdef KERNEL
+#define NFSINT_SIGMASK (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \
+ sigmask(SIGHUP)|sigmask(SIGQUIT))
+
+/*
+ * Socket errors ignored for connectionless sockets??
+ * For now, ignore them all
+ */
+#define NFSIGNORE_SOERROR(s, e) \
+ ((e) != EINTR && (e) != ERESTART && (e) != EWOULDBLOCK && \
+ ((s) & PR_CONNREQUIRED) == 0)
+
+/*
+ * Nfs outstanding request list element
+ */
+struct nfsreq {
+ struct nfsreq *r_next;
+ struct nfsreq *r_prev;
+ struct mbuf *r_mreq;
+ struct mbuf *r_mrep;
+ struct mbuf *r_md;
+ caddr_t r_dpos;
+ struct nfsmount *r_nmp;
+ struct vnode *r_vp;
+ u_long r_xid;
+ int r_flags; /* flags on request, see below */
+ int r_retry; /* max retransmission count */
+ int r_rexmit; /* current retrans count */
+ int r_timer; /* tick counter on reply */
+ int r_procnum; /* NFS procedure number */
+ int r_rtt; /* RTT for rpc */
+ struct proc *r_procp; /* Proc that did I/O system call */
+};
+
+/* Flag values for r_flags */
+#define R_TIMING 0x01 /* timing request (in mntp) */
+#define R_SENT 0x02 /* request has been sent */
+#define R_SOFTTERM 0x04 /* soft mnt, too many retries */
+#define R_INTR 0x08 /* intr mnt, signal pending */
+#define R_SOCKERR 0x10 /* Fatal error on socket */
+#define R_TPRINTFMSG 0x20 /* Did a tprintf msg. */
+#define R_MUSTRESEND 0x40 /* Must resend request */
+#define R_GETONEREP 0x80 /* Probe for one reply only */
+
+struct nfsstats nfsstats;
+
+/*
+ * A list of nfssvc_sock structures is maintained with all the sockets
+ * that require service by the nfsd.
+ * The nfsuid structs hang off of the nfssvc_sock structs in both lru
+ * and uid hash lists.
+ */
+#define NUIDHASHSIZ 32
+#define NUIDHASH(uid) ((uid) & (NUIDHASHSIZ - 1))
+
+/*
+ * Network address hash list element
+ */
+union nethostaddr {
+ u_long had_inetaddr;
+ struct mbuf *had_nam;
+};
+
+struct nfsuid {
+ struct nfsuid *nu_lrunext; /* MUST be first */
+ struct nfsuid *nu_lruprev;
+ struct nfsuid *nu_hnext;
+ struct nfsuid *nu_hprev;
+ int nu_flag; /* Flags */
+ uid_t nu_uid; /* Uid mapped by this entry */
+ union nethostaddr nu_haddr; /* Host addr. for dgram sockets */
+ struct ucred nu_cr; /* Cred uid mapped to */
+};
+
+#define nu_inetaddr nu_haddr.had_inetaddr
+#define nu_nam nu_haddr.had_nam
+/* Bits for nu_flag */
+#define NU_INETADDR 0x1
+
+struct nfssvc_sock {
+ struct nfsuid *ns_lrunext; /* MUST be first */
+ struct nfsuid *ns_lruprev;
+ struct nfssvc_sock *ns_next;
+ struct nfssvc_sock *ns_prev;
+ int ns_flag;
+ u_long ns_sref;
+ struct file *ns_fp;
+ struct socket *ns_so;
+ int ns_solock;
+ struct mbuf *ns_nam;
+ int ns_cc;
+ struct mbuf *ns_raw;
+ struct mbuf *ns_rawend;
+ int ns_reclen;
+ struct mbuf *ns_rec;
+ struct mbuf *ns_recend;
+ int ns_numuids;
+ struct nfsuid *ns_uidh[NUIDHASHSIZ];
+};
+
+/* Bits for "ns_flag" */
+#define SLP_VALID 0x01
+#define SLP_DOREC 0x02
+#define SLP_NEEDQ 0x04
+#define SLP_DISCONN 0x08
+#define SLP_GETSTREAM 0x10
+#define SLP_INIT 0x20
+#define SLP_WANTINIT 0x40
+
+#define SLP_ALLFLAGS 0xff
+
+/*
+ * One of these structures is allocated for each nfsd.
+ */
+struct nfsd {
+ struct nfsd *nd_next; /* Must be first */
+ struct nfsd *nd_prev;
+ int nd_flag; /* NFSD_ flags */
+ struct nfssvc_sock *nd_slp; /* Current socket */
+ struct mbuf *nd_nam; /* Client addr for datagram req. */
+ struct mbuf *nd_mrep; /* Req. mbuf list */
+ struct mbuf *nd_md;
+ caddr_t nd_dpos; /* Position in list */
+ int nd_procnum; /* RPC procedure number */
+ u_long nd_retxid; /* RPC xid */
+ int nd_repstat; /* Reply status value */
+ struct ucred nd_cr; /* Credentials for req. */
+ int nd_nqlflag; /* Leasing flag */
+ int nd_duration; /* Lease duration */
+ int nd_authlen; /* Authenticator len */
+ u_char nd_authstr[RPCAUTH_MAXSIZ]; /* Authenticator data */
+ struct proc *nd_procp; /* Proc ptr */
+};
+
+#define NFSD_WAITING 0x01
+#define NFSD_CHECKSLP 0x02
+#define NFSD_REQINPROG 0x04
+#define NFSD_NEEDAUTH 0x08
+#define NFSD_AUTHFAIL 0x10
+#endif /* KERNEL */
diff --git a/sys/powerpc/include/_limits.h b/sys/powerpc/include/_limits.h
new file mode 100644
index 0000000..e507313
--- /dev/null
+++ b/sys/powerpc/include/_limits.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)limits.h 8.3 (Berkeley) 1/4/94
+ */
+
+#ifndef _MACHINE_LIMITS_H_
+#define _MACHINE_LIMITS_H_ 1
+
+#define CHAR_BIT 8 /* number of bits in a char */
+#define MB_LEN_MAX 6 /* Allow 31 bit UTF2 */
+
+
+#define CLK_TCK 128 /* ticks per second */
+
+/*
+ * According to ANSI (section 2.2.4.2), the values below must be usable by
+ * #if preprocessing directives. Additionally, the expression must have the
+ * same type as would an expression that is an object of the corresponding
+ * type converted according to the integral promotions. The subtraction for
+ * INT_MIN and LONG_MIN is so the value is not unsigned; 2147483648 is an
+ * unsigned int for 32-bit two's complement ANSI compilers (section 3.1.3.2).
+ * These numbers work for pcc as well. The UINT_MAX and ULONG_MAX values
+ * are written as hex so that GCC will be quiet about large integer constants.
+ */
+#define SCHAR_MAX 127 /* min value for a signed char */
+#define SCHAR_MIN (-128) /* max value for a signed char */
+
+#define UCHAR_MAX 255 /* max value for an unsigned char */
+#define CHAR_MAX 127 /* max value for a char */
+#define CHAR_MIN (-128) /* min value for a char */
+
+#define USHRT_MAX 65535 /* max value for an unsigned short */
+#define SHRT_MAX 32767 /* max value for a short */
+#define SHRT_MIN (-32768) /* min value for a short */
+
+#define UINT_MAX 0xffffffff /* max value for an unsigned int */
+#define INT_MAX 2147483647 /* max value for an int */
+#define INT_MIN (-2147483647-1) /* min value for an int */
+
+#define ULONG_MAX 0xffffffff /* max value for an unsigned long */
+#define LONG_MAX 2147483647 /* max value for a long */
+#define LONG_MIN (-2147483647-1) /* min value for a long */
+
+#if !defined(_ANSI_SOURCE)
+#define SSIZE_MAX INT_MAX /* max value for a ssize_t */
+
+#if !defined(_POSIX_SOURCE)
+#define SIZE_T_MAX UINT_MAX /* max value for a size_t */
+
+/* GCC requires that quad constants be written as expressions. */
+#define UQUAD_MAX ((u_quad_t)0-1) /* max value for a uquad_t */
+ /* max value for a quad_t */
+#define QUAD_MAX ((quad_t)(UQUAD_MAX >> 1))
+#define QUAD_MIN (-QUAD_MAX-1) /* min value for a quad_t */
+
+#endif /* !_POSIX_SOURCE */
+#endif /* !_ANSI_SOURCE */
+
+#endif /* _MACHINE_LIMITS_H_ */
diff --git a/sys/powerpc/include/limits.h b/sys/powerpc/include/limits.h
new file mode 100644
index 0000000..e507313
--- /dev/null
+++ b/sys/powerpc/include/limits.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)limits.h 8.3 (Berkeley) 1/4/94
+ */
+
+#ifndef _MACHINE_LIMITS_H_
+#define _MACHINE_LIMITS_H_ 1
+
+#define CHAR_BIT 8 /* number of bits in a char */
+#define MB_LEN_MAX 6 /* Allow 31 bit UTF2 */
+
+
+#define CLK_TCK 128 /* ticks per second */
+
+/*
+ * According to ANSI (section 2.2.4.2), the values below must be usable by
+ * #if preprocessing directives. Additionally, the expression must have the
+ * same type as would an expression that is an object of the corresponding
+ * type converted according to the integral promotions. The subtraction for
+ * INT_MIN and LONG_MIN is so the value is not unsigned; 2147483648 is an
+ * unsigned int for 32-bit two's complement ANSI compilers (section 3.1.3.2).
+ * These numbers work for pcc as well. The UINT_MAX and ULONG_MAX values
+ * are written as hex so that GCC will be quiet about large integer constants.
+ */
+#define SCHAR_MAX 127 /* min value for a signed char */
+#define SCHAR_MIN (-128) /* max value for a signed char */
+
+#define UCHAR_MAX 255 /* max value for an unsigned char */
+#define CHAR_MAX 127 /* max value for a char */
+#define CHAR_MIN (-128) /* min value for a char */
+
+#define USHRT_MAX 65535 /* max value for an unsigned short */
+#define SHRT_MAX 32767 /* max value for a short */
+#define SHRT_MIN (-32768) /* min value for a short */
+
+#define UINT_MAX 0xffffffff /* max value for an unsigned int */
+#define INT_MAX 2147483647 /* max value for an int */
+#define INT_MIN (-2147483647-1) /* min value for an int */
+
+#define ULONG_MAX 0xffffffff /* max value for an unsigned long */
+#define LONG_MAX 2147483647 /* max value for a long */
+#define LONG_MIN (-2147483647-1) /* min value for a long */
+
+#if !defined(_ANSI_SOURCE)
+#define SSIZE_MAX INT_MAX /* max value for a ssize_t */
+
+#if !defined(_POSIX_SOURCE)
+#define SIZE_T_MAX UINT_MAX /* max value for a size_t */
+
+/* GCC requires that quad constants be written as expressions. */
+#define UQUAD_MAX ((u_quad_t)0-1) /* max value for a uquad_t */
+ /* max value for a quad_t */
+#define QUAD_MAX ((quad_t)(UQUAD_MAX >> 1))
+#define QUAD_MIN (-QUAD_MAX-1) /* min value for a quad_t */
+
+#endif /* !_POSIX_SOURCE */
+#endif /* !_ANSI_SOURCE */
+
+#endif /* _MACHINE_LIMITS_H_ */
diff --git a/sys/powerpc/include/ptrace.h b/sys/powerpc/include/ptrace.h
new file mode 100644
index 0000000..bfcc55f
--- /dev/null
+++ b/sys/powerpc/include/ptrace.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ptrace.h 8.1 (Berkeley) 6/11/93
+ */
+
+/*
+ * Machine dependent trace commands.
+ *
+ * None for the i386 at this time.
+ */
diff --git a/sys/scsi/README b/sys/scsi/README
new file mode 100644
index 0000000..b110930
--- /dev/null
+++ b/sys/scsi/README
@@ -0,0 +1,196 @@
+This release consists of the following files
+(relative to the base of the source tree )
+
+share/man/man4/scsi.4 <-useful general info
+share/man/man4/uk.4
+share/man/man4/su.4
+share/man/man4/ch.4
+share/man/man4/cd.4
+share/man/man4/sd.4
+share/man/man4/st.4 <--READ THIS IF YOU USE TAPES!
+sbin/scsi/procargs.c
+sbin/scsi/scsi.c
+sbin/scsi/scsi.1
+sbin/scsi/Makefile
+sbin/st/Makefile
+sbin/st/st.1
+sbin/st/st.c
+sys/sys/chio.h
+sys/sys/cdio.h
+sys/sys/mtio.h
+sys/sys/scsiio.h
+sys/i386/conf/EXAMPLE
+sys/i386/isa/ultra14f.c <-runs 14f and 34f
+sys/i386/isa/ultra_all.c.beta <-beta version, runs 14f,24f and 34f
+sys/i386/isa/bt742a.c
+sys/i386/isa/aha1742.c
+sys/i386/isa/aha1542.c
+sys/scsi/syspatches
+sys/scsi/syspatches/conf.c
+sys/scsi/syspatches/user_scsi.diffs
+sys/scsi/syspatches/MAKEDEV.diff
+sys/scsi/syspatches/isa.c.patch
+sys/scsi/syspatches/README
+sys/scsi/uk.c
+sys/scsi/su.c
+sys/scsi/st.c
+sys/scsi/sd.c
+sys/scsi/ch.c
+sys/scsi/cd.c
+sys/scsi/scsi_ioctl.c
+sys/scsi/scsi_base.c
+sys/scsi/scsiconf.c
+sys/scsi/scsi_tape.h
+sys/scsi/scsi_disk.h
+sys/scsi/scsi_changer.h
+sys/scsi/scsi_cd.h
+sys/scsi/scsi_all.h
+sys/scsi/scsi_debug.h
+sys/scsi/scsiconf.h
+sys/scsi/README <--this file
+
+notice sys/scsi/sg.c and sys/sys/sgio.h have been removed
+
+
+----------------------------------------------------------------
+This scsi system is designed to allow the re-use of top end drivers
+such as disk and tape drivers, with different scsi adapters.
+
+As of writing this document, There are top end drivers working for:
+----------------------------------------------------------------
+generic scsi disk
+generic scsi tape
+cd-rom (plays music under the xcplayer (?) program)
+AEG Character recognition devices *
+Calera Character recognition devices *
+Generic scsi-II scanners *
+Exabyte tape changer device.
+GENERIC SCSI DEVICES (user generated scsi commands)
+----------------------------------------------------------------
+
+
+There are also working bottom end drivers for:
+----------------------------------------------------------------
+adaptec 1542 (and 1742 in 1542 mode)
+bustec 742a (apparently works for VESA version (445S?))(and 747?)
+adaptec 174x (note NOT 27xx)
+Ultrastore 14f (works for 34f (VESA version))
+Ultrastore 24f RSN (Beta version included here)
+----------------------------------------------------------------
+
+
+################## Using the scsi system ##################
+------------minor numbers---------------
+This scsi system does not allocate minor numbers to devices depending
+on their SCSI IDs is any way. A devices minor number is dependant
+on the order in which it was found.
+e.g. the first tape found will become st0 (minor number 0)
+ the second found will become st1 (minor number 16)
+ the third will become st2 (minor 32)
+ etc.
+
+These devices could be on the same scsi bus or different scsi busses.
+That would not change their minor numbers.
+
+THE EXCEPTION TO THIS IS IN THE GENERIC SCSI DRIVER. in which case
+the following mapping applies:
+
+BB TTT LLL B= scsi bus number, T = target number, L = LUN.
+
+It is possible to run two different TYPES of scsi adapters at the
+same time and have st0 on one and st1 on another. (for example)
+
+There is a scheme supported in which scsi devices can be 'wired in' even
+if they are not present or powered on at probe time. (see scsiconf.c)
+In addition, the scsi(1) command allows the operator ask for a
+reprobe at any time. Newly found devices will be configured in. Any
+device that does not map to a known device type is attached to the
+'unknown' (uk) driver.
+
+
+--------------making devices------------
+A changed version of /dev/MAKEDEV is supplied that
+can be used to make devices sd[01234] and st[01234]
+
+e.g.
+cd /dev
+sh MAKEDEV sd0 sd1 sd2 st0 st1 cd0
+
+see st(1) and st(4) for info on tape devices.
+
+--------------file layout-------------------
+Originally I had all scsi definitions in one file: scsi.h
+I have since moved definitions of commands so that all
+definitions needed for a particular type of device are
+found together in the include file of that name.
+This approximatly follows the layout of their definition
+in the SCSI-2 spec.
+As such they are:
+
+scsi_all.h general commands for all devices --- CHAPTER 7
+scsi-disk.h commands relevant to disk --- CHAPTER 8
+scsi-tape.h commands for scsi tapes --- CHAPTER 9
+scsi-cd.h commands for cd-roms (and audio) --- CHAPTER 13
+scsi-changer.h commands medium changer devices --- CHAPTER 16
+
+---------ioctl definitions-------------
+User accessable structures (e.g. ioctl definitions) have been
+placed in sys/cdio, sys/sgio and sys/chio (based after sys/mtio for
+the ioctls for mag tapes (including st).
+General scsi ioctls are found in sys/scsiio.h.
+
+-----------cd-rom-----------------
+The cd rom driver ha been tested by a number of people and
+grefen@convex.com has completed the audio play
+functions.
+(xcdplayer was available from the 'from_ref' directory on agate)
+
+At this time it is possible audio play is broken on cdroms and I will
+be unable to fix it until I get one to test.
+***IMPORTANT***
+Cdrom audio is only suported at all for cdroms that use SCSI2 audio
+definitions.
+
+-------------media changer---------------
+Once again courtesy of grefen@convex.com (in germany)
+I have not tested this but he assures me it's ready for testing.
+If anyone has an exabyte tape changer or similar,
+contact the author for information regarding the control interface
+and program.
+
+WARNING: This has not been tested for a LONG TIME!
+
+
+---------recent changes-----------
+Removed all bitfields from machine independent sections to make
+it possible for them to be used on big-endian architectures.
+
+Removed scsi specific timeouts in favour of system timeout handling.
+
+Many structures (getting more all the time) now dynamically allocated.
+
+Addition of code in the tape driver to recognise models of drive that
+have particular problems so they can be handled specially.
+
+many bug-fixes and cleanups.
+
+---------even more recent changes:--------
+
+rewrote almost the entire thing..
+
+
+
+------Mon Oct 11 22:20:25 WST 1993------
+
+Code is now all KNF (or close to it).
+
+A new structure has been introduced..
+Called scsi_link, one of these exists for every bus/target/lun
+that has a driver attached to it.
+It has links to the adapter and to the driver, as well as status
+information of global interest. (e.g. if the device is in use).
+The use of this new structure has allowed the compaction of a
+lot of duplicated code into a single copy (now in scsi_base.c)
+and makes more simple the USER level scsi implimentation.
+
+
diff --git a/sys/scsi/cd.c b/sys/scsi/cd.c
new file mode 100644
index 0000000..899db93
--- /dev/null
+++ b/sys/scsi/cd.c
@@ -0,0 +1,1317 @@
+/*
+ * Written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ *
+ * $Id: cd.c,v 1.18 1994/04/20 07:06:51 davidg Exp $
+ */
+
+#define SPLCD splbio
+#define ESUCCESS 0
+#include <cd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/dkbad.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/cdio.h>
+
+#include <sys/errno.h>
+#include <sys/disklabel.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_cd.h>
+#include <scsi/scsi_disk.h> /* rw_big and start_stop come from there */
+#include <scsi/scsiconf.h>
+
+/* static function prototypes */
+static errval cd_get_parms(int, int);
+static errval cd_get_mode(u_int32, struct cd_mode_data *, u_int32);
+static errval cd_set_mode(u_int32 unit, struct cd_mode_data *);
+static errval cd_read_toc(u_int32, u_int32, u_int32, struct cd_toc_entry *,
+ u_int32);
+
+
+int32 cdstrats, cdqueues;
+
+#include <ddb.h>
+#if NDDB > 0
+#else /* NDDB > 0 */
+#define Debugger()
+#endif /* NDDB > 0 */
+
+#define PAGESIZ 4096
+#define SECSIZE 2048 /* XXX */ /* default only */
+#define CDOUTSTANDING 2
+#define CDRETRIES 1
+
+#define UNITSHIFT 3
+#define PARTITION(z) (minor(z) & 0x07)
+#define RAW_PART 3
+#define UNIT(z) ( (minor(z) >> UNITSHIFT) )
+
+errval cdstrategy();
+
+void cdstart();
+struct scsi_device cd_switch =
+{
+ NULL, /* use default error handler */
+ cdstart, /* we have a queue, which is started by this */
+ NULL, /* we do not have an async handler */
+ NULL, /* use default 'done' routine */
+ "cd", /* we are to be refered to by this name */
+ 0, /* no device specific flags */
+ 0, 0 /* spares not used */
+};
+
+struct cd_data {
+ u_int32 flags;
+#define CDINIT 0x04 /* device has been init'd */
+ struct scsi_link *sc_link; /* address of scsi low level switch */
+ u_int32 cmdscount; /* cmds allowed outstanding by board */
+ struct cd_parms {
+ u_int32 blksize;
+ u_long disksize; /* total number sectors */
+ } params;
+ struct disklabel disklabel;
+ u_int32 partflags[MAXPARTITIONS]; /* per partition flags */
+#define CDOPEN 0x01
+ u_int32 openparts; /* one bit for each open partition */
+ u_int32 xfer_block_wait;
+ struct buf buf_queue;
+};
+
+#define CD_STOP 0
+#define CD_START 1
+#define CD_EJECT -2
+
+struct cd_driver {
+ u_int32 size;
+ struct cd_data **cd_data;
+} cd_driver;
+
+static u_int32 next_cd_unit = 0;
+
+/*
+ * The routine called by the low level scsi routine when it discovers
+ * A device suitable for this driver
+ */
+int
+cdattach(sc_link)
+ struct scsi_link *sc_link;
+{
+ u_int32 unit, i;
+ unsigned char *tbl;
+ struct cd_data *cd, **cdrealloc;
+ struct cd_parms *dp;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("cdattach "));
+
+ /*
+ * Fill out any more info in the
+ * Link structure that we can
+ */
+ unit = next_cd_unit++;
+ sc_link->device = &cd_switch;
+ sc_link->dev_unit = unit;
+ /*
+ * allocate the resources for another drive
+ * if we have already allocate a cd_data pointer we must
+ * copy the old pointers into a new region that is
+ * larger and release the old region, aka realloc
+ */
+ /* XXX
+ * This if will always be true for now, but future code may
+ * preallocate more units to reduce overhead. This would be
+ * done by changing the malloc to be (next_cd_unit * x) and
+ * the cd_driver.size++ to be +x
+ */
+ if (unit >= cd_driver.size) {
+ cdrealloc =
+ malloc(sizeof(cd_driver.cd_data) * next_cd_unit,
+ M_DEVBUF, M_NOWAIT);
+ if (!cdrealloc) {
+ printf("cd%d: malloc failed for cdrealloc\n", unit);
+ return (0);
+ }
+ /* Make sure we have something to copy before we copy it */
+ bzero(cdrealloc, sizeof(cd_driver.cd_data) * next_cd_unit);
+ if (cd_driver.size) {
+ bcopy(cd_driver.cd_data, cdrealloc,
+ sizeof(cd_driver.cd_data) * cd_driver.size);
+ free(cd_driver.cd_data, M_DEVBUF);
+ }
+ cd_driver.cd_data = cdrealloc;
+ cd_driver.cd_data[unit] = NULL;
+ cd_driver.size++;
+ }
+ if (cd_driver.cd_data[unit]) {
+ printf("cd%d: Already has storage!\n", unit);
+ return (0);
+ }
+ /*
+ * allocate the per drive data area
+ */
+ cd = cd_driver.cd_data[unit] =
+ malloc(sizeof(struct cd_data), M_DEVBUF, M_NOWAIT);
+ if (!cd) {
+ printf("cd%d: malloc failed for cd_data\n", unit);
+ return (0);
+ }
+ bzero(cd, sizeof(struct cd_data));
+ dp = &(cd->params);
+ /*
+ * Store information needed to contact our base driver
+ */
+ cd->sc_link = sc_link;
+ /* only allow 1 outstanding command on tapes */
+ sc_link->opennings = cd->cmdscount = CDOUTSTANDING;
+
+ /*
+ * Use the subdriver to request information regarding
+ * the drive. We cannot use interrupts yet, so the
+ * request must specify this.
+ */
+ cd_get_parms(unit, SCSI_NOSLEEP | SCSI_NOMASK);
+ if (dp->disksize) {
+ printf("cd%d: cd present.[%d x %d byte records]\n",
+ unit,
+ cd->params.disksize,
+ cd->params.blksize);
+ } else {
+ printf("cd%d: drive empty\n", unit);
+ }
+ cd->flags |= CDINIT;
+ return (1);
+}
+
+/*
+ * open the device. Make sure the partition info is a up-to-date as can be.
+ */
+errval
+cdopen(dev)
+ dev_t dev;
+{
+ errval errcode = 0;
+ u_int32 unit, part;
+ struct cd_parms cd_parms;
+ struct cd_data *cd;
+ struct scsi_link *sc_link;
+ u_int32 heldflags;
+
+ unit = UNIT(dev);
+ part = PARTITION(dev);
+
+ /*
+ * Check the unit is legal
+ */
+ if (unit >= cd_driver.size) {
+ return (ENXIO);
+ }
+ cd = cd_driver.cd_data[unit];
+ /*
+ * Make sure the device has been initialised
+ */
+ if ((cd == NULL) || (!(cd->flags & CDINIT)))
+ return (ENXIO);
+
+ sc_link = cd->sc_link;
+ SC_DEBUG(sc_link, SDEV_DB1,
+ ("cdopen: dev=0x%x (unit %d (of %d),partition %d)\n",
+ dev, unit, cd_driver.size, part));
+ /*
+ * If it's been invalidated, and not everybody has closed it then
+ * forbid re-entry. (may have changed media)
+ */
+ if ((!(sc_link->flags & SDEV_MEDIA_LOADED))
+ && (cd->openparts))
+ return (ENXIO);
+
+ /*
+ * Check that it is still responding and ok.
+ * if the media has been changed this will result in a
+ * "unit attention" error which the error code will
+ * disregard because the SDEV_MEDIA_LOADED flag is not yet set
+ */
+ scsi_test_unit_ready(sc_link, SCSI_SILENT);
+
+ /*
+ * Next time actually take notice of error returns
+ */
+ sc_link->flags |= SDEV_OPEN; /* unit attn errors are now errors */
+ if (scsi_test_unit_ready(sc_link, SCSI_SILENT) != 0) {
+ SC_DEBUG(sc_link, SDEV_DB3, ("not ready\n"));
+ errcode = ENXIO;
+ goto bad;
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("Device present\n"));
+ /*
+ * In case it is a funny one, tell it to start
+ * not needed for some drives
+ */
+ scsi_start_unit(sc_link, CD_START);
+ scsi_prevent(sc_link, PR_PREVENT, SCSI_SILENT);
+ SC_DEBUG(sc_link, SDEV_DB3, ("started "));
+ /*
+ * Load the physical device parameters
+ */
+ if (cd_get_parms(unit, 0)) {
+ errcode = ENXIO;
+ goto bad;
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("Params loaded "));
+ /*
+ * Make up some partition information
+ */
+ cdgetdisklabel(unit);
+ SC_DEBUG(sc_link, SDEV_DB3, ("Disklabel fabricated "));
+ /*
+ * Check the partition is legal
+ */
+ if ((part >= cd->disklabel.d_npartitions)
+ && (part != RAW_PART)) {
+ SC_DEBUG(sc_link, SDEV_DB3, ("partition %d > %d\n", part
+ ,cd->disklabel.d_npartitions));
+ errcode = ENXIO;
+ goto bad;
+ }
+ /*
+ * Check that the partition exists
+ */
+ if ((cd->disklabel.d_partitions[part].p_fstype == FS_UNUSED)
+ && (part != RAW_PART)) {
+ SC_DEBUG(sc_link, SDEV_DB3, ("part %d type UNUSED\n", part));
+ errcode = ENXIO;
+ goto bad;
+ }
+ cd->partflags[part] |= CDOPEN;
+ cd->openparts |= (1 << part);
+ SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n"));
+ sc_link->flags |= SDEV_MEDIA_LOADED;
+ return (0);
+ bad:
+
+ /*
+ * if we would have been the only open
+ * then leave things back as they were
+ */
+ if (!(cd->openparts)) {
+ sc_link->flags &= ~SDEV_OPEN;
+ scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT);
+ }
+ return (errcode);
+}
+
+/*
+ * close the device.. only called if we are the LAST
+ * occurence of an open device
+ */
+errval
+cdclose(dev)
+ dev_t dev;
+{
+ u_int8 unit, part;
+ u_int32 old_priority;
+ struct cd_data *cd;
+ struct scsi_link *sc_link;
+
+ unit = UNIT(dev);
+ part = PARTITION(dev);
+ cd = cd_driver.cd_data[unit];
+ sc_link = cd->sc_link;
+ SC_DEBUG(sc_link, SDEV_DB2, ("cd%d: closing part %d\n", unit, part));
+ cd->partflags[part] &= ~CDOPEN;
+ cd->openparts &= ~(1 << part);
+
+ /*
+ * If we were the last open of the entire device, release it.
+ */
+ if (!(cd->openparts)) {
+ scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT);
+ cd->sc_link->flags &= ~SDEV_OPEN;
+ }
+ return (0);
+}
+
+/*
+ * trim the size of the transfer if needed,
+ * called by physio
+ * basically the smaller of our max and the scsi driver's
+ * minphys (note we have no max ourselves)
+ *
+ * Trim buffer length if buffer-size is bigger than page size
+ */
+void
+cdminphys(bp)
+ struct buf *bp;
+{
+ (*(cd_driver.cd_data[UNIT(bp->b_dev)]->sc_link->adapter->scsi_minphys)) (bp);
+}
+
+/*
+ * Actually translate the requested transfer into one the physical driver can
+ * understand. The transfer is described by a buf and will include only one
+ * physical transfer.
+ */
+errval
+cdstrategy(bp)
+ struct buf *bp;
+{
+ struct buf *dp;
+ u_int32 opri;
+ u_int32 unit = UNIT((bp->b_dev));
+ struct cd_data *cd = cd_driver.cd_data[unit];
+
+ cdstrats++;
+ SC_DEBUG(cd->sc_link, SDEV_DB2, ("\ncdstrategy "));
+ SC_DEBUG(cd->sc_link, SDEV_DB1, ("cd%d: %d bytes @ blk%d\n",
+ unit, bp->b_bcount, bp->b_blkno));
+ cdminphys(bp);
+ /*
+ * If the device has been made invalid, error out
+ * maybe the media changed
+ */
+ if (!(cd->sc_link->flags & SDEV_MEDIA_LOADED)) {
+ bp->b_error = EIO;
+ goto bad;
+ }
+ /*
+ * can't ever write to a CD
+ */
+ if ((bp->b_flags & B_READ) == 0) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+ /*
+ * If it's a null transfer, return immediatly
+ */
+ if (bp->b_bcount == 0) {
+ goto done;
+ }
+ /*
+ * Decide which unit and partition we are talking about
+ */
+ if (PARTITION(bp->b_dev) != RAW_PART) {
+ /*
+ * do bounds checking, adjust transfer. if error, process.
+ * if end of partition, just return
+ */
+ if (bounds_check_with_label(bp, &cd->disklabel, 1) <= 0)
+ goto done;
+ /* otherwise, process transfer request */
+ } else {
+ bp->b_pblkno = bp->b_blkno;
+ bp->b_resid = 0;
+ }
+ opri = SPLCD();
+ dp = &cd->buf_queue;
+
+ /*
+ * Use a bounce buffer if necessary
+ */
+#ifndef NOBOUNCE
+ if (cd->sc_link->flags & SDEV_BOUNCE)
+ vm_bounce_alloc(bp);
+#endif
+
+ /*
+ * Place it in the queue of disk activities for this disk
+ */
+ disksort(dp, bp);
+
+ /*
+ * Tell the device to get going on the transfer if it's
+ * not doing anything, otherwise just wait for completion
+ */
+ cdstart(unit);
+
+ splx(opri);
+ return 0; /* XXX ??? is this the right return? */
+ bad:
+ bp->b_flags |= B_ERROR;
+ done:
+
+ /*
+ * Correctly set the buf to indicate a completed xfer
+ */
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+ return (0);
+}
+
+/*
+ * cdstart looks to see if there is a buf waiting for the device
+ * and that the device is not already busy. If both are true,
+ * It deques the buf and creates a scsi command to perform the
+ * transfer in the buf. The transfer request will call scsi_done
+ * on completion, which will in turn call this routine again
+ * so that the next queued transfer is performed.
+ * The bufs are queued by the strategy routine (cdstrategy)
+ *
+ * This routine is also called after other non-queued requests
+ * have been made of the scsi driver, to ensure that the queue
+ * continues to be drained.
+ *
+ * must be called at the correct (highish) spl level
+ * cdstart() is called at SPLCD from cdstrategy and scsi_done
+ */
+void
+cdstart(unit)
+ u_int32 unit;
+{
+ register struct buf *bp = 0;
+ register struct buf *dp;
+ struct scsi_rw_big cmd;
+ u_int32 blkno, nblk;
+ struct partition *p;
+ struct cd_data *cd = cd_driver.cd_data[unit];
+ struct scsi_link *sc_link = cd->sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("cdstart%d ", unit));
+ /*
+ * See if there is a buf to do and we are not already
+ * doing one
+ */
+ if (!sc_link->opennings) {
+ return; /* no room for us, unit already underway */
+ }
+ if (sc_link->flags & SDEV_WAITING) { /* is room, but a special waits */
+ return; /* give the special that's waiting a chance to run */
+ }
+ dp = &cd->buf_queue;
+ if ((bp = dp->b_actf) != NULL) { /* yes, an assign */
+ dp->b_actf = bp->b_actf;
+ } else {
+ return;
+ }
+ /*
+ * Should reject all queued entries if SDEV_MEDIA_LOADED is not true.
+ */
+ if (!(sc_link->flags & SDEV_MEDIA_LOADED)) {
+ goto bad; /* no I/O.. media changed or something */
+ }
+ /*
+ * We have a buf, now we should make a command
+ *
+ * First, translate the block to absolute and put it in terms of the
+ * logical blocksize of the device. Really a bit silly until we have
+ * real partitions, but.
+ */
+ blkno = bp->b_blkno / (cd->params.blksize / 512);
+ if (PARTITION(bp->b_dev) != RAW_PART) {
+ p = cd->disklabel.d_partitions + PARTITION(bp->b_dev);
+ blkno += p->p_offset;
+ }
+ nblk = (bp->b_bcount + (cd->params.blksize - 1)) / (cd->params.blksize);
+ /* what if something asks for 512 bytes not on a 2k boundary? *//*XXX */
+
+ /*
+ * Fill out the scsi command
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.op_code = READ_BIG;
+ cmd.addr_3 = (blkno & 0xff000000UL) >> 24;
+ cmd.addr_2 = (blkno & 0xff0000) >> 16;
+ cmd.addr_1 = (blkno & 0xff00) >> 8;
+ cmd.addr_0 = blkno & 0xff;
+ cmd.length2 = (nblk & 0xff00) >> 8;
+ cmd.length1 = (nblk & 0xff);
+
+ /*
+ * Call the routine that chats with the adapter.
+ * Note: we cannot sleep as we may be an interrupt
+ */
+ if (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &cmd,
+ sizeof(cmd),
+ (u_char *) bp->b_un.b_addr,
+ bp->b_bcount,
+ CDRETRIES,
+ 30000,
+ bp,
+ SCSI_NOSLEEP | ((bp->b_flags & B_READ) ?
+ SCSI_DATA_IN : SCSI_DATA_OUT))
+ != SUCCESSFULLY_QUEUED) {
+ bad:
+ printf("cd%d: oops not queued", unit);
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return;
+ }
+ cdqueues++;
+}
+
+/*
+ * Perform special action on behalf of the user.
+ * Knows about the internals of this device
+ */
+errval
+cdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
+{
+ errval error = 0;
+ u_int32 opri;
+ u_int8 unit, part;
+ register struct cd_data *cd;
+
+ /*
+ * Find the device that the user is talking about
+ */
+ unit = UNIT(dev);
+ part = PARTITION(dev);
+ cd = cd_driver.cd_data[unit];
+ SC_DEBUG(cd->sc_link, SDEV_DB2, ("cdioctl 0x%x ", cmd));
+
+ /*
+ * If the device is not valid.. abandon ship
+ */
+ if (!(cd->sc_link->flags & SDEV_MEDIA_LOADED))
+ return (EIO);
+ switch (cmd) {
+
+ case DIOCSBAD:
+ error = EINVAL;
+ break;
+
+ case DIOCGDINFO:
+ *(struct disklabel *) addr = cd->disklabel;
+ break;
+
+ case DIOCGPART:
+ ((struct partinfo *) addr)->disklab = &cd->disklabel;
+ ((struct partinfo *) addr)->part =
+ &cd->disklabel.d_partitions[PARTITION(dev)];
+ break;
+
+ /*
+ * a bit silly, but someone might want to test something on a
+ * section of cdrom.
+ */
+ case DIOCWDINFO:
+ case DIOCSDINFO:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else
+ error = setdisklabel(&cd->disklabel,
+ (struct disklabel *) addr,
+ 0,
+ 0);
+ if (error == 0)
+ break;
+
+ case DIOCWLABEL:
+ error = EBADF;
+ break;
+
+ case CDIOCPLAYTRACKS:
+ {
+ struct ioc_play_track *args
+ = (struct ioc_play_track *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ data.page.audio.flags &= ~CD_PA_SOTC;
+ data.page.audio.flags |= CD_PA_IMMED;
+ if (error = cd_set_mode(unit, &data))
+ break;
+ return (cd_play_tracks(unit
+ ,args->start_track
+ ,args->start_index
+ ,args->end_track
+ ,args->end_index
+ ));
+ }
+ break;
+ case CDIOCPLAYMSF:
+ {
+ struct ioc_play_msf *args
+ = (struct ioc_play_msf *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ data.page.audio.flags &= ~CD_PA_SOTC;
+ data.page.audio.flags |= CD_PA_IMMED;
+ if (error = cd_set_mode(unit, &data))
+ break;
+ return (cd_play_msf(unit
+ ,args->start_m
+ ,args->start_s
+ ,args->start_f
+ ,args->end_m
+ ,args->end_s
+ ,args->end_f
+ ));
+ }
+ break;
+ case CDIOCPLAYBLOCKS:
+ {
+ struct ioc_play_blocks *args
+ = (struct ioc_play_blocks *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ data.page.audio.flags &= ~CD_PA_SOTC;
+ data.page.audio.flags |= CD_PA_IMMED;
+ if (error = cd_set_mode(unit, &data))
+ break;
+ return (cd_play(unit, args->blk, args->len));
+
+ }
+ break;
+ case CDIOCREADSUBCHANNEL:
+ {
+ struct ioc_read_subchannel *args
+ = (struct ioc_read_subchannel *) addr;
+ struct cd_sub_channel_info data;
+ u_int32 len = args->data_len;
+ if (len > sizeof(data) ||
+ len < sizeof(struct cd_sub_channel_header)) {
+ error = EINVAL;
+ break;
+ }
+ if (error = cd_read_subchannel(unit, args->address_format,
+ args->data_format, args->track, &data, len)) {
+ break;
+ }
+ len = min(len, ((data.header.data_len[0] << 8) + data.header.data_len[1] +
+ sizeof(struct cd_sub_channel_header)));
+ if (copyout(&data, args->data, len) != 0) {
+ error = EFAULT;
+ }
+ }
+ break;
+ case CDIOREADTOCHEADER:
+ { /* ??? useless bcopy? XXX */
+ struct ioc_toc_header th;
+ if (error = cd_read_toc(unit, 0, 0,
+ (struct cd_toc_entry *)&th,
+ sizeof th))
+ break;
+ th.len = (th.len & 0xff) << 8 + ((th.len >> 8) & 0xff);
+ bcopy(&th, addr, sizeof th);
+ }
+ break;
+ case CDIOREADTOCENTRYS:
+ {
+ struct cd_toc {
+ struct ioc_toc_header header;
+ struct cd_toc_entry entries[65];
+ } data;
+ struct ioc_read_toc_entry *te =
+ (struct ioc_read_toc_entry *) addr;
+ struct ioc_toc_header *th;
+ u_int32 len = te->data_len;
+ th = &data.header;
+
+ if (len > sizeof(data.entries) || len < sizeof(struct cd_toc_entry)) {
+ error = EINVAL;
+ break;
+ }
+ if (error = cd_read_toc(unit, te->address_format,
+ te->starting_track,
+ (struct cd_toc_entry *)&data,
+ len + sizeof(struct ioc_toc_header)))
+ break;
+ len = min(len, ((((th->len & 0xff) << 8) + ((th->len >> 8))) - (sizeof(th->starting_track) + sizeof(th->ending_track))));
+ if (copyout(data.entries, te->data, len) != 0) {
+ error = EFAULT;
+ }
+ }
+ break;
+ case CDIOCSETPATCH:
+ {
+ struct ioc_patch *arg = (struct ioc_patch *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ data.page.audio.port[LEFT_PORT].channels = arg->patch[0];
+ data.page.audio.port[RIGHT_PORT].channels = arg->patch[1];
+ data.page.audio.port[2].channels = arg->patch[2];
+ data.page.audio.port[3].channels = arg->patch[3];
+ if (error = cd_set_mode(unit, &data))
+ break; /* eh? */
+ }
+ break;
+ case CDIOCGETVOL:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ arg->vol[LEFT_PORT] = data.page.audio.port[LEFT_PORT].volume;
+ arg->vol[RIGHT_PORT] = data.page.audio.port[RIGHT_PORT].volume;
+ arg->vol[2] = data.page.audio.port[2].volume;
+ arg->vol[3] = data.page.audio.port[3].volume;
+ }
+ break;
+ case CDIOCSETVOL:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ data.page.audio.port[LEFT_PORT].channels = CHANNEL_0;
+ data.page.audio.port[LEFT_PORT].volume = arg->vol[LEFT_PORT];
+ data.page.audio.port[RIGHT_PORT].channels = CHANNEL_1;
+ data.page.audio.port[RIGHT_PORT].volume = arg->vol[RIGHT_PORT];
+ data.page.audio.port[2].volume = arg->vol[2];
+ data.page.audio.port[3].volume = arg->vol[3];
+ if (error = cd_set_mode(unit, &data))
+ break;
+ }
+ break;
+ case CDIOCSETMONO:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ data.page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL | RIGHT_CHANNEL | 4 | 8;
+ data.page.audio.port[RIGHT_PORT].channels = LEFT_CHANNEL | RIGHT_CHANNEL;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ if (error = cd_set_mode(unit, &data))
+ break;
+ }
+ break;
+ case CDIOCSETSTERIO:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ data.page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL;
+ data.page.audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ if (error = cd_set_mode(unit, &data))
+ break;
+ }
+ break;
+ case CDIOCSETMUTE:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ data.page.audio.port[LEFT_PORT].channels = 0;
+ data.page.audio.port[RIGHT_PORT].channels = 0;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ if (error = cd_set_mode(unit, &data))
+ break;
+ }
+ break;
+ case CDIOCSETLEFT:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ data.page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL;
+ data.page.audio.port[RIGHT_PORT].channels = LEFT_CHANNEL;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ if (error = cd_set_mode(unit, &data))
+ break;
+ }
+ break;
+ case CDIOCSETRIGHT:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ data.page.audio.port[LEFT_PORT].channels = RIGHT_CHANNEL;
+ data.page.audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ if (error = cd_set_mode(unit, &data))
+ break;
+ }
+ break;
+ case CDIOCRESUME:
+ error = cd_pause(unit, 1);
+ break;
+ case CDIOCPAUSE:
+ error = cd_pause(unit, 0);
+ break;
+ case CDIOCSTART:
+ error = scsi_start_unit(cd->sc_link, 0);
+ break;
+ case CDIOCSTOP:
+ error = scsi_stop_unit(cd->sc_link, 0, 0);
+ break;
+ case CDIOCEJECT:
+ error = scsi_stop_unit(cd->sc_link, 1, 0);
+ break;
+ case CDIOCALLOW:
+ error = scsi_prevent(cd->sc_link, PR_ALLOW, 0);
+ break;
+ case CDIOCPREVENT:
+ error = scsi_prevent(cd->sc_link, PR_PREVENT, 0);
+ break;
+ case CDIOCSETDEBUG:
+ cd->sc_link->flags |= (SDEV_DB1 | SDEV_DB2);
+ break;
+ case CDIOCCLRDEBUG:
+ cd->sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2);
+ break;
+ case CDIOCRESET:
+ return (cd_reset(unit));
+ break;
+ default:
+ if(part == RAW_PART)
+ error = scsi_do_ioctl(cd->sc_link,cmd,addr,flag);
+ else
+ error = ENOTTY;
+ break;
+ }
+ return (error);
+}
+
+/*
+ * Load the label information on the named device
+ * Actually fabricate a disklabel
+ *
+ * EVENTUALLY take information about different
+ * data tracks from the TOC and put it in the disklabel
+ */
+errval
+cdgetdisklabel(unit)
+ u_int8 unit;
+{
+ /*unsigned int n, m; */
+ char *errstring;
+ struct cd_data *cd;
+
+ cd = cd_driver.cd_data[unit];
+
+ bzero(&cd->disklabel, sizeof(struct disklabel));
+ /*
+ * make partition 0 the whole disk
+ */
+ strncpy(cd->disklabel.d_typename, "scsi cd_rom", 16);
+ strncpy(cd->disklabel.d_packname, "ficticious", 16);
+ cd->disklabel.d_secsize = cd->params.blksize; /* as long as it's not 0 */
+ cd->disklabel.d_nsectors = 100;
+ cd->disklabel.d_ntracks = 1;
+ cd->disklabel.d_ncylinders = (cd->params.disksize / 100) + 1;
+ cd->disklabel.d_secpercyl = 100;
+ cd->disklabel.d_secperunit = cd->params.disksize;
+ cd->disklabel.d_rpm = 300;
+ cd->disklabel.d_interleave = 1;
+ cd->disklabel.d_flags = D_REMOVABLE;
+
+ /*
+ * remember that comparisons with the partition are done
+ * assuming the blocks are 512 bytes so fudge it.
+ */
+ cd->disklabel.d_npartitions = 1;
+ cd->disklabel.d_partitions[0].p_offset = 0;
+ cd->disklabel.d_partitions[0].p_size
+ = cd->params.disksize * (cd->params.blksize / 512);
+ cd->disklabel.d_partitions[0].p_fstype = 9;
+
+ cd->disklabel.d_magic = DISKMAGIC;
+ cd->disklabel.d_magic2 = DISKMAGIC;
+ cd->disklabel.d_checksum = dkcksum(&(cd->disklabel));
+
+ /*
+ * Signal to other users and routines that we now have a
+ * disklabel that represents the media (maybe)
+ */
+ return (ESUCCESS);
+}
+
+/*
+ * Find out from the device what it's capacity is
+ */
+u_int32
+cd_size(unit, flags)
+ int unit;
+ int flags;
+{
+ struct scsi_read_cd_cap_data rdcap;
+ struct scsi_read_cd_capacity scsi_cmd;
+ u_int32 size;
+ u_int32 blksize;
+ struct cd_data *cd = cd_driver.cd_data[unit];
+
+ /*
+ * make up a scsi command and ask the scsi driver to do
+ * it for you.
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = READ_CD_CAPACITY;
+
+ /*
+ * If the command works, interpret the result as a 4 byte
+ * number of blocks and a blocksize
+ */
+ if (scsi_scsi_cmd(cd->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) & rdcap,
+ sizeof(rdcap),
+ CDRETRIES,
+ 20000, /* might be a disk-changer */
+ NULL,
+ SCSI_DATA_IN | flags) != 0) {
+ printf("cd%d: could not get size\n", unit);
+ return (0);
+ } else {
+ size = rdcap.addr_0 + 1;
+ size += rdcap.addr_1 << 8;
+ size += rdcap.addr_2 << 16;
+ size += rdcap.addr_3 << 24;
+ blksize = rdcap.length_0;
+ blksize += rdcap.length_1 << 8;
+ blksize += rdcap.length_2 << 16;
+ blksize += rdcap.length_3 << 24;
+ }
+ if (blksize < 512)
+ blksize = 2048; /* some drives lie ! */
+ if (size < 100)
+ size = 400000; /* ditto */
+ SC_DEBUG(cd->sc_link, SDEV_DB3, ("cd%d: %d %d byte blocks\n"
+ ,unit, size, blksize));
+ cd->params.disksize = size;
+ cd->params.blksize = blksize;
+ return (size);
+}
+
+/*
+ * Get the requested page into the buffer given
+ */
+static errval
+cd_get_mode(unit, data, page)
+ u_int32 unit;
+ struct cd_mode_data *data;
+ u_int32 page;
+{
+ struct scsi_mode_sense scsi_cmd;
+ errval retval;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ bzero(data, sizeof(*data));
+ scsi_cmd.op_code = MODE_SENSE;
+ scsi_cmd.page = page;
+ scsi_cmd.length = sizeof(*data) & 0xff;
+ retval = scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) data,
+ sizeof(*data),
+ CDRETRIES,
+ 20000, /* should be immed */
+ NULL,
+ SCSI_DATA_IN);
+ return (retval);
+}
+
+/*
+ * Get the requested page into the buffer given
+ */
+errval
+cd_set_mode(unit, data)
+ u_int32 unit;
+ struct cd_mode_data *data;
+{
+ struct scsi_mode_select scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = MODE_SELECT;
+ scsi_cmd.byte2 |= SMS_PF;
+ scsi_cmd.length = sizeof(*data) & 0xff;
+ data->header.data_length = 0;
+ return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) data,
+ sizeof(*data),
+ CDRETRIES,
+ 20000, /* should be immed */
+ NULL,
+ SCSI_DATA_OUT));
+}
+
+/*
+ * Get scsi driver to send a "start playing" command
+ */
+errval
+cd_play(unit, blk, len)
+ u_int32 unit, blk, len;
+{
+ struct scsi_play scsi_cmd;
+ errval retval;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = PLAY;
+ scsi_cmd.blk_addr[0] = (blk >> 24) & 0xff;
+ scsi_cmd.blk_addr[1] = (blk >> 16) & 0xff;
+ scsi_cmd.blk_addr[2] = (blk >> 8) & 0xff;
+ scsi_cmd.blk_addr[3] = blk & 0xff;
+ scsi_cmd.xfer_len[0] = (len >> 8) & 0xff;
+ scsi_cmd.xfer_len[1] = len & 0xff;
+ return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ CDRETRIES,
+ 200000, /* should be immed */
+ NULL,
+ 0));
+}
+
+/*
+ * Get scsi driver to send a "start playing" command
+ */
+errval
+cd_play_big(unit, blk, len)
+ u_int32 unit, blk, len;
+{
+ struct scsi_play_big scsi_cmd;
+ errval retval;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = PLAY_BIG;
+ scsi_cmd.blk_addr[0] = (blk >> 24) & 0xff;
+ scsi_cmd.blk_addr[1] = (blk >> 16) & 0xff;
+ scsi_cmd.blk_addr[2] = (blk >> 8) & 0xff;
+ scsi_cmd.blk_addr[3] = blk & 0xff;
+ scsi_cmd.xfer_len[0] = (len >> 24) & 0xff;
+ scsi_cmd.xfer_len[1] = (len >> 16) & 0xff;
+ scsi_cmd.xfer_len[2] = (len >> 8) & 0xff;
+ scsi_cmd.xfer_len[3] = len & 0xff;
+ return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ CDRETRIES,
+ 20000, /* should be immed */
+ NULL,
+ 0));
+}
+
+/*
+ * Get scsi driver to send a "start playing" command
+ */
+errval
+cd_play_tracks(unit, strack, sindex, etrack, eindex)
+ u_int32 unit, strack, sindex, etrack, eindex;
+{
+ struct scsi_play_track scsi_cmd;
+ errval retval;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = PLAY_TRACK;
+ scsi_cmd.start_track = strack;
+ scsi_cmd.start_index = sindex;
+ scsi_cmd.end_track = etrack;
+ scsi_cmd.end_index = eindex;
+ return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ CDRETRIES,
+ 20000, /* should be immed */
+ NULL,
+ 0));
+}
+
+/*
+ * Get scsi driver to send a "play msf" command
+ */
+errval
+cd_play_msf(unit, startm, starts, startf, endm, ends, endf)
+ u_int32 unit, startm, starts, startf, endm, ends, endf;
+{
+ struct scsi_play_msf scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = PLAY_MSF;
+ scsi_cmd.start_m = startm;
+ scsi_cmd.start_s = starts;
+ scsi_cmd.start_f = startf;
+ scsi_cmd.end_m = endm;
+ scsi_cmd.end_s = ends;
+ scsi_cmd.end_f = endf;
+
+ return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ CDRETRIES,
+ 2000,
+ NULL,
+ 0));
+}
+
+/*
+ * Get scsi driver to send a "start up" command
+ */
+errval
+cd_pause(unit, go)
+ u_int32 unit, go;
+{
+ struct scsi_pause scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = PAUSE;
+ scsi_cmd.resume = go;
+
+ return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ CDRETRIES,
+ 2000,
+ NULL,
+ 0));
+}
+
+/*
+ * Get scsi driver to send a "RESET" command
+ */
+errval
+cd_reset(unit)
+ u_int32 unit;
+{
+ return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ 0,
+ 0,
+ 0,
+ 0,
+ CDRETRIES,
+ 2000,
+ NULL,
+ SCSI_RESET));
+}
+
+/*
+ * Read subchannel
+ */
+errval
+cd_read_subchannel(unit, mode, format, track, data, len)
+ u_int32 unit, mode, format;
+ int track;
+ struct cd_sub_channel_info *data;
+ u_int32 len;
+{
+ struct scsi_read_subchannel scsi_cmd;
+ errval error;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+
+ scsi_cmd.op_code = READ_SUBCHANNEL;
+ if (mode == CD_MSF_FORMAT)
+ scsi_cmd.byte2 |= CD_MSF;
+ scsi_cmd.byte3 = SRS_SUBQ;
+ scsi_cmd.subchan_format = format;
+ scsi_cmd.track = track;
+ scsi_cmd.data_len[0] = (len) >> 8;
+ scsi_cmd.data_len[1] = (len) & 0xff;
+ return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(struct scsi_read_subchannel),
+ (u_char *) data,
+ len,
+ CDRETRIES,
+ 5000,
+ NULL,
+ SCSI_DATA_IN));
+}
+
+/*
+ * Read table of contents
+ */
+static errval
+cd_read_toc(unit, mode, start, data, len)
+ u_int32 unit, mode, start;
+ struct cd_toc_entry *data;
+ u_int32 len;
+{
+ struct scsi_read_toc scsi_cmd;
+ errval error;
+ u_int32 ntoc;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ /*if(len!=sizeof(struct ioc_toc_header))
+ * ntoc=((len)-sizeof(struct ioc_toc_header))/sizeof(struct cd_toc_entry);
+ * else */
+ ntoc = len;
+
+ scsi_cmd.op_code = READ_TOC;
+ if (mode == CD_MSF_FORMAT)
+ scsi_cmd.byte2 |= CD_MSF;
+ scsi_cmd.from_track = start;
+ scsi_cmd.data_len[0] = (ntoc) >> 8;
+ scsi_cmd.data_len[1] = (ntoc) & 0xff;
+ return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(struct scsi_read_toc),
+ (u_char *) data,
+ len,
+ CDRETRIES,
+ 5000,
+ NULL,
+ SCSI_DATA_IN));
+}
+
+#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 )
+
+/*
+ * Get the scsi driver to send a full inquiry to the device and use the
+ * results to fill out the disk parameter structure.
+ */
+static errval
+cd_get_parms(unit, flags)
+ int unit;
+ int flags;
+{
+ struct cd_data *cd = cd_driver.cd_data[unit];
+
+ /*
+ * First check if we have it all loaded
+ */
+ if (cd->sc_link->flags & SDEV_MEDIA_LOADED)
+ return (0);
+ /*
+ * give a number of sectors so that sec * trks * cyls
+ * is <= disk_size
+ */
+ if (cd_size(unit, flags)) {
+ cd->sc_link->flags |= SDEV_MEDIA_LOADED;
+ return (0);
+ } else {
+ return (ENXIO);
+ }
+}
+
+int
+cdsize(dev_t dev)
+{
+ return (-1);
+}
diff --git a/sys/scsi/ch.c b/sys/scsi/ch.c
new file mode 100644
index 0000000..315dab9
--- /dev/null
+++ b/sys/scsi/ch.c
@@ -0,0 +1,487 @@
+/*
+ * Written by grefen@?????
+ * Based on scsi drivers by Julian Elischer (julian@tfs.com)
+ *
+ * $Id: ch.c,v 1.7 1993/12/19 00:54:49 wollman Exp $
+ */
+
+#include <sys/types.h>
+#include <ch.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/chio.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_changer.h>
+#include <scsi/scsiconf.h>
+
+static errval ch_mode_sense(u_int32, u_int32);
+
+struct scsi_xfer ch_scsi_xfer[NCH];
+u_int32 ch_xfer_block_wait[NCH];
+
+#define PAGESIZ 4096
+#define STQSIZE 4
+#define CHRETRIES 2
+
+#define MODE(z) ( (minor(z) & 0x0F) )
+#define UNIT(z) ( (minor(z) >> 4) )
+
+#define ESUCCESS 0
+
+errval chattach();
+
+/*
+ * This driver is so simple it uses all the default services
+ */
+struct scsi_device ch_switch =
+{
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "ch",
+ 0,
+ 0, 0
+};
+
+struct ch_data {
+ u_int32 flags;
+ struct scsi_link *sc_link; /* all the inter level info */
+ u_int16 chmo; /* Offset of first CHM */
+ u_int16 chms; /* No. of CHM */
+ u_int16 slots; /* No. of Storage Elements */
+ u_int16 sloto; /* Offset of first SE */
+ u_int16 imexs; /* No. of Import/Export Slots */
+ u_int16 imexo; /* Offset of first IM/EX */
+ u_int16 drives; /* No. of CTS */
+ u_int16 driveo; /* Offset of first CTS */
+ u_int16 rot; /* CHM can rotate */
+ u_long op_matrix; /* possible opertaions */
+ u_int16 lsterr; /* details of lasterror */
+ u_char stor; /* posible Storage locations */
+ u_int32 initialized;
+} ch_data[NCH];
+
+#define CH_OPEN 0x01
+#define CH_KNOWN 0x02
+
+static u_int32 next_ch_unit = 0;
+
+/*
+ * The routine called by the low level scsi routine when it discovers
+ * a device suitable for this driver.
+ */
+errval
+chattach(sc_link)
+ struct scsi_link *sc_link;
+{
+ u_int32 unit, i, stat;
+ unsigned char *tbl;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("chattach: "));
+ /*
+ * Check we have the resources for another drive
+ */
+ unit = next_ch_unit++;
+ if (unit >= NCH) {
+ printf("Too many scsi changers..(%d > %d) reconfigure kernel\n", (unit + 1), NCH);
+ return (0);
+ }
+ /*
+ * Store information needed to contact our base driver
+ */
+ ch_data[unit].sc_link = sc_link;
+ sc_link->device = &ch_switch;
+ sc_link->dev_unit = unit;
+
+ /*
+ * Use the subdriver to request information regarding
+ * the drive. We cannot use interrupts yet, so the
+ * request must specify this.
+ */
+ if ((ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK /*| SCSI_SILENT */ ))) {
+ printf("ch%d: scsi changer :- offline\n", unit);
+ stat = CH_OPEN;
+ } else {
+ printf("ch%d: scsi changer, %d slot(s) %d drive(s) %d arm(s) %d i/e-slot(s)\n",
+ unit, ch_data[unit].slots, ch_data[unit].drives, ch_data[unit].chms, ch_data[unit].imexs);
+ stat = CH_KNOWN;
+ }
+ ch_data[unit].initialized = 1;
+
+ return 1;
+ /* XXX ??? is this the right return val? */
+}
+
+/*
+ * open the device.
+ */
+errval
+chopen(dev)
+ dev_t dev;
+{
+ errval errcode = 0;
+ u_int32 unit, mode;
+ struct scsi_link *sc_link;
+
+ unit = UNIT(dev);
+ mode = MODE(dev);
+
+ /*
+ * Check the unit is legal
+ */
+ if (unit >= NCH) {
+ printf("ch%d: ch %d > %d\n", unit, unit, NCH);
+ errcode = ENXIO;
+ return (errcode);
+ }
+ /*
+ * Only allow one at a time
+ */
+ if (ch_data[unit].flags & CH_OPEN) {
+ printf("ch%d: already open\n", unit);
+ return EBUSY;
+ }
+ /*
+ * Make sure the device has been initialised
+ */
+ if (!ch_data[unit].initialized)
+ return (ENXIO);
+
+ sc_link = ch_data[unit].sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB1, ("chopen: dev=0x%x (unit %d (of %d))\n"
+ ,dev, unit, NCH));
+ /*
+ * Catch any unit attention errors.
+ */
+ scsi_test_unit_ready(sc_link, SCSI_SILENT);
+
+ sc_link->flags |= SDEV_OPEN;
+ /*
+ * Check that it is still responding and ok.
+ */
+ if (errcode = (scsi_test_unit_ready(sc_link, 0))) {
+ printf("ch%d: not ready\n", unit);
+ sc_link->flags &= ~SDEV_OPEN;
+ return errcode;
+ }
+ /*
+ * Make sure data is loaded
+ */
+ if (errcode = (ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK))) {
+ printf("ch%d: scsi changer :- offline\n", unit);
+ sc_link->flags &= ~SDEV_OPEN;
+ return (errcode);
+ }
+ ch_data[unit].flags = CH_OPEN;
+ return 0;
+}
+
+/*
+ * close the device.. only called if we are the LAST
+ * occurence of an open device
+ */
+errval
+chclose(dev)
+ dev_t dev;
+{
+ unsigned char unit, mode;
+ struct scsi_link *sc_link;
+
+ unit = UNIT(dev);
+ mode = MODE(dev);
+ sc_link = ch_data[unit].sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB1, ("Closing device"));
+ ch_data[unit].flags = 0;
+ sc_link->flags &= ~SDEV_OPEN;
+ return (0);
+}
+
+/*
+ * Perform special action on behalf of the user
+ * Knows about the internals of this device
+ */
+errval
+chioctl(dev, cmd, arg, mode)
+ dev_t dev;
+ u_int32 cmd;
+ caddr_t arg;
+ int mode;
+{
+ /* struct ch_cmd_buf *args; */
+ union scsi_cmd *scsi_cmd;
+ register i, j;
+ u_int32 opri;
+ errval errcode = 0;
+ unsigned char unit;
+ u_int32 number, flags;
+ errval ret;
+ struct scsi_link *sc_link;
+
+ /*
+ * Find the device that the user is talking about
+ */
+ flags = 0; /* give error messages, act on errors etc. */
+ unit = UNIT(dev);
+ sc_link = ch_data[unit].sc_link;
+
+ switch ((int)cmd) {
+ case CHIOOP:{
+ struct chop *ch = (struct chop *) arg;
+ SC_DEBUG(sc_link, SDEV_DB2,
+ ("[chtape_chop: %x]\n", ch->ch_op));
+
+ switch ((short) (ch->ch_op)) {
+ case CHGETPARAM:
+ ch->u.getparam.chmo = ch_data[unit].chmo;
+ ch->u.getparam.chms = ch_data[unit].chms;
+ ch->u.getparam.sloto = ch_data[unit].sloto;
+ ch->u.getparam.slots = ch_data[unit].slots;
+ ch->u.getparam.imexo = ch_data[unit].imexo;
+ ch->u.getparam.imexs = ch_data[unit].imexs;
+ ch->u.getparam.driveo = ch_data[unit].driveo;
+ ch->u.getparam.drives = ch_data[unit].drives;
+ ch->u.getparam.rot = ch_data[unit].rot;
+ ch->result = 0;
+ return 0;
+ break;
+ case CHPOSITION:
+ return ch_position(unit, &ch->result, ch->u.position.chm,
+ ch->u.position.to,
+ flags);
+ case CHMOVE:
+ return ch_move(unit, &ch->result, ch->u.position.chm,
+ ch->u.move.from, ch->u.move.to,
+ flags);
+ case CHGETELEM:
+ return ch_getelem(unit, &ch->result, ch->u.get_elem_stat.type,
+ ch->u.get_elem_stat.from, &ch->u.get_elem_stat.elem_data,
+ flags);
+ default:
+ return EINVAL;
+ }
+ }
+ default:
+ return scsi_do_ioctl(sc_link, cmd, arg, mode);
+ }
+ return (ret ? ESUCCESS : EIO);
+}
+
+errval
+ch_getelem(unit, stat, type, from, data, flags)
+ u_int32 unit, from, flags;
+ int type;
+ short *stat;
+ char *data;
+{
+ struct scsi_read_element_status scsi_cmd;
+ char elbuf[32];
+ errval ret;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = READ_ELEMENT_STATUS;
+ scsi_cmd.byte2 = type;
+ scsi_cmd.starting_element_addr[0] = (from >> 8) & 0xff;
+ scsi_cmd.starting_element_addr[1] = from & 0xff;
+ scsi_cmd.number_of_elements[1] = 1;
+ scsi_cmd.allocation_length[2] = 32;
+
+ if ((ret = scsi_scsi_cmd(ch_data[unit].sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) elbuf,
+ 32,
+ CHRETRIES,
+ 100000,
+ NULL,
+ SCSI_DATA_IN | flags) != ESUCCESS)) {
+ *stat = ch_data[unit].lsterr;
+ bcopy(elbuf + 16, data, 16);
+ return ret;
+ }
+ bcopy(elbuf + 16, data, 16); /*Just a hack sh */
+ return ret;
+}
+
+errval
+ch_move(unit, stat, chm, from, to, flags)
+ u_int32 unit, chm, from, to, flags;
+ short *stat;
+{
+ struct scsi_move_medium scsi_cmd;
+ errval ret;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = MOVE_MEDIUM;
+ scsi_cmd.transport_element_address[0] = (chm >> 8) & 0xff;
+ scsi_cmd.transport_element_address[1] = chm & 0xff;
+ scsi_cmd.source_address[0] = (from >> 8) & 0xff;
+ scsi_cmd.source_address[1] = from & 0xff;
+ scsi_cmd.destination_address[0] = (to >> 8) & 0xff;
+ scsi_cmd.destination_address[1] = to & 0xff;
+ scsi_cmd.invert = (chm & CH_INVERT) ? 1 : 0;
+ if ((ret = scsi_scsi_cmd(ch_data[unit].sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ NULL,
+ 0,
+ CHRETRIES,
+ 100000,
+ NULL,
+ flags) != ESUCCESS)) {
+ *stat = ch_data[unit].lsterr;
+ return ret;
+ }
+ return ret;
+}
+
+errval
+ch_position(unit, stat, chm, to, flags)
+ u_int32 unit, chm, to, flags;
+ short *stat;
+{
+ struct scsi_position_to_element scsi_cmd;
+ errval ret;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = POSITION_TO_ELEMENT;
+ scsi_cmd.transport_element_address[0] = (chm >> 8) & 0xff;
+ scsi_cmd.transport_element_address[1] = chm & 0xff;
+ scsi_cmd.source_address[0] = (to >> 8) & 0xff;
+ scsi_cmd.source_address[1] = to & 0xff;
+ scsi_cmd.invert = (chm & CH_INVERT) ? 1 : 0;
+ if ((ret = scsi_scsi_cmd(ch_data[unit].sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ NULL,
+ 0,
+ CHRETRIES,
+ 100000,
+ NULL,
+ flags) != ESUCCESS)) {
+ *stat = ch_data[unit].lsterr;
+ return ret;
+ }
+ return ret;
+}
+
+#ifdef __STDC__
+#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 )
+#else
+#define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 )
+#endif
+
+/*
+ * Get the scsi driver to send a full inquiry to the
+ * device and use the results to fill out the global
+ * parameter structure.
+ */
+static errval
+ch_mode_sense(unit, flags)
+ u_int32 unit, flags;
+{
+ struct scsi_mode_sense scsi_cmd;
+ u_char scsi_sense[128]; /* Can't use scsi_mode_sense_data because of
+ * missing block descriptor
+ */
+ u_char *b;
+ int32 i, l;
+ errval errcode;
+ struct scsi_link *sc_link = ch_data[unit].sc_link;
+
+ /*
+ * First check if we have it all loaded
+ */
+ if (sc_link->flags & SDEV_MEDIA_LOADED)
+ return 0;
+
+ /*
+ * First do a mode sense
+ */
+ /* sc_link->flags &= ~SDEV_MEDIA_LOADED; *//*XXX */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = MODE_SENSE;
+ scsi_cmd.byte2 = SMS_DBD;
+ scsi_cmd.page = 0x3f; /* All Pages */
+ scsi_cmd.length = sizeof(scsi_sense);
+
+ /*
+ * Read in the pages
+ */
+ if (errcode = scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(struct scsi_mode_sense),
+ (u_char *) & scsi_sense,
+ sizeof (scsi_sense),
+ CHRETRIES,
+ 5000,
+ NULL,
+ flags | SCSI_DATA_IN) != 0) {
+ if (!(flags & SCSI_SILENT))
+ printf("ch%d: could not mode sense\n", unit);
+ return (errcode);
+ }
+ sc_link->flags |= SDEV_MEDIA_LOADED;
+ l = scsi_sense[0] - 3;
+ b = &scsi_sense[4];
+
+ /*
+ * To avoid alignment problems
+ */
+/* XXX - FIX THIS FOR MSB */
+#define p2copy(valp) (valp[1]+ (valp[0]<<8));valp+=2
+#define p4copy(valp) (valp[3]+ (valp[2]<<8) + (valp[1]<<16) + (valp[0]<<24));valp+=4
+#if 0
+ printf("\nmode_sense %d\n", l);
+ for (i = 0; i < l + 4; i++) {
+ printf("%x%c", scsi_sense[i], i % 8 == 7 ? '\n' : ':');
+ } printf("\n");
+#endif
+ for (i = 0; i < l;) {
+ u_int32 pc = (*b++) & 0x3f;
+ u_int32 pl = *b++;
+ u_char *bb = b;
+ switch ((int)pc) {
+ case 0x1d:
+ ch_data[unit].chmo = p2copy(bb);
+ ch_data[unit].chms = p2copy(bb);
+ ch_data[unit].sloto = p2copy(bb);
+ ch_data[unit].slots = p2copy(bb);
+ ch_data[unit].imexo = p2copy(bb);
+ ch_data[unit].imexs = p2copy(bb);
+ ch_data[unit].driveo = p2copy(bb);
+ ch_data[unit].drives = p2copy(bb);
+ break;
+ case 0x1e:
+ ch_data[unit].rot = (*b) & 1;
+ break;
+ case 0x1f:
+ ch_data[unit].stor = *b & 0xf;
+ bb += 2;
+ ch_data[unit].stor = p4copy(bb);
+ break;
+ default:
+ break;
+ }
+ b += pl;
+ i += pl + 2;
+ }
+ SC_DEBUG(sc_link, SDEV_DB2,
+ (" cht(%d-%d)slot(%d-%d)imex(%d-%d)cts(%d-%d) %s rotate\n",
+ ch_data[unit].chmo, ch_data[unit].chms,
+ ch_data[unit].sloto, ch_data[unit].slots,
+ ch_data[unit].imexo, ch_data[unit].imexs,
+ ch_data[unit].driveo, ch_data[unit].drives,
+ ch_data[unit].rot ? "can" : "can't"));
+ return (0);
+}
diff --git a/sys/scsi/scsi_all.h b/sys/scsi/scsi_all.h
new file mode 100644
index 0000000..2e7bdcf
--- /dev/null
+++ b/sys/scsi/scsi_all.h
@@ -0,0 +1,340 @@
+/*
+ * SCSI general interface description
+ */
+
+/*
+ * Largely written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ *
+ * $Id: scsi_all.h,v 2.0 93/10/06 21:10:28 julian Exp Locker: julian $
+ */
+
+#ifndef _SCSI_SCSI_ALL_H
+#define _SCSI_SCSI_ALL_H 1
+/*
+ * SCSI command format
+ */
+
+/*
+ * Define dome bits that are in ALL (or a lot of) scsi commands
+ */
+#define SCSI_CTL_LINK 0x01
+#define SCSI_CTL_FLAG 0x02
+#define SCSI_CTL_VENDOR 0xC0
+#define SCSI_CMD_LUN 0xA0 /* these two should not be needed */
+#define SCSI_CMD_LUN_SHIFT 5 /* LUN in the cmd is no longer SCSI */
+
+
+struct scsi_generic
+{
+ u_char opcode;
+ u_char bytes[11];
+};
+
+struct scsi_test_unit_ready
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[3];
+ u_char control;
+};
+
+struct scsi_send_diag
+{
+ u_char op_code;
+ u_char byte2;
+#define SSD_UOL 0x01
+#define SSD_DOL 0x02
+#define SSD_SELFTEST 0x04
+#define SSD_PF 0x10
+ u_char unused[1];
+ u_char paramlen[2];
+ u_char control;
+};
+
+struct scsi_sense
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[2];
+ u_char length;
+ u_char control;
+};
+
+struct scsi_inquiry
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[2];
+ u_char length;
+ u_char control;
+};
+
+struct scsi_mode_sense
+{
+ u_char op_code;
+ u_char byte2;
+#define SMS_DBD 0x08
+ u_char page;
+#define SMS_PAGE_CODE 0x3F
+#define SMS_PAGE_CTRL 0xC0
+#define SMS_PAGE_CTRL_CURRENT 0x00
+#define SMS_PAGE_CTRL_CHANGEABLE 0x40
+#define SMS_PAGE_CTRL_DEFAULT 0x80
+#define SMS_PAGE_CTRL_SAVED 0xC0
+ u_char unused;
+ u_char length;
+ u_char control;
+};
+
+struct scsi_mode_sense_big
+{
+ u_char op_code;
+ u_char byte2; /* same bits as small version */
+ u_char page; /* same bits as small version */
+ u_char unused[4];
+ u_char length[2];
+ u_char control;
+};
+
+struct scsi_mode_select
+{
+ u_char op_code;
+ u_char byte2;
+#define SMS_SP 0x01
+#define SMS_PF 0x10
+ u_char unused[2];
+ u_char length;
+ u_char control;
+};
+
+struct scsi_mode_select_big
+{
+ u_char op_code;
+ u_char byte2; /* same bits as small version */
+ u_char unused[5];
+ u_char length[2];
+ u_char control;
+};
+
+struct scsi_reserve
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[2];
+ u_char length;
+ u_char control;
+};
+
+struct scsi_release
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[2];
+ u_char length;
+ u_char control;
+};
+
+struct scsi_prevent
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[2];
+ u_char how;
+ u_char control;
+};
+#define PR_PREVENT 0x01
+#define PR_ALLOW 0x00
+
+struct scsi_changedef
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused1;
+ u_char how;
+ u_char unused[4];
+ u_char datalen;
+ u_char control;
+};
+#define SC_SCSI_1 0x01
+#define SC_SCSI_2 0x03
+
+/*
+ * Opcodes
+ */
+
+#define TEST_UNIT_READY 0x00
+#define REQUEST_SENSE 0x03
+#define INQUIRY 0x12
+#define MODE_SELECT 0x15
+#define MODE_SENSE 0x1a
+#define START_STOP 0x1b
+#define RESERVE 0x16
+#define RELEASE 0x17
+#define PREVENT_ALLOW 0x1e
+#define POSITION_TO_ELEMENT 0x2b
+#define CHANGE_DEFINITION 0x40
+#define MODE_SENSE_BIG 0x54
+#define MODE_SELECT_BIG 0x55
+#define MOVE_MEDIUM 0xa5
+#define READ_ELEMENT_STATUS 0xb8
+
+
+/*
+ * sense data format
+ */
+#define T_DIRECT 0
+#define T_SEQUENTIAL 1
+#define T_PRINTER 2
+#define T_PROCESSOR 3
+#define T_WORM 4
+#define T_READONLY 5
+#define T_SCANNER 6
+#define T_OPTICAL 7
+#define T_NODEVICE 0x1F
+
+#define T_CHANGER 8
+#define T_COMM 9
+
+#define T_REMOV 1
+#define T_FIXED 0
+
+struct scsi_inquiry_data
+{
+ u_char device;
+#define SID_TYPE 0x1F
+#define SID_QUAL 0xE0
+#define SID_QUAL_LU_OK 0x00
+#define SID_QUAL_LU_OFFLINE 0x20
+#define SID_QUAL_RSVD 0x40
+#define SID_QUAL_BAD_LU 0x60
+ u_char dev_qual2;
+#define SID_QUAL2 0x7F
+#define SID_REMOVABLE 0x80
+ u_char version;
+#define SID_ANSII 0x07
+#define SID_ECMA 0x38
+#define SID_ISO 0xC0
+ u_char response_format;
+ u_char additional_length;
+ u_char unused[2];
+ u_char flags;
+#define SID_SftRe 0x01
+#define SID_CmdQue 0x02
+#define SID_Linked 0x08
+#define SID_Sync 0x10
+#define SID_WBus16 0x20
+#define SID_WBus32 0x40
+#define SID_RelAdr 0x80
+ char vendor[8];
+ char product[16];
+ char revision[4];
+ u_char extra[8];
+};
+
+
+struct scsi_sense_data
+{
+/* 1*/ u_char error_code; /* same bits as new version */
+ union
+ {
+ struct
+ {
+/* 2*/ u_char blockhi;
+/* 3*/ u_char blockmed;
+/* 4*/ u_char blocklow;
+ } unextended;
+ struct
+ {
+/* 2*/ u_char segment;
+/* 3*/ u_char flags; /* same bits as new version */
+/* 7*/ u_char info[4];
+/* 8*/ u_char extra_len;
+ /* allocate enough room to hold new stuff
+ ( by increasing 16 to 24 below) */
+/*32*/ u_char extra_bytes[24];
+ } extended;
+ }ext;
+}; /* total of 32 bytes */
+struct scsi_sense_data_new
+{
+/* 1*/ u_char error_code;
+#define SSD_ERRCODE 0x7F
+#define SSD_ERRCODE_VALID 0x80
+ union
+ {
+ struct /* this is deprecated, the standard says "DON'T"*/
+ {
+/* 2*/ u_char blockhi;
+/* 3*/ u_char blockmed;
+/* 4*/ u_char blocklow;
+ } unextended;
+ struct
+ {
+/* 2*/ u_char segment;
+/* 3*/ u_char flags;
+#define SSD_KEY 0x0F
+#define SSD_ILI 0x20
+#define SSD_EOM 0x40
+#define SSD_FILEMARK 0x80
+/* 7*/ u_char info[4];
+/* 8*/ u_char extra_len;
+/*12*/ u_char cmd_spec_info[4];
+/*13*/ u_char add_sense_code;
+/*14*/ u_char add_sense_code_qual;
+/*15*/ u_char fru;
+/*16*/ u_char sense_key_spec_1;
+#define SSD_SCS_VALID 0x80
+/*17*/ u_char sense_key_spec_2;
+/*18*/ u_char sense_key_spec_3;
+/*32*/ u_char extra_bytes[14];
+ } extended;
+ }ext;
+}; /* total of 32 bytes */
+
+struct blk_desc
+{
+ u_char density;
+ u_char nblocks[3];
+ u_char reserved;
+ u_char blklen[3];
+};
+
+struct scsi_mode_header
+{
+ u_char data_length; /* Sense data length */
+ u_char medium_type;
+ u_char dev_spec;
+ u_char blk_desc_len;
+};
+
+struct scsi_mode_header_big
+{
+ u_char data_length[2]; /* Sense data length */
+ u_char medium_type;
+ u_char dev_spec;
+ u_char unused[2];
+ u_char blk_desc_len[2];
+};
+
+
+/*
+ * Status Byte
+ */
+#define SCSI_OK 0x00
+#define SCSI_CHECK 0x02
+#define SCSI_BUSY 0x08
+#define SCSI_INTERM 0x10
+#endif /*_SCSI_SCSI_ALL_H*/
diff --git a/sys/scsi/scsi_base.c b/sys/scsi/scsi_base.c
new file mode 100644
index 0000000..cb7f009
--- /dev/null
+++ b/sys/scsi/scsi_base.c
@@ -0,0 +1,896 @@
+/*
+ * Written By Julian ELischer
+ * Copyright julian Elischer 1993.
+ * Permission is granted to use or redistribute this file in any way as long
+ * as this notice remains. Julian Elischer does not guarantee that this file
+ * is totally correct for any given task and users of this file must
+ * accept responsibility for any damage that occurs from the application of this
+ * file.
+ *
+ * Written by Julian Elischer (julian@dialix.oz.au)
+ * $Id: scsi_base.c,v 1.8 1994/05/19 22:21:05 jkh Exp $
+ */
+
+#define SPLSD splbio
+#define ESUCCESS 0
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/errno.h>
+#include <vm/vm.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_disk.h>
+#include <scsi/scsiconf.h>
+
+static errval sc_err1(struct scsi_xfer *);
+static errval scsi_interpret_sense(struct scsi_xfer *);
+
+#ifdef NetBSD
+#ifdef DDB
+int Debugger();
+#else /* DDB */
+#define Debugger()
+#endif /* DDB */
+#else /* NetBSD */
+#include "ddb.h"
+#if NDDB > 0
+#else /* NDDB > 0 */
+#define Debugger()
+#endif /* NDDB > 0 */
+#endif
+
+void sc_print_addr __P((struct scsi_link *sc_link));
+
+struct scsi_xfer *next_free_xs;
+
+/*
+ * Get a scsi transfer structure for the caller. Charge the structure
+ * to the device that is referenced by the sc_link structure. If the
+ * sc_link structure has no 'credits' then the device already has the
+ * maximum number or outstanding operations under way. In this stage,
+ * wait on the structure so that when one is freed, we are awoken again
+ * If the SCSI_NOSLEEP flag is set, then do not wait, but rather, return
+ * a NULL pointer, signifying that no slots were available
+ * Note in the link structure, that we are waiting on it.
+ */
+
+struct scsi_xfer *
+get_xs(sc_link, flags)
+ struct scsi_link *sc_link; /* who to charge the xs to */
+ u_int32 flags; /* if this call can sleep */
+{
+ struct scsi_xfer *xs;
+ u_int32 s;
+
+ SC_DEBUG(sc_link, SDEV_DB3, ("get_xs\n"));
+ s = splbio();
+ while (!sc_link->opennings) {
+ SC_DEBUG(sc_link, SDEV_DB3, ("sleeping\n"));
+ if (flags & SCSI_NOSLEEP) {
+ splx(s);
+ return 0;
+ }
+ sc_link->flags |= SDEV_WAITING;
+ tsleep((caddr_t)sc_link, PRIBIO, "scsiget", 0);
+ }
+ sc_link->opennings--;
+ if (xs = next_free_xs) {
+ next_free_xs = xs->next;
+ splx(s);
+ } else {
+ splx(s);
+ SC_DEBUG(sc_link, SDEV_DB3, ("making\n"));
+ xs = malloc(sizeof(*xs), M_TEMP,
+ ((flags & SCSI_NOSLEEP) ? M_NOWAIT : M_WAITOK));
+ if (xs == NULL) {
+ sc_print_addr(sc_link);
+ printf("cannot allocate scsi xs\n");
+ return (NULL);
+ }
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("returning\n"));
+ xs->sc_link = sc_link;
+ return (xs);
+}
+
+/*
+ * Given a scsi_xfer struct, and a device (referenced through sc_link)
+ * return the struct to the free pool and credit the device with it
+ * If another process is waiting for an xs, do a wakeup, let it proceed
+ */
+void
+free_xs(xs, sc_link, flags)
+ struct scsi_xfer *xs;
+ struct scsi_link *sc_link; /* who to credit for returning it */
+ u_int32 flags;
+{
+ xs->next = next_free_xs;
+ next_free_xs = xs;
+
+ SC_DEBUG(sc_link, SDEV_DB3, ("free_xs\n"));
+ /* if was 0 and someone waits, wake them up */
+ if ((!sc_link->opennings++) && (sc_link->flags & SDEV_WAITING)) {
+ sc_link->flags &= ~SDEV_WAITING;
+ wakeup((caddr_t)sc_link); /* remember, it wakes them ALL up */
+ } else {
+ if (sc_link->device->start) {
+ SC_DEBUG(sc_link, SDEV_DB2, ("calling private start()\n"));
+ (*(sc_link->device->start)) (sc_link->dev_unit);
+ }
+ }
+}
+
+/*
+ * Find out from the device what its capacity is.
+ */
+u_int32
+scsi_size(sc_link, flags)
+ struct scsi_link *sc_link;
+ u_int32 flags;
+{
+ struct scsi_read_cap_data rdcap;
+ struct scsi_read_capacity scsi_cmd;
+ u_int32 size;
+
+ /*
+ * make up a scsi command and ask the scsi driver to do
+ * it for you.
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = READ_CAPACITY;
+
+ /*
+ * If the command works, interpret the result as a 4 byte
+ * number of blocks
+ */
+ if (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) & rdcap,
+ sizeof(rdcap),
+ 2,
+ 20000,
+ NULL,
+ flags | SCSI_DATA_IN) != 0) {
+
+ sc_print_addr(sc_link);
+ printf("could not get size\n");
+ return (0);
+ } else {
+ size = rdcap.addr_0 + 1;
+ size += rdcap.addr_1 << 8;
+ size += rdcap.addr_2 << 16;
+ size += rdcap.addr_3 << 24;
+ }
+ return (size);
+}
+
+/*
+ * Get scsi driver to send a "are you ready?" command
+ */
+errval
+scsi_test_unit_ready(sc_link, flags)
+ struct scsi_link *sc_link;
+ u_int32 flags;
+{
+ struct scsi_test_unit_ready scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = TEST_UNIT_READY;
+
+ return (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ 2,
+ 100000,
+ NULL,
+ flags));
+}
+
+/*
+ * Do a scsi operation, asking a device to run as SCSI-II if it can.
+ */
+errval
+scsi_change_def(sc_link, flags)
+ struct scsi_link *sc_link;
+ u_int32 flags;
+{
+ struct scsi_changedef scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = CHANGE_DEFINITION;
+ scsi_cmd.how = SC_SCSI_2;
+
+ return (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ 2,
+ 100000,
+ NULL,
+ flags));
+}
+
+/*
+ * Do a scsi operation asking a device what it is
+ * Use the scsi_cmd routine in the switch table.
+ */
+errval
+scsi_inquire(sc_link, inqbuf, flags)
+ struct scsi_link *sc_link;
+ struct scsi_inquiry_data *inqbuf;
+ u_int32 flags;
+{
+ struct scsi_inquiry scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = INQUIRY;
+ scsi_cmd.length = sizeof(struct scsi_inquiry_data);
+
+ return (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) inqbuf,
+ sizeof(struct scsi_inquiry_data),
+ 2,
+ 100000,
+ NULL,
+ SCSI_DATA_IN | flags));
+}
+
+/*
+ * Prevent or allow the user to remove the media
+ */
+errval
+scsi_prevent(sc_link, type, flags)
+ struct scsi_link *sc_link;
+ u_int32 type, flags;
+{
+ struct scsi_prevent scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = PREVENT_ALLOW;
+ scsi_cmd.how = type;
+ return (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ 2,
+ 5000,
+ NULL,
+ flags));
+}
+
+/*
+ * Get scsi driver to send a "start up" command
+ */
+errval
+scsi_start_unit(sc_link, flags)
+ struct scsi_link *sc_link;
+ u_int32 flags;
+{
+ struct scsi_start_stop scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = START_STOP;
+ scsi_cmd.how = SSS_START;
+
+ return (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ 2,
+ 10000,
+ NULL,
+ flags));
+}
+
+/*
+ * Get scsi driver to send a "stop" command
+ */
+errval
+scsi_stop_unit(sc_link, eject, flags)
+ struct scsi_link *sc_link;
+ u_int32 eject;
+ u_int32 flags;
+{
+ struct scsi_start_stop scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = START_STOP;
+ if (eject) {
+ scsi_cmd.how = SSS_LOEJ;
+ }
+
+ return (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ 2,
+ 10000,
+ NULL,
+ flags));
+}
+
+/*
+ * This routine is called by the scsi interrupt when the transfer is complete.
+ */
+void
+scsi_done(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_link *sc_link = xs->sc_link;
+ struct buf *bp = xs->bp;
+ errval retval;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("scsi_done\n"));
+#ifdef SCSIDEBUG
+ if (sc_link->flags & SDEV_DB1)
+ {
+ show_scsi_cmd(xs);
+ }
+#endif /*SCSIDEBUG */
+ /*
+ * If it's a user level request, bypass all usual completion processing,
+ * let the user work it out.. We take reponsibility for freeing the
+ * xs when the user returns. (and restarting the device's queue).
+ */
+ if (xs->flags & SCSI_USER) {
+ biodone(xs->bp);
+#ifdef NOTNOW
+ SC_DEBUG(sc_link, SDEV_DB3, ("calling user done()\n"));
+ scsi_user_done(xs); /* to take a copy of the sense etc. */
+ SC_DEBUG(sc_link, SDEV_DB3, ("returned from user done()\n "));
+#endif
+ free_xs(xs, sc_link, SCSI_NOSLEEP); /* restarts queue too */
+ SC_DEBUG(sc_link, SDEV_DB3, ("returning to adapter\n"));
+ return;
+ }
+ /*
+ * If the device has it's own done routine, call it first.
+ * If it returns a legit error value, return that, otherwise
+ * it wants us to continue with normal processing.
+ */
+
+ if (sc_link->device->done) {
+ SC_DEBUG(sc_link, SDEV_DB2, ("calling private done()\n"));
+ retval = (*sc_link->device->done) (xs);
+ if (retval == -1) {
+ free_xs(xs, sc_link, SCSI_NOSLEEP); /*XXX */
+ return; /* it did it all, finish up */
+ }
+ if (retval == -2) {
+ return; /* it did it all, finish up */
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("continuing with generic done()\n"));
+ }
+ if ((bp = xs->bp) == NULL) {
+ /*
+ * if it's a normal upper level request, then ask
+ * the upper level code to handle error checking
+ * rather than doing it here at interrupt time
+ */
+ wakeup((caddr_t)xs);
+ return;
+ }
+ /*
+ * Go and handle errors now.
+ * If it returns -1 then we should RETRY
+ */
+ if ((retval = sc_err1(xs)) == -1) {
+ if ((*(sc_link->adapter->scsi_cmd)) (xs)
+ == SUCCESSFULLY_QUEUED) { /* don't wake the job, ok? */
+ return;
+ }
+ xs->flags |= ITSDONE;
+ }
+ free_xs(xs, sc_link, SCSI_NOSLEEP); /* does a start if needed */
+ biodone(bp);
+}
+
+/*
+ * ask the scsi driver to perform a command for us.
+ * tell it where to read/write the data, and how
+ * long the data is supposed to be. If we have a buf
+ * to associate with the transfer, we need that too.
+ */
+errval
+scsi_scsi_cmd(sc_link, scsi_cmd, cmdlen, data_addr, datalen,
+ retries, timeout, bp, flags)
+ struct scsi_link *sc_link;
+ struct scsi_generic *scsi_cmd;
+ u_int32 cmdlen;
+ u_char *data_addr;
+ u_int32 datalen;
+ u_int32 retries;
+ u_int32 timeout;
+ struct buf *bp;
+ u_int32 flags;
+{
+ struct scsi_xfer *xs;
+ errval retval;
+ u_int32 s;
+
+ if (bp) flags |= SCSI_NOSLEEP;
+ SC_DEBUG(sc_link, SDEV_DB2, ("scsi_cmd\n"));
+
+ xs = get_xs(sc_link, flags); /* should wait unless booting */
+ if (!xs) return (ENOMEM);
+ /*
+ * Fill out the scsi_xfer structure. We don't know whose context
+ * the cmd is in, so copy it.
+ */
+ bcopy(scsi_cmd, &(xs->cmdstore), cmdlen);
+ xs->flags = INUSE | flags;
+ xs->sc_link = sc_link;
+ xs->retries = retries;
+ xs->timeout = timeout;
+ xs->cmd = &xs->cmdstore;
+ xs->cmdlen = cmdlen;
+ xs->data = data_addr;
+ xs->datalen = datalen;
+ xs->resid = datalen;
+ xs->bp = bp;
+/*XXX*/ /*use constant not magic number */
+ if (datalen && ((caddr_t) data_addr < (caddr_t) KERNBASE)) {
+ if (bp) {
+ printf("Data buffered space not in kernel context\n");
+#ifdef SCSIDEBUG
+ show_scsi_cmd(xs);
+#endif /* SCSIDEBUG */
+ retval = EFAULT;
+ goto bad;
+ }
+#ifdef NOBOUNCE
+ xs->data = malloc(datalen, M_TEMP, M_WAITOK);
+#else
+ xs->data = (caddr_t) vm_bounce_kva_alloc( (datalen + PAGE_SIZE - 1)/PAGE_SIZE);
+#endif
+ /* I think waiting is ok *//*XXX */
+ switch ((int)(flags & (SCSI_DATA_IN | SCSI_DATA_OUT))) {
+ case 0:
+ printf("No direction flags, assuming both\n");
+#ifdef SCSIDEBUG
+ show_scsi_cmd(xs);
+#endif /* SCSIDEBUG */
+ case SCSI_DATA_IN | SCSI_DATA_OUT: /* weird */
+ case SCSI_DATA_OUT:
+ bcopy(data_addr, xs->data, datalen);
+ break;
+ case SCSI_DATA_IN:
+ bzero(xs->data, datalen);
+ }
+ }
+retry:
+ xs->error = XS_NOERROR;
+#ifdef PARANOID
+ if (datalen && ((caddr_t) xs->data < (caddr_t) KERNBASE)) {
+ printf("It's still wrong!\n");
+ }
+#endif /*PARANOID*/
+#ifdef SCSIDEBUG
+ if (sc_link->flags & SDEV_DB3) show_scsi_xs(xs);
+#endif /* SCSIDEBUG */
+ /*
+ * Do the transfer. If we are polling we will return:
+ * COMPLETE, Was poll, and scsi_done has been called
+ * TRY_AGAIN_LATER, Adapter short resources, try again
+ *
+ * if under full steam (interrupts) it will return:
+ * SUCCESSFULLY_QUEUED, will do a wakeup when complete
+ * TRY_AGAIN_LATER, (as for polling)
+ * After the wakeup, we must still check if it succeeded
+ *
+ * If we have a bp however, all the error proccessing
+ * and the buffer code both expect us to return straight
+ * to them, so as soon as the command is queued, return
+ */
+
+ retval = (*(sc_link->adapter->scsi_cmd)) (xs);
+
+ switch (retval) {
+ case SUCCESSFULLY_QUEUED:
+ if (bp)
+ return retval; /* will sleep (or not) elsewhere */
+ s = splbio();
+ while (!(xs->flags & ITSDONE)) {
+ tsleep((caddr_t)xs, PRIBIO + 1, "scsicmd", 0);
+ }
+ splx(s);
+ /* fall through to check success of completed command */
+ case COMPLETE: /* Polling command completed ok */
+/*XXX*/ case HAD_ERROR: /* Polling command completed with error */
+ SC_DEBUG(sc_link, SDEV_DB3, ("back in cmd()\n"));
+ if ((retval = sc_err1(xs)) == -1)
+ goto retry;
+ break;
+
+ case TRY_AGAIN_LATER: /* adapter resource shortage */
+ SC_DEBUG(sc_link, SDEV_DB3, ("will try again \n"));
+ /* should sleep 1 sec here */
+ if (xs->retries--) {
+ xs->flags &= ~ITSDONE;
+ goto retry;
+ }
+ default:
+ retval = EIO;
+ }
+ /*
+ * If we had to copy the data out of the user's context,
+ * then do the other half (copy it back or whatever)
+ * and free the memory buffer
+ */
+ if (datalen && (xs->data != data_addr)) {
+ switch ((int)(flags & (SCSI_DATA_IN | SCSI_DATA_OUT))) {
+ case 0:
+ case SCSI_DATA_IN | SCSI_DATA_OUT: /* weird */
+ case SCSI_DATA_IN:
+ bcopy(xs->data, data_addr, datalen);
+ break;
+ }
+#ifdef NOBOUNCE
+ free(xs->data, M_TEMP);
+#else
+ vm_bounce_kva_alloc_free(xs->data, (datalen + PAGE_SIZE - 1)/PAGE_SIZE, 0);
+#endif
+ }
+ /*
+ * we have finished with the xfer stuct, free it and
+ * check if anyone else needs to be started up.
+ */
+bad:
+ free_xs(xs, sc_link, flags); /* includes the 'start' op */
+ if (bp && retval) {
+ bp->b_error = retval;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ }
+ return (retval);
+}
+
+static errval
+sc_err1(xs)
+ struct scsi_xfer *xs;
+{
+ struct buf *bp = xs->bp;
+ errval retval;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("sc_err1,err = 0x%x \n", xs->error));
+ /*
+ * If it has a buf, we might be working with
+ * a request from the buffer cache or some other
+ * piece of code that requires us to process
+ * errors at inetrrupt time. We have probably
+ * been called by scsi_done()
+ */
+ switch ((int)xs->error) {
+ case XS_NOERROR: /* nearly always hit this one */
+ retval = ESUCCESS;
+ if (bp) {
+ bp->b_error = 0;
+ bp->b_resid = 0;
+ }
+ break;
+
+ case XS_SENSE:
+ if (bp) {
+ bp->b_error = 0;
+ bp->b_resid = 0;
+ if (retval = (scsi_interpret_sense(xs))) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = retval;
+ bp->b_resid = bp->b_bcount;
+ }
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("scsi_interpret_sense (bp) returned %d\n", retval));
+ } else {
+ retval = (scsi_interpret_sense(xs));
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("scsi_interpret_sense (no bp) returned %d\n", retval));
+ }
+ break;
+
+ case XS_BUSY:
+ /*should somehow arange for a 1 sec delay here (how?) */
+ /* XXX tsleep(&localvar, priority, "foo", hz);
+ that's how! */
+ case XS_TIMEOUT:
+ /*
+ * If we can, resubmit it to the adapter.
+ */
+ if (xs->retries--) {
+ xs->error = XS_NOERROR;
+ xs->flags &= ~ITSDONE;
+ goto retry;
+ }
+ /* fall through */
+ case XS_DRIVER_STUFFUP:
+ if (bp) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ }
+ retval = EIO;
+ break;
+ default:
+ retval = EIO;
+ sc_print_addr(xs->sc_link);
+ printf("unknown error category from scsi driver\n");
+ }
+ return retval;
+retry:
+ return (-1);
+}
+
+/*
+ * Look at the returned sense and act on the error, determining
+ * the unix error number to pass back. (0 = report no error)
+ *
+ * THIS IS THE DEFAULT ERROR HANDLER
+ */
+static errval
+scsi_interpret_sense(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_sense_data *sense;
+ struct scsi_link *sc_link = xs->sc_link;
+ u_int32 key;
+ u_int32 silent;
+ u_int32 info;
+ errval errcode;
+
+ static char *error_mes[] =
+ {"soft error (corrected)",
+ "not ready", "medium error",
+ "non-media hardware failure", "illegal request",
+ "unit attention", "readonly device",
+ "no data found", "vendor unique",
+ "copy aborted", "command aborted",
+ "search returned equal", "volume overflow",
+ "verify miscompare", "unknown error key"
+ };
+
+ /*
+ * If the flags say errs are ok, then always return ok.
+ */
+ if (xs->flags & SCSI_ERR_OK)
+ return (ESUCCESS);
+
+ sense = &(xs->sense);
+#ifdef SCSIDEBUG
+ if (sc_link->flags & SDEV_DB1) {
+ u_int32 count = 0;
+ printf("code%x valid%x ",
+ sense->error_code & SSD_ERRCODE,
+ sense->error_code & SSD_ERRCODE_VALID ? 1 : 0);
+ printf("seg%x key%x ili%x eom%x fmark%x\n",
+ sense->ext.extended.segment,
+ sense->ext.extended.flags & SSD_KEY,
+ sense->ext.extended.flags & SSD_ILI ? 1 : 0,
+ sense->ext.extended.flags & SSD_EOM ? 1 : 0,
+ sense->ext.extended.flags & SSD_FILEMARK ? 1 : 0);
+ printf("info: %x %x %x %x followed by %d extra bytes\n",
+ sense->ext.extended.info[0],
+ sense->ext.extended.info[1],
+ sense->ext.extended.info[2],
+ sense->ext.extended.info[3],
+ sense->ext.extended.extra_len);
+ printf("extra: ");
+ while (count < sense->ext.extended.extra_len) {
+ printf("%x ", sense->ext.extended.extra_bytes[count++]);
+ }
+ printf("\n");
+ }
+#endif /*SCSIDEBUG */
+ /*
+ * If the device has it's own error handler, call it first.
+ * If it returns a legit error value, return that, otherwise
+ * it wants us to continue with normal error processing.
+ */
+ if (sc_link->device->err_handler) {
+ SC_DEBUG(sc_link, SDEV_DB2, ("calling private err_handler()\n"));
+ errcode = (*sc_link->device->err_handler) (xs);
+ if (errcode != -1)
+ return errcode; /* errcode >= 0 better ? */
+ }
+ /* otherwise use the default */
+ silent = (xs->flags & SCSI_SILENT);
+ switch (sense->error_code & SSD_ERRCODE) {
+ /*
+ * If it's code 70, use the extended stuff and interpret the key
+ */
+ case 0x71: /* delayed error */
+ sc_print_addr(sc_link);
+ key = sense->ext.extended.flags & SSD_KEY;
+ printf(" DELAYED ERROR, key = 0x%x\n", key);
+ case 0x70:
+ if (sense->error_code & SSD_ERRCODE_VALID) {
+ info = ntohl(*((long *) sense->ext.extended.info));
+ } else {
+ info = 0;
+ }
+ key = sense->ext.extended.flags & SSD_KEY;
+
+ if (key && !silent) {
+ sc_print_addr(sc_link);
+ printf("%s", error_mes[key - 1]);
+ if (sense->error_code & SSD_ERRCODE_VALID) {
+ switch ((int)key) {
+ case 0x2: /* NOT READY */
+ case 0x5: /* ILLEGAL REQUEST */
+ case 0x6: /* UNIT ATTENTION */
+ case 0x7: /* DATA PROTECT */
+ break;
+ case 0x8: /* BLANK CHECK */
+ printf(", requested size: %d (decimal)",
+ info);
+ break;
+ default:
+ printf(", info = %d (decimal)", info);
+ }
+ }
+ printf("\n");
+ }
+ switch ((int)key) {
+ case 0x0: /* NO SENSE */
+ case 0x1: /* RECOVERED ERROR */
+ if (xs->resid == xs->datalen)
+ xs->resid = 0; /* not short read */
+ case 0xc: /* EQUAL */
+ return (ESUCCESS);
+ case 0x2: /* NOT READY */
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+ return (EBUSY);
+ case 0x5: /* ILLEGAL REQUEST */
+ return (EINVAL);
+ case 0x6: /* UNIT ATTENTION */
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+ if (sc_link->flags & SDEV_OPEN) {
+ return (EIO);
+ } else {
+ return 0;
+ }
+ case 0x7: /* DATA PROTECT */
+ return (EACCES);
+ case 0xd: /* VOLUME OVERFLOW */
+ return (ENOSPC);
+ case 0x8: /* BLANK CHECK */
+ return (ESUCCESS);
+ default:
+ return (EIO);
+ }
+ /*
+ * Not code 70, just report it
+ */
+ default:
+ if (!silent) {
+ sc_print_addr(sc_link);
+ printf("error code %d",
+ sense->error_code & SSD_ERRCODE);
+ if (sense->error_code & SSD_ERRCODE_VALID) {
+ printf(" at block no. %d (decimal)",
+ (sense->ext.unextended.blockhi << 16) +
+ (sense->ext.unextended.blockmed << 8) +
+ (sense->ext.unextended.blocklow));
+ }
+ printf("\n");
+ }
+ return (EIO);
+ }
+}
+
+/*
+ * Utility routines often used in SCSI stuff
+ */
+
+/*
+ * convert a physical address to 3 bytes,
+ * MSB at the lowest address,
+ * LSB at the highest.
+ */
+void
+lto3b(val, bytes)
+ int val;
+ u_char *bytes;
+{
+ *bytes++ = (val & 0xff0000) >> 16;
+ *bytes++ = (val & 0xff00) >> 8;
+ *bytes = val & 0xff;
+}
+
+/*
+ * The reverse of lto3b
+ */
+int
+_3btol(bytes)
+ u_char *bytes;
+{
+ u_int32 rc;
+ rc = (*bytes++ << 16);
+ rc += (*bytes++ << 8);
+ rc += *bytes;
+ return ((int) rc);
+}
+
+/*
+ * Print out the scsi_link structure's address info.
+ */
+
+void
+sc_print_addr(sc_link)
+ struct scsi_link *sc_link;
+{
+
+ printf("%s%d(%s%d:%d:%d): ", sc_link->device->name, sc_link->dev_unit,
+ sc_link->adapter->name, sc_link->adapter_unit,
+ sc_link->target, sc_link->lun);
+}
+#ifdef SCSIDEBUG
+/*
+ * Given a scsi_xfer, dump the request, in all it's glory
+ */
+void
+show_scsi_xs(xs)
+ struct scsi_xfer *xs;
+{
+ printf("xs(0x%x): ", xs);
+ printf("flg(0x%x)", xs->flags);
+ printf("sc_link(0x%x)", xs->sc_link);
+ printf("retr(0x%x)", xs->retries);
+ printf("timo(0x%x)", xs->timeout);
+ printf("cmd(0x%x)", xs->cmd);
+ printf("len(0x%x)", xs->cmdlen);
+ printf("data(0x%x)", xs->data);
+ printf("len(0x%x)", xs->datalen);
+ printf("res(0x%x)", xs->resid);
+ printf("err(0x%x)", xs->error);
+ printf("bp(0x%x)", xs->bp);
+ show_scsi_cmd(xs);
+}
+
+void
+show_scsi_cmd(struct scsi_xfer *xs)
+{
+ u_char *b = (u_char *) xs->cmd;
+ int i = 0;
+
+ sc_print_addr(xs->sc_link);
+ printf("command: ");
+
+ if (!(xs->flags & SCSI_RESET)) {
+ while (i < xs->cmdlen) {
+ if (i)
+ printf(",");
+ printf("%x", b[i++]);
+ }
+ printf("-[%d bytes]\n", xs->datalen);
+ if (xs->datalen)
+ show_mem(xs->data, min(64, xs->datalen));
+ } else {
+ printf("-RESET-\n");
+ }
+}
+
+void
+show_mem(address, num)
+ unsigned char *address;
+ u_int32 num;
+{
+ u_int32 x, y;
+ printf("------------------------------");
+ for (y = 0; y < num; y += 1) {
+ if (!(y % 16))
+ printf("\n%03d: ", y);
+ printf("%02x ", *address++);
+ }
+ printf("\n------------------------------\n");
+}
+#endif /*SCSIDEBUG */
diff --git a/sys/scsi/scsi_cd.h b/sys/scsi/scsi_cd.h
new file mode 100644
index 0000000..0a4759b
--- /dev/null
+++ b/sys/scsi/scsi_cd.h
@@ -0,0 +1,229 @@
+/*
+ * Written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ *
+ * $Id: scsi_cd.h,v 1.6 93/08/26 21:09:19 julian Exp Locker: julian $
+ */
+#ifndef _SCSI_SCSI_CD_H
+#define _SCSI_SCSI_CD_H 1
+
+/*
+ * Define two bits always in the same place in byte 2 (flag byte)
+ */
+#define CD_RELADDR 0x01
+#define CD_MSF 0x02
+
+/*
+ * SCSI command format
+ */
+
+struct scsi_read_capacity_cd
+{
+ u_char op_code;
+ u_char byte2;
+ u_char addr_3; /* Most Significant */
+ u_char addr_2;
+ u_char addr_1;
+ u_char addr_0; /* Least Significant */
+ u_char unused[3];
+ u_char control;
+};
+
+struct scsi_pause
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[6];
+ u_char resume;
+ u_char control;
+};
+#define PA_PAUSE 1
+#define PA_RESUME 0
+
+struct scsi_play_msf
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused;
+ u_char start_m;
+ u_char start_s;
+ u_char start_f;
+ u_char end_m;
+ u_char end_s;
+ u_char end_f;
+ u_char control;
+};
+
+struct scsi_play_track
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[2];
+ u_char start_track;
+ u_char start_index;
+ u_char unused1;
+ u_char end_track;
+ u_char end_index;
+ u_char control;
+};
+
+struct scsi_play
+{
+ u_char op_code;
+ u_char byte2;
+ u_char blk_addr[4];
+ u_char unused;
+ u_char xfer_len[2];
+ u_char control;
+};
+
+struct scsi_play_big
+{
+ u_char op_code;
+ u_char byte2; /* same as above */
+ u_char blk_addr[4];
+ u_char xfer_len[4];
+ u_char unused;
+ u_char control;
+};
+
+struct scsi_play_rel_big
+{
+ u_char op_code;
+ u_char byte2; /* same as above */
+ u_char blk_addr[4];
+ u_char xfer_len[4];
+ u_char track;
+ u_char control;
+};
+
+struct scsi_read_header
+{
+ u_char op_code;
+ u_char byte2;
+ u_char blk_addr[4];
+ u_char unused;
+ u_char data_len[2];
+ u_char control;
+};
+
+struct scsi_read_subchannel
+{
+ u_char op_code;
+ u_char byte2;
+ u_char byte3;
+#define SRS_SUBQ 0x40
+ u_char subchan_format;
+ u_char unused[2];
+ u_char track;
+ u_char data_len[2];
+ u_char control;
+};
+
+struct scsi_read_toc
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[4];
+ u_char from_track;
+ u_char data_len[2];
+ u_char control;
+};
+;
+
+struct scsi_read_cd_capacity
+{
+ u_char op_code;
+ u_char byte2;
+ u_char addr_3; /* Most Significant */
+ u_char addr_2;
+ u_char addr_1;
+ u_char addr_0; /* Least Significant */
+ u_char unused[3];
+ u_char control;
+};
+
+/*
+ * Opcodes
+ */
+
+#define READ_CD_CAPACITY 0x25 /* slightly different from disk */
+#define READ_SUBCHANNEL 0x42 /* cdrom read Subchannel */
+#define READ_TOC 0x43 /* cdrom read TOC */
+#define READ_HEADER 0x44 /* cdrom read header */
+#define PLAY 0x45 /* cdrom play 'play audio' mode */
+#define PLAY_MSF 0x47 /* cdrom play Min,Sec,Frames mode */
+#define PLAY_TRACK 0x48 /* cdrom play track/index mode */
+#define PLAY_TRACK_REL 0x49 /* cdrom play track/index mode */
+#define PAUSE 0x4b /* cdrom pause in 'play audio' mode */
+#define PLAY_BIG 0xa5 /* cdrom pause in 'play audio' mode */
+#define PLAY_TRACK_REL_BIG 0xa9 /* cdrom play track/index mode */
+
+
+
+struct scsi_read_cd_cap_data
+{
+ u_char addr_3; /* Most significant */
+ u_char addr_2;
+ u_char addr_1;
+ u_char addr_0; /* Least significant */
+ u_char length_3; /* Most significant */
+ u_char length_2;
+ u_char length_1;
+ u_char length_0; /* Least significant */
+};
+
+union cd_pages
+{
+ struct audio_page
+ {
+ u_char page_code;
+#define CD_PAGE_CODE 0x3F
+#define AUDIO_PAGE 0x0e
+#define CD_PAGE_PS 0x80
+ u_char param_len;
+ u_char flags;
+#define CD_PA_SOTC 0x02
+#define CD_PA_IMMED 0x04
+ u_char unused[2];
+ u_char format_lba;
+#define CD_PA_FORMAT_LBA 0x0F
+#define CD_PA_APR_VALID 0x80
+ u_char lb_per_sec[2];
+ struct port_control
+ {
+ u_char channels;
+#define CHANNEL 0x0F
+#define CHANNEL_0 1
+#define CHANNEL_1 2
+#define CHANNEL_2 4
+#define CHANNEL_3 8
+#define LEFT_CHANNEL CHANNEL_0
+#define RIGHT_CHANNEL CHANNEL_1
+ u_char volume;
+ } port[4];
+#define LEFT_PORT 0
+#define RIGHT_PORT 1
+ }audio;
+};
+
+struct cd_mode_data
+{
+ struct scsi_mode_header header;
+ struct blk_desc blk_desc;
+ union cd_pages page;
+};
+#endif /*_SCSI_SCSI_CD_H*/
+
diff --git a/sys/scsi/scsi_changer.h b/sys/scsi/scsi_changer.h
new file mode 100644
index 0000000..85819c8
--- /dev/null
+++ b/sys/scsi/scsi_changer.h
@@ -0,0 +1,98 @@
+/*
+ * SCSI changer interface description
+ */
+
+/*
+ * Written by Stefan Grefen (grefen@goofy.zdv.uni-mainz.de soon grefen@convex.com)
+ * based on the SCSI System by written Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ *
+ * $Id: scsi_changer.h,v 1.5 93/08/26 21:09:22 julian Exp Locker: julian $
+ */
+#ifndef _SCSI_SCSI_CHANGER_H
+#define _SCSI_SCSI_CHANGER_H 1
+
+/*
+ * SCSI command format
+ */
+struct scsi_read_element_status
+{
+ u_char op_code;
+ u_char byte2;
+#define SRES_ELEM_TYPE_CODE 0x0F
+#define SRES_ELEM_VOLTAG 0x10
+ u_char starting_element_addr[2];
+ u_char number_of_elements[2];
+ u_char resv1;
+ u_char allocation_length[3];
+ u_char resv2;
+ u_char control;
+};
+#define RE_ALL_ELEMENTS 0
+#define RE_MEDIUM_TRANSPORT_ELEMENT 1
+#define RE_STORAGE_ELEMENT 2
+#define RE_IMPORT_EXPORT 3
+#define RE_DATA_TRANSFER_ELEMENT 4
+
+struct scsi_move_medium
+{
+ u_char op_code;
+ u_char byte2;
+ u_char transport_element_address[2];
+ u_char source_address[2];
+ u_char destination_address[2];
+ u_char rsvd[2];
+ u_char invert;
+ u_char control;
+};
+
+struct scsi_position_to_element
+{
+ u_char op_code;
+ u_char byte2;
+ u_char transport_element_address[2];
+ u_char source_address[2];
+ u_char rsvd[2];
+ u_char invert;
+ u_char control;
+};
+
+/*
+ * Opcodes
+ */
+#define POSITION_TO_ELEMENT 0x2b
+#define MOVE_MEDIUM 0xa5
+#define READ_ELEMENT_STATUS 0xb8
+
+struct scsi_element_status_data
+{
+ u_char first_element_reported[2];
+ u_char number_of_elements_reported[2];
+ u_char rsvd;
+ u_char byte_count_of_report[3];
+};
+
+struct element_status_page
+{
+ u_char element_type_code;
+ u_char flags;
+#define ESP_AVOLTAG 0x40
+#define ESP_PVOLTAG 0x80
+ u_char element_descriptor_length[2];
+ u_char rsvd;
+ u_char byte_count_of_descriptor_data[3];
+};
+#endif /*_SCSI_SCSI_CHANGER_H*/
+
diff --git a/sys/scsi/scsi_debug.h b/sys/scsi/scsi_debug.h
new file mode 100644
index 0000000..480ff14
--- /dev/null
+++ b/sys/scsi/scsi_debug.h
@@ -0,0 +1,53 @@
+/*#define SCSIDEBUG 1*/
+/*
+ * Written by Julian Elischer (julian@tfs.com)
+ *
+ * $Id: scsi_debug.h,v 1.3 93/10/10 09:26:05 julian Exp Locker: julian $
+ */
+#ifndef _SCSI_SCSI_DEBUG_H
+#define _SCSI_SCSI_DEBUG_H 1
+
+/*
+ * These are the new debug bits. (Sat Oct 2 12:46:46 WST 1993)
+ * the following DEBUG bits are defined to exist in the flags word of
+ * the scsi_link structure.
+ */
+#define SDEV_DB1 0x10 /* scsi commands, errors, data */
+#define SDEV_DB2 0x20 /* routine flow tracking */
+#define SDEV_DB3 0x40 /* internal to routine flows */
+#define SDEV_DB4 0x80 /* level 4 debugging for this dev */
+
+/* target and LUN we want to debug */
+#define DEBUGTARG 9 /*9 = dissable*/
+#define DEBUGLUN 0
+#define DEBUGLEVEL (SDEV_DB1|SDEV_DB2)
+
+/*
+ * This is the usual debug macro for use with the above bits
+ */
+#ifdef SCSIDEBUG
+#define SC_DEBUG(sc_link,Level,Printstuff) \
+ if((sc_link)->flags & (Level)) \
+ { \
+ printf("%s%d(%s%d:%d:%d): ", \
+ sc_link->device->name, \
+ sc_link->dev_unit, \
+ sc_link->adapter->name, \
+ sc_link->adapter_unit, \
+ sc_link->target, \
+ sc_link->lun); \
+ printf Printstuff; \
+ }
+#define SC_DEBUGN(sc_link,Level,Printstuff) \
+ if((sc_link)->flags & (Level)) \
+ { \
+ printf Printstuff; \
+ }
+#else
+#define SC_DEBUG(A,B,C) /* not included */
+#define SC_DEBUGN(A,B,C) /* not included */
+#endif
+
+#endif /*_SCSI_SCSI_DEBUG_H*/
+/* END OF FILE */
+
diff --git a/sys/scsi/scsi_disk.h b/sys/scsi/scsi_disk.h
new file mode 100644
index 0000000..60d0bcc
--- /dev/null
+++ b/sys/scsi/scsi_disk.h
@@ -0,0 +1,216 @@
+/*
+ * SCSI interface description
+ */
+
+/*
+ * Some lines of this file come from a file of the name "scsi.h"
+ * distributed by OSF as part of mach2.5,
+ * so the following disclaimer has been kept.
+ *
+ * Copyright 1990 by Open Software Foundation,
+ * Grenoble, FRANCE
+ *
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OSF or Open Software
+ * Foundation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission.
+ *
+ * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+ * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Largely written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ *
+ * $Id: scsi_disk.h,v 1.4 93/08/26 21:09:23 julian Exp Locker: julian $
+ */
+
+/*
+ * SCSI command format
+ */
+
+#ifndef _SCSI_SCSI_DISK_H
+#define _SCSI_SCSI_DISK_H 1
+
+struct scsi_reassign_blocks
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[3];
+ u_char control;
+};
+
+struct scsi_rw
+{
+ u_char op_code;
+ u_char addr_2; /* Most significant */
+#define SRW_TOPADDR 0x1F /* only 5 bits here */
+ u_char addr_1;
+ u_char addr_0; /* least significant */
+ u_char length;
+ u_char control;
+};
+
+struct scsi_rw_big
+{
+ u_char op_code;
+ u_char byte2;
+#define SRWB_RELADDR 0x01
+ u_char addr_3; /* Most significant */
+ u_char addr_2;
+ u_char addr_1;
+ u_char addr_0; /* least significant */
+ u_char reserved;;
+ u_char length2;
+ u_char length1;
+ u_char control;
+};
+
+struct scsi_read_capacity
+{
+ u_char op_code;
+ u_char byte2;
+ u_char addr_3; /* Most Significant */
+ u_char addr_2;
+ u_char addr_1;
+ u_char addr_0; /* Least Significant */
+ u_char unused[3];
+ u_char control;
+};
+
+struct scsi_start_stop
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[2];
+ u_char how;
+#define SSS_START 0x01
+#define SSS_LOEJ 0x02
+ u_char control;
+};
+
+
+
+/*
+ * Opcodes
+ */
+
+#define REASSIGN_BLOCKS 0x07
+#define READ_COMMAND 0x08
+#define WRITE_COMMAND 0x0a
+#define MODE_SELECT 0x15
+#define MODE_SENSE 0x1a
+#define START_STOP 0x1b
+#define PREVENT_ALLOW 0x1e
+#define READ_CAPACITY 0x25
+#define READ_BIG 0x28
+#define WRITE_BIG 0x2a
+
+
+
+struct scsi_read_cap_data
+{
+ u_char addr_3; /* Most significant */
+ u_char addr_2;
+ u_char addr_1;
+ u_char addr_0; /* Least significant */
+ u_char length_3; /* Most significant */
+ u_char length_2;
+ u_char length_1;
+ u_char length_0; /* Least significant */
+};
+
+struct scsi_reassign_blocks_data
+{
+ u_char reserved[2];
+ u_char length_msb;
+ u_char length_lsb;
+ struct
+ {
+ u_char dlbaddr_3; /* defect logical block address (MSB) */
+ u_char dlbaddr_2;
+ u_char dlbaddr_1;
+ u_char dlbaddr_0; /* defect logical block address (LSB) */
+ } defect_descriptor[1];
+};
+
+union disk_pages /* this is the structure copied from osf */
+{
+ struct page_disk_format {
+ u_char pg_code; /* page code (should be 3) */
+#define DISK_PGCODE 0x3F /* only 6 bits valid */
+ u_char pg_length; /* page length (should be 0x16) */
+ u_char trk_z_1; /* tracks per zone (MSB) */
+ u_char trk_z_0; /* tracks per zone (LSB) */
+ u_char alt_sec_1; /* alternate sectors per zone (MSB) */
+ u_char alt_sec_0; /* alternate sectors per zone (LSB) */
+ u_char alt_trk_z_1; /* alternate tracks per zone (MSB) */
+ u_char alt_trk_z_0; /* alternate tracks per zone (LSB) */
+ u_char alt_trk_v_1; /* alternate tracks per volume (MSB) */
+ u_char alt_trk_v_0; /* alternate tracks per volume (LSB) */
+ u_char ph_sec_t_1; /* physical sectors per track (MSB) */
+ u_char ph_sec_t_0; /* physical sectors per track (LSB) */
+ u_char bytes_s_1; /* bytes per sector (MSB) */
+ u_char bytes_s_0; /* bytes per sector (LSB) */
+ u_char interleave_1;/* interleave (MSB) */
+ u_char interleave_0;/* interleave (LSB) */
+ u_char trk_skew_1; /* track skew factor (MSB) */
+ u_char trk_skew_0; /* track skew factor (LSB) */
+ u_char cyl_skew_1; /* cylinder skew (MSB) */
+ u_char cyl_skew_0; /* cylinder skew (LSB) */
+ u_char flags; /* various */
+#define DISK_FMT_SURF 0x10
+#define DISK_FMT_RMB 0x20
+#define DISK_FMT_HSEC 0x40
+#define DISK_FMT_SSEC 0x80
+ u_char reserved2;
+ u_char reserved3;
+ } disk_format;
+ struct page_rigid_geometry {
+ u_char pg_code; /* page code (should be 4) */
+ u_char pg_length; /* page length (should be 0x16) */
+ u_char ncyl_2; /* number of cylinders (MSB) */
+ u_char ncyl_1; /* number of cylinders */
+ u_char ncyl_0; /* number of cylinders (LSB) */
+ u_char nheads; /* number of heads */
+ u_char st_cyl_wp_2; /* starting cyl., write precomp (MSB) */
+ u_char st_cyl_wp_1; /* starting cyl., write precomp */
+ u_char st_cyl_wp_0; /* starting cyl., write precomp (LSB) */
+ u_char st_cyl_rwc_2;/* starting cyl., red. write cur (MSB)*/
+ u_char st_cyl_rwc_1;/* starting cyl., red. write cur */
+ u_char st_cyl_rwc_0;/* starting cyl., red. write cur (LSB)*/
+ u_char driv_step_1; /* drive step rate (MSB) */
+ u_char driv_step_0; /* drive step rate (LSB) */
+ u_char land_zone_2; /* landing zone cylinder (MSB) */
+ u_char land_zone_1; /* landing zone cylinder */
+ u_char land_zone_0; /* landing zone cylinder (LSB) */
+ u_char reserved1;
+ u_char reserved2;
+ u_char reserved3;
+ } rigid_geometry;
+} ;
+#endif /* _SCSI_SCSI_DISK_H*/
diff --git a/sys/scsi/scsi_generic.h b/sys/scsi/scsi_generic.h
new file mode 100644
index 0000000..44f2bd1
--- /dev/null
+++ b/sys/scsi/scsi_generic.h
@@ -0,0 +1,63 @@
+/*
+ * Contributed by HD Associates (hd@world.std.com).
+ * Copyright (c) 1992, 1993 HD Associates
+ *
+ * Berkeley style copyright. I've just snarfed it out of stdio.h:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)stdio.h 5.17 (Berkeley) 6/3/91
+ * $Id$
+ */
+
+/* generic SCSI header file. We use the same minor number format
+ * as on SGI except that the flag bits aren't available because they
+ * are used as the board index.
+ *
+ * The minor number format is:
+ * FF UUU III (FFUU UIII)
+ *
+ * Where:
+ * FF is the board index
+ * UUU are the LUN
+ * III is the SCSI ID (controller)
+ */
+
+#ifndef _SCSI_GENERIC_H_
+#define _SCSI_GENERIC_H_
+
+#define G_SCSI_FLAG(DEV) (((DEV) & 0xC0) >> 6)
+#define G_SCSI_UNIT(DEV) G_SCSI_FLAG(DEV)
+#define G_SCSI_LUN(DEV) (((DEV) & 0x38) >> 3)
+#define G_SCSI_ID(DEV) ((DEV) & 0x7)
+
+#define G_SCSI_MINOR(FLAG, LUN, ID) \
+ (((FLAG) << 6) | ((LUN) << 3) | (ID))
+
+#endif /* _SCSI_GENERIC_H_ */
diff --git a/sys/scsi/scsi_ioctl.c b/sys/scsi/scsi_ioctl.c
new file mode 100644
index 0000000..d35ad2d
--- /dev/null
+++ b/sys/scsi/scsi_ioctl.c
@@ -0,0 +1,332 @@
+/*
+ * Contributed by HD Associates (hd@world.std.com).
+ * Copyright (c) 1992, 1993 HD Associates
+ *
+ * Berkeley style copyright.
+ *
+ *
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#define b_screq b_driver1 /* a patch in buf.h */
+#define b_sc_link b_driver2 /* a patch in buf.h */
+#include <sys/proc.h>
+#include <vm/vm.h>
+
+#include "scbus.h"
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#include <sys/scsiio.h>
+
+void scsierr(struct buf *, int); /* XXX ??? */
+
+/*
+ * We let the user interpret his own sense in the generic scsi world.
+ * This routine is called at interrupt time if the SCSI_USER bit was set
+ * in the flags passed to scsi_scsi_cmd(). No other completion processing
+ * takes place, even if we are running over another device driver.
+ * The lower level routines that call us here, will free the xs and restart
+ * the device's queue if such exists.
+ */
+#ifndef min
+#define min(A,B) ((A<B) ? A : B )
+#endif
+
+void scsi_user_done(xs)
+struct scsi_xfer *xs;
+{
+
+ struct buf *bp;
+ scsireq_t *screq;
+
+ bp = xs->bp;
+ if(!bp) { /* ALL user requests must have a buf */
+ sc_print_addr(xs->sc_link);
+ printf("User command with no buf\n");
+ return ;
+ }
+ screq = bp->b_screq;
+ if (!screq) { /* Is it one of ours? (the SCSI_USER bit says it is) */
+ sc_print_addr(xs->sc_link);
+ printf("User command with no request\n");
+ return ;
+ }
+
+ SC_DEBUG(xs->sc_link,SDEV_DB2,("user-done\n"));
+ screq->retsts = 0;
+ screq->status = xs->status;
+ switch((int)xs->error) {
+ case XS_NOERROR:
+ SC_DEBUG(xs->sc_link,SDEV_DB3,("no error\n"));
+ screq->datalen_used = xs->datalen - xs->resid; /* probably rubbish */
+ screq->retsts = SCCMD_OK;
+ break;
+
+ case XS_SENSE:
+ SC_DEBUG(xs->sc_link,SDEV_DB3,("have sense\n"));
+ screq->senselen_used = min(sizeof(xs->sense),SENSEBUFLEN);
+ bcopy(&xs->sense,screq->sense,screq->senselen);
+ screq->retsts = SCCMD_SENSE;
+ break;
+
+ case XS_DRIVER_STUFFUP:
+ sc_print_addr(xs->sc_link);
+ printf("host adapter code inconsistency\n");
+ screq->retsts = SCCMD_UNKNOWN;
+ break;
+
+ case XS_TIMEOUT:
+ SC_DEBUG(xs->sc_link,SDEV_DB3,("timeout\n"));
+ screq->retsts = SCCMD_TIMEOUT;
+ break;
+
+ case XS_BUSY:
+ SC_DEBUG(xs->sc_link,SDEV_DB3,("busy\n"));
+ screq->retsts = SCCMD_BUSY;
+ break;
+
+ default:
+ sc_print_addr(xs->sc_link);
+ printf("unknown error category from host adapter code\n");
+ screq->retsts = SCCMD_UNKNOWN;
+ break;
+ }
+ biodone(bp); /* we're waiting on it in scsi_strategy() */
+ return; /* it'll free the xs and restart any queue */
+}
+
+
+/* Pseudo strategy function
+ * Called by scsi_do_ioctl() via physio/physstrat if there is to
+ * be data transfered, and directly if there is no data transfer.
+ *
+ * Should I reorganize this so it returns to physio instead
+ * of sleeping in scsiio_scsi_cmd? Is there any advantage, other
+ * than avoiding the probable duplicate wakeup in iodone? [PD]
+ *
+ * No, seems ok to me... [JRE]
+ * (I don't see any duplicate wakeups)
+ *
+ * Can't be used with block devices or raw_read/raw_write directly
+ * from the cdevsw/bdevsw tables because they couldn't have added
+ * the screq structure. [JRE]
+ */
+void scsistrategy(struct buf *bp)
+{
+ errval err;
+ struct scsi_link *sc_link = bp->b_sc_link;
+ scsireq_t *screq;
+ u_int32 flags = 0;
+ int s;
+
+
+ if(!sc_link) {
+ printf("user_strat: No link pointer\n");
+ scsierr(bp,EINVAL);
+ return;
+ }
+ SC_DEBUG(sc_link,SDEV_DB2,("user_strategy\n"));
+ screq = bp->b_screq;
+ if(!screq) {
+ sc_print_addr(sc_link);
+ printf("No request block\n");
+ scsierr(bp,EINVAL);
+ return;
+ }
+
+ /* We're in trouble if physio tried to break up the
+ * transfer:
+ */
+ if (bp->b_bcount != screq->datalen) {
+ sc_print_addr(sc_link);
+ printf("physio split the request.. cannot proceed\n");
+ scsierr(bp, EIO);
+ return;
+ }
+
+ if (screq->timeout == 0) {
+ scsierr(bp, EINVAL);
+ return;
+ }
+
+ if (screq->cmdlen > sizeof(struct scsi_generic)) {
+ sc_print_addr(sc_link);
+ printf("cmdlen too big ");
+ scsierr(bp, EFAULT);
+ return;
+ }
+
+
+ if (screq->flags & SCCMD_READ)
+ flags |= SCSI_DATA_IN;
+
+ if (screq->flags & SCCMD_WRITE)
+ flags |= SCSI_DATA_OUT;
+
+ if (screq->flags & SCCMD_TARGET)
+ flags |= SCSI_TARGET;
+
+ if (screq->flags & SCCMD_ESCAPE)
+ flags |= SCSI_ESCAPE;
+ err = scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *)screq->cmd,
+ screq->cmdlen,
+ (u_char *)bp->b_un.b_addr,
+ screq->datalen,
+ 0, /* user must do the retries *//* ignored */
+ screq->timeout,
+ bp,
+ flags | SCSI_USER);
+
+
+
+ /*because there is a bp, scsi_scsi_cmd will return immediatly*/
+ if (err)
+ {
+ scsierr(bp, err);
+ return;
+ }
+ SC_DEBUG(sc_link,SDEV_DB3,("about to sleep\n"));
+ s = splbio();
+ while(!(bp->b_flags & B_DONE))
+ {
+ tsleep((caddr_t)bp, PRIBIO, "scsistrat", 0);
+ }
+ splx(s);
+ SC_DEBUG(sc_link,SDEV_DB3,("back from sleep\n"));
+ return;
+}
+
+void scsiminphys(struct buf *bp)
+{
+ /*XXX*//* call the adapter's minphys */
+}
+
+
+/*
+ * Something (e.g. another driver) has called us
+ * with an sc_link for a target/lun/adapter, and a scsi
+ * specific ioctl to perform, better try.
+ * If user-level type command, we must still be running
+ * in the context of the calling process
+ */
+errval scsi_do_ioctl(struct scsi_link *sc_link, int cmd, caddr_t addr, int f)
+{
+ errval ret = 0;
+ int phys;
+
+ SC_DEBUG(sc_link,SDEV_DB2,("scsi_do_ioctl(0x%x)\n",cmd));
+ switch(cmd)
+ {
+#if 0
+ case SCIOCCOMMAND:
+ {
+ /*
+ * You won't believe this, but the arg copied in
+ * from the user space, is on the kernel stack
+ * for this process, so we can't write
+ * to it at interrupt time..
+ * we need to copy it in and out!
+ * Make a static copy using malloc!
+ */
+ scsireq_t *screq2 = (scsireq_t *)addr;
+ scsireq_t *screq = (scsireq_t *)addr;
+ int rwflag = (screq->flags & SCCMD_READ) ? B_READ : B_WRITE;
+ struct buf *bp;
+ caddr_t d_addr;
+ int len;
+
+ if((unsigned int)screq < (unsigned int)KERNBASE)
+ {
+ screq = malloc(sizeof(scsireq_t),M_TEMP,M_WAITOK);
+ bcopy(screq2,screq,sizeof(scsireq_t));
+ }
+ bp = malloc(sizeof (struct buf),M_TEMP,M_WAITOK);
+ bzero(bp,sizeof(struct buf));
+ d_addr = screq->databuf;
+ bp->b_bcount = len = screq->datalen;
+ bp->b_screq = screq;
+ bp->b_sc_link = sc_link;
+ if (len) {
+ /* have data, translate it. (physio)*/
+#ifdef __NetBSD__
+#error "dev, mincntfn & uio need defining"
+ ret = physio(scsistrategy, bp, dev, rwflag,
+ mincntfn, uio);
+#else
+ ret = physio(scsistrategy,0,bp,0,rwflag,
+ d_addr,&len,curproc);
+#endif
+ } else {
+ /* if no data, no need to translate it.. */
+ bp->b_un.b_addr = 0;
+ bp->b_dev = -1; /* irrelevant info */
+ bp->b_flags = 0;
+
+ scsistrategy(bp);
+ ret = bp->b_error;
+ }
+ free(bp,M_TEMP);
+ if((unsigned int)screq2 < (unsigned int)KERNBASE)
+ {
+ bcopy(screq,screq2,sizeof(scsireq_t));
+ free(screq,M_TEMP);
+ }
+ break;
+ }
+#endif /* !NetBSD */
+ case SCIOCDEBUG:
+ {
+ int level = *((int *)addr);
+ SC_DEBUG(sc_link,SDEV_DB3,("debug set to %d\n",level));
+ sc_link->flags &= ~SDEV_DBX; /*clear debug bits */
+ if(level & 1) sc_link->flags |= SDEV_DB1;
+ if(level & 2) sc_link->flags |= SDEV_DB2;
+ if(level & 4) sc_link->flags |= SDEV_DB3;
+ if(level & 8) sc_link->flags |= SDEV_DB4;
+ ret = 0;
+ break;
+ }
+ case SCIOCREPROBE:
+ {
+ extern int scsibus;
+ struct scsi_addr *sca = (struct scsi_addr *) addr;
+
+ ret = scsi_probe_busses(sca->scbus,sca->target,sca->lun);
+ break;
+ }
+ case SCIOCRECONFIG:
+ case SCIOCDECONFIG:
+ ret = EINVAL;
+ break;
+ case SCIOCIDENTIFY:
+ {
+ struct scsi_addr *sca = (struct scsi_addr *) addr;
+ sca->scbus = sc_link->scsibus;
+ sca->target = sc_link->target;
+ sca->lun = sc_link->lun;
+ break;
+ }
+
+ default:
+ ret = ENOTTY;
+ break;
+ }
+
+ return ret;
+}
+
+void
+scsierr(bp,err)
+ struct buf *bp;
+ int err;
+{
+ bp->b_flags |= B_ERROR;
+ bp->b_error = err;
+ biodone(bp);
+ return;
+}
+
diff --git a/sys/scsi/scsi_tape.h b/sys/scsi/scsi_tape.h
new file mode 100644
index 0000000..ac417c2
--- /dev/null
+++ b/sys/scsi/scsi_tape.h
@@ -0,0 +1,204 @@
+/*
+ * SCSI tape interface description
+ */
+
+/*
+ * Written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ */
+
+/*
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ *
+ * $Id: scsi_tape.h,v 1.8 93/08/31 21:40:16 julian Exp Locker: julian $
+ */
+#ifndef SCSI_SCSI_TAPE_H
+#define SCSI_SCSI_TAPE_H 1
+
+
+
+/*
+ * SCSI command formats
+ */
+
+
+struct scsi_rw_tape
+{
+ u_char op_code;
+ u_char byte2;
+#define SRWT_FIXED 0x01
+ u_char len[3];
+ u_char control;
+} rw_tape;
+
+struct scsi_space
+{
+ u_char op_code;
+ u_char byte2;
+#define SS_CODE 0x03
+ u_char number[3];
+ u_char control;
+} space;
+#define SP_BLKS 0
+#define SP_FILEMARKS 1
+#define SP_SEQ_FILEMARKS 2
+#define SP_EOM 3
+
+struct scsi_write_filemarks
+{
+ u_char op_code;
+ u_char byte2;
+ u_char number[3];
+ u_char control;
+} write_filemarks;
+
+struct scsi_rewind
+{
+ u_char op_code;
+ u_char byte2;
+#define SR_IMMED 0x01
+ u_char unused[3];
+ u_char control;
+} rewind;
+
+struct scsi_load
+{
+ u_char op_code;
+ u_char byte2;
+#define SL_IMMED 0x01
+ u_char unused[2];
+ u_char how;
+ u_char control;
+} load;
+#define LD_UNLOAD 0
+#define LD_LOAD 1
+#define LD_RETEN 2
+
+
+struct scsi_blk_limits
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[3];
+ u_char control;
+} blk_limits;
+
+/*
+ * Opcodes
+ */
+
+#define REWIND 0x01
+#define READ_BLK_LIMITS 0x05
+#define READ_COMMAND_TAPE 0x08
+#define WRITE_COMMAND_TAPE 0x0a
+#define WRITE_FILEMARKS 0x10
+#define SPACE 0x11
+#define LOAD_UNLOAD 0x1b /* same as above */
+
+
+
+struct scsi_blk_limits_data
+{
+ u_char reserved;
+ u_char max_length_2; /* Most significant */
+ u_char max_length_1;
+ u_char max_length_0; /* Least significant */
+ u_char min_length_1; /* Most significant */
+ u_char min_length_0; /* Least significant */
+};
+
+/* defines for the device specific byte in the mode select/sense header */
+#define SMH_DSP_SPEED 0x0F
+#define SMH_DSP_BUFF_MODE 0x70
+#define SMH_DSP_BUFF_MODE_OFF 0x00
+#define SMH_DSP_BUFF_MODE_ON 0x10
+#define SMH_DSP_BUFF_MODE_MLTI 0x20
+#define SMH_DSP_WRITE_PROT 0x80
+
+/* A special for the CIPHER ST150S(old drive) */
+struct blk_desc_cipher
+{
+ u_char density;
+ u_char nblocks[3];
+ u_char reserved;
+ u_char blklen[3];
+ u_char other;
+#define ST150_SEC 0x01 /* soft error count */
+#define SR150_AUI 0x02 /* autoload inhibit */
+};
+
+
+
+/**********************************************************************
+ from the scsi2 spec
+ Value Tracks Density(bpi) Code Type Reference Note
+ 0x1 9 800 NRZI R X3.22-1983 2
+ 0x2 9 1600 PE R X3.39-1986 2
+ 0x3 9 6250 GCR R X3.54-1986 2
+ 0x5 4/9 8000 GCR C X3.136-1986 1
+ 0x6 9 3200 PE R X3.157-1987 2
+ 0x7 4 6400 IMFM C X3.116-1986 1
+ 0x8 4 8000 GCR CS X3.158-1986 1
+ 0x9 18 37871 GCR C X3B5/87-099 2
+ 0xA 22 6667 MFM C X3B5/86-199 1
+ 0xB 4 1600 PE C X3.56-1986 1
+ 0xC 24 12690 GCR C HI-TC1 1,5
+ 0xD 24 25380 GCR C HI-TC2 1,5
+ 0xF 15 10000 GCR C QIC-120 1,5
+ 0x10 18 10000 GCR C QIC-150 1,5
+ 0x11 26 16000 GCR C QIC-320(525?) 1,5
+ 0x12 30 51667 RLL C QIC-1350 1,5
+ 0x13 1 61000 DDS CS X3B5/88-185A 4
+ 0x14 1 43245 RLL CS X3.202-1991 4
+ 0x15 1 45434 RLL CS ECMA TC17 4
+ 0x16 48 10000 MFM C X3.193-1990 1
+ 0x17 48 42500 MFM C X3B5/91-174 1
+
+ where Code means:
+ NRZI Non Return to Zero, change on ones
+ GCR Group Code Recording
+ PE Phase Encoded
+ IMFM Inverted Modified Frequency Modulation
+ MFM Modified Frequency Modulation
+ DDS Dat Data Storage
+ RLL Run Length Encoding
+
+ where Type means:
+ R Real-to-Real
+ C Cartridge
+ CS cassette
+
+ where Notes means:
+ 1 Serial Recorded
+ 2 Parallel Recorded
+ 3 Old format know as QIC-11
+ 4 Helical Scan
+ 5 Not ANSI standard, rather industry standard.
+
+********************************************************************/
+
+#define HALFINCH_800 0x01
+#define HALFINCH_1600 0x02
+#define HALFINCH_6250 0x03
+#define QIC_11 0x04 /* from Archive 150S Theory of Op. XXX */
+#define QIC_24 0x05 /* may be bad, works for CIPHER ST150S XXX */
+#define QIC_120 0x0f
+#define QIC_150 0x10
+#define QIC_320 0x11
+#define QIC_525 0x11
+#define QIC_1320 0x12
+#define DDS 0x13
+#define DAT_1 0x13
+
+#endif /*SCSI_SCSI_TAPE_H*/
diff --git a/sys/scsi/scsiconf.c b/sys/scsi/scsiconf.c
new file mode 100644
index 0000000..9e2dfef
--- /dev/null
+++ b/sys/scsi/scsiconf.c
@@ -0,0 +1,699 @@
+/*
+ * Written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ *
+ * $Id: scsiconf.c,v 1.7 1993/11/18 05:02:58 rgrimes Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <sys/malloc.h>
+#include "st.h"
+#include "sd.h"
+#include "ch.h"
+#include "cd.h"
+#include "uk.h"
+#include "su.h"
+#ifndef NSCBUS
+#define NSCBUS 8
+#endif /* NSCBUS */
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#ifdef TFS
+#include "bll.h"
+#include "cals.h"
+#include "kil.h"
+#include "scan.h"
+#else /* TFS */
+#define NBLL 0
+#define NCALS 0
+#define NKIL 0
+#define NSCAN 0
+#endif /* TFS */
+
+#if NSD > 0
+extern sdattach();
+#endif /* NSD */
+#if NST > 0
+extern stattach();
+#endif /* NST */
+#if NCH > 0
+extern chattach();
+#endif /* NCH */
+#if NCD > 0
+extern cdattach();
+#endif /* NCD */
+#if NBLL > 0
+extern bllattach();
+#endif /* NBLL */
+#if NCALS > 0
+extern calsattach();
+#endif /* NCALS */
+#if NKIL > 0
+extern kil_attach();
+#endif /* NKIL */
+#if NUK > 0
+extern ukattach();
+#endif /* NUK */
+
+/*
+ * One of these is allocated and filled in for each scsi bus.
+ * it holds pointers to allow the scsi bus to get to the driver
+ * That is running each LUN on the bus
+ * it also has a template entry which is the prototype struct
+ * supplied by the adapter driver, this is used to initialise
+ * the others, before they have the rest of the fields filled in
+ */
+struct scsibus_data *scbus_data[NSCBUS];
+
+/*
+ * The structure of pre-configured devices that might be turned
+ * off and therefore may not show up
+ */
+struct predefined {
+ u_char scsibus;
+ u_char dev;
+ u_char lu;
+ errval(*attach_rtn) ();
+ char *devname;
+ char flags;
+} pd[] =
+
+{
+#ifdef EXAMPLE_PREDEFINE
+#if NSD > 0
+ {
+ 0, 0, 0, sdattach, "sd", 0
+ }, /* define a disk at scsibus=0 dev=0 lu=0 */
+#endif /* NSD */
+#endif /* EXAMPLE_PREDEFINE */
+ {
+ 0, 9, 9
+ } /*illegal dummy end entry */
+};
+
+/*
+ * The structure of known drivers for autoconfiguration
+ */
+struct scsidevs {
+ u_int32 type;
+ boolean removable;
+ char *manufacturer;
+ char *model;
+ char *version;
+ errval(*attach_rtn) ();
+ char *devname;
+ char flags; /* 1 show my comparisons during boot(debug) */
+};
+
+#define SC_SHOWME 0x01
+#define SC_ONE_LU 0x00
+#define SC_MORE_LUS 0x02
+#if NUK > 0
+
+static struct scsidevs unknowndev = {
+ -1, 0, "standard", "any"
+ ,"any", ukattach, "uk", SC_MORE_LUS
+};
+#endif /*NUK*/
+static struct scsidevs knowndevs[] =
+{
+#if NSD > 0
+ {
+ T_DIRECT, T_FIXED, "standard", "any"
+ ,"any", sdattach, "sd", SC_ONE_LU
+ },
+ {
+ T_DIRECT, T_FIXED, "MAXTOR ", "XT-4170S "
+ ,"B5A ", sdattach, "mx1", SC_ONE_LU
+ },
+#endif /* NSD */
+#if NST > 0
+ {
+ T_SEQUENTIAL, T_REMOV, "standard", "any"
+ ,"any", stattach, "st", SC_ONE_LU
+ },
+#endif /* NST */
+#if NCALS > 0
+ {
+ T_PROCESSOR, T_FIXED, "standard", "any"
+ ,"any", calsattach, "cals", SC_MORE_LUS
+ },
+#endif /* NCALS */
+#if NCH > 0
+ {
+ T_CHANGER, T_REMOV, "standard", "any"
+ ,"any", chattach, "ch", SC_ONE_LU
+ },
+#endif /* NCH */
+#if NCD > 0
+#ifndef UKTEST /* make cdroms unrecognised to test the uk driver */
+ {
+ T_READONLY, T_REMOV, "SONY ", "CD-ROM CDU-8012 "
+ ,"3.1a", cdattach, "cd", SC_ONE_LU
+ },
+ {
+ T_READONLY, T_REMOV, "PIONEER ", "CD-ROM DRM-600 "
+ ,"any", cdattach, "cd", SC_MORE_LUS
+ },
+#endif
+#endif /* NCD */
+#if NBLL > 0
+ {
+ T_PROCESSOR, T_FIXED, "AEG ", "READER "
+ ,"V1.0", bllattach, "bll", SC_MORE_LUS
+ },
+#endif /* NBLL */
+#if NKIL > 0
+ {
+ T_SCANNER, T_FIXED, "KODAK ", "IL Scanner 900 "
+ ,"any", kil_attach, "kil", SC_ONE_LU
+ },
+#endif /* NKIL */
+
+ {
+ 0
+ }
+};
+
+/*
+ * Declarations
+ */
+struct predefined *scsi_get_predef();
+struct scsidevs *scsi_probedev();
+struct scsidevs *selectdev();
+errval scsi_probe_bus __P((int bus, int targ, int lun));
+
+struct scsi_device probe_switch =
+{
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "probe",
+ 0,
+ { 0, 0 }
+};
+
+/*
+ * controls debug level within the scsi subsystem -
+ * see scsiconf.h for values
+ */
+int32 scsibus = 0x0; /* This is the Nth scsibus we've seen */
+
+/*
+ * The routine called by the adapter boards to get all their
+ * devices configured in.
+ */
+void
+scsi_attachdevs(sc_link_proto)
+ struct scsi_link *sc_link_proto;
+{
+
+ if(scsibus >= NSCBUS) {
+ printf("too many scsi busses, reconfigure the kernel\n");
+ return;
+ }
+ sc_link_proto->scsibus = scsibus;
+ scbus_data[scsibus] = malloc(sizeof(struct scsibus_data), M_TEMP, M_NOWAIT);
+ if(!scbus_data[scsibus]) {
+ panic("scsi_attachdevs: malloc\n");
+ }
+ bzero(scbus_data[scsibus], sizeof(struct scsibus_data));
+ scbus_data[scsibus]->adapter_link = sc_link_proto;
+#if defined(SCSI_DELAY) && SCSI_DELAY > 2
+ printf("%s%d waiting for scsi devices to settle\n",
+ sc_link_proto->adapter->name, sc_link_proto->adapter_unit);
+#else /* SCSI_DELAY > 2 */
+#undef SCSI_DELAY
+#define SCSI_DELAY 2
+#endif /* SCSI_DELAY */
+ DELAY(1000000 * SCSI_DELAY);
+ scsibus++;
+ scsi_probe_bus(scsibus - 1,-1,-1);
+}
+
+/*
+ * Probe the requested scsi bus. It must be already set up.
+ * -1 requests all set up scsi busses.
+ * targ and lun optionally narrow the search if not -1
+ */
+errval
+scsi_probe_busses(int bus, int targ, int lun)
+{
+ if (bus == -1) {
+ for(bus = 0; bus < scsibus; bus++) {
+ scsi_probe_bus(bus, targ, lun);
+ }
+ return 0;
+ } else {
+ return scsi_probe_bus(bus, targ, lun);
+ }
+}
+
+/*
+ * Probe the requested scsi bus. It must be already set up.
+ * targ and lun optionally narrow the search if not -1
+ */
+errval
+scsi_probe_bus(int bus, int targ, int lun)
+{
+ struct scsibus_data *scsi ;
+ int maxtarg,mintarg,maxlun,minlun;
+ struct scsi_link *sc_link_proto;
+ u_int8 scsi_addr ;
+ struct scsidevs *bestmatch = NULL;
+ struct predefined *predef = NULL;
+ struct scsi_link *sc_link = NULL;
+ boolean maybe_more;
+
+ if ((bus < 0 ) || ( bus >= scsibus)) {
+ return ENXIO;
+ }
+ scsi = scbus_data[bus];
+ if(!scsi) return ENXIO;
+ sc_link_proto = scsi->adapter_link;
+ scsi_addr = sc_link_proto->adapter_targ;
+ if(targ == -1){
+ maxtarg = 7;
+ mintarg = 0;
+ } else {
+ if((targ < 0 ) || (targ > 7)) return EINVAL;
+ maxtarg = mintarg = targ;
+ }
+
+ if(lun == -1){
+ maxlun = 7;
+ minlun = 0;
+ } else {
+ if((lun < 0 ) || (lun > 7)) return EINVAL;
+ maxlun = minlun = lun;
+ }
+
+
+ for ( targ = mintarg;targ <= maxtarg; targ++) {
+ maybe_more = 0; /* by default only check 1 lun */
+ if (targ == scsi_addr) {
+ continue;
+ }
+ for ( lun = minlun; lun <= maxlun ;lun++) {
+ /*
+ * The spot appears to already have something
+ * linked in, skip past it. Must be doing a 'reprobe'
+ */
+ if(scsi->sc_link[targ][lun])
+ {/* don't do this one, but check other luns */
+ maybe_more = 1;
+ continue;
+ }
+ /*
+ * If we presently don't have a link block
+ * then allocate one to use while probing
+ */
+ if (!sc_link) {
+ sc_link = malloc(sizeof(*sc_link), M_TEMP, M_NOWAIT);
+ *sc_link = *sc_link_proto; /* struct copy */
+ sc_link->opennings = 1;
+ sc_link->device = &probe_switch;
+ }
+ sc_link->target = targ;
+ sc_link->lun = lun;
+ predef = scsi_get_predef(sc_link, &maybe_more);
+ bestmatch = scsi_probedev(sc_link, &maybe_more);
+ if ((bestmatch) && (predef)) { /* both exist */
+ if (bestmatch->attach_rtn
+ != predef->attach_rtn) {
+ printf("Clash in found/expected devices\n");
+#if NUK > 0
+ if(bestmatch == &unknowndev) {
+ printf("will link in PREDEFINED\n");
+ (*(predef->attach_rtn)) (sc_link);
+ } else
+#endif /*NUK*/
+ {
+ printf("will link in FOUND\n");
+ (*(bestmatch->attach_rtn)) (sc_link);
+ }
+ } else {
+ (*(bestmatch->attach_rtn)) (sc_link);
+ }
+ }
+ if ((bestmatch) && (!predef)) { /* just FOUND */
+ (*(bestmatch->attach_rtn)) (sc_link);
+ }
+ if ((!bestmatch) && (predef)) { /* just predef */
+ (*(predef->attach_rtn)) (sc_link);
+ }
+ if ((bestmatch) || (predef)) { /* one exists */
+ scsi->sc_link[targ][lun] = sc_link;
+ sc_link = NULL; /* it's been used */
+ }
+ if (!(maybe_more)) { /* nothing suggests we'll find more */
+ break; /* nothing here, skip to next targ */
+ }
+ /* otherwise something says we should look further */
+ }
+ }
+ if (sc_link) {
+ free(sc_link, M_TEMP);
+ }
+ return 0;
+}
+
+/*
+ * given a target and lu, check if there is a predefined device for
+ * that address
+ */
+struct predefined *
+scsi_get_predef(sc_link, maybe_more)
+ struct scsi_link *sc_link;
+ boolean *maybe_more;
+{
+ u_int8 unit = sc_link->scsibus;
+ u_int8 target = sc_link->target;
+ u_int8 lu = sc_link->lun;
+ struct scsi_adapter *scsi_adapter = sc_link->adapter;
+ u_int32 upto, numents;
+
+ numents = (sizeof(pd) / sizeof(struct predefined)) - 1;
+
+ for (upto = 0; upto < numents; upto++) {
+ if (pd[upto].scsibus != unit)
+ continue;
+ if (pd[upto].dev != target)
+ continue;
+ if (pd[upto].lu != lu)
+ continue;
+
+ printf("%s%d targ %d lun %d: <%s> - PRECONFIGURED -\n"
+ ,scsi_adapter->name
+ ,unit
+ ,target
+ ,lu
+ ,pd[upto].devname);
+ *maybe_more = pd[upto].flags & SC_MORE_LUS;
+ return (&(pd[upto]));
+ }
+ return ((struct predefined *) 0);
+}
+
+/*
+ * given a target and lu, ask the device what
+ * it is, and find the correct driver table
+ * entry.
+ */
+struct scsidevs *
+scsi_probedev(sc_link, maybe_more)
+ boolean *maybe_more;
+ struct scsi_link *sc_link;
+{
+ u_int8 unit = sc_link->adapter_unit;
+ u_int8 target = sc_link->target;
+ u_int8 lu = sc_link->lun;
+ struct scsi_adapter *scsi_adapter = sc_link->adapter;
+ struct scsidevs *bestmatch = (struct scsidevs *) 0;
+ char *dtype = (char *) 0, *desc;
+ char *qtype;
+ static struct scsi_inquiry_data inqbuf;
+ u_int32 len, qualifier, type;
+ boolean remov;
+ char manu[32];
+ char model[32];
+ char version[32];
+
+ bzero(&inqbuf, sizeof(inqbuf));
+ /*
+ * Ask the device what it is
+ */
+#ifdef SCSIDEBUG
+ if ((target == DEBUGTARG) && (lu == DEBUGLUN))
+ sc_link->flags |= (DEBUGLEVEL);
+ else
+ sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2 | SDEV_DB3 | SDEV_DB4);
+#endif /* SCSIDEBUG */
+ /* catch unit attn */
+ scsi_test_unit_ready(sc_link, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT);
+#ifdef DOUBTFULL
+ switch (scsi_test_unit_ready(sc_link, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT)) {
+ case 0: /* said it WAS ready */
+ case EBUSY: /* replied 'NOT READY' but WAS present, continue */
+ case ENXIO:
+ break;
+ case EIO: /* device timed out */
+ case EINVAL: /* Lun not supported */
+ default:
+ return (struct scsidevs *) 0;
+
+ }
+#endif /*DOUBTFULL*/
+#ifdef SCSI_2_DEF
+ /* some devices need to be told to go to SCSI2 */
+ /* However some just explode if you tell them this.. leave it out */
+ scsi_change_def(sc_link, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT);
+#endif /*SCSI_2_DEF */
+
+ /* Now go ask the device all about itself */
+ if (scsi_inquire(sc_link, &inqbuf, SCSI_NOSLEEP | SCSI_NOMASK) != 0) {
+ return (struct scsidevs *) 0;
+ }
+
+ /*
+ * note what BASIC type of device it is
+ */
+ type = inqbuf.device & SID_TYPE;
+ qualifier = inqbuf.device & SID_QUAL;
+ remov = inqbuf.dev_qual2 & SID_REMOVABLE;
+
+ /*
+ * Any device qualifier that has the top bit set (qualifier&4 != 0)
+ * is vendor specific and won't match in this switch.
+ */
+
+ switch ((int)qualifier) {
+ case SID_QUAL_LU_OK:
+ qtype = "";
+ break;
+
+ case SID_QUAL_LU_OFFLINE:
+ qtype = ", Unit not Connected!";
+ break;
+
+ case SID_QUAL_RSVD:
+ qtype = ", Reserved Peripheral Qualifier!";
+ *maybe_more = 1;
+ return (struct scsidevs *) 0;
+ break;
+
+ case SID_QUAL_BAD_LU:
+ /*
+ * Check for a non-existent unit. If the device is returning
+ * this much, then we must set the flag that has
+ * the searchers keep looking on other luns.
+ */
+ qtype = ", The Target can't support this Unit!";
+ *maybe_more = 1;
+ return (struct scsidevs *) 0;
+
+ default:
+ dtype = "vendor specific";
+ qtype = "";
+ *maybe_more = 1;
+ break;
+ }
+ if (dtype == 0) {
+ switch ((int)type) {
+ case T_DIRECT:
+ dtype = "direct";
+ break;
+ case T_SEQUENTIAL:
+ dtype = "sequential";
+ break;
+ case T_PRINTER:
+ dtype = "printer";
+ break;
+ case T_PROCESSOR:
+ dtype = "processor";
+ break;
+ case T_READONLY:
+ dtype = "readonly";
+ break;
+ case T_WORM:
+ dtype = "worm";
+ break;
+ case T_SCANNER:
+ dtype = "scanner";
+ break;
+ case T_OPTICAL:
+ dtype = "optical";
+ break;
+ case T_CHANGER:
+ dtype = "changer";
+ break;
+ case T_COMM:
+ dtype = "communication";
+ break;
+ case T_NODEVICE:
+ *maybe_more = 1;
+ return (struct scsidevs *) 0;
+ default:
+ dtype = "unknown";
+ break;
+ }
+ }
+ /*
+ * Then if it's advanced enough, more detailed
+ * information
+ */
+ if ((inqbuf.version & SID_ANSII) > 0) {
+ if ((len = inqbuf.additional_length
+ + ((char *) inqbuf.unused
+ - (char *) &inqbuf))
+ > (sizeof(struct scsi_inquiry_data) - 1))
+ len = sizeof(struct scsi_inquiry_data) - 1;
+ desc = inqbuf.vendor;
+ desc[len - (desc - (char *) &inqbuf)] = 0;
+ strncpy(manu, inqbuf.vendor, 8);
+ manu[8] = 0;
+ strncpy(model, inqbuf.product, 16);
+ model[16] = 0;
+ strncpy(version, inqbuf.revision, 4);
+ version[4] = 0;
+ } else
+ /*
+ * If not advanced enough, use default values
+ */
+ {
+ desc = "early protocol device";
+ strncpy(manu, "unknown", 8);
+ strncpy(model, "unknown", 16);
+ strncpy(version, "????", 4);
+ }
+ printf("%s%d targ %d lun %d: type %d(%s) %s SCSI%d\n"
+ ,scsi_adapter->name
+ ,unit
+ ,target
+ ,lu
+ ,type
+ ,dtype
+ ,remov ? "removable" : "fixed"
+ ,inqbuf.version & SID_ANSII
+ );
+ printf("%s%d targ %d lun %d: <%s%s%s>\n"
+ ,scsi_adapter->name
+ ,unit
+ ,target
+ ,lu
+ ,manu
+ ,model
+ ,version
+ );
+ if (qtype[0]) {
+ printf("%s%d targ %d lun %d: qualifier %d(%s)\n"
+ ,scsi_adapter->name
+ ,unit
+ ,target
+ ,lu
+ ,qualifier
+ ,qtype
+ );
+ }
+ /*
+ * Try make as good a match as possible with
+ * available sub drivers
+ */
+ bestmatch = (selectdev(
+ qualifier, type, remov ? T_REMOV : T_FIXED, manu, model, version));
+ if ((bestmatch) && (bestmatch->flags & SC_MORE_LUS)) {
+ *maybe_more = 1;
+ }
+ return (bestmatch);
+}
+/*
+ * Try make as good a match as possible with
+ * available sub drivers
+ */
+struct scsidevs *
+selectdev(qualifier, type, remov, manu, model, rev)
+ u_int32 qualifier, type;
+ boolean remov;
+ char *manu, *model, *rev;
+{
+ u_int32 numents = (sizeof(knowndevs) / sizeof(struct scsidevs)) - 1;
+ u_int32 count = 0;
+ u_int32 bestmatches = 0;
+ struct scsidevs *bestmatch = (struct scsidevs *) 0;
+ struct scsidevs *thisentry = knowndevs;
+
+ type |= qualifier; /* why? */
+
+ thisentry--;
+ while (count++ < numents) {
+ thisentry++;
+ if (type != thisentry->type) {
+ continue;
+ }
+ if (bestmatches < 1) {
+ bestmatches = 1;
+ bestmatch = thisentry;
+ }
+ if (remov != thisentry->removable) {
+ continue;
+ }
+ if (bestmatches < 2) {
+ bestmatches = 2;
+ bestmatch = thisentry;
+ }
+ if (thisentry->flags & SC_SHOWME)
+ printf("\n%s-\n%s-", thisentry->manufacturer, manu);
+ if (strcmp(thisentry->manufacturer, manu)) {
+ continue;
+ }
+ if (bestmatches < 3) {
+ bestmatches = 3;
+ bestmatch = thisentry;
+ }
+ if (thisentry->flags & SC_SHOWME)
+ printf("\n%s-\n%s-", thisentry->model, model);
+ if (strcmp(thisentry->model, model)) {
+ continue;
+ }
+ if (bestmatches < 4) {
+ bestmatches = 4;
+ bestmatch = thisentry;
+ }
+ if (thisentry->flags & SC_SHOWME)
+ printf("\n%s-\n%s-", thisentry->version, rev);
+ if (strcmp(thisentry->version, rev)) {
+ continue;
+ }
+ if (bestmatches < 5) {
+ bestmatches = 5;
+ bestmatch = thisentry;
+ break;
+ }
+ }
+ if (bestmatch == (struct scsidevs *) 0) {
+#if NUK > 0
+ bestmatch = &unknowndev;
+#else
+ printf("No explicit device driver match.\n");
+#endif
+ }
+ return (bestmatch);
+}
diff --git a/sys/scsi/scsiconf.h b/sys/scsi/scsiconf.h
new file mode 100644
index 0000000..f3f9586
--- /dev/null
+++ b/sys/scsi/scsiconf.h
@@ -0,0 +1,249 @@
+/*
+ * Written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ *
+ * $Id: scsiconf.h,v 1.8 1993/12/19 00:54:55 wollman Exp $
+ */
+#ifndef SCSI_SCSICONF_H
+#define SCSI_SCSICONF_H 1
+typedef int boolean;
+typedef int errval;
+typedef long int int32;
+typedef short int int16;
+typedef char int8;
+typedef unsigned long int u_int32;
+typedef unsigned short int u_int16;
+typedef unsigned char u_int8;
+
+#include <scsi/scsi_debug.h>
+
+/*
+ * The following documentation tries to describe the relationship between the
+ * various structures defined in this file:
+ *
+ * each adapter type has a scsi_adapter struct. This describes the adapter and
+ * identifies routines that can be called to use the adapter.
+ * each device type has a scsi_device struct. This describes the device and
+ * identifies routines that can be called to use the device.
+ * each existing device position (scsibus + target + lun)
+ * can be described by a scsi_link struct.
+ * Only scsi positions that actually have devices, have a scsi_link
+ * structure assigned. so in effect each device has scsi_link struct.
+ * The scsi_link structure contains information identifying both the
+ * device driver and the adapter driver for that position on that scsi bus,
+ * and can be said to 'link' the two.
+ * each individual scsi bus has an array that points to all the scsi_link
+ * structs associated with that scsi bus. Slots with no device have
+ * a NULL pointer.
+ * each individual device also knows the address of it's own scsi_link
+ * structure.
+ *
+ * -------------
+ *
+ * The key to all this is the scsi_link structure which associates all the
+ * other structures with each other in the correct configuration. The
+ * scsi_link is the connecting information that allows each part of the
+ * scsi system to find the associated other parts.
+ */
+
+
+/*
+ * These entrypoints are called by the high-end drivers to get services from
+ * whatever low-end drivers they are attached to each adapter type has one of
+ * these statically allocated.
+ */
+struct scsi_adapter
+{
+/* 04*/ int32 (*scsi_cmd)();
+/* 08*/ void (*scsi_minphys)();
+/* 12*/ int32 (*open_target_lu)();
+/* 16*/ int32 (*close_target_lu)();
+/* 20*/ u_int32 (*adapter_info)(); /* see definitions below */
+/* 24*/ char *name; /* name of scsi bus controller */
+/* 32*/ u_long spare[2];
+};
+
+/*
+ * return values for scsi_cmd()
+ */
+#define SUCCESSFULLY_QUEUED 0
+#define TRY_AGAIN_LATER 1
+#define COMPLETE 2
+#define HAD_ERROR 3 /* do not use this, use COMPLETE */
+#define ESCAPE_NOT_SUPPORTED 4
+
+/*
+ * Format of adapter_info() response data
+ * e.g. maximum number of entries queuable to a device by the adapter
+ */
+#define AD_INF_MAX_CMDS 0x000000FF
+/* 24 bits of other adapter characteristics go here */
+
+/*
+ * These entry points are called by the low-end drivers to get services from
+ * whatever high-end drivers they are attached to. Each device type has one
+ * of these statically allocated.
+ */
+struct scsi_device
+{
+/* 4*/ errval (*err_handler)(); /* returns -1 to say err processing complete */
+/* 8*/ void (*start)();
+/* 12*/ int32 (*async)();
+/* 16*/ int32 (*done)(); /* returns -1 to say done processing complete */
+/* 20*/ char *name; /* name of device type */
+/* 24*/ u_int32 flags; /* device type dependent flags */
+/* 32*/ int32 spare[2];
+};
+
+/*
+ * This structure describes the connection between an adapter driver and
+ * a device driver, and is used by each to call services provided by
+ * the other, and to allow generic scsi glue code to call these services
+ * as well.
+ */
+struct scsi_link
+{
+/* 1*/ u_int8 target; /* targ of this dev */
+/* 2*/ u_int8 lun; /* lun of this dev */
+/* 3*/ u_int8 adapter_targ; /* what are we on the scsi bus */
+/* 4*/ u_int8 adapter_unit; /* e.g. the 0 in aha0 */
+/* 5*/ u_int8 scsibus; /* the Nth scsibus */
+/* 6*/ u_int8 dev_unit; /* e.g. the 0 in sd0 */
+/* 7*/ u_int8 opennings; /* available operations */
+/* 8*/ u_int8 active; /* operations in progress */
+/* 10*/ u_int16 flags; /* flags that all devices have */
+/* 12*/ u_int8 spareb[2]; /* unused */
+/* 16*/ struct scsi_adapter *adapter; /* adapter entry points etc. */
+/* 20*/ struct scsi_device *device; /* device entry points etc. */
+/* 24*/ struct scsi_xfer *active_xs; /* operations under way */
+/* 28*/ void * fordriver; /* for private use by the driver */
+/* 32*/ u_int32 spare;
+};
+#define SDEV_MEDIA_LOADED 0x01 /* device figures are still valid */
+#define SDEV_WAITING 0x02 /* a process is waiting for this */
+#define SDEV_OPEN 0x04 /* at least 1 open session */
+#define SDEV_BOUNCE 0x08 /* unit requires DMA bounce buffer */
+#define SDEV_DBX 0xF0 /* debuging flags (scsi_debug.h) */
+
+/*
+ * One of these is allocated and filled in for each scsi bus.
+ * it holds pointers to allow the scsi bus to get to the driver
+ * That is running each LUN on the bus
+ * it also has a template entry which is the prototype struct
+ * supplied by the adapter driver, this is used to initialise
+ * the others, before they have the rest of the fields filled in
+ */
+struct scsibus_data {
+ struct scsi_link *adapter_link; /* prototype supplied by adapter */
+ struct scsi_link *sc_link[8][8];
+};
+
+/*
+ * Each scsi transaction is fully described by one of these structures
+ * It includes information about the source of the command and also the
+ * device and adapter for which the command is destined.
+ * (via the scsi_link structure) *
+ */
+struct scsi_xfer
+{
+/*04*/ struct scsi_xfer *next; /* when free */
+/*08*/ u_int32 flags;
+/*12*/ struct scsi_link *sc_link; /* all about our device and adapter */
+/*13*/ u_int8 retries; /* the number of times to retry */
+/*16*/ u_int8 spare[3];
+/*20*/ int32 timeout; /* in milliseconds */
+/*24*/ struct scsi_generic *cmd; /* The scsi command to execute */
+/*28*/ int32 cmdlen; /* how long it is */
+/*32*/ u_char *data; /* dma address OR a uio address */
+/*36*/ int32 datalen; /* data len (blank if uio) */
+/*40*/ int32 resid; /* how much buffer was not touched */
+/*44*/ int32 error; /* an error value */
+/*48*/ struct buf *bp; /* If we need to associate with a buf */
+/*80*/ struct scsi_sense_data sense; /* 32 bytes*/
+ /*
+ * Believe it or not, Some targets fall on the ground with
+ * anything but a certain sense length.
+ */
+/*84*/ int32 req_sense_length; /* Explicit request sense length */
+/*88*/ int32 status; /* SCSI status */
+/*100*/ struct scsi_generic cmdstore; /* stash the command in here */
+};
+
+/*
+ * Per-request Flag values
+ */
+#define SCSI_NOSLEEP 0x01 /* Not a user... don't sleep */
+#define SCSI_NOMASK 0x02 /* dont allow interrupts.. booting */
+#define SCSI_NOSTART 0x04 /* left over from ancient history */
+#define SCSI_USER 0x08 /* Is a user cmd, call scsi_user_done */
+#define ITSDONE 0x10 /* the transfer is as done as it gets */
+#define INUSE 0x20 /* The scsi_xfer block is in use */
+#define SCSI_SILENT 0x40 /* Don't report errors to console */
+#define SCSI_ERR_OK 0x80 /* An error on this operation is OK. */
+#define SCSI_RESET 0x100 /* Reset the device in question */
+#define SCSI_DATA_UIO 0x200 /* The data address refers to a UIO */
+#define SCSI_DATA_IN 0x400 /* expect data to come INTO memory */
+#define SCSI_DATA_OUT 0x800 /* expect data to flow OUT of memory */
+#define SCSI_TARGET 0x1000 /* This defines a TARGET mode op. */
+#define SCSI_ESCAPE 0x2000 /* Escape operation */
+
+/*
+ * Escape op codes. This provides an extensible setup for operations
+ * that are not scsi commands. They are intended for modal operations.
+ */
+
+#define SCSI_OP_TARGET 0x0001
+#define SCSI_OP_RESET 0x0002
+#define SCSI_OP_BDINFO 0x0003
+
+/*
+ * Error values an adapter driver may return
+ */
+#define XS_NOERROR 0x0 /* there is no error, (sense is invalid) */
+#define XS_SENSE 0x1 /* Check the returned sense for the error */
+#define XS_DRIVER_STUFFUP 0x2 /* Driver failed to perform operation */
+#define XS_TIMEOUT 0x03 /* The device timed out.. turned off? */
+#define XS_SWTIMEOUT 0x04 /* The Timeout reported was caught by SW */
+#define XS_BUSY 0x08 /* The device busy, try again later? */
+
+void scsi_attachdevs __P((struct scsi_link *sc_link_proto));
+struct scsi_xfer *get_xs( struct scsi_link *sc_link, u_int32 flags);
+void free_xs(struct scsi_xfer *xs, struct scsi_link *sc_link,u_int32 flags);
+u_int32 scsi_size( struct scsi_link *sc_link,u_int32 flags);
+errval scsi_test_unit_ready( struct scsi_link *sc_link, u_int32 flags);
+errval scsi_change_def( struct scsi_link *sc_link, u_int32 flags);
+errval scsi_inquire( struct scsi_link *sc_link,
+ struct scsi_inquiry_data *inqbuf, u_int32 flags);
+errval scsi_prevent( struct scsi_link *sc_link, u_int32 type,u_int32 flags);
+errval scsi_start_unit( struct scsi_link *sc_link, u_int32 flags);
+void scsi_done(struct scsi_xfer *xs);
+errval scsi_scsi_cmd( struct scsi_link *sc_link, struct scsi_generic *scsi_cmd,
+ u_int32 cmdlen, u_char *data_addr,
+ u_int32 datalen, u_int32 retries,
+ u_int32 timeout, struct buf *bp,
+ u_int32 flags);
+errval scsi_do_ioctl __P((struct scsi_link *sc_link, int cmd, caddr_t addr, int f));
+
+void show_scsi_xs(struct scsi_xfer *xs);
+void show_scsi_cmd(struct scsi_xfer *xs);
+void show_mem(unsigned char * , u_int32);
+
+void lto3b __P((int val, u_char *bytes));
+int _3btol __P((u_char *bytes));
+
+extern void sc_print_addr(struct scsi_link *);
+
+#endif /*SCSI_SCSICONF_H*/
+/* END OF FILE */
diff --git a/sys/scsi/sd.c b/sys/scsi/sd.c
new file mode 100644
index 0000000..691430e
--- /dev/null
+++ b/sys/scsi/sd.c
@@ -0,0 +1,1072 @@
+/*
+ * Written by Julian Elischer (julian@dialix.oz.au)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@dialix.oz.au) Sept 1992
+ *
+ * $Id: sd.c,v 1.23 1994/04/20 07:06:57 davidg Exp $
+ */
+
+#define SPLSD splbio
+#define ESUCCESS 0
+#include <sd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/dkbad.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/errno.h>
+#include <sys/dkstat.h>
+#include <sys/disklabel.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_disk.h>
+#include <scsi/scsiconf.h>
+#include <vm/vm.h>
+
+u_int32 sdstrats, sdqueues;
+
+#ifdef NetBSD
+#ifdef DDB
+int Debugger();
+#else /* DDB */
+#define Debugger()
+#endif /* DDB */
+#else /* NetBSD */
+#include <ddb.h>
+#if NDDB > 0
+#else /* NDDB > 0 */
+#define Debugger(s)
+#endif /* NDDB > 0 */
+#endif
+
+#define PAGESIZ 4096
+#define SECSIZE 512
+#define PDLOCATION 29
+#define BOOTRECORDSIGNATURE (0x55aa & 0x00ff)
+#define SDOUTSTANDING 2
+#define SDQSIZE 4
+#define SD_RETRIES 4
+#define MAXTRANSFER 8 /* 1 page at a time */
+
+#define MAKESDDEV(maj, unit, part) (makedev(maj,((unit<<3)+part)))
+#define UNITSHIFT 3
+#define PARTITION(z) (minor(z) & 0x07)
+#define RAW_PART 3
+#define UNIT(z) ( (minor(z) >> UNITSHIFT) )
+
+#define WHOLE_DISK(unit) ( (unit << UNITSHIFT) + RAW_PART )
+
+extern char *readdisklabel();
+errval sdgetdisklabel __P((unsigned char unit));
+errval sd_get_parms __P((int unit, int flags));
+void sdstrategy __P((struct buf *));
+void sdstart __P((u_int32));
+
+struct scsi_device sd_switch =
+{
+ NULL, /* Use default error handler */
+ sdstart, /* have a queue, served by this */
+ NULL, /* have no async handler */
+ NULL, /* Use default 'done' routine */
+ "sd",
+ 0,
+ { 0, 0 }
+};
+
+struct sd_data {
+ u_int32 flags;
+#define SDINIT 0x04 /* device has been init'd */
+#define SDHAVELABEL 0x10 /* have read the label */
+#define SDDOSPART 0x20 /* Have read the DOS partition table */
+#define SDWRITEPROT 0x40 /* Device in readonly mode (S/W) */
+ struct scsi_link *sc_link; /* contains our targ, lun etc. */
+ u_int32 ad_info; /* info about the adapter */
+ u_int32 cmdscount; /* cmds allowed outstanding by board */
+ boolean wlabel; /* label is writable */
+ struct disk_parms {
+ u_char heads; /* Number of heads */
+ u_int16 cyls; /* Number of cylinders */
+ u_char sectors; /*dubious *//* Number of sectors/track */
+ u_int16 secsiz; /* Number of bytes/sector */
+ u_int32 disksize; /* total number sectors */
+ } params;
+ struct disklabel disklabel;
+#ifdef NetBSD
+ struct cpu_disklabel cpudisklabel;
+#else
+ struct dos_partition dosparts[NDOSPART]; /* DOS view of disk */
+#endif /* NetBSD */
+ u_int32 partflags[MAXPARTITIONS]; /* per partition flags */
+#define SDOPEN 0x01
+ u_int32 openparts; /* one bit for each open partition */
+ u_int32 sd_start_of_unix; /* unix vs dos partitions */
+ struct buf buf_queue;
+ u_int32 xfer_block_wait;
+} *sd_data[NSD];
+
+static u_int32 next_sd_unit = 0;
+
+static struct scsi_xfer sx;
+
+/*
+ * The routine called by the low level scsi routine when it discovers
+ * a device suitable for this driver.
+ */
+errval
+sdattach(sc_link)
+ struct scsi_link *sc_link;
+{
+ u_int32 unit;
+ struct sd_data *sd;
+ struct disk_parms *dp;
+
+ unit = next_sd_unit++;
+ SC_DEBUG(sc_link, SDEV_DB2, ("sdattach: "));
+ /*
+ * Check we have the resources for another drive
+ */
+ if (unit >= NSD) {
+ printf("Too many scsi disks..(%d > %d) reconfigure kernel\n",
+ (unit + 1), NSD);
+ return 0;
+ }
+ if (sd_data[unit]) {
+ printf("sd%d: unit already has storage allocated!\n", unit);
+ return 0;
+ }
+ sd = sd_data[unit] = malloc(sizeof(struct sd_data), M_DEVBUF, M_NOWAIT);
+ if (!sd) {
+ printf("malloc failed in sd.c\n");
+ return (0);
+ }
+ bzero(sd, sizeof(struct sd_data));
+
+ dp = &(sd->params);
+ /*
+ * Store information needed to contact our base driver
+ */
+ sd->sc_link = sc_link;
+ sc_link->device = &sd_switch;
+ sc_link->dev_unit = unit;
+
+ if (sd->sc_link->adapter->adapter_info) {
+ sd->ad_info = ((*(sd->sc_link->adapter->adapter_info)) (sc_link->adapter_unit));
+ sd->cmdscount = sd->ad_info & AD_INF_MAX_CMDS;
+ if (sd->cmdscount > SDOUTSTANDING) {
+ sd->cmdscount = SDOUTSTANDING;
+ }
+ } else {
+ sd->ad_info = 1;
+ sd->cmdscount = 1;
+ }
+ sc_link->opennings = sd->cmdscount;
+ /*
+ * Use the subdriver to request information regarding
+ * the drive. We cannot use interrupts yet, so the
+ * request must specify this.
+ */
+ sd_get_parms(unit, SCSI_NOSLEEP | SCSI_NOMASK);
+ printf("sd%d: %dMB (%d total sec), %d cyl, %d head, %d sec, bytes/sec %d\n",
+ unit,
+ dp->disksize / ((1024L * 1024L) / dp->secsiz),
+ dp->disksize,
+ dp->cyls,
+ dp->heads,
+ dp->sectors,
+ dp->secsiz);
+ sd->flags |= SDINIT;
+ return 0;
+}
+
+/*
+ * open the device. Make sure the partition info is a up-to-date as can be.
+ */
+errval
+sdopen(dev)
+ int dev; /* XXX should be dev_t, but avoid promotion problems for now */
+{
+ errval errcode = 0;
+ u_int32 unit, part;
+ struct sd_data *sd;
+ struct scsi_link *sc_link;
+
+ unit = UNIT(dev);
+ part = PARTITION(dev);
+ sd = sd_data[unit];
+ /*
+ * Check the unit is legal
+ */
+ if (unit >= NSD) {
+ return (ENXIO);
+ }
+ /*
+ * Make sure the disk has been initialised
+ * At some point in the future, get the scsi driver
+ * to look for a new device if we are not initted
+ */
+ if ((!sd) || (!(sd->flags & SDINIT))) {
+ return (ENXIO);
+ }
+ sc_link = sd->sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB1,
+ ("sdopen: dev=0x%x (unit %d (of %d),partition %d)\n"
+ ,dev, unit, NSD, part));
+
+ /*
+ * "unit attention" errors should occur here if the
+ * drive has been restarted or the pack changed.
+ * just ingnore the result, it's a decoy instruction
+ * The error code will act on the error though
+ * and invalidate any media information we had.
+ */
+ scsi_test_unit_ready(sc_link, 0);
+
+ /*
+ * If it's been invalidated, then forget the label
+ */
+ sc_link->flags |= SDEV_OPEN; /* unit attn becomes an err now */
+ if (!(sc_link->flags & SDEV_MEDIA_LOADED)) {
+ sd->flags &= ~SDHAVELABEL;
+
+ /*
+ * If somebody still has it open, then forbid re-entry.
+ */
+ if (sd->openparts) {
+ errcode = ENXIO;
+ goto bad;
+ }
+ }
+ /*
+ * In case it is a funny one, tell it to start
+ * not needed for most hard drives (ignore failure)
+ */
+ scsi_start_unit(sc_link, SCSI_ERR_OK | SCSI_SILENT);
+
+ /*
+ * Check that it is still responding and ok.
+ */
+ if (scsi_test_unit_ready(sc_link, 0)) {
+ SC_DEBUG(sc_link, SDEV_DB3, ("device not reponding\n"));
+ errcode = ENXIO;
+ goto bad;
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("device ok\n"));
+
+ /*
+ * Load the physical device parameters
+ */
+ sd_get_parms(unit, 0); /* sets SDEV_MEDIA_LOADED */
+ if (sd->params.secsiz != SECSIZE) { /* XXX One day... */
+ printf("sd%d: Can't deal with %d bytes logical blocks\n",
+ unit, sd->params.secsiz);
+ Debugger("sd");
+ errcode = ENXIO;
+ goto bad;
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("Params loaded "));
+
+ /* Lock the pack in. */
+ scsi_prevent(sc_link, PR_PREVENT, SCSI_ERR_OK | SCSI_SILENT);
+
+ /*
+ * Load the partition info if not already loaded.
+ */
+ if ((errcode = sdgetdisklabel(unit)) && (part != RAW_PART)) {
+ goto bad;
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("Disklabel loaded "));
+ /*
+ * Check the partition is legal
+ */
+ if (part >= MAXPARTITIONS) {
+ errcode = ENXIO;
+ goto bad;
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("partition ok"));
+
+ /*
+ * Check that the partition exists
+ */
+ if ((sd->disklabel.d_partitions[part].p_size == 0)
+ && (part != RAW_PART)) {
+ errcode = ENXIO;
+ goto bad;
+ }
+ sd->partflags[part] |= SDOPEN;
+ sd->openparts |= (1 << part);
+ SC_DEBUG(sc_link, SDEV_DB3, ("open %d %d\n", sdstrats, sdqueues));
+ return 0;
+
+bad:
+ if (!(sd->openparts)) {
+ scsi_prevent(sc_link, PR_ALLOW, SCSI_ERR_OK | SCSI_SILENT);
+ sc_link->flags &= ~SDEV_OPEN;
+ }
+ return errcode;
+}
+
+/*
+ * close the device.. only called if we are the LAST occurence of an open
+ * device. Convenient now but usually a pain.
+ */
+errval
+sdclose(dev)
+ dev_t dev;
+{
+ unsigned char unit, part;
+ struct sd_data *sd;
+
+ unit = UNIT(dev);
+ part = PARTITION(dev);
+ sd = sd_data[unit];
+ sd->partflags[part] &= ~SDOPEN;
+ sd->openparts &= ~(1 << part);
+ scsi_prevent(sd->sc_link, PR_ALLOW, SCSI_SILENT | SCSI_ERR_OK);
+ if (!(sd->openparts))
+ sd->sc_link->flags &= ~SDEV_OPEN;
+ return 0;
+}
+
+/*
+ * trim the size of the transfer if needed, called by physio
+ * basically the smaller of our max and the scsi driver's
+ * minphys (note we have no max)
+ *
+ * Trim buffer length if buffer-size is bigger than page size
+ */
+void
+sdminphys(bp)
+ struct buf *bp;
+{
+ (*(sd_data[UNIT(bp->b_dev)]->sc_link->adapter->scsi_minphys)) (bp);
+}
+
+/*
+ * Actually translate the requested transfer into one the physical driver
+ * can understand. The transfer is described by a buf and will include
+ * only one physical transfer.
+ */
+void
+sdstrategy(bp)
+ struct buf *bp;
+{
+ struct buf *dp;
+ u_int32 opri;
+ struct sd_data *sd;
+ u_int32 unit;
+
+ sdstrats++;
+ unit = UNIT((bp->b_dev));
+ sd = sd_data[unit];
+ SC_DEBUG(sd->sc_link, SDEV_DB2, ("sdstrategy "));
+ SC_DEBUG(sd->sc_link, SDEV_DB1,
+ (" %d bytes @ blk%d\n", bp->b_bcount, bp->b_blkno));
+ sdminphys(bp);
+ /*
+ * If the device has been made invalid, error out
+ */
+ if (!(sd->sc_link->flags & SDEV_MEDIA_LOADED)) {
+ sd->flags &= ~SDHAVELABEL;
+ bp->b_error = EIO;
+ goto bad;
+ }
+ /*
+ * "soft" write protect check
+ */
+ if ((sd->flags & SDWRITEPROT) && (bp->b_flags & B_READ) == 0) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+ /*
+ * If it's a null transfer, return immediatly
+ */
+ if (bp->b_bcount == 0) {
+ goto done;
+ }
+ /*
+ * Decide which unit and partition we are talking about
+ * only raw is ok if no label
+ */
+ if (PARTITION(bp->b_dev) != RAW_PART) {
+ if (!(sd->flags & SDHAVELABEL)) {
+ bp->b_error = EIO;
+ goto bad;
+ }
+ /*
+ * do bounds checking, adjust transfer. if error, process.
+ * if end of partition, just return
+ */
+ if (bounds_check_with_label(bp, &sd->disklabel, sd->wlabel) <= 0)
+ goto done;
+ /* otherwise, process transfer request */
+ } else {
+ bp->b_pblkno = bp->b_blkno;
+ bp->b_resid = 0;
+ }
+ opri = SPLSD();
+ dp = &sd->buf_queue;
+
+ /*
+ * Use a bounce buffer if necessary
+ */
+/*
+#ifndef NOBOUNCE
+ if (sd->sc_link->flags & SDEV_BOUNCE)
+ vm_bounce_alloc(bp);
+#endif
+*/
+
+ /*
+ * Place it in the queue of disk activities for this disk
+ */
+/*
+ cldisksort(dp, bp, 64*1024);
+*/
+if ((bp->b_blkno < 0) || (bp->b_bcount > 3000000) /* || (bp->b_flags & B_WRITE) */) {
+ printf("blkno=%d bcount=%d flags=0x%x\n", bp->b_blkno, bp->b_bcount, bp->b_flags);
+ Debugger("");
+}
+ disksort(dp, bp);
+
+ /*
+ * Tell the device to get going on the transfer if it's
+ * not doing anything, otherwise just wait for completion
+ */
+ sdstart(unit);
+
+ splx(opri);
+ return /*0*/;
+bad:
+ bp->b_flags |= B_ERROR;
+done:
+
+ /*
+ * Correctly set the buf to indicate a completed xfer
+ */
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+ return /*0*/;
+}
+
+/*
+ * sdstart looks to see if there is a buf waiting for the device
+ * and that the device is not already busy. If both are true,
+ * It dequeues the buf and creates a scsi command to perform the
+ * transfer in the buf. The transfer request will call scsi_done
+ * on completion, which will in turn call this routine again
+ * so that the next queued transfer is performed.
+ * The bufs are queued by the strategy routine (sdstrategy)
+ *
+ * This routine is also called after other non-queued requests
+ * have been made of the scsi driver, to ensure that the queue
+ * continues to be drained.
+ *
+ * must be called at the correct (highish) spl level
+ * sdstart() is called at SPLSD from sdstrategy and scsi_done
+ */
+void
+sdstart(unit)
+ u_int32 unit;
+{
+ register struct sd_data *sd = sd_data[unit];
+ register struct scsi_link *sc_link = sd->sc_link;
+ struct buf *bp = 0;
+ struct buf *dp;
+ struct scsi_rw_big cmd;
+ u_int32 blkno, nblk;
+ struct partition *p;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("sdstart "));
+ /*
+ * Check if the device has room for another command
+ */
+ while (sc_link->opennings) {
+
+ /*
+ * there is excess capacity, but a special waits
+ * It'll need the adapter as soon as we clear out of the
+ * way and let it run (user level wait).
+ */
+ if (sc_link->flags & SDEV_WAITING) {
+ return;
+ }
+ /*
+ * See if there is a buf with work for us to do..
+ */
+ dp = &sd->buf_queue;
+ if ((bp = dp->b_actf) == NULL) { /* yes, an assign */
+ return;
+ }
+ dp->b_actf = bp->b_actf;
+
+ /*
+ * If the device has become invalid, abort all the
+ * reads and writes until all files have been closed and
+ * re-openned
+ */
+ if (!(sc_link->flags & SDEV_MEDIA_LOADED)) {
+ sd->flags &= ~SDHAVELABEL;
+ goto bad;
+ }
+ /*
+ * We have a buf, now we know we are going to go through
+ * With this thing..
+ *
+ * First, translate the block to absolute
+ */
+ p = sd->disklabel.d_partitions + PARTITION(bp->b_dev);
+ blkno = bp->b_blkno + p->p_offset;
+ nblk = (bp->b_bcount + 511) >> 9;
+
+ /*
+ * Fill out the scsi command
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.op_code = (bp->b_flags & B_READ)
+ ? READ_BIG : WRITE_BIG;
+ cmd.addr_3 = (blkno & 0xff000000UL) >> 24;
+ cmd.addr_2 = (blkno & 0xff0000) >> 16;
+ cmd.addr_1 = (blkno & 0xff00) >> 8;
+ cmd.addr_0 = blkno & 0xff;
+ cmd.length2 = (nblk & 0xff00) >> 8;
+ cmd.length1 = (nblk & 0xff);
+ /*
+ * Call the routine that chats with the adapter.
+ * Note: we cannot sleep as we may be an interrupt
+ */
+ if (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &cmd,
+ sizeof(cmd),
+ (u_char *) bp->b_un.b_addr,
+ bp->b_bcount,
+ SD_RETRIES,
+ 10000,
+ bp,
+ SCSI_NOSLEEP | ((bp->b_flags & B_READ) ?
+ SCSI_DATA_IN : SCSI_DATA_OUT))
+ == SUCCESSFULLY_QUEUED) {
+ sdqueues++;
+ } else {
+bad:
+ printf("sd%d: oops not queued", unit);
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ }
+ }
+}
+
+/*
+ * Perform special action on behalf of the user
+ * Knows about the internals of this device
+ */
+errval
+sdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
+{
+ /* struct sd_cmd_buf *args; */
+ errval error = 0;
+ unsigned char unit, part;
+ register struct sd_data *sd;
+
+ /*
+ * Find the device that the user is talking about
+ */
+ unit = UNIT(dev);
+ part = PARTITION(dev);
+ sd = sd_data[unit];
+ SC_DEBUG(sd->sc_link, SDEV_DB1, ("sdioctl (0x%x)", cmd));
+
+ /*
+ * If the device is not valid.. abandon ship
+ */
+ if (!(sd->sc_link->flags & SDEV_MEDIA_LOADED))
+ return (EIO);
+ switch (cmd) {
+
+ case DIOCSBAD:
+ error = EINVAL;
+ break;
+
+ case DIOCGDINFO:
+ *(struct disklabel *) addr = sd->disklabel;
+ break;
+
+ case DIOCGPART:
+ ((struct partinfo *) addr)->disklab = &sd->disklabel;
+ ((struct partinfo *) addr)->part =
+ &sd->disklabel.d_partitions[PARTITION(dev)];
+ break;
+
+ case DIOCSDINFO:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else
+ error = setdisklabel(&sd->disklabel,
+ (struct disklabel *)addr,
+ /*(sd->flags & DKFL_BSDLABEL) ? sd->openparts : */ 0
+#ifdef NetBSD
+ ,&sd->cpudisklabel
+#else
+#if 0
+ ,sd->dosparts
+#endif
+#endif
+ );
+ if (error == 0) {
+ sd->flags |= SDHAVELABEL;
+ }
+ break;
+
+ case DIOCWLABEL:
+ sd->flags &= ~SDWRITEPROT;
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else
+ sd->wlabel = *(boolean *) addr;
+ break;
+
+ case DIOCWDINFO:
+ sd->flags &= ~SDWRITEPROT;
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else {
+ error = setdisklabel(&sd->disklabel,
+ (struct disklabel *)addr,
+ /*(sd->flags & SDHAVELABEL) ? sd->openparts : */ 0
+#ifdef NetBSD
+ ,&sd->cpudisklabel
+#else
+#if 0
+ ,sd->dosparts
+#endif
+#endif
+ );
+ if (!error) {
+ boolean wlab;
+
+ /* ok - write will succeed */
+ sd->flags |= SDHAVELABEL;
+
+ /* simulate opening partition 0 so write succeeds */
+ sd->openparts |= (1 << 0); /* XXX */
+ wlab = sd->wlabel;
+ sd->wlabel = 1;
+ error = writedisklabel(dev, sdstrategy,
+ &sd->disklabel
+#ifdef NetBSD
+ ,&sd->cpudisklabel
+#else
+#if 0
+ ,sd->dosparts
+#endif
+#endif
+ );
+ sd->wlabel = wlab;
+ }
+ }
+ break;
+
+ default:
+ if (part == RAW_PART)
+ error = scsi_do_ioctl(sd->sc_link, cmd, addr, flag);
+ else
+ error = ENOTTY;
+ break;
+ }
+ return error;
+}
+
+/*
+ * Load the label information on the named device
+ */
+errval
+sdgetdisklabel(unsigned char unit)
+{
+ char *errstring;
+ struct sd_data *sd = sd_data[unit];
+ dev_t dev;
+
+ dev = makedev(0, (unit << UNITSHIFT) + 3);
+ /*
+ * If the inflo is already loaded, use it
+ */
+ if (sd->flags & SDHAVELABEL)
+ return (ESUCCESS);
+
+ bzero(&sd->disklabel, sizeof(struct disklabel));
+ /*
+ * make partition 3 the whole disk in case of failure then get pdinfo
+ * for historical reasons, make part a same as raw part
+ */
+ sd->disklabel.d_partitions[0].p_offset = 0;
+ sd->disklabel.d_partitions[0].p_size = sd->params.disksize;
+ sd->disklabel.d_partitions[RAW_PART].p_offset = 0;
+ sd->disklabel.d_partitions[RAW_PART].p_size = sd->params.disksize;
+ sd->disklabel.d_npartitions = MAXPARTITIONS;
+ sd->disklabel.d_secsize = SECSIZE; /* as long as it's not 0 */
+ sd->disklabel.d_ntracks = sd->params.heads;
+ sd->disklabel.d_nsectors = sd->params.sectors;
+ sd->disklabel.d_ncylinders = sd->params.cyls;
+ sd->disklabel.d_secpercyl = sd->params.heads * sd->params.sectors;
+ if (sd->disklabel.d_secpercyl == 0) {
+ sd->disklabel.d_secpercyl = 100;
+ /* as long as it's not 0 - readdisklabel divides by it (?) */
+ }
+ /*
+ * Call the generic disklabel extraction routine
+ */
+ if (errstring = readdisklabel(makedev(0, (unit << UNITSHIFT) + 3),
+ sdstrategy,
+ &sd->disklabel
+#ifdef NetBSD
+ ,&sd->cpu_disklabel,
+#else
+ ,sd->dosparts, 0
+#endif
+ )) {
+ printf("sd%d: %s\n", unit, errstring);
+ return ENXIO;
+ }
+ sd->flags |= SDHAVELABEL; /* WE HAVE IT ALL NOW */
+ return ESUCCESS;
+}
+
+/*
+ * Find out from the device what it's capacity is
+ */
+u_int32
+sd_size(unit, flags)
+ int unit, flags;
+{
+ struct scsi_read_cap_data rdcap;
+ struct scsi_read_capacity scsi_cmd;
+ u_int32 size;
+
+ /*
+ * make up a scsi command and ask the scsi driver to do
+ * it for you.
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = READ_CAPACITY;
+
+ /*
+ * If the command works, interpret the result as a 4 byte
+ * number of blocks
+ */
+ if (scsi_scsi_cmd(sd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) & rdcap,
+ sizeof(rdcap),
+ SD_RETRIES,
+ 2000,
+ NULL,
+ flags | SCSI_DATA_IN) != 0) {
+ printf("sd%d: could not get size\n", unit);
+ return (0);
+ } else {
+ size = rdcap.addr_0 + 1;
+ size += rdcap.addr_1 << 8;
+ size += rdcap.addr_2 << 16;
+ size += rdcap.addr_3 << 24;
+ }
+ return (size);
+}
+
+/*
+ * Tell the device to map out a defective block
+ */
+errval
+sd_reassign_blocks(unit, block)
+ int unit, block;
+{
+ struct scsi_reassign_blocks scsi_cmd;
+ struct scsi_reassign_blocks_data rbdata;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ bzero(&rbdata, sizeof(rbdata));
+ scsi_cmd.op_code = REASSIGN_BLOCKS;
+
+ rbdata.length_msb = 0;
+ rbdata.length_lsb = sizeof(rbdata.defect_descriptor[0]);
+ rbdata.defect_descriptor[0].dlbaddr_3 = ((block >> 24) & 0xff);
+ rbdata.defect_descriptor[0].dlbaddr_2 = ((block >> 16) & 0xff);
+ rbdata.defect_descriptor[0].dlbaddr_1 = ((block >> 8) & 0xff);
+ rbdata.defect_descriptor[0].dlbaddr_0 = ((block) & 0xff);
+
+ return (scsi_scsi_cmd(sd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) & rbdata,
+ sizeof(rbdata),
+ SD_RETRIES,
+ 5000,
+ NULL,
+ SCSI_DATA_OUT));
+}
+#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 )
+
+/*
+ * Get the scsi driver to send a full inquiry to the
+ * device and use the results to fill out the disk
+ * parameter structure.
+ */
+errval
+sd_get_parms(unit, flags)
+ int unit, flags;
+{
+ struct sd_data *sd = sd_data[unit];
+ struct disk_parms *disk_parms = &sd->params;
+ struct scsi_mode_sense scsi_cmd;
+ struct scsi_mode_sense_data {
+ struct scsi_mode_header header;
+ struct blk_desc blk_desc;
+ union disk_pages pages;
+ } scsi_sense;
+ u_int32 sectors;
+
+ /*
+ * First check if we have it all loaded
+ */
+ if (sd->sc_link->flags & SDEV_MEDIA_LOADED)
+ return 0;
+
+ /*
+ * do a "mode sense page 4"
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = MODE_SENSE;
+ scsi_cmd.page = 4;
+ scsi_cmd.length = 0x20;
+ /*
+ * If the command worked, use the results to fill out
+ * the parameter structure
+ */
+ if (scsi_scsi_cmd(sd->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) & scsi_sense,
+ sizeof(scsi_sense),
+ SD_RETRIES,
+ 2000,
+ NULL,
+ flags | SCSI_DATA_IN) != 0) {
+
+ printf("sd%d could not mode sense (4).", unit);
+ printf(" Using ficticious geometry\n");
+ /*
+ * use adaptec standard ficticious geometry
+ * this depends on which controller (e.g. 1542C is
+ * different. but we have to put SOMETHING here..)
+ */
+ sectors = sd_size(unit, flags);
+ disk_parms->heads = 64;
+ disk_parms->sectors = 32;
+ disk_parms->cyls = sectors / (64 * 32);
+ disk_parms->secsiz = SECSIZE;
+ disk_parms->disksize = sectors;
+ } else {
+
+ SC_DEBUG(sd->sc_link, SDEV_DB3,
+ ("%d cyls, %d heads, %d precomp, %d red_write, %d land_zone\n",
+ _3btol(&scsi_sense.pages.rigid_geometry.ncyl_2),
+ scsi_sense.pages.rigid_geometry.nheads,
+ b2tol(scsi_sense.pages.rigid_geometry.st_cyl_wp),
+ b2tol(scsi_sense.pages.rigid_geometry.st_cyl_rwc),
+ b2tol(scsi_sense.pages.rigid_geometry.land_zone)));
+
+ /*
+ * KLUDGE!!(for zone recorded disks)
+ * give a number of sectors so that sec * trks * cyls
+ * is <= disk_size
+ * can lead to wasted space! THINK ABOUT THIS !
+ */
+ disk_parms->heads = scsi_sense.pages.rigid_geometry.nheads;
+ disk_parms->cyls = _3btol(&scsi_sense.pages.rigid_geometry.ncyl_2);
+ disk_parms->secsiz = _3btol(scsi_sense.blk_desc.blklen);
+
+ sectors = sd_size(unit, flags);
+ disk_parms->disksize = sectors;
+ /* Check if none of these values are zero */
+ if(disk_parms->heads && disk_parms->cyls) {
+ sectors /= (disk_parms->heads * disk_parms->cyls);
+ }
+ else {
+ /* set it to something reasonable */
+ sectors = 32;
+ disk_parms->heads = 64;
+ disk_parms->cyls = sectors / (64 * 32);
+ }
+ disk_parms->sectors = sectors; /* dubious on SCSI *//*XXX */
+ }
+ sd->sc_link->flags |= SDEV_MEDIA_LOADED;
+ return 0;
+}
+
+int
+sdsize(dev_t dev)
+{
+ u_int32 unit = UNIT(dev), part = PARTITION(dev), val;
+ struct sd_data *sd;
+
+ if (unit >= NSD)
+ return -1;
+
+ sd = sd_data[unit];
+ if (!sd)
+ return -1;
+ if ((sd->flags & SDINIT) == 0)
+ return -1;
+ if (sd == 0 || (sd->flags & SDHAVELABEL) == 0) {
+ val = sdopen(MAKESDDEV(major(dev), unit, RAW_PART), FREAD, S_IFBLK, 0);
+ if (val != 0)
+ return -1;
+ }
+ if (sd->flags & SDWRITEPROT)
+ return -1;
+
+ return (int)sd->disklabel.d_partitions[part].p_size;
+}
+
+/*
+ * dump all of physical memory into the partition specified, starting
+ * at offset 'dumplo' into the partition.
+ */
+errval
+sddump(dev_t dev)
+{ /* dump core after a system crash */
+ register struct sd_data *sd; /* disk unit to do the IO */
+ int32 num; /* number of sectors to write */
+ u_int32 unit, part;
+ int32 blkoff, blknum, blkcnt = MAXTRANSFER;
+ int32 nblocks;
+ char *addr;
+ struct scsi_rw_big cmd;
+ extern int Maxmem;
+ static int sddoingadump = 0;
+ extern caddr_t CADDR1; /* map the page we are about to write, here */
+ extern struct pte *CMAP1;
+ struct scsi_xfer *xs = &sx;
+ errval retval;
+ int c;
+
+ addr = (char *) 0; /* starting address */
+
+ /* toss any characters present prior to dump */
+ while ((c = sgetc(1)) && (c != 0x100)); /*syscons and pccons differ */
+
+ /* size of memory to dump */
+ num = Maxmem;
+ unit = UNIT(dev); /* eventually support floppies? */
+ part = PARTITION(dev); /* file system */
+ /* check for acceptable drive number */
+ if (unit >= NSD)
+ return (ENXIO);
+
+ sd = sd_data[unit];
+ if (!sd)
+ return (ENXIO);
+ /* was it ever initialized etc. ? */
+ if (!(sd->flags & SDINIT))
+ return (ENXIO);
+ if (sd->sc_link->flags & SDEV_MEDIA_LOADED != SDEV_MEDIA_LOADED)
+ return (ENXIO);
+ if (sd->flags & SDWRITEPROT)
+ return (ENXIO);
+
+ /* Convert to disk sectors */
+ num = (u_int32) num * NBPG / sd->disklabel.d_secsize;
+
+ /* check if controller active */
+ if (sddoingadump)
+ return (EFAULT);
+
+ nblocks = sd->disklabel.d_partitions[part].p_size;
+ blkoff = sd->disklabel.d_partitions[part].p_offset;
+
+ /* check transfer bounds against partition size */
+ if ((dumplo < 0) || ((dumplo + num) > nblocks))
+ return (EINVAL);
+
+ sddoingadump = 1;
+
+ blknum = dumplo + blkoff;
+ while (num > 0) {
+ *(int *)CMAP1 =
+ PG_V | PG_KW | trunc_page(addr);
+ tlbflush();
+ /*
+ * Fill out the scsi command
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.op_code = WRITE_BIG;
+ cmd.addr_3 = (blknum & 0xff000000) >> 24;
+ cmd.addr_2 = (blknum & 0xff0000) >> 16;
+ cmd.addr_1 = (blknum & 0xff00) >> 8;
+ cmd.addr_0 = blknum & 0xff;
+ cmd.length2 = (blkcnt & 0xff00) >> 8;
+ cmd.length1 = (blkcnt & 0xff);
+ /*
+ * Fill out the scsi_xfer structure
+ * Note: we cannot sleep as we may be an interrupt
+ * don't use scsi_scsi_cmd() as it may want
+ * to wait for an xs.
+ */
+ bzero(xs, sizeof(sx));
+ xs->flags |= SCSI_NOMASK | SCSI_NOSLEEP | INUSE;
+ xs->sc_link = sd->sc_link;
+ xs->retries = SD_RETRIES;
+ xs->timeout = 10000; /* 10000 millisecs for a disk ! */
+ xs->cmd = (struct scsi_generic *) &cmd;
+ xs->cmdlen = sizeof(cmd);
+ xs->resid = blkcnt * 512;
+ xs->error = XS_NOERROR;
+ xs->bp = 0;
+ xs->data = (u_char *) CADDR1;
+ xs->datalen = blkcnt * 512;
+
+ /*
+ * Pass all this info to the scsi driver.
+ */
+ retval = (*(sd->sc_link->adapter->scsi_cmd)) (xs);
+ switch (retval) {
+ case SUCCESSFULLY_QUEUED:
+ case HAD_ERROR:
+ return (ENXIO); /* we said not to sleep! */
+ case COMPLETE:
+ break;
+ default:
+ return (ENXIO); /* we said not to sleep! */
+ }
+
+ if ((unsigned) addr % (1024 * 1024) == 0)
+ printf("%d ", num / 2048);
+ /* update block count */
+ num -= blkcnt;
+ blknum += blkcnt;
+ (int) addr += 512 * blkcnt;
+
+ /* operator aborting dump? */
+ if ((c = sgetc(1)) && (c != 0x100))
+ return (EINTR);
+ }
+ return (0);
+}
diff --git a/sys/scsi/st.c b/sys/scsi/st.c
new file mode 100644
index 0000000..b72cb76
--- /dev/null
+++ b/sys/scsi/st.c
@@ -0,0 +1,1936 @@
+/*
+ * Written by Julian Elischer (julian@tfs.com)(now julian@DIALix.oz.au)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 1 00098
+ * -------------------- ----- ----------------------
+ *
+ * 16 Feb 93 Julian Elischer ADDED for SCSI system
+ * 1.15 is the last version to support MACH and OSF/1
+ */
+/* $Revision: 1.15 $ */
+
+/*
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ * major changes by Julian Elischer (julian@jules.dialix.oz.au) May 1993
+ *
+ * $Id: st.c,v 1.15 1994/01/29 10:30:41 rgrimes Exp $
+ */
+
+/*
+ * To do:
+ * work out some better way of guessing what a good timeout is going
+ * to be depending on whether we expect to retension or not.
+ *
+ */
+
+#include <sys/types.h>
+#include <st.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <fcntl.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/mtio.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_tape.h>
+#include <scsi/scsiconf.h>
+
+u_int32 ststrats, stqueues;
+
+/* Defines for device specific stuff */
+#define PAGE_0_SENSE_DATA_SIZE 12
+#define PAGESIZ 4096
+#define DEF_FIXED_BSIZE 512
+#define ST_RETRIES 4 /* only on non IO commands */
+
+#define MODE(z) ( (minor(z) & 0x03) )
+#define DSTY(z) ( ((minor(z) >> 2) & 0x03) )
+#define UNIT(z) ( (minor(z) >> 4) )
+#define CTLMODE 3
+
+#define SCSI_2_MAX_DENSITY_CODE 0x17 /* maximum density code specified
+ * in SCSI II spec. */
+/*
+ * Define various devices that we know mis-behave in some way,
+ * and note how they are bad, so we can correct for them
+ */
+struct modes {
+ u_int32 blksiz;
+ u_int32 quirks; /* same definitions as in rogues */
+ char density;
+ char spare[3];
+};
+
+struct rogues {
+ char *name;
+ char *manu;
+ char *model;
+ char *version;
+ u_int32 quirks; /* valid for all modes */
+ struct modes modes[4];
+};
+
+/* define behaviour codes (quirks) */
+#define ST_Q_NEEDS_PAGE_0 0x00001
+#define ST_Q_FORCE_FIXED_MODE 0x00002
+#define ST_Q_FORCE_VAR_MODE 0x00004
+#define ST_Q_SNS_HLP 0x00008 /* must do READ for good MODE SENSE */
+#define ST_Q_IGNORE_LOADS 0x00010
+#define ST_Q_BLKSIZ 0x00020 /* variable-block media_blksiz > 0 */
+
+static struct rogues gallery[] = /* ends with an all-null entry */
+{
+ {"Such an old device ", "pre-scsi", " unknown model ", "????",
+ 0,
+ {
+ {512, ST_Q_FORCE_FIXED_MODE, 0}, /* minor 0,1,2,3 */
+ {512, ST_Q_FORCE_FIXED_MODE, QIC_24}, /* minor 4,5,6,7 */
+ {0, ST_Q_FORCE_VAR_MODE, HALFINCH_1600}, /* minor 8,9,10,11 */
+ {0, ST_Q_FORCE_VAR_MODE, HALFINCH_6250} /* minor 12,13,14,15 */
+ }
+ },
+ {"Tandberg tdc3600", "TANDBERG", " TDC 3600", "????",
+ ST_Q_NEEDS_PAGE_0,
+ {
+ {0, 0, 0}, /* minor 0,1,2,3 */
+ {0, ST_Q_FORCE_VAR_MODE, QIC_525}, /* minor 4,5,6,7 */
+ {0, 0, QIC_150}, /* minor 8,9,10,11 */
+ {0, 0, QIC_120} /* minor 12,13,14,15 */
+ }
+ },
+ {"Rev 5 of the Archive 2525", "ARCHIVE ", "VIPER 2525 25462", "-005",
+ 0,
+ {
+ {0, ST_Q_SNS_HLP, 0}, /* minor 0,1,2,3 */
+ {0, ST_Q_SNS_HLP, QIC_525}, /* minor 4,5,6,7 */
+ {0, 0, QIC_150}, /* minor 8,9,10,11 */
+ {0, 0, QIC_120} /* minor 12,13,14,15 */
+ }
+ },
+ {"Archive Viper 150", "ARCHIVE ", "VIPER 150", "????",
+ ST_Q_NEEDS_PAGE_0,
+ {
+ {0, 0, 0}, /* minor 0,1,2,3 */
+ {0, 0, QIC_150}, /* minor 4,5,6,7 */
+ {0, 0, QIC_120}, /* minor 8,9,10,11 */
+ {0, 0, QIC_24} /* minor 12,13,14,15 */
+ }
+ },
+ {"Wangtek 5525ES", "WANGTEK ", "5525ES SCSI REV7", "????",
+ 0,
+ {
+ {0, 0, 0}, /* minor 0,1,2,3 */
+ {0, ST_Q_BLKSIZ, QIC_525}, /* minor 4,5,6,7 */
+ {0, 0, QIC_150}, /* minor 8,9,10,11 */
+ {0, 0, QIC_120} /* minor 12,13,14,15 */
+ }
+ },
+ {"WangDAT model 1300", "WangDAT ", "Model 1300", "????",
+ 0,
+ {
+ {0, 0, 0}, /* minor 0,1,2,3 */
+ {512, ST_Q_FORCE_FIXED_MODE, 0x13}, /* minor 4,5,6,7 */
+ {1024, ST_Q_FORCE_FIXED_MODE, 0x13}, /* minor 8,9,10,11 */
+ {0, ST_Q_FORCE_VAR_MODE, 0x13} /* minor 12,13,14,15 */
+ }
+ },
+ {(char *) 0}
+};
+
+errval st_space __P((u_int32 unit, int32 number, u_int32 what, u_int32 flags));
+errval st_rewind __P((u_int32 unit, boolean immed, u_int32 flags));
+errval st_mode_sense __P((u_int32 unit, u_int32 flags));
+errval st_decide_mode __P((u_int32 unit, boolean first_read));
+errval st_rd_blk_lim __P((u_int32 unit, u_int32 flags));
+errval st_touch_tape __P((u_int32 unit));
+errval st_write_filemarks __P((u_int32 unit, int32 number, u_int32 flags));
+errval st_load __P((u_int32 unit, u_int32 type, u_int32 flags));
+errval st_mode_select __P((u_int32 unit, u_int32 flags));
+void ststrategy();
+void stminphys();
+int32 st_chkeod();
+errval stattach();
+void ststart();
+void st_unmount();
+errval st_mount_tape();
+void st_loadquirks();
+void st_identify_drive();
+errval st_interpret_sense();
+
+#define ESUCCESS 0
+#define NOEJECT 0
+#define EJECT 1
+
+struct scsi_device st_switch =
+{
+ st_interpret_sense, /* check errors with us first */
+ ststart, /* we have a queue, and this is how we service it */
+ NULL,
+ NULL, /* use the default 'done' routine */
+ "st",
+ 0,
+ { 0, 0 }
+};
+
+struct st_data {
+/*--------------------present operating parameters, flags etc.----------------*/
+ u_int32 flags; /* see below */
+ u_int32 blksiz; /* blksiz we are using */
+ u_int32 density; /* present density */
+ u_int32 quirks; /* quirks for the open mode */
+ u_int32 last_dsty; /* last density openned */
+/*--------------------device/scsi parameters----------------------------------*/
+ struct scsi_link *sc_link; /* our link to the adpter etc. */
+/*--------------------parameters reported by the device ----------------------*/
+ u_int32 blkmin; /* min blk size */
+ u_int32 blkmax; /* max blk size */
+ struct rogues *rogues; /* if we have a rogue entry */
+/*--------------------parameters reported by the device for this media--------*/
+ u_int32 numblks; /* nominal blocks capacity */
+ u_int32 media_blksiz; /* 0 if not ST_FIXEDBLOCKS */
+ u_int32 media_density; /* this is what it said when asked */
+/*--------------------quirks for the whole drive------------------------------*/
+ u_int32 drive_quirks; /* quirks of this drive */
+/*--------------------How we should set up when openning each minor device----*/
+ struct modes modes[4]; /* plus more for each mode */
+ u_int8 modeflags[4]; /* flags for the modes */
+#define DENSITY_SET_BY_USER 0x01
+#define DENSITY_SET_BY_QUIRK 0x02
+#define BLKSIZE_SET_BY_USER 0x04
+#define BLKSIZE_SET_BY_QUIRK 0x08
+/*--------------------storage for sense data returned by the drive------------*/
+ unsigned char sense_data[12]; /*
+ * additional sense data needed
+ * for mode sense/select.
+ */
+ struct buf *buf_queue; /* the queue of pending IO operations */
+ struct scsi_xfer scsi_xfer; /* scsi xfer struct for this drive */
+ u_int32 xfer_block_wait; /* is a process waiting? */
+} *st_data[NST];
+
+#define ST_INITIALIZED 0x01
+#define ST_INFO_VALID 0x02
+#define ST_OPEN 0x04
+#define ST_BLOCK_SET 0x08 /* block size, mode set by ioctl */
+#define ST_WRITTEN 0x10 /* data have been written, EOD needed */
+#define ST_FIXEDBLOCKS 0x20
+#define ST_AT_FILEMARK 0x40
+#define ST_EIO_PENDING 0x80 /* we couldn't report it then (had data) */
+#define ST_NEW_MOUNT 0x100 /* still need to decide mode */
+#define ST_READONLY 0x200 /* st_mode_sense says write protected */
+#define ST_FM_WRITTEN 0x400 /*
+ * EOF file mark written -- used with
+ * ~ST_WRITTEN to indicate that multiple file
+ * marks have been written
+ */
+#define ST_BLANK_READ 0x800 /* BLANK CHECK encountered already */
+#define ST_2FM_AT_EOD 0x1000 /* write 2 file marks at EOD */
+#define ST_MOUNTED 0x2000 /* Device is presently mounted */
+
+#define ST_PER_ACTION (ST_AT_FILEMARK | ST_EIO_PENDING | ST_BLANK_READ)
+#define ST_PER_MOUNT (ST_INFO_VALID | ST_BLOCK_SET | ST_WRITTEN | \
+ ST_FIXEDBLOCKS | ST_READONLY | \
+ ST_FM_WRITTEN | ST_2FM_AT_EOD | ST_PER_ACTION)
+
+static u_int32 next_st_unit = 0;
+
+/*
+ * The routine called by the low level scsi routine when it discovers
+ * A device suitable for this driver
+ */
+
+errval
+stattach(sc_link)
+ struct scsi_link *sc_link;
+{
+ u_int32 unit;
+ struct st_data *st;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("stattach: "));
+ /*
+ * Check we have the resources for another drive
+ */
+ unit = next_st_unit++;
+
+ if (unit >= NST) {
+ printf("Too many scsi tapes..(%d > %d) reconfigure kernel\n",
+ (unit + 1), NST);
+ return 0;
+ }
+ if (st_data[unit]) {
+ printf("st%d: Already has storage!\n", unit);
+ return 0;
+ }
+ sc_link->device = &st_switch;
+ sc_link->dev_unit = unit;
+ st = st_data[unit] = malloc(sizeof(struct st_data), M_DEVBUF, M_NOWAIT);
+ if (!st) {
+ printf("st%d: malloc failed in st.c\n", unit);
+ return 0;
+ }
+ bzero(st, sizeof(struct st_data));
+
+ /*
+ * Store information needed to contact our base driver
+ */
+ st->sc_link = sc_link;
+
+ /*
+ * Check if the drive is a known criminal and take
+ * Any steps needed to bring it into line
+ */
+ st_identify_drive(unit);
+
+ /*
+ * Use the subdriver to request information regarding
+ * the drive. We cannot use interrupts yet, so the
+ * request must specify this.
+ */
+ if (st_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT)) {
+ printf("st%d: drive offline\n", unit);
+ } else {
+ printf("st%d: density code 0x%x, ", unit, st->media_density);
+ if (!scsi_test_unit_ready(sc_link, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT)) {
+ if (st->media_blksiz) {
+ printf("%d-byte", st->media_blksiz);
+ } else {
+ printf("variable");
+ }
+ printf(" blocks, write-%s\n",
+ (st->flags & ST_READONLY) ? "protected" : "enabled");
+ } else {
+ printf(" drive empty\n");
+ }
+ }
+ /*
+ * Set up the buf queue for this device
+ */
+ st->buf_queue = 0;
+ st->flags |= ST_INITIALIZED;
+ return 0;
+}
+
+/*
+ * Use the inquiry routine in 'scsi_base' to get drive info so we can
+ * Further tailor our behaviour.
+ */
+void
+st_identify_drive(unit)
+ u_int32 unit;
+{
+ struct st_data *st = st_data[unit];
+ struct scsi_inquiry_data inqbuf;
+ struct rogues *finger;
+ char manu[32];
+ char model[32];
+ char model2[32];
+ char version[32];
+ u_int32 model_len;
+
+ /*
+ * Get the device type information
+ */
+ if (scsi_inquire(st->sc_link, &inqbuf,
+ SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT) != 0) {
+ printf("st%d: couldn't get device type, using default\n", unit);
+ return;
+ }
+ if ((inqbuf.version & SID_ANSII) == 0) {
+ /*
+ * If not advanced enough, use default values
+ */
+ strncpy(manu, "pre-scsi", 8);
+ manu[8] = 0;
+ strncpy(model, " unknown model ", 16);
+ model[16] = 0;
+ strncpy(version, "????", 4);
+ version[4] = 0;
+ } else {
+ strncpy(manu, inqbuf.vendor, 8);
+ manu[8] = 0;
+ strncpy(model, inqbuf.product, 16);
+ model[16] = 0;
+ strncpy(version, inqbuf.revision, 4);
+ version[4] = 0;
+ }
+
+ /*
+ * Load the parameters for this kind of device, so we
+ * treat it as appropriate for each operating mode.
+ * Only check the number of characters in the array's
+ * model entry, not the entire model string returned.
+ */
+ finger = gallery;
+ while (finger->name) {
+ model_len = 0;
+ while (finger->model[model_len] && (model_len < 32)) {
+ model2[model_len] = model[model_len];
+ model_len++;
+ }
+ model2[model_len] = 0;
+ if ((strcmp(manu, finger->manu) == 0)
+ && (strcmp(model2, finger->model) == 0 ||
+ strcmp("????????????????", finger->model) == 0)
+ && (strcmp(version, finger->version) == 0 ||
+ strcmp("????", finger->version) == 0)) {
+ printf("st%d: %s is a known rogue\n", unit, finger->name);
+ st->rogues = finger;
+ st->drive_quirks = finger->quirks;
+ st->quirks = finger->quirks; /*start value */
+ st_loadquirks(st);
+ break;
+ } else {
+ finger++; /* go to next suspect */
+ }
+ }
+}
+
+/*
+ * initialise the subdevices to the default (QUIRK) state.
+ * this will remove any setting made by the system operator or previous
+ * operations.
+ */
+void
+st_loadquirks(st)
+ struct st_data *st;
+{
+ int i;
+ struct modes *mode;
+ struct modes *mode2;
+
+ if (!st->rogues)
+ return;
+ mode = st->rogues->modes;
+ mode2 = st->modes;
+ for (i = 0; i < 4; i++) {
+ bzero(mode2, sizeof(struct modes));
+ st->modeflags[i] &= ~(BLKSIZE_SET_BY_QUIRK
+ | DENSITY_SET_BY_QUIRK
+ | BLKSIZE_SET_BY_USER
+ | DENSITY_SET_BY_USER);
+ if (mode->blksiz && ((mode->quirks | st->drive_quirks)
+ & (ST_Q_FORCE_FIXED_MODE))) {
+ mode2->blksiz = mode->blksiz;
+ st->modeflags[i] |= BLKSIZE_SET_BY_QUIRK;
+ } else {
+ if ((mode->quirks | st->drive_quirks)
+ & ST_Q_FORCE_VAR_MODE) {
+ mode2->blksiz = 0;
+ st->modeflags[i] |= BLKSIZE_SET_BY_QUIRK;
+ }
+ }
+ if (mode->density) {
+ mode2->density = mode->density;
+ st->modeflags[i] |= DENSITY_SET_BY_QUIRK;
+ }
+ mode++;
+ mode2++;
+ }
+}
+
+/*
+ * open the device.
+ */
+errval
+stopen(dev, flags)
+ dev_t dev;
+ u_int32 flags;
+{
+ u_int32 unit, mode, dsty;
+ errval errno = 0;
+ struct st_data *st;
+ struct scsi_link *sc_link;
+ unit = UNIT(dev);
+ mode = MODE(dev);
+ dsty = DSTY(dev);
+
+ /*
+ * Check the unit is legal
+ */
+ if (unit >= NST) {
+ return (ENXIO);
+ }
+ st = st_data[unit];
+ /*
+ * Make sure the device has been initialised
+ */
+ if ((st == NULL) || (!(st->flags & ST_INITIALIZED)))
+ return (ENXIO);
+
+ sc_link = st->sc_link;
+ SC_DEBUG(sc_link, SDEV_DB1, ("open: dev=0x%x (unit %d (of %d))\n"
+ ,dev, unit, NST));
+ /*
+ * Only allow one at a time
+ */
+ if (st->flags & ST_OPEN) {
+ return (EBUSY);
+ }
+ /*
+ * Throw out a dummy instruction to catch 'Unit attention
+ * errors (the error handling will invalidate all our
+ * device info if we get one, but otherwise, ignore it)
+ */
+ scsi_test_unit_ready(sc_link, SCSI_SILENT);
+
+ sc_link->flags |= SDEV_OPEN; /* unit attn are now errors */
+ /*
+ * If the mode is 3 (e.g. minor = 3,7,11,15)
+ * then the device has been openned to set defaults
+ * This mode does NOT ALLOW I/O, only ioctls
+ */
+ if (mode == CTLMODE)
+ return 0;
+
+ /*
+ * Check that the device is ready to use (media loaded?)
+ * This time take notice of the return result
+ */
+ if (errno = (scsi_test_unit_ready(sc_link, 0))) {
+ printf("st%d: not ready\n", unit);
+ st_unmount(unit, NOEJECT);
+ return (errno);
+ }
+ /*
+ * if it's a different mode, or if the media has been
+ * invalidated, unmount the tape from the previous
+ * session but continue with open processing
+ */
+ if ((st->last_dsty != dsty)
+ || (!(sc_link->flags & SDEV_MEDIA_LOADED))) {
+ st_unmount(unit, NOEJECT);
+ }
+ /*
+ * If we are not mounted, then we should start a new
+ * mount session.
+ */
+ if (!(st->flags & ST_MOUNTED)) {
+ st_mount_tape(dev, flags);
+ st->last_dsty = dsty;
+ }
+ /*
+ * Make sure that a tape opened in write-only mode will have
+ * file marks written on it when closed, even if not written to.
+ * This is for SUN compatibility
+ */
+ if ((flags & O_ACCMODE) == FWRITE)
+ st->flags |= ST_WRITTEN;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("Open complete\n"));
+
+ st->flags |= ST_OPEN;
+ return (0);
+}
+
+/*
+ * close the device.. only called if we are the LAST
+ * occurence of an open device
+ */
+errval
+stclose(dev)
+ dev_t dev;
+{
+ unsigned char unit, mode;
+ struct st_data *st;
+ struct scsi_link *sc_link;
+
+ unit = UNIT(dev);
+ mode = MODE(dev);
+ st = st_data[unit];
+ sc_link = st->sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB1, ("closing\n"));
+ if ((st->flags & (ST_WRITTEN | ST_FM_WRITTEN)) == ST_WRITTEN)
+ st_write_filemarks(unit, 1, 0);
+ switch (mode & 0x3) {
+ case 0:
+ case 3: /* for now */
+ st_unmount(unit, NOEJECT);
+ break;
+ case 1: /*leave mounted unless media seems to have been removed */
+ if (!(sc_link->flags & SDEV_MEDIA_LOADED)) {
+ st_unmount(unit, NOEJECT);
+ }
+ break;
+ case 2:
+ st_unmount(unit, EJECT);
+ break;
+ }
+ sc_link->flags &= ~SDEV_OPEN;
+ st->flags &= ~ST_OPEN;
+ return (0);
+}
+
+/*
+ * Start a new mount session.
+ * Copy in all the default parameters from the selected device mode.
+ * and try guess any that seem to be defaulted.
+ */
+errval
+st_mount_tape(dev, flags)
+ dev_t dev;
+ u_int32 flags;
+{
+ u_int32 unit, mode, dsty;
+ struct st_data *st;
+ struct scsi_link *sc_link;
+ errval errno = 0;
+
+ unit = UNIT(dev);
+ mode = MODE(dev);
+ dsty = DSTY(dev);
+ st = st_data[unit];
+ sc_link = st->sc_link;
+
+ if (st->flags & ST_MOUNTED)
+ return 0;
+
+ SC_DEBUG(sc_link, SDEV_DB1, ("mounting\n "));
+ st->flags |= ST_NEW_MOUNT;
+ st->quirks = st->drive_quirks | st->modes[dsty].quirks;
+ /*
+ * If the media is new, then make sure we give it a chance to
+ * to do a 'load' instruction. ( We assume it is new)
+ */
+ if (errno = st_load(unit, LD_LOAD, 0)) {
+ return (errno);
+ }
+ /*
+ * Throw another dummy instruction to catch
+ * 'Unit attention' errors. Some drives appear to give
+ * these after doing a Load instruction.
+ * (noteably some DAT drives)
+ */
+ scsi_test_unit_ready(sc_link, SCSI_SILENT);
+
+ /*
+ * Some devices can't tell you much until they have been
+ * asked to look at the media. This quirk does this.
+ */
+ if (st->quirks & ST_Q_SNS_HLP) {
+ if (errno = st_touch_tape(unit))
+ return errno;
+ }
+ /*
+ * Load the physical device parameters
+ * loads: blkmin, blkmax
+ */
+ if (errno = st_rd_blk_lim(unit, 0)) {
+ return errno;
+ }
+ /*
+ * Load the media dependent parameters
+ * includes: media_blksiz,media_density,numblks
+ * As we have a tape in, it should be reflected here.
+ * If not you may need the "quirk" above.
+ */
+ if (errno = st_mode_sense(unit, 0)) {
+ return errno;
+ }
+ /*
+ * If we have gained a permanent density from somewhere,
+ * then use it in preference to the one supplied by
+ * default by the driver.
+ */
+ if (st->modeflags[dsty] & (DENSITY_SET_BY_QUIRK | DENSITY_SET_BY_USER)) {
+ st->density = st->modes[dsty].density;
+ } else {
+ st->density = st->media_density;
+ }
+ /*
+ * If we have gained a permanent blocksize
+ * then use it in preference to the one supplied by
+ * default by the driver.
+ */
+ st->flags &= ~ST_FIXEDBLOCKS;
+ if (st->modeflags[dsty] & (BLKSIZE_SET_BY_QUIRK | BLKSIZE_SET_BY_USER)) {
+ st->blksiz = st->modes[dsty].blksiz;
+ if (st->blksiz) {
+ st->flags |= ST_FIXEDBLOCKS;
+ }
+ } else {
+ if (errno = st_decide_mode(unit, FALSE)) {
+ return errno;
+ }
+ }
+ if (errno = st_mode_select(unit, 0)) {
+ printf("st%d: Cannot set selected mode", unit);
+ return errno;
+ }
+ scsi_prevent(sc_link, PR_PREVENT, 0); /* who cares if it fails? */
+ st->flags &= ~ST_NEW_MOUNT;
+ st->flags |= ST_MOUNTED;
+ sc_link->flags |= SDEV_MEDIA_LOADED; /* move earlier? */
+
+ return 0;
+}
+
+/*
+ * End the present mount session.
+ * Rewind, and optionally eject the tape.
+ * Reset various flags to indicate that all new
+ * operations require another mount operation
+ */
+void
+st_unmount(int unit, boolean eject)
+{
+ struct st_data *st = st_data[unit];
+ struct scsi_link *sc_link = st->sc_link;
+ int32 nmarks;
+
+ if (!(st->flags & ST_MOUNTED))
+ return;
+ SC_DEBUG(sc_link, SDEV_DB1, ("unmounting\n"));
+ st_chkeod(unit, FALSE, &nmarks, SCSI_SILENT);
+ st_rewind(unit, FALSE, SCSI_SILENT);
+ scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT);
+ if (eject) {
+ st_load(unit, LD_UNLOAD, SCSI_SILENT);
+ }
+ st->flags &= ~(ST_MOUNTED | ST_NEW_MOUNT);
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+}
+
+/*
+ * Given all we know about the device, media, mode, 'quirks' and
+ * initial operation, make a decision as to how we should be set
+ * to run (regarding blocking and EOD marks)
+ */
+errval
+st_decide_mode(unit, first_read)
+ u_int32 unit;
+ boolean first_read;
+{
+ struct st_data *st = st_data[unit];
+#ifdef SCSIDEBUG
+ struct scsi_link *sc_link = st->sc_link;
+#endif
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("starting block mode decision\n"));
+
+ /*
+ * If the user hasn't already specified fixed or variable-length
+ * blocks and the block size (zero if variable-length), we'll
+ * have to try to figure them out ourselves.
+ *
+ * Our first shot at a method is, "The quirks made me do it!"
+ */
+ switch ((int)(st->quirks & (ST_Q_FORCE_FIXED_MODE | ST_Q_FORCE_VAR_MODE))) {
+ case (ST_Q_FORCE_FIXED_MODE | ST_Q_FORCE_VAR_MODE):
+ printf("st%d: bad quirks\n", unit);
+ return (EINVAL);
+ case ST_Q_FORCE_FIXED_MODE: /*specified fixed, but not what size */
+ st->flags |= ST_FIXEDBLOCKS;
+ if (st->blkmin && (st->blkmin == st->blkmax))
+ st->blksiz = st->blkmin;
+ else if (st->media_blksiz > 0)
+ st->blksiz = st->media_blksiz;
+ else
+ st->blksiz = DEF_FIXED_BSIZE;
+ SC_DEBUG(sc_link, SDEV_DB3, ("Quirks force fixed mode(%d)\n",
+ st->blksiz));
+ goto done;
+ case ST_Q_FORCE_VAR_MODE:
+ st->flags &= ~ST_FIXEDBLOCKS;
+ st->blksiz = 0;
+ SC_DEBUG(sc_link, SDEV_DB3, ("Quirks force variable mode\n"));
+ goto done;
+ }
+ /*
+ * If the drive can only handle fixed-length blocks and only at
+ * one size, perhaps we should just do that.
+ */
+ if (st->blkmin && (st->blkmin == st->blkmax)) {
+ st->flags |= ST_FIXEDBLOCKS;
+ st->blksiz = st->blkmin;
+ SC_DEBUG(sc_link, SDEV_DB3,
+ ("blkmin == blkmax of %d\n", st->blkmin));
+ goto done;
+ }
+ /*
+ * If the tape density mandates (or even suggests) use of fixed
+ * or variable-length blocks, comply.
+ */
+ switch ((int)st->density) {
+ case HALFINCH_800:
+ case HALFINCH_1600:
+ case HALFINCH_6250:
+ case DDS:
+ st->flags &= ~ST_FIXEDBLOCKS;
+ st->blksiz = 0;
+ SC_DEBUG(sc_link, SDEV_DB3, ("density specified variable\n"));
+ goto done;
+ case QIC_11:
+ case QIC_24:
+ case QIC_120:
+ case QIC_150:
+ case QIC_525:
+ case QIC_1320:
+ st->flags |= ST_FIXEDBLOCKS;
+ if (st->media_blksiz > 0) {
+ st->blksiz = st->media_blksiz;
+ } else {
+ st->blksiz = DEF_FIXED_BSIZE;
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("density specified fixed\n"));
+ goto done;
+ }
+ /*
+ * If we're about to read the tape, perhaps we should choose
+ * fixed or variable-length blocks and block size according to
+ * what the drive found on the tape.
+ */
+ if (first_read
+ && (!(st->quirks & ST_Q_BLKSIZ)
+ || (st->media_blksiz == 0)
+ || (st->media_blksiz == DEF_FIXED_BSIZE)
+ || (st->media_blksiz == 1024))) {
+ if (st->media_blksiz == 0) {
+ st->flags &= ~ST_FIXEDBLOCKS;
+ } else {
+ st->flags |= ST_FIXEDBLOCKS;
+ }
+ st->blksiz = st->media_blksiz;
+ SC_DEBUG(sc_link, SDEV_DB3,
+ ("Used media_blksiz of %d\n", st->media_blksiz));
+ goto done;
+ }
+ /*
+ * We're getting no hints from any direction. Choose variable-
+ * length blocks arbitrarily.
+ */
+ st->flags &= ~ST_FIXEDBLOCKS;
+ st->blksiz = 0;
+ SC_DEBUG(sc_link, SDEV_DB3, ("Give up and default to variable mode\n"));
+done:
+
+ /*
+ * Decide whether or not to write two file marks to signify end-
+ * of-data. Make the decision as a function of density. If
+ * the decision is not to use a second file mark, the SCSI BLANK
+ * CHECK condition code will be recognized as end-of-data when
+ * first read.
+ * (I think this should be a by-product of fixed/variable..julian)
+ */
+ switch ((int)st->density) {
+/* case 8 mm: What is the SCSI density code for 8 mm, anyway? */
+ case QIC_11:
+ case QIC_24:
+ case QIC_120:
+ case QIC_150:
+ case QIC_525:
+ case QIC_1320:
+ st->flags &= ~ST_2FM_AT_EOD;
+ break;
+ default:
+ st->flags |= ST_2FM_AT_EOD;
+ }
+ return 0;
+}
+
+/*
+ * trim the size of the transfer if needed,
+ * called by physio
+ * basically the smaller of our min and the scsi driver's
+ * minphys
+ */
+void
+stminphys(bp)
+ struct buf *bp;
+{
+ (*(st_data[UNIT(bp->b_dev)]->sc_link->adapter->scsi_minphys)) (bp);
+}
+
+/*
+ * Actually translate the requested transfer into
+ * one the physical driver can understand
+ * The transfer is described by a buf and will include
+ * only one physical transfer.
+ */
+void
+ststrategy(bp)
+ struct buf *bp;
+{
+ struct buf **dp;
+ unsigned char unit;
+ u_int32 opri;
+ struct st_data *st;
+
+ ststrats++;
+ unit = UNIT((bp->b_dev));
+ st = st_data[unit];
+ SC_DEBUG(st->sc_link, SDEV_DB1,
+ (" strategy: %d bytes @ blk%d\n", bp->b_bcount, bp->b_blkno));
+ /*
+ * If it's a null transfer, return immediatly
+ */
+ if (bp->b_bcount == 0) {
+ goto done;
+ }
+ /*
+ * Odd sized request on fixed drives are verboten
+ */
+ if (st->flags & ST_FIXEDBLOCKS) {
+ if (bp->b_bcount % st->blksiz) {
+ printf("st%d: bad request, must be multiple of %d\n",
+ unit, st->blksiz);
+ bp->b_error = EIO;
+ goto bad;
+ }
+ }
+ /*
+ * as are out-of-range requests on variable drives.
+ */
+ else if (bp->b_bcount < st->blkmin || bp->b_bcount > st->blkmax) {
+ printf("st%d: bad request, must be between %d and %d\n",
+ unit, st->blkmin, st->blkmax);
+ bp->b_error = EIO;
+ goto bad;
+ }
+ stminphys(bp);
+ opri = splbio();
+
+ /*
+ * Use a bounce buffer if necessary
+ */
+#ifndef NOBOUNCE
+ if (st->sc_link->flags & SDEV_BOUNCE)
+ vm_bounce_alloc(bp);
+#endif
+
+ /*
+ * Place it in the queue of activities for this tape
+ * at the end (a bit silly because we only have on user..
+ * (but it could fork() ))
+ */
+ dp = &(st->buf_queue);
+ while (*dp) {
+ dp = &((*dp)->b_actf);
+ }
+ *dp = bp;
+ bp->b_actf = NULL;
+
+ /*
+ * Tell the device to get going on the transfer if it's
+ * not doing anything, otherwise just wait for completion
+ * (All a bit silly if we're only allowing 1 open but..)
+ */
+ ststart(unit);
+
+ splx(opri);
+ return;
+bad:
+ bp->b_flags |= B_ERROR;
+done:
+ /*
+ * Correctly set the buf to indicate a completed xfer
+ */
+ iodone(bp);
+ return;
+}
+
+/*
+ * ststart looks to see if there is a buf waiting for the device
+ * and that the device is not already busy. If both are true,
+ * It dequeues the buf and creates a scsi command to perform the
+ * transfer required. The transfer request will call scsi_done
+ * on completion, which will in turn call this routine again
+ * so that the next queued transfer is performed.
+ * The bufs are queued by the strategy routine (ststrategy)
+ *
+ * This routine is also called after other non-queued requests
+ * have been made of the scsi driver, to ensure that the queue
+ * continues to be drained.
+ * ststart() is called at splbio
+ */
+void
+ststart(unit)
+ u_int32 unit;
+{
+ struct st_data *st = st_data[unit];
+ struct scsi_link *sc_link = st->sc_link;
+ register struct buf *bp = 0;
+ struct scsi_rw_tape cmd;
+ u_int32 flags;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("ststart "));
+ /*
+ * See if there is a buf to do and we are not already
+ * doing one
+ */
+ while (sc_link->opennings != 0) {
+
+ /* if a special awaits, let it proceed first */
+ if (sc_link->flags & SDEV_WAITING) {
+ sc_link->flags &= ~SDEV_WAITING;
+ wakeup((caddr_t)sc_link);
+ return;
+ }
+ if ((bp = st->buf_queue) == NULL) {
+ return; /* no work to bother with */
+ }
+ st->buf_queue = bp->b_actf;
+
+ /*
+ * if the device has been unmounted byt the user
+ * then throw away all requests until done
+ */
+ if ((!(st->flags & ST_MOUNTED))
+ || (!(sc_link->flags & SDEV_MEDIA_LOADED))) {
+ /* make sure that one implies the other.. */
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+ goto badnews;
+ }
+ /*
+ * only FIXEDBLOCK devices have pending operations
+ */
+ if (st->flags & ST_FIXEDBLOCKS) {
+ /*
+ * If we are at a filemark but have not reported it yet
+ * then we should report it now
+ */
+ if (st->flags & ST_AT_FILEMARK) {
+ if ((bp->b_flags & B_READ) == B_WRITE) {
+ /*
+ * Handling of ST_AT_FILEMARK in
+ * st_space will fill in the right file
+ * mark count.
+ * Back up over filemark
+ */
+ if (st_space(unit, 0, SP_FILEMARKS, 0) !=
+ ESUCCESS)
+ goto badnews;
+ } else {
+ bp->b_resid = bp->b_bcount;
+ bp->b_error = 0;
+ bp->b_flags &= ~B_ERROR;
+ st->flags &= ~ST_AT_FILEMARK;
+ biodone(bp);
+ continue; /* seek more work */
+ }
+ }
+ /*
+ * If we are at EIO (e.g. EOM) but have not reported it
+ * yet then we should report it now
+ */
+ if (st->flags & ST_EIO_PENDING) {
+ bp->b_resid = bp->b_bcount;
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ st->flags &= ~ST_EIO_PENDING;
+ biodone(bp);
+ continue; /* seek more work */
+ }
+ }
+ /*
+ * Fill out the scsi command
+ */
+ bzero(&cmd, sizeof(cmd));
+ if ((bp->b_flags & B_READ) == B_WRITE) {
+ cmd.op_code = WRITE_COMMAND_TAPE;
+ st->flags &= ~ST_FM_WRITTEN;
+ st->flags |= ST_WRITTEN;
+ flags = SCSI_DATA_OUT;
+ } else {
+ cmd.op_code = READ_COMMAND_TAPE;
+ flags = SCSI_DATA_IN;
+ }
+ /*
+ * Handle "fixed-block-mode" tape drives by using the
+ * block count instead of the length.
+ */
+ if (st->flags & ST_FIXEDBLOCKS) {
+ cmd.byte2 |= SRWT_FIXED;
+ lto3b(bp->b_bcount / st->blksiz, cmd.len);
+ } else {
+ lto3b(bp->b_bcount, cmd.len);
+ }
+ /*
+ * go ask the adapter to do all this for us
+ */
+ if (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &cmd,
+ sizeof(cmd),
+ (u_char *) bp->b_un.b_addr,
+ bp->b_bcount,
+ 0, /* can't retry a read on a tape really */
+ 100000,
+ bp,
+ flags | SCSI_NOSLEEP) == SUCCESSFULLY_QUEUED) {
+ stqueues++;
+ } else {
+badnews:
+ printf("st%d: oops not queued\n", unit);
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ biodone(bp);
+ }
+ } /* go back and see if we can cram more work in.. */
+}
+
+/*
+ * Perform special action on behalf of the user;
+ * knows about the internals of this device
+ */
+errval
+stioctl(dev, cmd, arg, flag)
+ dev_t dev;
+ int cmd;
+ caddr_t arg;
+ int flag;
+{
+ errval errcode = 0;
+ unsigned char unit;
+ u_int32 number, flags, dsty;
+ struct st_data *st;
+ u_int32 hold_blksiz;
+ u_int32 hold_density;
+ int32 nmarks;
+ struct mtop *mt = (struct mtop *) arg;
+
+ /*
+ * Find the device that the user is talking about
+ */
+ flags = 0; /* give error messages, act on errors etc. */
+ unit = UNIT(dev);
+ dsty = DSTY(dev);
+ st = st_data[unit];
+ hold_blksiz = st->blksiz;
+ hold_density = st->density;
+
+ switch (cmd) {
+
+ case MTIOCGET:
+ {
+ struct mtget *g = (struct mtget *) arg;
+
+ SC_DEBUG(st->sc_link, SDEV_DB1, ("[ioctl: get status]\n"));
+ bzero(g, sizeof(struct mtget));
+ g->mt_type = 0x7; /* Ultrix compat *//*? */
+ g->mt_density = st->density;
+ g->mt_blksiz = st->blksiz;
+ g->mt_density0 = st->modes[0].density;
+ g->mt_density1 = st->modes[1].density;
+ g->mt_density2 = st->modes[2].density;
+ g->mt_density3 = st->modes[3].density;
+ g->mt_blksiz0 = st->modes[0].blksiz;
+ g->mt_blksiz1 = st->modes[1].blksiz;
+ g->mt_blksiz2 = st->modes[2].blksiz;
+ g->mt_blksiz3 = st->modes[3].blksiz;
+ break;
+ }
+ case MTIOCTOP:
+ {
+
+ SC_DEBUG(st->sc_link, SDEV_DB1, ("[ioctl: op=0x%x count=0x%x]\n",
+ mt->mt_op, mt->mt_count));
+
+ /* compat: in U*x it is a short */
+ number = mt->mt_count;
+ switch ((short) (mt->mt_op)) {
+ case MTWEOF: /* write an end-of-file record */
+ errcode = st_write_filemarks(unit, number, flags);
+ break;
+ case MTBSF: /* backward space file */
+ number = -number;
+ case MTFSF: /* forward space file */
+ errcode = st_chkeod(unit, FALSE, &nmarks, flags);
+ if (errcode == ESUCCESS)
+ errcode = st_space(unit, number - nmarks,
+ SP_FILEMARKS, flags);
+ break;
+ case MTBSR: /* backward space record */
+ number = -number;
+ case MTFSR: /* forward space record */
+ errcode = st_chkeod(unit, TRUE, &nmarks, flags);
+ if (errcode == ESUCCESS)
+ errcode = st_space(unit, number, SP_BLKS, flags);
+ break;
+ case MTREW: /* rewind */
+ errcode = st_rewind(unit, FALSE, flags);
+ break;
+ case MTOFFL: /* rewind and put the drive offline */
+ st_unmount(unit, EJECT);
+ break;
+ case MTNOP: /* no operation, sets status only */
+ case MTCACHE: /* enable controller cache */
+ case MTNOCACHE: /* disable controller cache */
+ break;
+ case MTSETBSIZ: /* Set block size for device */
+#ifdef NOTYET
+ if (!(st->flags & ST_NEW_MOUNT)) {
+ uprintf("re-mount tape before changing blocksize");
+ errcode = EINVAL;
+ break;
+ }
+#endif
+ if (number == 0) {
+ st->flags &= ~ST_FIXEDBLOCKS;
+ } else {
+ if ((st->blkmin || st->blkmax) /* they exist */
+ &&((number < st->blkmin
+ || number > st->blkmax))) {
+ errcode = EINVAL;
+ break;
+ }
+ st->flags |= ST_FIXEDBLOCKS;
+ }
+ st->blksiz = number;
+ st->flags |= ST_BLOCK_SET; /*XXX */
+ goto try_new_value;
+
+ case MTSETDNSTY: /* Set density for device and mode */
+ if (number > SCSI_2_MAX_DENSITY_CODE) {
+ errcode = EINVAL;
+ } else {
+ st->density = number;
+ }
+ goto try_new_value;
+
+ default:
+ errcode = EINVAL;
+ }
+ break;
+ }
+ case MTIOCIEOT:
+ case MTIOCEEOT:
+ break;
+ default:
+ if(MODE(dev) == CTLMODE)
+ errcode = scsi_do_ioctl(st->sc_link,cmd,arg,flag);
+ else
+ errcode = ENOTTY;
+ break;
+ }
+ return errcode;
+/*-----------------------------*/
+try_new_value:
+ /*
+ * Check that the mode being asked for is aggreeable to the
+ * drive. If not, put it back the way it was.
+ */
+ if (errcode = st_mode_select(unit, 0)) { /* put it back as it was */
+ printf("st%d: Cannot set selected mode", unit);
+ st->density = hold_density;
+ st->blksiz = hold_blksiz;
+ if (st->blksiz) {
+ st->flags |= ST_FIXEDBLOCKS;
+ } else {
+ st->flags &= ~ST_FIXEDBLOCKS;
+ }
+ return (errcode);
+ }
+ /*
+ * As the drive liked it, if we are setting a new default,
+ * set it into the structures as such.
+ *
+ * The means for deciding this are not finalised yet
+ */
+ if (MODE(dev) == 0x03) {
+ /* special mode */
+ /* XXX */
+ switch ((short) (mt->mt_op)) {
+ case MTSETBSIZ:
+ st->modes[dsty].blksiz = st->blksiz;
+ st->modeflags[dsty] |= BLKSIZE_SET_BY_USER;
+ break;
+ case MTSETDNSTY:
+ st->modes[dsty].density = st->density;
+ st->modeflags[dsty] |= DENSITY_SET_BY_USER;
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Do a synchronous read.
+ */
+errval
+st_read(unit, buf, size, flags)
+ u_int32 unit, size, flags;
+ char *buf;
+{
+ struct scsi_rw_tape scsi_cmd;
+ struct st_data *st = st_data[unit];
+
+ /*
+ * If it's a null transfer, return immediatly
+ */
+ if (size == 0) {
+ return (ESUCCESS);
+ }
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = READ_COMMAND_TAPE;
+ if (st->flags & ST_FIXEDBLOCKS) {
+ scsi_cmd.byte2 |= SRWT_FIXED;
+ lto3b(size / (st->blksiz ? st->blksiz : DEF_FIXED_BSIZE),
+ scsi_cmd.len);
+ } else {
+ lto3b(size, scsi_cmd.len);
+ }
+ return (scsi_scsi_cmd(st->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) buf,
+ size,
+ 0, /* not on io commands */
+ 100000,
+ NULL,
+ flags | SCSI_DATA_IN));
+}
+#ifdef __STDC__
+#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 )
+#else
+#define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 )
+#endif
+
+/*
+ * Ask the drive what it's min and max blk sizes are.
+ */
+errval
+st_rd_blk_lim(unit, flags)
+ u_int32 unit, flags;
+{
+ struct scsi_blk_limits scsi_cmd;
+ struct scsi_blk_limits_data scsi_blkl;
+ struct st_data *st = st_data[unit];
+ errval errno;
+ struct scsi_link *sc_link = st->sc_link;
+
+ /*
+ * First check if we have it all loaded
+ */
+ if ((sc_link->flags & SDEV_MEDIA_LOADED))
+ return 0;
+
+ /*
+ * do a 'Read Block Limits'
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = READ_BLK_LIMITS;
+
+ /*
+ * do the command, update the global values
+ */
+ if (errno = scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) & scsi_blkl,
+ sizeof(scsi_blkl),
+ ST_RETRIES,
+ 5000,
+ NULL,
+ flags | SCSI_DATA_IN)) {
+ return errno;
+ }
+ st->blkmin = b2tol(scsi_blkl.min_length);
+ st->blkmax = _3btol(&scsi_blkl.max_length_2);
+
+ SC_DEBUG(sc_link, SDEV_DB3,
+ ("(%d <= blksiz <= %d)\n", st->blkmin, st->blkmax));
+ return 0;
+}
+
+/*
+ * Get the scsi driver to send a full inquiry to the
+ * device and use the results to fill out the global
+ * parameter structure.
+ *
+ * called from:
+ * attach
+ * open
+ * ioctl (to reset original blksize)
+ */
+errval
+st_mode_sense(unit, flags)
+ u_int32 unit, flags;
+{
+ u_int32 scsi_sense_len;
+ errval errno;
+ char *scsi_sense_ptr;
+ struct scsi_mode_sense scsi_cmd;
+ struct scsi_sense {
+ struct scsi_mode_header header;
+ struct blk_desc blk_desc;
+ } scsi_sense;
+
+ struct scsi_sense_page_0 {
+ struct scsi_mode_header header;
+ struct blk_desc blk_desc;
+ unsigned char sense_data[PAGE_0_SENSE_DATA_SIZE];
+ /* Tandberg tape drives returns page 00
+ * with the sense data, whether or not
+ * you want it( ie the don't like you
+ * saying you want anything less!!!!!
+ * They also expect page 00
+ * back when you issue a mode select
+ */
+ } scsi_sense_page_0;
+ struct st_data *st = st_data[unit];
+ struct scsi_link *sc_link = st->sc_link;
+
+ /*
+ * Define what sort of structure we're working with
+ */
+ if (st->quirks & ST_Q_NEEDS_PAGE_0) {
+ scsi_sense_len = sizeof(scsi_sense_page_0);
+ scsi_sense_ptr = (char *) &scsi_sense_page_0;
+ } else {
+ scsi_sense_len = sizeof(scsi_sense);
+ scsi_sense_ptr = (char *) &scsi_sense;
+ }
+ /*
+ * Set up a mode sense
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = MODE_SENSE;
+ scsi_cmd.length = scsi_sense_len;
+
+ /*
+ * do the command, but we don't need the results
+ * just print them for our interest's sake, if asked,
+ * or if we need it as a template for the mode select
+ * store it away.
+ */
+ if (errno = scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) scsi_sense_ptr,
+ scsi_sense_len,
+ ST_RETRIES,
+ 5000,
+ NULL,
+ flags | SCSI_DATA_IN)) {
+ return errno;
+ }
+ st->numblks = _3btol(((struct scsi_sense *)scsi_sense_ptr)->blk_desc.nblocks);
+ st->media_blksiz = _3btol(((struct scsi_sense *)scsi_sense_ptr)->blk_desc.blklen);
+ st->media_density = ((struct scsi_sense *) scsi_sense_ptr)->blk_desc.density;
+ if (((struct scsi_sense *) scsi_sense_ptr)->header.dev_spec &
+ SMH_DSP_WRITE_PROT) {
+ st->flags |= ST_READONLY;
+ }
+ SC_DEBUG(sc_link, SDEV_DB3,
+ ("density code 0x%x, %d-byte blocks, write-%s, ",
+ st->media_density, st->media_blksiz,
+ st->flags & ST_READONLY ? "protected" : "enabled"));
+ SC_DEBUG(sc_link, SDEV_DB3,
+ ("%sbuffered\n",
+ ((struct scsi_sense *) scsi_sense_ptr)->header.dev_spec
+ & SMH_DSP_BUFF_MODE ? "" : "un"));
+ if (st->quirks & ST_Q_NEEDS_PAGE_0) {
+ bcopy(((struct scsi_sense_page_0 *) scsi_sense_ptr)->sense_data,
+ st->sense_data,
+ sizeof(((struct scsi_sense_page_0 *) scsi_sense_ptr)->sense_data));
+ }
+ sc_link->flags |= SDEV_MEDIA_LOADED;
+ return 0;
+}
+
+/*
+ * Send a filled out parameter structure to the drive to
+ * set it into the desire modes etc.
+ */
+errval
+st_mode_select(unit, flags)
+ u_int32 unit, flags;
+{
+ u_int32 dat_len;
+ char *dat_ptr;
+ struct scsi_mode_select scsi_cmd;
+ struct dat {
+ struct scsi_mode_header header;
+ struct blk_desc blk_desc;
+ } dat;
+ struct dat_page_0 {
+ struct scsi_mode_header header;
+ struct blk_desc blk_desc;
+ unsigned char sense_data[PAGE_0_SENSE_DATA_SIZE];
+ } dat_page_0;
+ struct st_data *st = st_data[unit];
+
+ /*
+ * Define what sort of structure we're working with
+ */
+ if (st->quirks & ST_Q_NEEDS_PAGE_0) {
+ dat_len = sizeof(dat_page_0);
+ dat_ptr = (char *) &dat_page_0;
+ } else {
+ dat_len = sizeof(dat);
+ dat_ptr = (char *) &dat;
+ }
+ /*
+ * Set up for a mode select
+ */
+ bzero(dat_ptr, dat_len);
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = MODE_SELECT;
+ scsi_cmd.length = dat_len;
+ ((struct dat *) dat_ptr)->header.blk_desc_len = sizeof(struct blk_desc);
+ ((struct dat *) dat_ptr)->header.dev_spec |= SMH_DSP_BUFF_MODE_ON;
+ ((struct dat *) dat_ptr)->blk_desc.density = st->density;
+ if (st->flags & ST_FIXEDBLOCKS) {
+ lto3b(st->blksiz, ((struct dat *) dat_ptr)->blk_desc.blklen);
+ }
+ if (st->quirks & ST_Q_NEEDS_PAGE_0) {
+ bcopy(st->sense_data, ((struct dat_page_0 *) dat_ptr)->sense_data,
+ sizeof(((struct dat_page_0 *) dat_ptr)->sense_data));
+ /* the Tandberg tapes need the block size to */
+ /* be set on each mode sense/select. */
+ }
+ /*
+ * do the command
+ */
+ return (scsi_scsi_cmd(st->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) dat_ptr,
+ dat_len,
+ ST_RETRIES,
+ 5000,
+ NULL,
+ flags | SCSI_DATA_OUT));
+}
+
+/*
+ * skip N blocks/filemarks/seq filemarks/eom
+ */
+errval
+st_space(unit, number, what, flags)
+ u_int32 unit, what, flags;
+ int32 number;
+{
+ errval error;
+ struct scsi_space scsi_cmd;
+ struct st_data *st = st_data[unit];
+
+ switch ((int)what) {
+ case SP_BLKS:
+ if (st->flags & ST_PER_ACTION) {
+ if (number > 0) {
+ st->flags &= ~ST_PER_ACTION;
+ return (EIO);
+ } else if (number < 0) {
+ if (st->flags & ST_AT_FILEMARK) {
+ /*
+ * Handling of ST_AT_FILEMARK
+ * in st_space will fill in the
+ * right file mark count.
+ */
+ error = st_space(unit, 0, SP_FILEMARKS,
+ flags);
+ if (error)
+ return (error);
+ }
+ if (st->flags & ST_BLANK_READ) {
+ st->flags &= ~ST_BLANK_READ;
+ return (EIO);
+ }
+ st->flags &= ~ST_EIO_PENDING;
+ }
+ }
+ break;
+ case SP_FILEMARKS:
+ if (st->flags & ST_EIO_PENDING) {
+ if (number > 0) { /* pretend we just discover the error */
+ st->flags &= ~ST_EIO_PENDING;
+ return (EIO);
+ } else if (number < 0) { /* back away from the error */
+ st->flags &= ~ST_EIO_PENDING;
+ }
+ }
+ if (st->flags & ST_AT_FILEMARK) {
+ st->flags &= ~ST_AT_FILEMARK;
+ number--;
+ }
+ if ((st->flags & ST_BLANK_READ) && (number < 0)) { /* back away from unwritten tape */
+ st->flags &= ~ST_BLANK_READ;
+ number++; /* dubious */
+ }
+ }
+ if (number == 0) {
+ return (ESUCCESS);
+ }
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = SPACE;
+ scsi_cmd.byte2 = what & SS_CODE;
+ lto3b(number, scsi_cmd.number);
+ return (scsi_scsi_cmd(st->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ 0, /* no retries please , just fail */
+ 600000, /* 10 mins enough? */
+ NULL,
+ flags));
+}
+
+/*
+ * write N filemarks
+ */
+errval
+st_write_filemarks(unit, number, flags)
+ u_int32 unit, flags;
+ int32 number;
+{
+ struct scsi_write_filemarks scsi_cmd;
+ struct st_data *st = st_data[unit];
+
+ /*
+ * It's hard to write a negative number of file marks.
+ * Don't try.
+ */
+ if (number < 0) {
+ return EINVAL;
+ }
+ switch ((int)number) {
+ case 0: /* really a command to sync the drive's buffers */
+ break;
+ case 1:
+ if (st->flags & ST_FM_WRITTEN) { /* already have one down */
+ st->flags &= ~ST_WRITTEN;
+ } else {
+ st->flags |= ST_FM_WRITTEN;
+ }
+ st->flags &= ~ST_PER_ACTION;
+ break;
+ default:
+ st->flags &= ~(ST_PER_ACTION | ST_WRITTEN);
+ }
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = WRITE_FILEMARKS;
+ lto3b(number, scsi_cmd.number);
+ return scsi_scsi_cmd(st->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ 0, /* no retries, just fail */
+ 100000, /* 10 secs.. (may need to repos head ) */
+ NULL,
+ flags);
+}
+
+/*
+ * Make sure the right number of file marks is on tape if the
+ * tape has been written. If the position argument is true,
+ * leave the tape positioned where it was originally.
+ *
+ * nmarks returns the number of marks to skip (or, if position
+ * true, which were skipped) to get back original position.
+ */
+int32
+st_chkeod(unit, position, nmarks, flags)
+ u_int32 unit;
+ boolean position;
+ int32 *nmarks;
+ u_int32 flags;
+{
+ errval error;
+ struct st_data *st = st_data[unit];
+
+ switch ((int)(st->flags & (ST_WRITTEN | ST_FM_WRITTEN | ST_2FM_AT_EOD))) {
+ default:
+ *nmarks = 0;
+ return (ESUCCESS);
+ case ST_WRITTEN:
+ case ST_WRITTEN | ST_FM_WRITTEN | ST_2FM_AT_EOD:
+ *nmarks = 1;
+ break;
+ case ST_WRITTEN | ST_2FM_AT_EOD:
+ *nmarks = 2;
+ }
+ error = st_write_filemarks(unit, *nmarks, flags);
+ if (position && (error == ESUCCESS))
+ error = st_space(unit, -*nmarks, SP_FILEMARKS, flags);
+ return (error);
+}
+
+/*
+ * load/unload (with retension if true)
+ */
+errval
+st_load(unit, type, flags)
+ u_int32 unit, type, flags;
+{
+ struct scsi_load scsi_cmd;
+ struct st_data *st = st_data[unit];
+ struct scsi_link *sc_link = st->sc_link;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ if (type != LD_LOAD) {
+ errval error;
+ int32 nmarks;
+
+ error = st_chkeod(unit, FALSE, &nmarks, flags);
+ if (error != ESUCCESS)
+ return (error);
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+ }
+ if (st->quirks & ST_Q_IGNORE_LOADS)
+ return (0);
+ scsi_cmd.op_code = LOAD_UNLOAD;
+ scsi_cmd.how |= type;
+ return (scsi_scsi_cmd(st->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ ST_RETRIES,
+ 300000, /* 5 min */
+ NULL,
+ flags));
+}
+
+/*
+ * Rewind the device
+ */
+errval
+st_rewind(unit, immed, flags)
+ u_int32 unit, flags;
+ boolean immed;
+{
+ struct scsi_rewind scsi_cmd;
+ struct st_data *st = st_data[unit];
+ errval error;
+ int32 nmarks;
+
+ error = st_chkeod(unit, FALSE, &nmarks, flags);
+ if (error != ESUCCESS)
+ return (error);
+ st->flags &= ~ST_PER_ACTION;
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = REWIND;
+ scsi_cmd.byte2 = immed ? SR_IMMED : 0;
+ return (scsi_scsi_cmd(st->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ ST_RETRIES,
+ immed ? 5000 : 300000, /* 5 sec or 5 min */
+ NULL,
+ flags));
+}
+
+#ifdef NETBSD
+#define SIGNAL_SHORT_READ
+#else
+#define SIGNAL_SHORT_READ bp->b_flags |= B_ERROR;
+#endif
+
+/*
+ * Look at the returned sense and act on the error and detirmine
+ * The unix error number to pass back... (0 = report no error)
+ * (-1 = continue processing)
+ */
+errval
+st_interpret_sense(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_link *sc_link = xs->sc_link;
+ struct scsi_sense_data *sense = &(xs->sense);
+ boolean silent = xs->flags & SCSI_SILENT;
+ struct buf *bp = xs->bp;
+ u_int32 unit = sc_link->dev_unit;
+ struct st_data *st = st_data[unit];
+ u_int32 key;
+ int32 info;
+
+ /*
+ * Get the sense fields and work out what code
+ */
+ if (sense->error_code & SSD_ERRCODE_VALID) {
+ info = ntohl(*((int32 *) sense->ext.extended.info));
+ } else {
+ info = xs->datalen; /* bad choice if fixed blocks */
+ }
+ if ((sense->error_code & SSD_ERRCODE) != 0x70) {
+ return (-1); /* let the generic code handle it */
+ }
+ if (st->flags & ST_FIXEDBLOCKS) {
+ xs->resid = info * st->blksiz;
+ if (sense->ext.extended.flags & SSD_EOM) {
+ st->flags |= ST_EIO_PENDING;
+ if (bp) {
+ bp->b_resid = xs->resid;
+ SIGNAL_SHORT_READ
+ }
+ }
+ if (sense->ext.extended.flags & SSD_FILEMARK) {
+ st->flags |= ST_AT_FILEMARK;
+ if (bp) {
+ bp->b_resid = xs->resid;
+ SIGNAL_SHORT_READ
+ }
+ }
+ if (sense->ext.extended.flags & SSD_ILI) {
+ st->flags |= ST_EIO_PENDING;
+ if (bp) {
+ bp->b_resid = xs->resid;
+ SIGNAL_SHORT_READ
+ }
+ if (sense->error_code & SSD_ERRCODE_VALID &&
+ !silent)
+ printf("st%d: block wrong size"
+ ", %d blocks residual\n", unit
+ ,info);
+
+ /*
+ * This quirk code helps the drive read
+ * the first tape block, regardless of
+ * format. That is required for these
+ * drives to return proper MODE SENSE
+ * information.
+ */
+ if ((st->quirks & ST_Q_SNS_HLP) &&
+ !(sc_link->flags & SDEV_MEDIA_LOADED)) {
+ st->blksiz -= 512;
+ }
+ }
+ /*
+ * If no data was tranfered, do it immediatly
+ */
+ if (xs->resid >= xs->datalen) {
+ if (st->flags & ST_EIO_PENDING) {
+ return EIO;
+ }
+ if (st->flags & ST_AT_FILEMARK) {
+ if (bp) {
+ bp->b_resid = xs->resid;
+ SIGNAL_SHORT_READ
+ }
+ return 0;
+ }
+ }
+ } else { /* must be variable mode */
+ xs->resid = xs->datalen; /* to be sure */
+ if (sense->ext.extended.flags & SSD_EOM) {
+ return (EIO);
+ }
+ if (sense->ext.extended.flags & SSD_FILEMARK) {
+ if (bp)
+ bp->b_resid = bp->b_bcount;
+ return 0;
+ }
+ if (sense->ext.extended.flags & SSD_ILI) {
+ if (info < 0) {
+ /*
+ * the record was bigger than the read
+ */
+ if (!silent)
+ printf("st%d: %d-byte record "
+ "too big\n", unit,
+ xs->datalen - info);
+ return (EIO);
+ }
+ xs->resid = info;
+ if (bp) {
+ bp->b_resid = info;
+ SIGNAL_SHORT_READ
+ }
+ }
+ }
+ key = sense->ext.extended.flags & SSD_KEY;
+
+ if (key == 0x8) {
+ /*
+ * This quirk code helps the drive read the
+ * first tape block, regardless of format. That
+ * is required for these drives to return proper
+ * MODE SENSE information.
+ */
+ if ((st->quirks & ST_Q_SNS_HLP) &&
+ !(sc_link->flags & SDEV_MEDIA_LOADED)) { /* still starting */
+ st->blksiz -= 512;
+ } else if (!(st->flags & (ST_2FM_AT_EOD | ST_BLANK_READ))) {
+ st->flags |= ST_BLANK_READ;
+ xs->resid = xs->datalen;
+ if (bp) {
+ bp->b_resid = xs->resid;
+ /*return an EOF */
+ }
+ return (ESUCCESS);
+ }
+ }
+ return (-1); /* let the default/generic handler handle it */
+}
+
+/*
+ * The quirk here is that the drive returns some value to st_mode_sense
+ * incorrectly until the tape has actually passed by the head.
+ *
+ * The method is to set the drive to large fixed-block state (user-specified
+ * density and 1024-byte blocks), then read and rewind to get it to sense the
+ * tape. If that doesn't work, try 512-byte fixed blocks. If that doesn't
+ * work, as a last resort, try variable- length blocks. The result will be
+ * the ability to do an accurate st_mode_sense.
+ *
+ * We know we can do a rewind because we just did a load, which implies rewind.
+ * Rewind seems preferable to space backward if we have a virgin tape.
+ *
+ * The rest of the code for this quirk is in ILI processing and BLANK CHECK
+ * error processing, both part of st_interpret_sense.
+ */
+errval
+st_touch_tape(unit)
+ u_int32 unit;
+{
+ struct st_data *st = st_data[unit];
+ char *buf;
+ u_int32 readsiz;
+ errval errno;
+
+ buf = malloc(1024, M_TEMP, M_NOWAIT);
+ if (!buf)
+ return (ENOMEM);
+
+ if (errno = st_mode_sense(unit, 0)) {
+ goto bad;
+ }
+ st->blksiz = 1024;
+ do {
+ switch ((int)st->blksiz) {
+ case 512:
+ case 1024:
+ readsiz = st->blksiz;
+ st->flags |= ST_FIXEDBLOCKS;
+ break;
+ default:
+ readsiz = 1;
+ st->flags &= ~ST_FIXEDBLOCKS;
+ } if (errno = st_mode_select(unit, 0)) {
+ goto bad;
+ }
+ st_read(unit, buf, readsiz, SCSI_SILENT);
+ if (errno = st_rewind(unit, FALSE, 0)) {
+bad: free(buf, M_TEMP);
+ return (errno);
+ }
+ } while (readsiz != 1 && readsiz > st->blksiz);
+ free(buf, M_TEMP);
+ return 0;
+}
diff --git a/sys/scsi/su.c b/sys/scsi/su.c
new file mode 100644
index 0000000..de4f017
--- /dev/null
+++ b/sys/scsi/su.c
@@ -0,0 +1,4 @@
+
+/* this will be a special user scsi device */
+/* not written yet */
+
diff --git a/sys/scsi/uk.c b/sys/scsi/uk.c
new file mode 100644
index 0000000..149e2df
--- /dev/null
+++ b/sys/scsi/uk.c
@@ -0,0 +1,158 @@
+/*
+ * Dummy driver for a device we can't identify.
+ * by Julian Elischer (julian@tfs.com)
+ *
+ * $Id: uk.c,v 1.2 1993/11/25 01:37:35 wollman Exp $
+ */
+
+#include <sys/param.h>
+#include "systm.h"
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#define NUK 16
+
+/*
+ * This driver is so simple it uses all the default services
+ */
+struct scsi_device uk_switch =
+{
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "uk",
+ 0,
+ 0, 0
+};
+
+struct uk_data {
+ u_int32 flags;
+ struct scsi_link *sc_link; /* all the inter level info */
+} uk_data[NUK];
+
+#define UK_KNOWN 0x02
+
+static u_int32 next_uk_unit = 0;
+
+/*
+ * The routine called by the low level scsi routine when it discovers
+ * a device suitable for this driver.
+ */
+errval
+ukattach(sc_link)
+ struct scsi_link *sc_link;
+{
+ u_int32 unit, i, stat;
+ unsigned char *tbl;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("ukattach: "));
+ /*
+ * Check we have the resources for another drive
+ */
+ unit = next_uk_unit++;
+ if (unit >= NUK) {
+ printf("Too many unknown devices..(%d > %d) reconfigure kernel\n",
+ (unit + 1), NUK);
+ return (0);
+ }
+ /*
+ * Store information needed to contact our base driver
+ */
+ uk_data[unit].sc_link = sc_link;
+ sc_link->device = &uk_switch;
+ sc_link->dev_unit = unit;
+
+ printf("uk%d: unknown device\n", unit);
+ uk_data[unit].flags = UK_KNOWN;
+
+ return 1; /* XXX ??? */
+
+}
+
+/*
+ * open the device.
+ */
+errval
+ukopen(dev)
+ dev_t dev;
+{
+ errval errcode = 0;
+ u_int32 unit, mode;
+ struct scsi_link *sc_link;
+ unit = minor(dev);
+
+ /*
+ * Check the unit is legal
+ */
+ if (unit >= NUK) {
+ printf("uk%d: uk %d > %d\n", unit, unit, NUK);
+ return ENXIO;
+ }
+
+ /*
+ * Make sure the device has been initialised
+ */
+ if((uk_data[unit].flags & UK_KNOWN) == 0) {
+ printf("uk%d: not set up\n", unit);
+ return ENXIO;
+ }
+
+ /*
+ * Only allow one at a time
+ */
+ sc_link = uk_data[unit].sc_link;
+ if (sc_link->flags & SDEV_OPEN) {
+ printf("uk%d: already open\n", unit);
+ return ENXIO;
+ }
+ sc_link->flags |= SDEV_OPEN;
+ SC_DEBUG(sc_link, SDEV_DB1, ("ukopen: dev=0x%x (unit %d (of %d))\n"
+ ,dev, unit, NUK));
+ /*
+ * Catch any unit attention errors.
+ */
+ return 0;
+}
+
+/*
+ * close the device.. only called if we are the LAST
+ * occurence of an open device
+ */
+errval
+ukclose(dev)
+ dev_t dev;
+{
+ unsigned char unit = 0, mode; /* XXX !!! XXX FIXME!!! 0??? */
+ struct scsi_link *sc_link;
+
+ sc_link = uk_data[unit].sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB1, ("Closing device"));
+ sc_link->flags &= ~SDEV_OPEN;
+ return (0);
+}
+
+/*
+ * Perform special action on behalf of the user
+ * Only does generic scsi ioctls.
+ */
+errval
+ukioctl(dev, cmd, arg, mode)
+ dev_t dev;
+ u_int32 cmd;
+ caddr_t arg;
+ int mode;
+{
+ unsigned char unit;
+ struct scsi_link *sc_link;
+
+ /*
+ * Find the device that the user is talking about
+ */
+ unit = minor(dev);
+ sc_link = uk_data[unit].sc_link;
+ return(scsi_do_ioctl(sc_link,cmd,arg,mode));
+}
+
diff --git a/sys/sys/_sigset.h b/sys/sys/_sigset.h
new file mode 100644
index 0000000..8ccded4
--- /dev/null
+++ b/sys/sys/_sigset.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)signal.h 8.2 (Berkeley) 1/21/94
+ */
+
+#ifndef _SYS_SIGNAL_H_
+#define _SYS_SIGNAL_H_
+
+#define NSIG 32 /* counting 0; could be 33 (mask is 1-32) */
+
+#ifndef _ANSI_SOURCE
+#include <machine/signal.h> /* sigcontext; codes for SIGILL, SIGFPE */
+#endif
+
+#define SIGHUP 1 /* hangup */
+#define SIGINT 2 /* interrupt */
+#define SIGQUIT 3 /* quit */
+#define SIGILL 4 /* illegal instruction (not reset when caught) */
+#ifndef _POSIX_SOURCE
+#define SIGTRAP 5 /* trace trap (not reset when caught) */
+#endif
+#define SIGABRT 6 /* abort() */
+#ifndef _POSIX_SOURCE
+#define SIGIOT SIGABRT /* compatibility */
+#define SIGEMT 7 /* EMT instruction */
+#endif
+#define SIGFPE 8 /* floating point exception */
+#define SIGKILL 9 /* kill (cannot be caught or ignored) */
+#ifndef _POSIX_SOURCE
+#define SIGBUS 10 /* bus error */
+#endif
+#define SIGSEGV 11 /* segmentation violation */
+#ifndef _POSIX_SOURCE
+#define SIGSYS 12 /* bad argument to system call */
+#endif
+#define SIGPIPE 13 /* write on a pipe with no one to read it */
+#define SIGALRM 14 /* alarm clock */
+#define SIGTERM 15 /* software termination signal from kill */
+#ifndef _POSIX_SOURCE
+#define SIGURG 16 /* urgent condition on IO channel */
+#endif
+#define SIGSTOP 17 /* sendable stop signal not from tty */
+#define SIGTSTP 18 /* stop signal from tty */
+#define SIGCONT 19 /* continue a stopped process */
+#define SIGCHLD 20 /* to parent on child stop or exit */
+#define SIGTTIN 21 /* to readers pgrp upon background tty read */
+#define SIGTTOU 22 /* like TTIN for output if (tp->t_local&LTOSTOP) */
+#ifndef _POSIX_SOURCE
+#define SIGIO 23 /* input/output possible signal */
+#define SIGXCPU 24 /* exceeded CPU time limit */
+#define SIGXFSZ 25 /* exceeded file size limit */
+#define SIGVTALRM 26 /* virtual time alarm */
+#define SIGPROF 27 /* profiling time alarm */
+#define SIGWINCH 28 /* window size changes */
+#define SIGINFO 29 /* information request */
+#endif
+#define SIGUSR1 30 /* user defined signal 1 */
+#define SIGUSR2 31 /* user defined signal 2 */
+
+#if defined(_ANSI_SOURCE) || defined(__cplusplus)
+/*
+ * Language spec sez we must list exactly one parameter, even though we
+ * actually supply three. Ugh!
+ */
+#define SIG_DFL (void (*)(int))0
+#define SIG_IGN (void (*)(int))1
+#define SIG_ERR (void (*)(int))-1
+#else
+#define SIG_DFL (void (*)())0
+#define SIG_IGN (void (*)())1
+#define SIG_ERR (void (*)())-1
+#endif
+
+#ifndef _ANSI_SOURCE
+typedef unsigned int sigset_t;
+
+/*
+ * Signal vector "template" used in sigaction call.
+ */
+struct sigaction {
+ void (*sa_handler)(); /* signal handler */
+ sigset_t sa_mask; /* signal mask to apply */
+ int sa_flags; /* see signal options below */
+};
+#ifndef _POSIX_SOURCE
+#define SA_ONSTACK 0x0001 /* take signal on signal stack */
+#define SA_RESTART 0x0002 /* restart system on signal return */
+#define SA_DISABLE 0x0004 /* disable taking signals on alternate stack */
+#ifdef COMPAT_SUNOS
+#define SA_USERTRAMP 0x0100 /* do not bounce off kernel's sigtramp */
+#endif
+#endif
+#define SA_NOCLDSTOP 0x0008 /* do not generate SIGCHLD on child stop */
+
+/*
+ * Flags for sigprocmask:
+ */
+#define SIG_BLOCK 1 /* block specified signal set */
+#define SIG_UNBLOCK 2 /* unblock specified signal set */
+#define SIG_SETMASK 3 /* set specified signal set */
+
+#ifndef _POSIX_SOURCE
+#ifndef KERNEL
+#include <sys/cdefs.h>
+#endif
+typedef void (*sig_t) __P((int)); /* type of signal function */
+
+/*
+ * Structure used in sigaltstack call.
+ */
+struct sigaltstack {
+ char *ss_base; /* signal stack base */
+ int ss_size; /* signal stack length */
+ int ss_flags; /* SA_DISABLE and/or SA_ONSTACK */
+};
+#define MINSIGSTKSZ 8192 /* minimum allowable stack */
+#define SIGSTKSZ (MINSIGSTKSZ + 32768) /* recommended stack size */
+
+/*
+ * 4.3 compatibility:
+ * Signal vector "template" used in sigvec call.
+ */
+struct sigvec {
+ void (*sv_handler)(); /* signal handler */
+ int sv_mask; /* signal mask to apply */
+ int sv_flags; /* see signal options below */
+};
+
+#define SV_ONSTACK SA_ONSTACK
+#define SV_INTERRUPT SA_RESTART /* same bit, opposite sense */
+#define sv_onstack sv_flags /* isn't compatibility wonderful! */
+
+/*
+ * Structure used in sigstack call.
+ */
+struct sigstack {
+ char *ss_sp; /* signal stack pointer */
+ int ss_onstack; /* current status */
+};
+
+/*
+ * Macro for converting signal number to a mask suitable for
+ * sigblock().
+ */
+#define sigmask(m) (1 << ((m)-1))
+
+#define BADSIG SIG_ERR
+
+#endif /* !_POSIX_SOURCE */
+#endif /* !_ANSI_SOURCE */
+
+/*
+ * For historical reasons; programs expect signal's return value to be
+ * defined by <sys/signal.h>.
+ */
+__BEGIN_DECLS
+void (*signal __P((int, void (*) __P((int))))) __P((int));
+__END_DECLS
+#endif /* !_SYS_SIGNAL_H_ */
diff --git a/sys/sys/acct.h b/sys/sys/acct.h
new file mode 100644
index 0000000..edc5bdb
--- /dev/null
+++ b/sys/sys/acct.h
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)acct.h 8.2 (Berkeley) 1/21/94
+ */
+
+/*
+ * Accounting structures; these use a comp_t type which is a 3 bits base 8
+ * exponent, 13 bit fraction ``floating point'' number. Units are 1/AHZ
+ * seconds.
+ */
+typedef u_short comp_t;
+
+struct acct {
+ char ac_comm[10]; /* command name */
+ comp_t ac_utime; /* user time */
+ comp_t ac_stime; /* system time */
+ comp_t ac_etime; /* elapsed time */
+ time_t ac_btime; /* starting time */
+ uid_t ac_uid; /* user id */
+ gid_t ac_gid; /* group id */
+ short ac_mem; /* average memory usage */
+ comp_t ac_io; /* count of IO blocks */
+ dev_t ac_tty; /* controlling tty */
+#define AFORK 0x01 /* forked but not execed */
+#define ASU 0x02 /* used super-user permissions */
+#define ACOMPAT 0x04 /* used compatibility mode */
+#define ACORE 0x08 /* dumped core */
+#define AXSIG 0x10 /* killed by a signal */
+ char ac_flag; /* accounting flags */
+};
+
+/*
+ * 1/AHZ is the granularity of the data encoded in the comp_t fields.
+ * This is not necessarily equal to hz.
+ */
+#define AHZ 64
+
+#ifdef KERNEL
+struct vnode *acctp;
+#endif
diff --git a/sys/sys/bio.h b/sys/sys/bio.h
new file mode 100644
index 0000000..96517b6
--- /dev/null
+++ b/sys/sys/bio.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)buf.h 8.7 (Berkeley) 1/21/94
+ */
+
+#ifndef _SYS_BUF_H_
+#define _SYS_BUF_H_
+#include <sys/queue.h>
+
+#define NOLIST ((struct buf *)0x87654321)
+
+/*
+ * The buffer header describes an I/O operation in the kernel.
+ */
+struct buf {
+ LIST_ENTRY(buf) b_hash; /* Hash chain. */
+ LIST_ENTRY(buf) b_vnbufs; /* Buffer's associated vnode. */
+ TAILQ_ENTRY(buf) b_freelist; /* Free list position if not active. */
+ struct buf *b_actf, **b_actb; /* Device driver queue when active. */
+ struct proc *b_proc; /* Associated proc; NULL if kernel. */
+ volatile long b_flags; /* B_* flags. */
+ int b_qindex; /* buffer queue index */
+ int b_error; /* Errno value. */
+ long b_bufsize; /* Allocated buffer size. */
+ long b_bcount; /* Valid bytes in buffer. */
+ long b_resid; /* Remaining I/O. */
+ dev_t b_dev; /* Device associated with buffer. */
+ struct {
+ caddr_t b_addr; /* Memory, superblocks, indirect etc. */
+ } b_un;
+ void *b_saveaddr; /* Original b_addr for physio. */
+ daddr_t b_lblkno; /* Logical block number. */
+ daddr_t b_blkno; /* Underlying physical block number. */
+ /* Function to call upon completion. */
+ void (*b_iodone) __P((struct buf *));
+ struct vnode *b_vp; /* Device vnode. */
+ int b_pfcent; /* Center page when swapping cluster. */
+ int b_dirtyoff; /* Offset in buffer of dirty region. */
+ int b_dirtyend; /* Offset of end of dirty region. */
+ struct ucred *b_rcred; /* Read credentials reference. */
+ struct ucred *b_wcred; /* Write credentials reference. */
+ int b_validoff; /* Offset in buffer of valid region. */
+ int b_validend; /* Offset of end of valid region. */
+ daddr_t b_pblkno; /* physical block number */
+ caddr_t b_savekva; /* saved kva for transfer while bouncing */
+ TAILQ_HEAD(b_clusterhd,buf) b_cluster; /* low level clustering */
+ void *b_driver1; /* for private use by the driver */
+ void *b_driver2; /* for private use by the driver */
+ void *b_spc;
+
+};
+
+/* Device driver compatibility definitions. */
+#define b_active b_bcount /* Driver queue head: drive active. */
+#define b_data b_un.b_addr /* b_un.b_addr is not changeable. */
+#define b_errcnt b_resid /* Retry count while I/O in progress. */
+#define iodone biodone /* Old name for biodone. */
+#define iowait biowait /* Old name for biowait. */
+
+/*
+ * These flags are kept in b_flags.
+ */
+#define B_AGE 0x00000001 /* Move to age queue when I/O done. */
+#define B_APPENDWRITE 0x00000002 /* Append-write in progress. */
+#define B_ASYNC 0x00000004 /* Start I/O, do not wait. */
+#define B_BAD 0x00000008 /* Bad block revectoring in progress. */
+#define B_BUSY 0x00000010 /* I/O in progress. */
+#define B_CACHE 0x00000020 /* Bread found us in the cache. */
+#define B_CALL 0x00000040 /* Call b_iodone from biodone. */
+#define B_DELWRI 0x00000080 /* Delay I/O until buffer reused. */
+#define B_DIRTY 0x00000100 /* Dirty page to be pushed out async. */
+#define B_DONE 0x00000200 /* I/O completed. */
+#define B_EINTR 0x00000400 /* I/O was interrupted */
+#define B_ERROR 0x00000800 /* I/O error occurred. */
+#define B_GATHERED 0x00001000 /* LFS: already in a segment. */
+#define B_INVAL 0x00002000 /* Does not contain valid info. */
+#define B_LOCKED 0x00004000 /* Locked in core (not reusable). */
+#define B_NOCACHE 0x00008000 /* Do not cache block after use. */
+#define B_PAGET 0x00010000 /* Page in/out of page table space. */
+#define B_PGIN 0x00020000 /* Pagein op, so swap() can count it. */
+#define B_PHYS 0x00040000 /* I/O to user memory. */
+#define B_RAW 0x00080000 /* Set by physio for raw transfers. */
+#define B_READ 0x00100000 /* Read buffer. */
+#define B_TAPE 0x00200000 /* Magnetic tape I/O. */
+#define B_UAREA 0x00400000 /* Buffer describes Uarea I/O. */
+#define B_WANTED 0x00800000 /* Process wants this buffer. */
+#define B_WRITE 0x00000000 /* Write buffer (pseudo flag). */
+#define B_WRITEINPROG 0x01000000 /* Write in progress. */
+#define B_XXX 0x02000000 /* Debugging flag. */
+#define B_CLUSTER 0x40000000 /* pagein op, so swap() can count it */
+#define B_BOUNCE 0x80000000 /* bounce buffer flag */
+
+/*
+ * This structure describes a clustered I/O. It is stored in the b_saveaddr
+ * field of the buffer on which I/O is done. At I/O completion, cluster
+ * callback uses the structure to parcel I/O's to individual buffers, and
+ * then free's this structure.
+ */
+struct cluster_save {
+ long bs_bcount; /* Saved b_bcount. */
+ long bs_bufsize; /* Saved b_bufsize. */
+ void *bs_saveaddr; /* Saved b_addr. */
+ int bs_nchildren; /* Number of associated buffers. */
+ struct buf **bs_children; /* List of associated buffers. */
+};
+
+/*
+ * number of buffer hash entries
+ */
+#define BUFHSZ 512
+
+/*
+ * buffer hash table calculation, originally by David Greenman
+ */
+#define BUFHASH(vnp, bn) \
+ (&bufhashtbl[(((int)(vnp) / sizeof(struct vnode))+(int)(bn)) % BUFHSZ])
+
+/*
+ * Definitions for the buffer free lists.
+ */
+#define BUFFER_QUEUES 5 /* number of free buffer queues */
+
+LIST_HEAD(bufhashhdr, buf) bufhashtbl[BUFHSZ], invalhash;
+TAILQ_HEAD(bqueues, buf) bufqueues[BUFFER_QUEUES];
+
+#define QUEUE_NONE 0 /* on no queue */
+#define QUEUE_LOCKED 1 /* locked buffers */
+#define QUEUE_LRU 2 /* useful buffers */
+#define QUEUE_AGE 3 /* less useful buffers */
+#define QUEUE_EMPTY 4 /* empty buffer headers*/
+
+/*
+ * Zero out the buffer's data area.
+ */
+#define clrbuf(bp) { \
+ blkclr((bp)->b_data, (u_int)(bp)->b_bcount); \
+ (bp)->b_resid = 0; \
+}
+
+/* Flags to low-level allocation routines. */
+#define B_CLRBUF 0x01 /* Request allocated buffer be cleared. */
+#define B_SYNC 0x02 /* Do all allocations synchronously. */
+
+#ifdef KERNEL
+int nbuf; /* The number of buffer headers */
+struct buf *buf; /* The buffer headers. */
+char *buffers; /* The buffer contents. */
+int bufpages; /* Number of memory pages in the buffer pool. */
+struct buf *swbuf; /* Swap I/O buffer headers. */
+int nswbuf; /* Number of swap I/O buffer headers. */
+TAILQ_HEAD(swqueue, buf) bswlist;
+struct buf *bclnlist; /* Head of cleaned page list. */
+
+__BEGIN_DECLS
+void allocbuf __P((struct buf *, int));
+void bawrite __P((struct buf *));
+void bdwrite __P((struct buf *));
+void biodone __P((struct buf *));
+int biowait __P((struct buf *));
+int bread __P((struct vnode *, daddr_t, int,
+ struct ucred *, struct buf **));
+int breadn __P((struct vnode *, daddr_t, int, daddr_t *, int *, int,
+ struct ucred *, struct buf **));
+void brelse __P((struct buf *));
+void bufinit __P((void));
+int bwrite __P((struct buf *));
+void cluster_callback __P((struct buf *));
+int cluster_read __P((struct vnode *, u_quad_t, daddr_t, long,
+ struct ucred *, struct buf **));
+void cluster_write __P((struct buf *, u_quad_t));
+struct buf *getblk __P((struct vnode *, daddr_t, int, int, int));
+struct buf *geteblk __P((int));
+struct buf *getnewbuf __P((int slpflag, int slptimeo));
+struct buf *incore __P((struct vnode *, daddr_t));
+u_int minphys __P((struct buf *bp));
+__END_DECLS
+#endif
+#endif /* !_SYS_BUF_H_ */
diff --git a/sys/sys/bitstring.h b/sys/sys/bitstring.h
new file mode 100644
index 0000000..88437e7
--- /dev/null
+++ b/sys/sys/bitstring.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Paul Vixie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)bitstring.h 8.1 (Berkeley) 7/19/93
+ */
+
+#ifndef _BITSTRING_H_
+#define _BITSTRING_H_
+
+typedef unsigned char bitstr_t;
+
+/* internal macros */
+ /* byte of the bitstring bit is in */
+#define _bit_byte(bit) \
+ ((bit) >> 3)
+
+ /* mask for the bit within its byte */
+#define _bit_mask(bit) \
+ (1 << ((bit)&0x7))
+
+/* external macros */
+ /* bytes in a bitstring of nbits bits */
+#define bitstr_size(nbits) \
+ ((((nbits) - 1) >> 3) + 1)
+
+ /* allocate a bitstring */
+#define bit_alloc(nbits) \
+ (bitstr_t *)calloc(1, \
+ (unsigned int)bitstr_size(nbits) * sizeof(bitstr_t))
+
+ /* allocate a bitstring on the stack */
+#define bit_decl(name, nbits) \
+ (name)[bitstr_size(nbits)]
+
+ /* is bit N of bitstring name set? */
+#define bit_test(name, bit) \
+ ((name)[_bit_byte(bit)] & _bit_mask(bit))
+
+ /* set bit N of bitstring name */
+#define bit_set(name, bit) \
+ (name)[_bit_byte(bit)] |= _bit_mask(bit)
+
+ /* clear bit N of bitstring name */
+#define bit_clear(name, bit) \
+ (name)[_bit_byte(bit)] &= ~_bit_mask(bit)
+
+ /* clear bits start ... stop in bitstring */
+#define bit_nclear(name, start, stop) { \
+ register bitstr_t *_name = name; \
+ register int _start = start, _stop = stop; \
+ register int _startbyte = _bit_byte(_start); \
+ register int _stopbyte = _bit_byte(_stop); \
+ if (_startbyte == _stopbyte) { \
+ _name[_startbyte] &= ((0xff >> (8 - (_start&0x7))) | \
+ (0xff << ((_stop&0x7) + 1))); \
+ } else { \
+ _name[_startbyte] &= 0xff >> (8 - (_start&0x7)); \
+ while (++_startbyte < _stopbyte) \
+ _name[_startbyte] = 0; \
+ _name[_stopbyte] &= 0xff << ((_stop&0x7) + 1); \
+ } \
+}
+
+ /* set bits start ... stop in bitstring */
+#define bit_nset(name, start, stop) { \
+ register bitstr_t *_name = name; \
+ register int _start = start, _stop = stop; \
+ register int _startbyte = _bit_byte(_start); \
+ register int _stopbyte = _bit_byte(_stop); \
+ if (_startbyte == _stopbyte) { \
+ _name[_startbyte] |= ((0xff << (_start&0x7)) & \
+ (0xff >> (7 - (_stop&0x7)))); \
+ } else { \
+ _name[_startbyte] |= 0xff << ((_start)&0x7); \
+ while (++_startbyte < _stopbyte) \
+ _name[_startbyte] = 0xff; \
+ _name[_stopbyte] |= 0xff >> (7 - (_stop&0x7)); \
+ } \
+}
+
+ /* find first bit clear in name */
+#define bit_ffc(name, nbits, value) { \
+ register bitstr_t *_name = name; \
+ register int _byte, _nbits = nbits; \
+ register int _stopbyte = _bit_byte(_nbits), _value = -1; \
+ for (_byte = 0; _byte <= _stopbyte; ++_byte) \
+ if (_name[_byte] != 0xff) { \
+ _value = _byte << 3; \
+ for (_stopbyte = _name[_byte]; (_stopbyte&0x1); \
+ ++_value, _stopbyte >>= 1); \
+ break; \
+ } \
+ *(value) = _value; \
+}
+
+ /* find first bit set in name */
+#define bit_ffs(name, nbits, value) { \
+ register bitstr_t *_name = name; \
+ register int _byte, _nbits = nbits; \
+ register int _stopbyte = _bit_byte(_nbits), _value = -1; \
+ for (_byte = 0; _byte <= _stopbyte; ++_byte) \
+ if (_name[_byte]) { \
+ _value = _byte << 3; \
+ for (_stopbyte = _name[_byte]; !(_stopbyte&0x1); \
+ ++_value, _stopbyte >>= 1); \
+ break; \
+ } \
+ *(value) = _value; \
+}
+
+#endif /* !_BITSTRING_H_ */
diff --git a/sys/sys/buf.h b/sys/sys/buf.h
new file mode 100644
index 0000000..96517b6
--- /dev/null
+++ b/sys/sys/buf.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)buf.h 8.7 (Berkeley) 1/21/94
+ */
+
+#ifndef _SYS_BUF_H_
+#define _SYS_BUF_H_
+#include <sys/queue.h>
+
+#define NOLIST ((struct buf *)0x87654321)
+
+/*
+ * The buffer header describes an I/O operation in the kernel.
+ */
+struct buf {
+ LIST_ENTRY(buf) b_hash; /* Hash chain. */
+ LIST_ENTRY(buf) b_vnbufs; /* Buffer's associated vnode. */
+ TAILQ_ENTRY(buf) b_freelist; /* Free list position if not active. */
+ struct buf *b_actf, **b_actb; /* Device driver queue when active. */
+ struct proc *b_proc; /* Associated proc; NULL if kernel. */
+ volatile long b_flags; /* B_* flags. */
+ int b_qindex; /* buffer queue index */
+ int b_error; /* Errno value. */
+ long b_bufsize; /* Allocated buffer size. */
+ long b_bcount; /* Valid bytes in buffer. */
+ long b_resid; /* Remaining I/O. */
+ dev_t b_dev; /* Device associated with buffer. */
+ struct {
+ caddr_t b_addr; /* Memory, superblocks, indirect etc. */
+ } b_un;
+ void *b_saveaddr; /* Original b_addr for physio. */
+ daddr_t b_lblkno; /* Logical block number. */
+ daddr_t b_blkno; /* Underlying physical block number. */
+ /* Function to call upon completion. */
+ void (*b_iodone) __P((struct buf *));
+ struct vnode *b_vp; /* Device vnode. */
+ int b_pfcent; /* Center page when swapping cluster. */
+ int b_dirtyoff; /* Offset in buffer of dirty region. */
+ int b_dirtyend; /* Offset of end of dirty region. */
+ struct ucred *b_rcred; /* Read credentials reference. */
+ struct ucred *b_wcred; /* Write credentials reference. */
+ int b_validoff; /* Offset in buffer of valid region. */
+ int b_validend; /* Offset of end of valid region. */
+ daddr_t b_pblkno; /* physical block number */
+ caddr_t b_savekva; /* saved kva for transfer while bouncing */
+ TAILQ_HEAD(b_clusterhd,buf) b_cluster; /* low level clustering */
+ void *b_driver1; /* for private use by the driver */
+ void *b_driver2; /* for private use by the driver */
+ void *b_spc;
+
+};
+
+/* Device driver compatibility definitions. */
+#define b_active b_bcount /* Driver queue head: drive active. */
+#define b_data b_un.b_addr /* b_un.b_addr is not changeable. */
+#define b_errcnt b_resid /* Retry count while I/O in progress. */
+#define iodone biodone /* Old name for biodone. */
+#define iowait biowait /* Old name for biowait. */
+
+/*
+ * These flags are kept in b_flags.
+ */
+#define B_AGE 0x00000001 /* Move to age queue when I/O done. */
+#define B_APPENDWRITE 0x00000002 /* Append-write in progress. */
+#define B_ASYNC 0x00000004 /* Start I/O, do not wait. */
+#define B_BAD 0x00000008 /* Bad block revectoring in progress. */
+#define B_BUSY 0x00000010 /* I/O in progress. */
+#define B_CACHE 0x00000020 /* Bread found us in the cache. */
+#define B_CALL 0x00000040 /* Call b_iodone from biodone. */
+#define B_DELWRI 0x00000080 /* Delay I/O until buffer reused. */
+#define B_DIRTY 0x00000100 /* Dirty page to be pushed out async. */
+#define B_DONE 0x00000200 /* I/O completed. */
+#define B_EINTR 0x00000400 /* I/O was interrupted */
+#define B_ERROR 0x00000800 /* I/O error occurred. */
+#define B_GATHERED 0x00001000 /* LFS: already in a segment. */
+#define B_INVAL 0x00002000 /* Does not contain valid info. */
+#define B_LOCKED 0x00004000 /* Locked in core (not reusable). */
+#define B_NOCACHE 0x00008000 /* Do not cache block after use. */
+#define B_PAGET 0x00010000 /* Page in/out of page table space. */
+#define B_PGIN 0x00020000 /* Pagein op, so swap() can count it. */
+#define B_PHYS 0x00040000 /* I/O to user memory. */
+#define B_RAW 0x00080000 /* Set by physio for raw transfers. */
+#define B_READ 0x00100000 /* Read buffer. */
+#define B_TAPE 0x00200000 /* Magnetic tape I/O. */
+#define B_UAREA 0x00400000 /* Buffer describes Uarea I/O. */
+#define B_WANTED 0x00800000 /* Process wants this buffer. */
+#define B_WRITE 0x00000000 /* Write buffer (pseudo flag). */
+#define B_WRITEINPROG 0x01000000 /* Write in progress. */
+#define B_XXX 0x02000000 /* Debugging flag. */
+#define B_CLUSTER 0x40000000 /* pagein op, so swap() can count it */
+#define B_BOUNCE 0x80000000 /* bounce buffer flag */
+
+/*
+ * This structure describes a clustered I/O. It is stored in the b_saveaddr
+ * field of the buffer on which I/O is done. At I/O completion, cluster
+ * callback uses the structure to parcel I/O's to individual buffers, and
+ * then free's this structure.
+ */
+struct cluster_save {
+ long bs_bcount; /* Saved b_bcount. */
+ long bs_bufsize; /* Saved b_bufsize. */
+ void *bs_saveaddr; /* Saved b_addr. */
+ int bs_nchildren; /* Number of associated buffers. */
+ struct buf **bs_children; /* List of associated buffers. */
+};
+
+/*
+ * number of buffer hash entries
+ */
+#define BUFHSZ 512
+
+/*
+ * buffer hash table calculation, originally by David Greenman
+ */
+#define BUFHASH(vnp, bn) \
+ (&bufhashtbl[(((int)(vnp) / sizeof(struct vnode))+(int)(bn)) % BUFHSZ])
+
+/*
+ * Definitions for the buffer free lists.
+ */
+#define BUFFER_QUEUES 5 /* number of free buffer queues */
+
+LIST_HEAD(bufhashhdr, buf) bufhashtbl[BUFHSZ], invalhash;
+TAILQ_HEAD(bqueues, buf) bufqueues[BUFFER_QUEUES];
+
+#define QUEUE_NONE 0 /* on no queue */
+#define QUEUE_LOCKED 1 /* locked buffers */
+#define QUEUE_LRU 2 /* useful buffers */
+#define QUEUE_AGE 3 /* less useful buffers */
+#define QUEUE_EMPTY 4 /* empty buffer headers*/
+
+/*
+ * Zero out the buffer's data area.
+ */
+#define clrbuf(bp) { \
+ blkclr((bp)->b_data, (u_int)(bp)->b_bcount); \
+ (bp)->b_resid = 0; \
+}
+
+/* Flags to low-level allocation routines. */
+#define B_CLRBUF 0x01 /* Request allocated buffer be cleared. */
+#define B_SYNC 0x02 /* Do all allocations synchronously. */
+
+#ifdef KERNEL
+int nbuf; /* The number of buffer headers */
+struct buf *buf; /* The buffer headers. */
+char *buffers; /* The buffer contents. */
+int bufpages; /* Number of memory pages in the buffer pool. */
+struct buf *swbuf; /* Swap I/O buffer headers. */
+int nswbuf; /* Number of swap I/O buffer headers. */
+TAILQ_HEAD(swqueue, buf) bswlist;
+struct buf *bclnlist; /* Head of cleaned page list. */
+
+__BEGIN_DECLS
+void allocbuf __P((struct buf *, int));
+void bawrite __P((struct buf *));
+void bdwrite __P((struct buf *));
+void biodone __P((struct buf *));
+int biowait __P((struct buf *));
+int bread __P((struct vnode *, daddr_t, int,
+ struct ucred *, struct buf **));
+int breadn __P((struct vnode *, daddr_t, int, daddr_t *, int *, int,
+ struct ucred *, struct buf **));
+void brelse __P((struct buf *));
+void bufinit __P((void));
+int bwrite __P((struct buf *));
+void cluster_callback __P((struct buf *));
+int cluster_read __P((struct vnode *, u_quad_t, daddr_t, long,
+ struct ucred *, struct buf **));
+void cluster_write __P((struct buf *, u_quad_t));
+struct buf *getblk __P((struct vnode *, daddr_t, int, int, int));
+struct buf *geteblk __P((int));
+struct buf *getnewbuf __P((int slpflag, int slptimeo));
+struct buf *incore __P((struct vnode *, daddr_t));
+u_int minphys __P((struct buf *bp));
+__END_DECLS
+#endif
+#endif /* !_SYS_BUF_H_ */
diff --git a/sys/sys/callout.h b/sys/sys/callout.h
new file mode 100644
index 0000000..d685e56
--- /dev/null
+++ b/sys/sys/callout.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)callout.h 8.2 (Berkeley) 1/21/94
+ */
+
+struct callout {
+ struct callout *c_next; /* next callout in queue */
+ void *c_arg; /* function argument */
+ void (*c_func) __P((void *)); /* function to call */
+ int c_time; /* ticks to the event */
+};
+
+#ifdef KERNEL
+struct callout *callfree, *callout, calltodo;
+int ncallout;
+#endif
diff --git a/sys/sys/cdefs.h b/sys/sys/cdefs.h
new file mode 100644
index 0000000..3c50711
--- /dev/null
+++ b/sys/sys/cdefs.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Berkeley Software Design, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cdefs.h 8.7 (Berkeley) 1/21/94
+ */
+
+#ifndef _CDEFS_H_
+#define _CDEFS_H_
+
+#if defined(__cplusplus)
+#define __BEGIN_DECLS extern "C" {
+#define __END_DECLS };
+#else
+#define __BEGIN_DECLS
+#define __END_DECLS
+#endif
+
+/*
+ * The __CONCAT macro is used to concatenate parts of symbol names, e.g.
+ * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo.
+ * The __CONCAT macro is a bit tricky -- make sure you don't put spaces
+ * in between its arguments. __CONCAT can also concatenate double-quoted
+ * strings produced by the __STRING macro, but this only works with ANSI C.
+ */
+#if defined(__STDC__) || defined(__cplusplus)
+#define __P(protos) protos /* full-blown ANSI C */
+#define __CONCAT(x,y) x ## y
+#define __STRING(x) #x
+
+#define __const const /* define reserved names to standard */
+#define __signed signed
+#define __volatile volatile
+#if defined(__cplusplus)
+#define __inline inline /* convert to C++ keyword */
+#else
+#ifndef __GNUC__
+#define __inline /* delete GCC keyword */
+#endif /* !__GNUC__ */
+#endif /* !__cplusplus */
+
+#else /* !(__STDC__ || __cplusplus) */
+#define __P(protos) () /* traditional C preprocessor */
+#define __CONCAT(x,y) x/**/y
+#define __STRING(x) "x"
+
+#ifndef __GNUC__
+#define __const /* delete pseudo-ANSI C keywords */
+#define __inline
+#define __signed
+#define __volatile
+/*
+ * In non-ANSI C environments, new programs will want ANSI-only C keywords
+ * deleted from the program and old programs will want them left alone.
+ * When using a compiler other than gcc, programs using the ANSI C keywords
+ * const, inline etc. as normal identifiers should define -DNO_ANSI_KEYWORDS.
+ * When using "gcc -traditional", we assume that this is the intent; if
+ * __GNUC__ is defined but __STDC__ is not, we leave the new keywords alone.
+ */
+#ifndef NO_ANSI_KEYWORDS
+#define const /* delete ANSI C keywords */
+#define inline
+#define signed
+#define volatile
+#endif
+#endif /* !__GNUC__ */
+#endif /* !(__STDC__ || __cplusplus) */
+
+/*
+ * GCC1 and some versions of GCC2 declare dead (non-returning) and
+ * pure (no side effects) functions using "volatile" and "const";
+ * unfortunately, these then cause warnings under "-ansi -pedantic".
+ * GCC2 uses a new, peculiar __attribute__((attrs)) style. All of
+ * these work for GNU C++ (modulo a slight glitch in the C++ grammar
+ * in the distribution version of 2.5.5).
+ */
+#if !defined(__GNUC__) || __GNUC__ < 2
+#define __attribute__(x) /* delete __attribute__ if non-gcc or gcc1 */
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+#define __dead __volatile
+#define __pure __const
+#endif
+#endif
+
+/* Delete pseudo-keywords wherever they are not available or needed. */
+#ifndef __dead
+#define __dead
+#define __pure
+#endif
+
+#endif /* !_CDEFS_H_ */
diff --git a/sys/sys/cdio.h b/sys/sys/cdio.h
new file mode 100644
index 0000000..c49dfed
--- /dev/null
+++ b/sys/sys/cdio.h
@@ -0,0 +1,170 @@
+/*
+ * 16 Feb 93 Julian Elischer (julian@dialix.oz.au)
+ *
+ * $Id: cdio.h,v 1.5 1994/01/29 10:31:20 rgrimes Exp $
+ */
+/* Shared between kernel & process */
+#ifndef _SYS_CDIO_H_
+#define _SYS_CDIO_H_
+
+union msf_lba {
+ struct {
+ unsigned char unused;
+ unsigned char minute;
+ unsigned char second;
+ unsigned char frame;
+ } msf;
+ int lba;
+ u_char addr[4];
+};
+
+struct cd_toc_entry {
+ u_char :8;
+ u_char control:4;
+ u_char addr_type:4;
+ u_char track;
+ u_char :8;
+ union msf_lba addr;
+};
+
+struct cd_sub_channel_header {
+ u_char :8;
+ u_char audio_status;
+#define CD_AS_AUDIO_INVALID 0x00
+#define CD_AS_PLAY_IN_PROGRESS 0x11
+#define CD_AS_PLAY_PAUSED 0x12
+#define CD_AS_PLAY_COMPLETED 0x13
+#define CD_AS_PLAY_ERROR 0x14
+#define CD_AS_NO_STATUS 0x15
+ u_char data_len[2];
+};
+
+struct cd_sub_channel_position_data {
+ u_char data_format;
+ u_char control:4;
+ u_char addr_type:4;
+ u_char track_number;
+ u_char index_number;
+ union msf_lba absaddr;
+ union msf_lba reladdr;
+};
+
+struct cd_sub_channel_media_catalog {
+ u_char data_format;
+ u_char :8;
+ u_char :8;
+ u_char :8;
+ u_char :7;
+ u_char mc_valid:1;
+ u_char mc_number[15];
+};
+
+struct cd_sub_channel_track_info {
+ u_char data_format;
+ u_char :8;
+ u_char track_number;
+ u_char :8;
+ u_char :7;
+ u_char ti_valid:1;
+ u_char ti_number[15];
+};
+
+struct cd_sub_channel_info {
+ struct cd_sub_channel_header header;
+ union {
+ struct cd_sub_channel_position_data position;
+ struct cd_sub_channel_media_catalog media_catalog;
+ struct cd_sub_channel_track_info track_info;
+ } what;
+};
+
+/***************************************************************\
+* Ioctls for the CD drive *
+\***************************************************************/
+struct ioc_play_track
+{
+ u_char start_track;
+ u_char start_index;
+ u_char end_track;
+ u_char end_index;
+};
+
+#define CDIOCPLAYTRACKS _IOW('c',1,struct ioc_play_track)
+struct ioc_play_blocks
+{
+ int blk;
+ int len;
+};
+#define CDIOCPLAYBLOCKS _IOW('c',2,struct ioc_play_blocks)
+
+struct ioc_read_subchannel {
+ u_char address_format;
+#define CD_LBA_FORMAT 1
+#define CD_MSF_FORMAT 2
+ u_char data_format;
+#define CD_SUBQ_DATA 0
+#define CD_CURRENT_POSITION 1
+#define CD_MEDIA_CATALOG 2
+#define CD_TRACK_INFO 3
+ u_char track;
+ int data_len;
+ struct cd_sub_channel_info *data;
+};
+#define CDIOCREADSUBCHANNEL _IOWR('c', 3 , struct ioc_read_subchannel )
+
+
+struct ioc_toc_header {
+ u_short len;
+ u_char starting_track;
+ u_char ending_track;
+};
+
+#define CDIOREADTOCHEADER _IOR('c',4,struct ioc_toc_header)
+
+struct ioc_read_toc_entry {
+ u_char address_format;
+ u_char starting_track;
+ u_short data_len;
+ struct cd_toc_entry *data;
+};
+#define CDIOREADTOCENTRYS _IOWR('c',5,struct ioc_read_toc_entry)
+
+struct ioc_patch
+{
+ u_char patch[4]; /* one for each channel */
+};
+#define CDIOCSETPATCH _IOW('c',9,struct ioc_patch)
+struct ioc_vol
+{
+ u_char vol[4]; /* one for each channel */
+};
+#define CDIOCGETVOL _IOR('c',10,struct ioc_vol)
+#define CDIOCSETVOL _IOW('c',11,struct ioc_vol)
+#define CDIOCSETMONO _IO('c',12)
+#define CDIOCSETSTERIO _IO('c',13)
+#define CDIOCSETMUTE _IO('c',14)
+#define CDIOCSETLEFT _IO('c',15)
+#define CDIOCSETRIGHT _IO('c',16)
+#define CDIOCSETDEBUG _IO('c',17)
+#define CDIOCCLRDEBUG _IO('c',18)
+#define CDIOCPAUSE _IO('c',19)
+#define CDIOCRESUME _IO('c',20)
+#define CDIOCRESET _IO('c',21)
+#define CDIOCSTART _IO('c',22)
+#define CDIOCSTOP _IO('c',23)
+#define CDIOCEJECT _IO('c',24)
+#define CDIOCALLOW _IO('c',25)
+#define CDIOCPREVENT _IO('c',26)
+
+struct ioc_play_msf
+{
+ u_char start_m;
+ u_char start_s;
+ u_char start_f;
+ u_char end_m;
+ u_char end_s;
+ u_char end_f;
+};
+#define CDIOCPLAYMSF _IOW('c',25,struct ioc_play_msf)
+
+#endif /* _SYS_CDIO_H_ */
diff --git a/sys/sys/chio.h b/sys/sys/chio.h
new file mode 100644
index 0000000..10fa51d
--- /dev/null
+++ b/sys/sys/chio.h
@@ -0,0 +1,92 @@
+/*
+ * 16 Feb 93 Julian Elischer ADDED for SCSI system
+ *
+ * $Id: chio.h,v 1.3 1993/10/16 17:16:27 rgrimes Exp $
+ */
+
+/* This is a "convertet" mtio.h from 386BSD
+ Stefan Grefen grefen@goofy.zdv.uni-mainz.de
+ */
+
+/*
+ * Structures and definitions for changer io control commands
+ */
+#ifndef _SYS_CHIO_H_
+#define _SYS_CHIO_H_
+
+#define CH_INVERT 0x10000
+#define CH_ADDR_MASK 0xffff
+struct chop {
+ short ch_op; /* operations defined below */
+ short result; /* The result */
+ union {
+ struct {
+ int chm; /* Transport element */
+ int from;
+ int to;
+ } move;
+ struct {
+ int chm; /* Transport element */
+ int to;
+ } position;
+ struct {
+ short chmo; /* Offset of first CHM */
+ short chms; /* No. of CHM */
+ short slots; /* No. of Storage Elements */
+ short sloto; /* Offset of first SE */
+ short imexs; /* No. of Import/Export Slots */
+ short imexo; /* Offset of first IM/EX */
+ short drives; /* No. of CTS */
+ short driveo; /* Offset of first CTS */
+ short rot; /* CHM can rotate */
+ } getparam;
+ struct {
+ int type;
+#define CH_CHM 1
+#define CH_STOR 2
+#define CH_IMEX 3
+#define CH_CTS 4
+ int from;
+ struct {
+ u_char elema_1;
+ u_char elema_0;
+ u_char full:1;
+ u_char rsvd:1;
+ u_char except:1;
+ u_char :5;
+ u_char rsvd2;
+ union {
+ struct {
+ u_char add_sense_code;
+ u_char add_sense_code_qualifier;
+ } specs;
+ short add_sense;
+/* WARINING LSB only */
+#define CH_CHOLDER 0x0290 /* Cartridge holder is missing */
+#define CH_STATUSQ 0x0390 /* Status is questionable */
+#define CH_CTS_CLOSED 0x0490 /* CTS door is closed */
+
+ } ch_add_sense;
+ u_char rsvd3[3];
+ u_char :6;
+ u_char invert:1;
+ u_char svalid:1;
+ u_char source_1;
+ u_char source_0;
+ u_char rsvd4[4];
+ } elem_data;
+ } get_elem_stat;
+ } u;
+};
+
+/* operations */
+#define CHMOVE 1
+#define CHPOSITION 2
+#define CHGETPARAM 3
+#define CHGETELEM 4
+
+
+/* Changer IO control command */
+#define CHIOOP _IOWR('c', 1, struct chop) /* do a mag tape op */
+
+#endif /*_SYS_CHIO_H*/
diff --git a/sys/sys/clist.h b/sys/sys/clist.h
new file mode 100644
index 0000000..3e05cf6
--- /dev/null
+++ b/sys/sys/clist.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clist.h 8.1 (Berkeley) 6/4/93
+ */
+
+struct cblock {
+ struct cblock *c_next; /* next cblock in queue */
+ unsigned char c_quote[CBQSIZE]; /* quoted characters */
+ unsigned char c_info[CBSIZE]; /* characters */
+};
+
+#ifdef KERNEL
+extern struct cblock *cfree, *cfreelist;
+extern int cfreecount, nclist;
+#endif
diff --git a/sys/sys/conf.h b/sys/sys/conf.h
new file mode 100644
index 0000000..58cb6fa
--- /dev/null
+++ b/sys/sys/conf.h
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)conf.h 8.3 (Berkeley) 1/21/94
+ */
+
+/*
+ * Definitions of device driver entry switches
+ */
+
+struct buf;
+struct proc;
+struct tty;
+struct uio;
+struct vnode;
+
+struct bdevsw {
+ int (*d_open) __P((dev_t dev, int oflags, int devtype,
+ struct proc *p));
+ int (*d_close) __P((dev_t dev, int fflag, int devtype,
+ struct proc *p));
+ int (*d_strategy) __P((struct buf *bp));
+ int (*d_ioctl) __P((dev_t dev, int cmd, caddr_t data,
+ int fflag, struct proc *p));
+ int (*d_dump) (); /* parameters vary by architecture */
+ int (*d_psize) __P((dev_t dev));
+ int d_flags;
+};
+
+#ifdef KERNEL
+extern struct bdevsw bdevsw[];
+#endif
+
+struct cdevsw {
+ int (*d_open) __P((dev_t dev, int oflags, int devtype,
+ struct proc *p));
+ int (*d_close) __P((dev_t dev, int fflag, int devtype,
+ struct proc *));
+ int (*d_read) __P((dev_t dev, struct uio *uio, int ioflag));
+ int (*d_write) __P((dev_t dev, struct uio *uio, int ioflag));
+ int (*d_ioctl) __P((dev_t dev, int cmd, caddr_t data,
+ int fflag, struct proc *p));
+ int (*d_stop) __P((struct tty *tp, int rw));
+ int (*d_reset) __P((int uban)); /* XXX */
+ struct tty *d_ttys;
+ int (*d_select) __P((dev_t dev, int which, struct proc *p));
+ int (*d_mmap) __P(());
+ int (*d_strategy) __P((struct buf *bp));
+};
+
+#ifdef KERNEL
+extern struct cdevsw cdevsw[];
+
+/* symbolic sleep message strings */
+extern char devopn[], devio[], devwait[], devin[], devout[];
+extern char devioc[], devcls[];
+#endif
+
+struct linesw {
+ int (*l_open) __P((dev_t dev, struct tty *tp));
+ int (*l_close) __P((struct tty *tp, int flag));
+ int (*l_read) __P((struct tty *tp, struct uio *uio,
+ int flag));
+ int (*l_write) __P((struct tty *tp, struct uio *uio,
+ int flag));
+ int (*l_ioctl) __P((struct tty *tp, int cmd, caddr_t data,
+ int flag, struct proc *p));
+ int (*l_rint) __P((int c, struct tty *tp));
+ int (*l_start) __P((struct tty *tp));
+ int (*l_modem) __P((struct tty *tp, int flag));
+};
+
+#ifdef KERNEL
+extern struct linesw linesw[];
+#endif
+
+struct swdevt {
+ dev_t sw_dev;
+ int sw_flags;
+ int sw_nblks;
+ struct vnode *sw_vp;
+};
+#define SW_FREED 0x01
+#define SW_SEQUENTIAL 0x02
+#define sw_freed sw_flags /* XXX compat */
+
+#ifdef KERNEL
+extern struct swdevt swdevt[];
+#endif
diff --git a/sys/sys/cons.h b/sys/sys/cons.h
new file mode 100644
index 0000000..2766193
--- /dev/null
+++ b/sys/sys/cons.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1988 University of Utah.
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)cons.h 7.2 (Berkeley) 5/9/91
+ * $Id: cons.h,v 1.3 1993/11/07 17:41:33 wollman Exp $
+ */
+
+#ifndef _MACHINE_CONS_H_
+#define _MACHINE_CONS_H_ 1
+
+struct consdev {
+ int (*cn_probe)(); /* probe hardware and fill in consdev info */
+ int (*cn_init)(); /* turn on as console */
+ int (*cn_getc)(); /* kernel getchar interface */
+ int (*cn_putc)(); /* kernel putchar interface */
+ struct tty *cn_tp; /* tty structure for console device */
+ dev_t cn_dev; /* major/minor of device */
+ short cn_pri; /* pecking order; the higher the better */
+};
+
+/* values for cn_pri - reflect our policy for console selection */
+#define CN_DEAD 0 /* device doesn't exist */
+#define CN_NORMAL 1 /* device exists but is nothing special */
+#define CN_INTERNAL 2 /* "internal" bit-mapped display */
+#define CN_REMOTE 3 /* serial interface with remote bit set */
+
+/* XXX */
+#define CONSMAJOR 0
+
+#ifdef KERNEL
+extern struct consdev constab[];
+extern struct consdev *cn_tab;
+extern struct tty *cn_tty;
+
+struct proc; struct uio;
+
+/* cdevsw[] entries */
+extern int cnopen(dev_t, int, int, struct proc *);
+extern int cnclose(dev_t, int, int, struct proc *);
+extern int cnread(dev_t, struct uio *, int);
+extern int cnwrite(dev_t, struct uio *, int);
+extern int cnioctl(dev_t, int, caddr_t, int, struct proc *);
+extern int cnselect(dev_t, int, struct proc *);
+
+/* other kernel entry points */
+extern void cninit(void);
+extern int cngetc(void);
+extern void cnputc(int /*char*/);
+extern int pg(const char *, ...);
+
+#endif /* KERNEL */
+#endif /* _MACHINE_CONS_H_ */
diff --git a/sys/sys/device.h b/sys/sys/device.h
new file mode 100644
index 0000000..0a233ed
--- /dev/null
+++ b/sys/sys/device.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)device.h 8.2 (Berkeley) 2/17/94
+ */
+
+#ifndef _SYS_DEVICE_H_
+#define _SYS_DEVICE_H_
+
+/*
+ * Minimal device structures.
+ * Note that all ``system'' device types are listed here.
+ */
+enum devclass {
+ DV_DULL, /* generic, no special info */
+ DV_CPU, /* CPU (carries resource utilization) */
+ DV_DISK, /* disk drive (label, etc) */
+ DV_IFNET, /* network interface */
+ DV_TAPE, /* tape device */
+ DV_TTY /* serial line interface (???) */
+};
+
+struct device {
+ enum devclass dv_class; /* this device's classification */
+ struct device *dv_next; /* next in list of all */
+ struct cfdata *dv_cfdata; /* config data that found us */
+ int dv_unit; /* device unit number */
+ char dv_xname[16]; /* external name (name + unit) */
+ struct device *dv_parent; /* pointer to parent device */
+};
+
+/* `event' counters (use zero or more per device instance, as needed) */
+struct evcnt {
+ struct evcnt *ev_next; /* linked list */
+ struct device *ev_dev; /* associated device */
+ int ev_count; /* how many have occurred */
+ char ev_name[8]; /* what to call them (systat display) */
+};
+
+/*
+ * Configuration data (i.e., data placed in ioconf.c).
+ */
+struct cfdata {
+ struct cfdriver *cf_driver; /* config driver */
+ short cf_unit; /* unit number */
+ short cf_fstate; /* finding state (below) */
+ int *cf_loc; /* locators (machine dependent) */
+ int cf_flags; /* flags from config */
+ short *cf_parents; /* potential parents */
+ void (**cf_ivstubs)(); /* config-generated vectors, if any */
+};
+#define FSTATE_NOTFOUND 0 /* has not been found */
+#define FSTATE_FOUND 1 /* has been found */
+#define FSTATE_STAR 2 /* duplicable */
+
+typedef int (*cfmatch_t) __P((struct device *, struct cfdata *, void *));
+
+/*
+ * `configuration' driver (what the machine-independent autoconf uses).
+ * As devices are found, they are applied against all the potential matches.
+ * The one with the best match is taken, and a device structure (plus any
+ * other data desired) is allocated. Pointers to these are placed into
+ * an array of pointers. The array itself must be dynamic since devices
+ * can be found long after the machine is up and running.
+ */
+struct cfdriver {
+ void **cd_devs; /* devices found */
+ char *cd_name; /* device name */
+ cfmatch_t cd_match; /* returns a match level */
+ void (*cd_attach) __P((struct device *, struct device *, void *));
+ enum devclass cd_class; /* device classification */
+ size_t cd_devsize; /* size of dev data (for malloc) */
+ void *cd_aux; /* additional driver, if any */
+ int cd_ndevs; /* size of cd_devs array */
+};
+
+/*
+ * Configuration printing functions, and their return codes. The second
+ * argument is NULL if the device was configured; otherwise it is the name
+ * of the parent device. The return value is ignored if the device was
+ * configured, so most functions can return UNCONF unconditionally.
+ */
+typedef int (*cfprint_t) __P((void *, char *));
+#define QUIET 0 /* print nothing */
+#define UNCONF 1 /* print " not configured\n" */
+#define UNSUPP 2 /* print " not supported\n" */
+
+/*
+ * Pseudo-device attach information (function + number of pseudo-devs).
+ */
+struct pdevinit {
+ void (*pdev_attach) __P((int));
+ int pdev_count;
+};
+
+struct device *alldevs; /* head of list of all devices */
+struct evcnt *allevents; /* head of list of all events */
+
+struct cfdata *config_search __P((cfmatch_t, struct device *, void *));
+struct cfdata *config_rootsearch __P((cfmatch_t, char *, void *));
+int config_found __P((struct device *, void *, cfprint_t));
+int config_rootfound __P((char *, void *));
+void config_attach __P((struct device *, struct cfdata *, void *, cfprint_t));
+void evcnt_attach __P((struct device *, const char *, struct evcnt *));
+#endif /* !_SYS_DEVICE_H_ */
diff --git a/sys/sys/dir.h b/sys/sys/dir.h
new file mode 100644
index 0000000..0c4cd67
--- /dev/null
+++ b/sys/sys/dir.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dir.h 8.2 (Berkeley) 1/4/94
+ */
+
+/*
+ * The information in this file should be obtained from <dirent.h>
+ * and is provided solely (and temporarily) for backward compatibility.
+ */
+
+#ifndef _SYS_DIR_H_
+#define _SYS_DIR_H_
+
+#include <dirent.h>
+
+/*
+ * Backwards compatibility.
+ */
+#define direct dirent
+
+/*
+ * The DIRSIZ macro gives the minimum record length which will hold
+ * the directory entry. This requires the amount of space in struct direct
+ * without the d_name field, plus enough space for the name with a terminating
+ * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
+ */
+#undef DIRSIZ
+#define DIRSIZ(dp) \
+ ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
+
+#endif /* !_SYS_DIR_H_ */
diff --git a/sys/sys/dirent.h b/sys/sys/dirent.h
new file mode 100644
index 0000000..1c4b96aa
--- /dev/null
+++ b/sys/sys/dirent.h
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dirent.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * The dirent structure defines the format of directory entries returned by
+ * the getdirentries(2) system call.
+ *
+ * A directory entry has a struct dirent at the front of it, containing its
+ * inode number, the length of the entry, and the length of the name
+ * contained in the entry. These are followed by the name padded to a 4
+ * byte boundary with null bytes. All names are guaranteed null terminated.
+ * The maximum length of a name in a directory is MAXNAMLEN.
+ */
+
+struct dirent {
+ unsigned long d_fileno; /* file number of entry */
+ unsigned short d_reclen; /* length of this record */
+ unsigned char d_type; /* file type, see below */
+ unsigned char d_namlen; /* length of string in d_name */
+#ifdef _POSIX_SOURCE
+ char d_name[255 + 1]; /* name must be no longer than this */
+#else
+#define MAXNAMLEN 255
+ char d_name[MAXNAMLEN + 1]; /* name must be no longer than this */
+#endif
+};
+
+/*
+ * File types
+ */
+#define DT_UNKNOWN 0
+#define DT_FIFO 1
+#define DT_CHR 2
+#define DT_DIR 4
+#define DT_BLK 6
+#define DT_REG 8
+#define DT_LNK 10
+#define DT_SOCK 12
+
+/*
+ * Convert between stat structure types and directory types.
+ */
+#define IFTODT(mode) (((mode) & 0170000) >> 12)
+#define DTTOIF(dirtype) ((dirtype) << 12)
diff --git a/sys/sys/disk.h b/sys/sys/disk.h
new file mode 100644
index 0000000..352ecf0
--- /dev/null
+++ b/sys/sys/disk.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)disk.h 8.1 (Berkeley) 6/2/93
+ *
+ * from: $Header: disk.h,v 1.5 92/11/19 04:33:03 torek Exp $ (LBL)
+ */
+
+/*
+ * Disk device structures.
+ *
+ * Note that this is only a preliminary outline. The final disk structures
+ * may be somewhat different.
+ */
+struct buf;
+
+struct dkdevice {
+ struct device dk_dev; /* base device */
+ struct dkdevice *dk_next; /* list of disks; not yet used */
+ int dk_bps; /* xfer rate: bytes per second */
+ int dk_bopenmask; /* block devices open */
+ int dk_copenmask; /* character devices open */
+ int dk_openmask; /* composite (bopen|copen) */
+ int dk_state; /* label state ### */
+ int dk_blkshift; /* shift to convert DEV_BSIZE to blks */
+ int dk_byteshift; /* shift to convert bytes to blks */
+ struct dkdriver *dk_driver; /* pointer to driver */
+ daddr_t dk_labelsector; /* sector containing label */
+ struct disklabel dk_label; /* label */
+};
+
+struct dkdriver {
+ void (*d_strategy) __P((struct buf *));
+#ifdef notyet
+ int (*d_open) __P((dev_t dev, int ifmt, int, struct proc *));
+ int (*d_close) __P((dev_t dev, int, int ifmt, struct proc *));
+ int (*d_ioctl) __P((dev_t dev, int cmd, caddr_t data, int fflag,
+ struct proc *));
+ int (*d_dump) __P((dev_t));
+ void (*d_start) __P((struct buf *, daddr_t));
+ int (*d_mklabel) __P((struct dkdevice *));
+#endif
+};
+
+/* states */
+#define DK_CLOSED 0 /* drive is closed */
+#define DK_WANTOPEN 1 /* drive being opened */
+#define DK_WANTOPENRAW 2 /* drive being opened */
+#define DK_RDLABEL 3 /* label being read */
+#define DK_OPEN 4 /* label read, drive open */
+#define DK_OPENRAW 5 /* open without label */
+
+#ifdef DISKSORT_STATS
+/*
+ * Stats from disksort().
+ */
+struct disksort_stats {
+ long ds_newhead; /* # new queue heads created */
+ long ds_newtail; /* # new queue tails created */
+ long ds_midfirst; /* # insertions into sort list */
+ long ds_endfirst; /* # insertions at end of sort list */
+ long ds_newsecond; /* # inversions (2nd lists) created */
+ long ds_midsecond; /* # insertions into 2nd list */
+ long ds_endsecond; /* # insertions at end of 2nd list */
+};
+#endif
+
+#ifdef KERNEL
+void disksort __P((struct buf *, struct buf *));
+char *readdisklabel __P((struct dkdevice *, int));
+int setdisklabel __P((struct dkdevice *, struct disklabel *));
+int writedisklabel __P((struct dkdevice *, int));
+int diskerr __P((struct dkdevice *, struct buf *, char *, int, int));
+#endif
diff --git a/sys/sys/disklabel.h b/sys/sys/disklabel.h
new file mode 100644
index 0000000..60b8f8f
--- /dev/null
+++ b/sys/sys/disklabel.h
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 1987, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)disklabel.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * Disk description table, see disktab(5)
+ */
+#define _PATH_DISKTAB "/etc/disktab"
+#define DISKTAB "/etc/disktab" /* deprecated */
+
+/*
+ * Each disk has a label which includes information about the hardware
+ * disk geometry, filesystem partitions, and drive specific information.
+ * The label is in block 0 or 1, possibly offset from the beginning
+ * to leave room for a bootstrap, etc.
+ */
+
+/* XXX these should be defined per controller (or drive) elsewhere, not here! */
+#ifdef i386
+#define LABELSECTOR 1 /* sector containing label */
+#define LABELOFFSET 0 /* offset of label in sector */
+#endif
+
+#ifndef LABELSECTOR
+#define LABELSECTOR 0 /* sector containing label */
+#endif
+
+#ifndef LABELOFFSET
+#define LABELOFFSET 64 /* offset of label in sector */
+#endif
+
+#define DISKMAGIC ((u_long) 0x82564557) /* The disk magic number */
+#ifndef MAXPARTITIONS
+#define MAXPARTITIONS 8
+#endif
+
+
+#ifndef LOCORE
+struct disklabel {
+ u_long d_magic; /* the magic number */
+ short d_type; /* drive type */
+ short d_subtype; /* controller/d_type specific */
+ char d_typename[16]; /* type name, e.g. "eagle" */
+ /*
+ * d_packname contains the pack identifier and is returned when
+ * the disklabel is read off the disk or in-core copy.
+ * d_boot0 and d_boot1 are the (optional) names of the
+ * primary (block 0) and secondary (block 1-15) bootstraps
+ * as found in /usr/mdec. These are returned when using
+ * getdiskbyname(3) to retrieve the values from /etc/disktab.
+ */
+#if defined(KERNEL) || defined(STANDALONE)
+ char d_packname[16]; /* pack identifier */
+#else
+ union {
+ char un_d_packname[16]; /* pack identifier */
+ struct {
+ char *un_d_boot0; /* primary bootstrap name */
+ char *un_d_boot1; /* secondary bootstrap name */
+ } un_b;
+ } d_un;
+#define d_packname d_un.un_d_packname
+#define d_boot0 d_un.un_b.un_d_boot0
+#define d_boot1 d_un.un_b.un_d_boot1
+#endif /* ! KERNEL or STANDALONE */
+ /* disk geometry: */
+ u_long d_secsize; /* # of bytes per sector */
+ u_long d_nsectors; /* # of data sectors per track */
+ u_long d_ntracks; /* # of tracks per cylinder */
+ u_long d_ncylinders; /* # of data cylinders per unit */
+ u_long d_secpercyl; /* # of data sectors per cylinder */
+ u_long d_secperunit; /* # of data sectors per unit */
+ /*
+ * Spares (bad sector replacements) below
+ * are not counted in d_nsectors or d_secpercyl.
+ * Spare sectors are assumed to be physical sectors
+ * which occupy space at the end of each track and/or cylinder.
+ */
+ u_short d_sparespertrack; /* # of spare sectors per track */
+ u_short d_sparespercyl; /* # of spare sectors per cylinder */
+ /*
+ * Alternate cylinders include maintenance, replacement,
+ * configuration description areas, etc.
+ */
+ u_long d_acylinders; /* # of alt. cylinders per unit */
+
+ /* hardware characteristics: */
+ /*
+ * d_interleave, d_trackskew and d_cylskew describe perturbations
+ * in the media format used to compensate for a slow controller.
+ * Interleave is physical sector interleave, set up by the formatter
+ * or controller when formatting. When interleaving is in use,
+ * logically adjacent sectors are not physically contiguous,
+ * but instead are separated by some number of sectors.
+ * It is specified as the ratio of physical sectors traversed
+ * per logical sector. Thus an interleave of 1:1 implies contiguous
+ * layout, while 2:1 implies that logical sector 0 is separated
+ * by one sector from logical sector 1.
+ * d_trackskew is the offset of sector 0 on track N
+ * relative to sector 0 on track N-1 on the same cylinder.
+ * Finally, d_cylskew is the offset of sector 0 on cylinder N
+ * relative to sector 0 on cylinder N-1.
+ */
+ u_short d_rpm; /* rotational speed */
+ u_short d_interleave; /* hardware sector interleave */
+ u_short d_trackskew; /* sector 0 skew, per track */
+ u_short d_cylskew; /* sector 0 skew, per cylinder */
+ u_long d_headswitch; /* head switch time, usec */
+ u_long d_trkseek; /* track-to-track seek, usec */
+ u_long d_flags; /* generic flags */
+#define NDDATA 5
+ u_long d_drivedata[NDDATA]; /* drive-type specific information */
+#define NSPARE 5
+ u_long d_spare[NSPARE]; /* reserved for future use */
+ u_long d_magic2; /* the magic number (again) */
+ u_short d_checksum; /* xor of data incl. partitions */
+
+ /* filesystem and partition information: */
+ u_short d_npartitions; /* number of partitions in following */
+ u_long d_bbsize; /* size of boot area at sn0, bytes */
+ u_long d_sbsize; /* max size of fs superblock, bytes */
+ struct partition { /* the partition table */
+ u_long p_size; /* number of sectors in partition */
+ u_long p_offset; /* starting sector */
+ u_long p_fsize; /* filesystem basic fragment size */
+ u_char p_fstype; /* filesystem type, see below */
+ u_char p_frag; /* filesystem fragments per block */
+ union {
+ u_short cpg; /* UFS: FS cylinders per group */
+ u_short sgs; /* LFS: FS segment shift */
+ } __partition_u1;
+#define p_cpg __partition_u1.cpg
+#define p_sgs __partition_u1.sgs
+ } d_partitions[MAXPARTITIONS]; /* actually may be more */
+};
+struct cpu_disklabel {
+};
+#else /* LOCORE */
+ /*
+ * offsets for asm boot files.
+ */
+ .set d_secsize,40
+ .set d_nsectors,44
+ .set d_ntracks,48
+ .set d_ncylinders,52
+ .set d_secpercyl,56
+ .set d_secperunit,60
+ .set d_end_,276 /* size of disk label */
+#endif /* LOCORE */
+
+/* d_type values: */
+#define DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */
+#define DTYPE_MSCP 2 /* MSCP */
+#define DTYPE_DEC 3 /* other DEC (rk, rl) */
+#define DTYPE_SCSI 4 /* SCSI */
+#define DTYPE_ESDI 5 /* ESDI interface */
+#define DTYPE_ST506 6 /* ST506 etc. */
+#define DTYPE_HPIB 7 /* CS/80 on HP-IB */
+#define DTYPE_HPFL 8 /* HP Fiber-link */
+#define DTYPE_FLOPPY 10 /* floppy */
+
+/* d_subtype values: */
+#define DSTYPE_INDOSPART 0x8 /* is inside dos partition */
+#define DSTYPE_DOSPART(s) ((s) & 3) /* dos partition number */
+#define DSTYPE_GEOMETRY 0x10 /* drive params in label */
+
+#ifdef DKTYPENAMES
+static char *dktypenames[] = {
+ "unknown",
+ "SMD",
+ "MSCP",
+ "old DEC",
+ "SCSI",
+ "ESDI",
+ "ST506",
+ "HP-IB",
+ "HP-FL",
+ "type 9",
+ "floppy",
+ 0
+};
+#define DKMAXTYPES (sizeof(dktypenames) / sizeof(dktypenames[0]) - 1)
+#endif
+
+/*
+ * Filesystem type and version.
+ * Used to interpret other filesystem-specific
+ * per-partition information.
+ */
+#define FS_UNUSED 0 /* unused */
+#define FS_SWAP 1 /* swap */
+#define FS_V6 2 /* Sixth Edition */
+#define FS_V7 3 /* Seventh Edition */
+#define FS_SYSV 4 /* System V */
+#define FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */
+#define FS_V8 6 /* Eighth Edition, 4K blocks */
+#define FS_BSDFFS 7 /* 4.2BSD fast file system */
+#define FS_MSDOS 8 /* MSDOS file system */
+#define FS_BSDLFS 9 /* 4.4BSD log-structured file system */
+#define FS_OTHER 10 /* in use, but unknown/unsupported */
+#define FS_HPFS 11 /* OS/2 high-performance file system */
+#define FS_ISO9660 12 /* ISO 9660, normally CD-ROM */
+#define FS_BOOT 13 /* partition contains bootstrap */
+
+#ifdef DKTYPENAMES
+static char *fstypenames[] = {
+ "unused",
+ "swap",
+ "Version 6",
+ "Version 7",
+ "System V",
+ "4.1BSD",
+ "Eighth Edition",
+ "4.2BSD",
+ "MSDOS",
+ "4.4LFS",
+ "unknown",
+ "HPFS",
+ "ISO9660",
+ "boot",
+ 0
+};
+#define FSMAXTYPES (sizeof(fstypenames) / sizeof(fstypenames[0]) - 1)
+#endif
+
+/*
+ * flags shared by various drives:
+ */
+#define D_REMOVABLE 0x01 /* removable media */
+#define D_ECC 0x02 /* supports ECC */
+#define D_BADSECT 0x04 /* supports bad sector forw. */
+#define D_RAMDISK 0x08 /* disk emulator */
+#define D_CHAIN 0x10 /* can do back-back transfers */
+
+/*
+ * Drive data for SMD.
+ */
+#define d_smdflags d_drivedata[0]
+#define D_SSE 0x1 /* supports skip sectoring */
+#define d_mindist d_drivedata[1]
+#define d_maxdist d_drivedata[2]
+#define d_sdist d_drivedata[3]
+
+/*
+ * Drive data for ST506.
+ */
+#define d_precompcyl d_drivedata[0]
+#define d_gap3 d_drivedata[1] /* used only when formatting */
+
+/*
+ * Drive data for SCSI.
+ */
+#define d_blind d_drivedata[0]
+
+#ifndef LOCORE
+/*
+ * Structure used to perform a format
+ * or other raw operation, returning data
+ * and/or register values.
+ * Register identification and format
+ * are device- and driver-dependent.
+ */
+struct format_op {
+ char *df_buf;
+ int df_count; /* value-result */
+ daddr_t df_startblk;
+ int df_reg[8]; /* result */
+};
+
+/*
+ * Structure used internally to retrieve
+ * information about a partition on a disk.
+ */
+struct partinfo {
+ struct disklabel *disklab;
+ struct partition *part;
+};
+
+/* DOS partition table -- located in boot block */
+
+#define DOSBBSECTOR 0 /* DOS boot block relative sector number */
+#define DOSPARTOFF 446
+#define NDOSPART 4
+
+struct dos_partition {
+ unsigned char dp_flag; /* bootstrap flags */
+ unsigned char dp_shd; /* starting head */
+ unsigned char dp_ssect; /* starting sector */
+ unsigned char dp_scyl; /* starting cylinder */
+ unsigned char dp_typ; /* partition type */
+#define DOSPTYP_386BSD 0xa5 /* 386BSD partition type */
+ unsigned char dp_ehd; /* end head */
+ unsigned char dp_esect; /* end sector */
+ unsigned char dp_ecyl; /* end cylinder */
+ unsigned long dp_start; /* absolute starting sector number */
+ unsigned long dp_size; /* partition size in sectors */
+};
+
+extern struct dos_partition dos_partitions[NDOSPART];
+
+#define DPSECT(s) ((s) & 0x3f) /* isolate relevant bits of sector */
+#define DPCYL(c, s) ((c) + (((s) & 0xc0)<<2)) /* and those that are cylinder */
+
+/*
+ * Disk-specific ioctls.
+ */
+ /* get and set disklabel; DIOCGPART used internally */
+#define DIOCGDINFO _IOR('d', 101, struct disklabel)/* get */
+#define DIOCSDINFO _IOW('d', 102, struct disklabel)/* set */
+#define DIOCWDINFO _IOW('d', 103, struct disklabel)/* set, update disk */
+#define DIOCGPART _IOW('d', 104, struct partinfo) /* get partition */
+
+/* do format operation, read or write */
+#define DIOCRFORMAT _IOWR('d', 105, struct format_op)
+#define DIOCWFORMAT _IOWR('d', 106, struct format_op)
+
+#define DIOCSSTEP _IOW('d', 107, int) /* set step rate */
+#define DIOCSRETRIES _IOW('d', 108, int) /* set # of retries */
+#define DIOCWLABEL _IOW('d', 109, int) /* write en/disable label */
+
+#define DIOCSBAD _IOW('d', 110, struct dkbad) /* set kernel dkbad */
+
+#endif /* LOCORE */
+
+#if !defined(KERNEL) && !defined(LOCORE)
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+struct disklabel *getdiskbyname __P((const char *));
+__END_DECLS
+
+#endif
diff --git a/sys/sys/diskmbr.h b/sys/sys/diskmbr.h
new file mode 100644
index 0000000..60b8f8f
--- /dev/null
+++ b/sys/sys/diskmbr.h
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 1987, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)disklabel.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * Disk description table, see disktab(5)
+ */
+#define _PATH_DISKTAB "/etc/disktab"
+#define DISKTAB "/etc/disktab" /* deprecated */
+
+/*
+ * Each disk has a label which includes information about the hardware
+ * disk geometry, filesystem partitions, and drive specific information.
+ * The label is in block 0 or 1, possibly offset from the beginning
+ * to leave room for a bootstrap, etc.
+ */
+
+/* XXX these should be defined per controller (or drive) elsewhere, not here! */
+#ifdef i386
+#define LABELSECTOR 1 /* sector containing label */
+#define LABELOFFSET 0 /* offset of label in sector */
+#endif
+
+#ifndef LABELSECTOR
+#define LABELSECTOR 0 /* sector containing label */
+#endif
+
+#ifndef LABELOFFSET
+#define LABELOFFSET 64 /* offset of label in sector */
+#endif
+
+#define DISKMAGIC ((u_long) 0x82564557) /* The disk magic number */
+#ifndef MAXPARTITIONS
+#define MAXPARTITIONS 8
+#endif
+
+
+#ifndef LOCORE
+struct disklabel {
+ u_long d_magic; /* the magic number */
+ short d_type; /* drive type */
+ short d_subtype; /* controller/d_type specific */
+ char d_typename[16]; /* type name, e.g. "eagle" */
+ /*
+ * d_packname contains the pack identifier and is returned when
+ * the disklabel is read off the disk or in-core copy.
+ * d_boot0 and d_boot1 are the (optional) names of the
+ * primary (block 0) and secondary (block 1-15) bootstraps
+ * as found in /usr/mdec. These are returned when using
+ * getdiskbyname(3) to retrieve the values from /etc/disktab.
+ */
+#if defined(KERNEL) || defined(STANDALONE)
+ char d_packname[16]; /* pack identifier */
+#else
+ union {
+ char un_d_packname[16]; /* pack identifier */
+ struct {
+ char *un_d_boot0; /* primary bootstrap name */
+ char *un_d_boot1; /* secondary bootstrap name */
+ } un_b;
+ } d_un;
+#define d_packname d_un.un_d_packname
+#define d_boot0 d_un.un_b.un_d_boot0
+#define d_boot1 d_un.un_b.un_d_boot1
+#endif /* ! KERNEL or STANDALONE */
+ /* disk geometry: */
+ u_long d_secsize; /* # of bytes per sector */
+ u_long d_nsectors; /* # of data sectors per track */
+ u_long d_ntracks; /* # of tracks per cylinder */
+ u_long d_ncylinders; /* # of data cylinders per unit */
+ u_long d_secpercyl; /* # of data sectors per cylinder */
+ u_long d_secperunit; /* # of data sectors per unit */
+ /*
+ * Spares (bad sector replacements) below
+ * are not counted in d_nsectors or d_secpercyl.
+ * Spare sectors are assumed to be physical sectors
+ * which occupy space at the end of each track and/or cylinder.
+ */
+ u_short d_sparespertrack; /* # of spare sectors per track */
+ u_short d_sparespercyl; /* # of spare sectors per cylinder */
+ /*
+ * Alternate cylinders include maintenance, replacement,
+ * configuration description areas, etc.
+ */
+ u_long d_acylinders; /* # of alt. cylinders per unit */
+
+ /* hardware characteristics: */
+ /*
+ * d_interleave, d_trackskew and d_cylskew describe perturbations
+ * in the media format used to compensate for a slow controller.
+ * Interleave is physical sector interleave, set up by the formatter
+ * or controller when formatting. When interleaving is in use,
+ * logically adjacent sectors are not physically contiguous,
+ * but instead are separated by some number of sectors.
+ * It is specified as the ratio of physical sectors traversed
+ * per logical sector. Thus an interleave of 1:1 implies contiguous
+ * layout, while 2:1 implies that logical sector 0 is separated
+ * by one sector from logical sector 1.
+ * d_trackskew is the offset of sector 0 on track N
+ * relative to sector 0 on track N-1 on the same cylinder.
+ * Finally, d_cylskew is the offset of sector 0 on cylinder N
+ * relative to sector 0 on cylinder N-1.
+ */
+ u_short d_rpm; /* rotational speed */
+ u_short d_interleave; /* hardware sector interleave */
+ u_short d_trackskew; /* sector 0 skew, per track */
+ u_short d_cylskew; /* sector 0 skew, per cylinder */
+ u_long d_headswitch; /* head switch time, usec */
+ u_long d_trkseek; /* track-to-track seek, usec */
+ u_long d_flags; /* generic flags */
+#define NDDATA 5
+ u_long d_drivedata[NDDATA]; /* drive-type specific information */
+#define NSPARE 5
+ u_long d_spare[NSPARE]; /* reserved for future use */
+ u_long d_magic2; /* the magic number (again) */
+ u_short d_checksum; /* xor of data incl. partitions */
+
+ /* filesystem and partition information: */
+ u_short d_npartitions; /* number of partitions in following */
+ u_long d_bbsize; /* size of boot area at sn0, bytes */
+ u_long d_sbsize; /* max size of fs superblock, bytes */
+ struct partition { /* the partition table */
+ u_long p_size; /* number of sectors in partition */
+ u_long p_offset; /* starting sector */
+ u_long p_fsize; /* filesystem basic fragment size */
+ u_char p_fstype; /* filesystem type, see below */
+ u_char p_frag; /* filesystem fragments per block */
+ union {
+ u_short cpg; /* UFS: FS cylinders per group */
+ u_short sgs; /* LFS: FS segment shift */
+ } __partition_u1;
+#define p_cpg __partition_u1.cpg
+#define p_sgs __partition_u1.sgs
+ } d_partitions[MAXPARTITIONS]; /* actually may be more */
+};
+struct cpu_disklabel {
+};
+#else /* LOCORE */
+ /*
+ * offsets for asm boot files.
+ */
+ .set d_secsize,40
+ .set d_nsectors,44
+ .set d_ntracks,48
+ .set d_ncylinders,52
+ .set d_secpercyl,56
+ .set d_secperunit,60
+ .set d_end_,276 /* size of disk label */
+#endif /* LOCORE */
+
+/* d_type values: */
+#define DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */
+#define DTYPE_MSCP 2 /* MSCP */
+#define DTYPE_DEC 3 /* other DEC (rk, rl) */
+#define DTYPE_SCSI 4 /* SCSI */
+#define DTYPE_ESDI 5 /* ESDI interface */
+#define DTYPE_ST506 6 /* ST506 etc. */
+#define DTYPE_HPIB 7 /* CS/80 on HP-IB */
+#define DTYPE_HPFL 8 /* HP Fiber-link */
+#define DTYPE_FLOPPY 10 /* floppy */
+
+/* d_subtype values: */
+#define DSTYPE_INDOSPART 0x8 /* is inside dos partition */
+#define DSTYPE_DOSPART(s) ((s) & 3) /* dos partition number */
+#define DSTYPE_GEOMETRY 0x10 /* drive params in label */
+
+#ifdef DKTYPENAMES
+static char *dktypenames[] = {
+ "unknown",
+ "SMD",
+ "MSCP",
+ "old DEC",
+ "SCSI",
+ "ESDI",
+ "ST506",
+ "HP-IB",
+ "HP-FL",
+ "type 9",
+ "floppy",
+ 0
+};
+#define DKMAXTYPES (sizeof(dktypenames) / sizeof(dktypenames[0]) - 1)
+#endif
+
+/*
+ * Filesystem type and version.
+ * Used to interpret other filesystem-specific
+ * per-partition information.
+ */
+#define FS_UNUSED 0 /* unused */
+#define FS_SWAP 1 /* swap */
+#define FS_V6 2 /* Sixth Edition */
+#define FS_V7 3 /* Seventh Edition */
+#define FS_SYSV 4 /* System V */
+#define FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */
+#define FS_V8 6 /* Eighth Edition, 4K blocks */
+#define FS_BSDFFS 7 /* 4.2BSD fast file system */
+#define FS_MSDOS 8 /* MSDOS file system */
+#define FS_BSDLFS 9 /* 4.4BSD log-structured file system */
+#define FS_OTHER 10 /* in use, but unknown/unsupported */
+#define FS_HPFS 11 /* OS/2 high-performance file system */
+#define FS_ISO9660 12 /* ISO 9660, normally CD-ROM */
+#define FS_BOOT 13 /* partition contains bootstrap */
+
+#ifdef DKTYPENAMES
+static char *fstypenames[] = {
+ "unused",
+ "swap",
+ "Version 6",
+ "Version 7",
+ "System V",
+ "4.1BSD",
+ "Eighth Edition",
+ "4.2BSD",
+ "MSDOS",
+ "4.4LFS",
+ "unknown",
+ "HPFS",
+ "ISO9660",
+ "boot",
+ 0
+};
+#define FSMAXTYPES (sizeof(fstypenames) / sizeof(fstypenames[0]) - 1)
+#endif
+
+/*
+ * flags shared by various drives:
+ */
+#define D_REMOVABLE 0x01 /* removable media */
+#define D_ECC 0x02 /* supports ECC */
+#define D_BADSECT 0x04 /* supports bad sector forw. */
+#define D_RAMDISK 0x08 /* disk emulator */
+#define D_CHAIN 0x10 /* can do back-back transfers */
+
+/*
+ * Drive data for SMD.
+ */
+#define d_smdflags d_drivedata[0]
+#define D_SSE 0x1 /* supports skip sectoring */
+#define d_mindist d_drivedata[1]
+#define d_maxdist d_drivedata[2]
+#define d_sdist d_drivedata[3]
+
+/*
+ * Drive data for ST506.
+ */
+#define d_precompcyl d_drivedata[0]
+#define d_gap3 d_drivedata[1] /* used only when formatting */
+
+/*
+ * Drive data for SCSI.
+ */
+#define d_blind d_drivedata[0]
+
+#ifndef LOCORE
+/*
+ * Structure used to perform a format
+ * or other raw operation, returning data
+ * and/or register values.
+ * Register identification and format
+ * are device- and driver-dependent.
+ */
+struct format_op {
+ char *df_buf;
+ int df_count; /* value-result */
+ daddr_t df_startblk;
+ int df_reg[8]; /* result */
+};
+
+/*
+ * Structure used internally to retrieve
+ * information about a partition on a disk.
+ */
+struct partinfo {
+ struct disklabel *disklab;
+ struct partition *part;
+};
+
+/* DOS partition table -- located in boot block */
+
+#define DOSBBSECTOR 0 /* DOS boot block relative sector number */
+#define DOSPARTOFF 446
+#define NDOSPART 4
+
+struct dos_partition {
+ unsigned char dp_flag; /* bootstrap flags */
+ unsigned char dp_shd; /* starting head */
+ unsigned char dp_ssect; /* starting sector */
+ unsigned char dp_scyl; /* starting cylinder */
+ unsigned char dp_typ; /* partition type */
+#define DOSPTYP_386BSD 0xa5 /* 386BSD partition type */
+ unsigned char dp_ehd; /* end head */
+ unsigned char dp_esect; /* end sector */
+ unsigned char dp_ecyl; /* end cylinder */
+ unsigned long dp_start; /* absolute starting sector number */
+ unsigned long dp_size; /* partition size in sectors */
+};
+
+extern struct dos_partition dos_partitions[NDOSPART];
+
+#define DPSECT(s) ((s) & 0x3f) /* isolate relevant bits of sector */
+#define DPCYL(c, s) ((c) + (((s) & 0xc0)<<2)) /* and those that are cylinder */
+
+/*
+ * Disk-specific ioctls.
+ */
+ /* get and set disklabel; DIOCGPART used internally */
+#define DIOCGDINFO _IOR('d', 101, struct disklabel)/* get */
+#define DIOCSDINFO _IOW('d', 102, struct disklabel)/* set */
+#define DIOCWDINFO _IOW('d', 103, struct disklabel)/* set, update disk */
+#define DIOCGPART _IOW('d', 104, struct partinfo) /* get partition */
+
+/* do format operation, read or write */
+#define DIOCRFORMAT _IOWR('d', 105, struct format_op)
+#define DIOCWFORMAT _IOWR('d', 106, struct format_op)
+
+#define DIOCSSTEP _IOW('d', 107, int) /* set step rate */
+#define DIOCSRETRIES _IOW('d', 108, int) /* set # of retries */
+#define DIOCWLABEL _IOW('d', 109, int) /* write en/disable label */
+
+#define DIOCSBAD _IOW('d', 110, struct dkbad) /* set kernel dkbad */
+
+#endif /* LOCORE */
+
+#if !defined(KERNEL) && !defined(LOCORE)
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+struct disklabel *getdiskbyname __P((const char *));
+__END_DECLS
+
+#endif
diff --git a/sys/sys/diskpc98.h b/sys/sys/diskpc98.h
new file mode 100644
index 0000000..60b8f8f
--- /dev/null
+++ b/sys/sys/diskpc98.h
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 1987, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)disklabel.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * Disk description table, see disktab(5)
+ */
+#define _PATH_DISKTAB "/etc/disktab"
+#define DISKTAB "/etc/disktab" /* deprecated */
+
+/*
+ * Each disk has a label which includes information about the hardware
+ * disk geometry, filesystem partitions, and drive specific information.
+ * The label is in block 0 or 1, possibly offset from the beginning
+ * to leave room for a bootstrap, etc.
+ */
+
+/* XXX these should be defined per controller (or drive) elsewhere, not here! */
+#ifdef i386
+#define LABELSECTOR 1 /* sector containing label */
+#define LABELOFFSET 0 /* offset of label in sector */
+#endif
+
+#ifndef LABELSECTOR
+#define LABELSECTOR 0 /* sector containing label */
+#endif
+
+#ifndef LABELOFFSET
+#define LABELOFFSET 64 /* offset of label in sector */
+#endif
+
+#define DISKMAGIC ((u_long) 0x82564557) /* The disk magic number */
+#ifndef MAXPARTITIONS
+#define MAXPARTITIONS 8
+#endif
+
+
+#ifndef LOCORE
+struct disklabel {
+ u_long d_magic; /* the magic number */
+ short d_type; /* drive type */
+ short d_subtype; /* controller/d_type specific */
+ char d_typename[16]; /* type name, e.g. "eagle" */
+ /*
+ * d_packname contains the pack identifier and is returned when
+ * the disklabel is read off the disk or in-core copy.
+ * d_boot0 and d_boot1 are the (optional) names of the
+ * primary (block 0) and secondary (block 1-15) bootstraps
+ * as found in /usr/mdec. These are returned when using
+ * getdiskbyname(3) to retrieve the values from /etc/disktab.
+ */
+#if defined(KERNEL) || defined(STANDALONE)
+ char d_packname[16]; /* pack identifier */
+#else
+ union {
+ char un_d_packname[16]; /* pack identifier */
+ struct {
+ char *un_d_boot0; /* primary bootstrap name */
+ char *un_d_boot1; /* secondary bootstrap name */
+ } un_b;
+ } d_un;
+#define d_packname d_un.un_d_packname
+#define d_boot0 d_un.un_b.un_d_boot0
+#define d_boot1 d_un.un_b.un_d_boot1
+#endif /* ! KERNEL or STANDALONE */
+ /* disk geometry: */
+ u_long d_secsize; /* # of bytes per sector */
+ u_long d_nsectors; /* # of data sectors per track */
+ u_long d_ntracks; /* # of tracks per cylinder */
+ u_long d_ncylinders; /* # of data cylinders per unit */
+ u_long d_secpercyl; /* # of data sectors per cylinder */
+ u_long d_secperunit; /* # of data sectors per unit */
+ /*
+ * Spares (bad sector replacements) below
+ * are not counted in d_nsectors or d_secpercyl.
+ * Spare sectors are assumed to be physical sectors
+ * which occupy space at the end of each track and/or cylinder.
+ */
+ u_short d_sparespertrack; /* # of spare sectors per track */
+ u_short d_sparespercyl; /* # of spare sectors per cylinder */
+ /*
+ * Alternate cylinders include maintenance, replacement,
+ * configuration description areas, etc.
+ */
+ u_long d_acylinders; /* # of alt. cylinders per unit */
+
+ /* hardware characteristics: */
+ /*
+ * d_interleave, d_trackskew and d_cylskew describe perturbations
+ * in the media format used to compensate for a slow controller.
+ * Interleave is physical sector interleave, set up by the formatter
+ * or controller when formatting. When interleaving is in use,
+ * logically adjacent sectors are not physically contiguous,
+ * but instead are separated by some number of sectors.
+ * It is specified as the ratio of physical sectors traversed
+ * per logical sector. Thus an interleave of 1:1 implies contiguous
+ * layout, while 2:1 implies that logical sector 0 is separated
+ * by one sector from logical sector 1.
+ * d_trackskew is the offset of sector 0 on track N
+ * relative to sector 0 on track N-1 on the same cylinder.
+ * Finally, d_cylskew is the offset of sector 0 on cylinder N
+ * relative to sector 0 on cylinder N-1.
+ */
+ u_short d_rpm; /* rotational speed */
+ u_short d_interleave; /* hardware sector interleave */
+ u_short d_trackskew; /* sector 0 skew, per track */
+ u_short d_cylskew; /* sector 0 skew, per cylinder */
+ u_long d_headswitch; /* head switch time, usec */
+ u_long d_trkseek; /* track-to-track seek, usec */
+ u_long d_flags; /* generic flags */
+#define NDDATA 5
+ u_long d_drivedata[NDDATA]; /* drive-type specific information */
+#define NSPARE 5
+ u_long d_spare[NSPARE]; /* reserved for future use */
+ u_long d_magic2; /* the magic number (again) */
+ u_short d_checksum; /* xor of data incl. partitions */
+
+ /* filesystem and partition information: */
+ u_short d_npartitions; /* number of partitions in following */
+ u_long d_bbsize; /* size of boot area at sn0, bytes */
+ u_long d_sbsize; /* max size of fs superblock, bytes */
+ struct partition { /* the partition table */
+ u_long p_size; /* number of sectors in partition */
+ u_long p_offset; /* starting sector */
+ u_long p_fsize; /* filesystem basic fragment size */
+ u_char p_fstype; /* filesystem type, see below */
+ u_char p_frag; /* filesystem fragments per block */
+ union {
+ u_short cpg; /* UFS: FS cylinders per group */
+ u_short sgs; /* LFS: FS segment shift */
+ } __partition_u1;
+#define p_cpg __partition_u1.cpg
+#define p_sgs __partition_u1.sgs
+ } d_partitions[MAXPARTITIONS]; /* actually may be more */
+};
+struct cpu_disklabel {
+};
+#else /* LOCORE */
+ /*
+ * offsets for asm boot files.
+ */
+ .set d_secsize,40
+ .set d_nsectors,44
+ .set d_ntracks,48
+ .set d_ncylinders,52
+ .set d_secpercyl,56
+ .set d_secperunit,60
+ .set d_end_,276 /* size of disk label */
+#endif /* LOCORE */
+
+/* d_type values: */
+#define DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */
+#define DTYPE_MSCP 2 /* MSCP */
+#define DTYPE_DEC 3 /* other DEC (rk, rl) */
+#define DTYPE_SCSI 4 /* SCSI */
+#define DTYPE_ESDI 5 /* ESDI interface */
+#define DTYPE_ST506 6 /* ST506 etc. */
+#define DTYPE_HPIB 7 /* CS/80 on HP-IB */
+#define DTYPE_HPFL 8 /* HP Fiber-link */
+#define DTYPE_FLOPPY 10 /* floppy */
+
+/* d_subtype values: */
+#define DSTYPE_INDOSPART 0x8 /* is inside dos partition */
+#define DSTYPE_DOSPART(s) ((s) & 3) /* dos partition number */
+#define DSTYPE_GEOMETRY 0x10 /* drive params in label */
+
+#ifdef DKTYPENAMES
+static char *dktypenames[] = {
+ "unknown",
+ "SMD",
+ "MSCP",
+ "old DEC",
+ "SCSI",
+ "ESDI",
+ "ST506",
+ "HP-IB",
+ "HP-FL",
+ "type 9",
+ "floppy",
+ 0
+};
+#define DKMAXTYPES (sizeof(dktypenames) / sizeof(dktypenames[0]) - 1)
+#endif
+
+/*
+ * Filesystem type and version.
+ * Used to interpret other filesystem-specific
+ * per-partition information.
+ */
+#define FS_UNUSED 0 /* unused */
+#define FS_SWAP 1 /* swap */
+#define FS_V6 2 /* Sixth Edition */
+#define FS_V7 3 /* Seventh Edition */
+#define FS_SYSV 4 /* System V */
+#define FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */
+#define FS_V8 6 /* Eighth Edition, 4K blocks */
+#define FS_BSDFFS 7 /* 4.2BSD fast file system */
+#define FS_MSDOS 8 /* MSDOS file system */
+#define FS_BSDLFS 9 /* 4.4BSD log-structured file system */
+#define FS_OTHER 10 /* in use, but unknown/unsupported */
+#define FS_HPFS 11 /* OS/2 high-performance file system */
+#define FS_ISO9660 12 /* ISO 9660, normally CD-ROM */
+#define FS_BOOT 13 /* partition contains bootstrap */
+
+#ifdef DKTYPENAMES
+static char *fstypenames[] = {
+ "unused",
+ "swap",
+ "Version 6",
+ "Version 7",
+ "System V",
+ "4.1BSD",
+ "Eighth Edition",
+ "4.2BSD",
+ "MSDOS",
+ "4.4LFS",
+ "unknown",
+ "HPFS",
+ "ISO9660",
+ "boot",
+ 0
+};
+#define FSMAXTYPES (sizeof(fstypenames) / sizeof(fstypenames[0]) - 1)
+#endif
+
+/*
+ * flags shared by various drives:
+ */
+#define D_REMOVABLE 0x01 /* removable media */
+#define D_ECC 0x02 /* supports ECC */
+#define D_BADSECT 0x04 /* supports bad sector forw. */
+#define D_RAMDISK 0x08 /* disk emulator */
+#define D_CHAIN 0x10 /* can do back-back transfers */
+
+/*
+ * Drive data for SMD.
+ */
+#define d_smdflags d_drivedata[0]
+#define D_SSE 0x1 /* supports skip sectoring */
+#define d_mindist d_drivedata[1]
+#define d_maxdist d_drivedata[2]
+#define d_sdist d_drivedata[3]
+
+/*
+ * Drive data for ST506.
+ */
+#define d_precompcyl d_drivedata[0]
+#define d_gap3 d_drivedata[1] /* used only when formatting */
+
+/*
+ * Drive data for SCSI.
+ */
+#define d_blind d_drivedata[0]
+
+#ifndef LOCORE
+/*
+ * Structure used to perform a format
+ * or other raw operation, returning data
+ * and/or register values.
+ * Register identification and format
+ * are device- and driver-dependent.
+ */
+struct format_op {
+ char *df_buf;
+ int df_count; /* value-result */
+ daddr_t df_startblk;
+ int df_reg[8]; /* result */
+};
+
+/*
+ * Structure used internally to retrieve
+ * information about a partition on a disk.
+ */
+struct partinfo {
+ struct disklabel *disklab;
+ struct partition *part;
+};
+
+/* DOS partition table -- located in boot block */
+
+#define DOSBBSECTOR 0 /* DOS boot block relative sector number */
+#define DOSPARTOFF 446
+#define NDOSPART 4
+
+struct dos_partition {
+ unsigned char dp_flag; /* bootstrap flags */
+ unsigned char dp_shd; /* starting head */
+ unsigned char dp_ssect; /* starting sector */
+ unsigned char dp_scyl; /* starting cylinder */
+ unsigned char dp_typ; /* partition type */
+#define DOSPTYP_386BSD 0xa5 /* 386BSD partition type */
+ unsigned char dp_ehd; /* end head */
+ unsigned char dp_esect; /* end sector */
+ unsigned char dp_ecyl; /* end cylinder */
+ unsigned long dp_start; /* absolute starting sector number */
+ unsigned long dp_size; /* partition size in sectors */
+};
+
+extern struct dos_partition dos_partitions[NDOSPART];
+
+#define DPSECT(s) ((s) & 0x3f) /* isolate relevant bits of sector */
+#define DPCYL(c, s) ((c) + (((s) & 0xc0)<<2)) /* and those that are cylinder */
+
+/*
+ * Disk-specific ioctls.
+ */
+ /* get and set disklabel; DIOCGPART used internally */
+#define DIOCGDINFO _IOR('d', 101, struct disklabel)/* get */
+#define DIOCSDINFO _IOW('d', 102, struct disklabel)/* set */
+#define DIOCWDINFO _IOW('d', 103, struct disklabel)/* set, update disk */
+#define DIOCGPART _IOW('d', 104, struct partinfo) /* get partition */
+
+/* do format operation, read or write */
+#define DIOCRFORMAT _IOWR('d', 105, struct format_op)
+#define DIOCWFORMAT _IOWR('d', 106, struct format_op)
+
+#define DIOCSSTEP _IOW('d', 107, int) /* set step rate */
+#define DIOCSRETRIES _IOW('d', 108, int) /* set # of retries */
+#define DIOCWLABEL _IOW('d', 109, int) /* write en/disable label */
+
+#define DIOCSBAD _IOW('d', 110, struct dkbad) /* set kernel dkbad */
+
+#endif /* LOCORE */
+
+#if !defined(KERNEL) && !defined(LOCORE)
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+struct disklabel *getdiskbyname __P((const char *));
+__END_DECLS
+
+#endif
diff --git a/sys/sys/dkbad.h b/sys/sys/dkbad.h
new file mode 100644
index 0000000..c574000
--- /dev/null
+++ b/sys/sys/dkbad.h
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dkbad.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * Definitions needed to perform bad sector revectoring ala DEC STD 144.
+ *
+ * The bad sector information is located in the first 5 even numbered
+ * sectors of the last track of the disk pack. There are five identical
+ * copies of the information, described by the dkbad structure.
+ *
+ * Replacement sectors are allocated starting with the first sector before
+ * the bad sector information and working backwards towards the beginning of
+ * the disk. A maximum of 126 bad sectors are supported. The position of
+ * the bad sector in the bad sector table determines which replacement sector
+ * it corresponds to.
+ *
+ * The bad sector information and replacement sectors are conventionally
+ * only accessible through the 'c' file system partition of the disk. If
+ * that partition is used for a file system, the user is responsible for
+ * making sure that it does not overlap the bad sector information or any
+ * replacement sectors.
+ */
+struct dkbad {
+ long bt_csn; /* cartridge serial number */
+ u_short bt_mbz; /* unused; should be 0 */
+ u_short bt_flag; /* -1 => alignment cartridge */
+ struct bt_bad {
+ u_short bt_cyl; /* cylinder number of bad sector */
+ u_short bt_trksec; /* track and sector number */
+ } bt_bad[126];
+};
+
+#define ECC 0
+#define SSE 1
+#define BSE 2
+#define CONT 3
diff --git a/sys/sys/dkstat.h b/sys/sys/dkstat.h
new file mode 100644
index 0000000..f7f5f15
--- /dev/null
+++ b/sys/sys/dkstat.h
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dkstat.h 8.2 (Berkeley) 1/21/94
+ */
+
+#define CP_USER 0
+#define CP_NICE 1
+#define CP_SYS 2
+#define CP_INTR 3
+#define CP_IDLE 4
+#define CPUSTATES 5
+
+#define DK_NDRIVE 8
+#ifdef KERNEL
+long cp_time[CPUSTATES];
+long dk_seek[DK_NDRIVE];
+long dk_time[DK_NDRIVE];
+long dk_wds[DK_NDRIVE];
+long dk_wpms[DK_NDRIVE];
+long dk_xfer[DK_NDRIVE];
+
+int dk_busy;
+int dk_ndrive;
+
+long tk_cancc;
+long tk_nin;
+long tk_nout;
+long tk_rawcc;
+#endif
diff --git a/sys/sys/dmap.h b/sys/sys/dmap.h
new file mode 100644
index 0000000..2a6f538
--- /dev/null
+++ b/sys/sys/dmap.h
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dmap.h 8.2 (Berkeley) 1/4/94
+ */
+
+#ifndef _SYS_DMAP_H_
+#define _SYS_DMAP_H_
+
+/*
+ * Definitions for the mapping of vitual swap space to the physical swap
+ * area - the disk map.
+ */
+#define NDMAP 38 /* size of the swap area map */
+
+struct dmap {
+ swblk_t dm_size; /* current size used by process */
+ swblk_t dm_alloc; /* amount of physical swap space allocated */
+ swblk_t dm_map[NDMAP]; /* first disk block number in each chunk */
+};
+#ifdef KERNEL
+struct dmap zdmap;
+int dmmin, dmmax, dmtext;
+#endif
+
+/* The following structure is that ``returned'' from a call to vstodb(). */
+struct dblock {
+ swblk_t db_base; /* base of physical contig drum block */
+ swblk_t db_size; /* size of block */
+};
+#endif /* !_SYS_DMAP_H_ */
diff --git a/sys/sys/domain.h b/sys/sys/domain.h
new file mode 100644
index 0000000..b056347
--- /dev/null
+++ b/sys/sys/domain.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)domain.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * Structure per communications domain.
+ */
+
+/*
+ * Forward structure declarations for function prototypes [sic].
+ */
+struct mbuf;
+
+struct domain {
+ int dom_family; /* AF_xxx */
+ char *dom_name;
+ void (*dom_init) /* initialize domain data structures */
+ __P((void));
+ int (*dom_externalize) /* externalize access rights */
+ __P((struct mbuf *));
+ int (*dom_dispose) /* dispose of internalized rights */
+ __P((struct mbuf *));
+ struct protosw *dom_protosw, *dom_protoswNPROTOSW;
+ struct domain *dom_next;
+ int (*dom_rtattach) /* initialize routing table */
+ __P((void **, int));
+ int dom_rtoffset; /* an arg to rtattach, in bits */
+ int dom_maxrtkey; /* for routing layer */
+};
+
+#ifdef KERNEL
+struct domain *domains;
+#endif
diff --git a/sys/sys/errno.h b/sys/sys/errno.h
new file mode 100644
index 0000000..a4e4ea6
--- /dev/null
+++ b/sys/sys/errno.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)errno.h 8.5 (Berkeley) 1/21/94
+ */
+
+#ifndef KERNEL
+extern int errno; /* global error number */
+#endif
+
+#define EPERM 1 /* Operation not permitted */
+#define ENOENT 2 /* No such file or directory */
+#define ESRCH 3 /* No such process */
+#define EINTR 4 /* Interrupted system call */
+#define EIO 5 /* Input/output error */
+#define ENXIO 6 /* Device not configured */
+#define E2BIG 7 /* Argument list too long */
+#define ENOEXEC 8 /* Exec format error */
+#define EBADF 9 /* Bad file descriptor */
+#define ECHILD 10 /* No child processes */
+#define EDEADLK 11 /* Resource deadlock avoided */
+ /* 11 was EAGAIN */
+#define ENOMEM 12 /* Cannot allocate memory */
+#define EACCES 13 /* Permission denied */
+#define EFAULT 14 /* Bad address */
+#ifndef _POSIX_SOURCE
+#define ENOTBLK 15 /* Block device required */
+#endif
+#define EBUSY 16 /* Device busy */
+#define EEXIST 17 /* File exists */
+#define EXDEV 18 /* Cross-device link */
+#define ENODEV 19 /* Operation not supported by device */
+#define ENOTDIR 20 /* Not a directory */
+#define EISDIR 21 /* Is a directory */
+#define EINVAL 22 /* Invalid argument */
+#define ENFILE 23 /* Too many open files in system */
+#define EMFILE 24 /* Too many open files */
+#define ENOTTY 25 /* Inappropriate ioctl for device */
+#ifndef _POSIX_SOURCE
+#define ETXTBSY 26 /* Text file busy */
+#endif
+#define EFBIG 27 /* File too large */
+#define ENOSPC 28 /* No space left on device */
+#define ESPIPE 29 /* Illegal seek */
+#define EROFS 30 /* Read-only file system */
+#define EMLINK 31 /* Too many links */
+#define EPIPE 32 /* Broken pipe */
+
+/* math software */
+#define EDOM 33 /* Numerical argument out of domain */
+#define ERANGE 34 /* Result too large */
+
+/* non-blocking and interrupt i/o */
+#define EAGAIN 35 /* Resource temporarily unavailable */
+#ifndef _POSIX_SOURCE
+#define EWOULDBLOCK EAGAIN /* Operation would block */
+#define EINPROGRESS 36 /* Operation now in progress */
+#define EALREADY 37 /* Operation already in progress */
+
+/* ipc/network software -- argument errors */
+#define ENOTSOCK 38 /* Socket operation on non-socket */
+#define EDESTADDRREQ 39 /* Destination address required */
+#define EMSGSIZE 40 /* Message too long */
+#define EPROTOTYPE 41 /* Protocol wrong type for socket */
+#define ENOPROTOOPT 42 /* Protocol not available */
+#define EPROTONOSUPPORT 43 /* Protocol not supported */
+#define ESOCKTNOSUPPORT 44 /* Socket type not supported */
+#define EOPNOTSUPP 45 /* Operation not supported */
+#define EPFNOSUPPORT 46 /* Protocol family not supported */
+#define EAFNOSUPPORT 47 /* Address family not supported by protocol family */
+#define EADDRINUSE 48 /* Address already in use */
+#define EADDRNOTAVAIL 49 /* Can't assign requested address */
+
+/* ipc/network software -- operational errors */
+#define ENETDOWN 50 /* Network is down */
+#define ENETUNREACH 51 /* Network is unreachable */
+#define ENETRESET 52 /* Network dropped connection on reset */
+#define ECONNABORTED 53 /* Software caused connection abort */
+#define ECONNRESET 54 /* Connection reset by peer */
+#define ENOBUFS 55 /* No buffer space available */
+#define EISCONN 56 /* Socket is already connected */
+#define ENOTCONN 57 /* Socket is not connected */
+#define ESHUTDOWN 58 /* Can't send after socket shutdown */
+#define ETOOMANYREFS 59 /* Too many references: can't splice */
+#define ETIMEDOUT 60 /* Operation timed out */
+#define ECONNREFUSED 61 /* Connection refused */
+
+#define ELOOP 62 /* Too many levels of symbolic links */
+#endif /* _POSIX_SOURCE */
+#define ENAMETOOLONG 63 /* File name too long */
+
+/* should be rearranged */
+#ifndef _POSIX_SOURCE
+#define EHOSTDOWN 64 /* Host is down */
+#define EHOSTUNREACH 65 /* No route to host */
+#endif /* _POSIX_SOURCE */
+#define ENOTEMPTY 66 /* Directory not empty */
+
+/* quotas & mush */
+#ifndef _POSIX_SOURCE
+#define EPROCLIM 67 /* Too many processes */
+#define EUSERS 68 /* Too many users */
+#define EDQUOT 69 /* Disc quota exceeded */
+
+/* Network File System */
+#define ESTALE 70 /* Stale NFS file handle */
+#define EREMOTE 71 /* Too many levels of remote in path */
+#define EBADRPC 72 /* RPC struct is bad */
+#define ERPCMISMATCH 73 /* RPC version wrong */
+#define EPROGUNAVAIL 74 /* RPC prog. not avail */
+#define EPROGMISMATCH 75 /* Program version wrong */
+#define EPROCUNAVAIL 76 /* Bad procedure for program */
+#endif /* _POSIX_SOURCE */
+
+#define ENOLCK 77 /* No locks available */
+#define ENOSYS 78 /* Function not implemented */
+
+#ifndef _POSIX_SOURCE
+#define EFTYPE 79 /* Inappropriate file type or format */
+#define EAUTH 80 /* Authentication error */
+#define ENEEDAUTH 81 /* Need authenticator */
+#define ELAST 81 /* Must be equal largest errno */
+#endif /* _POSIX_SOURCE */
+
+#ifdef KERNEL
+/* pseudo-errors returned inside kernel to modify return to process */
+#define ERESTART -1 /* restart syscall */
+#define EJUSTRETURN -2 /* don't modify regs, just return */
+#endif
diff --git a/sys/sys/exec.h b/sys/sys/exec.h
new file mode 100644
index 0000000..be8cd71
--- /dev/null
+++ b/sys/sys/exec.h
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)exec.h 8.3 (Berkeley) 1/21/94
+ */
+
+#include <machine/exec.h>
+
+/*
+ * The following structure is found at the top of the user stack of each
+ * user process. The ps program uses it to locate argv and environment
+ * strings. Programs that wish ps to display other information may modify
+ * it; normally ps_argvstr points to the text for argv[0], and ps_nargvstr
+ * is the same as the program's argc. The fields ps_envstr and ps_nenvstr
+ * are the equivalent for the environment.
+ */
+struct ps_strings {
+ char *ps_argvstr; /* first of 0 or more argument strings */
+ int ps_nargvstr; /* the number of argument strings */
+ char *ps_envstr; /* first of 0 or more environment strings */
+ int ps_nenvstr; /* the number of environment strings */
+};
+
+/*
+ * Address of ps_strings structure (in user space).
+ */
+#define PS_STRINGS \
+ ((struct ps_strings *)(USRSTACK - sizeof(struct ps_strings)))
+
+/*
+ * Arguments to the exec system call.
+ */
+struct execve_args {
+ char *fname;
+ char **argv;
+ char **envv;
+};
diff --git a/sys/sys/fbio.h b/sys/sys/fbio.h
new file mode 100644
index 0000000..63371b7
--- /dev/null
+++ b/sys/sys/fbio.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software developed by the Computer Systems
+ * Engineering group at Lawrence Berkeley Laboratory under DARPA
+ * contract BG 91-66 and contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fbio.h 8.2 (Berkeley) 10/30/93
+ *
+ * from: $Header: fbio.h,v 1.6 93/10/31 06:01:56 torek Exp $ (LBL)
+ */
+
+/*
+ * Frame buffer ioctls (from Sprite, trimmed to essentials for X11).
+ */
+
+/*
+ * Frame buffer type codes.
+ */
+#define FBTYPE_SUN1BW 0 /* multibus mono */
+#define FBTYPE_SUN1COLOR 1 /* multibus color */
+#define FBTYPE_SUN2BW 2 /* memory mono */
+#define FBTYPE_SUN2COLOR 3 /* color w/rasterop chips */
+#define FBTYPE_SUN2GP 4 /* GP1/GP2 */
+#define FBTYPE_SUN5COLOR 5 /* RoadRunner accelerator */
+#define FBTYPE_SUN3COLOR 6 /* memory color */
+#define FBTYPE_MEMCOLOR 7 /* memory 24-bit */
+#define FBTYPE_SUN4COLOR 8 /* memory color w/overlay */
+
+#define FBTYPE_NOTSUN1 9 /* reserved for customer */
+#define FBTYPE_NOTSUN2 10 /* reserved for customer */
+#define FBTYPE_NOTSUN3 11 /* reserved for customer */
+
+#define FBTYPE_SUNFAST_COLOR 12 /* accelerated 8bit */
+#define FBTYPE_SUNROP_COLOR 13 /* MEMCOLOR with rop h/w */
+#define FBTYPE_SUNFB_VIDEO 14 /* Simple video mixing */
+#define FBTYPE_RESERVED5 15 /* reserved, do not use */
+#define FBTYPE_RESERVED4 16 /* reserved, do not use */
+#define FBTYPE_RESERVED3 17 /* reserved, do not use */
+#define FBTYPE_RESERVED2 18 /* reserved, do not use */
+#define FBTYPE_RESERVED1 19 /* reserved, do not use */
+
+#define FBTYPE_LASTPLUSONE 20 /* max number of fbs (change as add) */
+
+/*
+ * Frame buffer descriptor as returned by FBIOGTYPE.
+ */
+struct fbtype {
+ int fb_type; /* as defined above */
+ int fb_height; /* in pixels */
+ int fb_width; /* in pixels */
+ int fb_depth; /* bits per pixel */
+ int fb_cmsize; /* size of color map (entries) */
+ int fb_size; /* total size in bytes */
+};
+#define FBIOGTYPE _IOR('F', 0, struct fbtype)
+
+#ifdef notdef
+/*
+ * General purpose structure for passing info in and out of frame buffers
+ * (used for gp1) -- unsupported.
+ */
+struct fbinfo {
+ int fb_physaddr; /* physical frame buffer address */
+ int fb_hwwidth; /* fb board width */
+ int fb_hwheight; /* fb board height */
+ int fb_addrdelta; /* phys addr diff between boards */
+ u_char *fb_ropaddr; /* fb virtual addr */
+ int fb_unit; /* minor devnum of fb */
+};
+#define FBIOGINFO _IOR('F', 2, struct fbinfo)
+#endif
+
+/*
+ * Color map I/O.
+ */
+struct fbcmap {
+ int index; /* first element (0 origin) */
+ int count; /* number of elements */
+ u_char *red; /* red color map elements */
+ u_char *green; /* green color map elements */
+ u_char *blue; /* blue color map elements */
+};
+#define FBIOPUTCMAP _IOW('F', 3, struct fbcmap)
+#define FBIOGETCMAP _IOW('F', 4, struct fbcmap)
+
+/*
+ * Set/get attributes.
+ */
+#define FB_ATTR_NDEVSPECIFIC 8 /* no. of device specific values */
+#define FB_ATTR_NEMUTYPES 4 /* no. of emulation types */
+
+struct fbsattr {
+ int flags; /* flags; see below */
+ int emu_type; /* emulation type (-1 if unused) */
+ int dev_specific[FB_ATTR_NDEVSPECIFIC]; /* catchall */
+};
+#define FB_ATTR_AUTOINIT 1 /* emulation auto init flag */
+#define FB_ATTR_DEVSPECIFIC 2 /* dev. specific stuff valid flag */
+
+struct fbgattr {
+ int real_type; /* real device type */
+ int owner; /* PID of owner, 0 if myself */
+ struct fbtype fbtype; /* fbtype info for real device */
+ struct fbsattr sattr; /* see above */
+ int emu_types[FB_ATTR_NEMUTYPES]; /* possible emulations */
+ /* (-1 if unused) */
+};
+/* FBIOSATTR _IOW('F', 5, struct fbsattr) -- unsupported */
+#define FBIOGATTR _IOR('F', 6, struct fbgattr)
+
+/*
+ * Video control.
+ */
+#define FBVIDEO_OFF 0
+#define FBVIDEO_ON 1
+
+#define FBIOSVIDEO _IOW('F', 7, int)
+#define FBIOGVIDEO _IOR('F', 8, int)
+
+/*
+ * Hardware cursor control (for, e.g., CG6). A rather complex and icky
+ * interface that smells like VMS, but there it is....
+ */
+struct fbcurpos {
+ short x;
+ short y;
+};
+
+struct fbcursor {
+ short set; /* flags; see below */
+ short enable; /* nonzero => cursor on, 0 => cursor off */
+ struct fbcurpos pos; /* position on display */
+ struct fbcurpos hot; /* hot-spot within cursor */
+ struct fbcmap cmap; /* cursor color map */
+ struct fbcurpos size; /* number of valid bits in image & mask */
+ caddr_t image; /* cursor image bits */
+ caddr_t mask; /* cursor mask bits */
+};
+#define FB_CUR_SETCUR 0x01 /* set on/off (i.e., obey fbcursor.enable) */
+#define FB_CUR_SETPOS 0x02 /* set position */
+#define FB_CUR_SETHOT 0x04 /* set hot-spot */
+#define FB_CUR_SETCMAP 0x08 /* set cursor color map */
+#define FB_CUR_SETSHAPE 0x10 /* set size & bits */
+#define FB_CUR_SETALL (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT | \
+ FB_CUR_SETCMAP | FB_CUR_SETSHAPE)
+
+/* controls for cursor attributes & shape (including position) */
+#define FBIOSCURSOR _IOW('F', 24, struct fbcursor)
+#define FBIOGCURSOR _IOWR('F', 25, struct fbcursor)
+
+/* controls for cursor position only */
+#define FBIOSCURPOS _IOW('F', 26, struct fbcurpos)
+#define FBIOGCURPOS _IOW('F', 27, struct fbcurpos)
+
+/* get maximum cursor size */
+#define FBIOGCURMAX _IOR('F', 28, struct fbcurpos)
diff --git a/sys/sys/fcntl.h b/sys/sys/fcntl.h
new file mode 100644
index 0000000..62762f3
--- /dev/null
+++ b/sys/sys/fcntl.h
@@ -0,0 +1,190 @@
+/*-
+ * Copyright (c) 1983, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fcntl.h 8.3 (Berkeley) 1/21/94
+ */
+
+#ifndef _SYS_FCNTL_H_
+#define _SYS_FCNTL_H_
+
+/*
+ * This file includes the definitions for open and fcntl
+ * described by POSIX for <fcntl.h>; it also includes
+ * related kernel definitions.
+ */
+
+#ifndef KERNEL
+#include <sys/types.h>
+#endif
+
+/*
+ * File status flags: these are used by open(2), fcntl(2).
+ * They are also used (indirectly) in the kernel file structure f_flags,
+ * which is a superset of the open/fcntl flags. Open flags and f_flags
+ * are inter-convertible using OFLAGS(fflags) and FFLAGS(oflags).
+ * Open/fcntl flags begin with O_; kernel-internal flags begin with F.
+ */
+/* open-only flags */
+#define O_RDONLY 0x0000 /* open for reading only */
+#define O_WRONLY 0x0001 /* open for writing only */
+#define O_RDWR 0x0002 /* open for reading and writing */
+#define O_ACCMODE 0x0003 /* mask for above modes */
+
+/*
+ * Kernel encoding of open mode; separate read and write bits that are
+ * independently testable: 1 greater than the above.
+ *
+ * XXX
+ * FREAD and FWRITE are excluded from the #ifdef KERNEL so that TIOCFLUSH,
+ * which was documented to use FREAD/FWRITE, continues to work.
+ */
+#ifndef _POSIX_SOURCE
+#define FREAD 0x0001
+#define FWRITE 0x0002
+#endif
+#define O_NONBLOCK 0x0004 /* no delay */
+#define O_APPEND 0x0008 /* set append mode */
+#ifndef _POSIX_SOURCE
+#define O_SHLOCK 0x0010 /* open with shared file lock */
+#define O_EXLOCK 0x0020 /* open with exclusive file lock */
+#define O_ASYNC 0x0040 /* signal pgrp when data ready */
+#define O_FSYNC 0x0080 /* synchronous writes */
+#endif
+#define O_CREAT 0x0200 /* create if nonexistant */
+#define O_TRUNC 0x0400 /* truncate to zero length */
+#define O_EXCL 0x0800 /* error if already exists */
+#ifdef KERNEL
+#define FMARK 0x1000 /* mark during gc() */
+#define FDEFER 0x2000 /* defer for next gc pass */
+#define FHASLOCK 0x4000 /* descriptor holds advisory lock */
+#endif
+
+/* defined by POSIX 1003.1; BSD default, so no bit required */
+#define O_NOCTTY 0 /* don't assign controlling terminal */
+
+#ifdef KERNEL
+/* convert from open() flags to/from fflags; convert O_RD/WR to FREAD/FWRITE */
+#define FFLAGS(oflags) ((oflags) + 1)
+#define OFLAGS(fflags) ((fflags) - 1)
+
+/* bits to save after open */
+#define FMASK (FREAD|FWRITE|FAPPEND|FASYNC|FFSYNC|FNONBLOCK)
+/* bits settable by fcntl(F_SETFL, ...) */
+#define FCNTLFLAGS (FAPPEND|FASYNC|FFSYNC|FNONBLOCK)
+#endif
+
+/*
+ * The O_* flags used to have only F* names, which were used in the kernel
+ * and by fcntl. We retain the F* names for the kernel f_flags field
+ * and for backward compatibility for fcntl.
+ */
+#ifndef _POSIX_SOURCE
+#define FAPPEND O_APPEND /* kernel/compat */
+#define FASYNC O_ASYNC /* kernel/compat */
+#define FFSYNC O_FSYNC /* kernel */
+#define FNONBLOCK O_NONBLOCK /* kernel */
+#define FNDELAY O_NONBLOCK /* compat */
+#define O_NDELAY O_NONBLOCK /* compat */
+#endif
+
+/*
+ * Constants used for fcntl(2)
+ */
+
+/* command values */
+#define F_DUPFD 0 /* duplicate file descriptor */
+#define F_GETFD 1 /* get file descriptor flags */
+#define F_SETFD 2 /* set file descriptor flags */
+#define F_GETFL 3 /* get file status flags */
+#define F_SETFL 4 /* set file status flags */
+#ifndef _POSIX_SOURCE
+#define F_GETOWN 5 /* get SIGIO/SIGURG proc/pgrp */
+#define F_SETOWN 6 /* set SIGIO/SIGURG proc/pgrp */
+#endif
+#define F_GETLK 7 /* get record locking information */
+#define F_SETLK 8 /* set record locking information */
+#define F_SETLKW 9 /* F_SETLK; wait if blocked */
+
+/* file descriptor flags (F_GETFD, F_SETFD) */
+#define FD_CLOEXEC 1 /* close-on-exec flag */
+
+/* record locking flags (F_GETLK, F_SETLK, F_SETLKW) */
+#define F_RDLCK 1 /* shared or read lock */
+#define F_UNLCK 2 /* unlock */
+#define F_WRLCK 3 /* exclusive or write lock */
+#ifdef KERNEL
+#define F_WAIT 0x010 /* Wait until lock is granted */
+#define F_FLOCK 0x020 /* Use flock(2) semantics for lock */
+#define F_POSIX 0x040 /* Use POSIX semantics for lock */
+#endif
+
+/*
+ * Advisory file segment locking data type -
+ * information passed to system by user
+ */
+struct flock {
+ off_t l_start; /* starting offset */
+ off_t l_len; /* len = 0 means until end of file */
+ pid_t l_pid; /* lock owner */
+ short l_type; /* lock type: read/write, etc. */
+ short l_whence; /* type of l_start */
+};
+
+
+#ifndef _POSIX_SOURCE
+/* lock operations for flock(2) */
+#define LOCK_SH 0x01 /* shared file lock */
+#define LOCK_EX 0x02 /* exclusive file lock */
+#define LOCK_NB 0x04 /* don't block when locking */
+#define LOCK_UN 0x08 /* unlock file */
+#endif
+
+
+#ifndef KERNEL
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int open __P((const char *, int, ...));
+int creat __P((const char *, mode_t));
+int fcntl __P((int, int, ...));
+#ifndef _POSIX_SOURCE
+int flock __P((int, int));
+#endif /* !_POSIX_SOURCE */
+__END_DECLS
+#endif
+
+#endif /* !_SYS_FCNTL_H_ */
diff --git a/sys/sys/fdcio.h b/sys/sys/fdcio.h
new file mode 100644
index 0000000..2e3ac31
--- /dev/null
+++ b/sys/sys/fdcio.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 1992-1993 by Joerg Wunsch, Dresden
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _IOCTL_FD_H
+#define _IOCTL_FD_H
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#define FD_FORMAT_VERSION 110 /* used to validate before formatting */
+#define FD_MAX_NSEC 36 /* highest known number of spt - allow for */
+ /* 2.88 MB drives */
+
+struct fd_formb {
+ int format_version; /* == FD_FORMAT_VERSION */
+ int cyl, head;
+ int transfer_rate; /* fdreg.h: FDC_???KBPS */
+
+ union {
+ struct fd_form_data {
+ /*
+ * DO NOT CHANGE THE LAYOUT OF THIS STRUCTS
+ * it is hardware-dependant since it exactly
+ * matches the byte sequence to write to FDC
+ * during its `format track' operation
+ */
+ u_char secshift; /* 0 -> 128, ...; usually 2 -> 512 */
+ u_char nsecs; /* must be <= FD_MAX_NSEC */
+ u_char gaplen; /* GAP 3 length; usually 84 */
+ u_char fillbyte; /* usually 0xf6 */
+ struct fd_idfield_data {
+ /*
+ * data to write into id fields;
+ * for obscure formats, they mustn't match
+ * the real values (but mostly do)
+ */
+ u_char cylno; /* 0 thru 79 (or 39) */
+ u_char headno; /* 0, or 1 */
+ u_char secno; /* starting at 1! */
+ u_char secsize; /* usually 2 */
+ } idfields[FD_MAX_NSEC]; /* 0 <= idx < nsecs used */
+ } structured;
+ u_char raw[1]; /* to have continuous indexed access */
+ } format_info;
+};
+
+/* make life easier */
+# define fd_formb_secshift format_info.structured.secshift
+# define fd_formb_nsecs format_info.structured.nsecs
+# define fd_formb_gaplen format_info.structured.gaplen
+# define fd_formb_fillbyte format_info.structured.fillbyte
+/* these data must be filled in for(i = 0; i < fd_formb_nsecs; i++) */
+# define fd_formb_cylno(i) format_info.structured.idfields[i].cylno
+# define fd_formb_headno(i) format_info.structured.idfields[i].headno
+# define fd_formb_secno(i) format_info.structured.idfields[i].secno
+# define fd_formb_secsize(i) format_info.structured.idfields[i].secsize
+
+struct fd_type {
+ int sectrac; /* sectors per track */
+ int secsize; /* size code for sectors */
+ int datalen; /* data len when secsize = 0 */
+ int gap; /* gap len between sectors */
+ int tracks; /* total num of tracks */
+ int size; /* size of disk in sectors */
+ int steptrac; /* steps per cylinder */
+ int trans; /* transfer speed code */
+ int heads; /* number of heads */
+ int f_gap; /* format gap len */
+ int f_inter; /* format interleave factor */
+};
+
+#define FD_FORM _IOW('F', 61, struct fd_formb) /* format a track */
+#define FD_GTYPE _IOR('F', 62, struct fd_type) /* get drive type */
+
+#endif /* !def _IOCTL_FD_H */
diff --git a/sys/sys/file.h b/sys/sys/file.h
new file mode 100644
index 0000000..3d82190
--- /dev/null
+++ b/sys/sys/file.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)file.h 8.1 (Berkeley) 6/2/93
+ */
+
+#include <sys/fcntl.h>
+#include <sys/unistd.h>
+
+#ifdef KERNEL
+struct proc;
+struct uio;
+
+/*
+ * Kernel descriptor table.
+ * One entry for each open kernel vnode and socket.
+ */
+struct file {
+ struct file *f_filef; /* list of active files */
+ struct file **f_fileb; /* list of active files */
+ short f_flag; /* see fcntl.h */
+#define DTYPE_VNODE 1 /* file */
+#define DTYPE_SOCKET 2 /* communications endpoint */
+ short f_type; /* descriptor type */
+ short f_count; /* reference count */
+ short f_msgcount; /* references from message queue */
+ struct ucred *f_cred; /* credentials associated with descriptor */
+ struct fileops {
+ int (*fo_read) __P((struct file *fp, struct uio *uio,
+ struct ucred *cred));
+ int (*fo_write) __P((struct file *fp, struct uio *uio,
+ struct ucred *cred));
+ int (*fo_ioctl) __P((struct file *fp, int com,
+ caddr_t data, struct proc *p));
+ int (*fo_select) __P((struct file *fp, int which,
+ struct proc *p));
+ int (*fo_close) __P((struct file *fp, struct proc *p));
+ } *f_ops;
+ off_t f_offset;
+ caddr_t f_data; /* vnode or socket */
+};
+
+extern struct file *filehead; /* head of list of open files */
+extern int maxfiles; /* kernel limit on number of open files */
+extern int nfiles; /* actual number of open files */
+
+#endif /* KERNEL */
diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h
new file mode 100644
index 0000000..1071bc1
--- /dev/null
+++ b/sys/sys/filedesc.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)filedesc.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * This structure is used for the management of descriptors. It may be
+ * shared by multiple processes.
+ *
+ * A process is initially started out with NDFILE descriptors stored within
+ * this structure, selected to be enough for typical applications based on
+ * the historical limit of 20 open files (and the usage of descriptors by
+ * shells). If these descriptors are exhausted, a larger descriptor table
+ * may be allocated, up to a process' resource limit; the internal arrays
+ * are then unused. The initial expansion is set to NDEXTENT; each time
+ * it runs out, it is doubled until the resource limit is reached. NDEXTENT
+ * should be selected to be the biggest multiple of OFILESIZE (see below)
+ * that will fit in a power-of-two sized piece of memory.
+ */
+#define NDFILE 20
+#define NDEXTENT 50 /* 250 bytes in 256-byte alloc. */
+
+struct filedesc {
+ struct file **fd_ofiles; /* file structures for open files */
+ char *fd_ofileflags; /* per-process open file flags */
+ struct vnode *fd_cdir; /* current directory */
+ struct vnode *fd_rdir; /* root directory */
+ int fd_nfiles; /* number of open files allocated */
+ u_short fd_lastfile; /* high-water mark of fd_ofiles */
+ u_short fd_freefile; /* approx. next free file */
+ u_short fd_cmask; /* mask for file creation */
+ u_short fd_refcnt; /* reference count */
+};
+
+/*
+ * Basic allocation of descriptors:
+ * one of the above, plus arrays for NDFILE descriptors.
+ */
+struct filedesc0 {
+ struct filedesc fd_fd;
+ /*
+ * These arrays are used when the number of open files is
+ * <= NDFILE, and are then pointed to by the pointers above.
+ */
+ struct file *fd_dfiles[NDFILE];
+ char fd_dfileflags[NDFILE];
+};
+
+/*
+ * Per-process open flags.
+ */
+#define UF_EXCLOSE 0x01 /* auto-close on exec */
+#define UF_MAPPED 0x02 /* mapped from device */
+
+/*
+ * Storage required per open file descriptor.
+ */
+#define OFILESIZE (sizeof(struct file *) + sizeof(char))
+
+#ifdef KERNEL
+/*
+ * Kernel global variables and routines.
+ */
+int fdalloc __P((struct proc *p, int want, int *result));
+int fdavail __P((struct proc *p, int n));
+int falloc __P((struct proc *p, struct file **resultfp, int *resultfd));
+struct filedesc *fdcopy __P((struct proc *p));
+void fdfree __P((struct proc *p));
+#endif
diff --git a/sys/sys/filio.h b/sys/sys/filio.h
new file mode 100644
index 0000000..5c8789b
--- /dev/null
+++ b/sys/sys/filio.h
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 1982, 1986, 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)filio.h 8.1 (Berkeley) 3/28/94
+ */
+
+#ifndef _SYS_FILIO_H_
+#define _SYS_FILIO_H_
+
+#include <sys/ioccom.h>
+
+/* Generic file-descriptor ioctl's. */
+#define FIOCLEX _IO('f', 1) /* set close on exec on fd */
+#define FIONCLEX _IO('f', 2) /* remove close on exec */
+#define FIONREAD _IOR('f', 127, int) /* get # bytes to read */
+#define FIONBIO _IOW('f', 126, int) /* set/clear non-blocking i/o */
+#define FIOASYNC _IOW('f', 125, int) /* set/clear async i/o */
+#define FIOSETOWN _IOW('f', 124, int) /* set owner */
+#define FIOGETOWN _IOR('f', 123, int) /* get owner */
+
+#endif /* !_SYS_FILIO_H_ */
diff --git a/sys/sys/gmon.h b/sys/sys/gmon.h
new file mode 100644
index 0000000..b103df8
--- /dev/null
+++ b/sys/sys/gmon.h
@@ -0,0 +1,159 @@
+/*-
+ * Copyright (c) 1982, 1986, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)gmon.h 8.2 (Berkeley) 1/4/94
+ */
+
+#ifndef _SYS_GMON_H_
+#define _SYS_GMON_H_
+
+#include <machine/profile.h>
+
+/*
+ * Structure prepended to gmon.out profiling data file.
+ */
+struct gmonhdr {
+ u_long lpc; /* base pc address of sample buffer */
+ u_long hpc; /* max pc address of sampled buffer */
+ int ncnt; /* size of sample buffer (plus this header) */
+ int version; /* version number */
+ int profrate; /* profiling clock rate */
+ int spare[3]; /* reserved */
+};
+#define GMONVERSION 0x00051879
+
+/*
+ * histogram counters are unsigned shorts (according to the kernel).
+ */
+#define HISTCOUNTER unsigned short
+
+/*
+ * fraction of text space to allocate for histogram counters here, 1/2
+ */
+#define HISTFRACTION 2
+
+/*
+ * Fraction of text space to allocate for from hash buckets.
+ * The value of HASHFRACTION is based on the minimum number of bytes
+ * of separation between two subroutine call points in the object code.
+ * Given MIN_SUBR_SEPARATION bytes of separation the value of
+ * HASHFRACTION is calculated as:
+ *
+ * HASHFRACTION = MIN_SUBR_SEPARATION / (2 * sizeof(short) - 1);
+ *
+ * For example, on the VAX, the shortest two call sequence is:
+ *
+ * calls $0,(r0)
+ * calls $0,(r0)
+ *
+ * which is separated by only three bytes, thus HASHFRACTION is
+ * calculated as:
+ *
+ * HASHFRACTION = 3 / (2 * 2 - 1) = 1
+ *
+ * Note that the division above rounds down, thus if MIN_SUBR_FRACTION
+ * is less than three, this algorithm will not work!
+ *
+ * In practice, however, call instructions are rarely at a minimal
+ * distance. Hence, we will define HASHFRACTION to be 2 across all
+ * architectures. This saves a reasonable amount of space for
+ * profiling data structures without (in practice) sacrificing
+ * any granularity.
+ */
+#define HASHFRACTION 2
+
+/*
+ * percent of text space to allocate for tostructs with a minimum.
+ */
+#define ARCDENSITY 2
+#define MINARCS 50
+#define MAXARCS ((1 << (8 * sizeof(HISTCOUNTER))) - 2)
+
+struct tostruct {
+ u_long selfpc;
+ long count;
+ u_short link;
+ u_short pad;
+};
+
+/*
+ * a raw arc, with pointers to the calling site and
+ * the called site and a count.
+ */
+struct rawarc {
+ u_long raw_frompc;
+ u_long raw_selfpc;
+ long raw_count;
+};
+
+/*
+ * general rounding functions.
+ */
+#define ROUNDDOWN(x,y) (((x)/(y))*(y))
+#define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y))
+
+/*
+ * The profiling data structures are housed in this structure.
+ */
+struct gmonparam {
+ int state;
+ u_short *kcount;
+ u_long kcountsize;
+ u_short *froms;
+ u_long fromssize;
+ struct tostruct *tos;
+ u_long tossize;
+ long tolimit;
+ u_long lowpc;
+ u_long highpc;
+ u_long textsize;
+ u_long hashfraction;
+};
+extern struct gmonparam _gmonparam;
+
+/*
+ * Possible states of profiling.
+ */
+#define GMON_PROF_ON 0
+#define GMON_PROF_BUSY 1
+#define GMON_PROF_ERROR 2
+#define GMON_PROF_OFF 3
+
+/*
+ * Sysctl definitions for extracting profiling information from the kernel.
+ */
+#define GPROF_STATE 0 /* int: profiling enabling variable */
+#define GPROF_COUNT 1 /* struct: profile tick count buffer */
+#define GPROF_FROMS 2 /* struct: from location hash bucket */
+#define GPROF_TOS 3 /* struct: destination/count structure */
+#define GPROF_GMONPARAM 4 /* struct: profiling parameters (see above) */
+#endif /* !_SYS_GMON_H_ */
diff --git a/sys/sys/imgact.h b/sys/sys/imgact.h
new file mode 100644
index 0000000..6681b72
--- /dev/null
+++ b/sys/sys/imgact.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1993, David Greenman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: imgact.h,v 1.1 1993/12/12 12:31:40 davidg Exp $
+ */
+
+#ifndef __h_imgact
+#define __h_imgact 1
+
+#include "proc.h"
+#include "namei.h"
+#include "vnode.h"
+
+struct image_params {
+ struct proc *proc; /* our process struct */
+ struct execve_args *uap; /* syscall arguments */
+ struct vnode *vnodep; /* pointer to vnode of file to exec */
+ struct vattr *attr; /* attributes of file */
+ const char *image_header; /* head of file to exec */
+ char *stringbase; /* base address of tmp string storage */
+ char *stringp; /* current 'end' pointer of tmp strings */
+ int stringspace; /* space left in tmp string storage area */
+ int argc, envc; /* count of argument and environment strings */
+ unsigned long entry_addr; /* entry address of target executable */
+ char vmspace_destroyed; /* flag - we've blown away original vm space */
+ char interpreted; /* flag - this executable is interpreted */
+ char interpreter_name[64]; /* name of the interpreter */
+};
+
+struct execsw {
+ int (*ex_imgact)(struct image_params *);
+};
+
+extern const struct execsw **execsw;
+
+#endif /* __h_imgact */
diff --git a/sys/sys/ioccom.h b/sys/sys/ioccom.h
new file mode 100644
index 0000000..5bc11b3
--- /dev/null
+++ b/sys/sys/ioccom.h
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 1982, 1986, 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ioccom.h 8.2 (Berkeley) 3/28/94
+ */
+
+#ifndef _SYS_IOCCOM_H_
+#define _SYS_IOCCOM_H_
+
+/*
+ * Ioctl's have the command encoded in the lower word, and the size of
+ * any in or out parameters in the upper word. The high 3 bits of the
+ * upper word are used to encode the in/out status of the parameter.
+ */
+#define IOCPARM_MASK 0x1fff /* parameter length, at most 13 bits */
+#define IOCPARM_LEN(x) (((x) >> 16) & IOCPARM_MASK)
+#define IOCBASECMD(x) ((x) & ~(IOCPARM_MASK << 16))
+#define IOCGROUP(x) (((x) >> 8) & 0xff)
+
+#define IOCPARM_MAX NBPG /* max size of ioctl, mult. of NBPG */
+#define IOC_VOID 0x20000000 /* no parameters */
+#define IOC_OUT 0x40000000 /* copy out parameters */
+#define IOC_IN 0x80000000 /* copy in parameters */
+#define IOC_INOUT (IOC_IN|IOC_OUT)
+#define IOC_DIRMASK 0xe0000000 /* mask for IN/OUT/VOID */
+
+#define _IOC(inout,group,num,len) \
+ (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num))
+#define _IO(g,n) _IOC(IOC_VOID, (g), (n), 0)
+#define _IOR(g,n,t) _IOC(IOC_OUT, (g), (n), sizeof(t))
+#define _IOW(g,n,t) _IOC(IOC_IN, (g), (n), sizeof(t))
+/* this should be _IORW, but stdio got there first */
+#define _IOWR(g,n,t) _IOC(IOC_INOUT, (g), (n), sizeof(t))
+
+#endif /* !_SYS_IOCCOM_H_ */
diff --git a/sys/sys/ioctl.h b/sys/sys/ioctl.h
new file mode 100644
index 0000000..d04394f
--- /dev/null
+++ b/sys/sys/ioctl.h
@@ -0,0 +1,84 @@
+/*-
+ * Copyright (c) 1982, 1986, 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ioctl.h 8.6 (Berkeley) 3/28/94
+ */
+
+#ifndef _SYS_IOCTL_H_
+#define _SYS_IOCTL_H_
+
+#include <sys/ttycom.h>
+
+/*
+ * Pun for SunOS prior to 3.2. SunOS 3.2 and later support TIOCGWINSZ
+ * and TIOCSWINSZ (yes, even 3.2-3.5, the fact that it wasn't documented
+ * nonwithstanding).
+ */
+struct ttysize {
+ unsigned short ts_lines;
+ unsigned short ts_cols;
+ unsigned short ts_xxx;
+ unsigned short ts_yyy;
+};
+#define TIOCGSIZE TIOCGWINSZ
+#define TIOCSSIZE TIOCSWINSZ
+
+#include <sys/ioccom.h>
+
+#include <sys/filio.h>
+#include <sys/sockio.h>
+
+#ifndef KERNEL
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int ioctl __P((int, unsigned long, ...));
+__END_DECLS
+#endif /* !KERNEL */
+#endif /* !_SYS_IOCTL_H_ */
+
+/*
+ * Keep outside _SYS_IOCTL_H_
+ * Compatability with old terminal driver
+ *
+ * Source level -> #define USE_OLD_TTY
+ * Kernel level -> options COMPAT_43 or COMPAT_SUNOS
+ */
+#if defined(USE_OLD_TTY) || defined(COMPAT_43) || defined(COMPAT_SUNOS)
+#include <sys/ioctl_compat.h>
+#endif
diff --git a/sys/sys/ioctl_compat.h b/sys/sys/ioctl_compat.h
new file mode 100644
index 0000000..fd87b51
--- /dev/null
+++ b/sys/sys/ioctl_compat.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ioctl_compat.h 8.4 (Berkeley) 1/21/94
+ */
+
+#ifndef _SYS_IOCTL_COMPAT_H_
+#define _SYS_IOCTL_COMPAT_H_
+
+#include <sys/ttychars.h>
+#include <sys/ttydev.h>
+
+struct tchars {
+ char t_intrc; /* interrupt */
+ char t_quitc; /* quit */
+ char t_startc; /* start output */
+ char t_stopc; /* stop output */
+ char t_eofc; /* end-of-file */
+ char t_brkc; /* input delimiter (like nl) */
+};
+
+struct ltchars {
+ char t_suspc; /* stop process signal */
+ char t_dsuspc; /* delayed stop process signal */
+ char t_rprntc; /* reprint line */
+ char t_flushc; /* flush output (toggles) */
+ char t_werasc; /* word erase */
+ char t_lnextc; /* literal next character */
+};
+
+/*
+ * Structure for TIOCGETP and TIOCSETP ioctls.
+ */
+#ifndef _SGTTYB_
+#define _SGTTYB_
+struct sgttyb {
+ char sg_ispeed; /* input speed */
+ char sg_ospeed; /* output speed */
+ char sg_erase; /* erase character */
+ char sg_kill; /* kill character */
+ short sg_flags; /* mode flags */
+};
+#endif
+
+#ifdef USE_OLD_TTY
+# undef TIOCGETD
+# define TIOCGETD _IOR('t', 0, int) /* get line discipline */
+# undef TIOCSETD
+# define TIOCSETD _IOW('t', 1, int) /* set line discipline */
+#else
+# define OTIOCGETD _IOR('t', 0, int) /* get line discipline */
+# define OTIOCSETD _IOW('t', 1, int) /* set line discipline */
+#endif
+#define TIOCHPCL _IO('t', 2) /* hang up on last close */
+#define TIOCGETP _IOR('t', 8,struct sgttyb)/* get parameters -- gtty */
+#define TIOCSETP _IOW('t', 9,struct sgttyb)/* set parameters -- stty */
+#define TIOCSETN _IOW('t',10,struct sgttyb)/* as above, but no flushtty*/
+#define TIOCSETC _IOW('t',17,struct tchars)/* set special characters */
+#define TIOCGETC _IOR('t',18,struct tchars)/* get special characters */
+#define TANDEM 0x00000001 /* send stopc on out q full */
+#define CBREAK 0x00000002 /* half-cooked mode */
+#define LCASE 0x00000004 /* simulate lower case */
+#define ECHO 0x00000008 /* echo input */
+#define CRMOD 0x00000010 /* map \r to \r\n on output */
+#define RAW 0x00000020 /* no i/o processing */
+#define ODDP 0x00000040 /* get/send odd parity */
+#define EVENP 0x00000080 /* get/send even parity */
+#define ANYP 0x000000c0 /* get any parity/send none */
+#define NLDELAY 0x00000300 /* \n delay */
+#define NL0 0x00000000
+#define NL1 0x00000100 /* tty 37 */
+#define NL2 0x00000200 /* vt05 */
+#define NL3 0x00000300
+#define TBDELAY 0x00000c00 /* horizontal tab delay */
+#define TAB0 0x00000000
+#define TAB1 0x00000400 /* tty 37 */
+#define TAB2 0x00000800
+#define XTABS 0x00000c00 /* expand tabs on output */
+#define CRDELAY 0x00003000 /* \r delay */
+#define CR0 0x00000000
+#define CR1 0x00001000 /* tn 300 */
+#define CR2 0x00002000 /* tty 37 */
+#define CR3 0x00003000 /* concept 100 */
+#define VTDELAY 0x00004000 /* vertical tab delay */
+#define FF0 0x00000000
+#define FF1 0x00004000 /* tty 37 */
+#define BSDELAY 0x00008000 /* \b delay */
+#define BS0 0x00000000
+#define BS1 0x00008000
+#define ALLDELAY (NLDELAY|TBDELAY|CRDELAY|VTDELAY|BSDELAY)
+#define CRTBS 0x00010000 /* do backspacing for crt */
+#define PRTERA 0x00020000 /* \ ... / erase */
+#define CRTERA 0x00040000 /* " \b " to wipe out char */
+#define TILDE 0x00080000 /* hazeltine tilde kludge */
+#define MDMBUF 0x00100000 /*start/stop output on carrier*/
+#define LITOUT 0x00200000 /* literal output */
+#define TOSTOP 0x00400000 /*SIGSTOP on background output*/
+#define FLUSHO 0x00800000 /* flush output to terminal */
+#define NOHANG 0x01000000 /* (no-op) was no SIGHUP on carrier drop */
+#define L001000 0x02000000
+#define CRTKIL 0x04000000 /* kill line with " \b " */
+#define PASS8 0x08000000
+#define CTLECH 0x10000000 /* echo control chars as ^X */
+#define PENDIN 0x20000000 /* tp->t_rawq needs reread */
+#define DECCTQ 0x40000000 /* only ^Q starts after ^S */
+#define NOFLSH 0x80000000 /* no output flush on signal */
+#define TIOCLBIS _IOW('t', 127, int) /* bis local mode bits */
+#define TIOCLBIC _IOW('t', 126, int) /* bic local mode bits */
+#define TIOCLSET _IOW('t', 125, int) /* set entire local mode word */
+#define TIOCLGET _IOR('t', 124, int) /* get local modes */
+#define LCRTBS (CRTBS>>16)
+#define LPRTERA (PRTERA>>16)
+#define LCRTERA (CRTERA>>16)
+#define LTILDE (TILDE>>16)
+#define LMDMBUF (MDMBUF>>16)
+#define LLITOUT (LITOUT>>16)
+#define LTOSTOP (TOSTOP>>16)
+#define LFLUSHO (FLUSHO>>16)
+#define LNOHANG (NOHANG>>16)
+#define LCRTKIL (CRTKIL>>16)
+#define LPASS8 (PASS8>>16)
+#define LCTLECH (CTLECH>>16)
+#define LPENDIN (PENDIN>>16)
+#define LDECCTQ (DECCTQ>>16)
+#define LNOFLSH (NOFLSH>>16)
+#define TIOCSLTC _IOW('t',117,struct ltchars)/* set local special chars*/
+#define TIOCGLTC _IOR('t',116,struct ltchars)/* get local special chars*/
+#define OTIOCCONS _IO('t', 98) /* for hp300 -- sans int arg */
+#define OTTYDISC 0
+#define NETLDISC 1
+#define NTTYDISC 2
+
+#endif /* !_SYS_IOCTL_COMPAT_H_ */
diff --git a/sys/sys/ipc.h b/sys/sys/ipc.h
new file mode 100644
index 0000000..cc036a8
--- /dev/null
+++ b/sys/sys/ipc.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1988 University of Utah.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ipc.h 8.3 (Berkeley) 1/21/94
+ */
+
+/*
+ * SVID compatible ipc.h file
+ */
+#ifndef _SYS_IPC_H_
+#define _SYS_IPC_H_
+
+typedef long key_t; /* XXX should be in types.h */
+
+struct ipc_perm {
+ ushort cuid; /* creator user id */
+ ushort cgid; /* creator group id */
+ ushort uid; /* user id */
+ ushort gid; /* group id */
+ ushort mode; /* r/w permission */
+ ushort seq; /* sequence # (to generate unique msg/sem/shm id) */
+ key_t key; /* user specified msg/sem/shm key */
+};
+
+/* common mode bits */
+#define IPC_R 00400 /* read permission */
+#define IPC_W 00200 /* write/alter permission */
+
+/* SVID required constants (same values as system 5) */
+#define IPC_CREAT 01000 /* create entry if key does not exist */
+#define IPC_EXCL 02000 /* fail if key exists */
+#define IPC_NOWAIT 04000 /* error if request must wait */
+
+#define IPC_PRIVATE (key_t)0 /* private key */
+
+#define IPC_RMID 0 /* remove identifier */
+#define IPC_SET 1 /* set options */
+#define IPC_STAT 2 /* get options */
+
+#endif /* !_SYS_IPC_H_ */
diff --git a/sys/sys/kernel.h b/sys/sys/kernel.h
new file mode 100644
index 0000000..aafa843
--- /dev/null
+++ b/sys/sys/kernel.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kernel.h 8.3 (Berkeley) 1/21/94
+ */
+
+/* Global variables for the kernel. */
+
+/* 1.1 */
+extern long hostid;
+extern char hostname[MAXHOSTNAMELEN];
+extern int hostnamelen;
+extern char domainname[MAXHOSTNAMELEN];
+extern int domainnamelen;
+
+
+/* 1.2 */
+extern volatile struct timeval mono_time;
+extern struct timeval boottime;
+extern struct timeval runtime;
+extern volatile struct timeval time;
+extern struct timezone tz; /* XXX */
+
+extern int tick; /* usec per tick (1000000 / hz) */
+extern int hz; /* system clock's frequency */
+extern int stathz; /* statistics clock's frequency */
+extern int profhz; /* profiling clock's frequency */
+extern int lbolt; /* once a second sleep address */
+
+/*
+ * The following macros are used to declare global sets of objects, which
+ * are collected by the linker into a `struct linker_set' as defined below.
+ *
+ * NB: the constants defined below must match those defined in
+ * ld/ld.h. Since their calculation requires arithmetic, we
+ * can't name them symbolically (e.g., 23 is N_SETT | N_EXT).
+ */
+#define MAKE_SET(set, sym, type) \
+ asm(".stabs \"_" #set "\", " #type ", 0, 0, _" #sym)
+#define TEXT_SET(set, sym) MAKE_SET(set, sym, 23)
+#define DATA_SET(set, sym) MAKE_SET(set, sym, 25)
+#define BSS_SET(set, sym) MAKE_SET(set, sym, 27)
+#define ABS_SET(set, sym) MAKE_SET(set, sym, 21)
+
+struct linker_set {
+ int ls_length;
+ caddr_t ls_items[1]; /* really ls_length of them, trailing NULL */
+};
+
diff --git a/sys/sys/ktrace.h b/sys/sys/ktrace.h
new file mode 100644
index 0000000..1623c35
--- /dev/null
+++ b/sys/sys/ktrace.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ktrace.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * operations to ktrace system call (KTROP(op))
+ */
+#define KTROP_SET 0 /* set trace points */
+#define KTROP_CLEAR 1 /* clear trace points */
+#define KTROP_CLEARFILE 2 /* stop all tracing to file */
+#define KTROP(o) ((o)&3) /* macro to extract operation */
+/*
+ * flags (ORed in with operation)
+ */
+#define KTRFLAG_DESCEND 4 /* perform op on all children too */
+
+/*
+ * ktrace record header
+ */
+struct ktr_header {
+ int ktr_len; /* length of buf */
+ short ktr_type; /* trace record type */
+ pid_t ktr_pid; /* process id */
+ char ktr_comm[MAXCOMLEN+1]; /* command name */
+ struct timeval ktr_time; /* timestamp */
+ caddr_t ktr_buf;
+};
+
+/*
+ * Test for kernel trace point
+ */
+#define KTRPOINT(p, type) \
+ (((p)->p_traceflag & ((1<<(type))|KTRFAC_ACTIVE)) == (1<<(type)))
+
+/*
+ * ktrace record types
+ */
+
+/*
+ * KTR_SYSCALL - system call record
+ */
+#define KTR_SYSCALL 1
+struct ktr_syscall {
+ short ktr_code; /* syscall number */
+ short ktr_narg; /* number of arguments */
+ /*
+ * followed by ktr_narg ints
+ */
+};
+
+/*
+ * KTR_SYSRET - return from system call record
+ */
+#define KTR_SYSRET 2
+struct ktr_sysret {
+ short ktr_code;
+ short ktr_eosys;
+ int ktr_error;
+ int ktr_retval;
+};
+
+/*
+ * KTR_NAMEI - namei record
+ */
+#define KTR_NAMEI 3
+ /* record contains pathname */
+
+/*
+ * KTR_GENIO - trace generic process i/o
+ */
+#define KTR_GENIO 4
+struct ktr_genio {
+ int ktr_fd;
+ enum uio_rw ktr_rw;
+ /*
+ * followed by data successfully read/written
+ */
+};
+
+/*
+ * KTR_PSIG - trace processed signal
+ */
+#define KTR_PSIG 5
+struct ktr_psig {
+ int signo;
+ sig_t action;
+ int mask;
+ int code;
+};
+
+/*
+ * KTR_CSW - trace context switches
+ */
+#define KTR_CSW 6
+struct ktr_csw {
+ int out; /* 1 if switch out, 0 if switch in */
+ int user; /* 1 if usermode (ivcsw), 0 if kernel (vcsw) */
+};
+
+/*
+ * kernel trace points (in p_traceflag)
+ */
+#define KTRFAC_MASK 0x00ffffff
+#define KTRFAC_SYSCALL (1<<KTR_SYSCALL)
+#define KTRFAC_SYSRET (1<<KTR_SYSRET)
+#define KTRFAC_NAMEI (1<<KTR_NAMEI)
+#define KTRFAC_GENIO (1<<KTR_GENIO)
+#define KTRFAC_PSIG (1<<KTR_PSIG)
+#define KTRFAC_CSW (1<<KTR_CSW)
+/*
+ * trace flags (also in p_traceflags)
+ */
+#define KTRFAC_ROOT 0x80000000 /* root set this trace */
+#define KTRFAC_INHERIT 0x40000000 /* pass trace flags to children */
+#define KTRFAC_ACTIVE 0x20000000 /* ktrace logging in progress, ignore */
+
+#ifndef KERNEL
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int ktrace __P((const char *, int, int, pid_t));
+__END_DECLS
+
+#endif /* !KERNEL */
diff --git a/sys/sys/libkern.h b/sys/sys/libkern.h
new file mode 100644
index 0000000..0e465e0
--- /dev/null
+++ b/sys/sys/libkern.h
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)libkern.h 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/types.h>
+
+static inline int
+imax(a, b)
+ int a, b;
+{
+ return (a > b ? a : b);
+}
+static inline int
+imin(a, b)
+ int a, b;
+{
+ return (a < b ? a : b);
+}
+static inline long
+lmax(a, b)
+ long a, b;
+{
+ return (a > b ? a : b);
+}
+static inline long
+lmin(a, b)
+ long a, b;
+{
+ return (a < b ? a : b);
+}
+static inline u_int
+max(a, b)
+ u_int a, b;
+{
+ return (a > b ? a : b);
+}
+static inline u_int
+min(a, b)
+ u_int a, b;
+{
+ return (a < b ? a : b);
+}
+static inline u_long
+ulmax(a, b)
+ u_long a, b;
+{
+ return (a > b ? a : b);
+}
+static inline u_long
+ulmin(a, b)
+ u_long a, b;
+{
+ return (a < b ? a : b);
+}
+
+/* Prototypes for non-quad routines. */
+int bcmp __P((const void *, const void *, size_t));
+int ffs __P((int));
+int locc __P((int, char *, u_int));
+u_long random __P((void));
+char *rindex __P((const char *, int));
+int scanc __P((u_int, u_char *, u_char *, int));
+int skpc __P((int, int, char *));
+char *strcat __P((char *, const char *));
+char *strcpy __P((char *, const char *));
+size_t strlen __P((const char *));
+char *strncpy __P((char *, const char *, size_t));
diff --git a/sys/sys/linedisc.h b/sys/sys/linedisc.h
new file mode 100644
index 0000000..58cb6fa
--- /dev/null
+++ b/sys/sys/linedisc.h
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)conf.h 8.3 (Berkeley) 1/21/94
+ */
+
+/*
+ * Definitions of device driver entry switches
+ */
+
+struct buf;
+struct proc;
+struct tty;
+struct uio;
+struct vnode;
+
+struct bdevsw {
+ int (*d_open) __P((dev_t dev, int oflags, int devtype,
+ struct proc *p));
+ int (*d_close) __P((dev_t dev, int fflag, int devtype,
+ struct proc *p));
+ int (*d_strategy) __P((struct buf *bp));
+ int (*d_ioctl) __P((dev_t dev, int cmd, caddr_t data,
+ int fflag, struct proc *p));
+ int (*d_dump) (); /* parameters vary by architecture */
+ int (*d_psize) __P((dev_t dev));
+ int d_flags;
+};
+
+#ifdef KERNEL
+extern struct bdevsw bdevsw[];
+#endif
+
+struct cdevsw {
+ int (*d_open) __P((dev_t dev, int oflags, int devtype,
+ struct proc *p));
+ int (*d_close) __P((dev_t dev, int fflag, int devtype,
+ struct proc *));
+ int (*d_read) __P((dev_t dev, struct uio *uio, int ioflag));
+ int (*d_write) __P((dev_t dev, struct uio *uio, int ioflag));
+ int (*d_ioctl) __P((dev_t dev, int cmd, caddr_t data,
+ int fflag, struct proc *p));
+ int (*d_stop) __P((struct tty *tp, int rw));
+ int (*d_reset) __P((int uban)); /* XXX */
+ struct tty *d_ttys;
+ int (*d_select) __P((dev_t dev, int which, struct proc *p));
+ int (*d_mmap) __P(());
+ int (*d_strategy) __P((struct buf *bp));
+};
+
+#ifdef KERNEL
+extern struct cdevsw cdevsw[];
+
+/* symbolic sleep message strings */
+extern char devopn[], devio[], devwait[], devin[], devout[];
+extern char devioc[], devcls[];
+#endif
+
+struct linesw {
+ int (*l_open) __P((dev_t dev, struct tty *tp));
+ int (*l_close) __P((struct tty *tp, int flag));
+ int (*l_read) __P((struct tty *tp, struct uio *uio,
+ int flag));
+ int (*l_write) __P((struct tty *tp, struct uio *uio,
+ int flag));
+ int (*l_ioctl) __P((struct tty *tp, int cmd, caddr_t data,
+ int flag, struct proc *p));
+ int (*l_rint) __P((int c, struct tty *tp));
+ int (*l_start) __P((struct tty *tp));
+ int (*l_modem) __P((struct tty *tp, int flag));
+};
+
+#ifdef KERNEL
+extern struct linesw linesw[];
+#endif
+
+struct swdevt {
+ dev_t sw_dev;
+ int sw_flags;
+ int sw_nblks;
+ struct vnode *sw_vp;
+};
+#define SW_FREED 0x01
+#define SW_SEQUENTIAL 0x02
+#define sw_freed sw_flags /* XXX compat */
+
+#ifdef KERNEL
+extern struct swdevt swdevt[];
+#endif
diff --git a/sys/sys/link_aout.h b/sys/sys/link_aout.h
new file mode 100644
index 0000000..3a5b6aa
--- /dev/null
+++ b/sys/sys/link_aout.h
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+/*
+ * RRS section definitions.
+ *
+ * The layout of some data structures defined in this header file is
+ * such that we can provide compatibility with the SunOS 4.x shared
+ * library scheme.
+ */
+
+#ifndef _LINK_H_
+#define _LINK_H_
+
+/*
+ * A `Shared Object Descriptor' descibes a shared object that is needed
+ * to complete the link edit process of the object containing it.
+ * A list of such objects (chained through `sod_next') is pointed at
+ * by `sdt_sods' in the section_dispatch_table structure.
+ */
+
+struct sod { /* Shared Object Descriptor */
+ long sod_name; /* name (relative to load address) */
+ u_int sod_library : 1, /* Searched for by library rules */
+ sod_reserved : 31;
+ short sod_major; /* major version number */
+ short sod_minor; /* minor version number */
+ long sod_next; /* next sod */
+};
+
+/*
+ * `Shared Object Map's are used by the run-time link editor (ld.so) to
+ * keep track of all shared objects loaded into a process' address space.
+ * These structures are only used at run-time and do not occur within
+ * the text or data segment of an executable or shared library.
+ */
+struct so_map { /* Shared Object Map */
+ caddr_t som_addr; /* Address at which object mapped */
+ char *som_path; /* Path to mmap'ed file */
+ struct so_map *som_next; /* Next map in chain */
+ struct sod *som_sod; /* Sod responsible for this map */
+ caddr_t som_sodbase; /* Base address of this sod */
+ u_int som_write : 1; /* Text is currently writable */
+ struct _dynamic *som_dynamic; /* _dynamic structure */
+ caddr_t som_spd; /* Private data */
+};
+
+/*
+ * Symbol description with size. This is simply an `nlist' with
+ * one field (nz_size) added.
+ * Used to convey size information on items in the data segment
+ * of shared objects. An array of these live in the shared object's
+ * text segment and is addressed by the `sdt_nzlist' field.
+ */
+struct nzlist {
+ struct nlist nlist;
+ u_long nz_size;
+#define nz_un nlist.n_un
+#define nz_strx nlist.n_un.n_strx
+#define nz_name nlist.n_un.n_name
+#define nz_type nlist.n_type
+#define nz_value nlist.n_value
+#define nz_desc nlist.n_desc
+#define nz_other nlist.n_other
+};
+
+#define N_AUX(p) ((p)->n_other & 0xf)
+#define N_RESERVED(p) (((unsigned int)(p)->n_other >> 4) & 0xf)
+#define N_OTHER(r, v) (((unsigned int)(r) << 4) | ((v) & 0xf))
+
+#define AUX_OBJECT 1
+#define AUX_FUNC 2
+
+
+/*
+ * The `section_dispatch_table' structure contains offsets to various data
+ * structures needed to do run-time relocation.
+ */
+struct section_dispatch_table {
+ struct so_map *sdt_loaded; /* List of loaded objects */
+ long sdt_sods; /* List of shared objects descriptors */
+ long sdt_filler1; /* Unused (was: search rules) */
+ long sdt_got; /* Global offset table */
+ long sdt_plt; /* Procedure linkage table */
+ long sdt_rel; /* Relocation table */
+ long sdt_hash; /* Symbol hash table */
+ long sdt_nzlist; /* Symbol table itself */
+ long sdt_filler2; /* Unused (was: stab_hash) */
+ long sdt_buckets; /* Number of hash buckets */
+ long sdt_strings; /* Symbol strings */
+ long sdt_str_sz; /* Size of symbol strings */
+ long sdt_text_sz; /* Size of text area */
+ long sdt_plt_sz; /* Size of procedure linkage table */
+};
+
+/*
+ * RRS symbol hash table, addressed by `sdt_hash' in section_dispatch_table.
+ * Used to quickly lookup symbols of the shared object by hashing
+ * on the symbol's name. `rh_symbolnum' is the index of the symbol
+ * in the shared object's symbol list (`sdt_nzlist'), `rh_next' is
+ * the next symbol in the hash bucket (in case of collisions).
+ */
+struct rrs_hash {
+ int rh_symbolnum; /* Symbol number */
+ int rh_next; /* Next hash entry */
+};
+
+/*
+ * `rt_symbols' is used to keep track of run-time allocated commons
+ * and data items copied from shared objects.
+ */
+struct rt_symbol {
+ struct nzlist *rt_sp; /* The symbol */
+ struct rt_symbol *rt_next; /* Next in linear list */
+ struct rt_symbol *rt_link; /* Next in bucket */
+ caddr_t rt_srcaddr; /* Address of "master" copy */
+ struct so_map *rt_smp; /* Originating map */
+};
+
+/*
+ * Debugger interface structure.
+ */
+struct so_debug {
+ int dd_version; /* Version # of interface */
+ int dd_in_debugger; /* Set when run by debugger */
+ int dd_sym_loaded; /* Run-time linking brought more
+ symbols into scope */
+ char *dd_bpt_addr; /* Address of rtld-generated bpt */
+ int dd_bpt_shadow; /* Original contents of bpt */
+ struct rt_symbol *dd_cc; /* Allocated commons/copied data */
+};
+
+/*
+ * Entry points into ld.so - user interface to the run-time linker.
+ */
+struct ld_entry {
+ void *(*dlopen) __P((char *, int));
+ int (*dlclose) __P((void *));
+ void *(*dlsym) __P((void *, char *));
+ int (*dlctl) __P((void *, int, void *));
+};
+
+/*
+ * dlctl() commands
+ */
+#define DL_GETERRNO 1
+
+/*
+ * dl*() prototypes.
+ */
+extern void *dlopen __P((char *, int));
+extern int dlclose __P((void *));
+extern void *dlsym __P((void *, char *));
+extern int dlctl __P((void *, int, void *));
+
+
+/*
+ * This is the structure pointed at by the __DYNAMIC symbol if an
+ * executable requires the attention of the run-time link editor.
+ * __DYNAMIC is given the value zero if no run-time linking needs to
+ * be done (it is always present in shared objects).
+ * The union `d_un' provides for different versions of the dynamic
+ * linking mechanism (switched on by `d_version'). The last version
+ * used by Sun is 3. We leave some room here and go to version number
+ * 8 for NetBSD, the main difference lying in the support for the
+ * `nz_list' type of symbols.
+ */
+
+struct _dynamic {
+ int d_version; /* version # of this interface */
+ struct so_debug *d_debug;
+ union {
+ struct section_dispatch_table *d_sdt;
+ } d_un;
+ struct ld_entry *d_entry;
+};
+
+#define LD_VERSION_SUN (3)
+#define LD_VERSION_BSD (8)
+#define LD_VERSION_NZLIST_P(v) ((v) >= 8)
+
+#define LD_GOT(x) ((x)->d_un.d_sdt->sdt_got)
+#define LD_PLT(x) ((x)->d_un.d_sdt->sdt_plt)
+#define LD_REL(x) ((x)->d_un.d_sdt->sdt_rel)
+#define LD_SYMBOL(x) ((x)->d_un.d_sdt->sdt_nzlist)
+#define LD_HASH(x) ((x)->d_un.d_sdt->sdt_hash)
+#define LD_STRINGS(x) ((x)->d_un.d_sdt->sdt_strings)
+#define LD_NEED(x) ((x)->d_un.d_sdt->sdt_sods)
+#define LD_BUCKETS(x) ((x)->d_un.d_sdt->sdt_buckets)
+
+#define LD_GOTSZ(x) ((x)->d_un.d_sdt->sdt_plt - (x)->d_un.d_sdt->sdt_got)
+#define LD_RELSZ(x) ((x)->d_un.d_sdt->sdt_hash - (x)->d_un.d_sdt->sdt_rel)
+#define LD_HASHSZ(x) ((x)->d_un.d_sdt->sdt_nzlist - (x)->d_un.d_sdt->sdt_hash)
+#define LD_STABSZ(x) ((x)->d_un.d_sdt->sdt_strings - (x)->d_un.d_sdt->sdt_nzlist)
+#define LD_PLTSZ(x) ((x)->d_un.d_sdt->sdt_plt_sz)
+#define LD_STRSZ(x) ((x)->d_un.d_sdt->sdt_str_sz)
+#define LD_TEXTSZ(x) ((x)->d_un.d_sdt->sdt_text_sz)
+
+/*
+ * Interface to ld.so
+ */
+struct crt_ldso {
+ int crt_ba; /* Base address of ld.so */
+ int crt_dzfd; /* "/dev/zero" file decriptor (SunOS) */
+ int crt_ldfd; /* ld.so file descriptor */
+ struct _dynamic *crt_dp; /* Main's __DYNAMIC */
+ char **crt_ep; /* environment strings */
+ caddr_t crt_bp; /* Breakpoint if run from debugger */
+ char *crt_prog; /* Program name */
+};
+
+/*
+ * Version passed from crt0 to ld.so (1st argument to _rtld()).
+ */
+#define CRT_VERSION_SUN 1
+#define CRT_VERSION_BSD 2
+#define CRT_VERSION_BSD_2 2
+#define CRT_VERSION_BSD_3 3
+
+
+/*
+ * Maximum number of recognized shared object version numbers.
+ */
+#define MAXDEWEY 8
+
+/*
+ * Header of the hints file.
+ */
+struct hints_header {
+ long hh_magic;
+#define HH_MAGIC 011421044151
+ long hh_version; /* Interface version number */
+#define LD_HINTS_VERSION_1 1
+ long hh_hashtab; /* Location of hash table */
+ long hh_nbucket; /* Number of buckets in hashtab */
+ long hh_strtab; /* Location of strings */
+ long hh_strtab_sz; /* Size of strings */
+ long hh_ehints; /* End of hints (max offset in file) */
+};
+
+#define HH_BADMAG(hdr) ((hdr).hh_magic != HH_MAGIC)
+
+/*
+ * Hash table element in hints file.
+ */
+struct hints_bucket {
+ /* namex and pathx are indices into the string table */
+ int hi_namex; /* Library name */
+ int hi_pathx; /* Full path */
+ int hi_dewey[MAXDEWEY]; /* The versions */
+ int hi_ndewey; /* Number of version numbers */
+#define hi_major hi_dewey[0]
+#define hi_minor hi_dewey[1]
+ int hi_next; /* Next in this bucket */
+};
+
+#define _PATH_LD_HINTS "/var/run/ld.so.hints"
+
+#endif /* _LINK_H_ */
+
diff --git a/sys/sys/link_elf.h b/sys/sys/link_elf.h
new file mode 100644
index 0000000..3a5b6aa
--- /dev/null
+++ b/sys/sys/link_elf.h
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+/*
+ * RRS section definitions.
+ *
+ * The layout of some data structures defined in this header file is
+ * such that we can provide compatibility with the SunOS 4.x shared
+ * library scheme.
+ */
+
+#ifndef _LINK_H_
+#define _LINK_H_
+
+/*
+ * A `Shared Object Descriptor' descibes a shared object that is needed
+ * to complete the link edit process of the object containing it.
+ * A list of such objects (chained through `sod_next') is pointed at
+ * by `sdt_sods' in the section_dispatch_table structure.
+ */
+
+struct sod { /* Shared Object Descriptor */
+ long sod_name; /* name (relative to load address) */
+ u_int sod_library : 1, /* Searched for by library rules */
+ sod_reserved : 31;
+ short sod_major; /* major version number */
+ short sod_minor; /* minor version number */
+ long sod_next; /* next sod */
+};
+
+/*
+ * `Shared Object Map's are used by the run-time link editor (ld.so) to
+ * keep track of all shared objects loaded into a process' address space.
+ * These structures are only used at run-time and do not occur within
+ * the text or data segment of an executable or shared library.
+ */
+struct so_map { /* Shared Object Map */
+ caddr_t som_addr; /* Address at which object mapped */
+ char *som_path; /* Path to mmap'ed file */
+ struct so_map *som_next; /* Next map in chain */
+ struct sod *som_sod; /* Sod responsible for this map */
+ caddr_t som_sodbase; /* Base address of this sod */
+ u_int som_write : 1; /* Text is currently writable */
+ struct _dynamic *som_dynamic; /* _dynamic structure */
+ caddr_t som_spd; /* Private data */
+};
+
+/*
+ * Symbol description with size. This is simply an `nlist' with
+ * one field (nz_size) added.
+ * Used to convey size information on items in the data segment
+ * of shared objects. An array of these live in the shared object's
+ * text segment and is addressed by the `sdt_nzlist' field.
+ */
+struct nzlist {
+ struct nlist nlist;
+ u_long nz_size;
+#define nz_un nlist.n_un
+#define nz_strx nlist.n_un.n_strx
+#define nz_name nlist.n_un.n_name
+#define nz_type nlist.n_type
+#define nz_value nlist.n_value
+#define nz_desc nlist.n_desc
+#define nz_other nlist.n_other
+};
+
+#define N_AUX(p) ((p)->n_other & 0xf)
+#define N_RESERVED(p) (((unsigned int)(p)->n_other >> 4) & 0xf)
+#define N_OTHER(r, v) (((unsigned int)(r) << 4) | ((v) & 0xf))
+
+#define AUX_OBJECT 1
+#define AUX_FUNC 2
+
+
+/*
+ * The `section_dispatch_table' structure contains offsets to various data
+ * structures needed to do run-time relocation.
+ */
+struct section_dispatch_table {
+ struct so_map *sdt_loaded; /* List of loaded objects */
+ long sdt_sods; /* List of shared objects descriptors */
+ long sdt_filler1; /* Unused (was: search rules) */
+ long sdt_got; /* Global offset table */
+ long sdt_plt; /* Procedure linkage table */
+ long sdt_rel; /* Relocation table */
+ long sdt_hash; /* Symbol hash table */
+ long sdt_nzlist; /* Symbol table itself */
+ long sdt_filler2; /* Unused (was: stab_hash) */
+ long sdt_buckets; /* Number of hash buckets */
+ long sdt_strings; /* Symbol strings */
+ long sdt_str_sz; /* Size of symbol strings */
+ long sdt_text_sz; /* Size of text area */
+ long sdt_plt_sz; /* Size of procedure linkage table */
+};
+
+/*
+ * RRS symbol hash table, addressed by `sdt_hash' in section_dispatch_table.
+ * Used to quickly lookup symbols of the shared object by hashing
+ * on the symbol's name. `rh_symbolnum' is the index of the symbol
+ * in the shared object's symbol list (`sdt_nzlist'), `rh_next' is
+ * the next symbol in the hash bucket (in case of collisions).
+ */
+struct rrs_hash {
+ int rh_symbolnum; /* Symbol number */
+ int rh_next; /* Next hash entry */
+};
+
+/*
+ * `rt_symbols' is used to keep track of run-time allocated commons
+ * and data items copied from shared objects.
+ */
+struct rt_symbol {
+ struct nzlist *rt_sp; /* The symbol */
+ struct rt_symbol *rt_next; /* Next in linear list */
+ struct rt_symbol *rt_link; /* Next in bucket */
+ caddr_t rt_srcaddr; /* Address of "master" copy */
+ struct so_map *rt_smp; /* Originating map */
+};
+
+/*
+ * Debugger interface structure.
+ */
+struct so_debug {
+ int dd_version; /* Version # of interface */
+ int dd_in_debugger; /* Set when run by debugger */
+ int dd_sym_loaded; /* Run-time linking brought more
+ symbols into scope */
+ char *dd_bpt_addr; /* Address of rtld-generated bpt */
+ int dd_bpt_shadow; /* Original contents of bpt */
+ struct rt_symbol *dd_cc; /* Allocated commons/copied data */
+};
+
+/*
+ * Entry points into ld.so - user interface to the run-time linker.
+ */
+struct ld_entry {
+ void *(*dlopen) __P((char *, int));
+ int (*dlclose) __P((void *));
+ void *(*dlsym) __P((void *, char *));
+ int (*dlctl) __P((void *, int, void *));
+};
+
+/*
+ * dlctl() commands
+ */
+#define DL_GETERRNO 1
+
+/*
+ * dl*() prototypes.
+ */
+extern void *dlopen __P((char *, int));
+extern int dlclose __P((void *));
+extern void *dlsym __P((void *, char *));
+extern int dlctl __P((void *, int, void *));
+
+
+/*
+ * This is the structure pointed at by the __DYNAMIC symbol if an
+ * executable requires the attention of the run-time link editor.
+ * __DYNAMIC is given the value zero if no run-time linking needs to
+ * be done (it is always present in shared objects).
+ * The union `d_un' provides for different versions of the dynamic
+ * linking mechanism (switched on by `d_version'). The last version
+ * used by Sun is 3. We leave some room here and go to version number
+ * 8 for NetBSD, the main difference lying in the support for the
+ * `nz_list' type of symbols.
+ */
+
+struct _dynamic {
+ int d_version; /* version # of this interface */
+ struct so_debug *d_debug;
+ union {
+ struct section_dispatch_table *d_sdt;
+ } d_un;
+ struct ld_entry *d_entry;
+};
+
+#define LD_VERSION_SUN (3)
+#define LD_VERSION_BSD (8)
+#define LD_VERSION_NZLIST_P(v) ((v) >= 8)
+
+#define LD_GOT(x) ((x)->d_un.d_sdt->sdt_got)
+#define LD_PLT(x) ((x)->d_un.d_sdt->sdt_plt)
+#define LD_REL(x) ((x)->d_un.d_sdt->sdt_rel)
+#define LD_SYMBOL(x) ((x)->d_un.d_sdt->sdt_nzlist)
+#define LD_HASH(x) ((x)->d_un.d_sdt->sdt_hash)
+#define LD_STRINGS(x) ((x)->d_un.d_sdt->sdt_strings)
+#define LD_NEED(x) ((x)->d_un.d_sdt->sdt_sods)
+#define LD_BUCKETS(x) ((x)->d_un.d_sdt->sdt_buckets)
+
+#define LD_GOTSZ(x) ((x)->d_un.d_sdt->sdt_plt - (x)->d_un.d_sdt->sdt_got)
+#define LD_RELSZ(x) ((x)->d_un.d_sdt->sdt_hash - (x)->d_un.d_sdt->sdt_rel)
+#define LD_HASHSZ(x) ((x)->d_un.d_sdt->sdt_nzlist - (x)->d_un.d_sdt->sdt_hash)
+#define LD_STABSZ(x) ((x)->d_un.d_sdt->sdt_strings - (x)->d_un.d_sdt->sdt_nzlist)
+#define LD_PLTSZ(x) ((x)->d_un.d_sdt->sdt_plt_sz)
+#define LD_STRSZ(x) ((x)->d_un.d_sdt->sdt_str_sz)
+#define LD_TEXTSZ(x) ((x)->d_un.d_sdt->sdt_text_sz)
+
+/*
+ * Interface to ld.so
+ */
+struct crt_ldso {
+ int crt_ba; /* Base address of ld.so */
+ int crt_dzfd; /* "/dev/zero" file decriptor (SunOS) */
+ int crt_ldfd; /* ld.so file descriptor */
+ struct _dynamic *crt_dp; /* Main's __DYNAMIC */
+ char **crt_ep; /* environment strings */
+ caddr_t crt_bp; /* Breakpoint if run from debugger */
+ char *crt_prog; /* Program name */
+};
+
+/*
+ * Version passed from crt0 to ld.so (1st argument to _rtld()).
+ */
+#define CRT_VERSION_SUN 1
+#define CRT_VERSION_BSD 2
+#define CRT_VERSION_BSD_2 2
+#define CRT_VERSION_BSD_3 3
+
+
+/*
+ * Maximum number of recognized shared object version numbers.
+ */
+#define MAXDEWEY 8
+
+/*
+ * Header of the hints file.
+ */
+struct hints_header {
+ long hh_magic;
+#define HH_MAGIC 011421044151
+ long hh_version; /* Interface version number */
+#define LD_HINTS_VERSION_1 1
+ long hh_hashtab; /* Location of hash table */
+ long hh_nbucket; /* Number of buckets in hashtab */
+ long hh_strtab; /* Location of strings */
+ long hh_strtab_sz; /* Size of strings */
+ long hh_ehints; /* End of hints (max offset in file) */
+};
+
+#define HH_BADMAG(hdr) ((hdr).hh_magic != HH_MAGIC)
+
+/*
+ * Hash table element in hints file.
+ */
+struct hints_bucket {
+ /* namex and pathx are indices into the string table */
+ int hi_namex; /* Library name */
+ int hi_pathx; /* Full path */
+ int hi_dewey[MAXDEWEY]; /* The versions */
+ int hi_ndewey; /* Number of version numbers */
+#define hi_major hi_dewey[0]
+#define hi_minor hi_dewey[1]
+ int hi_next; /* Next in this bucket */
+};
+
+#define _PATH_LD_HINTS "/var/run/ld.so.hints"
+
+#endif /* _LINK_H_ */
+
diff --git a/sys/sys/malloc.h b/sys/sys/malloc.h
new file mode 100644
index 0000000..aa10965
--- /dev/null
+++ b/sys/sys/malloc.h
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)malloc.h 8.3 (Berkeley) 1/12/94
+ */
+
+#ifndef _SYS_MALLOC_H_
+#define _SYS_MALLOC_H_
+
+#define KMEMSTATS
+
+/*
+ * flags to malloc
+ */
+#define M_WAITOK 0x0000
+#define M_NOWAIT 0x0001
+
+/*
+ * Types of memory to be allocated
+ */
+#define M_FREE 0 /* should be on free list */
+#define M_MBUF 1 /* mbuf */
+#define M_DEVBUF 2 /* device driver memory */
+#define M_SOCKET 3 /* socket structure */
+#define M_PCB 4 /* protocol control block */
+#define M_RTABLE 5 /* routing tables */
+#define M_HTABLE 6 /* IMP host tables */
+#define M_FTABLE 7 /* fragment reassembly header */
+#define M_ZOMBIE 8 /* zombie proc status */
+#define M_IFADDR 9 /* interface address */
+#define M_SOOPTS 10 /* socket options */
+#define M_SONAME 11 /* socket name */
+#define M_NAMEI 12 /* namei path name buffer */
+#define M_GPROF 13 /* kernel profiling buffer */
+#define M_IOCTLOPS 14 /* ioctl data buffer */
+#define M_MAPMEM 15 /* mapped memory descriptors */
+#define M_CRED 16 /* credentials */
+#define M_PGRP 17 /* process group header */
+#define M_SESSION 18 /* session header */
+#define M_IOV 19 /* large iov's */
+#define M_MOUNT 20 /* vfs mount struct */
+#define M_FHANDLE 21 /* network file handle */
+#define M_NFSREQ 22 /* NFS request header */
+#define M_NFSMNT 23 /* NFS mount structure */
+#define M_NFSNODE 24 /* NFS vnode private part */
+#define M_VNODE 25 /* Dynamically allocated vnodes */
+#define M_CACHE 26 /* Dynamically allocated cache entries */
+#define M_DQUOT 27 /* UFS quota entries */
+#define M_UFSMNT 28 /* UFS mount structure */
+#define M_SHM 29 /* SVID compatible shared memory segments */
+#define M_VMMAP 30 /* VM map structures */
+#define M_VMMAPENT 31 /* VM map entry structures */
+#define M_VMOBJ 32 /* VM object structure */
+#define M_VMOBJHASH 33 /* VM object hash structure */
+#define M_VMPMAP 34 /* VM pmap */
+#define M_VMPVENT 35 /* VM phys-virt mapping entry */
+#define M_VMPAGER 36 /* XXX: VM pager struct */
+#define M_VMPGDATA 37 /* XXX: VM pager private data */
+#define M_FILE 38 /* Open file structure */
+#define M_FILEDESC 39 /* Open file descriptor table */
+#define M_LOCKF 40 /* Byte-range locking structures */
+#define M_PROC 41 /* Proc structures */
+#define M_SUBPROC 42 /* Proc sub-structures */
+#define M_SEGMENT 43 /* Segment for LFS */
+#define M_LFSNODE 44 /* LFS vnode private part */
+#define M_FFSNODE 45 /* FFS vnode private part */
+#define M_MFSNODE 46 /* MFS vnode private part */
+#define M_NQLEASE 47 /* Nqnfs lease */
+#define M_NQMHOST 48 /* Nqnfs host address table */
+#define M_NETADDR 49 /* Export host address structure */
+#define M_NFSSVC 50 /* Nfs server structure */
+#define M_NFSUID 51 /* Nfs uid mapping structure */
+#define M_NFSD 52 /* Nfs server daemon structure */
+#define M_IPMOPTS 53 /* internet multicast options */
+#define M_IPMADDR 54 /* internet multicast address */
+#define M_IFMADDR 55 /* link-level multicast address */
+#define M_MRTABLE 56 /* multicast routing tables */
+#define M_ISOFSMNT 57 /* ISOFS mount structure */
+#define M_ISOFSNODE 58 /* ISOFS vnode private part */
+#define M_TEMP 74 /* misc temporary data buffers */
+#define M_TTYS 75 /* tty data structures */
+#define M_LAST 76 /* Must be last type + 1 */
+
+#define INITKMEMNAMES { \
+ "free", /* 0 M_FREE */ \
+ "mbuf", /* 1 M_MBUF */ \
+ "devbuf", /* 2 M_DEVBUF */ \
+ "socket", /* 3 M_SOCKET */ \
+ "pcb", /* 4 M_PCB */ \
+ "routetbl", /* 5 M_RTABLE */ \
+ "hosttbl", /* 6 M_HTABLE */ \
+ "fragtbl", /* 7 M_FTABLE */ \
+ "zombie", /* 8 M_ZOMBIE */ \
+ "ifaddr", /* 9 M_IFADDR */ \
+ "soopts", /* 10 M_SOOPTS */ \
+ "soname", /* 11 M_SONAME */ \
+ "namei", /* 12 M_NAMEI */ \
+ "gprof", /* 13 M_GPROF */ \
+ "ioctlops", /* 14 M_IOCTLOPS */ \
+ "mapmem", /* 15 M_MAPMEM */ \
+ "cred", /* 16 M_CRED */ \
+ "pgrp", /* 17 M_PGRP */ \
+ "session", /* 18 M_SESSION */ \
+ "iov", /* 19 M_IOV */ \
+ "mount", /* 20 M_MOUNT */ \
+ "fhandle", /* 21 M_FHANDLE */ \
+ "NFS req", /* 22 M_NFSREQ */ \
+ "NFS mount", /* 23 M_NFSMNT */ \
+ "NFS node", /* 24 M_NFSNODE */ \
+ "vnodes", /* 25 M_VNODE */ \
+ "namecache", /* 26 M_CACHE */ \
+ "UFS quota", /* 27 M_DQUOT */ \
+ "UFS mount", /* 28 M_UFSMNT */ \
+ "shm", /* 29 M_SHM */ \
+ "VM map", /* 30 M_VMMAP */ \
+ "VM mapent", /* 31 M_VMMAPENT */ \
+ "VM object", /* 32 M_VMOBJ */ \
+ "VM objhash", /* 33 M_VMOBJHASH */ \
+ "VM pmap", /* 34 M_VMPMAP */ \
+ "VM pvmap", /* 35 M_VMPVENT */ \
+ "VM pager", /* 36 M_VMPAGER */ \
+ "VM pgdata", /* 37 M_VMPGDATA */ \
+ "file", /* 38 M_FILE */ \
+ "file desc", /* 39 M_FILEDESC */ \
+ "lockf", /* 40 M_LOCKF */ \
+ "proc", /* 41 M_PROC */ \
+ "subproc", /* 42 M_SUBPROC */ \
+ "LFS segment", /* 43 M_SEGMENT */ \
+ "LFS node", /* 44 M_LFSNODE */ \
+ "FFS node", /* 45 M_FFSNODE */ \
+ "MFS node", /* 46 M_MFSNODE */ \
+ "NQNFS Lease", /* 47 M_NQLEASE */ \
+ "NQNFS Host", /* 48 M_NQMHOST */ \
+ "Export Host", /* 49 M_NETADDR */ \
+ "NFS srvsock", /* 50 M_NFSSVC */ \
+ "NFS uid", /* 51 M_NFSUID */ \
+ "NFS daemon", /* 52 M_NFSD */ \
+ "ip_moptions", /* 53 M_IPMOPTS */ \
+ "in_multi", /* 54 M_IPMADDR */ \
+ "ether_multi", /* 55 M_IFMADDR */ \
+ "mrt", /* 56 M_MRTABLE */ \
+ "ISOFS mount", /* 57 M_ISOFSMNT */ \
+ "ISOFS node", /* 58 M_ISOFSNODE */ \
+ NULL, NULL, NULL, NULL, NULL, \
+ NULL, NULL, NULL, NULL, NULL, \
+ NULL, NULL, NULL, NULL, NULL, \
+ "temp", /* 74 M_TEMP */ \
+ "ttys", /* 75 M_TTYS */ \
+}
+
+struct kmemstats {
+ long ks_inuse; /* # of packets of this type currently in use */
+ long ks_calls; /* total packets of this type ever allocated */
+ long ks_memuse; /* total memory held in bytes */
+ u_short ks_limblocks; /* number of times blocked for hitting limit */
+ u_short ks_mapblocks; /* number of times blocked for kernel map */
+ long ks_maxused; /* maximum number ever used */
+ long ks_limit; /* most that are allowed to exist */
+ long ks_size; /* sizes of this thing that are allocated */
+ long ks_spare;
+};
+
+/*
+ * Array of descriptors that describe the contents of each page
+ */
+struct kmemusage {
+ short ku_indx; /* bucket index */
+ union {
+ u_short freecnt;/* for small allocations, free pieces in page */
+ u_short pagecnt;/* for large allocations, pages alloced */
+ } ku_un;
+};
+#define ku_freecnt ku_un.freecnt
+#define ku_pagecnt ku_un.pagecnt
+
+/*
+ * Set of buckets for each size of memory block that is retained
+ */
+struct kmembuckets {
+ caddr_t kb_next; /* list of free blocks */
+ caddr_t kb_last; /* last free block */
+ long kb_calls; /* total calls to allocate this size */
+ long kb_total; /* total number of blocks allocated */
+ long kb_totalfree; /* # of free elements in this bucket */
+ long kb_elmpercl; /* # of elements in this sized allocation */
+ long kb_highwat; /* high water mark */
+ long kb_couldfree; /* over high water mark and could free */
+};
+
+#ifdef KERNEL
+#define MINALLOCSIZE (1 << MINBUCKET)
+#define BUCKETINDX(size) \
+ (size) <= (MINALLOCSIZE * 128) \
+ ? (size) <= (MINALLOCSIZE * 8) \
+ ? (size) <= (MINALLOCSIZE * 2) \
+ ? (size) <= (MINALLOCSIZE * 1) \
+ ? (MINBUCKET + 0) \
+ : (MINBUCKET + 1) \
+ : (size) <= (MINALLOCSIZE * 4) \
+ ? (MINBUCKET + 2) \
+ : (MINBUCKET + 3) \
+ : (size) <= (MINALLOCSIZE* 32) \
+ ? (size) <= (MINALLOCSIZE * 16) \
+ ? (MINBUCKET + 4) \
+ : (MINBUCKET + 5) \
+ : (size) <= (MINALLOCSIZE * 64) \
+ ? (MINBUCKET + 6) \
+ : (MINBUCKET + 7) \
+ : (size) <= (MINALLOCSIZE * 2048) \
+ ? (size) <= (MINALLOCSIZE * 512) \
+ ? (size) <= (MINALLOCSIZE * 256) \
+ ? (MINBUCKET + 8) \
+ : (MINBUCKET + 9) \
+ : (size) <= (MINALLOCSIZE * 1024) \
+ ? (MINBUCKET + 10) \
+ : (MINBUCKET + 11) \
+ : (size) <= (MINALLOCSIZE * 8192) \
+ ? (size) <= (MINALLOCSIZE * 4096) \
+ ? (MINBUCKET + 12) \
+ : (MINBUCKET + 13) \
+ : (size) <= (MINALLOCSIZE * 16384) \
+ ? (MINBUCKET + 14) \
+ : (MINBUCKET + 15)
+
+/*
+ * Turn virtual addresses into kmem map indicies
+ */
+#define kmemxtob(alloc) (kmembase + (alloc) * NBPG)
+#define btokmemx(addr) (((caddr_t)(addr) - kmembase) / NBPG)
+#define btokup(addr) (&kmemusage[((caddr_t)(addr) - kmembase) >> CLSHIFT])
+
+/*
+ * Macro versions for the usual cases of malloc/free
+ */
+#if defined(KMEMSTATS) || defined(DIAGNOSTIC)
+#define MALLOC(space, cast, size, type, flags) \
+ (space) = (cast)malloc((u_long)(size), type, flags)
+#define FREE(addr, type) free((caddr_t)(addr), type)
+
+#else /* do not collect statistics */
+#define MALLOC(space, cast, size, type, flags) { \
+ register struct kmembuckets *kbp = &bucket[BUCKETINDX(size)]; \
+ long s = splimp(); \
+ if (kbp->kb_next == NULL) { \
+ (space) = (cast)malloc((u_long)(size), type, flags); \
+ } else { \
+ (space) = (cast)kbp->kb_next; \
+ kbp->kb_next = *(caddr_t *)(space); \
+ } \
+ splx(s); \
+}
+
+#define FREE(addr, type) { \
+ register struct kmembuckets *kbp; \
+ register struct kmemusage *kup = btokup(addr); \
+ long s = splimp(); \
+ if (1 << kup->ku_indx > MAXALLOCSAVE) { \
+ free((caddr_t)(addr), type); \
+ } else { \
+ kbp = &bucket[kup->ku_indx]; \
+ if (kbp->kb_next == NULL) \
+ kbp->kb_next = (caddr_t)(addr); \
+ else \
+ *(caddr_t *)(kbp->kb_last) = (caddr_t)(addr); \
+ *(caddr_t *)(addr) = NULL; \
+ kbp->kb_last = (caddr_t)(addr); \
+ } \
+ splx(s); \
+}
+#endif /* do not collect statistics */
+
+extern struct kmemstats kmemstats[];
+extern struct kmemusage *kmemusage;
+extern char *kmembase;
+extern struct kmembuckets bucket[];
+extern void *malloc __P((unsigned long size, int type, int flags));
+extern void free __P((void *addr, int type));
+#endif /* KERNEL */
+#endif /* !_SYS_MALLOC_H_ */
diff --git a/sys/sys/map.h b/sys/sys/map.h
new file mode 100644
index 0000000..6cec4b5
--- /dev/null
+++ b/sys/sys/map.h
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)map.h 8.3 (Berkeley) 1/26/94
+ */
+
+/*
+ * Resource allocation maps.
+ *
+ * Associated routines manage sub-allocation of an address space using
+ * an array of segment descriptors. The first element of this array
+ * is a map structure, describing the arrays extent and the name
+ * of the controlled object. Each additional structure represents
+ * a free segment of the address space.
+ *
+ * A call to rminit initializes a resource map and may also be used
+ * to free some address space for the map. Subsequent calls to rmalloc
+ * and rmfree allocate and free space in the resource map. If the resource
+ * map becomes too fragmented to be described in the available space,
+ * then some of the resource is discarded. This may lead to critical
+ * shortages, but is better than not checking (as the previous versions
+ * of these routines did) or giving up and calling panic(). The routines
+ * could use linked lists and call a memory allocator when they run
+ * out of space, but that would not solve the out of space problem when
+ * called at interrupt time.
+ *
+ * N.B.: The address 0 in the resource address space is not available
+ * as it is used internally by the resource map routines.
+ */
+struct map {
+ struct mapent *m_limit; /* address of last slot in map */
+ char *m_name; /* name of resource, for messages */
+};
+
+struct mapent {
+ long m_size; /* size of this segment of the map */
+ long m_addr; /* start of segment */
+};
+
+#ifdef KERNEL
+#define ARGMAPSIZE 16
+struct map *kmemmap, *mbmap, *swapmap;
+int nswapmap;
+
+long rmalloc __P((struct map *, long));
+void rmfree __P((struct map *, long, long));
+void rminit __P((struct map *, long, long, char *, int));
+#endif
diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h
new file mode 100644
index 0000000..f3ea7ed
--- /dev/null
+++ b/sys/sys/mbuf.h
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mbuf.h 8.3 (Berkeley) 1/21/94
+ */
+
+#ifndef M_WAITOK
+#include <sys/malloc.h>
+#endif
+
+/*
+ * Mbufs are of a single size, MSIZE (machine/machparam.h), which
+ * includes overhead. An mbuf may add a single "mbuf cluster" of size
+ * MCLBYTES (also in machine/machparam.h), which has no additional overhead
+ * and is used instead of the internal data area; this is done when
+ * at least MINCLSIZE of data must be stored.
+ */
+
+#define MLEN (MSIZE - sizeof(struct m_hdr)) /* normal data len */
+#define MHLEN (MLEN - sizeof(struct pkthdr)) /* data len w/pkthdr */
+
+#define MINCLSIZE (MHLEN + MLEN) /* smallest amount to put in cluster */
+#define M_MAXCOMPRESS (MHLEN / 2) /* max amount to copy for compression */
+
+/*
+ * Macros for type conversion
+ * mtod(m,t) - convert mbuf pointer to data pointer of correct type
+ * dtom(x) - convert data pointer within mbuf to mbuf pointer (XXX)
+ * mtocl(x) - convert pointer within cluster to cluster index #
+ * cltom(x) - convert cluster # to ptr to beginning of cluster
+ */
+#define mtod(m,t) ((t)((m)->m_data))
+#define dtom(x) ((struct mbuf *)((int)(x) & ~(MSIZE-1)))
+#define mtocl(x) (((u_int)(x) - (u_int)mbutl) >> MCLSHIFT)
+#define cltom(x) ((caddr_t)((u_int)mbutl + ((u_int)(x) << MCLSHIFT)))
+
+/* header at beginning of each mbuf: */
+struct m_hdr {
+ struct mbuf *mh_next; /* next buffer in chain */
+ struct mbuf *mh_nextpkt; /* next chain in queue/record */
+ int mh_len; /* amount of data in this mbuf */
+ caddr_t mh_data; /* location of data */
+ short mh_type; /* type of data in this mbuf */
+ short mh_flags; /* flags; see below */
+};
+
+/* record/packet header in first mbuf of chain; valid if M_PKTHDR set */
+struct pkthdr {
+ int len; /* total packet length */
+ struct ifnet *rcvif; /* rcv interface */
+};
+
+/* description of external storage mapped into mbuf, valid if M_EXT set */
+struct m_ext {
+ caddr_t ext_buf; /* start of buffer */
+ void (*ext_free)(); /* free routine if not the usual */
+ u_int ext_size; /* size of buffer, for ext_free */
+};
+
+struct mbuf {
+ struct m_hdr m_hdr;
+ union {
+ struct {
+ struct pkthdr MH_pkthdr; /* M_PKTHDR set */
+ union {
+ struct m_ext MH_ext; /* M_EXT set */
+ char MH_databuf[MHLEN];
+ } MH_dat;
+ } MH;
+ char M_databuf[MLEN]; /* !M_PKTHDR, !M_EXT */
+ } M_dat;
+};
+#define m_next m_hdr.mh_next
+#define m_len m_hdr.mh_len
+#define m_data m_hdr.mh_data
+#define m_type m_hdr.mh_type
+#define m_flags m_hdr.mh_flags
+#define m_nextpkt m_hdr.mh_nextpkt
+#define m_act m_nextpkt
+#define m_pkthdr M_dat.MH.MH_pkthdr
+#define m_ext M_dat.MH.MH_dat.MH_ext
+#define m_pktdat M_dat.MH.MH_dat.MH_databuf
+#define m_dat M_dat.M_databuf
+
+/* mbuf flags */
+#define M_EXT 0x0001 /* has associated external storage */
+#define M_PKTHDR 0x0002 /* start of record */
+#define M_EOR 0x0004 /* end of record */
+
+/* mbuf pkthdr flags, also in m_flags */
+#define M_BCAST 0x0100 /* send/received as link-level broadcast */
+#define M_MCAST 0x0200 /* send/received as link-level multicast */
+
+/* flags copied when copying m_pkthdr */
+#define M_COPYFLAGS (M_PKTHDR|M_EOR|M_BCAST|M_MCAST)
+
+/* mbuf types */
+#define MT_FREE 0 /* should be on free list */
+#define MT_DATA 1 /* dynamic (data) allocation */
+#define MT_HEADER 2 /* packet header */
+#define MT_SOCKET 3 /* socket structure */
+#define MT_PCB 4 /* protocol control block */
+#define MT_RTABLE 5 /* routing tables */
+#define MT_HTABLE 6 /* IMP host tables */
+#define MT_ATABLE 7 /* address resolution tables */
+#define MT_SONAME 8 /* socket name */
+#define MT_SOOPTS 10 /* socket options */
+#define MT_FTABLE 11 /* fragment reassembly header */
+#define MT_RIGHTS 12 /* access rights */
+#define MT_IFADDR 13 /* interface address */
+#define MT_CONTROL 14 /* extra-data protocol message */
+#define MT_OOBDATA 15 /* expedited data */
+
+/* flags to m_get/MGET */
+#define M_DONTWAIT M_NOWAIT
+#define M_WAIT M_WAITOK
+
+/*
+ * mbuf utility macros:
+ *
+ * MBUFLOCK(code)
+ * prevents a section of code from from being interrupted by network
+ * drivers.
+ */
+#define MBUFLOCK(code) \
+ { int ms = splimp(); \
+ { code } \
+ splx(ms); \
+ }
+
+/*
+ * mbuf allocation/deallocation macros:
+ *
+ * MGET(struct mbuf *m, int how, int type)
+ * allocates an mbuf and initializes it to contain internal data.
+ *
+ * MGETHDR(struct mbuf *m, int how, int type)
+ * allocates an mbuf and initializes it to contain a packet header
+ * and internal data.
+ */
+#define MGET(m, how, type) { \
+ MALLOC((m), struct mbuf *, MSIZE, mbtypes[type], (how)); \
+ if (m) { \
+ (m)->m_type = (type); \
+ MBUFLOCK(mbstat.m_mtypes[type]++;) \
+ (m)->m_next = (struct mbuf *)NULL; \
+ (m)->m_nextpkt = (struct mbuf *)NULL; \
+ (m)->m_data = (m)->m_dat; \
+ (m)->m_flags = 0; \
+ } else \
+ (m) = m_retry((how), (type)); \
+}
+
+#define MGETHDR(m, how, type) { \
+ MALLOC((m), struct mbuf *, MSIZE, mbtypes[type], (how)); \
+ if (m) { \
+ (m)->m_type = (type); \
+ MBUFLOCK(mbstat.m_mtypes[type]++;) \
+ (m)->m_next = (struct mbuf *)NULL; \
+ (m)->m_nextpkt = (struct mbuf *)NULL; \
+ (m)->m_data = (m)->m_pktdat; \
+ (m)->m_flags = M_PKTHDR; \
+ } else \
+ (m) = m_retryhdr((how), (type)); \
+}
+
+/*
+ * Mbuf cluster macros.
+ * MCLALLOC(caddr_t p, int how) allocates an mbuf cluster.
+ * MCLGET adds such clusters to a normal mbuf;
+ * the flag M_EXT is set upon success.
+ * MCLFREE releases a reference to a cluster allocated by MCLALLOC,
+ * freeing the cluster if the reference count has reached 0.
+ *
+ * Normal mbuf clusters are normally treated as character arrays
+ * after allocation, but use the first word of the buffer as a free list
+ * pointer while on the free list.
+ */
+union mcluster {
+ union mcluster *mcl_next;
+ char mcl_buf[MCLBYTES];
+};
+
+#define MCLALLOC(p, how) \
+ MBUFLOCK( \
+ if (mclfree == 0) \
+ (void)m_clalloc(1, (how)); \
+ if ((p) = (caddr_t)mclfree) { \
+ ++mclrefcnt[mtocl(p)]; \
+ mbstat.m_clfree--; \
+ mclfree = ((union mcluster *)(p))->mcl_next; \
+ } \
+ )
+
+#define MCLGET(m, how) \
+ { MCLALLOC((m)->m_ext.ext_buf, (how)); \
+ if ((m)->m_ext.ext_buf != NULL) { \
+ (m)->m_data = (m)->m_ext.ext_buf; \
+ (m)->m_flags |= M_EXT; \
+ (m)->m_ext.ext_size = MCLBYTES; \
+ } \
+ }
+
+#define MCLFREE(p) \
+ MBUFLOCK ( \
+ if (--mclrefcnt[mtocl(p)] == 0) { \
+ ((union mcluster *)(p))->mcl_next = mclfree; \
+ mclfree = (union mcluster *)(p); \
+ mbstat.m_clfree++; \
+ } \
+ )
+
+/*
+ * MFREE(struct mbuf *m, struct mbuf *n)
+ * Free a single mbuf and associated external storage.
+ * Place the successor, if any, in n.
+ */
+#ifdef notyet
+#define MFREE(m, n) \
+ { MBUFLOCK(mbstat.m_mtypes[(m)->m_type]--;) \
+ if ((m)->m_flags & M_EXT) { \
+ if ((m)->m_ext.ext_free) \
+ (*((m)->m_ext.ext_free))((m)->m_ext.ext_buf, \
+ (m)->m_ext.ext_size); \
+ else \
+ MCLFREE((m)->m_ext.ext_buf); \
+ } \
+ (n) = (m)->m_next; \
+ FREE((m), mbtypes[(m)->m_type]); \
+ }
+#else /* notyet */
+#define MFREE(m, nn) \
+ { MBUFLOCK(mbstat.m_mtypes[(m)->m_type]--;) \
+ if ((m)->m_flags & M_EXT) { \
+ MCLFREE((m)->m_ext.ext_buf); \
+ } \
+ (nn) = (m)->m_next; \
+ FREE((m), mbtypes[(m)->m_type]); \
+ }
+#endif
+
+/*
+ * Copy mbuf pkthdr from from to to.
+ * from must have M_PKTHDR set, and to must be empty.
+ */
+#define M_COPY_PKTHDR(to, from) { \
+ (to)->m_pkthdr = (from)->m_pkthdr; \
+ (to)->m_flags = (from)->m_flags & M_COPYFLAGS; \
+ (to)->m_data = (to)->m_pktdat; \
+}
+
+/*
+ * Set the m_data pointer of a newly-allocated mbuf (m_get/MGET) to place
+ * an object of the specified size at the end of the mbuf, longword aligned.
+ */
+#define M_ALIGN(m, len) \
+ { (m)->m_data += (MLEN - (len)) &~ (sizeof(long) - 1); }
+/*
+ * As above, for mbufs allocated with m_gethdr/MGETHDR
+ * or initialized by M_COPY_PKTHDR.
+ */
+#define MH_ALIGN(m, len) \
+ { (m)->m_data += (MHLEN - (len)) &~ (sizeof(long) - 1); }
+
+/*
+ * Compute the amount of space available
+ * before the current start of data in an mbuf.
+ */
+#define M_LEADINGSPACE(m) \
+ ((m)->m_flags & M_EXT ? /* (m)->m_data - (m)->m_ext.ext_buf */ 0 : \
+ (m)->m_flags & M_PKTHDR ? (m)->m_data - (m)->m_pktdat : \
+ (m)->m_data - (m)->m_dat)
+
+/*
+ * Compute the amount of space available
+ * after the end of data in an mbuf.
+ */
+#define M_TRAILINGSPACE(m) \
+ ((m)->m_flags & M_EXT ? (m)->m_ext.ext_buf + (m)->m_ext.ext_size - \
+ ((m)->m_data + (m)->m_len) : \
+ &(m)->m_dat[MLEN] - ((m)->m_data + (m)->m_len))
+
+/*
+ * Arrange to prepend space of size plen to mbuf m.
+ * If a new mbuf must be allocated, how specifies whether to wait.
+ * If how is M_DONTWAIT and allocation fails, the original mbuf chain
+ * is freed and m is set to NULL.
+ */
+#define M_PREPEND(m, plen, how) { \
+ if (M_LEADINGSPACE(m) >= (plen)) { \
+ (m)->m_data -= (plen); \
+ (m)->m_len += (plen); \
+ } else \
+ (m) = m_prepend((m), (plen), (how)); \
+ if ((m) && (m)->m_flags & M_PKTHDR) \
+ (m)->m_pkthdr.len += (plen); \
+}
+
+/* change mbuf to new type */
+#define MCHTYPE(m, t) { \
+ MBUFLOCK(mbstat.m_mtypes[(m)->m_type]--; mbstat.m_mtypes[t]++;) \
+ (m)->m_type = t;\
+}
+
+/* length to m_copy to copy all */
+#define M_COPYALL 1000000000
+
+/* compatiblity with 4.3 */
+#define m_copy(m, o, l) m_copym((m), (o), (l), M_DONTWAIT)
+
+/*
+ * Mbuf statistics.
+ */
+struct mbstat {
+ u_long m_mbufs; /* mbufs obtained from page pool */
+ u_long m_clusters; /* clusters obtained from page pool */
+ u_long m_spare; /* spare field */
+ u_long m_clfree; /* free clusters */
+ u_long m_drops; /* times failed to find space */
+ u_long m_wait; /* times waited for space */
+ u_long m_drain; /* times drained protocols for space */
+ u_short m_mtypes[256]; /* type specific mbuf allocations */
+};
+
+#ifdef KERNEL
+extern struct mbuf *mbutl; /* virtual address of mclusters */
+extern char *mclrefcnt; /* cluster reference counts */
+struct mbstat mbstat;
+extern int nmbclusters;
+union mcluster *mclfree;
+int max_linkhdr; /* largest link-level header */
+int max_protohdr; /* largest protocol header */
+int max_hdr; /* largest link+protocol header */
+int max_datalen; /* MHLEN - max_hdr */
+extern int mbtypes[]; /* XXX */
+
+struct mbuf *m_copym __P((struct mbuf *, int, int, int));
+struct mbuf *m_free __P((struct mbuf *));
+struct mbuf *m_get __P((int, int));
+struct mbuf *m_getclr __P((int, int));
+struct mbuf *m_gethdr __P((int, int));
+struct mbuf *m_prepend __P((struct mbuf *, int, int));
+struct mbuf *m_pullup __P((struct mbuf *, int));
+struct mbuf *m_retry __P((int, int));
+struct mbuf *m_retryhdr __P((int, int));
+int m_clalloc __P((int, int));
+void m_copyback __P((struct mbuf *, int, int, caddr_t));
+void m_freem __P((struct mbuf *));
+
+#ifdef MBTYPES
+int mbtypes[] = { /* XXX */
+ M_FREE, /* MT_FREE 0 should be on free list */
+ M_MBUF, /* MT_DATA 1 dynamic (data) allocation */
+ M_MBUF, /* MT_HEADER 2 packet header */
+ M_SOCKET, /* MT_SOCKET 3 socket structure */
+ M_PCB, /* MT_PCB 4 protocol control block */
+ M_RTABLE, /* MT_RTABLE 5 routing tables */
+ M_HTABLE, /* MT_HTABLE 6 IMP host tables */
+ 0, /* MT_ATABLE 7 address resolution tables */
+ M_MBUF, /* MT_SONAME 8 socket name */
+ 0, /* 9 */
+ M_SOOPTS, /* MT_SOOPTS 10 socket options */
+ M_FTABLE, /* MT_FTABLE 11 fragment reassembly header */
+ M_MBUF, /* MT_RIGHTS 12 access rights */
+ M_IFADDR, /* MT_IFADDR 13 interface address */
+ M_MBUF, /* MT_CONTROL 14 extra-data protocol message */
+ M_MBUF, /* MT_OOBDATA 15 expedited data */
+#ifdef DATAKIT
+ 25, 26, 27, 28, 29, 30, 31, 32 /* datakit ugliness */
+#endif
+};
+#endif
+#endif
diff --git a/sys/sys/mman.h b/sys/sys/mman.h
new file mode 100644
index 0000000..b3951c2
--- /dev/null
+++ b/sys/sys/mman.h
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mman.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * Protections are chosen from these bits, or-ed together
+ */
+#define PROT_READ 0x01 /* pages can be read */
+#define PROT_WRITE 0x02 /* pages can be written */
+#define PROT_EXEC 0x04 /* pages can be executed */
+
+/*
+ * Flags contain sharing type and options.
+ * Sharing types; choose one.
+ */
+#define MAP_SHARED 0x0001 /* share changes */
+#define MAP_PRIVATE 0x0002 /* changes are private */
+#define MAP_COPY 0x0004 /* "copy" region at mmap time */
+
+/*
+ * Other flags
+ */
+#define MAP_FIXED 0x0010 /* map addr must be exactly as requested */
+#define MAP_RENAME 0x0020 /* Sun: rename private pages to file */
+#define MAP_NORESERVE 0x0040 /* Sun: don't reserve needed swap area */
+#define MAP_INHERIT 0x0080 /* region is retained after exec */
+#define MAP_NOEXTEND 0x0100 /* for MAP_FILE, don't change file size */
+#define MAP_HASSEMAPHORE 0x0200 /* region may contain semaphores */
+
+/*
+ * Mapping type; default is map from file.
+ */
+#define MAP_ANON 0x1000 /* allocated from memory, swap space */
+
+/*
+ * Advice to madvise
+ */
+#define MADV_NORMAL 0 /* no further special treatment */
+#define MADV_RANDOM 1 /* expect random page references */
+#define MADV_SEQUENTIAL 2 /* expect sequential page references */
+#define MADV_WILLNEED 3 /* will need these pages */
+#define MADV_DONTNEED 4 /* dont need these pages */
+
+#ifndef KERNEL
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+/* Some of these int's should probably be size_t's */
+caddr_t mmap __P((caddr_t, size_t, int, int, int, off_t));
+int mprotect __P((caddr_t, size_t, int));
+int munmap __P((caddr_t, size_t));
+int msync __P((caddr_t, size_t));
+int mlock __P((caddr_t, size_t));
+int munlock __P((caddr_t, size_t));
+__END_DECLS
+
+#endif /* !KERNEL */
diff --git a/sys/sys/mount.h b/sys/sys/mount.h
new file mode 100644
index 0000000..4561675
--- /dev/null
+++ b/sys/sys/mount.h
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mount.h 8.13 (Berkeley) 3/27/94
+ */
+
+#ifndef KERNEL
+#include <sys/ucred.h>
+#endif
+#include <sys/queue.h>
+
+typedef struct { long val[2]; } fsid_t; /* file system id type */
+
+/*
+ * File identifier.
+ * These are unique per filesystem on a single machine.
+ */
+#define MAXFIDSZ 16
+
+struct fid {
+ u_short fid_len; /* length of data in bytes */
+ u_short fid_reserved; /* force longword alignment */
+ char fid_data[MAXFIDSZ]; /* data (variable length) */
+};
+
+/*
+ * file system statistics
+ */
+
+#define MNAMELEN 90 /* length of buffer for returned name */
+
+struct statfs {
+ short f_type; /* type of filesystem (see below) */
+ short f_flags; /* copy of mount flags */
+ long f_bsize; /* fundamental file system block size */
+ long f_iosize; /* optimal transfer block size */
+ long f_blocks; /* total data blocks in file system */
+ long f_bfree; /* free blocks in fs */
+ long f_bavail; /* free blocks avail to non-superuser */
+ long f_files; /* total file nodes in file system */
+ long f_ffree; /* free file nodes in fs */
+ fsid_t f_fsid; /* file system id */
+ long f_spare[9]; /* spare for later */
+ char f_mntonname[MNAMELEN]; /* directory on which mounted */
+ char f_mntfromname[MNAMELEN];/* mounted filesystem */
+};
+
+/*
+ * File system types.
+ */
+#define MOUNT_NONE 0
+#define MOUNT_UFS 1 /* Fast Filesystem */
+#define MOUNT_NFS 2 /* Sun-compatible Network Filesystem */
+#define MOUNT_MFS 3 /* Memory-based Filesystem */
+#define MOUNT_MSDOS 4 /* MS/DOS Filesystem */
+#define MOUNT_LFS 5 /* Log-based Filesystem */
+#define MOUNT_LOFS 6 /* Loopback Filesystem */
+#define MOUNT_FDESC 7 /* File Descriptor Filesystem */
+#define MOUNT_PORTAL 8 /* Portal Filesystem */
+#define MOUNT_NULL 9 /* Minimal Filesystem Layer */
+#define MOUNT_UMAP 10 /* User/Group Identifer Remapping Filesystem */
+#define MOUNT_KERNFS 11 /* Kernel Information Filesystem */
+#define MOUNT_PROCFS 12 /* /proc Filesystem */
+#define MOUNT_AFS 13 /* Andrew Filesystem */
+#define MOUNT_CD9660 14 /* ISO9660 (aka CDROM) Filesystem */
+#define MOUNT_UNION 15 /* Union (translucent) Filesystem */
+#define MOUNT_MAXTYPE 15
+
+#define INITMOUNTNAMES { \
+ "none", /* 0 MOUNT_NONE */ \
+ "ufs", /* 1 MOUNT_UFS */ \
+ "nfs", /* 2 MOUNT_NFS */ \
+ "mfs", /* 3 MOUNT_MFS */ \
+ "msdos", /* 4 MOUNT_MSDOS */ \
+ "lfs", /* 5 MOUNT_LFS */ \
+ "lofs", /* 6 MOUNT_LOFS */ \
+ "fdesc", /* 7 MOUNT_FDESC */ \
+ "portal", /* 8 MOUNT_PORTAL */ \
+ "null", /* 9 MOUNT_NULL */ \
+ "umap", /* 10 MOUNT_UMAP */ \
+ "kernfs", /* 11 MOUNT_KERNFS */ \
+ "procfs", /* 12 MOUNT_PROCFS */ \
+ "afs", /* 13 MOUNT_AFS */ \
+ "iso9660fs", /* 14 MOUNT_CD9660 */ \
+ "union", /* 15 MOUNT_UNION */ \
+ 0, /* 16 MOUNT_SPARE */ \
+}
+
+/*
+ * Structure per mounted file system. Each mounted file system has an
+ * array of operations and an instance record. The file systems are
+ * put on a doubly linked list.
+ */
+LIST_HEAD(vnodelst, vnode);
+
+struct mount {
+ TAILQ_ENTRY(mount) mnt_list; /* mount list */
+ struct vfsops *mnt_op; /* operations on fs */
+ struct vnode *mnt_vnodecovered; /* vnode we mounted on */
+ struct vnodelst mnt_vnodelist; /* list of vnodes this mount */
+ int mnt_flag; /* flags */
+ int mnt_maxsymlinklen; /* max size of short symlink */
+ struct statfs mnt_stat; /* cache of filesystem stats */
+ qaddr_t mnt_data; /* private data */
+};
+
+/*
+ * Mount flags.
+ *
+ * Unmount uses MNT_FORCE flag.
+ */
+#define MNT_RDONLY 0x00000001 /* read only filesystem */
+#define MNT_SYNCHRONOUS 0x00000002 /* file system written synchronously */
+#define MNT_NOEXEC 0x00000004 /* can't exec from filesystem */
+#define MNT_NOSUID 0x00000008 /* don't honor setuid bits on fs */
+#define MNT_NODEV 0x00000010 /* don't interpret special files */
+#define MNT_UNION 0x00000020 /* union with underlying filesystem */
+#define MNT_ASYNC 0x00000040 /* file system written asynchronously */
+
+/*
+ * exported mount flags.
+ */
+#define MNT_EXRDONLY 0x00000080 /* exported read only */
+#define MNT_EXPORTED 0x00000100 /* file system is exported */
+#define MNT_DEFEXPORTED 0x00000200 /* exported to the world */
+#define MNT_EXPORTANON 0x00000400 /* use anon uid mapping for everyone */
+#define MNT_EXKERB 0x00000800 /* exported with Kerberos uid mapping */
+
+/*
+ * Flags set by internal operations.
+ */
+#define MNT_LOCAL 0x00001000 /* filesystem is stored locally */
+#define MNT_QUOTA 0x00002000 /* quotas are enabled on filesystem */
+#define MNT_ROOTFS 0x00004000 /* identifies the root filesystem */
+#define MNT_USER 0x00008000 /* mounted by a user */
+
+/*
+ * Mask of flags that are visible to statfs()
+ */
+#define MNT_VISFLAGMASK 0x0000ffff
+
+/*
+ * filesystem control flags.
+ *
+ * MNT_MLOCK lock the mount entry so that name lookup cannot proceed
+ * past the mount point. This keeps the subtree stable during mounts
+ * and unmounts.
+ */
+#define MNT_UPDATE 0x00010000 /* not a real mount, just an update */
+#define MNT_DELEXPORT 0x00020000 /* delete export host lists */
+#define MNT_RELOAD 0x00040000 /* reload filesystem data */
+#define MNT_FORCE 0x00080000 /* force unmount or readonly change */
+#define MNT_MLOCK 0x00100000 /* lock so that subtree is stable */
+#define MNT_MWAIT 0x00200000 /* someone is waiting for lock */
+#define MNT_MPBUSY 0x00400000 /* scan of mount point in progress */
+#define MNT_MPWANT 0x00800000 /* waiting for mount point */
+#define MNT_UNMOUNT 0x01000000 /* unmount in progress */
+#define MNT_WANTRDWR 0x02000000 /* want upgrade to read/write */
+
+/*
+ * Operations supported on mounted file system.
+ */
+#ifdef KERNEL
+#ifdef __STDC__
+struct nameidata;
+struct mbuf;
+#endif
+
+struct vfsops {
+ int (*vfs_mount) __P((struct mount *mp, char *path, caddr_t data,
+ struct nameidata *ndp, struct proc *p));
+ int (*vfs_start) __P((struct mount *mp, int flags,
+ struct proc *p));
+ int (*vfs_unmount) __P((struct mount *mp, int mntflags,
+ struct proc *p));
+ int (*vfs_root) __P((struct mount *mp, struct vnode **vpp));
+ int (*vfs_quotactl) __P((struct mount *mp, int cmds, uid_t uid,
+ caddr_t arg, struct proc *p));
+ int (*vfs_statfs) __P((struct mount *mp, struct statfs *sbp,
+ struct proc *p));
+ int (*vfs_sync) __P((struct mount *mp, int waitfor,
+ struct ucred *cred, struct proc *p));
+ int (*vfs_vget) __P((struct mount *mp, ino_t ino,
+ struct vnode **vpp));
+ int (*vfs_fhtovp) __P((struct mount *mp, struct fid *fhp,
+ struct mbuf *nam, struct vnode **vpp,
+ int *exflagsp, struct ucred **credanonp));
+ int (*vfs_vptofh) __P((struct vnode *vp, struct fid *fhp));
+ int (*vfs_init) __P((void));
+};
+
+#define VFS_MOUNT(MP, PATH, DATA, NDP, P) \
+ (*(MP)->mnt_op->vfs_mount)(MP, PATH, DATA, NDP, P)
+#define VFS_START(MP, FLAGS, P) (*(MP)->mnt_op->vfs_start)(MP, FLAGS, P)
+#define VFS_UNMOUNT(MP, FORCE, P) (*(MP)->mnt_op->vfs_unmount)(MP, FORCE, P)
+#define VFS_ROOT(MP, VPP) (*(MP)->mnt_op->vfs_root)(MP, VPP)
+#define VFS_QUOTACTL(MP,C,U,A,P) (*(MP)->mnt_op->vfs_quotactl)(MP, C, U, A, P)
+#define VFS_STATFS(MP, SBP, P) (*(MP)->mnt_op->vfs_statfs)(MP, SBP, P)
+#define VFS_SYNC(MP, WAIT, C, P) (*(MP)->mnt_op->vfs_sync)(MP, WAIT, C, P)
+#define VFS_VGET(MP, INO, VPP) (*(MP)->mnt_op->vfs_vget)(MP, INO, VPP)
+#define VFS_FHTOVP(MP, FIDP, NAM, VPP, EXFLG, CRED) \
+ (*(MP)->mnt_op->vfs_fhtovp)(MP, FIDP, NAM, VPP, EXFLG, CRED)
+#define VFS_VPTOFH(VP, FIDP) (*(VP)->v_mount->mnt_op->vfs_vptofh)(VP, FIDP)
+#endif /* KERNEL */
+
+/*
+ * Flags for various system call interfaces.
+ *
+ * waitfor flags to vfs_sync() and getfsstat()
+ */
+#define MNT_WAIT 1
+#define MNT_NOWAIT 2
+
+/*
+ * Generic file handle
+ */
+struct fhandle {
+ fsid_t fh_fsid; /* File system id of mount point */
+ struct fid fh_fid; /* File sys specific id */
+};
+typedef struct fhandle fhandle_t;
+
+#ifdef KERNEL
+#include <net/radix.h>
+#include <sys/socket.h> /* XXX for AF_MAX */
+
+/*
+ * Network address lookup element
+ */
+struct netcred {
+ struct radix_node netc_rnodes[2];
+ int netc_exflags;
+ struct ucred netc_anon;
+};
+
+/*
+ * Network export information
+ */
+struct netexport {
+ struct netcred ne_defexported; /* Default export */
+ struct radix_node_head *ne_rtable[AF_MAX+1]; /* Individual exports */
+};
+#endif /* KERNEL */
+
+/*
+ * Export arguments for local filesystem mount calls.
+ */
+struct export_args {
+ int ex_flags; /* export related flags */
+ uid_t ex_root; /* mapping for root uid */
+ struct ucred ex_anon; /* mapping for anonymous user */
+ struct sockaddr *ex_addr; /* net address to which exported */
+ int ex_addrlen; /* and the net address length */
+ struct sockaddr *ex_mask; /* mask of valid bits in saddr */
+ int ex_masklen; /* and the smask length */
+};
+
+/*
+ * Arguments to mount UFS-based filesystems
+ */
+struct ufs_args {
+ char *fspec; /* block special device to mount */
+ struct export_args export; /* network export information */
+};
+
+#ifdef MFS
+/*
+ * Arguments to mount MFS
+ */
+struct mfs_args {
+ char *fspec; /* name to export for statfs */
+ struct export_args export; /* if exported MFSes are supported */
+ caddr_t base; /* base of file system in memory */
+ u_long size; /* size of file system */
+};
+#endif /* MFS */
+
+#ifdef CD9660
+/*
+ * Arguments to mount ISO 9660 filesystems.
+ */
+struct iso_args {
+ char *fspec; /* block special device to mount */
+ struct export_args export; /* network export info */
+ int flags; /* mounting flags, see below */
+
+};
+#define ISOFSMNT_NORRIP 0x00000001 /* disable Rock Ridge Ext.*/
+#define ISOFSMNT_GENS 0x00000002 /* enable generation numbers */
+#define ISOFSMNT_EXTATT 0x00000004 /* enable extended attributes */
+#endif /* CD9660 */
+
+#ifdef NFS
+/*
+ * File Handle (32 bytes for version 2), variable up to 1024 for version 3
+ */
+union nfsv2fh {
+ fhandle_t fh_generic;
+ u_char fh_bytes[32];
+};
+typedef union nfsv2fh nfsv2fh_t;
+
+/*
+ * Arguments to mount NFS
+ */
+struct nfs_args {
+ struct sockaddr *addr; /* file server address */
+ int addrlen; /* length of address */
+ int sotype; /* Socket type */
+ int proto; /* and Protocol */
+ nfsv2fh_t *fh; /* File handle to be mounted */
+ int flags; /* flags */
+ int wsize; /* write size in bytes */
+ int rsize; /* read size in bytes */
+ int timeo; /* initial timeout in .1 secs */
+ int retrans; /* times to retry send */
+ int maxgrouplist; /* Max. size of group list */
+ int readahead; /* # of blocks to readahead */
+ int leaseterm; /* Term (sec) of lease */
+ int deadthresh; /* Retrans threshold */
+ char *hostname; /* server's name */
+};
+
+
+/*
+ * NFS mount option flags
+ */
+#define NFSMNT_SOFT 0x00000001 /* soft mount (hard is default) */
+#define NFSMNT_WSIZE 0x00000002 /* set write size */
+#define NFSMNT_RSIZE 0x00000004 /* set read size */
+#define NFSMNT_TIMEO 0x00000008 /* set initial timeout */
+#define NFSMNT_RETRANS 0x00000010 /* set number of request retrys */
+#define NFSMNT_MAXGRPS 0x00000020 /* set maximum grouplist size */
+#define NFSMNT_INT 0x00000040 /* allow interrupts on hard mount */
+#define NFSMNT_NOCONN 0x00000080 /* Don't Connect the socket */
+#define NFSMNT_NQNFS 0x00000100 /* Use Nqnfs protocol */
+#define NFSMNT_MYWRITE 0x00000200 /* Assume writes were mine */
+#define NFSMNT_KERB 0x00000400 /* Use Kerberos authentication */
+#define NFSMNT_DUMBTIMR 0x00000800 /* Don't estimate rtt dynamically */
+#define NFSMNT_RDIRALOOK 0x00001000 /* Do lookup with readdir (nqnfs) */
+#define NFSMNT_LEASETERM 0x00002000 /* set lease term (nqnfs) */
+#define NFSMNT_READAHEAD 0x00004000 /* set read ahead */
+#define NFSMNT_DEADTHRESH 0x00008000 /* set dead server retry thresh */
+#define NFSMNT_NQLOOKLEASE 0x00010000 /* Get lease for lookup */
+#define NFSMNT_RESVPORT 0x00020000 /* Allocate a reserved port */
+#define NFSMNT_INTERNAL 0xffe00000 /* Bits set internally */
+#define NFSMNT_MNTD 0x00200000 /* Mnt server for mnt point */
+#define NFSMNT_DISMINPROG 0x00400000 /* Dismount in progress */
+#define NFSMNT_DISMNT 0x00800000 /* Dismounted */
+#define NFSMNT_SNDLOCK 0x01000000 /* Send socket lock */
+#define NFSMNT_WANTSND 0x02000000 /* Want above */
+#define NFSMNT_RCVLOCK 0x04000000 /* Rcv socket lock */
+#define NFSMNT_WANTRCV 0x08000000 /* Want above */
+#define NFSMNT_WAITAUTH 0x10000000 /* Wait for authentication */
+#define NFSMNT_HASAUTH 0x20000000 /* Has authenticator */
+#define NFSMNT_WANTAUTH 0x40000000 /* Wants an authenticator */
+#define NFSMNT_AUTHERR 0x80000000 /* Authentication error */
+#endif /* NFS */
+
+#ifdef KERNEL
+/*
+ * exported vnode operations
+ */
+struct mount *getvfs __P((fsid_t *)); /* return vfs given fsid */
+int vfs_export /* process mount export info */
+ __P((struct mount *, struct netexport *, struct export_args *));
+struct netcred *vfs_export_lookup /* lookup host in fs export list */
+ __P((struct mount *, struct netexport *, struct mbuf *));
+int vfs_lock __P((struct mount *)); /* lock a vfs */
+int vfs_mountedon __P((struct vnode *));/* is a vfs mounted on vp */
+void vfs_unlock __P((struct mount *)); /* unlock a vfs */
+extern TAILQ_HEAD(mntlist, mount) mountlist; /* mounted filesystem list */
+extern struct vfsops *vfssw[]; /* filesystem type table */
+
+#else /* KERNEL */
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int fstatfs __P((int, struct statfs *));
+int getfh __P((const char *, fhandle_t *));
+int getfsstat __P((struct statfs *, long, int));
+int getmntinfo __P((struct statfs **, int));
+int mount __P((int, const char *, int, void *));
+int statfs __P((const char *, struct statfs *));
+int unmount __P((const char *, int));
+__END_DECLS
+
+#endif /* KERNEL */
diff --git a/sys/sys/msgbuf.h b/sys/sys/msgbuf.h
new file mode 100644
index 0000000..57ee0b6
--- /dev/null
+++ b/sys/sys/msgbuf.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 1981, 1984, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)msgbuf.h 8.1 (Berkeley) 6/2/93
+ */
+
+#define MSG_BSIZE (4096 - 3 * sizeof(long))
+struct msgbuf {
+#define MSG_MAGIC 0x063061
+ long msg_magic;
+ long msg_bufx; /* write pointer */
+ long msg_bufr; /* read pointer */
+ char msg_bufc[MSG_BSIZE]; /* buffer */
+};
+#ifdef KERNEL
+struct msgbuf *msgbufp;
+#endif
diff --git a/sys/sys/mtio.h b/sys/sys/mtio.h
new file mode 100644
index 0000000..e5e6965
--- /dev/null
+++ b/sys/sys/mtio.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mtio.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _SYS_MTIO_H_
+#define _SYS_MTIO_H_ 1
+
+/*
+ * Structures and definitions for mag tape io control commands
+ */
+
+/* structure for MTIOCTOP - mag tape op command */
+struct mtop {
+ short mt_op; /* operations defined below */
+ daddr_t mt_count; /* how many of them */
+};
+
+/* operations */
+#define MTWEOF 0 /* write an end-of-file record */
+#define MTFSF 1 /* forward space file */
+#define MTBSF 2 /* backward space file */
+#define MTFSR 3 /* forward space record */
+#define MTBSR 4 /* backward space record */
+#define MTREW 5 /* rewind */
+#define MTOFFL 6 /* rewind and put the drive offline */
+#define MTNOP 7 /* no operation, sets status only */
+#define MTCACHE 8 /* enable controller cache */
+#define MTNOCACHE 9 /* disable controller cache */
+
+#if defined(__FreeBSD__)
+/* Set block size for device. If device is a variable size dev */
+/* a non zero parameter will change the device to a fixed block size */
+/* device with block size set to that of the parameter passed in. */
+/* Resetting the block size to 0 will restore the device to a variable */
+/* block size device. */
+
+#define MTSETBSIZ 10
+
+/* Set density values for device. Thye aredefined in the SCSI II spec */
+/* and range from 0 to 0x17. Sets the value for the openned mode only */
+
+#define MTSETDNSTY 11
+#endif
+
+/* structure for MTIOCGET - mag tape get status command */
+
+struct mtget {
+ short mt_type; /* type of magtape device */
+/* the following two registers are grossly device dependent */
+ short mt_dsreg; /* ``drive status'' register */
+ short mt_erreg; /* ``error'' register */
+/* end device-dependent registers */
+ short mt_resid; /* residual count */
+#if defined (__FreeBSD__)
+ daddr_t mt_blksiz; /* presently operatin blocksize */
+ daddr_t mt_density; /* presently operatin density */
+ daddr_t mt_blksiz0; /* blocksize for mode 0 */
+ daddr_t mt_blksiz1; /* blocksize for mode 1 */
+ daddr_t mt_blksiz2; /* blocksize for mode 2 */
+ daddr_t mt_blksiz3; /* blocksize for mode 3 */
+ daddr_t mt_density0; /* density for mode 0 */
+ daddr_t mt_density1; /* density for mode 1 */
+ daddr_t mt_density2; /* density for mode 2 */
+ daddr_t mt_density3; /* density for mode 3 */
+#endif
+/* the following two are not yet implemented */
+ daddr_t mt_fileno; /* file number of current position */
+ daddr_t mt_blkno; /* block number of current position */
+/* end not yet implemented */
+};
+
+/*
+ * Constants for mt_type byte. These are the same
+ * for controllers compatible with the types listed.
+ */
+#define MT_ISTS 0x01 /* TS-11 */
+#define MT_ISHT 0x02 /* TM03 Massbus: TE16, TU45, TU77 */
+#define MT_ISTM 0x03 /* TM11/TE10 Unibus */
+#define MT_ISMT 0x04 /* TM78/TU78 Massbus */
+#define MT_ISUT 0x05 /* SI TU-45 emulation on Unibus */
+#define MT_ISCPC 0x06 /* SUN */
+#define MT_ISAR 0x07 /* SUN */
+#define MT_ISTMSCP 0x08 /* DEC TMSCP protocol (TU81, TK50) */
+#define MT_ISCY 0x09 /* CCI Cipher */
+#define MT_ISCT 0x0a /* HP 1/4 tape */
+#define MT_ISFHP 0x0b /* HP 7980 1/2 tape */
+#define MT_ISEXABYTE 0x0c /* Exabyte */
+#define MT_ISEXA8200 0x0c /* Exabyte EXB-8200 */
+#define MT_ISEXA8500 0x0d /* Exabyte EXB-8500 */
+#define MT_ISVIPER1 0x0e /* Archive Viper-150 */
+#define MT_ISPYTHON 0x0f /* Archive Python (DAT) */
+#define MT_ISHPDAT 0x10 /* HP 35450A DAT drive */
+#define MT_ISMFOUR 0x11 /* M4 Data 1/2 9track drive */
+#define MT_ISTK50 0x12 /* DEC SCSI TK50 */
+#define MT_ISMT02 0x13 /* Emulex MT02 SCSI tape controller */
+
+/* mag tape io control commands */
+#define MTIOCTOP _IOW('m', 1, struct mtop) /* do a mag tape op */
+#define MTIOCGET _IOR('m', 2, struct mtget) /* get tape status */
+#define MTIOCIEOT _IO('m', 3) /* ignore EOT error */
+#define MTIOCEEOT _IO('m', 4) /* enable EOT error */
+
+#ifndef KERNEL
+#define DEFTAPE "/dev/nrst0"
+#endif
+
+#ifdef KERNEL
+/*
+ * minor device number
+ */
+
+#define T_UNIT 003 /* unit selection */
+#define T_NOREWIND 004 /* no rewind on close */
+#define T_DENSEL 030 /* density select */
+#define T_800BPI 000 /* select 800 bpi */
+#define T_1600BPI 010 /* select 1600 bpi */
+#define T_6250BPI 020 /* select 6250 bpi */
+#define T_BADBPI 030 /* undefined selection */
+#endif
+#endif /* _SYS_MTIO_H_ */
diff --git a/sys/sys/namei.h b/sys/sys/namei.h
new file mode 100644
index 0000000..74ff360
--- /dev/null
+++ b/sys/sys/namei.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 1985, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)namei.h 8.2 (Berkeley) 1/4/94
+ */
+
+#ifndef _SYS_NAMEI_H_
+#define _SYS_NAMEI_H_
+
+/*
+ * Encapsulation of namei parameters.
+ */
+struct nameidata {
+ /*
+ * Arguments to namei/lookup.
+ */
+ caddr_t ni_dirp; /* pathname pointer */
+ enum uio_seg ni_segflg; /* location of pathname */
+ /* u_long ni_nameiop; namei operation */
+ /* u_long ni_flags; flags to namei */
+ /* struct proc *ni_proc; process requesting lookup */
+ /*
+ * Arguments to lookup.
+ */
+ /* struct ucred *ni_cred; credentials */
+ struct vnode *ni_startdir; /* starting directory */
+ struct vnode *ni_rootdir; /* logical root directory */
+ /*
+ * Results: returned from/manipulated by lookup
+ */
+ struct vnode *ni_vp; /* vnode of result */
+ struct vnode *ni_dvp; /* vnode of intermediate directory */
+ /*
+ * Shared between namei and lookup/commit routines.
+ */
+ long ni_pathlen; /* remaining chars in path */
+ char *ni_next; /* next location in pathname */
+ u_long ni_loopcnt; /* count of symlinks encountered */
+ /*
+ * Lookup parameters: this structure describes the subset of
+ * information from the nameidata structure that is passed
+ * through the VOP interface.
+ */
+ struct componentname {
+ /*
+ * Arguments to lookup.
+ */
+ u_long cn_nameiop; /* namei operation */
+ u_long cn_flags; /* flags to namei */
+ struct proc *cn_proc; /* process requesting lookup */
+ struct ucred *cn_cred; /* credentials */
+ /*
+ * Shared between lookup and commit routines.
+ */
+ char *cn_pnbuf; /* pathname buffer */
+ char *cn_nameptr; /* pointer to looked up name */
+ long cn_namelen; /* length of looked up component */
+ u_long cn_hash; /* hash value of looked up name */
+ long cn_consume; /* chars to consume in lookup() */
+ } ni_cnd;
+};
+
+#ifdef KERNEL
+/*
+ * namei operations
+ */
+#define LOOKUP 0 /* perform name lookup only */
+#define CREATE 1 /* setup for file creation */
+#define DELETE 2 /* setup for file deletion */
+#define RENAME 3 /* setup for file renaming */
+#define OPMASK 3 /* mask for operation */
+/*
+ * namei operational modifier flags, stored in ni_cnd.flags
+ */
+#define LOCKLEAF 0x0004 /* lock inode on return */
+#define LOCKPARENT 0x0008 /* want parent vnode returned locked */
+#define WANTPARENT 0x0010 /* want parent vnode returned unlocked */
+#define NOCACHE 0x0020 /* name must not be left in cache */
+#define FOLLOW 0x0040 /* follow symbolic links */
+#define NOFOLLOW 0x0000 /* do not follow symbolic links (pseudo) */
+#define MODMASK 0x00fc /* mask of operational modifiers */
+/*
+ * Namei parameter descriptors.
+ *
+ * SAVENAME may be set by either the callers of namei or by VOP_LOOKUP.
+ * If the caller of namei sets the flag (for example execve wants to
+ * know the name of the program that is being executed), then it must
+ * free the buffer. If VOP_LOOKUP sets the flag, then the buffer must
+ * be freed by either the commit routine or the VOP_ABORT routine.
+ * SAVESTART is set only by the callers of namei. It implies SAVENAME
+ * plus the addition of saving the parent directory that contains the
+ * name in ni_startdir. It allows repeated calls to lookup for the
+ * name being sought. The caller is responsible for releasing the
+ * buffer and for vrele'ing ni_startdir.
+ */
+#define NOCROSSMOUNT 0x00100 /* do not cross mount points */
+#define RDONLY 0x00200 /* lookup with read-only semantics */
+#define HASBUF 0x00400 /* has allocated pathname buffer */
+#define SAVENAME 0x00800 /* save pathanme buffer */
+#define SAVESTART 0x01000 /* save starting directory */
+#define ISDOTDOT 0x02000 /* current component name is .. */
+#define MAKEENTRY 0x04000 /* entry is to be added to name cache */
+#define ISLASTCN 0x08000 /* this is last component of pathname */
+#define ISSYMLINK 0x10000 /* symlink needs interpretation */
+#define PARAMASK 0xfff00 /* mask of parameter descriptors */
+/*
+ * Initialization of an nameidata structure.
+ */
+#define NDINIT(ndp, op, flags, segflg, namep, p) { \
+ (ndp)->ni_cnd.cn_nameiop = op; \
+ (ndp)->ni_cnd.cn_flags = flags; \
+ (ndp)->ni_segflg = segflg; \
+ (ndp)->ni_dirp = namep; \
+ (ndp)->ni_cnd.cn_proc = p; \
+}
+#endif
+
+/*
+ * This structure describes the elements in the cache of recent
+ * names looked up by namei. NCHNAMLEN is sized to make structure
+ * size a power of two to optimize malloc's. Minimum reasonable
+ * size is 15.
+ */
+
+#define NCHNAMLEN 31 /* maximum name segment length we bother with */
+
+struct namecache {
+ struct namecache *nc_forw; /* hash chain */
+ struct namecache **nc_back; /* hash chain */
+ struct namecache *nc_nxt; /* LRU chain */
+ struct namecache **nc_prev; /* LRU chain */
+ struct vnode *nc_dvp; /* vnode of parent of name */
+ u_long nc_dvpid; /* capability number of nc_dvp */
+ struct vnode *nc_vp; /* vnode the name refers to */
+ u_long nc_vpid; /* capability number of nc_vp */
+ char nc_nlen; /* length of name */
+ char nc_name[NCHNAMLEN]; /* segment name */
+};
+
+#ifdef KERNEL
+u_long nextvnodeid;
+int namei __P((struct nameidata *ndp));
+int lookup __P((struct nameidata *ndp));
+#endif
+
+/*
+ * Stats on usefulness of namei caches.
+ */
+struct nchstats {
+ long ncs_goodhits; /* hits that we can really use */
+ long ncs_neghits; /* negative hits that we can use */
+ long ncs_badhits; /* hits we must drop */
+ long ncs_falsehits; /* hits with id mismatch */
+ long ncs_miss; /* misses */
+ long ncs_long; /* long names that ignore cache */
+ long ncs_pass2; /* names found with passes == 2 */
+ long ncs_2passes; /* number of times we attempt it */
+};
+#endif /* !_SYS_NAMEI_H_ */
diff --git a/sys/sys/nlist_aout.h b/sys/sys/nlist_aout.h
new file mode 100644
index 0000000..11e0a43
--- /dev/null
+++ b/sys/sys/nlist_aout.h
@@ -0,0 +1,85 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nlist.h 8.2 (Berkeley) 1/21/94
+ */
+
+#ifndef _NLIST_H_
+#define _NLIST_H_
+
+/*
+ * Symbol table entry format. The #ifdef's are so that programs including
+ * nlist.h can initialize nlist structures statically.
+ */
+struct nlist {
+#ifdef _AOUT_INCLUDE_
+ union {
+ char *n_name; /* symbol name (in memory) */
+ long n_strx; /* file string table offset (on disk) */
+ } n_un;
+#else
+ char *n_name; /* symbol name (in memory) */
+#endif
+
+#define N_UNDF 0x00 /* undefined */
+#define N_ABS 0x02 /* absolute address */
+#define N_TEXT 0x04 /* text segment */
+#define N_DATA 0x06 /* data segment */
+#define N_BSS 0x08 /* bss segment */
+#define N_COMM 0x12 /* common reference */
+#define N_FN 0x1e /* file name */
+
+#define N_EXT 0x01 /* external (global) bit, OR'ed in */
+#define N_TYPE 0x1e /* mask for all the type bits */
+ unsigned char n_type; /* type defines */
+
+ char n_other; /* spare */
+#define n_hash n_desc /* used internally by ld(1); XXX */
+ short n_desc; /* used by stab entries */
+ unsigned long n_value; /* address/value of the symbol */
+};
+
+#define N_FORMAT "%08x" /* namelist value format; XXX */
+#define N_STAB 0x0e0 /* mask for debugger symbols -- stab(5) */
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int nlist __P((const char *, struct nlist *));
+__END_DECLS
+
+#endif /* !_NLIST_H_ */
diff --git a/sys/sys/param.h b/sys/sys/param.h
new file mode 100644
index 0000000..661af88
--- /dev/null
+++ b/sys/sys/param.h
@@ -0,0 +1,216 @@
+/*-
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)param.h 8.2 (Berkeley) 1/21/94
+ */
+
+#define BSD 199306 /* System version (year & month). */
+#define BSD4_3 1
+#define BSD4_4 1
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef LOCORE
+#include <sys/types.h>
+#endif
+
+/*
+ * Machine-independent constants (some used in following include files).
+ * Redefined constants are from POSIX 1003.1 limits file.
+ *
+ * MAXCOMLEN should be >= sizeof(ac_comm) (see <acct.h>)
+ * MAXLOGNAME should be >= UT_NAMESIZE (see <utmp.h>)
+ */
+#include <sys/syslimits.h>
+
+#define MAXCOMLEN 16 /* max command name remembered */
+#define MAXINTERP 32 /* max interpreter file name length */
+#define MAXLOGNAME 12 /* max login name length */
+#define MAXUPRC CHILD_MAX /* max simultaneous processes */
+#define NCARGS ARG_MAX /* max bytes for an exec function */
+#define NGROUPS NGROUPS_MAX /* max number groups */
+#define NOFILE OPEN_MAX /* max open files per process */
+#define NOGROUP 65535 /* marker for empty group set member */
+#define MAXHOSTNAMELEN 256 /* max hostname size */
+
+/* More types and definitions used throughout the kernel. */
+#ifdef KERNEL
+#include <sys/cdefs.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/ucred.h>
+#include <sys/uio.h>
+#endif
+
+/* Signals. */
+#include <sys/signal.h>
+
+/* Machine type dependent parameters. */
+#include <machine/param.h>
+#include <machine/limits.h>
+
+/*
+ * Priorities. Note that with 32 run queues, differences less than 4 are
+ * insignificant.
+ */
+#define PSWP 0
+#define PVM 4
+#define PINOD 8
+#define PRIBIO 16
+#define PVFS 20
+#define PZERO 22 /* No longer magic, shouldn't be here. XXX */
+#define PSOCK 24
+#define PWAIT 32
+#define PLOCK 36
+#define PPAUSE 40
+#define PUSER 50
+#define MAXPRI 127 /* Priorities range from 0 through MAXPRI. */
+
+#define PRIMASK 0x0ff
+#define PCATCH 0x100 /* OR'd with pri for tsleep to check signals */
+
+#define NZERO 0 /* default "nice" */
+
+#define NBPW sizeof(int) /* number of bytes per word (integer) */
+
+#define CMASK 022 /* default file mask: S_IWGRP|S_IWOTH */
+#define NODEV (dev_t)(-1) /* non-existent device */
+
+/*
+ * Clustering of hardware pages on machines with ridiculously small
+ * page sizes is done here. The paging subsystem deals with units of
+ * CLSIZE pte's describing NBPG (from machine/machparam.h) pages each.
+ */
+#define CLBYTES (CLSIZE*NBPG)
+#define CLOFSET (CLSIZE*NBPG-1) /* for clusters, like PGOFSET */
+#define claligned(x) ((((int)(x))&CLOFSET)==0)
+#define CLOFF CLOFSET
+#define CLSHIFT (PGSHIFT+CLSIZELOG2)
+
+#if CLSIZE==1
+#define clbase(i) (i)
+#define clrnd(i) (i)
+#else
+/* Give the base virtual address (first of CLSIZE). */
+#define clbase(i) ((i) &~ (CLSIZE-1))
+/* Round a number of clicks up to a whole cluster. */
+#define clrnd(i) (((i) + (CLSIZE-1)) &~ (CLSIZE-1))
+#endif
+
+#define CBLOCK 128 /* Clist block size, must be a power of 2. */
+#define CBQSIZE (CBLOCK/NBBY) /* Quote bytes/cblock - can do better. */
+ /* Data chars/clist. */
+#define CBSIZE (CBLOCK - sizeof(struct cblock *) - CBQSIZE)
+#define CROUND (CBLOCK - 1) /* Clist rounding. */
+
+/*
+ * File system parameters and macros.
+ *
+ * The file system is made out of blocks of at most MAXBSIZE units, with
+ * smaller units (fragments) only in the last direct block. MAXBSIZE
+ * primarily determines the size of buffers in the buffer pool. It may be
+ * made larger without any effect on existing file systems; however making
+ * it smaller make make some file systems unmountable.
+ */
+#define MAXBSIZE MAXPHYS
+#define MAXFRAG 8
+
+/*
+ * MAXPATHLEN defines the longest permissable path length after expanding
+ * symbolic links. It is used to allocate a temporary buffer from the buffer
+ * pool in which to do the name expansion, hence should be a power of two,
+ * and must be less than or equal to MAXBSIZE. MAXSYMLINKS defines the
+ * maximum number of symbolic links that may be expanded in a path name.
+ * It should be set high enough to allow all legitimate uses, but halt
+ * infinite loops reasonably quickly.
+ */
+#define MAXPATHLEN PATH_MAX
+#define MAXSYMLINKS 8
+
+/* Bit map related macros. */
+#define setbit(a,i) ((a)[(i)/NBBY] |= 1<<((i)%NBBY))
+#define clrbit(a,i) ((a)[(i)/NBBY] &= ~(1<<((i)%NBBY)))
+#define isset(a,i) ((a)[(i)/NBBY] & (1<<((i)%NBBY)))
+#define isclr(a,i) (((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0)
+
+/* Macros for counting and rounding. */
+#ifndef howmany
+#define howmany(x, y) (((x)+((y)-1))/(y))
+#endif
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+#define powerof2(x) ((((x)-1)&(x))==0)
+
+/* Macros for min/max. */
+#ifndef KERNEL
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
+/*
+ * Constants for setting the parameters of the kernel memory allocator.
+ *
+ * 2 ** MINBUCKET is the smallest unit of memory that will be
+ * allocated. It must be at least large enough to hold a pointer.
+ *
+ * Units of memory less or equal to MAXALLOCSAVE will permanently
+ * allocate physical memory; requests for these size pieces of
+ * memory are quite fast. Allocations greater than MAXALLOCSAVE must
+ * always allocate and free physical memory; requests for these
+ * size allocations should be done infrequently as they will be slow.
+ *
+ * Constraints: CLBYTES <= MAXALLOCSAVE <= 2 ** (MINBUCKET + 14), and
+ * MAXALLOCSIZE must be a power of two.
+ */
+#define MINBUCKET 4 /* 4 => min allocation of 16 bytes */
+#define MAXALLOCSAVE (2 * CLBYTES)
+
+/*
+ * Scale factor for scaled integers used to count %cpu time and load avgs.
+ *
+ * The number of CPU `tick's that map to a unique `%age' can be expressed
+ * by the formula (1 / (2 ^ (FSHIFT - 11))). The maximum load average that
+ * can be calculated (assuming 32 bits) can be closely approximated using
+ * the formula (2 ^ (2 * (16 - FSHIFT))) for (FSHIFT < 15).
+ *
+ * For the scheduler to maintain a 1:1 mapping of CPU `tick' to `%age',
+ * FSHIFT must be at least 11; this gives us a maximum load avg of ~1024.
+ */
+#define FSHIFT 11 /* bits to right of fixed binary point */
+#define FSCALE (1<<FSHIFT)
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
new file mode 100644
index 0000000..883227e8
--- /dev/null
+++ b/sys/sys/proc.h
@@ -0,0 +1,264 @@
+/*-
+ * Copyright (c) 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)proc.h 8.8 (Berkeley) 1/21/94
+ */
+
+#ifndef _SYS_PROC_H_
+#define _SYS_PROC_H_
+
+#include <machine/proc.h> /* Machine-dependent proc substruct. */
+#include <sys/select.h> /* For struct selinfo. */
+
+/*
+ * One structure allocated per session.
+ */
+struct session {
+ int s_count; /* Ref cnt; pgrps in session. */
+ struct proc *s_leader; /* Session leader. */
+ struct vnode *s_ttyvp; /* Vnode of controlling terminal. */
+ struct tty *s_ttyp; /* Controlling terminal. */
+ char s_login[MAXLOGNAME]; /* Setlogin() name. */
+};
+
+/*
+ * One structure allocated per process group.
+ */
+struct pgrp {
+ struct pgrp *pg_hforw; /* Forward link in hash bucket. */
+ struct proc *pg_mem; /* Pointer to pgrp members. */
+ struct session *pg_session; /* Pointer to session. */
+ pid_t pg_id; /* Pgrp id. */
+ int pg_jobc; /* # procs qualifying pgrp for job control */
+};
+
+/*
+ * Description of a process.
+ *
+ * This structure contains the information needed to manage a thread of
+ * control, known in UN*X as a process; it has references to substructures
+ * containing descriptions of things that the process uses, but may share
+ * with related processes. The process structure and the substructures
+ * are always addressible except for those marked "(PROC ONLY)" below,
+ * which might be addressible only on a processor on which the process
+ * is running.
+ */
+struct proc {
+ struct proc *p_forw; /* Doubly-linked run/sleep queue. */
+ struct proc *p_back;
+ struct proc *p_next; /* Linked list of active procs */
+ struct proc **p_prev; /* and zombies. */
+
+ /* substructures: */
+ struct pcred *p_cred; /* Process owner's identity. */
+ struct filedesc *p_fd; /* Ptr to open files structure. */
+ struct pstats *p_stats; /* Accounting/statistics (PROC ONLY). */
+ struct plimit *p_limit; /* Process limits. */
+ struct vmspace *p_vmspace; /* Address space. */
+ struct sigacts *p_sigacts; /* Signal actions, state (PROC ONLY). */
+
+#define p_ucred p_cred->pc_ucred
+#define p_rlimit p_limit->pl_rlimit
+
+ int p_flag; /* P_* flags. */
+ char p_stat; /* S* process status. */
+ char p_lock; /* process lock count */
+ char p_pad1[2];
+
+ pid_t p_pid; /* Process identifier. */
+ struct proc *p_hash; /* Hashed based on p_pid for kill+exit+... */
+ struct proc *p_pgrpnxt; /* Pointer to next process in process group. */
+ struct proc *p_pptr; /* Pointer to process structure of parent. */
+ struct proc *p_osptr; /* Pointer to older sibling processes. */
+
+/* The following fields are all zeroed upon creation in fork. */
+#define p_startzero p_ysptr
+ struct proc *p_ysptr; /* Pointer to younger siblings. */
+ struct proc *p_cptr; /* Pointer to youngest living child. */
+ pid_t p_oppid; /* Save parent pid during ptrace. XXX */
+ int p_dupfd; /* Sideways return value from fdopen. XXX */
+
+ /* scheduling */
+ u_int p_estcpu; /* Time averaged value of p_cpticks. */
+ int p_cpticks; /* Ticks of cpu time. */
+ fixpt_t p_pctcpu; /* %cpu for this process during p_swtime */
+ void *p_wchan; /* Sleep address. */
+ char *p_wmesg; /* Reason for sleep. */
+ u_int p_swtime; /* Time swapped in or out. */
+ u_int p_slptime; /* Time since last blocked. */
+
+ struct itimerval p_realtimer; /* Alarm timer. */
+ struct timeval p_rtime; /* Real time. */
+ u_quad_t p_uticks; /* Statclock hits in user mode. */
+ u_quad_t p_sticks; /* Statclock hits in system mode. */
+ u_quad_t p_iticks; /* Statclock hits processing intr. */
+
+ int p_traceflag; /* Kernel trace points. */
+ struct vnode *p_tracep; /* Trace to vnode. */
+
+ int p_siglist; /* Signals arrived but not delivered. */
+
+ struct vnode *p_textvp; /* Vnode of executable. */
+
+ long p_spare[5]; /* pad to 256, avoid shifting eproc. */
+
+/* End area that is zeroed on creation. */
+#define p_endzero p_startcopy
+
+/* The following fields are all copied upon creation in fork. */
+#define p_startcopy p_sigmask
+
+ sigset_t p_sigmask; /* Current signal mask. */
+ sigset_t p_sigignore; /* Signals being ignored. */
+ sigset_t p_sigcatch; /* Signals being caught by user. */
+
+ u_char p_priority; /* Process priority. */
+ u_char p_usrpri; /* User-priority based on p_cpu and p_nice. */
+ char p_nice; /* Process "nice" value. */
+ char p_comm[MAXCOMLEN+1];
+
+ struct pgrp *p_pgrp; /* Pointer to process group. */
+
+/* End area that is copied on creation. */
+#define p_endcopy p_thread
+ int p_thread; /* Id for this "thread"; Mach glue. XXX */
+ struct user *p_addr; /* Kernel virtual addr of u-area (PROC ONLY). */
+ struct mdproc p_md; /* Any machine-dependent fields. */
+
+ u_short p_xstat; /* Exit status for wait; also stop signal. */
+ u_short p_acflag; /* Accounting flags. */
+ struct rusage *p_ru; /* Exit information. XXX */
+
+};
+
+#define p_session p_pgrp->pg_session
+#define p_pgid p_pgrp->pg_id
+
+/* Status values. */
+#define SIDL 1 /* Process being created by fork. */
+#define SRUN 2 /* Currently runnable. */
+#define SSLEEP 3 /* Sleeping on an address. */
+#define SSTOP 4 /* Process debugging or suspension. */
+#define SZOMB 5 /* Awaiting collection by parent. */
+
+/* These flags are kept in p_flags. */
+#define P_ADVLOCK 0x00001 /* Process may hold a POSIX advisory lock. */
+#define P_CONTROLT 0x00002 /* Has a controlling terminal. */
+#define P_INMEM 0x00004 /* Loaded into memory. */
+#define P_NOCLDSTOP 0x00008 /* No SIGCHLD when children stop. */
+#define P_PPWAIT 0x00010 /* Parent is waiting for child to exec/exit. */
+#define P_PROFIL 0x00020 /* Has started profiling. */
+#define P_SELECT 0x00040 /* Selecting; wakeup/waiting danger. */
+#define P_SINTR 0x00080 /* Sleep is interruptible. */
+#define P_SUGID 0x00100 /* Had set id privileges since last exec. */
+#define P_SYSTEM 0x00200 /* System proc: no sigs, stats or swapping. */
+#define P_TIMEOUT 0x00400 /* Timing out during sleep. */
+#define P_TRACED 0x00800 /* Debugged process being traced. */
+#define P_WAITED 0x01000 /* Debugging process has waited for child. */
+#define P_WEXIT 0x02000 /* Working on exiting. */
+#define P_EXEC 0x04000 /* Process called exec. */
+
+/* Should probably be changed into a hold count. */
+#define P_NOSWAP 0x08000 /* Another flag to prevent swap out. */
+#define P_PHYSIO 0x10000 /* Doing physical I/O. */
+
+/* Should be moved to machine-dependent areas. */
+#define P_OWEUPC 0x20000 /* Owe process an addupc() call at next ast. */
+
+/*
+ * MOVE TO ucred.h?
+ *
+ * Shareable process credentials (always resident). This includes a reference
+ * to the current user credentials as well as real and saved ids that may be
+ * used to change ids.
+ */
+struct pcred {
+ struct ucred *pc_ucred; /* Current credentials. */
+ uid_t p_ruid; /* Real user id. */
+ uid_t p_svuid; /* Saved effective user id. */
+ gid_t p_rgid; /* Real group id. */
+ gid_t p_svgid; /* Saved effective group id. */
+ int p_refcnt; /* Number of references. */
+};
+
+#ifdef KERNEL
+/*
+ * We use process IDs <= PID_MAX; PID_MAX + 1 must also fit in a pid_t,
+ * as it is used to represent "no process group".
+ */
+#define PID_MAX 30000
+#define NO_PID 30001
+#define PIDHASH(pid) ((pid) & pidhashmask)
+
+#define SESS_LEADER(p) ((p)->p_session->s_leader == (p))
+#define SESSHOLD(s) ((s)->s_count++)
+#define SESSRELE(s) { \
+ if (--(s)->s_count == 0) \
+ FREE(s, M_SESSION); \
+}
+
+extern struct proc *pidhash[]; /* In param.c. */
+extern struct pgrp *pgrphash[]; /* In param.c. */
+extern struct proc *curproc; /* Current running proc. */
+extern struct proc proc0; /* Process slot for swapper. */
+extern int nprocs, maxproc; /* Current and max number of procs. */
+extern int pidhashmask; /* In param.c. */
+
+volatile struct proc *allproc; /* List of active procs. */
+struct proc *zombproc; /* List of zombie procs. */
+struct proc *initproc, *pageproc; /* Process slots for init, pager. */
+
+#define NQS 32 /* 32 run queues. */
+int whichqs; /* Bit mask summary of non-empty Q's. */
+struct prochd {
+ struct proc *ph_link; /* Linked list of running processes. */
+ struct proc *ph_rlink;
+} qs[NQS];
+
+struct proc *pfind __P((pid_t)); /* Find process by id. */
+struct pgrp *pgfind __P((pid_t)); /* Find process group by id. */
+
+void mi_switch __P((void));
+void resetpriority __P((struct proc *));
+void setrunnable __P((struct proc *));
+void setrunqueue __P((struct proc *));
+void sleep __P((void *chan, int pri));
+int tsleep __P((void *chan, int pri, char *wmesg, int timo));
+void unsleep __P((struct proc *));
+void wakeup __P((void *chan));
+#endif /* KERNEL */
+#endif /* !_SYS_PROC_H_ */
diff --git a/sys/sys/protosw.h b/sys/sys/protosw.h
new file mode 100644
index 0000000..76ac720
--- /dev/null
+++ b/sys/sys/protosw.h
@@ -0,0 +1,210 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)protosw.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * Protocol switch table.
+ *
+ * Each protocol has a handle initializing one of these structures,
+ * which is used for protocol-protocol and system-protocol communication.
+ *
+ * A protocol is called through the pr_init entry before any other.
+ * Thereafter it is called every 200ms through the pr_fasttimo entry and
+ * every 500ms through the pr_slowtimo for timer based actions.
+ * The system will call the pr_drain entry if it is low on space and
+ * this should throw away any non-critical data.
+ *
+ * Protocols pass data between themselves as chains of mbufs using
+ * the pr_input and pr_output hooks. Pr_input passes data up (towards
+ * UNIX) and pr_output passes it down (towards the imps); control
+ * information passes up and down on pr_ctlinput and pr_ctloutput.
+ * The protocol is responsible for the space occupied by any the
+ * arguments to these entries and must dispose it.
+ *
+ * The userreq routine interfaces protocols to the system and is
+ * described below.
+ */
+struct protosw {
+ short pr_type; /* socket type used for */
+ struct domain *pr_domain; /* domain protocol a member of */
+ short pr_protocol; /* protocol number */
+ short pr_flags; /* see below */
+/* protocol-protocol hooks */
+ void (*pr_input)(); /* input to protocol (from below) */
+ int (*pr_output)(); /* output to protocol (from above) */
+ void (*pr_ctlinput)(); /* control input (from below) */
+ int (*pr_ctloutput)(); /* control output (from above) */
+/* user-protocol hook */
+ int (*pr_usrreq)(); /* user request: see list below */
+/* utility hooks */
+ void (*pr_init)(); /* initialization hook */
+ void (*pr_fasttimo)(); /* fast timeout (200ms) */
+ void (*pr_slowtimo)(); /* slow timeout (500ms) */
+ void (*pr_drain)(); /* flush any excess space possible */
+ int (*pr_sysctl)(); /* sysctl for protocol */
+};
+
+#define PR_SLOWHZ 2 /* 2 slow timeouts per second */
+#define PR_FASTHZ 5 /* 5 fast timeouts per second */
+
+/*
+ * Values for pr_flags.
+ * PR_ADDR requires PR_ATOMIC;
+ * PR_ADDR and PR_CONNREQUIRED are mutually exclusive.
+ */
+#define PR_ATOMIC 0x01 /* exchange atomic messages only */
+#define PR_ADDR 0x02 /* addresses given with messages */
+#define PR_CONNREQUIRED 0x04 /* connection required by protocol */
+#define PR_WANTRCVD 0x08 /* want PRU_RCVD calls */
+#define PR_RIGHTS 0x10 /* passes capabilities */
+
+/*
+ * The arguments to usrreq are:
+ * (*protosw[].pr_usrreq)(up, req, m, nam, opt);
+ * where up is a (struct socket *), req is one of these requests,
+ * m is a optional mbuf chain containing a message,
+ * nam is an optional mbuf chain containing an address,
+ * and opt is a pointer to a socketopt structure or nil.
+ * The protocol is responsible for disposal of the mbuf chain m,
+ * the caller is responsible for any space held by nam and opt.
+ * A non-zero return from usrreq gives an
+ * UNIX error number which should be passed to higher level software.
+ */
+#define PRU_ATTACH 0 /* attach protocol to up */
+#define PRU_DETACH 1 /* detach protocol from up */
+#define PRU_BIND 2 /* bind socket to address */
+#define PRU_LISTEN 3 /* listen for connection */
+#define PRU_CONNECT 4 /* establish connection to peer */
+#define PRU_ACCEPT 5 /* accept connection from peer */
+#define PRU_DISCONNECT 6 /* disconnect from peer */
+#define PRU_SHUTDOWN 7 /* won't send any more data */
+#define PRU_RCVD 8 /* have taken data; more room now */
+#define PRU_SEND 9 /* send this data */
+#define PRU_ABORT 10 /* abort (fast DISCONNECT, DETATCH) */
+#define PRU_CONTROL 11 /* control operations on protocol */
+#define PRU_SENSE 12 /* return status into m */
+#define PRU_RCVOOB 13 /* retrieve out of band data */
+#define PRU_SENDOOB 14 /* send out of band data */
+#define PRU_SOCKADDR 15 /* fetch socket's address */
+#define PRU_PEERADDR 16 /* fetch peer's address */
+#define PRU_CONNECT2 17 /* connect two sockets */
+/* begin for protocols internal use */
+#define PRU_FASTTIMO 18 /* 200ms timeout */
+#define PRU_SLOWTIMO 19 /* 500ms timeout */
+#define PRU_PROTORCV 20 /* receive from below */
+#define PRU_PROTOSEND 21 /* send to below */
+
+#define PRU_NREQ 21
+
+#ifdef PRUREQUESTS
+char *prurequests[] = {
+ "ATTACH", "DETACH", "BIND", "LISTEN",
+ "CONNECT", "ACCEPT", "DISCONNECT", "SHUTDOWN",
+ "RCVD", "SEND", "ABORT", "CONTROL",
+ "SENSE", "RCVOOB", "SENDOOB", "SOCKADDR",
+ "PEERADDR", "CONNECT2", "FASTTIMO", "SLOWTIMO",
+ "PROTORCV", "PROTOSEND",
+};
+#endif
+
+/*
+ * The arguments to the ctlinput routine are
+ * (*protosw[].pr_ctlinput)(cmd, sa, arg);
+ * where cmd is one of the commands below, sa is a pointer to a sockaddr,
+ * and arg is an optional caddr_t argument used within a protocol family.
+ */
+#define PRC_IFDOWN 0 /* interface transition */
+#define PRC_ROUTEDEAD 1 /* select new route if possible ??? */
+#define PRC_QUENCH2 3 /* DEC congestion bit says slow down */
+#define PRC_QUENCH 4 /* some one said to slow down */
+#define PRC_MSGSIZE 5 /* message size forced drop */
+#define PRC_HOSTDEAD 6 /* host appears to be down */
+#define PRC_HOSTUNREACH 7 /* deprecated (use PRC_UNREACH_HOST) */
+#define PRC_UNREACH_NET 8 /* no route to network */
+#define PRC_UNREACH_HOST 9 /* no route to host */
+#define PRC_UNREACH_PROTOCOL 10 /* dst says bad protocol */
+#define PRC_UNREACH_PORT 11 /* bad port # */
+/* was PRC_UNREACH_NEEDFRAG 12 (use PRC_MSGSIZE) */
+#define PRC_UNREACH_SRCFAIL 13 /* source route failed */
+#define PRC_REDIRECT_NET 14 /* net routing redirect */
+#define PRC_REDIRECT_HOST 15 /* host routing redirect */
+#define PRC_REDIRECT_TOSNET 16 /* redirect for type of service & net */
+#define PRC_REDIRECT_TOSHOST 17 /* redirect for tos & host */
+#define PRC_TIMXCEED_INTRANS 18 /* packet lifetime expired in transit */
+#define PRC_TIMXCEED_REASS 19 /* lifetime expired on reass q */
+#define PRC_PARAMPROB 20 /* header incorrect */
+
+#define PRC_NCMDS 21
+
+#define PRC_IS_REDIRECT(cmd) \
+ ((cmd) >= PRC_REDIRECT_NET && (cmd) <= PRC_REDIRECT_TOSHOST)
+
+#ifdef PRCREQUESTS
+char *prcrequests[] = {
+ "IFDOWN", "ROUTEDEAD", "#2", "DEC-BIT-QUENCH2",
+ "QUENCH", "MSGSIZE", "HOSTDEAD", "#7",
+ "NET-UNREACH", "HOST-UNREACH", "PROTO-UNREACH", "PORT-UNREACH",
+ "#12", "SRCFAIL-UNREACH", "NET-REDIRECT", "HOST-REDIRECT",
+ "TOSNET-REDIRECT", "TOSHOST-REDIRECT", "TX-INTRANS", "TX-REASS",
+ "PARAMPROB"
+};
+#endif
+
+/*
+ * The arguments to ctloutput are:
+ * (*protosw[].pr_ctloutput)(req, so, level, optname, optval);
+ * req is one of the actions listed below, so is a (struct socket *),
+ * level is an indication of which protocol layer the option is intended.
+ * optname is a protocol dependent socket option request,
+ * optval is a pointer to a mbuf-chain pointer, for value-return results.
+ * The protocol is responsible for disposal of the mbuf chain *optval
+ * if supplied,
+ * the caller is responsible for any space held by *optval, when returned.
+ * A non-zero return from usrreq gives an
+ * UNIX error number which should be passed to higher level software.
+ */
+#define PRCO_GETOPT 0
+#define PRCO_SETOPT 1
+
+#define PRCO_NCMDS 2
+
+#ifdef PRCOREQUESTS
+char *prcorequests[] = {
+ "GETOPT", "SETOPT",
+};
+#endif
+
+#ifdef KERNEL
+extern struct protosw *pffindproto(), *pffindtype();
+#endif
diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h
new file mode 100644
index 0000000..f7f99d4
--- /dev/null
+++ b/sys/sys/ptrace.h
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 1984, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ptrace.h 8.2 (Berkeley) 1/4/94
+ */
+
+#ifndef _SYS_PTRACE_H_
+#define _SYS_PTRACE_H_
+
+#define PT_TRACE_ME 0 /* child declares it's being traced */
+#define PT_READ_I 1 /* read word in child's I space */
+#define PT_READ_D 2 /* read word in child's D space */
+#define PT_READ_U 3 /* read word in child's user structure */
+#define PT_WRITE_I 4 /* write word in child's I space */
+#define PT_WRITE_D 5 /* write word in child's D space */
+#define PT_WRITE_U 6 /* write word in child's user structure */
+#define PT_CONTINUE 7 /* continue the child */
+#define PT_KILL 8 /* kill the child process */
+#define PT_STEP 9 /* single step the child */
+#define PT_ATTACH 10 /* trace some running process */
+#define PT_DETACH 11 /* stop tracing a process */
+
+#define PT_FIRSTMACH 32 /* for machine-specific requests */
+#include <machine/ptrace.h> /* machine-specific requests, if any */
+
+#ifdef KERNEL
+void proc_reparent __P((struct proc *child, struct proc *newparent));
+#else /* !KERNEL */
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int ptrace __P((int _request, pid_t _pid, caddr_t _addr, int _data));
+__END_DECLS
+
+#endif /* !KERNEL */
+
+#endif /* !_SYS_PTRACE_H_ */
diff --git a/sys/sys/queue.h b/sys/sys/queue.h
new file mode 100644
index 0000000..c200c9f
--- /dev/null
+++ b/sys/sys/queue.h
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.4 (Berkeley) 1/4/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines three types of data structures: lists, tail queues,
+ * and circular queues.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list after
+ * an existing element or at the head of the list. A list may only be
+ * traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A tail queue may only be traversed in the forward direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) { \
+ (head)->lh_first = NULL; \
+}
+
+#define LIST_INSERT_AFTER(listelm, elm, field) { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+}
+
+#define LIST_INSERT_HEAD(head, elm, field) { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+}
+
+#define LIST_REMOVE(elm, field) { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+}
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+}
+
+#define TAILQ_INSERT_HEAD(head, elm, field) { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+}
+
+#define TAILQ_INSERT_TAIL(head, elm, field) { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+}
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+}
+
+#define TAILQ_REMOVE(head, elm, field) { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+}
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) { \
+ (head)->cqh_first = (void *)(head); \
+ (head)->cqh_last = (void *)(head); \
+}
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+}
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+}
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = (void *)(head); \
+ if ((head)->cqh_last == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+}
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) { \
+ (elm)->field.cqe_next = (void *)(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+}
+
+#define CIRCLEQ_REMOVE(head, elm, field) { \
+ if ((elm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+}
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/sys/sys/reboot.h b/sys/sys/reboot.h
new file mode 100644
index 0000000..c3c957e
--- /dev/null
+++ b/sys/sys/reboot.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)reboot.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * Arguments to reboot system call.
+ * These are passed to boot program in r11,
+ * and on to init.
+ */
+#define RB_AUTOBOOT 0 /* flags for system auto-booting itself */
+
+#define RB_ASKNAME 0x01 /* ask for file name to reboot from */
+#define RB_SINGLE 0x02 /* reboot to single user only */
+#define RB_NOSYNC 0x04 /* dont sync before reboot */
+#define RB_HALT 0x08 /* don't reboot, just halt */
+#define RB_INITNAME 0x10 /* name given for /etc/init (unused) */
+#define RB_DFLTROOT 0x20 /* use compiled-in rootdev */
+#define RB_KDB 0x40 /* give control to kernel debugger */
+#define RB_RDONLY 0x80 /* mount root fs read-only */
+#define RB_DUMP 0x100 /* dump kernel memory before reboot */
+#define RB_MINIROOT 0x200 /* mini-root present in memory at boot time */
+
+/*
+ * Constants for converting boot-style device number to type,
+ * adaptor (uba, mba, etc), unit number and partition number.
+ * Type (== major device number) is in the low byte
+ * for backward compatibility. Except for that of the "magic
+ * number", each mask applies to the shifted value.
+ * Format:
+ * (4) (4) (4) (4) (8) (8)
+ * --------------------------------
+ * |MA | AD| CT| UN| PART | TYPE |
+ * --------------------------------
+ */
+#define B_ADAPTORSHIFT 24
+#define B_ADAPTORMASK 0x0f
+#define B_ADAPTOR(val) (((val) >> B_ADAPTORSHIFT) & B_ADAPTORMASK)
+#define B_CONTROLLERSHIFT 20
+#define B_CONTROLLERMASK 0xf
+#define B_CONTROLLER(val) (((val)>>B_CONTROLLERSHIFT) & B_CONTROLLERMASK)
+#define B_UNITSHIFT 16
+#define B_UNITMASK 0xf
+#define B_UNIT(val) (((val) >> B_UNITSHIFT) & B_UNITMASK)
+#define B_PARTITIONSHIFT 8
+#define B_PARTITIONMASK 0xff
+#define B_PARTITION(val) (((val) >> B_PARTITIONSHIFT) & B_PARTITIONMASK)
+#define B_TYPESHIFT 0
+#define B_TYPEMASK 0xff
+#define B_TYPE(val) (((val) >> B_TYPESHIFT) & B_TYPEMASK)
+
+#define B_MAGICMASK ((u_long)0xf0000000)
+#define B_DEVMAGIC ((u_long)0xa0000000)
+
+#define MAKEBOOTDEV(type, adaptor, controller, unit, partition) \
+ (((type) << B_TYPESHIFT) | ((adaptor) << B_ADAPTORSHIFT) | \
+ ((controller) << B_CONTROLLERSHIFT) | ((unit) << B_UNITSHIFT) | \
+ ((partition) << B_PARTITIONSHIFT) | B_DEVMAGIC)
diff --git a/sys/sys/resource.h b/sys/sys/resource.h
new file mode 100644
index 0000000..559f1ac
--- /dev/null
+++ b/sys/sys/resource.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)resource.h 8.2 (Berkeley) 1/4/94
+ */
+
+#ifndef _SYS_RESOURCE_H_
+#define _SYS_RESOURCE_H_
+
+/*
+ * Process priority specifications to get/setpriority.
+ */
+#define PRIO_MIN -20
+#define PRIO_MAX 20
+
+#define PRIO_PROCESS 0
+#define PRIO_PGRP 1
+#define PRIO_USER 2
+
+/*
+ * Resource utilization information.
+ */
+
+#define RUSAGE_SELF 0
+#define RUSAGE_CHILDREN -1
+
+struct rusage {
+ struct timeval ru_utime; /* user time used */
+ struct timeval ru_stime; /* system time used */
+ long ru_maxrss; /* max resident set size */
+#define ru_first ru_ixrss
+ long ru_ixrss; /* integral shared memory size */
+ long ru_idrss; /* integral unshared data " */
+ long ru_isrss; /* integral unshared stack " */
+ long ru_minflt; /* page reclaims */
+ long ru_majflt; /* page faults */
+ long ru_nswap; /* swaps */
+ long ru_inblock; /* block input operations */
+ long ru_oublock; /* block output operations */
+ long ru_msgsnd; /* messages sent */
+ long ru_msgrcv; /* messages received */
+ long ru_nsignals; /* signals received */
+ long ru_nvcsw; /* voluntary context switches */
+ long ru_nivcsw; /* involuntary " */
+#define ru_last ru_nivcsw
+};
+
+/*
+ * Resource limits
+ */
+#define RLIMIT_CPU 0 /* cpu time in milliseconds */
+#define RLIMIT_FSIZE 1 /* maximum file size */
+#define RLIMIT_DATA 2 /* data size */
+#define RLIMIT_STACK 3 /* stack size */
+#define RLIMIT_CORE 4 /* core file size */
+#define RLIMIT_RSS 5 /* resident set size */
+#define RLIMIT_MEMLOCK 6 /* locked-in-memory address space */
+#define RLIMIT_NPROC 7 /* number of processes */
+#define RLIMIT_NOFILE 8 /* number of open files */
+
+#define RLIM_NLIMITS 9 /* number of resource limits */
+
+#define RLIM_INFINITY (((u_quad_t)1 << 63) - 1)
+
+struct orlimit {
+ long rlim_cur; /* current (soft) limit */
+ long rlim_max; /* maximum value for rlim_cur */
+};
+
+struct rlimit {
+ quad_t rlim_cur; /* current (soft) limit */
+ quad_t rlim_max; /* maximum value for rlim_cur */
+};
+
+/* Load average structure. */
+struct loadavg {
+ fixpt_t ldavg[3];
+ long fscale;
+};
+
+#ifdef KERNEL
+extern struct loadavg averunnable;
+
+#else
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int getpriority __P((int, int));
+int getrlimit __P((int, struct rlimit *));
+int getrusage __P((int, struct rusage *));
+int setpriority __P((int, int, int));
+int setrlimit __P((int, const struct rlimit *));
+__END_DECLS
+
+#endif /* KERNEL */
+#endif /* !_SYS_RESOURCE_H_ */
diff --git a/sys/sys/resourcevar.h b/sys/sys/resourcevar.h
new file mode 100644
index 0000000..0f8d5e3
--- /dev/null
+++ b/sys/sys/resourcevar.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)resourcevar.h 8.3 (Berkeley) 2/22/94
+ */
+
+#ifndef _SYS_RESOURCEVAR_H_
+#define _SYS_RESOURCEVAR_H_
+
+/*
+ * Kernel per-process accounting / statistics
+ * (not necessarily resident except when running).
+ */
+struct pstats {
+#define pstat_startzero p_ru
+ struct rusage p_ru; /* stats for this proc */
+ struct rusage p_cru; /* sum of stats for reaped children */
+#define pstat_endzero pstat_startcopy
+
+#define pstat_startcopy p_timer
+ struct itimerval p_timer[3]; /* virtual-time timers */
+
+ struct uprof { /* profile arguments */
+ caddr_t pr_base; /* buffer base */
+ u_long pr_size; /* buffer size */
+ u_long pr_off; /* pc offset */
+ u_long pr_scale; /* pc scaling */
+ u_long pr_addr; /* temp storage for addr until AST */
+ u_long pr_ticks; /* temp storage for ticks until AST */
+ } p_prof;
+#define pstat_endcopy p_start
+ struct timeval p_start; /* starting time */
+};
+
+/*
+ * Kernel shareable process resource limits. Because this structure
+ * is moderately large but changes infrequently, it is normally
+ * shared copy-on-write after forks. If a group of processes
+ * ("threads") share modifications, the PL_SHAREMOD flag is set,
+ * and a copy must be made for the child of a new fork that isn't
+ * sharing modifications to the limits.
+ */
+struct plimit {
+ struct rlimit pl_rlimit[RLIM_NLIMITS];
+#define PL_SHAREMOD 0x01 /* modifications are shared */
+ int p_lflags;
+ int p_refcnt; /* number of references */
+};
+
+/* add user profiling from AST */
+#define ADDUPROF(p) \
+ addupc_task(p, \
+ (p)->p_stats->p_prof.pr_addr, (p)->p_stats->p_prof.pr_ticks)
+
+#ifdef KERNEL
+void addupc_intr __P((struct proc *p, u_long pc, u_int ticks));
+void addupc_task __P((struct proc *p, u_long pc, u_int ticks));
+struct plimit
+ *limcopy __P((struct plimit *lim));
+#endif
+#endif /* !_SYS_RESOURCEVAR_H_ */
diff --git a/sys/sys/rlist.h b/sys/sys/rlist.h
new file mode 100644
index 0000000..f241f0f
--- /dev/null
+++ b/sys/sys/rlist.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1992 William Jolitz. All rights reserved.
+ * Written by William Jolitz 1/92
+ *
+ * Redistribution and use in source and binary forms are freely permitted
+ * provided that the above copyright notice and attribution and date of work
+ * and this paragraph are duplicated in all such forms.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Resource lists.
+ *
+ * Usage:
+ * rlist_free(&swapmap, 100, 200); add space to swapmap
+ * rlist_alloc(&swapmap, 100, &loc); obtain 100 sectors from swap
+ *
+ * from: unknown?
+ * $Id: rlist.h,v 1.5 1993/11/07 17:52:59 wollman Exp $
+ */
+
+#ifndef _SYS_RLIST_H_
+#define _SYS_RLIST_H_
+
+/* A resource list element. */
+struct rlist {
+ unsigned rl_start; /* boundaries of extent - inclusive */
+ unsigned rl_end; /* boundaries of extent - inclusive */
+ struct rlist *rl_next; /* next list entry, if present */
+};
+
+/* Functions to manipulate resource lists. */
+extern void rlist_free __P((struct rlist **, unsigned, unsigned));
+int rlist_alloc __P((struct rlist **, unsigned, unsigned *));
+extern void rlist_destroy __P((struct rlist **));
+
+
+/* heads of lists */
+extern struct rlist *swapmap;
+
+#endif /* _SYS_RLIST_H_ */
diff --git a/sys/sys/scsiio.h b/sys/sys/scsiio.h
new file mode 100644
index 0000000..05753e4
--- /dev/null
+++ b/sys/sys/scsiio.h
@@ -0,0 +1,63 @@
+
+#ifndef _SYS_SCSIIO_H_
+#define _SYS_SCSIIO_H_
+
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#define SENSEBUFLEN 48
+
+typedef struct scsireq {
+ u_long flags; /* info about the request status and type */
+ u_long timeout;
+ u_char cmd[16]; /* 12 is actually the max */
+ u_char cmdlen;
+ caddr_t databuf; /* address in user space of buffer */
+ u_long datalen; /* size of user buffer (request) */
+ u_long datalen_used; /* size of user buffer (used)*/
+ u_char sense[SENSEBUFLEN]; /* returned sense will be in here */
+ u_char senselen; /* sensedata request size (MAX of SENSEBUFLEN)*/
+ u_char senselen_used; /* return value only */
+ u_char status; /* what the scsi status was from the adapter */
+ u_char retsts; /* the return status for the command */
+ int error; /* error bits */
+} scsireq_t;
+
+/* bit defintions for flags */
+#define SCCMD_READ 0x00000001
+#define SCCMD_WRITE 0x00000002
+#define SCCMD_IOV 0x00000004
+#define SCCMD_ESCAPE 0x00000010
+#define SCCMD_TARGET 0x00000020
+
+
+/* definitions for the return status (retsts) */
+#define SCCMD_OK 0x00
+#define SCCMD_TIMEOUT 0x01
+#define SCCMD_BUSY 0x02
+#define SCCMD_SENSE 0x03
+#define SCCMD_UNKNOWN 0x04
+
+#define SCIOCCOMMAND _IOWR('Q', 1, scsireq_t)
+
+#define SC_DB_CMDS 0x00000001 /* show all scsi cmds and errors */
+#define SC_DB_FLOW 0x00000002 /* show routines entered */
+#define SC_DB_FLOW2 0x00000004 /* show path INSIDE routines */
+#define SC_DB_DMA 0x00000008 /* show DMA segments etc */
+#define SCIOCDEBUG _IOW('Q', 2, int) /* from 0 to 15 */
+
+struct scsi_addr {
+ int scbus; /* -1 if wildcard */
+ int target; /* -1 if wildcard */
+ int lun; /* -1 if wildcard */
+} ;
+
+#define SCIOCREPROBE _IOW('Q', 3, struct scsi_addr) /* look for new devs */
+#define SCIOCIDENTIFY _IOR('Q', 4, struct scsi_addr) /* where are you? */
+#define SCIOCDECONFIG _IO('Q', 5) /* please dissappear */
+#define SCIOCRECONFIG _IO('Q', 6) /* please check again */
+#define SCIOCRESET _IO('Q', 7) /* reset the device */
+
+
+#endif /* _SYS_SCSIIO_H_ */
diff --git a/sys/sys/select.h b/sys/sys/select.h
new file mode 100644
index 0000000..a279c59
--- /dev/null
+++ b/sys/sys/select.h
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)select.h 8.2 (Berkeley) 1/4/94
+ */
+
+#ifndef _SYS_SELECT_H_
+#define _SYS_SELECT_H_
+
+/*
+ * Used to maintain information about processes that wish to be
+ * notified when I/O becomes possible.
+ */
+struct selinfo {
+ pid_t si_pid; /* process to be notified */
+ short si_flags; /* see below */
+};
+#define SI_COLL 0x0001 /* collision occurred */
+
+#ifdef KERNEL
+struct proc;
+
+void selrecord __P((struct proc *selector, struct selinfo *));
+void selwakeup __P((struct selinfo *));
+#endif
+
+#endif /* !_SYS_SELECT_H_ */
diff --git a/sys/sys/selinfo.h b/sys/sys/selinfo.h
new file mode 100644
index 0000000..a279c59
--- /dev/null
+++ b/sys/sys/selinfo.h
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)select.h 8.2 (Berkeley) 1/4/94
+ */
+
+#ifndef _SYS_SELECT_H_
+#define _SYS_SELECT_H_
+
+/*
+ * Used to maintain information about processes that wish to be
+ * notified when I/O becomes possible.
+ */
+struct selinfo {
+ pid_t si_pid; /* process to be notified */
+ short si_flags; /* see below */
+};
+#define SI_COLL 0x0001 /* collision occurred */
+
+#ifdef KERNEL
+struct proc;
+
+void selrecord __P((struct proc *selector, struct selinfo *));
+void selwakeup __P((struct selinfo *));
+#endif
+
+#endif /* !_SYS_SELECT_H_ */
diff --git a/sys/sys/signal.h b/sys/sys/signal.h
new file mode 100644
index 0000000..8ccded4
--- /dev/null
+++ b/sys/sys/signal.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)signal.h 8.2 (Berkeley) 1/21/94
+ */
+
+#ifndef _SYS_SIGNAL_H_
+#define _SYS_SIGNAL_H_
+
+#define NSIG 32 /* counting 0; could be 33 (mask is 1-32) */
+
+#ifndef _ANSI_SOURCE
+#include <machine/signal.h> /* sigcontext; codes for SIGILL, SIGFPE */
+#endif
+
+#define SIGHUP 1 /* hangup */
+#define SIGINT 2 /* interrupt */
+#define SIGQUIT 3 /* quit */
+#define SIGILL 4 /* illegal instruction (not reset when caught) */
+#ifndef _POSIX_SOURCE
+#define SIGTRAP 5 /* trace trap (not reset when caught) */
+#endif
+#define SIGABRT 6 /* abort() */
+#ifndef _POSIX_SOURCE
+#define SIGIOT SIGABRT /* compatibility */
+#define SIGEMT 7 /* EMT instruction */
+#endif
+#define SIGFPE 8 /* floating point exception */
+#define SIGKILL 9 /* kill (cannot be caught or ignored) */
+#ifndef _POSIX_SOURCE
+#define SIGBUS 10 /* bus error */
+#endif
+#define SIGSEGV 11 /* segmentation violation */
+#ifndef _POSIX_SOURCE
+#define SIGSYS 12 /* bad argument to system call */
+#endif
+#define SIGPIPE 13 /* write on a pipe with no one to read it */
+#define SIGALRM 14 /* alarm clock */
+#define SIGTERM 15 /* software termination signal from kill */
+#ifndef _POSIX_SOURCE
+#define SIGURG 16 /* urgent condition on IO channel */
+#endif
+#define SIGSTOP 17 /* sendable stop signal not from tty */
+#define SIGTSTP 18 /* stop signal from tty */
+#define SIGCONT 19 /* continue a stopped process */
+#define SIGCHLD 20 /* to parent on child stop or exit */
+#define SIGTTIN 21 /* to readers pgrp upon background tty read */
+#define SIGTTOU 22 /* like TTIN for output if (tp->t_local&LTOSTOP) */
+#ifndef _POSIX_SOURCE
+#define SIGIO 23 /* input/output possible signal */
+#define SIGXCPU 24 /* exceeded CPU time limit */
+#define SIGXFSZ 25 /* exceeded file size limit */
+#define SIGVTALRM 26 /* virtual time alarm */
+#define SIGPROF 27 /* profiling time alarm */
+#define SIGWINCH 28 /* window size changes */
+#define SIGINFO 29 /* information request */
+#endif
+#define SIGUSR1 30 /* user defined signal 1 */
+#define SIGUSR2 31 /* user defined signal 2 */
+
+#if defined(_ANSI_SOURCE) || defined(__cplusplus)
+/*
+ * Language spec sez we must list exactly one parameter, even though we
+ * actually supply three. Ugh!
+ */
+#define SIG_DFL (void (*)(int))0
+#define SIG_IGN (void (*)(int))1
+#define SIG_ERR (void (*)(int))-1
+#else
+#define SIG_DFL (void (*)())0
+#define SIG_IGN (void (*)())1
+#define SIG_ERR (void (*)())-1
+#endif
+
+#ifndef _ANSI_SOURCE
+typedef unsigned int sigset_t;
+
+/*
+ * Signal vector "template" used in sigaction call.
+ */
+struct sigaction {
+ void (*sa_handler)(); /* signal handler */
+ sigset_t sa_mask; /* signal mask to apply */
+ int sa_flags; /* see signal options below */
+};
+#ifndef _POSIX_SOURCE
+#define SA_ONSTACK 0x0001 /* take signal on signal stack */
+#define SA_RESTART 0x0002 /* restart system on signal return */
+#define SA_DISABLE 0x0004 /* disable taking signals on alternate stack */
+#ifdef COMPAT_SUNOS
+#define SA_USERTRAMP 0x0100 /* do not bounce off kernel's sigtramp */
+#endif
+#endif
+#define SA_NOCLDSTOP 0x0008 /* do not generate SIGCHLD on child stop */
+
+/*
+ * Flags for sigprocmask:
+ */
+#define SIG_BLOCK 1 /* block specified signal set */
+#define SIG_UNBLOCK 2 /* unblock specified signal set */
+#define SIG_SETMASK 3 /* set specified signal set */
+
+#ifndef _POSIX_SOURCE
+#ifndef KERNEL
+#include <sys/cdefs.h>
+#endif
+typedef void (*sig_t) __P((int)); /* type of signal function */
+
+/*
+ * Structure used in sigaltstack call.
+ */
+struct sigaltstack {
+ char *ss_base; /* signal stack base */
+ int ss_size; /* signal stack length */
+ int ss_flags; /* SA_DISABLE and/or SA_ONSTACK */
+};
+#define MINSIGSTKSZ 8192 /* minimum allowable stack */
+#define SIGSTKSZ (MINSIGSTKSZ + 32768) /* recommended stack size */
+
+/*
+ * 4.3 compatibility:
+ * Signal vector "template" used in sigvec call.
+ */
+struct sigvec {
+ void (*sv_handler)(); /* signal handler */
+ int sv_mask; /* signal mask to apply */
+ int sv_flags; /* see signal options below */
+};
+
+#define SV_ONSTACK SA_ONSTACK
+#define SV_INTERRUPT SA_RESTART /* same bit, opposite sense */
+#define sv_onstack sv_flags /* isn't compatibility wonderful! */
+
+/*
+ * Structure used in sigstack call.
+ */
+struct sigstack {
+ char *ss_sp; /* signal stack pointer */
+ int ss_onstack; /* current status */
+};
+
+/*
+ * Macro for converting signal number to a mask suitable for
+ * sigblock().
+ */
+#define sigmask(m) (1 << ((m)-1))
+
+#define BADSIG SIG_ERR
+
+#endif /* !_POSIX_SOURCE */
+#endif /* !_ANSI_SOURCE */
+
+/*
+ * For historical reasons; programs expect signal's return value to be
+ * defined by <sys/signal.h>.
+ */
+__BEGIN_DECLS
+void (*signal __P((int, void (*) __P((int))))) __P((int));
+__END_DECLS
+#endif /* !_SYS_SIGNAL_H_ */
diff --git a/sys/sys/signalvar.h b/sys/sys/signalvar.h
new file mode 100644
index 0000000..3d7e68b
--- /dev/null
+++ b/sys/sys/signalvar.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)signalvar.h 8.3 (Berkeley) 1/4/94
+ */
+
+#ifndef _SYS_SIGNALVAR_H_ /* tmp for user.h */
+#define _SYS_SIGNALVAR_H_
+
+/*
+ * Kernel signal definitions and data structures,
+ * not exported to user programs.
+ */
+
+/*
+ * Process signal actions and state, needed only within the process
+ * (not necessarily resident).
+ */
+struct sigacts {
+ sig_t ps_sigact[NSIG]; /* disposition of signals */
+ sigset_t ps_catchmask[NSIG]; /* signals to be blocked */
+ sigset_t ps_sigonstack; /* signals to take on sigstack */
+ sigset_t ps_sigintr; /* signals that interrupt syscalls */
+ sigset_t ps_oldmask; /* saved mask from before sigpause */
+ int ps_flags; /* signal flags, below */
+ struct sigaltstack ps_sigstk; /* sp & on stack state variable */
+ int ps_sig; /* for core dump/debugger XXX */
+ int ps_code; /* for core dump/debugger XXX */
+ int ps_addr; /* for core dump/debugger XXX */
+ sigset_t ps_usertramp; /* SunOS compat; libc sigtramp XXX */
+};
+
+/* signal flags */
+#define SAS_OLDMASK 0x01 /* need to restore mask before pause */
+#define SAS_ALTSTACK 0x02 /* have alternate signal stack */
+
+/* additional signal action values, used only temporarily/internally */
+#define SIG_CATCH (void (*)())2
+#define SIG_HOLD (void (*)())3
+
+/*
+ * get signal action for process and signal; currently only for current process
+ */
+#define SIGACTION(p, sig) (p->p_sigacts->ps_sigact[(sig)])
+
+/*
+ * Determine signal that should be delivered to process p, the current
+ * process, 0 if none. If there is a pending stop signal with default
+ * action, the process stops in issig().
+ */
+#define CURSIG(p) \
+ (((p)->p_siglist == 0 || \
+ ((p)->p_flag & P_TRACED) == 0 && \
+ ((p)->p_siglist & ~(p)->p_sigmask) == 0) ? \
+ 0 : issignal(p))
+
+/*
+ * Clear a pending signal from a process.
+ */
+#define CLRSIG(p, sig) { (p)->p_siglist &= ~sigmask(sig); }
+
+/*
+ * Signal properties and actions.
+ * The array below categorizes the signals and their default actions
+ * according to the following properties:
+ */
+#define SA_KILL 0x01 /* terminates process by default */
+#define SA_CORE 0x02 /* ditto and coredumps */
+#define SA_STOP 0x04 /* suspend process */
+#define SA_TTYSTOP 0x08 /* ditto, from tty */
+#define SA_IGNORE 0x10 /* ignore by default */
+#define SA_CONT 0x20 /* continue if suspended */
+#define SA_CANTMASK 0x40 /* non-maskable, catchable */
+
+#ifdef SIGPROP
+int sigprop[NSIG + 1] = {
+ 0, /* unused */
+ SA_KILL, /* SIGHUP */
+ SA_KILL, /* SIGINT */
+ SA_KILL|SA_CORE, /* SIGQUIT */
+ SA_KILL|SA_CORE, /* SIGILL */
+ SA_KILL|SA_CORE, /* SIGTRAP */
+ SA_KILL|SA_CORE, /* SIGABRT */
+ SA_KILL|SA_CORE, /* SIGEMT */
+ SA_KILL|SA_CORE, /* SIGFPE */
+ SA_KILL, /* SIGKILL */
+ SA_KILL|SA_CORE, /* SIGBUS */
+ SA_KILL|SA_CORE, /* SIGSEGV */
+ SA_KILL|SA_CORE, /* SIGSYS */
+ SA_KILL, /* SIGPIPE */
+ SA_KILL, /* SIGALRM */
+ SA_KILL, /* SIGTERM */
+ SA_IGNORE, /* SIGURG */
+ SA_STOP, /* SIGSTOP */
+ SA_STOP|SA_TTYSTOP, /* SIGTSTP */
+ SA_IGNORE|SA_CONT, /* SIGCONT */
+ SA_IGNORE, /* SIGCHLD */
+ SA_STOP|SA_TTYSTOP, /* SIGTTIN */
+ SA_STOP|SA_TTYSTOP, /* SIGTTOU */
+ SA_IGNORE, /* SIGIO */
+ SA_KILL, /* SIGXCPU */
+ SA_KILL, /* SIGXFSZ */
+ SA_KILL, /* SIGVTALRM */
+ SA_KILL, /* SIGPROF */
+ SA_IGNORE, /* SIGWINCH */
+ SA_IGNORE, /* SIGINFO */
+ SA_KILL, /* SIGUSR1 */
+ SA_KILL, /* SIGUSR2 */
+};
+
+#define contsigmask (sigmask(SIGCONT))
+#define stopsigmask (sigmask(SIGSTOP) | sigmask(SIGTSTP) | \
+ sigmask(SIGTTIN) | sigmask(SIGTTOU))
+
+#endif /* SIGPROP */
+
+#define sigcantmask (sigmask(SIGKILL) | sigmask(SIGSTOP))
+
+#ifdef KERNEL
+/*
+ * Machine-independent functions:
+ */
+int coredump __P((struct proc *p));
+void execsigs __P((struct proc *p));
+void gsignal __P((int pgid, int sig));
+int issig __P((struct proc *p));
+void pgsignal __P((struct pgrp *pgrp, int sig, int checkctty));
+void postsig __P((int sig));
+void psignal __P((struct proc *p, int sig));
+void siginit __P((struct proc *p));
+void trapsignal __P((struct proc *p, int sig, unsigned code));
+
+/*
+ * Machine-dependent functions:
+ */
+void sendsig __P((sig_t action, int sig, int returnmask, unsigned code));
+#endif /* KERNEL */
+#endif /* !_SYS_SIGNALVAR_H_ */
diff --git a/sys/sys/socket.h b/sys/sys/socket.h
new file mode 100644
index 0000000..f6728e9
--- /dev/null
+++ b/sys/sys/socket.h
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 1982, 1985, 1986, 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)socket.h 8.4 (Berkeley) 2/21/94
+ */
+
+#ifndef _SYS_SOCKET_H_
+#define _SYS_SOCKET_H_
+
+/*
+ * Definitions related to sockets: types, address families, options.
+ */
+
+/*
+ * Types
+ */
+#define SOCK_STREAM 1 /* stream socket */
+#define SOCK_DGRAM 2 /* datagram socket */
+#define SOCK_RAW 3 /* raw-protocol interface */
+#define SOCK_RDM 4 /* reliably-delivered message */
+#define SOCK_SEQPACKET 5 /* sequenced packet stream */
+
+/*
+ * Option flags per-socket.
+ */
+#define SO_DEBUG 0x0001 /* turn on debugging info recording */
+#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */
+#define SO_REUSEADDR 0x0004 /* allow local address reuse */
+#define SO_KEEPALIVE 0x0008 /* keep connections alive */
+#define SO_DONTROUTE 0x0010 /* just use interface addresses */
+#define SO_BROADCAST 0x0020 /* permit sending of broadcast msgs */
+#define SO_USELOOPBACK 0x0040 /* bypass hardware when possible */
+#define SO_LINGER 0x0080 /* linger on close if data present */
+#define SO_OOBINLINE 0x0100 /* leave received OOB data in line */
+#define SO_REUSEPORT 0x0200 /* allow local address & port reuse */
+
+/*
+ * Additional options, not kept in so_options.
+ */
+#define SO_SNDBUF 0x1001 /* send buffer size */
+#define SO_RCVBUF 0x1002 /* receive buffer size */
+#define SO_SNDLOWAT 0x1003 /* send low-water mark */
+#define SO_RCVLOWAT 0x1004 /* receive low-water mark */
+#define SO_SNDTIMEO 0x1005 /* send timeout */
+#define SO_RCVTIMEO 0x1006 /* receive timeout */
+#define SO_ERROR 0x1007 /* get error status and clear */
+#define SO_TYPE 0x1008 /* get socket type */
+
+/*
+ * Structure used for manipulating linger option.
+ */
+struct linger {
+ int l_onoff; /* option on/off */
+ int l_linger; /* linger time */
+};
+
+/*
+ * Level number for (get/set)sockopt() to apply to socket itself.
+ */
+#define SOL_SOCKET 0xffff /* options for socket level */
+
+/*
+ * Address families.
+ */
+#define AF_UNSPEC 0 /* unspecified */
+#define AF_LOCAL 1 /* local to host (pipes, portals) */
+#define AF_UNIX AF_LOCAL /* backward compatibility */
+#define AF_INET 2 /* internetwork: UDP, TCP, etc. */
+#define AF_IMPLINK 3 /* arpanet imp addresses */
+#define AF_PUP 4 /* pup protocols: e.g. BSP */
+#define AF_CHAOS 5 /* mit CHAOS protocols */
+#define AF_NS 6 /* XEROX NS protocols */
+#define AF_ISO 7 /* ISO protocols */
+#define AF_OSI AF_ISO
+#define AF_ECMA 8 /* european computer manufacturers */
+#define AF_DATAKIT 9 /* datakit protocols */
+#define AF_CCITT 10 /* CCITT protocols, X.25 etc */
+#define AF_SNA 11 /* IBM SNA */
+#define AF_DECnet 12 /* DECnet */
+#define AF_DLI 13 /* DEC Direct data link interface */
+#define AF_LAT 14 /* LAT */
+#define AF_HYLINK 15 /* NSC Hyperchannel */
+#define AF_APPLETALK 16 /* Apple Talk */
+#define AF_ROUTE 17 /* Internal Routing Protocol */
+#define AF_LINK 18 /* Link layer interface */
+#define pseudo_AF_XTP 19 /* eXpress Transfer Protocol (no AF) */
+#define AF_COIP 20 /* connection-oriented IP, aka ST II */
+#define AF_CNT 21 /* Computer Network Technology */
+#define pseudo_AF_RTIP 22 /* Help Identify RTIP packets */
+#define AF_IPX 23 /* Novell Internet Protocol */
+#define AF_SIP 24 /* Simple Internet Protocol */
+#define pseudo_AF_PIP 25 /* Help Identify PIP packets */
+
+#define AF_MAX 26
+
+/*
+ * Structure used by kernel to store most
+ * addresses.
+ */
+struct sockaddr {
+ u_char sa_len; /* total length */
+ u_char sa_family; /* address family */
+ char sa_data[14]; /* actually longer; address value */
+};
+
+/*
+ * Structure used by kernel to pass protocol
+ * information in raw sockets.
+ */
+struct sockproto {
+ u_short sp_family; /* address family */
+ u_short sp_protocol; /* protocol */
+};
+
+/*
+ * Protocol families, same as address families for now.
+ */
+#define PF_UNSPEC AF_UNSPEC
+#define PF_LOCAL AF_LOCAL
+#define PF_UNIX PF_LOCAL /* backward compatibility */
+#define PF_INET AF_INET
+#define PF_IMPLINK AF_IMPLINK
+#define PF_PUP AF_PUP
+#define PF_CHAOS AF_CHAOS
+#define PF_NS AF_NS
+#define PF_ISO AF_ISO
+#define PF_OSI AF_ISO
+#define PF_ECMA AF_ECMA
+#define PF_DATAKIT AF_DATAKIT
+#define PF_CCITT AF_CCITT
+#define PF_SNA AF_SNA
+#define PF_DECnet AF_DECnet
+#define PF_DLI AF_DLI
+#define PF_LAT AF_LAT
+#define PF_HYLINK AF_HYLINK
+#define PF_APPLETALK AF_APPLETALK
+#define PF_ROUTE AF_ROUTE
+#define PF_LINK AF_LINK
+#define PF_XTP pseudo_AF_XTP /* really just proto family, no AF */
+#define PF_COIP AF_COIP
+#define PF_CNT AF_CNT
+#define PF_SIP AF_SIP
+#define PF_IPX AF_IPX /* same format as AF_NS */
+#define PF_RTIP pseudo_AF_FTIP /* same format as AF_INET */
+#define PF_PIP pseudo_AF_PIP
+
+#define PF_MAX AF_MAX
+
+/*
+ * Definitions for network related sysctl, CTL_NET.
+ *
+ * Second level is protocol family.
+ * Third level is protocol number.
+ *
+ * Further levels are defined by the individual families below.
+ */
+#define NET_MAXID AF_MAX
+
+#define CTL_NET_NAMES { \
+ { 0, 0 }, \
+ { "unix", CTLTYPE_NODE }, \
+ { "inet", CTLTYPE_NODE }, \
+ { "implink", CTLTYPE_NODE }, \
+ { "pup", CTLTYPE_NODE }, \
+ { "chaos", CTLTYPE_NODE }, \
+ { "xerox_ns", CTLTYPE_NODE }, \
+ { "iso", CTLTYPE_NODE }, \
+ { "emca", CTLTYPE_NODE }, \
+ { "datakit", CTLTYPE_NODE }, \
+ { "ccitt", CTLTYPE_NODE }, \
+ { "ibm_sna", CTLTYPE_NODE }, \
+ { "decnet", CTLTYPE_NODE }, \
+ { "dec_dli", CTLTYPE_NODE }, \
+ { "lat", CTLTYPE_NODE }, \
+ { "hylink", CTLTYPE_NODE }, \
+ { "appletalk", CTLTYPE_NODE }, \
+ { "route", CTLTYPE_NODE }, \
+ { "link_layer", CTLTYPE_NODE }, \
+ { "xtp", CTLTYPE_NODE }, \
+ { "coip", CTLTYPE_NODE }, \
+ { "cnt", CTLTYPE_NODE }, \
+ { "rtip", CTLTYPE_NODE }, \
+ { "ipx", CTLTYPE_NODE }, \
+ { "sip", CTLTYPE_NODE }, \
+ { "pip", CTLTYPE_NODE }, \
+}
+
+/*
+ * PF_ROUTE - Routing table
+ *
+ * Three additional levels are defined:
+ * Fourth: address family, 0 is wildcard
+ * Fifth: type of info, defined below
+ * Sixth: flag(s) to mask with for NET_RT_FLAGS
+ */
+#define NET_RT_DUMP 1 /* dump; may limit to a.f. */
+#define NET_RT_FLAGS 2 /* by flags, e.g. RESOLVING */
+#define NET_RT_IFLIST 3 /* survey interface list */
+#define NET_RT_MAXID 4
+
+#define CTL_NET_RT_NAMES { \
+ { 0, 0 }, \
+ { "dump", CTLTYPE_STRUCT }, \
+ { "flags", CTLTYPE_STRUCT }, \
+ { "iflist", CTLTYPE_STRUCT }, \
+}
+
+/*
+ * Maximum queue length specifiable by listen.
+ */
+#define SOMAXCONN 5
+
+/*
+ * Message header for recvmsg and sendmsg calls.
+ * Used value-result for recvmsg, value only for sendmsg.
+ */
+struct msghdr {
+ caddr_t msg_name; /* optional address */
+ u_int msg_namelen; /* size of address */
+ struct iovec *msg_iov; /* scatter/gather array */
+ u_int msg_iovlen; /* # elements in msg_iov */
+ caddr_t msg_control; /* ancillary data, see below */
+ u_int msg_controllen; /* ancillary data buffer len */
+ int msg_flags; /* flags on received message */
+};
+
+#define MSG_OOB 0x1 /* process out-of-band data */
+#define MSG_PEEK 0x2 /* peek at incoming message */
+#define MSG_DONTROUTE 0x4 /* send without using routing tables */
+#define MSG_EOR 0x8 /* data completes record */
+#define MSG_TRUNC 0x10 /* data discarded before delivery */
+#define MSG_CTRUNC 0x20 /* control data lost before delivery */
+#define MSG_WAITALL 0x40 /* wait for full request or error */
+#define MSG_DONTWAIT 0x80 /* this message should be nonblocking */
+
+/*
+ * Header for ancillary data objects in msg_control buffer.
+ * Used for additional information with/about a datagram
+ * not expressible by flags. The format is a sequence
+ * of message elements headed by cmsghdr structures.
+ */
+struct cmsghdr {
+ u_int cmsg_len; /* data byte count, including hdr */
+ int cmsg_level; /* originating protocol */
+ int cmsg_type; /* protocol-specific type */
+/* followed by u_char cmsg_data[]; */
+};
+
+/* given pointer to struct cmsghdr, return pointer to data */
+#define CMSG_DATA(cmsg) ((u_char *)((cmsg) + 1))
+
+/* given pointer to struct cmsghdr, return pointer to next cmsghdr */
+#define CMSG_NXTHDR(mhdr, cmsg) \
+ (((caddr_t)(cmsg) + (cmsg)->cmsg_len + sizeof(struct cmsghdr) > \
+ (mhdr)->msg_control + (mhdr)->msg_controllen) ? \
+ (struct cmsghdr *)NULL : \
+ (struct cmsghdr *)((caddr_t)(cmsg) + ALIGN((cmsg)->cmsg_len)))
+
+#define CMSG_FIRSTHDR(mhdr) ((struct cmsghdr *)(mhdr)->msg_control)
+
+/* "Socket"-level control message types: */
+#define SCM_RIGHTS 0x01 /* access rights (array of int) */
+
+/*
+ * 4.3 compat sockaddr, move to compat file later
+ */
+struct osockaddr {
+ u_short sa_family; /* address family */
+ char sa_data[14]; /* up to 14 bytes of direct address */
+};
+
+/*
+ * 4.3-compat message header (move to compat file later).
+ */
+struct omsghdr {
+ caddr_t msg_name; /* optional address */
+ int msg_namelen; /* size of address */
+ struct iovec *msg_iov; /* scatter/gather array */
+ int msg_iovlen; /* # elements in msg_iov */
+ caddr_t msg_accrights; /* access rights sent/received */
+ int msg_accrightslen;
+};
+
+#ifndef KERNEL
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int accept __P((int, struct sockaddr *, int *));
+int bind __P((int, const struct sockaddr *, int));
+int connect __P((int, const struct sockaddr *, int));
+int getpeername __P((int, struct sockaddr *, int *));
+int getsockname __P((int, struct sockaddr *, int *));
+int getsockopt __P((int, int, int, void *, int *));
+int listen __P((int, int));
+ssize_t recv __P((int, void *, size_t, int));
+ssize_t recvfrom __P((int, void *, size_t, int, struct sockaddr *, int *));
+ssize_t recvmsg __P((int, struct msghdr *, int));
+ssize_t send __P((int, const void *, size_t, int));
+ssize_t sendto __P((int, const void *,
+ size_t, int, const struct sockaddr *, int));
+ssize_t sendmsg __P((int, const struct msghdr *, int));
+int setsockopt __P((int, int, int, const void *, int));
+int shutdown __P((int, int));
+int socket __P((int, int, int));
+int socketpair __P((int, int, int, int *));
+__END_DECLS
+
+#endif /* !KERNEL */
+#endif /* !_SYS_SOCKET_H_ */
diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h
new file mode 100644
index 0000000..ff10404
--- /dev/null
+++ b/sys/sys/socketvar.h
@@ -0,0 +1,207 @@
+/*-
+ * Copyright (c) 1982, 1986, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)socketvar.h 8.1 (Berkeley) 6/2/93
+ */
+
+#include <sys/select.h> /* for struct selinfo */
+
+/*
+ * Kernel structure per socket.
+ * Contains send and receive buffer queues,
+ * handle on protocol and pointer to protocol
+ * private data and error information.
+ */
+struct socket {
+ short so_type; /* generic type, see socket.h */
+ short so_options; /* from socket call, see socket.h */
+ short so_linger; /* time to linger while closing */
+ short so_state; /* internal state flags SS_*, below */
+ caddr_t so_pcb; /* protocol control block */
+ struct protosw *so_proto; /* protocol handle */
+/*
+ * Variables for connection queueing.
+ * Socket where accepts occur is so_head in all subsidiary sockets.
+ * If so_head is 0, socket is not related to an accept.
+ * For head socket so_q0 queues partially completed connections,
+ * while so_q is a queue of connections ready to be accepted.
+ * If a connection is aborted and it has so_head set, then
+ * it has to be pulled out of either so_q0 or so_q.
+ * We allow connections to queue up based on current queue lengths
+ * and limit on number of queued connections for this socket.
+ */
+ struct socket *so_head; /* back pointer to accept socket */
+ struct socket *so_q0; /* queue of partial connections */
+ struct socket *so_q; /* queue of incoming connections */
+ short so_q0len; /* partials on so_q0 */
+ short so_qlen; /* number of connections on so_q */
+ short so_qlimit; /* max number queued connections */
+ short so_timeo; /* connection timeout */
+ u_short so_error; /* error affecting connection */
+ pid_t so_pgid; /* pgid for signals */
+ u_long so_oobmark; /* chars to oob mark */
+/*
+ * Variables for socket buffering.
+ */
+ struct sockbuf {
+ u_long sb_cc; /* actual chars in buffer */
+ u_long sb_hiwat; /* max actual char count */
+ u_long sb_mbcnt; /* chars of mbufs used */
+ u_long sb_mbmax; /* max chars of mbufs to use */
+ long sb_lowat; /* low water mark */
+ struct mbuf *sb_mb; /* the mbuf chain */
+ struct selinfo sb_sel; /* process selecting read/write */
+ short sb_flags; /* flags, see below */
+ short sb_timeo; /* timeout for read/write */
+ } so_rcv, so_snd;
+#define SB_MAX (256*1024) /* default for max chars in sockbuf */
+#define SB_LOCK 0x01 /* lock on data queue */
+#define SB_WANT 0x02 /* someone is waiting to lock */
+#define SB_WAIT 0x04 /* someone is waiting for data/space */
+#define SB_SEL 0x08 /* someone is selecting */
+#define SB_ASYNC 0x10 /* ASYNC I/O, need signals */
+#define SB_NOTIFY (SB_WAIT|SB_SEL|SB_ASYNC)
+#define SB_NOINTR 0x40 /* operations not interruptible */
+
+ caddr_t so_tpcb; /* Wisc. protocol control block XXX */
+ void (*so_upcall) __P((struct socket *so, caddr_t arg, int waitf));
+ caddr_t so_upcallarg; /* Arg for above */
+};
+
+/*
+ * Socket state bits.
+ */
+#define SS_NOFDREF 0x001 /* no file table ref any more */
+#define SS_ISCONNECTED 0x002 /* socket connected to a peer */
+#define SS_ISCONNECTING 0x004 /* in process of connecting to peer */
+#define SS_ISDISCONNECTING 0x008 /* in process of disconnecting */
+#define SS_CANTSENDMORE 0x010 /* can't send more data to peer */
+#define SS_CANTRCVMORE 0x020 /* can't receive more data from peer */
+#define SS_RCVATMARK 0x040 /* at mark on input */
+
+#define SS_PRIV 0x080 /* privileged for broadcast, raw... */
+#define SS_NBIO 0x100 /* non-blocking ops */
+#define SS_ASYNC 0x200 /* async i/o notify */
+#define SS_ISCONFIRMING 0x400 /* deciding to accept connection req */
+
+
+/*
+ * Macros for sockets and socket buffering.
+ */
+
+/*
+ * How much space is there in a socket buffer (so->so_snd or so->so_rcv)?
+ * This is problematical if the fields are unsigned, as the space might
+ * still be negative (cc > hiwat or mbcnt > mbmax). Should detect
+ * overflow and return 0. Should use "lmin" but it doesn't exist now.
+ */
+#define sbspace(sb) \
+ ((long) imin((int)((sb)->sb_hiwat - (sb)->sb_cc), \
+ (int)((sb)->sb_mbmax - (sb)->sb_mbcnt)))
+
+/* do we have to send all at once on a socket? */
+#define sosendallatonce(so) \
+ ((so)->so_proto->pr_flags & PR_ATOMIC)
+
+/* can we read something from so? */
+#define soreadable(so) \
+ ((so)->so_rcv.sb_cc >= (so)->so_rcv.sb_lowat || \
+ ((so)->so_state & SS_CANTRCVMORE) || \
+ (so)->so_qlen || (so)->so_error)
+
+/* can we write something to so? */
+#define sowriteable(so) \
+ (sbspace(&(so)->so_snd) >= (so)->so_snd.sb_lowat && \
+ (((so)->so_state&SS_ISCONNECTED) || \
+ ((so)->so_proto->pr_flags&PR_CONNREQUIRED)==0) || \
+ ((so)->so_state & SS_CANTSENDMORE) || \
+ (so)->so_error)
+
+/* adjust counters in sb reflecting allocation of m */
+#define sballoc(sb, m) { \
+ (sb)->sb_cc += (m)->m_len; \
+ (sb)->sb_mbcnt += MSIZE; \
+ if ((m)->m_flags & M_EXT) \
+ (sb)->sb_mbcnt += (m)->m_ext.ext_size; \
+}
+
+/* adjust counters in sb reflecting freeing of m */
+#define sbfree(sb, m) { \
+ (sb)->sb_cc -= (m)->m_len; \
+ (sb)->sb_mbcnt -= MSIZE; \
+ if ((m)->m_flags & M_EXT) \
+ (sb)->sb_mbcnt -= (m)->m_ext.ext_size; \
+}
+
+/*
+ * Set lock on sockbuf sb; sleep if lock is already held.
+ * Unless SB_NOINTR is set on sockbuf, sleep is interruptible.
+ * Returns error without lock if sleep is interrupted.
+ */
+#define sblock(sb, wf) ((sb)->sb_flags & SB_LOCK ? \
+ (((wf) == M_WAITOK) ? sb_lock(sb) : EWOULDBLOCK) : \
+ ((sb)->sb_flags |= SB_LOCK), 0)
+
+/* release lock on sockbuf sb */
+#define sbunlock(sb) { \
+ (sb)->sb_flags &= ~SB_LOCK; \
+ if ((sb)->sb_flags & SB_WANT) { \
+ (sb)->sb_flags &= ~SB_WANT; \
+ wakeup((caddr_t)&(sb)->sb_flags); \
+ } \
+}
+
+#define sorwakeup(so) { sowakeup((so), &(so)->so_rcv); \
+ if ((so)->so_upcall) \
+ (*((so)->so_upcall))((so), (so)->so_upcallarg, M_DONTWAIT); \
+ }
+
+#define sowwakeup(so) sowakeup((so), &(so)->so_snd)
+
+#ifdef KERNEL
+u_long sb_max;
+/* to catch callers missing new second argument to sonewconn: */
+#define sonewconn(head, connstatus) sonewconn1((head), (connstatus))
+struct socket *sonewconn1 __P((struct socket *head, int connstatus));
+
+/* strings for sleep message: */
+extern char netio[], netcon[], netcls[];
+
+/*
+ * File operations on sockets.
+ */
+int soo_read __P((struct file *fp, struct uio *uio, struct ucred *cred));
+int soo_write __P((struct file *fp, struct uio *uio, struct ucred *cred));
+int soo_ioctl __P((struct file *fp, int com, caddr_t data, struct proc *p));
+int soo_select __P((struct file *fp, int which, struct proc *p));
+int soo_close __P((struct file *fp, struct proc *p));
+#endif
diff --git a/sys/sys/sockio.h b/sys/sys/sockio.h
new file mode 100644
index 0000000..eb5a44a
--- /dev/null
+++ b/sys/sys/sockio.h
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 1982, 1986, 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)sockio.h 8.1 (Berkeley) 3/28/94
+ */
+
+#ifndef _SYS_SOCKIO_H_
+#define _SYS_SOCKIO_H_
+
+#include <sys/ioccom.h>
+
+/* Socket ioctl's. */
+#define SIOCSHIWAT _IOW('s', 0, int) /* set high watermark */
+#define SIOCGHIWAT _IOR('s', 1, int) /* get high watermark */
+#define SIOCSLOWAT _IOW('s', 2, int) /* set low watermark */
+#define SIOCGLOWAT _IOR('s', 3, int) /* get low watermark */
+#define SIOCATMARK _IOR('s', 7, int) /* at oob mark? */
+#define SIOCSPGRP _IOW('s', 8, int) /* set process group */
+#define SIOCGPGRP _IOR('s', 9, int) /* get process group */
+
+#define SIOCADDRT _IOW('r', 10, struct ortentry) /* add route */
+#define SIOCDELRT _IOW('r', 11, struct ortentry) /* delete route */
+
+#define SIOCSIFADDR _IOW('i', 12, struct ifreq) /* set ifnet address */
+#define OSIOCGIFADDR _IOWR('i', 13, struct ifreq) /* get ifnet address */
+#define SIOCGIFADDR _IOWR('i', 33, struct ifreq) /* get ifnet address */
+#define SIOCSIFDSTADDR _IOW('i', 14, struct ifreq) /* set p-p address */
+#define OSIOCGIFDSTADDR _IOWR('i', 15, struct ifreq) /* get p-p address */
+#define SIOCGIFDSTADDR _IOWR('i', 34, struct ifreq) /* get p-p address */
+#define SIOCSIFFLAGS _IOW('i', 16, struct ifreq) /* set ifnet flags */
+#define SIOCGIFFLAGS _IOWR('i', 17, struct ifreq) /* get ifnet flags */
+#define OSIOCGIFBRDADDR _IOWR('i', 18, struct ifreq) /* get broadcast addr */
+#define SIOCGIFBRDADDR _IOWR('i', 35, struct ifreq) /* get broadcast addr */
+#define SIOCSIFBRDADDR _IOW('i', 19, struct ifreq) /* set broadcast addr */
+#define OSIOCGIFCONF _IOWR('i', 20, struct ifconf) /* get ifnet list */
+#define SIOCGIFCONF _IOWR('i', 36, struct ifconf) /* get ifnet list */
+#define OSIOCGIFNETMASK _IOWR('i', 21, struct ifreq) /* get net addr mask */
+#define SIOCGIFNETMASK _IOWR('i', 37, struct ifreq) /* get net addr mask */
+#define SIOCSIFNETMASK _IOW('i', 22, struct ifreq) /* set net addr mask */
+#define SIOCGIFMETRIC _IOWR('i', 23, struct ifreq) /* get IF metric */
+#define SIOCSIFMETRIC _IOW('i', 24, struct ifreq) /* set IF metric */
+#define SIOCDIFADDR _IOW('i', 25, struct ifreq) /* delete IF addr */
+#define SIOCAIFADDR _IOW('i', 26, struct ifaliasreq)/* add/chg IF alias */
+
+#define SIOCADDMULTI _IOW('i', 49, struct ifreq) /* add m'cast addr */
+#define SIOCDELMULTI _IOW('i', 50, struct ifreq) /* del m'cast addr */
+
+#endif /* !_SYS_SOCKIO_H_ */
diff --git a/sys/sys/soundcard.h b/sys/sys/soundcard.h
new file mode 100644
index 0000000..ce28a14
--- /dev/null
+++ b/sys/sys/soundcard.h
@@ -0,0 +1,763 @@
+#ifndef _SOUNDCARD_H_
+#define _SOUNDCARD_H_
+/*
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ */
+
+ /*
+ * If you make modifications to this file, please contact me before
+ * distributing the modified version. There is already enough
+ * divercity in the world.
+ *
+ * Regards,
+ * Hannu Savolainen
+ * hannu@voxware.pp.fi, Hannu.Savolainen@helsinki.fi
+ */
+
+#define SOUND_VERSION 205
+#define VOXWARE
+
+#include <sys/ioctl.h>
+
+/*
+ * Supported card ID numbers (Should be somewhere else?)
+ */
+
+#define SNDCARD_ADLIB 1
+#define SNDCARD_SB 2
+#define SNDCARD_PAS 3
+#define SNDCARD_GUS 4
+#define SNDCARD_MPU401 5
+#define SNDCARD_SB16 6
+#define SNDCARD_SB16MIDI 7
+
+/***********************************
+ * IOCTL Commands for /dev/sequencer
+ */
+
+#ifndef _IOWR
+/* @(#)ioctlp.h */
+
+/* Ioctl's have the command encoded in the lower word,
+ * and the size of any in or out parameters in the upper
+ * word. The high 2 bits of the upper word are used
+ * to encode the in/out status of the parameter; for now
+ * we restrict parameters to at most 128 bytes.
+ */
+/* #define IOCTYPE (0xff<<8) */
+#define IOCPARM_MASK 0x7f /* parameters must be < 128 bytes */
+#define IOC_VOID 0x00000000 /* no parameters */
+#define IOC_OUT 0x20000000 /* copy out parameters */
+#define IOC_IN 0x40000000 /* copy in parameters */
+#define IOC_INOUT (IOC_IN|IOC_OUT)
+/* the 0x20000000 is so we can distinguish new ioctl's from old */
+#define _IO(x,y) ((int)(IOC_VOID|(x<<8)|y))
+#define _IOR(x,y,t) ((int)(IOC_OUT|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y))
+#define _IOW(x,y,t) ((int)(IOC_IN|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y))
+/* this should be _IORW, but stdio got there first */
+#define _IOWR(x,y,t) ((int)(IOC_INOUT|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y))
+#endif /* !_IOWR */
+
+#define SNDCTL_SEQ_RESET _IO ('Q', 0)
+#define SNDCTL_SEQ_SYNC _IO ('Q', 1)
+#define SNDCTL_SYNTH_INFO _IOWR('Q', 2, struct synth_info)
+#define SNDCTL_SEQ_CTRLRATE _IOWR('Q', 3, int) /* Set/get timer resolution (HZ) */
+#define SNDCTL_SEQ_GETOUTCOUNT _IOR ('Q', 4, int)
+#define SNDCTL_SEQ_GETINCOUNT _IOR ('Q', 5, int)
+#define SNDCTL_SEQ_PERCMODE _IOW ('Q', 6, int)
+#define SNDCTL_FM_LOAD_INSTR _IOW ('Q', 7, struct sbi_instrument) /* Valid for FM only */
+#define SNDCTL_SEQ_TESTMIDI _IOW ('Q', 8, int)
+#define SNDCTL_SEQ_RESETSAMPLES _IOW ('Q', 9, int)
+#define SNDCTL_SEQ_NRSYNTHS _IOR ('Q',10, int)
+#define SNDCTL_SEQ_NRMIDIS _IOR ('Q',11, int)
+#define SNDCTL_MIDI_INFO _IOWR('Q',12, struct midi_info)
+#define SNDCTL_SEQ_TRESHOLD _IOW ('Q',13, int)
+#define SNDCTL_SYNTH_MEMAVL _IOWR('Q',14, int) /* in=dev#, out=memsize */
+#define SNDCTL_FM_4OP_ENABLE _IOW ('Q',15, int) /* in=dev# */
+#define SNDCTL_PMGR_ACCESS _IOWR('Q',16, struct patmgr_info)
+
+/*
+ * Sample loading mechanism for internal synthesizers (/dev/sequencer)
+ * The following patch_info structure has been designed to support
+ * Gravis UltraSound. It tries to be universal format for uploading
+ * sample based patches but is propably too limited.
+ */
+
+struct patch_info {
+ short key; /* Use GUS_PATCH here */
+#define GUS_PATCH 0x04fd
+#define OBSOLETE_GUS_PATCH 0x02fd
+ short device_no; /* Synthesizer number */
+ short instr_no; /* Midi pgm# */
+
+ unsigned long mode;
+/*
+ * The least significant byte has the same format than the GUS .PAT
+ * files
+ */
+#define WAVE_16_BITS 0x01 /* bit 0 = 8 or 16 bit wave data. */
+#define WAVE_UNSIGNED 0x02 /* bit 1 = Signed - Unsigned data. */
+#define WAVE_LOOPING 0x04 /* bit 2 = looping enabled-1. */
+#define WAVE_BIDIR_LOOP 0x08 /* bit 3 = Set is bidirectional looping. */
+#define WAVE_LOOP_BACK 0x10 /* bit 4 = Set is looping backward. */
+#define WAVE_SUSTAIN_ON 0x20 /* bit 5 = Turn sustaining on. (Env. pts. 3)*/
+#define WAVE_ENVELOPES 0x40 /* bit 6 = Enable envelopes - 1 */
+ /* (use the env_rate/env_offs fields). */
+/* Linux specific bits */
+#define WAVE_VIBRATO 0x00010000 /* The vibrato info is valid */
+#define WAVE_TREMOLO 0x00020000 /* The tremolo info is valid */
+#define WAVE_SCALE 0x00040000 /* The scaling info is valid */
+/* Other bits must be zeroed */
+
+ long len; /* Size of the wave data in bytes */
+ long loop_start, loop_end; /* Byte offsets from the beginning */
+
+/*
+ * The base_freq and base_note fields are used when computing the
+ * playback speed for a note. The base_note defines the tone frequency
+ * which is heard if the sample is played using the base_freq as the
+ * playback speed.
+ *
+ * The low_note and high_note fields define the minimum and maximum note
+ * frequencies for which this sample is valid. It is possible to define
+ * more than one samples for a instrument number at the same time. The
+ * low_note and high_note fields are used to select the most suitable one.
+ *
+ * The fields base_note, high_note and low_note should contain
+ * the note frequency multiplied by 1000. For example value for the
+ * middle A is 440*1000.
+ */
+
+ unsigned int base_freq;
+ unsigned long base_note;
+ unsigned long high_note;
+ unsigned long low_note;
+ int panning; /* -128=left, 127=right */
+ int detuning;
+
+/* New fields introduced in version 1.99.5 */
+
+ /* Envelope. Enabled by mode bit WAVE_ENVELOPES */
+ unsigned char env_rate[ 6 ]; /* GUS HW ramping rate */
+ unsigned char env_offset[ 6 ]; /* 255 == 100% */
+
+ /*
+ * The tremolo, vibrato and scale info are not supported yet.
+ * Enable by setting the mode bits WAVE_TREMOLO, WAVE_VIBRATO or
+ * WAVE_SCALE
+ */
+
+ unsigned char tremolo_sweep;
+ unsigned char tremolo_rate;
+ unsigned char tremolo_depth;
+
+ unsigned char vibrato_sweep;
+ unsigned char vibrato_rate;
+ unsigned char vibrato_depth;
+
+ int scale_frequency;
+ unsigned int scale_factor; /* from 0 to 2048 or 0 to 2 */
+
+ int volume;
+ int spare[4];
+ char data[1]; /* The waveform data starts here */
+ };
+
+
+/*
+ * Patch management interface (/dev/sequencer, /dev/patmgr#)
+ * Don't use these calls if you want to maintain compatibility with
+ * the future versions of the driver.
+ */
+
+#define PS_NO_PATCHES 0 /* No patch support on device */
+#define PS_MGR_NOT_OK 1 /* Plain patch support (no mgr) */
+#define PS_MGR_OK 2 /* Patch manager supported */
+#define PS_MANAGED 3 /* Patch manager running */
+
+#define SNDCTL_PMGR_IFACE _IOWR('P', 1, struct patmgr_info)
+
+/*
+ * The patmgr_info is a fixed size structure which is used for two
+ * different purposes. The intended use is for communication between
+ * the application using /dev/sequencer and the patch manager daemon
+ * associated with a synthesizer device (ioctl(SNDCTL_PMGR_ACCESS)).
+ *
+ * This structure is also used with ioctl(SNDCTL_PGMR_IFACE) which allows
+ * a patch manager daemon to read and write device parameters. This
+ * ioctl available through /dev/sequencer also. Avoid using it since it's
+ * extremely hardware dependent. In addition access trough /dev/sequencer
+ * may confuse the patch manager daemon.
+ */
+
+struct patmgr_info { /* Note! size must be < 4k since kmalloc() is used */
+ unsigned long key; /* Don't worry. Reserved for communication
+ between the patch manager and the driver. */
+#define PM_K_EVENT 1 /* Event from the /dev/sequencer driver */
+#define PM_K_COMMAND 2 /* Request from a application */
+#define PM_K_RESPONSE 3 /* From patmgr to application */
+#define PM_ERROR 4 /* Error returned by the patmgr */
+ int device;
+ int command;
+
+/*
+ * Commands 0x000 to 0xfff reserved for patch manager programs
+ */
+#define PM_GET_DEVTYPE 1 /* Returns type of the patch mgr interface of dev */
+#define PMTYPE_FM2 1 /* 2 OP fm */
+#define PMTYPE_FM4 2 /* Mixed 4 or 2 op FM (OPL-3) */
+#define PMTYPE_WAVE 3 /* Wave table synthesizer (GUS) */
+#define PM_GET_NRPGM 2 /* Returns max # of midi programs in parm1 */
+#define PM_GET_PGMMAP 3 /* Returns map of loaded midi programs in data8 */
+#define PM_GET_PGM_PATCHES 4 /* Return list of patches of a program (parm1) */
+#define PM_GET_PATCH 5 /* Return patch header of patch parm1 */
+#define PM_SET_PATCH 6 /* Set patch header of patch parm1 */
+#define PM_READ_PATCH 7 /* Read patch (wave) data */
+#define PM_WRITE_PATCH 8 /* Write patch (wave) data */
+
+/*
+ * Commands 0x1000 to 0xffff are for communication between the patch manager
+ * and the client
+ */
+#define _PM_LOAD_PATCH 0x100
+
+/*
+ * Commands above 0xffff reserved for device specific use
+ */
+
+ long parm1;
+ long parm2;
+ long parm3;
+
+ union {
+ unsigned char data8[4000];
+ unsigned short data16[2000];
+ unsigned long data32[1000];
+ struct patch_info patch;
+ } data;
+ };
+
+/*
+ * When a patch manager daemon is present, it will be informed by the
+ * driver when something important happens. For example when the
+ * /dev/sequencer is opened or closed. A record with key == PM_K_EVENT is
+ * returned. The command field contains the event type:
+ */
+#define PM_E_OPENED 1 /* /dev/sequencer opened */
+#define PM_E_CLOSED 2 /* /dev/sequencer closed */
+#define PM_E_PATCH_RESET 3 /* SNDCTL_RESETSAMPLES called */
+#define PM_E_PATCH_LOADED 4 /* A patch has been loaded by appl */
+
+/*
+ * /dev/sequencer input events.
+ *
+ * The data written to the /dev/sequencer is a stream of events. Events
+ * are records of 4 or 8 bytes. The first byte defines the size.
+ * Any number of events can be written with a write call. There
+ * is a set of macros for sending these events. Use these macros if you
+ * want to maximize portability of your program.
+ *
+ * Events SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO. Are also input events.
+ * (All input events are currently 4 bytes long. Be prepared to support
+ * 8 byte events also. If you receive any event having first byte >= 0xf0,
+ * it's a 8 byte event.
+ *
+ * The events are documented at the end of this file.
+ *
+ * Normal events (4 bytes)
+ * There is also a 8 byte version of most of the 4 byte events. The
+ * 8 byte one is recommended.
+ */
+#define SEQ_NOTEOFF 0
+#define SEQ_FMNOTEOFF SEQ_NOTEOFF /* Just old name */
+#define SEQ_NOTEON 1
+#define SEQ_FMNOTEON SEQ_NOTEON
+#define SEQ_WAIT 2
+#define SEQ_PGMCHANGE 3
+#define SEQ_FMPGMCHANGE SEQ_PGMCHANGE
+#define SEQ_SYNCTIMER 4
+#define SEQ_MIDIPUTC 5
+#define SEQ_DRUMON 6 /*** OBSOLETE ***/
+#define SEQ_DRUMOFF 7 /*** OBSOLETE ***/
+#define SEQ_ECHO 8 /* For synching programs with output */
+#define SEQ_AFTERTOUCH 9
+#define SEQ_CONTROLLER 10
+#define CTRL_PITCH_BENDER 255
+#define CTRL_PITCH_BENDER_RANGE 254
+#define CTRL_EXPRESSION 253
+#define CTRL_MAIN_VOLUME 252
+#define SEQ_BALANCE 11
+#define SEQ_VOLMODE 12
+
+/*
+ * Volume mode decides how volumes are used
+ */
+
+#define VOL_METHOD_ADAGIO 1
+#define VOL_METHOD_LINEAR 2
+
+/*
+ * Note! SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO are used also as
+ * input events.
+ */
+
+/*
+ * Event codes 0xf0 to 0xfc are reserved for future extensions.
+ */
+
+#define SEQ_FULLSIZE 0xfd /* Long events */
+/*
+ * SEQ_FULLSIZE events are used for loading patches/samples to the
+ * synthesizer devices. These events are passed directly to the driver
+ * of the associated synthesizer device. There is no limit to the size
+ * of the extended events. These events are not queued but executed
+ * immediately when the write() is called (execution can take several
+ * seconds of time).
+ *
+ * When a SEQ_FULLSIZE message is written to the device, it must
+ * be written using exactly one write() call. Other events cannot
+ * be mixed to the same write.
+ *
+ * For FM synths (YM3812/OPL3) use struct sbi_instrument and write it to the
+ * /dev/sequencer. Don't write other data together with the instrument structure
+ * Set the key field of the structure to FM_PATCH. The device field is used to
+ * route the patch to the corresponding device.
+ *
+ * For Gravis UltraSound use struct patch_info. Initialize the key field
+ * to GUS_PATCH.
+ */
+#define SEQ_PRIVATE 0xfe /* Low level HW dependent events (8 bytes) */
+#define SEQ_EXTENDED 0xff /* Extended events (8 bytes) */
+
+/*
+ * Extended events for synthesizers (8 bytes)
+ *
+ * Format:
+ *
+ * b0 = SEQ_EXTENDED
+ * b1 = command
+ * b2 = device
+ * b3-b7 = parameters
+ *
+ * Command b3 b4 b5 b6 b7
+ * ----------------------------------------------------------------------------
+ * SEQ_NOTEON voice note volume 0 0
+ * SEQ_NOTEOFF voice note volume 0 0
+ * SEQ_PGMCHANGE voice pgm 0 0 0
+ * SEQ_DRUMON (voice) drum# volume 0 0
+ * SEQ_DRUMOFF (voice) drum# volume 0 0
+ */
+
+/*
+ * Record for FM patches
+ */
+
+typedef unsigned char sbi_instr_data[32];
+
+struct sbi_instrument {
+ unsigned short key; /* Initialize to FM_PATCH or OPL3_PATCH */
+#define FM_PATCH 0x01fd
+#define OPL3_PATCH 0x03fd
+ short device; /* Synth# (0-4) */
+ int channel; /* Program# to be initialized */
+ sbi_instr_data operators; /* Register settings for operator cells (.SBI format) */
+ };
+
+struct synth_info { /* Read only */
+ char name[30];
+ int device; /* 0-N. INITIALIZE BEFORE CALLING */
+ int synth_type;
+#define SYNTH_TYPE_FM 0
+#define SYNTH_TYPE_SAMPLE 1
+
+ int synth_subtype;
+#define FM_TYPE_ADLIB 0x00
+#define FM_TYPE_OPL3 0x01
+
+#define SAMPLE_TYPE_GUS 0x10
+
+ int perc_mode; /* No longer supported */
+ int nr_voices;
+ int nr_drums; /* Obsolete field */
+ int instr_bank_size;
+ unsigned long capabilities;
+#define SYNTH_CAP_PERCMODE 0x00000001 /* No longer used */
+#define SYNTH_CAP_OPL3 0x00000002 /* Set if OPL3 supported */
+ int dummies[19]; /* Reserve space */
+ };
+
+struct midi_info {
+ char name[30];
+ int device; /* 0-N. INITIALIZE BEFORE CALLING */
+ unsigned long capabilities; /* To be defined later */
+ int dev_type;
+ int dummies[18]; /* Reserve space */
+ };
+
+/********************************************
+ * IOCTL commands for /dev/dsp and /dev/audio
+ */
+
+#define SNDCTL_DSP_RESET _IO ('P', 0)
+#define SNDCTL_DSP_SYNC _IO ('P', 1)
+#define SNDCTL_DSP_SPEED _IOWR('P', 2, int)
+#define SNDCTL_DSP_STEREO _IOWR('P', 3, int)
+#define SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
+#define SNDCTL_DSP_SAMPLESIZE _IOWR('P', 5, int) /* 8, 12 or 16 */
+#define SOUND_PCM_WRITE_CHANNELS _IOWR('P', 6, int)
+#define SOUND_PCM_WRITE_FILTER _IOWR('P', 7, int)
+#define SNDCTL_DSP_POST _IO ('P', 8)
+#define SNDCTL_DSP_SUBDIVIDE _IOWR('P', 9, int)
+
+#define SOUND_PCM_READ_RATE _IOR ('P', 2, int)
+#define SOUND_PCM_READ_CHANNELS _IOR ('P', 6, int)
+#define SOUND_PCM_READ_BITS _IOR ('P', 5, int)
+#define SOUND_PCM_READ_FILTER _IOR ('P', 7, int)
+
+/* Some alias names */
+#define SOUND_PCM_WRITE_BITS SNDCTL_DSP_SAMPLESIZE
+#define SOUND_PCM_WRITE_RATE SNDCTL_DSP_SPEED
+#define SOUND_PCM_POST SNDCTL_DSP_POST
+#define SOUND_PCM_RESET SNDCTL_DSP_RESET
+#define SOUND_PCM_SYNC SNDCTL_DSP_SYNC
+#define SOUND_PCM_SUBDIVIDE SNDCTL_DSP_SUBDIVIDE
+
+/*********************************************
+ * IOCTL commands for /dev/mixer
+ */
+
+/*
+ * Mixer devices
+ *
+ * There can be up to 20 different analog mixer channels. The
+ * SOUND_MIXER_NRDEVICES gives the currently supported maximum.
+ * The SOUND_MIXER_READ_DEVMASK returns a bitmask which tells
+ * the devices supported by the particular mixer.
+ */
+
+#define SOUND_MIXER_NRDEVICES 12
+#define SOUND_MIXER_VOLUME 0
+#define SOUND_MIXER_BASS 1
+#define SOUND_MIXER_TREBLE 2
+#define SOUND_MIXER_SYNTH 3
+#define SOUND_MIXER_PCM 4
+#define SOUND_MIXER_SPEAKER 5
+#define SOUND_MIXER_LINE 6
+#define SOUND_MIXER_MIC 7
+#define SOUND_MIXER_CD 8
+#define SOUND_MIXER_IMIX 9 /* Recording monitor */
+#define SOUND_MIXER_ALTPCM 10
+#define SOUND_MIXER_RECLEV 11 /* Recording level */
+
+/* Some on/off settings (SOUND_SPECIAL_MIN - SOUND_SPECIAL_MAX) */
+/* Not counted to SOUND_MIXER_NRDEVICES, but use the same number space */
+#define SOUND_ONOFF_MIN 28
+#define SOUND_ONOFF_MAX 30
+#define SOUND_MIXER_MUTE 28 /* 0 or 1 */
+#define SOUND_MIXER_ENHANCE 29 /* Enhanced stereo (0, 40, 60 or 80) */
+#define SOUND_MIXER_LOUD 30 /* 0 or 1 */
+
+/* Note! Number 31 cannot be used since the sign bit is reserved */
+
+#define SOUND_DEVICE_LABELS {"Vol ", "Bass ", "Trebl", "Synth", "Pcm ", "Spkr ", "Line ", \
+ "Mic ", "CD ", "Mix ", "Pcm2 ", "rec"}
+
+#define SOUND_DEVICE_NAMES {"vol", "bass", "treble", "synth", "pcm", "speaker", "line", \
+ "mic", "cd", "mix", "pcm2", "rec"}
+
+/* Device bitmask identifiers */
+
+#define SOUND_MIXER_RECSRC 0xff /* Arg contains a bit for each recording source */
+#define SOUND_MIXER_DEVMASK 0xfe /* Arg contains a bit for each supported device */
+#define SOUND_MIXER_RECMASK 0xfd /* Arg contains a bit for each supported recording source */
+#define SOUND_MIXER_CAPS 0xfc
+ #define SOUND_CAP_EXCL_INPUT 0x00000001 /* Only one recording source at a time */
+#define SOUND_MIXER_STEREODEVS 0xfb /* Mixer channels supporting stereo */
+
+/* Device mask bits */
+
+#define SOUND_MASK_VOLUME (1 << SOUND_MIXER_VOLUME)
+#define SOUND_MASK_BASS (1 << SOUND_MIXER_BASS)
+#define SOUND_MASK_TREBLE (1 << SOUND_MIXER_TREBLE)
+#define SOUND_MASK_SYNTH (1 << SOUND_MIXER_SYNTH)
+#define SOUND_MASK_PCM (1 << SOUND_MIXER_PCM)
+#define SOUND_MASK_SPEAKER (1 << SOUND_MIXER_SPEAKER)
+#define SOUND_MASK_LINE (1 << SOUND_MIXER_LINE)
+#define SOUND_MASK_MIC (1 << SOUND_MIXER_MIC)
+#define SOUND_MASK_CD (1 << SOUND_MIXER_CD)
+#define SOUND_MASK_IMIX (1 << SOUND_MIXER_IMIX)
+#define SOUND_MASK_ALTPCM (1 << SOUND_MIXER_ALTPCM)
+#define SOUND_MASK_RECLEV (1 << SOUND_MIXER_RECLEV)
+
+#define SOUND_MASK_MUTE (1 << SOUND_MIXER_MUTE)
+#define SOUND_MASK_ENHANCE (1 << SOUND_MIXER_ENHANCE)
+#define SOUND_MASK_LOUD (1 << SOUND_MIXER_LOUD)
+
+#define MIXER_READ(dev) _IOR('M', dev, int)
+#define SOUND_MIXER_READ_VOLUME MIXER_READ(SOUND_MIXER_VOLUME)
+#define SOUND_MIXER_READ_BASS MIXER_READ(SOUND_MIXER_BASS)
+#define SOUND_MIXER_READ_TREBLE MIXER_READ(SOUND_MIXER_TREBLE)
+#define SOUND_MIXER_READ_SYNTH MIXER_READ(SOUND_MIXER_SYNTH)
+#define SOUND_MIXER_READ_PCM MIXER_READ(SOUND_MIXER_PCM)
+#define SOUND_MIXER_READ_SPEAKER MIXER_READ(SOUND_MIXER_SPEAKER)
+#define SOUND_MIXER_READ_LINE MIXER_READ(SOUND_MIXER_LINE)
+#define SOUND_MIXER_READ_MIC MIXER_READ(SOUND_MIXER_MIC)
+#define SOUND_MIXER_READ_CD MIXER_READ(SOUND_MIXER_CD)
+#define SOUND_MIXER_READ_IMIX MIXER_READ(SOUND_MIXER_IMIX)
+#define SOUND_MIXER_READ_ALTPCM MIXER_READ(SOUND_MIXER_ALTPCM)
+#define SOUND_MIXER_READ_RECLEV MIXER_READ(SOUND_MIXER_RECLEV)
+#define SOUND_MIXER_READ_MUTE MIXER_READ(SOUND_MIXER_MUTE)
+#define SOUND_MIXER_READ_ENHANCE MIXER_READ(SOUND_MIXER_ENHANCE)
+#define SOUND_MIXER_READ_LOUD MIXER_READ(SOUND_MIXER_LOUD)
+
+#define SOUND_MIXER_READ_RECSRC MIXER_READ(SOUND_MIXER_RECSRC)
+#define SOUND_MIXER_READ_DEVMASK MIXER_READ(SOUND_MIXER_DEVMASK)
+#define SOUND_MIXER_READ_RECMASK MIXER_READ(SOUND_MIXER_RECMASK)
+#define SOUND_MIXER_READ_STEREODEVS MIXER_READ(SOUND_MIXER_STEREODEVS)
+#define SOUND_MIXER_READ_CAPS MIXER_READ(SOUND_MIXER_CAPS)
+
+#define MIXER_WRITE(dev) _IOWR('M', dev, int)
+#define SOUND_MIXER_WRITE_VOLUME MIXER_WRITE(SOUND_MIXER_VOLUME)
+#define SOUND_MIXER_WRITE_BASS MIXER_WRITE(SOUND_MIXER_BASS)
+#define SOUND_MIXER_WRITE_TREBLE MIXER_WRITE(SOUND_MIXER_TREBLE)
+#define SOUND_MIXER_WRITE_SYNTH MIXER_WRITE(SOUND_MIXER_SYNTH)
+#define SOUND_MIXER_WRITE_PCM MIXER_WRITE(SOUND_MIXER_PCM)
+#define SOUND_MIXER_WRITE_SPEAKER MIXER_WRITE(SOUND_MIXER_SPEAKER)
+#define SOUND_MIXER_WRITE_LINE MIXER_WRITE(SOUND_MIXER_LINE)
+#define SOUND_MIXER_WRITE_MIC MIXER_WRITE(SOUND_MIXER_MIC)
+#define SOUND_MIXER_WRITE_CD MIXER_WRITE(SOUND_MIXER_CD)
+#define SOUND_MIXER_WRITE_IMIX MIXER_WRITE(SOUND_MIXER_IMIX)
+#define SOUND_MIXER_WRITE_ALTPCM MIXER_WRITE(SOUND_MIXER_ALTPCM)
+#define SOUND_MIXER_WRITE_RECLEV MIXER_WRITE(SOUND_MIXER_RECLEV)
+#define SOUND_MIXER_WRITE_MUTE MIXER_WRITE(SOUND_MIXER_MUTE)
+#define SOUND_MIXER_WRITE_ENHANCE MIXER_WRITE(SOUND_MIXER_ENHANCE)
+#define SOUND_MIXER_WRITE_LOUD MIXER_WRITE(SOUND_MIXER_LOUD)
+
+#define SOUND_MIXER_WRITE_RECSRC MIXER_WRITE(SOUND_MIXER_RECSRC)
+
+/*
+ * The following mixer ioctl calls are compatible with the BSD driver by
+ * Steve Haehnichen <shaehnic@ucsd.edu>
+ *
+ * Since this interface is entirely SB specific, it will be dropped in the
+ * near future.
+ */
+
+typedef unsigned char S_BYTE;
+typedef unsigned char S_FLAG;
+struct stereo_vol
+{
+ S_BYTE l; /* Left volume */
+ S_BYTE r; /* Right volume */
+};
+
+#define MIXER_IOCTL_SET_LEVELS _IOW ('s', 20, struct sb_mixer_levels)
+#define MIXER_IOCTL_SET_PARAMS _IOW ('s', 21, struct sb_mixer_params)
+#define MIXER_IOCTL_READ_LEVELS _IOR ('s', 22, struct sb_mixer_levels)
+#define MIXER_IOCTL_READ_PARAMS _IOR ('s', 23, struct sb_mixer_params)
+#define MIXER_IOCTL_RESET _IO ('s', 24)
+
+/*
+ * Mixer volume levels for MIXER_IOCTL_SET_VOL & MIXER_IOCTL_READ_VOL
+ */
+struct sb_mixer_levels
+{
+ struct stereo_vol master; /* Master volume */
+ struct stereo_vol voc; /* DSP Voice volume */
+ struct stereo_vol fm; /* FM volume */
+ struct stereo_vol line; /* Line-in volume */
+ struct stereo_vol cd; /* CD audio */
+ S_BYTE mic; /* Microphone level */
+};
+
+/*
+ * Mixer parameters for MIXER_IOCTL_SET_PARAMS & MIXER_IOCTL_READ_PARAMS
+ */
+struct sb_mixer_params
+{
+ S_BYTE record_source; /* Recording source (See SRC_xxx below) */
+ S_FLAG hifreq_filter; /* Filter frequency (hi/low) */
+ S_FLAG filter_input; /* ANFI input filter */
+ S_FLAG filter_output; /* DNFI output filter */
+ S_FLAG dsp_stereo; /* 1 if DSP is in Stereo mode */
+};
+
+#define SRC_MIC 1 /* Select Microphone recording source */
+#define SRC_CD 3 /* Select CD recording source */
+#define SRC_LINE 7 /* Use Line-in for recording source */
+
+#if !defined(KERNEL) && !defined(INKERNEL)
+/*
+ * Some convenience macros to simplify programming of the
+ * /dev/sequencer interface
+ *
+ * These macros define the API which should be used when possible.
+ */
+
+void seqbuf_dump(void); /* This function must be provided by programs */
+
+/* Sample seqbuf_dump() implementation:
+ *
+ * SEQ_DEFINEBUF (2048); -- Defines a buffer for 2048 bytes
+ *
+ * int seqfd; -- The file descriptor for /dev/sequencer.
+ *
+ * void
+ * seqbuf_dump ()
+ * {
+ * if (_seqbufptr)
+ * if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ * {
+ * perror ("write /dev/sequencer");
+ * exit (-1);
+ * }
+ * _seqbufptr = 0;
+ * }
+ */
+
+#define SEQ_DEFINEBUF(len) unsigned char _seqbuf[len]; int _seqbuflen = len; int _seqbufptr = 0
+#define SEQ_DECLAREBUF() extern unsigned char _seqbuf[]; extern int _seqbuflen;extern int _seqbufptr
+#define SEQ_PM_DEFINES struct patmgr_info _pm_info
+#define _SEQ_NEEDBUF(len) if ((_seqbufptr+(len)) > _seqbuflen) seqbuf_dump()
+#define _SEQ_ADVBUF(len) _seqbufptr += len
+#define SEQ_DUMPBUF seqbuf_dump
+#define PM_LOAD_PATCH(dev, bank, pgm) (SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \
+ _pm_info.device=dev, _pm_info.data.data8[0]=pgm, \
+ _pm_info.parm1 = bank, _pm_info.parm2 = 1, \
+ ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info))
+#define PM_LOAD_PATCHES(dev, bank, pgm) (SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \
+ _pm_info.device=dev, memcpy(_pm_info.data.data8, pgm, 128), \
+ _pm_info.parm1 = bank, _pm_info.parm2 = 128, \
+ ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info))
+
+#define SEQ_VOLUME_MODE(dev, mode) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_VOLMODE;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (mode);\
+ _seqbuf[_seqbufptr+4] = 0;\
+ _seqbuf[_seqbufptr+5] = 0;\
+ _seqbuf[_seqbufptr+6] = 0;\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_START_NOTE(dev, voice, note, vol) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_NOTEON;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (voice);\
+ _seqbuf[_seqbufptr+4] = (note);\
+ _seqbuf[_seqbufptr+5] = (vol);\
+ _seqbuf[_seqbufptr+6] = 0;\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_STOP_NOTE(dev, voice, note, vol) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_NOTEOFF;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (voice);\
+ _seqbuf[_seqbufptr+4] = (note);\
+ _seqbuf[_seqbufptr+5] = (vol);\
+ _seqbuf[_seqbufptr+6] = 0;\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_CHN_PRESSURE(dev, voice, pressure) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_AFTERTOUCH;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (voice);\
+ _seqbuf[_seqbufptr+4] = (pressure);\
+ _seqbuf[_seqbufptr+5] = 0;\
+ _seqbuf[_seqbufptr+6] = 0;\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_PANNING(dev, voice, pos) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_BALANCE;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (voice);\
+ (char)_seqbuf[_seqbufptr+4] = (pos);\
+ _seqbuf[_seqbufptr+5] = 0;\
+ _seqbuf[_seqbufptr+6] = 0;\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_CONTROL(dev, voice, controller, value) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_CONTROLLER;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (voice);\
+ _seqbuf[_seqbufptr+4] = (controller);\
+ *(short *)&_seqbuf[_seqbufptr+5] = (value);\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_PITCHBEND(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_PITCH_BENDER, value)
+#define SEQ_BENDER_RANGE(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_PITCH_BENDER_RANGE, value)
+#define SEQ_EXPRESSION(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_EXPRESSION, value)
+#define SEQ_MAIN_VOLUME(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_MAIN_VOLUME, value)
+
+#define SEQ_START_TIMER() {_SEQ_NEEDBUF(4);\
+ _seqbuf[_seqbufptr] = SEQ_SYNCTIMER;\
+ _seqbuf[_seqbufptr+1] = 0;\
+ _seqbuf[_seqbufptr+2] = 0;\
+ _seqbuf[_seqbufptr+3] = 0;\
+ _SEQ_ADVBUF(4);}
+#define SEQ_SET_PATCH(dev, voice, patch) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_PGMCHANGE;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (voice);\
+ _seqbuf[_seqbufptr+4] = (patch);\
+ _seqbuf[_seqbufptr+5] = 0;\
+ _seqbuf[_seqbufptr+6] = 0;\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_WAIT_TIME(ticks) {_SEQ_NEEDBUF(4);\
+ *(unsigned long *)&_seqbuf[_seqbufptr] = SEQ_WAIT | ((ticks) << 8);\
+ _SEQ_ADVBUF(4);}
+
+#define SEQ_ECHO_BACK(key) {_SEQ_NEEDBUF(4);\
+ *(unsigned long *)&_seqbuf[_seqbufptr] = SEQ_ECHO | ((key) << 8);\
+ _SEQ_ADVBUF(4);}
+
+#define SEQ_MIDIOUT(device, byte) {_SEQ_NEEDBUF(4);\
+ _seqbuf[_seqbufptr] = SEQ_MIDIPUTC;\
+ _seqbuf[_seqbufptr+1] = (byte);\
+ _seqbuf[_seqbufptr+2] = (device);\
+ _seqbuf[_seqbufptr+3] = 0;\
+ _SEQ_ADVBUF(4);}
+#define SEQ_WRPATCH(patchx, len) {if (_seqbufptr) seqbuf_dump();\
+ if (write(seqfd, (char*)(patchx), len)==-1) \
+ perror("Write patch: /dev/sequencer");}
+
+#endif
+long soundcard_init(long mem_start);
+#endif /* _SOUNDCARD_H_ */
diff --git a/sys/sys/stat.h b/sys/sys/stat.h
new file mode 100644
index 0000000..07020c3
--- /dev/null
+++ b/sys/sys/stat.h
@@ -0,0 +1,193 @@
+/*-
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)stat.h 8.6 (Berkeley) 3/8/94
+ */
+
+#ifndef _SYS_STAT_H_
+#define _SYS_STAT_H_
+
+#include <sys/time.h>
+
+#ifndef _POSIX_SOURCE
+struct ostat {
+ unsigned short st_dev; /* inode's device */
+ ino_t st_ino; /* inode's number */
+ mode_t st_mode; /* inode protection mode */
+ nlink_t st_nlink; /* number of hard links */
+ unsigned short st_uid; /* user ID of the file's owner */
+ unsigned short st_gid; /* group ID of the file's group */
+ unsigned short st_rdev; /* device type */
+ long st_size; /* file size, in bytes */
+ struct timespec st_atimespec; /* time of last access */
+ struct timespec st_mtimespec; /* time of last data modification */
+ struct timespec st_ctimespec; /* time of last file status change */
+ long st_blksize; /* optimal blocksize for I/O */
+ long st_blocks; /* blocks allocated for file */
+ unsigned long st_flags; /* user defined flags for file */
+ unsigned long st_gen; /* file generation number */
+};
+#endif /* !_POSIX_SOURCE */
+
+struct stat {
+ dev_t st_dev; /* inode's device */
+ ino_t st_ino; /* inode's number */
+ mode_t st_mode; /* inode protection mode */
+ nlink_t st_nlink; /* number of hard links */
+ uid_t st_uid; /* user ID of the file's owner */
+ gid_t st_gid; /* group ID of the file's group */
+ dev_t st_rdev; /* device type */
+ struct timespec st_atimespec; /* time of last access */
+ struct timespec st_mtimespec; /* time of last data modification */
+ struct timespec st_ctimespec; /* time of last file status change */
+ off_t st_size; /* file size, in bytes */
+ quad_t st_blocks; /* blocks allocated for file */
+ unsigned long st_blksize; /* optimal blocksize for I/O */
+ unsigned long st_flags; /* user defined flags for file */
+ unsigned long st_gen; /* file generation number */
+ long st_lspare;
+ quad_t st_qspare[2];
+};
+#define st_atime st_atimespec.ts_sec
+#define st_mtime st_mtimespec.ts_sec
+#define st_ctime st_ctimespec.ts_sec
+
+#define S_ISUID 0004000 /* set user id on execution */
+#define S_ISGID 0002000 /* set group id on execution */
+#ifndef _POSIX_SOURCE
+#define S_ISTXT 0001000 /* sticky bit */
+#endif
+
+#define S_IRWXU 0000700 /* RWX mask for owner */
+#define S_IRUSR 0000400 /* R for owner */
+#define S_IWUSR 0000200 /* W for owner */
+#define S_IXUSR 0000100 /* X for owner */
+
+#ifndef _POSIX_SOURCE
+#define S_IREAD S_IRUSR
+#define S_IWRITE S_IWUSR
+#define S_IEXEC S_IXUSR
+#endif
+
+#define S_IRWXG 0000070 /* RWX mask for group */
+#define S_IRGRP 0000040 /* R for group */
+#define S_IWGRP 0000020 /* W for group */
+#define S_IXGRP 0000010 /* X for group */
+
+#define S_IRWXO 0000007 /* RWX mask for other */
+#define S_IROTH 0000004 /* R for other */
+#define S_IWOTH 0000002 /* W for other */
+#define S_IXOTH 0000001 /* X for other */
+
+#ifndef _POSIX_SOURCE
+#define S_IFMT 0170000 /* type of file mask */
+#define S_IFIFO 0010000 /* named pipe (fifo) */
+#define S_IFCHR 0020000 /* character special */
+#define S_IFDIR 0040000 /* directory */
+#define S_IFBLK 0060000 /* block special */
+#define S_IFREG 0100000 /* regular */
+#define S_IFLNK 0120000 /* symbolic link */
+#define S_IFSOCK 0140000 /* socket */
+#define S_ISVTX 0001000 /* save swapped text even after use */
+#endif
+
+#define S_ISDIR(m) ((m & 0170000) == 0040000) /* directory */
+#define S_ISCHR(m) ((m & 0170000) == 0020000) /* char special */
+#define S_ISBLK(m) ((m & 0170000) == 0060000) /* block special */
+#define S_ISREG(m) ((m & 0170000) == 0100000) /* regular file */
+#define S_ISFIFO(m) ((m & 0170000) == 0100000 || \
+ (m & 0170000) == 0140000) /* fifo or socket */
+#ifndef _POSIX_SOURCE
+#define S_ISLNK(m) ((m & 0170000) == 0120000) /* symbolic link */
+#define S_ISSOCK(m) ((m & 0170000) == 0100000 || \
+ (m & 0170000) == 0140000) /* fifo or socket */
+#endif
+
+#ifndef _POSIX_SOURCE
+#define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO) /* 0777 */
+ /* 7777 */
+#define ALLPERMS (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
+ /* 0666 */
+#define DEFFILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
+
+#define S_BLKSIZE 512 /* block size used in the stat struct */
+
+/*
+ * Definitions of flags stored in file flags word.
+ *
+ * Super-user and owner changeable flags.
+ */
+#define UF_SETTABLE 0x0000ffff /* mask of owner changeable flags */
+#define UF_NODUMP 0x00000001 /* do not dump file */
+#define UF_IMMUTABLE 0x00000002 /* file may not be changed */
+#define UF_APPEND 0x00000004 /* writes to file may only append */
+/*
+ * Super-user changeable flags.
+ */
+#define SF_SETTABLE 0xffff0000 /* mask of superuser changeable flags */
+#define SF_ARCHIVED 0x00010000 /* file is archived */
+#define SF_IMMUTABLE 0x00020000 /* file may not be changed */
+#define SF_APPEND 0x00040000 /* writes to file may only append */
+
+#ifdef KERNEL
+/*
+ * Shorthand abbreviations of above.
+ */
+#define APPEND (UF_APPEND | SF_APPEND)
+#define IMMUTABLE (UF_IMMUTABLE | SF_IMMUTABLE)
+#endif
+#endif
+
+#ifndef KERNEL
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int chmod __P((const char *, mode_t));
+int fstat __P((int, struct stat *));
+int mkdir __P((const char *, mode_t));
+int mkfifo __P((const char *, mode_t));
+int stat __P((const char *, struct stat *));
+mode_t umask __P((mode_t));
+#ifndef _POSIX_SOURCE
+int chflags __P((const char *, u_long));
+int fchflags __P((int, u_long));
+int fchmod __P((int, mode_t));
+int lstat __P((const char *, struct stat *));
+#endif
+__END_DECLS
+#endif
+#endif /* !_SYS_STAT_H_ */
diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h
new file mode 100644
index 0000000..98e85dd
--- /dev/null
+++ b/sys/sys/syscall.h
@@ -0,0 +1,190 @@
+/*
+ * System call numbers.
+ *
+ * DO NOT EDIT-- this file is automatically generated.
+ * created from @(#)syscalls.master 8.2 (Berkeley) 1/13/94
+ */
+
+#define SYS_syscall 0
+#define SYS_exit 1
+#define SYS_fork 2
+#define SYS_read 3
+#define SYS_write 4
+#define SYS_open 5
+#define SYS_close 6
+#define SYS_wait4 7
+ /* 8 is old creat */
+#define SYS_link 9
+#define SYS_unlink 10
+ /* 11 is obsolete execv */
+#define SYS_chdir 12
+#define SYS_fchdir 13
+#define SYS_mknod 14
+#define SYS_chmod 15
+#define SYS_chown 16
+#define SYS_break 17
+#define SYS_getfsstat 18
+ /* 19 is old lseek */
+#define SYS_getpid 20
+#define SYS_mount 21
+#define SYS_unmount 22
+#define SYS_setuid 23
+#define SYS_getuid 24
+#define SYS_geteuid 25
+#define SYS_ptrace 26
+#define SYS_recvmsg 27
+#define SYS_sendmsg 28
+#define SYS_recvfrom 29
+#define SYS_accept 30
+#define SYS_getpeername 31
+#define SYS_getsockname 32
+#define SYS_access 33
+#define SYS_chflags 34
+#define SYS_fchflags 35
+#define SYS_sync 36
+#define SYS_kill 37
+ /* 38 is old stat */
+#define SYS_getppid 39
+ /* 40 is old lstat */
+#define SYS_dup 41
+#define SYS_pipe 42
+#define SYS_getegid 43
+#define SYS_profil 44
+#define SYS_ktrace 45
+#define SYS_sigaction 46
+#define SYS_getgid 47
+#define SYS_sigprocmask 48
+#define SYS_getlogin 49
+#define SYS_setlogin 50
+#define SYS_acct 51
+#define SYS_sigpending 52
+#define SYS_sigaltstack 53
+#define SYS_ioctl 54
+#define SYS_reboot 55
+#define SYS_revoke 56
+#define SYS_symlink 57
+#define SYS_readlink 58
+#define SYS_execve 59
+#define SYS_umask 60
+#define SYS_chroot 61
+ /* 62 is old fstat */
+ /* 63 is old getkerninfo */
+ /* 64 is old getpagesize */
+#define SYS_msync 65
+#define SYS_vfork 66
+ /* 67 is obsolete vread */
+ /* 68 is obsolete vwrite */
+#define SYS_sbrk 69
+#define SYS_sstk 70
+ /* 71 is old mmap */
+#define SYS_vadvise 72
+#define SYS_munmap 73
+#define SYS_mprotect 74
+#define SYS_madvise 75
+ /* 76 is obsolete vhangup */
+ /* 77 is obsolete vlimit */
+#define SYS_mincore 78
+#define SYS_getgroups 79
+#define SYS_setgroups 80
+#define SYS_getpgrp 81
+#define SYS_setpgid 82
+#define SYS_setitimer 83
+ /* 84 is old wait */
+#define SYS_swapon 85
+#define SYS_getitimer 86
+ /* 87 is old gethostname */
+ /* 88 is old sethostname */
+#define SYS_getdtablesize 89
+#define SYS_dup2 90
+#define SYS_fcntl 92
+#define SYS_select 93
+#define SYS_fsync 95
+#define SYS_setpriority 96
+#define SYS_socket 97
+#define SYS_connect 98
+ /* 99 is old accept */
+#define SYS_getpriority 100
+ /* 101 is old send */
+ /* 102 is old recv */
+#define SYS_sigreturn 103
+#define SYS_bind 104
+#define SYS_setsockopt 105
+#define SYS_listen 106
+ /* 107 is obsolete vtimes */
+ /* 108 is old sigvec */
+ /* 109 is old sigblock */
+ /* 110 is old sigsetmask */
+#define SYS_sigsuspend 111
+ /* 112 is old sigstack */
+ /* 113 is old recvmsg */
+ /* 114 is old sendmsg */
+#define SYS_vtrace 115
+ /* 115 is obsolete vtrace */
+#define SYS_gettimeofday 116
+#define SYS_getrusage 117
+#define SYS_getsockopt 118
+#define SYS_resuba 119
+#define SYS_readv 120
+#define SYS_writev 121
+#define SYS_settimeofday 122
+#define SYS_fchown 123
+#define SYS_fchmod 124
+ /* 125 is old recvfrom */
+ /* 126 is old setreuid */
+ /* 127 is old setregid */
+#define SYS_rename 128
+ /* 129 is old truncate */
+ /* 130 is old ftruncate */
+#define SYS_flock 131
+#define SYS_mkfifo 132
+#define SYS_sendto 133
+#define SYS_shutdown 134
+#define SYS_socketpair 135
+#define SYS_mkdir 136
+#define SYS_rmdir 137
+#define SYS_utimes 138
+ /* 139 is obsolete 4.2 sigreturn */
+#define SYS_adjtime 140
+ /* 141 is old getpeername */
+ /* 142 is old gethostid */
+ /* 143 is old sethostid */
+ /* 144 is old getrlimit */
+ /* 145 is old setrlimit */
+ /* 146 is old killpg */
+#define SYS_setsid 147
+#define SYS_quotactl 148
+ /* 149 is old quota */
+ /* 150 is old getsockname */
+#define SYS_nfssvc 155
+ /* 156 is old getdirentries */
+#define SYS_statfs 157
+#define SYS_fstatfs 158
+#define SYS_getfh 161
+#define SYS_getdomainname 162
+#define SYS_setdomainname 163
+#define SYS_uname 164
+#define SYS_sysarch 165
+#define SYS_shmsys 171
+#define SYS_setgid 181
+#define SYS_setegid 182
+#define SYS_seteuid 183
+#define SYS_lfs_bmapv 184
+#define SYS_lfs_markv 185
+#define SYS_lfs_segclean 186
+#define SYS_lfs_segwait 187
+#define SYS_stat 188
+#define SYS_fstat 189
+#define SYS_lstat 190
+#define SYS_pathconf 191
+#define SYS_fpathconf 192
+#define SYS_getrlimit 194
+#define SYS_setrlimit 195
+#define SYS_getdirentries 196
+#define SYS_mmap 197
+#define SYS___syscall 198
+#define SYS_lseek 199
+#define SYS_truncate 200
+#define SYS_ftruncate 201
+#define SYS___sysctl 202
+#define SYS_mlock 203
+#define SYS_munlock 204
diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h
new file mode 100644
index 0000000..4ad83a7
--- /dev/null
+++ b/sys/sys/sysctl.h
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Karels at Berkeley Software Design, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)sysctl.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _SYS_SYSCTL_H_
+#define _SYS_SYSCTL_H_
+
+/*
+ * These are for the eproc structure defined below.
+ */
+#ifndef KERNEL
+#include <sys/time.h>
+#include <sys/ucred.h>
+#include <sys/proc.h>
+#include <vm/vm.h>
+#endif
+
+/*
+ * Definitions for sysctl call. The sysctl call uses a hierarchical name
+ * for objects that can be examined or modified. The name is expressed as
+ * a sequence of integers. Like a file path name, the meaning of each
+ * component depends on its place in the hierarchy. The top-level and kern
+ * identifiers are defined here, and other identifiers are defined in the
+ * respective subsystem header files.
+ */
+
+#define CTL_MAXNAME 12 /* largest number of components supported */
+
+/*
+ * Each subsystem defined by sysctl defines a list of variables
+ * for that subsystem. Each name is either a node with further
+ * levels defined below it, or it is a leaf of some particular
+ * type given below. Each sysctl level defines a set of name/type
+ * pairs to be used by sysctl(1) in manipulating the subsystem.
+ */
+struct ctlname {
+ char *ctl_name; /* subsystem name */
+ int ctl_type; /* type of name */
+};
+#define CTLTYPE_NODE 1 /* name is a node */
+#define CTLTYPE_INT 2 /* name describes an integer */
+#define CTLTYPE_STRING 3 /* name describes a string */
+#define CTLTYPE_QUAD 4 /* name describes a 64-bit number */
+#define CTLTYPE_STRUCT 5 /* name describes a structure */
+
+/*
+ * Top-level identifiers
+ */
+#define CTL_UNSPEC 0 /* unused */
+#define CTL_KERN 1 /* "high kernel": proc, limits */
+#define CTL_VM 2 /* virtual memory */
+#define CTL_FS 3 /* file system, mount type is next */
+#define CTL_NET 4 /* network, see socket.h */
+#define CTL_DEBUG 5 /* debugging parameters */
+#define CTL_HW 6 /* generic cpu/io */
+#define CTL_MACHDEP 7 /* machine dependent */
+#define CTL_USER 8 /* user-level */
+#define CTL_MAXID 9 /* number of valid top-level ids */
+
+#define CTL_NAMES { \
+ { 0, 0 }, \
+ { "kern", CTLTYPE_NODE }, \
+ { "vm", CTLTYPE_NODE }, \
+ { "fs", CTLTYPE_NODE }, \
+ { "net", CTLTYPE_NODE }, \
+ { "debug", CTLTYPE_NODE }, \
+ { "hw", CTLTYPE_NODE }, \
+ { "machdep", CTLTYPE_NODE }, \
+ { "user", CTLTYPE_NODE }, \
+}
+
+/*
+ * CTL_KERN identifiers
+ */
+#define KERN_OSTYPE 1 /* string: system version */
+#define KERN_OSRELEASE 2 /* string: system release */
+#define KERN_OSREV 3 /* int: system revision */
+#define KERN_VERSION 4 /* string: compile time info */
+#define KERN_MAXVNODES 5 /* int: max vnodes */
+#define KERN_MAXPROC 6 /* int: max processes */
+#define KERN_MAXFILES 7 /* int: max open files */
+#define KERN_ARGMAX 8 /* int: max arguments to exec */
+#define KERN_SECURELVL 9 /* int: system security level */
+#define KERN_HOSTNAME 10 /* string: hostname */
+#define KERN_HOSTID 11 /* int: host identifier */
+#define KERN_CLOCKRATE 12 /* struct: struct clockrate */
+#define KERN_VNODE 13 /* struct: vnode structures */
+#define KERN_PROC 14 /* struct: process entries */
+#define KERN_FILE 15 /* struct: file entries */
+#define KERN_PROF 16 /* node: kernel profiling info */
+#define KERN_POSIX1 17 /* int: POSIX.1 version */
+#define KERN_NGROUPS 18 /* int: # of supplemental group ids */
+#define KERN_JOB_CONTROL 19 /* int: is job control available */
+#define KERN_SAVED_IDS 20 /* int: saved set-user/group-ID */
+#define KERN_BOOTTIME 21 /* struct: time kernel was booted */
+#define KERN_MAXID 22 /* number of valid kern ids */
+
+#define CTL_KERN_NAMES { \
+ { 0, 0 }, \
+ { "ostype", CTLTYPE_STRING }, \
+ { "osrelease", CTLTYPE_STRING }, \
+ { "osrevision", CTLTYPE_INT }, \
+ { "version", CTLTYPE_STRING }, \
+ { "maxvnodes", CTLTYPE_INT }, \
+ { "maxproc", CTLTYPE_INT }, \
+ { "maxfiles", CTLTYPE_INT }, \
+ { "argmax", CTLTYPE_INT }, \
+ { "securelevel", CTLTYPE_INT }, \
+ { "hostname", CTLTYPE_STRING }, \
+ { "hostid", CTLTYPE_INT }, \
+ { "clockrate", CTLTYPE_STRUCT }, \
+ { "vnode", CTLTYPE_STRUCT }, \
+ { "proc", CTLTYPE_STRUCT }, \
+ { "file", CTLTYPE_STRUCT }, \
+ { "profiling", CTLTYPE_NODE }, \
+ { "posix1version", CTLTYPE_INT }, \
+ { "ngroups", CTLTYPE_INT }, \
+ { "job_control", CTLTYPE_INT }, \
+ { "saved_ids", CTLTYPE_INT }, \
+ { "boottime", CTLTYPE_STRUCT }, \
+}
+
+/*
+ * KERN_PROC subtypes
+ */
+#define KERN_PROC_ALL 0 /* everything */
+#define KERN_PROC_PID 1 /* by process id */
+#define KERN_PROC_PGRP 2 /* by process group id */
+#define KERN_PROC_SESSION 3 /* by session of pid */
+#define KERN_PROC_TTY 4 /* by controlling tty */
+#define KERN_PROC_UID 5 /* by effective uid */
+#define KERN_PROC_RUID 6 /* by real uid */
+
+/*
+ * KERN_PROC subtype ops return arrays of augmented proc structures:
+ */
+struct kinfo_proc {
+ struct proc kp_proc; /* proc structure */
+ struct eproc {
+ struct proc *e_paddr; /* address of proc */
+ struct session *e_sess; /* session pointer */
+ struct pcred e_pcred; /* process credentials */
+ struct ucred e_ucred; /* current credentials */
+#ifdef sparc
+ struct {
+ segsz_t vm_rssize; /* resident set size */
+ segsz_t vm_tsize; /* text size */
+ segsz_t vm_dsize; /* data size */
+ segsz_t vm_ssize; /* stack size */
+ } e_vm;
+#else
+ struct vmspace e_vm; /* address space */
+#endif
+ pid_t e_ppid; /* parent process id */
+ pid_t e_pgid; /* process group id */
+ short e_jobc; /* job control counter */
+ dev_t e_tdev; /* controlling tty dev */
+ pid_t e_tpgid; /* tty process group id */
+ struct session *e_tsess; /* tty session pointer */
+#define WMESGLEN 7
+ char e_wmesg[WMESGLEN+1]; /* wchan message */
+ segsz_t e_xsize; /* text size */
+ short e_xrssize; /* text rss */
+ short e_xccount; /* text references */
+ short e_xswrss;
+ long e_flag;
+#define EPROC_CTTY 0x01 /* controlling tty vnode active */
+#define EPROC_SLEADER 0x02 /* session leader */
+ char e_login[MAXLOGNAME]; /* setlogin() name */
+ long e_spare[4];
+ } kp_eproc;
+};
+
+/*
+ * CTL_HW identifiers
+ */
+#define HW_MACHINE 1 /* string: machine class */
+#define HW_MODEL 2 /* string: specific machine model */
+#define HW_NCPU 3 /* int: number of cpus */
+#define HW_BYTEORDER 4 /* int: machine byte order */
+#define HW_PHYSMEM 5 /* int: total memory */
+#define HW_USERMEM 6 /* int: non-kernel memory */
+#define HW_PAGESIZE 7 /* int: software page size */
+#define HW_DISKNAMES 8 /* strings: disk drive names */
+#define HW_DISKSTATS 9 /* struct: diskstats[] */
+#define HW_MAXID 10 /* number of valid hw ids */
+
+#define CTL_HW_NAMES { \
+ { 0, 0 }, \
+ { "machine", CTLTYPE_STRING }, \
+ { "model", CTLTYPE_STRING }, \
+ { "ncpu", CTLTYPE_INT }, \
+ { "byteorder", CTLTYPE_INT }, \
+ { "physmem", CTLTYPE_INT }, \
+ { "usermem", CTLTYPE_INT }, \
+ { "pagesize", CTLTYPE_INT }, \
+ { "disknames", CTLTYPE_STRUCT }, \
+ { "diskstats", CTLTYPE_STRUCT }, \
+}
+
+/*
+ * CTL_USER definitions
+ */
+#define USER_CS_PATH 1 /* string: _CS_PATH */
+#define USER_BC_BASE_MAX 2 /* int: BC_BASE_MAX */
+#define USER_BC_DIM_MAX 3 /* int: BC_DIM_MAX */
+#define USER_BC_SCALE_MAX 4 /* int: BC_SCALE_MAX */
+#define USER_BC_STRING_MAX 5 /* int: BC_STRING_MAX */
+#define USER_COLL_WEIGHTS_MAX 6 /* int: COLL_WEIGHTS_MAX */
+#define USER_EXPR_NEST_MAX 7 /* int: EXPR_NEST_MAX */
+#define USER_LINE_MAX 8 /* int: LINE_MAX */
+#define USER_RE_DUP_MAX 9 /* int: RE_DUP_MAX */
+#define USER_POSIX2_VERSION 10 /* int: POSIX2_VERSION */
+#define USER_POSIX2_C_BIND 11 /* int: POSIX2_C_BIND */
+#define USER_POSIX2_C_DEV 12 /* int: POSIX2_C_DEV */
+#define USER_POSIX2_CHAR_TERM 13 /* int: POSIX2_CHAR_TERM */
+#define USER_POSIX2_FORT_DEV 14 /* int: POSIX2_FORT_DEV */
+#define USER_POSIX2_FORT_RUN 15 /* int: POSIX2_FORT_RUN */
+#define USER_POSIX2_LOCALEDEF 16 /* int: POSIX2_LOCALEDEF */
+#define USER_POSIX2_SW_DEV 17 /* int: POSIX2_SW_DEV */
+#define USER_POSIX2_UPE 18 /* int: POSIX2_UPE */
+#define USER_STREAM_MAX 19 /* int: POSIX2_STREAM_MAX */
+#define USER_TZNAME_MAX 20 /* int: POSIX2_TZNAME_MAX */
+#define USER_MAXID 21 /* number of valid user ids */
+
+#define CTL_USER_NAMES { \
+ { 0, 0 }, \
+ { "cs_path", CTLTYPE_STRING }, \
+ { "bc_base_max", CTLTYPE_INT }, \
+ { "bc_dim_max", CTLTYPE_INT }, \
+ { "bc_scale_max", CTLTYPE_INT }, \
+ { "bc_string_max", CTLTYPE_INT }, \
+ { "coll_weights_max", CTLTYPE_INT }, \
+ { "expr_nest_max", CTLTYPE_INT }, \
+ { "line_max", CTLTYPE_INT }, \
+ { "re_dup_max", CTLTYPE_INT }, \
+ { "posix2_version", CTLTYPE_INT }, \
+ { "posix2_c_bind", CTLTYPE_INT }, \
+ { "posix2_c_dev", CTLTYPE_INT }, \
+ { "posix2_char_term", CTLTYPE_INT }, \
+ { "posix2_fort_dev", CTLTYPE_INT }, \
+ { "posix2_fort_run", CTLTYPE_INT }, \
+ { "posix2_localedef", CTLTYPE_INT }, \
+ { "posix2_sw_dev", CTLTYPE_INT }, \
+ { "posix2_upe", CTLTYPE_INT }, \
+ { "stream_max", CTLTYPE_INT }, \
+ { "tzname_max", CTLTYPE_INT }, \
+}
+
+/*
+ * CTL_DEBUG definitions
+ *
+ * Second level identifier specifies which debug variable.
+ * Third level identifier specifies which stucture component.
+ */
+#define CTL_DEBUG_NAME 0 /* string: variable name */
+#define CTL_DEBUG_VALUE 1 /* int: variable value */
+#define CTL_DEBUG_MAXID 20
+
+#ifdef KERNEL
+#ifdef DEBUG
+/*
+ * CTL_DEBUG variables.
+ *
+ * These are declared as separate variables so that they can be
+ * individually initialized at the location of their associated
+ * variable. The loader prevents multiple use by issuing errors
+ * if a variable is initialized in more than one place. They are
+ * aggregated into an array in debug_sysctl(), so that it can
+ * conveniently locate them when querried. If more debugging
+ * variables are added, they must also be declared here and also
+ * entered into the array.
+ */
+struct ctldebug {
+ char *debugname; /* name of debugging variable */
+ int *debugvar; /* pointer to debugging variable */
+};
+extern struct ctldebug debug0, debug1, debug2, debug3, debug4;
+extern struct ctldebug debug5, debug6, debug7, debug8, debug9;
+extern struct ctldebug debug10, debug11, debug12, debug13, debug14;
+extern struct ctldebug debug15, debug16, debug17, debug18, debug19;
+#endif /* DEBUG */
+
+/*
+ * Internal sysctl function calling convention:
+ *
+ * (*sysctlfn)(name, namelen, oldval, oldlenp, newval, newlen);
+ *
+ * The name parameter points at the next component of the name to be
+ * interpreted. The namelen parameter is the number of integers in
+ * the name.
+ */
+typedef int (sysctlfn)
+ __P((int *, u_int, void *, size_t *, void *, size_t, struct proc *));
+
+int sysctl_int __P((void *, size_t *, void *, size_t, int *));
+int sysctl_rdint __P((void *, size_t *, void *, int));
+int sysctl_string __P((void *, size_t *, void *, size_t, char *, int));
+int sysctl_rdstring __P((void *, size_t *, void *, char *));
+int sysctl_rdstruct __P((void *, size_t *, void *, void *, int));
+void fill_eproc __P((struct proc *, struct eproc *));
+
+#else /* !KERNEL */
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int sysctl __P((int *, u_int, void *, size_t *, void *, size_t));
+__END_DECLS
+#endif /* KERNEL */
+#endif /* !_SYS_SYSCTL_H_ */
diff --git a/sys/sys/syslimits.h b/sys/sys/syslimits.h
new file mode 100644
index 0000000..550000c
--- /dev/null
+++ b/sys/sys/syslimits.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)syslimits.h 8.1 (Berkeley) 6/2/93
+ */
+
+#define ARG_MAX 20480 /* max bytes for an exec function */
+#define CHILD_MAX 40 /* max simultaneous processes */
+#define LINK_MAX 32767 /* max file link count */
+#define MAX_CANON 255 /* max bytes in term canon input line */
+#define MAX_INPUT 255 /* max bytes in terminal input */
+#define NAME_MAX 255 /* max bytes in a file name */
+#define NGROUPS_MAX 16 /* max supplemental group id's */
+#define OPEN_MAX 64 /* max open files per process */
+#define PATH_MAX 1024 /* max bytes in pathname */
+#define PIPE_BUF 512 /* max bytes for atomic pipe writes */
+
+#define BC_BASE_MAX 99 /* max ibase/obase values in bc(1) */
+#define BC_DIM_MAX 2048 /* max array elements in bc(1) */
+#define BC_SCALE_MAX 99 /* max scale value in bc(1) */
+#define BC_STRING_MAX 1000 /* max const string length in bc(1) */
+#define COLL_WEIGHTS_MAX 0 /* max weights for order keyword */
+#define EXPR_NEST_MAX 32 /* max expressions nested in expr(1) */
+#define LINE_MAX 2048 /* max bytes in an input line */
+#define RE_DUP_MAX 255 /* max RE's in interval notation */
diff --git a/sys/sys/syslog.h b/sys/sys/syslog.h
new file mode 100644
index 0000000..935db2d
--- /dev/null
+++ b/sys/sys/syslog.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)syslog.h 8.1 (Berkeley) 6/2/93
+ */
+
+#define _PATH_LOG "/dev/log"
+
+/*
+ * priorities/facilities are encoded into a single 32-bit quantity, where the
+ * bottom 3 bits are the priority (0-7) and the top 28 bits are the facility
+ * (0-big number). Both the priorities and the facilities map roughly
+ * one-to-one to strings in the syslogd(8) source code. This mapping is
+ * included in this file.
+ *
+ * priorities (these are ordered)
+ */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but significant condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+
+#define LOG_PRIMASK 0x07 /* mask to extract priority part (internal) */
+ /* extract priority */
+#define LOG_PRI(p) ((p) & LOG_PRIMASK)
+#define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri))
+
+#ifdef SYSLOG_NAMES
+#define INTERNAL_NOPRI 0x10 /* the "no priority" priority */
+ /* mark "facility" */
+#define INTERNAL_MARK LOG_MAKEPRI(LOG_NFACILITIES, 0)
+typedef struct _code {
+ char *c_name;
+ int c_val;
+} CODE;
+
+CODE prioritynames[] = {
+ "alert", LOG_ALERT,
+ "crit", LOG_CRIT,
+ "debug", LOG_DEBUG,
+ "emerg", LOG_EMERG,
+ "err", LOG_ERR,
+ "error", LOG_ERR, /* DEPRECATED */
+ "info", LOG_INFO,
+ "none", INTERNAL_NOPRI, /* INTERNAL */
+ "notice", LOG_NOTICE,
+ "panic", LOG_EMERG, /* DEPRECATED */
+ "warn", LOG_WARNING, /* DEPRECATED */
+ "warning", LOG_WARNING,
+ NULL, -1,
+};
+#endif
+
+/* facility codes */
+#define LOG_KERN (0<<3) /* kernel messages */
+#define LOG_USER (1<<3) /* random user-level messages */
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */
+#define LOG_LPR (6<<3) /* line printer subsystem */
+#define LOG_NEWS (7<<3) /* network news subsystem */
+#define LOG_UUCP (8<<3) /* UUCP subsystem */
+#define LOG_CRON (9<<3) /* clock daemon */
+#define LOG_AUTHPRIV (10<<3) /* security/authorization messages (private) */
+#define LOG_FTP (11<<3) /* ftp daemon */
+
+ /* other codes through 15 reserved for system use */
+#define LOG_LOCAL0 (16<<3) /* reserved for local use */
+#define LOG_LOCAL1 (17<<3) /* reserved for local use */
+#define LOG_LOCAL2 (18<<3) /* reserved for local use */
+#define LOG_LOCAL3 (19<<3) /* reserved for local use */
+#define LOG_LOCAL4 (20<<3) /* reserved for local use */
+#define LOG_LOCAL5 (21<<3) /* reserved for local use */
+#define LOG_LOCAL6 (22<<3) /* reserved for local use */
+#define LOG_LOCAL7 (23<<3) /* reserved for local use */
+
+#define LOG_NFACILITIES 24 /* current number of facilities */
+#define LOG_FACMASK 0x03f8 /* mask to extract facility part */
+ /* facility of pri */
+#define LOG_FAC(p) (((p) & LOG_FACMASK) >> 3)
+
+#ifdef SYSLOG_NAMES
+CODE facilitynames[] = {
+ "auth", LOG_AUTH,
+ "authpriv", LOG_AUTHPRIV,
+ "cron", LOG_CRON,
+ "daemon", LOG_DAEMON,
+ "ftp", LOG_FTP,
+ "kern", LOG_KERN,
+ "lpr", LOG_LPR,
+ "mail", LOG_MAIL,
+ "mark", INTERNAL_MARK, /* INTERNAL */
+ "news", LOG_NEWS,
+ "security", LOG_AUTH, /* DEPRECATED */
+ "syslog", LOG_SYSLOG,
+ "user", LOG_USER,
+ "uucp", LOG_UUCP,
+ "local0", LOG_LOCAL0,
+ "local1", LOG_LOCAL1,
+ "local2", LOG_LOCAL2,
+ "local3", LOG_LOCAL3,
+ "local4", LOG_LOCAL4,
+ "local5", LOG_LOCAL5,
+ "local6", LOG_LOCAL6,
+ "local7", LOG_LOCAL7,
+ NULL, -1,
+};
+#endif
+
+#ifdef KERNEL
+#define LOG_PRINTF -1 /* pseudo-priority to indicate use of printf */
+#endif
+
+/*
+ * arguments to setlogmask.
+ */
+#define LOG_MASK(pri) (1 << (pri)) /* mask for one priority */
+#define LOG_UPTO(pri) ((1 << ((pri)+1)) - 1) /* all priorities through pri */
+
+/*
+ * Option flags for openlog.
+ *
+ * LOG_ODELAY no longer does anything.
+ * LOG_NDELAY is the inverse of what it used to be.
+ */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until first syslog() (default) */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* don't wait for console forks: DEPRECATED */
+#define LOG_PERROR 0x20 /* log to stderr as well */
+
+#ifndef KERNEL
+
+/*
+ * Don't use va_list in the vsyslog() prototype. Va_list is typedef'd in two
+ * places (<machine/varargs.h> and <machine/stdarg.h>), so if we include one
+ * of them here we may collide with the utility's includes. It's unreasonable
+ * for utilities to have to include one of them to include syslog.h, so we get
+ * _BSD_VA_LIST_ from <machine/ansi.h> and use it.
+ */
+#include <machine/ansi.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+void closelog __P((void));
+void openlog __P((const char *, int, int));
+int setlogmask __P((int));
+void syslog __P((int, const char *, ...));
+void vsyslog __P((int, const char *, _BSD_VA_LIST_));
+__END_DECLS
+
+#endif /* !KERNEL */
diff --git a/sys/sys/systm.h b/sys/sys/systm.h
new file mode 100644
index 0000000..ba2f12b
--- /dev/null
+++ b/sys/sys/systm.h
@@ -0,0 +1,168 @@
+/*-
+ * Copyright (c) 1982, 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)systm.h 8.4 (Berkeley) 2/23/94
+ */
+
+#include <machine/cpufunc.h>
+
+/*
+ * The `securelevel' variable controls the security level of the system.
+ * It can only be decreased by process 1 (/sbin/init).
+ *
+ * Security levels are as follows:
+ * -1 permannently insecure mode - always run system in level 0 mode.
+ * 0 insecure mode - immutable and append-only flags make be turned off.
+ * All devices may be read or written subject to permission modes.
+ * 1 secure mode - immutable and append-only flags may not be changed;
+ * raw disks of mounted filesystems, /dev/mem, and /dev/kmem are
+ * read-only.
+ * 2 highly secure mode - same as (1) plus raw disks are always
+ * read-only whether mounted or not. This level precludes tampering
+ * with filesystems by unmounting them, but also inhibits running
+ * newfs while the system is secured.
+ *
+ * In normal operation, the system runs in level 0 mode while single user
+ * and in level 1 mode while multiuser. If level 2 mode is desired while
+ * running multiuser, it can be set in the multiuser startup script
+ * (/etc/rc.local) using sysctl(1). If it is desired to run the system
+ * in level 0 mode while multiuser, initialize the variable securelevel
+ * in /sys/kern/kern_sysctl.c to -1. Note that it is NOT initialized to
+ * zero as that would allow the vmunix binary to be patched to -1.
+ * Without initialization, securelevel loads in the BSS area which only
+ * comes into existence when the kernel is loaded and hence cannot be
+ * patched by a stalking hacker.
+ */
+extern int securelevel; /* system security level */
+extern const char *panicstr; /* panic message */
+extern char version[]; /* system version */
+extern char copyright[]; /* system copyright */
+
+extern int nblkdev; /* number of entries in bdevsw */
+extern int nchrdev; /* number of entries in cdevsw */
+extern int nswdev; /* number of swap devices */
+extern int nswap; /* size of swap space */
+
+extern int selwait; /* select timeout address */
+
+extern u_char curpriority; /* priority of current process */
+
+extern int maxmem; /* max memory per process */
+extern int physmem; /* physical memory */
+
+extern dev_t dumpdev; /* dump device */
+extern long dumplo; /* offset into dumpdev */
+
+extern dev_t rootdev; /* root device */
+extern struct vnode *rootvp; /* vnode equivalent to above */
+
+extern dev_t swapdev; /* swapping device */
+extern struct vnode *swapdev_vp;/* vnode equivalent to above */
+
+extern struct sysent { /* system call table */
+ int sy_narg; /* number of arguments */
+ int (*sy_call)(); /* implementing function */
+} sysent[];
+
+extern int boothowto; /* reboot flags, from console subsystem */
+
+/* casts to keep lint happy */
+#define insque(q,p) _insque((caddr_t)q,(caddr_t)p)
+#define remque(q) _remque((caddr_t)q)
+
+/*
+ * General function declarations.
+ */
+int nullop __P((void));
+int enodev __P((void));
+int enoioctl __P((void));
+int enxio __P((void));
+int eopnotsupp __P((void));
+int seltrue __P((dev_t dev, int which, struct proc *p));
+void *hashinit __P((int count, int type, u_long *hashmask));
+
+#ifdef __GNUC__
+volatile void panic __P((const char *, ...));
+#else
+void panic __P((const char *, ...));
+#endif
+void tablefull __P((const char *));
+void addlog __P((const char *, ...));
+void log __P((int, const char *, ...));
+void printf __P((const char *, ...));
+int sprintf __P((char *buf, const char *, ...));
+void ttyprintf __P((struct tty *, const char *, ...));
+
+void bcopy __P((const void *from, void *to, u_int len));
+void ovbcopy __P((const void *from, void *to, u_int len));
+void bzero __P((void *buf, u_int len));
+
+int copystr __P((void *kfaddr, void *kdaddr, u_int len, u_int *done));
+int copyinstr __P((void *udaddr, void *kaddr, u_int len, u_int *done));
+int copyoutstr __P((void *kaddr, void *udaddr, u_int len, u_int *done));
+int copyin __P((void *udaddr, void *kaddr, u_int len));
+int copyout __P((void *kaddr, void *udaddr, u_int len));
+
+int fubyte __P((void *base));
+#ifdef notdef
+int fuibyte __P((void *base));
+#endif
+int subyte __P((void *base, int byte));
+int suibyte __P((void *base, int byte));
+int fuword __P((void *base));
+int fuiword __P((void *base));
+int suword __P((void *base, int word));
+int suiword __P((void *base, int word));
+
+int hzto __P((struct timeval *tv));
+typedef void (*timeout_func_t)(void *);
+void timeout __P((void (*func)(void *), void *arg, int ticks));
+void untimeout __P((void (*func)(void *), void *arg));
+void realitexpire __P((void *));
+
+struct clockframe;
+void hardclock __P((struct clockframe *frame));
+void softclock __P((void));
+void statclock __P((struct clockframe *frame));
+
+void initclocks __P((void));
+
+void startprofclock __P((struct proc *));
+void stopprofclock __P((struct proc *));
+void setstatclockrate __P((int hzrate));
+
+#include <libkern/libkern.h>
diff --git a/sys/sys/tablet.h b/sys/sys/tablet.h
new file mode 100644
index 0000000..cbb3f23
--- /dev/null
+++ b/sys/sys/tablet.h
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 1985, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tablet.h 8.3 (Berkeley) 1/4/94
+ */
+
+#ifndef _SYS_TABLET_H_
+#define _SYS_TABLET_H_
+
+/*
+ * Tablet line discipline.
+ */
+#include <sys/ioctl.h>
+
+/*
+ * Reads on the tablet return one of the following structures, depending on
+ * the underlying tablet type. The first two are defined such that a read of
+ * sizeof (gtcopos) on a non-gtco tablet will return meaningful info. The
+ * in-proximity bit is simulated where the tablet does not directly provide
+ * the information.
+ */
+struct tbpos {
+ int xpos, ypos; /* raw x-y coordinates */
+ short status; /* buttons/pen down */
+#define TBINPROX 0100000 /* pen in proximity of tablet */
+ short scount; /* sample count */
+};
+
+struct gtcopos {
+ int xpos, ypos; /* raw x-y coordinates */
+ short status; /* as above */
+ short scount; /* sample count */
+ short xtilt, ytilt; /* raw tilt */
+ short pressure;
+ short pad; /* pad to longword boundary */
+};
+
+struct polpos {
+ short p_x, p_y, p_z; /* raw 3-space coordinates */
+ short p_azi, p_pit, p_rol; /* azimuth, pitch, and roll */
+ short p_stat; /* status, as above */
+ char p_key; /* calculator input keyboard */
+};
+
+#define BIOSMODE _IOW('b', 1, int) /* set mode bit(s) */
+#define BIOGMODE _IOR('b', 2, int) /* get mode bit(s) */
+#define TBMODE 0xfff0 /* mode bits: */
+#define TBPOINT 0x0010 /* single point */
+#define TBRUN 0x0000 /* runs contin. */
+#define TBSTOP 0x0020 /* shut-up */
+#define TBGO 0x0000 /* ~TBSTOP */
+#define TBTYPE 0x000f /* tablet type: */
+#define TBUNUSED 0x0
+#define TBHITACHI 0x1 /* hitachi tablet */
+#define TBTIGER 0x2 /* hitachi tiger */
+#define TBGTCO 0x3 /* gtco */
+#define TBPOL 0x4 /* polhemus 3space */
+#define TBHDG 0x5 /* hdg-1111b, low res */
+#define TBHDGHIRES 0x6 /* hdg-1111b, high res */
+#define TBDIGI 0x7 /* gtco digi-pad, low res */
+#define TBDIGIHIRES 0x8 /* gtco digi-pad, high res */
+#define BIOSTYPE _IOW('b', 3, int) /* set tablet type */
+#define BIOGTYPE _IOR('b', 4, int) /* get tablet type*/
+
+#endif /* !_SYS_TABLET_H_ */
diff --git a/sys/sys/termios.h b/sys/sys/termios.h
new file mode 100644
index 0000000..09c34a1
--- /dev/null
+++ b/sys/sys/termios.h
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 1988, 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)termios.h 8.3 (Berkeley) 3/28/94
+ */
+
+#ifndef _SYS_TERMIOS_H_
+#define _SYS_TERMIOS_H_
+
+/*
+ * Special Control Characters
+ *
+ * Index into c_cc[] character array.
+ *
+ * Name Subscript Enabled by
+ */
+#define VEOF 0 /* ICANON */
+#define VEOL 1 /* ICANON */
+#ifndef _POSIX_SOURCE
+#define VEOL2 2 /* ICANON */
+#endif
+#define VERASE 3 /* ICANON */
+#ifndef _POSIX_SOURCE
+#define VWERASE 4 /* ICANON */
+#endif
+#define VKILL 5 /* ICANON */
+#ifndef _POSIX_SOURCE
+#define VREPRINT 6 /* ICANON */
+#endif
+/* 7 spare 1 */
+#define VINTR 8 /* ISIG */
+#define VQUIT 9 /* ISIG */
+#define VSUSP 10 /* ISIG */
+#ifndef _POSIX_SOURCE
+#define VDSUSP 11 /* ISIG */
+#endif
+#define VSTART 12 /* IXON, IXOFF */
+#define VSTOP 13 /* IXON, IXOFF */
+#ifndef _POSIX_SOURCE
+#define VLNEXT 14 /* IEXTEN */
+#define VDISCARD 15 /* IEXTEN */
+#endif
+#define VMIN 16 /* !ICANON */
+#define VTIME 17 /* !ICANON */
+#ifndef _POSIX_SOURCE
+#define VSTATUS 18 /* ICANON */
+/* 19 spare 2 */
+#endif
+#define NCCS 20
+
+#define _POSIX_VDISABLE ((unsigned char)'\377')
+
+#ifndef _POSIX_SOURCE
+#define CCEQ(val, c) (c == val ? val != _POSIX_VDISABLE : 0)
+#endif
+
+/*
+ * Input flags - software input processing
+ */
+#define IGNBRK 0x00000001 /* ignore BREAK condition */
+#define BRKINT 0x00000002 /* map BREAK to SIGINTR */
+#define IGNPAR 0x00000004 /* ignore (discard) parity errors */
+#define PARMRK 0x00000008 /* mark parity and framing errors */
+#define INPCK 0x00000010 /* enable checking of parity errors */
+#define ISTRIP 0x00000020 /* strip 8th bit off chars */
+#define INLCR 0x00000040 /* map NL into CR */
+#define IGNCR 0x00000080 /* ignore CR */
+#define ICRNL 0x00000100 /* map CR to NL (ala CRMOD) */
+#define IXON 0x00000200 /* enable output flow control */
+#define IXOFF 0x00000400 /* enable input flow control */
+#ifndef _POSIX_SOURCE
+#define IXANY 0x00000800 /* any char will restart after stop */
+#define IMAXBEL 0x00002000 /* ring bell on input queue full */
+#endif /*_POSIX_SOURCE */
+
+/*
+ * Output flags - software output processing
+ */
+#define OPOST 0x00000001 /* enable following output processing */
+#ifndef _POSIX_SOURCE
+#define ONLCR 0x00000002 /* map NL to CR-NL (ala CRMOD) */
+#define OXTABS 0x00000004 /* expand tabs to spaces */
+#define ONOEOT 0x00000008 /* discard EOT's (^D) on output) */
+#endif /*_POSIX_SOURCE */
+
+/*
+ * Control flags - hardware control of terminal
+ */
+#ifndef _POSIX_SOURCE
+#define CIGNORE 0x00000001 /* ignore control flags */
+#endif
+#define CSIZE 0x00000300 /* character size mask */
+#define CS5 0x00000000 /* 5 bits (pseudo) */
+#define CS6 0x00000100 /* 6 bits */
+#define CS7 0x00000200 /* 7 bits */
+#define CS8 0x00000300 /* 8 bits */
+#define CSTOPB 0x00000400 /* send 2 stop bits */
+#define CREAD 0x00000800 /* enable receiver */
+#define PARENB 0x00001000 /* parity enable */
+#define PARODD 0x00002000 /* odd parity, else even */
+#define HUPCL 0x00004000 /* hang up on last close */
+#define CLOCAL 0x00008000 /* ignore modem status lines */
+#ifndef _POSIX_SOURCE
+#define CCTS_OFLOW 0x00010000 /* CTS flow control of output */
+#define CRTSCTS (CCTS_OFLOW | CRTS_IFLOW)
+#define CRTS_IFLOW 0x00020000 /* RTS flow control of input */
+#define CDSR_OFLOW 0x00080000 /* DSR flow control of output */
+#define MDMBUF 0x00100000 /* flow control output via Carrier */
+#endif
+
+
+/*
+ * "Local" flags - dumping ground for other state
+ *
+ * Warning: some flags in this structure begin with
+ * the letter "I" and look like they belong in the
+ * input flag.
+ */
+
+#ifndef _POSIX_SOURCE
+#define ECHOKE 0x00000001 /* visual erase for line kill */
+#endif /*_POSIX_SOURCE */
+#define ECHOE 0x00000002 /* visually erase chars */
+#define ECHOK 0x00000004 /* echo NL after line kill */
+#define ECHO 0x00000008 /* enable echoing */
+#define ECHONL 0x00000010 /* echo NL even if ECHO is off */
+#ifndef _POSIX_SOURCE
+#define ECHOPRT 0x00000020 /* visual erase mode for hardcopy */
+#define ECHOCTL 0x00000040 /* echo control chars as ^(Char) */
+#endif /*_POSIX_SOURCE */
+#define ISIG 0x00000080 /* enable signals INTR, QUIT, [D]SUSP */
+#define ICANON 0x00000100 /* canonicalize input lines */
+#ifndef _POSIX_SOURCE
+#define ALTWERASE 0x00000200 /* use alternate WERASE algorithm */
+#endif /*_POSIX_SOURCE */
+#define IEXTEN 0x00000400 /* enable DISCARD and LNEXT */
+#define EXTPROC 0x00000800 /* external processing */
+#define TOSTOP 0x00400000 /* stop background jobs from output */
+#ifndef _POSIX_SOURCE
+#define FLUSHO 0x00800000 /* output being flushed (state) */
+#define NOKERNINFO 0x02000000 /* no kernel output from VSTATUS */
+#define PENDIN 0x20000000 /* XXX retype pending input (state) */
+#endif /*_POSIX_SOURCE */
+#define NOFLSH 0x80000000 /* don't flush after interrupt */
+
+typedef unsigned long tcflag_t;
+typedef unsigned char cc_t;
+typedef long speed_t;
+
+struct termios {
+ tcflag_t c_iflag; /* input flags */
+ tcflag_t c_oflag; /* output flags */
+ tcflag_t c_cflag; /* control flags */
+ tcflag_t c_lflag; /* local flags */
+ cc_t c_cc[NCCS]; /* control chars */
+ long c_ispeed; /* input speed */
+ long c_ospeed; /* output speed */
+};
+
+/*
+ * Commands passed to tcsetattr() for setting the termios structure.
+ */
+#define TCSANOW 0 /* make change immediate */
+#define TCSADRAIN 1 /* drain output, then change */
+#define TCSAFLUSH 2 /* drain output, flush input */
+#ifndef _POSIX_SOURCE
+#define TCSASOFT 0x10 /* flag - don't alter h.w. state */
+#endif
+
+/*
+ * Standard speeds
+ */
+#define B0 0
+#define B50 50
+#define B75 75
+#define B110 110
+#define B134 134
+#define B150 150
+#define B200 200
+#define B300 300
+#define B600 600
+#define B1200 1200
+#define B1800 1800
+#define B2400 2400
+#define B4800 4800
+#define B9600 9600
+#define B19200 19200
+#define B38400 38400
+#ifndef _POSIX_SOURCE
+#define B7200 7200
+#define B14400 14400
+#define B28800 28800
+#define B57600 57600
+#define B76800 76800
+#define B115200 115200
+#define B230400 230400
+#define EXTA 19200
+#define EXTB 38400
+#endif /* !_POSIX_SOURCE */
+
+#ifndef KERNEL
+
+#define TCIFLUSH 1
+#define TCOFLUSH 2
+#define TCIOFLUSH 3
+#define TCOOFF 1
+#define TCOON 2
+#define TCIOFF 3
+#define TCION 4
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+speed_t cfgetispeed __P((const struct termios *));
+speed_t cfgetospeed __P((const struct termios *));
+int cfsetispeed __P((struct termios *, speed_t));
+int cfsetospeed __P((struct termios *, speed_t));
+int tcgetattr __P((int, struct termios *));
+int tcsetattr __P((int, int, const struct termios *));
+int tcdrain __P((int));
+int tcflow __P((int, int));
+int tcflush __P((int, int));
+int tcsendbreak __P((int, int));
+
+#ifndef _POSIX_SOURCE
+void cfmakeraw __P((struct termios *));
+int cfsetspeed __P((struct termios *, speed_t));
+#endif /* !_POSIX_SOURCE */
+__END_DECLS
+
+#endif /* !KERNEL */
+
+#ifndef _POSIX_SOURCE
+
+/*
+ * Include tty ioctl's that aren't just for backwards compatibility
+ * with the old tty driver. These ioctl definitions were previously
+ * in <sys/ioctl.h>.
+ */
+#include <sys/ttycom.h>
+#endif
+
+/*
+ * END OF PROTECTED INCLUDE.
+ */
+#endif /* !_SYS_TERMIOS_H_ */
+
+#ifndef _POSIX_SOURCE
+#include <sys/ttydefaults.h>
+#endif
diff --git a/sys/sys/time.h b/sys/sys/time.h
new file mode 100644
index 0000000..5322771
--- /dev/null
+++ b/sys/sys/time.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)time.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _SYS_TIME_H_
+#define _SYS_TIME_H_
+
+/*
+ * Structure returned by gettimeofday(2) system call,
+ * and used in other calls.
+ */
+struct timeval {
+ long tv_sec; /* seconds */
+ long tv_usec; /* and microseconds */
+};
+
+/*
+ * Structure defined by POSIX.4 to be like a timeval.
+ */
+struct timespec {
+ long ts_sec; /* seconds */
+ long ts_nsec; /* and nanoseconds */
+};
+
+#define TIMEVAL_TO_TIMESPEC(tv, ts) { \
+ (ts)->ts_sec = (tv)->tv_sec; \
+ (ts)->ts_nsec = (tv)->tv_usec * 1000; \
+}
+#define TIMESPEC_TO_TIMEVAL(tv, ts) { \
+ (tv)->tv_sec = (ts)->ts_sec; \
+ (tv)->tv_usec = (ts)->ts_nsec / 1000; \
+}
+
+struct timezone {
+ int tz_minuteswest; /* minutes west of Greenwich */
+ int tz_dsttime; /* type of dst correction */
+};
+#define DST_NONE 0 /* not on dst */
+#define DST_USA 1 /* USA style dst */
+#define DST_AUST 2 /* Australian style dst */
+#define DST_WET 3 /* Western European dst */
+#define DST_MET 4 /* Middle European dst */
+#define DST_EET 5 /* Eastern European dst */
+#define DST_CAN 6 /* Canada */
+
+/* Operations on timevals. */
+#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0
+#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
+#define timercmp(tvp, uvp, cmp) \
+ (((tvp)->tv_sec == (uvp)->tv_sec) ? \
+ ((tvp)->tv_usec cmp (uvp)->tv_usec) : \
+ ((tvp)->tv_sec cmp (uvp)->tv_sec))
+
+/*
+ * Names of the interval timers, and structure
+ * defining a timer setting.
+ */
+#define ITIMER_REAL 0
+#define ITIMER_VIRTUAL 1
+#define ITIMER_PROF 2
+
+struct itimerval {
+ struct timeval it_interval; /* timer interval */
+ struct timeval it_value; /* current value */
+};
+
+/*
+ * Getkerninfo clock information structure
+ */
+struct clockinfo {
+ int hz; /* clock frequency */
+ int tick; /* micro-seconds per hz tick */
+ int stathz; /* statistics clock frequency */
+ int profhz; /* profiling clock frequency */
+};
+
+#ifndef KERNEL
+#include <time.h>
+
+#ifndef _POSIX_SOURCE
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int adjtime __P((const struct timeval *, struct timeval *));
+int getitimer __P((int, struct itimerval *));
+int gettimeofday __P((struct timeval *, struct timezone *));
+int setitimer __P((int, const struct itimerval *, struct itimerval *));
+int settimeofday __P((const struct timeval *, const struct timezone *));
+int utimes __P((const char *, const struct timeval *));
+__END_DECLS
+#endif /* !POSIX */
+
+#endif /* !KERNEL */
+
+#endif /* !_SYS_TIME_H_ */
diff --git a/sys/sys/timeb.h b/sys/sys/timeb.h
new file mode 100644
index 0000000..2ab0105
--- /dev/null
+++ b/sys/sys/timeb.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)timeb.h 8.2 (Berkeley) 1/21/94
+ */
+
+/* The ftime(2) system call structure -- deprecated. */
+struct timeb {
+ time_t time; /* seconds since the Epoch */
+ unsigned short millitm; /* + milliseconds since the Epoch */
+ short timezone; /* minutes west of CUT */
+ short dstflag; /* DST == non-zero */
+};
diff --git a/sys/sys/times.h b/sys/sys/times.h
new file mode 100644
index 0000000..23a1500
--- /dev/null
+++ b/sys/sys/times.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)times.h 8.4 (Berkeley) 1/21/94
+ */
+
+#ifndef _SYS_TIMES_H_
+#define _SYS_TIMES_H_
+
+#include <machine/ansi.h>
+
+#ifdef _BSD_CLOCK_T_
+typedef _BSD_CLOCK_T_ clock_t;
+#undef _BSD_CLOCK_T_
+#endif
+
+struct tms {
+ clock_t tms_utime; /* User CPU time */
+ clock_t tms_stime; /* System CPU time */
+ clock_t tms_cutime; /* User CPU time of terminated child procs */
+ clock_t tms_cstime; /* System CPU time of terminated child procs */
+};
+
+#ifndef KERNEL
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+clock_t times __P((struct tms *));
+__END_DECLS
+#endif
+#endif /* !_SYS_TIMES_H_ */
diff --git a/sys/sys/timetc.h b/sys/sys/timetc.h
new file mode 100644
index 0000000..5322771
--- /dev/null
+++ b/sys/sys/timetc.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)time.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _SYS_TIME_H_
+#define _SYS_TIME_H_
+
+/*
+ * Structure returned by gettimeofday(2) system call,
+ * and used in other calls.
+ */
+struct timeval {
+ long tv_sec; /* seconds */
+ long tv_usec; /* and microseconds */
+};
+
+/*
+ * Structure defined by POSIX.4 to be like a timeval.
+ */
+struct timespec {
+ long ts_sec; /* seconds */
+ long ts_nsec; /* and nanoseconds */
+};
+
+#define TIMEVAL_TO_TIMESPEC(tv, ts) { \
+ (ts)->ts_sec = (tv)->tv_sec; \
+ (ts)->ts_nsec = (tv)->tv_usec * 1000; \
+}
+#define TIMESPEC_TO_TIMEVAL(tv, ts) { \
+ (tv)->tv_sec = (ts)->ts_sec; \
+ (tv)->tv_usec = (ts)->ts_nsec / 1000; \
+}
+
+struct timezone {
+ int tz_minuteswest; /* minutes west of Greenwich */
+ int tz_dsttime; /* type of dst correction */
+};
+#define DST_NONE 0 /* not on dst */
+#define DST_USA 1 /* USA style dst */
+#define DST_AUST 2 /* Australian style dst */
+#define DST_WET 3 /* Western European dst */
+#define DST_MET 4 /* Middle European dst */
+#define DST_EET 5 /* Eastern European dst */
+#define DST_CAN 6 /* Canada */
+
+/* Operations on timevals. */
+#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0
+#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
+#define timercmp(tvp, uvp, cmp) \
+ (((tvp)->tv_sec == (uvp)->tv_sec) ? \
+ ((tvp)->tv_usec cmp (uvp)->tv_usec) : \
+ ((tvp)->tv_sec cmp (uvp)->tv_sec))
+
+/*
+ * Names of the interval timers, and structure
+ * defining a timer setting.
+ */
+#define ITIMER_REAL 0
+#define ITIMER_VIRTUAL 1
+#define ITIMER_PROF 2
+
+struct itimerval {
+ struct timeval it_interval; /* timer interval */
+ struct timeval it_value; /* current value */
+};
+
+/*
+ * Getkerninfo clock information structure
+ */
+struct clockinfo {
+ int hz; /* clock frequency */
+ int tick; /* micro-seconds per hz tick */
+ int stathz; /* statistics clock frequency */
+ int profhz; /* profiling clock frequency */
+};
+
+#ifndef KERNEL
+#include <time.h>
+
+#ifndef _POSIX_SOURCE
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int adjtime __P((const struct timeval *, struct timeval *));
+int getitimer __P((int, struct itimerval *));
+int gettimeofday __P((struct timeval *, struct timezone *));
+int setitimer __P((int, const struct itimerval *, struct itimerval *));
+int settimeofday __P((const struct timeval *, const struct timezone *));
+int utimes __P((const char *, const struct timeval *));
+__END_DECLS
+#endif /* !POSIX */
+
+#endif /* !KERNEL */
+
+#endif /* !_SYS_TIME_H_ */
diff --git a/sys/sys/tprintf.h b/sys/sys/tprintf.h
new file mode 100644
index 0000000..5b83aae
--- /dev/null
+++ b/sys/sys/tprintf.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tprintf.h 8.1 (Berkeley) 6/2/93
+ */
+
+typedef struct session *tpr_t;
+
+tpr_t tprintf_open __P((struct proc *));
+void tprintf_close __P((tpr_t));
+
+void tprintf __P((tpr_t, const char *fmt, ...));
diff --git a/sys/sys/trace.h b/sys/sys/trace.h
new file mode 100644
index 0000000..d401f14
--- /dev/null
+++ b/sys/sys/trace.h
@@ -0,0 +1,116 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)trace.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * File system buffer tracing points; all trace <pack(dev, size), bn>
+ */
+#define TR_BREADHIT 0 /* buffer read found in cache */
+#define TR_BREADMISS 1 /* buffer read not in cache */
+#define TR_BWRITE 2 /* buffer written */
+#define TR_BREADHITRA 3 /* buffer read-ahead found in cache */
+#define TR_BREADMISSRA 4 /* buffer read-ahead not in cache */
+#define TR_XFODMISS 5 /* exe fod read */
+#define TR_XFODHIT 6 /* exe fod read */
+#define TR_BRELSE 7 /* brelse */
+#define TR_BREALLOC 8 /* expand/contract a buffer */
+
+/*
+ * Memory allocator trace points; all trace the amount of memory involved
+ */
+#define TR_MALL 10 /* memory allocated */
+
+/*
+ * Paging trace points: all are <vaddr, pid>
+ */
+#define TR_INTRANS 20 /* page intransit block */
+#define TR_EINTRANS 21 /* page intransit wait done */
+#define TR_FRECLAIM 22 /* reclaim from free list */
+#define TR_RECLAIM 23 /* reclaim from loop */
+#define TR_XSFREC 24 /* reclaim from free list instead of drum */
+#define TR_XIFREC 25 /* reclaim from free list instead of fsys */
+#define TR_WAITMEM 26 /* wait for memory in pagein */
+#define TR_EWAITMEM 27 /* end memory wait in pagein */
+#define TR_ZFOD 28 /* zfod page fault */
+#define TR_EXFOD 29 /* exec fod page fault */
+#define TR_VRFOD 30 /* vread fod page fault */
+#define TR_CACHEFOD 31 /* fod in file system cache */
+#define TR_SWAPIN 32 /* drum page fault */
+#define TR_PGINDONE 33 /* page in done */
+#define TR_SWAPIO 34 /* swap i/o request arrives */
+
+/*
+ * System call trace points.
+ */
+#define TR_VADVISE 40 /* vadvise occurred with <arg, pid> */
+
+/*
+ * Miscellaneous
+ */
+#define TR_STAMP 45 /* user said vtrace(VTR_STAMP, value); */
+
+/*
+ * This defines the size of the trace flags array.
+ */
+#define TR_NFLAGS 100 /* generous */
+
+#define TRCSIZ 4096
+
+/*
+ * Specifications of the vtrace() system call, which takes one argument.
+ */
+#define VTRACE 64+51
+
+#define VTR_DISABLE 0 /* set a trace flag to 0 */
+#define VTR_ENABLE 1 /* set a trace flag to 1 */
+#define VTR_VALUE 2 /* return value of a trace flag */
+#define VTR_UALARM 3 /* set alarm to go off (sig 16) */
+ /* in specified number of hz */
+#define VTR_STAMP 4 /* user specified stamp */
+
+#ifdef KERNEL
+#ifdef TRACE
+struct proc *traceproc;
+int tracewhich, tracebuf[TRCSIZ];
+u_int tracex;
+char traceflags[TR_NFLAGS];
+#define pack(v,b) (((v)->v_mount->mnt_stat.f_fsid.val[0])<<16)|(b)
+#define trace(a,b,c) { \
+ if (traceflags[a]) \
+ trace1(a,b,c); \
+}
+#else
+#define trace(a,b,c)
+#endif
+#endif
diff --git a/sys/sys/tty.h b/sys/sys/tty.h
new file mode 100644
index 0000000..4a89b03
--- /dev/null
+++ b/sys/sys/tty.h
@@ -0,0 +1,217 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tty.h 8.6 (Berkeley) 1/21/94
+ */
+
+#include <sys/termios.h>
+#include <sys/select.h> /* For struct selinfo. */
+
+/*
+ * Clists are character lists, which is a variable length linked list
+ * of cblocks, with a count of the number of characters in the list.
+ */
+struct clist {
+ int c_cc; /* Number of characters in the clist. */
+ char *c_cf; /* Pointer to the first cblock. */
+ char *c_cl; /* Pointer to the last cblock. */
+};
+
+/*
+ * Per-tty structure.
+ *
+ * Should be split in two, into device and tty drivers.
+ * Glue could be masks of what to echo and circular buffer
+ * (low, high, timeout).
+ */
+struct tty {
+ struct clist t_rawq; /* Device raw input queue. */
+ long t_rawcc; /* Raw input queue statistics. */
+ struct clist t_canq; /* Device canonical queue. */
+ long t_cancc; /* Canonical queue statistics. */
+ struct clist t_outq; /* Device output queue. */
+ long t_outcc; /* Output queue statistics. */
+ char t_line; /* Interface to device drivers. */
+ dev_t t_dev; /* Device. */
+ int t_state; /* Device and driver (TS*) state. */
+ int t_flags; /* Tty flags. */
+ struct pgrp *t_pgrp; /* Foreground process group. */
+ struct session *t_session; /* Enclosing session. */
+ struct selinfo t_rsel; /* Tty read/oob select. */
+ struct selinfo t_wsel; /* Tty write select. */
+ struct termios t_termios; /* Termios state. */
+ struct winsize t_winsize; /* Window size. */
+ /* Start output. */
+ void (*t_oproc) __P((struct tty *));
+ /* Stop output. */
+ void (*t_stop) __P((struct tty *, int));
+ /* Set hardware state. */
+ int (*t_param) __P((struct tty *, struct termios *));
+ void *t_sc; /* XXX: net/if_sl.c:sl_softc. */
+ short t_column; /* Tty output column. */
+ short t_rocount, t_rocol; /* Tty. */
+ short t_hiwat; /* High water mark. */
+ short t_lowat; /* Low water mark. */
+ short t_gen; /* Generation number. */
+};
+
+#define t_cc t_termios.c_cc
+#define t_cflag t_termios.c_cflag
+#define t_iflag t_termios.c_iflag
+#define t_ispeed t_termios.c_ispeed
+#define t_lflag t_termios.c_lflag
+#define t_min t_termios.c_min
+#define t_oflag t_termios.c_oflag
+#define t_ospeed t_termios.c_ospeed
+#define t_time t_termios.c_time
+
+#define TTIPRI 25 /* Sleep priority for tty reads. */
+#define TTOPRI 26 /* Sleep priority for tty writes. */
+
+#define TTMASK 15
+#define OBUFSIZ 100
+#define TTYHOG 1024
+
+#ifdef KERNEL
+#define TTMAXHIWAT roundup(2048, CBSIZE)
+#define TTMINHIWAT roundup(100, CBSIZE)
+#define TTMAXLOWAT 256
+#define TTMINLOWAT 32
+#endif
+
+/* These flags are kept in t_state. */
+#define TS_ASLEEP 0x00001 /* Process waiting for tty. */
+#define TS_ASYNC 0x00002 /* Tty in async I/O mode. */
+#define TS_BUSY 0x00004 /* Draining output. */
+#define TS_CARR_ON 0x00008 /* Carrier is present. */
+#define TS_FLUSH 0x00010 /* Outq has been flushed during DMA. */
+#define TS_ISOPEN 0x00020 /* Open has completed. */
+#define TS_TBLOCK 0x00040 /* Further input blocked. */
+#define TS_TIMEOUT 0x00080 /* Wait for output char processing. */
+#define TS_TTSTOP 0x00100 /* Output paused. */
+#define TS_WOPEN 0x00200 /* Open in progress. */
+#define TS_XCLUDE 0x00400 /* Tty requires exclusivity. */
+
+/* State for intra-line fancy editing work. */
+#define TS_BKSL 0x00800 /* State for lowercase \ work. */
+#define TS_CNTTB 0x01000 /* Counting tab width, ignore FLUSHO. */
+#define TS_ERASE 0x02000 /* Within a \.../ for PRTRUB. */
+#define TS_LNCH 0x04000 /* Next character is literal. */
+#define TS_TYPEN 0x08000 /* Retyping suspended input (PENDIN). */
+#define TS_LOCAL (TS_BKSL | TS_CNTTB | TS_ERASE | TS_LNCH | TS_TYPEN)
+
+/* Character type information. */
+#define ORDINARY 0
+#define CONTROL 1
+#define BACKSPACE 2
+#define NEWLINE 3
+#define TAB 4
+#define VTAB 5
+#define RETURN 6
+
+struct speedtab {
+ int sp_speed; /* Speed. */
+ int sp_code; /* Code. */
+};
+
+/* Modem control commands (driver). */
+#define DMSET 0
+#define DMBIS 1
+#define DMBIC 2
+#define DMGET 3
+
+/* Flags on a character passed to ttyinput. */
+#define TTY_CHARMASK 0x000000ff /* Character mask */
+#define TTY_QUOTE 0x00000100 /* Character quoted */
+#define TTY_ERRORMASK 0xff000000 /* Error mask */
+#define TTY_FE 0x01000000 /* Framing error or BREAK condition */
+#define TTY_PE 0x02000000 /* Parity error */
+
+/* Is tp controlling terminal for p? */
+#define isctty(p, tp) \
+ ((p)->p_session == (tp)->t_session && (p)->p_flag & P_CONTROLT)
+
+/* Is p in background of tp? */
+#define isbackground(p, tp) \
+ (isctty((p), (tp)) && (p)->p_pgrp != (tp)->t_pgrp)
+
+#ifdef KERNEL
+extern struct ttychars ttydefaults;
+
+/* Symbolic sleep message strings. */
+extern char ttyin[], ttyout[], ttopen[], ttclos[], ttybg[], ttybuf[];
+
+int b_to_q __P((char *cp, int cc, struct clist *q));
+void catq __P((struct clist *from, struct clist *to));
+void clist_init __P((void));
+int getc __P((struct clist *q));
+void ndflush __P((struct clist *q, int cc));
+int ndqb __P((struct clist *q, int flag));
+char *nextc __P((struct clist *q, char *cp, int *c));
+int putc __P((int c, struct clist *q));
+int q_to_b __P((struct clist *q, char *cp, int cc));
+int unputc __P((struct clist *q));
+
+int nullmodem __P((struct tty *tp, int flag));
+int tputchar __P((int c, struct tty *tp));
+int ttioctl __P((struct tty *tp, int com, void *data, int flag));
+int ttread __P((struct tty *tp, struct uio *uio, int flag));
+void ttrstrt __P((void *tp));
+int ttselect __P((dev_t device, int rw, struct proc *p));
+void ttsetwater __P((struct tty *tp));
+int ttspeedtab __P((int speed, struct speedtab *table));
+int ttstart __P((struct tty *tp));
+void ttwakeup __P((struct tty *tp));
+int ttwrite __P((struct tty *tp, struct uio *uio, int flag));
+void ttychars __P((struct tty *tp));
+int ttycheckoutq __P((struct tty *tp, int wait));
+int ttyclose __P((struct tty *tp));
+void ttyflush __P((struct tty *tp, int rw));
+void ttyinfo __P((struct tty *tp));
+int ttyinput __P((int c, struct tty *tp));
+int ttylclose __P((struct tty *tp, int flag));
+int ttymodem __P((struct tty *tp, int flag));
+int ttyopen __P((dev_t device, struct tty *tp));
+int ttyoutput __P((int c, struct tty *tp));
+void ttypend __P((struct tty *tp));
+void ttyretype __P((struct tty *tp));
+void ttyrub __P((int c, struct tty *tp));
+int ttysleep __P((struct tty *tp,
+ void *chan, int pri, char *wmesg, int timeout));
+int ttywait __P((struct tty *tp));
+int ttywflush __P((struct tty *tp));
+#endif
diff --git a/sys/sys/ttychars.h b/sys/sys/ttychars.h
new file mode 100644
index 0000000..1a23aa7
--- /dev/null
+++ b/sys/sys/ttychars.h
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 1982, 1986, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ttychars.h 8.2 (Berkeley) 1/4/94
+ */
+
+/*
+ * 4.3 COMPATIBILITY FILE
+ *
+ * User visible structures and constants related to terminal handling.
+ */
+#ifndef _SYS_TTYCHARS_H_
+#define _SYS_TTYCHARS_H_
+
+struct ttychars {
+ char tc_erase; /* erase last character */
+ char tc_kill; /* erase entire line */
+ char tc_intrc; /* interrupt */
+ char tc_quitc; /* quit */
+ char tc_startc; /* start output */
+ char tc_stopc; /* stop output */
+ char tc_eofc; /* end-of-file */
+ char tc_brkc; /* input delimiter (like nl) */
+ char tc_suspc; /* stop process signal */
+ char tc_dsuspc; /* delayed stop process signal */
+ char tc_rprntc; /* reprint line */
+ char tc_flushc; /* flush output (toggles) */
+ char tc_werasc; /* word erase */
+ char tc_lnextc; /* literal next character */
+};
+#ifdef USE_OLD_TTY
+#include <sys/ttydefaults.h> /* to pick up character defaults */
+#endif
+#endif /* !_SYS_TTYCHARS_H_ */
diff --git a/sys/sys/ttycom.h b/sys/sys/ttycom.h
new file mode 100644
index 0000000..3dade47
--- /dev/null
+++ b/sys/sys/ttycom.h
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 1982, 1986, 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ttycom.h 8.1 (Berkeley) 3/28/94
+ */
+
+#ifndef _SYS_TTYCOM_H_
+#define _SYS_TTYCOM_H_
+
+#include <sys/ioccom.h>
+
+/*
+ * Tty ioctl's except for those supported only for backwards compatibility
+ * with the old tty driver.
+ */
+
+/*
+ * Window/terminal size structure. This information is stored by the kernel
+ * in order to provide a consistent interface, but is not used by the kernel.
+ */
+struct winsize {
+ unsigned short ws_row; /* rows, in characters */
+ unsigned short ws_col; /* columns, in characters */
+ unsigned short ws_xpixel; /* horizontal size, pixels */
+ unsigned short ws_ypixel; /* vertical size, pixels */
+};
+
+#define TIOCMODG _IOR('t', 3, int) /* get modem control state */
+#define TIOCMODS _IOW('t', 4, int) /* set modem control state */
+#define TIOCM_LE 0001 /* line enable */
+#define TIOCM_DTR 0002 /* data terminal ready */
+#define TIOCM_RTS 0004 /* request to send */
+#define TIOCM_ST 0010 /* secondary transmit */
+#define TIOCM_SR 0020 /* secondary receive */
+#define TIOCM_CTS 0040 /* clear to send */
+#define TIOCM_CAR 0100 /* carrier detect */
+#define TIOCM_CD TIOCM_CAR
+#define TIOCM_RNG 0200 /* ring */
+#define TIOCM_RI TIOCM_RNG
+#define TIOCM_DSR 0400 /* data set ready */
+ /* 8-10 compat */
+#define TIOCEXCL _IO('t', 13) /* set exclusive use of tty */
+#define TIOCNXCL _IO('t', 14) /* reset exclusive use of tty */
+ /* 15 unused */
+#define TIOCFLUSH _IOW('t', 16, int) /* flush buffers */
+ /* 17-18 compat */
+#define TIOCGETA _IOR('t', 19, struct termios) /* get termios struct */
+#define TIOCSETA _IOW('t', 20, struct termios) /* set termios struct */
+#define TIOCSETAW _IOW('t', 21, struct termios) /* drain output, set */
+#define TIOCSETAF _IOW('t', 22, struct termios) /* drn out, fls in, set */
+#define TIOCGETD _IOR('t', 26, int) /* get line discipline */
+#define TIOCSETD _IOW('t', 27, int) /* set line discipline */
+ /* 127-124 compat */
+#define TIOCSBRK _IO('t', 123) /* set break bit */
+#define TIOCCBRK _IO('t', 122) /* clear break bit */
+#define TIOCSDTR _IO('t', 121) /* set data terminal ready */
+#define TIOCCDTR _IO('t', 120) /* clear data terminal ready */
+#define TIOCGPGRP _IOR('t', 119, int) /* get pgrp of tty */
+#define TIOCSPGRP _IOW('t', 118, int) /* set pgrp of tty */
+ /* 117-116 compat */
+#define TIOCOUTQ _IOR('t', 115, int) /* output queue size */
+#define TIOCSTI _IOW('t', 114, char) /* simulate terminal input */
+#define TIOCNOTTY _IO('t', 113) /* void tty association */
+#define TIOCPKT _IOW('t', 112, int) /* pty: set/clear packet mode */
+#define TIOCPKT_DATA 0x00 /* data packet */
+#define TIOCPKT_FLUSHREAD 0x01 /* flush packet */
+#define TIOCPKT_FLUSHWRITE 0x02 /* flush packet */
+#define TIOCPKT_STOP 0x04 /* stop output */
+#define TIOCPKT_START 0x08 /* start output */
+#define TIOCPKT_NOSTOP 0x10 /* no more ^S, ^Q */
+#define TIOCPKT_DOSTOP 0x20 /* now do ^S ^Q */
+#define TIOCPKT_IOCTL 0x40 /* state change of pty driver */
+#define TIOCSTOP _IO('t', 111) /* stop output, like ^S */
+#define TIOCSTART _IO('t', 110) /* start output, like ^Q */
+#define TIOCMSET _IOW('t', 109, int) /* set all modem bits */
+#define TIOCMBIS _IOW('t', 108, int) /* bis modem bits */
+#define TIOCMBIC _IOW('t', 107, int) /* bic modem bits */
+#define TIOCMGET _IOR('t', 106, int) /* get all modem bits */
+#define TIOCREMOTE _IOW('t', 105, int) /* remote input editing */
+#define TIOCGWINSZ _IOR('t', 104, struct winsize) /* get window size */
+#define TIOCSWINSZ _IOW('t', 103, struct winsize) /* set window size */
+#define TIOCUCNTL _IOW('t', 102, int) /* pty: set/clr usr cntl mode */
+#define UIOCCMD(n) _IO('u', n) /* usr cntl op "n" */
+#define TIOCCONS _IOW('t', 98, int) /* become virtual console */
+#define TIOCSCTTY _IO('t', 97) /* become controlling tty */
+#define TIOCEXT _IOW('t', 96, int) /* pty: external processing */
+#define TIOCSIG _IO('t', 95) /* pty: generate signal */
+#define TIOCDRAIN _IO('t', 94) /* wait till output drained */
+#define TIOCMSBIDIR _IOW('t', 93, int) /* modem: set bidir cap. */
+#define TIOCMGBIDIR _IOR('t', 92, int) /* modem: get bidir cap. */
+#define TIOCMSDTRWAIT _IOW('t', 91, int) /* modem: set wait on close */
+#define TIOCMGDTRWAIT _IOR('t', 90, int) /* modem: get wait on close */
+#define TIOCTIMESTAMP _IOR('t', 89, struct timeval) /* get timestamp of
+ last interrupt for xntp. */
+
+#define TTYDISC 0 /* termios tty line discipline */
+#define TABLDISC 3 /* tablet discipline */
+#define SLIPDISC 4 /* serial IP discipline */
+#define PPPDISC 5 /* PPP discipline */
+
+
+#endif /* !_SYS_TTYCOM_H_ */
diff --git a/sys/sys/ttydefaults.h b/sys/sys/ttydefaults.h
new file mode 100644
index 0000000..1a8aaa5
--- /dev/null
+++ b/sys/sys/ttydefaults.h
@@ -0,0 +1,96 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ttydefaults.h 8.4 (Berkeley) 1/21/94
+ */
+
+/*
+ * System wide defaults for terminal state.
+ */
+#ifndef _SYS_TTYDEFAULTS_H_
+#define _SYS_TTYDEFAULTS_H_
+
+/*
+ * Defaults on "first" open.
+ */
+#define TTYDEF_IFLAG (BRKINT | ISTRIP | ICRNL | IMAXBEL | IXON | IXANY)
+#define TTYDEF_OFLAG (OPOST | ONLCR | OXTABS)
+#define TTYDEF_LFLAG (ECHO | ICANON | ISIG | IEXTEN | ECHOE|ECHOKE|ECHOCTL)
+#define TTYDEF_CFLAG (CREAD | CS7 | PARENB | HUPCL)
+#define TTYDEF_SPEED (B9600)
+
+/*
+ * Control Character Defaults
+ */
+#define CTRL(x) (x&037)
+#define CEOF CTRL('d')
+#define CEOL ((unsigned char)'\377') /* XXX avoid _POSIX_VDISABLE */
+#define CERASE 0177
+#define CINTR CTRL('c')
+#define CSTATUS ((unsigned char)'\377') /* XXX avoid _POSIX_VDISABLE */
+#define CKILL CTRL('u')
+#define CMIN 1
+#define CQUIT 034 /* FS, ^\ */
+#define CSUSP CTRL('z')
+#define CTIME 0
+#define CDSUSP CTRL('y')
+#define CSTART CTRL('q')
+#define CSTOP CTRL('s')
+#define CLNEXT CTRL('v')
+#define CDISCARD CTRL('o')
+#define CWERASE CTRL('w')
+#define CREPRINT CTRL('r')
+#define CEOT CEOF
+/* compat */
+#define CBRK CEOL
+#define CRPRNT CREPRINT
+#define CFLUSH CDISCARD
+
+/* PROTECTED INCLUSION ENDS HERE */
+#endif /* !_SYS_TTYDEFAULTS_H_ */
+
+/*
+ * #define TTYDEFCHARS to include an array of default control characters.
+ */
+#ifdef TTYDEFCHARS
+cc_t ttydefchars[NCCS] = {
+ CEOF, CEOL, CEOL, CERASE, CWERASE, CKILL, CREPRINT,
+ _POSIX_VDISABLE, CINTR, CQUIT, CSUSP, CDSUSP, CSTART, CSTOP, CLNEXT,
+ CDISCARD, CMIN, CTIME, CSTATUS, _POSIX_VDISABLE
+};
+#undef TTYDEFCHARS
+#endif
diff --git a/sys/sys/ttydev.h b/sys/sys/ttydev.h
new file mode 100644
index 0000000..c52a213
--- /dev/null
+++ b/sys/sys/ttydev.h
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 1982, 1986, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ttydev.h 8.2 (Berkeley) 1/4/94
+ */
+
+/* COMPATABILITY HEADER FILE */
+
+#ifndef _SYS_TTYDEV_H_
+#define _SYS_TTYDEV_H_
+
+#ifdef USE_OLD_TTY
+#define B0 0
+#define B50 1
+#define B75 2
+#define B110 3
+#define B134 4
+#define B150 5
+#define B200 6
+#define B300 7
+#define B600 8
+#define B1200 9
+#define B1800 10
+#define B2400 11
+#define B4800 12
+#define B9600 13
+#define EXTA 14
+#define EXTB 15
+#endif /* USE_OLD_TTY */
+
+#endif /* !_SYS_TTYDEV_H_ */
diff --git a/sys/sys/types.h b/sys/sys/types.h
new file mode 100644
index 0000000..76d2975
--- /dev/null
+++ b/sys/sys/types.h
@@ -0,0 +1,162 @@
+/*-
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)types.h 8.4 (Berkeley) 1/21/94
+ */
+
+#ifndef _SYS_TYPES_H_
+#define _SYS_TYPES_H_
+
+/* Machine type dependent parameters. */
+#include <machine/endian.h>
+
+#ifndef _POSIX_SOURCE
+typedef unsigned char u_char;
+typedef unsigned short u_short;
+typedef unsigned int u_int;
+typedef unsigned long u_long;
+typedef unsigned short ushort; /* Sys V compatibility */
+typedef unsigned int uint; /* Sys V compatibility */
+#endif
+
+typedef unsigned long long u_quad_t; /* quads */
+typedef long long quad_t;
+typedef quad_t * qaddr_t;
+
+typedef char * caddr_t; /* core address */
+typedef long daddr_t; /* disk address */
+typedef unsigned long dev_t; /* device number */
+typedef unsigned long fixpt_t; /* fixed point number */
+typedef unsigned long gid_t; /* group id */
+typedef unsigned long ino_t; /* inode number */
+typedef unsigned short mode_t; /* permissions */
+typedef unsigned short nlink_t; /* link count */
+typedef quad_t off_t; /* file offset */
+typedef long pid_t; /* process id */
+typedef long segsz_t; /* segment size */
+typedef long swblk_t; /* swap offset */
+typedef unsigned long uid_t; /* user id */
+
+/*
+ * This belongs in unistd.h, but is placed here to ensure that programs
+ * casting the second parameter of lseek to off_t will get the correct
+ * version of lseek.
+ */
+#ifndef KERNEL
+#include <sys/cdefs.h>
+__BEGIN_DECLS
+off_t lseek __P((int, off_t, int));
+__END_DECLS
+#endif
+
+#ifndef _POSIX_SOURCE
+#define major(x) ((int)(((u_int)(x) >> 8)&0xff)) /* major number */
+#define minor(x) ((int)((x)&0xff)) /* minor number */
+#define makedev(x,y) ((dev_t)(((x)<<8) | (y))) /* create dev_t */
+#endif
+
+#include <machine/ansi.h>
+#include <machine/types.h>
+
+#ifdef _BSD_CLOCK_T_
+typedef _BSD_CLOCK_T_ clock_t;
+#undef _BSD_CLOCK_T_
+#endif
+
+#ifdef _BSD_SIZE_T_
+typedef _BSD_SIZE_T_ size_t;
+#undef _BSD_SIZE_T_
+#endif
+
+#ifdef _BSD_SSIZE_T_
+typedef _BSD_SSIZE_T_ ssize_t;
+#undef _BSD_SSIZE_T_
+#endif
+
+#ifdef _BSD_TIME_T_
+typedef _BSD_TIME_T_ time_t;
+#undef _BSD_TIME_T_
+#endif
+
+#ifndef _POSIX_SOURCE
+#define NBBY 8 /* number of bits in a byte */
+
+/*
+ * Select uses bit masks of file descriptors in longs. These macros
+ * manipulate such bit fields (the filesystem macros use chars).
+ * FD_SETSIZE may be defined by the user, but the default here should
+ * be enough for most uses.
+ */
+#ifndef FD_SETSIZE
+#define FD_SETSIZE 256
+#endif
+
+typedef long fd_mask;
+#define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */
+
+#ifndef howmany
+#define howmany(x, y) (((x)+((y)-1))/(y))
+#endif
+
+typedef struct fd_set {
+ fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)];
+} fd_set;
+
+#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
+#define FD_COPY(f, t) bcopy(f, t, sizeof(*(f)))
+#define FD_ZERO(p) bzero(p, sizeof(*(p)))
+
+#if defined(__STDC__) && defined(KERNEL)
+/*
+ * Forward structure declarations for function prototypes. We include the
+ * common structures that cross subsystem boundaries here; others are mostly
+ * used in the same place that the structure is defined.
+ */
+struct proc;
+struct pgrp;
+struct ucred;
+struct rusage;
+struct file;
+struct buf;
+struct tty;
+struct uio;
+#endif
+
+#endif /* !_POSIX_SOURCE */
+#endif /* !_SYS_TYPES_H_ */
diff --git a/sys/sys/ucred.h b/sys/sys/ucred.h
new file mode 100644
index 0000000..d3ee02d
--- /dev/null
+++ b/sys/sys/ucred.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ucred.h 8.2 (Berkeley) 1/4/94
+ */
+
+#ifndef _SYS_UCRED_H_
+#define _SYS_UCRED_H_
+
+/*
+ * Credentials.
+ */
+struct ucred {
+ u_short cr_ref; /* reference count */
+ uid_t cr_uid; /* effective user id */
+ short cr_ngroups; /* number of groups */
+ gid_t cr_groups[NGROUPS]; /* groups */
+};
+#define cr_gid cr_groups[0]
+#define NOCRED ((struct ucred *)-1) /* no credential available */
+#define FSCRED ((struct ucred *)-2) /* filesystem credential */
+
+#ifdef KERNEL
+#define crhold(cr) (cr)->cr_ref++
+struct ucred *crget();
+struct ucred *crcopy();
+struct ucred *crdup();
+#endif /* KERNEL */
+
+#endif /* !_SYS_UCRED_H_ */
diff --git a/sys/sys/uio.h b/sys/sys/uio.h
new file mode 100644
index 0000000..3356ebf
--- /dev/null
+++ b/sys/sys/uio.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1982, 1986, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)uio.h 8.5 (Berkeley) 2/22/94
+ */
+
+#ifndef _SYS_UIO_H_
+#define _SYS_UIO_H_
+
+/*
+ * XXX
+ * iov_base should be a void *.
+ */
+struct iovec {
+ char *iov_base; /* Base address. */
+ size_t iov_len; /* Length. */
+};
+
+enum uio_rw { UIO_READ, UIO_WRITE };
+
+/* Segment flag values. */
+enum uio_seg {
+ UIO_USERSPACE, /* from user data space */
+ UIO_SYSSPACE, /* from system space */
+ UIO_USERISPACE /* from user I space */
+};
+
+#ifdef KERNEL
+struct uio {
+ struct iovec *uio_iov;
+ int uio_iovcnt;
+ off_t uio_offset;
+ int uio_resid;
+ enum uio_seg uio_segflg;
+ enum uio_rw uio_rw;
+ struct proc *uio_procp;
+};
+
+/*
+ * Limits
+ */
+#define UIO_MAXIOV 1024 /* max 1K of iov's */
+#define UIO_SMALLIOV 8 /* 8 on stack, else malloc */
+#endif /* KERNEL */
+
+#ifndef KERNEL
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+ssize_t readv __P((int, const struct iovec *, int));
+ssize_t writev __P((int, const struct iovec *, int));
+__END_DECLS
+#endif /* !KERNEL */
+#endif /* !_SYS_UIO_H_ */
diff --git a/sys/sys/un.h b/sys/sys/un.h
new file mode 100644
index 0000000..dcbf800
--- /dev/null
+++ b/sys/sys/un.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)un.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * Definitions for UNIX IPC domain.
+ */
+struct sockaddr_un {
+ u_char sun_len; /* sockaddr len including null */
+ u_char sun_family; /* AF_UNIX */
+ char sun_path[104]; /* path name (gag) */
+};
+
+#ifndef KERNEL
+/* actual length of an initialized sockaddr_un */
+#define SUN_LEN(su) \
+ (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
+#endif
diff --git a/sys/sys/unistd.h b/sys/sys/unistd.h
new file mode 100644
index 0000000..e086f6f
--- /dev/null
+++ b/sys/sys/unistd.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)unistd.h 8.2 (Berkeley) 1/7/94
+ */
+
+#ifndef _SYS_UNISTD_H_
+#define _SYS_UNISTD_H_
+
+/* compile-time symbolic constants */
+#define _POSIX_JOB_CONTROL /* implementation supports job control */
+
+/*
+ * Although we have saved user/group IDs, we do not use them in setuid
+ * as described in POSIX 1003.1, because the feature does not work for
+ * root. We use the saved IDs in seteuid/setegid, which are not currently
+ * part of the POSIX 1003.1 specification.
+ */
+#ifdef _NOT_AVAILABLE
+#define _POSIX_SAVED_IDS /* saved set-user-ID and set-group-ID */
+#endif
+
+#define _POSIX_VERSION 198808L
+#define _POSIX2_VERSION 199212L
+
+/* execution-time symbolic constants */
+ /* chown requires appropriate privileges */
+#define _POSIX_CHOWN_RESTRICTED 1
+ /* too-long path components generate errors */
+#define _POSIX_NO_TRUNC 1
+ /* may disable terminal special characters */
+#define _POSIX_VDISABLE ((unsigned char)'\377')
+
+/* access function */
+#define F_OK 0 /* test for existence of file */
+#define X_OK 0x01 /* test for execute or search permission */
+#define W_OK 0x02 /* test for write permission */
+#define R_OK 0x04 /* test for read permission */
+
+/* whence values for lseek(2) */
+#define SEEK_SET 0 /* set file offset to offset */
+#define SEEK_CUR 1 /* set file offset to current plus offset */
+#define SEEK_END 2 /* set file offset to EOF plus offset */
+
+#ifndef _POSIX_SOURCE
+/* whence values for lseek(2); renamed by POSIX 1003.1 */
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+#endif
+
+/* configurable pathname variables */
+#define _PC_LINK_MAX 1
+#define _PC_MAX_CANON 2
+#define _PC_MAX_INPUT 3
+#define _PC_NAME_MAX 4
+#define _PC_PATH_MAX 5
+#define _PC_PIPE_BUF 6
+#define _PC_CHOWN_RESTRICTED 7
+#define _PC_NO_TRUNC 8
+#define _PC_VDISABLE 9
+
+/* configurable system variables */
+#define _SC_ARG_MAX 1
+#define _SC_CHILD_MAX 2
+#define _SC_CLK_TCK 3
+#define _SC_NGROUPS_MAX 4
+#define _SC_OPEN_MAX 5
+#define _SC_JOB_CONTROL 6
+#define _SC_SAVED_IDS 7
+#define _SC_VERSION 8
+#define _SC_BC_BASE_MAX 9
+#define _SC_BC_DIM_MAX 10
+#define _SC_BC_SCALE_MAX 11
+#define _SC_BC_STRING_MAX 12
+#define _SC_COLL_WEIGHTS_MAX 13
+#define _SC_EXPR_NEST_MAX 14
+#define _SC_LINE_MAX 15
+#define _SC_RE_DUP_MAX 16
+#define _SC_2_VERSION 17
+#define _SC_2_C_BIND 18
+#define _SC_2_C_DEV 19
+#define _SC_2_CHAR_TERM 20
+#define _SC_2_FORT_DEV 21
+#define _SC_2_FORT_RUN 22
+#define _SC_2_LOCALEDEF 23
+#define _SC_2_SW_DEV 24
+#define _SC_2_UPE 25
+#define _SC_STREAM_MAX 26
+#define _SC_TZNAME_MAX 27
+
+/* configurable system strings */
+#define _CS_PATH 1
+
+#endif /* !_SYS_UNISTD_H_ */
diff --git a/sys/sys/unpcb.h b/sys/sys/unpcb.h
new file mode 100644
index 0000000..efcfd0e
--- /dev/null
+++ b/sys/sys/unpcb.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)unpcb.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * Protocol control block for an active
+ * instance of a UNIX internal protocol.
+ *
+ * A socket may be associated with an vnode in the
+ * file system. If so, the unp_vnode pointer holds
+ * a reference count to this vnode, which should be irele'd
+ * when the socket goes away.
+ *
+ * A socket may be connected to another socket, in which
+ * case the control block of the socket to which it is connected
+ * is given by unp_conn.
+ *
+ * A socket may be referenced by a number of sockets (e.g. several
+ * sockets may be connected to a datagram socket.) These sockets
+ * are in a linked list starting with unp_refs, linked through
+ * unp_nextref and null-terminated. Note that a socket may be referenced
+ * by a number of other sockets and may also reference a socket (not
+ * necessarily one which is referencing it). This generates
+ * the need for unp_refs and unp_nextref to be separate fields.
+ *
+ * Stream sockets keep copies of receive sockbuf sb_cc and sb_mbcnt
+ * so that changes in the sockbuf may be computed to modify
+ * back pressure on the sender accordingly.
+ */
+struct unpcb {
+ struct socket *unp_socket; /* pointer back to socket */
+ struct vnode *unp_vnode; /* if associated with file */
+ ino_t unp_ino; /* fake inode number */
+ struct unpcb *unp_conn; /* control block of connected socket */
+ struct unpcb *unp_refs; /* referencing socket linked list */
+ struct unpcb *unp_nextref; /* link in unp_refs list */
+ struct mbuf *unp_addr; /* bound address of socket */
+ int unp_cc; /* copy of rcv.sb_cc */
+ int unp_mbcnt; /* copy of rcv.sb_mbcnt */
+};
+
+#define sotounpcb(so) ((struct unpcb *)((so)->so_pcb))
diff --git a/sys/sys/user.h b/sys/sys/user.h
new file mode 100644
index 0000000..85fdd13
--- /dev/null
+++ b/sys/sys/user.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)user.h 8.2 (Berkeley) 9/23/93
+ */
+
+#include <machine/pcb.h>
+#ifndef KERNEL
+/* stuff that *used* to be included by user.h, or is now needed */
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/ucred.h>
+#include <sys/uio.h>
+#endif
+#include <sys/resourcevar.h>
+#include <sys/signalvar.h>
+#include <vm/vm.h> /* XXX */
+#include <sys/sysctl.h>
+
+
+/*
+ * Per process structure containing data that isn't needed in core
+ * when the process isn't running (esp. when swapped out).
+ * This structure may or may not be at the same kernel address
+ * in all processes.
+ */
+
+struct user {
+ struct pcb u_pcb;
+
+ struct sigacts u_sigacts; /* p_sigacts points here (use it!) */
+ struct pstats u_stats; /* p_stats points here (use it!) */
+
+ /*
+ * Remaining fields only for core dump and/or ptrace--
+ * not valid at other times!
+ */
+ struct kinfo_proc u_kproc; /* proc + eproc */
+ struct md_coredump u_md; /* machine dependent glop */
+};
+
+/*
+ * Redefinitions to make the debuggers happy for now... This subterfuge
+ * brought to you by coredump() and trace_req(). These fields are *only*
+ * valid at those times!
+ */
+#define U_ar0 u_kproc.kp_proc.p_md.md_regs /* copy of curproc->p_md.md_regs */
+#define U_tsize u_kproc.kp_eproc.e_vm.vm_tsize
+#define U_dsize u_kproc.kp_eproc.e_vm.vm_dsize
+#define U_ssize u_kproc.kp_eproc.e_vm.vm_ssize
+#define U_sig u_sigacts.ps_sig
+#define U_code u_sigacts.ps_code
+
+#ifndef KERNEL
+#define u_ar0 U_ar0
+#define u_tsize U_tsize
+#define u_dsize U_dsize
+#define u_ssize U_ssize
+#define u_sig U_sig
+#define u_code U_code
+#endif /* KERNEL */
diff --git a/sys/sys/utsname.h b/sys/sys/utsname.h
new file mode 100644
index 0000000..60e6091
--- /dev/null
+++ b/sys/sys/utsname.h
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chuck Karish of Mindcraft, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)utsname.h 8.1 (Berkeley) 1/4/94
+ */
+
+#ifndef _SYS_UTSNAME_H
+#define _SYS_UTSNAME_H
+
+#define SYS_NMLN 32
+
+struct utsname {
+ char sysname[SYS_NMLN]; /* Name of this OS. */
+ char nodename[SYS_NMLN]; /* Name of this network node. */
+ char release[SYS_NMLN]; /* Release level. */
+ char version[SYS_NMLN]; /* Version level. */
+ char machine[SYS_NMLN]; /* Hardware type. */
+};
+
+
+#include <sys/cdefs.h>
+
+
+#ifndef KERNEL
+#ifdef __STDC__
+__BEGIN_DECLS
+int uname __P((struct utsname *));
+__END_DECLS
+#else
+extern int uname();
+#endif
+#else
+extern struct utsname utsname;
+#endif /* KERNEL */
+
+#endif /* !_SYS_UTSNAME_H */
diff --git a/sys/sys/vadvise.h b/sys/sys/vadvise.h
new file mode 100644
index 0000000..be793e8
--- /dev/null
+++ b/sys/sys/vadvise.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vadvise.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * Parameters to vadvise() to tell system of particular paging
+ * behaviour:
+ * VA_NORM Normal strategy
+ * VA_ANOM Sampling page behaviour is not a win, don't bother
+ * Suitable during GCs in LISP, or sequential or random
+ * page referencing.
+ * VA_SEQL Sequential behaviour expected.
+ * VA_FLUSH Invalidate all page table entries.
+ */
+#define VA_NORM 0
+#define VA_ANOM 1
+#define VA_SEQL 2
+#define VA_FLUSH 3
diff --git a/sys/sys/vcmd.h b/sys/sys/vcmd.h
new file mode 100644
index 0000000..de27ec1
--- /dev/null
+++ b/sys/sys/vcmd.h
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vcmd.h 8.1 (Berkeley) 6/2/93
+ */
+
+#include <sys/ioctl.h>
+
+#define VPRINT 0100
+#define VPLOT 0200
+#define VPRINTPLOT 0400
+
+#define VGETSTATE _IOR('v', 0, int)
+#define VSETSTATE _IOW('v', 1, int)
diff --git a/sys/sys/vlimit.h b/sys/sys/vlimit.h
new file mode 100644
index 0000000..b6457e6
--- /dev/null
+++ b/sys/sys/vlimit.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vlimit.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * Limits for u.u_limit[i], per process, inherited.
+ */
+#define LIM_NORAISE 0 /* if <> 0, can't raise limits */
+#define LIM_CPU 1 /* max secs cpu time */
+#define LIM_FSIZE 2 /* max size of file created */
+#define LIM_DATA 3 /* max growth of data space */
+#define LIM_STACK 4 /* max growth of stack */
+#define LIM_CORE 5 /* max size of ``core'' file */
+#define LIM_MAXRSS 6 /* max desired data+stack core usage */
+
+#define NLIMITS 6
+
+#define INFINITY 0x7fffffff
diff --git a/sys/sys/vmmeter.h b/sys/sys/vmmeter.h
new file mode 100644
index 0000000..ef890c2
--- /dev/null
+++ b/sys/sys/vmmeter.h
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vmmeter.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * System wide statistics counters.
+ */
+struct vmmeter {
+ /*
+ * General system activity.
+ */
+ unsigned v_swtch; /* context switches */
+ unsigned v_trap; /* calls to trap */
+ unsigned v_syscall; /* calls to syscall() */
+ unsigned v_intr; /* device interrupts */
+ unsigned v_soft; /* software interrupts */
+ unsigned v_faults; /* total faults taken */
+ /*
+ * Virtual memory activity.
+ */
+ unsigned v_lookups; /* object cache lookups */
+ unsigned v_hits; /* object cache hits */
+ unsigned v_vm_faults; /* number of address memory faults */
+ unsigned v_cow_faults; /* number of copy-on-writes */
+ unsigned v_swpin; /* swapins */
+ unsigned v_swpout; /* swapouts */
+ unsigned v_pswpin; /* pages swapped in */
+ unsigned v_pswpout; /* pages swapped out */
+ unsigned v_pageins; /* number of pageins */
+ unsigned v_pageouts; /* number of pageouts */
+ unsigned v_pgpgin; /* pages paged in */
+ unsigned v_pgpgout; /* pages paged out */
+ unsigned v_intrans; /* intransit blocking page faults */
+ unsigned v_reactivated; /* number of pages reactivated from free list */
+ unsigned v_rev; /* revolutions of the hand */
+ unsigned v_scan; /* scans in page out daemon */
+ unsigned v_dfree; /* pages freed by daemon */
+ unsigned v_pfree; /* pages freed by exiting processes */
+ unsigned v_zfod; /* pages zero filled on demand */
+ unsigned v_nzfod; /* number of zfod's created */
+ /*
+ * Distribution of page usages.
+ */
+ unsigned v_page_size; /* page size in bytes */
+ unsigned v_kernel_pages;/* number of pages in use by kernel */
+ unsigned v_page_count; /* total number of pages in system */
+ unsigned v_free_reserved; /* number of pages reserved for deadlock */
+ unsigned v_free_target; /* number of pages desired free */
+ unsigned v_free_min; /* minimum number of pages desired free */
+ unsigned v_free_count; /* number of pages free */
+ unsigned v_wire_count; /* number of pages wired down */
+ unsigned v_active_count;/* number of pages active */
+ unsigned v_inactive_target; /* number of pages desired inactive */
+ unsigned v_inactive_count; /* number of pages inactive */
+};
+#ifdef KERNEL
+struct vmmeter cnt;
+#endif
+
+/* systemwide totals computed every five seconds */
+struct vmtotal
+{
+ short t_rq; /* length of the run queue */
+ short t_dw; /* jobs in ``disk wait'' (neg priority) */
+ short t_pw; /* jobs in page wait */
+ short t_sl; /* jobs sleeping in core */
+ short t_sw; /* swapped out runnable/short block jobs */
+ long t_vm; /* total virtual memory */
+ long t_avm; /* active virtual memory */
+ long t_rm; /* total real memory in use */
+ long t_arm; /* active real memory */
+ long t_vmshr; /* shared virtual memory */
+ long t_avmshr; /* active shared virtual memory */
+ long t_rmshr; /* shared real memory */
+ long t_armshr; /* active shared real memory */
+ long t_free; /* free memory pages */
+};
+#ifdef KERNEL
+struct vmtotal total;
+#endif
+
+/*
+ * Optional instrumentation.
+ */
+#ifdef PGINPROF
+
+#define NDMON 128
+#define NSMON 128
+
+#define DRES 20
+#define SRES 5
+
+#define PMONMIN 20
+#define PRES 50
+#define NPMON 64
+
+#define RMONMIN 130
+#define RRES 5
+#define NRMON 64
+
+/* data and stack size distribution counters */
+unsigned int dmon[NDMON+1];
+unsigned int smon[NSMON+1];
+
+/* page in time distribution counters */
+unsigned int pmon[NPMON+2];
+
+/* reclaim time distribution counters */
+unsigned int rmon[NRMON+2];
+
+int pmonmin;
+int pres;
+int rmonmin;
+int rres;
+
+unsigned rectime; /* accumulator for reclaim times */
+unsigned pgintime; /* accumulator for page in times */
+#endif
diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h
new file mode 100644
index 0000000..fa51d99
--- /dev/null
+++ b/sys/sys/vnode.h
@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vnode.h 8.7 (Berkeley) 2/4/94
+ */
+
+#include <sys/queue.h>
+
+/*
+ * The vnode is the focus of all file activity in UNIX. There is a
+ * unique vnode allocated for each active file, each current directory,
+ * each mounted-on file, text file, and the root.
+ */
+
+/*
+ * Vnode types. VNON means no type.
+ */
+enum vtype { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VBAD };
+
+/*
+ * Vnode tag types.
+ * These are for the benefit of external programs only (e.g., pstat)
+ * and should NEVER be inspected by the kernel.
+ */
+enum vtagtype {
+ VT_NON, VT_UFS, VT_NFS, VT_MFS, VT_PC, VT_LFS, VT_LOFS, VT_FDESC,
+ VT_PORTAL, VT_NULL, VT_UMAP, VT_KERNFS, VT_PROCFS, VT_AFS, VT_ISOFS,
+ VT_UNION
+};
+
+/*
+ * Each underlying filesystem allocates its own private area and hangs
+ * it from v_data. If non-null, this area is freed in getnewvnode().
+ */
+LIST_HEAD(buflists, buf);
+
+struct vnode {
+ u_long v_flag; /* vnode flags (see below) */
+ short v_usecount; /* reference count of users */
+ short v_writecount; /* reference count of writers */
+ long v_holdcnt; /* page & buffer references */
+ daddr_t v_lastr; /* last read (read-ahead) */
+ u_long v_id; /* capability identifier */
+ struct mount *v_mount; /* ptr to vfs we are in */
+ int (**v_op)(); /* vnode operations vector */
+ TAILQ_ENTRY(vnode) v_freelist; /* vnode freelist */
+ LIST_ENTRY(vnode) v_mntvnodes; /* vnodes for mount point */
+ struct buflists v_cleanblkhd; /* clean blocklist head */
+ struct buflists v_dirtyblkhd; /* dirty blocklist head */
+ long v_numoutput; /* num of writes in progress */
+ enum vtype v_type; /* vnode type */
+ union {
+ struct mount *vu_mountedhere;/* ptr to mounted vfs (VDIR) */
+ struct socket *vu_socket; /* unix ipc (VSOCK) */
+ caddr_t vu_vmdata; /* private data for vm (VREG) */
+ struct specinfo *vu_specinfo; /* device (VCHR, VBLK) */
+ struct fifoinfo *vu_fifoinfo; /* fifo (VFIFO) */
+ } v_un;
+ struct nqlease *v_lease; /* Soft reference to lease */
+ daddr_t v_lastw; /* last write (write cluster) */
+ daddr_t v_cstart; /* start block of cluster */
+ daddr_t v_lasta; /* last allocation */
+ int v_clen; /* length of current cluster */
+ int v_ralen; /* Read-ahead length */
+ daddr_t v_maxra; /* last readahead block */
+ long v_spare[7]; /* round to 128 bytes */
+ enum vtagtype v_tag; /* type of underlying data */
+ void *v_data; /* private data for fs */
+};
+#define v_mountedhere v_un.vu_mountedhere
+#define v_socket v_un.vu_socket
+#define v_vmdata v_un.vu_vmdata
+#define v_specinfo v_un.vu_specinfo
+#define v_fifoinfo v_un.vu_fifoinfo
+
+/*
+ * Vnode flags.
+ */
+#define VROOT 0x0001 /* root of its file system */
+#define VTEXT 0x0002 /* vnode is a pure text prototype */
+#define VSYSTEM 0x0004 /* vnode being used by kernel */
+#define VXLOCK 0x0100 /* vnode is locked to change underlying type */
+#define VXWANT 0x0200 /* process is waiting for vnode */
+#define VBWAIT 0x0400 /* waiting for output to complete */
+#define VALIASED 0x0800 /* vnode has an alias */
+#define VDIROP 0x1000 /* LFS: vnode is involved in a directory op */
+
+/*
+ * Vnode attributes. A field value of VNOVAL represents a field whose value
+ * is unavailable (getattr) or which is not to be changed (setattr).
+ */
+struct vattr {
+ enum vtype va_type; /* vnode type (for create) */
+ u_short va_mode; /* files access mode and type */
+ short va_nlink; /* number of references to file */
+ uid_t va_uid; /* owner user id */
+ gid_t va_gid; /* owner group id */
+ long va_fsid; /* file system id (dev for now) */
+ long va_fileid; /* file id */
+ u_quad_t va_size; /* file size in bytes */
+ long va_blocksize; /* blocksize preferred for i/o */
+ struct timespec va_atime; /* time of last access */
+ struct timespec va_mtime; /* time of last modification */
+ struct timespec va_ctime; /* time file changed */
+ u_long va_gen; /* generation number of file */
+ u_long va_flags; /* flags defined for file */
+ dev_t va_rdev; /* device the special file represents */
+ u_quad_t va_bytes; /* bytes of disk space held by file */
+ u_quad_t va_filerev; /* file modification number */
+ u_int va_vaflags; /* operations flags, see below */
+ long va_spare; /* remain quad aligned */
+};
+
+/*
+ * Flags for va_cflags.
+ */
+#define VA_UTIMES_NULL 0x01 /* utimes argument was NULL */
+
+/*
+ * Flags for ioflag.
+ */
+#define IO_UNIT 0x01 /* do I/O as atomic unit */
+#define IO_APPEND 0x02 /* append write to end */
+#define IO_SYNC 0x04 /* do I/O synchronously */
+#define IO_NODELOCKED 0x08 /* underlying node already locked */
+#define IO_NDELAY 0x10 /* FNDELAY flag set in file table */
+
+/*
+ * Modes. Some values same as Ixxx entries from inode.h for now.
+ */
+#define VSUID 04000 /* set user id on execution */
+#define VSGID 02000 /* set group id on execution */
+#define VSVTX 01000 /* save swapped text even after use */
+#define VREAD 00400 /* read, write, execute permissions */
+#define VWRITE 00200
+#define VEXEC 00100
+
+/*
+ * Token indicating no attribute value yet assigned.
+ */
+#define VNOVAL (-1)
+
+#ifdef KERNEL
+/*
+ * Convert between vnode types and inode formats (since POSIX.1
+ * defines mode word of stat structure in terms of inode formats).
+ */
+extern enum vtype iftovt_tab[];
+extern int vttoif_tab[];
+#define IFTOVT(mode) (iftovt_tab[((mode) & S_IFMT) >> 12])
+#define VTTOIF(indx) (vttoif_tab[(int)(indx)])
+#define MAKEIMODE(indx, mode) (int)(VTTOIF(indx) | (mode))
+
+/*
+ * Flags to various vnode functions.
+ */
+#define SKIPSYSTEM 0x0001 /* vflush: skip vnodes marked VSYSTEM */
+#define FORCECLOSE 0x0002 /* vflush: force file closeure */
+#define WRITECLOSE 0x0004 /* vflush: only close writeable files */
+#define DOCLOSE 0x0008 /* vclean: close active files */
+#define V_SAVE 0x0001 /* vinvalbuf: sync file first */
+#define V_SAVEMETA 0x0002 /* vinvalbuf: leave indirect blocks */
+
+#ifdef DIAGNOSTIC
+#define HOLDRELE(vp) holdrele(vp)
+#define VATTR_NULL(vap) vattr_null(vap)
+#define VHOLD(vp) vhold(vp)
+#define VREF(vp) vref(vp)
+
+void holdrele __P((struct vnode *));
+void vattr_null __P((struct vattr *));
+void vhold __P((struct vnode *));
+void vref __P((struct vnode *));
+#else
+#define HOLDRELE(vp) (vp)->v_holdcnt-- /* decrease buf or page ref */
+#define VATTR_NULL(vap) (*(vap) = va_null) /* initialize a vattr */
+#define VHOLD(vp) (vp)->v_holdcnt++ /* increase buf or page ref */
+#define VREF(vp) (vp)->v_usecount++ /* increase reference */
+#endif
+
+#define NULLVP ((struct vnode *)NULL)
+
+/*
+ * Global vnode data.
+ */
+extern struct vnode *rootvnode; /* root (i.e. "/") vnode */
+extern int desiredvnodes; /* number of vnodes desired */
+extern struct vattr va_null; /* predefined null vattr structure */
+
+/*
+ * Macro/function to check for client cache inconsistency w.r.t. leasing.
+ */
+#define LEASE_READ 0x1 /* Check lease for readers */
+#define LEASE_WRITE 0x2 /* Check lease for modifiers */
+
+#ifdef NFS
+void lease_check __P((struct vnode *vp, struct proc *p,
+ struct ucred *ucred, int flag));
+void lease_updatetime __P((int deltat));
+#define LEASE_CHECK(vp, p, cred, flag) lease_check((vp), (p), (cred), (flag))
+#define LEASE_UPDATETIME(dt) lease_updatetime(dt)
+#else
+#define LEASE_CHECK(vp, p, cred, flag)
+#define LEASE_UPDATETIME(dt)
+#endif /* NFS */
+#endif /* KERNEL */
+
+
+/*
+ * Mods for exensibility.
+ */
+
+/*
+ * Flags for vdesc_flags:
+ */
+#define VDESC_MAX_VPS 16
+/* Low order 16 flag bits are reserved for willrele flags for vp arguments. */
+#define VDESC_VP0_WILLRELE 0x0001
+#define VDESC_VP1_WILLRELE 0x0002
+#define VDESC_VP2_WILLRELE 0x0004
+#define VDESC_VP3_WILLRELE 0x0008
+#define VDESC_NOMAP_VPP 0x0100
+#define VDESC_VPP_WILLRELE 0x0200
+
+/*
+ * VDESC_NO_OFFSET is used to identify the end of the offset list
+ * and in places where no such field exists.
+ */
+#define VDESC_NO_OFFSET -1
+
+/*
+ * This structure describes the vnode operation taking place.
+ */
+struct vnodeop_desc {
+ int vdesc_offset; /* offset in vector--first for speed */
+ char *vdesc_name; /* a readable name for debugging */
+ int vdesc_flags; /* VDESC_* flags */
+
+ /*
+ * These ops are used by bypass routines to map and locate arguments.
+ * Creds and procs are not needed in bypass routines, but sometimes
+ * they are useful to (for example) transport layers.
+ * Nameidata is useful because it has a cred in it.
+ */
+ int *vdesc_vp_offsets; /* list ended by VDESC_NO_OFFSET */
+ int vdesc_vpp_offset; /* return vpp location */
+ int vdesc_cred_offset; /* cred location, if any */
+ int vdesc_proc_offset; /* proc location, if any */
+ int vdesc_componentname_offset; /* if any */
+ /*
+ * Finally, we've got a list of private data (about each operation)
+ * for each transport layer. (Support to manage this list is not
+ * yet part of BSD.)
+ */
+ caddr_t *vdesc_transports;
+};
+
+#ifdef KERNEL
+/*
+ * A list of all the operation descs.
+ */
+extern struct vnodeop_desc *vnodeop_descs[];
+
+
+/*
+ * This macro is very helpful in defining those offsets in the vdesc struct.
+ *
+ * This is stolen from X11R4. I ingored all the fancy stuff for
+ * Crays, so if you decide to port this to such a serious machine,
+ * you might want to consult Intrisics.h's XtOffset{,Of,To}.
+ */
+#define VOPARG_OFFSET(p_type,field) \
+ ((int) (((char *) (&(((p_type)NULL)->field))) - ((char *) NULL)))
+#define VOPARG_OFFSETOF(s_type,field) \
+ VOPARG_OFFSET(s_type*,field)
+#define VOPARG_OFFSETTO(S_TYPE,S_OFFSET,STRUCT_P) \
+ ((S_TYPE)(((char*)(STRUCT_P))+(S_OFFSET)))
+
+
+/*
+ * This structure is used to configure the new vnodeops vector.
+ */
+struct vnodeopv_entry_desc {
+ struct vnodeop_desc *opve_op; /* which operation this is */
+ int (*opve_impl)(); /* code implementing this operation */
+};
+struct vnodeopv_desc {
+ /* ptr to the ptr to the vector where op should go */
+ int (***opv_desc_vector_p)();
+ struct vnodeopv_entry_desc *opv_desc_ops; /* null terminated list */
+};
+
+/*
+ * A default routine which just returns an error.
+ */
+int vn_default_error __P((void));
+
+/*
+ * A generic structure.
+ * This can be used by bypass routines to identify generic arguments.
+ */
+struct vop_generic_args {
+ struct vnodeop_desc *a_desc;
+ /* other random data follows, presumably */
+};
+
+/*
+ * VOCALL calls an op given an ops vector. We break it out because BSD's
+ * vclean changes the ops vector and then wants to call ops with the old
+ * vector.
+ */
+#define VOCALL(OPSV,OFF,AP) (( *((OPSV)[(OFF)])) (AP))
+
+/*
+ * This call works for vnodes in the kernel.
+ */
+#define VCALL(VP,OFF,AP) VOCALL((VP)->v_op,(OFF),(AP))
+#define VDESC(OP) (& __CONCAT(OP,_desc))
+#define VOFFSET(OP) (VDESC(OP)->vdesc_offset)
+
+/*
+ * Finally, include the default set of vnode operations.
+ */
+#include <vnode_if.h>
+
+/*
+ * Public vnode manipulation functions.
+ */
+struct file;
+struct mount;
+struct nameidata;
+struct proc;
+struct stat;
+struct ucred;
+struct uio;
+struct vattr;
+struct vnode;
+struct vop_bwrite_args;
+
+int bdevvp __P((dev_t dev, struct vnode **vpp));
+int getnewvnode __P((enum vtagtype tag,
+ struct mount *mp, int (**vops)(), struct vnode **vpp));
+int vinvalbuf __P((struct vnode *vp, int save, struct ucred *cred,
+ struct proc *p, int slpflag, int slptimeo));
+void vattr_null __P((struct vattr *vap));
+int vcount __P((struct vnode *vp));
+int vget __P((struct vnode *vp, int lockflag));
+void vgone __P((struct vnode *vp));
+void vgoneall __P((struct vnode *vp));
+int vn_bwrite __P((struct vop_bwrite_args *ap));
+int vn_close __P((struct vnode *vp,
+ int flags, struct ucred *cred, struct proc *p));
+int vn_closefile __P((struct file *fp, struct proc *p));
+int vn_ioctl __P((struct file *fp, int com, caddr_t data, struct proc *p));
+int vn_open __P((struct nameidata *ndp, int fmode, int cmode));
+int vn_rdwr __P((enum uio_rw rw, struct vnode *vp, caddr_t base,
+ int len, off_t offset, enum uio_seg segflg, int ioflg,
+ struct ucred *cred, int *aresid, struct proc *p));
+int vn_read __P((struct file *fp, struct uio *uio, struct ucred *cred));
+int vn_select __P((struct file *fp, int which, struct proc *p));
+int vn_stat __P((struct vnode *vp, struct stat *sb, struct proc *p));
+int vn_write __P((struct file *fp, struct uio *uio, struct ucred *cred));
+struct vnode *
+ checkalias __P((struct vnode *vp, dev_t nvp_rdev, struct mount *mp));
+void vput __P((struct vnode *vp));
+void vref __P((struct vnode *vp));
+void vrele __P((struct vnode *vp));
+#endif /* KERNEL */
diff --git a/sys/sys/vsio.h b/sys/sys/vsio.h
new file mode 100644
index 0000000..d84218c
--- /dev/null
+++ b/sys/sys/vsio.h
@@ -0,0 +1,153 @@
+/*-
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vsio.h 8.1 (Berkeley) 6/2/93
+ */
+
+ /****************************************************************************
+ * *
+ * Copyright (c) 1983, 1984 by *
+ * DIGITAL EQUIPMENT CORPORATION, Maynard, Massachusetts. *
+ * All rights reserved. *
+ * *
+ * This software is furnished on an as-is basis and may be used and copied *
+ * only with inclusion of the above copyright notice. This software or any *
+ * other copies thereof may be provided or otherwise made available to *
+ * others only for non-commercial purposes. No title to or ownership of *
+ * the software is hereby transferred. *
+ * *
+ * The information in this software is subject to change without notice *
+ * and should not be construed as a commitment by DIGITAL EQUIPMENT *
+ * CORPORATION. *
+ * *
+ * DIGITAL assumes no responsibility for the use or reliability of its *
+ * software on equipment which is not supplied by DIGITAL. *
+ * *
+ * *
+ ****************************************************************************/
+/*
+ * vsio.h - VS100 I/O command definitions
+ *
+ * Author: Christopher A. Kent
+ * Digital Equipment Corporation
+ * Western Research Lab
+ * Date: Tue Jun 21 1983
+ */
+
+/*
+ * Possible ioctl calls
+ */
+
+#define VSIOINIT _IO('V', 0) /* init the device */
+#define VSIOSTART _IOW('V', 1, int) /* start microcode */
+#define VSIOABORT _IO('V', 2) /* abort a command chain */
+#define VSIOPWRUP _IO('V', 3) /* power-up reset */
+#define VSIOGETVER _IOR('V', 4, int) /* get rom version */
+#define VSIOSYNC _IO('V', 6) /* synch with device */
+#define VSIOBBACTL _IOW('V', 8, int) /* control the BBA */
+#define VSIOFIBCTL _IOW('V', 9, int) /* lamp on/off */
+#define VSIOFIBRETRY _IOW('V',10, int) /* fiber retries */
+#define VSIOGETSTATS _IOR('V',11, vsStats) /* get statistics */
+#define VSIOGETIOA _IOR('V',13, vsIoAddrAddr)/* get ioreg address */
+#define VSIOUSERWAIT _IO('V', 15) /* wait for user I/O completion */
+#define VSIOWAITGO _IOW('V', 16, caddr_t) /* wait then go */
+
+
+#define VSIO_OFF 0 /* option off */
+#define VSIO_ON 1 /* option on */
+
+#define VS_FIB_FINITE 1 /* finite retries */
+#define VS_FIB_INFINITE 2 /* infinite retries */
+
+/*
+ * Event queue entries
+ */
+
+typedef struct _vs_event{
+ u_short vse_x; /* x position */
+ u_short vse_y; /* y position */
+ u_short vse_time; /* 10 millisecond units (button only) */
+ char vse_type; /* button or motion? */
+ u_char vse_key; /* the key (button only) */
+ char vse_direction; /* which direction (button only) */
+ char vse_device; /* which device (button only) */
+}vsEvent;
+
+#define VSE_BUTTON 0 /* button moved */
+#define VSE_MMOTION 1 /* mouse moved */
+#define VSE_TMOTION 2 /* tablet moved */
+
+#define VSE_KBTUP 0 /* up */
+#define VSE_KBTDOWN 1 /* down */
+
+#define VSE_MOUSE 1 /* mouse */
+#define VSE_DKB 2 /* main keyboard */
+#define VSE_TABLET 3 /* graphics tablet */
+#define VSE_AUX 4 /* auxiliary */
+#define VSE_CONSOLE 5 /* console */
+
+typedef struct _vsStats{
+ int errors; /* count errors */
+ int unsolIntr; /* count unsolicited interrupts */
+ int overruns; /* event queue overruns */
+ int flashes; /* flashes on fiber link */
+ int ignites; /* times turned on */
+ int douses; /* times turned off */
+ int linkErrors; /* link errors */
+}vsStats;
+
+typedef struct _vs_cursor{
+ short x;
+ short y;
+}vsCursor;
+
+typedef struct _vs_box {
+ short bottom;
+ short right;
+ short left;
+ short top;
+}vsBox;
+
+typedef struct _vsIoAddr {
+ short *ioreg;
+ short status;
+ caddr_t obuff;
+ int obufflen;
+ int reloc;
+ vsEvent *ibuff;
+ int iqsize; /* may assume power of 2 */
+ int ihead; /* atomic write */
+ int itail; /* atomic read */
+ vsCursor mouse; /* atomic read/write */
+ vsBox mbox; /* atomic read/write */
+} vsIoAddr;
+typedef vsIoAddr *vsIoAddrAddr;
diff --git a/sys/sys/wait.h b/sys/sys/wait.h
new file mode 100644
index 0000000..33a68d9
--- /dev/null
+++ b/sys/sys/wait.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)wait.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * This file holds definitions relevent to the wait4 system call
+ * and the alternate interfaces that use it (wait, wait3, waitpid).
+ */
+
+/*
+ * Macros to test the exit status returned by wait
+ * and extract the relevant values.
+ */
+#ifdef _POSIX_SOURCE
+#define _W_INT(i) (i)
+#else
+#define _W_INT(w) (*(int *)&(w)) /* convert union wait to int */
+#define WCOREFLAG 0200
+#endif
+
+#define _WSTATUS(x) (_W_INT(x) & 0177)
+#define _WSTOPPED 0177 /* _WSTATUS if process is stopped */
+#define WIFSTOPPED(x) (_WSTATUS(x) == _WSTOPPED)
+#define WSTOPSIG(x) (_W_INT(x) >> 8)
+#define WIFSIGNALED(x) (_WSTATUS(x) != _WSTOPPED && _WSTATUS(x) != 0)
+#define WTERMSIG(x) (_WSTATUS(x))
+#define WIFEXITED(x) (_WSTATUS(x) == 0)
+#define WEXITSTATUS(x) (_W_INT(x) >> 8)
+#ifndef _POSIX_SOURCE
+#define WCOREDUMP(x) (_W_INT(x) & WCOREFLAG)
+
+#define W_EXITCODE(ret, sig) ((ret) << 8 | (sig))
+#define W_STOPCODE(sig) ((sig) << 8 | _WSTOPPED)
+#endif
+
+/*
+ * Option bits for the third argument of wait4. WNOHANG causes the
+ * wait to not hang if there are no stopped or terminated processes, rather
+ * returning an error indication in this case (pid==0). WUNTRACED
+ * indicates that the caller should receive status about untraced children
+ * which stop due to signals. If children are stopped and a wait without
+ * this option is done, it is as though they were still running... nothing
+ * about them is returned.
+ */
+#define WNOHANG 1 /* dont hang in wait */
+#define WUNTRACED 2 /* tell about stopped, untraced children */
+
+#ifndef _POSIX_SOURCE
+/* POSIX extensions and 4.2/4.3 compatability: */
+
+/*
+ * Tokens for special values of the "pid" parameter to wait4.
+ */
+#define WAIT_ANY (-1) /* any process */
+#define WAIT_MYPGRP 0 /* any process in my process group */
+
+#include <machine/endian.h>
+
+/*
+ * Deprecated:
+ * Structure of the information in the status word returned by wait4.
+ * If w_stopval==WSTOPPED, then the second structure describes
+ * the information returned, else the first.
+ */
+union wait {
+ int w_status; /* used in syscall */
+ /*
+ * Terminated process status.
+ */
+ struct {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned int w_Termsig:7, /* termination signal */
+ w_Coredump:1, /* core dump indicator */
+ w_Retcode:8, /* exit code if w_termsig==0 */
+ w_Filler:16; /* upper bits filler */
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ unsigned int w_Filler:16, /* upper bits filler */
+ w_Retcode:8, /* exit code if w_termsig==0 */
+ w_Coredump:1, /* core dump indicator */
+ w_Termsig:7; /* termination signal */
+#endif
+ } w_T;
+ /*
+ * Stopped process status. Returned
+ * only for traced children unless requested
+ * with the WUNTRACED option bit.
+ */
+ struct {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned int w_Stopval:8, /* == W_STOPPED if stopped */
+ w_Stopsig:8, /* signal that stopped us */
+ w_Filler:16; /* upper bits filler */
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ unsigned int w_Filler:16, /* upper bits filler */
+ w_Stopsig:8, /* signal that stopped us */
+ w_Stopval:8; /* == W_STOPPED if stopped */
+#endif
+ } w_S;
+};
+#define w_termsig w_T.w_Termsig
+#define w_coredump w_T.w_Coredump
+#define w_retcode w_T.w_Retcode
+#define w_stopval w_S.w_Stopval
+#define w_stopsig w_S.w_Stopsig
+
+#define WSTOPPED _WSTOPPED
+#endif /* _POSIX_SOURCE */
+
+#ifndef KERNEL
+#include <sys/types.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+struct rusage; /* forward declaration */
+
+pid_t wait __P((int *));
+pid_t waitpid __P((pid_t, int *, int));
+#ifndef _POSIX_SOURCE
+pid_t wait3 __P((int *, int, struct rusage *));
+pid_t wait4 __P((pid_t, int *, int, struct rusage *));
+#endif
+__END_DECLS
+#endif
diff --git a/sys/tools/vnode_if.awk b/sys/tools/vnode_if.awk
new file mode 100644
index 0000000..e190fa0
--- /dev/null
+++ b/sys/tools/vnode_if.awk
@@ -0,0 +1,433 @@
+#!/bin/sh -
+#
+# Copyright (c) 1992, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)vnode_if.sh 8.1 (Berkeley) 6/10/93
+#
+
+# Script to produce VFS front-end sugar.
+#
+# usage: vnode_if.sh srcfile
+# (where srcfile is currently /sys/kern/vnode_if.src)
+#
+# These awk scripts are not particularly well written, specifically they
+# don't use arrays well and figure out the same information repeatedly.
+# Please rewrite them if you actually understand how to use awk. Note,
+# they use nawk extensions and gawk's toupper.
+
+if [ $# -ne 1 ] ; then
+ echo 'usage: vnode_if.sh srcfile'
+ exit 1
+fi
+
+# Name of the source file.
+SRC=$1
+
+# Names of the created files.
+CFILE=vnode_if.c
+HEADER=vnode_if.h
+
+# Awk program (must support nawk extensions and gawk's "toupper")
+# Use "awk" at Berkeley, "gawk" elsewhere.
+AWK=awk
+
+# Print out header information for vnode_if.h.
+cat << END_OF_LEADING_COMMENT > $HEADER
+/*
+ * This file is produced automatically.
+ * Do not modify anything in here by hand.
+ *
+ * Created from @(#)vnode_if.sh 8.1 (Berkeley) 6/10/93
+ */
+
+extern struct vnodeop_desc vop_default_desc;
+END_OF_LEADING_COMMENT
+
+# Awk script to take vnode_if.src and turn it into vnode_if.h.
+$AWK '
+ NF == 0 || $0 ~ "^#" {
+ next;
+ }
+ {
+ # Get the function name.
+ name = $1;
+ uname = toupper(name);
+
+ # Get the function arguments.
+ for (c1 = 0;; ++c1) {
+ if (getline <= 0)
+ exit
+ if ($0 ~ "^};")
+ break;
+ a[c1] = $0;
+ }
+
+ # Print out the vop_F_args structure.
+ printf("struct %s_args {\n\tstruct vnodeop_desc *a_desc;\n",
+ name);
+ for (c2 = 0; c2 < c1; ++c2) {
+ c3 = split(a[c2], t);
+ printf("\t");
+ if (t[2] ~ "WILLRELE")
+ c4 = 3;
+ else
+ c4 = 2;
+ for (; c4 < c3; ++c4)
+ printf("%s ", t[c4]);
+ beg = match(t[c3], "[^*]");
+ printf("%sa_%s\n",
+ substr(t[c4], 0, beg - 1), substr(t[c4], beg));
+ }
+ printf("};\n");
+
+ # Print out extern declaration.
+ printf("extern struct vnodeop_desc %s_desc;\n", name);
+
+ # Print out inline struct.
+ printf("static inline int %s(", uname);
+ sep = ", ";
+ for (c2 = 0; c2 < c1; ++c2) {
+ if (c2 == c1 - 1)
+ sep = ")\n";
+ c3 = split(a[c2], t);
+ beg = match(t[c3], "[^*]");
+ end = match(t[c3], ";");
+ printf("%s%s", substr(t[c3], beg, end - beg), sep);
+ }
+ for (c2 = 0; c2 < c1; ++c2) {
+ c3 = split(a[c2], t);
+ printf("\t");
+ if (t[2] ~ "WILLRELE")
+ c4 = 3;
+ else
+ c4 = 2;
+ for (; c4 < c3; ++c4)
+ printf("%s ", t[c4]);
+ beg = match(t[c3], "[^*]");
+ printf("%s%s\n",
+ substr(t[c4], 0, beg - 1), substr(t[c4], beg));
+ }
+ printf("{\n\tstruct %s_args a;\n\n", name);
+ printf("\ta.a_desc = VDESC(%s);\n", name);
+ for (c2 = 0; c2 < c1; ++c2) {
+ c3 = split(a[c2], t);
+ printf("\t");
+ beg = match(t[c3], "[^*]");
+ end = match(t[c3], ";");
+ printf("a.a_%s = %s\n",
+ substr(t[c3], beg, end - beg), substr(t[c3], beg));
+ }
+ c1 = split(a[0], t);
+ beg = match(t[c1], "[^*]");
+ end = match(t[c1], ";");
+ printf("\treturn (VCALL(%s, VOFFSET(%s), &a));\n}\n",
+ substr(t[c1], beg, end - beg), name);
+ }' < $SRC >> $HEADER
+
+# Print out header information for vnode_if.c.
+cat << END_OF_LEADING_COMMENT > $CFILE
+/*
+ * This file is produced automatically.
+ * Do not modify anything in here by hand.
+ *
+ * Created from @(#)vnode_if.sh 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+
+struct vnodeop_desc vop_default_desc = {
+ 0,
+ "default",
+ 0,
+ NULL,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ NULL,
+};
+
+END_OF_LEADING_COMMENT
+
+# Awk script to take vnode_if.src and turn it into vnode_if.c.
+$AWK 'function kill_surrounding_ws (s) {
+ sub (/^[ \t]*/, "", s);
+ sub (/[ \t]*$/, "", s);
+ return s;
+ }
+
+ function read_args() {
+ numargs = 0;
+ while (getline ln) {
+ if (ln ~ /}/) {
+ break;
+ };
+
+ # Delete comments, if any.
+ gsub (/\/\*.*\*\//, "", ln);
+
+ # Delete leading/trailing space.
+ ln = kill_surrounding_ws(ln);
+
+ # Pick off direction.
+ if (1 == sub(/^INOUT[ \t]+/, "", ln))
+ dir = "INOUT";
+ else if (1 == sub(/^IN[ \t]+/, "", ln))
+ dir = "IN";
+ else if (1 == sub(/^OUT[ \t]+/, "", ln))
+ dir = "OUT";
+ else
+ bail("No IN/OUT direction for \"" ln "\".");
+
+ # check for "WILLRELE"
+ if (1 == sub(/^WILLRELE[ \t]+/, "", ln)) {
+ rele = "WILLRELE";
+ } else {
+ rele = "WONTRELE";
+ };
+
+ # kill trailing ;
+ if (1 != sub (/;$/, "", ln)) {
+ bail("Missing end-of-line ; in \"" ln "\".");
+ };
+
+ # pick off variable name
+ if (!(i = match(ln, /[A-Za-z0-9_]+$/))) {
+ bail("Missing var name \"a_foo\" in \"" ln "\".");
+ };
+ arg = substr (ln, i);
+ # Want to <<substr(ln, i) = "";>>, but nawk cannot.
+ # Hack around this.
+ ln = substr(ln, 1, i-1);
+
+ # what is left must be type
+ # (put clean it up some)
+ type = ln;
+ gsub (/[ \t]+/, " ", type); # condense whitespace
+ type = kill_surrounding_ws(type);
+
+ # (boy this was easier in Perl)
+
+ numargs++;
+ dirs[numargs] = dir;
+ reles[numargs] = rele;
+ types[numargs] = type;
+ args[numargs] = arg;
+ };
+ }
+
+ function generate_operation_vp_offsets() {
+ printf ("int %s_vp_offsets[] = {\n", name);
+ # as a side effect, figure out the releflags
+ releflags = "";
+ vpnum = 0;
+ for (i=1; i<=numargs; i++) {
+ if (types[i] == "struct vnode *") {
+ printf ("\tVOPARG_OFFSETOF(struct %s_args,a_%s),\n",
+ name, args[i]);
+ if (reles[i] == "WILLRELE") {
+ releflags = releflags "|VDESC_VP" vpnum "_WILLRELE";
+ };
+ vpnum++;
+ };
+ };
+ sub (/^\|/, "", releflags);
+ print "\tVDESC_NO_OFFSET";
+ print "};";
+ }
+
+ function find_arg_with_type (type) {
+ for (i=1; i<=numargs; i++) {
+ if (types[i] == type) {
+ return "VOPARG_OFFSETOF(struct " name "_args,a_" args[i] ")";
+ };
+ };
+ return "VDESC_NO_OFFSET";
+ }
+
+ function generate_operation_desc() {
+ printf ("struct vnodeop_desc %s_desc = {\n", name);
+ # offset
+ printf ("\t0,\n");
+ # printable name
+ printf ("\t\"%s\",\n", name);
+ # flags
+ vppwillrele = "";
+ for (i=1; i<=numargs; i++) {
+ if (types[i] == "struct vnode **" &&
+ (reles[i] == "WILLRELE")) {
+ vppwillrele = "|VDESC_VPP_WILLRELE";
+ };
+ };
+ if (releflags == "") {
+ printf ("\t0%s,\n", vppwillrele);
+ } else {
+ printf ("\t%s%s,\n", releflags, vppwillrele);
+ };
+ # vp offsets
+ printf ("\t%s_vp_offsets,\n", name);
+ # vpp (if any)
+ printf ("\t%s,\n", find_arg_with_type("struct vnode **"));
+ # cred (if any)
+ printf ("\t%s,\n", find_arg_with_type("struct ucred *"));
+ # proc (if any)
+ printf ("\t%s,\n", find_arg_with_type("struct proc *"));
+ # componentname
+ printf ("\t%s,\n", find_arg_with_type("struct componentname *"));
+ # transport layer information
+ printf ("\tNULL,\n};\n");
+ }
+
+ NF == 0 || $0 ~ "^#" {
+ next;
+ }
+ {
+ # get the function name
+ name = $1;
+
+ # get the function arguments
+ read_args();
+
+ # Print out the vop_F_vp_offsets structure. This all depends
+ # on naming conventions and nothing else.
+ generate_operation_vp_offsets();
+
+ # Print out the vnodeop_desc structure.
+ generate_operation_desc();
+
+ printf "\n";
+
+ }' < $SRC >> $CFILE
+# THINGS THAT DON'T WORK RIGHT YET.
+#
+# Two existing BSD vnodeops (bwrite and strategy) don't take any vnodes as
+# arguments. This means that these operations can't function successfully
+# through a bypass routine.
+#
+# Bwrite and strategy will be replaced when the VM page/buffer cache
+# integration happens.
+#
+# To get around this problem for now we handle these ops as special cases.
+
+cat << END_OF_SPECIAL_CASES >> $HEADER
+#include <sys/buf.h>
+struct vop_strategy_args {
+ struct vnodeop_desc *a_desc;
+ struct buf *a_bp;
+};
+extern struct vnodeop_desc vop_strategy_desc;
+static inline int VOP_STRATEGY(bp)
+ struct buf *bp;
+{
+ struct vop_strategy_args a;
+
+ a.a_desc = VDESC(vop_strategy);
+ a.a_bp = bp;
+ return (VCALL((bp)->b_vp, VOFFSET(vop_strategy), &a));
+}
+
+struct vop_bwrite_args {
+ struct vnodeop_desc *a_desc;
+ struct buf *a_bp;
+};
+extern struct vnodeop_desc vop_bwrite_desc;
+static inline int VOP_BWRITE(bp)
+ struct buf *bp;
+{
+ struct vop_bwrite_args a;
+
+ a.a_desc = VDESC(vop_bwrite);
+ a.a_bp = bp;
+ return (VCALL((bp)->b_vp, VOFFSET(vop_bwrite), &a));
+}
+END_OF_SPECIAL_CASES
+
+cat << END_OF_SPECIAL_CASES >> $CFILE
+int vop_strategy_vp_offsets[] = {
+ VDESC_NO_OFFSET
+};
+struct vnodeop_desc vop_strategy_desc = {
+ 0,
+ "vop_strategy",
+ 0,
+ vop_strategy_vp_offsets,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ NULL,
+};
+int vop_bwrite_vp_offsets[] = {
+ VDESC_NO_OFFSET
+};
+struct vnodeop_desc vop_bwrite_desc = {
+ 0,
+ "vop_bwrite",
+ 0,
+ vop_bwrite_vp_offsets,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ VDESC_NO_OFFSET,
+ NULL,
+};
+END_OF_SPECIAL_CASES
+
+# Add the vfs_op_descs array to the C file.
+$AWK '
+ BEGIN {
+ printf("\nstruct vnodeop_desc *vfs_op_descs[] = {\n");
+ printf("\t&vop_default_desc, /* MUST BE FIRST */\n");
+ printf("\t&vop_strategy_desc, /* XXX: SPECIAL CASE */\n");
+ printf("\t&vop_bwrite_desc, /* XXX: SPECIAL CASE */\n");
+ }
+ END {
+ printf("\tNULL\n};\n");
+ }
+ NF == 0 || $0 ~ "^#" {
+ next;
+ }
+ {
+ # Get the function name.
+ printf("\t&%s_desc,\n", $1);
+
+ # Skip the function arguments.
+ for (;;) {
+ if (getline <= 0)
+ exit
+ if ($0 ~ "^};")
+ break;
+ }
+ }' < $SRC >> $CFILE
+
diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c
new file mode 100644
index 0000000..15e8718
--- /dev/null
+++ b/sys/ufs/ffs/ffs_alloc.c
@@ -0,0 +1,1483 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ffs_alloc.c 8.8 (Berkeley) 2/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+#include <vm/vm.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+
+#include <ufs/ffs/fs.h>
+#include <ufs/ffs/ffs_extern.h>
+
+extern u_long nextgennumber;
+
+static daddr_t ffs_alloccg __P((struct inode *, int, daddr_t, int));
+static daddr_t ffs_alloccgblk __P((struct fs *, struct cg *, daddr_t));
+static daddr_t ffs_clusteralloc __P((struct inode *, int, daddr_t, int));
+static ino_t ffs_dirpref __P((struct fs *));
+static daddr_t ffs_fragextend __P((struct inode *, int, long, int, int));
+static void ffs_fserr __P((struct fs *, u_int, char *));
+static u_long ffs_hashalloc
+ __P((struct inode *, int, long, int, u_long (*)()));
+static ino_t ffs_nodealloccg __P((struct inode *, int, daddr_t, int));
+static daddr_t ffs_mapsearch __P((struct fs *, struct cg *, daddr_t, int));
+
+void ffs_clusteracct __P((struct fs *, struct cg *, daddr_t, int));
+
+/*
+ * Allocate a block in the file system.
+ *
+ * The size of the requested block is given, which must be some
+ * multiple of fs_fsize and <= fs_bsize.
+ * A preference may be optionally specified. If a preference is given
+ * the following hierarchy is used to allocate a block:
+ * 1) allocate the requested block.
+ * 2) allocate a rotationally optimal block in the same cylinder.
+ * 3) allocate a block in the same cylinder group.
+ * 4) quadradically rehash into other cylinder groups, until an
+ * available block is located.
+ * If no block preference is given the following heirarchy is used
+ * to allocate a block:
+ * 1) allocate a block in the cylinder group that contains the
+ * inode for the file.
+ * 2) quadradically rehash into other cylinder groups, until an
+ * available block is located.
+ */
+int
+ffs_alloc(ip, lbn, bpref, size, cred, bnp)
+ register struct inode *ip;
+ daddr_t lbn, bpref;
+ int size;
+ struct ucred *cred;
+ daddr_t *bnp;
+{
+ register struct fs *fs;
+ daddr_t bno;
+ int cg, error;
+
+ *bnp = 0;
+ fs = ip->i_fs;
+#ifdef DIAGNOSTIC
+ if ((u_int)size > fs->fs_bsize || fragoff(fs, size) != 0) {
+ printf("dev = 0x%x, bsize = %d, size = %d, fs = %s\n",
+ ip->i_dev, fs->fs_bsize, size, fs->fs_fsmnt);
+ panic("ffs_alloc: bad size");
+ }
+ if (cred == NOCRED)
+ panic("ffs_alloc: missing credential\n");
+#endif /* DIAGNOSTIC */
+ if (size == fs->fs_bsize && fs->fs_cstotal.cs_nbfree == 0)
+ goto nospace;
+ if (cred->cr_uid != 0 && freespace(fs, fs->fs_minfree) <= 0)
+ goto nospace;
+#ifdef QUOTA
+ if (error = chkdq(ip, (long)btodb(size), cred, 0))
+ return (error);
+#endif
+ if (bpref >= fs->fs_size)
+ bpref = 0;
+ if (bpref == 0)
+ cg = ino_to_cg(fs, ip->i_number);
+ else
+ cg = dtog(fs, bpref);
+ bno = (daddr_t)ffs_hashalloc(ip, cg, (long)bpref, size,
+ (u_long (*)())ffs_alloccg);
+ if (bno > 0) {
+ ip->i_blocks += btodb(size);
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ *bnp = bno;
+ return (0);
+ }
+#ifdef QUOTA
+ /*
+ * Restore user's disk quota because allocation failed.
+ */
+ (void) chkdq(ip, (long)-btodb(size), cred, FORCE);
+#endif
+nospace:
+ ffs_fserr(fs, cred->cr_uid, "file system full");
+ uprintf("\n%s: write failed, file system is full\n", fs->fs_fsmnt);
+ return (ENOSPC);
+}
+
+/*
+ * Reallocate a fragment to a bigger size
+ *
+ * The number and size of the old block is given, and a preference
+ * and new size is also specified. The allocator attempts to extend
+ * the original block. Failing that, the regular block allocator is
+ * invoked to get an appropriate block.
+ */
+int
+ffs_realloccg(ip, lbprev, bpref, osize, nsize, cred, bpp)
+ register struct inode *ip;
+ daddr_t lbprev;
+ daddr_t bpref;
+ int osize, nsize;
+ struct ucred *cred;
+ struct buf **bpp;
+{
+ register struct fs *fs;
+ struct buf *bp;
+ int cg, request, error;
+ daddr_t bprev, bno;
+
+ *bpp = 0;
+ fs = ip->i_fs;
+#ifdef DIAGNOSTIC
+ if ((u_int)osize > fs->fs_bsize || fragoff(fs, osize) != 0 ||
+ (u_int)nsize > fs->fs_bsize || fragoff(fs, nsize) != 0) {
+ printf(
+ "dev = 0x%x, bsize = %d, osize = %d, nsize = %d, fs = %s\n",
+ ip->i_dev, fs->fs_bsize, osize, nsize, fs->fs_fsmnt);
+ panic("ffs_realloccg: bad size");
+ }
+ if (cred == NOCRED)
+ panic("ffs_realloccg: missing credential\n");
+#endif /* DIAGNOSTIC */
+ if (cred->cr_uid != 0 && freespace(fs, fs->fs_minfree) <= 0)
+ goto nospace;
+ if ((bprev = ip->i_db[lbprev]) == 0) {
+ printf("dev = 0x%x, bsize = %d, bprev = %d, fs = %s\n",
+ ip->i_dev, fs->fs_bsize, bprev, fs->fs_fsmnt);
+ panic("ffs_realloccg: bad bprev");
+ }
+ /*
+ * Allocate the extra space in the buffer.
+ */
+ if (error = bread(ITOV(ip), lbprev, osize, NOCRED, &bp)) {
+ brelse(bp);
+ return (error);
+ }
+#ifdef QUOTA
+ if (error = chkdq(ip, (long)btodb(nsize - osize), cred, 0)) {
+ brelse(bp);
+ return (error);
+ }
+#endif
+ /*
+ * Check for extension in the existing location.
+ */
+ cg = dtog(fs, bprev);
+ if (bno = ffs_fragextend(ip, cg, (long)bprev, osize, nsize)) {
+ if (bp->b_blkno != fsbtodb(fs, bno))
+ panic("bad blockno");
+ ip->i_blocks += btodb(nsize - osize);
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ allocbuf(bp, nsize);
+ bp->b_flags |= B_DONE;
+ bzero((char *)bp->b_data + osize, (u_int)nsize - osize);
+ *bpp = bp;
+ return (0);
+ }
+ /*
+ * Allocate a new disk location.
+ */
+ if (bpref >= fs->fs_size)
+ bpref = 0;
+ switch ((int)fs->fs_optim) {
+ case FS_OPTSPACE:
+ /*
+ * Allocate an exact sized fragment. Although this makes
+ * best use of space, we will waste time relocating it if
+ * the file continues to grow. If the fragmentation is
+ * less than half of the minimum free reserve, we choose
+ * to begin optimizing for time.
+ */
+ request = nsize;
+ if (fs->fs_minfree < 5 ||
+ fs->fs_cstotal.cs_nffree >
+ fs->fs_dsize * fs->fs_minfree / (2 * 100))
+ break;
+ log(LOG_NOTICE, "%s: optimization changed from SPACE to TIME\n",
+ fs->fs_fsmnt);
+ fs->fs_optim = FS_OPTTIME;
+ break;
+ case FS_OPTTIME:
+ /*
+ * At this point we have discovered a file that is trying to
+ * grow a small fragment to a larger fragment. To save time,
+ * we allocate a full sized block, then free the unused portion.
+ * If the file continues to grow, the `ffs_fragextend' call
+ * above will be able to grow it in place without further
+ * copying. If aberrant programs cause disk fragmentation to
+ * grow within 2% of the free reserve, we choose to begin
+ * optimizing for space.
+ */
+ request = fs->fs_bsize;
+ if (fs->fs_cstotal.cs_nffree <
+ fs->fs_dsize * (fs->fs_minfree - 2) / 100)
+ break;
+ log(LOG_NOTICE, "%s: optimization changed from TIME to SPACE\n",
+ fs->fs_fsmnt);
+ fs->fs_optim = FS_OPTSPACE;
+ break;
+ default:
+ printf("dev = 0x%x, optim = %d, fs = %s\n",
+ ip->i_dev, fs->fs_optim, fs->fs_fsmnt);
+ panic("ffs_realloccg: bad optim");
+ /* NOTREACHED */
+ }
+ bno = (daddr_t)ffs_hashalloc(ip, cg, (long)bpref, request,
+ (u_long (*)())ffs_alloccg);
+ if (bno > 0) {
+ bp->b_blkno = fsbtodb(fs, bno);
+ (void) vnode_pager_uncache(ITOV(ip));
+ ffs_blkfree(ip, bprev, (long)osize);
+ if (nsize < request)
+ ffs_blkfree(ip, bno + numfrags(fs, nsize),
+ (long)(request - nsize));
+ ip->i_blocks += btodb(nsize - osize);
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ allocbuf(bp, nsize);
+ bp->b_flags |= B_DONE;
+ bzero((char *)bp->b_data + osize, (u_int)nsize - osize);
+ *bpp = bp;
+ return (0);
+ }
+#ifdef QUOTA
+ /*
+ * Restore user's disk quota because allocation failed.
+ */
+ (void) chkdq(ip, (long)-btodb(nsize - osize), cred, FORCE);
+#endif
+ brelse(bp);
+nospace:
+ /*
+ * no space available
+ */
+ ffs_fserr(fs, cred->cr_uid, "file system full");
+ uprintf("\n%s: write failed, file system is full\n", fs->fs_fsmnt);
+ return (ENOSPC);
+}
+
+/*
+ * Reallocate a sequence of blocks into a contiguous sequence of blocks.
+ *
+ * The vnode and an array of buffer pointers for a range of sequential
+ * logical blocks to be made contiguous is given. The allocator attempts
+ * to find a range of sequential blocks starting as close as possible to
+ * an fs_rotdelay offset from the end of the allocation for the logical
+ * block immediately preceeding the current range. If successful, the
+ * physical block numbers in the buffer pointers and in the inode are
+ * changed to reflect the new allocation. If unsuccessful, the allocation
+ * is left unchanged. The success in doing the reallocation is returned.
+ * Note that the error return is not reflected back to the user. Rather
+ * the previous block allocation will be used.
+ */
+#include <sys/sysctl.h>
+int doasyncfree = 1;
+#ifdef DEBUG
+struct ctldebug debug14 = { "doasyncfree", &doasyncfree };
+#endif
+int
+ffs_reallocblks(ap)
+ struct vop_reallocblks_args /* {
+ struct vnode *a_vp;
+ struct cluster_save *a_buflist;
+ } */ *ap;
+{
+ struct fs *fs;
+ struct inode *ip;
+ struct vnode *vp;
+ struct buf *sbp, *ebp;
+ daddr_t *bap, *sbap, *ebap = 0;
+ struct cluster_save *buflist;
+ daddr_t start_lbn, end_lbn, soff, eoff, newblk, blkno;
+ struct indir start_ap[NIADDR + 1], end_ap[NIADDR + 1], *idp;
+ int i, len, start_lvl, end_lvl, pref, ssize;
+
+ vp = ap->a_vp;
+ ip = VTOI(vp);
+ fs = ip->i_fs;
+ if (fs->fs_contigsumsize <= 0)
+ return (ENOSPC);
+ buflist = ap->a_buflist;
+ len = buflist->bs_nchildren;
+ start_lbn = buflist->bs_children[0]->b_lblkno;
+ end_lbn = start_lbn + len - 1;
+#ifdef DIAGNOSTIC
+ for (i = 1; i < len; i++)
+ if (buflist->bs_children[i]->b_lblkno != start_lbn + i)
+ panic("ffs_reallocblks: non-cluster");
+#endif
+ /*
+ * If the latest allocation is in a new cylinder group, assume that
+ * the filesystem has decided to move and do not force it back to
+ * the previous cylinder group.
+ */
+ if (dtog(fs, dbtofsb(fs, buflist->bs_children[0]->b_blkno)) !=
+ dtog(fs, dbtofsb(fs, buflist->bs_children[len - 1]->b_blkno)))
+ return (ENOSPC);
+ if (ufs_getlbns(vp, start_lbn, start_ap, &start_lvl) ||
+ ufs_getlbns(vp, end_lbn, end_ap, &end_lvl))
+ return (ENOSPC);
+ /*
+ * Get the starting offset and block map for the first block.
+ */
+ if (start_lvl == 0) {
+ sbap = &ip->i_db[0];
+ soff = start_lbn;
+ } else {
+ idp = &start_ap[start_lvl - 1];
+ if (bread(vp, idp->in_lbn, (int)fs->fs_bsize, NOCRED, &sbp)) {
+ brelse(sbp);
+ return (ENOSPC);
+ }
+ sbap = (daddr_t *)sbp->b_data;
+ soff = idp->in_off;
+ }
+ /*
+ * Find the preferred location for the cluster.
+ */
+ pref = ffs_blkpref(ip, start_lbn, soff, sbap);
+ /*
+ * If the block range spans two block maps, get the second map.
+ */
+ if (end_lvl == 0 || (idp = &end_ap[end_lvl - 1])->in_off + 1 >= len) {
+ ssize = len;
+ } else {
+#ifdef DIAGNOSTIC
+ if (start_ap[start_lvl-1].in_lbn == idp->in_lbn)
+ panic("ffs_reallocblk: start == end");
+#endif
+ ssize = len - (idp->in_off + 1);
+ if (bread(vp, idp->in_lbn, (int)fs->fs_bsize, NOCRED, &ebp))
+ goto fail;
+ ebap = (daddr_t *)ebp->b_data;
+ }
+ /*
+ * Search the block map looking for an allocation of the desired size.
+ */
+ if ((newblk = (daddr_t)ffs_hashalloc(ip, dtog(fs, pref), (long)pref,
+ len, (u_long (*)())ffs_clusteralloc)) == 0)
+ goto fail;
+ /*
+ * We have found a new contiguous block.
+ *
+ * First we have to replace the old block pointers with the new
+ * block pointers in the inode and indirect blocks associated
+ * with the file.
+ */
+ blkno = newblk;
+ for (bap = &sbap[soff], i = 0; i < len; i++, blkno += fs->fs_frag) {
+ if (i == ssize)
+ bap = ebap;
+#ifdef DIAGNOSTIC
+ if (buflist->bs_children[i]->b_blkno != fsbtodb(fs, *bap))
+ panic("ffs_reallocblks: alloc mismatch");
+#endif
+ *bap++ = blkno;
+ }
+ /*
+ * Next we must write out the modified inode and indirect blocks.
+ * For strict correctness, the writes should be synchronous since
+ * the old block values may have been written to disk. In practise
+ * they are almost never written, but if we are concerned about
+ * strict correctness, the `doasyncfree' flag should be set to zero.
+ *
+ * The test on `doasyncfree' should be changed to test a flag
+ * that shows whether the associated buffers and inodes have
+ * been written. The flag should be set when the cluster is
+ * started and cleared whenever the buffer or inode is flushed.
+ * We can then check below to see if it is set, and do the
+ * synchronous write only when it has been cleared.
+ */
+ if (sbap != &ip->i_db[0]) {
+ if (doasyncfree)
+ bdwrite(sbp);
+ else
+ bwrite(sbp);
+ } else {
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ if (!doasyncfree)
+ VOP_UPDATE(vp, &time, &time, MNT_WAIT);
+ }
+ if (ssize < len)
+ if (doasyncfree)
+ bdwrite(ebp);
+ else
+ bwrite(ebp);
+ /*
+ * Last, free the old blocks and assign the new blocks to the buffers.
+ */
+ for (blkno = newblk, i = 0; i < len; i++, blkno += fs->fs_frag) {
+ ffs_blkfree(ip, dbtofsb(fs, buflist->bs_children[i]->b_blkno),
+ fs->fs_bsize);
+ buflist->bs_children[i]->b_blkno = fsbtodb(fs, blkno);
+ }
+ return (0);
+
+fail:
+ if (ssize < len)
+ brelse(ebp);
+ if (sbap != &ip->i_db[0])
+ brelse(sbp);
+ return (ENOSPC);
+}
+
+/*
+ * Allocate an inode in the file system.
+ *
+ * If allocating a directory, use ffs_dirpref to select the inode.
+ * If allocating in a directory, the following hierarchy is followed:
+ * 1) allocate the preferred inode.
+ * 2) allocate an inode in the same cylinder group.
+ * 3) quadradically rehash into other cylinder groups, until an
+ * available inode is located.
+ * If no inode preference is given the following heirarchy is used
+ * to allocate an inode:
+ * 1) allocate an inode in cylinder group 0.
+ * 2) quadradically rehash into other cylinder groups, until an
+ * available inode is located.
+ */
+int
+ffs_valloc(ap)
+ struct vop_valloc_args /* {
+ struct vnode *a_pvp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct vnode **a_vpp;
+ } */ *ap;
+{
+ register struct vnode *pvp = ap->a_pvp;
+ register struct inode *pip;
+ register struct fs *fs;
+ register struct inode *ip;
+ mode_t mode = ap->a_mode;
+ ino_t ino, ipref;
+ int cg, error;
+
+ *ap->a_vpp = NULL;
+ pip = VTOI(pvp);
+ fs = pip->i_fs;
+ if (fs->fs_cstotal.cs_nifree == 0)
+ goto noinodes;
+
+ if ((mode & IFMT) == IFDIR)
+ ipref = ffs_dirpref(fs);
+ else
+ ipref = pip->i_number;
+ if (ipref >= fs->fs_ncg * fs->fs_ipg)
+ ipref = 0;
+ cg = ino_to_cg(fs, ipref);
+ ino = (ino_t)ffs_hashalloc(pip, cg, (long)ipref, mode, ffs_nodealloccg);
+ if (ino == 0)
+ goto noinodes;
+ error = VFS_VGET(pvp->v_mount, ino, ap->a_vpp);
+ if (error) {
+ VOP_VFREE(pvp, ino, mode);
+ return (error);
+ }
+ ip = VTOI(*ap->a_vpp);
+ if (ip->i_mode) {
+ printf("mode = 0%o, inum = %d, fs = %s\n",
+ ip->i_mode, ip->i_number, fs->fs_fsmnt);
+ panic("ffs_valloc: dup alloc");
+ }
+ if (ip->i_blocks) { /* XXX */
+ printf("free inode %s/%d had %d blocks\n",
+ fs->fs_fsmnt, ino, ip->i_blocks);
+ ip->i_blocks = 0;
+ }
+ ip->i_flags = 0;
+ /*
+ * Set up a new generation number for this inode.
+ */
+ if (++nextgennumber < (u_long)time.tv_sec)
+ nextgennumber = time.tv_sec;
+ ip->i_gen = nextgennumber;
+ return (0);
+noinodes:
+ ffs_fserr(fs, ap->a_cred->cr_uid, "out of inodes");
+ uprintf("\n%s: create/symlink failed, no inodes free\n", fs->fs_fsmnt);
+ return (ENOSPC);
+}
+
+/*
+ * Find a cylinder to place a directory.
+ *
+ * The policy implemented by this algorithm is to select from
+ * among those cylinder groups with above the average number of
+ * free inodes, the one with the smallest number of directories.
+ */
+static ino_t
+ffs_dirpref(fs)
+ register struct fs *fs;
+{
+ int cg, minndir, mincg, avgifree;
+
+ avgifree = fs->fs_cstotal.cs_nifree / fs->fs_ncg;
+ minndir = fs->fs_ipg;
+ mincg = 0;
+ for (cg = 0; cg < fs->fs_ncg; cg++)
+ if (fs->fs_cs(fs, cg).cs_ndir < minndir &&
+ fs->fs_cs(fs, cg).cs_nifree >= avgifree) {
+ mincg = cg;
+ minndir = fs->fs_cs(fs, cg).cs_ndir;
+ }
+ return ((ino_t)(fs->fs_ipg * mincg));
+}
+
+/*
+ * Select the desired position for the next block in a file. The file is
+ * logically divided into sections. The first section is composed of the
+ * direct blocks. Each additional section contains fs_maxbpg blocks.
+ *
+ * If no blocks have been allocated in the first section, the policy is to
+ * request a block in the same cylinder group as the inode that describes
+ * the file. If no blocks have been allocated in any other section, the
+ * policy is to place the section in a cylinder group with a greater than
+ * average number of free blocks. An appropriate cylinder group is found
+ * by using a rotor that sweeps the cylinder groups. When a new group of
+ * blocks is needed, the sweep begins in the cylinder group following the
+ * cylinder group from which the previous allocation was made. The sweep
+ * continues until a cylinder group with greater than the average number
+ * of free blocks is found. If the allocation is for the first block in an
+ * indirect block, the information on the previous allocation is unavailable;
+ * here a best guess is made based upon the logical block number being
+ * allocated.
+ *
+ * If a section is already partially allocated, the policy is to
+ * contiguously allocate fs_maxcontig blocks. The end of one of these
+ * contiguous blocks and the beginning of the next is physically separated
+ * so that the disk head will be in transit between them for at least
+ * fs_rotdelay milliseconds. This is to allow time for the processor to
+ * schedule another I/O transfer.
+ */
+daddr_t
+ffs_blkpref(ip, lbn, indx, bap)
+ struct inode *ip;
+ daddr_t lbn;
+ int indx;
+ daddr_t *bap;
+{
+ register struct fs *fs;
+ register int cg;
+ int avgbfree, startcg;
+ daddr_t nextblk;
+
+ fs = ip->i_fs;
+ if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) {
+ if (lbn < NDADDR) {
+ cg = ino_to_cg(fs, ip->i_number);
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ }
+ /*
+ * Find a cylinder with greater than average number of
+ * unused data blocks.
+ */
+ if (indx == 0 || bap[indx - 1] == 0)
+ startcg =
+ ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg;
+ else
+ startcg = dtog(fs, bap[indx - 1]) + 1;
+ startcg %= fs->fs_ncg;
+ avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg;
+ for (cg = startcg; cg < fs->fs_ncg; cg++)
+ if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) {
+ fs->fs_cgrotor = cg;
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ }
+ for (cg = 0; cg <= startcg; cg++)
+ if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) {
+ fs->fs_cgrotor = cg;
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ }
+ return (NULL);
+ }
+ /*
+ * One or more previous blocks have been laid out. If less
+ * than fs_maxcontig previous blocks are contiguous, the
+ * next block is requested contiguously, otherwise it is
+ * requested rotationally delayed by fs_rotdelay milliseconds.
+ */
+ nextblk = bap[indx - 1] + fs->fs_frag;
+ if (indx < fs->fs_maxcontig || bap[indx - fs->fs_maxcontig] +
+ blkstofrags(fs, fs->fs_maxcontig) != nextblk)
+ return (nextblk);
+ if (fs->fs_rotdelay != 0)
+ /*
+ * Here we convert ms of delay to frags as:
+ * (frags) = (ms) * (rev/sec) * (sect/rev) /
+ * ((sect/frag) * (ms/sec))
+ * then round up to the next block.
+ */
+ nextblk += roundup(fs->fs_rotdelay * fs->fs_rps * fs->fs_nsect /
+ (NSPF(fs) * 1000), fs->fs_frag);
+ return (nextblk);
+}
+
+/*
+ * Implement the cylinder overflow algorithm.
+ *
+ * The policy implemented by this algorithm is:
+ * 1) allocate the block in its requested cylinder group.
+ * 2) quadradically rehash on the cylinder group number.
+ * 3) brute force search for a free block.
+ */
+/*VARARGS5*/
+static u_long
+ffs_hashalloc(ip, cg, pref, size, allocator)
+ struct inode *ip;
+ int cg;
+ long pref;
+ int size; /* size for data blocks, mode for inodes */
+ u_long (*allocator)();
+{
+ register struct fs *fs;
+ long result;
+ int i, icg = cg;
+
+ fs = ip->i_fs;
+ /*
+ * 1: preferred cylinder group
+ */
+ result = (*allocator)(ip, cg, pref, size);
+ if (result)
+ return (result);
+ /*
+ * 2: quadratic rehash
+ */
+ for (i = 1; i < fs->fs_ncg; i *= 2) {
+ cg += i;
+ if (cg >= fs->fs_ncg)
+ cg -= fs->fs_ncg;
+ result = (*allocator)(ip, cg, 0, size);
+ if (result)
+ return (result);
+ }
+ /*
+ * 3: brute force search
+ * Note that we start at i == 2, since 0 was checked initially,
+ * and 1 is always checked in the quadratic rehash.
+ */
+ cg = (icg + 2) % fs->fs_ncg;
+ for (i = 2; i < fs->fs_ncg; i++) {
+ result = (*allocator)(ip, cg, 0, size);
+ if (result)
+ return (result);
+ cg++;
+ if (cg == fs->fs_ncg)
+ cg = 0;
+ }
+ return (NULL);
+}
+
+/*
+ * Determine whether a fragment can be extended.
+ *
+ * Check to see if the necessary fragments are available, and
+ * if they are, allocate them.
+ */
+static daddr_t
+ffs_fragextend(ip, cg, bprev, osize, nsize)
+ struct inode *ip;
+ int cg;
+ long bprev;
+ int osize, nsize;
+{
+ register struct fs *fs;
+ register struct cg *cgp;
+ struct buf *bp;
+ long bno;
+ int frags, bbase;
+ int i, error;
+
+ fs = ip->i_fs;
+ if (fs->fs_cs(fs, cg).cs_nffree < numfrags(fs, nsize - osize))
+ return (NULL);
+ frags = numfrags(fs, nsize);
+ bbase = fragnum(fs, bprev);
+ if (bbase > fragnum(fs, (bprev + frags - 1))) {
+ /* cannot extend across a block boundary */
+ return (NULL);
+ }
+ error = bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)),
+ (int)fs->fs_cgsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (NULL);
+ }
+ cgp = (struct cg *)bp->b_data;
+ if (!cg_chkmagic(cgp)) {
+ brelse(bp);
+ return (NULL);
+ }
+ cgp->cg_time = time.tv_sec;
+ bno = dtogd(fs, bprev);
+ for (i = numfrags(fs, osize); i < frags; i++)
+ if (isclr(cg_blksfree(cgp), bno + i)) {
+ brelse(bp);
+ return (NULL);
+ }
+ /*
+ * the current fragment can be extended
+ * deduct the count on fragment being extended into
+ * increase the count on the remaining fragment (if any)
+ * allocate the extended piece
+ */
+ for (i = frags; i < fs->fs_frag - bbase; i++)
+ if (isclr(cg_blksfree(cgp), bno + i))
+ break;
+ cgp->cg_frsum[i - numfrags(fs, osize)]--;
+ if (i != frags)
+ cgp->cg_frsum[i - frags]++;
+ for (i = numfrags(fs, osize); i < frags; i++) {
+ clrbit(cg_blksfree(cgp), bno + i);
+ cgp->cg_cs.cs_nffree--;
+ fs->fs_cstotal.cs_nffree--;
+ fs->fs_cs(fs, cg).cs_nffree--;
+ }
+ fs->fs_fmod = 1;
+ bdwrite(bp);
+ return (bprev);
+}
+
+/*
+ * Determine whether a block can be allocated.
+ *
+ * Check to see if a block of the appropriate size is available,
+ * and if it is, allocate it.
+ */
+static daddr_t
+ffs_alloccg(ip, cg, bpref, size)
+ struct inode *ip;
+ int cg;
+ daddr_t bpref;
+ int size;
+{
+ register struct fs *fs;
+ register struct cg *cgp;
+ struct buf *bp;
+ register int i;
+ int error, bno, frags, allocsiz;
+
+ fs = ip->i_fs;
+ if (fs->fs_cs(fs, cg).cs_nbfree == 0 && size == fs->fs_bsize)
+ return (NULL);
+ error = bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)),
+ (int)fs->fs_cgsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (NULL);
+ }
+ cgp = (struct cg *)bp->b_data;
+ if (!cg_chkmagic(cgp) ||
+ (cgp->cg_cs.cs_nbfree == 0 && size == fs->fs_bsize)) {
+ brelse(bp);
+ return (NULL);
+ }
+ cgp->cg_time = time.tv_sec;
+ if (size == fs->fs_bsize) {
+ bno = ffs_alloccgblk(fs, cgp, bpref);
+ bdwrite(bp);
+ return (bno);
+ }
+ /*
+ * check to see if any fragments are already available
+ * allocsiz is the size which will be allocated, hacking
+ * it down to a smaller size if necessary
+ */
+ frags = numfrags(fs, size);
+ for (allocsiz = frags; allocsiz < fs->fs_frag; allocsiz++)
+ if (cgp->cg_frsum[allocsiz] != 0)
+ break;
+ if (allocsiz == fs->fs_frag) {
+ /*
+ * no fragments were available, so a block will be
+ * allocated, and hacked up
+ */
+ if (cgp->cg_cs.cs_nbfree == 0) {
+ brelse(bp);
+ return (NULL);
+ }
+ bno = ffs_alloccgblk(fs, cgp, bpref);
+ bpref = dtogd(fs, bno);
+ for (i = frags; i < fs->fs_frag; i++)
+ setbit(cg_blksfree(cgp), bpref + i);
+ i = fs->fs_frag - frags;
+ cgp->cg_cs.cs_nffree += i;
+ fs->fs_cstotal.cs_nffree += i;
+ fs->fs_cs(fs, cg).cs_nffree += i;
+ fs->fs_fmod = 1;
+ cgp->cg_frsum[i]++;
+ bdwrite(bp);
+ return (bno);
+ }
+ bno = ffs_mapsearch(fs, cgp, bpref, allocsiz);
+ if (bno < 0) {
+ brelse(bp);
+ return (NULL);
+ }
+ for (i = 0; i < frags; i++)
+ clrbit(cg_blksfree(cgp), bno + i);
+ cgp->cg_cs.cs_nffree -= frags;
+ fs->fs_cstotal.cs_nffree -= frags;
+ fs->fs_cs(fs, cg).cs_nffree -= frags;
+ fs->fs_fmod = 1;
+ cgp->cg_frsum[allocsiz]--;
+ if (frags != allocsiz)
+ cgp->cg_frsum[allocsiz - frags]++;
+ bdwrite(bp);
+ return (cg * fs->fs_fpg + bno);
+}
+
+/*
+ * Allocate a block in a cylinder group.
+ *
+ * This algorithm implements the following policy:
+ * 1) allocate the requested block.
+ * 2) allocate a rotationally optimal block in the same cylinder.
+ * 3) allocate the next available block on the block rotor for the
+ * specified cylinder group.
+ * Note that this routine only allocates fs_bsize blocks; these
+ * blocks may be fragmented by the routine that allocates them.
+ */
+static daddr_t
+ffs_alloccgblk(fs, cgp, bpref)
+ register struct fs *fs;
+ register struct cg *cgp;
+ daddr_t bpref;
+{
+ daddr_t bno, blkno;
+ int cylno, pos, delta;
+ short *cylbp;
+ register int i;
+
+ if (bpref == 0 || dtog(fs, bpref) != cgp->cg_cgx) {
+ bpref = cgp->cg_rotor;
+ goto norot;
+ }
+ bpref = blknum(fs, bpref);
+ bpref = dtogd(fs, bpref);
+ /*
+ * if the requested block is available, use it
+ */
+ if (ffs_isblock(fs, cg_blksfree(cgp), fragstoblks(fs, bpref))) {
+ bno = bpref;
+ goto gotit;
+ }
+ /*
+ * check for a block available on the same cylinder
+ */
+ cylno = cbtocylno(fs, bpref);
+ if (cg_blktot(cgp)[cylno] == 0)
+ goto norot;
+ if (fs->fs_cpc == 0) {
+ /*
+ * Block layout information is not available.
+ * Leaving bpref unchanged means we take the
+ * next available free block following the one
+ * we just allocated. Hopefully this will at
+ * least hit a track cache on drives of unknown
+ * geometry (e.g. SCSI).
+ */
+ goto norot;
+ }
+ /*
+ * check the summary information to see if a block is
+ * available in the requested cylinder starting at the
+ * requested rotational position and proceeding around.
+ */
+ cylbp = cg_blks(fs, cgp, cylno);
+ pos = cbtorpos(fs, bpref);
+ for (i = pos; i < fs->fs_nrpos; i++)
+ if (cylbp[i] > 0)
+ break;
+ if (i == fs->fs_nrpos)
+ for (i = 0; i < pos; i++)
+ if (cylbp[i] > 0)
+ break;
+ if (cylbp[i] > 0) {
+ /*
+ * found a rotational position, now find the actual
+ * block. A panic if none is actually there.
+ */
+ pos = cylno % fs->fs_cpc;
+ bno = (cylno - pos) * fs->fs_spc / NSPB(fs);
+ if (fs_postbl(fs, pos)[i] == -1) {
+ printf("pos = %d, i = %d, fs = %s\n",
+ pos, i, fs->fs_fsmnt);
+ panic("ffs_alloccgblk: cyl groups corrupted");
+ }
+ for (i = fs_postbl(fs, pos)[i];; ) {
+ if (ffs_isblock(fs, cg_blksfree(cgp), bno + i)) {
+ bno = blkstofrags(fs, (bno + i));
+ goto gotit;
+ }
+ delta = fs_rotbl(fs)[i];
+ if (delta <= 0 ||
+ delta + i > fragstoblks(fs, fs->fs_fpg))
+ break;
+ i += delta;
+ }
+ printf("pos = %d, i = %d, fs = %s\n", pos, i, fs->fs_fsmnt);
+ panic("ffs_alloccgblk: can't find blk in cyl");
+ }
+norot:
+ /*
+ * no blocks in the requested cylinder, so take next
+ * available one in this cylinder group.
+ */
+ bno = ffs_mapsearch(fs, cgp, bpref, (int)fs->fs_frag);
+ if (bno < 0)
+ return (NULL);
+ cgp->cg_rotor = bno;
+gotit:
+ blkno = fragstoblks(fs, bno);
+ ffs_clrblock(fs, cg_blksfree(cgp), (long)blkno);
+ ffs_clusteracct(fs, cgp, blkno, -1);
+ cgp->cg_cs.cs_nbfree--;
+ fs->fs_cstotal.cs_nbfree--;
+ fs->fs_cs(fs, cgp->cg_cgx).cs_nbfree--;
+ cylno = cbtocylno(fs, bno);
+ cg_blks(fs, cgp, cylno)[cbtorpos(fs, bno)]--;
+ cg_blktot(cgp)[cylno]--;
+ fs->fs_fmod = 1;
+ return (cgp->cg_cgx * fs->fs_fpg + bno);
+}
+
+/*
+ * Determine whether a cluster can be allocated.
+ *
+ * We do not currently check for optimal rotational layout if there
+ * are multiple choices in the same cylinder group. Instead we just
+ * take the first one that we find following bpref.
+ */
+static daddr_t
+ffs_clusteralloc(ip, cg, bpref, len)
+ struct inode *ip;
+ int cg;
+ daddr_t bpref;
+ int len;
+{
+ register struct fs *fs;
+ register struct cg *cgp;
+ struct buf *bp;
+ int i, run, bno, bit, map;
+ u_char *mapp;
+
+ fs = ip->i_fs;
+ if (fs->fs_cs(fs, cg).cs_nbfree < len)
+ return (NULL);
+ if (bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize,
+ NOCRED, &bp))
+ goto fail;
+ cgp = (struct cg *)bp->b_data;
+ if (!cg_chkmagic(cgp))
+ goto fail;
+ /*
+ * Check to see if a cluster of the needed size (or bigger) is
+ * available in this cylinder group.
+ */
+ for (i = len; i <= fs->fs_contigsumsize; i++)
+ if (cg_clustersum(cgp)[i] > 0)
+ break;
+ if (i > fs->fs_contigsumsize)
+ goto fail;
+ /*
+ * Search the cluster map to find a big enough cluster.
+ * We take the first one that we find, even if it is larger
+ * than we need as we prefer to get one close to the previous
+ * block allocation. We do not search before the current
+ * preference point as we do not want to allocate a block
+ * that is allocated before the previous one (as we will
+ * then have to wait for another pass of the elevator
+ * algorithm before it will be read). We prefer to fail and
+ * be recalled to try an allocation in the next cylinder group.
+ */
+ if (dtog(fs, bpref) != cg)
+ bpref = 0;
+ else
+ bpref = fragstoblks(fs, dtogd(fs, blknum(fs, bpref)));
+ mapp = &cg_clustersfree(cgp)[bpref / NBBY];
+ map = *mapp++;
+ bit = 1 << (bpref % NBBY);
+ for (run = 0, i = bpref; i < cgp->cg_nclusterblks; i++) {
+ if ((map & bit) == 0) {
+ run = 0;
+ } else {
+ run++;
+ if (run == len)
+ break;
+ }
+ if ((i & (NBBY - 1)) != (NBBY - 1)) {
+ bit <<= 1;
+ } else {
+ map = *mapp++;
+ bit = 1;
+ }
+ }
+ if (i == cgp->cg_nclusterblks)
+ goto fail;
+ /*
+ * Allocate the cluster that we have found.
+ */
+ bno = cg * fs->fs_fpg + blkstofrags(fs, i - run + 1);
+ len = blkstofrags(fs, len);
+ for (i = 0; i < len; i += fs->fs_frag)
+ if (ffs_alloccgblk(fs, cgp, bno + i) != bno + i)
+ panic("ffs_clusteralloc: lost block");
+ brelse(bp);
+ return (bno);
+
+fail:
+ brelse(bp);
+ return (0);
+}
+
+/*
+ * Determine whether an inode can be allocated.
+ *
+ * Check to see if an inode is available, and if it is,
+ * allocate it using the following policy:
+ * 1) allocate the requested inode.
+ * 2) allocate the next available inode after the requested
+ * inode in the specified cylinder group.
+ */
+static ino_t
+ffs_nodealloccg(ip, cg, ipref, mode)
+ struct inode *ip;
+ int cg;
+ daddr_t ipref;
+ int mode;
+{
+ register struct fs *fs;
+ register struct cg *cgp;
+ struct buf *bp;
+ int error, start, len, loc, map, i;
+
+ fs = ip->i_fs;
+ if (fs->fs_cs(fs, cg).cs_nifree == 0)
+ return (NULL);
+ error = bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)),
+ (int)fs->fs_cgsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (NULL);
+ }
+ cgp = (struct cg *)bp->b_data;
+ if (!cg_chkmagic(cgp) || cgp->cg_cs.cs_nifree == 0) {
+ brelse(bp);
+ return (NULL);
+ }
+ cgp->cg_time = time.tv_sec;
+ if (ipref) {
+ ipref %= fs->fs_ipg;
+ if (isclr(cg_inosused(cgp), ipref))
+ goto gotit;
+ }
+ start = cgp->cg_irotor / NBBY;
+ len = howmany(fs->fs_ipg - cgp->cg_irotor, NBBY);
+ loc = skpc(0xff, len, &cg_inosused(cgp)[start]);
+ if (loc == 0) {
+ len = start + 1;
+ start = 0;
+ loc = skpc(0xff, len, &cg_inosused(cgp)[0]);
+ if (loc == 0) {
+ printf("cg = %d, irotor = %d, fs = %s\n",
+ cg, cgp->cg_irotor, fs->fs_fsmnt);
+ panic("ffs_nodealloccg: map corrupted");
+ /* NOTREACHED */
+ }
+ }
+ i = start + len - loc;
+ map = cg_inosused(cgp)[i];
+ ipref = i * NBBY;
+ for (i = 1; i < (1 << NBBY); i <<= 1, ipref++) {
+ if ((map & i) == 0) {
+ cgp->cg_irotor = ipref;
+ goto gotit;
+ }
+ }
+ printf("fs = %s\n", fs->fs_fsmnt);
+ panic("ffs_nodealloccg: block not in map");
+ /* NOTREACHED */
+gotit:
+ setbit(cg_inosused(cgp), ipref);
+ cgp->cg_cs.cs_nifree--;
+ fs->fs_cstotal.cs_nifree--;
+ fs->fs_cs(fs, cg).cs_nifree--;
+ fs->fs_fmod = 1;
+ if ((mode & IFMT) == IFDIR) {
+ cgp->cg_cs.cs_ndir++;
+ fs->fs_cstotal.cs_ndir++;
+ fs->fs_cs(fs, cg).cs_ndir++;
+ }
+ bdwrite(bp);
+ return (cg * fs->fs_ipg + ipref);
+}
+
+/*
+ * Free a block or fragment.
+ *
+ * The specified block or fragment is placed back in the
+ * free map. If a fragment is deallocated, a possible
+ * block reassembly is checked.
+ */
+void
+ffs_blkfree(ip, bno, size)
+ register struct inode *ip;
+ daddr_t bno;
+ long size;
+{
+ register struct fs *fs;
+ register struct cg *cgp;
+ struct buf *bp;
+ daddr_t blkno;
+ int i, error, cg, blk, frags, bbase;
+
+ fs = ip->i_fs;
+ if ((u_int)size > fs->fs_bsize || fragoff(fs, size) != 0) {
+ printf("dev = 0x%x, bsize = %d, size = %d, fs = %s\n",
+ ip->i_dev, fs->fs_bsize, size, fs->fs_fsmnt);
+ panic("blkfree: bad size");
+ }
+ cg = dtog(fs, bno);
+ if ((u_int)bno >= fs->fs_size) {
+ printf("bad block %d, ino %d\n", bno, ip->i_number);
+ ffs_fserr(fs, ip->i_uid, "bad block");
+ return;
+ }
+ error = bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)),
+ (int)fs->fs_cgsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return;
+ }
+ cgp = (struct cg *)bp->b_data;
+ if (!cg_chkmagic(cgp)) {
+ brelse(bp);
+ return;
+ }
+ cgp->cg_time = time.tv_sec;
+ bno = dtogd(fs, bno);
+ if (size == fs->fs_bsize) {
+ blkno = fragstoblks(fs, bno);
+ if (ffs_isblock(fs, cg_blksfree(cgp), blkno)) {
+ printf("dev = 0x%x, block = %d, fs = %s\n",
+ ip->i_dev, bno, fs->fs_fsmnt);
+ panic("blkfree: freeing free block");
+ }
+ ffs_setblock(fs, cg_blksfree(cgp), blkno);
+ ffs_clusteracct(fs, cgp, blkno, 1);
+ cgp->cg_cs.cs_nbfree++;
+ fs->fs_cstotal.cs_nbfree++;
+ fs->fs_cs(fs, cg).cs_nbfree++;
+ i = cbtocylno(fs, bno);
+ cg_blks(fs, cgp, i)[cbtorpos(fs, bno)]++;
+ cg_blktot(cgp)[i]++;
+ } else {
+ bbase = bno - fragnum(fs, bno);
+ /*
+ * decrement the counts associated with the old frags
+ */
+ blk = blkmap(fs, cg_blksfree(cgp), bbase);
+ ffs_fragacct(fs, blk, cgp->cg_frsum, -1);
+ /*
+ * deallocate the fragment
+ */
+ frags = numfrags(fs, size);
+ for (i = 0; i < frags; i++) {
+ if (isset(cg_blksfree(cgp), bno + i)) {
+ printf("dev = 0x%x, block = %d, fs = %s\n",
+ ip->i_dev, bno + i, fs->fs_fsmnt);
+ panic("blkfree: freeing free frag");
+ }
+ setbit(cg_blksfree(cgp), bno + i);
+ }
+ cgp->cg_cs.cs_nffree += i;
+ fs->fs_cstotal.cs_nffree += i;
+ fs->fs_cs(fs, cg).cs_nffree += i;
+ /*
+ * add back in counts associated with the new frags
+ */
+ blk = blkmap(fs, cg_blksfree(cgp), bbase);
+ ffs_fragacct(fs, blk, cgp->cg_frsum, 1);
+ /*
+ * if a complete block has been reassembled, account for it
+ */
+ blkno = fragstoblks(fs, bbase);
+ if (ffs_isblock(fs, cg_blksfree(cgp), blkno)) {
+ cgp->cg_cs.cs_nffree -= fs->fs_frag;
+ fs->fs_cstotal.cs_nffree -= fs->fs_frag;
+ fs->fs_cs(fs, cg).cs_nffree -= fs->fs_frag;
+ ffs_clusteracct(fs, cgp, blkno, 1);
+ cgp->cg_cs.cs_nbfree++;
+ fs->fs_cstotal.cs_nbfree++;
+ fs->fs_cs(fs, cg).cs_nbfree++;
+ i = cbtocylno(fs, bbase);
+ cg_blks(fs, cgp, i)[cbtorpos(fs, bbase)]++;
+ cg_blktot(cgp)[i]++;
+ }
+ }
+ fs->fs_fmod = 1;
+ bdwrite(bp);
+}
+
+/*
+ * Free an inode.
+ *
+ * The specified inode is placed back in the free map.
+ */
+int
+ffs_vfree(ap)
+ struct vop_vfree_args /* {
+ struct vnode *a_pvp;
+ ino_t a_ino;
+ int a_mode;
+ } */ *ap;
+{
+ register struct fs *fs;
+ register struct cg *cgp;
+ register struct inode *pip;
+ ino_t ino = ap->a_ino;
+ struct buf *bp;
+ int error, cg;
+
+ pip = VTOI(ap->a_pvp);
+ fs = pip->i_fs;
+ if ((u_int)ino >= fs->fs_ipg * fs->fs_ncg)
+ panic("ifree: range: dev = 0x%x, ino = %d, fs = %s\n",
+ pip->i_dev, ino, fs->fs_fsmnt);
+ cg = ino_to_cg(fs, ino);
+ error = bread(pip->i_devvp, fsbtodb(fs, cgtod(fs, cg)),
+ (int)fs->fs_cgsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (0);
+ }
+ cgp = (struct cg *)bp->b_data;
+ if (!cg_chkmagic(cgp)) {
+ brelse(bp);
+ return (0);
+ }
+ cgp->cg_time = time.tv_sec;
+ ino %= fs->fs_ipg;
+ if (isclr(cg_inosused(cgp), ino)) {
+ printf("dev = 0x%x, ino = %d, fs = %s\n",
+ pip->i_dev, ino, fs->fs_fsmnt);
+ if (fs->fs_ronly == 0)
+ panic("ifree: freeing free inode");
+ }
+ clrbit(cg_inosused(cgp), ino);
+ if (ino < cgp->cg_irotor)
+ cgp->cg_irotor = ino;
+ cgp->cg_cs.cs_nifree++;
+ fs->fs_cstotal.cs_nifree++;
+ fs->fs_cs(fs, cg).cs_nifree++;
+ if ((ap->a_mode & IFMT) == IFDIR) {
+ cgp->cg_cs.cs_ndir--;
+ fs->fs_cstotal.cs_ndir--;
+ fs->fs_cs(fs, cg).cs_ndir--;
+ }
+ fs->fs_fmod = 1;
+ bdwrite(bp);
+ return (0);
+}
+
+/*
+ * Find a block of the specified size in the specified cylinder group.
+ *
+ * It is a panic if a request is made to find a block if none are
+ * available.
+ */
+static daddr_t
+ffs_mapsearch(fs, cgp, bpref, allocsiz)
+ register struct fs *fs;
+ register struct cg *cgp;
+ daddr_t bpref;
+ int allocsiz;
+{
+ daddr_t bno;
+ int start, len, loc, i;
+ int blk, field, subfield, pos;
+
+ /*
+ * find the fragment by searching through the free block
+ * map for an appropriate bit pattern
+ */
+ if (bpref)
+ start = dtogd(fs, bpref) / NBBY;
+ else
+ start = cgp->cg_frotor / NBBY;
+ len = howmany(fs->fs_fpg, NBBY) - start;
+ loc = scanc((u_int)len, (u_char *)&cg_blksfree(cgp)[start],
+ (u_char *)fragtbl[fs->fs_frag],
+ (u_char)(1 << (allocsiz - 1 + (fs->fs_frag % NBBY))));
+ if (loc == 0) {
+ len = start + 1;
+ start = 0;
+ loc = scanc((u_int)len, (u_char *)&cg_blksfree(cgp)[0],
+ (u_char *)fragtbl[fs->fs_frag],
+ (u_char)(1 << (allocsiz - 1 + (fs->fs_frag % NBBY))));
+ if (loc == 0) {
+ printf("start = %d, len = %d, fs = %s\n",
+ start, len, fs->fs_fsmnt);
+ panic("ffs_alloccg: map corrupted");
+ /* NOTREACHED */
+ }
+ }
+ bno = (start + len - loc) * NBBY;
+ cgp->cg_frotor = bno;
+ /*
+ * found the byte in the map
+ * sift through the bits to find the selected frag
+ */
+ for (i = bno + NBBY; bno < i; bno += fs->fs_frag) {
+ blk = blkmap(fs, cg_blksfree(cgp), bno);
+ blk <<= 1;
+ field = around[allocsiz];
+ subfield = inside[allocsiz];
+ for (pos = 0; pos <= fs->fs_frag - allocsiz; pos++) {
+ if ((blk & field) == subfield)
+ return (bno + pos);
+ field <<= 1;
+ subfield <<= 1;
+ }
+ }
+ printf("bno = %d, fs = %s\n", bno, fs->fs_fsmnt);
+ panic("ffs_alloccg: block not in map");
+ return (-1);
+}
+
+/*
+ * Update the cluster map because of an allocation or free.
+ *
+ * Cnt == 1 means free; cnt == -1 means allocating.
+ */
+void
+ffs_clusteracct(fs, cgp, blkno, cnt)
+ struct fs *fs;
+ struct cg *cgp;
+ daddr_t blkno;
+ int cnt;
+{
+ long *sump;
+ u_char *freemapp, *mapp;
+ int i, start, end, forw, back, map, bit;
+
+ if (fs->fs_contigsumsize <= 0)
+ return;
+ freemapp = cg_clustersfree(cgp);
+ sump = cg_clustersum(cgp);
+ /*
+ * Allocate or clear the actual block.
+ */
+ if (cnt > 0)
+ setbit(freemapp, blkno);
+ else
+ clrbit(freemapp, blkno);
+ /*
+ * Find the size of the cluster going forward.
+ */
+ start = blkno + 1;
+ end = start + fs->fs_contigsumsize;
+ if (end >= cgp->cg_nclusterblks)
+ end = cgp->cg_nclusterblks;
+ mapp = &freemapp[start / NBBY];
+ map = *mapp++;
+ bit = 1 << (start % NBBY);
+ for (i = start; i < end; i++) {
+ if ((map & bit) == 0)
+ break;
+ if ((i & (NBBY - 1)) != (NBBY - 1)) {
+ bit <<= 1;
+ } else {
+ map = *mapp++;
+ bit = 1;
+ }
+ }
+ forw = i - start;
+ /*
+ * Find the size of the cluster going backward.
+ */
+ start = blkno - 1;
+ end = start - fs->fs_contigsumsize;
+ if (end < 0)
+ end = -1;
+ mapp = &freemapp[start / NBBY];
+ map = *mapp--;
+ bit = 1 << (start % NBBY);
+ for (i = start; i > end; i--) {
+ if ((map & bit) == 0)
+ break;
+ if ((i & (NBBY - 1)) != 0) {
+ bit >>= 1;
+ } else {
+ map = *mapp--;
+ bit = 1 << (NBBY - 1);
+ }
+ }
+ back = start - i;
+ /*
+ * Account for old cluster and the possibly new forward and
+ * back clusters.
+ */
+ i = back + forw + 1;
+ if (i > fs->fs_contigsumsize)
+ i = fs->fs_contigsumsize;
+ sump[i] += cnt;
+ if (back > 0)
+ sump[back] -= cnt;
+ if (forw > 0)
+ sump[forw] -= cnt;
+}
+
+/*
+ * Fserr prints the name of a file system with an error diagnostic.
+ *
+ * The form of the error message is:
+ * fs: error message
+ */
+static void
+ffs_fserr(fs, uid, cp)
+ struct fs *fs;
+ u_int uid;
+ char *cp;
+{
+
+ log(LOG_ERR, "uid %d on %s: %s\n", uid, fs->fs_fsmnt, cp);
+}
diff --git a/sys/ufs/ffs/ffs_balloc.c b/sys/ufs/ffs/ffs_balloc.c
new file mode 100644
index 0000000..2addf95
--- /dev/null
+++ b/sys/ufs/ffs/ffs_balloc.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ffs_balloc.c 8.4 (Berkeley) 9/23/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+
+#include <vm/vm.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/ffs/fs.h>
+#include <ufs/ffs/ffs_extern.h>
+
+/*
+ * Balloc defines the structure of file system storage
+ * by allocating the physical blocks on a device given
+ * the inode and the logical block number in a file.
+ */
+int
+ffs_balloc(ip, bn, size, cred, bpp, flags)
+ register struct inode *ip;
+ register daddr_t bn;
+ int size;
+ struct ucred *cred;
+ struct buf **bpp;
+ int flags;
+{
+ register struct fs *fs;
+ register daddr_t nb;
+ struct buf *bp, *nbp;
+ struct vnode *vp = ITOV(ip);
+ struct indir indirs[NIADDR + 2];
+ daddr_t newb, lbn, *bap, pref;
+ int osize, nsize, num, i, error;
+
+ *bpp = NULL;
+ if (bn < 0)
+ return (EFBIG);
+ fs = ip->i_fs;
+ lbn = bn;
+
+ /*
+ * If the next write will extend the file into a new block,
+ * and the file is currently composed of a fragment
+ * this fragment has to be extended to be a full block.
+ */
+ nb = lblkno(fs, ip->i_size);
+ if (nb < NDADDR && nb < bn) {
+ osize = blksize(fs, ip, nb);
+ if (osize < fs->fs_bsize && osize > 0) {
+ error = ffs_realloccg(ip, nb,
+ ffs_blkpref(ip, nb, (int)nb, &ip->i_db[0]),
+ osize, (int)fs->fs_bsize, cred, &bp);
+ if (error)
+ return (error);
+ ip->i_size = (nb + 1) * fs->fs_bsize;
+ vnode_pager_setsize(vp, (u_long)ip->i_size);
+ ip->i_db[nb] = dbtofsb(fs, bp->b_blkno);
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ if (flags & B_SYNC)
+ bwrite(bp);
+ else
+ bawrite(bp);
+ }
+ }
+ /*
+ * The first NDADDR blocks are direct blocks
+ */
+ if (bn < NDADDR) {
+ nb = ip->i_db[bn];
+ if (nb != 0 && ip->i_size >= (bn + 1) * fs->fs_bsize) {
+ error = bread(vp, bn, fs->fs_bsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ *bpp = bp;
+ return (0);
+ }
+ if (nb != 0) {
+ /*
+ * Consider need to reallocate a fragment.
+ */
+ osize = fragroundup(fs, blkoff(fs, ip->i_size));
+ nsize = fragroundup(fs, size);
+ if (nsize <= osize) {
+ error = bread(vp, bn, osize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ } else {
+ error = ffs_realloccg(ip, bn,
+ ffs_blkpref(ip, bn, (int)bn, &ip->i_db[0]),
+ osize, nsize, cred, &bp);
+ if (error)
+ return (error);
+ }
+ } else {
+ if (ip->i_size < (bn + 1) * fs->fs_bsize)
+ nsize = fragroundup(fs, size);
+ else
+ nsize = fs->fs_bsize;
+ error = ffs_alloc(ip, bn,
+ ffs_blkpref(ip, bn, (int)bn, &ip->i_db[0]),
+ nsize, cred, &newb);
+ if (error)
+ return (error);
+ bp = getblk(vp, bn, nsize, 0, 0);
+ bp->b_blkno = fsbtodb(fs, newb);
+ if (flags & B_CLRBUF)
+ clrbuf(bp);
+ }
+ ip->i_db[bn] = dbtofsb(fs, bp->b_blkno);
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ *bpp = bp;
+ return (0);
+ }
+ /*
+ * Determine the number of levels of indirection.
+ */
+ pref = 0;
+ if (error = ufs_getlbns(vp, bn, indirs, &num))
+ return(error);
+#ifdef DIAGNOSTIC
+ if (num < 1)
+ panic ("ffs_balloc: ufs_bmaparray returned indirect block\n");
+#endif
+ /*
+ * Fetch the first indirect block allocating if necessary.
+ */
+ --num;
+ nb = ip->i_ib[indirs[0].in_off];
+ if (nb == 0) {
+ pref = ffs_blkpref(ip, lbn, 0, (daddr_t *)0);
+ if (error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize,
+ cred, &newb))
+ return (error);
+ nb = newb;
+ bp = getblk(vp, indirs[1].in_lbn, fs->fs_bsize, 0, 0);
+ bp->b_blkno = fsbtodb(fs, newb);
+ clrbuf(bp);
+ /*
+ * Write synchronously so that indirect blocks
+ * never point at garbage.
+ */
+ if (error = bwrite(bp)) {
+ ffs_blkfree(ip, nb, fs->fs_bsize);
+ return (error);
+ }
+ ip->i_ib[indirs[0].in_off] = newb;
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ }
+ /*
+ * Fetch through the indirect blocks, allocating as necessary.
+ */
+ for (i = 1;;) {
+ error = bread(vp,
+ indirs[i].in_lbn, (int)fs->fs_bsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ bap = (daddr_t *)bp->b_data;
+ nb = bap[indirs[i].in_off];
+ if (i == num)
+ break;
+ i += 1;
+ if (nb != 0) {
+ brelse(bp);
+ continue;
+ }
+ if (pref == 0)
+ pref = ffs_blkpref(ip, lbn, 0, (daddr_t *)0);
+ if (error =
+ ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred, &newb)) {
+ brelse(bp);
+ return (error);
+ }
+ nb = newb;
+ nbp = getblk(vp, indirs[i].in_lbn, fs->fs_bsize, 0, 0);
+ nbp->b_blkno = fsbtodb(fs, nb);
+ clrbuf(nbp);
+ /*
+ * Write synchronously so that indirect blocks
+ * never point at garbage.
+ */
+ if (error = bwrite(nbp)) {
+ ffs_blkfree(ip, nb, fs->fs_bsize);
+ brelse(bp);
+ return (error);
+ }
+ bap[indirs[i - 1].in_off] = nb;
+ /*
+ * If required, write synchronously, otherwise use
+ * delayed write.
+ */
+ if (flags & B_SYNC) {
+ bwrite(bp);
+ } else {
+ bdwrite(bp);
+ }
+ }
+ /*
+ * Get the data block, allocating if necessary.
+ */
+ if (nb == 0) {
+ pref = ffs_blkpref(ip, lbn, indirs[i].in_off, &bap[0]);
+ if (error = ffs_alloc(ip,
+ lbn, pref, (int)fs->fs_bsize, cred, &newb)) {
+ brelse(bp);
+ return (error);
+ }
+ nb = newb;
+ nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0);
+ nbp->b_blkno = fsbtodb(fs, nb);
+ if (flags & B_CLRBUF)
+ clrbuf(nbp);
+ bap[indirs[i].in_off] = nb;
+ /*
+ * If required, write synchronously, otherwise use
+ * delayed write.
+ */
+ if (flags & B_SYNC) {
+ bwrite(bp);
+ } else {
+ bdwrite(bp);
+ }
+ *bpp = nbp;
+ return (0);
+ }
+ brelse(bp);
+ if (flags & B_CLRBUF) {
+ error = bread(vp, lbn, (int)fs->fs_bsize, NOCRED, &nbp);
+ if (error) {
+ brelse(nbp);
+ return (error);
+ }
+ } else {
+ nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0);
+ nbp->b_blkno = fsbtodb(fs, nb);
+ }
+ *bpp = nbp;
+ return (0);
+}
diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h
new file mode 100644
index 0000000..6c30389
--- /dev/null
+++ b/sys/ufs/ffs/ffs_extern.h
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ffs_extern.h 8.3 (Berkeley) 4/16/94
+ */
+
+struct buf;
+struct fid;
+struct fs;
+struct inode;
+struct mount;
+struct nameidata;
+struct proc;
+struct statfs;
+struct timeval;
+struct ucred;
+struct uio;
+struct vnode;
+struct mbuf;
+
+__BEGIN_DECLS
+int ffs_alloc __P((struct inode *,
+ daddr_t, daddr_t, int, struct ucred *, daddr_t *));
+int ffs_balloc __P((struct inode *,
+ daddr_t, int, struct ucred *, struct buf **, int));
+int ffs_blkatoff __P((struct vop_blkatoff_args *));
+void ffs_blkfree __P((struct inode *, daddr_t, long));
+daddr_t ffs_blkpref __P((struct inode *, daddr_t, int, daddr_t *));
+int ffs_bmap __P((struct vop_bmap_args *));
+void ffs_clrblock __P((struct fs *, u_char *, daddr_t));
+int ffs_fhtovp __P((struct mount *, struct fid *, struct mbuf *,
+ struct vnode **, int *, struct ucred **));
+void ffs_fragacct __P((struct fs *, int, long [], int));
+int ffs_fsync __P((struct vop_fsync_args *));
+int ffs_init __P((void));
+int ffs_isblock __P((struct fs *, u_char *, daddr_t));
+int ffs_mount __P((struct mount *,
+ char *, caddr_t, struct nameidata *, struct proc *));
+int ffs_mountfs __P((struct vnode *, struct mount *, struct proc *));
+int ffs_mountroot __P((void));
+int ffs_read __P((struct vop_read_args *));
+int ffs_reallocblks __P((struct vop_reallocblks_args *));
+int ffs_realloccg __P((struct inode *,
+ daddr_t, daddr_t, int, int, struct ucred *, struct buf **));
+int ffs_reclaim __P((struct vop_reclaim_args *));
+void ffs_setblock __P((struct fs *, u_char *, daddr_t));
+int ffs_statfs __P((struct mount *, struct statfs *, struct proc *));
+int ffs_sync __P((struct mount *, int, struct ucred *, struct proc *));
+int ffs_truncate __P((struct vop_truncate_args *));
+int ffs_unmount __P((struct mount *, int, struct proc *));
+int ffs_update __P((struct vop_update_args *));
+int ffs_valloc __P((struct vop_valloc_args *));
+int ffs_vfree __P((struct vop_vfree_args *));
+int ffs_vget __P((struct mount *, ino_t, struct vnode **));
+int ffs_vptofh __P((struct vnode *, struct fid *));
+int ffs_write __P((struct vop_write_args *));
+
+int bwrite(); /* FFS needs a bwrite routine. XXX */
+
+#ifdef DIAGNOSTIC
+void ffs_checkoverlap __P((struct buf *, struct inode *));
+#endif
+__END_DECLS
+
+extern int (**ffs_vnodeop_p)();
+extern int (**ffs_specop_p)();
+#ifdef FIFO
+extern int (**ffs_fifoop_p)();
+#define FFS_FIFOOPS ffs_fifoop_p
+#else
+#define FFS_FIFOOPS NULL
+#endif
diff --git a/sys/ufs/ffs/ffs_inode.c b/sys/ufs/ffs/ffs_inode.c
new file mode 100644
index 0000000..cf4fae3
--- /dev/null
+++ b/sys/ufs/ffs/ffs_inode.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ffs_inode.c 8.5 (Berkeley) 12/30/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/trace.h>
+#include <sys/resourcevar.h>
+
+#include <vm/vm.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/ffs/fs.h>
+#include <ufs/ffs/ffs_extern.h>
+
+static int ffs_indirtrunc __P((struct inode *, daddr_t, daddr_t, daddr_t, int,
+ long *));
+
+int
+ffs_init()
+{
+ return (ufs_init());
+}
+
+/*
+ * Update the access, modified, and inode change times as specified by the
+ * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is
+ * used to specify that the inode needs to be updated but that the times have
+ * already been set. The access and modified times are taken from the second
+ * and third parameters; the inode change time is always taken from the current
+ * time. If waitfor is set, then wait for the disk write of the inode to
+ * complete.
+ */
+int
+ffs_update(ap)
+ struct vop_update_args /* {
+ struct vnode *a_vp;
+ struct timeval *a_access;
+ struct timeval *a_modify;
+ int a_waitfor;
+ } */ *ap;
+{
+ register struct fs *fs;
+ struct buf *bp;
+ struct inode *ip;
+ int error;
+
+ ip = VTOI(ap->a_vp);
+ if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) {
+ ip->i_flag &=
+ ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
+ return (0);
+ }
+ if ((ip->i_flag &
+ (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0)
+ return (0);
+ if (ip->i_flag & IN_ACCESS)
+ ip->i_atime.ts_sec = ap->a_access->tv_sec;
+ if (ip->i_flag & IN_UPDATE) {
+ ip->i_mtime.ts_sec = ap->a_modify->tv_sec;
+ ip->i_modrev++;
+ }
+ if (ip->i_flag & IN_CHANGE)
+ ip->i_ctime.ts_sec = time.tv_sec;
+ ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
+ fs = ip->i_fs;
+ /*
+ * Ensure that uid and gid are correct. This is a temporary
+ * fix until fsck has been changed to do the update.
+ */
+ if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */
+ ip->i_din.di_ouid = ip->i_uid; /* XXX */
+ ip->i_din.di_ogid = ip->i_gid; /* XXX */
+ } /* XXX */
+ if (error = bread(ip->i_devvp,
+ fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
+ (int)fs->fs_bsize, NOCRED, &bp)) {
+ brelse(bp);
+ return (error);
+ }
+ *((struct dinode *)bp->b_data +
+ ino_to_fsbo(fs, ip->i_number)) = ip->i_din;
+ if (ap->a_waitfor)
+ return (bwrite(bp));
+ else {
+ bdwrite(bp);
+ return (0);
+ }
+}
+
+#define SINGLE 0 /* index of single indirect block */
+#define DOUBLE 1 /* index of double indirect block */
+#define TRIPLE 2 /* index of triple indirect block */
+/*
+ * Truncate the inode oip to at most length size, freeing the
+ * disk blocks.
+ */
+int
+ffs_truncate(ap)
+ struct vop_truncate_args /* {
+ struct vnode *a_vp;
+ off_t a_length;
+ int a_flags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *ovp = ap->a_vp;
+ register daddr_t lastblock;
+ register struct inode *oip;
+ daddr_t bn, lbn, lastiblock[NIADDR], indir_lbn[NIADDR];
+ daddr_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR];
+ off_t length = ap->a_length;
+ register struct fs *fs;
+ struct buf *bp;
+ int offset, size, level;
+ long count, nblocks, vflags, blocksreleased = 0;
+ struct timeval tv;
+ register int i;
+ int aflags, error, allerror;
+ off_t osize;
+
+ oip = VTOI(ovp);
+ tv = time;
+ if (ovp->v_type == VLNK &&
+ oip->i_size < ovp->v_mount->mnt_maxsymlinklen) {
+#ifdef DIAGNOSTIC
+ if (length != 0)
+ panic("ffs_truncate: partial truncate of symlink");
+#endif
+ bzero((char *)&oip->i_shortlink, (u_int)oip->i_size);
+ oip->i_size = 0;
+ oip->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (VOP_UPDATE(ovp, &tv, &tv, 1));
+ }
+ if (oip->i_size == length) {
+ oip->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (VOP_UPDATE(ovp, &tv, &tv, 0));
+ }
+#ifdef QUOTA
+ if (error = getinoquota(oip))
+ return (error);
+#endif
+ vnode_pager_setsize(ovp, (u_long)length);
+ fs = oip->i_fs;
+ osize = oip->i_size;
+ /*
+ * Lengthen the size of the file. We must ensure that the
+ * last byte of the file is allocated. Since the smallest
+ * value of oszie is 0, length will be at least 1.
+ */
+ if (osize < length) {
+ offset = blkoff(fs, length - 1);
+ lbn = lblkno(fs, length - 1);
+ aflags = B_CLRBUF;
+ if (ap->a_flags & IO_SYNC)
+ aflags |= B_SYNC;
+ if (error = ffs_balloc(oip, lbn, offset + 1, ap->a_cred, &bp,
+ aflags))
+ return (error);
+ oip->i_size = length;
+ (void) vnode_pager_uncache(ovp);
+ if (aflags & IO_SYNC)
+ bwrite(bp);
+ else
+ bawrite(bp);
+ oip->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (VOP_UPDATE(ovp, &tv, &tv, 1));
+ }
+ /*
+ * Shorten the size of the file. If the file is not being
+ * truncated to a block boundry, the contents of the
+ * partial block following the end of the file must be
+ * zero'ed in case it ever become accessable again because
+ * of subsequent file growth.
+ */
+ offset = blkoff(fs, length);
+ if (offset == 0) {
+ oip->i_size = length;
+ } else {
+ lbn = lblkno(fs, length);
+ aflags = B_CLRBUF;
+ if (ap->a_flags & IO_SYNC)
+ aflags |= B_SYNC;
+ if (error = ffs_balloc(oip, lbn, offset, ap->a_cred, &bp,
+ aflags))
+ return (error);
+ oip->i_size = length;
+ size = blksize(fs, oip, lbn);
+ (void) vnode_pager_uncache(ovp);
+ bzero((char *)bp->b_data + offset, (u_int)(size - offset));
+ allocbuf(bp, size);
+ if (aflags & IO_SYNC)
+ bwrite(bp);
+ else
+ bawrite(bp);
+ }
+ /*
+ * Calculate index into inode's block list of
+ * last direct and indirect blocks (if any)
+ * which we want to keep. Lastblock is -1 when
+ * the file is truncated to 0.
+ */
+ lastblock = lblkno(fs, length + fs->fs_bsize - 1) - 1;
+ lastiblock[SINGLE] = lastblock - NDADDR;
+ lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs);
+ lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs);
+ nblocks = btodb(fs->fs_bsize);
+ /*
+ * Update file and block pointers on disk before we start freeing
+ * blocks. If we crash before free'ing blocks below, the blocks
+ * will be returned to the free list. lastiblock values are also
+ * normalized to -1 for calls to ffs_indirtrunc below.
+ */
+ bcopy((caddr_t)&oip->i_db[0], (caddr_t)oldblks, sizeof oldblks);
+ for (level = TRIPLE; level >= SINGLE; level--)
+ if (lastiblock[level] < 0) {
+ oip->i_ib[level] = 0;
+ lastiblock[level] = -1;
+ }
+ for (i = NDADDR - 1; i > lastblock; i--)
+ oip->i_db[i] = 0;
+ oip->i_flag |= IN_CHANGE | IN_UPDATE;
+ if (error = VOP_UPDATE(ovp, &tv, &tv, MNT_WAIT))
+ allerror = error;
+ /*
+ * Having written the new inode to disk, save its new configuration
+ * and put back the old block pointers long enough to process them.
+ * Note that we save the new block configuration so we can check it
+ * when we are done.
+ */
+ bcopy((caddr_t)&oip->i_db[0], (caddr_t)newblks, sizeof newblks);
+ bcopy((caddr_t)oldblks, (caddr_t)&oip->i_db[0], sizeof oldblks);
+ oip->i_size = osize;
+ vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA;
+ allerror = vinvalbuf(ovp, vflags, ap->a_cred, ap->a_p, 0, 0);
+
+ /*
+ * Indirect blocks first.
+ */
+ indir_lbn[SINGLE] = -NDADDR;
+ indir_lbn[DOUBLE] = indir_lbn[SINGLE] - NINDIR(fs) - 1;
+ indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - NINDIR(fs) * NINDIR(fs) - 1;
+ for (level = TRIPLE; level >= SINGLE; level--) {
+ bn = oip->i_ib[level];
+ if (bn != 0) {
+ error = ffs_indirtrunc(oip, indir_lbn[level],
+ fsbtodb(fs, bn), lastiblock[level], level, &count);
+ if (error)
+ allerror = error;
+ blocksreleased += count;
+ if (lastiblock[level] < 0) {
+ oip->i_ib[level] = 0;
+ ffs_blkfree(oip, bn, fs->fs_bsize);
+ blocksreleased += nblocks;
+ }
+ }
+ if (lastiblock[level] >= 0)
+ goto done;
+ }
+
+ /*
+ * All whole direct blocks or frags.
+ */
+ for (i = NDADDR - 1; i > lastblock; i--) {
+ register long bsize;
+
+ bn = oip->i_db[i];
+ if (bn == 0)
+ continue;
+ oip->i_db[i] = 0;
+ bsize = blksize(fs, oip, i);
+ ffs_blkfree(oip, bn, bsize);
+ blocksreleased += btodb(bsize);
+ }
+ if (lastblock < 0)
+ goto done;
+
+ /*
+ * Finally, look for a change in size of the
+ * last direct block; release any frags.
+ */
+ bn = oip->i_db[lastblock];
+ if (bn != 0) {
+ long oldspace, newspace;
+
+ /*
+ * Calculate amount of space we're giving
+ * back as old block size minus new block size.
+ */
+ oldspace = blksize(fs, oip, lastblock);
+ oip->i_size = length;
+ newspace = blksize(fs, oip, lastblock);
+ if (newspace == 0)
+ panic("itrunc: newspace");
+ if (oldspace - newspace > 0) {
+ /*
+ * Block number of space to be free'd is
+ * the old block # plus the number of frags
+ * required for the storage we're keeping.
+ */
+ bn += numfrags(fs, newspace);
+ ffs_blkfree(oip, bn, oldspace - newspace);
+ blocksreleased += btodb(oldspace - newspace);
+ }
+ }
+done:
+#ifdef DIAGNOSTIC
+ for (level = SINGLE; level <= TRIPLE; level++)
+ if (newblks[NDADDR + level] != oip->i_ib[level])
+ panic("itrunc1");
+ for (i = 0; i < NDADDR; i++)
+ if (newblks[i] != oip->i_db[i])
+ panic("itrunc2");
+ if (length == 0 &&
+ (ovp->v_dirtyblkhd.lh_first || ovp->v_cleanblkhd.lh_first))
+ panic("itrunc3");
+#endif /* DIAGNOSTIC */
+ /*
+ * Put back the real size.
+ */
+ oip->i_size = length;
+ oip->i_blocks -= blocksreleased;
+ if (oip->i_blocks < 0) /* sanity */
+ oip->i_blocks = 0;
+ oip->i_flag |= IN_CHANGE;
+#ifdef QUOTA
+ (void) chkdq(oip, -blocksreleased, NOCRED, 0);
+#endif
+ return (allerror);
+}
+
+/*
+ * Release blocks associated with the inode ip and stored in the indirect
+ * block bn. Blocks are free'd in LIFO order up to (but not including)
+ * lastbn. If level is greater than SINGLE, the block is an indirect block
+ * and recursive calls to indirtrunc must be used to cleanse other indirect
+ * blocks.
+ *
+ * NB: triple indirect blocks are untested.
+ */
+static int
+ffs_indirtrunc(ip, lbn, dbn, lastbn, level, countp)
+ register struct inode *ip;
+ daddr_t lbn, lastbn;
+ daddr_t dbn;
+ int level;
+ long *countp;
+{
+ register int i;
+ struct buf *bp;
+ register struct fs *fs = ip->i_fs;
+ register daddr_t *bap;
+ struct vnode *vp;
+ daddr_t *copy, nb, nlbn, last;
+ long blkcount, factor;
+ int nblocks, blocksreleased = 0;
+ int error = 0, allerror = 0;
+
+ /*
+ * Calculate index in current block of last
+ * block to be kept. -1 indicates the entire
+ * block so we need not calculate the index.
+ */
+ factor = 1;
+ for (i = SINGLE; i < level; i++)
+ factor *= NINDIR(fs);
+ last = lastbn;
+ if (lastbn > 0)
+ last /= factor;
+ nblocks = btodb(fs->fs_bsize);
+ /*
+ * Get buffer of block pointers, zero those entries corresponding
+ * to blocks to be free'd, and update on disk copy first. Since
+ * double(triple) indirect before single(double) indirect, calls
+ * to bmap on these blocks will fail. However, we already have
+ * the on disk address, so we have to set the b_blkno field
+ * explicitly instead of letting bread do everything for us.
+ */
+ vp = ITOV(ip);
+ bp = getblk(vp, lbn, (int)fs->fs_bsize, 0, 0);
+ if (bp->b_flags & (B_DONE | B_DELWRI)) {
+ /* Braces must be here in case trace evaluates to nothing. */
+ trace(TR_BREADHIT, pack(vp, fs->fs_bsize), lbn);
+ } else {
+ trace(TR_BREADMISS, pack(vp, fs->fs_bsize), lbn);
+ curproc->p_stats->p_ru.ru_inblock++; /* pay for read */
+ bp->b_flags |= B_READ;
+ if (bp->b_bcount > bp->b_bufsize)
+ panic("ffs_indirtrunc: bad buffer size");
+ bp->b_blkno = dbn;
+ VOP_STRATEGY(bp);
+ error = biowait(bp);
+ }
+ if (error) {
+ brelse(bp);
+ *countp = 0;
+ return (error);
+ }
+
+ bap = (daddr_t *)bp->b_data;
+ MALLOC(copy, daddr_t *, fs->fs_bsize, M_TEMP, M_WAITOK);
+ bcopy((caddr_t)bap, (caddr_t)copy, (u_int)fs->fs_bsize);
+ bzero((caddr_t)&bap[last + 1],
+ (u_int)(NINDIR(fs) - (last + 1)) * sizeof (daddr_t));
+ if (last == -1)
+ bp->b_flags |= B_INVAL;
+ error = bwrite(bp);
+ if (error)
+ allerror = error;
+ bap = copy;
+
+ /*
+ * Recursively free totally unused blocks.
+ */
+ for (i = NINDIR(fs) - 1, nlbn = lbn + 1 - i * factor; i > last;
+ i--, nlbn += factor) {
+ nb = bap[i];
+ if (nb == 0)
+ continue;
+ if (level > SINGLE) {
+ if (error = ffs_indirtrunc(ip, nlbn,
+ fsbtodb(fs, nb), (daddr_t)-1, level - 1, &blkcount))
+ allerror = error;
+ blocksreleased += blkcount;
+ }
+ ffs_blkfree(ip, nb, fs->fs_bsize);
+ blocksreleased += nblocks;
+ }
+
+ /*
+ * Recursively free last partial block.
+ */
+ if (level > SINGLE && lastbn >= 0) {
+ last = lastbn % factor;
+ nb = bap[i];
+ if (nb != 0) {
+ if (error = ffs_indirtrunc(ip, nlbn, fsbtodb(fs, nb),
+ last, level - 1, &blkcount))
+ allerror = error;
+ blocksreleased += blkcount;
+ }
+ }
+ FREE(copy, M_TEMP);
+ *countp = blocksreleased;
+ return (allerror);
+}
diff --git a/sys/ufs/ffs/ffs_subr.c b/sys/ufs/ffs/ffs_subr.c
new file mode 100644
index 0000000..c251b16
--- /dev/null
+++ b/sys/ufs/ffs/ffs_subr.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ffs_subr.c 8.2 (Berkeley) 9/21/93
+ */
+
+#include <sys/param.h>
+#include <ufs/ffs/fs.h>
+
+#ifdef KERNEL
+#include <sys/systm.h>
+#include <sys/vnode.h>
+#include <ufs/ffs/ffs_extern.h>
+#include <sys/buf.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+
+/*
+ * Return buffer with the contents of block "offset" from the beginning of
+ * directory "ip". If "res" is non-zero, fill it in with a pointer to the
+ * remaining space in the directory.
+ */
+int
+ffs_blkatoff(ap)
+ struct vop_blkatoff_args /* {
+ struct vnode *a_vp;
+ off_t a_offset;
+ char **a_res;
+ struct buf **a_bpp;
+ } */ *ap;
+{
+ struct inode *ip;
+ register struct fs *fs;
+ struct buf *bp;
+ daddr_t lbn;
+ int bsize, error;
+
+ ip = VTOI(ap->a_vp);
+ fs = ip->i_fs;
+ lbn = lblkno(fs, ap->a_offset);
+ bsize = blksize(fs, ip, lbn);
+
+ *ap->a_bpp = NULL;
+ if (error = bread(ap->a_vp, lbn, bsize, NOCRED, &bp)) {
+ brelse(bp);
+ return (error);
+ }
+ if (ap->a_res)
+ *ap->a_res = (char *)bp->b_data + blkoff(fs, ap->a_offset);
+ *ap->a_bpp = bp;
+ return (0);
+}
+#endif
+
+/*
+ * Update the frsum fields to reflect addition or deletion
+ * of some frags.
+ */
+void
+ffs_fragacct(fs, fragmap, fraglist, cnt)
+ struct fs *fs;
+ int fragmap;
+ long fraglist[];
+ int cnt;
+{
+ int inblk;
+ register int field, subfield;
+ register int siz, pos;
+
+ inblk = (int)(fragtbl[fs->fs_frag][fragmap]) << 1;
+ fragmap <<= 1;
+ for (siz = 1; siz < fs->fs_frag; siz++) {
+ if ((inblk & (1 << (siz + (fs->fs_frag % NBBY)))) == 0)
+ continue;
+ field = around[siz];
+ subfield = inside[siz];
+ for (pos = siz; pos <= fs->fs_frag; pos++) {
+ if ((fragmap & field) == subfield) {
+ fraglist[siz] += cnt;
+ pos += siz;
+ field <<= siz;
+ subfield <<= siz;
+ }
+ field <<= 1;
+ subfield <<= 1;
+ }
+ }
+}
+
+#if defined(KERNEL) && defined(DIAGNOSTIC)
+void
+ffs_checkoverlap(bp, ip)
+ struct buf *bp;
+ struct inode *ip;
+{
+ register struct buf *ebp, *ep;
+ register daddr_t start, last;
+ struct vnode *vp;
+
+ ebp = &buf[nbuf];
+ start = bp->b_blkno;
+ last = start + btodb(bp->b_bcount) - 1;
+ for (ep = buf; ep < ebp; ep++) {
+ if (ep == bp || (ep->b_flags & B_INVAL) ||
+ ep->b_vp == NULLVP)
+ continue;
+ if (VOP_BMAP(ep->b_vp, (daddr_t)0, &vp, (daddr_t)0, NULL))
+ continue;
+ if (vp != ip->i_devvp)
+ continue;
+ /* look for overlap */
+ if (ep->b_bcount == 0 || ep->b_blkno > last ||
+ ep->b_blkno + btodb(ep->b_bcount) <= start)
+ continue;
+ vprint("Disk overlap", vp);
+ (void)printf("\tstart %d, end %d overlap start %d, end %d\n",
+ start, last, ep->b_blkno,
+ ep->b_blkno + btodb(ep->b_bcount) - 1);
+ panic("Disk buffer overlap");
+ }
+}
+#endif /* DIAGNOSTIC */
+
+/*
+ * block operations
+ *
+ * check if a block is available
+ */
+int
+ffs_isblock(fs, cp, h)
+ struct fs *fs;
+ unsigned char *cp;
+ daddr_t h;
+{
+ unsigned char mask;
+
+ switch ((int)fs->fs_frag) {
+ case 8:
+ return (cp[h] == 0xff);
+ case 4:
+ mask = 0x0f << ((h & 0x1) << 2);
+ return ((cp[h >> 1] & mask) == mask);
+ case 2:
+ mask = 0x03 << ((h & 0x3) << 1);
+ return ((cp[h >> 2] & mask) == mask);
+ case 1:
+ mask = 0x01 << (h & 0x7);
+ return ((cp[h >> 3] & mask) == mask);
+ default:
+ panic("ffs_isblock");
+ }
+}
+
+/*
+ * take a block out of the map
+ */
+void
+ffs_clrblock(fs, cp, h)
+ struct fs *fs;
+ u_char *cp;
+ daddr_t h;
+{
+
+ switch ((int)fs->fs_frag) {
+ case 8:
+ cp[h] = 0;
+ return;
+ case 4:
+ cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2));
+ return;
+ case 2:
+ cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1));
+ return;
+ case 1:
+ cp[h >> 3] &= ~(0x01 << (h & 0x7));
+ return;
+ default:
+ panic("ffs_clrblock");
+ }
+}
+
+/*
+ * put a block into the map
+ */
+void
+ffs_setblock(fs, cp, h)
+ struct fs *fs;
+ unsigned char *cp;
+ daddr_t h;
+{
+
+ switch ((int)fs->fs_frag) {
+
+ case 8:
+ cp[h] = 0xff;
+ return;
+ case 4:
+ cp[h >> 1] |= (0x0f << ((h & 0x1) << 2));
+ return;
+ case 2:
+ cp[h >> 2] |= (0x03 << ((h & 0x3) << 1));
+ return;
+ case 1:
+ cp[h >> 3] |= (0x01 << (h & 0x7));
+ return;
+ default:
+ panic("ffs_setblock");
+ }
+}
diff --git a/sys/ufs/ffs/ffs_tables.c b/sys/ufs/ffs/ffs_tables.c
new file mode 100644
index 0000000..8cf46b0
--- /dev/null
+++ b/sys/ufs/ffs/ffs_tables.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ffs_tables.c 8.1 (Berkeley) 6/11/93
+ */
+
+#include <sys/param.h>
+
+/*
+ * Bit patterns for identifying fragments in the block map
+ * used as ((map & around) == inside)
+ */
+int around[9] = {
+ 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff
+};
+int inside[9] = {
+ 0x0, 0x2, 0x6, 0xe, 0x1e, 0x3e, 0x7e, 0xfe, 0x1fe
+};
+
+/*
+ * Given a block map bit pattern, the frag tables tell whether a
+ * particular size fragment is available.
+ *
+ * used as:
+ * if ((1 << (size - 1)) & fragtbl[fs->fs_frag][map] {
+ * at least one fragment of the indicated size is available
+ * }
+ *
+ * These tables are used by the scanc instruction on the VAX to
+ * quickly find an appropriate fragment.
+ */
+u_char fragtbl124[256] = {
+ 0x00, 0x16, 0x16, 0x2a, 0x16, 0x16, 0x26, 0x4e,
+ 0x16, 0x16, 0x16, 0x3e, 0x2a, 0x3e, 0x4e, 0x8a,
+ 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e,
+ 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e,
+ 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e,
+ 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e,
+ 0x2a, 0x3e, 0x3e, 0x2a, 0x3e, 0x3e, 0x2e, 0x6e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x2a, 0x3e, 0x6e, 0xaa,
+ 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e,
+ 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e,
+ 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e,
+ 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e,
+ 0x26, 0x36, 0x36, 0x2e, 0x36, 0x36, 0x26, 0x6e,
+ 0x36, 0x36, 0x36, 0x3e, 0x2e, 0x3e, 0x6e, 0xae,
+ 0x4e, 0x5e, 0x5e, 0x6e, 0x5e, 0x5e, 0x6e, 0x4e,
+ 0x5e, 0x5e, 0x5e, 0x7e, 0x6e, 0x7e, 0x4e, 0xce,
+ 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e,
+ 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e,
+ 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e,
+ 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e,
+ 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e,
+ 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x7e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x7e, 0xbe,
+ 0x2a, 0x3e, 0x3e, 0x2a, 0x3e, 0x3e, 0x2e, 0x6e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x2a, 0x3e, 0x6e, 0xaa,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x7e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x7e, 0xbe,
+ 0x4e, 0x5e, 0x5e, 0x6e, 0x5e, 0x5e, 0x6e, 0x4e,
+ 0x5e, 0x5e, 0x5e, 0x7e, 0x6e, 0x7e, 0x4e, 0xce,
+ 0x8a, 0x9e, 0x9e, 0xaa, 0x9e, 0x9e, 0xae, 0xce,
+ 0x9e, 0x9e, 0x9e, 0xbe, 0xaa, 0xbe, 0xce, 0x8a,
+};
+
+u_char fragtbl8[256] = {
+ 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04,
+ 0x01, 0x01, 0x01, 0x03, 0x02, 0x03, 0x04, 0x08,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05,
+ 0x02, 0x03, 0x03, 0x02, 0x04, 0x05, 0x08, 0x10,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05,
+ 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
+ 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06,
+ 0x04, 0x05, 0x05, 0x06, 0x08, 0x09, 0x10, 0x20,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05,
+ 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05,
+ 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11,
+ 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06,
+ 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0a,
+ 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04,
+ 0x08, 0x09, 0x09, 0x0a, 0x10, 0x11, 0x20, 0x40,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05,
+ 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05,
+ 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05,
+ 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07,
+ 0x05, 0x05, 0x05, 0x07, 0x09, 0x09, 0x11, 0x21,
+ 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06,
+ 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0a,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07,
+ 0x02, 0x03, 0x03, 0x02, 0x06, 0x07, 0x0a, 0x12,
+ 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04,
+ 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x04, 0x0c,
+ 0x08, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x0a, 0x0c,
+ 0x10, 0x11, 0x11, 0x12, 0x20, 0x21, 0x40, 0x80,
+};
+
+/*
+ * The actual fragtbl array.
+ */
+u_char *fragtbl[MAXFRAG + 1] = {
+ 0, fragtbl124, fragtbl124, 0, fragtbl124, 0, 0, 0, fragtbl8,
+};
diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c
new file mode 100644
index 0000000..41f4a0d
--- /dev/null
+++ b/sys/ufs/ffs/ffs_vfsops.c
@@ -0,0 +1,848 @@
+/*
+ * Copyright (c) 1989, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ffs_vfsops.c 8.8 (Berkeley) 4/18/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <sys/socket.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/mbuf.h>
+#include <sys/file.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/ffs/fs.h>
+#include <ufs/ffs/ffs_extern.h>
+
+int ffs_sbupdate __P((struct ufsmount *, int));
+
+struct vfsops ufs_vfsops = {
+ ffs_mount,
+ ufs_start,
+ ffs_unmount,
+ ufs_root,
+ ufs_quotactl,
+ ffs_statfs,
+ ffs_sync,
+ ffs_vget,
+ ffs_fhtovp,
+ ffs_vptofh,
+ ffs_init,
+};
+
+extern u_long nextgennumber;
+
+/*
+ * Called by main() when ufs is going to be mounted as root.
+ *
+ * Name is updated by mount(8) after booting.
+ */
+#define ROOTNAME "root_device"
+
+int
+ffs_mountroot()
+{
+ extern struct vnode *rootvp;
+ register struct fs *fs;
+ register struct mount *mp;
+ struct proc *p = curproc; /* XXX */
+ struct ufsmount *ump;
+ u_int size;
+ int error;
+
+ /*
+ * Get vnodes for swapdev and rootdev.
+ */
+ if (bdevvp(swapdev, &swapdev_vp) || bdevvp(rootdev, &rootvp))
+ panic("ffs_mountroot: can't setup bdevvp's");
+
+ mp = malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK);
+ bzero((char *)mp, (u_long)sizeof(struct mount));
+ mp->mnt_op = &ufs_vfsops;
+ mp->mnt_flag = MNT_RDONLY;
+ if (error = ffs_mountfs(rootvp, mp, p)) {
+ free(mp, M_MOUNT);
+ return (error);
+ }
+ if (error = vfs_lock(mp)) {
+ (void)ffs_unmount(mp, 0, p);
+ free(mp, M_MOUNT);
+ return (error);
+ }
+ TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
+ mp->mnt_flag |= MNT_ROOTFS;
+ mp->mnt_vnodecovered = NULLVP;
+ ump = VFSTOUFS(mp);
+ fs = ump->um_fs;
+ bzero(fs->fs_fsmnt, sizeof(fs->fs_fsmnt));
+ fs->fs_fsmnt[0] = '/';
+ bcopy((caddr_t)fs->fs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname,
+ MNAMELEN);
+ (void) copystr(ROOTNAME, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ (void)ffs_statfs(mp, &mp->mnt_stat, p);
+ vfs_unlock(mp);
+ inittodr(fs->fs_time);
+ return (0);
+}
+
+/*
+ * VFS Operations.
+ *
+ * mount system call
+ */
+int
+ffs_mount(mp, path, data, ndp, p)
+ register struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ struct vnode *devvp;
+ struct ufs_args args;
+ struct ufsmount *ump = 0;
+ register struct fs *fs;
+ u_int size;
+ int error, flags;
+
+ if (error = copyin(data, (caddr_t)&args, sizeof (struct ufs_args)))
+ return (error);
+ /*
+ * If updating, check whether changing from read-only to
+ * read/write; if there is no device name, that's all we do.
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ ump = VFSTOUFS(mp);
+ fs = ump->um_fs;
+ error = 0;
+ if (fs->fs_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
+ flags = WRITECLOSE;
+ if (mp->mnt_flag & MNT_FORCE)
+ flags |= FORCECLOSE;
+ if (vfs_busy(mp))
+ return (EBUSY);
+ error = ffs_flushfiles(mp, flags, p);
+ vfs_unbusy(mp);
+ }
+ if (!error && (mp->mnt_flag & MNT_RELOAD))
+ error = ffs_reload(mp, ndp->ni_cnd.cn_cred, p);
+ if (error)
+ return (error);
+ if (fs->fs_ronly && (mp->mnt_flag & MNT_WANTRDWR))
+ fs->fs_ronly = 0;
+ if (args.fspec == 0) {
+ /*
+ * Process export requests.
+ */
+ return (vfs_export(mp, &ump->um_export, &args.export));
+ }
+ }
+ /*
+ * Not an update, or updating the name: look up the name
+ * and verify that it refers to a sensible block device.
+ */
+ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
+ if (error = namei(ndp))
+ return (error);
+ devvp = ndp->ni_vp;
+
+ if (devvp->v_type != VBLK) {
+ vrele(devvp);
+ return (ENOTBLK);
+ }
+ if (major(devvp->v_rdev) >= nblkdev) {
+ vrele(devvp);
+ return (ENXIO);
+ }
+ if ((mp->mnt_flag & MNT_UPDATE) == 0)
+ error = ffs_mountfs(devvp, mp, p);
+ else {
+ if (devvp != ump->um_devvp)
+ error = EINVAL; /* needs translation */
+ else
+ vrele(devvp);
+ }
+ if (error) {
+ vrele(devvp);
+ return (error);
+ }
+ ump = VFSTOUFS(mp);
+ fs = ump->um_fs;
+ (void) copyinstr(path, fs->fs_fsmnt, sizeof(fs->fs_fsmnt) - 1, &size);
+ bzero(fs->fs_fsmnt + size, sizeof(fs->fs_fsmnt) - size);
+ bcopy((caddr_t)fs->fs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname,
+ MNAMELEN);
+ (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ (void)ffs_statfs(mp, &mp->mnt_stat, p);
+ return (0);
+}
+
+/*
+ * Reload all incore data for a filesystem (used after running fsck on
+ * the root filesystem and finding things to fix). The filesystem must
+ * be mounted read-only.
+ *
+ * Things to do to update the mount:
+ * 1) invalidate all cached meta-data.
+ * 2) re-read superblock from disk.
+ * 3) re-read summary information from disk.
+ * 4) invalidate all inactive vnodes.
+ * 5) invalidate all cached file data.
+ * 6) re-read inode data for all active vnodes.
+ */
+int
+ffs_reload(mountp, cred, p)
+ register struct mount *mountp;
+ struct ucred *cred;
+ struct proc *p;
+{
+ register struct vnode *vp, *nvp, *devvp;
+ struct inode *ip;
+ struct csum *space;
+ struct buf *bp;
+ struct fs *fs;
+ int i, blks, size, error;
+
+ if ((mountp->mnt_flag & MNT_RDONLY) == 0)
+ return (EINVAL);
+ /*
+ * Step 1: invalidate all cached meta-data.
+ */
+ devvp = VFSTOUFS(mountp)->um_devvp;
+ if (vinvalbuf(devvp, 0, cred, p, 0, 0))
+ panic("ffs_reload: dirty1");
+ /*
+ * Step 2: re-read superblock from disk.
+ */
+ if (error = bread(devvp, SBLOCK, SBSIZE, NOCRED, &bp))
+ return (error);
+ fs = (struct fs *)bp->b_data;
+ if (fs->fs_magic != FS_MAGIC || fs->fs_bsize > MAXBSIZE ||
+ fs->fs_bsize < sizeof(struct fs)) {
+ brelse(bp);
+ return (EIO); /* XXX needs translation */
+ }
+ fs = VFSTOUFS(mountp)->um_fs;
+ bcopy(&fs->fs_csp[0], &((struct fs *)bp->b_data)->fs_csp[0],
+ sizeof(fs->fs_csp));
+ bcopy(bp->b_data, fs, (u_int)fs->fs_sbsize);
+ if (fs->fs_sbsize < SBSIZE)
+ bp->b_flags |= B_INVAL;
+ brelse(bp);
+ ffs_oldfscompat(fs);
+ /*
+ * Step 3: re-read summary information from disk.
+ */
+ blks = howmany(fs->fs_cssize, fs->fs_fsize);
+ space = fs->fs_csp[0];
+ for (i = 0; i < blks; i += fs->fs_frag) {
+ size = fs->fs_bsize;
+ if (i + fs->fs_frag > blks)
+ size = (blks - i) * fs->fs_fsize;
+ if (error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + i), size,
+ NOCRED, &bp))
+ return (error);
+ bcopy(bp->b_data, fs->fs_csp[fragstoblks(fs, i)], (u_int)size);
+ brelse(bp);
+ }
+loop:
+ for (vp = mountp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) {
+ nvp = vp->v_mntvnodes.le_next;
+ /*
+ * Step 4: invalidate all inactive vnodes.
+ */
+ if (vp->v_usecount == 0) {
+ vgone(vp);
+ continue;
+ }
+ /*
+ * Step 5: invalidate all cached file data.
+ */
+ if (vget(vp, 1))
+ goto loop;
+ if (vinvalbuf(vp, 0, cred, p, 0, 0))
+ panic("ffs_reload: dirty2");
+ /*
+ * Step 6: re-read inode data for all active vnodes.
+ */
+ ip = VTOI(vp);
+ if (error =
+ bread(devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
+ (int)fs->fs_bsize, NOCRED, &bp)) {
+ vput(vp);
+ return (error);
+ }
+ ip->i_din = *((struct dinode *)bp->b_data +
+ ino_to_fsbo(fs, ip->i_number));
+ brelse(bp);
+ vput(vp);
+ if (vp->v_mount != mountp)
+ goto loop;
+ }
+ return (0);
+}
+
+/*
+ * Common code for mount and mountroot
+ */
+int
+ffs_mountfs(devvp, mp, p)
+ register struct vnode *devvp;
+ struct mount *mp;
+ struct proc *p;
+{
+ register struct ufsmount *ump;
+ struct buf *bp;
+ register struct fs *fs;
+ dev_t dev = devvp->v_rdev;
+ struct partinfo dpart;
+ caddr_t base, space;
+ int havepart = 0, blks;
+ int error, i, size;
+ int ronly;
+ extern struct vnode *rootvp;
+
+ /*
+ * Disallow multiple mounts of the same device.
+ * Disallow mounting of a device that is currently in use
+ * (except for root, which might share swap device for miniroot).
+ * Flush out any old buffers remaining from a previous use.
+ */
+ if (error = vfs_mountedon(devvp))
+ return (error);
+ if (vcount(devvp) > 1 && devvp != rootvp)
+ return (EBUSY);
+ if (error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0))
+ return (error);
+
+ ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
+ if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p))
+ return (error);
+ if (VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, NOCRED, p) != 0)
+ size = DEV_BSIZE;
+ else {
+ havepart = 1;
+ size = dpart.disklab->d_secsize;
+ }
+
+ bp = NULL;
+ ump = NULL;
+ if (error = bread(devvp, SBLOCK, SBSIZE, NOCRED, &bp))
+ goto out;
+ fs = (struct fs *)bp->b_data;
+ if (fs->fs_magic != FS_MAGIC || fs->fs_bsize > MAXBSIZE ||
+ fs->fs_bsize < sizeof(struct fs)) {
+ error = EINVAL; /* XXX needs translation */
+ goto out;
+ }
+ ump = malloc(sizeof *ump, M_UFSMNT, M_WAITOK);
+ bzero((caddr_t)ump, sizeof *ump);
+ ump->um_fs = malloc((u_long)fs->fs_sbsize, M_UFSMNT,
+ M_WAITOK);
+ bcopy(bp->b_data, ump->um_fs, (u_int)fs->fs_sbsize);
+ if (fs->fs_sbsize < SBSIZE)
+ bp->b_flags |= B_INVAL;
+ brelse(bp);
+ bp = NULL;
+ fs = ump->um_fs;
+ fs->fs_ronly = ronly;
+ if (ronly == 0)
+ fs->fs_fmod = 1;
+ blks = howmany(fs->fs_cssize, fs->fs_fsize);
+ base = space = malloc((u_long)fs->fs_cssize, M_UFSMNT,
+ M_WAITOK);
+ for (i = 0; i < blks; i += fs->fs_frag) {
+ size = fs->fs_bsize;
+ if (i + fs->fs_frag > blks)
+ size = (blks - i) * fs->fs_fsize;
+ error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + i), size,
+ NOCRED, &bp);
+ if (error) {
+ free(base, M_UFSMNT);
+ goto out;
+ }
+ bcopy(bp->b_data, space, (u_int)size);
+ fs->fs_csp[fragstoblks(fs, i)] = (struct csum *)space;
+ space += size;
+ brelse(bp);
+ bp = NULL;
+ }
+ mp->mnt_data = (qaddr_t)ump;
+ mp->mnt_stat.f_fsid.val[0] = (long)dev;
+ mp->mnt_stat.f_fsid.val[1] = MOUNT_UFS;
+ mp->mnt_maxsymlinklen = fs->fs_maxsymlinklen;
+ mp->mnt_flag |= MNT_LOCAL;
+ ump->um_mountp = mp;
+ ump->um_dev = dev;
+ ump->um_devvp = devvp;
+ ump->um_nindir = fs->fs_nindir;
+ ump->um_bptrtodb = fs->fs_fsbtodb;
+ ump->um_seqinc = fs->fs_frag;
+ for (i = 0; i < MAXQUOTAS; i++)
+ ump->um_quotas[i] = NULLVP;
+ devvp->v_specflags |= SI_MOUNTEDON;
+ ffs_oldfscompat(fs);
+ return (0);
+out:
+ if (bp)
+ brelse(bp);
+ (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p);
+ if (ump) {
+ free(ump->um_fs, M_UFSMNT);
+ free(ump, M_UFSMNT);
+ mp->mnt_data = (qaddr_t)0;
+ }
+ return (error);
+}
+
+/*
+ * Sanity checks for old file systems.
+ *
+ * XXX - goes away some day.
+ */
+int
+ffs_oldfscompat(fs)
+ struct fs *fs;
+{
+ int i;
+
+ fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect); /* XXX */
+ fs->fs_interleave = max(fs->fs_interleave, 1); /* XXX */
+ if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */
+ fs->fs_nrpos = 8; /* XXX */
+ if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */
+ quad_t sizepb = fs->fs_bsize; /* XXX */
+ /* XXX */
+ fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1; /* XXX */
+ for (i = 0; i < NIADDR; i++) { /* XXX */
+ sizepb *= NINDIR(fs); /* XXX */
+ fs->fs_maxfilesize += sizepb; /* XXX */
+ } /* XXX */
+ fs->fs_qbmask = ~fs->fs_bmask; /* XXX */
+ fs->fs_qfmask = ~fs->fs_fmask; /* XXX */
+ } /* XXX */
+ return (0);
+}
+
+/*
+ * unmount system call
+ */
+int
+ffs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ register struct ufsmount *ump;
+ register struct fs *fs;
+ int error, flags, ronly;
+
+ flags = 0;
+ if (mntflags & MNT_FORCE) {
+ if (mp->mnt_flag & MNT_ROOTFS)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+ if (error = ffs_flushfiles(mp, flags, p))
+ return (error);
+ ump = VFSTOUFS(mp);
+ fs = ump->um_fs;
+ ronly = !fs->fs_ronly;
+ ump->um_devvp->v_specflags &= ~SI_MOUNTEDON;
+ error = VOP_CLOSE(ump->um_devvp, ronly ? FREAD : FREAD|FWRITE,
+ NOCRED, p);
+ vrele(ump->um_devvp);
+ free(fs->fs_csp[0], M_UFSMNT);
+ free(fs, M_UFSMNT);
+ free(ump, M_UFSMNT);
+ mp->mnt_data = (qaddr_t)0;
+ mp->mnt_flag &= ~MNT_LOCAL;
+ return (error);
+}
+
+/*
+ * Flush out all the files in a filesystem.
+ */
+int
+ffs_flushfiles(mp, flags, p)
+ register struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ extern int doforce;
+ register struct ufsmount *ump;
+ int i, error;
+
+ if (!doforce)
+ flags &= ~FORCECLOSE;
+ ump = VFSTOUFS(mp);
+#ifdef QUOTA
+ if (mp->mnt_flag & MNT_QUOTA) {
+ if (error = vflush(mp, NULLVP, SKIPSYSTEM|flags))
+ return (error);
+ for (i = 0; i < MAXQUOTAS; i++) {
+ if (ump->um_quotas[i] == NULLVP)
+ continue;
+ quotaoff(p, mp, i);
+ }
+ /*
+ * Here we fall through to vflush again to ensure
+ * that we have gotten rid of all the system vnodes.
+ */
+ }
+#endif
+ error = vflush(mp, NULLVP, flags);
+ return (error);
+}
+
+/*
+ * Get file system statistics.
+ */
+int
+ffs_statfs(mp, sbp, p)
+ struct mount *mp;
+ register struct statfs *sbp;
+ struct proc *p;
+{
+ register struct ufsmount *ump;
+ register struct fs *fs;
+
+ ump = VFSTOUFS(mp);
+ fs = ump->um_fs;
+ if (fs->fs_magic != FS_MAGIC)
+ panic("ffs_statfs");
+ sbp->f_type = MOUNT_UFS;
+ sbp->f_bsize = fs->fs_fsize;
+ sbp->f_iosize = fs->fs_bsize;
+ sbp->f_blocks = fs->fs_dsize;
+ sbp->f_bfree = fs->fs_cstotal.cs_nbfree * fs->fs_frag +
+ fs->fs_cstotal.cs_nffree;
+ sbp->f_bavail = (fs->fs_dsize * (100 - fs->fs_minfree) / 100) -
+ (fs->fs_dsize - sbp->f_bfree);
+ sbp->f_files = fs->fs_ncg * fs->fs_ipg - ROOTINO;
+ sbp->f_ffree = fs->fs_cstotal.cs_nifree;
+ if (sbp != &mp->mnt_stat) {
+ bcopy((caddr_t)mp->mnt_stat.f_mntonname,
+ (caddr_t)&sbp->f_mntonname[0], MNAMELEN);
+ bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
+ (caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
+ }
+ return (0);
+}
+
+/*
+ * Go through the disk queues to initiate sandbagged IO;
+ * go through the inodes to write those that have been modified;
+ * initiate the writing of the super block if it has been modified.
+ *
+ * Note: we are always called with the filesystem marked `MPBUSY'.
+ */
+int
+ffs_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+ register struct vnode *vp;
+ register struct inode *ip;
+ register struct ufsmount *ump = VFSTOUFS(mp);
+ register struct fs *fs;
+ int error, allerror = 0;
+
+ fs = ump->um_fs;
+ /*
+ * Write back modified superblock.
+ * Consistency check that the superblock
+ * is still in the buffer cache.
+ */
+ if (fs->fs_fmod != 0) {
+ if (fs->fs_ronly != 0) { /* XXX */
+ printf("fs = %s\n", fs->fs_fsmnt);
+ panic("update: rofs mod");
+ }
+ fs->fs_fmod = 0;
+ fs->fs_time = time.tv_sec;
+ allerror = ffs_sbupdate(ump, waitfor);
+ }
+ /*
+ * Write back each (modified) inode.
+ */
+loop:
+ for (vp = mp->mnt_vnodelist.lh_first;
+ vp != NULL;
+ vp = vp->v_mntvnodes.le_next) {
+ /*
+ * If the vnode that we are about to sync is no longer
+ * associated with this mount point, start over.
+ */
+ if (vp->v_mount != mp)
+ goto loop;
+ if (VOP_ISLOCKED(vp))
+ continue;
+ ip = VTOI(vp);
+ if ((ip->i_flag &
+ (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 &&
+ vp->v_dirtyblkhd.lh_first == NULL)
+ continue;
+ if (vget(vp, 1))
+ goto loop;
+ if (error = VOP_FSYNC(vp, cred, waitfor, p))
+ allerror = error;
+ vput(vp);
+ }
+ /*
+ * Force stale file system control information to be flushed.
+ */
+ if (error = VOP_FSYNC(ump->um_devvp, cred, waitfor, p))
+ allerror = error;
+#ifdef QUOTA
+ qsync(mp);
+#endif
+ return (allerror);
+}
+
+/*
+ * Look up a FFS dinode number to find its incore vnode, otherwise read it
+ * in from disk. If it is in core, wait for the lock bit to clear, then
+ * return the inode locked. Detection and handling of mount points must be
+ * done by the calling routine.
+ */
+int
+ffs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+ register struct fs *fs;
+ register struct inode *ip;
+ struct ufsmount *ump;
+ struct buf *bp;
+ struct vnode *vp;
+ dev_t dev;
+ int i, type, error;
+
+ ump = VFSTOUFS(mp);
+ dev = ump->um_dev;
+ if ((*vpp = ufs_ihashget(dev, ino)) != NULL)
+ return (0);
+
+ /* Allocate a new vnode/inode. */
+ if (error = getnewvnode(VT_UFS, mp, ffs_vnodeop_p, &vp)) {
+ *vpp = NULL;
+ return (error);
+ }
+ type = ump->um_devvp->v_tag == VT_MFS ? M_MFSNODE : M_FFSNODE; /* XXX */
+ MALLOC(ip, struct inode *, sizeof(struct inode), type, M_WAITOK);
+ bzero((caddr_t)ip, sizeof(struct inode));
+ vp->v_data = ip;
+ ip->i_vnode = vp;
+ ip->i_fs = fs = ump->um_fs;
+ ip->i_dev = dev;
+ ip->i_number = ino;
+#ifdef QUOTA
+ for (i = 0; i < MAXQUOTAS; i++)
+ ip->i_dquot[i] = NODQUOT;
+#endif
+ /*
+ * Put it onto its hash chain and lock it so that other requests for
+ * this inode will block if they arrive while we are sleeping waiting
+ * for old data structures to be purged or for the contents of the
+ * disk portion of this inode to be read.
+ */
+ ufs_ihashins(ip);
+
+ /* Read in the disk contents for the inode, copy into the inode. */
+ if (error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)),
+ (int)fs->fs_bsize, NOCRED, &bp)) {
+ /*
+ * The inode does not contain anything useful, so it would
+ * be misleading to leave it on its hash chain. With mode
+ * still zero, it will be unlinked and returned to the free
+ * list by vput().
+ */
+ vput(vp);
+ brelse(bp);
+ *vpp = NULL;
+ return (error);
+ }
+ ip->i_din = *((struct dinode *)bp->b_data + ino_to_fsbo(fs, ino));
+ brelse(bp);
+
+ /*
+ * Initialize the vnode from the inode, check for aliases.
+ * Note that the underlying vnode may have changed.
+ */
+ if (error = ufs_vinit(mp, ffs_specop_p, FFS_FIFOOPS, &vp)) {
+ vput(vp);
+ *vpp = NULL;
+ return (error);
+ }
+ /*
+ * Finish inode initialization now that aliasing has been resolved.
+ */
+ ip->i_devvp = ump->um_devvp;
+ VREF(ip->i_devvp);
+ /*
+ * Set up a generation number for this inode if it does not
+ * already have one. This should only happen on old filesystems.
+ */
+ if (ip->i_gen == 0) {
+ if (++nextgennumber < (u_long)time.tv_sec)
+ nextgennumber = time.tv_sec;
+ ip->i_gen = nextgennumber;
+ if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
+ ip->i_flag |= IN_MODIFIED;
+ }
+ /*
+ * Ensure that uid and gid are correct. This is a temporary
+ * fix until fsck has been changed to do the update.
+ */
+ if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */
+ ip->i_uid = ip->i_din.di_ouid; /* XXX */
+ ip->i_gid = ip->i_din.di_ogid; /* XXX */
+ } /* XXX */
+
+ *vpp = vp;
+ return (0);
+}
+
+/*
+ * File handle to vnode
+ *
+ * Have to be really careful about stale file handles:
+ * - check that the inode number is valid
+ * - call ffs_vget() to get the locked inode
+ * - check for an unallocated inode (i_mode == 0)
+ * - check that the given client host has export rights and return
+ * those rights via. exflagsp and credanonp
+ */
+int
+ffs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
+ register struct mount *mp;
+ struct fid *fhp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred **credanonp;
+{
+ register struct ufid *ufhp;
+ struct fs *fs;
+
+ ufhp = (struct ufid *)fhp;
+ fs = VFSTOUFS(mp)->um_fs;
+ if (ufhp->ufid_ino < ROOTINO ||
+ ufhp->ufid_ino >= fs->fs_ncg * fs->fs_ipg)
+ return (ESTALE);
+ return (ufs_check_export(mp, ufhp, nam, vpp, exflagsp, credanonp));
+}
+
+/*
+ * Vnode pointer to File handle
+ */
+/* ARGSUSED */
+int
+ffs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+ register struct inode *ip;
+ register struct ufid *ufhp;
+
+ ip = VTOI(vp);
+ ufhp = (struct ufid *)fhp;
+ ufhp->ufid_len = sizeof(struct ufid);
+ ufhp->ufid_ino = ip->i_number;
+ ufhp->ufid_gen = ip->i_gen;
+ return (0);
+}
+
+/*
+ * Write a superblock and associated information back to disk.
+ */
+int
+ffs_sbupdate(mp, waitfor)
+ struct ufsmount *mp;
+ int waitfor;
+{
+ register struct fs *fs = mp->um_fs;
+ register struct buf *bp;
+ int blks;
+ caddr_t space;
+ int i, size, error = 0;
+
+ bp = getblk(mp->um_devvp, SBLOCK, (int)fs->fs_sbsize, 0, 0);
+ bcopy((caddr_t)fs, bp->b_data, (u_int)fs->fs_sbsize);
+ /* Restore compatibility to old file systems. XXX */
+ if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */
+ ((struct fs *)bp->b_data)->fs_nrpos = -1; /* XXX */
+ if (waitfor == MNT_WAIT)
+ error = bwrite(bp);
+ else
+ bawrite(bp);
+ blks = howmany(fs->fs_cssize, fs->fs_fsize);
+ space = (caddr_t)fs->fs_csp[0];
+ for (i = 0; i < blks; i += fs->fs_frag) {
+ size = fs->fs_bsize;
+ if (i + fs->fs_frag > blks)
+ size = (blks - i) * fs->fs_fsize;
+ bp = getblk(mp->um_devvp, fsbtodb(fs, fs->fs_csaddr + i),
+ size, 0, 0);
+ bcopy(space, bp->b_data, (u_int)size);
+ space += size;
+ if (waitfor == MNT_WAIT)
+ error = bwrite(bp);
+ else
+ bawrite(bp);
+ }
+ return (error);
+}
diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c
new file mode 100644
index 0000000..59814f2
--- /dev/null
+++ b/sys/ufs/ffs/ffs_vnops.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ffs_vnops.c 8.7 (Berkeley) 2/3/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/resourcevar.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
+
+#include <miscfs/specfs/specdev.h>
+#include <miscfs/fifofs/fifo.h>
+
+#include <ufs/ufs/lockf.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/ffs/fs.h>
+#include <ufs/ffs/ffs_extern.h>
+
+/* Global vfs data structures for ufs. */
+int (**ffs_vnodeop_p)();
+struct vnodeopv_entry_desc ffs_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, ufs_lookup }, /* lookup */
+ { &vop_create_desc, ufs_create }, /* create */
+ { &vop_mknod_desc, ufs_mknod }, /* mknod */
+ { &vop_open_desc, ufs_open }, /* open */
+ { &vop_close_desc, ufs_close }, /* close */
+ { &vop_access_desc, ufs_access }, /* access */
+ { &vop_getattr_desc, ufs_getattr }, /* getattr */
+ { &vop_setattr_desc, ufs_setattr }, /* setattr */
+ { &vop_read_desc, ffs_read }, /* read */
+ { &vop_write_desc, ffs_write }, /* write */
+ { &vop_ioctl_desc, ufs_ioctl }, /* ioctl */
+ { &vop_select_desc, ufs_select }, /* select */
+ { &vop_mmap_desc, ufs_mmap }, /* mmap */
+ { &vop_fsync_desc, ffs_fsync }, /* fsync */
+ { &vop_seek_desc, ufs_seek }, /* seek */
+ { &vop_remove_desc, ufs_remove }, /* remove */
+ { &vop_link_desc, ufs_link }, /* link */
+ { &vop_rename_desc, ufs_rename }, /* rename */
+ { &vop_mkdir_desc, ufs_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, ufs_rmdir }, /* rmdir */
+ { &vop_symlink_desc, ufs_symlink }, /* symlink */
+ { &vop_readdir_desc, ufs_readdir }, /* readdir */
+ { &vop_readlink_desc, ufs_readlink }, /* readlink */
+ { &vop_abortop_desc, ufs_abortop }, /* abortop */
+ { &vop_inactive_desc, ufs_inactive }, /* inactive */
+ { &vop_reclaim_desc, ufs_reclaim }, /* reclaim */
+ { &vop_lock_desc, ufs_lock }, /* lock */
+ { &vop_unlock_desc, ufs_unlock }, /* unlock */
+ { &vop_bmap_desc, ufs_bmap }, /* bmap */
+ { &vop_strategy_desc, ufs_strategy }, /* strategy */
+ { &vop_print_desc, ufs_print }, /* print */
+ { &vop_islocked_desc, ufs_islocked }, /* islocked */
+ { &vop_pathconf_desc, ufs_pathconf }, /* pathconf */
+ { &vop_advlock_desc, ufs_advlock }, /* advlock */
+ { &vop_blkatoff_desc, ffs_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, ffs_valloc }, /* valloc */
+ { &vop_reallocblks_desc, ffs_reallocblks }, /* reallocblks */
+ { &vop_vfree_desc, ffs_vfree }, /* vfree */
+ { &vop_truncate_desc, ffs_truncate }, /* truncate */
+ { &vop_update_desc, ffs_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc ffs_vnodeop_opv_desc =
+ { &ffs_vnodeop_p, ffs_vnodeop_entries };
+
+int (**ffs_specop_p)();
+struct vnodeopv_entry_desc ffs_specop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, spec_lookup }, /* lookup */
+ { &vop_create_desc, spec_create }, /* create */
+ { &vop_mknod_desc, spec_mknod }, /* mknod */
+ { &vop_open_desc, spec_open }, /* open */
+ { &vop_close_desc, ufsspec_close }, /* close */
+ { &vop_access_desc, ufs_access }, /* access */
+ { &vop_getattr_desc, ufs_getattr }, /* getattr */
+ { &vop_setattr_desc, ufs_setattr }, /* setattr */
+ { &vop_read_desc, ufsspec_read }, /* read */
+ { &vop_write_desc, ufsspec_write }, /* write */
+ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */
+ { &vop_select_desc, spec_select }, /* select */
+ { &vop_mmap_desc, spec_mmap }, /* mmap */
+ { &vop_fsync_desc, ffs_fsync }, /* fsync */
+ { &vop_seek_desc, spec_seek }, /* seek */
+ { &vop_remove_desc, spec_remove }, /* remove */
+ { &vop_link_desc, spec_link }, /* link */
+ { &vop_rename_desc, spec_rename }, /* rename */
+ { &vop_mkdir_desc, spec_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, spec_rmdir }, /* rmdir */
+ { &vop_symlink_desc, spec_symlink }, /* symlink */
+ { &vop_readdir_desc, spec_readdir }, /* readdir */
+ { &vop_readlink_desc, spec_readlink }, /* readlink */
+ { &vop_abortop_desc, spec_abortop }, /* abortop */
+ { &vop_inactive_desc, ufs_inactive }, /* inactive */
+ { &vop_reclaim_desc, ufs_reclaim }, /* reclaim */
+ { &vop_lock_desc, ufs_lock }, /* lock */
+ { &vop_unlock_desc, ufs_unlock }, /* unlock */
+ { &vop_bmap_desc, spec_bmap }, /* bmap */
+ { &vop_strategy_desc, spec_strategy }, /* strategy */
+ { &vop_print_desc, ufs_print }, /* print */
+ { &vop_islocked_desc, ufs_islocked }, /* islocked */
+ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */
+ { &vop_advlock_desc, spec_advlock }, /* advlock */
+ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, spec_valloc }, /* valloc */
+ { &vop_reallocblks_desc, spec_reallocblks }, /* reallocblks */
+ { &vop_vfree_desc, ffs_vfree }, /* vfree */
+ { &vop_truncate_desc, spec_truncate }, /* truncate */
+ { &vop_update_desc, ffs_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc ffs_specop_opv_desc =
+ { &ffs_specop_p, ffs_specop_entries };
+
+#ifdef FIFO
+int (**ffs_fifoop_p)();
+struct vnodeopv_entry_desc ffs_fifoop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, fifo_lookup }, /* lookup */
+ { &vop_create_desc, fifo_create }, /* create */
+ { &vop_mknod_desc, fifo_mknod }, /* mknod */
+ { &vop_open_desc, fifo_open }, /* open */
+ { &vop_close_desc, ufsfifo_close }, /* close */
+ { &vop_access_desc, ufs_access }, /* access */
+ { &vop_getattr_desc, ufs_getattr }, /* getattr */
+ { &vop_setattr_desc, ufs_setattr }, /* setattr */
+ { &vop_read_desc, ufsfifo_read }, /* read */
+ { &vop_write_desc, ufsfifo_write }, /* write */
+ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */
+ { &vop_select_desc, fifo_select }, /* select */
+ { &vop_mmap_desc, fifo_mmap }, /* mmap */
+ { &vop_fsync_desc, ffs_fsync }, /* fsync */
+ { &vop_seek_desc, fifo_seek }, /* seek */
+ { &vop_remove_desc, fifo_remove }, /* remove */
+ { &vop_link_desc, fifo_link }, /* link */
+ { &vop_rename_desc, fifo_rename }, /* rename */
+ { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */
+ { &vop_symlink_desc, fifo_symlink }, /* symlink */
+ { &vop_readdir_desc, fifo_readdir }, /* readdir */
+ { &vop_readlink_desc, fifo_readlink }, /* readlink */
+ { &vop_abortop_desc, fifo_abortop }, /* abortop */
+ { &vop_inactive_desc, ufs_inactive }, /* inactive */
+ { &vop_reclaim_desc, ufs_reclaim }, /* reclaim */
+ { &vop_lock_desc, ufs_lock }, /* lock */
+ { &vop_unlock_desc, ufs_unlock }, /* unlock */
+ { &vop_bmap_desc, fifo_bmap }, /* bmap */
+ { &vop_strategy_desc, fifo_strategy }, /* strategy */
+ { &vop_print_desc, ufs_print }, /* print */
+ { &vop_islocked_desc, ufs_islocked }, /* islocked */
+ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */
+ { &vop_advlock_desc, fifo_advlock }, /* advlock */
+ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, fifo_valloc }, /* valloc */
+ { &vop_reallocblks_desc, fifo_reallocblks }, /* reallocblks */
+ { &vop_vfree_desc, ffs_vfree }, /* vfree */
+ { &vop_truncate_desc, fifo_truncate }, /* truncate */
+ { &vop_update_desc, ffs_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc ffs_fifoop_opv_desc =
+ { &ffs_fifoop_p, ffs_fifoop_entries };
+#endif /* FIFO */
+
+#ifdef DEBUG
+/*
+ * Enabling cluster read/write operations.
+ */
+#include <sys/sysctl.h>
+int doclusterread = 1;
+struct ctldebug debug11 = { "doclusterread", &doclusterread };
+int doclusterwrite = 1;
+struct ctldebug debug12 = { "doclusterwrite", &doclusterwrite };
+#else
+/* XXX for ufs_readwrite */
+#define doclusterread 1
+#define doclusterwrite 1
+#endif
+
+#include <ufs/ufs/ufs_readwrite.c>
+
+/*
+ * Synch an open file.
+ */
+/* ARGSUSED */
+int
+ffs_fsync(ap)
+ struct vop_fsync_args /* {
+ struct vnode *a_vp;
+ struct ucred *a_cred;
+ int a_waitfor;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct buf *bp;
+ struct timeval tv;
+ struct buf *nbp;
+ int s;
+
+ /*
+ * Flush all dirty buffers associated with a vnode.
+ */
+loop:
+ s = splbio();
+ for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
+ nbp = bp->b_vnbufs.le_next;
+ if ((bp->b_flags & B_BUSY))
+ continue;
+ if ((bp->b_flags & B_DELWRI) == 0)
+ panic("ffs_fsync: not dirty");
+ bremfree(bp);
+ bp->b_flags |= B_BUSY;
+ splx(s);
+ /*
+ * Wait for I/O associated with indirect blocks to complete,
+ * since there is no way to quickly wait for them below.
+ */
+ if (bp->b_vp == vp || ap->a_waitfor == MNT_NOWAIT)
+ (void) bawrite(bp);
+ else
+ (void) bwrite(bp);
+ goto loop;
+ }
+ if (ap->a_waitfor == MNT_WAIT) {
+ while (vp->v_numoutput) {
+ vp->v_flag |= VBWAIT;
+ sleep((caddr_t)&vp->v_numoutput, PRIBIO + 1);
+ }
+#ifdef DIAGNOSTIC
+ if (vp->v_dirtyblkhd.lh_first) {
+ vprint("ffs_fsync: dirty", vp);
+ goto loop;
+ }
+#endif
+ }
+ splx(s);
+ tv = time;
+ return (VOP_UPDATE(ap->a_vp, &tv, &tv, ap->a_waitfor == MNT_WAIT));
+}
diff --git a/sys/ufs/ffs/fs.h b/sys/ufs/ffs/fs.h
new file mode 100644
index 0000000..bef052f
--- /dev/null
+++ b/sys/ufs/ffs/fs.h
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fs.h 8.7 (Berkeley) 4/19/94
+ */
+
+/*
+ * Each disk drive contains some number of file systems.
+ * A file system consists of a number of cylinder groups.
+ * Each cylinder group has inodes and data.
+ *
+ * A file system is described by its super-block, which in turn
+ * describes the cylinder groups. The super-block is critical
+ * data and is replicated in each cylinder group to protect against
+ * catastrophic loss. This is done at `newfs' time and the critical
+ * super-block data does not change, so the copies need not be
+ * referenced further unless disaster strikes.
+ *
+ * For file system fs, the offsets of the various blocks of interest
+ * are given in the super block as:
+ * [fs->fs_sblkno] Super-block
+ * [fs->fs_cblkno] Cylinder group block
+ * [fs->fs_iblkno] Inode blocks
+ * [fs->fs_dblkno] Data blocks
+ * The beginning of cylinder group cg in fs, is given by
+ * the ``cgbase(fs, cg)'' macro.
+ *
+ * The first boot and super blocks are given in absolute disk addresses.
+ * The byte-offset forms are preferred, as they don't imply a sector size.
+ */
+#define BBSIZE 8192
+#define SBSIZE 8192
+#define BBOFF ((off_t)(0))
+#define SBOFF ((off_t)(BBOFF + BBSIZE))
+#define BBLOCK ((daddr_t)(0))
+#define SBLOCK ((daddr_t)(BBLOCK + BBSIZE / DEV_BSIZE))
+
+/*
+ * Addresses stored in inodes are capable of addressing fragments
+ * of `blocks'. File system blocks of at most size MAXBSIZE can
+ * be optionally broken into 2, 4, or 8 pieces, each of which is
+ * addressible; these pieces may be DEV_BSIZE, or some multiple of
+ * a DEV_BSIZE unit.
+ *
+ * Large files consist of exclusively large data blocks. To avoid
+ * undue wasted disk space, the last data block of a small file may be
+ * allocated as only as many fragments of a large block as are
+ * necessary. The file system format retains only a single pointer
+ * to such a fragment, which is a piece of a single large block that
+ * has been divided. The size of such a fragment is determinable from
+ * information in the inode, using the ``blksize(fs, ip, lbn)'' macro.
+ *
+ * The file system records space availability at the fragment level;
+ * to determine block availability, aligned fragments are examined.
+ */
+
+/*
+ * MINBSIZE is the smallest allowable block size.
+ * In order to insure that it is possible to create files of size
+ * 2^32 with only two levels of indirection, MINBSIZE is set to 4096.
+ * MINBSIZE must be big enough to hold a cylinder group block,
+ * thus changes to (struct cg) must keep its size within MINBSIZE.
+ * Note that super blocks are always of size SBSIZE,
+ * and that both SBSIZE and MAXBSIZE must be >= MINBSIZE.
+ */
+#define MINBSIZE 4096
+
+/*
+ * The path name on which the file system is mounted is maintained
+ * in fs_fsmnt. MAXMNTLEN defines the amount of space allocated in
+ * the super block for this name.
+ * The limit on the amount of summary information per file system
+ * is defined by MAXCSBUFS. It is currently parameterized for a
+ * maximum of two million cylinders.
+ */
+#define MAXMNTLEN 512
+#define MAXCSBUFS 32
+
+/*
+ * A summary of contiguous blocks of various sizes is maintained
+ * in each cylinder group. Normally this is set by the initial
+ * value of fs_maxcontig. To conserve space, a maximum summary size
+ * is set by FS_MAXCONTIG.
+ */
+#define FS_MAXCONTIG 16
+
+/*
+ * MINFREE gives the minimum acceptable percentage of file system
+ * blocks which may be free. If the freelist drops below this level
+ * only the superuser may continue to allocate blocks. This may
+ * be set to 0 if no reserve of free blocks is deemed necessary,
+ * however throughput drops by fifty percent if the file system
+ * is run at between 95% and 100% full; thus the minimum default
+ * value of fs_minfree is 5%. However, to get good clustering
+ * performance, 10% is a better choice. hence we use 10% as our
+ * default value. With 10% free space, fragmentation is not a
+ * problem, so we choose to optimize for time.
+ */
+#define MINFREE 5
+#define DEFAULTOPT FS_OPTTIME
+
+/*
+ * Per cylinder group information; summarized in blocks allocated
+ * from first cylinder group data blocks. These blocks have to be
+ * read in from fs_csaddr (size fs_cssize) in addition to the
+ * super block.
+ *
+ * N.B. sizeof(struct csum) must be a power of two in order for
+ * the ``fs_cs'' macro to work (see below).
+ */
+struct csum {
+ long cs_ndir; /* number of directories */
+ long cs_nbfree; /* number of free blocks */
+ long cs_nifree; /* number of free inodes */
+ long cs_nffree; /* number of free frags */
+};
+
+/*
+ * Super block for a file system.
+ */
+struct fs {
+ struct fs *fs_link; /* linked list of file systems */
+ struct fs *fs_rlink; /* used for incore super blocks */
+ daddr_t fs_sblkno; /* addr of super-block in filesys */
+ daddr_t fs_cblkno; /* offset of cyl-block in filesys */
+ daddr_t fs_iblkno; /* offset of inode-blocks in filesys */
+ daddr_t fs_dblkno; /* offset of first data after cg */
+ long fs_cgoffset; /* cylinder group offset in cylinder */
+ long fs_cgmask; /* used to calc mod fs_ntrak */
+ time_t fs_time; /* last time written */
+ long fs_size; /* number of blocks in fs */
+ long fs_dsize; /* number of data blocks in fs */
+ long fs_ncg; /* number of cylinder groups */
+ long fs_bsize; /* size of basic blocks in fs */
+ long fs_fsize; /* size of frag blocks in fs */
+ long fs_frag; /* number of frags in a block in fs */
+/* these are configuration parameters */
+ long fs_minfree; /* minimum percentage of free blocks */
+ long fs_rotdelay; /* num of ms for optimal next block */
+ long fs_rps; /* disk revolutions per second */
+/* these fields can be computed from the others */
+ long fs_bmask; /* ``blkoff'' calc of blk offsets */
+ long fs_fmask; /* ``fragoff'' calc of frag offsets */
+ long fs_bshift; /* ``lblkno'' calc of logical blkno */
+ long fs_fshift; /* ``numfrags'' calc number of frags */
+/* these are configuration parameters */
+ long fs_maxcontig; /* max number of contiguous blks */
+ long fs_maxbpg; /* max number of blks per cyl group */
+/* these fields can be computed from the others */
+ long fs_fragshift; /* block to frag shift */
+ long fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */
+ long fs_sbsize; /* actual size of super block */
+ long fs_csmask; /* csum block offset */
+ long fs_csshift; /* csum block number */
+ long fs_nindir; /* value of NINDIR */
+ long fs_inopb; /* value of INOPB */
+ long fs_nspf; /* value of NSPF */
+/* yet another configuration parameter */
+ long fs_optim; /* optimization preference, see below */
+/* these fields are derived from the hardware */
+ long fs_npsect; /* # sectors/track including spares */
+ long fs_interleave; /* hardware sector interleave */
+ long fs_trackskew; /* sector 0 skew, per track */
+ long fs_headswitch; /* head switch time, usec */
+ long fs_trkseek; /* track-to-track seek, usec */
+/* sizes determined by number of cylinder groups and their sizes */
+ daddr_t fs_csaddr; /* blk addr of cyl grp summary area */
+ long fs_cssize; /* size of cyl grp summary area */
+ long fs_cgsize; /* cylinder group size */
+/* these fields are derived from the hardware */
+ long fs_ntrak; /* tracks per cylinder */
+ long fs_nsect; /* sectors per track */
+ long fs_spc; /* sectors per cylinder */
+/* this comes from the disk driver partitioning */
+ long fs_ncyl; /* cylinders in file system */
+/* these fields can be computed from the others */
+ long fs_cpg; /* cylinders per group */
+ long fs_ipg; /* inodes per group */
+ long fs_fpg; /* blocks per group * fs_frag */
+/* this data must be re-computed after crashes */
+ struct csum fs_cstotal; /* cylinder summary information */
+/* these fields are cleared at mount time */
+ char fs_fmod; /* super block modified flag */
+ char fs_clean; /* file system is clean flag */
+ char fs_ronly; /* mounted read-only flag */
+ char fs_flags; /* currently unused flag */
+ char fs_fsmnt[MAXMNTLEN]; /* name mounted on */
+/* these fields retain the current block allocation info */
+ long fs_cgrotor; /* last cg searched */
+ struct csum *fs_csp[MAXCSBUFS];/* list of fs_cs info buffers */
+ long fs_cpc; /* cyl per cycle in postbl */
+ short fs_opostbl[16][8]; /* old rotation block list head */
+ long fs_sparecon[50]; /* reserved for future constants */
+ long fs_contigsumsize; /* size of cluster summary array */
+ long fs_maxsymlinklen; /* max length of an internal symlink */
+ long fs_inodefmt; /* format of on-disk inodes */
+ u_quad_t fs_maxfilesize; /* maximum representable file size */
+ quad_t fs_qbmask; /* ~fs_bmask - for use with quad size */
+ quad_t fs_qfmask; /* ~fs_fmask - for use with quad size */
+ long fs_state; /* validate fs_clean field */
+ long fs_postblformat; /* format of positional layout tables */
+ long fs_nrpos; /* number of rotational positions */
+ long fs_postbloff; /* (short) rotation block list head */
+ long fs_rotbloff; /* (u_char) blocks for each rotation */
+ long fs_magic; /* magic number */
+ u_char fs_space[1]; /* list of blocks for each rotation */
+/* actually longer */
+};
+/*
+ * Filesystem idetification
+ */
+#define FS_MAGIC 0x011954 /* the fast filesystem magic number */
+#define FS_OKAY 0x7c269d38 /* superblock checksum */
+#define FS_42INODEFMT -1 /* 4.2BSD inode format */
+#define FS_44INODEFMT 2 /* 4.4BSD inode format */
+/*
+ * Preference for optimization.
+ */
+#define FS_OPTTIME 0 /* minimize allocation time */
+#define FS_OPTSPACE 1 /* minimize disk fragmentation */
+
+/*
+ * Rotational layout table format types
+ */
+#define FS_42POSTBLFMT -1 /* 4.2BSD rotational table format */
+#define FS_DYNAMICPOSTBLFMT 1 /* dynamic rotational table format */
+/*
+ * Macros for access to superblock array structures
+ */
+#define fs_postbl(fs, cylno) \
+ (((fs)->fs_postblformat == FS_42POSTBLFMT) \
+ ? ((fs)->fs_opostbl[cylno]) \
+ : ((short *)((char *)(fs) + (fs)->fs_postbloff) + (cylno) * (fs)->fs_nrpos))
+#define fs_rotbl(fs) \
+ (((fs)->fs_postblformat == FS_42POSTBLFMT) \
+ ? ((fs)->fs_space) \
+ : ((u_char *)((char *)(fs) + (fs)->fs_rotbloff)))
+
+/*
+ * The size of a cylinder group is calculated by CGSIZE. The maximum size
+ * is limited by the fact that cylinder groups are at most one block.
+ * Its size is derived from the size of the maps maintained in the
+ * cylinder group and the (struct cg) size.
+ */
+#define CGSIZE(fs) \
+ /* base cg */ (sizeof(struct cg) + sizeof(long) + \
+ /* blktot size */ (fs)->fs_cpg * sizeof(long) + \
+ /* blks size */ (fs)->fs_cpg * (fs)->fs_nrpos * sizeof(short) + \
+ /* inode map */ howmany((fs)->fs_ipg, NBBY) + \
+ /* block map */ howmany((fs)->fs_cpg * (fs)->fs_spc / NSPF(fs), NBBY) +\
+ /* if present */ ((fs)->fs_contigsumsize <= 0 ? 0 : \
+ /* cluster sum */ (fs)->fs_contigsumsize * sizeof(long) + \
+ /* cluster map */ howmany((fs)->fs_cpg * (fs)->fs_spc / NSPB(fs), NBBY)))
+
+/*
+ * Convert cylinder group to base address of its global summary info.
+ *
+ * N.B. This macro assumes that sizeof(struct csum) is a power of two.
+ */
+#define fs_cs(fs, indx) \
+ fs_csp[(indx) >> (fs)->fs_csshift][(indx) & ~(fs)->fs_csmask]
+
+/*
+ * Cylinder group block for a file system.
+ */
+#define CG_MAGIC 0x090255
+struct cg {
+ struct cg *cg_link; /* linked list of cyl groups */
+ long cg_magic; /* magic number */
+ time_t cg_time; /* time last written */
+ long cg_cgx; /* we are the cgx'th cylinder group */
+ short cg_ncyl; /* number of cyl's this cg */
+ short cg_niblk; /* number of inode blocks this cg */
+ long cg_ndblk; /* number of data blocks this cg */
+ struct csum cg_cs; /* cylinder summary information */
+ long cg_rotor; /* position of last used block */
+ long cg_frotor; /* position of last used frag */
+ long cg_irotor; /* position of last used inode */
+ long cg_frsum[MAXFRAG]; /* counts of available frags */
+ long cg_btotoff; /* (long) block totals per cylinder */
+ long cg_boff; /* (short) free block positions */
+ long cg_iusedoff; /* (char) used inode map */
+ long cg_freeoff; /* (u_char) free block map */
+ long cg_nextfreeoff; /* (u_char) next available space */
+ long cg_clustersumoff; /* (long) counts of avail clusters */
+ long cg_clusteroff; /* (char) free cluster map */
+ long cg_nclusterblks; /* number of clusters this cg */
+ long cg_sparecon[13]; /* reserved for future use */
+ u_char cg_space[1]; /* space for cylinder group maps */
+/* actually longer */
+};
+/*
+ * Macros for access to cylinder group array structures
+ */
+#define cg_blktot(cgp) \
+ (((cgp)->cg_magic != CG_MAGIC) \
+ ? (((struct ocg *)(cgp))->cg_btot) \
+ : ((long *)((char *)(cgp) + (cgp)->cg_btotoff)))
+#define cg_blks(fs, cgp, cylno) \
+ (((cgp)->cg_magic != CG_MAGIC) \
+ ? (((struct ocg *)(cgp))->cg_b[cylno]) \
+ : ((short *)((char *)(cgp) + (cgp)->cg_boff) + (cylno) * (fs)->fs_nrpos))
+#define cg_inosused(cgp) \
+ (((cgp)->cg_magic != CG_MAGIC) \
+ ? (((struct ocg *)(cgp))->cg_iused) \
+ : ((char *)((char *)(cgp) + (cgp)->cg_iusedoff)))
+#define cg_blksfree(cgp) \
+ (((cgp)->cg_magic != CG_MAGIC) \
+ ? (((struct ocg *)(cgp))->cg_free) \
+ : ((u_char *)((char *)(cgp) + (cgp)->cg_freeoff)))
+#define cg_chkmagic(cgp) \
+ ((cgp)->cg_magic == CG_MAGIC || ((struct ocg *)(cgp))->cg_magic == CG_MAGIC)
+#define cg_clustersfree(cgp) \
+ ((u_char *)((char *)(cgp) + (cgp)->cg_clusteroff))
+#define cg_clustersum(cgp) \
+ ((long *)((char *)(cgp) + (cgp)->cg_clustersumoff))
+
+/*
+ * The following structure is defined
+ * for compatibility with old file systems.
+ */
+struct ocg {
+ struct ocg *cg_link; /* linked list of cyl groups */
+ struct ocg *cg_rlink; /* used for incore cyl groups */
+ time_t cg_time; /* time last written */
+ long cg_cgx; /* we are the cgx'th cylinder group */
+ short cg_ncyl; /* number of cyl's this cg */
+ short cg_niblk; /* number of inode blocks this cg */
+ long cg_ndblk; /* number of data blocks this cg */
+ struct csum cg_cs; /* cylinder summary information */
+ long cg_rotor; /* position of last used block */
+ long cg_frotor; /* position of last used frag */
+ long cg_irotor; /* position of last used inode */
+ long cg_frsum[8]; /* counts of available frags */
+ long cg_btot[32]; /* block totals per cylinder */
+ short cg_b[32][8]; /* positions of free blocks */
+ char cg_iused[256]; /* used inode map */
+ long cg_magic; /* magic number */
+ u_char cg_free[1]; /* free block map */
+/* actually longer */
+};
+
+/*
+ * Turn file system block numbers into disk block addresses.
+ * This maps file system blocks to device size blocks.
+ */
+#define fsbtodb(fs, b) ((b) << (fs)->fs_fsbtodb)
+#define dbtofsb(fs, b) ((b) >> (fs)->fs_fsbtodb)
+
+/*
+ * Cylinder group macros to locate things in cylinder groups.
+ * They calc file system addresses of cylinder group data structures.
+ */
+#define cgbase(fs, c) ((daddr_t)((fs)->fs_fpg * (c)))
+#define cgdmin(fs, c) (cgstart(fs, c) + (fs)->fs_dblkno) /* 1st data */
+#define cgimin(fs, c) (cgstart(fs, c) + (fs)->fs_iblkno) /* inode blk */
+#define cgsblock(fs, c) (cgstart(fs, c) + (fs)->fs_sblkno) /* super blk */
+#define cgtod(fs, c) (cgstart(fs, c) + (fs)->fs_cblkno) /* cg block */
+#define cgstart(fs, c) \
+ (cgbase(fs, c) + (fs)->fs_cgoffset * ((c) & ~((fs)->fs_cgmask)))
+
+/*
+ * Macros for handling inode numbers:
+ * inode number to file system block offset.
+ * inode number to cylinder group number.
+ * inode number to file system block address.
+ */
+#define ino_to_cg(fs, x) ((x) / (fs)->fs_ipg)
+#define ino_to_fsba(fs, x) \
+ ((daddr_t)(cgimin(fs, ino_to_cg(fs, x)) + \
+ (blkstofrags((fs), (((x) % (fs)->fs_ipg) / INOPB(fs))))))
+#define ino_to_fsbo(fs, x) ((x) % INOPB(fs))
+
+/*
+ * Give cylinder group number for a file system block.
+ * Give cylinder group block number for a file system block.
+ */
+#define dtog(fs, d) ((d) / (fs)->fs_fpg)
+#define dtogd(fs, d) ((d) % (fs)->fs_fpg)
+
+/*
+ * Extract the bits for a block from a map.
+ * Compute the cylinder and rotational position of a cyl block addr.
+ */
+#define blkmap(fs, map, loc) \
+ (((map)[(loc) / NBBY] >> ((loc) % NBBY)) & (0xff >> (NBBY - (fs)->fs_frag)))
+#define cbtocylno(fs, bno) \
+ ((bno) * NSPF(fs) / (fs)->fs_spc)
+#define cbtorpos(fs, bno) \
+ (((bno) * NSPF(fs) % (fs)->fs_spc / (fs)->fs_nsect * (fs)->fs_trackskew + \
+ (bno) * NSPF(fs) % (fs)->fs_spc % (fs)->fs_nsect * (fs)->fs_interleave) % \
+ (fs)->fs_nsect * (fs)->fs_nrpos / (fs)->fs_npsect)
+
+/*
+ * The following macros optimize certain frequently calculated
+ * quantities by using shifts and masks in place of divisions
+ * modulos and multiplications.
+ */
+#define blkoff(fs, loc) /* calculates (loc % fs->fs_bsize) */ \
+ ((loc) & (fs)->fs_qbmask)
+#define fragoff(fs, loc) /* calculates (loc % fs->fs_fsize) */ \
+ ((loc) & (fs)->fs_qfmask)
+#define lblktosize(fs, blk) /* calculates (blk * fs->fs_bsize) */ \
+ ((blk) << (fs)->fs_bshift)
+#define lblkno(fs, loc) /* calculates (loc / fs->fs_bsize) */ \
+ ((loc) >> (fs)->fs_bshift)
+#define numfrags(fs, loc) /* calculates (loc / fs->fs_fsize) */ \
+ ((loc) >> (fs)->fs_fshift)
+#define blkroundup(fs, size) /* calculates roundup(size, fs->fs_bsize) */ \
+ (((size) + (fs)->fs_qbmask) & (fs)->fs_bmask)
+#define fragroundup(fs, size) /* calculates roundup(size, fs->fs_fsize) */ \
+ (((size) + (fs)->fs_qfmask) & (fs)->fs_fmask)
+#define fragstoblks(fs, frags) /* calculates (frags / fs->fs_frag) */ \
+ ((frags) >> (fs)->fs_fragshift)
+#define blkstofrags(fs, blks) /* calculates (blks * fs->fs_frag) */ \
+ ((blks) << (fs)->fs_fragshift)
+#define fragnum(fs, fsb) /* calculates (fsb % fs->fs_frag) */ \
+ ((fsb) & ((fs)->fs_frag - 1))
+#define blknum(fs, fsb) /* calculates rounddown(fsb, fs->fs_frag) */ \
+ ((fsb) &~ ((fs)->fs_frag - 1))
+
+/*
+ * Determine the number of available frags given a
+ * percentage to hold in reserve
+ */
+#define freespace(fs, percentreserved) \
+ (blkstofrags((fs), (fs)->fs_cstotal.cs_nbfree) + \
+ (fs)->fs_cstotal.cs_nffree - ((fs)->fs_dsize * (percentreserved) / 100))
+
+/*
+ * Determining the size of a file block in the file system.
+ */
+#define blksize(fs, ip, lbn) \
+ (((lbn) >= NDADDR || (ip)->i_size >= ((lbn) + 1) << (fs)->fs_bshift) \
+ ? (fs)->fs_bsize \
+ : (fragroundup(fs, blkoff(fs, (ip)->i_size))))
+#define dblksize(fs, dip, lbn) \
+ (((lbn) >= NDADDR || (dip)->di_size >= ((lbn) + 1) << (fs)->fs_bshift) \
+ ? (fs)->fs_bsize \
+ : (fragroundup(fs, blkoff(fs, (dip)->di_size))))
+
+/*
+ * Number of disk sectors per block; assumes DEV_BSIZE byte sector size.
+ */
+#define NSPB(fs) ((fs)->fs_nspf << (fs)->fs_fragshift)
+#define NSPF(fs) ((fs)->fs_nspf)
+
+/*
+ * INOPB is the number of inodes in a secondary storage block.
+ */
+#define INOPB(fs) ((fs)->fs_inopb)
+#define INOPF(fs) ((fs)->fs_inopb >> (fs)->fs_fragshift)
+
+/*
+ * NINDIR is the number of indirects in a file system block.
+ */
+#define NINDIR(fs) ((fs)->fs_nindir)
+
+extern int inside[], around[];
+extern u_char *fragtbl[];
diff --git a/sys/ufs/lfs/README b/sys/ufs/lfs/README
new file mode 100644
index 0000000..724b18f
--- /dev/null
+++ b/sys/ufs/lfs/README
@@ -0,0 +1,139 @@
+# @(#)README 8.1 (Berkeley) 6/11/93
+
+The file system is reasonably stable, but incomplete. There are
+places where cleaning performance can be improved dramatically (see
+comments in lfs_syscalls.c). For details on the implementation,
+performance and why garbage collection always wins, see Dr. Margo
+Seltzer's thesis available for anonymous ftp from toe.cs.berkeley.edu,
+in the directory pub/personal/margo/thesis.ps.Z, or the January 1993
+USENIX paper.
+
+Missing Functionality:
+ Multiple block sizes and/or fragments are not yet implemented.
+
+----------
+The disk is laid out in segments. The first segment starts 8K into the
+disk (the first 8K is used for boot information). Each segment is composed
+of the following:
+
+ An optional super block
+ One or more groups of:
+ segment summary
+ 0 or more data blocks
+ 0 or more inode blocks
+
+The segment summary and inode/data blocks start after the super block (if
+present), and grow toward the end of the segment.
+
+ _______________________________________________
+ | | | | |
+ | summary | data/inode | summary | data/inode |
+ | block | blocks | block | blocks | ...
+ |_________|____________|_________|____________|
+
+The data/inode blocks following a summary block are described by the
+summary block. In order to permit the segment to be written in any order
+and in a forward direction only, a checksum is calculated across the
+blocks described by the summary. Additionally, the summary is checksummed
+and timestamped. Both of these are intended for recovery; the former is
+to make it easy to determine that it *is* a summary block and the latter
+is to make it easy to determine when recovery is finished for partially
+written segments. These checksums are also used by the cleaner.
+
+ Summary block (detail)
+ ________________
+ | sum cksum |
+ | data cksum |
+ | next segment |
+ | timestamp |
+ | FINFO count |
+ | inode count |
+ | flags |
+ |______________|
+ | FINFO-1 | 0 or more file info structures, identifying the
+ | . | blocks in the segment.
+ | . |
+ | . |
+ | FINFO-N |
+ | inode-N |
+ | . |
+ | . |
+ | . | 0 or more inode daddr_t's, identifying the inode
+ | inode-1 | blocks in the segment.
+ |______________|
+
+Inode blocks are blocks of on-disk inodes in the same format as those in
+the FFS. However, spare[0] contains the inode number of the inode so we
+can find a particular inode on a page. They are packed page_size /
+sizeof(inode) to a block. Data blocks are exactly as in the FFS. Both
+inodes and data blocks move around the file system at will.
+
+The file system is described by a super-block which is replicated and
+occurs as the first block of the first and other segments. (The maximum
+number of super-blocks is MAXNUMSB). Each super-block maintains a list
+of the disk addresses of all the super-blocks. The super-block maintains
+a small amount of checkpoint information, essentially just enough to find
+the inode for the IFILE (fs->lfs_idaddr).
+
+The IFILE is visible in the file system, as inode number IFILE_INUM. It
+contains information shared between the kernel and various user processes.
+
+ Ifile (detail)
+ ________________
+ | cleaner info | Cleaner information per file system. (Page
+ | | granularity.)
+ |______________|
+ | segment | Space available and last modified times per
+ | usage table | segment. (Page granularity.)
+ |______________|
+ | IFILE-1 | Per inode status information: current version #,
+ | . | if currently allocated, last access time and
+ | . | current disk address of containing inode block.
+ | . | If current disk address is LFS_UNUSED_DADDR, the
+ | IFILE-N | inode is not in use, and it's on the free list.
+ |______________|
+
+
+First Segment at Creation Time:
+_____________________________________________________________
+| | | | | | | |
+| 8K pad | Super | summary | inode | ifile | root | l + f |
+| | block | | block | | dir | dir |
+|________|_______|_________|_______|_______|_______|_______|
+ ^
+ Segment starts here.
+
+Some differences from the Sprite LFS implementation.
+
+1. The LFS implementation placed the ifile metadata and the super block
+ at fixed locations. This implementation replicates the super block
+ and puts each at a fixed location. The checkpoint data is divided into
+ two parts -- just enough information to find the IFILE is stored in
+ two of the super blocks, although it is not toggled between them as in
+ the Sprite implementation. (This was deliberate, to avoid a single
+ point of failure.) The remaining checkpoint information is treated as
+ a regular file, which means that the cleaner info, the segment usage
+ table and the ifile meta-data are stored in normal log segments.
+ (Tastes great, less filling...)
+
+2. The segment layout is radically different in Sprite; this implementation
+ uses something a lot like network framing, where data/inode blocks are
+ written asynchronously, and a checksum is used to validate any set of
+ summary and data/inode blocks. Sprite writes summary blocks synchronously
+ after the data/inode blocks have been written and the existence of the
+ summary block validates the data/inode blocks. This permits us to write
+ everything contiguously, even partial segments and their summaries, whereas
+ Sprite is forced to seek (from the end of the data inode to the summary
+ which lives at the end of the segment). Additionally, writing the summary
+ synchronously should cost about 1/2 a rotation per summary.
+
+3. Sprite LFS distinguishes between different types of blocks in the segment.
+ Other than inode blocks and data blocks, we don't.
+
+4. Sprite LFS traverses the IFILE looking for free blocks. We maintain a
+ free list threaded through the IFILE entries.
+
+5. The cleaner runs in user space, as opposed to kernel space. It shares
+ information with the kernel by reading/writing the IFILE and through
+ cleaner specific system calls.
+
diff --git a/sys/ufs/lfs/TODO b/sys/ufs/lfs/TODO
new file mode 100644
index 0000000..ace8f5e
--- /dev/null
+++ b/sys/ufs/lfs/TODO
@@ -0,0 +1,116 @@
+# @(#)TODO 8.1 (Berkeley) 6/11/93
+
+NOTE: Changed the lookup on a page of inodes to search from the back
+in case the same inode gets written twice on the same page.
+
+Make sure that if you are writing a file, but not all the blocks
+make it into a single segment, that you do not write the inode in
+that segment.
+
+Keith:
+ Why not delete the lfs_bmapv call, just mark everything dirty
+ that isn't deleted/truncated? Get some numbers about
+ what percentage of the stuff that the cleaner thinks
+ might be live is live. If it's high, get rid of lfs_bmapv.
+
+ There is a nasty problem in that it may take *more* room to write
+ the data to clean a segment than is returned by the new segment
+ because of indirect blocks in segment 2 being dirtied by the data
+ being copied into the log from segment 1. The suggested solution
+ at this point is to detect it when we have no space left on the
+ filesystem, write the extra data into the last segment (leaving
+ no clean ones), make it a checkpoint and shut down the file system
+ for fixing by a utility reading the raw partition. Argument is
+ that this should never happen and is practically impossible to fix
+ since the cleaner would have to theoretically build a model of the
+ entire filesystem in memory to detect the condition occurring.
+ A file coalescing cleaner will help avoid the problem, and one
+ that reads/writes from the raw disk could fix it.
+
+DONE Currently, inodes are being flushed to disk synchronously upon
+ creation -- see ufs_makeinode. However, only the inode
+ is flushed, the directory "name" is written using VOP_BWRITE,
+ so it's not synchronous. Possible solutions: 1: get some
+ ordering in the writes so that inode/directory entries get
+ stuffed into the same segment. 2: do both synchronously
+ 3: add Mendel's information into the stream so we log
+ creation/deletion of inodes. 4: do some form of partial
+ segment when changing the inode (creation/deletion/rename).
+DONE Fix i_block increment for indirect blocks.
+ If the file system is tar'd, extracted on top of another LFS, the
+ IFILE ain't worth diddly. Is the cleaner writing the IFILE?
+ If not, let's make it read-only.
+DONE Delete unnecessary source from utils in main-line source tree.
+DONE Make sure that we're counting meta blocks in the inode i_block count.
+ Overlap the version and nextfree fields in the IFILE
+DONE Vinvalbuf (Kirk):
+ Why writing blocks that are no longer useful?
+ Are the semantics of close such that blocks have to be flushed?
+ How specify in the buf chain the blocks that don't need
+ to be written? (Different numbering of indirect blocks.)
+
+Margo:
+ Change so that only search one sector of inode block file for the
+ inode by using sector addresses in the ifile instead of
+ logical disk addresses.
+ Fix the use of the ifile version field to use the generation
+ number instead.
+DONE Unmount; not doing a bgetvp (VHOLD) in lfs_newbuf call.
+DONE Document in the README file where the checkpoint information is
+ on disk.
+ Variable block sizes (Margo/Keith).
+ Switch the byte accounting to sector accounting.
+DONE Check lfs.h and make sure that the #defines/structures are all
+ actually needed.
+DONE Add a check in lfs_segment.c so that if the segment is empty,
+ we don't write it.
+ Need to keep vnode v_numoutput up to date for pending writes?
+DONE USENIX paper (Carl/Margo).
+
+
+Evelyn:
+ lfsck: If delete a file that's being executed, the version number
+ isn't updated, and lfsck has to figure this out; case is the same as if have an inode that no directory references,
+ so the file should be reattached into lost+found.
+ Recovery/fsck.
+
+Carl:
+ Investigate: clustering of reads (if blocks in the segment are ordered,
+ should read them all) and writes (McVoy paper).
+ Investigate: should the access time be part of the IFILE:
+ pro: theoretically, saves disk writes
+ con: cacheing inodes should obviate this advantage
+ the IFILE is already humongous
+ Cleaner.
+ Port to OSF/1 (Carl/Keith).
+ Currently there's no notion of write error checking.
+ + Failed data/inode writes should be rescheduled (kernel level
+ bad blocking).
+ + Failed superblock writes should cause selection of new
+ superblock for checkpointing.
+
+FUTURE FANTASIES: ============
+
++ unrm, versioning
++ transactions
++ extended cleaner policies (hot/cold data, data placement)
+
+==============================
+Problem with the concept of multiple buffer headers referencing the segment:
+Positives:
+ Don't lock down 1 segment per file system of physical memory.
+ Don't copy from buffers to segment memory.
+ Don't tie down the bus to transfer 1M.
+ Works on controllers supporting less than large transfers.
+ Disk can start writing immediately instead of waiting 1/2 rotation
+ and the full transfer.
+Negatives:
+ Have to do segment write then segment summary write, since the latter
+ is what verifies that the segment is okay. (Is there another way
+ to do this?)
+==============================
+
+The algorithm for selecting the disk addresses of the super-blocks
+has to be available to the user program which checks the file system.
+
+(Currently in newfs, becomes a common subroutine.)
diff --git a/sys/ufs/lfs/lfs.h b/sys/ufs/lfs/lfs.h
new file mode 100644
index 0000000..87b8c22
--- /dev/null
+++ b/sys/ufs/lfs/lfs.h
@@ -0,0 +1,353 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lfs.h 8.3 (Berkeley) 9/23/93
+ */
+
+#define LFS_LABELPAD 8192 /* LFS label size */
+#define LFS_SBPAD 8192 /* LFS superblock size */
+
+/*
+ * XXX
+ * This is a kluge and NEEDS to go away.
+ *
+ * Right now, ufs code handles most of the calls for directory operations
+ * such as create, mkdir, link, etc. As a result VOP_UPDATE is being
+ * called with waitfor set (since ffs does these things synchronously).
+ * Since LFS does not want to do these synchronously, we treat the last
+ * argument to lfs_update as a set of flags. If LFS_SYNC is set, then
+ * the update should be synchronous, if not, do it asynchronously.
+ * Unfortunately, this means that LFS won't work with NFS yet because
+ * NFS goes through paths that will make normal calls to ufs which will
+ * call lfs with a last argument of 1.
+ */
+#define LFS_SYNC 0x02
+
+/* On-disk and in-memory checkpoint segment usage structure. */
+typedef struct segusage SEGUSE;
+struct segusage {
+ u_long su_nbytes; /* number of live bytes */
+ u_long su_lastmod; /* SEGUSE last modified timestamp */
+ u_short su_nsums; /* number of summaries in segment */
+ u_short su_ninos; /* number of inode blocks in seg */
+#define SEGUSE_ACTIVE 0x1 /* segment is currently being written */
+#define SEGUSE_DIRTY 0x2 /* segment has data in it */
+#define SEGUSE_SUPERBLOCK 0x4 /* segment contains a superblock */
+ u_long su_flags;
+};
+
+#define SEGUPB(fs) (1 << (fs)->lfs_sushift)
+#define SEGTABSIZE_SU(fs) \
+ (((fs)->lfs_nseg + SEGUPB(fs) - 1) >> (fs)->lfs_sushift)
+
+/* On-disk file information. One per file with data blocks in the segment. */
+typedef struct finfo FINFO;
+struct finfo {
+ u_long fi_nblocks; /* number of blocks */
+ u_long fi_version; /* version number */
+ u_long fi_ino; /* inode number */
+ long fi_blocks[1]; /* array of logical block numbers */
+};
+
+/* On-disk and in-memory super block. */
+struct lfs {
+#define LFS_MAGIC 0x070162
+ u_long lfs_magic; /* magic number */
+#define LFS_VERSION 1
+ u_long lfs_version; /* version number */
+
+ u_long lfs_size; /* number of blocks in fs */
+ u_long lfs_ssize; /* number of blocks per segment */
+ u_long lfs_dsize; /* number of disk blocks in fs */
+ u_long lfs_bsize; /* file system block size */
+ u_long lfs_fsize; /* size of frag blocks in fs */
+ u_long lfs_frag; /* number of frags in a block in fs */
+
+/* Checkpoint region. */
+ ino_t lfs_free; /* start of the free list */
+ u_long lfs_bfree; /* number of free disk blocks */
+ u_long lfs_nfiles; /* number of allocated inodes */
+ long lfs_avail; /* blocks available for writing */
+ u_long lfs_uinodes; /* inodes in cache not yet on disk */
+ daddr_t lfs_idaddr; /* inode file disk address */
+ ino_t lfs_ifile; /* inode file inode number */
+ daddr_t lfs_lastseg; /* address of last segment written */
+ daddr_t lfs_nextseg; /* address of next segment to write */
+ daddr_t lfs_curseg; /* current segment being written */
+ daddr_t lfs_offset; /* offset in curseg for next partial */
+ daddr_t lfs_lastpseg; /* address of last partial written */
+ u_long lfs_tstamp; /* time stamp */
+
+/* These are configuration parameters. */
+ u_long lfs_minfree; /* minimum percentage of free blocks */
+
+/* These fields can be computed from the others. */
+ u_quad_t lfs_maxfilesize; /* maximum representable file size */
+ u_long lfs_dbpseg; /* disk blocks per segment */
+ u_long lfs_inopb; /* inodes per block */
+ u_long lfs_ifpb; /* IFILE entries per block */
+ u_long lfs_sepb; /* SEGUSE entries per block */
+ u_long lfs_nindir; /* indirect pointers per block */
+ u_long lfs_nseg; /* number of segments */
+ u_long lfs_nspf; /* number of sectors per fragment */
+ u_long lfs_cleansz; /* cleaner info size in blocks */
+ u_long lfs_segtabsz; /* segment table size in blocks */
+
+ u_long lfs_segmask; /* calculate offset within a segment */
+ u_long lfs_segshift; /* fast mult/div for segments */
+ u_long lfs_bmask; /* calc block offset from file offset */
+ u_long lfs_bshift; /* calc block number from file offset */
+ u_long lfs_ffmask; /* calc frag offset from file offset */
+ u_long lfs_ffshift; /* fast mult/div for frag from file */
+ u_long lfs_fbmask; /* calc frag offset from block offset */
+ u_long lfs_fbshift; /* fast mult/div for frag from block */
+ u_long lfs_fsbtodb; /* fsbtodb and dbtofsb shift constant */
+ u_long lfs_sushift; /* fast mult/div for segusage table */
+
+#define LFS_MIN_SBINTERVAL 5 /* minimum superblock segment spacing */
+#define LFS_MAXNUMSB 10 /* superblock disk offsets */
+ daddr_t lfs_sboffs[LFS_MAXNUMSB];
+
+/* These fields are set at mount time and are meaningless on disk. */
+ struct segment *lfs_sp; /* current segment being written */
+ struct vnode *lfs_ivnode; /* vnode for the ifile */
+ u_long lfs_seglock; /* single-thread the segment writer */
+ pid_t lfs_lockpid; /* pid of lock holder */
+ u_long lfs_iocount; /* number of ios pending */
+ u_long lfs_writer; /* don't allow any dirops to start */
+ u_long lfs_dirops; /* count of active directory ops */
+ u_long lfs_doifile; /* Write ifile blocks on next write */
+ u_long lfs_nactive; /* Number of segments since last ckp */
+ u_char lfs_fmod; /* super block modified flag */
+ u_char lfs_clean; /* file system is clean flag */
+ u_char lfs_ronly; /* mounted read-only flag */
+ u_char lfs_flags; /* currently unused flag */
+ u_char lfs_fsmnt[MNAMELEN]; /* name mounted on */
+ u_char pad[3]; /* long-align */
+
+/* Checksum; valid on disk. */
+ u_long lfs_cksum; /* checksum for superblock checking */
+};
+
+/*
+ * Inode 0 is the out-of-band inode number, inode 1 is the inode number for
+ * the IFILE, the root inode is 2 and the lost+found inode is 3.
+ */
+
+/* Fixed inode numbers. */
+#define LFS_UNUSED_INUM 0 /* out of band inode number */
+#define LFS_IFILE_INUM 1 /* IFILE inode number */
+#define LOSTFOUNDINO 3 /* lost+found inode number */
+#define LFS_FIRST_INUM 4 /* first free inode number */
+
+/* Address calculations for metadata located in the inode */
+#define S_INDIR(fs) -NDADDR
+#define D_INDIR(fs) (S_INDIR(fs) - NINDIR(fs) - 1)
+#define T_INDIR(fs) (D_INDIR(fs) - NINDIR(fs) * NINDIR(fs) - 1)
+
+/* Unassigned disk address. */
+#define UNASSIGNED -1
+
+/* Unused logical block number */
+#define LFS_UNUSED_LBN -1
+
+typedef struct ifile IFILE;
+struct ifile {
+ u_long if_version; /* inode version number */
+#define LFS_UNUSED_DADDR 0 /* out-of-band daddr */
+ daddr_t if_daddr; /* inode disk address */
+ ino_t if_nextfree; /* next-unallocated inode */
+};
+
+/*
+ * Cleaner information structure. This resides in the ifile and is used
+ * to pass information between the cleaner and the kernel.
+ */
+typedef struct _cleanerinfo {
+ u_long clean; /* K: number of clean segments */
+ u_long dirty; /* K: number of dirty segments */
+} CLEANERINFO;
+
+#define CLEANSIZE_SU(fs) \
+ ((sizeof(CLEANERINFO) + (fs)->lfs_bsize - 1) >> (fs)->lfs_bshift)
+
+/*
+ * All summary blocks are the same size, so we can always read a summary
+ * block easily from a segment.
+ */
+#define LFS_SUMMARY_SIZE 512
+
+/* On-disk segment summary information */
+typedef struct segsum SEGSUM;
+struct segsum {
+ u_long ss_sumsum; /* check sum of summary block */
+ u_long ss_datasum; /* check sum of data */
+ daddr_t ss_next; /* next segment */
+ u_long ss_create; /* creation time stamp */
+ u_short ss_nfinfo; /* number of file info structures */
+ u_short ss_ninos; /* number of inodes in summary */
+#define SS_DIROP 0x01 /* segment begins a dirop */
+#define SS_CONT 0x02 /* more partials to finish this write*/
+ u_short ss_flags; /* used for directory operations */
+ u_short ss_pad; /* extra space */
+ /* FINFO's and inode daddr's... */
+};
+
+/* NINDIR is the number of indirects in a file system block. */
+#define NINDIR(fs) ((fs)->lfs_nindir)
+
+/* INOPB is the number of inodes in a secondary storage block. */
+#define INOPB(fs) ((fs)->lfs_inopb)
+
+#define blksize(fs) ((fs)->lfs_bsize)
+#define blkoff(fs, loc) ((loc) & (fs)->lfs_bmask)
+#define fsbtodb(fs, b) ((b) << (fs)->lfs_fsbtodb)
+#define dbtofsb(fs, b) ((b) >> (fs)->lfs_fsbtodb)
+#define lblkno(fs, loc) ((loc) >> (fs)->lfs_bshift)
+#define lblktosize(fs, blk) ((blk) << (fs)->lfs_bshift)
+#define numfrags(fs, loc) /* calculates (loc / fs->fs_fsize) */ \
+ ((loc) >> (fs)->lfs_bshift)
+
+#define datosn(fs, daddr) /* disk address to segment number */ \
+ (((daddr) - (fs)->lfs_sboffs[0]) / fsbtodb((fs), (fs)->lfs_ssize))
+#define sntoda(fs, sn) /* segment number to disk address */ \
+ ((daddr_t)((sn) * ((fs)->lfs_ssize << (fs)->lfs_fsbtodb) + \
+ (fs)->lfs_sboffs[0]))
+
+/* Read in the block with the cleaner info from the ifile. */
+#define LFS_CLEANERINFO(CP, F, BP) { \
+ VTOI((F)->lfs_ivnode)->i_flag |= IN_ACCESS; \
+ if (bread((F)->lfs_ivnode, \
+ (daddr_t)0, (F)->lfs_bsize, NOCRED, &(BP))) \
+ panic("lfs: ifile read"); \
+ (CP) = (CLEANERINFO *)(BP)->b_data; \
+}
+
+/* Read in the block with a specific inode from the ifile. */
+#define LFS_IENTRY(IP, F, IN, BP) { \
+ int _e; \
+ VTOI((F)->lfs_ivnode)->i_flag |= IN_ACCESS; \
+ if (_e = bread((F)->lfs_ivnode, \
+ (IN) / (F)->lfs_ifpb + (F)->lfs_cleansz + (F)->lfs_segtabsz,\
+ (F)->lfs_bsize, NOCRED, &(BP))) \
+ panic("lfs: ifile read %d", _e); \
+ (IP) = (IFILE *)(BP)->b_data + (IN) % (F)->lfs_ifpb; \
+}
+
+/* Read in the block with a specific segment usage entry from the ifile. */
+#define LFS_SEGENTRY(SP, F, IN, BP) { \
+ int _e; \
+ VTOI((F)->lfs_ivnode)->i_flag |= IN_ACCESS; \
+ if (_e = bread((F)->lfs_ivnode, \
+ ((IN) >> (F)->lfs_sushift) + (F)->lfs_cleansz, \
+ (F)->lfs_bsize, NOCRED, &(BP))) \
+ panic("lfs: ifile read: %d", _e); \
+ (SP) = (SEGUSE *)(BP)->b_data + ((IN) & (F)->lfs_sepb - 1); \
+}
+
+/*
+ * Determine if there is enough room currently available to write db
+ * disk blocks. We need enough blocks for the new blocks, the current,
+ * inode blocks, a summary block, plus potentially the ifile inode and
+ * the segment usage table, plus an ifile page.
+ */
+#define LFS_FITS(fs, db) \
+ ((long)((db + ((fs)->lfs_uinodes + INOPB((fs))) / INOPB((fs)) + \
+ fsbtodb(fs, 1) + LFS_SUMMARY_SIZE / DEV_BSIZE + \
+ (fs)->lfs_segtabsz)) < (fs)->lfs_avail)
+
+/* Determine if a buffer belongs to the ifile */
+#define IS_IFILE(bp) (VTOI(bp->b_vp)->i_number == LFS_IFILE_INUM)
+
+/*
+ * Structures used by lfs_bmapv and lfs_markv to communicate information
+ * about inodes and data blocks.
+ */
+typedef struct block_info {
+ ino_t bi_inode; /* inode # */
+ daddr_t bi_lbn; /* logical block w/in file */
+ daddr_t bi_daddr; /* disk address of block */
+ time_t bi_segcreate; /* origin segment create time */
+ int bi_version; /* file version number */
+ void *bi_bp; /* data buffer */
+} BLOCK_INFO;
+
+/* In-memory description of a segment about to be written. */
+struct segment {
+ struct lfs *fs; /* file system pointer */
+ struct buf **bpp; /* pointer to buffer array */
+ struct buf **cbpp; /* pointer to next available bp */
+ struct buf **start_bpp; /* pointer to first bp in this set */
+ struct buf *ibp; /* buffer pointer to inode page */
+ struct finfo *fip; /* current fileinfo pointer */
+ struct vnode *vp; /* vnode being gathered */
+ void *segsum; /* segment summary info */
+ u_long ninodes; /* number of inodes in this segment */
+ u_long seg_bytes_left; /* bytes left in segment */
+ u_long sum_bytes_left; /* bytes left in summary block */
+ u_long seg_number; /* number of this segment */
+ daddr_t *start_lbp; /* beginning lbn for this set */
+#define SEGM_CKP 0x01 /* doing a checkpoint */
+#define SEGM_CLEAN 0x02 /* cleaner call; don't sort */
+#define SEGM_SYNC 0x04 /* wait for segment */
+ u_long seg_flags; /* run-time flags for this segment */
+};
+
+#define ISSPACE(F, BB, C) \
+ (((C)->cr_uid == 0 && (F)->lfs_bfree >= (BB)) || \
+ ((C)->cr_uid != 0 && IS_FREESPACE(F, BB)))
+
+#define IS_FREESPACE(F, BB) \
+ ((F)->lfs_bfree > ((F)->lfs_dsize * (F)->lfs_minfree / 100 + (BB)))
+
+#define ISSPACE_XXX(F, BB) \
+ ((F)->lfs_bfree >= (BB))
+
+#define DOSTATS
+#ifdef DOSTATS
+/* Statistics Counters */
+struct lfs_stats {
+ int segsused;
+ int psegwrites;
+ int psyncwrites;
+ int pcleanwrites;
+ int blocktot;
+ int cleanblocks;
+ int ncheckpoints;
+ int nwrites;
+ int nsync_writes;
+ int wait_exceeded;
+ int write_exceeded;
+ int flush_invoked;
+};
+extern struct lfs_stats lfs_stats;
+#endif
diff --git a/sys/ufs/lfs/lfs_alloc.c b/sys/ufs/lfs/lfs_alloc.c
new file mode 100644
index 0000000..3f06c81
--- /dev/null
+++ b/sys/ufs/lfs/lfs_alloc.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lfs_alloc.c 8.4 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/syslog.h>
+#include <sys/mount.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+extern u_long nextgennumber;
+
+/* Allocate a new inode. */
+/* ARGSUSED */
+int
+lfs_valloc(ap)
+ struct vop_valloc_args /* {
+ struct vnode *a_pvp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct vnode **a_vpp;
+ } */ *ap;
+{
+ struct lfs *fs;
+ struct buf *bp;
+ struct ifile *ifp;
+ struct inode *ip;
+ struct vnode *vp;
+ daddr_t blkno;
+ ino_t new_ino;
+ u_long i, max;
+ int error;
+
+ /* Get the head of the freelist. */
+ fs = VTOI(ap->a_pvp)->i_lfs;
+ new_ino = fs->lfs_free;
+#ifdef ALLOCPRINT
+ printf("lfs_ialloc: allocate inode %d\n", new_ino);
+#endif
+
+ /*
+ * Remove the inode from the free list and write the new start
+ * of the free list into the superblock.
+ */
+ LFS_IENTRY(ifp, fs, new_ino, bp);
+ if (ifp->if_daddr != LFS_UNUSED_DADDR)
+ panic("lfs_ialloc: inuse inode on the free list");
+ fs->lfs_free = ifp->if_nextfree;
+ brelse(bp);
+
+ /* Extend IFILE so that the next lfs_valloc will succeed. */
+ if (fs->lfs_free == LFS_UNUSED_INUM) {
+ vp = fs->lfs_ivnode;
+ ip = VTOI(vp);
+ blkno = lblkno(fs, ip->i_size);
+ lfs_balloc(vp, fs->lfs_bsize, blkno, &bp);
+ ip->i_size += fs->lfs_bsize;
+ vnode_pager_setsize(vp, (u_long)ip->i_size);
+ vnode_pager_uncache(vp);
+
+ i = (blkno - fs->lfs_segtabsz - fs->lfs_cleansz) *
+ fs->lfs_ifpb;
+ fs->lfs_free = i;
+ max = i + fs->lfs_ifpb;
+ for (ifp = (struct ifile *)bp->b_data; i < max; ++ifp) {
+ ifp->if_version = 1;
+ ifp->if_daddr = LFS_UNUSED_DADDR;
+ ifp->if_nextfree = ++i;
+ }
+ ifp--;
+ ifp->if_nextfree = LFS_UNUSED_INUM;
+ if (error = VOP_BWRITE(bp))
+ return (error);
+ }
+
+ /* Create a vnode to associate with the inode. */
+ if (error = lfs_vcreate(ap->a_pvp->v_mount, new_ino, &vp))
+ return (error);
+
+
+ ip = VTOI(vp);
+ /* Zero out the direct and indirect block addresses. */
+ bzero(&ip->i_din, sizeof(struct dinode));
+ ip->i_din.di_inumber = new_ino;
+
+ /* Set a new generation number for this inode. */
+ if (++nextgennumber < (u_long)time.tv_sec)
+ nextgennumber = time.tv_sec;
+ ip->i_gen = nextgennumber;
+
+ /* Insert into the inode hash table. */
+ ufs_ihashins(ip);
+
+ if (error = ufs_vinit(vp->v_mount, lfs_specop_p, LFS_FIFOOPS, &vp)) {
+ vput(vp);
+ *ap->a_vpp = NULL;
+ return (error);
+ }
+
+ *ap->a_vpp = vp;
+ vp->v_flag |= VDIROP;
+ VREF(ip->i_devvp);
+
+ /* Set superblock modified bit and increment file count. */
+ fs->lfs_fmod = 1;
+ ++fs->lfs_nfiles;
+ return (0);
+}
+
+/* Create a new vnode/inode pair and initialize what fields we can. */
+int
+lfs_vcreate(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+ extern int (**lfs_vnodeop_p)();
+ struct inode *ip;
+ struct ufsmount *ump;
+ int error, i;
+
+ /* Create the vnode. */
+ if (error = getnewvnode(VT_LFS, mp, lfs_vnodeop_p, vpp)) {
+ *vpp = NULL;
+ return (error);
+ }
+
+ /* Get a pointer to the private mount structure. */
+ ump = VFSTOUFS(mp);
+
+ /* Initialize the inode. */
+ MALLOC(ip, struct inode *, sizeof(struct inode), M_LFSNODE, M_WAITOK);
+ (*vpp)->v_data = ip;
+ ip->i_vnode = *vpp;
+ ip->i_devvp = ump->um_devvp;
+ ip->i_flag = IN_MODIFIED;
+ ip->i_dev = ump->um_dev;
+ ip->i_number = ip->i_din.di_inumber = ino;
+ip->i_din.di_spare[0] = 0xdeadbeef;
+ip->i_din.di_spare[1] = 0xdeadbeef;
+ ip->i_lfs = ump->um_lfs;
+#ifdef QUOTA
+ for (i = 0; i < MAXQUOTAS; i++)
+ ip->i_dquot[i] = NODQUOT;
+#endif
+ ip->i_lockf = 0;
+ ip->i_diroff = 0;
+ ip->i_mode = 0;
+ ip->i_size = 0;
+ ip->i_blocks = 0;
+ ++ump->um_lfs->lfs_uinodes;
+ return (0);
+}
+
+/* Free an inode. */
+/* ARGUSED */
+int
+lfs_vfree(ap)
+ struct vop_vfree_args /* {
+ struct vnode *a_pvp;
+ ino_t a_ino;
+ int a_mode;
+ } */ *ap;
+{
+ SEGUSE *sup;
+ struct buf *bp;
+ struct ifile *ifp;
+ struct inode *ip;
+ struct lfs *fs;
+ daddr_t old_iaddr;
+ ino_t ino;
+
+ /* Get the inode number and file system. */
+ ip = VTOI(ap->a_pvp);
+ fs = ip->i_lfs;
+ ino = ip->i_number;
+ if (ip->i_flag & IN_MODIFIED) {
+ --fs->lfs_uinodes;
+ ip->i_flag &=
+ ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
+ }
+ /*
+ * Set the ifile's inode entry to unused, increment its version number
+ * and link it into the free chain.
+ */
+ LFS_IENTRY(ifp, fs, ino, bp);
+ old_iaddr = ifp->if_daddr;
+ ifp->if_daddr = LFS_UNUSED_DADDR;
+ ++ifp->if_version;
+ ifp->if_nextfree = fs->lfs_free;
+ fs->lfs_free = ino;
+ (void) VOP_BWRITE(bp);
+
+ if (old_iaddr != LFS_UNUSED_DADDR) {
+ LFS_SEGENTRY(sup, fs, datosn(fs, old_iaddr), bp);
+#ifdef DIAGNOSTIC
+ if (sup->su_nbytes < sizeof(struct dinode))
+ panic("lfs_vfree: negative byte count (segment %d)\n",
+ datosn(fs, old_iaddr));
+#endif
+ sup->su_nbytes -= sizeof(struct dinode);
+ (void) VOP_BWRITE(bp);
+ }
+
+ /* Set superblock modified bit and decrement file count. */
+ fs->lfs_fmod = 1;
+ --fs->lfs_nfiles;
+ return (0);
+}
diff --git a/sys/ufs/lfs/lfs_balloc.c b/sys/ufs/lfs/lfs_balloc.c
new file mode 100644
index 0000000..b56bc9e
--- /dev/null
+++ b/sys/ufs/lfs/lfs_balloc.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lfs_balloc.c 8.1 (Berkeley) 6/11/93
+ */
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/resourcevar.h>
+#include <sys/trace.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+int
+lfs_balloc(vp, iosize, lbn, bpp)
+ struct vnode *vp;
+ u_long iosize;
+ daddr_t lbn;
+ struct buf **bpp;
+{
+ struct buf *ibp, *bp;
+ struct inode *ip;
+ struct lfs *fs;
+ struct indir indirs[NIADDR+2];
+ daddr_t daddr;
+ int bb, error, i, num;
+
+ ip = VTOI(vp);
+ fs = ip->i_lfs;
+
+ /*
+ * Three cases: it's a block beyond the end of file, it's a block in
+ * the file that may or may not have been assigned a disk address or
+ * we're writing an entire block. Note, if the daddr is unassigned,
+ * the block might still have existed in the cache (if it was read
+ * or written earlier). If it did, make sure we don't count it as a
+ * new block or zero out its contents. If it did not, make sure
+ * we allocate any necessary indirect blocks.
+ */
+
+ *bpp = NULL;
+ if (error = ufs_bmaparray(vp, lbn, &daddr, &indirs[0], &num, NULL ))
+ return (error);
+
+ *bpp = bp = getblk(vp, lbn, fs->lfs_bsize, 0, 0);
+ bb = VFSTOUFS(vp->v_mount)->um_seqinc;
+ if (daddr == UNASSIGNED)
+ /* May need to allocate indirect blocks */
+ for (i = 1; i < num; ++i)
+ if (!indirs[i].in_exists) {
+ ibp =
+ getblk(vp, indirs[i].in_lbn, fs->lfs_bsize,
+ 0, 0);
+ if (!(ibp->b_flags & (B_DONE | B_DELWRI))) {
+ if (!ISSPACE(fs, bb, curproc->p_ucred)){
+ ibp->b_flags |= B_INVAL;
+ brelse(ibp);
+ error = ENOSPC;
+ } else {
+ ip->i_blocks += bb;
+ ip->i_lfs->lfs_bfree -= bb;
+ clrbuf(ibp);
+ error = VOP_BWRITE(ibp);
+ }
+ } else
+ panic ("Indirect block should not exist");
+ }
+ if (error) {
+ if (bp)
+ brelse(bp);
+ return(error);
+ }
+
+
+ /* Now, we may need to allocate the data block */
+ if (!(bp->b_flags & (B_CACHE | B_DONE | B_DELWRI))) {
+ if (daddr == UNASSIGNED)
+ if (!ISSPACE(fs, bb, curproc->p_ucred)) {
+ bp->b_flags |= B_INVAL;
+ brelse(bp);
+ return(ENOSPC);
+ } else {
+ ip->i_blocks += bb;
+ ip->i_lfs->lfs_bfree -= bb;
+ if (iosize != fs->lfs_bsize)
+ clrbuf(bp);
+ }
+ else if (iosize == fs->lfs_bsize)
+ bp->b_blkno = daddr; /* Skip the I/O */
+ else {
+ bp->b_blkno = daddr;
+ bp->b_flags |= B_READ;
+ VOP_STRATEGY(bp);
+ return(biowait(bp));
+ }
+ }
+ return (error);
+}
diff --git a/sys/ufs/lfs/lfs_bio.c b/sys/ufs/lfs/lfs_bio.c
new file mode 100644
index 0000000..d225427
--- /dev/null
+++ b/sys/ufs/lfs/lfs_bio.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lfs_bio.c 8.4 (Berkeley) 12/30/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/resourcevar.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+/*
+ * LFS block write function.
+ *
+ * XXX
+ * No write cost accounting is done.
+ * This is almost certainly wrong for synchronous operations and NFS.
+ */
+int lfs_allclean_wakeup; /* Cleaner wakeup address. */
+int locked_queue_count; /* XXX Count of locked-down buffers. */
+int lfs_writing; /* Set if already kicked off a writer
+ because of buffer space */
+/*
+#define WRITE_THRESHHOLD ((nbuf >> 2) - 10)
+#define WAIT_THRESHHOLD ((nbuf >> 1) - 10)
+*/
+#define WAIT_THRESHHOLD (nbuf - (nbuf >> 2) - 10)
+#define WRITE_THRESHHOLD ((nbuf >> 1) - 10)
+#define LFS_BUFWAIT 2
+
+int
+lfs_bwrite(ap)
+ struct vop_bwrite_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ register struct buf *bp = ap->a_bp;
+ struct lfs *fs;
+ struct inode *ip;
+ int error, s;
+
+ /*
+ * Set the delayed write flag and use reassignbuf to move the buffer
+ * from the clean list to the dirty one.
+ *
+ * Set the B_LOCKED flag and unlock the buffer, causing brelse to move
+ * the buffer onto the LOCKED free list. This is necessary, otherwise
+ * getnewbuf() would try to reclaim the buffers using bawrite, which
+ * isn't going to work.
+ *
+ * XXX we don't let meta-data writes run out of space because they can
+ * come from the segment writer. We need to make sure that there is
+ * enough space reserved so that there's room to write meta-data
+ * blocks.
+ */
+ if (!(bp->b_flags & B_LOCKED)) {
+ fs = VFSTOUFS(bp->b_vp->v_mount)->um_lfs;
+ while (!LFS_FITS(fs, fsbtodb(fs, 1)) && !IS_IFILE(bp) &&
+ bp->b_lblkno > 0) {
+ /* Out of space, need cleaner to run */
+ wakeup(&lfs_allclean_wakeup);
+ if (error = tsleep(&fs->lfs_avail, PCATCH | PUSER,
+ "cleaner", NULL)) {
+ brelse(bp);
+ return (error);
+ }
+ }
+ ip = VTOI((bp)->b_vp);
+ if (!(ip->i_flag & IN_MODIFIED))
+ ++fs->lfs_uinodes;
+ ip->i_flag |= IN_CHANGE | IN_MODIFIED | IN_UPDATE;
+ fs->lfs_avail -= fsbtodb(fs, 1);
+ ++locked_queue_count;
+ bp->b_flags |= B_DELWRI | B_LOCKED;
+ bp->b_flags &= ~(B_READ | B_ERROR);
+ s = splbio();
+ reassignbuf(bp, bp->b_vp);
+ splx(s);
+ }
+ brelse(bp);
+ return (0);
+}
+
+/*
+ * XXX
+ * This routine flushes buffers out of the B_LOCKED queue when LFS has too
+ * many locked down. Eventually the pageout daemon will simply call LFS
+ * when pages need to be reclaimed. Note, we have one static count of locked
+ * buffers, so we can't have more than a single file system. To make this
+ * work for multiple file systems, put the count into the mount structure.
+ */
+void
+lfs_flush()
+{
+ register struct mount *mp;
+
+#ifdef DOSTATS
+ ++lfs_stats.write_exceeded;
+#endif
+ if (lfs_writing)
+ return;
+ lfs_writing = 1;
+ for (mp = mountlist.tqh_first; mp != NULL; mp = mp->mnt_list.tqe_next) {
+ /* The lock check below is to avoid races with unmount. */
+ if (mp->mnt_stat.f_type == MOUNT_LFS &&
+ (mp->mnt_flag & (MNT_MLOCK|MNT_RDONLY|MNT_UNMOUNT)) == 0 &&
+ !((((struct ufsmount *)mp->mnt_data))->ufsmount_u.lfs)->lfs_dirops ) {
+ /*
+ * We set the queue to 0 here because we are about to
+ * write all the dirty buffers we have. If more come
+ * in while we're writing the segment, they may not
+ * get written, so we want the count to reflect these
+ * new writes after the segwrite completes.
+ */
+#ifdef DOSTATS
+ ++lfs_stats.flush_invoked;
+#endif
+ lfs_segwrite(mp, 0);
+ }
+ }
+ lfs_writing = 0;
+}
+
+int
+lfs_check(vp, blkno)
+ struct vnode *vp;
+ daddr_t blkno;
+{
+ extern int lfs_allclean_wakeup;
+ int error;
+
+ error = 0;
+ if (incore(vp, blkno))
+ return (0);
+ if (locked_queue_count > WRITE_THRESHHOLD)
+ lfs_flush();
+
+ /* If out of buffers, wait on writer */
+ while (locked_queue_count > WAIT_THRESHHOLD) {
+#ifdef DOSTATS
+ ++lfs_stats.wait_exceeded;
+#endif
+ error = tsleep(&locked_queue_count, PCATCH | PUSER, "buffers",
+ hz * LFS_BUFWAIT);
+ }
+
+ return (error);
+}
diff --git a/sys/ufs/lfs/lfs_cksum.c b/sys/ufs/lfs/lfs_cksum.c
new file mode 100644
index 0000000..77b011a
--- /dev/null
+++ b/sys/ufs/lfs/lfs_cksum.c
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lfs_cksum.c 8.1 (Berkeley) 6/11/93
+ */
+
+#include <sys/types.h>
+
+/*
+ * Simple, general purpose, fast checksum. Data must be short-aligned.
+ * Returns a u_long in case we ever want to do something more rigorous.
+ *
+ * XXX
+ * Use the TCP/IP checksum instead.
+ */
+u_long
+cksum(str, len)
+ register void *str;
+ register size_t len;
+{
+ register u_long sum;
+
+ len &= ~(sizeof(u_short) - 1);
+ for (sum = 0; len; len -= sizeof(u_short)) {
+ sum ^= *(u_short *)str;
+ ++(u_short *)str;
+ }
+ return (sum);
+}
diff --git a/sys/ufs/lfs/lfs_debug.c b/sys/ufs/lfs/lfs_debug.c
new file mode 100644
index 0000000..cc28d60
--- /dev/null
+++ b/sys/ufs/lfs/lfs_debug.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lfs_debug.c 8.1 (Berkeley) 6/11/93
+ */
+
+#ifdef DEBUG
+#include <sys/param.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+void
+lfs_dump_super(lfsp)
+ struct lfs *lfsp;
+{
+ int i;
+
+ (void)printf("%s%lx\t%s%lx\t%s%d\t%s%d\n",
+ "magic ", lfsp->lfs_magic,
+ "version ", lfsp->lfs_version,
+ "size ", lfsp->lfs_size,
+ "ssize ", lfsp->lfs_ssize);
+ (void)printf("%s%d\t%s%d\t%s%d\t%s%d\n",
+ "dsize ", lfsp->lfs_dsize,
+ "bsize ", lfsp->lfs_bsize,
+ "fsize ", lfsp->lfs_fsize,
+ "frag ", lfsp->lfs_frag);
+
+ (void)printf("%s%d\t%s%d\t%s%d\t%s%d\n",
+ "minfree ", lfsp->lfs_minfree,
+ "inopb ", lfsp->lfs_inopb,
+ "ifpb ", lfsp->lfs_ifpb,
+ "nindir ", lfsp->lfs_nindir);
+
+ (void)printf("%s%d\t%s%d\t%s%d\t%s%d\n",
+ "nseg ", lfsp->lfs_nseg,
+ "nspf ", lfsp->lfs_nspf,
+ "cleansz ", lfsp->lfs_cleansz,
+ "segtabsz ", lfsp->lfs_segtabsz);
+
+ (void)printf("%s%lx\t%s%d\t%s%lx\t%s%d\n",
+ "segmask ", lfsp->lfs_segmask,
+ "segshift ", lfsp->lfs_segshift,
+ "bmask ", lfsp->lfs_bmask,
+ "bshift ", lfsp->lfs_bshift);
+
+ (void)printf("%s%lx\t%s%d\t%s%lx\t%s%d\n",
+ "ffmask ", lfsp->lfs_ffmask,
+ "ffshift ", lfsp->lfs_ffshift,
+ "fbmask ", lfsp->lfs_fbmask,
+ "fbshift ", lfsp->lfs_fbshift);
+
+ (void)printf("%s%d\t%s%d\t%s%lx\t%s%qx\n",
+ "sushift ", lfsp->lfs_sushift,
+ "fsbtodb ", lfsp->lfs_fsbtodb,
+ "cksum ", lfsp->lfs_cksum,
+ "maxfilesize ", lfsp->lfs_maxfilesize);
+
+ (void)printf("Superblock disk addresses:");
+ for (i = 0; i < LFS_MAXNUMSB; i++)
+ (void)printf(" %lx", lfsp->lfs_sboffs[i]);
+ (void)printf("\n");
+
+ (void)printf("Checkpoint Info\n");
+ (void)printf("%s%d\t%s%lx\t%s%d\n",
+ "free ", lfsp->lfs_free,
+ "idaddr ", lfsp->lfs_idaddr,
+ "ifile ", lfsp->lfs_ifile);
+ (void)printf("%s%lx\t%s%d\t%s%lx\t%s%lx\t%s%lx\t%s%lx\n",
+ "bfree ", lfsp->lfs_bfree,
+ "nfiles ", lfsp->lfs_nfiles,
+ "lastseg ", lfsp->lfs_lastseg,
+ "nextseg ", lfsp->lfs_nextseg,
+ "curseg ", lfsp->lfs_curseg,
+ "offset ", lfsp->lfs_offset);
+ (void)printf("tstamp %lx\n", lfsp->lfs_tstamp);
+}
+
+void
+lfs_dump_dinode(dip)
+ struct dinode *dip;
+{
+ int i;
+
+ (void)printf("%s%u\t%s%d\t%s%u\t%s%u\t%s%lu\n",
+ "mode ", dip->di_mode,
+ "nlink ", dip->di_nlink,
+ "uid ", dip->di_uid,
+ "gid ", dip->di_gid,
+ "size ", dip->di_size);
+ (void)printf("inum %ld\n", dip->di_inumber);
+ (void)printf("Direct Addresses\n");
+ for (i = 0; i < NDADDR; i++) {
+ (void)printf("\t%lx", dip->di_db[i]);
+ if ((i % 6) == 5)
+ (void)printf("\n");
+ }
+ for (i = 0; i < NIADDR; i++)
+ (void)printf("\t%lx", dip->di_ib[i]);
+ (void)printf("\n");
+}
+#endif /* DEBUG */
diff --git a/sys/ufs/lfs/lfs_extern.h b/sys/ufs/lfs/lfs_extern.h
new file mode 100644
index 0000000..c1157ad
--- /dev/null
+++ b/sys/ufs/lfs/lfs_extern.h
@@ -0,0 +1,106 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lfs_extern.h 8.2 (Berkeley) 4/16/94
+ */
+
+struct fid;
+struct mount;
+struct nameidata;
+struct proc;
+struct statfs;
+struct timeval;
+struct inode;
+struct uio;
+struct mbuf;
+
+__BEGIN_DECLS
+u_long cksum __P((void *, size_t)); /* XXX */
+int lfs_balloc __P((struct vnode *, u_long, daddr_t, struct buf **));
+int lfs_blkatoff __P((struct vop_blkatoff_args *));
+int lfs_bwrite __P((struct vop_bwrite_args *));
+int lfs_check __P((struct vnode *, daddr_t));
+int lfs_close __P((struct vop_close_args *));
+int lfs_create __P((struct vop_create_args *));
+int lfs_fhtovp __P((struct mount *, struct fid *, struct mbuf *,
+ struct vnode **, int *, struct ucred **));
+int lfs_fsync __P((struct vop_fsync_args *));
+int lfs_getattr __P((struct vop_getattr_args *));
+struct dinode *
+ lfs_ifind __P((struct lfs *, ino_t, struct dinode *));
+int lfs_inactive __P((struct vop_inactive_args *));
+int lfs_init __P((void));
+int lfs_initseg __P((struct lfs *));
+int lfs_link __P((struct vop_link_args *));
+int lfs_makeinode __P((int, struct nameidata *, struct inode **));
+int lfs_mkdir __P((struct vop_mkdir_args *));
+int lfs_mknod __P((struct vop_mknod_args *));
+int lfs_mount __P((struct mount *,
+ char *, caddr_t, struct nameidata *, struct proc *));
+int lfs_mountroot __P((void));
+struct buf *
+ lfs_newbuf __P((struct vnode *, daddr_t, size_t));
+int lfs_read __P((struct vop_read_args *));
+int lfs_remove __P((struct vop_remove_args *));
+int lfs_rmdir __P((struct vop_rmdir_args *));
+int lfs_rename __P((struct vop_rename_args *));
+void lfs_seglock __P((struct lfs *, unsigned long flags));
+void lfs_segunlock __P((struct lfs *));
+int lfs_segwrite __P((struct mount *, int));
+int lfs_statfs __P((struct mount *, struct statfs *, struct proc *));
+int lfs_symlink __P((struct vop_symlink_args *));
+int lfs_sync __P((struct mount *, int, struct ucred *, struct proc *));
+int lfs_truncate __P((struct vop_truncate_args *));
+int lfs_unmount __P((struct mount *, int, struct proc *));
+int lfs_update __P((struct vop_update_args *));
+int lfs_valloc __P((struct vop_valloc_args *));
+int lfs_vcreate __P((struct mount *, ino_t, struct vnode **));
+int lfs_vfree __P((struct vop_vfree_args *));
+int lfs_vflush __P((struct vnode *));
+int lfs_vget __P((struct mount *, ino_t, struct vnode **));
+int lfs_vptofh __P((struct vnode *, struct fid *));
+int lfs_vref __P((struct vnode *));
+void lfs_vunref __P((struct vnode *));
+int lfs_write __P((struct vop_write_args *));
+#ifdef DEBUG
+void lfs_dump_dinode __P((struct dinode *));
+void lfs_dump_super __P((struct lfs *));
+#endif
+__END_DECLS
+extern int (**lfs_vnodeop_p)();
+extern int (**lfs_specop_p)();
+#ifdef FIFO
+extern int (**lfs_fifoop_p)();
+#define LFS_FIFOOPS lfs_fifoop_p
+#else
+#define LFS_FIFOOPS NULL
+#endif
diff --git a/sys/ufs/lfs/lfs_inode.c b/sys/ufs/lfs/lfs_inode.c
new file mode 100644
index 0000000..1a06aa2
--- /dev/null
+++ b/sys/ufs/lfs/lfs_inode.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lfs_inode.c 8.5 (Berkeley) 12/30/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+int
+lfs_init()
+{
+ return (ufs_init());
+}
+
+/* Search a block for a specific dinode. */
+struct dinode *
+lfs_ifind(fs, ino, dip)
+ struct lfs *fs;
+ ino_t ino;
+ register struct dinode *dip;
+{
+ register int cnt;
+ register struct dinode *ldip;
+
+ for (cnt = INOPB(fs), ldip = dip + (cnt - 1); cnt--; --ldip)
+ if (ldip->di_inumber == ino)
+ return (ldip);
+
+ panic("lfs_ifind: dinode %u not found", ino);
+ /* NOTREACHED */
+}
+
+int
+lfs_update(ap)
+ struct vop_update_args /* {
+ struct vnode *a_vp;
+ struct timeval *a_access;
+ struct timeval *a_modify;
+ int a_waitfor;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct inode *ip;
+
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (0);
+ ip = VTOI(vp);
+ if ((ip->i_flag &
+ (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0)
+ return (0);
+ if (ip->i_flag & IN_ACCESS)
+ ip->i_atime.ts_sec = ap->a_access->tv_sec;
+ if (ip->i_flag & IN_UPDATE) {
+ ip->i_mtime.ts_sec = ap->a_modify->tv_sec;
+ (ip)->i_modrev++;
+ }
+ if (ip->i_flag & IN_CHANGE)
+ ip->i_ctime.ts_sec = time.tv_sec;
+ ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE);
+
+ if (!(ip->i_flag & IN_MODIFIED))
+ ++(VFSTOUFS(vp->v_mount)->um_lfs->lfs_uinodes);
+ ip->i_flag |= IN_MODIFIED;
+
+ /* If sync, push back the vnode and any dirty blocks it may have. */
+ return (ap->a_waitfor & LFS_SYNC ? lfs_vflush(vp) : 0);
+}
+
+/* Update segment usage information when removing a block. */
+#define UPDATE_SEGUSE \
+ if (lastseg != -1) { \
+ LFS_SEGENTRY(sup, fs, lastseg, sup_bp); \
+ if ((num << fs->lfs_bshift) > sup->su_nbytes) \
+ panic("lfs_truncate: negative bytes in segment %d\n", \
+ lastseg); \
+ sup->su_nbytes -= num << fs->lfs_bshift; \
+ e1 = VOP_BWRITE(sup_bp); \
+ blocksreleased += num; \
+ }
+
+#define SEGDEC { \
+ if (daddr != 0) { \
+ if (lastseg != (seg = datosn(fs, daddr))) { \
+ UPDATE_SEGUSE; \
+ num = 1; \
+ lastseg = seg; \
+ } else \
+ ++num; \
+ } \
+}
+
+/*
+ * Truncate the inode ip to at most length size. Update segment usage
+ * table information.
+ */
+/* ARGSUSED */
+int
+lfs_truncate(ap)
+ struct vop_truncate_args /* {
+ struct vnode *a_vp;
+ off_t a_length;
+ int a_flags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct indir *inp;
+ register int i;
+ register daddr_t *daddrp;
+ register struct vnode *vp = ap->a_vp;
+ off_t length = ap->a_length;
+ struct buf *bp, *sup_bp;
+ struct timeval tv;
+ struct ifile *ifp;
+ struct inode *ip;
+ struct lfs *fs;
+ struct indir a[NIADDR + 2], a_end[NIADDR + 2];
+ SEGUSE *sup;
+ daddr_t daddr, lastblock, lbn, olastblock;
+ long off, a_released, blocksreleased, i_released;
+ int e1, e2, depth, lastseg, num, offset, seg, size;
+
+ ip = VTOI(vp);
+ tv = time;
+ if (vp->v_type == VLNK && vp->v_mount->mnt_maxsymlinklen > 0) {
+#ifdef DIAGNOSTIC
+ if (length != 0)
+ panic("lfs_truncate: partial truncate of symlink");
+#endif
+ bzero((char *)&ip->i_shortlink, (u_int)ip->i_size);
+ ip->i_size = 0;
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (VOP_UPDATE(vp, &tv, &tv, 0));
+ }
+ vnode_pager_setsize(vp, (u_long)length);
+
+ fs = ip->i_lfs;
+
+ /* If length is larger than the file, just update the times. */
+ if (ip->i_size <= length) {
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (VOP_UPDATE(vp, &tv, &tv, 0));
+ }
+
+ /*
+ * Calculate index into inode's block list of last direct and indirect
+ * blocks (if any) which we want to keep. Lastblock is 0 when the
+ * file is truncated to 0.
+ */
+ lastblock = lblkno(fs, length + fs->lfs_bsize - 1);
+ olastblock = lblkno(fs, ip->i_size + fs->lfs_bsize - 1) - 1;
+
+ /*
+ * Update the size of the file. If the file is not being truncated to
+ * a block boundry, the contents of the partial block following the end
+ * of the file must be zero'ed in case it ever become accessable again
+ * because of subsequent file growth.
+ */
+ offset = blkoff(fs, length);
+ if (offset == 0)
+ ip->i_size = length;
+ else {
+ lbn = lblkno(fs, length);
+#ifdef QUOTA
+ if (e1 = getinoquota(ip))
+ return (e1);
+#endif
+ if (e1 = bread(vp, lbn, fs->lfs_bsize, NOCRED, &bp))
+ return (e1);
+ ip->i_size = length;
+ size = blksize(fs);
+ (void)vnode_pager_uncache(vp);
+ bzero((char *)bp->b_data + offset, (u_int)(size - offset));
+ allocbuf(bp, size);
+ if (e1 = VOP_BWRITE(bp))
+ return (e1);
+ }
+ /*
+ * Modify sup->su_nbyte counters for each deleted block; keep track
+ * of number of blocks removed for ip->i_blocks.
+ */
+ blocksreleased = 0;
+ num = 0;
+ lastseg = -1;
+
+ for (lbn = olastblock; lbn >= lastblock;) {
+ /* XXX use run length from bmap array to make this faster */
+ ufs_bmaparray(vp, lbn, &daddr, a, &depth, NULL);
+ if (lbn == olastblock)
+ for (i = NIADDR + 2; i--;)
+ a_end[i] = a[i];
+ switch (depth) {
+ case 0: /* Direct block. */
+ daddr = ip->i_db[lbn];
+ SEGDEC;
+ ip->i_db[lbn] = 0;
+ --lbn;
+ break;
+#ifdef DIAGNOSTIC
+ case 1: /* An indirect block. */
+ panic("lfs_truncate: ufs_bmaparray returned depth 1");
+ /* NOTREACHED */
+#endif
+ default: /* Chain of indirect blocks. */
+ inp = a + --depth;
+ if (inp->in_off > 0 && lbn != lastblock) {
+ lbn -= inp->in_off < lbn - lastblock ?
+ inp->in_off : lbn - lastblock;
+ break;
+ }
+ for (; depth && (inp->in_off == 0 || lbn == lastblock);
+ --inp, --depth) {
+ if (bread(vp,
+ inp->in_lbn, fs->lfs_bsize, NOCRED, &bp))
+ panic("lfs_truncate: bread bno %d",
+ inp->in_lbn);
+ daddrp = (daddr_t *)bp->b_data + inp->in_off;
+ for (i = inp->in_off;
+ i++ <= a_end[depth].in_off;) {
+ daddr = *daddrp++;
+ SEGDEC;
+ }
+ a_end[depth].in_off = NINDIR(fs) - 1;
+ if (inp->in_off == 0)
+ brelse (bp);
+ else {
+ bzero((daddr_t *)bp->b_data +
+ inp->in_off, fs->lfs_bsize -
+ inp->in_off * sizeof(daddr_t));
+ if (e1 = VOP_BWRITE(bp))
+ return (e1);
+ }
+ }
+ if (depth == 0 && a[1].in_off == 0) {
+ off = a[0].in_off;
+ daddr = ip->i_ib[off];
+ SEGDEC;
+ ip->i_ib[off] = 0;
+ }
+ if (lbn == lastblock || lbn <= NDADDR)
+ --lbn;
+ else {
+ lbn -= NINDIR(fs);
+ if (lbn < lastblock)
+ lbn = lastblock;
+ }
+ }
+ }
+ UPDATE_SEGUSE;
+
+ /* If truncating the file to 0, update the version number. */
+ if (length == 0) {
+ LFS_IENTRY(ifp, fs, ip->i_number, bp);
+ ++ifp->if_version;
+ (void) VOP_BWRITE(bp);
+ }
+
+#ifdef DIAGNOSTIC
+ if (ip->i_blocks < fsbtodb(fs, blocksreleased)) {
+ printf("lfs_truncate: block count < 0\n");
+ blocksreleased = ip->i_blocks;
+ }
+#endif
+ ip->i_blocks -= fsbtodb(fs, blocksreleased);
+ fs->lfs_bfree += fsbtodb(fs, blocksreleased);
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ /*
+ * Traverse dirty block list counting number of dirty buffers
+ * that are being deleted out of the cache, so that the lfs_avail
+ * field can be updated.
+ */
+ a_released = 0;
+ i_released = 0;
+ for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = bp->b_vnbufs.le_next)
+ if (bp->b_flags & B_LOCKED) {
+ ++a_released;
+ /*
+ * XXX
+ * When buffers are created in the cache, their block
+ * number is set equal to their logical block number.
+ * If that is still true, we are assuming that the
+ * blocks are new (not yet on disk) and weren't
+ * counted above. However, there is a slight chance
+ * that a block's disk address is equal to its logical
+ * block number in which case, we'll get an overcounting
+ * here.
+ */
+ if (bp->b_blkno == bp->b_lblkno)
+ ++i_released;
+ }
+ blocksreleased = fsbtodb(fs, i_released);
+#ifdef DIAGNOSTIC
+ if (blocksreleased > ip->i_blocks) {
+ printf("lfs_inode: Warning! %s\n",
+ "more blocks released from inode than are in inode");
+ blocksreleased = ip->i_blocks;
+ }
+#endif
+ fs->lfs_bfree += blocksreleased;
+ ip->i_blocks -= blocksreleased;
+#ifdef DIAGNOSTIC
+ if (length == 0 && ip->i_blocks != 0)
+ printf("lfs_inode: Warning! %s%d%s\n",
+ "Truncation to zero, but ", ip->i_blocks,
+ " blocks left on inode");
+#endif
+ fs->lfs_avail += fsbtodb(fs, a_released);
+ e1 = vinvalbuf(vp, (length > 0) ? V_SAVE : 0, ap->a_cred, ap->a_p,
+ 0, 0);
+ e2 = VOP_UPDATE(vp, &tv, &tv, 0);
+ return (e1 ? e1 : e2 ? e2 : 0);
+}
diff --git a/sys/ufs/lfs/lfs_segment.c b/sys/ufs/lfs/lfs_segment.c
new file mode 100644
index 0000000..65cab73
--- /dev/null
+++ b/sys/ufs/lfs/lfs_segment.c
@@ -0,0 +1,1112 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lfs_segment.c 8.5 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/kernel.h>
+#include <sys/resourcevar.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+
+#include <miscfs/specfs/specdev.h>
+#include <miscfs/fifofs/fifo.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+extern int count_lock_queue __P((void));
+
+#define MAX_ACTIVE 10
+/*
+ * Determine if it's OK to start a partial in this segment, or if we need
+ * to go on to a new segment.
+ */
+#define LFS_PARTIAL_FITS(fs) \
+ ((fs)->lfs_dbpseg - ((fs)->lfs_offset - (fs)->lfs_curseg) > \
+ 1 << (fs)->lfs_fsbtodb)
+
+void lfs_callback __P((struct buf *));
+void lfs_gather __P((struct lfs *, struct segment *,
+ struct vnode *, int (*) __P((struct lfs *, struct buf *))));
+int lfs_gatherblock __P((struct segment *, struct buf *, int *));
+void lfs_iset __P((struct inode *, daddr_t, time_t));
+int lfs_match_data __P((struct lfs *, struct buf *));
+int lfs_match_dindir __P((struct lfs *, struct buf *));
+int lfs_match_indir __P((struct lfs *, struct buf *));
+int lfs_match_tindir __P((struct lfs *, struct buf *));
+void lfs_newseg __P((struct lfs *));
+void lfs_shellsort __P((struct buf **, daddr_t *, register int));
+void lfs_supercallback __P((struct buf *));
+void lfs_updatemeta __P((struct segment *));
+int lfs_vref __P((struct vnode *));
+void lfs_vunref __P((struct vnode *));
+void lfs_writefile __P((struct lfs *, struct segment *, struct vnode *));
+int lfs_writeinode __P((struct lfs *, struct segment *, struct inode *));
+int lfs_writeseg __P((struct lfs *, struct segment *));
+void lfs_writesuper __P((struct lfs *));
+void lfs_writevnodes __P((struct lfs *fs, struct mount *mp,
+ struct segment *sp, int dirops));
+
+int lfs_allclean_wakeup; /* Cleaner wakeup address. */
+
+/* Statistics Counters */
+#define DOSTATS
+struct lfs_stats lfs_stats;
+
+/* op values to lfs_writevnodes */
+#define VN_REG 0
+#define VN_DIROP 1
+#define VN_EMPTY 2
+
+/*
+ * Ifile and meta data blocks are not marked busy, so segment writes MUST be
+ * single threaded. Currently, there are two paths into lfs_segwrite, sync()
+ * and getnewbuf(). They both mark the file system busy. Lfs_vflush()
+ * explicitly marks the file system busy. So lfs_segwrite is safe. I think.
+ */
+
+int
+lfs_vflush(vp)
+ struct vnode *vp;
+{
+ struct inode *ip;
+ struct lfs *fs;
+ struct segment *sp;
+
+ fs = VFSTOUFS(vp->v_mount)->um_lfs;
+ if (fs->lfs_nactive > MAX_ACTIVE)
+ return(lfs_segwrite(vp->v_mount, SEGM_SYNC|SEGM_CKP));
+ lfs_seglock(fs, SEGM_SYNC);
+ sp = fs->lfs_sp;
+
+
+ ip = VTOI(vp);
+ if (vp->v_dirtyblkhd.lh_first == NULL)
+ lfs_writevnodes(fs, vp->v_mount, sp, VN_EMPTY);
+
+ do {
+ do {
+ if (vp->v_dirtyblkhd.lh_first != NULL)
+ lfs_writefile(fs, sp, vp);
+ } while (lfs_writeinode(fs, sp, ip));
+
+ } while (lfs_writeseg(fs, sp) && ip->i_number == LFS_IFILE_INUM);
+
+#ifdef DOSTATS
+ ++lfs_stats.nwrites;
+ if (sp->seg_flags & SEGM_SYNC)
+ ++lfs_stats.nsync_writes;
+ if (sp->seg_flags & SEGM_CKP)
+ ++lfs_stats.ncheckpoints;
+#endif
+ lfs_segunlock(fs);
+ return (0);
+}
+
+void
+lfs_writevnodes(fs, mp, sp, op)
+ struct lfs *fs;
+ struct mount *mp;
+ struct segment *sp;
+ int op;
+{
+ struct inode *ip;
+ struct vnode *vp;
+
+loop:
+ for (vp = mp->mnt_vnodelist.lh_first;
+ vp != NULL;
+ vp = vp->v_mntvnodes.le_next) {
+ /*
+ * If the vnode that we are about to sync is no longer
+ * associated with this mount point, start over.
+ */
+ if (vp->v_mount != mp)
+ goto loop;
+
+ /* XXX ignore dirops for now
+ if (op == VN_DIROP && !(vp->v_flag & VDIROP) ||
+ op != VN_DIROP && (vp->v_flag & VDIROP))
+ continue;
+ */
+
+ if (op == VN_EMPTY && vp->v_dirtyblkhd.lh_first)
+ continue;
+
+ if (vp->v_type == VNON)
+ continue;
+
+ if (lfs_vref(vp))
+ continue;
+
+ /*
+ * Write the inode/file if dirty and it's not the
+ * the IFILE.
+ */
+ ip = VTOI(vp);
+ if ((ip->i_flag &
+ (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE) ||
+ vp->v_dirtyblkhd.lh_first != NULL) &&
+ ip->i_number != LFS_IFILE_INUM) {
+ if (vp->v_dirtyblkhd.lh_first != NULL)
+ lfs_writefile(fs, sp, vp);
+ (void) lfs_writeinode(fs, sp, ip);
+ }
+ vp->v_flag &= ~VDIROP;
+ lfs_vunref(vp);
+ }
+}
+
+int
+lfs_segwrite(mp, flags)
+ struct mount *mp;
+ int flags; /* Do a checkpoint. */
+{
+ struct buf *bp;
+ struct inode *ip;
+ struct lfs *fs;
+ struct segment *sp;
+ struct vnode *vp;
+ SEGUSE *segusep;
+ daddr_t ibno;
+ CLEANERINFO *cip;
+ int clean, do_ckp, error, i;
+
+ fs = VFSTOUFS(mp)->um_lfs;
+
+ /*
+ * If we have fewer than 2 clean segments, wait until cleaner
+ * writes.
+ */
+ do {
+ LFS_CLEANERINFO(cip, fs, bp);
+ clean = cip->clean;
+ brelse(bp);
+ if (clean <= 2) {
+ printf ("segs clean: %d\n", clean);
+ wakeup(&lfs_allclean_wakeup);
+ if (error = tsleep(&fs->lfs_avail, PRIBIO + 1,
+ "lfs writer", 0))
+ return (error);
+ }
+ } while (clean <= 2 );
+
+ /*
+ * Allocate a segment structure and enough space to hold pointers to
+ * the maximum possible number of buffers which can be described in a
+ * single summary block.
+ */
+ do_ckp = flags & SEGM_CKP || fs->lfs_nactive > MAX_ACTIVE;
+ lfs_seglock(fs, flags | (do_ckp ? SEGM_CKP : 0));
+ sp = fs->lfs_sp;
+
+ lfs_writevnodes(fs, mp, sp, VN_REG);
+
+ /* XXX ignore ordering of dirops for now */
+ /* XXX
+ fs->lfs_writer = 1;
+ if (fs->lfs_dirops && (error =
+ tsleep(&fs->lfs_writer, PRIBIO + 1, "lfs writer", 0))) {
+ free(sp->bpp, M_SEGMENT);
+ free(sp, M_SEGMENT);
+ fs->lfs_writer = 0;
+ return (error);
+ }
+
+ lfs_writevnodes(fs, mp, sp, VN_DIROP);
+ */
+
+ /*
+ * If we are doing a checkpoint, mark everything since the
+ * last checkpoint as no longer ACTIVE.
+ */
+ if (do_ckp)
+ for (ibno = fs->lfs_cleansz + fs->lfs_segtabsz;
+ --ibno >= fs->lfs_cleansz; ) {
+ if (bread(fs->lfs_ivnode, ibno, fs->lfs_bsize,
+ NOCRED, &bp))
+
+ panic("lfs: ifile read");
+ segusep = (SEGUSE *)bp->b_data;
+ for (i = fs->lfs_sepb; i--; segusep++)
+ segusep->su_flags &= ~SEGUSE_ACTIVE;
+
+ error = VOP_BWRITE(bp);
+ }
+
+ if (do_ckp || fs->lfs_doifile) {
+redo:
+ vp = fs->lfs_ivnode;
+ while (vget(vp, 1));
+ ip = VTOI(vp);
+ if (vp->v_dirtyblkhd.lh_first != NULL)
+ lfs_writefile(fs, sp, vp);
+ (void)lfs_writeinode(fs, sp, ip);
+ vput(vp);
+ if (lfs_writeseg(fs, sp) && do_ckp)
+ goto redo;
+ } else
+ (void) lfs_writeseg(fs, sp);
+
+ /*
+ * If the I/O count is non-zero, sleep until it reaches zero. At the
+ * moment, the user's process hangs around so we can sleep.
+ */
+ /* XXX ignore dirops for now
+ fs->lfs_writer = 0;
+ fs->lfs_doifile = 0;
+ wakeup(&fs->lfs_dirops);
+ */
+
+#ifdef DOSTATS
+ ++lfs_stats.nwrites;
+ if (sp->seg_flags & SEGM_SYNC)
+ ++lfs_stats.nsync_writes;
+ if (sp->seg_flags & SEGM_CKP)
+ ++lfs_stats.ncheckpoints;
+#endif
+ lfs_segunlock(fs);
+ return (0);
+}
+
+/*
+ * Write the dirty blocks associated with a vnode.
+ */
+void
+lfs_writefile(fs, sp, vp)
+ struct lfs *fs;
+ struct segment *sp;
+ struct vnode *vp;
+{
+ struct buf *bp;
+ struct finfo *fip;
+ IFILE *ifp;
+
+ if (sp->seg_bytes_left < fs->lfs_bsize ||
+ sp->sum_bytes_left < sizeof(struct finfo))
+ (void) lfs_writeseg(fs, sp);
+
+ sp->sum_bytes_left -= sizeof(struct finfo) - sizeof(daddr_t);
+ ++((SEGSUM *)(sp->segsum))->ss_nfinfo;
+
+ fip = sp->fip;
+ fip->fi_nblocks = 0;
+ fip->fi_ino = VTOI(vp)->i_number;
+ LFS_IENTRY(ifp, fs, fip->fi_ino, bp);
+ fip->fi_version = ifp->if_version;
+ brelse(bp);
+
+ /*
+ * It may not be necessary to write the meta-data blocks at this point,
+ * as the roll-forward recovery code should be able to reconstruct the
+ * list.
+ */
+ lfs_gather(fs, sp, vp, lfs_match_data);
+ lfs_gather(fs, sp, vp, lfs_match_indir);
+ lfs_gather(fs, sp, vp, lfs_match_dindir);
+#ifdef TRIPLE
+ lfs_gather(fs, sp, vp, lfs_match_tindir);
+#endif
+
+ fip = sp->fip;
+ if (fip->fi_nblocks != 0) {
+ sp->fip =
+ (struct finfo *)((caddr_t)fip + sizeof(struct finfo) +
+ sizeof(daddr_t) * (fip->fi_nblocks - 1));
+ sp->start_lbp = &sp->fip->fi_blocks[0];
+ } else {
+ sp->sum_bytes_left += sizeof(struct finfo) - sizeof(daddr_t);
+ --((SEGSUM *)(sp->segsum))->ss_nfinfo;
+ }
+}
+
+int
+lfs_writeinode(fs, sp, ip)
+ struct lfs *fs;
+ struct segment *sp;
+ struct inode *ip;
+{
+ struct buf *bp, *ibp;
+ IFILE *ifp;
+ SEGUSE *sup;
+ daddr_t daddr;
+ ino_t ino;
+ int error, i, ndx;
+ int redo_ifile = 0;
+
+ if (!(ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)))
+ return(0);
+
+ /* Allocate a new inode block if necessary. */
+ if (sp->ibp == NULL) {
+ /* Allocate a new segment if necessary. */
+ if (sp->seg_bytes_left < fs->lfs_bsize ||
+ sp->sum_bytes_left < sizeof(daddr_t))
+ (void) lfs_writeseg(fs, sp);
+
+ /* Get next inode block. */
+ daddr = fs->lfs_offset;
+ fs->lfs_offset += fsbtodb(fs, 1);
+ sp->ibp = *sp->cbpp++ =
+ lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp, daddr,
+ fs->lfs_bsize);
+ /* Zero out inode numbers */
+ for (i = 0; i < INOPB(fs); ++i)
+ ((struct dinode *)sp->ibp->b_data)[i].di_inumber = 0;
+ ++sp->start_bpp;
+ fs->lfs_avail -= fsbtodb(fs, 1);
+ /* Set remaining space counters. */
+ sp->seg_bytes_left -= fs->lfs_bsize;
+ sp->sum_bytes_left -= sizeof(daddr_t);
+ ndx = LFS_SUMMARY_SIZE / sizeof(daddr_t) -
+ sp->ninodes / INOPB(fs) - 1;
+ ((daddr_t *)(sp->segsum))[ndx] = daddr;
+ }
+
+ /* Update the inode times and copy the inode onto the inode page. */
+ if (ip->i_flag & IN_MODIFIED)
+ --fs->lfs_uinodes;
+ ITIMES(ip, &time, &time);
+ ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
+ bp = sp->ibp;
+ ((struct dinode *)bp->b_data)[sp->ninodes % INOPB(fs)] = ip->i_din;
+ /* Increment inode count in segment summary block. */
+ ++((SEGSUM *)(sp->segsum))->ss_ninos;
+
+ /* If this page is full, set flag to allocate a new page. */
+ if (++sp->ninodes % INOPB(fs) == 0)
+ sp->ibp = NULL;
+
+ /*
+ * If updating the ifile, update the super-block. Update the disk
+ * address and access times for this inode in the ifile.
+ */
+ ino = ip->i_number;
+ if (ino == LFS_IFILE_INUM) {
+ daddr = fs->lfs_idaddr;
+ fs->lfs_idaddr = bp->b_blkno;
+ } else {
+ LFS_IENTRY(ifp, fs, ino, ibp);
+ daddr = ifp->if_daddr;
+ ifp->if_daddr = bp->b_blkno;
+ error = VOP_BWRITE(ibp);
+ }
+
+ /*
+ * No need to update segment usage if there was no former inode address
+ * or if the last inode address is in the current partial segment.
+ */
+ if (daddr != LFS_UNUSED_DADDR &&
+ !(daddr >= fs->lfs_lastpseg && daddr <= bp->b_blkno)) {
+ LFS_SEGENTRY(sup, fs, datosn(fs, daddr), bp);
+#ifdef DIAGNOSTIC
+ if (sup->su_nbytes < sizeof(struct dinode)) {
+ /* XXX -- Change to a panic. */
+ printf("lfs: negative bytes (segment %d)\n",
+ datosn(fs, daddr));
+ panic("negative bytes");
+ }
+#endif
+ sup->su_nbytes -= sizeof(struct dinode);
+ redo_ifile =
+ (ino == LFS_IFILE_INUM && !(bp->b_flags & B_GATHERED));
+ error = VOP_BWRITE(bp);
+ }
+ return (redo_ifile);
+}
+
+int
+lfs_gatherblock(sp, bp, sptr)
+ struct segment *sp;
+ struct buf *bp;
+ int *sptr;
+{
+ struct lfs *fs;
+ int version;
+
+ /*
+ * If full, finish this segment. We may be doing I/O, so
+ * release and reacquire the splbio().
+ */
+#ifdef DIAGNOSTIC
+ if (sp->vp == NULL)
+ panic ("lfs_gatherblock: Null vp in segment");
+#endif
+ fs = sp->fs;
+ if (sp->sum_bytes_left < sizeof(daddr_t) ||
+ sp->seg_bytes_left < fs->lfs_bsize) {
+ if (sptr)
+ splx(*sptr);
+ lfs_updatemeta(sp);
+
+ version = sp->fip->fi_version;
+ (void) lfs_writeseg(fs, sp);
+
+ sp->fip->fi_version = version;
+ sp->fip->fi_ino = VTOI(sp->vp)->i_number;
+ /* Add the current file to the segment summary. */
+ ++((SEGSUM *)(sp->segsum))->ss_nfinfo;
+ sp->sum_bytes_left -=
+ sizeof(struct finfo) - sizeof(daddr_t);
+
+ if (sptr)
+ *sptr = splbio();
+ return(1);
+ }
+
+ /* Insert into the buffer list, update the FINFO block. */
+ bp->b_flags |= B_GATHERED;
+ *sp->cbpp++ = bp;
+ sp->fip->fi_blocks[sp->fip->fi_nblocks++] = bp->b_lblkno;
+
+ sp->sum_bytes_left -= sizeof(daddr_t);
+ sp->seg_bytes_left -= fs->lfs_bsize;
+ return(0);
+}
+
+void
+lfs_gather(fs, sp, vp, match)
+ struct lfs *fs;
+ struct segment *sp;
+ struct vnode *vp;
+ int (*match) __P((struct lfs *, struct buf *));
+{
+ struct buf *bp;
+ int s;
+
+ sp->vp = vp;
+ s = splbio();
+loop: for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = bp->b_vnbufs.le_next) {
+ if (bp->b_flags & B_BUSY || !match(fs, bp) ||
+ bp->b_flags & B_GATHERED)
+ continue;
+#ifdef DIAGNOSTIC
+ if (!(bp->b_flags & B_DELWRI))
+ panic("lfs_gather: bp not B_DELWRI");
+ if (!(bp->b_flags & B_LOCKED))
+ panic("lfs_gather: bp not B_LOCKED");
+#endif
+ if (lfs_gatherblock(sp, bp, &s))
+ goto loop;
+ }
+ splx(s);
+ lfs_updatemeta(sp);
+ sp->vp = NULL;
+}
+
+
+/*
+ * Update the metadata that points to the blocks listed in the FINFO
+ * array.
+ */
+void
+lfs_updatemeta(sp)
+ struct segment *sp;
+{
+ SEGUSE *sup;
+ struct buf *bp;
+ struct lfs *fs;
+ struct vnode *vp;
+ struct indir a[NIADDR + 2], *ap;
+ struct inode *ip;
+ daddr_t daddr, lbn, off;
+ int db_per_fsb, error, i, nblocks, num;
+
+ vp = sp->vp;
+ nblocks = &sp->fip->fi_blocks[sp->fip->fi_nblocks] - sp->start_lbp;
+ if (vp == NULL || nblocks == 0)
+ return;
+
+ /* Sort the blocks. */
+ if (!(sp->seg_flags & SEGM_CLEAN))
+ lfs_shellsort(sp->start_bpp, sp->start_lbp, nblocks);
+
+ /*
+ * Assign disk addresses, and update references to the logical
+ * block and the segment usage information.
+ */
+ fs = sp->fs;
+ db_per_fsb = fsbtodb(fs, 1);
+ for (i = nblocks; i--; ++sp->start_bpp) {
+ lbn = *sp->start_lbp++;
+ (*sp->start_bpp)->b_blkno = off = fs->lfs_offset;
+ fs->lfs_offset += db_per_fsb;
+
+ if (error = ufs_bmaparray(vp, lbn, &daddr, a, &num, NULL))
+ panic("lfs_updatemeta: ufs_bmaparray %d", error);
+ ip = VTOI(vp);
+ switch (num) {
+ case 0:
+ ip->i_db[lbn] = off;
+ break;
+ case 1:
+ ip->i_ib[a[0].in_off] = off;
+ break;
+ default:
+ ap = &a[num - 1];
+ if (bread(vp, ap->in_lbn, fs->lfs_bsize, NOCRED, &bp))
+ panic("lfs_updatemeta: bread bno %d",
+ ap->in_lbn);
+ /*
+ * Bread may create a new indirect block which needs
+ * to get counted for the inode.
+ */
+ if (bp->b_blkno == -1 && !(bp->b_flags & B_CACHE)) {
+printf ("Updatemeta allocating indirect block: shouldn't happen\n");
+ ip->i_blocks += btodb(fs->lfs_bsize);
+ fs->lfs_bfree -= btodb(fs->lfs_bsize);
+ }
+ ((daddr_t *)bp->b_data)[ap->in_off] = off;
+ VOP_BWRITE(bp);
+ }
+
+ /* Update segment usage information. */
+ if (daddr != UNASSIGNED &&
+ !(daddr >= fs->lfs_lastpseg && daddr <= off)) {
+ LFS_SEGENTRY(sup, fs, datosn(fs, daddr), bp);
+#ifdef DIAGNOSTIC
+ if (sup->su_nbytes < fs->lfs_bsize) {
+ /* XXX -- Change to a panic. */
+ printf("lfs: negative bytes (segment %d)\n",
+ datosn(fs, daddr));
+ panic ("Negative Bytes");
+ }
+#endif
+ sup->su_nbytes -= fs->lfs_bsize;
+ error = VOP_BWRITE(bp);
+ }
+ }
+}
+
+/*
+ * Start a new segment.
+ */
+int
+lfs_initseg(fs)
+ struct lfs *fs;
+{
+ struct segment *sp;
+ SEGUSE *sup;
+ SEGSUM *ssp;
+ struct buf *bp;
+ int repeat;
+
+ sp = fs->lfs_sp;
+
+ repeat = 0;
+ /* Advance to the next segment. */
+ if (!LFS_PARTIAL_FITS(fs)) {
+ /* Wake up any cleaning procs waiting on this file system. */
+ wakeup(&lfs_allclean_wakeup);
+
+ lfs_newseg(fs);
+ repeat = 1;
+ fs->lfs_offset = fs->lfs_curseg;
+ sp->seg_number = datosn(fs, fs->lfs_curseg);
+ sp->seg_bytes_left = fs->lfs_dbpseg * DEV_BSIZE;
+
+ /*
+ * If the segment contains a superblock, update the offset
+ * and summary address to skip over it.
+ */
+ LFS_SEGENTRY(sup, fs, sp->seg_number, bp);
+ if (sup->su_flags & SEGUSE_SUPERBLOCK) {
+ fs->lfs_offset += LFS_SBPAD / DEV_BSIZE;
+ sp->seg_bytes_left -= LFS_SBPAD;
+ }
+ brelse(bp);
+ } else {
+ sp->seg_number = datosn(fs, fs->lfs_curseg);
+ sp->seg_bytes_left = (fs->lfs_dbpseg -
+ (fs->lfs_offset - fs->lfs_curseg)) * DEV_BSIZE;
+ }
+ fs->lfs_lastpseg = fs->lfs_offset;
+
+ sp->fs = fs;
+ sp->ibp = NULL;
+ sp->ninodes = 0;
+
+ /* Get a new buffer for SEGSUM and enter it into the buffer list. */
+ sp->cbpp = sp->bpp;
+ *sp->cbpp = lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp, fs->lfs_offset,
+ LFS_SUMMARY_SIZE);
+ sp->segsum = (*sp->cbpp)->b_data;
+ bzero(sp->segsum, LFS_SUMMARY_SIZE);
+ sp->start_bpp = ++sp->cbpp;
+ fs->lfs_offset += LFS_SUMMARY_SIZE / DEV_BSIZE;
+
+ /* Set point to SEGSUM, initialize it. */
+ ssp = sp->segsum;
+ ssp->ss_next = fs->lfs_nextseg;
+ ssp->ss_nfinfo = ssp->ss_ninos = 0;
+
+ /* Set pointer to first FINFO, initialize it. */
+ sp->fip = (struct finfo *)(sp->segsum + sizeof(SEGSUM));
+ sp->fip->fi_nblocks = 0;
+ sp->start_lbp = &sp->fip->fi_blocks[0];
+
+ sp->seg_bytes_left -= LFS_SUMMARY_SIZE;
+ sp->sum_bytes_left = LFS_SUMMARY_SIZE - sizeof(SEGSUM);
+
+ return(repeat);
+}
+
+/*
+ * Return the next segment to write.
+ */
+void
+lfs_newseg(fs)
+ struct lfs *fs;
+{
+ CLEANERINFO *cip;
+ SEGUSE *sup;
+ struct buf *bp;
+ int curseg, isdirty, sn;
+
+ LFS_SEGENTRY(sup, fs, datosn(fs, fs->lfs_nextseg), bp);
+ sup->su_flags |= SEGUSE_DIRTY | SEGUSE_ACTIVE;
+ sup->su_nbytes = 0;
+ sup->su_nsums = 0;
+ sup->su_ninos = 0;
+ (void) VOP_BWRITE(bp);
+
+ LFS_CLEANERINFO(cip, fs, bp);
+ --cip->clean;
+ ++cip->dirty;
+ (void) VOP_BWRITE(bp);
+
+ fs->lfs_lastseg = fs->lfs_curseg;
+ fs->lfs_curseg = fs->lfs_nextseg;
+ for (sn = curseg = datosn(fs, fs->lfs_curseg);;) {
+ sn = (sn + 1) % fs->lfs_nseg;
+ if (sn == curseg)
+ panic("lfs_nextseg: no clean segments");
+ LFS_SEGENTRY(sup, fs, sn, bp);
+ isdirty = sup->su_flags & SEGUSE_DIRTY;
+ brelse(bp);
+ if (!isdirty)
+ break;
+ }
+
+ ++fs->lfs_nactive;
+ fs->lfs_nextseg = sntoda(fs, sn);
+#ifdef DOSTATS
+ ++lfs_stats.segsused;
+#endif
+}
+
+int
+lfs_writeseg(fs, sp)
+ struct lfs *fs;
+ struct segment *sp;
+{
+ extern int locked_queue_count;
+ struct buf **bpp, *bp, *cbp;
+ SEGUSE *sup;
+ SEGSUM *ssp;
+ dev_t i_dev;
+ size_t size;
+ u_long *datap, *dp;
+ int ch_per_blk, do_again, i, nblocks, num, s;
+ int (*strategy)__P((struct vop_strategy_args *));
+ struct vop_strategy_args vop_strategy_a;
+ u_short ninos;
+ char *p;
+
+ /*
+ * If there are no buffers other than the segment summary to write
+ * and it is not a checkpoint, don't do anything. On a checkpoint,
+ * even if there aren't any buffers, you need to write the superblock.
+ */
+ if ((nblocks = sp->cbpp - sp->bpp) == 1)
+ return (0);
+
+ ssp = (SEGSUM *)sp->segsum;
+
+ /* Update the segment usage information. */
+ LFS_SEGENTRY(sup, fs, sp->seg_number, bp);
+ ninos = (ssp->ss_ninos + INOPB(fs) - 1) / INOPB(fs);
+ sup->su_nbytes += nblocks - 1 - ninos << fs->lfs_bshift;
+ sup->su_nbytes += ssp->ss_ninos * sizeof(struct dinode);
+ sup->su_nbytes += LFS_SUMMARY_SIZE;
+ sup->su_lastmod = time.tv_sec;
+ sup->su_ninos += ninos;
+ ++sup->su_nsums;
+ do_again = !(bp->b_flags & B_GATHERED);
+ (void)VOP_BWRITE(bp);
+ /*
+ * Compute checksum across data and then across summary; the first
+ * block (the summary block) is skipped. Set the create time here
+ * so that it's guaranteed to be later than the inode mod times.
+ *
+ * XXX
+ * Fix this to do it inline, instead of malloc/copy.
+ */
+ datap = dp = malloc(nblocks * sizeof(u_long), M_SEGMENT, M_WAITOK);
+ for (bpp = sp->bpp, i = nblocks - 1; i--;) {
+ if ((*++bpp)->b_flags & B_INVAL) {
+ if (copyin((*bpp)->b_saveaddr, dp++, sizeof(u_long)))
+ panic("lfs_writeseg: copyin failed");
+ } else
+ *dp++ = ((u_long *)(*bpp)->b_data)[0];
+ }
+ ssp->ss_create = time.tv_sec;
+ ssp->ss_datasum = cksum(datap, (nblocks - 1) * sizeof(u_long));
+ ssp->ss_sumsum =
+ cksum(&ssp->ss_datasum, LFS_SUMMARY_SIZE - sizeof(ssp->ss_sumsum));
+ free(datap, M_SEGMENT);
+#ifdef DIAGNOSTIC
+ if (fs->lfs_bfree < fsbtodb(fs, ninos) + LFS_SUMMARY_SIZE / DEV_BSIZE)
+ panic("lfs_writeseg: No diskspace for summary");
+#endif
+ fs->lfs_bfree -= (fsbtodb(fs, ninos) + LFS_SUMMARY_SIZE / DEV_BSIZE);
+
+ i_dev = VTOI(fs->lfs_ivnode)->i_dev;
+ strategy = VTOI(fs->lfs_ivnode)->i_devvp->v_op[VOFFSET(vop_strategy)];
+
+ /*
+ * When we simply write the blocks we lose a rotation for every block
+ * written. To avoid this problem, we allocate memory in chunks, copy
+ * the buffers into the chunk and write the chunk. MAXPHYS is the
+ * largest size I/O devices can handle.
+ * When the data is copied to the chunk, turn off the the B_LOCKED bit
+ * and brelse the buffer (which will move them to the LRU list). Add
+ * the B_CALL flag to the buffer header so we can count I/O's for the
+ * checkpoints and so we can release the allocated memory.
+ *
+ * XXX
+ * This should be removed if the new virtual memory system allows us to
+ * easily make the buffers contiguous in kernel memory and if that's
+ * fast enough.
+ */
+ ch_per_blk = MAXPHYS / fs->lfs_bsize;
+ for (bpp = sp->bpp, i = nblocks; i;) {
+ num = ch_per_blk;
+ if (num > i)
+ num = i;
+ i -= num;
+ size = num * fs->lfs_bsize;
+
+ cbp = lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp,
+ (*bpp)->b_blkno, size);
+ cbp->b_dev = i_dev;
+ cbp->b_flags |= B_ASYNC | B_BUSY;
+
+ s = splbio();
+ ++fs->lfs_iocount;
+ for (p = cbp->b_data; num--;) {
+ bp = *bpp++;
+ /*
+ * Fake buffers from the cleaner are marked as B_INVAL.
+ * We need to copy the data from user space rather than
+ * from the buffer indicated.
+ * XXX == what do I do on an error?
+ */
+ if (bp->b_flags & B_INVAL) {
+ if (copyin(bp->b_saveaddr, p, bp->b_bcount))
+ panic("lfs_writeseg: copyin failed");
+ } else
+ bcopy(bp->b_data, p, bp->b_bcount);
+ p += bp->b_bcount;
+ if (bp->b_flags & B_LOCKED)
+ --locked_queue_count;
+ bp->b_flags &= ~(B_ERROR | B_READ | B_DELWRI |
+ B_LOCKED | B_GATHERED);
+ if (bp->b_flags & B_CALL) {
+ /* if B_CALL, it was created with newbuf */
+ brelvp(bp);
+ if (!(bp->b_flags & B_INVAL))
+ free(bp->b_data, M_SEGMENT);
+ free(bp, M_SEGMENT);
+ } else {
+ bremfree(bp);
+ bp->b_flags |= B_DONE;
+ reassignbuf(bp, bp->b_vp);
+ brelse(bp);
+ }
+ }
+ ++cbp->b_vp->v_numoutput;
+ splx(s);
+ cbp->b_bcount = p - (char *)cbp->b_data;
+ /*
+ * XXXX This is a gross and disgusting hack. Since these
+ * buffers are physically addressed, they hang off the
+ * device vnode (devvp). As a result, they have no way
+ * of getting to the LFS superblock or lfs structure to
+ * keep track of the number of I/O's pending. So, I am
+ * going to stuff the fs into the saveaddr field of
+ * the buffer (yuk).
+ */
+ cbp->b_saveaddr = (caddr_t)fs;
+ vop_strategy_a.a_desc = VDESC(vop_strategy);
+ vop_strategy_a.a_bp = cbp;
+ (strategy)(&vop_strategy_a);
+ }
+ /*
+ * XXX
+ * Vinvalbuf can move locked buffers off the locked queue
+ * and we have no way of knowing about this. So, after
+ * doing a big write, we recalculate how many bufers are
+ * really still left on the locked queue.
+ */
+ locked_queue_count = count_lock_queue();
+ wakeup(&locked_queue_count);
+#ifdef DOSTATS
+ ++lfs_stats.psegwrites;
+ lfs_stats.blocktot += nblocks - 1;
+ if (fs->lfs_sp->seg_flags & SEGM_SYNC)
+ ++lfs_stats.psyncwrites;
+ if (fs->lfs_sp->seg_flags & SEGM_CLEAN) {
+ ++lfs_stats.pcleanwrites;
+ lfs_stats.cleanblocks += nblocks - 1;
+ }
+#endif
+ return (lfs_initseg(fs) || do_again);
+}
+
+void
+lfs_writesuper(fs)
+ struct lfs *fs;
+{
+ struct buf *bp;
+ dev_t i_dev;
+ int (*strategy) __P((struct vop_strategy_args *));
+ int s;
+ struct vop_strategy_args vop_strategy_a;
+
+ i_dev = VTOI(fs->lfs_ivnode)->i_dev;
+ strategy = VTOI(fs->lfs_ivnode)->i_devvp->v_op[VOFFSET(vop_strategy)];
+
+ /* Checksum the superblock and copy it into a buffer. */
+ fs->lfs_cksum = cksum(fs, sizeof(struct lfs) - sizeof(fs->lfs_cksum));
+ bp = lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp, fs->lfs_sboffs[0],
+ LFS_SBPAD);
+ *(struct lfs *)bp->b_data = *fs;
+
+ /* XXX Toggle between first two superblocks; for now just write first */
+ bp->b_dev = i_dev;
+ bp->b_flags |= B_BUSY | B_CALL | B_ASYNC;
+ bp->b_flags &= ~(B_DONE | B_ERROR | B_READ | B_DELWRI);
+ bp->b_iodone = lfs_supercallback;
+ vop_strategy_a.a_desc = VDESC(vop_strategy);
+ vop_strategy_a.a_bp = bp;
+ s = splbio();
+ ++bp->b_vp->v_numoutput;
+ splx(s);
+ (strategy)(&vop_strategy_a);
+}
+
+/*
+ * Logical block number match routines used when traversing the dirty block
+ * chain.
+ */
+int
+lfs_match_data(fs, bp)
+ struct lfs *fs;
+ struct buf *bp;
+{
+ return (bp->b_lblkno >= 0);
+}
+
+int
+lfs_match_indir(fs, bp)
+ struct lfs *fs;
+ struct buf *bp;
+{
+ int lbn;
+
+ lbn = bp->b_lblkno;
+ return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 0);
+}
+
+int
+lfs_match_dindir(fs, bp)
+ struct lfs *fs;
+ struct buf *bp;
+{
+ int lbn;
+
+ lbn = bp->b_lblkno;
+ return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 1);
+}
+
+int
+lfs_match_tindir(fs, bp)
+ struct lfs *fs;
+ struct buf *bp;
+{
+ int lbn;
+
+ lbn = bp->b_lblkno;
+ return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 2);
+}
+
+/*
+ * Allocate a new buffer header.
+ */
+struct buf *
+lfs_newbuf(vp, daddr, size)
+ struct vnode *vp;
+ daddr_t daddr;
+ size_t size;
+{
+ struct buf *bp;
+ size_t nbytes;
+
+ nbytes = roundup(size, DEV_BSIZE);
+ bp = malloc(sizeof(struct buf), M_SEGMENT, M_WAITOK);
+ bzero(bp, sizeof(struct buf));
+ if (nbytes)
+ bp->b_data = malloc(nbytes, M_SEGMENT, M_WAITOK);
+ bgetvp(vp, bp);
+ bp->b_bufsize = size;
+ bp->b_bcount = size;
+ bp->b_lblkno = daddr;
+ bp->b_blkno = daddr;
+ bp->b_error = 0;
+ bp->b_resid = 0;
+ bp->b_iodone = lfs_callback;
+ bp->b_flags |= B_BUSY | B_CALL | B_NOCACHE;
+ return (bp);
+}
+
+void
+lfs_callback(bp)
+ struct buf *bp;
+{
+ struct lfs *fs;
+
+ fs = (struct lfs *)bp->b_saveaddr;
+#ifdef DIAGNOSTIC
+ if (fs->lfs_iocount == 0)
+ panic("lfs_callback: zero iocount\n");
+#endif
+ if (--fs->lfs_iocount == 0)
+ wakeup(&fs->lfs_iocount);
+
+ brelvp(bp);
+ free(bp->b_data, M_SEGMENT);
+ free(bp, M_SEGMENT);
+}
+
+void
+lfs_supercallback(bp)
+ struct buf *bp;
+{
+ brelvp(bp);
+ free(bp->b_data, M_SEGMENT);
+ free(bp, M_SEGMENT);
+}
+
+/*
+ * Shellsort (diminishing increment sort) from Data Structures and
+ * Algorithms, Aho, Hopcraft and Ullman, 1983 Edition, page 290;
+ * see also Knuth Vol. 3, page 84. The increments are selected from
+ * formula (8), page 95. Roughly O(N^3/2).
+ */
+/*
+ * This is our own private copy of shellsort because we want to sort
+ * two parallel arrays (the array of buffer pointers and the array of
+ * logical block numbers) simultaneously. Note that we cast the array
+ * of logical block numbers to a unsigned in this routine so that the
+ * negative block numbers (meta data blocks) sort AFTER the data blocks.
+ */
+void
+lfs_shellsort(bp_array, lb_array, nmemb)
+ struct buf **bp_array;
+ daddr_t *lb_array;
+ register int nmemb;
+{
+ static int __rsshell_increments[] = { 4, 1, 0 };
+ register int incr, *incrp, t1, t2;
+ struct buf *bp_temp;
+ u_long lb_temp;
+
+ for (incrp = __rsshell_increments; incr = *incrp++;)
+ for (t1 = incr; t1 < nmemb; ++t1)
+ for (t2 = t1 - incr; t2 >= 0;)
+ if (lb_array[t2] > lb_array[t2 + incr]) {
+ lb_temp = lb_array[t2];
+ lb_array[t2] = lb_array[t2 + incr];
+ lb_array[t2 + incr] = lb_temp;
+ bp_temp = bp_array[t2];
+ bp_array[t2] = bp_array[t2 + incr];
+ bp_array[t2 + incr] = bp_temp;
+ t2 -= incr;
+ } else
+ break;
+}
+
+/*
+ * Check VXLOCK. Return 1 if the vnode is locked. Otherwise, vget it.
+ */
+int
+lfs_vref(vp)
+ register struct vnode *vp;
+{
+
+ if (vp->v_flag & VXLOCK)
+ return(1);
+ return (vget(vp, 0));
+}
+
+void
+lfs_vunref(vp)
+ register struct vnode *vp;
+{
+ extern int lfs_no_inactive;
+
+ /*
+ * This is vrele except that we do not want to VOP_INACTIVE
+ * this vnode. Rather than inline vrele here, we use a global
+ * flag to tell lfs_inactive not to run. Yes, its gross.
+ */
+ lfs_no_inactive = 1;
+ vrele(vp);
+ lfs_no_inactive = 0;
+}
diff --git a/sys/ufs/lfs/lfs_subr.c b/sys/ufs/lfs/lfs_subr.c
new file mode 100644
index 0000000..79e36f4
--- /dev/null
+++ b/sys/ufs/lfs/lfs_subr.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lfs_subr.c 8.2 (Berkeley) 9/21/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/buf.h>
+#include <sys/mount.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+/*
+ * Return buffer with the contents of block "offset" from the beginning of
+ * directory "ip". If "res" is non-zero, fill it in with a pointer to the
+ * remaining space in the directory.
+ */
+int
+lfs_blkatoff(ap)
+ struct vop_blkatoff_args /* {
+ struct vnode *a_vp;
+ off_t a_offset;
+ char **a_res;
+ struct buf **a_bpp;
+ } */ *ap;
+{
+ register struct lfs *fs;
+ struct inode *ip;
+ struct buf *bp;
+ daddr_t lbn;
+ int bsize, error;
+
+ ip = VTOI(ap->a_vp);
+ fs = ip->i_lfs;
+ lbn = lblkno(fs, ap->a_offset);
+ bsize = blksize(fs);
+
+ *ap->a_bpp = NULL;
+ if (error = bread(ap->a_vp, lbn, bsize, NOCRED, &bp)) {
+ brelse(bp);
+ return (error);
+ }
+ if (ap->a_res)
+ *ap->a_res = (char *)bp->b_data + blkoff(fs, ap->a_offset);
+ *ap->a_bpp = bp;
+ return (0);
+}
+
+
+/*
+ * lfs_seglock --
+ * Single thread the segment writer.
+ */
+void
+lfs_seglock(fs, flags)
+ struct lfs *fs;
+ unsigned long flags;
+{
+ struct segment *sp;
+ int s;
+
+ if (fs->lfs_seglock)
+ if (fs->lfs_lockpid == curproc->p_pid) {
+ ++fs->lfs_seglock;
+ fs->lfs_sp->seg_flags |= flags;
+ return;
+ } else while (fs->lfs_seglock)
+ (void)tsleep(&fs->lfs_seglock, PRIBIO + 1,
+ "lfs seglock", 0);
+
+ fs->lfs_seglock = 1;
+ fs->lfs_lockpid = curproc->p_pid;
+
+ sp = fs->lfs_sp = malloc(sizeof(struct segment), M_SEGMENT, M_WAITOK);
+ sp->bpp = malloc(((LFS_SUMMARY_SIZE - sizeof(SEGSUM)) /
+ sizeof(daddr_t) + 1) * sizeof(struct buf *), M_SEGMENT, M_WAITOK);
+ sp->seg_flags = flags;
+ sp->vp = NULL;
+ (void) lfs_initseg(fs);
+
+ /*
+ * Keep a cumulative count of the outstanding I/O operations. If the
+ * disk drive catches up with us it could go to zero before we finish,
+ * so we artificially increment it by one until we've scheduled all of
+ * the writes we intend to do.
+ */
+ s = splbio();
+ ++fs->lfs_iocount;
+ splx(s);
+}
+/*
+ * lfs_segunlock --
+ * Single thread the segment writer.
+ */
+void
+lfs_segunlock(fs)
+ struct lfs *fs;
+{
+ struct segment *sp;
+ unsigned long sync, ckp;
+ int s;
+
+ if (fs->lfs_seglock == 1) {
+
+ sp = fs->lfs_sp;
+ sync = sp->seg_flags & SEGM_SYNC;
+ ckp = sp->seg_flags & SEGM_CKP;
+ if (sp->bpp != sp->cbpp) {
+ /* Free allocated segment summary */
+ fs->lfs_offset -= LFS_SUMMARY_SIZE / DEV_BSIZE;
+ brelvp(*sp->bpp);
+ free((*sp->bpp)->b_data, M_SEGMENT);
+ free(*sp->bpp, M_SEGMENT);
+ } else
+ printf ("unlock to 0 with no summary");
+ free(sp->bpp, M_SEGMENT);
+ free(sp, M_SEGMENT);
+
+ /*
+ * If the I/O count is non-zero, sleep until it reaches zero.
+ * At the moment, the user's process hangs around so we can
+ * sleep.
+ */
+ s = splbio();
+ --fs->lfs_iocount;
+ /*
+ * We let checkpoints happen asynchronously. That means
+ * that during recovery, we have to roll forward between
+ * the two segments described by the first and second
+ * superblocks to make sure that the checkpoint described
+ * by a superblock completed.
+ */
+ if (sync && fs->lfs_iocount)
+ (void)tsleep(&fs->lfs_iocount, PRIBIO + 1, "lfs vflush", 0);
+ splx(s);
+ if (ckp) {
+ fs->lfs_nactive = 0;
+ lfs_writesuper(fs);
+ }
+ --fs->lfs_seglock;
+ fs->lfs_lockpid = 0;
+ wakeup(&fs->lfs_seglock);
+ } else if (fs->lfs_seglock == 0) {
+ panic ("Seglock not held");
+ } else {
+ --fs->lfs_seglock;
+ }
+}
diff --git a/sys/ufs/lfs/lfs_syscalls.c b/sys/ufs/lfs/lfs_syscalls.c
new file mode 100644
index 0000000..a4b1501
--- /dev/null
+++ b/sys/ufs/lfs/lfs_syscalls.c
@@ -0,0 +1,563 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lfs_syscalls.c 8.5 (Berkeley) 4/20/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+#define BUMP_FIP(SP) \
+ (SP)->fip = (FINFO *) (&(SP)->fip->fi_blocks[(SP)->fip->fi_nblocks])
+
+#define INC_FINFO(SP) ++((SEGSUM *)((SP)->segsum))->ss_nfinfo
+#define DEC_FINFO(SP) --((SEGSUM *)((SP)->segsum))->ss_nfinfo
+
+/*
+ * Before committing to add something to a segment summary, make sure there
+ * is enough room. S is the bytes added to the summary.
+ */
+#define CHECK_SEG(s) \
+if (sp->sum_bytes_left < (s)) { \
+ (void) lfs_writeseg(fs, sp); \
+}
+struct buf *lfs_fakebuf __P((struct vnode *, int, size_t, caddr_t));
+
+/*
+ * lfs_markv:
+ *
+ * This will mark inodes and blocks dirty, so they are written into the log.
+ * It will block until all the blocks have been written. The segment create
+ * time passed in the block_info and inode_info structures is used to decide
+ * if the data is valid for each block (in case some process dirtied a block
+ * or inode that is being cleaned between the determination that a block is
+ * live and the lfs_markv call).
+ *
+ * 0 on success
+ * -1/errno is return on error.
+ */
+struct lfs_markv_args {
+ fsid_t *fsidp; /* file system */
+ BLOCK_INFO *blkiov; /* block array */
+ int blkcnt; /* count of block array entries */
+};
+int
+lfs_markv(p, uap, retval)
+ struct proc *p;
+ struct lfs_markv_args *uap;
+ int *retval;
+{
+ struct segment *sp;
+ BLOCK_INFO *blkp;
+ IFILE *ifp;
+ struct buf *bp, **bpp;
+ struct inode *ip = 0;
+ struct lfs *fs;
+ struct mount *mntp;
+ struct vnode *vp;
+ fsid_t fsid;
+ void *start;
+ ino_t lastino;
+ daddr_t b_daddr, v_daddr;
+ u_long bsize;
+ int cnt, error;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+
+ if (error = copyin(uap->fsidp, &fsid, sizeof(fsid_t)))
+ return (error);
+ if ((mntp = getvfs(&fsid)) == NULL)
+ return (EINVAL);
+
+ cnt = uap->blkcnt;
+ start = malloc(cnt * sizeof(BLOCK_INFO), M_SEGMENT, M_WAITOK);
+ if (error = copyin(uap->blkiov, start, cnt * sizeof(BLOCK_INFO)))
+ goto err1;
+
+ /* Mark blocks/inodes dirty. */
+ fs = VFSTOUFS(mntp)->um_lfs;
+ bsize = fs->lfs_bsize;
+ error = 0;
+
+ lfs_seglock(fs, SEGM_SYNC | SEGM_CLEAN);
+ sp = fs->lfs_sp;
+ for (v_daddr = LFS_UNUSED_DADDR, lastino = LFS_UNUSED_INUM,
+ blkp = start; cnt--; ++blkp) {
+ /*
+ * Get the IFILE entry (only once) and see if the file still
+ * exists.
+ */
+ if (lastino != blkp->bi_inode) {
+ if (lastino != LFS_UNUSED_INUM) {
+ /* Finish up last file */
+ if (sp->fip->fi_nblocks == 0) {
+ DEC_FINFO(sp);
+ sp->sum_bytes_left +=
+ sizeof(FINFO) - sizeof(daddr_t);
+ } else {
+ lfs_updatemeta(sp);
+ BUMP_FIP(sp);
+ }
+
+ lfs_writeinode(fs, sp, ip);
+ lfs_vunref(vp);
+ }
+
+ /* Start a new file */
+ CHECK_SEG(sizeof(FINFO));
+ sp->sum_bytes_left -= sizeof(FINFO) - sizeof(daddr_t);
+ INC_FINFO(sp);
+ sp->start_lbp = &sp->fip->fi_blocks[0];
+ sp->vp = NULL;
+ sp->fip->fi_version = blkp->bi_version;
+ sp->fip->fi_nblocks = 0;
+ sp->fip->fi_ino = blkp->bi_inode;
+ lastino = blkp->bi_inode;
+ if (blkp->bi_inode == LFS_IFILE_INUM)
+ v_daddr = fs->lfs_idaddr;
+ else {
+ LFS_IENTRY(ifp, fs, blkp->bi_inode, bp);
+ v_daddr = ifp->if_daddr;
+ brelse(bp);
+ }
+ if (v_daddr == LFS_UNUSED_DADDR)
+ continue;
+
+ /* Get the vnode/inode. */
+ if (lfs_fastvget(mntp, blkp->bi_inode, v_daddr, &vp,
+ blkp->bi_lbn == LFS_UNUSED_LBN ?
+ blkp->bi_bp : NULL)) {
+#ifdef DIAGNOSTIC
+ printf("lfs_markv: VFS_VGET failed (%d)\n",
+ blkp->bi_inode);
+#endif
+ lastino = LFS_UNUSED_INUM;
+ v_daddr = LFS_UNUSED_DADDR;
+ continue;
+ }
+ sp->vp = vp;
+ ip = VTOI(vp);
+ } else if (v_daddr == LFS_UNUSED_DADDR)
+ continue;
+
+ /* If this BLOCK_INFO didn't contain a block, keep going. */
+ if (blkp->bi_lbn == LFS_UNUSED_LBN)
+ continue;
+ if (VOP_BMAP(vp, blkp->bi_lbn, NULL, &b_daddr, NULL) ||
+ b_daddr != blkp->bi_daddr)
+ continue;
+ /*
+ * If we got to here, then we are keeping the block. If it
+ * is an indirect block, we want to actually put it in the
+ * buffer cache so that it can be updated in the finish_meta
+ * section. If it's not, we need to allocate a fake buffer
+ * so that writeseg can perform the copyin and write the buffer.
+ */
+ if (blkp->bi_lbn >= 0) /* Data Block */
+ bp = lfs_fakebuf(vp, blkp->bi_lbn, bsize,
+ blkp->bi_bp);
+ else {
+ bp = getblk(vp, blkp->bi_lbn, bsize, 0, 0);
+ if (!(bp->b_flags & (B_DELWRI | B_DONE | B_CACHE)) &&
+ (error = copyin(blkp->bi_bp, bp->b_data,
+ bsize)))
+ goto err2;
+ if (error = VOP_BWRITE(bp))
+ goto err2;
+ }
+ while (lfs_gatherblock(sp, bp, NULL));
+ }
+ if (sp->vp) {
+ if (sp->fip->fi_nblocks == 0) {
+ DEC_FINFO(sp);
+ sp->sum_bytes_left +=
+ sizeof(FINFO) - sizeof(daddr_t);
+ } else
+ lfs_updatemeta(sp);
+
+ lfs_writeinode(fs, sp, ip);
+ lfs_vunref(vp);
+ }
+ (void) lfs_writeseg(fs, sp);
+ lfs_segunlock(fs);
+ free(start, M_SEGMENT);
+ return (error);
+
+/*
+ * XXX
+ * If we come in to error 2, we might have indirect blocks that were
+ * updated and now have bad block pointers. I don't know what to do
+ * about this.
+ */
+
+err2: lfs_vunref(vp);
+ /* Free up fakebuffers */
+ for (bpp = --sp->cbpp; bpp >= sp->bpp; --bpp)
+ if ((*bpp)->b_flags & B_CALL) {
+ brelvp(*bpp);
+ free(*bpp, M_SEGMENT);
+ } else
+ brelse(*bpp);
+ lfs_segunlock(fs);
+err1:
+ free(start, M_SEGMENT);
+ return (error);
+}
+
+/*
+ * lfs_bmapv:
+ *
+ * This will fill in the current disk address for arrays of blocks.
+ *
+ * 0 on success
+ * -1/errno is return on error.
+ */
+struct lfs_bmapv_args {
+ fsid_t *fsidp; /* file system */
+ BLOCK_INFO *blkiov; /* block array */
+ int blkcnt; /* count of block array entries */
+};
+int
+lfs_bmapv(p, uap, retval)
+ struct proc *p;
+ struct lfs_bmapv_args *uap;
+ int *retval;
+{
+ BLOCK_INFO *blkp;
+ struct mount *mntp;
+ struct vnode *vp;
+ fsid_t fsid;
+ void *start;
+ daddr_t daddr;
+ int cnt, error, step;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+
+ if (error = copyin(uap->fsidp, &fsid, sizeof(fsid_t)))
+ return (error);
+ if ((mntp = getvfs(&fsid)) == NULL)
+ return (EINVAL);
+
+ cnt = uap->blkcnt;
+ start = blkp = malloc(cnt * sizeof(BLOCK_INFO), M_SEGMENT, M_WAITOK);
+ if (error = copyin(uap->blkiov, blkp, cnt * sizeof(BLOCK_INFO))) {
+ free(blkp, M_SEGMENT);
+ return (error);
+ }
+
+ for (step = cnt; step--; ++blkp) {
+ if (blkp->bi_lbn == LFS_UNUSED_LBN)
+ continue;
+ /* Could be a deadlock ? */
+ if (VFS_VGET(mntp, blkp->bi_inode, &vp))
+ daddr = LFS_UNUSED_DADDR;
+ else {
+ if (VOP_BMAP(vp, blkp->bi_lbn, NULL, &daddr, NULL))
+ daddr = LFS_UNUSED_DADDR;
+ vput(vp);
+ }
+ blkp->bi_daddr = daddr;
+ }
+ copyout(start, uap->blkiov, cnt * sizeof(BLOCK_INFO));
+ free(start, M_SEGMENT);
+ return (0);
+}
+
+/*
+ * lfs_segclean:
+ *
+ * Mark the segment clean.
+ *
+ * 0 on success
+ * -1/errno is return on error.
+ */
+struct lfs_segclean_args {
+ fsid_t *fsidp; /* file system */
+ u_long segment; /* segment number */
+};
+int
+lfs_segclean(p, uap, retval)
+ struct proc *p;
+ struct lfs_segclean_args *uap;
+ int *retval;
+{
+ CLEANERINFO *cip;
+ SEGUSE *sup;
+ struct buf *bp;
+ struct mount *mntp;
+ struct lfs *fs;
+ fsid_t fsid;
+ int error;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+
+ if (error = copyin(uap->fsidp, &fsid, sizeof(fsid_t)))
+ return (error);
+ if ((mntp = getvfs(&fsid)) == NULL)
+ return (EINVAL);
+
+ fs = VFSTOUFS(mntp)->um_lfs;
+
+ if (datosn(fs, fs->lfs_curseg) == uap->segment)
+ return (EBUSY);
+
+ LFS_SEGENTRY(sup, fs, uap->segment, bp);
+ if (sup->su_flags & SEGUSE_ACTIVE) {
+ brelse(bp);
+ return (EBUSY);
+ }
+ fs->lfs_avail += fsbtodb(fs, fs->lfs_ssize) - 1;
+ fs->lfs_bfree += (sup->su_nsums * LFS_SUMMARY_SIZE / DEV_BSIZE) +
+ sup->su_ninos * btodb(fs->lfs_bsize);
+ sup->su_flags &= ~SEGUSE_DIRTY;
+ (void) VOP_BWRITE(bp);
+
+ LFS_CLEANERINFO(cip, fs, bp);
+ ++cip->clean;
+ --cip->dirty;
+ (void) VOP_BWRITE(bp);
+ wakeup(&fs->lfs_avail);
+ return (0);
+}
+
+/*
+ * lfs_segwait:
+ *
+ * This will block until a segment in file system fsid is written. A timeout
+ * in milliseconds may be specified which will awake the cleaner automatically.
+ * An fsid of -1 means any file system, and a timeout of 0 means forever.
+ *
+ * 0 on success
+ * 1 on timeout
+ * -1/errno is return on error.
+ */
+struct lfs_segwait_args {
+ fsid_t *fsidp; /* file system */
+ struct timeval *tv; /* timeout */
+};
+int
+lfs_segwait(p, uap, retval)
+ struct proc *p;
+ struct lfs_segwait_args *uap;
+ int *retval;
+{
+ extern int lfs_allclean_wakeup;
+ struct mount *mntp;
+ struct timeval atv;
+ fsid_t fsid;
+ void *addr;
+ u_long timeout;
+ int error, s;
+
+ if (error = suser(p->p_ucred, &p->p_acflag)) {
+ return (error);
+}
+#ifdef WHEN_QUADS_WORK
+ if (error = copyin(uap->fsidp, &fsid, sizeof(fsid_t)))
+ return (error);
+ if (fsid == (fsid_t)-1)
+ addr = &lfs_allclean_wakeup;
+ else {
+ if ((mntp = getvfs(&fsid)) == NULL)
+ return (EINVAL);
+ addr = &VFSTOUFS(mntp)->um_lfs->lfs_nextseg;
+ }
+#else
+ if (error = copyin(uap->fsidp, &fsid, sizeof(fsid_t)))
+ return (error);
+ if ((mntp = getvfs(&fsid)) == NULL)
+ addr = &lfs_allclean_wakeup;
+ else
+ addr = &VFSTOUFS(mntp)->um_lfs->lfs_nextseg;
+#endif
+
+ if (uap->tv) {
+ if (error = copyin(uap->tv, &atv, sizeof(struct timeval)))
+ return (error);
+ if (itimerfix(&atv))
+ return (EINVAL);
+ s = splclock();
+ timevaladd(&atv, (struct timeval *)&time);
+ timeout = hzto(&atv);
+ splx(s);
+ } else
+ timeout = 0;
+
+ error = tsleep(addr, PCATCH | PUSER, "segment", timeout);
+ return (error == ERESTART ? EINTR : 0);
+}
+
+/*
+ * VFS_VGET call specialized for the cleaner. The cleaner already knows the
+ * daddr from the ifile, so don't look it up again. If the cleaner is
+ * processing IINFO structures, it may have the ondisk inode already, so
+ * don't go retrieving it again.
+ */
+int
+lfs_fastvget(mp, ino, daddr, vpp, dinp)
+ struct mount *mp;
+ ino_t ino;
+ daddr_t daddr;
+ struct vnode **vpp;
+ struct dinode *dinp;
+{
+ register struct inode *ip;
+ struct vnode *vp;
+ struct ufsmount *ump;
+ struct buf *bp;
+ dev_t dev;
+ int error;
+
+ ump = VFSTOUFS(mp);
+ dev = ump->um_dev;
+ /*
+ * This is playing fast and loose. Someone may have the inode
+ * locked, in which case they are going to be distinctly unhappy
+ * if we trash something.
+ */
+ if ((*vpp = ufs_ihashlookup(dev, ino)) != NULL) {
+ lfs_vref(*vpp);
+ if ((*vpp)->v_flag & VXLOCK)
+ printf ("Cleaned vnode VXLOCKED\n");
+ ip = VTOI(*vpp);
+ if (ip->i_flags & IN_LOCKED)
+ printf("cleaned vnode locked\n");
+ if (!(ip->i_flag & IN_MODIFIED)) {
+ ++ump->um_lfs->lfs_uinodes;
+ ip->i_flag |= IN_MODIFIED;
+ }
+ ip->i_flag |= IN_MODIFIED;
+ return (0);
+ }
+
+ /* Allocate new vnode/inode. */
+ if (error = lfs_vcreate(mp, ino, &vp)) {
+ *vpp = NULL;
+ return (error);
+ }
+
+ /*
+ * Put it onto its hash chain and lock it so that other requests for
+ * this inode will block if they arrive while we are sleeping waiting
+ * for old data structures to be purged or for the contents of the
+ * disk portion of this inode to be read.
+ */
+ ip = VTOI(vp);
+ ufs_ihashins(ip);
+
+ /*
+ * XXX
+ * This may not need to be here, logically it should go down with
+ * the i_devvp initialization.
+ * Ask Kirk.
+ */
+ ip->i_lfs = ump->um_lfs;
+
+ /* Read in the disk contents for the inode, copy into the inode. */
+ if (dinp)
+ if (error = copyin(dinp, &ip->i_din, sizeof(struct dinode)))
+ return (error);
+ else {
+ if (error = bread(ump->um_devvp, daddr,
+ (int)ump->um_lfs->lfs_bsize, NOCRED, &bp)) {
+ /*
+ * The inode does not contain anything useful, so it
+ * would be misleading to leave it on its hash chain.
+ * Iput() will return it to the free list.
+ */
+ ufs_ihashrem(ip);
+
+ /* Unlock and discard unneeded inode. */
+ lfs_vunref(vp);
+ brelse(bp);
+ *vpp = NULL;
+ return (error);
+ }
+ ip->i_din =
+ *lfs_ifind(ump->um_lfs, ino, (struct dinode *)bp->b_data);
+ brelse(bp);
+ }
+
+ /* Inode was just read from user space or disk, make sure it's locked */
+ ip->i_flag |= IN_LOCKED;
+
+ /*
+ * Initialize the vnode from the inode, check for aliases. In all
+ * cases re-init ip, the underlying vnode/inode may have changed.
+ */
+ if (error = ufs_vinit(mp, lfs_specop_p, LFS_FIFOOPS, &vp)) {
+ lfs_vunref(vp);
+ *vpp = NULL;
+ return (error);
+ }
+ /*
+ * Finish inode initialization now that aliasing has been resolved.
+ */
+ ip->i_devvp = ump->um_devvp;
+ ip->i_flag |= IN_MODIFIED;
+ ++ump->um_lfs->lfs_uinodes;
+ VREF(ip->i_devvp);
+ *vpp = vp;
+ return (0);
+}
+struct buf *
+lfs_fakebuf(vp, lbn, size, uaddr)
+ struct vnode *vp;
+ int lbn;
+ size_t size;
+ caddr_t uaddr;
+{
+ struct buf *bp;
+
+ bp = lfs_newbuf(vp, lbn, 0);
+ bp->b_saveaddr = uaddr;
+ bp->b_bufsize = size;
+ bp->b_bcount = size;
+ bp->b_flags |= B_INVAL;
+ return (bp);
+}
diff --git a/sys/ufs/lfs/lfs_vfsops.c b/sys/ufs/lfs/lfs_vfsops.c
new file mode 100644
index 0000000..f5dd1c6
--- /dev/null
+++ b/sys/ufs/lfs/lfs_vfsops.c
@@ -0,0 +1,578 @@
+/*
+ * Copyright (c) 1989, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lfs_vfsops.c 8.7 (Berkeley) 4/16/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/mbuf.h>
+#include <sys/file.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+int lfs_mountfs __P((struct vnode *, struct mount *, struct proc *));
+
+struct vfsops lfs_vfsops = {
+ lfs_mount,
+ ufs_start,
+ lfs_unmount,
+ ufs_root,
+ ufs_quotactl,
+ lfs_statfs,
+ lfs_sync,
+ lfs_vget,
+ lfs_fhtovp,
+ lfs_vptofh,
+ lfs_init,
+};
+
+int
+lfs_mountroot()
+{
+ panic("lfs_mountroot"); /* XXX -- implement */
+}
+
+/*
+ * VFS Operations.
+ *
+ * mount system call
+ */
+int
+lfs_mount(mp, path, data, ndp, p)
+ register struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ struct vnode *devvp;
+ struct ufs_args args;
+ struct ufsmount *ump = 0;
+ register struct lfs *fs; /* LFS */
+ u_int size;
+ int error;
+
+ if (error = copyin(data, (caddr_t)&args, sizeof (struct ufs_args)))
+ return (error);
+
+ /* Until LFS can do NFS right. XXX */
+ if (args.export.ex_flags & MNT_EXPORTED)
+ return (EINVAL);
+
+ /*
+ * If updating, check whether changing from read-only to
+ * read/write; if there is no device name, that's all we do.
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ ump = VFSTOUFS(mp);
+#ifdef NOTLFS /* LFS */
+ fs = ump->um_fs;
+ if (fs->fs_ronly && (mp->mnt_flag & MNT_RDONLY) == 0)
+ fs->fs_ronly = 0;
+#else
+ fs = ump->um_lfs;
+ if (fs->lfs_ronly && (mp->mnt_flag & MNT_RDONLY) == 0)
+ fs->lfs_ronly = 0;
+#endif
+ if (args.fspec == 0) {
+ /*
+ * Process export requests.
+ */
+ return (vfs_export(mp, &ump->um_export, &args.export));
+ }
+ }
+ /*
+ * Not an update, or updating the name: look up the name
+ * and verify that it refers to a sensible block device.
+ */
+ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
+ if (error = namei(ndp))
+ return (error);
+ devvp = ndp->ni_vp;
+ if (devvp->v_type != VBLK) {
+ vrele(devvp);
+ return (ENOTBLK);
+ }
+ if (major(devvp->v_rdev) >= nblkdev) {
+ vrele(devvp);
+ return (ENXIO);
+ }
+ if ((mp->mnt_flag & MNT_UPDATE) == 0)
+ error = lfs_mountfs(devvp, mp, p); /* LFS */
+ else {
+ if (devvp != ump->um_devvp)
+ error = EINVAL; /* needs translation */
+ else
+ vrele(devvp);
+ }
+ if (error) {
+ vrele(devvp);
+ return (error);
+ }
+ ump = VFSTOUFS(mp);
+ fs = ump->um_lfs; /* LFS */
+#ifdef NOTLFS /* LFS */
+ (void) copyinstr(path, fs->fs_fsmnt, sizeof(fs->fs_fsmnt) - 1, &size);
+ bzero(fs->fs_fsmnt + size, sizeof(fs->fs_fsmnt) - size);
+ bcopy((caddr_t)fs->fs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname,
+ MNAMELEN);
+ (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ (void) ufs_statfs(mp, &mp->mnt_stat, p);
+#else
+ (void)copyinstr(path, fs->lfs_fsmnt, sizeof(fs->lfs_fsmnt) - 1, &size);
+ bzero(fs->lfs_fsmnt + size, sizeof(fs->lfs_fsmnt) - size);
+ bcopy((caddr_t)fs->lfs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname,
+ MNAMELEN);
+ (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ (void) lfs_statfs(mp, &mp->mnt_stat, p);
+#endif
+ return (0);
+}
+
+/*
+ * Common code for mount and mountroot
+ * LFS specific
+ */
+int
+lfs_mountfs(devvp, mp, p)
+ register struct vnode *devvp;
+ struct mount *mp;
+ struct proc *p;
+{
+ extern struct vnode *rootvp;
+ register struct lfs *fs;
+ register struct ufsmount *ump;
+ struct vnode *vp;
+ struct buf *bp;
+ struct partinfo dpart;
+ dev_t dev;
+ int error, i, ronly, size;
+
+ /*
+ * Disallow multiple mounts of the same device.
+ * Disallow mounting of a device that is currently in use
+ * (except for root, which might share swap device for miniroot).
+ * Flush out any old buffers remaining from a previous use.
+ */
+ if (error = vfs_mountedon(devvp))
+ return (error);
+ if (vcount(devvp) > 1 && devvp != rootvp)
+ return (EBUSY);
+ if (error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0))
+ return (error);
+
+ ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
+ if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p))
+ return (error);
+
+ if (VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, NOCRED, p) != 0)
+ size = DEV_BSIZE;
+ else {
+ size = dpart.disklab->d_secsize;
+#ifdef NEVER_USED
+ dpart.part->p_fstype = FS_LFS;
+ dpart.part->p_fsize = fs->lfs_fsize; /* frag size */
+ dpart.part->p_frag = fs->lfs_frag; /* frags per block */
+ dpart.part->p_cpg = fs->lfs_segshift; /* segment shift */
+#endif
+ }
+
+ /* Don't free random space on error. */
+ bp = NULL;
+ ump = NULL;
+
+ /* Read in the superblock. */
+ if (error = bread(devvp, LFS_LABELPAD / size, LFS_SBPAD, NOCRED, &bp))
+ goto out;
+ fs = (struct lfs *)bp->b_data;
+
+ /* Check the basics. */
+ if (fs->lfs_magic != LFS_MAGIC || fs->lfs_bsize > MAXBSIZE ||
+ fs->lfs_bsize < sizeof(struct lfs)) {
+ error = EINVAL; /* XXX needs translation */
+ goto out;
+ }
+
+ /* Allocate the mount structure, copy the superblock into it. */
+ ump = (struct ufsmount *)malloc(sizeof *ump, M_UFSMNT, M_WAITOK);
+ fs = ump->um_lfs = malloc(sizeof(struct lfs), M_UFSMNT, M_WAITOK);
+ bcopy(bp->b_data, fs, sizeof(struct lfs));
+ if (sizeof(struct lfs) < LFS_SBPAD) /* XXX why? */
+ bp->b_flags |= B_INVAL;
+ brelse(bp);
+ bp = NULL;
+
+ /* Set up the I/O information */
+ fs->lfs_iocount = 0;
+
+ /* Set up the ifile and lock aflags */
+ fs->lfs_doifile = 0;
+ fs->lfs_writer = 0;
+ fs->lfs_dirops = 0;
+ fs->lfs_seglock = 0;
+
+ /* Set the file system readonly/modify bits. */
+ fs->lfs_ronly = ronly;
+ if (ronly == 0)
+ fs->lfs_fmod = 1;
+
+ /* Initialize the mount structure. */
+ dev = devvp->v_rdev;
+ mp->mnt_data = (qaddr_t)ump;
+ mp->mnt_stat.f_fsid.val[0] = (long)dev;
+ mp->mnt_stat.f_fsid.val[1] = MOUNT_LFS;
+ mp->mnt_flag |= MNT_LOCAL;
+ ump->um_mountp = mp;
+ ump->um_dev = dev;
+ ump->um_devvp = devvp;
+ ump->um_bptrtodb = 0;
+ ump->um_seqinc = 1 << fs->lfs_fsbtodb;
+ ump->um_nindir = fs->lfs_nindir;
+ for (i = 0; i < MAXQUOTAS; i++)
+ ump->um_quotas[i] = NULLVP;
+ devvp->v_specflags |= SI_MOUNTEDON;
+
+ /*
+ * We use the ifile vnode for almost every operation. Instead of
+ * retrieving it from the hash table each time we retrieve it here,
+ * artificially increment the reference count and keep a pointer
+ * to it in the incore copy of the superblock.
+ */
+ if (error = VFS_VGET(mp, LFS_IFILE_INUM, &vp))
+ goto out;
+ fs->lfs_ivnode = vp;
+ VREF(vp);
+ vput(vp);
+
+ return (0);
+out:
+ if (bp)
+ brelse(bp);
+ (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p);
+ if (ump) {
+ free(ump->um_lfs, M_UFSMNT);
+ free(ump, M_UFSMNT);
+ mp->mnt_data = (qaddr_t)0;
+ }
+ return (error);
+}
+
+/*
+ * unmount system call
+ */
+int
+lfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ extern int doforce;
+ register struct ufsmount *ump;
+ register struct lfs *fs;
+ int i, error, flags, ronly;
+
+ flags = 0;
+ if (mntflags & MNT_FORCE) {
+ if (!doforce || (mp->mnt_flag & MNT_ROOTFS))
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ ump = VFSTOUFS(mp);
+ fs = ump->um_lfs;
+#ifdef QUOTA
+ if (mp->mnt_flag & MNT_QUOTA) {
+ if (error = vflush(mp, fs->lfs_ivnode, SKIPSYSTEM|flags))
+ return (error);
+ for (i = 0; i < MAXQUOTAS; i++) {
+ if (ump->um_quotas[i] == NULLVP)
+ continue;
+ quotaoff(p, mp, i);
+ }
+ /*
+ * Here we fall through to vflush again to ensure
+ * that we have gotten rid of all the system vnodes.
+ */
+ }
+#endif
+ if (error = vflush(mp, fs->lfs_ivnode, flags))
+ return (error);
+ fs->lfs_clean = 1;
+ if (error = VFS_SYNC(mp, 1, p->p_ucred, p))
+ return (error);
+ if (fs->lfs_ivnode->v_dirtyblkhd.lh_first)
+ panic("lfs_unmount: still dirty blocks on ifile vnode\n");
+ vrele(fs->lfs_ivnode);
+ vgone(fs->lfs_ivnode);
+
+ ronly = !fs->lfs_ronly;
+ ump->um_devvp->v_specflags &= ~SI_MOUNTEDON;
+ error = VOP_CLOSE(ump->um_devvp,
+ ronly ? FREAD : FREAD|FWRITE, NOCRED, p);
+ vrele(ump->um_devvp);
+ free(fs, M_UFSMNT);
+ free(ump, M_UFSMNT);
+ mp->mnt_data = (qaddr_t)0;
+ mp->mnt_flag &= ~MNT_LOCAL;
+ return (error);
+}
+
+/*
+ * Get file system statistics.
+ */
+int
+lfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ register struct statfs *sbp;
+ struct proc *p;
+{
+ register struct lfs *fs;
+ register struct ufsmount *ump;
+
+ ump = VFSTOUFS(mp);
+ fs = ump->um_lfs;
+ if (fs->lfs_magic != LFS_MAGIC)
+ panic("lfs_statfs: magic");
+ sbp->f_type = MOUNT_LFS;
+ sbp->f_bsize = fs->lfs_bsize;
+ sbp->f_iosize = fs->lfs_bsize;
+ sbp->f_blocks = dbtofsb(fs,fs->lfs_dsize);
+ sbp->f_bfree = dbtofsb(fs, fs->lfs_bfree);
+ sbp->f_bavail = (fs->lfs_dsize * (100 - fs->lfs_minfree) / 100) -
+ (fs->lfs_dsize - fs->lfs_bfree);
+ sbp->f_bavail = dbtofsb(fs, sbp->f_bavail);
+ sbp->f_files = fs->lfs_nfiles;
+ sbp->f_ffree = sbp->f_bfree * INOPB(fs);
+ if (sbp != &mp->mnt_stat) {
+ bcopy((caddr_t)mp->mnt_stat.f_mntonname,
+ (caddr_t)&sbp->f_mntonname[0], MNAMELEN);
+ bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
+ (caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
+ }
+ return (0);
+}
+
+/*
+ * Go through the disk queues to initiate sandbagged IO;
+ * go through the inodes to write those that have been modified;
+ * initiate the writing of the super block if it has been modified.
+ *
+ * Note: we are always called with the filesystem marked `MPBUSY'.
+ */
+int
+lfs_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+ int error;
+
+ /* All syncs must be checkpoints until roll-forward is implemented. */
+ error = lfs_segwrite(mp, SEGM_CKP | (waitfor ? SEGM_SYNC : 0));
+#ifdef QUOTA
+ qsync(mp);
+#endif
+ return (error);
+}
+
+/*
+ * Look up an LFS dinode number to find its incore vnode. If not already
+ * in core, read it in from the specified device. Return the inode locked.
+ * Detection and handling of mount points must be done by the calling routine.
+ */
+int
+lfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+ register struct lfs *fs;
+ register struct inode *ip;
+ struct buf *bp;
+ struct ifile *ifp;
+ struct vnode *vp;
+ struct ufsmount *ump;
+ daddr_t daddr;
+ dev_t dev;
+ int error;
+
+ ump = VFSTOUFS(mp);
+ dev = ump->um_dev;
+ if ((*vpp = ufs_ihashget(dev, ino)) != NULL)
+ return (0);
+
+ /* Translate the inode number to a disk address. */
+ fs = ump->um_lfs;
+ if (ino == LFS_IFILE_INUM)
+ daddr = fs->lfs_idaddr;
+ else {
+ LFS_IENTRY(ifp, fs, ino, bp);
+ daddr = ifp->if_daddr;
+ brelse(bp);
+ if (daddr == LFS_UNUSED_DADDR)
+ return (ENOENT);
+ }
+
+ /* Allocate new vnode/inode. */
+ if (error = lfs_vcreate(mp, ino, &vp)) {
+ *vpp = NULL;
+ return (error);
+ }
+
+ /*
+ * Put it onto its hash chain and lock it so that other requests for
+ * this inode will block if they arrive while we are sleeping waiting
+ * for old data structures to be purged or for the contents of the
+ * disk portion of this inode to be read.
+ */
+ ip = VTOI(vp);
+ ufs_ihashins(ip);
+
+ /*
+ * XXX
+ * This may not need to be here, logically it should go down with
+ * the i_devvp initialization.
+ * Ask Kirk.
+ */
+ ip->i_lfs = ump->um_lfs;
+
+ /* Read in the disk contents for the inode, copy into the inode. */
+ if (error =
+ bread(ump->um_devvp, daddr, (int)fs->lfs_bsize, NOCRED, &bp)) {
+ /*
+ * The inode does not contain anything useful, so it would
+ * be misleading to leave it on its hash chain. With mode
+ * still zero, it will be unlinked and returned to the free
+ * list by vput().
+ */
+ vput(vp);
+ brelse(bp);
+ *vpp = NULL;
+ return (error);
+ }
+ ip->i_din = *lfs_ifind(fs, ino, (struct dinode *)bp->b_data);
+ brelse(bp);
+
+ /*
+ * Initialize the vnode from the inode, check for aliases. In all
+ * cases re-init ip, the underlying vnode/inode may have changed.
+ */
+ if (error = ufs_vinit(mp, lfs_specop_p, LFS_FIFOOPS, &vp)) {
+ vput(vp);
+ *vpp = NULL;
+ return (error);
+ }
+ /*
+ * Finish inode initialization now that aliasing has been resolved.
+ */
+ ip->i_devvp = ump->um_devvp;
+ VREF(ip->i_devvp);
+ *vpp = vp;
+ return (0);
+}
+
+/*
+ * File handle to vnode
+ *
+ * Have to be really careful about stale file handles:
+ * - check that the inode number is valid
+ * - call lfs_vget() to get the locked inode
+ * - check for an unallocated inode (i_mode == 0)
+ * - check that the given client host has export rights and return
+ * those rights via. exflagsp and credanonp
+ *
+ * XXX
+ * use ifile to see if inode is allocated instead of reading off disk
+ * what is the relationship between my generational number and the NFS
+ * generational number.
+ */
+int
+lfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
+ register struct mount *mp;
+ struct fid *fhp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred **credanonp;
+{
+ register struct ufid *ufhp;
+
+ ufhp = (struct ufid *)fhp;
+ if (ufhp->ufid_ino < ROOTINO)
+ return (ESTALE);
+ return (ufs_check_export(mp, ufhp, nam, vpp, exflagsp, credanonp));
+}
+
+/*
+ * Vnode pointer to File handle
+ */
+/* ARGSUSED */
+int
+lfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+ register struct inode *ip;
+ register struct ufid *ufhp;
+
+ ip = VTOI(vp);
+ ufhp = (struct ufid *)fhp;
+ ufhp->ufid_len = sizeof(struct ufid);
+ ufhp->ufid_ino = ip->i_number;
+ ufhp->ufid_gen = ip->i_gen;
+ return (0);
+}
diff --git a/sys/ufs/lfs/lfs_vnops.c b/sys/ufs/lfs/lfs_vnops.c
new file mode 100644
index 0000000..e5da7fc
--- /dev/null
+++ b/sys/ufs/lfs/lfs_vnops.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (c) 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lfs_vnops.c 8.5 (Berkeley) 12/30/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/resourcevar.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
+
+#include <miscfs/specfs/specdev.h>
+#include <miscfs/fifofs/fifo.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+/* Global vfs data structures for lfs. */
+int (**lfs_vnodeop_p)();
+struct vnodeopv_entry_desc lfs_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, ufs_lookup }, /* lookup */
+ { &vop_create_desc, ufs_create }, /* create */
+ { &vop_mknod_desc, ufs_mknod }, /* mknod */
+ { &vop_open_desc, ufs_open }, /* open */
+ { &vop_close_desc, lfs_close }, /* close */
+ { &vop_access_desc, ufs_access }, /* access */
+ { &vop_getattr_desc, lfs_getattr }, /* getattr */
+ { &vop_setattr_desc, ufs_setattr }, /* setattr */
+ { &vop_read_desc, lfs_read }, /* read */
+ { &vop_write_desc, lfs_write }, /* write */
+ { &vop_ioctl_desc, ufs_ioctl }, /* ioctl */
+ { &vop_select_desc, ufs_select }, /* select */
+ { &vop_mmap_desc, ufs_mmap }, /* mmap */
+ { &vop_fsync_desc, lfs_fsync }, /* fsync */
+ { &vop_seek_desc, ufs_seek }, /* seek */
+ { &vop_remove_desc, ufs_remove }, /* remove */
+ { &vop_link_desc, ufs_link }, /* link */
+ { &vop_rename_desc, ufs_rename }, /* rename */
+ { &vop_mkdir_desc, ufs_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, ufs_rmdir }, /* rmdir */
+ { &vop_symlink_desc, ufs_symlink }, /* symlink */
+ { &vop_readdir_desc, ufs_readdir }, /* readdir */
+ { &vop_readlink_desc, ufs_readlink }, /* readlink */
+ { &vop_abortop_desc, ufs_abortop }, /* abortop */
+ { &vop_inactive_desc, lfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, ufs_reclaim }, /* reclaim */
+ { &vop_lock_desc, ufs_lock }, /* lock */
+ { &vop_unlock_desc, ufs_unlock }, /* unlock */
+ { &vop_bmap_desc, ufs_bmap }, /* bmap */
+ { &vop_strategy_desc, ufs_strategy }, /* strategy */
+ { &vop_print_desc, ufs_print }, /* print */
+ { &vop_islocked_desc, ufs_islocked }, /* islocked */
+ { &vop_pathconf_desc, ufs_pathconf }, /* pathconf */
+ { &vop_advlock_desc, ufs_advlock }, /* advlock */
+ { &vop_blkatoff_desc, lfs_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, lfs_valloc }, /* valloc */
+ { &vop_vfree_desc, lfs_vfree }, /* vfree */
+ { &vop_truncate_desc, lfs_truncate }, /* truncate */
+ { &vop_update_desc, lfs_update }, /* update */
+ { &vop_bwrite_desc, lfs_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc lfs_vnodeop_opv_desc =
+ { &lfs_vnodeop_p, lfs_vnodeop_entries };
+
+int (**lfs_specop_p)();
+struct vnodeopv_entry_desc lfs_specop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, spec_lookup }, /* lookup */
+ { &vop_create_desc, spec_create }, /* create */
+ { &vop_mknod_desc, spec_mknod }, /* mknod */
+ { &vop_open_desc, spec_open }, /* open */
+ { &vop_close_desc, ufsspec_close }, /* close */
+ { &vop_access_desc, ufs_access }, /* access */
+ { &vop_getattr_desc, lfs_getattr }, /* getattr */
+ { &vop_setattr_desc, ufs_setattr }, /* setattr */
+ { &vop_read_desc, ufsspec_read }, /* read */
+ { &vop_write_desc, ufsspec_write }, /* write */
+ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */
+ { &vop_select_desc, spec_select }, /* select */
+ { &vop_mmap_desc, spec_mmap }, /* mmap */
+ { &vop_fsync_desc, spec_fsync }, /* fsync */
+ { &vop_seek_desc, spec_seek }, /* seek */
+ { &vop_remove_desc, spec_remove }, /* remove */
+ { &vop_link_desc, spec_link }, /* link */
+ { &vop_rename_desc, spec_rename }, /* rename */
+ { &vop_mkdir_desc, spec_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, spec_rmdir }, /* rmdir */
+ { &vop_symlink_desc, spec_symlink }, /* symlink */
+ { &vop_readdir_desc, spec_readdir }, /* readdir */
+ { &vop_readlink_desc, spec_readlink }, /* readlink */
+ { &vop_abortop_desc, spec_abortop }, /* abortop */
+ { &vop_inactive_desc, lfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, ufs_reclaim }, /* reclaim */
+ { &vop_lock_desc, ufs_lock }, /* lock */
+ { &vop_unlock_desc, ufs_unlock }, /* unlock */
+ { &vop_bmap_desc, spec_bmap }, /* bmap */
+ { &vop_strategy_desc, spec_strategy }, /* strategy */
+ { &vop_print_desc, ufs_print }, /* print */
+ { &vop_islocked_desc, ufs_islocked }, /* islocked */
+ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */
+ { &vop_advlock_desc, spec_advlock }, /* advlock */
+ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, spec_valloc }, /* valloc */
+ { &vop_vfree_desc, lfs_vfree }, /* vfree */
+ { &vop_truncate_desc, spec_truncate }, /* truncate */
+ { &vop_update_desc, lfs_update }, /* update */
+ { &vop_bwrite_desc, lfs_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc lfs_specop_opv_desc =
+ { &lfs_specop_p, lfs_specop_entries };
+
+#ifdef FIFO
+int (**lfs_fifoop_p)();
+struct vnodeopv_entry_desc lfs_fifoop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, fifo_lookup }, /* lookup */
+ { &vop_create_desc, fifo_create }, /* create */
+ { &vop_mknod_desc, fifo_mknod }, /* mknod */
+ { &vop_open_desc, fifo_open }, /* open */
+ { &vop_close_desc, ufsfifo_close }, /* close */
+ { &vop_access_desc, ufs_access }, /* access */
+ { &vop_getattr_desc, lfs_getattr }, /* getattr */
+ { &vop_setattr_desc, ufs_setattr }, /* setattr */
+ { &vop_read_desc, ufsfifo_read }, /* read */
+ { &vop_write_desc, ufsfifo_write }, /* write */
+ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */
+ { &vop_select_desc, fifo_select }, /* select */
+ { &vop_mmap_desc, fifo_mmap }, /* mmap */
+ { &vop_fsync_desc, fifo_fsync }, /* fsync */
+ { &vop_seek_desc, fifo_seek }, /* seek */
+ { &vop_remove_desc, fifo_remove }, /* remove */
+ { &vop_link_desc, fifo_link }, /* link */
+ { &vop_rename_desc, fifo_rename }, /* rename */
+ { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */
+ { &vop_symlink_desc, fifo_symlink }, /* symlink */
+ { &vop_readdir_desc, fifo_readdir }, /* readdir */
+ { &vop_readlink_desc, fifo_readlink }, /* readlink */
+ { &vop_abortop_desc, fifo_abortop }, /* abortop */
+ { &vop_inactive_desc, lfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, ufs_reclaim }, /* reclaim */
+ { &vop_lock_desc, ufs_lock }, /* lock */
+ { &vop_unlock_desc, ufs_unlock }, /* unlock */
+ { &vop_bmap_desc, fifo_bmap }, /* bmap */
+ { &vop_strategy_desc, fifo_strategy }, /* strategy */
+ { &vop_print_desc, ufs_print }, /* print */
+ { &vop_islocked_desc, ufs_islocked }, /* islocked */
+ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */
+ { &vop_advlock_desc, fifo_advlock }, /* advlock */
+ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, fifo_valloc }, /* valloc */
+ { &vop_vfree_desc, lfs_vfree }, /* vfree */
+ { &vop_truncate_desc, fifo_truncate }, /* truncate */
+ { &vop_update_desc, lfs_update }, /* update */
+ { &vop_bwrite_desc, lfs_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc lfs_fifoop_opv_desc =
+ { &lfs_fifoop_p, lfs_fifoop_entries };
+#endif /* FIFO */
+
+#define LFS_READWRITE
+#include <ufs/ufs/ufs_readwrite.c>
+#undef LFS_READWRITE
+
+/*
+ * Synch an open file.
+ */
+/* ARGSUSED */
+int
+lfs_fsync(ap)
+ struct vop_fsync_args /* {
+ struct vnode *a_vp;
+ struct ucred *a_cred;
+ int a_waitfor;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct timeval tv;
+
+ tv = time;
+ return (VOP_UPDATE(ap->a_vp, &tv, &tv,
+ ap->a_waitfor == MNT_WAIT ? LFS_SYNC : 0));
+}
+
+/*
+ * These macros are used to bracket UFS directory ops, so that we can
+ * identify all the pages touched during directory ops which need to
+ * be ordered and flushed atomically, so that they may be recovered.
+ */
+#define SET_DIROP(fs) { \
+ if ((fs)->lfs_writer) \
+ tsleep(&(fs)->lfs_dirops, PRIBIO + 1, "lfs_dirop", 0); \
+ ++(fs)->lfs_dirops; \
+ (fs)->lfs_doifile = 1; \
+}
+
+#define SET_ENDOP(fs) { \
+ --(fs)->lfs_dirops; \
+ if (!(fs)->lfs_dirops) \
+ wakeup(&(fs)->lfs_writer); \
+}
+
+#define MARK_VNODE(dvp) (dvp)->v_flag |= VDIROP
+
+int
+lfs_symlink(ap)
+ struct vop_symlink_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ char *a_target;
+ } */ *ap;
+{
+ int ret;
+
+ SET_DIROP(VTOI(ap->a_dvp)->i_lfs);
+ MARK_VNODE(ap->a_dvp);
+ ret = ufs_symlink(ap);
+ SET_ENDOP(VTOI(ap->a_dvp)->i_lfs);
+ return (ret);
+}
+
+int
+lfs_mknod(ap)
+ struct vop_mknod_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ int ret;
+
+ SET_DIROP(VTOI(ap->a_dvp)->i_lfs);
+ MARK_VNODE(ap->a_dvp);
+ ret = ufs_mknod(ap);
+ SET_ENDOP(VTOI(ap->a_dvp)->i_lfs);
+ return (ret);
+}
+
+int
+lfs_create(ap)
+ struct vop_create_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ int ret;
+
+ SET_DIROP(VTOI(ap->a_dvp)->i_lfs);
+ MARK_VNODE(ap->a_dvp);
+ ret = ufs_create(ap);
+ SET_ENDOP(VTOI(ap->a_dvp)->i_lfs);
+ return (ret);
+}
+
+int
+lfs_mkdir(ap)
+ struct vop_mkdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ int ret;
+
+ SET_DIROP(VTOI(ap->a_dvp)->i_lfs);
+ MARK_VNODE(ap->a_dvp);
+ ret = ufs_mkdir(ap);
+ SET_ENDOP(VTOI(ap->a_dvp)->i_lfs);
+ return (ret);
+}
+
+int
+lfs_remove(ap)
+ struct vop_remove_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int ret;
+
+ SET_DIROP(VTOI(ap->a_dvp)->i_lfs);
+ MARK_VNODE(ap->a_dvp);
+ MARK_VNODE(ap->a_vp);
+ ret = ufs_remove(ap);
+ SET_ENDOP(VTOI(ap->a_dvp)->i_lfs);
+ return (ret);
+}
+
+int
+lfs_rmdir(ap)
+ struct vop_rmdir_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int ret;
+
+ SET_DIROP(VTOI(ap->a_dvp)->i_lfs);
+ MARK_VNODE(ap->a_dvp);
+ MARK_VNODE(ap->a_vp);
+ ret = ufs_rmdir(ap);
+ SET_ENDOP(VTOI(ap->a_dvp)->i_lfs);
+ return (ret);
+}
+
+int
+lfs_link(ap)
+ struct vop_link_args /* {
+ struct vnode *a_vp;
+ struct vnode *a_tdvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int ret;
+
+ SET_DIROP(VTOI(ap->a_vp)->i_lfs);
+ MARK_VNODE(ap->a_vp);
+ ret = ufs_link(ap);
+ SET_ENDOP(VTOI(ap->a_vp)->i_lfs);
+ return (ret);
+}
+
+int
+lfs_rename(ap)
+ struct vop_rename_args /* {
+ struct vnode *a_fdvp;
+ struct vnode *a_fvp;
+ struct componentname *a_fcnp;
+ struct vnode *a_tdvp;
+ struct vnode *a_tvp;
+ struct componentname *a_tcnp;
+ } */ *ap;
+{
+ int ret;
+
+ SET_DIROP(VTOI(ap->a_fdvp)->i_lfs);
+ MARK_VNODE(ap->a_fdvp);
+ MARK_VNODE(ap->a_tdvp);
+ ret = ufs_rename(ap);
+ SET_ENDOP(VTOI(ap->a_fdvp)->i_lfs);
+ return (ret);
+}
+/* XXX hack to avoid calling ITIMES in getattr */
+int
+lfs_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip = VTOI(vp);
+ register struct vattr *vap = ap->a_vap;
+ /*
+ * Copy from inode table
+ */
+ vap->va_fsid = ip->i_dev;
+ vap->va_fileid = ip->i_number;
+ vap->va_mode = ip->i_mode & ~IFMT;
+ vap->va_nlink = ip->i_nlink;
+ vap->va_uid = ip->i_uid;
+ vap->va_gid = ip->i_gid;
+ vap->va_rdev = (dev_t)ip->i_rdev;
+ vap->va_size = ip->i_din.di_size;
+ vap->va_atime = ip->i_atime;
+ vap->va_mtime = ip->i_mtime;
+ vap->va_ctime = ip->i_ctime;
+ vap->va_flags = ip->i_flags;
+ vap->va_gen = ip->i_gen;
+ /* this doesn't belong here */
+ if (vp->v_type == VBLK)
+ vap->va_blocksize = BLKDEV_IOSIZE;
+ else if (vp->v_type == VCHR)
+ vap->va_blocksize = MAXBSIZE;
+ else
+ vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
+ vap->va_bytes = dbtob(ip->i_blocks);
+ vap->va_type = vp->v_type;
+ vap->va_filerev = ip->i_modrev;
+ return (0);
+}
+/*
+ * Close called
+ *
+ * XXX -- we were using ufs_close, but since it updates the
+ * times on the inode, we might need to bump the uinodes
+ * count.
+ */
+/* ARGSUSED */
+int
+lfs_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip = VTOI(vp);
+ int mod;
+
+ if (vp->v_usecount > 1 && !(ip->i_flag & IN_LOCKED)) {
+ mod = ip->i_flag & IN_MODIFIED;
+ ITIMES(ip, &time, &time);
+ if (!mod && ip->i_flag & IN_MODIFIED)
+ ip->i_lfs->lfs_uinodes++;
+ }
+ return (0);
+}
+
+/*
+ * Stub inactive routine that avoid calling ufs_inactive in some cases.
+ */
+int lfs_no_inactive = 0;
+
+int
+lfs_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ if (lfs_no_inactive)
+ return (0);
+ return (ufs_inactive(ap));
+}
diff --git a/sys/ufs/mfs/mfs_extern.h b/sys/ufs/mfs/mfs_extern.h
new file mode 100644
index 0000000..e357faf6
--- /dev/null
+++ b/sys/ufs/mfs/mfs_extern.h
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mfs_extern.h 8.1 (Berkeley) 6/11/93
+ */
+
+struct buf;
+struct mount;
+struct nameidata;
+struct proc;
+struct statfs;
+struct ucred;
+struct vnode;
+
+__BEGIN_DECLS
+int mfs_badop __P((void));
+int mfs_bmap __P((struct vop_bmap_args *));
+int mfs_close __P((struct vop_close_args *));
+void mfs_doio __P((struct buf *bp, caddr_t base));
+int mfs_inactive __P((struct vop_inactive_args *)); /* XXX */
+int mfs_reclaim __P((struct vop_reclaim_args *)); /* XXX */
+int mfs_init __P((void));
+int mfs_ioctl __P((struct vop_ioctl_args *));
+int mfs_mount __P((struct mount *mp,
+ char *path, caddr_t data, struct nameidata *ndp, struct proc *p));
+int mfs_open __P((struct vop_open_args *));
+int mfs_print __P((struct vop_print_args *)); /* XXX */
+int mfs_start __P((struct mount *mp, int flags, struct proc *p));
+int mfs_statfs __P((struct mount *mp, struct statfs *sbp, struct proc *p));
+int mfs_strategy __P((struct vop_strategy_args *)); /* XXX */
+__END_DECLS
diff --git a/sys/ufs/mfs/mfs_vfsops.c b/sys/ufs/mfs/mfs_vfsops.c
new file mode 100644
index 0000000..fd87771
--- /dev/null
+++ b/sys/ufs/mfs/mfs_vfsops.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 1989, 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mfs_vfsops.c 8.4 (Berkeley) 4/16/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/mount.h>
+#include <sys/signalvar.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/ffs/fs.h>
+#include <ufs/ffs/ffs_extern.h>
+
+#include <ufs/mfs/mfsnode.h>
+#include <ufs/mfs/mfs_extern.h>
+
+caddr_t mfs_rootbase; /* address of mini-root in kernel virtual memory */
+u_long mfs_rootsize; /* size of mini-root in bytes */
+
+static int mfs_minor; /* used for building internal dev_t */
+
+extern int (**mfs_vnodeop_p)();
+
+/*
+ * mfs vfs operations.
+ */
+struct vfsops mfs_vfsops = {
+ mfs_mount,
+ mfs_start,
+ ffs_unmount,
+ ufs_root,
+ ufs_quotactl,
+ mfs_statfs,
+ ffs_sync,
+ ffs_vget,
+ ffs_fhtovp,
+ ffs_vptofh,
+ mfs_init,
+};
+
+/*
+ * Called by main() when mfs is going to be mounted as root.
+ *
+ * Name is updated by mount(8) after booting.
+ */
+#define ROOTNAME "mfs_root"
+
+int
+mfs_mountroot()
+{
+ extern struct vnode *rootvp;
+ register struct fs *fs;
+ register struct mount *mp;
+ struct proc *p = curproc; /* XXX */
+ struct ufsmount *ump;
+ struct mfsnode *mfsp;
+ u_int size;
+ int error;
+
+ /*
+ * Get vnodes for swapdev and rootdev.
+ */
+ if (bdevvp(swapdev, &swapdev_vp) || bdevvp(rootdev, &rootvp))
+ panic("mfs_mountroot: can't setup bdevvp's");
+
+ mp = malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK);
+ bzero((char *)mp, (u_long)sizeof(struct mount));
+ mp->mnt_op = &mfs_vfsops;
+ mp->mnt_flag = MNT_RDONLY;
+ mfsp = malloc(sizeof *mfsp, M_MFSNODE, M_WAITOK);
+ rootvp->v_data = mfsp;
+ rootvp->v_op = mfs_vnodeop_p;
+ rootvp->v_tag = VT_MFS;
+ mfsp->mfs_baseoff = mfs_rootbase;
+ mfsp->mfs_size = mfs_rootsize;
+ mfsp->mfs_vnode = rootvp;
+ mfsp->mfs_pid = p->p_pid;
+ mfsp->mfs_buflist = (struct buf *)0;
+ if (error = ffs_mountfs(rootvp, mp, p)) {
+ free(mp, M_MOUNT);
+ free(mfsp, M_MFSNODE);
+ return (error);
+ }
+ if (error = vfs_lock(mp)) {
+ (void)ffs_unmount(mp, 0, p);
+ free(mp, M_MOUNT);
+ free(mfsp, M_MFSNODE);
+ return (error);
+ }
+ TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
+ mp->mnt_flag |= MNT_ROOTFS;
+ mp->mnt_vnodecovered = NULLVP;
+ ump = VFSTOUFS(mp);
+ fs = ump->um_fs;
+ bzero(fs->fs_fsmnt, sizeof(fs->fs_fsmnt));
+ fs->fs_fsmnt[0] = '/';
+ bcopy((caddr_t)fs->fs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname,
+ MNAMELEN);
+ (void) copystr(ROOTNAME, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ (void)ffs_statfs(mp, &mp->mnt_stat, p);
+ vfs_unlock(mp);
+ inittodr((time_t)0);
+ return (0);
+}
+
+/*
+ * This is called early in boot to set the base address and size
+ * of the mini-root.
+ */
+int
+mfs_initminiroot(base)
+ caddr_t base;
+{
+ struct fs *fs = (struct fs *)(base + SBOFF);
+ extern int (*mountroot)();
+
+ /* check for valid super block */
+ if (fs->fs_magic != FS_MAGIC || fs->fs_bsize > MAXBSIZE ||
+ fs->fs_bsize < sizeof(struct fs))
+ return (0);
+ mountroot = mfs_mountroot;
+ mfs_rootbase = base;
+ mfs_rootsize = fs->fs_fsize * fs->fs_size;
+ rootdev = makedev(255, mfs_minor++);
+ return (mfs_rootsize);
+}
+
+/*
+ * VFS Operations.
+ *
+ * mount system call
+ */
+/* ARGSUSED */
+int
+mfs_mount(mp, path, data, ndp, p)
+ register struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ struct vnode *devvp;
+ struct mfs_args args;
+ struct ufsmount *ump;
+ register struct fs *fs;
+ register struct mfsnode *mfsp;
+ u_int size;
+ int flags, error;
+
+ if (error = copyin(data, (caddr_t)&args, sizeof (struct mfs_args)))
+ return (error);
+
+ /*
+ * If updating, check whether changing from read-only to
+ * read/write; if there is no device name, that's all we do.
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ ump = VFSTOUFS(mp);
+ fs = ump->um_fs;
+ if (fs->fs_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
+ flags = WRITECLOSE;
+ if (mp->mnt_flag & MNT_FORCE)
+ flags |= FORCECLOSE;
+ if (vfs_busy(mp))
+ return (EBUSY);
+ error = ffs_flushfiles(mp, flags, p);
+ vfs_unbusy(mp);
+ if (error)
+ return (error);
+ }
+ if (fs->fs_ronly && (mp->mnt_flag & MNT_WANTRDWR))
+ fs->fs_ronly = 0;
+#ifdef EXPORTMFS
+ if (args.fspec == 0)
+ return (vfs_export(mp, &ump->um_export, &args.export));
+#endif
+ return (0);
+ }
+ error = getnewvnode(VT_MFS, (struct mount *)0, mfs_vnodeop_p, &devvp);
+ if (error)
+ return (error);
+ devvp->v_type = VBLK;
+ if (checkalias(devvp, makedev(255, mfs_minor++), (struct mount *)0))
+ panic("mfs_mount: dup dev");
+ mfsp = (struct mfsnode *)malloc(sizeof *mfsp, M_MFSNODE, M_WAITOK);
+ devvp->v_data = mfsp;
+ mfsp->mfs_baseoff = args.base;
+ mfsp->mfs_size = args.size;
+ mfsp->mfs_vnode = devvp;
+ mfsp->mfs_pid = p->p_pid;
+ mfsp->mfs_buflist = (struct buf *)0;
+ if (error = ffs_mountfs(devvp, mp, p)) {
+ mfsp->mfs_buflist = (struct buf *)-1;
+ vrele(devvp);
+ return (error);
+ }
+ ump = VFSTOUFS(mp);
+ fs = ump->um_fs;
+ (void) copyinstr(path, fs->fs_fsmnt, sizeof(fs->fs_fsmnt) - 1, &size);
+ bzero(fs->fs_fsmnt + size, sizeof(fs->fs_fsmnt) - size);
+ bcopy((caddr_t)fs->fs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname,
+ MNAMELEN);
+ (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ (void) mfs_statfs(mp, &mp->mnt_stat, p);
+ return (0);
+}
+
+int mfs_pri = PWAIT | PCATCH; /* XXX prob. temp */
+
+/*
+ * Used to grab the process and keep it in the kernel to service
+ * memory filesystem I/O requests.
+ *
+ * Loop servicing I/O requests.
+ * Copy the requested data into or out of the memory filesystem
+ * address space.
+ */
+/* ARGSUSED */
+int
+mfs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ register struct vnode *vp = VFSTOUFS(mp)->um_devvp;
+ register struct mfsnode *mfsp = VTOMFS(vp);
+ register struct buf *bp;
+ register caddr_t base;
+ int error = 0;
+
+ base = mfsp->mfs_baseoff;
+ while (mfsp->mfs_buflist != (struct buf *)(-1)) {
+ while (bp = mfsp->mfs_buflist) {
+ mfsp->mfs_buflist = bp->b_actf;
+ mfs_doio(bp, base);
+ wakeup((caddr_t)bp);
+ }
+ /*
+ * If a non-ignored signal is received, try to unmount.
+ * If that fails, clear the signal (it has been "processed"),
+ * otherwise we will loop here, as tsleep will always return
+ * EINTR/ERESTART.
+ */
+ if (error = tsleep((caddr_t)vp, mfs_pri, "mfsidl", 0))
+ if (dounmount(mp, 0, p) != 0)
+ CLRSIG(p, CURSIG(p));
+ }
+ return (error);
+}
+
+/*
+ * Get file system statistics.
+ */
+int
+mfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ int error;
+
+ error = ffs_statfs(mp, sbp, p);
+ sbp->f_type = MOUNT_MFS;
+ return (error);
+}
diff --git a/sys/ufs/mfs/mfs_vnops.c b/sys/ufs/mfs/mfs_vnops.c
new file mode 100644
index 0000000..c70657b
--- /dev/null
+++ b/sys/ufs/mfs/mfs_vnops.c
@@ -0,0 +1,434 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mfs_vnops.c 8.3 (Berkeley) 9/21/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/map.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <machine/vmparam.h>
+
+#include <ufs/mfs/mfsnode.h>
+#include <ufs/mfs/mfsiom.h>
+#include <ufs/mfs/mfs_extern.h>
+
+#if !defined(hp300) && !defined(i386) && !defined(mips) && !defined(sparc) && !defined(luna68k)
+static int mfsmap_want; /* 1 => need kernel I/O resources */
+struct map mfsmap[MFS_MAPSIZE];
+extern char mfsiobuf[];
+#endif
+
+/*
+ * mfs vnode operations.
+ */
+int (**mfs_vnodeop_p)();
+struct vnodeopv_entry_desc mfs_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, mfs_lookup }, /* lookup */
+ { &vop_create_desc, mfs_create }, /* create */
+ { &vop_mknod_desc, mfs_mknod }, /* mknod */
+ { &vop_open_desc, mfs_open }, /* open */
+ { &vop_close_desc, mfs_close }, /* close */
+ { &vop_access_desc, mfs_access }, /* access */
+ { &vop_getattr_desc, mfs_getattr }, /* getattr */
+ { &vop_setattr_desc, mfs_setattr }, /* setattr */
+ { &vop_read_desc, mfs_read }, /* read */
+ { &vop_write_desc, mfs_write }, /* write */
+ { &vop_ioctl_desc, mfs_ioctl }, /* ioctl */
+ { &vop_select_desc, mfs_select }, /* select */
+ { &vop_mmap_desc, mfs_mmap }, /* mmap */
+ { &vop_fsync_desc, spec_fsync }, /* fsync */
+ { &vop_seek_desc, mfs_seek }, /* seek */
+ { &vop_remove_desc, mfs_remove }, /* remove */
+ { &vop_link_desc, mfs_link }, /* link */
+ { &vop_rename_desc, mfs_rename }, /* rename */
+ { &vop_mkdir_desc, mfs_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, mfs_rmdir }, /* rmdir */
+ { &vop_symlink_desc, mfs_symlink }, /* symlink */
+ { &vop_readdir_desc, mfs_readdir }, /* readdir */
+ { &vop_readlink_desc, mfs_readlink }, /* readlink */
+ { &vop_abortop_desc, mfs_abortop }, /* abortop */
+ { &vop_inactive_desc, mfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, mfs_reclaim }, /* reclaim */
+ { &vop_lock_desc, mfs_lock }, /* lock */
+ { &vop_unlock_desc, mfs_unlock }, /* unlock */
+ { &vop_bmap_desc, mfs_bmap }, /* bmap */
+ { &vop_strategy_desc, mfs_strategy }, /* strategy */
+ { &vop_print_desc, mfs_print }, /* print */
+ { &vop_islocked_desc, mfs_islocked }, /* islocked */
+ { &vop_pathconf_desc, mfs_pathconf }, /* pathconf */
+ { &vop_advlock_desc, mfs_advlock }, /* advlock */
+ { &vop_blkatoff_desc, mfs_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, mfs_valloc }, /* valloc */
+ { &vop_vfree_desc, mfs_vfree }, /* vfree */
+ { &vop_truncate_desc, mfs_truncate }, /* truncate */
+ { &vop_update_desc, mfs_update }, /* update */
+ { &vop_bwrite_desc, mfs_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc mfs_vnodeop_opv_desc =
+ { &mfs_vnodeop_p, mfs_vnodeop_entries };
+
+/*
+ * Vnode Operations.
+ *
+ * Open called to allow memory filesystem to initialize and
+ * validate before actual IO. Record our process identifier
+ * so we can tell when we are doing I/O to ourself.
+ */
+/* ARGSUSED */
+int
+mfs_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ if (ap->a_vp->v_type != VBLK) {
+ panic("mfs_ioctl not VBLK");
+ /* NOTREACHED */
+ }
+ return (0);
+}
+
+/*
+ * Ioctl operation.
+ */
+/* ARGSUSED */
+int
+mfs_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (ENOTTY);
+}
+
+/*
+ * Pass I/O requests to the memory filesystem process.
+ */
+int
+mfs_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ register struct buf *bp = ap->a_bp;
+ register struct mfsnode *mfsp;
+ struct vnode *vp;
+ struct proc *p = curproc; /* XXX */
+
+ if (!vfinddev(bp->b_dev, VBLK, &vp) || vp->v_usecount == 0)
+ panic("mfs_strategy: bad dev");
+ mfsp = VTOMFS(vp);
+ /* check for mini-root access */
+ if (mfsp->mfs_pid == 0) {
+ caddr_t base;
+
+ base = mfsp->mfs_baseoff + (bp->b_blkno << DEV_BSHIFT);
+ if (bp->b_flags & B_READ)
+ bcopy(base, bp->b_data, bp->b_bcount);
+ else
+ bcopy(bp->b_data, base, bp->b_bcount);
+ biodone(bp);
+ } else if (mfsp->mfs_pid == p->p_pid) {
+ mfs_doio(bp, mfsp->mfs_baseoff);
+ } else {
+ bp->b_actf = mfsp->mfs_buflist;
+ mfsp->mfs_buflist = bp;
+ wakeup((caddr_t)vp);
+ }
+ return (0);
+}
+
+#if defined(vax) || defined(tahoe)
+/*
+ * Memory file system I/O.
+ *
+ * Essentially play ubasetup() and disk interrupt service routine by
+ * doing the copies to or from the memfs process. If doing physio
+ * (i.e. pagein), we must map the I/O through the kernel virtual
+ * address space.
+ */
+void
+mfs_doio(bp, base)
+ register struct buf *bp;
+ caddr_t base;
+{
+ register struct pte *pte, *ppte;
+ register caddr_t vaddr;
+ int off, npf, npf2, reg;
+ caddr_t kernaddr, offset;
+
+ /*
+ * For phys I/O, map the b_data into kernel virtual space using
+ * the Mfsiomap pte's.
+ */
+ if ((bp->b_flags & B_PHYS) == 0) {
+ kernaddr = bp->b_data;
+ } else {
+ if (bp->b_flags & (B_PAGET | B_UAREA | B_DIRTY))
+ panic("swap on memfs?");
+ off = (int)bp->b_data & PGOFSET;
+ npf = btoc(bp->b_bcount + off);
+ /*
+ * Get some mapping page table entries
+ */
+ while ((reg = rmalloc(mfsmap, (long)npf)) == 0) {
+ mfsmap_want++;
+ sleep((caddr_t)&mfsmap_want, PZERO-1);
+ }
+ reg--;
+ pte = vtopte(bp->b_proc, btop(bp->b_data));
+ /*
+ * Do vmaccess() but with the Mfsiomap page table.
+ */
+ ppte = &Mfsiomap[reg];
+ vaddr = &mfsiobuf[reg * NBPG];
+ kernaddr = vaddr + off;
+ for (npf2 = npf; npf2; npf2--) {
+ mapin(ppte, (u_int)vaddr, pte->pg_pfnum,
+ (int)(PG_V|PG_KW));
+#if defined(tahoe)
+ if ((bp->b_flags & B_READ) == 0)
+ mtpr(P1DC, vaddr);
+#endif
+ ppte++;
+ pte++;
+ vaddr += NBPG;
+ }
+ }
+ offset = base + (bp->b_blkno << DEV_BSHIFT);
+ if (bp->b_flags & B_READ)
+ bp->b_error = copyin(offset, kernaddr, bp->b_bcount);
+ else
+ bp->b_error = copyout(kernaddr, offset, bp->b_bcount);
+ if (bp->b_error)
+ bp->b_flags |= B_ERROR;
+ /*
+ * Release pte's used by physical I/O.
+ */
+ if (bp->b_flags & B_PHYS) {
+ rmfree(mfsmap, (long)npf, (long)++reg);
+ if (mfsmap_want) {
+ mfsmap_want = 0;
+ wakeup((caddr_t)&mfsmap_want);
+ }
+ }
+ biodone(bp);
+}
+#endif /* vax || tahoe */
+
+#if defined(hp300) || defined(i386) || defined(mips) || defined(sparc) || defined(luna68k)
+/*
+ * Memory file system I/O.
+ *
+ * Trivial on the HP since buffer has already been mapping into KVA space.
+ */
+void
+mfs_doio(bp, base)
+ register struct buf *bp;
+ caddr_t base;
+{
+
+ base += (bp->b_blkno << DEV_BSHIFT);
+ if (bp->b_flags & B_READ)
+ bp->b_error = copyin(base, bp->b_data, bp->b_bcount);
+ else
+ bp->b_error = copyout(bp->b_data, base, bp->b_bcount);
+ if (bp->b_error)
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+}
+#endif
+
+/*
+ * This is a noop, simply returning what one has been given.
+ */
+int
+mfs_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap;
+{
+
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = ap->a_vp;
+ if (ap->a_bnp != NULL)
+ *ap->a_bnp = ap->a_bn;
+ return (0);
+}
+
+/*
+ * Memory filesystem close routine
+ */
+/* ARGSUSED */
+int
+mfs_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct mfsnode *mfsp = VTOMFS(vp);
+ register struct buf *bp;
+ int error;
+
+ /*
+ * Finish any pending I/O requests.
+ */
+ while (bp = mfsp->mfs_buflist) {
+ mfsp->mfs_buflist = bp->b_actf;
+ mfs_doio(bp, mfsp->mfs_baseoff);
+ wakeup((caddr_t)bp);
+ }
+ /*
+ * On last close of a memory filesystem
+ * we must invalidate any in core blocks, so that
+ * we can, free up its vnode.
+ */
+ if (error = vinvalbuf(vp, 1, ap->a_cred, ap->a_p, 0, 0))
+ return (error);
+ /*
+ * There should be no way to have any more uses of this
+ * vnode, so if we find any other uses, it is a panic.
+ */
+ if (vp->v_usecount > 1)
+ printf("mfs_close: ref count %d > 1\n", vp->v_usecount);
+ if (vp->v_usecount > 1 || mfsp->mfs_buflist)
+ panic("mfs_close");
+ /*
+ * Send a request to the filesystem server to exit.
+ */
+ mfsp->mfs_buflist = (struct buf *)(-1);
+ wakeup((caddr_t)vp);
+ return (0);
+}
+
+/*
+ * Memory filesystem inactive routine
+ */
+/* ARGSUSED */
+int
+mfs_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct mfsnode *mfsp = VTOMFS(ap->a_vp);
+
+ if (mfsp->mfs_buflist && mfsp->mfs_buflist != (struct buf *)(-1))
+ panic("mfs_inactive: not inactive (mfs_buflist %x)",
+ mfsp->mfs_buflist);
+ return (0);
+}
+
+/*
+ * Reclaim a memory filesystem devvp so that it can be reused.
+ */
+int
+mfs_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ FREE(ap->a_vp->v_data, M_MFSNODE);
+ ap->a_vp->v_data = NULL;
+ return (0);
+}
+
+/*
+ * Print out the contents of an mfsnode.
+ */
+int
+mfs_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct mfsnode *mfsp = VTOMFS(ap->a_vp);
+
+ printf("tag VT_MFS, pid %d, base %d, size %d\n", mfsp->mfs_pid,
+ mfsp->mfs_baseoff, mfsp->mfs_size);
+ return (0);
+}
+
+/*
+ * Block device bad operation
+ */
+int
+mfs_badop()
+{
+
+ panic("mfs_badop called\n");
+ /* NOTREACHED */
+}
+
+/*
+ * Memory based filesystem initialization.
+ */
+int
+mfs_init()
+{
+
+#if !defined(hp300) && !defined(i386) && !defined(mips) && !defined(sparc) && !defined(luna68k)
+ rminit(mfsmap, (long)MFS_MAPREG, (long)1, "mfs mapreg", MFS_MAPSIZE);
+#endif
+ return (0);
+}
diff --git a/sys/ufs/mfs/mfsiom.h b/sys/ufs/mfs/mfsiom.h
new file mode 100644
index 0000000..98aca85
--- /dev/null
+++ b/sys/ufs/mfs/mfsiom.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mfsiom.h 8.1 (Berkeley) 6/11/93
+ */
+
+#define MFS_MAPREG (MAXPHYS/NBPG + 2) /* Kernel mapping pte's */
+#define MFS_MAPSIZE 10 /* Size of alloc map for pte's */
diff --git a/sys/ufs/mfs/mfsnode.h b/sys/ufs/mfs/mfsnode.h
new file mode 100644
index 0000000..4480ab0
--- /dev/null
+++ b/sys/ufs/mfs/mfsnode.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mfsnode.h 8.2 (Berkeley) 8/11/93
+ */
+
+/*
+ * This structure defines the control data for the memory based file system.
+ */
+
+struct mfsnode {
+ struct vnode *mfs_vnode; /* vnode associated with this mfsnode */
+ caddr_t mfs_baseoff; /* base of file system in memory */
+ long mfs_size; /* size of memory file system */
+ pid_t mfs_pid; /* supporting process pid */
+ struct buf *mfs_buflist; /* list of I/O requests */
+ long mfs_spare[4];
+};
+
+/*
+ * Convert between mfsnode pointers and vnode pointers
+ */
+#define VTOMFS(vp) ((struct mfsnode *)(vp)->v_data)
+#define MFSTOV(mfsp) ((mfsp)->mfs_vnode)
+
+/* Prototypes for MFS operations on vnodes. */
+#define mfs_lookup ((int (*) __P((struct vop_lookup_args *)))mfs_badop)
+#define mfs_create ((int (*) __P((struct vop_create_args *)))mfs_badop)
+#define mfs_mknod ((int (*) __P((struct vop_mknod_args *)))mfs_badop)
+#define mfs_access ((int (*) __P((struct vop_access_args *)))mfs_badop)
+#define mfs_getattr ((int (*) __P((struct vop_getattr_args *)))mfs_badop)
+#define mfs_setattr ((int (*) __P((struct vop_setattr_args *)))mfs_badop)
+#define mfs_read ((int (*) __P((struct vop_read_args *)))mfs_badop)
+#define mfs_write ((int (*) __P((struct vop_write_args *)))mfs_badop)
+#define mfs_select ((int (*) __P((struct vop_select_args *)))mfs_badop)
+#define mfs_mmap ((int (*) __P((struct vop_mmap_args *)))mfs_badop)
+#define mfs_seek ((int (*) __P((struct vop_seek_args *)))mfs_badop)
+#define mfs_remove ((int (*) __P((struct vop_remove_args *)))mfs_badop)
+#define mfs_link ((int (*) __P((struct vop_link_args *)))mfs_badop)
+#define mfs_rename ((int (*) __P((struct vop_rename_args *)))mfs_badop)
+#define mfs_mkdir ((int (*) __P((struct vop_mkdir_args *)))mfs_badop)
+#define mfs_rmdir ((int (*) __P((struct vop_rmdir_args *)))mfs_badop)
+#define mfs_symlink ((int (*) __P((struct vop_symlink_args *)))mfs_badop)
+#define mfs_readdir ((int (*) __P((struct vop_readdir_args *)))mfs_badop)
+#define mfs_readlink ((int (*) __P((struct vop_readlink_args *)))mfs_badop)
+#define mfs_abortop ((int (*) __P((struct vop_abortop_args *)))mfs_badop)
+#define mfs_lock ((int (*) __P((struct vop_lock_args *)))nullop)
+#define mfs_unlock ((int (*) __P((struct vop_unlock_args *)))nullop)
+#define mfs_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+#define mfs_pathconf ((int (*) __P((struct vop_pathconf_args *)))mfs_badop)
+#define mfs_advlock ((int (*) __P((struct vop_advlock_args *)))mfs_badop)
+#define mfs_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))mfs_badop)
+#define mfs_valloc ((int (*) __P((struct vop_valloc_args *)))mfs_badop)
+#define mfs_vfree ((int (*) __P((struct vop_vfree_args *)))mfs_badop)
+#define mfs_truncate ((int (*) __P((struct vop_truncate_args *)))mfs_badop)
+#define mfs_update ((int (*) __P((struct vop_update_args *)))mfs_badop)
+#define mfs_bwrite ((int (*) __P((struct vop_bwrite_args *)))vn_bwrite)
diff --git a/sys/ufs/ufs/dinode.h b/sys/ufs/ufs/dinode.h
new file mode 100644
index 0000000..5b9915d
--- /dev/null
+++ b/sys/ufs/ufs/dinode.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1982, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dinode.h 8.3 (Berkeley) 1/21/94
+ */
+
+/*
+ * The root inode is the root of the file system. Inode 0 can't be used for
+ * normal purposes and historically bad blocks were linked to inode 1, thus
+ * the root inode is 2. (Inode 1 is no longer used for this purpose, however
+ * numerous dump tapes make this assumption, so we are stuck with it).
+ */
+#define ROOTINO ((ino_t)2)
+
+/*
+ * A dinode contains all the meta-data associated with a UFS file.
+ * This structure defines the on-disk format of a dinode.
+ */
+
+#define NDADDR 12 /* Direct addresses in inode. */
+#define NIADDR 3 /* Indirect addresses in inode. */
+
+struct dinode {
+ u_short di_mode; /* 0: IFMT and permissions. */
+ short di_nlink; /* 2: File link count. */
+ union {
+ u_short oldids[2]; /* 4: Ffs: old user and group ids. */
+ ino_t inumber; /* 4: Lfs: inode number. */
+ } di_u;
+ u_quad_t di_size; /* 8: File byte count. */
+ struct timespec di_atime; /* 16: Last access time. */
+ struct timespec di_mtime; /* 24: Last modified time. */
+ struct timespec di_ctime; /* 32: Last inode change time. */
+ daddr_t di_db[NDADDR]; /* 40: Direct disk blocks. */
+ daddr_t di_ib[NIADDR]; /* 88: Indirect disk blocks. */
+ u_long di_flags; /* 100: Status flags (chflags). */
+ long di_blocks; /* 104: Blocks actually held. */
+ long di_gen; /* 108: Generation number. */
+ u_long di_uid; /* 112: File owner. */
+ u_long di_gid; /* 116: File group. */
+ long di_spare[2]; /* 120: Reserved; currently unused */
+};
+
+/*
+ * The di_db fields may be overlaid with other information for
+ * file types that do not have associated disk storage. Block
+ * and character devices overlay the first data block with their
+ * dev_t value. Short symbolic links place their path in the
+ * di_db area.
+ */
+#define di_inumber di_u.inumber
+#define di_ogid di_u.oldids[1]
+#define di_ouid di_u.oldids[0]
+#define di_rdev di_db[0]
+#define di_shortlink di_db
+#define MAXSYMLINKLEN ((NDADDR + NIADDR) * sizeof(daddr_t))
+
+/* File modes. */
+#define IEXEC 0000100 /* Executable. */
+#define IWRITE 0000200 /* Writeable. */
+#define IREAD 0000400 /* Readable. */
+#define ISVTX 0001000 /* Sticky bit. */
+#define ISGID 0002000 /* Set-gid. */
+#define ISUID 0004000 /* Set-uid. */
+
+/* File types. */
+#define IFMT 0170000 /* Mask of file type. */
+#define IFIFO 0010000 /* Named pipe (fifo). */
+#define IFCHR 0020000 /* Character device. */
+#define IFDIR 0040000 /* Directory file. */
+#define IFBLK 0060000 /* Block device. */
+#define IFREG 0100000 /* Regular file. */
+#define IFLNK 0120000 /* Symbolic link. */
+#define IFSOCK 0140000 /* UNIX domain socket. */
diff --git a/sys/ufs/ufs/dir.h b/sys/ufs/ufs/dir.h
new file mode 100644
index 0000000..c51bd1c
--- /dev/null
+++ b/sys/ufs/ufs/dir.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dir.h 8.2 (Berkeley) 1/21/94
+ */
+
+#ifndef _DIR_H_
+#define _DIR_H_
+
+/*
+ * A directory consists of some number of blocks of DIRBLKSIZ
+ * bytes, where DIRBLKSIZ is chosen such that it can be transferred
+ * to disk in a single atomic operation (e.g. 512 bytes on most machines).
+ *
+ * Each DIRBLKSIZ byte block contains some number of directory entry
+ * structures, which are of variable length. Each directory entry has
+ * a struct direct at the front of it, containing its inode number,
+ * the length of the entry, and the length of the name contained in
+ * the entry. These are followed by the name padded to a 4 byte boundary
+ * with null bytes. All names are guaranteed null terminated.
+ * The maximum length of a name in a directory is MAXNAMLEN.
+ *
+ * The macro DIRSIZ(fmt, dp) gives the amount of space required to represent
+ * a directory entry. Free space in a directory is represented by
+ * entries which have dp->d_reclen > DIRSIZ(fmt, dp). All DIRBLKSIZ bytes
+ * in a directory block are claimed by the directory entries. This
+ * usually results in the last entry in a directory having a large
+ * dp->d_reclen. When entries are deleted from a directory, the
+ * space is returned to the previous entry in the same directory
+ * block by increasing its dp->d_reclen. If the first entry of
+ * a directory block is free, then its dp->d_ino is set to 0.
+ * Entries other than the first in a directory do not normally have
+ * dp->d_ino set to 0.
+ */
+#define DIRBLKSIZ DEV_BSIZE
+#define MAXNAMLEN 255
+
+struct direct {
+ u_long d_ino; /* inode number of entry */
+ u_short d_reclen; /* length of this record */
+ u_char d_type; /* file type, see below */
+ u_char d_namlen; /* length of string in d_name */
+ char d_name[MAXNAMLEN + 1]; /* name with length <= MAXNAMLEN */
+};
+
+/*
+ * File types
+ */
+#define DT_UNKNOWN 0
+#define DT_FIFO 1
+#define DT_CHR 2
+#define DT_DIR 4
+#define DT_BLK 6
+#define DT_REG 8
+#define DT_LNK 10
+#define DT_SOCK 12
+
+/*
+ * Convert between stat structure types and directory types.
+ */
+#define IFTODT(mode) (((mode) & 0170000) >> 12)
+#define DTTOIF(dirtype) ((dirtype) << 12)
+
+/*
+ * The DIRSIZ macro gives the minimum record length which will hold
+ * the directory entry. This requires the amount of space in struct direct
+ * without the d_name field, plus enough space for the name with a terminating
+ * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
+ */
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+#define DIRSIZ(oldfmt, dp) \
+ ((oldfmt) ? \
+ ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_type+1 + 3) &~ 3)) : \
+ ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3)))
+#else
+#define DIRSIZ(oldfmt, dp) \
+ ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
+#endif
+#define OLDDIRFMT 1
+#define NEWDIRFMT 0
+
+/*
+ * Template for manipulating directories.
+ * Should use struct direct's, but the name field
+ * is MAXNAMLEN - 1, and this just won't do.
+ */
+struct dirtemplate {
+ u_long dot_ino;
+ short dot_reclen;
+ u_char dot_type;
+ u_char dot_namlen;
+ char dot_name[4]; /* must be multiple of 4 */
+ u_long dotdot_ino;
+ short dotdot_reclen;
+ u_char dotdot_type;
+ u_char dotdot_namlen;
+ char dotdot_name[4]; /* ditto */
+};
+
+/*
+ * This is the old format of directories, sanz type element.
+ */
+struct odirtemplate {
+ u_long dot_ino;
+ short dot_reclen;
+ u_short dot_namlen;
+ char dot_name[4]; /* must be multiple of 4 */
+ u_long dotdot_ino;
+ short dotdot_reclen;
+ u_short dotdot_namlen;
+ char dotdot_name[4]; /* ditto */
+};
+#endif /* !_DIR_H_ */
diff --git a/sys/ufs/ufs/inode.h b/sys/ufs/ufs/inode.h
new file mode 100644
index 0000000..df15596
--- /dev/null
+++ b/sys/ufs/ufs/inode.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 1982, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)inode.h 8.4 (Berkeley) 1/21/94
+ */
+
+#include <ufs/ufs/dinode.h>
+
+/*
+ * Theoretically, directories can be more than 2Gb in length, however, in
+ * practice this seems unlikely. So, we define the type doff_t as a long
+ * to keep down the cost of doing lookup on a 32-bit machine. If you are
+ * porting to a 64-bit architecture, you should make doff_t the same as off_t.
+ */
+#define doff_t long
+
+/*
+ * The inode is used to describe each active (or recently active)
+ * file in the UFS filesystem. It is composed of two types of
+ * information. The first part is the information that is needed
+ * only while the file is active (such as the identity of the file
+ * and linkage to speed its lookup). The second part is the
+ * permannent meta-data associated with the file which is read
+ * in from the permanent dinode from long term storage when the
+ * file becomes active, and is put back when the file is no longer
+ * being used.
+ */
+struct inode {
+ struct inode *i_next; /* Hash chain forward. */
+ struct inode **i_prev; /* Hash chain back. */
+ struct vnode *i_vnode; /* Vnode associated with this inode. */
+ struct vnode *i_devvp; /* Vnode for block I/O. */
+ u_long i_flag; /* I* flags. */
+ dev_t i_dev; /* Device associated with the inode. */
+ ino_t i_number; /* The identity of the inode. */
+ union { /* Associated filesystem. */
+ struct fs *fs; /* FFS */
+ struct lfs *lfs; /* LFS */
+ } inode_u;
+#define i_fs inode_u.fs
+#define i_lfs inode_u.lfs
+ struct dquot *i_dquot[MAXQUOTAS]; /* Dquot structures. */
+ u_quad_t i_modrev; /* Revision level for lease. */
+ struct lockf *i_lockf; /* Head of byte-level lock list. */
+ pid_t i_lockholder; /* DEBUG: holder of inode lock. */
+ pid_t i_lockwaiter; /* DEBUG: latest blocked for inode lock. */
+ /*
+ * Side effects; used during directory lookup.
+ */
+ long i_count; /* Size of free slot in directory. */
+ doff_t i_endoff; /* End of useful stuff in directory. */
+ doff_t i_diroff; /* Offset in dir, where we found last entry. */
+ doff_t i_offset; /* Offset of free space in directory. */
+ ino_t i_ino; /* Inode number of found directory. */
+ u_long i_reclen; /* Size of found directory entry. */
+ long i_spare[11]; /* Spares to round up to 128 bytes. */
+ /*
+ * The on-disk dinode itself.
+ */
+ struct dinode i_din; /* 128 bytes of the on-disk dinode. */
+};
+
+#define i_atime i_din.di_atime
+#define i_blocks i_din.di_blocks
+#define i_ctime i_din.di_ctime
+#define i_db i_din.di_db
+#define i_flags i_din.di_flags
+#define i_gen i_din.di_gen
+#define i_gid i_din.di_gid
+#define i_ib i_din.di_ib
+#define i_mode i_din.di_mode
+#define i_mtime i_din.di_mtime
+#define i_nlink i_din.di_nlink
+#define i_rdev i_din.di_rdev
+#define i_shortlink i_din.di_shortlink
+#define i_size i_din.di_size
+#define i_uid i_din.di_uid
+
+/* These flags are kept in i_flag. */
+#define IN_ACCESS 0x0001 /* Access time update request. */
+#define IN_CHANGE 0x0002 /* Inode change time update request. */
+#define IN_EXLOCK 0x0004 /* File has exclusive lock. */
+#define IN_LOCKED 0x0008 /* Inode lock. */
+#define IN_LWAIT 0x0010 /* Process waiting on file lock. */
+#define IN_MODIFIED 0x0020 /* Inode has been modified. */
+#define IN_RENAME 0x0040 /* Inode is being renamed. */
+#define IN_SHLOCK 0x0080 /* File has shared lock. */
+#define IN_UPDATE 0x0100 /* Modification time update request. */
+#define IN_WANTED 0x0200 /* Inode is wanted by a process. */
+
+#ifdef KERNEL
+/*
+ * Structure used to pass around logical block paths generated by
+ * ufs_getlbns and used by truncate and bmap code.
+ */
+struct indir {
+ daddr_t in_lbn; /* Logical block number. */
+ int in_off; /* Offset in buffer. */
+ int in_exists; /* Flag if the block exists. */
+};
+
+/* Convert between inode pointers and vnode pointers. */
+#define VTOI(vp) ((struct inode *)(vp)->v_data)
+#define ITOV(ip) ((ip)->i_vnode)
+
+#define ITIMES(ip, t1, t2) { \
+ if ((ip)->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) { \
+ (ip)->i_flag |= IN_MODIFIED; \
+ if ((ip)->i_flag & IN_ACCESS) \
+ (ip)->i_atime.ts_sec = (t1)->tv_sec; \
+ if ((ip)->i_flag & IN_UPDATE) { \
+ (ip)->i_mtime.ts_sec = (t2)->tv_sec; \
+ (ip)->i_modrev++; \
+ } \
+ if ((ip)->i_flag & IN_CHANGE) \
+ (ip)->i_ctime.ts_sec = time.tv_sec; \
+ (ip)->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE); \
+ } \
+}
+
+/* This overlays the fid structure (see mount.h). */
+struct ufid {
+ u_short ufid_len; /* Length of structure. */
+ u_short ufid_pad; /* Force long alignment. */
+ ino_t ufid_ino; /* File number (ino). */
+ long ufid_gen; /* Generation number. */
+};
+#endif /* KERNEL */
diff --git a/sys/ufs/ufs/lockf.h b/sys/ufs/ufs/lockf.h
new file mode 100644
index 0000000..0ec61db
--- /dev/null
+++ b/sys/ufs/ufs/lockf.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Scooter Morris at Genentech Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lockf.h 8.1 (Berkeley) 6/11/93
+ */
+
+/*
+ * The lockf structure is a kernel structure which contains the information
+ * associated with a byte range lock. The lockf structures are linked into
+ * the inode structure. Locks are sorted by the starting byte of the lock for
+ * efficiency.
+ */
+struct lockf {
+ short lf_flags; /* Lock semantics: F_POSIX, F_FLOCK, F_WAIT */
+ short lf_type; /* Lock type: F_RDLCK, F_WRLCK */
+ off_t lf_start; /* The byte # of the start of the lock */
+ off_t lf_end; /* The byte # of the end of the lock (-1=EOF)*/
+ caddr_t lf_id; /* The id of the resource holding the lock */
+ struct inode *lf_inode; /* Back pointer to the inode */
+ struct lockf *lf_next; /* A pointer to the next lock on this inode */
+ struct lockf *lf_block; /* The list of blocked locks */
+};
+
+/* Maximum length of sleep chains to traverse to try and detect deadlock. */
+#define MAXDEPTH 50
+
+__BEGIN_DECLS
+void lf_addblock __P((struct lockf *, struct lockf *));
+int lf_clearlock __P((struct lockf *));
+int lf_findoverlap __P((struct lockf *,
+ struct lockf *, int, struct lockf ***, struct lockf **));
+struct lockf *
+ lf_getblock __P((struct lockf *));
+int lf_getlock __P((struct lockf *, struct flock *));
+int lf_setlock __P((struct lockf *));
+void lf_split __P((struct lockf *, struct lockf *));
+void lf_wakelock __P((struct lockf *));
+__END_DECLS
+
+#ifdef LOCKF_DEBUG
+extern int lockf_debug;
+
+__BEGIN_DECLS
+void lf_print __P((char *, struct lockf *));
+void lf_printlist __P((char *, struct lockf *));
+__END_DECLS
+#endif
diff --git a/sys/ufs/ufs/quota.h b/sys/ufs/ufs/quota.h
new file mode 100644
index 0000000..11efb40
--- /dev/null
+++ b/sys/ufs/ufs/quota.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)quota.h 8.1 (Berkeley) 6/11/93
+ */
+
+#ifndef _QUOTA_
+#define _QUOTA_
+
+/*
+ * Definitions for disk quotas imposed on the average user
+ * (big brother finally hits UNIX).
+ *
+ * The following constants define the amount of time given a user before the
+ * soft limits are treated as hard limits (usually resulting in an allocation
+ * failure). The timer is started when the user crosses their soft limit, it
+ * is reset when they go below their soft limit.
+ */
+#define MAX_IQ_TIME (7*24*60*60) /* 1 week */
+#define MAX_DQ_TIME (7*24*60*60) /* 1 week */
+
+/*
+ * The following constants define the usage of the quota file array in the
+ * ufsmount structure and dquot array in the inode structure. The semantics
+ * of the elements of these arrays are defined in the routine getinoquota;
+ * the remainder of the quota code treats them generically and need not be
+ * inspected when changing the size of the array.
+ */
+#define MAXQUOTAS 2
+#define USRQUOTA 0 /* element used for user quotas */
+#define GRPQUOTA 1 /* element used for group quotas */
+
+/*
+ * Definitions for the default names of the quotas files.
+ */
+#define INITQFNAMES { \
+ "user", /* USRQUOTA */ \
+ "group", /* GRPQUOTA */ \
+ "undefined", \
+};
+#define QUOTAFILENAME "quota"
+#define QUOTAGROUP "operator"
+
+/*
+ * Command definitions for the 'quotactl' system call. The commands are
+ * broken into a main command defined below and a subcommand that is used
+ * to convey the type of quota that is being manipulated (see above).
+ */
+#define SUBCMDMASK 0x00ff
+#define SUBCMDSHIFT 8
+#define QCMD(cmd, type) (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK))
+
+#define Q_QUOTAON 0x0100 /* enable quotas */
+#define Q_QUOTAOFF 0x0200 /* disable quotas */
+#define Q_GETQUOTA 0x0300 /* get limits and usage */
+#define Q_SETQUOTA 0x0400 /* set limits and usage */
+#define Q_SETUSE 0x0500 /* set usage */
+#define Q_SYNC 0x0600 /* sync disk copy of a filesystems quotas */
+
+/*
+ * The following structure defines the format of the disk quota file
+ * (as it appears on disk) - the file is an array of these structures
+ * indexed by user or group number. The setquota system call establishes
+ * the vnode for each quota file (a pointer is retained in the ufsmount
+ * structure).
+ */
+struct dqblk {
+ u_long dqb_bhardlimit; /* absolute limit on disk blks alloc */
+ u_long dqb_bsoftlimit; /* preferred limit on disk blks */
+ u_long dqb_curblocks; /* current block count */
+ u_long dqb_ihardlimit; /* maximum # allocated inodes + 1 */
+ u_long dqb_isoftlimit; /* preferred inode limit */
+ u_long dqb_curinodes; /* current # allocated inodes */
+ time_t dqb_btime; /* time limit for excessive disk use */
+ time_t dqb_itime; /* time limit for excessive files */
+};
+
+/*
+ * The following structure records disk usage for a user or group on a
+ * filesystem. There is one allocated for each quota that exists on any
+ * filesystem for the current user or group. A cache is kept of recently
+ * used entries.
+ */
+struct dquot {
+ struct dquot *dq_forw, **dq_back; /* hash list */
+ struct dquot *dq_freef, **dq_freeb; /* free list */
+ short dq_flags; /* flags, see below */
+ short dq_cnt; /* count of active references */
+ short dq_spare; /* unused spare padding */
+ short dq_type; /* quota type of this dquot */
+ u_long dq_id; /* identifier this applies to */
+ struct ufsmount *dq_ump; /* filesystem that this is taken from */
+ struct dqblk dq_dqb; /* actual usage & quotas */
+};
+/*
+ * Flag values.
+ */
+#define DQ_LOCK 0x01 /* this quota locked (no MODS) */
+#define DQ_WANT 0x02 /* wakeup on unlock */
+#define DQ_MOD 0x04 /* this quota modified since read */
+#define DQ_FAKE 0x08 /* no limits here, just usage */
+#define DQ_BLKS 0x10 /* has been warned about blk limit */
+#define DQ_INODS 0x20 /* has been warned about inode limit */
+/*
+ * Shorthand notation.
+ */
+#define dq_bhardlimit dq_dqb.dqb_bhardlimit
+#define dq_bsoftlimit dq_dqb.dqb_bsoftlimit
+#define dq_curblocks dq_dqb.dqb_curblocks
+#define dq_ihardlimit dq_dqb.dqb_ihardlimit
+#define dq_isoftlimit dq_dqb.dqb_isoftlimit
+#define dq_curinodes dq_dqb.dqb_curinodes
+#define dq_btime dq_dqb.dqb_btime
+#define dq_itime dq_dqb.dqb_itime
+
+/*
+ * If the system has never checked for a quota for this file, then it is set
+ * to NODQUOT. Once a write attempt is made the inode pointer is set to
+ * reference a dquot structure.
+ */
+#define NODQUOT ((struct dquot *) 0)
+
+/*
+ * Flags to chkdq() and chkiq()
+ */
+#define FORCE 0x01 /* force usage changes independent of limits */
+#define CHOWN 0x02 /* (advisory) change initiated by chown */
+
+/*
+ * Macros to avoid subroutine calls to trivial functions.
+ */
+#ifdef DIAGNOSTIC
+#define DQREF(dq) dqref(dq)
+#else
+#define DQREF(dq) (dq)->dq_cnt++
+#endif
+
+#include <sys/cdefs.h>
+
+struct dquot;
+struct inode;
+struct mount;
+struct proc;
+struct ucred;
+struct ufsmount;
+struct vnode;
+__BEGIN_DECLS
+int chkdq __P((struct inode *, long, struct ucred *, int));
+int chkdqchg __P((struct inode *, long, struct ucred *, int));
+int chkiq __P((struct inode *, long, struct ucred *, int));
+int chkiqchg __P((struct inode *, long, struct ucred *, int));
+void dqflush __P((struct vnode *));
+int dqget __P((struct vnode *,
+ u_long, struct ufsmount *, int, struct dquot **));
+void dqinit __P((void));
+void dqref __P((struct dquot *));
+void dqrele __P((struct vnode *, struct dquot *));
+int dqsync __P((struct vnode *, struct dquot *));
+int getinoquota __P((struct inode *));
+int getquota __P((struct mount *, u_long, int, caddr_t));
+int qsync __P((struct mount *mp));
+int quotaoff __P((struct proc *, struct mount *, int));
+int quotaon __P((struct proc *, struct mount *, int, caddr_t));
+int setquota __P((struct mount *, u_long, int, caddr_t));
+int setuse __P((struct mount *, u_long, int, caddr_t));
+int ufs_quotactl __P((struct mount *, int, uid_t, caddr_t, struct proc *));
+__END_DECLS
+
+#ifdef DIAGNOSTIC
+__BEGIN_DECLS
+void chkdquot __P((struct inode *));
+__END_DECLS
+#endif
+
+#endif /* _QUOTA_ */
diff --git a/sys/ufs/ufs/ufs_bmap.c b/sys/ufs/ufs/ufs_bmap.c
new file mode 100644
index 0000000..a424d31
--- /dev/null
+++ b/sys/ufs/ufs/ufs_bmap.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_bmap.c 8.6 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/resourcevar.h>
+#include <sys/trace.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+/*
+ * Bmap converts a the logical block number of a file to its physical block
+ * number on the disk. The conversion is done by using the logical block
+ * number to index into the array of block pointers described by the dinode.
+ */
+int
+ufs_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap;
+{
+ /*
+ * Check for underlying vnode requests and ensure that logical
+ * to physical mapping is requested.
+ */
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = VTOI(ap->a_vp)->i_devvp;
+ if (ap->a_bnp == NULL)
+ return (0);
+
+ return (ufs_bmaparray(ap->a_vp, ap->a_bn, ap->a_bnp, NULL, NULL,
+ ap->a_runp));
+}
+
+/*
+ * Indirect blocks are now on the vnode for the file. They are given negative
+ * logical block numbers. Indirect blocks are addressed by the negative
+ * address of the first data block to which they point. Double indirect blocks
+ * are addressed by one less than the address of the first indirect block to
+ * which they point. Triple indirect blocks are addressed by one less than
+ * the address of the first double indirect block to which they point.
+ *
+ * ufs_bmaparray does the bmap conversion, and if requested returns the
+ * array of logical blocks which must be traversed to get to a block.
+ * Each entry contains the offset into that block that gets you to the
+ * next block and the disk address of the block (if it is assigned).
+ */
+
+int
+ufs_bmaparray(vp, bn, bnp, ap, nump, runp)
+ struct vnode *vp;
+ register daddr_t bn;
+ daddr_t *bnp;
+ struct indir *ap;
+ int *nump;
+ int *runp;
+{
+ register struct inode *ip;
+ struct buf *bp;
+ struct ufsmount *ump;
+ struct mount *mp;
+ struct vnode *devvp;
+ struct indir a[NIADDR], *xap;
+ daddr_t daddr;
+ long metalbn;
+ int error, maxrun = 0, num;
+
+ ip = VTOI(vp);
+ mp = vp->v_mount;
+ ump = VFSTOUFS(mp);
+#ifdef DIAGNOSTIC
+ if (ap != NULL && nump == NULL || ap == NULL && nump != NULL)
+ panic("ufs_bmaparray: invalid arguments");
+#endif
+
+ if (runp) {
+ /*
+ * XXX
+ * If MAXBSIZE is the largest transfer the disks can handle,
+ * we probably want maxrun to be 1 block less so that we
+ * don't create a block larger than the device can handle.
+ */
+ *runp = 0;
+ maxrun = MAXBSIZE / mp->mnt_stat.f_iosize - 1;
+ }
+
+ xap = ap == NULL ? a : ap;
+ if (!nump)
+ nump = &num;
+ if (error = ufs_getlbns(vp, bn, xap, nump))
+ return (error);
+
+ num = *nump;
+ if (num == 0) {
+ *bnp = blkptrtodb(ump, ip->i_db[bn]);
+ if (*bnp == 0)
+ *bnp = -1;
+ else if (runp)
+ for (++bn; bn < NDADDR && *runp < maxrun &&
+ is_sequential(ump, ip->i_db[bn - 1], ip->i_db[bn]);
+ ++bn, ++*runp);
+ return (0);
+ }
+
+
+ /* Get disk address out of indirect block array */
+ daddr = ip->i_ib[xap->in_off];
+
+ devvp = VFSTOUFS(vp->v_mount)->um_devvp;
+ for (bp = NULL, ++xap; --num; ++xap) {
+ /*
+ * Exit the loop if there is no disk address assigned yet and
+ * the indirect block isn't in the cache, or if we were
+ * looking for an indirect block and we've found it.
+ */
+
+ metalbn = xap->in_lbn;
+ if (daddr == 0 && !incore(vp, metalbn) || metalbn == bn)
+ break;
+ /*
+ * If we get here, we've either got the block in the cache
+ * or we have a disk address for it, go fetch it.
+ */
+ if (bp)
+ brelse(bp);
+
+ xap->in_exists = 1;
+ bp = getblk(vp, metalbn, mp->mnt_stat.f_iosize, 0, 0);
+ if (bp->b_flags & (B_DONE | B_DELWRI)) {
+ trace(TR_BREADHIT, pack(vp, size), metalbn);
+ }
+#ifdef DIAGNOSTIC
+ else if (!daddr)
+ panic("ufs_bmaparry: indirect block not in cache");
+#endif
+ else {
+ trace(TR_BREADMISS, pack(vp, size), metalbn);
+ bp->b_blkno = blkptrtodb(ump, daddr);
+ bp->b_flags |= B_READ;
+ VOP_STRATEGY(bp);
+ curproc->p_stats->p_ru.ru_inblock++; /* XXX */
+ if (error = biowait(bp)) {
+ brelse(bp);
+ return (error);
+ }
+ }
+
+ daddr = ((daddr_t *)bp->b_data)[xap->in_off];
+ if (num == 1 && daddr && runp)
+ for (bn = xap->in_off + 1;
+ bn < MNINDIR(ump) && *runp < maxrun &&
+ is_sequential(ump, ((daddr_t *)bp->b_data)[bn - 1],
+ ((daddr_t *)bp->b_data)[bn]);
+ ++bn, ++*runp);
+ }
+ if (bp)
+ brelse(bp);
+
+ daddr = blkptrtodb(ump, daddr);
+ *bnp = daddr == 0 ? -1 : daddr;
+ return (0);
+}
+
+/*
+ * Create an array of logical block number/offset pairs which represent the
+ * path of indirect blocks required to access a data block. The first "pair"
+ * contains the logical block number of the appropriate single, double or
+ * triple indirect block and the offset into the inode indirect block array.
+ * Note, the logical block number of the inode single/double/triple indirect
+ * block appears twice in the array, once with the offset into the i_ib and
+ * once with the offset into the page itself.
+ */
+int
+ufs_getlbns(vp, bn, ap, nump)
+ struct vnode *vp;
+ register daddr_t bn;
+ struct indir *ap;
+ int *nump;
+{
+ long metalbn, realbn;
+ struct ufsmount *ump;
+ int blockcnt, i, numlevels, off;
+
+ ump = VFSTOUFS(vp->v_mount);
+ if (nump)
+ *nump = 0;
+ numlevels = 0;
+ realbn = bn;
+ if ((long)bn < 0)
+ bn = -(long)bn;
+
+ /* The first NDADDR blocks are direct blocks. */
+ if (bn < NDADDR)
+ return (0);
+
+ /*
+ * Determine the number of levels of indirection. After this loop
+ * is done, blockcnt indicates the number of data blocks possible
+ * at the given level of indirection, and NIADDR - i is the number
+ * of levels of indirection needed to locate the requested block.
+ */
+ for (blockcnt = 1, i = NIADDR, bn -= NDADDR;; i--, bn -= blockcnt) {
+ if (i == 0)
+ return (EFBIG);
+ blockcnt *= MNINDIR(ump);
+ if (bn < blockcnt)
+ break;
+ }
+
+ /* Calculate the address of the first meta-block. */
+ if (realbn >= 0)
+ metalbn = -(realbn - bn + NIADDR - i);
+ else
+ metalbn = -(-realbn - bn + NIADDR - i);
+
+ /*
+ * At each iteration, off is the offset into the bap array which is
+ * an array of disk addresses at the current level of indirection.
+ * The logical block number and the offset in that block are stored
+ * into the argument array.
+ */
+ ap->in_lbn = metalbn;
+ ap->in_off = off = NIADDR - i;
+ ap->in_exists = 0;
+ ap++;
+ for (++numlevels; i <= NIADDR; i++) {
+ /* If searching for a meta-data block, quit when found. */
+ if (metalbn == realbn)
+ break;
+
+ blockcnt /= MNINDIR(ump);
+ off = (bn / blockcnt) % MNINDIR(ump);
+
+ ++numlevels;
+ ap->in_lbn = metalbn;
+ ap->in_off = off;
+ ap->in_exists = 0;
+ ++ap;
+
+ metalbn -= -1 + off * blockcnt;
+ }
+ if (nump)
+ *nump = numlevels;
+ return (0);
+}
diff --git a/sys/ufs/ufs/ufs_disksubr.c b/sys/ufs/ufs/ufs_disksubr.c
new file mode 100644
index 0000000..cc0f28d
--- /dev/null
+++ b/sys/ufs/ufs/ufs_disksubr.c
@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_disksubr.c 8.5 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/disklabel.h>
+#include <sys/syslog.h>
+#include <sys/dkbad.h>
+
+/*
+ * Seek sort for disks. We depend on the driver which calls us using b_resid
+ * as the current cylinder number.
+ *
+ * The argument ap structure holds a b_actf activity chain pointer on which we
+ * keep two queues, sorted in ascending cylinder order. The first queue holds
+ * those requests which are positioned after the current cylinder (in the first
+ * request); the second holds requests which came in after their cylinder number
+ * was passed. Thus we implement a one way scan, retracting after reaching the
+ * end of the drive to the first request on the second queue, at which time it
+ * becomes the first queue.
+ *
+ * A one-way scan is natural because of the way UNIX read-ahead blocks are
+ * allocated.
+ */
+
+/*
+ * For portability with historic industry practice, the
+ * cylinder number has to be maintained in the `b_resid'
+ * field.
+ */
+#define b_cylinder b_resid
+
+void
+disksort(ap, bp)
+ register struct buf *ap, *bp;
+{
+ register struct buf *bq;
+
+ /* If the queue is empty, then it's easy. */
+ if (ap->b_actf == NULL) {
+ bp->b_actf = NULL;
+ ap->b_actf = bp;
+ return;
+ }
+
+ /*
+ * If we lie after the first (currently active) request, then we
+ * must locate the second request list and add ourselves to it.
+ */
+ bq = ap->b_actf;
+ if (bp->b_cylinder < bq->b_cylinder) {
+ while (bq->b_actf) {
+ /*
+ * Check for an ``inversion'' in the normally ascending
+ * cylinder numbers, indicating the start of the second
+ * request list.
+ */
+ if (bq->b_actf->b_cylinder < bq->b_cylinder) {
+ /*
+ * Search the second request list for the first
+ * request at a larger cylinder number. We go
+ * before that; if there is no such request, we
+ * go at end.
+ */
+ do {
+ if (bp->b_cylinder <
+ bq->b_actf->b_cylinder)
+ goto insert;
+ if (bp->b_cylinder ==
+ bq->b_actf->b_cylinder &&
+ bp->b_blkno < bq->b_actf->b_blkno)
+ goto insert;
+ bq = bq->b_actf;
+ } while (bq->b_actf);
+ goto insert; /* after last */
+ }
+ bq = bq->b_actf;
+ }
+ /*
+ * No inversions... we will go after the last, and
+ * be the first request in the second request list.
+ */
+ goto insert;
+ }
+ /*
+ * Request is at/after the current request...
+ * sort in the first request list.
+ */
+ while (bq->b_actf) {
+ /*
+ * We want to go after the current request if there is an
+ * inversion after it (i.e. it is the end of the first
+ * request list), or if the next request is a larger cylinder
+ * than our request.
+ */
+ if (bq->b_actf->b_cylinder < bq->b_cylinder ||
+ bp->b_cylinder < bq->b_actf->b_cylinder ||
+ (bp->b_cylinder == bq->b_actf->b_cylinder &&
+ bp->b_blkno < bq->b_actf->b_blkno))
+ goto insert;
+ bq = bq->b_actf;
+ }
+ /*
+ * Neither a second list nor a larger request... we go at the end of
+ * the first list, which is the same as the end of the whole schebang.
+ */
+insert: bp->b_actf = bq->b_actf;
+ bq->b_actf = bp;
+}
+
+/*
+ * Attempt to read a disk label from a device using the indicated stategy
+ * routine. The label must be partly set up before this: secpercyl and
+ * anything required in the strategy routine (e.g., sector size) must be
+ * filled in before calling us. Returns NULL on success and an error
+ * string on failure.
+ */
+char *
+readdisklabel(dev, strat, lp, dp, bdp)
+ dev_t dev;
+ int (*strat)();
+ register struct disklabel *lp;
+ struct dos_partition *dp;
+ struct dkbad *bdp;
+{
+ register struct buf *bp;
+ struct disklabel *dlp;
+ char *msg = NULL;
+ int dospartoff;
+ int i;
+ int cyl;
+
+ if (lp->d_secperunit == 0)
+ lp->d_secperunit = 0x1fffffff;
+ lp->d_npartitions = 1;
+ if (lp->d_partitions[0].p_size == 0)
+ lp->d_partitions[0].p_size = 0x1fffffff;
+ lp->d_partitions[0].p_offset = 0;
+
+ bp = geteblk((int)lp->d_secsize);
+ /* do dos partitions in the process of getting disklabel? */
+ dospartoff = 0;
+ cyl = LABELSECTOR / lp->d_secpercyl;
+ if (dp) {
+ struct dos_partition *ap;
+
+ /* read master boot record */
+ bp->b_dev = dev;
+ bp->b_blkno = DOSBBSECTOR;
+ bp->b_bcount = lp->d_secsize;
+ bp->b_flags = B_BUSY | B_READ;
+ bp->b_cylinder = DOSBBSECTOR / lp->d_secpercyl;
+ (*strat)(bp);
+
+ /* if successful, wander through dos partition table */
+ if (biowait(bp)) {
+ msg = "dos partition I/O error";
+ goto done;
+ } else {
+ /* XXX how do we check veracity/bounds of this? */
+ bcopy(bp->b_un.b_addr + DOSPARTOFF, dp,
+ NDOSPART * sizeof(*dp));
+ for (i = 0; i < NDOSPART; i++, dp++)
+ /* is this ours? */
+ if (dp->dp_size &&
+ dp->dp_typ == DOSPTYP_386BSD
+ && dospartoff == 0) {
+
+ /* need sector address for SCSI/IDE,
+ cylinder for ESDI/ST506/RLL */
+ dospartoff = dp->dp_start;
+ cyl = DPCYL(dp->dp_scyl, dp->dp_ssect);
+
+ /* update disklabel with details */
+ lp->d_partitions[0].p_size =
+ dp->dp_size;
+ lp->d_partitions[0].p_offset =
+ dp->dp_start;
+ lp->d_ntracks = dp->dp_ehd + 1;
+ lp->d_nsectors = DPSECT(dp->dp_esect);
+ lp->d_subtype |= (lp->d_subtype & 3)
+ + i | DSTYPE_INDOSPART;
+ lp->d_secpercyl = lp->d_ntracks *
+ lp->d_nsectors;
+ }
+ }
+
+ }
+
+ /* next, dig out disk label */
+ bp->b_blkno = dospartoff + LABELSECTOR;
+ bp->b_dev = dev;
+ bp->b_bcount = lp->d_secsize;
+ bp->b_flags = B_BUSY | B_READ;
+ bp->b_cylinder = cyl;
+ (*strat)(bp);
+ if (biowait(bp))
+ msg = "I/O error";
+ else for (dlp = (struct disklabel *)bp->b_data;
+ dlp <= (struct disklabel *)((char *)bp->b_data +
+ DEV_BSIZE - sizeof(*dlp));
+ dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
+ if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) {
+ if (msg == NULL)
+ msg = "no disk label";
+ } else if (dlp->d_npartitions > MAXPARTITIONS ||
+ dkcksum(dlp) != 0)
+ msg = "disk label corrupted";
+ else {
+ *lp = *dlp;
+ msg = NULL;
+ break;
+ }
+ }
+ if (msg)
+ goto done;
+
+ /* obtain bad sector table if requested and present */
+ if (bdp && (lp->d_flags & D_BADSECT)) {
+ struct dkbad *db;
+
+ printf("d_secsize: %d\n", lp->d_secsize);
+ i = 0;
+ do {
+ /* read a bad sector table */
+ bp->b_flags = B_BUSY | B_READ;
+ bp->b_blkno = lp->d_secperunit - lp->d_nsectors + i;
+ if (lp->d_secsize > DEV_BSIZE)
+ bp->b_blkno *= lp->d_secsize / DEV_BSIZE;
+ else
+ bp->b_blkno /= DEV_BSIZE / lp->d_secsize;
+ bp->b_bcount = lp->d_secsize;
+ bp->b_cylinder = lp->d_ncylinders - 1;
+ (*strat)(bp);
+
+ /* if successful, validate, otherwise try another */
+ if (biowait(bp)) {
+ msg = "bad sector table I/O error";
+ } else {
+ db = (struct dkbad *)(bp->b_un.b_addr);
+#define DKBAD_MAGIC 0x4321
+ if (db->bt_mbz == 0
+ && db->bt_flag == DKBAD_MAGIC) {
+ msg = NULL;
+ *bdp = *db;
+ break;
+ } else
+ msg = "bad sector table corrupted";
+ }
+ } while ((bp->b_flags & B_ERROR) && (i += 2) < 10 &&
+ i < lp->d_nsectors);
+ }
+
+done:
+ bp->b_flags = B_INVAL | B_AGE;
+ brelse(bp);
+ return (msg);
+}
+
+/*
+ * Check new disk label for sensibility before setting it.
+ */
+int
+setdisklabel(olp, nlp, openmask)
+ register struct disklabel *olp, *nlp;
+ u_long openmask;
+{
+ register i;
+ register struct partition *opp, *npp;
+
+ if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC ||
+ dkcksum(nlp) != 0)
+ return (EINVAL);
+ while ((i = ffs((long)openmask)) != 0) {
+ i--;
+ openmask &= ~(1 << i);
+ if (nlp->d_npartitions <= i)
+ return (EBUSY);
+ opp = &olp->d_partitions[i];
+ npp = &nlp->d_partitions[i];
+ if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size)
+ return (EBUSY);
+ /*
+ * Copy internally-set partition information
+ * if new label doesn't include it. XXX
+ */
+ if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) {
+ npp->p_fstype = opp->p_fstype;
+ npp->p_fsize = opp->p_fsize;
+ npp->p_frag = opp->p_frag;
+ npp->p_cpg = opp->p_cpg;
+ }
+ }
+ nlp->d_checksum = 0;
+ nlp->d_checksum = dkcksum(nlp);
+ *olp = *nlp;
+ return (0);
+}
+
+/* encoding of disk minor numbers, should be elsewhere... */
+#define dkunit(dev) (minor(dev) >> 3)
+#define dkpart(dev) (minor(dev) & 07)
+#define dkminor(unit, part) (((unit) << 3) | (part))
+
+/*
+ * Write disk label back to device after modification.
+ */
+int
+writedisklabel(dev, strat, lp)
+ dev_t dev;
+ int (*strat)();
+ register struct disklabel *lp;
+{
+ struct buf *bp;
+ struct disklabel *dlp;
+ int labelpart;
+ int error = 0;
+
+ labelpart = dkpart(dev);
+ if (lp->d_partitions[labelpart].p_offset != 0) {
+ if (lp->d_partitions[0].p_offset != 0)
+ return (EXDEV); /* not quite right */
+ labelpart = 0;
+ }
+ bp = geteblk((int)lp->d_secsize);
+ bp->b_dev = makedev(major(dev), dkminor(dkunit(dev), labelpart));
+ bp->b_blkno = LABELSECTOR;
+ bp->b_bcount = lp->d_secsize;
+ bp->b_flags = B_READ;
+ (*strat)(bp);
+ if (error = biowait(bp))
+ goto done;
+ for (dlp = (struct disklabel *)bp->b_data;
+ dlp <= (struct disklabel *)
+ ((char *)bp->b_data + lp->d_secsize - sizeof(*dlp));
+ dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
+ if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC &&
+ dkcksum(dlp) == 0) {
+ *dlp = *lp;
+ bp->b_flags = B_WRITE;
+ (*strat)(bp);
+ error = biowait(bp);
+ goto done;
+ }
+ }
+ error = ESRCH;
+done:
+ brelse(bp);
+ return (error);
+}
+
+/*
+ * Compute checksum for disk label.
+ */
+int
+dkcksum(lp)
+ register struct disklabel *lp;
+{
+ register u_short *start, *end;
+ register u_short sum = 0;
+
+ start = (u_short *)lp;
+ end = (u_short *)&lp->d_partitions[lp->d_npartitions];
+ while (start < end)
+ sum ^= *start++;
+ return (sum);
+}
+
+/*
+ * Disk error is the preface to plaintive error messages
+ * about failing disk transfers. It prints messages of the form
+
+hp0g: hard error reading fsbn 12345 of 12344-12347 (hp0 bn %d cn %d tn %d sn %d)
+
+ * if the offset of the error in the transfer and a disk label
+ * are both available. blkdone should be -1 if the position of the error
+ * is unknown; the disklabel pointer may be null from drivers that have not
+ * been converted to use them. The message is printed with printf
+ * if pri is LOG_PRINTF, otherwise it uses log at the specified priority.
+ * The message should be completed (with at least a newline) with printf
+ * or addlog, respectively. There is no trailing space.
+ */
+void
+diskerr(bp, dname, what, pri, blkdone, lp)
+ register struct buf *bp;
+ char *dname, *what;
+ int pri, blkdone;
+ register struct disklabel *lp;
+{
+ int unit = dkunit(bp->b_dev), part = dkpart(bp->b_dev);
+ register void (*pr) __P((const char *, ...));
+ char partname = 'a' + part;
+ int sn;
+
+ if (pri != LOG_PRINTF) {
+ log(pri, "");
+ pr = addlog;
+ } else
+ pr = printf;
+ (*pr)("%s%d%c: %s %sing fsbn ", dname, unit, partname, what,
+ bp->b_flags & B_READ ? "read" : "writ");
+ sn = bp->b_blkno;
+ if (bp->b_bcount <= DEV_BSIZE)
+ (*pr)("%d", sn);
+ else {
+ if (blkdone >= 0) {
+ sn += blkdone;
+ (*pr)("%d of ", sn);
+ }
+ (*pr)("%d-%d", bp->b_blkno,
+ bp->b_blkno + (bp->b_bcount - 1) / DEV_BSIZE);
+ }
+ if (lp && (blkdone >= 0 || bp->b_bcount <= lp->d_secsize)) {
+#ifdef tahoe
+ sn *= DEV_BSIZE / lp->d_secsize; /* XXX */
+#endif
+ sn += lp->d_partitions[part].p_offset;
+ (*pr)(" (%s%d bn %d; cn %d", dname, unit, sn,
+ sn / lp->d_secpercyl);
+ sn %= lp->d_secpercyl;
+ (*pr)(" tn %d sn %d)", sn / lp->d_nsectors, sn % lp->d_nsectors);
+ }
+}
diff --git a/sys/ufs/ufs/ufs_extern.h b/sys/ufs/ufs/ufs_extern.h
new file mode 100644
index 0000000..e25923e
--- /dev/null
+++ b/sys/ufs/ufs/ufs_extern.h
@@ -0,0 +1,125 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_extern.h 8.3 (Berkeley) 4/16/94
+ */
+
+struct buf;
+struct direct;
+struct disklabel;
+struct fid;
+struct flock;
+struct inode;
+struct mbuf;
+struct mount;
+struct nameidata;
+struct proc;
+struct ucred;
+struct uio;
+struct vattr;
+struct vnode;
+struct ufs_args;
+
+__BEGIN_DECLS
+void diskerr
+ __P((struct buf *, char *, char *, int, int, struct disklabel *));
+void disksort __P((struct buf *, struct buf *));
+u_int dkcksum __P((struct disklabel *));
+char *readdisklabel __P((dev_t, int (*)(), struct disklabel *));
+int setdisklabel __P((struct disklabel *, struct disklabel *, u_long));
+int writedisklabel __P((dev_t, int (*)(), struct disklabel *));
+
+int ufs_abortop __P((struct vop_abortop_args *));
+int ufs_access __P((struct vop_access_args *));
+int ufs_advlock __P((struct vop_advlock_args *));
+int ufs_bmap __P((struct vop_bmap_args *));
+int ufs_check_export __P((struct mount *, struct ufid *, struct mbuf *,
+ struct vnode **, int *exflagsp, struct ucred **));
+int ufs_checkpath __P((struct inode *, struct inode *, struct ucred *));
+int ufs_close __P((struct vop_close_args *));
+int ufs_create __P((struct vop_create_args *));
+void ufs_dirbad __P((struct inode *, doff_t, char *));
+int ufs_dirbadentry __P((struct vnode *, struct direct *, int));
+int ufs_dirempty __P((struct inode *, ino_t, struct ucred *));
+int ufs_direnter __P((struct inode *, struct vnode *,struct componentname *));
+int ufs_dirremove __P((struct vnode *, struct componentname*));
+int ufs_dirrewrite
+ __P((struct inode *, struct inode *, struct componentname *));
+int ufs_getattr __P((struct vop_getattr_args *));
+int ufs_getlbns __P((struct vnode *, daddr_t, struct indir *, int *));
+struct vnode *
+ ufs_ihashget __P((dev_t, ino_t));
+void ufs_ihashinit __P((void));
+void ufs_ihashins __P((struct inode *));
+struct vnode *
+ ufs_ihashlookup __P((dev_t, ino_t));
+void ufs_ihashrem __P((struct inode *));
+int ufs_inactive __P((struct vop_inactive_args *));
+int ufs_init __P((void));
+int ufs_ioctl __P((struct vop_ioctl_args *));
+int ufs_islocked __P((struct vop_islocked_args *));
+int ufs_link __P((struct vop_link_args *));
+int ufs_lock __P((struct vop_lock_args *));
+int ufs_lookup __P((struct vop_lookup_args *));
+int ufs_makeinode __P((int mode, struct vnode *, struct vnode **, struct componentname *));
+int ufs_mkdir __P((struct vop_mkdir_args *));
+int ufs_mknod __P((struct vop_mknod_args *));
+int ufs_mmap __P((struct vop_mmap_args *));
+int ufs_open __P((struct vop_open_args *));
+int ufs_pathconf __P((struct vop_pathconf_args *));
+int ufs_print __P((struct vop_print_args *));
+int ufs_readdir __P((struct vop_readdir_args *));
+int ufs_readlink __P((struct vop_readlink_args *));
+int ufs_reclaim __P((struct vop_reclaim_args *));
+int ufs_remove __P((struct vop_remove_args *));
+int ufs_rename __P((struct vop_rename_args *));
+int ufs_rmdir __P((struct vop_rmdir_args *));
+int ufs_root __P((struct mount *, struct vnode **));
+int ufs_seek __P((struct vop_seek_args *));
+int ufs_select __P((struct vop_select_args *));
+int ufs_setattr __P((struct vop_setattr_args *));
+int ufs_start __P((struct mount *, int, struct proc *));
+int ufs_strategy __P((struct vop_strategy_args *));
+int ufs_symlink __P((struct vop_symlink_args *));
+int ufs_unlock __P((struct vop_unlock_args *));
+int ufs_vinit __P((struct mount *,
+ int (**)(), int (**)(), struct vnode **));
+int ufsspec_close __P((struct vop_close_args *));
+int ufsspec_read __P((struct vop_read_args *));
+int ufsspec_write __P((struct vop_write_args *));
+
+#ifdef FIFO
+int ufsfifo_read __P((struct vop_read_args *));
+int ufsfifo_write __P((struct vop_write_args *));
+int ufsfifo_close __P((struct vop_close_args *));
+#endif
+__END_DECLS
diff --git a/sys/ufs/ufs/ufs_ihash.c b/sys/ufs/ufs/ufs_ihash.c
new file mode 100644
index 0000000..4a37c90
--- /dev/null
+++ b/sys/ufs/ufs/ufs_ihash.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_ihash.c 8.4 (Berkeley) 12/30/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufs_extern.h>
+
+/*
+ * Structures associated with inode cacheing.
+ */
+struct inode **ihashtbl;
+u_long ihash; /* size of hash table - 1 */
+#define INOHASH(device, inum) (((device) + (inum)) & ihash)
+
+/*
+ * Initialize inode hash table.
+ */
+void
+ufs_ihashinit()
+{
+
+ ihashtbl = hashinit(desiredvnodes, M_UFSMNT, &ihash);
+}
+
+/*
+ * Use the device/inum pair to find the incore inode, and return a pointer
+ * to it. If it is in core, return it, even if it is locked.
+ */
+struct vnode *
+ufs_ihashlookup(device, inum)
+ dev_t device;
+ ino_t inum;
+{
+ register struct inode *ip;
+
+ for (ip = ihashtbl[INOHASH(device, inum)];; ip = ip->i_next) {
+ if (ip == NULL)
+ return (NULL);
+ if (inum == ip->i_number && device == ip->i_dev)
+ return (ITOV(ip));
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Use the device/inum pair to find the incore inode, and return a pointer
+ * to it. If it is in core, but locked, wait for it.
+ */
+struct vnode *
+ufs_ihashget(device, inum)
+ dev_t device;
+ ino_t inum;
+{
+ register struct inode *ip;
+ struct vnode *vp;
+
+ for (;;)
+ for (ip = ihashtbl[INOHASH(device, inum)];; ip = ip->i_next) {
+ if (ip == NULL)
+ return (NULL);
+ if (inum == ip->i_number && device == ip->i_dev) {
+ if (ip->i_flag & IN_LOCKED) {
+ ip->i_flag |= IN_WANTED;
+ sleep(ip, PINOD);
+ break;
+ }
+ vp = ITOV(ip);
+ if (!vget(vp, 1))
+ return (vp);
+ break;
+ }
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Insert the inode into the hash table, and return it locked.
+ */
+void
+ufs_ihashins(ip)
+ struct inode *ip;
+{
+ struct inode **ipp, *iq;
+
+ ipp = &ihashtbl[INOHASH(ip->i_dev, ip->i_number)];
+ if (iq = *ipp)
+ iq->i_prev = &ip->i_next;
+ ip->i_next = iq;
+ ip->i_prev = ipp;
+ *ipp = ip;
+ if (ip->i_flag & IN_LOCKED)
+ panic("ufs_ihashins: already locked");
+ if (curproc)
+ ip->i_lockholder = curproc->p_pid;
+ else
+ ip->i_lockholder = -1;
+ ip->i_flag |= IN_LOCKED;
+}
+
+/*
+ * Remove the inode from the hash table.
+ */
+void
+ufs_ihashrem(ip)
+ register struct inode *ip;
+{
+ register struct inode *iq;
+
+ if (iq = ip->i_next)
+ iq->i_prev = ip->i_prev;
+ *ip->i_prev = iq;
+#ifdef DIAGNOSTIC
+ ip->i_next = NULL;
+ ip->i_prev = NULL;
+#endif
+}
diff --git a/sys/ufs/ufs/ufs_inode.c b/sys/ufs/ufs/ufs_inode.c
new file mode 100644
index 0000000..ac876f9
--- /dev/null
+++ b/sys/ufs/ufs/ufs_inode.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_inode.c 8.4 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+u_long nextgennumber; /* Next generation number to assign. */
+int prtactive = 0; /* 1 => print out reclaim of active vnodes */
+
+int
+ufs_init()
+{
+ static int first = 1;
+
+ if (!first)
+ return (0);
+ first = 0;
+
+#ifdef DIAGNOSTIC
+ if ((sizeof(struct inode) - 1) & sizeof(struct inode))
+ printf("ufs_init: bad size %d\n", sizeof(struct inode));
+#endif
+ ufs_ihashinit();
+ dqinit();
+ return (0);
+}
+
+/*
+ * Last reference to an inode. If necessary, write or delete it.
+ */
+int
+ufs_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip = VTOI(vp);
+ struct timeval tv;
+ int mode, error;
+ extern int prtactive;
+
+ if (prtactive && vp->v_usecount != 0)
+ vprint("ffs_inactive: pushing active", vp);
+
+ /* Get rid of inodes related to stale file handles. */
+ if (ip->i_mode == 0) {
+ if ((vp->v_flag & VXLOCK) == 0)
+ vgone(vp);
+ return (0);
+ }
+
+ error = 0;
+#ifdef DIAGNOSTIC
+ if (VOP_ISLOCKED(vp))
+ panic("ffs_inactive: locked inode");
+ if (curproc)
+ ip->i_lockholder = curproc->p_pid;
+ else
+ ip->i_lockholder = -1;
+#endif
+ ip->i_flag |= IN_LOCKED;
+ if (ip->i_nlink <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
+#ifdef QUOTA
+ if (!getinoquota(ip))
+ (void)chkiq(ip, -1, NOCRED, 0);
+#endif
+ error = VOP_TRUNCATE(vp, (off_t)0, 0, NOCRED, NULL);
+ ip->i_rdev = 0;
+ mode = ip->i_mode;
+ ip->i_mode = 0;
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ VOP_VFREE(vp, ip->i_number, mode);
+ }
+ if (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
+ tv = time;
+ VOP_UPDATE(vp, &tv, &tv, 0);
+ }
+ VOP_UNLOCK(vp);
+ /*
+ * If we are done with the inode, reclaim it
+ * so that it can be reused immediately.
+ */
+ if (vp->v_usecount == 0 && ip->i_mode == 0)
+ vgone(vp);
+ return (error);
+}
+
+/*
+ * Reclaim an inode so that it can be used for other purposes.
+ */
+int
+ufs_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip;
+ int i, type;
+
+ if (prtactive && vp->v_usecount != 0)
+ vprint("ufs_reclaim: pushing active", vp);
+ /*
+ * Remove the inode from its hash chain.
+ */
+ ip = VTOI(vp);
+ ufs_ihashrem(ip);
+ /*
+ * Purge old data structures associated with the inode.
+ */
+ cache_purge(vp);
+ if (ip->i_devvp) {
+ vrele(ip->i_devvp);
+ ip->i_devvp = 0;
+ }
+#ifdef QUOTA
+ for (i = 0; i < MAXQUOTAS; i++) {
+ if (ip->i_dquot[i] != NODQUOT) {
+ dqrele(vp, ip->i_dquot[i]);
+ ip->i_dquot[i] = NODQUOT;
+ }
+ }
+#endif
+ switch (vp->v_mount->mnt_stat.f_type) {
+ case MOUNT_UFS:
+ type = M_FFSNODE;
+ break;
+ case MOUNT_MFS:
+ type = M_MFSNODE;
+ break;
+ case MOUNT_LFS:
+ type = M_LFSNODE;
+ break;
+ default:
+ panic("ufs_reclaim: not ufs file");
+ }
+ FREE(vp->v_data, type);
+ vp->v_data = NULL;
+ return (0);
+}
diff --git a/sys/ufs/ufs/ufs_lockf.c b/sys/ufs/ufs/ufs_lockf.c
new file mode 100644
index 0000000..cb9a737
--- /dev/null
+++ b/sys/ufs/ufs/ufs_lockf.c
@@ -0,0 +1,707 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Scooter Morris at Genentech Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_lockf.c 8.3 (Berkeley) 1/6/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/fcntl.h>
+
+#include <ufs/ufs/lockf.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufs_extern.h>
+
+/*
+ * This variable controls the maximum number of processes that will
+ * be checked in doing deadlock detection.
+ */
+int maxlockdepth = MAXDEPTH;
+
+#ifdef LOCKF_DEBUG
+int lockf_debug = 0;
+#endif
+
+#define NOLOCKF (struct lockf *)0
+#define SELF 0x1
+#define OTHERS 0x2
+
+/*
+ * Set a byte-range lock.
+ */
+int
+lf_setlock(lock)
+ register struct lockf *lock;
+{
+ register struct lockf *block;
+ struct inode *ip = lock->lf_inode;
+ struct lockf **prev, *overlap, *ltmp;
+ static char lockstr[] = "lockf";
+ int ovcase, priority, needtolink, error;
+
+#ifdef LOCKF_DEBUG
+ if (lockf_debug & 1)
+ lf_print("lf_setlock", lock);
+#endif /* LOCKF_DEBUG */
+
+ /*
+ * Set the priority
+ */
+ priority = PLOCK;
+ if (lock->lf_type == F_WRLCK)
+ priority += 4;
+ priority |= PCATCH;
+ /*
+ * Scan lock list for this file looking for locks that would block us.
+ */
+ while (block = lf_getblock(lock)) {
+ /*
+ * Free the structure and return if nonblocking.
+ */
+ if ((lock->lf_flags & F_WAIT) == 0) {
+ FREE(lock, M_LOCKF);
+ return (EAGAIN);
+ }
+ /*
+ * We are blocked. Since flock style locks cover
+ * the whole file, there is no chance for deadlock.
+ * For byte-range locks we must check for deadlock.
+ *
+ * Deadlock detection is done by looking through the
+ * wait channels to see if there are any cycles that
+ * involve us. MAXDEPTH is set just to make sure we
+ * do not go off into neverland.
+ */
+ if ((lock->lf_flags & F_POSIX) &&
+ (block->lf_flags & F_POSIX)) {
+ register struct proc *wproc;
+ register struct lockf *waitblock;
+ int i = 0;
+
+ /* The block is waiting on something */
+ wproc = (struct proc *)block->lf_id;
+ while (wproc->p_wchan &&
+ (wproc->p_wmesg == lockstr) &&
+ (i++ < maxlockdepth)) {
+ waitblock = (struct lockf *)wproc->p_wchan;
+ /* Get the owner of the blocking lock */
+ waitblock = waitblock->lf_next;
+ if ((waitblock->lf_flags & F_POSIX) == 0)
+ break;
+ wproc = (struct proc *)waitblock->lf_id;
+ if (wproc == (struct proc *)lock->lf_id) {
+ free(lock, M_LOCKF);
+ return (EDEADLK);
+ }
+ }
+ }
+ /*
+ * For flock type locks, we must first remove
+ * any shared locks that we hold before we sleep
+ * waiting for an exclusive lock.
+ */
+ if ((lock->lf_flags & F_FLOCK) &&
+ lock->lf_type == F_WRLCK) {
+ lock->lf_type = F_UNLCK;
+ (void) lf_clearlock(lock);
+ lock->lf_type = F_WRLCK;
+ }
+ /*
+ * Add our lock to the blocked list and sleep until we're free.
+ * Remember who blocked us (for deadlock detection).
+ */
+ lock->lf_next = block;
+ lf_addblock(block, lock);
+#ifdef LOCKF_DEBUG
+ if (lockf_debug & 1) {
+ lf_print("lf_setlock: blocking on", block);
+ lf_printlist("lf_setlock", block);
+ }
+#endif /* LOCKF_DEBUG */
+ if (error = tsleep((caddr_t)lock, priority, lockstr, 0)) {
+ /*
+ * Delete ourselves from the waiting to lock list.
+ */
+ for (block = lock->lf_next;
+ block != NOLOCKF;
+ block = block->lf_block) {
+ if (block->lf_block != lock)
+ continue;
+ block->lf_block = block->lf_block->lf_block;
+ break;
+ }
+ /*
+ * If we did not find ourselves on the list, but
+ * are still linked onto a lock list, then something
+ * is very wrong.
+ */
+ if (block == NOLOCKF && lock->lf_next != NOLOCKF)
+ panic("lf_setlock: lost lock");
+ free(lock, M_LOCKF);
+ return (error);
+ }
+ }
+ /*
+ * No blocks!! Add the lock. Note that we will
+ * downgrade or upgrade any overlapping locks this
+ * process already owns.
+ *
+ * Skip over locks owned by other processes.
+ * Handle any locks that overlap and are owned by ourselves.
+ */
+ prev = &ip->i_lockf;
+ block = ip->i_lockf;
+ needtolink = 1;
+ for (;;) {
+ if (ovcase = lf_findoverlap(block, lock, SELF, &prev, &overlap))
+ block = overlap->lf_next;
+ /*
+ * Six cases:
+ * 0) no overlap
+ * 1) overlap == lock
+ * 2) overlap contains lock
+ * 3) lock contains overlap
+ * 4) overlap starts before lock
+ * 5) overlap ends after lock
+ */
+ switch (ovcase) {
+ case 0: /* no overlap */
+ if (needtolink) {
+ *prev = lock;
+ lock->lf_next = overlap;
+ }
+ break;
+
+ case 1: /* overlap == lock */
+ /*
+ * If downgrading lock, others may be
+ * able to acquire it.
+ */
+ if (lock->lf_type == F_RDLCK &&
+ overlap->lf_type == F_WRLCK)
+ lf_wakelock(overlap);
+ overlap->lf_type = lock->lf_type;
+ FREE(lock, M_LOCKF);
+ lock = overlap; /* for debug output below */
+ break;
+
+ case 2: /* overlap contains lock */
+ /*
+ * Check for common starting point and different types.
+ */
+ if (overlap->lf_type == lock->lf_type) {
+ free(lock, M_LOCKF);
+ lock = overlap; /* for debug output below */
+ break;
+ }
+ if (overlap->lf_start == lock->lf_start) {
+ *prev = lock;
+ lock->lf_next = overlap;
+ overlap->lf_start = lock->lf_end + 1;
+ } else
+ lf_split(overlap, lock);
+ lf_wakelock(overlap);
+ break;
+
+ case 3: /* lock contains overlap */
+ /*
+ * If downgrading lock, others may be able to
+ * acquire it, otherwise take the list.
+ */
+ if (lock->lf_type == F_RDLCK &&
+ overlap->lf_type == F_WRLCK) {
+ lf_wakelock(overlap);
+ } else {
+ ltmp = lock->lf_block;
+ lock->lf_block = overlap->lf_block;
+ lf_addblock(lock, ltmp);
+ }
+ /*
+ * Add the new lock if necessary and delete the overlap.
+ */
+ if (needtolink) {
+ *prev = lock;
+ lock->lf_next = overlap->lf_next;
+ prev = &lock->lf_next;
+ needtolink = 0;
+ } else
+ *prev = overlap->lf_next;
+ free(overlap, M_LOCKF);
+ continue;
+
+ case 4: /* overlap starts before lock */
+ /*
+ * Add lock after overlap on the list.
+ */
+ lock->lf_next = overlap->lf_next;
+ overlap->lf_next = lock;
+ overlap->lf_end = lock->lf_start - 1;
+ prev = &lock->lf_next;
+ lf_wakelock(overlap);
+ needtolink = 0;
+ continue;
+
+ case 5: /* overlap ends after lock */
+ /*
+ * Add the new lock before overlap.
+ */
+ if (needtolink) {
+ *prev = lock;
+ lock->lf_next = overlap;
+ }
+ overlap->lf_start = lock->lf_end + 1;
+ lf_wakelock(overlap);
+ break;
+ }
+ break;
+ }
+#ifdef LOCKF_DEBUG
+ if (lockf_debug & 1) {
+ lf_print("lf_setlock: got the lock", lock);
+ lf_printlist("lf_setlock", lock);
+ }
+#endif /* LOCKF_DEBUG */
+ return (0);
+}
+
+/*
+ * Remove a byte-range lock on an inode.
+ *
+ * Generally, find the lock (or an overlap to that lock)
+ * and remove it (or shrink it), then wakeup anyone we can.
+ */
+int
+lf_clearlock(unlock)
+ register struct lockf *unlock;
+{
+ struct inode *ip = unlock->lf_inode;
+ register struct lockf *lf = ip->i_lockf;
+ struct lockf *overlap, **prev;
+ int ovcase;
+
+ if (lf == NOLOCKF)
+ return (0);
+#ifdef LOCKF_DEBUG
+ if (unlock->lf_type != F_UNLCK)
+ panic("lf_clearlock: bad type");
+ if (lockf_debug & 1)
+ lf_print("lf_clearlock", unlock);
+#endif /* LOCKF_DEBUG */
+ prev = &ip->i_lockf;
+ while (ovcase = lf_findoverlap(lf, unlock, SELF, &prev, &overlap)) {
+ /*
+ * Wakeup the list of locks to be retried.
+ */
+ lf_wakelock(overlap);
+
+ switch (ovcase) {
+
+ case 1: /* overlap == lock */
+ *prev = overlap->lf_next;
+ FREE(overlap, M_LOCKF);
+ break;
+
+ case 2: /* overlap contains lock: split it */
+ if (overlap->lf_start == unlock->lf_start) {
+ overlap->lf_start = unlock->lf_end + 1;
+ break;
+ }
+ lf_split(overlap, unlock);
+ overlap->lf_next = unlock->lf_next;
+ break;
+
+ case 3: /* lock contains overlap */
+ *prev = overlap->lf_next;
+ lf = overlap->lf_next;
+ free(overlap, M_LOCKF);
+ continue;
+
+ case 4: /* overlap starts before lock */
+ overlap->lf_end = unlock->lf_start - 1;
+ prev = &overlap->lf_next;
+ lf = overlap->lf_next;
+ continue;
+
+ case 5: /* overlap ends after lock */
+ overlap->lf_start = unlock->lf_end + 1;
+ break;
+ }
+ break;
+ }
+#ifdef LOCKF_DEBUG
+ if (lockf_debug & 1)
+ lf_printlist("lf_clearlock", unlock);
+#endif /* LOCKF_DEBUG */
+ return (0);
+}
+
+/*
+ * Check whether there is a blocking lock,
+ * and if so return its process identifier.
+ */
+int
+lf_getlock(lock, fl)
+ register struct lockf *lock;
+ register struct flock *fl;
+{
+ register struct lockf *block;
+
+#ifdef LOCKF_DEBUG
+ if (lockf_debug & 1)
+ lf_print("lf_getlock", lock);
+#endif /* LOCKF_DEBUG */
+
+ if (block = lf_getblock(lock)) {
+ fl->l_type = block->lf_type;
+ fl->l_whence = SEEK_SET;
+ fl->l_start = block->lf_start;
+ if (block->lf_end == -1)
+ fl->l_len = 0;
+ else
+ fl->l_len = block->lf_end - block->lf_start + 1;
+ if (block->lf_flags & F_POSIX)
+ fl->l_pid = ((struct proc *)(block->lf_id))->p_pid;
+ else
+ fl->l_pid = -1;
+ } else {
+ fl->l_type = F_UNLCK;
+ }
+ return (0);
+}
+
+/*
+ * Walk the list of locks for an inode and
+ * return the first blocking lock.
+ */
+struct lockf *
+lf_getblock(lock)
+ register struct lockf *lock;
+{
+ struct lockf **prev, *overlap, *lf = lock->lf_inode->i_lockf;
+ int ovcase;
+
+ prev = &lock->lf_inode->i_lockf;
+ while (ovcase = lf_findoverlap(lf, lock, OTHERS, &prev, &overlap)) {
+ /*
+ * We've found an overlap, see if it blocks us
+ */
+ if ((lock->lf_type == F_WRLCK || overlap->lf_type == F_WRLCK))
+ return (overlap);
+ /*
+ * Nope, point to the next one on the list and
+ * see if it blocks us
+ */
+ lf = overlap->lf_next;
+ }
+ return (NOLOCKF);
+}
+
+/*
+ * Walk the list of locks for an inode to
+ * find an overlapping lock (if any).
+ *
+ * NOTE: this returns only the FIRST overlapping lock. There
+ * may be more than one.
+ */
+int
+lf_findoverlap(lf, lock, type, prev, overlap)
+ register struct lockf *lf;
+ struct lockf *lock;
+ int type;
+ struct lockf ***prev;
+ struct lockf **overlap;
+{
+ off_t start, end;
+
+ *overlap = lf;
+ if (lf == NOLOCKF)
+ return (0);
+#ifdef LOCKF_DEBUG
+ if (lockf_debug & 2)
+ lf_print("lf_findoverlap: looking for overlap in", lock);
+#endif /* LOCKF_DEBUG */
+ start = lock->lf_start;
+ end = lock->lf_end;
+ while (lf != NOLOCKF) {
+ if (((type & SELF) && lf->lf_id != lock->lf_id) ||
+ ((type & OTHERS) && lf->lf_id == lock->lf_id)) {
+ *prev = &lf->lf_next;
+ *overlap = lf = lf->lf_next;
+ continue;
+ }
+#ifdef LOCKF_DEBUG
+ if (lockf_debug & 2)
+ lf_print("\tchecking", lf);
+#endif /* LOCKF_DEBUG */
+ /*
+ * OK, check for overlap
+ *
+ * Six cases:
+ * 0) no overlap
+ * 1) overlap == lock
+ * 2) overlap contains lock
+ * 3) lock contains overlap
+ * 4) overlap starts before lock
+ * 5) overlap ends after lock
+ */
+ if ((lf->lf_end != -1 && start > lf->lf_end) ||
+ (end != -1 && lf->lf_start > end)) {
+ /* Case 0 */
+#ifdef LOCKF_DEBUG
+ if (lockf_debug & 2)
+ printf("no overlap\n");
+#endif /* LOCKF_DEBUG */
+ if ((type & SELF) && end != -1 && lf->lf_start > end)
+ return (0);
+ *prev = &lf->lf_next;
+ *overlap = lf = lf->lf_next;
+ continue;
+ }
+ if ((lf->lf_start == start) && (lf->lf_end == end)) {
+ /* Case 1 */
+#ifdef LOCKF_DEBUG
+ if (lockf_debug & 2)
+ printf("overlap == lock\n");
+#endif /* LOCKF_DEBUG */
+ return (1);
+ }
+ if ((lf->lf_start <= start) &&
+ (end != -1) &&
+ ((lf->lf_end >= end) || (lf->lf_end == -1))) {
+ /* Case 2 */
+#ifdef LOCKF_DEBUG
+ if (lockf_debug & 2)
+ printf("overlap contains lock\n");
+#endif /* LOCKF_DEBUG */
+ return (2);
+ }
+ if (start <= lf->lf_start &&
+ (end == -1 ||
+ (lf->lf_end != -1 && end >= lf->lf_end))) {
+ /* Case 3 */
+#ifdef LOCKF_DEBUG
+ if (lockf_debug & 2)
+ printf("lock contains overlap\n");
+#endif /* LOCKF_DEBUG */
+ return (3);
+ }
+ if ((lf->lf_start < start) &&
+ ((lf->lf_end >= start) || (lf->lf_end == -1))) {
+ /* Case 4 */
+#ifdef LOCKF_DEBUG
+ if (lockf_debug & 2)
+ printf("overlap starts before lock\n");
+#endif /* LOCKF_DEBUG */
+ return (4);
+ }
+ if ((lf->lf_start > start) &&
+ (end != -1) &&
+ ((lf->lf_end > end) || (lf->lf_end == -1))) {
+ /* Case 5 */
+#ifdef LOCKF_DEBUG
+ if (lockf_debug & 2)
+ printf("overlap ends after lock\n");
+#endif /* LOCKF_DEBUG */
+ return (5);
+ }
+ panic("lf_findoverlap: default");
+ }
+ return (0);
+}
+
+/*
+ * Add a lock to the end of the blocked list.
+ */
+void
+lf_addblock(lock, blocked)
+ struct lockf *lock;
+ struct lockf *blocked;
+{
+ register struct lockf *lf;
+
+ if (blocked == NOLOCKF)
+ return;
+#ifdef LOCKF_DEBUG
+ if (lockf_debug & 2) {
+ lf_print("addblock: adding", blocked);
+ lf_print("to blocked list of", lock);
+ }
+#endif /* LOCKF_DEBUG */
+ if ((lf = lock->lf_block) == NOLOCKF) {
+ lock->lf_block = blocked;
+ return;
+ }
+ while (lf->lf_block != NOLOCKF)
+ lf = lf->lf_block;
+ lf->lf_block = blocked;
+ return;
+}
+
+/*
+ * Split a lock and a contained region into
+ * two or three locks as necessary.
+ */
+void
+lf_split(lock1, lock2)
+ register struct lockf *lock1;
+ register struct lockf *lock2;
+{
+ register struct lockf *splitlock;
+
+#ifdef LOCKF_DEBUG
+ if (lockf_debug & 2) {
+ lf_print("lf_split", lock1);
+ lf_print("splitting from", lock2);
+ }
+#endif /* LOCKF_DEBUG */
+ /*
+ * Check to see if spliting into only two pieces.
+ */
+ if (lock1->lf_start == lock2->lf_start) {
+ lock1->lf_start = lock2->lf_end + 1;
+ lock2->lf_next = lock1;
+ return;
+ }
+ if (lock1->lf_end == lock2->lf_end) {
+ lock1->lf_end = lock2->lf_start - 1;
+ lock2->lf_next = lock1->lf_next;
+ lock1->lf_next = lock2;
+ return;
+ }
+ /*
+ * Make a new lock consisting of the last part of
+ * the encompassing lock
+ */
+ MALLOC(splitlock, struct lockf *, sizeof *splitlock, M_LOCKF, M_WAITOK);
+ bcopy((caddr_t)lock1, (caddr_t)splitlock, sizeof *splitlock);
+ splitlock->lf_start = lock2->lf_end + 1;
+ splitlock->lf_block = NOLOCKF;
+ lock1->lf_end = lock2->lf_start - 1;
+ /*
+ * OK, now link it in
+ */
+ splitlock->lf_next = lock1->lf_next;
+ lock2->lf_next = splitlock;
+ lock1->lf_next = lock2;
+}
+
+/*
+ * Wakeup a blocklist
+ */
+void
+lf_wakelock(listhead)
+ struct lockf *listhead;
+{
+ register struct lockf *blocklist, *wakelock;
+
+ blocklist = listhead->lf_block;
+ listhead->lf_block = NOLOCKF;
+ while (blocklist != NOLOCKF) {
+ wakelock = blocklist;
+ blocklist = blocklist->lf_block;
+ wakelock->lf_block = NOLOCKF;
+ wakelock->lf_next = NOLOCKF;
+#ifdef LOCKF_DEBUG
+ if (lockf_debug & 2)
+ lf_print("lf_wakelock: awakening", wakelock);
+#endif /* LOCKF_DEBUG */
+ wakeup((caddr_t)wakelock);
+ }
+}
+
+#ifdef LOCKF_DEBUG
+/*
+ * Print out a lock.
+ */
+void
+lf_print(tag, lock)
+ char *tag;
+ register struct lockf *lock;
+{
+
+ printf("%s: lock 0x%lx for ", tag, lock);
+ if (lock->lf_flags & F_POSIX)
+ printf("proc %d", ((struct proc *)(lock->lf_id))->p_pid);
+ else
+ printf("id 0x%x", lock->lf_id);
+ printf(" in ino %d on dev <%d, %d>, %s, start %d, end %d",
+ lock->lf_inode->i_number,
+ major(lock->lf_inode->i_dev),
+ minor(lock->lf_inode->i_dev),
+ lock->lf_type == F_RDLCK ? "shared" :
+ lock->lf_type == F_WRLCK ? "exclusive" :
+ lock->lf_type == F_UNLCK ? "unlock" :
+ "unknown", lock->lf_start, lock->lf_end);
+ if (lock->lf_block)
+ printf(" block 0x%x\n", lock->lf_block);
+ else
+ printf("\n");
+}
+
+void
+lf_printlist(tag, lock)
+ char *tag;
+ struct lockf *lock;
+{
+ register struct lockf *lf;
+
+ printf("%s: Lock list for ino %d on dev <%d, %d>:\n",
+ tag, lock->lf_inode->i_number,
+ major(lock->lf_inode->i_dev),
+ minor(lock->lf_inode->i_dev));
+ for (lf = lock->lf_inode->i_lockf; lf; lf = lf->lf_next) {
+ printf("\tlock 0x%lx for ", lf);
+ if (lf->lf_flags & F_POSIX)
+ printf("proc %d", ((struct proc *)(lf->lf_id))->p_pid);
+ else
+ printf("id 0x%x", lf->lf_id);
+ printf(", %s, start %d, end %d",
+ lf->lf_type == F_RDLCK ? "shared" :
+ lf->lf_type == F_WRLCK ? "exclusive" :
+ lf->lf_type == F_UNLCK ? "unlock" :
+ "unknown", lf->lf_start, lf->lf_end);
+ if (lf->lf_block)
+ printf(" block 0x%x\n", lf->lf_block);
+ else
+ printf("\n");
+ }
+}
+#endif /* LOCKF_DEBUG */
diff --git a/sys/ufs/ufs/ufs_lookup.c b/sys/ufs/ufs/ufs_lookup.c
new file mode 100644
index 0000000..87c6802
--- /dev/null
+++ b/sys/ufs/ufs/ufs_lookup.c
@@ -0,0 +1,970 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_lookup.c 8.6 (Berkeley) 4/1/94
+ */
+
+#include <sys/param.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+struct nchstats nchstats;
+#ifdef DIAGNOSTIC
+int dirchk = 1;
+#else
+int dirchk = 0;
+#endif
+
+#define FSFMT(vp) ((vp)->v_mount->mnt_maxsymlinklen <= 0)
+
+/*
+ * Convert a component of a pathname into a pointer to a locked inode.
+ * This is a very central and rather complicated routine.
+ * If the file system is not maintained in a strict tree hierarchy,
+ * this can result in a deadlock situation (see comments in code below).
+ *
+ * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
+ * on whether the name is to be looked up, created, renamed, or deleted.
+ * When CREATE, RENAME, or DELETE is specified, information usable in
+ * creating, renaming, or deleting a directory entry may be calculated.
+ * If flag has LOCKPARENT or'ed into it and the target of the pathname
+ * exists, lookup returns both the target and its parent directory locked.
+ * When creating or renaming and LOCKPARENT is specified, the target may
+ * not be ".". When deleting and LOCKPARENT is specified, the target may
+ * be "."., but the caller must check to ensure it does an vrele and vput
+ * instead of two vputs.
+ *
+ * Overall outline of ufs_lookup:
+ *
+ * check accessibility of directory
+ * look for name in cache, if found, then if at end of path
+ * and deleting or creating, drop it, else return name
+ * search for name in directory, to found or notfound
+ * notfound:
+ * if creating, return locked directory, leaving info on available slots
+ * else return error
+ * found:
+ * if at end of path and deleting, return information to allow delete
+ * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
+ * inode and return info to allow rewrite
+ * if not at end, add name to cache; if at end and neither creating
+ * nor deleting, add name to cache
+ */
+int
+ufs_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ register struct vnode *vdp; /* vnode for directory being searched */
+ register struct inode *dp; /* inode for directory being searched */
+ struct buf *bp; /* a buffer of directory entries */
+ register struct direct *ep; /* the current directory entry */
+ int entryoffsetinblock; /* offset of ep in bp's buffer */
+ enum {NONE, COMPACT, FOUND} slotstatus;
+ doff_t slotoffset; /* offset of area with free space */
+ int slotsize; /* size of area at slotoffset */
+ int slotfreespace; /* amount of space free in slot */
+ int slotneeded; /* size of the entry we're seeking */
+ int numdirpasses; /* strategy for directory search */
+ doff_t endsearch; /* offset to end directory search */
+ doff_t prevoff; /* prev entry dp->i_offset */
+ struct vnode *pdp; /* saved dp during symlink work */
+ struct vnode *tdp; /* returned by VFS_VGET */
+ doff_t enduseful; /* pointer past last used dir slot */
+ u_long bmask; /* block offset mask */
+ int lockparent; /* 1 => lockparent flag is set */
+ int wantparent; /* 1 => wantparent or lockparent flag */
+ int namlen, error;
+ struct vnode **vpp = ap->a_vpp;
+ struct componentname *cnp = ap->a_cnp;
+ struct ucred *cred = cnp->cn_cred;
+ int flags = cnp->cn_flags;
+ int nameiop = cnp->cn_nameiop;
+
+ bp = NULL;
+ slotoffset = -1;
+ *vpp = NULL;
+ vdp = ap->a_dvp;
+ dp = VTOI(vdp);
+ lockparent = flags & LOCKPARENT;
+ wantparent = flags & (LOCKPARENT|WANTPARENT);
+
+ /*
+ * Check accessiblity of directory.
+ */
+ if ((dp->i_mode & IFMT) != IFDIR)
+ return (ENOTDIR);
+ if (error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc))
+ return (error);
+
+ /*
+ * We now have a segment name to search for, and a directory to search.
+ *
+ * Before tediously performing a linear scan of the directory,
+ * check the name cache to see if the directory/name pair
+ * we are looking for is known already.
+ */
+ if (error = cache_lookup(vdp, vpp, cnp)) {
+ int vpid; /* capability number of vnode */
+
+ if (error == ENOENT)
+ return (error);
+ /*
+ * Get the next vnode in the path.
+ * See comment below starting `Step through' for
+ * an explaination of the locking protocol.
+ */
+ pdp = vdp;
+ dp = VTOI(*vpp);
+ vdp = *vpp;
+ vpid = vdp->v_id;
+ if (pdp == vdp) { /* lookup on "." */
+ VREF(vdp);
+ error = 0;
+ } else if (flags & ISDOTDOT) {
+ VOP_UNLOCK(pdp);
+ error = vget(vdp, 1);
+ if (!error && lockparent && (flags & ISLASTCN))
+ error = VOP_LOCK(pdp);
+ } else {
+ error = vget(vdp, 1);
+ if (!lockparent || error || !(flags & ISLASTCN))
+ VOP_UNLOCK(pdp);
+ }
+ /*
+ * Check that the capability number did not change
+ * while we were waiting for the lock.
+ */
+ if (!error) {
+ if (vpid == vdp->v_id)
+ return (0);
+ vput(vdp);
+ if (lockparent && pdp != vdp && (flags & ISLASTCN))
+ VOP_UNLOCK(pdp);
+ }
+ if (error = VOP_LOCK(pdp))
+ return (error);
+ vdp = pdp;
+ dp = VTOI(pdp);
+ *vpp = NULL;
+ }
+
+ /*
+ * Suppress search for slots unless creating
+ * file and at end of pathname, in which case
+ * we watch for a place to put the new file in
+ * case it doesn't already exist.
+ */
+ slotstatus = FOUND;
+ slotfreespace = slotsize = slotneeded = 0;
+ if ((nameiop == CREATE || nameiop == RENAME) &&
+ (flags & ISLASTCN)) {
+ slotstatus = NONE;
+ slotneeded = (sizeof(struct direct) - MAXNAMLEN +
+ cnp->cn_namelen + 3) &~ 3;
+ }
+
+ /*
+ * If there is cached information on a previous search of
+ * this directory, pick up where we last left off.
+ * We cache only lookups as these are the most common
+ * and have the greatest payoff. Caching CREATE has little
+ * benefit as it usually must search the entire directory
+ * to determine that the entry does not exist. Caching the
+ * location of the last DELETE or RENAME has not reduced
+ * profiling time and hence has been removed in the interest
+ * of simplicity.
+ */
+ bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
+ if (nameiop != LOOKUP || dp->i_diroff == 0 ||
+ dp->i_diroff > dp->i_size) {
+ entryoffsetinblock = 0;
+ dp->i_offset = 0;
+ numdirpasses = 1;
+ } else {
+ dp->i_offset = dp->i_diroff;
+ if ((entryoffsetinblock = dp->i_offset & bmask) &&
+ (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)))
+ return (error);
+ numdirpasses = 2;
+ nchstats.ncs_2passes++;
+ }
+ prevoff = dp->i_offset;
+ endsearch = roundup(dp->i_size, DIRBLKSIZ);
+ enduseful = 0;
+
+searchloop:
+ while (dp->i_offset < endsearch) {
+ /*
+ * If necessary, get the next directory block.
+ */
+ if ((dp->i_offset & bmask) == 0) {
+ if (bp != NULL)
+ brelse(bp);
+ if (error =
+ VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp))
+ return (error);
+ entryoffsetinblock = 0;
+ }
+ /*
+ * If still looking for a slot, and at a DIRBLKSIZE
+ * boundary, have to start looking for free space again.
+ */
+ if (slotstatus == NONE &&
+ (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
+ slotoffset = -1;
+ slotfreespace = 0;
+ }
+ /*
+ * Get pointer to next entry.
+ * Full validation checks are slow, so we only check
+ * enough to insure forward progress through the
+ * directory. Complete checks can be run by patching
+ * "dirchk" to be true.
+ */
+ ep = (struct direct *)((char *)bp->b_data + entryoffsetinblock);
+ if (ep->d_reclen == 0 ||
+ dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock)) {
+ int i;
+
+ ufs_dirbad(dp, dp->i_offset, "mangled entry");
+ i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
+ dp->i_offset += i;
+ entryoffsetinblock += i;
+ continue;
+ }
+
+ /*
+ * If an appropriate sized slot has not yet been found,
+ * check to see if one is available. Also accumulate space
+ * in the current block so that we can determine if
+ * compaction is viable.
+ */
+ if (slotstatus != FOUND) {
+ int size = ep->d_reclen;
+
+ if (ep->d_ino != 0)
+ size -= DIRSIZ(FSFMT(vdp), ep);
+ if (size > 0) {
+ if (size >= slotneeded) {
+ slotstatus = FOUND;
+ slotoffset = dp->i_offset;
+ slotsize = ep->d_reclen;
+ } else if (slotstatus == NONE) {
+ slotfreespace += size;
+ if (slotoffset == -1)
+ slotoffset = dp->i_offset;
+ if (slotfreespace >= slotneeded) {
+ slotstatus = COMPACT;
+ slotsize = dp->i_offset +
+ ep->d_reclen - slotoffset;
+ }
+ }
+ }
+ }
+
+ /*
+ * Check for a name match.
+ */
+ if (ep->d_ino) {
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ if (vdp->v_mount->mnt_maxsymlinklen > 0)
+ namlen = ep->d_namlen;
+ else
+ namlen = ep->d_type;
+# else
+ namlen = ep->d_namlen;
+# endif
+ if (namlen == cnp->cn_namelen &&
+ !bcmp(cnp->cn_nameptr, ep->d_name,
+ (unsigned)namlen)) {
+ /*
+ * Save directory entry's inode number and
+ * reclen in ndp->ni_ufs area, and release
+ * directory buffer.
+ */
+ dp->i_ino = ep->d_ino;
+ dp->i_reclen = ep->d_reclen;
+ brelse(bp);
+ goto found;
+ }
+ }
+ prevoff = dp->i_offset;
+ dp->i_offset += ep->d_reclen;
+ entryoffsetinblock += ep->d_reclen;
+ if (ep->d_ino)
+ enduseful = dp->i_offset;
+ }
+/* notfound: */
+ /*
+ * If we started in the middle of the directory and failed
+ * to find our target, we must check the beginning as well.
+ */
+ if (numdirpasses == 2) {
+ numdirpasses--;
+ dp->i_offset = 0;
+ endsearch = dp->i_diroff;
+ goto searchloop;
+ }
+ if (bp != NULL)
+ brelse(bp);
+ /*
+ * If creating, and at end of pathname and current
+ * directory has not been removed, then can consider
+ * allowing file to be created.
+ */
+ if ((nameiop == CREATE || nameiop == RENAME) &&
+ (flags & ISLASTCN) && dp->i_nlink != 0) {
+ /*
+ * Access for write is interpreted as allowing
+ * creation of files in the directory.
+ */
+ if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc))
+ return (error);
+ /*
+ * Return an indication of where the new directory
+ * entry should be put. If we didn't find a slot,
+ * then set dp->i_count to 0 indicating
+ * that the new slot belongs at the end of the
+ * directory. If we found a slot, then the new entry
+ * can be put in the range from dp->i_offset to
+ * dp->i_offset + dp->i_count.
+ */
+ if (slotstatus == NONE) {
+ dp->i_offset = roundup(dp->i_size, DIRBLKSIZ);
+ dp->i_count = 0;
+ enduseful = dp->i_offset;
+ } else {
+ dp->i_offset = slotoffset;
+ dp->i_count = slotsize;
+ if (enduseful < slotoffset + slotsize)
+ enduseful = slotoffset + slotsize;
+ }
+ dp->i_endoff = roundup(enduseful, DIRBLKSIZ);
+ dp->i_flag |= IN_CHANGE | IN_UPDATE;
+ /*
+ * We return with the directory locked, so that
+ * the parameters we set up above will still be
+ * valid if we actually decide to do a direnter().
+ * We return ni_vp == NULL to indicate that the entry
+ * does not currently exist; we leave a pointer to
+ * the (locked) directory inode in ndp->ni_dvp.
+ * The pathname buffer is saved so that the name
+ * can be obtained later.
+ *
+ * NB - if the directory is unlocked, then this
+ * information cannot be used.
+ */
+ cnp->cn_flags |= SAVENAME;
+ if (!lockparent)
+ VOP_UNLOCK(vdp);
+ return (EJUSTRETURN);
+ }
+ /*
+ * Insert name into cache (as non-existent) if appropriate.
+ */
+ if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
+ cache_enter(vdp, *vpp, cnp);
+ return (ENOENT);
+
+found:
+ if (numdirpasses == 2)
+ nchstats.ncs_pass2++;
+ /*
+ * Check that directory length properly reflects presence
+ * of this entry.
+ */
+ if (entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep) > dp->i_size) {
+ ufs_dirbad(dp, dp->i_offset, "i_size too small");
+ dp->i_size = entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep);
+ dp->i_flag |= IN_CHANGE | IN_UPDATE;
+ }
+
+ /*
+ * Found component in pathname.
+ * If the final component of path name, save information
+ * in the cache as to where the entry was found.
+ */
+ if ((flags & ISLASTCN) && nameiop == LOOKUP)
+ dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1);
+
+ /*
+ * If deleting, and at end of pathname, return
+ * parameters which can be used to remove file.
+ * If the wantparent flag isn't set, we return only
+ * the directory (in ndp->ni_dvp), otherwise we go
+ * on and lock the inode, being careful with ".".
+ */
+ if (nameiop == DELETE && (flags & ISLASTCN)) {
+ /*
+ * Write access to directory required to delete files.
+ */
+ if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc))
+ return (error);
+ /*
+ * Return pointer to current entry in dp->i_offset,
+ * and distance past previous entry (if there
+ * is a previous entry in this block) in dp->i_count.
+ * Save directory inode pointer in ndp->ni_dvp for dirremove().
+ */
+ if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
+ dp->i_count = 0;
+ else
+ dp->i_count = dp->i_offset - prevoff;
+ if (dp->i_number == dp->i_ino) {
+ VREF(vdp);
+ *vpp = vdp;
+ return (0);
+ }
+ if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp))
+ return (error);
+ /*
+ * If directory is "sticky", then user must own
+ * the directory, or the file in it, else she
+ * may not delete it (unless she's root). This
+ * implements append-only directories.
+ */
+ if ((dp->i_mode & ISVTX) &&
+ cred->cr_uid != 0 &&
+ cred->cr_uid != dp->i_uid &&
+ VTOI(tdp)->i_uid != cred->cr_uid) {
+ vput(tdp);
+ return (EPERM);
+ }
+ *vpp = tdp;
+ if (!lockparent)
+ VOP_UNLOCK(vdp);
+ return (0);
+ }
+
+ /*
+ * If rewriting (RENAME), return the inode and the
+ * information required to rewrite the present directory
+ * Must get inode of directory entry to verify it's a
+ * regular file, or empty directory.
+ */
+ if (nameiop == RENAME && wantparent &&
+ (flags & ISLASTCN)) {
+ if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc))
+ return (error);
+ /*
+ * Careful about locking second inode.
+ * This can only occur if the target is ".".
+ */
+ if (dp->i_number == dp->i_ino)
+ return (EISDIR);
+ if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp))
+ return (error);
+ *vpp = tdp;
+ cnp->cn_flags |= SAVENAME;
+ if (!lockparent)
+ VOP_UNLOCK(vdp);
+ return (0);
+ }
+
+ /*
+ * Step through the translation in the name. We do not `vput' the
+ * directory because we may need it again if a symbolic link
+ * is relative to the current directory. Instead we save it
+ * unlocked as "pdp". We must get the target inode before unlocking
+ * the directory to insure that the inode will not be removed
+ * before we get it. We prevent deadlock by always fetching
+ * inodes from the root, moving down the directory tree. Thus
+ * when following backward pointers ".." we must unlock the
+ * parent directory before getting the requested directory.
+ * There is a potential race condition here if both the current
+ * and parent directories are removed before the VFS_VGET for the
+ * inode associated with ".." returns. We hope that this occurs
+ * infrequently since we cannot avoid this race condition without
+ * implementing a sophisticated deadlock detection algorithm.
+ * Note also that this simple deadlock detection scheme will not
+ * work if the file system has any hard links other than ".."
+ * that point backwards in the directory structure.
+ */
+ pdp = vdp;
+ if (flags & ISDOTDOT) {
+ VOP_UNLOCK(pdp); /* race to get the inode */
+ if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) {
+ VOP_LOCK(pdp);
+ return (error);
+ }
+ if (lockparent && (flags & ISLASTCN) &&
+ (error = VOP_LOCK(pdp))) {
+ vput(tdp);
+ return (error);
+ }
+ *vpp = tdp;
+ } else if (dp->i_number == dp->i_ino) {
+ VREF(vdp); /* we want ourself, ie "." */
+ *vpp = vdp;
+ } else {
+ if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp))
+ return (error);
+ if (!lockparent || !(flags & ISLASTCN))
+ VOP_UNLOCK(pdp);
+ *vpp = tdp;
+ }
+
+ /*
+ * Insert name into cache if appropriate.
+ */
+ if (cnp->cn_flags & MAKEENTRY)
+ cache_enter(vdp, *vpp, cnp);
+ return (0);
+}
+
+void
+ufs_dirbad(ip, offset, how)
+ struct inode *ip;
+ doff_t offset;
+ char *how;
+{
+ struct mount *mp;
+
+ mp = ITOV(ip)->v_mount;
+ (void)printf("%s: bad dir ino %d at offset %d: %s\n",
+ mp->mnt_stat.f_mntonname, ip->i_number, offset, how);
+ if ((mp->mnt_stat.f_flags & MNT_RDONLY) == 0)
+ panic("bad dir");
+}
+
+/*
+ * Do consistency checking on a directory entry:
+ * record length must be multiple of 4
+ * entry must fit in rest of its DIRBLKSIZ block
+ * record must be large enough to contain entry
+ * name is not longer than MAXNAMLEN
+ * name must be as long as advertised, and null terminated
+ */
+int
+ufs_dirbadentry(dp, ep, entryoffsetinblock)
+ struct vnode *dp;
+ register struct direct *ep;
+ int entryoffsetinblock;
+{
+ register int i;
+ int namlen;
+
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ if (dp->v_mount->mnt_maxsymlinklen > 0)
+ namlen = ep->d_namlen;
+ else
+ namlen = ep->d_type;
+# else
+ namlen = ep->d_namlen;
+# endif
+ if ((ep->d_reclen & 0x3) != 0 ||
+ ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) ||
+ ep->d_reclen < DIRSIZ(FSFMT(dp), ep) || namlen > MAXNAMLEN) {
+ /*return (1); */
+ printf("First bad\n");
+ goto bad;
+ }
+ for (i = 0; i < namlen; i++)
+ if (ep->d_name[i] == '\0') {
+ /*return (1); */
+ printf("Second bad\n");
+ goto bad;
+ }
+ if (ep->d_name[i])
+ goto bad;
+ return (ep->d_name[i]);
+bad:
+ return(1);
+}
+
+/*
+ * Write a directory entry after a call to namei, using the parameters
+ * that it left in nameidata. The argument ip is the inode which the new
+ * directory entry will refer to. Dvp is a pointer to the directory to
+ * be written, which was left locked by namei. Remaining parameters
+ * (dp->i_offset, dp->i_count) indicate how the space for the new
+ * entry is to be obtained.
+ */
+int
+ufs_direnter(ip, dvp, cnp)
+ struct inode *ip;
+ struct vnode *dvp;
+ register struct componentname *cnp;
+{
+ register struct direct *ep, *nep;
+ register struct inode *dp;
+ struct buf *bp;
+ struct direct newdir;
+ struct iovec aiov;
+ struct uio auio;
+ u_int dsize;
+ int error, loc, newentrysize, spacefree;
+ char *dirbuf;
+
+#ifdef DIAGNOSTIC
+ if ((cnp->cn_flags & SAVENAME) == 0)
+ panic("direnter: missing name");
+#endif
+ dp = VTOI(dvp);
+ newdir.d_ino = ip->i_number;
+ newdir.d_namlen = cnp->cn_namelen;
+ bcopy(cnp->cn_nameptr, newdir.d_name, (unsigned)cnp->cn_namelen + 1);
+ if (dvp->v_mount->mnt_maxsymlinklen > 0)
+ newdir.d_type = IFTODT(ip->i_mode);
+ else {
+ newdir.d_type = 0;
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ { u_char tmp = newdir.d_namlen;
+ newdir.d_namlen = newdir.d_type;
+ newdir.d_type = tmp; }
+# endif
+ }
+ newentrysize = DIRSIZ(FSFMT(dvp), &newdir);
+ if (dp->i_count == 0) {
+ /*
+ * If dp->i_count is 0, then namei could find no
+ * space in the directory. Here, dp->i_offset will
+ * be on a directory block boundary and we will write the
+ * new entry into a fresh block.
+ */
+ if (dp->i_offset & (DIRBLKSIZ - 1))
+ panic("ufs_direnter: newblk");
+ auio.uio_offset = dp->i_offset;
+ newdir.d_reclen = DIRBLKSIZ;
+ auio.uio_resid = newentrysize;
+ aiov.iov_len = newentrysize;
+ aiov.iov_base = (caddr_t)&newdir;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_rw = UIO_WRITE;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_procp = (struct proc *)0;
+ error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
+ if (DIRBLKSIZ >
+ VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
+ /* XXX should grow with balloc() */
+ panic("ufs_direnter: frag size");
+ else if (!error) {
+ dp->i_size = roundup(dp->i_size, DIRBLKSIZ);
+ dp->i_flag |= IN_CHANGE;
+ }
+ return (error);
+ }
+
+ /*
+ * If dp->i_count is non-zero, then namei found space
+ * for the new entry in the range dp->i_offset to
+ * dp->i_offset + dp->i_count in the directory.
+ * To use this space, we may have to compact the entries located
+ * there, by copying them together towards the beginning of the
+ * block, leaving the free space in one usable chunk at the end.
+ */
+
+ /*
+ * Increase size of directory if entry eats into new space.
+ * This should never push the size past a new multiple of
+ * DIRBLKSIZE.
+ *
+ * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
+ */
+ if (dp->i_offset + dp->i_count > dp->i_size)
+ dp->i_size = dp->i_offset + dp->i_count;
+ /*
+ * Get the block containing the space for the new directory entry.
+ */
+ if (error = VOP_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp))
+ return (error);
+ /*
+ * Find space for the new entry. In the simple case, the entry at
+ * offset base will have the space. If it does not, then namei
+ * arranged that compacting the region dp->i_offset to
+ * dp->i_offset + dp->i_count would yield the
+ * space.
+ */
+ ep = (struct direct *)dirbuf;
+ dsize = DIRSIZ(FSFMT(dvp), ep);
+ spacefree = ep->d_reclen - dsize;
+ for (loc = ep->d_reclen; loc < dp->i_count; ) {
+ nep = (struct direct *)(dirbuf + loc);
+ if (ep->d_ino) {
+ /* trim the existing slot */
+ ep->d_reclen = dsize;
+ ep = (struct direct *)((char *)ep + dsize);
+ } else {
+ /* overwrite; nothing there; header is ours */
+ spacefree += dsize;
+ }
+ dsize = DIRSIZ(FSFMT(dvp), nep);
+ spacefree += nep->d_reclen - dsize;
+ loc += nep->d_reclen;
+ bcopy((caddr_t)nep, (caddr_t)ep, dsize);
+ }
+ /*
+ * Update the pointer fields in the previous entry (if any),
+ * copy in the new entry, and write out the block.
+ */
+ if (ep->d_ino == 0) {
+ if (spacefree + dsize < newentrysize)
+ panic("ufs_direnter: compact1");
+ newdir.d_reclen = spacefree + dsize;
+ } else {
+ if (spacefree < newentrysize)
+ panic("ufs_direnter: compact2");
+ newdir.d_reclen = spacefree;
+ ep->d_reclen = dsize;
+ ep = (struct direct *)((char *)ep + dsize);
+ }
+ bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize);
+ error = VOP_BWRITE(bp);
+ dp->i_flag |= IN_CHANGE | IN_UPDATE;
+ if (!error && dp->i_endoff && dp->i_endoff < dp->i_size)
+ error = VOP_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC,
+ cnp->cn_cred, cnp->cn_proc);
+ return (error);
+}
+
+/*
+ * Remove a directory entry after a call to namei, using
+ * the parameters which it left in nameidata. The entry
+ * dp->i_offset contains the offset into the directory of the
+ * entry to be eliminated. The dp->i_count field contains the
+ * size of the previous record in the directory. If this
+ * is 0, the first entry is being deleted, so we need only
+ * zero the inode number to mark the entry as free. If the
+ * entry is not the first in the directory, we must reclaim
+ * the space of the now empty record by adding the record size
+ * to the size of the previous entry.
+ */
+int
+ufs_dirremove(dvp, cnp)
+ struct vnode *dvp;
+ struct componentname *cnp;
+{
+ register struct inode *dp;
+ struct direct *ep;
+ struct buf *bp;
+ int error;
+
+ dp = VTOI(dvp);
+ if (dp->i_count == 0) {
+ /*
+ * First entry in block: set d_ino to zero.
+ */
+ if (error =
+ VOP_BLKATOFF(dvp, (off_t)dp->i_offset, (char **)&ep, &bp))
+ return (error);
+ ep->d_ino = 0;
+ error = VOP_BWRITE(bp);
+ dp->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (error);
+ }
+ /*
+ * Collapse new free space into previous entry.
+ */
+ if (error = VOP_BLKATOFF(dvp, (off_t)(dp->i_offset - dp->i_count),
+ (char **)&ep, &bp))
+ return (error);
+ ep->d_reclen += dp->i_reclen;
+ error = VOP_BWRITE(bp);
+ dp->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (error);
+}
+
+/*
+ * Rewrite an existing directory entry to point at the inode
+ * supplied. The parameters describing the directory entry are
+ * set up by a call to namei.
+ */
+int
+ufs_dirrewrite(dp, ip, cnp)
+ struct inode *dp, *ip;
+ struct componentname *cnp;
+{
+ struct buf *bp;
+ struct direct *ep;
+ struct vnode *vdp = ITOV(dp);
+ int error;
+
+ if (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, (char **)&ep, &bp))
+ return (error);
+ ep->d_ino = ip->i_number;
+ if (vdp->v_mount->mnt_maxsymlinklen > 0)
+ ep->d_type = IFTODT(ip->i_mode);
+ error = VOP_BWRITE(bp);
+ dp->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (error);
+}
+
+/*
+ * Check if a directory is empty or not.
+ * Inode supplied must be locked.
+ *
+ * Using a struct dirtemplate here is not precisely
+ * what we want, but better than using a struct direct.
+ *
+ * NB: does not handle corrupted directories.
+ */
+int
+ufs_dirempty(ip, parentino, cred)
+ register struct inode *ip;
+ ino_t parentino;
+ struct ucred *cred;
+{
+ register off_t off;
+ struct dirtemplate dbuf;
+ register struct direct *dp = (struct direct *)&dbuf;
+ int error, count, namlen;
+#define MINDIRSIZ (sizeof (struct dirtemplate) / 2)
+
+ for (off = 0; off < ip->i_size; off += dp->d_reclen) {
+ error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off,
+ UIO_SYSSPACE, IO_NODELOCKED, cred, &count, (struct proc *)0);
+ /*
+ * Since we read MINDIRSIZ, residual must
+ * be 0 unless we're at end of file.
+ */
+ if (error || count != 0)
+ return (0);
+ /* avoid infinite loops */
+ if (dp->d_reclen == 0)
+ return (0);
+ /* skip empty entries */
+ if (dp->d_ino == 0)
+ continue;
+ /* accept only "." and ".." */
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0)
+ namlen = dp->d_namlen;
+ else
+ namlen = dp->d_type;
+# else
+ namlen = dp->d_namlen;
+# endif
+ if (namlen > 2)
+ return (0);
+ if (dp->d_name[0] != '.')
+ return (0);
+ /*
+ * At this point namlen must be 1 or 2.
+ * 1 implies ".", 2 implies ".." if second
+ * char is also "."
+ */
+ if (namlen == 1)
+ continue;
+ if (dp->d_name[1] == '.' && dp->d_ino == parentino)
+ continue;
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Check if source directory is in the path of the target directory.
+ * Target is supplied locked, source is unlocked.
+ * The target is always vput before returning.
+ */
+int
+ufs_checkpath(source, target, cred)
+ struct inode *source, *target;
+ struct ucred *cred;
+{
+ struct vnode *vp;
+ int error, rootino, namlen;
+ struct dirtemplate dirbuf;
+
+ vp = ITOV(target);
+ if (target->i_number == source->i_number) {
+ error = EEXIST;
+ goto out;
+ }
+ rootino = ROOTINO;
+ error = 0;
+ if (target->i_number == rootino)
+ goto out;
+
+ for (;;) {
+ if (vp->v_type != VDIR) {
+ error = ENOTDIR;
+ break;
+ }
+ error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
+ sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
+ IO_NODELOCKED, cred, (int *)0, (struct proc *)0);
+ if (error != 0)
+ break;
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ if (vp->v_mount->mnt_maxsymlinklen > 0)
+ namlen = dirbuf.dotdot_namlen;
+ else
+ namlen = dirbuf.dotdot_type;
+# else
+ namlen = dirbuf.dotdot_namlen;
+# endif
+ if (namlen != 2 ||
+ dirbuf.dotdot_name[0] != '.' ||
+ dirbuf.dotdot_name[1] != '.') {
+ error = ENOTDIR;
+ break;
+ }
+ if (dirbuf.dotdot_ino == source->i_number) {
+ error = EINVAL;
+ break;
+ }
+ if (dirbuf.dotdot_ino == rootino)
+ break;
+ vput(vp);
+ if (error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, &vp)) {
+ vp = NULL;
+ break;
+ }
+ }
+
+out:
+ if (error == ENOTDIR)
+ printf("checkpath: .. not a directory\n");
+ if (vp != NULL)
+ vput(vp);
+ return (error);
+}
diff --git a/sys/ufs/ufs/ufs_quota.c b/sys/ufs/ufs/ufs_quota.c
new file mode 100644
index 0000000..15cb1cf
--- /dev/null
+++ b/sys/ufs/ufs/ufs_quota.c
@@ -0,0 +1,938 @@
+/*
+ * Copyright (c) 1982, 1986, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_quota.c 8.2 (Berkeley) 12/30/93
+ */
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/file.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+/*
+ * Quota name to error message mapping.
+ */
+static char *quotatypes[] = INITQFNAMES;
+
+/*
+ * Set up the quotas for an inode.
+ *
+ * This routine completely defines the semantics of quotas.
+ * If other criterion want to be used to establish quotas, the
+ * MAXQUOTAS value in quotas.h should be increased, and the
+ * additional dquots set up here.
+ */
+int
+getinoquota(ip)
+ register struct inode *ip;
+{
+ struct ufsmount *ump;
+ struct vnode *vp = ITOV(ip);
+ int error;
+
+ ump = VFSTOUFS(vp->v_mount);
+ /*
+ * Set up the user quota based on file uid.
+ * EINVAL means that quotas are not enabled.
+ */
+ if (ip->i_dquot[USRQUOTA] == NODQUOT &&
+ (error =
+ dqget(vp, ip->i_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) &&
+ error != EINVAL)
+ return (error);
+ /*
+ * Set up the group quota based on file gid.
+ * EINVAL means that quotas are not enabled.
+ */
+ if (ip->i_dquot[GRPQUOTA] == NODQUOT &&
+ (error =
+ dqget(vp, ip->i_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) &&
+ error != EINVAL)
+ return (error);
+ return (0);
+}
+
+/*
+ * Update disk usage, and take corrective action.
+ */
+int
+chkdq(ip, change, cred, flags)
+ register struct inode *ip;
+ long change;
+ struct ucred *cred;
+ int flags;
+{
+ register struct dquot *dq;
+ register int i;
+ int ncurblocks, error;
+
+#ifdef DIAGNOSTIC
+ if ((flags & CHOWN) == 0)
+ chkdquot(ip);
+#endif
+ if (change == 0)
+ return (0);
+ if (change < 0) {
+ for (i = 0; i < MAXQUOTAS; i++) {
+ if ((dq = ip->i_dquot[i]) == NODQUOT)
+ continue;
+ while (dq->dq_flags & DQ_LOCK) {
+ dq->dq_flags |= DQ_WANT;
+ sleep((caddr_t)dq, PINOD+1);
+ }
+ ncurblocks = dq->dq_curblocks + change;
+ if (ncurblocks >= 0)
+ dq->dq_curblocks = ncurblocks;
+ else
+ dq->dq_curblocks = 0;
+ dq->dq_flags &= ~DQ_BLKS;
+ dq->dq_flags |= DQ_MOD;
+ }
+ return (0);
+ }
+ if ((flags & FORCE) == 0 && cred->cr_uid != 0) {
+ for (i = 0; i < MAXQUOTAS; i++) {
+ if ((dq = ip->i_dquot[i]) == NODQUOT)
+ continue;
+ if (error = chkdqchg(ip, change, cred, i))
+ return (error);
+ }
+ }
+ for (i = 0; i < MAXQUOTAS; i++) {
+ if ((dq = ip->i_dquot[i]) == NODQUOT)
+ continue;
+ while (dq->dq_flags & DQ_LOCK) {
+ dq->dq_flags |= DQ_WANT;
+ sleep((caddr_t)dq, PINOD+1);
+ }
+ dq->dq_curblocks += change;
+ dq->dq_flags |= DQ_MOD;
+ }
+ return (0);
+}
+
+/*
+ * Check for a valid change to a users allocation.
+ * Issue an error message if appropriate.
+ */
+int
+chkdqchg(ip, change, cred, type)
+ struct inode *ip;
+ long change;
+ struct ucred *cred;
+ int type;
+{
+ register struct dquot *dq = ip->i_dquot[type];
+ long ncurblocks = dq->dq_curblocks + change;
+
+ /*
+ * If user would exceed their hard limit, disallow space allocation.
+ */
+ if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
+ if ((dq->dq_flags & DQ_BLKS) == 0 &&
+ ip->i_uid == cred->cr_uid) {
+ uprintf("\n%s: write failed, %s disk limit reached\n",
+ ITOV(ip)->v_mount->mnt_stat.f_mntonname,
+ quotatypes[type]);
+ dq->dq_flags |= DQ_BLKS;
+ }
+ return (EDQUOT);
+ }
+ /*
+ * If user is over their soft limit for too long, disallow space
+ * allocation. Reset time limit as they cross their soft limit.
+ */
+ if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
+ if (dq->dq_curblocks < dq->dq_bsoftlimit) {
+ dq->dq_btime = time.tv_sec +
+ VFSTOUFS(ITOV(ip)->v_mount)->um_btime[type];
+ if (ip->i_uid == cred->cr_uid)
+ uprintf("\n%s: warning, %s %s\n",
+ ITOV(ip)->v_mount->mnt_stat.f_mntonname,
+ quotatypes[type], "disk quota exceeded");
+ return (0);
+ }
+ if (time.tv_sec > dq->dq_btime) {
+ if ((dq->dq_flags & DQ_BLKS) == 0 &&
+ ip->i_uid == cred->cr_uid) {
+ uprintf("\n%s: write failed, %s %s\n",
+ ITOV(ip)->v_mount->mnt_stat.f_mntonname,
+ quotatypes[type],
+ "disk quota exceeded for too long");
+ dq->dq_flags |= DQ_BLKS;
+ }
+ return (EDQUOT);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Check the inode limit, applying corrective action.
+ */
+int
+chkiq(ip, change, cred, flags)
+ register struct inode *ip;
+ long change;
+ struct ucred *cred;
+ int flags;
+{
+ register struct dquot *dq;
+ register int i;
+ int ncurinodes, error;
+
+#ifdef DIAGNOSTIC
+ if ((flags & CHOWN) == 0)
+ chkdquot(ip);
+#endif
+ if (change == 0)
+ return (0);
+ if (change < 0) {
+ for (i = 0; i < MAXQUOTAS; i++) {
+ if ((dq = ip->i_dquot[i]) == NODQUOT)
+ continue;
+ while (dq->dq_flags & DQ_LOCK) {
+ dq->dq_flags |= DQ_WANT;
+ sleep((caddr_t)dq, PINOD+1);
+ }
+ ncurinodes = dq->dq_curinodes + change;
+ if (ncurinodes >= 0)
+ dq->dq_curinodes = ncurinodes;
+ else
+ dq->dq_curinodes = 0;
+ dq->dq_flags &= ~DQ_INODS;
+ dq->dq_flags |= DQ_MOD;
+ }
+ return (0);
+ }
+ if ((flags & FORCE) == 0 && cred->cr_uid != 0) {
+ for (i = 0; i < MAXQUOTAS; i++) {
+ if ((dq = ip->i_dquot[i]) == NODQUOT)
+ continue;
+ if (error = chkiqchg(ip, change, cred, i))
+ return (error);
+ }
+ }
+ for (i = 0; i < MAXQUOTAS; i++) {
+ if ((dq = ip->i_dquot[i]) == NODQUOT)
+ continue;
+ while (dq->dq_flags & DQ_LOCK) {
+ dq->dq_flags |= DQ_WANT;
+ sleep((caddr_t)dq, PINOD+1);
+ }
+ dq->dq_curinodes += change;
+ dq->dq_flags |= DQ_MOD;
+ }
+ return (0);
+}
+
+/*
+ * Check for a valid change to a users allocation.
+ * Issue an error message if appropriate.
+ */
+int
+chkiqchg(ip, change, cred, type)
+ struct inode *ip;
+ long change;
+ struct ucred *cred;
+ int type;
+{
+ register struct dquot *dq = ip->i_dquot[type];
+ long ncurinodes = dq->dq_curinodes + change;
+
+ /*
+ * If user would exceed their hard limit, disallow inode allocation.
+ */
+ if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
+ if ((dq->dq_flags & DQ_INODS) == 0 &&
+ ip->i_uid == cred->cr_uid) {
+ uprintf("\n%s: write failed, %s inode limit reached\n",
+ ITOV(ip)->v_mount->mnt_stat.f_mntonname,
+ quotatypes[type]);
+ dq->dq_flags |= DQ_INODS;
+ }
+ return (EDQUOT);
+ }
+ /*
+ * If user is over their soft limit for too long, disallow inode
+ * allocation. Reset time limit as they cross their soft limit.
+ */
+ if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
+ if (dq->dq_curinodes < dq->dq_isoftlimit) {
+ dq->dq_itime = time.tv_sec +
+ VFSTOUFS(ITOV(ip)->v_mount)->um_itime[type];
+ if (ip->i_uid == cred->cr_uid)
+ uprintf("\n%s: warning, %s %s\n",
+ ITOV(ip)->v_mount->mnt_stat.f_mntonname,
+ quotatypes[type], "inode quota exceeded");
+ return (0);
+ }
+ if (time.tv_sec > dq->dq_itime) {
+ if ((dq->dq_flags & DQ_INODS) == 0 &&
+ ip->i_uid == cred->cr_uid) {
+ uprintf("\n%s: write failed, %s %s\n",
+ ITOV(ip)->v_mount->mnt_stat.f_mntonname,
+ quotatypes[type],
+ "inode quota exceeded for too long");
+ dq->dq_flags |= DQ_INODS;
+ }
+ return (EDQUOT);
+ }
+ }
+ return (0);
+}
+
+#ifdef DIAGNOSTIC
+/*
+ * On filesystems with quotas enabled, it is an error for a file to change
+ * size and not to have a dquot structure associated with it.
+ */
+void
+chkdquot(ip)
+ register struct inode *ip;
+{
+ struct ufsmount *ump = VFSTOUFS(ITOV(ip)->v_mount);
+ register int i;
+
+ for (i = 0; i < MAXQUOTAS; i++) {
+ if (ump->um_quotas[i] == NULLVP ||
+ (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING)))
+ continue;
+ if (ip->i_dquot[i] == NODQUOT) {
+ vprint("chkdquot: missing dquot", ITOV(ip));
+ panic("missing dquot");
+ }
+ }
+}
+#endif
+
+/*
+ * Code to process quotactl commands.
+ */
+
+/*
+ * Q_QUOTAON - set up a quota file for a particular file system.
+ */
+int
+quotaon(p, mp, type, fname)
+ struct proc *p;
+ struct mount *mp;
+ register int type;
+ caddr_t fname;
+{
+ register struct ufsmount *ump = VFSTOUFS(mp);
+ register struct vnode *vp, **vpp;
+ struct vnode *nextvp;
+ struct dquot *dq;
+ int error;
+ struct nameidata nd;
+
+ vpp = &ump->um_quotas[type];
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, p);
+ if (error = vn_open(&nd, FREAD|FWRITE, 0))
+ return (error);
+ vp = nd.ni_vp;
+ VOP_UNLOCK(vp);
+ if (vp->v_type != VREG) {
+ (void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
+ return (EACCES);
+ }
+ if (vfs_busy(mp)) {
+ (void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
+ return (EBUSY);
+ }
+ if (*vpp != vp)
+ quotaoff(p, mp, type);
+ ump->um_qflags[type] |= QTF_OPENING;
+ mp->mnt_flag |= MNT_QUOTA;
+ vp->v_flag |= VSYSTEM;
+ *vpp = vp;
+ /*
+ * Save the credential of the process that turned on quotas.
+ * Set up the time limits for this quota.
+ */
+ crhold(p->p_ucred);
+ ump->um_cred[type] = p->p_ucred;
+ ump->um_btime[type] = MAX_DQ_TIME;
+ ump->um_itime[type] = MAX_IQ_TIME;
+ if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
+ if (dq->dq_btime > 0)
+ ump->um_btime[type] = dq->dq_btime;
+ if (dq->dq_itime > 0)
+ ump->um_itime[type] = dq->dq_itime;
+ dqrele(NULLVP, dq);
+ }
+ /*
+ * Search vnodes associated with this mount point,
+ * adding references to quota file being opened.
+ * NB: only need to add dquot's for inodes being modified.
+ */
+again:
+ for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) {
+ nextvp = vp->v_mntvnodes.le_next;
+ if (vp->v_writecount == 0)
+ continue;
+ if (vget(vp, 1))
+ goto again;
+ if (error = getinoquota(VTOI(vp))) {
+ vput(vp);
+ break;
+ }
+ vput(vp);
+ if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp)
+ goto again;
+ }
+ ump->um_qflags[type] &= ~QTF_OPENING;
+ if (error)
+ quotaoff(p, mp, type);
+ vfs_unbusy(mp);
+ return (error);
+}
+
+/*
+ * Q_QUOTAOFF - turn off disk quotas for a filesystem.
+ */
+int
+quotaoff(p, mp, type)
+ struct proc *p;
+ struct mount *mp;
+ register int type;
+{
+ register struct vnode *vp;
+ struct vnode *qvp, *nextvp;
+ struct ufsmount *ump = VFSTOUFS(mp);
+ register struct dquot *dq;
+ register struct inode *ip;
+ int error;
+
+ if ((mp->mnt_flag & MNT_MPBUSY) == 0)
+ panic("quotaoff: not busy");
+ if ((qvp = ump->um_quotas[type]) == NULLVP)
+ return (0);
+ ump->um_qflags[type] |= QTF_CLOSING;
+ /*
+ * Search vnodes associated with this mount point,
+ * deleting any references to quota file being closed.
+ */
+again:
+ for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) {
+ nextvp = vp->v_mntvnodes.le_next;
+ if (vget(vp, 1))
+ goto again;
+ ip = VTOI(vp);
+ dq = ip->i_dquot[type];
+ ip->i_dquot[type] = NODQUOT;
+ dqrele(vp, dq);
+ vput(vp);
+ if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp)
+ goto again;
+ }
+ dqflush(qvp);
+ qvp->v_flag &= ~VSYSTEM;
+ error = vn_close(qvp, FREAD|FWRITE, p->p_ucred, p);
+ ump->um_quotas[type] = NULLVP;
+ crfree(ump->um_cred[type]);
+ ump->um_cred[type] = NOCRED;
+ ump->um_qflags[type] &= ~QTF_CLOSING;
+ for (type = 0; type < MAXQUOTAS; type++)
+ if (ump->um_quotas[type] != NULLVP)
+ break;
+ if (type == MAXQUOTAS)
+ mp->mnt_flag &= ~MNT_QUOTA;
+ return (error);
+}
+
+/*
+ * Q_GETQUOTA - return current values in a dqblk structure.
+ */
+int
+getquota(mp, id, type, addr)
+ struct mount *mp;
+ u_long id;
+ int type;
+ caddr_t addr;
+{
+ struct dquot *dq;
+ int error;
+
+ if (error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq))
+ return (error);
+ error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk));
+ dqrele(NULLVP, dq);
+ return (error);
+}
+
+/*
+ * Q_SETQUOTA - assign an entire dqblk structure.
+ */
+int
+setquota(mp, id, type, addr)
+ struct mount *mp;
+ u_long id;
+ int type;
+ caddr_t addr;
+{
+ register struct dquot *dq;
+ struct dquot *ndq;
+ struct ufsmount *ump = VFSTOUFS(mp);
+ struct dqblk newlim;
+ int error;
+
+ if (error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk)))
+ return (error);
+ if (error = dqget(NULLVP, id, ump, type, &ndq))
+ return (error);
+ dq = ndq;
+ while (dq->dq_flags & DQ_LOCK) {
+ dq->dq_flags |= DQ_WANT;
+ sleep((caddr_t)dq, PINOD+1);
+ }
+ /*
+ * Copy all but the current values.
+ * Reset time limit if previously had no soft limit or were
+ * under it, but now have a soft limit and are over it.
+ */
+ newlim.dqb_curblocks = dq->dq_curblocks;
+ newlim.dqb_curinodes = dq->dq_curinodes;
+ if (dq->dq_id != 0) {
+ newlim.dqb_btime = dq->dq_btime;
+ newlim.dqb_itime = dq->dq_itime;
+ }
+ if (newlim.dqb_bsoftlimit &&
+ dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
+ (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
+ newlim.dqb_btime = time.tv_sec + ump->um_btime[type];
+ if (newlim.dqb_isoftlimit &&
+ dq->dq_curinodes >= newlim.dqb_isoftlimit &&
+ (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
+ newlim.dqb_itime = time.tv_sec + ump->um_itime[type];
+ dq->dq_dqb = newlim;
+ if (dq->dq_curblocks < dq->dq_bsoftlimit)
+ dq->dq_flags &= ~DQ_BLKS;
+ if (dq->dq_curinodes < dq->dq_isoftlimit)
+ dq->dq_flags &= ~DQ_INODS;
+ if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
+ dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
+ dq->dq_flags |= DQ_FAKE;
+ else
+ dq->dq_flags &= ~DQ_FAKE;
+ dq->dq_flags |= DQ_MOD;
+ dqrele(NULLVP, dq);
+ return (0);
+}
+
+/*
+ * Q_SETUSE - set current inode and block usage.
+ */
+int
+setuse(mp, id, type, addr)
+ struct mount *mp;
+ u_long id;
+ int type;
+ caddr_t addr;
+{
+ register struct dquot *dq;
+ struct ufsmount *ump = VFSTOUFS(mp);
+ struct dquot *ndq;
+ struct dqblk usage;
+ int error;
+
+ if (error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk)))
+ return (error);
+ if (error = dqget(NULLVP, id, ump, type, &ndq))
+ return (error);
+ dq = ndq;
+ while (dq->dq_flags & DQ_LOCK) {
+ dq->dq_flags |= DQ_WANT;
+ sleep((caddr_t)dq, PINOD+1);
+ }
+ /*
+ * Reset time limit if have a soft limit and were
+ * previously under it, but are now over it.
+ */
+ if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
+ usage.dqb_curblocks >= dq->dq_bsoftlimit)
+ dq->dq_btime = time.tv_sec + ump->um_btime[type];
+ if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
+ usage.dqb_curinodes >= dq->dq_isoftlimit)
+ dq->dq_itime = time.tv_sec + ump->um_itime[type];
+ dq->dq_curblocks = usage.dqb_curblocks;
+ dq->dq_curinodes = usage.dqb_curinodes;
+ if (dq->dq_curblocks < dq->dq_bsoftlimit)
+ dq->dq_flags &= ~DQ_BLKS;
+ if (dq->dq_curinodes < dq->dq_isoftlimit)
+ dq->dq_flags &= ~DQ_INODS;
+ dq->dq_flags |= DQ_MOD;
+ dqrele(NULLVP, dq);
+ return (0);
+}
+
+/*
+ * Q_SYNC - sync quota files to disk.
+ */
+int
+qsync(mp)
+ struct mount *mp;
+{
+ struct ufsmount *ump = VFSTOUFS(mp);
+ register struct vnode *vp, *nextvp;
+ register struct dquot *dq;
+ register int i;
+
+ /*
+ * Check if the mount point has any quotas.
+ * If not, simply return.
+ */
+ if ((mp->mnt_flag & MNT_MPBUSY) == 0)
+ panic("qsync: not busy");
+ for (i = 0; i < MAXQUOTAS; i++)
+ if (ump->um_quotas[i] != NULLVP)
+ break;
+ if (i == MAXQUOTAS)
+ return (0);
+ /*
+ * Search vnodes associated with this mount point,
+ * synchronizing any modified dquot structures.
+ */
+again:
+ for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) {
+ nextvp = vp->v_mntvnodes.le_next;
+ if (VOP_ISLOCKED(vp))
+ continue;
+ if (vget(vp, 1))
+ goto again;
+ for (i = 0; i < MAXQUOTAS; i++) {
+ dq = VTOI(vp)->i_dquot[i];
+ if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
+ dqsync(vp, dq);
+ }
+ vput(vp);
+ if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp)
+ goto again;
+ }
+ return (0);
+}
+
+/*
+ * Code pertaining to management of the in-core dquot data structures.
+ */
+struct dquot **dqhashtbl;
+u_long dqhash;
+
+/*
+ * Dquot free list.
+ */
+#define DQUOTINC 5 /* minimum free dquots desired */
+struct dquot *dqfreel, **dqback = &dqfreel;
+long numdquot, desireddquot = DQUOTINC;
+
+/*
+ * Initialize the quota system.
+ */
+void
+dqinit()
+{
+
+ dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
+}
+
+/*
+ * Obtain a dquot structure for the specified identifier and quota file
+ * reading the information from the file if necessary.
+ */
+int
+dqget(vp, id, ump, type, dqp)
+ struct vnode *vp;
+ u_long id;
+ register struct ufsmount *ump;
+ register int type;
+ struct dquot **dqp;
+{
+ register struct dquot *dq, *dp, **dpp;
+ register struct vnode *dqvp;
+ struct iovec aiov;
+ struct uio auio;
+ int error;
+
+ dqvp = ump->um_quotas[type];
+ if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
+ *dqp = NODQUOT;
+ return (EINVAL);
+ }
+ /*
+ * Check the cache first.
+ */
+ dpp = &dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash];
+ for (dq = *dpp; dq; dq = dq->dq_forw) {
+ if (dq->dq_id != id ||
+ dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
+ continue;
+ /*
+ * Cache hit with no references. Take
+ * the structure off the free list.
+ */
+ if (dq->dq_cnt == 0) {
+ if ((dp = dq->dq_freef) != NODQUOT)
+ dp->dq_freeb = dq->dq_freeb;
+ else
+ dqback = dq->dq_freeb;
+ *dq->dq_freeb = dp;
+ }
+ DQREF(dq);
+ *dqp = dq;
+ return (0);
+ }
+ /*
+ * Not in cache, allocate a new one.
+ */
+ if (dqfreel == NODQUOT && numdquot < MAXQUOTAS * desiredvnodes)
+ desireddquot += DQUOTINC;
+ if (numdquot < desireddquot) {
+ dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK);
+ bzero((char *)dq, sizeof *dq);
+ numdquot++;
+ } else {
+ if ((dq = dqfreel) == NULL) {
+ tablefull("dquot");
+ *dqp = NODQUOT;
+ return (EUSERS);
+ }
+ if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
+ panic("free dquot isn't");
+ if ((dp = dq->dq_freef) != NODQUOT)
+ dp->dq_freeb = &dqfreel;
+ else
+ dqback = &dqfreel;
+ dqfreel = dp;
+ dq->dq_freef = NULL;
+ dq->dq_freeb = NULL;
+ if (dp = dq->dq_forw)
+ dp->dq_back = dq->dq_back;
+ *dq->dq_back = dp;
+ }
+ /*
+ * Initialize the contents of the dquot structure.
+ */
+ if (vp != dqvp)
+ VOP_LOCK(dqvp);
+ if (dp = *dpp)
+ dp->dq_back = &dq->dq_forw;
+ dq->dq_forw = dp;
+ dq->dq_back = dpp;
+ *dpp = dq;
+ DQREF(dq);
+ dq->dq_flags = DQ_LOCK;
+ dq->dq_id = id;
+ dq->dq_ump = ump;
+ dq->dq_type = type;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ aiov.iov_base = (caddr_t)&dq->dq_dqb;
+ aiov.iov_len = sizeof (struct dqblk);
+ auio.uio_resid = sizeof (struct dqblk);
+ auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_READ;
+ auio.uio_procp = (struct proc *)0;
+ error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
+ if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
+ bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk));
+ if (vp != dqvp)
+ VOP_UNLOCK(dqvp);
+ if (dq->dq_flags & DQ_WANT)
+ wakeup((caddr_t)dq);
+ dq->dq_flags = 0;
+ /*
+ * I/O error in reading quota file, release
+ * quota structure and reflect problem to caller.
+ */
+ if (error) {
+ if (dp = dq->dq_forw)
+ dp->dq_back = dq->dq_back;
+ *dq->dq_back = dp;
+ dq->dq_forw = NULL;
+ dq->dq_back = NULL;
+ dqrele(vp, dq);
+ *dqp = NODQUOT;
+ return (error);
+ }
+ /*
+ * Check for no limit to enforce.
+ * Initialize time values if necessary.
+ */
+ if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
+ dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
+ dq->dq_flags |= DQ_FAKE;
+ if (dq->dq_id != 0) {
+ if (dq->dq_btime == 0)
+ dq->dq_btime = time.tv_sec + ump->um_btime[type];
+ if (dq->dq_itime == 0)
+ dq->dq_itime = time.tv_sec + ump->um_itime[type];
+ }
+ *dqp = dq;
+ return (0);
+}
+
+/*
+ * Obtain a reference to a dquot.
+ */
+void
+dqref(dq)
+ struct dquot *dq;
+{
+
+ dq->dq_cnt++;
+}
+
+/*
+ * Release a reference to a dquot.
+ */
+void
+dqrele(vp, dq)
+ struct vnode *vp;
+ register struct dquot *dq;
+{
+
+ if (dq == NODQUOT)
+ return;
+ if (dq->dq_cnt > 1) {
+ dq->dq_cnt--;
+ return;
+ }
+ if (dq->dq_flags & DQ_MOD)
+ (void) dqsync(vp, dq);
+ if (--dq->dq_cnt > 0)
+ return;
+ if (dqfreel != NODQUOT) {
+ *dqback = dq;
+ dq->dq_freeb = dqback;
+ } else {
+ dqfreel = dq;
+ dq->dq_freeb = &dqfreel;
+ }
+ dq->dq_freef = NODQUOT;
+ dqback = &dq->dq_freef;
+}
+
+/*
+ * Update the disk quota in the quota file.
+ */
+int
+dqsync(vp, dq)
+ struct vnode *vp;
+ register struct dquot *dq;
+{
+ struct vnode *dqvp;
+ struct iovec aiov;
+ struct uio auio;
+ int error;
+
+ if (dq == NODQUOT)
+ panic("dqsync: dquot");
+ if ((dq->dq_flags & DQ_MOD) == 0)
+ return (0);
+ if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
+ panic("dqsync: file");
+ if (vp != dqvp)
+ VOP_LOCK(dqvp);
+ while (dq->dq_flags & DQ_LOCK) {
+ dq->dq_flags |= DQ_WANT;
+ sleep((caddr_t)dq, PINOD+2);
+ if ((dq->dq_flags & DQ_MOD) == 0) {
+ if (vp != dqvp)
+ VOP_UNLOCK(dqvp);
+ return (0);
+ }
+ }
+ dq->dq_flags |= DQ_LOCK;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ aiov.iov_base = (caddr_t)&dq->dq_dqb;
+ aiov.iov_len = sizeof (struct dqblk);
+ auio.uio_resid = sizeof (struct dqblk);
+ auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_WRITE;
+ auio.uio_procp = (struct proc *)0;
+ error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
+ if (auio.uio_resid && error == 0)
+ error = EIO;
+ if (dq->dq_flags & DQ_WANT)
+ wakeup((caddr_t)dq);
+ dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
+ if (vp != dqvp)
+ VOP_UNLOCK(dqvp);
+ return (error);
+}
+
+/*
+ * Flush all entries from the cache for a particular vnode.
+ */
+void
+dqflush(vp)
+ register struct vnode *vp;
+{
+ register struct dquot *dq, *dp, **dpp, *nextdq;
+
+ /*
+ * Move all dquot's that used to refer to this quota
+ * file off their hash chains (they will eventually
+ * fall off the head of the free list and be re-used).
+ */
+ for (dpp = &dqhashtbl[dqhash]; dpp >= dqhashtbl; dpp--) {
+ for (dq = *dpp; dq; dq = nextdq) {
+ nextdq = dq->dq_forw;
+ if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
+ continue;
+ if (dq->dq_cnt)
+ panic("dqflush: stray dquot");
+ if (dp = dq->dq_forw)
+ dp->dq_back = dq->dq_back;
+ *dq->dq_back = dp;
+ dq->dq_forw = NULL;
+ dq->dq_back = NULL;
+ dq->dq_ump = (struct ufsmount *)0;
+ }
+ }
+}
diff --git a/sys/ufs/ufs/ufs_readwrite.c b/sys/ufs/ufs/ufs_readwrite.c
new file mode 100644
index 0000000..212210c
--- /dev/null
+++ b/sys/ufs/ufs/ufs_readwrite.c
@@ -0,0 +1,297 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_readwrite.c 8.7 (Berkeley) 1/21/94
+ */
+
+#ifdef LFS_READWRITE
+#define BLKSIZE(a, b, c) blksize(a)
+#define FS struct lfs
+#define I_FS i_lfs
+#define READ lfs_read
+#define READ_S "lfs_read"
+#define WRITE lfs_write
+#define WRITE_S "lfs_write"
+#define fs_bsize lfs_bsize
+#define fs_maxfilesize lfs_maxfilesize
+#else
+#define BLKSIZE(a, b, c) blksize(a, b, c)
+#define FS struct fs
+#define I_FS i_fs
+#define READ ffs_read
+#define READ_S "ffs_read"
+#define WRITE ffs_write
+#define WRITE_S "ffs_write"
+#endif
+
+/*
+ * Vnode op for reading.
+ */
+/* ARGSUSED */
+int
+READ(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct vnode *vp;
+ register struct inode *ip;
+ register struct uio *uio;
+ register FS *fs;
+ struct buf *bp;
+ daddr_t lbn, nextlbn;
+ off_t bytesinfile;
+ long size, xfersize, blkoffset;
+ int error;
+ u_short mode;
+
+ vp = ap->a_vp;
+ ip = VTOI(vp);
+ mode = ip->i_mode;
+ uio = ap->a_uio;
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_READ)
+ panic("%s: mode", READ_S);
+
+ if (vp->v_type == VLNK) {
+ if ((int)ip->i_size < vp->v_mount->mnt_maxsymlinklen)
+ panic("%s: short symlink", READ_S);
+ } else if (vp->v_type != VREG && vp->v_type != VDIR)
+ panic("%s: type %d", READ_S, vp->v_type);
+#endif
+ fs = ip->I_FS;
+ if ((u_quad_t)uio->uio_offset > fs->fs_maxfilesize)
+ return (EFBIG);
+
+ for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) {
+ if ((bytesinfile = ip->i_size - uio->uio_offset) <= 0)
+ break;
+ lbn = lblkno(fs, uio->uio_offset);
+ nextlbn = lbn + 1;
+ size = BLKSIZE(fs, ip, lbn);
+ blkoffset = blkoff(fs, uio->uio_offset);
+ xfersize = fs->fs_bsize - blkoffset;
+ if (uio->uio_resid < xfersize)
+ xfersize = uio->uio_resid;
+ if (bytesinfile < xfersize)
+ xfersize = bytesinfile;
+
+#ifdef LFS_READWRITE
+ (void)lfs_check(vp, lbn);
+ error = cluster_read(vp, ip->i_size, lbn, size, NOCRED, &bp);
+#else
+ if (lblktosize(fs, nextlbn) > ip->i_size)
+ error = bread(vp, lbn, size, NOCRED, &bp);
+ else if (doclusterread)
+ error = cluster_read(vp,
+ ip->i_size, lbn, size, NOCRED, &bp);
+ else if (lbn - 1 == vp->v_lastr) {
+ int nextsize = BLKSIZE(fs, ip, nextlbn);
+ error = breadn(vp, lbn,
+ size, &nextlbn, &nextsize, 1, NOCRED, &bp);
+ } else
+ error = bread(vp, lbn, size, NOCRED, &bp);
+#endif
+ if (error)
+ break;
+ vp->v_lastr = lbn;
+
+ /*
+ * We should only get non-zero b_resid when an I/O error
+ * has occurred, which should cause us to break above.
+ * However, if the short read did not cause an error,
+ * then we want to ensure that we do not uiomove bad
+ * or uninitialized data.
+ */
+ size -= bp->b_resid;
+ if (size < xfersize) {
+ if (size == 0)
+ break;
+ xfersize = size;
+ }
+ if (error =
+ uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio))
+ break;
+
+ if (S_ISREG(mode) && (xfersize + blkoffset == fs->fs_bsize ||
+ uio->uio_offset == ip->i_size))
+ bp->b_flags |= B_AGE;
+ brelse(bp);
+ }
+ if (bp != NULL)
+ brelse(bp);
+ ip->i_flag |= IN_ACCESS;
+ return (error);
+}
+
+/*
+ * Vnode op for writing.
+ */
+int
+WRITE(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct vnode *vp;
+ register struct uio *uio;
+ register struct inode *ip;
+ register FS *fs;
+ struct buf *bp;
+ struct proc *p;
+ daddr_t lbn;
+ off_t osize;
+ int blkoffset, error, flags, ioflag, resid, size, xfersize;
+
+ ioflag = ap->a_ioflag;
+ uio = ap->a_uio;
+ vp = ap->a_vp;
+ ip = VTOI(vp);
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_WRITE)
+ panic("%s: mode", WRITE_S);
+#endif
+
+ switch (vp->v_type) {
+ case VREG:
+ if (ioflag & IO_APPEND)
+ uio->uio_offset = ip->i_size;
+ if ((ip->i_flags & APPEND) && uio->uio_offset != ip->i_size)
+ return (EPERM);
+ /* FALLTHROUGH */
+ case VLNK:
+ break;
+ case VDIR:
+ if ((ioflag & IO_SYNC) == 0)
+ panic("%s: nonsync dir write", WRITE_S);
+ break;
+ default:
+ panic("%s: type", WRITE_S);
+ }
+
+ fs = ip->I_FS;
+ if (uio->uio_offset < 0 ||
+ (u_quad_t)uio->uio_offset + uio->uio_resid > fs->fs_maxfilesize)
+ return (EFBIG);
+ /*
+ * Maybe this should be above the vnode op call, but so long as
+ * file servers have no limits, I don't think it matters.
+ */
+ p = uio->uio_procp;
+ if (vp->v_type == VREG && p &&
+ uio->uio_offset + uio->uio_resid >
+ p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
+ psignal(p, SIGXFSZ);
+ return (EFBIG);
+ }
+
+ resid = uio->uio_resid;
+ osize = ip->i_size;
+ flags = ioflag & IO_SYNC ? B_SYNC : 0;
+
+ for (error = 0; uio->uio_resid > 0;) {
+ lbn = lblkno(fs, uio->uio_offset);
+ blkoffset = blkoff(fs, uio->uio_offset);
+ xfersize = fs->fs_bsize - blkoffset;
+ if (uio->uio_resid < xfersize)
+ xfersize = uio->uio_resid;
+#ifdef LFS_READWRITE
+ (void)lfs_check(vp, lbn);
+ error = lfs_balloc(vp, xfersize, lbn, &bp);
+#else
+ if (fs->fs_bsize > xfersize)
+ flags |= B_CLRBUF;
+ else
+ flags &= ~B_CLRBUF;
+
+ error = ffs_balloc(ip,
+ lbn, blkoffset + xfersize, ap->a_cred, &bp, flags);
+#endif
+ if (error)
+ break;
+ if (uio->uio_offset + xfersize > ip->i_size) {
+ ip->i_size = uio->uio_offset + xfersize;
+ vnode_pager_setsize(vp, (u_long)ip->i_size);
+ }
+ (void)vnode_pager_uncache(vp);
+
+ size = BLKSIZE(fs, ip, lbn) - bp->b_resid;
+ if (size < xfersize)
+ xfersize = size;
+
+ error =
+ uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio);
+#ifdef LFS_READWRITE
+ (void)VOP_BWRITE(bp);
+#else
+ if (ioflag & IO_SYNC)
+ (void)bwrite(bp);
+ else if (xfersize + blkoffset == fs->fs_bsize)
+ if (doclusterwrite)
+ cluster_write(bp, ip->i_size);
+ else {
+ bp->b_flags |= B_AGE;
+ bawrite(bp);
+ }
+ else
+ bdwrite(bp);
+#endif
+ if (error || xfersize == 0)
+ break;
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ }
+ /*
+ * If we successfully wrote any data, and we are not the superuser
+ * we clear the setuid and setgid bits as a precaution against
+ * tampering.
+ */
+ if (resid > uio->uio_resid && ap->a_cred && ap->a_cred->cr_uid != 0)
+ ip->i_mode &= ~(ISUID | ISGID);
+ if (error) {
+ if (ioflag & IO_UNIT) {
+ (void)VOP_TRUNCATE(vp, osize,
+ ioflag & IO_SYNC, ap->a_cred, uio->uio_procp);
+ uio->uio_offset -= resid - uio->uio_resid;
+ uio->uio_resid = resid;
+ }
+ } else if (resid > uio->uio_resid && (ioflag & IO_SYNC))
+ error = VOP_UPDATE(vp, &time, &time, 1);
+ return (error);
+}
diff --git a/sys/ufs/ufs/ufs_vfsops.c b/sys/ufs/ufs/ufs_vfsops.c
new file mode 100644
index 0000000..f806e0b
--- /dev/null
+++ b/sys/ufs/ufs/ufs_vfsops.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_vfsops.c 8.4 (Berkeley) 4/16/94
+ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+/*
+ * Flag to permit forcible unmounting.
+ */
+int doforce = 1;
+
+/*
+ * Make a filesystem operational.
+ * Nothing to do at the moment.
+ */
+/* ARGSUSED */
+int
+ufs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+
+ return (0);
+}
+
+/*
+ * Return the root of a filesystem.
+ */
+int
+ufs_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct vnode *nvp;
+ int error;
+
+ if (error = VFS_VGET(mp, (ino_t)ROOTINO, &nvp))
+ return (error);
+ *vpp = nvp;
+ return (0);
+}
+
+/*
+ * Do operations associated with quotas
+ */
+int
+ufs_quotactl(mp, cmds, uid, arg, p)
+ struct mount *mp;
+ int cmds;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+ int cmd, type, error;
+
+#ifndef QUOTA
+ return (EOPNOTSUPP);
+#else
+ if (uid == -1)
+ uid = p->p_cred->p_ruid;
+ cmd = cmds >> SUBCMDSHIFT;
+
+ switch (cmd) {
+ case Q_GETQUOTA:
+ case Q_SYNC:
+ if (uid == p->p_cred->p_ruid)
+ break;
+ /* fall through */
+ default:
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ }
+
+ type = cmd & SUBCMDMASK;
+ if ((u_int)type >= MAXQUOTAS)
+ return (EINVAL);
+
+ switch (cmd) {
+
+ case Q_QUOTAON:
+ return (quotaon(p, mp, type, arg));
+
+ case Q_QUOTAOFF:
+ if (vfs_busy(mp))
+ return (0);
+ error = quotaoff(p, mp, type);
+ vfs_unbusy(mp);
+ return (error);
+
+ case Q_SETQUOTA:
+ return (setquota(mp, uid, type, arg));
+
+ case Q_SETUSE:
+ return (setuse(mp, uid, type, arg));
+
+ case Q_GETQUOTA:
+ return (getquota(mp, uid, type, arg));
+
+ case Q_SYNC:
+ if (vfs_busy(mp))
+ return (0);
+ error = qsync(mp);
+ vfs_unbusy(mp);
+ return (error);
+
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+#endif
+}
+
+/*
+ * This is the generic part of fhtovp called after the underlying
+ * filesystem has validated the file handle.
+ *
+ * Verify that a host should have access to a filesystem, and if so
+ * return a vnode for the presented file handle.
+ */
+int
+ufs_check_export(mp, ufhp, nam, vpp, exflagsp, credanonp)
+ register struct mount *mp;
+ struct ufid *ufhp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred **credanonp;
+{
+ register struct inode *ip;
+ register struct netcred *np;
+ register struct ufsmount *ump = VFSTOUFS(mp);
+ struct vnode *nvp;
+ int error;
+
+ /*
+ * Get the export permission structure for this <mp, client> tuple.
+ */
+ np = vfs_export_lookup(mp, &ump->um_export, nam);
+ if (np == NULL)
+ return (EACCES);
+
+ if (error = VFS_VGET(mp, ufhp->ufid_ino, &nvp)) {
+ *vpp = NULLVP;
+ return (error);
+ }
+ ip = VTOI(nvp);
+ if (ip->i_mode == 0 || ip->i_gen != ufhp->ufid_gen) {
+ vput(nvp);
+ *vpp = NULLVP;
+ return (ESTALE);
+ }
+ *vpp = nvp;
+ *exflagsp = np->netc_exflags;
+ *credanonp = &np->netc_anon;
+ return (0);
+}
diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c
new file mode 100644
index 0000000..83bef92
--- /dev/null
+++ b/sys/ufs/ufs/ufs_vnops.c
@@ -0,0 +1,2162 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_vnops.c 8.10 (Berkeley) 4/1/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/resourcevar.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+
+#include <vm/vm.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <ufs/ufs/lockf.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+static int ufs_chmod __P((struct vnode *, int, struct ucred *, struct proc *));
+static int ufs_chown
+ __P((struct vnode *, uid_t, gid_t, struct ucred *, struct proc *));
+
+union _qcvt {
+ quad_t qcvt;
+ long val[2];
+};
+#define SETHIGH(q, h) { \
+ union _qcvt tmp; \
+ tmp.qcvt = (q); \
+ tmp.val[_QUAD_HIGHWORD] = (h); \
+ (q) = tmp.qcvt; \
+}
+#define SETLOW(q, l) { \
+ union _qcvt tmp; \
+ tmp.qcvt = (q); \
+ tmp.val[_QUAD_LOWWORD] = (l); \
+ (q) = tmp.qcvt; \
+}
+
+/*
+ * Create a regular file
+ */
+int
+ufs_create(ap)
+ struct vop_create_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ int error;
+
+ if (error =
+ ufs_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode),
+ ap->a_dvp, ap->a_vpp, ap->a_cnp))
+ return (error);
+ return (0);
+}
+
+/*
+ * Mknod vnode call
+ */
+/* ARGSUSED */
+int
+ufs_mknod(ap)
+ struct vop_mknod_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ register struct vattr *vap = ap->a_vap;
+ register struct vnode **vpp = ap->a_vpp;
+ register struct inode *ip;
+ int error;
+
+ if (error =
+ ufs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
+ ap->a_dvp, vpp, ap->a_cnp))
+ return (error);
+ ip = VTOI(*vpp);
+ ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
+ if (vap->va_rdev != VNOVAL) {
+ /*
+ * Want to be able to use this to make badblock
+ * inodes, so don't truncate the dev number.
+ */
+ ip->i_rdev = vap->va_rdev;
+ }
+ /*
+ * Remove inode so that it will be reloaded by VFS_VGET and
+ * checked to see if it is an alias of an existing entry in
+ * the inode cache.
+ */
+ vput(*vpp);
+ (*vpp)->v_type = VNON;
+ vgone(*vpp);
+ *vpp = 0;
+ return (0);
+}
+
+/*
+ * Open called.
+ *
+ * Nothing to do.
+ */
+/* ARGSUSED */
+int
+ufs_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ /*
+ * Files marked append-only must be opened for appending.
+ */
+ if ((VTOI(ap->a_vp)->i_flags & APPEND) &&
+ (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
+ return (EPERM);
+ return (0);
+}
+
+/*
+ * Close called.
+ *
+ * Update the times on the inode.
+ */
+/* ARGSUSED */
+int
+ufs_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip = VTOI(vp);
+
+ if (vp->v_usecount > 1 && !(ip->i_flag & IN_LOCKED))
+ ITIMES(ip, &time, &time);
+ return (0);
+}
+
+int
+ufs_access(ap)
+ struct vop_access_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip = VTOI(vp);
+ register struct ucred *cred = ap->a_cred;
+ mode_t mask, mode = ap->a_mode;
+ register gid_t *gp;
+ int i, error;
+
+#ifdef DIAGNOSTIC
+ if (!VOP_ISLOCKED(vp)) {
+ vprint("ufs_access: not locked", vp);
+ panic("ufs_access: not locked");
+ }
+#endif
+#ifdef QUOTA
+ if (mode & VWRITE)
+ switch (vp->v_type) {
+ case VDIR:
+ case VLNK:
+ case VREG:
+ if (error = getinoquota(ip))
+ return (error);
+ break;
+ }
+#endif
+
+ /* If immutable bit set, nobody gets to write it. */
+ if ((mode & VWRITE) && (ip->i_flags & IMMUTABLE))
+ return (EPERM);
+
+ /* Otherwise, user id 0 always gets access. */
+ if (cred->cr_uid == 0)
+ return (0);
+
+ mask = 0;
+
+ /* Otherwise, check the owner. */
+ if (cred->cr_uid == ip->i_uid) {
+ if (mode & VEXEC)
+ mask |= S_IXUSR;
+ if (mode & VREAD)
+ mask |= S_IRUSR;
+ if (mode & VWRITE)
+ mask |= S_IWUSR;
+ return ((ip->i_mode & mask) == mask ? 0 : EACCES);
+ }
+
+ /* Otherwise, check the groups. */
+ for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
+ if (ip->i_gid == *gp) {
+ if (mode & VEXEC)
+ mask |= S_IXGRP;
+ if (mode & VREAD)
+ mask |= S_IRGRP;
+ if (mode & VWRITE)
+ mask |= S_IWGRP;
+ return ((ip->i_mode & mask) == mask ? 0 : EACCES);
+ }
+
+ /* Otherwise, check everyone else. */
+ if (mode & VEXEC)
+ mask |= S_IXOTH;
+ if (mode & VREAD)
+ mask |= S_IROTH;
+ if (mode & VWRITE)
+ mask |= S_IWOTH;
+ return ((ip->i_mode & mask) == mask ? 0 : EACCES);
+}
+
+/* ARGSUSED */
+int
+ufs_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip = VTOI(vp);
+ register struct vattr *vap = ap->a_vap;
+
+ ITIMES(ip, &time, &time);
+ /*
+ * Copy from inode table
+ */
+ vap->va_fsid = ip->i_dev;
+ vap->va_fileid = ip->i_number;
+ vap->va_mode = ip->i_mode & ~IFMT;
+ vap->va_nlink = ip->i_nlink;
+ vap->va_uid = ip->i_uid;
+ vap->va_gid = ip->i_gid;
+ vap->va_rdev = (dev_t)ip->i_rdev;
+ vap->va_size = ip->i_din.di_size;
+ vap->va_atime = ip->i_atime;
+ vap->va_mtime = ip->i_mtime;
+ vap->va_ctime = ip->i_ctime;
+ vap->va_flags = ip->i_flags;
+ vap->va_gen = ip->i_gen;
+ /* this doesn't belong here */
+ if (vp->v_type == VBLK)
+ vap->va_blocksize = BLKDEV_IOSIZE;
+ else if (vp->v_type == VCHR)
+ vap->va_blocksize = MAXBSIZE;
+ else
+ vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
+ vap->va_bytes = dbtob(ip->i_blocks);
+ vap->va_type = vp->v_type;
+ vap->va_filerev = ip->i_modrev;
+ return (0);
+}
+
+/*
+ * Set attribute vnode op. called from several syscalls
+ */
+int
+ufs_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vattr *vap = ap->a_vap;
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip = VTOI(vp);
+ register struct ucred *cred = ap->a_cred;
+ register struct proc *p = ap->a_p;
+ struct timeval atimeval, mtimeval;
+ int error;
+
+ /*
+ * Check for unsettable attributes.
+ */
+ if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
+ (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
+ (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
+ ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
+ return (EINVAL);
+ }
+ if (vap->va_flags != VNOVAL) {
+ if (cred->cr_uid != ip->i_uid &&
+ (error = suser(cred, &p->p_acflag)))
+ return (error);
+ if (cred->cr_uid == 0) {
+ if ((ip->i_flags & (SF_IMMUTABLE | SF_APPEND)) &&
+ securelevel > 0)
+ return (EPERM);
+ ip->i_flags = vap->va_flags;
+ } else {
+ if (ip->i_flags & (SF_IMMUTABLE | SF_APPEND))
+ return (EPERM);
+ ip->i_flags &= SF_SETTABLE;
+ ip->i_flags |= (vap->va_flags & UF_SETTABLE);
+ }
+ ip->i_flag |= IN_CHANGE;
+ if (vap->va_flags & (IMMUTABLE | APPEND))
+ return (0);
+ }
+ if (ip->i_flags & (IMMUTABLE | APPEND))
+ return (EPERM);
+ /*
+ * Go through the fields and update iff not VNOVAL.
+ */
+ if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL)
+ if (error = ufs_chown(vp, vap->va_uid, vap->va_gid, cred, p))
+ return (error);
+ if (vap->va_size != VNOVAL) {
+ if (vp->v_type == VDIR)
+ return (EISDIR);
+ if (error = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p))
+ return (error);
+ }
+ ip = VTOI(vp);
+ if (vap->va_atime.ts_sec != VNOVAL || vap->va_mtime.ts_sec != VNOVAL) {
+ if (cred->cr_uid != ip->i_uid &&
+ (error = suser(cred, &p->p_acflag)) &&
+ ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
+ (error = VOP_ACCESS(vp, VWRITE, cred, p))))
+ return (error);
+ if (vap->va_atime.ts_sec != VNOVAL)
+ ip->i_flag |= IN_ACCESS;
+ if (vap->va_mtime.ts_sec != VNOVAL)
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ atimeval.tv_sec = vap->va_atime.ts_sec;
+ atimeval.tv_usec = vap->va_atime.ts_nsec / 1000;
+ mtimeval.tv_sec = vap->va_mtime.ts_sec;
+ mtimeval.tv_usec = vap->va_mtime.ts_nsec / 1000;
+ if (error = VOP_UPDATE(vp, &atimeval, &mtimeval, 1))
+ return (error);
+ }
+ error = 0;
+ if (vap->va_mode != (mode_t)VNOVAL)
+ error = ufs_chmod(vp, (int)vap->va_mode, cred, p);
+ return (error);
+}
+
+/*
+ * Change the mode on a file.
+ * Inode must be locked before calling.
+ */
+static int
+ufs_chmod(vp, mode, cred, p)
+ register struct vnode *vp;
+ register int mode;
+ register struct ucred *cred;
+ struct proc *p;
+{
+ register struct inode *ip = VTOI(vp);
+ int error;
+
+ if (cred->cr_uid != ip->i_uid &&
+ (error = suser(cred, &p->p_acflag)))
+ return (error);
+ if (cred->cr_uid) {
+ if (vp->v_type != VDIR && (mode & S_ISTXT))
+ return (EFTYPE);
+ if (!groupmember(ip->i_gid, cred) && (mode & ISGID))
+ return (EPERM);
+ }
+ ip->i_mode &= ~ALLPERMS;
+ ip->i_mode |= (mode & ALLPERMS);
+ ip->i_flag |= IN_CHANGE;
+ if ((vp->v_flag & VTEXT) && (ip->i_mode & S_ISTXT) == 0)
+ (void) vnode_pager_uncache(vp);
+ return (0);
+}
+
+/*
+ * Perform chown operation on inode ip;
+ * inode must be locked prior to call.
+ */
+static int
+ufs_chown(vp, uid, gid, cred, p)
+ register struct vnode *vp;
+ uid_t uid;
+ gid_t gid;
+ struct ucred *cred;
+ struct proc *p;
+{
+ register struct inode *ip = VTOI(vp);
+ uid_t ouid;
+ gid_t ogid;
+ int error = 0;
+#ifdef QUOTA
+ register int i;
+ long change;
+#endif
+
+ if (uid == (uid_t)VNOVAL)
+ uid = ip->i_uid;
+ if (gid == (gid_t)VNOVAL)
+ gid = ip->i_gid;
+ /*
+ * If we don't own the file, are trying to change the owner
+ * of the file, or are not a member of the target group,
+ * the caller must be superuser or the call fails.
+ */
+ if ((cred->cr_uid != ip->i_uid || uid != ip->i_uid ||
+ !groupmember((gid_t)gid, cred)) &&
+ (error = suser(cred, &p->p_acflag)))
+ return (error);
+ ogid = ip->i_gid;
+ ouid = ip->i_uid;
+#ifdef QUOTA
+ if (error = getinoquota(ip))
+ return (error);
+ if (ouid == uid) {
+ dqrele(vp, ip->i_dquot[USRQUOTA]);
+ ip->i_dquot[USRQUOTA] = NODQUOT;
+ }
+ if (ogid == gid) {
+ dqrele(vp, ip->i_dquot[GRPQUOTA]);
+ ip->i_dquot[GRPQUOTA] = NODQUOT;
+ }
+ change = ip->i_blocks;
+ (void) chkdq(ip, -change, cred, CHOWN);
+ (void) chkiq(ip, -1, cred, CHOWN);
+ for (i = 0; i < MAXQUOTAS; i++) {
+ dqrele(vp, ip->i_dquot[i]);
+ ip->i_dquot[i] = NODQUOT;
+ }
+#endif
+ ip->i_gid = gid;
+ ip->i_uid = uid;
+#ifdef QUOTA
+ if ((error = getinoquota(ip)) == 0) {
+ if (ouid == uid) {
+ dqrele(vp, ip->i_dquot[USRQUOTA]);
+ ip->i_dquot[USRQUOTA] = NODQUOT;
+ }
+ if (ogid == gid) {
+ dqrele(vp, ip->i_dquot[GRPQUOTA]);
+ ip->i_dquot[GRPQUOTA] = NODQUOT;
+ }
+ if ((error = chkdq(ip, change, cred, CHOWN)) == 0) {
+ if ((error = chkiq(ip, 1, cred, CHOWN)) == 0)
+ goto good;
+ else
+ (void) chkdq(ip, -change, cred, CHOWN|FORCE);
+ }
+ for (i = 0; i < MAXQUOTAS; i++) {
+ dqrele(vp, ip->i_dquot[i]);
+ ip->i_dquot[i] = NODQUOT;
+ }
+ }
+ ip->i_gid = ogid;
+ ip->i_uid = ouid;
+ if (getinoquota(ip) == 0) {
+ if (ouid == uid) {
+ dqrele(vp, ip->i_dquot[USRQUOTA]);
+ ip->i_dquot[USRQUOTA] = NODQUOT;
+ }
+ if (ogid == gid) {
+ dqrele(vp, ip->i_dquot[GRPQUOTA]);
+ ip->i_dquot[GRPQUOTA] = NODQUOT;
+ }
+ (void) chkdq(ip, change, cred, FORCE|CHOWN);
+ (void) chkiq(ip, 1, cred, FORCE|CHOWN);
+ (void) getinoquota(ip);
+ }
+ return (error);
+good:
+ if (getinoquota(ip))
+ panic("chown: lost quota");
+#endif /* QUOTA */
+ if (ouid != uid || ogid != gid)
+ ip->i_flag |= IN_CHANGE;
+ if (ouid != uid && cred->cr_uid != 0)
+ ip->i_mode &= ~ISUID;
+ if (ogid != gid && cred->cr_uid != 0)
+ ip->i_mode &= ~ISGID;
+ return (0);
+}
+
+/* ARGSUSED */
+int
+ufs_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (ENOTTY);
+}
+
+/* ARGSUSED */
+int
+ufs_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ /*
+ * We should really check to see if I/O is possible.
+ */
+ return (1);
+}
+
+/*
+ * Mmap a file
+ *
+ * NB Currently unsupported.
+ */
+/* ARGSUSED */
+int
+ufs_mmap(ap)
+ struct vop_mmap_args /* {
+ struct vnode *a_vp;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (EINVAL);
+}
+
+/*
+ * Seek on a file
+ *
+ * Nothing to do, so just return.
+ */
+/* ARGSUSED */
+int
+ufs_seek(ap)
+ struct vop_seek_args /* {
+ struct vnode *a_vp;
+ off_t a_oldoff;
+ off_t a_newoff;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+int
+ufs_remove(ap)
+ struct vop_remove_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ register struct inode *ip;
+ register struct vnode *vp = ap->a_vp;
+ register struct vnode *dvp = ap->a_dvp;
+ int error;
+
+ ip = VTOI(vp);
+ if ((ip->i_flags & (IMMUTABLE | APPEND)) ||
+ (VTOI(dvp)->i_flags & APPEND)) {
+ error = EPERM;
+ goto out;
+ }
+ if ((error = ufs_dirremove(dvp, ap->a_cnp)) == 0) {
+ ip->i_nlink--;
+ ip->i_flag |= IN_CHANGE;
+ }
+out:
+ if (dvp == vp)
+ vrele(vp);
+ else
+ vput(vp);
+ vput(dvp);
+ return (error);
+}
+
+/*
+ * link vnode call
+ */
+int
+ufs_link(ap)
+ struct vop_link_args /* {
+ struct vnode *a_vp;
+ struct vnode *a_tdvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct vnode *tdvp = ap->a_tdvp;
+ register struct componentname *cnp = ap->a_cnp;
+ register struct inode *ip;
+ struct timeval tv;
+ int error;
+
+#ifdef DIAGNOSTIC
+ if ((cnp->cn_flags & HASBUF) == 0)
+ panic("ufs_link: no name");
+#endif
+ if (vp->v_mount != tdvp->v_mount) {
+ VOP_ABORTOP(vp, cnp);
+ error = EXDEV;
+ goto out2;
+ }
+ if (vp != tdvp && (error = VOP_LOCK(tdvp))) {
+ VOP_ABORTOP(vp, cnp);
+ goto out2;
+ }
+ ip = VTOI(tdvp);
+ if ((nlink_t)ip->i_nlink >= LINK_MAX) {
+ VOP_ABORTOP(vp, cnp);
+ error = EMLINK;
+ goto out1;
+ }
+ if (ip->i_flags & (IMMUTABLE | APPEND)) {
+ VOP_ABORTOP(vp, cnp);
+ error = EPERM;
+ goto out1;
+ }
+ ip->i_nlink++;
+ ip->i_flag |= IN_CHANGE;
+ tv = time;
+ error = VOP_UPDATE(tdvp, &tv, &tv, 1);
+ if (!error)
+ error = ufs_direnter(ip, vp, cnp);
+ if (error) {
+ ip->i_nlink--;
+ ip->i_flag |= IN_CHANGE;
+ }
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+out1:
+ if (vp != tdvp)
+ VOP_UNLOCK(tdvp);
+out2:
+ vput(vp);
+ return (error);
+}
+
+
+
+/*
+ * relookup - lookup a path name component
+ * Used by lookup to re-aquire things.
+ */
+int
+relookup(dvp, vpp, cnp)
+ struct vnode *dvp, **vpp;
+ struct componentname *cnp;
+{
+ register struct vnode *dp = 0; /* the directory we are searching */
+ int docache; /* == 0 do not cache last component */
+ int wantparent; /* 1 => wantparent or lockparent flag */
+ int rdonly; /* lookup read-only flag bit */
+ int error = 0;
+#ifdef NAMEI_DIAGNOSTIC
+ int newhash; /* DEBUG: check name hash */
+ char *cp; /* DEBUG: check name ptr/len */
+#endif
+
+ /*
+ * Setup: break out flag bits into variables.
+ */
+ wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT);
+ docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
+ if (cnp->cn_nameiop == DELETE ||
+ (wantparent && cnp->cn_nameiop != CREATE))
+ docache = 0;
+ rdonly = cnp->cn_flags & RDONLY;
+ cnp->cn_flags &= ~ISSYMLINK;
+ dp = dvp;
+ VOP_LOCK(dp);
+
+/* dirloop: */
+ /*
+ * Search a new directory.
+ *
+ * The cn_hash value is for use by vfs_cache.
+ * The last component of the filename is left accessible via
+ * cnp->cn_nameptr for callers that need the name. Callers needing
+ * the name set the SAVENAME flag. When done, they assume
+ * responsibility for freeing the pathname buffer.
+ */
+#ifdef NAMEI_DIAGNOSTIC
+ for (newhash = 0, cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++)
+ newhash += (unsigned char)*cp;
+ if (newhash != cnp->cn_hash)
+ panic("relookup: bad hash");
+ if (cnp->cn_namelen != cp - cnp->cn_nameptr)
+ panic ("relookup: bad len");
+ if (*cp != 0)
+ panic("relookup: not last component");
+ printf("{%s}: ", cnp->cn_nameptr);
+#endif
+
+ /*
+ * Check for degenerate name (e.g. / or "")
+ * which is a way of talking about a directory,
+ * e.g. like "/." or ".".
+ */
+ if (cnp->cn_nameptr[0] == '\0') {
+ if (cnp->cn_nameiop != LOOKUP || wantparent) {
+ error = EISDIR;
+ goto bad;
+ }
+ if (dp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto bad;
+ }
+ if (!(cnp->cn_flags & LOCKLEAF))
+ VOP_UNLOCK(dp);
+ *vpp = dp;
+ if (cnp->cn_flags & SAVESTART)
+ panic("lookup: SAVESTART");
+ return (0);
+ }
+
+ if (cnp->cn_flags & ISDOTDOT)
+ panic ("relookup: lookup on dot-dot");
+
+ /*
+ * We now have a segment name to search for, and a directory to search.
+ */
+ if (error = VOP_LOOKUP(dp, vpp, cnp)) {
+#ifdef DIAGNOSTIC
+ if (*vpp != NULL)
+ panic("leaf should be empty");
+#endif
+ if (error != EJUSTRETURN)
+ goto bad;
+ /*
+ * If creating and at end of pathname, then can consider
+ * allowing file to be created.
+ */
+ if (rdonly || (dvp->v_mount->mnt_flag & MNT_RDONLY)) {
+ error = EROFS;
+ goto bad;
+ }
+ /* ASSERT(dvp == ndp->ni_startdir) */
+ if (cnp->cn_flags & SAVESTART)
+ VREF(dvp);
+ /*
+ * We return with ni_vp NULL to indicate that the entry
+ * doesn't currently exist, leaving a pointer to the
+ * (possibly locked) directory inode in ndp->ni_dvp.
+ */
+ return (0);
+ }
+ dp = *vpp;
+
+#ifdef DIAGNOSTIC
+ /*
+ * Check for symbolic link
+ */
+ if (dp->v_type == VLNK && (cnp->cn_flags & FOLLOW))
+ panic ("relookup: symlink found.\n");
+#endif
+
+ /*
+ * Check for read-only file systems.
+ */
+ if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
+ /*
+ * Disallow directory write attempts on read-only
+ * file systems.
+ */
+ if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) ||
+ (wantparent &&
+ (dvp->v_mount->mnt_flag & MNT_RDONLY))) {
+ error = EROFS;
+ goto bad2;
+ }
+ }
+ /* ASSERT(dvp == ndp->ni_startdir) */
+ if (cnp->cn_flags & SAVESTART)
+ VREF(dvp);
+
+ if (!wantparent)
+ vrele(dvp);
+ if ((cnp->cn_flags & LOCKLEAF) == 0)
+ VOP_UNLOCK(dp);
+ return (0);
+
+bad2:
+ if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN))
+ VOP_UNLOCK(dvp);
+ vrele(dvp);
+bad:
+ vput(dp);
+ *vpp = NULL;
+ return (error);
+}
+
+
+/*
+ * Rename system call.
+ * rename("foo", "bar");
+ * is essentially
+ * unlink("bar");
+ * link("foo", "bar");
+ * unlink("foo");
+ * but ``atomically''. Can't do full commit without saving state in the
+ * inode on disk which isn't feasible at this time. Best we can do is
+ * always guarantee the target exists.
+ *
+ * Basic algorithm is:
+ *
+ * 1) Bump link count on source while we're linking it to the
+ * target. This also ensure the inode won't be deleted out
+ * from underneath us while we work (it may be truncated by
+ * a concurrent `trunc' or `open' for creation).
+ * 2) Link source to destination. If destination already exists,
+ * delete it first.
+ * 3) Unlink source reference to inode if still around. If a
+ * directory was moved and the parent of the destination
+ * is different from the source, patch the ".." entry in the
+ * directory.
+ */
+int
+ufs_rename(ap)
+ struct vop_rename_args /* {
+ struct vnode *a_fdvp;
+ struct vnode *a_fvp;
+ struct componentname *a_fcnp;
+ struct vnode *a_tdvp;
+ struct vnode *a_tvp;
+ struct componentname *a_tcnp;
+ } */ *ap;
+{
+ struct vnode *tvp = ap->a_tvp;
+ register struct vnode *tdvp = ap->a_tdvp;
+ struct vnode *fvp = ap->a_fvp;
+ register struct vnode *fdvp = ap->a_fdvp;
+ register struct componentname *tcnp = ap->a_tcnp;
+ register struct componentname *fcnp = ap->a_fcnp;
+ register struct inode *ip, *xp, *dp;
+ struct dirtemplate dirbuf;
+ struct timeval tv;
+ int doingdirectory = 0, oldparent = 0, newparent = 0;
+ int error = 0;
+ u_char namlen;
+
+#ifdef DIAGNOSTIC
+ if ((tcnp->cn_flags & HASBUF) == 0 ||
+ (fcnp->cn_flags & HASBUF) == 0)
+ panic("ufs_rename: no name");
+#endif
+ /*
+ * Check for cross-device rename.
+ */
+ if ((fvp->v_mount != tdvp->v_mount) ||
+ (tvp && (fvp->v_mount != tvp->v_mount))) {
+ error = EXDEV;
+abortit:
+ VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */
+ if (tdvp == tvp)
+ vrele(tdvp);
+ else
+ vput(tdvp);
+ if (tvp)
+ vput(tvp);
+ VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
+ vrele(fdvp);
+ vrele(fvp);
+ return (error);
+ }
+
+ /*
+ * Check if just deleting a link name.
+ */
+ if (tvp && ((VTOI(tvp)->i_flags & (IMMUTABLE | APPEND)) ||
+ (VTOI(tdvp)->i_flags & APPEND))) {
+ error = EPERM;
+ goto abortit;
+ }
+ if (fvp == tvp) {
+ if (fvp->v_type == VDIR) {
+ error = EINVAL;
+ goto abortit;
+ }
+ VOP_ABORTOP(fdvp, fcnp);
+ vrele(fdvp);
+ vrele(fvp);
+ vput(tdvp);
+ vput(tvp);
+ tcnp->cn_flags &= ~MODMASK;
+ tcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
+ if ((tcnp->cn_flags & SAVESTART) == 0)
+ panic("ufs_rename: lost from startdir");
+ tcnp->cn_nameiop = DELETE;
+ (void) relookup(tdvp, &tvp, tcnp);
+ return (VOP_REMOVE(tdvp, tvp, tcnp));
+ }
+ if (error = VOP_LOCK(fvp))
+ goto abortit;
+ dp = VTOI(fdvp);
+ ip = VTOI(fvp);
+ if ((ip->i_flags & (IMMUTABLE | APPEND)) || (dp->i_flags & APPEND)) {
+ VOP_UNLOCK(fvp);
+ error = EPERM;
+ goto abortit;
+ }
+ if ((ip->i_mode & IFMT) == IFDIR) {
+ /*
+ * Avoid ".", "..", and aliases of "." for obvious reasons.
+ */
+ if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
+ dp == ip || (fcnp->cn_flags&ISDOTDOT) ||
+ (ip->i_flag & IN_RENAME)) {
+ VOP_UNLOCK(fvp);
+ error = EINVAL;
+ goto abortit;
+ }
+ ip->i_flag |= IN_RENAME;
+ oldparent = dp->i_number;
+ doingdirectory++;
+ }
+ vrele(fdvp);
+
+ /*
+ * When the target exists, both the directory
+ * and target vnodes are returned locked.
+ */
+ dp = VTOI(tdvp);
+ xp = NULL;
+ if (tvp)
+ xp = VTOI(tvp);
+
+ /*
+ * 1) Bump link count while we're moving stuff
+ * around. If we crash somewhere before
+ * completing our work, the link count
+ * may be wrong, but correctable.
+ */
+ ip->i_nlink++;
+ ip->i_flag |= IN_CHANGE;
+ tv = time;
+ if (error = VOP_UPDATE(fvp, &tv, &tv, 1)) {
+ VOP_UNLOCK(fvp);
+ goto bad;
+ }
+
+ /*
+ * If ".." must be changed (ie the directory gets a new
+ * parent) then the source directory must not be in the
+ * directory heirarchy above the target, as this would
+ * orphan everything below the source directory. Also
+ * the user must have write permission in the source so
+ * as to be able to change "..". We must repeat the call
+ * to namei, as the parent directory is unlocked by the
+ * call to checkpath().
+ */
+ error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
+ VOP_UNLOCK(fvp);
+ if (oldparent != dp->i_number)
+ newparent = dp->i_number;
+ if (doingdirectory && newparent) {
+ if (error) /* write access check above */
+ goto bad;
+ if (xp != NULL)
+ vput(tvp);
+ if (error = ufs_checkpath(ip, dp, tcnp->cn_cred))
+ goto out;
+ if ((tcnp->cn_flags & SAVESTART) == 0)
+ panic("ufs_rename: lost to startdir");
+ if (error = relookup(tdvp, &tvp, tcnp))
+ goto out;
+ dp = VTOI(tdvp);
+ xp = NULL;
+ if (tvp)
+ xp = VTOI(tvp);
+ }
+ /*
+ * 2) If target doesn't exist, link the target
+ * to the source and unlink the source.
+ * Otherwise, rewrite the target directory
+ * entry to reference the source inode and
+ * expunge the original entry's existence.
+ */
+ if (xp == NULL) {
+ if (dp->i_dev != ip->i_dev)
+ panic("rename: EXDEV");
+ /*
+ * Account for ".." in new directory.
+ * When source and destination have the same
+ * parent we don't fool with the link count.
+ */
+ if (doingdirectory && newparent) {
+ if ((nlink_t)dp->i_nlink >= LINK_MAX) {
+ error = EMLINK;
+ goto bad;
+ }
+ dp->i_nlink++;
+ dp->i_flag |= IN_CHANGE;
+ if (error = VOP_UPDATE(tdvp, &tv, &tv, 1))
+ goto bad;
+ }
+ if (error = ufs_direnter(ip, tdvp, tcnp)) {
+ if (doingdirectory && newparent) {
+ dp->i_nlink--;
+ dp->i_flag |= IN_CHANGE;
+ (void)VOP_UPDATE(tdvp, &tv, &tv, 1);
+ }
+ goto bad;
+ }
+ vput(tdvp);
+ } else {
+ if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
+ panic("rename: EXDEV");
+ /*
+ * Short circuit rename(foo, foo).
+ */
+ if (xp->i_number == ip->i_number)
+ panic("rename: same file");
+ /*
+ * If the parent directory is "sticky", then the user must
+ * own the parent directory, or the destination of the rename,
+ * otherwise the destination may not be changed (except by
+ * root). This implements append-only directories.
+ */
+ if ((dp->i_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&
+ tcnp->cn_cred->cr_uid != dp->i_uid &&
+ xp->i_uid != tcnp->cn_cred->cr_uid) {
+ error = EPERM;
+ goto bad;
+ }
+ /*
+ * Target must be empty if a directory and have no links
+ * to it. Also, ensure source and target are compatible
+ * (both directories, or both not directories).
+ */
+ if ((xp->i_mode&IFMT) == IFDIR) {
+ if (!ufs_dirempty(xp, dp->i_number, tcnp->cn_cred) ||
+ xp->i_nlink > 2) {
+ error = ENOTEMPTY;
+ goto bad;
+ }
+ if (!doingdirectory) {
+ error = ENOTDIR;
+ goto bad;
+ }
+ cache_purge(tdvp);
+ } else if (doingdirectory) {
+ error = EISDIR;
+ goto bad;
+ }
+ if (error = ufs_dirrewrite(dp, ip, tcnp))
+ goto bad;
+ /*
+ * If the target directory is in the same
+ * directory as the source directory,
+ * decrement the link count on the parent
+ * of the target directory.
+ */
+ if (doingdirectory && !newparent) {
+ dp->i_nlink--;
+ dp->i_flag |= IN_CHANGE;
+ }
+ vput(tdvp);
+ /*
+ * Adjust the link count of the target to
+ * reflect the dirrewrite above. If this is
+ * a directory it is empty and there are
+ * no links to it, so we can squash the inode and
+ * any space associated with it. We disallowed
+ * renaming over top of a directory with links to
+ * it above, as the remaining link would point to
+ * a directory without "." or ".." entries.
+ */
+ xp->i_nlink--;
+ if (doingdirectory) {
+ if (--xp->i_nlink != 0)
+ panic("rename: linked directory");
+ error = VOP_TRUNCATE(tvp, (off_t)0, IO_SYNC,
+ tcnp->cn_cred, tcnp->cn_proc);
+ }
+ xp->i_flag |= IN_CHANGE;
+ vput(tvp);
+ xp = NULL;
+ }
+
+ /*
+ * 3) Unlink the source.
+ */
+ fcnp->cn_flags &= ~MODMASK;
+ fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
+ if ((fcnp->cn_flags & SAVESTART) == 0)
+ panic("ufs_rename: lost from startdir");
+ (void) relookup(fdvp, &fvp, fcnp);
+ if (fvp != NULL) {
+ xp = VTOI(fvp);
+ dp = VTOI(fdvp);
+ } else {
+ /*
+ * From name has disappeared.
+ */
+ if (doingdirectory)
+ panic("rename: lost dir entry");
+ vrele(ap->a_fvp);
+ return (0);
+ }
+ /*
+ * Ensure that the directory entry still exists and has not
+ * changed while the new name has been entered. If the source is
+ * a file then the entry may have been unlinked or renamed. In
+ * either case there is no further work to be done. If the source
+ * is a directory then it cannot have been rmdir'ed; its link
+ * count of three would cause a rmdir to fail with ENOTEMPTY.
+ * The IRENAME flag ensures that it cannot be moved by another
+ * rename.
+ */
+ if (xp != ip) {
+ if (doingdirectory)
+ panic("rename: lost dir entry");
+ } else {
+ /*
+ * If the source is a directory with a
+ * new parent, the link count of the old
+ * parent directory must be decremented
+ * and ".." set to point to the new parent.
+ */
+ if (doingdirectory && newparent) {
+ dp->i_nlink--;
+ dp->i_flag |= IN_CHANGE;
+ error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf,
+ sizeof (struct dirtemplate), (off_t)0,
+ UIO_SYSSPACE, IO_NODELOCKED,
+ tcnp->cn_cred, (int *)0, (struct proc *)0);
+ if (error == 0) {
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ if (fvp->v_mount->mnt_maxsymlinklen <= 0)
+ namlen = dirbuf.dotdot_type;
+ else
+ namlen = dirbuf.dotdot_namlen;
+# else
+ namlen = dirbuf.dotdot_namlen;
+# endif
+ if (namlen != 2 ||
+ dirbuf.dotdot_name[0] != '.' ||
+ dirbuf.dotdot_name[1] != '.') {
+ ufs_dirbad(xp, (doff_t)12,
+ "rename: mangled dir");
+ } else {
+ dirbuf.dotdot_ino = newparent;
+ (void) vn_rdwr(UIO_WRITE, fvp,
+ (caddr_t)&dirbuf,
+ sizeof (struct dirtemplate),
+ (off_t)0, UIO_SYSSPACE,
+ IO_NODELOCKED|IO_SYNC,
+ tcnp->cn_cred, (int *)0,
+ (struct proc *)0);
+ cache_purge(fdvp);
+ }
+ }
+ }
+ error = ufs_dirremove(fdvp, fcnp);
+ if (!error) {
+ xp->i_nlink--;
+ xp->i_flag |= IN_CHANGE;
+ }
+ xp->i_flag &= ~IN_RENAME;
+ }
+ if (dp)
+ vput(fdvp);
+ if (xp)
+ vput(fvp);
+ vrele(ap->a_fvp);
+ return (error);
+
+bad:
+ if (xp)
+ vput(ITOV(xp));
+ vput(ITOV(dp));
+out:
+ if (VOP_LOCK(fvp) == 0) {
+ ip->i_nlink--;
+ ip->i_flag |= IN_CHANGE;
+ vput(fvp);
+ } else
+ vrele(fvp);
+ return (error);
+}
+
+/*
+ * A virgin directory (no blushing please).
+ */
+static struct dirtemplate mastertemplate = {
+ 0, 12, DT_DIR, 1, ".",
+ 0, DIRBLKSIZ - 12, DT_DIR, 2, ".."
+};
+static struct odirtemplate omastertemplate = {
+ 0, 12, 1, ".",
+ 0, DIRBLKSIZ - 12, 2, ".."
+};
+
+/*
+ * Mkdir system call
+ */
+int
+ufs_mkdir(ap)
+ struct vop_mkdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ register struct vnode *dvp = ap->a_dvp;
+ register struct vattr *vap = ap->a_vap;
+ register struct componentname *cnp = ap->a_cnp;
+ register struct inode *ip, *dp;
+ struct vnode *tvp;
+ struct dirtemplate dirtemplate, *dtp;
+ struct timeval tv;
+ int error, dmode;
+
+#ifdef DIAGNOSTIC
+ if ((cnp->cn_flags & HASBUF) == 0)
+ panic("ufs_mkdir: no name");
+#endif
+ dp = VTOI(dvp);
+ if ((nlink_t)dp->i_nlink >= LINK_MAX) {
+ error = EMLINK;
+ goto out;
+ }
+ dmode = vap->va_mode & 0777;
+ dmode |= IFDIR;
+ /*
+ * Must simulate part of ufs_makeinode here to acquire the inode,
+ * but not have it entered in the parent directory. The entry is
+ * made later after writing "." and ".." entries.
+ */
+ if (error = VOP_VALLOC(dvp, dmode, cnp->cn_cred, &tvp))
+ goto out;
+ ip = VTOI(tvp);
+ ip->i_uid = cnp->cn_cred->cr_uid;
+ ip->i_gid = dp->i_gid;
+#ifdef QUOTA
+ if ((error = getinoquota(ip)) ||
+ (error = chkiq(ip, 1, cnp->cn_cred, 0))) {
+ free(cnp->cn_pnbuf, M_NAMEI);
+ VOP_VFREE(tvp, ip->i_number, dmode);
+ vput(tvp);
+ vput(dvp);
+ return (error);
+ }
+#endif
+ ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
+ ip->i_mode = dmode;
+ tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */
+ ip->i_nlink = 2;
+ tv = time;
+ error = VOP_UPDATE(tvp, &tv, &tv, 1);
+
+ /*
+ * Bump link count in parent directory
+ * to reflect work done below. Should
+ * be done before reference is created
+ * so reparation is possible if we crash.
+ */
+ dp->i_nlink++;
+ dp->i_flag |= IN_CHANGE;
+ if (error = VOP_UPDATE(dvp, &tv, &tv, 1))
+ goto bad;
+
+ /* Initialize directory with "." and ".." from static template. */
+ if (dvp->v_mount->mnt_maxsymlinklen > 0)
+ dtp = &mastertemplate;
+ else
+ dtp = (struct dirtemplate *)&omastertemplate;
+ dirtemplate = *dtp;
+ dirtemplate.dot_ino = ip->i_number;
+ dirtemplate.dotdot_ino = dp->i_number;
+ error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate,
+ sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE,
+ IO_NODELOCKED|IO_SYNC, cnp->cn_cred, (int *)0, (struct proc *)0);
+ if (error) {
+ dp->i_nlink--;
+ dp->i_flag |= IN_CHANGE;
+ goto bad;
+ }
+ if (DIRBLKSIZ > VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
+ panic("ufs_mkdir: blksize"); /* XXX should grow with balloc() */
+ else {
+ ip->i_size = DIRBLKSIZ;
+ ip->i_flag |= IN_CHANGE;
+ }
+
+ /* Directory set up, now install it's entry in the parent directory. */
+ if (error = ufs_direnter(ip, dvp, cnp)) {
+ dp->i_nlink--;
+ dp->i_flag |= IN_CHANGE;
+ }
+bad:
+ /*
+ * No need to do an explicit VOP_TRUNCATE here, vrele will do this
+ * for us because we set the link count to 0.
+ */
+ if (error) {
+ ip->i_nlink = 0;
+ ip->i_flag |= IN_CHANGE;
+ vput(tvp);
+ } else
+ *ap->a_vpp = tvp;
+out:
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ vput(dvp);
+ return (error);
+}
+
+/*
+ * Rmdir system call.
+ */
+int
+ufs_rmdir(ap)
+ struct vop_rmdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct vnode *dvp = ap->a_dvp;
+ register struct componentname *cnp = ap->a_cnp;
+ register struct inode *ip, *dp;
+ int error;
+
+ ip = VTOI(vp);
+ dp = VTOI(dvp);
+ /*
+ * No rmdir "." please.
+ */
+ if (dp == ip) {
+ vrele(dvp);
+ vput(vp);
+ return (EINVAL);
+ }
+ /*
+ * Verify the directory is empty (and valid).
+ * (Rmdir ".." won't be valid since
+ * ".." will contain a reference to
+ * the current directory and thus be
+ * non-empty.)
+ */
+ error = 0;
+ if (ip->i_nlink != 2 ||
+ !ufs_dirempty(ip, dp->i_number, cnp->cn_cred)) {
+ error = ENOTEMPTY;
+ goto out;
+ }
+ if ((dp->i_flags & APPEND) || (ip->i_flags & (IMMUTABLE | APPEND))) {
+ error = EPERM;
+ goto out;
+ }
+ /*
+ * Delete reference to directory before purging
+ * inode. If we crash in between, the directory
+ * will be reattached to lost+found,
+ */
+ if (error = ufs_dirremove(dvp, cnp))
+ goto out;
+ dp->i_nlink--;
+ dp->i_flag |= IN_CHANGE;
+ cache_purge(dvp);
+ vput(dvp);
+ dvp = NULL;
+ /*
+ * Truncate inode. The only stuff left
+ * in the directory is "." and "..". The
+ * "." reference is inconsequential since
+ * we're quashing it. The ".." reference
+ * has already been adjusted above. We've
+ * removed the "." reference and the reference
+ * in the parent directory, but there may be
+ * other hard links so decrement by 2 and
+ * worry about them later.
+ */
+ ip->i_nlink -= 2;
+ error = VOP_TRUNCATE(vp, (off_t)0, IO_SYNC, cnp->cn_cred,
+ cnp->cn_proc);
+ cache_purge(ITOV(ip));
+out:
+ if (dvp)
+ vput(dvp);
+ vput(vp);
+ return (error);
+}
+
+/*
+ * symlink -- make a symbolic link
+ */
+int
+ufs_symlink(ap)
+ struct vop_symlink_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ char *a_target;
+ } */ *ap;
+{
+ register struct vnode *vp, **vpp = ap->a_vpp;
+ register struct inode *ip;
+ int len, error;
+
+ if (error = ufs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp,
+ vpp, ap->a_cnp))
+ return (error);
+ vp = *vpp;
+ len = strlen(ap->a_target);
+ if (len < vp->v_mount->mnt_maxsymlinklen) {
+ ip = VTOI(vp);
+ bcopy(ap->a_target, (char *)ip->i_shortlink, len);
+ ip->i_size = len;
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ } else
+ error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
+ UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, (int *)0,
+ (struct proc *)0);
+ vput(vp);
+ return (error);
+}
+
+/*
+ * Vnode op for reading directories.
+ *
+ * The routine below assumes that the on-disk format of a directory
+ * is the same as that defined by <sys/dirent.h>. If the on-disk
+ * format changes, then it will be necessary to do a conversion
+ * from the on-disk format that read returns to the format defined
+ * by <sys/dirent.h>.
+ */
+int
+ufs_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct uio *uio = ap->a_uio;
+ int count, lost, error;
+
+ count = uio->uio_resid;
+ count &= ~(DIRBLKSIZ - 1);
+ lost = uio->uio_resid - count;
+ if (count < DIRBLKSIZ || (uio->uio_offset & (DIRBLKSIZ -1)))
+ return (EINVAL);
+ uio->uio_resid = count;
+ uio->uio_iov->iov_len = count;
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ if (ap->a_vp->v_mount->mnt_maxsymlinklen > 0) {
+ error = VOP_READ(ap->a_vp, uio, 0, ap->a_cred);
+ } else {
+ struct dirent *dp, *edp;
+ struct uio auio;
+ struct iovec aiov;
+ caddr_t dirbuf;
+ int readcnt;
+ u_char tmp;
+
+ auio = *uio;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_segflg = UIO_SYSSPACE;
+ aiov.iov_len = count;
+ MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK);
+ aiov.iov_base = dirbuf;
+ error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred);
+ if (error == 0) {
+ readcnt = count - auio.uio_resid;
+ edp = (struct dirent *)&dirbuf[readcnt];
+ for (dp = (struct dirent *)dirbuf; dp < edp; ) {
+ tmp = dp->d_namlen;
+ dp->d_namlen = dp->d_type;
+ dp->d_type = tmp;
+ if (dp->d_reclen > 0) {
+ dp = (struct dirent *)
+ ((char *)dp + dp->d_reclen);
+ } else {
+ error = EIO;
+ break;
+ }
+ }
+ if (dp >= edp)
+ error = uiomove(dirbuf, readcnt, uio);
+ }
+ FREE(dirbuf, M_TEMP);
+ }
+# else
+ error = VOP_READ(ap->a_vp, uio, 0, ap->a_cred);
+# endif
+ uio->uio_resid += lost;
+ return (error);
+}
+
+/*
+ * Return target name of a symbolic link
+ */
+int
+ufs_readlink(ap)
+ struct vop_readlink_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip = VTOI(vp);
+ int isize;
+
+ isize = ip->i_size;
+ if ((isize < vp->v_mount->mnt_maxsymlinklen) ||
+ (ip->i_din.di_blocks == 0)) { /* XXX - for old fastlink support */
+ uiomove((char *)ip->i_shortlink, isize, ap->a_uio);
+ return (0);
+ }
+ return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
+}
+
+/*
+ * Ufs abort op, called after namei() when a CREATE/DELETE isn't actually
+ * done. If a buffer has been saved in anticipation of a CREATE, delete it.
+ */
+/* ARGSUSED */
+int
+ufs_abortop(ap)
+ struct vop_abortop_args /* {
+ struct vnode *a_dvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
+ FREE(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ return (0);
+}
+
+/*
+ * Lock an inode. If its already locked, set the WANT bit and sleep.
+ */
+int
+ufs_lock(ap)
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip;
+ struct proc *p = curproc; /* XXX */
+
+start:
+ while (vp->v_flag & VXLOCK) {
+ vp->v_flag |= VXWANT;
+ sleep((caddr_t)vp, PINOD);
+ }
+ if (vp->v_tag == VT_NON)
+ return (ENOENT);
+ ip = VTOI(vp);
+ if (ip->i_flag & IN_LOCKED) {
+ ip->i_flag |= IN_WANTED;
+#ifdef DIAGNOSTIC
+ if (p) {
+ if (p->p_pid == ip->i_lockholder)
+ panic("locking against myself");
+ ip->i_lockwaiter = p->p_pid;
+ } else
+ ip->i_lockwaiter = -1;
+#endif
+ (void) sleep((caddr_t)ip, PINOD);
+ goto start;
+ }
+#ifdef DIAGNOSTIC
+ ip->i_lockwaiter = 0;
+ if (ip->i_lockholder != 0)
+ panic("lockholder (%d) != 0", ip->i_lockholder);
+ if (p && p->p_pid == 0)
+ printf("locking by process 0\n");
+ if (p)
+ ip->i_lockholder = p->p_pid;
+ else
+ ip->i_lockholder = -1;
+#endif
+ ip->i_flag |= IN_LOCKED;
+ return (0);
+}
+
+/*
+ * Unlock an inode. If WANT bit is on, wakeup.
+ */
+int lockcount = 90;
+int
+ufs_unlock(ap)
+ struct vop_unlock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct inode *ip = VTOI(ap->a_vp);
+ struct proc *p = curproc; /* XXX */
+
+#ifdef DIAGNOSTIC
+ if ((ip->i_flag & IN_LOCKED) == 0) {
+ vprint("ufs_unlock: unlocked inode", ap->a_vp);
+ panic("ufs_unlock NOT LOCKED");
+ }
+ if (p && p->p_pid != ip->i_lockholder && p->p_pid > -1 &&
+ ip->i_lockholder > -1 && lockcount++ < 100)
+ panic("unlocker (%d) != lock holder (%d)",
+ p->p_pid, ip->i_lockholder);
+ ip->i_lockholder = 0;
+#endif
+ ip->i_flag &= ~IN_LOCKED;
+ if (ip->i_flag & IN_WANTED) {
+ ip->i_flag &= ~IN_WANTED;
+ wakeup((caddr_t)ip);
+ }
+ return (0);
+}
+
+/*
+ * Check for a locked inode.
+ */
+int
+ufs_islocked(ap)
+ struct vop_islocked_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ if (VTOI(ap->a_vp)->i_flag & IN_LOCKED)
+ return (1);
+ return (0);
+}
+
+/*
+ * Calculate the logical to physical mapping if not done already,
+ * then call the device strategy routine.
+ */
+int
+ufs_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ register struct buf *bp = ap->a_bp;
+ register struct vnode *vp = bp->b_vp;
+ register struct inode *ip;
+ int error;
+
+ ip = VTOI(vp);
+ if (vp->v_type == VBLK || vp->v_type == VCHR)
+ panic("ufs_strategy: spec");
+ if (bp->b_blkno == bp->b_lblkno) {
+ if (error =
+ VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL)) {
+ bp->b_error = error;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return (error);
+ }
+ if ((long)bp->b_blkno == -1)
+ clrbuf(bp);
+ }
+ if ((long)bp->b_blkno == -1) {
+ biodone(bp);
+ return (0);
+ }
+ vp = ip->i_devvp;
+ bp->b_dev = vp->v_rdev;
+ VOCALL (vp->v_op, VOFFSET(vop_strategy), ap);
+ return (0);
+}
+
+/*
+ * Print out the contents of an inode.
+ */
+int
+ufs_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip = VTOI(vp);
+
+ printf("tag VT_UFS, ino %d, on dev %d, %d", ip->i_number,
+ major(ip->i_dev), minor(ip->i_dev));
+#ifdef FIFO
+ if (vp->v_type == VFIFO)
+ fifo_printinfo(vp);
+#endif /* FIFO */
+ printf("%s\n", (ip->i_flag & IN_LOCKED) ? " (LOCKED)" : "");
+ if (ip->i_lockholder == 0)
+ return (0);
+ printf("\towner pid %d", ip->i_lockholder);
+ if (ip->i_lockwaiter)
+ printf(" waiting pid %d", ip->i_lockwaiter);
+ printf("\n");
+ return (0);
+}
+
+/*
+ * Read wrapper for special devices.
+ */
+int
+ufsspec_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+
+ /*
+ * Set access flag.
+ */
+ VTOI(ap->a_vp)->i_flag |= IN_ACCESS;
+ return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap));
+}
+
+/*
+ * Write wrapper for special devices.
+ */
+int
+ufsspec_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+
+ /*
+ * Set update and change flags.
+ */
+ VTOI(ap->a_vp)->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap));
+}
+
+/*
+ * Close wrapper for special devices.
+ *
+ * Update the times on the inode then do device close.
+ */
+int
+ufsspec_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct inode *ip = VTOI(ap->a_vp);
+
+ if (ap->a_vp->v_usecount > 1 && !(ip->i_flag & IN_LOCKED))
+ ITIMES(ip, &time, &time);
+ return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap));
+}
+
+#ifdef FIFO
+/*
+ * Read wrapper for fifo's
+ */
+int
+ufsfifo_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ extern int (**fifo_vnodeop_p)();
+
+ /*
+ * Set access flag.
+ */
+ VTOI(ap->a_vp)->i_flag |= IN_ACCESS;
+ return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_read), ap));
+}
+
+/*
+ * Write wrapper for fifo's.
+ */
+int
+ufsfifo_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ extern int (**fifo_vnodeop_p)();
+
+ /*
+ * Set update and change flags.
+ */
+ VTOI(ap->a_vp)->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_write), ap));
+}
+
+/*
+ * Close wrapper for fifo's.
+ *
+ * Update the times on the inode then do device close.
+ */
+int
+ufsfifo_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ extern int (**fifo_vnodeop_p)();
+ register struct inode *ip = VTOI(ap->a_vp);
+
+ if (ap->a_vp->v_usecount > 1 && !(ip->i_flag & IN_LOCKED))
+ ITIMES(ip, &time, &time);
+ return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_close), ap));
+}
+#endif /* FIFO */
+
+/*
+ * Return POSIX pathconf information applicable to ufs filesystems.
+ */
+int
+ufs_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_NAME_MAX:
+ *ap->a_retval = NAME_MAX;
+ return (0);
+ case _PC_PATH_MAX:
+ *ap->a_retval = PATH_MAX;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ case _PC_NO_TRUNC:
+ *ap->a_retval = 1;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Advisory record locking support
+ */
+int
+ufs_advlock(ap)
+ struct vop_advlock_args /* {
+ struct vnode *a_vp;
+ caddr_t a_id;
+ int a_op;
+ struct flock *a_fl;
+ int a_flags;
+ } */ *ap;
+{
+ register struct inode *ip = VTOI(ap->a_vp);
+ register struct flock *fl = ap->a_fl;
+ register struct lockf *lock;
+ off_t start, end;
+ int error;
+
+ /*
+ * Avoid the common case of unlocking when inode has no locks.
+ */
+ if (ip->i_lockf == (struct lockf *)0) {
+ if (ap->a_op != F_SETLK) {
+ fl->l_type = F_UNLCK;
+ return (0);
+ }
+ }
+ /*
+ * Convert the flock structure into a start and end.
+ */
+ switch (fl->l_whence) {
+
+ case SEEK_SET:
+ case SEEK_CUR:
+ /*
+ * Caller is responsible for adding any necessary offset
+ * when SEEK_CUR is used.
+ */
+ start = fl->l_start;
+ break;
+
+ case SEEK_END:
+ start = ip->i_size + fl->l_start;
+ break;
+
+ default:
+ return (EINVAL);
+ }
+ if (start < 0)
+ return (EINVAL);
+ if (fl->l_len == 0)
+ end = -1;
+ else
+ end = start + fl->l_len - 1;
+ /*
+ * Create the lockf structure
+ */
+ MALLOC(lock, struct lockf *, sizeof *lock, M_LOCKF, M_WAITOK);
+ lock->lf_start = start;
+ lock->lf_end = end;
+ lock->lf_id = ap->a_id;
+ lock->lf_inode = ip;
+ lock->lf_type = fl->l_type;
+ lock->lf_next = (struct lockf *)0;
+ lock->lf_block = (struct lockf *)0;
+ lock->lf_flags = ap->a_flags;
+ /*
+ * Do the requested operation.
+ */
+ switch(ap->a_op) {
+ case F_SETLK:
+ return (lf_setlock(lock));
+
+ case F_UNLCK:
+ error = lf_clearlock(lock);
+ FREE(lock, M_LOCKF);
+ return (error);
+
+ case F_GETLK:
+ error = lf_getlock(lock, fl);
+ FREE(lock, M_LOCKF);
+ return (error);
+
+ default:
+ free(lock, M_LOCKF);
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Initialize the vnode associated with a new inode, handle aliased
+ * vnodes.
+ */
+int
+ufs_vinit(mntp, specops, fifoops, vpp)
+ struct mount *mntp;
+ int (**specops)();
+ int (**fifoops)();
+ struct vnode **vpp;
+{
+ struct inode *ip;
+ struct vnode *vp, *nvp;
+
+ vp = *vpp;
+ ip = VTOI(vp);
+ switch(vp->v_type = IFTOVT(ip->i_mode)) {
+ case VCHR:
+ case VBLK:
+ vp->v_op = specops;
+ if (nvp = checkalias(vp, ip->i_rdev, mntp)) {
+ /*
+ * Discard unneeded vnode, but save its inode.
+ */
+ ufs_ihashrem(ip);
+ VOP_UNLOCK(vp);
+ nvp->v_data = vp->v_data;
+ vp->v_data = NULL;
+ vp->v_op = spec_vnodeop_p;
+ vrele(vp);
+ vgone(vp);
+ /*
+ * Reinitialize aliased inode.
+ */
+ vp = nvp;
+ ip->i_vnode = vp;
+ ufs_ihashins(ip);
+ }
+ break;
+ case VFIFO:
+#ifdef FIFO
+ vp->v_op = fifoops;
+ break;
+#else
+ return (EOPNOTSUPP);
+#endif
+ }
+ if (ip->i_number == ROOTINO)
+ vp->v_flag |= VROOT;
+ /*
+ * Initialize modrev times
+ */
+ SETHIGH(ip->i_modrev, mono_time.tv_sec);
+ SETLOW(ip->i_modrev, mono_time.tv_usec * 4294);
+ *vpp = vp;
+ return (0);
+}
+
+/*
+ * Allocate a new inode.
+ */
+int
+ufs_makeinode(mode, dvp, vpp, cnp)
+ int mode;
+ struct vnode *dvp;
+ struct vnode **vpp;
+ struct componentname *cnp;
+{
+ register struct inode *ip, *pdir;
+ struct timeval tv;
+ struct vnode *tvp;
+ int error;
+
+ pdir = VTOI(dvp);
+#ifdef DIAGNOSTIC
+ if ((cnp->cn_flags & HASBUF) == 0)
+ panic("ufs_makeinode: no name");
+#endif
+ *vpp = NULL;
+ if ((mode & IFMT) == 0)
+ mode |= IFREG;
+
+ if (error = VOP_VALLOC(dvp, mode, cnp->cn_cred, &tvp)) {
+ free(cnp->cn_pnbuf, M_NAMEI);
+ vput(dvp);
+ return (error);
+ }
+ ip = VTOI(tvp);
+ ip->i_gid = pdir->i_gid;
+ if ((mode & IFMT) == IFLNK)
+ ip->i_uid = pdir->i_uid;
+ else
+ ip->i_uid = cnp->cn_cred->cr_uid;
+#ifdef QUOTA
+ if ((error = getinoquota(ip)) ||
+ (error = chkiq(ip, 1, cnp->cn_cred, 0))) {
+ free(cnp->cn_pnbuf, M_NAMEI);
+ VOP_VFREE(tvp, ip->i_number, mode);
+ vput(tvp);
+ vput(dvp);
+ return (error);
+ }
+#endif
+ ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
+ ip->i_mode = mode;
+ tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */
+ ip->i_nlink = 1;
+ if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, cnp->cn_cred) &&
+ suser(cnp->cn_cred, NULL))
+ ip->i_mode &= ~ISGID;
+
+ /*
+ * Make sure inode goes to disk before directory entry.
+ */
+ tv = time;
+ if (error = VOP_UPDATE(tvp, &tv, &tv, 1))
+ goto bad;
+ if (error = ufs_direnter(ip, dvp, cnp))
+ goto bad;
+ if ((cnp->cn_flags & SAVESTART) == 0)
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ vput(dvp);
+ *vpp = tvp;
+ return (0);
+
+bad:
+ /*
+ * Write error occurred trying to update the inode
+ * or the directory so must deallocate the inode.
+ */
+ free(cnp->cn_pnbuf, M_NAMEI);
+ vput(dvp);
+ ip->i_nlink = 0;
+ ip->i_flag |= IN_CHANGE;
+ vput(tvp);
+ return (error);
+}
diff --git a/sys/ufs/ufs/ufsmount.h b/sys/ufs/ufs/ufsmount.h
new file mode 100644
index 0000000..237871f
--- /dev/null
+++ b/sys/ufs/ufs/ufsmount.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufsmount.h 8.2 (Berkeley) 1/12/94
+ */
+
+struct buf;
+struct inode;
+struct nameidata;
+struct timeval;
+struct ucred;
+struct uio;
+struct vnode;
+struct netexport;
+
+/* This structure describes the UFS specific mount structure data. */
+struct ufsmount {
+ struct mount *um_mountp; /* filesystem vfs structure */
+ dev_t um_dev; /* device mounted */
+ struct vnode *um_devvp; /* block device mounted vnode */
+ union { /* pointer to superblock */
+ struct lfs *lfs; /* LFS */
+ struct fs *fs; /* FFS */
+ } ufsmount_u;
+#define um_fs ufsmount_u.fs
+#define um_lfs ufsmount_u.lfs
+ struct vnode *um_quotas[MAXQUOTAS]; /* pointer to quota files */
+ struct ucred *um_cred[MAXQUOTAS]; /* quota file access cred */
+ u_long um_nindir; /* indirect ptrs per block */
+ u_long um_bptrtodb; /* indir ptr to disk block */
+ u_long um_seqinc; /* inc between seq blocks */
+ time_t um_btime[MAXQUOTAS]; /* block quota time limit */
+ time_t um_itime[MAXQUOTAS]; /* inode quota time limit */
+ char um_qflags[MAXQUOTAS]; /* quota specific flags */
+ struct netexport um_export; /* export information */
+};
+/*
+ * Flags describing the state of quotas.
+ */
+#define QTF_OPENING 0x01 /* Q_QUOTAON in progress */
+#define QTF_CLOSING 0x02 /* Q_QUOTAOFF in progress */
+
+/* Convert mount ptr to ufsmount ptr. */
+#define VFSTOUFS(mp) ((struct ufsmount *)((mp)->mnt_data))
+
+/*
+ * Macros to access file system parameters in the ufsmount structure.
+ * Used by ufs_bmap.
+ */
+#define blkptrtodb(ump, b) ((b) << (ump)->um_bptrtodb)
+#define is_sequential(ump, a, b) ((b) == (a) + ump->um_seqinc)
+#define MNINDIR(ump) ((ump)->um_nindir)
+
+
diff --git a/sys/vm/device_pager.c b/sys/vm/device_pager.c
new file mode 100644
index 0000000..b8083df
--- /dev/null
+++ b/sys/vm/device_pager.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 1990 University of Utah.
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)device_pager.c 8.1 (Berkeley) 6/11/93
+ */
+
+/*
+ * Page to/from special files.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/mman.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+#include <vm/device_pager.h>
+
+struct pagerlst dev_pager_list; /* list of managed devices */
+struct pglist dev_pager_fakelist; /* list of available vm_page_t's */
+
+#ifdef DEBUG
+int dpagerdebug = 0;
+#define DDB_FOLLOW 0x01
+#define DDB_INIT 0x02
+#define DDB_ALLOC 0x04
+#define DDB_FAIL 0x08
+#endif
+
+static vm_pager_t dev_pager_alloc
+ __P((caddr_t, vm_size_t, vm_prot_t, vm_offset_t));
+static void dev_pager_dealloc __P((vm_pager_t));
+static int dev_pager_getpage
+ __P((vm_pager_t, vm_page_t, boolean_t));
+static boolean_t dev_pager_haspage __P((vm_pager_t, vm_offset_t));
+static void dev_pager_init __P((void));
+static int dev_pager_putpage
+ __P((vm_pager_t, vm_page_t, boolean_t));
+static vm_page_t dev_pager_getfake __P((vm_offset_t));
+static void dev_pager_putfake __P((vm_page_t));
+
+struct pagerops devicepagerops = {
+ dev_pager_init,
+ dev_pager_alloc,
+ dev_pager_dealloc,
+ dev_pager_getpage,
+ 0,
+ dev_pager_putpage,
+ 0,
+ dev_pager_haspage
+};
+
+static void
+dev_pager_init()
+{
+#ifdef DEBUG
+ if (dpagerdebug & DDB_FOLLOW)
+ printf("dev_pager_init()\n");
+#endif
+ TAILQ_INIT(&dev_pager_list);
+ TAILQ_INIT(&dev_pager_fakelist);
+}
+
+static vm_pager_t
+dev_pager_alloc(handle, size, prot, foff)
+ caddr_t handle;
+ vm_size_t size;
+ vm_prot_t prot;
+ vm_offset_t foff;
+{
+ dev_t dev;
+ vm_pager_t pager;
+ int (*mapfunc)();
+ vm_object_t object;
+ dev_pager_t devp;
+ unsigned int npages, off;
+
+#ifdef DEBUG
+ if (dpagerdebug & DDB_FOLLOW)
+ printf("dev_pager_alloc(%x, %x, %x, %x)\n",
+ handle, size, prot, foff);
+#endif
+#ifdef DIAGNOSTIC
+ /*
+ * Pageout to device, should never happen.
+ */
+ if (handle == NULL)
+ panic("dev_pager_alloc called");
+#endif
+
+ /*
+ * Make sure this device can be mapped.
+ */
+ dev = (dev_t)(u_long)handle;
+ mapfunc = cdevsw[major(dev)].d_mmap;
+ if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop)
+ return(NULL);
+
+ /*
+ * Offset should be page aligned.
+ */
+ if (foff & (PAGE_SIZE-1))
+ return(NULL);
+
+ /*
+ * Check that the specified range of the device allows the
+ * desired protection.
+ *
+ * XXX assumes VM_PROT_* == PROT_*
+ */
+ npages = atop(round_page(size));
+ for (off = foff; npages--; off += PAGE_SIZE)
+ if ((*mapfunc)(dev, off, (int)prot) == -1)
+ return(NULL);
+
+ /*
+ * Look up pager, creating as necessary.
+ */
+top:
+ pager = vm_pager_lookup(&dev_pager_list, handle);
+ if (pager == NULL) {
+ /*
+ * Allocate and initialize pager structs
+ */
+ pager = (vm_pager_t)malloc(sizeof *pager, M_VMPAGER, M_WAITOK);
+ if (pager == NULL)
+ return(NULL);
+ devp = (dev_pager_t)malloc(sizeof *devp, M_VMPGDATA, M_WAITOK);
+ if (devp == NULL) {
+ free((caddr_t)pager, M_VMPAGER);
+ return(NULL);
+ }
+ pager->pg_handle = handle;
+ pager->pg_ops = &devicepagerops;
+ pager->pg_type = PG_DEVICE;
+ pager->pg_data = (caddr_t)devp;
+ pager->pg_flags = 0;
+ TAILQ_INIT(&devp->devp_pglist);
+ /*
+ * Allocate object and associate it with the pager.
+ */
+ object = devp->devp_object = vm_object_allocate(0);
+ vm_object_enter(object, pager);
+ vm_object_setpager(object, pager, (vm_offset_t)foff, FALSE);
+ /*
+ * Finally, put it on the managed list so other can find it.
+ * First we re-lookup in case someone else beat us to this
+ * point (due to blocking in the various mallocs). If so,
+ * we free everything and start over.
+ */
+ if (vm_pager_lookup(&dev_pager_list, handle)) {
+ free((caddr_t)devp, M_VMPGDATA);
+ free((caddr_t)pager, M_VMPAGER);
+ goto top;
+ }
+ TAILQ_INSERT_TAIL(&dev_pager_list, pager, pg_list);
+#ifdef DEBUG
+ if (dpagerdebug & DDB_ALLOC) {
+ printf("dev_pager_alloc: pager %x devp %x object %x\n",
+ pager, devp, object);
+ vm_object_print(object, FALSE);
+ }
+#endif
+ } else {
+ /*
+ * vm_object_lookup() gains a reference and also
+ * removes the object from the cache.
+ */
+ object = vm_object_lookup(pager);
+#ifdef DIAGNOSTIC
+ devp = (dev_pager_t)pager->pg_data;
+ if (object != devp->devp_object)
+ panic("dev_pager_setup: bad object");
+#endif
+ }
+ return(pager);
+}
+
+static void
+dev_pager_dealloc(pager)
+ vm_pager_t pager;
+{
+ dev_pager_t devp;
+ vm_object_t object;
+ vm_page_t m;
+
+#ifdef DEBUG
+ if (dpagerdebug & DDB_FOLLOW)
+ printf("dev_pager_dealloc(%x)\n", pager);
+#endif
+ TAILQ_REMOVE(&dev_pager_list, pager, pg_list);
+ /*
+ * Get the object.
+ * Note: cannot use vm_object_lookup since object has already
+ * been removed from the hash chain.
+ */
+ devp = (dev_pager_t)pager->pg_data;
+ object = devp->devp_object;
+#ifdef DEBUG
+ if (dpagerdebug & DDB_ALLOC)
+ printf("dev_pager_dealloc: devp %x object %x\n", devp, object);
+#endif
+ /*
+ * Free up our fake pages.
+ */
+ while (m=devp->devp_pglist.tqh_first) {
+ TAILQ_REMOVE(&devp->devp_pglist, m, pageq);
+ dev_pager_putfake(m);
+ }
+ free((caddr_t)devp, M_VMPGDATA);
+ free((caddr_t)pager, M_VMPAGER);
+}
+
+static int
+dev_pager_getpage(pager, m, sync)
+ vm_pager_t pager;
+ vm_page_t m;
+ boolean_t sync;
+{
+ register vm_object_t object;
+ vm_offset_t offset, paddr;
+ vm_page_t page;
+ dev_t dev;
+ int s;
+ int (*mapfunc)(), prot;
+
+#ifdef DEBUG
+ if (dpagerdebug & DDB_FOLLOW)
+ printf("dev_pager_getpage(%x, %x)\n", pager, m);
+#endif
+
+ object = m->object;
+ dev = (dev_t)(u_long)pager->pg_handle;
+ offset = m->offset + object->paging_offset;
+ prot = PROT_READ; /* XXX should pass in? */
+ mapfunc = cdevsw[major(dev)].d_mmap;
+
+ if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop)
+ panic("dev_pager_getpage: no map function");
+
+ paddr = pmap_phys_address((*mapfunc)((dev_t)dev, (int)offset, prot));
+#ifdef DIAGNOSTIC
+ if (paddr == -1)
+ panic("dev_pager_getpage: map function returns error");
+#endif
+ /*
+ * Replace the passed in page with our own fake page and free
+ * up the original.
+ */
+ page = dev_pager_getfake(paddr);
+ TAILQ_INSERT_TAIL(&((dev_pager_t)pager->pg_data)->devp_pglist,
+ page, pageq);
+ vm_object_lock(object);
+ vm_page_lock_queues();
+ vm_page_free(m);
+ vm_page_unlock_queues();
+ s = splhigh();
+ vm_page_insert(page, object, offset);
+ splx(s);
+ PAGE_WAKEUP(m);
+ if (offset + PAGE_SIZE > object->size)
+ object->size = offset + PAGE_SIZE; /* XXX anal */
+ vm_object_unlock(object);
+
+ return(VM_PAGER_OK);
+}
+
+static int
+dev_pager_putpage(pager, m, sync)
+ vm_pager_t pager;
+ vm_page_t m;
+ boolean_t sync;
+{
+#ifdef DEBUG
+ if (dpagerdebug & DDB_FOLLOW)
+ printf("dev_pager_putpage(%x, %x)\n", pager, m);
+#endif
+ if (pager == NULL)
+ return 0;
+ panic("dev_pager_putpage called");
+}
+
+static boolean_t
+dev_pager_haspage(pager, offset)
+ vm_pager_t pager;
+ vm_offset_t offset;
+{
+#ifdef DEBUG
+ if (dpagerdebug & DDB_FOLLOW)
+ printf("dev_pager_haspage(%x, %x)\n", pager, offset);
+#endif
+ return(TRUE);
+}
+
+static vm_page_t
+dev_pager_getfake(paddr)
+ vm_offset_t paddr;
+{
+ vm_page_t m;
+ int i;
+
+ if (dev_pager_fakelist.tqh_first == NULL) {
+ m = (vm_page_t)malloc(PAGE_SIZE, M_VMPGDATA, M_WAITOK);
+ for (i = PAGE_SIZE / sizeof(*m); i > 0; i--) {
+ TAILQ_INSERT_TAIL(&dev_pager_fakelist, m, pageq);
+ m++;
+ }
+ }
+ m = dev_pager_fakelist.tqh_first;
+ TAILQ_REMOVE(&dev_pager_fakelist, m, pageq);
+
+ m->flags = PG_BUSY | PG_CLEAN | PG_FAKE | PG_FICTITIOUS;
+
+ m->wire_count = 1;
+ m->phys_addr = paddr;
+
+ return(m);
+}
+
+static void
+dev_pager_putfake(m)
+ vm_page_t m;
+{
+#ifdef DIAGNOSTIC
+ if (!(m->flags & PG_FICTITIOUS))
+ panic("dev_pager_putfake: bad page");
+#endif
+ TAILQ_INSERT_TAIL(&dev_pager_fakelist, m, pageq);
+}
diff --git a/sys/vm/device_pager.h b/sys/vm/device_pager.h
new file mode 100644
index 0000000..88406229
--- /dev/null
+++ b/sys/vm/device_pager.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1990 University of Utah.
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)device_pager.h 8.3 (Berkeley) 12/13/93
+ */
+
+#ifndef _DEVICE_PAGER_
+#define _DEVICE_PAGER_ 1
+
+/*
+ * Device pager private data.
+ */
+struct devpager {
+ struct pglist devp_pglist; /* list of pages allocated */
+ vm_object_t devp_object; /* object representing this device */
+};
+typedef struct devpager *dev_pager_t;
+
+#endif /* _DEVICE_PAGER_ */
diff --git a/sys/vm/kern_lock.c b/sys/vm/kern_lock.c
new file mode 100644
index 0000000..c4fa052
--- /dev/null
+++ b/sys/vm/kern_lock.c
@@ -0,0 +1,534 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kern_lock.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Locking primitives implementation
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <vm/vm.h>
+
+/* XXX */
+#include <sys/proc.h>
+typedef int *thread_t;
+#define current_thread() ((thread_t)&curproc->p_thread)
+/* XXX */
+
+#if NCPUS > 1
+
+/*
+ * Module: lock
+ * Function:
+ * Provide reader/writer sychronization.
+ * Implementation:
+ * Simple interlock on a bit. Readers first interlock
+ * increment the reader count, then let go. Writers hold
+ * the interlock (thus preventing further readers), and
+ * wait for already-accepted readers to go away.
+ */
+
+/*
+ * The simple-lock routines are the primitives out of which
+ * the lock package is built. The implementation is left
+ * to the machine-dependent code.
+ */
+
+#ifdef notdef
+/*
+ * A sample implementation of simple locks.
+ * assumes:
+ * boolean_t test_and_set(boolean_t *)
+ * indivisibly sets the boolean to TRUE
+ * and returns its old value
+ * and that setting a boolean to FALSE is indivisible.
+ */
+/*
+ * simple_lock_init initializes a simple lock. A simple lock
+ * may only be used for exclusive locks.
+ */
+
+void simple_lock_init(l)
+ simple_lock_t l;
+{
+ *(boolean_t *)l = FALSE;
+}
+
+void simple_lock(l)
+ simple_lock_t l;
+{
+ while (test_and_set((boolean_t *)l))
+ continue;
+}
+
+void simple_unlock(l)
+ simple_lock_t l;
+{
+ *(boolean_t *)l = FALSE;
+}
+
+boolean_t simple_lock_try(l)
+ simple_lock_t l;
+{
+ return (!test_and_set((boolean_t *)l));
+}
+#endif /* notdef */
+#endif /* NCPUS > 1 */
+
+#if NCPUS > 1
+int lock_wait_time = 100;
+#else /* NCPUS > 1 */
+
+ /*
+ * It is silly to spin on a uni-processor as if we
+ * thought something magical would happen to the
+ * want_write bit while we are executing.
+ */
+int lock_wait_time = 0;
+#endif /* NCPUS > 1 */
+
+
+/*
+ * Routine: lock_init
+ * Function:
+ * Initialize a lock; required before use.
+ * Note that clients declare the "struct lock"
+ * variables and then initialize them, rather
+ * than getting a new one from this module.
+ */
+void lock_init(l, can_sleep)
+ lock_t l;
+ boolean_t can_sleep;
+{
+ bzero(l, sizeof(lock_data_t));
+ simple_lock_init(&l->interlock);
+ l->want_write = FALSE;
+ l->want_upgrade = FALSE;
+ l->read_count = 0;
+ l->can_sleep = can_sleep;
+ l->thread = (char *)-1; /* XXX */
+ l->recursion_depth = 0;
+}
+
+void lock_sleepable(l, can_sleep)
+ lock_t l;
+ boolean_t can_sleep;
+{
+ simple_lock(&l->interlock);
+ l->can_sleep = can_sleep;
+ simple_unlock(&l->interlock);
+}
+
+
+/*
+ * Sleep locks. These use the same data structure and algorithm
+ * as the spin locks, but the process sleeps while it is waiting
+ * for the lock. These work on uniprocessor systems.
+ */
+
+void lock_write(l)
+ register lock_t l;
+{
+ register int i;
+
+ simple_lock(&l->interlock);
+
+ if (((thread_t)l->thread) == current_thread()) {
+ /*
+ * Recursive lock.
+ */
+ l->recursion_depth++;
+ simple_unlock(&l->interlock);
+ return;
+ }
+
+ /*
+ * Try to acquire the want_write bit.
+ */
+ while (l->want_write) {
+ if ((i = lock_wait_time) > 0) {
+ simple_unlock(&l->interlock);
+ while (--i > 0 && l->want_write)
+ continue;
+ simple_lock(&l->interlock);
+ }
+
+ if (l->can_sleep && l->want_write) {
+ l->waiting = TRUE;
+ thread_sleep((int) l, &l->interlock, FALSE);
+ simple_lock(&l->interlock);
+ }
+ }
+ l->want_write = TRUE;
+
+ /* Wait for readers (and upgrades) to finish */
+
+ while ((l->read_count != 0) || l->want_upgrade) {
+ if ((i = lock_wait_time) > 0) {
+ simple_unlock(&l->interlock);
+ while (--i > 0 && (l->read_count != 0 ||
+ l->want_upgrade))
+ continue;
+ simple_lock(&l->interlock);
+ }
+
+ if (l->can_sleep && (l->read_count != 0 || l->want_upgrade)) {
+ l->waiting = TRUE;
+ thread_sleep((int) l, &l->interlock, FALSE);
+ simple_lock(&l->interlock);
+ }
+ }
+ simple_unlock(&l->interlock);
+}
+
+void lock_done(l)
+ register lock_t l;
+{
+ simple_lock(&l->interlock);
+
+ if (l->read_count != 0)
+ l->read_count--;
+ else
+ if (l->recursion_depth != 0)
+ l->recursion_depth--;
+ else
+ if (l->want_upgrade)
+ l->want_upgrade = FALSE;
+ else
+ l->want_write = FALSE;
+
+ if (l->waiting) {
+ l->waiting = FALSE;
+ thread_wakeup((int) l);
+ }
+ simple_unlock(&l->interlock);
+}
+
+void lock_read(l)
+ register lock_t l;
+{
+ register int i;
+
+ simple_lock(&l->interlock);
+
+ if (((thread_t)l->thread) == current_thread()) {
+ /*
+ * Recursive lock.
+ */
+ l->read_count++;
+ simple_unlock(&l->interlock);
+ return;
+ }
+
+ while (l->want_write || l->want_upgrade) {
+ if ((i = lock_wait_time) > 0) {
+ simple_unlock(&l->interlock);
+ while (--i > 0 && (l->want_write || l->want_upgrade))
+ continue;
+ simple_lock(&l->interlock);
+ }
+
+ if (l->can_sleep && (l->want_write || l->want_upgrade)) {
+ l->waiting = TRUE;
+ thread_sleep((int) l, &l->interlock, FALSE);
+ simple_lock(&l->interlock);
+ }
+ }
+
+ l->read_count++;
+ simple_unlock(&l->interlock);
+}
+
+/*
+ * Routine: lock_read_to_write
+ * Function:
+ * Improves a read-only lock to one with
+ * write permission. If another reader has
+ * already requested an upgrade to a write lock,
+ * no lock is held upon return.
+ *
+ * Returns TRUE if the upgrade *failed*.
+ */
+boolean_t lock_read_to_write(l)
+ register lock_t l;
+{
+ register int i;
+
+ simple_lock(&l->interlock);
+
+ l->read_count--;
+
+ if (((thread_t)l->thread) == current_thread()) {
+ /*
+ * Recursive lock.
+ */
+ l->recursion_depth++;
+ simple_unlock(&l->interlock);
+ return(FALSE);
+ }
+
+ if (l->want_upgrade) {
+ /*
+ * Someone else has requested upgrade.
+ * Since we've released a read lock, wake
+ * him up.
+ */
+ if (l->waiting) {
+ l->waiting = FALSE;
+ thread_wakeup((int) l);
+ }
+
+ simple_unlock(&l->interlock);
+ return (TRUE);
+ }
+
+ l->want_upgrade = TRUE;
+
+ while (l->read_count != 0) {
+ if ((i = lock_wait_time) > 0) {
+ simple_unlock(&l->interlock);
+ while (--i > 0 && l->read_count != 0)
+ continue;
+ simple_lock(&l->interlock);
+ }
+
+ if (l->can_sleep && l->read_count != 0) {
+ l->waiting = TRUE;
+ thread_sleep((int) l, &l->interlock, FALSE);
+ simple_lock(&l->interlock);
+ }
+ }
+
+ simple_unlock(&l->interlock);
+ return (FALSE);
+}
+
+void lock_write_to_read(l)
+ register lock_t l;
+{
+ simple_lock(&l->interlock);
+
+ l->read_count++;
+ if (l->recursion_depth != 0)
+ l->recursion_depth--;
+ else
+ if (l->want_upgrade)
+ l->want_upgrade = FALSE;
+ else
+ l->want_write = FALSE;
+
+ if (l->waiting) {
+ l->waiting = FALSE;
+ thread_wakeup((int) l);
+ }
+
+ simple_unlock(&l->interlock);
+}
+
+
+/*
+ * Routine: lock_try_write
+ * Function:
+ * Tries to get a write lock.
+ *
+ * Returns FALSE if the lock is not held on return.
+ */
+
+boolean_t lock_try_write(l)
+ register lock_t l;
+{
+
+ simple_lock(&l->interlock);
+
+ if (((thread_t)l->thread) == current_thread()) {
+ /*
+ * Recursive lock
+ */
+ l->recursion_depth++;
+ simple_unlock(&l->interlock);
+ return(TRUE);
+ }
+
+ if (l->want_write || l->want_upgrade || l->read_count) {
+ /*
+ * Can't get lock.
+ */
+ simple_unlock(&l->interlock);
+ return(FALSE);
+ }
+
+ /*
+ * Have lock.
+ */
+
+ l->want_write = TRUE;
+ simple_unlock(&l->interlock);
+ return(TRUE);
+}
+
+/*
+ * Routine: lock_try_read
+ * Function:
+ * Tries to get a read lock.
+ *
+ * Returns FALSE if the lock is not held on return.
+ */
+
+boolean_t lock_try_read(l)
+ register lock_t l;
+{
+ simple_lock(&l->interlock);
+
+ if (((thread_t)l->thread) == current_thread()) {
+ /*
+ * Recursive lock
+ */
+ l->read_count++;
+ simple_unlock(&l->interlock);
+ return(TRUE);
+ }
+
+ if (l->want_write || l->want_upgrade) {
+ simple_unlock(&l->interlock);
+ return(FALSE);
+ }
+
+ l->read_count++;
+ simple_unlock(&l->interlock);
+ return(TRUE);
+}
+
+/*
+ * Routine: lock_try_read_to_write
+ * Function:
+ * Improves a read-only lock to one with
+ * write permission. If another reader has
+ * already requested an upgrade to a write lock,
+ * the read lock is still held upon return.
+ *
+ * Returns FALSE if the upgrade *failed*.
+ */
+boolean_t lock_try_read_to_write(l)
+ register lock_t l;
+{
+
+ simple_lock(&l->interlock);
+
+ if (((thread_t)l->thread) == current_thread()) {
+ /*
+ * Recursive lock
+ */
+ l->read_count--;
+ l->recursion_depth++;
+ simple_unlock(&l->interlock);
+ return(TRUE);
+ }
+
+ if (l->want_upgrade) {
+ simple_unlock(&l->interlock);
+ return(FALSE);
+ }
+ l->want_upgrade = TRUE;
+ l->read_count--;
+
+ while (l->read_count != 0) {
+ l->waiting = TRUE;
+ thread_sleep((int) l, &l->interlock, FALSE);
+ simple_lock(&l->interlock);
+ }
+
+ simple_unlock(&l->interlock);
+ return(TRUE);
+}
+
+/*
+ * Allow a process that has a lock for write to acquire it
+ * recursively (for read, write, or update).
+ */
+void lock_set_recursive(l)
+ lock_t l;
+{
+ simple_lock(&l->interlock);
+ if (!l->want_write) {
+ panic("lock_set_recursive: don't have write lock");
+ }
+ l->thread = (char *) current_thread();
+ simple_unlock(&l->interlock);
+}
+
+/*
+ * Prevent a lock from being re-acquired.
+ */
+void lock_clear_recursive(l)
+ lock_t l;
+{
+ simple_lock(&l->interlock);
+ if (((thread_t) l->thread) != current_thread()) {
+ panic("lock_clear_recursive: wrong thread");
+ }
+ if (l->recursion_depth == 0)
+ l->thread = (char *)-1; /* XXX */
+ simple_unlock(&l->interlock);
+}
diff --git a/sys/vm/lock.h b/sys/vm/lock.h
new file mode 100644
index 0000000..26bed1f
--- /dev/null
+++ b/sys/vm/lock.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lock.h 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Locking primitives definitions
+ */
+
+#ifndef _LOCK_H_
+#define _LOCK_H_
+
+#define NCPUS 1 /* XXX */
+
+/*
+ * A simple spin lock.
+ */
+
+struct slock {
+ int lock_data; /* in general 1 bit is sufficient */
+};
+
+typedef struct slock simple_lock_data_t;
+typedef struct slock *simple_lock_t;
+
+/*
+ * The general lock structure. Provides for multiple readers,
+ * upgrading from read to write, and sleeping until the lock
+ * can be gained.
+ */
+
+struct lock {
+#ifdef vax
+ /*
+ * Efficient VAX implementation -- see field description below.
+ */
+ unsigned int read_count:16,
+ want_upgrade:1,
+ want_write:1,
+ waiting:1,
+ can_sleep:1,
+ :0;
+
+ simple_lock_data_t interlock;
+#else /* vax */
+#ifdef ns32000
+ /*
+ * Efficient ns32000 implementation --
+ * see field description below.
+ */
+ simple_lock_data_t interlock;
+ unsigned int read_count:16,
+ want_upgrade:1,
+ want_write:1,
+ waiting:1,
+ can_sleep:1,
+ :0;
+
+#else /* ns32000 */
+ /* Only the "interlock" field is used for hardware exclusion;
+ * other fields are modified with normal instructions after
+ * acquiring the interlock bit.
+ */
+ simple_lock_data_t
+ interlock; /* Interlock for remaining fields */
+ boolean_t want_write; /* Writer is waiting, or locked for write */
+ boolean_t want_upgrade; /* Read-to-write upgrade waiting */
+ boolean_t waiting; /* Someone is sleeping on lock */
+ boolean_t can_sleep; /* Can attempts to lock go to sleep */
+ int read_count; /* Number of accepted readers */
+#endif /* ns32000 */
+#endif /* vax */
+ char *thread; /* Thread that has lock, if recursive locking allowed */
+ /* (should be thread_t, but but we then have mutually
+ recursive definitions) */
+ int recursion_depth;/* Depth of recursion */
+};
+
+typedef struct lock lock_data_t;
+typedef struct lock *lock_t;
+
+#if NCPUS > 1
+__BEGIN_DECLS
+void simple_lock __P((simple_lock_t));
+void simple_lock_init __P((simple_lock_t));
+boolean_t simple_lock_try __P((simple_lock_t));
+void simple_unlock __P((simple_lock_t));
+__END_DECLS
+#else /* No multiprocessor locking is necessary. */
+#define simple_lock(l)
+#define simple_lock_init(l)
+#define simple_lock_try(l) (1) /* Always succeeds. */
+#define simple_unlock(l)
+#endif
+
+/* Sleep locks must work even if no multiprocessing. */
+
+#define lock_read_done(l) lock_done(l)
+#define lock_write_done(l) lock_done(l)
+
+void lock_clear_recursive __P((lock_t));
+void lock_done __P((lock_t));
+void lock_init __P((lock_t, boolean_t));
+void lock_read __P((lock_t));
+boolean_t lock_read_to_write __P((lock_t));
+void lock_set_recursive __P((lock_t));
+void lock_sleepable __P((lock_t, boolean_t));
+boolean_t lock_try_read __P((lock_t));
+boolean_t lock_try_read_to_write __P((lock_t));
+boolean_t lock_try_write __P((lock_t));
+void lock_write __P((lock_t));
+void lock_write_to_read __P((lock_t));
+#endif /* !_LOCK_H_ */
diff --git a/sys/vm/pmap.h b/sys/vm/pmap.h
new file mode 100644
index 0000000..63a83c9
--- /dev/null
+++ b/sys/vm/pmap.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pmap.h 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Author: Avadis Tevanian, Jr.
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Machine address mapping definitions -- machine-independent
+ * section. [For machine-dependent section, see "machine/pmap.h".]
+ */
+
+#ifndef _PMAP_VM_
+#define _PMAP_VM_
+
+/*
+ * Each machine dependent implementation is expected to
+ * keep certain statistics. They may do this anyway they
+ * so choose, but are expected to return the statistics
+ * in the following structure.
+ */
+struct pmap_statistics {
+ long resident_count; /* # of pages mapped (total)*/
+ long wired_count; /* # of pages wired */
+};
+typedef struct pmap_statistics *pmap_statistics_t;
+
+#include <machine/pmap.h>
+
+#ifdef KERNEL
+__BEGIN_DECLS
+void *pmap_bootstrap_alloc __P((int));
+void pmap_bootstrap( /* machine dependent */ );
+void pmap_change_wiring __P((pmap_t, vm_offset_t, boolean_t));
+void pmap_clear_modify __P((vm_offset_t pa));
+void pmap_clear_reference __P((vm_offset_t pa));
+void pmap_collect __P((pmap_t));
+void pmap_copy __P((pmap_t,
+ pmap_t, vm_offset_t, vm_size_t, vm_offset_t));
+void pmap_copy_page __P((vm_offset_t, vm_offset_t));
+pmap_t pmap_create __P((vm_size_t));
+void pmap_destroy __P((pmap_t));
+void pmap_enter __P((pmap_t,
+ vm_offset_t, vm_offset_t, vm_prot_t, boolean_t));
+vm_offset_t pmap_extract __P((pmap_t, vm_offset_t));
+void pmap_init __P((vm_offset_t, vm_offset_t));
+boolean_t pmap_is_modified __P((vm_offset_t pa));
+boolean_t pmap_is_referenced __P((vm_offset_t pa));
+vm_offset_t pmap_map __P((vm_offset_t, vm_offset_t, vm_offset_t, int));
+void pmap_page_protect __P((vm_offset_t, vm_prot_t));
+void pmap_pageable __P((pmap_t,
+ vm_offset_t, vm_offset_t, boolean_t));
+vm_offset_t pmap_phys_address __P((int));
+void pmap_pinit __P((pmap_t));
+void pmap_protect __P((pmap_t,
+ vm_offset_t, vm_offset_t, vm_prot_t));
+void pmap_reference __P((pmap_t));
+void pmap_release __P((pmap_t));
+void pmap_remove __P((pmap_t, vm_offset_t, vm_offset_t));
+void pmap_update __P((void));
+void pmap_zero_page __P((vm_offset_t));
+__END_DECLS
+#endif
+
+#endif /* _PMAP_VM_ */
diff --git a/sys/vm/swap_pager.c b/sys/vm/swap_pager.c
new file mode 100644
index 0000000..a534d42
--- /dev/null
+++ b/sys/vm/swap_pager.c
@@ -0,0 +1,1833 @@
+/*
+ * Copyright (c) 1994 John S. Dyson
+ * Copyright (c) 1990 University of Utah.
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: Utah $Hdr: swap_pager.c 1.4 91/04/30$
+ *
+ * @(#)swap_pager.c 8.9 (Berkeley) 3/21/94
+ */
+
+/*
+ * Quick hack to page to dedicated partition(s).
+ * TODO:
+ * Add multiprocessor locks
+ * Deal with async writes in a better fashion
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+
+#include <miscfs/specfs/specdev.h>
+#include <sys/rlist.h>
+
+#include <vm/vm.h>
+#include <vm/vm_pager.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pageout.h>
+#include <vm/swap_pager.h>
+
+#ifndef NPENDINGIO
+#define NPENDINGIO 16
+#endif
+
+extern int nswbuf;
+int nswiodone;
+extern int vm_pageout_rate_limit;
+static int cleandone;
+extern int hz;
+int swap_pager_full;
+extern vm_map_t pager_map;
+extern int vm_pageout_pages_needed;
+extern int vm_swap_size;
+extern struct vnode *swapdev_vp;
+
+#define MAX_PAGEOUT_CLUSTER 8
+
+TAILQ_HEAD(swpclean, swpagerclean);
+
+typedef struct swpagerclean *swp_clean_t;
+
+struct swpagerclean {
+ TAILQ_ENTRY(swpagerclean) spc_list;
+ int spc_flags;
+ struct buf *spc_bp;
+ sw_pager_t spc_swp;
+ vm_offset_t spc_kva;
+ vm_offset_t spc_altkva;
+ int spc_count;
+ vm_page_t spc_m[MAX_PAGEOUT_CLUSTER];
+} swcleanlist [NPENDINGIO] ;
+
+
+extern vm_map_t kernel_map;
+
+/* spc_flags values */
+#define SPC_ERROR 0x01
+
+#define SWB_EMPTY (-1)
+
+void swap_pager_init(void);
+vm_pager_t swap_pager_alloc(caddr_t, vm_size_t, vm_prot_t, vm_offset_t);
+void swap_pager_dealloc(vm_pager_t);
+boolean_t swap_pager_getpage(vm_pager_t, vm_page_t, boolean_t);
+boolean_t swap_pager_putpage(vm_pager_t, vm_page_t, boolean_t);
+boolean_t swap_pager_getmulti(vm_pager_t, vm_page_t *, int, int, boolean_t);
+boolean_t swap_pager_haspage(vm_pager_t, vm_offset_t);
+int swap_pager_io(sw_pager_t, vm_page_t *, int, int, int);
+void swap_pager_iodone(struct buf *);
+boolean_t swap_pager_clean();
+
+extern struct pagerops swappagerops;
+
+struct swpclean swap_pager_done; /* list of compileted page cleans */
+struct swpclean swap_pager_inuse; /* list of pending page cleans */
+struct swpclean swap_pager_free; /* list of free pager clean structs */
+struct pagerlst swap_pager_list; /* list of "named" anon regions */
+struct pagerlst swap_pager_un_list; /* list of "unnamed" anon pagers */
+
+#define SWAP_FREE_NEEDED 0x1 /* need a swap block */
+int swap_pager_needflags;
+struct rlist *swapfrag;
+
+struct pagerlst *swp_qs[]={
+ &swap_pager_list, &swap_pager_un_list, (struct pagerlst *) 0
+};
+
+int swap_pager_putmulti();
+
+struct pagerops swappagerops = {
+ swap_pager_init,
+ swap_pager_alloc,
+ swap_pager_dealloc,
+ swap_pager_getpage,
+ swap_pager_getmulti,
+ swap_pager_putpage,
+ swap_pager_putmulti,
+ swap_pager_haspage
+};
+
+extern int nswbuf;
+
+int npendingio = NPENDINGIO;
+int pendingiowait;
+int require_swap_init;
+void swap_pager_finish();
+int dmmin, dmmax;
+extern int vm_page_count;
+
+struct buf * getpbuf() ;
+void relpbuf(struct buf *bp) ;
+
+static inline void swapsizecheck() {
+ if( vm_swap_size < 128*btodb(PAGE_SIZE)) {
+ if( swap_pager_full)
+ printf("swap_pager: out of space\n");
+ swap_pager_full = 1;
+ } else if( vm_swap_size > 192*btodb(PAGE_SIZE))
+ swap_pager_full = 0;
+}
+
+void
+swap_pager_init()
+{
+ extern int dmmin, dmmax;
+
+ dfltpagerops = &swappagerops;
+
+ TAILQ_INIT(&swap_pager_list);
+ TAILQ_INIT(&swap_pager_un_list);
+
+ /*
+ * Initialize clean lists
+ */
+ TAILQ_INIT(&swap_pager_inuse);
+ TAILQ_INIT(&swap_pager_done);
+ TAILQ_INIT(&swap_pager_free);
+
+ require_swap_init = 1;
+
+ /*
+ * Calculate the swap allocation constants.
+ */
+
+ dmmin = CLBYTES/DEV_BSIZE;
+ dmmax = btodb(SWB_NPAGES*PAGE_SIZE)*2;
+
+}
+
+/*
+ * Allocate a pager structure and associated resources.
+ * Note that if we are called from the pageout daemon (handle == NULL)
+ * we should not wait for memory as it could resulting in deadlock.
+ */
+vm_pager_t
+swap_pager_alloc(handle, size, prot, offset)
+ caddr_t handle;
+ register vm_size_t size;
+ vm_prot_t prot;
+ vm_offset_t offset;
+{
+ register vm_pager_t pager;
+ register sw_pager_t swp;
+ int waitok;
+ int i,j;
+
+ if (require_swap_init) {
+ swp_clean_t spc;
+ struct buf *bp;
+ /*
+ * kva's are allocated here so that we dont need to keep
+ * doing kmem_alloc pageables at runtime
+ */
+ for (i = 0, spc = swcleanlist; i < npendingio ; i++, spc++) {
+ spc->spc_kva = kmem_alloc_pageable(pager_map, PAGE_SIZE);
+ if (!spc->spc_kva) {
+ break;
+ }
+ spc->spc_bp = malloc( sizeof( *bp), M_TEMP, M_NOWAIT);
+ if (!spc->spc_bp) {
+ kmem_free_wakeup(pager_map, spc->spc_kva, PAGE_SIZE);
+ break;
+ }
+ spc->spc_flags = 0;
+ TAILQ_INSERT_TAIL(&swap_pager_free, spc, spc_list);
+ }
+ require_swap_init = 0;
+ if( size == 0)
+ return(NULL);
+ }
+
+ /*
+ * If this is a "named" anonymous region, look it up and
+ * return the appropriate pager if it exists.
+ */
+ if (handle) {
+ pager = vm_pager_lookup(&swap_pager_list, handle);
+ if (pager != NULL) {
+ /*
+ * Use vm_object_lookup to gain a reference
+ * to the object and also to remove from the
+ * object cache.
+ */
+ if (vm_object_lookup(pager) == NULL)
+ panic("swap_pager_alloc: bad object");
+ return(pager);
+ }
+ }
+
+ if (swap_pager_full) {
+ return(NULL);
+ }
+
+ /*
+ * Pager doesn't exist, allocate swap management resources
+ * and initialize.
+ */
+ waitok = handle ? M_WAITOK : M_NOWAIT;
+ pager = (vm_pager_t)malloc(sizeof *pager, M_VMPAGER, waitok);
+ if (pager == NULL)
+ return(NULL);
+ swp = (sw_pager_t)malloc(sizeof *swp, M_VMPGDATA, waitok);
+ if (swp == NULL) {
+ free((caddr_t)pager, M_VMPAGER);
+ return(NULL);
+ }
+ size = round_page(size);
+ swp->sw_osize = size;
+ swp->sw_nblocks = (btodb(size) + btodb(SWB_NPAGES * PAGE_SIZE) - 1) / btodb(SWB_NPAGES*PAGE_SIZE);
+ swp->sw_blocks = (sw_blk_t)
+ malloc(swp->sw_nblocks*sizeof(*swp->sw_blocks),
+ M_VMPGDATA, waitok);
+ if (swp->sw_blocks == NULL) {
+ free((caddr_t)swp, M_VMPGDATA);
+ free((caddr_t)pager, M_VMPAGER);
+ return(NULL);
+ }
+
+ for (i = 0; i < swp->sw_nblocks; i++) {
+ swp->sw_blocks[i].swb_valid = 0;
+ swp->sw_blocks[i].swb_locked = 0;
+ for (j = 0; j < SWB_NPAGES; j++)
+ swp->sw_blocks[i].swb_block[j] = SWB_EMPTY;
+ }
+
+ swp->sw_poip = 0;
+ if (handle) {
+ vm_object_t object;
+
+ swp->sw_flags = SW_NAMED;
+ TAILQ_INSERT_TAIL(&swap_pager_list, pager, pg_list);
+ /*
+ * Consistant with other pagers: return with object
+ * referenced. Can't do this with handle == NULL
+ * since it might be the pageout daemon calling.
+ */
+ object = vm_object_allocate(size);
+ vm_object_enter(object, pager);
+ vm_object_setpager(object, pager, 0, FALSE);
+ } else {
+ swp->sw_flags = 0;
+ TAILQ_INSERT_TAIL(&swap_pager_un_list, pager, pg_list);
+ }
+ pager->pg_handle = handle;
+ pager->pg_ops = &swappagerops;
+ pager->pg_type = PG_SWAP;
+ pager->pg_data = (caddr_t)swp;
+
+ return(pager);
+}
+
+/*
+ * returns disk block associated with pager and offset
+ * additionally, as a side effect returns a flag indicating
+ * if the block has been written
+ */
+
+static int *
+swap_pager_diskaddr(swp, offset, valid)
+ sw_pager_t swp;
+ vm_offset_t offset;
+ int *valid;
+{
+ register sw_blk_t swb;
+ int ix;
+
+ if (valid)
+ *valid = 0;
+ ix = offset / (SWB_NPAGES*PAGE_SIZE);
+ if (swp->sw_blocks == NULL || ix >= swp->sw_nblocks) {
+ return(FALSE);
+ }
+ swb = &swp->sw_blocks[ix];
+ ix = (offset % (SWB_NPAGES*PAGE_SIZE)) / PAGE_SIZE;
+ if (valid)
+ *valid = swb->swb_valid & (1<<ix);
+ return &swb->swb_block[ix];
+}
+
+/*
+ * Utility routine to set the valid (written) bit for
+ * a block associated with a pager and offset
+ */
+static void
+swap_pager_setvalid(swp, offset, valid)
+ sw_pager_t swp;
+ vm_offset_t offset;
+ int valid;
+{
+ register sw_blk_t swb;
+ int ix;
+
+ ix = offset / (SWB_NPAGES*PAGE_SIZE);
+ if (swp->sw_blocks == NULL || ix >= swp->sw_nblocks)
+ return;
+
+ swb = &swp->sw_blocks[ix];
+ ix = (offset % (SWB_NPAGES*PAGE_SIZE)) / PAGE_SIZE;
+ if (valid)
+ swb->swb_valid |= (1 << ix);
+ else
+ swb->swb_valid &= ~(1 << ix);
+ return;
+}
+
+/*
+ * this routine allocates swap space with a fragmentation
+ * minimization policy.
+ */
+int
+swap_pager_getswapspace( unsigned amount, unsigned *rtval) {
+ unsigned tmpalloc;
+ unsigned nblocksfrag = btodb(SWB_NPAGES*PAGE_SIZE);
+ if( amount < nblocksfrag) {
+ if( rlist_alloc(&swapfrag, amount, rtval))
+ return 1;
+ if( !rlist_alloc(&swapmap, nblocksfrag, &tmpalloc))
+ return 0;
+ rlist_free( &swapfrag, tmpalloc+amount, tmpalloc + nblocksfrag - 1);
+ *rtval = tmpalloc;
+ return 1;
+ }
+ if( !rlist_alloc(&swapmap, amount, rtval))
+ return 0;
+ else
+ return 1;
+}
+
+/*
+ * this routine frees swap space with a fragmentation
+ * minimization policy.
+ */
+void
+swap_pager_freeswapspace( unsigned from, unsigned to) {
+ unsigned nblocksfrag = btodb(SWB_NPAGES*PAGE_SIZE);
+ unsigned tmpalloc;
+ if( ((to + 1) - from) >= nblocksfrag) {
+ while( (from + nblocksfrag) <= to + 1) {
+ rlist_free(&swapmap, from, from + nblocksfrag - 1);
+ from += nblocksfrag;
+ }
+ }
+ if( from >= to)
+ return;
+ rlist_free(&swapfrag, from, to);
+ while( rlist_alloc(&swapfrag, nblocksfrag, &tmpalloc)) {
+ rlist_free(&swapmap, tmpalloc, tmpalloc + nblocksfrag-1);
+ }
+}
+/*
+ * this routine frees swap blocks from a specified pager
+ */
+void
+_swap_pager_freespace(swp, start, size)
+ sw_pager_t swp;
+ vm_offset_t start;
+ vm_offset_t size;
+{
+ vm_offset_t i;
+ int s;
+
+ s = splbio();
+ for (i = start; i < round_page(start + size - 1); i += PAGE_SIZE) {
+ int valid;
+ int *addr = swap_pager_diskaddr(swp, i, &valid);
+ if (addr && *addr != SWB_EMPTY) {
+ swap_pager_freeswapspace(*addr, *addr+btodb(PAGE_SIZE) - 1);
+ if( valid) {
+ vm_swap_size += btodb(PAGE_SIZE);
+ swap_pager_setvalid(swp, i, 0);
+ }
+ *addr = SWB_EMPTY;
+ }
+ }
+ swapsizecheck();
+ splx(s);
+}
+
+void
+swap_pager_freespace(pager, start, size)
+ vm_pager_t pager;
+ vm_offset_t start;
+ vm_offset_t size;
+{
+ _swap_pager_freespace((sw_pager_t) pager->pg_data, start, size);
+}
+
+/*
+ * swap_pager_reclaim frees up over-allocated space from all pagers
+ * this eliminates internal fragmentation due to allocation of space
+ * for segments that are never swapped to. It has been written so that
+ * it does not block until the rlist_free operation occurs; it keeps
+ * the queues consistant.
+ */
+
+/*
+ * Maximum number of blocks (pages) to reclaim per pass
+ */
+#define MAXRECLAIM 256
+
+void
+swap_pager_reclaim()
+{
+ vm_pager_t p;
+ sw_pager_t swp;
+ int i, j, k;
+ int s;
+ int reclaimcount;
+ static int reclaims[MAXRECLAIM];
+ static int in_reclaim;
+
+/*
+ * allow only one process to be in the swap_pager_reclaim subroutine
+ */
+ s = splbio();
+ if (in_reclaim) {
+ tsleep((caddr_t) &in_reclaim, PSWP, "swrclm", 0);
+ splx(s);
+ return;
+ }
+ in_reclaim = 1;
+ reclaimcount = 0;
+
+ /* for each pager queue */
+ for (k = 0; swp_qs[k]; k++) {
+
+ p = swp_qs[k]->tqh_first;
+ while (p && (reclaimcount < MAXRECLAIM)) {
+
+ /*
+ * see if any blocks associated with a pager has been
+ * allocated but not used (written)
+ */
+ swp = (sw_pager_t) p->pg_data;
+ for (i = 0; i < swp->sw_nblocks; i++) {
+ sw_blk_t swb = &swp->sw_blocks[i];
+ if( swb->swb_locked)
+ continue;
+ for (j = 0; j < SWB_NPAGES; j++) {
+ if (swb->swb_block[j] != SWB_EMPTY &&
+ (swb->swb_valid & (1 << j)) == 0) {
+ reclaims[reclaimcount++] = swb->swb_block[j];
+ swb->swb_block[j] = SWB_EMPTY;
+ if (reclaimcount >= MAXRECLAIM)
+ goto rfinished;
+ }
+ }
+ }
+ p = p->pg_list.tqe_next;
+ }
+ }
+
+rfinished:
+
+/*
+ * free the blocks that have been added to the reclaim list
+ */
+ for (i = 0; i < reclaimcount; i++) {
+ swap_pager_freeswapspace(reclaims[i], reclaims[i]+btodb(PAGE_SIZE) - 1);
+ swapsizecheck();
+ wakeup((caddr_t) &in_reclaim);
+ }
+
+ splx(s);
+ in_reclaim = 0;
+ wakeup((caddr_t) &in_reclaim);
+}
+
+
+/*
+ * swap_pager_copy copies blocks from one pager to another and
+ * destroys the source pager
+ */
+
+void
+swap_pager_copy(srcpager, srcoffset, dstpager, dstoffset, offset)
+ vm_pager_t srcpager;
+ vm_offset_t srcoffset;
+ vm_pager_t dstpager;
+ vm_offset_t dstoffset;
+ vm_offset_t offset;
+{
+ sw_pager_t srcswp, dstswp;
+ vm_offset_t i;
+ int s;
+
+ srcswp = (sw_pager_t) srcpager->pg_data;
+ dstswp = (sw_pager_t) dstpager->pg_data;
+
+/*
+ * remove the source pager from the swap_pager internal queue
+ */
+ s = splbio();
+ if (srcswp->sw_flags & SW_NAMED) {
+ TAILQ_REMOVE(&swap_pager_list, srcpager, pg_list);
+ srcswp->sw_flags &= ~SW_NAMED;
+ } else {
+ TAILQ_REMOVE(&swap_pager_un_list, srcpager, pg_list);
+ }
+
+ while (srcswp->sw_poip) {
+ tsleep((caddr_t)srcswp, PVM, "spgout", 0);
+ }
+ splx(s);
+
+/*
+ * clean all of the pages that are currently active and finished
+ */
+ (void) swap_pager_clean();
+
+ s = splbio();
+/*
+ * clear source block before destination object
+ * (release allocated space)
+ */
+ for (i = 0; i < offset + srcoffset; i += PAGE_SIZE) {
+ int valid;
+ int *addr = swap_pager_diskaddr(srcswp, i, &valid);
+ if (addr && *addr != SWB_EMPTY) {
+ swap_pager_freeswapspace(*addr, *addr+btodb(PAGE_SIZE) - 1);
+ if( valid)
+ vm_swap_size += btodb(PAGE_SIZE);
+ swapsizecheck();
+ *addr = SWB_EMPTY;
+ }
+ }
+/*
+ * transfer source to destination
+ */
+ for (i = 0; i < dstswp->sw_osize; i += PAGE_SIZE) {
+ int srcvalid, dstvalid;
+ int *srcaddrp = swap_pager_diskaddr(srcswp, i + offset + srcoffset,
+ &srcvalid);
+ int *dstaddrp;
+ /*
+ * see if the source has space allocated
+ */
+ if (srcaddrp && *srcaddrp != SWB_EMPTY) {
+ /*
+ * if the source is valid and the dest has no space, then
+ * copy the allocation from the srouce to the dest.
+ */
+ if (srcvalid) {
+ dstaddrp = swap_pager_diskaddr(dstswp, i + dstoffset, &dstvalid);
+ /*
+ * if the dest already has a valid block, deallocate the
+ * source block without copying.
+ */
+ if (!dstvalid && dstaddrp && *dstaddrp != SWB_EMPTY) {
+ swap_pager_freeswapspace(*dstaddrp, *dstaddrp+btodb(PAGE_SIZE) - 1);
+ *dstaddrp = SWB_EMPTY;
+ }
+ if (dstaddrp && *dstaddrp == SWB_EMPTY) {
+ *dstaddrp = *srcaddrp;
+ *srcaddrp = SWB_EMPTY;
+ swap_pager_setvalid(dstswp, i + dstoffset, 1);
+ vm_swap_size -= btodb(PAGE_SIZE);
+ }
+ }
+ /*
+ * if the source is not empty at this point, then deallocate the space.
+ */
+ if (*srcaddrp != SWB_EMPTY) {
+ swap_pager_freeswapspace(*srcaddrp, *srcaddrp+btodb(PAGE_SIZE) - 1);
+ if( srcvalid)
+ vm_swap_size += btodb(PAGE_SIZE);
+ *srcaddrp = SWB_EMPTY;
+ }
+ }
+ }
+
+/*
+ * deallocate the rest of the source object
+ */
+ for (i = dstswp->sw_osize + offset + srcoffset; i < srcswp->sw_osize; i += PAGE_SIZE) {
+ int valid;
+ int *srcaddrp = swap_pager_diskaddr(srcswp, i, &valid);
+ if (srcaddrp && *srcaddrp != SWB_EMPTY) {
+ swap_pager_freeswapspace(*srcaddrp, *srcaddrp+btodb(PAGE_SIZE) - 1);
+ if( valid)
+ vm_swap_size += btodb(PAGE_SIZE);
+ *srcaddrp = SWB_EMPTY;
+ }
+ }
+
+ swapsizecheck();
+ splx(s);
+
+ free((caddr_t)srcswp->sw_blocks, M_VMPGDATA);
+ srcswp->sw_blocks = 0;
+ free((caddr_t)srcswp, M_VMPGDATA);
+ srcpager->pg_data = 0;
+ free((caddr_t)srcpager, M_VMPAGER);
+
+ return;
+}
+
+
+void
+swap_pager_dealloc(pager)
+ vm_pager_t pager;
+{
+ register int i,j;
+ register sw_blk_t bp;
+ register sw_pager_t swp;
+ int s;
+
+ /*
+ * Remove from list right away so lookups will fail if we
+ * block for pageout completion.
+ */
+ s = splbio();
+ swp = (sw_pager_t) pager->pg_data;
+ if (swp->sw_flags & SW_NAMED) {
+ TAILQ_REMOVE(&swap_pager_list, pager, pg_list);
+ swp->sw_flags &= ~SW_NAMED;
+ } else {
+ TAILQ_REMOVE(&swap_pager_un_list, pager, pg_list);
+ }
+ /*
+ * Wait for all pageouts to finish and remove
+ * all entries from cleaning list.
+ */
+
+ while (swp->sw_poip) {
+ tsleep((caddr_t)swp, PVM, "swpout", 0);
+ }
+ splx(s);
+
+
+ (void) swap_pager_clean();
+
+ /*
+ * Free left over swap blocks
+ */
+ s = splbio();
+ for (i = 0, bp = swp->sw_blocks; i < swp->sw_nblocks; i++, bp++) {
+ for (j = 0; j < SWB_NPAGES; j++)
+ if (bp->swb_block[j] != SWB_EMPTY) {
+ swap_pager_freeswapspace((unsigned)bp->swb_block[j],
+ (unsigned)bp->swb_block[j] + btodb(PAGE_SIZE) - 1);
+ if( bp->swb_valid & (1<<j))
+ vm_swap_size += btodb(PAGE_SIZE);
+ bp->swb_block[j] = SWB_EMPTY;
+ }
+ }
+ splx(s);
+ swapsizecheck();
+
+ /*
+ * Free swap management resources
+ */
+ free((caddr_t)swp->sw_blocks, M_VMPGDATA);
+ swp->sw_blocks = 0;
+ free((caddr_t)swp, M_VMPGDATA);
+ pager->pg_data = 0;
+ free((caddr_t)pager, M_VMPAGER);
+}
+
+/*
+ * swap_pager_getmulti can get multiple pages.
+ */
+int
+swap_pager_getmulti(pager, m, count, reqpage, sync)
+ vm_pager_t pager;
+ vm_page_t *m;
+ int count;
+ int reqpage;
+ boolean_t sync;
+{
+ if( reqpage >= count)
+ panic("swap_pager_getmulti: reqpage >= count\n");
+ return swap_pager_input((sw_pager_t) pager->pg_data, m, count, reqpage);
+}
+
+/*
+ * swap_pager_getpage gets individual pages
+ */
+int
+swap_pager_getpage(pager, m, sync)
+ vm_pager_t pager;
+ vm_page_t m;
+ boolean_t sync;
+{
+ vm_page_t marray[1];
+
+ marray[0] = m;
+ return swap_pager_input((sw_pager_t)pager->pg_data, marray, 1, 0);
+}
+
+int
+swap_pager_putmulti(pager, m, c, sync, rtvals)
+ vm_pager_t pager;
+ vm_page_t *m;
+ int c;
+ boolean_t sync;
+ int *rtvals;
+{
+ int flags;
+
+ if (pager == NULL) {
+ (void) swap_pager_clean();
+ return VM_PAGER_OK;
+ }
+
+ flags = B_WRITE;
+ if (!sync)
+ flags |= B_ASYNC;
+
+ return swap_pager_output((sw_pager_t)pager->pg_data, m, c, flags, rtvals);
+}
+
+/*
+ * swap_pager_putpage writes individual pages
+ */
+int
+swap_pager_putpage(pager, m, sync)
+ vm_pager_t pager;
+ vm_page_t m;
+ boolean_t sync;
+{
+ int flags;
+ vm_page_t marray[1];
+ int rtvals[1];
+
+
+ if (pager == NULL) {
+ (void) swap_pager_clean();
+ return VM_PAGER_OK;
+ }
+
+ marray[0] = m;
+ flags = B_WRITE;
+ if (!sync)
+ flags |= B_ASYNC;
+
+ swap_pager_output((sw_pager_t)pager->pg_data, marray, 1, flags, rtvals);
+
+ return rtvals[0];
+}
+
+static inline int
+const swap_pager_block_index(swp, offset)
+ sw_pager_t swp;
+ vm_offset_t offset;
+{
+ return (offset / (SWB_NPAGES*PAGE_SIZE));
+}
+
+static inline int
+const swap_pager_block_offset(swp, offset)
+ sw_pager_t swp;
+ vm_offset_t offset;
+{
+ return ((offset % (PAGE_SIZE*SWB_NPAGES)) / PAGE_SIZE);
+}
+
+/*
+ * _swap_pager_haspage returns TRUE if the pager has data that has
+ * been written out.
+ */
+static boolean_t
+_swap_pager_haspage(swp, offset)
+ sw_pager_t swp;
+ vm_offset_t offset;
+{
+ register sw_blk_t swb;
+ int ix;
+
+ ix = offset / (SWB_NPAGES*PAGE_SIZE);
+ if (swp->sw_blocks == NULL || ix >= swp->sw_nblocks) {
+ return(FALSE);
+ }
+ swb = &swp->sw_blocks[ix];
+ ix = (offset % (SWB_NPAGES*PAGE_SIZE)) / PAGE_SIZE;
+ if (swb->swb_block[ix] != SWB_EMPTY) {
+ if (swb->swb_valid & (1 << ix))
+ return TRUE;
+ }
+
+ return(FALSE);
+}
+
+/*
+ * swap_pager_haspage is the externally accessible version of
+ * _swap_pager_haspage above. this routine takes a vm_pager_t
+ * for an argument instead of sw_pager_t.
+ */
+boolean_t
+swap_pager_haspage(pager, offset)
+ vm_pager_t pager;
+ vm_offset_t offset;
+{
+ return _swap_pager_haspage((sw_pager_t) pager->pg_data, offset);
+}
+
+/*
+ * swap_pager_freepage is a convienience routine that clears the busy
+ * bit and deallocates a page.
+ */
+static void
+swap_pager_freepage(m)
+ vm_page_t m;
+{
+ PAGE_WAKEUP(m);
+ vm_page_free(m);
+}
+
+/*
+ * swap_pager_ridpages is a convienience routine that deallocates all
+ * but the required page. this is usually used in error returns that
+ * need to invalidate the "extra" readahead pages.
+ */
+static void
+swap_pager_ridpages(m, count, reqpage)
+ vm_page_t *m;
+ int count;
+ int reqpage;
+{
+ int i;
+ for (i = 0; i < count; i++)
+ if (i != reqpage)
+ swap_pager_freepage(m[i]);
+}
+
+int swapwritecount=0;
+
+/*
+ * swap_pager_iodone1 is the completion routine for both reads and async writes
+ */
+void
+swap_pager_iodone1(bp)
+ struct buf *bp;
+{
+ bp->b_flags |= B_DONE;
+ bp->b_flags &= ~B_ASYNC;
+ wakeup((caddr_t)bp);
+/*
+ if ((bp->b_flags & B_READ) == 0)
+ vwakeup(bp);
+*/
+}
+
+
+int
+swap_pager_input(swp, m, count, reqpage)
+ register sw_pager_t swp;
+ vm_page_t *m;
+ int count, reqpage;
+{
+ register struct buf *bp;
+ sw_blk_t swb[count];
+ register int s;
+ int i;
+ boolean_t rv;
+ vm_offset_t kva, off[count];
+ swp_clean_t spc;
+ vm_offset_t paging_offset;
+ vm_object_t object;
+ int reqaddr[count];
+
+ int first, last;
+ int failed;
+ int reqdskregion;
+
+ object = m[reqpage]->object;
+ paging_offset = object->paging_offset;
+ /*
+ * First determine if the page exists in the pager if this is
+ * a sync read. This quickly handles cases where we are
+ * following shadow chains looking for the top level object
+ * with the page.
+ */
+ if (swp->sw_blocks == NULL) {
+ swap_pager_ridpages(m, count, reqpage);
+ return(VM_PAGER_FAIL);
+ }
+
+ for(i = 0; i < count; i++) {
+ vm_offset_t foff = m[i]->offset + paging_offset;
+ int ix = swap_pager_block_index(swp, foff);
+ if (ix >= swp->sw_nblocks) {
+ int j;
+ if( i <= reqpage) {
+ swap_pager_ridpages(m, count, reqpage);
+ return(VM_PAGER_FAIL);
+ }
+ for(j = i; j < count; j++) {
+ swap_pager_freepage(m[j]);
+ }
+ count = i;
+ break;
+ }
+
+ swb[i] = &swp->sw_blocks[ix];
+ off[i] = swap_pager_block_offset(swp, foff);
+ reqaddr[i] = swb[i]->swb_block[off[i]];
+ }
+
+ /* make sure that our required input request is existant */
+
+ if (reqaddr[reqpage] == SWB_EMPTY ||
+ (swb[reqpage]->swb_valid & (1 << off[reqpage])) == 0) {
+ swap_pager_ridpages(m, count, reqpage);
+ return(VM_PAGER_FAIL);
+ }
+
+
+ reqdskregion = reqaddr[reqpage] / dmmax;
+
+ /*
+ * search backwards for the first contiguous page to transfer
+ */
+ failed = 0;
+ first = 0;
+ for (i = reqpage - 1; i >= 0; --i) {
+ if ( failed || (reqaddr[i] == SWB_EMPTY) ||
+ (swb[i]->swb_valid & (1 << off[i])) == 0 ||
+ (reqaddr[i] != (reqaddr[reqpage] + (i - reqpage) * btodb(PAGE_SIZE))) ||
+ ((reqaddr[i] / dmmax) != reqdskregion)) {
+ failed = 1;
+ swap_pager_freepage(m[i]);
+ if (first == 0)
+ first = i + 1;
+ }
+ }
+ /*
+ * search forwards for the last contiguous page to transfer
+ */
+ failed = 0;
+ last = count;
+ for (i = reqpage + 1; i < count; i++) {
+ if ( failed || (reqaddr[i] == SWB_EMPTY) ||
+ (swb[i]->swb_valid & (1 << off[i])) == 0 ||
+ (reqaddr[i] != (reqaddr[reqpage] + (i - reqpage) * btodb(PAGE_SIZE))) ||
+ ((reqaddr[i] / dmmax) != reqdskregion)) {
+ failed = 1;
+ swap_pager_freepage(m[i]);
+ if (last == count)
+ last = i;
+ }
+ }
+
+ count = last;
+ if (first != 0) {
+ for (i = first; i < count; i++) {
+ m[i-first] = m[i];
+ reqaddr[i-first] = reqaddr[i];
+ off[i-first] = off[i];
+ }
+ count -= first;
+ reqpage -= first;
+ }
+
+ ++swb[reqpage]->swb_locked;
+
+ /*
+ * at this point:
+ * "m" is a pointer to the array of vm_page_t for paging I/O
+ * "count" is the number of vm_page_t entries represented by "m"
+ * "object" is the vm_object_t for I/O
+ * "reqpage" is the index into "m" for the page actually faulted
+ */
+
+ spc = NULL; /* we might not use an spc data structure */
+ kva = 0;
+
+ /*
+ * we allocate a new kva for transfers > 1 page
+ * but for transfers == 1 page, the swap_pager_free list contains
+ * entries that have pre-allocated kva's (for efficiency).
+ */
+ if (count > 1) {
+ kva = kmem_alloc_pageable(pager_map, count*PAGE_SIZE);
+ }
+
+
+ if (!kva) {
+ /*
+ * if a kva has not been allocated, we can only do a one page transfer,
+ * so we free the other pages that might have been allocated by
+ * vm_fault.
+ */
+ swap_pager_ridpages(m, count, reqpage);
+ m[0] = m[reqpage];
+ reqaddr[0] = reqaddr[reqpage];
+
+ count = 1;
+ reqpage = 0;
+ /*
+ * get a swap pager clean data structure, block until we get it
+ */
+ if (swap_pager_free.tqh_first == NULL) {
+ s = splbio();
+ if( curproc == pageproc)
+ (void) swap_pager_clean();
+ else
+ wakeup((caddr_t) &vm_pages_needed);
+ while (swap_pager_free.tqh_first == NULL) {
+ swap_pager_needflags |= SWAP_FREE_NEEDED;
+ tsleep((caddr_t)&swap_pager_free,
+ PVM, "swpfre", 0);
+ if( curproc == pageproc)
+ (void) swap_pager_clean();
+ else
+ wakeup((caddr_t) &vm_pages_needed);
+ }
+ splx(s);
+ }
+ spc = swap_pager_free.tqh_first;
+ TAILQ_REMOVE(&swap_pager_free, spc, spc_list);
+ kva = spc->spc_kva;
+ }
+
+
+ /*
+ * map our page(s) into kva for input
+ */
+ for (i = 0; i < count; i++) {
+ pmap_kenter( kva + PAGE_SIZE * i, VM_PAGE_TO_PHYS(m[i]));
+ }
+ pmap_update();
+
+
+ /*
+ * Get a swap buffer header and perform the IO
+ */
+ if( spc) {
+ bp = spc->spc_bp;
+ bzero(bp, sizeof *bp);
+ bp->b_spc = spc;
+ } else {
+ bp = getpbuf();
+ }
+
+ s = splbio();
+ bp->b_flags = B_BUSY | B_READ | B_CALL;
+ bp->b_iodone = swap_pager_iodone1;
+ bp->b_proc = &proc0; /* XXX (but without B_PHYS set this is ok) */
+ bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred;
+ crhold(bp->b_rcred);
+ crhold(bp->b_wcred);
+ bp->b_un.b_addr = (caddr_t) kva;
+ bp->b_blkno = reqaddr[0];
+ bp->b_bcount = PAGE_SIZE*count;
+ bp->b_bufsize = PAGE_SIZE*count;
+
+/*
+ VHOLD(swapdev_vp);
+ bp->b_vp = swapdev_vp;
+ if (swapdev_vp->v_type == VBLK)
+ bp->b_dev = swapdev_vp->v_rdev;
+*/
+ bgetvp( swapdev_vp, bp);
+
+ swp->sw_piip++;
+
+ /*
+ * perform the I/O
+ */
+ VOP_STRATEGY(bp);
+
+ /*
+ * wait for the sync I/O to complete
+ */
+ while ((bp->b_flags & B_DONE) == 0) {
+ tsleep((caddr_t)bp, PVM, "swread", 0);
+ }
+ rv = (bp->b_flags & B_ERROR) ? VM_PAGER_FAIL : VM_PAGER_OK;
+ bp->b_flags &= ~(B_BUSY|B_WANTED|B_PHYS|B_DIRTY|B_CALL|B_DONE);
+
+ --swp->sw_piip;
+ if (swp->sw_piip == 0)
+ wakeup((caddr_t) swp);
+
+ /*
+ * relpbuf does this, but we maintain our own buffer
+ * list also...
+ */
+ if (bp->b_vp)
+ brelvp(bp);
+
+ splx(s);
+ --swb[reqpage]->swb_locked;
+
+ /*
+ * remove the mapping for kernel virtual
+ */
+ pmap_remove(vm_map_pmap(pager_map), kva, kva + count * PAGE_SIZE);
+
+ if (spc) {
+ /*
+ * if we have used an spc, we need to free it.
+ */
+ if( bp->b_rcred != NOCRED)
+ crfree(bp->b_rcred);
+ if( bp->b_wcred != NOCRED)
+ crfree(bp->b_wcred);
+ TAILQ_INSERT_TAIL(&swap_pager_free, spc, spc_list);
+ if (swap_pager_needflags & SWAP_FREE_NEEDED) {
+ swap_pager_needflags &= ~SWAP_FREE_NEEDED;
+ wakeup((caddr_t)&swap_pager_free);
+ }
+ } else {
+ /*
+ * free the kernel virtual addresses
+ */
+ kmem_free_wakeup(pager_map, kva, count * PAGE_SIZE);
+ /*
+ * release the physical I/O buffer
+ */
+ relpbuf(bp);
+ /*
+ * finish up input if everything is ok
+ */
+ if( rv == VM_PAGER_OK) {
+ for (i = 0; i < count; i++) {
+ pmap_clear_modify(VM_PAGE_TO_PHYS(m[i]));
+ m[i]->flags |= PG_CLEAN;
+ m[i]->flags &= ~PG_LAUNDRY;
+ if (i != reqpage) {
+ /*
+ * whether or not to leave the page activated
+ * is up in the air, but we should put the page
+ * on a page queue somewhere. (it already is in
+ * the object).
+ * After some emperical results, it is best
+ * to deactivate the readahead pages.
+ */
+ vm_page_deactivate(m[i]);
+
+ /*
+ * just in case someone was asking for this
+ * page we now tell them that it is ok to use
+ */
+ m[i]->flags &= ~PG_FAKE;
+ PAGE_WAKEUP(m[i]);
+ }
+ }
+ if( swap_pager_full) {
+ _swap_pager_freespace( swp, m[0]->offset+paging_offset, count*PAGE_SIZE);
+ }
+ } else {
+ swap_pager_ridpages(m, count, reqpage);
+ }
+ }
+ return(rv);
+}
+
+int
+swap_pager_output(swp, m, count, flags, rtvals)
+ register sw_pager_t swp;
+ vm_page_t *m;
+ int count;
+ int flags;
+ int *rtvals;
+{
+ register struct buf *bp;
+ sw_blk_t swb[count];
+ register int s;
+ int i, j, ix;
+ boolean_t rv;
+ vm_offset_t kva, off, foff;
+ swp_clean_t spc;
+ vm_offset_t paging_offset;
+ vm_object_t object;
+ int reqaddr[count];
+ int failed;
+
+/*
+ if( count > 1)
+ printf("off: 0x%x, count: %d\n", m[0]->offset, count);
+*/
+ spc = NULL;
+
+ object = m[0]->object;
+ paging_offset = object->paging_offset;
+
+ failed = 0;
+ for(j=0;j<count;j++) {
+ foff = m[j]->offset + paging_offset;
+ ix = swap_pager_block_index(swp, foff);
+ swb[j] = 0;
+ if( swp->sw_blocks == NULL || ix >= swp->sw_nblocks) {
+ rtvals[j] = VM_PAGER_FAIL;
+ failed = 1;
+ continue;
+ } else {
+ rtvals[j] = VM_PAGER_OK;
+ }
+ swb[j] = &swp->sw_blocks[ix];
+ ++swb[j]->swb_locked;
+ if( failed) {
+ rtvals[j] = VM_PAGER_FAIL;
+ continue;
+ }
+ off = swap_pager_block_offset(swp, foff);
+ reqaddr[j] = swb[j]->swb_block[off];
+ if( reqaddr[j] == SWB_EMPTY) {
+ int blk;
+ int tries;
+ int ntoget;
+ tries = 0;
+ s = splbio();
+
+ /*
+ * if any other pages have been allocated in this block, we
+ * only try to get one page.
+ */
+ for (i = 0; i < SWB_NPAGES; i++) {
+ if (swb[j]->swb_block[i] != SWB_EMPTY)
+ break;
+ }
+
+
+ ntoget = (i == SWB_NPAGES) ? SWB_NPAGES : 1;
+ /*
+ * this code is alittle conservative, but works
+ * (the intent of this code is to allocate small chunks
+ * for small objects)
+ */
+ if( (m[j]->offset == 0) && (ntoget*PAGE_SIZE > object->size)) {
+ ntoget = (object->size + (PAGE_SIZE-1))/PAGE_SIZE;
+ }
+
+retrygetspace:
+ if (!swap_pager_full && ntoget > 1 &&
+ swap_pager_getswapspace(ntoget * btodb(PAGE_SIZE), &blk)) {
+
+ for (i = 0; i < ntoget; i++) {
+ swb[j]->swb_block[i] = blk + btodb(PAGE_SIZE) * i;
+ swb[j]->swb_valid = 0;
+ }
+
+ reqaddr[j] = swb[j]->swb_block[off];
+ } else if (!swap_pager_getswapspace(btodb(PAGE_SIZE),
+ &swb[j]->swb_block[off])) {
+ /*
+ * if the allocation has failed, we try to reclaim space and
+ * retry.
+ */
+ if (++tries == 1) {
+ swap_pager_reclaim();
+ goto retrygetspace;
+ }
+ rtvals[j] = VM_PAGER_AGAIN;
+ failed = 1;
+ } else {
+ reqaddr[j] = swb[j]->swb_block[off];
+ swb[j]->swb_valid &= ~(1<<off);
+ }
+ splx(s);
+ }
+ }
+
+ /*
+ * search forwards for the last contiguous page to transfer
+ */
+ failed = 0;
+ for (i = 0; i < count; i++) {
+ if( failed || (reqaddr[i] != reqaddr[0] + i*btodb(PAGE_SIZE)) ||
+ (reqaddr[i] / dmmax) != (reqaddr[0] / dmmax) ||
+ (rtvals[i] != VM_PAGER_OK)) {
+ failed = 1;
+ if( rtvals[i] == VM_PAGER_OK)
+ rtvals[i] = VM_PAGER_AGAIN;
+ }
+ }
+
+ for(i = 0; i < count; i++) {
+ if( rtvals[i] != VM_PAGER_OK) {
+ if( swb[i])
+ --swb[i]->swb_locked;
+ }
+ }
+
+ for(i = 0; i < count; i++)
+ if( rtvals[i] != VM_PAGER_OK)
+ break;
+
+ if( i == 0) {
+ return VM_PAGER_AGAIN;
+ }
+
+ count = i;
+ for(i=0;i<count;i++) {
+ if( reqaddr[i] == SWB_EMPTY)
+ printf("I/O to empty block????\n");
+ }
+
+ /*
+ */
+
+ /*
+ * For synchronous writes, we clean up
+ * all completed async pageouts.
+ */
+ if ((flags & B_ASYNC) == 0) {
+ swap_pager_clean();
+ }
+
+ kva = 0;
+
+ /*
+ * we allocate a new kva for transfers > 1 page
+ * but for transfers == 1 page, the swap_pager_free list contains
+ * entries that have pre-allocated kva's (for efficiency).
+ */
+ if ( count > 1) {
+ kva = kmem_alloc_pageable(pager_map, count*PAGE_SIZE);
+ if( !kva) {
+ for (i = 0; i < count; i++) {
+ if( swb[i])
+ --swb[i]->swb_locked;
+ rtvals[i] = VM_PAGER_AGAIN;
+ }
+ return VM_PAGER_AGAIN;
+ }
+ }
+
+ /*
+ * get a swap pager clean data structure, block until we get it
+ */
+ if (swap_pager_free.tqh_first == NULL) {
+/*
+ if (flags & B_ASYNC) {
+ for(i=0;i<count;i++) {
+ rtvals[i] = VM_PAGER_AGAIN;
+ if( swb[i])
+ --swb[i]->swb_locked;
+ }
+ return VM_PAGER_AGAIN;
+ }
+*/
+
+ s = splbio();
+ if( curproc == pageproc)
+ (void) swap_pager_clean();
+ else
+ wakeup((caddr_t) &vm_pages_needed);
+ while (swap_pager_free.tqh_first == NULL) {
+ swap_pager_needflags |= SWAP_FREE_NEEDED;
+ tsleep((caddr_t)&swap_pager_free,
+ PVM, "swpfre", 0);
+ if( curproc == pageproc)
+ (void) swap_pager_clean();
+ else
+ wakeup((caddr_t) &vm_pages_needed);
+ }
+ splx(s);
+ }
+
+ spc = swap_pager_free.tqh_first;
+ TAILQ_REMOVE(&swap_pager_free, spc, spc_list);
+ if( !kva) {
+ kva = spc->spc_kva;
+ spc->spc_altkva = 0;
+ } else {
+ spc->spc_altkva = kva;
+ }
+
+ /*
+ * map our page(s) into kva for I/O
+ */
+ for (i = 0; i < count; i++) {
+ pmap_kenter( kva + PAGE_SIZE * i, VM_PAGE_TO_PHYS(m[i]));
+ }
+ pmap_update();
+
+ /*
+ * get the base I/O offset into the swap file
+ */
+ for(i=0;i<count;i++) {
+ foff = m[i]->offset + paging_offset;
+ off = swap_pager_block_offset(swp, foff);
+ /*
+ * if we are setting the valid bit anew,
+ * then diminish the swap free space
+ */
+ if( (swb[i]->swb_valid & (1 << off)) == 0)
+ vm_swap_size -= btodb(PAGE_SIZE);
+
+ /*
+ * set the valid bit
+ */
+ swb[i]->swb_valid |= (1 << off);
+ /*
+ * and unlock the data structure
+ */
+ --swb[i]->swb_locked;
+ }
+
+ s = splbio();
+ /*
+ * Get a swap buffer header and perform the IO
+ */
+ bp = spc->spc_bp;
+ bzero(bp, sizeof *bp);
+ bp->b_spc = spc;
+
+ bp->b_flags = B_BUSY;
+ bp->b_proc = &proc0; /* XXX (but without B_PHYS set this is ok) */
+ bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred;
+ crhold(bp->b_rcred);
+ crhold(bp->b_wcred);
+ bp->b_un.b_addr = (caddr_t) kva;
+ bp->b_blkno = reqaddr[0];
+ bgetvp( swapdev_vp, bp);
+/*
+ VHOLD(swapdev_vp);
+ bp->b_vp = swapdev_vp;
+ if (swapdev_vp->v_type == VBLK)
+ bp->b_dev = swapdev_vp->v_rdev;
+*/
+ bp->b_bcount = PAGE_SIZE*count;
+ bp->b_bufsize = PAGE_SIZE*count;
+ swapdev_vp->v_numoutput++;
+
+ /*
+ * If this is an async write we set up additional buffer fields
+ * and place a "cleaning" entry on the inuse queue.
+ */
+ if ( flags & B_ASYNC ) {
+ spc->spc_flags = 0;
+ spc->spc_swp = swp;
+ for(i=0;i<count;i++)
+ spc->spc_m[i] = m[i];
+ spc->spc_count = count;
+ /*
+ * the completion routine for async writes
+ */
+ bp->b_flags |= B_CALL;
+ bp->b_iodone = swap_pager_iodone;
+ bp->b_dirtyoff = 0;
+ bp->b_dirtyend = bp->b_bcount;
+ swp->sw_poip++;
+ TAILQ_INSERT_TAIL(&swap_pager_inuse, spc, spc_list);
+ } else {
+ swp->sw_poip++;
+ bp->b_flags |= B_CALL;
+ bp->b_iodone = swap_pager_iodone1;
+ }
+ /*
+ * perform the I/O
+ */
+ VOP_STRATEGY(bp);
+ if ((flags & (B_READ|B_ASYNC)) == B_ASYNC ) {
+ if ((bp->b_flags & B_DONE) == B_DONE) {
+ swap_pager_clean();
+ }
+ splx(s);
+ for(i=0;i<count;i++) {
+ rtvals[i] = VM_PAGER_PEND;
+ }
+ return VM_PAGER_PEND;
+ }
+
+ /*
+ * wait for the sync I/O to complete
+ */
+ while ((bp->b_flags & B_DONE) == 0) {
+ tsleep((caddr_t)bp, PVM, "swwrt", 0);
+ }
+ rv = (bp->b_flags & B_ERROR) ? VM_PAGER_FAIL : VM_PAGER_OK;
+ bp->b_flags &= ~(B_BUSY|B_WANTED|B_PHYS|B_DIRTY|B_CALL|B_DONE);
+
+ --swp->sw_poip;
+ if (swp->sw_poip == 0)
+ wakeup((caddr_t) swp);
+
+ if (bp->b_vp)
+ brelvp(bp);
+
+ splx(s);
+
+ /*
+ * remove the mapping for kernel virtual
+ */
+ pmap_remove(vm_map_pmap(pager_map), kva, kva + count * PAGE_SIZE);
+
+ /*
+ * if we have written the page, then indicate that the page
+ * is clean.
+ */
+ if (rv == VM_PAGER_OK) {
+ for(i=0;i<count;i++) {
+ if( rtvals[i] == VM_PAGER_OK) {
+ m[i]->flags |= PG_CLEAN;
+ m[i]->flags &= ~PG_LAUNDRY;
+ pmap_clear_modify(VM_PAGE_TO_PHYS(m[i]));
+ /*
+ * optimization, if a page has been read during the
+ * pageout process, we activate it.
+ */
+ if ( (m[i]->flags & PG_ACTIVE) == 0 &&
+ pmap_is_referenced(VM_PAGE_TO_PHYS(m[i])))
+ vm_page_activate(m[i]);
+ }
+ }
+ } else {
+ for(i=0;i<count;i++) {
+ rtvals[i] = rv;
+ m[i]->flags |= PG_LAUNDRY;
+ }
+ }
+
+ if( spc->spc_altkva)
+ kmem_free_wakeup(pager_map, kva, count * PAGE_SIZE);
+
+ if( bp->b_rcred != NOCRED)
+ crfree(bp->b_rcred);
+ if( bp->b_wcred != NOCRED)
+ crfree(bp->b_wcred);
+ TAILQ_INSERT_TAIL(&swap_pager_free, spc, spc_list);
+ if (swap_pager_needflags & SWAP_FREE_NEEDED) {
+ swap_pager_needflags &= ~SWAP_FREE_NEEDED;
+ wakeup((caddr_t)&swap_pager_free);
+ }
+
+ return(rv);
+}
+
+boolean_t
+swap_pager_clean()
+{
+ register swp_clean_t spc, tspc;
+ register int s;
+
+ tspc = NULL;
+ if (swap_pager_done.tqh_first == NULL)
+ return FALSE;
+ for (;;) {
+ s = splbio();
+ /*
+ * Look up and removal from done list must be done
+ * at splbio() to avoid conflicts with swap_pager_iodone.
+ */
+ while (spc = swap_pager_done.tqh_first) {
+ if( spc->spc_altkva) {
+ pmap_remove(vm_map_pmap(pager_map), spc->spc_altkva, spc->spc_altkva + spc->spc_count * PAGE_SIZE);
+ kmem_free_wakeup(pager_map, spc->spc_altkva, spc->spc_count * PAGE_SIZE);
+ spc->spc_altkva = 0;
+ } else {
+ pmap_remove(vm_map_pmap(pager_map), spc->spc_kva, spc->spc_kva + PAGE_SIZE);
+ }
+ swap_pager_finish(spc);
+ TAILQ_REMOVE(&swap_pager_done, spc, spc_list);
+ goto doclean;
+ }
+
+ /*
+ * No operations done, thats all we can do for now.
+ */
+
+ splx(s);
+ break;
+
+ /*
+ * The desired page was found to be busy earlier in
+ * the scan but has since completed.
+ */
+doclean:
+ if (tspc && tspc == spc) {
+ tspc = NULL;
+ }
+ spc->spc_flags = 0;
+ TAILQ_INSERT_TAIL(&swap_pager_free, spc, spc_list);
+ if (swap_pager_needflags & SWAP_FREE_NEEDED) {
+ swap_pager_needflags &= ~SWAP_FREE_NEEDED;
+ wakeup((caddr_t)&swap_pager_free);
+ }
+ ++cleandone;
+ splx(s);
+ }
+
+ return(tspc ? TRUE : FALSE);
+}
+
+void
+swap_pager_finish(spc)
+ register swp_clean_t spc;
+{
+ vm_object_t object = spc->spc_m[0]->object;
+ int i;
+
+ if ((object->paging_in_progress -= spc->spc_count) == 0)
+ thread_wakeup((int) object);
+
+ /*
+ * If no error mark as clean and inform the pmap system.
+ * If error, mark as dirty so we will try again.
+ * (XXX could get stuck doing this, should give up after awhile)
+ */
+ if (spc->spc_flags & SPC_ERROR) {
+ for(i=0;i<spc->spc_count;i++) {
+ printf("swap_pager_finish: clean of page %x failed\n",
+ VM_PAGE_TO_PHYS(spc->spc_m[i]));
+ spc->spc_m[i]->flags |= PG_LAUNDRY;
+ }
+ } else {
+ for(i=0;i<spc->spc_count;i++) {
+ pmap_clear_modify(VM_PAGE_TO_PHYS(spc->spc_m[i]));
+ spc->spc_m[i]->flags |= PG_CLEAN;
+ }
+ }
+
+
+ for(i=0;i<spc->spc_count;i++) {
+ /*
+ * we wakeup any processes that are waiting on
+ * these pages.
+ */
+ PAGE_WAKEUP(spc->spc_m[i]);
+ }
+ nswiodone -= spc->spc_count;
+
+ return;
+}
+
+/*
+ * swap_pager_iodone
+ */
+void
+swap_pager_iodone(bp)
+ register struct buf *bp;
+{
+ register swp_clean_t spc;
+ int s;
+
+ s = splbio();
+ spc = (swp_clean_t) bp->b_spc;
+ TAILQ_REMOVE(&swap_pager_inuse, spc, spc_list);
+ TAILQ_INSERT_TAIL(&swap_pager_done, spc, spc_list);
+ if (bp->b_flags & B_ERROR) {
+ spc->spc_flags |= SPC_ERROR;
+ printf("error %d blkno %d sz %d ",
+ bp->b_error, bp->b_blkno, bp->b_bcount);
+ }
+
+/*
+ if ((bp->b_flags & B_READ) == 0)
+ vwakeup(bp);
+*/
+
+ bp->b_flags &= ~(B_BUSY|B_WANTED|B_PHYS|B_DIRTY|B_ASYNC);
+ if (bp->b_vp) {
+ brelvp(bp);
+ }
+ if( bp->b_rcred != NOCRED)
+ crfree(bp->b_rcred);
+ if( bp->b_wcred != NOCRED)
+ crfree(bp->b_wcred);
+
+ nswiodone += spc->spc_count;
+ if (--spc->spc_swp->sw_poip == 0) {
+ wakeup((caddr_t)spc->spc_swp);
+ }
+
+ if ((swap_pager_needflags & SWAP_FREE_NEEDED) ||
+ swap_pager_inuse.tqh_first == 0) {
+ swap_pager_needflags &= ~SWAP_FREE_NEEDED;
+ wakeup((caddr_t)&swap_pager_free);
+ wakeup((caddr_t)&vm_pages_needed);
+ }
+
+ if (vm_pageout_pages_needed) {
+ wakeup((caddr_t)&vm_pageout_pages_needed);
+ }
+
+ if ((swap_pager_inuse.tqh_first == NULL) ||
+ (cnt.v_free_count < cnt.v_free_min &&
+ nswiodone + cnt.v_free_count >= cnt.v_free_min) ) {
+ wakeup((caddr_t)&vm_pages_needed);
+ }
+ splx(s);
+}
+
+int bswneeded;
+/* TAILQ_HEAD(swqueue, buf) bswlist; */
+/*
+ * allocate a physical buffer
+ */
+struct buf *
+getpbuf() {
+ int s;
+ struct buf *bp;
+
+ s = splbio();
+ /* get a bp from the swap buffer header pool */
+ while ((bp = bswlist.tqh_first) == NULL) {
+ bswneeded = 1;
+ tsleep((caddr_t)&bswneeded, PVM, "wswbuf", 0);
+ }
+ TAILQ_REMOVE(&bswlist, bp, b_freelist);
+
+ splx(s);
+
+ bzero(bp, sizeof *bp);
+ bp->b_rcred = NOCRED;
+ bp->b_wcred = NOCRED;
+ return bp;
+}
+
+/*
+ * allocate a physical buffer, if one is available
+ */
+struct buf *
+trypbuf() {
+ int s;
+ struct buf *bp;
+
+ s = splbio();
+ if ((bp = bswlist.tqh_first) == NULL) {
+ splx(s);
+ return NULL;
+ }
+ TAILQ_REMOVE(&bswlist, bp, b_freelist);
+ splx(s);
+
+ bzero(bp, sizeof *bp);
+ bp->b_rcred = NOCRED;
+ bp->b_wcred = NOCRED;
+ return bp;
+}
+
+/*
+ * release a physical buffer
+ */
+void
+relpbuf(bp)
+ struct buf *bp;
+{
+ int s;
+
+ s = splbio();
+
+ if (bp->b_rcred != NOCRED) {
+ crfree(bp->b_rcred);
+ bp->b_rcred = NOCRED;
+ }
+ if (bp->b_wcred != NOCRED) {
+ crfree(bp->b_wcred);
+ bp->b_wcred = NOCRED;
+ }
+
+ if (bp->b_vp)
+ brelvp(bp);
+
+ TAILQ_INSERT_HEAD(&bswlist, bp, b_freelist);
+
+ if (bswneeded) {
+ bswneeded = 0;
+ wakeup((caddr_t)&bswlist);
+ }
+ splx(s);
+}
+
+/*
+ * return true if any swap control structures can be allocated
+ */
+int
+swap_pager_ready() {
+ if( swap_pager_free.tqh_first)
+ return 1;
+ else
+ return 0;
+}
diff --git a/sys/vm/swap_pager.h b/sys/vm/swap_pager.h
new file mode 100644
index 0000000..853edd5
--- /dev/null
+++ b/sys/vm/swap_pager.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1990 University of Utah.
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)swap_pager.h 7.1 (Berkeley) 12/5/90
+ * $Id: swap_pager.h,v 1.9 1994/03/14 21:54:23 davidg Exp $
+ */
+
+/*
+ * Modifications to the block allocation data structure by John S. Dyson
+ * 18 Dec 93.
+ */
+
+#ifndef _SWAP_PAGER_
+#define _SWAP_PAGER_ 1
+
+/*
+ * SWB_NPAGES can be set to any value from 1 to 16 pages per allocation,
+ * however, due to the allocation spilling into non-swap pager backed memory,
+ * suggest keeping SWB_NPAGES small (1-4). If high performance is manditory
+ * perhaps up to 8 pages might be in order????
+ * Above problem has been fixed, now we support 16 pages per block. Unused
+ * space is recovered by the swap pager now...
+ */
+#define SWB_NPAGES 8
+struct swblock {
+ unsigned short swb_valid; /* bitmask for valid pages */
+ unsigned short swb_locked; /* block locked */
+ int swb_block[SWB_NPAGES]; /* unfortunately int instead of daddr_t */
+};
+typedef struct swblock *sw_blk_t;
+
+/*
+ * Swap pager private data.
+ */
+struct swpager {
+ vm_size_t sw_osize; /* size of object we are backing (bytes) */
+ int sw_nblocks;/* number of blocks in list (sw_blk_t units) */
+ sw_blk_t sw_blocks; /* pointer to list of swap blocks */
+ short sw_flags; /* flags */
+ short sw_poip; /* pageouts in progress */
+ short sw_piip; /* pageins in progress */
+};
+typedef struct swpager *sw_pager_t;
+
+#define SW_WANTED 0x01
+#define SW_NAMED 0x02
+
+#ifdef KERNEL
+
+void swap_pager_init(void);
+vm_pager_t swap_pager_alloc(caddr_t, vm_size_t, vm_prot_t, vm_offset_t);
+void swap_pager_dealloc(vm_pager_t);
+boolean_t swap_pager_getpage(vm_pager_t, vm_page_t, boolean_t);
+boolean_t swap_pager_putpage(vm_pager_t, vm_page_t, boolean_t);
+boolean_t swap_pager_getmulti(vm_pager_t, vm_page_t *, int, int, boolean_t);
+boolean_t swap_pager_haspage(vm_pager_t, vm_offset_t);
+int swap_pager_io(sw_pager_t, vm_page_t *, int, int, int);
+void swap_pager_iodone(struct buf *);
+boolean_t swap_pager_clean();
+
+extern struct pagerops swappagerops;
+
+#endif
+
+#endif /* _SWAP_PAGER_ */
diff --git a/sys/vm/vm.h b/sys/vm/vm.h
new file mode 100644
index 0000000..bc18dd2
--- /dev/null
+++ b/sys/vm/vm.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm.h 8.2 (Berkeley) 12/13/93
+ */
+
+#ifndef VM_H
+#define VM_H
+
+typedef char vm_inherit_t; /* XXX: inheritance codes */
+
+union vm_map_object;
+typedef union vm_map_object vm_map_object_t;
+
+struct vm_map_entry;
+typedef struct vm_map_entry *vm_map_entry_t;
+
+struct vm_map;
+typedef struct vm_map *vm_map_t;
+
+struct vm_object;
+typedef struct vm_object *vm_object_t;
+
+struct vm_page;
+typedef struct vm_page *vm_page_t;
+
+struct pager_struct;
+typedef struct pager_struct *vm_pager_t;
+
+#include <sys/vmmeter.h>
+#include <sys/queue.h>
+#include <machine/cpufunc.h>
+#include <vm/vm_param.h>
+#include <vm/lock.h>
+#include <vm/vm_prot.h>
+#include <vm/vm_inherit.h>
+#include <vm/vm_map.h>
+#include <vm/vm_object.h>
+#include <vm/pmap.h>
+#include <vm/vm_extern.h>
+
+/*
+ * Shareable process virtual address space.
+ * May eventually be merged with vm_map.
+ * Several fields are temporary (text, data stuff).
+ */
+struct vmspace {
+ struct vm_map vm_map; /* VM address map */
+ struct pmap vm_pmap; /* private physical map */
+ int vm_refcnt; /* number of references */
+ caddr_t vm_shm; /* SYS5 shared memory private data XXX */
+/* we copy from vm_startcopy to the end of the structure on fork */
+#define vm_startcopy vm_rssize
+ segsz_t vm_rssize; /* current resident set size in pages */
+ segsz_t vm_swrss; /* resident set size before last swap */
+ segsz_t vm_tsize; /* text size (pages) XXX */
+ segsz_t vm_dsize; /* data size (pages) XXX */
+ segsz_t vm_ssize; /* stack size (pages) */
+ caddr_t vm_taddr; /* user virtual address of text XXX */
+ caddr_t vm_daddr; /* user virtual address of data XXX */
+ caddr_t vm_maxsaddr; /* user VA at max stack growth */
+ caddr_t vm_minsaddr; /* user VA at max stack growth */
+};
+#endif /* VM_H */
diff --git a/sys/vm/vm_extern.h b/sys/vm/vm_extern.h
new file mode 100644
index 0000000..bc62e42
--- /dev/null
+++ b/sys/vm/vm_extern.h
@@ -0,0 +1,135 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_extern.h 8.2 (Berkeley) 1/12/94
+ */
+
+struct buf;
+struct loadavg;
+struct proc;
+struct vmspace;
+struct vmtotal;
+struct mount;
+struct vnode;
+
+#ifdef KGDB
+void chgkprot __P((caddr_t, int, int));
+#endif
+
+/*
+ * Try to get semi-meaningful wait messages into thread_sleep...
+ */
+extern void thread_sleep_(int, simple_lock_t, char *);
+#if __GNUC__ >= 2
+#define thread_sleep(a,b,c) thread_sleep_((a), (b), __FUNCTION__)
+#else
+#define thread_sleep(a,b,c) thread_sleep_((a), (b), "vmslp")
+#endif
+
+#ifdef KERNEL
+#ifdef TYPEDEF_FOR_UAP
+int getpagesize __P((struct proc *p, void *, int *));
+int madvise __P((struct proc *, void *, int *));
+int mincore __P((struct proc *, void *, int *));
+int mprotect __P((struct proc *, void *, int *));
+int msync __P((struct proc *, void *, int *));
+int munmap __P((struct proc *, void *, int *));
+int obreak __P((struct proc *, void *, int *));
+int sbrk __P((struct proc *, void *, int *));
+int smmap __P((struct proc *, void *, int *));
+int sstk __P((struct proc *, void *, int *));
+#endif
+
+void assert_wait __P((int, boolean_t));
+int grow __P((struct proc *, u_int));
+void iprintf __P((const char *, ...));
+int kernacc __P((caddr_t, int, int));
+int kinfo_loadavg __P((int, char *, int *, int, int *));
+int kinfo_meter __P((int, caddr_t, int *, int, int *));
+vm_offset_t kmem_alloc __P((vm_map_t, vm_size_t));
+vm_offset_t kmem_alloc_pageable __P((vm_map_t, vm_size_t));
+vm_offset_t kmem_alloc_wait __P((vm_map_t, vm_size_t));
+void kmem_free __P((vm_map_t, vm_offset_t, vm_size_t));
+void kmem_free_wakeup __P((vm_map_t, vm_offset_t, vm_size_t));
+void kmem_init __P((vm_offset_t, vm_offset_t));
+vm_offset_t kmem_malloc __P((vm_map_t, vm_size_t, boolean_t));
+vm_map_t kmem_suballoc __P((vm_map_t, vm_offset_t *, vm_offset_t *,
+ vm_size_t, boolean_t));
+void loadav __P((struct loadavg *));
+void munmapfd __P((int));
+int pager_cache __P((vm_object_t, boolean_t));
+void sched __P((void));
+int svm_allocate __P((struct proc *, void *, int *));
+int svm_deallocate __P((struct proc *, void *, int *));
+int svm_inherit __P((struct proc *, void *, int *));
+int svm_protect __P((struct proc *, void *, int *));
+void swapinit __P((void));
+int swapon __P((struct proc *, void *, int *));
+void swapout __P((struct proc *));
+void swapout_threads __P((void));
+int swfree __P((struct proc *, int));
+void swstrategy __P((struct buf *));
+void thread_block __P((char *));
+void thread_sleep __P((int, simple_lock_t, boolean_t));
+void thread_wakeup __P((int));
+int useracc __P((caddr_t, int, int));
+int vm_allocate __P((vm_map_t,
+ vm_offset_t *, vm_size_t, boolean_t));
+int vm_allocate_with_pager __P((vm_map_t, vm_offset_t *,
+ vm_size_t, boolean_t, vm_pager_t, vm_offset_t, boolean_t));
+int vm_deallocate __P((vm_map_t, vm_offset_t, vm_size_t));
+int vm_fault __P((vm_map_t, vm_offset_t, vm_prot_t, boolean_t));
+void vm_fault_copy_entry __P((vm_map_t,
+ vm_map_t, vm_map_entry_t, vm_map_entry_t));
+void vm_fault_unwire __P((vm_map_t, vm_offset_t, vm_offset_t));
+int vm_fault_wire __P((vm_map_t, vm_offset_t, vm_offset_t));
+int vm_fork __P((struct proc *, struct proc *, int));
+int vm_inherit __P((vm_map_t,
+ vm_offset_t, vm_size_t, vm_inherit_t));
+void vm_init_limits __P((struct proc *));
+void vm_mem_init __P((void));
+int vm_mmap __P((vm_map_t, vm_offset_t *, vm_size_t,
+ vm_prot_t, vm_prot_t, int, caddr_t, vm_offset_t));
+int vm_protect __P((vm_map_t,
+ vm_offset_t, vm_size_t, boolean_t, vm_prot_t));
+void vm_set_page_size __P((void));
+void vmmeter __P((void));
+struct vmspace *vmspace_alloc __P((vm_offset_t, vm_offset_t, int));
+struct vmspace *vmspace_fork __P((struct vmspace *));
+void vmspace_free __P((struct vmspace *));
+void vmtotal __P((struct vmtotal *));
+void vnode_pager_setsize __P((struct vnode *, u_long));
+void vnode_pager_umount __P((struct mount *));
+boolean_t vnode_pager_uncache __P((struct vnode *));
+void vslock __P((caddr_t, u_int));
+void vsunlock __P((caddr_t, u_int, int));
+#endif
diff --git a/sys/vm/vm_fault.c b/sys/vm/vm_fault.c
new file mode 100644
index 0000000..3ce2d6e
--- /dev/null
+++ b/sys/vm/vm_fault.c
@@ -0,0 +1,1305 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1994 John S. Dyson
+ * All rights reserved.
+ * Copyright (c) 1994 David Greenman
+ * All rights reserved.
+ *
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_fault.c 8.4 (Berkeley) 1/12/94
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Page fault handling module.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/resourcevar.h>
+
+#include <vm/vm.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pageout.h>
+
+
+#define VM_FAULT_READ_AHEAD 4
+#define VM_FAULT_READ_AHEAD_MIN 1
+#define VM_FAULT_READ_BEHIND 3
+#define VM_FAULT_READ (VM_FAULT_READ_AHEAD+VM_FAULT_READ_BEHIND+1)
+extern int swap_pager_full;
+extern int vm_pageout_proc_limit;
+
+/*
+ * vm_fault:
+ *
+ * Handle a page fault occuring at the given address,
+ * requiring the given permissions, in the map specified.
+ * If successful, the page is inserted into the
+ * associated physical map.
+ *
+ * NOTE: the given address should be truncated to the
+ * proper page address.
+ *
+ * KERN_SUCCESS is returned if the page fault is handled; otherwise,
+ * a standard error specifying why the fault is fatal is returned.
+ *
+ *
+ * The map in question must be referenced, and remains so.
+ * Caller may hold no locks.
+ */
+int
+vm_fault(map, vaddr, fault_type, change_wiring)
+ vm_map_t map;
+ vm_offset_t vaddr;
+ vm_prot_t fault_type;
+ boolean_t change_wiring;
+{
+ vm_object_t first_object;
+ vm_offset_t first_offset;
+ vm_map_entry_t entry;
+ register vm_object_t object;
+ register vm_offset_t offset;
+ vm_page_t m;
+ vm_page_t first_m;
+ vm_prot_t prot;
+ int result;
+ boolean_t wired;
+ boolean_t su;
+ boolean_t lookup_still_valid;
+ boolean_t page_exists;
+ vm_page_t old_m;
+ vm_object_t next_object;
+ vm_page_t marray[VM_FAULT_READ];
+ int reqpage;
+ int spl;
+ int hardfault=0;
+
+ cnt.v_faults++; /* needs lock XXX */
+/*
+ * Recovery actions
+ */
+#define FREE_PAGE(m) { \
+ PAGE_WAKEUP(m); \
+ vm_page_lock_queues(); \
+ vm_page_free(m); \
+ vm_page_unlock_queues(); \
+}
+
+#define RELEASE_PAGE(m) { \
+ PAGE_WAKEUP(m); \
+ vm_page_lock_queues(); \
+ vm_page_activate(m); \
+ vm_page_unlock_queues(); \
+}
+
+#define UNLOCK_MAP { \
+ if (lookup_still_valid) { \
+ vm_map_lookup_done(map, entry); \
+ lookup_still_valid = FALSE; \
+ } \
+}
+
+#define UNLOCK_THINGS { \
+ object->paging_in_progress--; \
+ if (object->paging_in_progress == 0) \
+ wakeup((caddr_t)object); \
+ vm_object_unlock(object); \
+ if (object != first_object) { \
+ vm_object_lock(first_object); \
+ FREE_PAGE(first_m); \
+ first_object->paging_in_progress--; \
+ if (first_object->paging_in_progress == 0) \
+ wakeup((caddr_t)first_object); \
+ vm_object_unlock(first_object); \
+ } \
+ UNLOCK_MAP; \
+}
+
+#define UNLOCK_AND_DEALLOCATE { \
+ UNLOCK_THINGS; \
+ vm_object_deallocate(first_object); \
+}
+
+
+ RetryFault: ;
+
+ /*
+ * Find the backing store object and offset into
+ * it to begin the search.
+ */
+
+ if ((result = vm_map_lookup(&map, vaddr, fault_type, &entry,
+ &first_object, &first_offset,
+ &prot, &wired, &su)) != KERN_SUCCESS) {
+ return(result);
+ }
+ lookup_still_valid = TRUE;
+
+ if (wired)
+ fault_type = prot;
+
+ first_m = NULL;
+
+ /*
+ * Make a reference to this object to
+ * prevent its disposal while we are messing with
+ * it. Once we have the reference, the map is free
+ * to be diddled. Since objects reference their
+ * shadows (and copies), they will stay around as well.
+ */
+
+ vm_object_lock(first_object);
+
+ first_object->ref_count++;
+ first_object->paging_in_progress++;
+
+ /*
+ * INVARIANTS (through entire routine):
+ *
+ * 1) At all times, we must either have the object
+ * lock or a busy page in some object to prevent
+ * some other thread from trying to bring in
+ * the same page.
+ *
+ * Note that we cannot hold any locks during the
+ * pager access or when waiting for memory, so
+ * we use a busy page then.
+ *
+ * Note also that we aren't as concerned about
+ * more than one thead attempting to pager_data_unlock
+ * the same page at once, so we don't hold the page
+ * as busy then, but do record the highest unlock
+ * value so far. [Unlock requests may also be delivered
+ * out of order.]
+ *
+ * 2) Once we have a busy page, we must remove it from
+ * the pageout queues, so that the pageout daemon
+ * will not grab it away.
+ *
+ * 3) To prevent another thread from racing us down the
+ * shadow chain and entering a new page in the top
+ * object before we do, we must keep a busy page in
+ * the top object while following the shadow chain.
+ *
+ * 4) We must increment paging_in_progress on any object
+ * for which we have a busy page, to prevent
+ * vm_object_collapse from removing the busy page
+ * without our noticing.
+ */
+
+ /*
+ * Search for the page at object/offset.
+ */
+
+ object = first_object;
+ offset = first_offset;
+
+ /*
+ * See whether this page is resident
+ */
+
+ while (TRUE) {
+ m = vm_page_lookup(object, offset);
+ if (m != NULL) {
+ /*
+ * If the page is being brought in,
+ * wait for it and then retry.
+ */
+ if (m->flags & PG_BUSY) {
+ UNLOCK_THINGS;
+ if (m->flags & PG_BUSY) {
+ m->flags |= PG_WANTED;
+ tsleep((caddr_t)m,PSWP,"vmpfw",0);
+ }
+ vm_object_deallocate(first_object);
+ goto RetryFault;
+ }
+
+ /*
+ * Remove the page from the pageout daemon's
+ * reach while we play with it.
+ */
+
+ vm_page_lock_queues();
+ spl = splimp();
+ if (m->flags & PG_INACTIVE) {
+ TAILQ_REMOVE(&vm_page_queue_inactive, m, pageq);
+ m->flags &= ~PG_INACTIVE;
+ cnt.v_inactive_count--;
+ cnt.v_reactivated++;
+ }
+
+ if (m->flags & PG_ACTIVE) {
+ TAILQ_REMOVE(&vm_page_queue_active, m, pageq);
+ m->flags &= ~PG_ACTIVE;
+ cnt.v_active_count--;
+ }
+ splx(spl);
+ vm_page_unlock_queues();
+
+ /*
+ * Mark page busy for other threads.
+ */
+ m->flags |= PG_BUSY;
+ break;
+ }
+
+ if (((object->pager != NULL) &&
+ (!change_wiring || wired))
+ || (object == first_object)) {
+
+#if 0
+ if (curproc && (vaddr < VM_MAXUSER_ADDRESS) &&
+ (curproc->p_rlimit[RLIMIT_RSS].rlim_max <
+ curproc->p_vmspace->vm_pmap.pm_stats.resident_count * NBPG)) {
+ UNLOCK_AND_DEALLOCATE;
+ vm_fault_free_pages(curproc);
+ goto RetryFault;
+ }
+#endif
+
+ if (swap_pager_full && !object->shadow && (!object->pager ||
+ (object->pager && object->pager->pg_type == PG_SWAP &&
+ !vm_pager_has_page(object->pager, offset+object->paging_offset)))) {
+ if (vaddr < VM_MAXUSER_ADDRESS && curproc && curproc->p_pid >= 48) /* XXX */ {
+ printf("Process %d killed by vm_fault -- out of swap\n", curproc->p_pid);
+ psignal(curproc, SIGKILL);
+ curproc->p_estcpu = 0;
+ curproc->p_nice = PRIO_MIN;
+ setpriority(curproc);
+ }
+ }
+
+ /*
+ * Allocate a new page for this object/offset
+ * pair.
+ */
+
+ m = vm_page_alloc(object, offset);
+
+ if (m == NULL) {
+ UNLOCK_AND_DEALLOCATE;
+ VM_WAIT;
+ goto RetryFault;
+ }
+ }
+
+ if (object->pager != NULL && (!change_wiring || wired)) {
+ int rv;
+ int faultcount;
+ int reqpage;
+
+ /*
+ * Now that we have a busy page, we can
+ * release the object lock.
+ */
+ vm_object_unlock(object);
+ /*
+ * now we find out if any other pages should
+ * be paged in at this time
+ * this routine checks to see if the pages surrounding this fault
+ * reside in the same object as the page for this fault. If
+ * they do, then they are faulted in also into the
+ * object. The array "marray" returned contains an array of
+ * vm_page_t structs where one of them is the vm_page_t passed to
+ * the routine. The reqpage return value is the index into the
+ * marray for the vm_page_t passed to the routine.
+ */
+ cnt.v_pageins++;
+ faultcount = vm_fault_additional_pages(first_object, first_offset,
+ m, VM_FAULT_READ_BEHIND, VM_FAULT_READ_AHEAD, marray, &reqpage);
+
+ /*
+ * Call the pager to retrieve the data, if any,
+ * after releasing the lock on the map.
+ */
+ UNLOCK_MAP;
+
+ rv = faultcount ?
+ vm_pager_get_pages(object->pager,
+ marray, faultcount, reqpage, TRUE): VM_PAGER_FAIL;
+ if (rv == VM_PAGER_OK) {
+ /*
+ * Found the page.
+ * Leave it busy while we play with it.
+ */
+ vm_object_lock(object);
+
+ /*
+ * Relookup in case pager changed page.
+ * Pager is responsible for disposition
+ * of old page if moved.
+ */
+ m = vm_page_lookup(object, offset);
+
+ cnt.v_pgpgin++;
+ m->flags &= ~PG_FAKE;
+ pmap_clear_modify(VM_PAGE_TO_PHYS(m));
+ hardfault++;
+ break;
+ }
+
+ /*
+ * Remove the bogus page (which does not
+ * exist at this object/offset); before
+ * doing so, we must get back our object
+ * lock to preserve our invariant.
+ *
+ * Also wake up any other thread that may want
+ * to bring in this page.
+ *
+ * If this is the top-level object, we must
+ * leave the busy page to prevent another
+ * thread from rushing past us, and inserting
+ * the page in that object at the same time
+ * that we are.
+ */
+
+ vm_object_lock(object);
+ /*
+ * Data outside the range of the pager; an error
+ */
+ if ((rv == VM_PAGER_ERROR) || (rv == VM_PAGER_BAD)) {
+ FREE_PAGE(m);
+ UNLOCK_AND_DEALLOCATE;
+ return(KERN_PROTECTION_FAILURE); /* XXX */
+ }
+ if (object != first_object) {
+ FREE_PAGE(m);
+ /*
+ * XXX - we cannot just fall out at this
+ * point, m has been freed and is invalid!
+ */
+ }
+ }
+
+ /*
+ * We get here if the object has no pager (or unwiring)
+ * or the pager doesn't have the page.
+ */
+ if (object == first_object)
+ first_m = m;
+
+ /*
+ * Move on to the next object. Lock the next
+ * object before unlocking the current one.
+ */
+
+ offset += object->shadow_offset;
+ next_object = object->shadow;
+ if (next_object == NULL) {
+ /*
+ * If there's no object left, fill the page
+ * in the top object with zeros.
+ */
+ if (object != first_object) {
+ object->paging_in_progress--;
+ if (object->paging_in_progress == 0)
+ wakeup((caddr_t) object);
+ vm_object_unlock(object);
+
+ object = first_object;
+ offset = first_offset;
+ m = first_m;
+ vm_object_lock(object);
+ }
+ first_m = NULL;
+
+ vm_page_zero_fill(m);
+ cnt.v_zfod++;
+ m->flags &= ~PG_FAKE;
+ break;
+ }
+ else {
+ vm_object_lock(next_object);
+ if (object != first_object) {
+ object->paging_in_progress--;
+ if (object->paging_in_progress == 0)
+ wakeup((caddr_t) object);
+ }
+ vm_object_unlock(object);
+ object = next_object;
+ object->paging_in_progress++;
+ }
+ }
+
+ if ((m->flags & (PG_ACTIVE|PG_INACTIVE) != 0) ||
+ (m->flags & PG_BUSY) == 0)
+ panic("vm_fault: absent or active or inactive or not busy after main loop");
+
+ /*
+ * PAGE HAS BEEN FOUND.
+ * [Loop invariant still holds -- the object lock
+ * is held.]
+ */
+
+ old_m = m; /* save page that would be copied */
+
+ /*
+ * If the page is being written, but isn't
+ * already owned by the top-level object,
+ * we have to copy it into a new page owned
+ * by the top-level object.
+ */
+
+ if (object != first_object) {
+ /*
+ * We only really need to copy if we
+ * want to write it.
+ */
+
+ if (fault_type & VM_PROT_WRITE) {
+
+ /*
+ * If we try to collapse first_object at this
+ * point, we may deadlock when we try to get
+ * the lock on an intermediate object (since we
+ * have the bottom object locked). We can't
+ * unlock the bottom object, because the page
+ * we found may move (by collapse) if we do.
+ *
+ * Instead, we first copy the page. Then, when
+ * we have no more use for the bottom object,
+ * we unlock it and try to collapse.
+ *
+ * Note that we copy the page even if we didn't
+ * need to... that's the breaks.
+ */
+
+ /*
+ * We already have an empty page in
+ * first_object - use it.
+ */
+
+ vm_page_copy(m, first_m);
+ first_m->flags &= ~PG_FAKE;
+
+ /*
+ * If another map is truly sharing this
+ * page with us, we have to flush all
+ * uses of the original page, since we
+ * can't distinguish those which want the
+ * original from those which need the
+ * new copy.
+ *
+ * XXX If we know that only one map has
+ * access to this page, then we could
+ * avoid the pmap_page_protect() call.
+ */
+
+ vm_page_lock_queues();
+
+ vm_page_activate(m);
+ pmap_page_protect(VM_PAGE_TO_PHYS(m), VM_PROT_NONE);
+ if ((m->flags & PG_CLEAN) == 0)
+ m->flags |= PG_LAUNDRY;
+ vm_page_unlock_queues();
+
+ /*
+ * We no longer need the old page or object.
+ */
+ PAGE_WAKEUP(m);
+ object->paging_in_progress--;
+ if (object->paging_in_progress == 0)
+ wakeup((caddr_t) object);
+ vm_object_unlock(object);
+
+ /*
+ * Only use the new page below...
+ */
+
+ cnt.v_cow_faults++;
+ m = first_m;
+ object = first_object;
+ offset = first_offset;
+
+ /*
+ * Now that we've gotten the copy out of the
+ * way, let's try to collapse the top object.
+ */
+ vm_object_lock(object);
+ /*
+ * But we have to play ugly games with
+ * paging_in_progress to do that...
+ */
+ object->paging_in_progress--;
+ if (object->paging_in_progress == 0)
+ wakeup((caddr_t) object);
+ vm_object_collapse(object);
+ object->paging_in_progress++;
+ }
+ else {
+ prot &= ~VM_PROT_WRITE;
+ m->flags |= PG_COPYONWRITE;
+ }
+ }
+
+ if (m->flags & (PG_ACTIVE|PG_INACTIVE))
+ panic("vm_fault: active or inactive before copy object handling");
+
+ /*
+ * If the page is being written, but hasn't been
+ * copied to the copy-object, we have to copy it there.
+ */
+ RetryCopy:
+ if (first_object->copy != NULL) {
+ vm_object_t copy_object = first_object->copy;
+ vm_offset_t copy_offset;
+ vm_page_t copy_m;
+
+ /*
+ * We only need to copy if we want to write it.
+ */
+ if ((fault_type & VM_PROT_WRITE) == 0) {
+ prot &= ~VM_PROT_WRITE;
+ m->flags |= PG_COPYONWRITE;
+ }
+ else {
+ /*
+ * Try to get the lock on the copy_object.
+ */
+ if (!vm_object_lock_try(copy_object)) {
+ vm_object_unlock(object);
+ /* should spin a bit here... */
+ vm_object_lock(object);
+ goto RetryCopy;
+ }
+
+ /*
+ * Make another reference to the copy-object,
+ * to keep it from disappearing during the
+ * copy.
+ */
+ copy_object->ref_count++;
+
+ /*
+ * Does the page exist in the copy?
+ */
+ copy_offset = first_offset
+ - copy_object->shadow_offset;
+ copy_m = vm_page_lookup(copy_object, copy_offset);
+ if (page_exists = (copy_m != NULL)) {
+ if (copy_m->flags & PG_BUSY) {
+ /*
+ * If the page is being brought
+ * in, wait for it and then retry.
+ */
+ PAGE_ASSERT_WAIT(copy_m, !change_wiring);
+ RELEASE_PAGE(m);
+ copy_object->ref_count--;
+ vm_object_unlock(copy_object);
+ UNLOCK_THINGS;
+ thread_block("fltcpy");
+ vm_object_deallocate(first_object);
+ goto RetryFault;
+ }
+ }
+
+ /*
+ * If the page is not in memory (in the object)
+ * and the object has a pager, we have to check
+ * if the pager has the data in secondary
+ * storage.
+ */
+ if (!page_exists) {
+
+ /*
+ * If we don't allocate a (blank) page
+ * here... another thread could try
+ * to page it in, allocate a page, and
+ * then block on the busy page in its
+ * shadow (first_object). Then we'd
+ * trip over the busy page after we
+ * found that the copy_object's pager
+ * doesn't have the page...
+ */
+ copy_m = vm_page_alloc(copy_object, copy_offset);
+ if (copy_m == NULL) {
+ /*
+ * Wait for a page, then retry.
+ */
+ RELEASE_PAGE(m);
+ copy_object->ref_count--;
+ vm_object_unlock(copy_object);
+ UNLOCK_AND_DEALLOCATE;
+ VM_WAIT;
+ goto RetryFault;
+ }
+
+ if (copy_object->pager != NULL) {
+ vm_object_unlock(object);
+ vm_object_unlock(copy_object);
+ UNLOCK_MAP;
+
+ page_exists = vm_pager_has_page(
+ copy_object->pager,
+ (copy_offset + copy_object->paging_offset));
+
+ vm_object_lock(copy_object);
+
+ /*
+ * Since the map is unlocked, someone
+ * else could have copied this object
+ * and put a different copy_object
+ * between the two. Or, the last
+ * reference to the copy-object (other
+ * than the one we have) may have
+ * disappeared - if that has happened,
+ * we don't need to make the copy.
+ */
+ if (copy_object->shadow != object ||
+ copy_object->ref_count == 1) {
+ /*
+ * Gaah... start over!
+ */
+ FREE_PAGE(copy_m);
+ vm_object_unlock(copy_object);
+ vm_object_deallocate(copy_object);
+ /* may block */
+ vm_object_lock(object);
+ goto RetryCopy;
+ }
+ vm_object_lock(object);
+
+ if (page_exists) {
+ /*
+ * We didn't need the page
+ */
+ FREE_PAGE(copy_m);
+ }
+ }
+ }
+ if (!page_exists) {
+ /*
+ * Must copy page into copy-object.
+ */
+ vm_page_copy(m, copy_m);
+ copy_m->flags &= ~PG_FAKE;
+
+ /*
+ * Things to remember:
+ * 1. The copied page must be marked 'dirty'
+ * so it will be paged out to the copy
+ * object.
+ * 2. If the old page was in use by any users
+ * of the copy-object, it must be removed
+ * from all pmaps. (We can't know which
+ * pmaps use it.)
+ */
+ vm_page_lock_queues();
+
+ vm_page_activate(old_m);
+
+
+ pmap_page_protect(VM_PAGE_TO_PHYS(old_m),
+ VM_PROT_NONE);
+ if ((old_m->flags & PG_CLEAN) == 0)
+ old_m->flags |= PG_LAUNDRY;
+ copy_m->flags &= ~PG_CLEAN;
+ vm_page_activate(copy_m);
+ vm_page_unlock_queues();
+
+ PAGE_WAKEUP(copy_m);
+ }
+ /*
+ * The reference count on copy_object must be
+ * at least 2: one for our extra reference,
+ * and at least one from the outside world
+ * (we checked that when we last locked
+ * copy_object).
+ */
+ copy_object->ref_count--;
+ vm_object_unlock(copy_object);
+ m->flags &= ~PG_COPYONWRITE;
+ }
+ }
+
+ if (m->flags & (PG_ACTIVE | PG_INACTIVE))
+ panic("vm_fault: active or inactive before retrying lookup");
+
+ /*
+ * We must verify that the maps have not changed
+ * since our last lookup.
+ */
+
+ if (!lookup_still_valid) {
+ vm_object_t retry_object;
+ vm_offset_t retry_offset;
+ vm_prot_t retry_prot;
+
+ /*
+ * Since map entries may be pageable, make sure we can
+ * take a page fault on them.
+ */
+ vm_object_unlock(object);
+
+ /*
+ * To avoid trying to write_lock the map while another
+ * thread has it read_locked (in vm_map_pageable), we
+ * do not try for write permission. If the page is
+ * still writable, we will get write permission. If it
+ * is not, or has been marked needs_copy, we enter the
+ * mapping without write permission, and will merely
+ * take another fault.
+ */
+ result = vm_map_lookup(&map, vaddr,
+ fault_type & ~VM_PROT_WRITE, &entry,
+ &retry_object, &retry_offset, &retry_prot,
+ &wired, &su);
+
+ vm_object_lock(object);
+
+ /*
+ * If we don't need the page any longer, put it on the
+ * active list (the easiest thing to do here). If no
+ * one needs it, pageout will grab it eventually.
+ */
+
+ if (result != KERN_SUCCESS) {
+ RELEASE_PAGE(m);
+ UNLOCK_AND_DEALLOCATE;
+ return(result);
+ }
+
+ lookup_still_valid = TRUE;
+
+ if ((retry_object != first_object) ||
+ (retry_offset != first_offset)) {
+ RELEASE_PAGE(m);
+ UNLOCK_AND_DEALLOCATE;
+ goto RetryFault;
+ }
+
+ /*
+ * Check whether the protection has changed or the object
+ * has been copied while we left the map unlocked.
+ * Changing from read to write permission is OK - we leave
+ * the page write-protected, and catch the write fault.
+ * Changing from write to read permission means that we
+ * can't mark the page write-enabled after all.
+ */
+ prot &= retry_prot;
+ if (m->flags & PG_COPYONWRITE)
+ prot &= ~VM_PROT_WRITE;
+ }
+
+ /*
+ * (the various bits we're fiddling with here are locked by
+ * the object's lock)
+ */
+
+ /* XXX This distorts the meaning of the copy_on_write bit */
+
+ if (prot & VM_PROT_WRITE)
+ m->flags &= ~PG_COPYONWRITE;
+
+ /*
+ * It's critically important that a wired-down page be faulted
+ * only once in each map for which it is wired.
+ */
+
+ if (m->flags & (PG_ACTIVE | PG_INACTIVE))
+ panic("vm_fault: active or inactive before pmap_enter");
+
+ vm_object_unlock(object);
+
+ /*
+ * Put this page into the physical map.
+ * We had to do the unlock above because pmap_enter
+ * may cause other faults. We don't put the
+ * page back on the active queue until later so
+ * that the page-out daemon won't find us (yet).
+ */
+
+ pmap_enter(map->pmap, vaddr, VM_PAGE_TO_PHYS(m), prot, wired);
+
+ /*
+ * If the page is not wired down, then put it where the
+ * pageout daemon can find it.
+ */
+ vm_object_lock(object);
+ vm_page_lock_queues();
+ if (change_wiring) {
+ if (wired)
+ vm_page_wire(m);
+ else
+ vm_page_unwire(m);
+ }
+ else {
+ vm_page_activate(m);
+ }
+
+ if( curproc && curproc->p_stats) {
+ if (hardfault) {
+ curproc->p_stats->p_ru.ru_majflt++;
+ } else {
+ curproc->p_stats->p_ru.ru_minflt++;
+ }
+ }
+
+ vm_page_unlock_queues();
+
+ /*
+ * Unlock everything, and return
+ */
+
+ PAGE_WAKEUP(m);
+ UNLOCK_AND_DEALLOCATE;
+
+ return(KERN_SUCCESS);
+
+}
+
+/*
+ * vm_fault_wire:
+ *
+ * Wire down a range of virtual addresses in a map.
+ */
+int
+vm_fault_wire(map, start, end)
+ vm_map_t map;
+ vm_offset_t start, end;
+{
+
+ register vm_offset_t va;
+ register pmap_t pmap;
+ int rv;
+
+ pmap = vm_map_pmap(map);
+
+ /*
+ * Inform the physical mapping system that the
+ * range of addresses may not fault, so that
+ * page tables and such can be locked down as well.
+ */
+
+ pmap_pageable(pmap, start, end, FALSE);
+
+ /*
+ * We simulate a fault to get the page and enter it
+ * in the physical map.
+ */
+
+ for (va = start; va < end; va += PAGE_SIZE) {
+ rv = vm_fault(map, va, VM_PROT_NONE, TRUE);
+ if (rv) {
+ if (va != start)
+ vm_fault_unwire(map, start, va);
+ return(rv);
+ }
+ }
+ return(KERN_SUCCESS);
+}
+
+
+/*
+ * vm_fault_unwire:
+ *
+ * Unwire a range of virtual addresses in a map.
+ */
+void
+vm_fault_unwire(map, start, end)
+ vm_map_t map;
+ vm_offset_t start, end;
+{
+
+ register vm_offset_t va, pa;
+ register pmap_t pmap;
+
+ pmap = vm_map_pmap(map);
+
+ /*
+ * Since the pages are wired down, we must be able to
+ * get their mappings from the physical map system.
+ */
+
+ vm_page_lock_queues();
+
+ for (va = start; va < end; va += PAGE_SIZE) {
+ pa = pmap_extract(pmap, va);
+ if (pa == (vm_offset_t) 0) {
+ panic("unwire: page not in pmap");
+ }
+ pmap_change_wiring(pmap, va, FALSE);
+ vm_page_unwire(PHYS_TO_VM_PAGE(pa));
+ }
+ vm_page_unlock_queues();
+
+ /*
+ * Inform the physical mapping system that the range
+ * of addresses may fault, so that page tables and
+ * such may be unwired themselves.
+ */
+
+ pmap_pageable(pmap, start, end, TRUE);
+
+}
+
+/*
+ * Routine:
+ * vm_fault_copy_entry
+ * Function:
+ * Copy all of the pages from a wired-down map entry to another.
+ *
+ * In/out conditions:
+ * The source and destination maps must be locked for write.
+ * The source map entry must be wired down (or be a sharing map
+ * entry corresponding to a main map entry that is wired down).
+ */
+
+void
+vm_fault_copy_entry(dst_map, src_map, dst_entry, src_entry)
+ vm_map_t dst_map;
+ vm_map_t src_map;
+ vm_map_entry_t dst_entry;
+ vm_map_entry_t src_entry;
+{
+ vm_object_t dst_object;
+ vm_object_t src_object;
+ vm_offset_t dst_offset;
+ vm_offset_t src_offset;
+ vm_prot_t prot;
+ vm_offset_t vaddr;
+ vm_page_t dst_m;
+ vm_page_t src_m;
+
+#ifdef lint
+ src_map++;
+#endif lint
+
+ src_object = src_entry->object.vm_object;
+ src_offset = src_entry->offset;
+
+ /*
+ * Create the top-level object for the destination entry.
+ * (Doesn't actually shadow anything - we copy the pages
+ * directly.)
+ */
+ dst_object = vm_object_allocate(
+ (vm_size_t) (dst_entry->end - dst_entry->start));
+
+ dst_entry->object.vm_object = dst_object;
+ dst_entry->offset = 0;
+
+ prot = dst_entry->max_protection;
+
+ /*
+ * Loop through all of the pages in the entry's range, copying
+ * each one from the source object (it should be there) to the
+ * destination object.
+ */
+ for (vaddr = dst_entry->start, dst_offset = 0;
+ vaddr < dst_entry->end;
+ vaddr += PAGE_SIZE, dst_offset += PAGE_SIZE) {
+
+ /*
+ * Allocate a page in the destination object
+ */
+ vm_object_lock(dst_object);
+ do {
+ dst_m = vm_page_alloc(dst_object, dst_offset);
+ if (dst_m == NULL) {
+ vm_object_unlock(dst_object);
+ VM_WAIT;
+ vm_object_lock(dst_object);
+ }
+ } while (dst_m == NULL);
+
+ /*
+ * Find the page in the source object, and copy it in.
+ * (Because the source is wired down, the page will be
+ * in memory.)
+ */
+ vm_object_lock(src_object);
+ src_m = vm_page_lookup(src_object, dst_offset + src_offset);
+ if (src_m == NULL)
+ panic("vm_fault_copy_wired: page missing");
+
+ vm_page_copy(src_m, dst_m);
+
+ /*
+ * Enter it in the pmap...
+ */
+ vm_object_unlock(src_object);
+ vm_object_unlock(dst_object);
+
+ pmap_enter(dst_map->pmap, vaddr, VM_PAGE_TO_PHYS(dst_m),
+ prot, FALSE);
+
+ /*
+ * Mark it no longer busy, and put it on the active list.
+ */
+ vm_object_lock(dst_object);
+ vm_page_lock_queues();
+ vm_page_activate(dst_m);
+ vm_page_unlock_queues();
+ PAGE_WAKEUP(dst_m);
+ vm_object_unlock(dst_object);
+ }
+}
+
+
+/*
+ * looks page up in shadow chain
+ */
+
+int
+vm_fault_page_lookup(object, offset, rtobject, rtoffset, rtm)
+ vm_object_t object;
+ vm_offset_t offset;
+ vm_object_t *rtobject;
+ vm_offset_t *rtoffset;
+ vm_page_t *rtm;
+{
+ vm_page_t m;
+ vm_object_t first_object = object;
+
+ *rtm = 0;
+ *rtobject = 0;
+ *rtoffset = 0;
+
+
+ while (!(m=vm_page_lookup(object, offset))) {
+ if (object->pager) {
+ if (vm_pager_has_page(object->pager, object->paging_offset+offset)) {
+ *rtobject = object;
+ *rtoffset = offset;
+ return 1;
+ }
+ }
+
+ if (!object->shadow)
+ return 0;
+ else {
+ offset += object->shadow_offset;
+ object = object->shadow;
+ }
+ }
+ *rtobject = object;
+ *rtoffset = offset;
+ *rtm = m;
+ return 1;
+}
+
+/*
+ * This routine checks around the requested page for other pages that
+ * might be able to be faulted in.
+ *
+ * Inputs:
+ * first_object, first_offset, m, rbehind, rahead
+ *
+ * Outputs:
+ * marray (array of vm_page_t), reqpage (index of requested page)
+ *
+ * Return value:
+ * number of pages in marray
+ */
+int
+vm_fault_additional_pages(first_object, first_offset, m, rbehind, raheada, marray, reqpage)
+ vm_object_t first_object;
+ vm_offset_t first_offset;
+ vm_page_t m;
+ int rbehind;
+ int raheada;
+ vm_page_t *marray;
+ int *reqpage;
+{
+ int i;
+ vm_page_t tmpm;
+ vm_object_t object;
+ vm_offset_t offset, startoffset, endoffset, toffset, size;
+ vm_object_t rtobject;
+ vm_page_t rtm;
+ vm_offset_t rtoffset;
+ vm_offset_t offsetdiff;
+ int rahead;
+ int treqpage;
+
+ object = m->object;
+ offset = m->offset;
+
+ offsetdiff = offset - first_offset;
+
+ /*
+ * if the requested page is not available, then give up now
+ */
+
+ if (!vm_pager_has_page(object->pager, object->paging_offset+offset))
+ return 0;
+
+ /*
+ * if there is no getmulti routine for this pager, then just allow
+ * one page to be read.
+ */
+/*
+ if (!object->pager->pg_ops->pgo_getpages) {
+ *reqpage = 0;
+ marray[0] = m;
+ return 1;
+ }
+*/
+
+ /*
+ * try to do any readahead that we might have free pages for.
+ */
+ rahead = raheada;
+ if (rahead > (cnt.v_free_count - cnt.v_free_reserved)) {
+ rahead = cnt.v_free_count - cnt.v_free_reserved;
+ rbehind = 0;
+ }
+
+ if (cnt.v_free_count < cnt.v_free_min) {
+ if (rahead > VM_FAULT_READ_AHEAD_MIN)
+ rahead = VM_FAULT_READ_AHEAD_MIN;
+ rbehind = 0;
+ }
+
+ /*
+ * if we don't have any free pages, then just read one page.
+ */
+ if (rahead <= 0) {
+ *reqpage = 0;
+ marray[0] = m;
+ return 1;
+ }
+
+ /*
+ * scan backward for the read behind pages --
+ * in memory or on disk not in same object
+ */
+ toffset = offset - NBPG;
+ if( rbehind*NBPG > offset)
+ rbehind = offset / NBPG;
+ startoffset = offset - rbehind*NBPG;
+ while (toffset >= startoffset) {
+ if (!vm_fault_page_lookup(first_object, toffset - offsetdiff, &rtobject, &rtoffset, &rtm) ||
+ rtm != 0 || rtobject != object) {
+ startoffset = toffset + NBPG;
+ break;
+ }
+ if( toffset == 0)
+ break;
+ toffset -= NBPG;
+ }
+
+ /*
+ * scan forward for the read ahead pages --
+ * in memory or on disk not in same object
+ */
+ toffset = offset + NBPG;
+ endoffset = offset + (rahead+1)*NBPG;
+ while (toffset < object->size && toffset < endoffset) {
+ if (!vm_fault_page_lookup(first_object, toffset - offsetdiff, &rtobject, &rtoffset, &rtm) ||
+ rtm != 0 || rtobject != object) {
+ break;
+ }
+ toffset += NBPG;
+ }
+ endoffset = toffset;
+
+ /* calculate number of bytes of pages */
+ size = (endoffset - startoffset) / NBPG;
+
+ /* calculate the page offset of the required page */
+ treqpage = (offset - startoffset) / NBPG;
+
+ /* see if we have space (again) */
+ if (cnt.v_free_count >= cnt.v_free_reserved + size) {
+ bzero(marray, (rahead + rbehind + 1) * sizeof(vm_page_t));
+ /*
+ * get our pages and don't block for them
+ */
+ for (i = 0; i < size; i++) {
+ if (i != treqpage)
+ rtm = vm_page_alloc(object, startoffset + i * NBPG);
+ else
+ rtm = m;
+ marray[i] = rtm;
+ }
+
+ for (i = 0; i < size; i++) {
+ if (marray[i] == 0)
+ break;
+ }
+
+ /*
+ * if we could not get our block of pages, then
+ * free the readahead/readbehind pages.
+ */
+ if (i < size) {
+ for (i = 0; i < size; i++) {
+ if (i != treqpage && marray[i])
+ FREE_PAGE(marray[i]);
+ }
+ *reqpage = 0;
+ marray[0] = m;
+ return 1;
+ }
+
+ *reqpage = treqpage;
+ return size;
+ }
+ *reqpage = 0;
+ marray[0] = m;
+ return 1;
+}
+
diff --git a/sys/vm/vm_glue.c b/sys/vm/vm_glue.c
new file mode 100644
index 0000000..f181ab0
--- /dev/null
+++ b/sys/vm/vm_glue.c
@@ -0,0 +1,685 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_glue.c 8.6 (Berkeley) 1/5/94
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/resourcevar.h>
+#include <sys/buf.h>
+#include <sys/user.h>
+
+#include <sys/kernel.h>
+#include <sys/dkstat.h>
+
+#include <vm/vm.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pageout.h>
+#include <vm/vm_kern.h>
+
+#include <machine/stdarg.h>
+
+extern char kstack[];
+int avefree = 0; /* XXX */
+int readbuffers = 0; /* XXX allow kgdb to read kernel buffer pool */
+/* vm_map_t upages_map; */
+
+void swapout(struct proc *p);
+int
+kernacc(addr, len, rw)
+ caddr_t addr;
+ int len, rw;
+{
+ boolean_t rv;
+ vm_offset_t saddr, eaddr;
+ vm_prot_t prot = rw == B_READ ? VM_PROT_READ : VM_PROT_WRITE;
+
+ saddr = trunc_page(addr);
+ eaddr = round_page(addr+len);
+ rv = vm_map_check_protection(kernel_map, saddr, eaddr, prot);
+ return(rv == TRUE);
+}
+
+int
+useracc(addr, len, rw)
+ caddr_t addr;
+ int len, rw;
+{
+ boolean_t rv;
+ vm_prot_t prot = rw == B_READ ? VM_PROT_READ : VM_PROT_WRITE;
+
+ /*
+ * XXX - specially disallow access to user page tables - they are
+ * in the map.
+ *
+ * XXX - don't specially disallow access to the user area - treat
+ * it as incorrectly as elsewhere.
+ *
+ * XXX - VM_MAXUSER_ADDRESS is an end address, not a max. It was
+ * only used (as an end address) in trap.c. Use it as an end
+ * address here too.
+ */
+ if ((vm_offset_t) addr >= VM_MAXUSER_ADDRESS
+ || (vm_offset_t) addr + len > VM_MAXUSER_ADDRESS
+ || (vm_offset_t) addr + len <= (vm_offset_t) addr) {
+ return (FALSE);
+ }
+
+ rv = vm_map_check_protection(&curproc->p_vmspace->vm_map,
+ trunc_page(addr), round_page(addr+len), prot);
+ return(rv == TRUE);
+}
+
+#ifdef KGDB
+/*
+ * Change protections on kernel pages from addr to addr+len
+ * (presumably so debugger can plant a breakpoint).
+ * All addresses are assumed to reside in the Sysmap,
+ */
+chgkprot(addr, len, rw)
+ register caddr_t addr;
+ int len, rw;
+{
+ vm_prot_t prot = rw == B_READ ? VM_PROT_READ : VM_PROT_WRITE;
+
+ vm_map_protect(kernel_map, trunc_page(addr),
+ round_page(addr+len), prot, FALSE);
+}
+#endif
+void
+vslock(addr, len)
+ caddr_t addr;
+ u_int len;
+{
+ vm_map_pageable(&curproc->p_vmspace->vm_map, trunc_page(addr),
+ round_page(addr+len), FALSE);
+}
+
+void
+vsunlock(addr, len, dirtied)
+ caddr_t addr;
+ u_int len;
+ int dirtied;
+{
+#ifdef lint
+ dirtied++;
+#endif lint
+ vm_map_pageable(&curproc->p_vmspace->vm_map, trunc_page(addr),
+ round_page(addr+len), TRUE);
+}
+
+/*
+ * Implement fork's actions on an address space.
+ * Here we arrange for the address space to be copied or referenced,
+ * allocate a user struct (pcb and kernel stack), then call the
+ * machine-dependent layer to fill those in and make the new process
+ * ready to run.
+ * NOTE: the kernel stack may be at a different location in the child
+ * process, and thus addresses of automatic variables may be invalid
+ * after cpu_fork returns in the child process. We do nothing here
+ * after cpu_fork returns.
+ */
+int
+vm_fork(p1, p2, isvfork)
+ register struct proc *p1, *p2;
+ int isvfork;
+{
+ register struct user *up;
+ vm_offset_t addr, ptaddr;
+ int i;
+ struct vm_map *vp;
+
+ while( cnt.v_free_count < cnt.v_free_min)
+ VM_WAIT;
+
+ /*
+ * avoid copying any of the parent's pagetables or other per-process
+ * objects that reside in the map by marking all of them non-inheritable
+ */
+ (void)vm_map_inherit(&p1->p_vmspace->vm_map,
+ UPT_MIN_ADDRESS - UPAGES * NBPG, VM_MAX_ADDRESS, VM_INHERIT_NONE);
+ p2->p_vmspace = vmspace_fork(p1->p_vmspace);
+
+#ifdef SYSVSHM
+ if (p1->p_vmspace->vm_shm)
+ shmfork(p1, p2, isvfork);
+#endif
+
+ /*
+ * Allocate a wired-down (for now) pcb and kernel stack for the process
+ */
+
+ addr = (vm_offset_t) kstack;
+
+ vp = &p2->p_vmspace->vm_map;
+
+ /* ream out old pagetables and kernel stack */
+ (void)vm_deallocate(vp, addr, UPT_MAX_ADDRESS - addr);
+
+ /* get new pagetables and kernel stack */
+ (void)vm_allocate(vp, &addr, UPT_MAX_ADDRESS - addr, FALSE);
+
+ /* force in the page table encompassing the UPAGES */
+ ptaddr = trunc_page((u_int)vtopte(addr));
+ vm_map_pageable(vp, ptaddr, ptaddr + NBPG, FALSE);
+
+ /* and force in (demand-zero) the UPAGES */
+ vm_map_pageable(vp, addr, addr + UPAGES * NBPG, FALSE);
+
+ /* get a kernel virtual address for the UPAGES for this proc */
+ up = (struct user *)kmem_alloc_pageable(kernel_map, UPAGES * NBPG);
+
+ /* and force-map the upages into the kernel pmap */
+ for (i = 0; i < UPAGES; i++)
+ pmap_enter(vm_map_pmap(kernel_map),
+ ((vm_offset_t) up) + NBPG * i,
+ pmap_extract(vp->pmap, addr + NBPG * i),
+ VM_PROT_READ|VM_PROT_WRITE, 1);
+
+ /* and allow the UPAGES page table entry to be paged (at the vm system level) */
+ vm_map_pageable(vp, ptaddr, ptaddr + NBPG, TRUE);
+
+ p2->p_addr = up;
+
+ /*
+ * p_stats and p_sigacts currently point at fields
+ * in the user struct but not at &u, instead at p_addr.
+ * Copy p_sigacts and parts of p_stats; zero the rest
+ * of p_stats (statistics).
+ */
+ p2->p_stats = &up->u_stats;
+ p2->p_sigacts = &up->u_sigacts;
+ up->u_sigacts = *p1->p_sigacts;
+ bzero(&up->u_stats.pstat_startzero,
+ (unsigned) ((caddr_t)&up->u_stats.pstat_endzero -
+ (caddr_t)&up->u_stats.pstat_startzero));
+ bcopy(&p1->p_stats->pstat_startcopy, &up->u_stats.pstat_startcopy,
+ ((caddr_t)&up->u_stats.pstat_endcopy -
+ (caddr_t)&up->u_stats.pstat_startcopy));
+
+
+ /*
+ * cpu_fork will copy and update the kernel stack and pcb,
+ * and make the child ready to run. It marks the child
+ * so that it can return differently than the parent.
+ * It returns twice, once in the parent process and
+ * once in the child.
+ */
+ return (cpu_fork(p1, p2));
+}
+
+/*
+ * Set default limits for VM system.
+ * Called for proc 0, and then inherited by all others.
+ */
+void
+vm_init_limits(p)
+ register struct proc *p;
+{
+ int tmp;
+
+ /*
+ * Set up the initial limits on process VM.
+ * Set the maximum resident set size to be all
+ * of (reasonably) available memory. This causes
+ * any single, large process to start random page
+ * replacement once it fills memory.
+ */
+ p->p_rlimit[RLIMIT_STACK].rlim_cur = DFLSSIZ;
+ p->p_rlimit[RLIMIT_STACK].rlim_max = MAXSSIZ;
+ p->p_rlimit[RLIMIT_DATA].rlim_cur = DFLDSIZ;
+ p->p_rlimit[RLIMIT_DATA].rlim_max = MAXDSIZ;
+ tmp = ((2 * cnt.v_free_count) / 3) - 32;
+ if (cnt.v_free_count < 512)
+ tmp = cnt.v_free_count;
+ p->p_rlimit[RLIMIT_RSS].rlim_cur = ptoa(tmp);
+ p->p_rlimit[RLIMIT_RSS].rlim_max = RLIM_INFINITY;
+}
+
+#ifdef DEBUG
+int enableswap = 1;
+int swapdebug = 0;
+#define SDB_FOLLOW 1
+#define SDB_SWAPIN 2
+#define SDB_SWAPOUT 4
+#endif
+
+void
+faultin(p)
+struct proc *p;
+{
+ vm_offset_t i;
+ vm_offset_t vaddr, ptaddr;
+ vm_offset_t v, v1;
+ struct user *up;
+ int s;
+ int opflag;
+
+ if ((p->p_flag & P_INMEM) == 0) {
+ int rv0, rv1;
+ vm_map_t map;
+
+ ++p->p_lock;
+
+ map = &p->p_vmspace->vm_map;
+ /* force the page table encompassing the kernel stack (upages) */
+ ptaddr = trunc_page((u_int)vtopte(kstack));
+ vm_map_pageable(map, ptaddr, ptaddr + NBPG, FALSE);
+
+ /* wire in the UPAGES */
+ vm_map_pageable(map, (vm_offset_t) kstack,
+ (vm_offset_t) kstack + UPAGES * NBPG, FALSE);
+
+ /* and map them nicely into the kernel pmap */
+ for (i = 0; i < UPAGES; i++) {
+ vm_offset_t off = i * NBPG;
+ vm_offset_t pa = (vm_offset_t)
+ pmap_extract(&p->p_vmspace->vm_pmap,
+ (vm_offset_t) kstack + off);
+ pmap_enter(vm_map_pmap(kernel_map),
+ ((vm_offset_t)p->p_addr) + off,
+ pa, VM_PROT_READ|VM_PROT_WRITE, 1);
+ }
+
+ /* and let the page table pages go (at least above pmap level) */
+ vm_map_pageable(map, ptaddr, ptaddr + NBPG, TRUE);
+
+ s = splhigh();
+
+ if (p->p_stat == SRUN)
+ setrunqueue(p);
+
+ p->p_flag |= P_INMEM;
+
+ /* undo the effect of setting SLOCK above */
+ --p->p_lock;
+ splx(s);
+
+ }
+
+}
+
+int swapinreq;
+int percentactive;
+/*
+ * This swapin algorithm attempts to swap-in processes only if there
+ * is enough space for them. Of course, if a process waits for a long
+ * time, it will be swapped in anyway.
+ */
+void
+scheduler()
+{
+ register struct proc *p;
+ register int pri;
+ struct proc *pp;
+ int ppri;
+ vm_offset_t addr;
+ int lastidle, lastrun;
+ int curidle, currun;
+ int forceload;
+ int percent;
+ int ntries;
+
+ lastidle = 0;
+ lastrun = 0;
+
+loop:
+ ntries = 0;
+ vmmeter();
+
+ curidle = cp_time[CP_IDLE];
+ currun = cp_time[CP_USER] + cp_time[CP_SYS] + cp_time[CP_NICE];
+ percent = (100*(currun-lastrun)) / ( 1 + (currun-lastrun) + (curidle-lastidle));
+ lastrun = currun;
+ lastidle = curidle;
+ if( percent > 100)
+ percent = 100;
+ percentactive = percent;
+
+ if( percentactive < 25)
+ forceload = 1;
+ else
+ forceload = 0;
+
+loop1:
+ pp = NULL;
+ ppri = INT_MIN;
+ for (p = (struct proc *)allproc; p != NULL; p = p->p_next) {
+ if (p->p_stat == SRUN && (p->p_flag & P_INMEM) == 0) {
+ int mempri;
+ pri = p->p_swtime + p->p_slptime - p->p_nice * 8;
+ mempri = pri > 0 ? pri : 0;
+ /*
+ * if this process is higher priority and there is
+ * enough space, then select this process instead
+ * of the previous selection.
+ */
+ if (pri > ppri &&
+ (((cnt.v_free_count + (mempri * (4*PAGE_SIZE) / PAGE_SIZE) >= (p->p_vmspace->vm_swrss)) || (ntries > 0 && forceload)))) {
+ pp = p;
+ ppri = pri;
+ }
+ }
+ }
+
+ if ((pp == NULL) && (ntries == 0) && forceload) {
+ ++ntries;
+ goto loop1;
+ }
+
+ /*
+ * Nothing to do, back to sleep
+ */
+ if ((p = pp) == NULL) {
+ tsleep((caddr_t)&proc0, PVM, "sched", 0);
+ goto loop;
+ }
+
+ /*
+ * We would like to bring someone in. (only if there is space).
+ */
+/*
+ printf("swapin: %d, free: %d, res: %d, min: %d\n",
+ p->p_pid, cnt.v_free_count, cnt.v_free_reserved, cnt.v_free_min);
+*/
+ (void) splhigh();
+ if ((forceload && (cnt.v_free_count > (cnt.v_free_reserved + UPAGES + 1))) ||
+ (cnt.v_free_count >= cnt.v_free_min)) {
+ spl0();
+ faultin(p);
+ p->p_swtime = 0;
+ goto loop;
+ }
+ /*
+ * log the memory shortage
+ */
+ swapinreq += p->p_vmspace->vm_swrss;
+ /*
+ * Not enough memory, jab the pageout daemon and wait til the
+ * coast is clear.
+ */
+ if( cnt.v_free_count < cnt.v_free_min) {
+ VM_WAIT;
+ } else {
+ tsleep((caddr_t)&proc0, PVM, "sched", 0);
+ }
+ (void) spl0();
+ goto loop;
+}
+
+#define swappable(p) \
+ (((p)->p_lock == 0) && \
+ ((p)->p_flag & (P_TRACED|P_NOSWAP|P_SYSTEM|P_INMEM|P_WEXIT|P_PHYSIO)) == P_INMEM)
+
+extern int vm_pageout_free_min;
+/*
+ * Swapout is driven by the pageout daemon. Very simple, we find eligible
+ * procs and unwire their u-areas. We try to always "swap" at least one
+ * process in case we need the room for a swapin.
+ * If any procs have been sleeping/stopped for at least maxslp seconds,
+ * they are swapped. Else, we swap the longest-sleeping or stopped process,
+ * if any, otherwise the longest-resident process.
+ */
+void
+swapout_threads()
+{
+ register struct proc *p;
+ struct proc *outp, *outp2;
+ int outpri, outpri2;
+ int tpri;
+ int didswap = 0;
+ int swapneeded = swapinreq;
+ extern int maxslp;
+ int runnablenow;
+ int s;
+
+swapmore:
+ runnablenow = 0;
+ outp = outp2 = NULL;
+ outpri = outpri2 = INT_MIN;
+ for (p = (struct proc *)allproc; p != NULL; p = p->p_next) {
+ if (!swappable(p))
+ continue;
+ switch (p->p_stat) {
+ case SRUN:
+ ++runnablenow;
+ /*
+ * count the process as being in a runnable state
+ */
+ if ((tpri = p->p_swtime + p->p_nice * 8) > outpri2) {
+ outp2 = p;
+ outpri2 = tpri;
+ }
+ continue;
+
+ case SSLEEP:
+ case SSTOP:
+ /*
+ * do not swapout a process that is waiting for VM datastructures
+ * there is a possible deadlock.
+ */
+ if (!lock_try_write( &p->p_vmspace->vm_map.lock)) {
+ continue;
+ }
+ vm_map_unlock( &p->p_vmspace->vm_map);
+ if (p->p_slptime > maxslp) {
+ swapout(p);
+ didswap++;
+ } else if ((tpri = p->p_slptime + p->p_nice * 8) > outpri) {
+ outp = p;
+ outpri = tpri ;
+ }
+ continue;
+ }
+ }
+ /*
+ * We swapout only if there are more than two runnable processes or if
+ * another process needs some space to swapin.
+ */
+ if ((swapinreq || ((percentactive > 90) && (runnablenow > 2))) &&
+ (((cnt.v_free_count + cnt.v_inactive_count) <= (cnt.v_free_target + cnt.v_inactive_target)) ||
+ (cnt.v_free_count < cnt.v_free_min))) {
+ if ((p = outp) == 0) {
+ p = outp2;
+ }
+
+ if (p) {
+ swapout(p);
+ didswap = 1;
+ }
+ }
+
+ /*
+ * if we previously had found a process to swapout, and we need to swapout
+ * more then try again.
+ */
+#if 0
+ if( p && swapinreq)
+ goto swapmore;
+#endif
+
+ /*
+ * If we swapped something out, and another process needed memory,
+ * then wakeup the sched process.
+ */
+ if (didswap) {
+ if (swapneeded)
+ wakeup((caddr_t)&proc0);
+ swapinreq = 0;
+ }
+}
+
+void
+swapout(p)
+ register struct proc *p;
+{
+ vm_offset_t addr;
+ struct pmap *pmap = &p->p_vmspace->vm_pmap;
+ vm_map_t map = &p->p_vmspace->vm_map;
+ vm_offset_t ptaddr;
+ int i;
+
+ ++p->p_stats->p_ru.ru_nswap;
+ /*
+ * remember the process resident count
+ */
+ p->p_vmspace->vm_swrss =
+ p->p_vmspace->vm_pmap.pm_stats.resident_count;
+ /*
+ * and decrement the amount of needed space
+ */
+ swapinreq -= min(swapinreq, p->p_vmspace->vm_pmap.pm_stats.resident_count);
+
+ (void) splhigh();
+ p->p_flag &= ~P_INMEM;
+ if (p->p_stat == SRUN)
+ remrq(p);
+ (void) spl0();
+
+ ++p->p_lock;
+/* let the upages be paged */
+ pmap_remove(vm_map_pmap(kernel_map),
+ (vm_offset_t) p->p_addr, ((vm_offset_t) p->p_addr) + UPAGES * NBPG);
+
+ vm_map_pageable(map, (vm_offset_t) kstack,
+ (vm_offset_t) kstack + UPAGES * NBPG, TRUE);
+
+ --p->p_lock;
+ p->p_swtime = 0;
+}
+
+/*
+ * The rest of these routines fake thread handling
+ */
+
+#ifndef assert_wait
+void
+assert_wait(event, ruptible)
+ int event;
+ boolean_t ruptible;
+{
+#ifdef lint
+ ruptible++;
+#endif
+ curproc->p_thread = event;
+}
+#endif
+
+void
+thread_block(char *msg)
+{
+ if (curproc->p_thread)
+ tsleep((caddr_t)curproc->p_thread, PVM, msg, 0);
+}
+
+
+void
+thread_sleep_(event, lock, wmesg)
+ int event;
+ simple_lock_t lock;
+ char *wmesg;
+{
+
+ curproc->p_thread = event;
+ simple_unlock(lock);
+ if (curproc->p_thread) {
+ tsleep((caddr_t)event, PVM, wmesg, 0);
+ }
+}
+
+#ifndef thread_wakeup
+void
+thread_wakeup(event)
+ int event;
+{
+ wakeup((caddr_t)event);
+}
+#endif
+
+/*
+ * DEBUG stuff
+ */
+
+int indent = 0;
+
+#include <machine/stdarg.h> /* see subr_prf.c */
+
+/*ARGSUSED2*/
+void
+#if __STDC__
+iprintf(const char *fmt, ...)
+#else
+iprintf(fmt /* , va_alist */)
+ char *fmt;
+ /* va_dcl */
+#endif
+{
+ register int i;
+ va_list ap;
+
+ for (i = indent; i >= 8; i -= 8)
+ printf("\t");
+ while (--i >= 0)
+ printf(" ");
+ va_start(ap, fmt);
+ printf("%r", fmt, ap);
+ va_end(ap);
+}
diff --git a/sys/vm/vm_inherit.h b/sys/vm/vm_inherit.h
new file mode 100644
index 0000000..455f91c
--- /dev/null
+++ b/sys/vm/vm_inherit.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_inherit.h 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Virtual memory map inheritance definitions.
+ */
+
+#ifndef _VM_INHERIT_
+#define _VM_INHERIT_
+
+/*
+ * Enumeration of valid values for vm_inherit_t.
+ */
+
+#define VM_INHERIT_SHARE ((vm_inherit_t) 0) /* share with child */
+#define VM_INHERIT_COPY ((vm_inherit_t) 1) /* copy into child */
+#define VM_INHERIT_NONE ((vm_inherit_t) 2) /* absent from child */
+#define VM_INHERIT_DONATE_COPY ((vm_inherit_t) 3) /* copy and delete */
+
+#define VM_INHERIT_DEFAULT VM_INHERIT_COPY
+
+#endif /* _VM_INHERIT_ */
diff --git a/sys/vm/vm_init.c b/sys/vm/vm_init.c
new file mode 100644
index 0000000..a0eac70
--- /dev/null
+++ b/sys/vm/vm_init.c
@@ -0,0 +1,105 @@
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_init.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Initialize the Virtual Memory subsystem.
+ */
+
+#include <sys/param.h>
+
+#include <vm/vm.h>
+#include <vm/vm_page.h>
+#include <vm/vm_kern.h>
+
+/*
+ * vm_init initializes the virtual memory system.
+ * This is done only by the first cpu up.
+ *
+ * The start and end address of physical memory is passed in.
+ */
+
+void
+vm_mem_init()
+{
+ extern vm_offset_t avail_start, avail_end;
+ extern vm_offset_t virtual_avail, virtual_end;
+
+ /*
+ * Initializes resident memory structures.
+ * From here on, all physical memory is accounted for,
+ * and we use only virtual addresses.
+ */
+
+ vm_set_page_size();
+ virtual_avail = vm_page_startup(avail_start, avail_end, virtual_avail);
+ /*
+ * Initialize other VM packages
+ */
+ vm_object_init(virtual_end - VM_MIN_KERNEL_ADDRESS);
+ vm_map_startup();
+ kmem_init(virtual_avail, virtual_end);
+ pmap_init(avail_start, avail_end);
+ vm_pager_init();
+}
diff --git a/sys/vm/vm_kern.c b/sys/vm/vm_kern.c
new file mode 100644
index 0000000..55a0949
--- /dev/null
+++ b/sys/vm/vm_kern.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_kern.c 8.3 (Berkeley) 1/12/94
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Kernel memory management.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <vm/vm.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pageout.h>
+#include <vm/vm_kern.h>
+
+/*
+ * kmem_alloc_pageable:
+ *
+ * Allocate pageable memory to the kernel's address map.
+ * map must be "kernel_map" below.
+ */
+
+vm_offset_t kmem_alloc_pageable(map, size)
+ vm_map_t map;
+ register vm_size_t size;
+{
+ vm_offset_t addr;
+ register int result;
+
+#if 0
+ if (map != kernel_map)
+ panic("kmem_alloc_pageable: not called with kernel_map");
+#endif
+
+ size = round_page(size);
+
+ addr = vm_map_min(map);
+ result = vm_map_find(map, NULL, (vm_offset_t) 0,
+ &addr, size, TRUE);
+ if (result != KERN_SUCCESS) {
+ return(0);
+ }
+
+ return(addr);
+}
+
+/*
+ * Allocate wired-down memory in the kernel's address map
+ * or a submap.
+ */
+vm_offset_t kmem_alloc(map, size)
+ register vm_map_t map;
+ register vm_size_t size;
+{
+ vm_offset_t addr;
+ register vm_offset_t offset;
+ extern vm_object_t kernel_object;
+ vm_offset_t i;
+
+ size = round_page(size);
+
+ /*
+ * Use the kernel object for wired-down kernel pages.
+ * Assume that no region of the kernel object is
+ * referenced more than once.
+ */
+
+ /*
+ * Locate sufficient space in the map. This will give us the
+ * final virtual address for the new memory, and thus will tell
+ * us the offset within the kernel map.
+ */
+ vm_map_lock(map);
+ if (vm_map_findspace(map, 0, size, &addr)) {
+ vm_map_unlock(map);
+ return (0);
+ }
+ offset = addr - VM_MIN_KERNEL_ADDRESS;
+ vm_object_reference(kernel_object);
+ vm_map_insert(map, kernel_object, offset, addr, addr + size);
+ vm_map_unlock(map);
+
+ /*
+ * Guarantee that there are pages already in this object
+ * before calling vm_map_pageable. This is to prevent the
+ * following scenario:
+ *
+ * 1) Threads have swapped out, so that there is a
+ * pager for the kernel_object.
+ * 2) The kmsg zone is empty, and so we are kmem_allocing
+ * a new page for it.
+ * 3) vm_map_pageable calls vm_fault; there is no page,
+ * but there is a pager, so we call
+ * pager_data_request. But the kmsg zone is empty,
+ * so we must kmem_alloc.
+ * 4) goto 1
+ * 5) Even if the kmsg zone is not empty: when we get
+ * the data back from the pager, it will be (very
+ * stale) non-zero data. kmem_alloc is defined to
+ * return zero-filled memory.
+ *
+ * We're intentionally not activating the pages we allocate
+ * to prevent a race with page-out. vm_map_pageable will wire
+ * the pages.
+ */
+
+ vm_object_lock(kernel_object);
+ for (i = 0 ; i < size; i+= PAGE_SIZE) {
+ vm_page_t mem;
+
+ while ((mem = vm_page_alloc(kernel_object, offset+i)) == NULL) {
+ vm_object_unlock(kernel_object);
+ VM_WAIT;
+ vm_object_lock(kernel_object);
+ }
+ vm_page_zero_fill(mem);
+ mem->flags &= ~PG_BUSY;
+ }
+ vm_object_unlock(kernel_object);
+
+ /*
+ * And finally, mark the data as non-pageable.
+ */
+
+ (void) vm_map_pageable(map, (vm_offset_t) addr, addr + size, FALSE);
+
+ /*
+ * Try to coalesce the map
+ */
+
+ vm_map_simplify(map, addr);
+
+ return(addr);
+}
+
+/*
+ * kmem_free:
+ *
+ * Release a region of kernel virtual memory allocated
+ * with kmem_alloc, and return the physical pages
+ * associated with that region.
+ */
+void kmem_free(map, addr, size)
+ vm_map_t map;
+ register vm_offset_t addr;
+ vm_size_t size;
+{
+ (void) vm_map_remove(map, trunc_page(addr), round_page(addr + size));
+}
+
+/*
+ * kmem_suballoc:
+ *
+ * Allocates a map to manage a subrange
+ * of the kernel virtual address space.
+ *
+ * Arguments are as follows:
+ *
+ * parent Map to take range from
+ * size Size of range to find
+ * min, max Returned endpoints of map
+ * pageable Can the region be paged
+ */
+vm_map_t kmem_suballoc(parent, min, max, size, pageable)
+ register vm_map_t parent;
+ vm_offset_t *min, *max;
+ register vm_size_t size;
+ boolean_t pageable;
+{
+ register int ret;
+ vm_map_t result;
+
+ size = round_page(size);
+
+ *min = (vm_offset_t) vm_map_min(parent);
+ ret = vm_map_find(parent, NULL, (vm_offset_t) 0,
+ min, size, TRUE);
+ if (ret != KERN_SUCCESS) {
+ printf("kmem_suballoc: bad status return of %d.\n", ret);
+ panic("kmem_suballoc");
+ }
+ *max = *min + size;
+ pmap_reference(vm_map_pmap(parent));
+ result = vm_map_create(vm_map_pmap(parent), *min, *max, pageable);
+ if (result == NULL)
+ panic("kmem_suballoc: cannot create submap");
+ if ((ret = vm_map_submap(parent, *min, *max, result)) != KERN_SUCCESS)
+ panic("kmem_suballoc: unable to change range to submap");
+ return(result);
+}
+
+/*
+ * Allocate wired-down memory in the kernel's address map for the higher
+ * level kernel memory allocator (kern/kern_malloc.c). We cannot use
+ * kmem_alloc() because we may need to allocate memory at interrupt
+ * level where we cannot block (canwait == FALSE).
+ *
+ * This routine has its own private kernel submap (kmem_map) and object
+ * (kmem_object). This, combined with the fact that only malloc uses
+ * this routine, ensures that we will never block in map or object waits.
+ *
+ * Note that this still only works in a uni-processor environment and
+ * when called at splhigh().
+ *
+ * We don't worry about expanding the map (adding entries) since entries
+ * for wired maps are statically allocated.
+ */
+vm_offset_t
+kmem_malloc(map, size, canwait)
+ register vm_map_t map;
+ register vm_size_t size;
+ boolean_t canwait;
+{
+ register vm_offset_t offset, i;
+ vm_map_entry_t entry;
+ vm_offset_t addr;
+ vm_page_t m;
+ extern vm_object_t kmem_object;
+
+ if (map != kmem_map && map != mb_map)
+ panic("kern_malloc_alloc: map != {kmem,mb}_map");
+
+ size = round_page(size);
+ addr = vm_map_min(map);
+
+ /*
+ * Locate sufficient space in the map. This will give us the
+ * final virtual address for the new memory, and thus will tell
+ * us the offset within the kernel map.
+ */
+ vm_map_lock(map);
+ if (vm_map_findspace(map, 0, size, &addr)) {
+ vm_map_unlock(map);
+#if 0
+ if (canwait) /* XXX should wait */
+ panic("kmem_malloc: %s too small",
+ map == kmem_map ? "kmem_map" : "mb_map");
+#endif
+ if (canwait)
+ panic("kmem_malloc: map too small");
+ return (0);
+ }
+ offset = addr - vm_map_min(kmem_map);
+ vm_object_reference(kmem_object);
+ vm_map_insert(map, kmem_object, offset, addr, addr + size);
+
+ /*
+ * If we can wait, just mark the range as wired
+ * (will fault pages as necessary).
+ */
+ if (canwait) {
+ vm_map_unlock(map);
+ (void) vm_map_pageable(map, (vm_offset_t) addr, addr + size,
+ FALSE);
+ vm_map_simplify(map, addr);
+ return(addr);
+ }
+
+ /*
+ * If we cannot wait then we must allocate all memory up front,
+ * pulling it off the active queue to prevent pageout.
+ */
+ vm_object_lock(kmem_object);
+ for (i = 0; i < size; i += PAGE_SIZE) {
+ m = vm_page_alloc(kmem_object, offset + i);
+
+ /*
+ * Ran out of space, free everything up and return.
+ * Don't need to lock page queues here as we know
+ * that the pages we got aren't on any queues.
+ */
+ if (m == NULL) {
+ while (i != 0) {
+ i -= PAGE_SIZE;
+ m = vm_page_lookup(kmem_object, offset + i);
+ vm_page_free(m);
+ }
+ vm_object_unlock(kmem_object);
+ vm_map_delete(map, addr, addr + size);
+ vm_map_unlock(map);
+ return(0);
+ }
+#if 0
+ vm_page_zero_fill(m);
+#endif
+ m->flags &= ~PG_BUSY;
+ }
+ vm_object_unlock(kmem_object);
+
+ /*
+ * Mark map entry as non-pageable.
+ * Assert: vm_map_insert() will never be able to extend the previous
+ * entry so there will be a new entry exactly corresponding to this
+ * address range and it will have wired_count == 0.
+ */
+ if (!vm_map_lookup_entry(map, addr, &entry) ||
+ entry->start != addr || entry->end != addr + size ||
+ entry->wired_count)
+ panic("kmem_malloc: entry not found or misaligned");
+ entry->wired_count++;
+
+ /*
+ * Loop thru pages, entering them in the pmap.
+ * (We cannot add them to the wired count without
+ * wrapping the vm_page_queue_lock in splimp...)
+ */
+ for (i = 0; i < size; i += PAGE_SIZE) {
+ vm_object_lock(kmem_object);
+ m = vm_page_lookup(kmem_object, offset + i);
+ vm_object_unlock(kmem_object);
+ pmap_enter(map->pmap, addr + i, VM_PAGE_TO_PHYS(m),
+ VM_PROT_DEFAULT, TRUE);
+ }
+ vm_map_unlock(map);
+
+ vm_map_simplify(map, addr);
+ return(addr);
+}
+
+/*
+ * kmem_alloc_wait
+ *
+ * Allocates pageable memory from a sub-map of the kernel. If the submap
+ * has no room, the caller sleeps waiting for more memory in the submap.
+ *
+ */
+vm_offset_t kmem_alloc_wait(map, size)
+ vm_map_t map;
+ vm_size_t size;
+{
+ vm_offset_t addr;
+
+ size = round_page(size);
+
+ for (;;) {
+ /*
+ * To make this work for more than one map,
+ * use the map's lock to lock out sleepers/wakers.
+ */
+ vm_map_lock(map);
+ if (vm_map_findspace(map, 0, size, &addr) == 0)
+ break;
+ /* no space now; see if we can ever get space */
+ if (vm_map_max(map) - vm_map_min(map) < size) {
+ vm_map_unlock(map);
+ return (0);
+ }
+ assert_wait((int)map, TRUE);
+ vm_map_unlock(map);
+ thread_block("kmaw");
+ }
+ vm_map_insert(map, NULL, (vm_offset_t)0, addr, addr + size);
+ vm_map_unlock(map);
+ return (addr);
+}
+
+/*
+ * kmem_free_wakeup
+ *
+ * Returns memory to a submap of the kernel, and wakes up any threads
+ * waiting for memory in that map.
+ */
+void kmem_free_wakeup(map, addr, size)
+ vm_map_t map;
+ vm_offset_t addr;
+ vm_size_t size;
+{
+ vm_map_lock(map);
+ (void) vm_map_delete(map, trunc_page(addr), round_page(addr + size));
+ thread_wakeup((int)map);
+ vm_map_unlock(map);
+}
+
+/*
+ * Create the kernel map; insert a mapping covering kernel text, data, bss,
+ * and all space allocated thus far (`boostrap' data). The new map will thus
+ * map the range between VM_MIN_KERNEL_ADDRESS and `start' as allocated, and
+ * the range between `start' and `end' as free.
+ */
+void kmem_init(start, end)
+ vm_offset_t start, end;
+{
+ register vm_map_t m;
+
+ m = vm_map_create(kernel_pmap, VM_MIN_KERNEL_ADDRESS, end, FALSE);
+ vm_map_lock(m);
+ /* N.B.: cannot use kgdb to debug, starting with this assignment ... */
+ kernel_map = m;
+ (void) vm_map_insert(m, NULL, (vm_offset_t)0,
+ VM_MIN_KERNEL_ADDRESS, start);
+ /* ... and ending with the completion of the above `insert' */
+ vm_map_unlock(m);
+}
diff --git a/sys/vm/vm_kern.h b/sys/vm/vm_kern.h
new file mode 100644
index 0000000..c032560
--- /dev/null
+++ b/sys/vm/vm_kern.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_kern.h 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/* Kernel memory management definitions. */
+
+vm_map_t buffer_map;
+vm_map_t kernel_map;
+vm_map_t kmem_map;
+vm_map_t mb_map;
+vm_map_t io_map;
+vm_map_t clean_map;
+vm_map_t pager_map;
+vm_map_t phys_map;
diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c
new file mode 100644
index 0000000..ffffa96
--- /dev/null
+++ b/sys/vm/vm_map.c
@@ -0,0 +1,2681 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_map.c 8.3 (Berkeley) 1/12/94
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Virtual memory mapping module.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
+#include <vm/vm_page.h>
+#include <vm/vm_object.h>
+#include <vm/vm_kern.h>
+
+/*
+ * Virtual memory maps provide for the mapping, protection,
+ * and sharing of virtual memory objects. In addition,
+ * this module provides for an efficient virtual copy of
+ * memory from one map to another.
+ *
+ * Synchronization is required prior to most operations.
+ *
+ * Maps consist of an ordered doubly-linked list of simple
+ * entries; a single hint is used to speed up lookups.
+ *
+ * In order to properly represent the sharing of virtual
+ * memory regions among maps, the map structure is bi-level.
+ * Top-level ("address") maps refer to regions of sharable
+ * virtual memory. These regions are implemented as
+ * ("sharing") maps, which then refer to the actual virtual
+ * memory objects. When two address maps "share" memory,
+ * their top-level maps both have references to the same
+ * sharing map. When memory is virtual-copied from one
+ * address map to another, the references in the sharing
+ * maps are actually copied -- no copying occurs at the
+ * virtual memory object level.
+ *
+ * Since portions of maps are specified by start/end addreses,
+ * which may not align with existing map entries, all
+ * routines merely "clip" entries to these start/end values.
+ * [That is, an entry is split into two, bordering at a
+ * start or end value.] Note that these clippings may not
+ * always be necessary (as the two resulting entries are then
+ * not changed); however, the clipping is done for convenience.
+ * No attempt is currently made to "glue back together" two
+ * abutting entries.
+ *
+ * As mentioned above, virtual copy operations are performed
+ * by copying VM object references from one sharing map to
+ * another, and then marking both regions as copy-on-write.
+ * It is important to note that only one writeable reference
+ * to a VM object region exists in any map -- this means that
+ * shadow object creation can be delayed until a write operation
+ * occurs.
+ */
+
+/*
+ * vm_map_startup:
+ *
+ * Initialize the vm_map module. Must be called before
+ * any other vm_map routines.
+ *
+ * Map and entry structures are allocated from the general
+ * purpose memory pool with some exceptions:
+ *
+ * - The kernel map and kmem submap are allocated statically.
+ * - Kernel map entries are allocated out of a static pool.
+ *
+ * These restrictions are necessary since malloc() uses the
+ * maps and requires map entries.
+ */
+
+vm_offset_t kentry_data;
+vm_size_t kentry_data_size;
+vm_map_entry_t kentry_free;
+vm_map_t kmap_free;
+
+int kentry_count;
+vm_map_t kmap_free;
+static vm_offset_t mapvm=0;
+static int mapvmpgcnt=0;
+
+static void _vm_map_clip_end __P((vm_map_t, vm_map_entry_t, vm_offset_t));
+static void _vm_map_clip_start __P((vm_map_t, vm_map_entry_t, vm_offset_t));
+
+void vm_map_startup()
+{
+ register int i;
+ register vm_map_entry_t mep;
+ vm_map_t mp;
+
+ /*
+ * Static map structures for allocation before initialization of
+ * kernel map or kmem map. vm_map_create knows how to deal with them.
+ */
+ kmap_free = mp = (vm_map_t) kentry_data;
+ i = MAX_KMAP;
+ while (--i > 0) {
+ mp->header.next = (vm_map_entry_t) (mp + 1);
+ mp++;
+ }
+ mp++->header.next = NULL;
+
+ /*
+ * Form a free list of statically allocated kernel map entries
+ * with the rest.
+ */
+ kentry_free = mep = (vm_map_entry_t) mp;
+ i = (kentry_data_size - MAX_KMAP * sizeof *mp) / sizeof *mep;
+ while (--i > 0) {
+ mep->next = mep + 1;
+ mep++;
+ }
+ mep->next = NULL;
+}
+
+/*
+ * Allocate a vmspace structure, including a vm_map and pmap,
+ * and initialize those structures. The refcnt is set to 1.
+ * The remaining fields must be initialized by the caller.
+ */
+struct vmspace *
+vmspace_alloc(min, max, pageable)
+ vm_offset_t min, max;
+ int pageable;
+{
+ register struct vmspace *vm;
+
+ MALLOC(vm, struct vmspace *, sizeof(struct vmspace), M_VMMAP, M_WAITOK);
+ bzero(vm, (caddr_t) &vm->vm_startcopy - (caddr_t) vm);
+ vm_map_init(&vm->vm_map, min, max, pageable);
+ pmap_pinit(&vm->vm_pmap);
+ vm->vm_map.pmap = &vm->vm_pmap; /* XXX */
+ vm->vm_refcnt = 1;
+ return (vm);
+}
+
+void
+vmspace_free(vm)
+ register struct vmspace *vm;
+{
+
+ if (--vm->vm_refcnt == 0) {
+ /*
+ * Lock the map, to wait out all other references to it.
+ * Delete all of the mappings and pages they hold,
+ * then call the pmap module to reclaim anything left.
+ */
+ vm_map_lock(&vm->vm_map);
+ (void) vm_map_delete(&vm->vm_map, vm->vm_map.min_offset,
+ vm->vm_map.max_offset);
+ pmap_release(&vm->vm_pmap);
+ FREE(vm, M_VMMAP);
+ }
+}
+
+/*
+ * vm_map_create:
+ *
+ * Creates and returns a new empty VM map with
+ * the given physical map structure, and having
+ * the given lower and upper address bounds.
+ */
+vm_map_t vm_map_create(pmap, min, max, pageable)
+ pmap_t pmap;
+ vm_offset_t min, max;
+ boolean_t pageable;
+{
+ register vm_map_t result;
+ extern vm_map_t kmem_map;
+
+ if (kmem_map == NULL) {
+ result = kmap_free;
+ kmap_free = (vm_map_t) result->header.next;
+ if (result == NULL)
+ panic("vm_map_create: out of maps");
+ } else
+ MALLOC(result, vm_map_t, sizeof(struct vm_map),
+ M_VMMAP, M_WAITOK);
+
+ vm_map_init(result, min, max, pageable);
+ result->pmap = pmap;
+ return(result);
+}
+
+/*
+ * Initialize an existing vm_map structure
+ * such as that in the vmspace structure.
+ * The pmap is set elsewhere.
+ */
+void
+vm_map_init(map, min, max, pageable)
+ register struct vm_map *map;
+ vm_offset_t min, max;
+ boolean_t pageable;
+{
+ map->header.next = map->header.prev = &map->header;
+ map->nentries = 0;
+ map->size = 0;
+ map->ref_count = 1;
+ map->is_main_map = TRUE;
+ map->min_offset = min;
+ map->max_offset = max;
+ map->entries_pageable = pageable;
+ map->first_free = &map->header;
+ map->hint = &map->header;
+ map->timestamp = 0;
+ lock_init(&map->lock, TRUE);
+ simple_lock_init(&map->ref_lock);
+ simple_lock_init(&map->hint_lock);
+}
+
+/*
+ * vm_map_entry_create: [ internal use only ]
+ *
+ * Allocates a VM map entry for insertion.
+ * No entry fields are filled in. This routine is
+ */
+static struct vm_map_entry *mappool;
+static int mappoolcnt;
+void vm_map_entry_dispose(vm_map_t map, vm_map_entry_t entry);
+
+vm_map_entry_t
+vm_map_entry_create(map)
+ vm_map_t map;
+{
+ vm_map_entry_t entry;
+ int s;
+ int i;
+#define KENTRY_LOW_WATER 64
+#define MAPENTRY_LOW_WATER 64
+
+ /*
+ * This is a *very* nasty (and sort of incomplete) hack!!!!
+ */
+ if (kentry_count < KENTRY_LOW_WATER) {
+ if (mapvmpgcnt && mapvm) {
+ vm_page_t m;
+ if (m = vm_page_alloc(kmem_object, mapvm-vm_map_min(kmem_map))) {
+ int newentries;
+ newentries = (NBPG/sizeof (struct vm_map_entry));
+ vm_page_wire(m);
+ m->flags &= ~PG_BUSY;
+ pmap_enter(vm_map_pmap(kmem_map), mapvm,
+ VM_PAGE_TO_PHYS(m), VM_PROT_DEFAULT, 1);
+
+ entry = (vm_map_entry_t) mapvm;
+ mapvm += NBPG;
+ --mapvmpgcnt;
+
+ for (i = 0; i < newentries; i++) {
+ vm_map_entry_dispose(kernel_map, entry);
+ entry++;
+ }
+ }
+ }
+ }
+
+ if (map == kernel_map || map == kmem_map || map == pager_map) {
+
+ if (entry = kentry_free) {
+ kentry_free = entry->next;
+ --kentry_count;
+ return entry;
+ }
+
+ if (entry = mappool) {
+ mappool = entry->next;
+ --mappoolcnt;
+ return entry;
+ }
+
+ } else {
+ if (entry = mappool) {
+ mappool = entry->next;
+ --mappoolcnt;
+ return entry;
+ }
+
+ MALLOC(entry, vm_map_entry_t, sizeof(struct vm_map_entry),
+ M_VMMAPENT, M_WAITOK);
+ }
+dopanic:
+ if (entry == NULL)
+ panic("vm_map_entry_create: out of map entries");
+
+ return(entry);
+}
+
+/*
+ * vm_map_entry_dispose: [ internal use only ]
+ *
+ * Inverse of vm_map_entry_create.
+ */
+void
+vm_map_entry_dispose(map, entry)
+ vm_map_t map;
+ vm_map_entry_t entry;
+{
+ extern vm_map_t kernel_map, kmem_map, pager_map;
+ int s;
+
+ if (map == kernel_map || map == kmem_map || map == pager_map ||
+ kentry_count < KENTRY_LOW_WATER) {
+ entry->next = kentry_free;
+ kentry_free = entry;
+ ++kentry_count;
+ } else {
+ if (mappoolcnt < MAPENTRY_LOW_WATER) {
+ entry->next = mappool;
+ mappool = entry;
+ ++mappoolcnt;
+ return;
+ }
+
+ FREE(entry, M_VMMAPENT);
+ }
+}
+
+/*
+ * vm_map_entry_{un,}link:
+ *
+ * Insert/remove entries from maps.
+ */
+#define vm_map_entry_link(map, after_where, entry) \
+ { \
+ (map)->nentries++; \
+ (entry)->prev = (after_where); \
+ (entry)->next = (after_where)->next; \
+ (entry)->prev->next = (entry); \
+ (entry)->next->prev = (entry); \
+ }
+#define vm_map_entry_unlink(map, entry) \
+ { \
+ (map)->nentries--; \
+ (entry)->next->prev = (entry)->prev; \
+ (entry)->prev->next = (entry)->next; \
+ }
+
+/*
+ * vm_map_reference:
+ *
+ * Creates another valid reference to the given map.
+ *
+ */
+void vm_map_reference(map)
+ register vm_map_t map;
+{
+ if (map == NULL)
+ return;
+
+ simple_lock(&map->ref_lock);
+ map->ref_count++;
+ simple_unlock(&map->ref_lock);
+}
+
+/*
+ * vm_map_deallocate:
+ *
+ * Removes a reference from the specified map,
+ * destroying it if no references remain.
+ * The map should not be locked.
+ */
+void vm_map_deallocate(map)
+ register vm_map_t map;
+{
+ register int c;
+
+ if (map == NULL)
+ return;
+
+ simple_lock(&map->ref_lock);
+ c = --map->ref_count;
+ simple_unlock(&map->ref_lock);
+
+ if (c > 0) {
+ return;
+ }
+
+ /*
+ * Lock the map, to wait out all other references
+ * to it.
+ */
+
+ vm_map_lock(map);
+
+ (void) vm_map_delete(map, map->min_offset, map->max_offset);
+
+ pmap_destroy(map->pmap);
+
+ FREE(map, M_VMMAP);
+}
+
+/*
+ * vm_map_insert:
+ *
+ * Inserts the given whole VM object into the target
+ * map at the specified address range. The object's
+ * size should match that of the address range.
+ *
+ * Requires that the map be locked, and leaves it so.
+ */
+int
+vm_map_insert(map, object, offset, start, end)
+ vm_map_t map;
+ vm_object_t object;
+ vm_offset_t offset;
+ vm_offset_t start;
+ vm_offset_t end;
+{
+ register vm_map_entry_t new_entry;
+ register vm_map_entry_t prev_entry;
+ vm_map_entry_t temp_entry;
+
+ /*
+ * Check that the start and end points are not bogus.
+ */
+
+ if ((start < map->min_offset) || (end > map->max_offset) ||
+ (start >= end))
+ return(KERN_INVALID_ADDRESS);
+
+ /*
+ * Find the entry prior to the proposed
+ * starting address; if it's part of an
+ * existing entry, this range is bogus.
+ */
+
+ if (vm_map_lookup_entry(map, start, &temp_entry))
+ return(KERN_NO_SPACE);
+
+ prev_entry = temp_entry;
+
+ /*
+ * Assert that the next entry doesn't overlap the
+ * end point.
+ */
+
+ if ((prev_entry->next != &map->header) &&
+ (prev_entry->next->start < end))
+ return(KERN_NO_SPACE);
+
+ /*
+ * See if we can avoid creating a new entry by
+ * extending one of our neighbors.
+ */
+
+ if (object == NULL) {
+ if ((prev_entry != &map->header) &&
+ (prev_entry->end == start) &&
+ (map->is_main_map) &&
+ (prev_entry->is_a_map == FALSE) &&
+ (prev_entry->is_sub_map == FALSE) &&
+ (prev_entry->inheritance == VM_INHERIT_DEFAULT) &&
+ (prev_entry->protection == VM_PROT_DEFAULT) &&
+ (prev_entry->max_protection == VM_PROT_DEFAULT) &&
+ (prev_entry->wired_count == 0)) {
+
+ if (vm_object_coalesce(prev_entry->object.vm_object,
+ NULL,
+ prev_entry->offset,
+ (vm_offset_t) 0,
+ (vm_size_t)(prev_entry->end
+ - prev_entry->start),
+ (vm_size_t)(end - prev_entry->end))) {
+ /*
+ * Coalesced the two objects - can extend
+ * the previous map entry to include the
+ * new range.
+ */
+ map->size += (end - prev_entry->end);
+ prev_entry->end = end;
+ return(KERN_SUCCESS);
+ }
+ }
+ }
+
+ /*
+ * Create a new entry
+ */
+
+ new_entry = vm_map_entry_create(map);
+ new_entry->start = start;
+ new_entry->end = end;
+
+ new_entry->is_a_map = FALSE;
+ new_entry->is_sub_map = FALSE;
+ new_entry->object.vm_object = object;
+ new_entry->offset = offset;
+
+ new_entry->copy_on_write = FALSE;
+ new_entry->needs_copy = FALSE;
+
+ if (map->is_main_map) {
+ new_entry->inheritance = VM_INHERIT_DEFAULT;
+ new_entry->protection = VM_PROT_DEFAULT;
+ new_entry->max_protection = VM_PROT_DEFAULT;
+ new_entry->wired_count = 0;
+ }
+
+ /*
+ * Insert the new entry into the list
+ */
+
+ vm_map_entry_link(map, prev_entry, new_entry);
+ map->size += new_entry->end - new_entry->start;
+
+ /*
+ * Update the free space hint
+ */
+
+ if ((map->first_free == prev_entry) && (prev_entry->end >= new_entry->start))
+ map->first_free = new_entry;
+
+ return(KERN_SUCCESS);
+}
+
+/*
+ * SAVE_HINT:
+ *
+ * Saves the specified entry as the hint for
+ * future lookups. Performs necessary interlocks.
+ */
+#define SAVE_HINT(map,value) \
+ simple_lock(&(map)->hint_lock); \
+ (map)->hint = (value); \
+ simple_unlock(&(map)->hint_lock);
+
+/*
+ * vm_map_lookup_entry: [ internal use only ]
+ *
+ * Finds the map entry containing (or
+ * immediately preceding) the specified address
+ * in the given map; the entry is returned
+ * in the "entry" parameter. The boolean
+ * result indicates whether the address is
+ * actually contained in the map.
+ */
+boolean_t vm_map_lookup_entry(map, address, entry)
+ register vm_map_t map;
+ register vm_offset_t address;
+ vm_map_entry_t *entry; /* OUT */
+{
+ register vm_map_entry_t cur;
+ register vm_map_entry_t last;
+
+ /*
+ * Start looking either from the head of the
+ * list, or from the hint.
+ */
+
+ simple_lock(&map->hint_lock);
+ cur = map->hint;
+ simple_unlock(&map->hint_lock);
+
+ if (cur == &map->header)
+ cur = cur->next;
+
+ if (address >= cur->start) {
+ /*
+ * Go from hint to end of list.
+ *
+ * But first, make a quick check to see if
+ * we are already looking at the entry we
+ * want (which is usually the case).
+ * Note also that we don't need to save the hint
+ * here... it is the same hint (unless we are
+ * at the header, in which case the hint didn't
+ * buy us anything anyway).
+ */
+ last = &map->header;
+ if ((cur != last) && (cur->end > address)) {
+ *entry = cur;
+ return(TRUE);
+ }
+ }
+ else {
+ /*
+ * Go from start to hint, *inclusively*
+ */
+ last = cur->next;
+ cur = map->header.next;
+ }
+
+ /*
+ * Search linearly
+ */
+
+ while (cur != last) {
+ if (cur->end > address) {
+ if (address >= cur->start) {
+ /*
+ * Save this lookup for future
+ * hints, and return
+ */
+
+ *entry = cur;
+ SAVE_HINT(map, cur);
+ return(TRUE);
+ }
+ break;
+ }
+ cur = cur->next;
+ }
+ *entry = cur->prev;
+ SAVE_HINT(map, *entry);
+ return(FALSE);
+}
+
+/*
+ * Find sufficient space for `length' bytes in the given map, starting at
+ * `start'. The map must be locked. Returns 0 on success, 1 on no space.
+ */
+int
+vm_map_findspace(map, start, length, addr)
+ register vm_map_t map;
+ register vm_offset_t start;
+ vm_size_t length;
+ vm_offset_t *addr;
+{
+ register vm_map_entry_t entry, next;
+ register vm_offset_t end;
+
+ if (start < map->min_offset)
+ start = map->min_offset;
+ if (start > map->max_offset)
+ return (1);
+
+ /*
+ * Look for the first possible address; if there's already
+ * something at this address, we have to start after it.
+ */
+ if (start == map->min_offset) {
+ if ((entry = map->first_free) != &map->header)
+ start = entry->end;
+ } else {
+ vm_map_entry_t tmp;
+ if (vm_map_lookup_entry(map, start, &tmp))
+ start = tmp->end;
+ entry = tmp;
+ }
+
+ /*
+ * Look through the rest of the map, trying to fit a new region in
+ * the gap between existing regions, or after the very last region.
+ */
+ for (;; start = (entry = next)->end) {
+ /*
+ * Find the end of the proposed new region. Be sure we didn't
+ * go beyond the end of the map, or wrap around the address;
+ * if so, we lose. Otherwise, if this is the last entry, or
+ * if the proposed new region fits before the next entry, we
+ * win.
+ */
+ end = start + length;
+ if (end > map->max_offset || end < start)
+ return (1);
+ next = entry->next;
+ if (next == &map->header || next->start >= end)
+ break;
+ }
+ SAVE_HINT(map, entry);
+ *addr = start;
+ return (0);
+}
+
+/*
+ * vm_map_find finds an unallocated region in the target address
+ * map with the given length. The search is defined to be
+ * first-fit from the specified address; the region found is
+ * returned in the same parameter.
+ *
+ */
+int
+vm_map_find(map, object, offset, addr, length, find_space)
+ vm_map_t map;
+ vm_object_t object;
+ vm_offset_t offset;
+ vm_offset_t *addr; /* IN/OUT */
+ vm_size_t length;
+ boolean_t find_space;
+{
+ register vm_offset_t start;
+ int result;
+
+ start = *addr;
+ vm_map_lock(map);
+ if (find_space) {
+ if (vm_map_findspace(map, start, length, addr)) {
+ vm_map_unlock(map);
+ return (KERN_NO_SPACE);
+ }
+ start = *addr;
+ }
+ result = vm_map_insert(map, object, offset, start, start + length);
+ vm_map_unlock(map);
+ return (result);
+}
+
+/*
+ * vm_map_simplify_entry: [ internal use only ]
+ *
+ * Simplify the given map entry by:
+ * removing extra sharing maps
+ * [XXX maybe later] merging with a neighbor
+ */
+void vm_map_simplify_entry(map, entry)
+ vm_map_t map;
+ vm_map_entry_t entry;
+{
+#ifdef lint
+ map++;
+#endif
+
+ /*
+ * If this entry corresponds to a sharing map, then
+ * see if we can remove the level of indirection.
+ * If it's not a sharing map, then it points to
+ * a VM object, so see if we can merge with either
+ * of our neighbors.
+ */
+
+ if (entry->is_sub_map)
+ return;
+ if (entry->is_a_map) {
+#if 0
+ vm_map_t my_share_map;
+ int count;
+
+ my_share_map = entry->object.share_map;
+ simple_lock(&my_share_map->ref_lock);
+ count = my_share_map->ref_count;
+ simple_unlock(&my_share_map->ref_lock);
+
+ if (count == 1) {
+ /* Can move the region from
+ * entry->start to entry->end (+ entry->offset)
+ * in my_share_map into place of entry.
+ * Later.
+ */
+ }
+#endif
+ }
+ else {
+ /*
+ * Try to merge with our neighbors.
+ *
+ * Conditions for merge are:
+ *
+ * 1. entries are adjacent.
+ * 2. both entries point to objects
+ * with null pagers.
+ *
+ * If a merge is possible, we replace the two
+ * entries with a single entry, then merge
+ * the two objects into a single object.
+ *
+ * Now, all that is left to do is write the
+ * code!
+ */
+ }
+}
+
+/*
+ * vm_map_clip_start: [ internal use only ]
+ *
+ * Asserts that the given entry begins at or after
+ * the specified address; if necessary,
+ * it splits the entry into two.
+ */
+#define vm_map_clip_start(map, entry, startaddr) \
+{ \
+ if (startaddr > entry->start) \
+ _vm_map_clip_start(map, entry, startaddr); \
+}
+
+/*
+ * This routine is called only when it is known that
+ * the entry must be split.
+ */
+static void _vm_map_clip_start(map, entry, start)
+ register vm_map_t map;
+ register vm_map_entry_t entry;
+ register vm_offset_t start;
+{
+ register vm_map_entry_t new_entry;
+
+ /*
+ * See if we can simplify this entry first
+ */
+
+ /* vm_map_simplify_entry(map, entry); */
+
+ /*
+ * Split off the front portion --
+ * note that we must insert the new
+ * entry BEFORE this one, so that
+ * this entry has the specified starting
+ * address.
+ */
+
+ new_entry = vm_map_entry_create(map);
+ *new_entry = *entry;
+
+ new_entry->end = start;
+ entry->offset += (start - entry->start);
+ entry->start = start;
+
+ vm_map_entry_link(map, entry->prev, new_entry);
+
+ if (entry->is_a_map || entry->is_sub_map)
+ vm_map_reference(new_entry->object.share_map);
+ else
+ vm_object_reference(new_entry->object.vm_object);
+}
+
+/*
+ * vm_map_clip_end: [ internal use only ]
+ *
+ * Asserts that the given entry ends at or before
+ * the specified address; if necessary,
+ * it splits the entry into two.
+ */
+
+#define vm_map_clip_end(map, entry, endaddr) \
+{ \
+ if (endaddr < entry->end) \
+ _vm_map_clip_end(map, entry, endaddr); \
+}
+
+/*
+ * This routine is called only when it is known that
+ * the entry must be split.
+ */
+static void _vm_map_clip_end(map, entry, end)
+ register vm_map_t map;
+ register vm_map_entry_t entry;
+ register vm_offset_t end;
+{
+ register vm_map_entry_t new_entry;
+
+ /*
+ * Create a new entry and insert it
+ * AFTER the specified entry
+ */
+
+ new_entry = vm_map_entry_create(map);
+ *new_entry = *entry;
+
+ new_entry->start = entry->end = end;
+ new_entry->offset += (end - entry->start);
+
+ vm_map_entry_link(map, entry, new_entry);
+
+ if (entry->is_a_map || entry->is_sub_map)
+ vm_map_reference(new_entry->object.share_map);
+ else
+ vm_object_reference(new_entry->object.vm_object);
+}
+
+/*
+ * VM_MAP_RANGE_CHECK: [ internal use only ]
+ *
+ * Asserts that the starting and ending region
+ * addresses fall within the valid range of the map.
+ */
+#define VM_MAP_RANGE_CHECK(map, start, end) \
+ { \
+ if (start < vm_map_min(map)) \
+ start = vm_map_min(map); \
+ if (end > vm_map_max(map)) \
+ end = vm_map_max(map); \
+ if (start > end) \
+ start = end; \
+ }
+
+/*
+ * vm_map_submap: [ kernel use only ]
+ *
+ * Mark the given range as handled by a subordinate map.
+ *
+ * This range must have been created with vm_map_find,
+ * and no other operations may have been performed on this
+ * range prior to calling vm_map_submap.
+ *
+ * Only a limited number of operations can be performed
+ * within this rage after calling vm_map_submap:
+ * vm_fault
+ * [Don't try vm_map_copy!]
+ *
+ * To remove a submapping, one must first remove the
+ * range from the superior map, and then destroy the
+ * submap (if desired). [Better yet, don't try it.]
+ */
+int
+vm_map_submap(map, start, end, submap)
+ register vm_map_t map;
+ register vm_offset_t start;
+ register vm_offset_t end;
+ vm_map_t submap;
+{
+ vm_map_entry_t entry;
+ register int result = KERN_INVALID_ARGUMENT;
+
+ vm_map_lock(map);
+
+ VM_MAP_RANGE_CHECK(map, start, end);
+
+ if (vm_map_lookup_entry(map, start, &entry)) {
+ vm_map_clip_start(map, entry, start);
+ }
+ else
+ entry = entry->next;
+
+ vm_map_clip_end(map, entry, end);
+
+ if ((entry->start == start) && (entry->end == end) &&
+ (!entry->is_a_map) &&
+ (entry->object.vm_object == NULL) &&
+ (!entry->copy_on_write)) {
+ entry->is_a_map = FALSE;
+ entry->is_sub_map = TRUE;
+ vm_map_reference(entry->object.sub_map = submap);
+ result = KERN_SUCCESS;
+ }
+ vm_map_unlock(map);
+
+ return(result);
+}
+
+/*
+ * vm_map_protect:
+ *
+ * Sets the protection of the specified address
+ * region in the target map. If "set_max" is
+ * specified, the maximum protection is to be set;
+ * otherwise, only the current protection is affected.
+ */
+int
+vm_map_protect(map, start, end, new_prot, set_max)
+ register vm_map_t map;
+ register vm_offset_t start;
+ register vm_offset_t end;
+ register vm_prot_t new_prot;
+ register boolean_t set_max;
+{
+ register vm_map_entry_t current;
+ vm_map_entry_t entry;
+
+ vm_map_lock(map);
+
+ VM_MAP_RANGE_CHECK(map, start, end);
+
+ if (vm_map_lookup_entry(map, start, &entry)) {
+ vm_map_clip_start(map, entry, start);
+ }
+ else
+ entry = entry->next;
+
+ /*
+ * Make a first pass to check for protection
+ * violations.
+ */
+
+ current = entry;
+ while ((current != &map->header) && (current->start < end)) {
+ if (current->is_sub_map)
+ return(KERN_INVALID_ARGUMENT);
+ if ((new_prot & current->max_protection) != new_prot) {
+ vm_map_unlock(map);
+ return(KERN_PROTECTION_FAILURE);
+ }
+
+ current = current->next;
+ }
+
+ /*
+ * Go back and fix up protections.
+ * [Note that clipping is not necessary the second time.]
+ */
+
+ current = entry;
+
+ while ((current != &map->header) && (current->start < end)) {
+ vm_prot_t old_prot;
+
+ vm_map_clip_end(map, current, end);
+
+ old_prot = current->protection;
+ if (set_max)
+ current->protection =
+ (current->max_protection = new_prot) &
+ old_prot;
+ else
+ current->protection = new_prot;
+
+ /*
+ * Update physical map if necessary.
+ * Worry about copy-on-write here -- CHECK THIS XXX
+ */
+
+ if (current->protection != old_prot) {
+
+#define MASK(entry) ((entry)->copy_on_write ? ~VM_PROT_WRITE : \
+ VM_PROT_ALL)
+#define max(a,b) ((a) > (b) ? (a) : (b))
+
+ if (current->is_a_map) {
+ vm_map_entry_t share_entry;
+ vm_offset_t share_end;
+
+ vm_map_lock(current->object.share_map);
+ (void) vm_map_lookup_entry(
+ current->object.share_map,
+ current->offset,
+ &share_entry);
+ share_end = current->offset +
+ (current->end - current->start);
+ while ((share_entry !=
+ &current->object.share_map->header) &&
+ (share_entry->start < share_end)) {
+
+ pmap_protect(map->pmap,
+ (max(share_entry->start,
+ current->offset) -
+ current->offset +
+ current->start),
+ min(share_entry->end,
+ share_end) -
+ current->offset +
+ current->start,
+ current->protection &
+ MASK(share_entry));
+
+ share_entry = share_entry->next;
+ }
+ vm_map_unlock(current->object.share_map);
+ }
+ else
+ pmap_protect(map->pmap, current->start,
+ current->end,
+ current->protection & MASK(entry));
+#undef max
+#undef MASK
+ }
+ current = current->next;
+ }
+
+ vm_map_unlock(map);
+ return(KERN_SUCCESS);
+}
+
+/*
+ * vm_map_inherit:
+ *
+ * Sets the inheritance of the specified address
+ * range in the target map. Inheritance
+ * affects how the map will be shared with
+ * child maps at the time of vm_map_fork.
+ */
+int
+vm_map_inherit(map, start, end, new_inheritance)
+ register vm_map_t map;
+ register vm_offset_t start;
+ register vm_offset_t end;
+ register vm_inherit_t new_inheritance;
+{
+ register vm_map_entry_t entry;
+ vm_map_entry_t temp_entry;
+
+ switch (new_inheritance) {
+ case VM_INHERIT_NONE:
+ case VM_INHERIT_COPY:
+ case VM_INHERIT_SHARE:
+ break;
+ default:
+ return(KERN_INVALID_ARGUMENT);
+ }
+
+ vm_map_lock(map);
+
+ VM_MAP_RANGE_CHECK(map, start, end);
+
+ if (vm_map_lookup_entry(map, start, &temp_entry)) {
+ entry = temp_entry;
+ vm_map_clip_start(map, entry, start);
+ }
+ else
+ entry = temp_entry->next;
+
+ while ((entry != &map->header) && (entry->start < end)) {
+ vm_map_clip_end(map, entry, end);
+
+ entry->inheritance = new_inheritance;
+
+ entry = entry->next;
+ }
+
+ vm_map_unlock(map);
+ return(KERN_SUCCESS);
+}
+
+/*
+ * vm_map_pageable:
+ *
+ * Sets the pageability of the specified address
+ * range in the target map. Regions specified
+ * as not pageable require locked-down physical
+ * memory and physical page maps.
+ *
+ * The map must not be locked, but a reference
+ * must remain to the map throughout the call.
+ */
+int
+vm_map_pageable(map, start, end, new_pageable)
+ register vm_map_t map;
+ register vm_offset_t start;
+ register vm_offset_t end;
+ register boolean_t new_pageable;
+{
+ register vm_map_entry_t entry;
+ vm_map_entry_t start_entry;
+ register vm_offset_t failed = 0;
+ int rv;
+
+ vm_map_lock(map);
+
+ VM_MAP_RANGE_CHECK(map, start, end);
+
+ /*
+ * Only one pageability change may take place at one
+ * time, since vm_fault assumes it will be called
+ * only once for each wiring/unwiring. Therefore, we
+ * have to make sure we're actually changing the pageability
+ * for the entire region. We do so before making any changes.
+ */
+
+ if (vm_map_lookup_entry(map, start, &start_entry) == FALSE) {
+ vm_map_unlock(map);
+ return(KERN_INVALID_ADDRESS);
+ }
+ entry = start_entry;
+
+ /*
+ * Actions are rather different for wiring and unwiring,
+ * so we have two separate cases.
+ */
+
+ if (new_pageable) {
+
+ vm_map_clip_start(map, entry, start);
+
+ /*
+ * Unwiring. First ensure that the range to be
+ * unwired is really wired down and that there
+ * are no holes.
+ */
+ while ((entry != &map->header) && (entry->start < end)) {
+
+ if (entry->wired_count == 0 ||
+ (entry->end < end &&
+ (entry->next == &map->header ||
+ entry->next->start > entry->end))) {
+ vm_map_unlock(map);
+ return(KERN_INVALID_ARGUMENT);
+ }
+ entry = entry->next;
+ }
+
+ /*
+ * Now decrement the wiring count for each region.
+ * If a region becomes completely unwired,
+ * unwire its physical pages and mappings.
+ */
+ lock_set_recursive(&map->lock);
+
+ entry = start_entry;
+ while ((entry != &map->header) && (entry->start < end)) {
+ vm_map_clip_end(map, entry, end);
+
+ entry->wired_count--;
+ if (entry->wired_count == 0)
+ vm_fault_unwire(map, entry->start, entry->end);
+
+ entry = entry->next;
+ }
+ lock_clear_recursive(&map->lock);
+ }
+
+ else {
+ /*
+ * Wiring. We must do this in two passes:
+ *
+ * 1. Holding the write lock, we create any shadow
+ * or zero-fill objects that need to be created.
+ * Then we clip each map entry to the region to be
+ * wired and increment its wiring count. We
+ * create objects before clipping the map entries
+ * to avoid object proliferation.
+ *
+ * 2. We downgrade to a read lock, and call
+ * vm_fault_wire to fault in the pages for any
+ * newly wired area (wired_count is 1).
+ *
+ * Downgrading to a read lock for vm_fault_wire avoids
+ * a possible deadlock with another thread that may have
+ * faulted on one of the pages to be wired (it would mark
+ * the page busy, blocking us, then in turn block on the
+ * map lock that we hold). Because of problems in the
+ * recursive lock package, we cannot upgrade to a write
+ * lock in vm_map_lookup. Thus, any actions that require
+ * the write lock must be done beforehand. Because we
+ * keep the read lock on the map, the copy-on-write status
+ * of the entries we modify here cannot change.
+ */
+
+ /*
+ * Pass 1.
+ */
+ while ((entry != &map->header) && (entry->start < end)) {
+ if (entry->wired_count == 0) {
+
+ /*
+ * Perform actions of vm_map_lookup that need
+ * the write lock on the map: create a shadow
+ * object for a copy-on-write region, or an
+ * object for a zero-fill region.
+ *
+ * We don't have to do this for entries that
+ * point to sharing maps, because we won't hold
+ * the lock on the sharing map.
+ */
+ if (!entry->is_a_map) {
+ if (entry->needs_copy &&
+ ((entry->protection & VM_PROT_WRITE) != 0)) {
+
+ vm_object_shadow(&entry->object.vm_object,
+ &entry->offset,
+ (vm_size_t)(entry->end
+ - entry->start));
+ entry->needs_copy = FALSE;
+ }
+ else if (entry->object.vm_object == NULL) {
+ entry->object.vm_object =
+ vm_object_allocate((vm_size_t)(entry->end
+ - entry->start));
+ entry->offset = (vm_offset_t)0;
+ }
+ }
+ }
+ vm_map_clip_start(map, entry, start);
+ vm_map_clip_end(map, entry, end);
+ entry->wired_count++;
+
+ /*
+ * Check for holes
+ */
+ if (entry->end < end &&
+ (entry->next == &map->header ||
+ entry->next->start > entry->end)) {
+ /*
+ * Found one. Object creation actions
+ * do not need to be undone, but the
+ * wired counts need to be restored.
+ */
+ while (entry != &map->header && entry->end > start) {
+ entry->wired_count--;
+ entry = entry->prev;
+ }
+ vm_map_unlock(map);
+ return(KERN_INVALID_ARGUMENT);
+ }
+ entry = entry->next;
+ }
+
+ /*
+ * Pass 2.
+ */
+
+ /*
+ * HACK HACK HACK HACK
+ *
+ * If we are wiring in the kernel map or a submap of it,
+ * unlock the map to avoid deadlocks. We trust that the
+ * kernel threads are well-behaved, and therefore will
+ * not do anything destructive to this region of the map
+ * while we have it unlocked. We cannot trust user threads
+ * to do the same.
+ *
+ * HACK HACK HACK HACK
+ */
+ if (vm_map_pmap(map) == kernel_pmap) {
+ vm_map_unlock(map); /* trust me ... */
+ }
+ else {
+ lock_set_recursive(&map->lock);
+ lock_write_to_read(&map->lock);
+ }
+
+ rv = 0;
+ entry = start_entry;
+ while (entry != &map->header && entry->start < end) {
+ /*
+ * If vm_fault_wire fails for any page we need to
+ * undo what has been done. We decrement the wiring
+ * count for those pages which have not yet been
+ * wired (now) and unwire those that have (later).
+ *
+ * XXX this violates the locking protocol on the map,
+ * needs to be fixed.
+ */
+ if (rv)
+ entry->wired_count--;
+ else if (entry->wired_count == 1) {
+ rv = vm_fault_wire(map, entry->start, entry->end);
+ if (rv) {
+ failed = entry->start;
+ entry->wired_count--;
+ }
+ }
+ entry = entry->next;
+ }
+
+ if (vm_map_pmap(map) == kernel_pmap) {
+ vm_map_lock(map);
+ }
+ else {
+ lock_clear_recursive(&map->lock);
+ }
+ if (rv) {
+ vm_map_unlock(map);
+ (void) vm_map_pageable(map, start, failed, TRUE);
+ return(rv);
+ }
+ }
+
+ vm_map_unlock(map);
+
+ return(KERN_SUCCESS);
+}
+
+/*
+ * vm_map_clean
+ *
+ * Push any dirty cached pages in the address range to their pager.
+ * If syncio is TRUE, dirty pages are written synchronously.
+ * If invalidate is TRUE, any cached pages are freed as well.
+ *
+ * Returns an error if any part of the specified range is not mapped.
+ */
+int
+vm_map_clean(map, start, end, syncio, invalidate)
+ vm_map_t map;
+ vm_offset_t start;
+ vm_offset_t end;
+ boolean_t syncio;
+ boolean_t invalidate;
+{
+ register vm_map_entry_t current;
+ vm_map_entry_t entry;
+ vm_size_t size;
+ vm_object_t object;
+ vm_offset_t offset;
+
+ vm_map_lock_read(map);
+ VM_MAP_RANGE_CHECK(map, start, end);
+ if (!vm_map_lookup_entry(map, start, &entry)) {
+ vm_map_unlock_read(map);
+ return(KERN_INVALID_ADDRESS);
+ }
+
+ /*
+ * Make a first pass to check for holes.
+ */
+ for (current = entry; current->start < end; current = current->next) {
+ if (current->is_sub_map) {
+ vm_map_unlock_read(map);
+ return(KERN_INVALID_ARGUMENT);
+ }
+ if (end > current->end &&
+ (current->next == &map->header ||
+ current->end != current->next->start)) {
+ vm_map_unlock_read(map);
+ return(KERN_INVALID_ADDRESS);
+ }
+ }
+
+ /*
+ * Make a second pass, cleaning/uncaching pages from the indicated
+ * objects as we go.
+ */
+ for (current = entry; current->start < end; current = current->next) {
+ offset = current->offset + (start - current->start);
+ size = (end <= current->end ? end : current->end) - start;
+ if (current->is_a_map) {
+ register vm_map_t smap;
+ vm_map_entry_t tentry;
+ vm_size_t tsize;
+
+ smap = current->object.share_map;
+ vm_map_lock_read(smap);
+ (void) vm_map_lookup_entry(smap, offset, &tentry);
+ tsize = tentry->end - offset;
+ if (tsize < size)
+ size = tsize;
+ object = tentry->object.vm_object;
+ offset = tentry->offset + (offset - tentry->start);
+ vm_object_lock(object);
+ vm_map_unlock_read(smap);
+ } else {
+ object = current->object.vm_object;
+ vm_object_lock(object);
+ }
+ /*
+ * Flush pages if writing is allowed.
+ * XXX should we continue on an error?
+ */
+ if ((current->protection & VM_PROT_WRITE) &&
+ !vm_object_page_clean(object, offset, offset+size,
+ syncio, FALSE)) {
+ vm_object_unlock(object);
+ vm_map_unlock_read(map);
+ return(KERN_FAILURE);
+ }
+ if (invalidate)
+ vm_object_page_remove(object, offset, offset+size);
+ vm_object_unlock(object);
+ start += size;
+ }
+
+ vm_map_unlock_read(map);
+ return(KERN_SUCCESS);
+}
+
+/*
+ * vm_map_entry_unwire: [ internal use only ]
+ *
+ * Make the region specified by this entry pageable.
+ *
+ * The map in question should be locked.
+ * [This is the reason for this routine's existence.]
+ */
+void vm_map_entry_unwire(map, entry)
+ vm_map_t map;
+ register vm_map_entry_t entry;
+{
+ vm_fault_unwire(map, entry->start, entry->end);
+ entry->wired_count = 0;
+}
+
+/*
+ * vm_map_entry_delete: [ internal use only ]
+ *
+ * Deallocate the given entry from the target map.
+ */
+void vm_map_entry_delete(map, entry)
+ register vm_map_t map;
+ register vm_map_entry_t entry;
+{
+ if (entry->wired_count != 0)
+ vm_map_entry_unwire(map, entry);
+
+ vm_map_entry_unlink(map, entry);
+ map->size -= entry->end - entry->start;
+
+ if (entry->is_a_map || entry->is_sub_map)
+ vm_map_deallocate(entry->object.share_map);
+ else
+ vm_object_deallocate(entry->object.vm_object);
+
+ vm_map_entry_dispose(map, entry);
+}
+
+/*
+ * vm_map_delete: [ internal use only ]
+ *
+ * Deallocates the given address range from the target
+ * map.
+ *
+ * When called with a sharing map, removes pages from
+ * that region from all physical maps.
+ */
+int
+vm_map_delete(map, start, end)
+ register vm_map_t map;
+ vm_offset_t start;
+ register vm_offset_t end;
+{
+ register vm_map_entry_t entry;
+ vm_map_entry_t first_entry;
+
+ /*
+ * Find the start of the region, and clip it
+ */
+
+ if (!vm_map_lookup_entry(map, start, &first_entry))
+ entry = first_entry->next;
+ else {
+ entry = first_entry;
+ vm_map_clip_start(map, entry, start);
+
+ /*
+ * Fix the lookup hint now, rather than each
+ * time though the loop.
+ */
+
+ SAVE_HINT(map, entry->prev);
+ }
+
+ /*
+ * Save the free space hint
+ */
+
+ if (map->first_free->start >= start)
+ map->first_free = entry->prev;
+
+ /*
+ * Step through all entries in this region
+ */
+
+ while ((entry != &map->header) && (entry->start < end)) {
+ vm_map_entry_t next;
+ register vm_offset_t s, e;
+ register vm_object_t object;
+
+ vm_map_clip_end(map, entry, end);
+
+ next = entry->next;
+ s = entry->start;
+ e = entry->end;
+
+ /*
+ * Unwire before removing addresses from the pmap;
+ * otherwise, unwiring will put the entries back in
+ * the pmap.
+ */
+
+ object = entry->object.vm_object;
+ if (entry->wired_count != 0)
+ vm_map_entry_unwire(map, entry);
+
+ /*
+ * If this is a sharing map, we must remove
+ * *all* references to this data, since we can't
+ * find all of the physical maps which are sharing
+ * it.
+ */
+
+ if (object == kernel_object || object == kmem_object)
+ vm_object_page_remove(object, entry->offset,
+ entry->offset + (e - s));
+ else if (!map->is_main_map)
+ vm_object_pmap_remove(object,
+ entry->offset,
+ entry->offset + (e - s));
+ else
+ pmap_remove(map->pmap, s, e);
+
+ /*
+ * Delete the entry (which may delete the object)
+ * only after removing all pmap entries pointing
+ * to its pages. (Otherwise, its page frames may
+ * be reallocated, and any modify bits will be
+ * set in the wrong object!)
+ */
+
+ vm_map_entry_delete(map, entry);
+ entry = next;
+ }
+ return(KERN_SUCCESS);
+}
+
+/*
+ * vm_map_remove:
+ *
+ * Remove the given address range from the target map.
+ * This is the exported form of vm_map_delete.
+ */
+int
+vm_map_remove(map, start, end)
+ register vm_map_t map;
+ register vm_offset_t start;
+ register vm_offset_t end;
+{
+ register int result;
+
+ vm_map_lock(map);
+ VM_MAP_RANGE_CHECK(map, start, end);
+ result = vm_map_delete(map, start, end);
+ vm_map_unlock(map);
+
+ return(result);
+}
+
+/*
+ * vm_map_check_protection:
+ *
+ * Assert that the target map allows the specified
+ * privilege on the entire address region given.
+ * The entire region must be allocated.
+ */
+boolean_t vm_map_check_protection(map, start, end, protection)
+ register vm_map_t map;
+ register vm_offset_t start;
+ register vm_offset_t end;
+ register vm_prot_t protection;
+{
+ register vm_map_entry_t entry;
+ vm_map_entry_t tmp_entry;
+
+ if (!vm_map_lookup_entry(map, start, &tmp_entry)) {
+ return(FALSE);
+ }
+
+ entry = tmp_entry;
+
+ while (start < end) {
+ if (entry == &map->header) {
+ return(FALSE);
+ }
+
+ /*
+ * No holes allowed!
+ */
+
+ if (start < entry->start) {
+ return(FALSE);
+ }
+
+ /*
+ * Check protection associated with entry.
+ */
+
+ if ((entry->protection & protection) != protection) {
+ return(FALSE);
+ }
+
+ /* go to next entry */
+
+ start = entry->end;
+ entry = entry->next;
+ }
+ return(TRUE);
+}
+
+/*
+ * vm_map_copy_entry:
+ *
+ * Copies the contents of the source entry to the destination
+ * entry. The entries *must* be aligned properly.
+ */
+void vm_map_copy_entry(src_map, dst_map, src_entry, dst_entry)
+ vm_map_t src_map, dst_map;
+ register vm_map_entry_t src_entry, dst_entry;
+{
+ vm_object_t temp_object;
+
+ if (src_entry->is_sub_map || dst_entry->is_sub_map)
+ return;
+
+ if (dst_entry->object.vm_object != NULL &&
+ (dst_entry->object.vm_object->flags & OBJ_INTERNAL) == 0)
+ printf("vm_map_copy_entry: copying over permanent data!\n");
+
+ /*
+ * If our destination map was wired down,
+ * unwire it now.
+ */
+
+ if (dst_entry->wired_count != 0)
+ vm_map_entry_unwire(dst_map, dst_entry);
+
+ /*
+ * If we're dealing with a sharing map, we
+ * must remove the destination pages from
+ * all maps (since we cannot know which maps
+ * this sharing map belongs in).
+ */
+
+ if (dst_map->is_main_map)
+ pmap_remove(dst_map->pmap, dst_entry->start, dst_entry->end);
+ else
+ vm_object_pmap_remove(dst_entry->object.vm_object,
+ dst_entry->offset,
+ dst_entry->offset +
+ (dst_entry->end - dst_entry->start));
+
+ if (src_entry->wired_count == 0) {
+
+ boolean_t src_needs_copy;
+
+ /*
+ * If the source entry is marked needs_copy,
+ * it is already write-protected.
+ */
+ if (!src_entry->needs_copy) {
+
+ boolean_t su;
+
+ /*
+ * If the source entry has only one mapping,
+ * we can just protect the virtual address
+ * range.
+ */
+ if (!(su = src_map->is_main_map)) {
+ simple_lock(&src_map->ref_lock);
+ su = (src_map->ref_count == 1);
+ simple_unlock(&src_map->ref_lock);
+ }
+
+ if (su) {
+ pmap_protect(src_map->pmap,
+ src_entry->start,
+ src_entry->end,
+ src_entry->protection & ~VM_PROT_WRITE);
+ }
+ else {
+ vm_object_pmap_copy(src_entry->object.vm_object,
+ src_entry->offset,
+ src_entry->offset + (src_entry->end
+ -src_entry->start));
+ }
+ }
+
+ /*
+ * Make a copy of the object.
+ */
+ temp_object = dst_entry->object.vm_object;
+ vm_object_copy(src_entry->object.vm_object,
+ src_entry->offset,
+ (vm_size_t)(src_entry->end -
+ src_entry->start),
+ &dst_entry->object.vm_object,
+ &dst_entry->offset,
+ &src_needs_copy);
+ /*
+ * If we didn't get a copy-object now, mark the
+ * source map entry so that a shadow will be created
+ * to hold its changed pages.
+ */
+ if (src_needs_copy)
+ src_entry->needs_copy = TRUE;
+
+ /*
+ * The destination always needs to have a shadow
+ * created.
+ */
+ dst_entry->needs_copy = TRUE;
+
+ /*
+ * Mark the entries copy-on-write, so that write-enabling
+ * the entry won't make copy-on-write pages writable.
+ */
+ src_entry->copy_on_write = TRUE;
+ dst_entry->copy_on_write = TRUE;
+ /*
+ * Get rid of the old object.
+ */
+ vm_object_deallocate(temp_object);
+
+ pmap_copy(dst_map->pmap, src_map->pmap, dst_entry->start,
+ dst_entry->end - dst_entry->start, src_entry->start);
+ }
+ else {
+ /*
+ * Of course, wired down pages can't be set copy-on-write.
+ * Cause wired pages to be copied into the new
+ * map by simulating faults (the new pages are
+ * pageable)
+ */
+ vm_fault_copy_entry(dst_map, src_map, dst_entry, src_entry);
+ }
+}
+
+/*
+ * vm_map_copy:
+ *
+ * Perform a virtual memory copy from the source
+ * address map/range to the destination map/range.
+ *
+ * If src_destroy or dst_alloc is requested,
+ * the source and destination regions should be
+ * disjoint, not only in the top-level map, but
+ * in the sharing maps as well. [The best way
+ * to guarantee this is to use a new intermediate
+ * map to make copies. This also reduces map
+ * fragmentation.]
+ */
+int
+vm_map_copy(dst_map, src_map,
+ dst_addr, len, src_addr,
+ dst_alloc, src_destroy)
+ vm_map_t dst_map;
+ vm_map_t src_map;
+ vm_offset_t dst_addr;
+ vm_size_t len;
+ vm_offset_t src_addr;
+ boolean_t dst_alloc;
+ boolean_t src_destroy;
+{
+ register
+ vm_map_entry_t src_entry;
+ register
+ vm_map_entry_t dst_entry;
+ vm_map_entry_t tmp_entry;
+ vm_offset_t src_start;
+ vm_offset_t src_end;
+ vm_offset_t dst_start;
+ vm_offset_t dst_end;
+ vm_offset_t src_clip;
+ vm_offset_t dst_clip;
+ int result;
+ boolean_t old_src_destroy;
+
+ /*
+ * XXX While we figure out why src_destroy screws up,
+ * we'll do it by explicitly vm_map_delete'ing at the end.
+ */
+
+ old_src_destroy = src_destroy;
+ src_destroy = FALSE;
+
+ /*
+ * Compute start and end of region in both maps
+ */
+
+ src_start = src_addr;
+ src_end = src_start + len;
+ dst_start = dst_addr;
+ dst_end = dst_start + len;
+
+ /*
+ * Check that the region can exist in both source
+ * and destination.
+ */
+
+ if ((dst_end < dst_start) || (src_end < src_start))
+ return(KERN_NO_SPACE);
+
+ /*
+ * Lock the maps in question -- we avoid deadlock
+ * by ordering lock acquisition by map value
+ */
+
+ if (src_map == dst_map) {
+ vm_map_lock(src_map);
+ }
+ else if ((int) src_map < (int) dst_map) {
+ vm_map_lock(src_map);
+ vm_map_lock(dst_map);
+ } else {
+ vm_map_lock(dst_map);
+ vm_map_lock(src_map);
+ }
+
+ result = KERN_SUCCESS;
+
+ /*
+ * Check protections... source must be completely readable and
+ * destination must be completely writable. [Note that if we're
+ * allocating the destination region, we don't have to worry
+ * about protection, but instead about whether the region
+ * exists.]
+ */
+
+ if (src_map->is_main_map && dst_map->is_main_map) {
+ if (!vm_map_check_protection(src_map, src_start, src_end,
+ VM_PROT_READ)) {
+ result = KERN_PROTECTION_FAILURE;
+ goto Return;
+ }
+
+ if (dst_alloc) {
+ /* XXX Consider making this a vm_map_find instead */
+ if ((result = vm_map_insert(dst_map, NULL,
+ (vm_offset_t) 0, dst_start, dst_end)) != KERN_SUCCESS)
+ goto Return;
+ }
+ else if (!vm_map_check_protection(dst_map, dst_start, dst_end,
+ VM_PROT_WRITE)) {
+ result = KERN_PROTECTION_FAILURE;
+ goto Return;
+ }
+ }
+
+ /*
+ * Find the start entries and clip.
+ *
+ * Note that checking protection asserts that the
+ * lookup cannot fail.
+ *
+ * Also note that we wait to do the second lookup
+ * until we have done the first clip, as the clip
+ * may affect which entry we get!
+ */
+
+ (void) vm_map_lookup_entry(src_map, src_addr, &tmp_entry);
+ src_entry = tmp_entry;
+ vm_map_clip_start(src_map, src_entry, src_start);
+
+ (void) vm_map_lookup_entry(dst_map, dst_addr, &tmp_entry);
+ dst_entry = tmp_entry;
+ vm_map_clip_start(dst_map, dst_entry, dst_start);
+
+ /*
+ * If both source and destination entries are the same,
+ * retry the first lookup, as it may have changed.
+ */
+
+ if (src_entry == dst_entry) {
+ (void) vm_map_lookup_entry(src_map, src_addr, &tmp_entry);
+ src_entry = tmp_entry;
+ }
+
+ /*
+ * If source and destination entries are still the same,
+ * a null copy is being performed.
+ */
+
+ if (src_entry == dst_entry)
+ goto Return;
+
+ /*
+ * Go through entries until we get to the end of the
+ * region.
+ */
+
+ while (src_start < src_end) {
+ /*
+ * Clip the entries to the endpoint of the entire region.
+ */
+
+ vm_map_clip_end(src_map, src_entry, src_end);
+ vm_map_clip_end(dst_map, dst_entry, dst_end);
+
+ /*
+ * Clip each entry to the endpoint of the other entry.
+ */
+
+ src_clip = src_entry->start + (dst_entry->end - dst_entry->start);
+ vm_map_clip_end(src_map, src_entry, src_clip);
+
+ dst_clip = dst_entry->start + (src_entry->end - src_entry->start);
+ vm_map_clip_end(dst_map, dst_entry, dst_clip);
+
+ /*
+ * Both entries now match in size and relative endpoints.
+ *
+ * If both entries refer to a VM object, we can
+ * deal with them now.
+ */
+
+ if (!src_entry->is_a_map && !dst_entry->is_a_map) {
+ vm_map_copy_entry(src_map, dst_map, src_entry,
+ dst_entry);
+ }
+ else {
+ register vm_map_t new_dst_map;
+ vm_offset_t new_dst_start;
+ vm_size_t new_size;
+ vm_map_t new_src_map;
+ vm_offset_t new_src_start;
+
+ /*
+ * We have to follow at least one sharing map.
+ */
+
+ new_size = (dst_entry->end - dst_entry->start);
+
+ if (src_entry->is_a_map) {
+ new_src_map = src_entry->object.share_map;
+ new_src_start = src_entry->offset;
+ }
+ else {
+ new_src_map = src_map;
+ new_src_start = src_entry->start;
+ lock_set_recursive(&src_map->lock);
+ }
+
+ if (dst_entry->is_a_map) {
+ vm_offset_t new_dst_end;
+
+ new_dst_map = dst_entry->object.share_map;
+ new_dst_start = dst_entry->offset;
+
+ /*
+ * Since the destination sharing entries
+ * will be merely deallocated, we can
+ * do that now, and replace the region
+ * with a null object. [This prevents
+ * splitting the source map to match
+ * the form of the destination map.]
+ * Note that we can only do so if the
+ * source and destination do not overlap.
+ */
+
+ new_dst_end = new_dst_start + new_size;
+
+ if (new_dst_map != new_src_map) {
+ vm_map_lock(new_dst_map);
+ (void) vm_map_delete(new_dst_map,
+ new_dst_start,
+ new_dst_end);
+ (void) vm_map_insert(new_dst_map,
+ NULL,
+ (vm_offset_t) 0,
+ new_dst_start,
+ new_dst_end);
+ vm_map_unlock(new_dst_map);
+ }
+ }
+ else {
+ new_dst_map = dst_map;
+ new_dst_start = dst_entry->start;
+ lock_set_recursive(&dst_map->lock);
+ }
+
+ /*
+ * Recursively copy the sharing map.
+ */
+
+ (void) vm_map_copy(new_dst_map, new_src_map,
+ new_dst_start, new_size, new_src_start,
+ FALSE, FALSE);
+
+ if (dst_map == new_dst_map)
+ lock_clear_recursive(&dst_map->lock);
+ if (src_map == new_src_map)
+ lock_clear_recursive(&src_map->lock);
+ }
+
+ /*
+ * Update variables for next pass through the loop.
+ */
+
+ src_start = src_entry->end;
+ src_entry = src_entry->next;
+ dst_start = dst_entry->end;
+ dst_entry = dst_entry->next;
+
+ /*
+ * If the source is to be destroyed, here is the
+ * place to do it.
+ */
+
+ if (src_destroy && src_map->is_main_map &&
+ dst_map->is_main_map)
+ vm_map_entry_delete(src_map, src_entry->prev);
+ }
+
+ /*
+ * Update the physical maps as appropriate
+ */
+
+ if (src_map->is_main_map && dst_map->is_main_map) {
+ if (src_destroy)
+ pmap_remove(src_map->pmap, src_addr, src_addr + len);
+ }
+
+ /*
+ * Unlock the maps
+ */
+
+ Return: ;
+
+ if (old_src_destroy)
+ vm_map_delete(src_map, src_addr, src_addr + len);
+
+ vm_map_unlock(src_map);
+ if (src_map != dst_map)
+ vm_map_unlock(dst_map);
+
+ return(result);
+}
+
+/*
+ * vmspace_fork:
+ * Create a new process vmspace structure and vm_map
+ * based on those of an existing process. The new map
+ * is based on the old map, according to the inheritance
+ * values on the regions in that map.
+ *
+ * The source map must not be locked.
+ */
+struct vmspace *
+vmspace_fork(vm1)
+ register struct vmspace *vm1;
+{
+ register struct vmspace *vm2;
+ vm_map_t old_map = &vm1->vm_map;
+ vm_map_t new_map;
+ vm_map_entry_t old_entry;
+ vm_map_entry_t new_entry;
+ pmap_t new_pmap;
+
+ vm_map_lock(old_map);
+
+ vm2 = vmspace_alloc(old_map->min_offset, old_map->max_offset,
+ old_map->entries_pageable);
+ bcopy(&vm1->vm_startcopy, &vm2->vm_startcopy,
+ (caddr_t) (vm1 + 1) - (caddr_t) &vm1->vm_startcopy);
+ new_pmap = &vm2->vm_pmap; /* XXX */
+ new_map = &vm2->vm_map; /* XXX */
+
+ old_entry = old_map->header.next;
+
+ while (old_entry != &old_map->header) {
+ if (old_entry->is_sub_map)
+ panic("vm_map_fork: encountered a submap");
+
+ switch (old_entry->inheritance) {
+ case VM_INHERIT_NONE:
+ break;
+
+ case VM_INHERIT_SHARE:
+ /*
+ * If we don't already have a sharing map:
+ */
+
+ if (!old_entry->is_a_map) {
+ vm_map_t new_share_map;
+ vm_map_entry_t new_share_entry;
+
+ /*
+ * Create a new sharing map
+ */
+
+ new_share_map = vm_map_create(NULL,
+ old_entry->start,
+ old_entry->end,
+ TRUE);
+ new_share_map->is_main_map = FALSE;
+
+ /*
+ * Create the only sharing entry from the
+ * old task map entry.
+ */
+
+ new_share_entry =
+ vm_map_entry_create(new_share_map);
+ *new_share_entry = *old_entry;
+ new_share_entry->wired_count = 0;
+
+ /*
+ * Insert the entry into the new sharing
+ * map
+ */
+
+ vm_map_entry_link(new_share_map,
+ new_share_map->header.prev,
+ new_share_entry);
+
+ /*
+ * Fix up the task map entry to refer
+ * to the sharing map now.
+ */
+
+ old_entry->is_a_map = TRUE;
+ old_entry->object.share_map = new_share_map;
+ old_entry->offset = old_entry->start;
+ }
+
+ /*
+ * Clone the entry, referencing the sharing map.
+ */
+
+ new_entry = vm_map_entry_create(new_map);
+ *new_entry = *old_entry;
+ new_entry->wired_count = 0;
+ vm_map_reference(new_entry->object.share_map);
+
+ /*
+ * Insert the entry into the new map -- we
+ * know we're inserting at the end of the new
+ * map.
+ */
+
+ vm_map_entry_link(new_map, new_map->header.prev,
+ new_entry);
+
+ /*
+ * Update the physical map
+ */
+
+ pmap_copy(new_map->pmap, old_map->pmap,
+ new_entry->start,
+ (old_entry->end - old_entry->start),
+ old_entry->start);
+ break;
+
+ case VM_INHERIT_COPY:
+ /*
+ * Clone the entry and link into the map.
+ */
+
+ new_entry = vm_map_entry_create(new_map);
+ *new_entry = *old_entry;
+ new_entry->wired_count = 0;
+ new_entry->object.vm_object = NULL;
+ new_entry->is_a_map = FALSE;
+ vm_map_entry_link(new_map, new_map->header.prev,
+ new_entry);
+ if (old_entry->is_a_map) {
+ int check;
+
+ check = vm_map_copy(new_map,
+ old_entry->object.share_map,
+ new_entry->start,
+ (vm_size_t)(new_entry->end -
+ new_entry->start),
+ old_entry->offset,
+ FALSE, FALSE);
+ if (check != KERN_SUCCESS)
+ printf("vm_map_fork: copy in share_map region failed\n");
+ }
+ else {
+ vm_map_copy_entry(old_map, new_map, old_entry,
+ new_entry);
+ }
+ break;
+ }
+ old_entry = old_entry->next;
+ }
+
+ new_map->size = old_map->size;
+ vm_map_unlock(old_map);
+
+ return(vm2);
+}
+
+/*
+ * vm_map_lookup:
+ *
+ * Finds the VM object, offset, and
+ * protection for a given virtual address in the
+ * specified map, assuming a page fault of the
+ * type specified.
+ *
+ * Leaves the map in question locked for read; return
+ * values are guaranteed until a vm_map_lookup_done
+ * call is performed. Note that the map argument
+ * is in/out; the returned map must be used in
+ * the call to vm_map_lookup_done.
+ *
+ * A handle (out_entry) is returned for use in
+ * vm_map_lookup_done, to make that fast.
+ *
+ * If a lookup is requested with "write protection"
+ * specified, the map may be changed to perform virtual
+ * copying operations, although the data referenced will
+ * remain the same.
+ */
+int
+vm_map_lookup(var_map, vaddr, fault_type, out_entry,
+ object, offset, out_prot, wired, single_use)
+ vm_map_t *var_map; /* IN/OUT */
+ register vm_offset_t vaddr;
+ register vm_prot_t fault_type;
+
+ vm_map_entry_t *out_entry; /* OUT */
+ vm_object_t *object; /* OUT */
+ vm_offset_t *offset; /* OUT */
+ vm_prot_t *out_prot; /* OUT */
+ boolean_t *wired; /* OUT */
+ boolean_t *single_use; /* OUT */
+{
+ vm_map_t share_map;
+ vm_offset_t share_offset;
+ register vm_map_entry_t entry;
+ register vm_map_t map = *var_map;
+ register vm_prot_t prot;
+ register boolean_t su;
+
+ RetryLookup: ;
+
+ /*
+ * Lookup the faulting address.
+ */
+
+ vm_map_lock_read(map);
+
+#define RETURN(why) \
+ { \
+ vm_map_unlock_read(map); \
+ return(why); \
+ }
+
+ /*
+ * If the map has an interesting hint, try it before calling
+ * full blown lookup routine.
+ */
+
+ simple_lock(&map->hint_lock);
+ entry = map->hint;
+ simple_unlock(&map->hint_lock);
+
+ *out_entry = entry;
+
+ if ((entry == &map->header) ||
+ (vaddr < entry->start) || (vaddr >= entry->end)) {
+ vm_map_entry_t tmp_entry;
+
+ /*
+ * Entry was either not a valid hint, or the vaddr
+ * was not contained in the entry, so do a full lookup.
+ */
+ if (!vm_map_lookup_entry(map, vaddr, &tmp_entry))
+ RETURN(KERN_INVALID_ADDRESS);
+
+ entry = tmp_entry;
+ *out_entry = entry;
+ }
+
+ /*
+ * Handle submaps.
+ */
+
+ if (entry->is_sub_map) {
+ vm_map_t old_map = map;
+
+ *var_map = map = entry->object.sub_map;
+ vm_map_unlock_read(old_map);
+ goto RetryLookup;
+ }
+
+ /*
+ * Check whether this task is allowed to have
+ * this page.
+ */
+
+ prot = entry->protection;
+ if ((fault_type & (prot)) != fault_type)
+ RETURN(KERN_PROTECTION_FAILURE);
+
+ /*
+ * If this page is not pageable, we have to get
+ * it for all possible accesses.
+ */
+
+ if (*wired = (entry->wired_count != 0))
+ prot = fault_type = entry->protection;
+
+ /*
+ * If we don't already have a VM object, track
+ * it down.
+ */
+
+ if (su = !entry->is_a_map) {
+ share_map = map;
+ share_offset = vaddr;
+ }
+ else {
+ vm_map_entry_t share_entry;
+
+ /*
+ * Compute the sharing map, and offset into it.
+ */
+
+ share_map = entry->object.share_map;
+ share_offset = (vaddr - entry->start) + entry->offset;
+
+ /*
+ * Look for the backing store object and offset
+ */
+
+ vm_map_lock_read(share_map);
+
+ if (!vm_map_lookup_entry(share_map, share_offset,
+ &share_entry)) {
+ vm_map_unlock_read(share_map);
+ RETURN(KERN_INVALID_ADDRESS);
+ }
+ entry = share_entry;
+ }
+
+ /*
+ * If the entry was copy-on-write, we either ...
+ */
+
+ if (entry->needs_copy) {
+ /*
+ * If we want to write the page, we may as well
+ * handle that now since we've got the sharing
+ * map locked.
+ *
+ * If we don't need to write the page, we just
+ * demote the permissions allowed.
+ */
+
+ if (fault_type & VM_PROT_WRITE) {
+ /*
+ * Make a new object, and place it in the
+ * object chain. Note that no new references
+ * have appeared -- one just moved from the
+ * share map to the new object.
+ */
+
+ if (lock_read_to_write(&share_map->lock)) {
+ if (share_map != map)
+ vm_map_unlock_read(map);
+ goto RetryLookup;
+ }
+
+ vm_object_shadow(
+ &entry->object.vm_object,
+ &entry->offset,
+ (vm_size_t) (entry->end - entry->start));
+
+ entry->needs_copy = FALSE;
+
+ lock_write_to_read(&share_map->lock);
+ }
+ else {
+ /*
+ * We're attempting to read a copy-on-write
+ * page -- don't allow writes.
+ */
+
+ prot &= (~VM_PROT_WRITE);
+ }
+ }
+
+ /*
+ * Create an object if necessary.
+ */
+ if (entry->object.vm_object == NULL) {
+
+ if (lock_read_to_write(&share_map->lock)) {
+ if (share_map != map)
+ vm_map_unlock_read(map);
+ goto RetryLookup;
+ }
+
+ entry->object.vm_object = vm_object_allocate(
+ (vm_size_t)(entry->end - entry->start));
+ entry->offset = 0;
+ lock_write_to_read(&share_map->lock);
+ }
+
+ /*
+ * Return the object/offset from this entry. If the entry
+ * was copy-on-write or empty, it has been fixed up.
+ */
+
+ *offset = (share_offset - entry->start) + entry->offset;
+ *object = entry->object.vm_object;
+
+ /*
+ * Return whether this is the only map sharing this data.
+ */
+
+ if (!su) {
+ simple_lock(&share_map->ref_lock);
+ su = (share_map->ref_count == 1);
+ simple_unlock(&share_map->ref_lock);
+ }
+
+ *out_prot = prot;
+ *single_use = su;
+
+ return(KERN_SUCCESS);
+
+#undef RETURN
+}
+
+/*
+ * vm_map_lookup_done:
+ *
+ * Releases locks acquired by a vm_map_lookup
+ * (according to the handle returned by that lookup).
+ */
+
+void vm_map_lookup_done(map, entry)
+ register vm_map_t map;
+ vm_map_entry_t entry;
+{
+ /*
+ * If this entry references a map, unlock it first.
+ */
+
+ if (entry->is_a_map)
+ vm_map_unlock_read(entry->object.share_map);
+
+ /*
+ * Unlock the main-level map
+ */
+
+ vm_map_unlock_read(map);
+}
+
+/*
+ * Routine: vm_map_simplify
+ * Purpose:
+ * Attempt to simplify the map representation in
+ * the vicinity of the given starting address.
+ * Note:
+ * This routine is intended primarily to keep the
+ * kernel maps more compact -- they generally don't
+ * benefit from the "expand a map entry" technology
+ * at allocation time because the adjacent entry
+ * is often wired down.
+ */
+void vm_map_simplify(map, start)
+ vm_map_t map;
+ vm_offset_t start;
+{
+ vm_map_entry_t this_entry;
+ vm_map_entry_t prev_entry;
+
+ vm_map_lock(map);
+ if (
+ (vm_map_lookup_entry(map, start, &this_entry)) &&
+ ((prev_entry = this_entry->prev) != &map->header) &&
+
+ (prev_entry->end == start) &&
+ (map->is_main_map) &&
+
+ (prev_entry->is_a_map == FALSE) &&
+ (prev_entry->is_sub_map == FALSE) &&
+
+ (this_entry->is_a_map == FALSE) &&
+ (this_entry->is_sub_map == FALSE) &&
+
+ (prev_entry->inheritance == this_entry->inheritance) &&
+ (prev_entry->protection == this_entry->protection) &&
+ (prev_entry->max_protection == this_entry->max_protection) &&
+ (prev_entry->wired_count == this_entry->wired_count) &&
+
+ (prev_entry->copy_on_write == this_entry->copy_on_write) &&
+ (prev_entry->needs_copy == this_entry->needs_copy) &&
+
+ (prev_entry->object.vm_object == this_entry->object.vm_object) &&
+ ((prev_entry->offset + (prev_entry->end - prev_entry->start))
+ == this_entry->offset)
+ ) {
+ if (map->first_free == this_entry)
+ map->first_free = prev_entry;
+
+ if (!this_entry->object.vm_object->paging_in_progress) {
+ SAVE_HINT(map, prev_entry);
+ vm_map_entry_unlink(map, this_entry);
+ prev_entry->end = this_entry->end;
+ vm_object_deallocate(this_entry->object.vm_object);
+ vm_map_entry_dispose(map, this_entry);
+ }
+ }
+ vm_map_unlock(map);
+}
+
+/*
+ * vm_map_print: [ debug ]
+ */
+void vm_map_print(map, full)
+ register vm_map_t map;
+ boolean_t full;
+{
+ register vm_map_entry_t entry;
+ extern int indent;
+
+ iprintf("%s map 0x%x: pmap=0x%x,ref=%d,nentries=%d,version=%d\n",
+ (map->is_main_map ? "Task" : "Share"),
+ (int) map, (int) (map->pmap), map->ref_count, map->nentries,
+ map->timestamp);
+
+ if (!full && indent)
+ return;
+
+ indent += 2;
+ for (entry = map->header.next; entry != &map->header;
+ entry = entry->next) {
+ iprintf("map entry 0x%x: start=0x%x, end=0x%x, ",
+ (int) entry, (int) entry->start, (int) entry->end);
+ if (map->is_main_map) {
+ static char *inheritance_name[4] =
+ { "share", "copy", "none", "donate_copy"};
+ printf("prot=%x/%x/%s, ",
+ entry->protection,
+ entry->max_protection,
+ inheritance_name[entry->inheritance]);
+ if (entry->wired_count != 0)
+ printf("wired, ");
+ }
+
+ if (entry->is_a_map || entry->is_sub_map) {
+ printf("share=0x%x, offset=0x%x\n",
+ (int) entry->object.share_map,
+ (int) entry->offset);
+ if ((entry->prev == &map->header) ||
+ (!entry->prev->is_a_map) ||
+ (entry->prev->object.share_map !=
+ entry->object.share_map)) {
+ indent += 2;
+ vm_map_print(entry->object.share_map, full);
+ indent -= 2;
+ }
+
+ }
+ else {
+ printf("object=0x%x, offset=0x%x",
+ (int) entry->object.vm_object,
+ (int) entry->offset);
+ if (entry->copy_on_write)
+ printf(", copy (%s)",
+ entry->needs_copy ? "needed" : "done");
+ printf("\n");
+
+ if ((entry->prev == &map->header) ||
+ (entry->prev->is_a_map) ||
+ (entry->prev->object.vm_object !=
+ entry->object.vm_object)) {
+ indent += 2;
+ vm_object_print(entry->object.vm_object, full);
+ indent -= 2;
+ }
+ }
+ }
+ indent -= 2;
+}
diff --git a/sys/vm/vm_map.h b/sys/vm/vm_map.h
new file mode 100644
index 0000000..ee253ef
--- /dev/null
+++ b/sys/vm/vm_map.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_map.h 8.3 (Berkeley) 3/15/94
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Virtual memory map module definitions.
+ */
+
+#ifndef _VM_MAP_
+#define _VM_MAP_
+
+/*
+ * Types defined:
+ *
+ * vm_map_t the high-level address map data structure.
+ * vm_map_entry_t an entry in an address map.
+ * vm_map_version_t a timestamp of a map, for use with vm_map_lookup
+ */
+
+/*
+ * Objects which live in maps may be either VM objects, or
+ * another map (called a "sharing map") which denotes read-write
+ * sharing with other maps.
+ */
+
+union vm_map_object {
+ struct vm_object *vm_object; /* object object */
+ struct vm_map *share_map; /* share map */
+ struct vm_map *sub_map; /* belongs to another map */
+};
+
+/*
+ * Address map entries consist of start and end addresses,
+ * a VM object (or sharing map) and offset into that object,
+ * and user-exported inheritance and protection information.
+ * Also included is control information for virtual copy operations.
+ */
+struct vm_map_entry {
+ struct vm_map_entry *prev; /* previous entry */
+ struct vm_map_entry *next; /* next entry */
+ vm_offset_t start; /* start address */
+ vm_offset_t end; /* end address */
+ union vm_map_object object; /* object I point to */
+ vm_offset_t offset; /* offset into object */
+ boolean_t is_a_map:1, /* Is "object" a map? */
+ is_sub_map:1, /* Is "object" a submap? */
+ /* Only in sharing maps: */
+ copy_on_write:1,/* is data copy-on-write */
+ needs_copy:1; /* does object need to be copied */
+ /* Only in task maps: */
+ vm_prot_t protection; /* protection code */
+ vm_prot_t max_protection; /* maximum protection */
+ vm_inherit_t inheritance; /* inheritance */
+ int wired_count; /* can be paged if = 0 */
+};
+
+/*
+ * Maps are doubly-linked lists of map entries, kept sorted
+ * by address. A single hint is provided to start
+ * searches again from the last successful search,
+ * insertion, or removal.
+ */
+struct vm_map {
+ struct pmap * pmap; /* Physical map */
+ lock_data_t lock; /* Lock for map data */
+ struct vm_map_entry header; /* List of entries */
+ int nentries; /* Number of entries */
+ vm_size_t size; /* virtual size */
+ boolean_t is_main_map; /* Am I a main map? */
+ int ref_count; /* Reference count */
+ simple_lock_data_t ref_lock; /* Lock for ref_count field */
+ vm_map_entry_t hint; /* hint for quick lookups */
+ simple_lock_data_t hint_lock; /* lock for hint storage */
+ vm_map_entry_t first_free; /* First free space hint */
+ boolean_t entries_pageable; /* map entries pageable?? */
+ unsigned int timestamp; /* Version number */
+#define min_offset header.start
+#define max_offset header.end
+};
+
+/*
+ * Map versions are used to validate a previous lookup attempt.
+ *
+ * Since lookup operations may involve both a main map and
+ * a sharing map, it is necessary to have a timestamp from each.
+ * [If the main map timestamp has changed, the share_map and
+ * associated timestamp are no longer valid; the map version
+ * does not include a reference for the imbedded share_map.]
+ */
+typedef struct {
+ int main_timestamp;
+ vm_map_t share_map;
+ int share_timestamp;
+} vm_map_version_t;
+
+/*
+ * Macros: vm_map_lock, etc.
+ * Function:
+ * Perform locking on the data portion of a map.
+ */
+
+#define vm_map_lock(map) { \
+ lock_write(&(map)->lock); \
+ (map)->timestamp++; \
+}
+#define vm_map_unlock(map) lock_write_done(&(map)->lock)
+#define vm_map_lock_read(map) lock_read(&(map)->lock)
+#define vm_map_unlock_read(map) lock_read_done(&(map)->lock)
+
+/*
+ * Functions implemented as macros
+ */
+#define vm_map_min(map) ((map)->min_offset)
+#define vm_map_max(map) ((map)->max_offset)
+#define vm_map_pmap(map) ((map)->pmap)
+
+/* XXX: number of kernel maps and entries to statically allocate */
+#define MAX_KMAP 10
+#define MAX_KMAPENT 128
+
+#ifdef KERNEL
+boolean_t vm_map_check_protection __P((vm_map_t,
+ vm_offset_t, vm_offset_t, vm_prot_t));
+int vm_map_copy __P((vm_map_t, vm_map_t, vm_offset_t,
+ vm_size_t, vm_offset_t, boolean_t, boolean_t));
+void vm_map_copy_entry __P((vm_map_t,
+ vm_map_t, vm_map_entry_t, vm_map_entry_t));
+struct pmap;
+vm_map_t vm_map_create __P((struct pmap *,
+ vm_offset_t, vm_offset_t, boolean_t));
+void vm_map_deallocate __P((vm_map_t));
+int vm_map_delete __P((vm_map_t, vm_offset_t, vm_offset_t));
+vm_map_entry_t vm_map_entry_create __P((vm_map_t));
+void vm_map_entry_delete __P((vm_map_t, vm_map_entry_t));
+void vm_map_entry_dispose __P((vm_map_t, vm_map_entry_t));
+void vm_map_entry_unwire __P((vm_map_t, vm_map_entry_t));
+int vm_map_find __P((vm_map_t, vm_object_t,
+ vm_offset_t, vm_offset_t *, vm_size_t, boolean_t));
+int vm_map_findspace __P((vm_map_t,
+ vm_offset_t, vm_size_t, vm_offset_t *));
+int vm_map_inherit __P((vm_map_t,
+ vm_offset_t, vm_offset_t, vm_inherit_t));
+void vm_map_init __P((struct vm_map *,
+ vm_offset_t, vm_offset_t, boolean_t));
+int vm_map_insert __P((vm_map_t,
+ vm_object_t, vm_offset_t, vm_offset_t, vm_offset_t));
+int vm_map_lookup __P((vm_map_t *, vm_offset_t, vm_prot_t,
+ vm_map_entry_t *, vm_object_t *, vm_offset_t *, vm_prot_t *,
+ boolean_t *, boolean_t *));
+void vm_map_lookup_done __P((vm_map_t, vm_map_entry_t));
+boolean_t vm_map_lookup_entry __P((vm_map_t,
+ vm_offset_t, vm_map_entry_t *));
+int vm_map_pageable __P((vm_map_t,
+ vm_offset_t, vm_offset_t, boolean_t));
+int vm_map_clean __P((vm_map_t,
+ vm_offset_t, vm_offset_t, boolean_t, boolean_t));
+void vm_map_print __P((vm_map_t, boolean_t));
+int vm_map_protect __P((vm_map_t,
+ vm_offset_t, vm_offset_t, vm_prot_t, boolean_t));
+void vm_map_reference __P((vm_map_t));
+int vm_map_remove __P((vm_map_t, vm_offset_t, vm_offset_t));
+void vm_map_simplify __P((vm_map_t, vm_offset_t));
+void vm_map_simplify_entry __P((vm_map_t, vm_map_entry_t));
+void vm_map_startup __P((void));
+int vm_map_submap __P((vm_map_t,
+ vm_offset_t, vm_offset_t, vm_map_t));
+#endif
+#endif /* _VM_MAP_ */
diff --git a/sys/vm/vm_meter.c b/sys/vm/vm_meter.c
new file mode 100644
index 0000000..2a8029b
--- /dev/null
+++ b/sys/vm/vm_meter.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_meter.c 8.4 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <vm/vm.h>
+#include <sys/sysctl.h>
+
+struct loadavg averunnable; /* load average, of runnable procs */
+
+int maxslp = MAXSLP;
+int saferss = SAFERSS;
+
+void
+vmmeter()
+{
+
+ if (time.tv_sec % 5 == 0)
+ loadav(&averunnable);
+ if (proc0.p_slptime > maxslp/2)
+ wakeup((caddr_t)&proc0);
+}
+
+/*
+ * Constants for averages over 1, 5, and 15 minutes
+ * when sampling at 5 second intervals.
+ */
+fixpt_t cexp[3] = {
+ 0.9200444146293232 * FSCALE, /* exp(-1/12) */
+ 0.9834714538216174 * FSCALE, /* exp(-1/60) */
+ 0.9944598480048967 * FSCALE, /* exp(-1/180) */
+};
+
+/*
+ * Compute a tenex style load average of a quantity on
+ * 1, 5 and 15 minute intervals.
+ */
+void
+loadav(avg)
+ register struct loadavg *avg;
+{
+ register int i, nrun;
+ register struct proc *p;
+
+ for (nrun = 0, p = (struct proc *)allproc; p != NULL; p = p->p_next) {
+ switch (p->p_stat) {
+ case SSLEEP:
+ if (p->p_priority > PZERO || p->p_slptime != 0)
+ continue;
+ /* fall through */
+ case SRUN:
+ case SIDL:
+ nrun++;
+ }
+ }
+ for (i = 0; i < 3; i++)
+ avg->ldavg[i] = (cexp[i] * avg->ldavg[i] +
+ nrun * FSCALE * (FSCALE - cexp[i])) >> FSHIFT;
+}
+
+/*
+ * Attributes associated with virtual memory.
+ */
+int
+vm_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+ struct proc *p;
+{
+ struct vmtotal vmtotals;
+
+ /* all sysctl names at this level are terminal */
+ if (namelen != 1)
+ return (ENOTDIR); /* overloaded */
+
+ switch (name[0]) {
+ case VM_LOADAVG:
+ averunnable.fscale = FSCALE;
+ return (sysctl_rdstruct(oldp, oldlenp, newp, &averunnable,
+ sizeof(averunnable)));
+ case VM_METER:
+ vmtotal(&vmtotals);
+ return (sysctl_rdstruct(oldp, oldlenp, newp, &vmtotals,
+ sizeof(vmtotals)));
+ default:
+ return (EOPNOTSUPP);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Calculate the current state of the system.
+ * Done on demand from getkerninfo().
+ */
+void
+vmtotal(totalp)
+ register struct vmtotal *totalp;
+{
+ register struct proc *p;
+ register vm_map_entry_t entry;
+ register vm_object_t object;
+ register vm_map_t map;
+ int paging;
+
+ bzero(totalp, sizeof *totalp);
+ /*
+ * Mark all objects as inactive.
+ */
+ simple_lock(&vm_object_list_lock);
+ for (object = vm_object_list.tqh_first;
+ object != NULL;
+ object = object->object_list.tqe_next)
+ object->flags &= ~OBJ_ACTIVE;
+ simple_unlock(&vm_object_list_lock);
+ /*
+ * Calculate process statistics.
+ */
+ for (p = (struct proc *)allproc; p != NULL; p = p->p_next) {
+ if (p->p_flag & P_SYSTEM)
+ continue;
+ switch (p->p_stat) {
+ case 0:
+ continue;
+
+ case SSLEEP:
+ case SSTOP:
+ if (p->p_flag & P_INMEM) {
+ if (p->p_priority <= PZERO)
+ totalp->t_dw++;
+ else if (p->p_slptime < maxslp)
+ totalp->t_sl++;
+ } else if (p->p_slptime < maxslp)
+ totalp->t_sw++;
+ if (p->p_slptime >= maxslp)
+ continue;
+ break;
+
+ case SRUN:
+ case SIDL:
+ if (p->p_flag & P_INMEM)
+ totalp->t_rq++;
+ else
+ totalp->t_sw++;
+ if (p->p_stat == SIDL)
+ continue;
+ break;
+ }
+ /*
+ * Note active objects.
+ */
+ paging = 0;
+ for (map = &p->p_vmspace->vm_map, entry = map->header.next;
+ entry != &map->header; entry = entry->next) {
+ if (entry->is_a_map || entry->is_sub_map ||
+ entry->object.vm_object == NULL)
+ continue;
+ entry->object.vm_object->flags |= OBJ_ACTIVE;
+ paging |= entry->object.vm_object->paging_in_progress;
+ }
+ if (paging)
+ totalp->t_pw++;
+ }
+ /*
+ * Calculate object memory usage statistics.
+ */
+ simple_lock(&vm_object_list_lock);
+ for (object = vm_object_list.tqh_first;
+ object != NULL;
+ object = object->object_list.tqe_next) {
+ totalp->t_vm += num_pages(object->size);
+ totalp->t_rm += object->resident_page_count;
+ if (object->flags & OBJ_ACTIVE) {
+ totalp->t_avm += num_pages(object->size);
+ totalp->t_arm += object->resident_page_count;
+ }
+ if (object->ref_count > 1) {
+ /* shared object */
+ totalp->t_vmshr += num_pages(object->size);
+ totalp->t_rmshr += object->resident_page_count;
+ if (object->flags & OBJ_ACTIVE) {
+ totalp->t_avmshr += num_pages(object->size);
+ totalp->t_armshr += object->resident_page_count;
+ }
+ }
+ }
+ totalp->t_free = cnt.v_free_count;
+}
diff --git a/sys/vm/vm_mmap.c b/sys/vm/vm_mmap.c
new file mode 100644
index 0000000..2e7204a
--- /dev/null
+++ b/sys/vm/vm_mmap.c
@@ -0,0 +1,836 @@
+/*
+ * Copyright (c) 1988 University of Utah.
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: Utah $Hdr: vm_mmap.c 1.6 91/10/21$
+ *
+ * @(#)vm_mmap.c 8.4 (Berkeley) 1/12/94
+ */
+
+/*
+ * Mapped file (mmap) interface to VM
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/filedesc.h>
+#include <sys/resourcevar.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <sys/conf.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <vm/vm.h>
+#include <vm/vm_pager.h>
+#include <vm/vm_prot.h>
+
+#ifdef DEBUG
+int mmapdebug = 0;
+#define MDB_FOLLOW 0x01
+#define MDB_SYNC 0x02
+#define MDB_MAPIT 0x04
+#endif
+
+struct sbrk_args {
+ int incr;
+};
+/* ARGSUSED */
+int
+sbrk(p, uap, retval)
+ struct proc *p;
+ struct sbrk_args *uap;
+ int *retval;
+{
+
+ /* Not yet implemented */
+ return (EOPNOTSUPP);
+}
+
+struct sstk_args {
+ int incr;
+};
+/* ARGSUSED */
+int
+sstk(p, uap, retval)
+ struct proc *p;
+ struct sstk_args *uap;
+ int *retval;
+{
+
+ /* Not yet implemented */
+ return (EOPNOTSUPP);
+}
+
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+struct getpagesize_args {
+ int dummy;
+};
+/* ARGSUSED */
+int
+ogetpagesize(p, uap, retval)
+ struct proc *p;
+ struct getpagesize_args *uap;
+ int *retval;
+{
+
+ *retval = PAGE_SIZE;
+ return (0);
+}
+#endif /* COMPAT_43 || COMPAT_SUNOS */
+
+struct mmap_args {
+ caddr_t addr;
+ size_t len;
+ int prot;
+ int flags;
+ int fd;
+ long pad;
+ off_t pos;
+};
+
+#ifdef COMPAT_43
+struct ommap_args {
+ caddr_t addr;
+ int len;
+ int prot;
+ int flags;
+ int fd;
+ long pos;
+};
+int
+ommap(p, uap, retval)
+ struct proc *p;
+ register struct ommap_args *uap;
+ int *retval;
+{
+ struct mmap_args nargs;
+ static const char cvtbsdprot[8] = {
+ 0,
+ PROT_EXEC,
+ PROT_WRITE,
+ PROT_EXEC|PROT_WRITE,
+ PROT_READ,
+ PROT_EXEC|PROT_READ,
+ PROT_WRITE|PROT_READ,
+ PROT_EXEC|PROT_WRITE|PROT_READ,
+ };
+#define OMAP_ANON 0x0002
+#define OMAP_COPY 0x0020
+#define OMAP_SHARED 0x0010
+#define OMAP_FIXED 0x0100
+#define OMAP_INHERIT 0x0800
+
+ nargs.addr = uap->addr;
+ nargs.len = uap->len;
+ nargs.prot = cvtbsdprot[uap->prot&0x7];
+ nargs.flags = 0;
+ if (uap->flags & OMAP_ANON)
+ nargs.flags |= MAP_ANON;
+ if (uap->flags & OMAP_COPY)
+ nargs.flags |= MAP_COPY;
+ if (uap->flags & OMAP_SHARED)
+ nargs.flags |= MAP_SHARED;
+ else
+ nargs.flags |= MAP_PRIVATE;
+ if (uap->flags & OMAP_FIXED)
+ nargs.flags |= MAP_FIXED;
+ if (uap->flags & OMAP_INHERIT)
+ nargs.flags |= MAP_INHERIT;
+ nargs.fd = uap->fd;
+ nargs.pos = uap->pos;
+ return (mmap(p, &nargs, retval));
+}
+#endif
+
+int
+mmap(p, uap, retval)
+ struct proc *p;
+ register struct mmap_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ register struct file *fp;
+ struct vnode *vp;
+ vm_offset_t addr;
+ vm_size_t size;
+ vm_prot_t prot, maxprot;
+ caddr_t handle;
+ int flags, error;
+
+ prot = uap->prot & VM_PROT_ALL;
+ flags = uap->flags;
+#ifdef DEBUG
+ if (mmapdebug & MDB_FOLLOW)
+ printf("mmap(%d): addr %x len %x pro %x flg %x fd %d pos %x\n",
+ p->p_pid, uap->addr, uap->len, prot,
+ flags, uap->fd, (vm_offset_t)uap->pos);
+#endif
+ /*
+ * Address (if FIXED) must be page aligned.
+ * Size is implicitly rounded to a page boundary.
+ */
+ addr = (vm_offset_t) uap->addr;
+ if (((flags & MAP_FIXED) && (addr & PAGE_MASK)) ||
+ (ssize_t)uap->len < 0 || ((flags & MAP_ANON) && uap->fd != -1))
+ return (EINVAL);
+ size = (vm_size_t) round_page(uap->len);
+ /*
+ * Check for illegal addresses. Watch out for address wrap...
+ * Note that VM_*_ADDRESS are not constants due to casts (argh).
+ */
+ if (flags & MAP_FIXED) {
+ if (VM_MAXUSER_ADDRESS > 0 && addr + size >= VM_MAXUSER_ADDRESS)
+ return (EINVAL);
+#ifndef i386
+ if (VM_MIN_ADDRESS > 0 && addr < VM_MIN_ADDRESS)
+ return (EINVAL);
+#endif
+ if (addr > addr + size)
+ return (EINVAL);
+ }
+ /*
+ * XXX if no hint provided for a non-fixed mapping place it after
+ * the end of the largest possible heap.
+ *
+ * There should really be a pmap call to determine a reasonable
+ * location.
+ */
+ if (addr == 0 && (flags & MAP_FIXED) == 0)
+ addr = round_page(p->p_vmspace->vm_daddr + MAXDSIZ);
+ if (flags & MAP_ANON) {
+ /*
+ * Mapping blank space is trivial.
+ */
+ handle = NULL;
+ maxprot = VM_PROT_ALL;
+ } else {
+ /*
+ * Mapping file, get fp for validation.
+ * Obtain vnode and make sure it is of appropriate type.
+ */
+ if (((unsigned)uap->fd) >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL)
+ return (EBADF);
+ if (fp->f_type != DTYPE_VNODE)
+ return (EINVAL);
+ vp = (struct vnode *)fp->f_data;
+ if (vp->v_type != VREG && vp->v_type != VCHR)
+ return (EINVAL);
+ /*
+ * XXX hack to handle use of /dev/zero to map anon
+ * memory (ala SunOS).
+ */
+ if (vp->v_type == VCHR && iszerodev(vp->v_rdev)) {
+ handle = NULL;
+ maxprot = VM_PROT_ALL;
+ flags |= MAP_ANON;
+ } else {
+ /*
+ * Ensure that file and memory protections are
+ * compatible. Note that we only worry about
+ * writability if mapping is shared; in this case,
+ * current and max prot are dictated by the open file.
+ * XXX use the vnode instead? Problem is: what
+ * credentials do we use for determination?
+ * What if proc does a setuid?
+ */
+ maxprot = VM_PROT_EXECUTE; /* ??? */
+ if (fp->f_flag & FREAD)
+ maxprot |= VM_PROT_READ;
+ else if (prot & PROT_READ)
+ return (EACCES);
+ if (flags & MAP_SHARED) {
+ if (fp->f_flag & FWRITE)
+ maxprot |= VM_PROT_WRITE;
+ else if (prot & PROT_WRITE)
+ return (EACCES);
+ } else
+ maxprot |= VM_PROT_WRITE;
+ handle = (caddr_t)vp;
+ }
+ }
+ error = vm_mmap(&p->p_vmspace->vm_map, &addr, size, prot, maxprot,
+ flags, handle, (vm_offset_t)uap->pos);
+ if (error == 0)
+ *retval = (int)addr;
+ return (error);
+}
+
+struct msync_args {
+ caddr_t addr;
+ int len;
+};
+int
+msync(p, uap, retval)
+ struct proc *p;
+ struct msync_args *uap;
+ int *retval;
+{
+ vm_offset_t addr;
+ vm_size_t size;
+ vm_map_t map;
+ int rv;
+ boolean_t syncio, invalidate;
+
+#ifdef DEBUG
+ if (mmapdebug & (MDB_FOLLOW|MDB_SYNC))
+ printf("msync(%d): addr %x len %x\n",
+ p->p_pid, uap->addr, uap->len);
+#endif
+ if (((int)uap->addr & PAGE_MASK) || uap->addr + uap->len < uap->addr)
+ return (EINVAL);
+ map = &p->p_vmspace->vm_map;
+ addr = (vm_offset_t)uap->addr;
+ size = (vm_size_t)uap->len;
+ /*
+ * XXX Gak! If size is zero we are supposed to sync "all modified
+ * pages with the region containing addr". Unfortunately, we
+ * don't really keep track of individual mmaps so we approximate
+ * by flushing the range of the map entry containing addr.
+ * This can be incorrect if the region splits or is coalesced
+ * with a neighbor.
+ */
+ if (size == 0) {
+ vm_map_entry_t entry;
+
+ vm_map_lock_read(map);
+ rv = vm_map_lookup_entry(map, addr, &entry);
+ vm_map_unlock_read(map);
+ if (rv)
+ return (EINVAL);
+ addr = entry->start;
+ size = entry->end - entry->start;
+ }
+#ifdef DEBUG
+ if (mmapdebug & MDB_SYNC)
+ printf("msync: cleaning/flushing address range [%x-%x)\n",
+ addr, addr+size);
+#endif
+ /*
+ * Could pass this in as a third flag argument to implement
+ * Sun's MS_ASYNC.
+ */
+ syncio = TRUE;
+ /*
+ * XXX bummer, gotta flush all cached pages to ensure
+ * consistency with the file system cache. Otherwise, we could
+ * pass this in to implement Sun's MS_INVALIDATE.
+ */
+ invalidate = TRUE;
+ /*
+ * Clean the pages and interpret the return value.
+ */
+ rv = vm_map_clean(map, addr, addr+size, syncio, invalidate);
+ switch (rv) {
+ case KERN_SUCCESS:
+ break;
+ case KERN_INVALID_ADDRESS:
+ return (EINVAL); /* Sun returns ENOMEM? */
+ case KERN_FAILURE:
+ return (EIO);
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+struct munmap_args {
+ caddr_t addr;
+ int len;
+};
+int
+munmap(p, uap, retval)
+ register struct proc *p;
+ register struct munmap_args *uap;
+ int *retval;
+{
+ vm_offset_t addr;
+ vm_size_t size;
+ vm_map_t map;
+
+#ifdef DEBUG
+ if (mmapdebug & MDB_FOLLOW)
+ printf("munmap(%d): addr %x len %x\n",
+ p->p_pid, uap->addr, uap->len);
+#endif
+
+ addr = (vm_offset_t) uap->addr;
+ if ((addr & PAGE_MASK) || uap->len < 0)
+ return(EINVAL);
+ size = (vm_size_t) round_page(uap->len);
+ if (size == 0)
+ return(0);
+ /*
+ * Check for illegal addresses. Watch out for address wrap...
+ * Note that VM_*_ADDRESS are not constants due to casts (argh).
+ */
+ if (VM_MAXUSER_ADDRESS > 0 && addr + size >= VM_MAXUSER_ADDRESS)
+ return (EINVAL);
+#ifndef i386
+ if (VM_MIN_ADDRESS > 0 && addr < VM_MIN_ADDRESS)
+ return (EINVAL);
+#endif
+ if (addr > addr + size)
+ return (EINVAL);
+ map = &p->p_vmspace->vm_map;
+ /*
+ * Make sure entire range is allocated.
+ */
+ if (!vm_map_check_protection(map, addr, addr + size, VM_PROT_NONE))
+ return(EINVAL);
+ /* returns nothing but KERN_SUCCESS anyway */
+ (void) vm_map_remove(map, addr, addr+size);
+ return(0);
+}
+
+void
+munmapfd(fd)
+ int fd;
+{
+#ifdef DEBUG
+ if (mmapdebug & MDB_FOLLOW)
+ printf("munmapfd(%d): fd %d\n", curproc->p_pid, fd);
+#endif
+
+ /*
+ * XXX should vm_deallocate any regions mapped to this file
+ */
+ curproc->p_fd->fd_ofileflags[fd] &= ~UF_MAPPED;
+}
+
+struct mprotect_args {
+ caddr_t addr;
+ int len;
+ int prot;
+};
+int
+mprotect(p, uap, retval)
+ struct proc *p;
+ struct mprotect_args *uap;
+ int *retval;
+{
+ vm_offset_t addr;
+ vm_size_t size;
+ register vm_prot_t prot;
+
+#ifdef DEBUG
+ if (mmapdebug & MDB_FOLLOW)
+ printf("mprotect(%d): addr %x len %x prot %d\n",
+ p->p_pid, uap->addr, uap->len, uap->prot);
+#endif
+
+ addr = (vm_offset_t)uap->addr;
+ if ((addr & PAGE_MASK) || uap->len < 0)
+ return(EINVAL);
+ size = (vm_size_t)uap->len;
+ prot = uap->prot & VM_PROT_ALL;
+
+ switch (vm_map_protect(&p->p_vmspace->vm_map, addr, addr+size, prot,
+ FALSE)) {
+ case KERN_SUCCESS:
+ return (0);
+ case KERN_PROTECTION_FAILURE:
+ return (EACCES);
+ }
+ return (EINVAL);
+}
+
+struct madvise_args {
+ caddr_t addr;
+ int len;
+ int behav;
+};
+/* ARGSUSED */
+int
+madvise(p, uap, retval)
+ struct proc *p;
+ struct madvise_args *uap;
+ int *retval;
+{
+
+ /* Not yet implemented */
+ return (EOPNOTSUPP);
+}
+
+struct mincore_args {
+ caddr_t addr;
+ int len;
+ char *vec;
+};
+/* ARGSUSED */
+int
+mincore(p, uap, retval)
+ struct proc *p;
+ struct mincore_args *uap;
+ int *retval;
+{
+
+ /* Not yet implemented */
+ return (EOPNOTSUPP);
+}
+
+struct mlock_args {
+ caddr_t addr;
+ size_t len;
+};
+int
+mlock(p, uap, retval)
+ struct proc *p;
+ struct mlock_args *uap;
+ int *retval;
+{
+ vm_offset_t addr;
+ vm_size_t size;
+ int error;
+ extern int vm_page_max_wired;
+
+#ifdef DEBUG
+ if (mmapdebug & MDB_FOLLOW)
+ printf("mlock(%d): addr %x len %x\n",
+ p->p_pid, uap->addr, uap->len);
+#endif
+ addr = (vm_offset_t)uap->addr;
+ if ((addr & PAGE_MASK) || uap->addr + uap->len < uap->addr)
+ return (EINVAL);
+ size = round_page((vm_size_t)uap->len);
+ if (atop(size) + cnt.v_wire_count > vm_page_max_wired)
+ return (EAGAIN);
+#ifdef pmap_wired_count
+ if (size + ptoa(pmap_wired_count(vm_map_pmap(&p->p_vmspace->vm_map))) >
+ p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur)
+ return (EAGAIN);
+#else
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+#endif
+
+ error = vm_map_pageable(&p->p_vmspace->vm_map, addr, addr+size, FALSE);
+ return (error == KERN_SUCCESS ? 0 : ENOMEM);
+}
+
+struct munlock_args {
+ caddr_t addr;
+ size_t len;
+};
+int
+munlock(p, uap, retval)
+ struct proc *p;
+ struct munlock_args *uap;
+ int *retval;
+{
+ vm_offset_t addr;
+ vm_size_t size;
+ int error;
+
+#ifdef DEBUG
+ if (mmapdebug & MDB_FOLLOW)
+ printf("munlock(%d): addr %x len %x\n",
+ p->p_pid, uap->addr, uap->len);
+#endif
+ addr = (vm_offset_t)uap->addr;
+ if ((addr & PAGE_MASK) || uap->addr + uap->len < uap->addr)
+ return (EINVAL);
+#ifndef pmap_wired_count
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+#endif
+ size = round_page((vm_size_t)uap->len);
+
+ error = vm_map_pageable(&p->p_vmspace->vm_map, addr, addr+size, TRUE);
+ return (error == KERN_SUCCESS ? 0 : ENOMEM);
+}
+
+/*
+ * Internal version of mmap.
+ * Currently used by mmap, exec, and sys5 shared memory.
+ * Handle is either a vnode pointer or NULL for MAP_ANON.
+ */
+int
+vm_mmap(map, addr, size, prot, maxprot, flags, handle, foff)
+ register vm_map_t map;
+ register vm_offset_t *addr;
+ register vm_size_t size;
+ vm_prot_t prot, maxprot;
+ register int flags;
+ caddr_t handle; /* XXX should be vp */
+ vm_offset_t foff;
+{
+ register vm_pager_t pager;
+ boolean_t fitit;
+ vm_object_t object;
+ struct vnode *vp = NULL;
+ int type;
+ int rv = KERN_SUCCESS;
+
+ if (size == 0)
+ return (0);
+
+ if ((flags & MAP_FIXED) == 0) {
+ fitit = TRUE;
+ *addr = round_page(*addr);
+ } else {
+ fitit = FALSE;
+ (void)vm_deallocate(map, *addr, size);
+ }
+
+ /*
+ * Lookup/allocate pager. All except an unnamed anonymous lookup
+ * gain a reference to ensure continued existance of the object.
+ * (XXX the exception is to appease the pageout daemon)
+ */
+ if (flags & MAP_ANON)
+ type = PG_DFLT;
+ else {
+ vp = (struct vnode *)handle;
+ if (vp->v_type == VCHR) {
+ type = PG_DEVICE;
+ handle = (caddr_t)vp->v_rdev;
+ } else
+ type = PG_VNODE;
+ }
+ pager = vm_pager_allocate(type, handle, size, prot, foff);
+ if (pager == NULL)
+ return (type == PG_DEVICE ? EINVAL : ENOMEM);
+ /*
+ * Find object and release extra reference gained by lookup
+ */
+ object = vm_object_lookup(pager);
+ vm_object_deallocate(object);
+
+ /*
+ * Anonymous memory.
+ */
+ if (flags & MAP_ANON) {
+ rv = vm_allocate_with_pager(map, addr, size, fitit,
+ pager, foff, TRUE);
+ if (rv != KERN_SUCCESS) {
+ if (handle == NULL)
+ vm_pager_deallocate(pager);
+ else
+ vm_object_deallocate(object);
+ goto out;
+ }
+ /*
+ * Don't cache anonymous objects.
+ * Loses the reference gained by vm_pager_allocate.
+ * Note that object will be NULL when handle == NULL,
+ * this is ok since vm_allocate_with_pager has made
+ * sure that these objects are uncached.
+ */
+ (void) pager_cache(object, FALSE);
+#ifdef DEBUG
+ if (mmapdebug & MDB_MAPIT)
+ printf("vm_mmap(%d): ANON *addr %x size %x pager %x\n",
+ curproc->p_pid, *addr, size, pager);
+#endif
+ }
+ /*
+ * Must be a mapped file.
+ * Distinguish between character special and regular files.
+ */
+ else if (vp->v_type == VCHR) {
+ rv = vm_allocate_with_pager(map, addr, size, fitit,
+ pager, foff, FALSE);
+ /*
+ * Uncache the object and lose the reference gained
+ * by vm_pager_allocate(). If the call to
+ * vm_allocate_with_pager() was sucessful, then we
+ * gained an additional reference ensuring the object
+ * will continue to exist. If the call failed then
+ * the deallocate call below will terminate the
+ * object which is fine.
+ */
+ (void) pager_cache(object, FALSE);
+ if (rv != KERN_SUCCESS)
+ goto out;
+ }
+ /*
+ * A regular file
+ */
+ else {
+#ifdef DEBUG
+ if (object == NULL)
+ printf("vm_mmap: no object: vp %x, pager %x\n",
+ vp, pager);
+#endif
+ /*
+ * Map it directly.
+ * Allows modifications to go out to the vnode.
+ */
+ if (flags & MAP_SHARED) {
+ rv = vm_allocate_with_pager(map, addr, size,
+ fitit, pager,
+ foff, FALSE);
+ if (rv != KERN_SUCCESS) {
+ vm_object_deallocate(object);
+ goto out;
+ }
+ /*
+ * Don't cache the object. This is the easiest way
+ * of ensuring that data gets back to the filesystem
+ * because vnode_pager_deallocate() will fsync the
+ * vnode. pager_cache() will lose the extra ref.
+ */
+ if (prot & VM_PROT_WRITE)
+ pager_cache(object, FALSE);
+ else
+ vm_object_deallocate(object);
+ }
+ /*
+ * Copy-on-write of file. Two flavors.
+ * MAP_COPY is true COW, you essentially get a snapshot of
+ * the region at the time of mapping. MAP_PRIVATE means only
+ * that your changes are not reflected back to the object.
+ * Changes made by others will be seen.
+ */
+ else {
+ vm_map_t tmap;
+ vm_offset_t off;
+
+ /* locate and allocate the target address space */
+ rv = vm_map_find(map, NULL, (vm_offset_t)0,
+ addr, size, fitit);
+ if (rv != KERN_SUCCESS) {
+ vm_object_deallocate(object);
+ goto out;
+ }
+ tmap = vm_map_create(pmap_create(size), VM_MIN_ADDRESS,
+ VM_MIN_ADDRESS+size, TRUE);
+ off = VM_MIN_ADDRESS;
+ rv = vm_allocate_with_pager(tmap, &off, size,
+ TRUE, pager,
+ foff, FALSE);
+ if (rv != KERN_SUCCESS) {
+ vm_object_deallocate(object);
+ vm_map_deallocate(tmap);
+ goto out;
+ }
+ /*
+ * (XXX)
+ * MAP_PRIVATE implies that we see changes made by
+ * others. To ensure that we need to guarentee that
+ * no copy object is created (otherwise original
+ * pages would be pushed to the copy object and we
+ * would never see changes made by others). We
+ * totally sleeze it right now by marking the object
+ * internal temporarily.
+ */
+ if ((flags & MAP_COPY) == 0)
+ object->flags |= OBJ_INTERNAL;
+ rv = vm_map_copy(map, tmap, *addr, size, off,
+ FALSE, FALSE);
+ object->flags &= ~OBJ_INTERNAL;
+ /*
+ * (XXX)
+ * My oh my, this only gets worse...
+ * Force creation of a shadow object so that
+ * vm_map_fork will do the right thing.
+ */
+ if ((flags & MAP_COPY) == 0) {
+ vm_map_t tmap;
+ vm_map_entry_t tentry;
+ vm_object_t tobject;
+ vm_offset_t toffset;
+ vm_prot_t tprot;
+ boolean_t twired, tsu;
+
+ tmap = map;
+ vm_map_lookup(&tmap, *addr, VM_PROT_WRITE,
+ &tentry, &tobject, &toffset,
+ &tprot, &twired, &tsu);
+ vm_map_lookup_done(tmap, tentry);
+ }
+ /*
+ * (XXX)
+ * Map copy code cannot detect sharing unless a
+ * sharing map is involved. So we cheat and write
+ * protect everything ourselves.
+ */
+ vm_object_pmap_copy(object, foff, foff + size);
+ vm_object_deallocate(object);
+ vm_map_deallocate(tmap);
+ if (rv != KERN_SUCCESS)
+ goto out;
+ }
+#ifdef DEBUG
+ if (mmapdebug & MDB_MAPIT)
+ printf("vm_mmap(%d): FILE *addr %x size %x pager %x\n",
+ curproc->p_pid, *addr, size, pager);
+#endif
+ }
+ /*
+ * Correct protection (default is VM_PROT_ALL).
+ * If maxprot is different than prot, we must set both explicitly.
+ */
+ rv = KERN_SUCCESS;
+ if (maxprot != VM_PROT_ALL)
+ rv = vm_map_protect(map, *addr, *addr+size, maxprot, TRUE);
+ if (rv == KERN_SUCCESS && prot != maxprot)
+ rv = vm_map_protect(map, *addr, *addr+size, prot, FALSE);
+ if (rv != KERN_SUCCESS) {
+ (void) vm_deallocate(map, *addr, size);
+ goto out;
+ }
+ /*
+ * Shared memory is also shared with children.
+ */
+ if (flags & MAP_SHARED) {
+ rv = vm_map_inherit(map, *addr, *addr+size, VM_INHERIT_SHARE);
+ if (rv != KERN_SUCCESS) {
+ (void) vm_deallocate(map, *addr, size);
+ goto out;
+ }
+ }
+out:
+#ifdef DEBUG
+ if (mmapdebug & MDB_MAPIT)
+ printf("vm_mmap: rv %d\n", rv);
+#endif
+ switch (rv) {
+ case KERN_SUCCESS:
+ return (0);
+ case KERN_INVALID_ADDRESS:
+ case KERN_NO_SPACE:
+ return (ENOMEM);
+ case KERN_PROTECTION_FAILURE:
+ return (EACCES);
+ default:
+ return (EINVAL);
+ }
+}
diff --git a/sys/vm/vm_object.c b/sys/vm/vm_object.c
new file mode 100644
index 0000000..a6419dc
--- /dev/null
+++ b/sys/vm/vm_object.c
@@ -0,0 +1,1645 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_object.c 8.5 (Berkeley) 3/22/94
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Virtual memory object module.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pageout.h>
+
+static void _vm_object_allocate(vm_size_t, vm_object_t);
+void vm_object_deactivate_pages(vm_object_t);
+void vm_object_cache_trim(void);
+void vm_object_remove(vm_pager_t);
+
+/*
+ * Virtual memory objects maintain the actual data
+ * associated with allocated virtual memory. A given
+ * page of memory exists within exactly one object.
+ *
+ * An object is only deallocated when all "references"
+ * are given up. Only one "reference" to a given
+ * region of an object should be writeable.
+ *
+ * Associated with each object is a list of all resident
+ * memory pages belonging to that object; this list is
+ * maintained by the "vm_page" module, and locked by the object's
+ * lock.
+ *
+ * Each object also records a "pager" routine which is
+ * used to retrieve (and store) pages to the proper backing
+ * storage. In addition, objects may be backed by other
+ * objects from which they were virtual-copied.
+ *
+ * The only items within the object structure which are
+ * modified after time of creation are:
+ * reference count locked by object's lock
+ * pager routine locked by object's lock
+ *
+ */
+
+
+struct vm_object kernel_object_store;
+struct vm_object kmem_object_store;
+
+extern int vm_cache_max;
+#define VM_OBJECT_HASH_COUNT 157
+
+struct vm_object_hash_head vm_object_hashtable[VM_OBJECT_HASH_COUNT];
+
+long object_collapses = 0;
+long object_bypasses = 0;
+
+static void
+_vm_object_allocate(size, object)
+ vm_size_t size;
+ register vm_object_t object;
+{
+ bzero(object, sizeof *object);
+ TAILQ_INIT(&object->memq);
+ vm_object_lock_init(object);
+ object->ref_count = 1;
+ object->resident_page_count = 0;
+ object->size = size;
+ object->flags = OBJ_INTERNAL; /* vm_allocate_with_pager will reset */
+ object->paging_in_progress = 0;
+ object->copy = NULL;
+
+ /*
+ * Object starts out read-write, with no pager.
+ */
+
+ object->pager = NULL;
+ object->paging_offset = 0;
+ object->shadow = NULL;
+ object->shadow_offset = (vm_offset_t) 0;
+
+ simple_lock(&vm_object_list_lock);
+ TAILQ_INSERT_TAIL(&vm_object_list, object, object_list);
+ vm_object_count++;
+ cnt.v_nzfod += atop(size);
+ simple_unlock(&vm_object_list_lock);
+}
+
+/*
+ * vm_object_init:
+ *
+ * Initialize the VM objects module.
+ */
+void
+vm_object_init(vm_offset_t nothing)
+{
+ register int i;
+
+ TAILQ_INIT(&vm_object_cached_list);
+ TAILQ_INIT(&vm_object_list);
+ vm_object_count = 0;
+ simple_lock_init(&vm_cache_lock);
+ simple_lock_init(&vm_object_list_lock);
+
+ for (i = 0; i < VM_OBJECT_HASH_COUNT; i++)
+ TAILQ_INIT(&vm_object_hashtable[i]);
+
+ kernel_object = &kernel_object_store;
+ _vm_object_allocate(VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS,
+ kernel_object);
+
+ kmem_object = &kmem_object_store;
+ _vm_object_allocate(VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS,
+ kmem_object);
+}
+
+/*
+ * vm_object_allocate:
+ *
+ * Returns a new object with the given size.
+ */
+
+vm_object_t
+vm_object_allocate(size)
+ vm_size_t size;
+{
+ register vm_object_t result;
+ int s;
+
+ result = (vm_object_t)
+ malloc((u_long)sizeof *result, M_VMOBJ, M_WAITOK);
+
+
+ _vm_object_allocate(size, result);
+
+ return(result);
+}
+
+
+/*
+ * vm_object_reference:
+ *
+ * Gets another reference to the given object.
+ */
+inline void
+vm_object_reference(object)
+ register vm_object_t object;
+{
+ if (object == NULL)
+ return;
+
+ vm_object_lock(object);
+ object->ref_count++;
+ vm_object_unlock(object);
+}
+
+/*
+ * vm_object_deallocate:
+ *
+ * Release a reference to the specified object,
+ * gained either through a vm_object_allocate
+ * or a vm_object_reference call. When all references
+ * are gone, storage associated with this object
+ * may be relinquished.
+ *
+ * No object may be locked.
+ */
+void
+vm_object_deallocate(object)
+ vm_object_t object;
+{
+ vm_object_t temp;
+
+ while (object != NULL) {
+
+ /*
+ * The cache holds a reference (uncounted) to
+ * the object; we must lock it before removing
+ * the object.
+ */
+
+ vm_object_cache_lock();
+
+ /*
+ * Lose the reference
+ */
+ vm_object_lock(object);
+ if (--(object->ref_count) != 0) {
+
+ vm_object_unlock(object);
+ /*
+ * If there are still references, then
+ * we are done.
+ */
+ vm_object_cache_unlock();
+ return;
+ }
+
+ /*
+ * See if this object can persist. If so, enter
+ * it in the cache, then deactivate all of its
+ * pages.
+ */
+
+ if (object->flags & OBJ_CANPERSIST) {
+
+ TAILQ_INSERT_TAIL(&vm_object_cached_list, object,
+ cached_list);
+ vm_object_cached++;
+ vm_object_cache_unlock();
+
+/*
+ * this code segment was removed because it kills performance with
+ * large -- repetively used binaries. The functionality now resides
+ * in the pageout daemon
+ * vm_object_deactivate_pages(object);
+ */
+ vm_object_unlock(object);
+
+ vm_object_cache_trim();
+ return;
+ }
+
+ /*
+ * Make sure no one can look us up now.
+ */
+ vm_object_remove(object->pager);
+ vm_object_cache_unlock();
+
+ temp = object->shadow;
+ vm_object_terminate(object);
+ /* unlocks and deallocates object */
+ object = temp;
+ }
+}
+
+/*
+ * vm_object_terminate actually destroys the specified object, freeing
+ * up all previously used resources.
+ *
+ * The object must be locked.
+ */
+void
+vm_object_terminate(object)
+ register vm_object_t object;
+{
+ register vm_page_t p;
+ vm_object_t shadow_object;
+ int s;
+
+ /*
+ * Detach the object from its shadow if we are the shadow's
+ * copy.
+ */
+ if ((shadow_object = object->shadow) != NULL) {
+ vm_object_lock(shadow_object);
+ if (shadow_object->copy == object)
+ shadow_object->copy = NULL;
+/*
+ else if (shadow_object->copy != NULL)
+ panic("vm_object_terminate: copy/shadow inconsistency");
+*/
+ vm_object_unlock(shadow_object);
+ }
+
+ /*
+ * Wait until the pageout daemon is through
+ * with the object.
+ */
+
+ while (object->paging_in_progress) {
+ vm_object_sleep((int)object, object, FALSE);
+ vm_object_lock(object);
+ }
+
+ /*
+ * While the paging system is locked,
+ * pull the object's pages off the active
+ * and inactive queues. This keeps the
+ * pageout daemon from playing with them
+ * during vm_pager_deallocate.
+ *
+ * We can't free the pages yet, because the
+ * object's pager may have to write them out
+ * before deallocating the paging space.
+ */
+
+ for( p = object->memq.tqh_first; p; p=p->listq.tqe_next) {
+ VM_PAGE_CHECK(p);
+
+ vm_page_lock_queues();
+ s = splimp();
+ if (p->flags & PG_ACTIVE) {
+ TAILQ_REMOVE(&vm_page_queue_active, p, pageq);
+ p->flags &= ~PG_ACTIVE;
+ cnt.v_active_count--;
+ }
+
+ if (p->flags & PG_INACTIVE) {
+ TAILQ_REMOVE(&vm_page_queue_inactive, p, pageq);
+ p->flags &= ~PG_INACTIVE;
+ cnt.v_inactive_count--;
+ }
+ splx(s);
+ vm_page_unlock_queues();
+ }
+
+ vm_object_unlock(object);
+
+ if (object->paging_in_progress != 0)
+ panic("vm_object_deallocate: pageout in progress");
+
+ /*
+ * Clean and free the pages, as appropriate.
+ * All references to the object are gone,
+ * so we don't need to lock it.
+ */
+
+ if ((object->flags & OBJ_INTERNAL) == 0) {
+ vm_object_lock(object);
+ (void) vm_object_page_clean(object, 0, 0, TRUE, TRUE);
+ vm_object_unlock(object);
+ }
+
+ /*
+ * Now free the pages.
+ * For internal objects, this also removes them from paging queues.
+ */
+ while ((p = object->memq.tqh_first) != NULL) {
+ VM_PAGE_CHECK(p);
+ vm_page_lock_queues();
+ vm_page_free(p);
+ cnt.v_pfree++;
+ vm_page_unlock_queues();
+ }
+
+ /*
+ * Let the pager know object is dead.
+ */
+
+ if (object->pager != NULL)
+ vm_pager_deallocate(object->pager);
+
+
+ simple_lock(&vm_object_list_lock);
+ TAILQ_REMOVE(&vm_object_list, object, object_list);
+ vm_object_count--;
+ simple_unlock(&vm_object_list_lock);
+
+ /*
+ * Free the space for the object.
+ */
+
+ free((caddr_t)object, M_VMOBJ);
+}
+
+/*
+ * vm_object_page_clean
+ *
+ * Clean all dirty pages in the specified range of object.
+ * Leaves page on whatever queue it is currently on.
+ *
+ * Odd semantics: if start == end, we clean everything.
+ *
+ * The object must be locked.
+ */
+#if 1
+boolean_t
+vm_object_page_clean(object, start, end, syncio, de_queue)
+ register vm_object_t object;
+ register vm_offset_t start;
+ register vm_offset_t end;
+ boolean_t syncio;
+ boolean_t de_queue;
+{
+ register vm_page_t p, nextp;
+ int s;
+ int size;
+
+ if (object->pager == NULL)
+ return 1;
+
+ if (start != end) {
+ start = trunc_page(start);
+ end = round_page(end);
+ }
+ size = end - start;
+
+again:
+ /*
+ * Wait until the pageout daemon is through with the object.
+ */
+ while (object->paging_in_progress) {
+ vm_object_sleep((int)object, object, FALSE);
+ }
+
+ nextp = object->memq.tqh_first;
+ while ( (p = nextp) && ((start == end) || (size != 0) ) ) {
+ nextp = p->listq.tqe_next;
+ if (start == end || (p->offset >= start && p->offset < end)) {
+ if (p->flags & PG_BUSY)
+ continue;
+
+ size -= PAGE_SIZE;
+
+ if ((p->flags & PG_CLEAN)
+ && pmap_is_modified(VM_PAGE_TO_PHYS(p)))
+ p->flags &= ~PG_CLEAN;
+
+ if ((p->flags & PG_CLEAN) == 0) {
+ vm_pageout_clean(p,VM_PAGEOUT_FORCE);
+ goto again;
+ }
+ }
+ }
+ wakeup((caddr_t)object);
+ return 1;
+}
+#endif
+/*
+ * vm_object_page_clean
+ *
+ * Clean all dirty pages in the specified range of object.
+ * If syncio is TRUE, page cleaning is done synchronously.
+ * If de_queue is TRUE, pages are removed from any paging queue
+ * they were on, otherwise they are left on whatever queue they
+ * were on before the cleaning operation began.
+ *
+ * Odd semantics: if start == end, we clean everything.
+ *
+ * The object must be locked.
+ *
+ * Returns TRUE if all was well, FALSE if there was a pager error
+ * somewhere. We attempt to clean (and dequeue) all pages regardless
+ * of where an error occurs.
+ */
+#if 0
+boolean_t
+vm_object_page_clean(object, start, end, syncio, de_queue)
+ register vm_object_t object;
+ register vm_offset_t start;
+ register vm_offset_t end;
+ boolean_t syncio;
+ boolean_t de_queue;
+{
+ register vm_page_t p;
+ int onqueue;
+ boolean_t noerror = TRUE;
+
+ if (object == NULL)
+ return (TRUE);
+
+ /*
+ * If it is an internal object and there is no pager, attempt to
+ * allocate one. Note that vm_object_collapse may relocate one
+ * from a collapsed object so we must recheck afterward.
+ */
+ if ((object->flags & OBJ_INTERNAL) && object->pager == NULL) {
+ vm_object_collapse(object);
+ if (object->pager == NULL) {
+ vm_pager_t pager;
+
+ vm_object_unlock(object);
+ pager = vm_pager_allocate(PG_DFLT, (caddr_t)0,
+ object->size, VM_PROT_ALL,
+ (vm_offset_t)0);
+ if (pager)
+ vm_object_setpager(object, pager, 0, FALSE);
+ vm_object_lock(object);
+ }
+ }
+ if (object->pager == NULL)
+ return (FALSE);
+
+again:
+ /*
+ * Wait until the pageout daemon is through with the object.
+ */
+ while (object->paging_in_progress) {
+ vm_object_sleep((int)object, object, FALSE);
+ vm_object_lock(object);
+ }
+ /*
+ * Loop through the object page list cleaning as necessary.
+ */
+ for (p = object->memq.tqh_first; p != NULL; p = p->listq.tqe_next) {
+ onqueue = 0;
+ if ((start == end || p->offset >= start && p->offset < end) &&
+ !(p->flags & PG_FICTITIOUS)) {
+ if ((p->flags & PG_CLEAN) &&
+ pmap_is_modified(VM_PAGE_TO_PHYS(p)))
+ p->flags &= ~PG_CLEAN;
+ /*
+ * Remove the page from any paging queue.
+ * This needs to be done if either we have been
+ * explicitly asked to do so or it is about to
+ * be cleaned (see comment below).
+ */
+ if (de_queue || !(p->flags & PG_CLEAN)) {
+ vm_page_lock_queues();
+ if (p->flags & PG_ACTIVE) {
+ TAILQ_REMOVE(&vm_page_queue_active,
+ p, pageq);
+ p->flags &= ~PG_ACTIVE;
+ cnt.v_active_count--;
+ onqueue = 1;
+ } else if (p->flags & PG_INACTIVE) {
+ TAILQ_REMOVE(&vm_page_queue_inactive,
+ p, pageq);
+ p->flags &= ~PG_INACTIVE;
+ cnt.v_inactive_count--;
+ onqueue = -1;
+ } else
+ onqueue = 0;
+ vm_page_unlock_queues();
+ }
+ /*
+ * To ensure the state of the page doesn't change
+ * during the clean operation we do two things.
+ * First we set the busy bit and write-protect all
+ * mappings to ensure that write accesses to the
+ * page block (in vm_fault). Second, we remove
+ * the page from any paging queue to foil the
+ * pageout daemon (vm_pageout_scan).
+ */
+ pmap_page_protect(VM_PAGE_TO_PHYS(p), VM_PROT_READ);
+ if (!(p->flags & PG_CLEAN)) {
+ p->flags |= PG_BUSY;
+ object->paging_in_progress++;
+ vm_object_unlock(object);
+ /*
+ * XXX if put fails we mark the page as
+ * clean to avoid an infinite loop.
+ * Will loose changes to the page.
+ */
+ if (vm_pager_put(object->pager, p, syncio)) {
+ printf("%s: pager_put error\n",
+ "vm_object_page_clean");
+ p->flags |= PG_CLEAN;
+ noerror = FALSE;
+ }
+ vm_object_lock(object);
+ object->paging_in_progress--;
+ if (!de_queue && onqueue) {
+ vm_page_lock_queues();
+ if (onqueue > 0)
+ vm_page_activate(p);
+ else
+ vm_page_deactivate(p);
+ vm_page_unlock_queues();
+ }
+ p->flags &= ~PG_BUSY;
+ PAGE_WAKEUP(p);
+ goto again;
+ }
+ }
+ }
+ return (noerror);
+}
+#endif
+
+/*
+ * vm_object_deactivate_pages
+ *
+ * Deactivate all pages in the specified object. (Keep its pages
+ * in memory even though it is no longer referenced.)
+ *
+ * The object must be locked.
+ */
+void
+vm_object_deactivate_pages(object)
+ register vm_object_t object;
+{
+ register vm_page_t p, next;
+
+ for (p = object->memq.tqh_first; p != NULL; p = next) {
+ next = p->listq.tqe_next;
+ vm_page_lock_queues();
+ vm_page_deactivate(p);
+ vm_page_unlock_queues();
+ }
+}
+
+/*
+ * Trim the object cache to size.
+ */
+void
+vm_object_cache_trim()
+{
+ register vm_object_t object;
+
+ vm_object_cache_lock();
+ while (vm_object_cached > vm_cache_max) {
+ object = vm_object_cached_list.tqh_first;
+ vm_object_cache_unlock();
+
+ if (object != vm_object_lookup(object->pager))
+ panic("vm_object_deactivate: I'm sooo confused.");
+
+ pager_cache(object, FALSE);
+
+ vm_object_cache_lock();
+ }
+ vm_object_cache_unlock();
+}
+
+
+/*
+ * vm_object_pmap_copy:
+ *
+ * Makes all physical pages in the specified
+ * object range copy-on-write. No writeable
+ * references to these pages should remain.
+ *
+ * The object must *not* be locked.
+ */
+void vm_object_pmap_copy(object, start, end)
+ register vm_object_t object;
+ register vm_offset_t start;
+ register vm_offset_t end;
+{
+ register vm_page_t p;
+
+ if (object == NULL)
+ return;
+
+ vm_object_lock(object);
+ for (p = object->memq.tqh_first; p != NULL; p = p->listq.tqe_next) {
+ if ((start <= p->offset) && (p->offset < end)) {
+ pmap_page_protect(VM_PAGE_TO_PHYS(p), VM_PROT_READ);
+ p->flags |= PG_COPYONWRITE;
+ }
+ }
+ vm_object_unlock(object);
+}
+
+/*
+ * vm_object_pmap_remove:
+ *
+ * Removes all physical pages in the specified
+ * object range from all physical maps.
+ *
+ * The object must *not* be locked.
+ */
+void
+vm_object_pmap_remove(object, start, end)
+ register vm_object_t object;
+ register vm_offset_t start;
+ register vm_offset_t end;
+{
+ register vm_page_t p;
+
+ if (object == NULL)
+ return;
+
+ vm_object_lock(object);
+again:
+ for (p = object->memq.tqh_first; p != NULL; p = p->listq.tqe_next) {
+ if ((start <= p->offset) && (p->offset < end)) {
+ if (p->flags & PG_BUSY) {
+ p->flags |= PG_WANTED;
+ tsleep((caddr_t) p, PVM, "vmopmr", 0);
+ goto again;
+ }
+ pmap_page_protect(VM_PAGE_TO_PHYS(p), VM_PROT_NONE);
+ if ((p->flags & PG_CLEAN) == 0)
+ p->flags |= PG_LAUNDRY;
+ }
+ }
+ vm_object_unlock(object);
+}
+
+/*
+ * vm_object_copy:
+ *
+ * Create a new object which is a copy of an existing
+ * object, and mark all of the pages in the existing
+ * object 'copy-on-write'. The new object has one reference.
+ * Returns the new object.
+ *
+ * May defer the copy until later if the object is not backed
+ * up by a non-default pager.
+ */
+void vm_object_copy(src_object, src_offset, size,
+ dst_object, dst_offset, src_needs_copy)
+ register vm_object_t src_object;
+ vm_offset_t src_offset;
+ vm_size_t size;
+ vm_object_t *dst_object; /* OUT */
+ vm_offset_t *dst_offset; /* OUT */
+ boolean_t *src_needs_copy; /* OUT */
+{
+ register vm_object_t new_copy;
+ register vm_object_t old_copy;
+ vm_offset_t new_start, new_end;
+
+ register vm_page_t p;
+
+ if (src_object == NULL) {
+ /*
+ * Nothing to copy
+ */
+ *dst_object = NULL;
+ *dst_offset = 0;
+ *src_needs_copy = FALSE;
+ return;
+ }
+
+
+ /*
+ * If the object's pager is null_pager or the
+ * default pager, we don't have to make a copy
+ * of it. Instead, we set the needs copy flag and
+ * make a shadow later.
+ */
+
+ vm_object_lock(src_object);
+
+ /*
+ * Try to collapse the object before copying it.
+ */
+
+ vm_object_collapse(src_object);
+
+ if (src_object->pager == NULL ||
+ src_object->pager->pg_type == PG_SWAP ||
+ (src_object->flags & OBJ_INTERNAL)) {
+
+ /*
+ * Make another reference to the object
+ */
+ src_object->ref_count++;
+
+ /*
+ * Mark all of the pages copy-on-write.
+ */
+ for (p = src_object->memq.tqh_first; p; p = p->listq.tqe_next)
+ if (src_offset <= p->offset &&
+ p->offset < src_offset + size)
+ p->flags |= PG_COPYONWRITE;
+ vm_object_unlock(src_object);
+
+ *dst_object = src_object;
+ *dst_offset = src_offset;
+
+ /*
+ * Must make a shadow when write is desired
+ */
+ *src_needs_copy = TRUE;
+ return;
+ }
+
+
+ /*
+ * If the object has a pager, the pager wants to
+ * see all of the changes. We need a copy-object
+ * for the changed pages.
+ *
+ * If there is a copy-object, and it is empty,
+ * no changes have been made to the object since the
+ * copy-object was made. We can use the same copy-
+ * object.
+ */
+
+ Retry1:
+ old_copy = src_object->copy;
+ if (old_copy != NULL) {
+ /*
+ * Try to get the locks (out of order)
+ */
+ if (!vm_object_lock_try(old_copy)) {
+ vm_object_unlock(src_object);
+
+ /* should spin a bit here... */
+ vm_object_lock(src_object);
+ goto Retry1;
+ }
+
+ if (old_copy->resident_page_count == 0 &&
+ old_copy->pager == NULL) {
+ /*
+ * Return another reference to
+ * the existing copy-object.
+ */
+ old_copy->ref_count++;
+ vm_object_unlock(old_copy);
+ vm_object_unlock(src_object);
+ *dst_object = old_copy;
+ *dst_offset = src_offset;
+ *src_needs_copy = FALSE;
+ return;
+ }
+ vm_object_unlock(old_copy);
+ }
+ vm_object_unlock(src_object);
+
+ /*
+ * If the object has a pager, the pager wants
+ * to see all of the changes. We must make
+ * a copy-object and put the changed pages there.
+ *
+ * The copy-object is always made large enough to
+ * completely shadow the original object, since
+ * it may have several users who want to shadow
+ * the original object at different points.
+ */
+
+ new_copy = vm_object_allocate(src_object->size);
+
+ Retry2:
+ vm_object_lock(src_object);
+ /*
+ * Copy object may have changed while we were unlocked
+ */
+ old_copy = src_object->copy;
+ if (old_copy != NULL) {
+ /*
+ * Try to get the locks (out of order)
+ */
+ if (!vm_object_lock_try(old_copy)) {
+ vm_object_unlock(src_object);
+ goto Retry2;
+ }
+
+ /*
+ * Consistency check
+ */
+ if (old_copy->shadow != src_object ||
+ old_copy->shadow_offset != (vm_offset_t) 0)
+ panic("vm_object_copy: copy/shadow inconsistency");
+
+ /*
+ * Make the old copy-object shadow the new one.
+ * It will receive no more pages from the original
+ * object.
+ */
+
+ src_object->ref_count--; /* remove ref. from old_copy */
+ old_copy->shadow = new_copy;
+ new_copy->ref_count++; /* locking not needed - we
+ have the only pointer */
+ vm_object_unlock(old_copy); /* done with old_copy */
+ }
+
+ new_start = (vm_offset_t) 0; /* always shadow original at 0 */
+ new_end = (vm_offset_t) new_copy->size; /* for the whole object */
+
+ /*
+ * Point the new copy at the existing object.
+ */
+
+ new_copy->shadow = src_object;
+ new_copy->shadow_offset = new_start;
+ src_object->ref_count++;
+ src_object->copy = new_copy;
+
+ /*
+ * Mark all the affected pages of the existing object
+ * copy-on-write.
+ */
+ for (p = src_object->memq.tqh_first; p != NULL; p = p->listq.tqe_next)
+ if ((new_start <= p->offset) && (p->offset < new_end))
+ p->flags |= PG_COPYONWRITE;
+
+ vm_object_unlock(src_object);
+
+ *dst_object = new_copy;
+ *dst_offset = src_offset - new_start;
+ *src_needs_copy = FALSE;
+}
+
+/*
+ * vm_object_shadow:
+ *
+ * Create a new object which is backed by the
+ * specified existing object range. The source
+ * object reference is deallocated.
+ *
+ * The new object and offset into that object
+ * are returned in the source parameters.
+ */
+
+void
+vm_object_shadow(object, offset, length)
+ vm_object_t *object; /* IN/OUT */
+ vm_offset_t *offset; /* IN/OUT */
+ vm_size_t length;
+{
+ register vm_object_t source;
+ register vm_object_t result;
+
+ source = *object;
+
+ /*
+ * Allocate a new object with the given length
+ */
+
+ if ((result = vm_object_allocate(length)) == NULL)
+ panic("vm_object_shadow: no object for shadowing");
+
+ /*
+ * The new object shadows the source object, adding
+ * a reference to it. Our caller changes his reference
+ * to point to the new object, removing a reference to
+ * the source object. Net result: no change of reference
+ * count.
+ */
+ result->shadow = source;
+
+ /*
+ * Store the offset into the source object,
+ * and fix up the offset into the new object.
+ */
+
+ result->shadow_offset = *offset;
+
+ /*
+ * Return the new things
+ */
+
+ *offset = 0;
+ *object = result;
+}
+
+/*
+ * Set the specified object's pager to the specified pager.
+ */
+
+void
+vm_object_setpager(object, pager, paging_offset,
+ read_only)
+ vm_object_t object;
+ vm_pager_t pager;
+ vm_offset_t paging_offset;
+ boolean_t read_only;
+{
+#ifdef lint
+ read_only++; /* No longer used */
+#endif lint
+
+ vm_object_lock(object); /* XXX ? */
+ if (object->pager && object->pager != pager) {
+ panic("!!!pager already allocated!!!\n");
+ }
+ object->pager = pager;
+ object->paging_offset = paging_offset;
+ vm_object_unlock(object); /* XXX ? */
+}
+
+/*
+ * vm_object_hash hashes the pager/id pair.
+ */
+
+#define vm_object_hash(pager) \
+ (((unsigned)pager >> 5)%VM_OBJECT_HASH_COUNT)
+
+/*
+ * vm_object_lookup looks in the object cache for an object with the
+ * specified pager and paging id.
+ */
+
+vm_object_t vm_object_lookup(pager)
+ vm_pager_t pager;
+{
+ register vm_object_hash_entry_t entry;
+ vm_object_t object;
+
+ vm_object_cache_lock();
+
+ for (entry = vm_object_hashtable[vm_object_hash(pager)].tqh_first;
+ entry != NULL;
+ entry = entry->hash_links.tqe_next) {
+ object = entry->object;
+ if (object->pager == pager) {
+ vm_object_lock(object);
+ if (object->ref_count == 0) {
+ TAILQ_REMOVE(&vm_object_cached_list, object,
+ cached_list);
+ vm_object_cached--;
+ }
+ object->ref_count++;
+ vm_object_unlock(object);
+ vm_object_cache_unlock();
+ return(object);
+ }
+ }
+
+ vm_object_cache_unlock();
+ return(NULL);
+}
+
+/*
+ * vm_object_enter enters the specified object/pager/id into
+ * the hash table.
+ */
+
+void vm_object_enter(object, pager)
+ vm_object_t object;
+ vm_pager_t pager;
+{
+ struct vm_object_hash_head *bucket;
+ register vm_object_hash_entry_t entry;
+
+ /*
+ * We don't cache null objects, and we can't cache
+ * objects with the null pager.
+ */
+
+ if (object == NULL)
+ return;
+ if (pager == NULL)
+ return;
+
+ bucket = &vm_object_hashtable[vm_object_hash(pager)];
+ entry = (vm_object_hash_entry_t)
+ malloc((u_long)sizeof *entry, M_VMOBJHASH, M_WAITOK);
+ entry->object = object;
+ object->flags |= OBJ_CANPERSIST;
+
+ vm_object_cache_lock();
+ TAILQ_INSERT_TAIL(bucket, entry, hash_links);
+ vm_object_cache_unlock();
+}
+
+/*
+ * vm_object_remove:
+ *
+ * Remove the pager from the hash table.
+ * Note: This assumes that the object cache
+ * is locked. XXX this should be fixed
+ * by reorganizing vm_object_deallocate.
+ */
+void
+vm_object_remove(pager)
+ register vm_pager_t pager;
+{
+ struct vm_object_hash_head *bucket;
+ register vm_object_hash_entry_t entry;
+ register vm_object_t object;
+
+ bucket = &vm_object_hashtable[vm_object_hash(pager)];
+
+ for (entry = bucket->tqh_first;
+ entry != NULL;
+ entry = entry->hash_links.tqe_next) {
+ object = entry->object;
+ if (object->pager == pager) {
+ TAILQ_REMOVE(bucket, entry, hash_links);
+ free((caddr_t)entry, M_VMOBJHASH);
+ break;
+ }
+ }
+}
+
+boolean_t vm_object_collapse_allowed = TRUE;
+/*
+ * vm_object_collapse:
+ *
+ * Collapse an object with the object backing it.
+ * Pages in the backing object are moved into the
+ * parent, and the backing object is deallocated.
+ *
+ * Requires that the object be locked and the page
+ * queues be unlocked.
+ *
+ * This routine has significant changes by John S. Dyson
+ * to fix some swap memory leaks. 18 Dec 93
+ *
+ */
+void
+vm_object_collapse(object)
+ register vm_object_t object;
+
+{
+ register vm_object_t backing_object;
+ register vm_offset_t backing_offset;
+ register vm_size_t size;
+ register vm_offset_t new_offset;
+ register vm_page_t p, pp;
+
+ if (!vm_object_collapse_allowed)
+ return;
+
+ while (TRUE) {
+ /*
+ * Verify that the conditions are right for collapse:
+ *
+ * The object exists and no pages in it are currently
+ * being paged out.
+ */
+ if (object == NULL ||
+ object->paging_in_progress != 0)
+ return;
+
+ /*
+ * There is a backing object, and
+ */
+
+ if ((backing_object = object->shadow) == NULL)
+ return;
+
+ vm_object_lock(backing_object);
+ /*
+ * ...
+ * The backing object is not read_only,
+ * and no pages in the backing object are
+ * currently being paged out.
+ * The backing object is internal.
+ */
+
+ if ((backing_object->flags & OBJ_INTERNAL) == 0 ||
+ backing_object->paging_in_progress != 0) {
+ vm_object_unlock(backing_object);
+ return;
+ }
+
+ /*
+ * The backing object can't be a copy-object:
+ * the shadow_offset for the copy-object must stay
+ * as 0. Furthermore (for the 'we have all the
+ * pages' case), if we bypass backing_object and
+ * just shadow the next object in the chain, old
+ * pages from that object would then have to be copied
+ * BOTH into the (former) backing_object and into the
+ * parent object.
+ */
+ if (backing_object->shadow != NULL &&
+ backing_object->shadow->copy == backing_object) {
+ vm_object_unlock(backing_object);
+ return;
+ }
+
+ /*
+ * we can deal only with the swap pager
+ */
+ if ((object->pager &&
+ object->pager->pg_type != PG_SWAP) ||
+ (backing_object->pager &&
+ backing_object->pager->pg_type != PG_SWAP)) {
+ vm_object_unlock(backing_object);
+ return;
+ }
+
+
+ /*
+ * We know that we can either collapse the backing
+ * object (if the parent is the only reference to
+ * it) or (perhaps) remove the parent's reference
+ * to it.
+ */
+
+ backing_offset = object->shadow_offset;
+ size = object->size;
+
+ /*
+ * If there is exactly one reference to the backing
+ * object, we can collapse it into the parent.
+ */
+
+ if (backing_object->ref_count == 1) {
+
+ /*
+ * We can collapse the backing object.
+ *
+ * Move all in-memory pages from backing_object
+ * to the parent. Pages that have been paged out
+ * will be overwritten by any of the parent's
+ * pages that shadow them.
+ */
+
+ while (p = backing_object->memq.tqh_first) {
+
+ new_offset = (p->offset - backing_offset);
+
+ /*
+ * If the parent has a page here, or if
+ * this page falls outside the parent,
+ * dispose of it.
+ *
+ * Otherwise, move it as planned.
+ */
+
+ if (p->offset < backing_offset ||
+ new_offset >= size) {
+ vm_page_lock_queues();
+ vm_page_free(p);
+ vm_page_unlock_queues();
+ } else {
+ pp = vm_page_lookup(object, new_offset);
+ if (pp != NULL || (object->pager && vm_pager_has_page(object->pager,
+ object->paging_offset + new_offset))) {
+ vm_page_lock_queues();
+ vm_page_free(p);
+ vm_page_unlock_queues();
+ } else {
+ vm_page_rename(p, object, new_offset);
+ }
+ }
+ }
+
+ /*
+ * Move the pager from backing_object to object.
+ */
+
+ if (backing_object->pager) {
+ backing_object->paging_in_progress++;
+ if (object->pager) {
+ vm_pager_t bopager;
+ object->paging_in_progress++;
+ /*
+ * copy shadow object pages into ours
+ * and destroy unneeded pages in shadow object.
+ */
+ bopager = backing_object->pager;
+ backing_object->pager = NULL;
+ vm_object_remove(backing_object->pager);
+ swap_pager_copy(
+ bopager, backing_object->paging_offset,
+ object->pager, object->paging_offset,
+ object->shadow_offset);
+ object->paging_in_progress--;
+ if (object->paging_in_progress == 0)
+ wakeup((caddr_t)object);
+ } else {
+ object->paging_in_progress++;
+ /*
+ * grab the shadow objects pager
+ */
+ object->pager = backing_object->pager;
+ object->paging_offset = backing_object->paging_offset + backing_offset;
+ vm_object_remove(backing_object->pager);
+ backing_object->pager = NULL;
+ /*
+ * free unnecessary blocks
+ */
+ swap_pager_freespace(object->pager, 0, object->paging_offset);
+ object->paging_in_progress--;
+ if (object->paging_in_progress == 0)
+ wakeup((caddr_t)object);
+ }
+ backing_object->paging_in_progress--;
+ if (backing_object->paging_in_progress == 0)
+ wakeup((caddr_t)backing_object);
+ }
+
+
+ /*
+ * Object now shadows whatever backing_object did.
+ * Note that the reference to backing_object->shadow
+ * moves from within backing_object to within object.
+ */
+
+ object->shadow = backing_object->shadow;
+ object->shadow_offset += backing_object->shadow_offset;
+ if (object->shadow != NULL &&
+ object->shadow->copy != NULL) {
+ panic("vm_object_collapse: we collapsed a copy-object!");
+ }
+ /*
+ * Discard backing_object.
+ *
+ * Since the backing object has no pages, no
+ * pager left, and no object references within it,
+ * all that is necessary is to dispose of it.
+ */
+
+ vm_object_unlock(backing_object);
+
+ simple_lock(&vm_object_list_lock);
+ TAILQ_REMOVE(&vm_object_list, backing_object,
+ object_list);
+ vm_object_count--;
+ simple_unlock(&vm_object_list_lock);
+
+ free((caddr_t)backing_object, M_VMOBJ);
+
+ object_collapses++;
+ }
+ else {
+ /*
+ * If all of the pages in the backing object are
+ * shadowed by the parent object, the parent
+ * object no longer has to shadow the backing
+ * object; it can shadow the next one in the
+ * chain.
+ *
+ * The backing object must not be paged out - we'd
+ * have to check all of the paged-out pages, as
+ * well.
+ */
+
+ if (backing_object->pager != NULL) {
+ vm_object_unlock(backing_object);
+ return;
+ }
+
+ /*
+ * Should have a check for a 'small' number
+ * of pages here.
+ */
+
+ for( p = backing_object->memq.tqh_first;p;p=p->listq.tqe_next) {
+ new_offset = (p->offset - backing_offset);
+
+ /*
+ * If the parent has a page here, or if
+ * this page falls outside the parent,
+ * keep going.
+ *
+ * Otherwise, the backing_object must be
+ * left in the chain.
+ */
+
+ if (p->offset >= backing_offset &&
+ new_offset <= size &&
+ ((pp = vm_page_lookup(object, new_offset)) == NULL || (pp->flags & PG_FAKE)) &&
+ (!object->pager || !vm_pager_has_page(object->pager, object->paging_offset+new_offset))) {
+ /*
+ * Page still needed.
+ * Can't go any further.
+ */
+ vm_object_unlock(backing_object);
+ return;
+ }
+ }
+
+ /*
+ * Make the parent shadow the next object
+ * in the chain. Deallocating backing_object
+ * will not remove it, since its reference
+ * count is at least 2.
+ */
+
+ vm_object_reference(object->shadow = backing_object->shadow);
+ object->shadow_offset += backing_object->shadow_offset;
+
+ /*
+ * Backing object might have had a copy pointer
+ * to us. If it did, clear it.
+ */
+ if (backing_object->copy == object) {
+ backing_object->copy = NULL;
+ }
+
+ /* Drop the reference count on backing_object.
+ * Since its ref_count was at least 2, it
+ * will not vanish; so we don't need to call
+ * vm_object_deallocate.
+ */
+ if (backing_object->ref_count == 1)
+ printf("should have called obj deallocate\n");
+ backing_object->ref_count--;
+ vm_object_unlock(backing_object);
+
+ object_bypasses ++;
+
+ }
+
+ /*
+ * Try again with this object's new backing object.
+ */
+ }
+}
+
+/*
+ * vm_object_page_remove: [internal]
+ *
+ * Removes all physical pages in the specified
+ * object range from the object's list of pages.
+ *
+ * The object must be locked.
+ */
+void
+vm_object_page_remove(object, start, end)
+ register vm_object_t object;
+ register vm_offset_t start;
+ register vm_offset_t end;
+{
+ register vm_page_t p, next;
+ vm_offset_t size;
+ int cnt;
+ int s;
+
+ if (object == NULL)
+ return;
+
+ start = trunc_page(start);
+ end = round_page(end);
+again:
+ size = end-start;
+ if (size > 4*PAGE_SIZE || size >= object->size/4) {
+ for (p = object->memq.tqh_first; (p != NULL && size > 0); p = next) {
+ next = p->listq.tqe_next;
+ if ((start <= p->offset) && (p->offset < end)) {
+ if (p->flags & PG_BUSY) {
+ p->flags |= PG_WANTED;
+ tsleep((caddr_t) p, PVM, "vmopar", 0);
+ goto again;
+ }
+ pmap_page_protect(VM_PAGE_TO_PHYS(p), VM_PROT_NONE);
+ vm_page_lock_queues();
+ vm_page_free(p);
+ vm_page_unlock_queues();
+ size -= PAGE_SIZE;
+ }
+ }
+ } else {
+ while (size > 0) {
+ while (p = vm_page_lookup(object, start)) {
+ if (p->flags & PG_BUSY) {
+ p->flags |= PG_WANTED;
+ tsleep((caddr_t) p, PVM, "vmopar", 0);
+ goto again;
+ }
+ pmap_page_protect(VM_PAGE_TO_PHYS(p), VM_PROT_NONE);
+ vm_page_lock_queues();
+ vm_page_free(p);
+ vm_page_unlock_queues();
+ }
+ start += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ }
+}
+
+/*
+ * Routine: vm_object_coalesce
+ * Function: Coalesces two objects backing up adjoining
+ * regions of memory into a single object.
+ *
+ * returns TRUE if objects were combined.
+ *
+ * NOTE: Only works at the moment if the second object is NULL -
+ * if it's not, which object do we lock first?
+ *
+ * Parameters:
+ * prev_object First object to coalesce
+ * prev_offset Offset into prev_object
+ * next_object Second object into coalesce
+ * next_offset Offset into next_object
+ *
+ * prev_size Size of reference to prev_object
+ * next_size Size of reference to next_object
+ *
+ * Conditions:
+ * The object must *not* be locked.
+ */
+boolean_t vm_object_coalesce(prev_object, next_object,
+ prev_offset, next_offset,
+ prev_size, next_size)
+
+ register vm_object_t prev_object;
+ vm_object_t next_object;
+ vm_offset_t prev_offset, next_offset;
+ vm_size_t prev_size, next_size;
+{
+ vm_size_t newsize;
+
+#ifdef lint
+ next_offset++;
+#endif
+
+ if (next_object != NULL) {
+ return(FALSE);
+ }
+
+ if (prev_object == NULL) {
+ return(TRUE);
+ }
+
+ vm_object_lock(prev_object);
+
+ /*
+ * Try to collapse the object first
+ */
+ vm_object_collapse(prev_object);
+
+ /*
+ * Can't coalesce if:
+ * . more than one reference
+ * . paged out
+ * . shadows another object
+ * . has a copy elsewhere
+ * (any of which mean that the pages not mapped to
+ * prev_entry may be in use anyway)
+ */
+
+ if (prev_object->ref_count > 1 ||
+ prev_object->pager != NULL ||
+ prev_object->shadow != NULL ||
+ prev_object->copy != NULL) {
+ vm_object_unlock(prev_object);
+ return(FALSE);
+ }
+
+ /*
+ * Remove any pages that may still be in the object from
+ * a previous deallocation.
+ */
+
+ vm_object_page_remove(prev_object,
+ prev_offset + prev_size,
+ prev_offset + prev_size + next_size);
+
+ /*
+ * Extend the object if necessary.
+ */
+ newsize = prev_offset + prev_size + next_size;
+ if (newsize > prev_object->size)
+ prev_object->size = newsize;
+
+ vm_object_unlock(prev_object);
+ return(TRUE);
+}
+
+/*
+ * returns page after looking up in shadow chain
+ */
+
+vm_page_t
+vm_object_page_lookup(object, offset)
+ vm_object_t object;
+ vm_offset_t offset;
+{
+ vm_page_t m;
+ if (!(m=vm_page_lookup(object, offset))) {
+ if (!object->shadow)
+ return 0;
+ else
+ return vm_object_page_lookup(object->shadow, offset + object->shadow_offset);
+ }
+ return m;
+}
+
+#define DEBUG
+#if defined(DEBUG) || (NDDB > 0)
+/*
+ * vm_object_print: [ debug ]
+ */
+void vm_object_print(object, full)
+ vm_object_t object;
+ boolean_t full;
+{
+ register vm_page_t p;
+ extern indent;
+
+ register int count;
+
+ if (object == NULL)
+ return;
+
+ iprintf("Object 0x%x: size=0x%x, res=%d, ref=%d, ",
+ (int) object, (int) object->size,
+ object->resident_page_count, object->ref_count);
+ printf("pager=0x%x+0x%x, shadow=(0x%x)+0x%x\n",
+ (int) object->pager, (int) object->paging_offset,
+ (int) object->shadow, (int) object->shadow_offset);
+ printf("cache: next=0x%x, prev=0x%x\n",
+ object->cached_list.tqe_next, object->cached_list.tqe_prev);
+
+ if (!full)
+ return;
+
+ indent += 2;
+ count = 0;
+ for (p = object->memq.tqh_first; p != NULL; p = p->listq.tqe_next) {
+ if (count == 0)
+ iprintf("memory:=");
+ else if (count == 6) {
+ printf("\n");
+ iprintf(" ...");
+ count = 0;
+ } else
+ printf(",");
+ count++;
+
+ printf("(off=0x%x,page=0x%x)", p->offset, VM_PAGE_TO_PHYS(p));
+ }
+ if (count != 0)
+ printf("\n");
+ indent -= 2;
+}
+#endif /* defined(DEBUG) || (NDDB > 0) */
diff --git a/sys/vm/vm_object.h b/sys/vm/vm_object.h
new file mode 100644
index 0000000..5e220ac
--- /dev/null
+++ b/sys/vm/vm_object.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_object.h 8.3 (Berkeley) 1/12/94
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Virtual memory object module definitions.
+ */
+
+#ifndef _VM_OBJECT_
+#define _VM_OBJECT_
+
+#include <vm/vm_page.h>
+#include <vm/vm_pager.h>
+
+/*
+ * Types defined:
+ *
+ * vm_object_t Virtual memory object.
+ */
+
+struct vm_object {
+ struct pglist memq; /* Resident memory */
+ TAILQ_ENTRY(vm_object) object_list; /* list of all objects */
+ u_short flags; /* see below */
+ u_short paging_in_progress; /* Paging (in or out) so
+ don't collapse or destroy */
+ simple_lock_data_t Lock; /* Synchronization */
+ int ref_count; /* How many refs?? */
+ vm_size_t size; /* Object size */
+ int resident_page_count;
+ /* number of resident pages */
+ struct vm_object *copy; /* Object that holds copies of
+ my changed pages */
+ vm_pager_t pager; /* Where to get data */
+ vm_offset_t paging_offset; /* Offset into paging space */
+ struct vm_object *shadow; /* My shadow */
+ vm_offset_t shadow_offset; /* Offset in shadow */
+ TAILQ_ENTRY(vm_object) cached_list; /* for persistence */
+};
+/*
+ * Flags
+ */
+#define OBJ_CANPERSIST 0x0001 /* allow to persist */
+#define OBJ_INTERNAL 0x0002 /* internally created object */
+#define OBJ_ACTIVE 0x0004 /* used to mark active objects */
+
+TAILQ_HEAD(vm_object_hash_head, vm_object_hash_entry);
+
+struct vm_object_hash_entry {
+ TAILQ_ENTRY(vm_object_hash_entry) hash_links; /* hash chain links */
+ vm_object_t object; /* object represened */
+};
+
+typedef struct vm_object_hash_entry *vm_object_hash_entry_t;
+
+#ifdef KERNEL
+TAILQ_HEAD(object_q, vm_object);
+
+struct object_q vm_object_cached_list; /* list of objects persisting */
+int vm_object_cached; /* size of cached list */
+simple_lock_data_t vm_cache_lock; /* lock for object cache */
+
+struct object_q vm_object_list; /* list of allocated objects */
+long vm_object_count; /* count of all objects */
+simple_lock_data_t vm_object_list_lock;
+ /* lock for object list and count */
+
+vm_object_t kernel_object; /* the single kernel object */
+vm_object_t kmem_object;
+
+#define vm_object_cache_lock() simple_lock(&vm_cache_lock)
+#define vm_object_cache_unlock() simple_unlock(&vm_cache_lock)
+#endif /* KERNEL */
+
+#define vm_object_lock_init(object) simple_lock_init(&(object)->Lock)
+#define vm_object_lock(object) simple_lock(&(object)->Lock)
+#define vm_object_unlock(object) simple_unlock(&(object)->Lock)
+#define vm_object_lock_try(object) simple_lock_try(&(object)->Lock)
+#define vm_object_sleep(event, object, interruptible) \
+ thread_sleep((event), &(object)->Lock, (interruptible))
+
+#ifdef KERNEL
+vm_object_t vm_object_allocate __P((vm_size_t));
+void vm_object_cache_clear __P((void));
+void vm_object_cache_trim __P((void));
+boolean_t vm_object_coalesce __P((vm_object_t, vm_object_t,
+ vm_offset_t, vm_offset_t, vm_offset_t, vm_size_t));
+void vm_object_collapse __P((vm_object_t));
+void vm_object_copy __P((vm_object_t, vm_offset_t, vm_size_t,
+ vm_object_t *, vm_offset_t *, boolean_t *));
+void vm_object_deactivate_pages __P((vm_object_t));
+void vm_object_deallocate __P((vm_object_t));
+void vm_object_enter __P((vm_object_t, vm_pager_t));
+void vm_object_init __P((vm_size_t));
+vm_object_t vm_object_lookup __P((vm_pager_t));
+boolean_t vm_object_page_clean __P((vm_object_t,
+ vm_offset_t, vm_offset_t, boolean_t, boolean_t));
+void vm_object_page_remove __P((vm_object_t,
+ vm_offset_t, vm_offset_t));
+void vm_object_pmap_copy __P((vm_object_t,
+ vm_offset_t, vm_offset_t));
+void vm_object_pmap_remove __P((vm_object_t,
+ vm_offset_t, vm_offset_t));
+void vm_object_print __P((vm_object_t, boolean_t));
+void vm_object_reference __P((vm_object_t));
+void vm_object_remove __P((vm_pager_t));
+void vm_object_setpager __P((vm_object_t,
+ vm_pager_t, vm_offset_t, boolean_t));
+void vm_object_shadow __P((vm_object_t *,
+ vm_offset_t *, vm_size_t));
+void vm_object_terminate __P((vm_object_t));
+#endif
+#endif /* _VM_OBJECT_ */
diff --git a/sys/vm/vm_page.c b/sys/vm/vm_page.c
new file mode 100644
index 0000000..4304100
--- /dev/null
+++ b/sys/vm/vm_page.c
@@ -0,0 +1,879 @@
+/*
+ * Copyright (c) 1991 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)vm_page.c 7.4 (Berkeley) 5/7/91
+ * $Id: vm_page.c,v 1.2 1994/05/25 09:20:05 rgrimes Exp $
+ */
+
+/*
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Resident memory management module.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+
+#include <vm/vm.h>
+#include <vm/vm_page.h>
+#include <vm/vm_map.h>
+#include <vm/vm_pageout.h>
+
+/*
+ * Associated with page of user-allocatable memory is a
+ * page structure.
+ */
+
+struct pglist *vm_page_buckets; /* Array of buckets */
+int vm_page_bucket_count = 0; /* How big is array? */
+int vm_page_hash_mask; /* Mask for hash function */
+simple_lock_data_t bucket_lock; /* lock for all buckets XXX */
+
+struct pglist vm_page_queue_free;
+struct pglist vm_page_queue_active;
+struct pglist vm_page_queue_inactive;
+simple_lock_data_t vm_page_queue_lock;
+simple_lock_data_t vm_page_queue_free_lock;
+
+/* has physical page allocation been initialized? */
+boolean_t vm_page_startup_initialized;
+
+vm_page_t vm_page_array;
+long first_page;
+long last_page;
+vm_offset_t first_phys_addr;
+vm_offset_t last_phys_addr;
+vm_size_t page_mask;
+int page_shift;
+
+/*
+ * vm_set_page_size:
+ *
+ * Sets the page size, perhaps based upon the memory
+ * size. Must be called before any use of page-size
+ * dependent functions.
+ *
+ * Sets page_shift and page_mask from cnt.v_page_size.
+ */
+void vm_set_page_size()
+{
+
+ if (cnt.v_page_size == 0)
+ cnt.v_page_size = DEFAULT_PAGE_SIZE;
+ page_mask = cnt.v_page_size - 1;
+ if ((page_mask & cnt.v_page_size) != 0)
+ panic("vm_set_page_size: page size not a power of two");
+ for (page_shift = 0; ; page_shift++)
+ if ((1 << page_shift) == cnt.v_page_size)
+ break;
+}
+
+/*
+ * vm_page_startup:
+ *
+ * Initializes the resident memory module.
+ *
+ * Allocates memory for the page cells, and
+ * for the object/offset-to-page hash table headers.
+ * Each page cell is initialized and placed on the free list.
+ */
+
+vm_offset_t
+vm_page_startup(starta, enda, vaddr)
+ register vm_offset_t starta;
+ vm_offset_t enda;
+ register vm_offset_t vaddr;
+{
+ register vm_offset_t mapped;
+ register vm_page_t m;
+ register struct pglist *bucket;
+ vm_size_t npages, page_range;
+ register vm_offset_t new_start;
+ int i;
+ vm_offset_t pa;
+ int nblocks;
+ vm_offset_t first_managed_page;
+ int size;
+
+ extern vm_offset_t kentry_data;
+ extern vm_size_t kentry_data_size;
+ extern vm_offset_t phys_avail[];
+/* the biggest memory array is the second group of pages */
+ vm_offset_t start;
+ vm_offset_t biggestone, biggestsize;
+
+ vm_offset_t total;
+
+ total = 0;
+ biggestsize = 0;
+ biggestone = 0;
+ nblocks = 0;
+ vaddr = round_page(vaddr);
+
+ for (i = 0; phys_avail[i + 1]; i += 2) {
+ phys_avail[i] = round_page(phys_avail[i]);
+ phys_avail[i+1] = trunc_page(phys_avail[i+1]);
+ }
+
+ for (i = 0; phys_avail[i + 1]; i += 2) {
+ int size = phys_avail[i+1] - phys_avail[i];
+ if (size > biggestsize) {
+ biggestone = i;
+ biggestsize = size;
+ }
+ ++nblocks;
+ total += size;
+ }
+
+ start = phys_avail[biggestone];
+
+
+ /*
+ * Initialize the locks
+ */
+
+ simple_lock_init(&vm_page_queue_free_lock);
+ simple_lock_init(&vm_page_queue_lock);
+
+ /*
+ * Initialize the queue headers for the free queue,
+ * the active queue and the inactive queue.
+ */
+
+ TAILQ_INIT(&vm_page_queue_free);
+ TAILQ_INIT(&vm_page_queue_active);
+ TAILQ_INIT(&vm_page_queue_inactive);
+
+ /*
+ * Allocate (and initialize) the hash table buckets.
+ *
+ * The number of buckets MUST BE a power of 2, and
+ * the actual value is the next power of 2 greater
+ * than the number of physical pages in the system.
+ *
+ * Note:
+ * This computation can be tweaked if desired.
+ */
+ vm_page_buckets = (struct pglist *)vaddr;
+ bucket = vm_page_buckets;
+ if (vm_page_bucket_count == 0) {
+ vm_page_bucket_count = 1;
+ while (vm_page_bucket_count < atop(total))
+ vm_page_bucket_count <<= 1;
+ }
+
+
+ vm_page_hash_mask = vm_page_bucket_count - 1;
+
+ /*
+ * Validate these addresses.
+ */
+
+ new_start = start + vm_page_bucket_count * sizeof(struct pglist);
+ new_start = round_page(new_start);
+ mapped = vaddr;
+ vaddr = pmap_map(mapped, start, new_start,
+ VM_PROT_READ|VM_PROT_WRITE);
+ start = new_start;
+ bzero((caddr_t) mapped, vaddr - mapped);
+ mapped = vaddr;
+
+ for (i = 0; i< vm_page_bucket_count; i++) {
+ TAILQ_INIT(bucket);
+ bucket++;
+ }
+
+ simple_lock_init(&bucket_lock);
+
+ /*
+ * round (or truncate) the addresses to our page size.
+ */
+
+ /*
+ * Pre-allocate maps and map entries that cannot be dynamically
+ * allocated via malloc(). The maps include the kernel_map and
+ * kmem_map which must be initialized before malloc() will
+ * work (obviously). Also could include pager maps which would
+ * be allocated before kmeminit.
+ *
+ * Allow some kernel map entries... this should be plenty
+ * since people shouldn't be cluttering up the kernel
+ * map (they should use their own maps).
+ */
+
+ kentry_data_size = MAX_KMAP * sizeof(struct vm_map) +
+ MAX_KMAPENT * sizeof(struct vm_map_entry);
+ kentry_data_size = round_page(kentry_data_size);
+ kentry_data = (vm_offset_t) vaddr;
+ vaddr += kentry_data_size;
+
+ /*
+ * Validate these zone addresses.
+ */
+
+ new_start = start + (vaddr - mapped);
+ pmap_map(mapped, start, new_start, VM_PROT_READ|VM_PROT_WRITE);
+ bzero((caddr_t) mapped, (vaddr - mapped));
+ start = round_page(new_start);
+
+ /*
+ * Compute the number of pages of memory that will be
+ * available for use (taking into account the overhead
+ * of a page structure per page).
+ */
+
+ npages = (total - (start - phys_avail[biggestone])) / (PAGE_SIZE + sizeof(struct vm_page));
+ first_page = phys_avail[0] / PAGE_SIZE;
+
+ page_range = (phys_avail[(nblocks-1)*2 + 1] - phys_avail[0]) / PAGE_SIZE;
+ /*
+ * Initialize the mem entry structures now, and
+ * put them in the free queue.
+ */
+
+ vm_page_array = (vm_page_t) vaddr;
+ mapped = vaddr;
+
+
+ /*
+ * Validate these addresses.
+ */
+
+ new_start = round_page(start + page_range * sizeof (struct vm_page));
+ mapped = pmap_map(mapped, start, new_start,
+ VM_PROT_READ|VM_PROT_WRITE);
+ start = new_start;
+
+ first_managed_page = start / PAGE_SIZE;
+
+ /*
+ * Clear all of the page structures
+ */
+ bzero((caddr_t)vm_page_array, page_range * sizeof(struct vm_page));
+
+ cnt.v_page_count = 0;
+ cnt.v_free_count= 0;
+ for (i = 0; phys_avail[i + 1] && npages > 0; i += 2) {
+ if (i == biggestone)
+ pa = ptoa(first_managed_page);
+ else
+ pa = phys_avail[i];
+ while (pa < phys_avail[i + 1] && npages-- > 0) {
+ ++cnt.v_page_count;
+ ++cnt.v_free_count;
+ m = PHYS_TO_VM_PAGE(pa);
+ m->flags = 0;
+ m->object = 0;
+ m->phys_addr = pa;
+ m->hold_count = 0;
+ TAILQ_INSERT_TAIL(&vm_page_queue_free, m, pageq);
+ pa += PAGE_SIZE;
+ }
+ }
+
+ /*
+ * Initialize vm_pages_needed lock here - don't wait for pageout
+ * daemon XXX
+ */
+ simple_lock_init(&vm_pages_needed_lock);
+
+ return(mapped);
+}
+
+/*
+ * vm_page_hash:
+ *
+ * Distributes the object/offset key pair among hash buckets.
+ *
+ * NOTE: This macro depends on vm_page_bucket_count being a power of 2.
+ */
+inline const int
+vm_page_hash(object, offset)
+ vm_object_t object;
+ vm_offset_t offset;
+{
+ return ((unsigned)object + offset/NBPG) & vm_page_hash_mask;
+}
+
+/*
+ * vm_page_insert: [ internal use only ]
+ *
+ * Inserts the given mem entry into the object/object-page
+ * table and object list.
+ *
+ * The object and page must be locked.
+ */
+
+void vm_page_insert(mem, object, offset)
+ register vm_page_t mem;
+ register vm_object_t object;
+ register vm_offset_t offset;
+{
+ register struct pglist *bucket;
+ int s;
+
+ VM_PAGE_CHECK(mem);
+
+ if (mem->flags & PG_TABLED)
+ panic("vm_page_insert: already inserted");
+
+ /*
+ * Record the object/offset pair in this page
+ */
+
+ mem->object = object;
+ mem->offset = offset;
+
+ /*
+ * Insert it into the object_object/offset hash table
+ */
+
+ bucket = &vm_page_buckets[vm_page_hash(object, offset)];
+ s = splimp();
+ simple_lock(&bucket_lock);
+ TAILQ_INSERT_TAIL(bucket, mem, hashq);
+ simple_unlock(&bucket_lock);
+ (void) splx(s);
+
+ /*
+ * Now link into the object's list of backed pages.
+ */
+
+ TAILQ_INSERT_TAIL(&object->memq, mem, listq);
+ mem->flags |= PG_TABLED;
+
+ /*
+ * And show that the object has one more resident
+ * page.
+ */
+
+ object->resident_page_count++;
+}
+
+/*
+ * vm_page_remove: [ internal use only ]
+ * NOTE: used by device pager as well -wfj
+ *
+ * Removes the given mem entry from the object/offset-page
+ * table and the object page list.
+ *
+ * The object and page must be locked.
+ */
+
+void vm_page_remove(mem)
+ register vm_page_t mem;
+{
+ register struct pglist *bucket;
+ int s;
+
+ VM_PAGE_CHECK(mem);
+
+ if (!(mem->flags & PG_TABLED))
+ return;
+
+ /*
+ * Remove from the object_object/offset hash table
+ */
+
+ bucket = &vm_page_buckets[vm_page_hash(mem->object, mem->offset)];
+ s = splimp();
+ simple_lock(&bucket_lock);
+ TAILQ_REMOVE(bucket, mem, hashq);
+ simple_unlock(&bucket_lock);
+ (void) splx(s);
+
+ /*
+ * Now remove from the object's list of backed pages.
+ */
+
+ TAILQ_REMOVE(&mem->object->memq, mem, listq);
+
+ /*
+ * And show that the object has one fewer resident
+ * page.
+ */
+
+ mem->object->resident_page_count--;
+
+ mem->flags &= ~PG_TABLED;
+}
+
+/*
+ * vm_page_lookup:
+ *
+ * Returns the page associated with the object/offset
+ * pair specified; if none is found, NULL is returned.
+ *
+ * The object must be locked. No side effects.
+ */
+
+vm_page_t vm_page_lookup(object, offset)
+ register vm_object_t object;
+ register vm_offset_t offset;
+{
+ register vm_page_t mem;
+ register struct pglist *bucket;
+ int s;
+
+ /*
+ * Search the hash table for this object/offset pair
+ */
+
+ bucket = &vm_page_buckets[vm_page_hash(object, offset)];
+
+ s = splimp();
+ simple_lock(&bucket_lock);
+ for (mem = bucket->tqh_first; mem != NULL; mem = mem->hashq.tqe_next) {
+ VM_PAGE_CHECK(mem);
+ if ((mem->object == object) && (mem->offset == offset)) {
+ simple_unlock(&bucket_lock);
+ splx(s);
+ return(mem);
+ }
+ }
+
+ simple_unlock(&bucket_lock);
+ splx(s);
+ return(NULL);
+}
+
+/*
+ * vm_page_rename:
+ *
+ * Move the given memory entry from its
+ * current object to the specified target object/offset.
+ *
+ * The object must be locked.
+ */
+void vm_page_rename(mem, new_object, new_offset)
+ register vm_page_t mem;
+ register vm_object_t new_object;
+ vm_offset_t new_offset;
+{
+ if (mem->object == new_object)
+ return;
+
+ vm_page_lock_queues(); /* keep page from moving out from
+ under pageout daemon */
+ vm_page_remove(mem);
+ vm_page_insert(mem, new_object, new_offset);
+ vm_page_unlock_queues();
+}
+
+/*
+ * vm_page_alloc:
+ *
+ * Allocate and return a memory cell associated
+ * with this VM object/offset pair.
+ *
+ * Object must be locked.
+ */
+vm_page_t
+vm_page_alloc(object, offset)
+ vm_object_t object;
+ vm_offset_t offset;
+{
+ register vm_page_t mem;
+ int s;
+
+ s = splimp();
+ simple_lock(&vm_page_queue_free_lock);
+ if ( object != kernel_object &&
+ object != kmem_object &&
+ curproc != pageproc && curproc != &proc0 &&
+ cnt.v_free_count < cnt.v_free_reserved) {
+
+ simple_unlock(&vm_page_queue_free_lock);
+ splx(s);
+ /*
+ * this wakeup seems unnecessary, but there is code that
+ * might just check to see if there are free pages, and
+ * punt if there aren't. VM_WAIT does this too, but
+ * redundant wakeups aren't that bad...
+ */
+ if (curproc != pageproc)
+ wakeup((caddr_t) &vm_pages_needed);
+ return(NULL);
+ }
+ if (( mem = vm_page_queue_free.tqh_first) == 0) {
+ simple_unlock(&vm_page_queue_free_lock);
+ printf("No pages???\n");
+ splx(s);
+ /*
+ * comment above re: wakeups applies here too...
+ */
+ if (curproc != pageproc)
+ wakeup((caddr_t) &vm_pages_needed);
+ return(NULL);
+ }
+
+ TAILQ_REMOVE(&vm_page_queue_free, mem, pageq);
+
+ cnt.v_free_count--;
+ simple_unlock(&vm_page_queue_free_lock);
+
+ VM_PAGE_INIT(mem, object, offset);
+ splx(s);
+
+/*
+ * don't wakeup too often, so we wakeup the pageout daemon when
+ * we would be nearly out of memory.
+ */
+ if (curproc != pageproc &&
+ (cnt.v_free_count < cnt.v_free_reserved))
+ wakeup((caddr_t) &vm_pages_needed);
+
+ return(mem);
+}
+
+/*
+ * vm_page_free:
+ *
+ * Returns the given page to the free list,
+ * disassociating it with any VM object.
+ *
+ * Object and page must be locked prior to entry.
+ */
+void vm_page_free(mem)
+ register vm_page_t mem;
+{
+ int s;
+ s = splimp();
+ vm_page_remove(mem);
+ if (mem->flags & PG_ACTIVE) {
+ TAILQ_REMOVE(&vm_page_queue_active, mem, pageq);
+ mem->flags &= ~PG_ACTIVE;
+ cnt.v_active_count--;
+ }
+
+ if (mem->flags & PG_INACTIVE) {
+ TAILQ_REMOVE(&vm_page_queue_inactive, mem, pageq);
+ mem->flags &= ~PG_INACTIVE;
+ cnt.v_inactive_count--;
+ }
+
+ if (!(mem->flags & PG_FICTITIOUS)) {
+
+ simple_lock(&vm_page_queue_free_lock);
+ if (mem->wire_count) {
+ cnt.v_wire_count--;
+ mem->wire_count = 0;
+ }
+ TAILQ_INSERT_TAIL(&vm_page_queue_free, mem, pageq);
+
+ cnt.v_free_count++;
+ simple_unlock(&vm_page_queue_free_lock);
+ splx(s);
+ /*
+ * if pageout daemon needs pages, then tell it that there
+ * are some free.
+ */
+ if (vm_pageout_pages_needed)
+ wakeup((caddr_t)&vm_pageout_pages_needed);
+
+ /*
+ * wakeup processes that are waiting on memory if we
+ * hit a high water mark.
+ */
+ if (cnt.v_free_count == cnt.v_free_min) {
+ wakeup((caddr_t)&cnt.v_free_count);
+ }
+
+ /*
+ * wakeup scheduler process if we have lots of memory.
+ * this process will swapin processes.
+ */
+ if (cnt.v_free_count == cnt.v_free_target) {
+ wakeup((caddr_t)&proc0);
+ }
+ } else {
+ splx(s);
+ }
+ wakeup((caddr_t) mem);
+}
+
+
+/*
+ * vm_page_wire:
+ *
+ * Mark this page as wired down by yet
+ * another map, removing it from paging queues
+ * as necessary.
+ *
+ * The page queues must be locked.
+ */
+void vm_page_wire(mem)
+ register vm_page_t mem;
+{
+ int s;
+ VM_PAGE_CHECK(mem);
+
+ if (mem->wire_count == 0) {
+ s = splimp();
+ if (mem->flags & PG_ACTIVE) {
+ TAILQ_REMOVE(&vm_page_queue_active, mem, pageq);
+ cnt.v_active_count--;
+ mem->flags &= ~PG_ACTIVE;
+ }
+ if (mem->flags & PG_INACTIVE) {
+ TAILQ_REMOVE(&vm_page_queue_inactive, mem, pageq);
+ cnt.v_inactive_count--;
+ mem->flags &= ~PG_INACTIVE;
+ }
+ splx(s);
+ cnt.v_wire_count++;
+ }
+ mem->wire_count++;
+}
+
+/*
+ * vm_page_unwire:
+ *
+ * Release one wiring of this page, potentially
+ * enabling it to be paged again.
+ *
+ * The page queues must be locked.
+ */
+void vm_page_unwire(mem)
+ register vm_page_t mem;
+{
+ int s;
+ VM_PAGE_CHECK(mem);
+
+ s = splimp();
+
+ if( mem->wire_count)
+ mem->wire_count--;
+ if (mem->wire_count == 0) {
+ TAILQ_INSERT_TAIL(&vm_page_queue_active, mem, pageq);
+ cnt.v_active_count++;
+ mem->flags |= PG_ACTIVE;
+ cnt.v_wire_count--;
+ }
+ splx(s);
+}
+
+#if 0
+/*
+ * vm_page_deactivate:
+ *
+ * Returns the given page to the inactive list,
+ * indicating that no physical maps have access
+ * to this page. [Used by the physical mapping system.]
+ *
+ * The page queues must be locked.
+ */
+void
+vm_page_deactivate(m)
+ register vm_page_t m;
+{
+ int spl;
+ VM_PAGE_CHECK(m);
+
+ /*
+ * Only move active pages -- ignore locked or already
+ * inactive ones.
+ *
+ * XXX: sometimes we get pages which aren't wired down
+ * or on any queue - we need to put them on the inactive
+ * queue also, otherwise we lose track of them.
+ * Paul Mackerras (paulus@cs.anu.edu.au) 9-Jan-93.
+ */
+
+ spl = splimp();
+ if (!(m->flags & PG_INACTIVE) && m->wire_count == 0 &&
+ m->hold_count == 0) {
+
+ pmap_clear_reference(VM_PAGE_TO_PHYS(m));
+ if (m->flags & PG_ACTIVE) {
+ TAILQ_REMOVE(&vm_page_queue_active, m, pageq);
+ m->flags &= ~PG_ACTIVE;
+ cnt.v_active_count--;
+ }
+ TAILQ_INSERT_TAIL(&vm_page_queue_inactive, m, pageq);
+ m->flags |= PG_INACTIVE;
+ cnt.v_inactive_count++;
+#define NOT_DEACTIVATE_PROTECTS
+#ifndef NOT_DEACTIVATE_PROTECTS
+ pmap_page_protect(VM_PAGE_TO_PHYS(m), VM_PROT_NONE);
+#else
+ if ((m->flags & PG_CLEAN) &&
+ pmap_is_modified(VM_PAGE_TO_PHYS(m)))
+ m->flags &= ~PG_CLEAN;
+#endif
+ if ((m->flags & PG_CLEAN) == 0)
+ m->flags |= PG_LAUNDRY;
+ }
+ splx(spl);
+}
+#endif
+#if 1
+/*
+ * vm_page_deactivate:
+ *
+ * Returns the given page to the inactive list,
+ * indicating that no physical maps have access
+ * to this page. [Used by the physical mapping system.]
+ *
+ * The page queues must be locked.
+ */
+void vm_page_deactivate(m)
+ register vm_page_t m;
+{
+ int s;
+ VM_PAGE_CHECK(m);
+
+ s = splimp();
+ /*
+ * Only move active pages -- ignore locked or already
+ * inactive ones.
+ */
+
+ if ((m->flags & PG_ACTIVE) && (m->hold_count == 0)) {
+ pmap_clear_reference(VM_PAGE_TO_PHYS(m));
+ TAILQ_REMOVE(&vm_page_queue_active, m, pageq);
+ TAILQ_INSERT_TAIL(&vm_page_queue_inactive, m, pageq);
+ m->flags &= ~PG_ACTIVE;
+ m->flags |= PG_INACTIVE;
+ cnt.v_active_count--;
+ cnt.v_inactive_count++;
+#define NOT_DEACTIVATE_PROTECTS
+#ifndef NOT_DEACTIVATE_PROTECTS
+ pmap_page_protect(VM_PAGE_TO_PHYS(m), VM_PROT_NONE);
+#else
+ if (pmap_is_modified(VM_PAGE_TO_PHYS(m)))
+ m->flags &= ~PG_CLEAN;
+#endif
+ if (m->flags & PG_CLEAN)
+ m->flags &= ~PG_LAUNDRY;
+ else
+ m->flags |= PG_LAUNDRY;
+ }
+ splx(s);
+}
+#endif
+/*
+ * vm_page_activate:
+ *
+ * Put the specified page on the active list (if appropriate).
+ *
+ * The page queues must be locked.
+ */
+
+void vm_page_activate(m)
+ register vm_page_t m;
+{
+ int s;
+ VM_PAGE_CHECK(m);
+
+ s = splimp();
+ if (m->flags & PG_INACTIVE) {
+ TAILQ_REMOVE(&vm_page_queue_inactive, m, pageq);
+ cnt.v_inactive_count--;
+ m->flags &= ~PG_INACTIVE;
+ }
+ if (m->wire_count == 0) {
+ if (m->flags & PG_ACTIVE)
+ panic("vm_page_activate: already active");
+
+ TAILQ_INSERT_TAIL(&vm_page_queue_active, m, pageq);
+ m->flags |= PG_ACTIVE;
+ TAILQ_REMOVE(&m->object->memq, m, listq);
+ TAILQ_INSERT_TAIL(&m->object->memq, m, listq);
+ m->act_count = 1;
+ cnt.v_active_count++;
+ }
+ splx(s);
+}
+
+/*
+ * vm_page_zero_fill:
+ *
+ * Zero-fill the specified page.
+ * Written as a standard pagein routine, to
+ * be used by the zero-fill object.
+ */
+
+boolean_t
+vm_page_zero_fill(m)
+ vm_page_t m;
+{
+ VM_PAGE_CHECK(m);
+
+ pmap_zero_page(VM_PAGE_TO_PHYS(m));
+ return(TRUE);
+}
+
+/*
+ * vm_page_copy:
+ *
+ * Copy one page to another
+ */
+void
+vm_page_copy(src_m, dest_m)
+ vm_page_t src_m;
+ vm_page_t dest_m;
+{
+ VM_PAGE_CHECK(src_m);
+ VM_PAGE_CHECK(dest_m);
+
+ pmap_copy_page(VM_PAGE_TO_PHYS(src_m), VM_PAGE_TO_PHYS(dest_m));
+}
diff --git a/sys/vm/vm_page.h b/sys/vm/vm_page.h
new file mode 100644
index 0000000..e8049c4
--- /dev/null
+++ b/sys/vm/vm_page.h
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_page.h 8.2 (Berkeley) 12/13/93
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Resident memory system definitions.
+ */
+
+#ifndef _VM_PAGE_
+#define _VM_PAGE_
+
+/*
+ * Management of resident (logical) pages.
+ *
+ * A small structure is kept for each resident
+ * page, indexed by page number. Each structure
+ * is an element of several lists:
+ *
+ * A hash table bucket used to quickly
+ * perform object/offset lookups
+ *
+ * A list of all pages for a given object,
+ * so they can be quickly deactivated at
+ * time of deallocation.
+ *
+ * An ordered list of pages due for pageout.
+ *
+ * In addition, the structure contains the object
+ * and offset to which this page belongs (for pageout),
+ * and sundry status bits.
+ *
+ * Fields in this structure are locked either by the lock on the
+ * object that the page belongs to (O) or by the lock on the page
+ * queues (P).
+ */
+
+TAILQ_HEAD(pglist, vm_page);
+
+struct vm_page {
+ TAILQ_ENTRY(vm_page) pageq; /* queue info for FIFO
+ * queue or free list (P) */
+ TAILQ_ENTRY(vm_page) hashq; /* hash table links (O)*/
+ TAILQ_ENTRY(vm_page) listq; /* pages in same object (O)*/
+
+ vm_object_t object; /* which object am I in (O,P)*/
+ vm_offset_t offset; /* offset into object (O,P) */
+
+ u_short wire_count; /* wired down maps refs (P) */
+ u_short flags; /* see below */
+ short hold_count; /* page hold count */
+ u_short act_count; /* page usage count */
+
+ vm_offset_t phys_addr; /* physical address of page */
+};
+
+/*
+ * These are the flags defined for vm_page.
+ *
+ * Note: PG_FILLED and PG_DIRTY are added for the filesystems.
+ */
+#define PG_INACTIVE 0x0001 /* page is in inactive list (P) */
+#define PG_ACTIVE 0x0002 /* page is in active list (P) */
+#define PG_LAUNDRY 0x0004 /* page is being cleaned now (P)*/
+#define PG_CLEAN 0x0008 /* page has not been modified */
+#define PG_BUSY 0x0010 /* page is in transit (O) */
+#define PG_WANTED 0x0020 /* someone is waiting for page (O) */
+#define PG_TABLED 0x0040 /* page is in VP table (O) */
+#define PG_COPYONWRITE 0x0080 /* must copy page before changing (O) */
+#define PG_FICTITIOUS 0x0100 /* physical page doesn't exist (O) */
+#define PG_FAKE 0x0200 /* page is placeholder for pagein (O) */
+#define PG_FILLED 0x0400 /* client flag to set when filled */
+#define PG_DIRTY 0x0800 /* client flag to set when dirty */
+#define PG_PAGEROWNED 0x4000 /* DEBUG: async paging op in progress */
+#define PG_PTPAGE 0x8000 /* DEBUG: is a user page table page */
+
+#if VM_PAGE_DEBUG
+#define VM_PAGE_CHECK(mem) { \
+ if ((((unsigned int) mem) < ((unsigned int) &vm_page_array[0])) || \
+ (((unsigned int) mem) > \
+ ((unsigned int) &vm_page_array[last_page-first_page])) || \
+ ((mem->flags & (PG_ACTIVE | PG_INACTIVE)) == \
+ (PG_ACTIVE | PG_INACTIVE))) \
+ panic("vm_page_check: not valid!"); \
+}
+#else /* VM_PAGE_DEBUG */
+#define VM_PAGE_CHECK(mem)
+#endif /* VM_PAGE_DEBUG */
+
+#ifdef KERNEL
+/*
+ * Each pageable resident page falls into one of three lists:
+ *
+ * free
+ * Available for allocation now.
+ * inactive
+ * Not referenced in any map, but still has an
+ * object/offset-page mapping, and may be dirty.
+ * This is the list of pages that should be
+ * paged out next.
+ * active
+ * A list of pages which have been placed in
+ * at least one physical map. This list is
+ * ordered, in LRU-like fashion.
+ */
+
+extern
+struct pglist vm_page_queue_free; /* memory free queue */
+extern
+struct pglist vm_page_queue_active; /* active memory queue */
+extern
+struct pglist vm_page_queue_inactive; /* inactive memory queue */
+
+extern
+vm_page_t vm_page_array; /* First resident page in table */
+extern
+long first_page; /* first physical page number */
+ /* ... represented in vm_page_array */
+extern
+long last_page; /* last physical page number */
+ /* ... represented in vm_page_array */
+ /* [INCLUSIVE] */
+extern
+vm_offset_t first_phys_addr; /* physical address for first_page */
+extern
+vm_offset_t last_phys_addr; /* physical address for last_page */
+
+#define VM_PAGE_TO_PHYS(entry) ((entry)->phys_addr)
+
+#define IS_VM_PHYSADDR(pa) \
+ ((pa) >= first_phys_addr && (pa) <= last_phys_addr)
+
+#define PHYS_TO_VM_PAGE(pa) \
+ (&vm_page_array[atop(pa) - first_page ])
+
+extern
+simple_lock_data_t vm_page_queue_lock; /* lock on active and inactive
+ page queues */
+extern /* lock on free page queue */
+simple_lock_data_t vm_page_queue_free_lock;
+
+/*
+ * Functions implemented as macros
+ */
+
+#define PAGE_ASSERT_WAIT(m, interruptible) { \
+ (m)->flags |= PG_WANTED; \
+ assert_wait((int) (m), (interruptible)); \
+ }
+
+#define PAGE_WAKEUP(m) { \
+ (m)->flags &= ~PG_BUSY; \
+ if ((m)->flags & PG_WANTED) { \
+ (m)->flags &= ~PG_WANTED; \
+ wakeup((caddr_t) (m)); \
+ } \
+ }
+
+#define vm_page_lock_queues() simple_lock(&vm_page_queue_lock)
+#define vm_page_unlock_queues() simple_unlock(&vm_page_queue_lock)
+
+#define vm_page_set_modified(m) { (m)->flags &= ~PG_CLEAN; }
+
+#define VM_PAGE_INIT(mem, object, offset) { \
+ (mem)->flags = PG_BUSY | PG_CLEAN | PG_FAKE; \
+ vm_page_insert((mem), (object), (offset)); \
+ (mem)->wire_count = 0; \
+ (mem)->hold_count = 0; \
+ (mem)->act_count = 0; \
+}
+
+void vm_page_activate __P((vm_page_t));
+vm_page_t vm_page_alloc __P((vm_object_t, vm_offset_t));
+void vm_page_copy __P((vm_page_t, vm_page_t));
+void vm_page_deactivate __P((vm_page_t));
+void vm_page_free __P((vm_page_t));
+void vm_page_insert __P((vm_page_t, vm_object_t, vm_offset_t));
+vm_page_t vm_page_lookup __P((vm_object_t, vm_offset_t));
+void vm_page_remove __P((vm_page_t));
+void vm_page_rename __P((vm_page_t, vm_object_t, vm_offset_t));
+vm_offset_t vm_page_startup __P((vm_offset_t, vm_offset_t, vm_offset_t));
+void vm_page_unwire __P((vm_page_t));
+void vm_page_wire __P((vm_page_t));
+boolean_t vm_page_zero_fill __P((vm_page_t));
+
+
+/*
+ * Keep page from being freed by the page daemon
+ * much of the same effect as wiring, except much lower
+ * overhead and should be used only for *very* temporary
+ * holding ("wiring").
+ */
+static inline void
+vm_page_hold(mem)
+ vm_page_t mem;
+{
+ mem->hold_count++;
+}
+
+static inline void
+vm_page_unhold(mem)
+ vm_page_t mem;
+{
+ if( --mem->hold_count < 0)
+ panic("vm_page_unhold: hold count < 0!!!");
+}
+
+#endif /* KERNEL */
+#endif /* !_VM_PAGE_ */
diff --git a/sys/vm/vm_pageout.c b/sys/vm/vm_pageout.c
new file mode 100644
index 0000000..cabb102
--- /dev/null
+++ b/sys/vm/vm_pageout.c
@@ -0,0 +1,790 @@
+/*
+ * Copyright (c) 1991 Regents of the University of California.
+ * All rights reserved.
+ * Copyright (c) 1994 John S. Dyson
+ * All rights reserved.
+ * Copyright (c) 1994 David Greenman
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_pageout.c 7.4 (Berkeley) 5/7/91
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ *
+ * $Id: vm_pageout.c,v 1.3 1994/06/06 11:56:27 davidg Exp $
+ */
+
+/*
+ * The proverbial page-out daemon.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/resourcevar.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pageout.h>
+
+extern vm_map_t kmem_map;
+int vm_pages_needed; /* Event on which pageout daemon sleeps */
+int vm_pagescanner; /* Event on which pagescanner sleeps */
+int vm_pageout_free_min = 0; /* Stop pageout to wait for pagers at this free level */
+
+int vm_pageout_pages_needed = 0; /* flag saying that the pageout daemon needs pages */
+int vm_page_pagesfreed;
+
+extern int npendingio;
+extern int hz;
+int vm_pageout_proc_limit;
+extern int nswiodone;
+extern int swap_pager_full;
+extern int swap_pager_ready();
+
+#define MAXREF 32767
+
+#define MAXSCAN 512 /* maximum number of pages to scan in active queue */
+ /* set the "clock" hands to be (MAXSCAN * 4096) Bytes */
+#define ACT_DECLINE 1
+#define ACT_ADVANCE 3
+#define ACT_MAX 300
+
+#define LOWATER ((2048*1024)/NBPG)
+
+#define VM_PAGEOUT_PAGE_COUNT 8
+static vm_offset_t vm_space_needed;
+int vm_pageout_req_do_stats;
+
+int vm_page_max_wired = 0; /* XXX max # of wired pages system-wide */
+
+
+/*
+ * vm_pageout_clean:
+ * cleans a vm_page
+ */
+int
+vm_pageout_clean(m, sync)
+ register vm_page_t m;
+ int sync;
+{
+ /*
+ * Clean the page and remove it from the
+ * laundry.
+ *
+ * We set the busy bit to cause
+ * potential page faults on this page to
+ * block.
+ *
+ * And we set pageout-in-progress to keep
+ * the object from disappearing during
+ * pageout. This guarantees that the
+ * page won't move from the inactive
+ * queue. (However, any other page on
+ * the inactive queue may move!)
+ */
+
+ register vm_object_t object;
+ register vm_pager_t pager;
+ int pageout_status[VM_PAGEOUT_PAGE_COUNT];
+ vm_page_t ms[VM_PAGEOUT_PAGE_COUNT];
+ int pageout_count;
+ int anyok=0;
+ int i;
+ vm_offset_t offset = m->offset;
+
+ object = m->object;
+ if (!object) {
+ printf("pager: object missing\n");
+ return 0;
+ }
+
+ /*
+ * Try to collapse the object before
+ * making a pager for it. We must
+ * unlock the page queues first.
+ * We try to defer the creation of a pager
+ * until all shadows are not paging. This
+ * allows vm_object_collapse to work better and
+ * helps control swap space size.
+ * (J. Dyson 11 Nov 93)
+ */
+
+ if (!object->pager &&
+ cnt.v_free_count < vm_pageout_free_min)
+ return 0;
+
+ if (!object->pager &&
+ object->shadow &&
+ object->shadow->paging_in_progress)
+ return 0;
+
+ if( !sync) {
+ if (object->shadow) {
+ vm_object_collapse(object);
+ if (!vm_page_lookup(object, offset))
+ return 0;
+ }
+
+ if ((m->flags & PG_BUSY) || (m->hold_count != 0)) {
+ return 0;
+ }
+ }
+
+ pageout_count = 1;
+ ms[0] = m;
+
+ if( pager = object->pager) {
+ for(i=1;i<VM_PAGEOUT_PAGE_COUNT;i++) {
+ if( ms[i] = vm_page_lookup( object, offset+i*NBPG)) {
+ if((((ms[i]->flags & (PG_CLEAN|PG_INACTIVE|PG_BUSY)) == PG_INACTIVE)
+ || (( ms[i]->flags & PG_CLEAN) == 0 && sync == VM_PAGEOUT_FORCE))
+ && (ms[i]->wire_count == 0)
+ && (ms[i]->hold_count == 0))
+ pageout_count++;
+ else
+ break;
+ } else
+ break;
+ }
+ for(i=0;i<pageout_count;i++) {
+ ms[i]->flags |= PG_BUSY;
+ pmap_page_protect(VM_PAGE_TO_PHYS(ms[i]), VM_PROT_READ);
+ }
+ object->paging_in_progress += pageout_count;
+ cnt.v_pageouts += pageout_count;
+ } else {
+
+ m->flags |= PG_BUSY;
+
+ pmap_page_protect(VM_PAGE_TO_PHYS(m), VM_PROT_READ);
+
+ cnt.v_pageouts++;
+
+ object->paging_in_progress++;
+
+ pager = vm_pager_allocate(PG_DFLT, (caddr_t)0,
+ object->size, VM_PROT_ALL, 0);
+ if (pager != NULL) {
+ vm_object_setpager(object, pager, 0, FALSE);
+ }
+ }
+
+ /*
+ * If there is no pager for the page,
+ * use the default pager. If there's
+ * no place to put the page at the
+ * moment, leave it in the laundry and
+ * hope that there will be paging space
+ * later.
+ */
+
+ if ((pager && pager->pg_type == PG_SWAP) ||
+ cnt.v_free_count >= vm_pageout_free_min) {
+ if( pageout_count == 1) {
+ pageout_status[0] = pager ?
+ vm_pager_put(pager, m,
+ ((sync || (object == kernel_object)) ? TRUE: FALSE)) :
+ VM_PAGER_FAIL;
+ } else {
+ if( !pager) {
+ for(i=0;i<pageout_count;i++)
+ pageout_status[i] = VM_PAGER_FAIL;
+ } else {
+ vm_pager_put_pages(pager, ms, pageout_count,
+ ((sync || (object == kernel_object)) ? TRUE : FALSE),
+ pageout_status);
+ }
+ }
+
+ } else {
+ for(i=0;i<pageout_count;i++)
+ pageout_status[i] = VM_PAGER_FAIL;
+ }
+
+ for(i=0;i<pageout_count;i++) {
+ switch (pageout_status[i]) {
+ case VM_PAGER_OK:
+ ms[i]->flags &= ~PG_LAUNDRY;
+ ++anyok;
+ break;
+ case VM_PAGER_PEND:
+ ms[i]->flags &= ~PG_LAUNDRY;
+ ++anyok;
+ break;
+ case VM_PAGER_BAD:
+ /*
+ * Page outside of range of object.
+ * Right now we essentially lose the
+ * changes by pretending it worked.
+ */
+ ms[i]->flags &= ~PG_LAUNDRY;
+ ms[i]->flags |= PG_CLEAN;
+ pmap_clear_modify(VM_PAGE_TO_PHYS(ms[i]));
+ break;
+ case VM_PAGER_ERROR:
+ case VM_PAGER_FAIL:
+ /*
+ * If page couldn't be paged out, then
+ * reactivate the page so it doesn't
+ * clog the inactive list. (We will
+ * try paging out it again later).
+ */
+ if (ms[i]->flags & PG_INACTIVE)
+ vm_page_activate(ms[i]);
+ break;
+ case VM_PAGER_AGAIN:
+ break;
+ }
+
+
+ /*
+ * If the operation is still going, leave
+ * the page busy to block all other accesses.
+ * Also, leave the paging in progress
+ * indicator set so that we don't attempt an
+ * object collapse.
+ */
+ if (pageout_status[i] != VM_PAGER_PEND) {
+ PAGE_WAKEUP(ms[i]);
+ if (--object->paging_in_progress == 0)
+ wakeup((caddr_t) object);
+ if (pmap_is_referenced(VM_PAGE_TO_PHYS(ms[i]))) {
+ pmap_clear_reference(VM_PAGE_TO_PHYS(ms[i]));
+ if( ms[i]->flags & PG_INACTIVE)
+ vm_page_activate(ms[i]);
+ }
+ }
+ }
+ return anyok;
+}
+
+/*
+ * vm_pageout_object_deactivate_pages
+ *
+ * deactivate enough pages to satisfy the inactive target
+ * requirements or if vm_page_proc_limit is set, then
+ * deactivate all of the pages in the object and its
+ * shadows.
+ *
+ * The object and map must be locked.
+ */
+int
+vm_pageout_object_deactivate_pages(map, object, count)
+ vm_map_t map;
+ vm_object_t object;
+ int count;
+{
+ register vm_page_t p, next;
+ int rcount;
+ int s;
+ int dcount;
+
+ dcount = 0;
+ if (count == 0)
+ count = 1;
+
+ if (object->shadow) {
+ int scount = count;
+ if( object->shadow->ref_count > 1)
+ scount /= object->shadow->ref_count;
+ if( scount)
+ dcount += vm_pageout_object_deactivate_pages(map, object->shadow, scount);
+ }
+
+ if (object->paging_in_progress)
+ return dcount;
+
+ /*
+ * scan the objects entire memory queue
+ */
+ rcount = object->resident_page_count;
+ p = object->memq.tqh_first;
+ while (p && (rcount-- > 0)) {
+ next = p->listq.tqe_next;
+ vm_page_lock_queues();
+ /*
+ * if a page is active, not wired and is in the processes pmap,
+ * then deactivate the page.
+ */
+ if ((p->flags & (PG_ACTIVE|PG_BUSY)) == PG_ACTIVE &&
+ p->wire_count == 0 &&
+ p->hold_count == 0 &&
+ pmap_page_exists(vm_map_pmap(map), VM_PAGE_TO_PHYS(p))) {
+ if (!pmap_is_referenced(VM_PAGE_TO_PHYS(p))) {
+ p->act_count -= min(p->act_count, ACT_DECLINE);
+ /*
+ * if the page act_count is zero -- then we deactivate
+ */
+ if (!p->act_count) {
+ vm_page_deactivate(p);
+ pmap_page_protect(VM_PAGE_TO_PHYS(p),
+ VM_PROT_NONE);
+ /*
+ * else if on the next go-around we will deactivate the page
+ * we need to place the page on the end of the queue to age
+ * the other pages in memory.
+ */
+ } else {
+ TAILQ_REMOVE(&vm_page_queue_active, p, pageq);
+ TAILQ_INSERT_TAIL(&vm_page_queue_active, p, pageq);
+ TAILQ_REMOVE(&object->memq, p, listq);
+ TAILQ_INSERT_TAIL(&object->memq, p, listq);
+ }
+ /*
+ * see if we are done yet
+ */
+ if (p->flags & PG_INACTIVE) {
+ --count;
+ ++dcount;
+ if (count <= 0 &&
+ cnt.v_inactive_count > cnt.v_inactive_target) {
+ vm_page_unlock_queues();
+ return dcount;
+ }
+ }
+
+ } else {
+ /*
+ * Move the page to the bottom of the queue.
+ */
+ pmap_clear_reference(VM_PAGE_TO_PHYS(p));
+ if (p->act_count < ACT_MAX)
+ p->act_count += ACT_ADVANCE;
+
+ TAILQ_REMOVE(&vm_page_queue_active, p, pageq);
+ TAILQ_INSERT_TAIL(&vm_page_queue_active, p, pageq);
+ TAILQ_REMOVE(&object->memq, p, listq);
+ TAILQ_INSERT_TAIL(&object->memq, p, listq);
+ }
+ }
+
+ vm_page_unlock_queues();
+ p = next;
+ }
+ return dcount;
+}
+
+
+/*
+ * deactivate some number of pages in a map, try to do it fairly, but
+ * that is really hard to do.
+ */
+
+void
+vm_pageout_map_deactivate_pages(map, entry, count, freeer)
+ vm_map_t map;
+ vm_map_entry_t entry;
+ int *count;
+ int (*freeer)(vm_map_t, vm_object_t, int);
+{
+ vm_map_t tmpm;
+ vm_map_entry_t tmpe;
+ vm_object_t obj;
+ if (*count <= 0)
+ return;
+ vm_map_reference(map);
+ if (!lock_try_read(&map->lock)) {
+ vm_map_deallocate(map);
+ return;
+ }
+ if (entry == 0) {
+ tmpe = map->header.next;
+ while (tmpe != &map->header && *count > 0) {
+ vm_pageout_map_deactivate_pages(map, tmpe, count, freeer);
+ tmpe = tmpe->next;
+ };
+ } else if (entry->is_sub_map || entry->is_a_map) {
+ tmpm = entry->object.share_map;
+ tmpe = tmpm->header.next;
+ while (tmpe != &tmpm->header && *count > 0) {
+ vm_pageout_map_deactivate_pages(tmpm, tmpe, count, freeer);
+ tmpe = tmpe->next;
+ };
+ } else if (obj = entry->object.vm_object) {
+ *count -= (*freeer)(map, obj, *count);
+ }
+ lock_read_done(&map->lock);
+ vm_map_deallocate(map);
+ return;
+}
+
+/*
+ * vm_pageout_scan does the dirty work for the pageout daemon.
+ */
+int
+vm_pageout_scan()
+{
+ vm_page_t m;
+ int page_shortage, maxscan, maxlaunder;
+ int pages_freed, free, nproc;
+ int desired_free;
+ vm_page_t next;
+ struct proc *p;
+ vm_object_t object;
+ int s;
+ int force_wakeup = 0;
+
+morefree:
+ /*
+ * scan the processes for exceeding their rlimits or if process
+ * is swapped out -- deactivate pages
+ */
+
+rescanproc1:
+ for (p = (struct proc *)allproc; p != NULL; p = p->p_next) {
+ vm_offset_t size;
+ int overage;
+ vm_offset_t limit;
+
+ /*
+ * if this is a system process or if we have already
+ * looked at this process, skip it.
+ */
+ if (p->p_flag & (P_SYSTEM|P_WEXIT)) {
+ continue;
+ }
+
+ /*
+ * if the process is in a non-running type state,
+ * don't touch it.
+ */
+ if (p->p_stat != SRUN && p->p_stat != SSLEEP) {
+ continue;
+ }
+
+ /*
+ * get a limit
+ */
+ limit = min(p->p_rlimit[RLIMIT_RSS].rlim_cur,
+ p->p_rlimit[RLIMIT_RSS].rlim_max);
+
+ /*
+ * let processes that are swapped out really be swapped out
+ * set the limit to nothing (will force a swap-out.)
+ */
+ if ((p->p_flag & P_INMEM) == 0)
+ limit = 0;
+
+ size = p->p_vmspace->vm_pmap.pm_stats.resident_count * NBPG;
+ if (size >= limit) {
+ overage = (size - limit) / NBPG;
+ vm_pageout_map_deactivate_pages(&p->p_vmspace->vm_map,
+ (vm_map_entry_t) 0, &overage, vm_pageout_object_deactivate_pages);
+ }
+
+ }
+
+ if (((cnt.v_free_count + cnt.v_inactive_count) >=
+ (cnt.v_inactive_target + cnt.v_free_target)) &&
+ (cnt.v_free_count >= cnt.v_free_target))
+ return force_wakeup;
+
+ pages_freed = 0;
+ desired_free = cnt.v_free_target;
+
+ /*
+ * Start scanning the inactive queue for pages we can free.
+ * We keep scanning until we have enough free pages or
+ * we have scanned through the entire queue. If we
+ * encounter dirty pages, we start cleaning them.
+ */
+
+ maxlaunder = (cnt.v_free_target - cnt.v_free_count);
+ maxscan = cnt.v_inactive_count;
+rescan1:
+ m = vm_page_queue_inactive.tqh_first;
+ while (m && (maxscan-- > 0) &&
+ (cnt.v_free_count < desired_free) ) {
+ vm_page_t next;
+
+ next = m->pageq.tqe_next;
+
+ if( (m->flags & PG_INACTIVE) == 0) {
+ printf("vm_pageout_scan: page not inactive?");
+ continue;
+ }
+
+ /*
+ * activate held pages
+ */
+ if (m->hold_count != 0) {
+ vm_page_activate(m);
+ m = next;
+ continue;
+ }
+
+ /*
+ * dont mess with busy pages
+ */
+ if (m->flags & PG_BUSY) {
+ m = next;
+ continue;
+ }
+
+ /*
+ * if page is clean and but the page has been referenced,
+ * then reactivate the page, but if we are very low on memory
+ * or the page has not been referenced, then we free it to the
+ * vm system.
+ */
+ if (m->flags & PG_CLEAN) {
+ if ((cnt.v_free_count > vm_pageout_free_min) /* XXX */
+ && pmap_is_referenced(VM_PAGE_TO_PHYS(m))) {
+ vm_page_activate(m);
+ } else if (!m->act_count) {
+ pmap_page_protect(VM_PAGE_TO_PHYS(m),
+ VM_PROT_NONE);
+ vm_page_free(m);
+ ++pages_freed;
+ } else {
+ m->act_count -= min(m->act_count, ACT_DECLINE);
+ TAILQ_REMOVE(&vm_page_queue_inactive, m, pageq);
+ TAILQ_INSERT_TAIL(&vm_page_queue_inactive, m, pageq);
+ }
+ } else if ((m->flags & PG_LAUNDRY) && maxlaunder > 0) {
+ int written;
+ if (pmap_is_referenced(VM_PAGE_TO_PHYS(m))) {
+ pmap_clear_reference(VM_PAGE_TO_PHYS(m));
+ vm_page_activate(m);
+ m = next;
+ continue;
+ }
+ /*
+ * If a page is dirty, then it is either
+ * being washed (but not yet cleaned)
+ * or it is still in the laundry. If it is
+ * still in the laundry, then we start the
+ * cleaning operation.
+ */
+
+ if (written = vm_pageout_clean(m,0)) {
+ maxlaunder -= written;
+ }
+ /*
+ * if the next page has been re-activated, start scanning again
+ */
+ if (next && (next->flags & PG_INACTIVE) == 0)
+ goto rescan1;
+ } else if (pmap_is_referenced(VM_PAGE_TO_PHYS(m))) {
+ pmap_clear_reference(VM_PAGE_TO_PHYS(m));
+ vm_page_activate(m);
+ }
+ m = next;
+ }
+
+ /*
+ * now check malloc area or swap processes out if we are in low
+ * memory conditions
+ */
+ if (cnt.v_free_count <= cnt.v_free_min) {
+ /*
+ * swap out inactive processes
+ */
+ swapout_threads();
+ }
+
+ /*
+ * Compute the page shortage. If we are still very low on memory
+ * be sure that we will move a minimal amount of pages from active
+ * to inactive.
+ */
+
+ page_shortage = cnt.v_inactive_target -
+ (cnt.v_free_count + cnt.v_inactive_count);
+
+ if (page_shortage <= 0) {
+ if (pages_freed == 0) {
+ if( cnt.v_free_count < cnt.v_free_min) {
+ page_shortage = cnt.v_free_min - cnt.v_free_count;
+ } else if(((cnt.v_free_count + cnt.v_inactive_count) <
+ (cnt.v_free_min + cnt.v_inactive_target))) {
+ page_shortage = 1;
+ } else {
+ page_shortage = 0;
+ }
+ }
+
+ }
+
+ maxscan = cnt.v_active_count;
+ m = vm_page_queue_active.tqh_first;
+ while (m && maxscan-- && (page_shortage > 0)) {
+
+ next = m->pageq.tqe_next;
+
+ /*
+ * Don't deactivate pages that are busy.
+ */
+ if ((m->flags & PG_BUSY) || (m->hold_count != 0)) {
+ m = next;
+ continue;
+ }
+
+ if (pmap_is_referenced(VM_PAGE_TO_PHYS(m))) {
+ pmap_clear_reference(VM_PAGE_TO_PHYS(m));
+ if (m->act_count < ACT_MAX)
+ m->act_count += ACT_ADVANCE;
+ TAILQ_REMOVE(&vm_page_queue_active, m, pageq);
+ TAILQ_INSERT_TAIL(&vm_page_queue_active, m, pageq);
+ TAILQ_REMOVE(&m->object->memq, m, listq);
+ TAILQ_INSERT_TAIL(&m->object->memq, m, listq);
+ } else {
+ m->act_count -= min(m->act_count, ACT_DECLINE);
+
+ /*
+ * if the page act_count is zero -- then we deactivate
+ */
+ if (!m->act_count) {
+ vm_page_deactivate(m);
+ --page_shortage;
+ /*
+ * else if on the next go-around we will deactivate the page
+ * we need to place the page on the end of the queue to age
+ * the other pages in memory.
+ */
+ } else {
+ TAILQ_REMOVE(&vm_page_queue_active, m, pageq);
+ TAILQ_INSERT_TAIL(&vm_page_queue_active, m, pageq);
+ TAILQ_REMOVE(&m->object->memq, m, listq);
+ TAILQ_INSERT_TAIL(&m->object->memq, m, listq);
+ }
+ }
+
+ m = next;
+ }
+
+ /*
+ * if we have not freed any pages and we are desparate for memory
+ * then we keep trying until we get some (any) memory.
+ */
+
+ if( !force_wakeup && (swap_pager_full || !force_wakeup ||
+ (pages_freed == 0 && (cnt.v_free_count < cnt.v_free_min)))){
+ vm_pager_sync();
+ force_wakeup = 1;
+ goto morefree;
+ }
+ vm_page_pagesfreed += pages_freed;
+ return force_wakeup;
+}
+
+/*
+ * vm_pageout is the high level pageout daemon.
+ */
+void
+vm_pageout()
+{
+ extern npendingio, swiopend;
+ static nowakeup;
+ (void) spl0();
+
+ /*
+ * Initialize some paging parameters.
+ */
+
+vmretry:
+ cnt.v_free_min = 12;
+ cnt.v_free_reserved = 8;
+ if (cnt.v_free_min < 8)
+ cnt.v_free_min = 8;
+ if (cnt.v_free_min > 32)
+ cnt.v_free_min = 32;
+ vm_pageout_free_min = 4;
+ cnt.v_free_target = 2*cnt.v_free_min + cnt.v_free_reserved;
+ cnt.v_inactive_target = cnt.v_free_count / 12;
+ cnt.v_free_min += cnt.v_free_reserved;
+
+ /* XXX does not really belong here */
+ if (vm_page_max_wired == 0)
+ vm_page_max_wired = cnt.v_free_count / 3;
+
+
+ (void) swap_pager_alloc(0, 0, 0, 0);
+
+ /*
+ * The pageout daemon is never done, so loop
+ * forever.
+ */
+ while (TRUE) {
+ int force_wakeup;
+ extern struct loadavg averunnable;
+/*
+ cnt.v_free_min = 12 + averunnable.ldavg[0] / 1024;
+ cnt.v_free_target = 2*cnt.v_free_min + cnt.v_free_reserved;
+ cnt.v_inactive_target = cnt.v_free_target*2;
+*/
+
+ tsleep((caddr_t) &vm_pages_needed, PVM, "psleep", 0);
+
+ vm_pager_sync();
+ /*
+ * The force wakeup hack added to eliminate delays and potiential
+ * deadlock. It was possible for the page daemon to indefintely
+ * postpone waking up a process that it might be waiting for memory
+ * on. The putmulti stuff seems to have aggravated the situation.
+ */
+ force_wakeup = vm_pageout_scan();
+ vm_pager_sync();
+ if( force_wakeup)
+ wakeup( (caddr_t) &cnt.v_free_count);
+ cnt.v_scan++;
+ wakeup((caddr_t) kmem_map);
+ }
+}
+
diff --git a/sys/vm/vm_pageout.h b/sys/vm/vm_pageout.h
new file mode 100644
index 0000000..834aee5
--- /dev/null
+++ b/sys/vm/vm_pageout.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_pageout.h 8.2 (Berkeley) 1/12/94
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Author: Avadis Tevanian, Jr.
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Header file for pageout daemon.
+ */
+
+/*
+ * Exported data structures.
+ */
+
+extern int vm_pages_needed; /* should be some "event" structure */
+simple_lock_data_t vm_pages_needed_lock;
+extern int vm_pageout_pages_needed;
+
+#define VM_PAGEOUT_ASYNC 0
+#define VM_PAGEOUT_SYNC 1
+#define VM_PAGEOUT_FORCE 2
+
+/*
+ * Exported routines.
+ */
+
+/*
+ * Signal pageout-daemon and wait for it.
+ */
+
+#define VM_WAIT vm_wait()
+
+inline static void vm_wait() {
+ extern struct proc *curproc, *pageproc;
+ int s;
+ s = splhigh();
+ if (curproc == pageproc) {
+ vm_pageout_pages_needed = 1;
+ tsleep((caddr_t) &vm_pageout_pages_needed, PSWP, "vmwait", 0);
+ vm_pageout_pages_needed = 0;
+ } else {
+ wakeup((caddr_t) &vm_pages_needed);
+ tsleep((caddr_t) &cnt.v_free_count, PVM, "vmwait", 0);
+ }
+ splx(s);
+}
+
+
+#ifdef KERNEL
+void vm_pageout __P((void));
+int vm_pageout_scan __P((void));
+void vm_pageout_page __P((vm_page_t, vm_object_t));
+void vm_pageout_cluster __P((vm_page_t, vm_object_t));
+#endif
diff --git a/sys/vm/vm_pager.c b/sys/vm/vm_pager.c
new file mode 100644
index 0000000..1e4b201
--- /dev/null
+++ b/sys/vm/vm_pager.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_pager.c 8.6 (Berkeley) 1/12/94
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Paging space routine stubs. Emulates a matchmaker-like interface
+ * for builtin pagers.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
+#include <vm/vm_page.h>
+#include <vm/vm_kern.h>
+
+extern struct pagerops swappagerops;
+extern struct pagerops vnodepagerops;
+extern struct pagerops devicepagerops;
+
+struct pagerops *pagertab[] = {
+ &swappagerops, /* PG_SWAP */
+ &vnodepagerops, /* PG_VNODE */
+ &devicepagerops, /* PG_DEV */
+};
+int npagers = sizeof (pagertab) / sizeof (pagertab[0]);
+
+struct pagerops *dfltpagerops = NULL; /* default pager */
+
+/*
+ * Kernel address space for mapping pages.
+ * Used by pagers where KVAs are needed for IO.
+ *
+ * XXX needs to be large enough to support the number of pending async
+ * cleaning requests (NPENDINGIO == 64) * the maximum swap cluster size
+ * (MAXPHYS == 64k) if you want to get the most efficiency.
+ */
+#define PAGER_MAP_SIZE (4 * 1024 * 1024)
+
+int pager_map_size = PAGER_MAP_SIZE;
+vm_map_t pager_map;
+boolean_t pager_map_wanted;
+vm_offset_t pager_sva, pager_eva;
+
+void
+vm_pager_init()
+{
+ struct pagerops **pgops;
+
+ /*
+ * Allocate a kernel submap for tracking get/put page mappings
+ */
+/*
+ pager_map = kmem_suballoc(kernel_map, &pager_sva, &pager_eva,
+ PAGER_MAP_SIZE, FALSE);
+*/
+ /*
+ * Initialize known pagers
+ */
+ for (pgops = pagertab; pgops < &pagertab[npagers]; pgops++)
+ if (pgops)
+ (*(*pgops)->pgo_init)();
+ if (dfltpagerops == NULL)
+ panic("no default pager");
+}
+
+/*
+ * Allocate an instance of a pager of the given type.
+ * Size, protection and offset parameters are passed in for pagers that
+ * need to perform page-level validation (e.g. the device pager).
+ */
+vm_pager_t
+vm_pager_allocate(type, handle, size, prot, off)
+ int type;
+ caddr_t handle;
+ vm_size_t size;
+ vm_prot_t prot;
+ vm_offset_t off;
+{
+ struct pagerops *ops;
+
+ ops = (type == PG_DFLT) ? dfltpagerops : pagertab[type];
+ if (ops)
+ return ((*ops->pgo_alloc)(handle, size, prot, off));
+ return (NULL);
+}
+
+void
+vm_pager_deallocate(pager)
+ vm_pager_t pager;
+{
+ if (pager == NULL)
+ panic("vm_pager_deallocate: null pager");
+
+ (*pager->pg_ops->pgo_dealloc)(pager);
+}
+
+
+int
+vm_pager_get_pages(pager, m, count, reqpage, sync)
+ vm_pager_t pager;
+ vm_page_t *m;
+ int count;
+ int reqpage;
+ boolean_t sync;
+{
+ extern boolean_t vm_page_zero_fill();
+ extern int vm_pageout_count;
+ int i;
+
+ if (pager == NULL) {
+ for (i=0;i<count;i++) {
+ if( i != reqpage) {
+ PAGE_WAKEUP(m[i]);
+ vm_page_free(m[i]);
+ }
+ }
+ vm_page_zero_fill(m[reqpage]);
+ return VM_PAGER_OK;
+ }
+
+ if( pager->pg_ops->pgo_getpages == 0) {
+ for(i=0;i<count;i++) {
+ if( i != reqpage) {
+ PAGE_WAKEUP(m[i]);
+ vm_page_free(m[i]);
+ }
+ }
+ return(VM_PAGER_GET(pager, m[reqpage], sync));
+ } else {
+ return(VM_PAGER_GET_MULTI(pager, m, count, reqpage, sync));
+ }
+}
+
+int
+vm_pager_put_pages(pager, m, count, sync, rtvals)
+ vm_pager_t pager;
+ vm_page_t *m;
+ int count;
+ boolean_t sync;
+ int *rtvals;
+{
+ int i;
+
+ if( pager->pg_ops->pgo_putpages)
+ return(VM_PAGER_PUT_MULTI(pager, m, count, sync, rtvals));
+ else {
+ for(i=0;i<count;i++) {
+ rtvals[i] = VM_PAGER_PUT( pager, m[i], sync);
+ }
+ return rtvals[0];
+ }
+}
+
+boolean_t
+vm_pager_has_page(pager, offset)
+ vm_pager_t pager;
+ vm_offset_t offset;
+{
+ if (pager == NULL)
+ panic("vm_pager_has_page: null pager");
+ return ((*pager->pg_ops->pgo_haspage)(pager, offset));
+}
+
+/*
+ * Called by pageout daemon before going back to sleep.
+ * Gives pagers a chance to clean up any completed async pageing operations.
+ */
+void
+vm_pager_sync()
+{
+ struct pagerops **pgops;
+
+ for (pgops = pagertab; pgops < &pagertab[npagers]; pgops++)
+ if (pgops)
+ (*(*pgops)->pgo_putpage)(NULL, NULL, 0);
+}
+
+#if 0
+void
+vm_pager_cluster(pager, offset, loff, hoff)
+ vm_pager_t pager;
+ vm_offset_t offset;
+ vm_offset_t *loff;
+ vm_offset_t *hoff;
+{
+ if (pager == NULL)
+ panic("vm_pager_cluster: null pager");
+ return ((*pager->pg_ops->pgo_cluster)(pager, offset, loff, hoff));
+}
+#endif
+
+vm_offset_t
+vm_pager_map_page(m)
+ vm_page_t m;
+{
+ vm_offset_t kva;
+
+ kva = kmem_alloc_wait(pager_map, PAGE_SIZE);
+ pmap_enter(vm_map_pmap(pager_map), kva, VM_PAGE_TO_PHYS(m),
+ VM_PROT_DEFAULT, TRUE);
+ return(kva);
+}
+
+void
+vm_pager_unmap_page(kva)
+ vm_offset_t kva;
+{
+ kmem_free_wakeup(pager_map, kva, PAGE_SIZE);
+}
+
+vm_page_t
+vm_pager_atop(kva)
+ vm_offset_t kva;
+{
+ vm_offset_t pa;
+
+ pa = pmap_extract(vm_map_pmap(pager_map), kva);
+ if (pa == 0)
+ panic("vm_pager_atop");
+ return (PHYS_TO_VM_PAGE(pa));
+}
+
+vm_pager_t
+vm_pager_lookup(pglist, handle)
+ register struct pagerlst *pglist;
+ caddr_t handle;
+{
+ register vm_pager_t pager;
+
+ for (pager = pglist->tqh_first; pager; pager = pager->pg_list.tqe_next)
+ if (pager->pg_handle == handle)
+ return (pager);
+ return (NULL);
+}
+
+/*
+ * This routine gains a reference to the object.
+ * Explicit deallocation is necessary.
+ */
+int
+pager_cache(object, should_cache)
+ vm_object_t object;
+ boolean_t should_cache;
+{
+ if (object == NULL)
+ return (KERN_INVALID_ARGUMENT);
+
+ vm_object_cache_lock();
+ vm_object_lock(object);
+ if (should_cache)
+ object->flags |= OBJ_CANPERSIST;
+ else
+ object->flags &= ~OBJ_CANPERSIST;
+ vm_object_unlock(object);
+ vm_object_cache_unlock();
+
+ vm_object_deallocate(object);
+
+ return (KERN_SUCCESS);
+}
diff --git a/sys/vm/vm_pager.h b/sys/vm/vm_pager.h
new file mode 100644
index 0000000..3e20e50
--- /dev/null
+++ b/sys/vm/vm_pager.h
@@ -0,0 +1,154 @@
+
+/*
+ * Copyright (c) 1990 University of Utah.
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_pager.h 8.4 (Berkeley) 1/12/94
+ */
+
+/*
+ * Pager routine interface definition.
+ * For BSD we use a cleaner version of the internal pager interface.
+ */
+
+#ifndef _VM_PAGER_
+#define _VM_PAGER_
+
+TAILQ_HEAD(pagerlst, pager_struct);
+
+struct pager_struct {
+ TAILQ_ENTRY(pager_struct) pg_list; /* links for list management */
+ caddr_t pg_handle; /* ext. handle (vp, dev, fp) */
+ int pg_type; /* type of pager */
+ int pg_flags; /* flags */
+ struct pagerops *pg_ops; /* pager operations */
+ void *pg_data; /* private pager data */
+};
+
+/* pager types */
+#define PG_DFLT -1
+#define PG_SWAP 0
+#define PG_VNODE 1
+#define PG_DEVICE 2
+
+/* flags */
+#define PG_CLUSTERGET 1
+#define PG_CLUSTERPUT 2
+
+struct pagerops {
+ void (*pgo_init) /* Initialize pager. */
+ __P((void));
+ vm_pager_t (*pgo_alloc) /* Allocate pager. */
+ __P((caddr_t, vm_size_t, vm_prot_t, vm_offset_t));
+ void (*pgo_dealloc) /* Disassociate. */
+ __P((vm_pager_t));
+ int (*pgo_getpage)
+ __P((vm_pager_t, vm_page_t, boolean_t));
+ int (*pgo_getpages) /* Get (read) page. */
+ __P((vm_pager_t, vm_page_t *, int, int, boolean_t));
+ int (*pgo_putpage)
+ __P((vm_pager_t, vm_page_t, boolean_t));
+ int (*pgo_putpages) /* Put (write) page. */
+ __P((vm_pager_t, vm_page_t *, int, boolean_t, int *));
+ boolean_t (*pgo_haspage) /* Does pager have page? */
+ __P((vm_pager_t, vm_offset_t));
+};
+
+#define VM_PAGER_ALLOC(h, s, p, o) (*(pg)->pg_ops->pgo_alloc)(h, s, p, o)
+#define VM_PAGER_DEALLOC(pg) (*(pg)->pg_ops->pgo_dealloc)(pg)
+#define VM_PAGER_GET(pg, m, s) (*(pg)->pg_ops->pgo_getpage)(pg, m, s)
+#define VM_PAGER_GET_MULTI(pg, m, c, r, s) (*(pg)->pg_ops->pgo_getpages)(pg, m, c, r, s)
+#define VM_PAGER_PUT(pg, m, s) (*(pg)->pg_ops->pgo_putpage)(pg, m, s)
+#define VM_PAGER_PUT_MULTI(pg, m, c, s, rtval) (*(pg)->pg_ops->pgo_putpages)(pg, m, c, s, rtval)
+#define VM_PAGER_HASPAGE(pg, o) (*(pg)->pg_ops->pgo_haspage)(pg, o)
+
+/*
+ * get/put return values
+ * OK operation was successful
+ * BAD specified data was out of the accepted range
+ * FAIL specified data was in range, but doesn't exist
+ * PEND operations was initiated but not completed
+ * ERROR error while accessing data that is in range and exists
+ * AGAIN temporary resource shortage prevented operation from happening
+ */
+#define VM_PAGER_OK 0
+#define VM_PAGER_BAD 1
+#define VM_PAGER_FAIL 2
+#define VM_PAGER_PEND 3
+#define VM_PAGER_ERROR 4
+#define VM_PAGER_AGAIN 5
+
+#ifdef KERNEL
+extern struct pagerops *dfltpagerops;
+
+vm_pager_t vm_pager_allocate
+ __P((int, caddr_t, vm_size_t, vm_prot_t, vm_offset_t));
+vm_page_t vm_pager_atop __P((vm_offset_t));
+void vm_pager_deallocate __P((vm_pager_t));
+int vm_pager_get_pages
+ __P((vm_pager_t, vm_page_t *, int, int, boolean_t));
+boolean_t vm_pager_has_page __P((vm_pager_t, vm_offset_t));
+void vm_pager_init __P((void));
+vm_pager_t vm_pager_lookup __P((struct pagerlst *, caddr_t));
+vm_offset_t vm_pager_map_pages __P((vm_page_t *, int, boolean_t));
+int vm_pager_put_pages
+ __P((vm_pager_t, vm_page_t *, int, boolean_t, int *));
+void vm_pager_sync __P((void));
+void vm_pager_unmap_pages __P((vm_offset_t, int));
+
+#define vm_pager_cancluster(p, b) ((p)->pg_flags & (b))
+
+/*
+ * XXX compat with old interface
+ */
+#define vm_pager_get(p, m, s) \
+({ \
+ vm_page_t ml[1]; \
+ ml[0] = (m); \
+ vm_pager_get_pages(p, ml, 1, 0, s); \
+})
+
+#define vm_pager_put(p, m, s) \
+({ \
+ int rtval; \
+ vm_page_t ml[1]; \
+ ml[0] = (m); \
+ vm_pager_put_pages(p, ml, 1, s, &rtval); \
+ rtval; \
+})
+#endif
+
+#endif /* _VM_PAGER_ */
diff --git a/sys/vm/vm_param.h b/sys/vm/vm_param.h
new file mode 100644
index 0000000..4a785ce
--- /dev/null
+++ b/sys/vm/vm_param.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_param.h 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Machine independent virtual memory parameters.
+ */
+
+#ifndef _VM_PARAM_
+#define _VM_PARAM_
+
+#include <machine/vmparam.h>
+
+/*
+ * This belongs in types.h, but breaks too many existing programs.
+ */
+typedef int boolean_t;
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * The machine independent pages are refered to as PAGES. A page
+ * is some number of hardware pages, depending on the target machine.
+ */
+#define DEFAULT_PAGE_SIZE 4096
+
+#if 0
+
+/*
+ * All references to the size of a page should be done with PAGE_SIZE
+ * or PAGE_SHIFT. The fact they are variables is hidden here so that
+ * we can easily make them constant if we so desire.
+ */
+#ifndef PAGE_SIZE
+#define PAGE_SIZE cnt.v_page_size /* size of page */
+#endif
+#ifndef PAGE_MASK
+#define PAGE_MASK page_mask /* size of page - 1 */
+#endif
+#ifndef PAGE_SHIFT
+#define PAGE_SHIFT page_shift /* bits to shift for pages */
+#endif
+
+#endif
+
+#ifdef KERNEL
+extern vm_size_t page_mask;
+extern int page_shift;
+#endif
+
+/*
+ * CTL_VM identifiers
+ */
+#define VM_METER 1 /* struct vmmeter */
+#define VM_LOADAVG 2 /* struct loadavg */
+#define VM_MAXID 3 /* number of valid vm ids */
+
+#define CTL_VM_NAMES { \
+ { 0, 0 }, \
+ { "vmmeter", CTLTYPE_STRUCT }, \
+ { "loadavg", CTLTYPE_STRUCT }, \
+}
+
+/*
+ * Return values from the VM routines.
+ */
+#define KERN_SUCCESS 0
+#define KERN_INVALID_ADDRESS 1
+#define KERN_PROTECTION_FAILURE 2
+#define KERN_NO_SPACE 3
+#define KERN_INVALID_ARGUMENT 4
+#define KERN_FAILURE 5
+#define KERN_RESOURCE_SHORTAGE 6
+#define KERN_NOT_RECEIVER 7
+#define KERN_NO_ACCESS 8
+
+#ifndef ASSEMBLER
+/*
+ * Convert addresses to pages and vice versa.
+ * No rounding is used.
+ */
+#ifdef KERNEL
+
+#if 0
+
+#ifndef atop
+#define atop(x) (((unsigned)(x)) >> PAGE_SHIFT)
+#endif
+#ifndef ptoa
+#define ptoa(x) ((vm_offset_t)((x) << PAGE_SHIFT))
+#endif
+
+/*
+ * Round off or truncate to the nearest page. These will work
+ * for either addresses or counts (i.e., 1 byte rounds to 1 page).
+ */
+#ifndef round_page
+#define round_page(x) \
+ ((vm_offset_t)((((vm_offset_t)(x)) + PAGE_MASK) & ~PAGE_MASK))
+#endif
+#ifndef trunc_page
+#define trunc_page(x) \
+ ((vm_offset_t)(((vm_offset_t)(x)) & ~PAGE_MASK))
+#endif
+#ifndef num_pages
+#define num_pages(x) \
+ ((vm_offset_t)((((vm_offset_t)(x)) + PAGE_MASK) >> PAGE_SHIFT))
+#endif
+
+#endif
+#define num_pages(x) \
+ ((vm_offset_t)((((vm_offset_t)(x)) + PAGE_MASK) >> PAGE_SHIFT))
+
+extern vm_size_t mem_size; /* size of physical memory (bytes) */
+extern vm_offset_t first_addr; /* first physical page */
+extern vm_offset_t last_addr; /* last physical page */
+
+#else
+#if 0
+/* out-of-kernel versions of round_page and trunc_page */
+#define round_page(x) \
+ ((((vm_offset_t)(x) + (vm_page_size - 1)) / vm_page_size) * vm_page_size)
+#define trunc_page(x) \
+ ((((vm_offset_t)(x)) / vm_page_size) * vm_page_size)
+#endif
+
+#endif /* KERNEL */
+#endif /* ASSEMBLER */
+#endif /* _VM_PARAM_ */
diff --git a/sys/vm/vm_prot.h b/sys/vm/vm_prot.h
new file mode 100644
index 0000000..ee009bc
--- /dev/null
+++ b/sys/vm/vm_prot.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_prot.h 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Virtual memory protection definitions.
+ */
+
+#ifndef _VM_PROT_
+#define _VM_PROT_
+
+/*
+ * Types defined:
+ *
+ * vm_prot_t VM protection values.
+ */
+
+typedef u_char vm_prot_t;
+
+/*
+ * Protection values, defined as bits within the vm_prot_t type
+ */
+
+#define VM_PROT_NONE ((vm_prot_t) 0x00)
+
+#define VM_PROT_READ ((vm_prot_t) 0x01) /* read permission */
+#define VM_PROT_WRITE ((vm_prot_t) 0x02) /* write permission */
+#define VM_PROT_EXECUTE ((vm_prot_t) 0x04) /* execute permission */
+
+/*
+ * The default protection for newly-created virtual memory
+ */
+
+#define VM_PROT_DEFAULT (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE)
+
+/*
+ * The maximum privileges possible, for parameter checking.
+ */
+
+#define VM_PROT_ALL (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE)
+
+#endif /* _VM_PROT_ */
diff --git a/sys/vm/vm_swap.c b/sys/vm/vm_swap.c
new file mode 100644
index 0000000..5008a09
--- /dev/null
+++ b/sys/vm/vm_swap.c
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_swap.c 8.5 (Berkeley) 2/17/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+#include <sys/namei.h>
+#include <sys/dmap.h> /* XXX */
+#include <sys/vnode.h>
+#include <sys/map.h>
+#include <sys/file.h>
+
+#include <miscfs/specfs/specdev.h>
+
+/*
+ * Indirect driver for multi-controller paging.
+ */
+
+int nswap, nswdev;
+int vm_swap_size;
+#ifdef SEQSWAP
+int niswdev; /* number of interleaved swap devices */
+int niswap; /* size of interleaved swap area */
+#endif
+
+/*
+ * Set up swap devices.
+ * Initialize linked list of free swap
+ * headers. These do not actually point
+ * to buffers, but rather to pages that
+ * are being swapped in and out.
+ */
+void
+swapinit()
+{
+ register int i;
+ register struct buf *sp = swbuf;
+ register struct proc *p = &proc0; /* XXX */
+ struct swdevt *swp;
+ int error;
+
+ /*
+ * Count swap devices, and adjust total swap space available.
+ * Some of the space will not be countable until later (dynamically
+ * configurable devices) and some of the counted space will not be
+ * available until a swapon() system call is issued, both usually
+ * happen when the system goes multi-user.
+ *
+ * If using NFS for swap, swdevt[0] will already be bdevvp'd. XXX
+ */
+#ifdef SEQSWAP
+ nswdev = niswdev = 0;
+ nswap = niswap = 0;
+ /*
+ * All interleaved devices must come first
+ */
+ for (swp = swdevt; swp->sw_dev != NODEV || swp->sw_vp != NULL; swp++) {
+ if (swp->sw_flags & SW_SEQUENTIAL)
+ break;
+ niswdev++;
+ if (swp->sw_nblks > niswap)
+ niswap = swp->sw_nblks;
+ }
+ niswap = roundup(niswap, dmmax);
+ niswap *= niswdev;
+ if (swdevt[0].sw_vp == NULL &&
+ bdevvp(swdevt[0].sw_dev, &swdevt[0].sw_vp))
+ panic("swapvp");
+ /*
+ * The remainder must be sequential
+ */
+ for ( ; swp->sw_dev != NODEV; swp++) {
+ if ((swp->sw_flags & SW_SEQUENTIAL) == 0)
+ panic("binit: mis-ordered swap devices");
+ nswdev++;
+ if (swp->sw_nblks > 0) {
+ if (swp->sw_nblks % dmmax)
+ swp->sw_nblks -= (swp->sw_nblks % dmmax);
+ nswap += swp->sw_nblks;
+ }
+ }
+ nswdev += niswdev;
+ if (nswdev == 0)
+ panic("swapinit");
+ nswap += niswap;
+#else
+ nswdev = 0;
+ nswap = 0;
+ for (swp = swdevt; swp->sw_dev != NODEV || swp->sw_vp != NULL; swp++) {
+ nswdev++;
+ if (swp->sw_nblks > nswap)
+ nswap = swp->sw_nblks;
+ }
+ if (nswdev == 0)
+ panic("swapinit");
+ if (nswdev > 1)
+ nswap = ((nswap + dmmax - 1) / dmmax) * dmmax;
+ nswap *= nswdev;
+ if (swdevt[0].sw_vp == NULL &&
+ bdevvp(swdevt[0].sw_dev, &swdevt[0].sw_vp))
+ panic("swapvp");
+#endif
+ if (nswap == 0)
+ printf("WARNING: no swap space found\n");
+ else if (error = swfree(p, 0)) {
+ printf("swfree errno %d\n", error); /* XXX */
+ panic("swapinit swfree 0");
+ }
+
+ /*
+ * Now set up swap buffer headers.
+ */
+ for (i = 0; i < nswbuf - 1; i++, sp++) {
+ TAILQ_INSERT_HEAD(&bswlist, sp, b_freelist);
+ sp->b_rcred = sp->b_wcred = p->p_ucred;
+ sp->b_vnbufs.le_next = NOLIST;
+ }
+ sp->b_rcred = sp->b_wcred = p->p_ucred;
+ sp->b_vnbufs.le_next = NOLIST;
+ sp->b_actf = NULL;
+}
+
+void
+swstrategy(bp)
+ register struct buf *bp;
+{
+ int sz, off, seg, index;
+ register struct swdevt *sp;
+ struct vnode *vp;
+
+#ifdef GENERIC
+ /*
+ * A mini-root gets copied into the front of the swap
+ * and we run over top of the swap area just long
+ * enough for us to do a mkfs and restor of the real
+ * root (sure beats rewriting standalone restor).
+ */
+#define MINIROOTSIZE 4096
+ if (rootdev == dumpdev)
+ bp->b_blkno += MINIROOTSIZE;
+#endif
+ sz = howmany(bp->b_bcount, DEV_BSIZE);
+ if (bp->b_blkno + sz > nswap) {
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return;
+ }
+ if (nswdev > 1) {
+#ifdef SEQSWAP
+ if (bp->b_blkno < niswap) {
+ if (niswdev > 1) {
+ off = bp->b_blkno % dmmax;
+ if (off+sz > dmmax) {
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return;
+ }
+ seg = bp->b_blkno / dmmax;
+ index = seg % niswdev;
+ seg /= niswdev;
+ bp->b_blkno = seg*dmmax + off;
+ } else
+ index = 0;
+ } else {
+ register struct swdevt *swp;
+
+ bp->b_blkno -= niswap;
+ for (index = niswdev, swp = &swdevt[niswdev];
+ swp->sw_dev != NODEV;
+ swp++, index++) {
+ if (bp->b_blkno < swp->sw_nblks)
+ break;
+ bp->b_blkno -= swp->sw_nblks;
+ }
+ if (swp->sw_dev == NODEV ||
+ bp->b_blkno+sz > swp->sw_nblks) {
+ bp->b_error = swp->sw_dev == NODEV ?
+ ENODEV : EINVAL;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return;
+ }
+ }
+#else
+ off = bp->b_blkno % dmmax;
+ if (off+sz > dmmax) {
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return;
+ }
+ seg = bp->b_blkno / dmmax;
+ index = seg % nswdev;
+ seg /= nswdev;
+ bp->b_blkno = seg*dmmax + off;
+#endif
+ } else
+ index = 0;
+ sp = &swdevt[index];
+ if ((bp->b_dev = sp->sw_dev) == NODEV)
+ panic("swstrategy");
+ if (sp->sw_vp == NULL) {
+ bp->b_error = ENODEV;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return;
+ }
+ VHOLD(sp->sw_vp);
+ if ((bp->b_flags & B_READ) == 0) {
+ if (vp = bp->b_vp) {
+ vp->v_numoutput--;
+ if ((vp->v_flag & VBWAIT) && vp->v_numoutput <= 0) {
+ vp->v_flag &= ~VBWAIT;
+ wakeup((caddr_t)&vp->v_numoutput);
+ }
+ }
+ sp->sw_vp->v_numoutput++;
+ }
+ if (bp->b_vp != NULL)
+ brelvp(bp);
+ bp->b_vp = sp->sw_vp;
+ VOP_STRATEGY(bp);
+}
+
+/*
+ * System call swapon(name) enables swapping on device name,
+ * which must be in the swdevsw. Return EBUSY
+ * if already swapping on this device.
+ */
+struct swapon_args {
+ char *name;
+};
+/* ARGSUSED */
+int
+swapon(p, uap, retval)
+ struct proc *p;
+ struct swapon_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ register struct swdevt *sp;
+ dev_t dev;
+ int error;
+ struct nameidata nd;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->name, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ if (vp->v_type != VBLK) {
+ vrele(vp);
+ return (ENOTBLK);
+ }
+ dev = (dev_t)vp->v_rdev;
+ if (major(dev) >= nblkdev) {
+ vrele(vp);
+ return (ENXIO);
+ }
+ for (sp = &swdevt[0]; sp->sw_dev != NODEV; sp++) {
+ if (sp->sw_dev == dev) {
+ if (sp->sw_flags & SW_FREED) {
+ vrele(vp);
+ return (EBUSY);
+ }
+ sp->sw_vp = vp;
+ if (error = swfree(p, sp - swdevt)) {
+ vrele(vp);
+ return (error);
+ }
+ return (0);
+ }
+#ifdef SEQSWAP
+ /*
+ * If we have reached a non-freed sequential device without
+ * finding what we are looking for, it is an error.
+ * That is because all interleaved devices must come first
+ * and sequential devices must be freed in order.
+ */
+ if ((sp->sw_flags & (SW_SEQUENTIAL|SW_FREED)) == SW_SEQUENTIAL)
+ break;
+#endif
+ }
+ vrele(vp);
+ return (EINVAL);
+}
+
+/*
+ * Swfree(index) frees the index'th portion of the swap map.
+ * Each of the nswdev devices provides 1/nswdev'th of the swap
+ * space, which is laid out with blocks of dmmax pages circularly
+ * among the devices.
+ */
+int
+swfree(p, index)
+ struct proc *p;
+ int index;
+{
+ register struct swdevt *sp;
+ register swblk_t vsbase;
+ register long blk;
+ struct vnode *vp;
+ register swblk_t dvbase;
+ register int nblks;
+ int error;
+
+ sp = &swdevt[index];
+ vp = sp->sw_vp;
+ if (error = VOP_OPEN(vp, FREAD|FWRITE, p->p_ucred, p))
+ return (error);
+ sp->sw_flags |= SW_FREED;
+ nblks = sp->sw_nblks;
+ /*
+ * Some devices may not exist til after boot time.
+ * If so, their nblk count will be 0.
+ */
+ if (nblks <= 0) {
+ int perdev;
+ dev_t dev = sp->sw_dev;
+
+ if (bdevsw[major(dev)].d_psize == 0 ||
+ (nblks = (*bdevsw[major(dev)].d_psize)(dev)) == -1) {
+ (void) VOP_CLOSE(vp, FREAD|FWRITE, p->p_ucred, p);
+ sp->sw_flags &= ~SW_FREED;
+ return (ENXIO);
+ }
+#ifdef SEQSWAP
+ if (index < niswdev) {
+ perdev = niswap / niswdev;
+ if (nblks > perdev)
+ nblks = perdev;
+ } else {
+ if (nblks % dmmax)
+ nblks -= (nblks % dmmax);
+ nswap += nblks;
+ }
+#else
+ perdev = nswap / nswdev;
+ if (nblks > perdev)
+ nblks = perdev;
+#endif
+ sp->sw_nblks = nblks;
+ }
+ if (nblks == 0) {
+ (void) VOP_CLOSE(vp, FREAD|FWRITE, p->p_ucred, p);
+ sp->sw_flags &= ~SW_FREED;
+ return (0); /* XXX error? */
+ }
+#ifdef SEQSWAP
+ if (sp->sw_flags & SW_SEQUENTIAL) {
+ register struct swdevt *swp;
+
+ blk = niswap;
+ for (swp = &swdevt[niswdev]; swp != sp; swp++)
+ blk += swp->sw_nblks;
+#if 0
+ rmfree(swapmap, nblks, blk);
+ return (0);
+#endif
+ rlist_free(&swapmap, blk, blk + nblks - 1);
+ vm_swap_size += nblks;
+ return (0);
+ }
+#endif
+ for (dvbase = 0; dvbase < nblks; dvbase += dmmax) {
+ blk = nblks - dvbase;
+
+#ifdef SEQSWAP
+ if ((vsbase = index*dmmax + dvbase*niswdev) >= niswap)
+ panic("swfree");
+#else
+ if ((vsbase = index*dmmax + dvbase*nswdev) >= nswap)
+ panic("swfree");
+#endif
+ if (blk > dmmax)
+ blk = dmmax;
+#if 0
+ if (vsbase == 0) {
+ /*
+ * First of all chunks... initialize the swapmap.
+ * Don't use the first cluster of the device
+ * in case it starts with a label or boot block.
+ */
+ rminit(swapmap, blk - ctod(CLSIZE),
+ vsbase + ctod(CLSIZE), "swap", nswapmap);
+ } else if (dvbase == 0) {
+ /*
+ * Don't use the first cluster of the device
+ * in case it starts with a label or boot block.
+ */
+ rmfree(swapmap, blk - ctod(CLSIZE),
+ vsbase + ctod(CLSIZE));
+ } else
+ rmfree(swapmap, blk, vsbase);
+#endif
+ /* XXX -- we need to exclude the first cluster as above */
+ /* but for now, this will work fine... */
+ rlist_free(&swapmap, vsbase, vsbase + blk - 1);
+ vm_swap_size += blk;
+ }
+ return (0);
+}
diff --git a/sys/vm/vm_unix.c b/sys/vm/vm_unix.c
new file mode 100644
index 0000000..ee6ddf6
--- /dev/null
+++ b/sys/vm/vm_unix.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 1988 University of Utah.
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: Utah $Hdr: vm_unix.c 1.1 89/11/07$
+ *
+ * @(#)vm_unix.c 8.1 (Berkeley) 6/11/93
+ */
+
+/*
+ * Traditional sbrk/grow interface to VM
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/resourcevar.h>
+
+#include <vm/vm.h>
+
+extern int swap_pager_full;
+
+struct obreak_args {
+ char *nsiz;
+};
+
+/* ARGSUSED */
+int
+obreak(p, uap, retval)
+ struct proc *p;
+ struct obreak_args *uap;
+ int *retval;
+{
+ register struct vmspace *vm = p->p_vmspace;
+ vm_offset_t new, old;
+ int rv;
+ register int diff;
+
+ old = (vm_offset_t)vm->vm_daddr;
+ new = round_page(uap->nsiz);
+ if ((int)(new - old) > p->p_rlimit[RLIMIT_DATA].rlim_cur)
+ return(ENOMEM);
+ old = round_page(old + ctob(vm->vm_dsize));
+ diff = new - old;
+ if (diff > 0) {
+ if (swap_pager_full) {
+ return(ENOMEM);
+ }
+ rv = vm_allocate(&vm->vm_map, &old, diff, FALSE);
+ if (rv != KERN_SUCCESS) {
+ return(ENOMEM);
+ }
+ vm->vm_dsize += btoc(diff);
+ } else if (diff < 0) {
+ diff = -diff;
+ rv = vm_deallocate(&vm->vm_map, new, diff);
+ if (rv != KERN_SUCCESS) {
+ return(ENOMEM);
+ }
+ vm->vm_dsize -= btoc(diff);
+ }
+ return(0);
+}
+
+struct ovadvise_args {
+ int anom;
+};
+
+/* ARGSUSED */
+int
+ovadvise(p, uap, retval)
+ struct proc *p;
+ struct ovadvise_args *uap;
+ int *retval;
+{
+
+ return (EINVAL);
+}
diff --git a/sys/vm/vm_user.c b/sys/vm/vm_user.c
new file mode 100644
index 0000000..0f2c234
--- /dev/null
+++ b/sys/vm/vm_user.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vm_user.c 8.2 (Berkeley) 1/12/94
+ *
+ *
+ * Copyright (c) 1987, 1990 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * User-exported virtual memory functions.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+
+#include <vm/vm.h>
+
+simple_lock_data_t vm_alloc_lock; /* XXX */
+
+#ifdef MACHVMCOMPAT
+/*
+ * BSD style syscall interfaces to MACH calls
+ * All return MACH return values.
+ */
+struct svm_allocate_args {
+ vm_map_t map;
+ vm_offset_t *addr;
+ vm_size_t size;
+ boolean_t anywhere;
+};
+/* ARGSUSED */
+int
+svm_allocate(p, uap, retval)
+ struct proc *p;
+ struct svm_allocate_args *uap;
+ int *retval;
+{
+ vm_offset_t addr;
+ int rv;
+
+ uap->map = p->p_map; /* XXX */
+
+ if (copyin((caddr_t)uap->addr, (caddr_t)&addr, sizeof (addr)))
+ rv = KERN_INVALID_ARGUMENT;
+ else
+ rv = vm_allocate(uap->map, &addr, uap->size, uap->anywhere);
+ if (rv == KERN_SUCCESS) {
+ if (copyout((caddr_t)&addr, (caddr_t)uap->addr, sizeof(addr)))
+ rv = KERN_INVALID_ARGUMENT;
+ }
+ return((int)rv);
+}
+
+struct svm_deallocate_args {
+ vm_map_t map;
+ vm_offset_t addr;
+ vm_size_t size;
+};
+/* ARGSUSED */
+int
+svm_deallocate(p, uap, retval)
+ struct proc *p;
+ struct svm_deallocate_args *uap;
+ int *retval;
+{
+ int rv;
+
+ uap->map = p->p_map; /* XXX */
+ rv = vm_deallocate(uap->map, uap->addr, uap->size);
+ return((int)rv);
+}
+
+struct svm_inherit_args {
+ vm_map_t map;
+ vm_offset_t addr;
+ vm_size_t size;
+ vm_inherit_t inherit;
+};
+/* ARGSUSED */
+int
+svm_inherit(p, uap, retval)
+ struct proc *p;
+ struct svm_inherit_args *uap;
+ int *retval;
+{
+ int rv;
+
+ uap->map = p->p_map; /* XXX */
+ rv = vm_inherit(uap->map, uap->addr, uap->size, uap->inherit);
+ return((int)rv);
+}
+
+struct svm_protect_args {
+ vm_map_t map;
+ vm_offset_t addr;
+ vm_size_t size;
+ boolean_t setmax;
+ vm_prot_t prot;
+};
+/* ARGSUSED */
+int
+svm_protect(p, uap, retval)
+ struct proc *p;
+ struct svm_protect_args *uap;
+ int *retval;
+{
+ int rv;
+
+ uap->map = p->p_map; /* XXX */
+ rv = vm_protect(uap->map, uap->addr, uap->size, uap->setmax, uap->prot);
+ return((int)rv);
+}
+
+#endif
+/*
+ * vm_inherit sets the inheritence of the specified range in the
+ * specified map.
+ */
+int
+vm_inherit(map, start, size, new_inheritance)
+ register vm_map_t map;
+ vm_offset_t start;
+ vm_size_t size;
+ vm_inherit_t new_inheritance;
+{
+ if (map == NULL)
+ return(KERN_INVALID_ARGUMENT);
+
+ return(vm_map_inherit(map, trunc_page(start), round_page(start+size), new_inheritance));
+}
+
+/*
+ * vm_protect sets the protection of the specified range in the
+ * specified map.
+ */
+
+int
+vm_protect(map, start, size, set_maximum, new_protection)
+ register vm_map_t map;
+ vm_offset_t start;
+ vm_size_t size;
+ boolean_t set_maximum;
+ vm_prot_t new_protection;
+{
+ if (map == NULL)
+ return(KERN_INVALID_ARGUMENT);
+
+ return(vm_map_protect(map, trunc_page(start), round_page(start+size), new_protection, set_maximum));
+}
+
+/*
+ * vm_allocate allocates "zero fill" memory in the specfied
+ * map.
+ */
+int
+vm_allocate(map, addr, size, anywhere)
+ register vm_map_t map;
+ register vm_offset_t *addr;
+ register vm_size_t size;
+ boolean_t anywhere;
+{
+ int result;
+
+ if (map == NULL)
+ return(KERN_INVALID_ARGUMENT);
+ if (size == 0) {
+ *addr = 0;
+ return(KERN_SUCCESS);
+ }
+
+ if (anywhere)
+ *addr = vm_map_min(map);
+ else
+ *addr = trunc_page(*addr);
+ size = round_page(size);
+
+ result = vm_map_find(map, NULL, (vm_offset_t) 0, addr, size, anywhere);
+
+ return(result);
+}
+
+/*
+ * vm_deallocate deallocates the specified range of addresses in the
+ * specified address map.
+ */
+int
+vm_deallocate(map, start, size)
+ register vm_map_t map;
+ vm_offset_t start;
+ vm_size_t size;
+{
+ if (map == NULL)
+ return(KERN_INVALID_ARGUMENT);
+
+ if (size == (vm_offset_t) 0)
+ return(KERN_SUCCESS);
+
+ return(vm_map_remove(map, trunc_page(start), round_page(start+size)));
+}
+
+#if 1
+/*
+ * Similar to vm_allocate but assigns an explicit pager.
+ */
+int
+vm_allocate_with_pager(map, addr, size, anywhere, pager, poffset, internal)
+ register vm_map_t map;
+ register vm_offset_t *addr;
+ register vm_size_t size;
+ boolean_t anywhere;
+ vm_pager_t pager;
+ vm_offset_t poffset;
+ boolean_t internal;
+{
+ register vm_object_t object;
+ register int result;
+
+ if (map == NULL)
+ return(KERN_INVALID_ARGUMENT);
+
+ *addr = trunc_page(*addr);
+ size = round_page(size);
+
+ /*
+ * Lookup the pager/paging-space in the object cache.
+ * If it's not there, then create a new object and cache
+ * it.
+ */
+ object = vm_object_lookup(pager);
+ cnt.v_lookups++;
+ if (object == NULL) {
+ object = vm_object_allocate(size);
+ /*
+ * From Mike Hibler: "unnamed anonymous objects should never
+ * be on the hash list ... For now you can just change
+ * vm_allocate_with_pager to not do vm_object_enter if this
+ * is an internal object ..."
+ */
+ if (!internal)
+ vm_object_enter(object, pager);
+ } else
+ cnt.v_hits++;
+ if (internal)
+ object->flags |= OBJ_INTERNAL;
+ else {
+ object->flags &= ~OBJ_INTERNAL;
+ cnt.v_nzfod -= atop(size);
+ }
+
+ result = vm_map_find(map, object, poffset, addr, size, anywhere);
+ if (result != KERN_SUCCESS)
+ vm_object_deallocate(object);
+ else if (pager != NULL)
+ vm_object_setpager(object, pager, (vm_offset_t) 0, TRUE);
+ return(result);
+}
+#endif
diff --git a/sys/vm/vnode_pager.c b/sys/vm/vnode_pager.c
new file mode 100644
index 0000000..b8e5a19
--- /dev/null
+++ b/sys/vm/vnode_pager.c
@@ -0,0 +1,1424 @@
+/*
+ * Copyright (c) 1990 University of Utah.
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ * Copyright (c) 1993,1994 John S. Dyson
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)vnode_pager.c 7.5 (Berkeley) 4/20/91
+ * $Id: vnode_pager.c,v 1.17 1994/04/05 03:23:53 davidg Exp $
+ */
+
+/*
+ * Page to/from files (vnodes).
+ *
+ * TODO:
+ * pageouts
+ * fix credential use (uses current process credentials now)
+ */
+
+/*
+ * MODIFICATIONS:
+ * John S. Dyson 08 Dec 93
+ *
+ * This file in conjunction with some vm_fault mods, eliminate the performance
+ * advantage for using the buffer cache and minimize memory copies.
+ *
+ * 1) Supports multiple - block reads
+ * 2) Bypasses buffer cache for reads
+ *
+ * TODO:
+ *
+ * 1) Totally bypass buffer cache for reads
+ * (Currently will still sometimes use buffer cache for reads)
+ * 2) Bypass buffer cache for writes
+ * (Code does not support it, but mods are simple)
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/vnode.h>
+#include <sys/uio.h>
+#include <sys/mount.h>
+
+#include <vm/vm.h>
+#include <vm/vm_page.h>
+#include <vm/vnode_pager.h>
+
+#include <sys/buf.h>
+#include <miscfs/specfs/specdev.h>
+
+int vnode_pager_putmulti();
+
+void vnode_pager_init();
+vm_pager_t vnode_pager_alloc(caddr_t, vm_offset_t, vm_prot_t, vm_offset_t);
+void vnode_pager_dealloc();
+int vnode_pager_getpage();
+int vnode_pager_getmulti();
+int vnode_pager_putpage();
+boolean_t vnode_pager_haspage();
+
+struct pagerops vnodepagerops = {
+ vnode_pager_init,
+ vnode_pager_alloc,
+ vnode_pager_dealloc,
+ vnode_pager_getpage,
+ vnode_pager_getmulti,
+ vnode_pager_putpage,
+ vnode_pager_putmulti,
+ vnode_pager_haspage
+};
+
+static int vnode_pager_input(vn_pager_t vnp, vm_page_t *m, int count, int reqpage);
+static int vnode_pager_output(vn_pager_t vnp, vm_page_t *m, int count, int *rtvals);
+struct buf * getpbuf() ;
+void relpbuf(struct buf *bp) ;
+
+extern vm_map_t pager_map;
+
+struct pagerlst vnode_pager_list; /* list of managed vnodes */
+
+#define MAXBP (PAGE_SIZE/DEV_BSIZE);
+
+void
+vnode_pager_init()
+{
+ TAILQ_INIT(&vnode_pager_list);
+}
+
+/*
+ * Allocate (or lookup) pager for a vnode.
+ * Handle is a vnode pointer.
+ */
+vm_pager_t
+vnode_pager_alloc(handle, size, prot, offset)
+ caddr_t handle;
+ vm_size_t size;
+ vm_prot_t prot;
+ vm_offset_t offset;
+{
+ register vm_pager_t pager;
+ register vn_pager_t vnp;
+ vm_object_t object;
+ struct vattr vattr;
+ struct vnode *vp;
+ struct proc *p = curproc; /* XXX */
+
+ /*
+ * Pageout to vnode, no can do yet.
+ */
+ if (handle == NULL)
+ return(NULL);
+
+ /*
+ * Vnodes keep a pointer to any associated pager so no need to
+ * lookup with vm_pager_lookup.
+ */
+ vp = (struct vnode *)handle;
+ pager = (vm_pager_t)vp->v_vmdata;
+ if (pager == NULL) {
+ /*
+ * Allocate pager structures
+ */
+ pager = (vm_pager_t)malloc(sizeof *pager, M_VMPAGER, M_WAITOK);
+ if (pager == NULL)
+ return(NULL);
+ vnp = (vn_pager_t)malloc(sizeof *vnp, M_VMPGDATA, M_WAITOK);
+ if (vnp == NULL) {
+ free((caddr_t)pager, M_VMPAGER);
+ return(NULL);
+ }
+ /*
+ * And an object of the appropriate size
+ */
+ if (VOP_GETATTR(vp, &vattr, p->p_ucred, p) == 0) {
+ object = vm_object_allocate(round_page(vattr.va_size));
+ vm_object_enter(object, pager);
+ vm_object_setpager(object, pager, 0, TRUE);
+ } else {
+ free((caddr_t)vnp, M_VMPGDATA);
+ free((caddr_t)pager, M_VMPAGER);
+ return(NULL);
+ }
+ /*
+ * Hold a reference to the vnode and initialize pager data.
+ */
+ VREF(vp);
+ vnp->vnp_flags = 0;
+ vnp->vnp_vp = vp;
+ vnp->vnp_size = vattr.va_size;
+
+ TAILQ_INSERT_TAIL(&vnode_pager_list, pager, pg_list);
+ pager->pg_handle = handle;
+ pager->pg_type = PG_VNODE;
+ pager->pg_ops = &vnodepagerops;
+ pager->pg_data = (caddr_t)vnp;
+ vp->v_vmdata = (caddr_t)pager;
+ } else {
+ /*
+ * vm_object_lookup() will remove the object from the
+ * cache if found and also gain a reference to the object.
+ */
+ object = vm_object_lookup(pager);
+ }
+ return(pager);
+}
+
+void
+vnode_pager_dealloc(pager)
+ vm_pager_t pager;
+{
+ register vn_pager_t vnp = (vn_pager_t)pager->pg_data;
+ register struct vnode *vp;
+ struct proc *p = curproc; /* XXX */
+
+ if (vp = vnp->vnp_vp) {
+ vp->v_vmdata = NULL;
+ vp->v_flag &= ~VTEXT;
+#if 0
+ /* can hang if done at reboot on NFS FS */
+ (void) VOP_FSYNC(vp, p->p_ucred, p);
+#endif
+ vrele(vp);
+ }
+
+ TAILQ_REMOVE(&vnode_pager_list, pager, pg_list);
+ free((caddr_t)vnp, M_VMPGDATA);
+ free((caddr_t)pager, M_VMPAGER);
+}
+
+int
+vnode_pager_getmulti(pager, m, count, reqpage, sync)
+ vm_pager_t pager;
+ vm_page_t *m;
+ int count;
+ int reqpage;
+ boolean_t sync;
+{
+
+ return vnode_pager_input((vn_pager_t) pager->pg_data, m, count, reqpage);
+}
+
+int
+vnode_pager_getpage(pager, m, sync)
+ vm_pager_t pager;
+ vm_page_t m;
+ boolean_t sync;
+{
+
+ int err;
+ vm_page_t marray[1];
+ if (pager == NULL)
+ return FALSE;
+ marray[0] = m;
+
+ return vnode_pager_input((vn_pager_t)pager->pg_data, marray, 1, 0);
+}
+
+boolean_t
+vnode_pager_putpage(pager, m, sync)
+ vm_pager_t pager;
+ vm_page_t m;
+ boolean_t sync;
+{
+ int err;
+ vm_page_t marray[1];
+ int rtvals[1];
+
+ if (pager == NULL)
+ return FALSE;
+ marray[0] = m;
+ vnode_pager_output((vn_pager_t)pager->pg_data, marray, 1, rtvals);
+ return rtvals[0];
+}
+
+int
+vnode_pager_putmulti(pager, m, c, sync, rtvals)
+ vm_pager_t pager;
+ vm_page_t *m;
+ int c;
+ boolean_t sync;
+ int *rtvals;
+{
+ return vnode_pager_output((vn_pager_t)pager->pg_data, m, c, rtvals);
+}
+
+
+boolean_t
+vnode_pager_haspage(pager, offset)
+ vm_pager_t pager;
+ vm_offset_t offset;
+{
+ register vn_pager_t vnp = (vn_pager_t)pager->pg_data;
+ daddr_t bn;
+ int run;
+ int err;
+
+ /*
+ * Offset beyond end of file, do not have the page
+ */
+ if (offset >= vnp->vnp_size) {
+ return(FALSE);
+ }
+
+ /*
+ * Read the index to find the disk block to read
+ * from. If there is no block, report that we don't
+ * have this data.
+ *
+ * Assumes that the vnode has whole page or nothing.
+ */
+ err = VOP_BMAP(vnp->vnp_vp,
+ offset / vnp->vnp_vp->v_mount->mnt_stat.f_iosize,
+ (struct vnode **)0, &bn, 0);
+ if (err) {
+ return(TRUE);
+ }
+ return((long)bn < 0 ? FALSE : TRUE);
+}
+
+/*
+ * Lets the VM system know about a change in size for a file.
+ * If this vnode is mapped into some address space (i.e. we have a pager
+ * for it) we adjust our own internal size and flush any cached pages in
+ * the associated object that are affected by the size change.
+ *
+ * Note: this routine may be invoked as a result of a pager put
+ * operation (possibly at object termination time), so we must be careful.
+ */
+void
+vnode_pager_setsize(vp, nsize)
+ struct vnode *vp;
+ u_long nsize;
+{
+ register vn_pager_t vnp;
+ register vm_object_t object;
+ vm_pager_t pager;
+
+ /*
+ * Not a mapped vnode
+ */
+ if (vp == NULL || vp->v_type != VREG || vp->v_vmdata == NULL)
+ return;
+ /*
+ * Hasn't changed size
+ */
+ pager = (vm_pager_t)vp->v_vmdata;
+ vnp = (vn_pager_t)pager->pg_data;
+ if (nsize == vnp->vnp_size)
+ return;
+ /*
+ * No object.
+ * This can happen during object termination since
+ * vm_object_page_clean is called after the object
+ * has been removed from the hash table, and clean
+ * may cause vnode write operations which can wind
+ * up back here.
+ */
+ object = vm_object_lookup(pager);
+ if (object == NULL)
+ return;
+
+ /*
+ * File has shrunk.
+ * Toss any cached pages beyond the new EOF.
+ */
+ if (round_page(nsize) < round_page(vnp->vnp_size)) {
+ vm_object_lock(object);
+ vm_object_page_remove(object,
+ (vm_offset_t)round_page(nsize), round_page(vnp->vnp_size));
+ vm_object_unlock(object);
+ }
+ vnp->vnp_size = (vm_offset_t)nsize;
+ vm_object_deallocate(object);
+}
+
+void
+vnode_pager_umount(mp)
+ register struct mount *mp;
+{
+ register vm_pager_t pager, npager;
+ struct vnode *vp;
+
+ pager = vnode_pager_list.tqh_first;
+ while( pager) {
+ /*
+ * Save the next pointer now since uncaching may
+ * terminate the object and render pager invalid
+ */
+ vp = ((vn_pager_t)pager->pg_data)->vnp_vp;
+ npager = pager->pg_list.tqe_next;
+ if (mp == (struct mount *)0 || vp->v_mount == mp)
+ (void) vnode_pager_uncache(vp);
+ pager = npager;
+ }
+}
+
+/*
+ * Remove vnode associated object from the object cache.
+ *
+ * Note: this routine may be invoked as a result of a pager put
+ * operation (possibly at object termination time), so we must be careful.
+ */
+boolean_t
+vnode_pager_uncache(vp)
+ register struct vnode *vp;
+{
+ register vm_object_t object;
+ boolean_t uncached, locked;
+ vm_pager_t pager;
+
+ /*
+ * Not a mapped vnode
+ */
+ pager = (vm_pager_t)vp->v_vmdata;
+ if (pager == NULL)
+ return (TRUE);
+ /*
+ * Unlock the vnode if it is currently locked.
+ * We do this since uncaching the object may result
+ * in its destruction which may initiate paging
+ * activity which may necessitate locking the vnode.
+ */
+ locked = VOP_ISLOCKED(vp);
+ if (locked)
+ VOP_UNLOCK(vp);
+ /*
+ * Must use vm_object_lookup() as it actually removes
+ * the object from the cache list.
+ */
+ object = vm_object_lookup(pager);
+ if (object) {
+ uncached = (object->ref_count <= 1);
+ pager_cache(object, FALSE);
+ } else
+ uncached = TRUE;
+ if (locked)
+ VOP_LOCK(vp);
+ return(uncached);
+}
+#if 0
+/*
+ * Remove vnode associated object from the object cache.
+ *
+ * XXX unlock the vnode if it is currently locked.
+ * We must do this since uncaching the object may result in its
+ * destruction which may initiate paging activity which may necessitate
+ * re-locking the vnode.
+ */
+boolean_t
+vnode_pager_uncache(vp)
+ register struct vnode *vp;
+{
+ register vm_object_t object;
+ boolean_t uncached;
+ vm_pager_t pager;
+
+ /*
+ * Not a mapped vnode
+ */
+ pager = (vm_pager_t)vp->v_vmdata;
+ if (pager == NULL)
+ return (TRUE);
+ /*
+ * Must use vm_object_lookup() as it actually removes
+ * the object from the cache list.
+ */
+ object = vm_object_lookup(pager);
+ if (object) {
+ uncached = (object->ref_count <= 1);
+ VOP_UNLOCK(vp);
+ pager_cache(object, FALSE);
+ VOP_LOCK(vp);
+ } else
+ uncached = TRUE;
+ return(uncached);
+}
+#endif
+
+
+void
+vnode_pager_freepage(m)
+ vm_page_t m;
+{
+ PAGE_WAKEUP(m);
+ vm_page_free(m);
+}
+
+/*
+ * calculate the linear (byte) disk address of specified virtual
+ * file address
+ */
+vm_offset_t
+vnode_pager_addr(vp, address)
+ struct vnode *vp;
+ vm_offset_t address;
+{
+ int rtaddress;
+ int bsize;
+ vm_offset_t block;
+ struct vnode *rtvp;
+ int err;
+ int vblock, voffset;
+ int run;
+
+ bsize = vp->v_mount->mnt_stat.f_iosize;
+ vblock = address / bsize;
+ voffset = address % bsize;
+
+ err = VOP_BMAP(vp,vblock,&rtvp,&block,0);
+
+ if( err)
+ rtaddress = -1;
+ else
+ rtaddress = block * DEV_BSIZE + voffset;
+
+ return rtaddress;
+}
+
+/*
+ * interrupt routine for I/O completion
+ */
+void
+vnode_pager_iodone(bp)
+ struct buf *bp;
+{
+ bp->b_flags |= B_DONE;
+ wakeup((caddr_t)bp);
+}
+
+/*
+ * small block file system vnode pager input
+ */
+int
+vnode_pager_input_smlfs(vnp, m)
+ vn_pager_t vnp;
+ vm_page_t m;
+{
+ int i;
+ int s;
+ vm_offset_t paging_offset;
+ struct vnode *dp, *vp;
+ struct buf *bp;
+ vm_offset_t mapsize;
+ vm_offset_t foff;
+ vm_offset_t kva;
+ int fileaddr;
+ int block;
+ vm_offset_t bsize;
+ int error = 0;
+ int run;
+
+ paging_offset = m->object->paging_offset;
+ vp = vnp->vnp_vp;
+ bsize = vp->v_mount->mnt_stat.f_iosize;
+ foff = m->offset + paging_offset;
+
+ VOP_BMAP(vp, foff, &dp, 0, 0);
+
+ kva = vm_pager_map_page(m);
+
+ for(i=0;i<PAGE_SIZE/bsize;i++) {
+ /*
+ * calculate logical block and offset
+ */
+ block = foff / bsize + i;
+ s = splbio();
+ while (bp = incore(vp, block)) {
+ int amount;
+
+ /*
+ * wait until the buffer is avail or gone
+ */
+ if (bp->b_flags & B_BUSY) {
+ bp->b_flags |= B_WANTED;
+ tsleep ((caddr_t)bp, PVM, "vnwblk", 0);
+ continue;
+ }
+
+ amount = bsize;
+ if ((foff + bsize) > vnp->vnp_size)
+ amount = vnp->vnp_size - foff;
+
+ /*
+ * make sure that this page is in the buffer
+ */
+ if ((amount > 0) && amount <= bp->b_bcount) {
+ bp->b_flags |= B_BUSY;
+ splx(s);
+
+ /*
+ * copy the data from the buffer
+ */
+ bcopy(bp->b_un.b_addr, (caddr_t)kva + i * bsize, amount);
+ if (amount < bsize) {
+ bzero((caddr_t)kva + amount, bsize - amount);
+ }
+ bp->b_flags &= ~B_BUSY;
+ wakeup((caddr_t)bp);
+ goto nextblock;
+ }
+ break;
+ }
+ splx(s);
+ fileaddr = vnode_pager_addr(vp, foff + i * bsize);
+ if( fileaddr != -1) {
+ bp = getpbuf();
+ VHOLD(vp);
+
+ /* build a minimal buffer header */
+ bp->b_flags = B_BUSY | B_READ | B_CALL;
+ bp->b_iodone = vnode_pager_iodone;
+ bp->b_proc = curproc;
+ bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred;
+ if( bp->b_rcred != NOCRED)
+ crhold(bp->b_rcred);
+ if( bp->b_wcred != NOCRED)
+ crhold(bp->b_wcred);
+ bp->b_un.b_addr = (caddr_t) kva + i * bsize;
+ bp->b_blkno = fileaddr / DEV_BSIZE;
+ bgetvp(dp, bp);
+ bp->b_bcount = bsize;
+ bp->b_bufsize = bsize;
+
+ /* do the input */
+ VOP_STRATEGY(bp);
+
+ /* we definitely need to be at splbio here */
+
+ s = splbio();
+ while ((bp->b_flags & B_DONE) == 0) {
+ tsleep((caddr_t)bp, PVM, "vnsrd", 0);
+ }
+ splx(s);
+ if ((bp->b_flags & B_ERROR) != 0)
+ error = EIO;
+
+ /*
+ * free the buffer header back to the swap buffer pool
+ */
+ relpbuf(bp);
+ HOLDRELE(vp);
+ if( error)
+ break;
+ } else {
+ bzero((caddr_t) kva + i * bsize, bsize);
+ }
+nextblock:
+ }
+ vm_pager_unmap_page(kva);
+ if( error) {
+ return VM_PAGER_FAIL;
+ }
+ pmap_clear_modify(VM_PAGE_TO_PHYS(m));
+ m->flags |= PG_CLEAN;
+ m->flags &= ~PG_LAUNDRY;
+ return VM_PAGER_OK;
+
+}
+
+
+/*
+ * old style vnode pager output routine
+ */
+int
+vnode_pager_input_old(vnp, m)
+ vn_pager_t vnp;
+ vm_page_t m;
+{
+ int i;
+ struct uio auio;
+ struct iovec aiov;
+ int error;
+ int size;
+ vm_offset_t foff;
+ vm_offset_t kva;
+
+ error = 0;
+ foff = m->offset + m->object->paging_offset;
+ /*
+ * Return failure if beyond current EOF
+ */
+ if (foff >= vnp->vnp_size) {
+ return VM_PAGER_BAD;
+ } else {
+ size = PAGE_SIZE;
+ if (foff + size > vnp->vnp_size)
+ size = vnp->vnp_size - foff;
+/*
+ * Allocate a kernel virtual address and initialize so that
+ * we can use VOP_READ/WRITE routines.
+ */
+ kva = vm_pager_map_page(m);
+ aiov.iov_base = (caddr_t)kva;
+ aiov.iov_len = size;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_offset = foff;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_READ;
+ auio.uio_resid = size;
+ auio.uio_procp = (struct proc *)0;
+
+ error = VOP_READ(vnp->vnp_vp, &auio, 0, curproc->p_ucred);
+ if (!error) {
+ register int count = size - auio.uio_resid;
+
+ if (count == 0)
+ error = EINVAL;
+ else if (count != PAGE_SIZE)
+ bzero((caddr_t)kva + count, PAGE_SIZE - count);
+ }
+ vm_pager_unmap_page(kva);
+ }
+ pmap_clear_modify(VM_PAGE_TO_PHYS(m));
+ m->flags |= PG_CLEAN;
+ m->flags &= ~PG_LAUNDRY;
+ return error?VM_PAGER_FAIL:VM_PAGER_OK;
+}
+
+/*
+ * generic vnode pager input routine
+ */
+int
+vnode_pager_input(vnp, m, count, reqpage)
+ register vn_pager_t vnp;
+ vm_page_t *m;
+ int count, reqpage;
+{
+ int i,j;
+ vm_offset_t kva, foff;
+ int size;
+ struct proc *p = curproc; /* XXX */
+ vm_object_t object;
+ vm_offset_t paging_offset;
+ struct vnode *dp, *vp;
+ vm_offset_t mapsize;
+ int bsize;
+
+ int first, last;
+ int reqaddr, firstaddr;
+ int run;
+ int block, offset;
+
+ int nbp;
+ struct buf *bp;
+ int s;
+ int failflag;
+
+ int errtype=0; /* 0 is file type otherwise vm type */
+ int error = 0;
+
+ object = m[reqpage]->object; /* all vm_page_t items are in same object */
+ paging_offset = object->paging_offset;
+
+ vp = vnp->vnp_vp;
+ bsize = vp->v_mount->mnt_stat.f_iosize;
+
+ /* get the UNDERLYING device for the file with VOP_BMAP() */
+ /*
+ * originally, we did not check for an error return
+ * value -- assuming an fs always has a bmap entry point
+ * -- that assumption is wrong!!!
+ */
+ kva = 0;
+ mapsize = 0;
+ foff = m[reqpage]->offset + paging_offset;
+ if (!VOP_BMAP(vp, foff, &dp, 0, 0)) {
+ /*
+ * we do not block for a kva, notice we default to a kva
+ * conservative behavior
+ */
+ kva = kmem_alloc_pageable(pager_map, (mapsize = count*PAGE_SIZE));
+ if( !kva) {
+ for (i = 0; i < count; i++) {
+ if (i != reqpage) {
+ vnode_pager_freepage(m[i]);
+ }
+ }
+ m[0] = m[reqpage];
+ kva = kmem_alloc_wait(pager_map, mapsize = PAGE_SIZE);
+ reqpage = 0;
+ count = 1;
+ }
+ }
+
+ /*
+ * if we can't get a kva or we can't bmap, use old VOP code
+ */
+ if (!kva) {
+ for (i = 0; i < count; i++) {
+ if (i != reqpage) {
+ vnode_pager_freepage(m[i]);
+ }
+ }
+ return vnode_pager_input_old(vnp, m[reqpage]);
+ /*
+ * if the blocksize is smaller than a page size, then use
+ * special small filesystem code. NFS sometimes has a small
+ * blocksize, but it can handle large reads itself.
+ */
+ } else if( (PAGE_SIZE / bsize) > 1 &&
+ (vp->v_mount->mnt_stat.f_type != MOUNT_NFS)) {
+
+ kmem_free_wakeup(pager_map, kva, mapsize);
+
+ for (i = 0; i < count; i++) {
+ if (i != reqpage) {
+ vnode_pager_freepage(m[i]);
+ }
+ }
+ return vnode_pager_input_smlfs(vnp, m[reqpage]);
+ }
+
+/*
+ * here on direct device I/O
+ */
+
+
+ /*
+ * This pathetic hack gets data from the buffer cache, if it's there.
+ * I believe that this is not really necessary, and the ends can
+ * be gotten by defaulting to the normal vfs read behavior, but this
+ * might be more efficient, because the will NOT invoke read-aheads
+ * and one of the purposes of this code is to bypass the buffer
+ * cache and keep from flushing it by reading in a program.
+ */
+ /*
+ * calculate logical block and offset
+ */
+ block = foff / bsize;
+ offset = foff % bsize;
+ s = splbio();
+
+ /*
+ * if we have a buffer in core, then try to use it
+ */
+ while (bp = incore(vp, block)) {
+ int amount;
+
+ /*
+ * wait until the buffer is avail or gone
+ */
+ if (bp->b_flags & B_BUSY) {
+ bp->b_flags |= B_WANTED;
+ tsleep ((caddr_t)bp, PVM, "vnwblk", 0);
+ continue;
+ }
+
+ amount = PAGE_SIZE;
+ if ((foff + amount) > vnp->vnp_size)
+ amount = vnp->vnp_size - foff;
+
+ /*
+ * make sure that this page is in the buffer
+ */
+ if ((amount > 0) && (offset + amount) <= bp->b_bcount) {
+ bp->b_flags |= B_BUSY;
+ splx(s);
+
+ /*
+ * map the requested page
+ */
+ pmap_kenter(kva, VM_PAGE_TO_PHYS(m[reqpage]));
+ pmap_update();
+
+ /*
+ * copy the data from the buffer
+ */
+ bcopy(bp->b_un.b_addr + offset, (caddr_t)kva, amount);
+ if (amount < PAGE_SIZE) {
+ bzero((caddr_t)kva + amount, PAGE_SIZE - amount);
+ }
+ /*
+ * unmap the page and free the kva
+ */
+ pmap_remove(vm_map_pmap(pager_map), kva, kva + PAGE_SIZE);
+ kmem_free_wakeup(pager_map, kva, mapsize);
+ /*
+ * release the buffer back to the block subsystem
+ */
+ bp->b_flags &= ~B_BUSY;
+ wakeup((caddr_t)bp);
+ /*
+ * we did not have to do any work to get the requested
+ * page, the read behind/ahead does not justify a read
+ */
+ for (i = 0; i < count; i++) {
+ if (i != reqpage) {
+ vnode_pager_freepage(m[i]);
+ }
+ }
+ count = 1;
+ reqpage = 0;
+ m[0] = m[reqpage];
+
+ /*
+ * sorry for the goto
+ */
+ goto finishup;
+ }
+ /*
+ * buffer is nowhere to be found, read from the disk
+ */
+ break;
+ }
+ splx(s);
+
+ reqaddr = vnode_pager_addr(vp, foff);
+ s = splbio();
+ /*
+ * Make sure that our I/O request is contiguous.
+ * Scan backward and stop for the first discontiguous
+ * entry or stop for a page being in buffer cache.
+ */
+ failflag = 0;
+ first = reqpage;
+ for (i = reqpage - 1; i >= 0; --i) {
+ if (failflag ||
+ incore(vp, (foff + (i - reqpage) * PAGE_SIZE) / bsize) ||
+ (vnode_pager_addr(vp, m[i]->offset + paging_offset))
+ != reqaddr + (i - reqpage) * PAGE_SIZE) {
+ vnode_pager_freepage(m[i]);
+ failflag = 1;
+ } else {
+ first = i;
+ }
+ }
+
+ /*
+ * Scan forward and stop for the first non-contiguous
+ * entry or stop for a page being in buffer cache.
+ */
+ failflag = 0;
+ last = reqpage + 1;
+ for (i = reqpage + 1; i < count; i++) {
+ if (failflag ||
+ incore(vp, (foff + (i - reqpage) * PAGE_SIZE) / bsize) ||
+ (vnode_pager_addr(vp, m[i]->offset + paging_offset))
+ != reqaddr + (i - reqpage) * PAGE_SIZE) {
+ vnode_pager_freepage(m[i]);
+ failflag = 1;
+ } else {
+ last = i + 1;
+ }
+ }
+ splx(s);
+
+ /*
+ * the first and last page have been calculated now, move input
+ * pages to be zero based...
+ */
+ count = last;
+ if (first != 0) {
+ for (i = first; i < count; i++) {
+ m[i - first] = m[i];
+ }
+ count -= first;
+ reqpage -= first;
+ }
+
+ /*
+ * calculate the file virtual address for the transfer
+ */
+ foff = m[0]->offset + paging_offset;
+ /*
+ * and get the disk physical address (in bytes)
+ */
+ firstaddr = vnode_pager_addr(vp, foff);
+
+ /*
+ * calculate the size of the transfer
+ */
+ size = count * PAGE_SIZE;
+ if ((foff + size) > vnp->vnp_size)
+ size = vnp->vnp_size - foff;
+
+ /*
+ * round up physical size for real devices
+ */
+ if( dp->v_type == VBLK || dp->v_type == VCHR)
+ size = (size + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1);
+
+ /*
+ * and map the pages to be read into the kva
+ */
+ for (i = 0; i < count; i++)
+ pmap_kenter( kva + PAGE_SIZE * i, VM_PAGE_TO_PHYS(m[i]));
+
+ pmap_update();
+ bp = getpbuf();
+ VHOLD(vp);
+
+ /* build a minimal buffer header */
+ bp->b_flags = B_BUSY | B_READ | B_CALL;
+ bp->b_iodone = vnode_pager_iodone;
+ /* B_PHYS is not set, but it is nice to fill this in */
+ bp->b_proc = curproc;
+ bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred;
+ if( bp->b_rcred != NOCRED)
+ crhold(bp->b_rcred);
+ if( bp->b_wcred != NOCRED)
+ crhold(bp->b_wcred);
+ bp->b_un.b_addr = (caddr_t) kva;
+ bp->b_blkno = firstaddr / DEV_BSIZE;
+ bgetvp(dp, bp);
+ bp->b_bcount = size;
+ bp->b_bufsize = size;
+
+ /* do the input */
+ VOP_STRATEGY(bp);
+
+ s = splbio();
+ /* we definitely need to be at splbio here */
+
+ while ((bp->b_flags & B_DONE) == 0) {
+ tsleep((caddr_t)bp, PVM, "vnread", 0);
+ }
+ splx(s);
+ if ((bp->b_flags & B_ERROR) != 0)
+ error = EIO;
+
+ if (!error) {
+ if (size != count * PAGE_SIZE)
+ bzero((caddr_t)kva + size, PAGE_SIZE * count - size);
+ }
+
+ pmap_remove(vm_map_pmap(pager_map), kva, kva + PAGE_SIZE * count);
+ kmem_free_wakeup(pager_map, kva, mapsize);
+
+ /*
+ * free the buffer header back to the swap buffer pool
+ */
+ relpbuf(bp);
+ HOLDRELE(vp);
+
+finishup:
+ for (i = 0; i < count; i++) {
+ pmap_clear_modify(VM_PAGE_TO_PHYS(m[i]));
+ m[i]->flags |= PG_CLEAN;
+ m[i]->flags &= ~PG_LAUNDRY;
+ if (i != reqpage) {
+ /*
+ * whether or not to leave the page activated
+ * is up in the air, but we should put the page
+ * on a page queue somewhere. (it already is in
+ * the object).
+ * Result: It appears that emperical results show
+ * that deactivating pages is best.
+ */
+ /*
+ * just in case someone was asking for this
+ * page we now tell them that it is ok to use
+ */
+ if (!error) {
+ vm_page_deactivate(m[i]);
+ PAGE_WAKEUP(m[i]);
+ m[i]->flags &= ~PG_FAKE;
+ m[i]->act_count = 2;
+ } else {
+ vnode_pager_freepage(m[i]);
+ }
+ }
+ }
+ if (error) {
+ printf("vnode pager read error: %d\n", error);
+ }
+ if (errtype)
+ return error;
+ return (error ? VM_PAGER_FAIL : VM_PAGER_OK);
+}
+
+/*
+ * old-style vnode pager output routine
+ */
+int
+vnode_pager_output_old(vnp, m)
+ register vn_pager_t vnp;
+ vm_page_t m;
+{
+ vm_offset_t foff;
+ vm_offset_t kva;
+ vm_offset_t size;
+ struct iovec aiov;
+ struct uio auio;
+ struct vnode *vp;
+ int error;
+
+ vp = vnp->vnp_vp;
+ foff = m->offset + m->object->paging_offset;
+ /*
+ * Return failure if beyond current EOF
+ */
+ if (foff >= vnp->vnp_size) {
+ return VM_PAGER_BAD;
+ } else {
+ size = PAGE_SIZE;
+ if (foff + size > vnp->vnp_size)
+ size = vnp->vnp_size - foff;
+/*
+ * Allocate a kernel virtual address and initialize so that
+ * we can use VOP_WRITE routines.
+ */
+ kva = vm_pager_map_page(m);
+ aiov.iov_base = (caddr_t)kva;
+ aiov.iov_len = size;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_offset = foff;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_WRITE;
+ auio.uio_resid = size;
+ auio.uio_procp = (struct proc *)0;
+
+ error = VOP_WRITE(vp, &auio, 0, curproc->p_ucred);
+
+ if (!error) {
+ if ((size - auio.uio_resid) == 0) {
+ error = EINVAL;
+ }
+ }
+ vm_pager_unmap_page(kva);
+ return error?VM_PAGER_FAIL:VM_PAGER_OK;
+ }
+}
+
+/*
+ * vnode pager output on a small-block file system
+ */
+int
+vnode_pager_output_smlfs(vnp, m)
+ vn_pager_t vnp;
+ vm_page_t m;
+{
+ int i;
+ int s;
+ vm_offset_t paging_offset;
+ struct vnode *dp, *vp;
+ struct buf *bp;
+ vm_offset_t mapsize;
+ vm_offset_t foff;
+ vm_offset_t kva;
+ int fileaddr;
+ int block;
+ vm_offset_t bsize;
+ int run;
+ int error = 0;
+
+ paging_offset = m->object->paging_offset;
+ vp = vnp->vnp_vp;
+ bsize = vp->v_mount->mnt_stat.f_iosize;
+ foff = m->offset + paging_offset;
+
+ VOP_BMAP(vp, foff, &dp, 0, 0);
+ kva = vm_pager_map_page(m);
+ for(i = 0; !error && i < (PAGE_SIZE/bsize); i++) {
+ /*
+ * calculate logical block and offset
+ */
+ fileaddr = vnode_pager_addr(vp, foff + i * bsize);
+ if( fileaddr != -1) {
+ s = splbio();
+ if( bp = incore( vp, (foff/bsize) + i)) {
+ bp = getblk(vp, (foff/bsize) + i, bp->b_bufsize,0, 0);
+ bp->b_flags |= B_INVAL;
+ brelse(bp);
+ }
+ splx(s);
+
+ bp = getpbuf();
+ VHOLD(vp);
+
+ /* build a minimal buffer header */
+ bp->b_flags = B_BUSY | B_CALL | B_WRITE;
+ bp->b_iodone = vnode_pager_iodone;
+ bp->b_proc = curproc;
+ bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred;
+ if( bp->b_rcred != NOCRED)
+ crhold(bp->b_rcred);
+ if( bp->b_wcred != NOCRED)
+ crhold(bp->b_wcred);
+ bp->b_un.b_addr = (caddr_t) kva + i * bsize;
+ bp->b_blkno = fileaddr / DEV_BSIZE;
+ bgetvp(dp, bp);
+ ++dp->v_numoutput;
+ /* for NFS */
+ bp->b_dirtyoff = 0;
+ bp->b_dirtyend = bsize;
+ bp->b_bcount = bsize;
+ bp->b_bufsize = bsize;
+
+ /* do the input */
+ VOP_STRATEGY(bp);
+
+ /* we definitely need to be at splbio here */
+
+ s = splbio();
+ while ((bp->b_flags & B_DONE) == 0) {
+ tsleep((caddr_t)bp, PVM, "vnswrt", 0);
+ }
+ splx(s);
+ if ((bp->b_flags & B_ERROR) != 0)
+ error = EIO;
+
+ /*
+ * free the buffer header back to the swap buffer pool
+ */
+ relpbuf(bp);
+ HOLDRELE(vp);
+ }
+ }
+ vm_pager_unmap_page(kva);
+ if( error)
+ return VM_PAGER_FAIL;
+ else
+ return VM_PAGER_OK;
+}
+
+/*
+ * generic vnode pager output routine
+ */
+int
+vnode_pager_output(vnp, m, count, rtvals)
+ vn_pager_t vnp;
+ vm_page_t *m;
+ int count;
+ int *rtvals;
+{
+ int i,j;
+ vm_offset_t kva, foff;
+ int size;
+ struct proc *p = curproc; /* XXX */
+ vm_object_t object;
+ vm_offset_t paging_offset;
+ struct vnode *dp, *vp;
+ struct buf *bp;
+ vm_offset_t mapsize;
+ vm_offset_t reqaddr;
+ int run;
+ int bsize;
+ int s;
+
+ int error = 0;
+
+retryoutput:
+ object = m[0]->object; /* all vm_page_t items are in same object */
+ paging_offset = object->paging_offset;
+
+ vp = vnp->vnp_vp;
+ bsize = vp->v_mount->mnt_stat.f_iosize;
+
+ for(i=0;i<count;i++)
+ rtvals[i] = VM_PAGER_AGAIN;
+
+ /*
+ * if the filesystem does not have a bmap, then use the
+ * old code
+ */
+ if (VOP_BMAP(vp, m[0]->offset+paging_offset, &dp, 0, 0)) {
+
+ rtvals[0] = vnode_pager_output_old(vnp, m[0]);
+
+ pmap_clear_modify(VM_PAGE_TO_PHYS(m[0]));
+ m[0]->flags |= PG_CLEAN;
+ m[0]->flags &= ~PG_LAUNDRY;
+ return rtvals[0];
+ }
+
+ /*
+ * if the filesystem has a small blocksize, then use
+ * the small block filesystem output code
+ */
+ if ((bsize < PAGE_SIZE) &&
+ (vp->v_mount->mnt_stat.f_type != MOUNT_NFS)) {
+
+ for(i=0;i<count;i++) {
+ rtvals[i] = vnode_pager_output_smlfs(vnp, m[i]);
+ if( rtvals[i] == VM_PAGER_OK) {
+ pmap_clear_modify(VM_PAGE_TO_PHYS(m[i]));
+ m[i]->flags |= PG_CLEAN;
+ m[i]->flags &= ~PG_LAUNDRY;
+ }
+ }
+ return rtvals[0];
+ }
+
+ /*
+ * get some kva for the output
+ */
+ kva = kmem_alloc_pageable(pager_map, (mapsize = count*PAGE_SIZE));
+ if( !kva) {
+ kva = kmem_alloc_pageable(pager_map, (mapsize = PAGE_SIZE));
+ count = 1;
+ if( !kva)
+ return rtvals[0];
+ }
+
+ for(i=0;i<count;i++) {
+ foff = m[i]->offset + paging_offset;
+ if (foff >= vnp->vnp_size) {
+ for(j=i;j<count;j++)
+ rtvals[j] = VM_PAGER_BAD;
+ count = i;
+ break;
+ }
+ }
+ if (count == 0) {
+ return rtvals[0];
+ }
+ foff = m[0]->offset + paging_offset;
+ reqaddr = vnode_pager_addr(vp, foff);
+ /*
+ * Scan forward and stop for the first non-contiguous
+ * entry or stop for a page being in buffer cache.
+ */
+ for (i = 1; i < count; i++) {
+ if ( vnode_pager_addr(vp, m[i]->offset + paging_offset)
+ != reqaddr + i * PAGE_SIZE) {
+ count = i;
+ break;
+ }
+ }
+
+ /*
+ * calculate the size of the transfer
+ */
+ size = count * PAGE_SIZE;
+ if ((foff + size) > vnp->vnp_size)
+ size = vnp->vnp_size - foff;
+
+ /*
+ * round up physical size for real devices
+ */
+ if( dp->v_type == VBLK || dp->v_type == VCHR)
+ size = (size + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1);
+
+ /*
+ * and map the pages to be read into the kva
+ */
+ for (i = 0; i < count; i++)
+ pmap_kenter( kva + PAGE_SIZE * i, VM_PAGE_TO_PHYS(m[i]));
+ pmap_update();
+/*
+ printf("vnode: writing foff: %d, devoff: %d, size: %d\n",
+ foff, reqaddr, size);
+*/
+ /*
+ * next invalidate the incore vfs_bio data
+ */
+ for (i = 0; i < count; i++) {
+ int filblock = (foff + i * PAGE_SIZE) / bsize;
+ struct buf *fbp;
+
+ s = splbio();
+ if( fbp = incore( vp, filblock)) {
+ /* printf("invalidating: %d\n", filblock); */
+ fbp = getblk(vp, filblock, fbp->b_bufsize,0,0);
+ fbp->b_flags |= B_INVAL;
+ brelse(fbp);
+ }
+ splx(s);
+ }
+
+
+ bp = getpbuf();
+ VHOLD(vp);
+ /* build a minimal buffer header */
+ bp->b_flags = B_BUSY | B_WRITE | B_CALL;
+ bp->b_iodone = vnode_pager_iodone;
+ /* B_PHYS is not set, but it is nice to fill this in */
+ bp->b_proc = curproc;
+ bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred;
+
+ if( bp->b_rcred != NOCRED)
+ crhold(bp->b_rcred);
+ if( bp->b_wcred != NOCRED)
+ crhold(bp->b_wcred);
+ bp->b_un.b_addr = (caddr_t) kva;
+ bp->b_blkno = reqaddr / DEV_BSIZE;
+ bgetvp(dp, bp);
+ ++dp->v_numoutput;
+
+ /* for NFS */
+ bp->b_dirtyoff = 0;
+ bp->b_dirtyend = size;
+
+ bp->b_bcount = size;
+ bp->b_bufsize = size;
+
+ /* do the output */
+ VOP_STRATEGY(bp);
+
+ s = splbio();
+
+ /* we definitely need to be at splbio here */
+
+ while ((bp->b_flags & B_DONE) == 0) {
+ tsleep((caddr_t)bp, PVM, "vnwrite", 0);
+ }
+ splx(s);
+
+ if ((bp->b_flags & B_ERROR) != 0)
+ error = EIO;
+
+ pmap_remove(vm_map_pmap(pager_map), kva, kva + PAGE_SIZE * count);
+ kmem_free_wakeup(pager_map, kva, mapsize);
+
+ /*
+ * free the buffer header back to the swap buffer pool
+ */
+ relpbuf(bp);
+ HOLDRELE(vp);
+
+ if( !error) {
+ for(i=0;i<count;i++) {
+ pmap_clear_modify(VM_PAGE_TO_PHYS(m[i]));
+ m[i]->flags |= PG_CLEAN;
+ m[i]->flags &= ~PG_LAUNDRY;
+ rtvals[i] = VM_PAGER_OK;
+ }
+ } else if( count != 1) {
+ error = 0;
+ count = 1;
+ goto retryoutput;
+ }
+
+ if (error) {
+ printf("vnode pager write error: %d\n", error);
+ }
+ return (error ? VM_PAGER_FAIL : VM_PAGER_OK);
+}
+
diff --git a/sys/vm/vnode_pager.h b/sys/vm/vnode_pager.h
new file mode 100644
index 0000000..b01dc54
--- /dev/null
+++ b/sys/vm/vnode_pager.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1990 University of Utah.
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vnode_pager.h 8.1 (Berkeley) 6/11/93
+ */
+
+#ifndef _VNODE_PAGER_
+#define _VNODE_PAGER_ 1
+
+/*
+ * VNODE pager private data.
+ */
+struct vnpager {
+ int vnp_flags; /* flags */
+ struct vnode *vnp_vp; /* vnode */
+ vm_size_t vnp_size; /* vnode current size */
+};
+typedef struct vnpager *vn_pager_t;
+
+#define VN_PAGER_NULL ((vn_pager_t)0)
+
+#endif /* _VNODE_PAGER_ */
OpenPOWER on IntegriCloud